am bb97d7bb: am 0467b0ab: Merge "Add a new public API to support recording rotated videos" into gingerbread

* commit 'bb97d7bbf33393af842de1d8fd8512a24c73b6b3':
  Add a new public API to support recording rotated videos
diff --git a/Android.mk b/Android.mk
index 1c8051b..4e01b97 100644
--- a/Android.mk
+++ b/Android.mk
@@ -100,9 +100,11 @@
 	core/java/android/bluetooth/IBluetoothCallback.aidl \
 	core/java/android/bluetooth/IBluetoothHeadset.aidl \
 	core/java/android/bluetooth/IBluetoothPbap.aidl \
+	core/java/android/content/IClipboard.aidl \
 	core/java/android/content/IContentService.aidl \
 	core/java/android/content/IIntentReceiver.aidl \
 	core/java/android/content/IIntentSender.aidl \
+	core/java/android/content/IOnPrimaryClipChangedListener.aidl \
 	core/java/android/content/ISyncAdapter.aidl \
 	core/java/android/content/ISyncContext.aidl \
 	core/java/android/content/ISyncStatusObserver.aidl \
@@ -132,12 +134,9 @@
 	core/java/android/os/IPowerManager.aidl \
     core/java/android/os/IRemoteCallback.aidl \
 	core/java/android/os/IVibratorService.aidl \
-	core/java/android/service/urlrenderer/IUrlRendererService.aidl \
-	core/java/android/service/urlrenderer/IUrlRendererCallback.aidl \
     core/java/android/service/wallpaper/IWallpaperConnection.aidl \
     core/java/android/service/wallpaper/IWallpaperEngine.aidl \
     core/java/android/service/wallpaper/IWallpaperService.aidl \
-	core/java/android/text/IClipboard.aidl \
 	core/java/android/view/accessibility/IAccessibilityManager.aidl \
 	core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
 	core/java/android/view/IApplicationToken.aidl \
@@ -167,6 +166,9 @@
 	core/java/com/android/internal/view/IInputMethodClient.aidl \
 	core/java/com/android/internal/view/IInputMethodManager.aidl \
 	core/java/com/android/internal/view/IInputMethodSession.aidl \
+	core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
+	location/java/android/location/ICountryDetector.aidl \
+	location/java/android/location/ICountryListener.aidl \
 	location/java/android/location/IGeocodeProvider.aidl \
 	location/java/android/location/IGpsStatusListener.aidl \
 	location/java/android/location/IGpsStatusProvider.aidl \
@@ -360,7 +362,7 @@
 			framework \
 
 framework_docs_LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-framework_docs_LOCAL_DROIDDOC_HTML_DIR := $(LOCAL_PATH)/docs/html $(OUT_DOCS)/gen
+framework_docs_LOCAL_DROIDDOC_HTML_DIR := docs/html
 # The since flag (-since N.xml API_LEVEL) is used to add API Level information
 # to the reference documentation. Must be in order of oldest to newest.
 framework_docs_LOCAL_DROIDDOC_OPTIONS := \
@@ -374,8 +376,9 @@
     -since ./frameworks/base/api/7.xml 7 \
     -since ./frameworks/base/api/8.xml 8 \
     -since ./frameworks/base/api/9.xml 9 \
-    -werror -hide 13 \
-    -overview $(LOCAL_PATH)/core/java/overview.html
+    -since ./frameworks/base/api/current.xml HC \
+		-werror -hide 113 \
+		-overview $(LOCAL_PATH)/core/java/overview.html
 
 framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= $(call intermediates-dir-for,JAVA_LIBRARIES,framework)
 
@@ -435,7 +438,9 @@
 		-samplecode $(sample_dir)/WiktionarySimple \
 		            resources/samples/WiktionarySimple "Wiktionary (Simplified)" \
 		-samplecode $(sample_dir)/VoiceRecognitionService \
-		            resources/samples/VoiceRecognitionService "Voice Recognition Service"
+		            resources/samples/VoiceRecognitionService "Voice Recognition Service" \
+		-samplecode $(sample_dir)/XmlAdapters \
+		            resources/samples/XmlAdapters "XML Adapters"
 
 ## SDK version identifiers used in the published docs
   # major[.minor] version for current SDK. (full releases only)
@@ -470,11 +475,13 @@
 		-nodocs
 
 LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
-LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-sdk
+
+LOCAL_UNINSTALLABLE_MODULE := true
 
 include $(BUILD_DROIDDOC)
 
-$(full_target): $(framework_built)
+# $(gen), i.e. framework.aidl, is also needed while building against the current stub.
+$(full_target): $(framework_built) $(gen)
 $(INTERNAL_PLATFORM_API_FILE): $(full_target)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
 
@@ -497,7 +504,8 @@
 		-parsecomments
 
 LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
-LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-sdk
+
+LOCAL_UNINSTALLABLE_MODULE := true
 
 include $(BUILD_DROIDDOC)
 
@@ -535,7 +543,6 @@
 endif
 
 LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
-LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-sdk
 
 include $(BUILD_DROIDDOC)
 
@@ -548,7 +555,6 @@
 $(full_target): $(static_doc_index_redirect)
 $(full_target): $(framework_built)
 
-
 # ==== docs for the web (on the google app engine server) =======================
 include $(CLEAR_VARS)
 
@@ -572,12 +578,11 @@
 		-hdf template.showLanguageMenu true
 
 LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
-LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-sdk
 
 include $(BUILD_DROIDDOC)
 
 # explicitly specify that online-sdk depends on framework-res and any generated docs
-$(full_target): framework-res-package-target $(ALL_GENERATED_DOCS)
+$(full_target): framework-res-package-target
 
 # ==== docs that have all of the stuff that's @hidden =======================
 include $(CLEAR_VARS)
@@ -594,24 +599,31 @@
 LOCAL_MODULE := hidden
 LOCAL_DROIDDOC_OPTIONS:=\
 		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-        -title "Android SDK - Including hidden APIs."
-#        -hidden
+		-title "Android SDK - Including hidden APIs."
+#		-hidden
 
 LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
-LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-sdk
 
 include $(BUILD_DROIDDOC)
 
 # Build ext.jar
 # ============================================================
 
+# NOTICE notes for non-obvious sections
+# apache-http - covered by the Apache Commons section.
+
+
 ext_dirs := \
 	../../external/nist-sip/java \
 	../../external/apache-http/src \
-	../../external/tagsoup/src
+	../../external/tagsoup/src \
+	../../external/libphonenumber/java/src
 
 ext_src_files := $(call all-java-files-under,$(ext_dirs))
 
+ext_res_dirs := \
+	../../external/libphonenumber/java/src
+
 # ====  the library  =========================================
 include $(CLEAR_VARS)
 
@@ -619,6 +631,7 @@
 
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVA_LIBRARIES := core
+LOCAL_JAVA_RESOURCE_DIRS := $(ext_res_dirs)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := ext
 
diff --git a/CleanSpec.mk b/CleanSpec.mk
index d19f3f8..c7bd9fd 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -67,11 +67,25 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libreverb_intermediates)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libreverbtest_intermediates)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/)
+$(call add-clean-step, find . -type f -name "*.rs" -print0 | xargs -0 touch)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libandroid_runtime_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/libandroid_runtime.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/libandroid_runtime.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libandroid_runtime.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libhwui_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/libhwui.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/libhwui.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libhwui.so)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/storage/*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/content/IClipboard.P)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/android/internal/telephony/ITelephonyRegistry.P)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates)
+$(call add-clean-step, rm -rf out/target/common/docs/api-stubs*)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/trustedlogic)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/com/trustedlogic)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/trustedlogic)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/com/trustedlogic)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/target/common/obj/APPS/Music2_intermediates)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/NOTICE b/NOTICE
index 2006201..8d6f583 100644
--- a/NOTICE
+++ b/NOTICE
@@ -17,7 +17,7 @@
    =========================================================================
 
 Apache Commons
-Copyright 1999-2004 The Apache Software Foundation
+Copyright 1999-2006 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
@@ -53,6 +53,26 @@
 These files are Copyright 1998 - 2009 PacketVideo, but released under
 the Apache2 License.
 
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the TagSoup code.                                 ==
+   =========================================================================
+
+This file is part of TagSoup and is Copyright 2002-2008 by John Cowan.
+
+TagSoup is licensed under the Apache License,
+Version 2.0.  You may obtain a copy of this license at
+http://www.apache.org/licenses/LICENSE-2.0 .  You may also have
+additional legal rights not granted by this license.
+
+TagSoup is distributed in the hope that it will be useful, but
+unless required by applicable law or agreed to in writing, TagSoup
+is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+OF ANY KIND, either express or implied; not even the implied warranty
+of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+
                                Apache License
                            Version 2.0, January 2004
                         http://www.apache.org/licenses/
diff --git a/api/9.xml b/api/9.xml
index 3056e47..3e7653c 100644
--- a/api/9.xml
+++ b/api/9.xml
@@ -705,17 +705,6 @@
  visibility="public"
 >
 </field>
-<field name="NFC"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.permission.NFC&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="PERSISTENT_ACTIVITY"
  type="java.lang.String"
  transient="false"
@@ -723,7 +712,7 @@
  value="&quot;android.permission.PERSISTENT_ACTIVITY&quot;"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -958,17 +947,6 @@
  visibility="public"
 >
 </field>
-<field name="SET_ALARM"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;com.android.alarm.permission.SET_ALARM&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="SET_ALWAYS_FINISH"
  type="java.lang.String"
  transient="false"
@@ -1156,17 +1134,6 @@
  visibility="public"
 >
 </field>
-<field name="USE_SIP"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.permission.USE_SIP&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="VIBRATE"
  type="java.lang.String"
  transient="false"
@@ -3705,17 +3672,6 @@
  visibility="public"
 >
 </field>
-<field name="filterTouchesWhenObscured"
- type="int"
- transient="false"
- volatile="false"
- value="16843460"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="finishOnCloseSystemDialogs"
  type="int"
  transient="false"
@@ -5960,39 +5916,6 @@
  visibility="public"
 >
 </field>
-<field name="overScrollFooter"
- type="int"
- transient="false"
- volatile="false"
- value="16843459"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="overScrollHeader"
- type="int"
- transient="false"
- volatile="false"
- value="16843458"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="overScrollMode"
- type="int"
- transient="false"
- volatile="false"
- value="16843457"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="padding"
  type="int"
  transient="false"
@@ -6224,17 +6147,6 @@
  visibility="public"
 >
 </field>
-<field name="popupAnimationStyle"
- type="int"
- transient="false"
- volatile="false"
- value="16843465"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="popupBackground"
  type="int"
  transient="false"
@@ -6961,17 +6873,6 @@
  visibility="public"
 >
 </field>
-<field name="screenDensity"
- type="int"
- transient="false"
- volatile="false"
- value="16843467"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="screenOrientation"
  type="int"
  transient="false"
@@ -6983,17 +6884,6 @@
  visibility="public"
 >
 </field>
-<field name="screenSize"
- type="int"
- transient="false"
- volatile="false"
- value="16843466"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="scrollHorizontally"
  type="int"
  transient="false"
@@ -8545,50 +8435,6 @@
  visibility="public"
 >
 </field>
-<field name="textSelectHandle"
- type="int"
- transient="false"
- volatile="false"
- value="16843463"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="textSelectHandleLeft"
- type="int"
- transient="false"
- volatile="false"
- value="16843461"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="textSelectHandleRight"
- type="int"
- transient="false"
- volatile="false"
- value="16843462"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="textSelectHandleWindowStyle"
- type="int"
- transient="false"
- volatile="false"
- value="16843464"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="textSize"
  type="int"
  transient="false"
@@ -9502,17 +9348,6 @@
  visibility="public"
 >
 </field>
-<field name="xlargeScreens"
- type="int"
- transient="false"
- volatile="false"
- value="16843455"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="y"
  type="int"
  transient="false"
@@ -11044,39 +10879,6 @@
  visibility="public"
 >
 </field>
-<field name="presence_audio_away"
- type="int"
- transient="false"
- volatile="false"
- value="17301679"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="presence_audio_busy"
- type="int"
- transient="false"
- volatile="false"
- value="17301680"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="presence_audio_online"
- type="int"
- transient="false"
- volatile="false"
- value="17301681"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="presence_away"
  type="int"
  transient="false"
@@ -11132,39 +10934,6 @@
  visibility="public"
 >
 </field>
-<field name="presence_video_away"
- type="int"
- transient="false"
- volatile="false"
- value="17301676"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="presence_video_busy"
- type="int"
- transient="false"
- volatile="false"
- value="17301677"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="presence_video_online"
- type="int"
- transient="false"
- volatile="false"
- value="17301678"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="progress_horizontal"
  type="int"
  transient="false"
@@ -11491,7 +11260,7 @@
  value="17301636"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -11502,7 +11271,7 @@
  value="17301637"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -11513,7 +11282,7 @@
  value="17301638"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -11557,7 +11326,7 @@
  value="17301671"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -11568,7 +11337,7 @@
  value="17301672"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -13077,50 +12846,6 @@
  visibility="public"
 >
 </field>
-<field name="TextAppearance_StatusBar_EventContent"
- type="int"
- transient="false"
- volatile="false"
- value="16973927"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TextAppearance_StatusBar_EventContent_Title"
- type="int"
- transient="false"
- volatile="false"
- value="16973928"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TextAppearance_StatusBar_Icon"
- type="int"
- transient="false"
- volatile="false"
- value="16973926"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TextAppearance_StatusBar_Title"
- type="int"
- transient="false"
- volatile="false"
- value="16973925"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TextAppearance_Theme"
  type="int"
  transient="false"
@@ -16120,17 +15845,6 @@
  visibility="public"
 >
 </method>
-<method name="getInstanceCount"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getIntent"
  return="android.content.Intent"
  abstract="false"
@@ -17293,7 +17007,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 <parameter name="isPersistent" type="boolean">
@@ -18536,17 +18250,6 @@
  visibility="public"
 >
 </field>
-<field name="IMPORTANCE_PERCEPTIBLE"
- type="int"
- transient="false"
- volatile="false"
- value="130"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="IMPORTANCE_SERVICE"
  type="int"
  transient="false"
@@ -21287,678 +20990,6 @@
 </parameter>
 </method>
 </class>
-<class name="DownloadManager"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="enqueue"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="request" type="android.app.DownloadManager.Request">
-</parameter>
-</method>
-<method name="openDownloadedFile"
- return="android.os.ParcelFileDescriptor"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="long">
-</parameter>
-<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
-</exception>
-</method>
-<method name="query"
- return="android.database.Cursor"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="query" type="android.app.DownloadManager.Query">
-</parameter>
-</method>
-<method name="remove"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="ids" type="long...">
-</parameter>
-</method>
-<field name="ACTION_DOWNLOAD_COMPLETE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.DOWNLOAD_COMPLETE&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACTION_NOTIFICATION_CLICKED"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACTION_VIEW_DOWNLOADS"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.VIEW_DOWNLOADS&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_BYTES_DOWNLOADED_SO_FAR"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;bytes_so_far&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_DESCRIPTION"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;description&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_ID"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;_id&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_LAST_MODIFIED_TIMESTAMP"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;last_modified_timestamp&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_LOCAL_URI"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;local_uri&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_MEDIAPROVIDER_URI"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;mediaprovider_uri&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_MEDIA_TYPE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;media_type&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_REASON"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;reason&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_STATUS"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;status&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_TITLE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;title&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_TOTAL_SIZE_BYTES"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;total_size&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="COLUMN_URI"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;uri&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_CANNOT_RESUME"
- type="int"
- transient="false"
- volatile="false"
- value="1008"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_DEVICE_NOT_FOUND"
- type="int"
- transient="false"
- volatile="false"
- value="1007"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_FILE_ALREADY_EXISTS"
- type="int"
- transient="false"
- volatile="false"
- value="1009"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_FILE_ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="1001"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_HTTP_DATA_ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="1004"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_INSUFFICIENT_SPACE"
- type="int"
- transient="false"
- volatile="false"
- value="1006"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_TOO_MANY_REDIRECTS"
- type="int"
- transient="false"
- volatile="false"
- value="1005"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_UNHANDLED_HTTP_CODE"
- type="int"
- transient="false"
- volatile="false"
- value="1002"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_UNKNOWN"
- type="int"
- transient="false"
- volatile="false"
- value="1000"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_DOWNLOAD_ID"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;extra_download_id&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PAUSED_QUEUED_FOR_WIFI"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PAUSED_UNKNOWN"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PAUSED_WAITING_FOR_NETWORK"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PAUSED_WAITING_TO_RETRY"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_FAILED"
- type="int"
- transient="false"
- volatile="false"
- value="16"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_PAUSED"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_PENDING"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_RUNNING"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATUS_SUCCESSFUL"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="DownloadManager.Query"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="DownloadManager.Query"
- type="android.app.DownloadManager.Query"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="setFilterById"
- return="android.app.DownloadManager.Query"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="ids" type="long...">
-</parameter>
-</method>
-<method name="setFilterByStatus"
- return="android.app.DownloadManager.Query"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="flags" type="int">
-</parameter>
-</method>
-</class>
-<class name="DownloadManager.Request"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="DownloadManager.Request"
- type="android.app.DownloadManager.Request"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-</constructor>
-<method name="addRequestHeader"
- return="android.app.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="header" type="java.lang.String">
-</parameter>
-<parameter name="value" type="java.lang.String">
-</parameter>
-</method>
-<method name="setAllowedNetworkTypes"
- return="android.app.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<method name="setAllowedOverRoaming"
- return="android.app.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="allowed" type="boolean">
-</parameter>
-</method>
-<method name="setDescription"
- return="android.app.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="description" type="java.lang.CharSequence">
-</parameter>
-</method>
-<method name="setDestinationInExternalFilesDir"
- return="android.app.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="dirType" type="java.lang.String">
-</parameter>
-<parameter name="subPath" type="java.lang.String">
-</parameter>
-</method>
-<method name="setDestinationInExternalPublicDir"
- return="android.app.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dirType" type="java.lang.String">
-</parameter>
-<parameter name="subPath" type="java.lang.String">
-</parameter>
-</method>
-<method name="setDestinationUri"
- return="android.app.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="android.net.Uri">
-</parameter>
-</method>
-<method name="setMimeType"
- return="android.app.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="mimeType" type="java.lang.String">
-</parameter>
-</method>
-<method name="setShowRunningNotification"
- return="android.app.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="show" type="boolean">
-</parameter>
-</method>
-<method name="setTitle"
- return="android.app.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="title" type="java.lang.CharSequence">
-</parameter>
-</method>
-<method name="setVisibleInDownloadsUi"
- return="android.app.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="isVisible" type="boolean">
-</parameter>
-</method>
-<field name="NETWORK_MOBILE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="NETWORK_WIFI"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="ExpandableListActivity"
  extends="android.app.Activity"
  abstract="false"
@@ -23736,146 +22767,6 @@
 </parameter>
 </method>
 </class>
-<class name="NativeActivity"
- extends="android.app.Activity"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.view.InputQueue.Callback">
-</implements>
-<implements name="android.view.SurfaceHolder.Callback2">
-</implements>
-<implements name="android.view.ViewTreeObserver.OnGlobalLayoutListener">
-</implements>
-<constructor name="NativeActivity"
- type="android.app.NativeActivity"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="onGlobalLayout"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="onInputQueueCreated"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="queue" type="android.view.InputQueue">
-</parameter>
-</method>
-<method name="onInputQueueDestroyed"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="queue" type="android.view.InputQueue">
-</parameter>
-</method>
-<method name="surfaceChanged"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="holder" type="android.view.SurfaceHolder">
-</parameter>
-<parameter name="format" type="int">
-</parameter>
-<parameter name="width" type="int">
-</parameter>
-<parameter name="height" type="int">
-</parameter>
-</method>
-<method name="surfaceCreated"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="holder" type="android.view.SurfaceHolder">
-</parameter>
-</method>
-<method name="surfaceDestroyed"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="holder" type="android.view.SurfaceHolder">
-</parameter>
-</method>
-<method name="surfaceRedrawNeeded"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="holder" type="android.view.SurfaceHolder">
-</parameter>
-</method>
-<field name="META_DATA_FUNC_NAME"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.app.func_name&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="META_DATA_LIB_NAME"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.app.lib_name&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="Notification"
  extends="java.lang.Object"
  abstract="false"
@@ -23918,17 +22809,6 @@
 <parameter name="parcel" type="android.os.Parcel">
 </parameter>
 </constructor>
-<method name="clone"
- return="android.app.Notification"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="describeContents"
  return="int"
  abstract="false"
@@ -24176,16 +23056,6 @@
  visibility="public"
 >
 </field>
-<field name="fullScreenIntent"
- type="android.app.PendingIntent"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="icon"
  type="int"
  transient="false"
@@ -27880,17 +26750,6 @@
  visibility="public"
 >
 </field>
-<field name="WIPE_EXTERNAL_STORAGE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
 </package>
 <package name="android.app.backup"
@@ -28252,7 +27111,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="newState" type="android.os.ParcelFileDescriptor">
+<parameter name="fd" type="android.os.ParcelFileDescriptor">
 </parameter>
 </method>
 </interface>
@@ -31080,7 +29939,7 @@
  value="2743"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -35563,17 +34422,6 @@
  visibility="public"
 >
 </field>
-<field name="DOWNLOAD_SERVICE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;download&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="DROPBOX_SERVICE"
  type="java.lang.String"
  transient="false"
@@ -35717,17 +34565,6 @@
  visibility="public"
 >
 </field>
-<field name="STORAGE_SERVICE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;storage&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TELEPHONY_SERVICE"
  type="java.lang.String"
  transient="false"
@@ -39053,28 +37890,6 @@
  visibility="public"
 >
 </field>
-<field name="ACTION_EXTERNAL_APPLICATIONS_AVAILABLE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="ACTION_FACTORY_TEST"
  type="java.lang.String"
  transient="false"
@@ -40042,28 +38857,6 @@
  visibility="public"
 >
 </field>
-<field name="EXTRA_CHANGED_PACKAGE_LIST"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.extra.changed_package_list&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_CHANGED_UID_LIST"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.extra.changed_uid_list&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="EXTRA_DATA_REMOVED"
  type="java.lang.String"
  transient="false"
@@ -41671,17 +40464,6 @@
  visibility="public"
 >
 </method>
-<method name="getTargetPackage"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="readIntentSenderOrNullFromParcel"
  return="android.content.IntentSender"
  abstract="false"
@@ -42398,17 +41180,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<method name="apply"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="clear"
  return="android.content.SharedPreferences.Editor"
  abstract="true"
@@ -43576,17 +42347,6 @@
  visibility="public"
 >
 </field>
-<field name="SCREEN_ORIENTATION_FULL_SENSOR"
- type="int"
- transient="false"
- volatile="false"
- value="10"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="SCREEN_ORIENTATION_LANDSCAPE"
  type="int"
  transient="false"
@@ -43620,28 +42380,6 @@
  visibility="public"
 >
 </field>
-<field name="SCREEN_ORIENTATION_REVERSE_LANDSCAPE"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SCREEN_ORIENTATION_REVERSE_PORTRAIT"
- type="int"
- transient="false"
- volatile="false"
- value="9"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="SCREEN_ORIENTATION_SENSOR"
  type="int"
  transient="false"
@@ -43653,28 +42391,6 @@
  visibility="public"
 >
 </field>
-<field name="SCREEN_ORIENTATION_SENSOR_LANDSCAPE"
- type="int"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SCREEN_ORIENTATION_SENSOR_PORTRAIT"
- type="int"
- transient="false"
- volatile="false"
- value="7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="SCREEN_ORIENTATION_UNSPECIFIED"
  type="int"
  transient="false"
@@ -43909,17 +42625,6 @@
  visibility="public"
 >
 </field>
-<field name="FLAG_EXTERNAL_STORAGE"
- type="int"
- transient="false"
- volatile="false"
- value="262144"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="FLAG_FACTORY_TEST"
  type="int"
  transient="false"
@@ -44144,16 +42849,6 @@
  visibility="public"
 >
 </field>
-<field name="nativeLibraryDir"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="permission"
  type="java.lang.String"
  transient="false"
@@ -45012,16 +43707,6 @@
  visibility="public"
 >
 </field>
-<field name="firstInstallTime"
- type="long"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="gids"
  type="int[]"
  transient="false"
@@ -45044,16 +43729,6 @@
  visibility="public"
 >
 </field>
-<field name="lastUpdateTime"
- type="long"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="packageName"
  type="java.lang.String"
  transient="false"
@@ -45274,19 +43949,6 @@
 <parameter name="pm" type="android.content.pm.PackageManager">
 </parameter>
 </method>
-<method name="loadLogo"
- return="android.graphics.drawable.Drawable"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pm" type="android.content.pm.PackageManager">
-</parameter>
-</method>
 <method name="loadXmlMetaData"
  return="android.content.res.XmlResourceParser"
  abstract="false"
@@ -45337,16 +43999,6 @@
  visibility="public"
 >
 </field>
-<field name="logo"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="metaData"
  type="android.os.Bundle"
  transient="false"
@@ -45622,43 +44274,13 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="component" type="android.content.ComponentName">
+<parameter name="className" type="android.content.ComponentName">
 </parameter>
 <parameter name="flags" type="int">
 </parameter>
 <exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
 </exception>
 </method>
-<method name="getActivityLogo"
- return="android.graphics.drawable.Drawable"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="activityName" type="android.content.ComponentName">
-</parameter>
-<exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
-</exception>
-</method>
-<method name="getActivityLogo"
- return="android.graphics.drawable.Drawable"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="intent" type="android.content.Intent">
-</parameter>
-<exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
-</exception>
-</method>
 <method name="getAllPermissionGroups"
  return="java.util.List&lt;android.content.pm.PermissionGroupInfo&gt;"
  abstract="true"
@@ -45743,34 +44365,6 @@
 <parameter name="info" type="android.content.pm.ApplicationInfo">
 </parameter>
 </method>
-<method name="getApplicationLogo"
- return="android.graphics.drawable.Drawable"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="info" type="android.content.pm.ApplicationInfo">
-</parameter>
-</method>
-<method name="getApplicationLogo"
- return="android.graphics.drawable.Drawable"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="packageName" type="java.lang.String">
-</parameter>
-<exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
-</exception>
-</method>
 <method name="getComponentEnabledSetting"
  return="int"
  abstract="true"
@@ -46018,23 +44612,6 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
-<method name="getProviderInfo"
- return="android.content.pm.ProviderInfo"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="component" type="android.content.ComponentName">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-<exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
-</exception>
-</method>
 <method name="getReceiverInfo"
  return="android.content.pm.ActivityInfo"
  abstract="true"
@@ -46045,7 +44622,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="component" type="android.content.ComponentName">
+<parameter name="className" type="android.content.ComponentName">
 </parameter>
 <parameter name="flags" type="int">
 </parameter>
@@ -46107,7 +44684,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="component" type="android.content.ComponentName">
+<parameter name="className" type="android.content.ComponentName">
 </parameter>
 <parameter name="flags" type="int">
 </parameter>
@@ -46456,17 +45033,6 @@
  visibility="public"
 >
 </field>
-<field name="FEATURE_AUDIO_LOW_LATENCY"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.hardware.audio.low_latency&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="FEATURE_BLUETOOTH"
  type="java.lang.String"
  transient="false"
@@ -46511,17 +45077,6 @@
  visibility="public"
 >
 </field>
-<field name="FEATURE_CAMERA_FRONT"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.hardware.camera.front&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="FEATURE_LIVE_WALLPAPER"
  type="java.lang.String"
  transient="false"
@@ -46577,17 +45132,6 @@
  visibility="public"
 >
 </field>
-<field name="FEATURE_NFC"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.hardware.nfc&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="FEATURE_SENSOR_ACCELEROMETER"
  type="java.lang.String"
  transient="false"
@@ -46599,17 +45143,6 @@
  visibility="public"
 >
 </field>
-<field name="FEATURE_SENSOR_BAROMETER"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.hardware.sensor.barometer&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="FEATURE_SENSOR_COMPASS"
  type="java.lang.String"
  transient="false"
@@ -46621,17 +45154,6 @@
  visibility="public"
 >
 </field>
-<field name="FEATURE_SENSOR_GYROSCOPE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.hardware.sensor.gyroscope&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="FEATURE_SENSOR_LIGHT"
  type="java.lang.String"
  transient="false"
@@ -46654,28 +45176,6 @@
  visibility="public"
 >
 </field>
-<field name="FEATURE_SIP"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.software.sip&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="FEATURE_SIP_VOIP"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.software.sip.voip&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="FEATURE_TELEPHONY"
  type="java.lang.String"
  transient="false"
@@ -46742,17 +45242,6 @@
  visibility="public"
 >
 </field>
-<field name="FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.hardware.touchscreen.multitouch.jazzhand&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="FEATURE_WIFI"
  type="java.lang.String"
  transient="false"
@@ -49285,17 +47774,6 @@
  visibility="public"
 >
 </field>
-<field name="SCREENLAYOUT_SIZE_XLARGE"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TOUCHSCREEN_FINGER"
  type="int"
  transient="false"
@@ -49570,128 +48048,6 @@
 >
 </field>
 </class>
-<class name="ObbInfo"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.os.Parcelable">
-</implements>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="writeToParcel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dest" type="android.os.Parcel">
-</parameter>
-<parameter name="parcelableFlags" type="int">
-</parameter>
-</method>
-<field name="CREATOR"
- type="android.os.Parcelable.Creator"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="OBB_OVERLAY"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="filename"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="flags"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="packageName"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="version"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="ObbScanner"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="getObbInfo"
- return="android.content.res.ObbInfo"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="filePath" type="java.lang.String">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-</class>
 <class name="Resources"
  extends="java.lang.Object"
  abstract="false"
@@ -60204,16 +58560,6 @@
 <parameter name="bitmap" type="android.graphics.Bitmap">
 </parameter>
 </constructor>
-<constructor name="Canvas"
- type="android.graphics.Canvas"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="gl" type="javax.microedition.khronos.opengles.GL">
-</parameter>
-</constructor>
 <method name="clipPath"
  return="boolean"
  abstract="false"
@@ -61130,17 +59476,6 @@
 <parameter name="paint" type="android.graphics.Paint">
 </parameter>
 </method>
-<method name="freeGlCaches"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getClipBounds"
  return="boolean"
  abstract="false"
@@ -61187,17 +59522,6 @@
  visibility="public"
 >
 </method>
-<method name="getGL"
- return="javax.microedition.khronos.opengles.GL"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getHeight"
  return="int"
  abstract="false"
@@ -61561,21 +59885,6 @@
 <parameter name="matrix" type="android.graphics.Matrix">
 </parameter>
 </method>
-<method name="setViewport"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="width" type="int">
-</parameter>
-<parameter name="height" type="int">
-</parameter>
-</method>
 <method name="skew"
  return="void"
  abstract="false"
@@ -62548,17 +60857,6 @@
  visibility="public"
 >
 </field>
-<field name="YV12"
- type="int"
- transient="false"
- volatile="false"
- value="842094169"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
 <class name="Interpolator"
  extends="java.lang.Object"
@@ -73082,32 +71380,6 @@
  visibility="public"
 >
 </method>
-<method name="getCameraInfo"
- return="void"
- abstract="false"
- native="true"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cameraId" type="int">
-</parameter>
-<parameter name="cameraInfo" type="android.hardware.Camera.CameraInfo">
-</parameter>
-</method>
-<method name="getNumberOfCameras"
- return="int"
- abstract="false"
- native="true"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getParameters"
  return="android.hardware.Camera.Parameters"
  abstract="false"
@@ -73140,19 +71412,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="cameraId" type="int">
-</parameter>
-</method>
-<method name="open"
- return="android.hardware.Camera"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 </method>
 <method name="reconnect"
  return="void"
@@ -73399,16 +71658,6 @@
  visibility="public"
 >
 </field>
-<field name="CAMERA_ID_DEFAULT"
- type="int"
- transient="false"
- volatile="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
 <interface name="Camera.AutoFocusCallback"
  abstract="true"
@@ -73433,65 +71682,6 @@
 </parameter>
 </method>
 </interface>
-<class name="Camera.CameraInfo"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Camera.CameraInfo"
- type="android.hardware.Camera.CameraInfo"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<field name="CAMERA_FACING_BACK"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CAMERA_FACING_FRONT"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="facing"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="orientation"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <interface name="Camera.ErrorCallback"
  abstract="true"
  static="true"
@@ -73638,19 +71828,6 @@
  visibility="public"
 >
 </method>
-<method name="getFocusDistances"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="output" type="float[]">
-</parameter>
-</method>
 <method name="getFocusMode"
  return="java.lang.String"
  abstract="false"
@@ -73785,19 +71962,6 @@
  visibility="public"
 >
 </method>
-<method name="getPreviewFpsRange"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="range" type="int[]">
-</parameter>
-</method>
 <method name="getPreviewFrameRate"
  return="int"
  abstract="false"
@@ -73805,7 +71969,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </method>
@@ -73919,17 +72083,6 @@
  visibility="public"
 >
 </method>
-<method name="getSupportedPreviewFpsRange"
- return="java.util.List&lt;int[]&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getSupportedPreviewFrameRates"
  return="java.util.List&lt;java.lang.Integer&gt;"
  abstract="false"
@@ -73937,7 +72090,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </method>
@@ -74306,21 +72459,6 @@
 <parameter name="pixel_format" type="int">
 </parameter>
 </method>
-<method name="setPreviewFpsRange"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="min" type="int">
-</parameter>
-<parameter name="max" type="int">
-</parameter>
-</method>
 <method name="setPreviewFrameRate"
  return="void"
  abstract="false"
@@ -74328,7 +72466,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 <parameter name="fps" type="int">
@@ -74612,39 +72750,6 @@
  visibility="public"
 >
 </field>
-<field name="FOCUS_DISTANCE_FAR_INDEX"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="FOCUS_DISTANCE_NEAR_INDEX"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="FOCUS_DISTANCE_OPTIMAL_INDEX"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="FOCUS_MODE_AUTO"
  type="java.lang.String"
  transient="false"
@@ -74656,17 +72761,6 @@
  visibility="public"
 >
 </field>
-<field name="FOCUS_MODE_CONTINUOUS_VIDEO"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;continuous-video&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="FOCUS_MODE_EDOF"
  type="java.lang.String"
  transient="false"
@@ -74711,28 +72805,6 @@
  visibility="public"
 >
 </field>
-<field name="PREVIEW_FPS_MAX_INDEX"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PREVIEW_FPS_MIN_INDEX"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="SCENE_MODE_ACTION"
  type="java.lang.String"
  transient="false"
@@ -75225,17 +73297,6 @@
  visibility="public"
 >
 </method>
-<method name="getMinDelay"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getName"
  return="java.lang.String"
  abstract="false"
@@ -75324,17 +73385,6 @@
  visibility="public"
 >
 </field>
-<field name="TYPE_GRAVITY"
- type="int"
- transient="false"
- volatile="false"
- value="9"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TYPE_GYROSCOPE"
  type="int"
  transient="false"
@@ -75357,17 +73407,6 @@
  visibility="public"
 >
 </field>
-<field name="TYPE_LINEAR_ACCELERATION"
- type="int"
- transient="false"
- volatile="false"
- value="10"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TYPE_MAGNETIC_FIELD"
  type="int"
  transient="false"
@@ -75412,17 +73451,6 @@
  visibility="public"
 >
 </field>
-<field name="TYPE_ROTATION_VECTOR"
- type="int"
- transient="false"
- volatile="false"
- value="11"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TYPE_TEMPERATURE"
  type="int"
  transient="false"
@@ -75567,38 +73595,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<method name="getAltitude"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="p0" type="float">
-</parameter>
-<parameter name="p" type="float">
-</parameter>
-</method>
-<method name="getAngleChange"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="angleChange" type="float[]">
-</parameter>
-<parameter name="R" type="float[]">
-</parameter>
-<parameter name="prevR" type="float[]">
-</parameter>
-</method>
 <method name="getDefaultSensor"
  return="android.hardware.Sensor"
  abstract="false"
@@ -75640,21 +73636,6 @@
 <parameter name="values" type="float[]">
 </parameter>
 </method>
-<method name="getQuaternionFromVector"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="Q" type="float[]">
-</parameter>
-<parameter name="rv" type="float[]">
-</parameter>
-</method>
 <method name="getRotationMatrix"
  return="boolean"
  abstract="false"
@@ -75674,21 +73655,6 @@
 <parameter name="geomagnetic" type="float[]">
 </parameter>
 </method>
-<method name="getRotationMatrixFromVector"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="R" type="float[]">
-</parameter>
-<parameter name="rotationVector" type="float[]">
-</parameter>
-</method>
 <method name="getSensorList"
  return="java.util.List&lt;android.hardware.Sensor&gt;"
  abstract="false"
@@ -76124,7 +74090,7 @@
  type="float"
  transient="false"
  volatile="false"
- value="0.0010f"
+ value="0.001f"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -76208,17 +74174,6 @@
  visibility="public"
 >
 </field>
-<field name="PRESSURE_STANDARD_ATMOSPHERE"
- type="float"
- transient="false"
- volatile="false"
- value="1013.25f"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="RAW_DATA_INDEX"
  type="int"
  transient="false"
@@ -79804,28 +77759,6 @@
  visibility="public"
 >
 </method>
-<method name="getBearingAccuracy"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getHorizontalAccuracy"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getPowerRequirement"
  return="int"
  abstract="false"
@@ -79837,28 +77770,6 @@
  visibility="public"
 >
 </method>
-<method name="getSpeedAccuracy"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getVerticalAccuracy"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="isAltitudeRequired"
  return="boolean"
  abstract="false"
@@ -79929,19 +77840,6 @@
 <parameter name="altitudeRequired" type="boolean">
 </parameter>
 </method>
-<method name="setBearingAccuracy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="accuracy" type="int">
-</parameter>
-</method>
 <method name="setBearingRequired"
  return="void"
  abstract="false"
@@ -79968,19 +77866,6 @@
 <parameter name="costAllowed" type="boolean">
 </parameter>
 </method>
-<method name="setHorizontalAccuracy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="accuracy" type="int">
-</parameter>
-</method>
 <method name="setPowerRequirement"
  return="void"
  abstract="false"
@@ -79994,19 +77879,6 @@
 <parameter name="level" type="int">
 </parameter>
 </method>
-<method name="setSpeedAccuracy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="accuracy" type="int">
-</parameter>
-</method>
 <method name="setSpeedRequired"
  return="void"
  abstract="false"
@@ -80020,19 +77892,6 @@
 <parameter name="speedRequired" type="boolean">
 </parameter>
 </method>
-<method name="setVerticalAccuracy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="accuracy" type="int">
-</parameter>
-</method>
 <method name="writeToParcel"
  return="void"
  abstract="false"
@@ -80070,39 +77929,6 @@
  visibility="public"
 >
 </field>
-<field name="ACCURACY_HIGH"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACCURACY_LOW"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACCURACY_MEDIUM"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="CREATOR"
  type="android.os.Parcelable.Creator"
  transient="false"
@@ -80249,17 +78075,6 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
-<method name="isPresent"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 </class>
 <class name="GpsSatellite"
  extends="java.lang.Object"
@@ -81418,27 +79233,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="minTime" type="long">
-</parameter>
-<parameter name="minDistance" type="float">
-</parameter>
-<parameter name="criteria" type="android.location.Criteria">
-</parameter>
-<parameter name="listener" type="android.location.LocationListener">
-</parameter>
-<parameter name="looper" type="android.os.Looper">
-</parameter>
-</method>
-<method name="requestLocationUpdates"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="provider" type="java.lang.String">
 </parameter>
 <parameter name="minTime" type="long">
@@ -81448,89 +79242,6 @@
 <parameter name="intent" type="android.app.PendingIntent">
 </parameter>
 </method>
-<method name="requestLocationUpdates"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="minTime" type="long">
-</parameter>
-<parameter name="minDistance" type="float">
-</parameter>
-<parameter name="criteria" type="android.location.Criteria">
-</parameter>
-<parameter name="intent" type="android.app.PendingIntent">
-</parameter>
-</method>
-<method name="requestSingleUpdate"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="provider" type="java.lang.String">
-</parameter>
-<parameter name="listener" type="android.location.LocationListener">
-</parameter>
-<parameter name="looper" type="android.os.Looper">
-</parameter>
-</method>
-<method name="requestSingleUpdate"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="criteria" type="android.location.Criteria">
-</parameter>
-<parameter name="listener" type="android.location.LocationListener">
-</parameter>
-<parameter name="looper" type="android.os.Looper">
-</parameter>
-</method>
-<method name="requestSingleUpdate"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="provider" type="java.lang.String">
-</parameter>
-<parameter name="intent" type="android.app.PendingIntent">
-</parameter>
-</method>
-<method name="requestSingleUpdate"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="criteria" type="android.location.Criteria">
-</parameter>
-<parameter name="intent" type="android.app.PendingIntent">
-</parameter>
-</method>
 <method name="sendExtraCommand"
  return="boolean"
  abstract="false"
@@ -81674,17 +79385,6 @@
  visibility="public"
 >
 </field>
-<field name="PROVIDERS_CHANGED_ACTION"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.location.PROVIDERS_CHANGED&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
 <class name="LocationProvider"
  extends="java.lang.Object"
@@ -84101,43 +81801,6 @@
 <exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
 </exception>
 </constructor>
-<constructor name="AudioTrack"
- type="android.media.AudioTrack"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="streamType" type="int">
-</parameter>
-<parameter name="sampleRateInHz" type="int">
-</parameter>
-<parameter name="channelConfig" type="int">
-</parameter>
-<parameter name="audioFormat" type="int">
-</parameter>
-<parameter name="bufferSizeInBytes" type="int">
-</parameter>
-<parameter name="mode" type="int">
-</parameter>
-<parameter name="sessionId" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-</constructor>
-<method name="attachAuxEffect"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effectId" type="int">
-</parameter>
-</method>
 <method name="flush"
  return="void"
  abstract="false"
@@ -84160,17 +81823,6 @@
  visibility="public"
 >
 </method>
-<method name="getAudioSessionId"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getChannelConfiguration"
  return="int"
  abstract="false"
@@ -84392,19 +82044,6 @@
  visibility="public"
 >
 </method>
-<method name="setAuxEffectSendLevel"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="level" type="float">
-</parameter>
-</method>
 <method name="setLoopPoints"
  return="int"
  abstract="false"
@@ -84765,21 +82404,6 @@
 <parameter name="quality" type="int">
 </parameter>
 </method>
-<method name="get"
- return="android.media.CamcorderProfile"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cameraId" type="int">
-</parameter>
-<parameter name="quality" type="int">
-</parameter>
-</method>
 <field name="QUALITY_HIGH"
  type="int"
  transient="false"
@@ -84952,21 +82576,6 @@
 <parameter name="quality" type="int">
 </parameter>
 </method>
-<method name="getJpegEncodingQualityParameter"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cameraId" type="int">
-</parameter>
-<parameter name="quality" type="int">
-</parameter>
-</method>
 <field name="QUALITY_HIGH"
  type="int"
  transient="false"
@@ -85021,19 +82630,6 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </constructor>
-<method name="getAltitude"
- return="double"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="defaultValue" type="double">
-</parameter>
-</method>
 <method name="getAttribute"
  return="java.lang.String"
  abstract="false"
@@ -85272,28 +82868,6 @@
  visibility="public"
 >
 </field>
-<field name="TAG_GPS_ALTITUDE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;GPSAltitude&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TAG_GPS_ALTITUDE_REF"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;GPSAltitudeRef&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TAG_GPS_DATESTAMP"
  type="java.lang.String"
  transient="false"
@@ -85952,19 +83526,6 @@
  visibility="public"
 >
 </constructor>
-<method name="attachAuxEffect"
- return="void"
- abstract="false"
- native="true"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effectId" type="int">
-</parameter>
-</method>
 <method name="create"
  return="android.media.MediaPlayer"
  abstract="false"
@@ -86012,17 +83573,6 @@
 <parameter name="resid" type="int">
 </parameter>
 </method>
-<method name="getAudioSessionId"
- return="int"
- abstract="false"
- native="true"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getCurrentPosition"
  return="int"
  abstract="false"
@@ -86167,23 +83717,6 @@
 <exception name="IllegalStateException" type="java.lang.IllegalStateException">
 </exception>
 </method>
-<method name="setAudioSessionId"
- return="void"
- abstract="false"
- native="true"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="sessionId" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
 <method name="setAudioStreamType"
  return="void"
  abstract="false"
@@ -86197,19 +83730,6 @@
 <parameter name="streamtype" type="int">
 </parameter>
 </method>
-<method name="setAuxEffectSendLevel"
- return="void"
- abstract="false"
- native="true"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="level" type="float">
-</parameter>
-</method>
 <method name="setDataSource"
  return="void"
  abstract="false"
@@ -86524,28 +84044,6 @@
  visibility="public"
 >
 </field>
-<field name="MEDIA_INFO_BUFFERING_END"
- type="int"
- transient="false"
- volatile="false"
- value="702"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MEDIA_INFO_BUFFERING_START"
- type="int"
- transient="false"
- volatile="false"
- value="701"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="MEDIA_INFO_METADATA_UPDATE"
  type="int"
  transient="false"
@@ -89740,2677 +87238,6 @@
 </field>
 </class>
 </package>
-<package name="android.media.audiofx"
->
-<class name="AudioEffect"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="getDescriptor"
- return="android.media.audiofx.AudioEffect.Descriptor"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getEnabled"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getId"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="hasControl"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="queryEffects"
- return="android.media.audiofx.AudioEffect.Descriptor[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="release"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setControlStatusListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.audiofx.AudioEffect.OnControlStatusChangeListener">
-</parameter>
-</method>
-<method name="setEnableStatusListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.audiofx.AudioEffect.OnEnableStatusChangeListener">
-</parameter>
-</method>
-<method name="setEnabled"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="enabled" type="boolean">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<field name="ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ALREADY_EXISTS"
- type="int"
- transient="false"
- volatile="false"
- value="-2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CONTENT_TYPE_GAME"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CONTENT_TYPE_MOVIE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CONTENT_TYPE_MUSIC"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CONTENT_TYPE_VOICE"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_AUXILIARY"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;Auxiliary&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EFFECT_INSERT"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;Insert&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="-1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_BAD_VALUE"
- type="int"
- transient="false"
- volatile="false"
- value="-4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_DEAD_OBJECT"
- type="int"
- transient="false"
- volatile="false"
- value="-7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_INVALID_OPERATION"
- type="int"
- transient="false"
- volatile="false"
- value="-5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_NO_INIT"
- type="int"
- transient="false"
- volatile="false"
- value="-3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_NO_MEMORY"
- type="int"
- transient="false"
- volatile="false"
- value="-6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_AUDIO_SESSION"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.extra.AUDIO_SESSION&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_CONTENT_TYPE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.extra.CONTENT_TYPE&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_PACKAGE_NAME"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.extra.PACKAGE_NAME&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SUCCESS"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="AudioEffect.Descriptor"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="AudioEffect.Descriptor"
- type="android.media.audiofx.AudioEffect.Descriptor"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="AudioEffect.Descriptor"
- type="android.media.audiofx.AudioEffect.Descriptor"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="type" type="java.lang.String">
-</parameter>
-<parameter name="uuid" type="java.lang.String">
-</parameter>
-<parameter name="connectMode" type="java.lang.String">
-</parameter>
-<parameter name="name" type="java.lang.String">
-</parameter>
-<parameter name="implementor" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="connectMode"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="implementor"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="name"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="type"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="uuid"
- type="java.util.UUID"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="AudioEffect.OnControlStatusChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onControlStatusChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.audiofx.AudioEffect">
-</parameter>
-<parameter name="controlGranted" type="boolean">
-</parameter>
-</method>
-</interface>
-<interface name="AudioEffect.OnEnableStatusChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onEnableStatusChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.audiofx.AudioEffect">
-</parameter>
-<parameter name="enabled" type="boolean">
-</parameter>
-</method>
-</interface>
-<class name="BassBoost"
- extends="android.media.audiofx.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="BassBoost"
- type="android.media.audiofx.BassBoost"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getProperties"
- return="android.media.audiofx.BassBoost.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getRoundedStrength"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getStrengthSupported"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.audiofx.BassBoost.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.audiofx.BassBoost.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setStrength"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="strength" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_STRENGTH"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_STRENGTH_SUPPORTED"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="BassBoost.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.audiofx.BassBoost">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="short">
-</parameter>
-</method>
-</interface>
-<class name="BassBoost.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="BassBoost.Settings"
- type="android.media.audiofx.BassBoost.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="BassBoost.Settings"
- type="android.media.audiofx.BassBoost.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="strength"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="EnvironmentalReverb"
- extends="android.media.audiofx.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="EnvironmentalReverb"
- type="android.media.audiofx.EnvironmentalReverb"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getDecayHFRatio"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getDecayTime"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getDensity"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getDiffusion"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getProperties"
- return="android.media.audiofx.EnvironmentalReverb.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getReflectionsDelay"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getReflectionsLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getReverbDelay"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getReverbLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getRoomHFLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getRoomLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setDecayHFRatio"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="decayHFRatio" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setDecayTime"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="decayTime" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setDensity"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="density" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setDiffusion"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="diffusion" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.audiofx.EnvironmentalReverb.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.audiofx.EnvironmentalReverb.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setReflectionsDelay"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reflectionsDelay" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setReflectionsLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reflectionsLevel" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setReverbDelay"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reverbDelay" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setReverbLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reverbLevel" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setRoomHFLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="roomHF" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setRoomLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="room" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_DECAY_HF_RATIO"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_DECAY_TIME"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_DENSITY"
- type="int"
- transient="false"
- volatile="false"
- value="9"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_DIFFUSION"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_REFLECTIONS_DELAY"
- type="int"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_REFLECTIONS_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_REVERB_DELAY"
- type="int"
- transient="false"
- volatile="false"
- value="7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_REVERB_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_ROOM_HF_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_ROOM_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="EnvironmentalReverb.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.audiofx.EnvironmentalReverb">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="int">
-</parameter>
-</method>
-</interface>
-<class name="EnvironmentalReverb.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="EnvironmentalReverb.Settings"
- type="android.media.audiofx.EnvironmentalReverb.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="EnvironmentalReverb.Settings"
- type="android.media.audiofx.EnvironmentalReverb.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="decayHFRatio"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="decayTime"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="density"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="diffusion"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="reflectionsDelay"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="reflectionsLevel"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="reverbDelay"
- type="int"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="reverbLevel"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="roomHFLevel"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="roomLevel"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="Equalizer"
- extends="android.media.audiofx.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Equalizer"
- type="android.media.audiofx.Equalizer"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getBand"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="frequency" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getBandFreqRange"
- return="int[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="band" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getBandLevel"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="band" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getBandLevelRange"
- return="short[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getCenterFreq"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="band" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getCurrentPreset"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getNumberOfBands"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getNumberOfPresets"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getPresetName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="preset" type="short">
-</parameter>
-</method>
-<method name="getProperties"
- return="android.media.audiofx.Equalizer.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setBandLevel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="band" type="short">
-</parameter>
-<parameter name="level" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.audiofx.Equalizer.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.audiofx.Equalizer.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="usePreset"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="preset" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_BAND_FREQ_RANGE"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_BAND_LEVEL"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_CENTER_FREQ"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_CURRENT_PRESET"
- type="int"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_GET_BAND"
- type="int"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_GET_NUM_OF_PRESETS"
- type="int"
- transient="false"
- volatile="false"
- value="7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_GET_PRESET_NAME"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_LEVEL_RANGE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_NUM_BANDS"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_STRING_SIZE_MAX"
- type="int"
- transient="false"
- volatile="false"
- value="32"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="Equalizer.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.audiofx.Equalizer">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param1" type="int">
-</parameter>
-<parameter name="param2" type="int">
-</parameter>
-<parameter name="value" type="int">
-</parameter>
-</method>
-</interface>
-<class name="Equalizer.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Equalizer.Settings"
- type="android.media.audiofx.Equalizer.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="Equalizer.Settings"
- type="android.media.audiofx.Equalizer.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="bandLevels"
- type="short[]"
- transient="false"
- volatile="false"
- value="null"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="curPreset"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="numBands"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="PresetReverb"
- extends="android.media.audiofx.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="PresetReverb"
- type="android.media.audiofx.PresetReverb"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getPreset"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getProperties"
- return="android.media.audiofx.PresetReverb.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.audiofx.PresetReverb.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setPreset"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="preset" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.audiofx.PresetReverb.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_PRESET"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_LARGEHALL"
- type="short"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_LARGEROOM"
- type="short"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_MEDIUMHALL"
- type="short"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_MEDIUMROOM"
- type="short"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_NONE"
- type="short"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_PLATE"
- type="short"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PRESET_SMALLROOM"
- type="short"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="PresetReverb.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.audiofx.PresetReverb">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="short">
-</parameter>
-</method>
-</interface>
-<class name="PresetReverb.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="PresetReverb.Settings"
- type="android.media.audiofx.PresetReverb.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="PresetReverb.Settings"
- type="android.media.audiofx.PresetReverb.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="preset"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="Virtualizer"
- extends="android.media.audiofx.AudioEffect"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Virtualizer"
- type="android.media.audiofx.Virtualizer"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="priority" type="int">
-</parameter>
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getProperties"
- return="android.media.audiofx.Virtualizer.Settings"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getRoundedStrength"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="getStrengthSupported"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setParameterListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.audiofx.Virtualizer.OnParameterChangeListener">
-</parameter>
-</method>
-<method name="setProperties"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="android.media.audiofx.Virtualizer.Settings">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<method name="setStrength"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="strength" type="short">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</method>
-<field name="PARAM_STRENGTH"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PARAM_STRENGTH_SUPPORTED"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="Virtualizer.OnParameterChangeListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onParameterChange"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="effect" type="android.media.audiofx.Virtualizer">
-</parameter>
-<parameter name="status" type="int">
-</parameter>
-<parameter name="param" type="int">
-</parameter>
-<parameter name="value" type="short">
-</parameter>
-</method>
-</interface>
-<class name="Virtualizer.Settings"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Virtualizer.Settings"
- type="android.media.audiofx.Virtualizer.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="Virtualizer.Settings"
- type="android.media.audiofx.Virtualizer.Settings"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="settings" type="java.lang.String">
-</parameter>
-</constructor>
-<field name="strength"
- type="short"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="Visualizer"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="Visualizer"
- type="android.media.audiofx.Visualizer"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="audioSession" type="int">
-</parameter>
-<exception name="RuntimeException" type="java.lang.RuntimeException">
-</exception>
-<exception name="UnsupportedOperationException" type="java.lang.UnsupportedOperationException">
-</exception>
-</constructor>
-<method name="getCaptureSize"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getCaptureSizeRange"
- return="int[]"
- abstract="false"
- native="true"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getEnabled"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getFft"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fft" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getMaxCaptureRate"
- return="int"
- abstract="false"
- native="true"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getSamplingRate"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="getWaveForm"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="waveform" type="byte[]">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="release"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setCaptureSize"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="size" type="int">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<method name="setDataCaptureListener"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.media.audiofx.Visualizer.OnDataCaptureListener">
-</parameter>
-<parameter name="rate" type="int">
-</parameter>
-<parameter name="waveform" type="boolean">
-</parameter>
-<parameter name="fft" type="boolean">
-</parameter>
-</method>
-<method name="setEnabled"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="enabled" type="boolean">
-</parameter>
-<exception name="IllegalStateException" type="java.lang.IllegalStateException">
-</exception>
-</method>
-<field name="ALREADY_EXISTS"
- type="int"
- transient="false"
- volatile="false"
- value="-2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="-1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_BAD_VALUE"
- type="int"
- transient="false"
- volatile="false"
- value="-4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_DEAD_OBJECT"
- type="int"
- transient="false"
- volatile="false"
- value="-7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_INVALID_OPERATION"
- type="int"
- transient="false"
- volatile="false"
- value="-5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_NO_INIT"
- type="int"
- transient="false"
- volatile="false"
- value="-3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_NO_MEMORY"
- type="int"
- transient="false"
- volatile="false"
- value="-6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATE_ENABLED"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATE_INITIALIZED"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="STATE_UNINITIALIZED"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SUCCESS"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="Visualizer.OnDataCaptureListener"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onFftDataCapture"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="visualizer" type="android.media.audiofx.Visualizer">
-</parameter>
-<parameter name="fft" type="byte[]">
-</parameter>
-<parameter name="samplingRate" type="int">
-</parameter>
-</method>
-<method name="onWaveFormDataCapture"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="visualizer" type="android.media.audiofx.Visualizer">
-</parameter>
-<parameter name="waveform" type="byte[]">
-</parameter>
-<parameter name="samplingRate" type="int">
-</parameter>
-</method>
-</interface>
-</package>
 <package name="android.net"
 >
 <class name="ConnectivityManager"
@@ -93860,7 +88687,7 @@
  type="android.net.SSLCertificateSocketFactory"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="handshakeTimeoutMillis" type="int">
@@ -95960,7 +90787,7 @@
  type="android.net.http.SslCertificate"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="issuedTo" type="java.lang.String">
@@ -96027,7 +90854,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -96049,7 +90876,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -96291,1900 +91118,6 @@
 </field>
 </class>
 </package>
-<package name="android.net.sip"
->
-<class name="SipAudioCall"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SipAudioCall"
- type="android.net.sip.SipAudioCall"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="localProfile" type="android.net.sip.SipProfile">
-</parameter>
-</constructor>
-<method name="answerCall"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="timeout" type="int">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="attachCall"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-<parameter name="sessionDescription" type="java.lang.String">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="close"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="continueCall"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="timeout" type="int">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="endCall"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="getLocalProfile"
- return="android.net.sip.SipProfile"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getPeerProfile"
- return="android.net.sip.SipProfile"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getState"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="holdCall"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="timeout" type="int">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="isInCall"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isMuted"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isOnHold"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="makeCall"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="peerProfile" type="android.net.sip.SipProfile">
-</parameter>
-<parameter name="sipSession" type="android.net.sip.SipSession">
-</parameter>
-<parameter name="timeout" type="int">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="sendDtmf"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="code" type="int">
-</parameter>
-</method>
-<method name="sendDtmf"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="code" type="int">
-</parameter>
-<parameter name="result" type="android.os.Message">
-</parameter>
-</method>
-<method name="setListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.net.sip.SipAudioCall.Listener">
-</parameter>
-</method>
-<method name="setListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.net.sip.SipAudioCall.Listener">
-</parameter>
-<parameter name="callbackImmediately" type="boolean">
-</parameter>
-</method>
-<method name="setSpeakerMode"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="speakerMode" type="boolean">
-</parameter>
-</method>
-<method name="startAudio"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="toggleMute"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
-<class name="SipAudioCall.Listener"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SipAudioCall.Listener"
- type="android.net.sip.SipAudioCall.Listener"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="onCallBusy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="call" type="android.net.sip.SipAudioCall">
-</parameter>
-</method>
-<method name="onCallEnded"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="call" type="android.net.sip.SipAudioCall">
-</parameter>
-</method>
-<method name="onCallEstablished"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="call" type="android.net.sip.SipAudioCall">
-</parameter>
-</method>
-<method name="onCallHeld"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="call" type="android.net.sip.SipAudioCall">
-</parameter>
-</method>
-<method name="onCalling"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="call" type="android.net.sip.SipAudioCall">
-</parameter>
-</method>
-<method name="onChanged"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="call" type="android.net.sip.SipAudioCall">
-</parameter>
-</method>
-<method name="onError"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="call" type="android.net.sip.SipAudioCall">
-</parameter>
-<parameter name="errorCode" type="int">
-</parameter>
-<parameter name="errorMessage" type="java.lang.String">
-</parameter>
-</method>
-<method name="onReadyToCall"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="call" type="android.net.sip.SipAudioCall">
-</parameter>
-</method>
-<method name="onRinging"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="call" type="android.net.sip.SipAudioCall">
-</parameter>
-<parameter name="caller" type="android.net.sip.SipProfile">
-</parameter>
-</method>
-<method name="onRingingBack"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="call" type="android.net.sip.SipAudioCall">
-</parameter>
-</method>
-</class>
-<class name="SipErrorCode"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="toString"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="errorCode" type="int">
-</parameter>
-</method>
-<field name="CLIENT_ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="-4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CROSS_DOMAIN_AUTHENTICATION"
- type="int"
- transient="false"
- volatile="false"
- value="-11"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="DATA_CONNECTION_LOST"
- type="int"
- transient="false"
- volatile="false"
- value="-10"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="INVALID_CREDENTIALS"
- type="int"
- transient="false"
- volatile="false"
- value="-8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="INVALID_REMOTE_URI"
- type="int"
- transient="false"
- volatile="false"
- value="-6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="IN_PROGRESS"
- type="int"
- transient="false"
- volatile="false"
- value="-9"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="NO_ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PEER_NOT_REACHABLE"
- type="int"
- transient="false"
- volatile="false"
- value="-7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SERVER_ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="-2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SERVER_UNREACHABLE"
- type="int"
- transient="false"
- volatile="false"
- value="-12"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOCKET_ERROR"
- type="int"
- transient="false"
- volatile="false"
- value="-1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TIME_OUT"
- type="int"
- transient="false"
- volatile="false"
- value="-5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSACTION_TERMINTED"
- type="int"
- transient="false"
- volatile="false"
- value="-3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="SipException"
- extends="java.lang.Exception"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SipException"
- type="android.net.sip.SipException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SipException"
- type="android.net.sip.SipException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="message" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SipException"
- type="android.net.sip.SipException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="message" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
-<class name="SipManager"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="close"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfileUri" type="java.lang.String">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="createSipSession"
- return="android.net.sip.SipSession"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfile" type="android.net.sip.SipProfile">
-</parameter>
-<parameter name="listener" type="android.net.sip.SipSession.Listener">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="getCallId"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="incomingCallIntent" type="android.content.Intent">
-</parameter>
-</method>
-<method name="getOfferSessionDescription"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="incomingCallIntent" type="android.content.Intent">
-</parameter>
-</method>
-<method name="getSessionFor"
- return="android.net.sip.SipSession"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="incomingCallIntent" type="android.content.Intent">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="isApiSupported"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-</method>
-<method name="isIncomingCallIntent"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="intent" type="android.content.Intent">
-</parameter>
-</method>
-<method name="isOpened"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfileUri" type="java.lang.String">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="isRegistered"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfileUri" type="java.lang.String">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="isSipWifiOnly"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-</method>
-<method name="isVoipSupported"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-</method>
-<method name="makeAudioCall"
- return="android.net.sip.SipAudioCall"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfile" type="android.net.sip.SipProfile">
-</parameter>
-<parameter name="peerProfile" type="android.net.sip.SipProfile">
-</parameter>
-<parameter name="listener" type="android.net.sip.SipAudioCall.Listener">
-</parameter>
-<parameter name="timeout" type="int">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="makeAudioCall"
- return="android.net.sip.SipAudioCall"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfileUri" type="java.lang.String">
-</parameter>
-<parameter name="peerProfileUri" type="java.lang.String">
-</parameter>
-<parameter name="listener" type="android.net.sip.SipAudioCall.Listener">
-</parameter>
-<parameter name="timeout" type="int">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="newInstance"
- return="android.net.sip.SipManager"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-</method>
-<method name="open"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfile" type="android.net.sip.SipProfile">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="open"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfile" type="android.net.sip.SipProfile">
-</parameter>
-<parameter name="incomingCallPendingIntent" type="android.app.PendingIntent">
-</parameter>
-<parameter name="listener" type="android.net.sip.SipRegistrationListener">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="register"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfile" type="android.net.sip.SipProfile">
-</parameter>
-<parameter name="expiryTime" type="int">
-</parameter>
-<parameter name="listener" type="android.net.sip.SipRegistrationListener">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="setRegistrationListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfileUri" type="java.lang.String">
-</parameter>
-<parameter name="listener" type="android.net.sip.SipRegistrationListener">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="takeAudioCall"
- return="android.net.sip.SipAudioCall"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="incomingCallIntent" type="android.content.Intent">
-</parameter>
-<parameter name="listener" type="android.net.sip.SipAudioCall.Listener">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<method name="unregister"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfile" type="android.net.sip.SipProfile">
-</parameter>
-<parameter name="listener" type="android.net.sip.SipRegistrationListener">
-</parameter>
-<exception name="SipException" type="android.net.sip.SipException">
-</exception>
-</method>
-<field name="EXTRA_CALL_ID"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android:sipCallID&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_OFFER_SD"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android:sipOfferSD&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="INCOMING_CALL_RESULT_CODE"
- type="int"
- transient="false"
- volatile="false"
- value="101"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="SipProfile"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.lang.Cloneable">
-</implements>
-<implements name="android.os.Parcelable">
-</implements>
-<implements name="java.io.Serializable">
-</implements>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getAutoRegistration"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getDisplayName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getPassword"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getPort"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getProfileName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getProtocol"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getProxyAddress"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getSendKeepAlive"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getSipDomain"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getUriString"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getUserName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="writeToParcel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="out" type="android.os.Parcel">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<field name="CREATOR"
- type="android.os.Parcelable.Creator"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="SipProfile.Builder"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SipProfile.Builder"
- type="android.net.sip.SipProfile.Builder"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="profile" type="android.net.sip.SipProfile">
-</parameter>
-</constructor>
-<constructor name="SipProfile.Builder"
- type="android.net.sip.SipProfile.Builder"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uriString" type="java.lang.String">
-</parameter>
-<exception name="ParseException" type="java.text.ParseException">
-</exception>
-</constructor>
-<constructor name="SipProfile.Builder"
- type="android.net.sip.SipProfile.Builder"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="username" type="java.lang.String">
-</parameter>
-<parameter name="serverDomain" type="java.lang.String">
-</parameter>
-<exception name="ParseException" type="java.text.ParseException">
-</exception>
-</constructor>
-<method name="build"
- return="android.net.sip.SipProfile"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setAutoRegistration"
- return="android.net.sip.SipProfile.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="flag" type="boolean">
-</parameter>
-</method>
-<method name="setDisplayName"
- return="android.net.sip.SipProfile.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="displayName" type="java.lang.String">
-</parameter>
-</method>
-<method name="setOutboundProxy"
- return="android.net.sip.SipProfile.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="outboundProxy" type="java.lang.String">
-</parameter>
-</method>
-<method name="setPassword"
- return="android.net.sip.SipProfile.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="password" type="java.lang.String">
-</parameter>
-</method>
-<method name="setPort"
- return="android.net.sip.SipProfile.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="port" type="int">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-</method>
-<method name="setProfileName"
- return="android.net.sip.SipProfile.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="name" type="java.lang.String">
-</parameter>
-</method>
-<method name="setProtocol"
- return="android.net.sip.SipProfile.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="protocol" type="java.lang.String">
-</parameter>
-<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
-</exception>
-</method>
-<method name="setSendKeepAlive"
- return="android.net.sip.SipProfile.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="flag" type="boolean">
-</parameter>
-</method>
-</class>
-<interface name="SipRegistrationListener"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onRegistering"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfileUri" type="java.lang.String">
-</parameter>
-</method>
-<method name="onRegistrationDone"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfileUri" type="java.lang.String">
-</parameter>
-<parameter name="expiryTime" type="long">
-</parameter>
-</method>
-<method name="onRegistrationFailed"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="localProfileUri" type="java.lang.String">
-</parameter>
-<parameter name="errorCode" type="int">
-</parameter>
-<parameter name="errorMessage" type="java.lang.String">
-</parameter>
-</method>
-</interface>
-<class name="SipSession"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="answerCall"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="sessionDescription" type="java.lang.String">
-</parameter>
-<parameter name="timeout" type="int">
-</parameter>
-</method>
-<method name="changeCall"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="sessionDescription" type="java.lang.String">
-</parameter>
-<parameter name="timeout" type="int">
-</parameter>
-</method>
-<method name="endCall"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCallId"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getLocalIp"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getLocalProfile"
- return="android.net.sip.SipProfile"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getPeerProfile"
- return="android.net.sip.SipProfile"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getState"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isInCall"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="makeCall"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="callee" type="android.net.sip.SipProfile">
-</parameter>
-<parameter name="sessionDescription" type="java.lang.String">
-</parameter>
-<parameter name="timeout" type="int">
-</parameter>
-</method>
-<method name="register"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="duration" type="int">
-</parameter>
-</method>
-<method name="setListener"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="android.net.sip.SipSession.Listener">
-</parameter>
-</method>
-<method name="unregister"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
-<class name="SipSession.Listener"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SipSession.Listener"
- type="android.net.sip.SipSession.Listener"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="onCallBusy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-</method>
-<method name="onCallChangeFailed"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-<parameter name="errorCode" type="int">
-</parameter>
-<parameter name="errorMessage" type="java.lang.String">
-</parameter>
-</method>
-<method name="onCallEnded"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-</method>
-<method name="onCallEstablished"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-<parameter name="sessionDescription" type="java.lang.String">
-</parameter>
-</method>
-<method name="onCalling"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-</method>
-<method name="onError"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-<parameter name="errorCode" type="int">
-</parameter>
-<parameter name="errorMessage" type="java.lang.String">
-</parameter>
-</method>
-<method name="onRegistering"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-</method>
-<method name="onRegistrationDone"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-<parameter name="duration" type="int">
-</parameter>
-</method>
-<method name="onRegistrationFailed"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-<parameter name="errorCode" type="int">
-</parameter>
-<parameter name="errorMessage" type="java.lang.String">
-</parameter>
-</method>
-<method name="onRegistrationTimeout"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-</method>
-<method name="onRinging"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-<parameter name="caller" type="android.net.sip.SipProfile">
-</parameter>
-<parameter name="sessionDescription" type="java.lang.String">
-</parameter>
-</method>
-<method name="onRingingBack"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="session" type="android.net.sip.SipSession">
-</parameter>
-</method>
-</class>
-<class name="SipSession.State"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="toString"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="state" type="int">
-</parameter>
-</method>
-<field name="DEREGISTERING"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="INCOMING_CALL"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="INCOMING_CALL_ANSWERING"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="IN_CALL"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="NOT_DEFINED"
- type="int"
- transient="false"
- volatile="false"
- value="101"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="OUTGOING_CALL"
- type="int"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="OUTGOING_CALL_CANCELING"
- type="int"
- transient="false"
- volatile="false"
- value="7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="OUTGOING_CALL_RING_BACK"
- type="int"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="PINGING"
- type="int"
- transient="false"
- volatile="false"
- value="9"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="READY_TO_CALL"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="REGISTERING"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-</package>
 <package name="android.net.wifi"
 >
 <class name="ScanResult"
@@ -99743,488 +92676,6 @@
 <parameter name="refCounted" type="boolean">
 </parameter>
 </method>
-<method name="setWorkSource"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="ws" type="android.os.WorkSource">
-</parameter>
-</method>
-</class>
-</package>
-<package name="android.nfc"
->
-<class name="FormatException"
- extends="java.lang.Exception"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="FormatException"
- type="android.nfc.FormatException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="FormatException"
- type="android.nfc.FormatException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="message" type="java.lang.String">
-</parameter>
-</constructor>
-</class>
-<class name="NdefMessage"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.os.Parcelable">
-</implements>
-<constructor name="NdefMessage"
- type="android.nfc.NdefMessage"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="data" type="byte[]">
-</parameter>
-<exception name="FormatException" type="android.nfc.FormatException">
-</exception>
-</constructor>
-<constructor name="NdefMessage"
- type="android.nfc.NdefMessage"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="records" type="android.nfc.NdefRecord[]">
-</parameter>
-</constructor>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getRecords"
- return="android.nfc.NdefRecord[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="toByteArray"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="writeToParcel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dest" type="android.os.Parcel">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<field name="CREATOR"
- type="android.os.Parcelable.Creator"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="NdefRecord"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.os.Parcelable">
-</implements>
-<constructor name="NdefRecord"
- type="android.nfc.NdefRecord"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="tnf" type="short">
-</parameter>
-<parameter name="type" type="byte[]">
-</parameter>
-<parameter name="id" type="byte[]">
-</parameter>
-<parameter name="payload" type="byte[]">
-</parameter>
-</constructor>
-<constructor name="NdefRecord"
- type="android.nfc.NdefRecord"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="data" type="byte[]">
-</parameter>
-<exception name="FormatException" type="android.nfc.FormatException">
-</exception>
-</constructor>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getId"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getPayload"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getTnf"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getType"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="toByteArray"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="writeToParcel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dest" type="android.os.Parcel">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<field name="CREATOR"
- type="android.os.Parcelable.Creator"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="RTD_ALTERNATIVE_CARRIER"
- type="byte[]"
- transient="false"
- volatile="false"
- value="null"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="RTD_HANDOVER_CARRIER"
- type="byte[]"
- transient="false"
- volatile="false"
- value="null"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="RTD_HANDOVER_REQUEST"
- type="byte[]"
- transient="false"
- volatile="false"
- value="null"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="RTD_HANDOVER_SELECT"
- type="byte[]"
- transient="false"
- volatile="false"
- value="null"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="RTD_SMART_POSTER"
- type="byte[]"
- transient="false"
- volatile="false"
- value="null"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="RTD_TEXT"
- type="byte[]"
- transient="false"
- volatile="false"
- value="null"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="RTD_URI"
- type="byte[]"
- transient="false"
- volatile="false"
- value="null"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TNF_ABSOLUTE_URI"
- type="short"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TNF_EMPTY"
- type="short"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TNF_EXTERNAL_TYPE"
- type="short"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TNF_MIME_MEDIA"
- type="short"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TNF_UNCHANGED"
- type="short"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TNF_UNKNOWN"
- type="short"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TNF_WELL_KNOWN"
- type="short"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="NfcAdapter"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="getDefaultAdapter"
- return="android.nfc.NfcAdapter"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isEnabled"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<field name="ACTION_TAG_DISCOVERED"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.nfc.action.TAG_DISCOVERED&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_ID"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.nfc.extra.ID&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_NDEF_MESSAGES"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.nfc.extra.NDEF_MESSAGES&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
 </package>
 <package name="android.opengl"
@@ -111884,25 +104335,6 @@
 </parameter>
 <parameter name="type" type="int">
 </parameter>
-<parameter name="offset" type="int">
-</parameter>
-</method>
-<method name="glDrawElements"
- return="void"
- abstract="false"
- native="true"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="mode" type="int">
-</parameter>
-<parameter name="count" type="int">
-</parameter>
-<parameter name="type" type="int">
-</parameter>
 <parameter name="indices" type="java.nio.Buffer">
 </parameter>
 </method>
@@ -114321,29 +106753,6 @@
 <method name="glVertexAttribPointer"
  return="void"
  abstract="false"
- native="true"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="indx" type="int">
-</parameter>
-<parameter name="size" type="int">
-</parameter>
-<parameter name="type" type="int">
-</parameter>
-<parameter name="normalized" type="boolean">
-</parameter>
-<parameter name="stride" type="int">
-</parameter>
-<parameter name="offset" type="int">
-</parameter>
-</method>
-<method name="glVertexAttribPointer"
- return="void"
- abstract="false"
  native="false"
  synchronized="false"
  static="true"
@@ -119987,16 +112396,6 @@
  visibility="public"
 >
 </field>
-<field name="SERIAL"
- type="java.lang.String"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TAGS"
  type="java.lang.String"
  transient="false"
@@ -120231,17 +112630,6 @@
  visibility="public"
 >
 </field>
-<field name="GINGERBREAD"
- type="int"
- transient="false"
- volatile="false"
- value="9"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
 <class name="Bundle"
  extends="java.lang.Object"
@@ -122740,8 +115128,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<implements name="java.io.Closeable">
-</implements>
 <implements name="android.os.Parcelable">
 </implements>
 <constructor name="DropBoxManager.Entry"
@@ -123011,17 +115397,6 @@
  visibility="public"
 >
 </method>
-<method name="isExternalStorageRemovable"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <field name="DIRECTORY_ALARMS"
  type="java.lang.String"
  transient="false"
@@ -126290,19 +118665,6 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
-<method name="createPipe"
- return="android.os.ParcelFileDescriptor[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
 <method name="describeContents"
  return="int"
  abstract="false"
@@ -127062,19 +119424,6 @@
 <parameter name="value" type="boolean">
 </parameter>
 </method>
-<method name="setWorkSource"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="ws" type="android.os.WorkSource">
-</parameter>
-</method>
 </class>
 <class name="Process"
  extends="java.lang.Object"
@@ -127867,387 +120216,6 @@
 </parameter>
 </method>
 </class>
-<class name="StrictMode"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="allowThreadDiskReads"
- return="android.os.StrictMode.ThreadPolicy"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="allowThreadDiskWrites"
- return="android.os.StrictMode.ThreadPolicy"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="enableDefaults"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getThreadPolicy"
- return="android.os.StrictMode.ThreadPolicy"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getVmPolicy"
- return="android.os.StrictMode.VmPolicy"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setThreadPolicy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="policy" type="android.os.StrictMode.ThreadPolicy">
-</parameter>
-</method>
-<method name="setVmPolicy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="policy" type="android.os.StrictMode.VmPolicy">
-</parameter>
-</method>
-</class>
-<class name="StrictMode.ThreadPolicy"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<field name="LAX"
- type="android.os.StrictMode.ThreadPolicy"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="StrictMode.ThreadPolicy.Builder"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="StrictMode.ThreadPolicy.Builder"
- type="android.os.StrictMode.ThreadPolicy.Builder"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="StrictMode.ThreadPolicy.Builder"
- type="android.os.StrictMode.ThreadPolicy.Builder"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="policy" type="android.os.StrictMode.ThreadPolicy">
-</parameter>
-</constructor>
-<method name="build"
- return="android.os.StrictMode.ThreadPolicy"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="detectAll"
- return="android.os.StrictMode.ThreadPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="detectDiskReads"
- return="android.os.StrictMode.ThreadPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="detectDiskWrites"
- return="android.os.StrictMode.ThreadPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="detectNetwork"
- return="android.os.StrictMode.ThreadPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="penaltyDeath"
- return="android.os.StrictMode.ThreadPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="penaltyDialog"
- return="android.os.StrictMode.ThreadPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="penaltyDropBox"
- return="android.os.StrictMode.ThreadPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="penaltyLog"
- return="android.os.StrictMode.ThreadPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="permitAll"
- return="android.os.StrictMode.ThreadPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="permitDiskReads"
- return="android.os.StrictMode.ThreadPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="permitDiskWrites"
- return="android.os.StrictMode.ThreadPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="permitNetwork"
- return="android.os.StrictMode.ThreadPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
-<class name="StrictMode.VmPolicy"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<field name="LAX"
- type="android.os.StrictMode.VmPolicy"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="StrictMode.VmPolicy.Builder"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="StrictMode.VmPolicy.Builder"
- type="android.os.StrictMode.VmPolicy.Builder"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="build"
- return="android.os.StrictMode.VmPolicy"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="detectAll"
- return="android.os.StrictMode.VmPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="detectLeakedSqlLiteObjects"
- return="android.os.StrictMode.VmPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="penaltyDeath"
- return="android.os.StrictMode.VmPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="penaltyDropBox"
- return="android.os.StrictMode.VmPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="penaltyLog"
- return="android.os.StrictMode.VmPolicy.Builder"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
 <class name="SystemClock"
  extends="java.lang.Object"
  abstract="false"
@@ -128472,326 +120440,6 @@
 </parameter>
 </method>
 </class>
-<class name="WorkSource"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.os.Parcelable">
-</implements>
-<constructor name="WorkSource"
- type="android.os.WorkSource"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="WorkSource"
- type="android.os.WorkSource"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="orig" type="android.os.WorkSource">
-</parameter>
-</constructor>
-<method name="add"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="other" type="android.os.WorkSource">
-</parameter>
-</method>
-<method name="clear"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="diff"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="other" type="android.os.WorkSource">
-</parameter>
-</method>
-<method name="remove"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="other" type="android.os.WorkSource">
-</parameter>
-</method>
-<method name="set"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="other" type="android.os.WorkSource">
-</parameter>
-</method>
-<method name="writeToParcel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="dest" type="android.os.Parcel">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<field name="CREATOR"
- type="android.os.Parcelable.Creator"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-</package>
-<package name="android.os.storage"
->
-<class name="OnObbStateChangeListener"
- extends="java.lang.Object"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="OnObbStateChangeListener"
- type="android.os.storage.OnObbStateChangeListener"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="onObbStateChange"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="path" type="java.lang.String">
-</parameter>
-<parameter name="state" type="int">
-</parameter>
-</method>
-<field name="ERROR_ALREADY_MOUNTED"
- type="int"
- transient="false"
- volatile="false"
- value="24"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_COULD_NOT_MOUNT"
- type="int"
- transient="false"
- volatile="false"
- value="21"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_COULD_NOT_UNMOUNT"
- type="int"
- transient="false"
- volatile="false"
- value="22"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_INTERNAL"
- type="int"
- transient="false"
- volatile="false"
- value="20"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_NOT_MOUNTED"
- type="int"
- transient="false"
- volatile="false"
- value="23"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ERROR_PERMISSION_DENIED"
- type="int"
- transient="false"
- volatile="false"
- value="25"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MOUNTED"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="UNMOUNTED"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="StorageManager"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="getMountedObbPath"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="filename" type="java.lang.String">
-</parameter>
-</method>
-<method name="isObbMounted"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="filename" type="java.lang.String">
-</parameter>
-</method>
-<method name="mountObb"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="filename" type="java.lang.String">
-</parameter>
-<parameter name="key" type="java.lang.String">
-</parameter>
-<parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
-</parameter>
-</method>
-<method name="unmountObb"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="filename" type="java.lang.String">
-</parameter>
-<parameter name="force" type="boolean">
-</parameter>
-<parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
-</parameter>
-</method>
-</class>
 </package>
 <package name="android.preference"
 >
@@ -131420,67 +123068,6 @@
 </package>
 <package name="android.provider"
 >
-<class name="AlarmClock"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="AlarmClock"
- type="android.provider.AlarmClock"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<field name="ACTION_SET_ALARM"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.action.SET_ALARM&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_HOUR"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.extra.alarm.HOUR&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_MESSAGE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.extra.alarm.MESSAGE&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="EXTRA_MINUTES"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.intent.extra.alarm.MINUTES&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <interface name="BaseColumns"
  abstract="true"
  static="false"
@@ -135945,17 +127532,6 @@
  visibility="public"
 >
 </field>
-<field name="TYPE_MAIDEN_NAME"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TYPE_MAINDEN_NAME"
  type="int"
  transient="false"
@@ -135963,7 +127539,7 @@
  value="3"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -136714,104 +128290,6 @@
 >
 </field>
 </class>
-<class name="ContactsContract.CommonDataKinds.SipAddress"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.provider.ContactsContract.CommonDataKinds.CommonColumns">
-</implements>
-<implements name="android.provider.ContactsContract.DataColumnsWithJoins">
-</implements>
-<method name="getTypeLabel"
- return="java.lang.CharSequence"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="res" type="android.content.res.Resources">
-</parameter>
-<parameter name="type" type="int">
-</parameter>
-<parameter name="label" type="java.lang.CharSequence">
-</parameter>
-</method>
-<method name="getTypeLabelResource"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="type" type="int">
-</parameter>
-</method>
-<field name="CONTENT_ITEM_TYPE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;vnd.android.cursor.item/sip_address&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SIP_ADDRESS"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;data1&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TYPE_HOME"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TYPE_OTHER"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TYPE_WORK"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="ContactsContract.CommonDataKinds.StructuredName"
  extends="java.lang.Object"
  abstract="false"
@@ -140003,17 +131481,6 @@
  visibility="public"
 >
 </field>
-<field name="INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.media.action.MEDIA_PLAY_FROM_SEARCH&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="INTENT_ACTION_MEDIA_SEARCH"
  type="java.lang.String"
  transient="false"
@@ -140058,17 +131525,6 @@
  visibility="public"
 >
 </field>
-<field name="MEDIA_IGNORE_FILENAME"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;.nomedia&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="MEDIA_SCANNER_VOLUME"
  type="java.lang.String"
  transient="false"
@@ -142703,17 +134159,6 @@
  visibility="public"
 >
 </field>
-<field name="ACTION_APPLICATION_DETAILS_SETTINGS"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.settings.APPLICATION_DETAILS_SETTINGS&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="ACTION_APPLICATION_DEVELOPMENT_SETTINGS"
  type="java.lang.String"
  transient="false"
@@ -142835,17 +134280,6 @@
  visibility="public"
 >
 </field>
-<field name="ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;android.settings.MANAGE_ALL_APPLICATIONS_SETTINGS&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="ACTION_MANAGE_APPLICATIONS_SETTINGS"
  type="java.lang.String"
  transient="false"
@@ -146342,19 +137776,6 @@
 <parameter name="holder" type="android.view.SurfaceHolder">
 </parameter>
 </method>
-<method name="onSurfaceRedrawNeeded"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="holder" type="android.view.SurfaceHolder">
-</parameter>
-</method>
 <method name="onTouchEvent"
  return="void"
  abstract="false"
@@ -150682,17 +142103,6 @@
  visibility="public"
 >
 </field>
-<field name="NETWORK_TYPE_EVDO_B"
- type="int"
- transient="false"
- volatile="false"
- value="12"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="NETWORK_TYPE_GPRS"
  type="int"
  transient="false"
@@ -151081,17 +142491,6 @@
  visibility="public"
 >
 </method>
-<method name="getPsc"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="setLacAndCid"
  return="void"
  abstract="false"
@@ -156303,17 +147702,6 @@
  visibility="public"
 >
 </constructor>
-<method name="abortUpdates"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="close"
  return="void"
  abstract="false"
@@ -156325,30 +147713,6 @@
  visibility="public"
 >
 </method>
-<method name="commitUpdates"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="commitUpdates"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="values" type="java.util.Map&lt;? extends java.lang.Long, ? extends java.util.Map&lt;java.lang.String, java.lang.Object&gt;&gt;">
-</parameter>
-</method>
 <method name="copyStringToBuffer"
  return="void"
  abstract="false"
@@ -156375,17 +147739,6 @@
  visibility="public"
 >
 </method>
-<method name="deleteRow"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getBlob"
  return="byte[]"
  abstract="false"
@@ -156582,17 +147935,6 @@
  visibility="public"
 >
 </method>
-<method name="hasUpdates"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="isAfterLast"
  return="boolean"
  abstract="false"
@@ -156796,17 +148138,6 @@
 <parameter name="uri" type="android.net.Uri">
 </parameter>
 </method>
-<method name="supportsUpdates"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="unregisterContentObserver"
  return="void"
  abstract="false"
@@ -156833,124 +148164,6 @@
 <parameter name="observer" type="android.database.DataSetObserver">
 </parameter>
 </method>
-<method name="updateBlob"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-</method>
-<method name="updateDouble"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="double">
-</parameter>
-</method>
-<method name="updateFloat"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="float">
-</parameter>
-</method>
-<method name="updateInt"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="int">
-</parameter>
-</method>
-<method name="updateLong"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="long">
-</parameter>
-</method>
-<method name="updateShort"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="short">
-</parameter>
-</method>
-<method name="updateString"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="java.lang.String">
-</parameter>
-</method>
-<method name="updateToNull"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-</method>
 </class>
 <class name="MockDialogInterface"
  extends="java.lang.Object"
@@ -157198,36 +148411,6 @@
 <exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
 </exception>
 </method>
-<method name="getActivityLogo"
- return="android.graphics.drawable.Drawable"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="activityName" type="android.content.ComponentName">
-</parameter>
-<exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
-</exception>
-</method>
-<method name="getActivityLogo"
- return="android.graphics.drawable.Drawable"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="intent" type="android.content.Intent">
-</parameter>
-<exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
-</exception>
-</method>
 <method name="getAllPermissionGroups"
  return="java.util.List&lt;android.content.pm.PermissionGroupInfo&gt;"
  abstract="false"
@@ -157312,34 +148495,6 @@
 <parameter name="info" type="android.content.pm.ApplicationInfo">
 </parameter>
 </method>
-<method name="getApplicationLogo"
- return="android.graphics.drawable.Drawable"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="info" type="android.content.pm.ApplicationInfo">
-</parameter>
-</method>
-<method name="getApplicationLogo"
- return="android.graphics.drawable.Drawable"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="packageName" type="java.lang.String">
-</parameter>
-<exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
-</exception>
-</method>
 <method name="getComponentEnabledSetting"
  return="int"
  abstract="false"
@@ -157572,23 +148727,6 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
-<method name="getProviderInfo"
- return="android.content.pm.ProviderInfo"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="className" type="android.content.ComponentName">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-<exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
-</exception>
-</method>
 <method name="getReceiverInfo"
  return="android.content.pm.ActivityInfo"
  abstract="false"
@@ -157964,21 +149102,6 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
-<method name="setPackageObbPath"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="packageName" type="java.lang.String">
-</parameter>
-<parameter name="path" type="java.lang.String">
-</parameter>
-</method>
 </class>
 <class name="MockResources"
  extends="android.content.res.Resources"
@@ -164115,29 +155238,6 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
-<method name="formatDateRange"
- return="java.util.Formatter"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="formatter" type="java.util.Formatter">
-</parameter>
-<parameter name="startMillis" type="long">
-</parameter>
-<parameter name="endMillis" type="long">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-<parameter name="timeZone" type="java.lang.String">
-</parameter>
-</method>
 <method name="formatDateTime"
  return="java.lang.String"
  abstract="false"
@@ -164631,7 +155731,7 @@
  value="8192"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -172190,17 +163290,6 @@
  visibility="public"
 >
 </field>
-<field name="DENSITY_XHIGH"
- type="int"
- transient="false"
- volatile="false"
- value="320"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="density"
  type="float"
  transient="false"
@@ -172461,7 +163550,7 @@
  abstract="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <constructor name="EventLogTags"
@@ -173341,7 +164430,7 @@
  type="java.lang.String"
  transient="false"
  volatile="false"
- value="&quot;((aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(biz|b[abdefghijmnorstvwyz])|(cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(edu|e[cegrstu])|f[ijkmor]|(gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(info|int|i[delmnoqrst])|(jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(name|net|n[acefgilopruz])|(org|om)|(pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnortuvyz]|(tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]|w[fs]|(xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)|y[etu]|z[amw])&quot;"
+ value="&quot;((aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(biz|b[abdefghijmnorstvwyz])|(cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(edu|e[cegrstu])|f[ijkmor]|(gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(info|int|i[delmnoqrst])|(jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(name|net|n[acefgilopruz])|(org|om)|(pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnortuvyz]|(tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]|w[fs]|(xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-mgbaam7a8h|xn\\-\\-mgberp4a5d4ar|xn\\-\\-wgbh1c|xn\\-\\-zckzah)|y[et]|z[amw])&quot;"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -173352,7 +164441,7 @@
  type="java.lang.String"
  transient="false"
  volatile="false"
- value="&quot;(?:(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(?:biz|b[abdefghijmnorstvwyz])|(?:cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(?:edu|e[cegrstu])|f[ijkmor]|(?:gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(?:info|int|i[delmnoqrst])|(?:jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(?:mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(?:name|net|n[acefgilopruz])|(?:org|om)|(?:pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnortuvyz]|(?:tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]|w[fs]|(?:xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)|y[etu]|z[amw]))&quot;"
+ value="&quot;(?:(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(?:biz|b[abdefghijmnorstvwyz])|(?:cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(?:edu|e[cegrstu])|f[ijkmor]|(?:gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(?:info|int|i[delmnoqrst])|(?:jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(?:mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(?:name|net|n[acefgilopruz])|(?:org|om)|(?:pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnortuvyz]|(?:tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]|w[fs]|(?:xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-mgbaam7a8h|xn\\-\\-mgberp4a5d4ar|xn\\-\\-wgbh1c|xn\\-\\-zckzah)|y[et]|z[amw]))&quot;"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -176439,484 +167528,6 @@
 </parameter>
 </constructor>
 </class>
-<class name="InputDevice"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.os.Parcelable">
-</implements>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getDevice"
- return="android.view.InputDevice"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="int">
-</parameter>
-</method>
-<method name="getDeviceIds"
- return="int[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getId"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getKeyCharacterMap"
- return="android.view.KeyCharacterMap"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getKeyboardType"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getMotionRange"
- return="android.view.InputDevice.MotionRange"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="rangeType" type="int">
-</parameter>
-</method>
-<method name="getName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getSources"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="writeToParcel"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="out" type="android.os.Parcel">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<field name="CREATOR"
- type="android.os.Parcelable.Creator"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYBOARD_TYPE_ALPHABETIC"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYBOARD_TYPE_NONE"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYBOARD_TYPE_NON_ALPHABETIC"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MOTION_RANGE_ORIENTATION"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MOTION_RANGE_PRESSURE"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MOTION_RANGE_SIZE"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MOTION_RANGE_TOOL_MAJOR"
- type="int"
- transient="false"
- volatile="false"
- value="6"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MOTION_RANGE_TOOL_MINOR"
- type="int"
- transient="false"
- volatile="false"
- value="7"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MOTION_RANGE_TOUCH_MAJOR"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MOTION_RANGE_TOUCH_MINOR"
- type="int"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MOTION_RANGE_X"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MOTION_RANGE_Y"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_ANY"
- type="int"
- transient="false"
- volatile="false"
- value="-256"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_CLASS_BUTTON"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_CLASS_MASK"
- type="int"
- transient="false"
- volatile="false"
- value="255"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_CLASS_POINTER"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_CLASS_POSITION"
- type="int"
- transient="false"
- volatile="false"
- value="8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_CLASS_TRACKBALL"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_DPAD"
- type="int"
- transient="false"
- volatile="false"
- value="513"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_KEYBOARD"
- type="int"
- transient="false"
- volatile="false"
- value="257"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_MOUSE"
- type="int"
- transient="false"
- volatile="false"
- value="8194"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_TOUCHPAD"
- type="int"
- transient="false"
- volatile="false"
- value="1048584"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_TOUCHSCREEN"
- type="int"
- transient="false"
- volatile="false"
- value="4098"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_TRACKBALL"
- type="int"
- transient="false"
- volatile="false"
- value="65540"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="SOURCE_UNKNOWN"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="InputDevice.MotionRange"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="getFlat"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getFuzz"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getMax"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getMin"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getRange"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
 <class name="InputEvent"
  extends="java.lang.Object"
  abstract="true"
@@ -176927,28 +167538,6 @@
 >
 <implements name="android.os.Parcelable">
 </implements>
-<method name="describeContents"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getDevice"
- return="android.view.InputDevice"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getDeviceId"
  return="int"
  abstract="false"
@@ -176960,71 +167549,7 @@
  visibility="public"
 >
 </method>
-<method name="getSource"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<field name="CREATOR"
- type="android.os.Parcelable.Creator"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
-<class name="InputQueue"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</class>
-<interface name="InputQueue.Callback"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="onInputQueueCreated"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="queue" type="android.view.InputQueue">
-</parameter>
-</method>
-<method name="onInputQueueDestroyed"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="queue" type="android.view.InputQueue">
-</parameter>
-</method>
-</interface>
 <class name="KeyCharacterMap"
  extends="java.lang.Object"
  abstract="false"
@@ -177439,7 +167964,7 @@
 </parameter>
 <parameter name="metaState" type="int">
 </parameter>
-<parameter name="deviceId" type="int">
+<parameter name="device" type="int">
 </parameter>
 <parameter name="scancode" type="int">
 </parameter>
@@ -177463,7 +167988,7 @@
 </parameter>
 <parameter name="metaState" type="int">
 </parameter>
-<parameter name="deviceId" type="int">
+<parameter name="device" type="int">
 </parameter>
 <parameter name="scancode" type="int">
 </parameter>
@@ -177477,39 +168002,11 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="downTime" type="long">
-</parameter>
-<parameter name="eventTime" type="long">
-</parameter>
-<parameter name="action" type="int">
-</parameter>
-<parameter name="code" type="int">
-</parameter>
-<parameter name="repeat" type="int">
-</parameter>
-<parameter name="metaState" type="int">
-</parameter>
-<parameter name="deviceId" type="int">
-</parameter>
-<parameter name="scancode" type="int">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-<parameter name="source" type="int">
-</parameter>
-</constructor>
-<constructor name="KeyEvent"
- type="android.view.KeyEvent"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="time" type="long">
 </parameter>
 <parameter name="characters" type="java.lang.String">
 </parameter>
-<parameter name="deviceId" type="int">
+<parameter name="device" type="int">
 </parameter>
 <parameter name="flags" type="int">
 </parameter>
@@ -177604,6 +168101,17 @@
 <parameter name="newFlags" type="int">
 </parameter>
 </method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="dispatch"
  return="boolean"
  abstract="false"
@@ -178324,171 +168832,6 @@
  visibility="public"
 >
 </field>
-<field name="KEYCODE_BUTTON_A"
- type="int"
- transient="false"
- volatile="false"
- value="96"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_B"
- type="int"
- transient="false"
- volatile="false"
- value="97"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_C"
- type="int"
- transient="false"
- volatile="false"
- value="98"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_L1"
- type="int"
- transient="false"
- volatile="false"
- value="102"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_L2"
- type="int"
- transient="false"
- volatile="false"
- value="104"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_MODE"
- type="int"
- transient="false"
- volatile="false"
- value="110"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_R1"
- type="int"
- transient="false"
- volatile="false"
- value="103"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_R2"
- type="int"
- transient="false"
- volatile="false"
- value="105"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_SELECT"
- type="int"
- transient="false"
- volatile="false"
- value="109"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_START"
- type="int"
- transient="false"
- volatile="false"
- value="108"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_THUMBL"
- type="int"
- transient="false"
- volatile="false"
- value="106"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_THUMBR"
- type="int"
- transient="false"
- volatile="false"
- value="107"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_X"
- type="int"
- transient="false"
- volatile="false"
- value="99"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_Y"
- type="int"
- transient="false"
- volatile="false"
- value="100"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_BUTTON_Z"
- type="int"
- transient="false"
- volatile="false"
- value="101"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="KEYCODE_C"
  type="int"
  transient="false"
@@ -178984,28 +169327,6 @@
  visibility="public"
 >
 </field>
-<field name="KEYCODE_PAGE_DOWN"
- type="int"
- transient="false"
- volatile="false"
- value="93"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KEYCODE_PAGE_UP"
- type="int"
- transient="false"
- volatile="false"
- value="92"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="KEYCODE_PERIOD"
  type="int"
  transient="false"
@@ -179017,17 +169338,6 @@
  visibility="public"
 >
 </field>
-<field name="KEYCODE_PICTSYMBOLS"
- type="int"
- transient="false"
- volatile="false"
- value="94"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="KEYCODE_PLUS"
  type="int"
  transient="false"
@@ -179204,17 +169514,6 @@
  visibility="public"
 >
 </field>
-<field name="KEYCODE_SWITCH_CHARSET"
- type="int"
- transient="false"
- volatile="false"
- value="95"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="KEYCODE_SYM"
  type="int"
  transient="false"
@@ -180786,22 +171085,16 @@
 <parameter name="metaState" type="int">
 </parameter>
 </method>
-<method name="addBatch"
- return="void"
+<method name="describeContents"
+ return="int"
  abstract="false"
  native="false"
  synchronized="false"
  static="false"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="eventTime" type="long">
-</parameter>
-<parameter name="pointerCoords" type="android.view.MotionEvent.PointerCoords[]">
-</parameter>
-<parameter name="metaState" type="int">
-</parameter>
 </method>
 <method name="findPointerIndex"
  return="int"
@@ -180882,17 +171175,6 @@
  visibility="public"
 >
 </method>
-<method name="getFlags"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getHistoricalEventTime"
  return="long"
  abstract="false"
@@ -180906,51 +171188,6 @@
 <parameter name="pos" type="int">
 </parameter>
 </method>
-<method name="getHistoricalOrientation"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pos" type="int">
-</parameter>
-</method>
-<method name="getHistoricalOrientation"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pointerIndex" type="int">
-</parameter>
-<parameter name="pos" type="int">
-</parameter>
-</method>
-<method name="getHistoricalPointerCoords"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pointerIndex" type="int">
-</parameter>
-<parameter name="pos" type="int">
-</parameter>
-<parameter name="outPointerCoords" type="android.view.MotionEvent.PointerCoords">
-</parameter>
-</method>
 <method name="getHistoricalPressure"
  return="float"
  abstract="false"
@@ -181007,118 +171244,6 @@
 <parameter name="pos" type="int">
 </parameter>
 </method>
-<method name="getHistoricalToolMajor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pos" type="int">
-</parameter>
-</method>
-<method name="getHistoricalToolMajor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pointerIndex" type="int">
-</parameter>
-<parameter name="pos" type="int">
-</parameter>
-</method>
-<method name="getHistoricalToolMinor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pos" type="int">
-</parameter>
-</method>
-<method name="getHistoricalToolMinor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pointerIndex" type="int">
-</parameter>
-<parameter name="pos" type="int">
-</parameter>
-</method>
-<method name="getHistoricalTouchMajor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pos" type="int">
-</parameter>
-</method>
-<method name="getHistoricalTouchMajor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pointerIndex" type="int">
-</parameter>
-<parameter name="pos" type="int">
-</parameter>
-</method>
-<method name="getHistoricalTouchMinor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pos" type="int">
-</parameter>
-</method>
-<method name="getHistoricalTouchMinor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pointerIndex" type="int">
-</parameter>
-<parameter name="pos" type="int">
-</parameter>
-</method>
 <method name="getHistoricalX"
  return="float"
  abstract="false"
@@ -181197,45 +171322,6 @@
  visibility="public"
 >
 </method>
-<method name="getOrientation"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getOrientation"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pointerIndex" type="int">
-</parameter>
-</method>
-<method name="getPointerCoords"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pointerIndex" type="int">
-</parameter>
-<parameter name="outPointerCoords" type="android.view.MotionEvent.PointerCoords">
-</parameter>
-</method>
 <method name="getPointerCount"
  return="int"
  abstract="false"
@@ -181330,102 +171416,6 @@
 <parameter name="pointerIndex" type="int">
 </parameter>
 </method>
-<method name="getToolMajor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getToolMajor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pointerIndex" type="int">
-</parameter>
-</method>
-<method name="getToolMinor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getToolMinor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pointerIndex" type="int">
-</parameter>
-</method>
-<method name="getTouchMajor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getTouchMajor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pointerIndex" type="int">
-</parameter>
-</method>
-<method name="getTouchMinor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getTouchMinor"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pointerIndex" type="int">
-</parameter>
-</method>
 <method name="getX"
  return="float"
  abstract="false"
@@ -181512,43 +171502,6 @@
 </parameter>
 <parameter name="action" type="int">
 </parameter>
-<parameter name="pointers" type="int">
-</parameter>
-<parameter name="pointerIds" type="int[]">
-</parameter>
-<parameter name="pointerCoords" type="android.view.MotionEvent.PointerCoords[]">
-</parameter>
-<parameter name="metaState" type="int">
-</parameter>
-<parameter name="xPrecision" type="float">
-</parameter>
-<parameter name="yPrecision" type="float">
-</parameter>
-<parameter name="deviceId" type="int">
-</parameter>
-<parameter name="edgeFlags" type="int">
-</parameter>
-<parameter name="source" type="int">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<method name="obtain"
- return="android.view.MotionEvent"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="downTime" type="long">
-</parameter>
-<parameter name="eventTime" type="long">
-</parameter>
-<parameter name="action" type="int">
-</parameter>
 <parameter name="x" type="float">
 </parameter>
 <parameter name="y" type="float">
@@ -181575,7 +171528,7 @@
  synchronized="false"
  static="true"
  final="false"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 <parameter name="downTime" type="long">
@@ -181675,7 +171628,7 @@
  native="false"
  synchronized="false"
  static="false"
- final="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -181988,124 +171941,6 @@
  visibility="public"
 >
 </field>
-<field name="FLAG_WINDOW_IS_OBSCURED"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<class name="MotionEvent.PointerCoords"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="MotionEvent.PointerCoords"
- type="android.view.MotionEvent.PointerCoords"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<field name="orientation"
- type="float"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="pressure"
- type="float"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="size"
- type="float"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="toolMajor"
- type="float"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="toolMinor"
- type="float"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="touchMajor"
- type="float"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="touchMinor"
- type="float"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="x"
- type="float"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="y"
- type="float"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
 <class name="OrientationEventListener"
  extends="java.lang.Object"
@@ -183549,29 +173384,6 @@
 </parameter>
 </method>
 </interface>
-<interface name="SurfaceHolder.Callback2"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="android.view.SurfaceHolder.Callback">
-</implements>
-<method name="surfaceRedrawNeeded"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="holder" type="android.view.SurfaceHolder">
-</parameter>
-</method>
-</interface>
 <class name="SurfaceView"
  extends="android.view.View"
  abstract="false"
@@ -184721,17 +174533,6 @@
  visibility="public"
 >
 </method>
-<method name="getFilterTouchesWhenObscured"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getFocusables"
  return="java.util.ArrayList&lt;android.view.View&gt;"
  abstract="false"
@@ -185036,17 +174837,6 @@
  visibility="public"
 >
 </method>
-<method name="getOverScrollMode"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getPaddingBottom"
  return="int"
  abstract="false"
@@ -186020,19 +175810,6 @@
 <parameter name="canvas" type="android.graphics.Canvas">
 </parameter>
 </method>
-<method name="onFilterTouchEventForSecurity"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="event" type="android.view.MotionEvent">
-</parameter>
-</method>
 <method name="onFinishInflate"
  return="void"
  abstract="false"
@@ -186200,25 +175977,6 @@
 <parameter name="heightMeasureSpec" type="int">
 </parameter>
 </method>
-<method name="onOverScrolled"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="scrollX" type="int">
-</parameter>
-<parameter name="scrollY" type="int">
-</parameter>
-<parameter name="clampedX" type="boolean">
-</parameter>
-<parameter name="clampedY" type="boolean">
-</parameter>
-</method>
 <method name="onRestoreInstanceState"
  return="void"
  abstract="false"
@@ -186372,35 +176130,6 @@
 <parameter name="visibility" type="int">
 </parameter>
 </method>
-<method name="overScrollBy"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="deltaX" type="int">
-</parameter>
-<parameter name="deltaY" type="int">
-</parameter>
-<parameter name="scrollX" type="int">
-</parameter>
-<parameter name="scrollY" type="int">
-</parameter>
-<parameter name="scrollRangeX" type="int">
-</parameter>
-<parameter name="scrollRangeY" type="int">
-</parameter>
-<parameter name="maxOverScrollX" type="int">
-</parameter>
-<parameter name="maxOverScrollY" type="int">
-</parameter>
-<parameter name="isTouchEvent" type="boolean">
-</parameter>
-</method>
 <method name="performClick"
  return="boolean"
  abstract="false"
@@ -186939,19 +176668,6 @@
 <parameter name="length" type="int">
 </parameter>
 </method>
-<method name="setFilterTouchesWhenObscured"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="enabled" type="boolean">
-</parameter>
-</method>
 <method name="setFocusable"
  return="void"
  abstract="false"
@@ -187240,19 +176956,6 @@
 <parameter name="l" type="android.view.View.OnTouchListener">
 </parameter>
 </method>
-<method name="setOverScrollMode"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="overScrollMode" type="int">
-</parameter>
-</method>
 <method name="setPadding"
  return="void"
  abstract="false"
@@ -187875,39 +177578,6 @@
  visibility="public"
 >
 </field>
-<field name="OVER_SCROLL_ALWAYS"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="OVER_SCROLL_IF_CONTENT_SCROLLS"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="OVER_SCROLL_NEVER"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET"
  type="int[]"
  transient="false"
@@ -188670,28 +178340,6 @@
  visibility="public"
 >
 </method>
-<method name="getScaledOverflingDistance"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getScaledOverscrollDistance"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getScaledPagingTouchSlop"
  return="int"
  abstract="false"
@@ -192289,19 +181937,6 @@
 <parameter name="event" type="android.view.MotionEvent">
 </parameter>
 </method>
-<method name="takeInputQueue"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="callback" type="android.view.InputQueue.Callback">
-</parameter>
-</method>
 <method name="takeKeyEvents"
  return="void"
  abstract="true"
@@ -192315,19 +181950,6 @@
 <parameter name="get" type="boolean">
 </parameter>
 </method>
-<method name="takeSurface"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="callback" type="android.view.SurfaceHolder.Callback2">
-</parameter>
-</method>
 <method name="togglePanel"
  return="void"
  abstract="true"
@@ -197469,19 +187091,6 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
-<method name="getSelectedText"
- return="java.lang.CharSequence"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="flags" type="int">
-</parameter>
-</method>
 <method name="getTextAfterCursor"
  return="java.lang.CharSequence"
  abstract="false"
@@ -197592,21 +187201,6 @@
 <parameter name="event" type="android.view.KeyEvent">
 </parameter>
 </method>
-<method name="setComposingRegion"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
 <method name="setComposingSpans"
  return="void"
  abstract="false"
@@ -198601,19 +188195,6 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
-<method name="getSelectedText"
- return="java.lang.CharSequence"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="flags" type="int">
-</parameter>
-</method>
 <method name="getTextAfterCursor"
  return="java.lang.CharSequence"
  abstract="true"
@@ -198711,21 +188292,6 @@
 <parameter name="event" type="android.view.KeyEvent">
 </parameter>
 </method>
-<method name="setComposingRegion"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
 <method name="setComposingText"
  return="boolean"
  abstract="true"
@@ -198918,19 +188484,6 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
-<method name="getSelectedText"
- return="java.lang.CharSequence"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="flags" type="int">
-</parameter>
-</method>
 <method name="getTextAfterCursor"
  return="java.lang.CharSequence"
  abstract="false"
@@ -199028,21 +188581,6 @@
 <parameter name="event" type="android.view.KeyEvent">
 </parameter>
 </method>
-<method name="setComposingRegion"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
 <method name="setComposingText"
  return="boolean"
  abstract="false"
@@ -202792,7 +192330,7 @@
  synchronized="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -202803,7 +192341,7 @@
  synchronized="true"
  static="false"
  final="false"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </method>
@@ -202884,17 +192422,6 @@
  visibility="public"
 >
 </method>
-<method name="getUseWebViewBackgroundForOverscrollBackground"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getUseWideViewPort"
  return="boolean"
  abstract="false"
@@ -203338,7 +192865,7 @@
  synchronized="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="flag" type="boolean">
@@ -203351,7 +192878,7 @@
  synchronized="true"
  static="false"
  final="false"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 <parameter name="pluginsPath" type="java.lang.String">
@@ -203487,19 +193014,6 @@
 <parameter name="use" type="boolean">
 </parameter>
 </method>
-<method name="setUseWebViewBackgroundForOverscrollBackground"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="view" type="boolean">
-</parameter>
-</method>
 <method name="setUseWideViewPort"
  return="void"
  abstract="false"
@@ -205463,7 +194977,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="view" type="android.webkit.WebView">
@@ -205838,6 +195352,39 @@
 <parameter name="defStyle" type="int">
 </parameter>
 </constructor>
+<field name="CHOICE_MODE_MULTIPLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_SINGLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <method name="afterTextChanged"
  return="void"
  abstract="false"
@@ -213996,7 +203543,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -214110,28 +203657,6 @@
  visibility="public"
 >
 </method>
-<method name="getOverscrollFooter"
- return="android.graphics.drawable.Drawable"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getOverscrollHeader"
- return="android.graphics.drawable.Drawable"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="isItemChecked"
  return="boolean"
  abstract="false"
@@ -214277,32 +203802,6 @@
 <parameter name="itemsCanFocus" type="boolean">
 </parameter>
 </method>
-<method name="setOverscrollFooter"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="footer" type="android.graphics.drawable.Drawable">
-</parameter>
-</method>
-<method name="setOverscrollHeader"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="header" type="android.graphics.drawable.Drawable">
-</parameter>
-</method>
 <method name="setSelection"
  return="void"
  abstract="false"
@@ -214342,39 +203841,6 @@
 <parameter name="y" type="int">
 </parameter>
 </method>
-<field name="CHOICE_MODE_MULTIPLE"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_NONE"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_SINGLE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
 <class name="ListView.FixedViewInfo"
  extends="java.lang.Object"
@@ -214874,331 +204340,6 @@
 </parameter>
 </method>
 </interface>
-<class name="OverScroller"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="OverScroller"
- type="android.widget.OverScroller"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-</constructor>
-<constructor name="OverScroller"
- type="android.widget.OverScroller"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="interpolator" type="android.view.animation.Interpolator">
-</parameter>
-</constructor>
-<constructor name="OverScroller"
- type="android.widget.OverScroller"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="interpolator" type="android.view.animation.Interpolator">
-</parameter>
-<parameter name="bounceCoefficientX" type="float">
-</parameter>
-<parameter name="bounceCoefficientY" type="float">
-</parameter>
-</constructor>
-<method name="abortAnimation"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="computeScrollOffset"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="fling"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startX" type="int">
-</parameter>
-<parameter name="startY" type="int">
-</parameter>
-<parameter name="velocityX" type="int">
-</parameter>
-<parameter name="velocityY" type="int">
-</parameter>
-<parameter name="minX" type="int">
-</parameter>
-<parameter name="maxX" type="int">
-</parameter>
-<parameter name="minY" type="int">
-</parameter>
-<parameter name="maxY" type="int">
-</parameter>
-</method>
-<method name="fling"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startX" type="int">
-</parameter>
-<parameter name="startY" type="int">
-</parameter>
-<parameter name="velocityX" type="int">
-</parameter>
-<parameter name="velocityY" type="int">
-</parameter>
-<parameter name="minX" type="int">
-</parameter>
-<parameter name="maxX" type="int">
-</parameter>
-<parameter name="minY" type="int">
-</parameter>
-<parameter name="maxY" type="int">
-</parameter>
-<parameter name="overX" type="int">
-</parameter>
-<parameter name="overY" type="int">
-</parameter>
-</method>
-<method name="forceFinished"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="finished" type="boolean">
-</parameter>
-</method>
-<method name="getCurrX"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCurrY"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getFinalX"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getFinalY"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getStartX"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getStartY"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isFinished"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isOverScrolled"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="notifyHorizontalEdgeReached"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startX" type="int">
-</parameter>
-<parameter name="finalX" type="int">
-</parameter>
-<parameter name="overX" type="int">
-</parameter>
-</method>
-<method name="notifyVerticalEdgeReached"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startY" type="int">
-</parameter>
-<parameter name="finalY" type="int">
-</parameter>
-<parameter name="overY" type="int">
-</parameter>
-</method>
-<method name="springBack"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startX" type="int">
-</parameter>
-<parameter name="startY" type="int">
-</parameter>
-<parameter name="minX" type="int">
-</parameter>
-<parameter name="maxX" type="int">
-</parameter>
-<parameter name="minY" type="int">
-</parameter>
-<parameter name="maxY" type="int">
-</parameter>
-</method>
-<method name="startScroll"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startX" type="int">
-</parameter>
-<parameter name="startY" type="int">
-</parameter>
-<parameter name="dx" type="int">
-</parameter>
-<parameter name="dy" type="int">
-</parameter>
-</method>
-<method name="startScroll"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startX" type="int">
-</parameter>
-<parameter name="startY" type="int">
-</parameter>
-<parameter name="dx" type="int">
-</parameter>
-<parameter name="dy" type="int">
-</parameter>
-<parameter name="duration" type="int">
-</parameter>
-</method>
-</class>
 <class name="PopupWindow"
  extends="java.lang.Object"
  abstract="false"
@@ -217192,17 +206333,6 @@
 <parameter name="parent" type="android.view.ViewGroup">
 </parameter>
 </method>
-<method name="clone"
- return="android.widget.RemoteViews"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="describeContents"
  return="int"
  abstract="false"
@@ -224570,7 +213700,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
@@ -225654,17 +214784,6 @@
  visibility="public"
 >
 </field>
-<field name="OP_IGET_WIDE_VOLATILE"
- type="int"
- transient="false"
- volatile="false"
- value="232"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="OP_INSTANCE_OF"
  type="int"
  transient="false"
@@ -226017,17 +215136,6 @@
  visibility="public"
 >
 </field>
-<field name="OP_IPUT_WIDE_VOLATILE"
- type="int"
- transient="false"
- volatile="false"
- value="233"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="OP_LONG_TO_DOUBLE"
  type="int"
  transient="false"
@@ -226765,17 +215873,6 @@
  visibility="public"
 >
 </field>
-<field name="OP_SGET_WIDE_VOLATILE"
- type="int"
- transient="false"
- volatile="false"
- value="234"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="OP_SHL_INT"
  type="int"
  transient="false"
@@ -226974,17 +216071,6 @@
  visibility="public"
 >
 </field>
-<field name="OP_SPUT_WIDE_VOLATILE"
- type="int"
- transient="false"
- volatile="false"
- value="235"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="OP_SUB_DOUBLE"
  type="int"
  transient="false"
@@ -227398,19 +216484,6 @@
 <parameter name="parent" type="java.lang.ClassLoader">
 </parameter>
 </constructor>
-<method name="findLibrary"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="libname" type="java.lang.String">
-</parameter>
-</method>
 </class>
 </package>
 <package name="java.awt.font"
@@ -227873,46 +216946,6 @@
  visibility="public"
 >
 </field>
-<field name="KERNING"
- type="java.awt.font.TextAttribute"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="KERNING_ON"
- type="java.lang.Integer"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="LIGATURES"
- type="java.awt.font.TextAttribute"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="LIGATURES_ON"
- type="java.lang.Integer"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="NUMERIC_SHAPING"
  type="java.awt.font.TextAttribute"
  transient="false"
@@ -228063,36 +217096,6 @@
  visibility="public"
 >
 </field>
-<field name="TRACKING"
- type="java.awt.font.TextAttribute"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRACKING_LOOSE"
- type="java.lang.Float"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRACKING_TIGHT"
- type="java.lang.Float"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="TRANSFORM"
  type="java.awt.font.TextAttribute"
  transient="false"
@@ -229501,7 +218504,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="buffer" type="char[]">
+<parameter name="c" type="char[]">
 </parameter>
 <parameter name="offset" type="int">
 </parameter>
@@ -229593,132 +218596,6 @@
 </exception>
 </method>
 </interface>
-<class name="Console"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.io.Flushable">
-</implements>
-<method name="flush"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="format"
- return="java.io.Console"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="format" type="java.lang.String">
-</parameter>
-<parameter name="args" type="java.lang.Object...">
-</parameter>
-</method>
-<method name="printf"
- return="java.io.Console"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="format" type="java.lang.String">
-</parameter>
-<parameter name="args" type="java.lang.Object...">
-</parameter>
-</method>
-<method name="readLine"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="readLine"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="format" type="java.lang.String">
-</parameter>
-<parameter name="args" type="java.lang.Object...">
-</parameter>
-</method>
-<method name="readPassword"
- return="char[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="readPassword"
- return="char[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="format" type="java.lang.String">
-</parameter>
-<parameter name="args" type="java.lang.Object...">
-</parameter>
-</method>
-<method name="reader"
- return="java.io.Reader"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="writer"
- return="java.io.PrintWriter"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
 <interface name="DataInput"
  abstract="true"
  static="false"
@@ -230761,17 +219638,6 @@
 <parameter name="uri" type="java.net.URI">
 </parameter>
 </constructor>
-<method name="canExecute"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="canRead"
  return="boolean"
  abstract="false"
@@ -230937,17 +219803,6 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
-<method name="getFreeSpace"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getName"
  return="java.lang.String"
  abstract="false"
@@ -230992,28 +219847,6 @@
  visibility="public"
 >
 </method>
-<method name="getTotalSpace"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getUsableSpace"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="isAbsolute"
  return="boolean"
  abstract="false"
@@ -231184,35 +220017,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="newPath" type="java.io.File">
-</parameter>
-</method>
-<method name="setExecutable"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="executable" type="boolean">
-</parameter>
-<parameter name="ownerOnly" type="boolean">
-</parameter>
-</method>
-<method name="setExecutable"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="executable" type="boolean">
+<parameter name="dest" type="java.io.File">
 </parameter>
 </method>
 <method name="setLastModified"
@@ -231239,62 +220044,6 @@
  visibility="public"
 >
 </method>
-<method name="setReadable"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="readable" type="boolean">
-</parameter>
-<parameter name="ownerOnly" type="boolean">
-</parameter>
-</method>
-<method name="setReadable"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="readable" type="boolean">
-</parameter>
-</method>
-<method name="setWritable"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="writable" type="boolean">
-</parameter>
-<parameter name="ownerOnly" type="boolean">
-</parameter>
-</method>
-<method name="setWritable"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="writable" type="boolean">
-</parameter>
-</method>
 <method name="toURI"
  return="java.net.URI"
  abstract="false"
@@ -231313,7 +220062,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 <exception name="MalformedURLException" type="java.net.MalformedURLException">
@@ -232123,25 +220872,6 @@
 </exception>
 </method>
 </interface>
-<class name="IOError"
- extends="java.lang.Error"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="IOError"
- type="java.io.IOError"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
 <class name="IOException"
  extends="java.lang.Exception"
  abstract="false"
@@ -232168,28 +220898,6 @@
 <parameter name="detailMessage" type="java.lang.String">
 </parameter>
 </constructor>
-<constructor name="IOException"
- type="java.io.IOException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="message" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="IOException"
- type="java.io.IOException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
 </class>
 <class name="InputStream"
  extends="java.lang.Object"
@@ -232329,7 +221037,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="byteCount" type="long">
+<parameter name="n" type="long">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -232425,7 +221133,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="buffer" type="char[]">
+<parameter name="buf" type="char[]">
 </parameter>
 <parameter name="offset" type="int">
 </parameter>
@@ -234264,19 +222972,6 @@
 <parameter name="cl" type="java.lang.Class&lt;?&gt;">
 </parameter>
 </method>
-<method name="lookupAny"
- return="java.io.ObjectStreamClass"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cl" type="java.lang.Class&lt;?&gt;">
-</parameter>
-</method>
 <field name="NO_FIELDS"
  type="java.io.ObjectStreamField[]"
  transient="false"
@@ -235011,7 +223706,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="buffer" type="char[]">
+<parameter name="buf" type="char[]">
 </parameter>
 <parameter name="offset" type="int">
 </parameter>
@@ -235049,30 +223744,6 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </constructor>
-<constructor name="PipedInputStream"
- type="java.io.PipedInputStream"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pipeSize" type="int">
-</parameter>
-</constructor>
-<constructor name="PipedInputStream"
- type="java.io.PipedInputStream"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="out" type="java.io.PipedOutputStream">
-</parameter>
-<parameter name="pipeSize" type="int">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</constructor>
 <method name="connect"
  return="void"
  abstract="false"
@@ -235182,7 +223853,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="target" type="java.io.PipedInputStream">
+<parameter name="dest" type="java.io.PipedInputStream">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -235246,30 +223917,6 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </constructor>
-<constructor name="PipedReader"
- type="java.io.PipedReader"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pipeSize" type="int">
-</parameter>
-</constructor>
-<constructor name="PipedReader"
- type="java.io.PipedReader"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="out" type="java.io.PipedWriter">
-</parameter>
-<parameter name="pipeSize" type="int">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</constructor>
 <method name="close"
  return="void"
  abstract="false"
@@ -235341,7 +223988,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="destination" type="java.io.PipedReader">
+<parameter name="dest" type="java.io.PipedReader">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -235567,17 +224214,6 @@
  visibility="public"
 >
 </method>
-<method name="clearError"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
 <method name="format"
  return="java.io.PrintStream"
  abstract="false"
@@ -236018,17 +224654,6 @@
  visibility="public"
 >
 </method>
-<method name="clearError"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
 <method name="close"
  return="void"
  abstract="false"
@@ -236932,7 +225557,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="offset" type="long">
+<parameter name="pos" type="long">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -237951,7 +226576,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="chars" type="char[]">
+<parameter name="cbuf" type="char[]">
 </parameter>
 <parameter name="offset" type="int">
 </parameter>
@@ -238394,9 +227019,9 @@
 </parameter>
 <parameter name="end" type="int">
 </parameter>
-<parameter name="dst" type="char[]">
+<parameter name="dest" type="char[]">
 </parameter>
-<parameter name="dstStart" type="int">
+<parameter name="destStart" type="int">
 </parameter>
 </method>
 <method name="indexOf"
@@ -242821,7 +231446,7 @@
 <method name="getName"
  return="java.lang.String"
  abstract="false"
- native="false"
+ native="true"
  synchronized="false"
  static="false"
  final="false"
@@ -244067,17 +232692,6 @@
 <parameter name="d" type="double">
 </parameter>
 </method>
-<field name="MAX_EXPONENT"
- type="int"
- transient="false"
- volatile="false"
- value="1023"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="MAX_VALUE"
  type="double"
  transient="false"
@@ -244089,28 +232703,6 @@
  visibility="public"
 >
 </field>
-<field name="MIN_EXPONENT"
- type="int"
- transient="false"
- volatile="false"
- value="-1022"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MIN_NORMAL"
- type="double"
- transient="false"
- volatile="false"
- value="2.2250738585072014E-308"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="MIN_VALUE"
  type="double"
  transient="false"
@@ -244240,17 +232832,6 @@
 <parameter name="other" type="java.lang.Object">
 </parameter>
 </method>
-<method name="finalize"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
 <method name="getDeclaringClass"
  return="java.lang.Class&lt;E&gt;"
  abstract="false"
@@ -244770,17 +233351,6 @@
 <parameter name="f" type="float">
 </parameter>
 </method>
-<field name="MAX_EXPONENT"
- type="int"
- transient="false"
- volatile="false"
- value="127"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="MAX_VALUE"
  type="float"
  transient="false"
@@ -244792,28 +233362,6 @@
  visibility="public"
 >
 </field>
-<field name="MIN_EXPONENT"
- type="int"
- transient="false"
- volatile="false"
- value="-126"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="MIN_NORMAL"
- type="float"
- transient="false"
- volatile="false"
- value="1.17549435E-38f"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="MIN_VALUE"
  type="float"
  transient="false"
@@ -246386,10 +234934,10 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="y" type="double">
-</parameter>
 <parameter name="x" type="double">
 </parameter>
+<parameter name="y" type="double">
+</parameter>
 </method>
 <method name="cbrt"
  return="double"
@@ -246417,36 +234965,6 @@
 <parameter name="d" type="double">
 </parameter>
 </method>
-<method name="copySign"
- return="double"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="magnitude" type="double">
-</parameter>
-<parameter name="sign" type="double">
-</parameter>
-</method>
-<method name="copySign"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="magnitude" type="float">
-</parameter>
-<parameter name="sign" type="float">
-</parameter>
-</method>
 <method name="cos"
  return="double"
  abstract="false"
@@ -246512,32 +235030,6 @@
 <parameter name="d" type="double">
 </parameter>
 </method>
-<method name="getExponent"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="f" type="float">
-</parameter>
-</method>
-<method name="getExponent"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="d" type="double">
-</parameter>
-</method>
 <method name="hypot"
  return="double"
  abstract="false"
@@ -246712,62 +235204,6 @@
 <parameter name="l2" type="long">
 </parameter>
 </method>
-<method name="nextAfter"
- return="double"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="double">
-</parameter>
-<parameter name="direction" type="double">
-</parameter>
-</method>
-<method name="nextAfter"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="float">
-</parameter>
-<parameter name="direction" type="double">
-</parameter>
-</method>
-<method name="nextUp"
- return="double"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="d" type="double">
-</parameter>
-</method>
-<method name="nextUp"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="f" type="float">
-</parameter>
-</method>
 <method name="pow"
  return="double"
  abstract="false"
@@ -246787,7 +235223,7 @@
  return="double"
  abstract="false"
  native="false"
- synchronized="true"
+ synchronized="false"
  static="true"
  final="false"
  deprecated="not deprecated"
@@ -246833,36 +235269,6 @@
 <parameter name="f" type="float">
 </parameter>
 </method>
-<method name="scalb"
- return="double"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="d" type="double">
-</parameter>
-<parameter name="scaleFactor" type="int">
-</parameter>
-</method>
-<method name="scalb"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="d" type="float">
-</parameter>
-<parameter name="scaleFactor" type="int">
-</parameter>
-</method>
 <method name="signum"
  return="double"
  abstract="false"
@@ -247534,7 +235940,7 @@
 <implements name="java.lang.reflect.AnnotatedElement">
 </implements>
 <method name="getAnnotation"
- return="A"
+ return="T"
  abstract="false"
  native="false"
  synchronized="false"
@@ -247543,7 +235949,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="annotationType" type="java.lang.Class&lt;A&gt;">
+<parameter name="annotationType" type="java.lang.Class&lt;T&gt;">
 </parameter>
 </method>
 <method name="getAnnotations"
@@ -248011,7 +236417,7 @@
 <method name="availableProcessors"
  return="int"
  abstract="false"
- native="true"
+ native="false"
  synchronized="false"
  static="false"
  final="false"
@@ -249510,36 +237916,6 @@
 <parameter name="d" type="double">
 </parameter>
 </method>
-<method name="copySign"
- return="double"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="magnitude" type="double">
-</parameter>
-<parameter name="sign" type="double">
-</parameter>
-</method>
-<method name="copySign"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="magnitude" type="float">
-</parameter>
-<parameter name="sign" type="float">
-</parameter>
-</method>
 <method name="cos"
  return="double"
  abstract="false"
@@ -249605,32 +237981,6 @@
 <parameter name="d" type="double">
 </parameter>
 </method>
-<method name="getExponent"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="f" type="float">
-</parameter>
-</method>
-<method name="getExponent"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="d" type="double">
-</parameter>
-</method>
 <method name="hypot"
  return="double"
  abstract="false"
@@ -249805,62 +238155,6 @@
 <parameter name="l2" type="long">
 </parameter>
 </method>
-<method name="nextAfter"
- return="double"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="double">
-</parameter>
-<parameter name="direction" type="double">
-</parameter>
-</method>
-<method name="nextAfter"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="float">
-</parameter>
-<parameter name="direction" type="double">
-</parameter>
-</method>
-<method name="nextUp"
- return="double"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="d" type="double">
-</parameter>
-</method>
-<method name="nextUp"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="f" type="float">
-</parameter>
-</method>
 <method name="pow"
  return="double"
  abstract="false"
@@ -249926,36 +238220,6 @@
 <parameter name="f" type="float">
 </parameter>
 </method>
-<method name="scalb"
- return="double"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="d" type="double">
-</parameter>
-<parameter name="scaleFactor" type="int">
-</parameter>
-</method>
-<method name="scalb"
- return="float"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="d" type="float">
-</parameter>
-<parameter name="scaleFactor" type="int">
-</parameter>
-</method>
 <method name="signum"
  return="double"
  abstract="false"
@@ -250209,7 +238473,7 @@
 </parameter>
 <parameter name="length" type="int">
 </parameter>
-<parameter name="charsetName" type="java.lang.String">
+<parameter name="encoding" type="java.lang.String">
 </parameter>
 <exception name="UnsupportedEncodingException" type="java.io.UnsupportedEncodingException">
 </exception>
@@ -250223,7 +238487,7 @@
 >
 <parameter name="data" type="byte[]">
 </parameter>
-<parameter name="charsetName" type="java.lang.String">
+<parameter name="encoding" type="java.lang.String">
 </parameter>
 <exception name="UnsupportedEncodingException" type="java.io.UnsupportedEncodingException">
 </exception>
@@ -250235,34 +238499,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="data" type="byte[]">
-</parameter>
-<parameter name="start" type="int">
-</parameter>
-<parameter name="length" type="int">
-</parameter>
-<parameter name="charset" type="java.nio.charset.Charset">
-</parameter>
-</constructor>
-<constructor name="String"
- type="java.lang.String"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="data" type="byte[]">
-</parameter>
-<parameter name="charset" type="java.nio.charset.Charset">
-</parameter>
-</constructor>
-<constructor name="String"
- type="java.lang.String"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="data" type="char[]">
 </parameter>
 </constructor>
@@ -250287,7 +238523,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="toCopy" type="java.lang.String">
+<parameter name="string" type="java.lang.String">
 </parameter>
 </constructor>
 <constructor name="String"
@@ -250537,7 +238773,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="locale" type="java.util.Locale">
+<parameter name="loc" type="java.util.Locale">
 </parameter>
 <parameter name="format" type="java.lang.String">
 </parameter>
@@ -250545,6 +238781,17 @@
 </parameter>
 </method>
 <method name="getBytes"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getBytes"
  return="void"
  abstract="false"
  native="false"
@@ -250573,35 +238820,11 @@
  deprecated="not deprecated"
  visibility="public"
 >
-</method>
-<method name="getBytes"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="charsetName" type="java.lang.String">
+<parameter name="encoding" type="java.lang.String">
 </parameter>
 <exception name="UnsupportedEncodingException" type="java.io.UnsupportedEncodingException">
 </exception>
 </method>
-<method name="getBytes"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="charset" type="java.nio.charset.Charset">
-</parameter>
-</method>
 <method name="getChars"
  return="void"
  abstract="false"
@@ -250688,17 +238911,6 @@
  visibility="public"
 >
 </method>
-<method name="isEmpty"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="lastIndexOf"
  return="int"
  abstract="false"
@@ -250776,7 +238988,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="regularExpression" type="java.lang.String">
+<parameter name="expr" type="java.lang.String">
 </parameter>
 </method>
 <method name="offsetByCodePoints"
@@ -250874,9 +239086,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="regularExpression" type="java.lang.String">
+<parameter name="expr" type="java.lang.String">
 </parameter>
-<parameter name="replacement" type="java.lang.String">
+<parameter name="substitute" type="java.lang.String">
 </parameter>
 </method>
 <method name="replaceFirst"
@@ -250889,9 +239101,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="regularExpression" type="java.lang.String">
+<parameter name="expr" type="java.lang.String">
 </parameter>
-<parameter name="replacement" type="java.lang.String">
+<parameter name="substitute" type="java.lang.String">
 </parameter>
 </method>
 <method name="split"
@@ -250904,7 +239116,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="regularExpression" type="java.lang.String">
+<parameter name="expr" type="java.lang.String">
 </parameter>
 </method>
 <method name="split"
@@ -250917,9 +239129,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="regularExpression" type="java.lang.String">
+<parameter name="expr" type="java.lang.String">
 </parameter>
-<parameter name="limit" type="int">
+<parameter name="max" type="int">
 </parameter>
 </method>
 <method name="startsWith"
@@ -251783,7 +239995,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="l" type="long">
+<parameter name="lng" type="long">
 </parameter>
 </method>
 <method name="append"
@@ -251861,7 +240073,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="chars" type="char[]">
+<parameter name="ch" type="char[]">
 </parameter>
 </method>
 <method name="append"
@@ -252239,9 +240451,9 @@
 </parameter>
 <parameter name="srcPos" type="int">
 </parameter>
-<parameter name="dst" type="java.lang.Object">
+<parameter name="dest" type="java.lang.Object">
 </parameter>
-<parameter name="dstPos" type="int">
+<parameter name="destPos" type="int">
 </parameter>
 <parameter name="length" type="int">
 </parameter>
@@ -252259,17 +240471,6 @@
 <parameter name="key" type="java.lang.String">
 </parameter>
 </method>
-<method name="console"
- return="java.io.Console"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="currentTimeMillis"
  return="long"
  abstract="false"
@@ -252326,7 +240527,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="propertyName" type="java.lang.String">
+<parameter name="prop" type="java.lang.String">
 </parameter>
 </method>
 <method name="getProperty"
@@ -257855,7 +246056,7 @@
 >
 <parameter name="numBits" type="int">
 </parameter>
-<parameter name="random" type="java.util.Random">
+<parameter name="rnd" type="java.util.Random">
 </parameter>
 </constructor>
 <constructor name="BigInteger"
@@ -257869,7 +246070,7 @@
 </parameter>
 <parameter name="certainty" type="int">
 </parameter>
-<parameter name="unused" type="java.util.Random">
+<parameter name="rnd" type="java.util.Random">
 </parameter>
 </constructor>
 <constructor name="BigInteger"
@@ -257879,7 +246080,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.lang.String">
+<parameter name="val" type="java.lang.String">
 </parameter>
 </constructor>
 <constructor name="BigInteger"
@@ -257889,7 +246090,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.lang.String">
+<parameter name="val" type="java.lang.String">
 </parameter>
 <parameter name="radix" type="int">
 </parameter>
@@ -257913,7 +246114,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="byte[]">
+<parameter name="val" type="byte[]">
 </parameter>
 </constructor>
 <method name="abs"
@@ -257937,7 +246138,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.math.BigInteger">
+<parameter name="val" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="and"
@@ -257950,7 +246151,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.math.BigInteger">
+<parameter name="val" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="andNot"
@@ -257963,7 +246164,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.math.BigInteger">
+<parameter name="val" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="bitCount"
@@ -258011,7 +246212,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.math.BigInteger">
+<parameter name="val" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="divide"
@@ -258085,7 +246286,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.math.BigInteger">
+<parameter name="val" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="getLowestSetBit"
@@ -258144,7 +246345,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.math.BigInteger">
+<parameter name="val" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="min"
@@ -258157,7 +246358,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.math.BigInteger">
+<parameter name="val" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="mod"
@@ -258211,7 +246412,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.math.BigInteger">
+<parameter name="val" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="negate"
@@ -258257,7 +246458,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.math.BigInteger">
+<parameter name="val" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="pow"
@@ -258285,7 +246486,7 @@
 >
 <parameter name="bitLength" type="int">
 </parameter>
-<parameter name="unused" type="java.util.Random">
+<parameter name="rnd" type="java.util.Random">
 </parameter>
 </method>
 <method name="remainder"
@@ -258361,7 +246562,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.math.BigInteger">
+<parameter name="val" type="java.math.BigInteger">
 </parameter>
 </method>
 <method name="testBit"
@@ -258411,7 +246612,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="long">
+<parameter name="val" type="long">
 </parameter>
 </method>
 <method name="xor"
@@ -258424,7 +246625,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="value" type="java.math.BigInteger">
+<parameter name="val" type="java.math.BigInteger">
 </parameter>
 </method>
 <field name="ONE"
@@ -259128,233 +247329,6 @@
 </parameter>
 </method>
 </class>
-<class name="CookieManager"
- extends="java.net.CookieHandler"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="CookieManager"
- type="java.net.CookieManager"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="CookieManager"
- type="java.net.CookieManager"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="store" type="java.net.CookieStore">
-</parameter>
-<parameter name="cookiePolicy" type="java.net.CookiePolicy">
-</parameter>
-</constructor>
-<method name="get"
- return="java.util.Map&lt;java.lang.String, java.util.List&lt;java.lang.String&gt;&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="java.net.URI">
-</parameter>
-<parameter name="requestHeaders" type="java.util.Map&lt;java.lang.String, java.util.List&lt;java.lang.String&gt;&gt;">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-<method name="getCookieStore"
- return="java.net.CookieStore"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="put"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="java.net.URI">
-</parameter>
-<parameter name="responseHeaders" type="java.util.Map&lt;java.lang.String, java.util.List&lt;java.lang.String&gt;&gt;">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-<method name="setCookiePolicy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cookiePolicy" type="java.net.CookiePolicy">
-</parameter>
-</method>
-</class>
-<interface name="CookiePolicy"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="shouldAccept"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="java.net.URI">
-</parameter>
-<parameter name="cookie" type="java.net.HttpCookie">
-</parameter>
-</method>
-<field name="ACCEPT_ALL"
- type="java.net.CookiePolicy"
- transient="false"
- volatile="false"
- value="null"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACCEPT_NONE"
- type="java.net.CookiePolicy"
- transient="false"
- volatile="false"
- value="null"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="ACCEPT_ORIGINAL_SERVER"
- type="java.net.CookiePolicy"
- transient="false"
- volatile="false"
- value="null"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</interface>
-<interface name="CookieStore"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="add"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="java.net.URI">
-</parameter>
-<parameter name="cookie" type="java.net.HttpCookie">
-</parameter>
-</method>
-<method name="get"
- return="java.util.List&lt;java.net.HttpCookie&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="java.net.URI">
-</parameter>
-</method>
-<method name="getCookies"
- return="java.util.List&lt;java.net.HttpCookie&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getURIs"
- return="java.util.List&lt;java.net.URI&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="remove"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="uri" type="java.net.URI">
-</parameter>
-<parameter name="cookie" type="java.net.HttpCookie">
-</parameter>
-</method>
-<method name="removeAll"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</interface>
 <class name="DatagramPacket"
  extends="java.lang.Object"
  abstract="false"
@@ -260409,334 +248383,10 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="filename" type="java.lang.String">
+<parameter name="fileName" type="java.lang.String">
 </parameter>
 </method>
 </interface>
-<class name="HttpCookie"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.lang.Cloneable">
-</implements>
-<constructor name="HttpCookie"
- type="java.net.HttpCookie"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="name" type="java.lang.String">
-</parameter>
-<parameter name="value" type="java.lang.String">
-</parameter>
-</constructor>
-<method name="clone"
- return="java.lang.Object"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="domainMatches"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="domainPattern" type="java.lang.String">
-</parameter>
-<parameter name="host" type="java.lang.String">
-</parameter>
-</method>
-<method name="getComment"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCommentURL"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getDiscard"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getDomain"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getMaxAge"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getPath"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getPortlist"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getSecure"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getValue"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getVersion"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="hasExpired"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="parse"
- return="java.util.List&lt;java.net.HttpCookie&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="header" type="java.lang.String">
-</parameter>
-</method>
-<method name="setComment"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="comment" type="java.lang.String">
-</parameter>
-</method>
-<method name="setCommentURL"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="commentURL" type="java.lang.String">
-</parameter>
-</method>
-<method name="setDiscard"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="discard" type="boolean">
-</parameter>
-</method>
-<method name="setDomain"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pattern" type="java.lang.String">
-</parameter>
-</method>
-<method name="setMaxAge"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="deltaSeconds" type="long">
-</parameter>
-</method>
-<method name="setPath"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="path" type="java.lang.String">
-</parameter>
-</method>
-<method name="setPortlist"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="portList" type="java.lang.String">
-</parameter>
-</method>
-<method name="setSecure"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="secure" type="boolean">
-</parameter>
-</method>
-<method name="setValue"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="value" type="java.lang.String">
-</parameter>
-</method>
-<method name="setVersion"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="v" type="int">
-</parameter>
-</method>
-</class>
 <class name="HttpRetryException"
  extends="java.io.IOException"
  abstract="false"
@@ -260914,7 +248564,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="chunkLength" type="int">
+<parameter name="chunklen" type="int">
 </parameter>
 </method>
 <method name="setFixedLengthStreamingMode"
@@ -261439,93 +249089,6 @@
 >
 </field>
 </class>
-<class name="IDN"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="toASCII"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="input" type="java.lang.String">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<method name="toASCII"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="input" type="java.lang.String">
-</parameter>
-</method>
-<method name="toUnicode"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="input" type="java.lang.String">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
-<method name="toUnicode"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="input" type="java.lang.String">
-</parameter>
-</method>
-<field name="ALLOW_UNASSIGNED"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="USE_STD3_ASCII_RULES"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="Inet4Address"
  extends="java.net.InetAddress"
  abstract="false"
@@ -261868,7 +249431,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="networkInterface" type="java.net.NetworkInterface">
+<parameter name="netif" type="java.net.NetworkInterface">
 </parameter>
 <parameter name="ttl" type="int">
 </parameter>
@@ -262015,48 +249578,6 @@
 >
 </method>
 </class>
-<class name="InterfaceAddress"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="getAddress"
- return="java.net.InetAddress"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getBroadcast"
- return="java.net.InetAddress"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getNetworkPrefixLength"
- return="short"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
 <class name="JarURLConnection"
  extends="java.net.URLConnection"
  abstract="true"
@@ -262428,7 +249949,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="disable" type="boolean">
+<parameter name="loop" type="boolean">
 </parameter>
 <exception name="SocketException" type="java.net.SocketException">
 </exception>
@@ -262559,19 +250080,6 @@
  visibility="public"
 >
 </method>
-<method name="getHardwareAddress"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SocketException" type="java.net.SocketException">
-</exception>
-</method>
 <method name="getInetAddresses"
  return="java.util.Enumeration&lt;java.net.InetAddress&gt;"
  abstract="false"
@@ -262583,30 +250091,6 @@
  visibility="public"
 >
 </method>
-<method name="getInterfaceAddresses"
- return="java.util.List&lt;java.net.InterfaceAddress&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getMTU"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SocketException" type="java.net.SocketException">
-</exception>
-</method>
 <method name="getName"
  return="java.lang.String"
  abstract="false"
@@ -262631,91 +250115,6 @@
 <exception name="SocketException" type="java.net.SocketException">
 </exception>
 </method>
-<method name="getParent"
- return="java.net.NetworkInterface"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getSubInterfaces"
- return="java.util.Enumeration&lt;java.net.NetworkInterface&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isLoopback"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SocketException" type="java.net.SocketException">
-</exception>
-</method>
-<method name="isPointToPoint"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SocketException" type="java.net.SocketException">
-</exception>
-</method>
-<method name="isUp"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SocketException" type="java.net.SocketException">
-</exception>
-</method>
-<method name="isVirtual"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="supportsMulticast"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SocketException" type="java.net.SocketException">
-</exception>
-</method>
 </class>
 <class name="NoRouteToHostException"
  extends="java.net.SocketException"
@@ -263050,9 +250449,9 @@
 >
 <parameter name="uri" type="java.net.URI">
 </parameter>
-<parameter name="requestMethod" type="java.lang.String">
+<parameter name="rqstMethod" type="java.lang.String">
 </parameter>
-<parameter name="requestHeaders" type="java.util.Map&lt;java.lang.String, java.util.List&lt;java.lang.String&gt;&gt;">
+<parameter name="rqstHeaders" type="java.util.Map&lt;java.lang.String, java.util.List&lt;java.lang.String&gt;&gt;">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -263622,7 +251021,7 @@
  deprecated="not deprecated"
  visibility="protected"
 >
-<parameter name="impl" type="java.net.SocketImpl">
+<parameter name="anImpl" type="java.net.SocketImpl">
 </parameter>
 <exception name="SocketException" type="java.net.SocketException">
 </exception>
@@ -263987,7 +251386,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="keepAlive" type="boolean">
+<parameter name="value" type="boolean">
 </parameter>
 <exception name="SocketException" type="java.net.SocketException">
 </exception>
@@ -264407,6 +251806,21 @@
  visibility="protected"
 >
 </method>
+<method name="getOption"
+ return="java.lang.Object"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="optID" type="int">
+</parameter>
+<exception name="SocketException" type="java.net.SocketException">
+</exception>
+</method>
 <method name="getOutputStream"
  return="java.io.OutputStream"
  abstract="true"
@@ -264461,6 +251875,23 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
+<method name="setOption"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="optID" type="int">
+</parameter>
+<parameter name="val" type="java.lang.Object">
+</parameter>
+<exception name="SocketException" type="java.net.SocketException">
+</exception>
+</method>
 <method name="setPerformancePreferences"
  return="void"
  abstract="false"
@@ -264916,7 +252347,7 @@
 >
 <parameter name="scheme" type="java.lang.String">
 </parameter>
-<parameter name="userInfo" type="java.lang.String">
+<parameter name="userinfo" type="java.lang.String">
 </parameter>
 <parameter name="host" type="java.lang.String">
 </parameter>
@@ -266596,7 +254027,7 @@
 >
 <parameter name="s" type="java.lang.String">
 </parameter>
-<parameter name="encoding" type="java.lang.String">
+<parameter name="enc" type="java.lang.String">
 </parameter>
 <exception name="UnsupportedEncodingException" type="java.io.UnsupportedEncodingException">
 </exception>
@@ -266719,9 +254150,9 @@
  deprecated="not deprecated"
  visibility="protected"
 >
-<parameter name="a" type="java.net.URL">
+<parameter name="url1" type="java.net.URL">
 </parameter>
-<parameter name="b" type="java.net.URL">
+<parameter name="url2" type="java.net.URL">
 </parameter>
 </method>
 <method name="openConnection"
@@ -266942,28 +254373,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<method name="array"
- return="java.lang.Object"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="arrayOffset"
- return="int"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="capacity"
  return="int"
  abstract="false"
@@ -266997,17 +254406,6 @@
  visibility="public"
 >
 </method>
-<method name="hasArray"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="hasRemaining"
  return="boolean"
  abstract="false"
@@ -267019,17 +254417,6 @@
  visibility="public"
 >
 </method>
-<method name="isDirect"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="isReadOnly"
  return="boolean"
  abstract="true"
@@ -267359,7 +254746,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="byte[]">
+<parameter name="dest" type="byte[]">
 </parameter>
 </method>
 <method name="get"
@@ -267372,7 +254759,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="byte[]">
+<parameter name="dest" type="byte[]">
 </parameter>
 <parameter name="off" type="int">
 </parameter>
@@ -268077,7 +255464,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="char[]">
+<parameter name="dest" type="char[]">
 </parameter>
 </method>
 <method name="get"
@@ -268090,7 +255477,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="char[]">
+<parameter name="dest" type="char[]">
 </parameter>
 <parameter name="off" type="int">
 </parameter>
@@ -268469,7 +255856,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="double[]">
+<parameter name="dest" type="double[]">
 </parameter>
 </method>
 <method name="get"
@@ -268482,7 +255869,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="double[]">
+<parameter name="dest" type="double[]">
 </parameter>
 <parameter name="off" type="int">
 </parameter>
@@ -268760,7 +256147,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="float[]">
+<parameter name="dest" type="float[]">
 </parameter>
 </method>
 <method name="get"
@@ -268773,7 +256160,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="float[]">
+<parameter name="dest" type="float[]">
 </parameter>
 <parameter name="off" type="int">
 </parameter>
@@ -269051,7 +256438,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="int[]">
+<parameter name="dest" type="int[]">
 </parameter>
 </method>
 <method name="get"
@@ -269064,7 +256451,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="int[]">
+<parameter name="dest" type="int[]">
 </parameter>
 <parameter name="off" type="int">
 </parameter>
@@ -269359,7 +256746,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="long[]">
+<parameter name="dest" type="long[]">
 </parameter>
 </method>
 <method name="get"
@@ -269372,7 +256759,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="long[]">
+<parameter name="dest" type="long[]">
 </parameter>
 <parameter name="off" type="int">
 </parameter>
@@ -269709,7 +257096,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="short[]">
+<parameter name="dest" type="short[]">
 </parameter>
 </method>
 <method name="get"
@@ -269722,7 +257109,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="dst" type="short[]">
+<parameter name="dest" type="short[]">
 </parameter>
 <parameter name="off" type="int">
 </parameter>
@@ -272898,7 +260285,7 @@
  return="boolean"
  abstract="false"
  native="false"
- synchronized="false"
+ synchronized="true"
  static="true"
  final="false"
  deprecated="not deprecated"
@@ -273466,7 +260853,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="replacement" type="byte[]">
+<parameter name="repl" type="byte[]">
 </parameter>
 </method>
 <method name="malformedInputAction"
@@ -273785,7 +261172,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="charsetName" type="java.lang.String">
+<parameter name="charset" type="java.lang.String">
 </parameter>
 </constructor>
 <method name="getCharsetName"
@@ -273875,7 +261262,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="charsetName" type="java.lang.String">
+<parameter name="charset" type="java.lang.String">
 </parameter>
 </constructor>
 <method name="getCharsetName"
@@ -274054,7 +261441,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="permission" type="java.security.Permission">
+<parameter name="perm" type="java.security.Permission">
 </parameter>
 <exception name="AccessControlException" type="java.security.AccessControlException">
 </exception>
@@ -274119,34 +261506,6 @@
 <exception name="PrivilegedActionException" type="java.security.PrivilegedActionException">
 </exception>
 </method>
-<method name="doPrivilegedWithCombiner"
- return="T"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="action" type="java.security.PrivilegedAction&lt;T&gt;">
-</parameter>
-</method>
-<method name="doPrivilegedWithCombiner"
- return="T"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="action" type="java.security.PrivilegedExceptionAction&lt;T&gt;">
-</parameter>
-<exception name="PrivilegedActionException" type="java.security.PrivilegedActionException">
-</exception>
-</method>
 <method name="getContext"
  return="java.security.AccessControlContext"
  abstract="false"
@@ -278573,77 +265932,9 @@
  visibility="public"
 >
 </constructor>
-<method name="getInstance"
- return="java.security.Policy"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="type" type="java.lang.String">
-</parameter>
-<parameter name="params" type="java.security.Policy.Parameters">
-</parameter>
-<exception name="NoSuchAlgorithmException" type="java.security.NoSuchAlgorithmException">
-</exception>
-</method>
-<method name="getInstance"
- return="java.security.Policy"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="type" type="java.lang.String">
-</parameter>
-<parameter name="params" type="java.security.Policy.Parameters">
-</parameter>
-<parameter name="provider" type="java.lang.String">
-</parameter>
-<exception name="NoSuchAlgorithmException" type="java.security.NoSuchAlgorithmException">
-</exception>
-<exception name="NoSuchProviderException" type="java.security.NoSuchProviderException">
-</exception>
-</method>
-<method name="getInstance"
- return="java.security.Policy"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="type" type="java.lang.String">
-</parameter>
-<parameter name="params" type="java.security.Policy.Parameters">
-</parameter>
-<parameter name="provider" type="java.security.Provider">
-</parameter>
-<exception name="NoSuchAlgorithmException" type="java.security.NoSuchAlgorithmException">
-</exception>
-</method>
-<method name="getParameters"
- return="java.security.Policy.Parameters"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getPermissions"
  return="java.security.PermissionCollection"
- abstract="false"
+ abstract="true"
  native="false"
  synchronized="false"
  static="false"
@@ -278678,28 +265969,6 @@
  visibility="public"
 >
 </method>
-<method name="getProvider"
- return="java.security.Provider"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getType"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="implies"
  return="boolean"
  abstract="false"
@@ -278717,7 +265986,7 @@
 </method>
 <method name="refresh"
  return="void"
- abstract="false"
+ abstract="true"
  native="false"
  synchronized="false"
  static="false"
@@ -278739,93 +266008,6 @@
 <parameter name="policy" type="java.security.Policy">
 </parameter>
 </method>
-<field name="UNSUPPORTED_EMPTY_COLLECTION"
- type="java.security.PermissionCollection"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
-<interface name="Policy.Parameters"
- abstract="true"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</interface>
-<class name="PolicySpi"
- extends="java.lang.Object"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="PolicySpi"
- type="java.security.PolicySpi"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="engineGetPermissions"
- return="java.security.PermissionCollection"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="codesource" type="java.security.CodeSource">
-</parameter>
-</method>
-<method name="engineGetPermissions"
- return="java.security.PermissionCollection"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="domain" type="java.security.ProtectionDomain">
-</parameter>
-</method>
-<method name="engineImplies"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="domain" type="java.security.ProtectionDomain">
-</parameter>
-<parameter name="permission" type="java.security.Permission">
-</parameter>
-</method>
-<method name="engineRefresh"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
 </class>
 <interface name="Principal"
  abstract="true"
@@ -287791,19 +274973,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<method name="free"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getArray"
  return="java.lang.Object"
  abstract="true"
@@ -287984,76 +275153,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="BatchUpdateException"
- type="java.sql.BatchUpdateException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="updateCounts" type="int[]">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="BatchUpdateException"
- type="java.sql.BatchUpdateException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="updateCounts" type="int[]">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="BatchUpdateException"
- type="java.sql.BatchUpdateException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="SQLState" type="java.lang.String">
-</parameter>
-<parameter name="updateCounts" type="int[]">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="BatchUpdateException"
- type="java.sql.BatchUpdateException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="SQLState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="updateCounts" type="int[]">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="BatchUpdateException"
- type="java.sql.BatchUpdateException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="updateCounts" type="int[]">
 </parameter>
 </constructor>
@@ -288118,19 +275217,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<method name="free"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getBinaryStream"
  return="java.io.InputStream"
  abstract="true"
@@ -288144,23 +275230,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getBinaryStream"
- return="java.io.InputStream"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pos" type="long">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getBytes"
  return="byte[]"
  abstract="true"
@@ -288500,36 +275569,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getCharacterStream"
- return="java.io.Reader"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getCharacterStream"
- return="java.io.Reader"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getClob"
  return="java.sql.Clob"
  abstract="true"
@@ -288744,96 +275783,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getNCharacterStream"
- return="java.io.Reader"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getNCharacterStream"
- return="java.io.Reader"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getNClob"
- return="java.sql.NClob"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getNClob"
- return="java.sql.NClob"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getNString"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getNString"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getObject"
  return="java.lang.Object"
  abstract="true"
@@ -288928,66 +275877,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getRowId"
- return="java.sql.RowId"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getRowId"
- return="java.sql.RowId"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getSQLXML"
- return="java.sql.SQLXML"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getSQLXML"
- return="java.sql.SQLXML"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getShort"
  return="short"
  abstract="true"
@@ -289335,42 +276224,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setAsciiStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="x" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setAsciiStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="x" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setBigDecimal"
  return="void"
  abstract="true"
@@ -289407,95 +276260,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setBinaryStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="x" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBinaryStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="x" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="inputStream" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="blob" type="java.sql.Blob">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="inputStream" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setBoolean"
  return="void"
  abstract="true"
@@ -289566,95 +276330,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="clob" type="java.sql.Clob">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setDate"
  return="void"
  abstract="true"
@@ -289759,112 +276434,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setNCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="value" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="nclob" type="java.sql.NClob">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNString"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="string" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setNull"
  return="void"
  abstract="true"
@@ -289958,40 +276527,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setRowId"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="rowId" type="java.sql.RowId">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setSQLXML"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="sqlXml" type="java.sql.SQLXML">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setShort"
  return="void"
  abstract="true"
@@ -290129,39 +276664,6 @@
 </exception>
 </method>
 </interface>
-<class name="ClientInfoStatus"
- extends="java.lang.Enum"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="valueOf"
- return="java.sql.ClientInfoStatus"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="name" type="java.lang.String">
-</parameter>
-</method>
-<method name="values"
- return="java.sql.ClientInfoStatus[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
 <interface name="Clob"
  abstract="true"
  static="false"
@@ -290169,19 +276671,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<method name="free"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getAsciiStream"
  return="java.io.InputStream"
  abstract="true"
@@ -290208,23 +276697,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getCharacterStream"
- return="java.io.Reader"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="pos" type="long">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getSubString"
  return="java.lang.String"
  abstract="true"
@@ -290380,8 +276852,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<implements name="java.sql.Wrapper">
-</implements>
 <method name="clearWarnings"
  return="void"
  abstract="true"
@@ -290421,75 +276891,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="createArrayOf"
- return="java.sql.Array"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="typeName" type="java.lang.String">
-</parameter>
-<parameter name="elements" type="java.lang.Object[]">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="createBlob"
- return="java.sql.Blob"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="createClob"
- return="java.sql.Clob"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="createNClob"
- return="java.sql.NClob"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="createSQLXML"
- return="java.sql.SQLXML"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="createStatement"
  return="java.sql.Statement"
  abstract="true"
@@ -290539,23 +276940,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="createStruct"
- return="java.sql.Struct"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="typeName" type="java.lang.String">
-</parameter>
-<parameter name="attributes" type="java.lang.Object[]">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getAutoCommit"
  return="boolean"
  abstract="true"
@@ -290582,34 +276966,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getClientInfo"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="name" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getClientInfo"
- return="java.util.Properties"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getHoldability"
  return="int"
  abstract="true"
@@ -290701,21 +277057,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="isValid"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="timeout" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="nativeSQL"
  return="java.lang.String"
  abstract="true"
@@ -290965,38 +277306,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setClientInfo"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="name" type="java.lang.String">
-</parameter>
-<parameter name="value" type="java.lang.String">
-</parameter>
-<exception name="SQLClientInfoException" type="java.sql.SQLClientInfoException">
-</exception>
-</method>
-<method name="setClientInfo"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="properties" type="java.util.Properties">
-</parameter>
-<exception name="SQLClientInfoException" type="java.sql.SQLClientInfoException">
-</exception>
-</method>
 <method name="setHoldability"
  return="void"
  abstract="true"
@@ -291169,26 +277478,6 @@
 <parameter name="transferSize" type="int">
 </parameter>
 </constructor>
-<constructor name="DataTruncation"
- type="java.sql.DataTruncation"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="index" type="int">
-</parameter>
-<parameter name="parameter" type="boolean">
-</parameter>
-<parameter name="read" type="boolean">
-</parameter>
-<parameter name="dataSize" type="int">
-</parameter>
-<parameter name="transferSize" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
 <method name="getDataSize"
  return="int"
  abstract="false"
@@ -291252,8 +277541,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<implements name="java.sql.Wrapper">
-</implements>
 <method name="allProceduresAreCallable"
  return="boolean"
  abstract="true"
@@ -291280,19 +277567,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="autoCommitFailureClosesAllResultSets"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="dataDefinitionCausesTransactionCommit"
  return="boolean"
  abstract="true"
@@ -291430,19 +277704,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getClientInfoProperties"
- return="java.sql.ResultSet"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getColumnPrivileges"
  return="java.sql.ResultSet"
  abstract="true"
@@ -291668,46 +277929,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getFunctionColumns"
- return="java.sql.ResultSet"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="catalog" type="java.lang.String">
-</parameter>
-<parameter name="schemaPattern" type="java.lang.String">
-</parameter>
-<parameter name="functionNamePattern" type="java.lang.String">
-</parameter>
-<parameter name="columnNamePattern" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getFunctions"
- return="java.sql.ResultSet"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="catalog" type="java.lang.String">
-</parameter>
-<parameter name="schemaPattern" type="java.lang.String">
-</parameter>
-<parameter name="functionNamePattern" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getIdentifierQuoteString"
  return="java.lang.String"
  abstract="true"
@@ -292147,19 +278368,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getRowIdLifetime"
- return="java.sql.RowIdLifetime"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getSQLKeywords"
  return="java.lang.String"
  abstract="true"
@@ -292212,23 +278420,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getSchemas"
- return="java.sql.ResultSet"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="catalog" type="java.lang.String">
-</parameter>
-<parameter name="schemaPattern" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getSearchStringEscape"
  return="java.lang.String"
  abstract="true"
@@ -293478,19 +279669,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="supportsStoredFunctionsUsingCallSyntax"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="supportsStoredProcedures"
  return="boolean"
  abstract="true"
@@ -293796,138 +279974,6 @@
  visibility="public"
 >
 </field>
-<field name="functionColumnIn"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="functionColumnInOut"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="functionColumnOut"
- type="int"
- transient="false"
- volatile="false"
- value="3"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="functionColumnResult"
- type="int"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="functionColumnUnknown"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="functionNoNulls"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="functionNoTable"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="functionNullable"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="functionNullableUnknown"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="functionResultUnknown"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="functionReturn"
- type="int"
- transient="false"
- volatile="false"
- value="4"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="functionReturnsTable"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="importedKeyCascade"
  type="int"
  transient="false"
@@ -294148,17 +280194,6 @@
  visibility="public"
 >
 </field>
-<field name="sqlStateSQL"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="sqlStateSQL99"
  type="int"
  transient="false"
@@ -294745,16 +280780,6 @@
 >
 </field>
 </class>
-<interface name="NClob"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.sql.Clob">
-</implements>
-</interface>
 <interface name="ParameterMetaData"
  abstract="true"
  static="false"
@@ -294762,8 +280787,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<implements name="java.sql.Wrapper">
-</implements>
 <method name="getParameterClassName"
  return="java.lang.String"
  abstract="true"
@@ -295111,42 +281134,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setAsciiStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="inputStream" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setAsciiStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="inputStream" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setBigDecimal"
  return="void"
  abstract="true"
@@ -295183,42 +281170,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setBinaryStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="inputStream" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBinaryStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="inputStream" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setBlob"
  return="void"
  abstract="true"
@@ -295236,42 +281187,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="inputStream" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="inputStream" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setBoolean"
  return="void"
  abstract="true"
@@ -295342,42 +281257,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setClob"
  return="void"
  abstract="true"
@@ -295395,42 +281274,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setDate"
  return="void"
  abstract="true"
@@ -295535,112 +281378,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setNCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="value" type="java.sql.NClob">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNString"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theString" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setNull"
  return="void"
  abstract="true"
@@ -295751,40 +281488,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setRowId"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theRowId" type="java.sql.RowId">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setSQLXML"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="xmlObject" type="java.sql.SQLXML">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setShort"
  return="void"
  abstract="true"
@@ -295999,8 +281702,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<implements name="java.sql.Wrapper">
-</implements>
 <method name="absolute"
  return="boolean"
  abstract="true"
@@ -296632,19 +282333,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getHoldability"
- return="int"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getInt"
  return="int"
  abstract="true"
@@ -296718,96 +282406,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getNCharacterStream"
- return="java.io.Reader"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getNCharacterStream"
- return="java.io.Reader"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getNClob"
- return="java.sql.NClob"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getNClob"
- return="java.sql.NClob"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getNString"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getNString"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getObject"
  return="java.lang.Object"
  abstract="true"
@@ -296915,66 +282513,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="getRowId"
- return="java.sql.RowId"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getRowId"
- return="java.sql.RowId"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getSQLXML"
- return="java.sql.SQLXML"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getSQLXML"
- return="java.sql.SQLXML"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="getShort"
  return="short"
  abstract="true"
@@ -297301,19 +282839,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="isClosed"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="isFirst"
  return="boolean"
  abstract="true"
@@ -297574,78 +283099,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="updateAsciiStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="x" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateAsciiStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="x" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateAsciiStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="x" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateAsciiStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="x" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="updateBigDecimal"
  return="void"
  abstract="true"
@@ -297718,78 +283171,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="updateBinaryStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="x" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateBinaryStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="x" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateBinaryStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="x" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateBinaryStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="x" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="updateBlob"
  return="void"
  abstract="true"
@@ -297824,78 +283205,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="updateBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="inputStream" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="inputStream" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="inputStream" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="inputStream" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="updateBoolean"
  return="void"
  abstract="true"
@@ -298036,78 +283345,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="updateCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="x" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="x" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="updateClob"
  return="void"
  abstract="true"
@@ -298142,78 +283379,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="updateClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="updateDate"
  return="void"
  abstract="true"
@@ -298384,218 +283549,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="updateNCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="x" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateNCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateNCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="x" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateNCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="nClob" type="java.sql.NClob">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="nClob" type="java.sql.NClob">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateNString"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="nString" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateNString"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="nString" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="updateNull"
  return="void"
  abstract="true"
@@ -298745,74 +283698,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="updateRowId"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="java.sql.RowId">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateRowId"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="value" type="java.sql.RowId">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateSQLXML"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="xmlObject" type="java.sql.SQLXML">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="updateSQLXML"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnLabel" type="java.lang.String">
-</parameter>
-<parameter name="xmlObject" type="java.sql.SQLXML">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="updateShort"
  return="void"
  abstract="true"
@@ -299080,8 +283965,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<implements name="java.sql.Wrapper">
-</implements>
 <method name="getCatalogName"
  return="java.lang.String"
  abstract="true"
@@ -299429,233 +284312,6 @@
 >
 </field>
 </interface>
-<interface name="RowId"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="equals"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="obj" type="java.lang.Object">
-</parameter>
-</method>
-<method name="getBytes"
- return="byte[]"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="hashCode"
- return="int"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="toString"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</interface>
-<class name="RowIdLifetime"
- extends="java.lang.Enum"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="valueOf"
- return="java.sql.RowIdLifetime"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="name" type="java.lang.String">
-</parameter>
-</method>
-<method name="values"
- return="java.sql.RowIdLifetime[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
-<class name="SQLClientInfoException"
- extends="java.sql.SQLException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLClientInfoException"
- type="java.sql.SQLClientInfoException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLClientInfoException"
- type="java.sql.SQLClientInfoException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="failedProperties" type="java.util.Map&lt;java.lang.String, java.sql.ClientInfoStatus&gt;">
-</parameter>
-</constructor>
-<constructor name="SQLClientInfoException"
- type="java.sql.SQLClientInfoException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="failedProperties" type="java.util.Map&lt;java.lang.String, java.sql.ClientInfoStatus&gt;">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLClientInfoException"
- type="java.sql.SQLClientInfoException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="failedProperties" type="java.util.Map&lt;java.lang.String, java.sql.ClientInfoStatus&gt;">
-</parameter>
-</constructor>
-<constructor name="SQLClientInfoException"
- type="java.sql.SQLClientInfoException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="failedProperties" type="java.util.Map&lt;java.lang.String, java.sql.ClientInfoStatus&gt;">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLClientInfoException"
- type="java.sql.SQLClientInfoException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="failedProperties" type="java.util.Map&lt;java.lang.String, java.sql.ClientInfoStatus&gt;">
-</parameter>
-</constructor>
-<constructor name="SQLClientInfoException"
- type="java.sql.SQLClientInfoException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="failedProperties" type="java.util.Map&lt;java.lang.String, java.sql.ClientInfoStatus&gt;">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLClientInfoException"
- type="java.sql.SQLClientInfoException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="failedProperties" type="java.util.Map&lt;java.lang.String, java.sql.ClientInfoStatus&gt;">
-</parameter>
-</constructor>
-<constructor name="SQLClientInfoException"
- type="java.sql.SQLClientInfoException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="failedProperties" type="java.util.Map&lt;java.lang.String, java.sql.ClientInfoStatus&gt;">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<method name="getFailedProperties"
- return="java.util.Map&lt;java.lang.String, java.sql.ClientInfoStatus&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
 <interface name="SQLData"
  abstract="true"
  static="false"
@@ -299709,111 +284365,6 @@
 </exception>
 </method>
 </interface>
-<class name="SQLDataException"
- extends="java.sql.SQLNonTransientException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLDataException"
- type="java.sql.SQLDataException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLDataException"
- type="java.sql.SQLDataException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLDataException"
- type="java.sql.SQLDataException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLDataException"
- type="java.sql.SQLDataException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-</constructor>
-<constructor name="SQLDataException"
- type="java.sql.SQLDataException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLDataException"
- type="java.sql.SQLDataException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLDataException"
- type="java.sql.SQLDataException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLDataException"
- type="java.sql.SQLDataException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
 <class name="SQLException"
  extends="java.lang.Exception"
  abstract="false"
@@ -299822,8 +284373,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<implements name="java.lang.Iterable">
-</implements>
 <implements name="java.io.Serializable">
 </implements>
 <constructor name="SQLException"
@@ -299870,58 +284419,6 @@
 <parameter name="theErrorCode" type="int">
 </parameter>
 </constructor>
-<constructor name="SQLException"
- type="java.sql.SQLException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="theCause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLException"
- type="java.sql.SQLException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="theReason" type="java.lang.String">
-</parameter>
-<parameter name="theCause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLException"
- type="java.sql.SQLException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="theReason" type="java.lang.String">
-</parameter>
-<parameter name="theSQLState" type="java.lang.String">
-</parameter>
-<parameter name="theCause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLException"
- type="java.sql.SQLException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="theReason" type="java.lang.String">
-</parameter>
-<parameter name="theSQLState" type="java.lang.String">
-</parameter>
-<parameter name="theErrorCode" type="int">
-</parameter>
-<parameter name="theCause" type="java.lang.Throwable">
-</parameter>
-</constructor>
 <method name="getErrorCode"
  return="int"
  abstract="false"
@@ -299955,17 +284452,6 @@
  visibility="public"
 >
 </method>
-<method name="iterator"
- return="java.util.Iterator&lt;java.lang.Throwable&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="setNextException"
  return="void"
  abstract="false"
@@ -299980,111 +284466,6 @@
 </parameter>
 </method>
 </class>
-<class name="SQLFeatureNotSupportedException"
- extends="java.sql.SQLNonTransientException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLFeatureNotSupportedException"
- type="java.sql.SQLFeatureNotSupportedException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLFeatureNotSupportedException"
- type="java.sql.SQLFeatureNotSupportedException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLFeatureNotSupportedException"
- type="java.sql.SQLFeatureNotSupportedException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLFeatureNotSupportedException"
- type="java.sql.SQLFeatureNotSupportedException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-</constructor>
-<constructor name="SQLFeatureNotSupportedException"
- type="java.sql.SQLFeatureNotSupportedException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLFeatureNotSupportedException"
- type="java.sql.SQLFeatureNotSupportedException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLFeatureNotSupportedException"
- type="java.sql.SQLFeatureNotSupportedException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLFeatureNotSupportedException"
- type="java.sql.SQLFeatureNotSupportedException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
 <interface name="SQLInput"
  abstract="true"
  static="false"
@@ -300287,32 +284668,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="readNClob"
- return="java.sql.NClob"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="readNString"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="readObject"
  return="java.lang.Object"
  abstract="true"
@@ -300339,32 +284694,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="readRowId"
- return="java.sql.RowId"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="readSQLXML"
- return="java.sql.SQLXML"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="readShort"
  return="short"
  abstract="true"
@@ -300444,426 +284773,6 @@
 </exception>
 </method>
 </interface>
-<class name="SQLIntegrityConstraintViolationException"
- extends="java.sql.SQLNonTransientException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLIntegrityConstraintViolationException"
- type="java.sql.SQLIntegrityConstraintViolationException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLIntegrityConstraintViolationException"
- type="java.sql.SQLIntegrityConstraintViolationException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLIntegrityConstraintViolationException"
- type="java.sql.SQLIntegrityConstraintViolationException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLIntegrityConstraintViolationException"
- type="java.sql.SQLIntegrityConstraintViolationException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-</constructor>
-<constructor name="SQLIntegrityConstraintViolationException"
- type="java.sql.SQLIntegrityConstraintViolationException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLIntegrityConstraintViolationException"
- type="java.sql.SQLIntegrityConstraintViolationException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLIntegrityConstraintViolationException"
- type="java.sql.SQLIntegrityConstraintViolationException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLIntegrityConstraintViolationException"
- type="java.sql.SQLIntegrityConstraintViolationException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
-<class name="SQLInvalidAuthorizationSpecException"
- extends="java.sql.SQLNonTransientException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLInvalidAuthorizationSpecException"
- type="java.sql.SQLInvalidAuthorizationSpecException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLInvalidAuthorizationSpecException"
- type="java.sql.SQLInvalidAuthorizationSpecException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLInvalidAuthorizationSpecException"
- type="java.sql.SQLInvalidAuthorizationSpecException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLInvalidAuthorizationSpecException"
- type="java.sql.SQLInvalidAuthorizationSpecException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-</constructor>
-<constructor name="SQLInvalidAuthorizationSpecException"
- type="java.sql.SQLInvalidAuthorizationSpecException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLInvalidAuthorizationSpecException"
- type="java.sql.SQLInvalidAuthorizationSpecException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLInvalidAuthorizationSpecException"
- type="java.sql.SQLInvalidAuthorizationSpecException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLInvalidAuthorizationSpecException"
- type="java.sql.SQLInvalidAuthorizationSpecException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
-<class name="SQLNonTransientConnectionException"
- extends="java.sql.SQLNonTransientException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLNonTransientConnectionException"
- type="java.sql.SQLNonTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLNonTransientConnectionException"
- type="java.sql.SQLNonTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLNonTransientConnectionException"
- type="java.sql.SQLNonTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLNonTransientConnectionException"
- type="java.sql.SQLNonTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-</constructor>
-<constructor name="SQLNonTransientConnectionException"
- type="java.sql.SQLNonTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLNonTransientConnectionException"
- type="java.sql.SQLNonTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLNonTransientConnectionException"
- type="java.sql.SQLNonTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLNonTransientConnectionException"
- type="java.sql.SQLNonTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
-<class name="SQLNonTransientException"
- extends="java.sql.SQLException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLNonTransientException"
- type="java.sql.SQLNonTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLNonTransientException"
- type="java.sql.SQLNonTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLNonTransientException"
- type="java.sql.SQLNonTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLNonTransientException"
- type="java.sql.SQLNonTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-</constructor>
-<constructor name="SQLNonTransientException"
- type="java.sql.SQLNonTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLNonTransientException"
- type="java.sql.SQLNonTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLNonTransientException"
- type="java.sql.SQLNonTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLNonTransientException"
- type="java.sql.SQLNonTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
 <interface name="SQLOutput"
  abstract="true"
  static="false"
@@ -301096,36 +285005,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="writeNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="theNClob" type="java.sql.NClob">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="writeNString"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="theString" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="writeObject"
  return="void"
  abstract="true"
@@ -301156,36 +285035,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="writeRowId"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="theRowId" type="java.sql.RowId">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="writeSQLXML"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="theXml" type="java.sql.SQLXML">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="writeShort"
  return="void"
  abstract="true"
@@ -301312,636 +285161,6 @@
 </parameter>
 </constructor>
 </class>
-<class name="SQLRecoverableException"
- extends="java.sql.SQLException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLRecoverableException"
- type="java.sql.SQLRecoverableException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLRecoverableException"
- type="java.sql.SQLRecoverableException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLRecoverableException"
- type="java.sql.SQLRecoverableException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLRecoverableException"
- type="java.sql.SQLRecoverableException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-</constructor>
-<constructor name="SQLRecoverableException"
- type="java.sql.SQLRecoverableException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLRecoverableException"
- type="java.sql.SQLRecoverableException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLRecoverableException"
- type="java.sql.SQLRecoverableException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLRecoverableException"
- type="java.sql.SQLRecoverableException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
-<class name="SQLSyntaxErrorException"
- extends="java.sql.SQLNonTransientException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLSyntaxErrorException"
- type="java.sql.SQLSyntaxErrorException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLSyntaxErrorException"
- type="java.sql.SQLSyntaxErrorException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLSyntaxErrorException"
- type="java.sql.SQLSyntaxErrorException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLSyntaxErrorException"
- type="java.sql.SQLSyntaxErrorException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-</constructor>
-<constructor name="SQLSyntaxErrorException"
- type="java.sql.SQLSyntaxErrorException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLSyntaxErrorException"
- type="java.sql.SQLSyntaxErrorException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLSyntaxErrorException"
- type="java.sql.SQLSyntaxErrorException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLSyntaxErrorException"
- type="java.sql.SQLSyntaxErrorException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
-<class name="SQLTimeoutException"
- extends="java.sql.SQLTransientException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLTimeoutException"
- type="java.sql.SQLTimeoutException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLTimeoutException"
- type="java.sql.SQLTimeoutException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLTimeoutException"
- type="java.sql.SQLTimeoutException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLTimeoutException"
- type="java.sql.SQLTimeoutException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-</constructor>
-<constructor name="SQLTimeoutException"
- type="java.sql.SQLTimeoutException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLTimeoutException"
- type="java.sql.SQLTimeoutException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLTimeoutException"
- type="java.sql.SQLTimeoutException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLTimeoutException"
- type="java.sql.SQLTimeoutException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
-<class name="SQLTransactionRollbackException"
- extends="java.sql.SQLTransientException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLTransactionRollbackException"
- type="java.sql.SQLTransactionRollbackException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLTransactionRollbackException"
- type="java.sql.SQLTransactionRollbackException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLTransactionRollbackException"
- type="java.sql.SQLTransactionRollbackException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLTransactionRollbackException"
- type="java.sql.SQLTransactionRollbackException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-</constructor>
-<constructor name="SQLTransactionRollbackException"
- type="java.sql.SQLTransactionRollbackException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLTransactionRollbackException"
- type="java.sql.SQLTransactionRollbackException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLTransactionRollbackException"
- type="java.sql.SQLTransactionRollbackException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLTransactionRollbackException"
- type="java.sql.SQLTransactionRollbackException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
-<class name="SQLTransientConnectionException"
- extends="java.sql.SQLTransientException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLTransientConnectionException"
- type="java.sql.SQLTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLTransientConnectionException"
- type="java.sql.SQLTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLTransientConnectionException"
- type="java.sql.SQLTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLTransientConnectionException"
- type="java.sql.SQLTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-</constructor>
-<constructor name="SQLTransientConnectionException"
- type="java.sql.SQLTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLTransientConnectionException"
- type="java.sql.SQLTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLTransientConnectionException"
- type="java.sql.SQLTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLTransientConnectionException"
- type="java.sql.SQLTransientConnectionException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
-<class name="SQLTransientException"
- extends="java.sql.SQLException"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SQLTransientException"
- type="java.sql.SQLTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SQLTransientException"
- type="java.sql.SQLTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLTransientException"
- type="java.sql.SQLTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="SQLTransientException"
- type="java.sql.SQLTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-</constructor>
-<constructor name="SQLTransientException"
- type="java.sql.SQLTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLTransientException"
- type="java.sql.SQLTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLTransientException"
- type="java.sql.SQLTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLTransientException"
- type="java.sql.SQLTransientException"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="sqlState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
 <class name="SQLWarning"
  extends="java.sql.SQLException"
  abstract="false"
@@ -301996,58 +285215,6 @@
 <parameter name="theErrorCode" type="int">
 </parameter>
 </constructor>
-<constructor name="SQLWarning"
- type="java.sql.SQLWarning"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLWarning"
- type="java.sql.SQLWarning"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLWarning"
- type="java.sql.SQLWarning"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="SQLState" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-<constructor name="SQLWarning"
- type="java.sql.SQLWarning"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reason" type="java.lang.String">
-</parameter>
-<parameter name="SQLState" type="java.lang.String">
-</parameter>
-<parameter name="vendorCode" type="int">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
 <method name="getNextWarning"
  return="java.sql.SQLWarning"
  abstract="false"
@@ -302073,137 +285240,6 @@
 </parameter>
 </method>
 </class>
-<interface name="SQLXML"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="free"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getBinaryStream"
- return="java.io.InputStream"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getCharacterStream"
- return="java.io.Reader"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getSource"
- return="T"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="sourceClass" type="java.lang.Class&lt;T&gt;">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getString"
- return="java.lang.String"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBinaryStream"
- return="java.io.OutputStream"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setCharacterStream"
- return="java.io.Writer"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setResult"
- return="T"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="resultClass" type="java.lang.Class&lt;T&gt;">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setString"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="value" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-</interface>
 <interface name="Savepoint"
  abstract="true"
  static="false"
@@ -302245,8 +285281,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<implements name="java.sql.Wrapper">
-</implements>
 <method name="addBatch"
  return="void"
  abstract="true"
@@ -302671,32 +285705,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="isClosed"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="isPoolable"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setCursorName"
  return="void"
  abstract="true"
@@ -302787,21 +285795,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setPoolable"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="poolable" type="boolean">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setQueryTimeout"
  return="void"
  abstract="true"
@@ -303310,17 +286303,6 @@
  visibility="public"
 >
 </field>
-<field name="LONGNVARCHAR"
- type="int"
- transient="false"
- volatile="false"
- value="-16"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="LONGVARBINARY"
  type="int"
  transient="false"
@@ -303343,28 +286325,6 @@
  visibility="public"
 >
 </field>
-<field name="NCHAR"
- type="int"
- transient="false"
- volatile="false"
- value="-15"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="NCLOB"
- type="int"
- transient="false"
- volatile="false"
- value="2011"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="NULL"
  type="int"
  transient="false"
@@ -303387,17 +286347,6 @@
  visibility="public"
 >
 </field>
-<field name="NVARCHAR"
- type="int"
- transient="false"
- volatile="false"
- value="-9"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="OTHER"
  type="int"
  transient="false"
@@ -303431,17 +286380,6 @@
  visibility="public"
 >
 </field>
-<field name="ROWID"
- type="int"
- transient="false"
- volatile="false"
- value="-8"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="SMALLINT"
  type="int"
  transient="false"
@@ -303453,17 +286391,6 @@
  visibility="public"
 >
 </field>
-<field name="SQLXML"
- type="int"
- transient="false"
- volatile="false"
- value="2009"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="STRUCT"
  type="int"
  transient="false"
@@ -303531,44 +286458,6 @@
 >
 </field>
 </class>
-<interface name="Wrapper"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="isWrapperFor"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="iface" type="java.lang.Class&lt;?&gt;">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="unwrap"
- return="T"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="iface" type="java.lang.Class&lt;T&gt;">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-</interface>
 </package>
 <package name="java.text"
 >
@@ -305032,16 +287921,6 @@
 >
 <implements name="java.lang.Comparable">
 </implements>
-<constructor name="CollationKey"
- type="java.text.CollationKey"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="source" type="java.lang.String">
-</parameter>
-</constructor>
 <method name="compareTo"
  return="int"
  abstract="true"
@@ -306237,17 +289116,6 @@
  visibility="public"
 >
 </method>
-<method name="getAvailableLocales"
- return="java.util.Locale[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getEras"
  return="java.lang.String[]"
  abstract="false"
@@ -306259,30 +289127,6 @@
  visibility="public"
 >
 </method>
-<method name="getInstance"
- return="java.text.DateFormatSymbols"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getInstance"
- return="java.text.DateFormatSymbols"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="locale" type="java.util.Locale">
-</parameter>
-</method>
 <method name="getLocalPatternChars"
  return="java.lang.String"
  abstract="false"
@@ -306450,7 +289294,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="zoneStrings" type="java.lang.String[][]">
+<parameter name="data" type="java.lang.String[][]">
 </parameter>
 </method>
 </class>
@@ -306564,9 +289408,9 @@
 >
 <parameter name="number" type="java.lang.Object">
 </parameter>
-<parameter name="buffer" type="java.lang.StringBuffer">
+<parameter name="toAppendTo" type="java.lang.StringBuffer">
 </parameter>
-<parameter name="position" type="java.text.FieldPosition">
+<parameter name="pos" type="java.text.FieldPosition">
 </parameter>
 </method>
 <method name="getDecimalFormatSymbols"
@@ -306864,17 +289708,6 @@
  visibility="public"
 >
 </method>
-<method name="getAvailableLocales"
- return="java.util.Locale[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getCurrency"
  return="java.util.Currency"
  abstract="false"
@@ -306919,17 +289752,6 @@
  visibility="public"
 >
 </method>
-<method name="getExponentSeparator"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getGroupingSeparator"
  return="char"
  abstract="false"
@@ -306952,30 +289774,6 @@
  visibility="public"
 >
 </method>
-<method name="getInstance"
- return="java.text.DecimalFormatSymbols"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getInstance"
- return="java.text.DecimalFormatSymbols"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="locale" type="java.util.Locale">
-</parameter>
-</method>
 <method name="getInternationalCurrencySymbol"
  return="java.lang.String"
  abstract="false"
@@ -307116,19 +289914,6 @@
 <parameter name="value" type="char">
 </parameter>
 </method>
-<method name="setExponentSeparator"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="value" type="java.lang.String">
-</parameter>
-</method>
 <method name="setGroupingSeparator"
  return="void"
  abstract="false"
@@ -307582,9 +290367,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="format" type="java.lang.String">
+<parameter name="template" type="java.lang.String">
 </parameter>
-<parameter name="args" type="java.lang.Object...">
+<parameter name="objects" type="java.lang.Object...">
 </parameter>
 </method>
 <method name="getFormats"
@@ -307775,78 +290560,6 @@
 >
 </field>
 </class>
-<class name="Normalizer"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="isNormalized"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="src" type="java.lang.CharSequence">
-</parameter>
-<parameter name="form" type="java.text.Normalizer.Form">
-</parameter>
-</method>
-<method name="normalize"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="src" type="java.lang.CharSequence">
-</parameter>
-<parameter name="form" type="java.text.Normalizer.Form">
-</parameter>
-</method>
-</class>
-<class name="Normalizer.Form"
- extends="java.lang.Enum"
- abstract="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="valueOf"
- return="java.text.Normalizer.Form"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="name" type="java.lang.String">
-</parameter>
-</method>
-<method name="values"
- return="java.text.Normalizer.Form[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
 <class name="NumberFormat"
  extends="java.text.Format"
  abstract="true"
@@ -308126,17 +290839,6 @@
 <parameter name="locale" type="java.util.Locale">
 </parameter>
 </method>
-<method name="getRoundingMode"
- return="java.math.RoundingMode"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="isGroupingUsed"
  return="boolean"
  abstract="false"
@@ -308295,19 +290997,6 @@
 <parameter name="value" type="boolean">
 </parameter>
 </method>
-<method name="setRoundingMode"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="roundingMode" type="java.math.RoundingMode">
-</parameter>
-</method>
 <field name="FRACTION_FIELD"
  type="int"
  transient="false"
@@ -309546,146 +292235,6 @@
 >
 </method>
 </class>
-<class name="AbstractMap.SimpleEntry"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.util.Map.Entry">
-</implements>
-<implements name="java.io.Serializable">
-</implements>
-<constructor name="AbstractMap.SimpleEntry"
- type="java.util.AbstractMap.SimpleEntry"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="theKey" type="K">
-</parameter>
-<parameter name="theValue" type="V">
-</parameter>
-</constructor>
-<constructor name="AbstractMap.SimpleEntry"
- type="java.util.AbstractMap.SimpleEntry"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="copyFrom" type="java.util.Map.Entry&lt;? extends K, ? extends V&gt;">
-</parameter>
-</constructor>
-<method name="getKey"
- return="K"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getValue"
- return="V"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setValue"
- return="V"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="object" type="V">
-</parameter>
-</method>
-</class>
-<class name="AbstractMap.SimpleImmutableEntry"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.util.Map.Entry">
-</implements>
-<implements name="java.io.Serializable">
-</implements>
-<constructor name="AbstractMap.SimpleImmutableEntry"
- type="java.util.AbstractMap.SimpleImmutableEntry"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="theKey" type="K">
-</parameter>
-<parameter name="theValue" type="V">
-</parameter>
-</constructor>
-<constructor name="AbstractMap.SimpleImmutableEntry"
- type="java.util.AbstractMap.SimpleImmutableEntry"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="copyFrom" type="java.util.Map.Entry&lt;? extends K, ? extends V&gt;">
-</parameter>
-</constructor>
-<method name="getKey"
- return="K"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getValue"
- return="V"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setValue"
- return="V"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="object" type="V">
-</parameter>
-</method>
-</class>
 <class name="AbstractQueue"
  extends="java.util.AbstractCollection"
  abstract="true"
@@ -309789,340 +292338,6 @@
 >
 </constructor>
 </class>
-<class name="ArrayDeque"
- extends="java.util.AbstractCollection"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.lang.Cloneable">
-</implements>
-<implements name="java.util.Deque">
-</implements>
-<implements name="java.io.Serializable">
-</implements>
-<constructor name="ArrayDeque"
- type="java.util.ArrayDeque"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="ArrayDeque"
- type="java.util.ArrayDeque"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="minSize" type="int">
-</parameter>
-</constructor>
-<constructor name="ArrayDeque"
- type="java.util.ArrayDeque"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="c" type="java.util.Collection&lt;? extends E&gt;">
-</parameter>
-</constructor>
-<method name="addFirst"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="addLast"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="clone"
- return="java.util.ArrayDeque&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="descendingIterator"
- return="java.util.Iterator&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="element"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="iterator"
- return="java.util.Iterator&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="offer"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="offerFirst"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="offerLast"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="peek"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="peekFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="peekLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="poll"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pop"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="push"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="remove"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="removeFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="removeFirstOccurrence"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="obj" type="java.lang.Object">
-</parameter>
-</method>
-<method name="removeLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="removeLastOccurrence"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="obj" type="java.lang.Object">
-</parameter>
-</method>
-<method name="size"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
 <class name="ArrayList"
  extends="java.util.AbstractList"
  abstract="false"
@@ -310271,25 +292486,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="array" type="byte[]">
-</parameter>
-<parameter name="startIndex" type="int">
-</parameter>
-<parameter name="endIndex" type="int">
-</parameter>
-<parameter name="value" type="byte">
-</parameter>
-</method>
-<method name="binarySearch"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="array" type="char[]">
 </parameter>
 <parameter name="value" type="char">
@@ -310305,25 +292501,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="array" type="char[]">
-</parameter>
-<parameter name="startIndex" type="int">
-</parameter>
-<parameter name="endIndex" type="int">
-</parameter>
-<parameter name="value" type="char">
-</parameter>
-</method>
-<method name="binarySearch"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="array" type="double[]">
 </parameter>
 <parameter name="value" type="double">
@@ -310339,25 +292516,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="array" type="double[]">
-</parameter>
-<parameter name="startIndex" type="int">
-</parameter>
-<parameter name="endIndex" type="int">
-</parameter>
-<parameter name="value" type="double">
-</parameter>
-</method>
-<method name="binarySearch"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="array" type="float[]">
 </parameter>
 <parameter name="value" type="float">
@@ -310373,25 +292531,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="array" type="float[]">
-</parameter>
-<parameter name="startIndex" type="int">
-</parameter>
-<parameter name="endIndex" type="int">
-</parameter>
-<parameter name="value" type="float">
-</parameter>
-</method>
-<method name="binarySearch"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="array" type="int[]">
 </parameter>
 <parameter name="value" type="int">
@@ -310407,25 +292546,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="array" type="int[]">
-</parameter>
-<parameter name="startIndex" type="int">
-</parameter>
-<parameter name="endIndex" type="int">
-</parameter>
-<parameter name="value" type="int">
-</parameter>
-</method>
-<method name="binarySearch"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="array" type="long[]">
 </parameter>
 <parameter name="value" type="long">
@@ -310441,25 +292561,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="array" type="long[]">
-</parameter>
-<parameter name="startIndex" type="int">
-</parameter>
-<parameter name="endIndex" type="int">
-</parameter>
-<parameter name="value" type="long">
-</parameter>
-</method>
-<method name="binarySearch"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="array" type="java.lang.Object[]">
 </parameter>
 <parameter name="value" type="java.lang.Object">
@@ -310475,25 +292576,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="array" type="java.lang.Object[]">
-</parameter>
-<parameter name="startIndex" type="int">
-</parameter>
-<parameter name="endIndex" type="int">
-</parameter>
-<parameter name="value" type="java.lang.Object">
-</parameter>
-</method>
-<method name="binarySearch"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="array" type="T[]">
 </parameter>
 <parameter name="value" type="T">
@@ -310511,375 +292593,11 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="array" type="T[]">
-</parameter>
-<parameter name="startIndex" type="int">
-</parameter>
-<parameter name="endIndex" type="int">
-</parameter>
-<parameter name="value" type="T">
-</parameter>
-<parameter name="comparator" type="java.util.Comparator&lt;? super T&gt;">
-</parameter>
-</method>
-<method name="binarySearch"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="array" type="short[]">
 </parameter>
 <parameter name="value" type="short">
 </parameter>
 </method>
-<method name="binarySearch"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="array" type="short[]">
-</parameter>
-<parameter name="startIndex" type="int">
-</parameter>
-<parameter name="endIndex" type="int">
-</parameter>
-<parameter name="value" type="short">
-</parameter>
-</method>
-<method name="copyOf"
- return="boolean[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="boolean[]">
-</parameter>
-<parameter name="newLength" type="int">
-</parameter>
-</method>
-<method name="copyOf"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="byte[]">
-</parameter>
-<parameter name="newLength" type="int">
-</parameter>
-</method>
-<method name="copyOf"
- return="char[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="char[]">
-</parameter>
-<parameter name="newLength" type="int">
-</parameter>
-</method>
-<method name="copyOf"
- return="double[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="double[]">
-</parameter>
-<parameter name="newLength" type="int">
-</parameter>
-</method>
-<method name="copyOf"
- return="float[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="float[]">
-</parameter>
-<parameter name="newLength" type="int">
-</parameter>
-</method>
-<method name="copyOf"
- return="int[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="int[]">
-</parameter>
-<parameter name="newLength" type="int">
-</parameter>
-</method>
-<method name="copyOf"
- return="long[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="long[]">
-</parameter>
-<parameter name="newLength" type="int">
-</parameter>
-</method>
-<method name="copyOf"
- return="short[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="short[]">
-</parameter>
-<parameter name="newLength" type="int">
-</parameter>
-</method>
-<method name="copyOf"
- return="T[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="T[]">
-</parameter>
-<parameter name="newLength" type="int">
-</parameter>
-</method>
-<method name="copyOf"
- return="T[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="U[]">
-</parameter>
-<parameter name="newLength" type="int">
-</parameter>
-<parameter name="newType" type="java.lang.Class&lt;? extends T[]&gt;">
-</parameter>
-</method>
-<method name="copyOfRange"
- return="boolean[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="boolean[]">
-</parameter>
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
-<method name="copyOfRange"
- return="byte[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="byte[]">
-</parameter>
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
-<method name="copyOfRange"
- return="char[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="char[]">
-</parameter>
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
-<method name="copyOfRange"
- return="double[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="double[]">
-</parameter>
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
-<method name="copyOfRange"
- return="float[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="float[]">
-</parameter>
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
-<method name="copyOfRange"
- return="int[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="int[]">
-</parameter>
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
-<method name="copyOfRange"
- return="long[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="long[]">
-</parameter>
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
-<method name="copyOfRange"
- return="short[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="short[]">
-</parameter>
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
-<method name="copyOfRange"
- return="T[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="T[]">
-</parameter>
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-</method>
-<method name="copyOfRange"
- return="T[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="original" type="U[]">
-</parameter>
-<parameter name="start" type="int">
-</parameter>
-<parameter name="end" type="int">
-</parameter>
-<parameter name="newType" type="java.lang.Class&lt;? extends T[]&gt;">
-</parameter>
-</method>
 <method name="deepEquals"
  return="boolean"
  abstract="false"
@@ -311959,7 +293677,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="index" type="int">
+<parameter name="pos" type="int">
 </parameter>
 </method>
 <method name="clear"
@@ -311972,9 +293690,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="fromIndex" type="int">
+<parameter name="pos1" type="int">
 </parameter>
-<parameter name="toIndex" type="int">
+<parameter name="pos2" type="int">
 </parameter>
 </method>
 <method name="clone"
@@ -311998,7 +293716,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="index" type="int">
+<parameter name="pos" type="int">
 </parameter>
 </method>
 <method name="flip"
@@ -312011,9 +293729,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="fromIndex" type="int">
+<parameter name="pos1" type="int">
 </parameter>
-<parameter name="toIndex" type="int">
+<parameter name="pos2" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -312026,7 +293744,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="index" type="int">
+<parameter name="pos" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -312039,9 +293757,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="fromIndex" type="int">
+<parameter name="pos1" type="int">
 </parameter>
-<parameter name="toIndex" type="int">
+<parameter name="pos2" type="int">
 </parameter>
 </method>
 <method name="intersects"
@@ -312089,7 +293807,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="index" type="int">
+<parameter name="pos" type="int">
 </parameter>
 </method>
 <method name="nextSetBit"
@@ -312102,7 +293820,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="index" type="int">
+<parameter name="pos" type="int">
 </parameter>
 </method>
 <method name="or"
@@ -312128,7 +293846,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="index" type="int">
+<parameter name="pos" type="int">
 </parameter>
 </method>
 <method name="set"
@@ -312141,7 +293859,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="index" type="int">
+<parameter name="pos" type="int">
 </parameter>
 <parameter name="val" type="boolean">
 </parameter>
@@ -312156,9 +293874,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="fromIndex" type="int">
+<parameter name="pos1" type="int">
 </parameter>
-<parameter name="toIndex" type="int">
+<parameter name="pos2" type="int">
 </parameter>
 </method>
 <method name="set"
@@ -312171,9 +293889,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="fromIndex" type="int">
+<parameter name="pos1" type="int">
 </parameter>
-<parameter name="toIndex" type="int">
+<parameter name="pos2" type="int">
 </parameter>
 <parameter name="val" type="boolean">
 </parameter>
@@ -312409,40 +294127,6 @@
  visibility="public"
 >
 </method>
-<method name="getDisplayName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="field" type="int">
-</parameter>
-<parameter name="style" type="int">
-</parameter>
-<parameter name="locale" type="java.util.Locale">
-</parameter>
-</method>
-<method name="getDisplayNames"
- return="java.util.Map&lt;java.lang.String, java.lang.Integer&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="field" type="int">
-</parameter>
-<parameter name="style" type="int">
-</parameter>
-<parameter name="locale" type="java.util.Locale">
-</parameter>
-</method>
 <method name="getFirstDayOfWeek"
  return="int"
  abstract="false"
@@ -312823,17 +294507,6 @@
 <parameter name="timezone" type="java.util.TimeZone">
 </parameter>
 </method>
-<field name="ALL_STYLES"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="AM"
  type="int"
  transient="false"
@@ -313054,17 +294727,6 @@
  visibility="public"
 >
 </field>
-<field name="LONG"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="MARCH"
  type="int"
  transient="false"
@@ -313197,17 +294859,6 @@
  visibility="public"
 >
 </field>
-<field name="SHORT"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="SUNDAY"
  type="int"
  transient="false"
@@ -313576,19 +295227,6 @@
 <parameter name="a" type="T...">
 </parameter>
 </method>
-<method name="asLifoQueue"
- return="java.util.Queue&lt;T&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="deque" type="java.util.Deque&lt;T&gt;">
-</parameter>
-</method>
 <method name="binarySearch"
  return="int"
  abstract="false"
@@ -313935,19 +295573,6 @@
 <parameter name="object" type="T">
 </parameter>
 </method>
-<method name="newSetFromMap"
- return="java.util.Set&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="map" type="java.util.Map&lt;E, java.lang.Boolean&gt;">
-</parameter>
-</method>
 <method name="replaceAll"
  return="boolean"
  abstract="false"
@@ -314857,217 +296482,6 @@
 >
 </method>
 </class>
-<interface name="Deque"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.util.Queue">
-</implements>
-<method name="addFirst"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="addLast"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="descendingIterator"
- return="java.util.Iterator&lt;E&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getFirst"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getLast"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="offerFirst"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="offerLast"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="peekFirst"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="peekLast"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollFirst"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollLast"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pop"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="push"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="removeFirst"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="removeFirstOccurrence"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="o" type="java.lang.Object">
-</parameter>
-</method>
-<method name="removeLast"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="removeLastOccurrence"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="o" type="java.lang.Object">
-</parameter>
-</method>
-</interface>
 <class name="Dictionary"
  extends="java.lang.Object"
  abstract="true"
@@ -316905,7 +298319,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="flags" type="java.lang.String">
+<parameter name="f" type="java.lang.String">
 </parameter>
 </constructor>
 <method name="getFlags"
@@ -317220,8 +298634,6 @@
 >
 <implements name="java.lang.Cloneable">
 </implements>
-<implements name="java.util.Deque">
-</implements>
 <implements name="java.util.List">
 </implements>
 <implements name="java.util.Queue">
@@ -317283,17 +298695,6 @@
  visibility="public"
 >
 </method>
-<method name="descendingIterator"
- return="java.util.Iterator&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="element"
  return="E"
  abstract="false"
@@ -317353,32 +298754,6 @@
 <parameter name="o" type="E">
 </parameter>
 </method>
-<method name="offerFirst"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="offerLast"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
 <method name="peek"
  return="E"
  abstract="false"
@@ -317390,28 +298765,6 @@
  visibility="public"
 >
 </method>
-<method name="peekFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="peekLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="poll"
  return="E"
  abstract="false"
@@ -317423,52 +298776,6 @@
  visibility="public"
 >
 </method>
-<method name="pollFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pop"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="push"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
 <method name="remove"
  return="E"
  abstract="false"
@@ -317491,19 +298798,6 @@
  visibility="public"
 >
 </method>
-<method name="removeFirstOccurrence"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="o" type="java.lang.Object">
-</parameter>
-</method>
 <method name="removeLast"
  return="E"
  abstract="false"
@@ -317515,19 +298809,6 @@
  visibility="public"
 >
 </method>
-<method name="removeLastOccurrence"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="o" type="java.lang.Object">
-</parameter>
-</method>
 <method name="size"
  return="int"
  abstract="false"
@@ -318476,16 +299757,6 @@
  visibility="public"
 >
 </field>
-<field name="ROOT"
- type="java.util.Locale"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="SIMPLIFIED_CHINESE"
  type="java.util.Locale"
  transient="false"
@@ -318887,401 +300158,6 @@
 >
 </method>
 </class>
-<interface name="NavigableMap"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.util.SortedMap">
-</implements>
-<method name="ceilingEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="ceilingKey"
- return="K"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="descendingKeySet"
- return="java.util.NavigableSet&lt;K&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="descendingMap"
- return="java.util.NavigableMap&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="firstEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="floorEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="floorKey"
- return="K"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="headMap"
- return="java.util.NavigableMap&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="endKey" type="K">
-</parameter>
-<parameter name="inclusive" type="boolean">
-</parameter>
-</method>
-<method name="higherEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="higherKey"
- return="K"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="lastEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="lowerEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="lowerKey"
- return="K"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="navigableKeySet"
- return="java.util.NavigableSet&lt;K&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollFirstEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollLastEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="subMap"
- return="java.util.NavigableMap&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startKey" type="K">
-</parameter>
-<parameter name="startInclusive" type="boolean">
-</parameter>
-<parameter name="endKey" type="K">
-</parameter>
-<parameter name="endInclusive" type="boolean">
-</parameter>
-</method>
-<method name="tailMap"
- return="java.util.NavigableMap&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startKey" type="K">
-</parameter>
-<parameter name="inclusive" type="boolean">
-</parameter>
-</method>
-</interface>
-<interface name="NavigableSet"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.util.SortedSet">
-</implements>
-<method name="ceiling"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="descendingIterator"
- return="java.util.Iterator&lt;E&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="descendingSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="floor"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="headSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="end" type="E">
-</parameter>
-<parameter name="endInclusive" type="boolean">
-</parameter>
-</method>
-<method name="higher"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="lower"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="pollFirst"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollLast"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="subSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="E">
-</parameter>
-<parameter name="startInclusive" type="boolean">
-</parameter>
-<parameter name="end" type="E">
-</parameter>
-<parameter name="endInclusive" type="boolean">
-</parameter>
-</method>
-<method name="tailSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="E">
-</parameter>
-<parameter name="startInclusive" type="boolean">
-</parameter>
-</method>
-</interface>
 <class name="NoSuchElementException"
  extends="java.lang.RuntimeException"
  abstract="false"
@@ -319688,21 +300564,6 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
-<method name="load"
- return="void"
- abstract="false"
- native="false"
- synchronized="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="in" type="java.io.Reader">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
 <method name="loadFromXML"
  return="void"
  abstract="false"
@@ -319778,23 +300639,6 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
-<method name="store"
- return="void"
- abstract="false"
- native="false"
- synchronized="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="writer" type="java.io.Writer">
-</parameter>
-<parameter name="comment" type="java.lang.String">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
 <method name="storeToXML"
  return="void"
  abstract="false"
@@ -319831,17 +300675,6 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </method>
-<method name="stringPropertyNames"
- return="java.util.Set&lt;java.lang.String&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <field name="defaults"
  type="java.util.Properties"
  transient="false"
@@ -319894,18 +300727,6 @@
 <exception name="IOException" type="java.io.IOException">
 </exception>
 </constructor>
-<constructor name="PropertyResourceBundle"
- type="java.util.PropertyResourceBundle"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="reader" type="java.io.Reader">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</constructor>
 <method name="getKeys"
  return="java.util.Enumeration&lt;java.lang.String&gt;"
  abstract="false"
@@ -320169,43 +300990,6 @@
  visibility="public"
 >
 </constructor>
-<method name="clearCache"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="clearCache"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="loader" type="java.lang.ClassLoader">
-</parameter>
-</method>
-<method name="containsKey"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="java.lang.String">
-</parameter>
-</method>
 <method name="getBundle"
  return="java.util.ResourceBundle"
  abstract="false"
@@ -320255,57 +301039,6 @@
 <exception name="MissingResourceException" type="java.util.MissingResourceException">
 </exception>
 </method>
-<method name="getBundle"
- return="java.util.ResourceBundle"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="baseName" type="java.lang.String">
-</parameter>
-<parameter name="control" type="java.util.ResourceBundle.Control">
-</parameter>
-</method>
-<method name="getBundle"
- return="java.util.ResourceBundle"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="baseName" type="java.lang.String">
-</parameter>
-<parameter name="targetLocale" type="java.util.Locale">
-</parameter>
-<parameter name="control" type="java.util.ResourceBundle.Control">
-</parameter>
-</method>
-<method name="getBundle"
- return="java.util.ResourceBundle"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="baseName" type="java.lang.String">
-</parameter>
-<parameter name="targetLocale" type="java.util.Locale">
-</parameter>
-<parameter name="loader" type="java.lang.ClassLoader">
-</parameter>
-<parameter name="control" type="java.util.ResourceBundle.Control">
-</parameter>
-</method>
 <method name="getKeys"
  return="java.util.Enumeration&lt;java.lang.String&gt;"
  abstract="true"
@@ -320380,28 +301113,6 @@
 <parameter name="key" type="java.lang.String">
 </parameter>
 </method>
-<method name="handleKeySet"
- return="java.util.Set&lt;java.lang.String&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
-<method name="keySet"
- return="java.util.Set&lt;java.lang.String&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="setParent"
  return="void"
  abstract="false"
@@ -320426,239 +301137,6 @@
 >
 </field>
 </class>
-<class name="ResourceBundle.Control"
- extends="java.lang.Object"
- abstract="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="ResourceBundle.Control"
- type="java.util.ResourceBundle.Control"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</constructor>
-<method name="getCandidateLocales"
- return="java.util.List&lt;java.util.Locale&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="baseName" type="java.lang.String">
-</parameter>
-<parameter name="locale" type="java.util.Locale">
-</parameter>
-</method>
-<method name="getControl"
- return="java.util.ResourceBundle.Control"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="formats" type="java.util.List&lt;java.lang.String&gt;">
-</parameter>
-</method>
-<method name="getFallbackLocale"
- return="java.util.Locale"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="baseName" type="java.lang.String">
-</parameter>
-<parameter name="locale" type="java.util.Locale">
-</parameter>
-</method>
-<method name="getFormats"
- return="java.util.List&lt;java.lang.String&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="baseName" type="java.lang.String">
-</parameter>
-</method>
-<method name="getNoFallbackControl"
- return="java.util.ResourceBundle.Control"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="formats" type="java.util.List&lt;java.lang.String&gt;">
-</parameter>
-</method>
-<method name="getTimeToLive"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="baseName" type="java.lang.String">
-</parameter>
-<parameter name="locale" type="java.util.Locale">
-</parameter>
-</method>
-<method name="needsReload"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="baseName" type="java.lang.String">
-</parameter>
-<parameter name="locale" type="java.util.Locale">
-</parameter>
-<parameter name="format" type="java.lang.String">
-</parameter>
-<parameter name="loader" type="java.lang.ClassLoader">
-</parameter>
-<parameter name="bundle" type="java.util.ResourceBundle">
-</parameter>
-<parameter name="loadTime" type="long">
-</parameter>
-</method>
-<method name="newBundle"
- return="java.util.ResourceBundle"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="baseName" type="java.lang.String">
-</parameter>
-<parameter name="locale" type="java.util.Locale">
-</parameter>
-<parameter name="format" type="java.lang.String">
-</parameter>
-<parameter name="loader" type="java.lang.ClassLoader">
-</parameter>
-<parameter name="reload" type="boolean">
-</parameter>
-<exception name="IOException" type="java.io.IOException">
-</exception>
-<exception name="IllegalAccessException" type="java.lang.IllegalAccessException">
-</exception>
-<exception name="InstantiationException" type="java.lang.InstantiationException">
-</exception>
-</method>
-<method name="toBundleName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="baseName" type="java.lang.String">
-</parameter>
-<parameter name="locale" type="java.util.Locale">
-</parameter>
-</method>
-<method name="toResourceName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="bundleName" type="java.lang.String">
-</parameter>
-<parameter name="suffix" type="java.lang.String">
-</parameter>
-</method>
-<field name="FORMAT_CLASS"
- type="java.util.List"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="FORMAT_DEFAULT"
- type="java.util.List"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="FORMAT_PROPERTIES"
- type="java.util.List"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TTL_DONT_CACHE"
- type="long"
- transient="false"
- volatile="false"
- value="-1L"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TTL_NO_EXPIRATION_CONTROL"
- type="long"
- transient="false"
- volatile="false"
- value="-2L"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-</class>
 <class name="Scanner"
  extends="java.lang.Object"
  abstract="false"
@@ -321316,17 +301794,6 @@
  visibility="public"
 >
 </method>
-<method name="reset"
- return="java.util.Scanner"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="skip"
  return="java.util.Scanner"
  abstract="false"
@@ -321406,111 +301873,6 @@
 </parameter>
 </method>
 </class>
-<class name="ServiceConfigurationError"
- extends="java.lang.Error"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="ServiceConfigurationError"
- type="java.util.ServiceConfigurationError"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="message" type="java.lang.String">
-</parameter>
-</constructor>
-<constructor name="ServiceConfigurationError"
- type="java.util.ServiceConfigurationError"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="message" type="java.lang.String">
-</parameter>
-<parameter name="cause" type="java.lang.Throwable">
-</parameter>
-</constructor>
-</class>
-<class name="ServiceLoader"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.lang.Iterable">
-</implements>
-<method name="iterator"
- return="java.util.Iterator&lt;S&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="load"
- return="java.util.ServiceLoader&lt;S&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="service" type="java.lang.Class&lt;S&gt;">
-</parameter>
-<parameter name="classLoader" type="java.lang.ClassLoader">
-</parameter>
-</method>
-<method name="load"
- return="java.util.ServiceLoader&lt;S&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="service" type="java.lang.Class&lt;S&gt;">
-</parameter>
-</method>
-<method name="loadInstalled"
- return="java.util.ServiceLoader&lt;S&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="service" type="java.lang.Class&lt;S&gt;">
-</parameter>
-</method>
-<method name="reload"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
 <interface name="Set"
  abstract="true"
  static="false"
@@ -322472,7 +302834,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="offsetMillis" type="int">
+<parameter name="offset" type="int">
 </parameter>
 </method>
 <method name="getDSTSavings"
@@ -322597,7 +302959,7 @@
 </parameter>
 <parameter name="dayOfWeek" type="int">
 </parameter>
-<parameter name="timeOfDayMillis" type="int">
+<parameter name="time" type="int">
 </parameter>
 </method>
 <method name="getRawOffset"
@@ -322621,7 +302983,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="id" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
 </parameter>
 </method>
 <method name="hasSameRules"
@@ -322634,7 +302996,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="timeZone" type="java.util.TimeZone">
+<parameter name="zone" type="java.util.TimeZone">
 </parameter>
 </method>
 <method name="inDaylightTime"
@@ -322660,7 +303022,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="timeZone" type="java.util.TimeZone">
+<parameter name="timezone" type="java.util.TimeZone">
 </parameter>
 </method>
 <method name="setID"
@@ -322673,7 +303035,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="id" type="java.lang.String">
+<parameter name="name" type="java.lang.String">
 </parameter>
 </method>
 <method name="setRawOffset"
@@ -322686,7 +303048,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="offsetMillis" type="int">
+<parameter name="offset" type="int">
 </parameter>
 </method>
 <method name="useDaylightTime"
@@ -322981,8 +303343,6 @@
 >
 <implements name="java.lang.Cloneable">
 </implements>
-<implements name="java.util.NavigableMap">
-</implements>
 <implements name="java.io.Serializable">
 </implements>
 <implements name="java.util.SortedMap">
@@ -323002,16 +303362,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="copyFrom" type="java.util.Map&lt;? extends K, ? extends V&gt;">
-</parameter>
-</constructor>
-<constructor name="TreeMap"
- type="java.util.TreeMap"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
 <parameter name="comparator" type="java.util.Comparator&lt;? super K&gt;">
 </parameter>
 </constructor>
@@ -323022,35 +303372,19 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="copyFrom" type="java.util.SortedMap&lt;K, ? extends V&gt;">
+<parameter name="map" type="java.util.Map&lt;? extends K, ? extends V&gt;">
 </parameter>
 </constructor>
-<method name="ceilingEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
+<constructor name="TreeMap"
+ type="java.util.TreeMap"
  static="false"
  final="false"
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="key" type="K">
+<parameter name="map" type="java.util.SortedMap&lt;K, ? extends V&gt;">
 </parameter>
-</method>
-<method name="ceilingKey"
- return="K"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
+</constructor>
 <method name="clone"
  return="java.lang.Object"
  abstract="false"
@@ -323073,28 +303407,6 @@
  visibility="public"
 >
 </method>
-<method name="descendingKeySet"
- return="java.util.NavigableSet&lt;K&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="descendingMap"
- return="java.util.NavigableMap&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="entrySet"
  return="java.util.Set&lt;java.util.Map.Entry&lt;K, V&gt;&gt;"
  abstract="false"
@@ -323106,17 +303418,6 @@
  visibility="public"
 >
 </method>
-<method name="firstEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="firstKey"
  return="K"
  abstract="false"
@@ -323128,47 +303429,6 @@
  visibility="public"
 >
 </method>
-<method name="floorEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="floorKey"
- return="K"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="headMap"
- return="java.util.NavigableMap&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="to" type="K">
-</parameter>
-<parameter name="inclusive" type="boolean">
-</parameter>
-</method>
 <method name="headMap"
  return="java.util.SortedMap&lt;K, V&gt;"
  abstract="false"
@@ -323179,46 +303439,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="toExclusive" type="K">
+<parameter name="endKey" type="K">
 </parameter>
 </method>
-<method name="higherEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="higherKey"
- return="K"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="lastEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="lastKey"
  return="K"
  abstract="false"
@@ -323230,84 +303453,6 @@
  visibility="public"
 >
 </method>
-<method name="lowerEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="lowerKey"
- return="K"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="navigableKeySet"
- return="java.util.NavigableSet&lt;K&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollFirstEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollLastEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="subMap"
- return="java.util.NavigableMap&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="from" type="K">
-</parameter>
-<parameter name="fromInclusive" type="boolean">
-</parameter>
-<parameter name="to" type="K">
-</parameter>
-<parameter name="toInclusive" type="boolean">
-</parameter>
-</method>
 <method name="subMap"
  return="java.util.SortedMap&lt;K, V&gt;"
  abstract="false"
@@ -323318,24 +303463,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="fromInclusive" type="K">
+<parameter name="startKey" type="K">
 </parameter>
-<parameter name="toExclusive" type="K">
-</parameter>
-</method>
-<method name="tailMap"
- return="java.util.NavigableMap&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="from" type="K">
-</parameter>
-<parameter name="inclusive" type="boolean">
+<parameter name="endKey" type="K">
 </parameter>
 </method>
 <method name="tailMap"
@@ -323348,7 +303478,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="fromInclusive" type="K">
+<parameter name="startKey" type="K">
 </parameter>
 </method>
 </class>
@@ -323362,8 +303492,6 @@
 >
 <implements name="java.lang.Cloneable">
 </implements>
-<implements name="java.util.NavigableSet">
-</implements>
 <implements name="java.io.Serializable">
 </implements>
 <constructor name="TreeSet"
@@ -323404,19 +303532,6 @@
 <parameter name="set" type="java.util.SortedSet&lt;E&gt;">
 </parameter>
 </constructor>
-<method name="ceiling"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
 <method name="clone"
  return="java.lang.Object"
  abstract="false"
@@ -323439,28 +303554,6 @@
  visibility="public"
 >
 </method>
-<method name="descendingIterator"
- return="java.util.Iterator&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="descendingSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="first"
  return="E"
  abstract="false"
@@ -323472,34 +303565,6 @@
  visibility="public"
 >
 </method>
-<method name="floor"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="headSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="end" type="E">
-</parameter>
-<parameter name="endInclusive" type="boolean">
-</parameter>
-</method>
 <method name="headSet"
  return="java.util.SortedSet&lt;E&gt;"
  abstract="false"
@@ -323513,19 +303578,6 @@
 <parameter name="end" type="E">
 </parameter>
 </method>
-<method name="higher"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
 <method name="iterator"
  return="java.util.Iterator&lt;E&gt;"
  abstract="false"
@@ -323548,41 +303600,6 @@
  visibility="public"
 >
 </method>
-<method name="lower"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="pollFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="size"
  return="int"
  abstract="false"
@@ -323595,25 +303612,6 @@
 >
 </method>
 <method name="subSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="E">
-</parameter>
-<parameter name="startInclusive" type="boolean">
-</parameter>
-<parameter name="end" type="E">
-</parameter>
-<parameter name="endInclusive" type="boolean">
-</parameter>
-</method>
-<method name="subSet"
  return="java.util.SortedSet&lt;E&gt;"
  abstract="false"
  native="false"
@@ -323629,21 +303627,6 @@
 </parameter>
 </method>
 <method name="tailSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="start" type="E">
-</parameter>
-<parameter name="startInclusive" type="boolean">
-</parameter>
-</method>
-<method name="tailSet"
  return="java.util.SortedSet&lt;E&gt;"
  abstract="false"
  native="false"
@@ -324366,34 +304349,6 @@
 <exception name="TimeoutException" type="java.util.concurrent.TimeoutException">
 </exception>
 </method>
-<method name="newTaskFor"
- return="java.util.concurrent.RunnableFuture&lt;T&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="runnable" type="java.lang.Runnable">
-</parameter>
-<parameter name="value" type="T">
-</parameter>
-</method>
-<method name="newTaskFor"
- return="java.util.concurrent.RunnableFuture&lt;T&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="callable" type="java.util.concurrent.Callable&lt;T&gt;">
-</parameter>
-</method>
 <method name="submit"
  return="java.util.concurrent.Future&lt;?&gt;"
  abstract="false"
@@ -324645,419 +304600,6 @@
 </exception>
 </method>
 </class>
-<interface name="BlockingDeque"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.util.concurrent.BlockingQueue">
-</implements>
-<implements name="java.util.Deque">
-</implements>
-<method name="add"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="addFirst"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="addLast"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="contains"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="o" type="java.lang.Object">
-</parameter>
-</method>
-<method name="element"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="iterator"
- return="java.util.Iterator&lt;E&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="offer"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="offer"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-<parameter name="timeout" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="offerFirst"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="offerFirst"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-<parameter name="timeout" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="offerLast"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="offerLast"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-<parameter name="timeout" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="peek"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="poll"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="poll"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="timeout" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="pollFirst"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="timeout" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="pollLast"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="timeout" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="push"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="put"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="putFirst"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="putLast"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="remove"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="remove"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="o" type="java.lang.Object">
-</parameter>
-</method>
-<method name="removeFirstOccurrence"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="o" type="java.lang.Object">
-</parameter>
-</method>
-<method name="removeLastOccurrence"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="o" type="java.lang.Object">
-</parameter>
-</method>
-<method name="size"
- return="int"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="take"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="takeFirst"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="takeLast"
- return="E"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-</interface>
 <interface name="BlockingQueue"
  abstract="true"
  static="false"
@@ -325410,18 +304952,6 @@
 >
 <parameter name="initialCapacity" type="int">
 </parameter>
-<parameter name="loadFactor" type="float">
-</parameter>
-</constructor>
-<constructor name="ConcurrentHashMap"
- type="java.util.concurrent.ConcurrentHashMap"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="initialCapacity" type="int">
-</parameter>
 </constructor>
 <constructor name="ConcurrentHashMap"
  type="java.util.concurrent.ConcurrentHashMap"
@@ -325710,898 +305240,6 @@
 </parameter>
 </method>
 </interface>
-<interface name="ConcurrentNavigableMap"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.util.concurrent.ConcurrentMap">
-</implements>
-<implements name="java.util.NavigableMap">
-</implements>
-<method name="descendingKeySet"
- return="java.util.NavigableSet&lt;K&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="descendingMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="headMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="toKey" type="K">
-</parameter>
-<parameter name="inclusive" type="boolean">
-</parameter>
-</method>
-<method name="headMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="toKey" type="K">
-</parameter>
-</method>
-<method name="keySet"
- return="java.util.NavigableSet&lt;K&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="navigableKeySet"
- return="java.util.NavigableSet&lt;K&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="subMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fromKey" type="K">
-</parameter>
-<parameter name="fromInclusive" type="boolean">
-</parameter>
-<parameter name="toKey" type="K">
-</parameter>
-<parameter name="toInclusive" type="boolean">
-</parameter>
-</method>
-<method name="subMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fromKey" type="K">
-</parameter>
-<parameter name="toKey" type="K">
-</parameter>
-</method>
-<method name="tailMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fromKey" type="K">
-</parameter>
-<parameter name="inclusive" type="boolean">
-</parameter>
-</method>
-<method name="tailMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fromKey" type="K">
-</parameter>
-</method>
-</interface>
-<class name="ConcurrentSkipListMap"
- extends="java.util.AbstractMap"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.lang.Cloneable">
-</implements>
-<implements name="java.util.concurrent.ConcurrentNavigableMap">
-</implements>
-<implements name="java.io.Serializable">
-</implements>
-<constructor name="ConcurrentSkipListMap"
- type="java.util.concurrent.ConcurrentSkipListMap"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="ConcurrentSkipListMap"
- type="java.util.concurrent.ConcurrentSkipListMap"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="comparator" type="java.util.Comparator&lt;? super K&gt;">
-</parameter>
-</constructor>
-<constructor name="ConcurrentSkipListMap"
- type="java.util.concurrent.ConcurrentSkipListMap"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="m" type="java.util.Map&lt;? extends K, ? extends V&gt;">
-</parameter>
-</constructor>
-<constructor name="ConcurrentSkipListMap"
- type="java.util.concurrent.ConcurrentSkipListMap"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="m" type="java.util.SortedMap&lt;K, ? extends V&gt;">
-</parameter>
-</constructor>
-<method name="ceilingEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="ceilingKey"
- return="K"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="clone"
- return="java.util.concurrent.ConcurrentSkipListMap&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="comparator"
- return="java.util.Comparator&lt;? super K&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="descendingKeySet"
- return="java.util.NavigableSet&lt;K&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="descendingMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="entrySet"
- return="java.util.Set&lt;java.util.Map.Entry&lt;K, V&gt;&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="firstEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="firstKey"
- return="K"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="floorEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="floorKey"
- return="K"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="headMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="toKey" type="K">
-</parameter>
-<parameter name="inclusive" type="boolean">
-</parameter>
-</method>
-<method name="headMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="toKey" type="K">
-</parameter>
-</method>
-<method name="higherEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="higherKey"
- return="K"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="lastEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="lastKey"
- return="K"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="lowerEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="lowerKey"
- return="K"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-</method>
-<method name="navigableKeySet"
- return="java.util.NavigableSet&lt;K&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollFirstEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollLastEntry"
- return="java.util.Map.Entry&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="putIfAbsent"
- return="V"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-<parameter name="value" type="V">
-</parameter>
-</method>
-<method name="remove"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="java.lang.Object">
-</parameter>
-<parameter name="value" type="java.lang.Object">
-</parameter>
-</method>
-<method name="replace"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-<parameter name="oldValue" type="V">
-</parameter>
-<parameter name="newValue" type="V">
-</parameter>
-</method>
-<method name="replace"
- return="V"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="key" type="K">
-</parameter>
-<parameter name="value" type="V">
-</parameter>
-</method>
-<method name="subMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fromKey" type="K">
-</parameter>
-<parameter name="fromInclusive" type="boolean">
-</parameter>
-<parameter name="toKey" type="K">
-</parameter>
-<parameter name="toInclusive" type="boolean">
-</parameter>
-</method>
-<method name="subMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fromKey" type="K">
-</parameter>
-<parameter name="toKey" type="K">
-</parameter>
-</method>
-<method name="tailMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fromKey" type="K">
-</parameter>
-<parameter name="inclusive" type="boolean">
-</parameter>
-</method>
-<method name="tailMap"
- return="java.util.concurrent.ConcurrentNavigableMap&lt;K, V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fromKey" type="K">
-</parameter>
-</method>
-</class>
-<class name="ConcurrentSkipListSet"
- extends="java.util.AbstractSet"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.lang.Cloneable">
-</implements>
-<implements name="java.util.NavigableSet">
-</implements>
-<implements name="java.io.Serializable">
-</implements>
-<constructor name="ConcurrentSkipListSet"
- type="java.util.concurrent.ConcurrentSkipListSet"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="ConcurrentSkipListSet"
- type="java.util.concurrent.ConcurrentSkipListSet"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="comparator" type="java.util.Comparator&lt;? super E&gt;">
-</parameter>
-</constructor>
-<constructor name="ConcurrentSkipListSet"
- type="java.util.concurrent.ConcurrentSkipListSet"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="c" type="java.util.Collection&lt;? extends E&gt;">
-</parameter>
-</constructor>
-<constructor name="ConcurrentSkipListSet"
- type="java.util.concurrent.ConcurrentSkipListSet"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="s" type="java.util.SortedSet&lt;E&gt;">
-</parameter>
-</constructor>
-<method name="ceiling"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="clone"
- return="java.util.concurrent.ConcurrentSkipListSet&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="comparator"
- return="java.util.Comparator&lt;? super E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="descendingIterator"
- return="java.util.Iterator&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="descendingSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="first"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="floor"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="headSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="toElement" type="E">
-</parameter>
-<parameter name="inclusive" type="boolean">
-</parameter>
-</method>
-<method name="headSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="toElement" type="E">
-</parameter>
-</method>
-<method name="higher"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="iterator"
- return="java.util.Iterator&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="last"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="lower"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="pollFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="size"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="subSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fromElement" type="E">
-</parameter>
-<parameter name="fromInclusive" type="boolean">
-</parameter>
-<parameter name="toElement" type="E">
-</parameter>
-<parameter name="toInclusive" type="boolean">
-</parameter>
-</method>
-<method name="subSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fromElement" type="E">
-</parameter>
-<parameter name="toElement" type="E">
-</parameter>
-</method>
-<method name="tailSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fromElement" type="E">
-</parameter>
-<parameter name="inclusive" type="boolean">
-</parameter>
-</method>
-<method name="tailSet"
- return="java.util.NavigableSet&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="fromElement" type="E">
-</parameter>
-</method>
-</class>
 <class name="CopyOnWriteArrayList"
  extends="java.lang.Object"
  abstract="false"
@@ -328213,8 +306851,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<implements name="java.util.concurrent.RunnableFuture">
-</implements>
 <constructor name="FutureTask"
  type="java.util.concurrent.FutureTask"
  static="false"
@@ -328368,536 +307004,6 @@
 </parameter>
 </method>
 </class>
-<class name="LinkedBlockingDeque"
- extends="java.util.AbstractQueue"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.util.concurrent.BlockingDeque">
-</implements>
-<implements name="java.io.Serializable">
-</implements>
-<constructor name="LinkedBlockingDeque"
- type="java.util.concurrent.LinkedBlockingDeque"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="LinkedBlockingDeque"
- type="java.util.concurrent.LinkedBlockingDeque"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="capacity" type="int">
-</parameter>
-</constructor>
-<constructor name="LinkedBlockingDeque"
- type="java.util.concurrent.LinkedBlockingDeque"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="c" type="java.util.Collection&lt;? extends E&gt;">
-</parameter>
-</constructor>
-<method name="addFirst"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="addLast"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="descendingIterator"
- return="java.util.Iterator&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="drainTo"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="c" type="java.util.Collection&lt;? super E&gt;">
-</parameter>
-</method>
-<method name="drainTo"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="c" type="java.util.Collection&lt;? super E&gt;">
-</parameter>
-<parameter name="maxElements" type="int">
-</parameter>
-</method>
-<method name="getFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="iterator"
- return="java.util.Iterator&lt;E&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="offer"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="offer"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-<parameter name="timeout" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="offerFirst"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="offerFirst"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-<parameter name="timeout" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="offerLast"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="offerLast"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-<parameter name="timeout" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="peek"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="peekFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="peekLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="poll"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="poll"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="timeout" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="pollFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="timeout" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="pollLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="pollLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="timeout" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="pop"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="push"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-</method>
-<method name="put"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="putFirst"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="putLast"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="e" type="E">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="remainingCapacity"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="removeFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="removeFirstOccurrence"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="o" type="java.lang.Object">
-</parameter>
-</method>
-<method name="removeLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="removeLastOccurrence"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="o" type="java.lang.Object">
-</parameter>
-</method>
-<method name="size"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="take"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="takeFirst"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="takeLast"
- return="E"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-</class>
 <class name="LinkedBlockingQueue"
  extends="java.util.AbstractQueue"
  abstract="false"
@@ -329391,52 +307497,6 @@
 </parameter>
 </method>
 </interface>
-<interface name="RunnableFuture"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.util.concurrent.Future">
-</implements>
-<implements name="java.lang.Runnable">
-</implements>
-<method name="run"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</interface>
-<interface name="RunnableScheduledFuture"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.util.concurrent.RunnableFuture">
-</implements>
-<implements name="java.util.concurrent.ScheduledFuture">
-</implements>
-<method name="isPeriodic"
- return="boolean"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</interface>
 <interface name="ScheduledExecutorService"
  abstract="true"
  static="false"
@@ -329589,36 +307649,6 @@
 <parameter name="handler" type="java.util.concurrent.RejectedExecutionHandler">
 </parameter>
 </constructor>
-<method name="decorateTask"
- return="java.util.concurrent.RunnableScheduledFuture&lt;V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="runnable" type="java.lang.Runnable">
-</parameter>
-<parameter name="task" type="java.util.concurrent.RunnableScheduledFuture&lt;V&gt;">
-</parameter>
-</method>
-<method name="decorateTask"
- return="java.util.concurrent.RunnableScheduledFuture&lt;V&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="callable" type="java.util.concurrent.Callable&lt;V&gt;">
-</parameter>
-<parameter name="task" type="java.util.concurrent.RunnableScheduledFuture&lt;V&gt;">
-</parameter>
-</method>
 <method name="getContinueExistingPeriodicTasksAfterShutdownPolicy"
  return="boolean"
  abstract="false"
@@ -330303,30 +308333,6 @@
 <parameter name="t" type="java.lang.Throwable">
 </parameter>
 </method>
-<method name="allowCoreThreadTimeOut"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="value" type="boolean">
-</parameter>
-</method>
-<method name="allowsCoreThreadTimeOut"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="awaitTermination"
  return="boolean"
  abstract="false"
@@ -330883,32 +308889,6 @@
 <exception name="InterruptedException" type="java.lang.InterruptedException">
 </exception>
 </method>
-<method name="toDays"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="duration" type="long">
-</parameter>
-</method>
-<method name="toHours"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="duration" type="long">
-</parameter>
-</method>
 <method name="toMicros"
  return="long"
  abstract="false"
@@ -330935,19 +308915,6 @@
 <parameter name="duration" type="long">
 </parameter>
 </method>
-<method name="toMinutes"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="duration" type="long">
-</parameter>
-</method>
 <method name="toNanos"
  return="long"
  abstract="false"
@@ -331096,19 +309063,6 @@
 <parameter name="newValue" type="boolean">
 </parameter>
 </method>
-<method name="lazySet"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="newValue" type="boolean">
-</parameter>
-</method>
 <method name="set"
  return="void"
  abstract="false"
@@ -331308,19 +309262,6 @@
  visibility="public"
 >
 </method>
-<method name="lazySet"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="newValue" type="int">
-</parameter>
-</method>
 <method name="longValue"
  return="long"
  abstract="false"
@@ -331518,21 +309459,6 @@
 <parameter name="i" type="int">
 </parameter>
 </method>
-<method name="lazySet"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="i" type="int">
-</parameter>
-<parameter name="newValue" type="int">
-</parameter>
-</method>
 <method name="length"
  return="int"
  abstract="false"
@@ -331720,21 +309646,6 @@
 <parameter name="obj" type="T">
 </parameter>
 </method>
-<method name="lazySet"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="obj" type="T">
-</parameter>
-<parameter name="newValue" type="int">
-</parameter>
-</method>
 <method name="newUpdater"
  return="java.util.concurrent.atomic.AtomicIntegerFieldUpdater&lt;U&gt;"
  abstract="false"
@@ -331953,19 +309864,6 @@
  visibility="public"
 >
 </method>
-<method name="lazySet"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="newValue" type="long">
-</parameter>
-</method>
 <method name="longValue"
  return="long"
  abstract="false"
@@ -332163,21 +310061,6 @@
 <parameter name="i" type="int">
 </parameter>
 </method>
-<method name="lazySet"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="i" type="int">
-</parameter>
-<parameter name="newValue" type="long">
-</parameter>
-</method>
 <method name="length"
  return="int"
  abstract="false"
@@ -332365,21 +310248,6 @@
 <parameter name="obj" type="T">
 </parameter>
 </method>
-<method name="lazySet"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="obj" type="T">
-</parameter>
-<parameter name="newValue" type="long">
-</parameter>
-</method>
 <method name="newUpdater"
  return="java.util.concurrent.atomic.AtomicLongFieldUpdater&lt;U&gt;"
  abstract="false"
@@ -332619,19 +310487,6 @@
 <parameter name="newValue" type="V">
 </parameter>
 </method>
-<method name="lazySet"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="newValue" type="V">
-</parameter>
-</method>
 <method name="set"
  return="void"
  abstract="false"
@@ -332736,21 +310591,6 @@
 <parameter name="newValue" type="E">
 </parameter>
 </method>
-<method name="lazySet"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="i" type="int">
-</parameter>
-<parameter name="newValue" type="E">
-</parameter>
-</method>
 <method name="length"
  return="int"
  abstract="false"
@@ -332856,21 +310696,6 @@
 <parameter name="newValue" type="V">
 </parameter>
 </method>
-<method name="lazySet"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="obj" type="T">
-</parameter>
-<parameter name="newValue" type="V">
-</parameter>
-</method>
 <method name="newUpdater"
  return="java.util.concurrent.atomic.AtomicReferenceFieldUpdater&lt;U, W&gt;"
  abstract="false"
@@ -333091,532 +310916,6 @@
 </parameter>
 </method>
 </class>
-<class name="AbstractQueuedLongSynchronizer"
- extends="java.util.concurrent.locks.AbstractOwnableSynchronizer"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.io.Serializable">
-</implements>
-<constructor name="AbstractQueuedLongSynchronizer"
- type="java.util.concurrent.locks.AbstractQueuedLongSynchronizer"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</constructor>
-<method name="acquire"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="arg" type="long">
-</parameter>
-</method>
-<method name="acquireInterruptibly"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="arg" type="long">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="acquireShared"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="arg" type="long">
-</parameter>
-</method>
-<method name="acquireSharedInterruptibly"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="arg" type="long">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="compareAndSetState"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="expect" type="long">
-</parameter>
-<parameter name="update" type="long">
-</parameter>
-</method>
-<method name="getExclusiveQueuedThreads"
- return="java.util.Collection&lt;java.lang.Thread&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getFirstQueuedThread"
- return="java.lang.Thread"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getQueueLength"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getQueuedThreads"
- return="java.util.Collection&lt;java.lang.Thread&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getSharedQueuedThreads"
- return="java.util.Collection&lt;java.lang.Thread&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getState"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
-<method name="getWaitQueueLength"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="condition" type="java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject">
-</parameter>
-</method>
-<method name="getWaitingThreads"
- return="java.util.Collection&lt;java.lang.Thread&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="condition" type="java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject">
-</parameter>
-</method>
-<method name="hasContended"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="hasQueuedThreads"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="hasWaiters"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="condition" type="java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject">
-</parameter>
-</method>
-<method name="isHeldExclusively"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
-<method name="isQueued"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="thread" type="java.lang.Thread">
-</parameter>
-</method>
-<method name="owns"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="condition" type="java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject">
-</parameter>
-</method>
-<method name="release"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="arg" type="long">
-</parameter>
-</method>
-<method name="releaseShared"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="arg" type="long">
-</parameter>
-</method>
-<method name="setState"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="newState" type="long">
-</parameter>
-</method>
-<method name="tryAcquire"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="arg" type="long">
-</parameter>
-</method>
-<method name="tryAcquireNanos"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="arg" type="long">
-</parameter>
-<parameter name="nanosTimeout" type="long">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="tryAcquireShared"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="arg" type="long">
-</parameter>
-</method>
-<method name="tryAcquireSharedNanos"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="arg" type="long">
-</parameter>
-<parameter name="nanosTimeout" type="long">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="tryRelease"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="arg" type="long">
-</parameter>
-</method>
-<method name="tryReleaseShared"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="arg" type="long">
-</parameter>
-</method>
-</class>
-<class name="AbstractQueuedLongSynchronizer.ConditionObject"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.util.concurrent.locks.Condition">
-</implements>
-<implements name="java.io.Serializable">
-</implements>
-<constructor name="AbstractQueuedLongSynchronizer.ConditionObject"
- type="java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="await"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="await"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="time" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="awaitNanos"
- return="long"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="nanosTimeout" type="long">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="awaitUninterruptibly"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="awaitUntil"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="deadline" type="java.util.Date">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
-</method>
-<method name="getWaitQueueLength"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
-<method name="getWaitingThreads"
- return="java.util.Collection&lt;java.lang.Thread&gt;"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
-<method name="hasWaiters"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
-<method name="signal"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="signalAll"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
 <class name="AbstractQueuedSynchronizer"
  extends="java.util.concurrent.locks.AbstractOwnableSynchronizer"
  abstract="true"
@@ -334334,32 +311633,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<method name="getBlocker"
- return="java.lang.Object"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="t" type="java.lang.Thread">
-</parameter>
-</method>
-<method name="park"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="blocker" type="java.lang.Object">
-</parameter>
-</method>
 <method name="park"
  return="void"
  abstract="false"
@@ -334381,39 +311654,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="blocker" type="java.lang.Object">
-</parameter>
 <parameter name="nanos" type="long">
 </parameter>
 </method>
-<method name="parkNanos"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="nanos" type="long">
-</parameter>
-</method>
-<method name="parkUntil"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="blocker" type="java.lang.Object">
-</parameter>
-<parameter name="deadline" type="long">
-</parameter>
-</method>
 <method name="parkUntil"
  return="void"
  abstract="false"
@@ -334801,17 +312044,6 @@
  visibility="protected"
 >
 </method>
-<method name="getReadHoldCount"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getReadLockCount"
  return="int"
  abstract="false"
@@ -335072,28 +312304,6 @@
 <parameter name="lock" type="java.util.concurrent.locks.ReentrantReadWriteLock">
 </parameter>
 </constructor>
-<method name="getHoldCount"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isHeldByCurrentThread"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="lock"
  return="void"
  abstract="false"
@@ -338370,24 +315580,13 @@
 <parameter name="msg" type="java.lang.String">
 </parameter>
 </method>
-<field name="GLOBAL_LOGGER_NAME"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value="&quot;global&quot;"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="global"
  type="java.util.logging.Logger"
  transient="false"
  volatile="false"
  static="true"
  final="true"
- deprecated="deprecated"
+ deprecated="not deprecated"
  visibility="public"
 >
 </field>
@@ -340643,6 +317842,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="input" type="java.lang.CharSequence">
+</parameter>
 </method>
 <method name="reset"
  return="java.util.regex.Matcher"
@@ -340654,8 +317855,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="input" type="java.lang.CharSequence">
-</parameter>
 </method>
 <method name="start"
  return="int"
@@ -340754,7 +317953,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="regularExpression" type="java.lang.String">
+<parameter name="pattern" type="java.lang.String">
 </parameter>
 <parameter name="flags" type="int">
 </parameter>
@@ -340808,7 +318007,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="regularExpression" type="java.lang.String">
+<parameter name="regex" type="java.lang.String">
 </parameter>
 <parameter name="input" type="java.lang.CharSequence">
 </parameter>
@@ -340834,7 +318033,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="string" type="java.lang.String">
+<parameter name="s" type="java.lang.String">
 </parameter>
 </method>
 <method name="split"
@@ -340847,7 +318046,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="input" type="java.lang.CharSequence">
+<parameter name="inputSeq" type="java.lang.CharSequence">
 </parameter>
 <parameter name="limit" type="int">
 </parameter>
@@ -341685,72 +318884,6 @@
 >
 </field>
 </class>
-<class name="DeflaterInputStream"
- extends="java.io.FilterInputStream"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="DeflaterInputStream"
- type="java.util.zip.DeflaterInputStream"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="in" type="java.io.InputStream">
-</parameter>
-</constructor>
-<constructor name="DeflaterInputStream"
- type="java.util.zip.DeflaterInputStream"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="in" type="java.io.InputStream">
-</parameter>
-<parameter name="deflater" type="java.util.zip.Deflater">
-</parameter>
-</constructor>
-<constructor name="DeflaterInputStream"
- type="java.util.zip.DeflaterInputStream"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="in" type="java.io.InputStream">
-</parameter>
-<parameter name="deflater" type="java.util.zip.Deflater">
-</parameter>
-<parameter name="bufferSize" type="int">
-</parameter>
-</constructor>
-<field name="buf"
- type="byte[]"
- transient="false"
- volatile="false"
- value="null"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
-<field name="def"
- type="java.util.zip.Deflater"
- transient="false"
- volatile="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
-</class>
 <class name="DeflaterOutputStream"
  extends="java.io.FilterOutputStream"
  abstract="false"
@@ -342223,7 +319356,7 @@
 >
 <parameter name="is" type="java.io.InputStream">
 </parameter>
-<parameter name="inflater" type="java.util.zip.Inflater">
+<parameter name="inf" type="java.util.zip.Inflater">
 </parameter>
 </constructor>
 <constructor name="InflaterInputStream"
@@ -342235,7 +319368,7 @@
 >
 <parameter name="is" type="java.io.InputStream">
 </parameter>
-<parameter name="inflater" type="java.util.zip.Inflater">
+<parameter name="inf" type="java.util.zip.Inflater">
 </parameter>
 <parameter name="bsize" type="int">
 </parameter>
@@ -342285,85 +319418,6 @@
 >
 </field>
 </class>
-<class name="InflaterOutputStream"
- extends="java.io.FilterOutputStream"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="InflaterOutputStream"
- type="java.util.zip.InflaterOutputStream"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="out" type="java.io.OutputStream">
-</parameter>
-</constructor>
-<constructor name="InflaterOutputStream"
- type="java.util.zip.InflaterOutputStream"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="out" type="java.io.OutputStream">
-</parameter>
-<parameter name="inf" type="java.util.zip.Inflater">
-</parameter>
-</constructor>
-<constructor name="InflaterOutputStream"
- type="java.util.zip.InflaterOutputStream"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="out" type="java.io.OutputStream">
-</parameter>
-<parameter name="inf" type="java.util.zip.Inflater">
-</parameter>
-<parameter name="bufferSize" type="int">
-</parameter>
-</constructor>
-<method name="finish"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="IOException" type="java.io.IOException">
-</exception>
-</method>
-<field name="buf"
- type="byte[]"
- transient="false"
- volatile="false"
- value="null"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
-<field name="inf"
- type="java.util.zip.Inflater"
- transient="false"
- volatile="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
-</class>
 <class name="ZipEntry"
  extends="java.lang.Object"
  abstract="false"
@@ -342618,25 +319672,6 @@
 >
 </field>
 </class>
-<class name="ZipError"
- extends="java.lang.InternalError"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="ZipError"
- type="java.util.zip.ZipError"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="s" type="java.lang.String">
-</parameter>
-</constructor>
-</class>
 <class name="ZipException"
  extends="java.io.IOException"
  abstract="false"
@@ -358438,6 +335473,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="java.io.Serializable">
+</implements>
 <constructor name="HandshakeCompletedEvent"
  type="javax.net.ssl.HandshakeCompletedEvent"
  static="false"
@@ -359110,30 +336147,6 @@
  visibility="public"
 >
 </method>
-<method name="getDefault"
- return="javax.net.ssl.SSLContext"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="NoSuchAlgorithmException" type="java.security.NoSuchAlgorithmException">
-</exception>
-</method>
-<method name="getDefaultSSLParameters"
- return="javax.net.ssl.SSLParameters"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getInstance"
  return="javax.net.ssl.SSLContext"
  abstract="false"
@@ -359240,17 +336253,6 @@
  visibility="public"
 >
 </method>
-<method name="getSupportedSSLParameters"
- return="javax.net.ssl.SSLParameters"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="init"
  return="void"
  abstract="false"
@@ -359270,19 +336272,6 @@
 <exception name="KeyManagementException" type="java.security.KeyManagementException">
 </exception>
 </method>
-<method name="setDefault"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="sslContext" type="javax.net.ssl.SSLContext">
-</parameter>
-</method>
 </class>
 <class name="SSLContextSpi"
  extends="java.lang.Object"
@@ -359337,17 +336326,6 @@
  visibility="protected"
 >
 </method>
-<method name="engineGetDefaultSSLParameters"
- return="javax.net.ssl.SSLParameters"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
 <method name="engineGetServerSessionContext"
  return="javax.net.ssl.SSLSessionContext"
  abstract="true"
@@ -359381,17 +336359,6 @@
  visibility="protected"
 >
 </method>
-<method name="engineGetSupportedSSLParameters"
- return="javax.net.ssl.SSLParameters"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
 <method name="engineInit"
  return="void"
  abstract="true"
@@ -359565,17 +336532,6 @@
  visibility="public"
 >
 </method>
-<method name="getSSLParameters"
- return="javax.net.ssl.SSLParameters"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getSession"
  return="javax.net.ssl.SSLSession"
  abstract="true"
@@ -359705,19 +336661,6 @@
 <parameter name="need" type="boolean">
 </parameter>
 </method>
-<method name="setSSLParameters"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="p" type="javax.net.ssl.SSLParameters">
-</parameter>
-</method>
 <method name="setUseClientMode"
  return="void"
  abstract="true"
@@ -360069,141 +337012,6 @@
 </parameter>
 </constructor>
 </class>
-<class name="SSLParameters"
- extends="java.lang.Object"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="SSLParameters"
- type="javax.net.ssl.SSLParameters"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<constructor name="SSLParameters"
- type="javax.net.ssl.SSLParameters"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cipherSuites" type="java.lang.String[]">
-</parameter>
-</constructor>
-<constructor name="SSLParameters"
- type="javax.net.ssl.SSLParameters"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cipherSuites" type="java.lang.String[]">
-</parameter>
-<parameter name="protocols" type="java.lang.String[]">
-</parameter>
-</constructor>
-<method name="getCipherSuites"
- return="java.lang.String[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getNeedClientAuth"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getProtocols"
- return="java.lang.String[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getWantClientAuth"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="setCipherSuites"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="cipherSuites" type="java.lang.String[]">
-</parameter>
-</method>
-<method name="setNeedClientAuth"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="needClientAuth" type="boolean">
-</parameter>
-</method>
-<method name="setProtocols"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="protocols" type="java.lang.String[]">
-</parameter>
-</method>
-<method name="setWantClientAuth"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="wantClientAuth" type="boolean">
-</parameter>
-</method>
-</class>
 <class name="SSLPeerUnverifiedException"
  extends="javax.net.ssl.SSLException"
  abstract="false"
@@ -360811,6 +337619,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="java.io.Serializable">
+</implements>
 <constructor name="SSLSessionBindingEvent"
  type="javax.net.ssl.SSLSessionBindingEvent"
  static="false"
@@ -361107,17 +337917,6 @@
  visibility="public"
 >
 </method>
-<method name="getSSLParameters"
- return="javax.net.ssl.SSLParameters"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getSession"
  return="javax.net.ssl.SSLSession"
  abstract="true"
@@ -361238,19 +338037,6 @@
 <parameter name="need" type="boolean">
 </parameter>
 </method>
-<method name="setSSLParameters"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="p" type="javax.net.ssl.SSLParameters">
-</parameter>
-</method>
 <method name="setUseClientMode"
  return="void"
  abstract="true"
@@ -362460,18 +339246,6 @@
 <parameter name="name" type="java.lang.String">
 </parameter>
 </constructor>
-<constructor name="X500Principal"
- type="javax.security.auth.x500.X500Principal"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="name" type="java.lang.String">
-</parameter>
-<parameter name="keywordMap" type="java.util.Map&lt;java.lang.String, java.lang.String&gt;">
-</parameter>
-</constructor>
 <method name="getEncoded"
  return="byte[]"
  abstract="false"
@@ -362507,21 +339281,6 @@
 <parameter name="format" type="java.lang.String">
 </parameter>
 </method>
-<method name="getName"
- return="java.lang.String"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="format" type="java.lang.String">
-</parameter>
-<parameter name="oidMap" type="java.util.Map&lt;java.lang.String, java.lang.String&gt;">
-</parameter>
-</method>
 <field name="CANONICAL"
  type="java.lang.String"
  transient="false"
@@ -362975,70 +339734,6 @@
 </package>
 <package name="javax.sql"
 >
-<interface name="CommonDataSource"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<method name="getLogWriter"
- return="java.io.PrintWriter"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="getLoginTimeout"
- return="int"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setLogWriter"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="out" type="java.io.PrintWriter">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setLoginTimeout"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="seconds" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-</interface>
 <class name="ConnectionEvent"
  extends="java.util.EventObject"
  abstract="false"
@@ -363126,8 +339821,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<implements name="javax.sql.CommonDataSource">
-</implements>
 <method name="getPooledConnection"
  return="javax.sql.PooledConnection"
  abstract="true"
@@ -363166,10 +339859,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<implements name="javax.sql.CommonDataSource">
-</implements>
-<implements name="java.sql.Wrapper">
-</implements>
 <method name="getConnection"
  return="java.sql.Connection"
  abstract="true"
@@ -363221,19 +339910,6 @@
 <parameter name="theListener" type="javax.sql.ConnectionEventListener">
 </parameter>
 </method>
-<method name="addStatementEventListener"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="javax.sql.StatementEventListener">
-</parameter>
-</method>
 <method name="close"
  return="void"
  abstract="true"
@@ -363273,19 +339949,6 @@
 <parameter name="theListener" type="javax.sql.ConnectionEventListener">
 </parameter>
 </method>
-<method name="removeStatementEventListener"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="listener" type="javax.sql.StatementEventListener">
-</parameter>
-</method>
 </interface>
 <interface name="RowSet"
  abstract="true"
@@ -363528,59 +340191,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setAsciiStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theInputStream" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setAsciiStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theInputStream" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setAsciiStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theInputStream" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setBigDecimal"
  return="void"
  abstract="true"
@@ -363598,23 +340208,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setBigDecimal"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theBigDecimal" type="java.math.BigDecimal">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setBinaryStream"
  return="void"
  abstract="true"
@@ -363634,59 +340227,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setBinaryStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theInputStream" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBinaryStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theInputStream" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBinaryStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theInputStream" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setBlob"
  return="void"
  abstract="true"
@@ -363704,95 +340244,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theInputStream" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theInputStream" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theInputStream" type="java.io.InputStream">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theInputStream" type="java.io.InputStream">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setBlob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theBlob" type="java.sql.Blob">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setBoolean"
  return="void"
  abstract="true"
@@ -363810,23 +340261,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setBoolean"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theBoolean" type="boolean">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setByte"
  return="void"
  abstract="true"
@@ -363844,23 +340278,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setByte"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theByte" type="byte">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setBytes"
  return="void"
  abstract="true"
@@ -363878,23 +340295,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setBytes"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theByteArray" type="byte[]">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setCharacterStream"
  return="void"
  abstract="true"
@@ -363914,59 +340314,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setClob"
  return="void"
  abstract="true"
@@ -363984,95 +340331,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theClob" type="java.sql.Clob">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setCommand"
  return="void"
  abstract="true"
@@ -364154,42 +340412,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setDate"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theDate" type="java.sql.Date">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setDate"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theDate" type="java.sql.Date">
-</parameter>
-<parameter name="theCalendar" type="java.util.Calendar">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setDouble"
  return="void"
  abstract="true"
@@ -364207,23 +340429,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setDouble"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theDouble" type="double">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setEscapeProcessing"
  return="void"
  abstract="true"
@@ -364256,23 +340461,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setFloat"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theFloat" type="float">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setInt"
  return="void"
  abstract="true"
@@ -364290,23 +340478,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setInt"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theInteger" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setLong"
  return="void"
  abstract="true"
@@ -364324,23 +340495,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setLong"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theLong" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setMaxFieldSize"
  return="void"
  abstract="true"
@@ -364371,218 +340525,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setNCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNCharacterStream"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theNClob" type="java.sql.NClob">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theNClob" type="java.sql.NClob">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNClob"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theReader" type="java.io.Reader">
-</parameter>
-<parameter name="length" type="long">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNString"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theNString" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNString"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theNString" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setNull"
  return="void"
  abstract="true"
@@ -364619,42 +340561,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setNull"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="sqlType" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setNull"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="sqlType" type="int">
-</parameter>
-<parameter name="typeName" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setObject"
  return="void"
  abstract="true"
@@ -364712,63 +340618,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setObject"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theObject" type="java.lang.Object">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setObject"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theObject" type="java.lang.Object">
-</parameter>
-<parameter name="targetSqlType" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setObject"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theObject" type="java.lang.Object">
-</parameter>
-<parameter name="targetSqlType" type="int">
-</parameter>
-<parameter name="scale" type="int">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setPassword"
  return="void"
  abstract="true"
@@ -364831,74 +340680,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setRowId"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theRowId" type="java.sql.RowId">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setRowId"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theRowId" type="java.sql.RowId">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setSQLXML"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theSQLXML" type="java.sql.SQLXML">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setSQLXML"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theSQLXML" type="java.sql.SQLXML">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setShort"
  return="void"
  abstract="true"
@@ -364916,23 +340697,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setShort"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theShort" type="short">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setString"
  return="void"
  abstract="true"
@@ -364950,23 +340714,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setString"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theString" type="java.lang.String">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setTime"
  return="void"
  abstract="true"
@@ -365003,42 +340750,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setTime"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theTime" type="java.sql.Time">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setTime"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theTime" type="java.sql.Time">
-</parameter>
-<parameter name="theCalendar" type="java.util.Calendar">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setTimestamp"
  return="void"
  abstract="true"
@@ -365075,42 +340786,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setTimestamp"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theTimestamp" type="java.sql.Timestamp">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
-<method name="setTimestamp"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterName" type="java.lang.String">
-</parameter>
-<parameter name="theTimestamp" type="java.sql.Timestamp">
-</parameter>
-<parameter name="theCalendar" type="java.util.Calendar">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setTransactionIsolation"
  return="void"
  abstract="true"
@@ -365156,23 +340831,6 @@
 <exception name="SQLException" type="java.sql.SQLException">
 </exception>
 </method>
-<method name="setURL"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="parameterIndex" type="int">
-</parameter>
-<parameter name="theURL" type="java.net.URL">
-</parameter>
-<exception name="SQLException" type="java.sql.SQLException">
-</exception>
-</method>
 <method name="setUrl"
  return="void"
  abstract="true"
@@ -365692,99 +341350,6 @@
 </exception>
 </method>
 </interface>
-<class name="StatementEvent"
- extends="java.util.EventObject"
- abstract="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<constructor name="StatementEvent"
- type="javax.sql.StatementEvent"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="con" type="javax.sql.PooledConnection">
-</parameter>
-<parameter name="statement" type="java.sql.PreparedStatement">
-</parameter>
-<parameter name="exception" type="java.sql.SQLException">
-</parameter>
-</constructor>
-<constructor name="StatementEvent"
- type="javax.sql.StatementEvent"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="con" type="javax.sql.PooledConnection">
-</parameter>
-<parameter name="statement" type="java.sql.PreparedStatement">
-</parameter>
-</constructor>
-<method name="getSQLException"
- return="java.sql.SQLException"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getStatement"
- return="java.sql.PreparedStatement"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-</class>
-<interface name="StatementEventListener"
- abstract="true"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<implements name="java.util.EventListener">
-</implements>
-<method name="statementClosed"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="event" type="javax.sql.StatementEvent">
-</parameter>
-</method>
-<method name="statementErrorOccurred"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="event" type="javax.sql.StatementEvent">
-</parameter>
-</method>
-</interface>
 </package>
 <package name="javax.xml"
 >
@@ -366622,23 +342187,6 @@
 <exception name="DatatypeConfigurationException" type="javax.xml.datatype.DatatypeConfigurationException">
 </exception>
 </method>
-<method name="newInstance"
- return="javax.xml.datatype.DatatypeFactory"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="factoryClassName" type="java.lang.String">
-</parameter>
-<parameter name="classLoader" type="java.lang.ClassLoader">
-</parameter>
-<exception name="DatatypeConfigurationException" type="javax.xml.datatype.DatatypeConfigurationException">
-</exception>
-</method>
 <method name="newXMLGregorianCalendar"
  return="javax.xml.datatype.XMLGregorianCalendar"
  abstract="true"
@@ -368145,21 +343693,6 @@
  visibility="public"
 >
 </method>
-<method name="newInstance"
- return="javax.xml.parsers.DocumentBuilderFactory"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="factoryClassName" type="java.lang.String">
-</parameter>
-<parameter name="classLoader" type="java.lang.ClassLoader">
-</parameter>
-</method>
 <method name="setAttribute"
  return="void"
  abstract="true"
@@ -368804,21 +344337,6 @@
  visibility="public"
 >
 </method>
-<method name="newInstance"
- return="javax.xml.parsers.SAXParserFactory"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="factoryClassName" type="java.lang.String">
-</parameter>
-<parameter name="classLoader" type="java.lang.ClassLoader">
-</parameter>
-</method>
 <method name="newSAXParser"
  return="javax.xml.parsers.SAXParser"
  abstract="true"
@@ -369740,23 +345258,6 @@
 <exception name="TransformerFactoryConfigurationError" type="javax.xml.transform.TransformerFactoryConfigurationError">
 </exception>
 </method>
-<method name="newInstance"
- return="javax.xml.transform.TransformerFactory"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="factoryClassName" type="java.lang.String">
-</parameter>
-<parameter name="classLoader" type="java.lang.ClassLoader">
-</parameter>
-<exception name="TransformerFactoryConfigurationError" type="javax.xml.transform.TransformerFactoryConfigurationError">
-</exception>
-</method>
 <method name="newTemplates"
  return="javax.xml.transform.Templates"
  abstract="true"
@@ -371198,30 +346699,13 @@
  native="false"
  synchronized="false"
  static="true"
- final="false"
+ final="true"
  deprecated="not deprecated"
  visibility="public"
 >
 <parameter name="schemaLanguage" type="java.lang.String">
 </parameter>
 </method>
-<method name="newInstance"
- return="javax.xml.validation.SchemaFactory"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="schemaLanguage" type="java.lang.String">
-</parameter>
-<parameter name="factoryClassName" type="java.lang.String">
-</parameter>
-<parameter name="classLoader" type="java.lang.ClassLoader">
-</parameter>
-</method>
 <method name="newSchema"
  return="javax.xml.validation.Schema"
  abstract="false"
@@ -411701,7 +387185,7 @@
  abstract="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <method name="getLength"
@@ -412190,7 +387674,7 @@
  abstract="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <method name="characters"
@@ -412405,7 +387889,7 @@
  abstract="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <implements name="org.xml.sax.DTDHandler">
@@ -412872,7 +388356,7 @@
  abstract="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <method name="parse"
@@ -414304,7 +389788,7 @@
  abstract="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <implements name="org.xml.sax.AttributeList">
@@ -415793,7 +391277,7 @@
  abstract="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <method name="makeParser"
diff --git a/api/current.xml b/api/current.xml
index 0ceda22..5cad414 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -1255,6 +1255,17 @@
  visibility="public"
 >
 </field>
+<field name="WRITE_MEDIA_STORAGE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.WRITE_MEDIA_STORAGE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="WRITE_SECURE_SETTINGS"
  type="java.lang.String"
  transient="false"
@@ -1471,6 +1482,17 @@
  visibility="public"
 >
 </constructor>
+<field name="accelerate_cubic_interpolator"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432591"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="accelerate_decelerate_interpolator"
  type="int"
  transient="false"
@@ -1493,6 +1515,39 @@
  visibility="public"
 >
 </field>
+<field name="accelerate_quint_interpolator"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432593"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="animator_fade_in"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432589"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="animator_fade_out"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432590"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="anticipate_interpolator"
  type="int"
  transient="false"
@@ -1537,6 +1592,17 @@
  visibility="public"
 >
 </field>
+<field name="decelerate_cubic_interpolator"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432592"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="decelerate_interpolator"
  type="int"
  transient="false"
@@ -1548,6 +1614,17 @@
  visibility="public"
 >
 </field>
+<field name="decelerate_quint_interpolator"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432594"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="fade_in"
  type="int"
  transient="false"
@@ -1747,6 +1824,193 @@
  visibility="public"
 >
 </field>
+<field name="actionBarSize"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843513"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionBarStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843482"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionBarTabBarStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843522"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionBarTabStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843521"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionBarTabTextStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843523"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionButtonStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843492"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionDropDownStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843491"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843529"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionModeBackground"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843495"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionModeCloseButtonStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843525"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionModeCloseDrawable"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843496"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionModeCopyDrawable"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843552"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionModeCutDrawable"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843551"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionModePasteDrawable"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843553"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionOverflowButtonStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843524"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="actionViewClass"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843530"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="activatedBackgroundIndicator"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843531"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="activityCloseEnterAnimation"
  type="int"
  transient="false"
@@ -1791,6 +2055,17 @@
  visibility="public"
 >
 </field>
+<field name="adapter"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843468"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="addStatesFromChildren"
  type="int"
  transient="false"
@@ -1813,6 +2088,17 @@
  visibility="public"
 >
 </field>
+<field name="alertDialogButtonGroupStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843547"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="alertDialogStyle"
  type="int"
  transient="false"
@@ -1824,6 +2110,28 @@
  visibility="public"
 >
 </field>
+<field name="alertDialogTheme"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843543"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="allContactsName"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843480"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="allowBackup"
  type="int"
  transient="false"
@@ -1912,6 +2220,28 @@
  visibility="public"
 >
 </field>
+<field name="animateFirstView"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843489"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="animateLayoutChanges"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843520"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="animateOnClick"
  type="int"
  transient="false"
@@ -2000,6 +2330,17 @@
  visibility="public"
 >
 </field>
+<field name="as"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843474"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="author"
  type="int"
  transient="false"
@@ -2231,6 +2572,28 @@
  visibility="public"
 >
 </field>
+<field name="breadCrumbShortTitle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843538"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="breadCrumbTitle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843537"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="bufferType"
  type="int"
  transient="false"
@@ -2253,6 +2616,17 @@
  visibility="public"
 >
 </field>
+<field name="buttonGroupStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843546"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="buttonStyle"
  type="int"
  transient="false"
@@ -2660,6 +3034,17 @@
  visibility="public"
 >
 </field>
+<field name="column"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843477"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="columnDelay"
  type="int"
  transient="false"
@@ -2803,6 +3188,17 @@
  visibility="public"
 >
 </field>
+<field name="customNavigationLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843486"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="cycles"
  type="int"
  transient="false"
@@ -3001,6 +3397,17 @@
  visibility="public"
 >
 </field>
+<field name="dialogTheme"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843542"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="dialogTitle"
  type="int"
  transient="false"
@@ -3067,6 +3474,17 @@
  visibility="public"
 >
 </field>
+<field name="displayOptions"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843484"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="dither"
  type="int"
  transient="false"
@@ -3100,6 +3518,17 @@
  visibility="public"
 >
 </field>
+<field name="dividerVertical"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843545"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="drawSelectorOnTop"
  type="int"
  transient="false"
@@ -3265,6 +3694,17 @@
  visibility="public"
 >
 </field>
+<field name="dropDownSpinnerStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843490"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="dropDownVerticalOffset"
  type="int"
  transient="false"
@@ -3408,6 +3848,17 @@
  visibility="public"
 >
 </field>
+<field name="enterFadeDuration"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843549"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="entries"
  type="int"
  transient="false"
@@ -3452,6 +3903,17 @@
  visibility="public"
 >
 </field>
+<field name="exitFadeDuration"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843550"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="expandableListPreferredChildIndicatorLeft"
  type="int"
  transient="false"
@@ -3826,6 +4288,105 @@
  visibility="public"
 >
 </field>
+<field name="fragment"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843503"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentCloseEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843507"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentCloseExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843508"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentNextEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843509"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentNextExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843510"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentOpenEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843505"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentOpenExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843506"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentPrevEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843511"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentPrevExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843512"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="freezesText"
  type="int"
  transient="false"
@@ -3837,6 +4398,17 @@
  visibility="public"
 >
 </field>
+<field name="from"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843472"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="fromAlpha"
  type="int"
  transient="false"
@@ -3859,6 +4431,17 @@
  visibility="public"
 >
 </field>
+<field name="fromValue"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843475"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="fromXDelta"
  type="int"
  transient="false"
@@ -4145,6 +4728,17 @@
  visibility="public"
 >
 </field>
+<field name="hardwareAccelerated"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843487"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="hasCode"
  type="int"
  transient="false"
@@ -4200,6 +4794,17 @@
  visibility="public"
 >
 </field>
+<field name="homeAsUpIndicator"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843548"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="horizontalDivider"
  type="int"
  transient="false"
@@ -4266,6 +4871,17 @@
  visibility="public"
 >
 </field>
+<field name="iconifiedByDefault"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843528"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="id"
  type="int"
  transient="false"
@@ -4376,6 +4992,50 @@
  visibility="public"
 >
 </field>
+<field name="imeSubtypeExtraValue"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843516"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="imeSubtypeLocale"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843514"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="imeSubtypeMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843515"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="immersive"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843456"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="inAnimation"
  type="int"
  transient="false"
@@ -5377,6 +6037,17 @@
  visibility="public"
 >
 </field>
+<field name="listChoiceBackgroundIndicator"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843518"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="listChoiceIndicatorMultiple"
  type="int"
  transient="false"
@@ -5410,6 +6081,28 @@
  visibility="public"
 >
 </field>
+<field name="listDividerAlertDialog"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843539"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="listPopupWindowStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843533"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="listPreferredItemHeight"
  type="int"
  transient="false"
@@ -5465,6 +6158,17 @@
  visibility="public"
 >
 </field>
+<field name="logo"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843454"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="longClickable"
  type="int"
  transient="false"
@@ -5476,6 +6180,17 @@
  visibility="public"
 >
 </field>
+<field name="loopViews"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843541"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="manageSpaceActivity"
  type="int"
  transient="false"
@@ -5630,6 +6345,17 @@
  visibility="public"
 >
 </field>
+<field name="measureWithLargestChild"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843488"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="menuCategory"
  type="int"
  transient="false"
@@ -5762,6 +6488,17 @@
  visibility="public"
 >
 </field>
+<field name="navigationMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843483"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="negativeButtonText"
  type="int"
  transient="false"
@@ -5927,6 +6664,17 @@
  visibility="public"
 >
 </field>
+<field name="ordering"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843502"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="orderingFromXml"
  type="int"
  transient="false"
@@ -5960,39 +6708,6 @@
  visibility="public"
 >
 </field>
-<field name="overScrollFooter"
- type="int"
- transient="false"
- volatile="false"
- value="16843459"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="overScrollHeader"
- type="int"
- transient="false"
- volatile="false"
- value="16843458"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="overScrollMode"
- type="int"
- transient="false"
- volatile="false"
- value="16843457"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="padding"
  type="int"
  transient="false"
@@ -6279,6 +6994,17 @@
  visibility="public"
 >
 </field>
+<field name="popupMenuStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843534"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="popupWindowStyle"
  type="int"
  transient="false"
@@ -6367,6 +7093,17 @@
  visibility="public"
 >
 </field>
+<field name="previewImage"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843494"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="priority"
  type="int"
  transient="false"
@@ -6521,6 +7258,17 @@
  visibility="public"
 >
 </field>
+<field name="propertyName"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843501"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="protectionLevel"
  type="int"
  transient="false"
@@ -7313,6 +8061,17 @@
  visibility="public"
 >
 </field>
+<field name="selection"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843469"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="settingsActivity"
  type="int"
  transient="false"
@@ -7423,6 +8182,17 @@
  visibility="public"
 >
 </field>
+<field name="showAsAction"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843493"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="showDefault"
  type="int"
  transient="false"
@@ -7500,6 +8270,17 @@
  visibility="public"
 >
 </field>
+<field name="sortOrder"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843470"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="soundEffectsEnabled"
  type="int"
  transient="false"
@@ -7544,6 +8325,17 @@
  visibility="public"
 >
 </field>
+<field name="spinnerMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843519"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="spinnerStyle"
  type="int"
  transient="false"
@@ -7555,6 +8347,17 @@
  visibility="public"
 >
 </field>
+<field name="splitMotionEvents"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843517"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="src"
  type="int"
  transient="false"
@@ -7643,6 +8446,17 @@
  visibility="public"
 >
 </field>
+<field name="state_activated"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843532"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="state_active"
  type="int"
  transient="false"
@@ -7852,6 +8666,28 @@
  visibility="public"
 >
 </field>
+<field name="subtitle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843485"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="subtitleTextStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843527"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="suggestActionMsg"
  type="int"
  transient="false"
@@ -8226,6 +9062,17 @@
  visibility="public"
 >
 </field>
+<field name="textAppearanceLargePopupMenu"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843535"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="textAppearanceMedium"
  type="int"
  transient="false"
@@ -8292,6 +9139,17 @@
  visibility="public"
 >
 </field>
+<field name="textAppearanceSmallPopupMenu"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843536"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="textCheckMark"
  type="int"
  transient="false"
@@ -8325,6 +9183,17 @@
  visibility="public"
 >
 </field>
+<field name="textColorAlertDialogListItem"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843540"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="textColorHighlight"
  type="int"
  transient="false"
@@ -8501,6 +9370,28 @@
  visibility="public"
 >
 </field>
+<field name="textEditNoPasteWindowLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843555"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="textEditPasteWindowLayout"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843554"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="textFilterEnabled"
  type="int"
  transient="false"
@@ -8512,6 +9403,17 @@
  visibility="public"
 >
 </field>
+<field name="textLineHeight"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843544"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="textOff"
  type="int"
  transient="false"
@@ -8732,6 +9634,28 @@
  visibility="public"
 >
 </field>
+<field name="titleTextStyle"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843526"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="to"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843473"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="toAlpha"
  type="int"
  transient="false"
@@ -8754,6 +9678,17 @@
  visibility="public"
 >
 </field>
+<field name="toValue"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843476"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="toXDelta"
  type="int"
  transient="false"
@@ -8930,6 +9865,17 @@
  visibility="public"
 >
 </field>
+<field name="uri"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843471"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="useLevel"
  type="int"
  transient="false"
@@ -8963,6 +9909,39 @@
  visibility="public"
 >
 </field>
+<field name="valueFrom"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843498"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="valueTo"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843499"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="valueType"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843500"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="variablePadding"
  type="int"
  transient="false"
@@ -9271,6 +10250,39 @@
  visibility="public"
 >
 </field>
+<field name="windowActionBar"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843481"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="windowActionBarOverlay"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843504"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="windowActionModeOverlay"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843497"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="windowAnimationStyle"
  type="int"
  transient="false"
@@ -9480,6 +10492,28 @@
  visibility="public"
 >
 </field>
+<field name="withClass"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843479"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="withExpression"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843478"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="writePermission"
  type="int"
  transient="false"
@@ -11941,6 +12975,17 @@
  visibility="public"
 >
 </field>
+<field name="home"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16908332"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="icon"
  type="int"
  transient="false"
@@ -12095,6 +13140,17 @@
  visibility="public"
 >
 </field>
+<field name="selectTextMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16908333"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="selectedIcon"
  type="int"
  transient="false"
@@ -12338,6 +13394,17 @@
  visibility="public"
 >
 </field>
+<field name="list_content"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17367060"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="preference_category"
  type="int"
  transient="false"
@@ -12448,6 +13515,28 @@
  visibility="public"
 >
 </field>
+<field name="simple_list_item_activated_1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17367062"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="simple_list_item_activated_2"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17367063"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="simple_list_item_checked"
  type="int"
  transient="false"
@@ -12481,6 +13570,17 @@
  visibility="public"
 >
 </field>
+<field name="simple_selectable_list_item"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17367061"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="simple_spinner_dropdown_item"
  type="int"
  transient="false"
@@ -12526,6 +13626,23 @@
 >
 </field>
 </class>
+<class name="R.menu"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="R.menu"
+ type="android.R.menu"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+</class>
 <class name="R.plurals"
  extends="java.lang.Object"
  abstract="false"
@@ -12785,6 +13902,17 @@
  visibility="public"
 >
 </field>
+<field name="selectTextMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17039382"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="unknownName"
  type="int"
  transient="false"
@@ -13209,6 +14337,28 @@
  visibility="public"
 >
 </field>
+<field name="TextAppearance_Widget_PopupMenu_Large"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973951"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TextAppearance_Widget_PopupMenu_Small"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973952"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TextAppearance_Widget_TabWidget"
  type="int"
  transient="false"
@@ -13319,6 +14469,127 @@
  visibility="public"
 >
 </field>
+<field name="Theme_Dialog_NoFrame"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973932"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Holo"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973934"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Holo_Dialog"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973941"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Holo_Dialog_Alert"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973945"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Holo_Extended"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973943"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Holo_Light"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973940"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Holo_Light_Dialog"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973942"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Holo_Light_Dialog_Alert"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973946"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Holo_Light_Extended"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973944"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Holo_NoActionBar"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973936"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Holo_NoActionBar_Fullscreen"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973937"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Theme_InputMethod"
  type="int"
  transient="false"
@@ -13341,6 +14612,39 @@
  visibility="public"
 >
 </field>
+<field name="Theme_Light_Holo"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973935"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Light_Holo_NoActionBar"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973938"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Light_Holo_NoActionBar_Fullscreen"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973939"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Theme_Light_NoTitleBar"
  type="int"
  transient="false"
@@ -13418,6 +14722,17 @@
  visibility="public"
 >
 </field>
+<field name="Theme_NoTitleBar_OverlayActionModes"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973933"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Theme_Panel"
  type="int"
  transient="false"
@@ -13506,6 +14821,17 @@
  visibility="public"
 >
 </field>
+<field name="Theme_WithActionBar"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973929"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget"
  type="int"
  transient="false"
@@ -13528,6 +14854,39 @@
  visibility="public"
 >
 </field>
+<field name="Widget_ActionButton"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973931"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Widget_ActionButton_CloseMode"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973950"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Widget_ActionButton_Overflow"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973949"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget_AutoCompleteTextView"
  type="int"
  transient="false"
@@ -13671,6 +15030,17 @@
  visibility="public"
 >
 </field>
+<field name="Widget_FragmentBreadCrumbs"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973953"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget_Gallery"
  type="int"
  transient="false"
@@ -13726,6 +15096,17 @@
  visibility="public"
 >
 </field>
+<field name="Widget_ListPopupWindow"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973947"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget_ListView"
  type="int"
  transient="false"
@@ -13770,6 +15151,17 @@
  visibility="public"
 >
 </field>
+<field name="Widget_PopupMenu"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973948"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget_PopupWindow"
  type="int"
  transient="false"
@@ -13902,6 +15294,17 @@
  visibility="public"
 >
 </field>
+<field name="Widget_Spinner_DropDown"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973930"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget_TabWidget"
  type="int"
  transient="false"
@@ -15842,8 +17245,3108 @@
 </constructor>
 </class>
 </package>
+<package name="android.animation"
+>
+<class name="Animator"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.lang.Cloneable">
+</implements>
+<constructor name="Animator"
+ type="android.animation.Animator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.animation.Animator.AnimatorListener">
+</parameter>
+</method>
+<method name="cancel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="clone"
+ return="android.animation.Animator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="end"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDuration"
+ return="long"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getListeners"
+ return="java.util.ArrayList&lt;android.animation.Animator.AnimatorListener&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStartDelay"
+ return="long"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isRunning"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeAllListeners"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.animation.Animator.AnimatorListener">
+</parameter>
+</method>
+<method name="setDuration"
+ return="android.animation.Animator"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="long">
+</parameter>
+</method>
+<method name="setInterpolator"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="android.animation.TimeInterpolator">
+</parameter>
+</method>
+<method name="setStartDelay"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startDelay" type="long">
+</parameter>
+</method>
+<method name="setTarget"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="target" type="java.lang.Object">
+</parameter>
+</method>
+<method name="setupEndValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setupStartValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="start"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<interface name="Animator.AnimatorListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onAnimationCancel"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animation" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="onAnimationEnd"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animation" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="onAnimationRepeat"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animation" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="onAnimationStart"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animation" type="android.animation.Animator">
+</parameter>
+</method>
+</interface>
+<class name="AnimatorInflater"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AnimatorInflater"
+ type="android.animation.AnimatorInflater"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="loadAnimator"
+ return="android.animation.Animator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="id" type="int">
+</parameter>
+<exception name="Resources.NotFoundException" type="android.content.res.Resources.NotFoundException">
+</exception>
+</method>
+</class>
+<class name="AnimatorListenerAdapter"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.animation.Animator.AnimatorListener">
+</implements>
+<constructor name="AnimatorListenerAdapter"
+ type="android.animation.AnimatorListenerAdapter"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onAnimationCancel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animation" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="onAnimationEnd"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animation" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="onAnimationRepeat"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animation" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="onAnimationStart"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animation" type="android.animation.Animator">
+</parameter>
+</method>
+</class>
+<class name="AnimatorSet"
+ extends="android.animation.Animator"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AnimatorSet"
+ type="android.animation.AnimatorSet"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getChildAnimations"
+ return="java.util.ArrayList&lt;android.animation.Animator&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDuration"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStartDelay"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isRunning"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="play"
+ return="android.animation.AnimatorSet.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="anim" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="playSequentially"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="items" type="android.animation.Animator...">
+</parameter>
+</method>
+<method name="playTogether"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="items" type="android.animation.Animator...">
+</parameter>
+</method>
+<method name="setDuration"
+ return="android.animation.AnimatorSet"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="long">
+</parameter>
+</method>
+<method name="setInterpolator"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="interpolator" type="android.animation.TimeInterpolator">
+</parameter>
+</method>
+<method name="setStartDelay"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startDelay" type="long">
+</parameter>
+</method>
+</class>
+<class name="AnimatorSet.Builder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="after"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="anim" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="after"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="delay" type="long">
+</parameter>
+</method>
+<method name="before"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="anim" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="with"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="anim" type="android.animation.Animator">
+</parameter>
+</method>
+</class>
+<class name="DoubleEvaluator"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.animation.TypeEvaluator">
+</implements>
+<constructor name="DoubleEvaluator"
+ type="android.animation.DoubleEvaluator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="evaluate"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="startValue" type="java.lang.Object">
+</parameter>
+<parameter name="endValue" type="java.lang.Object">
+</parameter>
+</method>
+</class>
+<class name="FloatEvaluator"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.animation.TypeEvaluator">
+</implements>
+<constructor name="FloatEvaluator"
+ type="android.animation.FloatEvaluator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="evaluate"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="startValue" type="java.lang.Object">
+</parameter>
+<parameter name="endValue" type="java.lang.Object">
+</parameter>
+</method>
+</class>
+<class name="IntEvaluator"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.animation.TypeEvaluator">
+</implements>
+<constructor name="IntEvaluator"
+ type="android.animation.IntEvaluator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="evaluate"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="startValue" type="java.lang.Object">
+</parameter>
+<parameter name="endValue" type="java.lang.Object">
+</parameter>
+</method>
+</class>
+<class name="Keyframe"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.lang.Cloneable">
+</implements>
+<constructor name="Keyframe"
+ type="android.animation.Keyframe"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="clone"
+ return="android.animation.Keyframe"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFraction"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInterpolator"
+ return="android.animation.TimeInterpolator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getType"
+ return="java.lang.Class"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getValue"
+ return="java.lang.Object"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasValue"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="ofFloat"
+ return="android.animation.Keyframe"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="value" type="float">
+</parameter>
+</method>
+<method name="ofFloat"
+ return="android.animation.Keyframe"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+</method>
+<method name="ofInt"
+ return="android.animation.Keyframe"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="value" type="int">
+</parameter>
+</method>
+<method name="ofInt"
+ return="android.animation.Keyframe"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+</method>
+<method name="ofObject"
+ return="android.animation.Keyframe"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="value" type="java.lang.Object">
+</parameter>
+</method>
+<method name="ofObject"
+ return="android.animation.Keyframe"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+</method>
+<method name="setFraction"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+</method>
+<method name="setInterpolator"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="interpolator" type="android.animation.TimeInterpolator">
+</parameter>
+</method>
+<method name="setValue"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="java.lang.Object">
+</parameter>
+</method>
+</class>
+<class name="LayoutTransition"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="LayoutTransition"
+ type="android.animation.LayoutTransition"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addChild"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parent" type="android.view.ViewGroup">
+</parameter>
+<parameter name="child" type="android.view.View">
+</parameter>
+</method>
+<method name="addTransitionListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.animation.LayoutTransition.TransitionListener">
+</parameter>
+</method>
+<method name="getAnimator"
+ return="android.animation.Animator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+<method name="getDuration"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+<method name="getInterpolator"
+ return="android.animation.TimeInterpolator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+<method name="getStagger"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+<method name="getStartDelay"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+<method name="getTransitionListeners"
+ return="java.util.List&lt;android.animation.LayoutTransition.TransitionListener&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hideChild"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parent" type="android.view.ViewGroup">
+</parameter>
+<parameter name="child" type="android.view.View">
+</parameter>
+</method>
+<method name="removeChild"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parent" type="android.view.ViewGroup">
+</parameter>
+<parameter name="child" type="android.view.View">
+</parameter>
+</method>
+<method name="removeTransitionListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.animation.LayoutTransition.TransitionListener">
+</parameter>
+</method>
+<method name="setAnimator"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+<parameter name="animator" type="android.animation.Animator">
+</parameter>
+</method>
+<method name="setDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="long">
+</parameter>
+</method>
+<method name="setDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+<parameter name="duration" type="long">
+</parameter>
+</method>
+<method name="setInterpolator"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+<parameter name="interpolator" type="android.animation.TimeInterpolator">
+</parameter>
+</method>
+<method name="setStagger"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+<parameter name="duration" type="long">
+</parameter>
+</method>
+<method name="setStartDelay"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transitionType" type="int">
+</parameter>
+<parameter name="delay" type="long">
+</parameter>
+</method>
+<method name="showChild"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parent" type="android.view.ViewGroup">
+</parameter>
+<parameter name="child" type="android.view.View">
+</parameter>
+</method>
+<field name="APPEARING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANGE_APPEARING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANGE_DISAPPEARING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISAPPEARING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="LayoutTransition.TransitionListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="endTransition"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transition" type="android.animation.LayoutTransition">
+</parameter>
+<parameter name="container" type="android.view.ViewGroup">
+</parameter>
+<parameter name="view" type="android.view.View">
+</parameter>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+<method name="startTransition"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transition" type="android.animation.LayoutTransition">
+</parameter>
+<parameter name="container" type="android.view.ViewGroup">
+</parameter>
+<parameter name="view" type="android.view.View">
+</parameter>
+<parameter name="transitionType" type="int">
+</parameter>
+</method>
+</interface>
+<class name="ObjectAnimator"
+ extends="android.animation.ValueAnimator"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ObjectAnimator"
+ type="android.animation.ObjectAnimator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getPropertyName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTarget"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="ofFloat"
+ return="android.animation.ObjectAnimator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="target" type="java.lang.Object">
+</parameter>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+<parameter name="values" type="float...">
+</parameter>
+</method>
+<method name="ofInt"
+ return="android.animation.ObjectAnimator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="target" type="java.lang.Object">
+</parameter>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+<parameter name="values" type="int...">
+</parameter>
+</method>
+<method name="ofObject"
+ return="android.animation.ObjectAnimator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="target" type="java.lang.Object">
+</parameter>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+<parameter name="evaluator" type="android.animation.TypeEvaluator">
+</parameter>
+<parameter name="values" type="java.lang.Object...">
+</parameter>
+</method>
+<method name="ofPropertyValuesHolder"
+ return="android.animation.ObjectAnimator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="target" type="java.lang.Object">
+</parameter>
+<parameter name="values" type="android.animation.PropertyValuesHolder...">
+</parameter>
+</method>
+<method name="setPropertyName"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+</method>
+</class>
+<class name="PropertyValuesHolder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.lang.Cloneable">
+</implements>
+<method name="clone"
+ return="android.animation.PropertyValuesHolder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGetter"
+ return="java.lang.reflect.Method"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPropertyName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSetter"
+ return="java.lang.reflect.Method"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="ofFloat"
+ return="android.animation.PropertyValuesHolder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+<parameter name="values" type="float...">
+</parameter>
+</method>
+<method name="ofInt"
+ return="android.animation.PropertyValuesHolder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+<parameter name="values" type="int...">
+</parameter>
+</method>
+<method name="ofKeyframe"
+ return="android.animation.PropertyValuesHolder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+<parameter name="values" type="android.animation.Keyframe...">
+</parameter>
+</method>
+<method name="ofObject"
+ return="android.animation.PropertyValuesHolder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+<parameter name="evaluator" type="android.animation.TypeEvaluator">
+</parameter>
+<parameter name="values" type="java.lang.Object...">
+</parameter>
+</method>
+<method name="setEvaluator"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="evaluator" type="android.animation.TypeEvaluator">
+</parameter>
+</method>
+<method name="setFloatValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="float...">
+</parameter>
+</method>
+<method name="setGetter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="getter" type="java.lang.reflect.Method">
+</parameter>
+</method>
+<method name="setIntValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="int...">
+</parameter>
+</method>
+<method name="setKeyframes"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="android.animation.Keyframe...">
+</parameter>
+</method>
+<method name="setObjectValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="java.lang.Object...">
+</parameter>
+</method>
+<method name="setPropertyName"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+</method>
+<method name="setSetter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="setter" type="java.lang.reflect.Method">
+</parameter>
+</method>
+</class>
+<class name="RGBEvaluator"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.animation.TypeEvaluator">
+</implements>
+<constructor name="RGBEvaluator"
+ type="android.animation.RGBEvaluator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="evaluate"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="startValue" type="java.lang.Object">
+</parameter>
+<parameter name="endValue" type="java.lang.Object">
+</parameter>
+</method>
+</class>
+<interface name="TimeInterpolator"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getInterpolation"
+ return="float"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="input" type="float">
+</parameter>
+</method>
+</interface>
+<interface name="TypeEvaluator"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="evaluate"
+ return="java.lang.Object"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fraction" type="float">
+</parameter>
+<parameter name="startValue" type="java.lang.Object">
+</parameter>
+<parameter name="endValue" type="java.lang.Object">
+</parameter>
+</method>
+</interface>
+<class name="ValueAnimator"
+ extends="android.animation.Animator"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ValueAnimator"
+ type="android.animation.ValueAnimator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addUpdateListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.animation.ValueAnimator.AnimatorUpdateListener">
+</parameter>
+</method>
+<method name="getAnimatedValue"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAnimatedValue"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+</method>
+<method name="getCurrentPlayTime"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDuration"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFrameDelay"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInterpolator"
+ return="android.animation.TimeInterpolator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRepeatCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRepeatMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStartDelay"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getValues"
+ return="android.animation.PropertyValuesHolder[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isRunning"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="ofFloat"
+ return="android.animation.ValueAnimator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="float...">
+</parameter>
+</method>
+<method name="ofInt"
+ return="android.animation.ValueAnimator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="int...">
+</parameter>
+</method>
+<method name="ofObject"
+ return="android.animation.ValueAnimator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="evaluator" type="android.animation.TypeEvaluator">
+</parameter>
+<parameter name="values" type="java.lang.Object...">
+</parameter>
+</method>
+<method name="ofPropertyValuesHolder"
+ return="android.animation.ValueAnimator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="android.animation.PropertyValuesHolder...">
+</parameter>
+</method>
+<method name="removeAllUpdateListeners"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeUpdateListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.animation.ValueAnimator.AnimatorUpdateListener">
+</parameter>
+</method>
+<method name="reverse"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setCurrentPlayTime"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="playTime" type="long">
+</parameter>
+</method>
+<method name="setDuration"
+ return="android.animation.ValueAnimator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="long">
+</parameter>
+</method>
+<method name="setEvaluator"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="android.animation.TypeEvaluator">
+</parameter>
+</method>
+<method name="setFloatValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="float...">
+</parameter>
+</method>
+<method name="setFrameDelay"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="frameDelay" type="long">
+</parameter>
+</method>
+<method name="setIntValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="int...">
+</parameter>
+</method>
+<method name="setInterpolator"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="android.animation.TimeInterpolator">
+</parameter>
+</method>
+<method name="setObjectValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="java.lang.Object...">
+</parameter>
+</method>
+<method name="setRepeatCount"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="int">
+</parameter>
+</method>
+<method name="setRepeatMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="int">
+</parameter>
+</method>
+<method name="setStartDelay"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startDelay" type="long">
+</parameter>
+</method>
+<method name="setValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="android.animation.PropertyValuesHolder...">
+</parameter>
+</method>
+<field name="INFINITE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RESTART"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="REVERSE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="ValueAnimator.AnimatorUpdateListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onAnimationUpdate"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animation" type="android.animation.ValueAnimator">
+</parameter>
+</method>
+</interface>
+</package>
 <package name="android.app"
 >
+<class name="ActionBar"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ActionBar"
+ type="android.app.ActionBar"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+</method>
+<method name="addTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+<parameter name="setSelected" type="boolean">
+</parameter>
+</method>
+<method name="addTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="addTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+<parameter name="setSelected" type="boolean">
+</parameter>
+</method>
+<method name="getCustomNavigationView"
+ return="android.view.View"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDisplayOptions"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getHeight"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getNavigationItemCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getNavigationMode"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedNavigationIndex"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedNavigationItem"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedTab"
+ return="android.app.ActionBar.Tab"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSubtitle"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTabAt"
+ return="android.app.ActionBar.Tab"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
+<method name="getTabCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTitle"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hide"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isShowing"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newTab"
+ return="android.app.ActionBar.Tab"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeAllTabs"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="removeTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+</method>
+<method name="removeTabAt"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="selectTab"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+</method>
+<method name="setBackgroundDrawable"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="d" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setCustomNavigationMode"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
+<method name="setCustomView"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
+<method name="setCustomView"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+<parameter name="layoutParams" type="android.app.ActionBar.LayoutParams">
+</parameter>
+</method>
+<method name="setDisplayOptions"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="options" type="int">
+</parameter>
+</method>
+<method name="setDisplayOptions"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="options" type="int">
+</parameter>
+<parameter name="mask" type="int">
+</parameter>
+</method>
+<method name="setDropdownNavigationMode"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.widget.SpinnerAdapter">
+</parameter>
+<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+</parameter>
+</method>
+<method name="setDropdownNavigationMode"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.widget.SpinnerAdapter">
+</parameter>
+<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+</parameter>
+<parameter name="defaultSelectedPosition" type="int">
+</parameter>
+</method>
+<method name="setListNavigationCallbacks"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.widget.SpinnerAdapter">
+</parameter>
+<parameter name="callback" type="android.app.ActionBar.NavigationCallback">
+</parameter>
+</method>
+<method name="setNavigationMode"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="int">
+</parameter>
+</method>
+<method name="setSelectedNavigationItem"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="setStandardNavigationMode"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+</method>
+<method name="setSubtitle"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="subtitle" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setSubtitle"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="resId" type="int">
+</parameter>
+</method>
+<method name="setTabNavigationMode"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+</method>
+<method name="setTitle"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="title" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setTitle"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="resId" type="int">
+</parameter>
+</method>
+<method name="show"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<field name="DISPLAY_HIDE_HOME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4096"
+ static="true"
+ final="true"
+ deprecated="deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISPLAY_HOME_AS_UP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISPLAY_SHOW_CUSTOM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISPLAY_SHOW_HOME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISPLAY_SHOW_TITLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISPLAY_USE_LOGO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NAVIGATION_MODE_DROPDOWN_LIST"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="deprecated"
+ visibility="public"
+>
+</field>
+<field name="NAVIGATION_MODE_LIST"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NAVIGATION_MODE_STANDARD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NAVIGATION_MODE_TABS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="ActionBar.LayoutParams"
+ extends="android.view.ViewGroup.MarginLayoutParams"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ActionBar.LayoutParams"
+ type="android.app.ActionBar.LayoutParams"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="c" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+<constructor name="ActionBar.LayoutParams"
+ type="android.app.ActionBar.LayoutParams"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+</constructor>
+<constructor name="ActionBar.LayoutParams"
+ type="android.app.ActionBar.LayoutParams"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+<parameter name="gravity" type="int">
+</parameter>
+</constructor>
+<constructor name="ActionBar.LayoutParams"
+ type="android.app.ActionBar.LayoutParams"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="gravity" type="int">
+</parameter>
+</constructor>
+<constructor name="ActionBar.LayoutParams"
+ type="android.app.ActionBar.LayoutParams"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="android.app.ActionBar.LayoutParams">
+</parameter>
+</constructor>
+<constructor name="ActionBar.LayoutParams"
+ type="android.app.ActionBar.LayoutParams"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="android.view.ViewGroup.LayoutParams">
+</parameter>
+</constructor>
+<field name="gravity"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="ActionBar.NavigationCallback"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onNavigationItemSelected"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="itemPosition" type="int">
+</parameter>
+<parameter name="itemId" type="long">
+</parameter>
+</method>
+</interface>
+<class name="ActionBar.Tab"
+ extends="java.lang.Object"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ActionBar.Tab"
+ type="android.app.ActionBar.Tab"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getCustomView"
+ return="android.view.View"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getIcon"
+ return="android.graphics.drawable.Drawable"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPosition"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTag"
+ return="java.lang.Object"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getText"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="select"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setCustomView"
+ return="android.app.ActionBar.Tab"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
+<method name="setIcon"
+ return="android.app.ActionBar.Tab"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="icon" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setTabListener"
+ return="android.app.ActionBar.Tab"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.app.ActionBar.TabListener">
+</parameter>
+</method>
+<method name="setTag"
+ return="android.app.ActionBar.Tab"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="obj" type="java.lang.Object">
+</parameter>
+</method>
+<method name="setText"
+ return="android.app.ActionBar.Tab"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
+<field name="INVALID_POSITION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="ActionBar.TabListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onTabReselected"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+<parameter name="ft" type="android.app.FragmentTransaction">
+</parameter>
+</method>
+<method name="onTabSelected"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+<parameter name="ft" type="android.app.FragmentTransaction">
+</parameter>
+</method>
+<method name="onTabUnselected"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tab" type="android.app.ActionBar.Tab">
+</parameter>
+<parameter name="ft" type="android.app.FragmentTransaction">
+</parameter>
+</method>
+</interface>
 <class name="Activity"
  extends="android.view.ContextThemeWrapper"
  abstract="false"
@@ -15856,7 +20359,7 @@
 </implements>
 <implements name="android.view.KeyEvent.Callback">
 </implements>
-<implements name="android.view.LayoutInflater.Factory">
+<implements name="android.view.LayoutInflater.Factory2">
 </implements>
 <implements name="android.view.View.OnCreateContextMenuListener">
 </implements>
@@ -15989,6 +20492,49 @@
 <parameter name="ev" type="android.view.MotionEvent">
 </parameter>
 </method>
+<method name="dump"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fd" type="java.io.FileDescriptor">
+</parameter>
+<parameter name="writer" type="java.io.PrintWriter">
+</parameter>
+<parameter name="args" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="findFragmentById"
+ return="android.app.Fragment"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
+<method name="findFragmentByTag"
+ return="android.app.Fragment"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
 <method name="findViewById"
  return="android.view.View"
  abstract="false"
@@ -16054,6 +20600,17 @@
 <parameter name="child" type="android.app.Activity">
 </parameter>
 </method>
+<method name="getActionBar"
+ return="android.app.ActionBar"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getApplication"
  return="android.app.Application"
  abstract="false"
@@ -16120,12 +20677,12 @@
  visibility="public"
 >
 </method>
-<method name="getInstanceCount"
- return="long"
+<method name="getFragmentManager"
+ return="android.app.FragmentManager"
  abstract="false"
  native="false"
  synchronized="false"
- static="true"
+ static="false"
  final="false"
  deprecated="not deprecated"
  visibility="public"
@@ -16164,6 +20721,17 @@
  visibility="public"
 >
 </method>
+<method name="getLoaderManager"
+ return="android.app.LoaderManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getLocalClassName"
  return="java.lang.String"
  abstract="false"
@@ -16298,6 +20866,28 @@
  visibility="public"
 >
 </method>
+<method name="invalidateOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isChangingConfigurations"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isChild"
  return="boolean"
  abstract="false"
@@ -16338,7 +20928,7 @@
  synchronized="false"
  static="false"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="uri" type="android.net.Uri">
@@ -16382,6 +20972,19 @@
 <parameter name="data" type="android.content.Intent">
 </parameter>
 </method>
+<method name="onAttachFragment"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+</method>
 <method name="onAttachedToWindow"
  return="void"
  abstract="false"
@@ -16611,6 +21214,25 @@
 <parameter name="attrs" type="android.util.AttributeSet">
 </parameter>
 </method>
+<method name="onCreateView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parent" type="android.view.View">
+</parameter>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</method>
 <method name="onDestroy"
  return="void"
  abstract="false"
@@ -16968,6 +21590,19 @@
  visibility="protected"
 >
 </method>
+<method name="onStartActionMode"
+ return="android.view.ActionMode"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.view.ActionMode.Callback">
+</parameter>
+</method>
 <method name="onStop"
  return="void"
  abstract="false"
@@ -17081,6 +21716,17 @@
 <parameter name="view" type="android.view.View">
 </parameter>
 </method>
+<method name="openFragmentTransaction"
+ return="android.app.FragmentTransaction"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+</method>
 <method name="openOptionsMenu"
  return="void"
  abstract="false"
@@ -17107,6 +21753,47 @@
 <parameter name="exitAnim" type="int">
 </parameter>
 </method>
+<method name="popBackStack"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+</method>
+<method name="popBackStack"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="popBackStack"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
 <method name="registerForContextMenu"
  return="void"
  abstract="false"
@@ -17498,6 +22185,19 @@
 <parameter name="args" type="android.os.Bundle">
 </parameter>
 </method>
+<method name="startActionMode"
+ return="android.view.ActionMode"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.view.ActionMode.Callback">
+</parameter>
+</method>
 <method name="startActivityForResult"
  return="void"
  abstract="false"
@@ -17530,6 +22230,23 @@
 <parameter name="requestCode" type="int">
 </parameter>
 </method>
+<method name="startActivityFromFragment"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+<parameter name="requestCode" type="int">
+</parameter>
+</method>
 <method name="startActivityIfNeeded"
  return="boolean"
  abstract="false"
@@ -17604,7 +22321,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="c" type="android.database.Cursor">
@@ -17649,7 +22366,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="c" type="android.database.Cursor">
@@ -17762,6 +22479,17 @@
  visibility="protected"
 >
 </field>
+<field name="POP_BACK_STACK_INCLUSIVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="RESULT_CANCELED"
  type="int"
  transient="false"
@@ -19621,6 +24349,17 @@
  visibility="public"
 >
 </method>
+<method name="getContext"
+ return="android.content.Context"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="setAdapter"
  return="android.app.AlertDialog.Builder"
  abstract="false"
@@ -20453,6 +25192,17 @@
 <parameter name="id" type="int">
 </parameter>
 </method>
+<method name="getActionBar"
+ return="android.app.ActionBar"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getContext"
  return="android.content.Context"
  abstract="false"
@@ -20530,6 +25280,17 @@
  visibility="public"
 >
 </method>
+<method name="invalidateOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isShowing"
  return="boolean"
  abstract="false"
@@ -20891,6 +25652,19 @@
  visibility="protected"
 >
 </method>
+<method name="onStartActionMode"
+ return="android.view.ActionMode"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.view.ActionMode.Callback">
+</parameter>
+</method>
 <method name="onStop"
  return="void"
  abstract="false"
@@ -21299,6 +26073,251 @@
 </parameter>
 </method>
 </class>
+<class name="DialogFragment"
+ extends="android.app.Fragment"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.content.DialogInterface.OnCancelListener">
+</implements>
+<implements name="android.content.DialogInterface.OnDismissListener">
+</implements>
+<constructor name="DialogFragment"
+ type="android.app.DialogFragment"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="dismiss"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCancelable"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDialog"
+ return="android.app.Dialog"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getShowsDialog"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTheme"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onCancel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dialog" type="android.content.DialogInterface">
+</parameter>
+</method>
+<method name="onCreateDialog"
+ return="android.app.Dialog"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="savedInstanceState" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onDismiss"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dialog" type="android.content.DialogInterface">
+</parameter>
+</method>
+<method name="setCancelable"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cancelable" type="boolean">
+</parameter>
+</method>
+<method name="setShowsDialog"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="showsDialog" type="boolean">
+</parameter>
+</method>
+<method name="setStyle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="style" type="int">
+</parameter>
+<parameter name="theme" type="int">
+</parameter>
+</method>
+<method name="show"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
+<method name="show"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="manager" type="android.app.FragmentManager">
+</parameter>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
+<method name="show"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transaction" type="android.app.FragmentTransaction">
+</parameter>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
+<field name="STYLE_NORMAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STYLE_NO_FRAME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STYLE_NO_INPUT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STYLE_NO_TITLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="DownloadManager"
  extends="java.lang.Object"
  abstract="false"
@@ -21320,6 +26339,19 @@
 <parameter name="request" type="android.app.DownloadManager.Request">
 </parameter>
 </method>
+<method name="getUriForDownloadedFile"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="long">
+</parameter>
+</method>
 <method name="openDownloadedFile"
  return="android.os.ParcelFileDescriptor"
  abstract="false"
@@ -21438,6 +26470,17 @@
  visibility="public"
 >
 </field>
+<field name="COLUMN_LOCAL_FILENAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;local_filename&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="COLUMN_LOCAL_URI"
  type="java.lang.String"
  transient="false"
@@ -21636,6 +26679,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;extra_click_download_ids&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="PAUSED_QUEUED_FOR_WIFI"
  type="int"
  transient="false"
@@ -21812,6 +26866,17 @@
 <parameter name="value" type="java.lang.String">
 </parameter>
 </method>
+<method name="allowScanningByMediaScanner"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="setAllowedNetworkTypes"
  return="android.app.DownloadManager.Request"
  abstract="false"
@@ -21909,7 +26974,7 @@
 <parameter name="mimeType" type="java.lang.String">
 </parameter>
 </method>
-<method name="setShowRunningNotification"
+<method name="setNotificationVisibility"
  return="android.app.DownloadManager.Request"
  abstract="false"
  native="false"
@@ -21919,6 +26984,19 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="visibility" type="int">
+</parameter>
+</method>
+<method name="setShowRunningNotification"
+ return="android.app.DownloadManager.Request"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
 <parameter name="show" type="boolean">
 </parameter>
 </method>
@@ -21970,6 +27048,39 @@
  visibility="public"
 >
 </field>
+<field name="VISIBILITY_HIDDEN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="VISIBILITY_VISIBLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="VISIBILITY_VISIBLE_NOTIFY_COMPLETED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="ExpandableListActivity"
  extends="android.app.Activity"
@@ -22130,6 +27241,1452 @@
 </parameter>
 </method>
 </class>
+<class name="Fragment"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.content.ComponentCallbacks">
+</implements>
+<implements name="android.view.View.OnCreateContextMenuListener">
+</implements>
+<constructor name="Fragment"
+ type="android.app.Fragment"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="dump"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="prefix" type="java.lang.String">
+</parameter>
+<parameter name="fd" type="java.io.FileDescriptor">
+</parameter>
+<parameter name="writer" type="java.io.PrintWriter">
+</parameter>
+<parameter name="args" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="equals"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="o" type="java.lang.Object">
+</parameter>
+</method>
+<method name="getActivity"
+ return="android.app.Activity"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getArguments"
+ return="android.os.Bundle"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFragmentManager"
+ return="android.app.FragmentManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getLoaderManager"
+ return="android.app.LoaderManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRetainInstance"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTag"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTargetFragment"
+ return="android.app.Fragment"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTargetRequestCode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hashCode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="instantiate"
+ return="android.app.Fragment"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="fname" type="java.lang.String">
+</parameter>
+</method>
+<method name="instantiate"
+ return="android.app.Fragment"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="fname" type="java.lang.String">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="isAdded"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isHidden"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isInLayout"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isResumed"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isVisible"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onActivityCreated"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="savedInstanceState" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onActivityResult"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="requestCode" type="int">
+</parameter>
+<parameter name="resultCode" type="int">
+</parameter>
+<parameter name="data" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onAttach"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+</method>
+<method name="onConfigurationChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="newConfig" type="android.content.res.Configuration">
+</parameter>
+</method>
+<method name="onContextItemSelected"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="item" type="android.view.MenuItem">
+</parameter>
+</method>
+<method name="onCreate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="savedInstanceState" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onCreateAnimator"
+ return="android.animation.Animator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transit" type="int">
+</parameter>
+<parameter name="enter" type="boolean">
+</parameter>
+<parameter name="nextAnim" type="int">
+</parameter>
+</method>
+<method name="onCreateContextMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="menu" type="android.view.ContextMenu">
+</parameter>
+<parameter name="v" type="android.view.View">
+</parameter>
+<parameter name="menuInfo" type="android.view.ContextMenu.ContextMenuInfo">
+</parameter>
+</method>
+<method name="onCreateOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="menu" type="android.view.Menu">
+</parameter>
+<parameter name="inflater" type="android.view.MenuInflater">
+</parameter>
+</method>
+<method name="onCreateView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="inflater" type="android.view.LayoutInflater">
+</parameter>
+<parameter name="container" type="android.view.ViewGroup">
+</parameter>
+<parameter name="savedInstanceState" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onDestroy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onDestroyOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onDestroyView"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onDetach"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onHiddenChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="hidden" type="boolean">
+</parameter>
+</method>
+<method name="onInflate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="savedInstanceState" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onLowMemory"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onOptionsItemSelected"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="item" type="android.view.MenuItem">
+</parameter>
+</method>
+<method name="onOptionsMenuClosed"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="menu" type="android.view.Menu">
+</parameter>
+</method>
+<method name="onPause"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onPrepareOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="menu" type="android.view.Menu">
+</parameter>
+</method>
+<method name="onResume"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onSaveInstanceState"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="outState" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onStart"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onStop"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="registerForContextMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
+<method name="setArguments"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="setHasOptionsMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="hasMenu" type="boolean">
+</parameter>
+</method>
+<method name="setRetainInstance"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="retain" type="boolean">
+</parameter>
+</method>
+<method name="setTargetFragment"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+<parameter name="requestCode" type="int">
+</parameter>
+</method>
+<method name="startActivity"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="startActivityForResult"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+<parameter name="requestCode" type="int">
+</parameter>
+</method>
+<method name="unregisterForContextMenu"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
+</class>
+<class name="Fragment.InstantiationException"
+ extends="android.util.AndroidRuntimeException"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Fragment.InstantiationException"
+ type="android.app.Fragment.InstantiationException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="msg" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Exception">
+</parameter>
+</constructor>
+</class>
+<class name="FragmentBreadCrumbs"
+ extends="android.view.ViewGroup"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.app.FragmentManager.OnBackStackChangedListener">
+</implements>
+<constructor name="FragmentBreadCrumbs"
+ type="android.app.FragmentBreadCrumbs"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="FragmentBreadCrumbs"
+ type="android.app.FragmentBreadCrumbs"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+<constructor name="FragmentBreadCrumbs"
+ type="android.app.FragmentBreadCrumbs"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyle" type="int">
+</parameter>
+</constructor>
+<method name="onBackStackChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onLayout"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="changed" type="boolean">
+</parameter>
+<parameter name="l" type="int">
+</parameter>
+<parameter name="t" type="int">
+</parameter>
+<parameter name="r" type="int">
+</parameter>
+<parameter name="b" type="int">
+</parameter>
+</method>
+<method name="setActivity"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="a" type="android.app.Activity">
+</parameter>
+</method>
+<method name="setTitle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="title" type="java.lang.CharSequence">
+</parameter>
+<parameter name="shortTitle" type="java.lang.CharSequence">
+</parameter>
+</method>
+</class>
+<interface name="FragmentManager"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="addOnBackStackChangedListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.app.FragmentManager.OnBackStackChangedListener">
+</parameter>
+</method>
+<method name="countBackStackEntries"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="dump"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="prefix" type="java.lang.String">
+</parameter>
+<parameter name="fd" type="java.io.FileDescriptor">
+</parameter>
+<parameter name="writer" type="java.io.PrintWriter">
+</parameter>
+<parameter name="args" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="findFragmentById"
+ return="android.app.Fragment"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
+<method name="findFragmentByTag"
+ return="android.app.Fragment"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
+<method name="getBackStackEntry"
+ return="android.app.FragmentManager.BackStackEntry"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
+<method name="getFragment"
+ return="android.app.Fragment"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bundle" type="android.os.Bundle">
+</parameter>
+<parameter name="key" type="java.lang.String">
+</parameter>
+</method>
+<method name="openTransaction"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="popBackStack"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="popBackStack"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="popBackStack"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<method name="putFragment"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bundle" type="android.os.Bundle">
+</parameter>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+</method>
+<method name="removeOnBackStackChangedListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.app.FragmentManager.OnBackStackChangedListener">
+</parameter>
+</method>
+<field name="POP_BACK_STACK_INCLUSIVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
+<interface name="FragmentManager.BackStackEntry"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getBreadCrumbShortTitle"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getBreadCrumbTitle"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getId"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
+<interface name="FragmentManager.OnBackStackChangedListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onBackStackChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
+<interface name="FragmentTransaction"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="add"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
+<method name="add"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="containerViewId" type="int">
+</parameter>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+</method>
+<method name="add"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="containerViewId" type="int">
+</parameter>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
+<method name="addToBackStack"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
+<method name="commit"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="disallowAddToBackStack"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hide"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+</method>
+<method name="isAddToBackStackAllowed"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isEmpty"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="remove"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+</method>
+<method name="replace"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="containerViewId" type="int">
+</parameter>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+</method>
+<method name="replace"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="containerViewId" type="int">
+</parameter>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
+<method name="setBreadCrumbShortTitle"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="res" type="int">
+</parameter>
+</method>
+<method name="setBreadCrumbShortTitle"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setBreadCrumbTitle"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="res" type="int">
+</parameter>
+</method>
+<method name="setBreadCrumbTitle"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setCustomAnimations"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enter" type="int">
+</parameter>
+<parameter name="exit" type="int">
+</parameter>
+</method>
+<method name="setTransition"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transit" type="int">
+</parameter>
+</method>
+<method name="setTransitionStyle"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="styleRes" type="int">
+</parameter>
+</method>
+<method name="show"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+</method>
+<field name="TRANSIT_ENTER_MASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4096"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TRANSIT_EXIT_MASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8192"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TRANSIT_FRAGMENT_CLOSE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8194"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TRANSIT_FRAGMENT_NEXT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4099"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TRANSIT_FRAGMENT_OPEN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4097"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TRANSIT_FRAGMENT_PREV"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8196"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TRANSIT_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TRANSIT_UNSET"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
 <class name="Instrumentation"
  extends="java.lang.Object"
  abstract="false"
@@ -23579,6 +30136,373 @@
 </parameter>
 </method>
 </class>
+<class name="ListFragment"
+ extends="android.app.Fragment"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ListFragment"
+ type="android.app.ListFragment"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getListAdapter"
+ return="android.widget.ListAdapter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getListView"
+ return="android.widget.ListView"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedItemId"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedItemPosition"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onListItemClick"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="l" type="android.widget.ListView">
+</parameter>
+<parameter name="v" type="android.view.View">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+<parameter name="id" type="long">
+</parameter>
+</method>
+<method name="setEmptyText"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setListAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.widget.ListAdapter">
+</parameter>
+</method>
+<method name="setListShown"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="shown" type="boolean">
+</parameter>
+</method>
+<method name="setListShownNoAnimation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="shown" type="boolean">
+</parameter>
+</method>
+<method name="setSelection"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+</class>
+<interface name="LoaderManager"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getLoader"
+ return="android.content.Loader&lt;D&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
+<method name="initLoader"
+ return="android.content.Loader&lt;D&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+<parameter name="callback" type="android.app.LoaderManager.LoaderCallbacks&lt;D&gt;">
+</parameter>
+</method>
+<method name="restartLoader"
+ return="android.content.Loader&lt;D&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+<parameter name="callback" type="android.app.LoaderManager.LoaderCallbacks&lt;D&gt;">
+</parameter>
+</method>
+<method name="stopLoader"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
+</interface>
+<interface name="LoaderManager.LoaderCallbacks"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onCreateLoader"
+ return="android.content.Loader&lt;D&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onLoadFinished"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="loader" type="android.content.Loader&lt;D&gt;">
+</parameter>
+<parameter name="data" type="D">
+</parameter>
+</method>
+</interface>
+<class name="LoaderManagingFragment"
+ extends="android.app.Fragment"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.content.Loader.OnLoadCompleteListener">
+</implements>
+<constructor name="LoaderManagingFragment"
+ type="android.app.LoaderManagingFragment"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getLoader"
+ return="android.content.Loader&lt;D&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
+<method name="onCreateLoader"
+ return="android.content.Loader&lt;D&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onInitializeLoaders"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
+<method name="onLoadComplete"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="loader" type="android.content.Loader&lt;D&gt;">
+</parameter>
+<parameter name="data" type="D">
+</parameter>
+</method>
+<method name="onLoadFinished"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="loader" type="android.content.Loader&lt;D&gt;">
+</parameter>
+<parameter name="data" type="D">
+</parameter>
+</method>
+<method name="startLoading"
+ return="android.content.Loader&lt;D&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="stopLoading"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
+</class>
 <class name="LocalActivityManager"
  extends="java.lang.Object"
  abstract="false"
@@ -24062,6 +30986,17 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_HIGH_PRIORITY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="128"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FLAG_INSISTENT"
  type="int"
  transient="false"
@@ -24268,6 +31203,27 @@
  visibility="public"
 >
 </field>
+<field name="tickerIcons"
+ type="android.graphics.Bitmap[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="tickerSubtitle"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="tickerText"
  type="java.lang.CharSequence"
  transient="false"
@@ -24278,6 +31234,16 @@
  visibility="public"
 >
 </field>
+<field name="tickerTitle"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="vibrate"
  type="long[]"
  transient="false"
@@ -25252,6 +32218,17 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_QUERY_REFINEMENT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="INTENT_ACTION_GLOBAL_SEARCH"
  type="java.lang.String"
  transient="false"
@@ -25351,6 +32328,17 @@
  visibility="public"
 >
 </field>
+<field name="SUGGEST_COLUMN_FLAGS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;suggest_flags&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SUGGEST_COLUMN_FORMAT"
  type="java.lang.String"
  transient="false"
@@ -27292,6 +34280,17 @@
  visibility="public"
 >
 </field>
+<field name="USES_POLICY_SETS_GLOBAL_PROXY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="USES_POLICY_WATCH_LOGIN"
  type="int"
  transient="false"
@@ -27581,6 +34580,17 @@
  visibility="public"
 >
 </method>
+<method name="getGlobalProxyAdmin"
+ return="android.content.ComponentName"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getMaximumFailedPasswordsForWipe"
  return="int"
  abstract="false"
@@ -27607,6 +34617,19 @@
 <parameter name="admin" type="android.content.ComponentName">
 </parameter>
 </method>
+<method name="getPasswordHistoryLength"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+</method>
 <method name="getPasswordMaximumLength"
  return="int"
  abstract="false"
@@ -27633,6 +34656,84 @@
 <parameter name="admin" type="android.content.ComponentName">
 </parameter>
 </method>
+<method name="getPasswordMinimumLetters"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+</method>
+<method name="getPasswordMinimumLowerCase"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+</method>
+<method name="getPasswordMinimumNonLetter"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+</method>
+<method name="getPasswordMinimumNumeric"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+</method>
+<method name="getPasswordMinimumSymbols"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+</method>
+<method name="getPasswordMinimumUpperCase"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+</method>
 <method name="getPasswordQuality"
  return="int"
  abstract="false"
@@ -27709,6 +34810,23 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
+<method name="setGlobalProxy"
+ return="android.content.ComponentName"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="proxySpec" type="java.net.Proxy">
+</parameter>
+<parameter name="exclusionList" type="java.util.List&lt;java.lang.String&gt;">
+</parameter>
+</method>
 <method name="setMaximumFailedPasswordsForWipe"
  return="void"
  abstract="false"
@@ -27739,6 +34857,21 @@
 <parameter name="timeMs" type="long">
 </parameter>
 </method>
+<method name="setPasswordHistoryLength"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+</method>
 <method name="setPasswordMinimumLength"
  return="void"
  abstract="false"
@@ -27754,6 +34887,96 @@
 <parameter name="length" type="int">
 </parameter>
 </method>
+<method name="setPasswordMinimumLetters"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+</method>
+<method name="setPasswordMinimumLowerCase"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+</method>
+<method name="setPasswordMinimumNonLetter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+</method>
+<method name="setPasswordMinimumNumeric"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+</method>
+<method name="setPasswordMinimumSymbols"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+</method>
+<method name="setPasswordMinimumUpperCase"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="admin" type="android.content.ComponentName">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+</method>
 <method name="setPasswordQuality"
  return="void"
  abstract="false"
@@ -27848,6 +35071,17 @@
  visibility="public"
 >
 </field>
+<field name="PASSWORD_QUALITY_COMPLEX"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="393216"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="PASSWORD_QUALITY_NUMERIC"
  type="int"
  transient="false"
@@ -28842,6 +36076,66 @@
 <parameter name="context" type="android.content.Context">
 </parameter>
 </method>
+<method name="notifyAppWidgetViewDataChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="appWidgetIds" type="int[]">
+</parameter>
+<parameter name="viewId" type="int">
+</parameter>
+</method>
+<method name="notifyAppWidgetViewDataChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="appWidgetId" type="int">
+</parameter>
+<parameter name="viewId" type="int">
+</parameter>
+</method>
+<method name="partiallyUpdateAppWidget"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="appWidgetIds" type="int[]">
+</parameter>
+<parameter name="views" type="android.widget.RemoteViews">
+</parameter>
+</method>
+<method name="partiallyUpdateAppWidget"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="appWidgetId" type="int">
+</parameter>
+<parameter name="views" type="android.widget.RemoteViews">
+</parameter>
+</method>
 <method name="updateAppWidget"
  return="void"
  abstract="false"
@@ -29258,6 +36552,111 @@
 </package>
 <package name="android.bluetooth"
 >
+<class name="BluetoothA2dp"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.bluetooth.BluetoothProfile">
+</implements>
+<method name="getConnectedDevices"
+ return="java.util.List&lt;android.bluetooth.BluetoothDevice&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getConnectionState"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<method name="getDevicesMatchingConnectionStates"
+ return="java.util.List&lt;android.bluetooth.BluetoothDevice&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="states" type="int[]">
+</parameter>
+</method>
+<method name="isA2dpPlaying"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<field name="ACTION_CONNECTION_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_PLAYING_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_NOT_PLAYING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="11"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_PLAYING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="BluetoothAdapter"
  extends="java.lang.Object"
  abstract="false"
@@ -29290,6 +36689,21 @@
 <parameter name="address" type="java.lang.String">
 </parameter>
 </method>
+<method name="closeProfileProxy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="profile" type="int">
+</parameter>
+<parameter name="proxy" type="android.bluetooth.BluetoothProfile">
+</parameter>
+</method>
 <method name="disable"
  return="boolean"
  abstract="false"
@@ -29356,6 +36770,23 @@
  visibility="public"
 >
 </method>
+<method name="getProfileProxy"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="listener" type="android.bluetooth.BluetoothProfile.ServiceListener">
+</parameter>
+<parameter name="profile" type="int">
+</parameter>
+</method>
 <method name="getRemoteDevice"
  return="android.bluetooth.BluetoothDevice"
  abstract="false"
@@ -29454,6 +36885,17 @@
  visibility="public"
 >
 </method>
+<field name="ACTION_CONNECTION_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_DISCOVERY_FINISHED"
  type="java.lang.String"
  transient="false"
@@ -29542,6 +36984,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_CONNECTION_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.adapter.extra.CONNECTION_STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EXTRA_DISCOVERABLE_DURATION"
  type="java.lang.String"
  transient="false"
@@ -29564,6 +37017,17 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_PREVIOUS_CONNECTION_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EXTRA_PREVIOUS_SCAN_MODE"
  type="java.lang.String"
  transient="false"
@@ -29641,6 +37105,50 @@
  visibility="public"
 >
 </field>
+<field name="STATE_CONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_CONNECTING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_DISCONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_DISCONNECTING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="STATE_OFF"
  type="int"
  transient="false"
@@ -29686,6 +37194,1082 @@
 >
 </field>
 </class>
+<class name="BluetoothAssignedNumbers"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="ACCEL_SEMICONDUCTOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="74"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ALCATEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="36"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="APPLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="76"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="APT_LICENSING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="79"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ATHEROS_COMMUNICATIONS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="69"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ATMEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="19"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AVAGO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="78"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AVM_BERLIN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="31"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="BANDSPEED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="BELKIN_INTERNATIONAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="92"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="BLUEGIGA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="71"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="BLUETOOTH_SIG"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="63"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="BROADCOM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="15"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CAMBRIDGE_SILICON_RADIO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CATC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="52"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="COMMIL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="51"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONEXANT_SYSTEMS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="28"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTINENTAL_AUTOMOTIVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="75"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONWISE_TECHNOLOGY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="66"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="C_TECHNOLOGIES"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="38"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIGIANSWER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="12"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ECLIPSE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="53"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EM_MICROELECTRONIC_MARIN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="90"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERICSSON_TECHNOLOGY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FREE2MOVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="83"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="GCT_SEMICONDUCTOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="45"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="GENNUM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="59"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="HARMAN_INTERNATIONAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="87"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="HITACHI"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="41"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IBM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INFINEON_TECHNOLOGIES"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INTEGRATED_SILICON_SOLUTION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="65"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INTEGRATED_SYSTEM_SOLUTION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="57"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INTEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INVENTEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="30"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IPEXTREME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="61"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="J_AND_M"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="82"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KC_TECHNOLOGY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="22"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="LUCENT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MACRONIX"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="44"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MANSELLA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="33"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MARVELL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="72"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MATSUSHITA_ELECTRIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="58"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MEDIATEK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="70"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MEWTEL_TECHNOLOGY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="47"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MICROSOFT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MITEL_SEMICONDUCTOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MITSUBISHI_ELECTRIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="20"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MOBILIAN_CORPORATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="55"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MOTOROLA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NEC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="34"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NEWLOGIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="23"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NOKIA_MOBILE_PHONES"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NORDIC_SEMICONDUCTOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="89"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NORWOOD_SYSTEMS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="46"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OPEN_INTERFACE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="39"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARROT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="67"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARTHUS_TECHNOLOGIES"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="14"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHILIPS_SEMICONDUCTORS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="37"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PLANTRONICS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="85"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="QUALCOMM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="29"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RALINK_TECHNOLOGY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="91"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="REALTEK_SEMICONDUCTOR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="93"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RED_M"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="50"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RENESAS_TECHNOLOGY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="54"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RESEARCH_IN_MOTION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="60"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RF_MICRO_DEVICES"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="40"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RIVIERAWAVES"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="96"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ROHDE_AND_SCHWARZ"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="25"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RTX_TELECOM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="21"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SEIKO_EPSON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="64"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SIGNIA_TECHNOLOGIES"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="27"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SILICON_WAVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="11"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SIRF_TECHNOLOGY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="80"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SOCKET_MOBILE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="68"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SONY_ERICSSON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="86"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STACCATO_COMMUNICATIONS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="77"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STONESTREET_ONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="94"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ST_MICROELECTRONICS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="48"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SYMBOL_TECHNOLOGIES"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="42"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SYNOPSYS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="49"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SYSTEMS_AND_CHIPS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="62"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TENOVIS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="43"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TERAX"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="56"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TEXAS_INSTRUMENTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="13"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THREECOM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THREE_DIJOY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="84"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="THREE_DSP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="73"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TOSHIBA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TRANSILICA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="24"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TTPCOM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="26"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TZERO_TECHNOLOGIES"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="81"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="VIZIO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="88"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WAVEPLUS_TECHNOLOGY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="35"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WICENTRIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="95"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WIDCOMM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ZEEVO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="18"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="BluetoothClass"
  extends="java.lang.Object"
  abstract="false"
@@ -30882,6 +39466,416 @@
 >
 </field>
 </class>
+<class name="BluetoothHeadset"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.bluetooth.BluetoothProfile">
+</implements>
+<method name="getConnectedDevices"
+ return="java.util.List&lt;android.bluetooth.BluetoothDevice&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getConnectionState"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<method name="getDevicesMatchingConnectionStates"
+ return="java.util.List&lt;android.bluetooth.BluetoothDevice&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="states" type="int[]">
+</parameter>
+</method>
+<method name="isAudioConnected"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<method name="startVoiceRecognition"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<method name="stopVoiceRecognition"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<field name="ACTION_AUDIO_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_CONNECTION_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_VENDOR_SPECIFIC_HEADSET_EVENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AT_CMD_TYPE_ACTION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AT_CMD_TYPE_BASIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AT_CMD_TYPE_READ"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AT_CMD_TYPE_SET"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AT_CMD_TYPE_TEST"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_AUDIO_CONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_AUDIO_DISCONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="11"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.headset.intent.category.companyid&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="BluetoothProfile"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getConnectedDevices"
+ return="java.util.List&lt;android.bluetooth.BluetoothDevice&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getConnectionState"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.bluetooth.BluetoothDevice">
+</parameter>
+</method>
+<method name="getDevicesMatchingConnectionStates"
+ return="java.util.List&lt;android.bluetooth.BluetoothDevice&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="states" type="int[]">
+</parameter>
+</method>
+<field name="A2DP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_PREVIOUS_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.profile.extra.PREVIOUS_STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.profile.extra.STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="HEADSET"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_CONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_CONNECTING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_DISCONNECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_DISCONNECTING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
+<interface name="BluetoothProfile.ServiceListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onServiceConnected"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="profile" type="int">
+</parameter>
+<parameter name="proxy" type="android.bluetooth.BluetoothProfile">
+</parameter>
+</method>
+<method name="onServiceDisconnected"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="profile" type="int">
+</parameter>
+</method>
+</interface>
 <class name="BluetoothServerSocket"
  extends="java.lang.Object"
  abstract="false"
@@ -31453,6 +40447,71 @@
 </parameter>
 </constructor>
 </class>
+<class name="AsyncTaskLoader"
+ extends="android.content.Loader"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AsyncTaskLoader"
+ type="android.content.AsyncTaskLoader"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<method name="cancelLoad"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="forceLoad"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="loadInBackground"
+ return="D"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onCancelled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="D">
+</parameter>
+</method>
+</class>
 <class name="BroadcastReceiver"
  extends="java.lang.Object"
  abstract="true"
@@ -31548,6 +40607,17 @@
 <parameter name="makeMap" type="boolean">
 </parameter>
 </method>
+<method name="goAsync"
+ return="android.content.BroadcastReceiver.PendingResult"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isInitialStickyBroadcast"
  return="boolean"
  abstract="false"
@@ -31683,6 +40753,768 @@
 </parameter>
 </method>
 </class>
+<class name="BroadcastReceiver.PendingResult"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="abortBroadcast"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="clearAbortBroadcast"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="finish"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAbortBroadcast"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getResultCode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getResultData"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getResultExtras"
+ return="android.os.Bundle"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="makeMap" type="boolean">
+</parameter>
+</method>
+<method name="setResult"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="code" type="int">
+</parameter>
+<parameter name="data" type="java.lang.String">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="setResultCode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="code" type="int">
+</parameter>
+</method>
+<method name="setResultData"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="java.lang.String">
+</parameter>
+</method>
+<method name="setResultExtras"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+</method>
+</class>
+<class name="ClipData"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="ClipData"
+ type="android.content.ClipData"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+<parameter name="mimeTypes" type="java.lang.String[]">
+</parameter>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+<parameter name="item" type="android.content.ClipData.Item">
+</parameter>
+</constructor>
+<constructor name="ClipData"
+ type="android.content.ClipData"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="description" type="android.content.ClipDescription">
+</parameter>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+<parameter name="item" type="android.content.ClipData.Item">
+</parameter>
+</constructor>
+<method name="addItem"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="item" type="android.content.ClipData.Item">
+</parameter>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDescription"
+ return="android.content.ClipDescription"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getIcon"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getItem"
+ return="android.content.ClipData.Item"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
+<method name="getItemCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newIntent"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="newPlainText"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="newRawUri"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="newUri"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="resolver" type="android.content.ContentResolver">
+</parameter>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="ClipData.Item"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ClipData.Item"
+ type="android.content.ClipData.Item"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</constructor>
+<constructor name="ClipData.Item"
+ type="android.content.ClipData.Item"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</constructor>
+<constructor name="ClipData.Item"
+ type="android.content.ClipData.Item"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</constructor>
+<constructor name="ClipData.Item"
+ type="android.content.ClipData.Item"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</constructor>
+<method name="coerceToText"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="getIntent"
+ return="android.content.Intent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getText"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="ClipDescription"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="ClipDescription"
+ type="android.content.ClipDescription"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+<parameter name="mimeTypes" type="java.lang.String[]">
+</parameter>
+</constructor>
+<constructor name="ClipDescription"
+ type="android.content.ClipDescription"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="o" type="android.content.ClipDescription">
+</parameter>
+</constructor>
+<method name="compareMimeTypes"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="concreteType" type="java.lang.String">
+</parameter>
+<parameter name="desiredType" type="java.lang.String">
+</parameter>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="filterMimeTypes"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</method>
+<method name="getLabel"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMimeType"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
+<method name="getMimeTypeCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasMimeType"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MIMETYPE_TEXT_INTENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;text/vnd.android.intent&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MIMETYPE_TEXT_PLAIN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;text/plain&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MIMETYPE_TEXT_URILIST"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;text/uri-list&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="ClipboardManager"
+ extends="android.text.ClipboardManager"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="addPrimaryClipChangedListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="what" type="android.content.ClipboardManager.OnPrimaryClipChangedListener">
+</parameter>
+</method>
+<method name="getPrimaryClip"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPrimaryClipDescription"
+ return="android.content.ClipDescription"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getText"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasPrimaryClip"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasText"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+</method>
+<method name="removePrimaryClipChangedListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="what" type="android.content.ClipboardManager.OnPrimaryClipChangedListener">
+</parameter>
+</method>
+<method name="setPrimaryClip"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="clip" type="android.content.ClipData">
+</parameter>
+</method>
+<method name="setText"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
+</class>
+<interface name="ClipboardManager.OnPrimaryClipChangedListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onPrimaryClipChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
 <interface name="ComponentCallbacks"
  abstract="true"
  static="false"
@@ -32056,6 +41888,21 @@
  visibility="public"
 >
 </method>
+<method name="getStreamTypes"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeTypeFilter" type="java.lang.String">
+</parameter>
+</method>
 <method name="getType"
  return="java.lang.String"
  abstract="true"
@@ -32192,6 +42039,48 @@
 <exception name="FileNotFoundException" type="java.io.FileNotFoundException">
 </exception>
 </method>
+<method name="openPipeHelper"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+<parameter name="opts" type="android.os.Bundle">
+</parameter>
+<parameter name="args" type="T">
+</parameter>
+<parameter name="func" type="android.content.ContentProvider.PipeDataWriter&lt;T&gt;">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+</method>
+<method name="openTypedAssetFile"
+ return="android.content.res.AssetFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeTypeFilter" type="java.lang.String">
+</parameter>
+<parameter name="opts" type="android.os.Bundle">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+</method>
 <method name="query"
  return="android.database.Cursor"
  abstract="true"
@@ -32252,6 +42141,17 @@
 <parameter name="permission" type="java.lang.String">
 </parameter>
 </method>
+<method name="shutdown"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="update"
  return="int"
  abstract="true"
@@ -32272,6 +42172,35 @@
 </parameter>
 </method>
 </class>
+<interface name="ContentProvider.PipeDataWriter"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="writeDataToPipe"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="output" type="android.os.ParcelFileDescriptor">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+<parameter name="opts" type="android.os.Bundle">
+</parameter>
+<parameter name="args" type="T">
+</parameter>
+</method>
+</interface>
 <class name="ContentProviderClient"
  extends="java.lang.Object"
  abstract="false"
@@ -32344,6 +42273,23 @@
  visibility="public"
 >
 </method>
+<method name="getStreamTypes"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="mimeTypeFilter" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
 <method name="getType"
  return="java.lang.String"
  abstract="false"
@@ -32414,6 +42360,27 @@
 <exception name="RemoteException" type="android.os.RemoteException">
 </exception>
 </method>
+<method name="openTypedAssetFileDescriptor"
+ return="android.content.res.AssetFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+<parameter name="opts" type="android.os.Bundle">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
 <method name="query"
  return="android.database.Cursor"
  abstract="false"
@@ -33139,6 +43106,17 @@
  synchronized="false"
  static="true"
  final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCurrentSyncs"
+ return="java.util.List&lt;android.content.SyncInfo&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -33184,6 +43162,21 @@
 <parameter name="authority" type="java.lang.String">
 </parameter>
 </method>
+<method name="getStreamTypes"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="mimeTypeFilter" type="java.lang.String">
+</parameter>
+</method>
 <method name="getSyncAdapterTypes"
  return="android.content.SyncAdapterType[]"
  abstract="false"
@@ -33381,6 +43374,25 @@
 <exception name="FileNotFoundException" type="java.io.FileNotFoundException">
 </exception>
 </method>
+<method name="openTypedAssetFileDescriptor"
+ return="android.content.res.AssetFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+<parameter name="opts" type="android.os.Bundle">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+</method>
 <method name="query"
  return="android.database.Cursor"
  abstract="false"
@@ -34046,6 +44058,17 @@
 <parameter name="key" type="java.lang.String">
 </parameter>
 </method>
+<method name="keySet"
+ return="java.util.Set&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="put"
  return="void"
  abstract="false"
@@ -35096,6 +45119,25 @@
 <parameter name="factory" type="android.database.sqlite.SQLiteDatabase.CursorFactory">
 </parameter>
 </method>
+<method name="openOrCreateDatabase"
+ return="android.database.sqlite.SQLiteDatabase"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="mode" type="int">
+</parameter>
+<parameter name="factory" type="android.database.sqlite.SQLiteDatabase.CursorFactory">
+</parameter>
+<parameter name="errorHandler" type="android.database.DatabaseErrorHandler">
+</parameter>
+</method>
 <method name="peekWallpaper"
  return="android.graphics.drawable.Drawable"
  abstract="true"
@@ -36534,6 +46576,25 @@
 <parameter name="factory" type="android.database.sqlite.SQLiteDatabase.CursorFactory">
 </parameter>
 </method>
+<method name="openOrCreateDatabase"
+ return="android.database.sqlite.SQLiteDatabase"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="mode" type="int">
+</parameter>
+<parameter name="factory" type="android.database.sqlite.SQLiteDatabase.CursorFactory">
+</parameter>
+<parameter name="errorHandler" type="android.database.DatabaseErrorHandler">
+</parameter>
+</method>
 <method name="peekWallpaper"
  return="android.graphics.drawable.Drawable"
  abstract="false"
@@ -36860,6 +46921,240 @@
 </parameter>
 </method>
 </class>
+<class name="CursorLoader"
+ extends="android.content.AsyncTaskLoader"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="CursorLoader"
+ type="android.content.CursorLoader"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="projection" type="java.lang.String[]">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="sortOrder" type="java.lang.String">
+</parameter>
+</constructor>
+<method name="deliverResult"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+</method>
+<method name="destroy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getProjection"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelection"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectionArgs"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSortOrder"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="loadInBackground"
+ return="android.database.Cursor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onCancelled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+</method>
+<method name="registerContentObserver"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+<parameter name="observer" type="android.database.ContentObserver">
+</parameter>
+</method>
+<method name="setProjection"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="projection" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="setSelection"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+</method>
+<method name="setSelectionArgs"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="setSortOrder"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sortOrder" type="java.lang.String">
+</parameter>
+</method>
+<method name="setUri"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="startLoading"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="stopLoading"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
 <interface name="DialogInterface"
  abstract="true"
  static="false"
@@ -39439,6 +49734,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_PASTE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.PASTE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_PICK"
  type="java.lang.String"
  transient="false"
@@ -39780,6 +50086,17 @@
  visibility="public"
 >
 </field>
+<field name="CATEGORY_APP_MARKET"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.category.APP_MARKET&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CATEGORY_BROWSABLE"
  type="java.lang.String"
  transient="false"
@@ -39868,6 +50185,17 @@
  visibility="public"
 >
 </field>
+<field name="CATEGORY_HE_DESK_DOCK"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.category.HE_DESK_DOCK&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CATEGORY_HOME"
  type="java.lang.String"
  transient="false"
@@ -39901,6 +50229,17 @@
  visibility="public"
 >
 </field>
+<field name="CATEGORY_LE_DESK_DOCK"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.category.LE_DESK_DOCK&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CATEGORY_MONKEY"
  type="java.lang.String"
  transient="false"
@@ -40120,6 +50459,28 @@
  visibility="public"
 >
 </field>
+<field name="EXTRA_DOCK_STATE_HE_DESK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_DOCK_STATE_LE_DESK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EXTRA_DOCK_STATE_UNDOCKED"
  type="int"
  transient="false"
@@ -41837,6 +52198,183 @@
 </parameter>
 </constructor>
 </class>
+<class name="Loader"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Loader"
+ type="android.content.Loader"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<method name="deliverResult"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="D">
+</parameter>
+</method>
+<method name="destroy"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="forceLoad"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getContext"
+ return="android.content.Context"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onContentChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="registerListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="listener" type="android.content.Loader.OnLoadCompleteListener&lt;D&gt;">
+</parameter>
+</method>
+<method name="startLoading"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="stopLoading"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="unregisterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.content.Loader.OnLoadCompleteListener&lt;D&gt;">
+</parameter>
+</method>
+</class>
+<class name="Loader.ForceLoadContentObserver"
+ extends="android.database.ContentObserver"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Loader.ForceLoadContentObserver"
+ type="android.content.Loader.ForceLoadContentObserver"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+</class>
+<interface name="Loader.OnLoadCompleteListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onLoadComplete"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="loader" type="android.content.Loader&lt;D&gt;">
+</parameter>
+<parameter name="data" type="D">
+</parameter>
+</method>
+</interface>
 <class name="MutableContextWrapper"
  extends="android.content.ContextWrapper"
  abstract="false"
@@ -42376,6 +52914,21 @@
 <parameter name="defValue" type="java.lang.String">
 </parameter>
 </method>
+<method name="getStringSet"
+ return="java.util.Set&lt;java.lang.String&gt;"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="defValues" type="java.util.Set&lt;java.lang.String&gt;">
+</parameter>
+</method>
 <method name="registerOnSharedPreferenceChangeListener"
  return="void"
  abstract="true"
@@ -42518,6 +53071,21 @@
 <parameter name="value" type="java.lang.String">
 </parameter>
 </method>
+<method name="putStringSet"
+ return="android.content.SharedPreferences.Editor"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="values" type="java.util.Set&lt;java.lang.String&gt;">
+</parameter>
+</method>
 <method name="remove"
  return="android.content.SharedPreferences.Editor"
  abstract="true"
@@ -42736,6 +53304,34 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parcel" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
 <field name="account"
  type="android.accounts.Account"
  transient="false"
@@ -43235,6 +53831,145 @@
 >
 </field>
 </class>
+<class name="XmlDocumentProvider"
+ extends="android.content.ContentProvider"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="XmlDocumentProvider"
+ type="android.content.XmlDocumentProvider"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="delete"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="getResourceXmlPullParser"
+ return="org.xmlpull.v1.XmlPullParser"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="resourceUri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="getType"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="getUriXmlPullParser"
+ return="org.xmlpull.v1.XmlPullParser"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="url" type="java.lang.String">
+</parameter>
+</method>
+<method name="insert"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+</method>
+<method name="onCreate"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="query"
+ return="android.database.Cursor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="projection" type="java.lang.String[]">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="sortOrder" type="java.lang.String">
+</parameter>
+</method>
+<method name="update"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+</method>
+</class>
 </package>
 <package name="android.content.pm"
 >
@@ -43500,6 +54235,28 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_HARDWARE_ACCELERATED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1024"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_IMMERSIVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="512"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FLAG_MULTIPROCESS"
  type="int"
  transient="false"
@@ -44042,6 +54799,17 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_SUPPORTS_XLARGE_SCREENS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="524288"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FLAG_SYSTEM"
  type="int"
  transient="false"
@@ -51188,6 +61956,17 @@
 <parameter name="column" type="int">
 </parameter>
 </method>
+<method name="getNotificationUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getPosition"
  return="int"
  abstract="false"
@@ -51225,6 +62004,19 @@
 <parameter name="column" type="int">
 </parameter>
 </method>
+<method name="getType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="column" type="int">
+</parameter>
+</method>
 <method name="getUpdatedField"
  return="java.lang.Object"
  abstract="false"
@@ -51232,7 +62024,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="protected"
 >
 <parameter name="columnIndex" type="int">
@@ -51300,7 +62092,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="protected"
 >
 <parameter name="columnIndex" type="int">
@@ -51586,7 +62378,7 @@
  volatile="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="protected"
 >
 </field>
@@ -51722,7 +62514,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="columnIndex" type="int">
@@ -51735,7 +62527,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="columnIndex" type="int">
@@ -51748,7 +62540,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="columnIndex" type="int">
@@ -51774,7 +62566,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="columnIndex" type="int">
@@ -52248,6 +63040,19 @@
 <parameter name="columnIndex" type="int">
 </parameter>
 </method>
+<method name="getType"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
 <method name="getWantsAllOnMoveCalls"
  return="boolean"
  abstract="true"
@@ -52430,7 +63235,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -52488,6 +63293,61 @@
 <parameter name="observer" type="android.database.DataSetObserver">
 </parameter>
 </method>
+<field name="FIELD_TYPE_BLOB"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FIELD_TYPE_FLOAT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FIELD_TYPE_INTEGER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FIELD_TYPE_NULL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FIELD_TYPE_STRING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </interface>
 <class name="CursorIndexOutOfBoundsException"
  extends="java.lang.IndexOutOfBoundsException"
@@ -52845,6 +63705,21 @@
 <parameter name="col" type="int">
 </parameter>
 </method>
+<method name="getType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="row" type="int">
+</parameter>
+<parameter name="col" type="int">
+</parameter>
+</method>
 <method name="isBlob"
  return="boolean"
  abstract="false"
@@ -52852,7 +63727,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="row" type="int">
@@ -52867,7 +63742,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="row" type="int">
@@ -52882,7 +63757,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="row" type="int">
@@ -52897,7 +63772,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="row" type="int">
@@ -52912,7 +63787,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="row" type="int">
@@ -53323,6 +64198,19 @@
 <parameter name="columnIndex" type="int">
 </parameter>
 </method>
+<method name="getType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
 <method name="getWantsAllOnMoveCalls"
  return="boolean"
  abstract="false"
@@ -53334,6 +64222,17 @@
  visibility="public"
 >
 </method>
+<method name="getWrappedCursor"
+ return="android.database.Cursor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isAfterLast"
  return="boolean"
  abstract="false"
@@ -53642,6 +64541,27 @@
 >
 </method>
 </class>
+<interface name="DatabaseErrorHandler"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onCorruption"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dbObj" type="android.database.sqlite.SQLiteDatabase">
+</parameter>
+</method>
+</interface>
 <class name="DatabaseUtils"
  extends="java.lang.Object"
  abstract="false"
@@ -53673,6 +64593,21 @@
 <parameter name="sqlString" type="java.lang.String">
 </parameter>
 </method>
+<method name="appendSelectionArgs"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="originalValues" type="java.lang.String[]">
+</parameter>
+<parameter name="newValues" type="java.lang.String[]">
+</parameter>
+</method>
 <method name="appendValueToSql"
  return="void"
  abstract="false"
@@ -53705,6 +64640,38 @@
 <parameter name="value" type="java.lang.Object">
 </parameter>
 </method>
+<method name="blobFileDescriptorForQuery"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="db" type="android.database.sqlite.SQLiteDatabase">
+</parameter>
+<parameter name="query" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="blobFileDescriptorForQuery"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="prog" type="android.database.sqlite.SQLiteStatement">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+</method>
 <method name="createDbFromSqlStatements"
  return="void"
  abstract="false"
@@ -54142,6 +65109,19 @@
 <parameter name="name" type="java.lang.String">
 </parameter>
 </method>
+<method name="getSqlStatementType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sql" type="java.lang.String">
+</parameter>
+</method>
 <method name="longForQuery"
  return="long"
  abstract="false"
@@ -54328,6 +65308,116 @@
 <parameter name="e" type="java.lang.Exception">
 </parameter>
 </method>
+<field name="STATEMENT_ABORT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_ATTACH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_BEGIN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_COMMIT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_DDL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_OTHER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="99"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_PRAGMA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_SELECT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_UNPREPARED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATEMENT_UPDATE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="DatabaseUtils.InsertHelper"
  extends="java.lang.Object"
@@ -54562,6 +65652,38 @@
 >
 </field>
 </class>
+<class name="DefaultDatabaseErrorHandler"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.database.DatabaseErrorHandler">
+</implements>
+<constructor name="DefaultDatabaseErrorHandler"
+ type="android.database.DefaultDatabaseErrorHandler"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onCorruption"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dbObj" type="android.database.sqlite.SQLiteDatabase">
+</parameter>
+</method>
+</class>
 <class name="MatrixCursor"
  extends="android.database.AbstractCursor"
  abstract="false"
@@ -55045,6 +66167,114 @@
 </parameter>
 </constructor>
 </class>
+<class name="SQLiteAccessPermException"
+ extends="android.database.sqlite.SQLiteException"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SQLiteAccessPermException"
+ type="android.database.sqlite.SQLiteAccessPermException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="SQLiteAccessPermException"
+ type="android.database.sqlite.SQLiteAccessPermException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="error" type="java.lang.String">
+</parameter>
+</constructor>
+</class>
+<class name="SQLiteBindOrColumnIndexOutOfRangeException"
+ extends="android.database.sqlite.SQLiteException"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SQLiteBindOrColumnIndexOutOfRangeException"
+ type="android.database.sqlite.SQLiteBindOrColumnIndexOutOfRangeException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="SQLiteBindOrColumnIndexOutOfRangeException"
+ type="android.database.sqlite.SQLiteBindOrColumnIndexOutOfRangeException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="error" type="java.lang.String">
+</parameter>
+</constructor>
+</class>
+<class name="SQLiteBlobTooBigException"
+ extends="android.database.sqlite.SQLiteException"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SQLiteBlobTooBigException"
+ type="android.database.sqlite.SQLiteBlobTooBigException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="SQLiteBlobTooBigException"
+ type="android.database.sqlite.SQLiteBlobTooBigException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="error" type="java.lang.String">
+</parameter>
+</constructor>
+</class>
+<class name="SQLiteCantOpenDatabaseException"
+ extends="android.database.sqlite.SQLiteException"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SQLiteCantOpenDatabaseException"
+ type="android.database.sqlite.SQLiteCantOpenDatabaseException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="SQLiteCantOpenDatabaseException"
+ type="android.database.sqlite.SQLiteCantOpenDatabaseException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="error" type="java.lang.String">
+</parameter>
+</constructor>
+</class>
 <class name="SQLiteClosable"
  extends="java.lang.Object"
  abstract="true"
@@ -55156,7 +66386,7 @@
  type="android.database.sqlite.SQLiteCursor"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="db" type="android.database.sqlite.SQLiteDatabase">
@@ -55168,6 +66398,20 @@
 <parameter name="query" type="android.database.sqlite.SQLiteQuery">
 </parameter>
 </constructor>
+<constructor name="SQLiteCursor"
+ type="android.database.sqlite.SQLiteCursor"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="driver" type="android.database.sqlite.SQLiteCursorDriver">
+</parameter>
+<parameter name="editTable" type="java.lang.String">
+</parameter>
+<parameter name="query" type="android.database.sqlite.SQLiteQuery">
+</parameter>
+</constructor>
 <method name="getColumnNames"
  return="java.lang.String[]"
  abstract="false"
@@ -55305,6 +66549,17 @@
  visibility="public"
 >
 </method>
+<method name="beginTransactionNonExclusive"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="beginTransactionWithListener"
  return="void"
  abstract="false"
@@ -55318,6 +66573,19 @@
 <parameter name="transactionListener" type="android.database.sqlite.SQLiteTransactionListener">
 </parameter>
 </method>
+<method name="beginTransactionWithListenerNonExclusive"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transactionListener" type="android.database.sqlite.SQLiteTransactionListener">
+</parameter>
+</method>
 <method name="close"
  return="void"
  abstract="false"
@@ -55374,6 +66642,17 @@
 <parameter name="whereArgs" type="java.lang.String[]">
 </parameter>
 </method>
+<method name="enableWriteAheadLogging"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="endTransaction"
  return="void"
  abstract="false"
@@ -55430,6 +66709,17 @@
 <parameter name="tables" type="java.lang.String">
 </parameter>
 </method>
+<method name="getAttachedDbs"
+ return="java.util.ArrayList&lt;android.util.Pair&lt;java.lang.String, java.lang.String&gt;&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getMaximumSize"
  return="long"
  abstract="false"
@@ -55470,7 +66760,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -55551,6 +66841,17 @@
 <parameter name="conflictAlgorithm" type="int">
 </parameter>
 </method>
+<method name="isDatabaseIntegrityOk"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isDbLockedByCurrentThread"
  return="boolean"
  abstract="false"
@@ -55602,7 +66903,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="table" type="java.lang.String">
@@ -55617,7 +66918,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="table" type="java.lang.String">
@@ -55668,6 +66969,25 @@
 <parameter name="flags" type="int">
 </parameter>
 </method>
+<method name="openDatabase"
+ return="android.database.sqlite.SQLiteDatabase"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="factory" type="android.database.sqlite.SQLiteDatabase.CursorFactory">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+<parameter name="errorHandler" type="android.database.DatabaseErrorHandler">
+</parameter>
+</method>
 <method name="openOrCreateDatabase"
  return="android.database.sqlite.SQLiteDatabase"
  abstract="false"
@@ -55698,6 +67018,23 @@
 <parameter name="factory" type="android.database.sqlite.SQLiteDatabase.CursorFactory">
 </parameter>
 </method>
+<method name="openOrCreateDatabase"
+ return="android.database.sqlite.SQLiteDatabase"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="factory" type="android.database.sqlite.SQLiteDatabase.CursorFactory">
+</parameter>
+<parameter name="errorHandler" type="android.database.DatabaseErrorHandler">
+</parameter>
+</method>
 <method name="query"
  return="android.database.Cursor"
  abstract="false"
@@ -55917,6 +67254,19 @@
 <parameter name="lockingEnabled" type="boolean">
 </parameter>
 </method>
+<method name="setMaxSqlCacheSize"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cacheSize" type="int">
+</parameter>
+</method>
 <method name="setMaximumSize"
  return="long"
  abstract="false"
@@ -56119,6 +67469,17 @@
  visibility="public"
 >
 </field>
+<field name="MAX_SQL_CACHE_SIZE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="100"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="NO_LOCALIZED_COLLATORS"
  type="int"
  transient="false"
@@ -56218,6 +67579,60 @@
 </parameter>
 </constructor>
 </class>
+<class name="SQLiteDatabaseLockedException"
+ extends="android.database.sqlite.SQLiteException"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SQLiteDatabaseLockedException"
+ type="android.database.sqlite.SQLiteDatabaseLockedException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="SQLiteDatabaseLockedException"
+ type="android.database.sqlite.SQLiteDatabaseLockedException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="error" type="java.lang.String">
+</parameter>
+</constructor>
+</class>
+<class name="SQLiteDatatypeMismatchException"
+ extends="android.database.sqlite.SQLiteException"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SQLiteDatatypeMismatchException"
+ type="android.database.sqlite.SQLiteDatatypeMismatchException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="SQLiteDatatypeMismatchException"
+ type="android.database.sqlite.SQLiteDatatypeMismatchException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="error" type="java.lang.String">
+</parameter>
+</constructor>
+</class>
 <class name="SQLiteDiskIOException"
  extends="android.database.sqlite.SQLiteException"
  abstract="false"
@@ -56377,6 +67792,24 @@
 <parameter name="version" type="int">
 </parameter>
 </constructor>
+<constructor name="SQLiteOpenHelper"
+ type="android.database.sqlite.SQLiteOpenHelper"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="factory" type="android.database.sqlite.SQLiteDatabase.CursorFactory">
+</parameter>
+<parameter name="version" type="int">
+</parameter>
+<parameter name="errorHandler" type="android.database.DatabaseErrorHandler">
+</parameter>
+</constructor>
 <method name="close"
  return="void"
  abstract="false"
@@ -56454,6 +67887,33 @@
 </parameter>
 </method>
 </class>
+<class name="SQLiteOutOfMemoryException"
+ extends="android.database.sqlite.SQLiteException"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SQLiteOutOfMemoryException"
+ type="android.database.sqlite.SQLiteOutOfMemoryException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="SQLiteOutOfMemoryException"
+ type="android.database.sqlite.SQLiteOutOfMemoryException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="error" type="java.lang.String">
+</parameter>
+</constructor>
+</class>
 <class name="SQLiteProgram"
  extends="android.database.sqlite.SQLiteClosable"
  abstract="true"
@@ -56462,6 +67922,19 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<method name="bindAllArgsAsStrings"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bindArgs" type="java.lang.String[]">
+</parameter>
+</method>
 <method name="bindBlob"
  return="void"
  abstract="false"
@@ -56579,7 +68052,7 @@
  synchronized="false"
  static="false"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -57002,6 +68475,33 @@
 </parameter>
 </method>
 </class>
+<class name="SQLiteReadOnlyDatabaseException"
+ extends="android.database.sqlite.SQLiteException"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SQLiteReadOnlyDatabaseException"
+ type="android.database.sqlite.SQLiteReadOnlyDatabaseException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="SQLiteReadOnlyDatabaseException"
+ type="android.database.sqlite.SQLiteReadOnlyDatabaseException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="error" type="java.lang.String">
+</parameter>
+</constructor>
+</class>
 <class name="SQLiteStatement"
  extends="android.database.sqlite.SQLiteProgram"
  abstract="false"
@@ -57032,6 +68532,28 @@
  visibility="public"
 >
 </method>
+<method name="executeUpdateDelete"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="simpleQueryForBlobFileDescriptor"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="simpleQueryForLong"
  return="long"
  abstract="false"
@@ -57055,6 +68577,33 @@
 >
 </method>
 </class>
+<class name="SQLiteTableLockedException"
+ extends="android.database.sqlite.SQLiteException"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SQLiteTableLockedException"
+ type="android.database.sqlite.SQLiteTableLockedException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="SQLiteTableLockedException"
+ type="android.database.sqlite.SQLiteTableLockedException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="error" type="java.lang.String">
+</parameter>
+</constructor>
+</class>
 <interface name="SQLiteTransactionListener"
  abstract="true"
  static="false"
@@ -61927,6 +73476,146 @@
 >
 </field>
 </class>
+<class name="BitmapRegionDecoder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="decodeRegion"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rect" type="android.graphics.Rect">
+</parameter>
+<parameter name="options" type="android.graphics.BitmapFactory.Options">
+</parameter>
+</method>
+<method name="getHeight"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getWidth"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isRecycled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newInstance"
+ return="android.graphics.BitmapRegionDecoder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="byte[]">
+</parameter>
+<parameter name="offset" type="int">
+</parameter>
+<parameter name="length" type="int">
+</parameter>
+<parameter name="isShareable" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="newInstance"
+ return="android.graphics.BitmapRegionDecoder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fd" type="java.io.FileDescriptor">
+</parameter>
+<parameter name="isShareable" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="newInstance"
+ return="android.graphics.BitmapRegionDecoder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="is" type="java.io.InputStream">
+</parameter>
+<parameter name="isShareable" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="newInstance"
+ return="android.graphics.BitmapRegionDecoder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pathName" type="java.lang.String">
+</parameter>
+<parameter name="isShareable" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="recycle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
 <class name="BitmapShader"
  extends="android.graphics.Shader"
  abstract="false"
@@ -62168,16 +73857,6 @@
 <parameter name="bitmap" type="android.graphics.Bitmap">
 </parameter>
 </constructor>
-<constructor name="Canvas"
- type="android.graphics.Canvas"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="gl" type="javax.microedition.khronos.opengles.GL">
-</parameter>
-</constructor>
 <method name="clipPath"
  return="boolean"
  abstract="false"
@@ -62951,7 +74630,7 @@
 <method name="drawText"
  return="void"
  abstract="false"
- native="true"
+ native="false"
  synchronized="false"
  static="false"
  final="false"
@@ -63094,17 +74773,6 @@
 <parameter name="paint" type="android.graphics.Paint">
 </parameter>
 </method>
-<method name="freeGlCaches"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getClipBounds"
  return="boolean"
  abstract="false"
@@ -63158,8 +74826,8 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
- visibility="public"
+ deprecated="deprecated"
+ visibility="protected"
 >
 </method>
 <method name="getHeight"
@@ -63219,6 +74887,17 @@
  visibility="public"
 >
 </method>
+<method name="isHardwareAccelerated"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isOpaque"
  return="boolean"
  abstract="false"
@@ -63525,21 +75204,6 @@
 <parameter name="matrix" type="android.graphics.Matrix">
 </parameter>
 </method>
-<method name="setViewport"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="width" type="int">
-</parameter>
-<parameter name="height" type="int">
-</parameter>
-</method>
 <method name="skew"
  return="void"
  abstract="false"
@@ -66937,7 +78601,7 @@
 <method name="setShadowLayer"
  return="void"
  abstract="false"
- native="true"
+ native="false"
  synchronized="false"
  static="false"
  final="false"
@@ -71958,6 +83622,17 @@
  visibility="public"
 >
 </method>
+<method name="getColor"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getOpacity"
  return="int"
  abstract="false"
@@ -71982,6 +83657,19 @@
 <parameter name="alpha" type="int">
 </parameter>
 </method>
+<method name="setColor"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="color" type="int">
+</parameter>
+</method>
 <method name="setColorFilter"
  return="void"
  abstract="false"
@@ -72367,6 +84055,17 @@
  visibility="public"
 >
 </method>
+<method name="jumpToCurrentState"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="mutate"
  return="android.graphics.drawable.Drawable"
  abstract="false"
@@ -72856,6 +84555,32 @@
 <parameter name="state" type="android.graphics.drawable.DrawableContainer.DrawableContainerState">
 </parameter>
 </method>
+<method name="setEnterFadeDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ms" type="int">
+</parameter>
+</method>
+<method name="setExitFadeDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ms" type="int">
+</parameter>
+</method>
 <method name="unscheduleDrawable"
  return="void"
  abstract="false"
@@ -72904,6 +84629,17 @@
  visibility="public"
 >
 </method>
+<method name="computeConstantSize"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
 <method name="getChangingConfigurations"
  return="int"
  abstract="false"
@@ -72992,6 +84728,28 @@
  visibility="public"
 >
 </method>
+<method name="getEnterFadeDuration"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExitFadeDuration"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getOpacity"
  return="int"
  abstract="false"
@@ -73053,6 +84811,32 @@
 <parameter name="constant" type="boolean">
 </parameter>
 </method>
+<method name="setEnterFadeDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="int">
+</parameter>
+</method>
+<method name="setExitFadeDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="int">
+</parameter>
+</method>
 <method name="setVariablePadding"
  return="void"
  abstract="false"
@@ -73798,6 +85582,36 @@
 </parameter>
 </method>
 </class>
+<class name="MipmapDrawable"
+ extends="android.graphics.drawable.DrawableContainer"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="MipmapDrawable"
+ type="android.graphics.drawable.MipmapDrawable"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addDrawable"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="drawable" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+</class>
 <class name="NinePatchDrawable"
  extends="android.graphics.drawable.Drawable"
  abstract="false"
@@ -78088,7 +89902,7 @@
  type="float"
  transient="false"
  volatile="false"
- value="0.0010f"
+ value="0.001f"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -79060,6 +90874,19 @@
  visibility="public"
 >
 </method>
+<method name="onCurrentInputMethodSubtypeChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="newSubtype" type="android.view.inputmethod.InputMethodSubtype">
+</parameter>
+</method>
 <method name="onDisplayCompletions"
  return="void"
  abstract="false"
@@ -79663,6 +91490,19 @@
 <parameter name="binding" type="android.view.inputmethod.InputBinding">
 </parameter>
 </method>
+<method name="changeInputMethodSubtype"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="subtype" type="android.view.inputmethod.InputMethodSubtype">
+</parameter>
+</method>
 <method name="hideSoftInput"
  return="void"
  abstract="false"
@@ -79970,6 +91810,24 @@
 </parameter>
 <parameter name="modeId" type="int">
 </parameter>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+</constructor>
+<constructor name="Keyboard"
+ type="android.inputmethodservice.Keyboard"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="xmlLayoutResId" type="int">
+</parameter>
+<parameter name="modeId" type="int">
+</parameter>
 </constructor>
 <constructor name="Keyboard"
  type="android.inputmethodservice.Keyboard"
@@ -86744,6 +98602,78 @@
 <parameter name="quality" type="int">
 </parameter>
 </method>
+<method name="hasProfile"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="quality" type="int">
+</parameter>
+</method>
+<method name="hasProfile"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cameraId" type="int">
+</parameter>
+<parameter name="quality" type="int">
+</parameter>
+</method>
+<field name="QUALITY_1080P"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="QUALITY_480P"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="QUALITY_720P"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="QUALITY_CIF"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="QUALITY_HIGH"
  type="int"
  transient="false"
@@ -86766,6 +98696,94 @@
  visibility="public"
 >
 </field>
+<field name="QUALITY_QCIF"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="QUALITY_TIME_LAPSE_1080P"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1006"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="QUALITY_TIME_LAPSE_480P"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1004"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="QUALITY_TIME_LAPSE_720P"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1005"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="QUALITY_TIME_LAPSE_CIF"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1003"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="QUALITY_TIME_LAPSE_HIGH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1001"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="QUALITY_TIME_LAPSE_LOW"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="QUALITY_TIME_LAPSE_QCIF"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1002"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="audioBitRate"
  type="int"
  transient="false"
@@ -87203,6 +99221,17 @@
  visibility="public"
 >
 </field>
+<field name="TAG_APERTURE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;FNumber&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TAG_DATETIME"
  type="java.lang.String"
  transient="false"
@@ -87214,6 +99243,17 @@
  visibility="public"
 >
 </field>
+<field name="TAG_EXPOSURE_TIME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;ExposureTime&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TAG_FLASH"
  type="java.lang.String"
  transient="false"
@@ -87357,6 +99397,17 @@
  visibility="public"
 >
 </field>
+<field name="TAG_ISO"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;ISOSpeedRatings&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TAG_MAKE"
  type="java.lang.String"
  transient="false"
@@ -88862,6 +100913,32 @@
 <exception name="IllegalStateException" type="java.lang.IllegalStateException">
 </exception>
 </method>
+<method name="setAuxiliaryOutputFile"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fd" type="java.io.FileDescriptor">
+</parameter>
+</method>
+<method name="setAuxiliaryOutputFile"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+</method>
 <method name="setCamera"
  return="void"
  abstract="false"
@@ -88875,6 +100952,19 @@
 <parameter name="c" type="android.hardware.Camera">
 </parameter>
 </method>
+<method name="setCaptureRate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fps" type="double">
+</parameter>
+</method>
 <method name="setMaxDuration"
  return="void"
  abstract="false"
@@ -95837,7 +107927,7 @@
  type="android.net.SSLCertificateSocketFactory"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="handshakeTimeoutMillis" type="int">
@@ -96312,6 +108402,21 @@
  visibility="public"
 >
 </method>
+<method name="getBooleanQueryParameter"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="defaultValue" type="boolean">
+</parameter>
+</method>
 <method name="getEncodedAuthority"
  return="java.lang.String"
  abstract="true"
@@ -96468,6 +108573,17 @@
 <parameter name="key" type="java.lang.String">
 </parameter>
 </method>
+<method name="getQueryParameterNames"
+ return="java.util.Set&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getQueryParameters"
  return="java.util.List&lt;java.lang.String&gt;"
  abstract="false"
@@ -96714,6 +108830,17 @@
  visibility="public"
 >
 </method>
+<method name="clearQuery"
+ return="android.net.Uri.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="encodedAuthority"
  return="android.net.Uri.Builder"
  abstract="false"
@@ -97937,7 +110064,7 @@
  type="android.net.http.SslCertificate"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="issuedTo" type="java.lang.String">
@@ -98004,7 +110131,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -98026,7 +110153,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -122230,6 +134357,17 @@
  visibility="public"
 >
 </field>
+<field name="HONEYCOMB"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="Bundle"
  extends="java.lang.Object"
@@ -122515,6 +134653,17 @@
 <parameter name="key" type="java.lang.String">
 </parameter>
 </method>
+<method name="getClassLoader"
+ return="java.lang.ClassLoader"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getDouble"
  return="double"
  abstract="false"
@@ -126962,6 +139111,23 @@
 >
 </field>
 </class>
+<class name="NetworkOnMainThreadException"
+ extends="java.lang.RuntimeException"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="NetworkOnMainThreadException"
+ type="android.os.NetworkOnMainThreadException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+</class>
 <class name="Parcel"
  extends="java.lang.Object"
  abstract="false"
@@ -128302,6 +140468,23 @@
  visibility="public"
 >
 </method>
+<method name="fromData"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="byte[]">
+</parameter>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
 <method name="fromSocket"
  return="android.os.ParcelFileDescriptor"
  abstract="false"
@@ -130056,6 +142239,17 @@
  visibility="public"
 >
 </method>
+<method name="penaltyDeathOnNetwork"
+ return="android.os.StrictMode.ThreadPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="penaltyDialog"
  return="android.os.StrictMode.ThreadPolicy.Builder"
  abstract="false"
@@ -130191,6 +142385,17 @@
  visibility="public"
 >
 </method>
+<method name="detectLeakedClosableObjects"
+ return="android.os.StrictMode.VmPolicy.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="detectLeakedSqlLiteObjects"
  return="android.os.StrictMode.VmPolicy.Builder"
  abstract="false"
@@ -130431,6 +142636,17 @@
  visibility="public"
 >
 </method>
+<method name="hasVibrator"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="vibrate"
  return="void"
  abstract="false"
@@ -130711,6 +142927,53 @@
 >
 </field>
 </class>
+<class name="StorageEventListener"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="StorageEventListener"
+ type="android.os.storage.StorageEventListener"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onStorageStateChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="oldState" type="java.lang.String">
+</parameter>
+<parameter name="newState" type="java.lang.String">
+</parameter>
+</method>
+<method name="onUsbMassStorageConnectionChanged"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="connected" type="boolean">
+</parameter>
+</method>
+</class>
 <class name="StorageManager"
  extends="java.lang.Object"
  abstract="false"
@@ -130719,6 +142982,28 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<method name="disableUsbMassStorage"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="enableUsbMassStorage"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getMountedObbPath"
  return="java.lang.String"
  abstract="false"
@@ -130745,6 +143030,28 @@
 <parameter name="filename" type="java.lang.String">
 </parameter>
 </method>
+<method name="isUsbMassStorageConnected"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isUsbMassStorageEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="mountObb"
  return="boolean"
  abstract="false"
@@ -130762,6 +143069,19 @@
 <parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
 </parameter>
 </method>
+<method name="registerListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.os.storage.StorageEventListener">
+</parameter>
+</method>
 <method name="unmountObb"
  return="boolean"
  abstract="false"
@@ -130779,6 +143099,124 @@
 <parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
 </parameter>
 </method>
+<method name="unregisterListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.os.storage.StorageEventListener">
+</parameter>
+</method>
+</class>
+<class name="StorageResultCode"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="StorageResultCode"
+ type="android.os.storage.StorageResultCode"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="OperationFailedInternalError"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedMediaBlank"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedMediaCorrupt"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedNoMedia"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedStorageBusy"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedStorageMounted"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationFailedStorageNotMounted"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OperationSucceeded"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 </package>
 <package name="android.preference"
@@ -131574,6 +144012,148 @@
 </parameter>
 </method>
 </class>
+<class name="MultiSelectListPreference"
+ extends="android.preference.DialogPreference"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="MultiSelectListPreference"
+ type="android.preference.MultiSelectListPreference"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+<constructor name="MultiSelectListPreference"
+ type="android.preference.MultiSelectListPreference"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<method name="findIndexOfValue"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="java.lang.String">
+</parameter>
+</method>
+<method name="getEntries"
+ return="java.lang.CharSequence[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getEntryValues"
+ return="java.lang.CharSequence[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getValues"
+ return="java.util.Set&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setEntries"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entries" type="java.lang.CharSequence[]">
+</parameter>
+</method>
+<method name="setEntries"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entriesResId" type="int">
+</parameter>
+</method>
+<method name="setEntryValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entryValues" type="java.lang.CharSequence[]">
+</parameter>
+</method>
+<method name="setEntryValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="entryValuesResId" type="int">
+</parameter>
+</method>
+<method name="setValues"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="java.util.Set&lt;java.lang.String&gt;">
+</parameter>
+</method>
+</class>
 <class name="Preference"
  extends="java.lang.Object"
  abstract="false"
@@ -131692,6 +144272,28 @@
  visibility="public"
 >
 </method>
+<method name="getExtras"
+ return="android.os.Bundle"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFragment"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getIntent"
  return="android.content.Intent"
  abstract="false"
@@ -132124,6 +144726,17 @@
 <parameter name="defaultValue" type="java.lang.Object">
 </parameter>
 </method>
+<method name="peekExtras"
+ return="android.os.Bundle"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="persistBoolean"
  return="boolean"
  abstract="false"
@@ -132254,6 +144867,19 @@
 <parameter name="enabled" type="boolean">
 </parameter>
 </method>
+<method name="setFragment"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="java.lang.String">
+</parameter>
+</method>
 <method name="setIntent"
  return="void"
  abstract="false"
@@ -132572,6 +145198,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.preference.PreferenceFragment.OnPreferenceStartFragmentCallback">
+</implements>
 <constructor name="PreferenceActivity"
  type="android.preference.PreferenceActivity"
  static="false"
@@ -132587,6 +145215,634 @@
  synchronized="false"
  static="false"
  final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="addPreferencesFromResource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="preferencesResId" type="int">
+</parameter>
+</method>
+<method name="findPreference"
+ return="android.preference.Preference"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="finishPreferencePanel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="caller" type="android.app.Fragment">
+</parameter>
+<parameter name="resultCode" type="int">
+</parameter>
+<parameter name="resultData" type="android.content.Intent">
+</parameter>
+</method>
+<method name="getPreferenceManager"
+ return="android.preference.PreferenceManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPreferenceScreen"
+ return="android.preference.PreferenceScreen"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasHeaders"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="invalidateHeaders"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isMultiPane"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="loadHeadersFromResource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="resid" type="int">
+</parameter>
+<parameter name="target" type="java.util.List&lt;android.preference.PreferenceActivity.Header&gt;">
+</parameter>
+</method>
+<method name="onBuildHeaders"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="target" type="java.util.List&lt;android.preference.PreferenceActivity.Header&gt;">
+</parameter>
+</method>
+<method name="onGetInitialHeader"
+ return="android.preference.PreferenceActivity.Header"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onGetNewHeader"
+ return="android.preference.PreferenceActivity.Header"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onHeaderClick"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="header" type="android.preference.PreferenceActivity.Header">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="onIsHidingHeaders"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onIsMultiPane"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onPreferenceStartFragment"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="caller" type="android.preference.PreferenceFragment">
+</parameter>
+<parameter name="pref" type="android.preference.Preference">
+</parameter>
+</method>
+<method name="onPreferenceTreeClick"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="preferenceScreen" type="android.preference.PreferenceScreen">
+</parameter>
+<parameter name="preference" type="android.preference.Preference">
+</parameter>
+</method>
+<method name="setListFooter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
+<method name="setPreferenceScreen"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="preferenceScreen" type="android.preference.PreferenceScreen">
+</parameter>
+</method>
+<method name="showBreadCrumbs"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="title" type="java.lang.CharSequence">
+</parameter>
+<parameter name="shortTitle" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="startPreferenceFragment"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragment" type="android.app.Fragment">
+</parameter>
+<parameter name="push" type="boolean">
+</parameter>
+</method>
+<method name="startPreferencePanel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragmentClass" type="java.lang.String">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+<parameter name="titleRes" type="int">
+</parameter>
+<parameter name="titleText" type="java.lang.CharSequence">
+</parameter>
+<parameter name="resultTo" type="android.app.Fragment">
+</parameter>
+<parameter name="resultRequestCode" type="int">
+</parameter>
+</method>
+<method name="startWithFragment"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragmentName" type="java.lang.String">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+<parameter name="resultTo" type="android.app.Fragment">
+</parameter>
+<parameter name="resultRequestCode" type="int">
+</parameter>
+</method>
+<method name="switchToHeader"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fragmentName" type="java.lang.String">
+</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="switchToHeader"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="header" type="android.preference.PreferenceActivity.Header">
+</parameter>
+</method>
+<field name="EXTRA_NO_HEADERS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;:android:no_headers&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_SHOW_FRAGMENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;:android:show_fragment&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_SHOW_FRAGMENT_ARGUMENTS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;:android:show_fragment_args&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="HEADER_ID_UNDEFINED"
+ type="long"
+ transient="false"
+ volatile="false"
+ value="-1L"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="PreferenceActivity.Header"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="PreferenceActivity.Header"
+ type="android.preference.PreferenceActivity.Header"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="readFromParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="in" type="android.os.Parcel">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="breadCrumbShortTitle"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="breadCrumbTitle"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="extras"
+ type="android.os.Bundle"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragment"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentArguments"
+ type="android.os.Bundle"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="iconRes"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="id"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="intent"
+ type="android.content.Intent"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="summary"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="title"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="PreferenceCategory"
+ extends="android.preference.PreferenceGroup"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="PreferenceCategory"
+ type="android.preference.PreferenceCategory"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyle" type="int">
+</parameter>
+</constructor>
+<constructor name="PreferenceCategory"
+ type="android.preference.PreferenceCategory"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+<constructor name="PreferenceCategory"
+ type="android.preference.PreferenceCategory"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+</class>
+<class name="PreferenceFragment"
+ extends="android.app.Fragment"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="PreferenceFragment"
+ type="android.preference.PreferenceFragment"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="addPreferencesFromIntent"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
@@ -132670,51 +145926,29 @@
 </parameter>
 </method>
 </class>
-<class name="PreferenceCategory"
- extends="android.preference.PreferenceGroup"
- abstract="false"
+<interface name="PreferenceFragment.OnPreferenceStartFragmentCallback"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onPreferenceStartFragment"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
  static="false"
  final="false"
  deprecated="not deprecated"
  visibility="public"
 >
-<constructor name="PreferenceCategory"
- type="android.preference.PreferenceCategory"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
+<parameter name="caller" type="android.preference.PreferenceFragment">
 </parameter>
-<parameter name="attrs" type="android.util.AttributeSet">
+<parameter name="pref" type="android.preference.Preference">
 </parameter>
-<parameter name="defStyle" type="int">
-</parameter>
-</constructor>
-<constructor name="PreferenceCategory"
- type="android.preference.PreferenceCategory"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="attrs" type="android.util.AttributeSet">
-</parameter>
-</constructor>
-<constructor name="PreferenceCategory"
- type="android.preference.PreferenceCategory"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-</constructor>
-</class>
+</method>
+</interface>
 <class name="PreferenceGroup"
  extends="android.preference.Preference"
  abstract="true"
@@ -133675,9 +146909,9 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="c" type="android.content.Context">
+<parameter name="context" type="android.content.Context">
 </parameter>
-<parameter name="s" type="java.lang.String">
+<parameter name="string" type="java.lang.String">
 </parameter>
 </method>
 <method name="truncateHistory"
@@ -134061,7 +147295,7 @@
  value="&quot;url&quot;"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -137120,6 +150354,28 @@
  visibility="public"
 >
 </field>
+<field name="DIRECTORY_PARAM_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;directory&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="LIMIT_PARAM_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;limit&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="ContactsContract.AggregationExceptions"
  extends="java.lang.Object"
@@ -137395,6 +150651,17 @@
 <parameter name="type" type="int">
 </parameter>
 </method>
+<field name="ADDRESS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;data1&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CONTENT_FILTER_URI"
  type="android.net.Uri"
  transient="false"
@@ -138525,6 +151792,36 @@
 </implements>
 <implements name="android.provider.ContactsContract.DataColumnsWithJoins">
 </implements>
+<method name="getTypeLabel"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="res" type="android.content.res.Resources">
+</parameter>
+<parameter name="type" type="int">
+</parameter>
+<parameter name="label" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="getTypeLabelResource"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="int">
+</parameter>
+</method>
 <field name="CONTENT_ITEM_TYPE"
  type="java.lang.String"
  transient="false"
@@ -139229,6 +152526,91 @@
 >
 </field>
 </class>
+<interface name="ContactsContract.ContactNameColumns"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<field name="DISPLAY_NAME_ALTERNATIVE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;display_name_alt&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISPLAY_NAME_PRIMARY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;display_name&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISPLAY_NAME_SOURCE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;display_name_source&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHONETIC_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;phonetic_name&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHONETIC_NAME_STYLE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;phonetic_name_style&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SORT_KEY_ALTERNATIVE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;sort_key_alt&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SORT_KEY_PRIMARY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;sort_key&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
 <interface name="ContactsContract.ContactOptionsColumns"
  abstract="true"
  static="true"
@@ -139299,6 +152681,17 @@
  deprecated="not deprecated"
  visibility="protected"
 >
+<field name="CONTACT_CHAT_CAPABILITY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;contact_chat_capability&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="CONTACT_PRESENCE"
  type="java.lang.String"
  transient="false"
@@ -139376,6 +152769,8 @@
 >
 <implements name="android.provider.BaseColumns">
 </implements>
+<implements name="android.provider.ContactsContract.ContactNameColumns">
+</implements>
 <implements name="android.provider.ContactsContract.ContactOptionsColumns">
 </implements>
 <implements name="android.provider.ContactsContract.ContactStatusColumns">
@@ -139571,6 +152966,10 @@
 >
 <implements name="android.provider.BaseColumns">
 </implements>
+<implements name="android.provider.ContactsContract.ContactOptionsColumns">
+</implements>
+<implements name="android.provider.ContactsContract.ContactStatusColumns">
+</implements>
 <implements name="android.provider.ContactsContract.ContactsColumns">
 </implements>
 <field name="CONTENT_DIRECTORY"
@@ -139609,6 +153008,68 @@
 >
 </field>
 </class>
+<class name="ContactsContract.Contacts.Entity"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.BaseColumns">
+</implements>
+<implements name="android.provider.ContactsContract.BaseSyncColumns">
+</implements>
+<implements name="android.provider.ContactsContract.ContactNameColumns">
+</implements>
+<implements name="android.provider.ContactsContract.ContactOptionsColumns">
+</implements>
+<implements name="android.provider.ContactsContract.ContactStatusColumns">
+</implements>
+<implements name="android.provider.ContactsContract.ContactsColumns">
+</implements>
+<implements name="android.provider.ContactsContract.DataColumns">
+</implements>
+<implements name="android.provider.ContactsContract.RawContactsColumns">
+</implements>
+<implements name="android.provider.ContactsContract.StatusColumns">
+</implements>
+<implements name="android.provider.ContactsContract.SyncColumns">
+</implements>
+<field name="CONTENT_DIRECTORY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;entities&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DATA_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;data_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="RAW_CONTACT_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;raw_contact_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="ContactsContract.Contacts.Photo"
  extends="java.lang.Object"
  abstract="false"
@@ -139619,7 +153080,7 @@
 >
 <implements name="android.provider.BaseColumns">
 </implements>
-<implements name="android.provider.ContactsContract.DataColumns">
+<implements name="android.provider.ContactsContract.DataColumnsWithJoins">
 </implements>
 <field name="CONTENT_DIRECTORY"
  type="java.lang.String"
@@ -139632,6 +153093,17 @@
  visibility="public"
 >
 </field>
+<field name="PHOTO"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;data15&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <interface name="ContactsContract.ContactsColumns"
  abstract="true"
@@ -139695,6 +153167,28 @@
  visibility="public"
 >
 </field>
+<field name="PHOTO_THUMBNAIL_URI"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;photo_thumb_uri&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHOTO_URI"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;photo_uri&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </interface>
 <class name="ContactsContract.Data"
  extends="java.lang.Object"
@@ -139937,6 +153431,17 @@
  visibility="public"
 >
 </field>
+<field name="IS_READ_ONLY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;is_read_only&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="IS_SUPER_PRIMARY"
  type="java.lang.String"
  transient="false"
@@ -140024,6 +153529,8 @@
 >
 <implements name="android.provider.BaseColumns">
 </implements>
+<implements name="android.provider.ContactsContract.ContactNameColumns">
+</implements>
 <implements name="android.provider.ContactsContract.ContactOptionsColumns">
 </implements>
 <implements name="android.provider.ContactsContract.ContactStatusColumns">
@@ -140037,6 +153544,441 @@
 <implements name="android.provider.ContactsContract.StatusColumns">
 </implements>
 </interface>
+<class name="ContactsContract.Directory"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.BaseColumns">
+</implements>
+<method name="notifyDirectoryChange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="resolver" type="android.content.ContentResolver">
+</parameter>
+</method>
+<field name="ACCOUNT_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;accountName&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACCOUNT_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;accountType&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_ITEM_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;vnd.android.cursor.item/contact_directory&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;vnd.android.cursor.dir/contact_directories&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CONTENT_URI"
+ type="android.net.Uri"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DEFAULT"
+ type="long"
+ transient="false"
+ volatile="false"
+ value="0L"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DIRECTORY_AUTHORITY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;authority&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DISPLAY_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;displayName&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXPORT_SUPPORT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;exportSupport&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXPORT_SUPPORT_ANY_ACCOUNT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXPORT_SUPPORT_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXPORT_SUPPORT_SAME_ACCOUNT_ONLY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="LOCAL_INVISIBLE"
+ type="long"
+ transient="false"
+ volatile="false"
+ value="1L"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PACKAGE_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;packageName&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHOTO_SUPPORT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;photoSupport&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHOTO_SUPPORT_FULL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHOTO_SUPPORT_FULL_SIZE_ONLY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHOTO_SUPPORT_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHOTO_SUPPORT_THUMBNAIL_ONLY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SHORTCUT_SUPPORT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;shortcutSupport&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SHORTCUT_SUPPORT_DATA_ITEMS_ONLY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SHORTCUT_SUPPORT_FULL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SHORTCUT_SUPPORT_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_RESOURCE_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;typeResourceId&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="ContactsContract.DisplayNameSources"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="EMAIL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="NICKNAME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="35"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ORGANIZATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="30"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PHONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="20"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STRUCTURED_NAME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="40"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="UNDEFINED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
+<interface name="ContactsContract.FullNameStyle"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="CHINESE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CJK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="JAPANESE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KOREAN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="UNDEFINED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WESTERN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
 <class name="ContactsContract.Groups"
  extends="java.lang.Object"
  abstract="false"
@@ -140114,6 +154056,17 @@
  deprecated="not deprecated"
  visibility="protected"
 >
+<field name="AUTO_ADD"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;auto_add&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="DELETED"
  type="java.lang.String"
  transient="false"
@@ -140125,6 +154078,17 @@
  visibility="public"
 >
 </field>
+<field name="FAVORITES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;favorites&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="GROUP_VISIBLE"
  type="java.lang.String"
  transient="false"
@@ -140335,6 +154299,17 @@
  visibility="public"
 >
 </field>
+<field name="DATA"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;data&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EMAIL"
  type="java.lang.String"
  transient="false"
@@ -140679,6 +154654,58 @@
 >
 </field>
 </interface>
+<interface name="ContactsContract.PhoneticNameStyle"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="JAPANESE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KOREAN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PINYIN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="UNDEFINED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
 <class name="ContactsContract.Presence"
  extends="android.provider.ContactsContract.StatusUpdates"
  abstract="false"
@@ -140861,6 +154888,8 @@
 >
 <implements name="android.provider.BaseColumns">
 </implements>
+<implements name="android.provider.ContactsContract.ContactNameColumns">
+</implements>
 <implements name="android.provider.ContactsContract.ContactOptionsColumns">
 </implements>
 <implements name="android.provider.ContactsContract.RawContactsColumns">
@@ -140924,7 +154953,7 @@
  value="1"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -141071,6 +155100,17 @@
  visibility="public"
 >
 </field>
+<field name="RAW_CONTACT_IS_READ_ONLY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;raw_contact_is_read_only&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </interface>
 <class name="ContactsContract.RawContactsEntity"
  extends="java.lang.Object"
@@ -141276,6 +155316,50 @@
  visibility="public"
 >
 </field>
+<field name="CAPABILITY_HAS_CAMERA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CAPABILITY_HAS_VIDEO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CAPABILITY_HAS_VOICE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHAT_CAPABILITY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;chat_capability&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="DO_NOT_DISTURB"
  type="int"
  transient="false"
@@ -142718,6 +156802,21 @@
 <parameter name="volumeName" type="java.lang.String">
 </parameter>
 </method>
+<method name="getContentUriForAudioId"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="volumeName" type="java.lang.String">
+</parameter>
+<parameter name="audioId" type="int">
+</parameter>
+</method>
 <field name="CONTENT_TYPE"
  type="java.lang.String"
  transient="false"
@@ -143236,6 +157335,160 @@
 >
 </field>
 </interface>
+<class name="MediaStore.Files"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="MediaStore.Files"
+ type="android.provider.MediaStore.Files"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getContentUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="volumeName" type="java.lang.String">
+</parameter>
+</method>
+<method name="getContentUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="volumeName" type="java.lang.String">
+</parameter>
+<parameter name="rowId" type="long">
+</parameter>
+</method>
+</class>
+<interface name="MediaStore.Files.FileColumns"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.MediaStore.MediaColumns">
+</implements>
+<field name="MEDIA_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;media_type&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MEDIA_TYPE_AUDIO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MEDIA_TYPE_IMAGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MEDIA_TYPE_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MEDIA_TYPE_PLAYLIST"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MEDIA_TYPE_VIDEO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MIME_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;mime_type&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PARENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;parent&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TITLE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;title&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
 <class name="MediaStore.Images"
  extends="java.lang.Object"
  abstract="false"
@@ -144779,6 +159032,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_INPUT_METHOD_AND_SUBTYPE_ENABLER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.settings.INPUT_METHOD_AND_SUBTYPE_ENABLER&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_INPUT_METHOD_SETTINGS"
  type="java.lang.String"
  transient="false"
@@ -145595,6 +159859,17 @@
  visibility="public"
 >
 </field>
+<field name="SELECTED_INPUT_METHOD_SUBTYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;selected_input_method_subtype&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SETTINGS_CLASSNAME"
  type="java.lang.String"
  transient="false"
@@ -146316,6 +160591,17 @@
  visibility="public"
 >
 </field>
+<field name="AUTO_TIME_ZONE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;auto_time_zone&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="BLUETOOTH_DISCOVERABILITY"
  type="java.lang.String"
  transient="false"
@@ -146814,7 +161100,7 @@
  value="&quot;show_web_suggestions&quot;"
  static="true"
  final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </field>
@@ -146928,6 +161214,17 @@
  visibility="public"
 >
 </field>
+<field name="USER_ROTATION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;user_rotation&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="USE_GOOGLE_MAIL"
  type="java.lang.String"
  transient="false"
@@ -150185,7 +164482,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="text" type="android.text.Editable">
+<parameter name="s" type="android.text.Editable">
 </parameter>
 </method>
 <method name="beforeTextChanged"
@@ -152648,6 +166945,17 @@
  visibility="public"
 >
 </field>
+<field name="NETWORK_TYPE_EHRPD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="14"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="NETWORK_TYPE_EVDO_0"
  type="int"
  transient="false"
@@ -152736,6 +167044,17 @@
  visibility="public"
 >
 </field>
+<field name="NETWORK_TYPE_LTE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="13"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="NETWORK_TYPE_UMTS"
  type="int"
  transient="false"
@@ -152791,6 +167110,17 @@
  visibility="public"
 >
 </field>
+<field name="PHONE_TYPE_SIP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SIM_STATE_ABSENT"
  type="int"
  transient="false"
@@ -155027,6 +169357,36 @@
 >
 </method>
 </class>
+<class name="LoaderTestCase"
+ extends="android.test.AndroidTestCase"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="LoaderTestCase"
+ type="android.test.LoaderTestCase"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getLoaderResultSynchronously"
+ return="T"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="loader" type="android.content.Loader&lt;T&gt;">
+</parameter>
+</method>
+</class>
 <class name="MoreAsserts"
  extends="java.lang.Object"
  abstract="false"
@@ -157947,6 +172307,25 @@
 <parameter name="factory" type="android.database.sqlite.SQLiteDatabase.CursorFactory">
 </parameter>
 </method>
+<method name="openOrCreateDatabase"
+ return="android.database.sqlite.SQLiteDatabase"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="file" type="java.lang.String">
+</parameter>
+<parameter name="mode" type="int">
+</parameter>
+<parameter name="factory" type="android.database.sqlite.SQLiteDatabase.CursorFactory">
+</parameter>
+<parameter name="errorHandler" type="android.database.DatabaseErrorHandler">
+</parameter>
+</method>
 <method name="peekWallpaper"
  return="android.graphics.drawable.Drawable"
  abstract="false"
@@ -158291,17 +172670,6 @@
  visibility="public"
 >
 </constructor>
-<method name="abortUpdates"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="close"
  return="void"
  abstract="false"
@@ -158313,30 +172681,6 @@
  visibility="public"
 >
 </method>
-<method name="commitUpdates"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="commitUpdates"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="values" type="java.util.Map&lt;? extends java.lang.Long, ? extends java.util.Map&lt;java.lang.String, java.lang.Object&gt;&gt;">
-</parameter>
-</method>
 <method name="copyStringToBuffer"
  return="void"
  abstract="false"
@@ -158363,17 +172707,6 @@
  visibility="public"
 >
 </method>
-<method name="deleteRow"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getBlob"
  return="byte[]"
  abstract="false"
@@ -158559,8 +172892,8 @@
 <parameter name="columnIndex" type="int">
 </parameter>
 </method>
-<method name="getWantsAllOnMoveCalls"
- return="boolean"
+<method name="getType"
+ return="int"
  abstract="false"
  native="false"
  synchronized="false"
@@ -158569,8 +172902,10 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="columnIndex" type="int">
+</parameter>
 </method>
-<method name="hasUpdates"
+<method name="getWantsAllOnMoveCalls"
  return="boolean"
  abstract="false"
  native="false"
@@ -158784,17 +173119,6 @@
 <parameter name="uri" type="android.net.Uri">
 </parameter>
 </method>
-<method name="supportsUpdates"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="unregisterContentObserver"
  return="void"
  abstract="false"
@@ -158821,124 +173145,6 @@
 <parameter name="observer" type="android.database.DataSetObserver">
 </parameter>
 </method>
-<method name="updateBlob"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="byte[]">
-</parameter>
-</method>
-<method name="updateDouble"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="double">
-</parameter>
-</method>
-<method name="updateFloat"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="float">
-</parameter>
-</method>
-<method name="updateInt"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="int">
-</parameter>
-</method>
-<method name="updateLong"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="long">
-</parameter>
-</method>
-<method name="updateShort"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="short">
-</parameter>
-</method>
-<method name="updateString"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-<parameter name="value" type="java.lang.String">
-</parameter>
-</method>
-<method name="updateToNull"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="columnIndex" type="int">
-</parameter>
-</method>
 </class>
 <class name="MockDialogInterface"
  extends="java.lang.Object"
@@ -161091,15 +175297,23 @@
 </class>
 <class name="ClipboardManager"
  extends="java.lang.Object"
- abstract="false"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<constructor name="ClipboardManager"
+ type="android.text.ClipboardManager"
  static="false"
  final="false"
  deprecated="not deprecated"
  visibility="public"
 >
+</constructor>
 <method name="getText"
  return="java.lang.CharSequence"
- abstract="false"
+ abstract="true"
  native="false"
  synchronized="false"
  static="false"
@@ -161110,7 +175324,7 @@
 </method>
 <method name="hasText"
  return="boolean"
- abstract="false"
+ abstract="true"
  native="false"
  synchronized="false"
  static="false"
@@ -161121,7 +175335,7 @@
 </method>
 <method name="setText"
  return="void"
- abstract="false"
+ abstract="true"
  native="false"
  synchronized="false"
  static="false"
@@ -162213,6 +176427,28 @@
  visibility="public"
 >
 </field>
+<field name="TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="208"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_TEXT_VARIATION_WEB_PASSWORD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="224"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </interface>
 <class name="Layout"
  extends="java.lang.Object"
@@ -163946,6 +178182,29 @@
 <parameter name="kind" type="java.lang.Class&lt;T&gt;">
 </parameter>
 </method>
+<method name="getTextRunCursor"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="contextStart" type="int">
+</parameter>
+<parameter name="contextEnd" type="int">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+<parameter name="offset" type="int">
+</parameter>
+<parameter name="cursorOpt" type="int">
+</parameter>
+<parameter name="p" type="android.graphics.Paint">
+</parameter>
+</method>
 <method name="insert"
  return="android.text.SpannableStringBuilder"
  abstract="false"
@@ -165006,7 +179265,7 @@
 >
 <parameter name="text" type="java.lang.CharSequence">
 </parameter>
-<parameter name="p" type="android.text.TextPaint">
+<parameter name="paint" type="android.text.TextPaint">
 </parameter>
 <parameter name="avail" type="float">
 </parameter>
@@ -173393,6 +187652,18 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="AndroidException"
+ type="android.util.AndroidException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
 <parameter name="cause" type="java.lang.Exception">
 </parameter>
 </constructor>
@@ -173430,6 +187701,18 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="AndroidRuntimeException"
+ type="android.util.AndroidRuntimeException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
 <parameter name="cause" type="java.lang.Exception">
 </parameter>
 </constructor>
@@ -174449,7 +188732,7 @@
  abstract="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <constructor name="EventLogTags"
@@ -174604,6 +188887,483 @@
 </parameter>
 </method>
 </class>
+<class name="JsonReader"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.io.Closeable">
+</implements>
+<constructor name="JsonReader"
+ type="android.util.JsonReader"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="in" type="java.io.Reader">
+</parameter>
+</constructor>
+<method name="beginArray"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="beginObject"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="close"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="endArray"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="endObject"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="hasNext"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="nextBoolean"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="nextDouble"
+ return="double"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="nextInt"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="nextLong"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="nextName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="nextNull"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="nextString"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="peek"
+ return="android.util.JsonToken"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="setLenient"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="lenient" type="boolean">
+</parameter>
+</method>
+<method name="skipValue"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="syntaxError"
+ return="java.io.IOException"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
+<class name="JsonToken"
+ extends="java.lang.Enum"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="valueOf"
+ return="android.util.JsonToken"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
+<method name="values"
+ return="android.util.JsonToken[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="JsonWriter"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.io.Closeable">
+</implements>
+<constructor name="JsonWriter"
+ type="android.util.JsonWriter"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="out" type="java.io.Writer">
+</parameter>
+</constructor>
+<method name="beginArray"
+ return="android.util.JsonWriter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="beginObject"
+ return="android.util.JsonWriter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="close"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="endArray"
+ return="android.util.JsonWriter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="endObject"
+ return="android.util.JsonWriter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="flush"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="name"
+ return="android.util.JsonWriter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="nullValue"
+ return="android.util.JsonWriter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="setIndent"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="indent" type="java.lang.String">
+</parameter>
+</method>
+<method name="value"
+ return="android.util.JsonWriter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="java.lang.String">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="value"
+ return="android.util.JsonWriter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="boolean">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="value"
+ return="android.util.JsonWriter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="double">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="value"
+ return="android.util.JsonWriter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="long">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
 <class name="Log"
  extends="java.lang.Object"
  abstract="false"
@@ -175329,7 +190089,7 @@
  type="java.lang.String"
  transient="false"
  volatile="false"
- value="&quot;((aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(biz|b[abdefghijmnorstvwyz])|(cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(edu|e[cegrstu])|f[ijkmor]|(gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(info|int|i[delmnoqrst])|(jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(name|net|n[acefgilopruz])|(org|om)|(pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnortuvyz]|(tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]|w[fs]|(xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)|y[etu]|z[amw])&quot;"
+ value="&quot;((aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(biz|b[abdefghijmnorstvwyz])|(cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(edu|e[cegrstu])|f[ijkmor]|(gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(info|int|i[delmnoqrst])|(jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(name|net|n[acefgilopruz])|(org|om)|(pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnortuvyz]|(tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]|w[fs]|(xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-mgbaam7a8h|xn\\-\\-mgberp4a5d4ar|xn\\-\\-wgbh1c|xn\\-\\-zckzah)|y[et]|z[amw])&quot;"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -175340,7 +190100,7 @@
  type="java.lang.String"
  transient="false"
  volatile="false"
- value="&quot;(?:(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(?:biz|b[abdefghijmnorstvwyz])|(?:cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(?:edu|e[cegrstu])|f[ijkmor]|(?:gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(?:info|int|i[delmnoqrst])|(?:jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(?:mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(?:name|net|n[acefgilopruz])|(?:org|om)|(?:pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnortuvyz]|(?:tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]|w[fs]|(?:xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)|y[etu]|z[amw]))&quot;"
+ value="&quot;(?:(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(?:biz|b[abdefghijmnorstvwyz])|(?:cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(?:edu|e[cegrstu])|f[ijkmor]|(?:gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(?:info|int|i[delmnoqrst])|(?:jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(?:mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(?:name|net|n[acefgilopruz])|(?:org|om)|(?:pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnortuvyz]|(?:tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]|w[fs]|(?:xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-mgbaam7a8h|xn\\-\\-mgberp4a5d4ar|xn\\-\\-wgbh1c|xn\\-\\-zckzah)|y[et]|z[amw]))&quot;"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -175607,6 +190367,19 @@
 <parameter name="key" type="int">
 </parameter>
 </method>
+<method name="removeAt"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="index" type="int">
+</parameter>
+</method>
 <method name="setValueAt"
  return="void"
  abstract="false"
@@ -176091,6 +190864,17 @@
 <parameter name="newSize" type="int">
 </parameter>
 </method>
+<field name="NOTHING"
+ type="int[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="WILD_CARD"
  type="int[]"
  transient="false"
@@ -177212,6 +191996,231 @@
 >
 </field>
 </class>
+<class name="ActionMode"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ActionMode"
+ type="android.view.ActionMode"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="finish"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCustomView"
+ return="android.view.View"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMenu"
+ return="android.view.Menu"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMenuInflater"
+ return="android.view.MenuInflater"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSubtitle"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTitle"
+ return="java.lang.CharSequence"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="invalidate"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setCustomView"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
+<method name="setSubtitle"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="subtitle" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setSubtitle"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="resId" type="int">
+</parameter>
+</method>
+<method name="setTitle"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="title" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setTitle"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="resId" type="int">
+</parameter>
+</method>
+</class>
+<interface name="ActionMode.Callback"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onActionItemClicked"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="android.view.ActionMode">
+</parameter>
+<parameter name="item" type="android.view.MenuItem">
+</parameter>
+</method>
+<method name="onCreateActionMode"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="android.view.ActionMode">
+</parameter>
+<parameter name="menu" type="android.view.Menu">
+</parameter>
+</method>
+<method name="onDestroyActionMode"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="android.view.ActionMode">
+</parameter>
+</method>
+<method name="onPrepareActionMode"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="android.view.ActionMode">
+</parameter>
+<parameter name="menu" type="android.view.Menu">
+</parameter>
+</method>
+</interface>
 <interface name="ContextMenu"
  abstract="true"
  static="false"
@@ -177462,6 +192471,232 @@
 >
 </field>
 </class>
+<class name="DragEvent"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAction"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getClipData"
+ return="android.content.ClipData"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getClipDescription"
+ return="android.content.ClipDescription"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getResult"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="obtain"
+ return="android.view.DragEvent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="action" type="int">
+</parameter>
+<parameter name="x" type="float">
+</parameter>
+<parameter name="y" type="float">
+</parameter>
+<parameter name="description" type="android.content.ClipDescription">
+</parameter>
+<parameter name="data" type="android.content.ClipData">
+</parameter>
+<parameter name="result" type="boolean">
+</parameter>
+</method>
+<method name="obtain"
+ return="android.view.DragEvent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="android.view.DragEvent">
+</parameter>
+</method>
+<method name="recycle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="ACTION_DRAG_ENDED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DRAG_ENTERED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DRAG_EXITED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DRAG_LOCATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DRAG_STARTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_DROP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="FocusFinder"
  extends="java.lang.Object"
  abstract="false"
@@ -179856,6 +195091,39 @@
  visibility="public"
 >
 </method>
+<method name="isCapsLockOn"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isCtrlPressed"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isFunctionPressed"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isLongPress"
  return="boolean"
  abstract="false"
@@ -179867,6 +195135,17 @@
  visibility="public"
 >
 </method>
+<method name="isMetaPressed"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isModifierKey"
  return="boolean"
  abstract="false"
@@ -179880,6 +195159,17 @@
 <parameter name="keyCode" type="int">
 </parameter>
 </method>
+<method name="isNumLockOn"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isPrintingKey"
  return="boolean"
  abstract="false"
@@ -179891,6 +195181,17 @@
  visibility="public"
 >
 </method>
+<method name="isScrollLockOn"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isShiftPressed"
  return="boolean"
  abstract="false"
@@ -180312,6 +195613,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_BREAK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="121"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_BUTTON_A"
  type="int"
  transient="false"
@@ -180510,6 +195822,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_CAPS_LOCK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="115"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_CLEAR"
  type="int"
  transient="false"
@@ -180532,6 +195855,28 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_CTRL_LEFT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="113"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_CTRL_RIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="114"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_D"
  type="int"
  transient="false"
@@ -180664,6 +196009,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_ESCAPE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="111"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_EXPLORER"
  type="int"
  transient="false"
@@ -180686,6 +196042,138 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_F1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="131"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F10"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="140"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F11"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="141"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F12"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="142"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F2"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="132"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F3"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="133"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F4"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="134"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F5"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="135"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F6"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="136"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F7"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="137"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F8"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="138"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_F9"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="139"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_FOCUS"
  type="int"
  transient="false"
@@ -180697,6 +196185,39 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_FORWARD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="125"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_FORWARD_DEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="112"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_FUNCTION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="119"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_G"
  type="int"
  transient="false"
@@ -180763,6 +196284,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_INSERT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="124"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_J"
  type="int"
  transient="false"
@@ -180818,6 +196350,28 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_MEDIA_CLOSE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="128"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_MEDIA_EJECT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="129"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_MEDIA_FAST_FORWARD"
  type="int"
  transient="false"
@@ -180840,6 +196394,28 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_MEDIA_PAUSE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="127"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_MEDIA_PLAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="126"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_MEDIA_PLAY_PAUSE"
  type="int"
  transient="false"
@@ -180862,6 +196438,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_MEDIA_RECORD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="130"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_MEDIA_REWIND"
  type="int"
  transient="false"
@@ -180895,6 +196482,28 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_META_LEFT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="117"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_META_RIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="118"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_MINUS"
  type="int"
  transient="false"
@@ -180906,6 +196515,28 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_MOVE_END"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="123"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_MOVE_HOME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="122"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_MUTE"
  type="int"
  transient="false"
@@ -180950,6 +196581,237 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_NUMPAD_0"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="144"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="145"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_2"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="146"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_3"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="147"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_4"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="148"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_5"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="149"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_6"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="150"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_7"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="151"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_8"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="152"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_9"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="153"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_ADD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="157"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_COMMA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="159"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_DIVIDE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="154"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_DOT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="158"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_ENTER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="160"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_EQUALS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="161"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_LEFT_PAREN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="162"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_MULTIPLY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="155"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_RIGHT_PAREN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="163"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUMPAD_SUBTRACT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="156"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEYCODE_NUM_LOCK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="143"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_O"
  type="int"
  transient="false"
@@ -181093,6 +196955,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_SCROLL_LOCK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="116"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_SEARCH"
  type="int"
  transient="false"
@@ -181214,6 +197087,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_SYSRQ"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="120"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_T"
  type="int"
  transient="false"
@@ -181280,6 +197164,17 @@
  visibility="public"
 >
 </field>
+<field name="KEYCODE_VOLUME_MUTE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="164"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="KEYCODE_VOLUME_UP"
  type="int"
  transient="false"
@@ -181379,6 +197274,116 @@
  visibility="public"
 >
 </field>
+<field name="META_CAPS_LOCK_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1048576"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_CTRL_LEFT_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8192"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_CTRL_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4096"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_CTRL_RIGHT_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16384"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_FUNCTION_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_META_LEFT_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="131072"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_META_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="65536"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_META_RIGHT_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="262144"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_NUM_LOCK_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2097152"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="META_SCROLL_LOCK_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4194304"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="META_SHIFT_LEFT_ON"
  type="int"
  transient="false"
@@ -181688,6 +197693,17 @@
  visibility="public"
 >
 </method>
+<method name="getFactory2"
+ return="android.view.LayoutInflater.Factory2"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getFilter"
  return="android.view.LayoutInflater.Filter"
  abstract="false"
@@ -181780,6 +197796,25 @@
 <exception name="ClassNotFoundException" type="java.lang.ClassNotFoundException">
 </exception>
 </method>
+<method name="onCreateView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="parent" type="android.view.View">
+</parameter>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<exception name="ClassNotFoundException" type="java.lang.ClassNotFoundException">
+</exception>
+</method>
 <method name="setFactory"
  return="void"
  abstract="false"
@@ -181793,6 +197828,19 @@
 <parameter name="factory" type="android.view.LayoutInflater.Factory">
 </parameter>
 </method>
+<method name="setFactory2"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="factory" type="android.view.LayoutInflater.Factory2">
+</parameter>
+</method>
 <method name="setFilter"
  return="void"
  abstract="false"
@@ -181832,6 +197880,35 @@
 </parameter>
 </method>
 </interface>
+<interface name="LayoutInflater.Factory2"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.view.LayoutInflater.Factory">
+</implements>
+<method name="onCreateView"
+ return="android.view.View"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parent" type="android.view.View">
+</parameter>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</method>
+</interface>
 <interface name="LayoutInflater.Filter"
  abstract="true"
  static="true"
@@ -182359,6 +198436,17 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<method name="getActionView"
+ return="android.view.View"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getAlphabeticShortcut"
  return="char"
  abstract="true"
@@ -182535,6 +198623,19 @@
  visibility="public"
 >
 </method>
+<method name="setActionView"
+ return="android.view.MenuItem"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
 <method name="setAlphabeticShortcut"
  return="android.view.MenuItem"
  abstract="true"
@@ -182667,6 +198768,19 @@
 <parameter name="alphaChar" type="char">
 </parameter>
 </method>
+<method name="setShowAsAction"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="actionEnum" type="int">
+</parameter>
+</method>
 <method name="setTitle"
  return="android.view.MenuItem"
  abstract="true"
@@ -182719,6 +198833,50 @@
 <parameter name="visible" type="boolean">
 </parameter>
 </method>
+<field name="SHOW_AS_ACTION_ALWAYS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SHOW_AS_ACTION_IF_ROOM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SHOW_AS_ACTION_NEVER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SHOW_AS_ACTION_WITH_TEXT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </interface>
 <interface name="MenuItem.OnMenuItemClickListener"
  abstract="true"
@@ -183709,6 +199867,19 @@
 <parameter name="y" type="float">
 </parameter>
 </method>
+<method name="transform"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="matrix" type="android.graphics.Matrix">
+</parameter>
+</method>
 <method name="writeToParcel"
  return="void"
  abstract="false"
@@ -184323,6 +200494,28 @@
  visibility="public"
 >
 </method>
+<method name="getCurrentSpanX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCurrentSpanY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getEventTime"
  return="long"
  abstract="false"
@@ -184367,6 +200560,28 @@
  visibility="public"
 >
 </method>
+<method name="getPreviousSpanX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPreviousSpanY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getScaleFactor"
  return="float"
  abstract="false"
@@ -185946,6 +202161,19 @@
 <parameter name="focusableMode" type="int">
 </parameter>
 </method>
+<method name="addOnLayoutChangeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.view.View.OnLayoutChangeListener">
+</parameter>
+</method>
 <method name="addTouchables"
  return="void"
  abstract="false"
@@ -186206,6 +202434,19 @@
 <parameter name="hint" type="int">
 </parameter>
 </method>
+<method name="dispatchDragEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.DragEvent">
+</parameter>
+</method>
 <method name="dispatchDraw"
  return="void"
  abstract="false"
@@ -186297,6 +202538,19 @@
 <parameter name="container" type="android.util.SparseArray&lt;android.os.Parcelable&gt;">
 </parameter>
 </method>
+<method name="dispatchSetActivated"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="activated" type="boolean">
+</parameter>
+</method>
 <method name="dispatchSetPressed"
  return="void"
  abstract="false"
@@ -186503,6 +202757,17 @@
  visibility="public"
 >
 </method>
+<method name="getAlpha"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getAnimation"
  return="android.view.animation.Animation"
  abstract="false"
@@ -186947,6 +203212,17 @@
 <parameter name="location" type="int[]">
 </parameter>
 </method>
+<method name="getMatrix"
+ return="android.graphics.Matrix"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getMeasuredHeight"
  return="int"
  abstract="false"
@@ -187024,8 +203300,8 @@
  visibility="public"
 >
 </method>
-<method name="getOverScrollMode"
- return="int"
+<method name="getOnLayoutChangeListeners"
+ return="java.util.List&lt;android.view.View.OnLayoutChangeListener&gt;"
  abstract="false"
  native="false"
  synchronized="false"
@@ -187090,6 +203366,28 @@
  visibility="public"
 >
 </method>
+<method name="getPivotX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPivotY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getResources"
  return="android.content.res.Resources"
  abstract="false"
@@ -187145,6 +203443,61 @@
  visibility="public"
 >
 </method>
+<method name="getRotation"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRotationX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRotationY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getScaleX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getScaleY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getScrollBarStyle"
  return="int"
  abstract="false"
@@ -187290,6 +203643,28 @@
  visibility="public"
 >
 </method>
+<method name="getTranslationX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getTranslationY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getVerticalFadingEdgeLength"
  return="int"
  abstract="false"
@@ -187391,6 +203766,28 @@
 <parameter name="outRect" type="android.graphics.Rect">
 </parameter>
 </method>
+<method name="getX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="hasFocus"
  return="boolean"
  abstract="false"
@@ -187523,6 +203920,17 @@
 <parameter name="drawable" type="android.graphics.drawable.Drawable">
 </parameter>
 </method>
+<method name="isActivated"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isClickable"
  return="boolean"
  abstract="false"
@@ -187611,6 +204019,17 @@
  visibility="public"
 >
 </method>
+<method name="isHardwareAccelerated"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isHorizontalFadingEdgeEnabled"
  return="boolean"
  abstract="false"
@@ -187721,6 +204140,17 @@
  visibility="public"
 >
 </method>
+<method name="isSaveFromParentEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isScrollbarFadingEnabled"
  return="boolean"
  abstract="false"
@@ -187787,6 +204217,17 @@
  visibility="public"
 >
 </method>
+<method name="jumpDrawablesToCurrentState"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="layout"
  return="void"
  abstract="false"
@@ -187982,6 +204423,19 @@
 <parameter name="hint" type="int">
 </parameter>
 </method>
+<method name="onDragEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.DragEvent">
+</parameter>
+</method>
 <method name="onDraw"
  return="void"
  abstract="false"
@@ -188188,25 +204642,6 @@
 <parameter name="heightMeasureSpec" type="int">
 </parameter>
 </method>
-<method name="onOverScrolled"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="scrollX" type="int">
-</parameter>
-<parameter name="scrollY" type="int">
-</parameter>
-<parameter name="clampedX" type="boolean">
-</parameter>
-<parameter name="clampedY" type="boolean">
-</parameter>
-</method>
 <method name="onRestoreInstanceState"
  return="void"
  abstract="false"
@@ -188360,35 +204795,6 @@
 <parameter name="visibility" type="int">
 </parameter>
 </method>
-<method name="overScrollBy"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="deltaX" type="int">
-</parameter>
-<parameter name="deltaY" type="int">
-</parameter>
-<parameter name="scrollX" type="int">
-</parameter>
-<parameter name="scrollY" type="int">
-</parameter>
-<parameter name="scrollRangeX" type="int">
-</parameter>
-<parameter name="scrollRangeY" type="int">
-</parameter>
-<parameter name="maxOverScrollX" type="int">
-</parameter>
-<parameter name="maxOverScrollY" type="int">
-</parameter>
-<parameter name="isTouchEvent" type="boolean">
-</parameter>
-</method>
 <method name="performClick"
  return="boolean"
  abstract="false"
@@ -188568,6 +204974,19 @@
 <parameter name="action" type="java.lang.Runnable">
 </parameter>
 </method>
+<method name="removeOnLayoutChangeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.view.View.OnLayoutChangeListener">
+</parameter>
+</method>
 <method name="requestFocus"
  return="boolean"
  abstract="false"
@@ -188771,6 +205190,32 @@
 <parameter name="event" type="android.view.accessibility.AccessibilityEvent">
 </parameter>
 </method>
+<method name="setActivated"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="activated" type="boolean">
+</parameter>
+</method>
+<method name="setAlpha"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="alpha" type="float">
+</parameter>
+</method>
 <method name="setAnimation"
  return="void"
  abstract="false"
@@ -188823,6 +205268,19 @@
 <parameter name="resid" type="int">
 </parameter>
 </method>
+<method name="setBottom"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bottom" type="int">
+</parameter>
+</method>
 <method name="setClickable"
  return="void"
  abstract="false"
@@ -189044,6 +205502,19 @@
 <parameter name="params" type="android.view.ViewGroup.LayoutParams">
 </parameter>
 </method>
+<method name="setLeft"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="left" type="int">
+</parameter>
+</method>
 <method name="setLongClickable"
  return="void"
  abstract="false"
@@ -189176,6 +205647,19 @@
 <parameter name="l" type="android.view.View.OnCreateContextMenuListener">
 </parameter>
 </method>
+<method name="setOnDragListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="l" type="android.view.View.OnDragListener">
+</parameter>
+</method>
 <method name="setOnFocusChangeListener"
  return="void"
  abstract="false"
@@ -189228,19 +205712,6 @@
 <parameter name="l" type="android.view.View.OnTouchListener">
 </parameter>
 </method>
-<method name="setOverScrollMode"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="overScrollMode" type="int">
-</parameter>
-</method>
 <method name="setPadding"
  return="void"
  abstract="false"
@@ -189260,6 +205731,32 @@
 <parameter name="bottom" type="int">
 </parameter>
 </method>
+<method name="setPivotX"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pivotX" type="float">
+</parameter>
+</method>
+<method name="setPivotY"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pivotY" type="float">
+</parameter>
+</method>
 <method name="setPressed"
  return="void"
  abstract="false"
@@ -189273,6 +205770,58 @@
 <parameter name="pressed" type="boolean">
 </parameter>
 </method>
+<method name="setRight"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="right" type="int">
+</parameter>
+</method>
+<method name="setRotation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rotation" type="float">
+</parameter>
+</method>
+<method name="setRotationX"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rotationX" type="float">
+</parameter>
+</method>
+<method name="setRotationY"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rotationY" type="float">
+</parameter>
+</method>
 <method name="setSaveEnabled"
  return="void"
  abstract="false"
@@ -189286,6 +205835,45 @@
 <parameter name="enabled" type="boolean">
 </parameter>
 </method>
+<method name="setSaveFromParentEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enabled" type="boolean">
+</parameter>
+</method>
+<method name="setScaleX"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="scaleX" type="float">
+</parameter>
+</method>
+<method name="setScaleY"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="scaleY" type="float">
+</parameter>
+</method>
 <method name="setScrollBarStyle"
  return="void"
  abstract="false"
@@ -189379,6 +205967,19 @@
 <parameter name="tag" type="java.lang.Object">
 </parameter>
 </method>
+<method name="setTop"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="top" type="int">
+</parameter>
+</method>
 <method name="setTouchDelegate"
  return="void"
  abstract="false"
@@ -189392,6 +205993,32 @@
 <parameter name="delegate" type="android.view.TouchDelegate">
 </parameter>
 </method>
+<method name="setTranslationX"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="translationX" type="float">
+</parameter>
+</method>
+<method name="setTranslationY"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="translationY" type="float">
+</parameter>
+</method>
 <method name="setVerticalFadingEdgeEnabled"
  return="void"
  abstract="false"
@@ -189457,6 +206084,32 @@
 <parameter name="willNotDraw" type="boolean">
 </parameter>
 </method>
+<method name="setX"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="x" type="float">
+</parameter>
+</method>
+<method name="setY"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="y" type="float">
+</parameter>
+</method>
 <method name="showContextMenu"
  return="boolean"
  abstract="false"
@@ -189468,6 +206121,19 @@
  visibility="public"
 >
 </method>
+<method name="startActionMode"
+ return="android.view.ActionMode"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.view.ActionMode.Callback">
+</parameter>
+</method>
 <method name="startAnimation"
  return="void"
  abstract="false"
@@ -189481,6 +206147,23 @@
 <parameter name="animation" type="android.view.animation.Animation">
 </parameter>
 </method>
+<method name="startDrag"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="android.content.ClipData">
+</parameter>
+<parameter name="thumbBuilder" type="android.view.View.DragThumbnailBuilder">
+</parameter>
+<parameter name="myWindowOnly" type="boolean">
+</parameter>
+</method>
 <method name="unscheduleDrawable"
  return="void"
  abstract="false"
@@ -189863,39 +206546,6 @@
  visibility="public"
 >
 </field>
-<field name="OVER_SCROLL_ALWAYS"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="OVER_SCROLL_IF_CONTENT_SCROLLS"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="OVER_SCROLL_NEVER"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET"
  type="int[]"
  transient="false"
@@ -190211,6 +206861,64 @@
 >
 </field>
 </class>
+<class name="View.DragThumbnailBuilder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="View.DragThumbnailBuilder"
+ type="android.view.View.DragThumbnailBuilder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</constructor>
+<method name="getView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onDrawThumbnail"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="canvas" type="android.graphics.Canvas">
+</parameter>
+</method>
+<method name="onProvideThumbnailMetrics"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="thumbnailSize" type="android.graphics.Point">
+</parameter>
+<parameter name="thumbnailTouchPoint" type="android.graphics.Point">
+</parameter>
+</method>
+</class>
 <class name="View.MeasureSpec"
  extends="java.lang.Object"
  abstract="false"
@@ -190361,6 +207069,29 @@
 </parameter>
 </method>
 </interface>
+<interface name="View.OnDragListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onDrag"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="v" type="android.view.View">
+</parameter>
+<parameter name="event" type="android.view.DragEvent">
+</parameter>
+</method>
+</interface>
 <interface name="View.OnFocusChangeListener"
  abstract="true"
  static="true"
@@ -190409,6 +207140,43 @@
 </parameter>
 </method>
 </interface>
+<interface name="View.OnLayoutChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onLayoutChange"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="v" type="android.view.View">
+</parameter>
+<parameter name="left" type="int">
+</parameter>
+<parameter name="top" type="int">
+</parameter>
+<parameter name="right" type="int">
+</parameter>
+<parameter name="bottom" type="int">
+</parameter>
+<parameter name="oldLeft" type="int">
+</parameter>
+<parameter name="oldTop" type="int">
+</parameter>
+<parameter name="oldRight" type="int">
+</parameter>
+<parameter name="oldBottom" type="int">
+</parameter>
+</method>
+</interface>
 <interface name="View.OnLongClickListener"
  abstract="true"
  static="true"
@@ -190658,28 +207426,6 @@
  visibility="public"
 >
 </method>
-<method name="getScaledOverflingDistance"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getScaledOverscrollDistance"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getScaledPagingTouchSlop"
  return="int"
  abstract="false"
@@ -191434,6 +208180,19 @@
 <parameter name="container" type="android.util.SparseArray&lt;android.os.Parcelable&gt;">
 </parameter>
 </method>
+<method name="dispatchSetActivated"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="activated" type="boolean">
+</parameter>
+</method>
 <method name="dispatchSetSelected"
  return="void"
  abstract="false"
@@ -191477,6 +208236,19 @@
 <parameter name="drawingTime" type="long">
 </parameter>
 </method>
+<method name="endViewTransition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
 <method name="focusSearch"
  return="android.view.View"
  abstract="false"
@@ -191687,6 +208459,17 @@
  visibility="public"
 >
 </method>
+<method name="getLayoutTransition"
+ return="android.animation.LayoutTransition"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getPersistentDrawingCache"
  return="int"
  abstract="false"
@@ -191785,6 +208568,17 @@
  visibility="protected"
 >
 </method>
+<method name="isMotionEventSplittingEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="measureChild"
  return="void"
  abstract="false"
@@ -192248,6 +209042,32 @@
 <parameter name="animationListener" type="android.view.animation.Animation.AnimationListener">
 </parameter>
 </method>
+<method name="setLayoutTransition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transition" type="android.animation.LayoutTransition">
+</parameter>
+</method>
+<method name="setMotionEventSplittingEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="split" type="boolean">
+</parameter>
+</method>
 <method name="setOnHierarchyChangeListener"
  return="void"
  abstract="false"
@@ -192300,6 +209120,21 @@
 <parameter name="originalView" type="android.view.View">
 </parameter>
 </method>
+<method name="startActionModeForChild"
+ return="android.view.ActionMode"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="originalView" type="android.view.View">
+</parameter>
+<parameter name="callback" type="android.view.ActionMode.Callback">
+</parameter>
+</method>
 <method name="startLayoutAnimation"
  return="void"
  abstract="false"
@@ -192311,6 +209146,19 @@
  visibility="public"
 >
 </method>
+<method name="startViewTransition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+</method>
 <method name="updateViewLayout"
  return="void"
  abstract="false"
@@ -192990,6 +209838,21 @@
 <parameter name="originalView" type="android.view.View">
 </parameter>
 </method>
+<method name="startActionModeForChild"
+ return="android.view.ActionMode"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="originalView" type="android.view.View">
+</parameter>
+<parameter name="callback" type="android.view.ActionMode.Callback">
+</parameter>
+</method>
 </interface>
 <class name="ViewStub"
  extends="android.view.View"
@@ -193666,6 +210529,19 @@
  visibility="public"
 >
 </method>
+<method name="hasFeature"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="feature" type="int">
+</parameter>
+</method>
 <method name="hasSoftInputMode"
  return="boolean"
  abstract="false"
@@ -193677,6 +210553,19 @@
  visibility="protected"
 >
 </method>
+<method name="invalidatePanelMenu"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="featureId" type="int">
+</parameter>
+</method>
 <method name="isActive"
  return="boolean"
  abstract="false"
@@ -194238,6 +211127,25 @@
 <parameter name="appName" type="java.lang.String">
 </parameter>
 </method>
+<method name="setWindowManager"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="wm" type="android.view.WindowManager">
+</parameter>
+<parameter name="appToken" type="android.os.IBinder">
+</parameter>
+<parameter name="appName" type="java.lang.String">
+</parameter>
+<parameter name="hardwareAccelerated" type="boolean">
+</parameter>
+</method>
 <method name="superDispatchKeyEvent"
  return="boolean"
  abstract="true"
@@ -194342,6 +211250,39 @@
  visibility="protected"
 >
 </field>
+<field name="FEATURE_ACTION_BAR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FEATURE_ACTION_BAR_OVERLAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FEATURE_ACTION_MODE_OVERLAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FEATURE_CONTEXT_MENU"
  type="int"
  transient="false"
@@ -194723,6 +211664,19 @@
  visibility="public"
 >
 </method>
+<method name="onStartActionMode"
+ return="android.view.ActionMode"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="callback" type="android.view.ActionMode.Callback">
+</parameter>
+</method>
 <method name="onWindowAttributesChanged"
  return="void"
  abstract="true"
@@ -195212,6 +212166,17 @@
  visibility="public"
 >
 </field>
+<field name="FLAG_HARDWARE_ACCELERATED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-2147483648"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="FLAG_IGNORE_CHEEK_PRESSES"
  type="int"
  transient="false"
@@ -195509,6 +212474,17 @@
  visibility="public"
 >
 </field>
+<field name="SOFT_INPUT_ADJUST_NOTHING"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="48"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="SOFT_INPUT_ADJUST_PAN"
  type="int"
  transient="false"
@@ -197098,6 +214074,17 @@
  visibility="public"
 >
 </method>
+<method name="getScaleFactor"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
 <method name="getStartOffset"
  return="long"
  abstract="false"
@@ -197135,6 +214122,23 @@
 <parameter name="outTransformation" type="android.view.animation.Transformation">
 </parameter>
 </method>
+<method name="getTransformation"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="currentTime" type="long">
+</parameter>
+<parameter name="outTransformation" type="android.view.animation.Transformation">
+</parameter>
+<parameter name="scale" type="float">
+</parameter>
+</method>
 <method name="getZAdjustment"
  return="int"
  abstract="false"
@@ -198420,19 +215424,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<method name="getInterpolation"
- return="float"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="input" type="float">
-</parameter>
-</method>
+<implements name="android.animation.TimeInterpolator">
+</implements>
 </interface>
 <class name="LayoutAnimationController"
  extends="java.lang.Object"
@@ -199806,6 +216799,19 @@
 <parameter name="prefix" type="java.lang.String">
 </parameter>
 </method>
+<method name="makeCompatible"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="targetSdkVersion" type="int">
+</parameter>
+</method>
 <method name="writeToParcel"
  return="void"
  abstract="false"
@@ -199875,6 +216881,17 @@
  visibility="public"
 >
 </field>
+<field name="IME_ACTION_PREVIOUS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="IME_ACTION_SEARCH"
  type="int"
  transient="false"
@@ -199908,6 +216925,28 @@
  visibility="public"
 >
 </field>
+<field name="IME_FLAG_NAVIGATE_NEXT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="134217728"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="IME_FLAG_NAVIGATE_PREVIOUS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="67108864"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="IME_FLAG_NO_ACCESSORY_ACTION"
  type="int"
  transient="false"
@@ -199941,6 +216980,17 @@
  visibility="public"
 >
 </field>
+<field name="IME_FLAG_NO_FULLSCREEN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="33554432"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="IME_MASK_ACTION"
  type="int"
  transient="false"
@@ -201108,6 +218158,19 @@
 <parameter name="binding" type="android.view.inputmethod.InputBinding">
 </parameter>
 </method>
+<method name="changeInputMethodSubtype"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="subtype" type="android.view.inputmethod.InputMethodSubtype">
+</parameter>
+</method>
 <method name="createSession"
  return="void"
  abstract="true"
@@ -201431,6 +218494,17 @@
  visibility="public"
 >
 </method>
+<method name="getSubtypes"
+ return="java.util.ArrayList&lt;android.view.inputmethod.InputMethodSubtype&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="loadIcon"
  return="android.graphics.drawable.Drawable"
  abstract="false"
@@ -201506,6 +218580,17 @@
 <parameter name="completions" type="android.view.inputmethod.CompletionInfo[]">
 </parameter>
 </method>
+<method name="getCurrentInputMethodSubtype"
+ return="android.view.inputmethod.InputMethodSubtype"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getEnabledInputMethodList"
  return="java.util.List&lt;android.view.inputmethod.InputMethodInfo&gt;"
  abstract="false"
@@ -201692,6 +218777,19 @@
 <parameter name="id" type="java.lang.String">
 </parameter>
 </method>
+<method name="showInputMethodAndSubtypeEnabler"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="topId" type="java.lang.String">
+</parameter>
+</method>
 <method name="showInputMethodPicker"
  return="void"
  abstract="false"
@@ -201703,6 +218801,17 @@
  visibility="public"
 >
 </method>
+<method name="showInputMethodSubtypePicker"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="showSoftInput"
  return="boolean"
  abstract="false"
@@ -202117,6 +219226,108 @@
 </parameter>
 </method>
 </interface>
+<class name="InputMethodSubtype"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExtraValue"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getIconResId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getLocale"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMode"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getNameResId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="parcelableFlags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 </package>
 <package name="android.webkit"
 >
@@ -204498,6 +221709,17 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<method name="enableSmoothTransition"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getAllowFileAccess"
  return="boolean"
  abstract="false"
@@ -204780,7 +222002,7 @@
  synchronized="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -204872,17 +222094,6 @@
  visibility="public"
 >
 </method>
-<method name="getUseWebViewBackgroundForOverscrollBackground"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="getUseWideViewPort"
  return="boolean"
  abstract="false"
@@ -205124,6 +222335,19 @@
 <parameter name="flag" type="boolean">
 </parameter>
 </method>
+<method name="setEnableSmoothTransition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enable" type="boolean">
+</parameter>
+</method>
 <method name="setFantasyFontFamily"
  return="void"
  abstract="false"
@@ -205326,7 +222550,7 @@
  synchronized="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="flag" type="boolean">
@@ -205475,19 +222699,6 @@
 <parameter name="use" type="boolean">
 </parameter>
 </method>
-<method name="setUseWebViewBackgroundForOverscrollBackground"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="view" type="boolean">
-</parameter>
-</method>
 <method name="setUseWideViewPort"
  return="void"
  abstract="false"
@@ -206069,6 +223280,22 @@
 <parameter name="defStyle" type="int">
 </parameter>
 </constructor>
+<constructor name="WebView"
+ type="android.webkit.WebView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyle" type="int">
+</parameter>
+<parameter name="privateBrowsing" type="boolean">
+</parameter>
+</constructor>
 <method name="addJavascriptInterface"
  return="void"
  abstract="false"
@@ -206119,6 +223346,28 @@
  visibility="public"
 >
 </method>
+<method name="canZoomIn"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="canZoomOut"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="capturePicture"
  return="android.graphics.Picture"
  abstract="false"
@@ -206478,6 +223727,17 @@
  visibility="public"
 >
 </method>
+<method name="getVisibleTitleHeight"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getZoomControls"
  return="android.view.View"
  abstract="false"
@@ -206535,6 +223795,17 @@
  visibility="public"
 >
 </method>
+<method name="isPrivateBrowsingEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="loadData"
  return="void"
  abstract="false"
@@ -206854,6 +224125,36 @@
 <parameter name="outState" type="android.os.Bundle">
 </parameter>
 </method>
+<method name="saveWebArchive"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="filename" type="java.lang.String">
+</parameter>
+</method>
+<method name="saveWebArchive"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="basename" type="java.lang.String">
+</parameter>
+<parameter name="autoname" type="boolean">
+</parameter>
+<parameter name="callback" type="android.webkit.ValueCallback&lt;java.lang.String&gt;">
+</parameter>
+</method>
 <method name="setCertificate"
  return="void"
  abstract="false"
@@ -207003,6 +224304,19 @@
 <parameter name="client" type="android.webkit.WebViewClient">
 </parameter>
 </method>
+<method name="showFindDialog"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.String">
+</parameter>
+</method>
 <method name="stopLoading"
  return="void"
  abstract="false"
@@ -207451,7 +224765,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <parameter name="view" type="android.webkit.WebView">
@@ -207858,6 +225172,17 @@
 <parameter name="after" type="int">
 </parameter>
 </method>
+<method name="clearChoices"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="clearTextFilter"
  return="void"
  abstract="false"
@@ -207880,6 +225205,61 @@
  visibility="public"
 >
 </method>
+<method name="getCheckedItemCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCheckedItemIds"
+ return="long[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCheckedItemPosition"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCheckedItemPositions"
+ return="android.util.SparseBooleanArray"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getChoiceMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getListPaddingBottom"
  return="int"
  abstract="false"
@@ -208023,6 +225403,19 @@
  visibility="protected"
 >
 </method>
+<method name="isItemChecked"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
 <method name="isScrollingCacheEnabled"
  return="boolean"
  abstract="false"
@@ -208102,6 +225495,28 @@
  visibility="public"
 >
 </method>
+<method name="onRemoteAdapterConnected"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRemoteAdapterDisconnected"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="onRestoreInstanceState"
  return="void"
  abstract="false"
@@ -208201,6 +225616,19 @@
 <parameter name="views" type="java.util.List&lt;android.view.View&gt;">
 </parameter>
 </method>
+<method name="setAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.widget.ListAdapter">
+</parameter>
+</method>
 <method name="setCacheColorHint"
  return="void"
  abstract="false"
@@ -208214,6 +225642,19 @@
 <parameter name="color" type="int">
 </parameter>
 </method>
+<method name="setChoiceMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="choiceMode" type="int">
+</parameter>
+</method>
 <method name="setDrawSelectorOnTop"
  return="void"
  abstract="false"
@@ -208253,6 +225694,47 @@
 <parameter name="filterText" type="java.lang.String">
 </parameter>
 </method>
+<method name="setFriction"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="friction" type="float">
+</parameter>
+</method>
+<method name="setItemChecked"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+<parameter name="value" type="boolean">
+</parameter>
+</method>
+<method name="setMultiChoiceModeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.AbsListView.MultiChoiceModeListener">
+</parameter>
+</method>
 <method name="setOnScrollListener"
  return="void"
  abstract="false"
@@ -208279,6 +225761,19 @@
 <parameter name="listener" type="android.widget.AbsListView.RecyclerListener">
 </parameter>
 </method>
+<method name="setRemoteViewsAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
 <method name="setScrollIndicators"
  return="void"
  abstract="false"
@@ -208385,6 +225880,19 @@
 <parameter name="mode" type="int">
 </parameter>
 </method>
+<method name="setVelocityScale"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="scale" type="float">
+</parameter>
+</method>
 <method name="smoothScrollBy"
  return="void"
  abstract="false"
@@ -208428,6 +225936,38 @@
 <parameter name="boundPosition" type="int">
 </parameter>
 </method>
+<method name="smoothScrollToPositionFromTop"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+<parameter name="offset" type="int">
+</parameter>
+<parameter name="duration" type="int">
+</parameter>
+</method>
+<method name="smoothScrollToPositionFromTop"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+<parameter name="offset" type="int">
+</parameter>
+</method>
 <method name="verifyDrawable"
  return="boolean"
  abstract="false"
@@ -208441,6 +225981,50 @@
 <parameter name="dr" type="android.graphics.drawable.Drawable">
 </parameter>
 </method>
+<field name="CHOICE_MODE_MULTIPLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_MULTIPLE_MODAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHOICE_MODE_SINGLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="TRANSCRIPT_MODE_ALWAYS_SCROLL"
  type="int"
  transient="false"
@@ -208532,6 +226116,35 @@
 </parameter>
 </constructor>
 </class>
+<interface name="AbsListView.MultiChoiceModeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.view.ActionMode.Callback">
+</implements>
+<method name="onItemCheckedStateChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="android.view.ActionMode">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+<parameter name="id" type="long">
+</parameter>
+<parameter name="checked" type="boolean">
+</parameter>
+</method>
+</interface>
 <interface name="AbsListView.OnScrollListener"
  abstract="true"
  static="true"
@@ -208628,6 +226241,27 @@
 </parameter>
 </method>
 </interface>
+<interface name="AbsListView.SelectionBoundsAdjuster"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="adjustListItemSelectionBounds"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bounds" type="android.graphics.Rect">
+</parameter>
+</method>
+</interface>
 <class name="AbsSeekBar"
  extends="android.widget.ProgressBar"
  abstract="true"
@@ -209708,6 +227342,582 @@
 </parameter>
 </method>
 </interface>
+<class name="AdapterViewAnimator"
+ extends="android.widget.AdapterView"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AdapterViewAnimator"
+ type="android.widget.AdapterViewAnimator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="AdapterViewAnimator"
+ type="android.widget.AdapterViewAnimator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+<method name="getAdapter"
+ return="android.widget.Adapter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCurrentView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDisplayedChild"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInAnimation"
+ return="android.animation.ObjectAnimator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getOutAnimation"
+ return="android.animation.ObjectAnimator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRemoteAdapterConnected"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRemoteAdapterDisconnected"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRestoreInstanceState"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="state" type="android.os.Parcelable">
+</parameter>
+</method>
+<method name="onSaveInstanceState"
+ return="android.os.Parcelable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.widget.Adapter">
+</parameter>
+</method>
+<method name="setAnimateFirstView"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animate" type="boolean">
+</parameter>
+</method>
+<method name="setDisplayedChild"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="whichChild" type="int">
+</parameter>
+</method>
+<method name="setInAnimation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="inAnimation" type="android.animation.ObjectAnimator">
+</parameter>
+</method>
+<method name="setInAnimation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="resourceID" type="int">
+</parameter>
+</method>
+<method name="setOutAnimation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="outAnimation" type="android.animation.ObjectAnimator">
+</parameter>
+</method>
+<method name="setOutAnimation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="resourceID" type="int">
+</parameter>
+</method>
+<method name="setRemoteViewsAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="setSelection"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="showNext"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="showPrevious"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="AdapterViewFlipper"
+ extends="android.widget.AdapterViewAnimator"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AdapterViewFlipper"
+ type="android.widget.AdapterViewFlipper"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="AdapterViewFlipper"
+ type="android.widget.AdapterViewFlipper"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+<method name="isAutoStart"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isFlipping"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setAutoStart"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="autoStart" type="boolean">
+</parameter>
+</method>
+<method name="setFlipInterval"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="milliseconds" type="int">
+</parameter>
+</method>
+<method name="startFlipping"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="stopFlipping"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="Adapters"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Adapters"
+ type="android.widget.Adapters"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="loadAdapter"
+ return="android.widget.BaseAdapter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="parameters" type="java.lang.Object...">
+</parameter>
+</method>
+<method name="loadCursorAdapter"
+ return="android.widget.CursorAdapter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="uri" type="java.lang.String">
+</parameter>
+<parameter name="parameters" type="java.lang.Object...">
+</parameter>
+</method>
+<method name="loadCursorAdapter"
+ return="android.widget.CursorAdapter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+<parameter name="parameters" type="java.lang.Object...">
+</parameter>
+</method>
+</class>
+<class name="Adapters.CursorBinder"
+ extends="java.lang.Object"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Adapters.CursorBinder"
+ type="android.widget.Adapters.CursorBinder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="transformation" type="android.widget.Adapters.CursorTransformation">
+</parameter>
+</constructor>
+<method name="bind"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
+<field name="mContext"
+ type="android.content.Context"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</field>
+<field name="mTransformation"
+ type="android.widget.Adapters.CursorTransformation"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</field>
+</class>
+<class name="Adapters.CursorTransformation"
+ extends="java.lang.Object"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Adapters.CursorTransformation"
+ type="android.widget.Adapters.CursorTransformation"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<method name="transform"
+ return="java.lang.String"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
+<method name="transformToResource"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
+<field name="mContext"
+ type="android.content.Context"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</field>
+</class>
 <class name="AlphabetIndexer"
  extends="android.database.DataSetObserver"
  abstract="false"
@@ -211695,6 +229905,20 @@
 <parameter name="autoRequery" type="boolean">
 </parameter>
 </constructor>
+<constructor name="CursorAdapter"
+ type="android.widget.CursorAdapter"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="c" type="android.database.Cursor">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</constructor>
 <method name="bindView"
  return="void"
  abstract="true"
@@ -211842,6 +230066,23 @@
 <parameter name="autoRequery" type="boolean">
 </parameter>
 </method>
+<method name="init"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="c" type="android.database.Cursor">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
 <method name="newDropDownView"
  return="android.view.View"
  abstract="false"
@@ -211913,6 +230154,28 @@
 <parameter name="filterQueryProvider" type="android.widget.FilterQueryProvider">
 </parameter>
 </method>
+<field name="FLAG_AUTO_REQUERY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_REGISTER_CONTENT_OBSERVER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <class name="CursorTreeAdapter"
  extends="android.widget.BaseExpandableListAdapter"
@@ -214352,7 +232615,7 @@
  visibility="public"
 >
 </method>
-<method name="getStretchMode"
+<method name="getNumColumns"
  return="int"
  abstract="false"
  native="false"
@@ -214363,8 +232626,8 @@
  visibility="public"
 >
 </method>
-<method name="setAdapter"
- return="void"
+<method name="getStretchMode"
+ return="int"
  abstract="false"
  native="false"
  synchronized="false"
@@ -214373,8 +232636,6 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="adapter" type="android.widget.ListAdapter">
-</parameter>
 </method>
 <method name="setColumnWidth"
  return="void"
@@ -214467,6 +232728,19 @@
 <parameter name="verticalSpacing" type="int">
 </parameter>
 </method>
+<method name="smoothScrollByOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
 <field name="AUTO_FIT"
  type="int"
  transient="false"
@@ -215538,6 +233812,20 @@
 <parameter name="attrs" type="android.util.AttributeSet">
 </parameter>
 </constructor>
+<constructor name="LinearLayout"
+ type="android.widget.LinearLayout"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyle" type="int">
+</parameter>
+</constructor>
 <method name="getBaselineAlignedChildIndex"
  return="int"
  abstract="false"
@@ -215582,6 +233870,17 @@
  visibility="public"
 >
 </method>
+<method name="isMeasureWithLargestChildEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="onLayout"
  return="void"
  abstract="false"
@@ -215655,6 +233954,19 @@
 <parameter name="horizontalGravity" type="int">
 </parameter>
 </method>
+<method name="setMeasureWithLargestChildEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enabled" type="boolean">
+</parameter>
+</method>
 <method name="setOrientation"
  return="void"
  abstract="false"
@@ -215851,6 +234163,691 @@
 </parameter>
 </method>
 </interface>
+<class name="ListPopupWindow"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ListPopupWindow"
+ type="android.widget.ListPopupWindow"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="ListPopupWindow"
+ type="android.widget.ListPopupWindow"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+<constructor name="ListPopupWindow"
+ type="android.widget.ListPopupWindow"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyleAttr" type="int">
+</parameter>
+</constructor>
+<constructor name="ListPopupWindow"
+ type="android.widget.ListPopupWindow"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyleAttr" type="int">
+</parameter>
+<parameter name="defStyleRes" type="int">
+</parameter>
+</constructor>
+<method name="clearListSelection"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="dismiss"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAnchorView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAnimationStyle"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getBackground"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getHeight"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getHorizontalOffset"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInputMethodMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getListView"
+ return="android.widget.ListView"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPromptPosition"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedItem"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedItemId"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedItemPosition"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSelectedView"
+ return="android.view.View"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSoftInputMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getVerticalOffset"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getWidth"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isInputMethodNotNeeded"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isModal"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isShowing"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onKeyDown"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyCode" type="int">
+</parameter>
+<parameter name="event" type="android.view.KeyEvent">
+</parameter>
+</method>
+<method name="onKeyPreIme"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyCode" type="int">
+</parameter>
+<parameter name="event" type="android.view.KeyEvent">
+</parameter>
+</method>
+<method name="onKeyUp"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyCode" type="int">
+</parameter>
+<parameter name="event" type="android.view.KeyEvent">
+</parameter>
+</method>
+<method name="performItemClick"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="postShow"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.widget.ListAdapter">
+</parameter>
+</method>
+<method name="setAnchorView"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="anchor" type="android.view.View">
+</parameter>
+</method>
+<method name="setAnimationStyle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="animationStyle" type="int">
+</parameter>
+</method>
+<method name="setBackgroundDrawable"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="d" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setContentWidth"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="int">
+</parameter>
+</method>
+<method name="setHeight"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="height" type="int">
+</parameter>
+</method>
+<method name="setHorizontalOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
+<method name="setInputMethodMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="int">
+</parameter>
+</method>
+<method name="setListSelector"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="selector" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setModal"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="modal" type="boolean">
+</parameter>
+</method>
+<method name="setOnDismissListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.PopupWindow.OnDismissListener">
+</parameter>
+</method>
+<method name="setOnItemClickListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="clickListener" type="android.widget.AdapterView.OnItemClickListener">
+</parameter>
+</method>
+<method name="setOnItemSelectedListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="selectedListener" type="android.widget.AdapterView.OnItemSelectedListener">
+</parameter>
+</method>
+<method name="setPromptPosition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="setPromptView"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="prompt" type="android.view.View">
+</parameter>
+</method>
+<method name="setSelection"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="setSoftInputMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="int">
+</parameter>
+</method>
+<method name="setVerticalOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
+<method name="setWidth"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="int">
+</parameter>
+</method>
+<method name="show"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<field name="INPUT_METHOD_FROM_FOCUSABLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INPUT_METHOD_NEEDED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INPUT_METHOD_NOT_NEEDED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MATCH_PARENT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="POSITION_PROMPT_ABOVE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="POSITION_PROMPT_BELOW"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WRAP_CONTENT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
 <class name="ListView"
  extends="android.widget.AbsListView"
  abstract="false"
@@ -215955,17 +234952,6 @@
 <parameter name="v" type="android.view.View">
 </parameter>
 </method>
-<method name="clearChoices"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
 <method name="findViewTraversal"
  return="android.view.View"
  abstract="false"
@@ -216010,51 +234996,7 @@
  synchronized="false"
  static="false"
  final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCheckedItemIds"
- return="long[]"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCheckedItemPosition"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCheckedItemPositions"
- return="android.util.SparseBooleanArray"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getChoiceMode"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 </method>
@@ -216124,41 +235066,6 @@
  visibility="public"
 >
 </method>
-<method name="getOverscrollFooter"
- return="android.graphics.drawable.Drawable"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getOverscrollHeader"
- return="android.graphics.drawable.Drawable"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isItemChecked"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="position" type="int">
-</parameter>
-</method>
 <method name="removeFooterView"
  return="boolean"
  abstract="false"
@@ -216185,32 +235092,6 @@
 <parameter name="v" type="android.view.View">
 </parameter>
 </method>
-<method name="setAdapter"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="adapter" type="android.widget.ListAdapter">
-</parameter>
-</method>
-<method name="setChoiceMode"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="choiceMode" type="int">
-</parameter>
-</method>
 <method name="setDivider"
  return="void"
  abstract="false"
@@ -216263,21 +235144,6 @@
 <parameter name="headerDividersEnabled" type="boolean">
 </parameter>
 </method>
-<method name="setItemChecked"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="position" type="int">
-</parameter>
-<parameter name="value" type="boolean">
-</parameter>
-</method>
 <method name="setItemsCanFocus"
  return="void"
  abstract="false"
@@ -216291,32 +235157,6 @@
 <parameter name="itemsCanFocus" type="boolean">
 </parameter>
 </method>
-<method name="setOverscrollFooter"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="footer" type="android.graphics.drawable.Drawable">
-</parameter>
-</method>
-<method name="setOverscrollHeader"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="header" type="android.graphics.drawable.Drawable">
-</parameter>
-</method>
 <method name="setSelection"
  return="void"
  abstract="false"
@@ -216356,39 +235196,19 @@
 <parameter name="y" type="int">
 </parameter>
 </method>
-<field name="CHOICE_MODE_MULTIPLE"
- type="int"
- transient="false"
- volatile="false"
- value="2"
- static="true"
- final="true"
+<method name="smoothScrollByOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
  deprecated="not deprecated"
  visibility="public"
 >
-</field>
-<field name="CHOICE_MODE_NONE"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="CHOICE_MODE_SINGLE"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
+<parameter name="offset" type="int">
+</parameter>
+</method>
 </class>
 <class name="ListView.FixedViewInfo"
  extends="java.lang.Object"
@@ -216888,7 +235708,7 @@
 </parameter>
 </method>
 </interface>
-<class name="OverScroller"
+<class name="PopupMenu"
  extends="java.lang.Object"
  abstract="false"
  static="false"
@@ -216896,8 +235716,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<constructor name="OverScroller"
- type="android.widget.OverScroller"
+<constructor name="PopupMenu"
+ type="android.widget.PopupMenu"
  static="false"
  final="false"
  deprecated="not deprecated"
@@ -216905,36 +235725,10 @@
 >
 <parameter name="context" type="android.content.Context">
 </parameter>
-</constructor>
-<constructor name="OverScroller"
- type="android.widget.OverScroller"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="interpolator" type="android.view.animation.Interpolator">
+<parameter name="anchor" type="android.view.View">
 </parameter>
 </constructor>
-<constructor name="OverScroller"
- type="android.widget.OverScroller"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="interpolator" type="android.view.animation.Interpolator">
-</parameter>
-<parameter name="bounceCoefficientX" type="float">
-</parameter>
-<parameter name="bounceCoefficientY" type="float">
-</parameter>
-</constructor>
-<method name="abortAnimation"
+<method name="dismiss"
  return="void"
  abstract="false"
  native="false"
@@ -216945,8 +235739,8 @@
  visibility="public"
 >
 </method>
-<method name="computeScrollOffset"
- return="boolean"
+<method name="getMenu"
+ return="android.view.Menu"
  abstract="false"
  native="false"
  synchronized="false"
@@ -216956,7 +235750,18 @@
  visibility="public"
 >
 </method>
-<method name="fling"
+<method name="getMenuInflater"
+ return="android.view.MenuInflater"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setOnMenuItemClickListener"
  return="void"
  abstract="false"
  native="false"
@@ -216966,24 +235771,10 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="startX" type="int">
-</parameter>
-<parameter name="startY" type="int">
-</parameter>
-<parameter name="velocityX" type="int">
-</parameter>
-<parameter name="velocityY" type="int">
-</parameter>
-<parameter name="minX" type="int">
-</parameter>
-<parameter name="maxX" type="int">
-</parameter>
-<parameter name="minY" type="int">
-</parameter>
-<parameter name="maxY" type="int">
+<parameter name="listener" type="android.widget.PopupMenu.OnMenuItemClickListener">
 </parameter>
 </method>
-<method name="fling"
+<method name="show"
  return="void"
  abstract="false"
  native="false"
@@ -216993,226 +235784,29 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="startX" type="int">
-</parameter>
-<parameter name="startY" type="int">
-</parameter>
-<parameter name="velocityX" type="int">
-</parameter>
-<parameter name="velocityY" type="int">
-</parameter>
-<parameter name="minX" type="int">
-</parameter>
-<parameter name="maxX" type="int">
-</parameter>
-<parameter name="minY" type="int">
-</parameter>
-<parameter name="maxY" type="int">
-</parameter>
-<parameter name="overX" type="int">
-</parameter>
-<parameter name="overY" type="int">
-</parameter>
-</method>
-<method name="forceFinished"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="finished" type="boolean">
-</parameter>
-</method>
-<method name="getCurrX"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getCurrY"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getFinalX"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getFinalY"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getStartX"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getStartY"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isFinished"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="isOverScrolled"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="notifyHorizontalEdgeReached"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startX" type="int">
-</parameter>
-<parameter name="finalX" type="int">
-</parameter>
-<parameter name="overX" type="int">
-</parameter>
-</method>
-<method name="notifyVerticalEdgeReached"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startY" type="int">
-</parameter>
-<parameter name="finalY" type="int">
-</parameter>
-<parameter name="overY" type="int">
-</parameter>
-</method>
-<method name="springBack"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startX" type="int">
-</parameter>
-<parameter name="startY" type="int">
-</parameter>
-<parameter name="minX" type="int">
-</parameter>
-<parameter name="maxX" type="int">
-</parameter>
-<parameter name="minY" type="int">
-</parameter>
-<parameter name="maxY" type="int">
-</parameter>
-</method>
-<method name="startScroll"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startX" type="int">
-</parameter>
-<parameter name="startY" type="int">
-</parameter>
-<parameter name="dx" type="int">
-</parameter>
-<parameter name="dy" type="int">
-</parameter>
-</method>
-<method name="startScroll"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="startX" type="int">
-</parameter>
-<parameter name="startY" type="int">
-</parameter>
-<parameter name="dx" type="int">
-</parameter>
-<parameter name="dy" type="int">
-</parameter>
-<parameter name="duration" type="int">
-</parameter>
 </method>
 </class>
+<interface name="PopupMenu.OnMenuItemClickListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onMenuItemClick"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="item" type="android.view.MenuItem">
+</parameter>
+</method>
+</interface>
 <class name="PopupWindow"
  extends="java.lang.Object"
  abstract="false"
@@ -217264,6 +235858,22 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyleAttr" type="int">
+</parameter>
+<parameter name="defStyleRes" type="int">
+</parameter>
+</constructor>
+<constructor name="PopupWindow"
+ type="android.widget.PopupWindow"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
 </constructor>
 <constructor name="PopupWindow"
  type="android.widget.PopupWindow"
@@ -218293,6 +236903,17 @@
 <parameter name="excludeMimes" type="java.lang.String[]">
 </parameter>
 </method>
+<method name="setImageToDefault"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="setMode"
  return="void"
  abstract="false"
@@ -219429,6 +238050,21 @@
 <parameter name="value" type="double">
 </parameter>
 </method>
+<method name="setEmptyView"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="emptyViewId" type="int">
+</parameter>
+</method>
 <method name="setFloat"
  return="void"
  abstract="false"
@@ -219508,6 +238144,23 @@
 <parameter name="value" type="int">
 </parameter>
 </method>
+<method name="setIntent"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="methodName" type="java.lang.String">
+</parameter>
+<parameter name="value" type="android.content.Intent">
+</parameter>
+</method>
 <method name="setLong"
  return="void"
  abstract="false"
@@ -219525,6 +238178,21 @@
 <parameter name="value" type="long">
 </parameter>
 </method>
+<method name="setOnClickFillInIntent"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="fillInIntent" type="android.content.Intent">
+</parameter>
+</method>
 <method name="setOnClickPendingIntent"
  return="void"
  abstract="false"
@@ -219540,6 +238208,21 @@
 <parameter name="pendingIntent" type="android.app.PendingIntent">
 </parameter>
 </method>
+<method name="setPendingIntentTemplate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="pendingIntentTemplate" type="android.app.PendingIntent">
+</parameter>
+</method>
 <method name="setProgressBar"
  return="void"
  abstract="false"
@@ -219559,6 +238242,51 @@
 <parameter name="indeterminate" type="boolean">
 </parameter>
 </method>
+<method name="setRelativeScrollPosition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="offset" type="int">
+</parameter>
+</method>
+<method name="setRemoteAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="setScrollPosition"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+<parameter name="position" type="int">
+</parameter>
+</method>
 <method name="setShort"
  return="void"
  abstract="false"
@@ -219655,6 +238383,32 @@
 <parameter name="visibility" type="int">
 </parameter>
 </method>
+<method name="showNext"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+</method>
+<method name="showPrevious"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewId" type="int">
+</parameter>
+</method>
 <method name="writeToParcel"
  return="void"
  abstract="false"
@@ -219721,6 +238475,160 @@
 <implements name="java.lang.annotation.Annotation">
 </implements>
 </class>
+<class name="RemoteViewsService"
+ extends="android.app.Service"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="RemoteViewsService"
+ type="android.widget.RemoteViewsService"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onBind"
+ return="android.os.IBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onGetViewFactory"
+ return="android.widget.RemoteViewsService.RemoteViewsFactory"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+</class>
+<interface name="RemoteViewsService.RemoteViewsFactory"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getItemId"
+ return="long"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="getLoadingView"
+ return="android.widget.RemoteViews"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getViewAt"
+ return="android.widget.RemoteViews"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="getViewTypeCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasStableIds"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onCreate"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onDataSetChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onDestroy"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
 <class name="ResourceCursorAdapter"
  extends="android.widget.CursorAdapter"
  abstract="true"
@@ -219759,6 +238667,22 @@
 <parameter name="autoRequery" type="boolean">
 </parameter>
 </constructor>
+<constructor name="ResourceCursorAdapter"
+ type="android.widget.ResourceCursorAdapter"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="layout" type="int">
+</parameter>
+<parameter name="c" type="android.database.Cursor">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</constructor>
 <method name="newView"
  return="android.view.View"
  abstract="false"
@@ -220335,6 +239259,19 @@
 <parameter name="newY" type="int">
 </parameter>
 </method>
+<method name="setFriction"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="friction" type="float">
+</parameter>
+</method>
 <method name="startScroll"
  return="void"
  abstract="false"
@@ -220387,6 +239324,350 @@
 >
 </method>
 </class>
+<class name="SearchView"
+ extends="android.widget.LinearLayout"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SearchView"
+ type="android.widget.SearchView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="SearchView"
+ type="android.widget.SearchView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+<method name="getSuggestionsAdapter"
+ return="android.widget.CursorAdapter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isIconfiedByDefault"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isIconified"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isQueryRefinementEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isSubmitButtonEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setIconified"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="iconify" type="boolean">
+</parameter>
+</method>
+<method name="setIconifiedByDefault"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="iconified" type="boolean">
+</parameter>
+</method>
+<method name="setMaxWidth"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="maxpixels" type="int">
+</parameter>
+</method>
+<method name="setOnCloseListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.SearchView.OnCloseListener">
+</parameter>
+</method>
+<method name="setOnQueryChangeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.SearchView.OnQueryChangeListener">
+</parameter>
+</method>
+<method name="setOnQueryTextFocusChangeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.view.View.OnFocusChangeListener">
+</parameter>
+</method>
+<method name="setOnSuggestionSelectionListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.SearchView.OnSuggestionSelectionListener">
+</parameter>
+</method>
+<method name="setQuery"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="query" type="java.lang.CharSequence">
+</parameter>
+<parameter name="submit" type="boolean">
+</parameter>
+</method>
+<method name="setQueryHint"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="hint" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setQueryRefinementEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enable" type="boolean">
+</parameter>
+</method>
+<method name="setSearchableInfo"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="searchable" type="android.app.SearchableInfo">
+</parameter>
+</method>
+<method name="setSubmitButtonEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enabled" type="boolean">
+</parameter>
+</method>
+<method name="setSuggestionsAdapter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="adapter" type="android.widget.CursorAdapter">
+</parameter>
+</method>
+</class>
+<interface name="SearchView.OnCloseListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onClose"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
+<interface name="SearchView.OnQueryChangeListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onQueryTextChanged"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="newText" type="java.lang.String">
+</parameter>
+</method>
+<method name="onSubmitQuery"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="query" type="java.lang.String">
+</parameter>
+</method>
+</interface>
+<interface name="SearchView.OnSuggestionSelectionListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onSuggestionClicked"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+<method name="onSuggestionSelected"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="position" type="int">
+</parameter>
+</method>
+</interface>
 <interface name="SectionIndexer"
  abstract="true"
  static="false"
@@ -221770,6 +241051,18 @@
 >
 <parameter name="context" type="android.content.Context">
 </parameter>
+<parameter name="mode" type="int">
+</parameter>
+</constructor>
+<constructor name="Spinner"
+ type="android.widget.Spinner"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
 <parameter name="attrs" type="android.util.AttributeSet">
 </parameter>
 </constructor>
@@ -221787,6 +241080,22 @@
 <parameter name="defStyle" type="int">
 </parameter>
 </constructor>
+<constructor name="Spinner"
+ type="android.widget.Spinner"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyle" type="int">
+</parameter>
+<parameter name="mode" type="int">
+</parameter>
+</constructor>
 <method name="getPrompt"
  return="java.lang.CharSequence"
  abstract="false"
@@ -221839,6 +241148,28 @@
 <parameter name="promptId" type="int">
 </parameter>
 </method>
+<field name="MODE_DIALOG"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODE_DROPDOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 </class>
 <interface name="SpinnerAdapter"
  abstract="true"
@@ -221867,6 +241198,37 @@
 </parameter>
 </method>
 </interface>
+<class name="StackView"
+ extends="android.widget.AdapterViewAnimator"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="StackView"
+ type="android.widget.StackView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="StackView"
+ type="android.widget.StackView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+</class>
 <class name="TabHost"
  extends="android.widget.FrameLayout"
  abstract="false"
@@ -224276,6 +243638,19 @@
 <parameter name="input" type="android.text.method.KeyListener">
 </parameter>
 </method>
+<method name="setLineHeight"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="lineHeight" type="int">
+</parameter>
+</method>
 <method name="setLineSpacing"
  return="void"
  abstract="false"
@@ -226584,7 +245959,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
 </parameter>
 </method>
 </interface>
@@ -231815,7 +251190,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="buffer" type="byte[]">
+<parameter name="dst" type="byte[]">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -231830,11 +251205,11 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="buffer" type="byte[]">
+<parameter name="dst" type="byte[]">
 </parameter>
 <parameter name="offset" type="int">
 </parameter>
-<parameter name="count" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -232075,7 +251450,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="buffer" type="byte[]">
+<parameter name="dst" type="byte[]">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -232090,11 +251465,11 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="buffer" type="byte[]">
+<parameter name="dst" type="byte[]">
 </parameter>
 <parameter name="offset" type="int">
 </parameter>
-<parameter name="length" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -233505,7 +252880,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="fileName" type="java.lang.String">
+<parameter name="path" type="java.lang.String">
 </parameter>
 <exception name="FileNotFoundException" type="java.io.FileNotFoundException">
 </exception>
@@ -233628,7 +253003,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="filename" type="java.lang.String">
+<parameter name="path" type="java.lang.String">
 </parameter>
 <exception name="FileNotFoundException" type="java.io.FileNotFoundException">
 </exception>
@@ -233640,7 +253015,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="filename" type="java.lang.String">
+<parameter name="path" type="java.lang.String">
 </parameter>
 <parameter name="append" type="boolean">
 </parameter>
@@ -234311,7 +253686,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="b" type="byte[]">
+<parameter name="buffer" type="byte[]">
 </parameter>
 <parameter name="offset" type="int">
 </parameter>
@@ -235001,7 +254376,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="buffer" type="byte[]">
+<parameter name="dst" type="byte[]">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -235016,11 +254391,11 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="buffer" type="byte[]">
+<parameter name="dst" type="byte[]">
 </parameter>
 <parameter name="offset" type="int">
 </parameter>
-<parameter name="length" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -238821,7 +258196,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="buffer" type="byte[]">
+<parameter name="dst" type="byte[]">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -238836,11 +258211,11 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="buffer" type="byte[]">
+<parameter name="dst" type="byte[]">
 </parameter>
 <parameter name="offset" type="int">
 </parameter>
-<parameter name="count" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -264993,9 +284368,9 @@
 >
 <parameter name="uri" type="java.net.URI">
 </parameter>
-<parameter name="sa" type="java.net.SocketAddress">
+<parameter name="address" type="java.net.SocketAddress">
 </parameter>
-<parameter name="ioe" type="java.io.IOException">
+<parameter name="failure" type="java.io.IOException">
 </parameter>
 </method>
 <method name="getDefault"
@@ -265094,7 +284469,7 @@
 >
 <parameter name="uri" type="java.net.URI">
 </parameter>
-<parameter name="conn" type="java.net.URLConnection">
+<parameter name="connection" type="java.net.URLConnection">
 </parameter>
 <exception name="IOException" type="java.io.IOException">
 </exception>
@@ -269388,9 +288763,9 @@
 >
 <parameter name="dst" type="byte[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -269634,9 +289009,9 @@
 >
 <parameter name="src" type="byte[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -269873,7 +289248,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="byteCount" type="int">
 </parameter>
 </method>
 </class>
@@ -270106,9 +289481,9 @@
 >
 <parameter name="dst" type="char[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="charCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -270206,9 +289581,9 @@
 >
 <parameter name="src" type="char[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="charCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -270337,7 +289712,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="charCount" type="int">
 </parameter>
 </method>
 <method name="wrap"
@@ -270498,9 +289873,9 @@
 >
 <parameter name="dst" type="double[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="doubleCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -270587,9 +289962,9 @@
 >
 <parameter name="src" type="double[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="doubleCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -270658,7 +290033,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="doubleCount" type="int">
 </parameter>
 </method>
 </class>
@@ -270789,9 +290164,9 @@
 >
 <parameter name="dst" type="float[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="floatCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -270878,9 +290253,9 @@
 >
 <parameter name="src" type="float[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="floatCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -270949,7 +290324,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="floatCount" type="int">
 </parameter>
 </method>
 </class>
@@ -271080,9 +290455,9 @@
 >
 <parameter name="dst" type="int[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="intCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -271169,9 +290544,9 @@
 >
 <parameter name="src" type="int[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="intCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -271240,7 +290615,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="intCount" type="int">
 </parameter>
 </method>
 </class>
@@ -271388,9 +290763,9 @@
 >
 <parameter name="dst" type="long[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="longCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -271477,9 +290852,9 @@
 >
 <parameter name="src" type="long[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="longCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -271548,7 +290923,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="longCount" type="int">
 </parameter>
 </method>
 </class>
@@ -271738,9 +291113,9 @@
 >
 <parameter name="dst" type="short[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="dstOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="shortCount" type="int">
 </parameter>
 </method>
 <method name="get"
@@ -271827,9 +291202,9 @@
 >
 <parameter name="src" type="short[]">
 </parameter>
-<parameter name="off" type="int">
+<parameter name="srcOffset" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="shortCount" type="int">
 </parameter>
 </method>
 <method name="put"
@@ -271898,7 +291273,7 @@
 </parameter>
 <parameter name="start" type="int">
 </parameter>
-<parameter name="len" type="int">
+<parameter name="shortCount" type="int">
 </parameter>
 </method>
 </class>
@@ -413715,7 +433090,7 @@
  abstract="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <method name="getLength"
@@ -414204,7 +433579,7 @@
  abstract="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <method name="characters"
@@ -414419,7 +433794,7 @@
  abstract="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <implements name="org.xml.sax.DTDHandler">
@@ -414886,7 +434261,7 @@
  abstract="true"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <method name="parse"
@@ -416318,7 +435693,7 @@
  abstract="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <implements name="org.xml.sax.AttributeList">
@@ -417807,7 +437182,7 @@
  abstract="false"
  static="false"
  final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
  visibility="public"
 >
 <method name="makeParser"
diff --git a/cmds/am/Android.mk b/cmds/am/Android.mk
index 170849d..8b321b3 100644
--- a/cmds/am/Android.mk
+++ b/cmds/am/Android.mk
@@ -12,3 +12,20 @@
 $(TARGET_OUT)/bin/am : $(LOCAL_PATH)/am | $(ACP)
 	$(transform-prebuilt-to-target)
 
+NOTICE_FILE := NOTICE
+files_noticed := bin/am
+
+# Generate rules for a single file. The argument is the file path relative to
+# the installation root
+define make-notice-file
+
+$(TARGET_OUT_NOTICE_FILES)/src/$(1).txt: $(LOCAL_PATH)/$(NOTICE_FILE)
+	@echo Notice file: $$< -- $$@
+	@mkdir -p $$(dir $$@)
+	@cat $$< >> $$@
+
+$(TARGET_OUT_NOTICE_FILES)/hash-timestamp: $(TARGET_OUT_NOTICE_FILES)/src/$(1).txt
+
+endef
+
+$(foreach file,$(files_noticed),$(eval $(call make-notice-file,$(file))))
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index f2aa91f..b073004 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -102,6 +102,8 @@
             sendBroadcast();
         } else if (op.equals("profile")) {
             runProfile();
+        } else if (op.equals("dumpheap")) {
+            runDumpHeap();
         } else if (op.equals("monitor")) {
             runMonitor();
         } else {
@@ -146,6 +148,31 @@
                 String value = nextArgRequired();
                 intent.putExtra(key, Integer.valueOf(value));
                 hasIntentInfo = true;
+            } else if (opt.equals("--eia")) {
+                String key = nextArgRequired();
+                String value = nextArgRequired();
+                String[] strings = value.split(",");
+                int[] list = new int[strings.length];
+                for (int i = 0; i < strings.length; i++) {
+                    list[i] = Integer.valueOf(strings[i]);
+                }
+                intent.putExtra(key, list);
+                hasIntentInfo = true;
+            } else if (opt.equals("--el")) {
+                String key = nextArgRequired();
+                String value = nextArgRequired();
+                intent.putExtra(key, Long.valueOf(value));
+                hasIntentInfo = true;
+            } else if (opt.equals("--ela")) {
+                String key = nextArgRequired();
+                String value = nextArgRequired();
+                String[] strings = value.split(",");
+                long[] list = new long[strings.length];
+                for (int i = 0; i < strings.length; i++) {
+                    list[i] = Long.valueOf(strings[i]);
+                }
+                intent.putExtra(key, list);
+                hasIntentInfo = true;
             } else if (opt.equals("--ez")) {
                 String key = nextArgRequired();
                 String value = nextArgRequired();
@@ -430,6 +457,28 @@
         }
     }
 
+    private void runDumpHeap() throws Exception {
+        boolean managed = !"-n".equals(nextOption());
+        String process = nextArgRequired();
+        String heapFile = nextArgRequired();
+        ParcelFileDescriptor fd = null;
+
+        try {
+            fd = ParcelFileDescriptor.open(
+                    new File(heapFile),
+                    ParcelFileDescriptor.MODE_CREATE |
+                    ParcelFileDescriptor.MODE_TRUNCATE |
+                    ParcelFileDescriptor.MODE_READ_WRITE);
+        } catch (FileNotFoundException e) {
+            System.err.println("Error: Unable to open file: " + heapFile);
+            return;
+        }
+
+        if (!mAm.dumpHeap(process, managed, heapFile, fd)) {
+            throw new AndroidException("HEAP DUMP FAILED on process " + process);
+        }
+    }
+
     class MyActivityController extends IActivityController.Stub {
         final String mGdbPort;
 
@@ -894,8 +943,15 @@
                 "        -p <FILE>: write profiling data to <FILE>\n" +
                 "        -w: wait for instrumentation to finish before returning\n" +
                 "\n" +
+                "    run a test package against an application: am instrument [flags] <TEST_PACKAGE>/<RUNNER_CLASS>\n" +
+                "        -e <testrunner_flag> <testrunner_value> [,<testrunner_value>]\n" +
+                "        -w wait for the test to finish (required)\n" +
+                "        -r use with -e perf true to generate raw output for performance measurements\n" +
+                "\n" +
                 "    start profiling: am profile <PROCESS> start <FILE>\n" +
                 "    stop profiling: am profile <PROCESS> stop\n" +
+                "    dump heap: am dumpheap [flags] <PROCESS> <FILE>\n" +
+                "        -n: dump native heap instead of managed heap\n" +
                 "\n" +
                 "    start monitoring: am monitor [--gdb <port>]\n" +
                 "        --gdb: start gdbserv on the given port at crash/ANR\n" +
@@ -906,7 +962,10 @@
                 "        [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
                 "        [--esn <EXTRA_KEY> ...]\n" +
                 "        [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
-                "        [-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
+                "        [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
+                "        [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
+                "        [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
+                "        [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
                 "        [-n <COMPONENT>] [-f <FLAGS>]\n" +
                 "        [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
                 "        [--debug-log-resolution]\n" +
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 7decf9a..0159edd 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -171,9 +171,9 @@
             runtime.start();
         }
     } else {
-        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
         fprintf(stderr, "Error: no class name or --zygote supplied.\n");
         app_usage();
+        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
         return 10;
     }
 
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 37c8ad0..b5fddfa 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -389,11 +389,13 @@
             if (err == 0) {
                 observer.waitForCompletion();
                 sets = observer.sets;
-                for (RestoreSet s : sets) {
-                    if (s.token == token) {
-                        System.out.println("Scheduling restore: " + s.name);
-                        didRestore = (mRestore.restoreAll(token, observer) == 0);
-                        break;
+                if (sets != null) {
+                    for (RestoreSet s : sets) {
+                        if (s.token == token) {
+                            System.out.println("Scheduling restore: " + s.name);
+                            didRestore = (mRestore.restoreAll(token, observer) == 0);
+                            break;
+                        }
                     }
                 }
             }
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 0723f67..b1b7715 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -39,6 +39,8 @@
 static char cmdline_buf[16384] = "(unknown)";
 static const char *dump_traces_path = NULL;
 
+static char screenshot_path[PATH_MAX] = "";
+
 /* dumps the current system state to stdout */
 static void dumpstate() {
     time_t now = time(NULL);
@@ -76,7 +78,13 @@
     dump_file("SLAB INFO", "/proc/slabinfo");
     dump_file("ZONEINFO", "/proc/zoneinfo");
 
-    run_command("SYSTEM LOG", 20, "logcat", "-v", "time", "-d", "*:v", NULL);
+    if (screenshot_path[0]) {
+        LOGI("taking screenshot\n");
+        run_command(NULL, 5, "su", "root", "screenshot", screenshot_path, NULL);
+        LOGI("wrote screenshot: %s\n", screenshot_path);
+    }
+
+    run_command("SYSTEM LOG", 20, "logcat", "-v", "threadtime", "-d", "*:v", NULL);
 
     /* show the traces we collected in main(), if that was done */
     if (dump_traces_path != NULL) {
@@ -96,16 +104,23 @@
     }
 
     // dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
-    run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "time", "-d", "*:v", NULL);
-    run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "time", "-d", "*:v", NULL);
+    run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "threadtime", "-d", "*:v", NULL);
+    run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "threadtime", "-d", "*:v", NULL);
 
-    run_command("NETWORK INTERFACES", 10, "netcfg", NULL);
+    run_command("NETWORK INTERFACES", 10, "su", "root", "netcfg", NULL);
     dump_file("NETWORK ROUTES", "/proc/net/route");
     dump_file("ARP CACHE", "/proc/net/arp");
 
+    run_command("WIFI NETWORKS", 20,
+            "su", "root", "wpa_cli", "list_networks", NULL);
+
 #ifdef FWDUMP_bcm4329
+    run_command("DUMP WIFI STATUS", 20,
+            "su", "root", "dhdutil", "-i", "eth0", "dump", NULL);
     run_command("DUMP WIFI FIRMWARE LOG", 60,
             "su", "root", "dhdutil", "-i", "eth0", "upload", "/data/local/tmp/wlan_crash.dump", NULL);
+    run_command("DUMP WIFI INTERNAL COUNTERS", 20,
+            "su", "root", "wlutil", "counters", NULL);
 #endif
 
     print_properties();
@@ -161,21 +176,36 @@
        to increase its timeout.  we really need to do the timeouts in
        dumpsys itself... */
     run_command("DUMPSYS", 60, "dumpsys", NULL);
+
+    printf("========================================================\n");
+    printf("== Application Services\n");
+    printf("========================================================\n");
+
+    /* Instead of a 60s timeout, we should give each service a 5 second timeout */
+    run_command("APP SERVICES", 60, "dumpsys", "activity", "service", NULL);
+
 }
 
 static void usage() {
-    fprintf(stderr, "usage: dumpstate [-d] [-o file] [-s] [-z]\n"
-            "  -d: append date to filename (requires -o)\n"
+    fprintf(stderr, "usage: dumpstate [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-z]] [-s]\n"
             "  -o: write to file (instead of stdout)\n"
+            "  -d: append date to filename (requires -o)\n"
+            "  -z: gzip output (requires -o)\n"
+            "  -p: capture screenshot to filename.png (requires -o)\n"
             "  -s: write output to control socket (for init)\n"
-            "  -z: gzip output (requires -o)\n");
+            "  -b: play sound file instead of vibrate, at beginning of job\n"
+            "  -e: play sound file instead of vibrate, at end of job\n"
+		);
 }
 
 int main(int argc, char *argv[]) {
     int do_add_date = 0;
     int do_compress = 0;
     char* use_outfile = 0;
+    char* begin_sound = 0;
+    char* end_sound = 0;
     int use_socket = 0;
+    int do_fb = 0;
 
     LOGI("begin\n");
 
@@ -191,13 +221,16 @@
     dump_traces_path = dump_vm_traces();
 
     int c;
-    while ((c = getopt(argc, argv, "dho:svz")) != -1) {
+    while ((c = getopt(argc, argv, "b:de:ho:svzp")) != -1) {
         switch (c) {
+            case 'b': begin_sound = optarg;  break;
             case 'd': do_add_date = 1;       break;
+            case 'e': end_sound = optarg;    break;
             case 'o': use_outfile = optarg;  break;
             case 's': use_socket = 1;        break;
             case 'v': break;  // compatibility no-op
             case 'z': do_compress = 6;       break;
+            case 'p': do_fb = 1;             break;
             case '?': printf("\n");
             case 'h':
                 usage();
@@ -246,6 +279,10 @@
             strftime(date, sizeof(date), "-%Y-%m-%d-%H-%M-%S", localtime(&now));
             strlcat(path, date, sizeof(path));
         }
+        if (do_fb) {
+            strlcpy(screenshot_path, path, sizeof(screenshot_path));
+            strlcat(screenshot_path, ".png", sizeof(screenshot_path));
+        }
         strlcat(path, ".txt", sizeof(path));
         if (do_compress) strlcat(path, ".gz", sizeof(path));
         strlcpy(tmp_path, path, sizeof(tmp_path));
@@ -253,16 +290,18 @@
         gzip_pid = redirect_to_file(stdout, tmp_path, do_compress);
     }
 
-    /* bzzzzzz */
-    if (vibrator) {
+    if (begin_sound) {
+        play_sound(begin_sound);
+    } else if (vibrator) {
         fputs("150", vibrator);
         fflush(vibrator);
     }
 
     dumpstate();
 
-    /* bzzz bzzz bzzz */
-    if (vibrator) {
+    if (end_sound) {
+        play_sound(end_sound);
+    } else if (vibrator) {
         int i;
         for (i = 0; i < 3; i++) {
             fputs("75\n", vibrator);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 682eafd..83b1d11 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -44,4 +44,7 @@
 /* Displays a blocked processes in-kernel wait channel */
 void show_wchan(int pid, const char *name);
 
+/* Play a sound via Stagefright */
+void play_sound(const char* path);
+
 #endif /* _DUMPSTATE_H_ */
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
index c7a78cc..f92acbbb 100644
--- a/cmds/dumpstate/utils.c
+++ b/cmds/dumpstate/utils.c
@@ -429,3 +429,7 @@
     rename(anr_traces_path, traces_path);
     return dump_traces_path;
 }
+
+void play_sound(const char* path) {
+    run_command(NULL, 5, "/system/bin/stagefright", "-o", "-a", path, NULL);
+}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 9b8b0ac..040421a 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -233,7 +233,7 @@
             for (int i=0; i<rawList.length; i++) {
                 list.add(rawList[i]);
             }
-                    
+
 
             // Sort by name
             Collections.sort(list, new Comparator<FeatureInfo>() {
@@ -909,6 +909,9 @@
         System.err.println("The list instrumentation command prints all instrumentations,");
         System.err.println("or only those that target a specified package.  Options:");
         System.err.println("  -f: see their associated file.");
+        System.err.println("(Use this command to list all test packages, or use <TARGET-PACKAGE> ");
+        System.err.println(" to list the test packages for a particular application. The -f ");
+        System.err.println(" option lists the .apk file for the test package.)");
         System.err.println("");
         System.err.println("The list features command prints all features of the system.");
         System.err.println("");
diff --git a/cmds/screenshot/Android.mk b/cmds/screenshot/Android.mk
new file mode 100644
index 0000000..99c7aeb
--- /dev/null
+++ b/cmds/screenshot/Android.mk
@@ -0,0 +1,16 @@
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := screenshot.c
+
+LOCAL_MODULE := screenshot
+
+LOCAL_SHARED_LIBRARIES := libcutils libz
+LOCAL_STATIC_LIBRARIES := libpng
+LOCAL_C_INCLUDES += external/zlib
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/cmds/screenshot/screenshot.c b/cmds/screenshot/screenshot.c
new file mode 100644
index 0000000..048636c
--- /dev/null
+++ b/cmds/screenshot/screenshot.c
@@ -0,0 +1,171 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <linux/fb.h>
+
+#include <zlib.h>
+#include <libpng/png.h>
+
+#include "private/android_filesystem_config.h"
+
+#define LOG_TAG "screenshot"
+#include <utils/Log.h>
+
+void take_screenshot(FILE *fb_in, FILE *fb_out) {
+    int fb;
+    char imgbuf[0x10000];
+    struct fb_var_screeninfo vinfo;
+    png_structp png;
+    png_infop info;
+    unsigned int r,c,rowlen;
+    unsigned int bytespp,offset;
+
+    fb = fileno(fb_in);
+    if(fb < 0) {
+        LOGE("failed to open framebuffer\n");
+        return;
+    }
+    fb_in = fdopen(fb, "r");
+
+    if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) {
+        LOGE("failed to get framebuffer info\n");
+        return;
+    }
+    fcntl(fb, F_SETFD, FD_CLOEXEC);
+
+    png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    if (png == NULL) {
+        LOGE("failed png_create_write_struct\n");
+        fclose(fb_in);
+        return;
+    }
+
+    png_init_io(png, fb_out);
+    info = png_create_info_struct(png);
+    if (info == NULL) {
+        LOGE("failed png_create_info_struct\n");
+        png_destroy_write_struct(&png, NULL);
+        fclose(fb_in);
+        return;
+    }
+    if (setjmp(png_jmpbuf(png))) {
+        LOGE("failed png setjmp\n");
+        png_destroy_write_struct(&png, NULL);
+        fclose(fb_in);
+        return;
+    }
+
+    bytespp = vinfo.bits_per_pixel / 8;
+    png_set_IHDR(png, info,
+        vinfo.xres, vinfo.yres, vinfo.bits_per_pixel / 4, 
+        PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
+        PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+    png_write_info(png, info);
+
+    rowlen=vinfo.xres * bytespp;
+    if (rowlen > sizeof(imgbuf)) {
+        LOGE("crazy rowlen: %d\n", rowlen);
+        png_destroy_write_struct(&png, NULL);
+        fclose(fb_in);
+        return;
+    }
+
+    offset = vinfo.xoffset * bytespp + vinfo.xres * vinfo.yoffset * bytespp;
+    fseek(fb_in, offset, SEEK_SET);
+
+    for(r=0; r<vinfo.yres; r++) {
+        int len = fread(imgbuf, 1, rowlen, fb_in);
+        if (len <= 0) break;
+        png_write_row(png, (png_bytep)imgbuf);
+    }
+
+    png_write_end(png, info);
+    fclose(fb_in);
+    png_destroy_write_struct(&png, NULL);
+}
+
+void fork_sound(const char* path) {
+    pid_t pid = fork();
+    if (pid == 0) {
+        execl("/system/bin/stagefright", "stagefright", "-o", "-a", path, NULL);
+    }
+}
+
+void usage() {
+    fprintf(stderr,
+            "usage: screenshot [-s soundfile] filename.png\n"
+            "   -s: play a sound effect to signal success\n"
+            "   -i: autoincrement to avoid overwriting filename.png\n"
+    );
+}
+
+int main(int argc, char**argv) {
+    FILE *png = NULL;
+    FILE *fb_in = NULL;
+    char outfile[PATH_MAX] = "";
+
+    char * soundfile = NULL;
+    int do_increment = 0;
+
+    int c;
+    while ((c = getopt(argc, argv, "s:i")) != -1) {
+        switch (c) {
+            case 's': soundfile = optarg; break;
+            case 'i': do_increment = 1; break;
+            case '?':
+            case 'h':
+                usage(); exit(1);
+        }
+    }
+    argc -= optind;
+    argv += optind;
+
+    if (argc < 1) {
+        usage(); exit(1);
+    }
+
+    strlcpy(outfile, argv[0], PATH_MAX);
+    if (do_increment) {
+        struct stat st;
+        char base[PATH_MAX] = "";
+        int i = 0;
+        while (stat(outfile, &st) == 0) {
+            if (!base[0]) {
+                char *p = strrchr(outfile, '.');
+                if (p) *p = '\0';
+                strcpy(base, outfile);
+            }
+            snprintf(outfile, PATH_MAX, "%s-%d.png", base, ++i);
+        }
+    }
+
+    fb_in = fopen("/dev/graphics/fb0", "r");
+    if (!fb_in) {
+        fprintf(stderr, "error: could not read framebuffer\n");
+        exit(1);
+    }
+
+    /* switch to non-root user and group */
+    gid_t groups[] = { AID_LOG, AID_SDCARD_RW };
+    setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+    setuid(AID_SHELL);
+
+    png = fopen(outfile, "w");
+    if (!png) {
+        fprintf(stderr, "error: writing file %s: %s\n",
+                outfile, strerror(errno));
+        exit(1);
+    }
+
+    take_screenshot(fb_in, png);
+
+    if (soundfile) {
+        fork_sound(soundfile);
+    }
+
+    exit(0);
+}
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 5b74007..654d9dc 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -53,6 +53,31 @@
 
 LOCAL_SRC_FILES:=         \
         SineSource.cpp    \
+        recordvideo.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libstagefright liblog libutils libbinder
+
+LOCAL_C_INCLUDES:= \
+	$(JNI_H_INCLUDE) \
+	frameworks/base/media/libstagefright \
+	$(TOP)/frameworks/base/include/media/stagefright/openmax
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE_TAGS := debug
+
+LOCAL_MODULE:= recordvideo
+
+include $(BUILD_EXECUTABLE)
+
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=         \
+        SineSource.cpp    \
         audioloop.cpp
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp
new file mode 100644
index 0000000..f8eb514
--- /dev/null
+++ b/cmds/stagefright/recordvideo.cpp
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SineSource.h"
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
+#include <media/MediaPlayerInterface.h>
+
+using namespace android;
+
+// Print usage showing how to use this utility to record videos
+static void usage(const char *me) {
+    fprintf(stderr, "usage: %s\n", me);
+    fprintf(stderr, "       -h(elp)\n");
+    fprintf(stderr, "       -b bit rate in bits per second (default: 300000)\n");
+    fprintf(stderr, "       -c YUV420 color format: [0] semi planar or [1] planar (default: 1)\n");
+    fprintf(stderr, "       -f frame rate in frames per second (default: 30)\n");
+    fprintf(stderr, "       -i I frame interval in seconds (default: 1)\n");
+    fprintf(stderr, "       -n number of frames to be recorded (default: 300)\n");
+    fprintf(stderr, "       -w width in pixels (default: 176)\n");
+    fprintf(stderr, "       -t height in pixels (default: 144)\n");
+    fprintf(stderr, "       -l encoder level. see omx il header (default: encoder specific)\n");
+    fprintf(stderr, "       -p encoder profile. see omx il header (default: encoder specific)\n");
+    fprintf(stderr, "       -v video codec: [0] AVC [1] M4V [2] H263 (default: 0)\n");
+    fprintf(stderr, "The output file is /sdcard/output.mp4\n");
+    exit(1);
+}
+
+class DummySource : public MediaSource {
+
+public:
+    DummySource(int width, int height, int nFrames, int fps, int colorFormat)
+        : mWidth(width),
+          mHeight(height),
+          mMaxNumFrames(nFrames),
+          mFrameRate(fps),
+          mColorFormat(colorFormat),
+          mSize((width * height * 3) / 2) {
+
+        mGroup.add_buffer(new MediaBuffer(mSize));
+
+        // Check the color format to make sure
+        // that the buffer size mSize it set correctly above.
+        CHECK(colorFormat == OMX_COLOR_FormatYUV420SemiPlanar ||
+              colorFormat == OMX_COLOR_FormatYUV420Planar);
+    }
+
+    virtual sp<MetaData> getFormat() {
+        sp<MetaData> meta = new MetaData;
+        meta->setInt32(kKeyWidth, mWidth);
+        meta->setInt32(kKeyHeight, mHeight);
+        meta->setInt32(kKeyColorFormat, mColorFormat);
+        meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
+
+        return meta;
+    }
+
+    virtual status_t start(MetaData *params) {
+        mNumFramesOutput = 0;
+        return OK;
+    }
+
+    virtual status_t stop() {
+        return OK;
+    }
+
+    virtual status_t read(
+            MediaBuffer **buffer, const MediaSource::ReadOptions *options) {
+
+        if (mNumFramesOutput % 10 == 0) {
+            fprintf(stderr, ".");
+        }
+        if (mNumFramesOutput == mMaxNumFrames) {
+            return ERROR_END_OF_STREAM;
+        }
+
+        status_t err = mGroup.acquire_buffer(buffer);
+        if (err != OK) {
+            return err;
+        }
+
+        // We don't care about the contents. we just test video encoder
+        // Also, by skipping the content generation, we can return from
+        // read() much faster.
+        //char x = (char)((double)rand() / RAND_MAX * 255);
+        //memset((*buffer)->data(), x, mSize);
+        (*buffer)->set_range(0, mSize);
+        (*buffer)->meta_data()->clear();
+        (*buffer)->meta_data()->setInt64(
+                kKeyTime, (mNumFramesOutput * 1000000) / mFrameRate);
+        ++mNumFramesOutput;
+
+        return OK;
+    }
+
+protected:
+    virtual ~DummySource() {}
+
+private:
+    MediaBufferGroup mGroup;
+    int mWidth, mHeight;
+    int mMaxNumFrames;
+    int mFrameRate;
+    int mColorFormat;
+    size_t mSize;
+    int64_t mNumFramesOutput;;
+
+    DummySource(const DummySource &);
+    DummySource &operator=(const DummySource &);
+};
+
+enum {
+    kYUV420SP = 0,
+    kYUV420P  = 1,
+};
+
+// returns -1 if mapping of the given color is unsuccessful
+// returns an omx color enum value otherwise
+static int translateColorToOmxEnumValue(int color) {
+    switch (color) {
+        case kYUV420SP:
+            return OMX_COLOR_FormatYUV420SemiPlanar;
+        case kYUV420P:
+            return OMX_COLOR_FormatYUV420Planar;
+        default:
+            fprintf(stderr, "Unsupported color: %d\n", color);
+            return -1;
+    }
+}
+
+int main(int argc, char **argv) {
+
+    // Default values for the program if not overwritten
+    int frameRateFps = 30;
+    int width = 176;
+    int height = 144;
+    int bitRateBps = 300000;
+    int iFramesIntervalSeconds = 1;
+    int colorFormat = OMX_COLOR_FormatYUV420Planar;
+    int nFrames = 300;
+    int level = -1;        // Encoder specific default
+    int profile = -1;      // Encoder specific default
+    int codec = 0;
+    const char *fileName = "/sdcard/output.mp4";
+
+    android::ProcessState::self()->startThreadPool();
+    int res;
+    while ((res = getopt(argc, argv, "b:c:f:i:n:w:t:l:p:v:h")) >= 0) {
+        switch (res) {
+            case 'b':
+            {
+                bitRateBps = atoi(optarg);
+                break;
+            }
+
+            case 'c':
+            {
+                colorFormat = translateColorToOmxEnumValue(atoi(optarg));
+                if (colorFormat == -1) {
+                    usage(argv[0]);
+                }
+                break;
+            }
+
+            case 'f':
+            {
+                frameRateFps = atoi(optarg);
+                break;
+            }
+
+            case 'i':
+            {
+                iFramesIntervalSeconds = atoi(optarg);
+                break;
+            }
+
+            case 'n':
+            {
+                nFrames = atoi(optarg);
+                break;
+            }
+
+            case 'w':
+            {
+                width = atoi(optarg);
+                break;
+            }
+
+            case 't':
+            {
+                height = atoi(optarg);
+                break;
+            }
+
+            case 'l':
+            {
+                level = atoi(optarg);
+                break;
+            }
+
+            case 'p':
+            {
+                profile = atoi(optarg);
+                break;
+            }
+
+            case 'v':
+            {
+                codec = atoi(optarg);
+                if (codec < 0 || codec > 2) {
+                    usage(argv[0]);
+                }
+                break;
+            }
+
+            case 'h':
+            default:
+            {
+                usage(argv[0]);
+                break;
+            }
+        }
+    }
+
+    OMXClient client;
+    CHECK_EQ(client.connect(), OK);
+
+    status_t err = OK;
+    sp<MediaSource> source =
+        new DummySource(width, height, nFrames, frameRateFps, colorFormat);
+
+    sp<MetaData> enc_meta = new MetaData;
+    switch (codec) {
+        case 1:
+            enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+            break;
+        case 2:
+            enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
+            break;
+        default:
+            enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+            break;
+    }
+    enc_meta->setInt32(kKeyWidth, width);
+    enc_meta->setInt32(kKeyHeight, height);
+    enc_meta->setInt32(kKeySampleRate, frameRateFps);
+    enc_meta->setInt32(kKeyBitRate, bitRateBps);
+    enc_meta->setInt32(kKeyStride, width);
+    enc_meta->setInt32(kKeySliceHeight, height);
+    enc_meta->setInt32(kKeyIFramesInterval, iFramesIntervalSeconds);
+    enc_meta->setInt32(kKeyColorFormat, colorFormat);
+    if (level != -1) {
+        enc_meta->setInt32(kKeyVideoLevel, level);
+    }
+    if (profile != -1) {
+        enc_meta->setInt32(kKeyVideoProfile, profile);
+    }
+
+    sp<MediaSource> encoder =
+        OMXCodec::Create(
+                client.interface(), enc_meta, true /* createEncoder */, source);
+
+    sp<MPEG4Writer> writer = new MPEG4Writer(fileName);
+    writer->addSource(encoder);
+    int64_t start = systemTime();
+    CHECK_EQ(OK, writer->start());
+    while (!writer->reachedEOS()) {
+    }
+    err = writer->stop();
+    int64_t end = systemTime();
+
+    fprintf(stderr, "$\n");
+    client.disconnect();
+
+    if (err != OK && err != ERROR_END_OF_STREAM) {
+        fprintf(stderr, "record failed: %d\n", err);
+        return 1;
+    }
+    fprintf(stderr, "encoding %d frames in %lld us\n", nFrames, (end-start)/1000);
+    fprintf(stderr, "encoding speed is: %.2f fps\n", (nFrames * 1E9) / (end-start));
+    return 0;
+}
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 1d9e0f1..e6b1c08 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -399,19 +399,19 @@
 
     private boolean insertAccountIntoDatabase(Account account, String password, Bundle extras) {
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        if (account == null) {
+            return false;
+        }
+        final boolean noBroadcast = account.type.equals(GOOGLE_ACCOUNT_TYPE)
+                && extras != null && extras.getBoolean(NO_BROADCAST_FLAG, false);
+        // Remove the 'nobroadcast' flag since we don't want it to persist in the db. It is instead
+        // used as a control signal to indicate whether or not this insertion should result in
+        // an accounts changed broadcast being sent.
+        if (extras != null) {
+            extras.remove(NO_BROADCAST_FLAG);
+        }
         db.beginTransaction();
         try {
-            if (account == null) {
-                return false;
-            }
-            boolean noBroadcast = false;
-            if (account.type.equals(GOOGLE_ACCOUNT_TYPE)) {
-                // Look for the 'nobroadcast' flag and remove it since we don't want it to persist
-                // in the db.
-                noBroadcast = extras.getBoolean(NO_BROADCAST_FLAG, false);
-                extras.remove(NO_BROADCAST_FLAG);
-            }
-
             long numMatches = DatabaseUtils.longForQuery(db,
                     "select count(*) from " + TABLE_ACCOUNTS
                             + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
@@ -436,13 +436,13 @@
                 }
             }
             db.setTransactionSuccessful();
-            if (!noBroadcast) {
-                sendAccountsChangedBroadcast();
-            }
-            return true;
         } finally {
             db.endTransaction();
         }
+        if (!noBroadcast) {
+            sendAccountsChangedBroadcast();
+        }
+        return true;
     }
 
     private long insertExtra(SQLiteDatabase db, long accountId, String key, String value) {
@@ -1657,7 +1657,7 @@
                 }
                 boolean needsProvisioning;
                 try {
-                    needsProvisioning = telephony.getCdmaNeedsProvisioning();
+                    needsProvisioning = telephony.needsOtaServiceProvisioning();
                 } catch (RemoteException e) {
                     Log.w(TAG, "exception while checking provisioning", e);
                     // default to NOT wiping out the passwords
@@ -1681,11 +1681,11 @@
                 try {
                     db.execSQL("DELETE from " + TABLE_AUTHTOKENS);
                     db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_PASSWORD + " = ''");
-                    sendAccountsChangedBroadcast();
                     db.setTransactionSuccessful();
                 } finally {
                     db.endTransaction();
                 }
+                sendAccountsChangedBroadcast();
             }
             setMetaValue("imsi", imsi);
         }
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
new file mode 100644
index 0000000..22e04a7
--- /dev/null
+++ b/core/java/android/animation/Animator.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import java.util.ArrayList;
+
+/**
+ * This is the superclass for classes which provide basic support for animations which can be
+ * started, ended, and have <code>AnimatorListeners</code> added to them.
+ */
+public abstract class Animator implements Cloneable {
+
+
+    /**
+     * The set of listeners to be sent events through the life of an animation.
+     */
+    ArrayList<AnimatorListener> mListeners = null;
+
+    /**
+     * Starts this animation. If the animation has a nonzero startDelay, the animation will start
+     * running after that delay elapses. Note that the animation does not start synchronously with
+     * this call, because all animation events are posted to a central timing loop so that animation
+     * times are all synchronized on a single timing pulse on the UI thread. So the animation will
+     * start the next time that event handler processes events.
+     */
+    public void start() {
+    }
+
+    /**
+     * Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to
+     * stop in its tracks, sending an {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to
+     * its listeners, followed by an {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message.
+     */
+    public void cancel() {
+    }
+
+    /**
+     * Ends the animation. This causes the animation to assign the end value of the property being
+     * animated, then calling the {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on
+     * its listeners.
+     */
+    public void end() {
+    }
+
+    /**
+     * The amount of time, in milliseconds, to delay starting the animation after
+     * {@link #start()} is called.
+     *
+     * @return the number of milliseconds to delay running the animation
+     */
+    public abstract long getStartDelay();
+
+    /**
+     * The amount of time, in milliseconds, to delay starting the animation after
+     * {@link #start()} is called.
+
+     * @param startDelay The amount of the delay, in milliseconds
+     */
+    public abstract void setStartDelay(long startDelay);
+
+
+    /**
+     * Sets the length of the animation.
+     *
+     * @param duration The length of the animation, in milliseconds.
+     */
+    public abstract Animator setDuration(long duration);
+
+    /**
+     * Gets the length of the animation.
+     *
+     * @return The length of the animation, in milliseconds.
+     */
+    public abstract long getDuration();
+
+    /**
+     * The time interpolator used in calculating the elapsed fraction of this animation. The
+     * interpolator determines whether the animation runs with linear or non-linear motion,
+     * such as acceleration and deceleration. The default value is
+     * {@link android.view.animation.AccelerateDecelerateInterpolator}
+     *
+     * @param value the interpolator to be used by this animation
+     */
+    public abstract void setInterpolator(TimeInterpolator value);
+
+    /**
+     * Returns whether this Animator is currently running (having been started and not yet ended).
+     * @return Whether the Animator is running.
+     */
+    public abstract boolean isRunning();
+
+    /**
+     * Adds a listener to the set of listeners that are sent events through the life of an
+     * animation, such as start, repeat, and end.
+     *
+     * @param listener the listener to be added to the current set of listeners for this animation.
+     */
+    public void addListener(AnimatorListener listener) {
+        if (mListeners == null) {
+            mListeners = new ArrayList<AnimatorListener>();
+        }
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener from the set listening to this animation.
+     *
+     * @param listener the listener to be removed from the current set of listeners for this
+     *                 animation.
+     */
+    public void removeListener(AnimatorListener listener) {
+        if (mListeners == null) {
+            return;
+        }
+        mListeners.remove(listener);
+        if (mListeners.size() == 0) {
+            mListeners = null;
+        }
+    }
+
+    /**
+     * Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently
+     * listening for events on this <code>Animator</code> object.
+     *
+     * @return ArrayList<AnimatorListener> The set of listeners.
+     */
+    public ArrayList<AnimatorListener> getListeners() {
+        return mListeners;
+    }
+
+    /**
+     * Removes all listeners from this object. This is equivalent to calling
+     * <code>getListeners()</code> followed by calling <code>clear()</code> on the
+     * returned list of listeners.
+     */
+    public void removeAllListeners() {
+        if (mListeners != null) {
+            mListeners.clear();
+            mListeners = null;
+        }
+    }
+
+    @Override
+    public Animator clone() {
+        try {
+            final Animator anim = (Animator) super.clone();
+            if (mListeners != null) {
+                ArrayList<AnimatorListener> oldListeners = mListeners;
+                anim.mListeners = new ArrayList<AnimatorListener>();
+                int numListeners = oldListeners.size();
+                for (int i = 0; i < numListeners; ++i) {
+                    anim.mListeners.add(oldListeners.get(i));
+                }
+            }
+            return anim;
+        } catch (CloneNotSupportedException e) {
+           throw new AssertionError();
+        }
+    }
+
+    /**
+     * This method tells the object to use appropriate information to extract
+     * starting values for the animation. For example, a AnimatorSet object will pass
+     * this call to its child objects to tell them to set up the values. A
+     * ObjectAnimator object will use the information it has about its target object
+     * and PropertyValuesHolder objects to get the start values for its properties.
+     * An ValueAnimator object will ignore the request since it does not have enough
+     * information (such as a target object) to gather these values.
+     */
+    public void setupStartValues() {
+    }
+
+    /**
+     * This method tells the object to use appropriate information to extract
+     * ending values for the animation. For example, a AnimatorSet object will pass
+     * this call to its child objects to tell them to set up the values. A
+     * ObjectAnimator object will use the information it has about its target object
+     * and PropertyValuesHolder objects to get the start values for its properties.
+     * An ValueAnimator object will ignore the request since it does not have enough
+     * information (such as a target object) to gather these values.
+     */
+    public void setupEndValues() {
+    }
+
+    /**
+     * Sets the target object whose property will be animated by this animation. Not all subclasses
+     * operate on target objects (for example, {@link ValueAnimator}, but this method
+     * is on the superclass for the convenience of dealing generically with those subclasses
+     * that do handle targets.
+     *
+     * @param target The object being animated
+     */
+    public void setTarget(Object target) {
+    }
+
+    /**
+     * <p>An animation listener receives notifications from an animation.
+     * Notifications indicate animation related events, such as the end or the
+     * repetition of the animation.</p>
+     */
+    public static interface AnimatorListener {
+        /**
+         * <p>Notifies the start of the animation.</p>
+         *
+         * @param animation The started animation.
+         */
+        void onAnimationStart(Animator animation);
+
+        /**
+         * <p>Notifies the end of the animation. This callback is not invoked
+         * for animations with repeat count set to INFINITE.</p>
+         *
+         * @param animation The animation which reached its end.
+         */
+        void onAnimationEnd(Animator animation);
+
+        /**
+         * <p>Notifies the cancellation of the animation. This callback is not invoked
+         * for animations with repeat count set to INFINITE.</p>
+         *
+         * @param animation The animation which was canceled.
+         */
+        void onAnimationCancel(Animator animation);
+
+        /**
+         * <p>Notifies the repetition of the animation.</p>
+         *
+         * @param animation The animation which was repeated.
+         */
+        void onAnimationRepeat(Animator animation);
+    }
+}
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
new file mode 100644
index 0000000..b96391a
--- /dev/null
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.animation;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.content.res.Resources.NotFoundException;
+import android.util.AttributeSet;
+import android.util.Xml;
+import android.view.animation.AnimationUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * This class is used to instantiate menu XML files into Animator objects.
+ * <p>
+ * For performance reasons, menu inflation relies heavily on pre-processing of
+ * XML files that is done at build time. Therefore, it is not currently possible
+ * to use MenuInflater with an XmlPullParser over a plain XML file at runtime;
+ * it only works with an XmlPullParser returned from a compiled resource (R.
+ * <em>something</em> file.)
+ */
+public class AnimatorInflater {
+
+    /**
+     * These flags are used when parsing AnimatorSet objects
+     */
+    private static final int TOGETHER = 0;
+    private static final int SEQUENTIALLY = 1;
+
+    /**
+     * Enum values used in XML attributes to indicate the value for mValueType
+     */
+    private static final int VALUE_TYPE_FLOAT       = 0;
+    private static final int VALUE_TYPE_INT         = 1;
+    private static final int VALUE_TYPE_COLOR       = 4;
+    private static final int VALUE_TYPE_CUSTOM      = 5;
+
+    /**
+     * Loads an {@link Animator} object from a resource
+     *
+     * @param context Application context used to access resources
+     * @param id The resource id of the animation to load
+     * @return The animator object reference by the specified id
+     * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
+     */
+    public static Animator loadAnimator(Context context, int id)
+            throws NotFoundException {
+
+        XmlResourceParser parser = null;
+        try {
+            parser = context.getResources().getAnimation(id);
+            return createAnimatorFromXml(context, parser);
+        } catch (XmlPullParserException ex) {
+            Resources.NotFoundException rnf =
+                    new Resources.NotFoundException("Can't load animation resource ID #0x" +
+                    Integer.toHexString(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } catch (IOException ex) {
+            Resources.NotFoundException rnf =
+                    new Resources.NotFoundException("Can't load animation resource ID #0x" +
+                    Integer.toHexString(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } finally {
+            if (parser != null) parser.close();
+        }
+    }
+
+    private static Animator createAnimatorFromXml(Context c, XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+
+        return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
+    }
+
+    private static Animator createAnimatorFromXml(Context c, XmlPullParser parser,
+            AttributeSet attrs, AnimatorSet parent, int sequenceOrdering)
+            throws XmlPullParserException, IOException {
+
+        Animator anim = null;
+        ArrayList<Animator> childAnims = null;
+
+        // Make sure we are on a start tag.
+        int type;
+        int depth = parser.getDepth();
+
+        while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+               && type != XmlPullParser.END_DOCUMENT) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String  name = parser.getName();
+
+            if (name.equals("objectAnimator")) {
+                anim = loadObjectAnimator(c, attrs);
+            } else if (name.equals("animator")) {
+                anim = loadAnimator(c, attrs, null);
+            } else if (name.equals("set")) {
+                anim = new AnimatorSet();
+                TypedArray a = c.obtainStyledAttributes(attrs,
+                        com.android.internal.R.styleable.AnimatorSet);
+                int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering,
+                        TOGETHER);
+                createAnimatorFromXml(c, parser, attrs, (AnimatorSet) anim,  ordering);
+                a.recycle();
+            } else {
+                throw new RuntimeException("Unknown animator name: " + parser.getName());
+            }
+
+            if (parent != null) {
+                if (childAnims == null) {
+                    childAnims = new ArrayList<Animator>();
+                }
+                childAnims.add(anim);
+            }
+        }
+        if (parent != null && childAnims != null) {
+            Animator[] animsArray = new Animator[childAnims.size()];
+            int index = 0;
+            for (Animator a : childAnims) {
+                animsArray[index++] = a;
+            }
+            if (sequenceOrdering == TOGETHER) {
+                parent.playTogether(animsArray);
+            } else {
+                parent.playSequentially(animsArray);
+            }
+        }
+
+        return anim;
+
+    }
+
+    private static ObjectAnimator loadObjectAnimator(Context context, AttributeSet attrs)
+            throws NotFoundException {
+
+        ObjectAnimator anim = new ObjectAnimator();
+
+        loadAnimator(context, attrs, anim);
+
+        TypedArray a =
+                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.PropertyAnimator);
+
+        String propertyName = a.getString(com.android.internal.R.styleable.PropertyAnimator_propertyName);
+
+        anim.setPropertyName(propertyName);
+
+        a.recycle();
+
+        return anim;
+    }
+
+    /**
+     * Creates a new animation whose parameters come from the specified context and
+     * attributes set.
+     *
+     * @param context the application environment
+     * @param attrs the set of attributes holding the animation parameters
+     */
+    private static ValueAnimator loadAnimator(Context context, AttributeSet attrs, ValueAnimator anim)
+            throws NotFoundException {
+
+        TypedArray a =
+                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animator);
+
+        long duration = a.getInt(com.android.internal.R.styleable.Animator_duration, 0);
+
+        long startDelay = a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0);
+
+        int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType,
+                VALUE_TYPE_FLOAT);
+
+        if (anim == null) {
+            anim = new ValueAnimator();
+        }
+        TypeEvaluator evaluator = null;
+        boolean hasFrom = a.hasValue(com.android.internal.R.styleable.Animator_valueFrom);
+        boolean hasTo = a.hasValue(com.android.internal.R.styleable.Animator_valueTo);
+
+        switch (valueType) {
+
+            case VALUE_TYPE_FLOAT: {
+                float valueFrom;
+                float valueTo;
+                if (hasFrom) {
+                    valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
+                    if (hasTo) {
+                        valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+                        anim.setFloatValues(valueFrom, valueTo);
+                    } else {
+                        anim.setFloatValues(valueFrom);
+                    }
+                } else {
+                    valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+                    anim.setFloatValues(valueTo);
+                }
+            }
+            break;
+
+            case VALUE_TYPE_COLOR:
+                evaluator = new RGBEvaluator();
+                anim.setEvaluator(evaluator);
+                // fall through to pick up values
+            case VALUE_TYPE_INT: {
+                int valueFrom;
+                int valueTo;
+                if (hasFrom) {
+                    valueFrom = a.getInteger(com.android.internal.R.styleable.Animator_valueFrom, 0);
+                    if (hasTo) {
+                        valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0);
+                        anim.setIntValues(valueFrom, valueTo);
+                    } else {
+                        anim.setIntValues(valueFrom);
+                    }
+                } else {
+                    valueTo = a.getInteger(com.android.internal.R.styleable.Animator_valueTo, 0);
+                    anim.setIntValues(valueTo);
+                }
+            }
+            break;
+
+            case VALUE_TYPE_CUSTOM: {
+                // TODO: How to get an 'Object' value?
+                float valueFrom;
+                float valueTo;
+                if (hasFrom) {
+                    valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
+                    if (hasTo) {
+                        valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+                        anim.setFloatValues(valueFrom, valueTo);
+                    } else {
+                        anim.setFloatValues(valueFrom);
+                    }
+                } else {
+                    valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+                    anim.setFloatValues(valueTo);
+                }
+            }
+            break;
+        }
+
+
+        anim.setDuration(duration);
+        anim.setStartDelay(startDelay);
+
+        if (a.hasValue(com.android.internal.R.styleable.Animator_repeatCount)) {
+            anim.setRepeatCount(
+                    a.getInt(com.android.internal.R.styleable.Animator_repeatCount, 0));
+        }
+        if (a.hasValue(com.android.internal.R.styleable.Animator_repeatMode)) {
+            anim.setRepeatMode(
+                    a.getInt(com.android.internal.R.styleable.Animator_repeatMode,
+                            ValueAnimator.RESTART));
+        }
+        if (evaluator != null) {
+            anim.setEvaluator(evaluator);
+        }
+
+        final int resID =
+                a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
+        if (resID > 0) {
+            anim.setInterpolator(AnimationUtils.loadInterpolator(context, resID));
+        }
+        a.recycle();
+
+        return anim;
+    }
+}
diff --git a/core/java/android/animation/AnimatorListenerAdapter.java b/core/java/android/animation/AnimatorListenerAdapter.java
new file mode 100644
index 0000000..e5d70a4
--- /dev/null
+++ b/core/java/android/animation/AnimatorListenerAdapter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+/**
+ * This adapter class provides empty implementations of the methods from {@link android.animation.Animator.AnimatorListener}.
+ * Any custom listener that cares only about a subset of the methods of this listener can
+ * simply subclass this adapter class instead of implementing the interface directly.
+ */
+public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAnimationCancel(Animator animation) {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAnimationEnd(Animator animation) {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAnimationRepeat(Animator animation) {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAnimationStart(Animator animation) {
+    }
+
+}
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
new file mode 100644
index 0000000..9ba9388
--- /dev/null
+++ b/core/java/android/animation/AnimatorSet.java
@@ -0,0 +1,963 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class plays a set of {@link Animator} objects in the specified order. Animations
+ * can be set up to play together, in sequence, or after a specified delay.
+ *
+ * <p>There are two different approaches to adding animations to a <code>AnimatorSet</code>:
+ * either the {@link AnimatorSet#playTogether(Animator[]) playTogether()} or
+ * {@link AnimatorSet#playSequentially(Animator[]) playSequentially()} methods can be called to add
+ * a set of animations all at once, or the {@link AnimatorSet#play(Animator)} can be
+ * used in conjunction with methods in the {@link AnimatorSet.Builder Builder}
+ * class to add animations
+ * one by one.</p>
+ *
+ * <p>It is possible to set up a <code>AnimatorSet</code> with circular dependencies between
+ * its animations. For example, an animation a1 could be set up to start before animation a2, a2
+ * before a3, and a3 before a1. The results of this configuration are undefined, but will typically
+ * result in none of the affected animations being played. Because of this (and because
+ * circular dependencies do not make logical sense anyway), circular dependencies
+ * should be avoided, and the dependency flow of animations should only be in one direction.
+ */
+public final class AnimatorSet extends Animator {
+
+    /**
+     * Internal variables
+     * NOTE: This object implements the clone() method, making a deep copy of any referenced
+     * objects. As other non-trivial fields are added to this class, make sure to add logic
+     * to clone() to make deep copies of them.
+     */
+
+    /**
+     * Tracks animations currently being played, so that we know what to
+     * cancel or end when cancel() or end() is called on this AnimatorSet
+     */
+    private ArrayList<Animator> mPlayingSet = new ArrayList<Animator>();
+
+    /**
+     * Contains all nodes, mapped to their respective Animators. When new
+     * dependency information is added for an Animator, we want to add it
+     * to a single node representing that Animator, not create a new Node
+     * if one already exists.
+     */
+    private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>();
+
+    /**
+     * Set of all nodes created for this AnimatorSet. This list is used upon
+     * starting the set, and the nodes are placed in sorted order into the
+     * sortedNodes collection.
+     */
+    private ArrayList<Node> mNodes = new ArrayList<Node>();
+
+    /**
+     * The sorted list of nodes. This is the order in which the animations will
+     * be played. The details about when exactly they will be played depend
+     * on the dependency relationships of the nodes.
+     */
+    private ArrayList<Node> mSortedNodes = new ArrayList<Node>();
+
+    /**
+     * Flag indicating whether the nodes should be sorted prior to playing. This
+     * flag allows us to cache the previous sorted nodes so that if the sequence
+     * is replayed with no changes, it does not have to re-sort the nodes again.
+     */
+    private boolean mNeedsSort = true;
+
+    private AnimatorSetListener mSetListener = null;
+
+    /**
+     * Flag indicating that the AnimatorSet has been canceled (by calling cancel() or end()).
+     * This flag is used to avoid starting other animations when currently-playing
+     * child animations of this AnimatorSet end.
+     */
+    boolean mCanceled = false;
+
+    // The amount of time in ms to delay starting the animation after start() is called
+    private long mStartDelay = 0;
+
+
+    // How long the child animations should last in ms. The default value is negative, which
+    // simply means that there is no duration set on the AnimatorSet. When a real duration is
+    // set, it is passed along to the child animations.
+    private long mDuration = -1;
+
+
+    /**
+     * Sets up this AnimatorSet to play all of the supplied animations at the same time.
+     *
+     * @param items The animations that will be started simultaneously.
+     */
+    public void playTogether(Animator... items) {
+        if (items != null) {
+            mNeedsSort = true;
+            Builder builder = play(items[0]);
+            for (int i = 1; i < items.length; ++i) {
+                builder.with(items[i]);
+            }
+        }
+    }
+
+    /**
+     * Sets up this AnimatorSet to play each of the supplied animations when the
+     * previous animation ends.
+     *
+     * @param items The aniamtions that will be started one after another.
+     */
+    public void playSequentially(Animator... items) {
+        if (items != null) {
+            mNeedsSort = true;
+            if (items.length == 1) {
+                play(items[0]);
+            } else {
+                for (int i = 0; i < items.length - 1; ++i) {
+                    play(items[i]).before(items[i+1]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the current list of child Animator objects controlled by this
+     * AnimatorSet. This is a copy of the internal list; modifications to the returned list
+     * will not affect the AnimatorSet, although changes to the underlying Animator objects
+     * will affect those objects being managed by the AnimatorSet.
+     *
+     * @return ArrayList<Animator> The list of child animations of this AnimatorSet.
+     */
+    public ArrayList<Animator> getChildAnimations() {
+        ArrayList<Animator> childList = new ArrayList<Animator>();
+        for (Node node : mNodes) {
+            childList.add(node.animation);
+        }
+        return childList;
+    }
+
+    /**
+     * Sets the target object for all current {@link #getChildAnimations() child animations}
+     * of this AnimatorSet that take targets ({@link ObjectAnimator} and
+     * AnimatorSet).
+     *
+     * @param target The object being animated
+     */
+    @Override
+    public void setTarget(Object target) {
+        for (Node node : mNodes) {
+            Animator animation = node.animation;
+            if (animation instanceof AnimatorSet) {
+                ((AnimatorSet)animation).setTarget(target);
+            } else if (animation instanceof ObjectAnimator) {
+                ((ObjectAnimator)animation).setTarget(target);
+            }
+        }
+    }
+
+    /**
+     * Sets the TimeInterpolator for all current {@link #getChildAnimations() child animations}
+     * of this AnimatorSet.
+     *
+     * @param interpolator the interpolator to be used by each child animation of this AnimatorSet
+     */
+    @Override
+    public void setInterpolator(TimeInterpolator interpolator) {
+        for (Node node : mNodes) {
+            node.animation.setInterpolator(interpolator);
+        }
+    }
+
+    /**
+     * This method creates a <code>Builder</code> object, which is used to
+     * set up playing constraints. This initial <code>play()</code> method
+     * tells the <code>Builder</code> the animation that is the dependency for
+     * the succeeding commands to the <code>Builder</code>. For example,
+     * calling <code>play(a1).with(a2)</code> sets up the AnimatorSet to play
+     * <code>a1</code> and <code>a2</code> at the same time,
+     * <code>play(a1).before(a2)</code> sets up the AnimatorSet to play
+     * <code>a1</code> first, followed by <code>a2</code>, and
+     * <code>play(a1).after(a2)</code> sets up the AnimatorSet to play
+     * <code>a2</code> first, followed by <code>a1</code>.
+     *
+     * <p>Note that <code>play()</code> is the only way to tell the
+     * <code>Builder</code> the animation upon which the dependency is created,
+     * so successive calls to the various functions in <code>Builder</code>
+     * will all refer to the initial parameter supplied in <code>play()</code>
+     * as the dependency of the other animations. For example, calling
+     * <code>play(a1).before(a2).before(a3)</code> will play both <code>a2</code>
+     * and <code>a3</code> when a1 ends; it does not set up a dependency between
+     * <code>a2</code> and <code>a3</code>.</p>
+     *
+     * @param anim The animation that is the dependency used in later calls to the
+     * methods in the returned <code>Builder</code> object. A null parameter will result
+     * in a null <code>Builder</code> return value.
+     * @return Builder The object that constructs the AnimatorSet based on the dependencies
+     * outlined in the calls to <code>play</code> and the other methods in the
+     * <code>Builder</code object.
+     */
+    public Builder play(Animator anim) {
+        if (anim != null) {
+            mNeedsSort = true;
+            return new Builder(anim);
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Note that canceling a <code>AnimatorSet</code> also cancels all of the animations that it is
+     * responsible for.</p>
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void cancel() {
+        mCanceled = true;
+        if (mListeners != null) {
+            ArrayList<AnimatorListener> tmpListeners =
+                    (ArrayList<AnimatorListener>) mListeners.clone();
+            for (AnimatorListener listener : tmpListeners) {
+                listener.onAnimationCancel(this);
+            }
+        }
+        if (mSortedNodes.size() > 0) {
+            for (Node node : mSortedNodes) {
+                node.animation.cancel();
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Note that ending a <code>AnimatorSet</code> also ends all of the animations that it is
+     * responsible for.</p>
+     */
+    @Override
+    public void end() {
+        mCanceled = true;
+        if (mSortedNodes.size() != mNodes.size()) {
+            // hasn't been started yet - sort the nodes now, then end them
+            sortNodes();
+            for (Node node : mSortedNodes) {
+                if (mSetListener == null) {
+                    mSetListener = new AnimatorSetListener(this);
+                }
+                node.animation.addListener(mSetListener);
+            }
+        }
+        if (mSortedNodes.size() > 0) {
+            for (Node node : mSortedNodes) {
+                node.animation.end();
+            }
+        }
+    }
+
+    /**
+     * Returns true if any of the child animations of this AnimatorSet have been started and have not
+     * yet ended.
+     * @return Whether this AnimatorSet has been started and has not yet ended.
+     */
+    @Override
+    public boolean isRunning() {
+        for (Node node : mNodes) {
+            if (node.animation.isRunning()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * The amount of time, in milliseconds, to delay starting the animation after
+     * {@link #start()} is called.
+     *
+     * @return the number of milliseconds to delay running the animation
+     */
+    @Override
+    public long getStartDelay() {
+        return mStartDelay;
+    }
+
+    /**
+     * The amount of time, in milliseconds, to delay starting the animation after
+     * {@link #start()} is called.
+
+     * @param startDelay The amount of the delay, in milliseconds
+     */
+    @Override
+    public void setStartDelay(long startDelay) {
+        mStartDelay = startDelay;
+    }
+
+    /**
+     * Gets the length of each of the child animations of this AnimatorSet. This value may
+     * be less than 0, which indicates that no duration has been set on this AnimatorSet
+     * and each of the child animations will use their own duration.
+     *
+     * @return The length of the animation, in milliseconds, of each of the child
+     * animations of this AnimatorSet.
+     */
+    @Override
+    public long getDuration() {
+        return mDuration;
+    }
+
+    /**
+     * Sets the length of each of the current child animations of this AnimatorSet. By default,
+     * each child animation will use its own duration. If the duration is set on the AnimatorSet,
+     * then each child animation inherits this duration.
+     *
+     * @param duration The length of the animation, in milliseconds, of each of the child
+     * animations of this AnimatorSet.
+     */
+    @Override
+    public AnimatorSet setDuration(long duration) {
+        if (duration < 0) {
+            throw new IllegalArgumentException("duration must be a value of zero or greater");
+        }
+        for (Node node : mNodes) {
+            // TODO: don't set the duration of the timing-only nodes created by AnimatorSet to
+            // insert "play-after" delays
+            node.animation.setDuration(duration);
+        }
+        mDuration = duration;
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Starting this <code>AnimatorSet</code> will, in turn, start the animations for which
+     * it is responsible. The details of when exactly those animations are started depends on
+     * the dependency relationships that have been set up between the animations.
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void start() {
+        mCanceled = false;
+
+        // First, sort the nodes (if necessary). This will ensure that sortedNodes
+        // contains the animation nodes in the correct order.
+        sortNodes();
+
+        // nodesToStart holds the list of nodes to be started immediately. We don't want to
+        // start the animations in the loop directly because we first need to set up
+        // dependencies on all of the nodes. For example, we don't want to start an animation
+        // when some other animation also wants to start when the first animation begins.
+        final ArrayList<Node> nodesToStart = new ArrayList<Node>();
+        int numSortedNodes = mSortedNodes.size();
+        for (int i = 0; i < numSortedNodes; ++i) {
+            Node node = mSortedNodes.get(i);
+            if (mSetListener == null) {
+                mSetListener = new AnimatorSetListener(this);
+            }
+            if (node.dependencies == null || node.dependencies.size() == 0) {
+                nodesToStart.add(node);
+            } else {
+                int numDependencies = node.dependencies.size();
+                for (int j = 0; j < numDependencies; ++j) {
+                    Dependency dependency = node.dependencies.get(j);
+                    dependency.node.animation.addListener(
+                            new DependencyListener(this, node, dependency.rule));
+                }
+                node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone();
+            }
+            node.animation.addListener(mSetListener);
+        }
+        // Now that all dependencies are set up, start the animations that should be started.
+        if (mStartDelay <= 0) {
+            for (Node node : nodesToStart) {
+                node.animation.start();
+                mPlayingSet.add(node.animation);
+            }
+        } else {
+            // TODO: Need to cancel out of the delay appropriately
+            ValueAnimator delayAnim = ValueAnimator.ofFloat(0f, 1f);
+            delayAnim.setDuration(mStartDelay);
+            delayAnim.addListener(new AnimatorListenerAdapter() {
+                public void onAnimationEnd(Animator anim) {
+                    int numNodes = nodesToStart.size();
+                    for (int i = 0; i < numNodes; ++i) {
+                        Node node = nodesToStart.get(i);
+                        node.animation.start();
+                        mPlayingSet.add(node.animation);
+                    }
+                }
+            });
+        }
+        if (mListeners != null) {
+            ArrayList<AnimatorListener> tmpListeners =
+                    (ArrayList<AnimatorListener>) mListeners.clone();
+            int numListeners = tmpListeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                tmpListeners.get(i).onAnimationStart(this);
+            }
+        }
+    }
+
+    @Override
+    public AnimatorSet clone() {
+        final AnimatorSet anim = (AnimatorSet) super.clone();
+        /*
+         * The basic clone() operation copies all items. This doesn't work very well for
+         * AnimatorSet, because it will copy references that need to be recreated and state
+         * that may not apply. What we need to do now is put the clone in an uninitialized
+         * state, with fresh, empty data structures. Then we will build up the nodes list
+         * manually, as we clone each Node (and its animation). The clone will then be sorted,
+         * and will populate any appropriate lists, when it is started.
+         */
+        anim.mNeedsSort = true;
+        anim.mCanceled = false;
+        anim.mPlayingSet = new ArrayList<Animator>();
+        anim.mNodeMap = new HashMap<Animator, Node>();
+        anim.mNodes = new ArrayList<Node>();
+        anim.mSortedNodes = new ArrayList<Node>();
+
+        // Walk through the old nodes list, cloning each node and adding it to the new nodemap.
+        // One problem is that the old node dependencies point to nodes in the old AnimatorSet.
+        // We need to track the old/new nodes in order to reconstruct the dependencies in the clone.
+        HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>(); // <old, new>
+        for (Node node : mNodes) {
+            Node nodeClone = node.clone();
+            nodeCloneMap.put(node, nodeClone);
+            anim.mNodes.add(nodeClone);
+            anim.mNodeMap.put(nodeClone.animation, nodeClone);
+            // Clear out the dependencies in the clone; we'll set these up manually later
+            nodeClone.dependencies = null;
+            nodeClone.tmpDependencies = null;
+            nodeClone.nodeDependents = null;
+            nodeClone.nodeDependencies = null;
+            // clear out any listeners that were set up by the AnimatorSet; these will
+            // be set up when the clone's nodes are sorted
+            ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
+            if (cloneListeners != null) {
+                ArrayList<AnimatorListener> listenersToRemove = null;
+                for (AnimatorListener listener : cloneListeners) {
+                    if (listener instanceof AnimatorSetListener) {
+                        if (listenersToRemove == null) {
+                            listenersToRemove = new ArrayList<AnimatorListener>();
+                        }
+                        listenersToRemove.add(listener);
+                    }
+                }
+                if (listenersToRemove != null) {
+                    for (AnimatorListener listener : listenersToRemove) {
+                        cloneListeners.remove(listener);
+                    }
+                }
+            }
+        }
+        // Now that we've cloned all of the nodes, we're ready to walk through their
+        // dependencies, mapping the old dependencies to the new nodes
+        for (Node node : mNodes) {
+            Node nodeClone = nodeCloneMap.get(node);
+            if (node.dependencies != null) {
+                for (Dependency dependency : node.dependencies) {
+                    Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
+                    Dependency cloneDependency = new Dependency(clonedDependencyNode,
+                            dependency.rule);
+                    nodeClone.addDependency(cloneDependency);
+                }
+            }
+        }
+
+        return anim;
+    }
+
+    /**
+     * This class is the mechanism by which animations are started based on events in other
+     * animations. If an animation has multiple dependencies on other animations, then
+     * all dependencies must be satisfied before the animation is started.
+     */
+    private static class DependencyListener implements AnimatorListener {
+
+        private AnimatorSet mAnimatorSet;
+
+        // The node upon which the dependency is based.
+        private Node mNode;
+
+        // The Dependency rule (WITH or AFTER) that the listener should wait for on
+        // the node
+        private int mRule;
+
+        public DependencyListener(AnimatorSet animatorSet, Node node, int rule) {
+            this.mAnimatorSet = animatorSet;
+            this.mNode = node;
+            this.mRule = rule;
+        }
+
+        /**
+         * Ignore cancel events for now. We may want to handle this eventually,
+         * to prevent follow-on animations from running when some dependency
+         * animation is canceled.
+         */
+        public void onAnimationCancel(Animator animation) {
+        }
+
+        /**
+         * An end event is received - see if this is an event we are listening for
+         */
+        public void onAnimationEnd(Animator animation) {
+            if (mRule == Dependency.AFTER) {
+                startIfReady(animation);
+            }
+        }
+
+        /**
+         * Ignore repeat events for now
+         */
+        public void onAnimationRepeat(Animator animation) {
+        }
+
+        /**
+         * A start event is received - see if this is an event we are listening for
+         */
+        public void onAnimationStart(Animator animation) {
+            if (mRule == Dependency.WITH) {
+                startIfReady(animation);
+            }
+        }
+
+        /**
+         * Check whether the event received is one that the node was waiting for.
+         * If so, mark it as complete and see whether it's time to start
+         * the animation.
+         * @param dependencyAnimation the animation that sent the event.
+         */
+        private void startIfReady(Animator dependencyAnimation) {
+            if (mAnimatorSet.mCanceled) {
+                // if the parent AnimatorSet was canceled, then don't start any dependent anims
+                return;
+            }
+            Dependency dependencyToRemove = null;
+            int numDependencies = mNode.tmpDependencies.size();
+            for (int i = 0; i < numDependencies; ++i) {
+                Dependency dependency = mNode.tmpDependencies.get(i);
+                if (dependency.rule == mRule &&
+                        dependency.node.animation == dependencyAnimation) {
+                    // rule fired - remove the dependency and listener and check to
+                    // see whether it's time to start the animation
+                    dependencyToRemove = dependency;
+                    dependencyAnimation.removeListener(this);
+                    break;
+                }
+            }
+            mNode.tmpDependencies.remove(dependencyToRemove);
+            if (mNode.tmpDependencies.size() == 0) {
+                // all dependencies satisfied: start the animation
+                mNode.animation.start();
+                mAnimatorSet.mPlayingSet.add(mNode.animation);
+            }
+        }
+
+    }
+
+    private class AnimatorSetListener implements AnimatorListener {
+
+        private AnimatorSet mAnimatorSet;
+
+        AnimatorSetListener(AnimatorSet animatorSet) {
+            mAnimatorSet = animatorSet;
+        }
+
+        public void onAnimationCancel(Animator animation) {
+            if (mPlayingSet.size() == 0) {
+                if (mListeners != null) {
+                    int numListeners = mListeners.size();
+                    for (int i = 0; i < numListeners; ++i) {
+                        mListeners.get(i).onAnimationCancel(mAnimatorSet);
+                    }
+                }
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        public void onAnimationEnd(Animator animation) {
+            animation.removeListener(this);
+            mPlayingSet.remove(animation);
+            Node animNode = mAnimatorSet.mNodeMap.get(animation);
+            animNode.done = true;
+            ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
+            boolean allDone = true;
+            int numSortedNodes = sortedNodes.size();
+            for (int i = 0; i < numSortedNodes; ++i) {
+                if (!sortedNodes.get(i).done) {
+                    allDone = false;
+                    break;
+                }
+            }
+            if (allDone) {
+                // If this was the last child animation to end, then notify listeners that this
+                // AnimatorSet has ended
+                if (mListeners != null) {
+                    ArrayList<AnimatorListener> tmpListeners =
+                            (ArrayList<AnimatorListener>) mListeners.clone();
+                    int numListeners = tmpListeners.size();
+                    for (int i = 0; i < numListeners; ++i) {
+                        tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
+                    }
+                }
+            }
+        }
+
+        // Nothing to do
+        public void onAnimationRepeat(Animator animation) {
+        }
+
+        // Nothing to do
+        public void onAnimationStart(Animator animation) {
+        }
+
+    }
+
+    /**
+     * This method sorts the current set of nodes, if needed. The sort is a simple
+     * DependencyGraph sort, which goes like this:
+     * - All nodes without dependencies become 'roots'
+     * - while roots list is not null
+     * -   for each root r
+     * -     add r to sorted list
+     * -     remove r as a dependency from any other node
+     * -   any nodes with no dependencies are added to the roots list
+     */
+    private void sortNodes() {
+        if (mNeedsSort) {
+            mSortedNodes.clear();
+            ArrayList<Node> roots = new ArrayList<Node>();
+            int numNodes = mNodes.size();
+            for (int i = 0; i < numNodes; ++i) {
+                Node node = mNodes.get(i);
+                if (node.dependencies == null || node.dependencies.size() == 0) {
+                    roots.add(node);
+                }
+            }
+            ArrayList<Node> tmpRoots = new ArrayList<Node>();
+            while (roots.size() > 0) {
+                int numRoots = roots.size();
+                for (int i = 0; i < numRoots; ++i) {
+                    Node root = roots.get(i);
+                    mSortedNodes.add(root);
+                    if (root.nodeDependents != null) {
+                        int numDependents = root.nodeDependents.size();
+                        for (int j = 0; j < numDependents; ++j) {
+                            Node node = root.nodeDependents.get(j);
+                            node.nodeDependencies.remove(root);
+                            if (node.nodeDependencies.size() == 0) {
+                                tmpRoots.add(node);
+                            }
+                        }
+                    }
+                }
+                roots.clear();
+                roots.addAll(tmpRoots);
+                tmpRoots.clear();
+            }
+            mNeedsSort = false;
+            if (mSortedNodes.size() != mNodes.size()) {
+                throw new IllegalStateException("Circular dependencies cannot exist"
+                        + " in AnimatorSet");
+            }
+        } else {
+            // Doesn't need sorting, but still need to add in the nodeDependencies list
+            // because these get removed as the event listeners fire and the dependencies
+            // are satisfied
+            int numNodes = mNodes.size();
+            for (int i = 0; i < numNodes; ++i) {
+                Node node = mNodes.get(i);
+                if (node.dependencies != null && node.dependencies.size() > 0) {
+                    int numDependencies = node.dependencies.size();
+                    for (int j = 0; j < numDependencies; ++j) {
+                        Dependency dependency = node.dependencies.get(j);
+                        if (node.nodeDependencies == null) {
+                            node.nodeDependencies = new ArrayList<Node>();
+                        }
+                        if (!node.nodeDependencies.contains(dependency.node)) {
+                            node.nodeDependencies.add(dependency.node);
+                        }
+                    }
+                }
+                node.done = false;
+            }
+        }
+    }
+
+    /**
+     * Dependency holds information about the node that some other node is
+     * dependent upon and the nature of that dependency.
+     *
+     */
+    private static class Dependency {
+        static final int WITH = 0; // dependent node must start with this dependency node
+        static final int AFTER = 1; // dependent node must start when this dependency node finishes
+
+        // The node that the other node with this Dependency is dependent upon
+        public Node node;
+
+        // The nature of the dependency (WITH or AFTER)
+        public int rule;
+
+        public Dependency(Node node, int rule) {
+            this.node = node;
+            this.rule = rule;
+        }
+    }
+
+    /**
+     * A Node is an embodiment of both the Animator that it wraps as well as
+     * any dependencies that are associated with that Animation. This includes
+     * both dependencies upon other nodes (in the dependencies list) as
+     * well as dependencies of other nodes upon this (in the nodeDependents list).
+     */
+    private static class Node implements Cloneable {
+        public Animator animation;
+
+        /**
+         *  These are the dependencies that this node's animation has on other
+         *  nodes. For example, if this node's animation should begin with some
+         *  other animation ends, then there will be an item in this node's
+         *  dependencies list for that other animation's node.
+         */
+        public ArrayList<Dependency> dependencies = null;
+
+        /**
+         * tmpDependencies is a runtime detail. We use the dependencies list for sorting.
+         * But we also use the list to keep track of when multiple dependencies are satisfied,
+         * but removing each dependency as it is satisfied. We do not want to remove
+         * the dependency itself from the list, because we need to retain that information
+         * if the AnimatorSet is launched in the future. So we create a copy of the dependency
+         * list when the AnimatorSet starts and use this tmpDependencies list to track the
+         * list of satisfied dependencies.
+         */
+        public ArrayList<Dependency> tmpDependencies = null;
+
+        /**
+         * nodeDependencies is just a list of the nodes that this Node is dependent upon.
+         * This information is used in sortNodes(), to determine when a node is a root.
+         */
+        public ArrayList<Node> nodeDependencies = null;
+
+        /**
+         * nodeDepdendents is the list of nodes that have this node as a dependency. This
+         * is a utility field used in sortNodes to facilitate removing this node as a
+         * dependency when it is a root node.
+         */
+        public ArrayList<Node> nodeDependents = null;
+
+        /**
+         * Flag indicating whether the animation in this node is finished. This flag
+         * is used by AnimatorSet to check, as each animation ends, whether all child animations
+         * are done and it's time to send out an end event for the entire AnimatorSet.
+         */
+        public boolean done = false;
+
+        /**
+         * Constructs the Node with the animation that it encapsulates. A Node has no
+         * dependencies by default; dependencies are added via the addDependency()
+         * method.
+         *
+         * @param animation The animation that the Node encapsulates.
+         */
+        public Node(Animator animation) {
+            this.animation = animation;
+        }
+
+        /**
+         * Add a dependency to this Node. The dependency includes information about the
+         * node that this node is dependency upon and the nature of the dependency.
+         * @param dependency
+         */
+        public void addDependency(Dependency dependency) {
+            if (dependencies == null) {
+                dependencies = new ArrayList<Dependency>();
+                nodeDependencies = new ArrayList<Node>();
+            }
+            dependencies.add(dependency);
+            if (!nodeDependencies.contains(dependency.node)) {
+                nodeDependencies.add(dependency.node);
+            }
+            Node dependencyNode = dependency.node;
+            if (dependencyNode.nodeDependents == null) {
+                dependencyNode.nodeDependents = new ArrayList<Node>();
+            }
+            dependencyNode.nodeDependents.add(this);
+        }
+
+        @Override
+        public Node clone() {
+            try {
+                Node node = (Node) super.clone();
+                node.animation = (Animator) animation.clone();
+                return node;
+            } catch (CloneNotSupportedException e) {
+               throw new AssertionError();
+            }
+        }
+    }
+
+    /**
+     * The <code>Builder</code> object is a utility class to facilitate adding animations to a
+     * <code>AnimatorSet</code> along with the relationships between the various animations. The
+     * intention of the <code>Builder</code> methods, along with the {@link
+     * AnimatorSet#play(Animator) play()} method of <code>AnimatorSet</code> is to make it possible to
+     * express the dependency relationships of animations in a natural way. Developers can also use
+     * the {@link AnimatorSet#playTogether(Animator[]) playTogether()} and {@link
+     * AnimatorSet#playSequentially(Animator[]) playSequentially()} methods if these suit the need,
+     * but it might be easier in some situations to express the AnimatorSet of animations in pairs.
+     * <p/>
+     * <p>The <code>Builder</code> object cannot be constructed directly, but is rather constructed
+     * internally via a call to {@link AnimatorSet#play(Animator)}.</p>
+     * <p/>
+     * <p>For example, this sets up a AnimatorSet to play anim1 and anim2 at the same time, anim3 to
+     * play when anim2 finishes, and anim4 to play when anim3 finishes:</p>
+     * <pre>
+     *     AnimatorSet s = new AnimatorSet();
+     *     s.play(anim1).with(anim2);
+     *     s.play(anim2).before(anim3);
+     *     s.play(anim4).after(anim3);
+     * </pre>
+     * <p/>
+     * <p>Note in the example that both {@link Builder#before(Animator)} and {@link
+     * Builder#after(Animator)} are used. These are just different ways of expressing the same
+     * relationship and are provided to make it easier to say things in a way that is more natural,
+     * depending on the situation.</p>
+     * <p/>
+     * <p>It is possible to make several calls into the same <code>Builder</code> object to express
+     * multiple relationships. However, note that it is only the animation passed into the initial
+     * {@link AnimatorSet#play(Animator)} method that is the dependency in any of the successive
+     * calls to the <code>Builder</code> object. For example, the following code starts both anim2
+     * and anim3 when anim1 ends; there is no direct dependency relationship between anim2 and
+     * anim3:
+     * <pre>
+     *   AnimatorSet s = new AnimatorSet();
+     *   s.play(anim1).before(anim2).before(anim3);
+     * </pre>
+     * If the desired result is to play anim1 then anim2 then anim3, this code expresses the
+     * relationship correctly:</p>
+     * <pre>
+     *   AnimatorSet s = new AnimatorSet();
+     *   s.play(anim1).before(anim2);
+     *   s.play(anim2).before(anim3);
+     * </pre>
+     * <p/>
+     * <p>Note that it is possible to express relationships that cannot be resolved and will not
+     * result in sensible results. For example, <code>play(anim1).after(anim1)</code> makes no
+     * sense. In general, circular dependencies like this one (or more indirect ones where a depends
+     * on b, which depends on c, which depends on a) should be avoided. Only create AnimatorSets
+     * that can boil down to a simple, one-way relationship of animations starting with, before, and
+     * after other, different, animations.</p>
+     */
+    public class Builder {
+
+        /**
+         * This tracks the current node being processed. It is supplied to the play() method
+         * of AnimatorSet and passed into the constructor of Builder.
+         */
+        private Node mCurrentNode;
+
+        /**
+         * package-private constructor. Builders are only constructed by AnimatorSet, when the
+         * play() method is called.
+         *
+         * @param anim The animation that is the dependency for the other animations passed into
+         * the other methods of this Builder object.
+         */
+        Builder(Animator anim) {
+            mCurrentNode = mNodeMap.get(anim);
+            if (mCurrentNode == null) {
+                mCurrentNode = new Node(anim);
+                mNodeMap.put(anim, mCurrentNode);
+                mNodes.add(mCurrentNode);
+            }
+        }
+
+        /**
+         * Sets up the given animation to play at the same time as the animation supplied in the
+         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object.
+         *
+         * @param anim The animation that will play when the animation supplied to the
+         * {@link AnimatorSet#play(Animator)} method starts.
+         */
+        public void with(Animator anim) {
+            Node node = mNodeMap.get(anim);
+            if (node == null) {
+                node = new Node(anim);
+                mNodeMap.put(anim, node);
+                mNodes.add(node);
+            }
+            Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
+            node.addDependency(dependency);
+        }
+
+        /**
+         * Sets up the given animation to play when the animation supplied in the
+         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
+         * ends.
+         *
+         * @param anim The animation that will play when the animation supplied to the
+         * {@link AnimatorSet#play(Animator)} method ends.
+         */
+        public void before(Animator anim) {
+            Node node = mNodeMap.get(anim);
+            if (node == null) {
+                node = new Node(anim);
+                mNodeMap.put(anim, node);
+                mNodes.add(node);
+            }
+            Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
+            node.addDependency(dependency);
+        }
+
+        /**
+         * Sets up the given animation to play when the animation supplied in the
+         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
+         * to start when the animation supplied in this method call ends.
+         *
+         * @param anim The animation whose end will cause the animation supplied to the
+         * {@link AnimatorSet#play(Animator)} method to play.
+         */
+        public void after(Animator anim) {
+            Node node = mNodeMap.get(anim);
+            if (node == null) {
+                node = new Node(anim);
+                mNodeMap.put(anim, node);
+                mNodes.add(node);
+            }
+            Dependency dependency = new Dependency(node, Dependency.AFTER);
+            mCurrentNode.addDependency(dependency);
+        }
+
+        /**
+         * Sets up the animation supplied in the
+         * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object
+         * to play when the given amount of time elapses.
+         *
+         * @param delay The number of milliseconds that should elapse before the
+         * animation starts.
+         */
+        public void after(long delay) {
+            // setup dummy ValueAnimator just to run the clock
+            ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+            anim.setDuration(delay);
+            after(anim);
+        }
+
+    }
+
+}
diff --git a/core/java/android/animation/DoubleEvaluator.java b/core/java/android/animation/DoubleEvaluator.java
new file mode 100644
index 0000000..e46eb37
--- /dev/null
+++ b/core/java/android/animation/DoubleEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+/**
+ * This evaluator can be used to perform type interpolation between <code>double</code> values.
+ */
+public class DoubleEvaluator implements TypeEvaluator {
+    /**
+     * This function returns the result of linearly interpolating the start and end values, with
+     * <code>fraction</code> representing the proportion between the start and end values. The
+     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
+     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
+     * and <code>t</code> is <code>fraction</code>.
+     *
+     * @param fraction   The fraction from the starting to the ending values
+     * @param startValue The start value; should be of type <code>double</code> or
+     *                   <code>Double</code>
+     * @param endValue   The end value; should be of type <code>double</code> or
+     *                   <code>Double</code>
+     * @return A linear interpolation between the start and end values, given the
+     *         <code>fraction</code> parameter.
+     */
+    public Object evaluate(float fraction, Object startValue, Object endValue) {
+        double startDouble = ((Number) startValue).doubleValue();
+        return startDouble + fraction * (((Number) endValue).doubleValue() - startDouble);
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/animation/FloatEvaluator.java b/core/java/android/animation/FloatEvaluator.java
new file mode 100644
index 0000000..9e2054d
--- /dev/null
+++ b/core/java/android/animation/FloatEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+/**
+ * This evaluator can be used to perform type interpolation between <code>float</code> values.
+ */
+public class FloatEvaluator implements TypeEvaluator {
+
+    /**
+     * This function returns the result of linearly interpolating the start and end values, with
+     * <code>fraction</code> representing the proportion between the start and end values. The
+     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
+     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
+     * and <code>t</code> is <code>fraction</code>.
+     *
+     * @param fraction   The fraction from the starting to the ending values
+     * @param startValue The start value; should be of type <code>float</code> or
+     *                   <code>Float</code>
+     * @param endValue   The end value; should be of type <code>float</code> or <code>Float</code>
+     * @return A linear interpolation between the start and end values, given the
+     *         <code>fraction</code> parameter.
+     */
+    public Object evaluate(float fraction, Object startValue, Object endValue) {
+        float startFloat = ((Number) startValue).floatValue();
+        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/animation/FloatKeyframeSet.java b/core/java/android/animation/FloatKeyframeSet.java
new file mode 100644
index 0000000..6fad4a68
--- /dev/null
+++ b/core/java/android/animation/FloatKeyframeSet.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import android.animation.Keyframe.FloatKeyframe;
+
+import java.util.ArrayList;
+
+/**
+ * This class holds a collection of FloatKeyframe objects and is called by ValueAnimator to calculate
+ * values between those keyframes for a given animation. The class internal to the animation
+ * package because it is an implementation detail of how Keyframes are stored and used.
+ *
+ * <p>This type-specific subclass of KeyframeSet, along with the other type-specific subclasses for
+ * int, long, and double, exists to speed up the getValue() method when there is no custom
+ * TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the
+ * Object equivalents of these primitive types.</p>
+ */
+class FloatKeyframeSet extends KeyframeSet {
+    private float firstValue;
+    private float lastValue;
+    private float deltaValue;
+    private boolean firstTime = true;
+
+    public FloatKeyframeSet(FloatKeyframe... keyframes) {
+        super(keyframes);
+    }
+
+    @Override
+    public Object getValue(float fraction) {
+        return getFloatValue(fraction);
+    }
+
+    @Override
+    public FloatKeyframeSet clone() {
+        ArrayList<Keyframe> keyframes = mKeyframes;
+        int numKeyframes = mKeyframes.size();
+        FloatKeyframe[] newKeyframes = new FloatKeyframe[numKeyframes];
+        for (int i = 0; i < numKeyframes; ++i) {
+            newKeyframes[i] = (FloatKeyframe) keyframes.get(i).clone();
+        }
+        FloatKeyframeSet newSet = new FloatKeyframeSet(newKeyframes);
+        return newSet;
+    }
+
+    public float getFloatValue(float fraction) {
+        if (mNumKeyframes == 2) {
+            if (firstTime) {
+                firstTime = false;
+                firstValue = ((FloatKeyframe) mKeyframes.get(0)).getFloatValue();
+                lastValue = ((FloatKeyframe) mKeyframes.get(1)).getFloatValue();
+                deltaValue = lastValue - firstValue;
+            }
+            if (mInterpolator != null) {
+                fraction = mInterpolator.getInterpolation(fraction);
+            }
+            if (mEvaluator == null) {
+                return firstValue + fraction * deltaValue;
+            } else {
+                return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).floatValue();
+            }
+        }
+        if (fraction <= 0f) {
+            final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
+            final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);
+            float prevValue = prevKeyframe.getFloatValue();
+            float nextValue = nextKeyframe.getFloatValue();
+            float prevFraction = prevKeyframe.getFraction();
+            float nextFraction = nextKeyframe.getFraction();
+            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+            if (interpolator != null) {
+                fraction = interpolator.getInterpolation(fraction);
+            }
+            float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
+            return mEvaluator == null ?
+                    prevValue + fraction * (nextValue - prevValue) :
+                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+                            floatValue();
+        } else if (fraction >= 1f) {
+            final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
+            final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
+            float prevValue = prevKeyframe.getFloatValue();
+            float nextValue = nextKeyframe.getFloatValue();
+            float prevFraction = prevKeyframe.getFraction();
+            float nextFraction = nextKeyframe.getFraction();
+            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+            if (interpolator != null) {
+                fraction = interpolator.getInterpolation(fraction);
+            }
+            float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
+            return mEvaluator == null ?
+                    prevValue + fraction * (nextValue - prevValue) :
+                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+                            floatValue();
+        }
+        FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
+        for (int i = 1; i < mNumKeyframes; ++i) {
+            FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
+            if (fraction < nextKeyframe.getFraction()) {
+                final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+                if (interpolator != null) {
+                    fraction = interpolator.getInterpolation(fraction);
+                }
+                float intervalFraction = (fraction - prevKeyframe.getFraction()) /
+                    (nextKeyframe.getFraction() - prevKeyframe.getFraction());
+                float prevValue = prevKeyframe.getFloatValue();
+                float nextValue = nextKeyframe.getFloatValue();
+                return mEvaluator == null ?
+                        prevValue + fraction * (nextValue - prevValue) :
+                        ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+                            floatValue();
+            }
+            prevKeyframe = nextKeyframe;
+        }
+        // shouldn't get here
+        return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
+    }
+
+}
+
diff --git a/core/java/android/animation/IntEvaluator.java b/core/java/android/animation/IntEvaluator.java
new file mode 100644
index 0000000..7288927
--- /dev/null
+++ b/core/java/android/animation/IntEvaluator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+/**
+ * This evaluator can be used to perform type interpolation between <code>int</code> values.
+ */
+public class IntEvaluator implements TypeEvaluator {
+
+    /**
+     * This function returns the result of linearly interpolating the start and end values, with
+     * <code>fraction</code> representing the proportion between the start and end values. The
+     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
+     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
+     * and <code>t</code> is <code>fraction</code>.
+     *
+     * @param fraction   The fraction from the starting to the ending values
+     * @param startValue The start value; should be of type <code>int</code> or
+     *                   <code>Integer</code>
+     * @param endValue   The end value; should be of type <code>int</code> or <code>Integer</code>
+     * @return A linear interpolation between the start and end values, given the
+     *         <code>fraction</code> parameter.
+     */
+    public Object evaluate(float fraction, Object startValue, Object endValue) {
+        int startInt = ((Number) startValue).intValue();
+        return (int) (startInt + fraction * (((Number) endValue).intValue() - startInt));
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/animation/IntKeyframeSet.java b/core/java/android/animation/IntKeyframeSet.java
new file mode 100644
index 0000000..14a4e3a
--- /dev/null
+++ b/core/java/android/animation/IntKeyframeSet.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import android.animation.Keyframe.IntKeyframe;
+
+import java.util.ArrayList;
+
+/**
+ * This class holds a collection of IntKeyframe objects and is called by ValueAnimator to calculate
+ * values between those keyframes for a given animation. The class internal to the animation
+ * package because it is an implementation detail of how Keyframes are stored and used.
+ *
+ * <p>This type-specific subclass of KeyframeSet, along with the other type-specific subclasses for
+ * float, long, and double, exists to speed up the getValue() method when there is no custom
+ * TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the
+ * Object equivalents of these primitive types.</p>
+ */
+class IntKeyframeSet extends KeyframeSet {
+    private int firstValue;
+    private int lastValue;
+    private int deltaValue;
+    private boolean firstTime = true;
+
+    public IntKeyframeSet(IntKeyframe... keyframes) {
+        super(keyframes);
+    }
+
+    @Override
+    public Object getValue(float fraction) {
+        return getIntValue(fraction);
+    }
+
+    @Override
+    public IntKeyframeSet clone() {
+        ArrayList<Keyframe> keyframes = mKeyframes;
+        int numKeyframes = mKeyframes.size();
+        IntKeyframe[] newKeyframes = new IntKeyframe[numKeyframes];
+        for (int i = 0; i < numKeyframes; ++i) {
+            newKeyframes[i] = (IntKeyframe) keyframes.get(i).clone();
+        }
+        IntKeyframeSet newSet = new IntKeyframeSet(newKeyframes);
+        return newSet;
+    }
+
+    public int getIntValue(float fraction) {
+        if (mNumKeyframes == 2) {
+            if (firstTime) {
+                firstTime = false;
+                firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue();
+                lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue();
+                deltaValue = lastValue - firstValue;
+            }
+            if (mInterpolator != null) {
+                fraction = mInterpolator.getInterpolation(fraction);
+            }
+            if (mEvaluator == null) {
+                return firstValue + (int)(fraction * deltaValue);
+            } else {
+                return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();
+            }
+        }
+        if (fraction <= 0f) {
+            final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
+            final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(1);
+            int prevValue = prevKeyframe.getIntValue();
+            int nextValue = nextKeyframe.getIntValue();
+            float prevFraction = prevKeyframe.getFraction();
+            float nextFraction = nextKeyframe.getFraction();
+            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+            if (interpolator != null) {
+                fraction = interpolator.getInterpolation(fraction);
+            }
+            float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
+            return mEvaluator == null ?
+                    prevValue + (int)(fraction * (nextValue - prevValue)) :
+                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+                            intValue();
+        } else if (fraction >= 1f) {
+            final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 2);
+            final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 1);
+            int prevValue = prevKeyframe.getIntValue();
+            int nextValue = nextKeyframe.getIntValue();
+            float prevFraction = prevKeyframe.getFraction();
+            float nextFraction = nextKeyframe.getFraction();
+            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+            if (interpolator != null) {
+                fraction = interpolator.getInterpolation(fraction);
+            }
+            float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
+            return mEvaluator == null ?
+                    prevValue + (int)(fraction * (nextValue - prevValue)) :
+                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue();
+        }
+        IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
+        for (int i = 1; i < mNumKeyframes; ++i) {
+            IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i);
+            if (fraction < nextKeyframe.getFraction()) {
+                final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+                if (interpolator != null) {
+                    fraction = interpolator.getInterpolation(fraction);
+                }
+                float intervalFraction = (fraction - prevKeyframe.getFraction()) /
+                    (nextKeyframe.getFraction() - prevKeyframe.getFraction());
+                int prevValue = prevKeyframe.getIntValue();
+                int nextValue = nextKeyframe.getIntValue();
+                return mEvaluator == null ?
+                        prevValue + (int)(fraction * (nextValue - prevValue)) :
+                        ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
+                                intValue();
+            }
+            prevKeyframe = nextKeyframe;
+        }
+        // shouldn't get here
+        return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).intValue();
+    }
+
+}
+
diff --git a/core/java/android/animation/Keyframe.java b/core/java/android/animation/Keyframe.java
new file mode 100644
index 0000000..e98719a
--- /dev/null
+++ b/core/java/android/animation/Keyframe.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+/**
+ * This class holds a time/value pair for an animation. The Keyframe class is used
+ * by {@link ValueAnimator} to define the values that the animation target will have over the course
+ * of the animation. As the time proceeds from one keyframe to the other, the value of the
+ * target object will animate between the value at the previous keyframe and the value at the
+ * next keyframe. Each keyframe also holds an optional {@link TimeInterpolator}
+ * object, which defines the time interpolation over the intervalue preceding the keyframe.
+ *
+ * <p>The Keyframe class itself is abstract. The type-specific factory methods will return
+ * a subclass of Keyframe specific to the type of value being stored. This is done to improve
+ * performance when dealing with the most common cases (e.g., <code>float</code> and
+ * <code>int</code> values). Other types will fall into a more general Keyframe class that
+ * treats its values as Objects. Unless your animation requires dealing with a custom type
+ * or a data structure that needs to be animated directly (and evaluated using an implementation
+ * of {@link TypeEvaluator}), you should stick to using float and int as animations using those
+ * types have lower runtime overhead than other types.</p>
+ */
+public abstract class Keyframe implements Cloneable {
+    /**
+     * The time at which mValue will hold true.
+     */
+    float mFraction;
+
+    /**
+     * The type of the value in this Keyframe. This type is determined at construction time,
+     * based on the type of the <code>value</code> object passed into the constructor.
+     */
+    Class mValueType;
+
+    /**
+     * The optional time interpolator for the interval preceding this keyframe. A null interpolator
+     * (the default) results in linear interpolation over the interval.
+     */
+    private TimeInterpolator mInterpolator = null;
+
+    /**
+     * Flag to indicate whether this keyframe has a valid value. This flag is used when an
+     * animation first starts, to populate placeholder keyframes with real values derived
+     * from the target object.
+     */
+    boolean mHasValue = false;
+
+    /**
+     * Constructs a Keyframe object with the given time and value. The time defines the
+     * time, as a proportion of an overall animation's duration, at which the value will hold true
+     * for the animation. The value for the animation between keyframes will be calculated as
+     * an interpolation between the values at those keyframes.
+     *
+     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+     * of time elapsed of the overall animation duration.
+     * @param value The value that the object will animate to as the animation time approaches
+     * the time in this keyframe, and the the value animated from as the time passes the time in
+     * this keyframe.
+     */
+    public static Keyframe ofInt(float fraction, int value) {
+        return new IntKeyframe(fraction, value);
+    }
+
+    /**
+     * Constructs a Keyframe object with the given time. The value at this time will be derived
+     * from the target object when the animation first starts (note that this implies that keyframes
+     * with no initial value must be used as part of an {@link ObjectAnimator}).
+     * The time defines the
+     * time, as a proportion of an overall animation's duration, at which the value will hold true
+     * for the animation. The value for the animation between keyframes will be calculated as
+     * an interpolation between the values at those keyframes.
+     *
+     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+     * of time elapsed of the overall animation duration.
+     */
+    public static Keyframe ofInt(float fraction) {
+        return new IntKeyframe(fraction);
+    }
+
+    /**
+     * Constructs a Keyframe object with the given time and value. The time defines the
+     * time, as a proportion of an overall animation's duration, at which the value will hold true
+     * for the animation. The value for the animation between keyframes will be calculated as
+     * an interpolation between the values at those keyframes.
+     *
+     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+     * of time elapsed of the overall animation duration.
+     * @param value The value that the object will animate to as the animation time approaches
+     * the time in this keyframe, and the the value animated from as the time passes the time in
+     * this keyframe.
+     */
+    public static Keyframe ofFloat(float fraction, float value) {
+        return new FloatKeyframe(fraction, value);
+    }
+
+    /**
+     * Constructs a Keyframe object with the given time. The value at this time will be derived
+     * from the target object when the animation first starts (note that this implies that keyframes
+     * with no initial value must be used as part of an {@link ObjectAnimator}).
+     * The time defines the
+     * time, as a proportion of an overall animation's duration, at which the value will hold true
+     * for the animation. The value for the animation between keyframes will be calculated as
+     * an interpolation between the values at those keyframes.
+     *
+     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+     * of time elapsed of the overall animation duration.
+     */
+    public static Keyframe ofFloat(float fraction) {
+        return new FloatKeyframe(fraction);
+    }
+
+    /**
+     * Constructs a Keyframe object with the given time and value. The time defines the
+     * time, as a proportion of an overall animation's duration, at which the value will hold true
+     * for the animation. The value for the animation between keyframes will be calculated as
+     * an interpolation between the values at those keyframes.
+     *
+     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+     * of time elapsed of the overall animation duration.
+     * @param value The value that the object will animate to as the animation time approaches
+     * the time in this keyframe, and the the value animated from as the time passes the time in
+     * this keyframe.
+     */
+    public static Keyframe ofObject(float fraction, Object value) {
+        return new ObjectKeyframe(fraction, value);
+    }
+
+    /**
+     * Constructs a Keyframe object with the given time. The value at this time will be derived
+     * from the target object when the animation first starts (note that this implies that keyframes
+     * with no initial value must be used as part of an {@link ObjectAnimator}).
+     * The time defines the
+     * time, as a proportion of an overall animation's duration, at which the value will hold true
+     * for the animation. The value for the animation between keyframes will be calculated as
+     * an interpolation between the values at those keyframes.
+     *
+     * @param fraction The time, expressed as a value between 0 and 1, representing the fraction
+     * of time elapsed of the overall animation duration.
+     */
+    public static Keyframe ofObject(float fraction) {
+        return new ObjectKeyframe(fraction, null);
+    }
+
+    /**
+     * Indicates whether this keyframe has a valid value. This method is called internally when
+     * an {@link ObjectAnimator} first starts; keyframes without values are assigned values at
+     * that time by deriving the value for the property from the target object.
+     *
+     * @return boolean Whether this object has a value assigned.
+     */
+    public boolean hasValue() {
+        return mHasValue;
+    }
+
+    /**
+     * Gets the value for this Keyframe.
+     *
+     * @return The value for this Keyframe.
+     */
+    public abstract Object getValue();
+
+    /**
+     * Sets the value for this Keyframe.
+     *
+     * @param value value for this Keyframe.
+     */
+    public abstract void setValue(Object value);
+
+    /**
+     * Gets the time for this keyframe, as a fraction of the overall animation duration.
+     *
+     * @return The time associated with this keyframe, as a fraction of the overall animation
+     * duration. This should be a value between 0 and 1.
+     */
+    public float getFraction() {
+        return mFraction;
+    }
+
+    /**
+     * Sets the time for this keyframe, as a fraction of the overall animation duration.
+     *
+     * @param fraction time associated with this keyframe, as a fraction of the overall animation
+     * duration. This should be a value between 0 and 1.
+     */
+    public void setFraction(float fraction) {
+        mFraction = fraction;
+    }
+
+    /**
+     * Gets the optional interpolator for this Keyframe. A value of <code>null</code> indicates
+     * that there is no interpolation, which is the same as linear interpolation.
+     *
+     * @return The optional interpolator for this Keyframe.
+     */
+    public TimeInterpolator getInterpolator() {
+        return mInterpolator;
+    }
+
+    /**
+     * Sets the optional interpolator for this Keyframe. A value of <code>null</code> indicates
+     * that there is no interpolation, which is the same as linear interpolation.
+     *
+     * @return The optional interpolator for this Keyframe.
+     */
+    public void setInterpolator(TimeInterpolator interpolator) {
+        mInterpolator = interpolator;
+    }
+
+    /**
+     * Gets the type of keyframe. This information is used by ValueAnimator to determine the type of
+     * {@link TypeEvaluator} to use when calculating values between keyframes. The type is based
+     * on the type of Keyframe created.
+     *
+     * @return The type of the value stored in the Keyframe.
+     */
+    public Class getType() {
+        return mValueType;
+    }
+
+    @Override
+    public abstract Keyframe clone();
+
+    /**
+     * This internal subclass is used for all types which are not int or float.
+     */
+    static class ObjectKeyframe extends Keyframe {
+
+        /**
+         * The value of the animation at the time mFraction.
+         */
+        Object mValue;
+
+        ObjectKeyframe(float fraction, Object value) {
+            mFraction = fraction;
+            mValue = value;
+            mHasValue = (value != null);
+            mValueType = mHasValue ? value.getClass() : Object.class;
+        }
+
+        public Object getValue() {
+            return mValue;
+        }
+
+        public void setValue(Object value) {
+            mValue = value;
+            mHasValue = (value != null);
+        }
+
+        @Override
+        public ObjectKeyframe clone() {
+            ObjectKeyframe kfClone = new ObjectKeyframe(getFraction(), mValue);
+            kfClone.setInterpolator(getInterpolator());
+            return kfClone;
+        }
+    }
+
+    /**
+     * Internal subclass used when the keyframe value is of type int.
+     */
+    static class IntKeyframe extends Keyframe {
+
+        /**
+         * The value of the animation at the time mFraction.
+         */
+        int mValue;
+
+        IntKeyframe(float fraction, int value) {
+            mFraction = fraction;
+            mValue = value;
+            mValueType = int.class;
+            mHasValue = true;
+        }
+
+        IntKeyframe(float fraction) {
+            mFraction = fraction;
+            mValueType = int.class;
+        }
+
+        public int getIntValue() {
+            return mValue;
+        }
+
+        public Object getValue() {
+            return mValue;
+        }
+
+        public void setValue(Object value) {
+            if (value != null && value.getClass() == Integer.class) {
+                mValue = ((Integer)value).intValue();
+                mHasValue = true;
+            }
+        }
+
+        @Override
+        public IntKeyframe clone() {
+            IntKeyframe kfClone = new IntKeyframe(getFraction(), mValue);
+            kfClone.setInterpolator(getInterpolator());
+            return kfClone;
+        }
+    }
+
+    /**
+     * Internal subclass used when the keyframe value is of type float.
+     */
+    static class FloatKeyframe extends Keyframe {
+        /**
+         * The value of the animation at the time mFraction.
+         */
+        float mValue;
+
+        FloatKeyframe(float fraction, float value) {
+            mFraction = fraction;
+            mValue = value;
+            mValueType = float.class;
+            mHasValue = true;
+        }
+
+        FloatKeyframe(float fraction) {
+            mFraction = fraction;
+            mValueType = float.class;
+        }
+
+        public float getFloatValue() {
+            return mValue;
+        }
+
+        public Object getValue() {
+            return mValue;
+        }
+
+        public void setValue(Object value) {
+            if (value != null && value.getClass() == Float.class) {
+                mValue = ((Float)value).floatValue();
+                mHasValue = true;
+            }
+        }
+
+        @Override
+        public FloatKeyframe clone() {
+            FloatKeyframe kfClone = new FloatKeyframe(getFraction(), mValue);
+            kfClone.setInterpolator(getInterpolator());
+            return kfClone;
+        }
+    }
+}
diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java
new file mode 100644
index 0000000..fa61b71
--- /dev/null
+++ b/core/java/android/animation/KeyframeSet.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import android.animation.Keyframe.IntKeyframe;
+import android.animation.Keyframe.FloatKeyframe;
+import android.animation.Keyframe.ObjectKeyframe;
+
+/**
+ * This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
+ * values between those keyframes for a given animation. The class internal to the animation
+ * package because it is an implementation detail of how Keyframes are stored and used.
+ */
+class KeyframeSet {
+
+    int mNumKeyframes;
+
+    Keyframe mFirstKeyframe;
+    Keyframe mLastKeyframe;
+    TimeInterpolator mInterpolator; // only used in the 2-keyframe case
+    ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes
+    TypeEvaluator mEvaluator;
+
+
+    public KeyframeSet(Keyframe... keyframes) {
+        mNumKeyframes = keyframes.length;
+        mKeyframes = new ArrayList<Keyframe>();
+        mKeyframes.addAll(Arrays.asList(keyframes));
+        mFirstKeyframe = mKeyframes.get(0);
+        mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
+        mInterpolator = mLastKeyframe.getInterpolator();
+    }
+
+    public static KeyframeSet ofInt(int... values) {
+        int numKeyframes = values.length;
+        IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
+        if (numKeyframes == 1) {
+            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
+            keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
+        } else {
+            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
+            for (int i = 1; i < numKeyframes; ++i) {
+                keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
+            }
+        }
+        return new IntKeyframeSet(keyframes);
+    }
+
+    public static KeyframeSet ofFloat(float... values) {
+        int numKeyframes = values.length;
+        FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
+        if (numKeyframes == 1) {
+            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
+            keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
+        } else {
+            keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
+            for (int i = 1; i < numKeyframes; ++i) {
+                keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
+            }
+        }
+        return new FloatKeyframeSet(keyframes);
+    }
+
+    public static KeyframeSet ofKeyframe(Keyframe... keyframes) {
+        // if all keyframes of same primitive type, create the appropriate KeyframeSet
+        int numKeyframes = keyframes.length;
+        boolean hasFloat = false;
+        boolean hasInt = false;
+        boolean hasOther = false;
+        for (int i = 0; i < numKeyframes; ++i) {
+            if (keyframes[i] instanceof FloatKeyframe) {
+                hasFloat = true;
+            } else if (keyframes[i] instanceof IntKeyframe) {
+                hasInt = true;
+            } else {
+                hasOther = true;
+            }
+        }
+        if (hasFloat && !hasInt && !hasOther) {
+            FloatKeyframe floatKeyframes[] = new FloatKeyframe[numKeyframes];
+            for (int i = 0; i < numKeyframes; ++i) {
+                floatKeyframes[i] = (FloatKeyframe) keyframes[i];
+            }
+            return new FloatKeyframeSet(floatKeyframes);
+        } else if (hasInt && !hasFloat && !hasOther) {
+            IntKeyframe intKeyframes[] = new IntKeyframe[numKeyframes];
+            for (int i = 0; i < numKeyframes; ++i) {
+                intKeyframes[i] = (IntKeyframe) keyframes[i];
+            }
+            return new IntKeyframeSet(intKeyframes);
+        } else {
+            return new KeyframeSet(keyframes);
+        }
+    }
+
+    public static KeyframeSet ofObject(Object... values) {
+        int numKeyframes = values.length;
+        ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,2)];
+        if (numKeyframes == 1) {
+            keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f);
+            keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]);
+        } else {
+            keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]);
+            for (int i = 1; i < numKeyframes; ++i) {
+                keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]);
+            }
+        }
+        return new KeyframeSet(keyframes);
+    }
+
+    /**
+     * Sets the TypeEvaluator to be used when calculating animated values. This object
+     * is required only for KeyframeSets that are not either IntKeyframeSet or FloatKeyframeSet,
+     * both of which assume their own evaluator to speed up calculations with those primitive
+     * types.
+     *
+     * @param evaluator The TypeEvaluator to be used to calculate animated values.
+     */
+    public void setEvaluator(TypeEvaluator evaluator) {
+        mEvaluator = evaluator;
+    }
+
+    @Override
+    public KeyframeSet clone() {
+        ArrayList<Keyframe> keyframes = mKeyframes;
+        int numKeyframes = mKeyframes.size();
+        Keyframe[] newKeyframes = new Keyframe[numKeyframes];
+        for (int i = 0; i < numKeyframes; ++i) {
+            newKeyframes[i] = keyframes.get(i).clone();
+        }
+        KeyframeSet newSet = new KeyframeSet(newKeyframes);
+        return newSet;
+    }
+
+    /**
+     * Gets the animated value, given the elapsed fraction of the animation (interpolated by the
+     * animation's interpolator) and the evaluator used to calculate in-between values. This
+     * function maps the input fraction to the appropriate keyframe interval and a fraction
+     * between them and returns the interpolated value. Note that the input fraction may fall
+     * outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a
+     * spring interpolation that might send the fraction past 1.0). We handle this situation by
+     * just using the two keyframes at the appropriate end when the value is outside those bounds.
+     *
+     * @param fraction The elapsed fraction of the animation
+     * @return The animated value.
+     */
+    public Object getValue(float fraction) {
+
+        // Special-case optimization for the common case of only two keyframes
+        if (mNumKeyframes == 2) {
+            if (mInterpolator != null) {
+                fraction = mInterpolator.getInterpolation(fraction);
+            }
+            return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
+                    mLastKeyframe.getValue());
+        }
+        if (fraction <= 0f) {
+            final Keyframe nextKeyframe = mKeyframes.get(1);
+            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+            if (interpolator != null) {
+                fraction = interpolator.getInterpolation(fraction);
+            }
+            final float prevFraction = mFirstKeyframe.getFraction();
+            float intervalFraction = (fraction - prevFraction) /
+                (nextKeyframe.getFraction() - prevFraction);
+            return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
+                    nextKeyframe.getValue());
+        } else if (fraction >= 1f) {
+            final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
+            final TimeInterpolator interpolator = mLastKeyframe.getInterpolator();
+            if (interpolator != null) {
+                fraction = interpolator.getInterpolation(fraction);
+            }
+            final float prevFraction = prevKeyframe.getFraction();
+            float intervalFraction = (fraction - prevFraction) /
+                (mLastKeyframe.getFraction() - prevFraction);
+            return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
+                    mLastKeyframe.getValue());
+        }
+        Keyframe prevKeyframe = mFirstKeyframe;
+        for (int i = 1; i < mNumKeyframes; ++i) {
+            Keyframe nextKeyframe = mKeyframes.get(i);
+            if (fraction < nextKeyframe.getFraction()) {
+                final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
+                if (interpolator != null) {
+                    fraction = interpolator.getInterpolation(fraction);
+                }
+                final float prevFraction = prevKeyframe.getFraction();
+                float intervalFraction = (fraction - prevFraction) /
+                    (nextKeyframe.getFraction() - prevFraction);
+                return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
+                        nextKeyframe.getValue());
+            }
+            prevKeyframe = nextKeyframe;
+        }
+        // shouldn't reach here
+        return mLastKeyframe.getValue();
+    }
+}
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
new file mode 100644
index 0000000..d843737
--- /dev/null
+++ b/core/java/android/animation/LayoutTransition.java
@@ -0,0 +1,835 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This class enables automatic animations on layout changes in ViewGroup objects. To enable
+ * transitions for a layout container, create a LayoutTransition object and set it on any
+ * ViewGroup by calling {@link ViewGroup#setLayoutTransition(LayoutTransition)}. This will cause
+ * default animations to run whenever items are added to or removed from that container. To specify
+ * custom animations, use the {@link LayoutTransition#setAnimator(int, Animator)
+ * setAnimator()} method.
+ *
+ * <p>One of the core concepts of these transition animations is that there are two core
+ * changes that cause the transition and four different animations that run because of
+ * those changes. The changes that trigger the transition are items being added to a container
+ * (referred to as an "appearing" transition) or removed from a container (also known as
+ * "disappearing"). The animations that run due to those events are one that animates
+ * items being added, one that animates items being removed, and two that animate the other
+ * items in the container that change due to the add/remove occurrence. Users of
+ * the transition may want different animations for the changing items depending on whether
+ * they are changing due to anappearing or disappearing event, so there is one animation for
+ * each of these variations of the changing event. Most of the API of this class is concerned
+ * with setting up the basic properties of the animations used in these four situations,
+ * or with setting up custom animations for any or all of the four.</p>
+ *
+ * <p>The animations specified for the transition, both the defaults and any custom animations
+ * set on the transition object, are templates only. That is, these animations exist to hold the
+ * basic animation properties, such as the duration, start delay, and properties being animated.
+ * But the actual target object, as well as the start and end values for those properties, are
+ * set automatically in the process of setting up the transition each time it runs. Each of the
+ * animations is cloned from the original copy and the clone is then populated with the dynamic
+ * values of the target being animated (such as one of the items in a layout container that is
+ * moving as a result of the layout event) as well as the values that are changing (such as the
+ * position and size of that object). The actual values that are pushed to each animation
+ * depends on what properties are specified for the animation. For example, the default
+ * CHANGE_APPEARING animation animates <code>left</code>, <code>top</code>, <code>right</code>,
+ * and <code>bottom</code>. Values for these properties are updated with the pre- and post-layout
+ * values when the transition begins. Custom animations will be similarly populated with
+ * the target and values being animated, assuming they use ObjectAnimator objects with
+ * property names that are known on the target object.</p>
+ */
+public class LayoutTransition {
+
+    /**
+     * A flag indicating the animation that runs on those items that are changing
+     * due to a new item appearing in the container.
+     */
+    public static final int CHANGE_APPEARING = 0;
+
+    /**
+     * A flag indicating the animation that runs on those items that are changing
+     * due to a new item disappearing from the container.
+     */
+    public static final int CHANGE_DISAPPEARING = 1;
+
+    /**
+     * A flag indicating the animation that runs on those items that are changing
+     * due to a new item appearing in the container.
+     */
+    public static final int APPEARING = 2;
+
+    /**
+     * A flag indicating the animation that runs on those items that are changing
+     * due to a new item appearing in the container.
+     */
+    public static final int DISAPPEARING = 3;
+
+    /**
+     * These variables hold the animations that are currently used to run the transition effects.
+     * These animations are set to defaults, but can be changed to custom animations by
+     * calls to setAnimator().
+     */
+    private Animator mDisappearingAnim = null;
+    private Animator mAppearingAnim = null;
+    private Animator mChangingAppearingAnim = null;
+    private Animator mChangingDisappearingAnim = null;
+
+    /**
+     * These are the default animations, defined in the constructor, that will be used
+     * unless the user specifies custom animations.
+     */
+    private static ObjectAnimator defaultChangeIn;
+    private static ObjectAnimator defaultChangeOut;
+    private static ObjectAnimator defaultFadeIn;
+    private static ObjectAnimator defaultFadeOut;
+
+    /**
+     * The default duration used by all animations.
+     */
+    private static long DEFAULT_DURATION = 300;
+
+    /**
+     * The durations of the four different animations
+     */
+    private long mChangingAppearingDuration = DEFAULT_DURATION;
+    private long mChangingDisappearingDuration = DEFAULT_DURATION;
+    private long mAppearingDuration = DEFAULT_DURATION;
+    private long mDisappearingDuration = DEFAULT_DURATION;
+
+    /**
+     * The start delays of the four different animations. Note that the default behavior of
+     * the appearing item is the default duration, since it should wait for the items to move
+     * before fading it. Same for the changing animation when disappearing; it waits for the item
+     * to fade out before moving the other items.
+     */
+    private long mAppearingDelay = DEFAULT_DURATION;
+    private long mDisappearingDelay = 0;
+    private long mChangingAppearingDelay = 0;
+    private long mChangingDisappearingDelay = DEFAULT_DURATION;
+
+    /**
+     * The inter-animation delays used on the two changing animations
+     */
+    private long mChangingAppearingStagger = 0;
+    private long mChangingDisappearingStagger = 0;
+
+    /**
+     * The default interpolators used for the animations
+     */
+    private TimeInterpolator mAppearingInterpolator = new AccelerateDecelerateInterpolator();
+    private TimeInterpolator mDisappearingInterpolator = new AccelerateDecelerateInterpolator();
+    private TimeInterpolator mChangingAppearingInterpolator = new DecelerateInterpolator();
+    private TimeInterpolator mChangingDisappearingInterpolator = new DecelerateInterpolator();
+
+    /**
+     * This hashmap is used to store the animations that are currently running as part of
+     * the transition. The reason for this is that a further layout event should cause
+     * existing animations to stop where they are prior to starting new animations. So
+     * we cache all of the current animations in this map for possible cancellation on
+     * another layout event.
+     */
+    private HashMap<View, Animator> currentAnimations = new HashMap<View, Animator>();
+
+    /**
+     * This hashmap is used to track the listeners that have been added to the children of
+     * a container. When a layout change occurs, an animation is created for each View, so that
+     * the pre-layout values can be cached in that animation. Then a listener is added to the
+     * view to see whether the layout changes the bounds of that view. If so, the animation
+     * is set with the final values and then run. If not, the animation is not started. When
+     * the process of setting up and running all appropriate animations is done, we need to
+     * remove these listeners and clear out the map.
+     */
+    private HashMap<View, View.OnLayoutChangeListener> layoutChangeListenerMap =
+            new HashMap<View, View.OnLayoutChangeListener>();
+
+    /**
+     * Used to track the current delay being assigned to successive animations as they are
+     * started. This value is incremented for each new animation, then zeroed before the next
+     * transition begins.
+     */
+    private long staggerDelay;
+
+    /**
+     * The set of listeners that should be notified when APPEARING/DISAPPEARING transitions
+     * start and end.
+     */
+    private ArrayList<TransitionListener> mListeners;
+
+
+    /**
+     * Constructs a LayoutTransition object. By default, the object will listen to layout
+     * events on any ViewGroup that it is set on and will run default animations for each
+     * type of layout event.
+     */
+    public LayoutTransition() {
+        if (defaultChangeIn == null) {
+            // "left" is just a placeholder; we'll put real properties/values in when needed
+            PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 1);
+            PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1);
+            PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 1);
+            PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1);
+            defaultChangeIn = ObjectAnimator.ofPropertyValuesHolder(this,
+                    pvhLeft, pvhTop, pvhRight, pvhBottom);
+            defaultChangeIn.setDuration(DEFAULT_DURATION);
+            defaultChangeIn.setStartDelay(mChangingAppearingDelay);
+            defaultChangeIn.setInterpolator(mChangingAppearingInterpolator);
+            defaultChangeOut = defaultChangeIn.clone();
+            defaultChangeOut.setStartDelay(mChangingDisappearingDelay);
+            defaultChangeOut.setInterpolator(mChangingDisappearingInterpolator);
+            defaultFadeIn = ObjectAnimator.ofFloat(this, "alpha", 0f, 1f);
+            defaultFadeIn.setDuration(DEFAULT_DURATION);
+            defaultFadeIn.setStartDelay(mAppearingDelay);
+            defaultFadeIn.setInterpolator(mAppearingInterpolator);
+            defaultFadeOut = ObjectAnimator.ofFloat(this, "alpha", 1f, 0f);
+            defaultFadeOut.setDuration(DEFAULT_DURATION);
+            defaultFadeOut.setStartDelay(mDisappearingDelay);
+            defaultFadeOut.setInterpolator(mDisappearingInterpolator);
+        }
+        mChangingAppearingAnim = defaultChangeIn;
+        mChangingDisappearingAnim = defaultChangeOut;
+        mAppearingAnim = defaultFadeIn;
+        mDisappearingAnim = defaultFadeOut;
+    }
+
+    /**
+     * Sets the duration to be used by all animations of this transition object. If you want to
+     * set the duration of just one of the animations in particular, use the
+     * {@link #setDuration(int, long)} method.
+     *
+     * @param duration The length of time, in milliseconds, that the transition animations
+     * should last.
+     */
+    public void setDuration(long duration) {
+        mChangingAppearingDuration = duration;
+        mChangingDisappearingDuration = duration;
+        mAppearingDuration = duration;
+        mDisappearingDuration = duration;
+    }
+
+    /**
+     * Sets the start delay on one of the animation objects used by this transition. The
+     * <code>transitionType</code> parameter determines the animation whose start delay
+     * is being set.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose start
+     * delay is being set.
+     * @param delay The length of time, in milliseconds, to delay before starting the animation.
+     * @see Animator#setStartDelay(long)
+     */
+    public void setStartDelay(int transitionType, long delay) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                mChangingAppearingDelay = delay;
+                break;
+            case CHANGE_DISAPPEARING:
+                mChangingDisappearingDelay = delay;
+                break;
+            case APPEARING:
+                mAppearingDelay = delay;
+                break;
+            case DISAPPEARING:
+                mDisappearingDelay = delay;
+                break;
+        }
+    }
+
+    /**
+     * Gets the start delay on one of the animation objects used by this transition. The
+     * <code>transitionType</code> parameter determines the animation whose start delay
+     * is returned.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose start
+     * delay is returned.
+     * @return long The start delay of the specified animation.
+     * @see Animator#getStartDelay()
+     */
+    public long getStartDelay(int transitionType) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                return mChangingAppearingDuration;
+            case CHANGE_DISAPPEARING:
+                return mChangingDisappearingDuration;
+            case APPEARING:
+                return mAppearingDuration;
+            case DISAPPEARING:
+                return mDisappearingDuration;
+        }
+        // shouldn't reach here
+        return 0;
+    }
+
+    /**
+     * Sets the duration on one of the animation objects used by this transition. The
+     * <code>transitionType</code> parameter determines the animation whose duration
+     * is being set.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+     * duration is being set.
+     * @param duration The length of time, in milliseconds, that the specified animation should run.
+     * @see Animator#setDuration(long)
+     */
+    public void setDuration(int transitionType, long duration) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                mChangingAppearingDuration = duration;
+                break;
+            case CHANGE_DISAPPEARING:
+                mChangingDisappearingDuration = duration;
+                break;
+            case APPEARING:
+                mAppearingDuration = duration;
+                break;
+            case DISAPPEARING:
+                mDisappearingDuration = duration;
+                break;
+        }
+    }
+
+    /**
+     * Gets the duration on one of the animation objects used by this transition. The
+     * <code>transitionType</code> parameter determines the animation whose duration
+     * is returned.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+     * duration is returned.
+     * @return long The duration of the specified animation.
+     * @see Animator#getDuration()
+     */
+    public long getDuration(int transitionType) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                return mChangingAppearingDuration;
+            case CHANGE_DISAPPEARING:
+                return mChangingDisappearingDuration;
+            case APPEARING:
+                return mAppearingDuration;
+            case DISAPPEARING:
+                return mDisappearingDuration;
+        }
+        // shouldn't reach here
+        return 0;
+    }
+
+    /**
+     * Sets the length of time to delay between starting each animation during one of the
+     * CHANGE animations.
+     *
+     * @param transitionType A value of {@link #CHANGE_APPEARING} or @link #CHANGE_DISAPPEARING}.
+     * @param duration The length of time, in milliseconds, to delay before launching the next
+     * animation in the sequence.
+     */
+    public void setStagger(int transitionType, long duration) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                mChangingAppearingStagger = duration;
+                break;
+            case CHANGE_DISAPPEARING:
+                mChangingDisappearingStagger = duration;
+                break;
+            // noop other cases
+        }
+    }
+
+    /**
+     * Tets the length of time to delay between starting each animation during one of the
+     * CHANGE animations.
+     *
+     * @param transitionType A value of {@link #CHANGE_APPEARING} or @link #CHANGE_DISAPPEARING}.
+     * @return long The length of time, in milliseconds, to delay before launching the next
+     * animation in the sequence.
+     */
+    public long getStagger(int transitionType) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                return mChangingAppearingStagger;
+            case CHANGE_DISAPPEARING:
+                return mChangingDisappearingStagger;
+        }
+        // shouldn't reach here
+        return 0;
+    }
+
+    /**
+     * Sets the interpolator on one of the animation objects used by this transition. The
+     * <code>transitionType</code> parameter determines the animation whose interpolator
+     * is being set.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+     * duration is being set.
+     * @param interpolator The interpolator that the specified animation should use.
+     * @see Animator#setInterpolator(TimeInterpolator)
+     */
+    public void setInterpolator(int transitionType, TimeInterpolator interpolator) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                mChangingAppearingInterpolator = interpolator;
+                break;
+            case CHANGE_DISAPPEARING:
+                mChangingDisappearingInterpolator = interpolator;
+                break;
+            case APPEARING:
+                mAppearingInterpolator = interpolator;
+                break;
+            case DISAPPEARING:
+                mDisappearingInterpolator = interpolator;
+                break;
+        }
+    }
+
+    /**
+     * Gets the interpolator on one of the animation objects used by this transition. The
+     * <code>transitionType</code> parameter determines the animation whose interpolator
+     * is returned.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+     * duration is being set.
+     * @return TimeInterpolator The interpolator that the specified animation uses.
+     * @see Animator#setInterpolator(TimeInterpolator)
+     */
+    public TimeInterpolator getInterpolator(int transitionType) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                return mChangingAppearingInterpolator;
+            case CHANGE_DISAPPEARING:
+                return mChangingDisappearingInterpolator;
+            case APPEARING:
+                return mAppearingInterpolator;
+            case DISAPPEARING:
+                return mDisappearingInterpolator;
+        }
+        // shouldn't reach here
+        return null;
+    }
+
+    /**
+     * Sets the animation used during one of the transition types that may run. Any
+     * Animator object can be used, but to be most useful in the context of layout
+     * transitions, the animation should either be a ObjectAnimator or a AnimatorSet
+     * of animations including PropertyAnimators. Also, these ObjectAnimator objects
+     * should be able to get and set values on their target objects automatically. For
+     * example, a ObjectAnimator that animates the property "left" is able to set and get the
+     * <code>left</code> property from the View objects being animated by the layout
+     * transition. The transition works by setting target objects and properties
+     * dynamically, according to the pre- and post-layoout values of those objects, so
+     * having animations that can handle those properties appropriately will work best
+     * for custom animation. The dynamic setting of values is only the case for the
+     * CHANGE animations; the APPEARING and DISAPPEARING animations are simply run with
+     * the values they have.
+     *
+     * <p>It is also worth noting that any and all animations (and their underlying
+     * PropertyValuesHolder objects) will have their start and end values set according
+     * to the pre- and post-layout values. So, for example, a custom animation on "alpha"
+     * as the CHANGE_APPEARING animation will inherit the real value of alpha on the target
+     * object (presumably 1) as its starting and ending value when the animation begins.
+     * Animations which need to use values at the beginning and end that may not match the
+     * values queried when the transition begins may need to use a different mechanism
+     * than a standard ObjectAnimator object.</p>
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+     * duration is being set.
+     * @param animator The animation being assigned. A value of <code>null</code> means that no
+     * animation will be run for the specified transitionType.
+     */
+    public void setAnimator(int transitionType, Animator animator) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                mChangingAppearingAnim = animator;
+                break;
+            case CHANGE_DISAPPEARING:
+                mChangingDisappearingAnim = animator;
+                break;
+            case APPEARING:
+                mAppearingAnim = animator;
+                break;
+            case DISAPPEARING:
+                mDisappearingAnim = animator;
+                break;
+        }
+    }
+
+    /**
+     * Gets the animation used during one of the transition types that may run.
+     *
+     * @param transitionType one of {@link #CHANGE_APPEARING}, {@link #CHANGE_DISAPPEARING},
+     * {@link #APPEARING}, or {@link #DISAPPEARING}, which determines the animation whose
+     * duration is being set.
+     * @return Animator The animation being used for the given transition type.
+     * @see #setAnimator(int, Animator)
+     */
+    public Animator getAnimator(int transitionType) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+                return mChangingAppearingAnim;
+            case CHANGE_DISAPPEARING:
+                return mChangingDisappearingAnim;
+            case APPEARING:
+                return mAppearingAnim;
+            case DISAPPEARING:
+                return mDisappearingAnim;
+        }
+        // shouldn't reach here
+        return null;
+    }
+
+    /**
+     * This function sets up runs animations on all of the views that change during layout.
+     * For every child in the parent, we create a change animation of the appropriate
+     * type (appearing or disappearing) and ask it to populate its start values from its
+     * target view. We add layout listeners to all child views and listen for changes. For
+     * those views that change, we populate the end values for those animations and start them.
+     * Animations are not run on unchanging views.
+     *
+     * @param parent The container which is undergoing an appearing or disappearing change.
+     * @param newView The view being added to or removed from the parent.
+     * @param changeReason A value of APPEARING or DISAPPEARING, indicating whether the
+     * transition is occuring because an item is being added to or removed from the parent.
+     */
+    private void runChangeTransition(final ViewGroup parent, View newView, final int changeReason) {
+
+        Animator baseAnimator = (changeReason == APPEARING) ?
+                mChangingAppearingAnim : mChangingDisappearingAnim;
+        // If the animation is null, there's nothing to do
+        if (baseAnimator == null) {
+            return;
+        }
+
+        // reset the inter-animation delay, in case we use it later
+        staggerDelay = 0;
+
+        final ViewTreeObserver observer = parent.getViewTreeObserver(); // used for later cleanup
+        if (!observer.isAlive()) {
+            // If the observer's not in a good state, skip the transition
+            return;
+        }
+        int numChildren = parent.getChildCount();
+
+        for (int i = 0; i < numChildren; ++i) {
+            final View child = parent.getChildAt(i);
+
+            // only animate the views not being added or removed
+            if (child != newView) {
+
+                // If there's an animation running on this view already, cancel it
+                Animator currentAnimation = currentAnimations.get(child);
+                if (currentAnimation != null) {
+                    currentAnimation.cancel();
+                    currentAnimations.remove(child);
+                }
+
+                // Make a copy of the appropriate animation
+                final Animator anim = baseAnimator.clone();
+
+                // Cache the animation in case we need to cancel it later
+                currentAnimations.put(child, anim);
+
+                // Set the target object for the animation
+                anim.setTarget(child);
+
+                // A ObjectAnimator (or AnimatorSet of them) can extract start values from
+                // its target object
+                anim.setupStartValues();
+
+                // Add a listener to track layout changes on this view. If we don't get a callback,
+                // then there's nothing to animate.
+                final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() {
+                    public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                            int oldLeft, int oldTop, int oldRight, int oldBottom) {
+
+                        // Tell the animation to extract end values from the changed object
+                        anim.setupEndValues();
+
+                        long startDelay;
+                        long duration;
+                        if (changeReason == APPEARING) {
+                            startDelay = mChangingAppearingDelay + staggerDelay;
+                            staggerDelay += mChangingAppearingStagger;
+                            duration = mChangingAppearingDuration;
+                        } else {
+                            startDelay = mChangingDisappearingDelay + staggerDelay;
+                            staggerDelay += mChangingDisappearingStagger;
+                            duration = mChangingDisappearingDuration;
+                        }
+                        anim.setStartDelay(startDelay);
+                        anim.setDuration(duration);
+
+                        if (anim instanceof ObjectAnimator) {
+                            ((ObjectAnimator) anim).setCurrentPlayTime(0);
+                        }
+                        anim.start();
+
+                        // this only removes listeners whose views changed - must clear the
+                        // other listeners later
+                        child.removeOnLayoutChangeListener(this);
+                        layoutChangeListenerMap.remove(child);
+                    }
+                };
+                // Remove the animation from the cache when it ends
+                anim.addListener(new AnimatorListenerAdapter() {
+                    private boolean canceled = false;
+                    public void onAnimationCancel(Animator animator) {
+                        // we remove canceled animations immediately, not here
+                        canceled = true;
+                        child.removeOnLayoutChangeListener(listener);
+                        layoutChangeListenerMap.remove(child);
+                    }
+                    public void onAnimationEnd(Animator animator) {
+                        if (!canceled) {
+                            currentAnimations.remove(child);
+                        }
+                    }
+                });
+
+                child.addOnLayoutChangeListener(listener);
+                // cache the listener for later removal
+                layoutChangeListenerMap.put(child, listener);
+            }
+        }
+        // This is the cleanup step. When we get this rendering event, we know that all of
+        // the appropriate animations have been set up and run. Now we can clear out the
+        // layout listeners.
+        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+            public boolean onPreDraw() {
+                parent.getViewTreeObserver().removeOnPreDrawListener(this);
+                int numChildren = parent.getChildCount();
+                for (int i = 0; i < numChildren; ++i) {
+                    final View child = parent.getChildAt(i);
+                    child.removeOnLayoutChangeListener(layoutChangeListenerMap.get(child));
+                }
+                layoutChangeListenerMap.clear();
+                return true;
+            }
+        });
+    }
+
+    /**
+     * This method runs the animation that makes an added item appear.
+     *
+     * @param parent The ViewGroup to which the View is being added.
+     * @param child The View being added to the ViewGroup.
+     */
+    private void runAppearingTransition(final ViewGroup parent, final View child) {
+        if (mAppearingAnim == null) {
+            if (mListeners != null) {
+                for (TransitionListener listener : mListeners) {
+                    listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
+                }
+            }
+            return;
+        }
+        Animator anim = mAppearingAnim.clone();
+        anim.setTarget(child);
+        anim.setStartDelay(mAppearingDelay);
+        anim.setDuration(mAppearingDuration);
+        if (anim instanceof ObjectAnimator) {
+            ((ObjectAnimator) anim).setCurrentPlayTime(0);
+        }
+        if (mListeners != null) {
+            anim.addListener(new AnimatorListenerAdapter() {
+                public void onAnimationEnd() {
+                    for (TransitionListener listener : mListeners) {
+                        listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
+                    }
+                }
+            });
+        }
+        anim.start();
+    }
+
+    /**
+     * This method runs the animation that makes a removed item disappear.
+     *
+     * @param parent The ViewGroup from which the View is being removed.
+     * @param child The View being removed from the ViewGroup.
+     */
+    private void runDisappearingTransition(final ViewGroup parent, final View child) {
+        if (mDisappearingAnim == null) {
+            if (mListeners != null) {
+                for (TransitionListener listener : mListeners) {
+                    listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING);
+                }
+            }
+            return;
+        }
+        Animator anim = mDisappearingAnim.clone();
+        anim.setStartDelay(mDisappearingDelay);
+        anim.setDuration(mDisappearingDuration);
+        anim.setTarget(child);
+        if (mListeners != null) {
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator anim) {
+                    for (TransitionListener listener : mListeners) {
+                        listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING);
+                    }
+                }
+            });
+        }
+        if (anim instanceof ObjectAnimator) {
+            ((ObjectAnimator) anim).setCurrentPlayTime(0);
+        }
+        anim.start();
+    }
+
+    /**
+     * This method is called by ViewGroup when a child view is about to be added to the
+     * container. This callback starts the process of a transition; we grab the starting
+     * values, listen for changes to all of the children of the container, and start appropriate
+     * animations.
+     *
+     * @param parent The ViewGroup to which the View is being added.
+     * @param child The View being added to the ViewGroup.
+     */
+    public void addChild(ViewGroup parent, View child) {
+        if (mListeners != null) {
+            for (TransitionListener listener : mListeners) {
+                listener.startTransition(this, parent, child, APPEARING);
+            }
+        }
+        runChangeTransition(parent, child, APPEARING);
+        runAppearingTransition(parent, child);
+    }
+
+    /**
+     * This method is called by ViewGroup when a child view is about to be added to the
+     * container. This callback starts the process of a transition; we grab the starting
+     * values, listen for changes to all of the children of the container, and start appropriate
+     * animations.
+     *
+     * @param parent The ViewGroup to which the View is being added.
+     * @param child The View being added to the ViewGroup.
+     */
+    public void showChild(ViewGroup parent, View child) {
+        addChild(parent, child);
+    }
+
+    /**
+     * This method is called by ViewGroup when a child view is about to be removed from the
+     * container. This callback starts the process of a transition; we grab the starting
+     * values, listen for changes to all of the children of the container, and start appropriate
+     * animations.
+     *
+     * @param parent The ViewGroup from which the View is being removed.
+     * @param child The View being removed from the ViewGroup.
+     */
+    public void removeChild(ViewGroup parent, View child) {
+        if (mListeners != null) {
+            for (TransitionListener listener : mListeners) {
+                listener.startTransition(this, parent, child, DISAPPEARING);
+            }
+        }
+        runChangeTransition(parent, child, DISAPPEARING);
+        runDisappearingTransition(parent, child);
+    }
+
+    /**
+     * This method is called by ViewGroup when a child view is about to be removed from the
+     * container. This callback starts the process of a transition; we grab the starting
+     * values, listen for changes to all of the children of the container, and start appropriate
+     * animations.
+     *
+     * @param parent The ViewGroup from which the View is being removed.
+     * @param child The View being removed from the ViewGroup.
+     */
+    public void hideChild(ViewGroup parent, View child) {
+        removeChild(parent, child);
+    }
+
+    /**
+     * Add a listener that will be called when the bounds of the view change due to
+     * layout processing.
+     *
+     * @param listener The listener that will be called when layout bounds change.
+     */
+    public void addTransitionListener(TransitionListener listener) {
+        if (mListeners == null) {
+            mListeners = new ArrayList<TransitionListener>();
+        }
+        mListeners.add(listener);
+    }
+
+    /**
+     * Remove a listener for layout changes.
+     *
+     * @param listener The listener for layout bounds change.
+     */
+    public void removeTransitionListener(TransitionListener listener) {
+        if (mListeners == null) {
+            return;
+        }
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Gets the current list of listeners for layout changes.
+     * @return
+     */
+    public List<TransitionListener> getTransitionListeners() {
+        return mListeners;
+    }
+
+    /**
+     * This interface is used for listening to starting and ending events for transitions.
+     */
+    public interface TransitionListener {
+
+        /**
+         * This event is sent to listeners when an APPEARING or DISAPPEARING transition
+         * begins.
+         *
+         * @param transition The LayoutTransition sending out the event.
+         * @param container The ViewGroup on which the transition is playing.
+         * @param view The View object being added or removed from its parent.
+         * @param transitionType The type of transition that is beginning, either
+         * {@link android.animation.LayoutTransition#APPEARING} or
+         * {@link android.animation.LayoutTransition#DISAPPEARING}.
+         */
+        public void startTransition(LayoutTransition transition, ViewGroup container,
+                View view, int transitionType);
+
+        /**
+         * This event is sent to listeners when an APPEARING or DISAPPEARING transition ends.
+         *
+         * @param transition The LayoutTransition sending out the event.
+         * @param container The ViewGroup on which the transition is playing.
+         * @param view The View object being added or removed from its parent.
+         * @param transitionType The type of transition that is ending, either
+         * {@link android.animation.LayoutTransition#APPEARING} or
+         * {@link android.animation.LayoutTransition#DISAPPEARING}.
+         */
+        public void endTransition(LayoutTransition transition, ViewGroup container,
+                View view, int transitionType);
+    }
+
+}
\ No newline at end of file
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
new file mode 100644
index 0000000..7c2e70d
--- /dev/null
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import android.util.Log;
+
+import java.lang.reflect.Method;
+
+/**
+ * This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
+ * The constructors of this class take parameters to define the target object that will be animated
+ * as well as the name of the property that will be animated. Appropriate set/get functions
+ * are then determined internally and the animation will call these functions as necessary to
+ * animate the property.
+ *
+ * @see #setPropertyName(String)
+ *
+ */
+public final class ObjectAnimator extends ValueAnimator {
+
+    // The target object on which the property exists, set in the constructor
+    private Object mTarget;
+
+    private String mPropertyName;
+
+    /**
+     * Sets the name of the property that will be animated. This name is used to derive
+     * a setter function that will be called to set animated values.
+     * For example, a property name of <code>foo</code> will result
+     * in a call to the function <code>setFoo()</code> on the target object. If either
+     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
+     * also be derived and called.
+     *
+     * <p>For best performance of the mechanism that calls the setter function determined by the
+     * name of the property being animated, use <code>float</code> or <code>int</code> typed values,
+     * and make the setter function for those properties have a <code>void</code> return value. This
+     * will cause the code to take an optimized path for these constrained circumstances. Other
+     * property types and return types will work, but will have more overhead in processing
+     * the requests due to normal reflection mechanisms.</p>
+     *
+     * <p>Note that the setter function derived from this property name
+     * must take the same parameter type as the
+     * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
+     * the setter function will fail.</p>
+     *
+     * <p>If this ObjectAnimator has been set up to animate several properties together,
+     * using more than one PropertyValuesHolder objects, then setting the propertyName simply
+     * sets the propertyName in the first of those PropertyValuesHolder objects.</p>
+     *
+     * @param propertyName The name of the property being animated.
+     */
+    public void setPropertyName(String propertyName) {
+        // mValues could be null if this is being constructed piecemeal. Just record the
+        // propertyName to be used later when setValues() is called if so.
+        if (mValues != null) {
+            PropertyValuesHolder valuesHolder = mValues[0];
+            String oldName = valuesHolder.getPropertyName();
+            valuesHolder.setPropertyName(propertyName);
+            mValuesMap.remove(oldName);
+            mValuesMap.put(propertyName, valuesHolder);
+        }
+        mPropertyName = propertyName;
+        // New property/values/target should cause re-initialization prior to starting
+        mInitialized = false;
+    }
+
+    /**
+     * Gets the name of the property that will be animated. This name will be used to derive
+     * a setter function that will be called to set animated values.
+     * For example, a property name of <code>foo</code> will result
+     * in a call to the function <code>setFoo()</code> on the target object. If either
+     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
+     * also be derived and called.
+     */
+    public String getPropertyName() {
+        return mPropertyName;
+    }
+
+    /**
+     * Determine the setter or getter function using the JavaBeans convention of setFoo or
+     * getFoo for a property named 'foo'. This function figures out what the name of the
+     * function should be and uses reflection to find the Method with that name on the
+     * target object.
+     *
+     * @param prefix "set" or "get", depending on whether we need a setter or getter.
+     * @return Method the method associated with mPropertyName.
+     */
+    private Method getPropertyFunction(String prefix, Class valueType) {
+        // TODO: faster implementation...
+        Method returnVal = null;
+        String firstLetter = mPropertyName.substring(0, 1);
+        String theRest = mPropertyName.substring(1);
+        firstLetter = firstLetter.toUpperCase();
+        String setterName = prefix + firstLetter + theRest;
+        Class args[] = null;
+        if (valueType != null) {
+            args = new Class[1];
+            args[0] = valueType;
+        }
+        try {
+            returnVal = mTarget.getClass().getMethod(setterName, args);
+        } catch (NoSuchMethodException e) {
+            Log.e("ObjectAnimator",
+                    "Couldn't find setter/getter for property " + mPropertyName + ": " + e);
+        }
+        return returnVal;
+    }
+
+    /**
+     * Creates a new ObjectAnimator object. This default constructor is primarily for
+     * use internally; the other constructors which take parameters are more generally
+     * useful.
+     */
+    public ObjectAnimator() {
+    }
+
+    /**
+     * A constructor that takes a single property name and set of values. This constructor is
+     * used in the simple case of animating a single property.
+     *
+     * @param target The object whose property is to be animated. This object should
+     * have a public method on it called <code>setName()</code>, where <code>name</code> is
+     * the value of the <code>propertyName</code> parameter.
+     * @param propertyName The name of the property being animated.
+     */
+    private ObjectAnimator(Object target, String propertyName) {
+        mTarget = target;
+        setPropertyName(propertyName);
+    }
+
+    /**
+     * Constructs and returns an ObjectAnimator that animates between int values. A single
+     * value implies that that value is the one being animated to. However, this is not typically
+     * useful in a ValueAnimator object because there is no way for the object to determine the
+     * starting value for the animation (unlike ObjectAnimator, which can derive that value
+     * from the target object and property being animated). Therefore, there should typically
+     * be two or more values.
+     *
+     * @param target The object whose property is to be animated. This object should
+     * have a public method on it called <code>setName()</code>, where <code>name</code> is
+     * the value of the <code>propertyName</code> parameter.
+     * @param propertyName The name of the property being animated.
+     * @param values A set of values that the animation will animate between over time.
+     * @return A ValueAnimator object that is set up to animate between the given values.
+     */
+    public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
+        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
+        anim.setIntValues(values);
+        return anim;
+    }
+
+    /**
+     * Constructs and returns an ObjectAnimator that animates between float values. A single
+     * value implies that that value is the one being animated to. However, this is not typically
+     * useful in a ValueAnimator object because there is no way for the object to determine the
+     * starting value for the animation (unlike ObjectAnimator, which can derive that value
+     * from the target object and property being animated). Therefore, there should typically
+     * be two or more values.
+     *
+     * @param target The object whose property is to be animated. This object should
+     * have a public method on it called <code>setName()</code>, where <code>name</code> is
+     * the value of the <code>propertyName</code> parameter.
+     * @param propertyName The name of the property being animated.
+     * @param values A set of values that the animation will animate between over time.
+     * @return A ValueAnimator object that is set up to animate between the given values.
+     */
+    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
+        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
+        anim.setFloatValues(values);
+        return anim;
+    }
+
+    /**
+     * A constructor that takes <code>PropertyValueHolder</code> values. This constructor should
+     * be used when animating several properties at once with the same ObjectAnimator, since
+     * PropertyValuesHolder allows you to associate a set of animation values with a property
+     * name.
+     *
+     * @param target The object whose property is to be animated. This object should
+     * have public methods on it called <code>setName()</code>, where <code>name</code> is
+     * the name of the property passed in as the <code>propertyName</code> parameter for
+     * each of the PropertyValuesHolder objects.
+     * @param propertyName The name of the property being animated.
+     * @param evaluator A TypeEvaluator that will be called on each animation frame to
+     * provide the ncessry interpolation between the Object values to derive the animated
+     * value.
+     * @param values The PropertyValuesHolder objects which hold each the property name and values
+     * to animate that property between.
+     */
+    public static ObjectAnimator ofObject(Object target, String propertyName,
+            TypeEvaluator evaluator, Object... values) {
+        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
+        anim.setObjectValues(values);
+        anim.setEvaluator(evaluator);
+        return anim;
+    }
+
+    /**
+     * Constructs and returns an ObjectAnimator that animates between the sets of values
+     * specifed in <code>PropertyValueHolder</code> objects. This variant should
+     * be used when animating several properties at once with the same ObjectAnimator, since
+     * PropertyValuesHolder allows you to associate a set of animation values with a property
+     * name.
+     *
+     * @param target The object whose property is to be animated. This object should
+     * have public methods on it called <code>setName()</code>, where <code>name</code> is
+     * the name of the property passed in as the <code>propertyName</code> parameter for
+     * each of the PropertyValuesHolder objects.
+     * @param values A set of PropertyValuesHolder objects whose values will be animated
+     * between over time.
+     * @return A ValueAnimator object that is set up to animate between the given values.
+     */
+    public static ObjectAnimator ofPropertyValuesHolder(Object target,
+            PropertyValuesHolder... values) {
+        ObjectAnimator anim = new ObjectAnimator();
+        anim.mTarget = target;
+        anim.setValues(values);
+        return anim;
+    }
+
+    @Override
+    public void setIntValues(int... values) {
+        if (mValues == null || mValues.length == 0) {
+            // No values yet - this animator is being constructed piecemeal. Init the values with
+            // whatever the current propertyName is
+            setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
+        } else {
+            super.setIntValues(values);
+        }
+    }
+
+    @Override
+    public void setFloatValues(float... values) {
+        if (mValues == null || mValues.length == 0) {
+            // No values yet - this animator is being constructed piecemeal. Init the values with
+            // whatever the current propertyName is
+            setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
+        } else {
+            super.setFloatValues(values);
+        }
+    }
+
+    @Override
+    public void setObjectValues(Object... values) {
+        if (mValues == null || mValues.length == 0) {
+            // No values yet - this animator is being constructed piecemeal. Init the values with
+            // whatever the current propertyName is
+            setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values));
+        } else {
+            super.setObjectValues(values);
+        }
+    }
+
+    /**
+     * This function is called immediately before processing the first animation
+     * frame of an animation. If there is a nonzero <code>startDelay</code>, the
+     * function is called after that delay ends.
+     * It takes care of the final initialization steps for the
+     * animation. This includes setting mEvaluator, if the user has not yet
+     * set it up, and the setter/getter methods, if the user did not supply
+     * them.
+     *
+     *  <p>Overriders of this method should call the superclass method to cause
+     *  internal mechanisms to be set up correctly.</p>
+     */
+    @Override
+    void initAnimation() {
+        if (!mInitialized) {
+            // mValueType may change due to setter/getter setup; do this before calling super.init(),
+            // which uses mValueType to set up the default type evaluator.
+            int numValues = mValues.length;
+            for (int i = 0; i < numValues; ++i) {
+                mValues[i].setupSetterAndGetter(mTarget);
+            }
+            super.initAnimation();
+        }
+    }
+
+    /**
+     * Sets the length of the animation. The default duration is 300 milliseconds.
+     *
+     * @param duration The length of the animation, in milliseconds.
+     * @return ObjectAnimator The object called with setDuration(). This return
+     * value makes it easier to compose statements together that construct and then set the
+     * duration, as in
+     * <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>.
+     */
+    @Override
+    public ObjectAnimator setDuration(long duration) {
+        super.setDuration(duration);
+        return this;
+    }
+
+
+    /**
+     * The target object whose property will be animated by this animation
+     *
+     * @return The object being animated
+     */
+    public Object getTarget() {
+        return mTarget;
+    }
+
+    /**
+     * Sets the target object whose property will be animated by this animation
+     *
+     * @param target The object being animated
+     */
+    @Override
+    public void setTarget(Object target) {
+        if (mTarget != target) {
+            mTarget = target;
+            if (mTarget  != null && target != null && mTarget.getClass() == target.getClass()) {
+                return;
+            }
+            // New target type should cause re-initialization prior to starting
+            mInitialized = false;
+        }
+    }
+
+    @Override
+    public void setupStartValues() {
+        initAnimation();
+        int numValues = mValues.length;
+        for (int i = 0; i < numValues; ++i) {
+            mValues[i].setupStartValue(mTarget);
+        }
+    }
+
+    @Override
+    public void setupEndValues() {
+        initAnimation();
+        int numValues = mValues.length;
+        for (int i = 0; i < numValues; ++i) {
+            mValues[i].setupEndValue(mTarget);
+        }
+    }
+
+    /**
+     * This method is called with the elapsed fraction of the animation during every
+     * animation frame. This function turns the elapsed fraction into an interpolated fraction
+     * and then into an animated value (from the evaluator. The function is called mostly during
+     * animation updates, but it is also called when the <code>end()</code>
+     * function is called, to set the final value on the property.
+     *
+     * <p>Overrides of this method must call the superclass to perform the calculation
+     * of the animated value.</p>
+     *
+     * @param fraction The elapsed fraction of the animation.
+     */
+    @Override
+    void animateValue(float fraction) {
+        super.animateValue(fraction);
+        int numValues = mValues.length;
+        for (int i = 0; i < numValues; ++i) {
+            mValues[i].setAnimatedValue(mTarget);
+        }
+    }
+
+    @Override
+    public ObjectAnimator clone() {
+        final ObjectAnimator anim = (ObjectAnimator) super.clone();
+        return anim;
+    }
+}
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
new file mode 100644
index 0000000..97aa5a1
--- /dev/null
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -0,0 +1,880 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * This class holds information about a property and the values that that property
+ * should take on during an animation. PropertyValuesHolder objects can be used to create
+ * animations with ValueAnimator or ObjectAnimator that operate on several different properties
+ * in parallel.
+ */
+public class PropertyValuesHolder implements Cloneable {
+
+    /**
+     * The name of the property associated with the values. This need not be a real property,
+     * unless this object is being used with ObjectAnimator. But this is the name by which
+     * aniamted values are looked up with getAnimatedValue(String) in ValueAnimator.
+     */
+    String mPropertyName;
+
+    /**
+     * The setter function, if needed. ObjectAnimator hands off this functionality to
+     * PropertyValuesHolder, since it holds all of the per-property information. This
+     * property can be manually set via setSetter(). Otherwise, it is automatically
+     * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
+     */
+    Method mSetter = null;
+
+    /**
+     * The getter function, if needed. ObjectAnimator hands off this functionality to
+     * PropertyValuesHolder, since it holds all of the per-property information. This
+     * property can be manually set via setSetter(). Otherwise, it is automatically
+     * derived when the animation starts in setupSetterAndGetter() if using ObjectAnimator.
+     * The getter is only derived and used if one of the values is null.
+     */
+    private Method mGetter = null;
+
+    /**
+     * The type of values supplied. This information is used both in deriving the setter/getter
+     * functions and in deriving the type of TypeEvaluator.
+     */
+    Class mValueType;
+
+    /**
+     * The set of keyframes (time/value pairs) that define this animation.
+     */
+    KeyframeSet mKeyframeSet = null;
+
+
+    // type evaluators for the three primitive types handled by this implementation
+    private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
+    private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
+    private static final TypeEvaluator sDoubleEvaluator = new DoubleEvaluator();
+
+    // We try several different types when searching for appropriate setter/getter functions.
+    // The caller may have supplied values in a type that does not match the setter/getter
+    // functions (such as the integers 0 and 1 to represent floating point values for alpha).
+    // Also, the use of generics in constructors means that we end up with the Object versions
+    // of primitive types (Float vs. float). But most likely, the setter/getter functions
+    // will take primitive types instead.
+    // So we supply an ordered array of other types to try before giving up.
+    private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
+            Double.class, Integer.class};
+    private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
+            Float.class, Double.class};
+    private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class,
+            Float.class, Integer.class};
+
+    // These maps hold all property entries for a particular class. This map
+    // is used to speed up property/setter/getter lookups for a given class/property
+    // combination. No need to use reflection on the combination more than once.
+    private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =
+            new HashMap<Class, HashMap<String, Method>>();
+    private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =
+            new HashMap<Class, HashMap<String, Method>>();
+
+    // This lock is used to ensure that only one thread is accessing the property maps
+    // at a time.
+    final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock();
+
+    // Used to pass single value to varargs parameter in setter invocation
+    final Object[] mTmpValueArray = new Object[1];
+
+    /**
+     * The type evaluator used to calculate the animated values. This evaluator is determined
+     * automatically based on the type of the start/end objects passed into the constructor,
+     * but the system only knows about the primitive types int, double, and float. Any other
+     * type will need to set the evaluator to a custom evaluator for that type.
+     */
+    private TypeEvaluator mEvaluator;
+
+    /**
+     * The value most recently calculated by calculateValue(). This is set during
+     * that function and might be retrieved later either by ValueAnimator.animatedValue() or
+     * by the property-setting logic in ObjectAnimator.animatedValue().
+     */
+    private Object mAnimatedValue;
+
+    /**
+     * Internal utility constructor, used by the factory methods to set the property name.
+     * @param propertyName The name of the property for this holder.
+     */
+    private PropertyValuesHolder(String propertyName) {
+        mPropertyName = propertyName;
+    }
+
+    /**
+     * Constructs and returns a PropertyValuesHolder with a given property name and
+     * set of int values.
+     * @param propertyName The name of the property being animated.
+     * @param values The values that the named property will animate between.
+     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+     */
+    public static PropertyValuesHolder ofInt(String propertyName, int... values) {
+        PropertyValuesHolder pvh = new IntPropertyValuesHolder(propertyName, values);
+        return pvh;
+    }
+
+    /**
+     * Constructs and returns a PropertyValuesHolder with a given property name and
+     * set of float values.
+     * @param propertyName The name of the property being animated.
+     * @param values The values that the named property will animate between.
+     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+     */
+    public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
+        PropertyValuesHolder pvh = new FloatPropertyValuesHolder(propertyName, values);
+        return pvh;
+    }
+
+    /**
+     * Constructs and returns a PropertyValuesHolder with a given property name and
+     * set of Object values. This variant also takes a TypeEvaluator because the system
+     * cannot interpolate between objects of unknown type.
+     *
+     * @param propertyName The name of the property being animated.
+     * @param evaluator A TypeEvaluator that will be called on each animation frame to
+     * provide the ncessry interpolation between the Object values to derive the animated
+     * value.
+     * @param values The values that the named property will animate between.
+     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+     */
+    public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
+            Object... values) {
+        PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
+        pvh.setObjectValues(values);
+        pvh.setEvaluator(evaluator);
+        return pvh;
+    }
+
+    /**
+     * Constructs and returns a PropertyValuesHolder object with the specified property name and set
+     * of values. These values can be of any type, but the type should be consistent so that
+     * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
+     * the common type.
+     * <p>If there is only one value, it is assumed to be the end value of an animation,
+     * and an initial value will be derived, if possible, by calling a getter function
+     * on the object. Also, if any value is null, the value will be filled in when the animation
+     * starts in the same way. This mechanism of automatically getting null values only works
+     * if the PropertyValuesHolder object is used in conjunction
+     * {@link ObjectAnimator}, and with a getter function either
+     * derived automatically from <code>propertyName</code> or set explicitly via
+     * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
+     * no way of determining what the value should be.
+     * @param propertyName The name of the property associated with this set of values. This
+     * can be the actual property name to be used when using a ObjectAnimator object, or
+     * just a name used to get animated values, such as if this object is used with an
+     * ValueAnimator object.
+     * @param values The set of values to animate between.
+     */
+    public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
+        KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
+        if (keyframeSet instanceof IntKeyframeSet) {
+            return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet);
+        } else if (keyframeSet instanceof FloatKeyframeSet) {
+            return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet);
+        }
+        else {
+            PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
+            pvh.mKeyframeSet = keyframeSet;
+            pvh.mValueType = ((Keyframe)values[0]).getType();
+            return pvh;
+        }
+    }
+
+    /**
+     * Set the animated values for this object to this set of ints.
+     * If there is only one value, it is assumed to be the end value of an animation,
+     * and an initial value will be derived, if possible, by calling a getter function
+     * on the object. Also, if any value is null, the value will be filled in when the animation
+     * starts in the same way. This mechanism of automatically getting null values only works
+     * if the PropertyValuesHolder object is used in conjunction
+     * {@link ObjectAnimator}, and with a getter function either
+     * derived automatically from <code>propertyName</code> or set explicitly via
+     * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
+     * no way of determining what the value should be.
+     *
+     * @param values One or more values that the animation will animate between.
+     */
+    public void setIntValues(int... values) {
+        mValueType = int.class;
+        mKeyframeSet = KeyframeSet.ofInt(values);
+    }
+
+    /**
+     * Set the animated values for this object to this set of floats.
+     * If there is only one value, it is assumed to be the end value of an animation,
+     * and an initial value will be derived, if possible, by calling a getter function
+     * on the object. Also, if any value is null, the value will be filled in when the animation
+     * starts in the same way. This mechanism of automatically getting null values only works
+     * if the PropertyValuesHolder object is used in conjunction
+     * {@link ObjectAnimator}, and with a getter function either
+     * derived automatically from <code>propertyName</code> or set explicitly via
+     * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
+     * no way of determining what the value should be.
+     *
+     * @param values One or more values that the animation will animate between.
+     */
+    public void setFloatValues(float... values) {
+        mValueType = float.class;
+        mKeyframeSet = KeyframeSet.ofFloat(values);
+    }
+
+    /**
+     * Set the animated values for this object to this set of Keyframes.
+     *
+     * @param values One or more values that the animation will animate between.
+     */
+    public void setKeyframes(Keyframe... values) {
+        int numKeyframes = values.length;
+        Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
+        mValueType = ((Keyframe)values[0]).getType();
+        for (int i = 0; i < numKeyframes; ++i) {
+            keyframes[i] = (Keyframe)values[i];
+        }
+        mKeyframeSet = new KeyframeSet(keyframes);
+    }
+
+    /**
+     * Set the animated values for this object to this set of Objects.
+     * If there is only one value, it is assumed to be the end value of an animation,
+     * and an initial value will be derived, if possible, by calling a getter function
+     * on the object. Also, if any value is null, the value will be filled in when the animation
+     * starts in the same way. This mechanism of automatically getting null values only works
+     * if the PropertyValuesHolder object is used in conjunction
+     * {@link ObjectAnimator}, and with a getter function either
+     * derived automatically from <code>propertyName</code> or set explicitly via
+     * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
+     * no way of determining what the value should be.
+     * 
+     * @param values One or more values that the animation will animate between.
+     */
+    public void setObjectValues(Object... values) {
+        mValueType = values[0].getClass();
+        mKeyframeSet = KeyframeSet.ofObject(values);
+    }
+
+    /**
+     * Determine the setter or getter function using the JavaBeans convention of setFoo or
+     * getFoo for a property named 'foo'. This function figures out what the name of the
+     * function should be and uses reflection to find the Method with that name on the
+     * target object.
+     *
+     * @param targetClass The class to search for the method
+     * @param prefix "set" or "get", depending on whether we need a setter or getter.
+     * @param valueType The type of the parameter (in the case of a setter). This type
+     * is derived from the values set on this PropertyValuesHolder. This type is used as
+     * a first guess at the parameter type, but we check for methods with several different
+     * types to avoid problems with slight mis-matches between supplied values and actual
+     * value types used on the setter.
+     * @return Method the method associated with mPropertyName.
+     */
+    private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
+        // TODO: faster implementation...
+        Method returnVal = null;
+        String methodName = getMethodName(prefix, mPropertyName);
+        Class args[] = null;
+        if (valueType == null) {
+            try {
+                returnVal = targetClass.getMethod(methodName, args);
+            } catch (NoSuchMethodException e) {
+                Log.e("PropertyValuesHolder",
+                        "Couldn't find no-arg method for property " + mPropertyName + ": " + e);
+            }
+        } else {
+            args = new Class[1];
+            Class typeVariants[];
+            if (mValueType.equals(Float.class)) {
+                typeVariants = FLOAT_VARIANTS;
+            } else if (mValueType.equals(Integer.class)) {
+                typeVariants = INTEGER_VARIANTS;
+            } else if (mValueType.equals(Double.class)) {
+                typeVariants = DOUBLE_VARIANTS;
+            } else {
+                typeVariants = new Class[1];
+                typeVariants[0] = mValueType;
+            }
+            for (Class typeVariant : typeVariants) {
+                args[0] = typeVariant;
+                try {
+                    returnVal = targetClass.getMethod(methodName, args);
+                    // change the value type to suit
+                    mValueType = typeVariant;
+                    return returnVal;
+                } catch (NoSuchMethodException e) {
+                    // Swallow the error and keep trying other variants
+                }
+            }
+            // If we got here, then no appropriate function was found
+            Log.e("PropertyValuesHolder",
+                    "Couldn't find setter/getter for property " + mPropertyName +
+                            "with value type "+ mValueType);
+        }
+
+        return returnVal;
+    }
+
+
+    /**
+     * Returns the setter or getter requested. This utility function checks whether the
+     * requested method exists in the propertyMapMap cache. If not, it calls another
+     * utility function to request the Method from the targetClass directly.
+     * @param targetClass The Class on which the requested method should exist.
+     * @param propertyMapMap The cache of setters/getters derived so far.
+     * @param prefix "set" or "get", for the setter or getter.
+     * @param valueType The type of parameter passed into the method (null for getter).
+     * @return Method the method associated with mPropertyName.
+     */
+    private Method setupSetterOrGetter(Class targetClass,
+            HashMap<Class, HashMap<String, Method>> propertyMapMap,
+            String prefix, Class valueType) {
+        Method setterOrGetter = null;
+        try {
+            // Have to lock property map prior to reading it, to guard against
+            // another thread putting something in there after we've checked it
+            // but before we've added an entry to it
+            // TODO: can we store the setter/getter per Class instead of per Object?
+            mPropertyMapLock.writeLock().lock();
+            HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
+            if (propertyMap != null) {
+                setterOrGetter = propertyMap.get(mPropertyName);
+            }
+            if (setterOrGetter == null) {
+                setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
+                if (propertyMap == null) {
+                    propertyMap = new HashMap<String, Method>();
+                    propertyMapMap.put(targetClass, propertyMap);
+                }
+                propertyMap.put(mPropertyName, setterOrGetter);
+            }
+        } finally {
+            mPropertyMapLock.writeLock().unlock();
+        }
+        return setterOrGetter;
+    }
+
+    /**
+     * Utility function to get the setter from targetClass
+     * @param targetClass The Class on which the requested method should exist.
+     */
+    void setupSetter(Class targetClass) {
+        mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
+    }
+
+    /**
+     * Utility function to get the getter from targetClass
+     */
+    private void setupGetter(Class targetClass) {
+        mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
+    }
+
+    /**
+     * Internal function (called from ObjectAnimator) to set up the setter and getter
+     * prior to running the animation. If the setter has not been manually set for this
+     * object, it will be derived automatically given the property name, target object, and
+     * types of values supplied. If no getter has been set, it will be supplied iff any of the
+     * supplied values was null. If there is a null value, then the getter (supplied or derived)
+     * will be called to set those null values to the current value of the property
+     * on the target object.
+     * @param target The object on which the setter (and possibly getter) exist.
+     */
+    void setupSetterAndGetter(Object target) {
+        Class targetClass = target.getClass();
+        if (mSetter == null) {
+            setupSetter(targetClass);
+        }
+        for (Keyframe kf : mKeyframeSet.mKeyframes) {
+            if (!kf.hasValue()) {
+                if (mGetter == null) {
+                    setupGetter(targetClass);
+                }
+                try {
+                    kf.setValue(mGetter.invoke(target));
+                } catch (InvocationTargetException e) {
+                    Log.e("PropertyValuesHolder", e.toString());
+                } catch (IllegalAccessException e) {
+                    Log.e("PropertyValuesHolder", e.toString());
+                }
+            }
+        }
+    }
+
+    /**
+     * Utility function to set the value stored in a particular Keyframe. The value used is
+     * whatever the value is for the property name specified in the keyframe on the target object.
+     *
+     * @param target The target object from which the current value should be extracted.
+     * @param kf The keyframe which holds the property name and value.
+     */
+    private void setupValue(Object target, Keyframe kf) {
+        try {
+            if (mGetter == null) {
+                Class targetClass = target.getClass();
+                setupGetter(targetClass);
+            }
+            kf.setValue(mGetter.invoke(target));
+        } catch (InvocationTargetException e) {
+            Log.e("PropertyValuesHolder", e.toString());
+        } catch (IllegalAccessException e) {
+            Log.e("PropertyValuesHolder", e.toString());
+        }
+    }
+
+    /**
+     * This function is called by ObjectAnimator when setting the start values for an animation.
+     * The start values are set according to the current values in the target object. The
+     * property whose value is extracted is whatever is specified by the propertyName of this
+     * PropertyValuesHolder object.
+     *
+     * @param target The object which holds the start values that should be set.
+     */
+    void setupStartValue(Object target) {
+        setupValue(target, mKeyframeSet.mKeyframes.get(0));
+    }
+
+    /**
+     * This function is called by ObjectAnimator when setting the end values for an animation.
+     * The end values are set according to the current values in the target object. The
+     * property whose value is extracted is whatever is specified by the propertyName of this
+     * PropertyValuesHolder object.
+     *
+     * @param target The object which holds the start values that should be set.
+     */
+    void setupEndValue(Object target) {
+        setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
+    }
+
+    @Override
+    public PropertyValuesHolder clone() {
+        try {
+            PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
+            newPVH.mPropertyName = mPropertyName;
+            newPVH.mKeyframeSet = mKeyframeSet.clone();
+            newPVH.mEvaluator = mEvaluator;
+            return newPVH;
+        } catch (CloneNotSupportedException e) {
+            // won't reach here
+            return null;
+        }
+    }
+
+    /**
+     * Internal function to set the value on the target object, using the setter set up
+     * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
+     * to handle turning the value calculated by ValueAnimator into a value set on the object
+     * according to the name of the property.
+     * @param target The target object on which the value is set
+     */
+    void setAnimatedValue(Object target) {
+        if (mSetter != null) {
+            try {
+                mTmpValueArray[0] = getAnimatedValue();
+                mSetter.invoke(target, mTmpValueArray);
+            } catch (InvocationTargetException e) {
+                Log.e("PropertyValuesHolder", e.toString());
+            } catch (IllegalAccessException e) {
+                Log.e("PropertyValuesHolder", e.toString());
+            }
+        }
+    }
+
+    /**
+     * Internal function, called by ValueAnimator, to set up the TypeEvaluator that will be used
+     * to calculate animated values.
+     */
+    void init() {
+        if (mEvaluator == null) {
+            // We already handle int, float, long, double automatically, but not their Object
+            // equivalents
+            mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
+                    (mValueType == Float.class) ? sFloatEvaluator :
+                    null;
+        }
+        if (mEvaluator != null) {
+            // KeyframeSet knows how to evaluate the common types - only give it a custom
+            // evaulator if one has been set on this class
+            mKeyframeSet.setEvaluator(mEvaluator);
+        }
+    }
+
+    /**
+     * The TypeEvaluator will the automatically determined based on the type of values
+     * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
+     * desired. This may be important in cases where either the type of the values supplied
+     * do not match the way that they should be interpolated between, or if the values
+     * are of a custom type or one not currently understood by the animation system. Currently,
+     * only values of type float, double, and int (and their Object equivalents, Float, Double,
+     * and Integer) are  correctly interpolated; all other types require setting a TypeEvaluator.
+     * @param evaluator
+     */
+    public void setEvaluator(TypeEvaluator evaluator) {
+        mEvaluator = evaluator;
+        mKeyframeSet.setEvaluator(evaluator);
+    }
+
+    /**
+     * Function used to calculate the value according to the evaluator set up for
+     * this PropertyValuesHolder object. This function is called by ValueAnimator.animateValue().
+     *
+     * @param fraction The elapsed, interpolated fraction of the animation.
+     */
+    void calculateValue(float fraction) {
+        mAnimatedValue = mKeyframeSet.getValue(fraction);
+    }
+
+    /**
+     * Sets the <code>Method</code> that is called with the animated values calculated
+     * during the animation. Setting the setter method is an alternative to supplying a
+     * {@link #setPropertyName(String) propertyName} from which the method is derived. This
+     * approach is more direct, and is especially useful when a function must be called that does
+     * not correspond to the convention of <code>setName()</code>. For example, if a function
+     * called <code>offset()</code> is to be called with the animated values, there is no way
+     * to tell <code>ObjectAnimator</code> how to call that function simply through a property
+     * name, so a setter method should be supplied instead.
+     *
+     * <p>Note that the setter function must take the same parameter type as the
+     * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
+     * the setter function will fail.</p>
+     *
+     * @param setter The setter method that should be called with the animated values.
+     */
+    public void setSetter(Method setter) {
+        mSetter = setter;
+    }
+
+    /**
+     * Gets the <code>Method</code> that is called with the animated values calculated
+     * during the animation.
+     */
+    public Method getSetter() {
+        return mSetter;
+    }
+
+    /**
+     * Sets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or
+     * <code>valueTo</code> properties. Setting the getter method is an alternative to supplying a
+     * {@link #setPropertyName(String) propertyName} from which the method is derived. This
+     * approach is more direct, and is especially useful when a function must be called that does
+     * not correspond to the convention of <code>setName()</code>. For example, if a function
+     * called <code>offset()</code> is to be called to get an initial value, there is no way
+     * to tell <code>ObjectAnimator</code> how to call that function simply through a property
+     * name, so a getter method should be supplied instead.
+     *
+     * <p>Note that the getter method is only called whether supplied here or derived
+     * from the property name, if one of <code>valueFrom</code> or <code>valueTo</code> are
+     * null. If both of those values are non-null, then there is no need to get one of the
+     * values and the getter is not called.
+     *
+     * <p>Note that the getter function must return the same parameter type as the
+     * <code>valueFrom</code> and <code>valueTo</code> properties (whichever of them are
+     * non-null), otherwise the call to the getter function will fail.</p>
+     *
+     * @param getter The getter method that should be called to get initial animation values.
+     */
+    public void setGetter(Method getter) {
+        mGetter = getter;
+    }
+
+    /**
+     * Gets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or
+     * <code>valueTo</code> properties.
+     */
+    public Method getGetter() {
+        return mGetter;
+    }
+
+    /**
+     * Sets the name of the property that will be animated. This name is used to derive
+     * a setter function that will be called to set animated values.
+     * For example, a property name of <code>foo</code> will result
+     * in a call to the function <code>setFoo()</code> on the target object. If either
+     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
+     * also be derived and called.
+     *
+     * <p>Note that the setter function derived from this property name
+     * must take the same parameter type as the
+     * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
+     * the setter function will fail.</p>
+     *
+     * @param propertyName The name of the property being animated.
+     */
+    public void setPropertyName(String propertyName) {
+        mPropertyName = propertyName;
+    }
+
+    /**
+     * Gets the name of the property that will be animated. This name will be used to derive
+     * a setter function that will be called to set animated values.
+     * For example, a property name of <code>foo</code> will result
+     * in a call to the function <code>setFoo()</code> on the target object. If either
+     * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
+     * also be derived and called.
+     */
+    public String getPropertyName() {
+        return mPropertyName;
+    }
+
+    /**
+     * Internal function, called by ValueAnimator and ObjectAnimator, to retrieve the value
+     * most recently calculated in calculateValue().
+     * @return
+     */
+    Object getAnimatedValue() {
+        return mAnimatedValue;
+    }
+
+    /**
+     * Utility method to derive a setter/getter method name from a property name, where the
+     * prefix is typically "set" or "get" and the first letter of the property name is
+     * capitalized.
+     *
+     * @param prefix The precursor to the method name, before the property name begins, typically
+     * "set" or "get".
+     * @param propertyName The name of the property that represents the bulk of the method name
+     * after the prefix. The first letter of this word will be capitalized in the resulting
+     * method name.
+     * @return String the property name converted to a method name according to the conventions
+     * specified above.
+     */
+    static String getMethodName(String prefix, String propertyName) {
+        char firstLetter = propertyName.charAt(0);
+        String theRest = propertyName.substring(1);
+        firstLetter = Character.toUpperCase(firstLetter);
+        return prefix + firstLetter + theRest;
+    }
+
+    static class IntPropertyValuesHolder extends PropertyValuesHolder {
+
+        private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
+                new HashMap<Class, HashMap<String, Integer>>();
+        int mJniSetter;
+
+        IntKeyframeSet mIntKeyframeSet;
+        int mIntAnimatedValue;
+
+        public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
+            super(propertyName);
+            mValueType = int.class;
+            mKeyframeSet = keyframeSet;
+            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+        }
+
+        public IntPropertyValuesHolder(String propertyName, int... values) {
+            super(propertyName);
+            setIntValues(values);
+        }
+
+        @Override
+        public void setIntValues(int... values) {
+            super.setIntValues(values);
+            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+        }
+
+        @Override
+        void calculateValue(float fraction) {
+            mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
+        }
+
+        @Override
+        Object getAnimatedValue() {
+            return mIntAnimatedValue;
+        }
+
+        @Override
+        public IntPropertyValuesHolder clone() {
+            IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
+            newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
+            return newPVH;
+        }
+
+        /**
+         * Internal function to set the value on the target object, using the setter set up
+         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
+         * to handle turning the value calculated by ValueAnimator into a value set on the object
+         * according to the name of the property.
+         * @param target The target object on which the value is set
+         */
+        @Override
+        void setAnimatedValue(Object target) {
+            if (mJniSetter != 0) {
+                nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
+                return;
+            }
+            if (mSetter != null) {
+                try {
+                    mTmpValueArray[0] = mIntAnimatedValue;
+                    mSetter.invoke(target, mTmpValueArray);
+                } catch (InvocationTargetException e) {
+                    Log.e("PropertyValuesHolder", e.toString());
+                } catch (IllegalAccessException e) {
+                    Log.e("PropertyValuesHolder", e.toString());
+                }
+            }
+        }
+
+        @Override
+        void setupSetter(Class targetClass) {
+            // Check new static hashmap<propName, int> for setter method
+            try {
+                mPropertyMapLock.writeLock().lock();
+                HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
+                if (propertyMap != null) {
+                    Integer mJniSetterInteger = propertyMap.get(mPropertyName);
+                    if (mJniSetterInteger != null) {
+                        mJniSetter = mJniSetterInteger;
+                    }
+                }
+                if (mJniSetter == 0) {
+                    String methodName = getMethodName("set", mPropertyName);
+                    mJniSetter = nGetIntMethod(targetClass, methodName);
+                    if (mJniSetter != 0) {
+                        if (propertyMap == null) {
+                            propertyMap = new HashMap<String, Integer>();
+                            sJNISetterPropertyMap.put(targetClass, propertyMap);
+                        }
+                        propertyMap.put(mPropertyName, mJniSetter);
+                    }
+                }
+            } catch (NoSuchMethodError e) {
+                // System.out.println("Can't find native method using JNI, use reflection" + e);
+            } finally {
+                mPropertyMapLock.writeLock().unlock();
+            }
+            if (mJniSetter == 0) {
+                // Couldn't find method through fast JNI approach - just use reflection
+                super.setupSetter(targetClass);
+            }
+        }
+    }
+
+    static class FloatPropertyValuesHolder extends PropertyValuesHolder {
+
+        private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
+                new HashMap<Class, HashMap<String, Integer>>();
+        int mJniSetter;
+
+        FloatKeyframeSet mFloatKeyframeSet;
+        float mFloatAnimatedValue;
+
+        public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
+            super(propertyName);
+            mValueType = float.class;
+            mKeyframeSet = keyframeSet;
+            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+        }
+
+        public FloatPropertyValuesHolder(String propertyName, float... values) {
+            super(propertyName);
+            setFloatValues(values);
+        }
+
+        @Override
+        public void setFloatValues(float... values) {
+            super.setFloatValues(values);
+            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+        }
+
+        @Override
+        void calculateValue(float fraction) {
+            mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
+        }
+
+        @Override
+        Object getAnimatedValue() {
+            return mFloatAnimatedValue;
+        }
+
+        @Override
+        public FloatPropertyValuesHolder clone() {
+            FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
+            newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
+            return newPVH;
+        }
+
+        /**
+         * Internal function to set the value on the target object, using the setter set up
+         * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
+         * to handle turning the value calculated by ValueAnimator into a value set on the object
+         * according to the name of the property.
+         * @param target The target object on which the value is set
+         */
+        @Override
+        void setAnimatedValue(Object target) {
+            if (mJniSetter != 0) {
+                nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
+                return;
+            }
+            if (mSetter != null) {
+                try {
+                    mTmpValueArray[0] = mFloatAnimatedValue;
+                    mSetter.invoke(target, mTmpValueArray);
+                } catch (InvocationTargetException e) {
+                    Log.e("PropertyValuesHolder", e.toString());
+                } catch (IllegalAccessException e) {
+                    Log.e("PropertyValuesHolder", e.toString());
+                }
+            }
+        }
+
+        @Override
+        void setupSetter(Class targetClass) {
+            // Check new static hashmap<propName, int> for setter method
+            try {
+                mPropertyMapLock.writeLock().lock();
+                HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass);
+                if (propertyMap != null) {
+                    Integer mJniSetterInteger = propertyMap.get(mPropertyName);
+                    if (mJniSetterInteger != null) {
+                        mJniSetter = mJniSetterInteger;
+                    }
+                }
+                if (mJniSetter == 0) {
+                    String methodName = getMethodName("set", mPropertyName);
+                    mJniSetter = nGetFloatMethod(targetClass, methodName);
+                    if (mJniSetter != 0) {
+                        if (propertyMap == null) {
+                            propertyMap = new HashMap<String, Integer>();
+                            sJNISetterPropertyMap.put(targetClass, propertyMap);
+                        }
+                        propertyMap.put(mPropertyName, mJniSetter);
+                    }
+                }
+            } catch (NoSuchMethodError e) {
+                // System.out.println("Can't find native method using JNI, use reflection" + e);
+            } finally {
+                mPropertyMapLock.writeLock().unlock();
+            }
+            if (mJniSetter == 0) {
+                // Couldn't find method through fast JNI approach - just use reflection
+                super.setupSetter(targetClass);
+            }
+        }
+
+    }
+
+    native static private int nGetIntMethod(Class targetClass, String methodName);
+    native static private int nGetFloatMethod(Class targetClass, String methodName);
+    native static private void nCallIntMethod(Object target, int methodID, int arg);
+    native static private void nCallFloatMethod(Object target, int methodID, float arg);
+}
\ No newline at end of file
diff --git a/core/java/android/animation/RGBEvaluator.java b/core/java/android/animation/RGBEvaluator.java
new file mode 100644
index 0000000..bae0af0
--- /dev/null
+++ b/core/java/android/animation/RGBEvaluator.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+/**
+ * This evaluator can be used to perform type interpolation between integer
+ * values that represent ARGB colors.
+ */
+public class RGBEvaluator implements TypeEvaluator {
+
+    /**
+     * This function returns the calculated in-between value for a color
+     * given integers that represent the start and end values in the four
+     * bytes of the 32-bit int. Each channel is separately linearly interpolated
+     * and the resulting calculated values are recombined into the return value.
+     *
+     * @param fraction The fraction from the starting to the ending values
+     * @param startValue A 32-bit int value representing colors in the
+     * separate bytes of the parameter
+     * @param endValue A 32-bit int value representing colors in the
+     * separate bytes of the parameter
+     * @return A value that is calculated to be the linearly interpolated
+     * result, derived by separating the start and end values into separate
+     * color channels and interpolating each one separately, recombining the
+     * resulting values in the same way.
+     */
+    public Object evaluate(float fraction, Object startValue, Object endValue) {
+        int startInt = (Integer) startValue;
+        int startA = (startInt >> 24);
+        int startR = (startInt >> 16) & 0xff;
+        int startG = (startInt >> 8) & 0xff;
+        int startB = startInt & 0xff;
+
+        int endInt = (Integer) endValue;
+        int endA = (endInt >> 24);
+        int endR = (endInt >> 16) & 0xff;
+        int endG = (endInt >> 8) & 0xff;
+        int endB = endInt & 0xff;
+
+        return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
+                (int)((startR + (int)(fraction * (endR - startR))) << 16) |
+                (int)((startG + (int)(fraction * (endG - startG))) << 8) |
+                (int)((startB + (int)(fraction * (endB - startB))));
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/animation/TimeInterpolator.java b/core/java/android/animation/TimeInterpolator.java
new file mode 100644
index 0000000..0f5d8bf
--- /dev/null
+++ b/core/java/android/animation/TimeInterpolator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+/**
+ * A time interpolator defines the rate of change of an animation. This allows animations
+ * to have non-linear motion, such as acceleration and deceleration.
+ */
+public interface TimeInterpolator {
+
+    /**
+     * Maps a value representing the elapsed fraction of an animation to a value that represents
+     * the interpolated fraction. This interpolated value is then multiplied by the change in
+     * value of an animation to derive the animated value at the current elapsed animation time.
+     *
+     * @param input A value between 0 and 1.0 indicating our current point
+     *        in the animation where 0 represents the start and 1.0 represents
+     *        the end
+     * @return The interpolation value. This value can be more than 1.0 for
+     *         interpolators which overshoot their targets, or less than 0 for
+     *         interpolators that undershoot their targets.
+     */
+    float getInterpolation(float input);
+}
diff --git a/core/java/android/animation/TypeEvaluator.java b/core/java/android/animation/TypeEvaluator.java
new file mode 100644
index 0000000..fa49175
--- /dev/null
+++ b/core/java/android/animation/TypeEvaluator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+/**
+ * Interface for use with the {@link ValueAnimator#setEvaluator(TypeEvaluator)} function. Evaluators
+ * allow developers to create animations on arbitrary property types, by allowing them to supply
+ * custom evaulators for types that are not automatically understood and used by the animation
+ * system.
+ *
+ * @see ValueAnimator#setEvaluator(TypeEvaluator)
+ */
+public interface TypeEvaluator {
+
+    /**
+     * This function returns the result of linearly interpolating the start and end values, with
+     * <code>fraction</code> representing the proportion between the start and end values. The
+     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
+     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
+     * and <code>t</code> is <code>fraction</code>.
+     *
+     * @param fraction   The fraction from the starting to the ending values
+     * @param startValue The start value.
+     * @param endValue   The end value.
+     * @return A linear interpolation between the start and end values, given the
+     *         <code>fraction</code> parameter.
+     */
+    public Object evaluate(float fraction, Object startValue, Object endValue);
+
+}
\ No newline at end of file
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
new file mode 100755
index 0000000..b021e75
--- /dev/null
+++ b/core/java/android/animation/ValueAnimator.java
@@ -0,0 +1,1189 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.animation;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AnimationUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class provides a simple timing engine for running animations
+ * which calculate animated values and set them on target objects.
+ *
+ * <p>There is a single timing pulse that all animations use. It runs in a
+ * custom handler to ensure that property changes happen on the UI thread.</p>
+ *
+ * <p>By default, ValueAnimator uses non-linear time interpolation, via the
+ * {@link AccelerateDecelerateInterpolator} class, which accelerates into and decelerates
+ * out of an animation. This behavior can be changed by calling
+ * {@link ValueAnimator#setInterpolator(TimeInterpolator)}.</p>
+ */
+public class ValueAnimator extends Animator {
+
+    /**
+     * Internal constants
+     */
+
+    /*
+     * The default amount of time in ms between animation frames
+     */
+    private static final long DEFAULT_FRAME_DELAY = 10;
+
+    /**
+     * Messages sent to timing handler: START is sent when an animation first begins, FRAME is sent
+     * by the handler to itself to process the next animation frame
+     */
+    private static final int ANIMATION_START = 0;
+    private static final int ANIMATION_FRAME = 1;
+
+    /**
+     * Values used with internal variable mPlayingState to indicate the current state of an
+     * animation.
+     */
+    private static final int STOPPED    = 0; // Not yet playing
+    private static final int RUNNING    = 1; // Playing normally
+    private static final int CANCELED   = 2; // cancel() called - need to end it
+    private static final int ENDED      = 3; // end() called - need to end it
+    private static final int SEEKED     = 4; // Seeked to some time value
+
+    /**
+     * Internal variables
+     * NOTE: This object implements the clone() method, making a deep copy of any referenced
+     * objects. As other non-trivial fields are added to this class, make sure to add logic
+     * to clone() to make deep copies of them.
+     */
+
+    // The first time that the animation's animateFrame() method is called. This time is used to
+    // determine elapsed time (and therefore the elapsed fraction) in subsequent calls
+    // to animateFrame()
+    private long mStartTime;
+
+    /**
+     * Set when setCurrentPlayTime() is called. If negative, animation is not currently seeked
+     * to a value.
+     */
+    private long mSeekTime = -1;
+
+    // TODO: We access the following ThreadLocal variables often, some of them on every update.
+    // If ThreadLocal access is significantly expensive, we may want to put all of these
+    // fields into a structure sot hat we just access ThreadLocal once to get the reference
+    // to that structure, then access the structure directly for each field.
+
+    // The static sAnimationHandler processes the internal timing loop on which all animations
+    // are based
+    private static ThreadLocal<AnimationHandler> sAnimationHandler =
+            new ThreadLocal<AnimationHandler>();
+
+    // The per-thread list of all active animations
+    private static final ThreadLocal<ArrayList<ValueAnimator>> sAnimations =
+            new ThreadLocal<ArrayList<ValueAnimator>>() {
+                @Override
+                protected ArrayList<ValueAnimator> initialValue() {
+                    return new ArrayList<ValueAnimator>();
+                }
+            };
+
+    // The per-thread set of animations to be started on the next animation frame
+    private static final ThreadLocal<ArrayList<ValueAnimator>> sPendingAnimations =
+            new ThreadLocal<ArrayList<ValueAnimator>>() {
+                @Override
+                protected ArrayList<ValueAnimator> initialValue() {
+                    return new ArrayList<ValueAnimator>();
+                }
+            };
+
+    /**
+     * Internal per-thread collections used to avoid set collisions as animations start and end
+     * while being processed.
+     */
+    private static final ThreadLocal<ArrayList<ValueAnimator>> sDelayedAnims =
+            new ThreadLocal<ArrayList<ValueAnimator>>() {
+                @Override
+                protected ArrayList<ValueAnimator> initialValue() {
+                    return new ArrayList<ValueAnimator>();
+                }
+            };
+
+    private static final ThreadLocal<ArrayList<ValueAnimator>> sEndingAnims =
+            new ThreadLocal<ArrayList<ValueAnimator>>() {
+                @Override
+                protected ArrayList<ValueAnimator> initialValue() {
+                    return new ArrayList<ValueAnimator>();
+                }
+            };
+
+    private static final ThreadLocal<ArrayList<ValueAnimator>> sReadyAnims =
+            new ThreadLocal<ArrayList<ValueAnimator>>() {
+                @Override
+                protected ArrayList<ValueAnimator> initialValue() {
+                    return new ArrayList<ValueAnimator>();
+                }
+            };
+
+    // The time interpolator to be used if none is set on the animation
+    private static final TimeInterpolator sDefaultInterpolator =
+            new AccelerateDecelerateInterpolator();
+
+    // type evaluators for the three primitive types handled by this implementation
+    private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
+    private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
+    private static final TypeEvaluator sDoubleEvaluator = new DoubleEvaluator();
+
+    /**
+     * Used to indicate whether the animation is currently playing in reverse. This causes the
+     * elapsed fraction to be inverted to calculate the appropriate values.
+     */
+    private boolean mPlayingBackwards = false;
+
+    /**
+     * This variable tracks the current iteration that is playing. When mCurrentIteration exceeds the
+     * repeatCount (if repeatCount!=INFINITE), the animation ends
+     */
+    private int mCurrentIteration = 0;
+
+    /**
+     * Tracks whether a startDelay'd animation has begun playing through the startDelay.
+     */
+    private boolean mStartedDelay = false;
+
+    /**
+     * Tracks the time at which the animation began playing through its startDelay. This is
+     * different from the mStartTime variable, which is used to track when the animation became
+     * active (which is when the startDelay expired and the animation was added to the active
+     * animations list).
+     */
+    private long mDelayStartTime;
+
+    /**
+     * Flag that represents the current state of the animation. Used to figure out when to start
+     * an animation (if state == STOPPED). Also used to end an animation that
+     * has been cancel()'d or end()'d since the last animation frame. Possible values are
+     * STOPPED, RUNNING, ENDED, CANCELED.
+     */
+    private int mPlayingState = STOPPED;
+
+    /**
+     * Flag that denotes whether the animation is set up and ready to go. Used to
+     * set up animation that has not yet been started.
+     */
+    boolean mInitialized = false;
+
+    //
+    // Backing variables
+    //
+
+    // How long the animation should last in ms
+    private long mDuration = 300;
+
+    // The amount of time in ms to delay starting the animation after start() is called
+    private long mStartDelay = 0;
+
+    // The number of milliseconds between animation frames
+    private static long sFrameDelay = DEFAULT_FRAME_DELAY;
+
+    // The number of times the animation will repeat. The default is 0, which means the animation
+    // will play only once
+    private int mRepeatCount = 0;
+
+    /**
+     * The type of repetition that will occur when repeatMode is nonzero. RESTART means the
+     * animation will start from the beginning on every new cycle. REVERSE means the animation
+     * will reverse directions on each iteration.
+     */
+    private int mRepeatMode = RESTART;
+
+    /**
+     * The time interpolator to be used. The elapsed fraction of the animation will be passed
+     * through this interpolator to calculate the interpolated fraction, which is then used to
+     * calculate the animated values.
+     */
+    private TimeInterpolator mInterpolator = sDefaultInterpolator;
+
+    /**
+     * The set of listeners to be sent events through the life of an animation.
+     */
+    private ArrayList<AnimatorUpdateListener> mUpdateListeners = null;
+
+    /**
+     * The property/value sets being animated.
+     */
+    PropertyValuesHolder[] mValues;
+
+    /**
+     * A hashmap of the PropertyValuesHolder objects. This map is used to lookup animated values
+     * by property name during calls to getAnimatedValue(String).
+     */
+    HashMap<String, PropertyValuesHolder> mValuesMap;
+
+    /**
+     * Public constants
+     */
+
+    /**
+     * When the animation reaches the end and <code>repeatCount</code> is INFINITE
+     * or a positive value, the animation restarts from the beginning.
+     */
+    public static final int RESTART = 1;
+    /**
+     * When the animation reaches the end and <code>repeatCount</code> is INFINITE
+     * or a positive value, the animation reverses direction on every iteration.
+     */
+    public static final int REVERSE = 2;
+    /**
+     * This value used used with the {@link #setRepeatCount(int)} property to repeat
+     * the animation indefinitely.
+     */
+    public static final int INFINITE = -1;
+
+    /**
+     * Creates a new ValueAnimator object. This default constructor is primarily for
+     * use internally; the factory methods which take parameters are more generally
+     * useful.
+     */
+    public ValueAnimator() {
+    }
+
+    /**
+     * Constructs and returns a ValueAnimator that animates between int values. A single
+     * value implies that that value is the one being animated to. However, this is not typically
+     * useful in a ValueAnimator object because there is no way for the object to determine the
+     * starting value for the animation (unlike ObjectAnimator, which can derive that value
+     * from the target object and property being animated). Therefore, there should typically
+     * be two or more values.
+     *
+     * @param values A set of values that the animation will animate between over time.
+     * @return A ValueAnimator object that is set up to animate between the given values.
+     */
+    public static ValueAnimator ofInt(int... values) {
+        ValueAnimator anim = new ValueAnimator();
+        anim.setIntValues(values);
+        return anim;
+    }
+
+    /**
+     * Constructs and returns a ValueAnimator that animates between float values. A single
+     * value implies that that value is the one being animated to. However, this is not typically
+     * useful in a ValueAnimator object because there is no way for the object to determine the
+     * starting value for the animation (unlike ObjectAnimator, which can derive that value
+     * from the target object and property being animated). Therefore, there should typically
+     * be two or more values.
+     *
+     * @param values A set of values that the animation will animate between over time.
+     * @return A ValueAnimator object that is set up to animate between the given values.
+     */
+    public static ValueAnimator ofFloat(float... values) {
+        ValueAnimator anim = new ValueAnimator();
+        anim.setFloatValues(values);
+        return anim;
+    }
+
+    /**
+     * Constructs and returns a ValueAnimator that animates between the values
+     * specified in the PropertyValuesHolder objects.
+     *
+     * @param values A set of PropertyValuesHolder objects whose values will be animated
+     * between over time.
+     * @return A ValueAnimator object that is set up to animate between the given values.
+     */
+    public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) {
+        ValueAnimator anim = new ValueAnimator();
+        anim.setValues(values);
+        return anim;
+    }
+    /**
+     * Constructs and returns a ValueAnimator that animates between Object values. A single
+     * value implies that that value is the one being animated to. However, this is not typically
+     * useful in a ValueAnimator object because there is no way for the object to determine the
+     * starting value for the animation (unlike ObjectAnimator, which can derive that value
+     * from the target object and property being animated). Therefore, there should typically
+     * be two or more values.
+     *
+     * <p>Since ValueAnimator does not know how to animate between arbitrary Objects, this
+     * factory method also takes a TypeEvaluator object that the ValueAnimator will use
+     * to perform that interpolation.
+     *
+     * @param evaluator A TypeEvaluator that will be called on each animation frame to
+     * provide the ncessry interpolation between the Object values to derive the animated
+     * value.
+     * @param values A set of values that the animation will animate between over time.
+     * @return A ValueAnimator object that is set up to animate between the given values.
+     */
+    public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
+        ValueAnimator anim = new ValueAnimator();
+        anim.setObjectValues(values);
+        anim.setEvaluator(evaluator);
+        return anim;
+    }
+
+    /**
+     * Sets int values that will be animated between. A single
+     * value implies that that value is the one being animated to. However, this is not typically
+     * useful in a ValueAnimator object because there is no way for the object to determine the
+     * starting value for the animation (unlike ObjectAnimator, which can derive that value
+     * from the target object and property being animated). Therefore, there should typically
+     * be two or more values.
+     *
+     * <p>If there are already multiple sets of values defined for this ValueAnimator via more
+     * than one PropertyValuesHolder object, this method will set the values for the first
+     * of those objects.</p>
+     *
+     * @param values A set of values that the animation will animate between over time.
+     */
+    public void setIntValues(int... values) {
+        if (values == null || values.length == 0) {
+            return;
+        }
+        if (mValues == null || mValues.length == 0) {
+            setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofInt("", values)});
+        } else {
+            PropertyValuesHolder valuesHolder = mValues[0];
+            valuesHolder.setIntValues(values);
+        }
+        // New property/values/target should cause re-initialization prior to starting
+        mInitialized = false;
+    }
+
+    /**
+     * Sets float values that will be animated between. A single
+     * value implies that that value is the one being animated to. However, this is not typically
+     * useful in a ValueAnimator object because there is no way for the object to determine the
+     * starting value for the animation (unlike ObjectAnimator, which can derive that value
+     * from the target object and property being animated). Therefore, there should typically
+     * be two or more values.
+     *
+     * <p>If there are already multiple sets of values defined for this ValueAnimator via more
+     * than one PropertyValuesHolder object, this method will set the values for the first
+     * of those objects.</p>
+     *
+     * @param values A set of values that the animation will animate between over time.
+     */
+    public void setFloatValues(float... values) {
+        if (values == null || values.length == 0) {
+            return;
+        }
+        if (mValues == null || mValues.length == 0) {
+            setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofFloat("", values)});
+        } else {
+            PropertyValuesHolder valuesHolder = mValues[0];
+            valuesHolder.setFloatValues(values);
+        }
+        // New property/values/target should cause re-initialization prior to starting
+        mInitialized = false;
+    }
+
+    /**
+     * Sets the values to animate between for this animation. A single
+     * value implies that that value is the one being animated to. However, this is not typically
+     * useful in a ValueAnimator object because there is no way for the object to determine the
+     * starting value for the animation (unlike ObjectAnimator, which can derive that value
+     * from the target object and property being animated). Therefore, there should typically
+     * be two or more values.
+     *
+     * <p>If there are already multiple sets of values defined for this ValueAnimator via more
+     * than one PropertyValuesHolder object, this method will set the values for the first
+     * of those objects.</p>
+     *
+     * <p>There should be a TypeEvaluator set on the ValueAnimator that knows how to interpolate
+     * between these value objects. ValueAnimator only knows how to interpolate between the
+     * primitive types specified in the other setValues() methods.</p>
+     *
+     * @param values The set of values to animate between.
+     */
+    public void setObjectValues(Object... values) {
+        if (values == null || values.length == 0) {
+            return;
+        }
+        if (mValues == null || mValues.length == 0) {
+            setValues(new PropertyValuesHolder[]{PropertyValuesHolder.ofObject("",
+                    (TypeEvaluator)null, values)});
+        } else {
+            PropertyValuesHolder valuesHolder = mValues[0];
+            valuesHolder.setObjectValues(values);
+        }
+        // New property/values/target should cause re-initialization prior to starting
+        mInitialized = false;
+    }
+
+    /**
+     * Sets the values, per property, being animated between. This function is called internally
+     * by the constructors of ValueAnimator that take a list of values. But an ValueAnimator can
+     * be constructed without values and this method can be called to set the values manually
+     * instead.
+     *
+     * @param values The set of values, per property, being animated between.
+     */
+    public void setValues(PropertyValuesHolder... values) {
+        int numValues = values.length;
+        mValues = values;
+        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
+        for (int i = 0; i < numValues; ++i) {
+            PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i];
+            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
+        }
+        // New property/values/target should cause re-initialization prior to starting
+        mInitialized = false;
+    }
+
+    /**
+     * Returns the values that this ValueAnimator animates between. These values are stored in
+     * PropertyValuesHolder objects, even if the ValueAnimator was created with a simple list
+     * of value objects instead.
+     *
+     * @return PropertyValuesHolder[] An array of PropertyValuesHolder objects which hold the
+     * values, per property, that define the animation.
+     */
+    public PropertyValuesHolder[] getValues() {
+        return mValues;
+    }
+
+    /**
+     * This function is called immediately before processing the first animation
+     * frame of an animation. If there is a nonzero <code>startDelay</code>, the
+     * function is called after that delay ends.
+     * It takes care of the final initialization steps for the
+     * animation.
+     *
+     *  <p>Overrides of this method should call the superclass method to ensure
+     *  that internal mechanisms for the animation are set up correctly.</p>
+     */
+    void initAnimation() {
+        if (!mInitialized) {
+            int numValues = mValues.length;
+            for (int i = 0; i < numValues; ++i) {
+                mValues[i].init();
+            }
+            mInitialized = true;
+        }
+    }
+
+
+    /**
+     * Sets the length of the animation. The default duration is 300 milliseconds.
+     *
+     * @param duration The length of the animation, in milliseconds.
+     * @return ValueAnimator The object called with setDuration(). This return
+     * value makes it easier to compose statements together that construct and then set the
+     * duration, as in <code>ValueAnimator.ofInt(0, 10).setDuration(500).start()</code>.
+     */
+    public ValueAnimator setDuration(long duration) {
+        mDuration = duration;
+        return this;
+    }
+
+    /**
+     * Gets the length of the animation. The default duration is 300 milliseconds.
+     *
+     * @return The length of the animation, in milliseconds.
+     */
+    public long getDuration() {
+        return mDuration;
+    }
+
+    /**
+     * Sets the position of the animation to the specified point in time. This time should
+     * be between 0 and the total duration of the animation, including any repetition. If
+     * the animation has not yet been started, then it will not advance forward after it is
+     * set to this time; it will simply set the time to this value and perform any appropriate
+     * actions based on that time. If the animation is already running, then setCurrentPlayTime()
+     * will set the current playing time to this value and continue playing from that point.
+     *
+     * @param playTime The time, in milliseconds, to which the animation is advanced or rewound.
+     */
+    public void setCurrentPlayTime(long playTime) {
+        initAnimation();
+        long currentTime = AnimationUtils.currentAnimationTimeMillis();
+        if (mPlayingState != RUNNING) {
+            mSeekTime = playTime;
+            mPlayingState = SEEKED;
+        }
+        mStartTime = currentTime - playTime;
+        animationFrame(currentTime);
+    }
+
+    /**
+     * Gets the current position of the animation in time, which is equal to the current
+     * time minus the time that the animation started. An animation that is not yet started will
+     * return a value of zero.
+     *
+     * @return The current position in time of the animation.
+     */
+    public long getCurrentPlayTime() {
+        if (!mInitialized || mPlayingState == STOPPED) {
+            return 0;
+        }
+        return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
+    }
+
+    /**
+     * This custom, static handler handles the timing pulse that is shared by
+     * all active animations. This approach ensures that the setting of animation
+     * values will happen on the UI thread and that all animations will share
+     * the same times for calculating their values, which makes synchronizing
+     * animations possible.
+     *
+     */
+    private static class AnimationHandler extends Handler {
+        /**
+         * There are only two messages that we care about: ANIMATION_START and
+         * ANIMATION_FRAME. The START message is sent when an animation's start()
+         * method is called. It cannot start synchronously when start() is called
+         * because the call may be on the wrong thread, and it would also not be
+         * synchronized with other animations because it would not start on a common
+         * timing pulse. So each animation sends a START message to the handler, which
+         * causes the handler to place the animation on the active animations queue and
+         * start processing frames for that animation.
+         * The FRAME message is the one that is sent over and over while there are any
+         * active animations to process.
+         */
+        @Override
+        public void handleMessage(Message msg) {
+            boolean callAgain = true;
+            ArrayList<ValueAnimator> animations = sAnimations.get();
+            ArrayList<ValueAnimator> delayedAnims = sDelayedAnims.get();
+            switch (msg.what) {
+                // TODO: should we avoid sending frame message when starting if we
+                // were already running?
+                case ANIMATION_START:
+                    ArrayList<ValueAnimator> pendingAnimations = sPendingAnimations.get();
+                    if (animations.size() > 0 || delayedAnims.size() > 0) {
+                        callAgain = false;
+                    }
+                    // pendingAnims holds any animations that have requested to be started
+                    // We're going to clear sPendingAnimations, but starting animation may
+                    // cause more to be added to the pending list (for example, if one animation
+                    // starting triggers another starting). So we loop until sPendingAnimations
+                    // is empty.
+                    while (pendingAnimations.size() > 0) {
+                        ArrayList<ValueAnimator> pendingCopy =
+                                (ArrayList<ValueAnimator>) pendingAnimations.clone();
+                        pendingAnimations.clear();
+                        int count = pendingCopy.size();
+                        for (int i = 0; i < count; ++i) {
+                            ValueAnimator anim = pendingCopy.get(i);
+                            // If the animation has a startDelay, place it on the delayed list
+                            if (anim.mStartDelay == 0 || anim.mPlayingState == ENDED ||
+                                    anim.mPlayingState == CANCELED) {
+                                anim.startAnimation();
+                            } else {
+                                delayedAnims.add(anim);
+                            }
+                        }
+                    }
+                    // fall through to process first frame of new animations
+                case ANIMATION_FRAME:
+                    // currentTime holds the common time for all animations processed
+                    // during this frame
+                    long currentTime = AnimationUtils.currentAnimationTimeMillis();
+                    ArrayList<ValueAnimator> readyAnims = sReadyAnims.get();
+                    ArrayList<ValueAnimator> endingAnims = sEndingAnims.get();
+
+                    // First, process animations currently sitting on the delayed queue, adding
+                    // them to the active animations if they are ready
+                    int numDelayedAnims = delayedAnims.size();
+                    for (int i = 0; i < numDelayedAnims; ++i) {
+                        ValueAnimator anim = delayedAnims.get(i);
+                        if (anim.delayedAnimationFrame(currentTime)) {
+                            readyAnims.add(anim);
+                        }
+                    }
+                    int numReadyAnims = readyAnims.size();
+                    if (numReadyAnims > 0) {
+                        for (int i = 0; i < numReadyAnims; ++i) {
+                            ValueAnimator anim = readyAnims.get(i);
+                            anim.startAnimation();
+                            delayedAnims.remove(anim);
+                        }
+                        readyAnims.clear();
+                    }
+
+                    // Now process all active animations. The return value from animationFrame()
+                    // tells the handler whether it should now be ended
+                    int numAnims = animations.size();
+                    for (int i = 0; i < numAnims; ++i) {
+                        ValueAnimator anim = animations.get(i);
+                        if (anim.animationFrame(currentTime)) {
+                            endingAnims.add(anim);
+                        }
+                    }
+                    if (endingAnims.size() > 0) {
+                        for (int i = 0; i < endingAnims.size(); ++i) {
+                            endingAnims.get(i).endAnimation();
+                        }
+                        endingAnims.clear();
+                    }
+
+                    // If there are still active or delayed animations, call the handler again
+                    // after the frameDelay
+                    if (callAgain && (!animations.isEmpty() || !delayedAnims.isEmpty())) {
+                        sendEmptyMessageDelayed(ANIMATION_FRAME, Math.max(0, sFrameDelay -
+                            (AnimationUtils.currentAnimationTimeMillis() - currentTime)));
+                    }
+                    break;
+            }
+        }
+    }
+
+    /**
+     * The amount of time, in milliseconds, to delay starting the animation after
+     * {@link #start()} is called.
+     *
+     * @return the number of milliseconds to delay running the animation
+     */
+    public long getStartDelay() {
+        return mStartDelay;
+    }
+
+    /**
+     * The amount of time, in milliseconds, to delay starting the animation after
+     * {@link #start()} is called.
+
+     * @param startDelay The amount of the delay, in milliseconds
+     */
+    public void setStartDelay(long startDelay) {
+        this.mStartDelay = startDelay;
+    }
+
+    /**
+     * The amount of time, in milliseconds, between each frame of the animation. This is a
+     * requested time that the animation will attempt to honor, but the actual delay between
+     * frames may be different, depending on system load and capabilities. This is a static
+     * function because the same delay will be applied to all animations, since they are all
+     * run off of a single timing loop.
+     *
+     * @return the requested time between frames, in milliseconds
+     */
+    public static long getFrameDelay() {
+        return sFrameDelay;
+    }
+
+    /**
+     * The amount of time, in milliseconds, between each frame of the animation. This is a
+     * requested time that the animation will attempt to honor, but the actual delay between
+     * frames may be different, depending on system load and capabilities. This is a static
+     * function because the same delay will be applied to all animations, since they are all
+     * run off of a single timing loop.
+     *
+     * @param frameDelay the requested time between frames, in milliseconds
+     */
+    public static void setFrameDelay(long frameDelay) {
+        sFrameDelay = frameDelay;
+    }
+
+    /**
+     * The most recent value calculated by this <code>ValueAnimator</code> when there is just one
+     * property being animated. This value is only sensible while the animation is running. The main
+     * purpose for this read-only property is to retrieve the value from the <code>ValueAnimator</code>
+     * during a call to {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
+     * is called during each animation frame, immediately after the value is calculated.
+     *
+     * @return animatedValue The value most recently calculated by this <code>ValueAnimator</code> for
+     * the single property being animated. If there are several properties being animated
+     * (specified by several PropertyValuesHolder objects in the constructor), this function
+     * returns the animated value for the first of those objects.
+     */
+    public Object getAnimatedValue() {
+        if (mValues != null && mValues.length > 0) {
+            return mValues[0].getAnimatedValue();
+        }
+        // Shouldn't get here; should always have values unless ValueAnimator was set up wrong
+        return null;
+    }
+
+    /**
+     * The most recent value calculated by this <code>ValueAnimator</code> for <code>propertyName</code>.
+     * The main purpose for this read-only property is to retrieve the value from the
+     * <code>ValueAnimator</code> during a call to
+     * {@link AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)}, which
+     * is called during each animation frame, immediately after the value is calculated.
+     *
+     * @return animatedValue The value most recently calculated for the named property
+     * by this <code>ValueAnimator</code>.
+     */
+    public Object getAnimatedValue(String propertyName) {
+        PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName);
+        if (valuesHolder != null) {
+            return valuesHolder.getAnimatedValue();
+        } else {
+            // At least avoid crashing if called with bogus propertyName
+            return null;
+        }
+    }
+
+    /**
+     * Sets how many times the animation should be repeated. If the repeat
+     * count is 0, the animation is never repeated. If the repeat count is
+     * greater than 0 or {@link #INFINITE}, the repeat mode will be taken
+     * into account. The repeat count is 0 by default.
+     *
+     * @param value the number of times the animation should be repeated
+     */
+    public void setRepeatCount(int value) {
+        mRepeatCount = value;
+    }
+    /**
+     * Defines how many times the animation should repeat. The default value
+     * is 0.
+     *
+     * @return the number of times the animation should repeat, or {@link #INFINITE}
+     */
+    public int getRepeatCount() {
+        return mRepeatCount;
+    }
+
+    /**
+     * Defines what this animation should do when it reaches the end. This
+     * setting is applied only when the repeat count is either greater than
+     * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}.
+     *
+     * @param value {@link #RESTART} or {@link #REVERSE}
+     */
+    public void setRepeatMode(int value) {
+        mRepeatMode = value;
+    }
+
+    /**
+     * Defines what this animation should do when it reaches the end.
+     *
+     * @return either one of {@link #REVERSE} or {@link #RESTART}
+     */
+    public int getRepeatMode() {
+        return mRepeatMode;
+    }
+
+    /**
+     * Adds a listener to the set of listeners that are sent update events through the life of
+     * an animation. This method is called on all listeners for every frame of the animation,
+     * after the values for the animation have been calculated.
+     *
+     * @param listener the listener to be added to the current set of listeners for this animation.
+     */
+    public void addUpdateListener(AnimatorUpdateListener listener) {
+        if (mUpdateListeners == null) {
+            mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
+        }
+        mUpdateListeners.add(listener);
+    }
+
+    /**
+     * Removes all listeners from the set listening to frame updates for this animation.
+     */
+    public void removeAllUpdateListeners() {
+        if (mUpdateListeners == null) {
+            return;
+        }
+        mUpdateListeners.clear();
+        mUpdateListeners = null;
+    }
+
+    /**
+     * Removes a listener from the set listening to frame updates for this animation.
+     *
+     * @param listener the listener to be removed from the current set of update listeners
+     * for this animation.
+     */
+    public void removeUpdateListener(AnimatorUpdateListener listener) {
+        if (mUpdateListeners == null) {
+            return;
+        }
+        mUpdateListeners.remove(listener);
+        if (mUpdateListeners.size() == 0) {
+            mUpdateListeners = null;
+        }
+    }
+
+
+    /**
+     * The time interpolator used in calculating the elapsed fraction of this animation. The
+     * interpolator determines whether the animation runs with linear or non-linear motion,
+     * such as acceleration and deceleration. The default value is
+     * {@link android.view.animation.AccelerateDecelerateInterpolator}
+     *
+     * @param value the interpolator to be used by this animation
+     */
+    @Override
+    public void setInterpolator(TimeInterpolator value) {
+        if (value != null) {
+            mInterpolator = value;
+        }
+    }
+
+    /**
+     * Returns the timing interpolator that this ValueAnimator uses.
+     *
+     * @return The timing interpolator for this ValueAnimator.
+     */
+    public TimeInterpolator getInterpolator() {
+        return mInterpolator;
+    }
+
+    /**
+     * The type evaluator to be used when calculating the animated values of this animation.
+     * The system will automatically assign a float, int, or double evaluator based on the type
+     * of <code>startValue</code> and <code>endValue</code> in the constructor. But if these values
+     * are not one of these primitive types, or if different evaluation is desired (such as is
+     * necessary with int values that represent colors), a custom evaluator needs to be assigned.
+     * For example, when running an animation on color values, the {@link RGBEvaluator}
+     * should be used to get correct RGB color interpolation.
+     *
+     * <p>If this ValueAnimator has only one set of values being animated between, this evaluator
+     * will be used for that set. If there are several sets of values being animated, which is
+     * the case if PropertyValuesHOlder objects were set on the ValueAnimator, then the evaluator
+     * is assigned just to the first PropertyValuesHolder object.</p>
+     *
+     * @param value the evaluator to be used this animation
+     */
+    public void setEvaluator(TypeEvaluator value) {
+        if (value != null && mValues != null && mValues.length > 0) {
+            mValues[0].setEvaluator(value);
+        }
+    }
+
+    /**
+     * Start the animation playing. This version of start() takes a boolean flag that indicates
+     * whether the animation should play in reverse. The flag is usually false, but may be set
+     * to true if called from the reverse() method/
+     *
+     * @param playBackwards Whether the ValueAnimator should start playing in reverse.
+     */
+    private void start(boolean playBackwards) {
+        mPlayingBackwards = playBackwards;
+        Looper looper = Looper.getMainLooper();
+        final boolean isUiThread;
+        if (looper != null) {
+            isUiThread = Thread.currentThread() == looper.getThread();
+        } else {
+            // ignore check if we don't have a Looper (this isn't an Activity)
+            isUiThread = true;
+        }
+        if ((mStartDelay == 0) && isUiThread) {
+            if (mListeners != null) {
+                ArrayList<AnimatorListener> tmpListeners =
+                        (ArrayList<AnimatorListener>) mListeners.clone();
+                int numListeners = tmpListeners.size();
+                for (int i = 0; i < numListeners; ++i) {
+                    tmpListeners.get(i).onAnimationStart(this);
+                }
+            }
+            // This sets the initial value of the animation, prior to actually starting it running
+            setCurrentPlayTime(getCurrentPlayTime());
+        }
+        mCurrentIteration = 0;
+        mPlayingState = STOPPED;
+        mStartedDelay = false;
+        sPendingAnimations.get().add(this);
+        AnimationHandler animationHandler = sAnimationHandler.get();
+        if (animationHandler == null) {
+            animationHandler = new AnimationHandler();
+            sAnimationHandler.set(animationHandler);
+        }
+        animationHandler.sendEmptyMessage(ANIMATION_START);
+    }
+
+    @Override
+    public void start() {
+        start(false);
+    }
+
+    @Override
+    public void cancel() {
+        if (mListeners != null) {
+            ArrayList<AnimatorListener> tmpListeners =
+                    (ArrayList<AnimatorListener>) mListeners.clone();
+            for (AnimatorListener listener : tmpListeners) {
+                listener.onAnimationCancel(this);
+            }
+        }
+        // Just set the CANCELED flag - this causes the animation to end the next time a frame
+        // is processed.
+        mPlayingState = CANCELED;
+    }
+
+    @Override
+    public void end() {
+        if (!sAnimations.get().contains(this) && !sPendingAnimations.get().contains(this)) {
+            // Special case if the animation has not yet started; get it ready for ending
+            mStartedDelay = false;
+            sPendingAnimations.get().add(this);
+            AnimationHandler animationHandler = sAnimationHandler.get();
+            if (animationHandler == null) {
+                animationHandler = new AnimationHandler();
+                sAnimationHandler.set(animationHandler);
+            }
+            animationHandler.sendEmptyMessage(ANIMATION_START);
+        }
+        // Just set the ENDED flag - this causes the animation to end the next time a frame
+        // is processed.
+        mPlayingState = ENDED;
+    }
+
+    @Override
+    public boolean isRunning() {
+        // ENDED or CANCELED indicate that it has been ended or canceled, but not processed yet
+        return (mPlayingState == RUNNING || mPlayingState == ENDED || mPlayingState == CANCELED);
+    }
+
+    /**
+     * Plays the ValueAnimator in reverse. If the animation is already running,
+     * it will stop itself and play backwards from the point reached when reverse was called.
+     * If the animation is not currently running, then it will start from the end and
+     * play backwards. This behavior is only set for the current animation; future playing
+     * of the animation will use the default behavior of playing forward.
+     */
+    public void reverse() {
+        mPlayingBackwards = !mPlayingBackwards;
+        if (mPlayingState == RUNNING) {
+            long currentTime = AnimationUtils.currentAnimationTimeMillis();
+            long currentPlayTime = currentTime - mStartTime;
+            long timeLeft = mDuration - currentPlayTime;
+            mStartTime = currentTime - timeLeft;
+        } else {
+            start(true);
+        }
+    }
+
+    /**
+     * Called internally to end an animation by removing it from the animations list. Must be
+     * called on the UI thread.
+     */
+    private void endAnimation() {
+        sAnimations.get().remove(this);
+        mPlayingState = STOPPED;
+        if (mListeners != null) {
+            ArrayList<AnimatorListener> tmpListeners =
+                    (ArrayList<AnimatorListener>) mListeners.clone();
+            int numListeners = tmpListeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                tmpListeners.get(i).onAnimationEnd(this);
+            }
+        }
+    }
+
+    /**
+     * Called internally to start an animation by adding it to the active animations list. Must be
+     * called on the UI thread.
+     */
+    private void startAnimation() {
+        initAnimation();
+        sAnimations.get().add(this);
+        if (mStartDelay > 0 && mListeners != null) {
+            // Listeners were already notified in start() if startDelay is 0; this is
+            // just for delayed animations
+            ArrayList<AnimatorListener> tmpListeners =
+                    (ArrayList<AnimatorListener>) mListeners.clone();
+            int numListeners = tmpListeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                tmpListeners.get(i).onAnimationStart(this);
+            }
+        }
+    }
+
+    /**
+     * Internal function called to process an animation frame on an animation that is currently
+     * sleeping through its <code>startDelay</code> phase. The return value indicates whether it
+     * should be woken up and put on the active animations queue.
+     *
+     * @param currentTime The current animation time, used to calculate whether the animation
+     * has exceeded its <code>startDelay</code> and should be started.
+     * @return True if the animation's <code>startDelay</code> has been exceeded and the animation
+     * should be added to the set of active animations.
+     */
+    private boolean delayedAnimationFrame(long currentTime) {
+        if (mPlayingState == CANCELED || mPlayingState == ENDED) {
+            // end the delay, process an animation frame to actually cancel it
+            return true;
+        }
+        if (!mStartedDelay) {
+            mStartedDelay = true;
+            mDelayStartTime = currentTime;
+        } else {
+            long deltaTime = currentTime - mDelayStartTime;
+            if (deltaTime > mStartDelay) {
+                // startDelay ended - start the anim and record the
+                // mStartTime appropriately
+                mStartTime = currentTime - (deltaTime - mStartDelay);
+                mPlayingState = RUNNING;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * This internal function processes a single animation frame for a given animation. The
+     * currentTime parameter is the timing pulse sent by the handler, used to calculate the
+     * elapsed duration, and therefore
+     * the elapsed fraction, of the animation. The return value indicates whether the animation
+     * should be ended (which happens when the elapsed time of the animation exceeds the
+     * animation's duration, including the repeatCount).
+     *
+     * @param currentTime The current time, as tracked by the static timing handler
+     * @return true if the animation's duration, including any repetitions due to
+     * <code>repeatCount</code> has been exceeded and the animation should be ended.
+     */
+    private boolean animationFrame(long currentTime) {
+        boolean done = false;
+
+        if (mPlayingState == STOPPED) {
+            mPlayingState = RUNNING;
+            if (mSeekTime < 0) {
+                mStartTime = currentTime;
+            } else {
+                mStartTime = currentTime - mSeekTime;
+                // Now that we're playing, reset the seek time
+                mSeekTime = -1;
+            }
+        }
+        switch (mPlayingState) {
+        case RUNNING:
+        case SEEKED:
+            float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
+            if (fraction >= 1f) {
+                if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
+                    // Time to repeat
+                    if (mListeners != null) {
+                        int numListeners = mListeners.size();
+                        for (int i = 0; i < numListeners; ++i) {
+                            mListeners.get(i).onAnimationRepeat(this);
+                        }
+                    }
+                    ++mCurrentIteration;
+                    if (mRepeatMode == REVERSE) {
+                        mPlayingBackwards = mPlayingBackwards ? false : true;
+                    }
+                    // TODO: doesn't account for fraction going Wayyyyy over 1, like 2+
+                    fraction = fraction - 1f;
+                    mStartTime += mDuration;
+                } else {
+                    done = true;
+                    fraction = Math.min(fraction, 1.0f);
+                }
+            }
+            if (mPlayingBackwards) {
+                fraction = 1f - fraction;
+            }
+            animateValue(fraction);
+            break;
+        case ENDED:
+            // The final value set on the target varies, depending on whether the animation
+            // was supposed to repeat an odd number of times
+            if (mRepeatCount > 0 && (mRepeatCount & 0x01) == 1) {
+                animateValue(0f);
+            } else {
+                animateValue(1f);
+            }
+            // Fall through to set done flag
+        case CANCELED:
+            done = true;
+            mPlayingState = STOPPED;
+            break;
+        }
+
+        return done;
+    }
+
+    /**
+     * This method is called with the elapsed fraction of the animation during every
+     * animation frame. This function turns the elapsed fraction into an interpolated fraction
+     * and then into an animated value (from the evaluator. The function is called mostly during
+     * animation updates, but it is also called when the <code>end()</code>
+     * function is called, to set the final value on the property.
+     *
+     * <p>Overrides of this method must call the superclass to perform the calculation
+     * of the animated value.</p>
+     *
+     * @param fraction The elapsed fraction of the animation.
+     */
+    void animateValue(float fraction) {
+        fraction = mInterpolator.getInterpolation(fraction);
+        int numValues = mValues.length;
+        for (int i = 0; i < numValues; ++i) {
+            mValues[i].calculateValue(fraction);
+        }
+        if (mUpdateListeners != null) {
+            int numListeners = mUpdateListeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                mUpdateListeners.get(i).onAnimationUpdate(this);
+            }
+        }
+    }
+
+    @Override
+    public ValueAnimator clone() {
+        final ValueAnimator anim = (ValueAnimator) super.clone();
+        if (mUpdateListeners != null) {
+            ArrayList<AnimatorUpdateListener> oldListeners = mUpdateListeners;
+            anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
+            int numListeners = oldListeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                anim.mUpdateListeners.add(oldListeners.get(i));
+            }
+        }
+        anim.mSeekTime = -1;
+        anim.mPlayingBackwards = false;
+        anim.mCurrentIteration = 0;
+        anim.mInitialized = false;
+        anim.mPlayingState = STOPPED;
+        anim.mStartedDelay = false;
+        PropertyValuesHolder[] oldValues = mValues;
+        if (oldValues != null) {
+            int numValues = oldValues.length;
+            anim.mValues = new PropertyValuesHolder[numValues];
+            for (int i = 0; i < numValues; ++i) {
+                anim.mValues[i] = oldValues[i].clone();
+            }
+            anim.mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
+            for (int i = 0; i < numValues; ++i) {
+                PropertyValuesHolder valuesHolder = mValues[i];
+                anim.mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
+            }
+        }
+        return anim;
+    }
+
+    /**
+     * Implementors of this interface can add themselves as update listeners
+     * to an <code>ValueAnimator</code> instance to receive callbacks on every animation
+     * frame, after the current frame's values have been calculated for that
+     * <code>ValueAnimator</code>.
+     */
+    public static interface AnimatorUpdateListener {
+        /**
+         * <p>Notifies the occurrence of another frame of the animation.</p>
+         *
+         * @param animation The animation which was repeated.
+         */
+        void onAnimationUpdate(ValueAnimator animation);
+
+    }
+
+    /**
+     * Return the number of animations currently running.
+     *
+     * Used by StrictMode internally to annotate violations.  Only
+     * called on the main thread.
+     *
+     * @hide
+     */
+    public static int getCurrentAnimationsCount() {
+        return sAnimations.get().size();
+    }
+}
diff --git a/core/java/android/animation/package.html b/core/java/android/animation/package.html
new file mode 100644
index 0000000..b66669b
--- /dev/null
+++ b/core/java/android/animation/package.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+Provides classes for animating values over time, and setting those values on target
+objects.
+</body>
+</html>
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
new file mode 100644
index 0000000..a57b54a
--- /dev/null
+++ b/core/java/android/app/ActionBar.java
@@ -0,0 +1,723 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.app.ActionBar.Tab;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.Window;
+import android.widget.SpinnerAdapter;
+
+/**
+ * This is the public interface to the contextual ActionBar.
+ * The ActionBar acts as a replacement for the title bar in Activities.
+ * It provides facilities for creating toolbar actions as well as
+ * methods of navigating around an application. 
+ */
+public abstract class ActionBar {
+    /**
+     * Standard navigation mode. Consists of either a logo or icon
+     * and title text with an optional subtitle. Clicking any of these elements
+     * will dispatch onActionItemSelected to the registered Callback with
+     * a MenuItem with item ID android.R.id.home.
+     */
+    public static final int NAVIGATION_MODE_STANDARD = 0;
+    
+    /**
+     * List navigation mode. Instead of static title text this mode
+     * presents a list menu for navigation within the activity.
+     * e.g. this might be presented to the user as a dropdown list.
+     */
+    public static final int NAVIGATION_MODE_LIST = 1;
+
+    /**
+     * @deprecated use NAVIGATION_MODE_LIST
+     */
+    @Deprecated
+    public static final int NAVIGATION_MODE_DROPDOWN_LIST = 1;
+    
+    /**
+     * Tab navigation mode. Instead of static title text this mode
+     * presents a series of tabs for navigation within the activity.
+     */
+    public static final int NAVIGATION_MODE_TABS = 2;
+
+    /**
+     * Use logo instead of icon if available. This flag will cause appropriate
+     * navigation modes to use a wider logo in place of the standard icon.
+     *
+     * @see #setDisplayOptions(int)
+     * @see #setDisplayOptions(int, int)
+     */
+    public static final int DISPLAY_USE_LOGO = 0x1;
+    
+    /**
+     * Show 'home' elements in this action bar, leaving more space for other
+     * navigation elements. This includes logo and icon.
+     *
+     * @see #setDisplayOptions(int)
+     * @see #setDisplayOptions(int, int)
+     */
+    public static final int DISPLAY_SHOW_HOME = 0x2;
+
+    /**
+     * @deprecated Display flags are now positive for consistency - 'show' instead of 'hide'.
+     *             Use DISPLAY_SHOW_HOME.
+     */
+    @Deprecated
+    public static final int DISPLAY_HIDE_HOME = 0x1000;
+
+    /**
+     * Display the 'home' element such that it appears as an 'up' affordance.
+     * e.g. show an arrow to the left indicating the action that will be taken.
+     *
+     * Set this flag if selecting the 'home' button in the action bar to return
+     * up by a single level in your UI rather than back to the top level or front page.
+     *
+     * @see #setDisplayOptions(int)
+     * @see #setDisplayOptions(int, int)
+     */
+    public static final int DISPLAY_HOME_AS_UP = 0x4;
+
+    /**
+     * Show the activity title and subtitle, if present.
+     *
+     * @see #setTitle(CharSequence)
+     * @see #setTitle(int)
+     * @see #setSubtitle(CharSequence)
+     * @see #setSubtitle(int)
+     * @see #setDisplayOptions(int)
+     * @see #setDisplayOptions(int, int)
+     */
+    public static final int DISPLAY_SHOW_TITLE = 0x8;
+
+    /**
+     * Show the custom view if one has been set.
+     * @see #setCustomView(View)
+     * @see #setDisplayOptions(int)
+     * @see #setDisplayOptions(int, int)
+     */
+    public static final int DISPLAY_SHOW_CUSTOM = 0x10;
+
+    /**
+     * Set the action bar into custom navigation mode, supplying a view
+     * for custom navigation.
+     *
+     * Custom navigation views appear between the application icon and
+     * any action buttons and may use any space available there. Common
+     * use cases for custom navigation views might include an auto-suggesting
+     * address bar for a browser or other navigation mechanisms that do not
+     * translate well to provided navigation modes.
+     *
+     * @param view Custom navigation view to place in the ActionBar.
+     */
+    public abstract void setCustomView(View view);
+
+    /**
+     * Set the action bar into custom navigation mode, supplying a view
+     * for custom navigation.
+     * 
+     * Custom navigation views appear between the application icon and
+     * any action buttons and may use any space available there. Common
+     * use cases for custom navigation views might include an auto-suggesting
+     * address bar for a browser or other navigation mechanisms that do not
+     * translate well to provided navigation modes.
+     * 
+     * @param view Custom navigation view to place in the ActionBar.
+     * @param layoutParams How this custom view should layout in the bar.
+     */
+    public abstract void setCustomView(View view, LayoutParams layoutParams);
+
+    /**
+     * @param view
+     * @deprecated Use {@link #setCustomView(View)} and {@link #setDisplayOptions(int)} instead.
+     */
+    @Deprecated
+    public abstract void setCustomNavigationMode(View view);
+    
+    /**
+     * Set the action bar into dropdown navigation mode and supply an adapter
+     * that will provide views for navigation choices.
+     * 
+     * @param adapter An adapter that will provide views both to display
+     *                the current navigation selection and populate views
+     *                within the dropdown navigation menu.
+     * @param callback A NavigationCallback that will receive events when the user
+     *                 selects a navigation item.
+     * @deprecated See setListNavigationCallbacks.
+     */
+    @Deprecated
+    public abstract void setDropdownNavigationMode(SpinnerAdapter adapter,
+            NavigationCallback callback);
+
+    /**
+     * Set the adapter and navigation callback for list navigation mode.
+     *
+     * The supplied adapter will provide views for the expanded list as well as
+     * the currently selected item. (These may be displayed differently.)
+     *
+     * The supplied NavigationCallback will alert the application when the user
+     * changes the current list selection.
+     *
+     * @param adapter An adapter that will provide views both to display
+     *                the current navigation selection and populate views
+     *                within the dropdown navigation menu.
+     * @param callback A NavigationCallback that will receive events when the user
+     *                 selects a navigation item.
+     */
+    public abstract void setListNavigationCallbacks(SpinnerAdapter adapter,
+            NavigationCallback callback);
+
+    /**
+     * Set the action bar into dropdown navigation mode and supply an adapter that will
+     * provide views for navigation choices.
+     *
+     * @param adapter An adapter that will provide views both to display the current
+     *                navigation selection and populate views within the dropdown
+     *                navigation menu.
+     * @param callback A NavigationCallback that will receive events when the user
+     *                 selects a navigation item.
+     * @param defaultSelectedPosition Position within the provided adapter that should be
+     *                                selected from the outset.
+     * @deprecated See setListNavigationCallbacks and setSelectedNavigationItem.
+     */
+    @Deprecated
+    public abstract void setDropdownNavigationMode(SpinnerAdapter adapter,
+            NavigationCallback callback, int defaultSelectedPosition);
+
+    /**
+     * Set the selected navigation item in list or tabbed navigation modes.
+     *
+     * @param position Position of the item to select.
+     */
+    public abstract void setSelectedNavigationItem(int position);
+
+    /**
+     * Get the position of the selected navigation item in list or tabbed navigation modes.
+     *
+     * @return Position of the selected item.
+     * @deprecated Use {@link #getSelectedNavigationIndex()} instead.
+     */
+    @Deprecated
+    public abstract int getSelectedNavigationItem();
+
+    /**
+     * Get the position of the selected navigation item in list or tabbed navigation modes.
+     *
+     * @return Position of the selected item.
+     */
+    public abstract int getSelectedNavigationIndex();
+
+    /**
+     * Get the number of navigation items present in the current navigation mode.
+     *
+     * @return Number of navigation items.
+     */
+    public abstract int getNavigationItemCount();
+
+    /**
+     * Set the action bar into standard navigation mode, using the currently set title
+     * and/or subtitle.
+     *
+     * Standard navigation mode is default. The title is automatically set to the name of
+     * your Activity on startup if an action bar is present.
+     * @deprecated See setNavigationMode
+     */
+    @Deprecated
+    public abstract void setStandardNavigationMode();
+
+    /**
+     * Set the action bar's title. This will only be displayed in standard navigation mode.
+     *
+     * @param title Title to set
+     *
+     * @see #setTitle(int)
+     */
+    public abstract void setTitle(CharSequence title);
+
+    /**
+     * Set the action bar's title. This will only be displayed in standard navigation mode.
+     *
+     * @param resId Resource ID of title string to set
+     *
+     * @see #setTitle(CharSequence)
+     */
+    public abstract void setTitle(int resId);
+
+    /**
+     * Set the action bar's subtitle. This will only be displayed in standard navigation mode.
+     * Set to null to disable the subtitle entirely.
+     *
+     * @param subtitle Subtitle to set
+     *
+     * @see #setSubtitle(int)
+     */
+    public abstract void setSubtitle(CharSequence subtitle);
+
+    /**
+     * Set the action bar's subtitle. This will only be displayed in standard navigation mode.
+     *
+     * @param resId Resource ID of subtitle string to set
+     *
+     * @see #setSubtitle(CharSequence)
+     */
+    public abstract void setSubtitle(int resId);
+
+    /**
+     * Set display options. This changes all display option bits at once. To change
+     * a limited subset of display options, see {@link #setDisplayOptions(int, int)}.
+     * 
+     * @param options A combination of the bits defined by the DISPLAY_ constants
+     *                defined in ActionBar.
+     */
+    public abstract void setDisplayOptions(int options);
+    
+    /**
+     * Set selected display options. Only the options specified by mask will be changed.
+     * To change all display option bits at once, see {@link #setDisplayOptions(int)}.
+     * 
+     * <p>Example: setDisplayOptions(0, DISPLAY_SHOW_HOME) will disable the
+     * {@link #DISPLAY_SHOW_HOME} option.
+     * setDisplayOptions(DISPLAY_SHOW_HOME, DISPLAY_SHOW_HOME | DISPLAY_USE_LOGO)
+     * will enable {@link #DISPLAY_SHOW_HOME} and disable {@link #DISPLAY_USE_LOGO}.
+     * 
+     * @param options A combination of the bits defined by the DISPLAY_ constants
+     *                defined in ActionBar.
+     * @param mask A bit mask declaring which display options should be changed.
+     */
+    public abstract void setDisplayOptions(int options, int mask);
+    
+    /**
+     * Set the ActionBar's background.
+     * 
+     * @param d Background drawable
+     */
+    public abstract void setBackgroundDrawable(Drawable d);
+    
+    /**
+     * @return The current custom navigation view.
+     */
+    public abstract View getCustomNavigationView();
+    
+    /**
+     * Returns the current ActionBar title in standard mode.
+     * Returns null if {@link #getNavigationMode()} would not return
+     * {@link #NAVIGATION_MODE_STANDARD}. 
+     *
+     * @return The current ActionBar title or null.
+     */
+    public abstract CharSequence getTitle();
+    
+    /**
+     * Returns the current ActionBar subtitle in standard mode.
+     * Returns null if {@link #getNavigationMode()} would not return
+     * {@link #NAVIGATION_MODE_STANDARD}. 
+     *
+     * @return The current ActionBar subtitle or null.
+     */
+    public abstract CharSequence getSubtitle();
+    
+    /**
+     * Returns the current navigation mode. The result will be one of:
+     * <ul>
+     * <li>{@link #NAVIGATION_MODE_STANDARD}</li>
+     * <li>{@link #NAVIGATION_MODE_LIST}</li>
+     * <li>{@link #NAVIGATION_MODE_TABS}</li>
+     * </ul>
+     *
+     * @return The current navigation mode.
+     * 
+     * @see #setStandardNavigationMode()
+     * @see #setStandardNavigationMode(CharSequence)
+     * @see #setStandardNavigationMode(CharSequence, CharSequence)
+     * @see #setDropdownNavigationMode(SpinnerAdapter)
+     * @see #setTabNavigationMode()
+     * @see #setCustomNavigationMode(View)
+     */
+    public abstract int getNavigationMode();
+
+    /**
+     * Set the current navigation mode.
+     *
+     * @param mode The new mode to set.
+     * @see #NAVIGATION_MODE_STANDARD
+     * @see #NAVIGATION_MODE_LIST
+     * @see #NAVIGATION_MODE_TABS
+     */
+    public abstract void setNavigationMode(int mode);
+
+    /**
+     * @return The current set of display options. 
+     */
+    public abstract int getDisplayOptions();
+
+    /**
+     * Set the action bar into tabbed navigation mode.
+     *
+     * @see #addTab(Tab)
+     * @see #insertTab(Tab, int)
+     * @see #removeTab(Tab)
+     * @see #removeTabAt(int)
+     *
+     * @deprecated See {@link #setNavigationMode(int)}
+     */
+    public abstract void setTabNavigationMode();
+
+    /**
+     * Create and return a new {@link Tab}.
+     * This tab will not be included in the action bar until it is added.
+     *
+     * @return A new Tab
+     *
+     * @see #addTab(Tab)
+     * @see #insertTab(Tab, int)
+     */
+    public abstract Tab newTab();
+
+    /**
+     * Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list.
+     * If this is the first tab to be added it will become the selected tab.
+     *
+     * @param tab Tab to add
+     */
+    public abstract void addTab(Tab tab);
+
+    /**
+     * Add a tab for use in tabbed navigation mode. The tab will be added at the end of the list.
+     *
+     * @param tab Tab to add
+     * @param setSelected True if the added tab should become the selected tab.
+     */
+    public abstract void addTab(Tab tab, boolean setSelected);
+
+    /**
+     * Add a tab for use in tabbed navigation mode. The tab will be inserted at
+     * <code>position</code>. If this is the first tab to be added it will become
+     * the selected tab.
+     *
+     * @param tab The tab to add
+     * @param position The new position of the tab
+     */
+    public abstract void addTab(Tab tab, int position);
+
+    /**
+     * Add a tab for use in tabbed navigation mode. The tab will be insterted at
+     * <code>position</code>.
+     *
+     * @param tab The tab to add
+     * @param position The new position of the tab
+     * @param setSelected True if the added tab should become the selected tab.
+     */
+    public abstract void addTab(Tab tab, int position, boolean setSelected);
+
+    /**
+     * Remove a tab from the action bar. If the removed tab was selected it will be deselected
+     * and another tab will be selected if present.
+     *
+     * @param tab The tab to remove
+     */
+    public abstract void removeTab(Tab tab);
+
+    /**
+     * Remove a tab from the action bar. If the removed tab was selected it will be deselected
+     * and another tab will be selected if present.
+     *
+     * @param position Position of the tab to remove
+     */
+    public abstract void removeTabAt(int position);
+
+    /**
+     * Remove all tabs from the action bar and deselect the current tab.
+     */
+    public abstract void removeAllTabs();
+
+    /**
+     * Select the specified tab. If it is not a child of this action bar it will be added.
+     *
+     * <p>Note: If you want to select by index, use {@link #setSelectedNavigationItem(int)}.</p>
+     *
+     * @param tab Tab to select
+     */
+    public abstract void selectTab(Tab tab);
+
+    /**
+     * Returns the currently selected tab if in tabbed navigation mode and there is at least
+     * one tab present.
+     *
+     * @return The currently selected tab or null
+     */
+    public abstract Tab getSelectedTab();
+
+    /**
+     * Returns the tab at the specified index.
+     *
+     * @param index Index value in the range 0-get
+     * @return
+     */
+    public abstract Tab getTabAt(int index);
+
+    /**
+     * Returns the number of tabs currently registered with the action bar.
+     * @return Tab count
+     */
+    public abstract int getTabCount();
+
+    /**
+     * Retrieve the current height of the ActionBar.
+     *
+     * @return The ActionBar's height
+     */
+    public abstract int getHeight();
+
+    /**
+     * Show the ActionBar if it is not currently showing.
+     * If the window hosting the ActionBar does not have the feature
+     * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
+     * content to fit the new space available.
+     */
+    public abstract void show();
+
+    /**
+     * Hide the ActionBar if it is not currently showing.
+     * If the window hosting the ActionBar does not have the feature
+     * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
+     * content to fit the new space available.
+     */
+    public abstract void hide();
+
+    /**
+     * @return <code>true</code> if the ActionBar is showing, <code>false</code> otherwise.
+     */
+    public abstract boolean isShowing();
+
+    /**
+     * Callback interface for ActionBar navigation events. 
+     */
+    public interface NavigationCallback {
+        /**
+         * This method is called whenever a navigation item in your action bar
+         * is selected.
+         *    
+         * @param itemPosition Position of the item clicked.
+         * @param itemId ID of the item clicked.
+         * @return True if the event was handled, false otherwise.
+         */
+        public boolean onNavigationItemSelected(int itemPosition, long itemId);
+    }
+
+    /**
+     * A tab in the action bar.
+     *
+     * <p>Tabs manage the hiding and showing of {@link Fragment}s.
+     */
+    public static abstract class Tab {
+        /**
+         * An invalid position for a tab.
+         *
+         * @see #getPosition()
+         */
+        public static final int INVALID_POSITION = -1;
+
+        /**
+         * Return the current position of this tab in the action bar.
+         *
+         * @return Current position, or {@link #INVALID_POSITION} if this tab is not currently in
+         *         the action bar.
+         */
+        public abstract int getPosition();
+
+        /**
+         * Return the icon associated with this tab.
+         *
+         * @return The tab's icon
+         */
+        public abstract Drawable getIcon();
+
+        /**
+         * Return the text of this tab.
+         *
+         * @return The tab's text
+         */
+        public abstract CharSequence getText();
+
+        /**
+         * Set the icon displayed on this tab.
+         *
+         * @param icon The drawable to use as an icon
+         * @return The current instance for call chaining
+         */
+        public abstract Tab setIcon(Drawable icon);
+
+        /**
+         * Set the text displayed on this tab. Text may be truncated if there is not
+         * room to display the entire string.
+         *
+         * @param text The text to display
+         * @return The current instance for call chaining
+         */
+        public abstract Tab setText(CharSequence text);
+
+        /**
+         * Set a custom view to be used for this tab. This overrides values set by
+         * {@link #setText(CharSequence)} and {@link #setIcon(Drawable)}.
+         *
+         * @param view Custom view to be used as a tab.
+         * @return The current instance for call chaining
+         */
+        public abstract Tab setCustomView(View view);
+
+        /**
+         * Retrieve a previously set custom view for this tab.
+         *
+         * @return The custom view set by {@link #setCustomView(View)}.
+         */
+        public abstract View getCustomView();
+
+        /**
+         * Give this Tab an arbitrary object to hold for later use.
+         *
+         * @param obj Object to store
+         * @return The current instance for call chaining
+         */
+        public abstract Tab setTag(Object obj);
+
+        /**
+         * @return This Tab's tag object.
+         */
+        public abstract Object getTag();
+
+        /**
+         * Set the {@link TabListener} that will handle switching to and from this tab.
+         * All tabs must have a TabListener set before being added to the ActionBar.
+         *
+         * @param listener Listener to handle tab selection events
+         * @return The current instance for call chaining
+         */
+        public abstract Tab setTabListener(TabListener listener);
+
+        /**
+         * Select this tab. Only valid if the tab has been added to the action bar.
+         */
+        public abstract void select();
+    }
+
+    /**
+     * Callback interface invoked when a tab is focused, unfocused, added, or removed.
+     */
+    public interface TabListener {
+        /**
+         * Called when a tab enters the selected state.
+         *
+         * @param tab The tab that was selected
+         * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
+         *        during a tab switch. The previous tab's unselect and this tab's select will be
+         *        executed in a single transaction. This FragmentTransaction does not support
+         *        being added to the back stack.
+         */
+        public void onTabSelected(Tab tab, FragmentTransaction ft);
+
+        /**
+         * Called when a tab exits the selected state.
+         *
+         * @param tab The tab that was unselected
+         * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
+         *        during a tab switch. This tab's unselect and the newly selected tab's select
+         *        will be executed in a single transaction. This FragmentTransaction does not
+         *        support being added to the back stack.
+         */
+        public void onTabUnselected(Tab tab, FragmentTransaction ft);
+
+        /**
+         * Called when a tab that is already selected is chosen again by the user.
+         * Some applications may use this action to return to the top level of a category.
+         *
+         * @param tab The tab that was reselected.
+         * @param ft A {@link FragmentTransaction} for queuing fragment operations to execute
+         *        once this method returns. This FragmentTransaction does not support
+         *        being added to the back stack.
+         */
+        public void onTabReselected(Tab tab, FragmentTransaction ft);
+    }
+
+    /**
+     * Per-child layout information associated with action bar custom views.
+     *
+     * @attr ref android.R.styleable#ActionBar_LayoutParams_layout_gravity
+     */
+    public static class LayoutParams extends MarginLayoutParams {
+        /**
+         * Gravity for the view associated with these LayoutParams.
+         *
+         * @see android.view.Gravity
+         */
+        @ViewDebug.ExportedProperty(category = "layout", mapping = {
+            @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
+            @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
+            @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
+            @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
+            @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
+            @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
+            @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
+            @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
+            @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
+            @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL,   to = "FILL_HORIZONTAL"),
+            @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
+            @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
+        })
+        public int gravity = -1;
+
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+
+            TypedArray a = c.obtainStyledAttributes(attrs,
+                    com.android.internal.R.styleable.ActionBar_LayoutParams);
+            gravity = a.getInt(
+                    com.android.internal.R.styleable.ActionBar_LayoutParams_layout_gravity, -1);
+        }
+
+        public LayoutParams(int width, int height) {
+            super(width, height);
+            this.gravity = Gravity.CENTER_VERTICAL | Gravity.LEFT;
+        }
+
+        public LayoutParams(int width, int height, int gravity) {
+            super(width, height);
+            this.gravity = gravity;
+        }
+
+        public LayoutParams(int gravity) {
+            this(WRAP_CONTENT, MATCH_PARENT, gravity);
+        }
+
+        public LayoutParams(LayoutParams source) {
+            super(source);
+
+            this.gravity = source.gravity;
+        }
+
+        public LayoutParams(ViewGroup.LayoutParams source) {
+            super(source);
+        }
+    }
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f25c4c3..33f88d8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,19 +16,22 @@
 
 package android.app;
 
+import com.android.internal.app.ActionBarImpl;
 import com.android.internal.policy.PolicyManager;
 
 import android.content.ComponentCallbacks;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.Intent;
+import android.content.CursorLoader;
 import android.content.IIntentSender;
+import android.content.Intent;
 import android.content.IntentSender;
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -39,6 +42,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
@@ -49,7 +53,9 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.SparseArray;
+import android.view.ActionMode;
 import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
 import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -58,18 +64,18 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.OnCreateContextMenuListener;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.ViewManager;
 import android.view.Window;
 import android.view.WindowManager;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.OnCreateContextMenuListener;
-import android.view.ViewGroup.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.AdapterView;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -110,6 +116,7 @@
  * 
  * <p>Topics covered here:
  * <ol>
+ * <li><a href="#Fragments">Fragments</a>
  * <li><a href="#ActivityLifecycle">Activity Lifecycle</a>
  * <li><a href="#ConfigurationChanges">Configuration Changes</a>
  * <li><a href="#StartingActivities">Starting Activities and Getting Results</a>
@@ -118,6 +125,14 @@
  * <li><a href="#ProcessLifecycle">Process Lifecycle</a>
  * </ol>
  * 
+ * <a name="Fragments"></a>
+ * <h3>Fragments</h3>
+ *
+ * <p>Starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB}, Activity
+ * implementations can make use of the {@link Fragment} class to better
+ * modularize their code, build more sophisticated user interfaces for larger
+ * screens, and help scale their application between small and large screens.
+ *
  * <a name="ActivityLifecycle"></a>
  * <h3>Activity Lifecycle</h3>
  *
@@ -592,7 +607,7 @@
  * or finished.
  */
 public class Activity extends ContextThemeWrapper
-        implements LayoutInflater.Factory,
+        implements LayoutInflater.Factory2,
         Window.Callback, KeyEvent.Callback,
         OnCreateContextMenuListener, ComponentCallbacks {
     private static final String TAG = "Activity";
@@ -604,9 +619,8 @@
     /** Start of user-defined activity results. */
     public static final int RESULT_FIRST_USER   = 1;
 
-    private static long sInstanceCount = 0;
-
     private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
+    private static final String FRAGMENTS_TAG = "android:fragments";
     private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
     private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
     private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";
@@ -628,18 +642,30 @@
     private ComponentName mComponent;
     /*package*/ ActivityInfo mActivityInfo;
     /*package*/ ActivityThread mMainThread;
-    /*package*/ Object mLastNonConfigurationInstance;
-    /*package*/ HashMap<String,Object> mLastNonConfigurationChildInstances;
     Activity mParent;
     boolean mCalled;
+    boolean mCheckedForLoaderManager;
+    boolean mLoadersStarted;
     private boolean mResumed;
     private boolean mStopped;
     boolean mFinished;
     boolean mStartedActivity;
+    /** true if the activity is going through a transient pause */
+    /*package*/ boolean mTemporaryPause = false;
+    /** true if the activity is being destroyed in order to recreate it with a new configuration */
+    /*package*/ boolean mChangingConfigurations = false;
     /*package*/ int mConfigChangeFlags;
     /*package*/ Configuration mCurrentConfig;
     private SearchManager mSearchManager;
 
+    static final class NonConfigurationInstances {
+        Object activity;
+        HashMap<String, Object> children;
+        ArrayList<Fragment> fragments;
+        SparseArray<LoaderManagerImpl> loaders;
+    }
+    /* package */ NonConfigurationInstances mLastNonConfigurationInstances;
+    
     private Window mWindow;
 
     private WindowManager mWindowManager;
@@ -647,10 +673,16 @@
     /*package*/ boolean mWindowAdded = false;
     /*package*/ boolean mVisibleFromServer = false;
     /*package*/ boolean mVisibleFromClient = true;
+    /*package*/ ActionBarImpl mActionBar = null;
 
     private CharSequence mTitle;
     private int mTitleColor = 0;
 
+    final FragmentManagerImpl mFragments = new FragmentManagerImpl();
+    
+    SparseArray<LoaderManagerImpl> mAllLoaderManagers;
+    LoaderManagerImpl mLoaderManager;
+    
     private static final class ManagedCursor {
         ManagedCursor(Cursor cursor) {
             mCursor = cursor;
@@ -677,24 +709,7 @@
     protected static final int[] FOCUSED_STATE_SET = {com.android.internal.R.attr.state_focused};
 
     private Thread mUiThread;
-    private final Handler mHandler = new Handler();
-
-    // Used for debug only
-    /*
-    public Activity() {
-        ++sInstanceCount;
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        super.finalize();
-        --sInstanceCount;
-    }
-    */
-
-    public static long getInstanceCount() {
-        return sInstanceCount;
-    }
+    final Handler mHandler = new Handler();
 
     /** Return the intent that started this activity. */
     public Intent getIntent() {
@@ -748,6 +763,34 @@
     }
 
     /**
+     * Return the LoaderManager for this fragment, creating it if needed.
+     */
+    public LoaderManager getLoaderManager() {
+        if (mLoaderManager != null) {
+            return mLoaderManager;
+        }
+        mCheckedForLoaderManager = true;
+        mLoaderManager = getLoaderManager(-1, mLoadersStarted, true);
+        return mLoaderManager;
+    }
+    
+    LoaderManagerImpl getLoaderManager(int index, boolean started, boolean create) {
+        if (mAllLoaderManagers == null) {
+            mAllLoaderManagers = new SparseArray<LoaderManagerImpl>();
+        }
+        LoaderManagerImpl lm = mAllLoaderManagers.get(index);
+        if (lm == null) {
+            if (create) {
+                lm = new LoaderManagerImpl(this, started);
+                mAllLoaderManagers.put(index, lm);
+            }
+        } else {
+            lm.updateActivity(this);
+        }
+        return lm;
+    }
+    
+    /**
      * Calls {@link android.view.Window#getCurrentFocus} on the
      * Window of this Activity to return the currently focused view.
      * 
@@ -801,6 +844,15 @@
     protected void onCreate(Bundle savedInstanceState) {
         mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
                 com.android.internal.R.styleable.Window_windowNoDisplay, false);
+        if (mLastNonConfigurationInstances != null) {
+            mAllLoaderManagers = mLastNonConfigurationInstances.loaders;
+        }
+        if (savedInstanceState != null) {
+            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
+            mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
+                    ? mLastNonConfigurationInstances.fragments : null);
+        }
+        mFragments.dispatchCreate();
         mCalled = true;
     }
 
@@ -933,6 +985,16 @@
      */
     protected void onStart() {
         mCalled = true;
+        
+        if (!mLoadersStarted) {
+            mLoadersStarted = true;
+            if (mLoaderManager != null) {
+                mLoaderManager.doStart();
+            } else if (!mCheckedForLoaderManager) {
+                mLoaderManager = getLoaderManager(-1, mLoadersStarted, false);
+            }
+            mCheckedForLoaderManager = true;
+        }
     }
 
     /**
@@ -1085,6 +1147,10 @@
      */
     protected void onSaveInstanceState(Bundle outState) {
         outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
+        Parcelable p = mFragments.saveAllState();
+        if (p != null) {
+            outState.putParcelable(FRAGMENTS_TAG, p);
+        }
     }
 
     /**
@@ -1407,7 +1473,8 @@
      * {@link #onRetainNonConfigurationInstance()}.
      */
     public Object getLastNonConfigurationInstance() {
-        return mLastNonConfigurationInstance;
+        return mLastNonConfigurationInstances != null
+                ? mLastNonConfigurationInstances.activity : null;
     }
     
     /**
@@ -1419,6 +1486,11 @@
      * {@link #getLastNonConfigurationInstance()} in the new activity
      * instance.
      * 
+     * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+     * or later, consider instead using a {@link Fragment} with
+     * {@link Fragment#setRetainInstance(boolean)
+     * Fragment.setRetainInstance(boolean}.</em>
+     *
      * <p>This function is called purely as an optimization, and you must
      * not rely on it being called.  When it is called, a number of guarantees
      * will be made to help optimize configuration switching:
@@ -1474,8 +1546,9 @@
      * @return Returns the object previously returned by
      * {@link #onRetainNonConfigurationChildInstances()}
      */
-    HashMap<String,Object> getLastNonConfigurationChildInstances() {
-        return mLastNonConfigurationChildInstances;
+    HashMap<String, Object> getLastNonConfigurationChildInstances() {
+        return mLastNonConfigurationInstances != null
+                ? mLastNonConfigurationInstances.children : null;
     }
     
     /**
@@ -1489,17 +1562,88 @@
         return null;
     }
     
+    NonConfigurationInstances retainNonConfigurationInstances() {
+        Object activity = onRetainNonConfigurationInstance();
+        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
+        ArrayList<Fragment> fragments = mFragments.retainNonConfig();
+        boolean retainLoaders = false;
+        if (mAllLoaderManagers != null) {
+            // prune out any loader managers that were already stopped and so
+            // have nothing useful to retain.
+            for (int i=mAllLoaderManagers.size()-1; i>=0; i--) {
+                LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i);
+                if (lm.mRetaining) {
+                    retainLoaders = true;
+                } else {
+                    lm.doDestroy();
+                    mAllLoaderManagers.removeAt(i);
+                }
+            }
+        }
+        if (activity == null && children == null && fragments == null && !retainLoaders) {
+            return null;
+        }
+        
+        NonConfigurationInstances nci = new NonConfigurationInstances();
+        nci.activity = activity;
+        nci.children = children;
+        nci.fragments = fragments;
+        nci.loaders = mAllLoaderManagers;
+        return nci;
+    }
+    
     public void onLowMemory() {
         mCalled = true;
     }
     
     /**
+     * Return the FragmentManager for interacting with fragments associated
+     * with this activity.
+     */
+    public FragmentManager getFragmentManager() {
+        return mFragments;
+    }
+
+    /**
+     * Start a series of edit operations on the Fragments associated with
+     * this activity.
+     * @deprecated use {@link #getFragmentManager}.
+     */
+    @Deprecated
+    public FragmentTransaction openFragmentTransaction() {
+        return mFragments.openTransaction();
+    }
+    
+    void invalidateFragmentIndex(int index) {
+        //Log.v(TAG, "invalidateFragmentIndex: index=" + index);
+        if (mAllLoaderManagers != null) {
+            LoaderManagerImpl lm = mAllLoaderManagers.get(index);
+            if (lm != null) {
+                lm.doDestroy();
+            }
+            mAllLoaderManagers.remove(index);
+        }
+    }
+    
+    /**
+     * Called when a Fragment is being attached to this activity, immediately
+     * after the call to its {@link Fragment#onAttach Fragment.onAttach()}
+     * method and before {@link Fragment#onCreate Fragment.onCreate()}.
+     */
+    public void onAttachFragment(Fragment fragment) {
+    }
+    
+    /**
      * Wrapper around
      * {@link ContentResolver#query(android.net.Uri , String[], String, String[], String)}
      * that gives the resulting {@link Cursor} to call
      * {@link #startManagingCursor} so that the activity will manage its
      * lifecycle for you.
      * 
+     * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+     * or later, consider instead using {@link LoaderManager} instead, available
+     * via {@link #getLoaderManager()}.</em>
+     *
      * @param uri The URI of the content provider to query.
      * @param projection List of columns to return.
      * @param selection SQL WHERE clause.
@@ -1510,12 +1654,12 @@
      * @see ContentResolver#query(android.net.Uri , String[], String, String[], String)
      * @see #startManagingCursor
      * @hide
+     *
+     * @deprecated Use {@link CursorLoader} instead.
      */
-    public final Cursor managedQuery(Uri uri,
-                                     String[] projection,
-                                     String selection,
-                                     String sortOrder)
-    {
+    @Deprecated
+    public final Cursor managedQuery(Uri uri, String[] projection, String selection,
+            String sortOrder) {
         Cursor c = getContentResolver().query(uri, projection, selection, null, sortOrder);
         if (c != null) {
             startManagingCursor(c);
@@ -1530,6 +1674,10 @@
      * {@link #startManagingCursor} so that the activity will manage its
      * lifecycle for you.
      * 
+     * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+     * or later, consider instead using {@link LoaderManager} instead, available
+     * via {@link #getLoaderManager()}.</em>
+     *
      * @param uri The URI of the content provider to query.
      * @param projection List of columns to return.
      * @param selection SQL WHERE clause.
@@ -1540,13 +1688,12 @@
      * 
      * @see ContentResolver#query(android.net.Uri , String[], String, String[], String)
      * @see #startManagingCursor
+     *
+     * @deprecated Use {@link CursorLoader} instead.
      */
-    public final Cursor managedQuery(Uri uri,
-                                     String[] projection,
-                                     String selection,
-                                     String[] selectionArgs,
-                                     String sortOrder)
-    {
+    @Deprecated
+    public final Cursor managedQuery(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
         Cursor c = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
         if (c != null) {
             startManagingCursor(c);
@@ -1555,40 +1702,6 @@
     }
 
     /**
-     * Wrapper around {@link Cursor#commitUpdates()} that takes care of noting
-     * that the Cursor needs to be requeried.  You can call this method in
-     * {@link #onPause} or {@link #onStop} to have the system call
-     * {@link Cursor#requery} for you if the activity is later resumed.  This
-     * allows you to avoid determing when to do the requery yourself (which is
-     * required for the Cursor to see any data changes that were committed with
-     * it).
-     * 
-     * @param c The Cursor whose changes are to be committed.
-     * 
-     * @see #managedQuery(android.net.Uri , String[], String, String[], String)
-     * @see #startManagingCursor
-     * @see Cursor#commitUpdates()
-     * @see Cursor#requery
-     * @hide
-     */
-    @Deprecated
-    public void managedCommitUpdates(Cursor c) {
-        synchronized (mManagedCursors) {
-            final int N = mManagedCursors.size();
-            for (int i=0; i<N; i++) {
-                ManagedCursor mc = mManagedCursors.get(i);
-                if (mc.mCursor == c) {
-                    c.commitUpdates();
-                    mc.mUpdated = true;
-                    return;
-                }
-            }
-            throw new RuntimeException(
-                "Cursor " + c + " is not currently managed");
-        }
-    }
-
-    /**
      * This method allows the activity to take care of managing the given
      * {@link Cursor}'s lifecycle for you based on the activity's lifecycle.
      * That is, when the activity is stopped it will automatically call
@@ -1596,11 +1709,18 @@
      * it will call {@link Cursor#requery} for you.  When the activity is
      * destroyed, all managed Cursors will be closed automatically.
      * 
+     * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+     * or later, consider instead using {@link LoaderManager} instead, available
+     * via {@link #getLoaderManager()}.</em>
+     *
      * @param c The Cursor to be managed.
      * 
      * @see #managedQuery(android.net.Uri , String[], String, String[], String)
      * @see #stopManagingCursor
+     *
+     * @deprecated Use {@link CursorLoader} instead.
      */
+    @Deprecated
     public void startManagingCursor(Cursor c) {
         synchronized (mManagedCursors) {
             mManagedCursors.add(new ManagedCursor(c));
@@ -1615,7 +1735,10 @@
      * @param c The Cursor that was being managed.
      * 
      * @see #startManagingCursor
+     *
+     * @deprecated Use {@link CursorLoader} instead.
      */
+    @Deprecated
     public void stopManagingCursor(Cursor c) {
         synchronized (mManagedCursors) {
             final int N = mManagedCursors.size();
@@ -1646,7 +1769,54 @@
     public View findViewById(int id) {
         return getWindow().findViewById(id);
     }
-
+    
+    /**
+     * Retrieve a reference to this activity's ActionBar.
+     *
+     * @return The Activity's ActionBar, or null if it does not have one.
+     */
+    public ActionBar getActionBar() {
+        initActionBar();
+        return mActionBar;
+    }
+    
+    /**
+     * Creates a new ActionBar, locates the inflated ActionBarView,
+     * initializes the ActionBar with the view, and sets mActionBar.
+     */
+    private void initActionBar() {
+        Window window = getWindow();
+        if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
+            return;
+        }
+        
+        mActionBar = new ActionBarImpl(this);
+    }
+    
+    /**
+     * Finds a fragment that was identified by the given id either when inflated
+     * from XML or as the container ID when added in a transaction.  This only
+     * returns fragments that are currently added to the activity's content.
+     * @return The fragment if found or null otherwise.
+     * @deprecated use {@link #getFragmentManager}.
+     */
+    @Deprecated
+    public Fragment findFragmentById(int id) {
+        return mFragments.findFragmentById(id);
+    }
+    
+    /**
+     * Finds a fragment that was identified by the given tag either when inflated
+     * from XML or as supplied when added in a transaction.  This only
+     * returns fragments that are currently added to the activity's content.
+     * @return The fragment if found or null otherwise.
+     * @deprecated use {@link #getFragmentManager}.
+     */
+    @Deprecated
+    public Fragment findFragmentByTag(String tag) {
+        return mFragments.findFragmentByTag(tag);
+    }
+    
     /**
      * Set the activity content from a layout resource.  The resource will be
      * inflated, adding all top-level views to the activity.
@@ -1655,6 +1825,7 @@
      */
     public void setContentView(int layoutResID) {
         getWindow().setContentView(layoutResID);
+        initActionBar();
     }
 
     /**
@@ -1666,6 +1837,7 @@
      */
     public void setContentView(View view) {
         getWindow().setContentView(view);
+        initActionBar();
     }
 
     /**
@@ -1678,6 +1850,7 @@
      */
     public void setContentView(View view, ViewGroup.LayoutParams params) {
         getWindow().setContentView(view, params);
+        initActionBar();
     }
 
     /**
@@ -1689,6 +1862,7 @@
      */
     public void addContentView(View view, ViewGroup.LayoutParams params) {
         getWindow().addContentView(view, params);
+        initActionBar();
     }
 
     /**
@@ -1912,12 +2086,65 @@
     }
     
     /**
+     * Flag for {@link #popBackStack(String, int)}
+     * and {@link #popBackStack(int, int)}: If set, and the name or ID of
+     * a back stack entry has been supplied, then all matching entries will
+     * be consumed until one that doesn't match is found or the bottom of
+     * the stack is reached.  Otherwise, all entries up to but not including that entry
+     * will be removed.
+     */
+    public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
+
+    /**
+     * Pop the top state off the back stack.  Returns true if there was one
+     * to pop, else false.
+     * @deprecated use {@link #getFragmentManager}.
+     */
+    @Deprecated
+    public boolean popBackStack() {
+        return mFragments.popBackStack();
+    }
+
+    /**
+     * Pop the last fragment transition from the local activity's fragment
+     * back stack.  If there is nothing to pop, false is returned.
+     * @param name If non-null, this is the name of a previous back state
+     * to look for; if found, all states up to that state will be popped.  The
+     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
+     * the named state itself is popped. If null, only the top state is popped.
+     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
+     * @deprecated use {@link #getFragmentManager}.
+     */
+    @Deprecated
+    public boolean popBackStack(String name, int flags) {
+        return mFragments.popBackStack(name, flags);
+    }
+
+    /**
+     * Pop all back stack states up to the one with the given identifier.
+     * @param id Identifier of the stated to be popped. If no identifier exists,
+     * false is returned.
+     * The identifier is the number returned by
+     * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.  The
+     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
+     * the named state itself is popped.
+     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
+     * @deprecated use {@link #getFragmentManager}.
+     */
+    @Deprecated
+    public boolean popBackStack(int id, int flags) {
+        return mFragments.popBackStack(id, flags);
+    }
+    
+    /**
      * Called when the activity has detected the user's press of the back
      * key.  The default implementation simply finishes the current activity,
      * but you can override this to do whatever you want.
      */
     public void onBackPressed() {
-        finish();
+        if (!mFragments.popBackStack()) {
+            finish();
+        }
     }
     
     /**
@@ -2155,7 +2382,9 @@
      */
     public boolean onCreatePanelMenu(int featureId, Menu menu) {
         if (featureId == Window.FEATURE_OPTIONS_PANEL) {
-            return onCreateOptionsMenu(menu);
+            boolean show = onCreateOptionsMenu(menu);
+            show |= mFragments.dispatchCreateOptionsMenu(menu, getMenuInflater());
+            return show;
         }
         return false;
     }
@@ -2172,6 +2401,7 @@
     public boolean onPreparePanel(int featureId, View view, Menu menu) {
         if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) {
             boolean goforit = onPrepareOptionsMenu(menu);
+            goforit |= mFragments.dispatchPrepareOptionsMenu(menu);
             return goforit && menu.hasVisibleItems();
         }
         return true;
@@ -2202,11 +2432,17 @@
                 // doesn't call through to superclass's implmeentation of each
                 // of these methods below
                 EventLog.writeEvent(50000, 0, item.getTitleCondensed());
-                return onOptionsItemSelected(item);
+                if (onOptionsItemSelected(item)) {
+                    return true;
+                }
+                return mFragments.dispatchOptionsItemSelected(item);
                 
             case Window.FEATURE_CONTEXT_MENU:
                 EventLog.writeEvent(50000, 1, item.getTitleCondensed());
-                return onContextItemSelected(item);
+                if (onContextItemSelected(item)) {
+                    return true;
+                }
+                return mFragments.dispatchContextItemSelected(item);
                 
             default:
                 return false;
@@ -2225,6 +2461,7 @@
     public void onPanelClosed(int featureId, Menu menu) {
         switch (featureId) {
             case Window.FEATURE_OPTIONS_PANEL:
+                mFragments.dispatchOptionsMenuClosed(menu);
                 onOptionsMenuClosed(menu);
                 break;
                 
@@ -2235,6 +2472,15 @@
     }
 
     /**
+     * Declare that the options menu has changed, so should be recreated.
+     * The {@link #onCreateOptionsMenu(Menu)} method will be called the next
+     * time it needs to be displayed.
+     */
+    public void invalidateOptionsMenu() {
+        mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+    }
+    
+    /**
      * Initialize the contents of the Activity's standard options menu.  You
      * should place your menu items in to <var>menu</var>.
      * 
@@ -2457,6 +2703,9 @@
      * by the activity.  The default implementation calls through to
      * {@link #onCreateDialog(int)} for compatibility.
      *
+     * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+     * or later, consider instead using a {@link DialogFragment} instead.</em>
+     *
      * <p>If you use {@link #showDialog(int)}, the activity will call through to
      * this method the first time, and hang onto it thereafter.  Any dialog
      * that is created by this method will automatically be saved and restored
@@ -2529,6 +2778,9 @@
      * will be made with the same id the first time this is called for a given
      * id.  From thereafter, the dialog will be automatically saved and restored.
      *
+     * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+     * or later, consider instead using a {@link DialogFragment} instead.</em>
+     *
      * <p>Each time a dialog is shown, {@link #onPrepareDialog(int, Dialog, Bundle)} will
      * be made to provide an opportunity to do any timely preparation.
      *
@@ -3076,6 +3328,36 @@
     }
 
     /**
+     * This is called when a Fragment in this activity calls its 
+     * {@link Fragment#startActivity} or {@link Fragment#startActivityForResult}
+     * method.
+     * 
+     * <p>This method throws {@link android.content.ActivityNotFoundException}
+     * if there was no Activity found to run the given Intent.
+     * 
+     * @param fragment The fragment making the call.
+     * @param intent The intent to start.
+     * @param requestCode Reply request code.  < 0 if reply is not requested. 
+     * 
+     * @throws android.content.ActivityNotFoundException
+     * 
+     * @see Fragment#startActivity 
+     * @see Fragment#startActivityForResult 
+     */
+    public void startActivityFromFragment(Fragment fragment, Intent intent, 
+            int requestCode) {
+        Instrumentation.ActivityResult ar =
+            mInstrumentation.execStartActivity(
+                this, mMainThread.getApplicationThread(), mToken, fragment,
+                intent, requestCode);
+        if (ar != null) {
+            mMainThread.sendActivityResult(
+                mToken, fragment.mWho, requestCode,
+                ar.getResultCode(), ar.getResultData());
+        }
+    }
+
+    /**
      * Like {@link #startActivityFromChild(Activity, Intent, int)}, but
      * taking a IntentSender; see
      * {@link #startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}
@@ -3234,6 +3516,19 @@
     }
 
     /**
+     * Check to see whether this activity is in the process of being destroyed in order to be
+     * recreated with a new configuration. This is often used in
+     * {@link #onStop} to determine whether the state needs to be cleaned up or will be passed
+     * on to the next instance of the activity via {@link #onRetainNonConfigurationInstance()}.
+     * 
+     * @return If the activity is being torn down in order to be recreated with a new configuration,
+     * returns true; else returns false.
+     */
+    public boolean isChangingConfigurations() {
+        return mChangingConfigurations;
+    }
+
+    /**
      * Call this when your activity is done and should be closed.  The
      * ActivityResult is propagated back to whoever launched you via
      * onActivityResult().
@@ -3334,8 +3629,7 @@
      * @see #createPendingResult
      * @see #setResult(int)
      */
-    protected void onActivityResult(int requestCode, int resultCode,
-            Intent data) {
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
     }
 
     /**
@@ -3719,9 +4013,12 @@
     }
 
     /**
-     * Stub implementation of {@link android.view.LayoutInflater.Factory#onCreateView} used when
-     * inflating with the LayoutInflater returned by {@link #getSystemService}.  This
-     * implementation simply returns null for all view names.
+     * Standard implementation of
+     * {@link android.view.LayoutInflater.Factory#onCreateView} used when
+     * inflating with the LayoutInflater returned by {@link #getSystemService}.
+     * This implementation does nothing and is for
+     * pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB} apps.  Newer apps
+     * should use {@link #onCreateView(View, String, Context, AttributeSet)}.
      *
      * @see android.view.LayoutInflater#createView
      * @see android.view.Window#getLayoutInflater
@@ -3730,6 +4027,172 @@
         return null;
     }
 
+    /**
+     * Standard implementation of
+     * {@link android.view.LayoutInflater.Factory2#onCreateView(View, String, Context, AttributeSet)}
+     * used when inflating with the LayoutInflater returned by {@link #getSystemService}.
+     * This implementation handles <fragment> tags to embed fragments inside
+     * of the activity.
+     *
+     * @see android.view.LayoutInflater#createView
+     * @see android.view.Window#getLayoutInflater
+     */
+    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+        if (!"fragment".equals(name)) {
+            return onCreateView(name, context, attrs);
+        }
+        
+        String fname = attrs.getAttributeValue(null, "class");
+        TypedArray a = 
+            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);
+        if (fname == null) {
+            fname = a.getString(com.android.internal.R.styleable.Fragment_name);
+        }
+        int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, View.NO_ID);
+        String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);
+        a.recycle();
+        
+        int containerId = parent != null ? parent.getId() : 0;
+        if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
+            throw new IllegalArgumentException(attrs.getPositionDescription()
+                    + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname);
+        }
+
+        // If we restored from a previous state, we may already have
+        // instantiated this fragment from the state and should use
+        // that instance instead of making a new one.
+        Fragment fragment = id != View.NO_ID ? mFragments.findFragmentById(id) : null;
+        if (fragment == null && tag != null) {
+            fragment = mFragments.findFragmentByTag(tag);
+        }
+        if (fragment == null && containerId != View.NO_ID) {
+            fragment = mFragments.findFragmentById(containerId);
+        }
+
+        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"
+                + Integer.toHexString(id) + " fname=" + fname
+                + " existing=" + fragment);
+        if (fragment == null) {
+            fragment = Fragment.instantiate(this, fname);
+            fragment.mFromLayout = true;
+            fragment.mFragmentId = id != 0 ? id : containerId;
+            fragment.mContainerId = containerId;
+            fragment.mTag = tag;
+            fragment.mInLayout = true;
+            fragment.mImmediateActivity = this;
+            fragment.mFragmentManager = mFragments;
+            fragment.onInflate(attrs, fragment.mSavedFragmentState);
+            mFragments.addFragment(fragment, true);
+
+        } else if (fragment.mInLayout) {
+            // A fragment already exists and it is not one we restored from
+            // previous state.
+            throw new IllegalArgumentException(attrs.getPositionDescription()
+                    + ": Duplicate id 0x" + Integer.toHexString(id)
+                    + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)
+                    + " with another fragment for " + fname);
+        } else {
+            // This fragment was retained from a previous instance; get it
+            // going now.
+            fragment.mInLayout = true;
+            fragment.mImmediateActivity = this;
+            // If this fragment is newly instantiated (either right now, or
+            // from last saved state), then give it the attributes to
+            // initialize itself.
+            if (!fragment.mRetaining) {
+                fragment.onInflate(attrs, fragment.mSavedFragmentState);
+            }
+            mFragments.moveToState(fragment);
+        }
+
+        if (fragment.mView == null) {
+            throw new IllegalStateException("Fragment " + fname
+                    + " did not create a view.");
+        }
+        if (id != 0) {
+            fragment.mView.setId(id);
+        }
+        if (fragment.mView.getTag() == null) {
+            fragment.mView.setTag(tag);
+        }
+        return fragment.mView;
+    }
+
+    /**
+     * Print the Activity's state into the given stream.  This gets invoked if
+     * you run "adb shell dumpsys activity <youractivityname>".
+     *
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param writer The PrintWriter to which you should dump your state.  This will be
+     * closed for you after you return.
+     * @param args additional arguments to the dump request.
+     */
+    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        mFragments.dump("", fd, writer, args);
+    }
+
+    /**
+     * Bit indicating that this activity is "immersive" and should not be
+     * interrupted by notifications if possible.
+     *
+     * This value is initially set by the manifest property
+     * <code>android:immersive</code> but may be changed at runtime by
+     * {@link #setImmersive}.
+     *
+     * @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
+     * @hide
+     */
+    public boolean isImmersive() {
+        try {
+            return ActivityManagerNative.getDefault().isImmersive(mToken);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Adjust the current immersive mode setting.
+     * 
+     * Note that changing this value will have no effect on the activity's
+     * {@link android.content.pm.ActivityInfo} structure; that is, if
+     * <code>android:immersive</code> is set to <code>true</code>
+     * in the application's manifest entry for this activity, the {@link
+     * android.content.pm.ActivityInfo#flags ActivityInfo.flags} member will
+     * always have its {@link android.content.pm.ActivityInfo#FLAG_IMMERSIVE
+     * FLAG_IMMERSIVE} bit set.
+     *
+     * @see #isImmersive
+     * @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
+     * @hide
+     */
+    public void setImmersive(boolean i) {
+        try {
+            ActivityManagerNative.getDefault().setImmersive(mToken, i);
+        } catch (RemoteException e) {
+            // pass
+        }
+    }
+
+    /**
+     * Start a context mode.
+     *
+     * @param callback Callback that will manage lifecycle events for this context mode
+     * @return The ContextMode that was started, or null if it was canceled
+     *
+     * @see ActionMode
+     */
+    public ActionMode startActionMode(ActionMode.Callback callback) {
+        return mWindow.getDecorView().startActionMode(callback);
+    }
+
+    public ActionMode onStartActionMode(ActionMode.Callback callback) {
+        initActionBar();
+        if (mActionBar != null) {
+            return mActionBar.startActionMode(callback);
+        }
+        return null;
+    }
+
     // ------------------ Internal API ------------------
     
     final void setParent(Activity parent) {
@@ -3738,28 +4201,30 @@
 
     final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token,
             Application application, Intent intent, ActivityInfo info, CharSequence title, 
-            Activity parent, String id, Object lastNonConfigurationInstance,
+            Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances,
             Configuration config) {
         attach(context, aThread, instr, token, 0, application, intent, info, title, parent, id,
-            lastNonConfigurationInstance, null, config);
+            lastNonConfigurationInstances, config);
     }
     
     final void attach(Context context, ActivityThread aThread,
             Instrumentation instr, IBinder token, int ident,
             Application application, Intent intent, ActivityInfo info,
             CharSequence title, Activity parent, String id,
-            Object lastNonConfigurationInstance,
-            HashMap<String,Object> lastNonConfigurationChildInstances,
+            NonConfigurationInstances lastNonConfigurationInstances,
             Configuration config) {
         attachBaseContext(context);
 
+        mFragments.attachActivity(this);
+        
         mWindow = PolicyManager.makeNewWindow(this);
         mWindow.setCallback(this);
+        mWindow.getLayoutInflater().setFactory2(this);
         if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
             mWindow.setSoftInputMode(info.softInputMode);
         }
         mUiThread = Thread.currentThread();
-
+        
         mMainThread = aThread;
         mInstrumentation = instr;
         mToken = token;
@@ -3771,10 +4236,10 @@
         mTitle = title;
         mParent = parent;
         mEmbeddedID = id;
-        mLastNonConfigurationInstance = lastNonConfigurationInstance;
-        mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;
+        mLastNonConfigurationInstances = lastNonConfigurationInstances;
 
-        mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
+        mWindow.setWindowManager(null, mToken, mComponent.flattenToString(),
+                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
         if (mParent != null) {
             mWindow.setContainer(mParent.getWindow());
         }
@@ -3786,23 +4251,41 @@
         return mParent != null ? mParent.getActivityToken() : mToken;
     }
 
+    final void performCreate(Bundle icicle) {
+        onCreate(icicle);
+        mFragments.dispatchActivityCreated();
+    }
+    
     final void performStart() {
+        mFragments.noteStateNotSaved();
         mCalled = false;
+        mFragments.execPendingActions();
         mInstrumentation.callActivityOnStart(this);
         if (!mCalled) {
             throw new SuperNotCalledException(
                 "Activity " + mComponent.toShortString() +
                 " did not call through to super.onStart()");
         }
+        mFragments.dispatchStart();
+        if (mAllLoaderManagers != null) {
+            for (int i=mAllLoaderManagers.size()-1; i>=0; i--) {
+                mAllLoaderManagers.valueAt(i).finishRetain();
+            }
+        }
     }
     
     final void performRestart() {
+        mFragments.noteStateNotSaved();
+
         synchronized (mManagedCursors) {
             final int N = mManagedCursors.size();
             for (int i=0; i<N; i++) {
                 ManagedCursor mc = mManagedCursors.get(i);
                 if (mc.mReleased || mc.mUpdated) {
-                    mc.mCursor.requery();
+                    if (!mc.mCursor.requery()) {
+                        throw new IllegalStateException(
+                                "trying to requery an already closed cursor");
+                    }
                     mc.mReleased = false;
                     mc.mUpdated = false;
                 }
@@ -3825,7 +4308,9 @@
     final void performResume() {
         performRestart();
         
-        mLastNonConfigurationInstance = null;
+        mFragments.execPendingActions();
+        
+        mLastNonConfigurationInstances = null;
         
         // First call onResume() -before- setting mResumed, so we don't
         // send out any status bar / menu notifications the client makes.
@@ -3840,6 +4325,10 @@
         // Now really resume, and install the current status bar and menu.
         mResumed = true;
         mCalled = false;
+        
+        mFragments.dispatchResume();
+        mFragments.execPendingActions();
+        
         onPostResume();
         if (!mCalled) {
             throw new SuperNotCalledException(
@@ -3849,6 +4338,7 @@
     }
 
     final void performPause() {
+        mFragments.dispatchPause();
         mCalled = false;
         onPause();
         if (!mCalled && getApplicationInfo().targetSdkVersion
@@ -3865,11 +4355,24 @@
     }
     
     final void performStop() {
+        if (mLoadersStarted) {
+            mLoadersStarted = false;
+            if (mLoaderManager != null) {
+                if (!mChangingConfigurations) {
+                    mLoaderManager.doStop();
+                } else {
+                    mLoaderManager.doRetain();
+                }
+            }
+        }
+        
         if (!mStopped) {
             if (mWindow != null) {
                 mWindow.closeAllPanels();
             }
 
+            mFragments.dispatchStop();
+            
             mCalled = false;
             mInstrumentation.callActivityOnStop(this);
             if (!mCalled) {
@@ -3894,6 +4397,15 @@
         mResumed = false;
     }
 
+    final void performDestroy() {
+        mWindow.destroy();
+        mFragments.dispatchDestroy();
+        onDestroy();
+        if (mLoaderManager != null) {
+            mLoaderManager.doDestroy();
+        }
+    }
+    
     final boolean isResumed() {
         return mResumed;
     }
@@ -3903,8 +4415,14 @@
         if (Config.LOGV) Log.v(
             TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
             + ", resCode=" + resultCode + ", data=" + data);
+        mFragments.noteStateNotSaved();
         if (who == null) {
             onActivityResult(requestCode, resultCode, data);
+        } else {
+            Fragment frag = mFragments.findFragmentByWho(who);
+            if (frag != null) {
+                frag.onActivityResult(requestCode, resultCode, data);
+            }
         }
     }
 }
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index d6231ea..8cc6428 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1250,6 +1250,32 @@
             return true;
         }
 
+        case IS_IMMERSIVE_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            boolean isit = isImmersive(token);
+            reply.writeNoException();
+            reply.writeInt(isit ? 1 : 0);
+            return true;
+        }
+
+        case SET_IMMERSIVE_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            boolean imm = data.readInt() == 1;
+            setImmersive(token, imm);
+            reply.writeNoException();
+            return true;
+        }
+        
+        case IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            boolean isit = isTopActivityImmersive();
+            reply.writeNoException();
+            reply.writeInt(isit ? 1 : 0);
+            return true;
+        }
+
         case CRASH_APPLICATION_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             int uid = data.readInt();
@@ -1304,6 +1330,31 @@
             return true;
         }
 
+        case CHECK_GRANT_URI_PERMISSION_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            int callingUid = data.readInt();
+            String targetPkg = data.readString();
+            Uri uri = Uri.CREATOR.createFromParcel(data);
+            int modeFlags = data.readInt();
+            int res = checkGrantUriPermission(callingUid, targetPkg, uri, modeFlags);
+            reply.writeNoException();
+            reply.writeInt(res);
+            return true;
+        }
+
+        case DUMP_HEAP_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String process = data.readString();
+            boolean managed = data.readInt() != 0;
+            String path = data.readString();
+            ParcelFileDescriptor fd = data.readInt() != 0
+                    ? data.readFileDescriptor() : null;
+            boolean res = dumpHeap(process, managed, path, fd);
+            reply.writeNoException();
+            reply.writeInt(res ? 1 : 0);
+            return true;
+        }
+
         }
         
         return super.onTransact(code, data, reply, flags);
@@ -2818,6 +2869,46 @@
         reply.recycle();
     }
     
+    public void setImmersive(IBinder token, boolean immersive)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        data.writeInt(immersive ? 1 : 0);
+        mRemote.transact(SET_IMMERSIVE_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    public boolean isImmersive(IBinder token)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(IS_IMMERSIVE_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean res = reply.readInt() == 1;
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
+    public boolean isTopActivityImmersive()
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean res = reply.readInt() == 1;
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
     public void crashApplication(int uid, int initialPid, String packageName,
             String message) throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -2895,6 +2986,45 @@
         data.recycle();
         reply.recycle();
     }
+
+    public int checkGrantUriPermission(int callingUid, String targetPkg,
+            Uri uri, int modeFlags) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeInt(callingUid);
+        data.writeString(targetPkg);
+        uri.writeToParcel(data, 0);
+        data.writeInt(modeFlags);
+        mRemote.transact(CHECK_GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int res = reply.readInt();
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
+    public boolean dumpHeap(String process, boolean managed,
+            String path, ParcelFileDescriptor fd) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(process);
+        data.writeInt(managed ? 1 : 0);
+        data.writeString(path);
+        if (fd != null) {
+            data.writeInt(1);
+            fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+        } else {
+            data.writeInt(0);
+        }
+        mRemote.transact(DUMP_HEAP_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean res = reply.readInt() != 0;
+        reply.recycle();
+        data.recycle();
+        return res;
+    }
     
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 78df780..174b6da 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -30,6 +30,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.AssetManager;
@@ -63,6 +64,7 @@
 import android.util.LogPrinter;
 import android.util.Slog;
 import android.view.Display;
+import android.view.HardwareRenderer;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewManager;
@@ -83,6 +85,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
+import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -115,12 +118,14 @@
  * {@hide}
  */
 public final class ActivityThread {
-    static final String TAG = "ActivityThread";
+    /** @hide */
+    public static final String TAG = "ActivityThread";
     private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
     private static final boolean DEBUG = false;
     static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
     static final boolean DEBUG_MESSAGES = false;
-    static final boolean DEBUG_BROADCAST = false;
+    /** @hide */
+    public static final boolean DEBUG_BROADCAST = false;
     private static final boolean DEBUG_RESULTS = false;
     private static final boolean DEBUG_BACKUP = false;
     private static final boolean DEBUG_CONFIGURATION = false;
@@ -199,8 +204,7 @@
         Window window;
         Activity parent;
         String embeddedID;
-        Object lastNonConfigurationInstance;
-        HashMap<String,Object> lastNonConfigurationChildInstances;
+        Activity.NonConfigurationInstances lastNonConfigurationInstances;
         boolean paused;
         boolean stopped;
         boolean hideForNow;
@@ -261,18 +265,20 @@
         }
     }
 
-    private static final class ReceiverData {
+    private static final class ReceiverData extends BroadcastReceiver.PendingResult {
+        public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
+                boolean ordered, boolean sticky, IBinder token) {
+            super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, token);
+            this.intent = intent;
+        }
+        
         Intent intent;
         ActivityInfo info;
-        int resultCode;
-        String resultData;
-        Bundle resultExtras;
-        boolean sync;
-        boolean resultAbort;
         public String toString() {
             return "ReceiverData{intent=" + intent + " packageName=" +
-            info.packageName + " resultCode=" + resultCode
-            + " resultData=" + resultData + " resultExtras=" + resultExtras + "}";
+                    info.packageName + " resultCode=" + getResultCode()
+                    + " resultData=" + getResultData() + " resultExtras="
+                    + getResultExtras(false) + "}";
         }
     }
 
@@ -335,9 +341,9 @@
         }
     }
 
-    private static final class DumpServiceInfo {
+    private static final class DumpComponentInfo {
         FileDescriptor fd;
-        IBinder service;
+        IBinder token;
         String[] args;
         boolean dumped;
     }
@@ -361,12 +367,17 @@
         ParcelFileDescriptor fd;
     }
 
+    private static final class DumpHeapData {
+        String path;
+        ParcelFileDescriptor fd;
+    }
+
     private final class ApplicationThread extends ApplicationThreadNative {
         private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
         private static final String ONE_COUNT_COLUMN = "%17s %8d";
         private static final String TWO_COUNT_COLUMNS = "%17s %8d %17s %8d";
         private static final String TWO_COUNT_COLUMNS_DB = "%20s %8d %20s %8d";
-        private static final String DB_INFO_FORMAT = "  %8d %8d %14d  %s";
+        private static final String DB_INFO_FORMAT = "  %8s %8s %14s %14s  %s";
 
         // Formatting for checkin service - update version if row format changes
         private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1;
@@ -460,15 +471,9 @@
 
         public final void scheduleReceiver(Intent intent, ActivityInfo info,
                 int resultCode, String data, Bundle extras, boolean sync) {
-            ReceiverData r = new ReceiverData();
-
-            r.intent = intent;
+            ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
+                    sync, false, mAppThread.asBinder());
             r.info = info;
-            r.resultCode = resultCode;
-            r.resultData = data;
-            r.resultExtras = extras;
-            r.sync = sync;
-
             queueOrSendMessage(H.RECEIVER, r);
         }
 
@@ -581,15 +586,20 @@
             TimeZone.setDefault(null);
         }
 
+        public void clearDnsCache() {
+            // a non-standard API to get this to libcore
+            InetAddress.clearDnsCache();
+        }
+
         public void processInBackground() {
             mH.removeMessages(H.GC_WHEN_IDLE);
             mH.sendMessage(mH.obtainMessage(H.GC_WHEN_IDLE));
         }
 
         public void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args) {
-            DumpServiceInfo data = new DumpServiceInfo();
+            DumpComponentInfo data = new DumpComponentInfo();
             data.fd = fd;
-            data.service = servicetoken;
+            data.token = servicetoken;
             data.args = args;
             data.dumped = false;
             queueOrSendMessage(H.DUMP_SERVICE, data);
@@ -629,6 +639,13 @@
             queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0);
         }
 
+        public void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) {
+            DumpHeapData dhd = new DumpHeapData();
+            dhd.path = path;
+            dhd.fd = fd;
+            queueOrSendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0);
+        }
+
         public void setSchedulingGroup(int group) {
             // Note: do this immediately, since going into the foreground
             // should happen regardless of what pending work we have to do
@@ -652,6 +669,25 @@
         public void scheduleCrash(String msg) {
             queueOrSendMessage(H.SCHEDULE_CRASH, msg);
         }
+
+        public void dumpActivity(FileDescriptor fd, IBinder activitytoken, String[] args) {
+            DumpComponentInfo data = new DumpComponentInfo();
+            data.fd = fd;
+            data.token = activitytoken;
+            data.args = args;
+            data.dumped = false;
+            queueOrSendMessage(H.DUMP_ACTIVITY, data);
+            synchronized (data) {
+                while (!data.dumped) {
+                    try {
+                        data.wait();
+                    } catch (InterruptedException e) {
+                        // no need to do anything here, we will keep waiting until
+                        // dumped is set
+                    }
+                }
+            }
+        }
         
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -677,14 +713,14 @@
             long dalvikAllocated = dalvikMax - dalvikFree;
             long viewInstanceCount = ViewDebug.getViewInstanceCount();
             long viewRootInstanceCount = ViewDebug.getViewRootInstanceCount();
-            long appContextInstanceCount = ContextImpl.getInstanceCount();
-            long activityInstanceCount = Activity.getInstanceCount();
+            long appContextInstanceCount = Debug.countInstancesOfClass(ContextImpl.class);
+            long activityInstanceCount = Debug.countInstancesOfClass(Activity.class);
             int globalAssetCount = AssetManager.getGlobalAssetCount();
             int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount();
             int binderLocalObjectCount = Debug.getBinderLocalObjectCount();
             int binderProxyObjectCount = Debug.getBinderProxyObjectCount();
             int binderDeathObjectCount = Debug.getBinderDeathObjectCount();
-            int openSslSocketCount = OpenSSLSocketImpl.getInstanceCount();
+            long openSslSocketCount = Debug.countInstancesOfClass(OpenSSLSocketImpl.class);
             long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024;
             SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo();
 
@@ -766,7 +802,7 @@
                 for (int i = 0; i < stats.dbStats.size(); i++) {
                     DbStats dbStats = stats.dbStats.get(i);
                     printRow(pw, DB_INFO_FORMAT, dbStats.pageSize, dbStats.dbSize,
-                            dbStats.lookaside, dbStats.dbName);
+                            dbStats.lookaside, dbStats.cache, dbStats.dbName);
                     pw.print(',');
                 }
 
@@ -817,11 +853,15 @@
             int N = stats.dbStats.size();
             if (N > 0) {
                 pw.println(" DATABASES");
-                printRow(pw, "  %8s %8s %14s  %s", "pgsz", "dbsz", "Lookaside(b)", "Dbname");
+                printRow(pw, "  %8s %8s %14s %14s  %s", "pgsz", "dbsz", "Lookaside(b)", "cache",
+                        "Dbname");
                 for (int i = 0; i < N; i++) {
                     DbStats dbStats = stats.dbStats.get(i);
-                    printRow(pw, DB_INFO_FORMAT, dbStats.pageSize, dbStats.dbSize,
-                            dbStats.lookaside, dbStats.dbName);
+                    printRow(pw, DB_INFO_FORMAT,
+                            (dbStats.pageSize > 0) ? String.valueOf(dbStats.pageSize) : " ",
+                            (dbStats.dbSize > 0) ? String.valueOf(dbStats.dbSize) : " ",
+                            (dbStats.lookaside > 0) ? String.valueOf(dbStats.lookaside) : " ",
+                            dbStats.cache, dbStats.dbName);
                 }
             }
 
@@ -875,6 +915,8 @@
         public static final int ENABLE_JIT              = 132;
         public static final int DISPATCH_PACKAGE_BROADCAST = 133;
         public static final int SCHEDULE_CRASH          = 134;
+        public static final int DUMP_HEAP               = 135;
+        public static final int DUMP_ACTIVITY           = 136;
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
                 switch (code) {
@@ -913,6 +955,8 @@
                     case ENABLE_JIT: return "ENABLE_JIT";
                     case DISPATCH_PACKAGE_BROADCAST: return "DISPATCH_PACKAGE_BROADCAST";
                     case SCHEDULE_CRASH: return "SCHEDULE_CRASH";
+                    case DUMP_HEAP: return "DUMP_HEAP";
+                    case DUMP_ACTIVITY: return "DUMP_ACTIVITY";
                 }
             }
             return "(unknown)";
@@ -1008,7 +1052,7 @@
                     scheduleGcIdler();
                     break;
                 case DUMP_SERVICE:
-                    handleDumpService((DumpServiceInfo)msg.obj);
+                    handleDumpService((DumpComponentInfo)msg.obj);
                     break;
                 case LOW_MEMORY:
                     handleLowMemory();
@@ -1039,14 +1083,39 @@
                     break;
                 case SCHEDULE_CRASH:
                     throw new RemoteServiceException((String)msg.obj);
+                case DUMP_HEAP:
+                    handleDumpHeap(msg.arg1 != 0, (DumpHeapData)msg.obj);
+                    break;
+                case DUMP_ACTIVITY:
+                    handleDumpActivity((DumpComponentInfo)msg.obj);
+                    break;
             }
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
         }
 
         void maybeSnapshot() {
             if (mBoundApplication != null) {
-                SamplingProfilerIntegration.writeSnapshot(
-                        mBoundApplication.processName);
+                // convert the *private* ActivityThread.PackageInfo to *public* known
+                // android.content.pm.PackageInfo
+                String packageName = mBoundApplication.info.mPackageName;
+                android.content.pm.PackageInfo packageInfo = null;
+                try {
+                    Context context = getSystemContext();
+                    if(context == null) {
+                        Log.e(TAG, "cannot get a valid context");
+                        return;
+                    }
+                    PackageManager pm = context.getPackageManager();
+                    if(pm == null) {
+                        Log.e(TAG, "cannot get a valid PackageManager");
+                        return;
+                    }
+                    packageInfo = pm.getPackageInfo(
+                            packageName, PackageManager.GET_ACTIVITIES);
+                } catch (NameNotFoundException e) {
+                    Log.e(TAG, "cannot get package info for " + packageName, e);
+                }
+                SamplingProfilerIntegration.writeSnapshot(mBoundApplication.processName, packageInfo);
             }
         }
     }
@@ -1437,7 +1506,7 @@
 
     public final Activity startActivityNow(Activity parent, String id,
         Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
-        Object lastNonConfigurationInstance) {
+        Activity.NonConfigurationInstances lastNonConfigurationInstances) {
         ActivityClientRecord r = new ActivityClientRecord();
             r.token = token;
             r.ident = 0;
@@ -1446,7 +1515,7 @@
             r.parent = parent;
             r.embeddedID = id;
             r.activityInfo = activityInfo;
-            r.lastNonConfigurationInstance = lastNonConfigurationInstance;
+            r.lastNonConfigurationInstances = lastNonConfigurationInstances;
         if (localLOGV) {
             ComponentName compname = intent.getComponent();
             String name;
@@ -1568,14 +1637,12 @@
                         + r.activityInfo.name + " with config " + config);
                 activity.attach(appContext, this, getInstrumentation(), r.token,
                         r.ident, app, r.intent, r.activityInfo, title, r.parent,
-                        r.embeddedID, r.lastNonConfigurationInstance,
-                        r.lastNonConfigurationChildInstances, config);
+                        r.embeddedID, r.lastNonConfigurationInstances, config);
 
                 if (customIntent != null) {
                     activity.mIntent = customIntent;
                 }
-                r.lastNonConfigurationInstance = null;
-                r.lastNonConfigurationChildInstances = null;
+                r.lastNonConfigurationInstances = null;
                 activity.mStartedActivity = false;
                 int theme = r.activityInfo.getThemeResource();
                 if (theme != 0) {
@@ -1694,6 +1761,7 @@
         for (int i=0; i<N; i++) {
             Intent intent = intents.get(i);
             intent.setExtrasClassLoader(r.activity.getClassLoader());
+            r.activity.mFragments.noteStateNotSaved();
             mInstrumentation.callActivityOnNewIntent(r.activity, intent);
         }
     }
@@ -1704,11 +1772,13 @@
         if (r != null) {
             final boolean resumed = !r.paused;
             if (resumed) {
+                r.activity.mTemporaryPause = true;
                 mInstrumentation.callActivityOnPause(r.activity);
             }
             deliverNewIntents(r, intents);
             if (resumed) {
                 mInstrumentation.callActivityOnResume(r.activity);
+                r.activity.mTemporaryPause = false;
             }
         }
     }
@@ -1717,6 +1787,17 @@
         performNewIntents(data.token, data.intents);
     }
 
+    private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
+
+    /**
+     * Return the Intent that's currently being handled by a
+     * BroadcastReceiver on this thread, or null if none.
+     * @hide
+     */
+    public static Intent getIntentBeingBroadcast() {
+        return sCurrentBroadcastIntent.get();
+    }
+
     private final void handleReceiver(ReceiverData data) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
@@ -1733,18 +1814,12 @@
         try {
             java.lang.ClassLoader cl = packageInfo.getClassLoader();
             data.intent.setExtrasClassLoader(cl);
-            if (data.resultExtras != null) {
-                data.resultExtras.setClassLoader(cl);
-            }
+            data.setExtrasClassLoader(cl);
             receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
         } catch (Exception e) {
-            try {
-                if (DEBUG_BROADCAST) Slog.i(TAG,
-                        "Finishing failed broadcast to " + data.intent.getComponent());
-                mgr.finishReceiver(mAppThread.asBinder(), data.resultCode,
-                                   data.resultData, data.resultExtras, data.resultAbort);
-            } catch (RemoteException ex) {
-            }
+            if (DEBUG_BROADCAST) Slog.i(TAG,
+                    "Finishing failed broadcast to " + data.intent.getComponent());
+            data.sendFinished(mgr);
             throw new RuntimeException(
                 "Unable to instantiate receiver " + component
                 + ": " + e.toString(), e);
@@ -1762,43 +1837,25 @@
                 + ", dir=" + packageInfo.getAppDir());
 
             ContextImpl context = (ContextImpl)app.getBaseContext();
-            receiver.setOrderedHint(true);
-            receiver.setResult(data.resultCode, data.resultData,
-                data.resultExtras);
-            receiver.setOrderedHint(data.sync);
+            sCurrentBroadcastIntent.set(data.intent);
+            receiver.setPendingResult(data);
             receiver.onReceive(context.getReceiverRestrictedContext(),
                     data.intent);
         } catch (Exception e) {
-            try {
-                if (DEBUG_BROADCAST) Slog.i(TAG,
-                        "Finishing failed broadcast to " + data.intent.getComponent());
-                mgr.finishReceiver(mAppThread.asBinder(), data.resultCode,
-                    data.resultData, data.resultExtras, data.resultAbort);
-            } catch (RemoteException ex) {
-            }
+            if (DEBUG_BROADCAST) Slog.i(TAG,
+                    "Finishing failed broadcast to " + data.intent.getComponent());
+            data.sendFinished(mgr);
             if (!mInstrumentation.onException(receiver, e)) {
                 throw new RuntimeException(
                     "Unable to start receiver " + component
                     + ": " + e.toString(), e);
             }
+        } finally {
+            sCurrentBroadcastIntent.set(null);
         }
 
-        QueuedWork.waitToFinish();
-
-        try {
-            if (data.sync) {
-                if (DEBUG_BROADCAST) Slog.i(TAG,
-                        "Finishing ordered broadcast to " + data.intent.getComponent());
-                mgr.finishReceiver(
-                    mAppThread.asBinder(), receiver.getResultCode(),
-                    receiver.getResultData(), receiver.getResultExtras(false),
-                        receiver.getAbortBroadcast());
-            } else {
-                if (DEBUG_BROADCAST) Slog.i(TAG,
-                        "Finishing broadcast to " + data.intent.getComponent());
-                mgr.finishReceiver(mAppThread.asBinder(), 0, null, null, false);
-            }
-        } catch (RemoteException ex) {
+        if (receiver.getPendingResult() != null) {
+            data.finish();
         }
     }
 
@@ -1988,9 +2045,9 @@
         }
     }
 
-    private void handleDumpService(DumpServiceInfo info) {
+    private void handleDumpService(DumpComponentInfo info) {
         try {
-            Service s = mServices.get(info.service);
+            Service s = mServices.get(info.token);
             if (s != null) {
                 PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd));
                 s.dump(info.fd, pw, info.args);
@@ -2004,6 +2061,22 @@
         }
     }
 
+    private void handleDumpActivity(DumpComponentInfo info) {
+        try {
+            ActivityClientRecord r = mActivities.get(info.token);
+            if (r != null && r.activity != null) {
+                PrintWriter pw = new PrintWriter(new FileOutputStream(info.fd));
+                r.activity.dump(info.fd, pw, info.args);
+                pw.close();
+            }
+        } finally {
+            synchronized (info) {
+                info.dumped = true;
+                info.notifyAll();
+            }
+        }
+    }
+
     private final void handleServiceArgs(ServiceArgsData data) {
         Service s = mServices.get(data.token);
         if (s != null) {
@@ -2518,6 +2591,7 @@
                 try {
                     // Now we are idle.
                     r.activity.mCalled = false;
+                    r.activity.mTemporaryPause = true;
                     mInstrumentation.callActivityOnPause(r.activity);
                     if (!r.activity.mCalled) {
                         throw new SuperNotCalledException(
@@ -2538,6 +2612,7 @@
             deliverResults(r, res.results);
             if (resumed) {
                 mInstrumentation.callActivityOnResume(r.activity);
+                r.activity.mTemporaryPause = false;
             }
         }
     }
@@ -2555,6 +2630,9 @@
             if (finishing) {
                 r.activity.mFinished = true;
             }
+            if (getNonConfigInstance) {
+                r.activity.mChangingConfigurations = true;
+            }
             if (!r.paused) {
                 try {
                     r.activity.mCalled = false;
@@ -2595,8 +2673,8 @@
             }
             if (getNonConfigInstance) {
                 try {
-                    r.lastNonConfigurationInstance
-                            = r.activity.onRetainNonConfigurationInstance();
+                    r.lastNonConfigurationInstances
+                            = r.activity.retainNonConfigurationInstances();
                 } catch (Exception e) {
                     if (!mInstrumentation.onException(r.activity, e)) {
                         throw new RuntimeException(
@@ -2605,22 +2683,10 @@
                                 + ": " + e.toString(), e);
                     }
                 }
-                try {
-                    r.lastNonConfigurationChildInstances
-                            = r.activity.onRetainNonConfigurationChildInstances();
-                } catch (Exception e) {
-                    if (!mInstrumentation.onException(r.activity, e)) {
-                        throw new RuntimeException(
-                                "Unable to retain child activities "
-                                + safeToComponentShortString(r.intent)
-                                + ": " + e.toString(), e);
-                    }
-                }
-
             }
             try {
                 r.activity.mCalled = false;
-                r.activity.onDestroy();
+                mInstrumentation.callActivityOnDestroy(r.activity);
                 if (!r.activity.mCalled) {
                     throw new SuperNotCalledException(
                         "Activity " + safeToComponentShortString(r.intent) +
@@ -3032,6 +3098,25 @@
         }
     }
 
+    final void handleDumpHeap(boolean managed, DumpHeapData dhd) {
+        if (managed) {
+            try {
+                Debug.dumpHprofData(dhd.path, dhd.fd.getFileDescriptor());
+            } catch (IOException e) {
+                Slog.w(TAG, "Managed heap dump failed on path " + dhd.path
+                        + " -- can the process access this path?");
+            } finally {
+                try {
+                    dhd.fd.close();
+                } catch (IOException e) {
+                    Slog.w(TAG, "Failure closing profile fd", e);
+                }
+            }
+        } else {
+            Debug.dumpNativeHeap(dhd.fd.getFileDescriptor());
+        }
+    }
+
     final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
         boolean hasPkgInfo = false;
         if (packages != null) {
@@ -3123,6 +3208,17 @@
         }
 
         /**
+         * For apps targetting SDK Honeycomb or later, we don't allow
+         * network usage on the main event loop / UI thread.
+         *
+         * Note to those grepping:  this is what ultimately throws
+         * NetworkOnMainThreadException ...
+         */
+        if (data.appInfo.targetSdkVersion > 9) {
+            StrictMode.enableDeathOnNetwork();
+        }
+
+        /**
          * Switch this process to density compatibility mode if needed.
          */
         if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
@@ -3614,6 +3710,7 @@
     }
 
     public static final ActivityThread systemMain() {
+        HardwareRenderer.disable();
         ActivityThread thread = new ActivityThread();
         thread.attach(true);
         return thread;
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 61a8fc3..ac3ca55 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -16,13 +16,19 @@
 
 package android.app;
 
+import com.android.internal.app.AlertController;
+
 import android.content.Context;
 import android.content.DialogInterface;
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Message;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.AdapterView;
@@ -30,8 +36,6 @@
 import android.widget.ListAdapter;
 import android.widget.ListView;
 
-import com.android.internal.app.AlertController;
-
 /**
  * A subclass of Dialog that can display one, two or three buttons. If you only want to
  * display a String in this dialog box, use the setMessage() method.  If you
@@ -56,21 +60,46 @@
     private AlertController mAlert;
 
     protected AlertDialog(Context context) {
-        this(context, com.android.internal.R.style.Theme_Dialog_Alert);
+        this(context, getDefaultDialogTheme(context));
     }
 
     protected AlertDialog(Context context, int theme) {
-        super(context, theme);
+        super(context, theme == 0 ? getDefaultDialogTheme(context) : theme);
         mAlert = new AlertController(context, this, getWindow());
     }
 
     protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
-        super(context, com.android.internal.R.style.Theme_Dialog_Alert);
+        super(context, getDefaultDialogTheme(context));
         setCancelable(cancelable);
         setOnCancelListener(cancelListener);
         mAlert = new AlertController(context, this, getWindow());
     }
 
+    private static int getDefaultDialogTheme(Context context) {
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme,
+                outValue, true);
+        return outValue.resourceId;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (mCancelable) {
+            final View decor = mWindow.getDecorView();
+            final int width = decor.getWidth();
+            final int height = decor.getHeight();
+            final float x = ev.getX();
+            final float y = ev.getY();
+
+            if (mCancelable && (x < 0 || x > width || y < 0 || y > height)) {
+                cancel();
+                return true;
+            }
+        }
+
+        return super.onTouchEvent(ev);
+    }
+
     /**
      * Gets one of the buttons used in the dialog.
      * <p>
@@ -271,7 +300,7 @@
          * Constructor using a context for this builder and the {@link AlertDialog} it creates.
          */
         public Builder(Context context) {
-            this(context, com.android.internal.R.style.Theme_Dialog_Alert);
+            this(context, getDefaultDialogTheme(context));
         }
 
         /**
@@ -279,11 +308,23 @@
          * the {@link AlertDialog} it creates.
          */
         public Builder(Context context, int theme) {
-            P = new AlertController.AlertParams(context);
+            P = new AlertController.AlertParams(new ContextThemeWrapper(context, theme));
             mTheme = theme;
         }
         
         /**
+         * Returns a {@link Context} with the appropriate theme for dialogs created by this Builder.
+         * Applications should use this Context for obtaining LayoutInflaters for inflating views
+         * that will be used in the resulting dialogs, as it will cause views to be inflated with
+         * the correct theme.
+         *
+         * @return A Context for built Dialogs.
+         */
+        public Context getContext() {
+            return P.mContext;
+        }
+
+        /**
          * Set the title using the given resource id.
          *
          * @return This Builder object to allow for chaining of calls to set methods
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 2382596..1e012eb 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -184,7 +184,7 @@
         candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
         return getErrorReportReceiver(pm, packageName, candidate);
     }
-    
+
     /**
      * Return activity in receiverPackage that handles ACTION_APP_ERROR.
      *
@@ -581,6 +581,9 @@
             case TYPE_BATTERY:
                 batteryInfo.dump(pw, prefix);
                 break;
+            case TYPE_RUNNING_SERVICE:
+                runningServiceInfo.dump(pw, prefix);
+                break;
         }
     }
 }
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 1c20062..b19fb59 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -291,6 +291,12 @@
             return true;
         }
 
+        case CLEAR_DNS_CACHE_TRANSACTION: {
+            data.enforceInterface(IApplicationThread.descriptor);
+            clearDnsCache();
+            return true;
+        }
+
         case PROCESS_IN_BACKGROUND_TRANSACTION: {
             data.enforceInterface(IApplicationThread.descriptor);
             processInBackground();
@@ -403,6 +409,32 @@
             scheduleCrash(msg);
             return true;
         }
+
+        case DUMP_HEAP_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            boolean managed = data.readInt() != 0;
+            String path = data.readString();
+            ParcelFileDescriptor fd = data.readInt() != 0
+                    ? data.readFileDescriptor() : null;
+            dumpHeap(managed, path, fd);
+            return true;
+        }
+
+        case DUMP_ACTIVITY_TRANSACTION: {
+            data.enforceInterface(IApplicationThread.descriptor);
+            ParcelFileDescriptor fd = data.readFileDescriptor();
+            final IBinder activity = data.readStrongBinder();
+            final String[] args = data.readStringArray();
+            if (fd != null) {
+                dumpActivity(fd.getFileDescriptor(), activity, args);
+                try {
+                    fd.close();
+                } catch (IOException e) {
+                }
+            }
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -718,6 +750,14 @@
         data.recycle();
     }
 
+    public void clearDnsCache() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        mRemote.transact(CLEAR_DNS_CACHE_TRANSACTION, data, null,
+                IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
+
     public void processInBackground() throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -829,5 +869,32 @@
         data.recycle();
         
     }
-}
 
+    public void dumpHeap(boolean managed, String path,
+            ParcelFileDescriptor fd) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeInt(managed ? 1 : 0);
+        data.writeString(path);
+        if (fd != null) {
+            data.writeInt(1);
+            fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+        } else {
+            data.writeInt(0);
+        }
+        mRemote.transact(DUMP_HEAP_TRANSACTION, data, null,
+                IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
+
+    public void dumpActivity(FileDescriptor fd, IBinder token, String[] args)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeFileDescriptor(fd);
+        data.writeStrongBinder(token);
+        data.writeStringArray(args);
+        mRemote.transact(DUMP_ACTIVITY_TRANSACTION, data, null, 0);
+        data.recycle();
+    }
+}
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
new file mode 100644
index 0000000..c75777d
--- /dev/null
+++ b/core/java/android/app/BackStackRecord.java
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+final class BackStackState implements Parcelable {
+    final int[] mOps;
+    final int mTransition;
+    final int mTransitionStyle;
+    final String mName;
+    final int mIndex;
+    final int mBreadCrumbTitleRes;
+    final CharSequence mBreadCrumbTitleText;
+    final int mBreadCrumbShortTitleRes;
+    final CharSequence mBreadCrumbShortTitleText;
+
+    public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
+        int numRemoved = 0;
+        BackStackRecord.Op op = bse.mHead;
+        while (op != null) {
+            if (op.removed != null) numRemoved += op.removed.size();
+            op = op.next;
+        }
+        mOps = new int[bse.mNumOp*5 + numRemoved];
+
+        if (!bse.mAddToBackStack) {
+            throw new IllegalStateException("Not on back stack");
+        }
+
+        op = bse.mHead;
+        int pos = 0;
+        while (op != null) {
+            mOps[pos++] = op.cmd;
+            mOps[pos++] = op.fragment.mIndex;
+            mOps[pos++] = op.enterAnim;
+            mOps[pos++] = op.exitAnim;
+            if (op.removed != null) {
+                final int N = op.removed.size();
+                mOps[pos++] = N;
+                for (int i=0; i<N; i++) {
+                    mOps[pos++] = op.removed.get(i).mIndex;
+                }
+            } else {
+                mOps[pos++] = 0;
+            }
+            op = op.next;
+        }
+        mTransition = bse.mTransition;
+        mTransitionStyle = bse.mTransitionStyle;
+        mName = bse.mName;
+        mIndex = bse.mIndex;
+        mBreadCrumbTitleRes = bse.mBreadCrumbTitleRes;
+        mBreadCrumbTitleText = bse.mBreadCrumbTitleText;
+        mBreadCrumbShortTitleRes = bse.mBreadCrumbShortTitleRes;
+        mBreadCrumbShortTitleText = bse.mBreadCrumbShortTitleText;
+    }
+
+    public BackStackState(Parcel in) {
+        mOps = in.createIntArray();
+        mTransition = in.readInt();
+        mTransitionStyle = in.readInt();
+        mName = in.readString();
+        mIndex = in.readInt();
+        mBreadCrumbTitleRes = in.readInt();
+        mBreadCrumbTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mBreadCrumbShortTitleRes = in.readInt();
+        mBreadCrumbShortTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+    }
+
+    public BackStackRecord instantiate(FragmentManagerImpl fm) {
+        BackStackRecord bse = new BackStackRecord(fm);
+        int pos = 0;
+        while (pos < mOps.length) {
+            BackStackRecord.Op op = new BackStackRecord.Op();
+            op.cmd = mOps[pos++];
+            if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
+                    "BSE " + bse + " set base fragment #" + mOps[pos]);
+            Fragment f = fm.mActive.get(mOps[pos++]);
+            op.fragment = f;
+            op.enterAnim = mOps[pos++];
+            op.exitAnim = mOps[pos++];
+            final int N = mOps[pos++];
+            if (N > 0) {
+                op.removed = new ArrayList<Fragment>(N);
+                for (int i=0; i<N; i++) {
+                    if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
+                            "BSE " + bse + " set remove fragment #" + mOps[pos]);
+                    Fragment r = fm.mActive.get(mOps[pos++]);
+                    op.removed.add(r);
+                }
+            }
+            bse.addOp(op);
+        }
+        bse.mTransition = mTransition;
+        bse.mTransitionStyle = mTransitionStyle;
+        bse.mName = mName;
+        bse.mIndex = mIndex;
+        bse.mAddToBackStack = true;
+        bse.mBreadCrumbTitleRes = mBreadCrumbTitleRes;
+        bse.mBreadCrumbTitleText = mBreadCrumbTitleText;
+        bse.mBreadCrumbShortTitleRes = mBreadCrumbShortTitleRes;
+        bse.mBreadCrumbShortTitleText = mBreadCrumbShortTitleText;
+        bse.bumpBackStackNesting(1);
+        return bse;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeIntArray(mOps);
+        dest.writeInt(mTransition);
+        dest.writeInt(mTransitionStyle);
+        dest.writeString(mName);
+        dest.writeInt(mIndex);
+        dest.writeInt(mBreadCrumbTitleRes);
+        TextUtils.writeToParcel(mBreadCrumbTitleText, dest, 0);
+        dest.writeInt(mBreadCrumbShortTitleRes);
+        TextUtils.writeToParcel(mBreadCrumbShortTitleText, dest, 0);
+    }
+
+    public static final Parcelable.Creator<BackStackState> CREATOR
+            = new Parcelable.Creator<BackStackState>() {
+        public BackStackState createFromParcel(Parcel in) {
+            return new BackStackState(in);
+        }
+
+        public BackStackState[] newArray(int size) {
+            return new BackStackState[size];
+        }
+    };
+}
+
+/**
+ * @hide Entry of an operation on the fragment back stack.
+ */
+final class BackStackRecord implements FragmentTransaction,
+        FragmentManager.BackStackEntry, Runnable {
+    static final String TAG = "BackStackEntry";
+
+    final FragmentManagerImpl mManager;
+
+    static final int OP_NULL = 0;
+    static final int OP_ADD = 1;
+    static final int OP_REPLACE = 2;
+    static final int OP_REMOVE = 3;
+    static final int OP_HIDE = 4;
+    static final int OP_SHOW = 5;
+
+    static final class Op {
+        Op next;
+        Op prev;
+        int cmd;
+        Fragment fragment;
+        int enterAnim;
+        int exitAnim;
+        ArrayList<Fragment> removed;
+    }
+
+    Op mHead;
+    Op mTail;
+    int mNumOp;
+    int mEnterAnim;
+    int mExitAnim;
+    int mTransition;
+    int mTransitionStyle;
+    boolean mAddToBackStack;
+    boolean mAllowAddToBackStack = true;
+    String mName;
+    boolean mCommitted;
+    int mIndex;
+
+    int mBreadCrumbTitleRes;
+    CharSequence mBreadCrumbTitleText;
+    int mBreadCrumbShortTitleRes;
+    CharSequence mBreadCrumbShortTitleText;
+
+    public BackStackRecord(FragmentManagerImpl manager) {
+        mManager = manager;
+    }
+
+    public int getId() {
+        return mIndex;
+    }
+
+    public CharSequence getBreadCrumbTitle() {
+        if (mBreadCrumbTitleRes != 0) {
+            return mManager.mActivity.getText(mBreadCrumbTitleRes);
+        }
+        return mBreadCrumbTitleText;
+    }
+
+    public CharSequence getBreadCrumbShortTitle() {
+        if (mBreadCrumbShortTitleRes != 0) {
+            return mManager.mActivity.getText(mBreadCrumbShortTitleRes);
+        }
+        return mBreadCrumbShortTitleText;
+    }
+
+    void addOp(Op op) {
+        if (mHead == null) {
+            mHead = mTail = op;
+        } else {
+            op.prev = mTail;
+            mTail.next = op;
+            mTail = op;
+        }
+        op.enterAnim = mEnterAnim;
+        op.exitAnim = mExitAnim;
+        mNumOp++;
+    }
+
+    public FragmentTransaction add(Fragment fragment, String tag) {
+        doAddOp(0, fragment, tag, OP_ADD);
+        return this;
+    }
+
+    public FragmentTransaction add(int containerViewId, Fragment fragment) {
+        doAddOp(containerViewId, fragment, null, OP_ADD);
+        return this;
+    }
+
+    public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
+        doAddOp(containerViewId, fragment, tag, OP_ADD);
+        return this;
+    }
+
+    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
+        if (fragment.mImmediateActivity != null) {
+            throw new IllegalStateException("Fragment already added: " + fragment);
+        }
+        fragment.mImmediateActivity = mManager.mActivity;
+        fragment.mFragmentManager = mManager;
+
+        if (tag != null) {
+            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
+                throw new IllegalStateException("Can't change tag of fragment "
+                        + fragment + ": was " + fragment.mTag
+                        + " now " + tag);
+            }
+            fragment.mTag = tag;
+        }
+
+        if (containerViewId != 0) {
+            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
+                throw new IllegalStateException("Can't change container ID of fragment "
+                        + fragment + ": was " + fragment.mFragmentId
+                        + " now " + containerViewId);
+            }
+            fragment.mContainerId = fragment.mFragmentId = containerViewId;
+        }
+
+        Op op = new Op();
+        op.cmd = opcmd;
+        op.fragment = fragment;
+        addOp(op);
+    }
+
+    public FragmentTransaction replace(int containerViewId, Fragment fragment) {
+        return replace(containerViewId, fragment, null);
+    }
+
+    public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
+        if (containerViewId == 0) {
+            throw new IllegalArgumentException("Must use non-zero containerViewId");
+        }
+
+        doAddOp(containerViewId, fragment, tag, OP_REPLACE);
+        return this;
+    }
+
+    public FragmentTransaction remove(Fragment fragment) {
+        if (fragment.mImmediateActivity == null) {
+            throw new IllegalStateException("Fragment not added: " + fragment);
+        }
+        fragment.mImmediateActivity = null;
+
+        Op op = new Op();
+        op.cmd = OP_REMOVE;
+        op.fragment = fragment;
+        addOp(op);
+
+        return this;
+    }
+
+    public FragmentTransaction hide(Fragment fragment) {
+        if (fragment.mImmediateActivity == null) {
+            throw new IllegalStateException("Fragment not added: " + fragment);
+        }
+
+        Op op = new Op();
+        op.cmd = OP_HIDE;
+        op.fragment = fragment;
+        addOp(op);
+
+        return this;
+    }
+
+    public FragmentTransaction show(Fragment fragment) {
+        if (fragment.mImmediateActivity == null) {
+            throw new IllegalStateException("Fragment not added: " + fragment);
+        }
+
+        Op op = new Op();
+        op.cmd = OP_SHOW;
+        op.fragment = fragment;
+        addOp(op);
+
+        return this;
+    }
+
+    public FragmentTransaction setCustomAnimations(int enter, int exit) {
+        mEnterAnim = enter;
+        mExitAnim = exit;
+        return this;
+    }
+
+    public FragmentTransaction setTransition(int transition) {
+        mTransition = transition;
+        return this;
+    }
+
+    public FragmentTransaction setTransitionStyle(int styleRes) {
+        mTransitionStyle = styleRes;
+        return this;
+    }
+
+    public FragmentTransaction addToBackStack(String name) {
+        if (!mAllowAddToBackStack) {
+            throw new IllegalStateException(
+                    "This FragmentTransaction is not allowed to be added to the back stack.");
+        }
+        mAddToBackStack = true;
+        mName = name;
+        return this;
+    }
+
+    public boolean isAddToBackStackAllowed() {
+        return mAllowAddToBackStack;
+    }
+
+    public FragmentTransaction disallowAddToBackStack() {
+        if (mAddToBackStack) {
+            throw new IllegalStateException(
+                    "This transaction is already being added to the back stack");
+        }
+        mAllowAddToBackStack = false;
+        return this;
+    }
+
+    public FragmentTransaction setBreadCrumbTitle(int res) {
+        mBreadCrumbTitleRes = res;
+        mBreadCrumbTitleText = null;
+        return this;
+    }
+
+    public FragmentTransaction setBreadCrumbTitle(CharSequence text) {
+        mBreadCrumbTitleRes = 0;
+        mBreadCrumbTitleText = text;
+        return this;
+    }
+
+    public FragmentTransaction setBreadCrumbShortTitle(int res) {
+        mBreadCrumbShortTitleRes = res;
+        mBreadCrumbShortTitleText = null;
+        return this;
+    }
+
+    public FragmentTransaction setBreadCrumbShortTitle(CharSequence text) {
+        mBreadCrumbShortTitleRes = 0;
+        mBreadCrumbShortTitleText = text;
+        return this;
+    }
+
+    void bumpBackStackNesting(int amt) {
+        if (!mAddToBackStack) {
+            return;
+        }
+        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting in " + this
+                + " by " + amt);
+        Op op = mHead;
+        while (op != null) {
+            op.fragment.mBackStackNesting += amt;
+            if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
+                    + op.fragment + " to " + op.fragment.mBackStackNesting);
+            if (op.removed != null) {
+                for (int i=op.removed.size()-1; i>=0; i--) {
+                    Fragment r = op.removed.get(i);
+                    r.mBackStackNesting += amt;
+                    if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
+                            + r + " to " + r.mBackStackNesting);
+                }
+            }
+            op = op.next;
+        }
+    }
+
+    public int commit() {
+        if (mCommitted) throw new IllegalStateException("commit already called");
+        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Commit: " + this);
+        mCommitted = true;
+        if (mAddToBackStack) {
+            mIndex = mManager.allocBackStackIndex(this);
+        } else {
+            mIndex = -1;
+        }
+        mManager.enqueueAction(this);
+        return mIndex;
+    }
+
+    public void run() {
+        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
+
+        if (mAddToBackStack) {
+            if (mIndex < 0) {
+                throw new IllegalStateException("addToBackStack() called after commit()");
+            }
+        }
+
+        bumpBackStackNesting(1);
+
+        Op op = mHead;
+        while (op != null) {
+            switch (op.cmd) {
+                case OP_ADD: {
+                    Fragment f = op.fragment;
+                    f.mNextAnim = op.enterAnim;
+                    mManager.addFragment(f, false);
+                } break;
+                case OP_REPLACE: {
+                    Fragment f = op.fragment;
+                    if (mManager.mAdded != null) {
+                        for (int i=0; i<mManager.mAdded.size(); i++) {
+                            Fragment old = mManager.mAdded.get(i);
+                            if (FragmentManagerImpl.DEBUG) Log.v(TAG,
+                                    "OP_REPLACE: adding=" + f + " old=" + old);
+                            if (old.mContainerId == f.mContainerId) {
+                                if (op.removed == null) {
+                                    op.removed = new ArrayList<Fragment>();
+                                }
+                                op.removed.add(old);
+                                old.mNextAnim = op.exitAnim;
+                                if (mAddToBackStack) {
+                                    old.mBackStackNesting += 1;
+                                    if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
+                                            + old + " to " + old.mBackStackNesting);
+                                }
+                                mManager.removeFragment(old, mTransition, mTransitionStyle);
+                            }
+                        }
+                    }
+                    f.mNextAnim = op.enterAnim;
+                    mManager.addFragment(f, false);
+                } break;
+                case OP_REMOVE: {
+                    Fragment f = op.fragment;
+                    f.mNextAnim = op.exitAnim;
+                    mManager.removeFragment(f, mTransition, mTransitionStyle);
+                } break;
+                case OP_HIDE: {
+                    Fragment f = op.fragment;
+                    f.mNextAnim = op.exitAnim;
+                    mManager.hideFragment(f, mTransition, mTransitionStyle);
+                } break;
+                case OP_SHOW: {
+                    Fragment f = op.fragment;
+                    f.mNextAnim = op.enterAnim;
+                    mManager.showFragment(f, mTransition, mTransitionStyle);
+                } break;
+                default: {
+                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
+                }
+            }
+
+            op = op.next;
+        }
+
+        mManager.moveToState(mManager.mCurState, mTransition,
+                mTransitionStyle, true);
+
+        if (mAddToBackStack) {
+            mManager.addBackStackState(this);
+        }
+    }
+
+    public void popFromBackStack(boolean doStateMove) {
+        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "popFromBackStack: " + this);
+
+        bumpBackStackNesting(-1);
+
+        Op op = mTail;
+        while (op != null) {
+            switch (op.cmd) {
+                case OP_ADD: {
+                    Fragment f = op.fragment;
+                    f.mImmediateActivity = null;
+                    mManager.removeFragment(f,
+                            FragmentManagerImpl.reverseTransit(mTransition),
+                            mTransitionStyle);
+                } break;
+                case OP_REPLACE: {
+                    Fragment f = op.fragment;
+                    f.mImmediateActivity = null;
+                    mManager.removeFragment(f,
+                            FragmentManagerImpl.reverseTransit(mTransition),
+                            mTransitionStyle);
+                    if (op.removed != null) {
+                        for (int i=0; i<op.removed.size(); i++) {
+                            Fragment old = op.removed.get(i);
+                            f.mImmediateActivity = mManager.mActivity;
+                            mManager.addFragment(old, false);
+                        }
+                    }
+                } break;
+                case OP_REMOVE: {
+                    Fragment f = op.fragment;
+                    f.mImmediateActivity = mManager.mActivity;
+                    mManager.addFragment(f, false);
+                } break;
+                case OP_HIDE: {
+                    Fragment f = op.fragment;
+                    mManager.showFragment(f,
+                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
+                } break;
+                case OP_SHOW: {
+                    Fragment f = op.fragment;
+                    mManager.hideFragment(f,
+                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
+                } break;
+                default: {
+                    throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
+                }
+            }
+
+            op = op.prev;
+        }
+
+        if (doStateMove) {
+            mManager.moveToState(mManager.mCurState,
+                    FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true);
+        }
+
+        if (mIndex >= 0) {
+            mManager.freeBackStackIndex(mIndex);
+            mIndex = -1;
+        }
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public int getTransition() {
+        return mTransition;
+    }
+
+    public int getTransitionStyle() {
+        return mTransitionStyle;
+    }
+
+    public boolean isEmpty() {
+        return mNumOp == 0;
+    }
+}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 69833032..5ce4cd6 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -56,11 +56,14 @@
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.hardware.SensorManager;
+import android.location.CountryDetector;
+import android.location.ICountryDetector;
 import android.location.ILocationManager;
 import android.location.LocationManager;
 import android.media.AudioManager;
@@ -72,6 +75,7 @@
 import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.DropBoxManager;
 import android.os.Environment;
@@ -88,7 +92,7 @@
 import android.os.FileUtils.FileStatus;
 import android.os.storage.StorageManager;
 import android.telephony.TelephonyManager;
-import android.text.ClipboardManager;
+import android.content.ClipboardManager;
 import android.util.AndroidRuntimeException;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
@@ -167,6 +171,7 @@
     private static ThrottleManager sThrottleManager;
     private static WifiManager sWifiManager;
     private static LocationManager sLocationManager;
+    private static CountryDetector sCountryDetector;
     private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs =
             new HashMap<String, SharedPreferencesImpl>();
 
@@ -208,23 +213,8 @@
     private File mExternalFilesDir;
     private File mExternalCacheDir;
 
-    private static long sInstanceCount = 0;
-
     private static final String[] EMPTY_FILE_LIST = {};
 
-    // For debug only
-    /*
-    @Override
-    protected void finalize() throws Throwable {
-        super.finalize();
-        --sInstanceCount;
-    }
-    */
-
-    public static long getInstanceCount() {
-        return sInstanceCount;
-    }
-
     @Override
     public AssetManager getAssets() {
         return mResources.getAssets();
@@ -275,7 +265,11 @@
     public Resources.Theme getTheme() {
         if (mTheme == null) {
             if (mThemeResource == 0) {
-                mThemeResource = com.android.internal.R.style.Theme;
+                final Context outerContext = getOuterContext();
+                mThemeResource = (outerContext.getApplicationInfo().targetSdkVersion
+                        >= Build.VERSION_CODES.HONEYCOMB)
+                                ? com.android.internal.R.style.Theme_Holo
+                                : com.android.internal.R.style.Theme;
             }
             mTheme = mResources.newTheme();
             mTheme.applyStyle(mThemeResource, true);
@@ -541,6 +535,15 @@
     }
 
     @Override
+    public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
+            DatabaseErrorHandler errorHandler) {
+        File f = validateFilePath(name, true);
+        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f.getPath(), factory, errorHandler);
+        setFilePermissionsFromMode(f.getPath(), mode, 0);
+        return db;
+    }
+
+    @Override
     public boolean deleteDatabase(String name) {
         try {
             File f = validateFilePath(name, false);
@@ -618,7 +621,8 @@
                     + " Is this really what you want?");
         }
         mMainThread.getInstrumentation().execStartActivity(
-            getOuterContext(), mMainThread.getApplicationThread(), null, null, intent, -1);
+            getOuterContext(), mMainThread.getApplicationThread(), null,
+            (Activity)null, intent, -1);
     }
 
     @Override
@@ -942,6 +946,8 @@
             return AccessibilityManager.getInstance(this);
         } else if (LOCATION_SERVICE.equals(name)) {
             return getLocationManager();
+        } else if (COUNTRY_DETECTOR.equals(name)) {
+            return getCountryDetector();
         } else if (SEARCH_SERVICE.equals(name)) {
             return getSearchManager();
         } else if (SENSOR_SERVICE.equals(name)) {
@@ -1060,8 +1066,13 @@
     private NotificationManager getNotificationManager() {
         synchronized (mSync) {
             if (mNotificationManager == null) {
+                final Context outerContext = getOuterContext();
                 mNotificationManager = new NotificationManager(
-                        new ContextThemeWrapper(getOuterContext(), com.android.internal.R.style.Theme_Dialog),
+                        new ContextThemeWrapper(outerContext,
+                                outerContext.getApplicationInfo().targetSdkVersion >=
+                                    Build.VERSION_CODES.HONEYCOMB
+                                ? com.android.internal.R.style.Theme_Holo_Dialog
+                                : com.android.internal.R.style.Theme_Dialog),
                         mMainThread.getHandler());
             }
         }
@@ -1108,6 +1119,17 @@
         return sLocationManager;
     }
 
+    private CountryDetector getCountryDetector() {
+        synchronized (sSync) {
+            if (sCountryDetector == null) {
+                IBinder b = ServiceManager.getService(COUNTRY_DETECTOR);
+                ICountryDetector service = ICountryDetector.Stub.asInterface(b);
+                sCountryDetector = new CountryDetector(service);
+            }
+        }
+        return sCountryDetector;
+    }
+
     private SearchManager getSearchManager() {
         synchronized (mSync) {
             if (mSearchManager == null) {
@@ -1481,8 +1503,6 @@
     }
 
     ContextImpl() {
-        // For debug only
-        //++sInstanceCount;
         mOuterContext = this;
     }
 
@@ -1493,7 +1513,6 @@
      * @param context Existing application context.
      */
     public ContextImpl(ContextImpl context) {
-        ++sInstanceCount;
         mPackageInfo = context.mPackageInfo;
         mResources = context.mResources;
         mMainThread = context.mMainThread;
@@ -2800,6 +2819,13 @@
                 return v != null ? v : defValue;
             }
         }
+        
+        public Set<String> getStringSet(String key, Set<String> defValues) {
+            synchronized (this) {
+                Set<String> v = (Set<String>) mMap.get(key);
+                return v != null ? v : defValues;
+            }
+        }
 
         public int getInt(String key, int defValue) {
             synchronized (this) {
@@ -2861,6 +2887,12 @@
                     return this;
                 }
             }
+            public Editor putStringSet(String key, Set<String> values) {
+                synchronized (this) {
+                    mModified.put(key, values);
+                    return this;
+                }
+            }
             public Editor putInt(String key, int value) {
                 synchronized (this) {
                     mModified.put(key, value);
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 8ba480d..37f8738 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
+import android.os.Build;
 import android.os.Bundle;
 import android.text.TextUtils.TruncateAt;
 import android.view.LayoutInflater;
@@ -82,7 +83,9 @@
             int year,
             int monthOfYear,
             int dayOfMonth) {
-        this(context, com.android.internal.R.style.Theme_Dialog_Alert,
+        this(context, context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB
+                        ? com.android.internal.R.style.Theme_Holo_Dialog_Alert
+                        : com.android.internal.R.style.Theme_Dialog_Alert,
                 callBack, year, monthOfYear, dayOfMonth);
     }
 
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index da8c9e5..526129a 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -16,18 +16,23 @@
 
 package android.app;
 
+import com.android.internal.app.ActionBarImpl;
 import com.android.internal.policy.PolicyManager;
 
-import android.content.Context;
-import android.content.DialogInterface;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.DialogInterface;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.util.TypedValue;
+import android.view.ActionMode;
 import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -36,13 +41,12 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.OnCreateContextMenuListener;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
 import android.view.Window;
 import android.view.WindowManager;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.OnCreateContextMenuListener;
-import android.view.ViewGroup.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 
 import java.lang.ref.WeakReference;
@@ -76,6 +80,7 @@
     final WindowManager mWindowManager;
     Window mWindow;
     View mDecor;
+    private ActionBarImpl mActionBar;
     /**
      * This field should be made private, so it is hidden from the SDK.
      * {@hide}
@@ -136,8 +141,14 @@
      * <var>context</var>.  If 0, the default dialog theme will be used.
      */
     public Dialog(Context context, int theme) {
-        mContext = new ContextThemeWrapper(
-            context, theme == 0 ? com.android.internal.R.style.Theme_Dialog : theme);
+        if (theme == 0) {
+            TypedValue outValue = new TypedValue();
+            context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
+                    outValue, true);
+            theme = outValue.resourceId;
+        }
+
+        mContext = new ContextThemeWrapper(context, theme);
         mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
         Window w = PolicyManager.makeNewWindow(mContext);
         mWindow = w;
@@ -170,13 +181,22 @@
     /**
      * Retrieve the Context this Dialog is running in.
      * 
-     * @return Context The Context that was supplied to the constructor.
+     * @return Context The Context used by the Dialog.
      */
     public final Context getContext() {
         return mContext;
     }
 
     /**
+     * Retrieve the {@link ActionBar} attached to this dialog, if present.
+     *
+     * @return The ActionBar attached to the dialog or null if no ActionBar is present.
+     */
+    public ActionBar getActionBar() {
+        return mActionBar;
+    }
+
+    /**
      * Sets the Activity that owns this dialog. An example use: This Dialog will
      * use the suggested volume control stream of the Activity.
      * 
@@ -216,6 +236,9 @@
     public void show() {
         if (mShowing) {
             if (mDecor != null) {
+                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
+                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
+                }
                 mDecor.setVisibility(View.VISIBLE);
             }
             return;
@@ -227,6 +250,11 @@
 
         onStart();
         mDecor = mWindow.getDecorView();
+
+        if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
+            mActionBar = new ActionBarImpl(this);
+        }
+
         WindowManager.LayoutParams l = mWindow.getAttributes();
         if ((l.softInputMode
                 & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
@@ -310,7 +338,7 @@
     }
 
     /**
-     * Similar to {@link Activity#onCreate}, you should initialized your dialog
+     * Similar to {@link Activity#onCreate}, you should initialize your dialog
      * in this method, including calling {@link #setContentView}.
      * @param savedInstanceState If this dialog is being reinitalized after a
      *     the hosting activity was previously shut down, holds the result from
@@ -775,6 +803,13 @@
     }
 
     /**
+     * @see Activity#invalidateOptionsMenu()
+     */
+    public void invalidateOptionsMenu() {
+        mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+    }
+
+    /**
      * @see Activity#onCreateContextMenu(ContextMenu, View, ContextMenuInfo)
      */
     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
@@ -832,8 +867,15 @@
         }
     }
 
+    public ActionMode onStartActionMode(ActionMode.Callback callback) {
+        if (mActionBar != null) {
+            return mActionBar.startActionMode(callback);
+        }
+        return null;
+    }
+
     /**
-     * @return The activity associated with this dialog, or null if there is no assocaited activity.
+     * @return The activity associated with this dialog, or null if there is no associated activity.
      */
     private ComponentName getAssociatedActivity() {
         Activity activity = mOwnerActivity;
diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java
new file mode 100644
index 0000000..8e2389b
--- /dev/null
+++ b/core/java/android/app/DialogFragment.java
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+
+/**
+ * A fragment that displays a dialog window, floating on top of its
+ * activity's window.  This fragment contains a Dialog object, which it
+ * displays as appropriate based on the fragment's state.  Control of
+ * the dialog (deciding when to show, hide, dismiss it) should be done through
+ * the API here, not with direct calls on the dialog.
+ *
+ * <p>Implementations should override this class and implement
+ * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} to supply the
+ * content of the dialog.  Alternatively, they can override
+ * {@link #onCreateDialog(Bundle)} to create an entirely custom dialog, such
+ * as an AlertDialog, with its own content.
+ *
+ * <p>Topics covered here:
+ * <ol>
+ * <li><a href="#Lifecycle">Lifecycle</a>
+ * <li><a href="#BasicDialog">Basic Dialog</a>
+ * <li><a href="#AlertDialog">Alert Dialog</a>
+ * <li><a href="#DialogOrEmbed">Selecting Between Dialog or Embedding</a>
+ * </ol>
+ *
+ * <a name="Lifecycle"></a>
+ * <h3>Lifecycle</h3>
+ *
+ * <p>DialogFragment does various things to keep the fragment's lifecycle
+ * driving it, instead of the Dialog.  Note that dialogs are generally
+ * autonomous entities -- they are their own window, receiving their own
+ * input events, and often deciding on their own when to disappear (by
+ * receiving a back key event or the user clicking on a button).
+ *
+ * <p>DialogFragment needs to ensure that what is happening with the Fragment
+ * and Dialog states remains consistent.  To do this, it watches for dismiss
+ * events from the dialog and takes are of removing its own state when they
+ * happen.  This means you should use {@link #show(FragmentManager, String)}
+ * or {@link #show(FragmentTransaction, String)} to add an instance of
+ * DialogFragment to your UI, as these keep track of how DialogFragment should
+ * remove itself when the dialog is dismissed.
+ *
+ * <a name="BasicDialog"></a>
+ * <h3>Basic Dialog</h3>
+ *
+ * <p>The simplest use of DialogFragment is as a floating container for the
+ * fragment's view hierarchy.  A simple implementation may look like this:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialog.java
+ *      dialog}
+ *
+ * <p>An example showDialog() method on the Activity could be:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialog.java
+ *      add_dialog}
+ *
+ * <p>This removes any currently shown dialog, creates a new DialogFragment
+ * with an argument, and shows it as a new state on the back stack.  When the
+ * transaction is popped, the current DialogFragment and its Dialog will be
+ * destroyed, and the previous one (if any) re-shown.  Note that in this case
+ * DialogFragment will take care of popping the transaction of the Dialog
+ * is dismissed separately from it.
+ *
+ * <a name="AlertDialog"></a>
+ * <h3>Alert Dialog</h3>
+ *
+ * <p>Instead of (or in addition to) implementing {@link #onCreateView} to
+ * generate the view hierarchy inside of a dialog, you may implement
+ * {@link #onCreateDialog(Bundle)} to create your own custom Dialog object.
+ *
+ * <p>This is most useful for creating an {@link AlertDialog}, allowing you
+ * to display standard alerts to the user that are managed by a fragment.
+ * A simple example implementation of this is:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialog.java
+ *      dialog}
+ *
+ * <p>The activity creating this fragment may have the following methods to
+ * show the dialog and receive results from it:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialog.java
+ *      activity}
+ *
+ * <p>Note that in this case the fragment is not placed on the back stack, it
+ * is just added as an indefinitely running fragment.  Because dialogs normally
+ * are modal, this will still operate as a back stack, since the dialog will
+ * capture user input until it is dismissed.  When it is dismissed, DialogFragment
+ * will take care of removing itself from its fragment manager.
+ *
+ * <a name="DialogOrEmbed"></a>
+ * <h3>Selecting Between Dialog or Embedding</h3>
+ *
+ * <p>A DialogFragment can still optionally be used as a normal fragment, if
+ * desired.  This is useful if you have a fragment that in some cases should
+ * be shown as a dialog and others embedded in a larger UI.  This behavior
+ * will normally be automatically selected for you based on how you are using
+ * the fragment, but can be customized with {@link #setShowsDialog(boolean)}.
+ *
+ * <p>For example, here is a simple dialog fragment:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
+ *      dialog}
+ *
+ * <p>An instance of this fragment can be created and shown as a dialog:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
+ *      show_dialog}
+ *
+ * <p>It can also be added as content in a view hierarchy:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
+ *      embed}
+ */
+public class DialogFragment extends Fragment
+        implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
+
+    /**
+     * Style for {@link #setStyle(int, int)}: a basic,
+     * normal dialog.
+     */
+    public static final int STYLE_NORMAL = 0;
+
+    /**
+     * Style for {@link #setStyle(int, int)}: don't include
+     * a title area.
+     */
+    public static final int STYLE_NO_TITLE = 1;
+
+    /**
+     * Style for {@link #setStyle(int, int)}: don't draw
+     * any frame at all; the view hierarchy returned by {@link #onCreateView}
+     * is entirely responsible for drawing the dialog.
+     */
+    public static final int STYLE_NO_FRAME = 2;
+
+    /**
+     * Style for {@link #setStyle(int, int)}: like
+     * {@link #STYLE_NO_FRAME}, but also disables all input to the dialog.
+     * The user can not touch it, and its window will not receive input focus.
+     */
+    public static final int STYLE_NO_INPUT = 3;
+
+    private static final String SAVED_DIALOG_STATE_TAG = "android:savedDialogState";
+    private static final String SAVED_STYLE = "android:style";
+    private static final String SAVED_THEME = "android:theme";
+    private static final String SAVED_CANCELABLE = "android:cancelable";
+    private static final String SAVED_SHOWS_DIALOG = "android:showsDialog";
+    private static final String SAVED_BACK_STACK_ID = "android:backStackId";
+
+    int mStyle = STYLE_NORMAL;
+    int mTheme = 0;
+    boolean mCancelable = true;
+    boolean mShowsDialog = true;
+    int mBackStackId = -1;
+
+    Dialog mDialog;
+    boolean mDestroyed;
+    boolean mRemoved;
+
+    public DialogFragment() {
+    }
+
+    /**
+     * Call to customize the basic appearance and behavior of the
+     * fragment's dialog.  This can be used for some common dialog behaviors,
+     * taking care of selecting flags, theme, and other options for you.  The
+     * same effect can be achieve by manually setting Dialog and Window
+     * attributes yourself.  Calling this after the fragment's Dialog is
+     * created will have no effect.
+     *
+     * @param style Selects a standard style: may be {@link #STYLE_NORMAL},
+     * {@link #STYLE_NO_TITLE}, {@link #STYLE_NO_FRAME}, or
+     * {@link #STYLE_NO_INPUT}.
+     * @param theme Optional custom theme.  If 0, an appropriate theme (based
+     * on the style) will be selected for you.
+     */
+    public void setStyle(int style, int theme) {
+        mStyle = style;
+        if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {
+            mTheme = com.android.internal.R.style.Theme_Holo_Dialog_NoFrame;
+        }
+        if (theme != 0) {
+            mTheme = theme;
+        }
+    }
+
+    /**
+     * @deprecated Please use {@link #show(FragmentManager, String)}.
+     */
+    @Deprecated
+    public void show(Activity activity, String tag) {
+        FragmentTransaction ft = activity.openFragmentTransaction();
+        ft.add(this, tag);
+        ft.commit();
+    }
+
+    /**
+     * Display the dialog, adding the fragment to the given FragmentManager.  This
+     * is a convenience for explicitly creating a transaction, adding the
+     * fragment to it with the given tag, and committing it.  This does
+     * <em>not</em> add the transaction to the back stack.  When the fragment
+     * is dismissed, a new transaction will be executed to remove it from
+     * the activity.
+     * @param manager The FragmentManager this fragment will be added to.
+     * @param tag The tag for this fragment, as per
+     * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
+     */
+    public void show(FragmentManager manager, String tag) {
+        FragmentTransaction ft = manager.openTransaction();
+        ft.add(this, tag);
+        ft.commit();
+    }
+
+    /**
+     * Display the dialog, adding the fragment using an existing transaction
+     * and then committing the transaction.
+     * @param transaction An existing transaction in which to add the fragment.
+     * @param tag The tag for this fragment, as per
+     * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
+     * @return Returns the identifier of the committed transaction, as per
+     * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.
+     */
+    public int show(FragmentTransaction transaction, String tag) {
+        transaction.add(this, tag);
+        mRemoved = false;
+        mBackStackId = transaction.commit();
+        return mBackStackId;
+    }
+
+    /**
+     * Dismiss the fragment and its dialog.  If the fragment was added to the
+     * back stack, all back stack state up to and including this entry will
+     * be popped.  Otherwise, a new transaction will be committed to remove
+     * the fragment.
+     */
+    public void dismiss() {
+        if (mDialog != null) {
+            mDialog.dismiss();
+            mDialog = null;
+        }
+        mRemoved = true;
+        if (mBackStackId >= 0) {
+            getFragmentManager().popBackStack(mBackStackId,
+                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
+            mBackStackId = -1;
+        } else {
+            FragmentTransaction ft = getFragmentManager().openTransaction();
+            ft.remove(this);
+            ft.commit();
+        }
+    }
+
+    public Dialog getDialog() {
+        return mDialog;
+    }
+
+    public int getTheme() {
+        return mTheme;
+    }
+
+    /**
+     * Control whether the shown Dialog is cancelable.  Use this instead of
+     * directly calling {@link Dialog#setCancelable(boolean)
+     * Dialog.setCancelable(boolean)}, because DialogFragment needs to change
+     * its behavior based on this.
+     *
+     * @param cancelable If true, the dialog is cancelable.  The default
+     * is true.
+     */
+    public void setCancelable(boolean cancelable) {
+        mCancelable = cancelable;
+        if (mDialog != null) mDialog.setCancelable(cancelable);
+    }
+
+    /**
+     * Return the current value of {@link #setCancelable(boolean)}.
+     */
+    public boolean getCancelable() {
+        return mCancelable;
+    }
+
+    /**
+     * Controls whether this fragment should be shown in a dialog.  If not
+     * set, no Dialog will be created in {@link #onActivityCreated(Bundle)},
+     * and the fragment's view hierarchy will thus not be added to it.  This
+     * allows you to instead use it as a normal fragment (embedded inside of
+     * its activity).
+     *
+     * <p>This is normally set for you based on whether the fragment is
+     * associated with a container view ID passed to
+     * {@link FragmentTransaction#add(int, Fragment) FragmentTransaction.add(int, Fragment)}.
+     * If the fragment was added with a container, setShowsDialog will be
+     * initialized to false; otherwise, it will be true.
+     *
+     * @param showsDialog If true, the fragment will be displayed in a Dialog.
+     * If false, no Dialog will be created and the fragment's view hierarchly
+     * left undisturbed.
+     */
+    public void setShowsDialog(boolean showsDialog) {
+        mShowsDialog = showsDialog;
+    }
+
+    /**
+     * Return the current value of {@link #setShowsDialog(boolean)}.
+     */
+    public boolean getShowsDialog() {
+        return mShowsDialog;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mShowsDialog = mContainerId == 0;
+
+        if (savedInstanceState != null) {
+            mStyle = savedInstanceState.getInt(SAVED_STYLE, STYLE_NORMAL);
+            mTheme = savedInstanceState.getInt(SAVED_THEME, 0);
+            mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true);
+            mShowsDialog = savedInstanceState.getBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
+            mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1);
+        }
+    }
+
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        return new Dialog(getActivity(), getTheme());
+    }
+
+    public void onCancel(DialogInterface dialog) {
+    }
+
+    public void onDismiss(DialogInterface dialog) {
+        if (!mRemoved) {
+            dismiss();
+        }
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        if (!mShowsDialog) {
+            return;
+        }
+
+        mDialog = onCreateDialog(savedInstanceState);
+        mDestroyed = false;
+        switch (mStyle) {
+            case STYLE_NO_INPUT:
+                mDialog.getWindow().addFlags(
+                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
+                        WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+                // fall through...
+            case STYLE_NO_FRAME:
+            case STYLE_NO_TITLE:
+                mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+        }
+        View view = getView();
+        if (view != null) {
+            if (view.getParent() != null) {
+                throw new IllegalStateException("DialogFragment can not be attached to a container view");
+            }
+            mDialog.setContentView(view);
+        }
+        mDialog.setOwnerActivity(getActivity());
+        mDialog.setCancelable(mCancelable);
+        mDialog.setOnCancelListener(this);
+        mDialog.setOnDismissListener(this);
+        if (savedInstanceState != null) {
+            Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
+            if (dialogState != null) {
+                mDialog.onRestoreInstanceState(dialogState);
+            }
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mDialog != null) {
+            mRemoved = false;
+            mDialog.show();
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (mDialog != null) {
+            Bundle dialogState = mDialog.onSaveInstanceState();
+            if (dialogState != null) {
+                outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
+            }
+        }
+        if (mStyle != STYLE_NORMAL) {
+            outState.putInt(SAVED_STYLE, mStyle);
+        }
+        if (mTheme != 0) {
+            outState.putInt(SAVED_THEME, mTheme);
+        }
+        if (!mCancelable) {
+            outState.putBoolean(SAVED_CANCELABLE, mCancelable);
+        }
+        if (!mShowsDialog) {
+            outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
+        }
+        if (mBackStackId != -1) {
+            outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
+        }
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mDialog != null) {
+            mDialog.hide();
+        }
+    }
+
+    /**
+     * Remove dialog.
+     */
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mDestroyed = true;
+        if (mDialog != null) {
+            // Set removed here because this dismissal is just to hide
+            // the dialog -- we don't want this to cause the fragment to
+            // actually be removed.
+            mRemoved = true;
+            mDialog.dismiss();
+            mDialog = null;
+        }
+    }
+}
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index a25aa6f..01414fa 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -26,8 +26,8 @@
 import android.net.Uri;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.provider.BaseColumns;
 import android.provider.Downloads;
+import android.util.Log;
 import android.util.Pair;
 
 import java.io.File;
@@ -59,24 +59,24 @@
      * An identifier for a particular download, unique across the system.  Clients use this ID to
      * make subsequent calls related to the download.
      */
-    public final static String COLUMN_ID = BaseColumns._ID;
+    public final static String COLUMN_ID = Downloads.Impl._ID;
 
     /**
      * The client-supplied title for this download.  This will be displayed in system notifications.
      * Defaults to the empty string.
      */
-    public final static String COLUMN_TITLE = "title";
+    public final static String COLUMN_TITLE = Downloads.Impl.COLUMN_TITLE;
 
     /**
      * The client-supplied description of this download.  This will be displayed in system
      * notifications.  Defaults to the empty string.
      */
-    public final static String COLUMN_DESCRIPTION = "description";
+    public final static String COLUMN_DESCRIPTION = Downloads.Impl.COLUMN_DESCRIPTION;
 
     /**
      * URI to be downloaded.
      */
-    public final static String COLUMN_URI = "uri";
+    public final static String COLUMN_URI = Downloads.Impl.COLUMN_URI;
 
     /**
      * Internet Media Type of the downloaded file.  If no value is provided upon creation, this will
@@ -101,9 +101,14 @@
     public final static String COLUMN_LOCAL_URI = "local_uri";
 
     /**
+     * The pathname of the file where the download is stored.
+     */
+    public final static String COLUMN_LOCAL_FILENAME = "local_filename";
+
+    /**
      * Current status of the download, as one of the STATUS_* constants.
      */
-    public final static String COLUMN_STATUS = "status";
+    public final static String COLUMN_STATUS = Downloads.Impl.COLUMN_STATUS;
 
     /**
      * Provides more detail on the status of the download.  Its meaning depends on the value of
@@ -140,7 +145,7 @@
      * used to delete the entries from MediaProvider database when it is deleted from the
      * downloaded list.
      */
-    public static final String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
+    public static final String COLUMN_MEDIAPROVIDER_URI = Downloads.Impl.COLUMN_MEDIAPROVIDER_URI;
 
     /**
      * Value of {@link #COLUMN_STATUS} when the download is waiting to start.
@@ -268,10 +273,19 @@
      */
     public static final String EXTRA_DOWNLOAD_ID = "extra_download_id";
 
+    /**
+     * When clicks on multiple notifications are received, the following
+     * provides an array of download ids corresponding to the download notification that was
+     * clicked. It can be retrieved by the receiver of this
+     * Intent using {@link android.content.Intent#getLongArrayExtra(String)}.
+     */
+    public static final String EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS = "extra_click_download_ids";
+
     // this array must contain all public columns
     private static final String[] COLUMNS = new String[] {
         COLUMN_ID,
         COLUMN_MEDIAPROVIDER_URI,
+        Downloads.Impl.COLUMN_DESTINATION,
         COLUMN_TITLE,
         COLUMN_DESCRIPTION,
         COLUMN_URI,
@@ -281,29 +295,31 @@
         COLUMN_STATUS,
         COLUMN_REASON,
         COLUMN_BYTES_DOWNLOADED_SO_FAR,
-        COLUMN_LAST_MODIFIED_TIMESTAMP
+        COLUMN_LAST_MODIFIED_TIMESTAMP,
+        COLUMN_LOCAL_FILENAME,
     };
 
     // columns to request from DownloadProvider
     private static final String[] UNDERLYING_COLUMNS = new String[] {
         Downloads.Impl._ID,
         Downloads.Impl.COLUMN_MEDIAPROVIDER_URI,
-        Downloads.COLUMN_TITLE,
-        Downloads.COLUMN_DESCRIPTION,
-        Downloads.COLUMN_URI,
-        Downloads.COLUMN_MIME_TYPE,
-        Downloads.COLUMN_TOTAL_BYTES,
-        Downloads.COLUMN_STATUS,
-        Downloads.COLUMN_CURRENT_BYTES,
-        Downloads.COLUMN_LAST_MODIFICATION,
-        Downloads.COLUMN_DESTINATION,
+        Downloads.Impl.COLUMN_DESTINATION,
+        Downloads.Impl.COLUMN_TITLE,
+        Downloads.Impl.COLUMN_DESCRIPTION,
+        Downloads.Impl.COLUMN_URI,
+        Downloads.Impl.COLUMN_MIME_TYPE,
+        Downloads.Impl.COLUMN_TOTAL_BYTES,
+        Downloads.Impl.COLUMN_STATUS,
+        Downloads.Impl.COLUMN_CURRENT_BYTES,
+        Downloads.Impl.COLUMN_LAST_MODIFICATION,
         Downloads.Impl.COLUMN_FILE_NAME_HINT,
         Downloads.Impl._DATA,
     };
 
     private static final Set<String> LONG_COLUMNS = new HashSet<String>(
             Arrays.asList(COLUMN_ID, COLUMN_TOTAL_SIZE_BYTES, COLUMN_STATUS, COLUMN_REASON,
-                          COLUMN_BYTES_DOWNLOADED_SO_FAR, COLUMN_LAST_MODIFIED_TIMESTAMP));
+                          COLUMN_BYTES_DOWNLOADED_SO_FAR, COLUMN_LAST_MODIFIED_TIMESTAMP,
+                          Downloads.Impl.COLUMN_DESTINATION));
 
     /**
      * This class contains all the information necessary to request a new download. The URI is the
@@ -331,11 +347,44 @@
         private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>();
         private CharSequence mTitle;
         private CharSequence mDescription;
-        private boolean mShowNotification = true;
         private String mMimeType;
         private boolean mRoamingAllowed = true;
         private int mAllowedNetworkTypes = ~0; // default to all network types allowed
         private boolean mIsVisibleInDownloadsUi = true;
+        private boolean mScannable = false;
+        /** if a file is designated as a MediaScanner scannable file, the following value is
+         * stored in the database column {@link Downloads.Impl#COLUMN_MEDIA_SCANNED}.
+         */
+        private static final int SCANNABLE_VALUE_YES = 0;
+        // value of 1 is stored in the above column by DownloadProvider after it is scanned by
+        // MediaScanner
+        /** if a file is designated as a file that should not be scanned by MediaScanner,
+         * the following value is stored in the database column
+         * {@link Downloads.Impl#COLUMN_MEDIA_SCANNED}.
+         */
+        private static final int SCANNABLE_VALUE_NO = 2;
+
+        /**
+         * This download is visible but only shows in the notifications
+         * while it's in progress.
+         */
+        public static final int VISIBILITY_VISIBLE = 0;
+
+        /**
+         * This download is visible and shows in the notifications while
+         * in progress and after completion.
+         */
+        public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
+
+        /**
+         * This download doesn't show in the UI or in the notifications.
+         */
+        public static final int VISIBILITY_HIDDEN = 2;
+
+        /** can take any of the following values: {@link #VISIBILITY_HIDDEN}
+         * {@link #VISIBILITY_VISIBLE_NOTIFY_COMPLETED}, {@link #VISIBILITY_VISIBLE}
+         */
+        private int mNotificationVisibility = VISIBILITY_VISIBLE;
 
         /**
          * @param uri the HTTP URI to download.
@@ -345,8 +394,8 @@
                 throw new NullPointerException();
             }
             String scheme = uri.getScheme();
-            if (scheme == null || !scheme.equals("http")) {
-                throw new IllegalArgumentException("Can only download HTTP URIs: " + uri);
+            if (scheme == null || (!scheme.equals("http") && !scheme.equals("https"))) {
+                throw new IllegalArgumentException("Can only download HTTP/HTTPS URIs: " + uri);
             }
             mUri = uri;
         }
@@ -355,7 +404,10 @@
          * Set the local destination for the downloaded file. Must be a file URI to a path on
          * external storage, and the calling application must have the WRITE_EXTERNAL_STORAGE
          * permission.
-         *
+         * <p>
+         * The downloaded file is not scanned by MediaScanner.
+         * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}.
+         * <p>
          * By default, downloads are saved to a generated filename in the shared download cache and
          * may be deleted by the system at any time to reclaim space.
          *
@@ -369,6 +421,9 @@
         /**
          * Set the local destination for the downloaded file to a path within the application's
          * external files directory (as returned by {@link Context#getExternalFilesDir(String)}.
+         * <p>
+         * The downloaded file is not scanned by MediaScanner.
+         * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}.
          *
          * @param context the {@link Context} to use in determining the external files directory
          * @param dirType the directory type to pass to {@link Context#getExternalFilesDir(String)}
@@ -385,6 +440,9 @@
          * Set the local destination for the downloaded file to a path within the public external
          * storage directory (as returned by
          * {@link Environment#getExternalStoragePublicDirectory(String)}.
+         *<p>
+         * The downloaded file is not scanned by MediaScanner.
+         * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}.
          *
          * @param dirType the directory type to pass to
          *        {@link Environment#getExternalStoragePublicDirectory(String)}
@@ -404,6 +462,14 @@
         }
 
         /**
+         * If the file to be downloaded is to be scanned by MediaScanner, this method
+         * should be called before {@link DownloadManager#enqueue(Request)} is called.
+         */
+        public void allowScanningByMediaScanner() {
+            mScannable = true;
+        }
+
+        /**
          * Add an HTTP header to be included with the download request.  The header will be added to
          * the end of the list.
          * @param header HTTP header name
@@ -469,9 +535,33 @@
          *
          * @param show whether the download manager should show a notification for this download.
          * @return this object
+         * @deprecated use {@link #setNotificationVisibility(int)}
          */
+        @Deprecated
         public Request setShowRunningNotification(boolean show) {
-            mShowNotification = show;
+            return (show) ? setNotificationVisibility(VISIBILITY_VISIBLE) :
+                    setNotificationVisibility(VISIBILITY_HIDDEN);
+        }
+
+        /**
+         * Control whether a system notification is posted by the download manager while this
+         * download is running or when it is completed.
+         * If enabled, the download manager posts notifications about downloads
+         * through the system {@link android.app.NotificationManager}.
+         * By default, a notification is shown only when the download is in progress.
+         *<p>
+         * It can take the following values: {@link #VISIBILITY_HIDDEN},
+         * {@link #VISIBILITY_VISIBLE},
+         * {@link #VISIBILITY_VISIBLE_NOTIFY_COMPLETED}.
+         *<p>
+         * If set to {@link #VISIBILITY_HIDDEN}, this requires the permission
+         * android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
+         *
+         * @param visibility the visibility setting value
+         * @return this object
+         */
+        public Request setNotificationVisibility(int visibility) {
+            mNotificationVisibility = visibility;
             return this;
         }
 
@@ -514,30 +604,30 @@
         ContentValues toContentValues(String packageName) {
             ContentValues values = new ContentValues();
             assert mUri != null;
-            values.put(Downloads.COLUMN_URI, mUri.toString());
+            values.put(Downloads.Impl.COLUMN_URI, mUri.toString());
             values.put(Downloads.Impl.COLUMN_IS_PUBLIC_API, true);
-            values.put(Downloads.COLUMN_NOTIFICATION_PACKAGE, packageName);
+            values.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, packageName);
 
             if (mDestinationUri != null) {
-                values.put(Downloads.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_FILE_URI);
-                values.put(Downloads.COLUMN_FILE_NAME_HINT, mDestinationUri.toString());
+                values.put(Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_FILE_URI);
+                values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT, mDestinationUri.toString());
             } else {
-                values.put(Downloads.COLUMN_DESTINATION,
-                           Downloads.DESTINATION_CACHE_PARTITION_PURGEABLE);
+                values.put(Downloads.Impl.COLUMN_DESTINATION,
+                           Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
             }
+            // is the file supposed to be media-scannable?
+            values.put(Downloads.Impl.COLUMN_MEDIA_SCANNED, (mScannable) ? SCANNABLE_VALUE_YES :
+                    SCANNABLE_VALUE_NO);
 
             if (!mRequestHeaders.isEmpty()) {
                 encodeHttpHeaders(values);
             }
 
-            putIfNonNull(values, Downloads.COLUMN_TITLE, mTitle);
-            putIfNonNull(values, Downloads.COLUMN_DESCRIPTION, mDescription);
-            putIfNonNull(values, Downloads.COLUMN_MIME_TYPE, mMimeType);
+            putIfNonNull(values, Downloads.Impl.COLUMN_TITLE, mTitle);
+            putIfNonNull(values, Downloads.Impl.COLUMN_DESCRIPTION, mDescription);
+            putIfNonNull(values, Downloads.Impl.COLUMN_MIME_TYPE, mMimeType);
 
-            values.put(Downloads.COLUMN_VISIBILITY,
-                    mShowNotification ? Downloads.VISIBILITY_VISIBLE
-                            : Downloads.VISIBILITY_HIDDEN);
-
+            values.put(Downloads.Impl.COLUMN_VISIBILITY, mNotificationVisibility);
             values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, mAllowedNetworkTypes);
             values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed);
             values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, mIsVisibleInDownloadsUi);
@@ -579,7 +669,7 @@
 
         private long[] mIds = null;
         private Integer mStatusFlags = null;
-        private String mOrderByColumn = Downloads.COLUMN_LAST_MODIFICATION;
+        private String mOrderByColumn = Downloads.Impl.COLUMN_LAST_MODIFICATION;
         private int mOrderDirection = ORDER_DESCENDING;
         private boolean mOnlyIncludeVisibleInDownloadsUi = false;
 
@@ -631,9 +721,9 @@
             }
 
             if (column.equals(COLUMN_LAST_MODIFIED_TIMESTAMP)) {
-                mOrderByColumn = Downloads.COLUMN_LAST_MODIFICATION;
+                mOrderByColumn = Downloads.Impl.COLUMN_LAST_MODIFICATION;
             } else if (column.equals(COLUMN_TOTAL_SIZE_BYTES)) {
-                mOrderByColumn = Downloads.COLUMN_TOTAL_BYTES;
+                mOrderByColumn = Downloads.Impl.COLUMN_TOTAL_BYTES;
             } else {
                 throw new IllegalArgumentException("Cannot order by " + column);
             }
@@ -659,10 +749,10 @@
             if (mStatusFlags != null) {
                 List<String> parts = new ArrayList<String>();
                 if ((mStatusFlags & STATUS_PENDING) != 0) {
-                    parts.add(statusClause("=", Downloads.STATUS_PENDING));
+                    parts.add(statusClause("=", Downloads.Impl.STATUS_PENDING));
                 }
                 if ((mStatusFlags & STATUS_RUNNING) != 0) {
-                    parts.add(statusClause("=", Downloads.STATUS_RUNNING));
+                    parts.add(statusClause("=", Downloads.Impl.STATUS_RUNNING));
                 }
                 if ((mStatusFlags & STATUS_PAUSED) != 0) {
                     parts.add(statusClause("=", Downloads.Impl.STATUS_PAUSED_BY_APP));
@@ -671,7 +761,7 @@
                     parts.add(statusClause("=", Downloads.Impl.STATUS_QUEUED_FOR_WIFI));
                 }
                 if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) {
-                    parts.add(statusClause("=", Downloads.STATUS_SUCCESS));
+                    parts.add(statusClause("=", Downloads.Impl.STATUS_SUCCESS));
                 }
                 if ((mStatusFlags & STATUS_FAILED) != 0) {
                     parts.add("(" + statusClause(">=", 400)
@@ -708,7 +798,7 @@
         }
 
         private String statusClause(String operator, int value) {
-            return Downloads.COLUMN_STATUS + operator + "'" + value + "'";
+            return Downloads.Impl.COLUMN_STATUS + operator + "'" + value + "'";
         }
     }
 
@@ -747,7 +837,7 @@
      */
     public long enqueue(Request request) {
         ContentValues values = request.toContentValues(mPackageName);
-        Uri downloadUri = mResolver.insert(Downloads.CONTENT_URI, values);
+        Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values);
         long id = Long.parseLong(downloadUri.getLastPathSegment());
         return id;
     }
@@ -813,6 +903,58 @@
     }
 
     /**
+     * Returns {@link Uri} for the given downloaded file id, if the file is
+     * downloaded successfully. otherwise, null is returned.
+     *<p>
+     * If the specified downloaded file is in external storage (for example, /sdcard dir),
+     * then it is assumed to be safe for anyone to read and the returned {@link Uri} can be used
+     * by any app to access the downloaded file.
+     *
+     * @param id the id of the downloaded file.
+     * @return the {@link Uri} for the given downloaded file id, if download was successful. null
+     * otherwise.
+     */
+    public Uri getUriForDownloadedFile(long id) {
+        // to check if the file is in cache, get its destination from the database
+        Query query = new Query().setFilterById(id);
+        Cursor cursor = null;
+        try {
+            cursor = query(query);
+            if (cursor == null) {
+                return null;
+            }
+            while (cursor.moveToFirst()) {
+                int status = cursor.getInt(cursor.getColumnIndexOrThrow(
+                        DownloadManager.COLUMN_STATUS));
+                if (DownloadManager.STATUS_SUCCESSFUL == status) {
+                    int indx = cursor.getColumnIndexOrThrow(
+                            Downloads.Impl.COLUMN_DESTINATION);
+                    int destination = cursor.getInt(indx);
+                    // TODO: if we ever add API to DownloadManager to let the caller specify
+                    // non-external storage for a downloaded file, then the following code
+                    // should also check for that destination.
+                    if (destination == Downloads.Impl.DESTINATION_CACHE_PARTITION ||
+                            destination == Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING ||
+                            destination == Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE) {
+                        // return private uri
+                        return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, id);
+                    } else {
+                        // return public uri
+                        return ContentUris.withAppendedId(
+                                Downloads.Impl.PUBLICLY_ACCESSIBLE_DOWNLOADS_URI, id);
+                    }
+                }
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        // downloaded file not found or its status is not 'successfully completed'
+        return null;
+    }
+
+    /**
      * Restart the given downloads, which must have already completed (successfully or not).  This
      * method will only work when called from within the download manager's process.
      * @param ids the IDs of the downloads
@@ -969,16 +1111,19 @@
                 return Long.toString(translateLong(column));
             }
             if (column.equals(COLUMN_TITLE)) {
-                return getUnderlyingString(Downloads.COLUMN_TITLE);
+                return getUnderlyingString(Downloads.Impl.COLUMN_TITLE);
             }
             if (column.equals(COLUMN_DESCRIPTION)) {
-                return getUnderlyingString(Downloads.COLUMN_DESCRIPTION);
+                return getUnderlyingString(Downloads.Impl.COLUMN_DESCRIPTION);
             }
             if (column.equals(COLUMN_URI)) {
-                return getUnderlyingString(Downloads.COLUMN_URI);
+                return getUnderlyingString(Downloads.Impl.COLUMN_URI);
             }
             if (column.equals(COLUMN_MEDIA_TYPE)) {
-                return getUnderlyingString(Downloads.COLUMN_MIME_TYPE);
+                return getUnderlyingString(Downloads.Impl.COLUMN_MIME_TYPE);
+            }
+            if (column.equals(COLUMN_LOCAL_FILENAME)) {
+                return getUnderlyingString(Downloads.Impl._DATA);
             }
             if (column.equals(COLUMN_MEDIAPROVIDER_URI)) {
                 return getUnderlyingString(Downloads.Impl.COLUMN_MEDIAPROVIDER_URI);
@@ -1019,19 +1164,22 @@
                 return getUnderlyingLong(Downloads.Impl._ID);
             }
             if (column.equals(COLUMN_TOTAL_SIZE_BYTES)) {
-                return getUnderlyingLong(Downloads.COLUMN_TOTAL_BYTES);
+                return getUnderlyingLong(Downloads.Impl.COLUMN_TOTAL_BYTES);
             }
             if (column.equals(COLUMN_STATUS)) {
-                return translateStatus((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
+                return translateStatus((int) getUnderlyingLong(Downloads.Impl.COLUMN_STATUS));
             }
             if (column.equals(COLUMN_REASON)) {
-                return getReason((int) getUnderlyingLong(Downloads.COLUMN_STATUS));
+                return getReason((int) getUnderlyingLong(Downloads.Impl.COLUMN_STATUS));
             }
             if (column.equals(COLUMN_BYTES_DOWNLOADED_SO_FAR)) {
-                return getUnderlyingLong(Downloads.COLUMN_CURRENT_BYTES);
+                return getUnderlyingLong(Downloads.Impl.COLUMN_CURRENT_BYTES);
+            }
+            if (column.equals(Downloads.Impl.COLUMN_DESTINATION)) {
+                return getUnderlyingLong(Downloads.Impl.COLUMN_DESTINATION);
             }
             assert column.equals(COLUMN_LAST_MODIFIED_TIMESTAMP);
-            return getUnderlyingLong(Downloads.COLUMN_LAST_MODIFICATION);
+            return getUnderlyingLong(Downloads.Impl.COLUMN_LAST_MODIFICATION);
         }
 
         private long getReason(int status) {
@@ -1071,23 +1219,23 @@
             }
 
             switch (status) {
-                case Downloads.STATUS_FILE_ERROR:
+                case Downloads.Impl.STATUS_FILE_ERROR:
                     return ERROR_FILE_ERROR;
 
-                case Downloads.STATUS_UNHANDLED_HTTP_CODE:
-                case Downloads.STATUS_UNHANDLED_REDIRECT:
+                case Downloads.Impl.STATUS_UNHANDLED_HTTP_CODE:
+                case Downloads.Impl.STATUS_UNHANDLED_REDIRECT:
                     return ERROR_UNHANDLED_HTTP_CODE;
 
-                case Downloads.STATUS_HTTP_DATA_ERROR:
+                case Downloads.Impl.STATUS_HTTP_DATA_ERROR:
                     return ERROR_HTTP_DATA_ERROR;
 
-                case Downloads.STATUS_TOO_MANY_REDIRECTS:
+                case Downloads.Impl.STATUS_TOO_MANY_REDIRECTS:
                     return ERROR_TOO_MANY_REDIRECTS;
 
-                case Downloads.STATUS_INSUFFICIENT_SPACE_ERROR:
+                case Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR:
                     return ERROR_INSUFFICIENT_SPACE;
 
-                case Downloads.STATUS_DEVICE_NOT_FOUND_ERROR:
+                case Downloads.Impl.STATUS_DEVICE_NOT_FOUND_ERROR:
                     return ERROR_DEVICE_NOT_FOUND;
 
                 case Downloads.Impl.STATUS_CANNOT_RESUME:
@@ -1111,10 +1259,10 @@
 
         private int translateStatus(int status) {
             switch (status) {
-                case Downloads.STATUS_PENDING:
+                case Downloads.Impl.STATUS_PENDING:
                     return STATUS_PENDING;
 
-                case Downloads.STATUS_RUNNING:
+                case Downloads.Impl.STATUS_RUNNING:
                     return STATUS_RUNNING;
 
                 case Downloads.Impl.STATUS_PAUSED_BY_APP:
@@ -1123,11 +1271,11 @@
                 case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
                     return STATUS_PAUSED;
 
-                case Downloads.STATUS_SUCCESS:
+                case Downloads.Impl.STATUS_SUCCESS:
                     return STATUS_SUCCESSFUL;
 
                 default:
-                    assert Downloads.isStatusError(status);
+                    assert Downloads.Impl.isStatusError(status);
                     return STATUS_FAILED;
             }
         }
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
new file mode 100644
index 0000000..eaf1aee
--- /dev/null
+++ b/core/java/android/app/Fragment.java
@@ -0,0 +1,1226 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.animation.Animator;
+import android.content.ComponentCallbacks;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AndroidRuntimeException;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnCreateContextMenuListener;
+import android.widget.AdapterView;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+
+final class FragmentState implements Parcelable {
+    final String mClassName;
+    final int mIndex;
+    final boolean mFromLayout;
+    final int mFragmentId;
+    final int mContainerId;
+    final String mTag;
+    final boolean mRetainInstance;
+    final Bundle mArguments;
+    
+    Bundle mSavedFragmentState;
+    
+    Fragment mInstance;
+    
+    public FragmentState(Fragment frag) {
+        mClassName = frag.getClass().getName();
+        mIndex = frag.mIndex;
+        mFromLayout = frag.mFromLayout;
+        mFragmentId = frag.mFragmentId;
+        mContainerId = frag.mContainerId;
+        mTag = frag.mTag;
+        mRetainInstance = frag.mRetainInstance;
+        mArguments = frag.mArguments;
+    }
+    
+    public FragmentState(Parcel in) {
+        mClassName = in.readString();
+        mIndex = in.readInt();
+        mFromLayout = in.readInt() != 0;
+        mFragmentId = in.readInt();
+        mContainerId = in.readInt();
+        mTag = in.readString();
+        mRetainInstance = in.readInt() != 0;
+        mArguments = in.readBundle();
+        mSavedFragmentState = in.readBundle();
+    }
+    
+    public Fragment instantiate(Activity activity) {
+        if (mInstance != null) {
+            return mInstance;
+        }
+        
+        if (mArguments != null) {
+            mArguments.setClassLoader(activity.getClassLoader());
+        }
+        
+        mInstance = Fragment.instantiate(activity, mClassName, mArguments);
+        
+        if (mSavedFragmentState != null) {
+            mSavedFragmentState.setClassLoader(activity.getClassLoader());
+            mInstance.mSavedFragmentState = mSavedFragmentState;
+        }
+        mInstance.setIndex(mIndex);
+        mInstance.mFromLayout = mFromLayout;
+        mInstance.mFragmentId = mFragmentId;
+        mInstance.mContainerId = mContainerId;
+        mInstance.mTag = mTag;
+        mInstance.mRetainInstance = mRetainInstance;
+        mInstance.mFragmentManager = activity.mFragments;
+        
+        return mInstance;
+    }
+    
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mClassName);
+        dest.writeInt(mIndex);
+        dest.writeInt(mFromLayout ? 1 : 0);
+        dest.writeInt(mFragmentId);
+        dest.writeInt(mContainerId);
+        dest.writeString(mTag);
+        dest.writeInt(mRetainInstance ? 1 : 0);
+        dest.writeBundle(mArguments);
+        dest.writeBundle(mSavedFragmentState);
+    }
+    
+    public static final Parcelable.Creator<FragmentState> CREATOR
+            = new Parcelable.Creator<FragmentState>() {
+        public FragmentState createFromParcel(Parcel in) {
+            return new FragmentState(in);
+        }
+        
+        public FragmentState[] newArray(int size) {
+            return new FragmentState[size];
+        }
+    };
+}
+
+/**
+ * A Fragment is a piece of an application's user interface or behavior
+ * that can be placed in an {@link Activity}.  Interaction with fragments
+ * is done through {@link FragmentManager}, which can be obtained via
+ * {@link Activity#getFragmentManager() Activity.getFragmentManager()} and
+ * {@link Fragment#getFragmentManager() Fragment.getFragmentManager()}.
+ *
+ * <p>The Fragment class can be used many ways to achieve a wide variety of
+ * results.  It is core, it represents a particular operation or interface
+ * that is running within a larger {@link Activity}.  A Fragment is closely
+ * tied to the Activity it is in, and can not be used apart from one.  Though
+ * Fragment defines its own lifecycle, that lifecycle is dependent on its
+ * activity: if the activity is stopped, no fragments inside of it can be
+ * started; when the activity is destroyed, all fragments will be destroyed.
+ *
+ * <p>All subclasses of Fragment must include a public empty constructor.
+ * The framework will often re-instantiate a fragment class when needed,
+ * in particular during state restore, and needs to be able to find this
+ * constructor to instantiate it.  If the empty constructor is not available,
+ * a runtime exception will occur in some cases during state restore.
+ *
+ * <p>Topics covered here:
+ * <ol>
+ * <li><a href="#Lifecycle">Lifecycle</a>
+ * <li><a href="#Layout">Layout</a>
+ * <li><a href="#BackStack">Back Stack</a>
+ * </ol>
+ *
+ * <a name="Lifecycle"></a>
+ * <h3>Lifecycle</h3>
+ *
+ * <p>Though a Fragment's lifecycle is tied to its owning activity, it has
+ * its own wrinkle on the standard activity lifecycle.  It includes basic
+ * activity lifecycle methods such as {@link #onResume}, but also important
+ * are methods related to interactions with the activity and UI generation.
+ *
+ * <p>The core series of lifecycle methods that are called to bring a fragment
+ * up to resumed state (interacting with the user) are:
+ *
+ * <ol>
+ * <li> {@link #onAttach} called once the fragment is associated with its activity.
+ * <li> {@link #onCreate} called to do initial creation of the fragment.
+ * <li> {@link #onCreateView} creates and returns the view hierarchy associated
+ * with the fragment.
+ * <li> {@link #onActivityCreated} tells the fragment that its activity has
+ * completed its own {@link Activity#onCreate Activity.onCreaate}.
+ * <li> {@link #onStart} makes the fragment visible to the user (based on its
+ * containing activity being started).
+ * <li> {@link #onResume} makes the fragment interacting with the user (based on its
+ * containing activity being resumed).
+ * </ol>
+ *
+ * <p>As a fragment is no longer being used, it goes through a reverse
+ * series of callbacks:
+ *
+ * <ol>
+ * <li> {@link #onPause} fragment is no longer interacting with the user either
+ * because its activity is being paused or a fragment operation is modifying it
+ * in the activity.
+ * <li> {@link #onStop} fragment is no longer visible to the user either
+ * because its activity is being stopped or a fragment operation is modifying it
+ * in the activity.
+ * <li> {@link #onDestroyView} allows the fragment to clean up resources
+ * associated with its View.
+ * <li> {@link #onDestroy} called to do final cleanup of the fragment's state.
+ * <li> {@link #onDetach} called immediately prior to the fragment no longer
+ * being associated with its activity.
+ * </ol>
+ *
+ * <a name="Layout"></a>
+ * <h3>Layout</h3>
+ *
+ * <p>Fragments can be used as part of your application's layout, allowing
+ * you to better modularize your code and more easily adjust your user
+ * interface to the screen it is running on.  As an example, we can look
+ * at a simple program consisting of a list of items, and display of the
+ * details of each item.</p>
+ *
+ * <p>An activity's layout XML can include <code>&lt;fragment&gt;</code> tags
+ * to embed fragment instances inside of the layout.  For example, here is
+ * a simple layout that embeds one fragment:</p>
+ *
+ * {@sample development/samples/ApiDemos/res/layout/fragment_layout.xml layout}
+ *
+ * <p>The layout is installed in the activity in the normal way:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
+ *      main}
+ *
+ * <p>The titles fragment, showing a list of titles, is very simple, relying
+ * on {@link ListFragment} for most of its work.  Note the implementation of
+ * clicking an item, which can either update
+ * the content of the details fragment or start a new activity show the
+ * details depending on whether the current activity's layout can show the
+ * details.</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
+ *      titles}
+ *
+ * <p>The details fragment showing the contents of selected item here just
+ * displays a string of text based on an index of a string array built in to
+ * the app:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
+ *      details}
+ *
+ * <p>In this case when the user clicks on a title, there is no details
+ * fragment in the current activity, so the title title fragment's click code will
+ * launch a new activity to display the details fragment:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
+ *      details_activity}
+ *
+ * <p>However the screen may be large enough to show both the list of titles
+ * and details about the currently selected title.  To use such a layout on
+ * a landscape screen, this alternative layout can be placed under layout-land:</p>
+ *
+ * {@sample development/samples/ApiDemos/res/layout-land/fragment_layout.xml layout}
+ *
+ * <p>Note how the prior code will adjust to this alternative UI flow: the
+ * titles fragment will now show its text inside of its activity, and the
+ * details activity will finish of it finds itself running in a configuration
+ * where the details can be shown inline.
+ *
+ * <p>When a configuration change causes the activity hosting these fragments
+ * to restart, its new instance may use a different layout that doesn't
+ * include the same fragments as the previous layout.  In this case all of
+ * the previous fragments will still be instantiated and running in the new
+ * instance; however, any that are no longer associated with a &lt;fragment&gt;
+ * tag in the view hierarchy will not have their content view created and will
+ * return false from {@link #isInLayout}.
+ * 
+ * <p>The attributes of the &lt;fragment&gt; tag are used to control the
+ * LayoutParams provider when attaching the fragment's view to the parent
+ * container.  They can alse be parsed by the fragment in {@link #onInflate}
+ * as parameters.
+ * 
+ * <p>The fragment being instantiated must have some kind of unique identifier
+ * so that it can be re-associated with a previous instance if the parent
+ * activity needs to be destroyed and recreated.  This can be provided these
+ * ways:
+ * 
+ * <ul>
+ * <li>If nothing is explicitly supplied, the view ID of the container will
+ * be used.
+ * <li><code>android:tag</code> can be used in &lt;fragment&gt; to provide
+ * a specific tag name for the fragment.
+ * <li><code>android:id</code> can be used in &lt;fragment&gt; to provide
+ * a specific identifier for the fragment.
+ * </ul>
+ * 
+ * <a name="BackStack"></a>
+ * <h3>Back Stack</h3>
+ *
+ * <p>The transaction in which fragments are modified can be placed on an
+ * internal back-stack of the owning activity.  When the user presses back
+ * in the activity, any transactions on the back stack are popped off before
+ * the activity itself is finished.
+ *
+ * <p>For example, consider this simple fragment that is instantiated with
+ * an integer argument and displays that in a TextView in its UI:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java
+ *      fragment}
+ *
+ * <p>A function that creates a new instance of the fragment, replacing
+ * whatever current fragment instance is being shown and pushing that change
+ * on to the back stack could be written as:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentStack.java
+ *      add_stack}
+ *
+ * <p>After each call to this function, a new entry is on the stack, and
+ * pressing back will pop it to return the user to whatever previous state
+ * the activity UI was in.
+ */
+public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener {
+    private static final HashMap<String, Class<?>> sClassMap =
+            new HashMap<String, Class<?>>();
+    
+    static final int INITIALIZING = 0;     // Not yet created.
+    static final int CREATED = 1;          // Created.
+    static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
+    static final int STARTED = 3;          // Created and started, not resumed.
+    static final int RESUMED = 4;          // Created started and resumed.
+    
+    int mState = INITIALIZING;
+    
+    // When instantiated from saved state, this is the saved state.
+    Bundle mSavedFragmentState;
+    SparseArray<Parcelable> mSavedViewState;
+    
+    // Index into active fragment array.
+    int mIndex = -1;
+    
+    // Internal unique name for this fragment;
+    String mWho;
+    
+    // Construction arguments;
+    Bundle mArguments;
+
+    // Target fragment.
+    Fragment mTarget;
+
+    // Target request code.
+    int mTargetRequestCode;
+
+    // True if the fragment is in the list of added fragments.
+    boolean mAdded;
+    
+    // True if the fragment is in the resumed state.
+    boolean mResumed;
+    
+    // Set to true if this fragment was instantiated from a layout file.
+    boolean mFromLayout;
+    
+    // Set to true when the view has actually been inflated in its layout.
+    boolean mInLayout;
+
+    // Number of active back stack entries this fragment is in.
+    int mBackStackNesting;
+    
+    // The fragment manager we are associated with.  Set as soon as the
+    // fragment is used in a transaction; cleared after it has been removed
+    // from all transactions.
+    FragmentManager mFragmentManager;
+
+    // Set as soon as a fragment is added to a transaction (or removed),
+    // to be able to do validation.
+    Activity mImmediateActivity;
+    
+    // Activity this fragment is attached to.
+    Activity mActivity;
+    
+    // The optional identifier for this fragment -- either the container ID if it
+    // was dynamically added to the view hierarchy, or the ID supplied in
+    // layout.
+    int mFragmentId;
+    
+    // When a fragment is being dynamically added to the view hierarchy, this
+    // is the identifier of the parent container it is being added to.
+    int mContainerId;
+    
+    // The optional named tag for this fragment -- usually used to find
+    // fragments that are not part of the layout.
+    String mTag;
+    
+    // Set to true when the app has requested that this fragment be hidden
+    // from the user.
+    boolean mHidden;
+    
+    // If set this fragment would like its instance retained across
+    // configuration changes.
+    boolean mRetainInstance;
+    
+    // If set this fragment is being retained across the current config change.
+    boolean mRetaining;
+    
+    // If set this fragment has menu items to contribute.
+    boolean mHasMenu;
+    
+    // Used to verify that subclasses call through to super class.
+    boolean mCalled;
+    
+    // If app has requested a specific animation, this is the one to use.
+    int mNextAnim;
+    
+    // The parent container of the fragment after dynamically added to UI.
+    ViewGroup mContainer;
+    
+    // The View generated for this fragment.
+    View mView;
+    
+    LoaderManagerImpl mLoaderManager;
+    boolean mLoadersStarted;
+    boolean mCheckedForLoaderManager;
+    
+    /**
+     * Thrown by {@link Fragment#instantiate(Context, String, Bundle)} when
+     * there is an instantiation failure.
+     */
+    static public class InstantiationException extends AndroidRuntimeException {
+        public InstantiationException(String msg, Exception cause) {
+            super(msg, cause);
+        }
+    }
+
+    /**
+     * Default constructor.  <strong>Every</strong> fragment must have an
+     * empty constructor, so it can be instantiated when restoring its
+     * activity's state.  It is strongly recommended that subclasses do not
+     * have other constructors with parameters, since these constructors
+     * will not be called when the fragment is re-instantiated; instead,
+     * arguments can be supplied by the caller with {@link #setArguments}
+     * and later retrieved by the Fragment with {@link #getArguments}.
+     * 
+     * <p>Applications should generally not implement a constructor.  The
+     * first place application code an run where the fragment is ready to
+     * be used is in {@link #onAttach(Activity)}, the point where the fragment
+     * is actually associated with its activity.  Some applications may also
+     * want to implement {@link #onInflate} to retrieve attributes from a
+     * layout resource, though should take care here because this happens for
+     * the fragment is attached to its activity.
+     */
+    public Fragment() {
+    }
+
+    /**
+     * Like {@link #instantiate(Context, String, Bundle)} but with a null
+     * argument Bundle.
+     */
+    public static Fragment instantiate(Context context, String fname) {
+        return instantiate(context, fname, null);
+    }
+
+    /**
+     * Create a new instance of a Fragment with the given class name.  This is
+     * the same as calling its empty constructor.
+     *
+     * @param context The calling context being used to instantiate the fragment.
+     * This is currently just used to get its ClassLoader.
+     * @param fname The class name of the fragment to instantiate.
+     * @param args Bundle of arguments to supply to the fragment, which it
+     * can retrieve with {@link #getArguments()}.  May be null.
+     * @return Returns a new fragment instance.
+     * @throws InstantiationException If there is a failure in instantiating
+     * the given fragment class.  This is a runtime exception; it is not
+     * normally expected to happen.
+     */
+    public static Fragment instantiate(Context context, String fname, Bundle args) {
+        try {
+            Class<?> clazz = sClassMap.get(fname);
+            if (clazz == null) {
+                // Class not found in the cache, see if it's real, and try to add it
+                clazz = context.getClassLoader().loadClass(fname);
+                sClassMap.put(fname, clazz);
+            }
+            Fragment f = (Fragment)clazz.newInstance();
+            if (args != null) {
+                args.setClassLoader(f.getClass().getClassLoader());
+                f.mArguments = args;
+            }
+            return f;
+        } catch (ClassNotFoundException e) {
+            throw new InstantiationException("Unable to instantiate fragment " + fname
+                    + ": make sure class name exists, is public, and has an"
+                    + " empty constructor that is public", e);
+        } catch (java.lang.InstantiationException e) {
+            throw new InstantiationException("Unable to instantiate fragment " + fname
+                    + ": make sure class name exists, is public, and has an"
+                    + " empty constructor that is public", e);
+        } catch (IllegalAccessException e) {
+            throw new InstantiationException("Unable to instantiate fragment " + fname
+                    + ": make sure class name exists, is public, and has an"
+                    + " empty constructor that is public", e);
+        }
+    }
+    
+    void restoreViewState() {
+        if (mSavedViewState != null) {
+            mView.restoreHierarchyState(mSavedViewState);
+            mSavedViewState = null;
+        }
+    }
+    
+    void setIndex(int index) {
+        mIndex = index;
+        mWho = "android:fragment:" + mIndex;
+   }
+    
+    void clearIndex() {
+        mIndex = -1;
+        mWho = null;
+    }
+    
+    /**
+     * Subclasses can not override equals().
+     */
+    @Override final public boolean equals(Object o) {
+        return super.equals(o);
+    }
+
+    /**
+     * Subclasses can not override hashCode().
+     */
+    @Override final public int hashCode() {
+        return super.hashCode();
+    }
+    
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("Fragment{");
+        sb.append(Integer.toHexString(System.identityHashCode(this)));
+        if (mIndex >= 0) {
+            sb.append(" #");
+            sb.append(mIndex);
+        }
+        if (mFragmentId != 0) {
+            sb.append(" id=0x");
+            sb.append(Integer.toHexString(mFragmentId));
+        }
+        if (mTag != null) {
+            sb.append(" ");
+            sb.append(mTag);
+        }
+        sb.append('}');
+        return sb.toString();
+    }
+    
+    /**
+     * Return the identifier this fragment is known by.  This is either
+     * the android:id value supplied in a layout or the container view ID
+     * supplied when adding the fragment.
+     */
+    final public int getId() {
+        return mFragmentId;
+    }
+    
+    /**
+     * Get the tag name of the fragment, if specified.
+     */
+    final public String getTag() {
+        return mTag;
+    }
+    
+    /**
+     * Supply the construction arguments for this fragment.  This can only
+     * be called before the fragment has been attached to its activity; that
+     * is, you should call it immediately after constructing the fragment.  The
+     * arguments supplied here will be retained across fragment destroy and
+     * creation.
+     */
+    public void setArguments(Bundle args) {
+        if (mIndex >= 0) {
+            throw new IllegalStateException("Fragment already active");
+        }
+        mArguments = args;
+    }
+
+    /**
+     * Return the arguments supplied when the fragment was instantiated,
+     * if any.
+     */
+    final public Bundle getArguments() {
+        return mArguments;
+    }
+
+    /**
+     * Optional target for this fragment.  This may be used, for example,
+     * if this fragment is being started by another, and when done wants to
+     * give a result back to the first.  The target set here is retained
+     * across instances via {@link FragmentManager#putFragment
+     * FragmentManager.putFragment()}.
+     *
+     * @param fragment The fragment that is the target of this one.
+     * @param requestCode Optional request code, for convenience if you
+     * are going to call back with {@link #onActivityResult(int, int, Intent)}.
+     */
+    public void setTargetFragment(Fragment fragment, int requestCode) {
+        mTarget = fragment;
+        mTargetRequestCode = requestCode;
+    }
+
+    /**
+     * Return the target fragment set by {@link #setTargetFragment}.
+     */
+    final public Fragment getTargetFragment() {
+        return mTarget;
+    }
+
+    /**
+     * Return the target request code set by {@link #setTargetFragment}.
+     */
+    final public int getTargetRequestCode() {
+        return mTargetRequestCode;
+    }
+
+    /**
+     * Return the Activity this fragment is currently associated with.
+     */
+    final public Activity getActivity() {
+        return mActivity;
+    }
+    
+    /**
+     * Return the FragmentManager for interacting with fragments associated
+     * with this fragment's activity.  Note that this will be non-null slightly
+     * before {@link #getActivity()}, during the time from when the fragment is
+     * placed in a {@link FragmentTransaction} until it is committed and
+     * attached to its activity.
+     */
+    final public FragmentManager getFragmentManager() {
+        return mFragmentManager;
+    }
+
+    /**
+     * Return true if the fragment is currently added to its activity.
+     */
+    final public boolean isAdded() {
+        return mActivity != null && mActivity.mFragments.mAdded.contains(this);
+    }
+    
+    /**
+     * Return true if the layout is included as part of an activity view
+     * hierarchy via the &lt;fragment&gt; tag.  This will always be true when
+     * fragments are created through the &lt;fragment&gt; tag, <em>except</em>
+     * in the case where an old fragment is restored from a previous state and
+     * it does not appear in the layout of the current state.
+     */
+    final public boolean isInLayout() {
+        return mInLayout;
+    }
+
+    /**
+     * Return true if the fragment is in the resumed state.  This is true
+     * for the duration of {@link #onResume()} and {@link #onPause()} as well.
+     */
+    final public boolean isResumed() {
+        return mResumed;
+    }
+    
+    /**
+     * Return true if the fragment is currently visible to the user.  This means
+     * it: (1) has been added, (2) has its view attached to the window, and 
+     * (3) is not hidden.
+     */
+    final public boolean isVisible() {
+        return isAdded() && !isHidden() && mView != null
+                && mView.getWindowToken() != null && mView.getVisibility() == View.VISIBLE;
+    }
+    
+    /**
+     * Return true if the fragment has been hidden.  By default fragments
+     * are shown.  You can find out about changes to this state with
+     * {@link #onHiddenChanged}.  Note that the hidden state is orthogonal
+     * to other states -- that is, to be visible to the user, a fragment
+     * must be both started and not hidden.
+     */
+    final public boolean isHidden() {
+        return mHidden;
+    }
+    
+    /**
+     * Called when the hidden state (as returned by {@link #isHidden()} of
+     * the fragment has changed.  Fragments start out not hidden; this will
+     * be called whenever the fragment changes state from that.
+     * @param hidden True if the fragment is now hidden, false if it is not
+     * visible.
+     */
+    public void onHiddenChanged(boolean hidden) {
+    }
+    
+    /**
+     * Control whether a fragment instance is retained across Activity
+     * re-creation (such as from a configuration change).  This can only
+     * be used with fragments not in the back stack.  If set, the fragment
+     * lifecycle will be slightly different when an activity is recreated:
+     * <ul>
+     * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
+     * will be, because the fragment is being detached from its current activity).
+     * <li> {@link #onCreate(Bundle)} will not be called since the fragment
+     * is not being re-created.
+     * <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b>
+     * still be called.
+     * </ul>
+     */
+    public void setRetainInstance(boolean retain) {
+        mRetainInstance = retain;
+    }
+    
+    final public boolean getRetainInstance() {
+        return mRetainInstance;
+    }
+    
+    /**
+     * Report that this fragment would like to participate in populating
+     * the options menu by receiving a call to {@link #onCreateOptionsMenu}
+     * and related methods.
+     * 
+     * @param hasMenu If true, the fragment has menu items to contribute.
+     */
+    public void setHasOptionsMenu(boolean hasMenu) {
+        if (mHasMenu != hasMenu) {
+            mHasMenu = hasMenu;
+            if (isAdded() && !isHidden()) {
+                mActivity.invalidateOptionsMenu();
+            }
+        }
+    }
+    
+    /**
+     * Return the LoaderManager for this fragment, creating it if needed.
+     */
+    public LoaderManager getLoaderManager() {
+        if (mLoaderManager != null) {
+            return mLoaderManager;
+        }
+        mCheckedForLoaderManager = true;
+        mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, true);
+        return mLoaderManager;
+    }
+    
+    /**
+     * Call {@link Activity#startActivity(Intent)} on the fragment's
+     * containing Activity.
+     */
+    public void startActivity(Intent intent) {
+        mActivity.startActivityFromFragment(this, intent, -1);
+    }
+    
+    /**
+     * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's
+     * containing Activity.
+     */
+    public void startActivityForResult(Intent intent, int requestCode) {
+        mActivity.startActivityFromFragment(this, intent, requestCode);
+    }
+    
+    /**
+     * Receive the result from a previous call to
+     * {@link #startActivityForResult(Intent, int)}.  This follows the
+     * related Activity API as described there in
+     * {@link Activity#onActivityResult(int, int, Intent)}.
+     * 
+     * @param requestCode The integer request code originally supplied to
+     *                    startActivityForResult(), allowing you to identify who this
+     *                    result came from.
+     * @param resultCode The integer result code returned by the child activity
+     *                   through its setResult().
+     * @param data An Intent, which can return result data to the caller
+     *               (various data can be attached to Intent "extras").
+     */
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+    }
+    
+    /**
+     * Called when a fragment is being created as part of a view layout
+     * inflation, typically from setting the content view of an activity.  This
+     * will be called immediately after the fragment is created from a <fragment>
+     * tag in a layout file.  Note this is <em>before</em> the fragment's
+     * {@link #onAttach(Activity)} has been called; all you should do here is
+     * parse the attributes and save them away.  A convenient thing to do is
+     * simply copy them into a Bundle that is given to {@link #setArguments(Bundle)}.
+     * 
+     * <p>This is called every time the fragment is inflated, even if it is
+     * being inflated into a new instance with saved state.  Because a fragment's
+     * arguments are retained across instances, it may make no sense to re-parse
+     * the attributes into new arguments.  You may want to first check
+     * {@link #getArguments()} and only parse the attributes if it returns null,
+     * the assumption being that if it is non-null those are the same arguments
+     * from the first time the fragment was inflated.  (That said, you may want
+     * to have layouts change for different configurations such as landscape
+     * and portrait, which can have different attributes.  If so, you will need
+     * to re-parse the attributes each time this is called to generate new
+     * arguments.)</p>
+     * 
+     * @param attrs The attributes at the tag where the fragment is
+     * being created.
+     * @param savedInstanceState If the fragment is being re-created from
+     * a previous saved state, this is the state.
+     */
+    public void onInflate(AttributeSet attrs, Bundle savedInstanceState) {
+        mCalled = true;
+    }
+    
+    /**
+     * Called when a fragment is first attached to its activity.
+     * {@link #onCreate(Bundle)} will be called after this.
+     */
+    public void onAttach(Activity activity) {
+        mCalled = true;
+    }
+    
+    /**
+     * Called when a fragment loads an animation.
+     */
+    public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
+        return null;
+    }
+    
+    /**
+     * Called to do initial creation of a fragment.  This is called after
+     * {@link #onAttach(Activity)} and before
+     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
+     * 
+     * <p>Note that this can be called while the fragment's activity is
+     * still in the process of being created.  As such, you can not rely
+     * on things like the activity's content view hierarchy being initialized
+     * at this point.  If you want to do work once the activity itself is
+     * created, see {@link #onActivityCreated(Bundle)}.
+     * 
+     * @param savedInstanceState If the fragment is being re-created from
+     * a previous saved state, this is the state.
+     */
+    public void onCreate(Bundle savedInstanceState) {
+        mCalled = true;
+    }
+    
+    /**
+     * Called to have the fragment instantiate its user interface view.
+     * This is optional, and non-graphical fragments can return null (which
+     * is the default implementation).  This will be called between
+     * {@link #onCreate(Bundle)} and {@link #onActivityCreated(Bundle)}.
+     * 
+     * <p>If you return a View from here, you will later be called in
+     * {@link #onDestroyView} when the view is being released.
+     * 
+     * @param inflater The LayoutInflater object that can be used to inflate
+     * any views in the fragment,
+     * @param container If non-null, this is the parent view that the fragment's
+     * UI should be attached to.  The fragment should not add the view itself,
+     * but this can be used to generate the LayoutParams of the view.
+     * @param savedInstanceState If non-null, this fragment is being re-constructed
+     * from a previous saved state as given here.
+     * 
+     * @return Return the View for the fragment's UI, or null.
+     */
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return null;
+    }
+    
+    public View getView() {
+        return mView;
+    }
+    
+    /**
+     * Called when the fragment's activity has been created and this
+     * fragment's view hierarchy instantiated.  It can be used to do final
+     * initialization once these pieces are in place, such as retrieving
+     * views or restoring state.  It is also useful for fragments that use
+     * {@link #setRetainInstance(boolean)} to retain their instance,
+     * as this callback tells the fragment when it is fully associated with
+     * the new activity instance.  This is called after {@link #onCreateView}
+     * and before {@link #onStart()}.
+     * 
+     * @param savedInstanceState If the fragment is being re-created from
+     * a previous saved state, this is the state.
+     */
+    public void onActivityCreated(Bundle savedInstanceState) {
+        mCalled = true;
+    }
+    
+    /**
+     * Called when the Fragment is visible to the user.  This is generally
+     * tied to {@link Activity#onStart() Activity.onStart} of the containing
+     * Activity's lifecycle.
+     */
+    public void onStart() {
+        mCalled = true;
+        
+        if (!mLoadersStarted) {
+            mLoadersStarted = true;
+            if (!mCheckedForLoaderManager) {
+                mCheckedForLoaderManager = true;
+                mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false);
+            }
+            if (mLoaderManager != null) {
+                mLoaderManager.doStart();
+            }
+        }
+    }
+    
+    /**
+     * Called when the fragment is visible to the user and actively running.
+     * This is generally
+     * tied to {@link Activity#onResume() Activity.onResume} of the containing
+     * Activity's lifecycle.
+     */
+    public void onResume() {
+        mCalled = true;
+    }
+    
+    /**
+     * Called to ask the fragment to save its current dynamic state, so it
+     * can later be reconstructed in a new instance of its process is
+     * restarted.  If a new instance of the fragment later needs to be
+     * created, the data you place in the Bundle here will be available
+     * in the Bundle given to {@link #onCreate(Bundle)},
+     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}, and
+     * {@link #onActivityCreated(Bundle)}.
+     *
+     * <p>This corresponds to {@link Activity#onSaveInstanceState(Bundle)
+     * Activity.onSaveInstanceState(Bundle)} and most of the discussion there
+     * applies here as well.  Note however: <em>this method may be called
+     * at any time before {@link #onDestroy()}</em>.  There are many situations
+     * where a fragment may be mostly torn down (such as when placed on the
+     * back stack with no UI showing), but its state will not be saved until
+     * its owning activity actually needs to save its state.
+     *
+     * @param outState Bundle in which to place your saved state.
+     */
+    public void onSaveInstanceState(Bundle outState) {
+    }
+    
+    public void onConfigurationChanged(Configuration newConfig) {
+        mCalled = true;
+    }
+    
+    /**
+     * Called when the Fragment is no longer resumed.  This is generally
+     * tied to {@link Activity#onPause() Activity.onPause} of the containing
+     * Activity's lifecycle.
+     */
+    public void onPause() {
+        mCalled = true;
+    }
+    
+    /**
+     * Called when the Fragment is no longer started.  This is generally
+     * tied to {@link Activity#onStop() Activity.onStop} of the containing
+     * Activity's lifecycle.
+     */
+    public void onStop() {
+        mCalled = true;
+    }
+    
+    public void onLowMemory() {
+        mCalled = true;
+    }
+    
+    /**
+     * Called when the view previously created by {@link #onCreateView} has
+     * been detached from the fragment.  The next time the fragment needs
+     * to be displayed, a new view will be created.  This is called
+     * after {@link #onStop()} and before {@link #onDestroy()}.  It is called
+     * <em>regardless</em> of whether {@link #onCreateView} returned a
+     * non-null view.  Internally it is called after the view's state has
+     * been saved but before it has been removed from its parent.
+     */
+    public void onDestroyView() {
+        mCalled = true;
+    }
+    
+    /**
+     * Called when the fragment is no longer in use.  This is called
+     * after {@link #onStop()} and before {@link #onDetach()}.
+     */
+    public void onDestroy() {
+        mCalled = true;
+        //Log.v("foo", "onDestroy: mCheckedForLoaderManager=" + mCheckedForLoaderManager
+        //        + " mLoaderManager=" + mLoaderManager);
+        if (!mCheckedForLoaderManager) {
+            mCheckedForLoaderManager = true;
+            mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false);
+        }
+        if (mLoaderManager != null) {
+            mLoaderManager.doDestroy();
+        }
+    }
+
+    /**
+     * Called when the fragment is no longer attached to its activity.  This
+     * is called after {@link #onDestroy()}.
+     */
+    public void onDetach() {
+        mCalled = true;
+    }
+    
+    /**
+     * Initialize the contents of the Activity's standard options menu.  You
+     * should place your menu items in to <var>menu</var>.  For this method
+     * to be called, you must have first called {@link #setHasOptionsMenu}.  See
+     * {@link Activity#onCreateOptionsMenu(Menu) Activity.onCreateOptionsMenu}
+     * for more information.
+     * 
+     * @param menu The options menu in which you place your items.
+     * 
+     * @see #setHasOptionsMenu
+     * @see #onPrepareOptionsMenu
+     * @see #onOptionsItemSelected
+     */
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+    }
+
+    /**
+     * Prepare the Screen's standard options menu to be displayed.  This is
+     * called right before the menu is shown, every time it is shown.  You can
+     * use this method to efficiently enable/disable items or otherwise
+     * dynamically modify the contents.  See
+     * {@link Activity#onPrepareOptionsMenu(Menu) Activity.onPrepareOptionsMenu}
+     * for more information.
+     * 
+     * @param menu The options menu as last shown or first initialized by
+     *             onCreateOptionsMenu().
+     * 
+     * @see #setHasOptionsMenu
+     * @see #onCreateOptionsMenu
+     */
+    public void onPrepareOptionsMenu(Menu menu) {
+    }
+
+    /**
+     * Called when this fragment's option menu items are no longer being
+     * included in the overall options menu.  Receiving this call means that
+     * the menu needed to be rebuilt, but this fragment's items were not
+     * included in the newly built menu (its {@link #onCreateOptionsMenu(Menu, MenuInflater)}
+     * was not called).
+     */
+    public void onDestroyOptionsMenu() {
+    }
+    
+    /**
+     * This hook is called whenever an item in your options menu is selected.
+     * The default implementation simply returns false to have the normal
+     * processing happen (calling the item's Runnable or sending a message to
+     * its Handler as appropriate).  You can use this method for any items
+     * for which you would like to do processing without those other
+     * facilities.
+     * 
+     * <p>Derived classes should call through to the base class for it to
+     * perform the default menu handling.
+     * 
+     * @param item The menu item that was selected.
+     * 
+     * @return boolean Return false to allow normal menu processing to
+     *         proceed, true to consume it here.
+     * 
+     * @see #onCreateOptionsMenu
+     */
+    public boolean onOptionsItemSelected(MenuItem item) {
+        return false;
+    }
+
+    /**
+     * This hook is called whenever the options menu is being closed (either by the user canceling
+     * the menu with the back/menu button, or when an item is selected).
+     *  
+     * @param menu The options menu as last shown or first initialized by
+     *             onCreateOptionsMenu().
+     */
+    public void onOptionsMenuClosed(Menu menu) {
+    }
+    
+    /**
+     * Called when a context menu for the {@code view} is about to be shown.
+     * Unlike {@link #onCreateOptionsMenu}, this will be called every
+     * time the context menu is about to be shown and should be populated for
+     * the view (or item inside the view for {@link AdapterView} subclasses,
+     * this can be found in the {@code menuInfo})).
+     * <p>
+     * Use {@link #onContextItemSelected(android.view.MenuItem)} to know when an
+     * item has been selected.
+     * <p>
+     * The default implementation calls up to
+     * {@link Activity#onCreateContextMenu Activity.onCreateContextMenu}, though
+     * you can not call this implementation if you don't want that behavior.
+     * <p>
+     * It is not safe to hold onto the context menu after this method returns.
+     * {@inheritDoc}
+     */
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+        getActivity().onCreateContextMenu(menu, v, menuInfo);
+    }
+
+    /**
+     * Registers a context menu to be shown for the given view (multiple views
+     * can show the context menu). This method will set the
+     * {@link OnCreateContextMenuListener} on the view to this fragment, so
+     * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} will be
+     * called when it is time to show the context menu.
+     * 
+     * @see #unregisterForContextMenu(View)
+     * @param view The view that should show a context menu.
+     */
+    public void registerForContextMenu(View view) {
+        view.setOnCreateContextMenuListener(this);
+    }
+    
+    /**
+     * Prevents a context menu to be shown for the given view. This method will
+     * remove the {@link OnCreateContextMenuListener} on the view.
+     * 
+     * @see #registerForContextMenu(View)
+     * @param view The view that should stop showing a context menu.
+     */
+    public void unregisterForContextMenu(View view) {
+        view.setOnCreateContextMenuListener(null);
+    }
+    
+    /**
+     * This hook is called whenever an item in a context menu is selected. The
+     * default implementation simply returns false to have the normal processing
+     * happen (calling the item's Runnable or sending a message to its Handler
+     * as appropriate). You can use this method for any items for which you
+     * would like to do processing without those other facilities.
+     * <p>
+     * Use {@link MenuItem#getMenuInfo()} to get extra information set by the
+     * View that added this menu item.
+     * <p>
+     * Derived classes should call through to the base class for it to perform
+     * the default menu handling.
+     * 
+     * @param item The context menu item that was selected.
+     * @return boolean Return false to allow normal context menu processing to
+     *         proceed, true to consume it here.
+     */
+    public boolean onContextItemSelected(MenuItem item) {
+        return false;
+    }
+    
+    /**
+     * Print the Fragments's state into the given stream.
+     *
+     * @param prefix Text to print at the front of each line.
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param writer The PrintWriter to which you should dump your state.  This will be
+     * closed for you after you return.
+     * @param args additional arguments to the dump request.
+     */
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        writer.print(prefix); writer.print("mFragmentId="); writer.print(mFragmentId);
+                writer.print(" mContainerId="); writer.print(mContainerId);
+                writer.print(" mTag="); writer.println(mTag);
+        writer.print(prefix); writer.print("mState="); writer.print(mState);
+                writer.print(" mIndex="); writer.print(mIndex);
+                writer.print(" mWho="); writer.print(mWho);
+                writer.print(" mBackStackNesting="); writer.println(mBackStackNesting);
+        writer.print(prefix); writer.print("mAdded="); writer.print(mAdded);
+                writer.print(" mResumed="); writer.print(mResumed);
+                writer.print(" mFromLayout="); writer.print(mFromLayout);
+                writer.print(" mInLayout="); writer.println(mInLayout);
+        writer.print(prefix); writer.print("mHidden="); writer.print(mHidden);
+                writer.print(" mRetainInstance="); writer.print(mRetainInstance);
+                writer.print(" mRetaining="); writer.print(mRetaining);
+                writer.print(" mHasMenu="); writer.println(mHasMenu);
+        if (mFragmentManager != null) {
+            writer.print(prefix); writer.print("mFragmentManager=");
+                    writer.println(mFragmentManager);
+        }
+        if (mImmediateActivity != null) {
+            writer.print(prefix); writer.print("mImmediateActivity=");
+                    writer.println(mImmediateActivity);
+        }
+        if (mActivity != null) {
+            writer.print(prefix); writer.print("mActivity=");
+                    writer.println(mActivity);
+        }
+        if (mArguments != null) {
+            writer.print(prefix); writer.print("mArguments="); writer.println(mArguments);
+        }
+        if (mSavedFragmentState != null) {
+            writer.print(prefix); writer.print("mSavedFragmentState=");
+                    writer.println(mSavedFragmentState);
+        }
+        if (mSavedViewState != null) {
+            writer.print(prefix); writer.print("mSavedViewState=");
+                    writer.println(mSavedViewState);
+        }
+        if (mTarget != null) {
+            writer.print(prefix); writer.print("mTarget="); writer.print(mTarget);
+                    writer.print(" mTargetRequestCode=");
+                    writer.println(mTargetRequestCode);
+        }
+        if (mNextAnim != 0) {
+            writer.print(prefix); writer.print("mNextAnim="); writer.println(mNextAnim);
+        }
+        if (mContainer != null) {
+            writer.print(prefix); writer.print("mContainer="); writer.println(mContainer);
+        }
+        if (mView != null) {
+            writer.print(prefix); writer.print("mView="); writer.println(mView);
+        }
+        if (mLoaderManager != null) {
+            writer.print(prefix); writer.print("mLoaderManager="); writer.print(mLoaderManager);
+                    writer.print(" mLoadersStarted="); writer.print(mLoadersStarted);
+                    writer.print(" mCheckedForLoaderManager=");
+                    writer.println(mCheckedForLoaderManager);
+        }
+    }
+
+    void performStop() {
+        onStop();
+        
+        if (mLoadersStarted) {
+            mLoadersStarted = false;
+            if (!mCheckedForLoaderManager) {
+                mCheckedForLoaderManager = true;
+                mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false);
+            }
+            if (mLoaderManager != null) {
+                if (mActivity == null || !mActivity.mChangingConfigurations) {
+                    mLoaderManager.doStop();
+                } else {
+                    mLoaderManager.doRetain();
+                }
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java
new file mode 100644
index 0000000..e924c1c
--- /dev/null
+++ b/core/java/android/app/FragmentBreadCrumbs.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.animation.LayoutTransition;
+import android.app.FragmentManager.BackStackEntry;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * Helper class for showing "bread crumbs" representing the fragment
+ * stack in an activity.  This is intended to be used with
+ * {@link ActionBar#setCustomNavigationMode(View)
+ * ActionBar.setCustomNavigationMode(View)} to place the bread crumbs in
+ * the navigation area of the action bar.
+ *
+ * <p>The default style for this view is
+ * {@link android.R.style#Widget_FragmentBreadCrumbs}.
+ */
+public class FragmentBreadCrumbs extends ViewGroup
+        implements FragmentManager.OnBackStackChangedListener {
+    Activity mActivity;
+    LayoutInflater mInflater;
+    LinearLayout mContainer;
+
+    // Hahah
+    BackStackRecord mTopEntry;
+
+    public FragmentBreadCrumbs(Context context) {
+        this(context, null);
+    }
+
+    public FragmentBreadCrumbs(Context context, AttributeSet attrs) {
+        this(context, attrs, android.R.style.Widget_FragmentBreadCrumbs);
+    }
+
+    public FragmentBreadCrumbs(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    /**
+     * Attach the bread crumbs to their activity.  This must be called once
+     * when creating the bread crumbs.
+     */
+    public void setActivity(Activity a) {
+        mActivity = a;
+        mInflater = (LayoutInflater)a.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        mContainer = (LinearLayout)mInflater.inflate(
+                com.android.internal.R.layout.fragment_bread_crumbs,
+                this, false);
+        addView(mContainer);
+        a.getFragmentManager().addOnBackStackChangedListener(this);
+        updateCrumbs();
+        setLayoutTransition(new LayoutTransition());
+    }
+
+    /**
+     * Set a custom title for the bread crumbs.  This will be the first entry
+     * shown at the left, representing the root of the bread crumbs.  If the
+     * title is null, it will not be shown.
+     */
+    public void setTitle(CharSequence title, CharSequence shortTitle) {
+        if (title == null) {
+            mTopEntry = null;
+        } else {
+            mTopEntry = new BackStackRecord((FragmentManagerImpl)
+                    mActivity.getFragmentManager());
+            mTopEntry.setBreadCrumbTitle(title);
+            mTopEntry.setBreadCrumbShortTitle(shortTitle);
+        }
+        updateCrumbs();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        // Eventually we should implement our own layout of the views,
+        // rather than relying on a linear layout.
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+
+            int childRight = mPaddingLeft + child.getMeasuredWidth() - mPaddingRight;
+            int childBottom = mPaddingTop + child.getMeasuredHeight() - mPaddingBottom;
+            child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int count = getChildCount();
+
+        int maxHeight = 0;
+        int maxWidth = 0;
+
+        // Find rightmost and bottom-most child
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() != GONE) {
+                measureChild(child, widthMeasureSpec, heightMeasureSpec);
+                maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
+                maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
+            }
+        }
+
+        // Account for padding too
+        maxWidth += mPaddingLeft + mPaddingRight;
+        maxHeight += mPaddingTop + mPaddingBottom;
+
+        // Check against our minimum height and width
+        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+        setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
+                resolveSize(maxHeight, heightMeasureSpec));
+    }
+
+    @Override
+    public void onBackStackChanged() {
+        updateCrumbs();
+    }
+
+    void updateCrumbs() {
+        FragmentManager fm = mActivity.getFragmentManager();
+        int numEntries = fm.countBackStackEntries();
+        int numViews = mContainer.getChildCount();
+        for (int i = mTopEntry != null ? -1 : 0; i < numEntries; i++) {
+            BackStackEntry bse = i == -1 ? mTopEntry : fm.getBackStackEntry(i);
+            int viewI = mTopEntry != null ? i + 1 : i;
+            if (viewI < numViews) {
+                View v = mContainer.getChildAt(viewI);
+                Object tag = v.getTag();
+                if (tag != bse) {
+                    for (int j = viewI; j < numViews; j++) {
+                        mContainer.removeViewAt(viewI);
+                    }
+                    numViews = viewI;
+                }
+            }
+            if (viewI >= numViews) {
+                View item = mInflater.inflate(
+                        com.android.internal.R.layout.fragment_bread_crumb_item,
+                        this, false);
+                TextView text = (TextView)item.findViewById(com.android.internal.R.id.title);
+                text.setText(bse.getBreadCrumbTitle());
+                item.setTag(bse);
+                if (viewI == 0) {
+                    text.setCompoundDrawables(null, null, null, null);
+                }
+                mContainer.addView(item);
+                item.setOnClickListener(mOnClickListener);
+            }
+        }
+        int viewI = mTopEntry != null ? numEntries + 1 : numEntries;
+        numViews = mContainer.getChildCount();
+        while (numViews > viewI) {
+            mContainer.removeViewAt(numViews-1);
+            numViews--;
+        }
+    }
+
+    private OnClickListener mOnClickListener = new OnClickListener() {
+        public void onClick(View v) {
+            if (v.getTag() instanceof BackStackEntry) {
+                BackStackEntry bse = (BackStackEntry) v.getTag();
+                mActivity.getFragmentManager().popBackStack(bse.getId(),
+                        bse == mTopEntry? FragmentManager.POP_BACK_STACK_INCLUSIVE : 0);
+            }
+        }
+    };
+}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
new file mode 100644
index 0000000..512ca16
--- /dev/null
+++ b/core/java/android/app/FragmentManager.java
@@ -0,0 +1,1457 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Interface for interacting with {@link Fragment} objects inside of an
+ * {@link Activity}
+ */
+public interface FragmentManager {
+    /**
+     * Representation of an entry on the fragment back stack, as created
+     * with {@link FragmentTransaction#addToBackStack(String)
+     * FragmentTransaction.addToBackStack()}.  Entries can later be
+     * retrieved with {@link FragmentManager#getBackStackEntry(int)
+     * FragmentManager.getBackStackEntry()}.
+     *
+     * <p>Note that you should never hold on to a BackStackEntry object;
+     * the identifier as returned by {@link #getId} is the only thing that
+     * will be persisted across activity instances.
+     */
+    public interface BackStackEntry {
+        /**
+         * Return the unique identifier for the entry.  This is the only
+         * representation of the entry that will persist across activity
+         * instances.
+         */
+        public int getId();
+
+        /**
+         * Return the full bread crumb title for the entry, or null if it
+         * does not have one.
+         */
+        public CharSequence getBreadCrumbTitle();
+
+        /**
+         * Return the short bread crumb title for the entry, or null if it
+         * does not have one.
+         */
+        public CharSequence getBreadCrumbShortTitle();
+    }
+
+    /**
+     * Interface to watch for changes to the back stack.
+     */
+    public interface OnBackStackChangedListener {
+        /**
+         * Called whenever the contents of the back stack change.
+         */
+        public void onBackStackChanged();
+    }
+
+    /**
+     * Start a series of edit operations on the Fragments associated with
+     * this FragmentManager.
+     * 
+     * <p>Note: A fragment transaction can only be created/committed prior
+     * to an activity saving its state.  If you try to commit a transaction
+     * after {@link Activity#onSaveInstanceState Activity.onSaveInstanceState()}
+     * (and prior to a following {@link Activity#onStart Activity.onStart}
+     * or {@link Activity#onResume Activity.onResume()}, you will get an error.
+     * This is because the framework takes care of saving your current fragments
+     * in the state, and if changes are made after the state is saved then they
+     * will be lost.</p>
+     */
+    public FragmentTransaction openTransaction();
+
+    /**
+     * Finds a fragment that was identified by the given id either when inflated
+     * from XML or as the container ID when added in a transaction.  This first
+     * searches through fragments that are currently added to the manager's
+     * activity; if no such fragment is found, then all fragments currently
+     * on the back stack associated with this ID are searched.
+     * @return The fragment if found or null otherwise.
+     */
+    public Fragment findFragmentById(int id);
+
+    /**
+     * Finds a fragment that was identified by the given tag either when inflated
+     * from XML or as supplied when added in a transaction.  This first
+     * searches through fragments that are currently added to the manager's
+     * activity; if no such fragment is found, then all fragments currently
+     * on the back stack are searched.
+     * @return The fragment if found or null otherwise.
+     */
+    public Fragment findFragmentByTag(String tag);
+
+    /**
+     * Flag for {@link #popBackStack(String, int)}
+     * and {@link #popBackStack(int, int)}: If set, and the name or ID of
+     * a back stack entry has been supplied, then all matching entries will
+     * be consumed until one that doesn't match is found or the bottom of
+     * the stack is reached.  Otherwise, all entries up to but not including that entry
+     * will be removed.
+     */
+    public static final int POP_BACK_STACK_INCLUSIVE = 1<<0;
+
+    /**
+     * Pop the top state off the back stack.  Returns true if there was one
+     * to pop, else false.
+     */
+    public boolean popBackStack();
+
+    /**
+     * Pop the last fragment transition from the manager's fragment
+     * back stack.  If there is nothing to pop, false is returned.
+     * @param name If non-null, this is the name of a previous back state
+     * to look for; if found, all states up to that state will be popped.  The
+     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
+     * the named state itself is popped. If null, only the top state is popped.
+     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
+     */
+    public boolean popBackStack(String name, int flags);
+
+    /**
+     * Pop all back stack states up to the one with the given identifier.
+     * @param id Identifier of the stated to be popped. If no identifier exists,
+     * false is returned.
+     * The identifier is the number returned by
+     * {@link FragmentTransaction#commit() FragmentTransaction.commit()}.  The
+     * {@link #POP_BACK_STACK_INCLUSIVE} flag can be used to control whether
+     * the named state itself is popped.
+     * @param flags Either 0 or {@link #POP_BACK_STACK_INCLUSIVE}.
+     */
+    public boolean popBackStack(int id, int flags);
+
+    /**
+     * Return the number of entries currently in the back stack.
+     */
+    public int countBackStackEntries();
+
+    /**
+     * Return the BackStackEntry at index <var>index</var> in the back stack;
+     * entries start index 0 being the bottom of the stack.
+     */
+    public BackStackEntry getBackStackEntry(int index);
+
+    /**
+     * Add a new listener for changes to the fragment back stack.
+     */
+    public void addOnBackStackChangedListener(OnBackStackChangedListener listener);
+
+    /**
+     * Remove a listener that was previously added with
+     * {@link #addOnBackStackChangedListener(OnBackStackChangedListener)}.
+     */
+    public void removeOnBackStackChangedListener(OnBackStackChangedListener listener);
+
+    /**
+     * Put a reference to a fragment in a Bundle.  This Bundle can be
+     * persisted as saved state, and when later restoring
+     * {@link #getFragment(Bundle, String)} will return the current
+     * instance of the same fragment.
+     *
+     * @param bundle The bundle in which to put the fragment reference.
+     * @param key The name of the entry in the bundle.
+     * @param fragment The Fragment whose reference is to be stored.
+     */
+    public void putFragment(Bundle bundle, String key, Fragment fragment);
+
+    /**
+     * Retrieve the current Fragment instance for a reference previously
+     * placed with {@link #putFragment(Bundle, String, Fragment)}.
+     *
+     * @param bundle The bundle from which to retrieve the fragment reference.
+     * @param key The name of the entry in the bundle.
+     * @return Returns the current Fragment instance that is associated with
+     * the given reference.
+     */
+    public Fragment getFragment(Bundle bundle, String key);
+
+    /**
+     * Print the FragmentManager's state into the given stream.
+     *
+     * @param prefix Text to print at the front of each line.
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param writer A PrintWriter to which the dump is to be set.
+     * @param args additional arguments to the dump request.
+     */
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
+}
+
+final class FragmentManagerState implements Parcelable {
+    FragmentState[] mActive;
+    int[] mAdded;
+    BackStackState[] mBackStack;
+    
+    public FragmentManagerState() {
+    }
+    
+    public FragmentManagerState(Parcel in) {
+        mActive = in.createTypedArray(FragmentState.CREATOR);
+        mAdded = in.createIntArray();
+        mBackStack = in.createTypedArray(BackStackState.CREATOR);
+    }
+    
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeTypedArray(mActive, flags);
+        dest.writeIntArray(mAdded);
+        dest.writeTypedArray(mBackStack, flags);
+    }
+    
+    public static final Parcelable.Creator<FragmentManagerState> CREATOR
+            = new Parcelable.Creator<FragmentManagerState>() {
+        public FragmentManagerState createFromParcel(Parcel in) {
+            return new FragmentManagerState(in);
+        }
+        
+        public FragmentManagerState[] newArray(int size) {
+            return new FragmentManagerState[size];
+        }
+    };
+}
+
+/**
+ * Container for fragments associated with an activity.
+ */
+final class FragmentManagerImpl implements FragmentManager {
+    static final boolean DEBUG = true;
+    static final String TAG = "FragmentManager";
+    
+    static final String TARGET_REQUEST_CODE_STATE_TAG = "android:target_req_state";
+    static final String TARGET_STATE_TAG = "android:target_state";
+    static final String VIEW_STATE_TAG = "android:view_state";
+
+    ArrayList<Runnable> mPendingActions;
+    Runnable[] mTmpActions;
+    boolean mExecutingActions;
+    
+    ArrayList<Fragment> mActive;
+    ArrayList<Fragment> mAdded;
+    ArrayList<Integer> mAvailIndices;
+    ArrayList<BackStackRecord> mBackStack;
+    ArrayList<Fragment> mCreatedMenus;
+    
+    // Must be accessed while locked.
+    ArrayList<BackStackRecord> mBackStackIndices;
+    ArrayList<Integer> mAvailBackStackIndices;
+
+    ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
+
+    int mCurState = Fragment.INITIALIZING;
+    Activity mActivity;
+    
+    boolean mNeedMenuInvalidate;
+    boolean mStateSaved;
+    String mNoTransactionsBecause;
+    
+    // Temporary vars for state save and restore.
+    Bundle mStateBundle = null;
+    SparseArray<Parcelable> mStateArray = null;
+    
+    Runnable mExecCommit = new Runnable() {
+        @Override
+        public void run() {
+            execPendingActions();
+        }
+    };
+
+    @Override
+    public FragmentTransaction openTransaction() {
+        return new BackStackRecord(this);
+    }
+
+    @Override
+    public boolean popBackStack() {
+        return popBackStackState(mActivity.mHandler, null, -1, 0);
+    }
+
+    @Override
+    public boolean popBackStack(String name, int flags) {
+        return popBackStackState(mActivity.mHandler, name, -1, flags);
+    }
+
+    @Override
+    public boolean popBackStack(int id, int flags) {
+        if (id < 0) {
+            throw new IllegalArgumentException("Bad id: " + id);
+        }
+        return popBackStackState(mActivity.mHandler, null, id, flags);
+    }
+
+    @Override
+    public int countBackStackEntries() {
+        return mBackStack != null ? mBackStack.size() : 0;
+    }
+
+    @Override
+    public BackStackEntry getBackStackEntry(int index) {
+        return mBackStack.get(index);
+    }
+
+    @Override
+    public void addOnBackStackChangedListener(OnBackStackChangedListener listener) {
+        if (mBackStackChangeListeners == null) {
+            mBackStackChangeListeners = new ArrayList<OnBackStackChangedListener>();
+        }
+        mBackStackChangeListeners.add(listener);
+    }
+
+    @Override
+    public void removeOnBackStackChangedListener(OnBackStackChangedListener listener) {
+        if (mBackStackChangeListeners != null) {
+            mBackStackChangeListeners.remove(listener);
+        }
+    }
+
+    @Override
+    public void putFragment(Bundle bundle, String key, Fragment fragment) {
+        if (fragment.mIndex < 0) {
+            throw new IllegalStateException("Fragment " + fragment
+                    + " is not currently in the FragmentManager");
+        }
+        bundle.putInt(key, fragment.mIndex);
+    }
+
+    @Override
+    public Fragment getFragment(Bundle bundle, String key) {
+        int index = bundle.getInt(key, -1);
+        if (index == -1) {
+            return null;
+        }
+        if (index >= mActive.size()) {
+            throw new IllegalStateException("Fragement no longer exists for key "
+                    + key + ": index " + index);
+        }
+        Fragment f = mActive.get(index);
+        if (f == null) {
+            throw new IllegalStateException("Fragement no longer exists for key "
+                    + key + ": index " + index);
+        }
+        return f;
+    }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        if (mActive == null || mActive.size() <= 0) {
+            return;
+        }
+
+        writer.print(prefix); writer.println("Active Fragments:");
+
+        String innerPrefix = prefix + "    ";
+
+        int N = mActive.size();
+        for (int i=0; i<N; i++) {
+            Fragment f = mActive.get(i);
+            if (f != null) {
+                writer.print(prefix); writer.print("  #"); writer.print(i);
+                        writer.print(": "); writer.println(f.toString());
+                f.dump(innerPrefix, fd, writer, args);
+            }
+        }
+
+        if (mAdded != null) {
+            N = mAdded.size();
+            if (N > 0) {
+                writer.print(prefix); writer.println("Added Fragments:");
+                for (int i=0; i<N; i++) {
+                    Fragment f = mAdded.get(i);
+                    writer.print(prefix); writer.print("  #"); writer.print(i);
+                            writer.print(": "); writer.println(f.toString());
+                }
+            }
+        }
+
+        if (mBackStack != null) {
+            N = mBackStack.size();
+            if (N > 0) {
+                writer.print(prefix); writer.println("Back Stack:");
+                for (int i=0; i<N; i++) {
+                    BackStackRecord bs = mBackStack.get(i);
+                    writer.print(prefix); writer.print("  #"); writer.print(i);
+                            writer.print(": "); writer.println(bs.toString());
+                }
+            }
+        }
+    }
+
+    Animator loadAnimator(Fragment fragment, int transit, boolean enter,
+            int transitionStyle) {
+        Animator animObj = fragment.onCreateAnimator(transit, enter,
+                fragment.mNextAnim);
+        if (animObj != null) {
+            return animObj;
+        }
+        
+        if (fragment.mNextAnim != 0) {
+            Animator anim = AnimatorInflater.loadAnimator(mActivity, fragment.mNextAnim);
+            if (anim != null) {
+                return anim;
+            }
+        }
+        
+        if (transit == 0) {
+            return null;
+        }
+        
+        int styleIndex = transitToStyleIndex(transit, enter);
+        if (styleIndex < 0) {
+            return null;
+        }
+        
+        if (transitionStyle == 0 && mActivity.getWindow() != null) {
+            transitionStyle = mActivity.getWindow().getAttributes().windowAnimations;
+        }
+        if (transitionStyle == 0) {
+            return null;
+        }
+        
+        TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle,
+                com.android.internal.R.styleable.FragmentAnimation);
+        int anim = attrs.getResourceId(styleIndex, 0);
+        attrs.recycle();
+        
+        if (anim == 0) {
+            return null;
+        }
+        
+        return AnimatorInflater.loadAnimator(mActivity, anim);
+    }
+    
+    void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
+        // Fragments that are not currently added will sit in the onCreate() state.
+        if (!f.mAdded && newState > Fragment.CREATED) {
+            newState = Fragment.CREATED;
+        }
+        
+        if (f.mState < newState) {
+            switch (f.mState) {
+                case Fragment.INITIALIZING:
+                    if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
+                    if (f.mSavedFragmentState != null) {
+                        f.mSavedViewState = f.mSavedFragmentState.getSparseParcelableArray(
+                                FragmentManagerImpl.VIEW_STATE_TAG);
+                        f.mTarget = getFragment(f.mSavedFragmentState,
+                                FragmentManagerImpl.TARGET_STATE_TAG);
+                        if (f.mTarget != null) {
+                            f.mTargetRequestCode = f.mSavedFragmentState.getInt(
+                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG, 0);
+                        }
+                    }
+                    f.mActivity = mActivity;
+                    f.mCalled = false;
+                    f.onAttach(mActivity);
+                    if (!f.mCalled) {
+                        throw new SuperNotCalledException("Fragment " + f
+                                + " did not call through to super.onAttach()");
+                    }
+                    mActivity.onAttachFragment(f);
+                    
+                    if (!f.mRetaining) {
+                        f.mCalled = false;
+                        f.onCreate(f.mSavedFragmentState);
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onCreate()");
+                        }
+                    }
+                    f.mRetaining = false;
+                    if (f.mFromLayout) {
+                        // For fragments that are part of the content view
+                        // layout, we need to instantiate the view immediately
+                        // and the inflater will take care of adding it.
+                        f.mView = f.onCreateView(mActivity.getLayoutInflater(),
+                                null, f.mSavedFragmentState);
+                        if (f.mView != null) {
+                            f.mView.setSaveFromParentEnabled(false);
+                            f.restoreViewState();
+                            if (f.mHidden) f.mView.setVisibility(View.GONE); 
+                        }
+                    }
+                case Fragment.CREATED:
+                    if (newState > Fragment.CREATED) {
+                        if (DEBUG) Log.v(TAG, "moveto CONTENT: " + f);
+                        if (!f.mFromLayout) {
+                            ViewGroup container = null;
+                            if (f.mContainerId != 0) {
+                                container = (ViewGroup)mActivity.findViewById(f.mContainerId);
+                                if (container == null) {
+                                    throw new IllegalArgumentException("No view found for id 0x"
+                                            + Integer.toHexString(f.mContainerId)
+                                            + " for fragment " + f);
+                                }
+                            }
+                            f.mContainer = container;
+                            f.mView = f.onCreateView(mActivity.getLayoutInflater(),
+                                    container, f.mSavedFragmentState);
+                            if (f.mView != null) {
+                                f.mView.setSaveFromParentEnabled(false);
+                                if (container != null) {
+                                    Animator anim = loadAnimator(f, transit, true,
+                                            transitionStyle);
+                                    if (anim != null) {
+                                        anim.setTarget(f.mView);
+                                        anim.start();
+                                    }
+                                    container.addView(f.mView);
+                                    f.restoreViewState();
+                                }
+                                if (f.mHidden) f.mView.setVisibility(View.GONE); 
+                            }
+                        }
+                        
+                        f.mCalled = false;
+                        f.onActivityCreated(f.mSavedFragmentState);
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onReady()");
+                        }
+                        f.mSavedFragmentState = null;
+                    }
+                case Fragment.ACTIVITY_CREATED:
+                    if (newState > Fragment.ACTIVITY_CREATED) {
+                        if (DEBUG) Log.v(TAG, "moveto STARTED: " + f);
+                        f.mCalled = false;
+                        f.onStart();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onStart()");
+                        }
+                    }
+                case Fragment.STARTED:
+                    if (newState > Fragment.STARTED) {
+                        if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
+                        f.mCalled = false;
+                        f.mResumed = true;
+                        f.onResume();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onResume()");
+                        }
+                    }
+            }
+        } else if (f.mState > newState) {
+            switch (f.mState) {
+                case Fragment.RESUMED:
+                    if (newState < Fragment.RESUMED) {
+                        if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
+                        f.mCalled = false;
+                        f.onPause();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onPause()");
+                        }
+                        f.mResumed = false;
+                    }
+                case Fragment.STARTED:
+                    if (newState < Fragment.STARTED) {
+                        if (DEBUG) Log.v(TAG, "movefrom STARTED: " + f);
+                        f.mCalled = false;
+                        f.performStop();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onStop()");
+                        }
+                    }
+                case Fragment.ACTIVITY_CREATED:
+                    if (newState < Fragment.ACTIVITY_CREATED) {
+                        if (DEBUG) Log.v(TAG, "movefrom CONTENT: " + f);
+                        if (f.mView != null) {
+                            // Need to save the current view state if not
+                            // done already.
+                            if (!mActivity.isFinishing() && f.mSavedViewState == null) {
+                                saveFragmentViewState(f);
+                            }
+                        }
+                        f.mCalled = false;
+                        f.onDestroyView();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onDestroyedView()");
+                        }
+                        if (f.mView != null && f.mContainer != null) {
+                            Animator anim = null;
+                            if (mCurState > Fragment.INITIALIZING) {
+                                anim = loadAnimator(f, transit, false,
+                                        transitionStyle);
+                            }
+                            if (anim != null) {
+                                final ViewGroup container = f.mContainer;
+                                final View view = f.mView;
+                                container.startViewTransition(view);
+                                anim.addListener(new AnimatorListenerAdapter() {
+                                    @Override
+                                    public void onAnimationEnd(Animator anim) {
+                                        container.endViewTransition(view);
+                                    }
+                                });
+                                anim.setTarget(f.mView);
+                                anim.start();
+
+                            }
+                            f.mContainer.removeView(f.mView);
+                        }
+                        f.mContainer = null;
+                        f.mView = null;
+                    }
+                case Fragment.CREATED:
+                    if (newState < Fragment.CREATED) {
+                        if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
+                        if (!f.mRetaining) {
+                            f.mCalled = false;
+                            f.onDestroy();
+                            if (!f.mCalled) {
+                                throw new SuperNotCalledException("Fragment " + f
+                                        + " did not call through to super.onDestroy()");
+                            }
+                        }
+                        
+                        f.mCalled = false;
+                        f.onDetach();
+                        if (!f.mCalled) {
+                            throw new SuperNotCalledException("Fragment " + f
+                                    + " did not call through to super.onDetach()");
+                        }
+                        f.mImmediateActivity = null;
+                        f.mActivity = null;
+                    }
+            }
+        }
+        
+        f.mState = newState;
+    }
+    
+    void moveToState(Fragment f) {
+        moveToState(f, mCurState, 0, 0);
+    }
+
+    void moveToState(int newState, boolean always) {
+        moveToState(newState, 0, 0, always);
+    }
+    
+    void moveToState(int newState, int transit, int transitStyle, boolean always) {
+        if (mActivity == null && newState != Fragment.INITIALIZING) {
+            throw new IllegalStateException("No activity");
+        }
+        
+        if (!always && mCurState == newState) {
+            return;
+        }
+        
+        mCurState = newState;
+        if (mActive != null) {
+            for (int i=0; i<mActive.size(); i++) {
+                Fragment f = mActive.get(i);
+                if (f != null) {
+                    moveToState(f, newState, transit, transitStyle);
+                }
+            }
+
+            if (mNeedMenuInvalidate && mActivity != null) {
+                mActivity.invalidateOptionsMenu();
+                mNeedMenuInvalidate = false;
+            }
+        }
+    }
+    
+    void makeActive(Fragment f) {
+        if (f.mIndex >= 0) {
+            return;
+        }
+        
+        if (mAvailIndices == null || mAvailIndices.size() <= 0) {
+            if (mActive == null) {
+                mActive = new ArrayList<Fragment>();
+            }
+            f.setIndex(mActive.size());
+            mActive.add(f);
+            
+        } else {
+            f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1));
+            mActive.set(f.mIndex, f);
+        }
+    }
+    
+    void makeInactive(Fragment f) {
+        if (f.mIndex < 0) {
+            return;
+        }
+        
+        if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex);
+        mActive.set(f.mIndex, null);
+        if (mAvailIndices == null) {
+            mAvailIndices = new ArrayList<Integer>();
+        }
+        mAvailIndices.add(f.mIndex);
+        mActivity.invalidateFragmentIndex(f.mIndex);
+        f.clearIndex();
+    }
+    
+    public void addFragment(Fragment fragment, boolean moveToStateNow) {
+        if (mAdded == null) {
+            mAdded = new ArrayList<Fragment>();
+        }
+        mAdded.add(fragment);
+        makeActive(fragment);
+        if (DEBUG) Log.v(TAG, "add: " + fragment);
+        fragment.mAdded = true;
+        if (fragment.mHasMenu) {
+            mNeedMenuInvalidate = true;
+        }
+        if (moveToStateNow) {
+            moveToState(fragment);
+        }
+    }
+    
+    public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
+        if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
+        mAdded.remove(fragment);
+        final boolean inactive = fragment.mBackStackNesting <= 0;
+        if (fragment.mHasMenu) {
+            mNeedMenuInvalidate = true;
+        }
+        fragment.mAdded = false;
+        moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
+                transition, transitionStyle);
+        if (inactive) {
+            makeInactive(fragment);
+        }
+    }
+    
+    public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
+        if (DEBUG) Log.v(TAG, "hide: " + fragment);
+        if (!fragment.mHidden) {
+            fragment.mHidden = true;
+            if (fragment.mView != null) {
+                Animator anim = loadAnimator(fragment, transition, true,
+                        transitionStyle);
+                if (anim != null) {
+                    anim.setTarget(fragment.mView);
+                    anim.start();
+                }
+                fragment.mView.setVisibility(View.GONE);
+            }
+            if (fragment.mAdded && fragment.mHasMenu) {
+                mNeedMenuInvalidate = true;
+            }
+            fragment.onHiddenChanged(true);
+        }
+    }
+    
+    public void showFragment(Fragment fragment, int transition, int transitionStyle) {
+        if (DEBUG) Log.v(TAG, "show: " + fragment);
+        if (fragment.mHidden) {
+            fragment.mHidden = false;
+            if (fragment.mView != null) {
+                Animator anim = loadAnimator(fragment, transition, true,
+                        transitionStyle);
+                if (anim != null) {
+                    anim.setTarget(fragment.mView);
+                    anim.start();
+                }
+                fragment.mView.setVisibility(View.VISIBLE);
+            }
+            if (fragment.mAdded && fragment.mHasMenu) {
+                mNeedMenuInvalidate = true;
+            }
+            fragment.onHiddenChanged(false);
+        }
+    }
+    
+    public Fragment findFragmentById(int id) {
+        if (mActive != null) {
+            // First look through added fragments.
+            for (int i=mAdded.size()-1; i>=0; i--) {
+                Fragment f = mAdded.get(i);
+                if (f != null && f.mFragmentId == id) {
+                    return f;
+                }
+            }
+            // Now for any known fragment.
+            for (int i=mActive.size()-1; i>=0; i--) {
+                Fragment f = mActive.get(i);
+                if (f != null && f.mFragmentId == id) {
+                    return f;
+                }
+            }
+        }
+        return null;
+    }
+    
+    public Fragment findFragmentByTag(String tag) {
+        if (mActive != null && tag != null) {
+            // First look through added fragments.
+            for (int i=mAdded.size()-1; i>=0; i--) {
+                Fragment f = mAdded.get(i);
+                if (f != null && tag.equals(f.mTag)) {
+                    return f;
+                }
+            }
+            // Now for any known fragment.
+            for (int i=mActive.size()-1; i>=0; i--) {
+                Fragment f = mActive.get(i);
+                if (f != null && tag.equals(f.mTag)) {
+                    return f;
+                }
+            }
+        }
+        return null;
+    }
+    
+    public Fragment findFragmentByWho(String who) {
+        if (mActive != null && who != null) {
+            for (int i=mActive.size()-1; i>=0; i--) {
+                Fragment f = mActive.get(i);
+                if (f != null && who.equals(f.mWho)) {
+                    return f;
+                }
+            }
+        }
+        return null;
+    }
+    
+    public void enqueueAction(Runnable action) {
+        if (mStateSaved) {
+            throw new IllegalStateException(
+                    "Can not perform this action after onSaveInstanceState");
+        }
+        if (mNoTransactionsBecause != null) {
+            throw new IllegalStateException(
+                    "Can not perform this action inside of " + mNoTransactionsBecause);
+        }
+        synchronized (this) {
+            if (mPendingActions == null) {
+                mPendingActions = new ArrayList<Runnable>();
+            }
+            mPendingActions.add(action);
+            if (mPendingActions.size() == 1) {
+                mActivity.mHandler.removeCallbacks(mExecCommit);
+                mActivity.mHandler.post(mExecCommit);
+            }
+        }
+    }
+    
+    public int allocBackStackIndex(BackStackRecord bse) {
+        synchronized (this) {
+            if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
+                if (mBackStackIndices == null) {
+                    mBackStackIndices = new ArrayList<BackStackRecord>();
+                }
+                int index = mBackStackIndices.size();
+                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
+                mBackStackIndices.add(bse);
+                return index;
+
+            } else {
+                int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
+                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
+                mBackStackIndices.set(index, bse);
+                return index;
+            }
+        }
+    }
+
+    public void setBackStackIndex(int index, BackStackRecord bse) {
+        synchronized (this) {
+            if (mBackStackIndices == null) {
+                mBackStackIndices = new ArrayList<BackStackRecord>();
+            }
+            int N = mBackStackIndices.size();
+            if (index < N) {
+                if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
+                mBackStackIndices.set(index, bse);
+            } else {
+                while (N < index) {
+                    mBackStackIndices.add(null);
+                    if (mAvailBackStackIndices == null) {
+                        mAvailBackStackIndices = new ArrayList<Integer>();
+                    }
+                    if (DEBUG) Log.v(TAG, "Adding available back stack index " + N);
+                    mAvailBackStackIndices.add(N);
+                    N++;
+                }
+                if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
+                mBackStackIndices.add(bse);
+            }
+        }
+    }
+
+    public void freeBackStackIndex(int index) {
+        synchronized (this) {
+            mBackStackIndices.set(index, null);
+            if (mAvailBackStackIndices == null) {
+                mAvailBackStackIndices = new ArrayList<Integer>();
+            }
+            if (DEBUG) Log.v(TAG, "Freeing back stack index " + index);
+            mAvailBackStackIndices.add(index);
+        }
+    }
+
+    /**
+     * Only call from main thread!
+     */
+    public void execPendingActions() {
+        if (mExecutingActions) {
+            throw new IllegalStateException("Recursive entry to execPendingActions");
+        }
+        
+        while (true) {
+            int numActions;
+            
+            synchronized (this) {
+                if (mPendingActions == null || mPendingActions.size() == 0) {
+                    return;
+                }
+                
+                numActions = mPendingActions.size();
+                if (mTmpActions == null || mTmpActions.length < numActions) {
+                    mTmpActions = new Runnable[numActions];
+                }
+                mPendingActions.toArray(mTmpActions);
+                mPendingActions.clear();
+                mActivity.mHandler.removeCallbacks(mExecCommit);
+            }
+            
+            mExecutingActions = true;
+            for (int i=0; i<numActions; i++) {
+                mTmpActions[i].run();
+            }
+            mExecutingActions = false;
+        }
+    }
+    
+    void reportBackStackChanged() {
+        if (mBackStackChangeListeners != null) {
+            for (int i=0; i<mBackStackChangeListeners.size(); i++) {
+                mBackStackChangeListeners.get(i).onBackStackChanged();
+            }
+        }
+    }
+
+    void addBackStackState(BackStackRecord state) {
+        if (mBackStack == null) {
+            mBackStack = new ArrayList<BackStackRecord>();
+        }
+        mBackStack.add(state);
+        reportBackStackChanged();
+    }
+    
+    boolean popBackStackState(Handler handler, String name, int id, int flags) {
+        if (mBackStack == null) {
+            return false;
+        }
+        if (name == null && id < 0 && (flags&Activity.POP_BACK_STACK_INCLUSIVE) == 0) {
+            int last = mBackStack.size()-1;
+            if (last < 0) {
+                return false;
+            }
+            final BackStackRecord bss = mBackStack.remove(last);
+            enqueueAction(new Runnable() {
+                public void run() {
+                    if (DEBUG) Log.v(TAG, "Popping back stack state: " + bss);
+                    bss.popFromBackStack(true);
+                    reportBackStackChanged();
+                }
+            });
+        } else {
+            int index = -1;
+            if (name != null || id >= 0) {
+                // If a name or ID is specified, look for that place in
+                // the stack.
+                index = mBackStack.size()-1;
+                while (index >= 0) {
+                    BackStackRecord bss = mBackStack.get(index);
+                    if (name != null && name.equals(bss.getName())) {
+                        break;
+                    }
+                    if (id >= 0 && id == bss.mIndex) {
+                        break;
+                    }
+                    index--;
+                }
+                if (index < 0) {
+                    return false;
+                }
+                if ((flags&Activity.POP_BACK_STACK_INCLUSIVE) != 0) {
+                    index--;
+                    // Consume all following entries that match.
+                    while (index >= 0) {
+                        BackStackRecord bss = mBackStack.get(index);
+                        if ((name != null && name.equals(bss.getName()))
+                                || (id >= 0 && id == bss.mIndex)) {
+                            index--;
+                            continue;
+                        }
+                        break;
+                    }
+                }
+            }
+            if (index == mBackStack.size()-1) {
+                return false;
+            }
+            final ArrayList<BackStackRecord> states
+                    = new ArrayList<BackStackRecord>();
+            for (int i=mBackStack.size()-1; i>index; i--) {
+                states.add(mBackStack.remove(i));
+            }
+            enqueueAction(new Runnable() {
+                public void run() {
+                    final int LAST = states.size()-1;
+                    for (int i=0; i<=LAST; i++) {
+                        if (DEBUG) Log.v(TAG, "Popping back stack state: " + states.get(i));
+                        states.get(i).popFromBackStack(i == LAST);
+                    }
+                    reportBackStackChanged();
+                }
+            });
+        }
+        return true;
+    }
+    
+    ArrayList<Fragment> retainNonConfig() {
+        ArrayList<Fragment> fragments = null;
+        if (mActive != null) {
+            for (int i=0; i<mActive.size(); i++) {
+                Fragment f = mActive.get(i);
+                if (f != null && f.mRetainInstance) {
+                    if (fragments == null) {
+                        fragments = new ArrayList<Fragment>();
+                    }
+                    fragments.add(f);
+                    f.mRetaining = true;
+                }
+            }
+        }
+        return fragments;
+    }
+    
+    void saveFragmentViewState(Fragment f) {
+        if (f.mView == null) {
+            return;
+        }
+        if (mStateArray == null) {
+            mStateArray = new SparseArray<Parcelable>();
+        }
+        f.mView.saveHierarchyState(mStateArray);
+        if (mStateArray.size() > 0) {
+            f.mSavedViewState = mStateArray;
+            mStateArray = null;
+        }
+    }
+    
+    Parcelable saveAllState() {
+        mStateSaved = true;
+
+        if (mActive == null || mActive.size() <= 0) {
+            return null;
+        }
+        
+        // First collect all active fragments.
+        int N = mActive.size();
+        FragmentState[] active = new FragmentState[N];
+        boolean haveFragments = false;
+        for (int i=0; i<N; i++) {
+            Fragment f = mActive.get(i);
+            if (f != null) {
+                haveFragments = true;
+                
+                FragmentState fs = new FragmentState(f);
+                active[i] = fs;
+                
+                if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
+                    if (mStateBundle == null) {
+                        mStateBundle = new Bundle();
+                    }
+                    f.onSaveInstanceState(mStateBundle);
+                    if (!mStateBundle.isEmpty()) {
+                        fs.mSavedFragmentState = mStateBundle;
+                        mStateBundle = null;
+                    }
+
+                    if (f.mView != null) {
+                        saveFragmentViewState(f);
+                        if (f.mSavedViewState != null) {
+                            if (fs.mSavedFragmentState == null) {
+                                fs.mSavedFragmentState = new Bundle();
+                            }
+                            fs.mSavedFragmentState.putSparseParcelableArray(
+                                    FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
+                        }
+                    }
+
+                    if (f.mTarget != null) {
+                        if (fs.mSavedFragmentState == null) {
+                            fs.mSavedFragmentState = new Bundle();
+                        }
+                        putFragment(fs.mSavedFragmentState,
+                                FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
+                        if (f.mTargetRequestCode != 0) {
+                            fs.mSavedFragmentState.putInt(
+                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
+                                    f.mTargetRequestCode);
+                        }
+                    }
+
+                } else {
+                    fs.mSavedFragmentState = f.mSavedFragmentState;
+                }
+                
+                if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
+                        + fs.mSavedFragmentState);
+            }
+        }
+        
+        if (!haveFragments) {
+            if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
+            return null;
+        }
+        
+        int[] added = null;
+        BackStackState[] backStack = null;
+        
+        // Build list of currently added fragments.
+        if (mAdded != null) {
+            N = mAdded.size();
+            if (N > 0) {
+                added = new int[N];
+                for (int i=0; i<N; i++) {
+                    added[i] = mAdded.get(i).mIndex;
+                    if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
+                            + ": " + mAdded.get(i));
+                }
+            }
+        }
+        
+        // Now save back stack.
+        if (mBackStack != null) {
+            N = mBackStack.size();
+            if (N > 0) {
+                backStack = new BackStackState[N];
+                for (int i=0; i<N; i++) {
+                    backStack[i] = new BackStackState(this, mBackStack.get(i));
+                    if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
+                            + ": " + mBackStack.get(i));
+                }
+            }
+        }
+        
+        FragmentManagerState fms = new FragmentManagerState();
+        fms.mActive = active;
+        fms.mAdded = added;
+        fms.mBackStack = backStack;
+        return fms;
+    }
+    
+    void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
+        // If there is no saved state at all, then there can not be
+        // any nonConfig fragments either, so that is that.
+        if (state == null) return;
+        FragmentManagerState fms = (FragmentManagerState)state;
+        if (fms.mActive == null) return;
+        
+        // First re-attach any non-config instances we are retaining back
+        // to their saved state, so we don't try to instantiate them again.
+        if (nonConfig != null) {
+            for (int i=0; i<nonConfig.size(); i++) {
+                Fragment f = nonConfig.get(i);
+                if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
+                FragmentState fs = fms.mActive[f.mIndex];
+                fs.mInstance = f;
+                f.mSavedViewState = null;
+                f.mBackStackNesting = 0;
+                f.mInLayout = false;
+                f.mAdded = false;
+                if (fs.mSavedFragmentState != null) {
+                    fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader());
+                    f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
+                            FragmentManagerImpl.VIEW_STATE_TAG);
+                }
+            }
+        }
+        
+        // Build the full list of active fragments, instantiating them from
+        // their saved state.
+        mActive = new ArrayList<Fragment>(fms.mActive.length);
+        if (mAvailIndices != null) {
+            mAvailIndices.clear();
+        }
+        for (int i=0; i<fms.mActive.length; i++) {
+            FragmentState fs = fms.mActive[i];
+            if (fs != null) {
+                Fragment f = fs.instantiate(mActivity);
+                if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": " + f);
+                mActive.add(f);
+            } else {
+                if (DEBUG) Log.v(TAG, "restoreAllState: adding #" + i + ": (null)");
+                mActive.add(null);
+                if (mAvailIndices == null) {
+                    mAvailIndices = new ArrayList<Integer>();
+                }
+                if (DEBUG) Log.v(TAG, "restoreAllState: adding avail #" + i);
+                mAvailIndices.add(i);
+            }
+        }
+        
+        // Update the target of all retained fragments.
+        if (nonConfig != null) {
+            for (int i=0; i<nonConfig.size(); i++) {
+                Fragment f = nonConfig.get(i);
+                if (f.mTarget != null) {
+                    if (f.mTarget.mIndex < mActive.size()) {
+                        f.mTarget = mActive.get(f.mTarget.mIndex);
+                    } else {
+                        Log.w(TAG, "Re-attaching retained fragment " + f
+                                + " target no longer exists: " + f.mTarget);
+                        f.mTarget = null;
+                    }
+                }
+            }
+        }
+
+        // Build the list of currently added fragments.
+        if (fms.mAdded != null) {
+            mAdded = new ArrayList<Fragment>(fms.mAdded.length);
+            for (int i=0; i<fms.mAdded.length; i++) {
+                Fragment f = mActive.get(fms.mAdded[i]);
+                if (f == null) {
+                    throw new IllegalStateException(
+                            "No instantiated fragment for index #" + fms.mAdded[i]);
+                }
+                f.mAdded = true;
+                f.mImmediateActivity = mActivity;
+                if (DEBUG) Log.v(TAG, "restoreAllState: making added #" + i + ": " + f);
+                mAdded.add(f);
+            }
+        } else {
+            mAdded = null;
+        }
+        
+        // Build the back stack.
+        if (fms.mBackStack != null) {
+            mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
+            for (int i=0; i<fms.mBackStack.length; i++) {
+                BackStackRecord bse = fms.mBackStack[i].instantiate(this);
+                if (DEBUG) Log.v(TAG, "restoreAllState: adding bse #" + i
+                        + " (index " + bse.mIndex + "): " + bse);
+                mBackStack.add(bse);
+                if (bse.mIndex >= 0) {
+                    setBackStackIndex(bse.mIndex, bse);
+                }
+            }
+        } else {
+            mBackStack = null;
+        }
+    }
+    
+    public void attachActivity(Activity activity) {
+        if (mActivity != null) throw new IllegalStateException();
+        mActivity = activity;
+    }
+    
+    public void noteStateNotSaved() {
+        mStateSaved = false;
+    }
+    
+    public void dispatchCreate() {
+        mStateSaved = false;
+        moveToState(Fragment.CREATED, false);
+    }
+    
+    public void dispatchActivityCreated() {
+        mStateSaved = false;
+        moveToState(Fragment.ACTIVITY_CREATED, false);
+    }
+    
+    public void dispatchStart() {
+        mStateSaved = false;
+        moveToState(Fragment.STARTED, false);
+    }
+    
+    public void dispatchResume() {
+        mStateSaved = false;
+        moveToState(Fragment.RESUMED, false);
+    }
+    
+    public void dispatchPause() {
+        moveToState(Fragment.STARTED, false);
+    }
+    
+    public void dispatchStop() {
+        moveToState(Fragment.ACTIVITY_CREATED, false);
+    }
+    
+    public void dispatchDestroy() {
+        moveToState(Fragment.INITIALIZING, false);
+        mActivity = null;
+    }
+    
+    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        boolean show = false;
+        ArrayList<Fragment> newMenus = null;
+        if (mActive != null) {
+            for (int i=0; i<mAdded.size(); i++) {
+                Fragment f = mAdded.get(i);
+                if (f != null && !f.mHidden && f.mHasMenu) {
+                    show = true;
+                    f.onCreateOptionsMenu(menu, inflater);
+                    if (newMenus == null) {
+                        newMenus = new ArrayList<Fragment>();
+                    }
+                    newMenus.add(f);
+                }
+            }
+        }
+        
+        if (mCreatedMenus != null) {
+            for (int i=0; i<mCreatedMenus.size(); i++) {
+                Fragment f = mCreatedMenus.get(i);
+                if (newMenus == null || !newMenus.contains(f)) {
+                    f.onDestroyOptionsMenu();
+                }
+            }
+        }
+        
+        mCreatedMenus = newMenus;
+        
+        return show;
+    }
+    
+    public boolean dispatchPrepareOptionsMenu(Menu menu) {
+        boolean show = false;
+        if (mActive != null) {
+            for (int i=0; i<mAdded.size(); i++) {
+                Fragment f = mAdded.get(i);
+                if (f != null && !f.mHidden && f.mHasMenu) {
+                    show = true;
+                    f.onPrepareOptionsMenu(menu);
+                }
+            }
+        }
+        return show;
+    }
+    
+    public boolean dispatchOptionsItemSelected(MenuItem item) {
+        if (mActive != null) {
+            for (int i=0; i<mAdded.size(); i++) {
+                Fragment f = mAdded.get(i);
+                if (f != null && !f.mHidden && f.mHasMenu) {
+                    if (f.onOptionsItemSelected(item)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+    
+    public boolean dispatchContextItemSelected(MenuItem item) {
+        if (mActive != null) {
+            for (int i=0; i<mAdded.size(); i++) {
+                Fragment f = mAdded.get(i);
+                if (f != null && !f.mHidden) {
+                    if (f.onContextItemSelected(item)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+    
+    public void dispatchOptionsMenuClosed(Menu menu) {
+        if (mActive != null) {
+            for (int i=0; i<mAdded.size(); i++) {
+                Fragment f = mAdded.get(i);
+                if (f != null && !f.mHidden && f.mHasMenu) {
+                    f.onOptionsMenuClosed(menu);
+                }
+            }
+        }
+    }
+    
+    public static int reverseTransit(int transit) {
+        int rev = 0;
+        switch (transit) {
+            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
+                rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
+                break;
+            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
+                rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
+                break;
+            case FragmentTransaction.TRANSIT_FRAGMENT_NEXT:
+                rev = FragmentTransaction.TRANSIT_FRAGMENT_PREV;
+                break;
+            case FragmentTransaction.TRANSIT_FRAGMENT_PREV:
+                rev = FragmentTransaction.TRANSIT_FRAGMENT_NEXT;
+                break;
+        }
+        return rev;
+        
+    }
+    
+    public static int transitToStyleIndex(int transit, boolean enter) {
+        int animAttr = -1;
+        switch (transit) {
+            case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
+                animAttr = enter
+                    ? com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation
+                    : com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation;
+                break;
+            case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
+                animAttr = enter
+                    ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation
+                    : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation;
+                break;
+            case FragmentTransaction.TRANSIT_FRAGMENT_NEXT:
+                animAttr = enter
+                    ? com.android.internal.R.styleable.FragmentAnimation_fragmentNextEnterAnimation
+                    : com.android.internal.R.styleable.FragmentAnimation_fragmentNextExitAnimation;
+                break;
+            case FragmentTransaction.TRANSIT_FRAGMENT_PREV:
+                animAttr = enter
+                    ? com.android.internal.R.styleable.FragmentAnimation_fragmentPrevEnterAnimation
+                    : com.android.internal.R.styleable.FragmentAnimation_fragmentPrevExitAnimation;
+                break;
+        }
+        return animAttr;
+    }
+}
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
new file mode 100644
index 0000000..19da763
--- /dev/null
+++ b/core/java/android/app/FragmentTransaction.java
@@ -0,0 +1,202 @@
+package android.app;
+
+/**
+ * API for performing a set of Fragment operations.
+ */
+public interface FragmentTransaction {
+    /**
+     * Calls {@link #add(int, Fragment, String)} with a 0 containerViewId.
+     */
+    public FragmentTransaction add(Fragment fragment, String tag);
+    
+    /**
+     * Calls {@link #add(int, Fragment, String)} with a null tag.
+     */
+    public FragmentTransaction add(int containerViewId, Fragment fragment);
+    
+    /**
+     * Add a fragment to the activity state.  This fragment may optionally
+     * also have its view (if {@link Fragment#onCreateView Fragment.onCreateView}
+     * returns non-null) into a container view of the activity.
+     * 
+     * @param containerViewId Optional identifier of the container this fragment is
+     * to be placed in.  If 0, it will not be placed in a container.
+     * @param fragment The fragment to be added.  This fragment must not already
+     * be added to the activity.
+     * @param tag Optional tag name for the fragment, to later retrieve the
+     * fragment with {@link Activity#findFragmentByTag(String)
+     * Activity.findFragmentByTag(String)}.
+     * 
+     * @return Returns the same FragmentTransaction instance.
+     */
+    public FragmentTransaction add(int containerViewId, Fragment fragment, String tag);
+    
+    /**
+     * Calls {@link #replace(int, Fragment, String)} with a null tag.
+     */
+    public FragmentTransaction replace(int containerViewId, Fragment fragment);
+    
+    /**
+     * Replace an existing fragment that was added to a container.  This is
+     * essentially the same as calling {@link #remove(Fragment)} for all
+     * currently added fragments that were added with the same containerViewId
+     * and then {@link #add(int, Fragment, String)} with the same arguments
+     * given here.
+     * 
+     * @param containerViewId Identifier of the container whose fragment(s) are
+     * to be replaced.
+     * @param fragment The new fragment to place in the container.
+     * @param tag Optional tag name for the fragment, to later retrieve the
+     * fragment with {@link Activity#findFragmentByTag(String)
+     * Activity.findFragmentByTag(String)}.
+     * 
+     * @return Returns the same FragmentTransaction instance.
+     */
+    public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag);
+    
+    /**
+     * Remove an existing fragment.  If it was added to a container, its view
+     * is also removed from that container.
+     * 
+     * @param fragment The fragment to be removed.
+     * 
+     * @return Returns the same FragmentTransaction instance.
+     */
+    public FragmentTransaction remove(Fragment fragment);
+    
+    /**
+     * Hides an existing fragment.  This is only relevant for fragments whose
+     * views have been added to a container, as this will cause the view to
+     * be hidden.
+     * 
+     * @param fragment The fragment to be hidden.
+     * 
+     * @return Returns the same FragmentTransaction instance.
+     */
+    public FragmentTransaction hide(Fragment fragment);
+    
+    /**
+     * Hides a previously hidden fragment.  This is only relevant for fragments whose
+     * views have been added to a container, as this will cause the view to
+     * be shown.
+     * 
+     * @param fragment The fragment to be shown.
+     * 
+     * @return Returns the same FragmentTransaction instance.
+     */
+    public FragmentTransaction show(Fragment fragment);
+
+    /**
+     * @return <code>true</code> if this transaction contains no operations,
+     * <code>false</code> otherwise.
+     */
+    public boolean isEmpty();
+    
+    /**
+     * Bit mask that is set for all enter transitions.
+     */
+    public final int TRANSIT_ENTER_MASK = 0x1000;
+    
+    /**
+     * Bit mask that is set for all exit transitions.
+     */
+    public final int TRANSIT_EXIT_MASK = 0x2000;
+    
+    /** Not set up for a transition. */
+    public final int TRANSIT_UNSET = -1;
+    /** No animation for transition. */
+    public final int TRANSIT_NONE = 0;
+    /** Fragment is being added onto the stack */
+    public final int TRANSIT_FRAGMENT_OPEN = 1 | TRANSIT_ENTER_MASK;
+    /** Fragment is being removed from the stack */
+    public final int TRANSIT_FRAGMENT_CLOSE = 2 | TRANSIT_EXIT_MASK;
+    /** Fragment is being added in a 'next' operation*/
+    public final int TRANSIT_FRAGMENT_NEXT = 3 | TRANSIT_ENTER_MASK;
+    /** Fragment is being removed in a 'previous' operation */
+    public final int TRANSIT_FRAGMENT_PREV = 4 | TRANSIT_EXIT_MASK;
+
+    /**
+     * Set specific animation resources to run for the fragments that are
+     * entering and exiting in this transaction.
+     */
+    public FragmentTransaction setCustomAnimations(int enter, int exit);
+    
+    /**
+     * Select a standard transition animation for this transaction.  May be
+     * one of {@link #TRANSIT_NONE}, {@link #TRANSIT_FRAGMENT_OPEN},
+     * or {@link #TRANSIT_FRAGMENT_CLOSE}
+     */
+    public FragmentTransaction setTransition(int transit);
+
+    /**
+     * Set a custom style resource that will be used for resolving transit
+     * animations.
+     */
+    public FragmentTransaction setTransitionStyle(int styleRes);
+    
+    /**
+     * Add this transaction to the back stack.  This means that the transaction
+     * will be remembered after it is committed, and will reverse its operation
+     * when later popped off the stack.
+     *
+     * @param name An optional name for this back stack state, or null.
+     */
+    public FragmentTransaction addToBackStack(String name);
+
+    /**
+     * Returns true if this FragmentTransaction is allowed to be added to the back
+     * stack. If this method would return false, {@link #addToBackStack(String)}
+     * will throw {@link IllegalStateException}.
+     *
+     * @return True if {@link #addToBackStack(String)} is permitted on this transaction.
+     */
+    public boolean isAddToBackStackAllowed();
+
+    /**
+     * Disallow calls to {@link #addToBackStack(String)}. Any future calls to
+     * addToBackStack will throw {@link IllegalStateException}. If addToBackStack
+     * has already been called, this method will throw IllegalStateException.
+     */
+    public FragmentTransaction disallowAddToBackStack();
+
+    /**
+     * Set the full title to show as a bread crumb when this transaction
+     * is on the back stack, as used by {@link FragmentBreadCrumbs}.
+     *
+     * @param res A string resource containing the title.
+     */
+    public FragmentTransaction setBreadCrumbTitle(int res);
+
+    /**
+     * Like {@link #setBreadCrumbTitle(int)} but taking a raw string; this
+     * method is <em>not</em> recommended, as the string can not be changed
+     * later if the locale changes.
+     */
+    public FragmentTransaction setBreadCrumbTitle(CharSequence text);
+
+    /**
+     * Set the short title to show as a bread crumb when this transaction
+     * is on the back stack, as used by {@link FragmentBreadCrumbs}.
+     *
+     * @param res A string resource containing the title.
+     */
+    public FragmentTransaction setBreadCrumbShortTitle(int res);
+
+    /**
+     * Like {@link #setBreadCrumbShortTitle(int)} but taking a raw string; this
+     * method is <em>not</em> recommended, as the string can not be changed
+     * later if the locale changes.
+     */
+    public FragmentTransaction setBreadCrumbShortTitle(CharSequence text);
+
+    /**
+     * Schedules a commit of this transaction.  Note that the commit does
+     * not happen immediately; it will be scheduled as work on the main thread
+     * to be done the next time that thread is ready.
+     *
+     * @return Returns the identifier of this transaction's back stack entry,
+     * if {@link #addToBackStack(String)} had been called.  Otherwise, returns
+     * a negative number.
+     */
+    public int commit();
+}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 83d422e..c9d5448 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -310,6 +310,10 @@
     
     public void finishHeavyWeightApp() throws RemoteException;
 
+    public void setImmersive(IBinder token, boolean immersive) throws RemoteException;
+    public boolean isImmersive(IBinder token) throws RemoteException;
+    public boolean isTopActivityImmersive() throws RemoteException;
+    
     public void crashApplication(int uid, int initialPid, String packageName,
             String message) throws RemoteException;
 
@@ -321,6 +325,13 @@
     public void revokeUriPermissionFromOwner(IBinder owner, Uri uri,
             int mode) throws RemoteException;
 
+    public int checkGrantUriPermission(int callingUid, String targetPkg,
+            Uri uri, int modeFlags) throws RemoteException;
+
+    // Cause the specified process to dump the specified heap.
+    public boolean dumpHeap(String process, boolean managed, String path,
+        ParcelFileDescriptor fd) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -531,4 +542,6 @@
     int NEW_URI_PERMISSION_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+115;
     int GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+116;
     int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+117;
+    int CHECK_GRANT_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+118;
+    int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+119;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index c8ef17f..830c702 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -87,6 +87,7 @@
     void requestThumbnail(IBinder token) throws RemoteException;
     void scheduleConfigurationChanged(Configuration config) throws RemoteException;
     void updateTimeZone() throws RemoteException;
+    void clearDnsCache() throws RemoteException;
     void processInBackground() throws RemoteException;
     void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
             throws RemoteException;
@@ -97,13 +98,17 @@
     void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
     void profilerControl(boolean start, String path, ParcelFileDescriptor fd)
             throws RemoteException;
+    void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd)
+            throws RemoteException;
     void setSchedulingGroup(int group) throws RemoteException;
     void getMemoryInfo(Debug.MemoryInfo outInfo) throws RemoteException;
     static final int PACKAGE_REMOVED = 0;
     static final int EXTERNAL_STORAGE_UNAVAILABLE = 1;
     void dispatchPackageBroadcast(int cmd, String[] packages) throws RemoteException;
     void scheduleCrash(String msg) throws RemoteException;
-    
+    void dumpActivity(FileDescriptor fd, IBinder servicetoken, String[] args)
+            throws RemoteException;
+
     String descriptor = "android.app.IApplicationThread";
 
     int SCHEDULE_PAUSE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
@@ -140,4 +145,7 @@
     int SCHEDULE_SUICIDE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
     int DISPATCH_PACKAGE_BROADCAST_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
     int SCHEDULE_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+34;
+    int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35;
+    int DUMP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36;
+    int CLEAR_DNS_CACHE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37;
 }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b8c3aa3..4d5f36a 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -997,8 +997,10 @@
             IllegalAccessException {
         Activity activity = (Activity)clazz.newInstance();
         ActivityThread aThread = null;
-        activity.attach(context, aThread, this, token, application, intent, info, title,
-                parent, id, lastNonConfigurationInstance, new Configuration());
+        activity.attach(context, aThread, this, token, application, intent,
+                info, title, parent, id,
+                (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
+                new Configuration());
         return activity;
     }
 
@@ -1058,21 +1060,23 @@
     }
     
     public void callActivityOnDestroy(Activity activity) {
-      if (mWaitingActivities != null) {
-          synchronized (mSync) {
-              final int N = mWaitingActivities.size();
-              for (int i=0; i<N; i++) {
-                  final ActivityWaiter aw = mWaitingActivities.get(i);
-                  final Intent intent = aw.intent;
-                  if (intent.filterEquals(activity.getIntent())) {
-                      aw.activity = activity;
-                      mMessageQueue.addIdleHandler(new ActivityGoing(aw));
-                  }
-              }
-          }
-      }
+      // TODO: the following block causes intermittent hangs when using startActivity
+      // temporarily comment out until root cause is fixed (bug 2630683)
+//      if (mWaitingActivities != null) {
+//          synchronized (mSync) {
+//              final int N = mWaitingActivities.size();
+//              for (int i=0; i<N; i++) {
+//                  final ActivityWaiter aw = mWaitingActivities.get(i);
+//                  final Intent intent = aw.intent;
+//                  if (intent.filterEquals(activity.getIntent())) {
+//                      aw.activity = activity;
+//                      mMessageQueue.addIdleHandler(new ActivityGoing(aw));
+//                  }
+//              }
+//          }
+//      }
       
-      activity.onDestroy();
+      activity.performDestroy();
       
       if (mActivityMonitors != null) {
           synchronized (mSync) {
@@ -1331,7 +1335,7 @@
      *                      is being started.
      * @param token Internal token identifying to the system who is starting 
      *              the activity; may be null.
-     * @param target Which activity is perform the start (and thus receiving 
+     * @param target Which activity is performing the start (and thus receiving 
      *               any result); may be null if this call is not being made
      *               from an activity.
      * @param intent The actual Intent to start.
@@ -1381,6 +1385,64 @@
         return null;
     }
 
+    /**
+     * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
+     * but for calls from a {#link Fragment}.
+     * 
+     * @param who The Context from which the activity is being started.
+     * @param contextThread The main thread of the Context from which the activity
+     *                      is being started.
+     * @param token Internal token identifying to the system who is starting 
+     *              the activity; may be null.
+     * @param target Which fragment is performing the start (and thus receiving 
+     *               any result).
+     * @param intent The actual Intent to start.
+     * @param requestCode Identifier for this request's result; less than zero 
+     *                    if the caller is not expecting a result.
+     * 
+     * @return To force the return of a particular result, return an 
+     *         ActivityResult object containing the desired data; otherwise
+     *         return null.  The default implementation always returns null.
+     *  
+     * @throws android.content.ActivityNotFoundException
+     * 
+     * @see Activity#startActivity(Intent)
+     * @see Activity#startActivityForResult(Intent, int)
+     * @see Activity#startActivityFromChild
+     * 
+     * {@hide}
+     */
+    public ActivityResult execStartActivity(
+        Context who, IBinder contextThread, IBinder token, Fragment target,
+        Intent intent, int requestCode) {
+        IApplicationThread whoThread = (IApplicationThread) contextThread;
+        if (mActivityMonitors != null) {
+            synchronized (mSync) {
+                final int N = mActivityMonitors.size();
+                for (int i=0; i<N; i++) {
+                    final ActivityMonitor am = mActivityMonitors.get(i);
+                    if (am.match(who, null, intent)) {
+                        am.mHits++;
+                        if (am.isBlocking()) {
+                            return requestCode >= 0 ? am.getResult() : null;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        try {
+            int result = ActivityManagerNative.getDefault()
+                .startActivity(whoThread, intent,
+                        intent.resolveTypeIfNeeded(who.getContentResolver()),
+                        null, 0, token, target != null ? target.mWho : null,
+                        requestCode, false, false);
+            checkStartActivityResult(result, intent);
+        } catch (RemoteException e) {
+        }
+        return null;
+    }
+
     /*package*/ final void init(ActivityThread thread,
             Context instrContext, Context appContext, ComponentName component, 
             IInstrumentationWatcher watcher) {
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index 0ece2fc..7b9bd60 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -36,7 +36,9 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
+import android.view.View.OnClickListener;
 import android.widget.BaseAdapter;
+import android.widget.Button;
 import android.widget.Filter;
 import android.widget.Filterable;
 import android.widget.ListView;
@@ -101,11 +103,13 @@
         protected List<ListItem> mActivitiesList;
 
         private Filter mFilter;
+        private final boolean mShowIcons;
         
         public ActivityAdapter(IconResizer resizer) {
             mIconResizer = resizer;
             mInflater = (LayoutInflater) LauncherActivity.this.getSystemService(
                     Context.LAYOUT_INFLATER_SERVICE);
+            mShowIcons = onEvaluateShowIcons();
             mActivitiesList = makeListItems();
         }
 
@@ -158,13 +162,14 @@
         private void bindView(View view, ListItem item) {
             TextView text = (TextView) view;
             text.setText(item.label);
-            if (item.icon == null) {
-                item.icon = mIconResizer.createIconThumbnail(
-                        item.resolveInfo.loadIcon(getPackageManager()));
+            if (mShowIcons) {
+                if (item.icon == null) {
+                    item.icon = mIconResizer.createIconThumbnail(item.resolveInfo.loadIcon(getPackageManager()));
+                }
+                text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null);
             }
-            text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null);
         }
-        
+
         public Filter getFilter() {
             if (mFilter == null) {
                 mFilter = new ArrayFilter();
@@ -337,19 +342,52 @@
         requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
         setProgressBarIndeterminateVisibility(true);
         onSetContentView();
-            
+
         mIconResizer = new IconResizer();
         
         mIntent = new Intent(getTargetIntent());
         mIntent.setComponent(null);
         mAdapter = new ActivityAdapter(mIconResizer);
-        
+
         setListAdapter(mAdapter);
         getListView().setTextFilterEnabled(true);
-        
+
+        updateAlertTitle();
+        updateButtonText();
+
         setProgressBarIndeterminateVisibility(false);
     }
 
+    private void updateAlertTitle() {
+        TextView alertTitle = (TextView) findViewById(com.android.internal.R.id.alertTitle);
+        if (alertTitle != null) {
+            alertTitle.setText(getTitle());
+        }
+    }
+
+    private void updateButtonText() {
+        Button cancelButton = (Button) findViewById(com.android.internal.R.id.button1);
+        if (cancelButton != null) {
+            cancelButton.setOnClickListener(new OnClickListener() {
+                public void onClick(View v) {
+                    finish();
+                }
+            });
+        }
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        super.setTitle(title);
+        updateAlertTitle();
+    }
+
+    @Override
+    public void setTitle(int titleId) {
+        super.setTitle(titleId);
+        updateAlertTitle();
+    }
+
     /**
      * Override to call setContentView() with your own content view to
      * customize the list layout.
@@ -407,7 +445,7 @@
         // Load all matching activities and sort correctly
         List<ResolveInfo> list = onQueryPackageManager(mIntent);
         Collections.sort(list, new ResolveInfo.DisplayNameComparator(mPackageManager));
-        
+
         ArrayList<ListItem> result = new ArrayList<ListItem>(list.size());
         int listSize = list.size();
         for (int i = 0; i < listSize; i++) {
@@ -417,4 +455,13 @@
 
         return result;
     }
+
+    /**
+     * Whether or not to show icons in the list
+     * @hide keeping this private for now, since only Settings needs it
+     * @return true to show icons beside the activity names, false otherwise
+     */
+    protected boolean onEvaluateShowIcons() {
+        return true;
+    }
 }
diff --git a/core/java/android/app/ListActivity.java b/core/java/android/app/ListActivity.java
index 4bf5518..d49968f 100644
--- a/core/java/android/app/ListActivity.java
+++ b/core/java/android/app/ListActivity.java
@@ -309,7 +309,7 @@
         if (mList != null) {
             return;
         }
-        setContentView(com.android.internal.R.layout.list_content);
+        setContentView(com.android.internal.R.layout.list_content_simple);
 
     }
 
diff --git a/core/java/android/app/ListFragment.java b/core/java/android/app/ListFragment.java
new file mode 100644
index 0000000..6e2f4b6
--- /dev/null
+++ b/core/java/android/app/ListFragment.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import android.widget.AdapterView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * A fragment that displays a list of items by binding to a data source such as
+ * an array or Cursor, and exposes event handlers when the user selects an item.
+ * <p>
+ * ListFragment hosts a {@link android.widget.ListView ListView} object that can
+ * be bound to different data sources, typically either an array or a Cursor
+ * holding query results. Binding, screen layout, and row layout are discussed
+ * in the following sections.
+ * <p>
+ * <strong>Screen Layout</strong>
+ * </p>
+ * <p>
+ * ListFragment has a default layout that consists of a single list view.
+ * However, if you desire, you can customize the fragment layout by returning
+ * your own view hierarchy from {@link #onCreateView}.
+ * To do this, your view hierarchy <em>must</em> contain a ListView object with the
+ * id "@android:id/list" (or {@link android.R.id#list} if it's in code)
+ * <p>
+ * Optionally, your view hierarchy can contain another view object of any type to
+ * display when the list view is empty. This "empty list" notifier must have an
+ * id "android:empty". Note that when an empty view is present, the list view
+ * will be hidden when there is no data to display.
+ * <p>
+ * The following code demonstrates an (ugly) custom list layout. It has a list
+ * with a green background, and an alternate red "no data" message.
+ * </p>
+ *
+ * <pre>
+ * &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+ * &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ *         android:orientation=&quot;vertical&quot;
+ *         android:layout_width=&quot;match_parent&quot;
+ *         android:layout_height=&quot;match_parent&quot;
+ *         android:paddingLeft=&quot;8dp&quot;
+ *         android:paddingRight=&quot;8dp&quot;&gt;
+ *
+ *     &lt;ListView android:id=&quot;@id/android:list&quot;
+ *               android:layout_width=&quot;match_parent&quot;
+ *               android:layout_height=&quot;match_parent&quot;
+ *               android:background=&quot;#00FF00&quot;
+ *               android:layout_weight=&quot;1&quot;
+ *               android:drawSelectorOnTop=&quot;false&quot;/&gt;
+ *
+ *     &lt;TextView android:id=&quot;@id/android:empty&quot;
+ *               android:layout_width=&quot;match_parent&quot;
+ *               android:layout_height=&quot;match_parent&quot;
+ *               android:background=&quot;#FF0000&quot;
+ *               android:text=&quot;No data&quot;/&gt;
+ * &lt;/LinearLayout&gt;
+ * </pre>
+ *
+ * <p>
+ * <strong>Row Layout</strong>
+ * </p>
+ * <p>
+ * You can specify the layout of individual rows in the list. You do this by
+ * specifying a layout resource in the ListAdapter object hosted by the fragment
+ * (the ListAdapter binds the ListView to the data; more on this later).
+ * <p>
+ * A ListAdapter constructor takes a parameter that specifies a layout resource
+ * for each row. It also has two additional parameters that let you specify
+ * which data field to associate with which object in the row layout resource.
+ * These two parameters are typically parallel arrays.
+ * </p>
+ * <p>
+ * Android provides some standard row layout resources. These are in the
+ * {@link android.R.layout} class, and have names such as simple_list_item_1,
+ * simple_list_item_2, and two_line_list_item. The following layout XML is the
+ * source for the resource two_line_list_item, which displays two data
+ * fields,one above the other, for each list row.
+ * </p>
+ *
+ * <pre>
+ * &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
+ * &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ *     android:layout_width=&quot;match_parent&quot;
+ *     android:layout_height=&quot;wrap_content&quot;
+ *     android:orientation=&quot;vertical&quot;&gt;
+ *
+ *     &lt;TextView android:id=&quot;@+id/text1&quot;
+ *         android:textSize=&quot;16sp&quot;
+ *         android:textStyle=&quot;bold&quot;
+ *         android:layout_width=&quot;match_parent&quot;
+ *         android:layout_height=&quot;wrap_content&quot;/&gt;
+ *
+ *     &lt;TextView android:id=&quot;@+id/text2&quot;
+ *         android:textSize=&quot;16sp&quot;
+ *         android:layout_width=&quot;match_parent&quot;
+ *         android:layout_height=&quot;wrap_content&quot;/&gt;
+ * &lt;/LinearLayout&gt;
+ * </pre>
+ *
+ * <p>
+ * You must identify the data bound to each TextView object in this layout. The
+ * syntax for this is discussed in the next section.
+ * </p>
+ * <p>
+ * <strong>Binding to Data</strong>
+ * </p>
+ * <p>
+ * You bind the ListFragment's ListView object to data using a class that
+ * implements the {@link android.widget.ListAdapter ListAdapter} interface.
+ * Android provides two standard list adapters:
+ * {@link android.widget.SimpleAdapter SimpleAdapter} for static data (Maps),
+ * and {@link android.widget.SimpleCursorAdapter SimpleCursorAdapter} for Cursor
+ * query results.
+ * </p>
+ * <p>
+ * You <b>must</b> use
+ * {@link #setListAdapter(ListAdapter) ListFragment.setListAdapter()} to
+ * associate the list with an adapter.  Do not directly call
+ * {@link ListView#setAdapter(ListAdapter) ListView.setAdapter()} or else
+ * important initialization will be skipped.
+ * </p>
+ *
+ * @see #setListAdapter
+ * @see android.widget.ListView
+ */
+public class ListFragment extends Fragment {
+    final private Handler mHandler = new Handler();
+
+    final private Runnable mRequestFocus = new Runnable() {
+        public void run() {
+            mList.focusableViewAvailable(mList);
+        }
+    };
+    
+    final private AdapterView.OnItemClickListener mOnClickListener
+            = new AdapterView.OnItemClickListener() {
+        public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+            onListItemClick((ListView)parent, v, position, id);
+        }
+    };
+
+    ListAdapter mAdapter;
+    ListView mList;
+    View mEmptyView;
+    TextView mStandardEmptyView;
+    View mProgressContainer;
+    View mListContainer;
+    boolean mSetEmptyText;
+    boolean mListShown;
+
+    public ListFragment() {
+    }
+
+    /**
+     * Provide default implementation to return a simple list view.  Subclasses
+     * can override to replace with their own layout.  If doing so, the
+     * returned view hierarchy <em>must</em> have a ListView whose id
+     * is {@link android.R.id#list android.R.id.list} and can optionally
+     * have a sibling view id {@link android.R.id#empty android.R.id.empty}
+     * that is to be shown when the list is empty.
+     * 
+     * <p>If you are overriding this method with your own custom content,
+     * consider including the standard layout {@link android.R.layout#list_content}
+     * in your layout file, so that you continue to retain all of the standard
+     * behavior of ListFragment.  In particular, this is currently the only
+     * way to have the built-in indeterminant progress state be shown.
+     */
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(com.android.internal.R.layout.list_content,
+                container, false);
+    }
+
+    /**
+     * Attach to list view once Fragment is ready to run.
+     */
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        ensureList();
+    }
+
+    /**
+     * Detach from list view.
+     */
+    @Override
+    public void onDestroyView() {
+        mHandler.removeCallbacks(mRequestFocus);
+        mList = null;
+        super.onDestroyView();
+    }
+
+    /**
+     * This method will be called when an item in the list is selected.
+     * Subclasses should override. Subclasses can call
+     * getListView().getItemAtPosition(position) if they need to access the
+     * data associated with the selected item.
+     *
+     * @param l The ListView where the click happened
+     * @param v The view that was clicked within the ListView
+     * @param position The position of the view in the list
+     * @param id The row id of the item that was clicked
+     */
+    public void onListItemClick(ListView l, View v, int position, long id) {
+    }
+
+    /**
+     * Provide the cursor for the list view.
+     */
+    public void setListAdapter(ListAdapter adapter) {
+        boolean hadAdapter = mAdapter != null;
+        mAdapter = adapter;
+        if (mList != null) {
+            mList.setAdapter(adapter);
+            if (!mListShown && !hadAdapter) {
+                // The list was hidden, and previously didn't have an
+                // adapter.  It is now time to show it.
+                setListShown(true, getView().getWindowToken() != null);
+            }
+        }
+    }
+
+    /**
+     * Set the currently selected list item to the specified
+     * position with the adapter's data
+     *
+     * @param position
+     */
+    public void setSelection(int position) {
+        ensureList();
+        mList.setSelection(position);
+    }
+
+    /**
+     * Get the position of the currently selected list item.
+     */
+    public int getSelectedItemPosition() {
+        ensureList();
+        return mList.getSelectedItemPosition();
+    }
+
+    /**
+     * Get the cursor row ID of the currently selected list item.
+     */
+    public long getSelectedItemId() {
+        ensureList();
+        return mList.getSelectedItemId();
+    }
+
+    /**
+     * Get the activity's list view widget.
+     */
+    public ListView getListView() {
+        ensureList();
+        return mList;
+    }
+
+    /**
+     * The default content for a ListFragment has a TextView that can
+     * be shown when the list is empty.  If you would like to have it
+     * shown, call this method to supply the text it should use.
+     */
+    public void setEmptyText(CharSequence text) {
+        ensureList();
+        if (mStandardEmptyView == null) {
+            throw new IllegalStateException("Can't be used with a custom content view");
+        }
+        mStandardEmptyView.setText(text);
+        if (!mSetEmptyText) {
+            mList.setEmptyView(mStandardEmptyView);
+            mSetEmptyText = true;
+        }
+    }
+    
+    /**
+     * Control whether the list is being displayed.  You can make it not
+     * displayed if you are waiting for the initial data to show in it.  During
+     * this time an indeterminant progress indicator will be shown instead.
+     * 
+     * <p>Applications do not normally need to use this themselves.  The default
+     * behavior of ListFragment is to start with the list not being shown, only
+     * showing it once an adapter is given with {@link #setListAdapter(ListAdapter)}.
+     * If the list at that point had not been shown, when it does get shown
+     * it will be do without the user ever seeing the hidden state.
+     * 
+     * @param shown If true, the list view is shown; if false, the progress
+     * indicator.  The initial value is true.
+     */
+    public void setListShown(boolean shown) {
+        setListShown(shown, true);
+    }
+    
+    /**
+     * Like {@link #setListShown(boolean)}, but no animation is used when
+     * transitioning from the previous state.
+     */
+    public void setListShownNoAnimation(boolean shown) {
+        setListShown(shown, false);
+    }
+    
+    /**
+     * Control whether the list is being displayed.  You can make it not
+     * displayed if you are waiting for the initial data to show in it.  During
+     * this time an indeterminant progress indicator will be shown instead.
+     * 
+     * @param shown If true, the list view is shown; if false, the progress
+     * indicator.  The initial value is true.
+     * @param animate If true, an animation will be used to transition to the
+     * new state.
+     */
+    private void setListShown(boolean shown, boolean animate) {
+        ensureList();
+        if (mProgressContainer == null) {
+            throw new IllegalStateException("Can't be used with a custom content view");
+        }
+        if (mListShown == shown) {
+            return;
+        }
+        mListShown = shown;
+        if (shown) {
+            if (animate) {
+                mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
+                        getActivity(), android.R.anim.fade_out));
+                mListContainer.startAnimation(AnimationUtils.loadAnimation(
+                        getActivity(), android.R.anim.fade_in));
+            }
+            mProgressContainer.setVisibility(View.GONE);
+            mListContainer.setVisibility(View.VISIBLE);
+        } else {
+            if (animate) {
+                mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
+                        getActivity(), android.R.anim.fade_in));
+                mListContainer.startAnimation(AnimationUtils.loadAnimation(
+                        getActivity(), android.R.anim.fade_out));
+            }
+            mProgressContainer.setVisibility(View.VISIBLE);
+            mListContainer.setVisibility(View.GONE);
+        }
+    }
+    
+    /**
+     * Get the ListAdapter associated with this activity's ListView.
+     */
+    public ListAdapter getListAdapter() {
+        return mAdapter;
+    }
+
+    private void ensureList() {
+        if (mList != null) {
+            return;
+        }
+        View root = getView();
+        if (root == null) {
+            throw new IllegalStateException("Content view not yet created");
+        }
+        if (root instanceof ListView) {
+            mList = (ListView)root;
+        } else {
+            mStandardEmptyView = (TextView)root.findViewById(
+                    com.android.internal.R.id.internalEmpty);
+            if (mStandardEmptyView == null) {
+                mEmptyView = root.findViewById(android.R.id.empty);
+            }
+            mProgressContainer = root.findViewById(com.android.internal.R.id.progressContainer);
+            mListContainer = root.findViewById(com.android.internal.R.id.listContainer);
+            View rawListView = root.findViewById(android.R.id.list);
+            if (!(rawListView instanceof ListView)) {
+                throw new RuntimeException(
+                        "Content has view with id attribute 'android.R.id.list' "
+                        + "that is not a ListView class");
+            }
+            mList = (ListView)rawListView;
+            if (mList == null) {
+                throw new RuntimeException(
+                        "Your content must have a ListView whose id attribute is " +
+                        "'android.R.id.list'");
+            }
+            if (mEmptyView != null) {
+                mList.setEmptyView(mEmptyView);
+            }
+        }
+        mListShown = true;
+        mList.setOnItemClickListener(mOnClickListener);
+        if (mAdapter != null) {
+            setListAdapter(mAdapter);
+        } else {
+            // We are starting without an adapter, so assume we won't
+            // have our data right away and start with the progress indicator.
+            if (mProgressContainer != null) {
+                setListShown(false, false);
+            }
+        }
+        mHandler.post(mRequestFocus);
+    }
+}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0644f96..7f24d27 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -660,37 +660,40 @@
         final IntentReceiverLeaked mLocation;
         RuntimeException mUnregisterLocation;
 
-        final class Args implements Runnable {
+        final class Args extends BroadcastReceiver.PendingResult implements Runnable {
             private Intent mCurIntent;
-            private int mCurCode;
-            private String mCurData;
-            private Bundle mCurMap;
-            private boolean mCurOrdered;
-            private boolean mCurSticky;
+            private final boolean mOrdered;
 
+            public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
+                    boolean ordered, boolean sticky) {
+                super(resultCode, resultData, resultExtras,
+                        mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED,
+                        ordered, sticky, mIIntentReceiver.asBinder());
+                mCurIntent = intent;
+                mOrdered = ordered;
+            }
+            
             public void run() {
-                BroadcastReceiver receiver = mReceiver;
+                final BroadcastReceiver receiver = mReceiver;
+                final boolean ordered = mOrdered;
+                
                 if (ActivityThread.DEBUG_BROADCAST) {
                     int seq = mCurIntent.getIntExtra("seq", -1);
                     Slog.i(ActivityThread.TAG, "Dispatching broadcast " + mCurIntent.getAction()
                             + " seq=" + seq + " to " + mReceiver);
                     Slog.i(ActivityThread.TAG, "  mRegistered=" + mRegistered
-                            + " mCurOrdered=" + mCurOrdered);
+                            + " mOrderedHint=" + ordered);
                 }
                 
-                IActivityManager mgr = ActivityManagerNative.getDefault();
-                Intent intent = mCurIntent;
+                final IActivityManager mgr = ActivityManagerNative.getDefault();
+                final Intent intent = mCurIntent;
                 mCurIntent = null;
                 
                 if (receiver == null) {
-                    if (mRegistered && mCurOrdered) {
-                        try {
-                            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
-                                    "Finishing null broadcast to " + mReceiver);
-                            mgr.finishReceiver(mIIntentReceiver,
-                                    mCurCode, mCurData, mCurMap, false);
-                        } catch (RemoteException ex) {
-                        }
+                    if (mRegistered && ordered) {
+                        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+                                "Finishing null broadcast to " + mReceiver);
+                        sendFinished(mgr);
                     }
                     return;
                 }
@@ -698,24 +701,14 @@
                 try {
                     ClassLoader cl =  mReceiver.getClass().getClassLoader();
                     intent.setExtrasClassLoader(cl);
-                    if (mCurMap != null) {
-                        mCurMap.setClassLoader(cl);
-                    }
-                    receiver.setOrderedHint(true);
-                    receiver.setResult(mCurCode, mCurData, mCurMap);
-                    receiver.clearAbortBroadcast();
-                    receiver.setOrderedHint(mCurOrdered);
-                    receiver.setInitialStickyHint(mCurSticky);
+                    setExtrasClassLoader(cl);
+                    receiver.setPendingResult(this);
                     receiver.onReceive(mContext, intent);
                 } catch (Exception e) {
-                    if (mRegistered && mCurOrdered) {
-                        try {
-                            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
-                                    "Finishing failed broadcast to " + mReceiver);
-                            mgr.finishReceiver(mIIntentReceiver,
-                                    mCurCode, mCurData, mCurMap, false);
-                        } catch (RemoteException ex) {
-                        }
+                    if (mRegistered && ordered) {
+                        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+                                "Finishing failed broadcast to " + mReceiver);
+                        sendFinished(mgr);
                     }
                     if (mInstrumentation == null ||
                             !mInstrumentation.onException(mReceiver, e)) {
@@ -724,17 +717,9 @@
                             + " in " + mReceiver, e);
                     }
                 }
-                if (mRegistered && mCurOrdered) {
-                    try {
-                        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
-                                "Finishing broadcast to " + mReceiver);
-                        mgr.finishReceiver(mIIntentReceiver,
-                                receiver.getResultCode(),
-                                receiver.getResultData(),
-                                receiver.getResultExtras(false),
-                                receiver.getAbortBroadcast());
-                    } catch (RemoteException ex) {
-                    }
+                
+                if (receiver.getPendingResult() != null) {
+                    finish();
                 }
             }
         }
@@ -798,23 +783,13 @@
                 Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
                         + " to " + mReceiver);
             }
-            Args args = new Args();
-            args.mCurIntent = intent;
-            args.mCurCode = resultCode;
-            args.mCurData = data;
-            args.mCurMap = extras;
-            args.mCurOrdered = ordered;
-            args.mCurSticky = sticky;
+            Args args = new Args(intent, resultCode, data, extras, ordered, sticky);
             if (!mActivityThread.post(args)) {
                 if (mRegistered && ordered) {
                     IActivityManager mgr = ActivityManagerNative.getDefault();
-                    try {
-                        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
-                                "Finishing sync broadcast to " + mReceiver);
-                        mgr.finishReceiver(mIIntentReceiver, args.mCurCode,
-                                args.mCurData, args.mCurMap, false);
-                    } catch (RemoteException ex) {
-                    }
+                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+                            "Finishing sync broadcast to " + mReceiver);
+                    args.sendFinished(mgr);
                 }
             }
         }
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
new file mode 100644
index 0000000..4d4ea9a
--- /dev/null
+++ b/core/java/android/app/LoaderManager.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.Loader;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.SparseArray;
+
+/**
+ * Interface associated with an {@link Activity} or {@link Fragment} for managing
+ * one or more {@link android.content.Loader} instances associated with it.
+ */
+public interface LoaderManager {
+    /**
+     * Callback interface for a client to interact with the manager.
+     */
+    public interface LoaderCallbacks<D> {
+        /**
+         * Instantiate and return a new Loader for the given ID.
+         *
+         * @param id The ID whose loader is to be created.
+         * @param args Any arguments supplied by the caller.
+         * @return Return a new Loader instance that is ready to start loading.
+         */
+        public Loader<D> onCreateLoader(int id, Bundle args);
+
+        /**
+         * Called when a previously created loader has finished its load.  Note
+         * that normally an application is <em>not</em> allowed to commit fragment
+         * transactions while in this call, since it can happen after an
+         * activity's state is saved.  See {@link FragmentManager#openTransaction()
+         * FragmentManager.openTransaction()} for further discussion on this.
+         * 
+         * @param loader The Loader that has finished.
+         * @param data The data generated by the Loader.
+         */
+        public void onLoadFinished(Loader<D> loader, D data);
+    }
+    
+    /**
+     * Ensures a loader is initialized and active.  If the loader doesn't
+     * already exist, one is created and (if the activity/fragment is currently
+     * started) starts the loader.  Otherwise the last created
+     * loader is re-used.
+     *
+     * <p>In either case, the given callback is associated with the loader, and
+     * will be called as the loader state changes.  If at the point of call
+     * the caller is in its started state, and the requested loader
+     * already exists and has generated its data, then
+     * callback. {@link LoaderCallbacks#onLoadFinished} will
+     * be called immediately (inside of this function), so you must be prepared
+     * for this to happen.
+     */
+    public <D> Loader<D> initLoader(int id, Bundle args,
+            LoaderManager.LoaderCallbacks<D> callback);
+
+    /**
+     * Creates a new loader in this manager, registers the callbacks to it,
+     * and (if the activity/fragment is currently started) starts loading it.
+     * If a loader with the same id has previously been
+     * started it will automatically be destroyed when the new loader completes
+     * its work. The callback will be delivered before the old loader
+     * is destroyed.
+     */
+    public <D> Loader<D> restartLoader(int id, Bundle args,
+            LoaderManager.LoaderCallbacks<D> callback);
+
+    /**
+     * Stops and removes the loader with the given ID.
+     */
+    public void stopLoader(int id);
+
+    /**
+     * Return the Loader with the given id or null if no matching Loader
+     * is found.
+     */
+    public <D> Loader<D> getLoader(int id);
+}
+
+class LoaderManagerImpl implements LoaderManager {
+    static final String TAG = "LoaderManagerImpl";
+    static final boolean DEBUG = true;
+
+    // These are the currently active loaders.  A loader is here
+    // from the time its load is started until it has been explicitly
+    // stopped or restarted by the application.
+    final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>();
+
+    // These are previously run loaders.  This list is maintained internally
+    // to avoid destroying a loader while an application is still using it.
+    // It allows an application to restart a loader, but continue using its
+    // previously run loader until the new loader's data is available.
+    final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>();
+
+    Activity mActivity;
+    boolean mStarted;
+    boolean mRetaining;
+    boolean mRetainingStarted;
+
+    final class LoaderInfo implements Loader.OnLoadCompleteListener<Object> {
+        final int mId;
+        final Bundle mArgs;
+        LoaderManager.LoaderCallbacks<Object> mCallbacks;
+        Loader<Object> mLoader;
+        Object mData;
+        boolean mStarted;
+        boolean mRetaining;
+        boolean mRetainingStarted;
+        boolean mDestroyed;
+        boolean mListenerRegistered;
+        
+        public LoaderInfo(int id, Bundle args, LoaderManager.LoaderCallbacks<Object> callbacks) {
+            mId = id;
+            mArgs = args;
+            mCallbacks = callbacks;
+        }
+        
+        void start() {
+            if (mRetaining && mRetainingStarted) {
+                // Our owner is started, but we were being retained from a
+                // previous instance in the started state...  so there is really
+                // nothing to do here, since the loaders are still started.
+                mStarted = true;
+                return;
+            }
+
+            if (mStarted) {
+                // If loader already started, don't restart.
+                return;
+            }
+
+            if (DEBUG) Log.v(TAG, "  Starting: " + this);
+            if (mLoader == null && mCallbacks != null) {
+               mLoader = mCallbacks.onCreateLoader(mId, mArgs);
+            }
+            if (mLoader != null) {
+                if (!mListenerRegistered) {
+                    mLoader.registerListener(mId, this);
+                    mListenerRegistered = true;
+                }
+                mLoader.startLoading();
+                mStarted = true;
+            }
+        }
+        
+        void retain() {
+            if (DEBUG) Log.v(TAG, "  Retaining: " + this);
+            mRetaining = true;
+            mRetainingStarted = mStarted;
+            mStarted = false;
+            mCallbacks = null;
+        }
+        
+        void finishRetain() {
+            if (mRetaining) {
+                if (DEBUG) Log.v(TAG, "  Finished Retaining: " + this);
+                mRetaining = false;
+                if (mStarted != mRetainingStarted) {
+                    if (!mStarted) {
+                        // This loader was retained in a started state, but
+                        // at the end of retaining everything our owner is
+                        // no longer started...  so make it stop.
+                        stop();
+                    }
+                }
+                if (mStarted && mData != null) {
+                    // This loader was retained, and now at the point of
+                    // finishing the retain we find we remain started, have
+                    // our data, and the owner has a new callback...  so
+                    // let's deliver the data now.
+                    callOnLoadFinished(mLoader, mData);
+                }
+            }
+        }
+        
+        void stop() {
+            if (DEBUG) Log.v(TAG, "  Stopping: " + this);
+            mStarted = false;
+            if (!mRetaining) {
+                if (mLoader != null && mListenerRegistered) {
+                    // Let the loader know we're done with it
+                    mListenerRegistered = false;
+                    mLoader.unregisterListener(this);
+                    mLoader.stopLoading();
+                }
+                mData = null;
+            }
+        }
+        
+        void destroy() {
+            if (DEBUG) Log.v(TAG, "  Destroying: " + this);
+            mDestroyed = true;
+            mCallbacks = null;
+            if (mLoader != null) {
+                if (mListenerRegistered) {
+                    mListenerRegistered = false;
+                    mLoader.unregisterListener(this);
+                }
+                mLoader.destroy();
+            }
+        }
+        
+        @Override public void onLoadComplete(Loader<Object> loader, Object data) {
+            if (DEBUG) Log.v(TAG, "onLoadComplete: " + this + " mDestroyed=" + mDestroyed);
+
+            if (mDestroyed) {
+                return;
+            }
+            
+            // Notify of the new data so the app can switch out the old data before
+            // we try to destroy it.
+            mData = data;
+            callOnLoadFinished(loader, data);
+
+            if (DEBUG) Log.v(TAG, "onLoadFinished returned: " + this);
+
+            // We have now given the application the new loader with its
+            // loaded data, so it should have stopped using the previous
+            // loader.  If there is a previous loader on the inactive list,
+            // clean it up.
+            LoaderInfo info = mInactiveLoaders.get(mId);
+            if (info != null && info != this) {
+                info.destroy();
+                mInactiveLoaders.remove(mId);
+            }
+        }
+
+        void callOnLoadFinished(Loader<Object> loader, Object data) {
+            if (mCallbacks != null) {
+                String lastBecause = null;
+                if (mActivity != null) {
+                    lastBecause = mActivity.mFragments.mNoTransactionsBecause;
+                    mActivity.mFragments.mNoTransactionsBecause = "onLoadFinished";
+                }
+                try {
+                    mCallbacks.onLoadFinished(loader, data);
+                } finally {
+                    if (mActivity != null) {
+                        mActivity.mFragments.mNoTransactionsBecause = lastBecause;
+                    }
+                }
+            }
+        }
+        
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(64);
+            sb.append("LoaderInfo{");
+            sb.append(Integer.toHexString(System.identityHashCode(this)));
+            sb.append(" #");
+            sb.append(mId);
+            if (mArgs != null) {
+                sb.append(" ");
+                sb.append(mArgs.toString());
+            }
+            sb.append("}");
+            return sb.toString();
+        }
+    }
+    
+    LoaderManagerImpl(Activity activity, boolean started) {
+        mActivity = activity;
+        mStarted = started;
+    }
+    
+    void updateActivity(Activity activity) {
+        mActivity = activity;
+    }
+    
+    private LoaderInfo createLoader(int id, Bundle args,
+            LoaderManager.LoaderCallbacks<Object> callback) {
+        LoaderInfo info = new LoaderInfo(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
+        mLoaders.put(id, info);
+        Loader<Object> loader = callback.onCreateLoader(id, args);
+        info.mLoader = (Loader<Object>)loader;
+        if (mStarted) {
+            // The activity will start all existing loaders in it's onStart(),
+            // so only start them here if we're past that point of the activitiy's
+            // life cycle
+            info.start();
+        }
+        return info;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
+        LoaderInfo info = mLoaders.get(id);
+        
+        if (DEBUG) Log.v(TAG, "initLoader in " + this + ": cur=" + info);
+
+        if (info == null) {
+            // Loader doesn't already exist; create.
+            info = createLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
+        } else {
+            info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
+        }
+        
+        if (info.mData != null && mStarted) {
+            // If the loader has already generated its data, report it now.
+            info.callOnLoadFinished(info.mLoader, info.mData);
+        }
+        
+        return (Loader<D>)info.mLoader;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public <D> Loader<D> restartLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
+        LoaderInfo info = mLoaders.get(id);
+        if (DEBUG) Log.v(TAG, "restartLoader in " + this + ": cur=" + info);
+        if (info != null) {
+            LoaderInfo inactive = mInactiveLoaders.get(id);
+            if (inactive != null) {
+                if (info.mData != null) {
+                    // This loader now has data...  we are probably being
+                    // called from within onLoadComplete, where we haven't
+                    // yet destroyed the last inactive loader.  So just do
+                    // that now.
+                    if (DEBUG) Log.v(TAG, "  Removing last inactive loader in " + this);
+                    inactive.destroy();
+                    mInactiveLoaders.put(id, info);
+                } else {
+                    // We already have an inactive loader for this ID that we are
+                    // waiting for!  Now we have three active loaders... let's just
+                    // drop the one in the middle, since we are still waiting for
+                    // its result but that result is already out of date.
+                    if (DEBUG) Log.v(TAG, "  Removing intermediate loader in " + this);
+                    info.destroy();
+                }
+            } else {
+                // Keep track of the previous instance of this loader so we can destroy
+                // it when the new one completes.
+                if (DEBUG) Log.v(TAG, "  Making inactive: " + info);
+                mInactiveLoaders.put(id, info);
+            }
+        }
+        
+        info = createLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
+        return (Loader<D>)info.mLoader;
+    }
+    
+    public void stopLoader(int id) {
+        if (DEBUG) Log.v(TAG, "stopLoader in " + this + " of " + id);
+        int idx = mLoaders.indexOfKey(id);
+        if (idx >= 0) {
+            LoaderInfo info = mLoaders.valueAt(idx);
+            mLoaders.removeAt(idx);
+            info.destroy();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public <D> Loader<D> getLoader(int id) {
+        LoaderInfo loaderInfo = mLoaders.get(id);
+        if (loaderInfo != null) {
+            return (Loader<D>)mLoaders.get(id).mLoader;
+        }
+        return null;
+    }
+ 
+    void doStart() {
+        if (DEBUG) Log.v(TAG, "Starting: " + this);
+        if (mStarted) {
+            RuntimeException e = new RuntimeException("here");
+            e.fillInStackTrace();
+            Log.w(TAG, "Called doStart when already started: " + this, e);
+            return;
+        }
+        
+        // Call out to sub classes so they can start their loaders
+        // Let the existing loaders know that we want to be notified when a load is complete
+        for (int i = mLoaders.size()-1; i >= 0; i--) {
+            mLoaders.valueAt(i).start();
+        }
+        mStarted = true;
+    }
+    
+    void doStop() {
+        if (DEBUG) Log.v(TAG, "Stopping: " + this);
+        if (!mStarted) {
+            RuntimeException e = new RuntimeException("here");
+            e.fillInStackTrace();
+            Log.w(TAG, "Called doStop when not started: " + this, e);
+            return;
+        }
+
+        for (int i = mLoaders.size()-1; i >= 0; i--) {
+            mLoaders.valueAt(i).stop();
+        }
+        mStarted = false;
+    }
+    
+    void doRetain() {
+        if (DEBUG) Log.v(TAG, "Retaining: " + this);
+        if (!mStarted) {
+            RuntimeException e = new RuntimeException("here");
+            e.fillInStackTrace();
+            Log.w(TAG, "Called doRetain when not started: " + this, e);
+            return;
+        }
+
+        mRetaining = true;
+        mStarted = false;
+        for (int i = mLoaders.size()-1; i >= 0; i--) {
+            mLoaders.valueAt(i).retain();
+        }
+    }
+    
+    void finishRetain() {
+        if (DEBUG) Log.v(TAG, "Finished Retaining: " + this);
+
+        mRetaining = false;
+        for (int i = mLoaders.size()-1; i >= 0; i--) {
+            mLoaders.valueAt(i).finishRetain();
+        }
+    }
+    
+    void doDestroy() {
+        if (!mRetaining) {
+            if (DEBUG) Log.v(TAG, "Destroying Active: " + this);
+            for (int i = mLoaders.size()-1; i >= 0; i--) {
+                mLoaders.valueAt(i).destroy();
+            }
+        }
+        
+        if (DEBUG) Log.v(TAG, "Destroying Inactive: " + this);
+        for (int i = mInactiveLoaders.size()-1; i >= 0; i--) {
+            mInactiveLoaders.valueAt(i).destroy();
+        }
+        mInactiveLoaders.clear();
+    }
+}
diff --git a/core/java/android/app/LoaderManagingFragment.java b/core/java/android/app/LoaderManagingFragment.java
new file mode 100644
index 0000000..af71170
--- /dev/null
+++ b/core/java/android/app/LoaderManagingFragment.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.Loader;
+import android.os.Bundle;
+
+import java.util.HashMap;
+
+/**
+ * A Fragment that has utility methods for managing {@link Loader}s.
+ *
+ * @param <D> The type of data returned by the Loader. If you're using multiple Loaders with
+ * different return types use Object and case the results.
+ */
+public abstract class LoaderManagingFragment<D> extends Fragment
+        implements Loader.OnLoadCompleteListener<D> {
+    private boolean mStarted = false;
+
+    static final class LoaderInfo<D> {
+        public Bundle args;
+        public Loader<D> loader;
+    }
+    private HashMap<Integer, LoaderInfo<D>> mLoaders;
+    private HashMap<Integer, LoaderInfo<D>> mInactiveLoaders;
+
+    /**
+     * Registers a loader with this activity, registers the callbacks on it, and starts it loading.
+     * If a loader with the same id has previously been started it will automatically be destroyed
+     * when the new loader completes it's work. The callback will be delivered before the old loader
+     * is destroyed.
+     */
+    public Loader<D> startLoading(int id, Bundle args) {
+        LoaderInfo<D> info = mLoaders.get(id);
+        if (info != null) {
+            // Keep track of the previous instance of this loader so we can destroy
+            // it when the new one completes.
+            mInactiveLoaders.put(id, info);
+        }
+        info = new LoaderInfo<D>();
+        info.args = args;
+        mLoaders.put(id, info);
+        Loader<D> loader = onCreateLoader(id, args);
+        info.loader = loader;
+        if (mStarted) {
+            // The activity will start all existing loaders in it's onStart(), so only start them
+            // here if we're past that point of the activitiy's life cycle
+            loader.registerListener(id, this);
+            loader.startLoading();
+        }
+        return loader;
+    }
+
+    protected abstract Loader<D> onCreateLoader(int id, Bundle args);
+    protected abstract void onInitializeLoaders();
+    protected abstract void onLoadFinished(Loader<D> loader, D data);
+
+    public final void onLoadComplete(Loader<D> loader, D data) {
+        // Notify of the new data so the app can switch out the old data before
+        // we try to destroy it.
+        onLoadFinished(loader, data);
+
+        // Look for an inactive loader and destroy it if found
+        int id = loader.getId();
+        LoaderInfo<D> info = mInactiveLoaders.get(id);
+        if (info != null) {
+            Loader<D> oldLoader = info.loader;
+            if (oldLoader != null) {
+                oldLoader.destroy();
+            }
+            mInactiveLoaders.remove(id);
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedState) {
+        super.onCreate(savedState);
+
+        if (mLoaders == null) {
+            // Look for a passed along loader and create a new one if it's not there
+// TODO: uncomment once getLastNonConfigurationInstance method is available
+//            mLoaders = (HashMap<Integer, LoaderInfo>) getLastNonConfigurationInstance();
+            if (mLoaders == null) {
+                mLoaders = new HashMap<Integer, LoaderInfo<D>>();
+                onInitializeLoaders();
+            }
+        }
+        if (mInactiveLoaders == null) {
+            mInactiveLoaders = new HashMap<Integer, LoaderInfo<D>>();
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+
+        // Call out to sub classes so they can start their loaders
+        // Let the existing loaders know that we want to be notified when a load is complete
+        for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
+            LoaderInfo<D> info = entry.getValue();
+            Loader<D> loader = info.loader;
+            int id = entry.getKey();
+            if (loader == null) {
+               loader = onCreateLoader(id, info.args);
+               info.loader = loader;
+            }
+            loader.registerListener(id, this);
+            loader.startLoading();
+        }
+
+        mStarted = true;
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+
+        for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
+            LoaderInfo<D> info = entry.getValue();
+            Loader<D> loader = info.loader;
+            if (loader == null) {
+                continue;
+            }
+
+            // Let the loader know we're done with it
+            loader.unregisterListener(this);
+
+            // The loader isn't getting passed along to the next instance so ask it to stop loading
+            if (!getActivity().isChangingConfigurations()) {
+                loader.stopLoading();
+            }
+        }
+
+        mStarted = false;
+    }
+
+    /* TO DO: This needs to be turned into a retained fragment.
+    @Override
+    public Object onRetainNonConfigurationInstance() {
+        // Pass the loader along to the next guy
+        Object result = mLoaders;
+        mLoaders = null;
+        return result;
+    }
+    **/
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+
+        if (mLoaders != null) {
+            for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
+                LoaderInfo<D> info = entry.getValue();
+                Loader<D> loader = info.loader;
+                if (loader == null) {
+                    continue;
+                }
+                loader.destroy();
+            }
+        }
+    }
+
+    /**
+     * Stops and removes the loader with the given ID.
+     */
+    public void stopLoading(int id) {
+        if (mLoaders != null) {
+            LoaderInfo<D> info = mLoaders.remove(id);
+            if (info != null) {
+                Loader<D> loader = info.loader;
+                if (loader != null) {
+                    loader.unregisterListener(this);
+                    loader.destroy();
+                }
+            }
+        }
+    }
+
+    /**
+     * @return the Loader with the given id or null if no matching Loader
+     * is found.
+     */
+    public Loader<D> getLoader(int id) {
+        LoaderInfo<D> loaderInfo = mLoaders.get(id);
+        if (loaderInfo != null) {
+            return mLoaders.get(id).loader;
+        }
+        return null;
+    }
+}
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index a24fcae..524de6f 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -20,13 +20,11 @@
 import android.content.pm.ActivityInfo;
 import android.os.Binder;
 import android.os.Bundle;
-import android.util.Config;
 import android.util.Log;
 import android.view.Window;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.Map;
 
 /**
@@ -38,7 +36,7 @@
  */
 public class LocalActivityManager {
     private static final String TAG = "LocalActivityManager";
-    private static final boolean localLOGV = false || Config.LOGV;
+    private static final boolean localLOGV = false;
 
     // Internal token for an Activity being managed by LocalActivityManager.
     private static class LocalActivityRecord extends Binder {
@@ -112,11 +110,16 @@
         
         if (r.curState == INITIALIZING) {
             // Get the lastNonConfigurationInstance for the activity
-            HashMap<String,Object> lastNonConfigurationInstances =
-                mParent.getLastNonConfigurationChildInstances();
-            Object instance = null;
+            HashMap<String, Object> lastNonConfigurationInstances =
+                    mParent.getLastNonConfigurationChildInstances();
+            Object instanceObj = null;
             if (lastNonConfigurationInstances != null) {
-                instance = lastNonConfigurationInstances.get(r.id);
+                instanceObj = lastNonConfigurationInstances.get(r.id);
+            }
+            Activity.NonConfigurationInstances instance = null;
+            if (instanceObj != null) {
+                instance = new Activity.NonConfigurationInstances();
+                instance.activity = instanceObj;
             }
             
             // We need to have always created the activity.
@@ -346,7 +349,7 @@
     }
 
     private Window performDestroy(LocalActivityRecord r, boolean finish) {
-        Window win = null;
+        Window win;
         win = r.window;
         if (r.curState == RESUMED && !finish) {
             performPause(r, finish);
@@ -380,7 +383,8 @@
         if (r != null) {
             win = performDestroy(r, finish);
             if (finish) {
-                mActivities.remove(r);
+                mActivities.remove(id);
+                mActivityArray.remove(r);
             }
         }
         return win;
@@ -441,10 +445,8 @@
      */
     public void dispatchCreate(Bundle state) {
         if (state != null) {
-            final Iterator<String> i = state.keySet().iterator();
-            while (i.hasNext()) {
+            for (String id : state.keySet()) {
                 try {
-                    final String id = i.next();
                     final Bundle astate = state.getBundle(id);
                     LocalActivityRecord r = mActivities.get(id);
                     if (r != null) {
@@ -457,9 +459,7 @@
                     }
                 } catch (Exception e) {
                     // Recover from -all- app errors.
-                    Log.e(TAG,
-                          "Exception thrown when restoring LocalActivityManager state",
-                          e);
+                    Log.e(TAG, "Exception thrown when restoring LocalActivityManager state", e);
                 }
             }
         }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 856943d..e602518 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -21,6 +21,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Bitmap;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Parcel;
@@ -116,16 +117,55 @@
      * If this facility is used for something else, please give the user an option
      * to turn it off and use a normal notification, as this can be extremely
      * disruptive.
+     * 
+     * <p>Use with {@link #FLAG_HIGH_PRIORITY} to ensure that this notification
+     * will reach the user even when other notifications are suppressed.
      */
     public PendingIntent fullScreenIntent;
 
     /**
      * Text to scroll across the screen when this item is added to
-     * the status bar.
+     * the status bar on large and smaller devices.
+     *
+     * <p>This field is provided separately from the other ticker fields
+     * both for compatibility and to allow an application to choose different
+     * text for when the text scrolls in and when it is displayed all at once
+     * in conjunction with one or more icons.
+     *
+     * @see #tickerTitle
+     * @see #tickerSubtitle
+     * @see #tickerIcons
      */
     public CharSequence tickerText;
 
     /**
+     * The title line for the ticker over a the fat status bar on xlarge devices.
+     *
+     * @see #tickerText
+     * @see #tickerSubtitle
+     * @see #tickerIcons
+     */
+    public CharSequence tickerTitle;
+
+    /**
+     * The subtitle line for the ticker over a the fat status bar on xlarge devices.
+     *
+     * @see #tickerText
+     * @see #tickerTitle
+     * @see #tickerIcons
+     */
+    public CharSequence tickerSubtitle;
+
+    /**
+     * The icons to show to the left of the other ticker fields.
+     *
+     * @see #tickerText
+     * @see #tickerTitle
+     * @see #tickerSubtitle
+     */
+    public Bitmap[] tickerIcons;
+
+    /**
      * The view that will represent this notification in the expanded status bar.
      */
     public RemoteViews contentView;
@@ -275,6 +315,14 @@
      */
     public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
 
+    /**
+     * Bit to be bitwise-ored into the {@link #flags} field that should be set if this notification
+     * represents a high-priority event that may be shown to the user even if notifications are
+     * otherwise unavailable (that is, when the status bar is hidden). This flag is ideally used
+     * in conjunction with {@link #fullScreenIntent}.
+     */
+    public static final int FLAG_HIGH_PRIORITY = 0x00000080;
+
     public int flags;
 
     /**
@@ -336,6 +384,21 @@
             tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
         }
         if (parcel.readInt() != 0) {
+            tickerTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        }
+        if (parcel.readInt() != 0) {
+            tickerSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        }
+        final int tickerIconCount = parcel.readInt();
+        if (tickerIconCount >= 0) {
+            tickerIcons = new Bitmap[tickerIconCount];
+            for (int i=0; i<tickerIconCount; i++) {
+                if (parcel.readInt() != 0) {
+                    tickerIcons[i] = Bitmap.CREATOR.createFromParcel(parcel);
+                }
+            }
+        }
+        if (parcel.readInt() != 0) {
             contentView = RemoteViews.CREATOR.createFromParcel(parcel);
         }
         defaults = parcel.readInt();
@@ -371,6 +434,19 @@
         if (this.tickerText != null) {
             that.tickerText = this.tickerText.toString();
         }
+        if (this.tickerTitle != null) {
+            that.tickerTitle = this.tickerTitle.toString();
+        }
+        if (this.tickerSubtitle != null) {
+            that.tickerSubtitle = this.tickerSubtitle.toString();
+        }
+        if (this.tickerIcons != null) {
+            final int N = this.tickerIcons.length;
+            that.tickerIcons = new Bitmap[N];
+            for (int i=0; i<N; i++) {
+                that.tickerIcons[i] = Bitmap.createBitmap(this.tickerIcons[i]);
+            }
+        }
         if (this.contentView != null) {
             that.contentView = this.contentView.clone();
         }
@@ -427,6 +503,32 @@
         } else {
             parcel.writeInt(0);
         }
+        if (tickerTitle != null) {
+            parcel.writeInt(1);
+            TextUtils.writeToParcel(tickerTitle, parcel, flags);
+        } else {
+            parcel.writeInt(0);
+        }
+        if (tickerSubtitle != null) {
+            parcel.writeInt(1);
+            TextUtils.writeToParcel(tickerSubtitle, parcel, flags);
+        } else {
+            parcel.writeInt(0);
+        }
+        if (tickerIcons != null) {
+            final int N = tickerIcons.length;
+            parcel.writeInt(N);
+            for (int i=0; i<N; i++) {
+                if (tickerIcons[i] != null) {
+                    parcel.writeInt(1);
+                    tickerIcons[i].writeToParcel(parcel, flags);
+                } else {
+                    parcel.writeInt(0);
+                }
+            }
+        } else {
+            parcel.writeInt(-1);
+        }
         if (contentView != null) {
             parcel.writeInt(1);
             contentView.writeToParcel(parcel, 0);
@@ -543,6 +645,9 @@
         sb.append(Integer.toHexString(this.defaults));
         sb.append(",flags=0x");
         sb.append(Integer.toHexString(this.flags));
+        if ((this.flags & FLAG_HIGH_PRIORITY) != 0) {
+            sb.append("!!!1!one!");
+        }
         sb.append(")");
         return sb.toString();
     }
diff --git a/core/java/android/app/ProgressDialog.java b/core/java/android/app/ProgressDialog.java
index bdea069..4c5d28e 100644
--- a/core/java/android/app/ProgressDialog.java
+++ b/core/java/android/app/ProgressDialog.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -73,7 +74,7 @@
     private Handler mViewUpdateHandler;
     
     public ProgressDialog(Context context) {
-        this(context, com.android.internal.R.style.Theme_Dialog_Alert);
+        super(context);
     }
 
     public ProgressDialog(Context context, int theme) {
diff --git a/core/java/android/app/QueuedWork.java b/core/java/android/app/QueuedWork.java
index af6bb1b..6ee4780 100644
--- a/core/java/android/app/QueuedWork.java
+++ b/core/java/android/app/QueuedWork.java
@@ -88,4 +88,14 @@
             toFinish.run();
         }
     }
+    
+    /**
+     * Returns true if there is pending work to be done.  Note that the
+     * result is out of data as soon as you receive it, so be careful how you
+     * use it.
+     */
+    public static boolean hasPendingWork() {
+        return !sPendingWorkFinishers.isEmpty();
+    }
+    
 }
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 1f115e8..18602df 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -1083,7 +1083,7 @@
     protected void launchQuerySearch(int actionKey, String actionMsg)  {
         String query = mSearchAutoComplete.getText().toString();
         String action = Intent.ACTION_SEARCH;
-        Intent intent = createIntent(action, null, null, query, null,
+        Intent intent = createIntent(action, null, null, query,
                 actionKey, actionMsg);
         launchIntent(intent);
     }
@@ -1185,11 +1185,6 @@
             // use specific action if supplied, or default action if supplied, or fixed default
             String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
 
-            // some items are display only, or have effect via the cursor respond click reporting.
-            if (SearchManager.INTENT_ACTION_NONE.equals(action)) {
-                return null;
-            }
-
             if (action == null) {
                 action = mSearchable.getSuggestIntentAction();
             }
@@ -1211,14 +1206,10 @@
             }
             Uri dataUri = (data == null) ? null : Uri.parse(data);
 
-            String componentName = getColumnString(
-                    c, SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME);
-
             String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
             String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
 
-            return createIntent(action, dataUri, extraData, query, componentName, actionKey,
-                    actionMsg);
+            return createIntent(action, dataUri, extraData, query, actionKey, actionMsg);
         } catch (RuntimeException e ) {
             int rowNum;
             try {                       // be really paranoid now
@@ -1239,7 +1230,6 @@
      * @param data Intent data, or <code>null</code>.
      * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>.
      * @param query Intent query, or <code>null</code>.
-     * @param componentName Data for {@link SearchManager#COMPONENT_NAME_KEY} or <code>null</code>.
      * @param actionKey The key code of the action key that was pressed,
      *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
      * @param actionMsg The message for the action key that was pressed,
@@ -1249,7 +1239,7 @@
      * @return The intent.
      */
     private Intent createIntent(String action, Uri data, String extraData, String query,
-            String componentName, int actionKey, String actionMsg) {
+            int actionKey, String actionMsg) {
         // Now build the Intent
         Intent intent = new Intent(action);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 2e9cd96..6715012 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -16,16 +16,12 @@
 
 package android.app;
 
-import android.Manifest;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
@@ -40,7 +36,7 @@
 
 /**
  * This class provides access to the system search services.
- * 
+ *
  * <p>In practice, you won't interact with this class directly, as search
  * services are provided through methods in {@link android.app.Activity Activity}
  * and the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
@@ -56,7 +52,7 @@
  * href="{@docRoot}guide/topics/search/index.html">Search</a></strong>.</p>
  * </div>
  */
-public class SearchManager 
+public class SearchManager
         implements DialogInterface.OnDismissListener, DialogInterface.OnCancelListener
 {
 
@@ -65,20 +61,20 @@
 
     /**
      * This is a shortcut definition for the default menu key to use for invoking search.
-     * 
+     *
      * See Menu.Item.setAlphabeticShortcut() for more information.
      */
     public final static char MENU_KEY = 's';
 
     /**
      * This is a shortcut definition for the default menu key to use for invoking search.
-     * 
+     *
      * See Menu.Item.setAlphabeticShortcut() for more information.
      */
     public final static int MENU_KEYCODE = KeyEvent.KEYCODE_S;
 
     /**
-     * Intent extra data key: Use this key with 
+     * Intent extra data key: Use this key with
      * {@link android.content.Intent#getStringExtra
      *  content.Intent.getStringExtra()}
      * to obtain the query string from Intent.ACTION_SEARCH.
@@ -103,7 +99,7 @@
      * Intent extra data key: Use this key with Intent.ACTION_SEARCH and
      * {@link android.content.Intent#getBundleExtra
      *  content.Intent.getBundleExtra()}
-     * to obtain any additional app-specific data that was inserted by the 
+     * to obtain any additional app-specific data that was inserted by the
      * activity that launched the search.
      */
     public final static String APP_DATA = "app_data";
@@ -127,7 +123,7 @@
      * file.
      */
     public final static String ACTION_KEY = "action_key";
-    
+
     /**
      * Intent extra data key: This key will be used for the extra populated by the
      * {@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA} column.
@@ -153,11 +149,19 @@
      * Intent extra data key: Use this key with Intent.ACTION_SEARCH and
      * {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()}
      * to obtain the action message that was defined for a particular search action key and/or
-     * suggestion.  It will be null if the search was launched by typing "enter", touched the the 
-     * "GO" button, or other means not involving any action key. 
+     * suggestion.  It will be null if the search was launched by typing "enter", touched the the
+     * "GO" button, or other means not involving any action key.
      */
     public final static String ACTION_MSG = "action_msg";
-    
+
+    /**
+     * Flag to specify that the entry can be used for query refinement, i.e., the query text
+     * in the search field can be replaced with the text in this entry, when a query refinement
+     * icon is clicked. The suggestion list should show such a clickable icon beside the entry.
+     * <p>Use this flag as a bit-field for {@link #SUGGEST_COLUMN_FLAGS}.
+     */
+    public final static int FLAG_QUERY_REFINEMENT = 1 << 0;
+
     /**
      * Uri path for queried suggestions data.  This is the path that the search manager
      * will use when querying your content provider for suggestions data based on user input
@@ -182,12 +186,12 @@
      * @see #SUGGEST_COLUMN_SHORTCUT_ID
      */
     public final static String SUGGEST_URI_PATH_SHORTCUT = "search_suggest_shortcut";
-    
+
     /**
      * MIME type for shortcut validation.  You'll use this in your suggestions content provider
      * in the getType() function.
      */
-    public final static String SHORTCUT_MIME_TYPE = 
+    public final static String SHORTCUT_MIME_TYPE =
             "vnd.android.cursor.item/vnd.android.search.suggest";
 
     /**
@@ -195,7 +199,7 @@
      */
     public final static String SUGGEST_COLUMN_FORMAT = "suggest_format";
     /**
-     * Column name for suggestions cursor.  <i>Required.</i>  This is the primary line of text that 
+     * Column name for suggestions cursor.  <i>Required.</i>  This is the primary line of text that
      * will be presented to the user as the suggestion.
      */
     public final static String SUGGEST_COLUMN_TEXT_1 = "suggest_text_1";
@@ -227,8 +231,8 @@
      * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
      * </ul>
      *
-     * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)} 
-     * for more information on these schemes. 
+     * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)}
+     * for more information on these schemes.
      */
     public final static String SUGGEST_COLUMN_ICON_1 = "suggest_icon_1";
     /**
@@ -243,8 +247,8 @@
      * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
      * </ul>
      *
-     * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)} 
-     * for more information on these schemes. 
+     * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)}
+     * for more information on these schemes.
      */
     public final static String SUGGEST_COLUMN_ICON_2 = "suggest_icon_2";
     /**
@@ -276,12 +280,6 @@
      */
     public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data";
     /**
-     * TODO: Remove
-     *
-     * @hide
-     */
-    public final static String SUGGEST_COLUMN_INTENT_COMPONENT_NAME = "suggest_intent_component";
-    /**
      * Column name for suggestions cursor.  <i>Optional.</i>  If this column exists <i>and</i>
      * this element exists at the given row, then "/" and this value will be appended to the data
      * field in the Intent.  This should only be used if the data field has already been set to an
@@ -289,8 +287,8 @@
      */
     public final static String SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id";
     /**
-     * Column name for suggestions cursor.  <i>Required if action is 
-     * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</i>  If this 
+     * Column name for suggestions cursor.  <i>Required if action is
+     * {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</i>  If this
      * column exists <i>and</i> this element exists at the given row, this is the data that will be
      * used when forming the suggestion's query.
      */
@@ -307,15 +305,6 @@
     public final static String SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id";
 
     /**
-     * Column name for suggestions cursor. <i>Optional.</i>  This column is used to specify the
-     * cursor item's background color if it needs a non-default background color. A non-zero value
-     * indicates a valid background color to override the default.
-     *
-     * @hide For internal use, not part of the public API.
-     */
-    public final static String SUGGEST_COLUMN_BACKGROUND_COLOR = "suggest_background_color";
-    
-    /**
      * Column name for suggestions cursor. <i>Optional.</i> This column is used to specify
      * that a spinner should be shown in lieu of an icon2 while the shortcut of this suggestion
      * is being refreshed.
@@ -324,6 +313,15 @@
             "suggest_spinner_while_refreshing";
 
     /**
+     * Column name for suggestions cursor. <i>Optional.</i> This column is used to specify
+     * additional flags per item. Multiple flags can be specified.
+     * <p>
+     * Must be one of {@link #FLAG_QUERY_REFINEMENT} or 0 to indicate no flags.
+     * </p>
+     */
+    public final static String SUGGEST_COLUMN_FLAGS = "suggest_flags";
+
+    /**
      * Column value for suggestion column {@link #SUGGEST_COLUMN_SHORTCUT_ID} when a suggestion
      * should not be stored as a shortcut in global search.
      */
@@ -343,16 +341,16 @@
      * {@link #EXTRA_SELECT_QUERY},
      * {@link #APP_DATA}.
      */
-    public final static String INTENT_ACTION_GLOBAL_SEARCH 
+    public final static String INTENT_ACTION_GLOBAL_SEARCH
             = "android.search.action.GLOBAL_SEARCH";
-    
+
     /**
      * Intent action for starting the global search settings activity.
      * The global search provider should handle this intent.
      */
-    public final static String INTENT_ACTION_SEARCH_SETTINGS 
+    public final static String INTENT_ACTION_SEARCH_SETTINGS
             = "android.search.action.SEARCH_SETTINGS";
-    
+
     /**
      * Intent action for starting a web search provider's settings activity.
      * Web search providers should handle this intent if they have provider-specific
@@ -368,7 +366,7 @@
      */
     public final static String INTENT_ACTION_SEARCHABLES_CHANGED
             = "android.search.action.SEARCHABLES_CHANGED";
-    
+
     /**
      * Intent action broadcasted to inform that the search settings have changed in some way.
      * Either searchables have been enabled or disabled, or a different web search provider
@@ -378,14 +376,6 @@
             = "android.search.action.SETTINGS_CHANGED";
 
     /**
-     * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
-     * the search dialog will take no action.
-     *
-     * @hide
-     */
-    public final static String INTENT_ACTION_NONE = "android.search.action.ZILCH";
-
-    /**
      * This means that context is voice, and therefore the SearchDialog should
      * continue showing the microphone until the user indicates that he/she does
      * not want to re-speak (e.g. by typing).
@@ -413,7 +403,7 @@
      * The package associated with this seach manager.
      */
     private String mAssociatedPackage;
-    
+
     // package private since they are used by the inner class SearchManagerCallback
     /* package */ final Handler mHandler;
     /* package */ OnDismissListener mDismissListener = null;
@@ -427,15 +417,15 @@
         mService = ISearchManager.Stub.asInterface(
                 ServiceManager.getService(Context.SEARCH_SERVICE));
     }
-    
+
     /**
      * Launch search UI.
      *
      * <p>The search manager will open a search widget in an overlapping
-     * window, and the underlying activity may be obscured.  The search 
+     * window, and the underlying activity may be obscured.  The search
      * entry state will remain in effect until one of the following events:
      * <ul>
-     * <li>The user completes the search.  In most cases this will launch 
+     * <li>The user completes the search.  In most cases this will launch
      * a search intent.</li>
      * <li>The user uses the back, home, or other keys to exit the search.</li>
      * <li>The application calls the {@link #stopSearch}
@@ -443,8 +433,8 @@
      * activity from which it was launched.</li>
      *
      * <p>Most applications will <i>not</i> use this interface to invoke search.
-     * The primary method for invoking search is to call 
-     * {@link android.app.Activity#onSearchRequested Activity.onSearchRequested()} or 
+     * The primary method for invoking search is to call
+     * {@link android.app.Activity#onSearchRequested Activity.onSearchRequested()} or
      * {@link android.app.Activity#startSearch Activity.startSearch()}.
      *
      * @param initialQuery A search string can be pre-entered here, but this
@@ -456,19 +446,19 @@
      * and the user would expect to be able to keep typing.  <i>This parameter is only meaningful
      * if initialQuery is a non-empty string.</i>
      * @param launchActivity The ComponentName of the activity that has launched this search.
-     * @param appSearchData An application can insert application-specific 
-     * context here, in order to improve quality or specificity of its own 
+     * @param appSearchData An application can insert application-specific
+     * context here, in order to improve quality or specificity of its own
      * searches.  This data will be returned with SEARCH intent(s).  Null if
      * no extra data is required.
      * @param globalSearch If false, this will only launch the search that has been specifically
-     * defined by the application (which is usually defined as a local search).  If no default 
+     * defined by the application (which is usually defined as a local search).  If no default
      * search is defined in the current application or activity, global search will be launched.
      * If true, this will always launch a platform-global (e.g. web-based) search instead.
-     * 
+     *
      * @see android.app.Activity#onSearchRequested
      * @see #stopSearch
      */
-    public void startSearch(String initialQuery, 
+    public void startSearch(String initialQuery,
                             boolean selectInitialQuery,
                             ComponentName launchActivity,
                             Bundle appSearchData,
@@ -595,7 +585,7 @@
      * <p>Typically the user will terminate the search UI by launching a
      * search or by canceling.  This function allows the underlying application
      * or activity to cancel the search prematurely (for any reason).
-     * 
+     *
      * <p>This function can be safely called at any time (even if no search is active.)
      *
      * @see #startSearch
@@ -607,12 +597,12 @@
     }
 
     /**
-     * Determine if the Search UI is currently displayed.  
-     * 
+     * Determine if the Search UI is currently displayed.
+     *
      * This is provided primarily for application test purposes.
      *
      * @return Returns true if the search UI is currently displayed.
-     * 
+     *
      * @hide
      */
     public boolean isVisible() {
@@ -631,7 +621,7 @@
          */
         public void onDismiss();
     }
-    
+
     /**
      * See {@link SearchManager#setOnCancelListener} for configuring your activity to monitor
      * search UI state.
@@ -647,7 +637,7 @@
 
     /**
      * Set or clear the callback that will be invoked whenever the search UI is dismissed.
-     * 
+     *
      * @param listener The {@link OnDismissListener} to use, or null.
      */
     public void setOnDismissListener(final OnDismissListener listener) {
@@ -656,7 +646,7 @@
 
     /**
      * Set or clear the callback that will be invoked whenever the search UI is canceled.
-     * 
+     *
      * @param listener The {@link OnCancelListener} to use, or null.
      */
     public void setOnCancelListener(OnCancelListener listener) {
@@ -767,10 +757,10 @@
         // finally, make the query
         return mContext.getContentResolver().query(uri, null, selection, selArgs, null);
     }
-     
+
     /**
      * Returns a list of the searchable activities that can be included in global search.
-     * 
+     *
      * @return a list containing searchable information for all searchable activities
      *         that have the <code>android:includeInGlobalSearch</code> attribute set
      *         in their searchable meta-data.
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index de544fb..fa7f794 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -55,6 +55,18 @@
     public static final int DISABLE_NOTIFICATION_TICKER = 0x00000008;
 
     /**
+     * Flag for {@link #disable} to hide the center system info area.
+     */
+    public static final int DISABLE_SYSTEM_INFO = 0x00000010;
+
+    /**
+     * Flag for {@link #disable} to hide only the navigation buttons.  Don't use this
+     * unless you're the setup wizard.
+     */
+    public static final int DISABLE_NAVIGATION = 0x00000020;
+
+
+    /**
      * Re-enable all of the status bar features that you've disabled.
      */
     public static final int DISABLE_NONE = 0x00000000;
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 8d8864f..5705bff 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -68,7 +68,6 @@
     private SearchableInfo mSearchable;
     private Context mProviderContext;
     private WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache;
-    private SparseArray<Drawable.ConstantState> mBackgroundsCache;
     private boolean mClosed = false;
 
     // URL color
@@ -80,7 +79,6 @@
     private int mText2UrlCol;
     private int mIconName1Col;
     private int mIconName2Col;
-    private int mBackgroundColorCol;
 
     static final int NONE = -1;
 
@@ -109,7 +107,6 @@
         mProviderContext = mSearchable.getProviderContext(mContext, activityContext);
 
         mOutsideDrawablesCache = outsideDrawablesCache;
-        mBackgroundsCache = new SparseArray<Drawable.ConstantState>();
 
         mStartSpinnerRunnable = new Runnable() {
                 public void run() {
@@ -243,8 +240,6 @@
                 mText2UrlCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
                 mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
                 mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
-                mBackgroundColorCol =
-                        c.getColumnIndex(SearchManager.SUGGEST_COLUMN_BACKGROUND_COLOR);
             }
         } catch (Exception e) {
             Log.e(LOG_TAG, "error changing cursor and caching columns", e);
@@ -283,13 +278,6 @@
     public void bindView(View view, Context context, Cursor cursor) {
         ChildViewCache views = (ChildViewCache) view.getTag();
 
-        int backgroundColor = 0;
-        if (mBackgroundColorCol != -1) {
-            backgroundColor = cursor.getInt(mBackgroundColorCol);
-        }
-        Drawable background = getItemBackground(backgroundColor);
-        view.setBackgroundDrawable(background);
-
         if (views.mText1 != null) {
             String text1 = getStringOrNull(cursor, mText1Col);
             setViewText(views.mText1, text1);
@@ -342,33 +330,6 @@
         return text;
     }
 
-    /**
-     * Gets a drawable with no color when selected or pressed, and the given color when
-     * neither selected nor pressed.
-     *
-     * @return A drawable, or {@code null} if the given color is transparent.
-     */
-    private Drawable getItemBackground(int backgroundColor) {
-        if (backgroundColor == 0) {
-            return null;
-        } else {
-            Drawable.ConstantState cachedBg = mBackgroundsCache.get(backgroundColor);
-            if (cachedBg != null) {
-                if (DBG) Log.d(LOG_TAG, "Background cache hit for color " + backgroundColor);
-                return cachedBg.newDrawable(mProviderContext.getResources());
-            }
-            if (DBG) Log.d(LOG_TAG, "Creating new background for color " + backgroundColor);
-            ColorDrawable transparent = new ColorDrawable(0);
-            ColorDrawable background = new ColorDrawable(backgroundColor);
-            StateListDrawable newBg = new StateListDrawable();
-            newBg.addState(new int[]{android.R.attr.state_selected}, transparent);
-            newBg.addState(new int[]{android.R.attr.state_pressed}, transparent);
-            newBg.addState(new int[]{}, background);
-            mBackgroundsCache.put(backgroundColor, newBg.getConstantState());
-            return newBg;
-        }
-    }
-
     private void setViewText(TextView v, CharSequence text) {
         // Set the text even if it's null, since we need to clear any previous text.
         v.setText(text);
@@ -601,21 +562,7 @@
      * @return A non-null drawable.
      */
     private Drawable getDefaultIcon1(Cursor cursor) {
-        // First check the component that the suggestion is originally from
-        String c = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME);
-        if (c != null) {
-            ComponentName component = ComponentName.unflattenFromString(c);
-            if (component != null) {
-                Drawable drawable = getActivityIconWithCache(component);
-                if (drawable != null) {
-                    return drawable;
-                }
-            } else {
-                Log.w(LOG_TAG, "Bad component name: " + c);
-            }
-        }
-
-        // Then check the component that gave us the suggestion
+        // Check the component that gave us the suggestion
         Drawable drawable = getActivityIconWithCache(mSearchable.getSearchActivity());
         if (drawable != null) {
             return drawable;
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 521d41c..381143c 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
+import android.os.Build;
 import android.os.Bundle;
 import android.text.format.DateFormat;
 import android.view.LayoutInflater;
@@ -76,7 +77,10 @@
     public TimePickerDialog(Context context,
             OnTimeSetListener callBack,
             int hourOfDay, int minute, boolean is24HourView) {
-        this(context, com.android.internal.R.style.Theme_Dialog_Alert,
+        this(context,
+                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB
+                        ? com.android.internal.R.style.Theme_Holo_Dialog_Alert
+                        : com.android.internal.R.style.Theme_Dialog_Alert,
                 callBack, hourOfDay, minute, is24HourView);
     }
 
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 0bcd65c..2237c82 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -103,6 +103,15 @@
      */
     public static final int USES_POLICY_WIPE_DATA = 4;
 
+    /**
+     * A type of policy that this device admin can use: able to specify the
+     * device Global Proxy, via {@link DevicePolicyManager#setGlobalProxy}.
+     *
+     * <p>To control this policy, the device admin must have a "set-global-proxy"
+     * tag in the "uses-policies" section of its meta-data.
+     */
+    public static final int USES_POLICY_SETS_GLOBAL_PROXY = 5;
+
     /** @hide */
     public static class PolicyInfo {
         public final int ident;
@@ -138,6 +147,9 @@
         sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_FORCE_LOCK, "force-lock",
                 com.android.internal.R.string.policylab_forceLock,
                 com.android.internal.R.string.policydesc_forceLock));
+        sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_SETS_GLOBAL_PROXY, "set-global-proxy",
+                com.android.internal.R.string.policylab_setGlobalProxy,
+                com.android.internal.R.string.policydesc_setGlobalProxy));
         
         for (int i=0; i<sPoliciesDisplayOrder.size(); i++) {
             PolicyInfo pi = sPoliciesDisplayOrder.get(i);
@@ -328,7 +340,7 @@
      * the given policy control.  The possible policy identifier inputs are:
      * {@link #USES_POLICY_LIMIT_PASSWORD}, {@link #USES_POLICY_WATCH_LOGIN},
      * {@link #USES_POLICY_RESET_PASSWORD}, {@link #USES_POLICY_FORCE_LOCK},
-     * {@link #USES_POLICY_WIPE_DATA}.
+     * {@link #USES_POLICY_WIPE_DATA}, {@link #USES_POLICY_SETS_GLOBAL_PROXY}.
      */
     public boolean usesPolicy(int policyIdent) {
         return (mUsesPolicies & (1<<policyIdent)) != 0;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 570351d..ca27010 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -32,6 +32,8 @@
 import android.util.Log;
 
 import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
 import java.util.List;
 
 /**
@@ -46,7 +48,7 @@
 
     private final Context mContext;
     private final IDevicePolicyManager mService;
-    
+
     private final Handler mHandler;
 
     private DevicePolicyManager(Context context, Handler handler) {
@@ -61,14 +63,14 @@
         DevicePolicyManager me = new DevicePolicyManager(context, handler);
         return me.mService != null ? me : null;
     }
-    
+
     /**
      * Activity action: ask the user to add a new device administrator to the system.
      * The desired policy is the ComponentName of the policy in the
      * {@link #EXTRA_DEVICE_ADMIN} extra field.  This will invoke a UI to
      * bring the user through adding the device administrator to the system (or
      * allowing them to reject it).
-     * 
+     *
      * <p>You can optionally include the {@link #EXTRA_ADD_EXPLANATION}
      * field to provide the user with additional explanation (in addition
      * to your component's description) about what is being added.
@@ -76,7 +78,7 @@
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_ADD_DEVICE_ADMIN
             = "android.app.action.ADD_DEVICE_ADMIN";
-    
+
     /**
      * Activity action: send when any policy admin changes a policy.
      * This is generally used to find out when a new policy is in effect.
@@ -92,7 +94,7 @@
      * @see #ACTION_ADD_DEVICE_ADMIN
      */
     public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
-    
+
     /**
      * An optional CharSequence providing additional explanation for why the
      * admin is being added.
@@ -100,22 +102,21 @@
      * @see #ACTION_ADD_DEVICE_ADMIN
      */
     public static final String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
-    
+
     /**
-     * Activity action: have the user enter a new password.  This activity
-     * should be launched after using {@link #setPasswordQuality(ComponentName, int)}
-     * or {@link #setPasswordMinimumLength(ComponentName, int)} to have the
-     * user enter a new password that meets the current requirements.  You can
-     * use {@link #isActivePasswordSufficient()} to determine whether you need
-     * to have the user select a new password in order to meet the current
-     * constraints.  Upon being resumed from this activity,
-     * you can check the new password characteristics to see if they are
-     * sufficient.
+     * Activity action: have the user enter a new password. This activity should
+     * be launched after using {@link #setPasswordQuality(ComponentName, int)},
+     * or {@link #setPasswordMinimumLength(ComponentName, int)} to have the user
+     * enter a new password that meets the current requirements. You can use
+     * {@link #isActivePasswordSufficient()} to determine whether you need to
+     * have the user select a new password in order to meet the current
+     * constraints. Upon being resumed from this activity, you can check the new
+     * password characteristics to see if they are sufficient.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_SET_NEW_PASSWORD
             = "android.app.action.SET_NEW_PASSWORD";
-    
+
     /**
      * Return true if the given administrator component is currently
      * active (enabled) in the system.
@@ -130,7 +131,7 @@
         }
         return false;
     }
-    
+
     /**
      * Return a list of all currently active device administrator's component
      * names.  Note that if there are no administrators than null may be
@@ -146,7 +147,7 @@
         }
         return null;
     }
-    
+
     /**
      * @hide
      */
@@ -160,7 +161,7 @@
         }
         return false;
     }
-    
+
     /**
      * Remove a current administration component.  This can only be called
      * by the application that owns the administration component; if you
@@ -176,28 +177,28 @@
             }
         }
     }
-    
+
     /**
      * Constant for {@link #setPasswordQuality}: the policy has no requirements
      * for the password.  Note that quality constants are ordered so that higher
      * values are more restrictive.
      */
     public static final int PASSWORD_QUALITY_UNSPECIFIED = 0;
-    
+
     /**
      * Constant for {@link #setPasswordQuality}: the policy requires some kind
      * of password, but doesn't care what it is.  Note that quality constants
      * are ordered so that higher values are more restrictive.
      */
     public static final int PASSWORD_QUALITY_SOMETHING = 0x10000;
-    
+
     /**
      * Constant for {@link #setPasswordQuality}: the user must have entered a
      * password containing at least numeric characters.  Note that quality
      * constants are ordered so that higher values are more restrictive.
      */
     public static final int PASSWORD_QUALITY_NUMERIC = 0x20000;
-    
+
     /**
      * Constant for {@link #setPasswordQuality}: the user must have entered a
      * password containing at least alphabetic (or other symbol) characters.
@@ -205,7 +206,7 @@
      * restrictive.
      */
     public static final int PASSWORD_QUALITY_ALPHABETIC = 0x40000;
-    
+
     /**
      * Constant for {@link #setPasswordQuality}: the user must have entered a
      * password containing at least <em>both></em> numeric <em>and</em>
@@ -213,7 +214,19 @@
      * ordered so that higher values are more restrictive.
      */
     public static final int PASSWORD_QUALITY_ALPHANUMERIC = 0x50000;
-    
+
+    /**
+     * Constant for {@link #setPasswordQuality}: the user must have entered a
+     * password containing at least a letter, a numerical digit and a special
+     * symbol, by default. With this password quality, passwords can be
+     * restricted to contain various sets of characters, like at least an
+     * uppercase letter, etc. These are specified using various methods,
+     * like {@link #setPasswordMinimumLowerCase(ComponentName, int)}. Note
+     * that quality constants are ordered so that higher values are more
+     * restrictive.
+     */
+    public static final int PASSWORD_QUALITY_COMPLEX = 0x60000;
+
     /**
      * Called by an application that is administering the device to set the
      * password restrictions it is imposing.  After setting this, the user
@@ -222,21 +235,21 @@
      * will remain until the user has set a new one, so the change does not
      * take place immediately.  To prompt the user for a new password, use
      * {@link #ACTION_SET_NEW_PASSWORD} after setting this value.
-     * 
+     *
      * <p>Quality constants are ordered so that higher values are more restrictive;
      * thus the highest requested quality constant (between the policy set here,
      * the user's preference, and any other considerations) is the one that
      * is in effect.
-     * 
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
      * this method; if it has not, a security exception will be thrown.
-     * 
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param quality The new desired quality.  One of
      * {@link #PASSWORD_QUALITY_UNSPECIFIED}, {@link #PASSWORD_QUALITY_SOMETHING},
      * {@link #PASSWORD_QUALITY_NUMERIC}, {@link #PASSWORD_QUALITY_ALPHABETIC},
-     * or {@link #PASSWORD_QUALITY_ALPHANUMERIC}.
+     * {@link #PASSWORD_QUALITY_ALPHANUMERIC} or {@link #PASSWORD_QUALITY_COMPLEX}.
      */
     public void setPasswordQuality(ComponentName admin, int quality) {
         if (mService != null) {
@@ -247,7 +260,7 @@
             }
         }
     }
-    
+
     /**
      * Retrieve the current minimum password quality for all admins
      * or a particular one.
@@ -264,7 +277,7 @@
         }
         return PASSWORD_QUALITY_UNSPECIFIED;
     }
-    
+
     /**
      * Called by an application that is administering the device to set the
      * minimum allowed password length.  After setting this, the user
@@ -274,14 +287,14 @@
      * take place immediately.  To prompt the user for a new password, use
      * {@link #ACTION_SET_NEW_PASSWORD} after setting this value.  This
      * constraint is only imposed if the administrator has also requested either
-     * {@link #PASSWORD_QUALITY_NUMERIC}, {@link #PASSWORD_QUALITY_ALPHABETIC},
-     * or {@link #PASSWORD_QUALITY_ALPHANUMERIC}
+     * {@link #PASSWORD_QUALITY_NUMERIC}, {@link #PASSWORD_QUALITY_ALPHABETIC}
+     * {@link #PASSWORD_QUALITY_ALPHANUMERIC}, or {@link #PASSWORD_QUALITY_COMPLEX}
      * with {@link #setPasswordQuality}.
-     * 
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
      * this method; if it has not, a security exception will be thrown.
-     * 
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param length The new desired minimum password length.  A value of 0
      * means there is no restriction.
@@ -295,7 +308,7 @@
             }
         }
     }
-    
+
     /**
      * Retrieve the current minimum password length for all admins
      * or a particular one.
@@ -312,7 +325,379 @@
         }
         return 0;
     }
-    
+
+    /**
+     * Called by an application that is administering the device to set the
+     * minimum number of upper case letters required in the password. After
+     * setting this, the user will not be able to enter a new password that is
+     * not at least as restrictive as what has been set. Note that the current
+     * password will remain until the user has set a new one, so the change does
+     * not take place immediately. To prompt the user for a new password, use
+     * {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
+     * constraint is only imposed if the administrator has also requested
+     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The
+     * default value is 0.
+     * <p>
+     * The calling device admin must have requested
+     * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
+     * this method; if it has not, a security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated
+     *            with.
+     * @param length The new desired minimum number of upper case letters
+     *            required in the password. A value of 0 means there is no
+     *            restriction.
+     */
+    public void setPasswordMinimumUpperCase(ComponentName admin, int length) {
+        if (mService != null) {
+            try {
+                mService.setPasswordMinimumUpperCase(admin, length);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Retrieve the current number of upper case letters required in the
+     * password for all admins or a particular one. This is the same value as
+     * set by {#link {@link #setPasswordMinimumUpperCase(ComponentName, int)}
+     * and only applies when the password quality is
+     * {@link #PASSWORD_QUALITY_COMPLEX}.
+     *
+     * @param admin The name of the admin component to check, or null to
+     *            aggregate all admins.
+     * @return The minimum number of upper case letters required in the
+     *         password.
+     */
+    public int getPasswordMinimumUpperCase(ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getPasswordMinimumUpperCase(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Called by an application that is administering the device to set the
+     * minimum number of lower case letters required in the password. After
+     * setting this, the user will not be able to enter a new password that is
+     * not at least as restrictive as what has been set. Note that the current
+     * password will remain until the user has set a new one, so the change does
+     * not take place immediately. To prompt the user for a new password, use
+     * {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
+     * constraint is only imposed if the administrator has also requested
+     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The
+     * default value is 0.
+     * <p>
+     * The calling device admin must have requested
+     * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
+     * this method; if it has not, a security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated
+     *            with.
+     * @param length The new desired minimum number of lower case letters
+     *            required in the password. A value of 0 means there is no
+     *            restriction.
+     */
+    public void setPasswordMinimumLowerCase(ComponentName admin, int length) {
+        if (mService != null) {
+            try {
+                mService.setPasswordMinimumLowerCase(admin, length);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Retrieve the current number of lower case letters required in the
+     * password for all admins or a particular one. This is the same value as
+     * set by {#link {@link #setPasswordMinimumLowerCase(ComponentName, int)}
+     * and only applies when the password quality is
+     * {@link #PASSWORD_QUALITY_COMPLEX}.
+     *
+     * @param admin The name of the admin component to check, or null to
+     *            aggregate all admins.
+     * @return The minimum number of lower case letters required in the
+     *         password.
+     */
+    public int getPasswordMinimumLowerCase(ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getPasswordMinimumLowerCase(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Called by an application that is administering the device to set the
+     * minimum number of letters required in the password. After setting this,
+     * the user will not be able to enter a new password that is not at least as
+     * restrictive as what has been set. Note that the current password will
+     * remain until the user has set a new one, so the change does not take
+     * place immediately. To prompt the user for a new password, use
+     * {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
+     * constraint is only imposed if the administrator has also requested
+     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The
+     * default value is 1.
+     * <p>
+     * The calling device admin must have requested
+     * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
+     * this method; if it has not, a security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated
+     *            with.
+     * @param length The new desired minimum number of letters required in the
+     *            password. A value of 0 means there is no restriction.
+     */
+    public void setPasswordMinimumLetters(ComponentName admin, int length) {
+        if (mService != null) {
+            try {
+                mService.setPasswordMinimumLetters(admin, length);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Retrieve the current number of letters required in the password for all
+     * admins or a particular one. This is the same value as
+     * set by {#link {@link #setPasswordMinimumLetters(ComponentName, int)}
+     * and only applies when the password quality is
+     * {@link #PASSWORD_QUALITY_COMPLEX}.
+     *
+     * @param admin The name of the admin component to check, or null to
+     *            aggregate all admins.
+     * @return The minimum number of letters required in the password.
+     */
+    public int getPasswordMinimumLetters(ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getPasswordMinimumLetters(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Called by an application that is administering the device to set the
+     * minimum number of numerical digits required in the password. After
+     * setting this, the user will not be able to enter a new password that is
+     * not at least as restrictive as what has been set. Note that the current
+     * password will remain until the user has set a new one, so the change does
+     * not take place immediately. To prompt the user for a new password, use
+     * {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
+     * constraint is only imposed if the administrator has also requested
+     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The
+     * default value is 1.
+     * <p>
+     * The calling device admin must have requested
+     * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
+     * this method; if it has not, a security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated
+     *            with.
+     * @param length The new desired minimum number of numerical digits required
+     *            in the password. A value of 0 means there is no restriction.
+     */
+    public void setPasswordMinimumNumeric(ComponentName admin, int length) {
+        if (mService != null) {
+            try {
+                mService.setPasswordMinimumNumeric(admin, length);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Retrieve the current number of numerical digits required in the password
+     * for all admins or a particular one. This is the same value as
+     * set by {#link {@link #setPasswordMinimumNumeric(ComponentName, int)}
+     * and only applies when the password quality is
+     * {@link #PASSWORD_QUALITY_COMPLEX}.
+     *
+     * @param admin The name of the admin component to check, or null to
+     *            aggregate all admins.
+     * @return The minimum number of numerical digits required in the password.
+     */
+    public int getPasswordMinimumNumeric(ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getPasswordMinimumNumeric(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Called by an application that is administering the device to set the
+     * minimum number of symbols required in the password. After setting this,
+     * the user will not be able to enter a new password that is not at least as
+     * restrictive as what has been set. Note that the current password will
+     * remain until the user has set a new one, so the change does not take
+     * place immediately. To prompt the user for a new password, use
+     * {@link #ACTION_SET_NEW_PASSWORD} after setting this value. This
+     * constraint is only imposed if the administrator has also requested
+     * {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The
+     * default value is 1.
+     * <p>
+     * The calling device admin must have requested
+     * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
+     * this method; if it has not, a security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated
+     *            with.
+     * @param length The new desired minimum number of symbols required in the
+     *            password. A value of 0 means there is no restriction.
+     */
+    public void setPasswordMinimumSymbols(ComponentName admin, int length) {
+        if (mService != null) {
+            try {
+                mService.setPasswordMinimumSymbols(admin, length);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Retrieve the current number of symbols required in the password for all
+     * admins or a particular one. This is the same value as
+     * set by {#link {@link #setPasswordMinimumSymbols(ComponentName, int)}
+     * and only applies when the password quality is
+     * {@link #PASSWORD_QUALITY_COMPLEX}.
+     *
+     * @param admin The name of the admin component to check, or null to
+     *            aggregate all admins.
+     * @return The minimum number of symbols required in the password.
+     */
+    public int getPasswordMinimumSymbols(ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getPasswordMinimumSymbols(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Called by an application that is administering the device to set the
+     * minimum number of non-letter characters (numerical digits or symbols)
+     * required in the password. After setting this, the user will not be able
+     * to enter a new password that is not at least as restrictive as what has
+     * been set. Note that the current password will remain until the user has
+     * set a new one, so the change does not take place immediately. To prompt
+     * the user for a new password, use {@link #ACTION_SET_NEW_PASSWORD} after
+     * setting this value. This constraint is only imposed if the administrator
+     * has also requested {@link #PASSWORD_QUALITY_COMPLEX} with
+     * {@link #setPasswordQuality}. The default value is 0.
+     * <p>
+     * The calling device admin must have requested
+     * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
+     * this method; if it has not, a security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated
+     *            with.
+     * @param length The new desired minimum number of letters required in the
+     *            password. A value of 0 means there is no restriction.
+     */
+    public void setPasswordMinimumNonLetter(ComponentName admin, int length) {
+        if (mService != null) {
+            try {
+                mService.setPasswordMinimumNonLetter(admin, length);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Retrieve the current number of non-letter characters required in the
+     * password for all admins or a particular one. This is the same value as
+     * set by {#link {@link #setPasswordMinimumNonLetter(ComponentName, int)}
+     * and only applies when the password quality is
+     * {@link #PASSWORD_QUALITY_COMPLEX}.
+     *
+     * @param admin The name of the admin component to check, or null to
+     *            aggregate all admins.
+     * @return The minimum number of letters required in the password.
+     */
+    public int getPasswordMinimumNonLetter(ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getPasswordMinimumNonLetter(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return 0;
+    }
+
+  /**
+   * Called by an application that is administering the device to set the length
+   * of the password history. After setting this, the user will not be able to
+   * enter a new password that is the same as any password in the history. Note
+   * that the current password will remain until the user has set a new one, so
+   * the change does not take place immediately. To prompt the user for a new
+   * password, use {@link #ACTION_SET_NEW_PASSWORD} after setting this value.
+   * This constraint is only imposed if the administrator has also requested
+   * either {@link #PASSWORD_QUALITY_NUMERIC},
+   * {@link #PASSWORD_QUALITY_ALPHABETIC}, or
+   * {@link #PASSWORD_QUALITY_ALPHANUMERIC} with {@link #setPasswordQuality}.
+   *
+   * <p>
+   * The calling device admin must have requested
+   * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this
+   * method; if it has not, a security exception will be thrown.
+   *
+   * @param admin Which {@link DeviceAdminReceiver} this request is associated
+   *        with.
+   * @param length The new desired length of password history. A value of 0
+   *        means there is no restriction.
+   */
+    public void setPasswordHistoryLength(ComponentName admin, int length) {
+        if (mService != null) {
+            try {
+                mService.setPasswordHistoryLength(admin, length);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Retrieve the current password history length for all admins
+     * or a particular one.
+     * @param admin The name of the admin component to check, or null to aggregate
+     * all admins.
+     * @return The length of the password history
+     */
+    public int getPasswordHistoryLength(ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getPasswordHistoryLength(admin);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return 0;
+    }
+
     /**
      * Return the maximum password length that the device supports for a
      * particular password quality.
@@ -323,16 +708,16 @@
         // Kind-of arbitrary.
         return 16;
     }
-    
+
     /**
      * Determine whether the current password the user has set is sufficient
      * to meet the policy requirements (quality, minimum length) that have been
      * requested.
-     * 
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
      * this method; if it has not, a security exception will be thrown.
-     * 
+     *
      * @return Returns true if the password meets the current requirements,
      * else false.
      */
@@ -346,11 +731,11 @@
         }
         return false;
     }
-    
+
     /**
      * Retrieve the number of times the user has failed at entering a
      * password since that last successful password entry.
-     * 
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to be able to call
      * this method; if it has not, a security exception will be thrown.
@@ -373,14 +758,14 @@
      * watching for failed passwords and wiping the device, and requires
      * that you request both {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} and
      * {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}}.
-     * 
+     *
      * <p>To implement any other policy (e.g. wiping data for a particular
      * application only, erasing or revoking credentials, or reporting the
      * failure to a server), you should implement
      * {@link DeviceAdminReceiver#onPasswordFailed(Context, android.content.Intent)}
      * instead.  Do not use this API, because if the maximum count is reached,
      * the device will be wiped immediately, and your callback will not be invoked.
-     * 
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param num The number of failed password attempts at which point the
      * device will wipe its data.
@@ -394,7 +779,7 @@
             }
         }
     }
-    
+
     /**
      * Retrieve the current maximum number of login attempts that are allowed
      * before the device wipes itself, for all admins
@@ -412,13 +797,13 @@
         }
         return 0;
     }
-    
+
     /**
      * Flag for {@link #resetPassword}: don't allow other admins to change
      * the password again until the user has entered it.
      */
     public static final int RESET_PASSWORD_REQUIRE_ENTRY = 0x0001;
-    
+
     /**
      * Force a new device unlock password (the password needed to access the
      * entire device, not for individual accounts) on the user.  This takes
@@ -431,11 +816,11 @@
      * that the password may be a stronger quality (containing alphanumeric
      * characters when the requested quality is only numeric), in which case
      * the currently active quality will be increased to match.
-     * 
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD} to be able to call
      * this method; if it has not, a security exception will be thrown.
-     * 
+     *
      * @param password The new password for the user.
      * @param flags May be 0 or {@link #RESET_PASSWORD_REQUIRE_ENTRY}.
      * @return Returns true if the password was applied, or false if it is
@@ -451,16 +836,16 @@
         }
         return false;
     }
-    
+
     /**
      * Called by an application that is administering the device to set the
      * maximum time for user activity until the device will lock.  This limits
      * the length that the user can set.  It takes effect immediately.
-     * 
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} to be able to call
      * this method; if it has not, a security exception will be thrown.
-     * 
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param timeMs The new desired maximum time to lock in milliseconds.
      * A value of 0 means there is no restriction.
@@ -474,7 +859,7 @@
             }
         }
     }
-    
+
     /**
      * Retrieve the current maximum time to unlock for all admins
      * or a particular one.
@@ -491,11 +876,11 @@
         }
         return 0;
     }
-    
+
     /**
      * Make the device lock immediately, as if the lock screen timeout has
      * expired at the point of this call.
-     * 
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} to be able to call
      * this method; if it has not, a security exception will be thrown.
@@ -509,7 +894,7 @@
             }
         }
     }
-    
+
     /**
      * Flag for {@link #wipeData(int)}: also erase the device's external
      * storage.
@@ -520,11 +905,11 @@
      * Ask the user date be wiped.  This will cause the device to reboot,
      * erasing all user data while next booting up.  External storage such
      * as SD cards will not be erased.
-     * 
+     *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to be able to call
      * this method; if it has not, a security exception will be thrown.
-     * 
+     *
      * @param flags Bit mask of additional options: currently must be 0.
      */
     public void wipeData(int flags) {
@@ -536,7 +921,92 @@
             }
         }
     }
-    
+
+    /**
+     * Called by an application that is administering the device to set the
+     * global proxy and exclusion list.
+     * <p>
+     * The calling device admin must have requested
+     * {@link DeviceAdminInfo#USES_POLICY_SETS_GLOBAL_PROXY} to be able to call
+     * this method; if it has not, a security exception will be thrown.
+     * Only the first device admin can set the proxy. If a second admin attempts
+     * to set the proxy, the {@link ComponentName} of the admin originally setting the
+     * proxy will be returned. If successful in setting the proxy, null will
+     * be returned.
+     * The method can be called repeatedly by the device admin alrady setting the
+     * proxy to update the proxy and exclusion list.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated
+     *            with.
+     * @param proxySpec the global proxy desired. Must be an HTTP Proxy.
+     *            Pass Proxy.NO_PROXY to reset the proxy.
+     * @param exclusionList a list of domains to be excluded from the global proxy.
+     * @return returns null if the proxy was successfully set, or a {@link ComponentName}
+     *            of the device admin that sets thew proxy otherwise.
+     */
+    public ComponentName setGlobalProxy(ComponentName admin, Proxy proxySpec,
+            List<String> exclusionList ) {
+        if (proxySpec == null) {
+            throw new NullPointerException();
+        }
+        if (mService != null) {
+            try {
+                String hostSpec;
+                String exclSpec;
+                if (proxySpec.equals(Proxy.NO_PROXY)) {
+                    hostSpec = null;
+                    exclSpec = null;
+                } else {
+                    if (!proxySpec.type().equals(Proxy.Type.HTTP)) {
+                        throw new IllegalArgumentException();
+                    }
+                    InetSocketAddress sa = (InetSocketAddress)proxySpec.address();
+                    String hostName = sa.getHostName();
+                    int port = sa.getPort();
+                    StringBuilder hostBuilder = new StringBuilder();
+                    hostSpec = hostBuilder.append(hostName)
+                        .append(":").append(Integer.toString(port)).toString();
+                    if (exclusionList == null) {
+                        exclSpec = "";
+                    } else {
+                        StringBuilder listBuilder = new StringBuilder();
+                        boolean firstDomain = true;
+                        for (String exclDomain : exclusionList) {
+                            if (!firstDomain) {
+                                listBuilder = listBuilder.append(",");
+                            } else {
+                                firstDomain = false;
+                            }
+                            listBuilder = listBuilder.append(exclDomain.trim());
+                        }
+                        exclSpec = listBuilder.toString();
+                    }
+                    android.net.Proxy.validate(hostName, Integer.toString(port), exclSpec);
+                }
+                return mService.setGlobalProxy(admin, hostSpec, exclSpec);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the component name setting the global proxy.
+     * @return ComponentName object of the device admin that set the global proxy, or
+     *            null if no admin has set the proxy.
+     */
+    public ComponentName getGlobalProxyAdmin() {
+        if (mService != null) {
+            try {
+                return mService.getGlobalProxyAdmin();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return null;
+    }
+
     /**
      * @hide
      */
@@ -549,7 +1019,7 @@
             }
         }
     }
-    
+
     /**
      * @hide
      */
@@ -562,10 +1032,10 @@
             Log.w(TAG, "Unable to retrieve device policy " + cn, e);
             return null;
         }
-        
+
         ResolveInfo ri = new ResolveInfo();
         ri.activityInfo = ai;
-        
+
         try {
             return new DeviceAdminInfo(mContext, ri);
         } catch (XmlPullParserException e) {
@@ -576,7 +1046,7 @@
             return null;
         }
     }
-    
+
     /**
      * @hide
      */
@@ -593,16 +1063,18 @@
     /**
      * @hide
      */
-    public void setActivePasswordState(int quality, int length) {
+    public void setActivePasswordState(int quality, int length, int letters, int uppercase,
+            int lowercase, int numbers, int symbols, int nonletter) {
         if (mService != null) {
             try {
-                mService.setActivePasswordState(quality, length);
+                mService.setActivePasswordState(quality, length, letters, uppercase, lowercase,
+                        numbers, symbols, nonletter);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
         }
     }
-    
+
     /**
      * @hide
      */
@@ -615,7 +1087,7 @@
             }
         }
     }
-    
+
     /**
      * @hide
      */
@@ -628,4 +1100,5 @@
             }
         }
     }
+
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6fc4dc5..3fcd6fc 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -27,10 +27,31 @@
 interface IDevicePolicyManager {
     void setPasswordQuality(in ComponentName who, int quality);
     int getPasswordQuality(in ComponentName who);
-    
+
     void setPasswordMinimumLength(in ComponentName who, int length);
     int getPasswordMinimumLength(in ComponentName who);
+
+    void setPasswordMinimumUpperCase(in ComponentName who, int length);
+    int getPasswordMinimumUpperCase(in ComponentName who);
+
+    void setPasswordMinimumLowerCase(in ComponentName who, int length);
+    int getPasswordMinimumLowerCase(in ComponentName who);
+
+    void setPasswordMinimumLetters(in ComponentName who, int length);
+    int getPasswordMinimumLetters(in ComponentName who);
+
+    void setPasswordMinimumNumeric(in ComponentName who, int length);
+    int getPasswordMinimumNumeric(in ComponentName who);
+
+    void setPasswordMinimumSymbols(in ComponentName who, int length);
+    int getPasswordMinimumSymbols(in ComponentName who);
+
+    void setPasswordMinimumNonLetter(in ComponentName who, int length);
+    int getPasswordMinimumNonLetter(in ComponentName who);
     
+    void setPasswordHistoryLength(in ComponentName who, int length);
+    int getPasswordHistoryLength(in ComponentName who);
+
     boolean isActivePasswordSufficient();
     int getCurrentFailedPasswordAttempts();
     
@@ -45,6 +66,9 @@
     void lockNow();
     
     void wipeData(int flags);
+
+    ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList);
+    ComponentName getGlobalProxyAdmin();
     
     void setActiveAdmin(in ComponentName policyReceiver);
     boolean isAdminActive(in ComponentName policyReceiver);
@@ -53,7 +77,8 @@
     void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result);
     void removeActiveAdmin(in ComponentName policyReceiver);
     
-    void setActivePasswordState(int quality, int length);
+    void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase,
+        int numbers, int symbols, int nonletter);
     void reportFailedPasswordAttempt();
     void reportSuccessfulPasswordAttempt();
 }
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index b2fc13f..7730942 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -39,6 +39,7 @@
 
     static final int HANDLE_UPDATE = 1;
     static final int HANDLE_PROVIDER_CHANGED = 2;
+    static final int HANDLE_VIEW_DATA_CHANGED = 3;
 
     final static Object sServiceLock = new Object();
     static IAppWidgetService sService;
@@ -60,6 +61,13 @@
             msg.obj = info;
             msg.sendToTarget();
         }
+
+        public void viewDataChanged(int appWidgetId, int viewId) {
+            Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED);
+            msg.arg1 = appWidgetId;
+            msg.arg2 = viewId;
+            msg.sendToTarget();
+        }
     }
 
     class UpdateHandler extends Handler {
@@ -77,6 +85,10 @@
                     onProviderChanged(msg.arg1, (AppWidgetProviderInfo)msg.obj);
                     break;
                 }
+                case HANDLE_VIEW_DATA_CHANGED: {
+                    viewDataChanged(msg.arg1, msg.arg2);
+                    break;
+                }
             }
         }
     }
@@ -250,6 +262,16 @@
             v.updateAppWidget(views);
         }
     }
+
+    void viewDataChanged(int appWidgetId, int viewId) {
+        AppWidgetHostView v;
+        synchronized (mViews) {
+            v = mViews.get(appWidgetId);
+        }
+        if (v != null) {
+            v.viewDataChanged(viewId);
+        }
+    }
 }
 
 
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index b33b097..4f8ee93 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -23,15 +23,18 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.os.SystemClock;
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.Adapter;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
 import android.widget.FrameLayout;
 import android.widget.RemoteViews;
 import android.widget.TextView;
@@ -258,6 +261,22 @@
     }
 
     /**
+     * Process data-changed notifications for the specified view in the specified
+     * set of {@link RemoteViews} views.
+     */
+    void viewDataChanged(int viewId) {
+        View v = findViewById(viewId);
+        if ((v != null) && (v instanceof AdapterView<?>)) {
+            AdapterView<?> adapterView = (AdapterView<?>) v;
+            Adapter adapter = adapterView.getAdapter();
+            if (adapter instanceof BaseAdapter) {
+                BaseAdapter baseAdapter = (BaseAdapter) adapter;
+                baseAdapter.notifyDataSetChanged();
+            }
+        }
+    }
+
+    /**
      * Build a {@link Context} cloned into another package name, usually for the
      * purposes of reading remote resources.
      */
@@ -275,6 +294,7 @@
         }
     }
 
+    @Override
     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
         if (CROSSFADE) {
             int alpha;
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index d2ab85e..2a583c1 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -233,6 +233,10 @@
     /**
      * Set the RemoteViews to use for the specified appWidgetIds.
      *
+     * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
+     * contain a complete representation of the widget. For performing partial widget updates, see
+     * {@link #partiallyUpdateAppWidget(int[], RemoteViews)}.
+     *
      * <p>
      * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
      * and outside of the handler.
@@ -253,6 +257,10 @@
     /**
      * Set the RemoteViews to use for the specified appWidgetId.
      *
+     * Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
+     * contain a complete representation of the widget. For performing partial widget updates, see
+     * {@link #partiallyUpdateAppWidget(int, RemoteViews)}.
+     *
      * <p>
      * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
      * and outside of the handler.
@@ -266,6 +274,59 @@
     }
 
     /**
+     * Perform an incremental update or command on the widget(s) specified by appWidgetIds.
+     *
+     * This update  differs from {@link #updateAppWidget(int[], RemoteViews)} in that the
+     * RemoteViews object which is passed is understood to be an incomplete representation of the 
+     * widget, and hence is not cached by the AppWidgetService. Note that because these updates are 
+     * not cached, any state that they modify that is not restored by restoreInstanceState will not
+     * persist in the case that the widgets are restored using the cached version in
+     * AppWidgetService.
+     *
+     * Use with {@link RemoteViews#showNext(int)}, {@link RemoteViews#showPrevious(int)},
+     * {@link RemoteViews#setScrollPosition(int, int)} and similar commands.
+     *
+     * <p>
+     * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
+     * and outside of the handler.
+     * This method will only work when called from the uid that owns the AppWidget provider.
+     *
+     * @param appWidgetIds     The AppWidget instances for which to set the RemoteViews.
+     * @param views            The RemoteViews object containing the incremental update / command.
+     */
+    public void partiallyUpdateAppWidget(int[] appWidgetIds, RemoteViews views) {
+        try {
+            sService.partiallyUpdateAppWidgetIds(appWidgetIds, views);
+        } catch (RemoteException e) {
+            throw new RuntimeException("system server dead?", e);
+        }
+    }
+
+    /**
+     * Perform an incremental update or command on the widget specified by appWidgetId.
+     *
+     * This update  differs from {@link #updateAppWidget(int, RemoteViews)} in that the RemoteViews
+     * object which is passed is understood to be an incomplete representation of the widget, and
+     * hence is not cached by the AppWidgetService. Note that because these updates are not cached,
+     * any state that they modify that is not restored by restoreInstanceState will not persist in
+     * the case that the widgets are restored using the cached version in AppWidgetService.
+     *
+     * Use with {@link RemoteViews#showNext(int)}, {@link RemoteViews#showPrevious(int)},
+     * {@link RemoteViews#setScrollPosition(int, int)} and similar commands.
+     *
+     * <p>
+     * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
+     * and outside of the handler.
+     * This method will only work when called from the uid that owns the AppWidget provider.
+     *
+     * @param appWidgetId      The AppWidget instance for which to set the RemoteViews.
+     * @param views            The RemoteViews object containing the incremental update / command.
+     */
+    public void partiallyUpdateAppWidget(int appWidgetId, RemoteViews views) {
+        partiallyUpdateAppWidget(new int[] { appWidgetId }, views);
+    }
+
+    /**
      * Set the RemoteViews to use for all AppWidget instances for the supplied AppWidget provider.
      *
      * <p>
@@ -288,11 +349,46 @@
     }
 
     /**
+     * Notifies the specified collection view in all the specified AppWidget instances
+     * to invalidate their currently data.
+     *
+     * @param appWidgetIds  The AppWidget instances for which to notify of view data changes.
+     * @param viewId        The collection view id.
+     */
+    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+        try {
+            sService.notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
+        }
+        catch (RemoteException e) {
+            throw new RuntimeException("system server dead?", e);
+        }
+    }
+
+    /**
+     * Notifies the specified collection view in all the specified AppWidget instance
+     * to invalidate it's currently data.
+     *
+     * @param appWidgetId  The AppWidget instance for which to notify of view data changes.
+     * @param viewId        The collection view id.
+     */
+    public void notifyAppWidgetViewDataChanged(int appWidgetId, int viewId) {
+        notifyAppWidgetViewDataChanged(new int[] { appWidgetId }, viewId);
+    }
+
+    /**
      * Return a list of the AppWidget providers that are currently installed.
      */
     public List<AppWidgetProviderInfo> getInstalledProviders() {
         try {
-            return sService.getInstalledProviders();
+            List<AppWidgetProviderInfo> providers = sService.getInstalledProviders();
+            for (AppWidgetProviderInfo info : providers) {
+                // Converting complex to dp.
+                info.minWidth =
+                        TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
+                info.minHeight =
+                        TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
+            }
+            return providers;
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index cee2865..396e92d 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -110,6 +110,17 @@
      * @hide Pending API approval
      */
     public String oldName;
+    
+    /**
+     * A preview of what the AppWidget will look like after it's configured.
+     * If not supplied, the AppWidget's icon will be used.
+     *
+     * <p>This field corresponds to the <code>android:previewImage</code> attribute in
+     * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+     * 
+     * @hide Pending API approval
+     */
+	public int previewImage;
 
     public AppWidgetProviderInfo() {
     }
@@ -130,6 +141,7 @@
         }
         this.label = in.readString();
         this.icon = in.readInt();
+        this.previewImage = in.readInt();
     }
 
 
@@ -152,6 +164,7 @@
         }
         out.writeString(this.label);
         out.writeInt(this.icon);
+        out.writeInt(this.previewImage);
     }
 
     public int describeContents() {
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 7e5f858..61b4303 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -18,88 +18,102 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.server.BluetoothA2dpService;
 import android.content.Context;
-import android.os.ServiceManager;
-import android.os.RemoteException;
 import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.server.BluetoothA2dpService;
 import android.util.Log;
 
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Set;
-import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.List;
+
 
 /**
- * Public API for controlling the Bluetooth A2DP Profile Service.
+ * This class provides the public APIs to control the Bluetooth A2DP
+ * profile.
  *
- * BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
- * Service via IPC.
+ *<p>BluetoothA2dp is a proxy object for controlling the Bluetooth A2DP
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothA2dp proxy object.
  *
- * Creating a BluetoothA2dp object will initiate a binding with the
- * BluetoothHeadset service. Users of this object should call close() when they
- * are finished, so that this proxy object can unbind from the service.
- *
- * Currently the BluetoothA2dp service runs in the system server and this
- * proxy object will be immediately bound to the service on construction.
- *
- * Currently this class provides methods to connect to A2DP audio sinks.
- *
- * @hide
+ * <p> Android only supports one connected Bluetooth A2dp device at a time.
+ * Each method is protected with its appropriate permission.
  */
-public final class BluetoothA2dp {
+public final class BluetoothA2dp implements BluetoothProfile {
     private static final String TAG = "BluetoothA2dp";
     private static final boolean DBG = false;
 
-    /** int extra for ACTION_SINK_STATE_CHANGED */
-    public static final String EXTRA_SINK_STATE =
-        "android.bluetooth.a2dp.extra.SINK_STATE";
-    /** int extra for ACTION_SINK_STATE_CHANGED */
-    public static final String EXTRA_PREVIOUS_SINK_STATE =
-        "android.bluetooth.a2dp.extra.PREVIOUS_SINK_STATE";
-
-    /** Indicates the state of an A2DP audio sink has changed.
-     * This intent will always contain EXTRA_SINK_STATE,
-     * EXTRA_PREVIOUS_SINK_STATE and BluetoothDevice.EXTRA_DEVICE
-     * extras.
+    /**
+     * Intent used to broadcast the change in connection state of the A2DP
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * {@link #EXTRA_STATE} - The current state of the profile.
+     * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
+     *
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_SINK_STATE_CHANGED =
-        "android.bluetooth.a2dp.action.SINK_STATE_CHANGED";
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED";
 
-    public static final int STATE_DISCONNECTED = 0;
-    public static final int STATE_CONNECTING   = 1;
-    public static final int STATE_CONNECTED    = 2;
-    public static final int STATE_DISCONNECTING = 3;
-    /** Playing implies connected */
-    public static final int STATE_PLAYING    = 4;
+    /**
+     * Intent used to broadcast the change in the Playing state of the A2DP
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * {@link #EXTRA_STATE} - The current state of the profile.
+     * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
+     *
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PLAYING_STATE_CHANGED =
+        "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED";
 
-    /** Default priority for a2dp devices that we try to auto-connect
-     * and allow incoming connections */
-    public static final int PRIORITY_AUTO_CONNECT = 1000;
-    /** Default priority for a2dp devices that should allow incoming
-     * connections */
-    public static final int PRIORITY_ON = 100;
-    /** Default priority for a2dp devices that should not allow incoming
-     * connections */
-    public static final int PRIORITY_OFF = 0;
-    /** Default priority when not set or when the device is unpaired */
-    public static final int PRIORITY_UNDEFINED = -1;
+    /**
+     * A2DP sink device is streaming music. This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+     */
+    public static final int STATE_PLAYING   =  10;
 
-    private final IBluetoothA2dp mService;
-    private final Context mContext;
+    /**
+     * A2DP sink device is NOT streaming music. This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+     */
+    public static final int STATE_NOT_PLAYING   =  11;
+
+    private ServiceListener mServiceListener;
+    private IBluetoothA2dp mService;
+    private BluetoothAdapter mAdapter;
 
     /**
      * Create a BluetoothA2dp proxy object for interacting with the local
      * Bluetooth A2DP service.
-     * @param c Context
+     *
      */
-    public BluetoothA2dp(Context c) {
-        mContext = c;
-
+    /*package*/ BluetoothA2dp(Context mContext, ServiceListener l) {
         IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE);
+        mServiceListener = l;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
         if (b != null) {
             mService = IBluetoothA2dp.Stub.asInterface(b);
+            if (mServiceListener != null) {
+                mServiceListener.onServiceConnected(BluetoothProfile.A2DP, this);
+            }
         } else {
             Log.w(TAG, "Bluetooth A2DP service not available!");
 
@@ -109,167 +123,242 @@
         }
     }
 
-    /** Initiate a connection to an A2DP sink.
-     *  Listen for SINK_STATE_CHANGED_ACTION to find out when the
-     *  connection is completed.
-     *  @param device Remote BT device.
-     *  @return false on immediate error, true otherwise
-     *  @hide
+    /**
+     * {@inheritDoc}
+     * @hide
      */
-    public boolean connectSink(BluetoothDevice device) {
-        if (DBG) log("connectSink(" + device + ")");
-        try {
-            return mService.connectSink(device);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) log("connect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.connect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
     }
 
-    /** Initiate disconnect from an A2DP sink.
-     *  Listen for SINK_STATE_CHANGED_ACTION to find out when
-     *  disconnect is completed.
-     *  @param device Remote BT device.
-     *  @return false on immediate error, true otherwise
-     *  @hide
+    /**
+     * {@inheritDoc}
+     * @hide
      */
-    public boolean disconnectSink(BluetoothDevice device) {
-        if (DBG) log("disconnectSink(" + device + ")");
-        try {
-            return mService.disconnectSink(device);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.disconnect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
     }
 
-    /** Initiate suspend from an A2DP sink.
-     *  Listen for SINK_STATE_CHANGED_ACTION to find out when
-     *  suspend is completed.
-     *  @param device Remote BT device.
-     *  @return false on immediate error, true otherwise
-     *  @hide
+    /**
+     * {@inheritDoc}
+     */
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (DBG) log("getConnectedDevices()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getConnectedDevices();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (DBG) log("getDevicesMatchingStates()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getDevicesMatchingConnectionStates(states);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getConnectionState(BluetoothDevice device) {
+        if (DBG) log("getState(" + device + ")");
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    public boolean setPriority(BluetoothDevice device, int priority) {
+        if (DBG) log("setPriority(" + device + ", " + priority + ")");
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            if (priority != BluetoothProfile.PRIORITY_OFF &&
+                priority != BluetoothProfile.PRIORITY_ON) {
+              return false;
+            }
+            try {
+                return mService.setPriority(device, priority);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @hide
+     */
+    public int getPriority(BluetoothDevice device) {
+        if (DBG) log("getPriority(" + device + ")");
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.getPriority(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.PRIORITY_OFF;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.PRIORITY_OFF;
+    }
+
+    /**
+     * Check if A2DP profile is streaming music.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device BluetoothDevice device
+     */
+    public boolean isA2dpPlaying(BluetoothDevice device) {
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.isA2dpPlaying(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Initiate suspend from an A2DP sink.
+     *
+     * <p> This API will return false in scenarios like the A2DP
+     * device is not in connected state etc. When this API returns,
+     * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED}
+     * intent will be broadcasted with the state. Users can get the
+     * state of the A2DP device from this intent.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     *
+     * @param device Remote A2DP sink
+     * @return false on immediate error,
+     *               true otherwise
+     * @hide
      */
     public boolean suspendSink(BluetoothDevice device) {
-        try {
-            return mService.suspendSink(device);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.suspendSink(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
     }
 
-    /** Initiate resume from an suspended A2DP sink.
-     *  Listen for SINK_STATE_CHANGED_ACTION to find out when
-     *  resume is completed.
-     *  @param device Remote BT device.
-     *  @return false on immediate error, true otherwise
-     *  @hide
+    /**
+     * Initiate resume from a suspended A2DP sink.
+     *
+     * <p> This API will return false in scenarios like the A2DP
+     * device is not in suspended state etc. When this API returns,
+     * true, it is guaranteed that {@link #ACTION_SINK_STATE_CHANGED}
+     * intent will be broadcasted with the state. Users can get the
+     * state of the A2DP device from this intent.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     *
+     * @param device Remote A2DP sink
+     * @return false on immediate error,
+     *               true otherwise
+     * @hide
      */
     public boolean resumeSink(BluetoothDevice device) {
-        try {
-            return mService.resumeSink(device);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
+        if (mService != null && isEnabled()
+            && isValidDevice(device)) {
+            try {
+                return mService.resumeSink(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
-    }
-
-    /** Check if a specified A2DP sink is connected.
-     *  @param device Remote BT device.
-     *  @return True if connected (or playing), false otherwise and on error.
-     *  @hide
-     */
-    public boolean isSinkConnected(BluetoothDevice device) {
-        if (DBG) log("isSinkConnected(" + device + ")");
-        int state = getSinkState(device);
-        return state == STATE_CONNECTED || state == STATE_PLAYING;
-    }
-
-    /** Check if any A2DP sink is connected.
-     * @return a unmodifiable set of connected A2DP sinks, or null on error.
-     * @hide
-     */
-    public Set<BluetoothDevice> getConnectedSinks() {
-        if (DBG) log("getConnectedSinks()");
-        try {
-            return Collections.unmodifiableSet(
-                    new HashSet<BluetoothDevice>(Arrays.asList(mService.getConnectedSinks())));
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return null;
-        }
-    }
-
-    /** Check if any A2DP sink is in Non Disconnected state
-     * i.e playing, connected, connecting, disconnecting.
-     * @return a unmodifiable set of connected A2DP sinks, or null on error.
-     * @hide
-     */
-    public Set<BluetoothDevice> getNonDisconnectedSinks() {
-        if (DBG) log("getNonDisconnectedSinks()");
-        try {
-            return Collections.unmodifiableSet(
-                    new HashSet<BluetoothDevice>(Arrays.asList(mService.getNonDisconnectedSinks())));
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return null;
-        }
-    }
-
-    /** Get the state of an A2DP sink
-     *  @param device Remote BT device.
-     *  @return State code, one of STATE_
-     *  @hide
-     */
-    public int getSinkState(BluetoothDevice device) {
-        if (DBG) log("getSinkState(" + device + ")");
-        try {
-            return mService.getSinkState(device);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return BluetoothA2dp.STATE_DISCONNECTED;
-        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
     }
 
     /**
-     * Set priority of a2dp sink.
-     * Priority is a non-negative integer. By default paired sinks will have
-     * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0).
-     * Sinks with priority greater than zero will accept incoming connections
-     * (if no sink is currently connected).
-     * Priority for unpaired sink must be PRIORITY_NONE.
-     * @param device Paired sink
-     * @param priority Integer priority, for example PRIORITY_AUTO or
-     *                 PRIORITY_NONE
-     * @return true if priority is set, false on error
+     * This function checks if the remote device is an AVCRP
+     * target and thus whether we should send volume keys
+     * changes or not.
+     * @hide
      */
-    public boolean setSinkPriority(BluetoothDevice device, int priority) {
-        if (DBG) log("setSinkPriority(" + device + ", " + priority + ")");
-        try {
-            return mService.setSinkPriority(device, priority);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return false;
+    public boolean shouldSendVolumeKeys(BluetoothDevice device) {
+        if (isEnabled() && isValidDevice(device)) {
+            ParcelUuid[] uuids = device.getUuids();
+            if (uuids == null) return false;
+
+            for (ParcelUuid uuid: uuids) {
+                if (BluetoothUuid.isAvrcpTarget(uuid)) {
+                    return true;
+                }
+            }
         }
+        return false;
     }
 
-    /**
-     * Get priority of a2dp sink.
-     * @param device Sink
-     * @return non-negative priority, or negative error code on error.
-     */
-    public int getSinkPriority(BluetoothDevice device) {
-        if (DBG) log("getSinkPriority(" + device + ")");
-        try {
-            return mService.getSinkPriority(device);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-            return PRIORITY_OFF;
-        }
-    }
-
-    /** Helper for converting a state to a string.
+     /**
+     * Helper for converting a state to a string.
+     *
      * For debug use only - strings are not internationalized.
      * @hide
      */
@@ -285,12 +374,26 @@
             return "disconnecting";
         case STATE_PLAYING:
             return "playing";
+        case STATE_NOT_PLAYING:
+          return "not playing";
         default:
             return "<unknown state " + state + ">";
         }
     }
 
+    private boolean isEnabled() {
+       if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+       return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+       if (device == null) return false;
+
+       if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+       return false;
+    }
+
     private static void log(String msg) {
-        Log.d(TAG, msg);
+      Log.d(TAG, msg);
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 3040319..556fb10 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -279,6 +280,61 @@
      */
     public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME";
 
+    /**
+     * Intent used to broadcast the change in connection state of the local
+     * Bluetooth adapter to a profile of the remote device. When the adapter is
+     * not connected to any profiles of any remote devices and it attempts a
+     * connection to a profile this intent will sent. Once connected, this intent
+     * will not be sent for any more connection attempts to any profiles of any
+     * remote device. When the adapter disconnects from the last profile its
+     * connected to of any remote device, this intent will be sent.
+     *
+     * <p> This intent is useful for applications that are only concerned about
+     * whether the local adapter is connected to any profile of any device and
+     * are not really concerned about which profile. For example, an application
+     * which displays an icon to display whether Bluetooth is connected or not
+     * can use this intent.
+     *
+     * <p>This intent will have 3 extras:
+     * {@link #EXTRA_STATE} - The current state.
+     * {@link #EXTRA_PREVIOUS_STATE}- The previous.
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
+     *
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
+
+    /**
+     * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
+     *
+     * This extra represents the current connection state.
+     */
+    public static final String EXTRA_CONNECTION_STATE =
+        "android.bluetooth.adapter.extra.CONNECTION_STATE";
+
+    /**
+     * Extra used by {@link #ACTION_CONNECTION_STATE_CHANGED}
+     *
+     * This extra represents the previous connection state.
+     */
+    public static final String EXTRA_PREVIOUS_CONNECTION_STATE =
+          "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE";
+
+    /** The profile is in disconnected state */
+    public static final int STATE_DISCONNECTED  = 0;
+    /** The profile is in connecting state */
+    public static final int STATE_CONNECTING    = 1;
+    /** The profile is in connected state */
+    public static final int STATE_CONNECTED     = 2;
+    /** The profile is in disconnecting state */
+    public static final int STATE_DISCONNECTING = 3;
+
     /** @hide */
     public static final String BLUETOOTH_SERVICE = "bluetooth";
 
@@ -682,6 +738,27 @@
     }
 
     /**
+     * Get the current connection state of the local Bluetooth adapter.
+     * This can be used to check whether the local Bluetooth adapter is connected
+     * to any profile of any other remote Bluetooth Device.
+     *
+     * <p> Use this function along with {@link #ACTION_CONNECTION_STATE_CHANGED}
+     * intent to get the connection state of the adapter.
+     *
+     * @return One of {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTED},
+     * {@link #STATE_CONNECTING} or {@link #STATE_DISCONNECTED}
+     *
+     * @hide
+     */
+    public int getConnectionState() {
+        if (getState() != STATE_ON) return BluetoothAdapter.STATE_DISCONNECTED;
+        try {
+            return mService.getAdapterConnectionState();
+        } catch (RemoteException e) {Log.e(TAG, "getConnectionState:", e);}
+        return BluetoothAdapter.STATE_DISCONNECTED;
+    }
+
+    /**
      * Picks RFCOMM channels until none are left.
      * Avoids reserved channels.
      */
@@ -823,6 +900,7 @@
         return socket;
     }
 
+
     /**
      * Construct an unencrypted, unauthenticated, RFCOMM server socket.
      * Call #accept to retrieve connections to this socket.
@@ -896,6 +974,54 @@
         return null;
     }
 
+    /*
+     * Get the profile proxy object associated with the profile.
+     *
+     * <p>Profile can be one of {@link BluetoothProfile.HEADSET} or
+     * {@link BluetoothProfile.A2DP}. Clients must implements
+     * {@link BluetoothProfile.ServiceListener} to get notified of
+     * the connection status and to get the proxy object.
+     *
+     * @param context Context of the application
+     * @param listener The service Listener for connection callbacks.
+     * @param profile
+     * @return true on success, false on error
+     */
+    public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
+                                   int profile) {
+        if (context == null || listener == null) return false;
+
+        if (profile == BluetoothProfile.HEADSET) {
+            BluetoothHeadset headset = new BluetoothHeadset(context, listener);
+            return true;
+        } else if (profile == BluetoothProfile.A2DP) {
+            BluetoothA2dp a2dp = new BluetoothA2dp(context, listener);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Close the connection of the profile proxy to the Service.
+     *
+     * <p> Clients should call this when they are no longer using
+     * the proxy obtained from {@link #getProfileProxy}.
+     * Profile can be one of {@link BluetoothProfile#HEADSET} or
+     * {@link BluetoothProfile#A2DP}
+     *
+     * @param profile
+     * @param proxy Profile proxy object
+     */
+    public void closeProfileProxy(int profile, BluetoothProfile proxy) {
+        if (profile == BluetoothProfile.HEADSET) {
+            BluetoothHeadset headset = (BluetoothHeadset)proxy;
+            if (headset != null) {
+                headset.close();
+            }
+        }
+    }
+
     private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
         Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
         for (int i = 0; i < addresses.length; i++) {
diff --git a/core/java/android/bluetooth/BluetoothAssignedNumbers.java b/core/java/android/bluetooth/BluetoothAssignedNumbers.java
index 55bc814..580e9ff 100644
--- a/core/java/android/bluetooth/BluetoothAssignedNumbers.java
+++ b/core/java/android/bluetooth/BluetoothAssignedNumbers.java
@@ -23,12 +23,10 @@
  * @see <a href="https://www.bluetooth.org/technical/assignednumbers/identifiers.htm">
  * The Official Bluetooth SIG Member Website | Company Identifiers</a>
  *
- * @hide
  */
 public class BluetoothAssignedNumbers {
 
-    //// Bluetooth SIG Company ID values
-
+    // Bluetooth SIG Company ID values
     /*
      * Ericsson Technology Licensing.
      */
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 6a878d7..e604e6b 100644
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -259,6 +259,12 @@
     public static final int PROFILE_A2DP = 1;
     /** @hide */
     public static final int PROFILE_OPP = 2;
+    /** @hide */
+    public static final int PROFILE_HID = 3;
+    /** @hide */
+    public static final int PROFILE_PANU = 4;
+    /** @hide */
+    public static final int PROFILE_NAP = 5;
 
     /**
      * Check class bits for possible bluetooth profile support.
@@ -324,6 +330,14 @@
                 default:
                     return false;
             }
+        } else if (profile == PROFILE_HID) {
+            return (getDeviceClass() & Device.Major.PERIPHERAL) == Device.Major.PERIPHERAL;
+        } else if (profile == PROFILE_PANU || profile == PROFILE_NAP){
+            // No good way to distinguish between the two, based on class bits.
+            if (hasService(Service.NETWORKING)) {
+                return true;
+            }
+            return (getDeviceClass() & Device.Major.NETWORKING) == Device.Major.NETWORKING;
         } else {
             return false;
         }
diff --git a/core/java/android/bluetooth/BluetoothDevicePicker.java b/core/java/android/bluetooth/BluetoothDevicePicker.java
index 3a07033..c794be2 100644
--- a/core/java/android/bluetooth/BluetoothDevicePicker.java
+++ b/core/java/android/bluetooth/BluetoothDevicePicker.java
@@ -64,4 +64,9 @@
     public static final int FILTER_TYPE_AUDIO = 1;
     /** Ask device picker to show BT devices that support Object Transfer */
     public static final int FILTER_TYPE_TRANSFER = 2;
+    /** Ask device picker to show BT devices that support
+     * Personal Area Networking User (PANU) profile*/
+    public static final int FILTER_TYPE_PANU = 3;
+    /** Ask device picker to show BT devices that support Network Access Point (NAP) profile */
+    public static final int FILTER_TYPE_NAP = 4;
 }
diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index 9be4c8f..138e7f2 100644
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Message;
+import android.bluetooth.BluetoothAdapter;
 import android.server.BluetoothA2dpService;
 import android.server.BluetoothService;
 import android.util.Log;
@@ -28,6 +29,8 @@
 import com.android.internal.util.HierarchicalState;
 import com.android.internal.util.HierarchicalStateMachine;
 
+import java.util.Set;
+
 /**
  * This class is the Profile connection state machine associated with a remote
  * device. When the device bonds an instance of this class is created.
@@ -58,16 +61,21 @@
     private static final String TAG = "BluetoothDeviceProfileState";
     private static final boolean DBG = false;
 
+    // TODO(): Restructure the state machine to make it scalable with regard to profiles.
     public static final int CONNECT_HFP_OUTGOING = 1;
     public static final int CONNECT_HFP_INCOMING = 2;
     public static final int CONNECT_A2DP_OUTGOING = 3;
     public static final int CONNECT_A2DP_INCOMING = 4;
+    public static final int CONNECT_HID_OUTGOING = 5;
+    public static final int CONNECT_HID_INCOMING = 6;
 
-    public static final int DISCONNECT_HFP_OUTGOING = 5;
-    private static final int DISCONNECT_HFP_INCOMING = 6;
-    public static final int DISCONNECT_A2DP_OUTGOING = 7;
-    public static final int DISCONNECT_A2DP_INCOMING = 8;
-    public static final int DISCONNECT_PBAP_OUTGOING = 9;
+    public static final int DISCONNECT_HFP_OUTGOING = 50;
+    private static final int DISCONNECT_HFP_INCOMING = 51;
+    public static final int DISCONNECT_A2DP_OUTGOING = 52;
+    public static final int DISCONNECT_A2DP_INCOMING = 53;
+    public static final int DISCONNECT_HID_OUTGOING = 54;
+    public static final int DISCONNECT_HID_INCOMING = 55;
+    public static final int DISCONNECT_PBAP_OUTGOING = 56;
 
     public static final int UNPAIR = 100;
     public static final int AUTO_CONNECT_PROFILES = 101;
@@ -81,18 +89,20 @@
     private IncomingHandsfree mIncomingHandsfree = new IncomingHandsfree();
     private IncomingA2dp mIncomingA2dp = new IncomingA2dp();
     private OutgoingA2dp mOutgoingA2dp = new OutgoingA2dp();
+    private OutgoingHid mOutgoingHid = new OutgoingHid();
+    private IncomingHid mIncomingHid = new IncomingHid();
 
     private Context mContext;
     private BluetoothService mService;
     private BluetoothA2dpService mA2dpService;
     private BluetoothHeadset  mHeadsetService;
     private BluetoothPbap     mPbapService;
-    private boolean mHeadsetServiceConnected;
     private boolean mPbapServiceConnected;
 
     private BluetoothDevice mDevice;
-    private int mHeadsetState;
-    private int mA2dpState;
+    private int mHeadsetState = BluetoothProfile.STATE_DISCONNECTED;
+    private int mA2dpState = BluetoothProfile.STATE_DISCONNECTED;
+    private int mHidState = BluetoothProfile.STATE_DISCONNECTED;
 
     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -101,32 +111,42 @@
             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
             if (!device.equals(mDevice)) return;
 
-            if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
-                int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0);
-                int oldState = intent.getIntExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, 0);
-                int initiator = intent.getIntExtra(
-                    BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR,
-                    BluetoothHeadset.LOCAL_DISCONNECT);
-                mHeadsetState = newState;
-                if (newState == BluetoothHeadset.STATE_DISCONNECTED &&
-                    initiator == BluetoothHeadset.REMOTE_DISCONNECT) {
-                    sendMessage(DISCONNECT_HFP_INCOMING);
-                }
-                if (newState == BluetoothHeadset.STATE_CONNECTED ||
-                    newState == BluetoothHeadset.STATE_DISCONNECTED) {
-                    sendMessage(TRANSITION_TO_STABLE);
-                }
-            } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
-                int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
-                int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0);
+            if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
+                int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+                int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
                 mA2dpState = newState;
-                if ((oldState == BluetoothA2dp.STATE_CONNECTED ||
-                           oldState == BluetoothA2dp.STATE_PLAYING) &&
-                           newState == BluetoothA2dp.STATE_DISCONNECTED) {
+                if (oldState == BluetoothA2dp.STATE_CONNECTED &&
+                    newState == BluetoothA2dp.STATE_DISCONNECTED) {
                     sendMessage(DISCONNECT_A2DP_INCOMING);
                 }
-                if (newState == BluetoothA2dp.STATE_CONNECTED ||
-                    newState == BluetoothA2dp.STATE_DISCONNECTED) {
+                if (newState == BluetoothProfile.STATE_CONNECTED ||
+                    newState == BluetoothProfile.STATE_DISCONNECTED) {
+                    sendMessage(TRANSITION_TO_STABLE);
+                }
+            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+                int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+                int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
+
+                mHeadsetState = newState;
+                if (oldState == BluetoothHeadset.STATE_CONNECTED &&
+                    newState == BluetoothHeadset.STATE_DISCONNECTED) {
+                    sendMessage(DISCONNECT_HFP_INCOMING);
+                }
+                if (newState == BluetoothProfile.STATE_CONNECTED ||
+                    newState == BluetoothProfile.STATE_DISCONNECTED) {
+                    sendMessage(TRANSITION_TO_STABLE);
+                }
+            } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) {
+                int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0);
+                int oldState =
+                    intent.getIntExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, 0);
+                mHidState = newState;
+                if (oldState == BluetoothInputDevice.STATE_CONNECTED &&
+                    newState == BluetoothInputDevice.STATE_DISCONNECTED) {
+                    sendMessage(DISCONNECT_HID_INCOMING);
+                }
+                if (newState == BluetoothInputDevice.STATE_CONNECTED ||
+                    newState == BluetoothInputDevice.STATE_DISCONNECTED) {
                     sendMessage(TRANSITION_TO_STABLE);
                 }
             } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
@@ -169,36 +189,40 @@
         addState(mIncomingHandsfree);
         addState(mIncomingA2dp);
         addState(mOutgoingA2dp);
+        addState(mOutgoingHid);
+        addState(mIncomingHid);
         setInitialState(mBondedDevice);
 
         IntentFilter filter = new IntentFilter();
         // Fine-grained state broadcasts
-        filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
-        filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
         filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+        filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
         filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
 
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
-        HeadsetServiceListener l = new HeadsetServiceListener();
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+                                BluetoothProfile.HEADSET);
+        // TODO(): Convert PBAP to the new Profile APIs.
         PbapServiceListener p = new PbapServiceListener();
     }
 
-    private class HeadsetServiceListener implements BluetoothHeadset.ServiceListener {
-        public HeadsetServiceListener() {
-            mHeadsetService = new BluetoothHeadset(mContext, this);
-        }
-        public void onServiceConnected() {
+    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
             synchronized(BluetoothDeviceProfileState.this) {
-                mHeadsetServiceConnected = true;
+                mHeadsetService = (BluetoothHeadset) proxy;
             }
         }
-        public void onServiceDisconnected() {
+        public void onServiceDisconnected(int profile) {
             synchronized(BluetoothDeviceProfileState.this) {
-                mHeadsetServiceConnected = false;
+                mHeadsetService = null;
             }
         }
-    }
+    };
 
     private class PbapServiceListener implements BluetoothPbap.ServiceListener {
         public PbapServiceListener() {
@@ -246,6 +270,14 @@
                 case DISCONNECT_A2DP_INCOMING:
                     transitionTo(mIncomingA2dp);
                     break;
+                case CONNECT_HID_OUTGOING:
+                case DISCONNECT_HID_OUTGOING:
+                    transitionTo(mOutgoingHid);
+                    break;
+                case CONNECT_HID_INCOMING:
+                case DISCONNECT_HID_INCOMING:
+                    transitionTo(mIncomingHid);
+                    break;
                 case DISCONNECT_PBAP_OUTGOING:
                     processCommand(DISCONNECT_PBAP_OUTGOING);
                     break;
@@ -258,6 +290,10 @@
                         sendMessage(DISCONNECT_A2DP_OUTGOING);
                         deferMessage(message);
                         break;
+                    } else if (mHidState != BluetoothInputDevice.STATE_DISCONNECTED) {
+                        sendMessage(DISCONNECT_HID_OUTGOING);
+                        deferMessage(message);
+                        break;
                     }
                     processCommand(UNPAIR);
                     break;
@@ -265,21 +301,29 @@
                     if (isPhoneDocked(mDevice)) {
                         // Don't auto connect to docks.
                         break;
-                    } else if (!mHeadsetServiceConnected) {
+                    } else if (mHeadsetService == null) {
                         deferMessage(message);
                     } else {
                         if (mHeadsetService.getPriority(mDevice) ==
                               BluetoothHeadset.PRIORITY_AUTO_CONNECT &&
-                              !mHeadsetService.isConnected(mDevice)) {
-                            Log.i(TAG, "Headset:Auto Connect Profiles");
-                            mHeadsetService.connectHeadset(mDevice);
+                              mHeadsetService.getDevicesMatchingConnectionStates(
+                                  new int[] {BluetoothProfile.STATE_CONNECTED,
+                                             BluetoothProfile.STATE_CONNECTING,
+                                             BluetoothProfile.STATE_DISCONNECTING}).size() == 0) {
+                            mHeadsetService.connect(mDevice);
                         }
                         if (mA2dpService != null &&
-                              mA2dpService.getSinkPriority(mDevice) ==
+                              mA2dpService.getPriority(mDevice) ==
                               BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
-                              mA2dpService.getConnectedSinks().length == 0) {
-                            Log.i(TAG, "A2dp:Auto Connect Profiles");
-                            mA2dpService.connectSink(mDevice);
+                              mA2dpService.getDevicesMatchingConnectionStates(
+                                  new int[] {BluetoothA2dp.STATE_CONNECTED,
+                                             BluetoothProfile.STATE_CONNECTING,
+                                             BluetoothProfile.STATE_DISCONNECTING}).size() == 0) {
+                            mA2dpService.connect(mDevice);
+                        }
+                        if (mService.getInputDevicePriority(mDevice) ==
+                              BluetoothInputDevice.PRIORITY_AUTO_CONNECT) {
+                            mService.connectInputDevice(mDevice);
                         }
                     }
                     break;
@@ -289,17 +333,17 @@
                     }
                     if (message.arg1 == CONNECT_A2DP_OUTGOING) {
                         if (mA2dpService != null &&
-                            mA2dpService.getConnectedSinks().length == 0) {
+                            mA2dpService.getConnectedDevices().size() == 0) {
                             Log.i(TAG, "A2dp:Connect Other Profiles");
-                            mA2dpService.connectSink(mDevice);
+                            mA2dpService.connect(mDevice);
                         }
                     } else if (message.arg1 == CONNECT_HFP_OUTGOING) {
-                        if (!mHeadsetServiceConnected) {
+                        if (mHeadsetService == null) {
                             deferMessage(message);
                         } else {
-                            if (!mHeadsetService.isConnected(mDevice)) {
+                            if (mHeadsetService.getConnectedDevices().size() == 0) {
                                 Log.i(TAG, "Headset:Connect Other Profiles");
-                                mHeadsetService.connectHeadset(mDevice);
+                                mHeadsetService.connect(mDevice);
                             }
                         }
                     }
@@ -394,6 +438,23 @@
                        deferMessage(deferMsg);
                     }
                     break;
+                case CONNECT_HID_OUTGOING:
+                case DISCONNECT_HID_OUTGOING:
+                    deferMessage(message);
+                    break;
+                case CONNECT_HID_INCOMING:
+                    transitionTo(mIncomingHid);
+                    if (mStatus) {
+                        deferMsg.what = mCommand;
+                        deferMessage(deferMsg);
+                    }
+                    break;
+                case DISCONNECT_HID_INCOMING:
+                    if (mStatus) {
+                        deferMsg.what = mCommand;
+                        deferMessage(deferMsg);
+                    }
+                    break; // ignore
                 case DISCONNECT_PBAP_OUTGOING:
                 case UNPAIR:
                 case AUTO_CONNECT_PROFILES:
@@ -467,6 +528,13 @@
                     // If this causes incoming HFP to fail, it is more of a headset problem
                     // since both connections are incoming ones.
                     break;
+                case CONNECT_HID_OUTGOING:
+                case DISCONNECT_HID_OUTGOING:
+                    deferMessage(message);
+                    break;
+                case CONNECT_HID_INCOMING:
+                case DISCONNECT_HID_INCOMING:
+                     break; // ignore
                 case DISCONNECT_PBAP_OUTGOING:
                 case UNPAIR:
                 case AUTO_CONNECT_PROFILES:
@@ -560,6 +628,23 @@
                 case DISCONNECT_A2DP_INCOMING:
                     // Ignore, will be handled by Bluez
                     break;
+                case CONNECT_HID_OUTGOING:
+                case DISCONNECT_HID_OUTGOING:
+                    deferMessage(message);
+                    break;
+                case CONNECT_HID_INCOMING:
+                    transitionTo(mIncomingHid);
+                    if (mStatus) {
+                        deferMsg.what = mCommand;
+                        deferMessage(deferMsg);
+                    }
+                    break;
+                case DISCONNECT_HID_INCOMING:
+                    if (mStatus) {
+                        deferMsg.what = mCommand;
+                        deferMessage(deferMsg);
+                    }
+                    break; // ignore
                 case DISCONNECT_PBAP_OUTGOING:
                 case UNPAIR:
                 case AUTO_CONNECT_PROFILES:
@@ -631,6 +716,13 @@
                 case DISCONNECT_A2DP_INCOMING:
                     // Ignore, will be handled by Bluez
                     break;
+                case CONNECT_HID_OUTGOING:
+                case DISCONNECT_HID_OUTGOING:
+                    deferMessage(message);
+                    break;
+                case CONNECT_HID_INCOMING:
+                case DISCONNECT_HID_INCOMING:
+                     break; // ignore
                 case DISCONNECT_PBAP_OUTGOING:
                 case UNPAIR:
                 case AUTO_CONNECT_PROFILES:
@@ -648,11 +740,150 @@
     }
 
 
+    private class OutgoingHid extends HierarchicalState {
+        private boolean mStatus = false;
+        private int mCommand;
+
+        @Override
+        protected void enter() {
+            log("Entering OutgoingHid state with: " + getCurrentMessage().what);
+            mCommand = getCurrentMessage().what;
+            if (mCommand != CONNECT_HID_OUTGOING &&
+                mCommand != DISCONNECT_HID_OUTGOING) {
+                Log.e(TAG, "Error: OutgoingHid state with command:" + mCommand);
+            }
+            mStatus = processCommand(mCommand);
+            if (!mStatus) sendMessage(TRANSITION_TO_STABLE);
+        }
+
+        @Override
+        protected boolean processMessage(Message message) {
+            log("OutgoingHid State->Processing Message: " + message.what);
+            Message deferMsg = new Message();
+            switch(message.what) {
+                // defer all outgoing messages
+                case CONNECT_HFP_OUTGOING:
+                case CONNECT_A2DP_OUTGOING:
+                case CONNECT_HID_OUTGOING:
+                case DISCONNECT_HFP_OUTGOING:
+                case DISCONNECT_A2DP_OUTGOING:
+                case DISCONNECT_HID_OUTGOING:
+                    deferMessage(message);
+                    break;
+
+                case CONNECT_HFP_INCOMING:
+                    transitionTo(mIncomingHandsfree);
+                    break;
+                case CONNECT_A2DP_INCOMING:
+                    transitionTo(mIncomingA2dp);
+
+                    // Don't cancel HID outgoing as there is no guarantee it
+                    // will get canceled.
+                    // It might already be connected but we might not have got the
+                    // INPUT_DEVICE_STATE_CHANGE. Hence, no point disconnecting here.
+                    // The worst case, the connection will fail, retry.
+                    if (mStatus) {
+                        deferMsg.what = mCommand;
+                        deferMessage(deferMsg);
+                    }
+                    break;
+                case CONNECT_HID_INCOMING:
+                  // Bluez will take care of the conflicts
+                    transitionTo(mIncomingHid);
+                    break;
+
+                case DISCONNECT_HFP_INCOMING:
+                case DISCONNECT_A2DP_INCOMING:
+                    // At this point, we are already disconnected
+                    // with HFP. Sometimes HID connection can
+                    // fail due to the disconnection of HFP. So add a retry
+                    // for the HID.
+                    if (mStatus) {
+                        deferMsg.what = mCommand;
+                        deferMessage(deferMsg);
+                    }
+                    break;
+                case DISCONNECT_HID_INCOMING:
+                    // Ignore, will be handled by Bluez
+                    break;
+                case DISCONNECT_PBAP_OUTGOING:
+                case UNPAIR:
+                case AUTO_CONNECT_PROFILES:
+                    deferMessage(message);
+                    break;
+                case TRANSITION_TO_STABLE:
+                    transitionTo(mBondedDevice);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+  private class IncomingHid extends HierarchicalState {
+      private boolean mStatus = false;
+      private int mCommand;
+
+      @Override
+      protected void enter() {
+          log("Entering IncomingHid state with: " + getCurrentMessage().what);
+          mCommand = getCurrentMessage().what;
+          if (mCommand != CONNECT_HID_INCOMING &&
+              mCommand != DISCONNECT_HID_INCOMING) {
+              Log.e(TAG, "Error: IncomingHid state with command:" + mCommand);
+          }
+          mStatus = processCommand(mCommand);
+          if (!mStatus) sendMessage(TRANSITION_TO_STABLE);
+      }
+
+      @Override
+      protected boolean processMessage(Message message) {
+          log("IncomingHid State->Processing Message: " + message.what);
+          Message deferMsg = new Message();
+          switch(message.what) {
+              case CONNECT_HFP_OUTGOING:
+              case CONNECT_HFP_INCOMING:
+              case DISCONNECT_HFP_OUTGOING:
+              case CONNECT_A2DP_INCOMING:
+              case CONNECT_A2DP_OUTGOING:
+              case DISCONNECT_A2DP_OUTGOING:
+              case CONNECT_HID_OUTGOING:
+              case CONNECT_HID_INCOMING:
+              case DISCONNECT_HID_OUTGOING:
+                  deferMessage(message);
+                  break;
+              case DISCONNECT_HFP_INCOMING:
+                  // Shouldn't happen but if does, we can handle it.
+                  // Depends if the headset can handle it.
+                  // Incoming HID will be handled by Bluez, Disconnect HFP
+                  // the socket would have already been closed.
+                  // ignore
+                  break;
+              case DISCONNECT_HID_INCOMING:
+              case DISCONNECT_A2DP_INCOMING:
+                  // Ignore, will be handled by Bluez
+                  break;
+              case DISCONNECT_PBAP_OUTGOING:
+              case UNPAIR:
+              case AUTO_CONNECT_PROFILES:
+                  deferMessage(message);
+                  break;
+              case TRANSITION_TO_STABLE:
+                  transitionTo(mBondedDevice);
+                  break;
+              default:
+                  return NOT_HANDLED;
+          }
+          return HANDLED;
+      }
+  }
+
 
     synchronized void cancelCommand(int command) {
         if (command == CONNECT_HFP_OUTGOING ) {
             // Cancel the outgoing thread.
-            if (mHeadsetServiceConnected) {
+            if (mHeadsetService != null) {
                 mHeadsetService.cancelConnectThread();
             }
             // HeadsetService is down. Phone process most likely crashed.
@@ -670,12 +901,14 @@
         Log.i(TAG, "Processing command:" + command);
         switch(command) {
             case  CONNECT_HFP_OUTGOING:
-                if (mHeadsetService != null) {
+                if (mHeadsetService == null) {
+                    deferProfileServiceMessage(command);
+                } else {
                     return mHeadsetService.connectHeadsetInternal(mDevice);
                 }
                 break;
             case CONNECT_HFP_INCOMING:
-                if (!mHeadsetServiceConnected) {
+                if (mHeadsetService == null) {
                     deferProfileServiceMessage(command);
                 } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) {
                     return mHeadsetService.acceptIncomingConnect(mDevice);
@@ -693,8 +926,12 @@
                 handleConnectionOfOtherProfiles(command);
                 // ignore, Bluez takes care
                 return true;
+            case CONNECT_HID_OUTGOING:
+                return mService.connectInputDeviceInternal(mDevice);
+            case CONNECT_HID_INCOMING:
+                return true;
             case DISCONNECT_HFP_OUTGOING:
-                if (!mHeadsetServiceConnected) {
+                if (mHeadsetService == null) {
                     deferProfileServiceMessage(command);
                 } else {
                     // Disconnect PBAP
@@ -717,13 +954,22 @@
                 return true;
             case DISCONNECT_A2DP_OUTGOING:
                 if (mA2dpService != null) {
-                    if (mA2dpService.getSinkPriority(mDevice) ==
+                    if (mA2dpService.getPriority(mDevice) ==
                         BluetoothA2dp.PRIORITY_AUTO_CONNECT) {
-                        mA2dpService.setSinkPriority(mDevice, BluetoothHeadset.PRIORITY_ON);
+                        mA2dpService.setPriority(mDevice, BluetoothHeadset.PRIORITY_ON);
                     }
                     return mA2dpService.disconnectSinkInternal(mDevice);
                 }
                 break;
+            case DISCONNECT_HID_INCOMING:
+                // ignore
+                return true;
+            case DISCONNECT_HID_OUTGOING:
+                if (mService.getInputDevicePriority(mDevice) ==
+                    BluetoothInputDevice.PRIORITY_AUTO_CONNECT) {
+                    mService.setInputDevicePriority(mDevice, BluetoothInputDevice.PRIORITY_ON);
+                }
+                return mService.disconnectInputDeviceInternal(mDevice);
             case DISCONNECT_PBAP_OUTGOING:
                 if (!mPbapServiceConnected) {
                     deferProfileServiceMessage(command);
@@ -770,7 +1016,7 @@
                 // Connect A2DP if there is no incoming connection
                 // If the priority is OFF - don't auto connect.
                 // If the priority is AUTO_CONNECT, auto connect code takes care.
-                if (mA2dpService.getSinkPriority(mDevice) == BluetoothA2dp.PRIORITY_ON) {
+                if (mA2dpService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON) {
                     Message msg = new Message();
                     msg.what = CONNECT_OTHER_PROFILES;
                     msg.arg1 = CONNECT_A2DP_OUTGOING;
@@ -781,7 +1027,7 @@
                 // This is again against spec. HFP incoming connections should be made
                 // before A2DP, so we should not hit this case. But many devices
                 // don't follow this.
-                if (mHeadsetService.getPriority(mDevice) == BluetoothHeadset.PRIORITY_ON) {
+                if (mHeadsetService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON) {
                     Message msg = new Message();
                     msg.what = CONNECT_OTHER_PROFILES;
                     msg.arg1 = CONNECT_HFP_OUTGOING;
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index da1aa45..c03b444 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -22,73 +22,96 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.os.RemoteException;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- *
  * Public API for controlling the Bluetooth Headset Service. This includes both
- * Bluetooth Headset and Handsfree (v1.5) profiles. The Headset service will
- * attempt a handsfree connection first, and fall back to headset.
+ * Bluetooth Headset and Handsfree (v1.5) profiles.
  *
- * BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
+ * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset
  * Service via IPC.
  *
- * Creating a BluetoothHeadset object will create a binding with the
- * BluetoothHeadset service. Users of this object should call close() when they
- * are finished with the BluetoothHeadset, so that this proxy object can unbind
- * from the service.
+ * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothHeadset proxy object. Use
+ * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
  *
- * This BluetoothHeadset object is not immediately bound to the
- * BluetoothHeadset service. Use the ServiceListener interface to obtain a
- * notification when it is bound, this is especially important if you wish to
- * immediately call methods on BluetoothHeadset after construction.
- *
- * Android only supports one connected Bluetooth Headset at a time.
- *
- * @hide
+ * <p> Android only supports one connected Bluetooth Headset at a time.
+ * Each method is protected with its appropriate permission.
  */
-public final class BluetoothHeadset {
-
+public final class BluetoothHeadset implements BluetoothProfile {
     private static final String TAG = "BluetoothHeadset";
     private static final boolean DBG = false;
 
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_STATE_CHANGED =
-            "android.bluetooth.headset.action.STATE_CHANGED";
     /**
-     * TODO(API release): Consider incorporating as new state in
-     * HEADSET_STATE_CHANGED
+     * Intent used to broadcast the change in connection state of the Headset
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * {@link #EXTRA_STATE} - The current state of the profile.
+     * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
+     *
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED";
+
+    /**
+     * Intent used to broadcast the change in the Audio Connection state of the
+     * A2DP profile.
+     *
+     * <p>This intent will have 3 extras:
+     * {@link #EXTRA_STATE} - The current state of the profile.
+     * {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
+     *
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_AUDIO_STATE_CHANGED =
-            "android.bluetooth.headset.action.AUDIO_STATE_CHANGED";
-    public static final String EXTRA_STATE =
-            "android.bluetooth.headset.extra.STATE";
-    public static final String EXTRA_PREVIOUS_STATE =
-            "android.bluetooth.headset.extra.PREVIOUS_STATE";
-    public static final String EXTRA_AUDIO_STATE =
-            "android.bluetooth.headset.extra.AUDIO_STATE";
+        "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED";
 
-    /** Extra to be used with the Headset State change intent.
-     * This will be used only when Headset state changes to
-     * {@link #STATE_DISCONNECTED} from any previous state.
-     * This extra field is optional and will be used when
-     * we have deterministic information regarding whether
-     * the disconnect was initiated by the remote device or
-     * by the local adapter.
-     */
-    public static final String EXTRA_DISCONNECT_INITIATOR =
-            "android.bluetooth.headset.extra.DISCONNECT_INITIATOR";
 
     /**
-     * Broadcast Action: Indicates a headset has posted a vendor-specific event.
-     * <p>Always contains the extra fields {@link #EXTRA_DEVICE},
-     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD}, and
-     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS}.
+     * Intent used to broadcast that the headset has posted a
+     * vendor-specific event.
+     *
+     * <p>This intent will have 4 extras and 1 category.
+     * {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device
+     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor specific
+     *                                                    command
+     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT command
+     *                                                         type.
+     * Can be one of  {@link #AT_CMD_TYPE_READ}, {@link #AT_CMD_TYPE_TEST},
+     * or {@link #AT_CMD_TYPE_SET}, {@link #AT_CMD_TYPE_BASIC},
+     * {@link #AT_CMD_TYPE_ACTION}.
+     *
+     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command arguments.
+     *
+     * The category is the Company ID of the vendor defining the
+     * vendor-specific command. {@link BluetoothAssignedNumbers}
+     *
+     * For example, for Plantronics specific events
+     * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55
+     *
+     * <p> For example, an AT+XEVENT=foo,3 will get translated into
+     * EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT
+     * EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET
+     * EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3
+     *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -104,13 +127,46 @@
 
     /**
      * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
-     * intents that contains the Company ID of the vendor defining the vendor-specific
-     * command.
-     * @see <a href="https://www.bluetooth.org/Technical/AssignedNumbers/identifiers.htm">
-     * Bluetooth SIG Assigned Numbers - Company Identifiers</a>
+     * intents that contains the AT command type of the vendor-specific command.
      */
-    public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID =
-            "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID";
+    public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE =
+            "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE";
+
+    /**
+     * AT command type READ used with
+     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
+     * For example, AT+VGM?. There are no arguments for this command type.
+     */
+    public static final int AT_CMD_TYPE_READ = 0;
+
+    /**
+     * AT command type TEST used with
+     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
+     * For example, AT+VGM=?. There are no arguments for this command type.
+     */
+    public static final int AT_CMD_TYPE_TEST = 1;
+
+    /**
+     * AT command type SET used with
+     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
+     * For example, AT+VGM=<args>.
+     */
+    public static final int AT_CMD_TYPE_SET = 2;
+
+    /**
+     * AT command type BASIC used with
+     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
+     * For example, ATD. Single character commands and everything following the
+     * character are arguments.
+     */
+    public static final int AT_CMD_TYPE_BASIC = 3;
+
+    /**
+     * AT command type ACTION used with
+     * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
+     * For example, AT+CHUP. There are no arguments for action commands.
+     */
+    public static final int AT_CMD_TYPE_ACTION = 4;
 
     /**
      * A Parcelable String array extra field in
@@ -120,100 +176,62 @@
     public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
             "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
 
+    /**
+     * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
+     * for the companyId
+     */
+    public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY  =
+            "android.bluetooth.headset.intent.category.companyid";
 
     /**
-     * TODO(API release): Consider incorporating as new state in
-     * HEADSET_STATE_CHANGED
+     * Headset state when SCO audio is connected
+     * This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
      */
+    public static final int STATE_AUDIO_CONNECTED = 10;
+
+    /**
+     * Headset state when SCO audio is connecting
+     * This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
+     * @hide
+     */
+    public static final int STATE_AUDIO_CONNECTING = 12;
+
+    /**
+     * Headset state when SCO audio is not connected
+     * This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_AUDIO_STATE_CHANGED} intent.
+     */
+    public static final int STATE_AUDIO_DISCONNECTED = 11;
+
+    private Context mContext;
+    private ServiceListener mServiceListener;
     private IBluetoothHeadset mService;
-    private final Context mContext;
-    private final ServiceListener mServiceListener;
-
-    /** There was an error trying to obtain the state */
-    public static final int STATE_ERROR        = -1;
-    /** No headset currently connected */
-    public static final int STATE_DISCONNECTED = 0;
-    /** Connection attempt in progress */
-    public static final int STATE_CONNECTING   = 1;
-    /** A headset is currently connected */
-    public static final int STATE_CONNECTED    = 2;
-
-    /** A SCO audio channel is not established */
-    public static final int AUDIO_STATE_DISCONNECTED = 0;
-    /** A SCO audio channel is established */
-    public static final int AUDIO_STATE_CONNECTED = 1;
-
-    public static final int RESULT_FAILURE = 0;
-    public static final int RESULT_SUCCESS = 1;
-    /** Connection canceled before completion. */
-    public static final int RESULT_CANCELED = 2;
-
-    /** Values for {@link #EXTRA_DISCONNECT_INITIATOR} */
-    public static final int REMOTE_DISCONNECT = 0;
-    public static final int LOCAL_DISCONNECT = 1;
-
-
-    /** Default priority for headsets for which we will accept
-     * incoming connections and auto-connect. */
-    public static final int PRIORITY_AUTO_CONNECT = 1000;
-    /** Default priority for headsets for which we will accept
-     * incoming connections but not auto-connect. */
-    public static final int PRIORITY_ON = 100;
-    /** Default priority for headsets that should not be auto-connected
-     * and not allow incoming connections. */
-    public static final int PRIORITY_OFF = 0;
-    /** Default priority when not set or when the device is unpaired */
-    public static final int PRIORITY_UNDEFINED = -1;
-
-    /**
-     * An interface for notifying BluetoothHeadset IPC clients when they have
-     * been connected to the BluetoothHeadset service.
-     */
-    public interface ServiceListener {
-        /**
-         * Called to notify the client when this proxy object has been
-         * connected to the BluetoothHeadset service. Clients must wait for
-         * this callback before making IPC calls on the BluetoothHeadset
-         * service.
-         */
-        public void onServiceConnected();
-
-        /**
-         * Called to notify the client that this proxy object has been
-         * disconnected from the BluetoothHeadset service. Clients must not
-         * make IPC calls on the BluetoothHeadset service after this callback.
-         * This callback will currently only occur if the application hosting
-         * the BluetoothHeadset service, but may be called more often in future.
-         */
-        public void onServiceDisconnected();
-    }
+    BluetoothAdapter mAdapter;
 
     /**
      * Create a BluetoothHeadset proxy object.
      */
-    public BluetoothHeadset(Context context, ServiceListener l) {
+    /*package*/ BluetoothHeadset(Context context, ServiceListener l) {
         mContext = context;
         mServiceListener = l;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
         if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) {
             Log.e(TAG, "Could not bind to Bluetooth Headset Service");
         }
     }
 
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
     /**
      * Close the connection to the backing service.
      * Other public functions of BluetoothHeadset will return default error
      * results once close() has been called. Multiple invocations of close()
      * are ok.
      */
-    public synchronized void close() {
+    /*package*/ synchronized void close() {
         if (DBG) log("close()");
         if (mConnection != null) {
             mContext.unbindService(mConnection);
@@ -222,190 +240,213 @@
     }
 
     /**
-     * Get the current state of the Bluetooth Headset service.
-     * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
-     *         object is currently not connected to the Headset service.
+     * {@inheritDoc}
+     * @hide
      */
-    public int getState(BluetoothDevice device) {
-        if (DBG) log("getState()");
-        if (mService != null) {
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) log("connect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
-                return mService.getState(device);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.connect(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
-        return BluetoothHeadset.STATE_ERROR;
-    }
-
-    /**
-     * Get the BluetoothDevice for the current headset.
-     * @return current headset, or null if not in connected or connecting
-     *         state, or if this proxy object is not connected to the Headset
-     *         service.
-     */
-    public BluetoothDevice getCurrentHeadset() {
-        if (DBG) log("getCurrentHeadset()");
-        if (mService != null) {
-            try {
-                return mService.getCurrentHeadset();
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-        return null;
-    }
-
-    /**
-     * Request to initiate a connection to a headset.
-     * This call does not block. Fails if a headset is already connecting
-     * or connected.
-     * Initiates auto-connection if device is null. Tries to connect to all
-     * devices with priority greater than PRIORITY_AUTO in descending order.
-     * @param device device to connect to, or null to auto-connect last connected
-     *               headset
-     * @return       false if there was a problem initiating the connection
-     *               procedure, and no further HEADSET_STATE_CHANGED intents
-     *               will be expected.
-     */
-    public boolean connectHeadset(BluetoothDevice device) {
-        if (DBG) log("connectHeadset(" + device + ")");
-        if (mService != null) {
-            try {
-                if (mService.connectHeadset(device)) {
-                    return true;
-                }
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
         return false;
     }
 
     /**
-     * Returns true if the specified headset is connected (does not include
-     * connecting). Returns false if not connected, or if this proxy object
-     * if not currently connected to the headset service.
+     * {@inheritDoc}
+     * @hide
      */
-    public boolean isConnected(BluetoothDevice device) {
-        if (DBG) log("isConnected(" + device + ")");
-        if (mService != null) {
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
-                return mService.isConnected(device);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.disconnect(device);
+            } catch (RemoteException e) {
+              Log.e(TAG, Log.getStackTraceString(new Throwable()));
+              return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
         return false;
     }
 
     /**
-     * Disconnects the current headset. Currently this call blocks, it may soon
-     * be made asynchronous. Returns false if this proxy object is
-     * not currently connected to the Headset service.
+     * {@inheritDoc}
      */
-    public boolean disconnectHeadset(BluetoothDevice device) {
-        if (DBG) log("disconnectHeadset()");
-        if (mService != null) {
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (DBG) log("getConnectedDevices()");
+        if (mService != null && isEnabled()) {
             try {
-                mService.disconnectHeadset(device);
-                return true;
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.getConnectedDevices();
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
         }
-        return false;
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
     }
 
     /**
-     * Start BT Voice Recognition mode, and set up Bluetooth audio path.
-     * Returns false if there is no headset connected, or if the
-     * connected headset does not support voice recognition, or on
-     * error.
+     * {@inheritDoc}
      */
-    public boolean startVoiceRecognition() {
-        if (DBG) log("startVoiceRecognition()");
-        if (mService != null) {
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (DBG) log("getDevicesMatchingStates()");
+        if (mService != null && isEnabled()) {
             try {
-                return mService.startVoiceRecognition();
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.getDevicesMatchingConnectionStates(states);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
         }
-        return false;
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
     }
 
     /**
-     * Stop BT Voice Recognition mode, and shut down Bluetooth audio path.
-     * Returns false if there is no headset connected, or the connected
-     * headset is not in voice recognition mode, or on error.
+     * {@inheritDoc}
      */
-    public boolean stopVoiceRecognition() {
-        if (DBG) log("stopVoiceRecognition()");
-        if (mService != null) {
+    public int getConnectionState(BluetoothDevice device) {
+        if (DBG) log("getConnectionState(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
-                return mService.stopVoiceRecognition();
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
         }
-        return false;
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.STATE_DISCONNECTED;
     }
 
     /**
-     * Set priority of headset.
-     * Priority is a non-negative integer. By default paired headsets will have
-     * a priority of PRIORITY_AUTO, and unpaired headset PRIORITY_NONE (0).
-     * Headsets with priority greater than zero will be auto-connected, and
-     * incoming connections will be accepted (if no other headset is
-     * connected).
-     * Auto-connection occurs at the following events: boot, incoming phone
-     * call, outgoing phone call.
-     * Headsets with priority equal to zero, or that are unpaired, are not
-     * auto-connected.
-     * Incoming connections are ignored regardless of priority if there is
-     * already a headset connected.
-     * @param device paired headset
-     * @param priority Integer priority, for example PRIORITY_AUTO or
-     *                 PRIORITY_NONE
-     * @return true if successful, false if there was some error
+     * {@inheritDoc}
+     * @hide
      */
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
-        if (mService != null) {
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            if (priority != BluetoothProfile.PRIORITY_OFF &&
+                priority != BluetoothProfile.PRIORITY_ON) {
+              return false;
+            }
             try {
                 return mService.setPriority(device, priority);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
         return false;
     }
 
     /**
-     * Get priority of headset.
-     * @param device headset
-     * @return non-negative priority, or negative error code on error
+     * {@inheritDoc}
+     * @hide
      */
     public int getPriority(BluetoothDevice device) {
         if (DBG) log("getPriority(" + device + ")");
-        if (mService != null) {
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
                 return mService.getPriority(device);
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return PRIORITY_OFF;
+            }
         }
-        return -1;
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return PRIORITY_OFF;
+    }
+
+    /**
+     * Start Bluetooth voice recognition. This methods sends the voice
+     * recognition AT command to the headset and establishes the
+     * audio connection.
+     *
+     * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
+     * {@link #EXTRA_STATE} will be set to {@link #STATE_AUDIO_CONNECTED}
+     * when the audio connection is established,
+     * and to {@link #STATE_AUDIO_DISCONNECTED} in case of failure.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Bluetooth headset
+     * @return false if there is no headset connected of if the
+     *               connected headset doesn't support voice recognition
+     *               or on error, true otherwise
+     */
+    public boolean startVoiceRecognition(BluetoothDevice device) {
+        if (DBG) log("startVoiceRecognition()");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.startVoiceRecognition(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Stop Bluetooth Voice Recognition mode, and shut down the
+     * Bluetooth audio path.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Bluetooth headset
+     * @return false if there is no headset connected
+     *               or on error, true otherwise
+     */
+    public boolean stopVoiceRecognition(BluetoothDevice device) {
+        if (DBG) log("stopVoiceRecognition()");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.stopVoiceRecognition(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Check if Bluetooth SCO audio is connected.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Bluetooth headset
+     * @return true if SCO is connected,
+     *         false otherwise or on error
+     */
+    public boolean isAudioConnected(BluetoothDevice device) {
+        if (DBG) log("isAudioConnected()");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+              return mService.isAudioConnected(device);
+            } catch (RemoteException e) {
+              Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
     }
 
     /**
@@ -416,24 +457,29 @@
      * boot. This is a good indicator for spammy headset/handsfree units that
      * can keep the device awake by polling for cellular status updates. As a
      * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms
+     *
+     * @param device the bluetooth headset.
      * @return monotonically increasing battery usage hint, or a negative error
      *         code on error
      * @hide
      */
-    public int getBatteryUsageHint() {
+    public int getBatteryUsageHint(BluetoothDevice device) {
         if (DBG) log("getBatteryUsageHint()");
-        if (mService != null) {
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
             try {
-                return mService.getBatteryUsageHint();
-            } catch (RemoteException e) {Log.e(TAG, e.toString());}
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+                return mService.getBatteryUsageHint(device);
+            } catch (RemoteException e) {
+                Log.e(TAG,  Log.getStackTraceString(new Throwable()));
+            }
         }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
         return -1;
     }
+
     /**
      * Indicates if current platform supports voice dialing over bluetooth SCO.
+     *
      * @return true if voice dialing over bluetooth is supported, false otherwise.
      * @hide
      */
@@ -444,11 +490,13 @@
 
     /**
      * Cancel the outgoing connection.
+     * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean cancelConnectThread() {
         if (DBG) log("cancelConnectThread");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                 return mService.cancelConnectThread();
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -461,11 +509,13 @@
 
     /**
      * Accept the incoming connection.
+     * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean acceptIncomingConnect(BluetoothDevice device) {
         if (DBG) log("acceptIncomingConnect");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                 return mService.acceptIncomingConnect(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -477,12 +527,14 @@
     }
 
     /**
-     * Create the connect thread the incoming connection.
+     * Create the connect thread for the incoming connection.
+     * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean createIncomingConnect(BluetoothDevice device) {
         if (DBG) log("createIncomingConnect");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                 return mService.createIncomingConnect(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -496,11 +548,12 @@
     /**
      * Connect to a Bluetooth Headset.
      * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean connectHeadsetInternal(BluetoothDevice device) {
         if (DBG) log("connectHeadsetInternal");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                 return mService.connectHeadsetInternal(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -514,11 +567,12 @@
     /**
      * Disconnect a Bluetooth Headset.
      * Note: This is an internal function and shouldn't be exposed
+     *
      * @hide
      */
     public boolean disconnectHeadsetInternal(BluetoothDevice device) {
         if (DBG) log("disconnectHeadsetInternal");
-        if (mService != null) {
+        if (mService != null && isEnabled()) {
             try {
                  return mService.disconnectHeadsetInternal(device);
             } catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -528,23 +582,75 @@
         }
         return false;
     }
+
+    /**
+     * Set the audio state of the Headset.
+     * Note: This is an internal function and shouldn't be exposed
+     *
+     * @hide
+     */
+    public boolean setAudioState(BluetoothDevice device, int state) {
+        if (DBG) log("setAudioState");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.setAudioState(device, state);
+            } catch (RemoteException e) {Log.e(TAG, e.toString());}
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+        }
+        return false;
+    }
+
+    /**
+     * Get the current audio state of the Headset.
+     * Note: This is an internal function and shouldn't be exposed
+     *
+     * @hide
+     */
+    public int getAudioState(BluetoothDevice device) {
+        if (DBG) log("getAudioState");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getAudioState(device);
+            } catch (RemoteException e) {Log.e(TAG, e.toString());}
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+        }
+        return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+    }
+
     private ServiceConnection mConnection = new ServiceConnection() {
         public void onServiceConnected(ComponentName className, IBinder service) {
             if (DBG) Log.d(TAG, "Proxy object connected");
             mService = IBluetoothHeadset.Stub.asInterface(service);
+
             if (mServiceListener != null) {
-                mServiceListener.onServiceConnected();
+                mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, BluetoothHeadset.this);
             }
         }
         public void onServiceDisconnected(ComponentName className) {
             if (DBG) Log.d(TAG, "Proxy object disconnected");
             mService = null;
             if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected();
+                mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
             }
         }
     };
 
+    private boolean isEnabled() {
+       if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+       return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+       if (device == null) return false;
+
+       if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+       return false;
+    }
+
     private static void log(String msg) {
         Log.d(TAG, msg);
     }
diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java
new file mode 100644
index 0000000..bc8a836
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothInputDevice.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Public API for controlling the Bluetooth HID (Input Device) Profile
+ *
+ * BluetoothInputDevice is a proxy object used to make calls to Bluetooth Service
+ * which handles the HID profile.
+ *
+ * Creating a BluetoothInputDevice object will initiate a binding with the
+ * Bluetooth service. Users of this object should call close() when they
+ * are finished, so that this proxy object can unbind from the service.
+ *
+ * Currently the Bluetooth service runs in the system server and this
+ * proxy object will be immediately bound to the service on construction.
+ *
+ *  @hide
+ */
+public final class BluetoothInputDevice {
+    private static final String TAG = "BluetoothInputDevice";
+    private static final boolean DBG = false;
+
+    /** int extra for ACTION_INPUT_DEVICE_STATE_CHANGED */
+    public static final String EXTRA_INPUT_DEVICE_STATE =
+        "android.bluetooth.inputdevice.extra.INPUT_DEVICE_STATE";
+    /** int extra for ACTION_INPUT_DEVICE_STATE_CHANGED */
+    public static final String EXTRA_PREVIOUS_INPUT_DEVICE_STATE =
+        "android.bluetooth.inputdevice.extra.PREVIOUS_INPUT_DEVICE_STATE";
+
+    /** Indicates the state of an input device has changed.
+     * This intent will always contain EXTRA_INPUT_DEVICE_STATE,
+     * EXTRA_PREVIOUS_INPUT_DEVICE_STATE and BluetoothDevice.EXTRA_DEVICE
+     * extras.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_INPUT_DEVICE_STATE_CHANGED =
+        "android.bluetooth.inputdevice.action.INPUT_DEVICE_STATE_CHANGED";
+
+    public static final int STATE_DISCONNECTED = 0;
+    public static final int STATE_CONNECTING   = 1;
+    public static final int STATE_CONNECTED    = 2;
+    public static final int STATE_DISCONNECTING = 3;
+
+    /**
+     * Auto connection, incoming and outgoing connection are allowed at this
+     * priority level.
+     */
+    public static final int PRIORITY_AUTO_CONNECT = 1000;
+    /**
+     * Incoming and outgoing connection are allowed at this priority level
+     */
+    public static final int PRIORITY_ON = 100;
+    /**
+     * Connections to the device are not allowed at this priority level.
+     */
+    public static final int PRIORITY_OFF = 0;
+    /**
+     * Default priority level when the device is unpaired.
+     */
+    public static final int PRIORITY_UNDEFINED = -1;
+
+    private final IBluetooth mService;
+    private final Context mContext;
+
+    /**
+     * Create a BluetoothInputDevice proxy object for interacting with the local
+     * Bluetooth Service which handle the HID profile.
+     * @param c Context
+     */
+    public BluetoothInputDevice(Context c) {
+        mContext = c;
+
+        IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
+        if (b != null) {
+            mService = IBluetooth.Stub.asInterface(b);
+        } else {
+            Log.w(TAG, "Bluetooth Service not available!");
+
+            // Instead of throwing an exception which prevents people from going
+            // into Wireless settings in the emulator. Let it crash later when it is actually used.
+            mService = null;
+        }
+    }
+
+    /** Initiate a connection to an Input device.
+     *
+     *  This function returns false on error and true if the connection
+     *  attempt is being made.
+     *
+     *  Listen for INPUT_DEVICE_STATE_CHANGED_ACTION to find out when the
+     *  connection is completed.
+     *  @param device Remote BT device.
+     *  @return false on immediate error, true otherwise
+     *  @hide
+     */
+    public boolean connectInputDevice(BluetoothDevice device) {
+        if (DBG) log("connectInputDevice(" + device + ")");
+        try {
+            return mService.connectInputDevice(device);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return false;
+        }
+    }
+
+    /** Initiate disconnect from an Input Device.
+     *  This function return false on error and true if the disconnection
+     *  attempt is being made.
+     *
+     *  Listen for INPUT_DEVICE_STATE_CHANGED_ACTION to find out when
+     *  disconnect is completed.
+     *
+     *  @param device Remote BT device.
+     *  @return false on immediate error, true otherwise
+     *  @hide
+     */
+    public boolean disconnectInputDevice(BluetoothDevice device) {
+        if (DBG) log("disconnectInputDevice(" + device + ")");
+        try {
+            return mService.disconnectInputDevice(device);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return false;
+        }
+    }
+
+    /** Check if a specified InputDevice is connected.
+     *
+     *  @param device Remote BT device.
+     *  @return True if connected , false otherwise and on error.
+     *  @hide
+     */
+    public boolean isInputDeviceConnected(BluetoothDevice device) {
+        if (DBG) log("isInputDeviceConnected(" + device + ")");
+        int state = getInputDeviceState(device);
+        if (state == STATE_CONNECTED) return true;
+        return false;
+    }
+
+    /** Check if any Input Device is connected.
+     *
+     * @return List of devices, empty List on error.
+     * @hide
+     */
+    public List<BluetoothDevice> getConnectedInputDevices() {
+        if (DBG) log("getConnectedInputDevices()");
+        try {
+            return mService.getConnectedInputDevices();
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return new ArrayList<BluetoothDevice>();
+        }
+    }
+
+    /** Get the state of an Input Device.
+     *
+     *  @param device Remote BT device.
+     *  @return The current state of the Input Device
+     *  @hide
+     */
+    public int getInputDeviceState(BluetoothDevice device) {
+        if (DBG) log("getInputDeviceState(" + device + ")");
+        try {
+            return mService.getInputDeviceState(device);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return STATE_DISCONNECTED;
+        }
+    }
+
+    /**
+     * Set priority of an input device.
+     *
+     * Priority is a non-negative integer. Priority can take the following
+     * values:
+     * {@link PRIORITY_ON}, {@link PRIORITY_OFF}, {@link PRIORITY_AUTO_CONNECT}
+     *
+     * @param device Paired device.
+     * @param priority Integer priority
+     * @return true if priority is set, false on error
+     */
+    public boolean setInputDevicePriority(BluetoothDevice device, int priority) {
+        if (DBG) log("setInputDevicePriority(" + device + ", " + priority + ")");
+        try {
+            return mService.setInputDevicePriority(device, priority);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return false;
+        }
+    }
+
+    /**
+     * Get the priority associated with an Input Device.
+     *
+     * @param device Input Device
+     * @return non-negative priority, or negative error code on error.
+     */
+    public int getInputDevicePriority(BluetoothDevice device) {
+        if (DBG) log("getInputDevicePriority(" + device + ")");
+        try {
+            return mService.getInputDevicePriority(device);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return PRIORITY_OFF;
+        }
+    }
+
+    private static void log(String msg) {
+        Log.d(TAG, msg);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
new file mode 100644
index 0000000..f55e96a
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -0,0 +1,188 @@
+/*
+ * 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.bluetooth;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public final class BluetoothPan {
+    private static final String TAG = "BluetoothPan";
+    private static final boolean DBG = false;
+
+    /** int extra for ACTION_PAN_STATE_CHANGED */
+    public static final String EXTRA_PAN_STATE =
+        "android.bluetooth.pan.extra.STATE";
+    /** int extra for ACTION_PAN_STATE_CHANGED */
+    public static final String EXTRA_PREVIOUS_PAN_STATE =
+        "android.bluetooth.pan.extra.PREVIOUS_STATE";
+
+    /** Indicates the state of an PAN device has changed.
+     * This intent will always contain EXTRA_DEVICE_STATE,
+     * EXTRA_PREVIOUS_DEVICE_STATE and BluetoothDevice.EXTRA_DEVICE
+     * extras.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PAN_STATE_CHANGED =
+        "android.bluetooth.pan.action.STATE_CHANGED";
+
+    public static final String NAP_ROLE = "nap";
+    public static final String NAP_BRIDGE = "pan1";
+
+    public static final int MAX_CONNECTIONS = 7;
+
+    public static final int STATE_DISCONNECTED = 0;
+    public static final int STATE_CONNECTING   = 1;
+    public static final int STATE_CONNECTED    = 2;
+    public static final int STATE_DISCONNECTING = 3;
+
+    private final IBluetooth mService;
+    private final Context mContext;
+
+    /**
+     * Create a BluetoothPan proxy object for interacting with the local
+     * Bluetooth Pan service.
+     * @param c Context
+     */
+    public BluetoothPan(Context c) {
+        mContext = c;
+
+        IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
+        if (b != null) {
+            mService = IBluetooth.Stub.asInterface(b);
+        } else {
+            Log.w(TAG, "Bluetooth Service not available!");
+
+            // Instead of throwing an exception which prevents people from going
+            // into Wireless settings in the emulator. Let it crash later
+            // when it is actually used.
+            mService = null;
+        }
+    }
+
+    /**
+     * Initiate a PAN connection.
+     *
+     * This function returns false on error and true if the connection
+     * attempt is being made.
+     *
+     * Listen for {@link #ACTION_PAN_STATE_CHANGED} to find out when the
+     * connection is completed.
+     *
+     * @param device Remote BT device.
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) log("connect(" + device + ")");
+        try {
+            return mService.connectPanDevice(device);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return false;
+        }
+    }
+
+    /**
+     * Initiate disconnect from PAN.
+     *
+     * This function return false on error and true if the disconnection
+     * attempt is being made.
+     *
+     * Listen for {@link #ACTION_PAN_STATE_CHANGED} to find out when
+     * disconnect is completed.
+     *
+     * @param device Remote BT device.
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        try {
+            return mService.disconnectPanDevice(device);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return false;
+        }
+    }
+
+    /** Get the state of a PAN Device.
+    *
+    * This function returns an int representing the state of the PAN connection
+    *
+    *  @param device Remote BT device.
+    *  @return The current state of the PAN Device
+    *  @hide
+    */
+   public int getPanDeviceState(BluetoothDevice device) {
+       if (DBG) log("getPanDeviceState(" + device + ")");
+       try {
+           return mService.getPanDeviceState(device);
+       } catch (RemoteException e) {
+           Log.e(TAG, "", e);
+           return STATE_DISCONNECTED;
+       }
+   }
+
+   /** Returns a set of all the connected PAN Devices
+   *
+   * Does not include devices that are currently connecting or disconnecting
+   *
+   * @return List of PAN devices or empty on Error
+   * @hide
+   */
+   public List<BluetoothDevice> getConnectedDevices() {
+      if (DBG) log("getConnectedDevices");
+      try {
+          return mService.getConnectedPanDevices();
+      } catch (RemoteException e) {
+          Log.e(TAG, "", e);
+          return new ArrayList<BluetoothDevice>();
+      }
+   }
+
+    private static void log(String msg) {
+        Log.d(TAG, msg);
+    }
+
+    public void setBluetoothTethering(boolean value) {
+        try {
+            mService.setBluetoothTethering(value);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+    }
+
+    public boolean isTetheringOn() {
+        try {
+            return mService.isTetheringOn();
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            return false;
+        }
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
new file mode 100644
index 0000000..3949b26
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.bluetooth;
+
+import java.util.List;
+
+/**
+ * Public APIs for the Bluetooth Profiles.
+ *
+ * <p> Clients should call {@link BluetoothAdapter#getProfileProxy},
+ * to get the Profile Proxy. Each public profile implements this
+ * interface.
+ */
+public interface BluetoothProfile {
+
+    /**
+     * Extra for the connection state intents of the individual profiles.
+     *
+     * This extra represents the current connection state of the profile of the
+     * Bluetooth device.
+     */
+    public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
+
+    /**
+     * Extra for the connection state intents of the individual profiles.
+     *
+     * This extra represents the previous connection state of the profile of the
+     * Bluetooth device.
+     */
+    public static final String EXTRA_PREVIOUS_STATE =
+        "android.bluetooth.profile.extra.PREVIOUS_STATE";
+
+    /** The profile is in disconnected state */
+    public static final int STATE_DISCONNECTED  = 0;
+    /** The profile is in connecting state */
+    public static final int STATE_CONNECTING    = 1;
+    /** The profile is in connected state */
+    public static final int STATE_CONNECTED     = 2;
+    /** The profile is in disconnecting state */
+    public static final int STATE_DISCONNECTING = 3;
+
+    /**
+     * Headset and Handsfree profile
+     */
+    public static final int HEADSET = 1;
+    /**
+     * A2DP profile.
+     */
+    public static final int A2DP = 2;
+
+    /**
+     * Default priority for devices that we try to auto-connect to and
+     * and allow incoming connections for the profile
+     * @hide
+     **/
+    public static final int PRIORITY_AUTO_CONNECT = 1000;
+
+    /**
+     *  Default priority for devices that allow incoming
+     * and outgoing connections for the profile
+     * @hide
+     **/
+    public static final int PRIORITY_ON = 100;
+
+    /**
+     * Default priority for devices that does not allow incoming
+     * connections and outgoing connections for the profile.
+     * @hide
+     **/
+    public static final int PRIORITY_OFF = 0;
+
+    /**
+     * Default priority when not set or when the device is unpaired
+     * @hide
+     * */
+    public static final int PRIORITY_UNDEFINED = -1;
+
+    /**
+     * Initiate connection to a profile of the remote bluetooth device.
+     *
+     * <p> Currently, the system supports only 1 connection to the
+     * A2DP and Headset/Handsfree profile. The API will automatically
+     * disconnect connected devices before connecting.
+     *
+     * <p> This API returns false in scenarios like the profile on the
+     * device is already connected or Bluetooth is not turned on.
+     * When this API returns true, it is guaranteed that
+     * connection state intent for the profile will be broadcasted with
+     * the state. Users can get the connection state of the profile
+     * from this intent.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on immediate error,
+     *               true otherwise
+     * @hide
+     */
+    public boolean connect(BluetoothDevice device);
+
+    /**
+     * Initiate disconnection from a profile
+     *
+     * <p> This API will return false in scenarios like the profile on the
+     * Bluetooth device is not in connected state etc. When this API returns,
+     * true, it is guaranteed that the connection state change
+     * intent will be broadcasted with the state. Users can get the
+     * disconnection state of the profile from this intent.
+     *
+     * <p> If the disconnection is initiated by a remote device, the state
+     * will transition from {@link #STATE_CONNECTED} to
+     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
+     * host (local) device the state will transition from
+     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
+     * state {@link #STATE_DISCONNECTED}. The transition to
+     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
+     * two scenarios.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on immediate error,
+     *               true otherwise
+     * @hide
+     */
+    public boolean disconnect(BluetoothDevice device);
+
+    /**
+     * Get connected devices for this specific profile.
+     *
+     * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @return List of devices. The list will be empty on error.
+     */
+    public List<BluetoothDevice> getConnectedDevices();
+
+    /**
+     * Get a set of devices that match any of the given connection
+     * states.
+     *
+     * <p> If none of devices match any of the given states,
+     * an empty set will be returned.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param states Array of states. States can be one of
+     *              {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
+     *              {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
+     * @return List of devices. The list will be empty on error.
+     */
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states);
+
+    /**
+     * Get the current connection state of the profile
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Remote bluetooth device.
+     * @return State of the profile connection. One of
+     *               {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
+     *               {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
+     */
+    public int getConnectionState(BluetoothDevice device);
+
+    /**
+     * Set priority of the profile
+     *
+     * <p> The device should already be paired.
+     *  Priority can be one of {@link #PRIORITY_ON} or
+     * {@link #PRIORITY_OFF},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     *
+     * @param device Paired bluetooth device
+     * @param priority
+     * @return true if priority is set, false on error
+     * @hide
+     */
+    public boolean setPriority(BluetoothDevice device, int priority);
+
+    /**
+     * Get the priority of the profile.
+     *
+     * <p> The priority can be any of:
+     * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+     * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param device Bluetooth device
+     * @return priority of the device
+     * @hide
+     */
+    public int getPriority(BluetoothDevice device);
+
+    /**
+     * An interface for notifying BluetoothProfile IPC clients when they have
+     * been connected or disconnected to the service.
+     */
+    public interface ServiceListener {
+        /**
+         * Called to notify the client when the proxy object has been
+         * connected to the service.
+         * @param profile - One of {@link #HEADSET} or
+         *                  {@link #A2DP}
+         * @param proxy - One of {@link BluetoothHeadset} or
+         *                {@link BluetoothA2dp}
+         */
+        public void onServiceConnected(int profile, BluetoothProfile proxy);
+
+        /**
+         * Called to notify the client that this proxy object has been
+         * disconnected from the service.
+         * @param profile - One of {@link #HEADSET} or
+         *                  {@link #A2DP}
+         */
+        public void onServiceDisconnected(int profile);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothProfileState.java b/core/java/android/bluetooth/BluetoothProfileState.java
index 686ff7c..3f36926 100644
--- a/core/java/android/bluetooth/BluetoothProfileState.java
+++ b/core/java/android/bluetooth/BluetoothProfileState.java
@@ -45,6 +45,7 @@
 
     public static final int HFP = 0;
     public static final int A2DP = 1;
+    public static final int HID = 2;
 
     static final int TRANSITION_TO_STABLE = 100;
 
@@ -58,16 +59,23 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-            if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
-                int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 0);
-                if (mProfile == HFP && (newState == BluetoothHeadset.STATE_CONNECTED ||
-                    newState == BluetoothHeadset.STATE_DISCONNECTED)) {
+
+            if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+                int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+                if (mProfile == HFP && (newState == BluetoothProfile.STATE_CONNECTED ||
+                    newState == BluetoothProfile.STATE_DISCONNECTED)) {
                     sendMessage(TRANSITION_TO_STABLE);
                 }
-            } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
-                int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
-                if (mProfile == A2DP && (newState == BluetoothA2dp.STATE_CONNECTED ||
-                    newState == BluetoothA2dp.STATE_DISCONNECTED)) {
+            } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
+                int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+                if (mProfile == A2DP && (newState == BluetoothProfile.STATE_CONNECTED ||
+                    newState == BluetoothProfile.STATE_DISCONNECTED)) {
+                    sendMessage(TRANSITION_TO_STABLE);
+                }
+            } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) {
+                int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0);
+                if (mProfile == HID && (newState == BluetoothInputDevice.STATE_CONNECTED ||
+                    newState == BluetoothInputDevice.STATE_DISCONNECTED)) {
                     sendMessage(TRANSITION_TO_STABLE);
                 }
             } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
@@ -86,8 +94,9 @@
         setInitialState(mStableState);
 
         IntentFilter filter = new IntentFilter();
-        filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
-        filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
+        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
         filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
         context.registerReceiver(mBroadcastReceiver, filter);
     }
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 4164a3d..3fbfc70 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -49,10 +49,18 @@
             ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid ObexObjectPush =
             ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
+    public static final ParcelUuid Hid =
+            ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb");
+    public static final ParcelUuid PANU =
+            ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB");
+    public static final ParcelUuid NAP =
+            ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB");
+    public static final ParcelUuid BNEP =
+            ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB");
 
     public static final ParcelUuid[] RESERVED_UUIDS = {
         AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
-        ObexObjectPush};
+        ObexObjectPush, PANU, NAP};
 
     public static boolean isAudioSource(ParcelUuid uuid) {
         return uuid.equals(AudioSource);
@@ -82,6 +90,21 @@
         return uuid.equals(AvrcpTarget);
     }
 
+    public static boolean isInputDevice(ParcelUuid uuid) {
+        return uuid.equals(Hid);
+    }
+
+    public static boolean isPanu(ParcelUuid uuid) {
+        return uuid.equals(PANU);
+    }
+
+    public static boolean isNap(ParcelUuid uuid) {
+        return uuid.equals(NAP);
+    }
+
+    public static boolean isBnep(ParcelUuid uuid) {
+        return uuid.equals(BNEP);
+    }
     /**
      * Returns true if ParcelUuid is present in uuidArray
      *
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 6dd8dd6..aefb3f2 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -17,6 +17,7 @@
 package android.bluetooth;
 
 import android.bluetooth.IBluetoothCallback;
+import android.bluetooth.BluetoothDevice;
 import android.os.ParcelUuid;
 
 /**
@@ -46,6 +47,8 @@
     boolean isDiscovering();
     byte[] readOutOfBandData();
 
+    int getAdapterConnectionState();
+
     boolean createBond(in String address);
     boolean createBondOutOfBand(in String address, in byte[] hash, in byte[] randomizer);
     boolean cancelBondProcess(in String address);
@@ -76,4 +79,21 @@
     boolean connectHeadset(String address);
     boolean disconnectHeadset(String address);
     boolean notifyIncomingConnection(String address);
+
+    // HID profile APIs
+    boolean connectInputDevice(in BluetoothDevice device);
+    boolean disconnectInputDevice(in BluetoothDevice device);
+    List<BluetoothDevice> getConnectedInputDevices();
+    int getInputDeviceState(in BluetoothDevice device);
+    boolean setInputDevicePriority(in BluetoothDevice device, int priority);
+    int getInputDevicePriority(in BluetoothDevice device);
+
+    boolean isTetheringOn();
+    void setBluetoothTethering(boolean value);
+    int getPanDeviceState(in BluetoothDevice device);
+    List<BluetoothDevice> getConnectedPanDevices();
+    boolean connectPanDevice(in BluetoothDevice device);
+    boolean disconnectPanDevice(in BluetoothDevice device);
+
+    void sendConnectionStateChange(in BluetoothDevice device, int state, int prevState);
 }
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index 40f1058..b4fc366 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -19,21 +19,24 @@
 import android.bluetooth.BluetoothDevice;
 
 /**
- * System private API for Bluetooth A2DP service
+ * APIs for Bluetooth A2DP service
  *
- * {@hide}
+ * @hide
  */
 interface IBluetoothA2dp {
-    boolean connectSink(in BluetoothDevice device);
-    boolean disconnectSink(in BluetoothDevice device);
+    // Public API
+    boolean connect(in BluetoothDevice device);
+    boolean disconnect(in BluetoothDevice device);
+    List<BluetoothDevice> getConnectedDevices();
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
+    boolean setPriority(in BluetoothDevice device, int priority);
+    int getPriority(in BluetoothDevice device);
+    boolean isA2dpPlaying(in BluetoothDevice device);
+
+    // Internal APIs
     boolean suspendSink(in BluetoothDevice device);
     boolean resumeSink(in BluetoothDevice device);
-    BluetoothDevice[] getConnectedSinks();  // change to Set<> once AIDL supports
-    BluetoothDevice[] getNonDisconnectedSinks();  // change to Set<> once AIDL supports
-    int getSinkState(in BluetoothDevice device);
-    boolean setSinkPriority(in BluetoothDevice device, int priority);
-    int getSinkPriority(in BluetoothDevice device);
-
     boolean connectSinkInternal(in BluetoothDevice device);
     boolean disconnectSinkInternal(in BluetoothDevice device);
 }
diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl
index d96f0ca..ab07931 100644
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -19,25 +19,32 @@
 import android.bluetooth.BluetoothDevice;
 
 /**
- * System private API for Bluetooth Headset service
+ * API for Bluetooth Headset service
  *
  * {@hide}
  */
 interface IBluetoothHeadset {
-    int getState(in BluetoothDevice device);
-    BluetoothDevice getCurrentHeadset();
-    boolean connectHeadset(in BluetoothDevice device);
-    void disconnectHeadset(in BluetoothDevice device);
-    boolean isConnected(in BluetoothDevice device);
-    boolean startVoiceRecognition();
-    boolean stopVoiceRecognition();
+    // Public API
+    boolean connect(in BluetoothDevice device);
+    boolean disconnect(in BluetoothDevice device);
+    List<BluetoothDevice> getConnectedDevices();
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
     boolean setPriority(in BluetoothDevice device, int priority);
     int getPriority(in BluetoothDevice device);
-    int getBatteryUsageHint();
+    boolean startVoiceRecognition(in BluetoothDevice device);
+    boolean stopVoiceRecognition(in BluetoothDevice device);
+    boolean isAudioConnected(in BluetoothDevice device);
 
+    // APIs that can be made public in future
+    int getBatteryUsageHint(in BluetoothDevice device);
+
+    // Internal functions, not be made public
     boolean createIncomingConnect(in BluetoothDevice device);
     boolean acceptIncomingConnect(in BluetoothDevice device);
     boolean cancelConnectThread();
     boolean connectHeadsetInternal(in BluetoothDevice device);
     boolean disconnectHeadsetInternal(in BluetoothDevice device);
+    boolean setAudioState(in BluetoothDevice device, int state);
+    int getAudioState(in BluetoothDevice device);
 }
diff --git a/core/java/android/bluetooth/ScoSocket.java b/core/java/android/bluetooth/ScoSocket.java
deleted file mode 100644
index b65a99a..0000000
--- a/core/java/android/bluetooth/ScoSocket.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.os.Handler;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.util.Log;
-
-/**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- *
- * Simple SCO Socket.
- * Currently in Android, there is no support for sending data over a SCO
- * socket - this is managed by the hardware link to the Bluetooth Chip. This
- * class is instead intended for management of the SCO socket lifetime, 
- * and is tailored for use with the headset / handsfree profiles.
- * @hide
- */
-public class ScoSocket {
-    private static final String TAG = "ScoSocket";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;  // even more logging
-
-    public static final int STATE_READY = 1;    // Ready for use. No threads or sockets
-    public static final int STATE_ACCEPT = 2;   // accept() thread running
-    public static final int STATE_CONNECTING = 3;  // connect() thread running
-    public static final int STATE_CONNECTED = 4;   // connected, waiting for close()
-    public static final int STATE_CLOSED = 5;   // was connected, now closed.
-
-    private int mState;
-    private int mNativeData;
-    private Handler mHandler;
-    private int mAcceptedCode;
-    private int mConnectedCode;
-    private int mClosedCode;
-
-    private WakeLock mWakeLock;  // held while in STATE_CONNECTING
-
-    static {
-        classInitNative();
-    }
-    private native static void classInitNative();
-
-    public ScoSocket(PowerManager pm, Handler handler, int acceptedCode, int connectedCode,
-                     int closedCode) {
-        initNative();
-        mState = STATE_READY;
-        mHandler = handler;
-        mAcceptedCode = acceptedCode;
-        mConnectedCode = connectedCode;
-        mClosedCode = closedCode;
-        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ScoSocket");
-        mWakeLock.setReferenceCounted(false);
-        if (VDBG) log(this + " SCO OBJECT CTOR");
-    }
-    private native void initNative();
-
-    protected void finalize() throws Throwable {
-        try {
-            if (VDBG) log(this + " SCO OBJECT DTOR");
-            destroyNative();
-            releaseWakeLockNow();
-        } finally {
-            super.finalize();
-        }
-    }
-    private native void destroyNative();
-
-    /** Connect this SCO socket to the given BT address.
-     *  Does not block.
-     */
-    public synchronized boolean connect(String address, String name) {
-        if (DBG) log("connect() " + this);
-        if (mState != STATE_READY) {
-            if (DBG) log("connect(): Bad state");
-            return false;
-        }
-        acquireWakeLock();
-        if (connectNative(address, name)) {
-            mState = STATE_CONNECTING;
-            return true;
-        } else {
-            mState = STATE_CLOSED;
-            releaseWakeLockNow();
-            return false;
-        }
-    }
-    private native boolean connectNative(String address, String name);
-
-    /** Accept incoming SCO connections.
-     *  Does not block.
-     */
-    public synchronized boolean accept() {
-        if (VDBG) log("accept() " + this);
-        if (mState != STATE_READY) {
-            if (DBG) log("Bad state");
-            return false;
-        }
-        if (acceptNative()) {
-            mState = STATE_ACCEPT;
-            return true;
-        } else {
-            mState = STATE_CLOSED;
-            return false;
-        }
-    }
-    private native boolean acceptNative();
-
-    public synchronized void close() {
-        if (DBG) log(this + " SCO OBJECT close() mState = " + mState);
-        acquireWakeLock();
-        mState = STATE_CLOSED;
-        closeNative();
-        releaseWakeLock();
-    }
-    private native void closeNative();
-
-    public synchronized int getState() {
-        return mState;
-    }
-
-    private synchronized void onConnected(int result) {
-        if (VDBG) log(this + " onConnected() mState = " + mState + " " + this);
-        if (mState != STATE_CONNECTING) {
-            if (DBG) log("Strange state, closing " + mState + " " + this);
-            return;
-        }
-        if (result >= 0) {
-            mState = STATE_CONNECTED;
-        } else {
-            mState = STATE_CLOSED;
-        }
-        mHandler.obtainMessage(mConnectedCode, mState, -1, this).sendToTarget();
-        releaseWakeLockNow();
-    }
-
-    private synchronized void onAccepted(int result) {
-        if (VDBG) log("onAccepted() " + this);
-        if (mState != STATE_ACCEPT) {
-            if (DBG) log("Strange state " + this);
-            return;
-        }
-        if (result >= 0) {
-            mState = STATE_CONNECTED;
-        } else {
-            mState = STATE_CLOSED;
-        }
-        mHandler.obtainMessage(mAcceptedCode, mState, -1, this).sendToTarget();
-    }
-
-    private synchronized void onClosed() {
-        if (DBG) log("onClosed() " + this);
-        if (mState != STATE_CLOSED) {
-            mState = STATE_CLOSED;
-            mHandler.obtainMessage(mClosedCode, mState, -1, this).sendToTarget();
-            releaseWakeLock();
-        }
-    }
-
-    private void acquireWakeLock() {
-        if (!mWakeLock.isHeld()) {
-            mWakeLock.acquire();
-            if (VDBG) log("mWakeLock.acquire() " + this);
-        }
-    }
-
-    private void releaseWakeLock() {
-        if (mWakeLock.isHeld()) {
-            // Keep apps processor awake for a further 2 seconds.
-            // This is a hack to resolve issue http://b/1616263 - in which
-            // we are left in a 80 mA power state when remotely terminating a
-            // call while connected to BT headset "HTC BH S100 " with A2DP and
-            // HFP profiles.
-            if (VDBG) log("mWakeLock.release() in 2 sec" + this);
-            mWakeLock.acquire(2000);
-        }
-    }
-
-    private void releaseWakeLockNow() {
-        if (mWakeLock.isHeld()) {
-            if (VDBG) log("mWakeLock.release() now" + this);
-            mWakeLock.release();
-        }
-    }
-
-    private void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java
new file mode 100644
index 0000000..b19c072
--- /dev/null
+++ b/core/java/android/content/AsyncTaskLoader.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.AsyncTask;
+
+/**
+ * Abstract Loader that provides an {@link AsyncTask} to do the work.
+ *
+ * @param <D> the data type to be loaded.
+ */
+public abstract class AsyncTaskLoader<D> extends Loader<D> {
+    final class LoadTask extends AsyncTask<Void, Void, D> {
+
+        private D result;
+
+        /* Runs on a worker thread */
+        @Override
+        protected D doInBackground(Void... params) {
+            result = AsyncTaskLoader.this.loadInBackground();
+            return result;
+        }
+
+        /* Runs on the UI thread */
+        @Override
+        protected void onPostExecute(D data) {
+            AsyncTaskLoader.this.dispatchOnLoadComplete(data);
+        }
+
+        @Override
+        protected void onCancelled() {
+            AsyncTaskLoader.this.onCancelled(result);
+        }
+    }
+
+    LoadTask mTask;
+
+    public AsyncTaskLoader(Context context) {
+        super(context);
+    }
+
+    /**
+     * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
+     * loaded data set and load a new one.
+     */
+    @Override
+    public void forceLoad() {
+        cancelLoad();
+        mTask = new LoadTask();
+        mTask.execute((Void[]) null);
+    }
+
+    /**
+     * Attempt to cancel the current load task. See {@link AsyncTask#cancel(boolean)}
+     * for more info.
+     *
+     * @return <tt>false</tt> if the task could not be canceled,
+     *         typically because it has already completed normally, or
+     *         because {@link #startLoading()} hasn't been called, and
+     *         <tt>true</tt> otherwise
+     */
+    public boolean cancelLoad() {
+        if (mTask != null) {
+            boolean cancelled = mTask.cancel(false);
+            mTask = null;
+            return cancelled;
+        }
+        return false;
+    }
+
+    /**
+     * Called if the task was canceled before it was completed.  Gives the class a chance
+     * to properly dispose of the result.
+     */
+    public void onCancelled(D data) {
+    }
+
+    void dispatchOnLoadComplete(D data) {
+        mTask = null;
+        deliverResult(data);
+    }
+
+    /**
+     * Called on a worker thread to perform the actual load. Implementations should not deliver the
+     * results directly, but should return them from this method, which will eventually end up
+     * calling deliverResult on the UI thread. If implementations need to process
+     * the results on the UI thread they may override deliverResult and do so
+     * there.
+     *
+     * @return the result of the load
+     */
+    public abstract D loadInBackground();
+}
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index b63d026..5939643 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -17,11 +17,14 @@
 package android.content;
 
 import android.app.ActivityManagerNative;
+import android.app.ActivityThread;
 import android.app.IActivityManager;
+import android.app.QueuedWork;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.Slog;
 
 /**
  * Base class for code that will receive intents sent by sendBroadcast().
@@ -160,6 +163,226 @@
  * the containing process active for the entire time of your operation.
  */
 public abstract class BroadcastReceiver {
+    private PendingResult mPendingResult;
+    private boolean mDebugUnregister;
+    
+    /**
+     * State for a result that is pending for a broadcast receiver.  Returned
+     * by {@link BroadcastReceiver#goAsync() goAsync()}
+     * while in {@link BroadcastReceiver#onReceive BroadcastReceiver.onReceive()}.
+     */
+    public static class PendingResult {
+        /** @hide */
+        public static final int TYPE_COMPONENT = 0;
+        /** @hide */
+        public static final int TYPE_REGISTERED = 1;
+        /** @hide */
+        public static final int TYPE_UNREGISTERED = 2;
+        
+        final int mType;
+        final boolean mOrderedHint;
+        final boolean mInitialStickyHint;
+        final IBinder mToken;
+        
+        int mResultCode;
+        String mResultData;
+        Bundle mResultExtras;
+        boolean mAbortBroadcast;
+        boolean mFinished;
+        
+        /** @hide */
+        public PendingResult(int resultCode, String resultData, Bundle resultExtras,
+                int type, boolean ordered, boolean sticky, IBinder token) {
+            mResultCode = resultCode;
+            mResultData = resultData;
+            mResultExtras = resultExtras;
+            mType = type;
+            mOrderedHint = ordered;
+            mInitialStickyHint = sticky;
+            mToken = token;
+        }
+        
+        /**
+         * Version of {@link BroadcastReceiver#setResultCode(int)
+         * BroadcastReceiver.setResultCode(int)} for
+         * asynchronous broadcast handling.
+         */
+        public final void setResultCode(int code) {
+            checkSynchronousHint();
+            mResultCode = code;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#getResultCode()
+         * BroadcastReceiver.getResultCode()} for
+         * asynchronous broadcast handling.
+         */
+        public final int getResultCode() {
+            return mResultCode;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#setResultData(String)
+         * BroadcastReceiver.setResultData(String)} for
+         * asynchronous broadcast handling.
+         */
+        public final void setResultData(String data) {
+            checkSynchronousHint();
+            mResultData = data;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#getResultData()
+         * BroadcastReceiver.getResultData()} for
+         * asynchronous broadcast handling.
+         */
+        public final String getResultData() {
+            return mResultData;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#setResultExtras(Bundle)
+         * BroadcastReceiver.setResultExtras(Bundle)} for
+         * asynchronous broadcast handling.
+         */
+        public final void setResultExtras(Bundle extras) {
+            checkSynchronousHint();
+            mResultExtras = extras;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#getResultExtras(boolean)
+         * BroadcastReceiver.getResultExtras(boolean)} for
+         * asynchronous broadcast handling.
+         */
+        public final Bundle getResultExtras(boolean makeMap) {
+            Bundle e = mResultExtras;
+            if (!makeMap) return e;
+            if (e == null) mResultExtras = e = new Bundle();
+            return e;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#setResult(int, String, Bundle)
+         * BroadcastReceiver.setResult(int, String, Bundle)} for
+         * asynchronous broadcast handling.
+         */
+        public final void setResult(int code, String data, Bundle extras) {
+            checkSynchronousHint();
+            mResultCode = code;
+            mResultData = data;
+            mResultExtras = extras;
+        }
+     
+        /**
+         * Version of {@link BroadcastReceiver#getAbortBroadcast()
+         * BroadcastReceiver.getAbortBroadcast()} for
+         * asynchronous broadcast handling.
+         */
+        public final boolean getAbortBroadcast() {
+            return mAbortBroadcast;
+        }
+
+        /**
+         * Version of {@link BroadcastReceiver#abortBroadcast()
+         * BroadcastReceiver.abortBroadcast()} for
+         * asynchronous broadcast handling.
+         */
+        public final void abortBroadcast() {
+            checkSynchronousHint();
+            mAbortBroadcast = true;
+        }
+        
+        /**
+         * Version of {@link BroadcastReceiver#clearAbortBroadcast()
+         * BroadcastReceiver.clearAbortBroadcast()} for
+         * asynchronous broadcast handling.
+         */
+        public final void clearAbortBroadcast() {
+            mAbortBroadcast = false;
+        }
+        
+        /**
+         * Finish the broadcast.  The current result will be sent and the
+         * next broadcast will proceed.
+         */
+        public final void finish() {
+            if (mType == TYPE_COMPONENT) {
+                final IActivityManager mgr = ActivityManagerNative.getDefault();
+                if (QueuedWork.hasPendingWork()) {
+                    // If this is a broadcast component, we need to make sure any
+                    // queued work is complete before telling AM we are done, so
+                    // we don't have our process killed before that.  We now know
+                    // there is pending work; put another piece of work at the end
+                    // of the list to finish the broadcast, so we don't block this
+                    // thread (which may be the main thread) to have it finished.
+                    //
+                    // Note that we don't need to use QueuedWork.add() with the
+                    // runnable, since we know the AM is waiting for us until the
+                    // executor gets to it.
+                    QueuedWork.singleThreadExecutor().execute( new Runnable() {
+                        @Override public void run() {
+                            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+                                    "Finishing broadcast after work to component " + mToken);
+                            sendFinished(mgr);
+                        }
+                    });
+                } else {
+                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+                            "Finishing broadcast to component " + mToken);
+                    sendFinished(mgr);
+                }
+            } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
+                if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+                        "Finishing broadcast to " + mToken);
+                final IActivityManager mgr = ActivityManagerNative.getDefault();
+                sendFinished(mgr);
+            }
+        }
+        
+        /** @hide */
+        public void setExtrasClassLoader(ClassLoader cl) {
+            if (mResultExtras != null) {
+                mResultExtras.setClassLoader(cl);
+            }
+        }
+        
+        /** @hide */
+        public void sendFinished(IActivityManager am) {
+            synchronized (this) {
+                if (mFinished) {
+                    throw new IllegalStateException("Broadcast already finished");
+                }
+                mFinished = true;
+            
+                try {
+                    if (mOrderedHint) {
+                        am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
+                                mAbortBroadcast);
+                    } else {
+                        // This broadcast was sent to a component; it is not ordered,
+                        // but we still need to tell the activity manager we are done.
+                        am.finishReceiver(mToken, 0, null, null, false);
+                    }
+                } catch (RemoteException ex) {
+                }
+            }
+        }
+        
+        void checkSynchronousHint() {
+            // Note that we don't assert when receiving the initial sticky value,
+            // since that may have come from an ordered broadcast.  We'll catch
+            // them later when the real broadcast happens again.
+            if (mOrderedHint || mInitialStickyHint) {
+                return;
+            }
+            RuntimeException e = new RuntimeException(
+                    "BroadcastReceiver trying to return result during a non-ordered broadcast");
+            e.fillInStackTrace();
+            Log.e("BroadcastReceiver", e.getMessage(), e);
+        }
+    }
+    
     public BroadcastReceiver() {
     }
 
@@ -197,6 +420,26 @@
     public abstract void onReceive(Context context, Intent intent);
 
     /**
+     * This can be called by an application in {@link #onReceive} to allow
+     * it to keep the broadcast active after returning from that function.
+     * This does <em>not</em> change the expectation of being relatively
+     * responsive to the broadcast (finishing it within 10s), but does allow
+     * the implementation to move work related to it over to another thread
+     * to avoid glitching the main UI thread due to disk IO.
+     * 
+     * @return Returns a {@link PendingResult} representing the result of
+     * the active broadcast.  The BroadcastRecord itself is no longer active;
+     * all data and other interaction must go through {@link PendingResult}
+     * APIs.  The {@link PendingResult#finish PendingResult.finish()} method
+     * must be called once processing of the broadcast is done.
+     */
+    public final PendingResult goAsync() {
+        PendingResult res = mPendingResult;
+        mPendingResult = null;
+        return res;
+    }
+    
+    /**
      * Provide a binder to an already-running service.  This method is synchronous
      * and will not start the target service if it is not present, so it is safe
      * to call from {@link #onReceive}.
@@ -225,9 +468,9 @@
      * {@link android.app.Activity#RESULT_OK} constants, though the
      * actual meaning of this value is ultimately up to the broadcaster.
      * 
-     * <p><strong>This method does not work with non-ordered broadcasts such
+     * <p class="note">This method does not work with non-ordered broadcasts such
      * as those sent with {@link Context#sendBroadcast(Intent)
-     * Context.sendBroadcast}</strong></p>
+     * Context.sendBroadcast}</p>
      * 
      * @param code The new result code.
      * 
@@ -235,7 +478,7 @@
      */
     public final void setResultCode(int code) {
         checkSynchronousHint();
-        mResultCode = code;
+        mPendingResult.mResultCode = code;
     }
 
     /**
@@ -244,7 +487,7 @@
      * @return int The current result code.
      */
     public final int getResultCode() {
-        return mResultCode;
+        return mPendingResult != null ? mPendingResult.mResultCode : 0;
     }
 
     /**
@@ -264,7 +507,7 @@
      */
     public final void setResultData(String data) {
         checkSynchronousHint();
-        mResultData = data;
+        mPendingResult.mResultData = data;
     }
 
     /**
@@ -274,7 +517,7 @@
      * @return String The current result data; may be null.
      */
     public final String getResultData() {
-        return mResultData;
+        return mPendingResult != null ? mPendingResult.mResultData : null;
     }
 
     /**
@@ -296,7 +539,7 @@
      */
     public final void setResultExtras(Bundle extras) {
         checkSynchronousHint();
-        mResultExtras = extras;
+        mPendingResult.mResultExtras = extras;
     }
 
     /**
@@ -311,9 +554,12 @@
      * @return Map The current extras map.
      */
     public final Bundle getResultExtras(boolean makeMap) {
-        Bundle e = mResultExtras;
+        if (mPendingResult == null) {
+            return null;
+        }
+        Bundle e = mPendingResult.mResultExtras;
         if (!makeMap) return e;
-        if (e == null) mResultExtras = e = new Bundle();
+        if (e == null) mPendingResult.mResultExtras = e = new Bundle();
         return e;
     }
 
@@ -341,9 +587,9 @@
      */
     public final void setResult(int code, String data, Bundle extras) {
         checkSynchronousHint();
-        mResultCode = code;
-        mResultData = data;
-        mResultExtras = extras;
+        mPendingResult.mResultCode = code;
+        mPendingResult.mResultData = data;
+        mPendingResult.mResultExtras = extras;
     }
  
     /**
@@ -353,7 +599,7 @@
      * @return True if the broadcast should be aborted.
      */
     public final boolean getAbortBroadcast() {
-        return mAbortBroadcast;
+        return mPendingResult != null ? mPendingResult.mAbortBroadcast : false;
     }
 
     /**
@@ -372,7 +618,7 @@
      */
     public final void abortBroadcast() {
         checkSynchronousHint();
-        mAbortBroadcast = true;
+        mPendingResult.mAbortBroadcast = true;
     }
     
     /**
@@ -380,7 +626,9 @@
      * broadcast.
      */
     public final void clearAbortBroadcast() {
-        mAbortBroadcast = false;
+        if (mPendingResult != null) {
+            mPendingResult.mAbortBroadcast = false;
+        }
     }
     
     /**
@@ -388,7 +636,7 @@
      * broadcast.
      */
     public final boolean isOrderedBroadcast() {
-        return mOrderedHint;
+        return mPendingResult != null ? mPendingResult.mOrderedHint : false;
     }
     
     /**
@@ -398,7 +646,7 @@
      * not directly the result of a broadcast right now.
      */
     public final boolean isInitialStickyBroadcast() {
-        return mInitialStickyHint;
+        return mPendingResult != null ? mPendingResult.mInitialStickyHint : false;
     }
     
     /**
@@ -406,15 +654,21 @@
      * running in ordered mode.
      */
     public final void setOrderedHint(boolean isOrdered) {
-        mOrderedHint = isOrdered;
+        // Accidentally left in the SDK.
     }
     
     /**
-     * For internal use, sets the hint about whether this BroadcastReceiver is
-     * receiving the initial sticky broadcast value. @hide
+     * For internal use to set the result data that is active. @hide
      */
-    public final void setInitialStickyHint(boolean isInitialSticky) {
-        mInitialStickyHint = isInitialSticky;
+    public final void setPendingResult(PendingResult result) {
+        mPendingResult = result;
+    }
+    
+    /**
+     * For internal use to set the result data that is active. @hide
+     */
+    public final PendingResult getPendingResult() {
+        return mPendingResult;
     }
     
     /**
@@ -440,10 +694,14 @@
     }
     
     void checkSynchronousHint() {
+        if (mPendingResult == null) {
+            throw new IllegalStateException("Call while result is not pending");
+        }
+        
         // Note that we don't assert when receiving the initial sticky value,
         // since that may have come from an ordered broadcast.  We'll catch
         // them later when the real broadcast happens again.
-        if (mOrderedHint || mInitialStickyHint) {
+        if (mPendingResult.mOrderedHint || mPendingResult.mInitialStickyHint) {
             return;
         }
         RuntimeException e = new RuntimeException(
@@ -451,13 +709,5 @@
         e.fillInStackTrace();
         Log.e("BroadcastReceiver", e.getMessage(), e);
     }
-    
-    private int mResultCode;
-    private String mResultData;
-    private Bundle mResultExtras;
-    private boolean mAbortBroadcast;
-    private boolean mDebugUnregister;
-    private boolean mOrderedHint;
-    private boolean mInitialStickyHint;
 }
 
diff --git a/core/java/android/content/ClipData.aidl b/core/java/android/content/ClipData.aidl
new file mode 100644
index 0000000..5fc12ce
--- /dev/null
+++ b/core/java/android/content/ClipData.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+parcelable ClipData;
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
new file mode 100644
index 0000000..6f4d098
--- /dev/null
+++ b/core/java/android/content/ClipData.java
@@ -0,0 +1,528 @@
+/**
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+/**
+ * Representation of a clipped data on the clipboard.
+ *
+ * <p>ClippedData is a complex type containing one or Item instances,
+ * each of which can hold one or more representations of an item of data.
+ * For display to the user, it also has a label and iconic representation.</p>
+ *
+ * <p>A ClipData contains a {@link ClipDescription}, which describes
+ * important meta-data about the clip.  In particular, its
+ * {@link ClipDescription#getMimeType(int) getDescription().getMimeType(int)}
+ * must return correct MIME type(s) describing the data in the clip.  For help
+ * in correctly constructing a clip with the correct MIME type, use
+ * {@link #newPlainText(CharSequence, Bitmap, CharSequence)},
+ * {@link #newUri(ContentResolver, CharSequence, Bitmap, Uri)}, and
+ * {@link #newIntent(CharSequence, Bitmap, Intent)}.
+ *
+ * <p>Each Item instance can be one of three main classes of data: a simple
+ * CharSequence of text, a single Intent object, or a Uri.  See {@link Item}
+ * for more details.
+ *
+ * <a name="ImplementingPaste"></a>
+ * <h3>Implementing Paste or Drop</h3>
+ *
+ * <p>To implement a paste or drop of a ClippedData object into an application,
+ * the application must correctly interpret the data for its use.  If the {@link Item}
+ * it contains is simple text or an Intent, there is little to be done: text
+ * can only be interpreted as text, and an Intent will typically be used for
+ * creating shortcuts (such as placing icons on the home screen) or other
+ * actions.
+ *
+ * <p>If all you want is the textual representation of the clipped data, you
+ * can use the convenience method {@link Item#coerceToText Item.coerceToText}.
+ * In this case there is generally no need to worry about the MIME types
+ * reported by {@link ClipDescription#getMimeType(int) getDescription().getMimeType(int)},
+ * since any clip item an always be converted to a string.
+ *
+ * <p>More complicated exchanges will be done through URIs, in particular
+ * "content:" URIs.  A content URI allows the recipient of a ClippedData item
+ * to interact closely with the ContentProvider holding the data in order to
+ * negotiate the transfer of that data.  The clip must also be filled in with
+ * the available MIME types; {@link #newUri(ContentResolver, CharSequence, Bitmap, Uri)}
+ * will take care of correctly doing this.
+ *
+ * <p>For example, here is the paste function of a simple NotePad application.
+ * When retrieving the data from the clipboard, it can do either two things:
+ * if the clipboard contains a URI reference to an existing note, it copies
+ * the entire structure of the note into a new note; otherwise, it simply
+ * coerces the clip into text and uses that as the new note's contents.
+ *
+ * {@sample development/samples/NotePad/src/com/example/android/notepad/NoteEditor.java
+ *      paste}
+ *
+ * <p>In many cases an application can paste various types of streams of data.  For
+ * example, an e-mail application may want to allow the user to paste an image
+ * or other binary data as an attachment.  This is accomplished through the
+ * ContentResolver {@link ContentResolver#getStreamTypes(Uri, String)} and
+ * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, android.os.Bundle)}
+ * methods.  These allow a client to discover the type(s) of data that a particular
+ * content URI can make available as a stream and retrieve the stream of data.
+ *
+ * <p>For example, the implementation of {@link Item#coerceToText Item.coerceToText}
+ * itself uses this to try to retrieve a URI clip as a stream of text:
+ *
+ * {@sample frameworks/base/core/java/android/content/ClipData.java coerceToText}
+ *
+ * <a name="ImplementingCopy"></a>
+ * <h3>Implementing Copy or Drag</h3>
+ *
+ * <p>To be the source of a clip, the application must construct a ClippedData
+ * object that any recipient can interpret best for their context.  If the clip
+ * is to contain a simple text, Intent, or URI, this is easy: an {@link Item}
+ * containing the appropriate data type can be constructed and used.
+ *
+ * <p>More complicated data types require the implementation of support in
+ * a ContentProvider for describing and generating the data for the recipient.
+ * A common scenario is one where an application places on the clipboard the
+ * content: URI of an object that the user has copied, with the data at that
+ * URI consisting of a complicated structure that only other applications with
+ * direct knowledge of the structure can use.
+ *
+ * <p>For applications that do not have intrinsic knowledge of the data structure,
+ * the content provider holding it can make the data available as an arbitrary
+ * number of types of data streams.  This is done by implementing the
+ * ContentProvider {@link ContentProvider#getStreamTypes(Uri, String)} and
+ * {@link ContentProvider#openTypedAssetFile(Uri, String, android.os.Bundle)}
+ * methods.
+ *
+ * <p>Going back to our simple NotePad application, this is the implementation
+ * it may have to convert a single note URI (consisting of a title and the note
+ * text) into a stream of plain text data.
+ *
+ * {@sample development/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java
+ *      stream}
+ *
+ * <p>The copy operation in our NotePad application is now just a simple matter
+ * of making a clip containing the URI of the note being copied:
+ *
+ * {@sample development/samples/NotePad/src/com/example/android/notepad/NotesList.java
+ *      copy}
+ *
+ * <p>Note if a paste operation needs this clip as text (for example to paste
+ * into an editor), then {@link Item#coerceToText(Context)} will ask the content
+ * provider for the clip URI as text and successfully paste the entire note.
+ */
+public class ClipData implements Parcelable {
+    static final String[] MIMETYPES_TEXT_PLAIN = new String[] {
+        ClipDescription.MIMETYPE_TEXT_PLAIN };
+    static final String[] MIMETYPES_TEXT_URILIST = new String[] {
+        ClipDescription.MIMETYPE_TEXT_URILIST };
+    static final String[] MIMETYPES_TEXT_INTENT = new String[] {
+        ClipDescription.MIMETYPE_TEXT_INTENT };
+
+    final ClipDescription mClipDescription;
+    
+    final Bitmap mIcon;
+
+    final ArrayList<Item> mItems = new ArrayList<Item>();
+
+    /**
+     * Description of a single item in a ClippedData.
+     *
+     * <p>The types than an individual item can currently contain are:</p>
+     *
+     * <ul>
+     * <li> Text: a basic string of text.  This is actually a CharSequence,
+     * so it can be formatted text supported by corresponding Android built-in
+     * style spans.  (Custom application spans are not supported and will be
+     * stripped when transporting through the clipboard.)
+     * <li> Intent: an arbitrary Intent object.  A typical use is the shortcut
+     * to create when pasting a clipped item on to the home screen.
+     * <li> Uri: a URI reference.  This may be any URI (such as an http: URI
+     * representing a bookmark), however it is often a content: URI.  Using
+     * content provider references as clips like this allows an application to
+     * share complex or large clips through the standard content provider
+     * facilities.
+     * </ul>
+     */
+    public static class Item {
+        final CharSequence mText;
+        final Intent mIntent;
+        final Uri mUri;
+
+        /**
+         * Create an Item consisting of a single block of (possibly styled) text.
+         */
+        public Item(CharSequence text) {
+            mText = text;
+            mIntent = null;
+            mUri = null;
+        }
+
+        /**
+         * Create an Item consisting of an arbitrary Intent.
+         */
+        public Item(Intent intent) {
+            mText = null;
+            mIntent = intent;
+            mUri = null;
+        }
+
+        /**
+         * Create an Item consisting of an arbitrary URI.
+         */
+        public Item(Uri uri) {
+            mText = null;
+            mIntent = null;
+            mUri = uri;
+        }
+
+        /**
+         * Create a complex Item, containing multiple representations of
+         * text, intent, and/or URI.
+         */
+        public Item(CharSequence text, Intent intent, Uri uri) {
+            mText = text;
+            mIntent = intent;
+            mUri = uri;
+        }
+
+        /**
+         * Retrieve the raw text contained in this Item.
+         */
+        public CharSequence getText() {
+            return mText;
+        }
+
+        /**
+         * Retrieve the raw Intent contained in this Item.
+         */
+        public Intent getIntent() {
+            return mIntent;
+        }
+
+        /**
+         * Retrieve the raw URI contained in this Item.
+         */
+        public Uri getUri() {
+            return mUri;
+        }
+
+        /**
+         * Turn this item into text, regardless of the type of data it
+         * actually contains.
+         *
+         * <p>The algorithm for deciding what text to return is:
+         * <ul>
+         * <li> If {@link #getText} is non-null, return that.
+         * <li> If {@link #getUri} is non-null, try to retrieve its data
+         * as a text stream from its content provider.  If this succeeds, copy
+         * the text into a String and return it.  If it is not a content: URI or
+         * the content provider does not supply a text representation, return
+         * the raw URI as a string.
+         * <li> If {@link #getIntent} is non-null, convert that to an intent:
+         * URI and returnit.
+         * <li> Otherwise, return an empty string.
+         * </ul>
+         *
+         * @param context The caller's Context, from which its ContentResolver
+         * and other things can be retrieved.
+         * @return Returns the item's textual representation.
+         */
+//BEGIN_INCLUDE(coerceToText)
+        public CharSequence coerceToText(Context context) {
+            // If this Item has an explicit textual value, simply return that.
+            if (mText != null) {
+                return mText;
+            }
+
+            // If this Item has a URI value, try using that.
+            if (mUri != null) {
+
+                // First see if the URI can be opened as a plain text stream
+                // (of any sub-type).  If so, this is the best textual
+                // representation for it.
+                FileInputStream stream = null;
+                try {
+                    // Ask for a stream of the desired type.
+                    AssetFileDescriptor descr = context.getContentResolver()
+                            .openTypedAssetFileDescriptor(mUri, "text/*", null);
+                    stream = descr.createInputStream();
+                    InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
+
+                    // Got it...  copy the stream into a local string and return it.
+                    StringBuilder builder = new StringBuilder(128);
+                    char[] buffer = new char[8192];
+                    int len;
+                    while ((len=reader.read(buffer)) > 0) {
+                        builder.append(buffer, 0, len);
+                    }
+                    return builder.toString();
+
+                } catch (FileNotFoundException e) {
+                    // Unable to open content URI as text...  not really an
+                    // error, just something to ignore.
+
+                } catch (IOException e) {
+                    // Something bad has happened.
+                    Log.w("ClippedData", "Failure loading text", e);
+                    return e.toString();
+
+                } finally {
+                    if (stream != null) {
+                        try {
+                            stream.close();
+                        } catch (IOException e) {
+                        }
+                    }
+                }
+
+                // If we couldn't open the URI as a stream, then the URI itself
+                // probably serves fairly well as a textual representation.
+                return mUri.toString();
+            }
+
+            // Finally, if all we have is an Intent, then we can just turn that
+            // into text.  Not the most user-friendly thing, but it's something.
+            if (mIntent != null) {
+                return mIntent.toUri(Intent.URI_INTENT_SCHEME);
+            }
+
+            // Shouldn't get here, but just in case...
+            return "";
+        }
+//END_INCLUDE(coerceToText)
+    }
+
+    /**
+     * Create a new clip.
+     *
+     * @param label Label to show to the user describing this clip.
+     * @param mimeTypes An array of MIME types this data is available as.
+     * @param icon Bitmap providing the user with an iconing representation of
+     * the clip.
+     * @param item The contents of the first item in the clip.
+     */
+    public ClipData(CharSequence label, String[] mimeTypes, Bitmap icon, Item item) {
+        mClipDescription = new ClipDescription(label, mimeTypes);
+        if (item == null) {
+            throw new NullPointerException("item is null");
+        }
+        mIcon = icon;
+        mItems.add(item);
+    }
+
+    /**
+     * Create a new clip.
+     *
+     * @param description The ClipDescription describing the clip contents.
+     * @param icon Bitmap providing the user with an iconing representation of
+     * the clip.
+     * @param item The contents of the first item in the clip.
+     */
+    public ClipData(ClipDescription description, Bitmap icon, Item item) {
+        mClipDescription = description;
+        if (item == null) {
+            throw new NullPointerException("item is null");
+        }
+        mIcon = icon;
+        mItems.add(item);
+    }
+
+    /**
+     * Create a new ClipData holding data of the type
+     * {@link ClipDescription#MIMETYPE_TEXT_PLAIN}.
+     *
+     * @param label User-visible label for the clip data.
+     * @param icon Iconic representation of the clip data.
+     * @param text The actual text in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newPlainText(CharSequence label, Bitmap icon, CharSequence text) {
+        Item item = new Item(text);
+        return new ClipData(label, MIMETYPES_TEXT_PLAIN, icon, item);
+    }
+
+    /**
+     * Create a new ClipData holding an Intent with MIME type
+     * {@link ClipDescription#MIMETYPE_TEXT_INTENT}.
+     *
+     * @param label User-visible label for the clip data.
+     * @param icon Iconic representation of the clip data.
+     * @param intent The actual Intent in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newIntent(CharSequence label, Bitmap icon, Intent intent) {
+        Item item = new Item(intent);
+        return new ClipData(label, MIMETYPES_TEXT_INTENT, icon, item);
+    }
+
+    /**
+     * Create a new ClipData holding a URI.  If the URI is a content: URI,
+     * this will query the content provider for the MIME type of its data and
+     * use that as the MIME type.  Otherwise, it will use the MIME type
+     * {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
+     *
+     * @param resolver ContentResolver used to get information about the URI.
+     * @param label User-visible label for the clip data.
+     * @param icon Iconic representation of the clip data.
+     * @param uri The URI in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newUri(ContentResolver resolver, CharSequence label,
+            Bitmap icon, Uri uri) {
+        Item item = new Item(uri);
+        String[] mimeTypes = null;
+        if ("content".equals(uri.getScheme())) {
+            String realType = resolver.getType(uri);
+            mimeTypes = resolver.getStreamTypes(uri, "*/*");
+            if (mimeTypes == null) {
+                if (realType != null) {
+                    mimeTypes = new String[] { realType, ClipDescription.MIMETYPE_TEXT_URILIST };
+                }
+            } else {
+                String[] tmp = new String[mimeTypes.length + (realType != null ? 2 : 1)];
+                int i = 0;
+                if (realType != null) {
+                    tmp[0] = realType;
+                    i++;
+                }
+                System.arraycopy(mimeTypes, 0, tmp, i, mimeTypes.length);
+                tmp[i + mimeTypes.length] = ClipDescription.MIMETYPE_TEXT_URILIST;
+                mimeTypes = tmp;
+            }
+        }
+        if (mimeTypes == null) {
+            mimeTypes = MIMETYPES_TEXT_URILIST;
+        }
+        return new ClipData(label, mimeTypes, icon, item);
+    }
+
+    /**
+     * Create a new ClipData holding an URI with MIME type
+     * {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
+     * Unlike {@link #newUri(ContentResolver, CharSequence, Bitmap, Uri)}, nothing
+     * is inferred about the URI -- if it is a content: URI holding a bitmap,
+     * the reported type will still be uri-list.  Use this with care!
+     *
+     * @param label User-visible label for the clip data.
+     * @param icon Iconic representation of the clip data.
+     * @param uri The URI in the clip.
+     * @return Returns a new ClipData containing the specified data.
+     */
+    static public ClipData newRawUri(CharSequence label, Bitmap icon, Uri uri) {
+        Item item = new Item(uri);
+        return new ClipData(label, MIMETYPES_TEXT_URILIST, icon, item);
+    }
+
+    /**
+     * Return the {@link ClipDescription} associated with this data, describing
+     * what it contains.
+     */
+    public ClipDescription getDescription() {
+        return mClipDescription;
+    }
+    
+    public void addItem(Item item) {
+        if (item == null) {
+            throw new NullPointerException("item is null");
+        }
+        mItems.add(item);
+    }
+
+    public Bitmap getIcon() {
+        return mIcon;
+    }
+
+    public int getItemCount() {
+        return mItems.size();
+    }
+
+    public Item getItem(int index) {
+        return mItems.get(index);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        mClipDescription.writeToParcel(dest, flags);
+        if (mIcon != null) {
+            dest.writeInt(1);
+            mIcon.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
+        final int N = mItems.size();
+        dest.writeInt(N);
+        for (int i=0; i<N; i++) {
+            Item item = mItems.get(i);
+            TextUtils.writeToParcel(item.mText, dest, flags);
+            if (item.mIntent != null) {
+                dest.writeInt(1);
+                item.mIntent.writeToParcel(dest, flags);
+            } else {
+                dest.writeInt(0);
+            }
+            if (item.mUri != null) {
+                dest.writeInt(1);
+                item.mUri.writeToParcel(dest, flags);
+            } else {
+                dest.writeInt(0);
+            }
+        }
+    }
+
+    ClipData(Parcel in) {
+        mClipDescription = new ClipDescription(in);
+        if (in.readInt() != 0) {
+            mIcon = Bitmap.CREATOR.createFromParcel(in);
+        } else {
+            mIcon = null;
+        }
+        final int N = in.readInt();
+        for (int i=0; i<N; i++) {
+            CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            Intent intent = in.readInt() != 0 ? Intent.CREATOR.createFromParcel(in) : null;
+            Uri uri = in.readInt() != 0 ? Uri.CREATOR.createFromParcel(in) : null;
+            mItems.add(new Item(text, intent, uri));
+        }
+    }
+
+    public static final Parcelable.Creator<ClipData> CREATOR =
+        new Parcelable.Creator<ClipData>() {
+
+            public ClipData createFromParcel(Parcel source) {
+                return new ClipData(source);
+            }
+
+            public ClipData[] newArray(int size) {
+                return new ClipData[size];
+            }
+        };
+}
diff --git a/core/java/android/content/ClipDescription.aidl b/core/java/android/content/ClipDescription.aidl
new file mode 100644
index 0000000..391fd5a
--- /dev/null
+++ b/core/java/android/content/ClipDescription.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+parcelable ClipDescription;
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
new file mode 100644
index 0000000..0d9d807
--- /dev/null
+++ b/core/java/android/content/ClipDescription.java
@@ -0,0 +1,206 @@
+/**
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Meta-data describing the contents of a {@link ClipData}.  Provides enough
+ * information to know if you can handle the ClipData, but not the data
+ * itself.
+ */
+public class ClipDescription implements Parcelable {
+    /**
+     * The MIME type for a clip holding plain text.
+     */
+    public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
+
+    /**
+     * The MIME type for a clip holding one or more URIs.  This should be
+     * used for URIs that are meaningful to a user (such as an http: URI).
+     * It should <em>not</em> be used for a content: URI that references some
+     * other piece of data; in that case the MIME type should be the type
+     * of the referenced data.
+     */
+    public static final String MIMETYPE_TEXT_URILIST = "text/uri-list";
+
+    /**
+     * The MIME type for a clip holding an Intent.
+     */
+    public static final String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
+
+    final CharSequence mLabel;
+    final String[] mMimeTypes;
+
+    /**
+     * Create a new clip.
+     *
+     * @param label Label to show to the user describing this clip.
+     * @param mimeTypes An array of MIME types this data is available as.
+     */
+    public ClipDescription(CharSequence label, String[] mimeTypes) {
+        if (mimeTypes == null) {
+            throw new NullPointerException("mimeTypes is null");
+        }
+        mLabel = label;
+        mMimeTypes = mimeTypes;
+    }
+
+    /**
+     * Create a copy of a ClipDescription.
+     */
+    public ClipDescription(ClipDescription o) {
+        mLabel = o.mLabel;
+        mMimeTypes = o.mMimeTypes;
+    }
+
+    /**
+     * Helper to compare two MIME types, where one may be a pattern.
+     * @param concreteType A fully-specified MIME type.
+     * @param desiredType A desired MIME type that may be a pattern such as *\/*.
+     * @return Returns true if the two MIME types match.
+     */
+    public static boolean compareMimeTypes(String concreteType, String desiredType) {
+        final int typeLength = desiredType.length();
+        if (typeLength == 3 && desiredType.equals("*/*")) {
+            return true;
+        }
+
+        final int slashpos = desiredType.indexOf('/');
+        if (slashpos > 0) {
+            if (typeLength == slashpos+2 && desiredType.charAt(slashpos+1) == '*') {
+                if (desiredType.regionMatches(0, concreteType, 0, slashpos+1)) {
+                    return true;
+                }
+            } else if (desiredType.equals(concreteType)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Return the label for this clip.
+     */
+    public CharSequence getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * Check whether the clip description contains the given MIME type.
+     *
+     * @param mimeType The desired MIME type.  May be a pattern.
+     * @return Returns true if one of the MIME types in the clip description
+     * matches the desired MIME type, else false.
+     */
+    public boolean hasMimeType(String mimeType) {
+        for (int i=0; i<mMimeTypes.length; i++) {
+            if (compareMimeTypes(mMimeTypes[i], mimeType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Filter the clip description MIME types by the given MIME type.  Returns
+     * all MIME types in the clip that match the given MIME type.
+     *
+     * @param mimeType The desired MIME type.  May be a pattern.
+     * @return Returns an array of all matching MIME types.  If there are no
+     * matching MIME types, null is returned.
+     */
+    public String[] filterMimeTypes(String mimeType) {
+        ArrayList<String> array = null;
+        for (int i=0; i<mMimeTypes.length; i++) {
+            if (compareMimeTypes(mMimeTypes[i], mimeType)) {
+                if (array == null) {
+                    array = new ArrayList<String>();
+                }
+                array.add(mMimeTypes[i]);
+            }
+        }
+        if (array == null) {
+            return null;
+        }
+        String[] rawArray = new String[array.size()];
+        array.toArray(rawArray);
+        return rawArray;
+    }
+
+    /**
+     * Return the number of MIME types the clip is available in.
+     */
+    public int getMimeTypeCount() {
+        return mMimeTypes.length;
+    }
+
+    /**
+     * Return one of the possible clip MIME types.
+     */
+    public String getMimeType(int index) {
+        return mMimeTypes[index];
+    }
+
+    /** @hide */
+    public void validate() {
+        if (mMimeTypes == null) {
+            throw new NullPointerException("null mime types");
+        }
+        if (mMimeTypes.length <= 0) {
+            throw new IllegalArgumentException("must have at least 1 mime type");
+        }
+        for (int i=0; i<mMimeTypes.length; i++) {
+            if (mMimeTypes[i] == null) {
+                throw new NullPointerException("mime type at " + i + " is null");
+            }
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        TextUtils.writeToParcel(mLabel, dest, flags);
+        dest.writeStringArray(mMimeTypes);
+    }
+
+    ClipDescription(Parcel in) {
+        mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mMimeTypes = in.createStringArray();
+    }
+
+    public static final Parcelable.Creator<ClipDescription> CREATOR =
+        new Parcelable.Creator<ClipDescription>() {
+
+            public ClipDescription createFromParcel(Parcel source) {
+                return new ClipDescription(source);
+            }
+
+            public ClipDescription[] newArray(int size) {
+                return new ClipDescription[size];
+            }
+        };
+}
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
new file mode 100644
index 0000000..85a6765
--- /dev/null
+++ b/core/java/android/content/ClipboardManager.java
@@ -0,0 +1,213 @@
+/**
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.content.Context;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Interface to the clipboard service, for placing and retrieving text in
+ * the global clipboard.
+ *
+ * <p>
+ * You do not instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService}.
+ *
+ * <p>
+ * The ClipboardManager API itself is very simple: it consists of methods
+ * to atomically get and set the current primary clipboard data.  That data
+ * is expressed as a {@link ClipData} object, which defines the protocol
+ * for data exchange between applications.
+ *
+ * @see android.content.Context#getSystemService
+ */
+public class ClipboardManager extends android.text.ClipboardManager {
+    private final static Object sStaticLock = new Object();
+    private static IClipboard sService;
+
+    private final Context mContext;
+
+    private final ArrayList<OnPrimaryClipChangedListener> mPrimaryClipChangedListeners
+             = new ArrayList<OnPrimaryClipChangedListener>();
+
+    private final IOnPrimaryClipChangedListener.Stub mPrimaryClipChangedServiceListener
+            = new IOnPrimaryClipChangedListener.Stub() {
+        public void dispatchPrimaryClipChanged() {
+            mHandler.sendEmptyMessage(MSG_REPORT_PRIMARY_CLIP_CHANGED);
+        }
+    };
+
+    static final int MSG_REPORT_PRIMARY_CLIP_CHANGED = 1;
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_REPORT_PRIMARY_CLIP_CHANGED:
+                    reportPrimaryClipChanged();
+            }
+        }
+    };
+
+    public interface OnPrimaryClipChangedListener {
+        void onPrimaryClipChanged();
+    }
+
+    static private IClipboard getService() {
+        synchronized (sStaticLock) {
+            if (sService != null) {
+                return sService;
+            }
+            IBinder b = ServiceManager.getService("clipboard");
+            sService = IClipboard.Stub.asInterface(b);
+            return sService;
+        }
+    }
+
+    /** {@hide} */
+    public ClipboardManager(Context context, Handler handler) {
+        mContext = context;
+    }
+
+    /**
+     * Sets the current primary clip on the clipboard.  This is the clip that
+     * is involved in normal cut and paste operations.
+     *
+     * @param clip The clipped data item to set.
+     */
+    public void setPrimaryClip(ClipData clip) {
+        try {
+            getService().setPrimaryClip(clip);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Returns the current primary clip on the clipboard.
+     */
+    public ClipData getPrimaryClip() {
+        try {
+            return getService().getPrimaryClip(mContext.getPackageName());
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns a description of the current primary clip on the clipboard
+     * but not a copy of its data.
+     */
+    public ClipDescription getPrimaryClipDescription() {
+        try {
+            return getService().getPrimaryClipDescription();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Returns true if there is currently a primary clip on the clipboard.
+     */
+    public boolean hasPrimaryClip() {
+        try {
+            return getService().hasPrimaryClip();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    public void addPrimaryClipChangedListener(OnPrimaryClipChangedListener what) {
+        synchronized (mPrimaryClipChangedListeners) {
+            if (mPrimaryClipChangedListeners.size() == 0) {
+                try {
+                    getService().addPrimaryClipChangedListener(
+                            mPrimaryClipChangedServiceListener);
+                } catch (RemoteException e) {
+                }
+            }
+            mPrimaryClipChangedListeners.add(what);
+        }
+    }
+
+    public void removePrimaryClipChangedListener(OnPrimaryClipChangedListener what) {
+        synchronized (mPrimaryClipChangedListeners) {
+            mPrimaryClipChangedListeners.remove(what);
+            if (mPrimaryClipChangedListeners.size() == 0) {
+                try {
+                    getService().removePrimaryClipChangedListener(
+                            mPrimaryClipChangedServiceListener);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+    }
+
+    /**
+     * @deprecated Use {@link #getPrimaryClip()} instead.  This retrieves
+     * the primary clip and tries to coerce it to a string.
+     */
+    public CharSequence getText() {
+        ClipData clip = getPrimaryClip();
+        if (clip != null && clip.getItemCount() > 0) {
+            return clip.getItem(0).coerceToText(mContext);
+        }
+        return null;
+    }
+
+    /**
+     * @deprecated Use {@link #setPrimaryClip(ClipData)} instead.  This
+     * creates a ClippedItem holding the given text and sets it as the
+     * primary clip.  It has no label or icon.
+     */
+    public void setText(CharSequence text) {
+        setPrimaryClip(ClipData.newPlainText(null, null, text));
+    }
+
+    /**
+     * @deprecated Use {@link #hasPrimaryClip()} instead.
+     */
+    public boolean hasText() {
+        try {
+            return getService().hasPrimaryClip();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    void reportPrimaryClipChanged() {
+        Object[] listeners;
+
+        synchronized (mPrimaryClipChangedListeners) {
+            final int N = mPrimaryClipChangedListeners.size();
+            if (N <= 0) {
+                return;
+            }
+            listeners = mPrimaryClipChangedListeners.toArray();
+        }
+
+        for (int i=0; i<listeners.length; i++) {
+            ((OnPrimaryClipChangedListener)listeners[i]).onPrimaryClipChanged();
+        }
+    }
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 1d6e8b8..6bb32c1 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -28,13 +28,16 @@
 import android.database.IContentObserver;
 import android.database.SQLException;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.util.Log;
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.util.ArrayList;
 
 /**
@@ -76,6 +79,8 @@
  * cross-process calls.</p>
  */
 public abstract class ContentProvider implements ComponentCallbacks {
+    private static final String TAG = "ContentProvider";
+
     /*
      * Note: if you add methods to ContentProvider, you must add similar methods to
      *       MockContentProvider.
@@ -249,6 +254,18 @@
             return ContentProvider.this.call(method, request, args);
         }
 
+        @Override
+        public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+            return ContentProvider.this.getStreamTypes(uri, mimeTypeFilter);
+        }
+
+        @Override
+        public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeType, Bundle opts)
+                throws FileNotFoundException {
+            enforceReadPermission(uri);
+            return ContentProvider.this.openTypedAssetFile(uri, mimeType, opts);
+        }
+
         private void enforceReadPermission(Uri uri) {
             final int uid = Binder.getCallingUid();
             if (uid == mMyUid) {
@@ -756,6 +773,144 @@
     }
 
     /**
+     * Called by a client to determine the types of data streams that this
+     * content provider supports for the given URI.  The default implementation
+     * returns null, meaning no types.  If your content provider stores data
+     * of a particular type, return that MIME type if it matches the given
+     * mimeTypeFilter.  If it can perform type conversions, return an array
+     * of all supported MIME types that match mimeTypeFilter.
+     *
+     * @param uri The data in the content provider being queried.
+     * @param mimeTypeFilter The type of data the client desires.  May be
+     * a pattern, such as *\/* to retrieve all possible data types.
+     * @return Returns null if there are no possible data streams for the
+     * given mimeTypeFilter.  Otherwise returns an array of all available
+     * concrete MIME types.
+     *
+     * @see #getType(Uri)
+     * @see #openTypedAssetFile(Uri, String, Bundle)
+     * @see ClipDescription#compareMimeTypes(String, String)
+     */
+    public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+        return null;
+    }
+
+    /**
+     * Called by a client to open a read-only stream containing data of a
+     * particular MIME type.  This is like {@link #openAssetFile(Uri, String)},
+     * except the file can only be read-only and the content provider may
+     * perform data conversions to generate data of the desired type.
+     *
+     * <p>The default implementation compares the given mimeType against the
+     * result of {@link #getType(Uri)} and, if the match, simple calls
+     * {@link #openAssetFile(Uri, String)}.
+     *
+     * <p>See {@link ClipData} for examples of the use and implementation
+     * of this method.
+     *
+     * @param uri The data in the content provider being queried.
+     * @param mimeTypeFilter The type of data the client desires.  May be
+     * a pattern, such as *\/*, if the caller does not have specific type
+     * requirements; in this case the content provider will pick its best
+     * type matching the pattern.
+     * @param opts Additional options from the client.  The definitions of
+     * these are specific to the content provider being called.
+     *
+     * @return Returns a new AssetFileDescriptor from which the client can
+     * read data of the desired type.
+     *
+     * @throws FileNotFoundException Throws FileNotFoundException if there is
+     * no file associated with the given URI or the mode is invalid.
+     * @throws SecurityException Throws SecurityException if the caller does
+     * not have permission to access the data.
+     * @throws IllegalArgumentException Throws IllegalArgumentException if the
+     * content provider does not support the requested MIME type.
+     *
+     * @see #getStreamTypes(Uri, String)
+     * @see #openAssetFile(Uri, String)
+     * @see ClipDescription#compareMimeTypes(String, String)
+     */
+    public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
+            throws FileNotFoundException {
+        if ("*/*".equals(mimeTypeFilter)) {
+            // If they can take anything, the untyped open call is good enough.
+            return openAssetFile(uri, "r");
+        }
+        String baseType = getType(uri);
+        if (baseType != null && ClipDescription.compareMimeTypes(baseType, mimeTypeFilter)) {
+            // Use old untyped open call if this provider has a type for this
+            // URI and it matches the request.
+            return openAssetFile(uri, "r");
+        }
+        throw new FileNotFoundException("Can't open " + uri + " as type " + mimeTypeFilter);
+    }
+
+    /**
+     * Interface to write a stream of data to a pipe.  Use with
+     * {@link ContentProvider#openPipeHelper}.
+     */
+    public interface PipeDataWriter<T> {
+        /**
+         * Called from a background thread to stream data out to a pipe.
+         * Note that the pipe is blocking, so this thread can block on
+         * writes for an arbitrary amount of time if the client is slow
+         * at reading.
+         *
+         * @param output The pipe where data should be written.  This will be
+         * closed for you upon returning from this function.
+         * @param uri The URI whose data is to be written.
+         * @param mimeType The desired type of data to be written.
+         * @param opts Options supplied by caller.
+         * @param args Your own custom arguments.
+         */
+        public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
+                Bundle opts, T args);
+    }
+
+    /**
+     * A helper function for implementing {@link #openTypedAssetFile}, for
+     * creating a data pipe and background thread allowing you to stream
+     * generated data back to the client.  This function returns a new
+     * ParcelFileDescriptor that should be returned to the caller (the caller
+     * is responsible for closing it).
+     *
+     * @param uri The URI whose data is to be written.
+     * @param mimeType The desired type of data to be written.
+     * @param opts Options supplied by caller.
+     * @param args Your own custom arguments.
+     * @param func Interface implementing the function that will actually
+     * stream the data.
+     * @return Returns a new ParcelFileDescriptor holding the read side of
+     * the pipe.  This should be returned to the caller for reading; the caller
+     * is responsible for closing it when done.
+     */
+    public <T> ParcelFileDescriptor openPipeHelper(final Uri uri, final String mimeType,
+            final Bundle opts, final T args, final PipeDataWriter<T> func)
+            throws FileNotFoundException {
+        try {
+            final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+
+            AsyncTask<Object, Object, Object> task = new AsyncTask<Object, Object, Object>() {
+                @Override
+                protected Object doInBackground(Object... params) {
+                    func.writeDataToPipe(fds[1], uri, mimeType, opts, args);
+                    try {
+                        fds[1].close();
+                    } catch (IOException e) {
+                        Log.w(TAG, "Failure closing pipe", e);
+                    }
+                    return null;
+                }
+            };
+            task.execute((Object[])null);
+
+            return fds[0];
+        } catch (IOException e) {
+            throw new FileNotFoundException("failure making pipe");
+        }
+    }
+
+    /**
      * Returns true if this instance is a temporary content provider.
      * @return true if this instance is a temporary content provider
      */
@@ -781,6 +936,11 @@
      * @param info Registered information about this content provider
      */
     public void attachInfo(Context context, ProviderInfo info) {
+        /*
+         * We may be using AsyncTask from binder threads.  Make it init here
+         * so its static handler is on the main thread.
+         */
+        AsyncTask.init();
 
         /*
          * Only allow it to be set once, so after the content service gives
@@ -829,7 +989,7 @@
     /**
      * @hide -- until interface has proven itself
      *
-     * Call an provider-defined method.  This can be used to implement
+     * Call a provider-defined method.  This can be used to implement
      * interfaces that are cheaper than using a Cursor.
      *
      * @param method Method name to call.  Opaque to framework.
@@ -839,4 +999,31 @@
     public Bundle call(String method, String request, Bundle args) {
         return null;
     }
+
+    /**
+     * Implement this to shut down the ContentProvider instance. You can then
+     * invoke this method in unit tests.
+     * 
+     * <p>
+     * Android normally handles ContentProvider startup and shutdown
+     * automatically. You do not need to start up or shut down a
+     * ContentProvider. When you invoke a test method on a ContentProvider,
+     * however, a ContentProvider instance is started and keeps running after
+     * the test finishes, even if a succeeding test instantiates another
+     * ContentProvider. A conflict develops because the two instances are
+     * usually running against the same underlying data source (for example, an
+     * sqlite database).
+     * </p>
+     * <p>
+     * Implementing shutDown() avoids this conflict by providing a way to
+     * terminate the ContentProvider. This method can also prevent memory leaks
+     * from multiple instantiations of the ContentProvider, and it can ensure
+     * unit test isolation by allowing you to completely clean up the test
+     * fixture before moving on to the next test.
+     * </p>
+     */
+    public void shutdown() {
+        Log.w(TAG, "implement ContentProvider shutdown() to make sure all database " +
+                "connections are gracefully shutdown");
+    }
 }
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 0858ea5..0540109 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -18,6 +18,7 @@
 
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ParcelFileDescriptor;
 import android.content.res.AssetFileDescriptor;
@@ -43,53 +44,77 @@
         mContentResolver = contentResolver;
     }
 
-    /** see {@link ContentProvider#query} */
+    /** See {@link ContentProvider#query ContentProvider.query} */
     public Cursor query(Uri url, String[] projection, String selection,
             String[] selectionArgs, String sortOrder) throws RemoteException {
         return mContentProvider.query(url, projection, selection,  selectionArgs, sortOrder);
     }
 
-    /** see {@link ContentProvider#getType} */
+    /** See {@link ContentProvider#getType ContentProvider.getType} */
     public String getType(Uri url) throws RemoteException {
         return mContentProvider.getType(url);
     }
 
-    /** see {@link ContentProvider#insert} */
+    /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
+    public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
+        return mContentProvider.getStreamTypes(url, mimeTypeFilter);
+    }
+
+    /** See {@link ContentProvider#insert ContentProvider.insert} */
     public Uri insert(Uri url, ContentValues initialValues)
             throws RemoteException {
         return mContentProvider.insert(url, initialValues);
     }
 
-    /** see {@link ContentProvider#bulkInsert} */
+    /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
     public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
         return mContentProvider.bulkInsert(url, initialValues);
     }
 
-    /** see {@link ContentProvider#delete} */
+    /** See {@link ContentProvider#delete ContentProvider.delete} */
     public int delete(Uri url, String selection, String[] selectionArgs)
             throws RemoteException {
         return mContentProvider.delete(url, selection, selectionArgs);
     }
 
-    /** see {@link ContentProvider#update} */
+    /** See {@link ContentProvider#update ContentProvider.update} */
     public int update(Uri url, ContentValues values, String selection,
             String[] selectionArgs) throws RemoteException {
         return mContentProvider.update(url, values, selection, selectionArgs);
     }
 
-    /** see {@link ContentProvider#openFile} */
+    /**
+     * See {@link ContentProvider#openFile ContentProvider.openFile}.  Note that
+     * this <em>does not</em>
+     * take care of non-content: URIs such as file:.  It is strongly recommended
+     * you use the {@link ContentResolver#openFileDescriptor
+     * ContentResolver.openFileDescriptor} API instead.
+     */
     public ParcelFileDescriptor openFile(Uri url, String mode)
             throws RemoteException, FileNotFoundException {
         return mContentProvider.openFile(url, mode);
     }
 
-    /** see {@link ContentProvider#openAssetFile} */
+    /**
+     * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
+     * Note that this <em>does not</em>
+     * take care of non-content: URIs such as file:.  It is strongly recommended
+     * you use the {@link ContentResolver#openAssetFileDescriptor
+     * ContentResolver.openAssetFileDescriptor} API instead.
+     */
     public AssetFileDescriptor openAssetFile(Uri url, String mode)
             throws RemoteException, FileNotFoundException {
         return mContentProvider.openAssetFile(url, mode);
     }
 
-     /** see {@link ContentProvider#applyBatch} */
+    /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
+    public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
+            String mimeType, Bundle opts)
+            throws RemoteException, FileNotFoundException {
+        return mContentProvider.openTypedAssetFile(uri, mimeType, opts);
+    }
+
+    /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws RemoteException, OperationApplicationException {
         return mContentProvider.applyBatch(operations);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index fdb3d20..d1ab8d5 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -257,6 +257,38 @@
                     reply.writeBundle(responseBundle);
                     return true;
                 }
+
+                case GET_STREAM_TYPES_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    String mimeTypeFilter = data.readString();
+                    String[] types = getStreamTypes(url, mimeTypeFilter);
+                    reply.writeNoException();
+                    reply.writeStringArray(types);
+
+                    return true;
+                }
+
+                case OPEN_TYPED_ASSET_FILE_TRANSACTION:
+                {
+                    data.enforceInterface(IContentProvider.descriptor);
+                    Uri url = Uri.CREATOR.createFromParcel(data);
+                    String mimeType = data.readString();
+                    Bundle opts = data.readBundle();
+
+                    AssetFileDescriptor fd;
+                    fd = openTypedAssetFile(url, mimeType, opts);
+                    reply.writeNoException();
+                    if (fd != null) {
+                        reply.writeInt(1);
+                        fd.writeToParcel(reply,
+                                Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+                    } else {
+                        reply.writeInt(0);
+                    }
+                    return true;
+                }
             }
         } catch (Exception e) {
             DatabaseUtils.writeExceptionToParcel(reply, e);
@@ -568,5 +600,50 @@
         return bundle;
     }
 
+    public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+
+        data.writeInterfaceToken(IContentProvider.descriptor);
+
+        url.writeToParcel(data, 0);
+        data.writeString(mimeTypeFilter);
+
+        mRemote.transact(IContentProvider.GET_STREAM_TYPES_TRANSACTION, data, reply, 0);
+
+        DatabaseUtils.readExceptionFromParcel(reply);
+        String[] out = reply.createStringArray();
+
+        data.recycle();
+        reply.recycle();
+
+        return out;
+    }
+
+    public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+            throws RemoteException, FileNotFoundException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+
+        data.writeInterfaceToken(IContentProvider.descriptor);
+
+        url.writeToParcel(data, 0);
+        data.writeString(mimeType);
+        data.writeBundle(opts);
+
+        mRemote.transact(IContentProvider.OPEN_TYPED_ASSET_FILE_TRANSACTION, data, reply, 0);
+
+        DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply);
+        int has = reply.readInt();
+        AssetFileDescriptor fd = has != 0
+                ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null;
+
+        data.recycle();
+        reply.recycle();
+
+        return fd;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/content/ContentQueryMap.java b/core/java/android/content/ContentQueryMap.java
index dbcb4a7..c955094 100644
--- a/core/java/android/content/ContentQueryMap.java
+++ b/core/java/android/content/ContentQueryMap.java
@@ -129,7 +129,9 @@
     /** Requeries the cursor and reads the contents into the cache */
     public void requery() {
         mDirty = false;
-        mCursor.requery();
+        if (!mCursor.requery()) {
+            throw new IllegalStateException("trying to requery an already closed cursor");
+        }
         readCursorIntoCache();
         setChanged();
         notifyObservers();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 81ff414..da1aac4 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -45,9 +45,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
-import java.util.ArrayList;
 
 
 /**
@@ -220,6 +220,38 @@
     }
 
     /**
+     * Query for the possible MIME types for the representations the given
+     * content URL can be returned when opened as as stream with
+     * {@link #openTypedAssetFileDescriptor}.  Note that the types here are
+     * not necessarily a superset of the type returned by {@link #getType} --
+     * many content providers can not return a raw stream for the structured
+     * data that they contain.
+     *
+     * @param url A Uri identifying content (either a list or specific type),
+     * using the content:// scheme.
+     * @param mimeTypeFilter The desired MIME type.  This may be a pattern,
+     * such as *\/*, to query for all available MIME types that match the
+     * pattern.
+     * @return Returns an array of MIME type strings for all availablle
+     * data streams that match the given mimeTypeFilter.  If there are none,
+     * null is returned.
+     */
+    public String[] getStreamTypes(Uri url, String mimeTypeFilter) {
+        IContentProvider provider = acquireProvider(url);
+        if (provider == null) {
+            return null;
+        }
+
+        try {
+            return provider.getStreamTypes(url, mimeTypeFilter);
+        } catch (RemoteException e) {
+            return null;
+        } finally {
+			releaseProvider(provider);
+		}
+    }
+
+    /**
      * <p>
      * Query the given URI, returning a {@link Cursor} over the result set.
      * </p>
@@ -365,7 +397,7 @@
     }
 
     /**
-     * Open a raw file descriptor to access data under a "content:" URI.  This
+     * Open a raw file descriptor to access data under a URI.  This
      * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
      * underlying {@link ContentProvider#openFile}
      * ContentProvider.openFile()} method, so will <em>not</em> work with
@@ -415,10 +447,9 @@
     }
 
     /**
-     * Open a raw file descriptor to access data under a "content:" URI.  This
+     * Open a raw file descriptor to access data under a URI.  This
      * interacts with the underlying {@link ContentProvider#openAssetFile}
-     * ContentProvider.openAssetFile()} method of the provider associated with the
-     * given URI, to retrieve any file stored there.
+     * method of the provider associated with the given URI, to retrieve any file stored there.
      *
      * <h5>Accepts the following URI schemes:</h5>
      * <ul>
@@ -450,6 +481,11 @@
      * </li>
      * </ul>
      *
+     * <p>Note that if this function is called for read-only input (mode is "r")
+     * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
+     * for you with a MIME type of "*\/*".  This allows such callers to benefit
+     * from any built-in data conversion that a provider implements.
+     *
      * @param uri The desired URI to open.
      * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
      * ContentProvider.openAssetFile}.
@@ -476,29 +512,97 @@
                     new File(uri.getPath()), modeToMode(uri, mode));
             return new AssetFileDescriptor(pfd, 0, -1);
         } else {
-            IContentProvider provider = acquireProvider(uri);
-            if (provider == null) {
-                throw new FileNotFoundException("No content provider: " + uri);
-            }
-            try {
-                AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
-                if(fd == null) {
-                    releaseProvider(provider);
-                    return null;
+            if ("r".equals(mode)) {
+                return openTypedAssetFileDescriptor(uri, "*/*", null);
+            } else {
+                IContentProvider provider = acquireProvider(uri);
+                if (provider == null) {
+                    throw new FileNotFoundException("No content provider: " + uri);
                 }
-                ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
-                        fd.getParcelFileDescriptor(), provider);
-                return new AssetFileDescriptor(pfd, fd.getStartOffset(),
-                        fd.getDeclaredLength());
-            } catch (RemoteException e) {
+                try {
+                    AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
+                    if(fd == null) {
+                        releaseProvider(provider);
+                        return null;
+                    }
+                    ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
+                            fd.getParcelFileDescriptor(), provider);
+
+                    // Success!  Don't release the provider when exiting, let
+                    // ParcelFileDescriptorInner do that when it is closed.
+                    provider = null;
+
+                    return new AssetFileDescriptor(pfd, fd.getStartOffset(),
+                            fd.getDeclaredLength());
+                } catch (RemoteException e) {
+                    throw new FileNotFoundException("Dead content provider: " + uri);
+                } catch (FileNotFoundException e) {
+                    throw e;
+                } finally {
+                    if (provider != null) {
+                        releaseProvider(provider);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Open a raw file descriptor to access (potentially type transformed)
+     * data from a "content:" URI.  This interacts with the underlying
+     * {@link ContentProvider#openTypedAssetFile} method of the provider
+     * associated with the given URI, to retrieve retrieve any appropriate
+     * data stream for the data stored there.
+     *
+     * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
+     * with "content:" URIs, because content providers are the only facility
+     * with an associated MIME type to ensure that the returned data stream
+     * is of the desired type.
+     *
+     * <p>All text/* streams are encoded in UTF-8.
+     *
+     * @param uri The desired URI to open.
+     * @param mimeType The desired MIME type of the returned data.  This can
+     * be a pattern such as *\/*, which will allow the content provider to
+     * select a type, though there is no way for you to determine what type
+     * it is returning.
+     * @param opts Additional provider-dependent options.
+     * @return Returns a new ParcelFileDescriptor from which you can read the
+     * data stream from the provider.  Note that this may be a pipe, meaning
+     * you can't seek in it.  The only seek you should do is if the
+     * AssetFileDescriptor contains an offset, to move to that offset before
+     * reading.  You own this descriptor and are responsible for closing it when done.
+     * @throws FileNotFoundException Throws FileNotFoundException of no
+     * data of the desired type exists under the URI.
+     */
+    public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
+            String mimeType, Bundle opts) throws FileNotFoundException {
+        IContentProvider provider = acquireProvider(uri);
+        if (provider == null) {
+            throw new FileNotFoundException("No content provider: " + uri);
+        }
+        try {
+            AssetFileDescriptor fd = provider.openTypedAssetFile(uri, mimeType, opts);
+            if (fd == null) {
                 releaseProvider(provider);
-                throw new FileNotFoundException("Dead content provider: " + uri);
-            } catch (FileNotFoundException e) {
+                return null;
+            }
+            ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
+                    fd.getParcelFileDescriptor(), provider);
+
+            // Success!  Don't release the provider when exiting, let
+            // ParcelFileDescriptorInner do that when it is closed.
+            provider = null;
+
+            return new AssetFileDescriptor(pfd, fd.getStartOffset(),
+                    fd.getDeclaredLength());
+        } catch (RemoteException e) {
+            throw new FileNotFoundException("Dead content provider: " + uri);
+        } catch (FileNotFoundException e) {
+            throw e;
+        } finally {
+            if (provider != null) {
                 releaseProvider(provider);
-                throw e;
-            } catch (RuntimeException e) {
-                releaseProvider(provider);
-                throw e;
             }
         }
     }
@@ -1215,12 +1319,36 @@
     }
 
     /**
-     * If a sync is active returns the information about it, otherwise returns false.
+     * If a sync is active returns the information about it, otherwise returns null.
+     * <p>
      * @return the SyncInfo for the currently active sync or null if one is not active.
+     * @deprecated
+     * Since multiple concurrent syncs are now supported you should use
+     * {@link #getCurrentSyncs()} to get the accurate list of current syncs.
+     * This method returns the first item from the list of current syncs
+     * or null if there are none.
      */
+    @Deprecated
     public static SyncInfo getCurrentSync() {
         try {
-            return getContentService().getCurrentSync();
+            final List<SyncInfo> syncs = getContentService().getCurrentSyncs();
+            if (syncs.isEmpty()) {
+                return null;
+            }
+            return syncs.get(0);
+        } catch (RemoteException e) {
+            throw new RuntimeException("the ContentService should always be reachable", e);
+        }
+    }
+
+    /**
+     * Returns a list with information about all the active syncs. This list will be empty
+     * if there are no active syncs.
+     * @return a List of SyncInfo objects for the currently active syncs.
+     */
+    public static List<SyncInfo> getCurrentSyncs() {
+        try {
+            return getContentService().getCurrentSyncs();
         } catch (RemoteException e) {
             throw new RuntimeException("the ContentService should always be reachable", e);
         }
@@ -1376,7 +1504,7 @@
     }
 
     private final class CursorWrapperInner extends CursorWrapper {
-        private IContentProvider mContentProvider;
+        private final IContentProvider mContentProvider;
         public static final String TAG="CursorWrapperInner";
         private boolean mCloseFlag = false;
 
@@ -1405,7 +1533,7 @@
     }
 
     private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
-        private IContentProvider mContentProvider;
+        private final IContentProvider mContentProvider;
         public static final String TAG="ParcelFileDescriptorInner";
         private boolean mReleaseProviderFlag = false;
 
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index fc2dfc0..afe8483 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -386,19 +386,15 @@
         return false;
     }
 
-    public SyncInfo getCurrentSync() {
+    public List<SyncInfo> getCurrentSyncs() {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
                 "no permission to read the sync stats");
         long identityToken = clearCallingIdentity();
         try {
-            SyncManager syncManager = getSyncManager();
-            if (syncManager != null) {
-                return syncManager.getSyncStorageEngine().getCurrentSync();
-            }
+            return getSyncManager().getSyncStorageEngine().getCurrentSyncs();
         } finally {
             restoreCallingIdentity(identityToken);
         }
-        return null;
     }
 
     public SyncStatusInfo getSyncStatus(Account account, String authority) {
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index 75787cd..0d25f80 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -414,6 +414,8 @@
         } catch (ClassCastException e) {
             if (value instanceof CharSequence) {
                 return Boolean.valueOf(value.toString());
+            } else if (value instanceof Number) {
+                return ((Number) value).intValue() != 0;
             } else {
                 Log.e(TAG, "Cannot cast value for " + key + " to a Boolean: " + value, e);
                 return null;
@@ -446,6 +448,15 @@
         return mValues.entrySet();
     }
 
+    /**
+     * Returns a set of all of the keys
+     *
+     * @return a set of all of the keys
+     */
+    public Set<String> keySet() {
+        return mValues.keySet();
+    }
+
     public static final Parcelable.Creator<ContentValues> CREATOR =
             new Parcelable.Creator<ContentValues>() {
         @SuppressWarnings({"deprecation", "unchecked"})
@@ -488,6 +499,10 @@
         return (ArrayList<String>) mValues.get(key);
     }
 
+    /**
+     * Returns a string containing a concise, human-readable description of this object.
+     * @return a printable representation of this object.
+     */
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7563831..a463afc 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -21,10 +21,12 @@
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
+import android.media.MediaScannerConnection.OnScanCompletedListener;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -589,6 +591,32 @@
             int mode, CursorFactory factory);
 
     /**
+     * Open a new private SQLiteDatabase associated with this Context's
+     * application package.  Creates the database file if it doesn't exist.
+     *
+     * <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
+     * used to handle corruption when sqlite reports database corruption.</p>
+     *
+     * @param name The name (unique in the application package) of the database.
+     * @param mode Operating mode.  Use 0 or {@link #MODE_PRIVATE} for the
+     *     default operation, {@link #MODE_WORLD_READABLE}
+     *     and {@link #MODE_WORLD_WRITEABLE} to control permissions.
+     * @param factory An optional factory class that is called to instantiate a
+     *     cursor when query is called.
+     * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
+     * corruption. if null, {@link android.database.DefaultDatabaseErrorHandler} is assumed.
+     * @return The contents of a newly created database with the given name.
+     * @throws android.database.sqlite.SQLiteException if the database file could not be opened.
+     *
+     * @see #MODE_PRIVATE
+     * @see #MODE_WORLD_READABLE
+     * @see #MODE_WORLD_WRITEABLE
+     * @see #deleteDatabase
+     */
+    public abstract SQLiteDatabase openOrCreateDatabase(String name,
+            int mode, CursorFactory factory, DatabaseErrorHandler errorHandler);
+
+    /**
      * Delete an existing private SQLiteDatabase associated with this Context's
      * application package.
      *
@@ -1362,6 +1390,15 @@
     public static final String LOCATION_SERVICE = "location";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link android.location.CountryDetector} for detecting the country that
+     * the user is in.
+     *
+     * @hide
+     */
+    public static final String COUNTRY_DETECTOR = "country_detector";
+
+    /**
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.app.SearchManager} for handling searches.
      *
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index a447108..3f5d215 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -20,6 +20,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
+import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
@@ -204,6 +205,12 @@
     }
 
     @Override
+    public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
+            DatabaseErrorHandler errorHandler) {
+        return mBase.openOrCreateDatabase(name, mode, factory, errorHandler);
+    }
+
+    @Override
     public boolean deleteDatabase(String name) {
         return mBase.deleteDatabase(name);
     }
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
new file mode 100644
index 0000000..7776874
--- /dev/null
+++ b/core/java/android/content/CursorLoader.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * A loader that queries the {@link ContentResolver} and returns a {@link Cursor}.
+ */
+public class CursorLoader extends AsyncTaskLoader<Cursor> {
+    Cursor mCursor;
+    ForceLoadContentObserver mObserver;
+    boolean mStopped;
+    Uri mUri;
+    String[] mProjection;
+    String mSelection;
+    String[] mSelectionArgs;
+    String mSortOrder;
+
+    /* Runs on a worker thread */
+    @Override
+    public Cursor loadInBackground() {
+        Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
+                mSelectionArgs, mSortOrder);
+        if (cursor != null) {
+            // Ensure the cursor window is filled
+            cursor.getCount();
+            registerContentObserver(cursor, mObserver);
+        }
+        return cursor;
+    }
+
+    /**
+     * Registers an observer to get notifications from the content provider
+     * when the cursor needs to be refreshed.
+     */
+    public void registerContentObserver(Cursor cursor, ContentObserver observer) {
+        cursor.registerContentObserver(mObserver);
+    }
+
+    /* Runs on the UI thread */
+    @Override
+    public void deliverResult(Cursor cursor) {
+        if (mStopped) {
+            // An async query came in while the loader is stopped
+            if (cursor != null) {
+                cursor.close();
+            }
+            return;
+        }
+        Cursor oldCursor = mCursor;
+        mCursor = cursor;
+        super.deliverResult(cursor);
+
+        if (oldCursor != null && !oldCursor.isClosed()) {
+            oldCursor.close();
+        }
+    }
+
+    public CursorLoader(Context context, Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        super(context);
+        mObserver = new ForceLoadContentObserver();
+        mUri = uri;
+        mProjection = projection;
+        mSelection = selection;
+        mSelectionArgs = selectionArgs;
+        mSortOrder = sortOrder;
+    }
+
+    /**
+     * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
+     * will be called on the UI thread. If a previous load has been completed and is still valid
+     * the result may be passed to the callbacks immediately.
+     *
+     * Must be called from the UI thread
+     */
+    @Override
+    public void startLoading() {
+        mStopped = false;
+
+        if (mCursor != null) {
+            deliverResult(mCursor);
+        } else {
+            forceLoad();
+        }
+    }
+
+    /**
+     * Must be called from the UI thread
+     */
+    @Override
+    public void stopLoading() {
+        if (mCursor != null && !mCursor.isClosed()) {
+            mCursor.close();
+        }
+        mCursor = null;
+
+        // Attempt to cancel the current load task if possible.
+        cancelLoad();
+
+        // Make sure that any outstanding loads clean themselves up properly
+        mStopped = true;
+    }
+
+    @Override
+    public void onCancelled(Cursor cursor) {
+        if (cursor != null && !cursor.isClosed()) {
+            cursor.close();
+        }
+    }
+
+    @Override
+    public void destroy() {
+        // Ensure the loader is stopped
+        stopLoading();
+    }
+
+    public Uri getUri() {
+        return mUri;
+    }
+
+    public void setUri(Uri uri) {
+        mUri = uri;
+    }
+
+    public String[] getProjection() {
+        return mProjection;
+    }
+
+    public void setProjection(String[] projection) {
+        mProjection = projection;
+    }
+
+    public String getSelection() {
+        return mSelection;
+    }
+
+    public void setSelection(String selection) {
+        mSelection = selection;
+    }
+
+    public String[] getSelectionArgs() {
+        return mSelectionArgs;
+    }
+
+    public void setSelectionArgs(String[] selectionArgs) {
+        mSelectionArgs = selectionArgs;
+    }
+
+    public String getSortOrder() {
+        return mSortOrder;
+    }
+
+    public void setSortOrder(String sortOrder) {
+        mSortOrder = sortOrder;
+    }
+}
diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl
new file mode 100644
index 0000000..254901b
--- /dev/null
+++ b/core/java/android/content/IClipboard.aidl
@@ -0,0 +1,40 @@
+/**
+ * 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.ClipData;
+import android.content.ClipDescription;
+import android.content.IOnPrimaryClipChangedListener;
+
+/**
+ * Programming interface to the clipboard, which allows copying and pasting
+ * between applications.
+ * {@hide}
+ */
+interface IClipboard {
+    void setPrimaryClip(in ClipData clip);
+    ClipData getPrimaryClip(String pkg);
+    ClipDescription getPrimaryClipDescription();
+    boolean hasPrimaryClip();
+    void addPrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener);
+    void removePrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener);
+
+    /**
+     * Returns true if the clipboard contains text; false otherwise.
+     */
+    boolean hasClipboardText();
+}
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 67e7581..8f122ce 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -59,6 +59,7 @@
             throws RemoteException, FileNotFoundException;
     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws RemoteException, OperationApplicationException;
+
     /**
      * @hide -- until interface has proven itself
      *
@@ -71,6 +72,11 @@
      */
     public Bundle call(String method, String request, Bundle args) throws RemoteException;
 
+    // Data interchange.
+    public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
+    public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+            throws RemoteException, FileNotFoundException;
+
     /* IPC constants */
     static final String descriptor = "android.content.IContentProvider";
 
@@ -84,4 +90,6 @@
     static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14;
     static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19;
     static final int CALL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 20;
+    static final int GET_STREAM_TYPES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 21;
+    static final int OPEN_TYPED_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 22;
 }
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index a6368d5..86a9392 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -104,7 +104,7 @@
      */
     boolean isSyncActive(in Account account, String authority);
 
-    SyncInfo getCurrentSync();
+    List<SyncInfo> getCurrentSyncs();
 
     /**
      * Returns the types of the SyncAdapters that are registered with the system.
diff --git a/core/java/android/content/IOnPrimaryClipChangedListener.aidl b/core/java/android/content/IOnPrimaryClipChangedListener.aidl
new file mode 100644
index 0000000..fb42a45
--- /dev/null
+++ b/core/java/android/content/IOnPrimaryClipChangedListener.aidl
@@ -0,0 +1,21 @@
+/**
+ * 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;
+
+oneway interface IOnPrimaryClipChangedListener {
+    void dispatchPrimaryClipChanged();
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index de8e3f4..4b6333e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -533,7 +533,10 @@
  *     <li> {@link #CATEGORY_TEST}
  *     <li> {@link #CATEGORY_CAR_DOCK}
  *     <li> {@link #CATEGORY_DESK_DOCK}
+ *     <li> {@link #CATEGORY_LE_DESK_DOCK}
+ *     <li> {@link #CATEGORY_HE_DESK_DOCK}
  *     <li> {@link #CATEGORY_CAR_MODE}
+ *     <li> {@link #CATEGORY_APP_MARKET}
  * </ul>
  *
  * <h3>Standard Extra Data</h3>
@@ -548,6 +551,8 @@
  *     <li> {@link #EXTRA_CHANGED_COMPONENT_NAME}
  *     <li> {@link #EXTRA_DATA_REMOVED}
  *     <li> {@link #EXTRA_DOCK_STATE}
+ *     <li> {@link #EXTRA_DOCK_STATE_HE_DESK}
+ *     <li> {@link #EXTRA_DOCK_STATE_LE_DESK}
  *     <li> {@link #EXTRA_DOCK_STATE_CAR}
  *     <li> {@link #EXTRA_DOCK_STATE_DESK}
  *     <li> {@link #EXTRA_DOCK_STATE_UNDOCKED}
@@ -985,6 +990,15 @@
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_INSERT = "android.intent.action.INSERT";
     /**
+     * Activity Action: Create a new item in the given container, initializing it
+     * from the current contents of the clipboard.
+     * <p>Input: {@link #getData} is URI of the directory (vnd.android.cursor.dir/*)
+     * in which to place the data.
+     * <p>Output: URI of the new data that was created.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_PASTE = "android.intent.action.PASTE";
+    /**
      * Activity Action: Delete the given data from its container.
      * <p>Input: {@link #getData} is URI of data to be deleted.
      * <p>Output: nothing.
@@ -1209,6 +1223,13 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED";
     /**
+     * Clear DNS Cache Action: This is broadcast when networks have changed and old
+     * DNS entries should be tossed.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
+    /**
      * Alarm Changed Action: This is broadcast when the AlarmClock
      * application's alarm is set or unset.  It is used by the
      * AlarmClock application and the StatusBar service.
@@ -1812,6 +1833,51 @@
             "android.intent.action.HEADSET_PLUG";
 
     /**
+     * Broadcast Action: An analog audio speaker/headset plugged in or unplugged.
+     *
+     * <p>The intent will have the following extra values:
+     * <ul>
+     *   <li><em>state</em> - 0 for unplugged, 1 for plugged. </li>
+     *   <li><em>name</em> - Headset type, human readable string </li>
+     * </ul>
+     * </ul>
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_USB_ANLG_HEADSET_PLUG =
+            "android.intent.action.DOCK_HEADSET_PLUG";
+
+    /**
+     * Broadcast Action: An analog audio speaker/headset plugged in or unplugged.
+     *
+     * <p>The intent will have the following extra values:
+     * <ul>
+     *   <li><em>state</em> - 0 for unplugged, 1 for plugged. </li>
+     *   <li><em>name</em> - Headset type, human readable string </li>
+     * </ul>
+     * </ul>
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_USB_DGTL_HEADSET_PLUG =
+            "android.intent.action.HDMI_AUDIO_PLUG";
+
+    /**
+     * Broadcast Action: A HMDI cable was plugged or unplugged
+     *
+     * <p>The intent will have the following extra values:
+     * <ul>
+     *   <li><em>state</em> - 0 for unplugged, 1 for plugged. </li>
+     *   <li><em>name</em> - HDMI cable, human readable string </li>
+     * </ul>
+     * </ul>
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_HDMI_AUDIO_PLUG =
+            "android.intent.action.HDMI_AUDIO_PLUG";
+
+    /**
      * Broadcast Action: An outgoing call is about to be placed.
      *
      * <p>The Intent will have the following extra value:
@@ -2001,6 +2067,11 @@
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_EMBED = "android.intent.category.EMBED";
     /**
+     * This activity allows the user to browse and download new applications.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_APP_MARKET = "android.intent.category.APP_MARKET";
+    /**
      * This activity may be exercised by the monkey or other automated test tools.
      */
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
@@ -2045,6 +2116,21 @@
      */
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_DESK_DOCK = "android.intent.category.DESK_DOCK";
+    /**
+     * An activity to run when device is inserted into a analog (low end) dock.
+     * Used with {@link #ACTION_MAIN} to launch an activity.  For more
+     * information, see {@link android.app.UiModeManager}.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_LE_DESK_DOCK = "android.intent.category.LE_DESK_DOCK";
+
+    /**
+     * An activity to run when device is inserted into a digital (high end) dock.
+     * Used with {@link #ACTION_MAIN} to launch an activity.  For more
+     * information, see {@link android.app.UiModeManager}.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_HE_DESK_DOCK = "android.intent.category.HE_DESK_DOCK";
 
     /**
      * Used to indicate that the activity can be used in a car environment.
@@ -2193,7 +2279,9 @@
      * intents to request the dock state.  Possible values are
      * {@link android.content.Intent#EXTRA_DOCK_STATE_UNDOCKED},
      * {@link android.content.Intent#EXTRA_DOCK_STATE_DESK}, or
-     * {@link android.content.Intent#EXTRA_DOCK_STATE_CAR}.
+     * {@link android.content.Intent#EXTRA_DOCK_STATE_CAR}, or
+     * {@link android.content.Intent#EXTRA_DOCK_STATE_LE_DESK}, or
+     * {@link android.content.Intent#EXTRA_DOCK_STATE_HE_DESK}.
      */
     public static final String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
 
@@ -2216,6 +2304,18 @@
     public static final int EXTRA_DOCK_STATE_CAR = 2;
 
     /**
+     * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE}
+     * to represent that the phone is in a analog (low end) dock.
+     */
+    public static final int EXTRA_DOCK_STATE_LE_DESK = 3;
+
+    /**
+     * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE}
+     * to represent that the phone is in a digital (high end) dock.
+     */
+    public static final int EXTRA_DOCK_STATE_HE_DESK = 4;
+
+    /**
      * Boolean that can be supplied as meta-data with a dock activity, to
      * indicate that the dock should take over the home key when it is active.
      */
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
new file mode 100644
index 0000000..234096a
--- /dev/null
+++ b/core/java/android/content/Loader.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.database.ContentObserver;
+import android.os.Handler;
+
+/**
+ * An abstract class that performs asynchronous loading of data. While Loaders are active
+ * they should monitor the source of their data and deliver new results when the contents
+ * change.
+ *
+ * @param <D> The result returned when the load is complete
+ */
+public abstract class Loader<D> {
+    int mId;
+    OnLoadCompleteListener<D> mListener;
+    Context mContext;
+
+    public final class ForceLoadContentObserver extends ContentObserver {
+        public ForceLoadContentObserver() {
+            super(new Handler());
+        }
+
+        @Override
+        public boolean deliverSelfNotifications() {
+            return true;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            onContentChanged();
+        }
+    }
+
+    public interface OnLoadCompleteListener<D> {
+        /**
+         * Called on the thread that created the Loader when the load is complete.
+         *
+         * @param loader the loader that completed the load
+         * @param data the result of the load
+         */
+        public void onLoadComplete(Loader<D> loader, D data);
+    }
+
+    /**
+     * Stores away the application context associated with context. Since Loaders can be used
+     * across multiple activities it's dangerous to store the context directly.
+     *
+     * @param context used to retrieve the application context.
+     */
+    public Loader(Context context) {
+        mContext = context.getApplicationContext();
+    }
+
+    /**
+     * Sends the result of the load to the registered listener. Should only be called by subclasses.
+     *
+     * Must be called from the UI thread.
+     *
+     * @param data the result of the load
+     */
+    public void deliverResult(D data) {
+        if (mListener != null) {
+            mListener.onLoadComplete(this, data);
+        }
+    }
+
+    /**
+     * @return an application context retrieved from the Context passed to the constructor.
+     */
+    public Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * @return the ID of this loader
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Registers a class that will receive callbacks when a load is complete. The callbacks will
+     * be called on the UI thread so it's safe to pass the results to widgets.
+     *
+     * Must be called from the UI thread
+     */
+    public void registerListener(int id, OnLoadCompleteListener<D> listener) {
+        if (mListener != null) {
+            throw new IllegalStateException("There is already a listener registered");
+        }
+        mListener = listener;
+        mId = id;
+    }
+
+    /**
+     * Must be called from the UI thread
+     */
+    public void unregisterListener(OnLoadCompleteListener<D> listener) {
+        if (mListener == null) {
+            throw new IllegalStateException("No listener register");
+        }
+        if (mListener != listener) {
+            throw new IllegalArgumentException("Attempting to unregister the wrong listener");
+        }
+        mListener = null;
+    }
+
+    /**
+     * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
+     * will be called on the UI thread. If a previous load has been completed and is still valid
+     * the result may be passed to the callbacks immediately. The loader will monitor the source of
+     * the data set and may deliver future callbacks if the source changes. Calling
+     * {@link #stopLoading} will stop the delivery of callbacks.
+     *
+     * Must be called from the UI thread
+     */
+    public abstract void startLoading();
+
+    /**
+     * Force an asynchronous load. Unlike {@link #startLoading()} this will ignore a previously
+     * loaded data set and load a new one.
+     */
+    public abstract void forceLoad();
+
+    /**
+     * Stops delivery of updates until the next time {@link #startLoading()} is called
+     *
+     * Must be called from the UI thread
+     */
+    public abstract void stopLoading();
+
+    /**
+     * Destroys the loader and frees its resources, making it unusable.
+     *
+     * Must be called from the UI thread
+     */
+    public abstract void destroy();
+
+    /**
+     * Called when {@link ForceLoadContentObserver} detects a change.  Calls {@link #forceLoad()}
+     * by default.
+     *
+     * Must be called from the UI thread
+     */
+    public void onContentChanged() {
+        forceLoad();
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index 3221afa..4d9ee54 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Interface for accessing and modifying preference data returned by {@link
@@ -71,6 +72,17 @@
         Editor putString(String key, String value);
         
         /**
+         * Set a set of String values in the preferences editor, to be written
+         * back once {@link #commit} is called.
+         * 
+         * @param key The name of the preference to modify.
+         * @param values The new values for the preference.
+         * @return Returns a reference to the same Editor object, so you can
+         * chain put calls together.
+         */
+        Editor putStringSet(String key, Set<String> values);
+        
+        /**
          * Set an int value in the preferences editor, to be written back once
          * {@link #commit} or {@link #apply} are called.
          * 
@@ -230,6 +242,20 @@
     String getString(String key, String defValue);
     
     /**
+     * Retrieve a set of String values from the preferences.
+     * 
+     * @param key The name of the preference to retrieve.
+     * @param defValues Values to return if this preference does not exist.
+     * 
+     * @return Returns the preference values if they exist, or defValues.
+     * Throws ClassCastException if there is a preference with this name
+     * that is not a Set.
+     * 
+     * @throws ClassCastException
+     */
+    Set<String> getStringSet(String key, Set<String> defValues);
+    
+    /**
      * Retrieve an int value from the preferences.
      * 
      * @param key The name of the preference to retrieve.
diff --git a/core/java/android/content/SyncInfo.java b/core/java/android/content/SyncInfo.java
index 616b05f..abfe964 100644
--- a/core/java/android/content/SyncInfo.java
+++ b/core/java/android/content/SyncInfo.java
@@ -18,12 +18,13 @@
 
 import android.accounts.Account;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.Parcelable.Creator;
 
 /**
  * Information about the sync operation that is currently underway.
  */
-public class SyncInfo {
+public class SyncInfo implements Parcelable {
     /** @hide */
     public final int authorityId;
 
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 26b6ad7..34bd386 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -16,6 +16,20 @@
 
 package android.content;
 
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
 import com.android.internal.R;
 import com.android.internal.util.ArrayUtils;
 
@@ -34,17 +48,6 @@
 import android.content.pm.RegisteredServicesCacheListener;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.text.format.DateUtils;
@@ -56,10 +59,13 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
-import java.util.Collection;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -78,30 +84,17 @@
     private static final long MAX_TIME_PER_SYNC;
 
     static {
-        String localSyncDelayString = SystemProperties.get("sync.local_sync_delay");
-        long localSyncDelay = 30 * 1000; // 30 seconds
-        if (localSyncDelayString != null) {
-            try {
-                localSyncDelay = Long.parseLong(localSyncDelayString);
-            } catch (NumberFormatException nfe) {
-                // ignore, use default
-            }
-        }
-        LOCAL_SYNC_DELAY = localSyncDelay;
-
-        String maxTimePerSyncString = SystemProperties.get("sync.max_time_per_sync");
-        long maxTimePerSync = 5 * 60 * 1000; // 5 minutes
-        if (maxTimePerSyncString != null) {
-            try {
-                maxTimePerSync = Long.parseLong(maxTimePerSyncString);
-            } catch (NumberFormatException nfe) {
-                // ignore, use default
-            }
-        }
-        MAX_TIME_PER_SYNC = maxTimePerSync;
+        MAX_SIMULTANEOUS_INITIALIZATION_SYNCS = SystemProperties.getInt("sync.max_init_syncs", 5);
+        MAX_SIMULTANEOUS_REGULAR_SYNCS = SystemProperties.getInt("sync.max_regular_syncs", 2);
+        LOCAL_SYNC_DELAY =
+                SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */);
+        MAX_TIME_PER_SYNC =
+                SystemProperties.getLong("sync.max_time_per_sync", 5 * 60 * 1000 /* 5 minutes */);
+        SYNC_NOTIFICATION_DELAY =
+                SystemProperties.getLong("sync.notification_delay", 30 * 1000 /* 30 seconds */);
     }
 
-    private static final long SYNC_NOTIFICATION_DELAY = 30 * 1000; // 30 seconds
+    private static final long SYNC_NOTIFICATION_DELAY;
 
     /**
      * When retrying a sync for the first time use this delay. After that
@@ -120,22 +113,21 @@
      */
     private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10;
 
-    /**
-     * An error notification is sent if sync of any of the providers has been failing for this long.
-     */
-    private static final long ERROR_NOTIFICATION_DELAY_MS = 1000 * 60 * 10; // 10 minutes
-
     private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000;
 
-    private static final String SYNC_WAKE_LOCK = "*sync*";
+    private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*";
     private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
+    private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
+
+    private static final int MAX_SIMULTANEOUS_REGULAR_SYNCS;
+    private static final int MAX_SIMULTANEOUS_INITIALIZATION_SYNCS;
 
     private Context mContext;
 
     private volatile Account[] mAccounts = INITIAL_ACCOUNTS_ARRAY;
 
-    volatile private PowerManager.WakeLock mSyncWakeLock;
     volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
+    volatile private PowerManager.WakeLock mSyncManagerWakeLock;
     volatile private boolean mDataConnectionIsConnected = false;
     volatile private boolean mStorageIsLow = false;
 
@@ -145,10 +137,8 @@
     private final SyncStorageEngine mSyncStorageEngine;
     public final SyncQueue mSyncQueue;
 
-    private ActiveSyncContext mActiveSyncContext = null;
+    private final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList();
 
-    // set if the sync error indicator should be reported.
-    private boolean mNeedSyncErrorNotification = false;
     // set if the sync active indicator should be reported
     private boolean mNeedSyncActiveNotification = false;
 
@@ -196,6 +186,11 @@
 
     private static final Account[] INITIAL_ACCOUNTS_ARRAY = new Account[0];
 
+    private final PowerManager mPowerManager;
+
+    private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds
+    private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours
+
     public void onAccountsUpdated(Account[] accounts) {
         // remember if this was the first time this was called after an update
         final boolean justBootedUp = mAccounts == INITIAL_ACCOUNTS_ARRAY;
@@ -203,11 +198,10 @@
 
         // if a sync is in progress yet it is no longer in the accounts list,
         // cancel it
-        ActiveSyncContext activeSyncContext = mActiveSyncContext;
-        if (activeSyncContext != null) {
-            if (!ArrayUtils.contains(accounts, activeSyncContext.mSyncOperation.account)) {
+        for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
+            if (!ArrayUtils.contains(accounts, currentSyncContext.mSyncOperation.account)) {
                 Log.d(TAG, "canceling sync since the account has been removed");
-                sendSyncFinishedOrCanceledMessage(activeSyncContext,
+                sendSyncFinishedOrCanceledMessage(currentSyncContext,
                         null /* no result since this is a cancel */);
             }
         }
@@ -234,6 +228,7 @@
             // If this was the bootup case then don't sync everything, instead only
             // sync those that have an unknown syncable state, which will give them
             // a chance to set their syncable state.
+
             boolean onlyThoseWithUnkownSyncableState = justBootedUp;
             scheduleSync(null, null, null, 0 /* no delay */, onlyThoseWithUnkownSyncableState);
         }
@@ -357,18 +352,25 @@
         } else {
             mNotificationMgr = null;
         }
-        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mSyncWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, SYNC_WAKE_LOCK);
-        mSyncWakeLock.setReferenceCounted(false);
+        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
 
         // This WakeLock is used to ensure that we stay awake between the time that we receive
         // a sync alarm notification and when we finish processing it. We need to do this
         // because we don't do the work in the alarm handler, rather we do it in a message
         // handler.
-        mHandleAlarmWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+        mHandleAlarmWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                 HANDLE_SYNC_ALARM_WAKE_LOCK);
         mHandleAlarmWakeLock.setReferenceCounted(false);
 
+        // This WakeLock is used to ensure that we stay awake while running the sync loop
+        // message handler. Normally we will hold a sync adapter wake lock while it is being
+        // synced but during the execution of the sync loop it might finish a sync for
+        // one sync adapter before starting the sync for the other sync adapter and we
+        // don't want the device to go to sleep during that window.
+        mSyncManagerWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                SYNC_LOOP_WAKE_LOCK);
+        mSyncManagerWakeLock.setReferenceCounted(false);
+
         mSyncStorageEngine.addStatusChangeListener(
                 ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() {
             public void onStatusChanged(int which) {
@@ -425,8 +427,8 @@
         Intent intent = new Intent();
         intent.setAction("android.content.SyncAdapter");
         intent.setComponent(syncAdapterInfo.componentName);
-        if (!mContext.bindService(intent, new InitializerServiceConnection(account, authority, mContext,
-                mMainHandler),
+        if (!mContext.bindService(intent,
+                new InitializerServiceConnection(account, authority, mContext, mMainHandler),
                 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND)) {
             Log.w(TAG, "initializeSyncAdapter: failed to bind to " + intent);
         }
@@ -506,8 +508,8 @@
      * @param requestedAccount the account to sync, may be null to signify all accounts
      * @param requestedAuthority the authority to sync, may be null to indicate all authorities
      * @param extras a Map of SyncAdapter-specific information to control
-*          syncs of a specific provider. Can be null. Is ignored
-*          if the url is null.
+     *          syncs of a specific provider. Can be null. Is ignored
+     *          if the url is null.
      * @param delay how many milliseconds in the future to wait before performing this
      * @param onlyThoseWithUnkownSyncableState
      */
@@ -611,16 +613,29 @@
                         continue;
                     }
 
-                    if (isLoggable) {
-                        Log.v(TAG, "scheduleSync:"
-                                + " delay " + delay
-                                + ", source " + source
-                                + ", account " + account
-                                + ", authority " + authority
-                                + ", extras " + extras);
+                    Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(account, authority);
+                    long delayUntil = mSyncStorageEngine.getDelayUntilTime(account, authority);
+                    final long backoffTime = backoff != null ? backoff.first : 0;
+                    if (isSyncable < 0) {
+                        Bundle newExtras = new Bundle();
+                        newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
+                        scheduleSyncOperation(
+                                new SyncOperation(account, source, authority, newExtras, 0,
+                                        backoffTime, delayUntil));
                     }
-                    scheduleSyncOperation(
-                            new SyncOperation(account, source, authority, extras, delay));
+                    if (!onlyThoseWithUnkownSyncableState) {
+                        if (isLoggable) {
+                            Log.v(TAG, "scheduleSync:"
+                                    + " delay " + delay
+                                    + ", source " + source
+                                    + ", account " + account
+                                    + ", authority " + authority
+                                    + ", extras " + extras);
+                        }
+                        scheduleSyncOperation(
+                                new SyncOperation(account, source, authority, extras, delay,
+                                        backoffTime, delayUntil));
+                    }
                 }
             }
         }
@@ -634,7 +649,8 @@
     }
 
     public SyncAdapterType[] getSyncAdapterTypes() {
-        final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos =
+        final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>
+                serviceInfos =
                 mSyncAdapters.getAllServices();
         SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()];
         int i = 0;
@@ -664,6 +680,14 @@
         mSyncHandler.sendMessage(msg);
     }
 
+    private void sendCancelSyncsMessage(final Account account, final String authority) {
+        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL");
+        Message msg = mSyncHandler.obtainMessage();
+        msg.what = SyncHandler.MESSAGE_CANCEL;
+        msg.obj = Pair.create(account, authority);
+        mSyncHandler.sendMessage(msg);
+    }
+
     class SyncHandlerMessagePayload {
         public final ActiveSyncContext activeSyncContext;
         public final SyncResult syncResult;
@@ -681,11 +705,6 @@
         }
     }
 
-    private void clearBackoffSetting(SyncOperation op) {
-        mSyncStorageEngine.setBackoff(op.account, op.authority,
-                SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
-    }
-
     private void increaseBackoffSetting(SyncOperation op) {
         final long now = SystemClock.elapsedRealtime();
 
@@ -709,8 +728,17 @@
             newDelayInMs = maxSyncRetryTimeInSeconds * 1000;
         }
 
+        final long backoff = now + newDelayInMs;
+
         mSyncStorageEngine.setBackoff(op.account, op.authority,
-                now + newDelayInMs, newDelayInMs);
+                backoff, newDelayInMs);
+
+        op.backoff = backoff;
+        op.updateEffectiveRunTime();
+
+        synchronized (mSyncQueue) {
+            mSyncQueue.onBackoffChanged(op.account, op.authority, backoff);
+        }
     }
 
     private void setDelayUntilTime(SyncOperation op, long delayUntilSeconds) {
@@ -723,6 +751,9 @@
             newDelayUntilTime = 0;
         }
         mSyncStorageEngine.setDelayUntilTime(op.account, op.authority, newDelayUntilTime);
+        synchronized (mSyncQueue) {
+            mSyncQueue.onDelayUntilTimeChanged(op.account, op.authority, newDelayUntilTime);
+        }
     }
 
     /**
@@ -731,23 +762,7 @@
      * @param authority limit the cancelations to syncs with this authority, if non-null
      */
     public void cancelActiveSync(Account account, String authority) {
-        ActiveSyncContext activeSyncContext = mActiveSyncContext;
-        if (activeSyncContext != null) {
-            // if an authority was specified then only cancel the sync if it matches
-            if (account != null) {
-                if (!account.equals(activeSyncContext.mSyncOperation.account)) {
-                    return;
-                }
-            }
-            // if an account was specified then only cancel the sync if it matches
-            if (authority != null) {
-                if (!authority.equals(activeSyncContext.mSyncOperation.authority)) {
-                    return;
-                }
-            }
-            sendSyncFinishedOrCanceledMessage(activeSyncContext,
-                    null /* no result since this is a cancel */);
-        }
+        sendCancelSyncsMessage(account, authority);
     }
 
     /**
@@ -756,22 +771,6 @@
      * @param syncOperation the SyncOperation to schedule
      */
     public void scheduleSyncOperation(SyncOperation syncOperation) {
-        // If this operation is expedited and there is a sync in progress then
-        // reschedule the current operation and send a cancel for it.
-        final ActiveSyncContext activeSyncContext = mActiveSyncContext;
-        if (syncOperation.expedited && activeSyncContext != null) {
-            final boolean hasSameKey =
-                    activeSyncContext.mSyncOperation.key.equals(syncOperation.key);
-            // This request is expedited and there is a sync in progress.
-            // Interrupt the current sync only if it is not expedited and if it has a different
-            // key than the one we are scheduling.
-            if (!activeSyncContext.mSyncOperation.expedited && !hasSameKey) {
-                scheduleSyncOperation(new SyncOperation(activeSyncContext.mSyncOperation));
-                sendSyncFinishedOrCanceledMessage(activeSyncContext,
-                        null /* no result since this is a cancel */);
-            }
-        }
-
         boolean queueChanged;
         synchronized (mSyncQueue) {
             queueChanged = mSyncQueue.add(syncOperation);
@@ -796,11 +795,11 @@
      * @param authority limit the removals to operations with this authority, if non-null
      */
     public void clearScheduledSyncOperations(Account account, String authority) {
-        mSyncStorageEngine.setBackoff(account, authority,
-                SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
         synchronized (mSyncQueue) {
             mSyncQueue.remove(account, authority);
         }
+        mSyncStorageEngine.setBackoff(account, authority,
+                SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
     }
 
     void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) {
@@ -827,7 +826,8 @@
         if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) {
             Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified "
                     + operation);
-        } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)) {
+        } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)
+                && !syncResult.syncAlreadyInProgress) {
             operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD);
             Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
                     + "encountered an error: " + operation);
@@ -848,7 +848,8 @@
             }
             scheduleSyncOperation(new SyncOperation(operation.account, operation.syncSource,
                     operation.authority, operation.extras,
-                    DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000));
+                    DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000,
+                    operation.backoff, operation.delayUntil));
         } else if (syncResult.hasSoftError()) {
             if (isLoggable) {
                 Log.d(TAG, "retrying sync operation because it encountered a soft error: "
@@ -871,15 +872,33 @@
         final long mStartTime;
         long mTimeoutStartTime;
         boolean mBound;
+        final PowerManager.WakeLock mSyncWakeLock;
+        final int mSyncAdapterUid;
+        SyncInfo mSyncInfo;
 
-        public ActiveSyncContext(SyncOperation syncOperation,
-                long historyRowId) {
+        /**
+         * Create an ActiveSyncContext for an impending sync and grab the wakelock for that
+         * sync adapter. Since this grabs the wakelock you need to be sure to call
+         * close() when you are done with this ActiveSyncContext, whether the sync succeeded
+         * or not.
+         * @param syncOperation the SyncOperation we are about to sync
+         * @param historyRowId the row in which to record the history info for this sync
+         * @param syncAdapterUid the UID of the application that contains the sync adapter
+         * for this sync. This is used to attribute the wakelock hold to that application.
+         */
+        public ActiveSyncContext(SyncOperation syncOperation, long historyRowId,
+                int syncAdapterUid) {
             super();
+            mSyncAdapterUid = syncAdapterUid;
             mSyncOperation = syncOperation;
             mHistoryRowId = historyRowId;
             mSyncAdapter = null;
             mStartTime = SystemClock.elapsedRealtime();
             mTimeoutStartTime = mStartTime;
+            mSyncWakeLock = mSyncHandler.getSyncWakeLock(
+                    mSyncOperation.account.type, mSyncOperation.authority);
+            mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid));
+            mSyncWakeLock.acquire();
         }
 
         public void sendHeartbeat() {
@@ -887,6 +906,7 @@
         }
 
         public void onFinished(SyncResult result) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "onFinished: " + this);
             // include "this" in the message so that the handler can ignore it if this
             // ActiveSyncContext is no longer the mActiveSyncContext at message handling
             // time
@@ -934,6 +954,10 @@
             return bindResult;
         }
 
+        /**
+         * Performs the required cleanup, which is the releasing of the wakelock and
+         * unbinding from the sync adapter (if actually bound).
+         */
         protected void close() {
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Log.d(TAG, "unBindFromSyncAdapter: connection " + this);
@@ -942,6 +966,8 @@
                 mBound = false;
                 mContext.unbindService(this);
             }
+            mSyncWakeLock.setWorkSource(null);
+            mSyncWakeLock.release();
         }
 
         @Override
@@ -1001,62 +1027,28 @@
             pw.println("no alarm is scheduled (there had better not be any pending syncs)");
         }
 
-        final SyncManager.ActiveSyncContext activeSyncContext = mActiveSyncContext;
-
-        pw.print("active sync: "); pw.println(activeSyncContext);
-
         pw.print("notification info: ");
         sb.setLength(0);
         mSyncHandler.mSyncNotificationInfo.toString(sb);
         pw.println(sb.toString());
 
+        pw.println();
+        pw.println("Active Syncs: " + mActiveSyncContexts.size());
+        for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
+            final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000;
+            pw.print("  ");
+            pw.print(DateUtils.formatElapsedTime(durationInSeconds));
+            pw.print(" - ");
+            pw.print(activeSyncContext.mSyncOperation.dump(false));
+            pw.println();
+        }
+
         synchronized (mSyncQueue) {
-            pw.print("sync queue: ");
             sb.setLength(0);
             mSyncQueue.dump(sb);
-            pw.println(sb.toString());
         }
-
-        SyncInfo active = mSyncStorageEngine.getCurrentSync();
-        if (active != null) {
-            SyncStorageEngine.AuthorityInfo authority
-                    = mSyncStorageEngine.getAuthority(active.authorityId);
-            final long durationInSeconds = (now - active.startTime) / 1000;
-            pw.print("Active sync: ");
-                    pw.print(authority != null ? authority.account : "<no account>");
-                    pw.print(" ");
-                    pw.print(authority != null ? authority.authority : "<no account>");
-                    if (activeSyncContext != null) {
-                        pw.print(" ");
-                        pw.print(SyncStorageEngine.SOURCES[
-                                activeSyncContext.mSyncOperation.syncSource]);
-                    }
-                    pw.print(", duration is ");
-                    pw.println(DateUtils.formatElapsedTime(durationInSeconds));
-        } else {
-            pw.println("No sync is in progress.");
-        }
-
-        ArrayList<SyncStorageEngine.PendingOperation> ops
-                = mSyncStorageEngine.getPendingOperations();
-        if (ops != null && ops.size() > 0) {
-            pw.println();
-            pw.println("Pending Syncs");
-            final int N = ops.size();
-            for (int i=0; i<N; i++) {
-                SyncStorageEngine.PendingOperation op = ops.get(i);
-                pw.print("  #"); pw.print(i); pw.print(": account=");
-                pw.print(op.account.name); pw.print(":");
-                pw.print(op.account.type); pw.print(" authority=");
-                pw.print(op.authority); pw.print(" expedited=");
-                pw.println(op.expedited);
-                if (op.extras != null && op.extras.size() > 0) {
-                    sb.setLength(0);
-                    SyncOperation.extrasToStringBuilder(op.extras, sb, false /* asKey */);
-                    pw.print("    extras: "); pw.println(sb.toString());
-                }
-            }
-        }
+        pw.println();
+        pw.print(sb.toString());
 
         // join the installed sync adapter with the accounts list and emit for everything
         pw.println();
@@ -1259,7 +1251,7 @@
 
         /** Call to let the tracker know that the sync state may have changed */
         public synchronized void update() {
-            final boolean isSyncInProgress = mActiveSyncContext != null;
+            final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty();
             if (isSyncInProgress == mLastWasSyncing) return;
             final long now = SystemClock.elapsedRealtime();
             if (isSyncInProgress) {
@@ -1299,14 +1291,14 @@
         private static final int MESSAGE_CHECK_ALARMS = 3;
         private static final int MESSAGE_SERVICE_CONNECTED = 4;
         private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
+        private static final int MESSAGE_CANCEL = 6;
 
         public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
         private Long mAlarmScheduleTime = null;
         public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
+        private final HashMap<Pair<String, String>, PowerManager.WakeLock> mWakeLocks =
+                Maps.newHashMap();
 
-        // used to track if we have installed the error notification so that we don't reinstall
-        // it if sync is still failing
-        private boolean mErrorNotificationInstalled = false;
         private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1);
         public void onBootCompleted() {
             mBootCompleted = true;
@@ -1316,6 +1308,18 @@
             }
         }
 
+        private PowerManager.WakeLock getSyncWakeLock(String accountType, String authority) {
+            final Pair<String, String> wakeLockKey = Pair.create(accountType, authority);
+            PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey);
+            if (wakeLock == null) {
+                final String name = SYNC_WAKE_LOCK_PREFIX + "_" + authority + "_" + accountType;
+                wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
+                wakeLock.setReferenceCounted(false);
+                mWakeLocks.put(wakeLockKey, wakeLock);
+            }
+            return wakeLock;
+        }
+
         private void waitUntilReadyToRun() {
             CountDownLatch latch = mReadyToRunLatch;
             if (latch != null) {
@@ -1334,12 +1338,6 @@
          * Used to keep track of whether a sync notification is active and who it is for.
          */
         class SyncNotificationInfo {
-            // only valid if isActive is true
-            public Account account;
-
-            // only valid if isActive is true
-            public String authority;
-
             // true iff the notification manager has been asked to send the notification
             public boolean isActive = false;
 
@@ -1348,10 +1346,7 @@
             public Long startTime = null;
 
             public void toString(StringBuilder sb) {
-                sb.append("account ").append(account)
-                        .append(", authority ").append(authority)
-                        .append(", isActive ").append(isActive)
-                        .append(", startTime ").append(startTime);
+                sb.append("isActive ").append(isActive).append(", startTime ").append(startTime);
             }
 
             @Override
@@ -1367,60 +1362,72 @@
         }
 
         public void handleMessage(Message msg) {
-            Long earliestFuturePollTime = null;
+            long earliestFuturePollTime = Long.MAX_VALUE;
+            long nextPendingSyncTime = Long.MAX_VALUE;
             try {
                 waitUntilReadyToRun();
+                mSyncManagerWakeLock.acquire();
                 // Always do this first so that we be sure that any periodic syncs that
                 // are ready to run have been converted into pending syncs. This allows the
                 // logic that considers the next steps to take based on the set of pending syncs
                 // to also take into account the periodic syncs.
                 earliestFuturePollTime = scheduleReadyPeriodicSyncs();
                 switch (msg.what) {
+                    case SyncHandler.MESSAGE_CANCEL: {
+                        Pair<Account, String> payload = (Pair<Account, String>)msg.obj;
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: "
+                                    + payload.first + ", " + payload.second);
+                        }
+                        cancelActiveSyncLocked(payload.first, payload.second);
+                        nextPendingSyncTime = maybeStartNextSyncLocked();
+                        break;
+                    }
+
                     case SyncHandler.MESSAGE_SYNC_FINISHED:
                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
                             Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED");
                         }
                         SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload)msg.obj;
-                        if (mActiveSyncContext != payload.activeSyncContext) {
-                            Log.d(TAG, "handleSyncHandlerMessage: sync context doesn't match, "
-                                    + "dropping: mActiveSyncContext " + mActiveSyncContext
-                                    + " != " + payload.activeSyncContext);
-                            return;
+                        if (!isSyncStillActive(payload.activeSyncContext)) {
+                            Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
+                                    + "sync is no longer active: "
+                                    + payload.activeSyncContext);
+                            break;
                         }
-                        runSyncFinishedOrCanceled(payload.syncResult);
+                        runSyncFinishedOrCanceledLocked(payload.syncResult, payload.activeSyncContext);
 
-                        // since we are no longer syncing, check if it is time to start a new sync
-                        runStateIdle();
+                        // since a sync just finished check if it is time to start a new sync
+                        nextPendingSyncTime = maybeStartNextSyncLocked();
                         break;
 
                     case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
                         ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
-                                    + msgData.activeSyncContext
-                                    + " active is " + mActiveSyncContext);
+                                    + msgData.activeSyncContext);
                         }
                         // check that this isn't an old message
-                        if (mActiveSyncContext == msgData.activeSyncContext) {
-                            runBoundToSyncAdapter(msgData.syncAdapter);
+                        if (isSyncStillActive(msgData.activeSyncContext)) {
+                            runBoundToSyncAdapter(msgData.activeSyncContext, msgData.syncAdapter);
                         }
                         break;
                     }
 
                     case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
-                        ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
+                        final ActiveSyncContext currentSyncContext =
+                                ((ServiceConnectionData)msg.obj).activeSyncContext;
                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
-                                    + msgData.activeSyncContext
-                                    + " active is " + mActiveSyncContext);
+                                    + currentSyncContext);
                         }
                         // check that this isn't an old message
-                        if (mActiveSyncContext == msgData.activeSyncContext) {
+                        if (isSyncStillActive(currentSyncContext)) {
                             // cancel the sync if we have a syncadapter, which means one is
                             // outstanding
-                            if (mActiveSyncContext.mSyncAdapter != null) {
+                            if (currentSyncContext.mSyncAdapter != null) {
                                 try {
-                                    mActiveSyncContext.mSyncAdapter.cancelSync(mActiveSyncContext);
+                                    currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext);
                                 } catch (RemoteException e) {
                                     // we don't need to retry this in this case
                                 }
@@ -1430,11 +1437,10 @@
                             // which is a soft error
                             SyncResult syncResult = new SyncResult();
                             syncResult.stats.numIoExceptions++;
-                            runSyncFinishedOrCanceled(syncResult);
+                            runSyncFinishedOrCanceledLocked(syncResult, currentSyncContext);
 
-                            // since we are no longer syncing, check if it is time to start a new
-                            // sync
-                            runStateIdle();
+                            // since a sync just finished check if it is time to start a new sync
+                            nextPendingSyncTime = maybeStartNextSyncLocked();
                         }
 
                         break;
@@ -1447,22 +1453,7 @@
                         }
                         mAlarmScheduleTime = null;
                         try {
-                            if (mActiveSyncContext != null) {
-                                if (isLoggable) {
-                                    Log.v(TAG, "handleSyncHandlerMessage: sync context is active");
-                                }
-                                runStateSyncing();
-                            }
-
-                            // if the above call to runStateSyncing() resulted in the end of a sync,
-                            // check if it is time to start a new sync
-                            if (mActiveSyncContext == null) {
-                                if (isLoggable) {
-                                    Log.v(TAG, "handleSyncHandlerMessage: "
-                                            + "sync context is not active");
-                                }
-                                runStateIdle();
-                            }
+                            nextPendingSyncTime = maybeStartNextSyncLocked();
                         } finally {
                             mHandleAlarmWakeLock.release();
                         }
@@ -1473,18 +1464,14 @@
                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
                             Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS");
                         }
-                        // we do all the work for this case in the finally block
+                        nextPendingSyncTime = maybeStartNextSyncLocked();
                         break;
                 }
             } finally {
-                final boolean isSyncInProgress = mActiveSyncContext != null;
-                if (!isSyncInProgress) {
-                    mSyncWakeLock.release();
-                }
-                manageSyncNotification();
-                manageErrorNotification();
-                manageSyncAlarm(earliestFuturePollTime);
+                manageSyncNotificationLocked();
+                manageSyncAlarmLocked(earliestFuturePollTime, nextPendingSyncTime);
                 mSyncTimeTracker.update();
+                mSyncManagerWakeLock.release();
             }
         }
 
@@ -1493,10 +1480,10 @@
          * @return the desired start time of the earliest future  periodic sync operation,
          * in milliseconds since boot
          */
-        private Long scheduleReadyPeriodicSyncs() {
+        private long scheduleReadyPeriodicSyncs() {
             final boolean backgroundDataUsageAllowed =
                     getConnectivityManager().getBackgroundDataSetting();
-            Long earliestFuturePollTime = null;
+            long earliestFuturePollTime = Long.MAX_VALUE;
             if (!backgroundDataUsageAllowed || !mSyncStorageEngine.getMasterSyncAutomatically()) {
                 return earliestFuturePollTime;
             }
@@ -1526,23 +1513,27 @@
                     long nextPollTimeAbsolute = lastPollTimeAbsolute + periodInSeconds * 1000;
                     // if it is ready to run then schedule it and mark it as having been scheduled
                     if (nextPollTimeAbsolute <= nowAbsolute) {
+                        final Pair<Long, Long> backoff =
+                                mSyncStorageEngine.getBackoff(info.account, info.authority);
                         scheduleSyncOperation(
                                 new SyncOperation(info.account, SyncStorageEngine.SOURCE_PERIODIC,
-                                        info.authority, extras, 0 /* delay */));
+                                        info.authority, extras, 0 /* delay */,
+                                        backoff != null ? backoff.first : 0,
+                                        mSyncStorageEngine.getDelayUntilTime(
+                                                info.account, info.authority)));
                         status.setPeriodicSyncTime(i, nowAbsolute);
                     } else {
                         // it isn't ready to run, remember this time if it is earlier than
                         // earliestFuturePollTime
-                        if (earliestFuturePollTime == null
-                                || nextPollTimeAbsolute < earliestFuturePollTime) {
+                        if (nextPollTimeAbsolute < earliestFuturePollTime) {
                             earliestFuturePollTime = nextPollTimeAbsolute;
                         }
                     }
                 }
             }
 
-            if (earliestFuturePollTime == null) {
-                return null;
+            if (earliestFuturePollTime == Long.MAX_VALUE) {
+                return Long.MAX_VALUE;
             }
 
             // convert absolute time to elapsed time
@@ -1552,47 +1543,23 @@
                       : (earliestFuturePollTime - nowAbsolute));
         }
 
-        private void runStateSyncing() {
-            // if the sync timeout has been reached then cancel it
-            ActiveSyncContext activeSyncContext = mActiveSyncContext;
-
-            final long now = SystemClock.elapsedRealtime();
-            if (now > activeSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC) {
-                Pair<SyncOperation, Long> nextOpAndRunTime;
-                synchronized (mSyncQueue) {
-                    nextOpAndRunTime = mSyncQueue.nextOperation();
-                }
-                if (nextOpAndRunTime != null && nextOpAndRunTime.second <= now) {
-                    Log.d(TAG, "canceling and rescheduling sync because it ran too long: "
-                            + activeSyncContext.mSyncOperation);
-                    scheduleSyncOperation(new SyncOperation(activeSyncContext.mSyncOperation));
-                    sendSyncFinishedOrCanceledMessage(activeSyncContext,
-                            null /* no result since this is a cancel */);
-                } else {
-                    activeSyncContext.mTimeoutStartTime = now + MAX_TIME_PER_SYNC;
-                }
-            }
-
-            // no need to schedule an alarm, as that will be done by our caller.
-        }
-
-        private void runStateIdle() {
-            boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
-            if (isLoggable) Log.v(TAG, "runStateIdle");
+        private long maybeStartNextSyncLocked() {
+            final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+            if (isLoggable) Log.v(TAG, "maybeStartNextSync");
 
             // If we aren't ready to run (e.g. the data connection is down), get out.
             if (!mDataConnectionIsConnected) {
                 if (isLoggable) {
-                    Log.v(TAG, "runStateIdle: no data connection, skipping");
+                    Log.v(TAG, "maybeStartNextSync: no data connection, skipping");
                 }
-                return;
+                return Long.MAX_VALUE;
             }
 
             if (mStorageIsLow) {
                 if (isLoggable) {
-                    Log.v(TAG, "runStateIdle: memory low, skipping");
+                    Log.v(TAG, "maybeStartNextSync: memory low, skipping");
                 }
-                return;
+                return Long.MAX_VALUE;
             }
 
             // If the accounts aren't known yet then we aren't ready to run. We will be kicked
@@ -1600,46 +1567,56 @@
             Account[] accounts = mAccounts;
             if (accounts == INITIAL_ACCOUNTS_ARRAY) {
                 if (isLoggable) {
-                    Log.v(TAG, "runStateIdle: accounts not known, skipping");
+                    Log.v(TAG, "maybeStartNextSync: accounts not known, skipping");
                 }
-                return;
+                return Long.MAX_VALUE;
             }
 
             // Otherwise consume SyncOperations from the head of the SyncQueue until one is
             // found that is runnable (not disabled, etc). If that one is ready to run then
             // start it, otherwise just get out.
-            SyncOperation op;
-            int syncableState;
             final boolean backgroundDataUsageAllowed =
                     getConnectivityManager().getBackgroundDataSetting();
             final boolean masterSyncAutomatically = mSyncStorageEngine.getMasterSyncAutomatically();
 
-            synchronized (mSyncQueue) {
-                final long now = SystemClock.elapsedRealtime();
-                while (true) {
-                    Pair<SyncOperation, Long> nextOpAndRunTime = mSyncQueue.nextOperation();
-                    if (nextOpAndRunTime == null || nextOpAndRunTime.second > now) {
-                        if (isLoggable) {
-                            Log.v(TAG, "runStateIdle: no more ready sync operations, returning");
-                        }
-                        return;
-                    }
-                    op = nextOpAndRunTime.first;
+            final long now = SystemClock.elapsedRealtime();
 
-                    // we are either going to run this sync or drop it so go ahead and
-                    // remove it from the queue now
-                    mSyncQueue.remove(op);
+            // will be set to the next time that a sync should be considered for running
+            long nextReadyToRunTime = Long.MAX_VALUE;
+
+            // order the sync queue, dropping syncs that are not allowed
+            ArrayList<SyncOperation> operations = new ArrayList<SyncOperation>();
+            synchronized (mSyncQueue) {
+                if (isLoggable) {
+                    Log.v(TAG, "build the operation array, syncQueue size is "
+                        + mSyncQueue.mOperationsMap.size());
+                }
+                Iterator<SyncOperation> operationIterator =
+                        mSyncQueue.mOperationsMap.values().iterator();
+                while (operationIterator.hasNext()) {
+                    final SyncOperation op = operationIterator.next();
 
                     // drop the sync if the account of this operation no longer exists
                     if (!ArrayUtils.contains(mAccounts, op.account)) {
+                        operationIterator.remove();
+                        mSyncStorageEngine.deleteFromPending(op.pendingOperation);
                         continue;
                     }
 
-
-                    // drop this sync request if it isn't syncable, intializing the sync adapter
-                    // if the syncable state is set to "unknown"
-                    syncableState = mSyncStorageEngine.getIsSyncable(op.account, op.authority);
+                    // drop this sync request if it isn't syncable
+                    int syncableState = mSyncStorageEngine.getIsSyncable(op.account, op.authority);
                     if (syncableState == 0) {
+                        operationIterator.remove();
+                        mSyncStorageEngine.deleteFromPending(op.pendingOperation);
+                        continue;
+                    }
+
+                    // if the next run time is in the future, meaning there are no syncs ready
+                    // to run, return the time
+                    if (op.effectiveRunTime > now) {
+                        if (nextReadyToRunTime > op.effectiveRunTime) {
+                            nextReadyToRunTime = op.effectiveRunTime;
+                        }
                         continue;
                     }
 
@@ -1651,30 +1628,139 @@
                                 || !backgroundDataUsageAllowed
                                 || !mSyncStorageEngine.getSyncAutomatically(
                                        op.account, op.authority))) {
+                        operationIterator.remove();
+                        mSyncStorageEngine.deleteFromPending(op.pendingOperation);
                         continue;
                     }
 
-                    // go ahead and try to sync this syncOperation
-                    break;
-                }
-
-                // We will do this sync. Run it outside of the synchronized block.
-                if (isLoggable) {
-                    Log.v(TAG, "runStateIdle: we are going to sync " + op);
+                    operations.add(op);
                 }
             }
 
-            // convert the op into an initialization sync if the syncable state is "unknown" and
-            // op isn't already an initialization sync. If it is marked syncable then convert
-            // this into a regular sync
-            final boolean initializeIsSet =
-                    op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
-            if (syncableState < 0 && !initializeIsSet) {
-                op.extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
-                op = new SyncOperation(op);
-            } else if (syncableState > 0 && initializeIsSet) {
-                op.extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
-                op = new SyncOperation(op);
+            // find the next operation to dispatch, if one is ready
+            // iterate from the top, keep issuing (while potentially cancelling existing syncs)
+            // until the quotas are filled.
+            // once the quotas are filled iterate once more to find when the next one would be
+            // (also considering pre-emption reasons).
+            if (isLoggable) Log.v(TAG, "sort the candidate operations, size " + operations.size());
+            Collections.sort(operations);
+            if (isLoggable) Log.v(TAG, "dispatch all ready sync operations");
+            for (int i = 0, N = operations.size(); i < N; i++) {
+                final SyncOperation candidate = operations.get(i);
+                final boolean candidateIsInitialization = candidate.isInitialization();
+
+                int numInit = 0;
+                int numRegular = 0;
+                ActiveSyncContext conflict = null;
+                ActiveSyncContext longRunning = null;
+                ActiveSyncContext toReschedule = null;
+
+                for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
+                    final SyncOperation activeOp = activeSyncContext.mSyncOperation;
+                    if (activeOp.isInitialization()) {
+                        numInit++;
+                    } else {
+                        numRegular++;
+                    }
+                    if (activeOp.account.type.equals(candidate.account.type)
+                            && activeOp.authority.equals(candidate.authority)) {
+                        conflict = activeSyncContext;
+                        // don't break out since we want to do a full count of the varieties
+                    } else {
+                        if (candidateIsInitialization == activeOp.isInitialization()
+                                && activeSyncContext.mStartTime + MAX_TIME_PER_SYNC < now) {
+                            longRunning = activeSyncContext;
+                            // don't break out since we want to do a full count of the varieties
+                        }
+                    }
+                }
+
+                if (isLoggable) {
+                    Log.v(TAG, "candidate " + (i + 1) + " of " + N + ": " + candidate);
+                    Log.v(TAG, "  numActiveInit=" + numInit + ", numActiveRegular=" + numRegular);
+                    Log.v(TAG, "  longRunning: " + longRunning);
+                    Log.v(TAG, "  conflict: " + conflict);
+                }
+
+                if (conflict != null) {
+                    if (candidateIsInitialization && !conflict.mSyncOperation.isInitialization()
+                            && numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS) {
+                        toReschedule = conflict;
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "canceling and rescheduling sync since an initialization "
+                                    + "takes higher priority, " + conflict);
+                        }
+                    } else if (candidate.expedited && !conflict.mSyncOperation.expedited
+                            && (candidateIsInitialization
+                                == conflict.mSyncOperation.isInitialization())) {
+                        toReschedule = conflict;
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "canceling and rescheduling sync since an expedited "
+                                    + "takes higher priority, " + conflict);
+                        }
+                    } else {
+                        continue;
+                    }
+                } else {
+                    final boolean roomAvailable = candidateIsInitialization 
+                            ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS 
+                            : numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS;
+                    if (roomAvailable) {
+                        // dispatch candidate
+                    } else if (longRunning != null
+                            && (candidateIsInitialization
+                                == longRunning.mSyncOperation.isInitialization())) {
+                        toReschedule = longRunning;
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "canceling and rescheduling sync since it ran roo long, "
+                                    + longRunning);
+                        }
+                    } else {
+                        continue;
+                    }
+                }
+
+                if (toReschedule != null) {
+                    runSyncFinishedOrCanceledLocked(null, toReschedule);
+                    scheduleSyncOperation(toReschedule.mSyncOperation);
+                }
+    
+                synchronized (mSyncQueue){
+                    mSyncQueue.remove(candidate);
+                }
+                dispatchSyncOperation(candidate);
+            }
+
+            return nextReadyToRunTime;
+     }
+
+        private boolean dispatchSyncOperation(SyncOperation op) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "maybeStartNextSync: we are going to sync " + op);
+                Log.v(TAG, "num active syncs: " + mActiveSyncContexts.size());
+                for (ActiveSyncContext syncContext : mActiveSyncContexts) {
+                    Log.v(TAG, syncContext.toString());
+                }
+            }
+
+            // if this is an initialization sync and there is already a sync running with
+            // the same account type and authority cancel that sync before starting this one
+            // since otherwise the syncadapter will likely reject this request
+            if (op.isInitialization()) {
+                Iterator<ActiveSyncContext> iterator = mActiveSyncContexts.iterator();
+                while (iterator.hasNext()) {
+                    ActiveSyncContext syncContext = iterator.next();
+                    if (!syncContext.mSyncOperation.isInitialization()
+                            && syncContext.mSyncOperation.account.type.equals(op.account.type)
+                            && syncContext.mSyncOperation.authority.equals(op.authority)) {
+                        Log.d(TAG, "canceling and rescheduling " + syncContext.mSyncOperation
+                                + " since we are about to start a sync that used the "
+                                + "same sync adapter, " + op);
+                        iterator.remove();
+                        runSyncFinishedOrCanceledLocked(null, syncContext);
+                        scheduleSyncOperation(syncContext.mSyncOperation);
+                    }
+                }
             }
 
             // connect to the sync adapter
@@ -1685,61 +1771,70 @@
                 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
                         + ", removing settings for it");
                 mSyncStorageEngine.removeAuthority(op.account, op.authority);
-                runStateIdle();
-                return;
+                return false;
             }
 
             ActiveSyncContext activeSyncContext =
-                    new ActiveSyncContext(op, insertStartSyncEvent(op));
-            mActiveSyncContext = activeSyncContext;
+                    new ActiveSyncContext(op, insertStartSyncEvent(op), syncAdapterInfo.uid);
+            activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext);
+            mActiveSyncContexts.add(activeSyncContext);
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Log.v(TAG, "runStateIdle: setting mActiveSyncContext to " + mActiveSyncContext);
+                Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
             }
-            mSyncStorageEngine.setActiveSync(mActiveSyncContext);
             if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo)) {
                 Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo);
-                mActiveSyncContext.close();
-                mActiveSyncContext = null;
-                mSyncStorageEngine.setActiveSync(mActiveSyncContext);
-                mSyncWakeLock.setWorkSource(null);
-                runStateIdle();
-                return;
+                closeActiveSyncContext(activeSyncContext);
+                return false;
             }
 
-            mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterInfo.uid));
-            mSyncWakeLock.acquire();
-            // no need to schedule an alarm, as that will be done by our caller.
-
-            // the next step will occur when we get either a timeout or a
-            // MESSAGE_SERVICE_CONNECTED or MESSAGE_SERVICE_DISCONNECTED message
+            return true;
         }
 
-        private void runBoundToSyncAdapter(ISyncAdapter syncAdapter) {
-            mActiveSyncContext.mSyncAdapter = syncAdapter;
-            final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
+        private void runBoundToSyncAdapter(ActiveSyncContext activeSyncContext,
+              ISyncAdapter syncAdapter) {
+            activeSyncContext.mSyncAdapter = syncAdapter;
+            final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
             try {
-                syncAdapter.startSync(mActiveSyncContext, syncOperation.authority,
+                syncAdapter.startSync(activeSyncContext, syncOperation.authority,
                         syncOperation.account, syncOperation.extras);
             } catch (RemoteException remoteExc) {
-                Log.d(TAG, "runStateIdle: caught a RemoteException, rescheduling", remoteExc);
-                mActiveSyncContext.close();
-                mActiveSyncContext = null;
-                mSyncStorageEngine.setActiveSync(mActiveSyncContext);
+                Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc);
+                closeActiveSyncContext(activeSyncContext);
                 increaseBackoffSetting(syncOperation);
                 scheduleSyncOperation(new SyncOperation(syncOperation));
             } catch (RuntimeException exc) {
-                mActiveSyncContext.close();
-                mActiveSyncContext = null;
-                mSyncStorageEngine.setActiveSync(mActiveSyncContext);
+                closeActiveSyncContext(activeSyncContext);
                 Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
             }
         }
 
-        private void runSyncFinishedOrCanceled(SyncResult syncResult) {
+        private void cancelActiveSyncLocked(Account account, String authority) {
+            ArrayList<ActiveSyncContext> activeSyncs =
+                    new ArrayList<ActiveSyncContext>(mActiveSyncContexts);
+            for (ActiveSyncContext activeSyncContext : activeSyncs) {
+                if (activeSyncContext != null) {
+                    // if an authority was specified then only cancel the sync if it matches
+                    if (account != null) {
+                        if (!account.equals(activeSyncContext.mSyncOperation.account)) {
+                            return;
+                        }
+                    }
+                    // if an account was specified then only cancel the sync if it matches
+                    if (authority != null) {
+                        if (!authority.equals(activeSyncContext.mSyncOperation.authority)) {
+                            return;
+                        }
+                    }
+                    runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */,
+                            activeSyncContext);
+                }
+            }
+        }
+
+        private void runSyncFinishedOrCanceledLocked(SyncResult syncResult,
+                ActiveSyncContext activeSyncContext) {
             boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
-            final ActiveSyncContext activeSyncContext = mActiveSyncContext;
-            mActiveSyncContext = null;
-            mSyncStorageEngine.setActiveSync(mActiveSyncContext);
+            closeActiveSyncContext(activeSyncContext);
 
             final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
 
@@ -1759,16 +1854,6 @@
                     // TODO: set these correctly when the SyncResult is extended to include it
                     downstreamActivity = 0;
                     upstreamActivity = 0;
-                    clearBackoffSetting(syncOperation);
-                    // if this was an initialization sync and the sync adapter is now
-                    // marked syncable then reschedule the sync. The next time it runs it
-                    // will be made into a regular sync.
-                    if (syncOperation.extras.getBoolean(
-                                ContentResolver.SYNC_EXTRAS_INITIALIZE, false)
-                            && mSyncStorageEngine.getIsSyncable(
-                                syncOperation.account, syncOperation.authority) > 0) {
-                        scheduleSyncOperation(new SyncOperation(syncOperation));
-                    }
                 } else {
                     Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult);
                     // the operation failed so increase the backoff time
@@ -1803,8 +1888,6 @@
             stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
                     upstreamActivity, downstreamActivity, elapsedTime);
 
-            activeSyncContext.close();
-
             if (syncResult != null && syncResult.tooManyDeletions) {
                 installHandleTooManyDeletesNotification(syncOperation.account,
                         syncOperation.authority, syncResult.stats.numDeletes);
@@ -1815,11 +1898,18 @@
 
             if (syncResult != null && syncResult.fullSyncRequested) {
                 scheduleSyncOperation(new SyncOperation(syncOperation.account,
-                        syncOperation.syncSource, syncOperation.authority, new Bundle(), 0));
+                        syncOperation.syncSource, syncOperation.authority, new Bundle(), 0,
+                        syncOperation.backoff, syncOperation.delayUntil));
             }
             // no need to schedule an alarm, as that will be done by our caller.
         }
 
+        private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) {
+            activeSyncContext.close();
+            mActiveSyncContexts.remove(activeSyncContext);
+            mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo);
+        }
+
         /**
          * Convert the error-containing SyncResult into the Sync.History error number. Since
          * the SyncResult may indicate multiple errors at once, this method just returns the
@@ -1849,11 +1939,11 @@
             throw new IllegalStateException("we are not in an error state, " + syncResult);
         }
 
-        private void manageSyncNotification() {
+        private void manageSyncNotificationLocked() {
             boolean shouldCancel;
             boolean shouldInstall;
 
-            if (mActiveSyncContext == null) {
+            if (mActiveSyncContexts.isEmpty()) {
                 mSyncNotificationInfo.startTime = null;
 
                 // we aren't syncing. if the notification is active then remember that we need
@@ -1862,34 +1952,38 @@
                 shouldInstall = false;
             } else {
                 // we are syncing
-                final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
-
                 final long now = SystemClock.elapsedRealtime();
                 if (mSyncNotificationInfo.startTime == null) {
                     mSyncNotificationInfo.startTime = now;
                 }
 
-                // cancel the notification if it is up and the authority or account is wrong
-                shouldCancel = mSyncNotificationInfo.isActive &&
-                        (!syncOperation.authority.equals(mSyncNotificationInfo.authority)
-                        || !syncOperation.account.equals(mSyncNotificationInfo.account));
-
-                // there are four cases:
-                // - the notification is up and there is no change: do nothing
-                // - the notification is up but we should cancel since it is stale:
-                //   need to install
+                // there are three cases:
+                // - the notification is up: do nothing
                 // - the notification is not up but it isn't time yet: don't install
                 // - the notification is not up and it is time: need to install
 
                 if (mSyncNotificationInfo.isActive) {
-                    shouldInstall = shouldCancel;
+                    shouldInstall = shouldCancel = false;
                 } else {
+                    // it isn't currently up, so there is nothing to cancel
+                    shouldCancel = false;
+
                     final boolean timeToShowNotification =
                             now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY;
-                    // show the notification immediately if this is a manual sync
-                    final boolean manualSync = syncOperation.extras
-                            .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
-                    shouldInstall = timeToShowNotification || manualSync;
+                    if (timeToShowNotification) {
+                        shouldInstall = true;
+                    } else {
+                        // show the notification immediately if this is a manual sync
+                        shouldInstall = false;
+                        for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
+                            final boolean manualSync = activeSyncContext.mSyncOperation.extras
+                                    .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
+                            if (manualSync) {
+                                shouldInstall = true;
+                                break;
+                            }
+                        }
+                    }
                 }
             }
 
@@ -1900,94 +1994,82 @@
             }
 
             if (shouldInstall) {
-                SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
                 mNeedSyncActiveNotification = true;
                 sendSyncStateIntent();
                 mSyncNotificationInfo.isActive = true;
-                mSyncNotificationInfo.account = syncOperation.account;
-                mSyncNotificationInfo.authority = syncOperation.authority;
             }
         }
 
-        /**
-         * Check if there were any long-lasting errors, if so install the error notification,
-         * otherwise cancel the error notification.
-         */
-        private void manageErrorNotification() {
-            //
-            long when = mSyncStorageEngine.getInitialSyncFailureTime();
-            if ((when > 0) && (when + ERROR_NOTIFICATION_DELAY_MS < System.currentTimeMillis())) {
-                if (!mErrorNotificationInstalled) {
-                    mNeedSyncErrorNotification = true;
-                    sendSyncStateIntent();
-                }
-                mErrorNotificationInstalled = true;
-            } else {
-                if (mErrorNotificationInstalled) {
-                    mNeedSyncErrorNotification = false;
-                    sendSyncStateIntent();
-                }
-                mErrorNotificationInstalled = false;
-            }
-        }
-
-        private void manageSyncAlarm(Long earliestFuturePollElapsedTime) {
+        private void manageSyncAlarmLocked(long nextPeriodicEventElapsedTime,
+                long nextPendingEventElapsedTime) {
             // in each of these cases the sync loop will be kicked, which will cause this
             // method to be called again
             if (!mDataConnectionIsConnected) return;
             if (mStorageIsLow) return;
 
-            final long now = SystemClock.elapsedRealtime();
+            // When the status bar notification should be raised
+            final long notificationTime =
+                    (!mSyncHandler.mSyncNotificationInfo.isActive
+                            && mSyncHandler.mSyncNotificationInfo.startTime != null)
+                            ? mSyncHandler.mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY
+                            : Long.MAX_VALUE;
 
-            // Compute the alarm fire time:
-            // - not syncing: time of the next sync operation
-            // - syncing, no notification: time from sync start to notification create time
-            // - syncing, with notification: time till timeout of the active sync operation
-            Long alarmTime;
-            ActiveSyncContext activeSyncContext = mActiveSyncContext;
-            if (activeSyncContext == null) {
-                synchronized (mSyncQueue) {
-                    final Pair<SyncOperation, Long> candidate = mSyncQueue.nextOperation();
-                    if (earliestFuturePollElapsedTime == null && candidate == null) {
-                        alarmTime = null;
-                    } else if (earliestFuturePollElapsedTime == null) {
-                        alarmTime = candidate.second;
-                    } else if (candidate == null) {
-                        alarmTime = earliestFuturePollElapsedTime;
-                    } else {
-                        alarmTime = Math.min(earliestFuturePollElapsedTime, candidate.second);
-                    }
+            // When we should consider canceling an active sync
+            long earliestTimeoutTime = Long.MAX_VALUE;
+            for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
+                final long currentSyncTimeoutTime =
+                        currentSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC;
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "manageSyncAlarm: active sync, mTimeoutStartTime + MAX is "
+                            + currentSyncTimeoutTime);
                 }
-            } else {
-                final long notificationTime =
-                        mSyncHandler.mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY;
-                final long timeoutTime =
-                        mActiveSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC;
-                if (mSyncHandler.mSyncNotificationInfo.isActive) {
-                    alarmTime = timeoutTime;
-                } else {
-                    alarmTime = Math.min(notificationTime, timeoutTime);
+                if (earliestTimeoutTime > currentSyncTimeoutTime) {
+                    earliestTimeoutTime = currentSyncTimeoutTime;
                 }
             }
 
-            // adjust the alarmTime so that we will wake up when it is time to
-            // install the error notification
-            if (!mErrorNotificationInstalled) {
-                long when = mSyncStorageEngine.getInitialSyncFailureTime();
-                if (when > 0) {
-                    when += ERROR_NOTIFICATION_DELAY_MS;
-                    // convert when fron absolute time to elapsed run time
-                    long delay = when - System.currentTimeMillis();
-                    when = now + delay;
-                    alarmTime = alarmTime != null ? Math.min(alarmTime, when) : when;
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "manageSyncAlarm: notificationTime is " + notificationTime);
+            }
+
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "manageSyncAlarm: earliestTimeoutTime is " + earliestTimeoutTime);
+            }
+
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "manageSyncAlarm: nextPeriodicEventElapsedTime is "
+                        + nextPeriodicEventElapsedTime);
+            }
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "manageSyncAlarm: nextPendingEventElapsedTime is "
+                        + nextPendingEventElapsedTime);
+            }
+
+            long alarmTime = Math.min(notificationTime, earliestTimeoutTime);
+            alarmTime = Math.min(alarmTime, nextPeriodicEventElapsedTime);
+            alarmTime = Math.min(alarmTime, nextPendingEventElapsedTime);
+
+            // Bound the alarm time.
+            final long now = SystemClock.elapsedRealtime();
+            if (alarmTime < now + SYNC_ALARM_TIMEOUT_MIN) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "manageSyncAlarm: the alarmTime is too small, "
+                            + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN));
                 }
+                alarmTime = now + SYNC_ALARM_TIMEOUT_MIN;
+            } else if (alarmTime > now + SYNC_ALARM_TIMEOUT_MAX) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "manageSyncAlarm: the alarmTime is too large, "
+                            + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN));
+                }
+                alarmTime = now + SYNC_ALARM_TIMEOUT_MAX;
             }
 
             // determine if we need to set or cancel the alarm
             boolean shouldSet = false;
             boolean shouldCancel = false;
             final boolean alarmIsActive = mAlarmScheduleTime != null;
-            final boolean needAlarm = alarmTime != null;
+            final boolean needAlarm = alarmTime != Long.MAX_VALUE;
             if (needAlarm) {
                 if (!alarmIsActive || alarmTime < mAlarmScheduleTime) {
                     shouldSet = true;
@@ -1999,6 +2081,11 @@
             // set or cancel the alarm as directed
             ensureAlarmService();
             if (shouldSet) {
+                if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                    Log.v(TAG, "requesting that the alarm manager wake us up at elapsed time "
+                            + alarmTime + ", now is " + now + ", " + ((alarmTime - now) / 1000)
+                            + " secs from now");
+                }
                 mAlarmScheduleTime = alarmTime;
                 mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime,
                         mSyncAlarmIntent);
@@ -2012,7 +2099,7 @@
             Intent syncStateIntent = new Intent(Intent.ACTION_SYNC_STATE_CHANGED);
             syncStateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             syncStateIntent.putExtra("active", mNeedSyncActiveNotification);
-            syncStateIntent.putExtra("failing", mNeedSyncErrorNotification);
+            syncStateIntent.putExtra("failing", false);
             mContext.sendBroadcast(syncStateIntent);
         }
 
@@ -2101,4 +2188,13 @@
                     resultMessage, downstreamActivity, upstreamActivity);
         }
     }
+
+    private boolean isSyncStillActive(ActiveSyncContext activeSyncContext) {
+        for (ActiveSyncContext sync : mActiveSyncContexts) {
+            if (sync == activeSyncContext) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java
index b0160885..3d7f3fbf 100644
--- a/core/java/android/content/SyncOperation.java
+++ b/core/java/android/content/SyncOperation.java
@@ -33,9 +33,12 @@
     public long earliestRunTime;
     public boolean expedited;
     public SyncStorageEngine.PendingOperation pendingOperation;
+    public Long backoff;
+    public long delayUntil;
+    public long effectiveRunTime;
 
     public SyncOperation(Account account, int source, String authority, Bundle extras,
-            long delayInMs) {
+            long delayInMs, long backoff, long delayUntil) {
         this.account = account;
         this.syncSource = source;
         this.authority = authority;
@@ -48,6 +51,8 @@
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS);
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_EXPEDITED);
         removeFalseExtra(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS);
+        this.delayUntil = delayUntil;
+        this.backoff = backoff;
         final long now = SystemClock.elapsedRealtime();
         if (delayInMs < 0) {
             this.expedited = true;
@@ -56,6 +61,7 @@
             this.expedited = false;
             this.earliestRunTime = now + delayInMs;
         }
+        updateEffectiveRunTime();
         this.key = toKey();
     }
 
@@ -72,49 +78,78 @@
         this.extras = new Bundle(other.extras);
         this.expedited = other.expedited;
         this.earliestRunTime = SystemClock.elapsedRealtime();
+        this.backoff = other.backoff;
+        this.delayUntil = other.delayUntil;
+        this.updateEffectiveRunTime();
         this.key = toKey();
     }
 
     public String toString() {
+        return dump(true);
+    }
+
+    public String dump(boolean useOneLine) {
         StringBuilder sb = new StringBuilder();
-        sb.append("authority: ").append(authority);
-        sb.append(" account: ").append(account);
-        sb.append(" extras: ");
-        extrasToStringBuilder(extras, sb, false /* asKey */);
-        sb.append(" syncSource: ").append(syncSource);
-        sb.append(" when: ").append(earliestRunTime);
-        sb.append(" expedited: ").append(expedited);
+        sb.append(account.name);
+        sb.append(" (" + account.type + ")");
+        sb.append(", " + authority);
+        sb.append(", ");
+        sb.append(SyncStorageEngine.SOURCES[syncSource]);
+        sb.append(", earliestRunTime " + earliestRunTime);
+        if (expedited) {
+            sb.append(", EXPEDITED");
+        }
+        if (!useOneLine && !extras.keySet().isEmpty()) {
+            sb.append("\n    ");
+            extrasToStringBuilder(extras, sb);
+        }
         return sb.toString();
     }
 
+    public boolean isInitialization() {
+        return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
+    }
+
+    public boolean ignoreBackoff() {
+        return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false);
+    }
+
     private String toKey() {
         StringBuilder sb = new StringBuilder();
         sb.append("authority: ").append(authority);
-	sb.append(" account {name=" + account.name + ", type=" + account.type + "}");
+    	sb.append(" account {name=" + account.name + ", type=" + account.type + "}");
         sb.append(" extras: ");
-        extrasToStringBuilder(extras, sb, true /* asKey */);
+        extrasToStringBuilder(extras, sb);
         return sb.toString();
     }
 
-    public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb, boolean asKey) {
+    public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
         sb.append("[");
         for (String key : bundle.keySet()) {
-            // if we are writing this as a key don't consider whether this
-            // is an initialization sync or not when computing the key since
-            // we set this flag appropriately when dispatching the sync request.
-            if (asKey && ContentResolver.SYNC_EXTRAS_INITIALIZE.equals(key)) {
-                continue;
-            }
             sb.append(key).append("=").append(bundle.get(key)).append(" ");
         }
         sb.append("]");
     }
 
+    public void updateEffectiveRunTime() {
+        effectiveRunTime = ignoreBackoff()
+                ? earliestRunTime
+                : Math.max(
+                    Math.max(earliestRunTime, delayUntil),
+                    backoff);
+    }
+
     public int compareTo(Object o) {
         SyncOperation other = (SyncOperation)o;
-        if (earliestRunTime == other.earliestRunTime) {
+
+        if (expedited != other.expedited) {
+            return expedited ? -1 : 1;
+        }
+
+        if (effectiveRunTime == other.effectiveRunTime) {
             return 0;
         }
-        return (earliestRunTime < other.earliestRunTime) ? -1 : 1;
+
+        return effectiveRunTime < other.effectiveRunTime ? -1 : 1;
     }
 }
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index 28baa0d..f826147 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -16,17 +16,18 @@
 
 package android.content;
 
-import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
+import android.os.SystemClock;
+import android.text.format.DateUtils;
 import android.util.Pair;
 import android.util.Log;
 import android.accounts.Account;
 
-import java.util.HashMap;
 import java.util.ArrayList;
-import java.util.Map;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
 
 /**
  *
@@ -38,7 +39,7 @@
 
     // A Map of SyncOperations operationKey -> SyncOperation that is designed for
     // quick lookup of an enqueued SyncOperation.
-    private final HashMap<String, SyncOperation> mOperationsMap = Maps.newHashMap();
+    public final HashMap<String, SyncOperation> mOperationsMap = Maps.newHashMap();
 
     public SyncQueue(SyncStorageEngine syncStorageEngine) {
         mSyncStorageEngine = syncStorageEngine;
@@ -47,8 +48,11 @@
         final int N = ops.size();
         for (int i=0; i<N; i++) {
             SyncStorageEngine.PendingOperation op = ops.get(i);
+            final Pair<Long, Long> backoff = syncStorageEngine.getBackoff(op.account, op.authority);
             SyncOperation syncOperation = new SyncOperation(
-                    op.account, op.syncSource, op.authority, op.extras, 0 /* delay */);
+                    op.account, op.syncSource, op.authority, op.extras, 0 /* delay */,
+                    backoff != null ? backoff.first : 0,
+                    syncStorageEngine.getDelayUntilTime(op.account, op.authority));
             syncOperation.expedited = op.expedited;
             syncOperation.pendingOperation = op;
             add(syncOperation, op);
@@ -119,65 +123,26 @@
         }
     }
 
-    /**
-     * Find the operation that should run next. Operations are sorted by their earliestRunTime,
-     * prioritizing first those with a syncable state of "unknown" that aren't retries then
-     * expedited operations.
-     * The earliestRunTime is adjusted by the sync adapter's backoff and delayUntil times, if any.
-     * @return the operation that should run next and when it should run. The time may be in
-     * the future. It is expressed in milliseconds since boot.
-     */
-    public Pair<SyncOperation, Long> nextOperation() {
-        SyncOperation best = null;
-        long bestRunTime = 0;
-        boolean bestSyncableIsUnknownAndNotARetry = false;
+    public void onBackoffChanged(Account account, String providerName, long backoff) {
+        // for each op that matches the account and provider update its
+        // backoff and effectiveStartTime
         for (SyncOperation op : mOperationsMap.values()) {
-            long opRunTime = op.earliestRunTime;
-            if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) {
-                Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(op.account, op.authority);
-                long delayUntil = mSyncStorageEngine.getDelayUntilTime(op.account, op.authority);
-                opRunTime = Math.max(
-                        Math.max(opRunTime, delayUntil),
-                        backoff != null ? backoff.first : 0);
-            }
-            // we know a sync is a retry if the intialization flag is set, since that will only
-            // be set by the sync dispatching code, thus if it is set it must have already been
-            // dispatched
-            final boolean syncableIsUnknownAndNotARetry =
-                    !op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)
-                    && mSyncStorageEngine.getIsSyncable(op.account, op.authority) < 0;
-            // if the unsyncable state differs, make the current the best if it is unsyncable
-            // else, if the expedited state differs, make the current the best if it is expedited
-            // else, make the current the best if it is earlier than the best
-            if (best == null
-                    || ((bestSyncableIsUnknownAndNotARetry == syncableIsUnknownAndNotARetry)
-                        ? (best.expedited == op.expedited
-                           ? opRunTime < bestRunTime
-                           : op.expedited)
-                        : syncableIsUnknownAndNotARetry)) {
-                best = op;
-                bestSyncableIsUnknownAndNotARetry = syncableIsUnknownAndNotARetry;
-                bestRunTime = opRunTime;
+            if (op.account.equals(account) && op.authority.equals(providerName)) {
+                op.backoff = backoff;
+                op.updateEffectiveRunTime();
             }
         }
-        if (best == null) {
-            return null;
-        }
-        return Pair.create(best, bestRunTime);
     }
 
-    /**
-     * Find and return the SyncOperation that should be run next and is ready to run.
-     * @param now the current {@link android.os.SystemClock#elapsedRealtime()}, used to
-     * decide if the sync operation is ready to run
-     * @return the SyncOperation that should be run next and is ready to run.
-     */
-    public Pair<SyncOperation, Long> nextReadyToRun(long now) {
-        Pair<SyncOperation, Long> nextOpAndRunTime = nextOperation();
-        if (nextOpAndRunTime == null || nextOpAndRunTime.second > now) {
-            return null;
+    public void onDelayUntilTimeChanged(Account account, String providerName, long delayUntil) {
+        // for each op that matches the account and provider update its
+        // delayUntilTime and effectiveStartTime
+        for (SyncOperation op : mOperationsMap.values()) {
+            if (op.account.equals(account) && op.authority.equals(providerName)) {
+                op.delayUntil = delayUntil;
+                op.updateEffectiveRunTime();
+            }
         }
-        return nextOpAndRunTime;
     }
 
     public void remove(Account account, String authority) {
@@ -200,9 +165,17 @@
     }
 
     public void dump(StringBuilder sb) {
+        final long now = SystemClock.elapsedRealtime();
         sb.append("SyncQueue: ").append(mOperationsMap.size()).append(" operation(s)\n");
         for (SyncOperation operation : mOperationsMap.values()) {
-            sb.append(operation).append("\n");
+            sb.append("  ");
+            if (operation.effectiveRunTime <= now) {
+                sb.append("READY");
+            } else {
+                sb.append(DateUtils.formatElapsedTime((operation.effectiveRunTime - now) / 1000));
+            }
+            sb.append(" - ");
+            sb.append(operation.dump(false)).append("\n");
         }
     }
 }
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index e1a9dbb..bae6ad36 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -229,7 +229,7 @@
     private final ArrayList<PendingOperation> mPendingOperations =
             new ArrayList<PendingOperation>();
 
-    private SyncInfo mCurrentSync;
+    private final ArrayList<SyncInfo> mCurrentSyncs = new ArrayList<SyncInfo>();
 
     private final SparseArray<SyncStatusInfo> mSyncStatus =
             new SparseArray<SyncStatusInfo>();
@@ -690,23 +690,12 @@
 
     /**
      * Returns true if there is currently a sync operation for the given
-     * account or authority in the pending list, or actively being processed.
+     * account or authority actively being processed.
      */
     public boolean isSyncActive(Account account, String authority) {
         synchronized (mAuthorities) {
-            int i = mPendingOperations.size();
-            while (i > 0) {
-                i--;
-                // TODO(fredq): this probably shouldn't be considering
-                // pending operations.
-                PendingOperation op = mPendingOperations.get(i);
-                if (op.account.equals(account) && op.authority.equals(authority)) {
-                    return true;
-                }
-            }
-
-            if (mCurrentSync != null) {
-                AuthorityInfo ainfo = getAuthority(mCurrentSync.authorityId);
+            for (SyncInfo syncInfo : mCurrentSyncs) {
+                AuthorityInfo ainfo = getAuthority(syncInfo.authorityId);
                 if (ainfo != null && ainfo.account.equals(account)
                         && ainfo.authority.equals(authority)) {
                     return true;
@@ -887,40 +876,47 @@
     }
 
     /**
-     * Called when the currently active sync is changing (there can only be
-     * one at a time).  Either supply a valid ActiveSyncContext with information
-     * about the sync, or null to stop the currently active sync.
+     * Called when a sync is starting. Supply a valid ActiveSyncContext with information
+     * about the sync.
      */
-    public void setActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
+    public SyncInfo addActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
+        final SyncInfo syncInfo;
         synchronized (mAuthorities) {
-            if (activeSyncContext != null) {
-                if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "setActiveSync: account="
-                        + activeSyncContext.mSyncOperation.account
-                        + " auth=" + activeSyncContext.mSyncOperation.authority
-                        + " src=" + activeSyncContext.mSyncOperation.syncSource
-                        + " extras=" + activeSyncContext.mSyncOperation.extras);
-                }
-                if (mCurrentSync != null) {
-                    Log.w(TAG, "setActiveSync called with existing active sync!");
-                }
-                AuthorityInfo authority = getAuthorityLocked(
-                        activeSyncContext.mSyncOperation.account,
-                        activeSyncContext.mSyncOperation.authority,
-                        "setActiveSync");
-                if (authority == null) {
-                    return;
-                }
-                mCurrentSync = new SyncInfo(authority.ident,
-                        authority.account, authority.authority,
-                        activeSyncContext.mStartTime);
-            } else {
-                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "setActiveSync: null");
-                mCurrentSync = null;
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "setActiveSync: account="
+                    + activeSyncContext.mSyncOperation.account
+                    + " auth=" + activeSyncContext.mSyncOperation.authority
+                    + " src=" + activeSyncContext.mSyncOperation.syncSource
+                    + " extras=" + activeSyncContext.mSyncOperation.extras);
             }
+            AuthorityInfo authority = getOrCreateAuthorityLocked(
+                    activeSyncContext.mSyncOperation.account,
+                    activeSyncContext.mSyncOperation.authority,
+                    -1 /* assign a new identifier if creating a new authority */,
+                    true /* write to storage if this results in a change */);
+            syncInfo = new SyncInfo(authority.ident,
+                    authority.account, authority.authority,
+                    activeSyncContext.mStartTime);
+            mCurrentSyncs.add(syncInfo);
         }
 
-        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
+        reportActiveChange();
+        return syncInfo;
+    }
+
+    /**
+     * Called to indicate that a previously active sync is no longer active.
+     */
+    public void removeActiveSync(SyncInfo syncInfo) {
+        synchronized (mAuthorities) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, "removeActiveSync: account="
+                        + syncInfo.account + " auth=" + syncInfo.authority);
+            }
+            mCurrentSyncs.remove(syncInfo);
+        }
+
+        reportActiveChange();
     }
 
     /**
@@ -1092,13 +1088,12 @@
     }
 
     /**
-     * Return the currently active sync information, or null if there is no
-     * active sync.  Note that the returned object is the real, live active
-     * sync object, so be careful what you do with it.
+     * Return a list of the currently active syncs. Note that the returned items are the
+     * real, live active sync objects, so be careful what you do with it.
      */
-    public SyncInfo getCurrentSync() {
+    public List<SyncInfo> getCurrentSyncs() {
         synchronized (mAuthorities) {
-            return mCurrentSync;
+            return new ArrayList<SyncInfo>(mCurrentSyncs);
         }
     }
 
diff --git a/core/java/android/content/XmlDocumentProvider.java b/core/java/android/content/XmlDocumentProvider.java
new file mode 100644
index 0000000..153ad38
--- /dev/null
+++ b/core/java/android/content/XmlDocumentProvider.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.HttpGet;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import android.content.ContentResolver.OpenResourceIdResult;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.net.http.AndroidHttpClient;
+import android.util.Log;
+import android.widget.CursorAdapter;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.BitSet;
+import java.util.Stack;
+import java.util.regex.Pattern;
+
+/**
+ * A read-only content provider which extracts data out of an XML document.
+ *
+ * <p>A XPath-like selection pattern is used to select some nodes in the XML document. Each such
+ * node will create a row in the {@link Cursor} result.</p>
+ *
+ * Each row is then populated with columns that are also defined as XPath-like projections. These
+ * projections fetch attributes values or text in the matching row node or its children.
+ *
+ * <p>To add this provider in your application, you should add its declaration to your application
+ * manifest:
+ * <pre class="prettyprint">
+ * &lt;provider android:name="android.content.XmlDocumentProvider" android:authorities="xmldocument" /&gt;
+ * </pre>
+ * </p>
+ *
+ * <h2>Node selection syntax</h2>
+ * The node selection syntax is made of the concatenation of an arbitrary number (at least one) of
+ * <code>/node_name</code> node selection patterns.
+ *
+ * <p>The <code>/root/child1/child2</code> pattern will for instance match all nodes named
+ * <code>child2</code> which are children of a node named <code>child1</code> which are themselves
+ * children of a root node named <code>root</code>.</p>
+ *
+ * Any <code>/</code> separator in the previous expression can be replaced by a <code>//</code>
+ * separator instead, which indicated a <i>descendant</i> instead of a child.
+ *
+ * <p>The <code>//node1//node2</code> pattern will for instance match all nodes named
+ * <code>node2</code> which are descendant of a node named <code>node1</code> located anywhere in
+ * the document hierarchy.</p>
+ *
+ * Node names can contain namespaces in the form <code>namespace:node</code>.
+ *
+ * <h2>Projection syntax</h2>
+ * For every selected node, the projection will then extract actual data from this node and its
+ * descendant.
+ *
+ * <p>Use a syntax similar to the selection syntax described above to select the text associated
+ * with a child of the selected node. The implicit root of this projection pattern is the selected
+ * node. <code>/</code> will hence refer to the text of the selected node, while
+ * <code>/child1</code> will fetch the text of its child named <code>child1</code> and
+ * <code>//child1</code> will match any <i>descendant</i> named <code>child1</code>. If several
+ * nodes match the projection pattern, their texts are appended as a result.</p>
+ *
+ * A projection can also fetch any node attribute by appending a <code>@attribute_name</code>
+ * pattern to the previously described syntax. <code>//child1@price</code> will for instance match
+ * the attribute <code>price</code> of any <code>child1</code> descendant.
+ *
+ * <p>If a projection does not match any node/attribute, its associated value will be an empty
+ * string.</p>
+ *
+ * <h2>Example</h2>
+ * Using the following XML document:
+ * <pre class="prettyprint">
+ * &lt;library&gt;
+ *   &lt;book id="EH94"&gt;
+ *     &lt;title&gt;The Old Man and the Sea&lt;/title&gt;
+ *     &lt;author&gt;Ernest Hemingway&lt;/author&gt;
+ *   &lt;/book&gt;
+ *   &lt;book id="XX10"&gt;
+ *     &lt;title&gt;The Arabian Nights: Tales of 1,001 Nights&lt;/title&gt;
+ *   &lt;/book&gt;
+ *   &lt;no-id&gt;
+ *     &lt;book&gt;
+ *       &lt;title&gt;Animal Farm&lt;/title&gt;
+ *       &lt;author&gt;George Orwell&lt;/author&gt;
+ *     &lt;/book&gt;
+ *   &lt;/no-id&gt;
+ * &lt;/library&gt;
+ * </pre>
+ * A selection pattern of <code>/library//book</code> will match the three book entries (while
+ * <code>/library/book</code> will only match the first two ones).
+ *
+ * <p>Defining the projections as <code>/title</code>, <code>/author</code> and <code>@id</code>
+ * will retrieve the associated data. Note that the author of the second book as well as the id of
+ * the third are empty strings.
+ */
+public class XmlDocumentProvider extends ContentProvider {
+    /*
+     * Ideas for improvement:
+     * - Expand XPath-like syntax to allow for [nb] child number selector
+     * - Address the starting . bug in AbstractCursor which prevents a true XPath syntax.
+     * - Provide an alternative to concatenation when several node match (list-like).
+     * - Support namespaces in attribute names.
+     * - Incremental Cursor creation, pagination
+     */
+    private static final String LOG_TAG = "XmlDocumentProvider";
+    private AndroidHttpClient mHttpClient;
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    /**
+     * Query data from the XML document referenced in the URI.
+     *
+     * <p>The XML document can be a local resource or a file that will be downloaded from the
+     * Internet. In the latter case, your application needs to request the INTERNET permission in
+     * its manifest.</p>
+     *
+     * The URI will be of the form <code>content://xmldocument/?resource=R.xml.myFile</code> for a
+     * local resource. <code>xmldocument</code> should match the authority declared for this
+     * provider in your manifest. Internet documents are referenced using
+     * <code>content://xmldocument/?url=</code> followed by an encoded version of the URL of your
+     * document (see {@link Uri#encode(String)}).
+     *
+     * <p>The number of columns of the resulting Cursor is equal to the size of the projection
+     * array plus one, named <code>_id</code> which will contain a unique row id (allowing the
+     * Cursor to be used with a {@link CursorAdapter}). The other columns' names are the projection
+     * patterns.</p>
+     *
+     * @param uri The URI of your local resource or Internet document.
+     * @param projection A set of patterns that will be used to extract data from each selected
+     * node. See class documentation for pattern syntax.
+     * @param selection A selection pattern which will select the nodes that will create the
+     * Cursor's rows. See class documentation for pattern syntax.
+     * @param selectionArgs This parameter is ignored.
+     * @param sortOrder The row order in the resulting cursor is determined from the node order in
+     * the XML document. This parameter is ignored.
+     * @return A Cursor or null in case of error.
+     */
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+
+        XmlPullParser parser = null;
+        mHttpClient = null;
+
+        final String url = uri.getQueryParameter("url");
+        if (url != null) {
+            parser = getUriXmlPullParser(url);
+        } else {
+            final String resource = uri.getQueryParameter("resource");
+            if (resource != null) {
+                Uri resourceUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
+                        getContext().getPackageName() + "/" + resource);
+                parser = getResourceXmlPullParser(resourceUri);
+            }
+        }
+
+        if (parser != null) {
+            XMLCursor xmlCursor = new XMLCursor(selection, projection);
+            try {
+                xmlCursor.parseWith(parser);
+                return xmlCursor;
+            } catch (IOException e) {
+                Log.w(LOG_TAG, "I/O error while parsing XML " + uri, e);
+            } catch (XmlPullParserException e) {
+                Log.w(LOG_TAG, "Error while parsing XML " + uri, e);
+            } finally {
+                if (mHttpClient != null) {
+                    mHttpClient.close();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates an XmlPullParser for the provided URL. Can be overloaded to provide your own parser.
+     * @param url The URL of the XML document that is to be parsed.
+     * @return An XmlPullParser on this document.
+     */
+    protected XmlPullParser getUriXmlPullParser(String url) {
+        XmlPullParser parser = null;
+        try {
+            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+            factory.setNamespaceAware(true);
+            parser = factory.newPullParser();
+        } catch (XmlPullParserException e) {
+            Log.e(LOG_TAG, "Unable to create XmlPullParser", e);
+            return null;
+        }
+
+        InputStream inputStream = null;
+        try {
+            final HttpGet get = new HttpGet(url);
+            mHttpClient = AndroidHttpClient.newInstance("Android");
+            HttpResponse response = mHttpClient.execute(get);
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                final HttpEntity entity = response.getEntity();
+                if (entity != null) {
+                    inputStream = entity.getContent();
+                }
+            }
+        } catch (IOException e) {
+            Log.w(LOG_TAG, "Error while retrieving XML file " + url, e);
+            return null;
+        }
+
+        try {
+            parser.setInput(inputStream, null);
+        } catch (XmlPullParserException e) {
+            Log.w(LOG_TAG, "Error while reading XML file from " + url, e);
+            return null;
+        }
+
+        return parser;
+    }
+
+    /**
+     * Creates an XmlPullParser for the provided local resource. Can be overloaded to provide your
+     * own parser.
+     * @param resourceUri A fully qualified resource name referencing a local XML resource.
+     * @return An XmlPullParser on this resource.
+     */
+    protected XmlPullParser getResourceXmlPullParser(Uri resourceUri) {
+        OpenResourceIdResult resourceId;
+        try {
+            resourceId = getContext().getContentResolver().getResourceId(resourceUri);
+            return resourceId.r.getXml(resourceId.id);
+        } catch (FileNotFoundException e) {
+            Log.w(LOG_TAG, "XML resource not found: " + resourceUri.toString(), e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns "vnd.android.cursor.dir/xmldoc".
+     */
+    @Override
+    public String getType(Uri uri) {
+        return "vnd.android.cursor.dir/xmldoc";
+    }
+
+    /**
+     * This ContentProvider is read-only. This method throws an UnsupportedOperationException.
+     **/
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * This ContentProvider is read-only. This method throws an UnsupportedOperationException.
+     **/
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * This ContentProvider is read-only. This method throws an UnsupportedOperationException.
+     **/
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException();
+    }
+
+    private static class XMLCursor extends MatrixCursor {
+        private final Pattern mSelectionPattern;
+        private Pattern[] mProjectionPatterns;
+        private String[] mAttributeNames;
+        private String[] mCurrentValues;
+        private BitSet[] mActiveTextDepthMask;
+        private final int mNumberOfProjections;
+
+        public XMLCursor(String selection, String[] projections) {
+            super(projections);
+            // The first column in projections is used for the _ID
+            mNumberOfProjections = projections.length - 1;
+            mSelectionPattern = createPattern(selection);
+            createProjectionPattern(projections);
+        }
+
+        private Pattern createPattern(String input) {
+            String pattern = input.replaceAll("//", "/(.*/|)").replaceAll("^/", "^/") + "$";
+            return Pattern.compile(pattern);
+        }
+
+        private void createProjectionPattern(String[] projections) {
+            mProjectionPatterns = new Pattern[mNumberOfProjections];
+            mAttributeNames = new String[mNumberOfProjections];
+            mActiveTextDepthMask = new BitSet[mNumberOfProjections];
+            // Add a column to store _ID
+            mCurrentValues = new String[mNumberOfProjections + 1];
+
+            for (int i=0; i<mNumberOfProjections; i++) {
+                mActiveTextDepthMask[i] = new BitSet();
+                String projection = projections[i + 1]; // +1 to skip the _ID column
+                int atIndex = projection.lastIndexOf('@', projection.length());
+                if (atIndex >= 0) {
+                    mAttributeNames[i] = projection.substring(atIndex+1);
+                    projection = projection.substring(0, atIndex);
+                } else {
+                    mAttributeNames[i] = null;
+                }
+
+                // Conforms to XPath standard: reference to local context starts with a .
+                if (projection.charAt(0) == '.') {
+                    projection = projection.substring(1);
+                }
+                mProjectionPatterns[i] = createPattern(projection);
+            }
+        }
+
+        public void parseWith(XmlPullParser parser) throws IOException, XmlPullParserException {
+            StringBuilder path = new StringBuilder();
+            Stack<Integer> pathLengthStack = new Stack<Integer>();
+
+            // There are two parsing mode: in root mode, rootPath is updated and nodes matching
+            // selectionPattern are searched for and currentNodeDepth is negative.
+            // When a node matching selectionPattern is found, currentNodeDepth is set to 0 and
+            // updated as children are parsed and projectionPatterns are searched in nodePath.
+            int currentNodeDepth = -1;
+
+            // Index where local selected node path starts from in path
+            int currentNodePathStartIndex = 0;
+
+            int eventType = parser.getEventType();
+            while (eventType != XmlPullParser.END_DOCUMENT) {
+
+                if (eventType == XmlPullParser.START_TAG) {
+                    // Update path
+                    pathLengthStack.push(path.length());
+                    path.append('/');
+                    String prefix = null;
+                    try {
+                        // getPrefix is not supported by local Xml resource parser
+                        prefix = parser.getPrefix();
+                    } catch (RuntimeException e) {
+                        prefix = null;
+                    }
+                    if (prefix != null) {
+                        path.append(prefix);
+                        path.append(':');
+                    }
+                    path.append(parser.getName());
+
+                    if (currentNodeDepth >= 0) {
+                        currentNodeDepth++;
+                    } else {
+                        // A node matching selection is found: initialize child parsing mode
+                        if (mSelectionPattern.matcher(path.toString()).matches()) {
+                            currentNodeDepth = 0;
+                            currentNodePathStartIndex = path.length();
+                            mCurrentValues[0] = Integer.toString(getCount()); // _ID
+                            for (int i = 0; i < mNumberOfProjections; i++) {
+                                // Reset values to default (empty string)
+                                mCurrentValues[i + 1] = "";
+                                mActiveTextDepthMask[i].clear();
+                            }
+                        }
+                    }
+
+                    // This test has to be separated from the previous one as currentNodeDepth can
+                    // be modified above (when a node matching selection is found).
+                    if (currentNodeDepth >= 0) {
+                        final String localNodePath = path.substring(currentNodePathStartIndex);
+                        for (int i = 0; i < mNumberOfProjections; i++) {
+                            if (mProjectionPatterns[i].matcher(localNodePath).matches()) {
+                                String attribute = mAttributeNames[i];
+                                if (attribute != null) {
+                                    mCurrentValues[i + 1] =
+                                        parser.getAttributeValue(null, attribute);
+                                } else {
+                                    mActiveTextDepthMask[i].set(currentNodeDepth, true);
+                                }
+                            }
+                        }
+                    }
+
+                } else if (eventType == XmlPullParser.END_TAG) {
+                    // Pop last node from path
+                    final int length = pathLengthStack.pop();
+                    path.setLength(length);
+
+                    if (currentNodeDepth >= 0) {
+                        if (currentNodeDepth == 0) {
+                            // Leaving a selection matching node: add a new row with results
+                            addRow(mCurrentValues);
+                        } else {
+                            for (int i = 0; i < mNumberOfProjections; i++) {
+                                mActiveTextDepthMask[i].set(currentNodeDepth, false);
+                            }
+                        }
+                        currentNodeDepth--;
+                    }
+
+                } else if ((eventType == XmlPullParser.TEXT) && (!parser.isWhitespace())) {
+                    for (int i = 0; i < mNumberOfProjections; i++) {
+                        if ((currentNodeDepth >= 0) &&
+                            (mActiveTextDepthMask[i].get(currentNodeDepth))) {
+                            mCurrentValues[i + 1] += parser.getText();
+                        }
+                    }
+                }
+
+                eventType = parser.next();
+            }
+        }
+    }
+}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e26f160..e688c86 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -149,6 +149,27 @@
      * {@link android.R.attr#finishOnCloseSystemDialogs} attribute.
      */
     public static final int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 0x0100;
+    /** 
+     * Bit in {@link #flags} corresponding to an immersive activity
+     * that wishes not to be interrupted by notifications.
+     * Applications that hide the system notification bar with
+     * {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN}
+     * may still be interrupted by high-priority notifications; for example, an
+     * incoming phone call may use
+     * {@link android.app.Notification#fullScreenIntent fullScreenIntent}
+     * to present a full-screen in-call activity to the user, pausing the
+     * current activity as a side-effect. An activity with
+     * {@link #FLAG_IMMERSIVE} set, however, will not be interrupted; the
+     * notification may be shown in some other way (such as a small floating
+     * "toast" window).
+     * {@see android.app.Notification#FLAG_HIGH_PRIORITY}
+     */
+    public static final int FLAG_IMMERSIVE = 0x0200;
+    /**
+     * Value for {@link #flags}: true when the application's rendering should
+     * be hardware accelerated.
+     */
+    public static final int FLAG_HARDWARE_ACCELERATED = 0x0400;
     /**
      * Options that have been set in the activity declaration in the
      * manifest.
@@ -159,6 +180,7 @@
      * {@link #FLAG_STATE_NOT_NEEDED}, {@link #FLAG_EXCLUDE_FROM_RECENTS},
      * {@link #FLAG_ALLOW_TASK_REPARENTING}, {@link #FLAG_NO_HISTORY},
      * {@link #FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS},
+     * {@link #FLAG_IMMERSIVE}, {@link #FLAG_HARDWARE_ACCELERATED}
      */
     public int flags;
 
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 665b606..bb0ed6a 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -266,7 +266,6 @@
      * increased in size for extra large screens.  Corresponds to
      * {@link android.R.styleable#AndroidManifestSupportsScreens_xlargeScreens
      * android:xlargeScreens}.
-     * @hide
      */
     public static final int FLAG_SUPPORTS_XLARGE_SCREENS = 1<<19;
     
@@ -309,7 +308,7 @@
      * {@link #FLAG_ALLOW_CLEAR_USER_DATA}, {@link #FLAG_UPDATED_SYSTEM_APP},
      * {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS},
      * {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
-     * {@link #FLAG_SUPPORTS_LARGE_SCREENS},
+     * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_SUPPORTS_XLARGE_SCREENS},
      * {@link #FLAG_RESIZEABLE_FOR_SCREENS},
      * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}, {@link #FLAG_VM_SAFE_MODE}
      */
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ff3656c..54dbe37 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -16,9 +16,6 @@
 
 package android.content.pm;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -30,14 +27,14 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.PatternMatcher;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Config;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
-
 import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.BufferedInputStream;
 import java.io.File;
@@ -49,7 +46,6 @@
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.Iterator;
-import java.util.List;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 
@@ -1266,7 +1262,8 @@
                 "<permission-group>", sa,
                 com.android.internal.R.styleable.AndroidManifestPermissionGroup_name,
                 com.android.internal.R.styleable.AndroidManifestPermissionGroup_label,
-                com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon, 0)) {
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon,
+                com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo)) {
             sa.recycle();
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
             return null;
@@ -1301,7 +1298,8 @@
                 "<permission>", sa,
                 com.android.internal.R.styleable.AndroidManifestPermission_name,
                 com.android.internal.R.styleable.AndroidManifestPermission_label,
-                com.android.internal.R.styleable.AndroidManifestPermission_icon, 0)) {
+                com.android.internal.R.styleable.AndroidManifestPermission_icon,
+                com.android.internal.R.styleable.AndroidManifestPermission_logo)) {
             sa.recycle();
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
             return null;
@@ -1354,7 +1352,8 @@
                 "<permission-tree>", sa,
                 com.android.internal.R.styleable.AndroidManifestPermissionTree_name,
                 com.android.internal.R.styleable.AndroidManifestPermissionTree_label,
-                com.android.internal.R.styleable.AndroidManifestPermissionTree_icon, 0)) {
+                com.android.internal.R.styleable.AndroidManifestPermissionTree_icon,
+                com.android.internal.R.styleable.AndroidManifestPermissionTree_logo)) {
             sa.recycle();
             mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
             return null;
@@ -1398,7 +1397,8 @@
             mParseInstrumentationArgs = new ParsePackageItemArgs(owner, outError,
                     com.android.internal.R.styleable.AndroidManifestInstrumentation_name,
                     com.android.internal.R.styleable.AndroidManifestInstrumentation_label,
-                    com.android.internal.R.styleable.AndroidManifestInstrumentation_icon, 0);
+                    com.android.internal.R.styleable.AndroidManifestInstrumentation_icon,
+                    com.android.internal.R.styleable.AndroidManifestInstrumentation_logo);
             mParseInstrumentationArgs.tag = "<instrumentation>";
         }
         
@@ -1508,10 +1508,18 @@
             ai.nonLocalizedLabel = v.coerceToString();
         }
 
+        int defaultTheme = 0;
+        if (owner.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+            // As of honeycomb, the default application theme is holographic.
+            defaultTheme = android.R.style.Theme_Holo;
+        }
+
         ai.icon = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
+        ai.logo = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestApplication_logo, 0);
         ai.theme = sa.getResourceId(
-                com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
+                com.android.internal.R.styleable.AndroidManifestApplication_theme, defaultTheme);
         ai.descriptionRes = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
 
@@ -1543,6 +1551,10 @@
             ai.flags |= ApplicationInfo.FLAG_VM_SAFE_MODE;
         }
 
+        boolean hardwareAccelerated = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_hardwareAccelerated,
+                false);
+
         if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_hasCode,
                 true)) {
@@ -1642,7 +1654,8 @@
 
             String tagName = parser.getName();
             if (tagName.equals("activity")) {
-                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false);
+                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
+                        hardwareAccelerated);
                 if (a == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
@@ -1651,7 +1664,7 @@
                 owner.activities.add(a);
 
             } else if (tagName.equals("receiver")) {
-                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true);
+                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
                 if (a == null) {
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return false;
@@ -1774,6 +1787,11 @@
             outInfo.nonLocalizedLabel = null;
         }
         
+        int logoVal = sa.getResourceId(logoRes, 0);
+        if (logoVal != 0) {
+            outInfo.logo = logoVal;
+        }
+
         TypedValue v = sa.peekValue(labelRes);
         if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
             outInfo.nonLocalizedLabel = v.coerceToString();
@@ -1786,7 +1804,8 @@
 
     private Activity parseActivity(Package owner, Resources res,
             XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,
-            boolean receiver) throws XmlPullParserException, IOException {
+            boolean receiver, boolean hardwareAccelerated)
+            throws XmlPullParserException, IOException {
         TypedArray sa = res.obtainAttributes(attrs,
                 com.android.internal.R.styleable.AndroidManifestActivity);
 
@@ -1794,7 +1813,8 @@
             mParseActivityArgs = new ParseComponentArgs(owner, outError,
                     com.android.internal.R.styleable.AndroidManifestActivity_name,
                     com.android.internal.R.styleable.AndroidManifestActivity_label,
-                    com.android.internal.R.styleable.AndroidManifestActivity_icon, 0,
+                    com.android.internal.R.styleable.AndroidManifestActivity_icon,
+                    com.android.internal.R.styleable.AndroidManifestActivity_logo,
                     mSeparateProcesses,
                     com.android.internal.R.styleable.AndroidManifestActivity_process,
                     com.android.internal.R.styleable.AndroidManifestActivity_description,
@@ -1890,7 +1910,19 @@
             a.info.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
         }
 
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestActivity_immersive,
+                false)) {
+            a.info.flags |= ActivityInfo.FLAG_IMMERSIVE;
+        }
+        
         if (!receiver) {
+            if (sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestActivity_hardwareAccelerated,
+                    hardwareAccelerated)) {
+                a.info.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
+            }
+
             a.info.launchMode = sa.getInt(
                     com.android.internal.R.styleable.AndroidManifestActivity_launchMode,
                     ActivityInfo.LAUNCH_MULTIPLE);
@@ -2004,7 +2036,8 @@
             mParseActivityAliasArgs = new ParseComponentArgs(owner, outError,
                     com.android.internal.R.styleable.AndroidManifestActivityAlias_name,
                     com.android.internal.R.styleable.AndroidManifestActivityAlias_label,
-                    com.android.internal.R.styleable.AndroidManifestActivityAlias_icon, 0,
+                    com.android.internal.R.styleable.AndroidManifestActivityAlias_icon,
+                    com.android.internal.R.styleable.AndroidManifestActivityAlias_logo,
                     mSeparateProcesses,
                     0,
                     com.android.internal.R.styleable.AndroidManifestActivityAlias_description,
@@ -2132,7 +2165,8 @@
             mParseProviderArgs = new ParseComponentArgs(owner, outError,
                     com.android.internal.R.styleable.AndroidManifestProvider_name,
                     com.android.internal.R.styleable.AndroidManifestProvider_label,
-                    com.android.internal.R.styleable.AndroidManifestProvider_icon, 0,
+                    com.android.internal.R.styleable.AndroidManifestProvider_icon,
+                    com.android.internal.R.styleable.AndroidManifestProvider_logo,
                     mSeparateProcesses,
                     com.android.internal.R.styleable.AndroidManifestProvider_process,
                     com.android.internal.R.styleable.AndroidManifestProvider_description,
@@ -2404,7 +2438,8 @@
             mParseServiceArgs = new ParseComponentArgs(owner, outError,
                     com.android.internal.R.styleable.AndroidManifestService_name,
                     com.android.internal.R.styleable.AndroidManifestService_label,
-                    com.android.internal.R.styleable.AndroidManifestService_icon, 0,
+                    com.android.internal.R.styleable.AndroidManifestService_icon,
+                    com.android.internal.R.styleable.AndroidManifestService_logo,
                     mSeparateProcesses,
                     com.android.internal.R.styleable.AndroidManifestService_process,
                     com.android.internal.R.styleable.AndroidManifestService_description,
@@ -2618,6 +2653,9 @@
         outInfo.icon = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0);
         
+        outInfo.logo = sa.getResourceId(
+                com.android.internal.R.styleable.AndroidManifestIntentFilter_logo, 0);
+
         sa.recycle();
 
         int outerDepth = parser.getDepth();
@@ -2885,6 +2923,11 @@
                 outInfo.nonLocalizedLabel = null;
             }
             
+            int logoVal = args.sa.getResourceId(args.logoRes, 0);
+            if (logoVal != 0) {
+                outInfo.logo = logoVal;
+            }
+
             TypedValue v = args.sa.peekValue(args.labelRes);
             if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
                 outInfo.nonLocalizedLabel = v.coerceToString();
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index 34f1f5c..01ae1da 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -25,8 +25,6 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.nio.channels.FileChannel;
 
 /**
  * File descriptor of an entry in the AssetManager.  This provides your own
@@ -51,7 +49,7 @@
      * @param startOffset The location within the file that the asset starts.
      * This must be 0 if length is UNKNOWN_LENGTH.
      * @param length The number of bytes of the asset, or
-     * {@link #UNKNOWN_LENGTH if it extends to the end of the file.
+     * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
      */
     public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
             long length) {
@@ -125,17 +123,6 @@
     public void close() throws IOException {
         mFd.close();
     }
-    
-    /**
-     * Checks whether this file descriptor is for a memory file.
-     */
-    private boolean isMemoryFile() throws IOException {
-        try {
-            return MemoryFile.isMemoryFile(mFd.getFileDescriptor());
-        } catch (IOException e) {
-            return false;
-        }
-    }
 
     /**
      * Create and return a new auto-close input stream for this asset.  This
@@ -146,12 +133,6 @@
      * should only call this once for a particular asset.
      */
     public FileInputStream createInputStream() throws IOException {
-        if (isMemoryFile()) {
-            if (mLength > Integer.MAX_VALUE) {
-                throw new IOException("File length too large for a memory file: " + mLength);
-            }
-            return new AutoCloseMemoryFileInputStream(mFd, (int)mLength);
-        }
         if (mLength < 0) {
             return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
         }
@@ -280,66 +261,6 @@
             super.reset();
         }
     }
-    
-    /**
-     * An input stream that reads from a MemoryFile and closes it when the stream is closed.
-     * This extends FileInputStream just because {@link #createInputStream} returns
-     * a FileInputStream. All the FileInputStream methods are
-     * overridden to use the MemoryFile instead.
-     */
-    private static class AutoCloseMemoryFileInputStream extends FileInputStream {
-        private ParcelFileDescriptor mParcelFd;
-        private MemoryFile mFile;
-        private InputStream mStream;
-
-        public AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length)
-                throws IOException {
-            super(fd.getFileDescriptor());
-            mParcelFd = fd;
-            mFile = new MemoryFile(fd.getFileDescriptor(), length, "r");
-            mStream = mFile.getInputStream();
-        }
-
-        @Override
-        public int available() throws IOException {
-            return mStream.available();
-        }
-
-        @Override
-        public void close() throws IOException {
-            mParcelFd.close();  // must close ParcelFileDescriptor, not just the file descriptor,
-                                // since it could be a subclass of ParcelFileDescriptor.
-                                // E.g. ContentResolver.ParcelFileDescriptorInner.close() releases
-                                // a content provider
-            mFile.close();      // to unmap the memory file from the address space.
-            mStream.close();    // doesn't actually do anything
-        }
-
-        @Override
-        public FileChannel getChannel() {
-            return null;
-        }
-
-        @Override
-        public int read() throws IOException {
-            return mStream.read();
-        }
-
-        @Override
-        public int read(byte[] buffer, int offset, int count) throws IOException {
-            return mStream.read(buffer, offset, count);
-        }
-
-        @Override
-        public int read(byte[] buffer) throws IOException {
-            return mStream.read(buffer);
-        }
-
-        @Override
-        public long skip(long count) throws IOException {
-            return mStream.skip(count);
-        }
-    }
 
     /**
      * An OutputStream you can create on a ParcelFileDescriptor, which will
@@ -426,15 +347,4 @@
         }
     };
 
-    /**
-     * Creates an AssetFileDescriptor from a memory file.
-     *
-     * @hide
-     */
-    public static AssetFileDescriptor fromMemoryFile(MemoryFile memoryFile)
-            throws IOException {
-        ParcelFileDescriptor fd = memoryFile.getParcelFileDescriptor();
-        return new AssetFileDescriptor(fd, 0, memoryFile.length());
-    }
-
 }
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 73d9458..e279f64 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -149,7 +149,7 @@
     /*package*/ final CharSequence getResourceText(int ident) {
         synchronized (this) {
             TypedValue tmpValue = mValue;
-            int block = loadResourceValue(ident, tmpValue, true);
+            int block = loadResourceValue(ident, (short) 0, tmpValue, true);
             if (block >= 0) {
                 if (tmpValue.type == TypedValue.TYPE_STRING) {
                     return mStringBlocks[block].get(tmpValue.data);
@@ -190,10 +190,11 @@
 
 
     /*package*/ final boolean getResourceValue(int ident,
+                                               int density,
                                                TypedValue outValue,
                                                boolean resolveRefs)
     {
-        int block = loadResourceValue(ident, outValue, resolveRefs);
+        int block = loadResourceValue(ident, (short) density, outValue, resolveRefs);
         if (block >= 0) {
             if (outValue.type != TypedValue.TYPE_STRING) {
                 return true;
@@ -681,8 +682,8 @@
 
     /** Returns true if the resource was found, filling in mRetStringBlock and
      *  mRetData. */
-    private native final int loadResourceValue(int ident, TypedValue outValue,
-                                               boolean resolve);
+    private native final int loadResourceValue(int ident, short density, TypedValue outValue,
+            boolean resolve);
     /** Returns true if the resource was found, filling in mRetStringBlock and
      *  mRetData. */
     private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue,
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index d0ba590..406b091 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -18,6 +18,7 @@
 
 import android.content.pm.ApplicationInfo;
 import android.graphics.Canvas;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.util.DisplayMetrics;
@@ -363,6 +364,17 @@
         }
 
         /**
+         * Translate a Point in screen coordinates into the app window's coordinates.
+         */
+        public void translatePointInScreenToAppWindow(PointF point) {
+            final float scale = applicationInvertedScale;
+            if (scale != 1.0f) {
+                point.x *= scale;
+                point.y *= scale;
+            }
+        }
+
+        /**
          * Translate the location of the sub window.
          * @param params
          */
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index ef38236..2f110f0 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -83,7 +83,7 @@
      * <p>The {@link #SCREENLAYOUT_SIZE_MASK} bits define the overall size
      * of the screen.  They may be one of
      * {@link #SCREENLAYOUT_SIZE_SMALL}, {@link #SCREENLAYOUT_SIZE_NORMAL},
-     * {@link #SCREENLAYOUT_SIZE_LARGE}.
+     * {@link #SCREENLAYOUT_SIZE_LARGE}, or {@link #SCREENLAYOUT_SIZE_XLARGE}.
      * 
      * <p>The {@link #SCREENLAYOUT_LONG_MASK} defines whether the screen
      * is wider/taller than normal.  They may be one of
@@ -266,11 +266,18 @@
         sb.append("/");
         sb.append(navigationHidden);
         sb.append(" orien=");
-        sb.append(orientation);
-        sb.append(" layout=");
-        sb.append(screenLayout);
-        sb.append(" uiMode=");
-        sb.append(uiMode);
+        switch(orientation) {
+            case ORIENTATION_LANDSCAPE:
+                sb.append("L"); break;
+            case ORIENTATION_PORTRAIT:
+                sb.append("P"); break;
+            default:
+                sb.append(orientation);
+        }
+        sb.append(" layout=0x");
+        sb.append(java.lang.Integer.toHexString(screenLayout));
+        sb.append(" uiMode=0x");
+        sb.append(java.lang.Integer.toHexString(uiMode));
         if (seq != 0) {
             sb.append(" seq=");
             sb.append(seq);
diff --git a/core/java/android/content/res/PluralRules.java b/core/java/android/content/res/PluralRules.java
deleted file mode 100644
index 2dce3c1..0000000
--- a/core/java/android/content/res/PluralRules.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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.content.res;
-
-import java.util.Locale;
-
-/*
- * Yuck-o.  This is not the right way to implement this.  When the ICU PluralRules
- * object has been integrated to android, we should switch to that.  For now, yuck-o.
- */
-
-abstract class PluralRules {
-
-    static final int QUANTITY_OTHER = 0x0000;
-    static final int QUANTITY_ZERO  = 0x0001;
-    static final int QUANTITY_ONE   = 0x0002;
-    static final int QUANTITY_TWO   = 0x0004;
-    static final int QUANTITY_FEW   = 0x0008;
-    static final int QUANTITY_MANY  = 0x0010;
-
-    static final int ID_OTHER = 0x01000004;
-
-    abstract int quantityForNumber(int n);
-
-    final int attrForNumber(int n) {
-        return PluralRules.attrForQuantity(quantityForNumber(n));
-    }
-
-    static final int attrForQuantity(int quantity) {
-        // see include/utils/ResourceTypes.h
-        switch (quantity) {
-            case QUANTITY_ZERO: return 0x01000005;
-            case QUANTITY_ONE:  return 0x01000006;
-            case QUANTITY_TWO:  return 0x01000007;
-            case QUANTITY_FEW:  return 0x01000008;
-            case QUANTITY_MANY: return 0x01000009;
-            default:            return ID_OTHER;
-        }
-    }
-
-    static final String stringForQuantity(int quantity) {
-        switch (quantity) {
-            case QUANTITY_ZERO:
-                return "zero";
-            case QUANTITY_ONE:
-                return "one";
-            case QUANTITY_TWO:
-                return "two";
-            case QUANTITY_FEW:
-                return "few";
-            case QUANTITY_MANY:
-                return "many";
-            default:
-                return "other";
-        }
-    }
-
-    static final PluralRules ruleForLocale(Locale locale) {
-        String lang = locale.getLanguage();
-        if ("cs".equals(lang)) {
-            if (cs == null) cs = new cs();
-            return cs;
-        }
-        else {
-            if (en == null) en = new en();
-            return en;
-        }
-    }
-
-    private static PluralRules cs;
-    private static class cs extends PluralRules {
-        int quantityForNumber(int n) {
-            if (n == 1) {
-                return QUANTITY_ONE;
-            }
-            else if (n >= 2 && n <= 4) {
-                return QUANTITY_FEW;
-            }
-            else {
-                return QUANTITY_OTHER;
-            }
-        }
-    }
-
-    private static PluralRules en;
-    private static class en extends PluralRules {
-        int quantityForNumber(int n) {
-            if (n == 1) {
-                return QUANTITY_ONE;
-            }
-            else {
-                return QUANTITY_OTHER;
-            }
-        }
-    }
-}
-
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index a6513aa..e6fd039 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -16,7 +16,6 @@
 
 package android.content.res;
 
-
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -42,6 +41,8 @@
 import java.lang.ref.WeakReference;
 import java.util.Locale;
 
+import libcore.icu.NativePluralRules;
+
 /**
  * Class for accessing an application's resources.  This sits on top of the
  * asset manager of the application (accessible through getAssets()) and
@@ -53,6 +54,8 @@
     private static final boolean DEBUG_CONFIG = false;
     private static final boolean TRACE_FOR_PRELOAD = false;
 
+    private static final int ID_OTHER = 0x01000004;
+
     // Use the current SDK version code.  If we are a development build,
     // also allow the previous SDK version + 1.
     private static final int sSdkVersion = Build.VERSION.SDK_INT
@@ -91,7 +94,7 @@
     /*package*/ final AssetManager mAssets;
     private final Configuration mConfiguration = new Configuration();
     /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics();
-    PluralRules mPluralRule;
+    private NativePluralRules mPluralRule;
     
     private CompatibilityInfo mCompatibilityInfo;
     private Display mDefaultDisplay;
@@ -208,9 +211,17 @@
     }
 
     /**
+     * Return the character sequence associated with a particular resource ID for a particular
+     * numerical quantity.
+     *
+     * <p>See <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String
+     * Resources</a> for more on quantity strings.
+     *
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
      *           entry. The value 0 is an invalid identifier.
+     * @param quantity The number used to get the correct string for the current language's
+     *           plural rules.
      *
      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
      *
@@ -218,29 +229,52 @@
      *         possibly styled text information.
      */
     public CharSequence getQuantityText(int id, int quantity) throws NotFoundException {
-        PluralRules rule = getPluralRule();
-        CharSequence res = mAssets.getResourceBagText(id, rule.attrForNumber(quantity));
+        NativePluralRules rule = getPluralRule();
+        CharSequence res = mAssets.getResourceBagText(id,
+                attrForQuantityCode(rule.quantityForInt(quantity)));
         if (res != null) {
             return res;
         }
-        res = mAssets.getResourceBagText(id, PluralRules.ID_OTHER);
+        res = mAssets.getResourceBagText(id, ID_OTHER);
         if (res != null) {
             return res;
         }
         throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
                 + " quantity=" + quantity
-                + " item=" + PluralRules.stringForQuantity(rule.quantityForNumber(quantity)));
+                + " item=" + stringForQuantityCode(rule.quantityForInt(quantity)));
     }
 
-    private PluralRules getPluralRule() {
+    private NativePluralRules getPluralRule() {
         synchronized (mSync) {
             if (mPluralRule == null) {
-                mPluralRule = PluralRules.ruleForLocale(mConfiguration.locale);
+                mPluralRule = NativePluralRules.forLocale(mConfiguration.locale);
             }
             return mPluralRule;
         }
     }
 
+    private static int attrForQuantityCode(int quantityCode) {
+        switch (quantityCode) {
+            case NativePluralRules.ZERO: return 0x01000005;
+            case NativePluralRules.ONE:  return 0x01000006;
+            case NativePluralRules.TWO:  return 0x01000007;
+            case NativePluralRules.FEW:  return 0x01000008;
+            case NativePluralRules.MANY: return 0x01000009;
+            default:                     return ID_OTHER;
+        }
+    }
+
+    private static String stringForQuantityCode(int quantityCode) {
+        switch (quantityCode) {
+            case NativePluralRules.ZERO: return "zero";
+            case NativePluralRules.ONE:  return "one";
+            case NativePluralRules.TWO:  return "two";
+            case NativePluralRules.FEW:  return "few";
+            case NativePluralRules.MANY: return "many";
+            default:                     return "other";
+        }
+    }
+
     /**
      * Return the string value associated with a particular resource ID.  It
      * will be stripped of any styled text information.
@@ -295,6 +329,9 @@
      * stripped of any styled text information.
      * {@more}
      *
+     * <p>See <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String
+     * Resources</a> for more on quantity strings.
+     *
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
      *           entry. The value 0 is an invalid identifier.
@@ -317,6 +354,9 @@
      * Return the string value associated with a particular resource ID for a particular
      * numerical quantity.
      *
+     * <p>See <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String
+     * Resources</a> for more on quantity strings.
+     *
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
      *           entry. The value 0 is an invalid identifier.
@@ -588,6 +628,50 @@
     }
 
     /**
+     * Return a drawable object associated with a particular resource ID for the
+     * given screen density in DPI. This will set the drawable's density to be
+     * the device's density multiplied by the ratio of actual drawable density
+     * to requested density. This allows the drawable to be scaled up to the
+     * correct size if needed. Various types of objects will be returned
+     * depending on the underlying resource -- for example, a solid color, PNG
+     * image, scalable image, etc. The Drawable API hides these implementation
+     * details.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt tool.
+     *            This integer encodes the package, type, and resource entry.
+     *            The value 0 is an invalid identifier.
+     * @param density the desired screen density indicated by the resource as
+     *            found in {@link DisplayMetrics}.
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *             not exist.
+     * @return Drawable An object that can be used to draw this resource.
+     * @hide
+     */
+    public Drawable getDrawableForDensity(int id, int density) throws NotFoundException {
+        synchronized (mTmpValue) {
+            TypedValue value = mTmpValue;
+            getValueForDensity(id, density, value, true);
+
+            /*
+             * Pretend the requested density is actually the display density. If
+             * the drawable returned is not the requested density, then force it
+             * to be scaled later by dividing its density by the ratio of
+             * requested density to actual device density. Drawables that have
+             * undefined density or no density don't need to be handled here.
+             */
+            if (value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
+                if (value.density == density) {
+                    value.density = DisplayMetrics.DENSITY_DEVICE;
+                } else {
+                    value.density = (value.density * DisplayMetrics.DENSITY_DEVICE) / density;
+                }
+            }
+
+            return loadDrawable(value, id);
+        }
+    }
+
+    /**
      * Return a movie object associated with the particular resource ID.
      * @param id The desired resource identifier, as generated by the aapt
      *           tool. This integer encodes the package, type, and resource
@@ -890,7 +974,7 @@
      */
     public void getValue(int id, TypedValue outValue, boolean resolveRefs)
             throws NotFoundException {
-        boolean found = mAssets.getResourceValue(id, outValue, resolveRefs);
+        boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
         if (found) {
             return;
         }
@@ -899,6 +983,29 @@
     }
 
     /**
+     * Get the raw value associated with a resource with associated density.
+     * 
+     * @param id resource identifier
+     * @param density density in DPI
+     * @param resolveRefs If true, a resource that is a reference to another
+     *            resource will be followed so that you receive the actual final
+     *            resource data. If false, the TypedValue will be filled in with
+     *            the reference itself.
+     * @throws NotFoundException Throws NotFoundException if the given ID does
+     *             not exist.
+     * @see #getValue(String, TypedValue, boolean)
+     * @hide
+     */
+    public void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
+        if (found) {
+            return;
+        }
+        throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
+    }
+
+    /**
      * Return the raw data associated with a particular resource ID.
      * See getIdentifier() for information on how names are mapped to resource
      * IDs, and getString(int) for information on how string resources are
@@ -1315,7 +1422,7 @@
         }
         synchronized (mSync) {
             if (mPluralRule != null) {
-                mPluralRule = PluralRules.ruleForLocale(config.locale);
+                mPluralRule = NativePluralRules.forLocale(config.locale);
             }
         }
     }
@@ -1519,7 +1626,7 @@
     /**
      * Parse a series of {@link android.R.styleable#Extra &lt;extra&gt;} tags from
      * an XML file.  You call this when you are at the parent tag of the
-     * extra tags, and it return once all of the child tags have been parsed.
+     * extra tags, and it will return once all of the child tags have been parsed.
      * This will call {@link #parseBundleExtra} for each extra tag encountered.
      * 
      * @param parser The parser from which to retrieve the extras.
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index a5e5e46..bfaeb82 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -18,16 +18,11 @@
 
 import android.content.ContentResolver;
 import android.net.Uri;
+import android.os.Bundle;
 import android.util.Config;
 import android.util.Log;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
 
 import java.lang.ref.WeakReference;
-import java.lang.UnsupportedOperationException;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -56,6 +51,10 @@
     abstract public double getDouble(int column);
     abstract public boolean isNull(int column);
 
+    public int getType(int column) {
+        throw new UnsupportedOperationException();
+    }
+
     // TODO implement getBlob in all cursor types
     public byte[] getBlob(int column) {
         throw new UnsupportedOperationException("getBlob is not supported");
@@ -88,7 +87,7 @@
         }
         mDataSetObservable.notifyInvalidated();
     }
-    
+
     public boolean requery() {
         if (mSelfObserver != null && mSelfObserverRegistered == false) {
             mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
@@ -109,22 +108,6 @@
     }
 
     /**
-     * @hide
-     * @deprecated
-     */
-    public boolean commitUpdates(Map<? extends Long,? extends Map<String,Object>> values) {
-        return false;
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean deleteRow() {
-        return false;
-    }
-
-    /**
      * This function is called every time the cursor is successfully scrolled
      * to a new position, giving the subclass a chance to update any state it
      * may have. If it returns false the move function will also do so and the
@@ -320,137 +303,6 @@
         return getColumnNames()[columnIndex];
     }
 
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateBlob(int columnIndex, byte[] value) {
-        return update(columnIndex, value);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateString(int columnIndex, String value) {
-        return update(columnIndex, value);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateShort(int columnIndex, short value) {
-        return update(columnIndex, Short.valueOf(value));
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateInt(int columnIndex, int value) {
-        return update(columnIndex, Integer.valueOf(value));
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateLong(int columnIndex, long value) {
-        return update(columnIndex, Long.valueOf(value));
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateFloat(int columnIndex, float value) {
-        return update(columnIndex, Float.valueOf(value));
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateDouble(int columnIndex, double value) {
-        return update(columnIndex, Double.valueOf(value));
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateToNull(int columnIndex) {
-        return update(columnIndex, null);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean update(int columnIndex, Object obj) {
-        if (!supportsUpdates()) {
-            return false;
-        }
-
-        // Long.valueOf() returns null sometimes!
-//        Long rowid = Long.valueOf(getLong(mRowIdColumnIndex));
-        Long rowid = new Long(getLong(mRowIdColumnIndex));
-        if (rowid == null) {
-            throw new IllegalStateException("null rowid. mRowIdColumnIndex = " + mRowIdColumnIndex);
-        }
-
-        synchronized(mUpdatedRows) {
-            Map<String, Object> row = mUpdatedRows.get(rowid);
-            if (row == null) {
-                row = new HashMap<String, Object>();
-                mUpdatedRows.put(rowid, row);
-            }
-            row.put(getColumnNames()[columnIndex], obj);
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns <code>true</code> if there are pending updates that have not yet been committed.
-     * 
-     * @return <code>true</code> if there are pending updates that have not yet been committed.
-     * @hide
-     * @deprecated
-     */
-    public boolean hasUpdates() {
-        synchronized(mUpdatedRows) {
-            return mUpdatedRows.size() > 0;
-        }
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public void abortUpdates() {
-        synchronized(mUpdatedRows) {
-            mUpdatedRows.clear();
-        }
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean commitUpdates() {
-        return commitUpdates(null);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean supportsUpdates() {
-        return mRowIdColumnIndex != -1;
-    }
-
     public void registerContentObserver(ContentObserver observer) {
         mContentObservable.registerObserver(observer);
     }
@@ -478,9 +330,9 @@
         return mDataSetObservable;
         
     }
+
     public void registerDataSetObserver(DataSetObserver observer) {
         mDataSetObservable.registerObserver(observer);
-        
     }
 
     public void unregisterDataSetObserver(DataSetObserver observer) {
@@ -522,6 +374,10 @@
         }
     }
 
+    public Uri getNotificationUri() {
+        return mNotifyUri;
+    }
+
     public boolean getWantsAllOnMoveCalls() {
         return false;
     }
@@ -535,36 +391,19 @@
     }
 
     /**
-     * This function returns true if the field has been updated and is
-     * used in conjunction with {@link #getUpdatedField} to allow subclasses to
-     * support reading uncommitted updates. NOTE: This function and
-     * {@link #getUpdatedField} should be called together inside of a
-     * block synchronized on mUpdatedRows.
-     *
-     * @param columnIndex the column index of the field to check
-     * @return true if the field has been updated, false otherwise
+     * @deprecated Always returns false since Cursors do not support updating rows
      */
+    @Deprecated
     protected boolean isFieldUpdated(int columnIndex) {
-        if (mRowIdColumnIndex != -1 && mUpdatedRows.size() > 0) {
-            Map<String, Object> updates = mUpdatedRows.get(mCurrentRowID);
-            if (updates != null && updates.containsKey(getColumnNames()[columnIndex])) {
-                return true;
-            }
-        }
         return false;
     }
 
     /**
-     * This function returns the uncommitted updated value for the field
-     * at columnIndex.  NOTE: This function and {@link #isFieldUpdated} should
-     * be called together inside of a block synchronized on mUpdatedRows.
-     *
-     * @param columnIndex the column index of the field to retrieve
-     * @return the updated value
+     * @deprecated Always returns null since Cursors do not support updating rows
      */
+    @Deprecated
     protected Object getUpdatedField(int columnIndex) {
-        Map<String, Object> updates = mUpdatedRows.get(mCurrentRowID);
-        return updates.get(getColumnNames()[columnIndex]);
+        return null;
     }
 
     /**
@@ -614,11 +453,9 @@
     }
 
     /**
-     * This HashMap contains a mapping from Long rowIDs to another Map
-     * that maps from String column names to new values. A NULL value means to
-     * remove an existing value, and all numeric values are in their class
-     * forms, i.e. Integer, Long, Float, etc.
+     * @deprecated This is never updated by this class and should not be used
      */
+    @Deprecated
     protected HashMap<Long, Map<String, Object>> mUpdatedRows;
 
     /**
@@ -628,6 +465,11 @@
     protected int mRowIdColumnIndex;
 
     protected int mPos;
+    /**
+     * If {@link #mRowIdColumnIndex} is not -1 this contains contains the value of
+     * the column at {@link #mRowIdColumnIndex} for the current row this cursor is
+     * pointing at.
+     */
     protected Long mCurrentRowID;
     protected ContentResolver mContentResolver;
     protected boolean mClosed = false;
diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java
index 27a02e2..8addaa8 100644
--- a/core/java/android/database/AbstractWindowedCursor.java
+++ b/core/java/android/database/AbstractWindowedCursor.java
@@ -19,202 +19,105 @@
 /**
  * A base class for Cursors that store their data in {@link CursorWindow}s.
  */
-public abstract class AbstractWindowedCursor extends AbstractCursor
-{
+public abstract class AbstractWindowedCursor extends AbstractCursor {
     @Override
-    public byte[] getBlob(int columnIndex)
-    {
+    public byte[] getBlob(int columnIndex) {
         checkPosition();
-
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                return (byte[])getUpdatedField(columnIndex);
-            }
-        }
-
         return mWindow.getBlob(mPos, columnIndex);
     }
 
     @Override
-    public String getString(int columnIndex)
-    {
+    public String getString(int columnIndex) {
         checkPosition();
-
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                return (String)getUpdatedField(columnIndex);
-            }
-        }
-
         return mWindow.getString(mPos, columnIndex);
     }
-    
+
     @Override
-    public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer)
-    {
+    public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
         checkPosition();
-        
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                super.copyStringToBuffer(columnIndex, buffer);
-            }
-        }
-        
         mWindow.copyStringToBuffer(mPos, columnIndex, buffer);
     }
 
     @Override
-    public short getShort(int columnIndex)
-    {
+    public short getShort(int columnIndex) {
         checkPosition();
-
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                Number value = (Number)getUpdatedField(columnIndex);
-                return value.shortValue();
-            }
-        }
-
         return mWindow.getShort(mPos, columnIndex);
     }
 
     @Override
-    public int getInt(int columnIndex)
-    {
+    public int getInt(int columnIndex) {
         checkPosition();
-
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                Number value = (Number)getUpdatedField(columnIndex);
-                return value.intValue();
-            }
-        }
-
         return mWindow.getInt(mPos, columnIndex);
     }
 
     @Override
-    public long getLong(int columnIndex)
-    {
+    public long getLong(int columnIndex) {
         checkPosition();
-
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                Number value = (Number)getUpdatedField(columnIndex);
-                return value.longValue();
-            }
-        }
-
         return mWindow.getLong(mPos, columnIndex);
     }
 
     @Override
-    public float getFloat(int columnIndex)
-    {
+    public float getFloat(int columnIndex) {
         checkPosition();
-
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                Number value = (Number)getUpdatedField(columnIndex);
-                return value.floatValue();
-            }
-        }
-
         return mWindow.getFloat(mPos, columnIndex);
     }
 
     @Override
-    public double getDouble(int columnIndex)
-    {
+    public double getDouble(int columnIndex) {
         checkPosition();
-
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                Number value = (Number)getUpdatedField(columnIndex);
-                return value.doubleValue();
-            }
-        }
-
         return mWindow.getDouble(mPos, columnIndex);
     }
 
     @Override
-    public boolean isNull(int columnIndex)
-    {
+    public boolean isNull(int columnIndex) {
         checkPosition();
-
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                return getUpdatedField(columnIndex) == null;
-            }
-        }
-
-        return mWindow.isNull(mPos, columnIndex);
+        return mWindow.getType(mPos, columnIndex) == Cursor.FIELD_TYPE_NULL;
     }
 
-    public boolean isBlob(int columnIndex)
-    {
-        checkPosition();
-
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                Object object = getUpdatedField(columnIndex);
-                return object == null || object instanceof byte[];
-            }
-        }
-
-        return mWindow.isBlob(mPos, columnIndex);
+    /**
+     * @deprecated Use {@link #getType}
+     */
+    @Deprecated
+    public boolean isBlob(int columnIndex) {
+        return getType(columnIndex) == Cursor.FIELD_TYPE_BLOB;
     }
 
-    public boolean isString(int columnIndex)
-    {
-        checkPosition();
-
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                Object object = getUpdatedField(columnIndex);
-                return object == null || object instanceof String;
-            }
-        }
-
-        return mWindow.isString(mPos, columnIndex);
+    /**
+     * @deprecated Use {@link #getType}
+     */
+    @Deprecated
+    public boolean isString(int columnIndex) {
+        return getType(columnIndex) == Cursor.FIELD_TYPE_STRING;
     }
 
-    public boolean isLong(int columnIndex)
-    {
-        checkPosition();
-
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                Object object = getUpdatedField(columnIndex);
-                return object != null && (object instanceof Integer || object instanceof Long);
-            }
-        }
-
-        return mWindow.isLong(mPos, columnIndex);
+    /**
+     * @deprecated Use {@link #getType}
+     */
+    @Deprecated
+    public boolean isLong(int columnIndex) {
+        return getType(columnIndex) == Cursor.FIELD_TYPE_INTEGER;
     }
 
-    public boolean isFloat(int columnIndex)
-    {
-        checkPosition();
-
-        synchronized(mUpdatedRows) {
-            if (isFieldUpdated(columnIndex)) {
-                Object object = getUpdatedField(columnIndex);
-                return object != null && (object instanceof Float || object instanceof Double);
-            }
-        }
-
-        return mWindow.isFloat(mPos, columnIndex);
+    /**
+     * @deprecated Use {@link #getType}
+     */
+    @Deprecated
+    public boolean isFloat(int columnIndex) {
+        return getType(columnIndex) == Cursor.FIELD_TYPE_FLOAT;
     }
 
     @Override
-    protected void checkPosition()
-    {
+    public int getType(int columnIndex) {
+        checkPosition();
+        return mWindow.getType(mPos, columnIndex);
+    }
+
+    @Override
+    protected void checkPosition() {
         super.checkPosition();
         
         if (mWindow == null) {
-            throw new StaleDataException("Access closed cursor");
+            throw new StaleDataException("Attempting to access a closed cursor");
         }
     }
 
diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java
index baa94d8..fa62d69 100644
--- a/core/java/android/database/BulkCursorNative.java
+++ b/core/java/android/database/BulkCursorNative.java
@@ -17,13 +17,10 @@
 package android.database;
 
 import android.os.Binder;
-import android.os.RemoteException;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Parcel;
-import android.os.Bundle;
-
-import java.util.HashMap;
-import java.util.Map;
+import android.os.RemoteException;
 
 /**
  * Native implementation of the bulk cursor. This is only for use in implementing
@@ -120,26 +117,6 @@
                     return true;
                 }
 
-                case UPDATE_ROWS_TRANSACTION: {
-                    data.enforceInterface(IBulkCursor.descriptor);
-                    // TODO - what ClassLoader should be passed to readHashMap?
-                    // TODO - switch to Bundle
-                    HashMap<Long, Map<String, Object>> values = data.readHashMap(null);
-                    boolean result = updateRows(values);
-                    reply.writeNoException();
-                    reply.writeInt((result == true ? 1 : 0));
-                    return true;
-                }
-
-                case DELETE_ROW_TRANSACTION: {
-                    data.enforceInterface(IBulkCursor.descriptor);
-                    int position = data.readInt();
-                    boolean result = deleteRow(position);
-                    reply.writeNoException();
-                    reply.writeInt((result == true ? 1 : 0));
-                    return true;
-                }
-
                 case ON_MOVE_TRANSACTION: {
                     data.enforceInterface(IBulkCursor.descriptor);
                     int position = data.readInt();
@@ -343,48 +320,6 @@
         return count;
     }
 
-    public boolean updateRows(Map values) throws RemoteException
-    {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-
-        data.writeInterfaceToken(IBulkCursor.descriptor);
-
-        data.writeMap(values);
-
-        mRemote.transact(UPDATE_ROWS_TRANSACTION, data, reply, 0);
-
-        DatabaseUtils.readExceptionFromParcel(reply);
-        
-        boolean result = (reply.readInt() == 1 ? true : false);
-
-        data.recycle();
-        reply.recycle();
-
-        return result;
-    }
-
-    public boolean deleteRow(int position) throws RemoteException
-    {
-        Parcel data = Parcel.obtain();
-        Parcel reply = Parcel.obtain();
-
-        data.writeInterfaceToken(IBulkCursor.descriptor);
-
-        data.writeInt(position);
-
-        mRemote.transact(DELETE_ROW_TRANSACTION, data, reply, 0);
-
-        DatabaseUtils.readExceptionFromParcel(reply);
-        
-        boolean result = (reply.readInt() == 1 ? true : false);
-
-        data.recycle();
-        reply.recycle();
-
-        return result;
-    }
-
     public boolean getWantsAllOnMoveCalls() throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java
index 1469ea2..2cb2aec 100644
--- a/core/java/android/database/BulkCursorToCursorAdaptor.java
+++ b/core/java/android/database/BulkCursorToCursorAdaptor.java
@@ -16,12 +16,10 @@
 
 package android.database;
 
-import android.os.RemoteException;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.util.Log;
 
-import java.util.Map;
-
 /**
  * Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local
  * process.
@@ -174,38 +172,6 @@
         }
     }
 
-    /**
-     * @hide
-     * @deprecated
-     */
-    @Override
-    public boolean deleteRow() {
-        try {
-            boolean result = mBulkCursor.deleteRow(mPos);
-            if (result != false) {
-                // The window contains the old value, discard it
-                mWindow = null;
-    
-                // Fix up the position
-                mCount = mBulkCursor.count();
-                if (mPos < mCount) {
-                    int oldPos = mPos;
-                    mPos = -1;
-                    moveToPosition(oldPos);
-                } else {
-                    mPos = mCount;
-                }
-
-                // Send the change notification
-                onChange(true);
-            }
-            return result;
-        } catch (RemoteException ex) {
-            Log.e(TAG, "Unable to delete row because the remote process is dead");
-            return false;
-        }
-    }
-
     @Override
     public String[] getColumnNames() {
         if (mColumns == null) {
@@ -219,44 +185,6 @@
         return mColumns;
     }
 
-    /**
-     * @hide
-     * @deprecated
-     */
-    @Override
-    public boolean commitUpdates(Map<? extends Long,
-            ? extends Map<String,Object>> additionalValues) {
-        if (!supportsUpdates()) {
-            Log.e(TAG, "commitUpdates not supported on this cursor, did you include the _id column?");
-            return false;
-        }
-
-        synchronized(mUpdatedRows) {
-            if (additionalValues != null) {
-                mUpdatedRows.putAll(additionalValues);
-            }
-
-            if (mUpdatedRows.size() <= 0) {
-                return false;
-            }
-
-            try {
-                boolean result = mBulkCursor.updateRows(mUpdatedRows);
-    
-                if (result == true) {
-                    mUpdatedRows.clear();
-
-                    // Send the change notification
-                    onChange(true);
-                }
-                return result;
-            } catch (RemoteException ex) {
-                Log.e(TAG, "Unable to commit updates because the remote process is dead");
-                return false;
-            }
-        }
-    }
-
     @Override
     public Bundle getExtras() {
         try {
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index e339a63..a9a71cf 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -20,8 +20,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 
-import java.util.Map;
-
 /**
  * This interface provides random read-write access to the result set returned
  * by a database query.
@@ -30,6 +28,25 @@
  * threads should perform its own synchronization when using the Cursor.
  */
 public interface Cursor {
+    /*
+     * Values returned by {@link #getType(int)}.
+     * These should be consistent with the corresponding types defined in CursorWindow.h
+     */
+    /** Value returned by {@link #getType(int)} if the specified column is null */
+    static final int FIELD_TYPE_NULL = 0;
+
+    /** Value returned by {@link #getType(int)} if the specified  column type is integer */
+    static final int FIELD_TYPE_INTEGER = 1;
+
+    /** Value returned by {@link #getType(int)} if the specified column type is float */
+    static final int FIELD_TYPE_FLOAT = 2;
+
+    /** Value returned by {@link #getType(int)} if the specified column type is string */
+    static final int FIELD_TYPE_STRING = 3;
+
+    /** Value returned by {@link #getType(int)} if the specified column type is blob */
+    static final int FIELD_TYPE_BLOB = 4;
+
     /**
      * Returns the numbers of rows in the cursor.
      *
@@ -146,22 +163,6 @@
     boolean isAfterLast();
 
     /**
-     * Removes the row at the current cursor position from the underlying data
-     * store. After this method returns the cursor will be pointing to the row
-     * after the row that is deleted. This has the side effect of decrementing
-     * the result of count() by one.
-     * <p>
-     * The query must have the row ID column in its selection, otherwise this
-     * call will fail.
-     * 
-     * @hide
-     * @return whether the record was successfully deleted.
-     * @deprecated use {@link ContentResolver#delete(Uri, String, String[])}
-     */
-    @Deprecated
-    boolean deleteRow();
-
-    /**
      * Returns the zero-based index for the given column name, or -1 if the column doesn't exist.
      * If you expect the column to exist use {@link #getColumnIndexOrThrow(String)} instead, which
      * will make the error more clear.
@@ -308,6 +309,27 @@
     double getDouble(int columnIndex);
 
     /**
+     * Returns data type of the given column's value.
+     * The preferred type of the column is returned but the data may be converted to other types
+     * as documented in the get-type methods such as {@link #getInt(int)}, {@link #getFloat(int)}
+     * etc.
+     *<p>
+     * Returned column types are
+     * <ul>
+     *   <li>{@link #FIELD_TYPE_NULL}</li>
+     *   <li>{@link #FIELD_TYPE_INTEGER}</li>
+     *   <li>{@link #FIELD_TYPE_FLOAT}</li>
+     *   <li>{@link #FIELD_TYPE_STRING}</li>
+     *   <li>{@link #FIELD_TYPE_BLOB}</li>
+     *</ul>
+     *</p>
+     *
+     * @param columnIndex the zero-based index of the target column.
+     * @return column value type
+     */
+    int getType(int columnIndex);
+
+    /**
      * Returns <code>true</code> if the value in the indicated column is null.
      *
      * @param columnIndex the zero-based index of the target column.
@@ -316,188 +338,6 @@
     boolean isNull(int columnIndex);
 
     /**
-     * Returns <code>true</code> if the cursor supports updates.
-     *
-     * @return whether the cursor supports updates.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    boolean supportsUpdates();
-
-    /**
-     * Returns <code>true</code> if there are pending updates that have not yet been committed.
-     * 
-     * @return <code>true</code> if there are pending updates that have not yet been committed.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    boolean hasUpdates();
-
-    /**
-     * Updates the value for the given column in the row the cursor is
-     * currently pointing at. Updates are not committed to the backing store
-     * until {@link #commitUpdates()} is called.
-     *
-     * @param columnIndex the zero-based index of the target column.
-     * @param value the new value.
-     * @return whether the operation succeeded.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    boolean updateBlob(int columnIndex, byte[] value);
-
-    /**
-     * Updates the value for the given column in the row the cursor is
-     * currently pointing at. Updates are not committed to the backing store
-     * until {@link #commitUpdates()} is called.
-     *
-     * @param columnIndex the zero-based index of the target column.
-     * @param value the new value.
-     * @return whether the operation succeeded.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    boolean updateString(int columnIndex, String value);
-
-    /**
-     * Updates the value for the given column in the row the cursor is
-     * currently pointing at. Updates are not committed to the backing store
-     * until {@link #commitUpdates()} is called.
-     *
-     * @param columnIndex the zero-based index of the target column.
-     * @param value the new value.
-     * @return whether the operation succeeded.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    boolean updateShort(int columnIndex, short value);
-
-    /**
-     * Updates the value for the given column in the row the cursor is
-     * currently pointing at. Updates are not committed to the backing store
-     * until {@link #commitUpdates()} is called.
-     *
-     * @param columnIndex the zero-based index of the target column.
-     * @param value the new value.
-     * @return whether the operation succeeded.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    boolean updateInt(int columnIndex, int value);
-
-    /**
-     * Updates the value for the given column in the row the cursor is
-     * currently pointing at. Updates are not committed to the backing store
-     * until {@link #commitUpdates()} is called.
-     *
-     * @param columnIndex the zero-based index of the target column.
-     * @param value the new value.
-     * @return whether the operation succeeded.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    boolean updateLong(int columnIndex, long value);
-
-    /**
-     * Updates the value for the given column in the row the cursor is
-     * currently pointing at. Updates are not committed to the backing store
-     * until {@link #commitUpdates()} is called.
-     *
-     * @param columnIndex the zero-based index of the target column.
-     * @param value the new value.
-     * @return whether the operation succeeded.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    boolean updateFloat(int columnIndex, float value);
-
-    /**
-     * Updates the value for the given column in the row the cursor is
-     * currently pointing at. Updates are not committed to the backing store
-     * until {@link #commitUpdates()} is called.
-     *
-     * @param columnIndex the zero-based index of the target column.
-     * @param value the new value.
-     * @return whether the operation succeeded.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    boolean updateDouble(int columnIndex, double value);
-
-    /**
-     * Removes the value for the given column in the row the cursor is
-     * currently pointing at. Updates are not committed to the backing store
-     * until {@link #commitUpdates()} is called.
-     *
-     * @param columnIndex the zero-based index of the target column.
-     * @return whether the operation succeeded.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    boolean updateToNull(int columnIndex);
-
-    /**
-     * Atomically commits all updates to the backing store. After completion,
-     * this method leaves the data in an inconsistent state and you should call
-     * {@link #requery} before reading data from the cursor again.
-     *
-     * @return whether the operation succeeded.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    boolean commitUpdates();
-
-    /**
-     * Atomically commits all updates to the backing store, as well as the
-     * updates included in values. After completion,
-     * this method leaves the data in an inconsistent state and you should call
-     * {@link #requery} before reading data from the cursor again.
-     *
-     * @param values A map from row IDs to Maps associating column names with
-     *               updated values. A null value indicates the field should be
-                     removed.
-     * @return whether the operation succeeded.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    boolean commitUpdates(Map<? extends Long,
-            ? extends Map<String,Object>> values);
-
-    /**
-     * Reverts all updates made to the cursor since the last call to
-     * commitUpdates.
-     * @hide
-     * @deprecated use the {@link ContentResolver} update methods instead of the Cursor
-     * update methods
-     */
-    @Deprecated
-    void abortUpdates();
-
-    /**
      * Deactivates the Cursor, making all calls on it fail until {@link #requery} is called.
      * Inactive Cursors use fewer resources than active Cursors.
      * Calling {@link #requery} will make the cursor active again.
@@ -509,9 +349,16 @@
      * contents. This may be done at any time, including after a call to {@link
      * #deactivate}.
      *
+     * Since this method could execute a query on the database and potentially take
+     * a while, it could cause ANR if it is called on Main (UI) thread.
+     * A warning is printed if this method is being executed on Main thread.
+     *
      * @return true if the requery succeeded, false if not, in which case the
      *         cursor becomes invalid.
+     * @deprecated Don't use this. Just request a new cursor, so you can do this
+     * asynchronously and update your list view once the new cursor comes back.
      */
+    @Deprecated
     boolean requery();
 
     /**
diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java
index 748eb99..8bc7de2 100644
--- a/core/java/android/database/CursorToBulkCursorAdaptor.java
+++ b/core/java/android/database/CursorToBulkCursorAdaptor.java
@@ -16,16 +16,12 @@
 
 package android.database;
 
-import android.database.sqlite.SQLiteMisuseException;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Config;
 import android.util.Log;
 
-import java.util.Map;
-
 
 /**
  * Wraps a BulkCursor around an existing Cursor making it remotable.
@@ -38,7 +34,6 @@
     private final CrossProcessCursor mCursor;
     private CursorWindow mWindow;
     private final String mProviderName;
-    private final boolean mReadOnly;
     private ContentObserverProxy mObserver;
 
     private static final class ContentObserverProxy extends ContentObserver 
@@ -98,7 +93,6 @@
                     "Only CrossProcessCursor cursors are supported across process for now", e);
         }
         mProviderName = providerName;
-        mReadOnly = !allowWrite;
 
         createAndRegisterObserverProxy(observer);
     }
@@ -197,31 +191,6 @@
         }
     }
 
-    public boolean updateRows(Map<? extends Long, ? extends Map<String, Object>> values) {
-        if (mReadOnly) {
-            Log.w("ContentProvider", "Permission Denial: modifying "
-                    + mProviderName
-                    + " from pid=" + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
-            return false;
-        }
-        return mCursor.commitUpdates(values);
-    }
-
-    public boolean deleteRow(int position) {
-        if (mReadOnly) {
-            Log.w("ContentProvider", "Permission Denial: modifying "
-                    + mProviderName
-                    + " from pid=" + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
-            return false;
-        }
-        if (mCursor.moveToPosition(position) == false) {
-            return false;
-        }
-        return mCursor.deleteRow();
-    }
-
     public Bundle getExtras() {
         return mCursor.getExtras();
     }
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 76b91f5..a026eca 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -217,18 +217,13 @@
      * @param row the row to read from, row - getStartPosition() being the actual row in the window
      * @param col the column to read from
      * @return {@code true} if given field is {@code NULL}
+     * @deprecated use {@link #getType(int, int)} instead
      */
+    @Deprecated
     public boolean isNull(int row, int col) {
-        acquireReference();
-        try {
-            return isNull_native(row - mStartPos, col);
-        } finally {
-            releaseReference();
-        }
+        return getType(row, col) == Cursor.FIELD_TYPE_NULL;
     }
     
-    private native boolean isNull_native(int row, int col);
-    
     /**
      * Returns a byte array for the given field.
      *
@@ -257,19 +252,43 @@
     private native byte[] getBlob_native(int row, int col);
 
     /**
+     * Returns data type of the given column's value.
+     *<p>
+     * Returned column types are
+     * <ul>
+     *   <li>{@link Cursor#FIELD_TYPE_NULL}</li>
+     *   <li>{@link Cursor#FIELD_TYPE_INTEGER}</li>
+     *   <li>{@link Cursor#FIELD_TYPE_FLOAT}</li>
+     *   <li>{@link Cursor#FIELD_TYPE_STRING}</li>
+     *   <li>{@link Cursor#FIELD_TYPE_BLOB}</li>
+     *</ul>
+     *</p>
+     *
+     * @param row the row to read from, row - getStartPosition() being the actual row in the window
+     * @param col the column to read from
+     * @return the value type
+     */
+    public int getType(int row, int col) {
+        acquireReference();
+        try {
+            return getType_native(row - mStartPos, col);
+        } finally {
+            releaseReference();
+        }
+    }
+
+    /**
      * Checks if a field contains either a blob or is null.
      *
      * @param row the row to read from, row - getStartPosition() being the actual row in the window
      * @param col the column to read from
      * @return {@code true} if given field is {@code NULL} or a blob
+     * @deprecated use {@link #getType(int, int)} instead
      */
+    @Deprecated
     public boolean isBlob(int row, int col) {
-        acquireReference();
-        try {
-            return isBlob_native(row - mStartPos, col);
-        } finally {
-            releaseReference();
-        }
+        int type = getType(row, col);
+        return type == Cursor.FIELD_TYPE_BLOB || type == Cursor.FIELD_TYPE_NULL;
     }
 
     /**
@@ -278,14 +297,11 @@
      * @param row the row to read from, row - getStartPosition() being the actual row in the window
      * @param col the column to read from
      * @return {@code true} if given field is a long
+     * @deprecated use {@link #getType(int, int)} instead
      */
+    @Deprecated
     public boolean isLong(int row, int col) {
-        acquireReference();
-        try {
-            return isInteger_native(row - mStartPos, col);
-        } finally {
-            releaseReference();
-        }
+        return getType(row, col) == Cursor.FIELD_TYPE_INTEGER;
     }
 
     /**
@@ -294,14 +310,11 @@
      * @param row the row to read from, row - getStartPosition() being the actual row in the window
      * @param col the column to read from
      * @return {@code true} if given field is a float
+     * @deprecated use {@link #getType(int, int)} instead
      */
+    @Deprecated
     public boolean isFloat(int row, int col) {
-        acquireReference();
-        try {
-            return isFloat_native(row - mStartPos, col);
-        } finally {
-            releaseReference();
-        }
+        return getType(row, col) == Cursor.FIELD_TYPE_FLOAT;
     }
 
     /**
@@ -310,20 +323,15 @@
      * @param row the row to read from, row - getStartPosition() being the actual row in the window
      * @param col the column to read from
      * @return {@code true} if given field is {@code NULL} or a String
+     * @deprecated use {@link #getType(int, int)} instead
      */
+    @Deprecated
     public boolean isString(int row, int col) {
-        acquireReference();
-        try {
-            return isString_native(row - mStartPos, col);
-        } finally {
-            releaseReference();
-        }
+        int type = getType(row, col);
+        return type == Cursor.FIELD_TYPE_STRING || type == Cursor.FIELD_TYPE_NULL;
     }
 
-    private native boolean isBlob_native(int row, int col);
-    private native boolean isString_native(int row, int col);
-    private native boolean isInteger_native(int row, int col);
-    private native boolean isFloat_native(int row, int col);
+    private native int getType_native(int row, int col);
 
     /**
      * Returns a String for the given field.
diff --git a/core/java/android/database/CursorWrapper.java b/core/java/android/database/CursorWrapper.java
index f0aa7d7..3c3bd43 100644
--- a/core/java/android/database/CursorWrapper.java
+++ b/core/java/android/database/CursorWrapper.java
@@ -17,28 +17,26 @@
 package android.database;
 
 import android.content.ContentResolver;
-import android.database.CharArrayBuffer;
 import android.net.Uri;
 import android.os.Bundle;
 
-import java.util.Map;
-
 /**
- * Wrapper class for Cursor that delegates all calls to the actual cursor object
+ * Wrapper class for Cursor that delegates all calls to the actual cursor object.  The primary
+ * use for this class is to extend a cursor while overriding only a subset of its methods.
  */
-
 public class CursorWrapper implements Cursor {
 
+    private final Cursor mCursor;
+
     public CursorWrapper(Cursor cursor) {
         mCursor = cursor;
     }
-    
+
     /**
-     * @hide
-     * @deprecated
+     * @return the wrapped cursor
      */
-    public void abortUpdates() {
-        mCursor.abortUpdates();
+    public Cursor getWrappedCursor() {
+        return mCursor;
     }
 
     public void close() {
@@ -49,23 +47,6 @@
         return mCursor.isClosed();
     }
 
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean commitUpdates() {
-        return mCursor.commitUpdates();
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean commitUpdates(
-            Map<? extends Long, ? extends Map<String, Object>> values) {
-        return mCursor.commitUpdates(values);
-    }
-
     public int getCount() {
         return mCursor.getCount();
     }
@@ -74,14 +55,6 @@
         mCursor.deactivate();
     }
 
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean deleteRow() {
-        return mCursor.deleteRow();
-    }
-
     public boolean moveToFirst() {
         return mCursor.moveToFirst();
     }
@@ -147,14 +120,6 @@
         return mCursor.getWantsAllOnMoveCalls();
     }
 
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean hasUpdates() {
-        return mCursor.hasUpdates();
-    }
-
     public boolean isAfterLast() {
         return mCursor.isAfterLast();
     }
@@ -171,6 +136,10 @@
         return mCursor.isLast();
     }
 
+    public int getType(int columnIndex) {
+        return mCursor.getType(columnIndex);
+    }
+
     public boolean isNull(int columnIndex) {
         return mCursor.isNull(columnIndex);
     }
@@ -219,14 +188,6 @@
         mCursor.setNotificationUri(cr, uri);        
     }
 
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean supportsUpdates() {
-        return mCursor.supportsUpdates();
-    }
-
     public void unregisterContentObserver(ContentObserver observer) {
         mCursor.unregisterContentObserver(observer);        
     }
@@ -234,72 +195,5 @@
     public void unregisterDataSetObserver(DataSetObserver observer) {
         mCursor.unregisterDataSetObserver(observer);
     }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateDouble(int columnIndex, double value) {
-        return mCursor.updateDouble(columnIndex, value);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateFloat(int columnIndex, float value) {
-        return mCursor.updateFloat(columnIndex, value);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateInt(int columnIndex, int value) {
-        return mCursor.updateInt(columnIndex, value);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateLong(int columnIndex, long value) {
-        return mCursor.updateLong(columnIndex, value);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateShort(int columnIndex, short value) {
-        return mCursor.updateShort(columnIndex, value);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateString(int columnIndex, String value) {
-        return mCursor.updateString(columnIndex, value);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateBlob(int columnIndex, byte[] value) {
-        return mCursor.updateBlob(columnIndex, value);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    public boolean updateToNull(int columnIndex) {
-        return mCursor.updateToNull(columnIndex);
-    }
-    
-    private Cursor mCursor;
-    
 }
 
diff --git a/core/java/android/database/DataSetObservable.java b/core/java/android/database/DataSetObservable.java
index 9200e81..51c72c1 100644
--- a/core/java/android/database/DataSetObservable.java
+++ b/core/java/android/database/DataSetObservable.java
@@ -27,8 +27,12 @@
      */
     public void notifyChanged() {
         synchronized(mObservers) {
-            for (DataSetObserver observer : mObservers) {
-                observer.onChanged();
+            // since onChanged() is implemented by the app, it could do anything, including
+            // removing itself from {@link mObservers} - and that could cause problems if
+            // an iterator is used on the ArrayList {@link mObservers}.
+            // to avoid such problems, just march thru the list in the reverse order.
+            for (int i = mObservers.size() - 1; i >= 0; i--) {
+                mObservers.get(i).onChanged();
             }
         }
     }
@@ -39,8 +43,8 @@
      */
     public void notifyInvalidated() {
         synchronized (mObservers) {
-            for (DataSetObserver observer : mObservers) {
-                observer.onInvalidated();
+            for (int i = mObservers.size() - 1; i >= 0; i--) {
+                mObservers.get(i).onInvalidated();
             }
         }
     }
diff --git a/core/java/android/database/DatabaseErrorHandler.java b/core/java/android/database/DatabaseErrorHandler.java
new file mode 100644
index 0000000..f0c5452
--- /dev/null
+++ b/core/java/android/database/DatabaseErrorHandler.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database;
+
+import android.database.sqlite.SQLiteDatabase;
+
+/**
+ * An interface to let the apps define the actions to take when the following errors are detected
+ *   database corruption
+ */
+public interface DatabaseErrorHandler {
+
+    /**
+     * defines the method to be invoked when database corruption is detected.
+     * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
+     * is detected.
+     */
+    void onCorruption(SQLiteDatabase dbObj);
+}
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 95fc1fd..70a7fb6 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -31,6 +31,7 @@
 import android.database.sqlite.SQLiteProgram;
 import android.database.sqlite.SQLiteStatement;
 import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
 import android.text.TextUtils;
 import android.util.Config;
 import android.util.Log;
@@ -50,6 +51,29 @@
     private static final boolean DEBUG = false;
     private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
 
+    private static final String[] countProjection = new String[]{"count(*)"};
+
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_SELECT = 1;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_UPDATE = 2;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_ATTACH = 3;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_BEGIN = 4;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_COMMIT = 5;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_ABORT = 6;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_PRAGMA = 7;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_DDL = 8;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_UNPREPARED = 9;
+    /** One of the values returned by {@link #getSqlStatementType(String)}. */
+    public static final int STATEMENT_OTHER = 99;
+
     /**
      * Special function for writing an exception result at the header of
      * a parcel, to be used when returning an exception from a transaction.
@@ -191,6 +215,37 @@
     }
 
     /**
+     * Returns data type of the given object's value.
+     *<p>
+     * Returned values are
+     * <ul>
+     *   <li>{@link Cursor#FIELD_TYPE_NULL}</li>
+     *   <li>{@link Cursor#FIELD_TYPE_INTEGER}</li>
+     *   <li>{@link Cursor#FIELD_TYPE_FLOAT}</li>
+     *   <li>{@link Cursor#FIELD_TYPE_STRING}</li>
+     *   <li>{@link Cursor#FIELD_TYPE_BLOB}</li>
+     *</ul>
+     *</p>
+     *
+     * @param obj the object whose value type is to be returned
+     * @return object value type
+     * @hide
+     */
+    public static int getTypeOfObject(Object obj) {
+        if (obj == null) {
+            return Cursor.FIELD_TYPE_NULL;
+        } else if (obj instanceof byte[]) {
+            return Cursor.FIELD_TYPE_BLOB;
+        } else if (obj instanceof Float || obj instanceof Double) {
+            return Cursor.FIELD_TYPE_FLOAT;
+        } else if (obj instanceof Long || obj instanceof Integer) {
+            return Cursor.FIELD_TYPE_INTEGER;
+        } else {
+            return Cursor.FIELD_TYPE_STRING;
+        }
+    }
+
+    /**
      * Appends an SQL string to the given StringBuilder, including the opening
      * and closing single quotes. Any single quotes internal to sqlString will
      * be escaped.
@@ -656,14 +711,8 @@
      * first column of the first row.
      */
     public static long longForQuery(SQLiteStatement prog, String[] selectionArgs) {
-        if (selectionArgs != null) {
-            int size = selectionArgs.length;
-            for (int i = 0; i < size; i++) {
-                bindObjectToProgram(prog, i + 1, selectionArgs[i]);
-            }
-        }
-        long value = prog.simpleQueryForLong();
-        return value;
+        prog.bindAllArgsAsStrings(selectionArgs);
+        return prog.simpleQueryForLong();
     }
 
     /**
@@ -684,14 +733,36 @@
      * first column of the first row.
      */
     public static String stringForQuery(SQLiteStatement prog, String[] selectionArgs) {
-        if (selectionArgs != null) {
-            int size = selectionArgs.length;
-            for (int i = 0; i < size; i++) {
-                bindObjectToProgram(prog, i + 1, selectionArgs[i]);
-            }
+        prog.bindAllArgsAsStrings(selectionArgs);
+        return prog.simpleQueryForString();
+    }
+
+    /**
+     * Utility method to run the query on the db and return the blob value in the
+     * first column of the first row.
+     *
+     * @return A read-only file descriptor for a copy of the blob value.
+     */
+    public static ParcelFileDescriptor blobFileDescriptorForQuery(SQLiteDatabase db,
+            String query, String[] selectionArgs) {
+        SQLiteStatement prog = db.compileStatement(query);
+        try {
+            return blobFileDescriptorForQuery(prog, selectionArgs);
+        } finally {
+            prog.close();
         }
-        String value = prog.simpleQueryForString();
-        return value;
+    }
+
+    /**
+     * Utility method to run the pre-compiled query and return the blob value in the
+     * first column of the first row.
+     *
+     * @return A read-only file descriptor for a copy of the blob value.
+     */
+    public static ParcelFileDescriptor blobFileDescriptorForQuery(SQLiteStatement prog,
+            String[] selectionArgs) {
+        prog.bindAllArgsAsStrings(selectionArgs);
+        return prog.simpleQueryForBlobFileDescriptor();
     }
 
     /**
@@ -704,8 +775,8 @@
      */
     public static void cursorStringToContentValuesIfPresent(Cursor cursor, ContentValues values,
             String column) {
-        final int index = cursor.getColumnIndexOrThrow(column);
-        if (!cursor.isNull(index)) {
+        final int index = cursor.getColumnIndex(column);
+        if (index != -1 && !cursor.isNull(index)) {
             values.put(column, cursor.getString(index));
         }
     }
@@ -720,8 +791,8 @@
      */
     public static void cursorLongToContentValuesIfPresent(Cursor cursor, ContentValues values,
             String column) {
-        final int index = cursor.getColumnIndexOrThrow(column);
-        if (!cursor.isNull(index)) {
+        final int index = cursor.getColumnIndex(column);
+        if (index != -1 && !cursor.isNull(index)) {
             values.put(column, cursor.getLong(index));
         }
     }
@@ -736,8 +807,8 @@
      */
     public static void cursorShortToContentValuesIfPresent(Cursor cursor, ContentValues values,
             String column) {
-        final int index = cursor.getColumnIndexOrThrow(column);
-        if (!cursor.isNull(index)) {
+        final int index = cursor.getColumnIndex(column);
+        if (index != -1 && !cursor.isNull(index)) {
             values.put(column, cursor.getShort(index));
         }
     }
@@ -752,8 +823,8 @@
      */
     public static void cursorIntToContentValuesIfPresent(Cursor cursor, ContentValues values,
             String column) {
-        final int index = cursor.getColumnIndexOrThrow(column);
-        if (!cursor.isNull(index)) {
+        final int index = cursor.getColumnIndex(column);
+        if (index != -1 && !cursor.isNull(index)) {
             values.put(column, cursor.getInt(index));
         }
     }
@@ -768,8 +839,8 @@
      */
     public static void cursorFloatToContentValuesIfPresent(Cursor cursor, ContentValues values,
             String column) {
-        final int index = cursor.getColumnIndexOrThrow(column);
-        if (!cursor.isNull(index)) {
+        final int index = cursor.getColumnIndex(column);
+        if (index != -1 && !cursor.isNull(index)) {
             values.put(column, cursor.getFloat(index));
         }
     }
@@ -784,8 +855,8 @@
      */
     public static void cursorDoubleToContentValuesIfPresent(Cursor cursor, ContentValues values,
             String column) {
-        final int index = cursor.getColumnIndexOrThrow(column);
-        if (!cursor.isNull(index)) {
+        final int index = cursor.getColumnIndex(column);
+        if (index != -1 && !cursor.isNull(index)) {
             values.put(column, cursor.getDouble(index));
         }
     }
@@ -1152,4 +1223,66 @@
         db.setVersion(dbVersion);
         db.close();
     }
+
+    /**
+     * Returns one of the following which represent the type of the given SQL statement.
+     * <ol>
+     *   <li>{@link #STATEMENT_SELECT}</li>
+     *   <li>{@link #STATEMENT_UPDATE}</li>
+     *   <li>{@link #STATEMENT_ATTACH}</li>
+     *   <li>{@link #STATEMENT_BEGIN}</li>
+     *   <li>{@link #STATEMENT_COMMIT}</li>
+     *   <li>{@link #STATEMENT_ABORT}</li>
+     *   <li>{@link #STATEMENT_OTHER}</li>
+     * </ol>
+     * @param sql the SQL statement whose type is returned by this method
+     * @return one of the values listed above
+     */
+    public static int getSqlStatementType(String sql) {
+        sql = sql.trim();
+        if (sql.length() < 3) {
+            return STATEMENT_OTHER;
+        }
+        String prefixSql = sql.substring(0, 3).toUpperCase();
+        if (prefixSql.equals("SEL")) {
+            return STATEMENT_SELECT;
+        } else if (prefixSql.equals("INS") ||
+                prefixSql.equals("UPD") ||
+                prefixSql.equals("REP") ||
+                prefixSql.equals("DEL")) {
+            return STATEMENT_UPDATE;
+        } else if (prefixSql.equals("ATT")) {
+            return STATEMENT_ATTACH;
+        } else if (prefixSql.equals("COM")) {
+            return STATEMENT_COMMIT;
+        } else if (prefixSql.equals("END")) {
+            return STATEMENT_COMMIT;
+        } else if (prefixSql.equals("ROL")) {
+            return STATEMENT_ABORT;
+        } else if (prefixSql.equals("BEG")) {
+            return STATEMENT_BEGIN;
+        } else if (prefixSql.equals("PRA")) {
+            return STATEMENT_PRAGMA;
+        } else if (prefixSql.equals("CRE") || prefixSql.equals("DRO") ||
+                prefixSql.equals("ALT")) {
+            return STATEMENT_DDL;
+        } else if (prefixSql.equals("ANA") || prefixSql.equals("DET")) {
+            return STATEMENT_UNPREPARED;
+        }
+        return STATEMENT_OTHER;
+    }
+
+    /**
+     * Appends one set of selection args to another. This is useful when adding a selection
+     * argument to a user provided set.
+     */
+    public static String[] appendSelectionArgs(String[] originalValues, String[] newValues) {
+        if (originalValues == null || originalValues.length == 0) {
+            return newValues;
+        }
+        String[] result = new String[originalValues.length + newValues.length ];
+        System.arraycopy(originalValues, 0, result, 0, originalValues.length);
+        System.arraycopy(newValues, 0, result, originalValues.length, newValues.length);
+        return result;
+    }
 }
diff --git a/core/java/android/database/DefaultDatabaseErrorHandler.java b/core/java/android/database/DefaultDatabaseErrorHandler.java
new file mode 100644
index 0000000..3619e48
--- /dev/null
+++ b/core/java/android/database/DefaultDatabaseErrorHandler.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.database;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.util.Log;
+import android.util.Pair;
+
+/**
+ * Default class used defining the actions to take when the following errors are detected
+ *   database corruption
+ */
+public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler {
+
+    private static final String TAG = "DefaultDatabaseErrorHandler";
+
+    /**
+     * defines the default method to be invoked when database corruption is detected.
+     * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
+     * is detected.
+     */
+    public void onCorruption(SQLiteDatabase dbObj) {
+        Log.e(TAG, "Corruption reported by sqlite on database: " + dbObj.getPath());
+
+        // is the corruption detected even before database could be 'opened'?
+        if (!dbObj.isOpen()) {
+            // database files are not even openable. delete this database file.
+            // NOTE if the database has attached databases, then any of them could be corrupt.
+            // and not deleting all of them could cause corrupted database file to remain and 
+            // make the application crash on database open operation. To avoid this problem,
+            // the application should provide its own {@link DatabaseErrorHandler} impl class
+            // to delete ALL files of the database (including the attached databases).
+            deleteDatabaseFile(dbObj.getPath());
+            return;
+        }
+
+        ArrayList<Pair<String, String>> attachedDbs = null;
+        try {
+            // Close the database, which will cause subsequent operations to fail.
+            // before that, get the attached database list first.
+            try {
+                attachedDbs = dbObj.getAttachedDbs();
+            } catch (SQLiteException e) {
+                /* ignore */
+            }
+            try {
+                dbObj.close();
+            } catch (SQLiteException e) {
+                /* ignore */
+            }
+        } finally {
+            // Delete all files of this corrupt database and/or attached databases
+            if (attachedDbs != null) {
+                for (Pair<String, String> p : attachedDbs) {
+                    deleteDatabaseFile(p.second);
+                }
+            } else {
+                // attachedDbs = null is possible when the database is so corrupt that even
+                // "PRAGMA database_list;" also fails. delete the main database file
+                deleteDatabaseFile(dbObj.getPath());
+            }
+        }
+    }
+
+    private void deleteDatabaseFile(String fileName) {
+        if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) {
+            return;
+        }
+        Log.e(TAG, "deleting the database file: " + fileName);
+        try {
+            new File(fileName).delete();
+        } catch (Exception e) {
+            /* print warning and ignore exception */
+            Log.w(TAG, "delete failed: " + e.getMessage());
+        }
+    }
+}
diff --git a/core/java/android/database/IBulkCursor.java b/core/java/android/database/IBulkCursor.java
index 46790a3..244c88f 100644
--- a/core/java/android/database/IBulkCursor.java
+++ b/core/java/android/database/IBulkCursor.java
@@ -16,16 +16,14 @@
 
 package android.database;
 
-import android.os.RemoteException;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IInterface;
-import android.os.Bundle;
-
-import java.util.Map;
+import android.os.RemoteException;
 
 /**
  * This interface provides a low-level way to pass bulk cursor data across
- * both process and language boundries. Application code should use the Cursor
+ * both process and language boundaries. Application code should use the Cursor
  * interface directly.
  *
  * {@hide}
@@ -54,10 +52,6 @@
      */
     public String[] getColumnNames() throws RemoteException;
 
-    public boolean updateRows(Map<? extends Long, ? extends Map<String, Object>> values) throws RemoteException;
-
-    public boolean deleteRow(int position) throws RemoteException;
-
     public void deactivate() throws RemoteException;
 
     public void close() throws RemoteException;
@@ -76,8 +70,6 @@
     static final int GET_CURSOR_WINDOW_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
     static final int COUNT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
     static final int GET_COLUMN_NAMES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
-    static final int UPDATE_ROWS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3;
-    static final int DELETE_ROW_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 4;
     static final int DEACTIVATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 5;
     static final int REQUERY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 6;
     static final int ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 7;
diff --git a/core/java/android/database/MatrixCursor.java b/core/java/android/database/MatrixCursor.java
index d5c3a32..5c1b968 100644
--- a/core/java/android/database/MatrixCursor.java
+++ b/core/java/android/database/MatrixCursor.java
@@ -272,6 +272,11 @@
     }
 
     @Override
+    public int getType(int column) {
+        return DatabaseUtils.getTypeOfObject(get(column));
+    }
+
+    @Override
     public boolean isNull(int column) {
         return get(column) == null;
     }
diff --git a/core/java/android/database/MergeCursor.java b/core/java/android/database/MergeCursor.java
index 722d707..2c25db7 100644
--- a/core/java/android/database/MergeCursor.java
+++ b/core/java/android/database/MergeCursor.java
@@ -92,32 +92,6 @@
         return false;
     }
 
-    /**
-     * @hide
-     * @deprecated
-     */
-    @Override
-    public boolean deleteRow()
-    {
-        return mCursor.deleteRow();
-    }
-    
-    /**
-     * @hide
-     * @deprecated
-     */
-    @Override
-    public boolean commitUpdates() {
-        int length = mCursors.length;
-        for (int i = 0 ; i < length ; i++) {
-            if (mCursors[i] != null) {
-                mCursors[i].commitUpdates();
-            }
-        }
-        onChange(true);
-        return true;
-    }
-
     @Override
     public String getString(int column)
     {
@@ -155,6 +129,11 @@
     }
 
     @Override
+    public int getType(int column) {
+        return mCursor.getType(column);
+    }
+
+    @Override
     public boolean isNull(int column)
     {
         return mCursor.isNull(column);
diff --git a/core/java/android/database/sqlite/DatabaseConnectionPool.java b/core/java/android/database/sqlite/DatabaseConnectionPool.java
new file mode 100644
index 0000000..39a9d23
--- /dev/null
+++ b/core/java/android/database/sqlite/DatabaseConnectionPool.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 20010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.sqlite;
+
+import android.content.res.Resources;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Random;
+
+/**
+ * A connection pool to be used by readers.
+ * Note that each connection can be used by only one reader at a time.
+ */
+/* package */ class DatabaseConnectionPool {
+
+    private static final String TAG = "DatabaseConnectionPool";
+
+    /** The default connection pool size. */
+    private volatile int mMaxPoolSize =
+        Resources.getSystem().getInteger(com.android.internal.R.integer.db_connection_pool_size);
+
+    /** The connection pool objects are stored in this member.
+     * TODO: revisit this data struct as the number of pooled connections increase beyond
+     * single-digit values.
+     */
+    private final ArrayList<PoolObj> mPool = new ArrayList<PoolObj>(mMaxPoolSize);
+
+    /** the main database connection to which this connection pool is attached */
+    private final SQLiteDatabase mParentDbObj;
+
+    /** Random number generator used to pick a free connection out of the pool */
+    private Random rand; // lazily initialized
+
+    /* package */ DatabaseConnectionPool(SQLiteDatabase db) {
+        this.mParentDbObj = db;
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Max Pool Size: " + mMaxPoolSize);
+        }
+    }
+
+    /**
+     * close all database connections in the pool - even if they are in use!
+     */
+    /* package */ synchronized void close() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Closing the connection pool on " + mParentDbObj.getPath() + toString());
+        }
+        for (int i = mPool.size() - 1; i >= 0; i--) {
+            mPool.get(i).mDb.close();
+        }
+        mPool.clear();
+    }
+
+    /**
+     * get a free connection from the pool
+     *
+     * @param sql if not null, try to find a connection inthe pool which already has cached
+     * the compiled statement for this sql.
+     * @return the Database connection that the caller can use
+     */
+    /* package */ synchronized SQLiteDatabase get(String sql) {
+        SQLiteDatabase db = null;
+        PoolObj poolObj = null;
+        int poolSize = mPool.size();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            assert sql != null;
+            doAsserts();
+        }
+        if (getFreePoolSize() == 0) {
+            // no free ( = available) connections
+            if (mMaxPoolSize == poolSize) {
+                // maxed out. can't open any more connections.
+                // let the caller wait on one of the pooled connections
+                // preferably a connection caching the pre-compiled statement of the given SQL
+                if (mMaxPoolSize == 1) {
+                    poolObj = mPool.get(0);
+                } else {
+                    for (int i = 0; i < mMaxPoolSize; i++) {
+                        if (mPool.get(i).mDb.isInStatementCache(sql)) {
+                            poolObj = mPool.get(i);
+                            break;
+                        }
+                    }
+                    if (poolObj == null) {
+                        // there are no database connections with the given SQL pre-compiled.
+                        // ok to return any of the connections.
+                        if (rand == null) {
+                            rand = new Random(SystemClock.elapsedRealtime());
+                        }
+                        poolObj = mPool.get(rand.nextInt(mMaxPoolSize));
+                    }
+                }
+                db = poolObj.mDb;
+            } else {
+                // create a new connection and add it to the pool, since we haven't reached
+                // max pool size allowed
+                db = mParentDbObj.createPoolConnection((short)(poolSize + 1));
+                poolObj = new PoolObj(db);
+                mPool.add(poolSize, poolObj);
+            }
+        } else {
+            // there are free connections available. pick one
+            // preferably a connection caching the pre-compiled statement of the given SQL
+            for (int i = 0; i < poolSize; i++) {
+                if (mPool.get(i).isFree() && mPool.get(i).mDb.isInStatementCache(sql)) {
+                    poolObj = mPool.get(i);
+                    break;
+                }
+            }
+            if (poolObj == null) {
+                // didn't find a free database connection with the given SQL already
+                // pre-compiled. return a free connection (this means, the same SQL could be
+                // pre-compiled on more than one database connection. potential wasted memory.)
+                for (int i = 0; i < poolSize; i++) {
+                    if (mPool.get(i).isFree()) {
+                        poolObj = mPool.get(i);
+                        break;
+                    }
+                }
+            }
+            db = poolObj.mDb;
+        }
+
+        assert poolObj != null;
+        assert poolObj.mDb == db;
+
+        poolObj.acquire();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "END get-connection: " + toString() + poolObj.toString());
+        }
+        return db;
+        // TODO if a thread acquires a connection and dies without releasing the connection, then
+        // there could be a connection leak.
+    }
+
+    /**
+     * release the given database connection back to the pool.
+     * @param db the connection to be released
+     */
+    /* package */ synchronized void release(SQLiteDatabase db) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            assert db.mConnectionNum > 0;
+            doAsserts();
+            assert mPool.get(db.mConnectionNum - 1).mDb == db;
+        }
+
+        PoolObj poolObj = mPool.get(db.mConnectionNum - 1);
+
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "BEGIN release-conn: " + toString() + poolObj.toString());
+        }
+
+        if (poolObj.isFree()) {
+            throw new IllegalStateException("Releasing object already freed: " +
+                    db.mConnectionNum);
+        }
+
+        poolObj.release();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "END release-conn: " + toString() + poolObj.toString());
+        }
+    }
+
+    /**
+     * Returns a list of all database connections in the pool (both free and busy connections).
+     * This method is used when "adb bugreport" is done.
+     */
+    /* package */ synchronized ArrayList<SQLiteDatabase> getConnectionList() {
+        ArrayList<SQLiteDatabase> list = new ArrayList<SQLiteDatabase>();
+        for (int i = mPool.size() - 1; i >= 0; i--) {
+            list.add(mPool.get(i).mDb);
+        }
+        return list;
+    }
+
+    /**
+     * package level access for testing purposes only. otherwise, private should be sufficient.
+     */
+    /* package */ int getFreePoolSize() {
+        int count = 0;
+        for (int i = mPool.size() - 1; i >= 0; i--) {
+            if (mPool.get(i).isFree()) {
+                count++;
+            }
+        }
+        return count++;
+    }
+
+    /**
+     * only for testing purposes
+     */
+    /* package */ ArrayList<PoolObj> getPool() {
+        return mPool;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder buff = new StringBuilder();
+        buff.append("db: ");
+        buff.append(mParentDbObj.getPath());
+        buff.append(", totalsize = ");
+        buff.append(mPool.size());
+        buff.append(", #free = ");
+        buff.append(getFreePoolSize());
+        buff.append(", maxpoolsize = ");
+        buff.append(mMaxPoolSize);
+        for (PoolObj p : mPool) {
+            buff.append("\n");
+            buff.append(p.toString());
+        }
+        return buff.toString();
+    }
+
+    private void doAsserts() {
+        for (int i = 0; i < mPool.size(); i++) {
+            mPool.get(i).verify();
+            assert mPool.get(i).mDb.mConnectionNum == (i + 1);
+        }
+    }
+
+    /** only used for testing purposes. */
+    /* package */ synchronized void setMaxPoolSize(int size) {
+        mMaxPoolSize = size;
+    }
+
+    /** only used for testing purposes. */
+    /* package */ synchronized int getMaxPoolSize() {
+        return mMaxPoolSize;
+    }
+
+    /** only used for testing purposes. */
+    /* package */ boolean isDatabaseObjFree(SQLiteDatabase db) {
+        return mPool.get(db.mConnectionNum - 1).isFree();
+    }
+
+    /** only used for testing purposes. */
+    /* package */ int getSize() {
+        return mPool.size();
+    }
+
+    /**
+     * represents objects in the connection pool.
+     * package-level access for testing purposes only.
+     */
+    /* package */ static class PoolObj {
+
+        private final SQLiteDatabase mDb;
+        private boolean mFreeBusyFlag = FREE;
+        private static final boolean FREE = true;
+        private static final boolean BUSY = false;
+
+        /** the number of threads holding this connection */
+        // @GuardedBy("this")
+        private int mNumHolders = 0;
+
+        /** contains the threadIds of the threads holding this connection.
+         * used for debugging purposes only.
+         */
+        // @GuardedBy("this")
+        private HashSet<Long> mHolderIds = new HashSet<Long>();
+
+        public PoolObj(SQLiteDatabase db) {
+            mDb = db;
+        }
+
+        private synchronized void acquire() {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                assert isFree();
+                long id = Thread.currentThread().getId();
+                assert !mHolderIds.contains(id);
+                mHolderIds.add(id);
+            }
+
+            mNumHolders++;
+            mFreeBusyFlag = BUSY;
+        }
+
+        private synchronized void release() {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                long id = Thread.currentThread().getId();
+                assert mHolderIds.size() == mNumHolders;
+                assert mHolderIds.contains(id);
+                mHolderIds.remove(id);
+            }
+
+            mNumHolders--;
+            if (mNumHolders == 0) {
+                mFreeBusyFlag = FREE;
+            }
+        }
+
+        private synchronized boolean isFree() {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                verify();
+            }
+            return (mFreeBusyFlag == FREE);
+        }
+
+        private synchronized void verify() {
+            if (mFreeBusyFlag == FREE) {
+                assert mNumHolders == 0;
+            } else {
+                assert mNumHolders > 0;
+            }
+        }
+
+        /**
+         * only for testing purposes
+         */
+        /* package */ synchronized int getNumHolders() {
+            return mNumHolders;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder buff = new StringBuilder();
+            buff.append(", conn # ");
+            buff.append(mDb.mConnectionNum);
+            buff.append(", mCountHolders = ");
+            synchronized(this) {
+                buff.append(mNumHolders);
+                buff.append(", freeBusyFlag = ");
+                buff.append(mFreeBusyFlag);
+                for (Long l : mHolderIds) {
+                    buff.append(", id = " + l);
+                }
+            }
+            return buff.toString();
+        }
+    }
+}
diff --git a/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java b/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java
index 8ac4c0f..f28c70f 100644
--- a/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java
+++ b/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java
@@ -21,13 +21,11 @@
  * that is not explicitly closed
  * @hide
  */
-public class DatabaseObjectNotClosedException extends RuntimeException
-{
+public class DatabaseObjectNotClosedException extends RuntimeException {
     private static final String s = "Application did not close the cursor or database object " +
             "that was opened here";
 
-    public DatabaseObjectNotClosedException()
-    {
+    public DatabaseObjectNotClosedException() {
         super(s);
     }
 }
diff --git a/core/java/android/database/sqlite/SQLiteAccessPermException.java b/core/java/android/database/sqlite/SQLiteAccessPermException.java
new file mode 100644
index 0000000..238da4b
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteAccessPermException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+/**
+ * This exception class is used when sqlite can't access the database file
+ * due to lack of permissions on the file.
+ */
+public class SQLiteAccessPermException extends SQLiteException {
+    public SQLiteAccessPermException() {}
+
+    public SQLiteAccessPermException(String error) {
+        super(error);
+    }
+}
diff --git a/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java b/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java
new file mode 100644
index 0000000..41f2f9c
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+/**
+ * Thrown if the the bind or column parameter index is out of range
+ */
+public class SQLiteBindOrColumnIndexOutOfRangeException extends SQLiteException {
+    public SQLiteBindOrColumnIndexOutOfRangeException() {}
+
+    public SQLiteBindOrColumnIndexOutOfRangeException(String error) {
+        super(error);
+    }
+}
diff --git a/core/java/android/database/sqlite/SQLiteBlobTooBigException.java b/core/java/android/database/sqlite/SQLiteBlobTooBigException.java
new file mode 100644
index 0000000..a82676b
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteBlobTooBigException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+public class SQLiteBlobTooBigException extends SQLiteException {
+    public SQLiteBlobTooBigException() {}
+
+    public SQLiteBlobTooBigException(String error) {
+        super(error);
+    }
+}
diff --git a/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java b/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java
new file mode 100644
index 0000000..6f01796
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+public class SQLiteCantOpenDatabaseException extends SQLiteException {
+    public SQLiteCantOpenDatabaseException() {}
+
+    public SQLiteCantOpenDatabaseException(String error) {
+        super(error);
+    }
+}
diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java
index 1830f6c..93f9a71e 100644
--- a/core/java/android/database/sqlite/SQLiteClosable.java
+++ b/core/java/android/database/sqlite/SQLiteClosable.java
@@ -23,13 +23,12 @@
  */
 public abstract class SQLiteClosable {
     private int mReferenceCount = 1;
-    private Object mLock = new Object();
 
     protected abstract void onAllReferencesReleased();
     protected void onAllReferencesReleasedFromContainer() {}
 
     public void acquireReference() {
-        synchronized(mLock) {
+        synchronized(this) {
             if (mReferenceCount <= 0) {
                 throw new IllegalStateException(
                         "attempt to re-open an already-closed object: " + getObjInfo());
@@ -39,7 +38,7 @@
     }
 
     public void releaseReference() {
-        synchronized(mLock) {
+        synchronized(this) {
             mReferenceCount--;
             if (mReferenceCount == 0) {
                 onAllReferencesReleased();
@@ -48,7 +47,7 @@
     }
 
     public void releaseReferenceFromContainer() {
-        synchronized(mLock) {
+        synchronized(this) {
             mReferenceCount--;
             if (mReferenceCount == 0) {
                 onAllReferencesReleasedFromContainer();
@@ -63,8 +62,7 @@
         if (this instanceof SQLiteDatabase) {
             buff.append("database = ");
             buff.append(((SQLiteDatabase)this).getPath());
-        } else if (this instanceof SQLiteProgram || this instanceof SQLiteStatement ||
-                this instanceof SQLiteQuery) {
+        } else if (this instanceof SQLiteProgram) {
             buff.append("mSql = ");
             buff.append(((SQLiteProgram)this).mSql);
         } else if (this instanceof CursorWindow) {
diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java
index 003d0f2..feea47e 100644
--- a/core/java/android/database/sqlite/SQLiteCompiledSql.java
+++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java
@@ -32,12 +32,12 @@
     private static final String TAG = "SQLiteCompiledSql";
 
     /** The database this program is compiled against. */
-    /* package */ SQLiteDatabase mDatabase;
+    /* package */ final SQLiteDatabase mDatabase;
 
     /**
      * Native linkage, do not modify. This comes from the database.
      */
-    /* package */ int nHandle = 0;
+    /* package */ final int nHandle;
 
     /**
      * Native linkage, do not modify. When non-0 this holds a reference to a valid
@@ -55,61 +55,21 @@
     private boolean mInUse = false;
 
     /* package */ SQLiteCompiledSql(SQLiteDatabase db, String sql) {
-        if (!db.isOpen()) {
-            throw new IllegalStateException("database " + db.getPath() + " already closed");
-        }
+        db.verifyDbIsOpen();
+        db.verifyLockOwner();
         mDatabase = db;
         mSqlStmt = sql;
         mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
-        this.nHandle = db.mNativeHandle;
-        compile(sql, true);
-    }
-
-    /**
-     * Compiles the given SQL into a SQLite byte code program using sqlite3_prepare_v2(). If
-     * this method has been called previously without a call to close and forCompilation is set
-     * to false the previous compilation will be used. Setting forceCompilation to true will
-     * always re-compile the program and should be done if you pass differing SQL strings to this
-     * method.
-     *
-     * <P>Note: this method acquires the database lock.</P>
-     *
-     * @param sql the SQL string to compile
-     * @param forceCompilation forces the SQL to be recompiled in the event that there is an
-     *  existing compiled SQL program already around
-     */
-    private void compile(String sql, boolean forceCompilation) {
-        if (!mDatabase.isOpen()) {
-            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
-        }
-        // Only compile if we don't have a valid statement already or the caller has
-        // explicitly requested a recompile.
-        if (forceCompilation) {
-            mDatabase.lock();
-            try {
-                // Note that the native_compile() takes care of destroying any previously
-                // existing programs before it compiles.
-                native_compile(sql);
-            } finally {
-                mDatabase.unlock();
-            }
-        }
+        nHandle = db.mNativeHandle;
+        native_compile(sql);
     }
 
     /* package */ void releaseSqlStatement() {
         // Note that native_finalize() checks to make sure that nStatement is
         // non-null before destroying it.
         if (nStatement != 0) {
-            if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
-                Log.v(TAG, "closed and deallocated DbObj (id#" + nStatement +")");
-            }
-            try {
-                mDatabase.lock();
-                native_finalize();
-                nStatement = 0;
-            } finally {
-                mDatabase.unlock();
-            }
+            mDatabase.finalizeStatementLater(nStatement);
+            nStatement = 0;
         }
     }
 
@@ -118,23 +78,24 @@
      */
     /* package */ synchronized boolean acquire() {
         if (mInUse) {
-            // someone already has acquired it.
+            // it is already in use.
             return false;
         }
         mInUse = true;
-        if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
-            Log.v(TAG, "Acquired DbObj (id#" + nStatement + ") from DB cache");
-        }
         return true;
     }
 
     /* package */ synchronized void release() {
-        if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
-            Log.v(TAG, "Released DbObj (id#" + nStatement + ") back to DB cache");
-        }
         mInUse = false;
     }
 
+    /* package */ synchronized void releaseIfNotInUse() {
+        // if it is not in use, release its memory from the database
+        if (!mInUse) {
+            releaseSqlStatement();
+        }
+    }
+
     /**
      * Make sure that the native resource is cleaned up.
      */
@@ -143,15 +104,15 @@
         try {
             if (nStatement == 0) return;
             // finalizer should NEVER get called
-            if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
-                Log.v(TAG, "** warning ** Finalized DbObj (id#" + nStatement + ")");
-            }
-            if (StrictMode.vmSqliteObjectLeaksEnabled()) {
+            // but if the database itself is not closed and is GC'ed, then
+            // all sub-objects attached to the database could end up getting GC'ed too.
+            // in that case, don't print any warning.
+            if (mInUse && StrictMode.vmSqliteObjectLeaksEnabled()) {
                 int len = mSqlStmt.length();
                 StrictMode.onSqliteObjectLeaked(
                     "Releasing statement in a finalizer. Please ensure " +
                     "that you explicitly call close() on your cursor: " +
-                    mSqlStmt.substring(0, (len > 100) ? 100 : len),
+                    mSqlStmt.substring(0, (len > 1000) ? 1000 : len),
                     mStackTrace);
             }
             releaseSqlStatement();
@@ -160,6 +121,24 @@
         }
     }
 
+    @Override public String toString() {
+        synchronized(this) {
+            StringBuilder buff = new StringBuilder();
+            buff.append(" nStatement=");
+            buff.append(nStatement);
+            buff.append(", mInUse=");
+            buff.append(mInUse);
+            buff.append(", db=");
+            buff.append(mDatabase.getPath());
+            buff.append(", db_connectionNum=");
+            buff.append(mDatabase.mConnectionNum);
+            buff.append(", sql=");
+            int len = mSqlStmt.length();
+            buff.append(mSqlStmt.substring(0, (len > 100) ? 100 : len));
+            return buff.toString();
+        }
+    }
+
     /**
      * Compiles SQL into a SQLite program.
      *
@@ -167,5 +146,4 @@
      * @param sql The SQL to compile.
      */
     private final native void native_compile(String sql);
-    private final native void native_finalize();
 }
diff --git a/core/java/android/database/sqlite/SQLiteContentHelper.java b/core/java/android/database/sqlite/SQLiteContentHelper.java
deleted file mode 100644
index 2800d86..0000000
--- a/core/java/android/database/sqlite/SQLiteContentHelper.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.database.sqlite;
-
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.os.MemoryFile;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-/**
- * Some helper functions for using SQLite database to implement content providers.
- *
- * @hide
- */
-public class SQLiteContentHelper {
-
-    /**
-     * Runs an SQLite query and returns an AssetFileDescriptor for the
-     * blob in column 0 of the first row. If the first column does
-     * not contain a blob, an unspecified exception is thrown.
-     *
-     * @param db Handle to a readable database.
-     * @param sql SQL query, possibly with query arguments.
-     * @param selectionArgs Query argument values, or {@code null} for no argument.
-     * @return If no exception is thrown, a non-null AssetFileDescriptor is returned.
-     * @throws FileNotFoundException If the query returns no results or the
-     *         value of column 0 is NULL, or if there is an error creating the
-     *         asset file descriptor.
-     */
-    public static AssetFileDescriptor getBlobColumnAsAssetFile(SQLiteDatabase db, String sql,
-            String[] selectionArgs) throws FileNotFoundException {
-        try {
-            MemoryFile file = simpleQueryForBlobMemoryFile(db, sql, selectionArgs);
-            if (file == null) {
-                throw new FileNotFoundException("No results.");
-            }
-            return AssetFileDescriptor.fromMemoryFile(file);
-        } catch (IOException ex) {
-            throw new FileNotFoundException(ex.toString());
-        }
-    }
-
-    /**
-     * Runs an SQLite query and returns a MemoryFile for the
-     * blob in column 0 of the first row. If the first column does
-     * not contain a blob, an unspecified exception is thrown.
-     *
-     * @return A memory file, or {@code null} if the query returns no results
-     *         or the value column 0 is NULL.
-     * @throws IOException If there is an error creating the memory file.
-     */
-    // TODO: make this native and use the SQLite blob API to reduce copying
-    private static MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql,
-            String[] selectionArgs) throws IOException {
-        Cursor cursor = db.rawQuery(sql, selectionArgs);
-        if (cursor == null) {
-            return null;
-        }
-        try {
-            if (!cursor.moveToFirst()) {
-                return null;
-            }
-            byte[] bytes = cursor.getBlob(0);
-            if (bytes == null) {
-                return null;
-            }
-            MemoryFile file = new MemoryFile(null, bytes.length);
-            file.writeBytes(bytes, 0, 0, bytes.length);
-            file.deactivate();
-            return file;
-        } finally {
-            cursor.close();
-        }
-    }
-
-}
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index d8dcaf7..fc30da2 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -19,18 +19,14 @@
 import android.database.AbstractWindowedCursor;
 import android.database.CursorWindow;
 import android.database.DataSetObserver;
-import android.database.SQLException;
-
 import android.os.Handler;
 import android.os.Message;
 import android.os.Process;
 import android.os.StrictMode;
-import android.text.TextUtils;
 import android.util.Config;
 import android.util.Log;
 
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.Map;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -42,32 +38,29 @@
  * threads should perform its own synchronization when using the SQLiteCursor.
  */
 public class SQLiteCursor extends AbstractWindowedCursor {
-    static final String TAG = "Cursor";
+    static final String TAG = "SQLiteCursor";
     static final int NO_COUNT = -1;
 
     /** The name of the table to edit */
-    private String mEditTable;
+    private final String mEditTable;
 
     /** The names of the columns in the rows */
-    private String[] mColumns;
+    private final String[] mColumns;
 
     /** The query object for the cursor */
     private SQLiteQuery mQuery;
 
-    /** The database the cursor was created from */
-    private SQLiteDatabase mDatabase;
-
     /** The compiled query this cursor came from */
-    private SQLiteCursorDriver mDriver;
+    private final SQLiteCursorDriver mDriver;
 
     /** The number of rows in the cursor */
-    private int mCount = NO_COUNT;
+    private volatile int mCount = NO_COUNT;
 
     /** A mapping of column names to column indices, to speed up lookups */
     private Map<String, Integer> mColumnNameMap;
 
     /** Used to find out where a cursor was allocated in case it never got released. */
-    private Throwable mStackTrace;
+    private final Throwable mStackTrace;
     
     /** 
      *  mMaxRead is the max items that each cursor window reads 
@@ -78,7 +71,7 @@
     private int mCursorState = 0;
     private ReentrantLock mLock = null;
     private boolean mPendingData = false;
-    
+
     /**
      *  support for a cursor variant that doesn't always read all results
      *  initialRead is the initial number of items that cursor window reads 
@@ -137,14 +130,22 @@
                     break;
                 }
                 try {
-                    int count = mQuery.fillWindow(cw, mMaxRead, mCount);
-                    // return -1 means not finished
+                    int count = getQuery().fillWindow(cw, mMaxRead, mCount);
+                    // return -1 means there is still more data to be retrieved from the resultset
                     if (count != 0) {
                         if (count == NO_COUNT){
                             mCount += mMaxRead;
+                            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                                Log.d(TAG, "received -1 from native_fill_window. read " +
+                                        mCount + " rows so far");
+                            }
                             sendMessage();
                         } else {                                
-                            mCount = count;
+                            mCount += count;
+                            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                                Log.d(TAG, "received all data from native_fill_window. read " +
+                                        mCount + " rows.");
+                            }
                             sendMessage();
                             break;
                         }
@@ -202,24 +203,46 @@
      * has package scope.
      *
      * @param db a reference to a Database object that is already constructed
-     *     and opened
+     *     and opened. This param is not used any longer
      * @param editTable the name of the table used for this query
      * @param query the rest of the query terms
      *     cursor is finalized
+     * @deprecated use {@link #SQLiteCursor(SQLiteCursorDriver, String, SQLiteQuery)} instead
      */
+    @Deprecated
     public SQLiteCursor(SQLiteDatabase db, SQLiteCursorDriver driver,
             String editTable, SQLiteQuery query) {
+        this(driver, editTable, query);
+    }
+
+    /**
+     * Execute a query and provide access to its result set through a Cursor
+     * interface. For a query such as: {@code SELECT name, birth, phone FROM
+     * myTable WHERE ... LIMIT 1,20 ORDER BY...} the column names (name, birth,
+     * phone) would be in the projection argument and everything from
+     * {@code FROM} onward would be in the params argument. This constructor
+     * has package scope.
+     *
+     * @param editTable the name of the table used for this query
+     * @param query the {@link SQLiteQuery} object associated with this cursor object.
+     */
+    public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {
         // The AbstractCursor constructor needs to do some setup.
         super();
+        if (query == null) {
+            throw new IllegalArgumentException("query object cannot be null");
+        }
+        if (query.mDatabase == null) {
+            throw new IllegalArgumentException("query.mDatabase cannot be null");
+        }
         mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
-        mDatabase = db;
         mDriver = driver;
         mEditTable = editTable;
         mColumnNameMap = null;
         mQuery = query;
 
         try {
-            db.lock();
+            query.mDatabase.lock();
 
             // Setup the list of columns
             int columnCount = mQuery.columnCountLocked();
@@ -240,7 +263,7 @@
                 }
             }
         } finally {
-            db.unlock();
+            query.mDatabase.unlock();
         }
     }
 
@@ -248,7 +271,9 @@
      * @return the SQLiteDatabase that this cursor is associated with.
      */
     public SQLiteDatabase getDatabase() {
-        return mDatabase;
+        synchronized (this) {
+            return mQuery.mDatabase;
+        }
     }
 
     @Override
@@ -284,13 +309,27 @@
                 }
         }
         mWindow.setStartPosition(startPos);
-        mCount = mQuery.fillWindow(mWindow, mInitialRead, 0);
-        // return -1 means not finished
-        if (mCount == NO_COUNT){
+        int count = getQuery().fillWindow(mWindow, mInitialRead, 0);
+        // return -1 means there is still more data to be retrieved from the resultset
+        if (count == NO_COUNT){
             mCount = startPos + mInitialRead;
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "received -1 from native_fill_window. read " + mCount + " rows so far");
+            }
             Thread t = new Thread(new QueryThread(mCursorState), "query thread");
             t.start();
-        } 
+        } else if (startPos == 0) { // native_fill_window returns count(*) only for startPos = 0
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "received count(*) from native_fill_window: " + count);
+            }
+            mCount = count;
+        } else if (mCount <= 0) {
+            throw new IllegalStateException("count should never be non-zero negative number");
+        }
+    }
+
+    private synchronized SQLiteQuery getQuery() {
+        return mQuery;
     }
 
     @Override
@@ -322,166 +361,11 @@
         }
     }
 
-    /**
-     * @hide
-     * @deprecated
-     */
-    @Override
-    public boolean deleteRow() {
-        checkPosition();
-
-        // Only allow deletes if there is an ID column, and the ID has been read from it
-        if (mRowIdColumnIndex == -1 || mCurrentRowID == null) {
-            Log.e(TAG,
-                    "Could not delete row because either the row ID column is not available or it" +
-                    "has not been read.");
-            return false;
-        }
-
-        boolean success;
-
-        /*
-         * Ensure we don't change the state of the database when another
-         * thread is holding the database lock. requery() and moveTo() are also
-         * synchronized here to make sure they get the state of the database
-         * immediately following the DELETE.
-         */
-        mDatabase.lock();
-        try {
-            try {
-                mDatabase.delete(mEditTable, mColumns[mRowIdColumnIndex] + "=?",
-                        new String[] {mCurrentRowID.toString()});
-                success = true;
-            } catch (SQLException e) {
-                success = false;
-            }
-
-            int pos = mPos;
-            requery();
-
-            /*
-             * Ensure proper cursor state. Note that mCurrentRowID changes
-             * in this call.
-             */
-            moveToPosition(pos);
-        } finally {
-            mDatabase.unlock();
-        }
-
-        if (success) {
-            onChange(true);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
     @Override
     public String[] getColumnNames() {
         return mColumns;
     }
 
-    /**
-     * @hide
-     * @deprecated
-     */
-    @Override
-    public boolean supportsUpdates() {
-        return super.supportsUpdates() && !TextUtils.isEmpty(mEditTable);
-    }
-
-    /**
-     * @hide
-     * @deprecated
-     */
-    @Override
-    public boolean commitUpdates(Map<? extends Long,
-            ? extends Map<String, Object>> additionalValues) {
-        if (!supportsUpdates()) {
-            Log.e(TAG, "commitUpdates not supported on this cursor, did you "
-                    + "include the _id column?");
-            return false;
-        }
-
-        /*
-         * Prevent other threads from changing the updated rows while they're
-         * being processed here.
-         */
-        synchronized (mUpdatedRows) {
-            if (additionalValues != null) {
-                mUpdatedRows.putAll(additionalValues);
-            }
-
-            if (mUpdatedRows.size() == 0) {
-                return true;
-            }
-
-            /*
-             * Prevent other threads from changing the database state while
-             * we process the updated rows, and prevents us from changing the
-             * database behind the back of another thread.
-             */
-            mDatabase.beginTransaction();
-            try {
-                StringBuilder sql = new StringBuilder(128);
-
-                // For each row that has been updated
-                for (Map.Entry<Long, Map<String, Object>> rowEntry :
-                        mUpdatedRows.entrySet()) {
-                    Map<String, Object> values = rowEntry.getValue();
-                    Long rowIdObj = rowEntry.getKey();
-
-                    if (rowIdObj == null || values == null) {
-                        throw new IllegalStateException("null rowId or values found! rowId = "
-                                + rowIdObj + ", values = " + values);
-                    }
-
-                    if (values.size() == 0) {
-                        continue;
-                    }
-
-                    long rowId = rowIdObj.longValue();
-
-                    Iterator<Map.Entry<String, Object>> valuesIter =
-                            values.entrySet().iterator();
-
-                    sql.setLength(0);
-                    sql.append("UPDATE " + mEditTable + " SET ");
-
-                    // For each column value that has been updated
-                    Object[] bindings = new Object[values.size()];
-                    int i = 0;
-                    while (valuesIter.hasNext()) {
-                        Map.Entry<String, Object> entry = valuesIter.next();
-                        sql.append(entry.getKey());
-                        sql.append("=?");
-                        bindings[i] = entry.getValue();
-                        if (valuesIter.hasNext()) {
-                            sql.append(", ");
-                        }
-                        i++;
-                    }
-
-                    sql.append(" WHERE " + mColumns[mRowIdColumnIndex]
-                            + '=' + rowId);
-                    sql.append(';');
-                    mDatabase.execSQL(sql.toString(), bindings);
-                    mDatabase.rowUpdated(mEditTable, rowId);
-                }
-                mDatabase.setTransactionSuccessful();
-            } finally {
-                mDatabase.endTransaction();
-            }
-
-            mUpdatedRows.clear();
-        }
-
-        // Let any change observers know about the update
-        onChange(true);
-
-        return true;
-    }
-
     private void deactivateCommon() {
         if (Config.LOGV) Log.v(TAG, "<<< Releasing cursor " + this);
         mCursorState = 0;
@@ -502,9 +386,11 @@
     @Override
     public void close() {
         super.close();
-        deactivateCommon();
-        mQuery.close();
-        mDriver.cursorClosed();
+        synchronized (this) {
+            deactivateCommon();
+            mQuery.close();
+            mDriver.cursorClosed();
+        }
     }
 
     @Override
@@ -516,16 +402,25 @@
         if (Config.LOGV) {
             timeStart = System.currentTimeMillis();
         }
-        /*
-         * Synchronize on the database lock to ensure that mCount matches the
-         * results of mQuery.requery().
-         */
-        mDatabase.lock();
-        try {
+
+        synchronized (this) {
             if (mWindow != null) {
                 mWindow.clear();
             }
             mPos = -1;
+            SQLiteDatabase db = mQuery.mDatabase.getDatabaseHandle(mQuery.mSql);
+            if (!db.equals(mQuery.mDatabase)) {
+                // since we need to use a different database connection handle,
+                // re-compile the query
+                db.lock();
+                try {
+                    // close the old mQuery object and open a new one
+                    mQuery.close();
+                    mQuery = new SQLiteQuery(db, mQuery);
+                } finally {
+                    db.unlock();
+                }
+            }
             // This one will recreate the temp table, and get its count
             mDriver.cursorRequeried(this);
             mCount = NO_COUNT;
@@ -536,8 +431,6 @@
             } finally {
                 queryThreadUnlock();
             }
-        } finally {
-            mDatabase.unlock();
         }
 
         if (Config.LOGV) {
@@ -587,15 +480,15 @@
                     int len = mQuery.mSql.length();
                     StrictMode.onSqliteObjectLeaked(
                         "Finalizing a Cursor that has not been deactivated or closed. " +
-                        "database = " + mDatabase.getPath() + ", table = " + mEditTable +
-                        ", query = " + mQuery.mSql.substring(0, (len > 100) ? 100 : len),
+                        "database = " + mQuery.mDatabase.getPath() + ", table = " + mEditTable +
+                        ", query = " + mQuery.mSql.substring(0, (len > 1000) ? 1000 : len),
                         mStackTrace);
                 }
                 close();
                 SQLiteDebug.notifyActiveCursorFinalized();
             } else {
                 if (Config.LOGV) {
-                    Log.v(TAG, "Finalizing cursor on database = " + mDatabase.getPath() +
+                    Log.v(TAG, "Finalizing cursor on database = " + mQuery.mDatabase.getPath() +
                             ", table = " + mEditTable + ", query = " + mQuery.mSql);
                 }
             }
@@ -603,4 +496,11 @@
             super.finalize();
         }
     }
+
+    /**
+     * this is only for testing purposes.
+     */
+    /* package */ int getMCount() {
+        return mCount;
+    }
 }
diff --git a/core/java/android/database/sqlite/SQLiteCursorDriver.java b/core/java/android/database/sqlite/SQLiteCursorDriver.java
index eda1b78..b3963f9 100644
--- a/core/java/android/database/sqlite/SQLiteCursorDriver.java
+++ b/core/java/android/database/sqlite/SQLiteCursorDriver.java
@@ -40,8 +40,6 @@
 
     /**
      * Called by a SQLiteCursor when it is requeryed.
-     * 
-     * @return The new count value.
      */
     void cursorRequeried(Cursor cursor);
 
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index cdc9bbb..79425d6 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -16,16 +16,16 @@
 
 package android.database.sqlite;
 
-import com.google.android.collect.Maps;
-
-import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.content.ContentValues;
 import android.database.Cursor;
+import android.database.DatabaseErrorHandler;
 import android.database.DatabaseUtils;
+import android.database.DefaultDatabaseErrorHandler;
 import android.database.SQLException;
 import android.database.sqlite.SQLiteDebug.DbStats;
 import android.os.Debug;
+import android.os.StatFs;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.text.TextUtils;
@@ -38,15 +38,13 @@
 
 import java.io.File;
 import java.lang.ref.WeakReference;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashMap;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Random;
-import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.regex.Pattern;
@@ -67,7 +65,7 @@
  * is the Unicode Collation Algorithm and not tailored to the current locale.
  */
 public class SQLiteDatabase extends SQLiteClosable {
-    private static final String TAG = "Database";
+    private static final String TAG = "SQLiteDatabase";
     private static final int EVENT_DB_OPERATION = 52000;
     private static final int EVENT_DB_CORRUPT = 75004;
 
@@ -192,6 +190,11 @@
      */
     private SQLiteTransactionListener mTransactionListener;
 
+    /**
+     * this member is set if {@link #execSQL(String)} is used to begin and end transactions.
+     */
+    private boolean mTransactionUsingExecSql;
+
     /** Synchronize on this when accessing the database */
     private final ReentrantLock mLock = new ReentrantLock(true);
 
@@ -233,94 +236,138 @@
     // lock acquistions of the database.
     /* package */ static final String GET_LOCK_LOG_PREFIX = "GETLOCK:";
 
-    /** Used by native code, do not rename */
-    /* package */ int mNativeHandle = 0;
+    /** Used by native code, do not rename. make it volatile, so it is thread-safe. */
+    /* package */ volatile int mNativeHandle = 0;
 
-    /** Used to make temp table names unique */
-    /* package */ int mTempTableSequence = 0;
+    /**
+     * The size, in bytes, of a block on "/data". This corresponds to the Unix
+     * statfs.f_bsize field. note that this field is lazily initialized.
+     */
+    private static int sBlockSize = 0;
 
     /** The path for the database file */
-    private String mPath;
+    private final String mPath;
 
     /** The anonymized path for the database file for logging purposes */
     private String mPathForLogs = null;  // lazily populated
 
     /** The flags passed to open/create */
-    private int mFlags;
+    private final int mFlags;
 
     /** The optional factory to use when creating new Cursors */
-    private CursorFactory mFactory;
+    private final CursorFactory mFactory;
 
-    private WeakHashMap<SQLiteClosable, Object> mPrograms;
+    private final WeakHashMap<SQLiteClosable, Object> mPrograms;
 
     /**
-     * for each instance of this class, a cache is maintained to store
+     * for each instance of this class, a LRU cache is maintained to store
      * the compiled query statement ids returned by sqlite database.
-     *     key = sql statement with "?" for bind args
+     *     key = SQL statement with "?" for bind args
      *     value = {@link SQLiteCompiledSql}
      * If an application opens the database and keeps it open during its entire life, then
-     * there will not be an overhead of compilation of sql statements by sqlite.
+     * there will not be an overhead of compilation of SQL statements by sqlite.
      *
      * why is this cache NOT static? because sqlite attaches compiledsql statements to the
      * struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is
      * invoked.
      *
      * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
-     * (@link setMaxCacheSize(int)}). its default is 0 - i.e., no caching by default because
-     * most of the apps don't use "?" syntax in their sql, caching is not useful for them.
+     * (@link #setMaxSqlCacheSize(int)}).
      */
-    /* package */ Map<String, SQLiteCompiledSql> mCompiledQueries = Maps.newHashMap();
+    // default statement-cache size per database connection ( = instance of this class)
+    private int mMaxSqlCacheSize = 25;
+    // guarded by itself
+    /* package */ final Map<String, SQLiteCompiledSql> mCompiledQueries =
+        new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) {
+            @Override
+            public boolean removeEldestEntry(Map.Entry<String, SQLiteCompiledSql> eldest) {
+                // eldest = least-recently used entry
+                // if it needs to be removed to accommodate a new entry,
+                //     close {@link SQLiteCompiledSql} represented by this entry, if not in use
+                //     and then let it be removed from the Map.
+                // when this is called, the caller must be trying to add a just-compiled stmt
+                // to cache; i.e., caller should already have acquired database lock AND
+                // the lock on mCompiledQueries. do as assert of these two 2 facts.
+                verifyLockOwner();
+                if (this.size() <= mMaxSqlCacheSize) {
+                    // cache is not full. nothing needs to be removed
+                    return false;
+                }
+                // cache is full. eldest will be removed.
+                eldest.getValue().releaseIfNotInUse();
+                // return true, so that this entry is removed automatically by the caller.
+                return true;
+            }
+        };
     /**
-     * @hide
+     * absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}
+     * size of each prepared-statement is between 1K - 6K, depending on the complexity of the
+     * SQL statement & schema.
      */
-    public static final int MAX_SQL_CACHE_SIZE = 250;
-    private int mMaxSqlCacheSize = MAX_SQL_CACHE_SIZE; // max cache size per Database instance
-    private int mCacheFullWarnings;
-    private static final int MAX_WARNINGS_ON_CACHESIZE_CONDITION = 1;
+    public static final int MAX_SQL_CACHE_SIZE = 100;
+    private boolean mCacheFullWarning;
 
-    /** maintain stats about number of cache hits and misses */
+    /** Number of cache hits on this database connection. guarded by {@link #mCompiledQueries}. */
     private int mNumCacheHits;
+    /** Number of cache misses on this database connection. guarded by {@link #mCompiledQueries}. */
     private int mNumCacheMisses;
 
-    /** the following 2 members maintain the time when a database is opened and closed */
-    private String mTimeOpened = null;
-    private String mTimeClosed = null;
-
     /** Used to find out where this object was created in case it never got closed. */
-    private Throwable mStackTrace = null;
+    private final Throwable mStackTrace;
 
     // System property that enables logging of slow queries. Specify the threshold in ms.
     private static final String LOG_SLOW_QUERIES_PROPERTY = "db.log.slow_query_threshold";
     private final int mSlowQueryThreshold;
 
-    /**
-     * @param closable
+    /** stores the list of statement ids that need to be finalized by sqlite */
+    private final ArrayList<Integer> mClosedStatementIds = new ArrayList<Integer>();
+
+    /** {@link DatabaseErrorHandler} to be used when SQLite returns any of the following errors
+     *    Corruption
+     * */
+    private final DatabaseErrorHandler mErrorHandler;
+
+    /** The Database connection pool {@link DatabaseConnectionPool}.
+     * Visibility is package-private for testing purposes. otherwise, private visibility is enough.
      */
-    void addSQLiteClosable(SQLiteClosable closable) {
-        lock();
-        try {
-            mPrograms.put(closable, null);
-        } finally {
-            unlock();
-        }
+    /* package */ volatile DatabaseConnectionPool mConnectionPool = null;
+
+    /** Each database connection handle in the pool is assigned a number 1..N, where N is the
+     * size of the connection pool.
+     * The main connection handle to which the pool is attached is assigned a value of 0.
+     */
+    /* package */ final short mConnectionNum;
+
+    /** on pooled database connections, this member points to the parent ( = main)
+     * database connection handle.
+     * package visibility only for testing purposes
+     */
+    /* package */ SQLiteDatabase mParentConnObj = null;
+
+    private static final String MEMORY_DB_PATH = ":memory:";
+
+    /** set to true if the database has attached databases */
+    private volatile boolean mHasAttachedDbs = false;
+
+    /** stores reference to all databases opened in the current process. */
+    private static ArrayList<WeakReference<SQLiteDatabase>> mActiveDatabases =
+            new ArrayList<WeakReference<SQLiteDatabase>>();
+
+    synchronized void addSQLiteClosable(SQLiteClosable closable) {
+        // mPrograms is per instance of SQLiteDatabase and it doesn't actually touch the database
+        // itself. so, there is no need to lock().
+        mPrograms.put(closable, null);
     }
 
-    void removeSQLiteClosable(SQLiteClosable closable) {
-        lock();
-        try {
-            mPrograms.remove(closable);
-        } finally {
-            unlock();
-        }
+    synchronized void removeSQLiteClosable(SQLiteClosable closable) {
+        mPrograms.remove(closable);
     }
 
     @Override
     protected void onAllReferencesReleased() {
         if (isOpen()) {
-            if (SQLiteDebug.DEBUG_SQL_CACHE) {
-                mTimeClosed = getTime();
-            }
-            dbclose();
+            // close the database which will close all pending statements to be finalized also
+            close();
         }
     }
 
@@ -350,19 +397,8 @@
     private boolean mLockingEnabled = true;
 
     /* package */ void onCorruption() {
-        Log.e(TAG, "Removing corrupt database: " + mPath);
         EventLog.writeEvent(EVENT_DB_CORRUPT, mPath);
-        try {
-            // Close the database (if we can), which will cause subsequent operations to fail.
-            close();
-        } finally {
-            // Delete the corrupt file.  Don't re-create it now -- that would just confuse people
-            // -- but the next time someone tries to open it, they can set it up from scratch.
-            if (!mPath.equalsIgnoreCase(":memory")) {
-                // delete is only for non-memory database files
-                new File(mPath).delete();
-            }
-        }
+        mErrorHandler.onCorruption(this);
     }
 
     /**
@@ -374,7 +410,16 @@
      * @see #unlock()
      */
     /* package */ void lock() {
-        if (!mLockingEnabled) return;
+        lock(false);
+    }
+    private void lock(boolean forced) {
+        // make sure this method is NOT being called from a 'synchronized' method
+        if (Thread.holdsLock(this)) {
+            // STOPSHIP change the following line to Log.w()
+            throw new IllegalStateException("don't lock() while in a synchronized method");
+        }
+        verifyDbIsOpen();
+        if (!forced && !mLockingEnabled) return;
         mLock.lock();
         if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
             if (mLock.getHoldCount() == 1) {
@@ -384,7 +429,6 @@
             }
         }
     }
-
     /**
      * Locks the database for exclusive access. The database lock must be held when
      * touch the native sqlite3* object since it is single threaded and uses
@@ -394,14 +438,7 @@
      * @see #unlockForced()
      */
     private void lockForced() {
-        mLock.lock();
-        if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
-            if (mLock.getHoldCount() == 1) {
-                // Use elapsed real-time since the CPU may sleep when waiting for IO
-                mLockAcquiredWallTime = SystemClock.elapsedRealtime();
-                mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
-            }
-        }
+        lock(true);
     }
 
     /**
@@ -460,11 +497,14 @@
     }
 
     /**
-     * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
+     * Begins a transaction in EXCLUSIVE mode.
+     * <p>
+     * Transactions can be nested.
+     * When the outer transaction is ended all of
      * the work done in that transaction and all of the nested transactions will be committed or
      * rolled back. The changes will be rolled back if any transaction is ended without being
      * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
-     *
+     * </p>
      * <p>Here is the standard idiom for transactions:
      *
      * <pre>
@@ -478,15 +518,42 @@
      * </pre>
      */
     public void beginTransaction() {
-        beginTransactionWithListener(null /* transactionStatusCallback */);
+        beginTransaction(null /* transactionStatusCallback */, true);
     }
 
     /**
-     * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
+     * Begins a transaction in IMMEDIATE mode. Transactions can be nested. When
+     * the outer transaction is ended all of the work done in that transaction
+     * and all of the nested transactions will be committed or rolled back. The
+     * changes will be rolled back if any transaction is ended without being
+     * marked as clean (by calling setTransactionSuccessful). Otherwise they
+     * will be committed.
+     * <p>
+     * Here is the standard idiom for transactions:
+     *
+     * <pre>
+     *   db.beginTransactionNonExclusive();
+     *   try {
+     *     ...
+     *     db.setTransactionSuccessful();
+     *   } finally {
+     *     db.endTransaction();
+     *   }
+     * </pre>
+     */
+    public void beginTransactionNonExclusive() {
+        beginTransaction(null /* transactionStatusCallback */, false);
+    }
+
+    /**
+     * Begins a transaction in EXCLUSIVE mode.
+     * <p>
+     * Transactions can be nested.
+     * When the outer transaction is ended all of
      * the work done in that transaction and all of the nested transactions will be committed or
      * rolled back. The changes will be rolled back if any transaction is ended without being
      * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
-     *
+     * </p>
      * <p>Here is the standard idiom for transactions:
      *
      * <pre>
@@ -498,15 +565,48 @@
      *     db.endTransaction();
      *   }
      * </pre>
+     *
      * @param transactionListener listener that should be notified when the transaction begins,
      * commits, or is rolled back, either explicitly or by a call to
      * {@link #yieldIfContendedSafely}.
      */
     public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
+        beginTransaction(transactionListener, true);
+    }
+
+    /**
+     * Begins a transaction in IMMEDIATE mode. Transactions can be nested. When
+     * the outer transaction is ended all of the work done in that transaction
+     * and all of the nested transactions will be committed or rolled back. The
+     * changes will be rolled back if any transaction is ended without being
+     * marked as clean (by calling setTransactionSuccessful). Otherwise they
+     * will be committed.
+     * <p>
+     * Here is the standard idiom for transactions:
+     *
+     * <pre>
+     *   db.beginTransactionWithListenerNonExclusive(listener);
+     *   try {
+     *     ...
+     *     db.setTransactionSuccessful();
+     *   } finally {
+     *     db.endTransaction();
+     *   }
+     * </pre>
+     *
+     * @param transactionListener listener that should be notified when the
+     *            transaction begins, commits, or is rolled back, either
+     *            explicitly or by a call to {@link #yieldIfContendedSafely}.
+     */
+    public void beginTransactionWithListenerNonExclusive(
+            SQLiteTransactionListener transactionListener) {
+        beginTransaction(transactionListener, false);
+    }
+
+    private void beginTransaction(SQLiteTransactionListener transactionListener,
+            boolean exclusive) {
+        verifyDbIsOpen();
         lockForced();
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
         boolean ok = false;
         try {
             // If this thread already had the lock then get out
@@ -524,7 +624,11 @@
 
             // This thread didn't already have the lock, so begin a database
             // transaction now.
-            execSQL("BEGIN EXCLUSIVE;");
+            if (exclusive && mConnectionPool == null) {
+                execSQL("BEGIN EXCLUSIVE;");
+            } else {
+                execSQL("BEGIN IMMEDIATE;");
+            }
             mTransactionListener = transactionListener;
             mTransactionIsSuccessful = true;
             mInnerTransactionIsSuccessful = false;
@@ -551,12 +655,7 @@
      * are committed and rolled back.
      */
     public void endTransaction() {
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
-        if (!mLock.isHeldByCurrentThread()) {
-            throw new IllegalStateException("no transaction pending");
-        }
+        verifyLockOwner();
         try {
             if (mInnerTransactionIsSuccessful) {
                 mInnerTransactionIsSuccessful = false;
@@ -581,6 +680,18 @@
             }
             if (mTransactionIsSuccessful) {
                 execSQL(COMMIT_SQL);
+                // if write-ahead logging is used, we have to take care of checkpoint.
+                // TODO: should applications be given the flexibility of choosing when to
+                // trigger checkpoint?
+                // for now, do checkpoint after every COMMIT because that is the fastest
+                // way to guarantee that readers will see latest data.
+                // but this is the slowest way to run sqlite with in write-ahead logging mode.
+                if (this.mConnectionPool != null) {
+                    execSQL("PRAGMA wal_checkpoint;");
+                    if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
+                        Log.i(TAG, "PRAGMA wal_Checkpoint done");
+                    }
+                }
             } else {
                 try {
                     execSQL("ROLLBACK;");
@@ -614,9 +725,7 @@
      * transaction is already marked as successful.
      */
     public void setTransactionSuccessful() {
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
+        verifyDbIsOpen();
         if (!mLock.isHeldByCurrentThread()) {
             throw new IllegalStateException("no transaction pending");
         }
@@ -631,7 +740,50 @@
      * return true if there is a transaction pending
      */
     public boolean inTransaction() {
-        return mLock.getHoldCount() > 0;
+        return mLock.getHoldCount() > 0 || mTransactionUsingExecSql;
+    }
+
+    /* package */ synchronized void setTransactionUsingExecSqlFlag() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.i(TAG, "found execSQL('begin transaction')");
+        }
+        mTransactionUsingExecSql = true;
+    }
+
+    /* package */ synchronized void resetTransactionUsingExecSqlFlag() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            if (mTransactionUsingExecSql) {
+                Log.i(TAG, "found execSQL('commit or end or rollback')");
+            }
+        }
+        mTransactionUsingExecSql = false;
+    }
+
+    /**
+     * Returns true if the caller is considered part of the current transaction, if any.
+     * <p>
+     * Caller is part of the current transaction if either of the following is true
+     * <ol>
+     *   <li>If transaction is started by calling beginTransaction() methods AND if the caller is
+     *   in the same thread as the thread that started the transaction.
+     *   </li>
+     *   <li>If the transaction is started by calling {@link #execSQL(String)} like this:
+     *   execSQL("BEGIN transaction"). In this case, every thread in the process is considered
+     *   part of the current transaction.</li>
+     * </ol>
+     *
+     * @return true if the caller is considered part of the current transaction, if any.
+     */
+    /* package */ synchronized boolean amIInTransaction() {
+        // always do this test on the main database connection - NOT on pooled database connection
+        // since transactions always occur on the main database connections only.
+        SQLiteDatabase db = (isPooledConnection()) ? mParentConnObj : this;
+        boolean b = (!db.inTransaction()) ? false :
+                db.mTransactionUsingExecSql || db.mLock.isHeldByCurrentThread();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.i(TAG, "amIinTransaction: " + b);
+        }
+        return b;
     }
 
     /**
@@ -735,54 +887,12 @@
         return true;
     }
 
-    /** Maps table names to info about what to which _sync_time column to set
-     * to NULL on an update. This is used to support syncing. */
-    private final Map<String, SyncUpdateInfo> mSyncUpdateInfo =
-            new HashMap<String, SyncUpdateInfo>();
-
-    public Map<String, String> getSyncedTables() {
-        synchronized(mSyncUpdateInfo) {
-            HashMap<String, String> tables = new HashMap<String, String>();
-            for (String table : mSyncUpdateInfo.keySet()) {
-                SyncUpdateInfo info = mSyncUpdateInfo.get(table);
-                if (info.deletedTable != null) {
-                    tables.put(table, info.deletedTable);
-                }
-            }
-            return tables;
-        }
-    }
-
     /**
-     * Internal class used to keep track what needs to be marked as changed
-     * when an update occurs. This is used for syncing, so the sync engine
-     * knows what data has been updated locally.
+     * @deprecated This method no longer serves any useful purpose and has been deprecated.
      */
-    static private class SyncUpdateInfo {
-        /**
-         * Creates the SyncUpdateInfo class.
-         *
-         * @param masterTable The table to set _sync_time to NULL in
-         * @param deletedTable The deleted table that corresponds to the
-         *          master table
-         * @param foreignKey The key that refers to the primary key in table
-         */
-        SyncUpdateInfo(String masterTable, String deletedTable,
-                String foreignKey) {
-            this.masterTable = masterTable;
-            this.deletedTable = deletedTable;
-            this.foreignKey = foreignKey;
-        }
-
-        /** The table containing the _sync_time column */
-        String masterTable;
-
-        /** The deleted table that corresponds to the master table */
-        String deletedTable;
-
-        /** The key in the local table the row in table. It may be _id, if table
-         * is the local table. */
-        String foreignKey;
+    @Deprecated
+    public Map<String, String> getSyncedTables() {
+        return new HashMap<String, String>(0);
     }
 
     /**
@@ -791,8 +901,7 @@
     public interface CursorFactory {
         /**
          * See
-         * {@link SQLiteCursor#SQLiteCursor(SQLiteDatabase, SQLiteCursorDriver,
-         * String, SQLiteQuery)}.
+         * {@link SQLiteCursor#SQLiteCursor(SQLiteCursorDriver, String, SQLiteQuery)}.
          */
         public Cursor newCursor(SQLiteDatabase db,
                 SQLiteCursorDriver masterQuery, String editTable,
@@ -814,30 +923,80 @@
      * @throws SQLiteException if the database cannot be opened
      */
     public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
-        SQLiteDatabase sqliteDatabase = null;
+        return openDatabase(path, factory, flags, new DefaultDatabaseErrorHandler());
+    }
+
+    /**
+     * Open the database according to the flags {@link #OPEN_READWRITE}
+     * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
+     *
+     * <p>Sets the locale of the database to the  the system's current locale.
+     * Call {@link #setLocale} if you would like something else.</p>
+     *
+     * <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
+     * used to handle corruption when sqlite reports database corruption.</p>
+     *
+     * @param path to database file to open and/or create
+     * @param factory an optional factory class that is called to instantiate a
+     *            cursor when query is called, or null for default
+     * @param flags to control database access mode
+     * @param errorHandler the {@link DatabaseErrorHandler} obj to be used to handle corruption
+     * when sqlite reports database corruption
+     * @return the newly opened database
+     * @throws SQLiteException if the database cannot be opened
+     */
+    public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
+            DatabaseErrorHandler errorHandler) {
+        SQLiteDatabase sqliteDatabase = openDatabase(path, factory, flags, errorHandler,
+                (short) 0 /* the main connection handle */);
+
+        // set sqlite pagesize to mBlockSize
+        if (sBlockSize == 0) {
+            // TODO: "/data" should be a static final String constant somewhere. it is hardcoded
+            // in several places right now.
+            sBlockSize = new StatFs("/data").getBlockSize();
+        }
+        sqliteDatabase.setPageSize(sBlockSize);
+        //STOPSHIP - uncomment the following line
+        //sqliteDatabase.setJournalMode(path, "TRUNCATE");
+        // STOPSHIP remove the following lines
+        if (!path.equalsIgnoreCase(MEMORY_DB_PATH)) {
+            sqliteDatabase.enableWriteAheadLogging();
+        }
+        // END STOPSHIP
+
+        // add this database to the list of databases opened in this process
+        synchronized(mActiveDatabases) {
+            mActiveDatabases.add(new WeakReference<SQLiteDatabase>(sqliteDatabase));
+        }
+        return sqliteDatabase;
+    }
+
+    private static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
+            DatabaseErrorHandler errorHandler, short connectionNum) {
+        SQLiteDatabase db = new SQLiteDatabase(path, factory, flags, errorHandler, connectionNum);
         try {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.i(TAG, "opening the db : " + path);
+            }
             // Open the database.
-            sqliteDatabase = new SQLiteDatabase(path, factory, flags);
+            db.dbopen(path, flags);
+            db.setLocale(Locale.getDefault());
             if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
-                sqliteDatabase.enableSqlTracing(path);
+                db.enableSqlTracing(path, connectionNum);
             }
             if (SQLiteDebug.DEBUG_SQL_TIME) {
-                sqliteDatabase.enableSqlProfiling(path);
+                db.enableSqlProfiling(path, connectionNum);
             }
+            return db;
         } catch (SQLiteDatabaseCorruptException e) {
-            // Try to recover from this, if we can.
-            // TODO: should we do this for other open failures?
-            Log.e(TAG, "Deleting and re-creating corrupt database " + path, e);
-            EventLog.writeEvent(EVENT_DB_CORRUPT, path);
-            if (!path.equalsIgnoreCase(":memory")) {
-                // delete is only for non-memory database files
-                new File(path).delete();
-            }
-            sqliteDatabase = new SQLiteDatabase(path, factory, flags);
+            db.mErrorHandler.onCorruption(db);
+            return SQLiteDatabase.openDatabase(path, factory, flags, errorHandler);
+        } catch (SQLiteException e) {
+            Log.e(TAG, "Failed to open the database. closing it.", e);
+            db.close();
+            throw e;
         }
-        ActiveDatabases.getInstance().mActiveDatabases.add(
-                new WeakReference<SQLiteDatabase>(sqliteDatabase));
-        return sqliteDatabase;
     }
 
     /**
@@ -855,6 +1014,25 @@
     }
 
     /**
+     * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler).
+     */
+    public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory,
+            DatabaseErrorHandler errorHandler) {
+        return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler);
+    }
+
+    private void setJournalMode(final String dbPath, final String mode) {
+        // journal mode can be set only for non-memory databases
+        if (!dbPath.equalsIgnoreCase(MEMORY_DB_PATH)) {
+            String s = DatabaseUtils.stringForQuery(this, "PRAGMA journal_mode=" + mode, null);
+            if (!s.equalsIgnoreCase(mode)) {
+                Log.e(TAG, "setting journal_mode to " + mode + " failed for db: " + dbPath +
+                        " (on pragma set journal_mode, sqlite returned:" + s);
+            }
+        }
+    }
+
+    /**
      * Create a memory backed SQLite database.  Its contents will be destroyed
      * when the database is closed.
      *
@@ -867,7 +1045,7 @@
      */
     public static SQLiteDatabase create(CursorFactory factory) {
         // This is a magic string with special meaning for SQLite.
-        return openDatabase(":memory:", factory, CREATE_IF_NECESSARY);
+        return openDatabase(MEMORY_DB_PATH, factory, CREATE_IF_NECESSARY);
     }
 
     /**
@@ -875,20 +1053,38 @@
      */
     public void close() {
         if (!isOpen()) {
-            return; // already closed
+            return;
+        }
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.i(TAG, "closing db: " + mPath + " (connection # " + mConnectionNum);
         }
         lock();
         try {
+            // some other thread could have closed this database while I was waiting for lock.
+            // check the database state
+            if (!isOpen()) {
+                return;
+            }
             closeClosable();
+            // finalize ALL statements queued up so far
+            closePendingStatements();
+            releaseCustomFunctions();
             // close this database instance - regardless of its reference count value
-            onAllReferencesReleased();
+            closeDatabase();
+            if (mConnectionPool != null) {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    assert mConnectionPool != null;
+                    Log.i(TAG, mConnectionPool.toString());
+                }
+                mConnectionPool.close();
+            }
         } finally {
-            unlock();
+            unlock();            
         }
     }
 
     private void closeClosable() {
-        /* deallocate all compiled sql statement objects from mCompiledQueries cache.
+        /* deallocate all compiled SQL statement objects from mCompiledQueries cache.
          * this should be done before de-referencing all {@link SQLiteClosable} objects
          * from this database object because calling
          * {@link SQLiteClosable#onAllReferencesReleasedFromContainer()} could cause the database
@@ -908,29 +1104,106 @@
     }
 
     /**
+     * package level access for testing purposes
+     */
+    /* package */ void closeDatabase() throws SQLiteException {
+        try {
+            dbclose();
+        } catch (SQLiteUnfinalizedObjectsException e)  {
+            String msg = e.getMessage();
+            String[] tokens = msg.split(",", 2);
+            int stmtId = Integer.parseInt(tokens[0]);
+            // get extra info about this statement, if it is still to be released by closeClosable()
+            Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
+            boolean found = false;
+            while (iter.hasNext()) {
+                Map.Entry<SQLiteClosable, Object> entry = iter.next();
+                SQLiteClosable program = entry.getKey();
+                if (program != null && program instanceof SQLiteProgram) {
+                        SQLiteCompiledSql compiledSql = ((SQLiteProgram)program).mCompiledSql;
+                        if (compiledSql.nStatement == stmtId) {
+                            msg = compiledSql.toString();
+                            found = true;
+                        }
+                }
+            }
+            if (!found) {
+                // the statement is already released by closeClosable(). is it waiting to be
+                // finalized?
+                if (mClosedStatementIds.contains(stmtId)) {
+                    Log.w(TAG, "this shouldn't happen. finalizing the statement now: ");
+                    closePendingStatements();
+                    // try to close the database again
+                    closeDatabase();
+                }
+            } else {
+                // the statement is not yet closed. most probably programming error in the app.
+                Log.w(TAG, "dbclose failed due to un-close()d SQL statements: " + msg);
+                throw e;
+            }
+        }
+    }
+
+    /**
      * Native call to close the database.
      */
     private native void dbclose();
 
     /**
+     * A callback interface for a custom sqlite3 function.
+     * This can be used to create a function that can be called from
+     * sqlite3 database triggers.
+     * @hide
+     */
+    public interface CustomFunction {
+        public void callback(String[] args);
+    }
+
+    /**
+     * Registers a CustomFunction callback as a function that can be called from
+     * sqlite3 database triggers.
+     * @param name the name of the sqlite3 function
+     * @param numArgs the number of arguments for the function
+     * @param function callback to call when the function is executed
+     * @hide
+     */
+    public void addCustomFunction(String name, int numArgs, CustomFunction function) {
+        verifyDbIsOpen();
+        synchronized (mCustomFunctions) {
+            int ref = native_addCustomFunction(name, numArgs, function);
+            if (ref != 0) {
+                // save a reference to the function for cleanup later
+                mCustomFunctions.add(new Integer(ref));
+            } else {
+                throw new SQLiteException("failed to add custom function " + name);
+            }
+        }
+    }
+
+    private void releaseCustomFunctions() {
+        synchronized (mCustomFunctions) {
+            for (int i = 0; i < mCustomFunctions.size(); i++) {
+                Integer function = mCustomFunctions.get(i);
+                native_releaseCustomFunction(function.intValue());
+            }
+            mCustomFunctions.clear();
+        }
+    }
+
+    // list of CustomFunction references so we can clean up when the database closes
+    private final ArrayList<Integer> mCustomFunctions =
+            new ArrayList<Integer>();
+
+    private native int native_addCustomFunction(String name, int numArgs, CustomFunction function);
+    private native void native_releaseCustomFunction(int function);
+
+    /**
      * Gets the database version.
      *
      * @return the database version
      */
     public int getVersion() {
-        SQLiteStatement prog = null;
-        lock();
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
-        try {
-            prog = new SQLiteStatement(this, "PRAGMA user_version;");
-            long version = prog.simpleQueryForLong();
-            return (int) version;
-        } finally {
-            if (prog != null) prog.close();
-            unlock();
-        }
+        return ((Long) DatabaseUtils.longForQuery(this, "PRAGMA user_version;", null)).intValue();
     }
 
     /**
@@ -948,20 +1221,8 @@
      * @return the new maximum database size
      */
     public long getMaximumSize() {
-        SQLiteStatement prog = null;
-        lock();
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
-        try {
-            prog = new SQLiteStatement(this,
-                    "PRAGMA max_page_count;");
-            long pageCount = prog.simpleQueryForLong();
-            return pageCount * getPageSize();
-        } finally {
-            if (prog != null) prog.close();
-            unlock();
-        }
+        long pageCount = DatabaseUtils.longForQuery(this, "PRAGMA max_page_count;", null);
+        return pageCount * getPageSize();
     }
 
     /**
@@ -972,26 +1233,15 @@
      * @return the new maximum database size
      */
     public long setMaximumSize(long numBytes) {
-        SQLiteStatement prog = null;
-        lock();
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
+        long pageSize = getPageSize();
+        long numPages = numBytes / pageSize;
+        // If numBytes isn't a multiple of pageSize, bump up a page
+        if ((numBytes % pageSize) != 0) {
+            numPages++;
         }
-        try {
-            long pageSize = getPageSize();
-            long numPages = numBytes / pageSize;
-            // If numBytes isn't a multiple of pageSize, bump up a page
-            if ((numBytes % pageSize) != 0) {
-                numPages++;
-            }
-            prog = new SQLiteStatement(this,
-                    "PRAGMA max_page_count = " + numPages);
-            long newPageCount = prog.simpleQueryForLong();
-            return newPageCount * pageSize;
-        } finally {
-            if (prog != null) prog.close();
-            unlock();
-        }
+        long newPageCount = DatabaseUtils.longForQuery(this, "PRAGMA max_page_count = " + numPages,
+                null);
+        return newPageCount * pageSize;
     }
 
     /**
@@ -1000,20 +1250,7 @@
      * @return the database page size, in bytes
      */
     public long getPageSize() {
-        SQLiteStatement prog = null;
-        lock();
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
-        try {
-            prog = new SQLiteStatement(this,
-                    "PRAGMA page_size;");
-            long size = prog.simpleQueryForLong();
-            return size;
-        } finally {
-            if (prog != null) prog.close();
-            unlock();
-        }
+        return DatabaseUtils.longForQuery(this, "PRAGMA page_size;", null);
     }
 
     /**
@@ -1034,9 +1271,10 @@
      * @param table the table to mark as syncable
      * @param deletedTable The deleted table that corresponds to the
      *          syncable table
+     * @deprecated This method no longer serves any useful purpose and has been deprecated.
      */
+    @Deprecated
     public void markTableSyncable(String table, String deletedTable) {
-        markTableSyncable(table, "_id", table, deletedTable);
     }
 
     /**
@@ -1049,60 +1287,10 @@
      * @param foreignKey this is the column in table whose value is an _id in
      *          updateTable
      * @param updateTable this is the table that will have its _sync_dirty
+     * @deprecated This method no longer serves any useful purpose and has been deprecated.
      */
-    public void markTableSyncable(String table, String foreignKey,
-            String updateTable) {
-        markTableSyncable(table, foreignKey, updateTable, null);
-    }
-
-    /**
-     * Mark this table as syncable, with the _sync_dirty residing in another
-     * table. When an update occurs in this table the _sync_dirty field of the
-     * row in updateTable with the _id in foreignKey will be set to
-     * ensure proper syncing operation.
-     *
-     * @param table an update on this table will trigger a sync time removal
-     * @param foreignKey this is the column in table whose value is an _id in
-     *          updateTable
-     * @param updateTable this is the table that will have its _sync_dirty
-     * @param deletedTable The deleted table that corresponds to the
-     *          updateTable
-     */
-    private void markTableSyncable(String table, String foreignKey,
-            String updateTable, String deletedTable) {
-        lock();
-        try {
-            native_execSQL("SELECT _sync_dirty FROM " + updateTable
-                    + " LIMIT 0");
-            native_execSQL("SELECT " + foreignKey + " FROM " + table
-                    + " LIMIT 0");
-        } finally {
-            unlock();
-        }
-
-        SyncUpdateInfo info = new SyncUpdateInfo(updateTable, deletedTable,
-                foreignKey);
-        synchronized (mSyncUpdateInfo) {
-            mSyncUpdateInfo.put(table, info);
-        }
-    }
-
-    /**
-     * Call for each row that is updated in a cursor.
-     *
-     * @param table the table the row is in
-     * @param rowId the row ID of the updated row
-     */
-    /* package */ void rowUpdated(String table, long rowId) {
-        SyncUpdateInfo info;
-        synchronized (mSyncUpdateInfo) {
-            info = mSyncUpdateInfo.get(table);
-        }
-        if (info != null) {
-            execSQL("UPDATE " + info.masterTable
-                    + " SET _sync_dirty=1 WHERE _id=(SELECT " + info.foreignKey
-                    + " FROM " + table + " WHERE _id=" + rowId + ")");
-        }
+    @Deprecated
+    public void markTableSyncable(String table, String foreignKey, String updateTable) {
     }
 
     /**
@@ -1134,6 +1322,8 @@
      * statement and fill in those values with {@link SQLiteProgram#bindString}
      * and {@link SQLiteProgram#bindLong} each time you want to run the
      * statement. Statements may not return result sets larger than 1x1.
+     *<p>
+     * No two threads should be using the same {@link SQLiteStatement} at the same time.
      *
      * @param sql The raw SQL statement, may contain ? for unknown values to be
      *            bound later.
@@ -1141,15 +1331,8 @@
      * {@link SQLiteStatement}s are not synchronized, see the documentation for more details.
      */
     public SQLiteStatement compileStatement(String sql) throws SQLException {
-        lock();
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
-        try {
-            return new SQLiteStatement(this, sql);
-        } finally {
-            unlock();
-        }
+        verifyDbIsOpen();
+        return new SQLiteStatement(this, sql, null);
     }
 
     /**
@@ -1226,9 +1409,7 @@
             boolean distinct, String table, String[] columns,
             String selection, String[] selectionArgs, String groupBy,
             String having, String orderBy, String limit) {
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
+        verifyDbIsOpen();
         String sql = SQLiteQueryBuilder.buildQueryString(
                 distinct, table, columns, selection, groupBy, having, orderBy, limit);
 
@@ -1339,9 +1520,7 @@
     public Cursor rawQueryWithFactory(
             CursorFactory cursorFactory, String sql, String[] selectionArgs,
             String editTable) {
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
+        verifyDbIsOpen();
         BlockGuard.getThreadPolicy().onReadFromDisk();
         long timeStart = 0;
 
@@ -1349,7 +1528,8 @@
             timeStart = System.currentTimeMillis();
         }
 
-        SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable);
+        SQLiteDatabase db = getDbConnection(sql);
+        SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(db, sql, editTable);
 
         Cursor cursor = null;
         try {
@@ -1375,6 +1555,7 @@
                                   : "<null>")  + ", count is " + count);
                 }
             }
+            releaseDbConnection(db);
         }
         return cursor;
     }
@@ -1501,84 +1682,41 @@
      */
     public long insertWithOnConflict(String table, String nullColumnHack,
             ContentValues initialValues, int conflictAlgorithm) {
-        BlockGuard.getThreadPolicy().onWriteToDisk();
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
-
-        // Measurements show most sql lengths <= 152
-        StringBuilder sql = new StringBuilder(152);
+        StringBuilder sql = new StringBuilder();
         sql.append("INSERT");
         sql.append(CONFLICT_VALUES[conflictAlgorithm]);
         sql.append(" INTO ");
         sql.append(table);
-        // Measurements show most values lengths < 40
-        StringBuilder values = new StringBuilder(40);
+        sql.append('(');
 
-        Set<Map.Entry<String, Object>> entrySet = null;
-        if (initialValues != null && initialValues.size() > 0) {
-            entrySet = initialValues.valueSet();
-            Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
-            sql.append('(');
-
-            boolean needSeparator = false;
-            while (entriesIter.hasNext()) {
-                if (needSeparator) {
-                    sql.append(", ");
-                    values.append(", ");
-                }
-                needSeparator = true;
-                Map.Entry<String, Object> entry = entriesIter.next();
-                sql.append(entry.getKey());
-                values.append('?');
+        Object[] bindArgs = null;
+        int size = (initialValues != null && initialValues.size() > 0) ? initialValues.size() : 0;
+        if (size > 0) {
+            bindArgs = new Object[size];
+            int i = 0;
+            for (String colName : initialValues.keySet()) {
+                sql.append((i > 0) ? "," : "");
+                sql.append(colName);
+                bindArgs[i++] = initialValues.get(colName);
             }
-
             sql.append(')');
+            sql.append(" VALUES (");
+            for (i = 0; i < size; i++) {
+                sql.append((i > 0) ? ",?" : "?");
+            }
         } else {
-            sql.append("(" + nullColumnHack + ") ");
-            values.append("NULL");
+            sql.append(nullColumnHack + ") VALUES (NULL");
         }
+        sql.append(')');
 
-        sql.append(" VALUES(");
-        sql.append(values);
-        sql.append(");");
-
-        lock();
-        SQLiteStatement statement = null;
+        SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
         try {
-            statement = compileStatement(sql.toString());
-
-            // Bind the values
-            if (entrySet != null) {
-                int size = entrySet.size();
-                Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
-                for (int i = 0; i < size; i++) {
-                    Map.Entry<String, Object> entry = entriesIter.next();
-                    DatabaseUtils.bindObjectToProgram(statement, i + 1, entry.getValue());
-                }
-            }
-
-            // Run the program and then cleanup
-            statement.execute();
-
-            long insertedRowId = lastInsertRow();
-            if (insertedRowId == -1) {
-                Log.e(TAG, "Error inserting " + initialValues + " using " + sql);
-            } else {
-                if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "Inserting row " + insertedRowId + " from "
-                            + initialValues + " using " + sql);
-                }
-            }
-            return insertedRowId;
+            return statement.executeInsert();
         } catch (SQLiteDatabaseCorruptException e) {
             onCorruption();
             throw e;
         } finally {
-            if (statement != null) {
-                statement.close();
-            }
-            unlock();
+            statement.close();
         }
     }
 
@@ -1593,32 +1731,15 @@
      *         whereClause.
      */
     public int delete(String table, String whereClause, String[] whereArgs) {
-        BlockGuard.getThreadPolicy().onWriteToDisk();
-        lock();
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
-        SQLiteStatement statement = null;
+        SQLiteStatement statement =  new SQLiteStatement(this, "DELETE FROM " + table +
+                (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
         try {
-            statement = compileStatement("DELETE FROM " + table
-                    + (!TextUtils.isEmpty(whereClause)
-                    ? " WHERE " + whereClause : ""));
-            if (whereArgs != null) {
-                int numArgs = whereArgs.length;
-                for (int i = 0; i < numArgs; i++) {
-                    DatabaseUtils.bindObjectToProgram(statement, i + 1, whereArgs[i]);
-                }
-            }
-            statement.execute();
-            return lastChangeCount();
+            return statement.executeUpdateDelete();
         } catch (SQLiteDatabaseCorruptException e) {
             onCorruption();
             throw e;
         } finally {
-            if (statement != null) {
-                statement.close();
-            }
-            unlock();
+            statement.close();
         }
     }
 
@@ -1649,8 +1770,8 @@
      */
     public int updateWithOnConflict(String table, ContentValues values,
             String whereClause, String[] whereArgs, int conflictAlgorithm) {
-        BlockGuard.getThreadPolicy().onWriteToDisk();
-        if (values == null || values.size() == 0) {
+        int setValuesSize = values.size();
+        if (values == null || setValuesSize == 0) {
             throw new IllegalArgumentException("Empty values");
         }
 
@@ -1660,98 +1781,71 @@
         sql.append(table);
         sql.append(" SET ");
 
-        Set<Map.Entry<String, Object>> entrySet = values.valueSet();
-        Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
-
-        while (entriesIter.hasNext()) {
-            Map.Entry<String, Object> entry = entriesIter.next();
-            sql.append(entry.getKey());
+        // move all bind args to one array
+        int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
+        Object[] bindArgs = new Object[bindArgsSize];
+        int i = 0;
+        for (String colName : values.keySet()) {
+            sql.append((i > 0) ? "," : "");
+            sql.append(colName);
+            bindArgs[i++] = values.get(colName);
             sql.append("=?");
-            if (entriesIter.hasNext()) {
-                sql.append(", ");
+        }
+        if (whereArgs != null) {
+            for (i = setValuesSize; i < bindArgsSize; i++) {
+                bindArgs[i] = whereArgs[i - setValuesSize];
             }
         }
-
         if (!TextUtils.isEmpty(whereClause)) {
             sql.append(" WHERE ");
             sql.append(whereClause);
         }
 
-        lock();
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
-        SQLiteStatement statement = null;
+        SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
         try {
-            statement = compileStatement(sql.toString());
-
-            // Bind the values
-            int size = entrySet.size();
-            entriesIter = entrySet.iterator();
-            int bindArg = 1;
-            for (int i = 0; i < size; i++) {
-                Map.Entry<String, Object> entry = entriesIter.next();
-                DatabaseUtils.bindObjectToProgram(statement, bindArg, entry.getValue());
-                bindArg++;
-            }
-
-            if (whereArgs != null) {
-                size = whereArgs.length;
-                for (int i = 0; i < size; i++) {
-                    statement.bindString(bindArg, whereArgs[i]);
-                    bindArg++;
-                }
-            }
-
-            // Run the program and then cleanup
-            statement.execute();
-            int numChangedRows = lastChangeCount();
-            if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
-                Log.v(TAG, "Updated " + numChangedRows + " using " + values + " and " + sql);
-            }
-            return numChangedRows;
+            return statement.executeUpdateDelete();
         } catch (SQLiteDatabaseCorruptException e) {
             onCorruption();
             throw e;
-        } catch (SQLException e) {
-            Log.e(TAG, "Error updating " + values + " using " + sql);
-            throw e;
         } finally {
-            if (statement != null) {
-                statement.close();
-            }
-            unlock();
+            statement.close();
         }
     }
 
     /**
-     * Execute a single SQL statement that is not a query. For example, CREATE
-     * TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
-     * supported. it takes a write lock
+     * Execute a single SQL statement that is NOT a SELECT
+     * or any other SQL statement that returns data.
+     * <p>
+     * It has no means to return any data (such as the number of affected rows).
+     * Instead, you're encouraged to use {@link #insert(String, String, ContentValues)},
+     * {@link #update(String, ContentValues, String, String[])}, et al, when possible.
+     * </p>
+     * <p>
+     * When using {@link #enableWriteAheadLogging()}, journal_mode is
+     * automatically managed by this class. So, do not set journal_mode
+     * using "PRAGMA journal_mode'<value>" statement if your app is using
+     * {@link #enableWriteAheadLogging()}
+     * </p>
      *
+     * @param sql the SQL statement to be executed. Multiple statements separated by semicolons are
+     * not supported.
      * @throws SQLException If the SQL string is invalid for some reason
      */
     public void execSQL(String sql) throws SQLException {
-        BlockGuard.getThreadPolicy().onWriteToDisk();
+        int stmtType = DatabaseUtils.getSqlStatementType(sql);
+        if (stmtType == DatabaseUtils.STATEMENT_ATTACH) {
+            disableWriteAheadLogging();
+        }
         long timeStart = SystemClock.uptimeMillis();
-        lock();
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
         logTimeStat(mLastSqlStatement, timeStart, GET_LOCK_LOG_PREFIX);
-        try {
-            native_execSQL(sql);
-        } catch (SQLiteDatabaseCorruptException e) {
-            onCorruption();
-            throw e;
-        } finally {
-            unlock();
-        }
+        executeSql(sql, null);
 
+        if (stmtType == DatabaseUtils.STATEMENT_ATTACH) {
+            mHasAttachedDbs = true;
+        }
         // Log commit statements along with the most recently executed
-        // SQL statement for disambiguation.  Note that instance
-        // equality to COMMIT_SQL is safe here.
-        if (sql == COMMIT_SQL) {
+        // SQL statement for disambiguation.
+        if (stmtType == DatabaseUtils.STATEMENT_COMMIT) {
             logTimeStat(mLastSqlStatement, timeStart, COMMIT_SQL);
         } else {
             logTimeStat(sql, timeStart, null);
@@ -1759,65 +1853,100 @@
     }
 
     /**
-     * Execute a single SQL statement that is not a query. For example, CREATE
-     * TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
-     * supported. it takes a write lock,
+     * Execute a single SQL statement that is NOT a SELECT/INSERT/UPDATE/DELETE.
+     * <p>
+     * For INSERT statements, use any of the following instead.
+     * <ul>
+     *   <li>{@link #insert(String, String, ContentValues)}</li>
+     *   <li>{@link #insertOrThrow(String, String, ContentValues)}</li>
+     *   <li>{@link #insertWithOnConflict(String, String, ContentValues, int)}</li>
+     * </ul>
+     * <p>
+     * For UPDATE statements, use any of the following instead.
+     * <ul>
+     *   <li>{@link #update(String, ContentValues, String, String[])}</li>
+     *   <li>{@link #updateWithOnConflict(String, ContentValues, String, String[], int)}</li>
+     * </ul>
+     * <p>
+     * For DELETE statements, use any of the following instead.
+     * <ul>
+     *   <li>{@link #delete(String, String, String[])}</li>
+     * </ul>
+     * <p>
+     * For example, the following are good candidates for using this method:
+     * <ul>
+     *   <li>ALTER TABLE</li>
+     *   <li>CREATE or DROP table / trigger / view / index / virtual table</li>
+     *   <li>REINDEX</li>
+     *   <li>RELEASE</li>
+     *   <li>SAVEPOINT</li>
+     *   <li>PRAGMA that returns no data</li>
+     * </ul>
+     * </p>
+     * <p>
+     * When using {@link #enableWriteAheadLogging()}, journal_mode is
+     * automatically managed by this class. So, do not set journal_mode
+     * using "PRAGMA journal_mode'<value>" statement if your app is using
+     * {@link #enableWriteAheadLogging()}
+     * </p>
      *
-     * @param sql
+     * @param sql the SQL statement to be executed. Multiple statements separated by semicolons are
+     * not supported.
      * @param bindArgs only byte[], String, Long and Double are supported in bindArgs.
      * @throws SQLException If the SQL string is invalid for some reason
      */
     public void execSQL(String sql, Object[] bindArgs) throws SQLException {
-        BlockGuard.getThreadPolicy().onWriteToDisk();
         if (bindArgs == null) {
             throw new IllegalArgumentException("Empty bindArgs");
         }
+        executeSql(sql, bindArgs);
+    }
+
+    private int executeSql(String sql, Object[] bindArgs) throws SQLException {
         long timeStart = SystemClock.uptimeMillis();
-        lock();
-        if (!isOpen()) {
-            throw new IllegalStateException("database not open");
-        }
-        SQLiteStatement statement = null;
+        int n;
+        SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
         try {
-            statement = compileStatement(sql);
-            if (bindArgs != null) {
-                int numArgs = bindArgs.length;
-                for (int i = 0; i < numArgs; i++) {
-                    DatabaseUtils.bindObjectToProgram(statement, i + 1, bindArgs[i]);
-                }
-            }
-            statement.execute();
+            n = statement.executeUpdateDelete();
         } catch (SQLiteDatabaseCorruptException e) {
             onCorruption();
             throw e;
         } finally {
-            if (statement != null) {
-                statement.close();
-            }
-            unlock();
+            statement.close();
         }
         logTimeStat(sql, timeStart);
+        return n;
     }
 
     @Override
-    protected void finalize() {
-        if (isOpen()) {
-            Log.e(TAG, "close() was never explicitly called on database '" +
-                    mPath + "' ", mStackTrace);
-            closeClosable();
-            onAllReferencesReleased();
+    protected void finalize() throws Throwable {
+        try {
+            if (isOpen()) {
+                Log.e(TAG, "close() was never explicitly called on database '" +
+                        mPath + "' ", mStackTrace);
+                closeClosable();
+                onAllReferencesReleased();
+                releaseCustomFunctions();
+            }
+        } finally {
+            super.finalize();
         }
     }
 
     /**
-     * Private constructor. See {@link #create} and {@link #openDatabase}.
+     * Private constructor.
      *
      * @param path The full path to the database
      * @param factory The factory to use when creating cursors, may be NULL.
      * @param flags 0 or {@link #NO_LOCALIZED_COLLATORS}.  If the database file already
      *              exists, mFlags will be updated appropriately.
+     * @param errorHandler The {@link DatabaseErrorHandler} to be used when sqlite reports database
+     * corruption. may be NULL.
+     * @param connectionNum 0 for main database connection handle. 1..N for pooled database
+     * connection handles.
      */
-    private SQLiteDatabase(String path, CursorFactory factory, int flags) {
+    private SQLiteDatabase(String path, CursorFactory factory, int flags,
+            DatabaseErrorHandler errorHandler, short connectionNum) {
         if (path == null) {
             throw new IllegalArgumentException("path should not be null");
         }
@@ -1826,25 +1955,11 @@
         mSlowQueryThreshold = SystemProperties.getInt(LOG_SLOW_QUERIES_PROPERTY, -1);
         mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
         mFactory = factory;
-        dbopen(mPath, mFlags);
-        if (SQLiteDebug.DEBUG_SQL_CACHE) {
-            mTimeOpened = getTime();
-        }
         mPrograms = new WeakHashMap<SQLiteClosable,Object>();
-        try {
-            setLocale(Locale.getDefault());
-        } catch (RuntimeException e) {
-            Log.e(TAG, "Failed to setLocale() when constructing, closing the database", e);
-            dbclose();
-            if (SQLiteDebug.DEBUG_SQL_CACHE) {
-                mTimeClosed = getTime();
-            }
-            throw e;
-        }
-    }
-
-    private String getTime() {
-        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ").format(System.currentTimeMillis());
+        // Set the DatabaseErrorHandler to be used when SQLite reports corruption.
+        // If the caller sets errorHandler = null, then use default errorhandler.
+        mErrorHandler = (errorHandler == null) ? new DefaultDatabaseErrorHandler() : errorHandler;
+        mConnectionNum = connectionNum;
     }
 
     /**
@@ -1901,7 +2016,7 @@
         }
         if (durationMillis >= sQueryLogTimeInMillis) {
             samplePercent = 100;
-        } else {;
+        } else {
             samplePercent = (int) (100 * durationMillis / sQueryLogTimeInMillis) + 1;
             if (mRandom.nextInt(100) >= samplePercent) return;
         }
@@ -1970,66 +2085,68 @@
         }
     }
 
-    /*
-     * ============================================================================
-     *
-     *       The following methods deal with compiled-sql cache
-     * ============================================================================
-     */
+    /* package */ void verifyDbIsOpen() {
+        if (!isOpen()) {
+            throw new IllegalStateException("database " + getPath() + " (conn# " +
+                    mConnectionNum + ") already closed");
+        }
+    }
+
+    /* package */ void verifyLockOwner() {
+        verifyDbIsOpen();
+        if (mLockingEnabled && !isDbLockedByCurrentThread()) {
+            throw new IllegalStateException("Don't have database lock!");
+        }
+    }
+
     /**
-     * adds the given sql and its compiled-statement-id-returned-by-sqlite to the
+     * Adds the given SQL and its compiled-statement-id-returned-by-sqlite to the
      * cache of compiledQueries attached to 'this'.
-     *
-     * if there is already a {@link SQLiteCompiledSql} in compiledQueries for the given sql,
+     * <p>
+     * If there is already a {@link SQLiteCompiledSql} in compiledQueries for the given SQL,
      * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
      * mapping is NOT replaced with the new mapping).
      */
     /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
-        if (mMaxSqlCacheSize == 0) {
-            // for this database, there is no cache of compiled sql.
-            if (SQLiteDebug.DEBUG_SQL_CACHE) {
-                Log.v(TAG, "|NOT adding_sql_to_cache|" + getPath() + "|" + sql);
-            }
-            return;
-        }
-
-        SQLiteCompiledSql compiledSql = null;
         synchronized(mCompiledQueries) {
             // don't insert the new mapping if a mapping already exists
-            compiledSql = mCompiledQueries.get(sql);
-            if (compiledSql != null) {
+            if (mCompiledQueries.containsKey(sql)) {
                 return;
             }
-            // add this <sql, compiledStatement> to the cache
-            if (mCompiledQueries.size() == mMaxSqlCacheSize) {
-                /*
-                 * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
-                 * log a warning MAX_WARNINGS_ON_CACHESIZE_CONDITION times
-                 * chances are it is NOT using ? for bindargs - so caching is useless.
-                 * TODO: either let the callers set max cchesize for their app, or intelligently
-                 * figure out what should be cached for a given app.
-                 */
-                if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
+
+            int maxCacheSz = (mConnectionNum == 0) ? mMaxSqlCacheSize :
+                    mParentConnObj.mMaxSqlCacheSize;
+            
+            if (SQLiteDebug.DEBUG_SQL_CACHE) {
+                boolean printWarning = (mConnectionNum == 0)
+                        ? (!mCacheFullWarning && mCompiledQueries.size() == maxCacheSz)
+                        : (!mParentConnObj.mCacheFullWarning &&
+                        mParentConnObj.mCompiledQueries.size() == maxCacheSz);
+                if (printWarning) {
+                    /*
+                     * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
+                     * log a warning.
+                     * chances are it is NOT using ? for bindargs - or cachesize is too small.
+                     */
                     Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
-                            getPath() + "; i.e., NO space for this sql statement in cache: " +
-                            sql + ". Please change your sql statements to use '?' for " +
-                            "bindargs, instead of using actual values");
-                }
-                // don't add this entry to cache
-            } else {
-                // cache is NOT full. add this to cache.
-                mCompiledQueries.put(sql, compiledStatement);
-                if (SQLiteDebug.DEBUG_SQL_CACHE) {
-                    Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" +
-                            mCompiledQueries.size() + "|" + sql);
+                            getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. ");
+                    mCacheFullWarning = true;
+                    Log.d(TAG, "Here are the SQL statements in Cache of database: " + mPath);
+                    for (String s : mCompiledQueries.keySet()) {
+                        Log.d(TAG, "Sql stament in Cache: " + s);
+                    }
                 }
             }
+            /* add the given SQLiteCompiledSql compiledStatement to cache.
+             * no need to worry about the cache size - because {@link #mCompiledQueries}
+             * self-limits its size to {@link #mMaxSqlCacheSize}.
+             */
+            mCompiledQueries.put(sql, compiledStatement);
         }
-        return;
     }
 
-
-    private void deallocCachedSqlStatements() {
+    /** package-level access for testing purposes */
+    /* package */ void deallocCachedSqlStatements() {
         synchronized (mCompiledQueries) {
             for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
                 compiledSql.releaseSqlStatement();
@@ -2039,199 +2156,464 @@
     }
 
     /**
-     * from the compiledQueries cache, returns the compiled-statement-id for the given sql.
-     * returns null, if not found in the cache.
+     * From the compiledQueries cache, returns the compiled-statement-id for the given SQL.
+     * Returns null, if not found in the cache.
      */
     /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
-        SQLiteCompiledSql compiledStatement = null;
-        boolean cacheHit;
-        synchronized(mCompiledQueries) {
-            if (mMaxSqlCacheSize == 0) {
-                // for this database, there is no cache of compiled sql.
-                if (SQLiteDebug.DEBUG_SQL_CACHE) {
-                    Log.v(TAG, "|cache NOT found|" + getPath());
-                }
+        synchronized (mCompiledQueries) {
+            SQLiteCompiledSql compiledStatement = mCompiledQueries.get(sql);
+            if (compiledStatement == null) {
+                mNumCacheMisses++;
                 return null;
             }
-            cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
-        }
-        if (cacheHit) {
             mNumCacheHits++;
-        } else {
-            mNumCacheMisses++;
+            return compiledStatement;
         }
-
-        if (SQLiteDebug.DEBUG_SQL_CACHE) {
-            Log.v(TAG, "|cache_stats|" +
-                    getPath() + "|" + mCompiledQueries.size() +
-                    "|" + mNumCacheHits + "|" + mNumCacheMisses +
-                    "|" + cacheHit + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql);
-        }
-        return compiledStatement;
     }
 
     /**
-     * returns true if the given sql is cached in compiled-sql cache.
-     * @hide
+     * Sets the maximum size of the prepared-statement cache for this database.
+     * (size of the cache = number of compiled-sql-statements stored in the cache).
+     *<p>
+     * Maximum cache size can ONLY be increased from its current size (default = 10).
+     * If this method is called with smaller size than the current maximum value,
+     * then IllegalStateException is thrown.
+     *<p>
+     * This method is thread-safe.
+     *
+     * @param cacheSize the size of the cache. can be (0 to {@link #MAX_SQL_CACHE_SIZE})
+     * @throws IllegalStateException if input cacheSize > {@link #MAX_SQL_CACHE_SIZE} or
+     * the value set with previous setMaxSqlCacheSize() call.
      */
-    public boolean isInCompiledSqlCache(String sql) {
+    public void setMaxSqlCacheSize(int cacheSize) {
         synchronized(mCompiledQueries) {
+            if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
+                throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
+            } else if (cacheSize < mMaxSqlCacheSize) {
+                throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
+                        "set with previous setMaxSqlCacheSize() call.");
+            }
+            mMaxSqlCacheSize = cacheSize;
+        }
+    }
+
+    /* package */ boolean isInStatementCache(String sql) {
+        synchronized (mCompiledQueries) {
             return mCompiledQueries.containsKey(sql);
         }
     }
 
-    /**
-     * purges the given sql from the compiled-sql cache.
-     * @hide
-     */
-    public void purgeFromCompiledSqlCache(String sql) {
+    /* package */ void releaseCompiledSqlObj(SQLiteCompiledSql compiledSql) {
+        synchronized (mCompiledQueries) {
+            if (mCompiledQueries.containsValue(compiledSql)) {
+                // it is in cache - reset its inUse flag
+                compiledSql.release();
+            } else {
+                // it is NOT in cache. finalize it.
+                compiledSql.releaseSqlStatement();
+            }
+        }
+    }
+
+    private int getCacheHitNum() {
         synchronized(mCompiledQueries) {
-            mCompiledQueries.remove(sql);
+            return mNumCacheHits;
         }
     }
 
-    /**
-     * remove everything from the compiled sql cache
-     * @hide
-     */
-    public void resetCompiledSqlCache() {
+    private int getCacheMissNum() {
         synchronized(mCompiledQueries) {
-            mCompiledQueries.clear();
+            return mNumCacheMisses;
+        }
+    }
+
+    private int getCachesize() {
+        synchronized(mCompiledQueries) {
+            return mCompiledQueries.size();
+        }
+    }
+
+    /* package */ void finalizeStatementLater(int id) {
+        if (!isOpen()) {
+            // database already closed. this statement will already have been finalized.
+            return;
+        }
+        synchronized(mClosedStatementIds) {
+            if (mClosedStatementIds.contains(id)) {
+                // this statement id is already queued up for finalization.
+                return;
+            }
+            mClosedStatementIds.add(id);
+        }
+    }
+
+    /* package */ void closePendingStatements() {
+        if (!isOpen()) {
+            // since this database is already closed, no need to finalize anything.
+            mClosedStatementIds.clear();
+            return;
+        }
+        verifyLockOwner();
+        /* to minimize synchronization on mClosedStatementIds, make a copy of the list */
+        ArrayList<Integer> list = new ArrayList<Integer>(mClosedStatementIds.size());
+        synchronized(mClosedStatementIds) {
+            list.addAll(mClosedStatementIds);
+            mClosedStatementIds.clear();
+        }
+        // finalize all the statements from the copied list
+        int size = list.size();
+        for (int i = 0; i < size; i++) {
+            native_finalize(list.get(i));
         }
     }
 
     /**
-     * return the current maxCacheSqlCacheSize
-     * @hide
+     * for testing only
      */
-    public synchronized int getMaxSqlCacheSize() {
-        return mMaxSqlCacheSize;
+    /* package */ ArrayList<Integer> getQueuedUpStmtList() {
+        return mClosedStatementIds;
     }
 
     /**
-     * set the max size of the compiled sql cache for this database after purging the cache.
-     * (size of the cache = number of compiled-sql-statements stored in the cache).
+     * This method enables parallel execution of queries from multiple threads on the same database.
+     * It does this by opening multiple handles to the database and using a different
+     * database handle for each query.
+     * <p>
+     * If a transaction is in progress on one connection handle and say, a table is updated in the
+     * transaction, then query on the same table on another connection handle will block for the
+     * transaction to complete. But this method enables such queries to execute by having them
+     * return old version of the data from the table. Most often it is the data that existed in the
+     * table prior to the above transaction updates on that table.
+     * <p>
+     * Maximum number of simultaneous handles used to execute queries in parallel is
+     * dependent upon the device memory and possibly other properties.
+     * <p>
+     * After calling this method, execution of queries in parallel is enabled as long as this
+     * database handle is open. To disable execution of queries in parallel, database should
+     * be closed and reopened.
+     * <p>
+     * If a query is part of a transaction, then it is executed on the same database handle the
+     * transaction was begun.
+     * <p>
+     * If the database has any attached databases, then execution of queries in paralel is NOT
+     * possible. In such cases, a message is printed to logcat and false is returned.
+     * <p>
+     * This feature is not available for :memory: databases. In such cases,
+     * a message is printed to logcat and false is returned.
+     * <p>
+     * A typical way to use this method is the following:
+     * <pre>
+     *     SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
+     *             CREATE_IF_NECESSARY, myDatabaseErrorHandler);
+     *     db.enableWriteAheadLogging();
+     * </pre>
+     * <p>
+     * Writers should use {@link #beginTransactionNonExclusive()} or
+     * {@link #beginTransactionWithListenerNonExclusive(SQLiteTransactionListener)}
+     * to start a trsnsaction.
+     * Non-exclusive mode allows database file to be in readable by threads executing queries.
+     * </p>
      *
-     * max cache size can ONLY be increased from its current size (default = 0).
-     * if this method is called with smaller size than the current value of mMaxSqlCacheSize,
-     * then IllegalStateException is thrown
-     *
-     * synchronized because we don't want t threads to change cache size at the same time.
-     * @param cacheSize the size of the cache. can be (0 to MAX_SQL_CACHE_SIZE)
-     * @throws IllegalStateException if input cacheSize > MAX_SQL_CACHE_SIZE or < 0 or
-     * < the value set with previous setMaxSqlCacheSize() call.
-     *
-     * @hide
+     * @return true if write-ahead-logging is set. false otherwise
      */
-    public synchronized void setMaxSqlCacheSize(int cacheSize) {
-        if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
-            throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
-        } else if (cacheSize < mMaxSqlCacheSize) {
-            throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
-                    "set with previous setMaxSqlCacheSize() call.");
+    public boolean enableWriteAheadLogging() {
+        // acquire lock - no that no other thread is enabling WAL at the same time
+        lock();
+        try {
+            if (mConnectionPool != null) {
+                // already enabled
+                return true;
+            }
+            if (mPath.equalsIgnoreCase(MEMORY_DB_PATH)) {
+                Log.i(TAG, "can't enable WAL for memory databases.");
+                return false;
+            }
+
+            // make sure this database has NO attached databases because sqlite's write-ahead-logging
+            // doesn't work for databases with attached databases
+            if (mHasAttachedDbs) {
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG,
+                            "this database: " + mPath + " has attached databases. can't  enable WAL.");
+                }
+                return false;
+            }
+            mConnectionPool = new DatabaseConnectionPool(this);
+            setJournalMode(mPath, "WAL");
+            return true;
+        } finally {
+            unlock();
         }
-        mMaxSqlCacheSize = cacheSize;
     }
 
-    static class ActiveDatabases {
-        private static final ActiveDatabases activeDatabases = new ActiveDatabases();
-        private HashSet<WeakReference<SQLiteDatabase>> mActiveDatabases =
-            new HashSet<WeakReference<SQLiteDatabase>>();
-        private ActiveDatabases() {} // disable instantiation of this class
-        static ActiveDatabases getInstance() {return activeDatabases;}
+    /**
+     * This method disables the features enabled by {@link #enableWriteAheadLogging()}.
+     * @hide
+     */
+    public void disableWriteAheadLogging() {
+        // grab database lock so that writeAheadLogging is not disabled from 2 different threads
+        // at the same time
+        lock();
+        try {
+            if (mConnectionPool == null) {
+                return; // already disabled
+            }
+            mConnectionPool.close();
+            setJournalMode(mPath, "TRUNCATE");
+            mConnectionPool = null;
+        } finally {
+            unlock();
+        }
+    }
+
+    /* package */ SQLiteDatabase getDatabaseHandle(String sql) {
+        if (isPooledConnection()) {
+            // this is a pooled database connection
+            // use it if it is open AND if I am not currently part of a transaction
+            if (isOpen() && !amIInTransaction()) {
+                // TODO: use another connection from the pool
+                // if this connection is currently in use by some other thread
+                // AND if there are free connections in the pool
+                return this;
+            } else {
+                // the pooled connection is not open! could have been closed either due
+                // to corruption on this or some other connection to the database
+                // OR, maybe the connection pool is disabled after this connection has been
+                // allocated to me. try to get some other pooled or main database connection
+                return getParentDbConnObj().getDbConnection(sql);
+            }
+        } else {
+            // this is NOT a pooled connection. can we get one?
+            return getDbConnection(sql);
+        }
+    }
+
+    /* package */ SQLiteDatabase createPoolConnection(short connectionNum) {
+        SQLiteDatabase db = openDatabase(mPath, mFactory, mFlags, mErrorHandler, connectionNum);
+        db.mParentConnObj = this;
+        return db;
+    }
+
+    private synchronized SQLiteDatabase getParentDbConnObj() {
+        return mParentConnObj;
+    }
+
+    private boolean isPooledConnection() {
+        return this.mConnectionNum > 0;
+    }
+
+    /* package */ SQLiteDatabase getDbConnection(String sql) {
+        verifyDbIsOpen();
+        // this method should always be called with main database connection handle.
+        // the only time when it is called with pooled database connection handle is
+        // corruption occurs while trying to open a pooled database connection handle.
+        // in that case, simply return 'this' handle
+        if (isPooledConnection()) {
+            return this;
+        }
+
+        // use the current connection handle if
+        // 1. if the caller is part of the ongoing transaction, if any
+        // 2. OR, if there is NO connection handle pool setup
+        if (amIInTransaction() || mConnectionPool == null) {
+            return this;
+        } else {
+            // get a connection handle from the pool
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                assert mConnectionPool != null;
+                Log.i(TAG, mConnectionPool.toString());
+            }
+            return mConnectionPool.get(sql);
+        }
+    }
+
+    private void releaseDbConnection(SQLiteDatabase db) {
+        // ignore this release call if
+        // 1. the database is closed
+        // 2. OR, if db is NOT a pooled connection handle
+        // 3. OR, if the database being released is same as 'this' (this condition means
+        //     that we should always be releasing a pooled connection handle by calling this method
+        //     from the 'main' connection handle
+        if (!isOpen() || !db.isPooledConnection() || (db == this)) {
+            return;
+        }
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            assert isPooledConnection();
+            assert mConnectionPool != null;
+            Log.d(TAG, "releaseDbConnection threadid = " + Thread.currentThread().getId() +
+                    ", releasing # " + db.mConnectionNum + ", " + getPath());
+        }
+        mConnectionPool.release(db);
     }
 
     /**
      * this method is used to collect data about ALL open databases in the current process.
-     * bugreport is a user of this data. 
+     * bugreport is a user of this data.
      */
     /* package */ static ArrayList<DbStats> getDbStats() {
         ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>();
-        for (WeakReference<SQLiteDatabase> w : ActiveDatabases.getInstance().mActiveDatabases) {
+        // make a local copy of mActiveDatabases - so that this method is not competing
+        // for synchronization lock on mActiveDatabases
+        ArrayList<WeakReference<SQLiteDatabase>> tempList;
+        synchronized(mActiveDatabases) {
+            tempList = (ArrayList<WeakReference<SQLiteDatabase>>)mActiveDatabases.clone();
+        }
+        for (WeakReference<SQLiteDatabase> w : tempList) {
             SQLiteDatabase db = w.get();
             if (db == null || !db.isOpen()) {
                 continue;
             }
-            // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
-            int lookasideUsed = db.native_getDbLookaside();
 
-            // get the lastnode of the dbname
-            String path = db.getPath();
-            int indx = path.lastIndexOf("/");
-            String lastnode = path.substring((indx != -1) ? ++indx : 0);
+            try {
+                // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
+                int lookasideUsed = db.native_getDbLookaside();
 
-            // get list of attached dbs and for each db, get its size and pagesize
-            ArrayList<Pair<String, String>> attachedDbs = getAttachedDbs(db);
-            if (attachedDbs == null) {
-                continue;
-            }
-            for (int i = 0; i < attachedDbs.size(); i++) {
-                Pair<String, String> p = attachedDbs.get(i);
-                long pageCount = getPragmaVal(db, p.first + ".page_count;");
+                // get the lastnode of the dbname
+                String path = db.getPath();
+                int indx = path.lastIndexOf("/");
+                String lastnode = path.substring((indx != -1) ? ++indx : 0);
 
-                // first entry in the attached db list is always the main database
-                // don't worry about prefixing the dbname with "main"
-                String dbName;
-                if (i == 0) {
-                    dbName = lastnode;
-                } else {
-                    // lookaside is only relevant for the main db
-                    lookasideUsed = 0;
-                    dbName = "  (attached) " + p.first;
-                    // if the attached db has a path, attach the lastnode from the path to above
-                    if (p.second.trim().length() > 0) {
-                        int idx = p.second.lastIndexOf("/");
-                        dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
+                // get list of attached dbs and for each db, get its size and pagesize
+                ArrayList<Pair<String, String>> attachedDbs = db.getAttachedDbs();
+                if (attachedDbs == null) {
+                    continue;
+                }
+                for (int i = 0; i < attachedDbs.size(); i++) {
+                    Pair<String, String> p = attachedDbs.get(i);
+                    long pageCount = DatabaseUtils.longForQuery(db, "PRAGMA " + p.first
+                            + ".page_count;", null);
+
+                    // first entry in the attached db list is always the main database
+                    // don't worry about prefixing the dbname with "main"
+                    String dbName;
+                    if (i == 0) {
+                        dbName = lastnode;
+                    } else {
+                        // lookaside is only relevant for the main db
+                        lookasideUsed = 0;
+                        dbName = "  (attached) " + p.first;
+                        // if the attached db has a path, attach the lastnode from the path to above
+                        if (p.second.trim().length() > 0) {
+                            int idx = p.second.lastIndexOf("/");
+                            dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
+                        }
+                    }
+                    if (pageCount > 0) {
+                        dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
+                                lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(),
+                                db.getCachesize()));
                     }
                 }
-                if (pageCount > 0) {
-                    dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
-                            lookasideUsed));
+                // if there are pooled connections, return the cache stats for them also.
+                // while we are trying to query the pooled connections for stats, some other thread
+                // could be disabling conneciton pool. so, grab a reference to the connection pool.
+                DatabaseConnectionPool connPool = db.mConnectionPool;
+                if (connPool != null) {
+                    for (SQLiteDatabase pDb : connPool.getConnectionList()) {
+                        dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
+                                + lastnode, 0, 0, 0, pDb.getCacheHitNum(),
+                                pDb.getCacheMissNum(), pDb.getCachesize()));
+                    }
                 }
+            } catch (SQLiteException e) {
+                // ignore. we don't care about exceptions when we are taking adb
+                // bugreport!
             }
         }
         return dbStatsList;
     }
 
     /**
-     * get the specified pragma value from sqlite for the specified database.
-     * only handles pragma's that return int/long.
-     * NO JAVA locks are held in this method.
-     * TODO: use this to do all pragma's in this class
+     * Returns list of full pathnames of all attached databases including the main database
+     * by executing 'pragma database_list' on the database.
+     *
+     * @return ArrayList of pairs of (database name, database file path) or null if the database
+     * is not open.
      */
-    private static long getPragmaVal(SQLiteDatabase db, String pragma) {
-        if (!db.isOpen()) {
-            return 0;
-        }
-        SQLiteStatement prog = null;
-        try {
-            prog = new SQLiteStatement(db, "PRAGMA " + pragma);
-            long val = prog.simpleQueryForLong();
-            return val;
-        } finally {
-            if (prog != null) prog.close();
-        }
-    }
-
-    /**
-     * returns list of full pathnames of all attached databases
-     * including the main database
-     * TODO: move this to {@link DatabaseUtils}
-     */
-    private static ArrayList<Pair<String, String>> getAttachedDbs(SQLiteDatabase dbObj) {
-        if (!dbObj.isOpen()) {
+    public ArrayList<Pair<String, String>> getAttachedDbs() {
+        if (!isOpen()) {
             return null;
         }
         ArrayList<Pair<String, String>> attachedDbs = new ArrayList<Pair<String, String>>();
-        Cursor c = dbObj.rawQuery("pragma database_list;", null);
-        while (c.moveToNext()) {
-             attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
+        if (!mHasAttachedDbs) {
+            // No attached databases.
+            // There is a small window where attached databases exist but this flag is not set yet.
+            // This can occur when this thread is in a race condition with another thread
+            // that is executing the SQL statement: "attach database <blah> as <foo>"
+            // If this thread is NOT ok with such a race condition (and thus possibly not receive
+            // the entire list of attached databases), then the caller should ensure that no thread
+            // is executing any SQL statements while a thread is calling this method.
+            // Typically, this method is called when 'adb bugreport' is done or the caller wants to
+            // collect stats on the database and all its attached databases.
+            attachedDbs.add(new Pair<String, String>("main", mPath));
+            return attachedDbs;
         }
-        c.close();
+        // has attached databases. query sqlite to get the list of attached databases.
+        Cursor c = null;
+        try {
+            c = rawQuery("pragma database_list;", null);
+            while (c.moveToNext()) {
+                // sqlite returns a row for each database in the returned list of databases.
+                //   in each row,
+                //       1st column is the database name such as main, or the database
+                //                              name specified on the "ATTACH" command
+                //       2nd column is the database file path.
+                attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
+            }
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
         return attachedDbs;
     }
 
     /**
+     * Runs 'pragma integrity_check' on the given database (and all the attached databases)
+     * and returns true if the given database (and all its attached databases) pass integrity_check,
+     * false otherwise.
+     *<p>
+     * If the result is false, then this method logs the errors reported by the integrity_check
+     * command execution.
+     *<p>
+     * Note that 'pragma integrity_check' on a database can take a long time.
+     *
+     * @return true if the given database (and all its attached databases) pass integrity_check,
+     * false otherwise.
+     */
+    public boolean isDatabaseIntegrityOk() {
+        verifyDbIsOpen();
+        ArrayList<Pair<String, String>> attachedDbs = null;
+        try {
+            attachedDbs = getAttachedDbs();
+            if (attachedDbs == null) {
+                throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
+                        "be retrieved. probably because the database is closed");
+            }
+        } catch (SQLiteException e) {
+            // can't get attachedDb list. do integrity check on the main database
+            attachedDbs = new ArrayList<Pair<String, String>>();
+            attachedDbs.add(new Pair<String, String>("main", this.mPath));
+        }
+        for (int i = 0; i < attachedDbs.size(); i++) {
+            Pair<String, String> p = attachedDbs.get(i);
+            SQLiteStatement prog = null;
+            try {
+                prog = compileStatement("PRAGMA " + p.first + ".integrity_check(1);");
+                String rslt = prog.simpleQueryForString();
+                if (!rslt.equalsIgnoreCase("ok")) {
+                    // integrity_checker failed on main or attached databases
+                    Log.e(TAG, "PRAGMA integrity_check on " + p.second + " returned: " + rslt);
+                    return false;
+                }
+            } finally {
+                if (prog != null) prog.close();
+            }
+        }
+        return true;
+    }
+
+    /**
      * Native call to open the database.
      *
      * @param path The full path to the database
@@ -2239,51 +2621,34 @@
     private native void dbopen(String path, int flags);
 
     /**
-     * Native call to setup tracing of all sql statements
+     * Native call to setup tracing of all SQL statements
      *
      * @param path the full path to the database
+     * @param connectionNum connection number: 0 - N, where the main database
+     *            connection handle is numbered 0 and the connection handles in the connection
+     *            pool are numbered 1..N.
      */
-    private native void enableSqlTracing(String path);
+    private native void enableSqlTracing(String path, short connectionNum);
 
     /**
-     * Native call to setup profiling of all sql statements.
+     * Native call to setup profiling of all SQL statements.
      * currently, sqlite's profiling = printing of execution-time
-     * (wall-clock time) of each of the sql statements, as they
+     * (wall-clock time) of each of the SQL statements, as they
      * are executed.
      *
      * @param path the full path to the database
+     * @param connectionNum connection number: 0 - N, where the main database
+     *            connection handle is numbered 0 and the connection handles in the connection
+     *            pool are numbered 1..N.
      */
-    private native void enableSqlProfiling(String path);
-
-    /**
-     * Native call to execute a raw SQL statement. {@link #lock} must be held
-     * when calling this method.
-     *
-     * @param sql The raw SQL string
-     * @throws SQLException
-     */
-    /* package */ native void native_execSQL(String sql) throws SQLException;
+    private native void enableSqlProfiling(String path, short connectionNum);
 
     /**
      * Native call to set the locale.  {@link #lock} must be held when calling
      * this method.
      * @throws SQLException
      */
-    /* package */ native void native_setLocale(String loc, int flags);
-
-    /**
-     * Returns the row ID of the last row inserted into the database.
-     *
-     * @return the row ID of the last row inserted into the database.
-     */
-    /* package */ native long lastInsertRow();
-
-    /**
-     * Returns the number of changes made in the last statement executed.
-     *
-     * @return the number of changes made in the last statement executed.
-     */
-    /* package */ native int lastChangeCount();
+    private native void native_setLocale(String loc, int flags);
 
     /**
      * return the SQLITE_DBSTATUS_LOOKASIDE_USED documented here
@@ -2291,4 +2656,11 @@
      * @return int value of SQLITE_DBSTATUS_LOOKASIDE_USED
      */
     private native int native_getDbLookaside();
+
+    /**
+     * finalizes the given statement id.
+     *
+     * @param statementId statement to be finzlied by sqlite
+     */
+    private final native void native_finalize(int statementId);
 }
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseLockedException.java b/core/java/android/database/sqlite/SQLiteDatabaseLockedException.java
new file mode 100644
index 0000000..f0e2d81
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteDatabaseLockedException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+/**
+ * Thrown if  the database engine was unable to acquire the
+ * database locks it needs to do its job.  If the statement is a [COMMIT]
+ * or occurs outside of an explicit transaction, then you can retry the
+ * statement.  If the statement is not a [COMMIT] and occurs within a
+ * explicit transaction then you should rollback the transaction before
+ * continuing.
+ */
+public class SQLiteDatabaseLockedException extends SQLiteException {
+    public SQLiteDatabaseLockedException() {}
+
+    public SQLiteDatabaseLockedException(String error) {
+        super(error);
+    }
+}
diff --git a/core/java/android/database/sqlite/SQLiteDatatypeMismatchException.java b/core/java/android/database/sqlite/SQLiteDatatypeMismatchException.java
new file mode 100644
index 0000000..7f82535
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteDatatypeMismatchException.java
@@ -0,0 +1,25 @@
+/*
+ * 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.sqlite;
+
+public class SQLiteDatatypeMismatchException extends SQLiteException {
+    public SQLiteDatatypeMismatchException() {}
+
+    public SQLiteDatatypeMismatchException(String error) {
+        super(error);
+    }
+}
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index b2a166b..94960791 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -132,11 +132,16 @@
         /** documented here http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */
         public int lookaside;
 
-        public DbStats(String dbName, long pageCount, long pageSize, int lookaside) {
+        /** statement cache stats: hits/misses/cachesize */
+        public String cache;
+
+        public DbStats(String dbName, long pageCount, long pageSize, int lookaside,
+            int hits, int misses, int cachesize) {
             this.dbName = dbName;
             this.pageSize = pageSize / 1024;
             dbSize = (pageCount * pageSize) / 1024;
             this.lookaside = lookaside;
+            this.cache = hits + "/" + misses + "/" + cachesize;
         }
     }
 
diff --git a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
index 2144fc3..de2fca9 100644
--- a/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
+++ b/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java
@@ -39,18 +39,16 @@
 
     public Cursor query(CursorFactory factory, String[] selectionArgs) {
         // Compile the query
-        SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);
+        SQLiteQuery query = null;
 
         try {
-            // Arg binding
-            int numArgs = selectionArgs == null ? 0 : selectionArgs.length;
-            for (int i = 0; i < numArgs; i++) {
-                query.bindString(i + 1, selectionArgs[i]);
-            }
+            mDatabase.lock();
+            mDatabase.closePendingStatements();
+            query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);
 
             // Create the cursor
             if (factory == null) {
-                mCursor = new SQLiteCursor(mDatabase, this, mEditTable, query);
+                mCursor = new SQLiteCursor(this, mEditTable, query);
             } else {
                 mCursor = factory.newCursor(mDatabase, this, mEditTable, query);
             }
@@ -61,6 +59,7 @@
         } finally {
             // Make sure this object is cleaned up if something happens
             if (query != null) query.close();
+            mDatabase.unlock();
         }
     }
 
@@ -69,10 +68,7 @@
     }
 
     public void setBindArguments(String[] bindArgs) {
-        final int numArgs = bindArgs.length;
-        for (int i = 0; i < numArgs; i++) {
-            mQuery.bindString(i + 1, bindArgs[i]);
-        }
+        mQuery.bindAllArgsAsStrings(bindArgs);
     }
 
     public void cursorDeactivated() {
diff --git a/core/java/android/database/sqlite/SQLiteMisuseException.java b/core/java/android/database/sqlite/SQLiteMisuseException.java
index 685f3ea..546ec08 100644
--- a/core/java/android/database/sqlite/SQLiteMisuseException.java
+++ b/core/java/android/database/sqlite/SQLiteMisuseException.java
@@ -16,6 +16,18 @@
 
 package android.database.sqlite;
 
+/**
+ * This error can occur if the application creates a SQLiteStatement object and allows multiple
+ * threads in the application use it at the same time.
+ * Sqlite returns this error if bind and execute methods on this object occur at the same time
+ * from multiple threads, like so:
+ *     thread # 1: in execute() method of the SQLiteStatement object
+ *     while thread # 2: is in bind..() on the same object.
+ *</p>
+ * FIX this by NEVER sharing the same SQLiteStatement object between threads.
+ * Create a local instance of the SQLiteStatement whenever it is needed, use it and close it ASAP.
+ * NEVER make it globally available.
+ */
 public class SQLiteMisuseException extends SQLiteException {
     public SQLiteMisuseException() {}
 
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index b4615eb..ccf8d68 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -17,6 +17,8 @@
 package android.database.sqlite;
 
 import android.content.Context;
+import android.database.DatabaseErrorHandler;
+import android.database.DefaultDatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.util.Log;
 
@@ -51,6 +53,7 @@
 
     private SQLiteDatabase mDatabase = null;
     private boolean mIsInitializing = false;
+    private final DatabaseErrorHandler mErrorHandler;
 
     /**
      * Create a helper object to create, open, and/or manage a database.
@@ -65,12 +68,37 @@
      *     {@link #onUpgrade} will be used to upgrade the database
      */
     public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
+        this(context, name, factory, version, new DefaultDatabaseErrorHandler());
+    }
+
+    /**
+     * Create a helper object to create, open, and/or manage a database.
+     * The database is not actually created or opened until one of
+     * {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
+     *
+     * <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
+     * used to handle corruption when sqlite reports database corruption.</p>
+     *
+     * @param context to use to open or create the database
+     * @param name of the database file, or null for an in-memory database
+     * @param factory to use for creating cursor objects, or null for the default
+     * @param version number of the database (starting at 1); if the database is older,
+     *     {@link #onUpgrade} will be used to upgrade the database
+     * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
+     * corruption.
+     */
+    public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
+            DatabaseErrorHandler errorHandler) {
         if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
+        if (errorHandler == null) {
+            throw new IllegalArgumentException("DatabaseErrorHandler param value can't be null.");
+        }
 
         mContext = context;
         mName = name;
         mFactory = factory;
         mNewVersion = version;
+        mErrorHandler = errorHandler;
     }
 
     /**
@@ -93,8 +121,13 @@
      * @return a read/write database object valid until {@link #close} is called
      */
     public synchronized SQLiteDatabase getWritableDatabase() {
-        if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
-            return mDatabase;  // The database is already open for business
+        if (mDatabase != null) {
+            if (!mDatabase.isOpen()) {
+                // darn! the user closed the database by calling mDatabase.close()
+                mDatabase = null;
+            } else if (!mDatabase.isReadOnly()) {
+                return mDatabase;  // The database is already open for business
+            }
         }
 
         if (mIsInitializing) {
@@ -115,10 +148,14 @@
             if (mName == null) {
                 db = SQLiteDatabase.create(null);
             } else {
-                db = mContext.openOrCreateDatabase(mName, 0, mFactory);
+                db = mContext.openOrCreateDatabase(mName, 0, mFactory, mErrorHandler);
             }
 
             int version = db.getVersion();
+            if (version > mNewVersion) {
+                throw new IllegalStateException("Database " + mName +
+                        " cannot be downgraded. instead, please uninstall new version first.");
+            }
             if (version != mNewVersion) {
                 db.beginTransaction();
                 try {
@@ -175,8 +212,13 @@
      *     or {@link #close} is called.
      */
     public synchronized SQLiteDatabase getReadableDatabase() {
-        if (mDatabase != null && mDatabase.isOpen()) {
-            return mDatabase;  // The database is already open for business
+        if (mDatabase != null) {
+            if (!mDatabase.isOpen()) {
+                // darn! the user closed the database by calling mDatabase.close()
+                mDatabase = null;
+            } else {
+                return mDatabase;  // The database is already open for business
+            }
         }
 
         if (mIsInitializing) {
@@ -194,7 +236,8 @@
         try {
             mIsInitializing = true;
             String path = mContext.getDatabasePath(mName).getPath();
-            db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);
+            db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY,
+                    mErrorHandler);
             if (db.getVersion() != mNewVersion) {
                 throw new SQLiteException("Can't upgrade read-only database from version " +
                         db.getVersion() + " to " + mNewVersion + ": " + path);
diff --git a/core/java/android/database/sqlite/SQLiteOutOfMemoryException.java b/core/java/android/database/sqlite/SQLiteOutOfMemoryException.java
new file mode 100644
index 0000000..98ce8b5
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteOutOfMemoryException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+public class SQLiteOutOfMemoryException extends SQLiteException {
+    public SQLiteOutOfMemoryException() {}
+
+    public SQLiteOutOfMemoryException(String error) {
+        super(error);
+    }
+}
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 4d96f12..83621f2 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -16,12 +16,16 @@
 
 package android.database.sqlite;
 
+import android.database.DatabaseUtils;
+import android.database.Cursor;
 import android.util.Log;
 
+import java.util.HashMap;
+
 /**
  * A base class for compiled SQLite programs.
- *
- * SQLiteProgram is not internally synchronized so code using a SQLiteProgram from multiple
+ *<p>
+ * SQLiteProgram is NOT internally synchronized so code using a SQLiteProgram from multiple
  * threads should perform its own synchronization when using the SQLiteProgram.
  */
 public abstract class SQLiteProgram extends SQLiteClosable {
@@ -43,12 +47,12 @@
      * @deprecated do not use this
      */
     @Deprecated
-    protected int nHandle = 0;
+    protected int nHandle;
 
     /**
      * the SQLiteCompiledSql object for the given sql statement.
      */
-    private SQLiteCompiledSql mCompiledSql;
+    /* package */ SQLiteCompiledSql mCompiledSql;
 
     /**
      * SQLiteCompiledSql statement id is populated with the corresponding object from the above
@@ -56,41 +60,97 @@
      * @deprecated do not use this
      */
     @Deprecated
-    protected int nStatement = 0;
+    protected int nStatement;
+
+    /**
+     * In the case of {@link SQLiteStatement}, this member stores the bindargs passed
+     * to the following methods, instead of actually doing the binding.
+     * <ul>
+     *   <li>{@link #bindBlob(int, byte[])}</li>
+     *   <li>{@link #bindDouble(int, double)}</li>
+     *   <li>{@link #bindLong(int, long)}</li>
+     *   <li>{@link #bindNull(int)}</li>
+     *   <li>{@link #bindString(int, String)}</li>
+     * </ul>
+     * <p>
+     * Each entry in the array is a Pair of
+     * <ol>
+     *   <li>bind arg position number</li>
+     *   <li>the value to be bound to the bindarg</li>
+     * </ol>
+     * <p>
+     * It is lazily initialized in the above bind methods
+     * and it is cleared in {@link #clearBindings()} method.
+     * <p>
+     * It is protected (in multi-threaded environment) by {@link SQLiteProgram}.this
+     */
+    /* package */ HashMap<Integer, Object> mBindArgs = null;
+    /* package */ final int mStatementType;
+    /* package */ static final int STATEMENT_CACHEABLE = 16;
+    /* package */ static final int STATEMENT_DONT_PREPARE = 32;
+    /* package */ static final int STATEMENT_USE_POOLED_CONN = 64;
+    /* package */ static final int STATEMENT_TYPE_MASK = 0x0f;
 
     /* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
-        mDatabase = db;
+        this(db, sql, null, true);
+    }
+
+    /* package */ SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
+            boolean compileFlag) {
         mSql = sql.trim();
+        int n = DatabaseUtils.getSqlStatementType(mSql);
+        switch (n) {
+            case DatabaseUtils.STATEMENT_UPDATE:
+                mStatementType = n | STATEMENT_CACHEABLE;
+                break;
+            case DatabaseUtils.STATEMENT_SELECT:
+                mStatementType = n | STATEMENT_CACHEABLE | STATEMENT_USE_POOLED_CONN;
+                break;
+            case DatabaseUtils.STATEMENT_ATTACH:
+            case DatabaseUtils.STATEMENT_BEGIN:
+            case DatabaseUtils.STATEMENT_COMMIT:
+            case DatabaseUtils.STATEMENT_ABORT:
+            case DatabaseUtils.STATEMENT_DDL:
+            case DatabaseUtils.STATEMENT_UNPREPARED:
+                mStatementType = n | STATEMENT_DONT_PREPARE;
+                break;
+            default:
+                mStatementType = n;
+        }
         db.acquireReference();
         db.addSQLiteClosable(this);
-        this.nHandle = db.mNativeHandle;
+        mDatabase = db;
+        nHandle = db.mNativeHandle;
+        if (bindArgs != null) {
+            int size = bindArgs.length;
+            for (int i = 0; i < size; i++) {
+                this.addToBindArgs(i + 1, bindArgs[i]);
+            }
+        }
+        if (compileFlag) {
+            compileAndbindAllArgs();
+        }
+    }
 
+    private void compileSql() {
         // only cache CRUD statements
-        String prefixSql = mSql.substring(0, 6);
-        if (!prefixSql.equalsIgnoreCase("INSERT") && !prefixSql.equalsIgnoreCase("UPDATE") &&
-                !prefixSql.equalsIgnoreCase("REPLAC") &&
-                !prefixSql.equalsIgnoreCase("DELETE") && !prefixSql.equalsIgnoreCase("SELECT")) {
-            mCompiledSql = new SQLiteCompiledSql(db, sql);
+        if ((mStatementType & STATEMENT_CACHEABLE) == 0) {
+            mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
             nStatement = mCompiledSql.nStatement;
             // since it is not in the cache, no need to acquire() it.
             return;
         }
 
-        // it is not pragma
-        mCompiledSql = db.getCompiledStatementForSql(sql);
+        mCompiledSql = mDatabase.getCompiledStatementForSql(mSql);
         if (mCompiledSql == null) {
             // create a new compiled-sql obj
-            mCompiledSql = new SQLiteCompiledSql(db, sql);
+            mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
 
             // add it to the cache of compiled-sqls
             // but before adding it and thus making it available for anyone else to use it,
             // make sure it is acquired by me.
             mCompiledSql.acquire();
-            db.addToCompiledQueries(sql, mCompiledSql);
-            if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
-                Log.v(TAG, "Created DbObj (id#" + mCompiledSql.nStatement +
-                        ") for sql: " + sql);
-            }
+            mDatabase.addToCompiledQueries(mSql, mCompiledSql);
         } else {
             // it is already in compiled-sql cache.
             // try to acquire the object.
@@ -100,13 +160,7 @@
                 // we can't have two different SQLiteProgam objects can't share the same
                 // CompiledSql object. create a new one.
                 // finalize it when I am done with it in "this" object.
-                mCompiledSql = new SQLiteCompiledSql(db, sql);
-                if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
-                    Log.v(TAG, "** possible bug ** Created NEW DbObj (id#" +
-                            mCompiledSql.nStatement +
-                            ") because the previously created DbObj (id#" + last +
-                            ") was not released for sql:" + sql);
-                }
+                mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
                 // since it is not in the cache, no need to acquire() it.
             }
         }
@@ -115,42 +169,45 @@
 
     @Override
     protected void onAllReferencesReleased() {
-        releaseCompiledSqlIfNotInCache();
-        mDatabase.releaseReference();
+        release();
         mDatabase.removeSQLiteClosable(this);
+        mDatabase.releaseReference();
     }
 
     @Override
     protected void onAllReferencesReleasedFromContainer() {
-        releaseCompiledSqlIfNotInCache();
+        release();
         mDatabase.releaseReference();
     }
 
-    private void releaseCompiledSqlIfNotInCache() {
+    /* package */ void release() {
         if (mCompiledSql == null) {
             return;
         }
-        synchronized(mDatabase.mCompiledQueries) {
-            if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) {
-                // it is NOT in compiled-sql cache. i.e., responsibility of
-                // releasing this statement is on me.
-                mCompiledSql.releaseSqlStatement();
-                mCompiledSql = null;
-                nStatement = 0;
-            } else {
-                // it is in compiled-sql cache. reset its CompiledSql#mInUse flag
-                mCompiledSql.release();
-            }
-        } 
+        mDatabase.releaseCompiledSqlObj(mCompiledSql);
+        mCompiledSql = null;
+        nStatement = 0;
     }
 
     /**
      * Returns a unique identifier for this program.
      *
      * @return a unique identifier for this program
+     * @deprecated do not use this method. it is not guaranteed to be the same across executions of
+     * the SQL statement contained in this object.
      */
+    @Deprecated
     public final int getUniqueId() {
-        return nStatement;
+      return -1;
+    }
+
+    /**
+     * used only for testing purposes
+     */
+    /* package */ int getSqlStatementId() {
+      synchronized(this) {
+        return (mCompiledSql == null) ? 0 : nStatement;
+      }
     }
 
     /* package */ String getSqlString() {
@@ -169,6 +226,37 @@
         // TODO is there a need for this?
     }
 
+    private void bind(int type, int index, Object value) {
+        mDatabase.verifyDbIsOpen();
+        addToBindArgs(index, (type == Cursor.FIELD_TYPE_NULL) ? null : value);
+        if (nStatement > 0) {
+            // bind only if the SQL statement is compiled
+            acquireReference();
+            try {
+                switch (type) {
+                    case Cursor.FIELD_TYPE_NULL:
+                        native_bind_null(index);
+                        break;
+                    case Cursor.FIELD_TYPE_BLOB:
+                        native_bind_blob(index, (byte[]) value);
+                        break;
+                    case Cursor.FIELD_TYPE_FLOAT:
+                        native_bind_double(index, (Double) value);
+                        break;
+                    case Cursor.FIELD_TYPE_INTEGER:
+                        native_bind_long(index, (Long) value);
+                        break;
+                    case Cursor.FIELD_TYPE_STRING:
+                    default:
+                        native_bind_string(index, (String) value);
+                        break;
+                }
+            } finally {
+                releaseReference();
+            }
+        }
+    }
+
     /**
      * Bind a NULL value to this statement. The value remains bound until
      * {@link #clearBindings} is called.
@@ -176,34 +264,18 @@
      * @param index The 1-based index to the parameter to bind null to
      */
     public void bindNull(int index) {
-        if (!mDatabase.isOpen()) {
-            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
-        }
-        acquireReference();
-        try {
-            native_bind_null(index);
-        } finally {
-            releaseReference();
-        }
+        bind(Cursor.FIELD_TYPE_NULL, index, null);
     }
 
     /**
      * Bind a long value to this statement. The value remains bound until
      * {@link #clearBindings} is called.
-     *
+     *addToBindArgs
      * @param index The 1-based index to the parameter to bind
      * @param value The value to bind
      */
     public void bindLong(int index, long value) {
-        if (!mDatabase.isOpen()) {
-            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
-        }
-        acquireReference();
-        try {
-            native_bind_long(index, value);
-        } finally {
-            releaseReference();
-        }
+        bind(Cursor.FIELD_TYPE_INTEGER, index, value);
     }
 
     /**
@@ -214,15 +286,7 @@
      * @param value The value to bind
      */
     public void bindDouble(int index, double value) {
-        if (!mDatabase.isOpen()) {
-            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
-        }
-        acquireReference();
-        try {
-            native_bind_double(index, value);
-        } finally {
-            releaseReference();
-        }
+        bind(Cursor.FIELD_TYPE_FLOAT, index, value);
     }
 
     /**
@@ -236,15 +300,7 @@
         if (value == null) {
             throw new IllegalArgumentException("the bind value at index " + index + " is null");
         }
-        if (!mDatabase.isOpen()) {
-            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
-        }
-        acquireReference();
-        try {
-            native_bind_string(index, value);
-        } finally {
-            releaseReference();
-        }
+        bind(Cursor.FIELD_TYPE_STRING, index, value);
     }
 
     /**
@@ -258,24 +314,18 @@
         if (value == null) {
             throw new IllegalArgumentException("the bind value at index " + index + " is null");
         }
-        if (!mDatabase.isOpen()) {
-            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
-        }
-        acquireReference();
-        try {
-            native_bind_blob(index, value);
-        } finally {
-            releaseReference();
-        }
+        bind(Cursor.FIELD_TYPE_BLOB, index, value);
     }
 
     /**
      * Clears all existing bindings. Unset bindings are treated as NULL.
      */
     public void clearBindings() {
-        if (!mDatabase.isOpen()) {
-            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
+        mBindArgs = null;
+        if (this.nStatement == 0) {
+            return;
         }
+        mDatabase.verifyDbIsOpen();
         acquireReference();
         try {
             native_clear_bindings();
@@ -288,15 +338,79 @@
      * Release this program's resources, making it invalid.
      */
     public void close() {
-        if (!mDatabase.isOpen()) {
+        mBindArgs = null;
+        if (nHandle == 0 || !mDatabase.isOpen()) {
             return;
         }
-        mDatabase.lock();
-        try {
-            releaseReference();
-        } finally {
-            mDatabase.unlock();
+        releaseReference();
+    }
+
+    private void addToBindArgs(int index, Object value) {
+        if (mBindArgs == null) {
+            mBindArgs = new HashMap<Integer, Object>();
         }
+        mBindArgs.put(index, value);
+    }
+
+    /* package */ void compileAndbindAllArgs() {
+        if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
+            // no need to prepare this SQL statement
+            if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
+                if (mBindArgs != null) {
+                    throw new IllegalArgumentException("no need to pass bindargs for this sql :" +
+                            mSql);
+                }
+            }
+            return;
+        }
+        if (nStatement == 0) {
+            // SQL statement is not compiled yet. compile it now.
+            compileSql();
+        }
+        if (mBindArgs == null) {
+            return;
+        }
+        for (int index : mBindArgs.keySet()) {
+            Object value = mBindArgs.get(index);
+            if (value == null) {
+                native_bind_null(index);
+            } else if (value instanceof Double || value instanceof Float) {
+                native_bind_double(index, ((Number) value).doubleValue());
+            } else if (value instanceof Number) {
+                native_bind_long(index, ((Number) value).longValue());
+            } else if (value instanceof Boolean) {
+                Boolean bool = (Boolean)value;
+                native_bind_long(index, (bool) ? 1 : 0);
+                if (bool) {
+                    native_bind_long(index, 1);
+                } else {
+                    native_bind_long(index, 0);
+                }
+            } else if (value instanceof byte[]){
+                native_bind_blob(index, (byte[]) value);
+            } else {
+                native_bind_string(index, value.toString());
+            }
+        }
+    }
+
+    /**
+     * Given an array of String bindArgs, this method binds all of them in one single call.
+     *
+     * @param bindArgs the String array of bind args.
+     */
+    public void bindAllArgsAsStrings(String[] bindArgs) {
+        if (bindArgs == null) {
+            return;
+        }
+        int size = bindArgs.length;
+        for (int i = 0; i < size; i++) {
+            bindString(i + 1, bindArgs[i]);
+        }
+    }
+
+    /* package */ synchronized final void setNativeHandle(int nHandle) {
+        this.nHandle = nHandle;
     }
 
     /**
diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java
index 905b66b..e9e0172 100644
--- a/core/java/android/database/sqlite/SQLiteQuery.java
+++ b/core/java/android/database/sqlite/SQLiteQuery.java
@@ -28,28 +28,38 @@
  * threads should perform its own synchronization when using the SQLiteQuery.
  */
 public class SQLiteQuery extends SQLiteProgram {
-    private static final String TAG = "Cursor";
+    private static final String TAG = "SQLiteQuery";
 
     /** The index of the unbound OFFSET parameter */
-    private int mOffsetIndex;
-    
-    /** Args to bind on requery */
-    private String[] mBindArgs;
+    private int mOffsetIndex = 0;
 
     private boolean mClosed = false;
 
     /**
      * Create a persistent query object.
-     * 
+     *
      * @param db The database that this query object is associated with
      * @param query The SQL string for this query. 
      * @param offsetIndex The 1-based index to the OFFSET parameter, 
      */
     /* package */ SQLiteQuery(SQLiteDatabase db, String query, int offsetIndex, String[] bindArgs) {
         super(db, query);
-
         mOffsetIndex = offsetIndex;
-        mBindArgs = bindArgs;
+        bindAllArgsAsStrings(bindArgs);
+    }
+
+    /**
+     * Constructor used to create new instance to replace a given instance of this class.
+     * This constructor is used when the current Query object is now associated with a different
+     * {@link SQLiteDatabase} object.
+     *
+     * @param db The database that this query object is associated with
+     * @param query the instance of {@link SQLiteQuery} to be replaced
+     */
+    /* package */ SQLiteQuery(SQLiteDatabase db, SQLiteQuery query) {
+        super(db, query.mSql);
+        this.mBindArgs = query.mBindArgs;
+        this.mOffsetIndex = query.mOffsetIndex;
     }
 
     /**
@@ -70,13 +80,8 @@
                 // if the start pos is not equal to 0, then most likely window is
                 // too small for the data set, loading by another thread
                 // is not safe in this situation. the native code will ignore maxRead
-                int numRows = native_fill_window(window, window.getStartPosition(), mOffsetIndex,
-                        maxRead, lastPos);
-
-                // Logging
-                if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
-                    Log.d(TAG, "fillWindow(): " + mSql);
-                }
+                int numRows = native_fill_window(window, window.getStartPosition(),
+                        mOffsetIndex, maxRead, lastPos);
                 mDatabase.logTimeStat(mSql, timeStart);
                 return numRows;
             } catch (IllegalStateException e){
@@ -85,6 +90,9 @@
             } catch (SQLiteDatabaseCorruptException e) {
                 mDatabase.onCorruption();
                 throw e;
+            } catch (SQLiteException e) {
+                Log.e(TAG, "exception: " + e.getMessage() + "; query: " + mSql);
+                throw e;
             } finally {
                 window.releaseReference();
             }
@@ -141,51 +149,13 @@
      * Called by SQLiteCursor when it is requeried.
      */
     /* package */ void requery() {
-        if (mBindArgs != null) {
-            int len = mBindArgs.length;
-            try {
-                for (int i = 0; i < len; i++) {
-                    super.bindString(i + 1, mBindArgs[i]);
-                }
-            } catch (SQLiteMisuseException e) {
-                StringBuilder errMsg = new StringBuilder("mSql " + mSql);
-                for (int i = 0; i < len; i++) {
-                    errMsg.append(" ");
-                    errMsg.append(mBindArgs[i]);
-                }
-                errMsg.append(" ");
-                IllegalStateException leakProgram = new IllegalStateException(
-                        errMsg.toString(), e);
-                throw leakProgram;                
-            }
+        if (mClosed) {
+            throw new IllegalStateException("requerying a closed cursor");
         }
+        compileAndbindAllArgs();
     }
 
-    @Override
-    public void bindNull(int index) {
-        mBindArgs[index - 1] = null;
-        if (!mClosed) super.bindNull(index);
-    }
-
-    @Override
-    public void bindLong(int index, long value) {
-        mBindArgs[index - 1] = Long.toString(value);
-        if (!mClosed) super.bindLong(index, value);
-    }
-
-    @Override
-    public void bindDouble(int index, double value) {
-        mBindArgs[index - 1] = Double.toString(value);
-        if (!mClosed) super.bindDouble(index, value);
-    }
-
-    @Override
-    public void bindString(int index, String value) {
-        mBindArgs[index - 1] = value;
-        if (!mClosed) super.bindString(index, value);
-    }
-
-    private final native int native_fill_window(CursorWindow window, 
+    private final native int native_fill_window(CursorWindow window,
             int startPos, int offsetParam, int maxRead, int lastPos);
 
     private final native int native_column_count();
diff --git a/core/java/android/database/sqlite/SQLiteReadOnlyDatabaseException.java b/core/java/android/database/sqlite/SQLiteReadOnlyDatabaseException.java
new file mode 100644
index 0000000..5b633c62
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteReadOnlyDatabaseException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+public class SQLiteReadOnlyDatabaseException extends SQLiteException {
+    public SQLiteReadOnlyDatabaseException() {}
+
+    public SQLiteReadOnlyDatabaseException(String error) {
+        super(error);
+    }
+}
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 9e425c3..5e96928 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -16,7 +16,12 @@
 
 package android.database.sqlite;
 
+import android.database.DatabaseUtils;
+import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
+import android.util.Log;
+
+import java.io.IOException;
 
 import dalvik.system.BlockGuard;
 
@@ -25,44 +30,70 @@
  * The statement cannot return multiple rows, but 1x1 result sets are allowed.
  * Don't use SQLiteStatement constructor directly, please use
  * {@link SQLiteDatabase#compileStatement(String)}
- *
- * SQLiteStatement is not internally synchronized so code using a SQLiteStatement from multiple
+ *<p>
+ * SQLiteStatement is NOT internally synchronized so code using a SQLiteStatement from multiple
  * threads should perform its own synchronization when using the SQLiteStatement.
  */
+@SuppressWarnings("deprecation")
 public class SQLiteStatement extends SQLiteProgram
 {
+    private static final String TAG = "SQLiteStatement";
+
+    private static final boolean READ = true;
+    private static final boolean WRITE = false;
+
+    private SQLiteDatabase mOrigDb;
+    private int mState;
+    /** possible value for {@link #mState}. indicates that a transaction is started. */
+    private static final int TRANS_STARTED = 1;
+    /** possible value for {@link #mState}. indicates that a lock is acquired. */
+    private static final int LOCK_ACQUIRED = 2;
+
     /**
      * Don't use SQLiteStatement constructor directly, please use
      * {@link SQLiteDatabase#compileStatement(String)}
      * @param db
      * @param sql
      */
-    /* package */ SQLiteStatement(SQLiteDatabase db, String sql) {
-        super(db, sql);
+    /* package */ SQLiteStatement(SQLiteDatabase db, String sql, Object[] bindArgs) {
+        super(db, sql, bindArgs, false /* don't compile sql statement */);
     }
 
     /**
-     * Execute this SQL statement, if it is not a query. For example,
-     * CREATE TABLE, DELTE, INSERT, etc.
+     * Execute this SQL statement, if it is not a SELECT / INSERT / DELETE / UPDATE, for example
+     * CREATE / DROP table, view, trigger, index etc.
      *
      * @throws android.database.SQLException If the SQL string is invalid for
      *         some reason
      */
     public void execute() {
-        BlockGuard.getThreadPolicy().onWriteToDisk();
-        if (!mDatabase.isOpen()) {
-            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
-        }
-        long timeStart = SystemClock.uptimeMillis();
-        mDatabase.lock();
+        executeUpdateDelete();
+    }
 
-        acquireReference();
+    /**
+     * Execute this SQL statement, if the the number of rows affected by execution of this SQL
+     * statement is of any importance to the caller - for example, UPDATE / DELETE SQL statements.
+     *
+     * @return the number of rows affected by this SQL statement execution.
+     * @throws android.database.SQLException If the SQL string is invalid for
+     *         some reason
+     */
+    public int executeUpdateDelete() {
         try {
-            native_execute();
+            long timeStart = acquireAndLock(WRITE);
+            int numChanges = 0;
+            if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
+                // since the statement doesn't have to be prepared,
+                // call the following native method which will not prepare
+                // the query plan
+                native_executeSql(mSql);
+            } else {
+                numChanges = native_execute();
+            }
             mDatabase.logTimeStat(mSql, timeStart);
+            return numChanges;
         } finally {
-            releaseReference();
-            mDatabase.unlock();
+            releaseAndUnlock();
         }
     }
 
@@ -76,21 +107,13 @@
      *         some reason
      */
     public long executeInsert() {
-        BlockGuard.getThreadPolicy().onWriteToDisk();
-        if (!mDatabase.isOpen()) {
-            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
-        }
-        long timeStart = SystemClock.uptimeMillis();
-        mDatabase.lock();
-
-        acquireReference();
         try {
-            native_execute();
+            long timeStart = acquireAndLock(WRITE);
+            long lastInsertedRowId = native_executeInsert();
             mDatabase.logTimeStat(mSql, timeStart);
-            return (mDatabase.lastChangeCount() > 0) ? mDatabase.lastInsertRow() : -1;
+            return lastInsertedRowId;
         } finally {
-            releaseReference();
-            mDatabase.unlock();
+            releaseAndUnlock();
         }
     }
 
@@ -103,21 +126,13 @@
      * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
      */
     public long simpleQueryForLong() {
-        BlockGuard.getThreadPolicy().onReadFromDisk();
-        if (!mDatabase.isOpen()) {
-            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
-        }
-        long timeStart = SystemClock.uptimeMillis();
-        mDatabase.lock();
-
-        acquireReference();
         try {
+            long timeStart = acquireAndLock(READ);
             long retValue = native_1x1_long();
             mDatabase.logTimeStat(mSql, timeStart);
             return retValue;
         } finally {
-            releaseReference();
-            mDatabase.unlock();
+            releaseAndUnlock();
         }
     }
 
@@ -130,25 +145,136 @@
      * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
      */
     public String simpleQueryForString() {
-        BlockGuard.getThreadPolicy().onReadFromDisk();
-        if (!mDatabase.isOpen()) {
-            throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
-        }
-        long timeStart = SystemClock.uptimeMillis();
-        mDatabase.lock();
-
-        acquireReference();
         try {
+            long timeStart = acquireAndLock(READ);
             String retValue = native_1x1_string();
             mDatabase.logTimeStat(mSql, timeStart);
             return retValue;
         } finally {
-            releaseReference();
-            mDatabase.unlock();
+            releaseAndUnlock();
         }
     }
 
-    private final native void native_execute();
+    /**
+     * Executes a statement that returns a 1 by 1 table with a blob value.
+     *
+     * @return A read-only file descriptor for a copy of the blob value, or {@code null}
+     *         if the value is null or could not be read for some reason.
+     *
+     * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
+     */
+    public ParcelFileDescriptor simpleQueryForBlobFileDescriptor() {
+        try {
+            long timeStart = acquireAndLock(READ);
+            ParcelFileDescriptor retValue = native_1x1_blob_ashmem();
+            mDatabase.logTimeStat(mSql, timeStart);
+            return retValue;
+        } catch (IOException ex) {
+            Log.e(TAG, "simpleQueryForBlobFileDescriptor() failed", ex);
+            return null;
+        } finally {
+            releaseAndUnlock();
+        }
+    }
+
+    /**
+     * Called before every method in this class before executing a SQL statement,
+     * this method does the following:
+     * <ul>
+     *   <li>make sure the database is open</li>
+     *   <li>get a database connection from the connection pool,if possible</li>
+     *   <li>notifies {@link BlockGuard} of read/write</li>
+     *   <li>if the SQL statement is an update, start transaction if not already in one.
+     *   otherwise, get lock on the database</li>
+     *   <li>acquire reference on this object</li>
+     *   <li>and then return the current time _before_ the database lock was acquired</li>
+     * </ul>
+     * <p>
+     * This method removes the duplicate code from the other public
+     * methods in this class.
+     */
+    private long acquireAndLock(boolean rwFlag) {
+        mState = 0;
+        // use pooled database connection handles for SELECT SQL statements
+        mDatabase.verifyDbIsOpen();
+        SQLiteDatabase db = ((mStatementType & SQLiteProgram.STATEMENT_USE_POOLED_CONN) > 0)
+                ? mDatabase.getDbConnection(mSql) : mDatabase;
+        // use the database connection obtained above
+        mOrigDb = mDatabase;
+        mDatabase = db;
+        setNativeHandle(mDatabase.mNativeHandle);
+        if (rwFlag == WRITE) {
+            BlockGuard.getThreadPolicy().onWriteToDisk();
+        } else {
+            BlockGuard.getThreadPolicy().onReadFromDisk();
+        }
+
+        /*
+         * Special case handling of SQLiteDatabase.execSQL("BEGIN transaction").
+         * we know it is execSQL("BEGIN transaction") from the caller IF there is no lock held.
+         * beginTransaction() methods in SQLiteDatabase call lockForced() before
+         * calling execSQL("BEGIN transaction").
+         */
+        if ((mStatementType & SQLiteProgram.STATEMENT_TYPE_MASK) == DatabaseUtils.STATEMENT_BEGIN) {
+            if (!mDatabase.isDbLockedByCurrentThread()) {
+                // transaction is  NOT started by calling beginTransaction() methods in
+                // SQLiteDatabase
+                mDatabase.setTransactionUsingExecSqlFlag();
+            }
+        } else if ((mStatementType & SQLiteProgram.STATEMENT_TYPE_MASK) ==
+                DatabaseUtils.STATEMENT_UPDATE) {
+            // got update SQL statement. if there is NO pending transaction, start one
+            if (!mDatabase.inTransaction()) {
+                mDatabase.beginTransactionNonExclusive();
+                mState = TRANS_STARTED;
+            }
+        }
+        // do I have database lock? if not, grab it.
+        if (!mDatabase.isDbLockedByCurrentThread()) {
+            mDatabase.lock();
+            mState = LOCK_ACQUIRED;
+        }
+
+        acquireReference();
+        long startTime = SystemClock.uptimeMillis();
+        mDatabase.closePendingStatements();
+        compileAndbindAllArgs();
+        return startTime;
+    }
+
+    /**
+     * this method releases locks and references acquired in {@link #acquireAndLock(boolean)}
+     */
+    private void releaseAndUnlock() {
+        releaseReference();
+        if (mState == TRANS_STARTED) {
+            try {
+                mDatabase.setTransactionSuccessful();
+            } finally {
+                mDatabase.endTransaction();
+            }
+        } else if (mState == LOCK_ACQUIRED) {
+            mDatabase.unlock();
+        }
+        if ((mStatementType & SQLiteProgram.STATEMENT_TYPE_MASK) ==
+                DatabaseUtils.STATEMENT_COMMIT ||
+                (mStatementType & SQLiteProgram.STATEMENT_TYPE_MASK) ==
+                DatabaseUtils.STATEMENT_ABORT) {
+            mDatabase.resetTransactionUsingExecSqlFlag();
+        }
+        clearBindings();
+        // release the compiled sql statement so that the caller's SQLiteStatement no longer
+        // has a hard reference to a database object that may get deallocated at any point.
+        release();
+        // restore the database connection handle to the original value
+        mDatabase = mOrigDb;
+        setNativeHandle(mDatabase.mNativeHandle);
+    }
+
+    private final native int native_execute();
+    private final native long native_executeInsert();
     private final native long native_1x1_long();
     private final native String native_1x1_string();
+    private final native ParcelFileDescriptor native_1x1_blob_ashmem() throws IOException;
+    private final native void native_executeSql(String sql);
 }
diff --git a/core/java/android/database/sqlite/SQLiteTableLockedException.java b/core/java/android/database/sqlite/SQLiteTableLockedException.java
new file mode 100644
index 0000000..8278df0
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteTableLockedException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+public class SQLiteTableLockedException extends SQLiteException {
+    public SQLiteTableLockedException() {}
+
+    public SQLiteTableLockedException(String error) {
+        super(error);
+    }
+}
diff --git a/core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java b/core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java
new file mode 100644
index 0000000..bcf95e2
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteUnfinalizedObjectsException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+/**
+ * Thrown if the database can't be closed because of some un-closed
+ * Cursor or SQLiteStatement objects. Could happen when a thread is trying to close
+ * the database while another thread still hasn't closed a Cursor on that database.
+ * @hide
+ */
+public class SQLiteUnfinalizedObjectsException extends SQLiteException {
+    public SQLiteUnfinalizedObjectsException() {}
+
+    public SQLiteUnfinalizedObjectsException(String error) {
+        super(error);
+    }
+}
diff --git a/core/java/android/hardware/Usb.java b/core/java/android/hardware/Usb.java
index 57271d4..ebb8296 100644
--- a/core/java/android/hardware/Usb.java
+++ b/core/java/android/hardware/Usb.java
@@ -17,6 +17,10 @@
 
 package android.hardware;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
 /**
  * Class for accessing USB state information.
  * @hide
@@ -55,6 +59,24 @@
     public static final String ACTION_USB_STATE =
             "android.hardware.action.USB_STATE";
 
+   /**
+     * Broadcast Action:  A broadcast for USB camera attached event.
+     *
+     * This intent is sent when a USB device supporting PTP is attached to the host USB bus.
+     * The intent's data contains a Uri for the device in the MTP provider.
+     */
+    public static final String ACTION_USB_CAMERA_ATTACHED =
+            "android.hardware.action.USB_CAMERA_ATTACHED";
+
+   /**
+     * Broadcast Action:  A broadcast for USB camera detached event.
+     *
+     * This intent is sent when a USB device supporting PTP is detached from the host USB bus.
+     * The intent's data contains a Uri for the device in the MTP provider.
+     */
+    public static final String ACTION_USB_CAMERA_DETACHED =
+            "android.hardware.action.USB_CAMERA_DETACHED";
+
     /**
      * Boolean extra indicating whether USB is connected or disconnected.
      * Used in extras for the {@link #ACTION_USB_STATE} broadcast.
@@ -96,4 +118,30 @@
      * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast
      */
     public static final String USB_FUNCTION_DISABLED = "disabled";
+
+    private static File getFunctionEnableFile(String function) {
+        return new File("/sys/class/usb_composite/" + function + "/enable");
+    }
+
+    /**
+     * Returns true if the specified USB function is supported by the kernel.
+     * Note that a USB function maybe supported but disabled.
+     */
+    public static boolean isFunctionSupported(String function) {
+        return getFunctionEnableFile(function).exists();
+    }
+
+    /**
+     * Returns true if the specified USB function is currently enabled.
+     */
+    public static boolean isFunctionEnabled(String function) {
+        try {
+            FileInputStream stream = new FileInputStream(getFunctionEnableFile(function));
+            boolean enabled = (stream.read() == '1');
+            stream.close();
+            return enabled;
+        } catch (IOException e) {
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 3619653..27af013 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -177,6 +177,7 @@
      * Implement this to handle {@link android.os.Binder#dump Binder.dump()}
      * calls on your input method.
      */
+    @Override
     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
     }
 
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 44f30f7..01fc010 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -31,7 +31,6 @@
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.InputMethodSession;
-import android.view.inputmethod.EditorInfo;
 
 class IInputMethodSessionWrapper extends IInputMethodSession.Stub
         implements HandlerCaller.Callback {
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 35fd46f..17c9ee7 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -36,6 +36,7 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodSession;
+import android.view.inputmethod.InputMethodSubtype;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -64,10 +65,12 @@
     private static final int DO_REVOKE_SESSION = 50;
     private static final int DO_SHOW_SOFT_INPUT = 60;
     private static final int DO_HIDE_SOFT_INPUT = 70;
+    private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
    
     final WeakReference<AbstractInputMethodService> mTarget;
     final HandlerCaller mCaller;
     final WeakReference<InputMethod> mInputMethod;
+    final int mTargetSdkVersion;
     
     static class Notifier {
         boolean notified;
@@ -100,6 +103,7 @@
         mTarget = new WeakReference<AbstractInputMethodService>(context);
         mCaller = new HandlerCaller(context.getApplicationContext(), this);
         mInputMethod = new WeakReference<InputMethod>(inputMethod);
+        mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
     }
 
     public InputMethod getInternalInputMethod() {
@@ -149,7 +153,9 @@
                 IInputContext inputContext = (IInputContext)args.arg1;
                 InputConnection ic = inputContext != null
                         ? new InputConnectionWrapper(inputContext) : null;
-                inputMethod.startInput(ic, (EditorInfo)args.arg2);
+                EditorInfo info = (EditorInfo)args.arg2;
+                info.makeCompatible(mTargetSdkVersion);
+                inputMethod.startInput(ic, info);
                 return;
             }
             case DO_RESTART_INPUT: {
@@ -157,7 +163,9 @@
                 IInputContext inputContext = (IInputContext)args.arg1;
                 InputConnection ic = inputContext != null
                         ? new InputConnectionWrapper(inputContext) : null;
-                inputMethod.restartInput(ic, (EditorInfo)args.arg2);
+                EditorInfo info = (EditorInfo)args.arg2;
+                info.makeCompatible(mTargetSdkVersion);
+                inputMethod.restartInput(ic, info);
                 return;
             }
             case DO_CREATE_SESSION: {
@@ -178,6 +186,9 @@
             case DO_HIDE_SOFT_INPUT:
                 inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
                 return;
+            case DO_CHANGE_INPUTMETHOD_SUBTYPE:
+                inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj);
+                return;
         }
         Log.w(TAG, "Unhandled message code: " + msg.what);
     }
@@ -267,4 +278,9 @@
         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
                 flags, resultReceiver));
     }
+
+    public void changeInputMethodSubtype(InputMethodSubtype subtype) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE,
+                subtype));
+    }
 }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 1a261d3..8409baa 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -52,6 +52,7 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.EditorInfo;
 import android.widget.Button;
 import android.widget.FrameLayout;
@@ -270,7 +271,7 @@
 
     final Insets mTmpInsets = new Insets();
     final int[] mTmpLocation = new int[2];
-    
+
     final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
             new ViewTreeObserver.OnComputeInternalInsetsListener() {
         public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
@@ -394,8 +395,12 @@
                                 : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
             }
         }
+
+        public void changeInputMethodSubtype(InputMethodSubtype subtype) {
+            onCurrentInputMethodSubtypeChanged(subtype);
+        }
     }
-    
+
     /**
      * Concrete implementation of
      * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
@@ -541,6 +546,7 @@
      * will typically call it in your constructor with the resource ID
      * of your custom theme.
      */
+    @Override
     public void setTheme(int theme) {
         if (mWindow != null) {
             throw new IllegalStateException("Must be called before onCreate()");
@@ -558,7 +564,7 @@
         initViews();
         mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
     }
-    
+
     /**
      * This is a hook that subclasses can use to perform initialization of
      * their interface.  It is called for you prior to any of your UI objects
@@ -567,14 +573,14 @@
      */
     public void onInitializeInterface() {
     }
-    
+
     void initialize() {
         if (!mInitialized) {
             mInitialized = true;
             onInitializeInterface();
         }
     }
-    
+
     void initViews() {
         mInitialized = false;
         mWindowCreated = false;
@@ -610,7 +616,7 @@
         mCandidatesFrame.setVisibility(mCandidatesVisibility);
         mInputFrame.setVisibility(View.GONE);
     }
-    
+
     @Override public void onDestroy() {
         super.onDestroy();
         mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
@@ -677,6 +683,7 @@
      * Implement to return our standard {@link InputMethodImpl}.  Subclasses
      * can override to provide their own customized version.
      */
+    @Override
     public AbstractInputMethodImpl onCreateInputMethodInterface() {
         return new InputMethodImpl();
     }
@@ -685,6 +692,7 @@
      * Implement to return our standard {@link InputMethodSessionImpl}.  Subclasses
      * can override to provide their own customized version.
      */
+    @Override
     public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
         return new InputMethodSessionImpl();
     }
@@ -1288,7 +1296,7 @@
             mInShowWindow = false;
         }
     }
-    
+
     void showWindowInner(boolean showInput) {
         boolean doShowInput = false;
         boolean wasVisible = mWindowVisible;
@@ -1303,7 +1311,7 @@
         } else {
             showInput = true;
         }
-        
+
         if (DEBUG) Log.v(TAG, "showWindow: updating UI");
         initialize();
         updateFullscreenMode();
@@ -1335,14 +1343,15 @@
         if (doShowInput) {
             startExtractingText(false);
         }
-        
+
         if (!wasVisible) {
             if (DEBUG) Log.v(TAG, "showWindow: showing!");
+            mImm.setIMEButtonVisible(mToken, true);
             onWindowShown();
             mWindow.show();
         }
     }
-    
+
     public void hideWindow() {
         if (mInputViewStarted) {
             if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
@@ -1356,11 +1365,12 @@
         if (mWindowVisible) {
             mWindow.hide();
             mWindowVisible = false;
+            mImm.setIMEButtonVisible(mToken, false);
             onWindowHidden();
             mWindowWasVisible = false;
         }
     }
-    
+
     /**
      * Called when the input method window has been shown to the user, after
      * previously not being visible.  This is done after all of the UI setup
@@ -1656,6 +1666,7 @@
         return doMovementKey(keyCode, event, MOVEMENT_UP);
     }
 
+    @Override
     public boolean onTrackballEvent(MotionEvent event) {
         return false;
     }
@@ -1694,7 +1705,7 @@
                 dy = count;
                 break;
         }
-        onExtractedCursorMovement(dx, dy);       
+        onExtractedCursorMovement(dx, dy);
     }
     
     boolean doMovementKey(int keyCode, KeyEvent event, int count) {
@@ -1934,6 +1945,8 @@
                 return getText(com.android.internal.R.string.ime_action_next);
             case EditorInfo.IME_ACTION_DONE:
                 return getText(com.android.internal.R.string.ime_action_done);
+            case EditorInfo.IME_ACTION_PREVIOUS:
+                return getText(com.android.internal.R.string.ime_action_previous);
             default:
                 return getText(com.android.internal.R.string.ime_action_default);
         }
@@ -2064,7 +2077,24 @@
             }
         }
     }
-    
+
+    // TODO: Handle the subtype change event
+    /**
+     * Called when the subtype was changed.
+     * @param newSubtype the subtype which is being changed to.
+     */
+    protected void onCurrentInputMethodSubtypeChanged(InputMethodSubtype newSubtype) {
+        if (DEBUG) {
+            int nameResId = newSubtype.getNameResId();
+            String mode = newSubtype.getMode();
+            String output = "changeInputMethodSubtype:"
+                + (nameResId == 0 ? "<none>" : getString(nameResId)) + ","
+                + mode + ","
+                + newSubtype.getLocale() + "," + newSubtype.getExtraValue();
+            Log.v(TAG, "--- " + output);
+        }
+    }
+
     /**
      * Performs a dump of the InputMethodService's internal state.  Override
      * to add your own information to the dump.
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index 269e1c9..75c945b 100644
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -504,7 +504,30 @@
     public Keyboard(Context context, int xmlLayoutResId) {
         this(context, xmlLayoutResId, 0);
     }
-    
+
+    /**
+     * Creates a keyboard from the given xml key layout file. Weeds out rows
+     * that have a keyboard mode defined but don't match the specified mode.
+     * @param context the application or service context
+     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
+     * @param modeId keyboard mode identifier
+     * @param width sets width of keyboard
+     * @param height sets height of keyboard
+     */
+    public Keyboard(Context context, int xmlLayoutResId, int modeId, int width, int height) {
+        mDisplayWidth = width;
+        mDisplayHeight = height;
+
+        mDefaultHorizontalGap = 0;
+        mDefaultWidth = mDisplayWidth / 10;
+        mDefaultVerticalGap = 0;
+        mDefaultHeight = mDefaultWidth;
+        mKeys = new ArrayList<Key>();
+        mModifierKeys = new ArrayList<Key>();
+        mKeyboardMode = modeId;
+        loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
+    }
+
     /**
      * Creates a keyboard from the given xml key layout file. Weeds out rows
      * that have a keyboard mode defined but don't match the specified mode. 
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 4b48409..ab5c78a 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -1128,7 +1128,9 @@
 
     private boolean onModifiedTouchEvent(MotionEvent me, boolean possiblePoly) {
         int touchX = (int) me.getX() - mPaddingLeft;
-        int touchY = (int) me.getY() + mVerticalCorrection - mPaddingTop;
+        int touchY = (int) me.getY() - mPaddingTop;
+        if (touchY >= -mVerticalCorrection)
+            touchY += mVerticalCorrection;
         final int action = me.getAction();
         final long eventTime = me.getEventTime();
         mOldEventTime = eventTime;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1cfe419b7..dd9c8f0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -264,6 +264,24 @@
         }
     }
 
+    /** @hide */
+    public LinkProperties getActiveLinkProperties() {
+        try {
+            return mService.getActiveLinkProperties();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /** @hide */
+    public LinkProperties getLinkProperties(int networkType) {
+        try {
+            return mService.getLinkProperties(networkType);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
     /** {@hide} */
     public boolean setRadios(boolean turnOn) {
         try {
@@ -367,14 +385,14 @@
      * <p>
      * All applications that have background services that use the network
      * should listen to {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}.
-     * 
+     *
      * @return Whether background data usage is allowed.
      */
     public boolean getBackgroundDataSetting() {
         try {
             return mService.getBackgroundDataSetting();
         } catch (RemoteException e) {
-            // Err on the side of safety 
+            // Err on the side of safety
             return false;
         }
     }
@@ -532,6 +550,17 @@
         }
     }
 
+    /**
+     * {@hide}
+     */
+    public String[] getTetherableBluetoothRegexs() {
+        try {
+            return mService.getTetherableBluetoothRegexs();
+        } catch (RemoteException e) {
+            return new String[0];
+        }
+    }
+
     /** {@hide} */
     public static final int TETHER_ERROR_NO_ERROR           = 0;
     /** {@hide} */
@@ -570,6 +599,21 @@
     }
 
     /**
+     * Ensure the device stays awake until we connect with the next network
+     * @param forWhome The name of the network going down for logging purposes
+     * @return {@code true} on success, {@code false} on failure
+     * {@hide}
+     */
+    public boolean requestNetworkTransitionWakelock(String forWhom) {
+        try {
+            mService.requestNetworkTransitionWakelock(forWhom);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * @param networkType The type of network you want to report on
      * @param percentage The quality of the connection 0 is bad, 100 is good
      * {@hide}
diff --git a/core/java/android/net/DhcpInfo.java b/core/java/android/net/DhcpInfo.java
index 1178bec..9c81c19 100644
--- a/core/java/android/net/DhcpInfo.java
+++ b/core/java/android/net/DhcpInfo.java
@@ -37,6 +37,19 @@
         super();
     }
 
+    /** copy constructor {@hide} */
+    public DhcpInfo(DhcpInfo source) {
+        if (source != null) {
+            ipAddress = source.ipAddress;
+            gateway = source.gateway;
+            netmask = source.netmask;
+            dns1 = source.dns1;
+            dns2 = source.dns2;
+            serverAddress = source.serverAddress;
+            leaseDuration = source.leaseDuration;
+        }
+    }
+
     public String toString() {
         StringBuffer str = new StringBuffer();
 
diff --git a/core/java/android/net/Downloads.java b/core/java/android/net/Downloads.java
index fd33781..ddde5c1 100644
--- a/core/java/android/net/Downloads.java
+++ b/core/java/android/net/Downloads.java
@@ -430,11 +430,10 @@
 
             ContentResolver cr = context.getContentResolver();
 
-            Cursor c = cr.query(
-                    downloadUri, DOWNLOADS_PROJECTION, null /* selection */, null /* selection args */,
-                    null /* sort order */);
+            Cursor c = cr.query(downloadUri, DOWNLOADS_PROJECTION, null /* selection */,
+                    null /* selection args */, null /* sort order */);
             try {
-                if (!c.moveToNext()) {
+                if (c == null || !c.moveToNext()) {
                     return result;
                 }
 
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index afccdd9..35054d6 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -1,21 +1,22 @@
 /**
  * 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 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0 
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software 
- * distributed under the License is distributed on an "AS IS" BASIS, 
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
- * See the License for the specific language governing permissions and 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.LinkProperties;
 import android.net.NetworkInfo;
 import android.os.IBinder;
 
@@ -36,6 +37,10 @@
 
     NetworkInfo[] getAllNetworkInfo();
 
+    LinkProperties getActiveLinkProperties();
+
+    LinkProperties getLinkProperties(int networkType);
+
     boolean setRadios(boolean onOff);
 
     boolean setRadio(int networkType, boolean turnOn);
@@ -75,5 +80,9 @@
 
     String[] getTetherableWifiRegexs();
 
+    String[] getTetherableBluetoothRegexs();
+
+    void requestNetworkTransitionWakelock(in String forWhom);
+
     void reportInetCondition(int networkType, int percentage);
 }
diff --git a/core/java/android/net/LinkAddress.aidl b/core/java/android/net/LinkAddress.aidl
new file mode 100644
index 0000000..e7d8646
--- /dev/null
+++ b/core/java/android/net/LinkAddress.aidl
@@ -0,0 +1,21 @@
+/**
+ *
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+parcelable LinkAddress;
+
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
new file mode 100644
index 0000000..3f03a2a
--- /dev/null
+++ b/core/java/android/net/LinkAddress.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Identifies an address of a network link
+ * @hide
+ */
+public class LinkAddress implements Parcelable {
+    /**
+     * IPv4 or IPv6 address.
+     */
+    private final InetAddress address;
+
+    /**
+     * Network prefix length
+     */
+    private final int prefixLength;
+
+    public LinkAddress(InetAddress address, InetAddress mask) {
+        this.address = address;
+        this.prefixLength = computeprefixLength(mask);
+    }
+
+    public LinkAddress(InetAddress address, int prefixLength) {
+        this.address = address;
+        this.prefixLength = prefixLength;
+    }
+
+    public LinkAddress(InterfaceAddress interfaceAddress) {
+        this.address = interfaceAddress.getAddress();
+        this.prefixLength = interfaceAddress.getNetworkPrefixLength();
+    }
+
+    private static int computeprefixLength(InetAddress mask) {
+        int count = 0;
+        for (byte b : mask.getAddress()) {
+            for (int i = 0; i < 8; ++i) {
+                if ((b & (1 << i)) != 0) {
+                    ++count;
+                }
+            }
+        }
+        return count;
+    }
+
+    @Override
+    public String toString() {
+        return (address == null ? "" : (address.getHostAddress() + "/" + prefixLength));
+    }
+
+    /**
+     * Compares this {@code LinkAddress} instance against the specified address
+     * in {@code obj}. Two addresses are equal if their InetAddress and prefixLength
+     * are equal
+     *
+     * @param obj the object to be tested for equality.
+     * @return {@code true} if both objects are equal, {@code false} otherwise.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof LinkAddress)) {
+            return false;
+        }
+        LinkAddress linkAddress = (LinkAddress) obj;
+        return this.address.equals(linkAddress.address) &&
+            this.prefixLength == linkAddress.prefixLength;
+    }
+
+    /**
+     * Returns the InetAddress for this address.
+     */
+    public InetAddress getAddress() {
+        return address;
+    }
+
+    /**
+     * Get network prefix length
+     */
+    public int getNetworkPrefixLength() {
+        return prefixLength;
+    }
+
+    /**
+     * Implement the Parcelable interface
+     * @hide
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        if (address != null) {
+            dest.writeByte((byte)1);
+            dest.writeByteArray(address.getAddress());
+            dest.writeInt(prefixLength);
+        } else {
+            dest.writeByte((byte)0);
+        }
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public static final Creator<LinkAddress> CREATOR =
+        new Creator<LinkAddress>() {
+            public LinkAddress createFromParcel(Parcel in) {
+                InetAddress address = null;
+                int prefixLength = 0;
+                if (in.readByte() == 1) {
+                    try {
+                        address = InetAddress.getByAddress(in.createByteArray());
+                        prefixLength = in.readInt();
+                    } catch (UnknownHostException e) { }
+                }
+                return new LinkAddress(address, prefixLength);
+            }
+
+            public LinkAddress[] newArray(int size) {
+                return new LinkAddress[size];
+            }
+        };
+}
diff --git a/core/java/android/net/LinkCapabilities.aidl b/core/java/android/net/LinkCapabilities.aidl
new file mode 100644
index 0000000..df72599
--- /dev/null
+++ b/core/java/android/net/LinkCapabilities.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** Copyright (C) 2010 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.net;
+
+parcelable LinkCapabilities;
+
diff --git a/core/java/android/net/LinkCapabilities.java b/core/java/android/net/LinkCapabilities.java
new file mode 100644
index 0000000..eb9166f
--- /dev/null
+++ b/core/java/android/net/LinkCapabilities.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * A class representing the capabilities of a link
+ *
+ * @hide
+ */
+public class LinkCapabilities implements Parcelable {
+    private static final String TAG = "LinkCapabilities";
+    private static final boolean DBG = false;
+
+    /** The Map of Keys to Values */
+    private HashMap<Integer, String> mCapabilities;
+
+
+    /**
+     * The set of keys defined for a links capabilities.
+     *
+     * Keys starting with RW are read + write, i.e. the application
+     * can request for a certain requirement corresponding to that key.
+     * Keys starting with RO are read only, i.e. the the application
+     * can read the value of that key from the socket but cannot request
+     * a corresponding requirement.
+     *
+     * TODO: Provide a documentation technique for concisely and precisely
+     * define the syntax for each value string associated with a key.
+     */
+    public static final class Key {
+        /** No constructor */
+        private Key() {}
+
+        /**
+         * An integer representing the network type.
+         * @see ConnectivityManager
+         */
+        public final static int RO_NETWORK_TYPE = 1;
+
+        /**
+         * Desired minimum forward link (download) bandwidth for the
+         * in kilobits per second (kbps). Values should be strings such
+         * "50", "100", "1500", etc.
+         */
+        public final static int RW_DESIRED_FWD_BW = 2;
+
+        /**
+         * Required minimum forward link (download) bandwidth, in
+         * per second (kbps), below which the socket cannot function.
+         * Values should be strings such as "50", "100", "1500", etc.
+         */
+        public final static int RW_REQUIRED_FWD_BW = 3;
+
+        /**
+         * Available forward link (download) bandwidth for the socket.
+         * This value is in kilobits per second (kbps).
+         * Values will be strings such as "50", "100", "1500", etc.
+         */
+        public final static int RO_AVAILABLE_FWD_BW = 4;
+
+        /**
+         * Desired minimum reverse link (upload) bandwidth for the socket
+         * in kilobits per second (kbps).
+         * Values should be strings such as "50", "100", "1500", etc.
+         * <p>
+         * This key is set via the needs map.
+         */
+        public final static int RW_DESIRED_REV_BW = 5;
+
+        /**
+         * Required minimum reverse link (upload) bandwidth, in kilobits
+         * per second (kbps), below which the socket cannot function.
+         * If a rate is not specified, the default rate of kbps will be
+         * Values should be strings such as "50", "100", "1500", etc.
+         */
+        public final static int RW_REQUIRED_REV_BW = 6;
+
+        /**
+         * Available reverse link (upload) bandwidth for the socket.
+         * This value is in kilobits per second (kbps).
+         * Values will be strings such as "50", "100", "1500", etc.
+         */
+        public final static int RO_AVAILABLE_REV_BW = 7;
+
+        /**
+         * Maximum latency for the socket, in milliseconds, above which
+         * socket cannot function.
+         * Values should be strings such as "50", "300", "500", etc.
+         */
+        public final static int RW_MAX_ALLOWED_LATENCY = 8;
+
+        /**
+         * Interface that the socket is bound to. This can be a virtual
+         * interface (e.g. VPN or Mobile IP) or a physical interface
+         * (e.g. wlan0 or rmnet0).
+         * Values will be strings such as "wlan0", "rmnet0"
+         */
+        public final static int RO_BOUND_INTERFACE = 9;
+
+        /**
+         * Physical interface that the socket is routed on.
+         * This can be different from BOUND_INTERFACE in cases such as
+         * VPN or Mobile IP. The physical interface may change over time
+         * if seamless mobility is supported.
+         * Values will be strings such as "wlan0", "rmnet0"
+         */
+        public final static int RO_PHYSICAL_INTERFACE = 10;
+    }
+
+    /**
+     * Role informs the LinkSocket about the data usage patterns of your
+     * application.
+     * <P>
+     * {@code Role.DEFAULT} is the default role, and is used whenever
+     * a role isn't set.
+     */
+    public static final class Role {
+        /** No constructor */
+        private Role() {}
+
+        // examples only, discuss which roles should be defined, and then
+        // code these to match
+
+        /** Default Role */
+        public static final String DEFAULT = "default";
+        /** Bulk down load */
+        public static final String BULK_DOWNLOAD = "bulk.download";
+        /** Bulk upload */
+        public static final String BULK_UPLOAD = "bulk.upload";
+
+        /** VoIP Application at 24kbps */
+        public static final String VOIP_24KBPS = "voip.24k";
+        /** VoIP Application at 32kbps */
+        public static final String VOIP_32KBPS = "voip.32k";
+
+        /** Video Streaming at 480p */
+        public static final String VIDEO_STREAMING_480P = "video.streaming.480p";
+        /** Video Streaming at 720p */
+        public static final String VIDEO_STREAMING_720I = "video.streaming.720i";
+
+        /** Video Chat Application at 360p */
+        public static final String VIDEO_CHAT_360P = "video.chat.360p";
+        /** Video Chat Application at 480p */
+        public static final String VIDEO_CHAT_480P = "video.chat.480i";
+    }
+
+    /**
+     * Constructor
+     */
+    public LinkCapabilities() {
+        mCapabilities = new HashMap<Integer, String>();
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @param source
+     */
+    public LinkCapabilities(LinkCapabilities source) {
+        if (source != null) {
+            mCapabilities = new HashMap<Integer, String>(source.mCapabilities);
+        } else {
+            mCapabilities = new HashMap<Integer, String>();
+        }
+    }
+
+    /**
+     * Create the {@code LinkCapabilities} with values depending on role type.
+     * @param applicationRole a {@code LinkSocket.Role}
+     * @return the {@code LinkCapabilities} associated with the applicationRole, empty if none
+     */
+    public static LinkCapabilities createNeedsMap(String applicationRole) {
+        if (DBG) log("createNeededCapabilities(applicationRole) EX");
+        return new LinkCapabilities();
+    }
+
+    /**
+     * Remove all capabilities
+     */
+    public void clear() {
+        mCapabilities.clear();
+    }
+
+    /**
+     * Returns whether this map is empty.
+     */
+    public boolean isEmpty() {
+        return mCapabilities.isEmpty();
+    }
+
+    /**
+     * Returns the number of elements in this map.
+     *
+     * @return the number of elements in this map.
+     */
+    public int size() {
+        return mCapabilities.size();
+    }
+
+    /**
+     * Given the key return the capability string
+     *
+     * @param key
+     * @return the capability string
+     */
+    public String get(int key) {
+        return mCapabilities.get(key);
+    }
+
+    /**
+     * Store the key/value capability pair
+     *
+     * @param key
+     * @param value
+     */
+    public void put(int key, String value) {
+        mCapabilities.put(key, value);
+    }
+
+    /**
+     * Returns whether this map contains the specified key.
+     *
+     * @param key to search for.
+     * @return {@code true} if this map contains the specified key,
+     *         {@code false} otherwise.
+     */
+    public boolean containsKey(int key) {
+        return mCapabilities.containsKey(key);
+    }
+
+    /**
+     * Returns whether this map contains the specified value.
+     *
+     * @param value to search for.
+     * @return {@code true} if this map contains the specified value,
+     *         {@code false} otherwise.
+     */
+    public boolean containsValue(String value) {
+        return mCapabilities.containsValue(value);
+    }
+
+    /**
+     * Returns a set containing all of the mappings in this map. Each mapping is
+     * an instance of {@link Map.Entry}. As the set is backed by this map,
+     * changes in one will be reflected in the other.
+     *
+     * @return a set of the mappings.
+     */
+    public Set<Entry<Integer, String>> entrySet() {
+        return mCapabilities.entrySet();
+    }
+
+    /**
+     * @return the set of the keys.
+     */
+    public Set<Integer> keySet() {
+        return mCapabilities.keySet();
+    }
+
+    /**
+     * @return the set of values
+     */
+    public Collection<String> values() {
+        return mCapabilities.values();
+    }
+
+    /**
+     * Implement the Parcelable interface
+     * @hide
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Convert to string for debugging
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("{");
+        boolean firstTime = true;
+        for (Entry<Integer, String> entry : mCapabilities.entrySet()) {
+            if (firstTime) {
+                firstTime = false;
+            } else {
+                sb.append(",");
+            }
+            sb.append(entry.getKey());
+            sb.append(":\"");
+            sb.append(entry.getValue());
+            sb.append("\"");
+            return mCapabilities.toString();
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCapabilities.size());
+        for (Entry<Integer, String> entry : mCapabilities.entrySet()) {
+            dest.writeInt(entry.getKey().intValue());
+            dest.writeString(entry.getValue());
+        }
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public static final Creator<LinkCapabilities> CREATOR =
+        new Creator<LinkCapabilities>() {
+            public LinkCapabilities createFromParcel(Parcel in) {
+                LinkCapabilities capabilities = new LinkCapabilities();
+                int size = in.readInt();
+                while (size-- != 0) {
+                    int key = in.readInt();
+                    String value = in.readString();
+                    capabilities.mCapabilities.put(key, value);
+                }
+                return capabilities;
+            }
+
+            public LinkCapabilities[] newArray(int size) {
+                return new LinkCapabilities[size];
+            }
+        };
+
+    /**
+     * Debug logging
+     */
+    protected static void log(String s) {
+        Log.d(TAG, s);
+    }
+}
diff --git a/core/java/android/net/LinkProperties.aidl b/core/java/android/net/LinkProperties.aidl
new file mode 100644
index 0000000..9cd06d5
--- /dev/null
+++ b/core/java/android/net/LinkProperties.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** Copyright (C) 2010 The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.net;
+
+parcelable LinkProperties;
+
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
new file mode 100644
index 0000000..f1545ea
--- /dev/null
+++ b/core/java/android/net/LinkProperties.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.ProxyProperties;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Describes the properties of a network link.
+ * TODO - consider adding optional fields like Apn and ApnType
+ * @hide
+ */
+public class LinkProperties implements Parcelable {
+
+    String mIfaceName;
+    private Collection<LinkAddress> mLinkAddresses;
+    private Collection<InetAddress> mDnses;
+    private InetAddress mGateway;
+    private ProxyProperties mHttpProxy;
+
+    public LinkProperties() {
+        clear();
+    }
+
+    // copy constructor instead of clone
+    public LinkProperties(LinkProperties source) {
+        if (source != null) {
+            mIfaceName = source.getInterfaceName();
+            mLinkAddresses = source.getLinkAddresses();
+            mDnses = source.getDnses();
+            mGateway = source.getGateway();
+            mHttpProxy = new ProxyProperties(source.getHttpProxy());
+        }
+    }
+
+    public void setInterfaceName(String iface) {
+        mIfaceName = iface;
+    }
+
+    public String getInterfaceName() {
+        return mIfaceName;
+    }
+
+    public Collection<InetAddress> getAddresses() {
+        Collection<InetAddress> addresses = new ArrayList<InetAddress>();
+        for (LinkAddress linkAddress : mLinkAddresses) {
+            addresses.add(linkAddress.getAddress());
+        }
+        return Collections.unmodifiableCollection(addresses);
+    }
+
+    public void addLinkAddress(LinkAddress address) {
+        mLinkAddresses.add(address);
+    }
+
+    public Collection<LinkAddress> getLinkAddresses() {
+        return Collections.unmodifiableCollection(mLinkAddresses);
+    }
+
+    public void addDns(InetAddress dns) {
+        mDnses.add(dns);
+    }
+
+    public Collection<InetAddress> getDnses() {
+        return Collections.unmodifiableCollection(mDnses);
+    }
+
+    public void setGateway(InetAddress gateway) {
+        mGateway = gateway;
+    }
+    public InetAddress getGateway() {
+        return mGateway;
+    }
+
+    public void setHttpProxy(ProxyProperties proxy) {
+        mHttpProxy = proxy;
+    }
+    public ProxyProperties getHttpProxy() {
+        return mHttpProxy;
+    }
+
+    public void clear() {
+        mIfaceName = null;
+        mLinkAddresses = new ArrayList<LinkAddress>();
+        mDnses = new ArrayList<InetAddress>();
+        mGateway = null;
+        mHttpProxy = null;
+    }
+
+    /**
+     * Implement the Parcelable interface
+     * @hide
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " ");
+
+        String linkAddresses = "LinkAddresses: [";
+        for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString();
+        linkAddresses += "] ";
+
+        String dns = "DnsAddresses: [";
+        for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ",";
+        dns += "] ";
+
+        String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
+        String gateway = (mGateway == null ? "" : "Gateway: " + mGateway.getHostAddress() + " ");
+
+        return ifaceName + linkAddresses + gateway + dns + proxy;
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(getInterfaceName());
+        dest.writeInt(mLinkAddresses.size());
+        for(LinkAddress linkAddress : mLinkAddresses) {
+            dest.writeParcelable(linkAddress, flags);
+        }
+
+        dest.writeInt(mDnses.size());
+        for(InetAddress d : mDnses) {
+            dest.writeByteArray(d.getAddress());
+        }
+        if (mGateway != null) {
+            dest.writeByte((byte)1);
+            dest.writeByteArray(mGateway.getAddress());
+        } else {
+            dest.writeByte((byte)0);
+        }
+        if (mHttpProxy != null) {
+            dest.writeByte((byte)1);
+            dest.writeParcelable(mHttpProxy, flags);
+        } else {
+            dest.writeByte((byte)0);
+        }
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public static final Creator<LinkProperties> CREATOR =
+        new Creator<LinkProperties>() {
+            public LinkProperties createFromParcel(Parcel in) {
+                LinkProperties netProp = new LinkProperties();
+                String iface = in.readString();
+                if (iface != null) {
+                    try {
+                        netProp.setInterfaceName(iface);
+                    } catch (Exception e) {
+                        return null;
+                    }
+                }
+                int addressCount = in.readInt();
+                for (int i=0; i<addressCount; i++) {
+                    netProp.addLinkAddress((LinkAddress)in.readParcelable(null));
+                }
+                addressCount = in.readInt();
+                for (int i=0; i<addressCount; i++) {
+                    try {
+                        netProp.addDns(InetAddress.getByAddress(in.createByteArray()));
+                    } catch (UnknownHostException e) { }
+                }
+                if (in.readByte() == 1) {
+                    try {
+                        netProp.setGateway(InetAddress.getByAddress(in.createByteArray()));
+                    } catch (UnknownHostException e) {}
+                }
+                if (in.readByte() == 1) {
+                    netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
+                }
+                return netProp;
+            }
+
+            public LinkProperties[] newArray(int size) {
+                return new LinkProperties[size];
+            }
+        };
+}
diff --git a/core/java/android/net/LinkSocket.java b/core/java/android/net/LinkSocket.java
new file mode 100644
index 0000000..5aa6451
--- /dev/null
+++ b/core/java/android/net/LinkSocket.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.net.LinkSocketNotifier;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+import java.util.HashSet;
+import java.util.Set;
+
+/** @hide */
+public class LinkSocket extends Socket {
+    private final static String TAG = "LinkSocket";
+    private final static boolean DBG = true;
+
+    /**
+     * Default constructor
+     */
+    public LinkSocket() {
+        if (DBG) log("LinkSocket() EX");
+    }
+
+    /**
+     * Creates a new unconnected socket.
+     * @param notifier a reference to a class that implements {@code LinkSocketNotifier}
+     */
+    public LinkSocket(LinkSocketNotifier notifier) {
+        if (DBG) log("LinkSocket(notifier) EX");
+    }
+
+    /**
+     * Creates a new unconnected socket usign the given proxy type.
+     * @param notifier a reference to a class that implements {@code LinkSocketNotifier}
+     * @param proxy the specified proxy for this socket
+     * @throws IllegalArgumentException if the argument proxy is null or of an invalid type.
+     * @throws SecurityException if a security manager exists and it denies the permission
+     *                           to connect to the given proxy.
+     */
+    public LinkSocket(LinkSocketNotifier notifier, Proxy proxy) {
+        if (DBG) log("LinkSocket(notifier, proxy) EX");
+    }
+
+    /**
+     * @return the {@code LinkProperties} for the socket
+     */
+    public LinkProperties getLinkProperties() {
+        if (DBG) log("LinkProperties() EX");
+        return new LinkProperties();
+    }
+
+    /**
+     * Set the {@code LinkCapabilies} needed for this socket.  If the socket is already connected
+     * or is a duplicate socket the request is ignored and {@code false} will
+     * be returned. A needs map can be created via the {@code createNeedsMap} static
+     * method.
+     * @param needs the needs of the socket
+     * @return {@code true} if needs are successfully set, {@code false} otherwise
+     */
+    public boolean setNeededCapabilities(LinkCapabilities needs) {
+        if (DBG) log("setNeeds() EX");
+        return false;
+    }
+
+    /**
+     * @return the LinkCapabilites set by setNeededCapabilities, empty if none has been set
+     */
+    public LinkCapabilities getNeededCapabilities() {
+        if (DBG) log("getNeeds() EX");
+        return null;
+    }
+
+    /**
+     * @return all of the {@code LinkCapabilities} of the link used by this socket
+     */
+    public LinkCapabilities getCapabilities() {
+        if (DBG) log("getCapabilities() EX");
+        return null;
+    }
+
+    /**
+     * Returns this LinkSockets set of capabilities, filtered according to
+     * the given {@code Set}.  Capabilities in the Set but not available from
+     * the link will not be reported in the results.  Capabilities of the link
+     * but not listed in the Set will also not be reported in the results.
+     * @param capabilities {@code Set} of capabilities requested
+     * @return the filtered {@code LinkCapabilities} of this LinkSocket, may be empty
+     */
+    public LinkCapabilities getCapabilities(Set<Integer> capabilities) {
+        if (DBG) log("getCapabilities(capabilities) EX");
+        return new LinkCapabilities();
+    }
+
+    /**
+     * Provide the set of capabilities the application is interested in tracking
+     * for this LinkSocket.
+     * @param capabilities a {@code Set} of capabilities to track
+     */
+    public void setTrackedCapabilities(Set<Integer> capabilities) {
+        if (DBG) log("setTrackedCapabilities(capabilities) EX");
+    }
+
+    /**
+     * @return the {@code LinkCapabilities} that are tracked, empty if none has been set.
+     */
+    public Set<Integer> getTrackedCapabilities() {
+        if (DBG) log("getTrackedCapabilities(capabilities) EX");
+        return new HashSet<Integer>();
+    }
+
+    /**
+     * Connects this socket to the given remote host address and port specified
+     * by dstName and dstPort.
+     * @param dstName the address of the remote host to connect to
+     * @param dstPort the port to connect to on the remote host
+     * @param timeout the timeout value in milliseconds or 0 for infinite timeout
+     * @throws UnknownHostException if the given dstName is invalid
+     * @throws IOException if the socket is already connected or an error occurs
+     *                     while connecting
+     * @throws SocketTimeoutException if the timeout fires
+     */
+    public void connect(String dstName, int dstPort, int timeout)
+            throws UnknownHostException, IOException, SocketTimeoutException {
+        if (DBG) log("connect(dstName, dstPort, timeout) EX");
+    }
+
+    /**
+     * Connects this socket to the given remote host address and port specified
+     * by dstName and dstPort.
+     * @param dstName the address of the remote host to connect to
+     * @param dstPort the port to connect to on the remote host
+     * @throws UnknownHostException if the given dstName is invalid
+     * @throws IOException if the socket is already connected or an error occurs
+     *                     while connecting
+     */
+    public void connect(String dstName, int dstPort)
+            throws UnknownHostException, IOException {
+        if (DBG) log("connect(dstName, dstPort, timeout) EX");
+    }
+
+    /**
+     * Connects this socket to the given remote host address and port specified
+     * by the SocketAddress with the specified timeout.
+     * @deprecated Use {@code connect(String dstName, int dstPort, int timeout)}
+     *             instead.  Using this method may result in reduced functionality.
+     * @param remoteAddr the address and port of the remote host to connect to
+     * @throws IllegalArgumentException if the given SocketAddress is invalid
+     * @throws IOException if the socket is already connected or an error occurs
+     *                     while connecting
+     * @throws SocketTimeoutException if the timeout expires
+     */
+    @Override
+    @Deprecated
+    public void connect(SocketAddress remoteAddr, int timeout)
+            throws IOException, SocketTimeoutException {
+        if (DBG) log("connect(remoteAddr, timeout) EX DEPRECATED");
+    }
+
+    /**
+     * Connects this socket to the given remote host address and port specified
+     * by the SocketAddress.
+     * TODO add comment on all these that the network selection happens during connect
+     * and may take 30 seconds
+     * @deprecated Use {@code connect(String dstName, int dstPort)}
+     *             Using this method may result in reduced functionality.
+     * @param remoteAddr the address and port of the remote host to connect to.
+     * @throws IllegalArgumentException if the SocketAddress is invalid or not supported.
+     * @throws IOException if the socket is already connected or an error occurs
+     *                     while connecting
+     */
+    @Override
+    @Deprecated
+    public void connect(SocketAddress remoteAddr) throws IOException {
+        if (DBG) log("connect(remoteAddr) EX DEPRECATED");
+    }
+
+    /**
+     * Connect a duplicate socket socket to the same remote host address and port
+     * as the original with a timeout parameter.
+     * @param timeout the timeout value in milliseconds or 0 for infinite timeout
+     * @throws IOException if the socket is already connected or an error occurs
+     *                     while connecting
+     */
+    public void connect(int timeout) throws IOException {
+        if (DBG) log("connect(timeout) EX");
+    }
+
+    /**
+     * Connect a duplicate socket socket to the same remote host address and port
+     * as the original.
+     * @throws IOException if the socket is already connected or an error occurs
+     *                     while connecting
+     */
+    public void connect() throws IOException {
+        if (DBG) log("connect() EX");
+    }
+
+    /**
+     * Closes the socket.  It is not possible to reconnect or rebind to this
+     * socket thereafter which means a new socket instance has to be created.
+     * @throws IOException if an error occurs while closing the socket
+     */
+    @Override
+    public synchronized void close() throws IOException {
+        if (DBG) log("close() EX");
+    }
+
+    /**
+     * Request that a new LinkSocket be created using a different radio
+     * (such as WiFi or 3G) than the current LinkSocket.  If a different
+     * radio is available a call back will be made via {@code onBetterLinkAvail}.
+     * If unable to find a better radio, application will be notified via
+     * {@code onNewLinkUnavailable}
+     * @see LinkSocketNotifier#onBetterLinkAvailable(LinkSocket, LinkSocket)
+     * @param linkRequestReason reason for requesting a new link.
+     */
+    public void requestNewLink(LinkRequestReason linkRequestReason) {
+        if (DBG) log("requestNewLink(linkRequestReason) EX");
+    }
+
+    /**
+     * @deprecated LinkSocket will automatically pick the optimum interface
+     *             to bind to
+     * @param localAddr the specific address and port on the local machine
+     *                  to bind to
+     * @throws IOException always as this method is deprecated for LinkSocket
+     */
+    @Override
+    @Deprecated
+    public void bind(SocketAddress localAddr) throws UnsupportedOperationException {
+        if (DBG) log("bind(localAddr) EX throws IOException");
+        throw new UnsupportedOperationException("bind is deprecated for LinkSocket");
+    }
+
+    /**
+     * Reason codes an application can specify when requesting for a new link.
+     * TODO: need better documentation
+     */
+    public static final class LinkRequestReason {
+        /** No constructor */
+        private LinkRequestReason() {}
+
+        /** This link is working properly */
+        public static final int LINK_PROBLEM_NONE = 0;
+        /** This link has an unknown issue */
+        public static final int LINK_PROBLEM_UNKNOWN = 1;
+    }
+
+    /**
+     * Debug logging
+     */
+    protected static void log(String s) {
+        Log.d(TAG, s);
+    }
+}
diff --git a/core/java/android/net/LinkSocketNotifier.java b/core/java/android/net/LinkSocketNotifier.java
new file mode 100644
index 0000000..28e2834
--- /dev/null
+++ b/core/java/android/net/LinkSocketNotifier.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import java.util.Map;
+
+/**
+ * Interface used to get feedback about a {@link android.net.LinkSocket}.  Instance is optionally
+ * passed when a LinkSocket is constructed.  Multiple LinkSockets may use the same notifier.
+ * @hide
+ */
+public interface LinkSocketNotifier {
+    /**
+     * This callback function will be called if a better link
+     * becomes available.
+     * TODO - this shouldn't be checked for all cases - what's the conditional
+     *        flag?
+     * If the duplicate socket is accepted, the original will be marked invalid
+     * and additional use will throw exceptions.
+     * @param original the original LinkSocket
+     * @param duplicate the new LinkSocket that better meets the application
+     *                  requirements
+     * @return {@code true} if the application intends to use this link
+     *
+     * REM
+     * TODO - how agressive should we be?
+     * At a minimum CS tracks which LS have this turned on and tracks the requirements
+     * When a new link becomes available, automatically check if any of the LinkSockets
+     *   will care.
+     * If found, grab a refcount on the link so it doesn't go away and send notification
+     * Optionally, periodically setup connection on available networks to check for better links
+     * Maybe pass this info into the LinkFactories so condition changes can be acted on more quickly
+     */
+    public boolean onBetterLinkAvailable(LinkSocket original, LinkSocket duplicate);
+
+    /**
+     * This callback function will be called when a LinkSocket no longer has
+     * an active link.
+     * @param socket the LinkSocket that lost its link
+     *
+     * REM
+     * NetworkStateTracker tells us it is disconnected
+     * CS checks the table for LS on that link
+     * CS calls each callback (need to work out p2p cross process callback)
+     */
+    public void onLinkLost(LinkSocket socket);
+
+    /**
+     * This callback function will be called when an application calls
+     * requestNewLink on a LinkSocket but the LinkSocket is unable to find
+     * a suitable new link.
+     * @param socket the LinkSocket for which a new link was not found
+     * TODO - why the diff between initial request (sync) and requestNewLink?
+     *
+     * REM
+     * CS process of trying to find a new link must track the LS that started it
+     * on failure, call callback
+     */
+    public void onNewLinkUnavailable(LinkSocket socket);
+
+    /**
+     * This callback function will be called when any of the notification-marked
+     * capabilities of the LinkSocket (e.g. upstream bandwidth) have changed.
+     * @param socket the linkSocet for which capabilities have changed
+     * @param changedCapabilities the set of capabilities that the application
+     *                            is interested in and have changed (with new values)
+     *
+     * REM
+     * Maybe pass the interesting capabilities into the Links.
+     * Get notified of every capability change
+     * check for LinkSockets on that Link that are interested in that Capability - call them
+     */
+    public void onCapabilitiesChanged(LinkSocket socket, LinkCapabilities changedCapabilities);
+}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 8d18eba..40f72b2 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -16,19 +16,21 @@
 
 package android.net;
 
-import java.net.InetAddress;
-
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.RemoteException;
 import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
 import android.os.ServiceManager;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyIntents;
 import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo;
+import android.net.LinkProperties;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.text.TextUtils;
@@ -40,19 +42,25 @@
  *
  * {@hide}
  */
-public class MobileDataStateTracker extends NetworkStateTracker {
+public class MobileDataStateTracker implements NetworkStateTracker {
 
     private static final String TAG = "MobileDataStateTracker";
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
 
     private Phone.DataState mMobileDataState;
     private ITelephony mPhoneService;
 
     private String mApnType;
-    private String mApnTypeToWatchFor;
-    private String mApnName;
-    private boolean mEnabled;
-    private BroadcastReceiver mStateReceiver;
+    private static String[] sDnsPropNames;
+    private NetworkInfo mNetworkInfo;
+    private boolean mTeardownRequested = false;
+    private Handler mTarget;
+    private Context mContext;
+    private LinkProperties mLinkProperties;
+    private LinkCapabilities mLinkCapabilities;
+    private boolean mPrivateDnsRouteSet = false;
+    private int mDefaultGatewayAddr = 0;
+    private boolean mDefaultRouteSet = false;
 
     // DEFAULT and HIPRI are the same connection.  If we're one of these we need to check if
     // the other is also disconnected before we reset sockets
@@ -60,35 +68,22 @@
 
     /**
      * Create a new MobileDataStateTracker
-     * @param context the application context of the caller
-     * @param target a message handler for getting callbacks about state changes
      * @param netType the ConnectivityManager network type
-     * @param apnType the Phone apnType
      * @param tag the name of this network
      */
-    public MobileDataStateTracker(Context context, Handler target, int netType, String tag) {
-        super(context, target, netType,
+    public MobileDataStateTracker(int netType, String tag) {
+        mNetworkInfo = new NetworkInfo(netType,
                 TelephonyManager.getDefault().getNetworkType(), tag,
                 TelephonyManager.getDefault().getNetworkTypeName());
         mApnType = networkTypeToApnType(netType);
-        if (TextUtils.equals(mApnType, Phone.APN_TYPE_HIPRI)) {
-            mApnTypeToWatchFor = Phone.APN_TYPE_DEFAULT;
-        } else {
-            mApnTypeToWatchFor = mApnType;
-        }
         if (netType == ConnectivityManager.TYPE_MOBILE ||
                 netType == ConnectivityManager.TYPE_MOBILE_HIPRI) {
             mIsDefaultOrHipri = true;
         }
 
         mPhoneService = null;
-        if(netType == ConnectivityManager.TYPE_MOBILE) {
-            mEnabled = true;
-        } else {
-            mEnabled = false;
-        }
 
-        mDnsPropNames = new String[] {
+        sDnsPropNames = new String[] {
                 "net.rmnet0.dns1",
                 "net.rmnet0.dns2",
                 "net.eth0.dns1",
@@ -99,165 +94,168 @@
                 "net.gprs.dns2",
                 "net.ppp0.dns1",
                 "net.ppp0.dns2"};
-
     }
 
     /**
-     * Begin monitoring mobile data connectivity.
+     * Begin monitoring data connectivity.
+     *
+     * @param context is the current Android context
+     * @param target is the Hander to which to return the events.
      */
-    public void startMonitoring() {
-        IntentFilter filter =
-                new IntentFilter(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+    public void startMonitoring(Context context, Handler target) {
+        mTarget = target;
+        mContext = context;
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
         filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
-        filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
 
-        mStateReceiver = new MobileDataStateReceiver();
-        Intent intent = mContext.registerReceiver(mStateReceiver, filter);
-        if (intent != null)
-            mMobileDataState = getMobileDataState(intent);
-        else
-            mMobileDataState = Phone.DataState.DISCONNECTED;
+        mContext.registerReceiver(new MobileDataStateReceiver(), filter);
+        mMobileDataState = Phone.DataState.DISCONNECTED;
     }
 
-    private Phone.DataState getMobileDataState(Intent intent) {
-        String str = intent.getStringExtra(Phone.STATE_KEY);
-        if (str != null) {
-            String apnTypeList =
-                    intent.getStringExtra(Phone.DATA_APN_TYPES_KEY);
-            if (isApnTypeIncluded(apnTypeList)) {
-                return Enum.valueOf(Phone.DataState.class, str);
-            }
-        }
-        return Phone.DataState.DISCONNECTED;
+    /**
+     * Return the IP addresses of the DNS servers available for the mobile data
+     * network interface.
+     * @return a list of DNS addresses, with no holes.
+     */
+    public String[] getDnsPropNames() {
+        return sDnsPropNames;
     }
 
-    private boolean isApnTypeIncluded(String typeList) {
-        /* comma seperated list - split and check */
-        if (typeList == null)
-            return false;
+    public boolean isPrivateDnsRouteSet() {
+        return mPrivateDnsRouteSet;
+    }
 
-        String[] list = typeList.split(",");
-        for(int i=0; i< list.length; i++) {
-            if (TextUtils.equals(list[i], mApnTypeToWatchFor) ||
-                TextUtils.equals(list[i], Phone.APN_TYPE_ALL)) {
-                return true;
-            }
-        }
-        return false;
+    public void privateDnsRouteSet(boolean enabled) {
+        mPrivateDnsRouteSet = enabled;
+    }
+
+    public NetworkInfo getNetworkInfo() {
+        return mNetworkInfo;
+    }
+
+    public int getDefaultGatewayAddr() {
+        return mDefaultGatewayAddr;
+    }
+
+    public boolean isDefaultRouteSet() {
+        return mDefaultRouteSet;
+    }
+
+    public void defaultRouteSet(boolean enabled) {
+        mDefaultRouteSet = enabled;
+    }
+
+    /**
+     * This is not implemented.
+     */
+    public void releaseWakeLock() {
     }
 
     private class MobileDataStateReceiver extends BroadcastReceiver {
-        ConnectivityManager mConnectivityManager;
+        IConnectivityManager mConnectivityManager;
+
         public void onReceive(Context context, Intent intent) {
-            synchronized(this) {
-                if (intent.getAction().equals(TelephonyIntents.
-                        ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
-                    Phone.DataState state = getMobileDataState(intent);
-                    String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY);
-                    String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
-                    String apnTypeList = intent.getStringExtra(Phone.DATA_APN_TYPES_KEY);
-                    mApnName = apnName;
+            if (intent.getAction().equals(TelephonyIntents.
+                    ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
+                String apnType = intent.getStringExtra(Phone.DATA_APN_TYPE_KEY);
+                if (!TextUtils.equals(apnType, mApnType)) {
+                    return;
+                }
+                Phone.DataState state = Enum.valueOf(Phone.DataState.class,
+                        intent.getStringExtra(Phone.STATE_KEY));
+                String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY);
+                String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
 
-                    boolean unavailable = intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY,
-                            false);
+                mNetworkInfo.setIsAvailable(!intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY,
+                        false));
 
-                    // set this regardless of the apnTypeList.  It's all the same radio/network
-                    // underneath
-                    mNetworkInfo.setIsAvailable(!unavailable);
+                if (DBG) Log.d(TAG, mApnType + " Received state= " + state + ", old= " +
+                        mMobileDataState + ", reason= " +
+                        (reason == null ? "(unspecified)" : reason));
 
-                    if (isApnTypeIncluded(apnTypeList)) {
-                        if (mEnabled == false) {
-                            // if we're not enabled but the APN Type is supported by this connection
-                            // we should record the interface name if one's provided.  If the user
-                            // turns on this network we will need the interfacename but won't get
-                            // a fresh connected message - TODO fix this when we get per-APN
-                            // notifications
-                            if (state == Phone.DataState.CONNECTED) {
-                                if (DBG) Log.d(TAG, "replacing old mInterfaceName (" +
-                                        mInterfaceName + ") with " +
-                                        intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY) +
-                                        " for " + mApnType);
-                                mInterfaceName = intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY);
+                if (mMobileDataState != state) {
+                    mMobileDataState = state;
+                    switch (state) {
+                        case DISCONNECTED:
+                            if(isTeardownRequested()) {
+                                setTeardownRequested(false);
                             }
-                            return;
-                        }
-                    } else {
-                        return;
-                    }
 
-                    if (DBG) Log.d(TAG, mApnType + " Received state= " + state + ", old= " +
-                            mMobileDataState + ", reason= " +
-                            (reason == null ? "(unspecified)" : reason) +
-                            ", apnTypeList= " + apnTypeList);
-
-                    if (mMobileDataState != state) {
-                        mMobileDataState = state;
-                        switch (state) {
-                            case DISCONNECTED:
-                                if(isTeardownRequested()) {
-                                    mEnabled = false;
-                                    setTeardownRequested(false);
+                            setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
+                            boolean doReset = true;
+                            if (mIsDefaultOrHipri == true) {
+                                // both default and hipri must go down before we reset
+                                int typeToCheck = (Phone.APN_TYPE_DEFAULT.equals(mApnType) ?
+                                    ConnectivityManager.TYPE_MOBILE_HIPRI :
+                                    ConnectivityManager.TYPE_MOBILE);
+                                if (mConnectivityManager == null) {
+                                    IBinder b = ServiceManager.getService(
+                                            Context.CONNECTIVITY_SERVICE);
+                                    mConnectivityManager = IConnectivityManager.Stub.asInterface(b);
                                 }
-
-                                setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
-                                boolean doReset = true;
-                                if (mIsDefaultOrHipri == true) {
-                                    // both default and hipri must go down before we reset
-                                    int typeToCheck = (Phone.APN_TYPE_DEFAULT.equals(mApnType) ?
-                                            ConnectivityManager.TYPE_MOBILE_HIPRI :
-                                            ConnectivityManager.TYPE_MOBILE);
-                                    if (mConnectivityManager == null) {
-                                        mConnectivityManager =
-                                                (ConnectivityManager)context.getSystemService(
-                                                Context.CONNECTIVITY_SERVICE);
-                                    }
+                                try {
                                     if (mConnectivityManager != null) {
                                         NetworkInfo info = mConnectivityManager.getNetworkInfo(
-                                                    typeToCheck);
-                                        if (info != null && info.isConnected() == true) {
+                                                typeToCheck);
+                                        if (info.isConnected() == true) {
                                             doReset = false;
                                         }
                                     }
+                                } catch (RemoteException e) {
+                                    // just go ahead with the reset
+                                    Log.e(TAG, "Exception trying to contact ConnService: "
+                                            + e);
                                 }
-                                if (doReset && mInterfaceName != null) {
-                                    NetworkUtils.resetConnections(mInterfaceName);
-                                }
-                                // can't do this here - ConnectivityService needs it to clear stuff
-                                // it's ok though - just leave it to be refreshed next time
-                                // we connect.
-                                //if (DBG) Log.d(TAG, "clearing mInterfaceName for "+ mApnType +
-                                //        " as it DISCONNECTED");
-                                //mInterfaceName = null;
-                                //mDefaultGatewayAddr = 0;
-                                break;
-                            case CONNECTING:
-                                setDetailedState(DetailedState.CONNECTING, reason, apnName);
-                                break;
-                            case SUSPENDED:
-                                setDetailedState(DetailedState.SUSPENDED, reason, apnName);
-                                break;
-                            case CONNECTED:
-                                mInterfaceName = intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY);
-                                if (mInterfaceName == null) {
-                                    Log.d(TAG, "CONNECTED event did not supply interface name.");
-                                }
-                                mDefaultGatewayAddr = intent.getIntExtra(Phone.DATA_GATEWAY_KEY, 0);
-                                setDetailedState(DetailedState.CONNECTED, reason, apnName);
-                                break;
-                        }
+                            }
+                            if (doReset && mLinkProperties != null) {
+                                String iface = mLinkProperties.getInterfaceName();
+                                if (iface != null) NetworkUtils.resetConnections(iface);
+                            }
+                            // TODO - check this
+                            // can't do this here - ConnectivityService needs it to clear stuff
+                            // it's ok though - just leave it to be refreshed next time
+                            // we connect.
+                            //if (DBG) Log.d(TAG, "clearing mInterfaceName for "+ mApnType +
+                            //        " as it DISCONNECTED");
+                            //mInterfaceName = null;
+                            //mDefaultGatewayAddr = 0;
+                            break;
+                        case CONNECTING:
+                            setDetailedState(DetailedState.CONNECTING, reason, apnName);
+                            break;
+                        case SUSPENDED:
+                            setDetailedState(DetailedState.SUSPENDED, reason, apnName);
+                            break;
+                        case CONNECTED:
+                            mLinkProperties = intent.getParcelableExtra(
+                                    Phone.DATA_LINK_PROPERTIES_KEY);
+                            if (mLinkProperties == null) {
+                                Log.d(TAG, "CONNECTED event did not supply link properties.");
+                                mLinkProperties = new LinkProperties();
+                            }
+                            mLinkCapabilities = intent.getParcelableExtra(
+                                    Phone.DATA_LINK_CAPABILITIES_KEY);
+                            if (mLinkCapabilities == null) {
+                                Log.d(TAG, "CONNECTED event did not supply link capabilities.");
+                                mLinkCapabilities = new LinkCapabilities();
+                            }
+                            setDetailedState(DetailedState.CONNECTED, reason, apnName);
+                            break;
                     }
-                } else if (intent.getAction().
-                        equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
-                    mEnabled = false;
-                    String reason = intent.getStringExtra(Phone.FAILURE_REASON_KEY);
-                    String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
-                    if (DBG) Log.d(TAG, "Received " + intent.getAction() + " broadcast" +
-                            reason == null ? "" : "(" + reason + ")");
-                    setDetailedState(DetailedState.FAILED, reason, apnName);
                 }
-                TelephonyManager tm = TelephonyManager.getDefault();
-                setRoamingStatus(tm.isNetworkRoaming());
-                setSubtype(tm.getNetworkType(), tm.getNetworkTypeName());
+            } else if (intent.getAction().
+                    equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
+                String apnType = intent.getStringExtra(Phone.DATA_APN_TYPE_KEY);
+                if (!TextUtils.equals(apnType, mApnType)) {
+                    return;
+                }
+                String reason = intent.getStringExtra(Phone.FAILURE_REASON_KEY);
+                String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
+                if (DBG) Log.d(TAG, mApnType + "Received " + intent.getAction() +
+                        " broadcast" + reason == null ? "" : "(" + reason + ")");
+                setDetailedState(DetailedState.FAILED, reason, apnName);
             }
         }
     }
@@ -272,33 +270,7 @@
      * Report whether data connectivity is possible.
      */
     public boolean isAvailable() {
-        getPhoneService(false);
-
-        /*
-         * If the phone process has crashed in the past, we'll get a
-         * RemoteException and need to re-reference the service.
-         */
-        for (int retry = 0; retry < 2; retry++) {
-            if (mPhoneService == null) break;
-
-            try {
-                return mPhoneService.isDataConnectivityPossible();
-            } catch (RemoteException e) {
-                // First-time failed, get the phone service again
-                if (retry == 0) getPhoneService(true);
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * The mobile data network subtype indicates what generation network technology is in effect,
-     * e.g., GPRS, EDGE, UMTS, etc.
-     */
-    public int getNetworkSubtype() {
-        return TelephonyManager.getDefault().getNetworkType();
+        return mNetworkInfo.isAvailable();
     }
 
     /**
@@ -343,6 +315,17 @@
         case TelephonyManager.NETWORK_TYPE_EVDO_B:
             networkTypeStr = "evdo";
             break;
+        case TelephonyManager.NETWORK_TYPE_IDEN:
+            networkTypeStr = "iden";
+            break;
+        case TelephonyManager.NETWORK_TYPE_LTE:
+            networkTypeStr = "lte";
+            break;
+        case TelephonyManager.NETWORK_TYPE_EHRPD:
+            networkTypeStr = "ehrpd";
+            break;
+        default:
+            Log.e(TAG, "unknown network type: " + tm.getNetworkType());
         }
         return "net.tcp.buffersize." + networkTypeStr;
     }
@@ -350,70 +333,74 @@
     /**
      * Tear down mobile data connectivity, i.e., disable the ability to create
      * mobile data connections.
+     * TODO - make async and return nothing?
      */
-    @Override
     public boolean teardown() {
-        // since we won't get a notification currently (TODO - per APN notifications)
-        // we won't get a disconnect message until all APN's on the current connection's
-        // APN list are disabled.  That means privateRoutes for DNS and such will remain on -
-        // not a problem since that's all shared with whatever other APN is still on, but
-        // ugly.
         setTeardownRequested(true);
         return (setEnableApn(mApnType, false) != Phone.APN_REQUEST_FAILED);
     }
 
     /**
+     * Record the detailed state of a network, and if it is a
+     * change from the previous state, send a notification to
+     * any listeners.
+     * @param state the new @{code DetailedState}
+     * @param reason a {@code String} indicating a reason for the state change,
+     * if one was supplied. May be {@code null}.
+     * @param extraInfo optional {@code String} providing extra information about the state change
+     */
+    private void setDetailedState(NetworkInfo.DetailedState state, String reason, String extraInfo) {
+        if (DBG) Log.d(TAG, "setDetailed state, old ="
+                + mNetworkInfo.getDetailedState() + " and new state=" + state);
+        if (state != mNetworkInfo.getDetailedState()) {
+            boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING);
+            String lastReason = mNetworkInfo.getReason();
+            /*
+             * If a reason was supplied when the CONNECTING state was entered, and no
+             * reason was supplied for entering the CONNECTED state, then retain the
+             * reason that was supplied when going to CONNECTING.
+             */
+            if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
+                    && lastReason != null)
+                reason = lastReason;
+            mNetworkInfo.setDetailedState(state, reason, extraInfo);
+            Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
+            msg.sendToTarget();
+        }
+    }
+
+    public void setTeardownRequested(boolean isRequested) {
+        mTeardownRequested = isRequested;
+    }
+
+    public boolean isTeardownRequested() {
+        return mTeardownRequested;
+    }
+
+    /**
      * Re-enable mobile data connectivity after a {@link #teardown()}.
+     * TODO - make async and always get a notification?
      */
     public boolean reconnect() {
+        boolean retValue = false; //connected or expect to be?
         setTeardownRequested(false);
         switch (setEnableApn(mApnType, true)) {
             case Phone.APN_ALREADY_ACTIVE:
-                // TODO - remove this when we get per-apn notifications
-                mEnabled = true;
                 // need to set self to CONNECTING so the below message is handled.
-                mMobileDataState = Phone.DataState.CONNECTING;
-                setDetailedState(DetailedState.CONNECTING, Phone.REASON_APN_CHANGED, null);
-                //send out a connected message
-                Intent intent = new Intent(TelephonyIntents.
-                        ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
-                intent.putExtra(Phone.STATE_KEY, Phone.DataState.CONNECTED.toString());
-                intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, Phone.REASON_APN_CHANGED);
-                intent.putExtra(Phone.DATA_APN_TYPES_KEY, mApnTypeToWatchFor);
-                intent.putExtra(Phone.DATA_APN_KEY, mApnName);
-                intent.putExtra(Phone.DATA_IFACE_NAME_KEY, mInterfaceName);
-                intent.putExtra(Phone.NETWORK_UNAVAILABLE_KEY, false);
-                if (mStateReceiver != null) mStateReceiver.onReceive(mContext, intent);
+                retValue = true;
                 break;
             case Phone.APN_REQUEST_STARTED:
-                mEnabled = true;
                 // no need to do anything - we're already due some status update intents
+                retValue = true;
                 break;
             case Phone.APN_REQUEST_FAILED:
-                if (mPhoneService == null && mApnType == Phone.APN_TYPE_DEFAULT) {
-                    // on startup we may try to talk to the phone before it's ready
-                    // since the phone will come up enabled, go with that.
-                    // TODO - this also comes up on telephony crash: if we think mobile data is
-                    // off and the telephony stuff crashes and has to restart it will come up
-                    // enabled (making a data connection).  We will then be out of sync.
-                    // A possible solution is a broadcast when telephony restarts.
-                    mEnabled = true;
-                    return false;
-                }
-                // else fall through
             case Phone.APN_TYPE_NOT_AVAILABLE:
-                // Default is always available, but may be off due to
-                // AirplaneMode or E-Call or whatever..
-                if (mApnType != Phone.APN_TYPE_DEFAULT) {
-                    mEnabled = false;
-                }
                 break;
             default:
                 Log.e(TAG, "Error in reconnect - unexpected response.");
-                mEnabled = false;
                 break;
         }
-        return mEnabled;
+        return retValue;
     }
 
     /**
@@ -486,25 +473,6 @@
         return -1;
     }
 
-    /**
-     * Ensure that a network route exists to deliver traffic to the specified
-     * host via the mobile data network.
-     * @param hostAddress the IP address of the host to which the route is desired,
-     * @return {@code true} on success, {@code false} on failure
-     */
-    @Override
-    public boolean requestRouteToHost(InetAddress hostAddress) {
-        if (DBG) {
-            Log.d(TAG, "Requested host route to " + hostAddress.getHostAddress() +
-                    " for " + mApnType + "(" + mInterfaceName + ")");
-        }
-        if (mInterfaceName != null) {
-            return NetworkUtils.addHostRoute(mInterfaceName, hostAddress, null);
-        } else {
-            return false;
-        }
-    }
-
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer("Mobile data state: ");
@@ -566,4 +534,18 @@
                 return null;
         }
     }
+
+    /**
+     * @see android.net.NetworkStateTracker#getLinkProperties()
+     */
+    public LinkProperties getLinkProperties() {
+        return new LinkProperties(mLinkProperties);
+    }
+
+    /**
+     * @see android.net.NetworkStateTracker#getLinkCapabilities()
+     */
+    public LinkCapabilities getLinkCapabilities() {
+        return new LinkCapabilities(mLinkCapabilities);
+    }
 }
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 649cb8c..5f5e11c 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -97,7 +97,7 @@
         stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED);
         stateMap.put(DetailedState.FAILED, State.DISCONNECTED);
     }
-    
+
     private int mNetworkType;
     private int mSubtype;
     private String mTypeName;
@@ -121,7 +121,10 @@
      */
     public NetworkInfo(int type) {}
 
-    NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
+    /**
+     * @hide
+     */
+    public NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
         if (!ConnectivityManager.isNetworkTypeValid(type)) {
             throw new IllegalArgumentException("Invalid network type: " + type);
         }
@@ -141,7 +144,9 @@
      * @return the network type
      */
     public int getType() {
-        return mNetworkType;
+        synchronized (this) {
+            return mNetworkType;
+        }
     }
 
     /**
@@ -150,12 +155,16 @@
      * @return the network subtype
      */
     public int getSubtype() {
-        return mSubtype;
+        synchronized (this) {
+            return mSubtype;
+        }
     }
 
     void setSubtype(int subtype, String subtypeName) {
-        mSubtype = subtype;
-        mSubtypeName = subtypeName;
+        synchronized (this) {
+            mSubtype = subtype;
+            mSubtypeName = subtypeName;
+        }
     }
 
     /**
@@ -164,7 +173,9 @@
      * @return the name of the network type
      */
     public String getTypeName() {
-        return mTypeName;
+        synchronized (this) {
+            return mTypeName;
+        }
     }
 
     /**
@@ -172,7 +183,9 @@
      * @return the name of the network subtype
      */
     public String getSubtypeName() {
-        return mSubtypeName;
+        synchronized (this) {
+            return mSubtypeName;
+        }
     }
 
     /**
@@ -185,7 +198,9 @@
      * of being established, {@code false} otherwise.
      */
     public boolean isConnectedOrConnecting() {
-        return mState == State.CONNECTED || mState == State.CONNECTING;
+        synchronized (this) {
+            return mState == State.CONNECTED || mState == State.CONNECTING;
+        }
     }
 
     /**
@@ -194,7 +209,9 @@
      * @return {@code true} if network connectivity exists, {@code false} otherwise.
      */
     public boolean isConnected() {
-        return mState == State.CONNECTED;
+        synchronized (this) {
+            return mState == State.CONNECTED;
+        }
     }
 
     /**
@@ -210,7 +227,9 @@
      * @return {@code true} if the network is available, {@code false} otherwise
      */
     public boolean isAvailable() {
-        return mIsAvailable;
+        synchronized (this) {
+            return mIsAvailable;
+        }
     }
 
     /**
@@ -220,7 +239,9 @@
      * @hide
      */
     public void setIsAvailable(boolean isAvailable) {
-        mIsAvailable = isAvailable;
+        synchronized (this) {
+            mIsAvailable = isAvailable;
+        }
     }
 
     /**
@@ -231,7 +252,9 @@
      * otherwise.
      */
     public boolean isFailover() {
-        return mIsFailover;
+        synchronized (this) {
+            return mIsFailover;
+        }
     }
 
     /**
@@ -241,7 +264,9 @@
      * @hide
      */
     public void setFailover(boolean isFailover) {
-        mIsFailover = isFailover;
+        synchronized (this) {
+            mIsFailover = isFailover;
+        }
     }
 
     /**
@@ -251,11 +276,15 @@
      * @return {@code true} if roaming is in effect, {@code false} otherwise.
      */
     public boolean isRoaming() {
-        return mIsRoaming;
+        synchronized (this) {
+            return mIsRoaming;
+        }
     }
 
     void setRoaming(boolean isRoaming) {
-        mIsRoaming = isRoaming;
+        synchronized (this) {
+            mIsRoaming = isRoaming;
+        }
     }
 
     /**
@@ -263,7 +292,9 @@
      * @return the coarse-grained state
      */
     public State getState() {
-        return mState;
+        synchronized (this) {
+            return mState;
+        }
     }
 
     /**
@@ -271,7 +302,9 @@
      * @return the fine-grained state
      */
     public DetailedState getDetailedState() {
-        return mDetailedState;
+        synchronized (this) {
+            return mDetailedState;
+        }
     }
 
     /**
@@ -281,12 +314,15 @@
      * if one was supplied. May be {@code null}.
      * @param extraInfo an optional {@code String} providing addditional network state
      * information passed up from the lower networking layers.
+     * @hide
      */
-    void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
-        this.mDetailedState = detailedState;
-        this.mState = stateMap.get(detailedState);
-        this.mReason = reason;
-        this.mExtraInfo = extraInfo;
+    public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
+        synchronized (this) {
+            this.mDetailedState = detailedState;
+            this.mState = stateMap.get(detailedState);
+            this.mReason = reason;
+            this.mExtraInfo = extraInfo;
+        }
     }
 
     /**
@@ -295,7 +331,9 @@
      * @return the reason for failure, or null if not available
      */
     public String getReason() {
-        return mReason;
+        synchronized (this) {
+            return mReason;
+        }
     }
 
     /**
@@ -305,20 +343,24 @@
      * @return the extra information, or null if not available
      */
     public String getExtraInfo() {
-        return mExtraInfo;
+        synchronized (this) {
+            return mExtraInfo;
+        }
     }
 
     @Override
     public String toString() {
-        StringBuilder builder = new StringBuilder("NetworkInfo: ");
-        builder.append("type: ").append(getTypeName()).append("[").append(getSubtypeName()).
-                append("], state: ").append(mState).append("/").append(mDetailedState).
-                append(", reason: ").append(mReason == null ? "(unspecified)" : mReason).
-                append(", extra: ").append(mExtraInfo == null ? "(none)" : mExtraInfo).
-                append(", roaming: ").append(mIsRoaming).
-                append(", failover: ").append(mIsFailover).
-                append(", isAvailable: ").append(mIsAvailable);
-        return builder.toString();
+        synchronized (this) {
+            StringBuilder builder = new StringBuilder("NetworkInfo: ");
+            builder.append("type: ").append(getTypeName()).append("[").append(getSubtypeName()).
+            append("], state: ").append(mState).append("/").append(mDetailedState).
+            append(", reason: ").append(mReason == null ? "(unspecified)" : mReason).
+            append(", extra: ").append(mExtraInfo == null ? "(none)" : mExtraInfo).
+            append(", roaming: ").append(mIsRoaming).
+            append(", failover: ").append(mIsFailover).
+            append(", isAvailable: ").append(mIsAvailable);
+            return builder.toString();
+        }
     }
 
     /**
@@ -334,17 +376,19 @@
      * @hide
      */
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mNetworkType);
-        dest.writeInt(mSubtype);
-        dest.writeString(mTypeName);
-        dest.writeString(mSubtypeName);
-        dest.writeString(mState.name());
-        dest.writeString(mDetailedState.name());
-        dest.writeInt(mIsFailover ? 1 : 0);
-        dest.writeInt(mIsAvailable ? 1 : 0);
-        dest.writeInt(mIsRoaming ? 1 : 0);
-        dest.writeString(mReason);
-        dest.writeString(mExtraInfo);
+        synchronized (this) {
+            dest.writeInt(mNetworkType);
+            dest.writeInt(mSubtype);
+            dest.writeString(mTypeName);
+            dest.writeString(mSubtypeName);
+            dest.writeString(mState.name());
+            dest.writeString(mDetailedState.name());
+            dest.writeInt(mIsFailover ? 1 : 0);
+            dest.writeInt(mIsAvailable ? 1 : 0);
+            dest.writeInt(mIsRoaming ? 1 : 0);
+            dest.writeString(mReason);
+            dest.writeString(mExtraInfo);
+        }
     }
 
     /**
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index c161f3a..8afdcee 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -16,367 +16,129 @@
 
 package android.net;
 
-import java.io.FileWriter;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemProperties;
 import android.content.Context;
-import android.text.TextUtils;
-import android.util.Log;
-
+import android.os.Handler;
 
 /**
- * Each subclass of this class keeps track of the state of connectivity
- * of a network interface. All state information for a network should
- * be kept in a Tracker class. This superclass manages the
- * network-type-independent aspects of network state.
+ * Interface provides the {@link com.android.server.ConnectivityService}
+ * with three services. Events to the ConnectivityService when
+ * changes occur, an API for controlling the network and storage
+ * for network specific information.
+ *
+ * The Connectivity will call startMonitoring before any other
+ * method is called.
  *
  * {@hide}
  */
-public abstract class NetworkStateTracker extends Handler {
+public interface NetworkStateTracker {
 
-    protected NetworkInfo mNetworkInfo;
-    protected Context mContext;
-    protected Handler mTarget;
-    protected String mInterfaceName;
-    protected String[] mDnsPropNames;
-    private boolean mPrivateDnsRouteSet;
-    protected int mDefaultGatewayAddr;
-    private boolean mTeardownRequested;
-
-    private static boolean DBG = false;
-    private static final String TAG = "NetworkStateTracker";
+    /**
+     * -------------------------------------------------------------
+     * Event Interface back to ConnectivityService.
+     *
+     * The events that are to be sent back to the Handler passed
+     * to startMonitoring when the particular event occurs.
+     * -------------------------------------------------------------
+     */
 
     // Share the event space with ConnectivityService (which we can't see, but
     // must send events to).  If you change these, change ConnectivityService
     // too.
-    private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
-    private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
+    static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
+    static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
 
-    public static final int EVENT_STATE_CHANGED = 1;
-    public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2;
     /**
-     * arg1: 1 to show, 0 to hide
-     * arg2: ID of the notification
-     * obj: Notification (if showing)
+     * The network state has changed and the NetworkInfo object
+     * contains the new state.
+     *
+     * msg.what = EVENT_STATE_CHANGED
+     * msg.obj = NetworkInfo object
      */
-    public static final int EVENT_NOTIFICATION_CHANGED = 3;
-    public static final int EVENT_CONFIGURATION_CHANGED = 4;
-    public static final int EVENT_ROAMING_CHANGED = 5;
-    public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6;
+    public static final int EVENT_STATE_CHANGED = 1;
 
-    public NetworkStateTracker(Context context,
-            Handler target,
-            int networkType,
-            int subType,
-            String typeName,
-            String subtypeName) {
-        super();
-        mContext = context;
-        mTarget = target;
-        mTeardownRequested = false;
+    /**
+     * msg.what = EVENT_CONFIGURATION_CHANGED
+     * msg.obj = NetworkInfo object
+     */
+    public static final int EVENT_CONFIGURATION_CHANGED = 3;
 
-        this.mNetworkInfo = new NetworkInfo(networkType, subType, typeName, subtypeName);
-    }
+    /**
+     * msg.what = EVENT_RESTORE_DEFAULT_NETWORK
+     * msg.obj = FeatureUser object
+     */
+    public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6;
 
-    public NetworkInfo getNetworkInfo() {
-        return mNetworkInfo;
-    }
+    /**
+     * -------------------------------------------------------------
+     * Control Interface
+     * -------------------------------------------------------------
+     */
+    /**
+     * Begin monitoring data connectivity.
+     *
+     * This is the first method called when this interface is used.
+     *
+     * @param context is the current Android context
+     * @param target is the Hander to which to return the events.
+     */
+    public void startMonitoring(Context context, Handler target);
+
+    /**
+     * Fetch NetworkInfo for the network
+     */
+    public NetworkInfo getNetworkInfo();
+
+    /**
+     * Return the LinkProperties for the connection.
+     *
+     * @return a copy of the LinkProperties, is never null.
+     */
+    public LinkProperties getLinkProperties();
+
+    /**
+     * A capability is an Integer/String pair, the capabilities
+     * are defined in the class LinkSocket#Key.
+     *
+     * @return a copy of this connections capabilities, may be empty but never null.
+     */
+    public LinkCapabilities getLinkCapabilities();
 
     /**
      * Return the system properties name associated with the tcp buffer sizes
      * for this network.
      */
-    public abstract String getTcpBufferSizesPropName();
-
-    /**
-     * Return the IP addresses of the DNS servers available for the mobile data
-     * network interface.
-     * @return a list of DNS addresses, with no holes.
-     */
-    public String[] getNameServers() {
-        return getNameServerList(mDnsPropNames);
-    }
-
-    /**
-     * Return the IP addresses of the DNS servers available for this
-     * network interface.
-     * @param propertyNames the names of the system properties whose values
-     * give the IP addresses. Properties with no values are skipped.
-     * @return an array of {@code String}s containing the IP addresses
-     * of the DNS servers, in dot-notation. This may have fewer
-     * non-null entries than the list of names passed in, since
-     * some of the passed-in names may have empty values.
-     */
-    static protected String[] getNameServerList(String[] propertyNames) {
-        String[] dnsAddresses = new String[propertyNames.length];
-        int i, j;
-
-        for (i = 0, j = 0; i < propertyNames.length; i++) {
-            String value = SystemProperties.get(propertyNames[i]);
-            // The GSM layer sometimes sets a bogus DNS server address of
-            // 0.0.0.0
-            if (!TextUtils.isEmpty(value) && !TextUtils.equals(value, "0.0.0.0")) {
-                dnsAddresses[j++] = value;
-            }
-        }
-        return dnsAddresses;
-    }
-
-    public void addPrivateDnsRoutes() {
-        if (DBG) {
-            Log.d(TAG, "addPrivateDnsRoutes for " + this +
-                    "(" + mInterfaceName + ") - mPrivateDnsRouteSet = "+mPrivateDnsRouteSet);
-        }
-        if (mInterfaceName != null && !mPrivateDnsRouteSet) {
-            for (String addrString : getNameServers()) {
-                if (addrString != null) {
-                    try {
-                        InetAddress inetAddress = InetAddress.getByName(addrString);
-                        if (DBG) Log.d(TAG, "  adding " + addrString);
-                        if (NetworkUtils.addHostRoute(mInterfaceName, inetAddress, null)) {
-                            mPrivateDnsRouteSet = true;
-                        }
-                    } catch (UnknownHostException e) {
-                        if (DBG) Log.d(TAG, " DNS address " + addrString + " : Exception " + e);
-                    }
-                }
-            }
-        }
-    }
-
-    public void removePrivateDnsRoutes() {
-        // TODO - we should do this explicitly but the NetUtils api doesnt
-        // support this yet - must remove all.  No worse than before
-        if (mInterfaceName != null && mPrivateDnsRouteSet) {
-            if (DBG) {
-                Log.d(TAG, "removePrivateDnsRoutes for " + mNetworkInfo.getTypeName() +
-                        " (" + mInterfaceName + ")");
-            }
-            NetworkUtils.removeHostRoutes(mInterfaceName);
-            mPrivateDnsRouteSet = false;
-        }
-    }
-
-    public void addDefaultRoute() {
-        if ((mInterfaceName != null) && (mDefaultGatewayAddr != 0)) {
-            if (DBG) {
-                Log.d(TAG, "addDefaultRoute for " + mNetworkInfo.getTypeName() +
-                        " (" + mInterfaceName + "), GatewayAddr=" + mDefaultGatewayAddr);
-            }
-            InetAddress inetAddress = NetworkUtils.intToInetAddress(mDefaultGatewayAddr);
-            if (inetAddress == null) {
-                if (DBG) Log.d(TAG, " Unable to add default route. mDefaultGatewayAddr Error");
-            } else {
-                if (!NetworkUtils.addDefaultRoute(mInterfaceName, inetAddress) && DBG) {
-                    Log.d(TAG, "  Unable to add default route.");
-                }
-            }
-        }
-    }
-
-    public void removeDefaultRoute() {
-        if (mInterfaceName != null) {
-            if (DBG) {
-                Log.d(TAG, "removeDefaultRoute for " + mNetworkInfo.getTypeName() + " (" +
-                        mInterfaceName + ")");
-            }
-            NetworkUtils.removeDefaultRoute(mInterfaceName);
-        }
-    }
-
-    /**
-     * Reads the network specific TCP buffer sizes from SystemProperties
-     * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
-     * wide use
-     */
-   public void updateNetworkSettings() {
-        String key = getTcpBufferSizesPropName();
-        String bufferSizes = SystemProperties.get(key);
-
-        if (bufferSizes.length() == 0) {
-            Log.w(TAG, key + " not found in system properties. Using defaults");
-
-            // Setting to default values so we won't be stuck to previous values
-            key = "net.tcp.buffersize.default";
-            bufferSizes = SystemProperties.get(key);
-        }
-
-        // Set values in kernel
-        if (bufferSizes.length() != 0) {
-            if (DBG) {
-                Log.v(TAG, "Setting TCP values: [" + bufferSizes
-                        + "] which comes from [" + key + "]");
-            }
-            setBufferSize(bufferSizes);
-        }
-    }
-
-    /**
-     * Release the wakelock, if any, that may be held while handling a
-     * disconnect operation.
-     */
-    public void releaseWakeLock() {
-    }
-
-    /**
-     * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max]
-     * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem
-     * 
-     * @param bufferSizes in the format of "readMin, readInitial, readMax,
-     *        writeMin, writeInitial, writeMax"
-     */
-    private void setBufferSize(String bufferSizes) {
-        try {
-            String[] values = bufferSizes.split(",");
-
-            if (values.length == 6) {
-              final String prefix = "/sys/kernel/ipv4/tcp_";
-                stringToFile(prefix + "rmem_min", values[0]);
-                stringToFile(prefix + "rmem_def", values[1]);
-                stringToFile(prefix + "rmem_max", values[2]);
-                stringToFile(prefix + "wmem_min", values[3]);
-                stringToFile(prefix + "wmem_def", values[4]);
-                stringToFile(prefix + "wmem_max", values[5]);
-            } else {
-                Log.w(TAG, "Invalid buffersize string: " + bufferSizes);
-            }
-        } catch (IOException e) {
-            Log.w(TAG, "Can't set tcp buffer sizes:" + e);
-        }
-    }
-
-    /**
-     * Writes string to file. Basically same as "echo -n $string > $filename"
-     * 
-     * @param filename
-     * @param string
-     * @throws IOException
-     */
-    private void stringToFile(String filename, String string) throws IOException {
-        FileWriter out = new FileWriter(filename);
-        try {
-            out.write(string);
-        } finally {
-            out.close();
-        }
-    }
-
-    /**
-     * Record the detailed state of a network, and if it is a
-     * change from the previous state, send a notification to
-     * any listeners.
-     * @param state the new @{code DetailedState}
-     */
-    public void setDetailedState(NetworkInfo.DetailedState state) {
-        setDetailedState(state, null, null);
-    }
-
-    /**
-     * Record the detailed state of a network, and if it is a
-     * change from the previous state, send a notification to
-     * any listeners.
-     * @param state the new @{code DetailedState}
-     * @param reason a {@code String} indicating a reason for the state change,
-     * if one was supplied. May be {@code null}.
-     * @param extraInfo optional {@code String} providing extra information about the state change
-     */
-    public void setDetailedState(NetworkInfo.DetailedState state, String reason, String extraInfo) {
-        if (DBG) Log.d(TAG, "setDetailed state, old ="+mNetworkInfo.getDetailedState()+" and new state="+state);
-        if (state != mNetworkInfo.getDetailedState()) {
-            boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING);
-            String lastReason = mNetworkInfo.getReason();
-            /*
-             * If a reason was supplied when the CONNECTING state was entered, and no
-             * reason was supplied for entering the CONNECTED state, then retain the
-             * reason that was supplied when going to CONNECTING.
-             */
-            if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
-                    && lastReason != null)
-                reason = lastReason;
-            mNetworkInfo.setDetailedState(state, reason, extraInfo);
-            Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
-            msg.sendToTarget();
-        }
-    }
-
-    protected void setDetailedStateInternal(NetworkInfo.DetailedState state) {
-        mNetworkInfo.setDetailedState(state, null, null);
-    }
-
-    public void setTeardownRequested(boolean isRequested) {
-        mTeardownRequested = isRequested;
-    }
-
-    public boolean isTeardownRequested() {
-        return mTeardownRequested;
-    }
-    
-    /**
-     * Send a  notification that the results of a scan for network access
-     * points has completed, and results are available.
-     */
-    protected void sendScanResultsAvailable() {
-        Message msg = mTarget.obtainMessage(EVENT_SCAN_RESULTS_AVAILABLE, mNetworkInfo);
-        msg.sendToTarget();
-    }
-
-    /**
-     * Record the roaming status of the device, and if it is a change from the previous
-     * status, send a notification to any listeners.
-     * @param isRoaming {@code true} if the device is now roaming, {@code false}
-     * if it is no longer roaming.
-     */
-    protected void setRoamingStatus(boolean isRoaming) {
-        if (isRoaming != mNetworkInfo.isRoaming()) {
-            mNetworkInfo.setRoaming(isRoaming);
-            Message msg = mTarget.obtainMessage(EVENT_ROAMING_CHANGED, mNetworkInfo);
-            msg.sendToTarget();
-        }
-    }
-
-    protected void setSubtype(int subtype, String subtypeName) {
-        if (mNetworkInfo.isConnected()) {
-            int oldSubtype = mNetworkInfo.getSubtype();
-            if (subtype != oldSubtype) {
-                mNetworkInfo.setSubtype(subtype, subtypeName);
-                Message msg = mTarget.obtainMessage(
-                        EVENT_NETWORK_SUBTYPE_CHANGED, oldSubtype, 0, mNetworkInfo);
-                msg.sendToTarget();
-            }
-        }
-    }
-
-    public abstract void startMonitoring();
+    public String getTcpBufferSizesPropName();
 
     /**
      * Disable connectivity to a network
      * @return {@code true} if a teardown occurred, {@code false} if the
      * teardown did not occur.
      */
-    public abstract boolean teardown();
+    public boolean teardown();
 
     /**
      * Reenable connectivity to a network after a {@link #teardown()}.
+     * @return {@code true} if we're connected or expect to be connected
      */
-    public abstract boolean reconnect();
+    public boolean reconnect();
 
     /**
      * Turn the wireless radio off for a network.
      * @param turnOn {@code true} to turn the radio on, {@code false}
      */
-    public abstract boolean setRadio(boolean turnOn);
+    public boolean setRadio(boolean turnOn);
 
     /**
      * Returns an indication of whether this network is available for
      * connections. A value of {@code false} means that some quasi-permanent
      * condition prevents connectivity to this network.
      */
-    public abstract boolean isAvailable();
+    public boolean isAvailable();
+
+    /**
+     * Fetch default gateway address for the network
+     */
+    public int getDefaultGatewayAddr();
 
     /**
      * Tells the underlying networking system that the caller wants to
@@ -390,7 +152,7 @@
      * implementation+feature combination, except that the value {@code -1}
      * always indicates failure.
      */
-    public abstract int startUsingNetworkFeature(String feature, int callingPid, int callingUid);
+    public int startUsingNetworkFeature(String feature, int callingPid, int callingUid);
 
     /**
      * Tells the underlying networking system that the caller is finished
@@ -404,23 +166,43 @@
      * implementation+feature combination, except that the value {@code -1}
      * always indicates failure.
      */
-    public abstract int stopUsingNetworkFeature(String feature, int callingPid, int callingUid);
+    public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid);
 
     /**
-     * Ensure that a network route exists to deliver traffic to the specified
-     * host via this network interface.
-     * @param hostAddress the IP address of the host to which the route is desired
-     * @return {@code true} on success, {@code false} on failure
+     * -------------------------------------------------------------
+     * Storage API used by ConnectivityService for saving
+     * Network specific information.
+     * -------------------------------------------------------------
      */
-    public boolean requestRouteToHost(InetAddress hostAddress) {
-        return false;
-    }
 
     /**
-     * Interprets scan results. This will be called at a safe time for
-     * processing, and from a safe thread.
+     * Check if private DNS route is set for the network
      */
-    public void interpretScanResultsAvailable() {
-    }
+    public boolean isPrivateDnsRouteSet();
+
+    /**
+     * Set a flag indicating private DNS route is set
+     */
+    public void privateDnsRouteSet(boolean enabled);
+
+    /**
+     * Check if default route is set
+     */
+    public boolean isDefaultRouteSet();
+
+    /**
+     * Set a flag indicating default route is set for the network
+     */
+    public void defaultRouteSet(boolean enabled);
+
+    /**
+     * Check if tear down was requested
+     */
+    public boolean isTeardownRequested();
+
+    /**
+     * Indicate tear down requested from connectivity
+     */
+    public void setTeardownRequested(boolean isRequested);
 
 }
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 5d4b099..8a653dd 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -52,7 +52,11 @@
           int prefixLength, String gw);
 
     /** Return the gateway address for the default route for the named interface. */
-    public native static int getDefaultRoute(String interfaceName);
+    public static InetAddress getDefaultRoute(String interfaceName) {
+        int addr = getDefaultRouteNative(interfaceName);
+        return intToInetAddress(addr);
+    }
+    private native static int getDefaultRouteNative(String interfaceName);
 
     /** Remove host routes that uses the named interface. */
     public native static int removeHostRoutes(String interfaceName);
@@ -142,6 +146,35 @@
     }
 
     /**
+     * Convert a IPv4 address from an InetAddress to an integer
+     * @param inetAddr is an InetAddress corresponding to the IPv4 address
+     * @return the IP address as an integer in network byte order
+     */
+    public static int inetAddressToInt(InetAddress inetAddr)
+            throws IllegalArgumentException {
+        byte [] addr = inetAddr.getAddress();
+        if (addr.length != 4) {
+            throw new IllegalArgumentException("Not an IPv4 address");
+        }
+        return ((addr[3] & 0xff) << 24) | ((addr[2] & 0xff) << 16) |
+                ((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
+    }
+
+    /**
+     * Convert a network prefix length to an IPv4 netmask integer
+     * @param prefixLength
+     * @return the IPv4 netmask as an integer in network byte order
+     */
+    public static int prefixLengthToNetmaskInt(int prefixLength)
+            throws IllegalArgumentException {
+        if (prefixLength < 0 || prefixLength > 32) {
+            throw new IllegalArgumentException("Invalid prefix length (0 <= prefix <= 32)");
+        }
+        int value = 0xffffffff << (32 - prefixLength);
+        return Integer.reverseBytes(value);
+    }
+
+    /**
      * Add a default route through the specified gateway.
      * @param interfaceName interface on which the route should be added
      * @param gw the IP address of the gateway to which the route is desired,
@@ -194,19 +227,4 @@
         }
         return addRoute(interfaceName, dstStr, prefixLength, gwStr) == 0;
     }
-
-    public static int v4StringToInt(String str) {
-        int result = 0;
-        String[] array = str.split("\\.");
-        if (array.length != 4) return 0;
-        try {
-            result = Integer.parseInt(array[3]);
-            result = (result << 8) + Integer.parseInt(array[2]);
-            result = (result << 8) + Integer.parseInt(array[1]);
-            result = (result << 8) + Integer.parseInt(array[0]);
-        } catch (NumberFormatException e) {
-            return 0;
-        }
-        return result;
-    }
 }
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index c1fa5b4..21c485e 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -16,30 +16,173 @@
 
 package android.net;
 
-import org.apache.http.HttpHost;
-
 import android.content.ContentResolver;
 import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.provider.Settings;
 import android.util.Log;
 
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
 import java.net.URI;
+import java.net.UnknownHostException;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import junit.framework.Assert;
 
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
+import org.apache.http.protocol.HttpContext;
+
 /**
  * A convenience class for accessing the user and default proxy
  * settings.
  */
-final public class Proxy {
+public final class Proxy {
 
     // Set to true to enable extra debugging.
-    static final private boolean DEBUG = false;
+    private static final boolean DEBUG = false;
 
-    static final public String PROXY_CHANGE_ACTION =
+    // Used to notify an app that's caching the default connection proxy
+    // that either the default connection or its proxy has changed
+    public static final String PROXY_CHANGE_ACTION =
         "android.intent.action.PROXY_CHANGE";
 
+    private static ReadWriteLock sProxyInfoLock = new ReentrantReadWriteLock();
+
+    private static SettingsObserver sGlobalProxyChangedObserver = null;
+
+    private static ProxySpec sGlobalProxySpec = null;
+
+    private static ConnectivityManager sConnectivityManager = null;
+
+    // Hostname / IP REGEX validation
+    // Matches blank input, ips, and domain names
+    private static final String NAME_IP_REGEX =
+        "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
+
+    private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
+
+    private static final Pattern HOSTNAME_PATTERN;
+
+    private static final String EXCLLIST_REGEXP = "$|^(.?" + NAME_IP_REGEX
+        + ")+(,(.?" + NAME_IP_REGEX + "))*$";
+
+    private static final Pattern EXCLLIST_PATTERN;
+
+    static {
+        HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
+        EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
+    }
+
+    // useful because it holds the processed exclusion list - don't want to reparse it each time
+    private static class ProxySpec {
+        String[] exclusionList;
+        InetSocketAddress address = null;
+        public ProxySpec() {
+            exclusionList = new String[0];
+        };
+    }
+
+    private static boolean isURLInExclusionList(String url, String[] exclusionList) {
+        if (url == null) {
+            return false;
+        }
+        Uri u = Uri.parse(url);
+        String urlDomain = u.getHost();
+        // If the domain is defined as ".android.com" or "android.com", we wish to match
+        // http://android.com as well as http://xxx.android.com , but not
+        // http://myandroid.com . This code works out the logic.
+        for (String excludedDomain : exclusionList) {
+            String dotDomain = "." + excludedDomain;
+            if (urlDomain.equals(excludedDomain)) {
+                return true;
+            }
+            if (urlDomain.endsWith(dotDomain)) {
+                return true;
+            }
+        }
+        // No match
+        return false;
+    }
+
+    private static String parseHost(String proxySpec) {
+        int i = proxySpec.indexOf(':');
+        if (i == -1) {
+            if (DEBUG) {
+                Assert.assertTrue(proxySpec.length() == 0);
+            }
+            return null;
+        }
+        return proxySpec.substring(0, i);
+    }
+
+    private static int parsePort(String proxySpec) {
+        int i = proxySpec.indexOf(':');
+        if (i == -1) {
+            if (DEBUG) {
+                Assert.assertTrue(proxySpec.length() == 0);
+            }
+            return -1;
+        }
+        if (DEBUG) {
+            Assert.assertTrue(i < proxySpec.length());
+        }
+        return Integer.parseInt(proxySpec.substring(i+1));
+    }
+
+    /**
+     * Return the proxy object to be used for the URL given as parameter.
+     * @param ctx A Context used to get the settings for the proxy host.
+     * @param url A URL to be accessed. Used to evaluate exclusion list.
+     * @return Proxy (java.net) object containing the host name. If the
+     *         user did not set a hostname it returns the default host.
+     *         A null value means that no host is to be used.
+     * {@hide}
+     */
+    public static final java.net.Proxy getProxy(Context ctx, String url) {
+        sProxyInfoLock.readLock().lock();
+        java.net.Proxy retval;
+        try {
+            if (sGlobalProxyChangedObserver == null) {
+                registerContentObserversReadLocked(ctx);
+                parseGlobalProxyInfoReadLocked(ctx);
+            }
+            if (sGlobalProxySpec != null) {
+                // Proxy defined - Apply exclusion rules
+                if (isURLInExclusionList(url, sGlobalProxySpec.exclusionList)) {
+                    // Return no proxy
+                    retval = java.net.Proxy.NO_PROXY;
+                } else {
+                    retval =
+                        new java.net.Proxy(java.net.Proxy.Type.HTTP, sGlobalProxySpec.address);
+                }
+            } else {
+                retval = getDefaultProxy(ctx, url);
+            }
+        } finally {
+            sProxyInfoLock.readLock().unlock();
+        }
+        if ((retval != java.net.Proxy.NO_PROXY) && (isLocalHost(url))) {
+            retval = java.net.Proxy.NO_PROXY;
+        }
+        return retval;
+    }
+
+    // TODO: deprecate this function
     /**
      * Return the proxy host set by the user.
      * @param ctx A Context used to get the settings for the proxy host.
@@ -47,83 +190,82 @@
      *         name it returns the default host. A null value means that no
      *         host is to be used.
      */
-    static final public String getHost(Context ctx) {
-        ContentResolver contentResolver = ctx.getContentResolver();
-        Assert.assertNotNull(contentResolver);
-        String host = Settings.Secure.getString(
-                contentResolver,
-                Settings.Secure.HTTP_PROXY);
-        if (host != null) {
-            int i = host.indexOf(':');
-            if (i == -1) {
-                if (DEBUG) {
-                    Assert.assertTrue(host.length() == 0);
-                }
-                return null;
-            }
-            return host.substring(0, i);
+    public static final String getHost(Context ctx) {
+        java.net.Proxy proxy = getProxy(ctx, null);
+        if (proxy == java.net.Proxy.NO_PROXY) return null;
+        try {
+            return ((InetSocketAddress)(proxy.address())).getHostName();
+        } catch (Exception e) {
+            return null;
         }
-        return getDefaultHost();
     }
 
+    // TODO: deprecate this function
     /**
      * Return the proxy port set by the user.
      * @param ctx A Context used to get the settings for the proxy port.
      * @return The port number to use or -1 if no proxy is to be used.
      */
-    static final public int getPort(Context ctx) {
-        ContentResolver contentResolver = ctx.getContentResolver();
-        Assert.assertNotNull(contentResolver);
-        String host = Settings.Secure.getString(
-                contentResolver,
-                Settings.Secure.HTTP_PROXY);
-        if (host != null) {
-            int i = host.indexOf(':');
-            if (i == -1) {
-                if (DEBUG) {
-                    Assert.assertTrue(host.length() == 0);
-                }
-                return -1;
-            }
-            if (DEBUG) {
-                Assert.assertTrue(i < host.length());
-            }
-            return Integer.parseInt(host.substring(i+1));
+    public static final int getPort(Context ctx) {
+        java.net.Proxy proxy = getProxy(ctx, null);
+        if (proxy == java.net.Proxy.NO_PROXY) return -1;
+        try {
+            return ((InetSocketAddress)(proxy.address())).getPort();
+        } catch (Exception e) {
+            return -1;
         }
-        return getDefaultPort();
     }
 
+    // TODO: deprecate this function
     /**
      * Return the default proxy host specified by the carrier.
      * @return String containing the host name or null if there is no proxy for
      * this carrier.
      */
-    static final public String getDefaultHost() {
-        String host = SystemProperties.get("net.gprs.http-proxy");
-        if (host != null) {
-            Uri u = Uri.parse(host);
-            host = u.getHost();
-            return host;
-        } else {
-            return null;
-        }
+    public static final String getDefaultHost() {
+        return null;
     }
 
+    // TODO: deprecate this function
     /**
      * Return the default proxy port specified by the carrier.
      * @return The port number to be used with the proxy host or -1 if there is
      * no proxy for this carrier.
      */
-    static final public int getDefaultPort() {
-        String host = SystemProperties.get("net.gprs.http-proxy");
-        if (host != null) {
-            Uri u = Uri.parse(host);
-            return u.getPort();
-        } else {
-            return -1;
-        }
+    public static final int getDefaultPort() {
+        return -1;
     }
 
+    // TODO - cache the details for each network so we don't have to fetch and parse
+    // on each request
+    private static final java.net.Proxy getDefaultProxy(Context context, String url) {
+        if (sConnectivityManager == null) {
+            sConnectivityManager = (ConnectivityManager)context.getSystemService(
+                    Context.CONNECTIVITY_SERVICE);
+        }
+        if (sConnectivityManager == null) return java.net.Proxy.NO_PROXY;
+
+        LinkProperties linkProperties = sConnectivityManager.getActiveLinkProperties();
+
+        if (linkProperties != null) {
+            ProxyProperties proxyProperties = linkProperties.getHttpProxy();
+
+            if (proxyProperties != null) {
+                String exclusionList = proxyProperties.getExclusionList();
+                SocketAddress socketAddr = proxyProperties.getSocketAddress();
+                if (socketAddr != null) {
+                    String[] parsedExclusionArray =
+                            parsedExclusionArray = parseExclusionList(exclusionList);
+                    if (!isURLInExclusionList(url, parsedExclusionArray)) {
+                        return new java.net.Proxy(java.net.Proxy.Type.HTTP, socketAddr);
+                    }
+                }
+            }
+        }
+        return java.net.Proxy.NO_PROXY;
+    }
+
+    // TODO: remove this function / deprecate
     /**
      * Returns the preferred proxy to be used by clients. This is a wrapper
      * around {@link android.net.Proxy#getHost()}. Currently no proxy will
@@ -136,61 +278,206 @@
      * android.permission.ACCESS_NETWORK_STATE
      * @return The preferred proxy to be used by clients, or null if there
      * is no proxy.
-     *
      * {@hide}
      */
-    static final public HttpHost getPreferredHttpHost(Context context,
+    public static final HttpHost getPreferredHttpHost(Context context,
             String url) {
-        if (!isLocalHost(url) && !isNetworkWifi(context)) {
-            final String proxyHost = Proxy.getHost(context);
-            if (proxyHost != null) {
-                return new HttpHost(proxyHost, Proxy.getPort(context), "http");
-            }
+        java.net.Proxy prefProxy = getProxy(context, url);
+        if (prefProxy.equals(java.net.Proxy.NO_PROXY)) {
+            return null;
+        } else {
+            InetSocketAddress sa = (InetSocketAddress)prefProxy.address();
+            return new HttpHost(sa.getHostName(), sa.getPort(), "http");
         }
-
-        return null;
     }
 
-    static final private boolean isLocalHost(String url) {
+    private static final boolean isLocalHost(String url) {
         if (url == null) {
             return false;
         }
-
         try {
             final URI uri = URI.create(url);
             final String host = uri.getHost();
             if (host != null) {
-                // TODO: InetAddress.isLoopbackAddress should be used to check
-                // for localhost. However no public factory methods exist which
-                // can be used without triggering DNS lookup if host is not localhost.
-                if (host.equalsIgnoreCase("localhost") ||
-                        host.equals("127.0.0.1") ||
-                        host.equals("[::1]")) {
+                if (host.equalsIgnoreCase("localhost")) {
                     return true;
                 }
+                // Check we have a numeric address so we don't cause a DNS lookup in getByName.
+                if (InetAddress.isNumeric(host)) {
+                    if (InetAddress.getByName(host).isLoopbackAddress()) {
+                        return true;
+                    }
+                }
             }
+        } catch (UnknownHostException ignored) {
+            // Can't happen for a numeric address (InetAddress.getByName).
         } catch (IllegalArgumentException iex) {
             // Ignore (URI.create)
         }
-
         return false;
     }
 
-    static final private boolean isNetworkWifi(Context context) {
-        if (context == null) {
-            return false;
+    private static class SettingsObserver extends ContentObserver {
+
+        private Context mContext;
+
+        SettingsObserver(Context ctx) {
+            super(new Handler(ctx.getMainLooper()));
+            mContext = ctx;
         }
 
-        final ConnectivityManager connectivity = (ConnectivityManager)
-            context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        if (connectivity != null) {
-            final NetworkInfo info = connectivity.getActiveNetworkInfo();
-            if (info != null &&
-                    info.getType() == ConnectivityManager.TYPE_WIFI) {
-                return true;
+        @Override
+        public void onChange(boolean selfChange) {
+            sProxyInfoLock.readLock().lock();
+            parseGlobalProxyInfoReadLocked(mContext);
+            sProxyInfoLock.readLock().unlock();
+        }
+    }
+
+    private static final void registerContentObserversReadLocked(Context ctx) {
+        Uri uriGlobalProxy = Settings.Secure.getUriFor(Settings.Secure.HTTP_PROXY);
+        Uri uriGlobalExclList =
+            Settings.Secure.getUriFor(Settings.Secure.HTTP_PROXY_EXCLUSION_LIST);
+
+        // No lock upgrading (from read to write) allowed
+        sProxyInfoLock.readLock().unlock();
+        sProxyInfoLock.writeLock().lock();
+        try {
+            sGlobalProxyChangedObserver = new SettingsObserver(ctx);
+        } finally {
+            // Downgrading locks (from write to read) is allowed
+            sProxyInfoLock.readLock().lock();
+            sProxyInfoLock.writeLock().unlock();
+        }
+        ctx.getContentResolver().registerContentObserver(uriGlobalProxy, false,
+                sGlobalProxyChangedObserver);
+        ctx.getContentResolver().registerContentObserver(uriGlobalExclList, false,
+                sGlobalProxyChangedObserver);
+    }
+
+    private static final void parseGlobalProxyInfoReadLocked(Context ctx) {
+        ContentResolver contentResolver = ctx.getContentResolver();
+        String proxyHost =  Settings.Secure.getString(
+                contentResolver,
+                Settings.Secure.HTTP_PROXY);
+        if (TextUtils.isEmpty(proxyHost)) {
+            // Clear signal
+            sProxyInfoLock.readLock().unlock();
+            sProxyInfoLock.writeLock().lock();
+            sGlobalProxySpec = null;
+            sProxyInfoLock.readLock().lock();
+            sProxyInfoLock.writeLock().unlock();
+            return;
+        }
+        String exclusionListSpec = Settings.Secure.getString(
+                contentResolver,
+                Settings.Secure.HTTP_PROXY_EXCLUSION_LIST);
+        String host = parseHost(proxyHost);
+        int port = parsePort(proxyHost);
+        ProxySpec tmpProxySpec = null;
+        if (proxyHost != null) {
+            tmpProxySpec = new ProxySpec();
+            tmpProxySpec.address = new InetSocketAddress(host, port);
+            tmpProxySpec.exclusionList = parseExclusionList(exclusionListSpec);
+        }
+        sProxyInfoLock.readLock().unlock();
+        sProxyInfoLock.writeLock().lock();
+        sGlobalProxySpec = tmpProxySpec;
+        sProxyInfoLock.readLock().lock();
+        sProxyInfoLock.writeLock().unlock();
+    }
+
+    private static String[] parseExclusionList(String exclusionList) {
+        String[] processedArray = new String[0];
+        if (!TextUtils.isEmpty(exclusionList)) {
+            String[] exclusionListArray = exclusionList.toLowerCase().split(",");
+            processedArray = new String[exclusionListArray.length];
+            for (int i = 0; i < exclusionListArray.length; i++) {
+                String entry = exclusionListArray[i].trim();
+                if (entry.startsWith(".")) {
+                    entry = entry.substring(1);
+                }
+                processedArray[i] = entry;
             }
         }
-
-        return false;
+        return processedArray;
     }
-};
+
+    /**
+     * Validate syntax of hostname, port and exclusion list entries
+     * {@hide}
+     */
+    public static void validate(String hostname, String port, String exclList) {
+        Matcher match = HOSTNAME_PATTERN.matcher(hostname);
+        Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
+
+        if (!match.matches()) {
+            throw new IllegalArgumentException();
+        }
+
+        if (!listMatch.matches()) {
+            throw new IllegalArgumentException();
+        }
+
+        if (hostname.length() > 0 && port.length() == 0) {
+            throw new IllegalArgumentException();
+        }
+
+        if (port.length() > 0) {
+            if (hostname.length() == 0) {
+                throw new IllegalArgumentException();
+            }
+            int portVal = -1;
+            try {
+                portVal = Integer.parseInt(port);
+            } catch (NumberFormatException ex) {
+                throw new IllegalArgumentException();
+            }
+            if (portVal <= 0 || portVal > 0xFFFF) {
+                throw new IllegalArgumentException();
+            }
+        }
+    }
+
+    static class AndroidProxySelectorRoutePlanner
+            extends org.apache.http.impl.conn.ProxySelectorRoutePlanner {
+
+        private Context mContext;
+
+        public AndroidProxySelectorRoutePlanner(SchemeRegistry schreg, ProxySelector prosel,
+                Context context) {
+            super(schreg, prosel);
+            mContext = context;
+        }
+
+        @Override
+        protected java.net.Proxy chooseProxy(List<java.net.Proxy> proxies, HttpHost target,
+                HttpRequest request, HttpContext context) {
+            return getProxy(mContext, target.getHostName());
+        }
+
+        @Override
+        protected HttpHost determineProxy(HttpHost target, HttpRequest request,
+                HttpContext context) {
+            return getPreferredHttpHost(mContext, target.getHostName());
+        }
+
+        @Override
+        public HttpRoute determineRoute(HttpHost target, HttpRequest request,
+                HttpContext context) {
+            HttpHost proxy = getPreferredHttpHost(mContext, target.getHostName());
+            if (proxy == null) {
+                return new HttpRoute(target);
+            } else {
+                return new HttpRoute(target, null, proxy, false);
+            }
+        }
+    }
+
+    /** @hide */
+    public static final HttpRoutePlanner getAndroidProxySelectorRoutePlanner(Context context) {
+        AndroidProxySelectorRoutePlanner ret = new AndroidProxySelectorRoutePlanner(
+                new SchemeRegistry(), ProxySelector.getDefault(), context);
+        return ret;
+    }
+}
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java
new file mode 100644
index 0000000..5fd0d89
--- /dev/null
+++ b/core/java/android/net/ProxyProperties.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+
+/**
+ * A container class for the http proxy info
+ * @hide
+ */
+public class ProxyProperties implements Parcelable {
+
+    private InetSocketAddress mProxy;
+    private String mExclusionList;
+
+    public ProxyProperties() {
+    }
+
+    // copy constructor instead of clone
+    public ProxyProperties(ProxyProperties source) {
+        if (source != null) {
+            mProxy = source.getSocketAddress();
+            String exclusionList = source.getExclusionList();
+            if (exclusionList != null) {
+                mExclusionList = new String(exclusionList);
+            }
+        }
+    }
+
+    public InetSocketAddress getSocketAddress() {
+        return mProxy;
+    }
+
+    public void setSocketAddress(InetSocketAddress proxy) {
+        mProxy = proxy;
+    }
+
+    public String getExclusionList() {
+        return mExclusionList;
+    }
+
+    public void setExclusionList(String exclusionList) {
+        mExclusionList = exclusionList;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if (mProxy != null) {
+            sb.append(mProxy.toString());
+            if (mExclusionList != null) {
+                    sb.append(" xl=").append(mExclusionList);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Implement the Parcelable interface
+     * @hide
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        String host = null;
+        if (mProxy != null) {
+            try {
+                InetAddress addr = mProxy.getAddress();
+                if (addr != null) {
+                    host = addr.getHostAddress();
+                } else {
+                    /* Does not resolve when addr is null */
+                    host = mProxy.getHostName();
+                }
+            } catch (Exception e) { }
+        }
+
+        if (host != null) {
+            dest.writeByte((byte)1);
+            dest.writeString(host);
+            dest.writeInt(mProxy.getPort());
+        } else {
+            dest.writeByte((byte)0);
+        }
+        dest.writeString(mExclusionList);
+    }
+
+    /**
+     * Implement the Parcelable interface.
+     * @hide
+     */
+    public static final Creator<ProxyProperties> CREATOR =
+        new Creator<ProxyProperties>() {
+            public ProxyProperties createFromParcel(Parcel in) {
+                ProxyProperties proxyProperties = new ProxyProperties();
+                if (in.readByte() == 1) {
+                    try {
+                        String host = in.readString();
+                        int port = in.readInt();
+                        proxyProperties.setSocketAddress(InetSocketAddress.createUnresolved(
+                                host, port));
+                    } catch (IllegalArgumentException e) { }
+                }
+                proxyProperties.setExclusionList(in.readString());
+                return proxyProperties;
+            }
+
+            public ProxyProperties[] newArray(int size) {
+                return new ProxyProperties[size];
+            }
+        };
+}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 47faaba..3b21590 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -28,8 +28,10 @@
 import java.util.AbstractList;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.RandomAccess;
+import java.util.Set;
 
 /**
  * Immutable URI reference. A URI reference includes a URI and a fragment, the
@@ -47,7 +49,7 @@
     /*
 
     This class aims to do as little up front work as possible. To accomplish
-    that, we vary the implementation dependending on what the user passes in.
+    that, we vary the implementation depending on what the user passes in.
     For example, we have one implementation if the user passes in a
     URI string (StringUri) and another if the user passes in the
     individual components (OpaqueUri).
@@ -1253,14 +1255,16 @@
      * concurrent use.
      *
      * <p>An absolute hierarchical URI reference follows the pattern:
-     * {@code &lt;scheme&gt;://&lt;authority&gt;&lt;absolute path&gt;?&lt;query&gt;#&lt;fragment&gt;}
+     * {@code <scheme>://<authority><absolute path>?<query>#<fragment>}
      *
      * <p>Relative URI references (which are always hierarchical) follow one
-     * of two patterns: {@code &lt;relative or absolute path&gt;?&lt;query&gt;#&lt;fragment&gt;}
-     * or {@code //&lt;authority&gt;&lt;absolute path&gt;?&lt;query&gt;#&lt;fragment&gt;}
+     * of two patterns: {@code <relative or absolute path>?<query>#<fragment>}
+     * or {@code //<authority><absolute path>?<query>#<fragment>}
      *
      * <p>An opaque URI follows this pattern:
-     * {@code &lt;scheme&gt;:&lt;opaque part&gt;#&lt;fragment&gt;}
+     * {@code <scheme>:<opaque part>#<fragment>}
+     * 
+     * <p>Use {@link Uri#buildUpon()} to obtain a builder representing an existing URI.
      */
     public static final class Builder {
 
@@ -1447,6 +1451,13 @@
         }
 
         /**
+         * Clears the the previously set query.
+         */
+        public Builder clearQuery() {
+          return query((Part) null);
+        }
+
+        /**
          * Constructs a Uri with the current attributes.
          *
          * @throws UnsupportedOperationException if the URI is opaque and the
@@ -1491,19 +1502,60 @@
     }
 
     /**
+     * Returns a set of the unique names of all query parameters. Iterating
+     * over the set will return the names in order of their first occurrence.
+     *
+     * @throws UnsupportedOperationException if this isn't a hierarchical URI
+     *
+     * @return a set of decoded names
+     */
+    public Set<String> getQueryParameterNames() {
+        if (isOpaque()) {
+            throw new UnsupportedOperationException(NOT_HIERARCHICAL);
+        }
+
+        String query = getEncodedQuery();
+        if (query == null) {
+            return Collections.emptySet();
+        }
+
+        Set<String> names = new LinkedHashSet<String>();
+        int start = 0;
+        do {
+            int next = query.indexOf('&', start);
+            int end = (next == -1) ? query.length() : next;
+
+            int separator = query.indexOf('=', start);
+            if (separator > end || separator == -1) {
+                separator = end;
+            }
+
+            String name = query.substring(start, separator);
+            names.add(decode(name));
+
+            // Move start to end of name.
+            start = end + 1;
+        } while (start < query.length());
+
+        return Collections.unmodifiableSet(names);
+    }
+
+    /**
      * Searches the query string for parameter values with the given key.
      *
      * @param key which will be encoded
      *
      * @throws UnsupportedOperationException if this isn't a hierarchical URI
      * @throws NullPointerException if key is null
-     *
      * @return a list of decoded values
      */
     public List<String> getQueryParameters(String key) {
         if (isOpaque()) {
             throw new UnsupportedOperationException(NOT_HIERARCHICAL);
         }
+        if (key == null) {
+          throw new NullPointerException("key");
+        }
 
         String query = getEncodedQuery();
         if (query == null) {
@@ -1517,39 +1569,34 @@
             throw new AssertionError(e);
         }
 
-        // Prepend query with "&" making the first parameter the same as the
-        // rest.
-        query = "&" + query;
-
-        // Parameter prefix.
-        String prefix = "&" + encodedKey + "=";
-
         ArrayList<String> values = new ArrayList<String>();
 
         int start = 0;
-        int length = query.length();
-        while (start < length) {
-            start = query.indexOf(prefix, start);
+        do {
+            int nextAmpersand = query.indexOf('&', start);
+            int end = nextAmpersand != -1 ? nextAmpersand : query.length();
 
-            if (start == -1) {
-                // No more values.
+            int separator = query.indexOf('=', start);
+            if (separator > end || separator == -1) {
+                separator = end;
+            }
+
+            if (separator - start == encodedKey.length()
+                    && query.regionMatches(start, encodedKey, 0, encodedKey.length())) {
+                if (separator == end) {
+                  values.add("");
+                } else {
+                  values.add(decode(query.substring(separator + 1, end)));
+                }
+            }
+
+            // Move start to end of name.
+            if (nextAmpersand != -1) {
+                start = nextAmpersand + 1;
+            } else {
                 break;
             }
-
-            // Move start to start of value.
-            start += prefix.length();
-
-            // Find end of value.
-            int end = query.indexOf('&', start);
-            if (end == -1) {
-                end = query.length();
-            }
-
-            String value = query.substring(start, end);
-            values.add(decode(value));
-
-            start = end;
-        }
+        } while (true);
 
         return Collections.unmodifiableList(values);
     }
@@ -1560,7 +1607,6 @@
      * @param key which will be encoded
      * @throws UnsupportedOperationException if this isn't a hierarchical URI
      * @throws NullPointerException if key is null
-     *
      * @return the decoded value or null if no parameter is found
      */
     public String getQueryParameter(String key) {
@@ -1568,7 +1614,7 @@
             throw new UnsupportedOperationException(NOT_HIERARCHICAL);
         }
         if (key == null) {
-          throw new NullPointerException("key");
+            throw new NullPointerException("key");
         }
 
         final String query = getEncodedQuery();
@@ -1577,37 +1623,54 @@
         }
 
         final String encodedKey = encode(key, null);
-        final int encodedKeyLength = encodedKey.length();
+        final int length = query.length();
+        int start = 0;
+        do {
+            int nextAmpersand = query.indexOf('&', start);
+            int end = nextAmpersand != -1 ? nextAmpersand : length;
 
-        int encodedKeySearchIndex = 0;
-        final int encodedKeySearchEnd = query.length() - (encodedKeyLength + 1);
+            int separator = query.indexOf('=', start);
+            if (separator > end || separator == -1) {
+                separator = end;
+            }
 
-        while (encodedKeySearchIndex <= encodedKeySearchEnd) {
-            int keyIndex = query.indexOf(encodedKey, encodedKeySearchIndex);
-            if (keyIndex == -1) {
-                break;
-            }
-            final int equalsIndex = keyIndex + encodedKeyLength;
-            if (equalsIndex >= query.length()) {
-                break;
-            }
-            if (query.charAt(equalsIndex) != '=') {
-                encodedKeySearchIndex = equalsIndex + 1;
-                continue;
-            }
-            if (keyIndex == 0 || query.charAt(keyIndex - 1) == '&') {
-                int end = query.indexOf('&', equalsIndex);
-                if (end == -1) {
-                    end = query.length();
+            if (separator - start == encodedKey.length()
+                    && query.regionMatches(start, encodedKey, 0, encodedKey.length())) {
+                if (separator == end) {
+                  return "";
+                } else {
+                  return decode(query.substring(separator + 1, end));
                 }
-                return decode(query.substring(equalsIndex + 1, end));
-            } else {
-                encodedKeySearchIndex = equalsIndex + 1;
             }
-        }
+
+            // Move start to end of name.
+            if (nextAmpersand != -1) {
+                start = nextAmpersand + 1;
+            } else {
+                break;
+            }
+        } while (true);
         return null;
     }
 
+    /**
+     * Searches the query string for the first value with the given key and interprets it
+     * as a boolean value. "false" and "0" are interpreted as <code>false</code>, everything
+     * else is interpreted as <code>true</code>.
+     *
+     * @param key which will be decoded
+     * @param defaultValue the default value to return if there is no query parameter for key
+     * @return the boolean interpretation of the query parameter key
+     */
+    public boolean getBooleanQueryParameter(String key, boolean defaultValue) {
+        String flag = getQueryParameter(key);
+        if (flag == null) {
+            return defaultValue;
+        }
+        flag = flag.toLowerCase();
+        return (!"false".equals(flag) && !"0".equals(flag));
+    }
+
     /** Identifies a null parcelled Uri. */
     private static final int NULL_TYPE_ID = 0;
 
diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java
index 4101ab4..052f921 100644
--- a/core/java/android/net/WebAddress.java
+++ b/core/java/android/net/WebAddress.java
@@ -39,13 +39,11 @@
  */
 public class WebAddress {
 
-    private final static String LOGTAG = "http";
-
-    public String mScheme;
-    public String mHost;
-    public int mPort;
-    public String mPath;
-    public String mAuthInfo;
+    private String mScheme;
+    private String mHost;
+    private int mPort;
+    private String mPath;
+    private String mAuthInfo;
 
     static final int MATCH_GROUP_SCHEME = 1;
     static final int MATCH_GROUP_AUTHORITY = 2;
@@ -56,7 +54,7 @@
     static Pattern sAddressPattern = Pattern.compile(
             /* scheme    */ "(?:(http|https|file)\\:\\/\\/)?" +
             /* authority */ "(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" +
-            /* host      */ "([-" + GOOD_IRI_CHAR + "%_]+(?:\\.[-" + GOOD_IRI_CHAR + "%_]+)*|\\[[0-9a-fA-F:\\.]+\\])?" +
+            /* host      */ "([" + GOOD_IRI_CHAR + "%_-][" + GOOD_IRI_CHAR + "%_\\.-]*|\\[[0-9a-fA-F:\\.]+\\])?" +
             /* port      */ "(?:\\:([0-9]*))?" +
             /* path      */ "(\\/?[^#]*)?" +
             /* anchor    */ ".*", Pattern.CASE_INSENSITIVE);
@@ -122,6 +120,7 @@
         if (mScheme.equals("")) mScheme = "http";
     }
 
+    @Override
     public String toString() {
         String port = "";
         if ((mPort != 443 && mScheme.equals("https")) ||
@@ -135,4 +134,44 @@
 
         return mScheme + "://" + authInfo + mHost + port + mPath;
     }
+
+    public void setScheme(String scheme) {
+      mScheme = scheme;
+    }
+
+    public String getScheme() {
+      return mScheme;
+    }
+
+    public void setHost(String host) {
+      mHost = host;
+    }
+
+    public String getHost() {
+      return mHost;
+    }
+
+    public void setPort(int port) {
+      mPort = port;
+    }
+
+    public int getPort() {
+      return mPort;
+    }
+
+    public void setPath(String path) {
+      mPath = path;
+    }
+
+    public String getPath() {
+      return mPath;
+    }
+
+    public void setAuthInfo(String authInfo) {
+      mAuthInfo = authInfo;
+    }
+
+    public String getAuthInfo() {
+      return mAuthInfo;
+    }
 }
diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java
index e07ee59..915e342 100644
--- a/core/java/android/net/http/AndroidHttpClient.java
+++ b/core/java/android/net/http/AndroidHttpClient.java
@@ -61,6 +61,7 @@
 import android.net.SSLCertificateSocketFactory;
 import android.net.SSLSessionCache;
 import android.os.Looper;
+import android.util.Base64;
 import android.util.Log;
 
 /**
@@ -81,6 +82,11 @@
 
     private static final String TAG = "AndroidHttpClient";
 
+    private static String[] textContentTypes = new String[] {
+            "text/",
+            "application/xml",
+            "application/json"
+    };
 
     /** Interceptor throws an exception if the executing thread is blocked */
     private static final HttpRequestInterceptor sThreadCheckInterceptor =
@@ -358,7 +364,7 @@
         }
         if (level < Log.VERBOSE || level > Log.ASSERT) {
             throw new IllegalArgumentException("Level is out of range ["
-                + Log.VERBOSE + ".." + Log.ASSERT + "]");    
+                + Log.VERBOSE + ".." + Log.ASSERT + "]");
         }
 
         curlConfiguration = new LoggingConfiguration(name, level);
@@ -431,12 +437,17 @@
                 if (entity.getContentLength() < 1024) {
                     ByteArrayOutputStream stream = new ByteArrayOutputStream();
                     entity.writeTo(stream);
-                    String entityString = stream.toString();
 
-                    // TODO: Check the content type, too.
-                    builder.append(" --data-ascii \"")
-                            .append(entityString)
-                            .append("\"");
+                    if (isBinaryContent(request)) {
+                        String base64 = Base64.encodeToString(stream.toByteArray(), Base64.NO_WRAP);
+                        builder.insert(0, "echo '" + base64 + "' | base64 -d > /tmp/$$.bin; ");
+                        builder.append(" --data-binary @/tmp/$$.bin");
+                    } else {
+                        String entityString = stream.toString();
+                        builder.append(" --data-ascii \"")
+                                .append(entityString)
+                                .append("\"");
+                    }
                 } else {
                     builder.append(" [TOO MUCH DATA TO INCLUDE]");
                 }
@@ -446,6 +457,30 @@
         return builder.toString();
     }
 
+    private static boolean isBinaryContent(HttpUriRequest request) {
+        Header[] headers;
+        headers = request.getHeaders(Headers.CONTENT_ENCODING);
+        if (headers != null) {
+            for (Header header : headers) {
+                if ("gzip".equalsIgnoreCase(header.getValue())) {
+                    return true;
+                }
+            }
+        }
+
+        headers = request.getHeaders(Headers.CONTENT_TYPE);
+        if (headers != null) {
+            for (Header header : headers) {
+                for (String contentType : textContentTypes) {
+                    if (header.getValue().startsWith(contentType)) {
+                        return false;
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
     /**
      * Returns the date of the given HTTP date string. This method can identify
      * and parse the date formats emitted by common HTTP servers, such as
diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java
index 4ca5903..218df75 100644
--- a/core/java/android/net/http/CertificateChainValidator.java
+++ b/core/java/android/net/http/CertificateChainValidator.java
@@ -80,14 +80,10 @@
             throws IOException {
         X509Certificate[] serverCertificates = null;
 
-        // start handshake, close the socket if we fail
-        try {
-            sslSocket.setUseClientMode(true);
-            sslSocket.startHandshake();
-        } catch (IOException e) {
-            closeSocketThrowException(
-                sslSocket, e.getMessage(),
-                "failed to perform SSL handshake");
+        // get a valid SSLSession, close the socket if we fail
+        SSLSession sslSession = sslSession = sslSocket.getSession();
+        if (!sslSession.isValid()) {
+            closeSocketThrowException(sslSocket, "failed to perform SSL handshake");
         }
 
         // retrieve the chain of the server peer certificates
@@ -133,57 +129,6 @@
             }
         }
 
-        // Clean up the certificates chain and build a new one.
-        // Theoretically, we shouldn't have to do this, but various web servers
-        // in practice are mis-configured to have out-of-order certificates or
-        // expired self-issued root certificate.
-        int chainLength = serverCertificates.length;
-        if (serverCertificates.length > 1) {
-          // 1. we clean the received certificates chain.
-          // We start from the end-entity certificate, tracing down by matching
-          // the "issuer" field and "subject" field until we can't continue.
-          // This helps when the certificates are out of order or
-          // some certificates are not related to the site.
-          int currIndex;
-          for (currIndex = 0; currIndex < serverCertificates.length; ++currIndex) {
-            boolean foundNext = false;
-            for (int nextIndex = currIndex + 1;
-                 nextIndex < serverCertificates.length;
-                 ++nextIndex) {
-              if (serverCertificates[currIndex].getIssuerDN().equals(
-                  serverCertificates[nextIndex].getSubjectDN())) {
-                foundNext = true;
-                // Exchange certificates so that 0 through currIndex + 1 are in proper order
-                if (nextIndex != currIndex + 1) {
-                  X509Certificate tempCertificate = serverCertificates[nextIndex];
-                  serverCertificates[nextIndex] = serverCertificates[currIndex + 1];
-                  serverCertificates[currIndex + 1] = tempCertificate;
-                }
-                break;
-              }
-            }
-            if (!foundNext) break;
-          }
-
-          // 2. we exam if the last traced certificate is self issued and it is expired.
-          // If so, we drop it and pass the rest to checkServerTrusted(), hoping we might
-          // have a similar but unexpired trusted root.
-          chainLength = currIndex + 1;
-          X509Certificate lastCertificate = serverCertificates[chainLength - 1];
-          Date now = new Date();
-          if (lastCertificate.getSubjectDN().equals(lastCertificate.getIssuerDN())
-              && now.after(lastCertificate.getNotAfter())) {
-            --chainLength;
-          }
-        }
-
-        // 3. Now we copy the newly built chain into an appropriately sized array.
-        X509Certificate[] newServerCertificates = null;
-        newServerCertificates = new X509Certificate[chainLength];
-        for (int i = 0; i < chainLength; ++i) {
-          newServerCertificates[i] = serverCertificates[i];
-        }
-
         // first, we validate the new chain using the standard validation
         // solution; if we do not find any errors, we are done; if we
         // fail the standard validation, we re-validate again below,
@@ -192,7 +137,7 @@
         //
         try {
             SSLParametersImpl.getDefaultTrustManager().checkServerTrusted(
-                newServerCertificates, "RSA");
+                serverCertificates, "RSA");
 
             // no errors!!!
             return null;
diff --git a/core/java/android/net/http/RequestQueue.java b/core/java/android/net/http/RequestQueue.java
index a31639f..ce6b1ad 100644
--- a/core/java/android/net/http/RequestQueue.java
+++ b/core/java/android/net/http/RequestQueue.java
@@ -327,10 +327,10 @@
 
         /* Create and queue request */
         Request req;
-        HttpHost httpHost = new HttpHost(uri.mHost, uri.mPort, uri.mScheme);
+        HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
 
         // set up request
-        req = new Request(method, httpHost, mProxyHost, uri.mPath, bodyProvider,
+        req = new Request(method, httpHost, mProxyHost, uri.getPath(), bodyProvider,
                           bodyLength, eventHandler, headers);
 
         queueRequest(req, false);
@@ -375,9 +375,9 @@
             HttpLog.v("RequestQueue.dispatchSynchronousRequest " + uri);
         }
 
-        HttpHost host = new HttpHost(uri.mHost, uri.mPort, uri.mScheme);
+        HttpHost host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
 
-        Request req = new Request(method, host, mProxyHost, uri.mPath,
+        Request req = new Request(method, host, mProxyHost, uri.getPath(),
                 bodyProvider, bodyLength, eventHandler, headers);
 
         // Open a new connection that uses our special RequestFeeder
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 5fb1d7c..f0309d6 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -16,16 +16,16 @@
 
 package android.os;
 
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.Callable;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-import java.util.concurrent.CancellationException;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -36,8 +36,8 @@
  * <p>An asynchronous task is defined by a computation that runs on a background thread and
  * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
  * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
- * and 4 steps, called <code>begin</code>, <code>doInBackground</code>,
- * <code>processProgress</code> and <code>end</code>.</p>
+ * and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
+ * <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
  *
  * <h2>Usage</h2>
  * <p>AsyncTask must be subclassed to be used. The subclass will override at least
@@ -123,6 +123,16 @@
  *     <li>The task can be executed only once (an exception will be thrown if
  *     a second execution is attempted.)</li>
  * </ul>
+ *
+ * <h2>Memory observability</h2>
+ * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
+ * operations are safe without explicit synchronizations.</p>
+ * <ul>
+ *     <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
+ *     in {@link #doInBackground}.
+ *     <li>Set member fields in {@link #doInBackground}, and refer to them in
+ *     {@link #onProgressUpdate} and {@link #onPostExecute}.
+ * </ul>
  */
 public abstract class AsyncTask<Params, Progress, Result> {
     private static final String LOG_TAG = "AsyncTask";
@@ -175,6 +185,11 @@
         FINISHED,
     }
 
+    /** @hide Used to force static handler to be created. */
+    public static void init() {
+        sHandler.getLooper();
+    }
+
     /**
      * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
      */
@@ -187,6 +202,17 @@
         };
 
         mFuture = new FutureTask<Result>(mWorker) {
+
+            @Override
+            protected void set(Result v) {
+                super.set(v);
+                if (isCancelled()) {
+                    Message message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
+                            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
+                    message.sendToTarget();
+                }
+            }
+
             @Override
             protected void done() {
                 Message message;
@@ -402,14 +428,19 @@
      * still running. Each call to this method will trigger the execution of
      * {@link #onProgressUpdate} on the UI thread.
      *
+     * {@link #onProgressUpdate} will note be called if the task has been
+     * canceled.
+     *
      * @param values The progress values to update the UI with.
      *
      * @see #onProgressUpdate
      * @see #doInBackground
      */
     protected final void publishProgress(Progress... values) {
-        sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
-                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
+        if (!isCancelled()) {
+            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
+                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
+        }
     }
 
     private void finish(Result result) {
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index bd57b33..c62715b 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -84,7 +84,15 @@
      * String describing the technology of the current battery.
      */
     public static final String EXTRA_TECHNOLOGY = "technology";
-    
+
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * Int value set to nonzero if an unsupported charger is attached
+     * to the device.
+     * {@hide}
+     */
+    public static final String EXTRA_INVALID_CHARGER = "invalid_charger";
+
     // values for "status" field in the ACTION_BATTERY_CHANGED Intent
     public static final int BATTERY_STATUS_UNKNOWN = 1;
     public static final int BATTERY_STATUS_CHARGING = 2;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index d67e6f5..44f1757 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -654,11 +654,14 @@
     public static final int DATA_CONNECTION_HSPA = 10;
     public static final int DATA_CONNECTION_IDEN = 11;
     public static final int DATA_CONNECTION_EVDO_B = 12;
-    public static final int DATA_CONNECTION_OTHER = 13;
-    
+    public static final int DATA_CONNECTION_LTE = 13;
+    public static final int DATA_CONNECTION_EHRPD = 14;
+    public static final int DATA_CONNECTION_OTHER = 15;
+
     static final String[] DATA_CONNECTION_NAMES = {
         "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
-        "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "other"
+        "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
+        "ehrpd", "other"
     };
     
     public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index edd0bd3..b4c6a2923 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -191,6 +191,18 @@
          * Newest version of Android, version 2.3.
          */
         public static final int GINGERBREAD = 9;
+
+        /**
+         * Next next version of Android.
+         *
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li> The default theme for applications is now dark holographic:
+         *      {@link android.R.style#Theme_Holo}.
+         * </ul>
+         */
+        public static final int HONEYCOMB = CUR_DEVELOPMENT;
     }
     
     /** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 0ec1c74..8eac7aa 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -181,6 +181,13 @@
     }
 
     /**
+     * Return the ClassLoader currently associated with this Bundle.
+     */
+    public ClassLoader getClassLoader() {
+        return mClassLoader;
+    }
+    
+    /**
      * Clones the current Bundle. The internal map is cloned, but the keys and
      * values to which it refers are copied by reference.
      */
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 86f9a6b..2b4f39a 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -731,7 +731,7 @@
     }
 
     /**
-     * Dump "hprof" data to the specified file.  This will cause a GC.
+     * Dump "hprof" data to the specified file.  This may cause a GC.
      *
      * @param fileName Full pathname of output file (e.g. "/sdcard/dump.hprof").
      * @throws UnsupportedOperationException if the VM was built without
@@ -743,11 +743,24 @@
     }
 
     /**
-     * Collect "hprof" and send it to DDMS.  This will cause a GC.
+     * Like dumpHprofData(String), but takes an already-opened
+     * FileDescriptor to which the trace is written.  The file name is also
+     * supplied simply for logging.  Makes a dup of the file descriptor.
+     *
+     * Primarily for use by the "am" shell command.
+     *
+     * @hide
+     */
+    public static void dumpHprofData(String fileName, FileDescriptor fd)
+            throws IOException {
+        VMDebug.dumpHprofData(fileName, fd);
+    }
+
+    /**
+     * Collect "hprof" and send it to DDMS.  This may cause a GC.
      *
      * @throws UnsupportedOperationException if the VM was built without
      *         HPROF support.
-     *
      * @hide
      */
     public static void dumpHprofDataDdms() {
@@ -755,6 +768,22 @@
     }
 
     /**
+     * Writes native heap data to the specified file descriptor.
+     *
+     * @hide
+     */
+    public static native void dumpNativeHeap(FileDescriptor fd);
+
+    /**
+      * Returns a count of the extant instances of a class.
+     *
+     * @hide
+     */
+    public static long countInstancesOfClass(Class cls) {
+        return VMDebug.countInstancesOfClass(cls);
+    }
+
+    /**
      * Returns the number of sent transactions from this process.
      * @return The number of sent transactions or -1 if it could not read t.
      */
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e8ae7e6..4688847 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -31,7 +31,14 @@
 
     private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
 
-    private static IMountService mMntSvc = null;
+    private static class MountServiceHolder {
+        static IMountService mSingleton = IMountService.Stub.asInterface(ServiceManager
+                .getService("mount"));
+    }
+
+    private static final Object mLock = new Object();
+
+    private volatile static Boolean mIsExternalStorageEmulated = null;
 
     /**
      * Gets the Android root directory.
@@ -382,11 +389,8 @@
      */
     public static String getExternalStorageState() {
         try {
-            if (mMntSvc == null) {
-                mMntSvc = IMountService.Stub.asInterface(ServiceManager
-                                                         .getService("mount"));
-            }
-            return mMntSvc.getVolumeState(getExternalStorageDirectory().toString());
+            return MountServiceHolder.mSingleton.getVolumeState(getExternalStorageDirectory()
+                    .toString());
         } catch (Exception rex) {
             return Environment.MEDIA_REMOVED;
         }
@@ -405,6 +409,32 @@
                 com.android.internal.R.bool.config_externalStorageRemovable);
     }
 
+    /**
+     * Returns whether the device has an external storage device which is
+     * emulated. If true, the device does not have real external storage
+     * and certain system services such as the package manager use this
+     * to determine where to install an application.
+     *
+     * @hide
+     */
+    public static boolean isExternalStorageEmulated() {
+        if (mIsExternalStorageEmulated == null) {
+            synchronized (mLock) {
+                if (mIsExternalStorageEmulated == null) {
+                    boolean externalStorageEmulated;
+                    try {
+                        externalStorageEmulated =
+                                MountServiceHolder.mSingleton.isExternalStorageEmulated();
+                    } catch (Exception e) {
+                        externalStorageEmulated = false;
+                    }
+                    mIsExternalStorageEmulated = Boolean.valueOf(externalStorageEmulated);
+                }
+            }
+        }
+        return mIsExternalStorageEmulated;
+    }
+
     static File getDirectory(String variableName, String defaultPath) {
         String path = System.getenv(variableName);
         return path == null ? new File(defaultPath) : new File(path);
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 9b57873..9d50fd9 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -85,6 +85,8 @@
 
     public static native int getPermissions(String file, int[] outPermissions);
 
+    public static native int setUMask(int mask);
+
     /** returns the FAT file system volume ID for the volume mounted 
      * at the given mount point, or -1 for failure
      * @param mount point for FAT volume
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 3b2bf1e..165e438 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -58,7 +58,7 @@
  * they create.  You can create your own threads, and communicate back with
  * the main application thread through a Handler.  This is done by calling
  * the same <em>post</em> or <em>sendMessage</em> methods as before, but from
- * your new thread.  The given Runnable or Message will than be scheduled
+ * your new thread.  The given Runnable or Message will then be scheduled
  * in the Handler's message queue and processed when appropriate.
  */
 public class Handler {
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index c98fb56..2c2fe8a 100755
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -19,6 +19,7 @@
 /** {@hide} */
 interface IVibratorService
 {
+    boolean hasVibrator();
     void vibrate(long milliseconds, IBinder token);
     void vibratePattern(in long[] pattern, int repeat, IBinder token);
     void cancelVibrate(IBinder token);
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index a81e16b..f82702a 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -58,7 +58,6 @@
     private int mAddress;   // address of ashmem memory
     private int mLength;    // total length of our ashmem region
     private boolean mAllowPurging = false;  // true if our ashmem region is unpinned
-    private final boolean mOwnsRegion;  // false if this is a ref to an existing ashmem region
 
     /**
      * Allocates a new ashmem region. The region is initially not purgable.
@@ -70,38 +69,11 @@
     public MemoryFile(String name, int length) throws IOException {
         mLength = length;
         mFD = native_open(name, length);
-        mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
-        mOwnsRegion = true;
-    }
-
-    /**
-     * Creates a reference to an existing memory file. Changes to the original file
-     * will be available through this reference.
-     * Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail.
-     *
-     * @param fd File descriptor for an existing memory file, as returned by
-     *        {@link #getFileDescriptor()}. This file descriptor will be closed
-     *        by {@link #close()}.
-     * @param length Length of the memory file in bytes.
-     * @param mode File mode. Currently only "r" for read-only access is supported.
-     * @throws NullPointerException if <code>fd</code> is null.
-     * @throws IOException If <code>fd</code> does not refer to an existing memory file,
-     *         or if the file mode of the existing memory file is more restrictive
-     *         than <code>mode</code>.
-     *
-     * @hide
-     */
-    public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException {
-        if (fd == null) {
-            throw new NullPointerException("File descriptor is null.");
+        if (length > 0) {
+            mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
+        } else {
+            mAddress = 0;
         }
-        if (!isMemoryFile(fd)) {
-            throw new IllegalArgumentException("Not a memory file.");
-        }
-        mLength = length;
-        mFD = fd;
-        mAddress = native_mmap(mFD, length, modeToProt(mode));
-        mOwnsRegion = false;
     }
 
     /**
@@ -122,7 +94,7 @@
      *
      * @hide
      */
-    public void deactivate() {
+    void deactivate() {
         if (!isDeactivated()) {
             try {
                 native_munmap(mAddress, mLength);
@@ -181,9 +153,6 @@
      * @return previous value of allowPurging
      */
     synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
-        if (!mOwnsRegion) {
-            throw new IOException("Only the owner can make ashmem regions purgable.");
-        }
         boolean oldValue = mAllowPurging;
         if (oldValue != allowPurging) {
             native_pin(mFD, !allowPurging);
@@ -260,28 +229,7 @@
     }
 
     /**
-     * Gets a ParcelFileDescriptor for the memory file. See {@link #getFileDescriptor()}
-     * for caveats. This must be here to allow classes outside <code>android.os</code< to
-     * make ParcelFileDescriptors from MemoryFiles, as
-     * {@link ParcelFileDescriptor#ParcelFileDescriptor(FileDescriptor)} is package private.
-     *
-     *
-     * @return The file descriptor owned by this memory file object.
-     *         The file descriptor is not duplicated.
-     * @throws IOException If the memory file has been closed.
-     *
-     * @hide
-     */
-    public ParcelFileDescriptor getParcelFileDescriptor() throws IOException {
-        FileDescriptor fd = getFileDescriptor();
-        return fd != null ? new ParcelFileDescriptor(fd) : null;
-    }
-
-    /**
-     * Gets a FileDescriptor for the memory file. Note that this file descriptor
-     * is only safe to pass to {@link #MemoryFile(FileDescriptor,int)}). It
-     * should not be used with file descriptor operations that expect a file descriptor
-     * for a normal file.
+     * Gets a FileDescriptor for the memory file.
      *
      * The returned file descriptor is not duplicated.
      *
@@ -294,17 +242,6 @@
     }
 
     /**
-     * Checks whether the given file descriptor refers to a memory file.
-     *
-     * @throws IOException If <code>fd</code> is not a valid file descriptor.
-     *
-     * @hide
-     */
-    public static boolean isMemoryFile(FileDescriptor fd) throws IOException {
-        return (native_get_size(fd) >= 0);
-    }
-
-    /**
      * Returns the size of the memory file that the file descriptor refers to,
      * or -1 if the file descriptor does not refer to a memory file.
      *
@@ -316,20 +253,6 @@
         return native_get_size(fd);
     }
 
-    /**
-     * Converts a file mode string to a <code>prot</code> value as expected by
-     * native_mmap().
-     *
-     * @throws IllegalArgumentException if the file mode is invalid.
-     */
-    private static int modeToProt(String mode) {
-        if ("r".equals(mode)) {
-            return PROT_READ;
-        } else {
-            throw new IllegalArgumentException("Unsupported file mode: '" + mode + "'");
-        }
-    }
-
     private class MemoryInputStream extends InputStream {
 
         private int mMark = 0;
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 49b72fe..eb941e4 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -73,7 +73,18 @@
      * receiver.
      */
     public Messenger replyTo;
-    
+
+    /** If set message is in use */
+    /*package*/ static final int FLAG_IN_USE = 1;
+
+    /** Flags reserved for future use (All are reserved for now) */
+    /*package*/ static final int FLAGS_RESERVED = ~FLAG_IN_USE;
+
+    /** Flags to clear in the copyFrom method */
+    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAGS_RESERVED | FLAG_IN_USE;
+
+    /*package*/ int flags;
+
     /*package*/ long when;
     
     /*package*/ Bundle data;
@@ -253,6 +264,7 @@
      * target/callback of the original message.
      */
     public void copyFrom(Message o) {
+        this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
         this.what = o.what;
         this.arg1 = o.arg1;
         this.arg2 = o.arg2;
@@ -350,6 +362,7 @@
     }
 
     /*package*/ void clearForRecycle() {
+        flags = 0;
         what = 0;
         arg1 = 0;
         arg2 = 0;
@@ -361,6 +374,14 @@
         data = null;
     }
 
+    /*package*/ boolean isInUse() {
+        return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
+    }
+
+    /*package*/ void markInUse() {
+        flags |= FLAG_IN_USE;
+    }
+
     /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
     */
     public Message() {
@@ -453,4 +474,3 @@
         replyTo = Messenger.readMessengerOrNullFromParcel(source);
     }
 }
-
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 0090177..bb07825 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -129,6 +129,7 @@
                         mMessages = msg.next;
                         msg.next = null;
                         if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
+                        msg.markInUse();
                         return msg;
                     } else {
                         nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
@@ -183,7 +184,7 @@
     }
 
     final boolean enqueueMessage(Message msg, long when) {
-        if (msg.when != 0) {
+        if (msg.isInUse()) {
             throw new AndroidRuntimeException(msg
                     + " This message is already in use.");
         }
diff --git a/core/java/android/os/NetworkOnMainThreadException.java b/core/java/android/os/NetworkOnMainThreadException.java
new file mode 100644
index 0000000..dd8c66c
--- /dev/null
+++ b/core/java/android/os/NetworkOnMainThreadException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os;
+
+/**
+ * The exception that is thrown when an application attempts
+ * to perform a networking operation on its main thread.
+ *
+ * <p>This is only thrown for applications targeting the Honeycomb
+ * SDK or higher.  Applications targeting earlier SDK versions
+ * are allowed to do networking on their main event loop threads,
+ * but it's heavily discouraged.  See the document
+ * <a href="{@docRoot}guide/practices/design/responsiveness.html">
+ * Designing for Responsiveness</a>.
+ *
+ * <p>Also see {@link StrictMode}.
+ */
+public class NetworkOnMainThreadException extends RuntimeException {
+}
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index d853f13..5f6c4f0 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -153,6 +153,26 @@
     private static native int createPipeNative(FileDescriptor[] outFds);
 
     /**
+     * Gets a file descriptor for a read-only copy of the given data.
+     *
+     * @param data Data to copy.
+     * @param name Name for the shared memory area that may back the file descriptor.
+     *        This is purely informative and may be {@code null}.
+     * @return A ParcelFileDescriptor.
+     * @throws IOException if there is an error while creating the shared memory area.
+     */
+    public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException {
+        if (data == null) return null;
+        MemoryFile file = new MemoryFile(name, data.length);
+        if (data.length > 0) {
+            file.writeBytes(data, 0, 0, data.length);
+        }
+        file.deactivate();
+        FileDescriptor fd = file.getFileDescriptor();
+        return fd != null ? new ParcelFileDescriptor(fd) : null;
+    }
+
+    /**
      * Retrieve the actual FileDescriptor associated with this object.
      * 
      * @return Returns the FileDescriptor associated with this object.
@@ -210,7 +230,11 @@
 
         @Override
         public void close() throws IOException {
-            mFd.close();
+            try {
+                mFd.close();
+            } finally {
+                super.close();
+            }
         }
     }
     
@@ -229,7 +253,11 @@
 
         @Override
         public void close() throws IOException {
-            mFd.close();
+            try {
+                mFd.close();
+            } finally {
+                super.close();
+            }
         }
     }
     
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index a718fc6..507cfab 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -86,6 +86,12 @@
     public static final int WIFI_UID = 1010;
 
     /**
+     * Defines the GID for the group that allows write access to the SD card.
+     * @hide
+     */
+    public static final int SDCARD_RW_GID = 1015;
+
+    /**
      * Defines the UID/GID for the NFC service process.
      * @hide
      */
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index e912089..ab5173e 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -15,14 +15,18 @@
  */
 package android.os;
 
+import android.animation.ValueAnimator;
 import android.app.ActivityManagerNative;
+import android.app.ActivityThread;
 import android.app.ApplicationErrorReport;
+import android.content.Intent;
 import android.util.Log;
 import android.util.Printer;
 
 import com.android.internal.os.RuntimeInit;
 
 import dalvik.system.BlockGuard;
+import dalvik.system.CloseGuard;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -67,6 +71,7 @@
  *                 .build());
  *         StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
  *                 .detectLeakedSqlLiteObjects()
+ *                 .detectLeakedClosableObjects()
  *                 .penaltyLog()
  *                 .penaltyDeath()
  *                 .build());
@@ -137,6 +142,12 @@
     public static final int DETECT_VM_CURSOR_LEAKS = 0x200;  // for ProcessPolicy
 
     /**
+     * Note, a "VM_" bit, not thread.
+     * @hide
+     */
+    public static final int DETECT_VM_CLOSABLE_LEAKS = 0x400;  // for ProcessPolicy
+
+    /**
      * @hide
      */
     public static final int PENALTY_LOG = 0x10;  // normal android.util.Log
@@ -149,11 +160,20 @@
     public static final int PENALTY_DIALOG = 0x20;
 
     /**
+     * Death on any detected violation.
+     *
      * @hide
      */
     public static final int PENALTY_DEATH = 0x40;
 
     /**
+     * Death just for detected network usage.
+     *
+     * @hide
+     */
+    public static final int PENALTY_DEATH_ON_NETWORK = 0x200;
+
+    /**
      * @hide
      */
     public static final int PENALTY_DROPBOX = 0x80;
@@ -172,6 +192,13 @@
     public static final int PENALTY_GATHER = 0x100;
 
     /**
+     * Mask of all the penalty bits.
+     */
+    private static final int PENALTY_MASK =
+            PENALTY_LOG | PENALTY_DIALOG | PENALTY_DEATH | PENALTY_DROPBOX | PENALTY_GATHER |
+            PENALTY_DEATH_ON_NETWORK;
+
+    /**
      * The current VmPolicy in effect.
      */
     private static volatile int sVmPolicyMask = 0;
@@ -316,12 +343,28 @@
              * Crash the whole process on violation.  This penalty runs at
              * the end of all enabled penalties so you'll still get
              * see logging or other violations before the process dies.
+             *
+             * <p>Unlike {@link #penaltyDeathOnNetwork}, this applies
+             * to disk reads, disk writes, and network usage if their
+             * corresponding detect flags are set.
              */
             public Builder penaltyDeath() {
                 return enable(PENALTY_DEATH);
             }
 
             /**
+             * Crash the whole process on any network usage.  Unlike
+             * {@link #penaltyDeath}, this penalty runs
+             * <em>before</em> anything else.  You must still have
+             * called {@link #detectNetwork} to enable this.
+             *
+             * <p>In the Honeycomb or later SDKs, this is on by default.
+             */
+            public Builder penaltyDeathOnNetwork() {
+                return enable(PENALTY_DEATH_ON_NETWORK);
+            }
+
+            /**
              * Log detected violations to the system log.
              */
             public Builder penaltyLog() {
@@ -415,12 +458,12 @@
             /**
              * Detect everything that's potentially suspect.
              *
-             * <p>As of the Gingerbread release this only includes
-             * SQLite cursor leaks but will likely expand in future
-             * releases.
+             * <p>In the Honeycomb release this includes leaks of
+             * SQLite cursors and other closable objects but will
+             * likely expand in future releases.
              */
             public Builder detectAll() {
-                return enable(DETECT_VM_CURSOR_LEAKS);
+                return enable(DETECT_VM_CURSOR_LEAKS | DETECT_VM_CLOSABLE_LEAKS);
             }
 
             /**
@@ -437,6 +480,18 @@
             }
 
             /**
+             * Detect when an {@link java.io.Closeable} or other
+             * object with a explict termination method is finalized
+             * without having been closed.
+             *
+             * <p>You always want to explicitly close such objects to
+             * avoid unnecessary resources leaks.
+             */
+            public Builder detectLeakedClosableObjects() {
+                return enable(DETECT_VM_CLOSABLE_LEAKS);
+            }
+
+            /**
              * Crashes the whole process on violation.  This penalty runs at
              * the end of all enabled penalties so yo you'll still get
              * your logging or other violations before the process dies.
@@ -636,12 +691,25 @@
             StrictMode.DETECT_NETWORK |
             StrictMode.PENALTY_DROPBOX);
         sVmPolicyMask = StrictMode.DETECT_VM_CURSOR_LEAKS |
+                StrictMode.DETECT_VM_CLOSABLE_LEAKS |
                 StrictMode.PENALTY_DROPBOX |
                 StrictMode.PENALTY_LOG;
         return true;
     }
 
     /**
+     * Used by the framework to make network usage on the main
+     * thread a fatal error.
+     *
+     * @hide
+     */
+    public static void enableDeathOnNetwork() {
+        int oldPolicy = getThreadPolicyMask();
+        int newPolicy = oldPolicy | DETECT_NETWORK | PENALTY_DEATH_ON_NETWORK;
+        setThreadPolicyMask(newPolicy);
+    }
+
+    /**
      * Parses the BlockGuard policy mask out from the Exception's
      * getMessage() String value.  Kinda gross, but least
      * invasive.  :/
@@ -751,6 +819,9 @@
             if ((mPolicyMask & DETECT_NETWORK) == 0) {
                 return;
             }
+            if ((mPolicyMask & PENALTY_DEATH_ON_NETWORK) != 0) {
+                throw new NetworkOnMainThreadException();
+            }
             if (tooManyViolationsThisLoop()) {
                 return;
             }
@@ -881,7 +952,7 @@
                 }
             }
 
-            // The violationMask, passed to ActivityManager, is a
+            // The violationMaskSubset, passed to ActivityManager, is a
             // subset of the original StrictMode policy bitmask, with
             // only the bit violated and penalty bits to be executed
             // by the ActivityManagerService remaining set.
@@ -899,7 +970,35 @@
             if (violationMaskSubset != 0) {
                 int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage);
                 violationMaskSubset |= violationBit;
+                final int violationMaskSubsetFinal = violationMaskSubset;
                 final int savedPolicyMask = getThreadPolicyMask();
+
+                final boolean justDropBox = (info.policy & PENALTY_MASK) == PENALTY_DROPBOX;
+                if (justDropBox) {
+                    // If all we're going to ask the activity manager
+                    // to do is dropbox it (the common case during
+                    // platform development), we can avoid doing this
+                    // call synchronously which Binder data suggests
+                    // isn't always super fast, despite the implementation
+                    // in the ActivityManager trying to be mostly async.
+                    new Thread("callActivityManagerForStrictModeDropbox") {
+                        public void run() {
+                            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                            try {
+                                ActivityManagerNative.getDefault().
+                                        handleApplicationStrictModeViolation(
+                                            RuntimeInit.getApplicationObject(),
+                                            violationMaskSubsetFinal,
+                                            info);
+                            } catch (RemoteException e) {
+                                Log.e(TAG, "RemoteException handling StrictMode violation", e);
+                            }
+                        }
+                    }.start();
+                    return;
+                }
+
+                // Normal synchronous call to the ActivityManager.
                 try {
                     // First, remove any policy before we call into the Activity Manager,
                     // otherwise we'll infinite recurse as we try to log policy violations
@@ -952,6 +1051,7 @@
      */
     public static void setVmPolicy(final VmPolicy policy) {
         sVmPolicyMask = policy.mask;
+        CloseGuard.setEnabled(vmClosableObjectLeaksEnabled());
     }
 
     /**
@@ -965,8 +1065,9 @@
      * Enable the recommended StrictMode defaults, with violations just being logged.
      *
      * <p>This catches disk and network access on the main thread, as
-     * well as leaked SQLite cursors.  This is simply a wrapper around
-     * {@link #setVmPolicy} and {@link #setThreadPolicy}.
+     * well as leaked SQLite cursors and unclosed resources.  This is
+     * simply a wrapper around {@link #setVmPolicy} and {@link
+     * #setThreadPolicy}.
      */
     public static void enableDefaults() {
         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
@@ -975,6 +1076,7 @@
                                    .build());
         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                                .detectLeakedSqlLiteObjects()
+                               .detectLeakedClosableObjects()
                                .penaltyLog()
                                .build());
     }
@@ -989,6 +1091,13 @@
     /**
      * @hide
      */
+    public static boolean vmClosableObjectLeaksEnabled() {
+        return (sVmPolicyMask & DETECT_VM_CLOSABLE_LEAKS) != 0;
+    }
+
+    /**
+     * @hide
+     */
     public static void onSqliteObjectLeaked(String message, Throwable originStack) {
         if ((sVmPolicyMask & PENALTY_LOG) != 0) {
             Log.e(TAG, message, originStack);
@@ -1110,6 +1219,11 @@
         public int durationMillis = -1;
 
         /**
+         * The number of animations currently running.
+         */
+        public int numAnimationsRunning = 0;
+
+        /**
          * Which violation number this was (1-based) since the last Looper loop,
          * from the perspective of the root caller (if it crossed any processes
          * via Binder calls).  The value is 0 if the root caller wasn't on a Looper
@@ -1124,6 +1238,12 @@
         public long violationUptimeMillis;
 
         /**
+         * The action of the Intent being broadcast to somebody's onReceive
+         * on this thread right now, or null.
+         */
+        public String broadcastIntentAction;
+
+        /**
          * Create an uninitialized instance of ViolationInfo
          */
         public ViolationInfo() {
@@ -1138,6 +1258,11 @@
             crashInfo = new ApplicationErrorReport.CrashInfo(tr);
             violationUptimeMillis = SystemClock.uptimeMillis();
             this.policy = policy;
+            this.numAnimationsRunning = ValueAnimator.getCurrentAnimationsCount();
+            Intent broadcastIntent = ActivityThread.getIntentBeingBroadcast();
+            if (broadcastIntent != null) {
+                broadcastIntentAction = broadcastIntent.getAction();
+            }
         }
 
         /**
@@ -1163,7 +1288,9 @@
             }
             durationMillis = in.readInt();
             violationNumThisLoop = in.readInt();
+            numAnimationsRunning = in.readInt();
             violationUptimeMillis = in.readLong();
+            broadcastIntentAction = in.readString();
         }
 
         /**
@@ -1174,7 +1301,9 @@
             dest.writeInt(policy);
             dest.writeInt(durationMillis);
             dest.writeInt(violationNumThisLoop);
+            dest.writeInt(numAnimationsRunning);
             dest.writeLong(violationUptimeMillis);
+            dest.writeString(broadcastIntentAction);
         }
 
 
@@ -1190,7 +1319,13 @@
             if (violationNumThisLoop != 0) {
                 pw.println(prefix + "violationNumThisLoop: " + violationNumThisLoop);
             }
+            if (numAnimationsRunning != 0) {
+                pw.println(prefix + "numAnimationsRunning: " + numAnimationsRunning);
+            }
             pw.println(prefix + "violationUptimeMillis: " + violationUptimeMillis);
+            if (broadcastIntentAction != null) {
+                pw.println(prefix + "broadcastIntentAction: " + broadcastIntentAction);
+            }
         }
 
     }
diff --git a/core/java/android/os/SystemService.java b/core/java/android/os/SystemService.java
index 447cd1f..da27db5 100644
--- a/core/java/android/os/SystemService.java
+++ b/core/java/android/os/SystemService.java
@@ -28,4 +28,9 @@
     public static void stop(String name) {
         SystemProperties.set("ctl.stop", name);
     }
+
+    /** Request that the init daemon restart a named service. */
+    public static void restart(String name) {
+        SystemProperties.set("ctl.restart", name);
+    }
 }
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index be818da..e9428f7 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -38,6 +38,22 @@
     }
 
     /**
+     * Check whether the hardware has a vibrator.  Returns true if a vibrator
+     * exists, else false.
+     */
+    public boolean hasVibrator() {
+        if (mService == null) {
+            Log.w(TAG, "Failed to vibrate; no vibrator service.");
+            return false;
+        }
+        try {
+            return mService.hasVibrator();
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+    
+    /**
      * Turn the vibrator on.
      *
      * @param milliseconds How long to vibrate for.
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 467a0ac..3a2add4 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -567,6 +567,25 @@
                 }
                 return _result;
             }
+
+            /**
+             * Returns whether the external storage is emulated.
+             */
+            public boolean isExternalStorageEmulated() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                boolean _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_isExternalStorageEmulated, _data, _reply, 0);
+                    _reply.readException();
+                    _result = 0 != _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
         }
 
         private static final String DESCRIPTOR = "IMountService";
@@ -621,6 +640,8 @@
 
         static final int TRANSACTION_getMountedObbPath = IBinder.FIRST_CALL_TRANSACTION + 24;
 
+        static final int TRANSACTION_isExternalStorageEmulated = IBinder.FIRST_CALL_TRANSACTION + 25;
+
         /**
          * Cast an IBinder object into an IMountService interface, generating a
          * proxy if needed.
@@ -895,6 +916,13 @@
                     reply.writeString(mountedPath);
                     return true;
                 }
+                case TRANSACTION_isExternalStorageEmulated: {
+                    data.enforceInterface(DESCRIPTOR);
+                    boolean emulated = isExternalStorageEmulated();
+                    reply.writeNoException();
+                    reply.writeInt(emulated ? 1 : 0);
+                    return true;
+                }
             }
             return super.onTransact(code, data, reply, flags);
         }
@@ -1049,4 +1077,9 @@
      * Unregisters an IMountServiceListener
      */
     public void unregisterListener(IMountServiceListener listener) throws RemoteException;
+
+    /**
+     * Returns whether or not the external storage is emulated.
+     */
+    public boolean isExternalStorageEmulated() throws RemoteException;
 }
diff --git a/core/java/android/os/storage/StorageEventListener.java b/core/java/android/os/storage/StorageEventListener.java
index 7b883a7..d3d39d6 100644
--- a/core/java/android/os/storage/StorageEventListener.java
+++ b/core/java/android/os/storage/StorageEventListener.java
@@ -18,7 +18,6 @@
 
 /**
  * Used for receiving notifications from the StorageManager
- * @hide
  */
 public abstract class StorageEventListener {
     /**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 73ac79f..fb76937 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -312,7 +312,6 @@
      *
      * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
      *
-     * @hide
      */
     public void registerListener(StorageEventListener listener) {
         if (listener == null) {
@@ -329,7 +328,6 @@
      *
      * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
      *
-     * @hide
      */
     public void unregisterListener(StorageEventListener listener) {
         if (listener == null) {
@@ -350,8 +348,6 @@
 
     /**
      * Enables USB Mass Storage (UMS) on the device.
-     *
-     * @hide
      */
     public void enableUsbMassStorage() {
         try {
@@ -363,8 +359,6 @@
 
     /**
      * Disables USB Mass Storage (UMS) on the device.
-     *
-     * @hide
      */
     public void disableUsbMassStorage() {
         try {
@@ -377,8 +371,6 @@
     /**
      * Query if a USB Mass Storage (UMS) host is connected.
      * @return true if UMS host is connected.
-     *
-     * @hide
      */
     public boolean isUsbMassStorageConnected() {
         try {
@@ -392,8 +384,6 @@
     /**
      * Query if a USB Mass Storage (UMS) is enabled on the device.
      * @return true if UMS host is enabled.
-     *
-     * @hide
      */
     public boolean isUsbMassStorageEnabled() {
         try {
diff --git a/core/java/android/os/storage/StorageResultCode.java b/core/java/android/os/storage/StorageResultCode.java
index 075f47f..07d95df 100644
--- a/core/java/android/os/storage/StorageResultCode.java
+++ b/core/java/android/os/storage/StorageResultCode.java
@@ -19,8 +19,6 @@
 /**
  * Class that provides access to constants returned from StorageManager
  * and lower level MountService APIs.
- *
- * @hide
  */
 public class StorageResultCode
 {
diff --git a/core/java/android/pim/RecurrenceSet.java b/core/java/android/pim/RecurrenceSet.java
index f5e1bac..282417d 100644
--- a/core/java/android/pim/RecurrenceSet.java
+++ b/core/java/android/pim/RecurrenceSet.java
@@ -206,10 +206,7 @@
         }
         
         if (allDay) {
-        	// TODO: also change tzid to be UTC?  that would be consistent, but
-        	// that would not reflect the original timezone value back to the
-        	// server.
-        	start.timezone = Time.TIMEZONE_UTC;
+            start.timezone = Time.TIMEZONE_UTC;
         }
         long millis = start.toMillis(false /* use isDst */);
         values.put(Calendar.Events.DTSTART, millis);
diff --git a/core/java/android/pim/vcard/JapaneseUtils.java b/core/java/android/pim/vcard/JapaneseUtils.java
deleted file mode 100644
index dcfe980..0000000
--- a/core/java/android/pim/vcard/JapaneseUtils.java
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.pim.vcard;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * TextUtils especially for Japanese.
- */
-/* package */ class JapaneseUtils {
-    static private final Map<Character, String> sHalfWidthMap =
-        new HashMap<Character, String>();
-
-    static {
-        sHalfWidthMap.put('\u3001', "\uFF64");
-        sHalfWidthMap.put('\u3002', "\uFF61");
-        sHalfWidthMap.put('\u300C', "\uFF62");
-        sHalfWidthMap.put('\u300D', "\uFF63");
-        sHalfWidthMap.put('\u301C', "~");
-        sHalfWidthMap.put('\u3041', "\uFF67");
-        sHalfWidthMap.put('\u3042', "\uFF71");
-        sHalfWidthMap.put('\u3043', "\uFF68");
-        sHalfWidthMap.put('\u3044', "\uFF72");
-        sHalfWidthMap.put('\u3045', "\uFF69");
-        sHalfWidthMap.put('\u3046', "\uFF73");
-        sHalfWidthMap.put('\u3047', "\uFF6A");
-        sHalfWidthMap.put('\u3048', "\uFF74");
-        sHalfWidthMap.put('\u3049', "\uFF6B");
-        sHalfWidthMap.put('\u304A', "\uFF75");
-        sHalfWidthMap.put('\u304B', "\uFF76");
-        sHalfWidthMap.put('\u304C', "\uFF76\uFF9E");
-        sHalfWidthMap.put('\u304D', "\uFF77");
-        sHalfWidthMap.put('\u304E', "\uFF77\uFF9E");
-        sHalfWidthMap.put('\u304F', "\uFF78");
-        sHalfWidthMap.put('\u3050', "\uFF78\uFF9E");
-        sHalfWidthMap.put('\u3051', "\uFF79");
-        sHalfWidthMap.put('\u3052', "\uFF79\uFF9E");
-        sHalfWidthMap.put('\u3053', "\uFF7A");
-        sHalfWidthMap.put('\u3054', "\uFF7A\uFF9E");
-        sHalfWidthMap.put('\u3055', "\uFF7B");
-        sHalfWidthMap.put('\u3056', "\uFF7B\uFF9E");
-        sHalfWidthMap.put('\u3057', "\uFF7C");
-        sHalfWidthMap.put('\u3058', "\uFF7C\uFF9E");
-        sHalfWidthMap.put('\u3059', "\uFF7D");
-        sHalfWidthMap.put('\u305A', "\uFF7D\uFF9E");
-        sHalfWidthMap.put('\u305B', "\uFF7E");
-        sHalfWidthMap.put('\u305C', "\uFF7E\uFF9E");
-        sHalfWidthMap.put('\u305D', "\uFF7F");
-        sHalfWidthMap.put('\u305E', "\uFF7F\uFF9E");
-        sHalfWidthMap.put('\u305F', "\uFF80");
-        sHalfWidthMap.put('\u3060', "\uFF80\uFF9E");
-        sHalfWidthMap.put('\u3061', "\uFF81");
-        sHalfWidthMap.put('\u3062', "\uFF81\uFF9E");
-        sHalfWidthMap.put('\u3063', "\uFF6F");
-        sHalfWidthMap.put('\u3064', "\uFF82");
-        sHalfWidthMap.put('\u3065', "\uFF82\uFF9E");
-        sHalfWidthMap.put('\u3066', "\uFF83");
-        sHalfWidthMap.put('\u3067', "\uFF83\uFF9E");
-        sHalfWidthMap.put('\u3068', "\uFF84");
-        sHalfWidthMap.put('\u3069', "\uFF84\uFF9E");
-        sHalfWidthMap.put('\u306A', "\uFF85");
-        sHalfWidthMap.put('\u306B', "\uFF86");
-        sHalfWidthMap.put('\u306C', "\uFF87");
-        sHalfWidthMap.put('\u306D', "\uFF88");
-        sHalfWidthMap.put('\u306E', "\uFF89");
-        sHalfWidthMap.put('\u306F', "\uFF8A");
-        sHalfWidthMap.put('\u3070', "\uFF8A\uFF9E");
-        sHalfWidthMap.put('\u3071', "\uFF8A\uFF9F");
-        sHalfWidthMap.put('\u3072', "\uFF8B");
-        sHalfWidthMap.put('\u3073', "\uFF8B\uFF9E");
-        sHalfWidthMap.put('\u3074', "\uFF8B\uFF9F");
-        sHalfWidthMap.put('\u3075', "\uFF8C");
-        sHalfWidthMap.put('\u3076', "\uFF8C\uFF9E");
-        sHalfWidthMap.put('\u3077', "\uFF8C\uFF9F");
-        sHalfWidthMap.put('\u3078', "\uFF8D");
-        sHalfWidthMap.put('\u3079', "\uFF8D\uFF9E");
-        sHalfWidthMap.put('\u307A', "\uFF8D\uFF9F");
-        sHalfWidthMap.put('\u307B', "\uFF8E");
-        sHalfWidthMap.put('\u307C', "\uFF8E\uFF9E");
-        sHalfWidthMap.put('\u307D', "\uFF8E\uFF9F");
-        sHalfWidthMap.put('\u307E', "\uFF8F");
-        sHalfWidthMap.put('\u307F', "\uFF90");
-        sHalfWidthMap.put('\u3080', "\uFF91");
-        sHalfWidthMap.put('\u3081', "\uFF92");
-        sHalfWidthMap.put('\u3082', "\uFF93");
-        sHalfWidthMap.put('\u3083', "\uFF6C");
-        sHalfWidthMap.put('\u3084', "\uFF94");
-        sHalfWidthMap.put('\u3085', "\uFF6D");
-        sHalfWidthMap.put('\u3086', "\uFF95");
-        sHalfWidthMap.put('\u3087', "\uFF6E");
-        sHalfWidthMap.put('\u3088', "\uFF96");
-        sHalfWidthMap.put('\u3089', "\uFF97");
-        sHalfWidthMap.put('\u308A', "\uFF98");
-        sHalfWidthMap.put('\u308B', "\uFF99");
-        sHalfWidthMap.put('\u308C', "\uFF9A");
-        sHalfWidthMap.put('\u308D', "\uFF9B");
-        sHalfWidthMap.put('\u308E', "\uFF9C");
-        sHalfWidthMap.put('\u308F', "\uFF9C");
-        sHalfWidthMap.put('\u3090', "\uFF72");
-        sHalfWidthMap.put('\u3091', "\uFF74");
-        sHalfWidthMap.put('\u3092', "\uFF66");
-        sHalfWidthMap.put('\u3093', "\uFF9D");
-        sHalfWidthMap.put('\u309B', "\uFF9E");
-        sHalfWidthMap.put('\u309C', "\uFF9F");
-        sHalfWidthMap.put('\u30A1', "\uFF67");
-        sHalfWidthMap.put('\u30A2', "\uFF71");
-        sHalfWidthMap.put('\u30A3', "\uFF68");
-        sHalfWidthMap.put('\u30A4', "\uFF72");
-        sHalfWidthMap.put('\u30A5', "\uFF69");
-        sHalfWidthMap.put('\u30A6', "\uFF73");
-        sHalfWidthMap.put('\u30A7', "\uFF6A");
-        sHalfWidthMap.put('\u30A8', "\uFF74");
-        sHalfWidthMap.put('\u30A9', "\uFF6B");
-        sHalfWidthMap.put('\u30AA', "\uFF75");
-        sHalfWidthMap.put('\u30AB', "\uFF76");
-        sHalfWidthMap.put('\u30AC', "\uFF76\uFF9E");
-        sHalfWidthMap.put('\u30AD', "\uFF77");
-        sHalfWidthMap.put('\u30AE', "\uFF77\uFF9E");
-        sHalfWidthMap.put('\u30AF', "\uFF78");
-        sHalfWidthMap.put('\u30B0', "\uFF78\uFF9E");
-        sHalfWidthMap.put('\u30B1', "\uFF79");
-        sHalfWidthMap.put('\u30B2', "\uFF79\uFF9E");
-        sHalfWidthMap.put('\u30B3', "\uFF7A");
-        sHalfWidthMap.put('\u30B4', "\uFF7A\uFF9E");
-        sHalfWidthMap.put('\u30B5', "\uFF7B");
-        sHalfWidthMap.put('\u30B6', "\uFF7B\uFF9E");
-        sHalfWidthMap.put('\u30B7', "\uFF7C");
-        sHalfWidthMap.put('\u30B8', "\uFF7C\uFF9E");
-        sHalfWidthMap.put('\u30B9', "\uFF7D");
-        sHalfWidthMap.put('\u30BA', "\uFF7D\uFF9E");
-        sHalfWidthMap.put('\u30BB', "\uFF7E");
-        sHalfWidthMap.put('\u30BC', "\uFF7E\uFF9E");
-        sHalfWidthMap.put('\u30BD', "\uFF7F");
-        sHalfWidthMap.put('\u30BE', "\uFF7F\uFF9E");
-        sHalfWidthMap.put('\u30BF', "\uFF80");
-        sHalfWidthMap.put('\u30C0', "\uFF80\uFF9E");
-        sHalfWidthMap.put('\u30C1', "\uFF81");
-        sHalfWidthMap.put('\u30C2', "\uFF81\uFF9E");
-        sHalfWidthMap.put('\u30C3', "\uFF6F");
-        sHalfWidthMap.put('\u30C4', "\uFF82");
-        sHalfWidthMap.put('\u30C5', "\uFF82\uFF9E");
-        sHalfWidthMap.put('\u30C6', "\uFF83");
-        sHalfWidthMap.put('\u30C7', "\uFF83\uFF9E");
-        sHalfWidthMap.put('\u30C8', "\uFF84");
-        sHalfWidthMap.put('\u30C9', "\uFF84\uFF9E");
-        sHalfWidthMap.put('\u30CA', "\uFF85");
-        sHalfWidthMap.put('\u30CB', "\uFF86");
-        sHalfWidthMap.put('\u30CC', "\uFF87");
-        sHalfWidthMap.put('\u30CD', "\uFF88");
-        sHalfWidthMap.put('\u30CE', "\uFF89");
-        sHalfWidthMap.put('\u30CF', "\uFF8A");
-        sHalfWidthMap.put('\u30D0', "\uFF8A\uFF9E");
-        sHalfWidthMap.put('\u30D1', "\uFF8A\uFF9F");
-        sHalfWidthMap.put('\u30D2', "\uFF8B");
-        sHalfWidthMap.put('\u30D3', "\uFF8B\uFF9E");
-        sHalfWidthMap.put('\u30D4', "\uFF8B\uFF9F");
-        sHalfWidthMap.put('\u30D5', "\uFF8C");
-        sHalfWidthMap.put('\u30D6', "\uFF8C\uFF9E");
-        sHalfWidthMap.put('\u30D7', "\uFF8C\uFF9F");
-        sHalfWidthMap.put('\u30D8', "\uFF8D");
-        sHalfWidthMap.put('\u30D9', "\uFF8D\uFF9E");
-        sHalfWidthMap.put('\u30DA', "\uFF8D\uFF9F");
-        sHalfWidthMap.put('\u30DB', "\uFF8E");
-        sHalfWidthMap.put('\u30DC', "\uFF8E\uFF9E");
-        sHalfWidthMap.put('\u30DD', "\uFF8E\uFF9F");
-        sHalfWidthMap.put('\u30DE', "\uFF8F");
-        sHalfWidthMap.put('\u30DF', "\uFF90");
-        sHalfWidthMap.put('\u30E0', "\uFF91");
-        sHalfWidthMap.put('\u30E1', "\uFF92");
-        sHalfWidthMap.put('\u30E2', "\uFF93");
-        sHalfWidthMap.put('\u30E3', "\uFF6C");
-        sHalfWidthMap.put('\u30E4', "\uFF94");
-        sHalfWidthMap.put('\u30E5', "\uFF6D");
-        sHalfWidthMap.put('\u30E6', "\uFF95");
-        sHalfWidthMap.put('\u30E7', "\uFF6E");
-        sHalfWidthMap.put('\u30E8', "\uFF96");
-        sHalfWidthMap.put('\u30E9', "\uFF97");
-        sHalfWidthMap.put('\u30EA', "\uFF98");
-        sHalfWidthMap.put('\u30EB', "\uFF99");
-        sHalfWidthMap.put('\u30EC', "\uFF9A");
-        sHalfWidthMap.put('\u30ED', "\uFF9B");
-        sHalfWidthMap.put('\u30EE', "\uFF9C");
-        sHalfWidthMap.put('\u30EF', "\uFF9C");
-        sHalfWidthMap.put('\u30F0', "\uFF72");
-        sHalfWidthMap.put('\u30F1', "\uFF74");
-        sHalfWidthMap.put('\u30F2', "\uFF66");
-        sHalfWidthMap.put('\u30F3', "\uFF9D");
-        sHalfWidthMap.put('\u30F4', "\uFF73\uFF9E");
-        sHalfWidthMap.put('\u30F5', "\uFF76");
-        sHalfWidthMap.put('\u30F6', "\uFF79");
-        sHalfWidthMap.put('\u30FB', "\uFF65");
-        sHalfWidthMap.put('\u30FC', "\uFF70");
-        sHalfWidthMap.put('\uFF01', "!");
-        sHalfWidthMap.put('\uFF02', "\"");
-        sHalfWidthMap.put('\uFF03', "#");
-        sHalfWidthMap.put('\uFF04', "$");
-        sHalfWidthMap.put('\uFF05', "%");
-        sHalfWidthMap.put('\uFF06', "&");
-        sHalfWidthMap.put('\uFF07', "'");
-        sHalfWidthMap.put('\uFF08', "(");
-        sHalfWidthMap.put('\uFF09', ")");
-        sHalfWidthMap.put('\uFF0A', "*");
-        sHalfWidthMap.put('\uFF0B', "+");
-        sHalfWidthMap.put('\uFF0C', ",");
-        sHalfWidthMap.put('\uFF0D', "-");
-        sHalfWidthMap.put('\uFF0E', ".");
-        sHalfWidthMap.put('\uFF0F', "/");
-        sHalfWidthMap.put('\uFF10', "0");
-        sHalfWidthMap.put('\uFF11', "1");
-        sHalfWidthMap.put('\uFF12', "2");
-        sHalfWidthMap.put('\uFF13', "3");
-        sHalfWidthMap.put('\uFF14', "4");
-        sHalfWidthMap.put('\uFF15', "5");
-        sHalfWidthMap.put('\uFF16', "6");
-        sHalfWidthMap.put('\uFF17', "7");
-        sHalfWidthMap.put('\uFF18', "8");
-        sHalfWidthMap.put('\uFF19', "9");
-        sHalfWidthMap.put('\uFF1A', ":");
-        sHalfWidthMap.put('\uFF1B', ";");
-        sHalfWidthMap.put('\uFF1C', "<");
-        sHalfWidthMap.put('\uFF1D', "=");
-        sHalfWidthMap.put('\uFF1E', ">");
-        sHalfWidthMap.put('\uFF1F', "?");
-        sHalfWidthMap.put('\uFF20', "@");
-        sHalfWidthMap.put('\uFF21', "A");
-        sHalfWidthMap.put('\uFF22', "B");
-        sHalfWidthMap.put('\uFF23', "C");
-        sHalfWidthMap.put('\uFF24', "D");
-        sHalfWidthMap.put('\uFF25', "E");
-        sHalfWidthMap.put('\uFF26', "F");
-        sHalfWidthMap.put('\uFF27', "G");
-        sHalfWidthMap.put('\uFF28', "H");
-        sHalfWidthMap.put('\uFF29', "I");
-        sHalfWidthMap.put('\uFF2A', "J");
-        sHalfWidthMap.put('\uFF2B', "K");
-        sHalfWidthMap.put('\uFF2C', "L");
-        sHalfWidthMap.put('\uFF2D', "M");
-        sHalfWidthMap.put('\uFF2E', "N");
-        sHalfWidthMap.put('\uFF2F', "O");
-        sHalfWidthMap.put('\uFF30', "P");
-        sHalfWidthMap.put('\uFF31', "Q");
-        sHalfWidthMap.put('\uFF32', "R");
-        sHalfWidthMap.put('\uFF33', "S");
-        sHalfWidthMap.put('\uFF34', "T");
-        sHalfWidthMap.put('\uFF35', "U");
-        sHalfWidthMap.put('\uFF36', "V");
-        sHalfWidthMap.put('\uFF37', "W");
-        sHalfWidthMap.put('\uFF38', "X");
-        sHalfWidthMap.put('\uFF39', "Y");
-        sHalfWidthMap.put('\uFF3A', "Z");
-        sHalfWidthMap.put('\uFF3B', "[");
-        sHalfWidthMap.put('\uFF3C', "\\");
-        sHalfWidthMap.put('\uFF3D', "]");
-        sHalfWidthMap.put('\uFF3E', "^");
-        sHalfWidthMap.put('\uFF3F', "_");
-        sHalfWidthMap.put('\uFF41', "a");
-        sHalfWidthMap.put('\uFF42', "b");
-        sHalfWidthMap.put('\uFF43', "c");
-        sHalfWidthMap.put('\uFF44', "d");
-        sHalfWidthMap.put('\uFF45', "e");
-        sHalfWidthMap.put('\uFF46', "f");
-        sHalfWidthMap.put('\uFF47', "g");
-        sHalfWidthMap.put('\uFF48', "h");
-        sHalfWidthMap.put('\uFF49', "i");
-        sHalfWidthMap.put('\uFF4A', "j");
-        sHalfWidthMap.put('\uFF4B', "k");
-        sHalfWidthMap.put('\uFF4C', "l");
-        sHalfWidthMap.put('\uFF4D', "m");
-        sHalfWidthMap.put('\uFF4E', "n");
-        sHalfWidthMap.put('\uFF4F', "o");
-        sHalfWidthMap.put('\uFF50', "p");
-        sHalfWidthMap.put('\uFF51', "q");
-        sHalfWidthMap.put('\uFF52', "r");
-        sHalfWidthMap.put('\uFF53', "s");
-        sHalfWidthMap.put('\uFF54', "t");
-        sHalfWidthMap.put('\uFF55', "u");
-        sHalfWidthMap.put('\uFF56', "v");
-        sHalfWidthMap.put('\uFF57', "w");
-        sHalfWidthMap.put('\uFF58', "x");
-        sHalfWidthMap.put('\uFF59', "y");
-        sHalfWidthMap.put('\uFF5A', "z");
-        sHalfWidthMap.put('\uFF5B', "{");
-        sHalfWidthMap.put('\uFF5C', "|");
-        sHalfWidthMap.put('\uFF5D', "}");
-        sHalfWidthMap.put('\uFF5E', "~");
-        sHalfWidthMap.put('\uFF61', "\uFF61");
-        sHalfWidthMap.put('\uFF62', "\uFF62");
-        sHalfWidthMap.put('\uFF63', "\uFF63");
-        sHalfWidthMap.put('\uFF64', "\uFF64");
-        sHalfWidthMap.put('\uFF65', "\uFF65");
-        sHalfWidthMap.put('\uFF66', "\uFF66");
-        sHalfWidthMap.put('\uFF67', "\uFF67");
-        sHalfWidthMap.put('\uFF68', "\uFF68");
-        sHalfWidthMap.put('\uFF69', "\uFF69");
-        sHalfWidthMap.put('\uFF6A', "\uFF6A");
-        sHalfWidthMap.put('\uFF6B', "\uFF6B");
-        sHalfWidthMap.put('\uFF6C', "\uFF6C");
-        sHalfWidthMap.put('\uFF6D', "\uFF6D");
-        sHalfWidthMap.put('\uFF6E', "\uFF6E");
-        sHalfWidthMap.put('\uFF6F', "\uFF6F");
-        sHalfWidthMap.put('\uFF70', "\uFF70");
-        sHalfWidthMap.put('\uFF71', "\uFF71");
-        sHalfWidthMap.put('\uFF72', "\uFF72");
-        sHalfWidthMap.put('\uFF73', "\uFF73");
-        sHalfWidthMap.put('\uFF74', "\uFF74");
-        sHalfWidthMap.put('\uFF75', "\uFF75");
-        sHalfWidthMap.put('\uFF76', "\uFF76");
-        sHalfWidthMap.put('\uFF77', "\uFF77");
-        sHalfWidthMap.put('\uFF78', "\uFF78");
-        sHalfWidthMap.put('\uFF79', "\uFF79");
-        sHalfWidthMap.put('\uFF7A', "\uFF7A");
-        sHalfWidthMap.put('\uFF7B', "\uFF7B");
-        sHalfWidthMap.put('\uFF7C', "\uFF7C");
-        sHalfWidthMap.put('\uFF7D', "\uFF7D");
-        sHalfWidthMap.put('\uFF7E', "\uFF7E");
-        sHalfWidthMap.put('\uFF7F', "\uFF7F");
-        sHalfWidthMap.put('\uFF80', "\uFF80");
-        sHalfWidthMap.put('\uFF81', "\uFF81");
-        sHalfWidthMap.put('\uFF82', "\uFF82");
-        sHalfWidthMap.put('\uFF83', "\uFF83");
-        sHalfWidthMap.put('\uFF84', "\uFF84");
-        sHalfWidthMap.put('\uFF85', "\uFF85");
-        sHalfWidthMap.put('\uFF86', "\uFF86");
-        sHalfWidthMap.put('\uFF87', "\uFF87");
-        sHalfWidthMap.put('\uFF88', "\uFF88");
-        sHalfWidthMap.put('\uFF89', "\uFF89");
-        sHalfWidthMap.put('\uFF8A', "\uFF8A");
-        sHalfWidthMap.put('\uFF8B', "\uFF8B");
-        sHalfWidthMap.put('\uFF8C', "\uFF8C");
-        sHalfWidthMap.put('\uFF8D', "\uFF8D");
-        sHalfWidthMap.put('\uFF8E', "\uFF8E");
-        sHalfWidthMap.put('\uFF8F', "\uFF8F");
-        sHalfWidthMap.put('\uFF90', "\uFF90");
-        sHalfWidthMap.put('\uFF91', "\uFF91");
-        sHalfWidthMap.put('\uFF92', "\uFF92");
-        sHalfWidthMap.put('\uFF93', "\uFF93");
-        sHalfWidthMap.put('\uFF94', "\uFF94");
-        sHalfWidthMap.put('\uFF95', "\uFF95");
-        sHalfWidthMap.put('\uFF96', "\uFF96");
-        sHalfWidthMap.put('\uFF97', "\uFF97");
-        sHalfWidthMap.put('\uFF98', "\uFF98");
-        sHalfWidthMap.put('\uFF99', "\uFF99");
-        sHalfWidthMap.put('\uFF9A', "\uFF9A");
-        sHalfWidthMap.put('\uFF9B', "\uFF9B");
-        sHalfWidthMap.put('\uFF9C', "\uFF9C");
-        sHalfWidthMap.put('\uFF9D', "\uFF9D");
-        sHalfWidthMap.put('\uFF9E', "\uFF9E");
-        sHalfWidthMap.put('\uFF9F', "\uFF9F");
-        sHalfWidthMap.put('\uFFE5', "\u005C\u005C");
-    }
-
-    /**
-     * Returns half-width version of that character if possible. Returns null if not possible
-     * @param ch input character
-     * @return CharSequence object if the mapping for ch exists. Return null otherwise.
-     */
-    public static String tryGetHalfWidthText(final char ch) {
-        if (sHalfWidthMap.containsKey(ch)) {
-            return sHalfWidthMap.get(ch);
-        } else {
-            return null;
-        }
-    }
-}
diff --git a/core/java/android/pim/vcard/VCardBuilder.java b/core/java/android/pim/vcard/VCardBuilder.java
deleted file mode 100644
index 58f6ebe..0000000
--- a/core/java/android/pim/vcard/VCardBuilder.java
+++ /dev/null
@@ -1,2147 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.pim.vcard;
-
-import android.content.ContentValues;
-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.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.CharsetUtils;
-import android.util.Log;
-
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.UnsupportedCharsetException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * <p>
- * The class which lets users create their own vCard String. Typical usage is as follows:
- * </p>
- * <pre class="prettyprint">final VCardBuilder builder = new VCardBuilder(vcardType);
- * builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
- *     .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
- *     .appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
- *     .appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE))
- *     .appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE))
- *     .appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE))
- *     .appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE))
- *     .appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE))
- *     .appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE))
- *     .appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE))
- *     .appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE))
- *     .appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE));
- * return builder.toString();</pre>
- */
-public class VCardBuilder {
-    private static final String LOG_TAG = "VCardBuilder";
-
-    // If you add the other element, please check all the columns are able to be
-    // converted to String.
-    //
-    // e.g. BLOB is not what we can handle here now.
-    private static final Set<String> sAllowedAndroidPropertySet =
-            Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
-                    Nickname.CONTENT_ITEM_TYPE, Event.CONTENT_ITEM_TYPE,
-                    Relation.CONTENT_ITEM_TYPE)));
-
-    public static final int DEFAULT_PHONE_TYPE = Phone.TYPE_HOME;
-    public static final int DEFAULT_POSTAL_TYPE = StructuredPostal.TYPE_HOME;
-    public static final int DEFAULT_EMAIL_TYPE = Email.TYPE_OTHER;
-
-    private static final String VCARD_DATA_VCARD = "VCARD";
-    private static final String VCARD_DATA_PUBLIC = "PUBLIC";
-
-    private static final String VCARD_PARAM_SEPARATOR = ";";
-    private static final String VCARD_END_OF_LINE = "\r\n";
-    private static final String VCARD_DATA_SEPARATOR = ":";
-    private static final String VCARD_ITEM_SEPARATOR = ";";
-    private static final String VCARD_WS = " ";
-    private static final String VCARD_PARAM_EQUAL = "=";
-
-    private static final String VCARD_PARAM_ENCODING_QP =
-            "ENCODING=" + VCardConstants.PARAM_ENCODING_QP;
-    private static final String VCARD_PARAM_ENCODING_BASE64_V21 =
-            "ENCODING=" + VCardConstants.PARAM_ENCODING_BASE64;
-    private static final String VCARD_PARAM_ENCODING_BASE64_AS_B =
-            "ENCODING=" + VCardConstants.PARAM_ENCODING_B;
-
-    private static final String SHIFT_JIS = "SHIFT_JIS";
-
-    private final int mVCardType;
-
-    private final boolean mIsV30OrV40;
-    private final boolean mIsJapaneseMobilePhone;
-    private final boolean mOnlyOneNoteFieldIsAvailable;
-    private final boolean mIsDoCoMo;
-    private final boolean mShouldUseQuotedPrintable;
-    private final boolean mUsesAndroidProperty;
-    private final boolean mUsesDefactProperty;
-    private final boolean mAppendTypeParamName;
-    private final boolean mRefrainsQPToNameProperties;
-    private final boolean mNeedsToConvertPhoneticString;
-
-    private final boolean mShouldAppendCharsetParam;
-
-    private final String mCharset;
-    private final String mVCardCharsetParameter;
-
-    private StringBuilder mBuilder;
-    private boolean mEndAppended;
-
-    public VCardBuilder(final int vcardType) {
-        // Default charset should be used
-        this(vcardType, null);
-    }
-
-    /**
-     * @param vcardType
-     * @param charset If null, we use default charset for export.
-     * @hide
-     */
-    public VCardBuilder(final int vcardType, String charset) {
-        mVCardType = vcardType;
-
-        if (VCardConfig.isVersion40(vcardType)) {
-            Log.w(LOG_TAG,
-                    "Should not use vCard 4.0 when building vCard. " +
-                    "It is not officially published yet.");
-        }
-
-        mIsV30OrV40 = VCardConfig.isVersion30(vcardType) || VCardConfig.isVersion40(vcardType);
-        mShouldUseQuotedPrintable = VCardConfig.shouldUseQuotedPrintable(vcardType);
-        mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
-        mIsJapaneseMobilePhone = VCardConfig.needsToConvertPhoneticString(vcardType);
-        mOnlyOneNoteFieldIsAvailable = VCardConfig.onlyOneNoteFieldIsAvailable(vcardType);
-        mUsesAndroidProperty = VCardConfig.usesAndroidSpecificProperty(vcardType);
-        mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType);
-        mRefrainsQPToNameProperties = VCardConfig.shouldRefrainQPToNameProperties(vcardType);
-        mAppendTypeParamName = VCardConfig.appendTypeParamName(vcardType);
-        mNeedsToConvertPhoneticString = VCardConfig.needsToConvertPhoneticString(vcardType);
-
-        // vCard 2.1 requires charset.
-        // vCard 3.0 does not allow it but we found some devices use it to determine
-        // the exact charset.
-        // We currently append it only when charset other than UTF_8 is used.
-        mShouldAppendCharsetParam =
-                !(VCardConfig.isVersion30(vcardType) && "UTF-8".equalsIgnoreCase(charset));
-
-        if (VCardConfig.isDoCoMo(vcardType)) {
-            if (!SHIFT_JIS.equalsIgnoreCase(charset)) {
-                Log.w(LOG_TAG,
-                        "The charset \"" + charset + "\" is used while "
-                        + SHIFT_JIS + " is needed to be used.");
-                if (TextUtils.isEmpty(charset)) {
-                    mCharset = SHIFT_JIS;
-                } else {
-                    try {
-                        charset = CharsetUtils.charsetForVendor(charset).name();
-                    } catch (UnsupportedCharsetException e) {
-                        Log.i(LOG_TAG,
-                                "Career-specific \"" + charset + "\" was not found (as usual). "
-                                + "Use it as is.");
-                    }
-                    mCharset = charset;
-                }
-            } else {
-                if (mIsDoCoMo) {
-                    try {
-                        charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
-                    } catch (UnsupportedCharsetException e) {
-                        Log.e(LOG_TAG,
-                                "DoCoMo-specific SHIFT_JIS was not found. "
-                                + "Use SHIFT_JIS as is.");
-                        charset = SHIFT_JIS;
-                    }
-                } else {
-                    try {
-                        charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
-                    } catch (UnsupportedCharsetException e) {
-                        Log.e(LOG_TAG,
-                                "Career-specific SHIFT_JIS was not found. "
-                                + "Use SHIFT_JIS as is.");
-                        charset = SHIFT_JIS;
-                    }
-                }
-                mCharset = charset;
-            }
-            mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS;
-        } else {
-            if (TextUtils.isEmpty(charset)) {
-                Log.i(LOG_TAG,
-                        "Use the charset \"" + VCardConfig.DEFAULT_EXPORT_CHARSET
-                        + "\" for export.");
-                mCharset = VCardConfig.DEFAULT_EXPORT_CHARSET;
-                mVCardCharsetParameter = "CHARSET=" + VCardConfig.DEFAULT_EXPORT_CHARSET;
-            } else {
-                try {
-                    charset = CharsetUtils.charsetForVendor(charset).name();
-                } catch (UnsupportedCharsetException e) {
-                    Log.i(LOG_TAG,
-                            "Career-specific \"" + charset + "\" was not found (as usual). "
-                            + "Use it as is.");
-                }
-                mCharset = charset;
-                mVCardCharsetParameter = "CHARSET=" + charset;
-            }
-        }
-        clear();
-    }
-
-    public void clear() {
-        mBuilder = new StringBuilder();
-        mEndAppended = false;
-        appendLine(VCardConstants.PROPERTY_BEGIN, VCARD_DATA_VCARD);
-        if (VCardConfig.isVersion40(mVCardType)) {
-            appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V40);
-        } else if (VCardConfig.isVersion30(mVCardType)) {
-            appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V30);
-        } else {
-            if (!VCardConfig.isVersion21(mVCardType)) {
-                Log.w(LOG_TAG, "Unknown vCard version detected.");
-            }
-            appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V21);
-        }
-    }
-
-    private boolean containsNonEmptyName(final ContentValues contentValues) {
-        final String familyName = contentValues.getAsString(StructuredName.FAMILY_NAME);
-        final String middleName = contentValues.getAsString(StructuredName.MIDDLE_NAME);
-        final String givenName = contentValues.getAsString(StructuredName.GIVEN_NAME);
-        final String prefix = contentValues.getAsString(StructuredName.PREFIX);
-        final String suffix = contentValues.getAsString(StructuredName.SUFFIX);
-        final String phoneticFamilyName =
-                contentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME);
-        final String phoneticMiddleName =
-                contentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
-        final String phoneticGivenName =
-                contentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME);
-        final String displayName = contentValues.getAsString(StructuredName.DISPLAY_NAME);
-        return !(TextUtils.isEmpty(familyName) && TextUtils.isEmpty(middleName) &&
-                TextUtils.isEmpty(givenName) && TextUtils.isEmpty(prefix) &&
-                TextUtils.isEmpty(suffix) && TextUtils.isEmpty(phoneticFamilyName) &&
-                TextUtils.isEmpty(phoneticMiddleName) && TextUtils.isEmpty(phoneticGivenName) &&
-                TextUtils.isEmpty(displayName));
-    }
-
-    private ContentValues getPrimaryContentValue(final List<ContentValues> contentValuesList) {
-        ContentValues primaryContentValues = null;
-        ContentValues subprimaryContentValues = null;
-        for (ContentValues contentValues : contentValuesList) {
-            if (contentValues == null){
-                continue;
-            }
-            Integer isSuperPrimary = contentValues.getAsInteger(StructuredName.IS_SUPER_PRIMARY);
-            if (isSuperPrimary != null && isSuperPrimary > 0) {
-                // We choose "super primary" ContentValues.
-                primaryContentValues = contentValues;
-                break;
-            } else if (primaryContentValues == null) {
-                // We choose the first "primary" ContentValues
-                // if "super primary" ContentValues does not exist.
-                final Integer isPrimary = contentValues.getAsInteger(StructuredName.IS_PRIMARY);
-                if (isPrimary != null && isPrimary > 0 &&
-                        containsNonEmptyName(contentValues)) {
-                    primaryContentValues = contentValues;
-                    // Do not break, since there may be ContentValues with "super primary"
-                    // afterword.
-                } else if (subprimaryContentValues == null &&
-                        containsNonEmptyName(contentValues)) {
-                    subprimaryContentValues = contentValues;
-                }
-            }
-        }
-
-        if (primaryContentValues == null) {
-            if (subprimaryContentValues != null) {
-                // We choose the first ContentValues if any "primary" ContentValues does not exist.
-                primaryContentValues = subprimaryContentValues;
-            } else {
-                Log.e(LOG_TAG, "All ContentValues given from database is empty.");
-                primaryContentValues = new ContentValues();
-            }
-        }
-
-        return primaryContentValues;
-    }
-
-    /**
-     * To avoid unnecessary complication in logic, we use this method to construct N, FN
-     * properties for vCard 4.0.
-     */
-    private VCardBuilder appendNamePropertiesV40(final List<ContentValues> contentValuesList) {
-        if (mIsDoCoMo || mNeedsToConvertPhoneticString) {
-            // Ignore all flags that look stale from the view of vCard 4.0 to
-            // simplify construction algorithm. Actually we don't have any vCard file
-            // available from real world yet, so we may need to re-enable some of these
-            // in the future.
-            Log.w(LOG_TAG, "Invalid flag is used in vCard 4.0 construction. Ignored.");
-        }
-
-        if (contentValuesList == null || contentValuesList.isEmpty()) {
-            appendLine(VCardConstants.PROPERTY_FN, "");
-            return this;
-        }
-
-        // We have difficulty here. How can we appropriately handle StructuredName with
-        // missing parts necessary for displaying while it has suppremental information.
-        //
-        // e.g. How to handle non-empty phonetic names with empty structured names?
-
-        final ContentValues contentValues = getPrimaryContentValue(contentValuesList);
-        String familyName = contentValues.getAsString(StructuredName.FAMILY_NAME);
-        final String middleName = contentValues.getAsString(StructuredName.MIDDLE_NAME);
-        final String givenName = contentValues.getAsString(StructuredName.GIVEN_NAME);
-        final String prefix = contentValues.getAsString(StructuredName.PREFIX);
-        final String suffix = contentValues.getAsString(StructuredName.SUFFIX);
-        final String formattedName = contentValues.getAsString(StructuredName.DISPLAY_NAME);
-        if (TextUtils.isEmpty(familyName)
-                && TextUtils.isEmpty(givenName)
-                && TextUtils.isEmpty(middleName)
-                && TextUtils.isEmpty(prefix)
-                && TextUtils.isEmpty(suffix)) {
-            if (TextUtils.isEmpty(formattedName)) {
-                appendLine(VCardConstants.PROPERTY_FN, "");
-                return this;
-            }
-            familyName = formattedName;
-        }
-
-        final String phoneticFamilyName =
-                contentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME);
-        final String phoneticMiddleName =
-                contentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
-        final String phoneticGivenName =
-                contentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME);
-        final String escapedFamily = escapeCharacters(familyName);
-        final String escapedGiven = escapeCharacters(givenName);
-        final String escapedMiddle = escapeCharacters(middleName);
-        final String escapedPrefix = escapeCharacters(prefix);
-        final String escapedSuffix = escapeCharacters(suffix);
-
-        mBuilder.append(VCardConstants.PROPERTY_N);
-
-        if (!(TextUtils.isEmpty(phoneticFamilyName) &&
-                        TextUtils.isEmpty(phoneticMiddleName) &&
-                        TextUtils.isEmpty(phoneticGivenName))) {
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            final String sortAs = escapeCharacters(phoneticFamilyName)
-                    + ';' + escapeCharacters(phoneticGivenName)
-                    + ';' + escapeCharacters(phoneticMiddleName);
-            mBuilder.append("SORT-AS=").append(
-                    VCardUtils.toStringAsV40ParamValue(sortAs));
-        }
-
-        mBuilder.append(VCARD_DATA_SEPARATOR);
-        mBuilder.append(escapedFamily);
-        mBuilder.append(VCARD_ITEM_SEPARATOR);
-        mBuilder.append(escapedGiven);
-        mBuilder.append(VCARD_ITEM_SEPARATOR);
-        mBuilder.append(escapedMiddle);
-        mBuilder.append(VCARD_ITEM_SEPARATOR);
-        mBuilder.append(escapedPrefix);
-        mBuilder.append(VCARD_ITEM_SEPARATOR);
-        mBuilder.append(escapedSuffix);
-        mBuilder.append(VCARD_END_OF_LINE);
-
-        if (TextUtils.isEmpty(formattedName)) {
-            // Note:
-            // DISPLAY_NAME doesn't exist while some other elements do, which is usually
-            // weird in Android, as DISPLAY_NAME should (usually) be constructed
-            // from the others using locale information and its code points.
-            Log.w(LOG_TAG, "DISPLAY_NAME is empty.");
-
-            final String escaped = escapeCharacters(VCardUtils.constructNameFromElements(
-                    VCardConfig.getNameOrderType(mVCardType),
-                    familyName, middleName, givenName, prefix, suffix));
-            appendLine(VCardConstants.PROPERTY_FN, escaped);
-        } else {
-            final String escapedFormatted = escapeCharacters(formattedName);
-            mBuilder.append(VCardConstants.PROPERTY_FN);
-            mBuilder.append(VCARD_DATA_SEPARATOR);
-            mBuilder.append(escapedFormatted);
-            mBuilder.append(VCARD_END_OF_LINE);
-        }
-
-        // We may need X- properties for phonetic names.
-        appendPhoneticNameFields(contentValues);
-        return this;
-    }
-
-    /**
-     * For safety, we'll emit just one value around StructuredName, as external importers
-     * may get confused with multiple "N", "FN", etc. properties, though it is valid in
-     * vCard spec.
-     */
-    public VCardBuilder appendNameProperties(final List<ContentValues> contentValuesList) {
-        if (VCardConfig.isVersion40(mVCardType)) {
-            return appendNamePropertiesV40(contentValuesList);
-        }
-
-        if (contentValuesList == null || contentValuesList.isEmpty()) {
-            if (VCardConfig.isVersion30(mVCardType)) {
-                // vCard 3.0 requires "N" and "FN" properties.
-                // vCard 4.0 does NOT require N, but we take care of possible backward
-                // compatibility issues.
-                appendLine(VCardConstants.PROPERTY_N, "");
-                appendLine(VCardConstants.PROPERTY_FN, "");
-            } else if (mIsDoCoMo) {
-                appendLine(VCardConstants.PROPERTY_N, "");
-            }
-            return this;
-        }
-
-        final ContentValues contentValues = getPrimaryContentValue(contentValuesList);
-        final String familyName = contentValues.getAsString(StructuredName.FAMILY_NAME);
-        final String middleName = contentValues.getAsString(StructuredName.MIDDLE_NAME);
-        final String givenName = contentValues.getAsString(StructuredName.GIVEN_NAME);
-        final String prefix = contentValues.getAsString(StructuredName.PREFIX);
-        final String suffix = contentValues.getAsString(StructuredName.SUFFIX);
-        final String displayName = contentValues.getAsString(StructuredName.DISPLAY_NAME);
-
-        if (!TextUtils.isEmpty(familyName) || !TextUtils.isEmpty(givenName)) {
-            final boolean reallyAppendCharsetParameterToName =
-                    shouldAppendCharsetParam(familyName, givenName, middleName, prefix, suffix);
-            final boolean reallyUseQuotedPrintableToName =
-                    (!mRefrainsQPToNameProperties &&
-                            !(VCardUtils.containsOnlyNonCrLfPrintableAscii(familyName) &&
-                                    VCardUtils.containsOnlyNonCrLfPrintableAscii(givenName) &&
-                                    VCardUtils.containsOnlyNonCrLfPrintableAscii(middleName) &&
-                                    VCardUtils.containsOnlyNonCrLfPrintableAscii(prefix) &&
-                                    VCardUtils.containsOnlyNonCrLfPrintableAscii(suffix)));
-
-            final String formattedName;
-            if (!TextUtils.isEmpty(displayName)) {
-                formattedName = displayName;
-            } else {
-                formattedName = VCardUtils.constructNameFromElements(
-                        VCardConfig.getNameOrderType(mVCardType),
-                        familyName, middleName, givenName, prefix, suffix);
-            }
-            final boolean reallyAppendCharsetParameterToFN =
-                    shouldAppendCharsetParam(formattedName);
-            final boolean reallyUseQuotedPrintableToFN =
-                    !mRefrainsQPToNameProperties &&
-                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(formattedName);
-
-            final String encodedFamily;
-            final String encodedGiven;
-            final String encodedMiddle;
-            final String encodedPrefix;
-            final String encodedSuffix;
-            if (reallyUseQuotedPrintableToName) {
-                encodedFamily = encodeQuotedPrintable(familyName);
-                encodedGiven = encodeQuotedPrintable(givenName);
-                encodedMiddle = encodeQuotedPrintable(middleName);
-                encodedPrefix = encodeQuotedPrintable(prefix);
-                encodedSuffix = encodeQuotedPrintable(suffix);
-            } else {
-                encodedFamily = escapeCharacters(familyName);
-                encodedGiven = escapeCharacters(givenName);
-                encodedMiddle = escapeCharacters(middleName);
-                encodedPrefix = escapeCharacters(prefix);
-                encodedSuffix = escapeCharacters(suffix);
-            }
-
-            final String encodedFormattedname =
-                    (reallyUseQuotedPrintableToFN ?
-                            encodeQuotedPrintable(formattedName) : escapeCharacters(formattedName));
-
-            mBuilder.append(VCardConstants.PROPERTY_N);
-            if (mIsDoCoMo) {
-                if (reallyAppendCharsetParameterToName) {
-                    mBuilder.append(VCARD_PARAM_SEPARATOR);
-                    mBuilder.append(mVCardCharsetParameter);
-                }
-                if (reallyUseQuotedPrintableToName) {
-                    mBuilder.append(VCARD_PARAM_SEPARATOR);
-                    mBuilder.append(VCARD_PARAM_ENCODING_QP);
-                }
-                mBuilder.append(VCARD_DATA_SEPARATOR);
-                // DoCoMo phones require that all the elements in the "family name" field.
-                mBuilder.append(formattedName);
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-            } else {
-                if (reallyAppendCharsetParameterToName) {
-                    mBuilder.append(VCARD_PARAM_SEPARATOR);
-                    mBuilder.append(mVCardCharsetParameter);
-                }
-                if (reallyUseQuotedPrintableToName) {
-                    mBuilder.append(VCARD_PARAM_SEPARATOR);
-                    mBuilder.append(VCARD_PARAM_ENCODING_QP);
-                }
-                mBuilder.append(VCARD_DATA_SEPARATOR);
-                mBuilder.append(encodedFamily);
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-                mBuilder.append(encodedGiven);
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-                mBuilder.append(encodedMiddle);
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-                mBuilder.append(encodedPrefix);
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-                mBuilder.append(encodedSuffix);
-            }
-            mBuilder.append(VCARD_END_OF_LINE);
-
-            // FN property
-            mBuilder.append(VCardConstants.PROPERTY_FN);
-            if (reallyAppendCharsetParameterToFN) {
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(mVCardCharsetParameter);
-            }
-            if (reallyUseQuotedPrintableToFN) {
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(VCARD_PARAM_ENCODING_QP);
-            }
-            mBuilder.append(VCARD_DATA_SEPARATOR);
-            mBuilder.append(encodedFormattedname);
-            mBuilder.append(VCARD_END_OF_LINE);
-        } else if (!TextUtils.isEmpty(displayName)) {
-            final boolean reallyUseQuotedPrintableToDisplayName =
-                (!mRefrainsQPToNameProperties &&
-                        !VCardUtils.containsOnlyNonCrLfPrintableAscii(displayName));
-            final String encodedDisplayName =
-                    reallyUseQuotedPrintableToDisplayName ?
-                            encodeQuotedPrintable(displayName) :
-                                escapeCharacters(displayName);
-
-            // N
-            mBuilder.append(VCardConstants.PROPERTY_N);
-            if (shouldAppendCharsetParam(displayName)) {
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(mVCardCharsetParameter);
-            }
-            if (reallyUseQuotedPrintableToDisplayName) {
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(VCARD_PARAM_ENCODING_QP);
-            }
-            mBuilder.append(VCARD_DATA_SEPARATOR);
-            mBuilder.append(encodedDisplayName);
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
-            mBuilder.append(VCARD_END_OF_LINE);
-
-            // FN
-            mBuilder.append(VCardConstants.PROPERTY_FN);
-
-            // Note: "CHARSET" param is not allowed in vCard 3.0, but we may add it
-            //       when it would be useful or necessary for external importers,
-            //       assuming the external importer allows this vioration of the spec.
-            if (shouldAppendCharsetParam(displayName)) {
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(mVCardCharsetParameter);
-            }
-            mBuilder.append(VCARD_DATA_SEPARATOR);
-            mBuilder.append(encodedDisplayName);
-            mBuilder.append(VCARD_END_OF_LINE);
-        } else if (VCardConfig.isVersion30(mVCardType)) {
-            appendLine(VCardConstants.PROPERTY_N, "");
-            appendLine(VCardConstants.PROPERTY_FN, "");
-        } else if (mIsDoCoMo) {
-            appendLine(VCardConstants.PROPERTY_N, "");
-        }
-
-        appendPhoneticNameFields(contentValues);
-        return this;
-    }
-
-    /**
-     * Emits SOUND;IRMC, SORT-STRING, and de-fact values for phonetic names like X-PHONETIC-FAMILY.
-     */
-    private void appendPhoneticNameFields(final ContentValues contentValues) {
-        final String phoneticFamilyName;
-        final String phoneticMiddleName;
-        final String phoneticGivenName;
-        {
-            final String tmpPhoneticFamilyName =
-                contentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME);
-            final String tmpPhoneticMiddleName =
-                contentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
-            final String tmpPhoneticGivenName =
-                contentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME);
-            if (mNeedsToConvertPhoneticString) {
-                phoneticFamilyName = VCardUtils.toHalfWidthString(tmpPhoneticFamilyName);
-                phoneticMiddleName = VCardUtils.toHalfWidthString(tmpPhoneticMiddleName);
-                phoneticGivenName = VCardUtils.toHalfWidthString(tmpPhoneticGivenName);
-            } else {
-                phoneticFamilyName = tmpPhoneticFamilyName;
-                phoneticMiddleName = tmpPhoneticMiddleName;
-                phoneticGivenName = tmpPhoneticGivenName;
-            }
-        }
-
-        if (TextUtils.isEmpty(phoneticFamilyName)
-                && TextUtils.isEmpty(phoneticMiddleName)
-                && TextUtils.isEmpty(phoneticGivenName)) {
-            if (mIsDoCoMo) {
-                mBuilder.append(VCardConstants.PROPERTY_SOUND);
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(VCardConstants.PARAM_TYPE_X_IRMC_N);
-                mBuilder.append(VCARD_DATA_SEPARATOR);
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-                mBuilder.append(VCARD_END_OF_LINE);
-            }
-            return;
-        }
-
-        if (VCardConfig.isVersion40(mVCardType)) {
-            // We don't want SORT-STRING anyway.
-        } else if (VCardConfig.isVersion30(mVCardType)) {
-            final String sortString =
-                    VCardUtils.constructNameFromElements(mVCardType,
-                            phoneticFamilyName, phoneticMiddleName, phoneticGivenName);
-            mBuilder.append(VCardConstants.PROPERTY_SORT_STRING);
-            if (VCardConfig.isVersion30(mVCardType) && shouldAppendCharsetParam(sortString)) {
-                // vCard 3.0 does not force us to use UTF-8 and actually we see some
-                // programs which emit this value. It is incorrect from the view of
-                // specification, but actually necessary for parsing vCard with non-UTF-8
-                // charsets, expecting other parsers not get confused with this value.
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(mVCardCharsetParameter);
-            }
-            mBuilder.append(VCARD_DATA_SEPARATOR);
-            mBuilder.append(escapeCharacters(sortString));
-            mBuilder.append(VCARD_END_OF_LINE);
-        } else if (mIsJapaneseMobilePhone) {
-            // Note: There is no appropriate property for expressing
-            //       phonetic name (Yomigana in Japanese) in vCard 2.1, while there is in
-            //       vCard 3.0 (SORT-STRING).
-            //       We use DoCoMo's way when the device is Japanese one since it is already
-            //       supported by a lot of Japanese mobile phones.
-            //       This is "X-" property, so any parser hopefully would not get
-            //       confused with this.
-            //
-            //       Also, DoCoMo's specification requires vCard composer to use just the first
-            //       column.
-            //       i.e.
-            //       good:  SOUND;X-IRMC-N:Miyakawa Daisuke;;;;
-            //       bad :  SOUND;X-IRMC-N:Miyakawa;Daisuke;;;
-            mBuilder.append(VCardConstants.PROPERTY_SOUND);
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            mBuilder.append(VCardConstants.PARAM_TYPE_X_IRMC_N);
-
-            boolean reallyUseQuotedPrintable =
-                (!mRefrainsQPToNameProperties
-                        && !(VCardUtils.containsOnlyNonCrLfPrintableAscii(
-                                phoneticFamilyName)
-                                && VCardUtils.containsOnlyNonCrLfPrintableAscii(
-                                        phoneticMiddleName)
-                                && VCardUtils.containsOnlyNonCrLfPrintableAscii(
-                                        phoneticGivenName)));
-
-            final String encodedPhoneticFamilyName;
-            final String encodedPhoneticMiddleName;
-            final String encodedPhoneticGivenName;
-            if (reallyUseQuotedPrintable) {
-                encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName);
-                encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName);
-                encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName);
-            } else {
-                encodedPhoneticFamilyName = escapeCharacters(phoneticFamilyName);
-                encodedPhoneticMiddleName = escapeCharacters(phoneticMiddleName);
-                encodedPhoneticGivenName = escapeCharacters(phoneticGivenName);
-            }
-
-            if (shouldAppendCharsetParam(encodedPhoneticFamilyName,
-                    encodedPhoneticMiddleName, encodedPhoneticGivenName)) {
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(mVCardCharsetParameter);
-            }
-            mBuilder.append(VCARD_DATA_SEPARATOR);
-            {
-                boolean first = true;
-                if (!TextUtils.isEmpty(encodedPhoneticFamilyName)) {
-                    mBuilder.append(encodedPhoneticFamilyName);
-                    first = false;
-                }
-                if (!TextUtils.isEmpty(encodedPhoneticMiddleName)) {
-                    if (first) {
-                        first = false;
-                    } else {
-                        mBuilder.append(' ');
-                    }
-                    mBuilder.append(encodedPhoneticMiddleName);
-                }
-                if (!TextUtils.isEmpty(encodedPhoneticGivenName)) {
-                    if (!first) {
-                        mBuilder.append(' ');
-                    }
-                    mBuilder.append(encodedPhoneticGivenName);
-                }
-            }
-            mBuilder.append(VCARD_ITEM_SEPARATOR);  // family;given
-            mBuilder.append(VCARD_ITEM_SEPARATOR);  // given;middle
-            mBuilder.append(VCARD_ITEM_SEPARATOR);  // middle;prefix
-            mBuilder.append(VCARD_ITEM_SEPARATOR);  // prefix;suffix
-            mBuilder.append(VCARD_END_OF_LINE);
-        }
-
-        Log.d("@@@", "hoge");
-        if (mUsesDefactProperty) {
-            if (!TextUtils.isEmpty(phoneticGivenName)) {
-                final boolean reallyUseQuotedPrintable =
-                    (mShouldUseQuotedPrintable &&
-                            !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName));
-                final String encodedPhoneticGivenName;
-                if (reallyUseQuotedPrintable) {
-                    encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName);
-                } else {
-                    encodedPhoneticGivenName = escapeCharacters(phoneticGivenName);
-                }
-                mBuilder.append(VCardConstants.PROPERTY_X_PHONETIC_FIRST_NAME);
-                if (shouldAppendCharsetParam(phoneticGivenName)) {
-                    mBuilder.append(VCARD_PARAM_SEPARATOR);
-                    mBuilder.append(mVCardCharsetParameter);
-                }
-                if (reallyUseQuotedPrintable) {
-                    mBuilder.append(VCARD_PARAM_SEPARATOR);
-                    mBuilder.append(VCARD_PARAM_ENCODING_QP);
-                }
-                mBuilder.append(VCARD_DATA_SEPARATOR);
-                mBuilder.append(encodedPhoneticGivenName);
-                mBuilder.append(VCARD_END_OF_LINE);
-            }  // if (!TextUtils.isEmpty(phoneticGivenName))
-            if (!TextUtils.isEmpty(phoneticMiddleName)) {
-                final boolean reallyUseQuotedPrintable =
-                    (mShouldUseQuotedPrintable &&
-                            !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName));
-                final String encodedPhoneticMiddleName;
-                if (reallyUseQuotedPrintable) {
-                    encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName);
-                } else {
-                    encodedPhoneticMiddleName = escapeCharacters(phoneticMiddleName);
-                }
-                mBuilder.append(VCardConstants.PROPERTY_X_PHONETIC_MIDDLE_NAME);
-                if (shouldAppendCharsetParam(phoneticMiddleName)) {
-                    mBuilder.append(VCARD_PARAM_SEPARATOR);
-                    mBuilder.append(mVCardCharsetParameter);
-                }
-                if (reallyUseQuotedPrintable) {
-                    mBuilder.append(VCARD_PARAM_SEPARATOR);
-                    mBuilder.append(VCARD_PARAM_ENCODING_QP);
-                }
-                mBuilder.append(VCARD_DATA_SEPARATOR);
-                mBuilder.append(encodedPhoneticMiddleName);
-                mBuilder.append(VCARD_END_OF_LINE);
-            }  // if (!TextUtils.isEmpty(phoneticGivenName))
-            if (!TextUtils.isEmpty(phoneticFamilyName)) {
-                final boolean reallyUseQuotedPrintable =
-                    (mShouldUseQuotedPrintable &&
-                            !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName));
-                final String encodedPhoneticFamilyName;
-                if (reallyUseQuotedPrintable) {
-                    encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName);
-                } else {
-                    encodedPhoneticFamilyName = escapeCharacters(phoneticFamilyName);
-                }
-                mBuilder.append(VCardConstants.PROPERTY_X_PHONETIC_LAST_NAME);
-                if (shouldAppendCharsetParam(phoneticFamilyName)) {
-                    mBuilder.append(VCARD_PARAM_SEPARATOR);
-                    mBuilder.append(mVCardCharsetParameter);
-                }
-                if (reallyUseQuotedPrintable) {
-                    mBuilder.append(VCARD_PARAM_SEPARATOR);
-                    mBuilder.append(VCARD_PARAM_ENCODING_QP);
-                }
-                mBuilder.append(VCARD_DATA_SEPARATOR);
-                mBuilder.append(encodedPhoneticFamilyName);
-                mBuilder.append(VCARD_END_OF_LINE);
-            }  // if (!TextUtils.isEmpty(phoneticFamilyName))
-        }
-    }
-
-    public VCardBuilder appendNickNames(final List<ContentValues> contentValuesList) {
-        final boolean useAndroidProperty;
-        if (mIsV30OrV40) {   // These specifications have NICKNAME property.
-            useAndroidProperty = false;
-        } else if (mUsesAndroidProperty) {
-            useAndroidProperty = true;
-        } else {
-            // There's no way to add this field.
-            return this;
-        }
-        if (contentValuesList != null) {
-            for (ContentValues contentValues : contentValuesList) {
-                final String nickname = contentValues.getAsString(Nickname.NAME);
-                if (TextUtils.isEmpty(nickname)) {
-                    continue;
-                }
-                if (useAndroidProperty) {
-                    appendAndroidSpecificProperty(Nickname.CONTENT_ITEM_TYPE, contentValues);
-                } else {
-                    appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_NICKNAME, nickname);
-                }
-            }
-        }
-        return this;
-    }
-
-    public VCardBuilder appendPhones(final List<ContentValues> contentValuesList) {
-        boolean phoneLineExists = false;
-        if (contentValuesList != null) {
-            Set<String> phoneSet = new HashSet<String>();
-            for (ContentValues contentValues : contentValuesList) {
-                final Integer typeAsObject = contentValues.getAsInteger(Phone.TYPE);
-                final String label = contentValues.getAsString(Phone.LABEL);
-                final Integer isPrimaryAsInteger = contentValues.getAsInteger(Phone.IS_PRIMARY);
-                final boolean isPrimary = (isPrimaryAsInteger != null ?
-                        (isPrimaryAsInteger > 0) : false);
-                String phoneNumber = contentValues.getAsString(Phone.NUMBER);
-                if (phoneNumber != null) {
-                    phoneNumber = phoneNumber.trim();
-                }
-                if (TextUtils.isEmpty(phoneNumber)) {
-                    continue;
-                }
-
-                // PAGER number needs unformatted "phone number".
-                final int type = (typeAsObject != null ? typeAsObject : DEFAULT_PHONE_TYPE);
-                if (type == Phone.TYPE_PAGER ||
-                        VCardConfig.refrainPhoneNumberFormatting(mVCardType)) {
-                    phoneLineExists = true;
-                    if (!phoneSet.contains(phoneNumber)) {
-                        phoneSet.add(phoneNumber);
-                        appendTelLine(type, label, phoneNumber, isPrimary);
-                    }
-                } else {
-                    final List<String> phoneNumberList = splitAndTrimPhoneNumbers(phoneNumber);
-                    if (phoneNumberList.isEmpty()) {
-                        continue;
-                    }
-                    phoneLineExists = true;
-                    for (String actualPhoneNumber : phoneNumberList) {
-                        if (!phoneSet.contains(actualPhoneNumber)) {
-                            final int format = VCardUtils.getPhoneNumberFormat(mVCardType);
-                            final String formattedPhoneNumber =
-                                    PhoneNumberUtils.formatNumber(actualPhoneNumber, format);
-                            phoneSet.add(actualPhoneNumber);
-                            appendTelLine(type, label, formattedPhoneNumber, isPrimary);
-                        }
-                    }  // for (String actualPhoneNumber : phoneNumberList) {
-                }
-            }
-        }
-
-        if (!phoneLineExists && mIsDoCoMo) {
-            appendTelLine(Phone.TYPE_HOME, "", "", false);
-        }
-
-        return this;
-    }
-
-    /**
-     * <p>
-     * Splits a given string expressing phone numbers into several strings, and remove
-     * unnecessary characters inside them. The size of a returned list becomes 1 when
-     * no split is needed.
-     * </p>
-     * <p>
-     * The given number "may" have several phone numbers when the contact entry is corrupted
-     * because of its original source.
-     * e.g. "111-222-3333 (Miami)\n444-555-6666 (Broward; 305-653-6796 (Miami)"
-     * </p>
-     * <p>
-     * This kind of "phone numbers" will not be created with Android vCard implementation,
-     * but we may encounter them if the source of the input data has already corrupted
-     * implementation.
-     * </p>
-     * <p>
-     * To handle this case, this method first splits its input into multiple parts
-     * (e.g. "111-222-3333 (Miami)", "444-555-6666 (Broward", and 305653-6796 (Miami)") and
-     * removes unnecessary strings like "(Miami)".
-     * </p>
-     * <p>
-     * Do not call this method when trimming is inappropriate for its receivers.
-     * </p>
-     */
-    private List<String> splitAndTrimPhoneNumbers(final String phoneNumber) {
-        final List<String> phoneList = new ArrayList<String>();
-
-        StringBuilder builder = new StringBuilder();
-        final int length = phoneNumber.length();
-        for (int i = 0; i < length; i++) {
-            final char ch = phoneNumber.charAt(i);
-            if (Character.isDigit(ch) || ch == '+') {
-                builder.append(ch);
-            } else if ((ch == ';' || ch == '\n') && builder.length() > 0) {
-                phoneList.add(builder.toString());
-                builder = new StringBuilder();
-            }
-        }
-        if (builder.length() > 0) {
-            phoneList.add(builder.toString());
-        }
-
-        return phoneList;
-    }
-
-    public VCardBuilder appendEmails(final List<ContentValues> contentValuesList) {
-        boolean emailAddressExists = false;
-        if (contentValuesList != null) {
-            final Set<String> addressSet = new HashSet<String>();
-            for (ContentValues contentValues : contentValuesList) {
-                String emailAddress = contentValues.getAsString(Email.DATA);
-                if (emailAddress != null) {
-                    emailAddress = emailAddress.trim();
-                }
-                if (TextUtils.isEmpty(emailAddress)) {
-                    continue;
-                }
-                Integer typeAsObject = contentValues.getAsInteger(Email.TYPE);
-                final int type = (typeAsObject != null ?
-                        typeAsObject : DEFAULT_EMAIL_TYPE);
-                final String label = contentValues.getAsString(Email.LABEL);
-                Integer isPrimaryAsInteger = contentValues.getAsInteger(Email.IS_PRIMARY);
-                final boolean isPrimary = (isPrimaryAsInteger != null ?
-                        (isPrimaryAsInteger > 0) : false);
-                emailAddressExists = true;
-                if (!addressSet.contains(emailAddress)) {
-                    addressSet.add(emailAddress);
-                    appendEmailLine(type, label, emailAddress, isPrimary);
-                }
-            }
-        }
-
-        if (!emailAddressExists && mIsDoCoMo) {
-            appendEmailLine(Email.TYPE_HOME, "", "", false);
-        }
-
-        return this;
-    }
-
-    public VCardBuilder appendPostals(final List<ContentValues> contentValuesList) {
-        if (contentValuesList == null || contentValuesList.isEmpty()) {
-            if (mIsDoCoMo) {
-                mBuilder.append(VCardConstants.PROPERTY_ADR);
-                mBuilder.append(VCARD_PARAM_SEPARATOR);
-                mBuilder.append(VCardConstants.PARAM_TYPE_HOME);
-                mBuilder.append(VCARD_DATA_SEPARATOR);
-                mBuilder.append(VCARD_END_OF_LINE);
-            }
-        } else {
-            if (mIsDoCoMo) {
-                appendPostalsForDoCoMo(contentValuesList);
-            } else {
-                appendPostalsForGeneric(contentValuesList);
-            }
-        }
-
-        return this;
-    }
-
-    private static final Map<Integer, Integer> sPostalTypePriorityMap;
-
-    static {
-        sPostalTypePriorityMap = new HashMap<Integer, Integer>();
-        sPostalTypePriorityMap.put(StructuredPostal.TYPE_HOME, 0);
-        sPostalTypePriorityMap.put(StructuredPostal.TYPE_WORK, 1);
-        sPostalTypePriorityMap.put(StructuredPostal.TYPE_OTHER, 2);
-        sPostalTypePriorityMap.put(StructuredPostal.TYPE_CUSTOM, 3);
-    }
-
-    /**
-     * Tries to append just one line. If there's no appropriate address
-     * information, append an empty line.
-     */
-    private void appendPostalsForDoCoMo(final List<ContentValues> contentValuesList) {
-        int currentPriority = Integer.MAX_VALUE;
-        int currentType = Integer.MAX_VALUE;
-        ContentValues currentContentValues = null;
-        for (final ContentValues contentValues : contentValuesList) {
-            if (contentValues == null) {
-                continue;
-            }
-            final Integer typeAsInteger = contentValues.getAsInteger(StructuredPostal.TYPE);
-            final Integer priorityAsInteger = sPostalTypePriorityMap.get(typeAsInteger);
-            final int priority =
-                    (priorityAsInteger != null ? priorityAsInteger : Integer.MAX_VALUE);
-            if (priority < currentPriority) {
-                currentPriority = priority;
-                currentType = typeAsInteger;
-                currentContentValues = contentValues;
-                if (priority == 0) {
-                    break;
-                }
-            }
-        }
-
-        if (currentContentValues == null) {
-            Log.w(LOG_TAG, "Should not come here. Must have at least one postal data.");
-            return;
-        }
-
-        final String label = currentContentValues.getAsString(StructuredPostal.LABEL);
-        appendPostalLine(currentType, label, currentContentValues, false, true);
-    }
-
-    private void appendPostalsForGeneric(final List<ContentValues> contentValuesList) {
-        for (final ContentValues contentValues : contentValuesList) {
-            if (contentValues == null) {
-                continue;
-            }
-            final Integer typeAsInteger = contentValues.getAsInteger(StructuredPostal.TYPE);
-            final int type = (typeAsInteger != null ?
-                    typeAsInteger : DEFAULT_POSTAL_TYPE);
-            final String label = contentValues.getAsString(StructuredPostal.LABEL);
-            final Integer isPrimaryAsInteger =
-                contentValues.getAsInteger(StructuredPostal.IS_PRIMARY);
-            final boolean isPrimary = (isPrimaryAsInteger != null ?
-                    (isPrimaryAsInteger > 0) : false);
-            appendPostalLine(type, label, contentValues, isPrimary, false);
-        }
-    }
-
-    private static class PostalStruct {
-        final boolean reallyUseQuotedPrintable;
-        final boolean appendCharset;
-        final String addressData;
-        public PostalStruct(final boolean reallyUseQuotedPrintable,
-                final boolean appendCharset, final String addressData) {
-            this.reallyUseQuotedPrintable = reallyUseQuotedPrintable;
-            this.appendCharset = appendCharset;
-            this.addressData = addressData;
-        }
-    }
-
-    /**
-     * @return null when there's no information available to construct the data.
-     */
-    private PostalStruct tryConstructPostalStruct(ContentValues contentValues) {
-        // adr-value    = 0*6(text-value ";") text-value
-        //              ; PO Box, Extended Address, Street, Locality, Region, Postal
-        //              ; Code, Country Name
-        final String rawPoBox = contentValues.getAsString(StructuredPostal.POBOX);
-        final String rawNeighborhood = contentValues.getAsString(StructuredPostal.NEIGHBORHOOD);
-        final String rawStreet = contentValues.getAsString(StructuredPostal.STREET);
-        final String rawLocality = contentValues.getAsString(StructuredPostal.CITY);
-        final String rawRegion = contentValues.getAsString(StructuredPostal.REGION);
-        final String rawPostalCode = contentValues.getAsString(StructuredPostal.POSTCODE);
-        final String rawCountry = contentValues.getAsString(StructuredPostal.COUNTRY);
-        final String[] rawAddressArray = new String[]{
-                rawPoBox, rawNeighborhood, rawStreet, rawLocality,
-                rawRegion, rawPostalCode, rawCountry};
-        if (!VCardUtils.areAllEmpty(rawAddressArray)) {
-            final boolean reallyUseQuotedPrintable =
-                (mShouldUseQuotedPrintable &&
-                        !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawAddressArray));
-            final boolean appendCharset =
-                !VCardUtils.containsOnlyPrintableAscii(rawAddressArray);
-            final String encodedPoBox;
-            final String encodedStreet;
-            final String encodedLocality;
-            final String encodedRegion;
-            final String encodedPostalCode;
-            final String encodedCountry;
-            final String encodedNeighborhood;
-
-            final String rawLocality2;
-            // This looks inefficient since we encode rawLocality and rawNeighborhood twice,
-            // but this is intentional.
-            //
-            // QP encoding may add line feeds when needed and the result of
-            // - encodeQuotedPrintable(rawLocality + " " + rawNeighborhood)
-            // may be different from
-            // - encodedLocality + " " + encodedNeighborhood.
-            //
-            // We use safer way.
-            if (TextUtils.isEmpty(rawLocality)) {
-                if (TextUtils.isEmpty(rawNeighborhood)) {
-                    rawLocality2 = "";
-                } else {
-                    rawLocality2 = rawNeighborhood;
-                }
-            } else {
-                if (TextUtils.isEmpty(rawNeighborhood)) {
-                    rawLocality2 = rawLocality;
-                } else {
-                    rawLocality2 = rawLocality + " " + rawNeighborhood;
-                }
-            }
-            if (reallyUseQuotedPrintable) {
-                encodedPoBox = encodeQuotedPrintable(rawPoBox);
-                encodedStreet = encodeQuotedPrintable(rawStreet);
-                encodedLocality = encodeQuotedPrintable(rawLocality2);
-                encodedRegion = encodeQuotedPrintable(rawRegion);
-                encodedPostalCode = encodeQuotedPrintable(rawPostalCode);
-                encodedCountry = encodeQuotedPrintable(rawCountry);
-            } else {
-                encodedPoBox = escapeCharacters(rawPoBox);
-                encodedStreet = escapeCharacters(rawStreet);
-                encodedLocality = escapeCharacters(rawLocality2);
-                encodedRegion = escapeCharacters(rawRegion);
-                encodedPostalCode = escapeCharacters(rawPostalCode);
-                encodedCountry = escapeCharacters(rawCountry);
-                encodedNeighborhood = escapeCharacters(rawNeighborhood);
-            }
-            final StringBuilder addressBuilder = new StringBuilder();
-            addressBuilder.append(encodedPoBox);
-            addressBuilder.append(VCARD_ITEM_SEPARATOR);  // PO BOX ; Extended Address
-            addressBuilder.append(VCARD_ITEM_SEPARATOR);  // Extended Address : Street
-            addressBuilder.append(encodedStreet);
-            addressBuilder.append(VCARD_ITEM_SEPARATOR);  // Street : Locality
-            addressBuilder.append(encodedLocality);
-            addressBuilder.append(VCARD_ITEM_SEPARATOR);  // Locality : Region
-            addressBuilder.append(encodedRegion);
-            addressBuilder.append(VCARD_ITEM_SEPARATOR);  // Region : Postal Code
-            addressBuilder.append(encodedPostalCode);
-            addressBuilder.append(VCARD_ITEM_SEPARATOR);  // Postal Code : Country
-            addressBuilder.append(encodedCountry);
-            return new PostalStruct(
-                    reallyUseQuotedPrintable, appendCharset, addressBuilder.toString());
-        } else {  // VCardUtils.areAllEmpty(rawAddressArray) == true
-            // Try to use FORMATTED_ADDRESS instead.
-            final String rawFormattedAddress =
-                contentValues.getAsString(StructuredPostal.FORMATTED_ADDRESS);
-            if (TextUtils.isEmpty(rawFormattedAddress)) {
-                return null;
-            }
-            final boolean reallyUseQuotedPrintable =
-                (mShouldUseQuotedPrintable &&
-                        !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawFormattedAddress));
-            final boolean appendCharset =
-                !VCardUtils.containsOnlyPrintableAscii(rawFormattedAddress);
-            final String encodedFormattedAddress;
-            if (reallyUseQuotedPrintable) {
-                encodedFormattedAddress = encodeQuotedPrintable(rawFormattedAddress);
-            } else {
-                encodedFormattedAddress = escapeCharacters(rawFormattedAddress);
-            }
-
-            // We use the second value ("Extended Address") just because Japanese mobile phones
-            // do so. If the other importer expects the value be in the other field, some flag may
-            // be needed.
-            final StringBuilder addressBuilder = new StringBuilder();
-            addressBuilder.append(VCARD_ITEM_SEPARATOR);  // PO BOX ; Extended Address
-            addressBuilder.append(encodedFormattedAddress);
-            addressBuilder.append(VCARD_ITEM_SEPARATOR);  // Extended Address : Street
-            addressBuilder.append(VCARD_ITEM_SEPARATOR);  // Street : Locality
-            addressBuilder.append(VCARD_ITEM_SEPARATOR);  // Locality : Region
-            addressBuilder.append(VCARD_ITEM_SEPARATOR);  // Region : Postal Code
-            addressBuilder.append(VCARD_ITEM_SEPARATOR);  // Postal Code : Country
-            return new PostalStruct(
-                    reallyUseQuotedPrintable, appendCharset, addressBuilder.toString());
-        }
-    }
-
-    public VCardBuilder appendIms(final List<ContentValues> contentValuesList) {
-        if (contentValuesList != null) {
-            for (ContentValues contentValues : contentValuesList) {
-                final Integer protocolAsObject = contentValues.getAsInteger(Im.PROTOCOL);
-                if (protocolAsObject == null) {
-                    continue;
-                }
-                final String propertyName = VCardUtils.getPropertyNameForIm(protocolAsObject);
-                if (propertyName == null) {
-                    continue;
-                }
-                String data = contentValues.getAsString(Im.DATA);
-                if (data != null) {
-                    data = data.trim();
-                }
-                if (TextUtils.isEmpty(data)) {
-                    continue;
-                }
-                final String typeAsString;
-                {
-                    final Integer typeAsInteger = contentValues.getAsInteger(Im.TYPE);
-                    switch (typeAsInteger != null ? typeAsInteger : Im.TYPE_OTHER) {
-                        case Im.TYPE_HOME: {
-                            typeAsString = VCardConstants.PARAM_TYPE_HOME;
-                            break;
-                        }
-                        case Im.TYPE_WORK: {
-                            typeAsString = VCardConstants.PARAM_TYPE_WORK;
-                            break;
-                        }
-                        case Im.TYPE_CUSTOM: {
-                            final String label = contentValues.getAsString(Im.LABEL);
-                            typeAsString = (label != null ? "X-" + label : null);
-                            break;
-                        }
-                        case Im.TYPE_OTHER:  // Ignore
-                        default: {
-                            typeAsString = null;
-                            break;
-                        }
-                    }
-                }
-
-                final List<String> parameterList = new ArrayList<String>();
-                if (!TextUtils.isEmpty(typeAsString)) {
-                    parameterList.add(typeAsString);
-                }
-                final Integer isPrimaryAsInteger = contentValues.getAsInteger(Im.IS_PRIMARY);
-                final boolean isPrimary = (isPrimaryAsInteger != null ?
-                        (isPrimaryAsInteger > 0) : false);
-                if (isPrimary) {
-                    parameterList.add(VCardConstants.PARAM_TYPE_PREF);
-                }
-
-                appendLineWithCharsetAndQPDetection(propertyName, parameterList, data);
-            }
-        }
-        return this;
-    }
-
-    public VCardBuilder appendWebsites(final List<ContentValues> contentValuesList) {
-        if (contentValuesList != null) {
-            for (ContentValues contentValues : contentValuesList) {
-                String website = contentValues.getAsString(Website.URL);
-                if (website != null) {
-                    website = website.trim();
-                }
-
-                // Note: vCard 3.0 does not allow any parameter addition toward "URL"
-                //       property, while there's no document in vCard 2.1.
-                if (!TextUtils.isEmpty(website)) {
-                    appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_URL, website);
-                }
-            }
-        }
-        return this;
-    }
-
-    public VCardBuilder appendOrganizations(final List<ContentValues> contentValuesList) {
-        if (contentValuesList != null) {
-            for (ContentValues contentValues : contentValuesList) {
-                String company = contentValues.getAsString(Organization.COMPANY);
-                if (company != null) {
-                    company = company.trim();
-                }
-                String department = contentValues.getAsString(Organization.DEPARTMENT);
-                if (department != null) {
-                    department = department.trim();
-                }
-                String title = contentValues.getAsString(Organization.TITLE);
-                if (title != null) {
-                    title = title.trim();
-                }
-
-                StringBuilder orgBuilder = new StringBuilder();
-                if (!TextUtils.isEmpty(company)) {
-                    orgBuilder.append(company);
-                }
-                if (!TextUtils.isEmpty(department)) {
-                    if (orgBuilder.length() > 0) {
-                        orgBuilder.append(';');
-                    }
-                    orgBuilder.append(department);
-                }
-                final String orgline = orgBuilder.toString();
-                appendLine(VCardConstants.PROPERTY_ORG, orgline,
-                        !VCardUtils.containsOnlyPrintableAscii(orgline),
-                        (mShouldUseQuotedPrintable &&
-                                !VCardUtils.containsOnlyNonCrLfPrintableAscii(orgline)));
-
-                if (!TextUtils.isEmpty(title)) {
-                    appendLine(VCardConstants.PROPERTY_TITLE, title,
-                            !VCardUtils.containsOnlyPrintableAscii(title),
-                            (mShouldUseQuotedPrintable &&
-                                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(title)));
-                }
-            }
-        }
-        return this;
-    }
-
-    public VCardBuilder appendPhotos(final List<ContentValues> contentValuesList) {
-        if (contentValuesList != null) {
-            for (ContentValues contentValues : contentValuesList) {
-                if (contentValues == null) {
-                    continue;
-                }
-                byte[] data = contentValues.getAsByteArray(Photo.PHOTO);
-                if (data == null) {
-                    continue;
-                }
-                final String photoType = VCardUtils.guessImageType(data);
-                if (photoType == null) {
-                    Log.d(LOG_TAG, "Unknown photo type. Ignored.");
-                    continue;
-                }
-                // TODO: check this works fine.
-                final String photoString = new String(Base64.encode(data, Base64.NO_WRAP));
-                if (!TextUtils.isEmpty(photoString)) {
-                    appendPhotoLine(photoString, photoType);
-                }
-            }
-        }
-        return this;
-    }
-
-    public VCardBuilder appendNotes(final List<ContentValues> contentValuesList) {
-        if (contentValuesList != null) {
-            if (mOnlyOneNoteFieldIsAvailable) {
-                final StringBuilder noteBuilder = new StringBuilder();
-                boolean first = true;
-                for (final ContentValues contentValues : contentValuesList) {
-                    String note = contentValues.getAsString(Note.NOTE);
-                    if (note == null) {
-                        note = "";
-                    }
-                    if (note.length() > 0) {
-                        if (first) {
-                            first = false;
-                        } else {
-                            noteBuilder.append('\n');
-                        }
-                        noteBuilder.append(note);
-                    }
-                }
-                final String noteStr = noteBuilder.toString();
-                // This means we scan noteStr completely twice, which is redundant.
-                // But for now, we assume this is not so time-consuming..
-                final boolean shouldAppendCharsetInfo =
-                    !VCardUtils.containsOnlyPrintableAscii(noteStr);
-                final boolean reallyUseQuotedPrintable =
-                        (mShouldUseQuotedPrintable &&
-                            !VCardUtils.containsOnlyNonCrLfPrintableAscii(noteStr));
-                appendLine(VCardConstants.PROPERTY_NOTE, noteStr,
-                        shouldAppendCharsetInfo, reallyUseQuotedPrintable);
-            } else {
-                for (ContentValues contentValues : contentValuesList) {
-                    final String noteStr = contentValues.getAsString(Note.NOTE);
-                    if (!TextUtils.isEmpty(noteStr)) {
-                        final boolean shouldAppendCharsetInfo =
-                                !VCardUtils.containsOnlyPrintableAscii(noteStr);
-                        final boolean reallyUseQuotedPrintable =
-                                (mShouldUseQuotedPrintable &&
-                                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(noteStr));
-                        appendLine(VCardConstants.PROPERTY_NOTE, noteStr,
-                                shouldAppendCharsetInfo, reallyUseQuotedPrintable);
-                    }
-                }
-            }
-        }
-        return this;
-    }
-
-    public VCardBuilder appendEvents(final List<ContentValues> contentValuesList) {
-        // There's possibility where a given object may have more than one birthday, which
-        // is inappropriate. We just build one birthday.
-        if (contentValuesList != null) {
-            String primaryBirthday = null;
-            String secondaryBirthday = null;
-            for (final ContentValues contentValues : contentValuesList) {
-                if (contentValues == null) {
-                    continue;
-                }
-                final Integer eventTypeAsInteger = contentValues.getAsInteger(Event.TYPE);
-                final int eventType;
-                if (eventTypeAsInteger != null) {
-                    eventType = eventTypeAsInteger;
-                } else {
-                    eventType = Event.TYPE_OTHER;
-                }
-                if (eventType == Event.TYPE_BIRTHDAY) {
-                    final String birthdayCandidate = contentValues.getAsString(Event.START_DATE);
-                    if (birthdayCandidate == null) {
-                        continue;
-                    }
-                    final Integer isSuperPrimaryAsInteger =
-                        contentValues.getAsInteger(Event.IS_SUPER_PRIMARY);
-                    final boolean isSuperPrimary = (isSuperPrimaryAsInteger != null ?
-                            (isSuperPrimaryAsInteger > 0) : false);
-                    if (isSuperPrimary) {
-                        // "super primary" birthday should the prefered one.
-                        primaryBirthday = birthdayCandidate;
-                        break;
-                    }
-                    final Integer isPrimaryAsInteger =
-                        contentValues.getAsInteger(Event.IS_PRIMARY);
-                    final boolean isPrimary = (isPrimaryAsInteger != null ?
-                            (isPrimaryAsInteger > 0) : false);
-                    if (isPrimary) {
-                        // We don't break here since "super primary" birthday may exist later.
-                        primaryBirthday = birthdayCandidate;
-                    } else if (secondaryBirthday == null) {
-                        // First entry is set to the "secondary" candidate.
-                        secondaryBirthday = birthdayCandidate;
-                    }
-                } else if (mUsesAndroidProperty) {
-                    // Event types other than Birthday is not supported by vCard.
-                    appendAndroidSpecificProperty(Event.CONTENT_ITEM_TYPE, contentValues);
-                }
-            }
-            if (primaryBirthday != null) {
-                appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_BDAY,
-                        primaryBirthday.trim());
-            } else if (secondaryBirthday != null){
-                appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_BDAY,
-                        secondaryBirthday.trim());
-            }
-        }
-        return this;
-    }
-
-    public VCardBuilder appendRelation(final List<ContentValues> contentValuesList) {
-        if (mUsesAndroidProperty && contentValuesList != null) {
-            for (final ContentValues contentValues : contentValuesList) {
-                if (contentValues == null) {
-                    continue;
-                }
-                appendAndroidSpecificProperty(Relation.CONTENT_ITEM_TYPE, contentValues);
-            }
-        }
-        return this;
-    }
-
-    /**
-     * @param emitEveryTime If true, builder builds the line even when there's no entry.
-     */
-    public void appendPostalLine(final int type, final String label,
-            final ContentValues contentValues,
-            final boolean isPrimary, final boolean emitEveryTime) {
-        final boolean reallyUseQuotedPrintable;
-        final boolean appendCharset;
-        final String addressValue;
-        {
-            PostalStruct postalStruct = tryConstructPostalStruct(contentValues);
-            if (postalStruct == null) {
-                if (emitEveryTime) {
-                    reallyUseQuotedPrintable = false;
-                    appendCharset = false;
-                    addressValue = "";
-                } else {
-                    return;
-                }
-            } else {
-                reallyUseQuotedPrintable = postalStruct.reallyUseQuotedPrintable;
-                appendCharset = postalStruct.appendCharset;
-                addressValue = postalStruct.addressData;
-            }
-        }
-
-        List<String> parameterList = new ArrayList<String>();
-        if (isPrimary) {
-            parameterList.add(VCardConstants.PARAM_TYPE_PREF);
-        }
-        switch (type) {
-            case StructuredPostal.TYPE_HOME: {
-                parameterList.add(VCardConstants.PARAM_TYPE_HOME);
-                break;
-            }
-            case StructuredPostal.TYPE_WORK: {
-                parameterList.add(VCardConstants.PARAM_TYPE_WORK);
-                break;
-            }
-            case StructuredPostal.TYPE_CUSTOM: {
-                if (!TextUtils.isEmpty(label)
-                        && VCardUtils.containsOnlyAlphaDigitHyphen(label)) {
-                    // We're not sure whether the label is valid in the spec
-                    // ("IANA-token" in the vCard 3.0 is unclear...)
-                    // Just  for safety, we add "X-" at the beggining of each label.
-                    // Also checks the label obeys with vCard 3.0 spec.
-                    parameterList.add("X-" + label);
-                }
-                break;
-            }
-            case StructuredPostal.TYPE_OTHER: {
-                break;
-            }
-            default: {
-                Log.e(LOG_TAG, "Unknown StructuredPostal type: " + type);
-                break;
-            }
-        }
-
-        mBuilder.append(VCardConstants.PROPERTY_ADR);
-        if (!parameterList.isEmpty()) {
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            appendTypeParameters(parameterList);
-        }
-        if (appendCharset) {
-            // Strictly, vCard 3.0 does not allow exporters to emit charset information,
-            // but we will add it since the information should be useful for importers,
-            //
-            // Assume no parser does not emit error with this parameter in vCard 3.0.
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            mBuilder.append(mVCardCharsetParameter);
-        }
-        if (reallyUseQuotedPrintable) {
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            mBuilder.append(VCARD_PARAM_ENCODING_QP);
-        }
-        mBuilder.append(VCARD_DATA_SEPARATOR);
-        mBuilder.append(addressValue);
-        mBuilder.append(VCARD_END_OF_LINE);
-    }
-
-    public void appendEmailLine(final int type, final String label,
-            final String rawValue, final boolean isPrimary) {
-        final String typeAsString;
-        switch (type) {
-            case Email.TYPE_CUSTOM: {
-                if (VCardUtils.isMobilePhoneLabel(label)) {
-                    typeAsString = VCardConstants.PARAM_TYPE_CELL;
-                } else if (!TextUtils.isEmpty(label)
-                        && VCardUtils.containsOnlyAlphaDigitHyphen(label)) {
-                    typeAsString = "X-" + label;
-                } else {
-                    typeAsString = null;
-                }
-                break;
-            }
-            case Email.TYPE_HOME: {
-                typeAsString = VCardConstants.PARAM_TYPE_HOME;
-                break;
-            }
-            case Email.TYPE_WORK: {
-                typeAsString = VCardConstants.PARAM_TYPE_WORK;
-                break;
-            }
-            case Email.TYPE_OTHER: {
-                typeAsString = null;
-                break;
-            }
-            case Email.TYPE_MOBILE: {
-                typeAsString = VCardConstants.PARAM_TYPE_CELL;
-                break;
-            }
-            default: {
-                Log.e(LOG_TAG, "Unknown Email type: " + type);
-                typeAsString = null;
-                break;
-            }
-        }
-
-        final List<String> parameterList = new ArrayList<String>();
-        if (isPrimary) {
-            parameterList.add(VCardConstants.PARAM_TYPE_PREF);
-        }
-        if (!TextUtils.isEmpty(typeAsString)) {
-            parameterList.add(typeAsString);
-        }
-
-        appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_EMAIL, parameterList,
-                rawValue);
-    }
-
-    public void appendTelLine(final Integer typeAsInteger, final String label,
-            final String encodedValue, boolean isPrimary) {
-        mBuilder.append(VCardConstants.PROPERTY_TEL);
-        mBuilder.append(VCARD_PARAM_SEPARATOR);
-
-        final int type;
-        if (typeAsInteger == null) {
-            type = Phone.TYPE_OTHER;
-        } else {
-            type = typeAsInteger;
-        }
-
-        ArrayList<String> parameterList = new ArrayList<String>();
-        switch (type) {
-            case Phone.TYPE_HOME: {
-                parameterList.addAll(
-                        Arrays.asList(VCardConstants.PARAM_TYPE_HOME));
-                break;
-            }
-            case Phone.TYPE_WORK: {
-                parameterList.addAll(
-                        Arrays.asList(VCardConstants.PARAM_TYPE_WORK));
-                break;
-            }
-            case Phone.TYPE_FAX_HOME: {
-                parameterList.addAll(
-                        Arrays.asList(VCardConstants.PARAM_TYPE_HOME, VCardConstants.PARAM_TYPE_FAX));
-                break;
-            }
-            case Phone.TYPE_FAX_WORK: {
-                parameterList.addAll(
-                        Arrays.asList(VCardConstants.PARAM_TYPE_WORK, VCardConstants.PARAM_TYPE_FAX));
-                break;
-            }
-            case Phone.TYPE_MOBILE: {
-                parameterList.add(VCardConstants.PARAM_TYPE_CELL);
-                break;
-            }
-            case Phone.TYPE_PAGER: {
-                if (mIsDoCoMo) {
-                    // Not sure about the reason, but previous implementation had
-                    // used "VOICE" instead of "PAGER"
-                    parameterList.add(VCardConstants.PARAM_TYPE_VOICE);
-                } else {
-                    parameterList.add(VCardConstants.PARAM_TYPE_PAGER);
-                }
-                break;
-            }
-            case Phone.TYPE_OTHER: {
-                parameterList.add(VCardConstants.PARAM_TYPE_VOICE);
-                break;
-            }
-            case Phone.TYPE_CAR: {
-                parameterList.add(VCardConstants.PARAM_TYPE_CAR);
-                break;
-            }
-            case Phone.TYPE_COMPANY_MAIN: {
-                // There's no relevant field in vCard (at least 2.1).
-                parameterList.add(VCardConstants.PARAM_TYPE_WORK);
-                isPrimary = true;
-                break;
-            }
-            case Phone.TYPE_ISDN: {
-                parameterList.add(VCardConstants.PARAM_TYPE_ISDN);
-                break;
-            }
-            case Phone.TYPE_MAIN: {
-                isPrimary = true;
-                break;
-            }
-            case Phone.TYPE_OTHER_FAX: {
-                parameterList.add(VCardConstants.PARAM_TYPE_FAX);
-                break;
-            }
-            case Phone.TYPE_TELEX: {
-                parameterList.add(VCardConstants.PARAM_TYPE_TLX);
-                break;
-            }
-            case Phone.TYPE_WORK_MOBILE: {
-                parameterList.addAll(
-                        Arrays.asList(VCardConstants.PARAM_TYPE_WORK, VCardConstants.PARAM_TYPE_CELL));
-                break;
-            }
-            case Phone.TYPE_WORK_PAGER: {
-                parameterList.add(VCardConstants.PARAM_TYPE_WORK);
-                // See above.
-                if (mIsDoCoMo) {
-                    parameterList.add(VCardConstants.PARAM_TYPE_VOICE);
-                } else {
-                    parameterList.add(VCardConstants.PARAM_TYPE_PAGER);
-                }
-                break;
-            }
-            case Phone.TYPE_MMS: {
-                parameterList.add(VCardConstants.PARAM_TYPE_MSG);
-                break;
-            }
-            case Phone.TYPE_CUSTOM: {
-                if (TextUtils.isEmpty(label)) {
-                    // Just ignore the custom type.
-                    parameterList.add(VCardConstants.PARAM_TYPE_VOICE);
-                } else if (VCardUtils.isMobilePhoneLabel(label)) {
-                    parameterList.add(VCardConstants.PARAM_TYPE_CELL);
-                } else if (mIsV30OrV40) {
-                    // This label is appropriately encoded in appendTypeParameters.
-                    parameterList.add(label);
-                } else {
-                    final String upperLabel = label.toUpperCase();
-                    if (VCardUtils.isValidInV21ButUnknownToContactsPhoteType(upperLabel)) {
-                        parameterList.add(upperLabel);
-                    } else if (VCardUtils.containsOnlyAlphaDigitHyphen(label)) {
-                        // Note: Strictly, vCard 2.1 does not allow "X-" parameter without
-                        //       "TYPE=" string.
-                        parameterList.add("X-" + label);
-                    }
-                }
-                break;
-            }
-            case Phone.TYPE_RADIO:
-            case Phone.TYPE_TTY_TDD:
-            default: {
-                break;
-            }
-        }
-
-        if (isPrimary) {
-            parameterList.add(VCardConstants.PARAM_TYPE_PREF);
-        }
-
-        if (parameterList.isEmpty()) {
-            appendUncommonPhoneType(mBuilder, type);
-        } else {
-            appendTypeParameters(parameterList);
-        }
-
-        mBuilder.append(VCARD_DATA_SEPARATOR);
-        mBuilder.append(encodedValue);
-        mBuilder.append(VCARD_END_OF_LINE);
-    }
-
-    /**
-     * Appends phone type string which may not be available in some devices.
-     */
-    private void appendUncommonPhoneType(final StringBuilder builder, final Integer type) {
-        if (mIsDoCoMo) {
-            // The previous implementation for DoCoMo had been conservative
-            // about miscellaneous types.
-            builder.append(VCardConstants.PARAM_TYPE_VOICE);
-        } else {
-            String phoneType = VCardUtils.getPhoneTypeString(type);
-            if (phoneType != null) {
-                appendTypeParameter(phoneType);
-            } else {
-                Log.e(LOG_TAG, "Unknown or unsupported (by vCard) Phone type: " + type);
-            }
-        }
-    }
-
-    /**
-     * @param encodedValue Must be encoded by BASE64 
-     * @param photoType
-     */
-    public void appendPhotoLine(final String encodedValue, final String photoType) {
-        StringBuilder tmpBuilder = new StringBuilder();
-        tmpBuilder.append(VCardConstants.PROPERTY_PHOTO);
-        tmpBuilder.append(VCARD_PARAM_SEPARATOR);
-        if (mIsV30OrV40) {
-            tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_AS_B);
-        } else {
-            tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_V21);
-        }
-        tmpBuilder.append(VCARD_PARAM_SEPARATOR);
-        appendTypeParameter(tmpBuilder, photoType);
-        tmpBuilder.append(VCARD_DATA_SEPARATOR);
-        tmpBuilder.append(encodedValue);
-
-        final String tmpStr = tmpBuilder.toString();
-        tmpBuilder = new StringBuilder();
-        int lineCount = 0;
-        final int length = tmpStr.length();
-        final int maxNumForFirstLine = VCardConstants.MAX_CHARACTER_NUMS_BASE64_V30
-                - VCARD_END_OF_LINE.length();
-        final int maxNumInGeneral = maxNumForFirstLine - VCARD_WS.length();
-        int maxNum = maxNumForFirstLine;
-        for (int i = 0; i < length; i++) {
-            tmpBuilder.append(tmpStr.charAt(i));
-            lineCount++;
-            if (lineCount > maxNum) {
-                tmpBuilder.append(VCARD_END_OF_LINE);
-                tmpBuilder.append(VCARD_WS);
-                maxNum = maxNumInGeneral;
-                lineCount = 0;
-            }
-        }
-        mBuilder.append(tmpBuilder.toString());
-        mBuilder.append(VCARD_END_OF_LINE);
-        mBuilder.append(VCARD_END_OF_LINE);
-    }
-
-    public void appendAndroidSpecificProperty(
-            final String mimeType, ContentValues contentValues) {
-        if (!sAllowedAndroidPropertySet.contains(mimeType)) {
-            return;
-        }
-        final List<String> rawValueList = new ArrayList<String>();
-        for (int i = 1; i <= VCardConstants.MAX_DATA_COLUMN; i++) {
-            String value = contentValues.getAsString("data" + i);
-            if (value == null) {
-                value = "";
-            }
-            rawValueList.add(value);
-        }
-
-        boolean needCharset =
-            (mShouldAppendCharsetParam &&
-                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList));
-        boolean reallyUseQuotedPrintable =
-            (mShouldUseQuotedPrintable &&
-                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList));
-        mBuilder.append(VCardConstants.PROPERTY_X_ANDROID_CUSTOM);
-        if (needCharset) {
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            mBuilder.append(mVCardCharsetParameter);
-        }
-        if (reallyUseQuotedPrintable) {
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            mBuilder.append(VCARD_PARAM_ENCODING_QP);
-        }
-        mBuilder.append(VCARD_DATA_SEPARATOR);
-        mBuilder.append(mimeType);  // Should not be encoded.
-        for (String rawValue : rawValueList) {
-            final String encodedValue;
-            if (reallyUseQuotedPrintable) {
-                encodedValue = encodeQuotedPrintable(rawValue);
-            } else {
-                // TODO: one line may be too huge, which may be invalid in vCard 3.0
-                //        (which says "When generating a content line, lines longer than
-                //        75 characters SHOULD be folded"), though several
-                //        (even well-known) applications do not care this.
-                encodedValue = escapeCharacters(rawValue);
-            }
-            mBuilder.append(VCARD_ITEM_SEPARATOR);
-            mBuilder.append(encodedValue);
-        }
-        mBuilder.append(VCARD_END_OF_LINE);
-    }
-
-    public void appendLineWithCharsetAndQPDetection(final String propertyName,
-            final String rawValue) {
-        appendLineWithCharsetAndQPDetection(propertyName, null, rawValue);
-    }
-
-    public void appendLineWithCharsetAndQPDetection(
-            final String propertyName, final List<String> rawValueList) {
-        appendLineWithCharsetAndQPDetection(propertyName, null, rawValueList);
-    }
-
-    public void appendLineWithCharsetAndQPDetection(final String propertyName,
-            final List<String> parameterList, final String rawValue) {
-        final boolean needCharset =
-                !VCardUtils.containsOnlyPrintableAscii(rawValue);
-        final boolean reallyUseQuotedPrintable =
-                (mShouldUseQuotedPrintable &&
-                        !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValue));
-        appendLine(propertyName, parameterList,
-                rawValue, needCharset, reallyUseQuotedPrintable);
-    }
-
-    public void appendLineWithCharsetAndQPDetection(final String propertyName,
-            final List<String> parameterList, final List<String> rawValueList) {
-        boolean needCharset =
-            (mShouldAppendCharsetParam &&
-                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList));
-        boolean reallyUseQuotedPrintable =
-            (mShouldUseQuotedPrintable &&
-                    !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList));
-        appendLine(propertyName, parameterList, rawValueList,
-                needCharset, reallyUseQuotedPrintable);
-    }
-
-    /**
-     * Appends one line with a given property name and value.  
-     */
-    public void appendLine(final String propertyName, final String rawValue) {
-        appendLine(propertyName, rawValue, false, false);
-    }
-
-    public void appendLine(final String propertyName, final List<String> rawValueList) {
-        appendLine(propertyName, rawValueList, false, false);
-    }
-
-    public void appendLine(final String propertyName,
-            final String rawValue, final boolean needCharset,
-            boolean reallyUseQuotedPrintable) {
-        appendLine(propertyName, null, rawValue, needCharset, reallyUseQuotedPrintable);
-    }
-
-    public void appendLine(final String propertyName, final List<String> parameterList,
-            final String rawValue) {
-        appendLine(propertyName, parameterList, rawValue, false, false);
-    }
-
-    public void appendLine(final String propertyName, final List<String> parameterList,
-            final String rawValue, final boolean needCharset,
-            boolean reallyUseQuotedPrintable) {
-        mBuilder.append(propertyName);
-        if (parameterList != null && parameterList.size() > 0) {
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            appendTypeParameters(parameterList);
-        }
-        if (needCharset) {
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            mBuilder.append(mVCardCharsetParameter);
-        }
-
-        final String encodedValue;
-        if (reallyUseQuotedPrintable) {
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            mBuilder.append(VCARD_PARAM_ENCODING_QP);
-            encodedValue = encodeQuotedPrintable(rawValue);
-        } else {
-            // TODO: one line may be too huge, which may be invalid in vCard spec, though
-            //       several (even well-known) applications do not care that violation.
-            encodedValue = escapeCharacters(rawValue);
-        }
-
-        mBuilder.append(VCARD_DATA_SEPARATOR);
-        mBuilder.append(encodedValue);
-        mBuilder.append(VCARD_END_OF_LINE);
-    }
-
-    public void appendLine(final String propertyName, final List<String> rawValueList,
-            final boolean needCharset, boolean needQuotedPrintable) {
-        appendLine(propertyName, null, rawValueList, needCharset, needQuotedPrintable);
-    }
-
-    public void appendLine(final String propertyName, final List<String> parameterList,
-            final List<String> rawValueList, final boolean needCharset,
-            final boolean needQuotedPrintable) {
-        mBuilder.append(propertyName);
-        if (parameterList != null && parameterList.size() > 0) {
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            appendTypeParameters(parameterList);
-        }
-        if (needCharset) {
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            mBuilder.append(mVCardCharsetParameter);
-        }
-        if (needQuotedPrintable) {
-            mBuilder.append(VCARD_PARAM_SEPARATOR);
-            mBuilder.append(VCARD_PARAM_ENCODING_QP);
-        }
-
-        mBuilder.append(VCARD_DATA_SEPARATOR);
-        boolean first = true;
-        for (String rawValue : rawValueList) {
-            final String encodedValue;
-            if (needQuotedPrintable) {
-                encodedValue = encodeQuotedPrintable(rawValue);
-            } else {
-                // TODO: one line may be too huge, which may be invalid in vCard 3.0
-                //        (which says "When generating a content line, lines longer than
-                //        75 characters SHOULD be folded"), though several
-                //        (even well-known) applications do not care this.
-                encodedValue = escapeCharacters(rawValue);
-            }
-
-            if (first) {
-                first = false;
-            } else {
-                mBuilder.append(VCARD_ITEM_SEPARATOR);
-            }
-            mBuilder.append(encodedValue);
-        }
-        mBuilder.append(VCARD_END_OF_LINE);
-    }
-
-    /**
-     * VCARD_PARAM_SEPARATOR must be appended before this method being called.
-     */
-    private void appendTypeParameters(final List<String> types) {
-        // We may have to make this comma separated form like "TYPE=DOM,WORK" in the future,
-        // which would be recommended way in vcard 3.0 though not valid in vCard 2.1.
-        boolean first = true;
-        for (final String typeValue : types) {
-            if (VCardConfig.isVersion30(mVCardType)) {
-                final String encoded = VCardUtils.toStringAsV30ParamValue(typeValue);
-                if (TextUtils.isEmpty(encoded)) {
-                    continue;
-                }
-
-                // Note: vCard 3.0 specifies the different type of acceptable type Strings, but
-                //       we don't emit that kind of vCard 3.0 specific type since there should be
-                //       high probabilyty in which external importers cannot understand them.
-                //
-                // e.g. TYPE="\u578B\u306B\u3087" (vCard 3.0 allows non-Ascii characters if they
-                //      are quoted.)
-                if (first) {
-                    first = false;
-                } else {
-                    mBuilder.append(VCARD_PARAM_SEPARATOR);
-                }
-                appendTypeParameter(encoded);
-            } else {  // vCard 2.1
-                if (!VCardUtils.isV21Word(typeValue)) {
-                    continue;
-                }
-                if (first) {
-                    first = false;
-                } else {
-                    mBuilder.append(VCARD_PARAM_SEPARATOR);
-                }
-                appendTypeParameter(typeValue);
-            }
-        }
-    }
-
-    /**
-     * VCARD_PARAM_SEPARATOR must be appended before this method being called.
-     */
-    private void appendTypeParameter(final String type) {
-        appendTypeParameter(mBuilder, type);
-    }
-
-    private void appendTypeParameter(final StringBuilder builder, final String type) {
-        // Refrain from using appendType() so that "TYPE=" is not be appended when the
-        // device is DoCoMo's (just for safety).
-        //
-        // Note: In vCard 3.0, Type strings also can be like this: "TYPE=HOME,PREF"
-        if (VCardConfig.isVersion40(mVCardType) ||
-                ((VCardConfig.isVersion30(mVCardType) || mAppendTypeParamName) && !mIsDoCoMo)) {
-            builder.append(VCardConstants.PARAM_TYPE).append(VCARD_PARAM_EQUAL);
-        }
-        builder.append(type);
-    }
-
-    /**
-     * Returns true when the property line should contain charset parameter
-     * information. This method may return true even when vCard version is 3.0.
-     *
-     * Strictly, adding charset information is invalid in VCard 3.0.
-     * However we'll add the info only when charset we use is not UTF-8
-     * in vCard 3.0 format, since parser side may be able to use the charset
-     * via this field, though we may encounter another problem by adding it.
-     *
-     * e.g. Japanese mobile phones use Shift_Jis while RFC 2426
-     * recommends UTF-8. By adding this field, parsers may be able
-     * to know this text is NOT UTF-8 but Shift_Jis.
-     */
-    private boolean shouldAppendCharsetParam(String...propertyValueList) {
-        if (!mShouldAppendCharsetParam) {
-            return false;
-        }
-        for (String propertyValue : propertyValueList) {
-            if (!VCardUtils.containsOnlyPrintableAscii(propertyValue)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private String encodeQuotedPrintable(final String str) {
-        if (TextUtils.isEmpty(str)) {
-            return "";
-        }
-
-        final StringBuilder builder = new StringBuilder();
-        int index = 0;
-        int lineCount = 0;
-        byte[] strArray = null;
-
-        try {
-            strArray = str.getBytes(mCharset);
-        } catch (UnsupportedEncodingException e) {
-            Log.e(LOG_TAG, "Charset " + mCharset + " cannot be used. "
-                    + "Try default charset");
-            strArray = str.getBytes();
-        }
-        while (index < strArray.length) {
-            builder.append(String.format("=%02X", strArray[index]));
-            index += 1;
-            lineCount += 3;
-
-            if (lineCount >= 67) {
-                // Specification requires CRLF must be inserted before the
-                // length of the line
-                // becomes more than 76.
-                // Assuming that the next character is a multi-byte character,
-                // it will become
-                // 6 bytes.
-                // 76 - 6 - 3 = 67
-                builder.append("=\r\n");
-                lineCount = 0;
-            }
-        }
-
-        return builder.toString();
-    }
-
-    /**
-     * Append '\' to the characters which should be escaped. The character set is different
-     * not only between vCard 2.1 and vCard 3.0 but also among each device.
-     *
-     * Note that Quoted-Printable string must not be input here.
-     */
-    @SuppressWarnings("fallthrough")
-    private String escapeCharacters(final String unescaped) {
-        if (TextUtils.isEmpty(unescaped)) {
-            return "";
-        }
-
-        final StringBuilder tmpBuilder = new StringBuilder();
-        final int length = unescaped.length();
-        for (int i = 0; i < length; i++) {
-            final char ch = unescaped.charAt(i);
-            switch (ch) {
-                case ';': {
-                    tmpBuilder.append('\\');
-                    tmpBuilder.append(';');
-                    break;
-                }
-                case '\r': {
-                    if (i + 1 < length) {
-                        char nextChar = unescaped.charAt(i);
-                        if (nextChar == '\n') {
-                            break;
-                        } else {
-                            // fall through
-                        }
-                    } else {
-                        // fall through
-                    }
-                }
-                case '\n': {
-                    // In vCard 2.1, there's no specification about this, while
-                    // vCard 3.0 explicitly requires this should be encoded to "\n".
-                    tmpBuilder.append("\\n");
-                    break;
-                }
-                case '\\': {
-                    if (mIsV30OrV40) {
-                        tmpBuilder.append("\\\\");
-                        break;
-                    } else {
-                        // fall through
-                    }
-                }
-                case '<':
-                case '>': {
-                    if (mIsDoCoMo) {
-                        tmpBuilder.append('\\');
-                        tmpBuilder.append(ch);
-                    } else {
-                        tmpBuilder.append(ch);
-                    }
-                    break;
-                }
-                case ',': {
-                    if (mIsV30OrV40) {
-                        tmpBuilder.append("\\,");
-                    } else {
-                        tmpBuilder.append(ch);
-                    }
-                    break;
-                }
-                default: {
-                    tmpBuilder.append(ch);
-                    break;
-                }
-            }
-        }
-        return tmpBuilder.toString();
-    }
-
-    @Override
-    public String toString() {
-        if (!mEndAppended) {
-            if (mIsDoCoMo) {
-                appendLine(VCardConstants.PROPERTY_X_CLASS, VCARD_DATA_PUBLIC);
-                appendLine(VCardConstants.PROPERTY_X_REDUCTION, "");
-                appendLine(VCardConstants.PROPERTY_X_NO, "");
-                appendLine(VCardConstants.PROPERTY_X_DCM_HMN_MODE, "");
-            }
-            appendLine(VCardConstants.PROPERTY_END, VCARD_DATA_VCARD);
-            mEndAppended = true;
-        }
-        return mBuilder.toString();
-    }
-}
diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java
deleted file mode 100644
index 193cf1e..0000000
--- a/core/java/android/pim/vcard/VCardComposer.java
+++ /dev/null
@@ -1,678 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.pim.vcard;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Entity;
-import android.content.Entity.NamedContentValues;
-import android.content.EntityIterator;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteException;
-import android.net.Uri;
-import android.pim.vcard.exception.VCardException;
-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.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.RawContactsEntity;
-import android.text.TextUtils;
-import android.util.CharsetUtils;
-import android.util.Log;
-
-import java.io.BufferedWriter;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.nio.charset.UnsupportedCharsetException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * <p>
- * The class for composing vCard from Contacts information.
- * </p>
- * <p>
- * Usually, this class should be used like this.
- * </p>
- * <pre class="prettyprint">VCardComposer composer = null;
- * try {
- *     composer = new VCardComposer(context);
- *     composer.addHandler(
- *             composer.new HandlerForOutputStream(outputStream));
- *     if (!composer.init()) {
- *         // Do something handling the situation.
- *         return;
- *     }
- *     while (!composer.isAfterLast()) {
- *         if (mCanceled) {
- *             // Assume a user may cancel this operation during the export.
- *             return;
- *         }
- *         if (!composer.createOneEntry()) {
- *             // Do something handling the error situation.
- *             return;
- *         }
- *     }
- * } finally {
- *     if (composer != null) {
- *         composer.terminate();
- *     }
- * }</pre>
- * <p>
- * Users have to manually take care of memory efficiency. Even one vCard may contain
- * image of non-trivial size for mobile devices.
- * </p>
- * <p>
- * {@link VCardBuilder} is used to build each vCard.
- * </p>
- */
-public class VCardComposer {
-    private static final String LOG_TAG = "VCardComposer";
-
-    public static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO =
-        "Failed to get database information";
-
-    public static final String FAILURE_REASON_NO_ENTRY =
-        "There's no exportable in the database";
-
-    public static final String FAILURE_REASON_NOT_INITIALIZED =
-        "The vCard composer object is not correctly initialized";
-
-    /** Should be visible only from developers... (no need to translate, hopefully) */
-    public static final String FAILURE_REASON_UNSUPPORTED_URI =
-        "The Uri vCard composer received is not supported by the composer.";
-
-    public static final String NO_ERROR = "No error";
-
-    public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";
-
-    // Strictly speaking, "Shift_JIS" is the most appropriate, but we use upper version here,
-    // since usual vCard devices for Japanese devices already use it.
-    private static final String SHIFT_JIS = "SHIFT_JIS";
-    private static final String UTF_8 = "UTF-8";
-
-    /**
-     * Special URI for testing.
-     */
-    public static final String VCARD_TEST_AUTHORITY = "com.android.unit_tests.vcard";
-    public static final Uri VCARD_TEST_AUTHORITY_URI =
-        Uri.parse("content://" + VCARD_TEST_AUTHORITY);
-    public static final Uri CONTACTS_TEST_CONTENT_URI =
-        Uri.withAppendedPath(VCARD_TEST_AUTHORITY_URI, "contacts");
-
-    private static final Map<Integer, String> sImMap;
-
-    static {
-        sImMap = new HashMap<Integer, String>();
-        sImMap.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM);
-        sImMap.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN);
-        sImMap.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO);
-        sImMap.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ);
-        sImMap.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER);
-        sImMap.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME);
-        // We don't add Google talk here since it has to be handled separately.
-    }
-
-    public static interface OneEntryHandler {
-        public boolean onInit(Context context);
-        public boolean onEntryCreated(String vcard);
-        public void onTerminate();
-    }
-
-    /**
-     * <p>
-     * An useful handler for emitting vCard String to an OutputStream object one by one.
-     * </p>
-     * <p>
-     * The input OutputStream object is closed() on {@link #onTerminate()}.
-     * Must not close the stream outside this class.
-     * </p>
-     */
-    public final class HandlerForOutputStream implements OneEntryHandler {
-        @SuppressWarnings("hiding")
-        private static final String LOG_TAG = "VCardComposer.HandlerForOutputStream";
-
-        private boolean mOnTerminateIsCalled = false;
-
-        private final OutputStream mOutputStream; // mWriter will close this.
-        private Writer mWriter;
-
-        /**
-         * Input stream will be closed on the detruction of this object.
-         */
-        public HandlerForOutputStream(final OutputStream outputStream) {
-            mOutputStream = outputStream;
-        }
-
-        public boolean onInit(final Context context) {
-            try {
-                mWriter = new BufferedWriter(new OutputStreamWriter(
-                        mOutputStream, mCharset));
-            } catch (UnsupportedEncodingException e1) {
-                Log.e(LOG_TAG, "Unsupported charset: " + mCharset);
-                mErrorReason = "Encoding is not supported (usually this does not happen!): "
-                        + mCharset;
-                return false;
-            }
-
-            if (mIsDoCoMo) {
-                try {
-                    // Create one empty entry.
-                    mWriter.write(createOneEntryInternal("-1", null));
-                } catch (VCardException e) {
-                    Log.e(LOG_TAG, "VCardException has been thrown during on Init(): " +
-                            e.getMessage());
-                    return false;
-                } catch (IOException e) {
-                    Log.e(LOG_TAG,
-                            "IOException occurred during exportOneContactData: "
-                                    + e.getMessage());
-                    mErrorReason = "IOException occurred: " + e.getMessage();
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        public boolean onEntryCreated(String vcard) {
-            try {
-                mWriter.write(vcard);
-            } catch (IOException e) {
-                Log.e(LOG_TAG,
-                        "IOException occurred during exportOneContactData: "
-                                + e.getMessage());
-                mErrorReason = "IOException occurred: " + e.getMessage();
-                return false;
-            }
-            return true;
-        }
-
-        public void onTerminate() {
-            mOnTerminateIsCalled = true;
-            if (mWriter != null) {
-                try {
-                    // Flush and sync the data so that a user is able to pull
-                    // the SDCard just after
-                    // the export.
-                    mWriter.flush();
-                    if (mOutputStream != null
-                            && mOutputStream instanceof FileOutputStream) {
-                            ((FileOutputStream) mOutputStream).getFD().sync();
-                    }
-                } catch (IOException e) {
-                    Log.d(LOG_TAG,
-                            "IOException during closing the output stream: "
-                                    + e.getMessage());
-                } finally {
-                    closeOutputStream();
-                }
-            }
-        }
-
-        public void closeOutputStream() {
-            try {
-                mWriter.close();
-            } catch (IOException e) {
-                Log.w(LOG_TAG, "IOException is thrown during close(). Ignoring.");
-            }
-        }
-
-        @Override
-        public void finalize() {
-            if (!mOnTerminateIsCalled) {
-                onTerminate();
-            }
-        }
-    }
-
-    private final Context mContext;
-    private final int mVCardType;
-    private final boolean mCareHandlerErrors;
-    private final ContentResolver mContentResolver;
-
-    private final boolean mIsDoCoMo;
-    private Cursor mCursor;
-    private int mIdColumn;
-
-    private final String mCharset;
-    private boolean mTerminateIsCalled;
-    private final List<OneEntryHandler> mHandlerList;
-
-    private String mErrorReason = NO_ERROR;
-
-    private static final String[] sContactsProjection = new String[] {
-        Contacts._ID,
-    };
-
-    public VCardComposer(Context context) {
-        this(context, VCardConfig.VCARD_TYPE_DEFAULT, null, true);
-    }
-
-    /**
-     * The variant which sets charset to null and sets careHandlerErrors to true.
-     */
-    public VCardComposer(Context context, int vcardType) {
-        this(context, vcardType, null, true);
-    }
-
-    public VCardComposer(Context context, int vcardType, String charset) {
-        this(context, vcardType, charset, true);
-    }
-
-    /**
-     * The variant which sets charset to null.
-     */
-    public VCardComposer(final Context context, final int vcardType,
-            final boolean careHandlerErrors) {
-        this(context, vcardType, null, careHandlerErrors);
-    }
-
-    /**
-     * Construct for supporting call log entry vCard composing.
-     *
-     * @param context Context to be used during the composition.
-     * @param vcardType The type of vCard, typically available via {@link VCardConfig}.
-     * @param charset The charset to be used. Use null when you don't need the charset.
-     * @param careHandlerErrors If true, This object returns false everytime
-     * a Handler object given via {{@link #addHandler(OneEntryHandler)} returns false.
-     * If false, this ignores those errors.
-     */
-    public VCardComposer(final Context context, final int vcardType, String charset,
-            final boolean careHandlerErrors) {
-        mContext = context;
-        mVCardType = vcardType;
-        mCareHandlerErrors = careHandlerErrors;
-        mContentResolver = context.getContentResolver();
-
-        mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
-        mHandlerList = new ArrayList<OneEntryHandler>();
-
-        charset = (TextUtils.isEmpty(charset) ? VCardConfig.DEFAULT_EXPORT_CHARSET : charset);
-        final boolean shouldAppendCharsetParam = !(
-                VCardConfig.isVersion30(vcardType) && UTF_8.equalsIgnoreCase(charset));
-
-        if (mIsDoCoMo || shouldAppendCharsetParam) {
-            if (SHIFT_JIS.equalsIgnoreCase(charset)) {
-                if (mIsDoCoMo) {
-                    try {
-                        charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
-                    } catch (UnsupportedCharsetException e) {
-                        Log.e(LOG_TAG,
-                                "DoCoMo-specific SHIFT_JIS was not found. "
-                                + "Use SHIFT_JIS as is.");
-                        charset = SHIFT_JIS;
-                    }
-                } else {
-                    try {
-                        charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
-                    } catch (UnsupportedCharsetException e) {
-                        Log.e(LOG_TAG,
-                                "Career-specific SHIFT_JIS was not found. "
-                                + "Use SHIFT_JIS as is.");
-                        charset = SHIFT_JIS;
-                    }
-                }
-                mCharset = charset;
-            } else {
-                Log.w(LOG_TAG,
-                        "The charset \"" + charset + "\" is used while "
-                        + SHIFT_JIS + " is needed to be used.");
-                if (TextUtils.isEmpty(charset)) {
-                    mCharset = SHIFT_JIS;
-                } else {
-                    try {
-                        charset = CharsetUtils.charsetForVendor(charset).name();
-                    } catch (UnsupportedCharsetException e) {
-                        Log.i(LOG_TAG,
-                                "Career-specific \"" + charset + "\" was not found (as usual). "
-                                + "Use it as is.");
-                    }
-                    mCharset = charset;
-                }
-            }
-        } else {
-            if (TextUtils.isEmpty(charset)) {
-                mCharset = UTF_8;
-            } else {
-                try {
-                    charset = CharsetUtils.charsetForVendor(charset).name();
-                } catch (UnsupportedCharsetException e) {
-                    Log.i(LOG_TAG,
-                            "Career-specific \"" + charset + "\" was not found (as usual). "
-                            + "Use it as is.");
-                }
-                mCharset = charset;
-            }
-        }
-
-        Log.d(LOG_TAG, "Use the charset \"" + mCharset + "\"");
-    }
-
-    /**
-     * Must be called before {@link #init()}.
-     */
-    public void addHandler(OneEntryHandler handler) {
-        if (handler != null) {
-            mHandlerList.add(handler);
-        }
-    }
-
-    /**
-     * @return Returns true when initialization is successful and all the other
-     *          methods are available. Returns false otherwise.
-     */
-    public boolean init() {
-        return init(null, null);
-    }
-
-    public boolean init(final String selection, final String[] selectionArgs) {
-        return init(Contacts.CONTENT_URI, selection, selectionArgs, null);
-    }
-
-    /**
-     * Note that this is unstable interface, may be deleted in the future.
-     */
-    public boolean init(final Uri contentUri, final String selection,
-            final String[] selectionArgs, final String sortOrder) {
-        if (contentUri == null) {
-            return false;
-        }
-
-        if (mCareHandlerErrors) {
-            final List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
-                    mHandlerList.size());
-            for (OneEntryHandler handler : mHandlerList) {
-                if (!handler.onInit(mContext)) {
-                    for (OneEntryHandler finished : finishedList) {
-                        finished.onTerminate();
-                    }
-                    return false;
-                }
-            }
-        } else {
-            // Just ignore the false returned from onInit().
-            for (OneEntryHandler handler : mHandlerList) {
-                handler.onInit(mContext);
-            }
-        }
-
-        final String[] projection;
-        if (Contacts.CONTENT_URI.equals(contentUri) ||
-                CONTACTS_TEST_CONTENT_URI.equals(contentUri)) {
-            projection = sContactsProjection;
-        } else {
-            mErrorReason = FAILURE_REASON_UNSUPPORTED_URI;
-            return false;
-        }
-        mCursor = mContentResolver.query(
-                contentUri, projection, selection, selectionArgs, sortOrder);
-
-        if (mCursor == null) {
-            mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO;
-            return false;
-        }
-
-        if (getCount() == 0 || !mCursor.moveToFirst()) {
-            try {
-                mCursor.close();
-            } catch (SQLiteException e) {
-                Log.e(LOG_TAG, "SQLiteException on Cursor#close(): " + e.getMessage());
-            } finally {
-                mCursor = null;
-                mErrorReason = FAILURE_REASON_NO_ENTRY;
-            }
-            return false;
-        }
-
-        mIdColumn = mCursor.getColumnIndex(Contacts._ID);
-
-        return true;
-    }
-
-    public boolean createOneEntry() {
-        return createOneEntry(null);
-    }
-
-    /**
-     * @param getEntityIteratorMethod For Dependency Injection.
-     * @hide just for testing.
-     */
-    public boolean createOneEntry(Method getEntityIteratorMethod) {
-        if (mCursor == null || mCursor.isAfterLast()) {
-            mErrorReason = FAILURE_REASON_NOT_INITIALIZED;
-            return false;
-        }
-        final String vcard;
-        try {
-            if (mIdColumn >= 0) {
-                vcard = createOneEntryInternal(mCursor.getString(mIdColumn),
-                        getEntityIteratorMethod);
-            } else {
-                Log.e(LOG_TAG, "Incorrect mIdColumn: " + mIdColumn);
-                return true;
-            }
-        } catch (VCardException e) {
-            Log.e(LOG_TAG, "VCardException has been thrown: " + e.getMessage());
-            return false;
-        } catch (OutOfMemoryError error) {
-            // Maybe some data (e.g. photo) is too big to have in memory. But it
-            // should be rare.
-            Log.e(LOG_TAG, "OutOfMemoryError occured. Ignore the entry.");
-            System.gc();
-            // TODO: should tell users what happened?
-            return true;
-        } finally {
-            mCursor.moveToNext();
-        }
-
-        // This function does not care the OutOfMemoryError on the handler side :-P
-        if (mCareHandlerErrors) {
-            List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
-                    mHandlerList.size());
-            for (OneEntryHandler handler : mHandlerList) {
-                if (!handler.onEntryCreated(vcard)) {
-                    return false;
-                }
-            }
-        } else {
-            for (OneEntryHandler handler : mHandlerList) {
-                handler.onEntryCreated(vcard);
-            }
-        }
-
-        return true;
-    }
-
-    private String createOneEntryInternal(final String contactId,
-            final Method getEntityIteratorMethod) throws VCardException {
-        final Map<String, List<ContentValues>> contentValuesListMap =
-                new HashMap<String, List<ContentValues>>();
-        // The resolver may return the entity iterator with no data. It is possible.
-        // e.g. If all the data in the contact of the given contact id are not exportable ones,
-        //      they are hidden from the view of this method, though contact id itself exists.
-        EntityIterator entityIterator = null;
-        try {
-            final Uri uri = RawContactsEntity.CONTENT_URI.buildUpon()
-                    // .appendQueryParameter("for_export_only", "1")
-                    .appendQueryParameter(Data.FOR_EXPORT_ONLY, "1")
-                    .build();
-            final String selection = Data.CONTACT_ID + "=?";
-            final String[] selectionArgs = new String[] {contactId};
-            if (getEntityIteratorMethod != null) {
-                // Please note that this branch is executed by unit tests only
-                try {
-                    entityIterator = (EntityIterator)getEntityIteratorMethod.invoke(null,
-                            mContentResolver, uri, selection, selectionArgs, null);
-                } catch (IllegalArgumentException e) {
-                    Log.e(LOG_TAG, "IllegalArgumentException has been thrown: " +
-                            e.getMessage());
-                } catch (IllegalAccessException e) {
-                    Log.e(LOG_TAG, "IllegalAccessException has been thrown: " +
-                            e.getMessage());
-                } catch (InvocationTargetException e) {
-                    Log.e(LOG_TAG, "InvocationTargetException has been thrown: ");
-                    StackTraceElement[] stackTraceElements = e.getCause().getStackTrace();
-                    for (StackTraceElement element : stackTraceElements) {
-                        Log.e(LOG_TAG, "    at " + element.toString());
-                    }
-                    throw new VCardException("InvocationTargetException has been thrown: " +
-                            e.getCause().getMessage());
-                }
-            } else {
-                entityIterator = RawContacts.newEntityIterator(mContentResolver.query(
-                        uri, null, selection, selectionArgs, null));
-            }
-
-            if (entityIterator == null) {
-                Log.e(LOG_TAG, "EntityIterator is null");
-                return "";
-            }
-
-            if (!entityIterator.hasNext()) {
-                Log.w(LOG_TAG, "Data does not exist. contactId: " + contactId);
-                return "";
-            }
-
-            while (entityIterator.hasNext()) {
-                Entity entity = entityIterator.next();
-                for (NamedContentValues namedContentValues : entity.getSubValues()) {
-                    ContentValues contentValues = namedContentValues.values;
-                    String key = contentValues.getAsString(Data.MIMETYPE);
-                    if (key != null) {
-                        List<ContentValues> contentValuesList =
-                                contentValuesListMap.get(key);
-                        if (contentValuesList == null) {
-                            contentValuesList = new ArrayList<ContentValues>();
-                            contentValuesListMap.put(key, contentValuesList);
-                        }
-                        contentValuesList.add(contentValues);
-                    }
-                }
-            }
-        } finally {
-            if (entityIterator != null) {
-                entityIterator.close();
-            }
-        }
-
-        return buildVCard(contentValuesListMap);
-    }
-
-    /**
-     * Builds and returns vCard using given map, whose key is CONTENT_ITEM_TYPE defined in
-     * {ContactsContract}. Developers can override this method to customize the output.
-     */
-    public String buildVCard(final Map<String, List<ContentValues>> contentValuesListMap) {
-        if (contentValuesListMap == null) {
-            Log.e(LOG_TAG, "The given map is null. Ignore and return empty String");
-            return "";
-        } else {
-            final VCardBuilder builder = new VCardBuilder(mVCardType, mCharset);
-            builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
-                    .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
-                    .appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
-                    .appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE))
-                    .appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE))
-                    .appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE))
-                    .appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE));
-            if ((mVCardType & VCardConfig.FLAG_REFRAIN_IMAGE_EXPORT) == 0) {
-                builder.appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE));            
-            }
-            builder.appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE))
-                    .appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE))
-                    .appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE))
-                    .appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE));
-            return builder.toString();
-        }
-    }
-
-    public void terminate() {
-        for (OneEntryHandler handler : mHandlerList) {
-            handler.onTerminate();
-        }
-
-        if (mCursor != null) {
-            try {
-                mCursor.close();
-            } catch (SQLiteException e) {
-                Log.e(LOG_TAG, "SQLiteException on Cursor#close(): " + e.getMessage());
-            }
-            mCursor = null;
-        }
-
-        mTerminateIsCalled = true;
-    }
-
-    @Override
-    public void finalize() {
-        if (!mTerminateIsCalled) {
-            Log.w(LOG_TAG, "terminate() is not called yet. We call it in finalize() step.");
-            terminate();
-        }
-    }
-
-    /**
-     * @return returns the number of available entities. The return value is undefined
-     * when this object is not ready yet (typically when {{@link #init()} is not called
-     * or when {@link #terminate()} is already called).
-     */
-    public int getCount() {
-        if (mCursor == null) {
-            Log.w(LOG_TAG, "This object is not ready yet.");
-            return 0;
-        }
-        return mCursor.getCount();
-    }
-
-    /**
-     * @return true when there's no entity to be built. The return value is undefined
-     * when this object is not ready yet.
-     */
-    public boolean isAfterLast() {
-        if (mCursor == null) {
-            Log.w(LOG_TAG, "This object is not ready yet.");
-            return false;
-        }
-        return mCursor.isAfterLast();
-    }
-
-    /**
-     * @return Returns the error reason.
-     */
-    public String getErrorReason() {
-        return mErrorReason;
-    }
-}
diff --git a/core/java/android/pim/vcard/VCardConfig.java b/core/java/android/pim/vcard/VCardConfig.java
deleted file mode 100644
index 8e759e3..0000000
--- a/core/java/android/pim/vcard/VCardConfig.java
+++ /dev/null
@@ -1,505 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.telephony.PhoneNumberUtils;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * The class representing VCard related configurations. Useful static methods are not in this class
- * but in VCardUtils.
- */
-public class VCardConfig {
-    private static final String LOG_TAG = "VCardConfig";
-
-    /* package */ static final int LOG_LEVEL_NONE = 0;
-    /* package */ static final int LOG_LEVEL_PERFORMANCE_MEASUREMENT = 0x1;
-    /* package */ static final int LOG_LEVEL_SHOW_WARNING = 0x2;
-    /* package */ static final int LOG_LEVEL_VERBOSE =
-        LOG_LEVEL_PERFORMANCE_MEASUREMENT | LOG_LEVEL_SHOW_WARNING;
-
-    /* package */ static final int LOG_LEVEL = LOG_LEVEL_NONE;
-
-    /**
-     * <p>
-     * The charset used during import.
-     * </p>
-     * <p>
-     * We cannot determine which charset should be used to interpret lines in vCard,
-     * while Java requires us to specify it when InputStream is used.
-     * We need to rely on the mechanism due to some performance reason.
-     * </p>
-     * <p>
-     * In order to avoid "misinterpretation" of charset and lose any data in vCard,
-     * "ISO-8859-1" is first used for reading the stream.
-     * When a charset is specified in a property (with "CHARSET=..." parameter),
-     * the string is decoded to raw bytes and encoded into the specific charset,
-     * </p>
-     * <p>
-     * Unicode specification there's a one to one mapping between each byte in ISO-8859-1
-     * and a codepoint, and Java specification requires runtime must have the charset.
-     * Thus, ISO-8859-1 is one effective mapping for intermediate mapping.
-     * </p>
-     */
-    public static final String DEFAULT_INTERMEDIATE_CHARSET = "ISO-8859-1";
-
-    /**
-     * The charset used when there's no information affbout what charset should be used to
-     * encode the binary given from vCard.
-     */
-    public static final String DEFAULT_IMPORT_CHARSET = "UTF-8";
-    public static final String DEFAULT_EXPORT_CHARSET = "UTF-8";
-
-    /**
-     * Do not use statically like "version == VERSION_V21"
-     */
-    public static final int VERSION_21 = 0;
-    public static final int VERSION_30 = 1;
-    public static final int VERSION_40 = 2;
-    public static final int VERSION_MASK = 3;
-
-    public static final int NAME_ORDER_DEFAULT = 0;
-    public static final int NAME_ORDER_EUROPE = 0x4;
-    public static final int NAME_ORDER_JAPANESE = 0x8;
-    private static final int NAME_ORDER_MASK = 0xC;
-
-    // 0x10 is reserved for safety
-
-    /**
-     * <p>
-     * The flag indicating the vCard composer will add some "X-" properties used only in Android
-     * when the formal vCard specification does not have appropriate fields for that data.
-     * </p>
-     * <p>
-     * For example, Android accepts nickname information while vCard 2.1 does not.
-     * When this flag is on, vCard composer emits alternative "X-" property (like "X-NICKNAME")
-     * instead of just dropping it.
-     * </p>
-     * <p>
-     * vCard parser code automatically parses the field emitted even when this flag is off.
-     * </p>
-     */
-    private static final int FLAG_USE_ANDROID_PROPERTY = 0x80000000;
-    
-    /**
-     * <p>
-     * The flag indicating the vCard composer will add some "X-" properties seen in the
-     * vCard data emitted by the other softwares/devices when the formal vCard specification
-     * does not have appropriate field(s) for that data.
-     * </p> 
-     * <p>
-     * One example is X-PHONETIC-FIRST-NAME/X-PHONETIC-MIDDLE-NAME/X-PHONETIC-LAST-NAME, which are
-     * for phonetic name (how the name is pronounced), seen in the vCard emitted by some other
-     * non-Android devices/softwares. We chose to enable the vCard composer to use those
-     * defact properties since they are also useful for Android devices.
-     * </p>
-     * <p>
-     * Note for developers: only "X-" properties should be added with this flag. vCard 2.1/3.0
-     * allows any kind of "X-" properties but does not allow non-"X-" properties (except IANA tokens
-     * in vCard 3.0). Some external parsers may get confused with non-valid, non-"X-" properties.
-     * </p>
-     */
-    private static final int FLAG_USE_DEFACT_PROPERTY = 0x40000000;
-
-    /**
-     * <p>
-     * The flag indicating some specific dialect seen in vCard of DoCoMo (one of Japanese
-     * mobile careers) should be used. This flag does not include any other information like
-     * that "the vCard is for Japanese". So it is "possible" that "the vCard should have DoCoMo's
-     * dialect but the name order should be European", but it is not recommended.
-     * </p>
-     */
-    private static final int FLAG_DOCOMO = 0x20000000;
-
-    /**
-     * <p>
-     * The flag indicating the vCard composer does "NOT" use Quoted-Printable toward "primary"
-     * properties even though it is required by vCard 2.1 (QP is prohibited in vCard 3.0).
-     * </p>
-     * <p>
-     * We actually cannot define what is the "primary" property. Note that this is NOT defined
-     * in vCard specification either. Also be aware that it is NOT related to "primary" notion
-     * used in {@link android.provider.ContactsContract}.
-     * This notion is just for vCard composition in Android.
-     * </p>
-     * <p>
-     * We added this Android-specific notion since some (incomplete) vCard exporters for vCard 2.1
-     * do NOT use Quoted-Printable encoding toward some properties related names like "N", "FN", etc.
-     * even when their values contain non-ascii or/and CR/LF, while they use the encoding in the
-     * other properties like "ADR", "ORG", etc.
-     * <p>
-     * We are afraid of the case where some vCard importer also forget handling QP presuming QP is
-     * not used in such fields.
-     * </p>
-     * <p>
-     * This flag is useful when some target importer you are going to focus on does not accept
-     * such properties with Quoted-Printable encoding.
-     * </p>
-     * <p>
-     * Again, we should not use this flag at all for complying vCard 2.1 spec.
-     * </p>
-     * <p>
-     * In vCard 3.0, Quoted-Printable is explicitly "prohibitted", so we don't need to care this
-     * kind of problem (hopefully).
-     * </p>
-     * @hide
-     */
-    public static final int FLAG_REFRAIN_QP_TO_NAME_PROPERTIES = 0x10000000;
-
-    /**
-     * <p>
-     * The flag indicating that phonetic name related fields must be converted to
-     * appropriate form. Note that "appropriate" is not defined in any vCard specification.
-     * This is Android-specific.
-     * </p>
-     * <p>
-     * One typical (and currently sole) example where we need this flag is the time when
-     * we need to emit Japanese phonetic names into vCard entries. The property values
-     * should be encoded into half-width katakana when the target importer is Japanese mobile
-     * phones', which are probably not able to parse full-width hiragana/katakana for
-     * historical reasons, while the vCard importers embedded to softwares for PC should be
-     * able to parse them as we expect.
-     * </p>
-     */
-    public static final int FLAG_CONVERT_PHONETIC_NAME_STRINGS = 0x08000000;
-
-    /**
-     * <p>
-     * The flag indicating the vCard composer "for 2.1" emits "TYPE=" string toward TYPE params
-     * every time possible. The default behavior does not emit it and is valid in the spec.
-     * In vCrad 3.0, this flag is unnecessary, since "TYPE=" is MUST in vCard 3.0 specification.
-     * </p>
-     * <p>
-     * Detail:
-     * How more than one TYPE fields are expressed is different between vCard 2.1 and vCard 3.0.
-     * </p>
-     * <p>
-     * e.g.
-     * </p>
-     * <ol>
-     * <li>Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."</li>
-     * <li>Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."</li>
-     * <li>Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."</li>
-     * </ol>
-     * <p>
-     * If you are targeting to the importer which cannot accept TYPE params without "TYPE="
-     * strings (which should be rare though), please use this flag.
-     * </p>
-     * <p>
-     * Example usage:
-     * <pre class="prettyprint">int type = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);</pre>
-     * </p>
-     */
-    public static final int FLAG_APPEND_TYPE_PARAM = 0x04000000;
-
-    /**
-     * <p>
-     * The flag indicating the vCard composer does touch nothing toward phone number Strings
-     * but leave it as is.
-     * </p>
-     * <p>
-     * The vCard specifications mention nothing toward phone numbers, while some devices
-     * do (wrongly, but with innevitable reasons).
-     * For example, there's a possibility Japanese mobile phones are expected to have
-     * just numbers, hypens, plus, etc. but not usual alphabets, while US mobile phones
-     * should get such characters. To make exported vCard simple for external parsers,
-     * we have used {@link PhoneNumberUtils#formatNumber(String)} during export, and
-     * removed unnecessary characters inside the number (e.g. "111-222-3333 (Miami)"
-     * becomes "111-222-3333").
-     * Unfortunate side effect of that use was some control characters used in the other
-     * areas may be badly affected by the formatting.
-     * </p>
-     * <p>
-     * This flag disables that formatting, affecting both importer and exporter.
-     * If the user is aware of some side effects due to the implicit formatting, use this flag.
-     * </p>
-     */
-    public static final int FLAG_REFRAIN_PHONE_NUMBER_FORMATTING = 0x02000000;
-
-    /**
-     * <p>
-     * For importer only. Ignored in exporter.
-     * </p>
-     * <p>
-     * The flag indicating the parser should handle a nested vCard, in which vCard clause starts
-     * in another vCard clause. Here's a typical example.
-     * </p>
-     * <pre class="prettyprint">BEGIN:VCARD
-     * BEGIN:VCARD
-     * VERSION:2.1
-     * ...
-     * END:VCARD
-     * END:VCARD</pre>
-     * <p>
-     * The vCard 2.1 specification allows the nest, but also let parsers ignore nested entries,
-     * while some mobile devices emit nested ones as primary data to be imported.
-     * </p>
-     * <p>
-     * This flag forces a vCard parser to torelate such a nest and understand its content.
-     * </p>
-     */
-    public static final int FLAG_TORELATE_NEST = 0x01000000;
-
-    //// The followings are VCard types available from importer/exporter. ////
-
-    public static final int FLAG_REFRAIN_IMAGE_EXPORT = 0x00800000;
-
-    /**
-     * <p>
-     * The type indicating nothing. Used by {@link VCardSourceDetector} when it
-     * was not able to guess the exact vCard type.
-     * </p>
-     */
-    public static final int VCARD_TYPE_UNKNOWN = 0;
-
-    /**
-     * <p>
-     * Generic vCard format with the vCard 2.1. When composing a vCard entry,
-     * the US convension will be used toward formatting some values.
-     * </p>
-     * <p>
-     * e.g. The order of the display name would be "Prefix Given Middle Family Suffix",
-     * while it should be "Prefix Family Middle Given Suffix" in Japan for example.
-     * </p>
-     * <p>
-     * Uses UTF-8 for the charset as a charset for exporting. Note that old vCard importer
-     * outside Android cannot accept it since vCard 2.1 specifically does not allow
-     * that charset, while we need to use it to support various languages around the world.
-     * </p>
-     * <p>
-     * If you want to use alternative charset, you should notify the charset to the other
-     * compontent to be used.
-     * </p>
-     */
-    public static final int VCARD_TYPE_V21_GENERIC =
-        (VERSION_21 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
-
-    /* package */ static String VCARD_TYPE_V21_GENERIC_STR = "v21_generic";
-    
-    /**
-     * <p>
-     * General vCard format with the version 3.0. Uses UTF-8 for the charset.
-     * </p>
-     * <p>
-     * Not fully ready yet. Use with caution when you use this.
-     * </p>
-     */
-    public static final int VCARD_TYPE_V30_GENERIC =
-        (VERSION_30 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
-
-    /* package */ static final String VCARD_TYPE_V30_GENERIC_STR = "v30_generic";
-
-    /**
-     * General vCard format with the version 4.0.
-     * @hide vCard 4.0 is not published yet.
-     */
-    public static final int VCARD_TYPE_V40_GENERIC =
-        (VERSION_40 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
-
-    /* package */ static final String VCARD_TYPE_V40_GENERIC_STR = "v40_generic";
-
-    /**
-     * <p>
-     * General vCard format for the vCard 2.1 with some Europe convension. Uses Utf-8.
-     * Currently, only name order is considered ("Prefix Middle Given Family Suffix")
-     * </p>
-     */
-    public static final int VCARD_TYPE_V21_EUROPE =
-        (VERSION_21 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
-
-    /* package */ static final String VCARD_TYPE_V21_EUROPE_STR = "v21_europe";
-    
-    /**
-     * <p>
-     * General vCard format with the version 3.0 with some Europe convension. Uses UTF-8.
-     * </p>
-     * <p>
-     * Not ready yet. Use with caution when you use this.
-     * </p>
-     */
-    public static final int VCARD_TYPE_V30_EUROPE =
-        (VERSION_30 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
-    
-    /* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe";
-
-    /**
-     * <p>
-     * The vCard 2.1 format for miscellaneous Japanese devices, using UTF-8 as default charset.
-     * </p>
-     * <p>
-     * Not ready yet. Use with caution when you use this.
-     * </p>
-     */
-    public static final int VCARD_TYPE_V21_JAPANESE =
-        (VERSION_21 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
-
-    /* package */ static final String VCARD_TYPE_V21_JAPANESE_STR = "v21_japanese_utf8";
-
-    /**
-     * <p>
-     * The vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset.
-     * </p>
-     * <p>
-     * Not ready yet. Use with caution when you use this.
-     * </p>
-     */
-    public static final int VCARD_TYPE_V30_JAPANESE =
-        (VERSION_30 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
-
-    /* package */ static final String VCARD_TYPE_V30_JAPANESE_STR = "v30_japanese_utf8";
-
-    /**
-     * <p>
-     * The vCard 2.1 based format which (partially) considers the convention in Japanese
-     * mobile phones, where phonetic names are translated to half-width katakana if
-     * possible, etc. It would be better to use Shift_JIS as a charset for maximum
-     * compatibility.
-     * </p>
-     * @hide Should not be available world wide.
-     */
-    public static final int VCARD_TYPE_V21_JAPANESE_MOBILE =
-        (VERSION_21 | NAME_ORDER_JAPANESE |
-                FLAG_CONVERT_PHONETIC_NAME_STRINGS | FLAG_REFRAIN_QP_TO_NAME_PROPERTIES);
-
-    /* package */ static final String VCARD_TYPE_V21_JAPANESE_MOBILE_STR = "v21_japanese_mobile";
-
-    /**
-     * <p>
-     * The vCard format used in DoCoMo, which is one of Japanese mobile phone careers.
-     * </p>
-     * <p>
-     * Base version is vCard 2.1, but the data has several DoCoMo-specific convensions.
-     * No Android-specific property nor defact property is included. The "Primary" properties
-     * are NOT encoded to Quoted-Printable.
-     * </p>
-     * @hide Should not be available world wide.
-     */
-    public static final int VCARD_TYPE_DOCOMO =
-        (VCARD_TYPE_V21_JAPANESE_MOBILE | FLAG_DOCOMO);
-
-    /* package */ static final String VCARD_TYPE_DOCOMO_STR = "docomo";
-
-    public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC;
-
-    private static final Map<String, Integer> sVCardTypeMap;
-    private static final Set<Integer> sJapaneseMobileTypeSet;
-    
-    static {
-        sVCardTypeMap = new HashMap<String, Integer>();
-        sVCardTypeMap.put(VCARD_TYPE_V21_GENERIC_STR, VCARD_TYPE_V21_GENERIC);
-        sVCardTypeMap.put(VCARD_TYPE_V30_GENERIC_STR, VCARD_TYPE_V30_GENERIC);
-        sVCardTypeMap.put(VCARD_TYPE_V21_EUROPE_STR, VCARD_TYPE_V21_EUROPE);
-        sVCardTypeMap.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE);
-        sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_STR, VCARD_TYPE_V21_JAPANESE);
-        sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_STR, VCARD_TYPE_V30_JAPANESE);
-        sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_MOBILE_STR, VCARD_TYPE_V21_JAPANESE_MOBILE);
-        sVCardTypeMap.put(VCARD_TYPE_DOCOMO_STR, VCARD_TYPE_DOCOMO);
-
-        sJapaneseMobileTypeSet = new HashSet<Integer>();
-        sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE);
-        sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE);
-        sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_MOBILE);
-        sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO);
-    }
-
-    public static int getVCardTypeFromString(final String vcardTypeString) {
-        final String loweredKey = vcardTypeString.toLowerCase();
-        if (sVCardTypeMap.containsKey(loweredKey)) {
-            return sVCardTypeMap.get(loweredKey);
-        } else if ("default".equalsIgnoreCase(vcardTypeString)) {
-            return VCARD_TYPE_DEFAULT;
-        } else {
-            Log.e(LOG_TAG, "Unknown vCard type String: \"" + vcardTypeString + "\"");
-            return VCARD_TYPE_DEFAULT;
-        }
-    }
-
-    public static boolean isVersion21(final int vcardType) {
-        return (vcardType & VERSION_MASK) == VERSION_21;
-    }
-
-    public static boolean isVersion30(final int vcardType) {
-        return (vcardType & VERSION_MASK) == VERSION_30;
-    }
-
-    public static boolean isVersion40(final int vcardType) {
-        return (vcardType & VERSION_MASK) == VERSION_40;
-    }
-
-    public static boolean shouldUseQuotedPrintable(final int vcardType) {
-        return !isVersion30(vcardType);
-    }
-
-    public static int getNameOrderType(final int vcardType) {
-        return vcardType & NAME_ORDER_MASK;
-    }
-
-    public static boolean usesAndroidSpecificProperty(final int vcardType) {
-        return ((vcardType & FLAG_USE_ANDROID_PROPERTY) != 0);
-    }
-
-    public static boolean usesDefactProperty(final int vcardType) {
-        return ((vcardType & FLAG_USE_DEFACT_PROPERTY) != 0);
-    }
-
-    public static boolean showPerformanceLog() {
-        return (VCardConfig.LOG_LEVEL & VCardConfig.LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0;
-    }
-
-    public static boolean shouldRefrainQPToNameProperties(final int vcardType) {
-       return (!shouldUseQuotedPrintable(vcardType) ||
-               ((vcardType & FLAG_REFRAIN_QP_TO_NAME_PROPERTIES) != 0));
-    }
-
-    public static boolean appendTypeParamName(final int vcardType) {
-        return (isVersion30(vcardType) || ((vcardType & FLAG_APPEND_TYPE_PARAM) != 0));
-    }
-
-    /**
-     * @return true if the device is Japanese and some Japanese convension is
-     * applied to creating "formatted" something like FORMATTED_ADDRESS.
-     */
-    public static boolean isJapaneseDevice(final int vcardType) {
-        // TODO: Some mask will be required so that this method wrongly interpret
-        //        Japanese"-like" vCard type.
-        //        e.g. VCARD_TYPE_V21_JAPANESE_SJIS | FLAG_APPEND_TYPE_PARAMS
-        return sJapaneseMobileTypeSet.contains(vcardType);
-    }
-
-    /* package */ static boolean refrainPhoneNumberFormatting(final int vcardType) {
-        return ((vcardType & FLAG_REFRAIN_PHONE_NUMBER_FORMATTING) != 0);
-    }
-
-    public static boolean needsToConvertPhoneticString(final int vcardType) {
-        return ((vcardType & FLAG_CONVERT_PHONETIC_NAME_STRINGS) != 0);
-    }
-
-    public static boolean onlyOneNoteFieldIsAvailable(final int vcardType) {
-        return vcardType == VCARD_TYPE_DOCOMO;
-    }
-
-    public static boolean isDoCoMo(final int vcardType) {
-        return ((vcardType & FLAG_DOCOMO) != 0);
-    }
-
-    private VCardConfig() {
-    }
-}
\ No newline at end of file
diff --git a/core/java/android/pim/vcard/VCardConstants.java b/core/java/android/pim/vcard/VCardConstants.java
deleted file mode 100644
index 76371ef9..0000000
--- a/core/java/android/pim/vcard/VCardConstants.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-/**
- * Constants used in both exporter and importer code.
- */
-public class VCardConstants {
-    public static final String VERSION_V21 = "2.1";
-    public static final String VERSION_V30 = "3.0";
-    public static final String VERSION_V40 = "4.0";
-
-    // The property names valid both in vCard 2.1 and 3.0.
-    public static final String PROPERTY_BEGIN = "BEGIN";
-    public static final String PROPERTY_VERSION = "VERSION";
-    public static final String PROPERTY_N = "N";
-    public static final String PROPERTY_FN = "FN";
-    public static final String PROPERTY_ADR = "ADR";
-    public static final String PROPERTY_EMAIL = "EMAIL";
-    public static final String PROPERTY_NOTE = "NOTE";
-    public static final String PROPERTY_ORG = "ORG";
-    public static final String PROPERTY_SOUND = "SOUND";  // Not fully supported.
-    public static final String PROPERTY_TEL = "TEL";
-    public static final String PROPERTY_TITLE = "TITLE";
-    public static final String PROPERTY_ROLE = "ROLE";
-    public static final String PROPERTY_PHOTO = "PHOTO";
-    public static final String PROPERTY_LOGO = "LOGO";
-    public static final String PROPERTY_URL = "URL";
-    public static final String PROPERTY_BDAY = "BDAY";  // Birthday (3.0, 4.0)
-    public static final String PROPERTY_BIRTH = "BIRTH";  // Place of birth (4.0)
-    public static final String PROPERTY_ANNIVERSARY = "ANNIVERSARY";  // Date of marriage (4.0)
-    public static final String PROPERTY_NAME = "NAME";  // (3.0, 4,0)
-    public static final String PROPERTY_NICKNAME = "NICKNAME";  // (3.0, 4.0)
-    public static final String PROPERTY_SORT_STRING = "SORT-STRING";  // (3.0, 4.0)
-    public static final String PROPERTY_END = "END";
-
-    // Valid property names not supported (not appropriately handled) by our importer.
-    // TODO: Should be removed from the view of memory efficiency?
-    public static final String PROPERTY_REV = "REV";
-    public static final String PROPERTY_AGENT = "AGENT";  // (3.0)
-    public static final String PROPERTY_DDAY = "DDAY";  // Date of death (4.0)
-    public static final String PROPERTY_DEATH = "DEATH";  // Place of death (4.0)
-
-    // Available in vCard 3.0. Shoud not use when composing vCard 2.1 file.
-    
-    // De-fact property values expressing phonetic names.
-    public static final String PROPERTY_X_PHONETIC_FIRST_NAME = "X-PHONETIC-FIRST-NAME";
-    public static final String PROPERTY_X_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME";
-    public static final String PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME";
-
-    // Properties both ContactsStruct and de-fact vCard extensions
-    // Shown in http://en.wikipedia.org/wiki/VCard support are defined here.
-    public static final String PROPERTY_X_AIM = "X-AIM";
-    public static final String PROPERTY_X_MSN = "X-MSN";
-    public static final String PROPERTY_X_YAHOO = "X-YAHOO";
-    public static final String PROPERTY_X_ICQ = "X-ICQ";
-    public static final String PROPERTY_X_JABBER = "X-JABBER";
-    public static final String PROPERTY_X_GOOGLE_TALK = "X-GOOGLE-TALK";
-    public static final String PROPERTY_X_SKYPE_USERNAME = "X-SKYPE-USERNAME";
-    // Properties only ContactsStruct has. We alse use this.
-    public static final String PROPERTY_X_QQ = "X-QQ";
-    public static final String PROPERTY_X_NETMEETING = "X-NETMEETING";
-
-    // Phone number for Skype, available as usual phone.
-    public static final String PROPERTY_X_SKYPE_PSTNNUMBER = "X-SKYPE-PSTNNUMBER";
-
-    // Property for Android-specific fields.
-    public static final String PROPERTY_X_ANDROID_CUSTOM = "X-ANDROID-CUSTOM";
-
-    // Properties for DoCoMo vCard.
-    public static final String PROPERTY_X_CLASS = "X-CLASS";
-    public static final String PROPERTY_X_REDUCTION = "X-REDUCTION";
-    public static final String PROPERTY_X_NO = "X-NO";
-    public static final String PROPERTY_X_DCM_HMN_MODE = "X-DCM-HMN-MODE";
-
-    public static final String PARAM_TYPE = "TYPE";
-
-    public static final String PARAM_TYPE_HOME = "HOME";
-    public static final String PARAM_TYPE_WORK = "WORK";
-    public static final String PARAM_TYPE_FAX = "FAX";
-    public static final String PARAM_TYPE_CELL = "CELL";
-    public static final String PARAM_TYPE_VOICE = "VOICE";
-    public static final String PARAM_TYPE_INTERNET = "INTERNET";
-
-    public static final String PARAM_CHARSET = "CHARSET";
-    public static final String PARAM_ENCODING = "ENCODING";
-
-    // Abbreviation of "prefered" according to vCard 2.1 specification.
-    // We interpret this value as "primary" property during import/export.
-    //
-    // Note: Both vCard specs does not mention anything about the requirement for this parameter,
-    //       but there may be some vCard importer which will get confused with more than
-    //       one "PREF"s in one property name, while Android accepts them.
-    public static final String PARAM_TYPE_PREF = "PREF";
-
-    // Phone type parameters valid in vCard and known to ContactsContract, but not so common.
-    public static final String PARAM_TYPE_CAR = "CAR";
-    public static final String PARAM_TYPE_ISDN = "ISDN";
-    public static final String PARAM_TYPE_PAGER = "PAGER";
-    public static final String PARAM_TYPE_TLX = "TLX";  // Telex
-
-    // Phone types existing in vCard 2.1 but not known to ContactsContract.
-    public static final String PARAM_TYPE_MODEM = "MODEM";
-    public static final String PARAM_TYPE_MSG = "MSG";
-    public static final String PARAM_TYPE_BBS = "BBS";
-    public static final String PARAM_TYPE_VIDEO = "VIDEO";
-
-    public static final String PARAM_ENCODING_7BIT = "7BIT";
-    public static final String PARAM_ENCODING_8BIT = "8BIT";
-    public static final String PARAM_ENCODING_QP = "QUOTED-PRINTABLE";
-    public static final String PARAM_ENCODING_BASE64 = "BASE64";  // Available in vCard 2.1
-    public static final String PARAM_ENCODING_B = "B";  // Available in vCard 3.0
-
-    // TYPE parameters for Phones, which are not formally valid in vCard (at least 2.1).
-    // These types are basically encoded to "X-" parameters when composing vCard.
-    // Parser passes these when "X-" is added to the parameter or not.
-    public static final String PARAM_PHONE_EXTRA_TYPE_CALLBACK = "CALLBACK";
-    public static final String PARAM_PHONE_EXTRA_TYPE_RADIO = "RADIO";
-    public static final String PARAM_PHONE_EXTRA_TYPE_TTY_TDD = "TTY-TDD";
-    public static final String PARAM_PHONE_EXTRA_TYPE_ASSISTANT = "ASSISTANT";
-    // vCard composer translates this type to "WORK" + "PREF". Just for parsing.
-    public static final String PARAM_PHONE_EXTRA_TYPE_COMPANY_MAIN = "COMPANY-MAIN";
-    // vCard composer translates this type to "VOICE" Just for parsing.
-    public static final String PARAM_PHONE_EXTRA_TYPE_OTHER = "OTHER";
-
-    // TYPE parameters for postal addresses.
-    public static final String PARAM_ADR_TYPE_PARCEL = "PARCEL";
-    public static final String PARAM_ADR_TYPE_DOM = "DOM";
-    public static final String PARAM_ADR_TYPE_INTL = "INTL";
-
-    public static final String PARAM_LANGUAGE = "LANGUAGE";
-
-    // SORT-AS parameter introduced in vCard 4.0 (as of rev.13)
-    public static final String PARAM_SORT_AS = "SORT-AS";
-
-    // TYPE parameters not officially valid but used in some vCard exporter.
-    // Do not use in composer side.
-    public static final String PARAM_EXTRA_TYPE_COMPANY = "COMPANY";
-
-    public interface ImportOnly {
-        public static final String PROPERTY_X_NICKNAME = "X-NICKNAME";
-        // Some device emits this "X-" parameter for expressing Google Talk,
-        // which is specifically invalid but should be always properly accepted, and emitted
-        // in some special case (for that device/application).
-        public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK";
-    }
-
-    //// Mainly for package constants.
-
-    // DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of
-    // SORT-STRING invCard 3.0.
-    /* package */ static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N";
-
-    // Used in unit test.
-    public static final int MAX_DATA_COLUMN = 15;
-
-    /* package */ static final int MAX_CHARACTER_NUMS_QP = 76;
-    static final int MAX_CHARACTER_NUMS_BASE64_V30 = 75;
-
-    private VCardConstants() {
-    }
-}
\ No newline at end of file
diff --git a/core/java/android/pim/vcard/VCardEntry.java b/core/java/android/pim/vcard/VCardEntry.java
deleted file mode 100644
index 02e1674..0000000
--- a/core/java/android/pim/vcard/VCardEntry.java
+++ /dev/null
@@ -1,1515 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.accounts.Account;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-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.StructuredName;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.RawContacts;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-
-/**
- * This class bridges between data structure of Contact app and VCard data.
- */
-public class VCardEntry {
-    private static final String LOG_TAG = "VCardEntry";
-
-    private final static int DEFAULT_ORGANIZATION_TYPE = Organization.TYPE_WORK;
-
-    private static final String ACCOUNT_TYPE_GOOGLE = "com.google";
-    private static final String GOOGLE_MY_CONTACTS_GROUP = "System Group: My Contacts";
-
-    private static final Map<String, Integer> sImMap = new HashMap<String, Integer>();
-
-    static {
-        sImMap.put(VCardConstants.PROPERTY_X_AIM, Im.PROTOCOL_AIM);
-        sImMap.put(VCardConstants.PROPERTY_X_MSN, Im.PROTOCOL_MSN);
-        sImMap.put(VCardConstants.PROPERTY_X_YAHOO, Im.PROTOCOL_YAHOO);
-        sImMap.put(VCardConstants.PROPERTY_X_ICQ, Im.PROTOCOL_ICQ);
-        sImMap.put(VCardConstants.PROPERTY_X_JABBER, Im.PROTOCOL_JABBER);
-        sImMap.put(VCardConstants.PROPERTY_X_SKYPE_USERNAME, Im.PROTOCOL_SKYPE);
-        sImMap.put(VCardConstants.PROPERTY_X_GOOGLE_TALK, Im.PROTOCOL_GOOGLE_TALK);
-        sImMap.put(VCardConstants.ImportOnly.PROPERTY_X_GOOGLE_TALK_WITH_SPACE,
-                Im.PROTOCOL_GOOGLE_TALK);
-    }
-
-    public static class PhoneData {
-        public final int type;
-        public final String data;
-        public final String label;
-        // isPrimary is (not final but) changable, only when there's no appropriate one existing
-        // in the original VCard.
-        public boolean isPrimary;
-        public PhoneData(int type, String data, String label, boolean isPrimary) {
-            this.type = type;
-            this.data = data;
-            this.label = label;
-            this.isPrimary = isPrimary;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof PhoneData)) {
-                return false;
-            }
-            PhoneData phoneData = (PhoneData)obj;
-            return (type == phoneData.type && data.equals(phoneData.data) &&
-                    label.equals(phoneData.label) && isPrimary == phoneData.isPrimary);
-        }
-
-        @Override
-        public String toString() {
-            return String.format("type: %d, data: %s, label: %s, isPrimary: %s",
-                    type, data, label, isPrimary);
-        }
-    }
-
-    public static class EmailData {
-        public final int type;
-        public final String data;
-        // Used only when TYPE is TYPE_CUSTOM.
-        public final String label;
-        public boolean isPrimary;
-        public EmailData(int type, String data, String label, boolean isPrimary) {
-            this.type = type;
-            this.data = data;
-            this.label = label;
-            this.isPrimary = isPrimary;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof EmailData)) {
-                return false;
-            }
-            EmailData emailData = (EmailData)obj;
-            return (type == emailData.type && data.equals(emailData.data) &&
-                    label.equals(emailData.label) && isPrimary == emailData.isPrimary);
-        }
-
-        @Override
-        public String toString() {
-            return String.format("type: %d, data: %s, label: %s, isPrimary: %s",
-                    type, data, label, isPrimary);
-        }
-    }
-
-    public static class PostalData {
-        // Determined by vCard specification.
-        // - PO Box, Extended Addr, Street, Locality, Region, Postal Code, Country Name
-        public static final int ADDR_MAX_DATA_SIZE = 7;
-        private final String[] dataArray;
-        public final String pobox;
-        public final String extendedAddress;
-        public final String street;
-        public final String localty;
-        public final String region;
-        public final String postalCode;
-        public final String country;
-        public final int type;
-        public final String label;
-        public boolean isPrimary;
-
-        public PostalData(final int type, final List<String> propValueList,
-                final String label, boolean isPrimary) {
-            this.type = type;
-            dataArray = new String[ADDR_MAX_DATA_SIZE];
-
-            int size = propValueList.size();
-            if (size > ADDR_MAX_DATA_SIZE) {
-                size = ADDR_MAX_DATA_SIZE;
-            }
-
-            // adr-value = 0*6(text-value ";") text-value
-            //           ; PO Box, Extended Address, Street, Locality, Region, Postal
-            //           ; Code, Country Name
-            //
-            // Use Iterator assuming List may be LinkedList, though actually it is
-            // always ArrayList in the current implementation.
-            int i = 0;
-            for (String addressElement : propValueList) {
-                dataArray[i] = addressElement;
-                if (++i >= size) {
-                    break;
-                }
-            }
-            while (i < ADDR_MAX_DATA_SIZE) {
-                dataArray[i++] = null;
-            }
-
-            this.pobox = dataArray[0];
-            this.extendedAddress = dataArray[1];
-            this.street = dataArray[2];
-            this.localty = dataArray[3];
-            this.region = dataArray[4];
-            this.postalCode = dataArray[5];
-            this.country = dataArray[6];
-            this.label = label;
-            this.isPrimary = isPrimary;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof PostalData)) {
-                return false;
-            }
-            final PostalData postalData = (PostalData)obj;
-            return (Arrays.equals(dataArray, postalData.dataArray) &&
-                    (type == postalData.type &&
-                            (type == StructuredPostal.TYPE_CUSTOM ?
-                                    (label == postalData.label) : true)) &&
-                    (isPrimary == postalData.isPrimary));
-        }
-
-        public String getFormattedAddress(final int vcardType) {
-            StringBuilder builder = new StringBuilder();
-            boolean empty = true;
-            if (VCardConfig.isJapaneseDevice(vcardType)) {
-                // In Japan, the order is reversed.
-                for (int i = ADDR_MAX_DATA_SIZE - 1; i >= 0; i--) {
-                    String addressPart = dataArray[i];
-                    if (!TextUtils.isEmpty(addressPart)) {
-                        if (!empty) {
-                            builder.append(' ');
-                        } else {
-                            empty = false;
-                        }
-                        builder.append(addressPart);
-                    }
-                }
-            } else {
-                for (int i = 0; i < ADDR_MAX_DATA_SIZE; i++) {
-                    String addressPart = dataArray[i];
-                    if (!TextUtils.isEmpty(addressPart)) {
-                        if (!empty) {
-                            builder.append(' ');
-                        } else {
-                            empty = false;
-                        }
-                        builder.append(addressPart);
-                    }
-                }
-            }
-
-            return builder.toString().trim();
-        }
-
-        @Override
-        public String toString() {
-            return String.format("type: %d, label: %s, isPrimary: %s",
-                    type, label, isPrimary);
-        }
-    }
-
-    public static class OrganizationData {
-        public final int type;
-        // non-final is Intentional: we may change the values since this info is separated into
-        // two parts in vCard: "ORG" + "TITLE", and we have to cope with each field in
-        // different timing.
-        public String companyName;
-        public String departmentName;
-        public String titleName;
-        public final String phoneticName;  // We won't have this in "TITLE" property.
-        public boolean isPrimary;
-
-        public OrganizationData(int type,
-                final String companyName,
-                final String departmentName,
-                final String titleName,
-                final String phoneticName,
-                final boolean isPrimary) {
-            this.type = type;
-            this.companyName = companyName;
-            this.departmentName = departmentName;
-            this.titleName = titleName;
-            this.phoneticName = phoneticName;
-            this.isPrimary = isPrimary;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof OrganizationData)) {
-                return false;
-            }
-            OrganizationData organization = (OrganizationData)obj;
-            return (type == organization.type &&
-                    TextUtils.equals(companyName, organization.companyName) &&
-                    TextUtils.equals(departmentName, organization.departmentName) &&
-                    TextUtils.equals(titleName, organization.titleName) &&
-                    isPrimary == organization.isPrimary);
-        }
-
-        public String getFormattedString() {
-            final StringBuilder builder = new StringBuilder();
-            if (!TextUtils.isEmpty(companyName)) {
-                builder.append(companyName);
-            }
-
-            if (!TextUtils.isEmpty(departmentName)) {
-                if (builder.length() > 0) {
-                    builder.append(", ");
-                }
-                builder.append(departmentName);
-            }
-
-            if (!TextUtils.isEmpty(titleName)) {
-                if (builder.length() > 0) {
-                    builder.append(", ");
-                }
-                builder.append(titleName);
-            }
-
-            return builder.toString();
-        }
-
-        @Override
-        public String toString() {
-            return String.format(
-                    "type: %d, company: %s, department: %s, title: %s, isPrimary: %s",
-                    type, companyName, departmentName, titleName, isPrimary);
-        }
-    }
-
-    public static class ImData {
-        public final int protocol;
-        public final String customProtocol;
-        public final int type;
-        public final String data;
-        public final boolean isPrimary;
-
-        public ImData(final int protocol, final String customProtocol, final int type,
-                final String data, final boolean isPrimary) {
-            this.protocol = protocol;
-            this.customProtocol = customProtocol;
-            this.type = type;
-            this.data = data;
-            this.isPrimary = isPrimary;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof ImData)) {
-                return false;
-            }
-            ImData imData = (ImData)obj;
-            return (type == imData.type && protocol == imData.protocol
-                    && (customProtocol != null ? customProtocol.equals(imData.customProtocol) :
-                        (imData.customProtocol == null))
-                    && (data != null ? data.equals(imData.data) : (imData.data == null))
-                    && isPrimary == imData.isPrimary);
-        }
-
-        @Override
-        public String toString() {
-            return String.format(
-                    "type: %d, protocol: %d, custom_protcol: %s, data: %s, isPrimary: %s",
-                    type, protocol, customProtocol, data, isPrimary);
-        }
-    }
-
-    public static class PhotoData {
-        public static final String FORMAT_FLASH = "SWF";
-        public final int type;
-        public final String formatName;  // used when type is not defined in ContactsContract.
-        public final byte[] photoBytes;
-        public final boolean isPrimary;
-
-        public PhotoData(int type, String formatName, byte[] photoBytes, boolean isPrimary) {
-            this.type = type;
-            this.formatName = formatName;
-            this.photoBytes = photoBytes;
-            this.isPrimary = isPrimary;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof PhotoData)) {
-                return false;
-            }
-            PhotoData photoData = (PhotoData)obj;
-            return (type == photoData.type &&
-                    (formatName == null ? (photoData.formatName == null) :
-                            formatName.equals(photoData.formatName)) &&
-                    (Arrays.equals(photoBytes, photoData.photoBytes)) &&
-                    (isPrimary == photoData.isPrimary));
-        }
-
-        @Override
-        public String toString() {
-            return String.format("type: %d, format: %s: size: %d, isPrimary: %s",
-                    type, formatName, photoBytes.length, isPrimary);
-        }
-    }
-
-    /* package */ static class Property {
-        private String mPropertyName;
-        private Map<String, Collection<String>> mParameterMap =
-            new HashMap<String, Collection<String>>();
-        private List<String> mPropertyValueList = new ArrayList<String>();
-        private byte[] mPropertyBytes;
-
-        public void setPropertyName(final String propertyName) {
-            mPropertyName = propertyName;
-        }
-
-        public void addParameter(final String paramName, final String paramValue) {
-            Collection<String> values;
-            if (!mParameterMap.containsKey(paramName)) {
-                if (paramName.equals("TYPE")) {
-                    values = new HashSet<String>();
-                } else {
-                    values = new ArrayList<String>();
-                }
-                mParameterMap.put(paramName, values);
-            } else {
-                values = mParameterMap.get(paramName);
-            }
-            values.add(paramValue);
-        }
-
-        public void addToPropertyValueList(final String propertyValue) {
-            mPropertyValueList.add(propertyValue);
-        }
-
-        public void setPropertyBytes(final byte[] propertyBytes) {
-            mPropertyBytes = propertyBytes;
-        }
-
-        public final Collection<String> getParameters(String type) {
-            return mParameterMap.get(type);
-        }
-
-        public final List<String> getPropertyValueList() {
-            return mPropertyValueList;
-        }
-
-        public void clear() {
-            mPropertyName = null;
-            mParameterMap.clear();
-            mPropertyValueList.clear();
-            mPropertyBytes = null;
-        }
-    }
-
-    // TODO(dmiyakawa): vCard 4.0 logically has multiple formatted names and we need to
-    // select the most preferable one using PREF parameter.
-    //
-    // e.g. (based on rev.13)
-    // FN;PREF=1:John M. Doe
-    // FN;PREF=2:John Doe
-    // FN;PREF=3;John
-
-    private String mFamilyName;
-    private String mGivenName;
-    private String mMiddleName;
-    private String mPrefix;
-    private String mSuffix;
-
-    // Used only when no family nor given name is found.
-    private String mFormattedName;
-
-    private String mPhoneticFamilyName;
-    private String mPhoneticGivenName;
-    private String mPhoneticMiddleName;
-
-    private String mPhoneticFullName;
-
-    private List<String> mNickNameList;
-
-    private String mDisplayName;
-
-    private String mBirthday;
-    private String mAnniversary;
-
-    private List<String> mNoteList;
-    private List<PhoneData> mPhoneList;
-    private List<EmailData> mEmailList;
-    private List<PostalData> mPostalList;
-    private List<OrganizationData> mOrganizationList;
-    private List<ImData> mImList;
-    private List<PhotoData> mPhotoList;
-    private List<String> mWebsiteList;
-    private List<List<String>> mAndroidCustomPropertyList;
-
-    private final int mVCardType;
-    private final Account mAccount;
-
-    public VCardEntry() {
-        this(VCardConfig.VCARD_TYPE_V21_GENERIC);
-    }
-
-    public VCardEntry(int vcardType) {
-        this(vcardType, null);
-    }
-
-    public VCardEntry(int vcardType, Account account) {
-        mVCardType = vcardType;
-        mAccount = account;
-    }
-
-    private void addPhone(int type, String data, String label, boolean isPrimary) {
-        if (mPhoneList == null) {
-            mPhoneList = new ArrayList<PhoneData>();
-        }
-        final StringBuilder builder = new StringBuilder();
-        final String trimed = data.trim();
-        final String formattedNumber;
-        if (type == Phone.TYPE_PAGER || VCardConfig.refrainPhoneNumberFormatting(mVCardType)) {
-            formattedNumber = trimed;
-        } else {
-            final int length = trimed.length();
-            for (int i = 0; i < length; i++) {
-                char ch = trimed.charAt(i);
-                if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) {
-                    builder.append(ch);
-                }
-            }
-
-            final int formattingType = VCardUtils.getPhoneNumberFormat(mVCardType);
-            formattedNumber = PhoneNumberUtils.formatNumber(builder.toString(), formattingType);
-        }
-        PhoneData phoneData = new PhoneData(type, formattedNumber, label, isPrimary);
-        mPhoneList.add(phoneData);
-    }
-
-    private void addNickName(final String nickName) {
-        if (mNickNameList == null) {
-            mNickNameList = new ArrayList<String>();
-        }
-        mNickNameList.add(nickName);
-    }
-
-    private void addEmail(int type, String data, String label, boolean isPrimary){
-        if (mEmailList == null) {
-            mEmailList = new ArrayList<EmailData>();
-        }
-        mEmailList.add(new EmailData(type, data, label, isPrimary));
-    }
-
-    private void addPostal(int type, List<String> propValueList, String label, boolean isPrimary){
-        if (mPostalList == null) {
-            mPostalList = new ArrayList<PostalData>(0);
-        }
-        mPostalList.add(new PostalData(type, propValueList, label, isPrimary));
-    }
-
-    /**
-     * Should be called via {@link #handleOrgValue(int, List, Map, boolean) or
-     * {@link #handleTitleValue(String)}.
-     */
-    private void addNewOrganization(int type, final String companyName,
-            final String departmentName,
-            final String titleName,
-            final String phoneticName,
-            final boolean isPrimary) {
-        if (mOrganizationList == null) {
-            mOrganizationList = new ArrayList<OrganizationData>();
-        }
-        mOrganizationList.add(new OrganizationData(type, companyName,
-                departmentName, titleName, phoneticName, isPrimary));
-    }
-
-    private static final List<String> sEmptyList =
-            Collections.unmodifiableList(new ArrayList<String>(0));
-
-    private String buildSinglePhoneticNameFromSortAsParam(Map<String, Collection<String>> paramMap) {
-        final Collection<String> sortAsCollection = paramMap.get(VCardConstants.PARAM_SORT_AS);
-        if (sortAsCollection != null && sortAsCollection.size() != 0) {
-            if (sortAsCollection.size() > 1) {
-                Log.w(LOG_TAG, "Incorrect multiple SORT_AS parameters detected: " +
-                        Arrays.toString(sortAsCollection.toArray()));
-            }
-            final List<String> sortNames =
-                    VCardUtils.constructListFromValue(sortAsCollection.iterator().next(),
-                            mVCardType);
-            final StringBuilder builder = new StringBuilder();
-            for (final String elem : sortNames) {
-                builder.append(elem);
-            }
-            return builder.toString();
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Set "ORG" related values to the appropriate data. If there's more than one
-     * {@link OrganizationData} objects, this input data are attached to the last one which
-     * does not have valid values (not including empty but only null). If there's no
-     * {@link OrganizationData} object, a new {@link OrganizationData} is created,
-     * whose title is set to null.
-     */
-    private void handleOrgValue(final int type, List<String> orgList,
-            Map<String, Collection<String>> paramMap, boolean isPrimary) {
-        final String phoneticName = buildSinglePhoneticNameFromSortAsParam(paramMap);
-        if (orgList == null) {
-            orgList = sEmptyList;
-        }
-        final String companyName;
-        final String departmentName;
-        final int size = orgList.size();
-        switch (size) {
-            case 0: {
-                companyName = "";
-                departmentName = null;
-                break;
-            }
-            case 1: {
-                companyName = orgList.get(0);
-                departmentName = null;
-                break;
-            }
-            default: {  // More than 1.
-                companyName = orgList.get(0);
-                // We're not sure which is the correct string for department.
-                // In order to keep all the data, concatinate the rest of elements.
-                StringBuilder builder = new StringBuilder();
-                for (int i = 1; i < size; i++) {
-                    if (i > 1) {
-                        builder.append(' ');
-                    }
-                    builder.append(orgList.get(i));
-                }
-                departmentName = builder.toString();
-            }
-        }
-        if (mOrganizationList == null) {
-            // Create new first organization entry, with "null" title which may be
-            // added via handleTitleValue().
-            addNewOrganization(type, companyName, departmentName, null, phoneticName, isPrimary);
-            return;
-        }
-        for (OrganizationData organizationData : mOrganizationList) {
-            // Not use TextUtils.isEmpty() since ORG was set but the elements might be empty.
-            // e.g. "ORG;PREF:;" -> Both companyName and departmentName become empty but not null.
-            if (organizationData.companyName == null &&
-                    organizationData.departmentName == null) {
-                // Probably the "TITLE" property comes before the "ORG" property via
-                // handleTitleLine().
-                organizationData.companyName = companyName;
-                organizationData.departmentName = departmentName;
-                organizationData.isPrimary = isPrimary;
-                return;
-            }
-        }
-        // No OrganizatioData is available. Create another one, with "null" title, which may be
-        // added via handleTitleValue().
-        addNewOrganization(type, companyName, departmentName, null, phoneticName, isPrimary);
-    }
-
-    /**
-     * Set "title" value to the appropriate data. If there's more than one
-     * OrganizationData objects, this input is attached to the last one which does not
-     * have valid title value (not including empty but only null). If there's no
-     * OrganizationData object, a new OrganizationData is created, whose company name is
-     * set to null.
-     */
-    private void handleTitleValue(final String title) {
-        if (mOrganizationList == null) {
-            // Create new first organization entry, with "null" other info, which may be
-            // added via handleOrgValue().
-            addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, null, false);
-            return;
-        }
-        for (OrganizationData organizationData : mOrganizationList) {
-            if (organizationData.titleName == null) {
-                organizationData.titleName = title;
-                return;
-            }
-        }
-        // No Organization is available. Create another one, with "null" other info, which may be
-        // added via handleOrgValue().
-        addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, null, false);
-    }
-
-    private void addIm(int protocol, String customProtocol, int type,
-            String propValue, boolean isPrimary) {
-        if (mImList == null) {
-            mImList = new ArrayList<ImData>();
-        }
-        mImList.add(new ImData(protocol, customProtocol, type, propValue, isPrimary));
-    }
-
-    private void addNote(final String note) {
-        if (mNoteList == null) {
-            mNoteList = new ArrayList<String>(1);
-        }
-        mNoteList.add(note);
-    }
-
-    private void addPhotoBytes(String formatName, byte[] photoBytes, boolean isPrimary) {
-        if (mPhotoList == null) {
-            mPhotoList = new ArrayList<PhotoData>(1);
-        }
-        final PhotoData photoData = new PhotoData(0, null, photoBytes, isPrimary);
-        mPhotoList.add(photoData);
-    }
-
-    /**
-     * Tries to extract paramMap, constructs SORT-AS parameter values, and store them in
-     * appropriate phonetic name variables.
-     *
-     * This method does not care the vCard version. Even when we have SORT-AS parameters in
-     * invalid versions (i.e. 2.1 and 3.0), we scilently accept them so that we won't drop
-     * meaningful information. If we had this parameter in the N field of vCard 3.0, and
-     * the contact data also have SORT-STRING, we will prefer SORT-STRING, since it is
-     * regitimate property to be understood.
-     */
-    private void tryHandleSortAsName(final Map<String, Collection<String>> paramMap) {
-        if (VCardConfig.isVersion30(mVCardType) &&
-                !(TextUtils.isEmpty(mPhoneticFamilyName) &&
-                        TextUtils.isEmpty(mPhoneticMiddleName) &&
-                        TextUtils.isEmpty(mPhoneticGivenName))) {
-            return;
-        }
-
-        final Collection<String> sortAsCollection = paramMap.get(VCardConstants.PARAM_SORT_AS);
-        if (sortAsCollection != null && sortAsCollection.size() != 0) {
-            if (sortAsCollection.size() > 1) {
-                Log.w(LOG_TAG, "Incorrect multiple SORT_AS parameters detected: " +
-                        Arrays.toString(sortAsCollection.toArray()));
-            }
-            final List<String> sortNames =
-                    VCardUtils.constructListFromValue(sortAsCollection.iterator().next(),
-                            mVCardType);
-            int size = sortNames.size();
-            if (size > 3) {
-                size = 3;
-            }
-            switch (size) {
-            case 3: mPhoneticMiddleName = sortNames.get(2); //$FALL-THROUGH$
-            case 2: mPhoneticGivenName = sortNames.get(1); //$FALL-THROUGH$
-            default: mPhoneticFamilyName = sortNames.get(0); break;
-            }
-        }
-    }
-
-    @SuppressWarnings("fallthrough")
-    private void handleNProperty(final List<String> paramValues,
-            Map<String, Collection<String>> paramMap) {
-        // in vCard 4.0, SORT-AS parameter is available.
-        tryHandleSortAsName(paramMap);
-
-        // Family, Given, Middle, Prefix, Suffix. (1 - 5)
-        int size;
-        if (paramValues == null || (size = paramValues.size()) < 1) {
-            return;
-        }
-        if (size > 5) {
-            size = 5;
-        }
-
-        switch (size) {
-        // Fall-through.
-        case 5: mSuffix = paramValues.get(4);
-        case 4: mPrefix = paramValues.get(3);
-        case 3: mMiddleName = paramValues.get(2);
-        case 2: mGivenName = paramValues.get(1);
-        default: mFamilyName = paramValues.get(0);
-        }
-    }
-
-    /**
-     * Note: Some Japanese mobile phones use this field for phonetic name,
-     *       since vCard 2.1 does not have "SORT-STRING" type.
-     *       Also, in some cases, the field has some ';'s in it.
-     *       Assume the ';' means the same meaning in N property
-     */
-    @SuppressWarnings("fallthrough")
-    private void handlePhoneticNameFromSound(List<String> elems) {
-        if (!(TextUtils.isEmpty(mPhoneticFamilyName) &&
-                TextUtils.isEmpty(mPhoneticMiddleName) &&
-                TextUtils.isEmpty(mPhoneticGivenName))) {
-            // This means the other properties like "X-PHONETIC-FIRST-NAME" was already found.
-            // Ignore "SOUND;X-IRMC-N".
-            return;
-        }
-
-        int size;
-        if (elems == null || (size = elems.size()) < 1) {
-            return;
-        }
-
-        // Assume that the order is "Family, Given, Middle".
-        // This is not from specification but mere assumption. Some Japanese phones use this order.
-        if (size > 3) {
-            size = 3;
-        }
-
-        if (elems.get(0).length() > 0) {
-            boolean onlyFirstElemIsNonEmpty = true;
-            for (int i = 1; i < size; i++) {
-                if (elems.get(i).length() > 0) {
-                    onlyFirstElemIsNonEmpty = false;
-                    break;
-                }
-            }
-            if (onlyFirstElemIsNonEmpty) {
-                final String[] namesArray = elems.get(0).split(" ");
-                final int nameArrayLength = namesArray.length;
-                if (nameArrayLength == 3) {
-                    // Assume the string is "Family Middle Given".
-                    mPhoneticFamilyName = namesArray[0];
-                    mPhoneticMiddleName = namesArray[1];
-                    mPhoneticGivenName = namesArray[2];
-                } else if (nameArrayLength == 2) {
-                    // Assume the string is "Family Given" based on the Japanese mobile
-                    // phones' preference.
-                    mPhoneticFamilyName = namesArray[0];
-                    mPhoneticGivenName = namesArray[1];
-                } else {
-                    mPhoneticFullName = elems.get(0);
-                }
-                return;
-            }
-        }
-
-        switch (size) {
-            // fallthrough
-            case 3: mPhoneticMiddleName = elems.get(2);
-            case 2: mPhoneticGivenName = elems.get(1);
-            default: mPhoneticFamilyName = elems.get(0);
-        }
-    }
-
-    public void addProperty(final Property property) {
-        final String propName = property.mPropertyName;
-        final Map<String, Collection<String>> paramMap = property.mParameterMap;
-        final List<String> propValueList = property.mPropertyValueList;
-        byte[] propBytes = property.mPropertyBytes;
-
-        if (propValueList.size() == 0) {
-            return;
-        }
-        final String propValue = listToString(propValueList).trim();
-
-        if (propName.equals(VCardConstants.PROPERTY_VERSION)) {
-            // vCard version. Ignore this.
-        } else if (propName.equals(VCardConstants.PROPERTY_FN)) {
-            mFormattedName = propValue;
-        } else if (propName.equals(VCardConstants.PROPERTY_NAME) && mFormattedName == null) {
-            // Only in vCard 3.0. Use this if FN, which must exist in vCard 3.0 but may not
-            // actually exist in the real vCard data, does not exist.
-            mFormattedName = propValue;
-        } else if (propName.equals(VCardConstants.PROPERTY_N)) {
-            handleNProperty(propValueList, paramMap);
-        } else if (propName.equals(VCardConstants.PROPERTY_SORT_STRING)) {
-            mPhoneticFullName = propValue;
-        } else if (propName.equals(VCardConstants.PROPERTY_NICKNAME) ||
-                propName.equals(VCardConstants.ImportOnly.PROPERTY_X_NICKNAME)) {
-            addNickName(propValue);
-        } else if (propName.equals(VCardConstants.PROPERTY_SOUND)) {
-            Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
-            if (typeCollection != null
-                    && typeCollection.contains(VCardConstants.PARAM_TYPE_X_IRMC_N)) {
-                // As of 2009-10-08, Parser side does not split a property value into separated
-                // values using ';' (in other words, propValueList.size() == 1),
-                // which is correct behavior from the view of vCard 2.1.
-                // But we want it to be separated, so do the separation here.
-                final List<String> phoneticNameList =
-                        VCardUtils.constructListFromValue(propValue, mVCardType);
-                handlePhoneticNameFromSound(phoneticNameList);
-            } else {
-                // Ignore this field since Android cannot understand what it is.
-            }
-        } else if (propName.equals(VCardConstants.PROPERTY_ADR)) {
-            boolean valuesAreAllEmpty = true;
-            for (String value : propValueList) {
-                if (value.length() > 0) {
-                    valuesAreAllEmpty = false;
-                    break;
-                }
-            }
-            if (valuesAreAllEmpty) {
-                return;
-            }
-
-            int type = -1;
-            String label = "";
-            boolean isPrimary = false;
-            Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
-            if (typeCollection != null) {
-                for (String typeString : typeCollection) {
-                    typeString = typeString.toUpperCase();
-                    if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) {
-                        isPrimary = true;
-                    } else if (typeString.equals(VCardConstants.PARAM_TYPE_HOME)) {
-                        type = StructuredPostal.TYPE_HOME;
-                        label = "";
-                    } else if (typeString.equals(VCardConstants.PARAM_TYPE_WORK) ||
-                            typeString.equalsIgnoreCase(VCardConstants.PARAM_EXTRA_TYPE_COMPANY)) {
-                        // "COMPANY" seems emitted by Windows Mobile, which is not
-                        // specifically supported by vCard 2.1. We assume this is same
-                        // as "WORK".
-                        type = StructuredPostal.TYPE_WORK;
-                        label = "";
-                    } else if (typeString.equals(VCardConstants.PARAM_ADR_TYPE_PARCEL) ||
-                            typeString.equals(VCardConstants.PARAM_ADR_TYPE_DOM) ||
-                            typeString.equals(VCardConstants.PARAM_ADR_TYPE_INTL)) {
-                        // We do not have any appropriate way to store this information.
-                    } else {
-                        if (typeString.startsWith("X-") && type < 0) {
-                            typeString = typeString.substring(2);
-                        }
-                        // vCard 3.0 allows iana-token. Also some vCard 2.1 exporters
-                        // emit non-standard types. We do not handle their values now.
-                        type = StructuredPostal.TYPE_CUSTOM;
-                        label = typeString;
-                    }
-                }
-            }
-            // We use "HOME" as default
-            if (type < 0) {
-                type = StructuredPostal.TYPE_HOME;
-            }
-
-            addPostal(type, propValueList, label, isPrimary);
-        } else if (propName.equals(VCardConstants.PROPERTY_EMAIL)) {
-            int type = -1;
-            String label = null;
-            boolean isPrimary = false;
-            Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
-            if (typeCollection != null) {
-                for (String typeString : typeCollection) {
-                    typeString = typeString.toUpperCase();
-                    if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) {
-                        isPrimary = true;
-                    } else if (typeString.equals(VCardConstants.PARAM_TYPE_HOME)) {
-                        type = Email.TYPE_HOME;
-                    } else if (typeString.equals(VCardConstants.PARAM_TYPE_WORK)) {
-                        type = Email.TYPE_WORK;
-                    } else if (typeString.equals(VCardConstants.PARAM_TYPE_CELL)) {
-                        type = Email.TYPE_MOBILE;
-                    } else {
-                        if (typeString.startsWith("X-") && type < 0) {
-                            typeString = typeString.substring(2);
-                        }
-                        // vCard 3.0 allows iana-token.
-                        // We may have INTERNET (specified in vCard spec),
-                        // SCHOOL, etc.
-                        type = Email.TYPE_CUSTOM;
-                        label = typeString;
-                    }
-                }
-            }
-            if (type < 0) {
-                type = Email.TYPE_OTHER;
-            }
-            addEmail(type, propValue, label, isPrimary);
-        } else if (propName.equals(VCardConstants.PROPERTY_ORG)) {
-            // vCard specification does not specify other types.
-            final int type = Organization.TYPE_WORK;
-            boolean isPrimary = false;
-            Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
-            if (typeCollection != null) {
-                for (String typeString : typeCollection) {
-                    if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) {
-                        isPrimary = true;
-                    }
-                }
-            }
-            handleOrgValue(type, propValueList, paramMap, isPrimary);
-        } else if (propName.equals(VCardConstants.PROPERTY_TITLE)) {
-            handleTitleValue(propValue);
-        } else if (propName.equals(VCardConstants.PROPERTY_ROLE)) {
-            // This conflicts with TITLE. Ignore for now...
-            // handleTitleValue(propValue);
-        } else if (propName.equals(VCardConstants.PROPERTY_PHOTO) ||
-                propName.equals(VCardConstants.PROPERTY_LOGO)) {
-            Collection<String> paramMapValue = paramMap.get("VALUE");
-            if (paramMapValue != null && paramMapValue.contains("URL")) {
-                // Currently we do not have appropriate example for testing this case.
-            } else {
-                final Collection<String> typeCollection = paramMap.get("TYPE");
-                String formatName = null;
-                boolean isPrimary = false;
-                if (typeCollection != null) {
-                    for (String typeValue : typeCollection) {
-                        if (VCardConstants.PARAM_TYPE_PREF.equals(typeValue)) {
-                            isPrimary = true;
-                        } else if (formatName == null){
-                            formatName = typeValue;
-                        }
-                    }
-                }
-                addPhotoBytes(formatName, propBytes, isPrimary);
-            }
-        } else if (propName.equals(VCardConstants.PROPERTY_TEL)) {
-            final Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
-            final Object typeObject =
-                VCardUtils.getPhoneTypeFromStrings(typeCollection, propValue);
-            final int type;
-            final String label;
-            if (typeObject instanceof Integer) {
-                type = (Integer)typeObject;
-                label = null;
-            } else {
-                type = Phone.TYPE_CUSTOM;
-                label = typeObject.toString();
-            }
-
-            final boolean isPrimary;
-            if (typeCollection != null && typeCollection.contains(VCardConstants.PARAM_TYPE_PREF)) {
-                isPrimary = true;
-            } else {
-                isPrimary = false;
-            }
-            addPhone(type, propValue, label, isPrimary);
-        } else if (propName.equals(VCardConstants.PROPERTY_X_SKYPE_PSTNNUMBER)) {
-            // The phone number available via Skype.
-            Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
-            final int type = Phone.TYPE_OTHER;
-            final boolean isPrimary;
-            if (typeCollection != null && typeCollection.contains(VCardConstants.PARAM_TYPE_PREF)) {
-                isPrimary = true;
-            } else {
-                isPrimary = false;
-            }
-            addPhone(type, propValue, null, isPrimary);
-        } else if (sImMap.containsKey(propName)) {
-            final int protocol = sImMap.get(propName);
-            boolean isPrimary = false;
-            int type = -1;
-            final Collection<String> typeCollection = paramMap.get(VCardConstants.PARAM_TYPE);
-            if (typeCollection != null) {
-                for (String typeString : typeCollection) {
-                    if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) {
-                        isPrimary = true;
-                    } else if (type < 0) {
-                        if (typeString.equalsIgnoreCase(VCardConstants.PARAM_TYPE_HOME)) {
-                            type = Im.TYPE_HOME;
-                        } else if (typeString.equalsIgnoreCase(VCardConstants.PARAM_TYPE_WORK)) {
-                            type = Im.TYPE_WORK;
-                        }
-                    }
-                }
-            }
-            if (type < 0) {
-                type = Im.TYPE_HOME;
-            }
-            addIm(protocol, null, type, propValue, isPrimary);
-        } else if (propName.equals(VCardConstants.PROPERTY_NOTE)) {
-            addNote(propValue);
-        } else if (propName.equals(VCardConstants.PROPERTY_URL)) {
-            if (mWebsiteList == null) {
-                mWebsiteList = new ArrayList<String>(1);
-            }
-            mWebsiteList.add(propValue);
-        } else if (propName.equals(VCardConstants.PROPERTY_BDAY)) {
-            mBirthday = propValue;
-        } else if (propName.equals(VCardConstants.PROPERTY_ANNIVERSARY)) {
-            mAnniversary = propValue;
-        } else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_FIRST_NAME)) {
-            mPhoneticGivenName = propValue;
-        } else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_MIDDLE_NAME)) {
-            mPhoneticMiddleName = propValue;
-        } else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_LAST_NAME)) {
-            mPhoneticFamilyName = propValue;
-        } else if (propName.equals(VCardConstants.PROPERTY_X_ANDROID_CUSTOM)) {
-            final List<String> customPropertyList =
-                VCardUtils.constructListFromValue(propValue, mVCardType);
-            handleAndroidCustomProperty(customPropertyList);
-        } else {
-        }
-    }
-
-    private void handleAndroidCustomProperty(final List<String> customPropertyList) {
-        if (mAndroidCustomPropertyList == null) {
-            mAndroidCustomPropertyList = new ArrayList<List<String>>();
-        }
-        mAndroidCustomPropertyList.add(customPropertyList);
-    }
-
-    /**
-     * Construct the display name. The constructed data must not be null.
-     */
-    private void constructDisplayName() {
-        // FullName (created via "FN" or "NAME" field) is prefered.
-        if (!TextUtils.isEmpty(mFormattedName)) {
-            mDisplayName = mFormattedName;
-        } else if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) {
-            mDisplayName = VCardUtils.constructNameFromElements(mVCardType,
-                    mFamilyName, mMiddleName, mGivenName, mPrefix, mSuffix);
-        } else if (!(TextUtils.isEmpty(mPhoneticFamilyName) &&
-                TextUtils.isEmpty(mPhoneticGivenName))) {
-            mDisplayName = VCardUtils.constructNameFromElements(mVCardType,
-                    mPhoneticFamilyName, mPhoneticMiddleName, mPhoneticGivenName);
-        } else if (mEmailList != null && mEmailList.size() > 0) {
-            mDisplayName = mEmailList.get(0).data;
-        } else if (mPhoneList != null && mPhoneList.size() > 0) {
-            mDisplayName = mPhoneList.get(0).data;
-        } else if (mPostalList != null && mPostalList.size() > 0) {
-            mDisplayName = mPostalList.get(0).getFormattedAddress(mVCardType);
-        } else if (mOrganizationList != null && mOrganizationList.size() > 0) {
-            mDisplayName = mOrganizationList.get(0).getFormattedString();
-        }
-
-        if (mDisplayName == null) {
-            mDisplayName = "";
-        }
-    }
-
-    /**
-     * Consolidate several fielsds (like mName) using name candidates,
-     */
-    public void consolidateFields() {
-        constructDisplayName();
-
-        if (mPhoneticFullName != null) {
-            mPhoneticFullName = mPhoneticFullName.trim();
-        }
-    }
-
-    public Uri pushIntoContentResolver(ContentResolver resolver) {
-        ArrayList<ContentProviderOperation> operationList =
-            new ArrayList<ContentProviderOperation>();
-        // After applying the batch the first result's Uri is returned so it is important that
-        // the RawContact is the first operation that gets inserted into the list
-        ContentProviderOperation.Builder builder =
-            ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
-        String myGroupsId = null;
-        if (mAccount != null) {
-            builder.withValue(RawContacts.ACCOUNT_NAME, mAccount.name);
-            builder.withValue(RawContacts.ACCOUNT_TYPE, mAccount.type);
-
-            // Assume that caller side creates this group if it does not exist.
-            if (ACCOUNT_TYPE_GOOGLE.equals(mAccount.type)) {
-                final Cursor cursor = resolver.query(Groups.CONTENT_URI, new String[] {
-                        Groups.SOURCE_ID },
-                        Groups.TITLE + "=?", new String[] {
-                        GOOGLE_MY_CONTACTS_GROUP }, null);
-                try {
-                    if (cursor != null && cursor.moveToFirst()) {
-                        myGroupsId = cursor.getString(0);
-                    }
-                } finally {
-                    if (cursor != null) {
-                        cursor.close();
-                    }
-                }
-            }
-        } else {
-            builder.withValue(RawContacts.ACCOUNT_NAME, null);
-            builder.withValue(RawContacts.ACCOUNT_TYPE, null);
-        }
-        operationList.add(builder.build());
-
-        if (!nameFieldsAreEmpty()) {
-            builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-            builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0);
-            builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
-
-            builder.withValue(StructuredName.GIVEN_NAME, mGivenName);
-            builder.withValue(StructuredName.FAMILY_NAME, mFamilyName);
-            builder.withValue(StructuredName.MIDDLE_NAME, mMiddleName);
-            builder.withValue(StructuredName.PREFIX, mPrefix);
-            builder.withValue(StructuredName.SUFFIX, mSuffix);
-
-            if (!(TextUtils.isEmpty(mPhoneticGivenName)
-                    && TextUtils.isEmpty(mPhoneticFamilyName)
-                    && TextUtils.isEmpty(mPhoneticMiddleName))) {
-                builder.withValue(StructuredName.PHONETIC_GIVEN_NAME, mPhoneticGivenName);
-                builder.withValue(StructuredName.PHONETIC_FAMILY_NAME, mPhoneticFamilyName);
-                builder.withValue(StructuredName.PHONETIC_MIDDLE_NAME, mPhoneticMiddleName);
-            } else if (!TextUtils.isEmpty(mPhoneticFullName)) {
-                builder.withValue(StructuredName.PHONETIC_GIVEN_NAME, mPhoneticFullName);
-            }
-
-            builder.withValue(StructuredName.DISPLAY_NAME, getDisplayName());
-            operationList.add(builder.build());
-        }
-
-        if (mNickNameList != null && mNickNameList.size() > 0) {
-            for (String nickName : mNickNameList) {
-                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-                builder.withValueBackReference(Nickname.RAW_CONTACT_ID, 0);
-                builder.withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE);
-                builder.withValue(Nickname.TYPE, Nickname.TYPE_DEFAULT);
-                builder.withValue(Nickname.NAME, nickName);
-                operationList.add(builder.build());
-            }
-        }
-
-        if (mPhoneList != null) {
-            for (PhoneData phoneData : mPhoneList) {
-                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-                builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0);
-                builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
-
-                builder.withValue(Phone.TYPE, phoneData.type);
-                if (phoneData.type == Phone.TYPE_CUSTOM) {
-                    builder.withValue(Phone.LABEL, phoneData.label);
-                }
-                builder.withValue(Phone.NUMBER, phoneData.data);
-                if (phoneData.isPrimary) {
-                    builder.withValue(Phone.IS_PRIMARY, 1);
-                }
-                operationList.add(builder.build());
-            }
-        }
-
-        if (mOrganizationList != null) {
-            for (OrganizationData organizationData : mOrganizationList) {
-                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-                builder.withValueBackReference(Organization.RAW_CONTACT_ID, 0);
-                builder.withValue(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
-                builder.withValue(Organization.TYPE, organizationData.type);
-                if (organizationData.companyName != null) {
-                    builder.withValue(Organization.COMPANY, organizationData.companyName);
-                }
-                if (organizationData.departmentName != null) {
-                    builder.withValue(Organization.DEPARTMENT, organizationData.departmentName);
-                }
-                if (organizationData.titleName != null) {
-                    builder.withValue(Organization.TITLE, organizationData.titleName);
-                }
-                if (organizationData.phoneticName != null) {
-                    builder.withValue(Organization.PHONETIC_NAME, organizationData.phoneticName);
-                }
-                if (organizationData.isPrimary) {
-                    builder.withValue(Organization.IS_PRIMARY, 1);
-                }
-                operationList.add(builder.build());
-            }
-        }
-
-        if (mEmailList != null) {
-            for (EmailData emailData : mEmailList) {
-                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-                builder.withValueBackReference(Email.RAW_CONTACT_ID, 0);
-                builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
-
-                builder.withValue(Email.TYPE, emailData.type);
-                if (emailData.type == Email.TYPE_CUSTOM) {
-                    builder.withValue(Email.LABEL, emailData.label);
-                }
-                builder.withValue(Email.DATA, emailData.data);
-                if (emailData.isPrimary) {
-                    builder.withValue(Data.IS_PRIMARY, 1);
-                }
-                operationList.add(builder.build());
-            }
-        }
-
-        if (mPostalList != null) {
-            for (PostalData postalData : mPostalList) {
-                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-                VCardUtils.insertStructuredPostalDataUsingContactsStruct(
-                        mVCardType, builder, postalData);
-                operationList.add(builder.build());
-            }
-        }
-
-        if (mImList != null) {
-            for (ImData imData : mImList) {
-                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-                builder.withValueBackReference(Im.RAW_CONTACT_ID, 0);
-                builder.withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
-                builder.withValue(Im.TYPE, imData.type);
-                builder.withValue(Im.PROTOCOL, imData.protocol);
-                builder.withValue(Im.DATA, imData.data);
-                if (imData.protocol == Im.PROTOCOL_CUSTOM) {
-                    builder.withValue(Im.CUSTOM_PROTOCOL, imData.customProtocol);
-                }
-                if (imData.isPrimary) {
-                    builder.withValue(Data.IS_PRIMARY, 1);
-                }
-                operationList.add(builder.build());
-            }
-        }
-
-        if (mNoteList != null) {
-            for (String note : mNoteList) {
-                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-                builder.withValueBackReference(Note.RAW_CONTACT_ID, 0);
-                builder.withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
-                builder.withValue(Note.NOTE, note);
-                operationList.add(builder.build());
-            }
-        }
-
-        if (mPhotoList != null) {
-            for (PhotoData photoData : mPhotoList) {
-                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-                builder.withValueBackReference(Photo.RAW_CONTACT_ID, 0);
-                builder.withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
-                builder.withValue(Photo.PHOTO, photoData.photoBytes);
-                if (photoData.isPrimary) {
-                    builder.withValue(Photo.IS_PRIMARY, 1);
-                }
-                operationList.add(builder.build());
-            }
-        }
-
-        if (mWebsiteList != null) {
-            for (String website : mWebsiteList) {
-                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-                builder.withValueBackReference(Website.RAW_CONTACT_ID, 0);
-                builder.withValue(Data.MIMETYPE, Website.CONTENT_ITEM_TYPE);
-                builder.withValue(Website.URL, website);
-                // There's no information about the type of URL in vCard.
-                // We use TYPE_HOMEPAGE for safety.
-                builder.withValue(Website.TYPE, Website.TYPE_HOMEPAGE);
-                operationList.add(builder.build());
-            }
-        }
-
-        if (!TextUtils.isEmpty(mBirthday)) {
-            builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-            builder.withValueBackReference(Event.RAW_CONTACT_ID, 0);
-            builder.withValue(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
-            builder.withValue(Event.START_DATE, mBirthday);
-            builder.withValue(Event.TYPE, Event.TYPE_BIRTHDAY);
-            operationList.add(builder.build());
-        }
-
-        if (!TextUtils.isEmpty(mAnniversary)) {
-            builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-            builder.withValueBackReference(Event.RAW_CONTACT_ID, 0);
-            builder.withValue(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
-            builder.withValue(Event.START_DATE, mAnniversary);
-            builder.withValue(Event.TYPE, Event.TYPE_ANNIVERSARY);
-            operationList.add(builder.build());
-        }
-
-        if (mAndroidCustomPropertyList != null) {
-            for (List<String> customPropertyList : mAndroidCustomPropertyList) {
-                int size = customPropertyList.size();
-                if (size < 2 || TextUtils.isEmpty(customPropertyList.get(0))) {
-                    continue;
-                } else if (size > VCardConstants.MAX_DATA_COLUMN + 1) {
-                    size = VCardConstants.MAX_DATA_COLUMN + 1;
-                    customPropertyList =
-                        customPropertyList.subList(0, VCardConstants.MAX_DATA_COLUMN + 2);
-                }
-
-                int i = 0;
-                for (final String customPropertyValue : customPropertyList) {
-                    if (i == 0) {
-                        final String mimeType = customPropertyValue;
-                        builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-                        builder.withValueBackReference(GroupMembership.RAW_CONTACT_ID, 0);
-                        builder.withValue(Data.MIMETYPE, mimeType);
-                    } else {  // 1 <= i && i <= MAX_DATA_COLUMNS
-                        if (!TextUtils.isEmpty(customPropertyValue)) {
-                            builder.withValue("data" + i, customPropertyValue);
-                        }
-                    }
-
-                    i++;
-                }
-                operationList.add(builder.build());
-            }
-        }
-
-        if (myGroupsId != null) {
-            builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
-            builder.withValueBackReference(GroupMembership.RAW_CONTACT_ID, 0);
-            builder.withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
-            builder.withValue(GroupMembership.GROUP_SOURCE_ID, myGroupsId);
-            operationList.add(builder.build());
-        }
-
-        try {
-            ContentProviderResult[] results = resolver.applyBatch(
-                        ContactsContract.AUTHORITY, operationList);
-            // the first result is always the raw_contact. return it's uri so
-            // that it can be found later. do null checking for badly behaving
-            // ContentResolvers
-            return (results == null || results.length == 0 || results[0] == null)
-                ? null
-                : results[0].uri;
-        } catch (RemoteException e) {
-            Log.e(LOG_TAG, String.format("%s: %s", e.toString(), e.getMessage()));
-            return null;
-        } catch (OperationApplicationException e) {
-            Log.e(LOG_TAG, String.format("%s: %s", e.toString(), e.getMessage()));
-            return null;
-        }
-    }
-
-    public static VCardEntry buildFromResolver(ContentResolver resolver) {
-        return buildFromResolver(resolver, Contacts.CONTENT_URI);
-    }
-
-    public static VCardEntry buildFromResolver(ContentResolver resolver, Uri uri) {
-
-        return null;
-    }
-
-    private boolean nameFieldsAreEmpty() {
-        return (TextUtils.isEmpty(mFamilyName)
-                && TextUtils.isEmpty(mMiddleName)
-                && TextUtils.isEmpty(mGivenName)
-                && TextUtils.isEmpty(mPrefix)
-                && TextUtils.isEmpty(mSuffix)
-                && TextUtils.isEmpty(mFormattedName)
-                && TextUtils.isEmpty(mPhoneticFamilyName)
-                && TextUtils.isEmpty(mPhoneticMiddleName)
-                && TextUtils.isEmpty(mPhoneticGivenName)
-                && TextUtils.isEmpty(mPhoneticFullName));
-    }
-
-    public boolean isIgnorable() {
-        return getDisplayName().length() == 0;
-    }
-
-    private String listToString(List<String> list){
-        final int size = list.size();
-        if (size > 1) {
-            StringBuilder builder = new StringBuilder();
-            int i = 0;
-            for (String type : list) {
-                builder.append(type);
-                if (i < size - 1) {
-                    builder.append(";");
-                }
-            }
-            return builder.toString();
-        } else if (size == 1) {
-            return list.get(0);
-        } else {
-            return "";
-        }
-    }
-
-    // All getter methods should be used carefully, since they may change
-    // in the future as of 2009-10-05, on which I cannot be sure this structure
-    // is completely consolidated.
-    //
-    // Also note that these getter methods should be used only after
-    // all properties being pushed into this object. If not, incorrect
-    // value will "be stored in the local cache and" be returned to you.
-
-    public String getFamilyName() {
-        return mFamilyName;
-    }
-
-    public String getGivenName() {
-        return mGivenName;
-    }
-
-    public String getMiddleName() {
-        return mMiddleName;
-    }
-
-    public String getPrefix() {
-        return mPrefix;
-    }
-
-    public String getSuffix() {
-        return mSuffix;
-    }
-
-    public String getFullName() {
-        return mFormattedName;
-    }
-
-    public String getPhoneticFamilyName() {
-        return mPhoneticFamilyName;
-    }
-
-    public String getPhoneticGivenName() {
-        return mPhoneticGivenName;
-    }
-
-    public String getPhoneticMiddleName() {
-        return mPhoneticMiddleName;
-    }
-
-    public String getPhoneticFullName() {
-        return mPhoneticFullName;
-    }
-
-    public final List<String> getNickNameList() {
-        return mNickNameList;
-    }
-
-    public String getBirthday() {
-        return mBirthday;
-    }
-
-    public final List<String> getNotes() {
-        return mNoteList;
-    }
-
-    public final List<PhoneData> getPhoneList() {
-        return mPhoneList;
-    }
-
-    public final List<EmailData> getEmailList() {
-        return mEmailList;
-    }
-
-    public final List<PostalData> getPostalList() {
-        return mPostalList;
-    }
-
-    public final List<OrganizationData> getOrganizationList() {
-        return mOrganizationList;
-    }
-
-    public final List<ImData> getImList() {
-        return mImList;
-    }
-
-    public final List<PhotoData> getPhotoList() {
-        return mPhotoList;
-    }
-
-    public final List<String> getWebsiteList() {
-        return mWebsiteList;
-    }
-
-    public String getDisplayName() {
-        if (mDisplayName == null) {
-            constructDisplayName();
-        }
-        return mDisplayName;
-    }
-}
diff --git a/core/java/android/pim/vcard/VCardEntryCommitter.java b/core/java/android/pim/vcard/VCardEntryCommitter.java
deleted file mode 100644
index a8c8057..0000000
--- a/core/java/android/pim/vcard/VCardEntryCommitter.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.content.ContentResolver;
-import android.net.Uri;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * <P>
- * {@link VCardEntryHandler} implementation which commits the entry to ContentResolver.
- * </P>
- * <P>
- * Note:<BR />
- * Each vCard may contain big photo images encoded by BASE64,
- * If we store all vCard entries in memory, OutOfMemoryError may be thrown.
- * Thus, this class push each VCard entry into ContentResolver immediately.
- * </P>
- */
-public class VCardEntryCommitter implements VCardEntryHandler {
-    public static String LOG_TAG = "VCardEntryComitter";
-
-    private final ContentResolver mContentResolver;
-    private long mTimeToCommit;
-    private ArrayList<Uri> mCreatedUris = new ArrayList<Uri>();
-
-    public VCardEntryCommitter(ContentResolver resolver) {
-        mContentResolver = resolver;
-    }
-
-    public void onStart() {
-    }
-
-    public void onEnd() {
-        if (VCardConfig.showPerformanceLog()) {
-            Log.d(LOG_TAG, String.format("time to commit entries: %d ms", mTimeToCommit));
-        }
-    }
-
-    public void onEntryCreated(final VCardEntry vcardEntry) {
-        long start = System.currentTimeMillis();
-        mCreatedUris.add(vcardEntry.pushIntoContentResolver(mContentResolver));
-        mTimeToCommit += System.currentTimeMillis() - start;
-    }
-
-    /**
-     * Returns the list of created Uris. This list should not be modified by the caller as it is
-     * not a clone.
-     */
-   public ArrayList<Uri> getCreatedUris() {
-        return mCreatedUris;
-    }
-}
\ No newline at end of file
diff --git a/core/java/android/pim/vcard/VCardEntryConstructor.java b/core/java/android/pim/vcard/VCardEntryConstructor.java
deleted file mode 100644
index aa3e3e2..0000000
--- a/core/java/android/pim/vcard/VCardEntryConstructor.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.accounts.Account;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.CharsetUtils;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * <p>
- * The {@link VCardInterpreter} implementation which enables {@link VCardEntryHandler} objects
- * to easily handle each vCard entry.
- * </p>
- * <p>
- * This class understand details inside vCard and translates it to {@link VCardEntry}.
- * Then the class throw it to {@link VCardEntryHandler} registered via
- * {@link #addEntryHandler(VCardEntryHandler)}, so that all those registered objects
- * are able to handle the {@link VCardEntry} object.
- * </p>
- * <p>
- * If you want to know the detail inside vCard, it would be better to implement
- * {@link VCardInterpreter} directly, instead of relying on this class and
- * {@link VCardEntry} created by the object.
- * </p>
- */
-public class VCardEntryConstructor implements VCardInterpreter {
-    private static String LOG_TAG = "VCardEntryConstructor";
-
-    private VCardEntry.Property mCurrentProperty = new VCardEntry.Property();
-    private VCardEntry mCurrentVCardEntry;
-    private String mParamType;
-    
-    // The charset using which {@link VCardInterpreter} parses the text.
-    // Each String is first decoded into binary stream with this charset, and encoded back
-    // to "target charset", which may be explicitly specified by the vCard with "CHARSET"
-    // property or implicitly mentioned by its version (e.g. vCard 3.0 recommends UTF-8).
-    private final String mSourceCharset;
-
-    private final boolean mStrictLineBreaking;
-    private final int mVCardType;
-    private final Account mAccount;
-    
-    // For measuring performance.
-    private long mTimePushIntoContentResolver;
-
-    private final List<VCardEntryHandler> mEntryHandlers = new ArrayList<VCardEntryHandler>();
-
-    public VCardEntryConstructor() {
-        this(VCardConfig.VCARD_TYPE_V21_GENERIC, null);
-    }
-
-    public VCardEntryConstructor(final int vcardType) {
-        this(vcardType, null, null, false);
-    }
-
-    public VCardEntryConstructor(final int vcardType, final Account account) {
-        this(vcardType, account, null, false);
-    }
-
-    public VCardEntryConstructor(final int vcardType, final Account account,
-            final String inputCharset) {
-        this(vcardType, account, inputCharset, false);
-    }
-
-    /**
-     * @hide Just for testing.
-     */
-    public VCardEntryConstructor(final int vcardType, final Account account,
-            final String inputCharset, final boolean strictLineBreakParsing) {
-        if (inputCharset != null) {
-            mSourceCharset = inputCharset;
-        } else {
-            mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
-        }
-        mStrictLineBreaking = strictLineBreakParsing;
-        mVCardType = vcardType;
-        mAccount = account;
-    }
-
-    public void addEntryHandler(VCardEntryHandler entryHandler) {
-        mEntryHandlers.add(entryHandler);
-    }
-
-    @Override
-    public void start() {
-        for (VCardEntryHandler entryHandler : mEntryHandlers) {
-            entryHandler.onStart();
-        }
-    }
-
-    @Override
-    public void end() {
-        for (VCardEntryHandler entryHandler : mEntryHandlers) {
-            entryHandler.onEnd();
-        }
-    }
-
-    public void clear() {
-        mCurrentVCardEntry = null;
-        mCurrentProperty = new VCardEntry.Property();
-    }
-
-    @Override
-    public void startEntry() {
-        if (mCurrentVCardEntry != null) {
-            Log.e(LOG_TAG, "Nested VCard code is not supported now.");
-        }
-        mCurrentVCardEntry = new VCardEntry(mVCardType, mAccount);
-    }
-
-    @Override
-    public void endEntry() {
-        mCurrentVCardEntry.consolidateFields();
-        for (VCardEntryHandler entryHandler : mEntryHandlers) {
-            entryHandler.onEntryCreated(mCurrentVCardEntry);
-        }
-        mCurrentVCardEntry = null;
-    }
-
-    @Override
-    public void startProperty() {
-        mCurrentProperty.clear();
-    }
-
-    @Override
-    public void endProperty() {
-        mCurrentVCardEntry.addProperty(mCurrentProperty);
-    }
-    
-    @Override
-    public void propertyName(String name) {
-        mCurrentProperty.setPropertyName(name);
-    }
-
-    @Override
-    public void propertyGroup(String group) {
-    }
-
-    @Override
-    public void propertyParamType(String type) {
-        if (mParamType != null) {
-            Log.e(LOG_TAG, "propertyParamType() is called more than once " +
-                    "before propertyParamValue() is called");
-        }
-        mParamType = type;
-    }
-
-    @Override
-    public void propertyParamValue(String value) {
-        if (mParamType == null) {
-            // From vCard 2.1 specification. vCard 3.0 formally does not allow this case.
-            mParamType = "TYPE";
-        }
-        if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) {
-            value = VCardUtils.convertStringCharset(
-                    value, mSourceCharset, VCardConfig.DEFAULT_IMPORT_CHARSET);
-        }
-        mCurrentProperty.addParameter(mParamType, value);
-        mParamType = null;
-    }
-
-    private String handleOneValue(String value,
-            String sourceCharset, String targetCharset, String encoding) {
-        // It is possible when some of multiple values are empty.
-        // e.g. N:;a;;; -> values are "", "a", "", "", and "".
-        if (TextUtils.isEmpty(value)) {
-            return "";
-        }
-
-        if (encoding != null) {
-            if (encoding.equals("BASE64") || encoding.equals("B")) {
-                mCurrentProperty.setPropertyBytes(Base64.decode(value.getBytes(), Base64.DEFAULT));
-                return value;
-            } else if (encoding.equals("QUOTED-PRINTABLE")) {
-                return VCardUtils.parseQuotedPrintable(
-                        value, mStrictLineBreaking, sourceCharset, targetCharset);
-            }
-            Log.w(LOG_TAG, "Unknown encoding. Fall back to default.");
-        }
-
-        // Just translate the charset of a given String from inputCharset to a system one. 
-        return VCardUtils.convertStringCharset(value, sourceCharset, targetCharset);
-    }
-    
-    public void propertyValues(List<String> values) {
-        if (values == null || values.isEmpty()) {
-            return;
-        }
-
-        final Collection<String> charsetCollection =
-                mCurrentProperty.getParameters(VCardConstants.PARAM_CHARSET);
-        final Collection<String> encodingCollection =
-                mCurrentProperty.getParameters(VCardConstants.PARAM_ENCODING);
-        final String encoding =
-            ((encodingCollection != null) ? encodingCollection.iterator().next() : null);
-        String targetCharset = CharsetUtils.nameForDefaultVendor(
-                ((charsetCollection != null) ? charsetCollection.iterator().next() : null));
-        if (TextUtils.isEmpty(targetCharset)) {
-            targetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET;
-        }
-
-        for (final String value : values) {
-            mCurrentProperty.addToPropertyValueList(
-                    handleOneValue(value, mSourceCharset, targetCharset, encoding));
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public void showPerformanceInfo() {
-        Log.d(LOG_TAG, "time for insert ContactStruct to database: " + 
-                mTimePushIntoContentResolver + " ms");
-    }
-}
diff --git a/core/java/android/pim/vcard/VCardEntryCounter.java b/core/java/android/pim/vcard/VCardEntryCounter.java
deleted file mode 100644
index 7bab50d..0000000
--- a/core/java/android/pim/vcard/VCardEntryCounter.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import java.util.List;
-
-/**
- * The class which just counts the number of vCard entries in the specified input.
- */
-public class VCardEntryCounter implements VCardInterpreter {
-    private int mCount;
-
-    public int getCount() {
-        return mCount;
-    }
-
-    public void start() {
-    }
-
-    public void end() {
-    }
-
-    public void startEntry() {
-    }
-
-    public void endEntry() {
-        mCount++;
-    }
-
-    public void startProperty() {
-    }
-
-    public void endProperty() {
-    }
-
-    public void propertyGroup(String group) {
-    }
-
-    public void propertyName(String name) {
-    }
-
-    public void propertyParamType(String type) {
-    }
-
-    public void propertyParamValue(String value) {
-    }
-
-    public void propertyValues(List<String> values) {
-    }    
-}
diff --git a/core/java/android/pim/vcard/VCardEntryHandler.java b/core/java/android/pim/vcard/VCardEntryHandler.java
deleted file mode 100644
index 56bf69d..0000000
--- a/core/java/android/pim/vcard/VCardEntryHandler.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-/**
- * <p>
- * The interface called by {@link VCardEntryConstructor}.
- * </p>
- * <p>
- * This class is useful when you don't want to know vCard data in detail. If you want to know
- * it, it would be better to consider using {@link VCardInterpreter}.
- * </p>
- */
-public interface VCardEntryHandler {
-    /**
-     * Called when the parsing started.
-     */
-    public void onStart();
-
-    /**
-     * The method called when one VCard entry is successfully created
-     */
-    public void onEntryCreated(final VCardEntry entry);
-
-    /**
-     * Called when the parsing ended.
-     * Able to be use this method for showing performance log, etc.
-     */
-    public void onEnd();
-}
diff --git a/core/java/android/pim/vcard/VCardInterpreter.java b/core/java/android/pim/vcard/VCardInterpreter.java
deleted file mode 100644
index 03704a2..0000000
--- a/core/java/android/pim/vcard/VCardInterpreter.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import java.util.List;
-
-/**
- * <P>
- * The interface which should be implemented by the classes which have to analyze each
- * vCard entry minutely.
- * </P>
- * <P>
- * Here, there are several terms specific to vCard (and this library).
- * </P>
- * <P>
- * The term "entry" is one vCard representation in the input, which should start with "BEGIN:VCARD"
- * and end with "END:VCARD".
- * </P>
- * <P>
- * The term "property" is one line in vCard entry, which consists of "group", "property name",
- * "parameter(param) names and values", and "property values".
- * </P>
- * <P>
- * e.g. group1.propName;paramName1=paramValue1;paramName2=paramValue2;propertyValue1;propertyValue2...
- * </P>
- */
-public interface VCardInterpreter {
-    /**
-     * Called when vCard interpretation started.
-     */
-    void start();
-
-    /**
-     * Called when vCard interpretation finished.
-     */
-    void end();
-
-    /** 
-     * Called when parsing one vCard entry started.
-     * More specifically, this method is called when "BEGIN:VCARD" is read.
-     */
-    void startEntry();
-
-    /**
-     * Called when parsing one vCard entry ended.
-     * More specifically, this method is called when "END:VCARD" is read.
-     * Note that {@link #startEntry()} may be called since
-     * vCard (especially 2.1) allows nested vCard.
-     */
-    void endEntry();
-
-    /**
-     * Called when reading one property started.
-     */
-    void startProperty();
-
-    /**
-     * Called when reading one property ended.
-     */
-    void endProperty();
-
-    /**
-     * @param group A group name. This method may be called more than once or may not be
-     * called at all, depending on how many gruoups are appended to the property.
-     */
-    void propertyGroup(String group);
-
-    /**
-     * @param name A property name like "N", "FN", "ADR", etc.
-     */
-    void propertyName(String name);
-
-    /**
-     * @param type A parameter name like "ENCODING", "CHARSET", etc.
-     */
-    void propertyParamType(String type);
-
-    /**
-     * @param value A parameter value. This method may be called without
-     * {@link #propertyParamType(String)} being called (when the vCard is vCard 2.1).
-     */
-    void propertyParamValue(String value);
-
-    /**
-     * @param values List of property values. The size of values would be 1 unless
-     * coressponding property name is "N", "ADR", or "ORG".
-     */
-    void propertyValues(List<String> values);
-}
diff --git a/core/java/android/pim/vcard/VCardInterpreterCollection.java b/core/java/android/pim/vcard/VCardInterpreterCollection.java
deleted file mode 100644
index 77e44a0..0000000
--- a/core/java/android/pim/vcard/VCardInterpreterCollection.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import java.util.Collection;
-import java.util.List;
-
-/**
- * The {@link VCardInterpreter} implementation which aggregates more than one
- * {@link VCardInterpreter} objects and make a user object treat them as one
- * {@link VCardInterpreter} object.
- */
-public final class VCardInterpreterCollection implements VCardInterpreter {
-    private final Collection<VCardInterpreter> mInterpreterCollection;
-
-    public VCardInterpreterCollection(Collection<VCardInterpreter> interpreterCollection) {
-        mInterpreterCollection = interpreterCollection;
-    }
-
-    public Collection<VCardInterpreter> getCollection() {
-        return mInterpreterCollection;
-    }
-
-    public void start() {
-        for (VCardInterpreter builder : mInterpreterCollection) {
-            builder.start();
-        }
-    }
-
-    public void end() {
-        for (VCardInterpreter builder : mInterpreterCollection) {
-            builder.end();
-        }
-    }
-
-    public void startEntry() {
-        for (VCardInterpreter builder : mInterpreterCollection) {
-            builder.startEntry();
-        }
-    }
-
-    public void endEntry() {
-        for (VCardInterpreter builder : mInterpreterCollection) {
-            builder.endEntry();
-        }
-    }
-
-    public void startProperty() {
-        for (VCardInterpreter builder : mInterpreterCollection) {
-            builder.startProperty();
-        }
-    }
-
-    public void endProperty() {
-        for (VCardInterpreter builder : mInterpreterCollection) {
-            builder.endProperty();
-        }
-    }
-
-    public void propertyGroup(String group) {
-        for (VCardInterpreter builder : mInterpreterCollection) {
-            builder.propertyGroup(group);
-        }
-    }
-
-    public void propertyName(String name) {
-        for (VCardInterpreter builder : mInterpreterCollection) {
-            builder.propertyName(name);
-        }
-    }
-
-    public void propertyParamType(String type) {
-        for (VCardInterpreter builder : mInterpreterCollection) {
-            builder.propertyParamType(type);
-        }
-    }
-
-    public void propertyParamValue(String value) {
-        for (VCardInterpreter builder : mInterpreterCollection) {
-            builder.propertyParamValue(value);
-        }
-    }
-
-    public void propertyValues(List<String> values) {
-        for (VCardInterpreter builder : mInterpreterCollection) {
-            builder.propertyValues(values);
-        }
-    }
-}
diff --git a/core/java/android/pim/vcard/VCardParser.java b/core/java/android/pim/vcard/VCardParser.java
deleted file mode 100644
index 31b9369..0000000
--- a/core/java/android/pim/vcard/VCardParser.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.pim.vcard.exception.VCardException;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public interface VCardParser {
-    /**
-     * <p>
-     * Parses the given stream and send the vCard data into VCardBuilderBase object.
-     * </p>.
-     * <p>
-     * Note that vCard 2.1 specification allows "CHARSET" parameter, and some career sets
-     * local encoding to it. For example, Japanese phone career uses Shift_JIS, which is
-     * formally allowed in vCard 2.1, but not allowed in vCard 3.0. In vCard 2.1,
-     * In some exreme case, it is allowed for vCard to have different charsets in one vCard.
-     * </p>
-     * <p>
-     * We recommend you use {@link VCardSourceDetector} and detect which kind of source the
-     * vCard comes from and explicitly specify a charset using the result.
-     * </p>
-     *
-     * @param is The source to parse.
-     * @param interepreter A {@link VCardInterpreter} object which used to construct data.
-     * @throws IOException, VCardException
-     */
-    public void parse(InputStream is, VCardInterpreter interepreter)
-            throws IOException, VCardException;
-
-    /**
-     * <p>
-     * Cancel parsing vCard. Useful when you want to stop the parse in the other threads.
-     * </p>
-     * <p>
-     * Actual cancel is done after parsing the current vcard.
-     * </p>
-     */
-    public abstract void cancel();
-}
diff --git a/core/java/android/pim/vcard/VCardParserImpl_V21.java b/core/java/android/pim/vcard/VCardParserImpl_V21.java
deleted file mode 100644
index 8b365eb..0000000
--- a/core/java/android/pim/vcard/VCardParserImpl_V21.java
+++ /dev/null
@@ -1,1046 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.pim.vcard.exception.VCardAgentNotSupportedException;
-import android.pim.vcard.exception.VCardException;
-import android.pim.vcard.exception.VCardInvalidCommentLineException;
-import android.pim.vcard.exception.VCardInvalidLineException;
-import android.pim.vcard.exception.VCardNestedException;
-import android.pim.vcard.exception.VCardVersionException;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * <p>
- * Basic implementation achieving vCard parsing. Based on vCard 2.1,
- * </p>
- * @hide
- */
-/* package */ class VCardParserImpl_V21 {
-    private static final String LOG_TAG = "VCardParserImpl_V21";
-
-    private static final class EmptyInterpreter implements VCardInterpreter {
-        @Override
-        public void end() {
-        }
-        @Override
-        public void endEntry() {
-        }
-        @Override
-        public void endProperty() {
-        }
-        @Override
-        public void propertyGroup(String group) {
-        }
-        @Override
-        public void propertyName(String name) {
-        }
-        @Override
-        public void propertyParamType(String type) {
-        }
-        @Override
-        public void propertyParamValue(String value) {
-        }
-        @Override
-        public void propertyValues(List<String> values) {
-        }
-        @Override
-        public void start() {
-        }
-        @Override
-        public void startEntry() {
-        }
-        @Override
-        public void startProperty() {
-        }
-    }
-
-    protected static final class CustomBufferedReader extends BufferedReader {
-        private long mTime;
-
-        /**
-         * Needed since "next line" may be null due to end of line.
-         */
-        private boolean mNextLineIsValid;
-        private String mNextLine;
-
-        public CustomBufferedReader(Reader in) {
-            super(in);
-        }
-
-        @Override
-        public String readLine() throws IOException {
-            if (mNextLineIsValid) {
-                final String ret = mNextLine;
-                mNextLine = null;
-                mNextLineIsValid = false;
-                return ret;
-            }
-
-            long start = System.currentTimeMillis();
-            final String line = super.readLine();
-            long end = System.currentTimeMillis();
-            mTime += end - start;
-            return line;
-        }
-
-        /**
-         * Read one line, but make this object store it in its queue.
-         */
-        public String peekLine() throws IOException {
-            if (!mNextLineIsValid) {
-                long start = System.currentTimeMillis();
-                final String line = super.readLine();
-                long end = System.currentTimeMillis();
-                mTime += end - start;
-
-                mNextLine = line;
-                mNextLineIsValid = true;
-            }
-
-            return mNextLine;
-        }
-
-        public long getTotalmillisecond() {
-            return mTime;
-        }
-    }
-
-    private static final String DEFAULT_ENCODING = "8BIT";
-
-    protected boolean mCanceled;
-    protected VCardInterpreter mInterpreter;
-
-    protected final String mIntermediateCharset;
-
-    /**
-     * <p>
-     * The encoding type for deconding byte streams. This member variable is
-     * reset to a default encoding every time when a new item comes.
-     * </p>
-     * <p>
-     * "Encoding" in vCard is different from "Charset". It is mainly used for
-     * addresses, notes, images. "7BIT", "8BIT", "BASE64", and
-     * "QUOTED-PRINTABLE" are known examples.
-     * </p>
-     */
-    protected String mCurrentEncoding;
-
-    /**
-     * <p>
-     * The reader object to be used internally.
-     * </p>
-     * <p>
-     * Developers should not directly read a line from this object. Use
-     * getLine() unless there some reason.
-     * </p>
-     */
-    protected CustomBufferedReader mReader;
-
-    /**
-     * <p>
-     * Set for storing unkonwn TYPE attributes, which is not acceptable in vCard
-     * specification, but happens to be seen in real world vCard.
-     * </p>
-     */
-    protected final Set<String> mUnknownTypeSet = new HashSet<String>();
-
-    /**
-     * <p>
-     * Set for storing unkonwn VALUE attributes, which is not acceptable in
-     * vCard specification, but happens to be seen in real world vCard.
-     * </p>
-     */
-    protected final Set<String> mUnknownValueSet = new HashSet<String>();
-
-
-    // In some cases, vCard is nested. Currently, we only consider the most
-    // interior vCard data.
-    // See v21_foma_1.vcf in test directory for more information.
-    // TODO: Don't ignore by using count, but read all of information outside vCard.
-    private int mNestCount;
-
-    // Used only for parsing END:VCARD.
-    private String mPreviousLine;
-
-    // For measuring performance.
-    private long mTimeTotal;
-    private long mTimeReadStartRecord;
-    private long mTimeReadEndRecord;
-    private long mTimeStartProperty;
-    private long mTimeEndProperty;
-    private long mTimeParseItems;
-    private long mTimeParseLineAndHandleGroup;
-    private long mTimeParsePropertyValues;
-    private long mTimeParseAdrOrgN;
-    private long mTimeHandleMiscPropertyValue;
-    private long mTimeHandleQuotedPrintable;
-    private long mTimeHandleBase64;
-
-    public VCardParserImpl_V21() {
-        this(VCardConfig.VCARD_TYPE_DEFAULT);
-    }
-
-    public VCardParserImpl_V21(int vcardType) {
-        if ((vcardType & VCardConfig.FLAG_TORELATE_NEST) != 0) {
-            mNestCount = 1;
-        }
-
-        mIntermediateCharset =  VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
-    }
-
-    /**
-     * <p>
-     * Parses the file at the given position.
-     * </p>
-     */
-    // <pre class="prettyprint">vcard_file = [wsls] vcard [wsls]</pre>
-    protected void parseVCardFile() throws IOException, VCardException {
-        boolean readingFirstFile = true;
-        while (true) {
-            if (mCanceled) {
-                break;
-            }
-            if (!parseOneVCard(readingFirstFile)) {
-                break;
-            }
-            readingFirstFile = false;
-        }
-
-        if (mNestCount > 0) {
-            boolean useCache = true;
-            for (int i = 0; i < mNestCount; i++) {
-                readEndVCard(useCache, true);
-                useCache = false;
-            }
-        }
-    }
-
-    /**
-     * @return true when a given property name is a valid property name.
-     */
-    protected boolean isValidPropertyName(final String propertyName) {
-        if (!(getKnownPropertyNameSet().contains(propertyName.toUpperCase()) ||
-                propertyName.startsWith("X-"))
-                && !mUnknownTypeSet.contains(propertyName)) {
-            mUnknownTypeSet.add(propertyName);
-            Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName);
-        }
-        return true;
-    }
-
-    /**
-     * @return String. It may be null, or its length may be 0
-     * @throws IOException
-     */
-    protected String getLine() throws IOException {
-        return mReader.readLine();
-    }
-
-    protected String peekLine() throws IOException {
-        return mReader.peekLine();
-    }
-
-    /**
-     * @return String with it's length > 0
-     * @throws IOException
-     * @throws VCardException when the stream reached end of line
-     */
-    protected String getNonEmptyLine() throws IOException, VCardException {
-        String line;
-        while (true) {
-            line = getLine();
-            if (line == null) {
-                throw new VCardException("Reached end of buffer.");
-            } else if (line.trim().length() > 0) {
-                return line;
-            }
-        }
-    }
-
-    /*
-     * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
-     *         items *CRLF
-     *         "END" [ws] ":" [ws] "VCARD"
-     */
-    private boolean parseOneVCard(boolean firstRead) throws IOException, VCardException {
-        boolean allowGarbage = false;
-        if (firstRead) {
-            if (mNestCount > 0) {
-                for (int i = 0; i < mNestCount; i++) {
-                    if (!readBeginVCard(allowGarbage)) {
-                        return false;
-                    }
-                    allowGarbage = true;
-                }
-            }
-        }
-
-        if (!readBeginVCard(allowGarbage)) {
-            return false;
-        }
-        final long beforeStartEntry = System.currentTimeMillis();
-        mInterpreter.startEntry();
-        mTimeReadStartRecord += System.currentTimeMillis() - beforeStartEntry;
-
-        final long beforeParseItems = System.currentTimeMillis();
-        parseItems();
-        mTimeParseItems += System.currentTimeMillis() - beforeParseItems;
-
-        readEndVCard(true, false);
-
-        final long beforeEndEntry = System.currentTimeMillis();
-        mInterpreter.endEntry();
-        mTimeReadEndRecord += System.currentTimeMillis() - beforeEndEntry;
-        return true;
-    }
-
-    /**
-     * @return True when successful. False when reaching the end of line
-     * @throws IOException
-     * @throws VCardException
-     */
-    protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
-        String line;
-        do {
-            while (true) {
-                line = getLine();
-                if (line == null) {
-                    return false;
-                } else if (line.trim().length() > 0) {
-                    break;
-                }
-            }
-            final String[] strArray = line.split(":", 2);
-            final int length = strArray.length;
-
-            // Although vCard 2.1/3.0 specification does not allow lower cases,
-            // we found vCard file emitted by some external vCard expoter have such
-            // invalid Strings.
-            // So we allow it.
-            // e.g.
-            // BEGIN:vCard
-            if (length == 2 && strArray[0].trim().equalsIgnoreCase("BEGIN")
-                    && strArray[1].trim().equalsIgnoreCase("VCARD")) {
-                return true;
-            } else if (!allowGarbage) {
-                if (mNestCount > 0) {
-                    mPreviousLine = line;
-                    return false;
-                } else {
-                    throw new VCardException("Expected String \"BEGIN:VCARD\" did not come "
-                            + "(Instead, \"" + line + "\" came)");
-                }
-            }
-        } while (allowGarbage);
-
-        throw new VCardException("Reached where must not be reached.");
-    }
-
-    /**
-     * <p>
-     * The arguments useCache and allowGarbase are usually true and false
-     * accordingly when this function is called outside this function itself.
-     * </p>
-     * 
-     * @param useCache When true, line is obtained from mPreviousline.
-     *            Otherwise, getLine() is used.
-     * @param allowGarbage When true, ignore non "END:VCARD" line.
-     * @throws IOException
-     * @throws VCardException
-     */
-    protected void readEndVCard(boolean useCache, boolean allowGarbage) throws IOException,
-            VCardException {
-        String line;
-        do {
-            if (useCache) {
-                // Though vCard specification does not allow lower cases,
-                // some data may have them, so we allow it.
-                line = mPreviousLine;
-            } else {
-                while (true) {
-                    line = getLine();
-                    if (line == null) {
-                        throw new VCardException("Expected END:VCARD was not found.");
-                    } else if (line.trim().length() > 0) {
-                        break;
-                    }
-                }
-            }
-
-            String[] strArray = line.split(":", 2);
-            if (strArray.length == 2 && strArray[0].trim().equalsIgnoreCase("END")
-                    && strArray[1].trim().equalsIgnoreCase("VCARD")) {
-                return;
-            } else if (!allowGarbage) {
-                throw new VCardException("END:VCARD != \"" + mPreviousLine + "\"");
-            }
-            useCache = false;
-        } while (allowGarbage);
-    }
-
-    /*
-     * items = *CRLF item / item
-     */
-    protected void parseItems() throws IOException, VCardException {
-        boolean ended = false;
-
-        final long beforeBeginProperty = System.currentTimeMillis();
-        mInterpreter.startProperty();
-        mTimeStartProperty += System.currentTimeMillis() - beforeBeginProperty;
-        ended = parseItem();
-        if (!ended) {
-            final long beforeEndProperty = System.currentTimeMillis();
-            mInterpreter.endProperty();
-            mTimeEndProperty += System.currentTimeMillis() - beforeEndProperty;
-        }
-
-        while (!ended) {
-            final long beforeStartProperty = System.currentTimeMillis();
-            mInterpreter.startProperty();
-            mTimeStartProperty += System.currentTimeMillis() - beforeStartProperty;
-            try {
-                ended = parseItem();
-            } catch (VCardInvalidCommentLineException e) {
-                Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored.");
-                ended = false;
-            }
-
-            if (!ended) {
-                final long beforeEndProperty = System.currentTimeMillis();
-                mInterpreter.endProperty();
-                mTimeEndProperty += System.currentTimeMillis() - beforeEndProperty;
-            }
-        }
-    }
-
-    /*
-     * item = [groups "."] name [params] ":" value CRLF / [groups "."] "ADR"
-     * [params] ":" addressparts CRLF / [groups "."] "ORG" [params] ":" orgparts
-     * CRLF / [groups "."] "N" [params] ":" nameparts CRLF / [groups "."]
-     * "AGENT" [params] ":" vcard CRLF
-     */
-    protected boolean parseItem() throws IOException, VCardException {
-        mCurrentEncoding = DEFAULT_ENCODING;
-
-        final String line = getNonEmptyLine();
-        long start = System.currentTimeMillis();
-
-        String[] propertyNameAndValue = separateLineAndHandleGroup(line);
-        if (propertyNameAndValue == null) {
-            return true;
-        }
-        if (propertyNameAndValue.length != 2) {
-            throw new VCardInvalidLineException("Invalid line \"" + line + "\"");
-        }
-        String propertyName = propertyNameAndValue[0].toUpperCase();
-        String propertyValue = propertyNameAndValue[1];
-
-        mTimeParseLineAndHandleGroup += System.currentTimeMillis() - start;
-
-        if (propertyName.equals("ADR") || propertyName.equals("ORG") || propertyName.equals("N")) {
-            start = System.currentTimeMillis();
-            handleMultiplePropertyValue(propertyName, propertyValue);
-            mTimeParseAdrOrgN += System.currentTimeMillis() - start;
-            return false;
-        } else if (propertyName.equals("AGENT")) {
-            handleAgent(propertyValue);
-            return false;
-        } else if (isValidPropertyName(propertyName)) {
-            if (propertyName.equals("BEGIN")) {
-                if (propertyValue.equals("VCARD")) {
-                    throw new VCardNestedException("This vCard has nested vCard data in it.");
-                } else {
-                    throw new VCardException("Unknown BEGIN type: " + propertyValue);
-                }
-            } else if (propertyName.equals("VERSION") && !propertyValue.equals(getVersionString())) {
-                throw new VCardVersionException("Incompatible version: " + propertyValue + " != "
-                        + getVersionString());
-            }
-            start = System.currentTimeMillis();
-            handlePropertyValue(propertyName, propertyValue);
-            mTimeParsePropertyValues += System.currentTimeMillis() - start;
-            return false;
-        }
-
-        throw new VCardException("Unknown property name: \"" + propertyName + "\"");
-    }
-
-    // For performance reason, the states for group and property name are merged into one.
-    static private final int STATE_GROUP_OR_PROPERTY_NAME = 0;
-    static private final int STATE_PARAMS = 1;
-    // vCard 3.0 specification allows double-quoted parameters, while vCard 2.1 does not.
-    static private final int STATE_PARAMS_IN_DQUOTE = 2;
-
-    protected String[] separateLineAndHandleGroup(String line) throws VCardException {
-        final String[] propertyNameAndValue = new String[2];
-        final int length = line.length();
-        if (length > 0 && line.charAt(0) == '#') {
-            throw new VCardInvalidCommentLineException();
-        }
-
-        int state = STATE_GROUP_OR_PROPERTY_NAME;
-        int nameIndex = 0;
-
-        // This loop is developed so that we don't have to take care of bottle neck here.
-        // Refactor carefully when you need to do so.
-        for (int i = 0; i < length; i++) {
-            final char ch = line.charAt(i);
-            switch (state) {
-                case STATE_GROUP_OR_PROPERTY_NAME: {
-                    if (ch == ':') {  // End of a property name.
-                        final String propertyName = line.substring(nameIndex, i);
-                        if (propertyName.equalsIgnoreCase("END")) {
-                            mPreviousLine = line;
-                            return null;
-                        }
-                        mInterpreter.propertyName(propertyName);
-                        propertyNameAndValue[0] = propertyName;
-                        if (i < length - 1) {
-                            propertyNameAndValue[1] = line.substring(i + 1);
-                        } else {
-                            propertyNameAndValue[1] = "";
-                        }
-                        return propertyNameAndValue;
-                    } else if (ch == '.') {  // Each group is followed by the dot.
-                        final String groupName = line.substring(nameIndex, i);
-                        if (groupName.length() == 0) {
-                            Log.w(LOG_TAG, "Empty group found. Ignoring.");
-                        } else {
-                            mInterpreter.propertyGroup(groupName);
-                        }
-                        nameIndex = i + 1;  // Next should be another group or a property name.
-                    } else if (ch == ';') {  // End of property name and beginneng of parameters.  
-                        final String propertyName = line.substring(nameIndex, i);
-                        if (propertyName.equalsIgnoreCase("END")) {
-                            mPreviousLine = line;
-                            return null;
-                        }
-                        mInterpreter.propertyName(propertyName);
-                        propertyNameAndValue[0] = propertyName;
-                        nameIndex = i + 1;
-                        state = STATE_PARAMS;  // Start parameter parsing.
-                    }
-                    // TODO: comma support (in vCard 3.0 and 4.0).
-                    break;
-                }
-                case STATE_PARAMS: {
-                    if (ch == '"') {
-                        if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) {
-                            Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " +
-                                    "Silently allow it");
-                        }
-                        state = STATE_PARAMS_IN_DQUOTE;
-                    } else if (ch == ';') {  // Starts another param.
-                        handleParams(line.substring(nameIndex, i));
-                        nameIndex = i + 1;
-                    } else if (ch == ':') {  // End of param and beginenning of values.
-                        handleParams(line.substring(nameIndex, i));
-                        if (i < length - 1) {
-                            propertyNameAndValue[1] = line.substring(i + 1);
-                        } else {
-                            propertyNameAndValue[1] = "";
-                        }
-                        return propertyNameAndValue;
-                    }
-                    break;
-                }
-                case STATE_PARAMS_IN_DQUOTE: {
-                    if (ch == '"') {
-                        if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) {
-                            Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " +
-                                    "Silently allow it");
-                        }
-                        state = STATE_PARAMS;
-                    }
-                    break;
-                }
-            }
-        }
-
-        throw new VCardInvalidLineException("Invalid line: \"" + line + "\"");
-    }
-
-    /*
-     * params = ";" [ws] paramlist paramlist = paramlist [ws] ";" [ws] param /
-     * param param = "TYPE" [ws] "=" [ws] ptypeval / "VALUE" [ws] "=" [ws]
-     * pvalueval / "ENCODING" [ws] "=" [ws] pencodingval / "CHARSET" [ws] "="
-     * [ws] charsetval / "LANGUAGE" [ws] "=" [ws] langval / "X-" word [ws] "="
-     * [ws] word / knowntype
-     */
-    protected void handleParams(String params) throws VCardException {
-        final String[] strArray = params.split("=", 2);
-        if (strArray.length == 2) {
-            final String paramName = strArray[0].trim().toUpperCase();
-            String paramValue = strArray[1].trim();
-            if (paramName.equals("TYPE")) {
-                handleType(paramValue);
-            } else if (paramName.equals("VALUE")) {
-                handleValue(paramValue);
-            } else if (paramName.equals("ENCODING")) {
-                handleEncoding(paramValue);
-            } else if (paramName.equals("CHARSET")) {
-                handleCharset(paramValue);
-            } else if (paramName.equals("LANGUAGE")) {
-                handleLanguage(paramValue);
-            } else if (paramName.startsWith("X-")) {
-                handleAnyParam(paramName, paramValue);
-            } else {
-                throw new VCardException("Unknown type \"" + paramName + "\"");
-            }
-        } else {
-            handleParamWithoutName(strArray[0]);
-        }
-    }
-
-    /**
-     * vCard 3.0 parser implementation may throw VCardException.
-     */
-    @SuppressWarnings("unused")
-    protected void handleParamWithoutName(final String paramValue) throws VCardException {
-        handleType(paramValue);
-    }
-
-    /*
-     * ptypeval = knowntype / "X-" word
-     */
-    protected void handleType(final String ptypeval) {
-        if (!(getKnownTypeSet().contains(ptypeval.toUpperCase())
-                || ptypeval.startsWith("X-"))
-                && !mUnknownTypeSet.contains(ptypeval)) {
-            mUnknownTypeSet.add(ptypeval);
-            Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), ptypeval));
-        }
-        mInterpreter.propertyParamType("TYPE");
-        mInterpreter.propertyParamValue(ptypeval);
-    }
-
-    /*
-     * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
-     */
-    protected void handleValue(final String pvalueval) {
-        if (!(getKnownValueSet().contains(pvalueval.toUpperCase())
-                || pvalueval.startsWith("X-")
-                || mUnknownValueSet.contains(pvalueval))) {
-            mUnknownValueSet.add(pvalueval);
-            Log.w(LOG_TAG, String.format(
-                    "The value unsupported by TYPE of %s: ", getVersion(), pvalueval));
-        }
-        mInterpreter.propertyParamType("VALUE");
-        mInterpreter.propertyParamValue(pvalueval);
-    }
-
-    /*
-     * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word
-     */
-    protected void handleEncoding(String pencodingval) throws VCardException {
-        if (getAvailableEncodingSet().contains(pencodingval) ||
-                pencodingval.startsWith("X-")) {
-            mInterpreter.propertyParamType("ENCODING");
-            mInterpreter.propertyParamValue(pencodingval);
-            mCurrentEncoding = pencodingval;
-        } else {
-            throw new VCardException("Unknown encoding \"" + pencodingval + "\"");
-        }
-    }
-
-    /**
-     * <p>
-     * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521),
-     * but recent vCard files often contain other charset like UTF-8, SHIFT_JIS, etc.
-     * We allow any charset.
-     * </p>
-     */
-    protected void handleCharset(String charsetval) {
-        mInterpreter.propertyParamType("CHARSET");
-        mInterpreter.propertyParamValue(charsetval);
-    }
-
-    /**
-     * See also Section 7.1 of RFC 1521
-     */
-    protected void handleLanguage(String langval) throws VCardException {
-        String[] strArray = langval.split("-");
-        if (strArray.length != 2) {
-            throw new VCardException("Invalid Language: \"" + langval + "\"");
-        }
-        String tmp = strArray[0];
-        int length = tmp.length();
-        for (int i = 0; i < length; i++) {
-            if (!isAsciiLetter(tmp.charAt(i))) {
-                throw new VCardException("Invalid Language: \"" + langval + "\"");
-            }
-        }
-        tmp = strArray[1];
-        length = tmp.length();
-        for (int i = 0; i < length; i++) {
-            if (!isAsciiLetter(tmp.charAt(i))) {
-                throw new VCardException("Invalid Language: \"" + langval + "\"");
-            }
-        }
-        mInterpreter.propertyParamType(VCardConstants.PARAM_LANGUAGE);
-        mInterpreter.propertyParamValue(langval);
-    }
-
-    private boolean isAsciiLetter(char ch) {
-        if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Mainly for "X-" type. This accepts any kind of type without check.
-     */
-    protected void handleAnyParam(String paramName, String paramValue) {
-        mInterpreter.propertyParamType(paramName);
-        mInterpreter.propertyParamValue(paramValue);
-    }
-
-    protected void handlePropertyValue(String propertyName, String propertyValue)
-            throws IOException, VCardException {
-        final String upperEncoding = mCurrentEncoding.toUpperCase();
-        if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) {
-            final long start = System.currentTimeMillis();
-            final String result = getQuotedPrintable(propertyValue);
-            final ArrayList<String> v = new ArrayList<String>();
-            v.add(result);
-            mInterpreter.propertyValues(v);
-            mTimeHandleQuotedPrintable += System.currentTimeMillis() - start;
-        } else if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_BASE64)
-                || upperEncoding.equals(VCardConstants.PARAM_ENCODING_B)) {
-            final long start = System.currentTimeMillis();
-            // It is very rare, but some BASE64 data may be so big that
-            // OutOfMemoryError occurs. To ignore such cases, use try-catch.
-            try {
-                final ArrayList<String> arrayList = new ArrayList<String>();
-                arrayList.add(getBase64(propertyValue));
-                mInterpreter.propertyValues(arrayList);
-            } catch (OutOfMemoryError error) {
-                Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!");
-                mInterpreter.propertyValues(null);
-            }
-            mTimeHandleBase64 += System.currentTimeMillis() - start;
-        } else {
-            if (!(upperEncoding.equals("7BIT") || upperEncoding.equals("8BIT") ||
-                    upperEncoding.startsWith("X-"))) {
-                Log.w(LOG_TAG,
-                        String.format("The encoding \"%s\" is unsupported by vCard %s",
-                                mCurrentEncoding, getVersionString()));
-            }
-
-            // Some device uses line folding defined in RFC 2425, which is not allowed
-            // in vCard 2.1 (while needed in vCard 3.0).
-            //
-            // e.g.
-            // BEGIN:VCARD
-            // VERSION:2.1
-            // N:;Omega;;;
-            // EMAIL;INTERNET:"Omega"
-            //   <omega@example.com>
-            // FN:Omega
-            // END:VCARD
-            //
-            // The vCard above assumes that email address should become:
-            // "Omega" <omega@example.com>
-            //
-            // But vCard 2.1 requires Quote-Printable when a line contains line break(s).
-            //
-            // For more information about line folding,
-            // see "5.8.1. Line delimiting and folding" in RFC 2425.
-            //
-            // We take care of this case more formally in vCard 3.0, so we only need to
-            // do this in vCard 2.1.
-            if (getVersion() == VCardConfig.VERSION_21) {
-                StringBuilder builder = null;
-                while (true) {
-                    final String nextLine = peekLine();
-                    // We don't need to care too much about this exceptional case,
-                    // but we should not wrongly eat up "END:VCARD", since it critically
-                    // breaks this parser's state machine.
-                    // Thus we roughly look over the next line and confirm it is at least not
-                    // "END:VCARD". This extra fee is worth paying. This is exceptional
-                    // anyway.
-                    if (!TextUtils.isEmpty(nextLine) &&
-                            nextLine.charAt(0) == ' ' &&
-                            !"END:VCARD".contains(nextLine.toUpperCase())) {
-                        getLine();  // Drop the next line.
-
-                        if (builder == null) {
-                            builder = new StringBuilder();
-                            builder.append(propertyValue);
-                        }
-                        builder.append(nextLine.substring(1));
-                    } else {
-                        break;
-                    }
-                }
-                if (builder != null) {
-                    propertyValue = builder.toString();
-                }
-            }
-
-            final long start = System.currentTimeMillis();
-            ArrayList<String> v = new ArrayList<String>();
-            v.add(maybeUnescapeText(propertyValue));
-            mInterpreter.propertyValues(v);
-            mTimeHandleMiscPropertyValue += System.currentTimeMillis() - start;
-        }
-    }
-
-    /**
-     * <p>
-     * Parses and returns Quoted-Printable.
-     * </p>
-     *
-     * @param firstString The string following a parameter name and attributes.
-     *            Example: "string" in
-     *            "ADR:ENCODING=QUOTED-PRINTABLE:string\n\r".
-     * @return whole Quoted-Printable string, including a given argument and
-     *         following lines. Excludes the last empty line following to Quoted
-     *         Printable lines.
-     * @throws IOException
-     * @throws VCardException
-     */
-    private String getQuotedPrintable(String firstString) throws IOException, VCardException {
-        // Specifically, there may be some padding between = and CRLF.
-        // See the following:
-        //
-        // qp-line := *(qp-segment transport-padding CRLF)
-        // qp-part transport-padding
-        // qp-segment := qp-section *(SPACE / TAB) "="
-        // ; Maximum length of 76 characters
-        //
-        // e.g. (from RFC 2045)
-        // Now's the time =
-        // for all folk to come=
-        // to the aid of their country.
-        if (firstString.trim().endsWith("=")) {
-            // remove "transport-padding"
-            int pos = firstString.length() - 1;
-            while (firstString.charAt(pos) != '=') {
-            }
-            StringBuilder builder = new StringBuilder();
-            builder.append(firstString.substring(0, pos + 1));
-            builder.append("\r\n");
-            String line;
-            while (true) {
-                line = getLine();
-                if (line == null) {
-                    throw new VCardException("File ended during parsing a Quoted-Printable String");
-                }
-                if (line.trim().endsWith("=")) {
-                    // remove "transport-padding"
-                    pos = line.length() - 1;
-                    while (line.charAt(pos) != '=') {
-                    }
-                    builder.append(line.substring(0, pos + 1));
-                    builder.append("\r\n");
-                } else {
-                    builder.append(line);
-                    break;
-                }
-            }
-            return builder.toString();
-        } else {
-            return firstString;
-        }
-    }
-
-    protected String getBase64(String firstString) throws IOException, VCardException {
-        StringBuilder builder = new StringBuilder();
-        builder.append(firstString);
-
-        while (true) {
-            String line = getLine();
-            if (line == null) {
-                throw new VCardException("File ended during parsing BASE64 binary");
-            }
-            if (line.length() == 0) {
-                break;
-            }
-            builder.append(line);
-        }
-
-        return builder.toString();
-    }
-
-    /**
-     * <p>
-     * Mainly for "ADR", "ORG", and "N"
-     * </p>
-     */
-    /*
-     * addressparts = 0*6(strnosemi ";") strnosemi ; PO Box, Extended Addr,
-     * Street, Locality, Region, Postal Code, Country Name orgparts =
-     * *(strnosemi ";") strnosemi ; First is Organization Name, remainder are
-     * Organization Units. nameparts = 0*4(strnosemi ";") strnosemi ; Family,
-     * Given, Middle, Prefix, Suffix. ; Example:Public;John;Q.;Reverend Dr.;III,
-     * Esq. strnosemi = *(*nonsemi ("\;" / "\" CRLF)) *nonsemi ; To include a
-     * semicolon in this string, it must be escaped ; with a "\" character. We
-     * do not care the number of "strnosemi" here. We are not sure whether we
-     * should add "\" CRLF to each value. We exclude them for now.
-     */
-    protected void handleMultiplePropertyValue(String propertyName, String propertyValue)
-            throws IOException, VCardException {
-        // vCard 2.1 does not allow QUOTED-PRINTABLE here, but some
-        // softwares/devices
-        // emit such data.
-        if (mCurrentEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
-            propertyValue = getQuotedPrintable(propertyValue);
-        }
-
-        mInterpreter.propertyValues(VCardUtils.constructListFromValue(propertyValue,
-                getVersion()));
-    }
-
-    /*
-     * vCard 2.1 specifies AGENT allows one vcard entry. Currently we emit an
-     * error toward the AGENT property.
-     * // TODO: Support AGENT property.
-     * item =
-     * ... / [groups "."] "AGENT" [params] ":" vcard CRLF vcard = "BEGIN" [ws]
-     * ":" [ws] "VCARD" [ws] 1*CRLF items *CRLF "END" [ws] ":" [ws] "VCARD"
-     */
-    protected void handleAgent(final String propertyValue) throws VCardException {
-        if (!propertyValue.toUpperCase().contains("BEGIN:VCARD")) {
-            // Apparently invalid line seen in Windows Mobile 6.5. Ignore them.
-            return;
-        } else {
-            throw new VCardAgentNotSupportedException("AGENT Property is not supported now.");
-        }
-    }
-
-    /**
-     * For vCard 3.0.
-     */
-    protected String maybeUnescapeText(final String text) {
-        return text;
-    }
-
-    /**
-     * Returns unescaped String if the character should be unescaped. Return
-     * null otherwise. e.g. In vCard 2.1, "\;" should be unescaped into ";"
-     * while "\x" should not be.
-     */
-    protected String maybeUnescapeCharacter(final char ch) {
-        return unescapeCharacter(ch);
-    }
-
-    /* package */ static String unescapeCharacter(final char ch) {
-        // Original vCard 2.1 specification does not allow transformation
-        // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous
-        // implementation of
-        // this class allowed them, so keep it as is.
-        if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') {
-            return String.valueOf(ch);
-        } else {
-            return null;
-        }
-    }
-
-    private void showPerformanceInfo() {
-        Log.d(LOG_TAG, "Total parsing time:  " + mTimeTotal + " ms");
-        Log.d(LOG_TAG, "Total readLine time: " + mReader.getTotalmillisecond() + " ms");
-        Log.d(LOG_TAG, "Time for handling the beggining of the record: " + mTimeReadStartRecord
-                + " ms");
-        Log.d(LOG_TAG, "Time for handling the end of the record: " + mTimeReadEndRecord + " ms");
-        Log.d(LOG_TAG, "Time for parsing line, and handling group: " + mTimeParseLineAndHandleGroup
-                + " ms");
-        Log.d(LOG_TAG, "Time for parsing ADR, ORG, and N fields:" + mTimeParseAdrOrgN + " ms");
-        Log.d(LOG_TAG, "Time for parsing property values: " + mTimeParsePropertyValues + " ms");
-        Log.d(LOG_TAG, "Time for handling normal property values: " + mTimeHandleMiscPropertyValue
-                + " ms");
-        Log.d(LOG_TAG, "Time for handling Quoted-Printable: " + mTimeHandleQuotedPrintable + " ms");
-        Log.d(LOG_TAG, "Time for handling Base64: " + mTimeHandleBase64 + " ms");
-    }
-
-    /**
-     * @return {@link VCardConfig#VERSION_21}
-     */
-    protected int getVersion() {
-        return VCardConfig.VERSION_21;
-    }
-
-    /**
-     * @return {@link VCardConfig#VERSION_30}
-     */
-    protected String getVersionString() {
-        return VCardConstants.VERSION_V21;
-    }
-
-    protected Set<String> getKnownPropertyNameSet() {
-        return VCardParser_V21.sKnownPropertyNameSet;
-    }
-
-    protected Set<String> getKnownTypeSet() {
-        return VCardParser_V21.sKnownTypeSet;
-    }
-
-    protected Set<String> getKnownValueSet() {
-        return VCardParser_V21.sKnownValueSet;
-    }
-
-    protected Set<String> getAvailableEncodingSet() {
-        return VCardParser_V21.sAvailableEncoding;
-    }
-
-    protected String getDefaultEncoding() {
-        return DEFAULT_ENCODING;
-    }
-
-
-    public void parse(InputStream is, VCardInterpreter interpreter)
-            throws IOException, VCardException {
-        if (is == null) {
-            throw new NullPointerException("InputStream must not be null.");
-        }
-
-        final InputStreamReader tmpReader = new InputStreamReader(is, mIntermediateCharset);
-        mReader = new CustomBufferedReader(tmpReader);
-
-        mInterpreter = (interpreter != null ? interpreter : new EmptyInterpreter());
-
-        final long start = System.currentTimeMillis();
-        if (mInterpreter != null) {
-            mInterpreter.start();
-        }
-        parseVCardFile();
-        if (mInterpreter != null) {
-            mInterpreter.end();
-        }
-        mTimeTotal += System.currentTimeMillis() - start;
-
-        if (VCardConfig.showPerformanceLog()) {
-            showPerformanceInfo();
-        }
-    }
-
-    public final void cancel() {
-        mCanceled = true;
-    }
-}
diff --git a/core/java/android/pim/vcard/VCardParserImpl_V30.java b/core/java/android/pim/vcard/VCardParserImpl_V30.java
deleted file mode 100644
index 3fad9a0..0000000
--- a/core/java/android/pim/vcard/VCardParserImpl_V30.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.pim.vcard.exception.VCardException;
-import android.util.Log;
-
-import java.io.IOException;
-import java.util.Set;
-
-/**
- * <p>
- * Basic implementation achieving vCard 3.0 parsing.
- * </p>
- * <p>
- * This class inherits vCard 2.1 implementation since technically they are similar,
- * while specifically there's logical no relevance between them.
- * So that developers are not confused with the inheritance,
- * {@link VCardParser_V30} does not inherit {@link VCardParser_V21}, while
- * {@link VCardParserImpl_V30} inherits {@link VCardParserImpl_V21}.
- * </p>
- * @hide
- */
-/* package */ class VCardParserImpl_V30 extends VCardParserImpl_V21 {
-    private static final String LOG_TAG = "VCardParserImpl_V30";
-
-    private String mPreviousLine;
-    private boolean mEmittedAgentWarning = false;
-
-    public VCardParserImpl_V30() {
-        super();
-    }
-
-    public VCardParserImpl_V30(int vcardType) {
-        super(vcardType);
-    }
-
-    @Override
-    protected int getVersion() {
-        return VCardConfig.VERSION_30;
-    }
-
-    @Override
-    protected String getVersionString() {
-        return VCardConstants.VERSION_V30;
-    }
-
-    @Override
-    protected String getLine() throws IOException {
-        if (mPreviousLine != null) {
-            String ret = mPreviousLine;
-            mPreviousLine = null;
-            return ret;
-        } else {
-            return mReader.readLine();
-        }
-    }
-
-    /**
-     * vCard 3.0 requires that the line with space at the beginning of the line
-     * must be combined with previous line.
-     */
-    @Override
-    protected String getNonEmptyLine() throws IOException, VCardException {
-        String line;
-        StringBuilder builder = null;
-        while (true) {
-            line = mReader.readLine();
-            if (line == null) {
-                if (builder != null) {
-                    return builder.toString();
-                } else if (mPreviousLine != null) {
-                    String ret = mPreviousLine;
-                    mPreviousLine = null;
-                    return ret;
-                }
-                throw new VCardException("Reached end of buffer.");
-            } else if (line.length() == 0) {
-                if (builder != null) {
-                    return builder.toString();
-                } else if (mPreviousLine != null) {
-                    String ret = mPreviousLine;
-                    mPreviousLine = null;
-                    return ret;
-                }
-            } else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') {
-                if (builder != null) {
-                    // See Section 5.8.1 of RFC 2425 (MIME-DIR document).
-                    // Following is the excerpts from it.
-                    //
-                    // DESCRIPTION:This is a long description that exists on a long line.
-                    //
-                    // Can be represented as:
-                    //
-                    // DESCRIPTION:This is a long description
-                    //  that exists on a long line.
-                    //
-                    // It could also be represented as:
-                    //
-                    // DESCRIPTION:This is a long descrip
-                    //  tion that exists o
-                    //  n a long line.
-                    builder.append(line.substring(1));
-                } else if (mPreviousLine != null) {
-                    builder = new StringBuilder();
-                    builder.append(mPreviousLine);
-                    mPreviousLine = null;
-                    builder.append(line.substring(1));
-                } else {
-                    throw new VCardException("Space exists at the beginning of the line");
-                }
-            } else {
-                if (mPreviousLine == null) {
-                    mPreviousLine = line;
-                    if (builder != null) {
-                        return builder.toString();
-                    }
-                } else {
-                    String ret = mPreviousLine;
-                    mPreviousLine = line;
-                    return ret;
-                }
-            }
-        }
-    }
-
-    /*
-     * vcard = [group "."] "BEGIN" ":" "VCARD" 1 * CRLF
-     *         1 * (contentline)
-     *         ;A vCard object MUST include the VERSION, FN and N types.
-     *         [group "."] "END" ":" "VCARD" 1 * CRLF
-     */
-    @Override
-    protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
-        // TODO: vCard 3.0 supports group.
-        return super.readBeginVCard(allowGarbage);
-    }
-
-    @Override
-    protected void readEndVCard(boolean useCache, boolean allowGarbage)
-            throws IOException, VCardException {
-        // TODO: vCard 3.0 supports group.
-        super.readEndVCard(useCache, allowGarbage);
-    }
-
-    /**
-     * vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
-     */
-    @Override
-    protected void handleParams(final String params) throws VCardException {
-        try {
-            super.handleParams(params);
-        } catch (VCardException e) {
-            // maybe IANA type
-            String[] strArray = params.split("=", 2);
-            if (strArray.length == 2) {
-                handleAnyParam(strArray[0], strArray[1]);
-            } else {
-                // Must not come here in the current implementation.
-                throw new VCardException(
-                        "Unknown params value: " + params);
-            }
-        }
-    }
-
-    @Override
-    protected void handleAnyParam(final String paramName, final String paramValue) {
-        mInterpreter.propertyParamType(paramName);
-        splitAndPutParamValue(paramValue);
-    }
-
-    @Override
-    protected void handleParamWithoutName(final String paramValue) {
-        handleType(paramValue);
-    }
-
-    /*
-     *  vCard 3.0 defines
-     *
-     *  param         = param-name "=" param-value *("," param-value)
-     *  param-name    = iana-token / x-name
-     *  param-value   = ptext / quoted-string
-     *  quoted-string = DQUOTE QSAFE-CHAR DQUOTE
-     *  QSAFE-CHAR    = WSP / %x21 / %x23-7E / NON-ASCII
-     *                ; Any character except CTLs, DQUOTE
-     *
-     *  QSAFE-CHAR must not contain DQUOTE, including escaped one (\").
-     */
-    @Override
-    protected void handleType(final String paramValue) {
-        mInterpreter.propertyParamType("TYPE");
-        splitAndPutParamValue(paramValue);
-    }
-
-    /**
-     * Splits parameter values into pieces in accordance with vCard 3.0 specification and
-     * puts pieces into mInterpreter.
-     */
-    /*
-     *  param-value   = ptext / quoted-string
-     *  quoted-string = DQUOTE QSAFE-CHAR DQUOTE
-     *  QSAFE-CHAR    = WSP / %x21 / %x23-7E / NON-ASCII
-     *                ; Any character except CTLs, DQUOTE
-     *
-     *  QSAFE-CHAR must not contain DQUOTE, including escaped one (\")
-     */
-    private void splitAndPutParamValue(String paramValue) {
-        // "comma,separated:inside.dquote",pref
-        //   -->
-        // - comma,separated:inside.dquote
-        // - pref
-        //
-        // Note: Though there's a code, we don't need to take much care of
-        // wrongly-added quotes like the example above, as they induce
-        // parse errors at the top level (when splitting a line into parts).
-        StringBuilder builder = null;  // Delay initialization.
-        boolean insideDquote = false;
-        final int length = paramValue.length();
-        for (int i = 0; i < length; i++) {
-            final char ch = paramValue.charAt(i);
-            if (ch == '"') {
-                if (insideDquote) {
-                    // End of Dquote.
-                    mInterpreter.propertyParamValue(builder.toString());
-                    builder = null;
-                    insideDquote = false;
-                } else {
-                    if (builder != null) {
-                        if (builder.length() > 0) {
-                            // e.g.
-                            // pref"quoted"
-                            Log.w(LOG_TAG, "Unexpected Dquote inside property.");
-                        } else {
-                            // e.g.
-                            // pref,"quoted"
-                            // "quoted",pref
-                            mInterpreter.propertyParamValue(builder.toString());
-                        }
-                    }
-                    insideDquote = true;
-                }
-            } else if (ch == ',' && !insideDquote) {
-                if (builder == null) {
-                    Log.w(LOG_TAG, "Comma is used before actual string comes. (" +
-                            paramValue + ")");
-                } else {
-                    mInterpreter.propertyParamValue(builder.toString());
-                    builder = null;
-                }
-            } else {
-                // To stop creating empty StringBuffer at the end of parameter,
-                // we delay creating this object until this point.
-                if (builder == null) {
-                    builder = new StringBuilder();
-                }
-                builder.append(ch);
-            }
-        }
-        if (insideDquote) {
-            // e.g.
-            // "non-quote-at-end
-            Log.d(LOG_TAG, "Dangling Dquote.");
-        }
-        if (builder != null) {
-            if (builder.length() == 0) {
-                Log.w(LOG_TAG, "Unintended behavior. We must not see empty StringBuilder " +
-                        "at the end of parameter value parsing.");
-            } else {
-                mInterpreter.propertyParamValue(builder.toString());
-            }
-        }
-    }
-
-    @Override
-    protected void handleAgent(final String propertyValue) {
-        // The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.1.
-        //
-        // e.g.
-        // AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n
-        //  TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n
-        //  ET:jfriday@host.com\nEND:VCARD\n
-        //
-        // TODO: fix this.
-        //
-        // issue:
-        //  vCard 3.0 also allows this as an example.
-        //
-        // AGENT;VALUE=uri:
-        //  CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
-        //
-        // This is not vCard. Should we support this?
-        //
-        // Just ignore the line for now, since we cannot know how to handle it...
-        if (!mEmittedAgentWarning) {
-            Log.w(LOG_TAG, "AGENT in vCard 3.0 is not supported yet. Ignore it");
-            mEmittedAgentWarning = true;
-        }
-    }
-
-    /**
-     * vCard 3.0 does not require two CRLF at the last of BASE64 data.
-     * It only requires that data should be MIME-encoded.
-     */
-    @Override
-    protected String getBase64(final String firstString)
-            throws IOException, VCardException {
-        final StringBuilder builder = new StringBuilder();
-        builder.append(firstString);
-
-        while (true) {
-            final String line = getLine();
-            if (line == null) {
-                throw new VCardException("File ended during parsing BASE64 binary");
-            }
-            if (line.length() == 0) {
-                break;
-            } else if (!line.startsWith(" ") && !line.startsWith("\t")) {
-                mPreviousLine = line;
-                break;
-            }
-            builder.append(line);
-        }
-
-        return builder.toString();
-    }
-
-    /**
-     * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N")
-     *              ; \\ encodes \, \n or \N encodes newline
-     *              ; \; encodes ;, \, encodes ,
-     *
-     * Note: Apple escapes ':' into '\:' while does not escape '\'
-     */
-    @Override
-    protected String maybeUnescapeText(final String text) {
-        return unescapeText(text);
-    }
-
-    public static String unescapeText(final String text) {
-        StringBuilder builder = new StringBuilder();
-        final int length = text.length();
-        for (int i = 0; i < length; i++) {
-            char ch = text.charAt(i);
-            if (ch == '\\' && i < length - 1) {
-                final char next_ch = text.charAt(++i);
-                if (next_ch == 'n' || next_ch == 'N') {
-                    builder.append("\n");
-                } else {
-                    builder.append(next_ch);
-                }
-            } else {
-                builder.append(ch);
-            }
-        }
-        return builder.toString();
-    }
-
-    @Override
-    protected String maybeUnescapeCharacter(final char ch) {
-        return unescapeCharacter(ch);
-    }
-
-    public static String unescapeCharacter(final char ch) {
-        if (ch == 'n' || ch == 'N') {
-            return "\n";
-        } else {
-            return String.valueOf(ch);
-        }
-    }
-
-    @Override
-    protected Set<String> getKnownPropertyNameSet() {
-        return VCardParser_V30.sKnownPropertyNameSet;
-    }
-}
diff --git a/core/java/android/pim/vcard/VCardParserImpl_V40.java b/core/java/android/pim/vcard/VCardParserImpl_V40.java
deleted file mode 100644
index 0fe76bb..0000000
--- a/core/java/android/pim/vcard/VCardParserImpl_V40.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import java.util.Set;
-
-
-/**
- * <p>
- * Basic implementation parsing vCard 4.0.
- * </p>
- * <p>
- * vCard 4.0 is not published yet. Also this implementation is premature. 
- * </p>
- * @hide
- */
-/* package */ class VCardParserImpl_V40 extends VCardParserImpl_V30 {
-    // private static final String LOG_TAG = "VCardParserImpl_V40";
-
-    public VCardParserImpl_V40() {
-        super();
-    }
-
-    public VCardParserImpl_V40(final int vcardType) {
-        super(vcardType);
-    }
-
-    @Override
-    protected int getVersion() {
-        return VCardConfig.VERSION_40;
-    }
-
-    @Override
-    protected String getVersionString() {
-        return VCardConstants.VERSION_V40;
-    }
-
-    /**
-     * We escape "\N" into new line for safety.
-     */
-    @Override
-    protected String maybeUnescapeText(final String text) {
-        return unescapeText(text);
-    }
-
-    public static String unescapeText(final String text) {
-        // TODO: more strictly, vCard 4.0 requires different type of unescaping rule
-        //       toward each property.
-        final StringBuilder builder = new StringBuilder();
-        final int length = text.length();
-        for (int i = 0; i < length; i++) {
-            char ch = text.charAt(i);
-            if (ch == '\\' && i < length - 1) {
-                final char next_ch = text.charAt(++i);
-                if (next_ch == 'n' || next_ch == 'N') {
-                    builder.append("\n");
-                } else {
-                    builder.append(next_ch);
-                }
-            } else {
-                builder.append(ch);
-            }
-        }
-        return builder.toString();
-    }
-
-    public static String unescapeCharacter(final char ch) {
-        if (ch == 'n' || ch == 'N') {
-            return "\n";
-        } else {
-            return String.valueOf(ch);
-        }
-    }
-
-    @Override
-    protected Set<String> getKnownPropertyNameSet() {
-        return VCardParser_V40.sKnownPropertyNameSet;
-    }
-}
\ No newline at end of file
diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java
deleted file mode 100644
index 507a176..0000000
--- a/core/java/android/pim/vcard/VCardParser_V21.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.pim.vcard.exception.VCardException;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * </p>
- * vCard parser for vCard 2.1. See the specification for more detail about the spec itself.
- * </p>
- * <p>
- * The spec is written in 1996, and currently various types of "vCard 2.1" exist.
- * To handle real the world vCard formats appropriately and effectively, this class does not
- * obey with strict vCard 2.1.
- * In stead, not only vCard spec but also real world vCard is considered.
- * </p>
- * e.g. A lot of devices and softwares let vCard importer/exporter to use
- * the PNG format to determine the type of image, while it is not allowed in
- * the original specification. As of 2010, we can see even the FLV format
- * (possible in Japanese mobile phones).
- * </p>
- */
-public final class VCardParser_V21 implements VCardParser {
-    /**
-     * A unmodifiable Set storing the property names available in the vCard 2.1 specification.
-     */
-    /* package */ static final Set<String> sKnownPropertyNameSet =
-            Collections.unmodifiableSet(new HashSet<String>(
-                    Arrays.asList("BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
-                            "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
-                            "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER")));
-
-    /**
-     * A unmodifiable Set storing the types known in vCard 2.1.
-     */
-    /* package */ static final Set<String> sKnownTypeSet =
-            Collections.unmodifiableSet(new HashSet<String>(
-                    Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK",
-                            "PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS",
-                            "MODEM", "CAR", "ISDN", "VIDEO", "AOL", "APPLELINK",
-                            "ATTMAIL", "CIS", "EWORLD", "INTERNET", "IBMMAIL",
-                            "MCIMAIL", "POWERSHARE", "PRODIGY", "TLX", "X400", "GIF",
-                            "CGM", "WMF", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF",
-                            "PDF", "PS", "JPEG", "QTIME", "MPEG", "MPEG2", "AVI",
-                            "WAVE", "AIFF", "PCM", "X509", "PGP")));
-
-    /**
-     * A unmodifiable Set storing the values for the type "VALUE", available in the vCard 2.1.
-     */
-    /* package */ static final Set<String> sKnownValueSet =
-            Collections.unmodifiableSet(new HashSet<String>(
-                    Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID")));
-
-    /**
-     * <p>
-     * A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 2.1.
-     * </p>
-     * <p>
-     * Though vCard 2.1 specification does not allow "B" encoding, some data may have it.
-     * We allow it for safety.
-     * </p>
-     */
-    /* package */ static final Set<String> sAvailableEncoding =
-        Collections.unmodifiableSet(new HashSet<String>(
-                Arrays.asList(VCardConstants.PARAM_ENCODING_7BIT,
-                        VCardConstants.PARAM_ENCODING_8BIT,
-                        VCardConstants.PARAM_ENCODING_QP,
-                        VCardConstants.PARAM_ENCODING_BASE64,
-                        VCardConstants.PARAM_ENCODING_B)));
-
-    private final VCardParserImpl_V21 mVCardParserImpl;
-
-    public VCardParser_V21() {
-        mVCardParserImpl = new VCardParserImpl_V21();
-    }
-
-    public VCardParser_V21(int vcardType) {
-        mVCardParserImpl = new VCardParserImpl_V21(vcardType);
-    }
-
-    public void parse(InputStream is, VCardInterpreter interepreter)
-            throws IOException, VCardException {
-        mVCardParserImpl.parse(is, interepreter);
-    }
-
-    public void cancel() {
-        mVCardParserImpl.cancel();
-    }
-}
diff --git a/core/java/android/pim/vcard/VCardParser_V30.java b/core/java/android/pim/vcard/VCardParser_V30.java
deleted file mode 100644
index 238d3a8..0000000
--- a/core/java/android/pim/vcard/VCardParser_V30.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.pim.vcard.exception.VCardException;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * <p>
- * vCard parser for vCard 3.0. See RFC 2426 for more detail.
- * </p>
- * <p>
- * This parser allows vCard format which is not allowed in the RFC, since
- * we have seen several vCard 3.0 files which don't comply with it.
- * </p>
- * <p>
- * e.g. vCard 3.0 does not allow "CHARSET" attribute, but some actual files
- * have it and they uses non UTF-8 charsets. UTF-8 is recommended in RFC 2426,
- * but it is not a must. We silently allow "CHARSET".
- * </p>
- */
-public class VCardParser_V30 implements VCardParser {
-    /* package */ static final Set<String> sKnownPropertyNameSet =
-            Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
-                    "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", 
-                    "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
-                    "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1
-                    "NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS",
-                    "SORT-STRING", "CATEGORIES", "PRODID"))); // 3.0
-
-    /**
-     * <p>
-     * A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 3.0.
-     * </p>
-     * <p>
-     * Though vCard 2.1 specification does not allow "7BIT" or "BASE64", we allow them for safety.
-     * </p>
-     * <p>
-     * "QUOTED-PRINTABLE" is not allowed in vCard 3.0 and not in this parser either,
-     * because the encoding ambiguates how the vCard file to be parsed.
-     * </p>
-     */
-    /* package */ static final Set<String> sAcceptableEncoding =
-            Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
-                    VCardConstants.PARAM_ENCODING_7BIT,
-                    VCardConstants.PARAM_ENCODING_8BIT,
-                    VCardConstants.PARAM_ENCODING_BASE64,
-                    VCardConstants.PARAM_ENCODING_B)));
-
-    private final VCardParserImpl_V30 mVCardParserImpl;
-
-    public VCardParser_V30() {
-        mVCardParserImpl = new VCardParserImpl_V30();
-    }
-
-    public VCardParser_V30(int vcardType) {
-        mVCardParserImpl = new VCardParserImpl_V30(vcardType);
-    }
-
-    public void parse(InputStream is, VCardInterpreter interepreter)
-            throws IOException, VCardException {
-        mVCardParserImpl.parse(is, interepreter);
-    }
-
-    public void cancel() {
-        mVCardParserImpl.cancel();
-    }
-}
diff --git a/core/java/android/pim/vcard/VCardParser_V40.java b/core/java/android/pim/vcard/VCardParser_V40.java
deleted file mode 100644
index 65a2f68..0000000
--- a/core/java/android/pim/vcard/VCardParser_V40.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.pim.vcard.exception.VCardException;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * <p>
- * vCard parser for vCard 4.0.
- * </p>
- * <p>
- * Currently this parser is based on vCard 4.0 specification rev 11.
- * </p>
- */
-public class VCardParser_V40 implements VCardParser {
-    /* package */ static final Set<String> sKnownPropertyNameSet =
-            Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
-                    "BEGIN", "END", "SOURCE", "NAME", "KIND", "XML",
-                    "FN", "N", "NICKNAME", "PHOTO", "BDAY", "DDAY",
-                    "BIRTH", "DEATH", "ANNIVERSARY", "SEX", "ADR",
-                    "LABEL", "TEL", "EMAIL", "IMPP", "LANG", "TZ",
-                    "GEO", "TITLE", "ROLE", "LOGO", "ORG", "MEMBER",
-                    "RELATED", "CATEGORIES", "NOTE", "PRODID",
-                    "REV", "SOUND", "UID", "CLIENTPIDMAP",
-                    "URL", "VERSION", "CLASS", "KEY", "FBURL", "CALENDRURI",
-                    "CALURI")));
-
-    /**
-     * <p>
-     * A unmodifiable Set storing the values for the type "ENCODING", available in vCard 4.0.
-     * </p>
-     */
-    /* package */ static final Set<String> sAcceptableEncoding =
-            Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
-                    VCardConstants.PARAM_ENCODING_8BIT,
-                    VCardConstants.PARAM_ENCODING_B)));
-
-    private final VCardParserImpl_V30 mVCardParserImpl;
-
-    public VCardParser_V40() {
-        mVCardParserImpl = new VCardParserImpl_V40();
-    }
-
-    public VCardParser_V40(int vcardType) {
-        mVCardParserImpl = new VCardParserImpl_V40(vcardType);
-    }
-
-    @Override
-    public void parse(InputStream is, VCardInterpreter interepreter)
-            throws IOException, VCardException {
-        mVCardParserImpl.parse(is, interepreter);
-    }
-
-    @Override
-    public void cancel() {
-        mVCardParserImpl.cancel();
-    }
-}
\ No newline at end of file
diff --git a/core/java/android/pim/vcard/VCardSourceDetector.java b/core/java/android/pim/vcard/VCardSourceDetector.java
deleted file mode 100644
index 4c6461e..0000000
--- a/core/java/android/pim/vcard/VCardSourceDetector.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * <p>
- * The class which tries to detects the source of a vCard file from its contents.
- * </p>
- * <p>
- * The specification of vCard (including both 2.1 and 3.0) is not so strict as to
- * guess its format just by reading beginning few lines (usually we can, but in
- * some most pessimistic case, we cannot until at almost the end of the file).
- * Also we cannot store all vCard entries in memory, while there's no specification
- * how big the vCard entry would become after the parse.
- * </p>
- * <p>
- * This class is usually used for the "first scan", in which we can understand which vCard
- * version is used (and how many entries exist in a file).
- * </p>
- */
-public class VCardSourceDetector implements VCardInterpreter {
-    private static final String LOG_TAG = "VCardSourceDetector";
-
-    private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList(
-            "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME",
-            "X-ABADR", "X-ABUID"));
-    
-    private static Set<String> JAPANESE_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
-            "X-GNO", "X-GN", "X-REDUCTION"));
-    
-    private static Set<String> WINDOWS_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
-            "X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC"));
-    
-    // Note: these signes appears before the signs of the other type (e.g. "X-GN").
-    // In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES.
-    private static Set<String> FOMA_SIGNS = new HashSet<String>(Arrays.asList(
-            "X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED",
-            "X-SD-DESCRIPTION"));
-    private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE";
-
-    /**
-     * Represents that no estimation is available. Users of this class is able to this
-     * constant when you don't want to let a vCard parser rely on estimation for parse type.
-     */
-    public static final int PARSE_TYPE_UNKNOWN = 0;
-
-    // For Apple's software, which does not mean this type is effective for all its products.
-    // We confirmed they usually use UTF-8, but not sure about vCard type.
-    private static final int PARSE_TYPE_APPLE = 1;
-    // For Japanese mobile phones, which are usually using Shift_JIS as a charset.
-    private static final int PARSE_TYPE_MOBILE_PHONE_JP = 2;
-    // For some of mobile phones released from DoCoMo, which use nested vCard. 
-    private static final int PARSE_TYPE_DOCOMO_TORELATE_NEST = 3;
-    // For Japanese Windows Mobel phones. It's version is supposed to be 6.5.
-    private static final int PARSE_TYPE_WINDOWS_MOBILE_V65_JP = 4;
-
-    private int mParseType = 0;  // Not sure.
-
-    private boolean mNeedToParseVersion = false;
-    private int mVersion = -1;  // -1 == unknown
-
-    // Some mobile phones (like FOMA) tells us the charset of the data.
-    private boolean mNeedToParseCharset;
-    private String mSpecifiedCharset;
-    
-    public void start() {
-    }
-    
-    public void end() {
-    }
-
-    public void startEntry() {
-    }    
-
-    public void startProperty() {
-        mNeedToParseCharset = false;
-        mNeedToParseVersion = false;
-    }
-    
-    public void endProperty() {
-    }
-
-    public void endEntry() {
-    }
-
-    public void propertyGroup(String group) {
-    }
-    
-    public void propertyName(String name) {
-        if (name.equalsIgnoreCase(VCardConstants.PROPERTY_VERSION)) {
-            mNeedToParseVersion = true;
-            return;
-        } else if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
-            mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
-            // Probably Shift_JIS is used, but we should double confirm.
-            mNeedToParseCharset = true;
-            return;
-        }
-        if (mParseType != PARSE_TYPE_UNKNOWN) {
-            return;
-        }
-        if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) {
-            mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP;
-        } else if (FOMA_SIGNS.contains(name)) {
-            mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST;
-        } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) {
-            mParseType = PARSE_TYPE_MOBILE_PHONE_JP;
-        } else if (APPLE_SIGNS.contains(name)) {
-            mParseType = PARSE_TYPE_APPLE;
-        }
-    }
-
-    public void propertyParamType(String type) {
-    }
-
-    public void propertyParamValue(String value) {
-    }
-
-    public void propertyValues(List<String> values) {
-        if (mNeedToParseVersion && values.size() > 0) {
-            final String versionString = values.get(0);
-            if (versionString.equals(VCardConstants.VERSION_V21)) {
-                mVersion = VCardConfig.VERSION_21;
-            } else if (versionString.equals(VCardConstants.VERSION_V30)) {
-                mVersion = VCardConfig.VERSION_30;
-            } else if (versionString.equals(VCardConstants.VERSION_V40)) {
-                mVersion = VCardConfig.VERSION_40;
-            } else {
-                Log.w(LOG_TAG, "Invalid version string: " + versionString);
-            }
-        } else if (mNeedToParseCharset && values.size() > 0) {
-            mSpecifiedCharset = values.get(0);
-        }
-    }
-
-    /**
-     * @return The available type can be used with vCard parser. You probably need to
-     * use {{@link #getEstimatedCharset()} to understand the charset to be used.
-     */
-    public int getEstimatedType() {
-        switch (mParseType) {
-            case PARSE_TYPE_DOCOMO_TORELATE_NEST:
-                return VCardConfig.VCARD_TYPE_DOCOMO | VCardConfig.FLAG_TORELATE_NEST;
-            case PARSE_TYPE_MOBILE_PHONE_JP:
-                return VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE;
-            case PARSE_TYPE_APPLE:
-            case PARSE_TYPE_WINDOWS_MOBILE_V65_JP:
-            default: {
-                if (mVersion == VCardConfig.VERSION_21) {
-                    return VCardConfig.VCARD_TYPE_V21_GENERIC;
-                } else if (mVersion == VCardConfig.VERSION_30) {
-                    return VCardConfig.VCARD_TYPE_V30_GENERIC;
-                } else if (mVersion == VCardConfig.VERSION_40) {
-                    return VCardConfig.VCARD_TYPE_V40_GENERIC;
-                } else {
-                    return VCardConfig.VCARD_TYPE_UNKNOWN;
-                }
-            }
-        }
-    }
-
-    /**
-     * <p>
-     * Returns charset String guessed from the source's properties.
-     * This method must be called after parsing target file(s).
-     * </p>
-     * @return Charset String. Null is returned if guessing the source fails.
-     */
-    public String getEstimatedCharset() {
-        if (TextUtils.isEmpty(mSpecifiedCharset)) {
-            return mSpecifiedCharset;
-        }
-        switch (mParseType) {
-            case PARSE_TYPE_WINDOWS_MOBILE_V65_JP:
-            case PARSE_TYPE_DOCOMO_TORELATE_NEST:
-            case PARSE_TYPE_MOBILE_PHONE_JP:
-                return "SHIFT_JIS";
-            case PARSE_TYPE_APPLE:
-                return "UTF-8";
-            default:
-                return null;
-        }
-    }
-}
diff --git a/core/java/android/pim/vcard/VCardUtils.java b/core/java/android/pim/vcard/VCardUtils.java
deleted file mode 100644
index abceca0..0000000
--- a/core/java/android/pim/vcard/VCardUtils.java
+++ /dev/null
@@ -1,796 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.content.ContentProviderOperation;
-import android.pim.vcard.exception.VCardException;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.Data;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-
-import org.apache.commons.codec.DecoderException;
-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.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Utilities for VCard handling codes.
- */
-public class VCardUtils {
-    private static final String LOG_TAG = "VCardUtils";
-
-    // Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is
-    // converted to two parameter Strings. These only contain some minor fields valid in both
-    // vCard and current (as of 2009-08-07) Contacts structure. 
-    private static final Map<Integer, String> sKnownPhoneTypesMap_ItoS;
-    private static final Set<String> sPhoneTypesUnknownToContactsSet;
-    private static final Map<String, Integer> sKnownPhoneTypeMap_StoI;
-    private static final Map<Integer, String> sKnownImPropNameMap_ItoS;
-    private static final Set<String> sMobilePhoneLabelSet;
-
-    static {
-        sKnownPhoneTypesMap_ItoS = new HashMap<Integer, String>();
-        sKnownPhoneTypeMap_StoI = new HashMap<String, Integer>();
-
-        sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, VCardConstants.PARAM_TYPE_CAR);
-        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CAR, Phone.TYPE_CAR);
-        sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, VCardConstants.PARAM_TYPE_PAGER);
-        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_PAGER, Phone.TYPE_PAGER);
-        sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, VCardConstants.PARAM_TYPE_ISDN);
-        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_ISDN, Phone.TYPE_ISDN);
-        
-        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_HOME, Phone.TYPE_HOME);
-        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_WORK, Phone.TYPE_WORK);
-        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CELL, Phone.TYPE_MOBILE);
-                
-        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_OTHER, Phone.TYPE_OTHER);
-        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_CALLBACK,
-                Phone.TYPE_CALLBACK);
-        sKnownPhoneTypeMap_StoI.put(
-                VCardConstants.PARAM_PHONE_EXTRA_TYPE_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN);
-        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_RADIO, Phone.TYPE_RADIO);
-        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_TTY_TDD,
-                Phone.TYPE_TTY_TDD);
-        sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_ASSISTANT,
-                Phone.TYPE_ASSISTANT);
-
-        sPhoneTypesUnknownToContactsSet = new HashSet<String>();
-        sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MODEM);
-        sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MSG);
-        sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_BBS);
-        sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_VIDEO);
-
-        sKnownImPropNameMap_ItoS = new HashMap<Integer, String>();
-        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM);
-        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN);
-        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO);
-        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME);
-        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_GOOGLE_TALK,
-                VCardConstants.PROPERTY_X_GOOGLE_TALK);
-        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ);
-        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER);
-        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_QQ, VCardConstants.PROPERTY_X_QQ);
-        sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_NETMEETING, VCardConstants.PROPERTY_X_NETMEETING);
-
-        // \u643A\u5E2F\u96FB\u8A71 = Full-width Hiragana "Keitai-Denwa" (mobile phone)
-        // \u643A\u5E2F = Full-width Hiragana "Keitai" (mobile phone)
-        // \u30B1\u30A4\u30BF\u30A4 = Full-width Katakana "Keitai" (mobile phone)
-        // \uFF79\uFF72\uFF80\uFF72 = Half-width Katakana "Keitai" (mobile phone)
-        sMobilePhoneLabelSet = new HashSet<String>(Arrays.asList(
-                "MOBILE", "\u643A\u5E2F\u96FB\u8A71", "\u643A\u5E2F", "\u30B1\u30A4\u30BF\u30A4",
-                "\uFF79\uFF72\uFF80\uFF72"));
-    }
-
-    public static String getPhoneTypeString(Integer type) {
-        return sKnownPhoneTypesMap_ItoS.get(type);
-    }
-
-    /**
-     * Returns Interger when the given types can be parsed as known type. Returns String object
-     * when not, which should be set to label. 
-     */
-    public static Object getPhoneTypeFromStrings(Collection<String> types,
-            String number) {
-        if (number == null) {
-            number = "";
-        }
-        int type = -1;
-        String label = null;
-        boolean isFax = false;
-        boolean hasPref = false;
-        
-        if (types != null) {
-            for (String typeString : types) {
-                if (typeString == null) {
-                    continue;
-                }
-                typeString = typeString.toUpperCase();
-                if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) {
-                    hasPref = true;
-                } else if (typeString.equals(VCardConstants.PARAM_TYPE_FAX)) {
-                    isFax = true;
-                } else {
-                    if (typeString.startsWith("X-") && type < 0) {
-                        typeString = typeString.substring(2);
-                    }
-                    if (typeString.length() == 0) {
-                        continue;
-                    }
-                    final Integer tmp = sKnownPhoneTypeMap_StoI.get(typeString);
-                    if (tmp != null) {
-                        final int typeCandidate = tmp;
-                        // TYPE_PAGER is prefered when the number contains @ surronded by
-                        // a pager number and a domain name.
-                        // e.g.
-                        // o 1111@domain.com
-                        // x @domain.com
-                        // x 1111@
-                        final int indexOfAt = number.indexOf("@");
-                        if ((typeCandidate == Phone.TYPE_PAGER
-                                && 0 < indexOfAt && indexOfAt < number.length() - 1)
-                                || type < 0
-                                || type == Phone.TYPE_CUSTOM) {
-                            type = tmp;
-                        }
-                    } else if (type < 0) {
-                        type = Phone.TYPE_CUSTOM;
-                        label = typeString;
-                    }
-                }
-            }
-        }
-        if (type < 0) {
-            if (hasPref) {
-                type = Phone.TYPE_MAIN;
-            } else {
-                // default to TYPE_HOME
-                type = Phone.TYPE_HOME;
-            }
-        }
-        if (isFax) {
-            if (type == Phone.TYPE_HOME) {
-                type = Phone.TYPE_FAX_HOME;
-            } else if (type == Phone.TYPE_WORK) {
-                type = Phone.TYPE_FAX_WORK;
-            } else if (type == Phone.TYPE_OTHER) {
-                type = Phone.TYPE_OTHER_FAX;
-            }
-        }
-        if (type == Phone.TYPE_CUSTOM) {
-            return label;
-        } else {
-            return type;
-        }
-    }
-
-    @SuppressWarnings("deprecation")
-    public static boolean isMobilePhoneLabel(final String label) {
-        // For backward compatibility.
-        // Detail: Until Donut, there isn't TYPE_MOBILE for email while there is now.
-        //         To support mobile type at that time, this custom label had been used.
-        return ("_AUTO_CELL".equals(label) || sMobilePhoneLabelSet.contains(label));
-    }
-
-    public static boolean isValidInV21ButUnknownToContactsPhoteType(final String label) {
-        return sPhoneTypesUnknownToContactsSet.contains(label);
-    }
-
-    public static String getPropertyNameForIm(final int protocol) {
-        return sKnownImPropNameMap_ItoS.get(protocol);
-    }
-
-    public static String[] sortNameElements(final int vcardType,
-            final String familyName, final String middleName, final String givenName) {
-        final String[] list = new String[3];
-        final int nameOrderType = VCardConfig.getNameOrderType(vcardType);
-        switch (nameOrderType) {
-            case VCardConfig.NAME_ORDER_JAPANESE: {
-                if (containsOnlyPrintableAscii(familyName) &&
-                        containsOnlyPrintableAscii(givenName)) {
-                    list[0] = givenName;
-                    list[1] = middleName;
-                    list[2] = familyName;
-                } else {
-                    list[0] = familyName;
-                    list[1] = middleName;
-                    list[2] = givenName;
-                }
-                break;
-            }
-            case VCardConfig.NAME_ORDER_EUROPE: {
-                list[0] = middleName;
-                list[1] = givenName;
-                list[2] = familyName;
-                break;
-            }
-            default: {
-                list[0] = givenName;
-                list[1] = middleName;
-                list[2] = familyName;
-                break;
-            }
-        }
-        return list;
-    }
-
-    public static int getPhoneNumberFormat(final int vcardType) {
-        if (VCardConfig.isJapaneseDevice(vcardType)) {
-            return PhoneNumberUtils.FORMAT_JAPAN;
-        } else {
-            return PhoneNumberUtils.FORMAT_NANP;
-        }
-    }
-
-    /**
-     * <p>
-     * Inserts postal data into the builder object.
-     * </p>
-     * <p>
-     * Note that the data structure of ContactsContract is different from that defined in vCard.
-     * So some conversion may be performed in this method.
-     * </p>
-     */
-    public static void insertStructuredPostalDataUsingContactsStruct(int vcardType,
-            final ContentProviderOperation.Builder builder,
-            final VCardEntry.PostalData postalData) {
-        builder.withValueBackReference(StructuredPostal.RAW_CONTACT_ID, 0);
-        builder.withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
-
-        builder.withValue(StructuredPostal.TYPE, postalData.type);
-        if (postalData.type == StructuredPostal.TYPE_CUSTOM) {
-            builder.withValue(StructuredPostal.LABEL, postalData.label);
-        }
-
-        final String streetString;
-        if (TextUtils.isEmpty(postalData.street)) {
-            if (TextUtils.isEmpty(postalData.extendedAddress)) {
-                streetString = null;
-            } else {
-                streetString = postalData.extendedAddress;
-            }
-        } else {
-            if (TextUtils.isEmpty(postalData.extendedAddress)) {
-                streetString = postalData.street;
-            } else {
-                streetString = postalData.street + " " + postalData.extendedAddress;
-            }
-        }
-        builder.withValue(StructuredPostal.POBOX, postalData.pobox);
-        builder.withValue(StructuredPostal.STREET, streetString);
-        builder.withValue(StructuredPostal.CITY, postalData.localty);
-        builder.withValue(StructuredPostal.REGION, postalData.region);
-        builder.withValue(StructuredPostal.POSTCODE, postalData.postalCode);
-        builder.withValue(StructuredPostal.COUNTRY, postalData.country);
-
-        builder.withValue(StructuredPostal.FORMATTED_ADDRESS,
-                postalData.getFormattedAddress(vcardType));
-        if (postalData.isPrimary) {
-            builder.withValue(Data.IS_PRIMARY, 1);
-        }
-    }
-
-    public static String constructNameFromElements(final int vcardType,
-            final String familyName, final String middleName, final String givenName) {
-        return constructNameFromElements(vcardType, familyName, middleName, givenName,
-                null, null);
-    }
-
-    public static String constructNameFromElements(final int vcardType,
-            final String familyName, final String middleName, final String givenName,
-            final String prefix, final String suffix) {
-        final StringBuilder builder = new StringBuilder();
-        final String[] nameList = sortNameElements(vcardType, familyName, middleName, givenName);
-        boolean first = true;
-        if (!TextUtils.isEmpty(prefix)) {
-            first = false;
-            builder.append(prefix);
-        }
-        for (final String namePart : nameList) {
-            if (!TextUtils.isEmpty(namePart)) {
-                if (first) {
-                    first = false;
-                } else {
-                    builder.append(' ');
-                }
-                builder.append(namePart);
-            }
-        }
-        if (!TextUtils.isEmpty(suffix)) {
-            if (!first) {
-                builder.append(' ');
-            }
-            builder.append(suffix);
-        }
-        return builder.toString();
-    }
-
-    /**
-     * Splits the given value into pieces using the delimiter ';' inside it.
-     *
-     * Escaped characters in those values are automatically unescaped into original form.
-     */
-    public static List<String> constructListFromValue(final String value,
-            final int vcardType) {
-        final List<String> list = new ArrayList<String>();
-        StringBuilder builder = new StringBuilder();
-        final int length = value.length();
-        for (int i = 0; i < length; i++) {
-            char ch = value.charAt(i);
-            if (ch == '\\' && i < length - 1) {
-                char nextCh = value.charAt(i + 1);
-                final String unescapedString;
-                if (VCardConfig.isVersion40(vcardType)) {
-                    unescapedString = VCardParserImpl_V40.unescapeCharacter(nextCh);
-                } else if (VCardConfig.isVersion30(vcardType)) {
-                    unescapedString = VCardParserImpl_V30.unescapeCharacter(nextCh);
-                } else {
-                    if (!VCardConfig.isVersion21(vcardType)) {
-                        // Unknown vCard type
-                        Log.w(LOG_TAG, "Unknown vCard type");
-                    }
-                    unescapedString = VCardParserImpl_V21.unescapeCharacter(nextCh);
-                }
-
-                if (unescapedString != null) {
-                    builder.append(unescapedString);
-                    i++;
-                } else {
-                    builder.append(ch);
-                }
-            } else if (ch == ';') {
-                list.add(builder.toString());
-                builder = new StringBuilder();
-            } else {
-                builder.append(ch);
-            }
-        }
-        list.add(builder.toString());
-        return list;
-    }
-
-    public static boolean containsOnlyPrintableAscii(final String...values) {
-        if (values == null) {
-            return true;
-        }
-        return containsOnlyPrintableAscii(Arrays.asList(values));
-    }
-
-    public static boolean containsOnlyPrintableAscii(final Collection<String> values) {
-        if (values == null) {
-            return true;
-        }
-        for (final String value : values) {
-            if (TextUtils.isEmpty(value)) {
-                continue;
-            }
-            if (!TextUtils.isPrintableAsciiOnly(value)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * <p>
-     * This is useful when checking the string should be encoded into quoted-printable
-     * or not, which is required by vCard 2.1.
-     * </p>
-     * <p>
-     * See the definition of "7bit" in vCard 2.1 spec for more information.
-     * </p>
-     */
-    public static boolean containsOnlyNonCrLfPrintableAscii(final String...values) {
-        if (values == null) {
-            return true;
-        }
-        return containsOnlyNonCrLfPrintableAscii(Arrays.asList(values));
-    }
-
-    public static boolean containsOnlyNonCrLfPrintableAscii(final Collection<String> values) {
-        if (values == null) {
-            return true;
-        }
-        final int asciiFirst = 0x20;
-        final int asciiLast = 0x7E;  // included
-        for (final String value : values) {
-            if (TextUtils.isEmpty(value)) {
-                continue;
-            }
-            final int length = value.length();
-            for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
-                final int c = value.codePointAt(i);
-                if (!(asciiFirst <= c && c <= asciiLast)) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    private static final Set<Character> sUnAcceptableAsciiInV21WordSet =
-        new HashSet<Character>(Arrays.asList('[', ']', '=', ':', '.', ',', ' '));
-
-    /**
-     * <p>
-     * This is useful since vCard 3.0 often requires the ("X-") properties and groups
-     * should contain only alphabets, digits, and hyphen.
-     * </p>
-     * <p> 
-     * Note: It is already known some devices (wrongly) outputs properties with characters
-     *       which should not be in the field. One example is "X-GOOGLE TALK". We accept
-     *       such kind of input but must never output it unless the target is very specific
-     *       to the device which is able to parse the malformed input.
-     * </p>
-     */
-    public static boolean containsOnlyAlphaDigitHyphen(final String...values) {
-        if (values == null) {
-            return true;
-        }
-        return containsOnlyAlphaDigitHyphen(Arrays.asList(values));
-    }
-
-    public static boolean containsOnlyAlphaDigitHyphen(final Collection<String> values) {
-        if (values == null) {
-            return true;
-        }
-        final int upperAlphabetFirst = 0x41;  // A
-        final int upperAlphabetAfterLast = 0x5b;  // [
-        final int lowerAlphabetFirst = 0x61;  // a
-        final int lowerAlphabetAfterLast = 0x7b;  // {
-        final int digitFirst = 0x30;  // 0
-        final int digitAfterLast = 0x3A;  // :
-        final int hyphen = '-';
-        for (final String str : values) {
-            if (TextUtils.isEmpty(str)) {
-                continue;
-            }
-            final int length = str.length();
-            for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
-                int codepoint = str.codePointAt(i);
-                if (!((lowerAlphabetFirst <= codepoint && codepoint < lowerAlphabetAfterLast) ||
-                    (upperAlphabetFirst <= codepoint && codepoint < upperAlphabetAfterLast) ||
-                    (digitFirst <= codepoint && codepoint < digitAfterLast) ||
-                    (codepoint == hyphen))) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    public static boolean containsOnlyWhiteSpaces(final String...values) {
-        if (values == null) {
-            return true;
-        }
-        return containsOnlyWhiteSpaces(Arrays.asList(values));
-    }
-
-    public static boolean containsOnlyWhiteSpaces(final Collection<String> values) {
-        if (values == null) {
-            return true;
-        }
-        for (final String str : values) {
-            if (TextUtils.isEmpty(str)) {
-                continue;
-            }
-            final int length = str.length();
-            for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
-                if (!Character.isWhitespace(str.codePointAt(i))) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    /**
-     * <p>
-     * Returns true when the given String is categorized as "word" specified in vCard spec 2.1.
-     * </p>
-     * <p>
-     * vCard 2.1 specifies:<br />
-     * word = &lt;any printable 7bit us-ascii except []=:., &gt;
-     * </p>
-     */
-    public static boolean isV21Word(final String value) {
-        if (TextUtils.isEmpty(value)) {
-            return true;
-        }
-        final int asciiFirst = 0x20;
-        final int asciiLast = 0x7E;  // included
-        final int length = value.length();
-        for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
-            final int c = value.codePointAt(i);
-            if (!(asciiFirst <= c && c <= asciiLast) ||
-                    sUnAcceptableAsciiInV21WordSet.contains((char)c)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private static final int[] sEscapeIndicatorsV30 = new int[]{
-        ':', ';', ',', ' '
-    };
-
-    private static final int[] sEscapeIndicatorsV40 = new int[]{
-        ';', ':'
-    };
-
-    /**
-     * <P>
-     * Returns String available as parameter value in vCard 3.0.
-     * </P>
-     * <P>
-     * RFC 2426 requires vCard composer to quote parameter values when it contains
-     * semi-colon, for example (See RFC 2426 for more information).
-     * This method checks whether the given String can be used without quotes.
-     * </P>
-     * <P>
-     * Note: We remove DQUOTE inside the given value silently for now.
-     * </P>
-     */
-    public static String toStringAsV30ParamValue(String value) {
-        return toStringAsParamValue(value, sEscapeIndicatorsV30);
-    }
-
-    public static String toStringAsV40ParamValue(String value) {
-        return toStringAsParamValue(value, sEscapeIndicatorsV40);
-    }
-
-    private static String toStringAsParamValue(String value, final int[] escapeIndicators) {
-        if (TextUtils.isEmpty(value)) {
-            value = "";
-        }
-        final int asciiFirst = 0x20;
-        final int asciiLast = 0x7E;  // included
-        final StringBuilder builder = new StringBuilder();
-        final int length = value.length();
-        boolean needQuote = false;
-        for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
-            final int codePoint = value.codePointAt(i);
-            if (codePoint < asciiFirst || codePoint == '"') {
-                // CTL characters and DQUOTE are never accepted. Remove them.
-                continue;
-            }
-            builder.appendCodePoint(codePoint);
-            for (int indicator : escapeIndicators) {
-                if (codePoint == indicator) {
-                    needQuote = true;
-                    break;
-                }
-            }
-        }
-
-        final String result = builder.toString();
-        return ((result.isEmpty() || VCardUtils.containsOnlyWhiteSpaces(result))
-                ? ""
-                : (needQuote ? ('"' + result + '"')
-                : result));
-    }
-
-    public static String toHalfWidthString(final String orgString) {
-        if (TextUtils.isEmpty(orgString)) {
-            return null;
-        }
-        final StringBuilder builder = new StringBuilder();
-        final int length = orgString.length();
-        for (int i = 0; i < length; i = orgString.offsetByCodePoints(i, 1)) {
-            // All Japanese character is able to be expressed by char.
-            // Do not need to use String#codepPointAt().
-            final char ch = orgString.charAt(i);
-            final String halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch);
-            if (halfWidthText != null) {
-                builder.append(halfWidthText);
-            } else {
-                builder.append(ch);
-            }
-        }
-        return builder.toString();
-    }
-
-    /**
-     * Guesses the format of input image. Currently just the first few bytes are used.
-     * The type "GIF", "PNG", or "JPEG" is returned when possible. Returns null when
-     * the guess failed.
-     * @param input Image as byte array.
-     * @return The image type or null when the type cannot be determined.
-     */
-    public static String guessImageType(final byte[] input) {
-        if (input == null) {
-            return null;
-        }
-        if (input.length >= 3 && input[0] == 'G' && input[1] == 'I' && input[2] == 'F') {
-            return "GIF";
-        } else if (input.length >= 4 && input[0] == (byte) 0x89
-                && input[1] == 'P' && input[2] == 'N' && input[3] == 'G') {
-            // Note: vCard 2.1 officially does not support PNG, but we may have it and
-            //       using X- word like "X-PNG" may not let importers know it is PNG.
-            //       So we use the String "PNG" as is...
-            return "PNG";
-        } else if (input.length >= 2 && input[0] == (byte) 0xff
-                && input[1] == (byte) 0xd8) {
-            return "JPEG";
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * @return True when all the given values are null or empty Strings.
-     */
-    public static boolean areAllEmpty(final String...values) {
-        if (values == null) {
-            return true;
-        }
-
-        for (final String value : values) {
-            if (!TextUtils.isEmpty(value)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    //// The methods bellow may be used by unit test.
-
-    /**
-     * Unquotes given Quoted-Printable value. value must not be null.
-     */
-    public static String parseQuotedPrintable(
-            final String value, boolean strictLineBreaking,
-            String sourceCharset, String targetCharset) {
-        // "= " -> " ", "=\t" -> "\t".
-        // Previous code had done this replacement. Keep on the safe side.
-        final String quotedPrintable;
-        {
-            final StringBuilder builder = new StringBuilder();
-            final int length = value.length();
-            for (int i = 0; i < length; i++) {
-                char ch = value.charAt(i);
-                if (ch == '=' && i < length - 1) {
-                    char nextCh = value.charAt(i + 1);
-                    if (nextCh == ' ' || nextCh == '\t') {
-                        builder.append(nextCh);
-                        i++;
-                        continue;
-                    }
-                }
-                builder.append(ch);
-            }
-            quotedPrintable = builder.toString();
-        }
-
-        String[] lines;
-        if (strictLineBreaking) {
-            lines = quotedPrintable.split("\r\n");
-        } else {
-            StringBuilder builder = new StringBuilder();
-            final 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);
-                }
-            }
-            final String lastLine = builder.toString();
-            if (lastLine.length() > 0) {
-                list.add(lastLine);
-            }
-            lines = list.toArray(new String[0]);
-        }
-
-        final StringBuilder builder = new StringBuilder();
-        for (String line : lines) {
-            if (line.endsWith("=")) {
-                line = line.substring(0, line.length() - 1);
-            }
-            builder.append(line);
-        }
-
-        final String rawString = builder.toString();
-        if (TextUtils.isEmpty(rawString)) {
-            Log.w(LOG_TAG, "Given raw string is empty.");
-        }
-
-        byte[] rawBytes = null;
-        try {
-            rawBytes = rawString.getBytes(sourceCharset); 
-        } catch (UnsupportedEncodingException e) {
-            Log.w(LOG_TAG, "Failed to decode: " + sourceCharset);
-            rawBytes = rawString.getBytes();
-        }
-
-        byte[] decodedBytes = null;
-        try {
-            decodedBytes = QuotedPrintableCodec.decodeQuotedPrintable(rawBytes);
-        } catch (DecoderException e) {
-            Log.e(LOG_TAG, "DecoderException is thrown.");
-            decodedBytes = rawBytes;
-        }
-
-        try {
-            return new String(decodedBytes, targetCharset);
-        } catch (UnsupportedEncodingException e) {
-            Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
-            return new String(decodedBytes);
-        }
-    }
-
-    public static final VCardParser getAppropriateParser(int vcardType)
-            throws VCardException {
-        if (VCardConfig.isVersion21(vcardType)) {
-            return new VCardParser_V21();
-        } else if (VCardConfig.isVersion30(vcardType)) {
-            return new VCardParser_V30();
-        } else if (VCardConfig.isVersion40(vcardType)) {
-            return new VCardParser_V40();
-        } else {
-            throw new VCardException("Version is not specified");
-        }
-    }
-
-    public static final String convertStringCharset(
-            String originalString, String sourceCharset, String targetCharset) {
-        if (sourceCharset.equalsIgnoreCase(targetCharset)) {
-            return originalString;
-        }
-        final Charset charset = Charset.forName(sourceCharset);
-        final ByteBuffer byteBuffer = charset.encode(originalString);
-        // byteBuffer.array() "may" return byte array which is larger than
-        // byteBuffer.remaining(). Here, we keep on the safe side.
-        final 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;
-        }
-    }
-
-    // TODO: utilities for vCard 4.0: datetime, timestamp, integer, float, and boolean
-
-    private VCardUtils() {
-    }
-}
diff --git a/core/java/android/pim/vcard/exception/VCardAgentNotSupportedException.java b/core/java/android/pim/vcard/exception/VCardAgentNotSupportedException.java
deleted file mode 100644
index e72c7df..0000000
--- a/core/java/android/pim/vcard/exception/VCardAgentNotSupportedException.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.exception;
-
-public class VCardAgentNotSupportedException extends VCardNotSupportedException {
-    public VCardAgentNotSupportedException() {
-        super();
-    }
-
-    public VCardAgentNotSupportedException(String message) {
-        super(message);
-    }
-
-}
\ No newline at end of file
diff --git a/core/java/android/pim/vcard/exception/VCardException.java b/core/java/android/pim/vcard/exception/VCardException.java
deleted file mode 100644
index e557219..0000000
--- a/core/java/android/pim/vcard/exception/VCardException.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.exception;
-
-public class VCardException extends java.lang.Exception {
-    /**
-     * Constructs a VCardException object
-     */
-    public VCardException() {
-        super();
-    }
-
-    /**
-     * Constructs a VCardException object
-     *
-     * @param message the error message
-     */
-    public VCardException(String message) {
-        super(message);
-    }
-
-}
diff --git a/core/java/android/pim/vcard/exception/VCardInvalidCommentLineException.java b/core/java/android/pim/vcard/exception/VCardInvalidCommentLineException.java
deleted file mode 100644
index 67db62c..0000000
--- a/core/java/android/pim/vcard/exception/VCardInvalidCommentLineException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.pim.vcard.exception;
-
-/**
- * Thrown when the vCard has some line starting with '#'. In the specification,
- * both vCard 2.1 and vCard 3.0 does not allow such line, but some actual exporter emit
- * such lines.
- */
-public class VCardInvalidCommentLineException extends VCardInvalidLineException {
-    public VCardInvalidCommentLineException() {
-        super();
-    }
-
-    public VCardInvalidCommentLineException(final String message) {
-        super(message);
-    }
-}
diff --git a/core/java/android/pim/vcard/exception/VCardInvalidLineException.java b/core/java/android/pim/vcard/exception/VCardInvalidLineException.java
deleted file mode 100644
index b80584b..0000000
--- a/core/java/android/pim/vcard/exception/VCardInvalidLineException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.exception;
-
-/**
- * Thrown when the vCard has some line starting with '#'. In the specification,
- * both vCard 2.1 and vCard 3.0 does not allow such line, but some actual exporter emit
- * such lines.
- */
-public class VCardInvalidLineException extends VCardException {
-    public VCardInvalidLineException() {
-        super();
-    }
-
-    public VCardInvalidLineException(final String message) {
-        super(message);
-    }
-}
diff --git a/core/java/android/pim/vcard/exception/VCardNestedException.java b/core/java/android/pim/vcard/exception/VCardNestedException.java
deleted file mode 100644
index 503c2fb..0000000
--- a/core/java/android/pim/vcard/exception/VCardNestedException.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.pim.vcard.exception;
-
-/**
- * VCardException thrown when VCard is nested without VCardParser's being notified.
- */
-public class VCardNestedException extends VCardNotSupportedException {
-    public VCardNestedException() {
-        super();
-    }
-    public VCardNestedException(String message) {
-        super(message);
-    }
-}
diff --git a/core/java/android/pim/vcard/exception/VCardNotSupportedException.java b/core/java/android/pim/vcard/exception/VCardNotSupportedException.java
deleted file mode 100644
index 616aa7763..0000000
--- a/core/java/android/pim/vcard/exception/VCardNotSupportedException.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.exception;
-
-/**
- * The exception which tells that the input VCard is probably valid from the view of
- * specification but not supported in the current framework for now.
- * 
- * This is a kind of a good news from the view of development.
- * It may be good to ask users to send a report with the VCard example
- * for the future development.
- */
-public class VCardNotSupportedException extends VCardException {
-    public VCardNotSupportedException() {
-        super();
-    }
-    public VCardNotSupportedException(String message) {
-        super(message);
-    }
-}
\ No newline at end of file
diff --git a/core/java/android/pim/vcard/exception/VCardVersionException.java b/core/java/android/pim/vcard/exception/VCardVersionException.java
deleted file mode 100644
index 0709fe4..0000000
--- a/core/java/android/pim/vcard/exception/VCardVersionException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.exception;
-
-/**
- * VCardException used only when the version of the vCard is different. 
- */
-public class VCardVersionException extends VCardException {
-    public VCardVersionException() {
-        super();
-    }
-    public VCardVersionException(String message) {
-        super(message);
-    }
-}
diff --git a/core/java/android/pim/vcard/exception/package.html b/core/java/android/pim/vcard/exception/package.html
deleted file mode 100644
index 26b8a32..0000000
--- a/core/java/android/pim/vcard/exception/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<HTML>
-<BODY>
-{@hide}
-</BODY>
-</HTML>
\ No newline at end of file
diff --git a/core/java/android/pim/vcard/package.html b/core/java/android/pim/vcard/package.html
deleted file mode 100644
index 26b8a32..0000000
--- a/core/java/android/pim/vcard/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<HTML>
-<BODY>
-{@hide}
-</BODY>
-</HTML>
\ No newline at end of file
diff --git a/core/java/android/preference/MultiSelectListPreference.java b/core/java/android/preference/MultiSelectListPreference.java
new file mode 100644
index 0000000..42d555cf
--- /dev/null
+++ b/core/java/android/preference/MultiSelectListPreference.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.preference;
+
+import android.app.AlertDialog.Builder;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A {@link Preference} that displays a list of entries as
+ * a dialog.
+ * <p>
+ * This preference will store a set of strings into the SharedPreferences.
+ * This set will contain one or more values from the
+ * {@link #setEntryValues(CharSequence[])} array.
+ * 
+ * @attr ref android.R.styleable#MultiSelectListPreference_entries
+ * @attr ref android.R.styleable#MultiSelectListPreference_entryValues
+ */
+public class MultiSelectListPreference extends DialogPreference {
+    private CharSequence[] mEntries;
+    private CharSequence[] mEntryValues;
+    private Set<String> mValues = new HashSet<String>();
+    private Set<String> mNewValues = new HashSet<String>();
+    private boolean mPreferenceChanged;
+    
+    public MultiSelectListPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.MultiSelectListPreference, 0, 0);
+        mEntries = a.getTextArray(com.android.internal.R.styleable.MultiSelectListPreference_entries);
+        mEntryValues = a.getTextArray(com.android.internal.R.styleable.MultiSelectListPreference_entryValues);
+        a.recycle();
+    }
+    
+    public MultiSelectListPreference(Context context) {
+        this(context, null);
+    }
+    
+    /**
+     * Sets the human-readable entries to be shown in the list. This will be
+     * shown in subsequent dialogs.
+     * <p>
+     * Each entry must have a corresponding index in
+     * {@link #setEntryValues(CharSequence[])}.
+     * 
+     * @param entries The entries.
+     * @see #setEntryValues(CharSequence[])
+     */
+    public void setEntries(CharSequence[] entries) {
+        mEntries = entries;
+    }
+    
+    /**
+     * @see #setEntries(CharSequence[])
+     * @param entriesResId The entries array as a resource.
+     */
+    public void setEntries(int entriesResId) {
+        setEntries(getContext().getResources().getTextArray(entriesResId));
+    }
+    
+    /**
+     * The list of entries to be shown in the list in subsequent dialogs.
+     * 
+     * @return The list as an array.
+     */
+    public CharSequence[] getEntries() {
+        return mEntries;
+    }
+    
+    /**
+     * The array to find the value to save for a preference when an entry from
+     * entries is selected. If a user clicks on the second item in entries, the
+     * second item in this array will be saved to the preference.
+     * 
+     * @param entryValues The array to be used as values to save for the preference.
+     */
+    public void setEntryValues(CharSequence[] entryValues) {
+        mEntryValues = entryValues;
+    }
+
+    /**
+     * @see #setEntryValues(CharSequence[])
+     * @param entryValuesResId The entry values array as a resource.
+     */
+    public void setEntryValues(int entryValuesResId) {
+        setEntryValues(getContext().getResources().getTextArray(entryValuesResId));
+    }
+    
+    /**
+     * Returns the array of values to be saved for the preference.
+     * 
+     * @return The array of values.
+     */
+    public CharSequence[] getEntryValues() {
+        return mEntryValues;
+    }
+    
+    /**
+     * Sets the value of the key. This should contain entries in
+     * {@link #getEntryValues()}.
+     * 
+     * @param values The values to set for the key.
+     */
+    public void setValues(Set<String> values) {
+        mValues = values;
+        
+        persistStringSet(values);
+    }
+    
+    /**
+     * Retrieves the current value of the key.
+     */
+    public Set<String> getValues() {
+        return mValues;
+    }
+    
+    /**
+     * Returns the index of the given value (in the entry values array).
+     * 
+     * @param value The value whose index should be returned.
+     * @return The index of the value, or -1 if not found.
+     */
+    public int findIndexOfValue(String value) {
+        if (value != null && mEntryValues != null) {
+            for (int i = mEntryValues.length - 1; i >= 0; i--) {
+                if (mEntryValues[i].equals(value)) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+    
+    @Override
+    protected void onPrepareDialogBuilder(Builder builder) {
+        super.onPrepareDialogBuilder(builder);
+        
+        if (mEntries == null || mEntryValues == null) {
+            throw new IllegalStateException(
+                    "MultiSelectListPreference requires an entries array and " +
+                    "an entryValues array.");
+        }
+        
+        boolean[] checkedItems = getSelectedItems();
+        builder.setMultiChoiceItems(mEntries, checkedItems,
+                new DialogInterface.OnMultiChoiceClickListener() {
+                    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+                        if (isChecked) {
+                            mPreferenceChanged |= mNewValues.add(mEntries[which].toString());
+                        } else {
+                            mPreferenceChanged |= mNewValues.remove(mEntries[which].toString());
+                        }
+                    }
+                });
+        mNewValues.clear();
+        mNewValues.addAll(mValues);
+    }
+    
+    private boolean[] getSelectedItems() {
+        final CharSequence[] entries = mEntries;
+        final int entryCount = entries.length;
+        final Set<String> values = mValues;
+        boolean[] result = new boolean[entryCount];
+        
+        for (int i = 0; i < entryCount; i++) {
+            result[i] = values.contains(entries[i].toString());
+        }
+        
+        return result;
+    }
+    
+    @Override
+    protected void onDialogClosed(boolean positiveResult) {
+        super.onDialogClosed(positiveResult);
+        
+        if (positiveResult && mPreferenceChanged) {
+            final Set<String> values = mNewValues;
+            if (callChangeListener(values)) {
+                setValues(values);
+            }
+        }
+        mPreferenceChanged = false;
+    }
+    
+    @Override
+    protected Object onGetDefaultValue(TypedArray a, int index) {
+        final CharSequence[] defaultValues = a.getTextArray(index);
+        final int valueCount = defaultValues.length;
+        final Set<String> result = new HashSet<String>();
+        
+        for (int i = 0; i < valueCount; i++) {
+            result.add(defaultValues[i].toString());
+        }
+        
+        return result;
+    }
+    
+    @Override
+    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+        setValues(restoreValue ? getPersistedStringSet(mValues) : (Set<String>) defaultValue);
+    }
+    
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        final Parcelable superState = super.onSaveInstanceState();
+        if (isPersistent()) {
+            // No need to save instance state
+            return superState;
+        }
+        
+        final SavedState myState = new SavedState(superState);
+        myState.values = getValues();
+        return myState;
+    }
+    
+    private static class SavedState extends BaseSavedState {
+        Set<String> values;
+        
+        public SavedState(Parcel source) {
+            super(source);
+            values = new HashSet<String>();
+            String[] strings = source.readStringArray();
+            
+            final int stringCount = strings.length;
+            for (int i = 0; i < stringCount; i++) {
+                values.add(strings[i]);
+            }
+        }
+        
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+        
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeStringArray(values.toArray(new String[0]));
+        }
+        
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+            
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+}
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 36d676a..12b9f0c 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -16,8 +16,7 @@
 
 package android.preference;
 
-import java.util.ArrayList;
-import java.util.List;
+import com.android.internal.util.CharSequences;
 
 import android.content.Context;
 import android.content.Intent;
@@ -28,7 +27,6 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import com.android.internal.util.CharSequences;
 import android.view.AbsSavedState;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -36,6 +34,10 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
 /**
  * Represents the basic Preference UI building
  * block displayed by a {@link PreferenceActivity} in the form of a
@@ -54,6 +56,7 @@
  * @attr ref android.R.styleable#Preference_title
  * @attr ref android.R.styleable#Preference_summary
  * @attr ref android.R.styleable#Preference_order
+ * @attr ref android.R.styleable#Preference_fragment
  * @attr ref android.R.styleable#Preference_layout
  * @attr ref android.R.styleable#Preference_widgetLayout
  * @attr ref android.R.styleable#Preference_enabled
@@ -86,6 +89,8 @@
     private CharSequence mSummary;
     private String mKey;
     private Intent mIntent;
+    private String mFragment;
+    private Bundle mExtras;
     private boolean mEnabled = true;
     private boolean mSelectable = true;
     private boolean mRequiresKey;
@@ -208,6 +213,10 @@
                     mOrder = a.getInt(attr, mOrder);
                     break;
 
+                case com.android.internal.R.styleable.Preference_fragment:
+                    mFragment = a.getString(attr);
+                    break;
+
                 case com.android.internal.R.styleable.Preference_layout:
                     mLayoutResId = a.getResourceId(attr, mLayoutResId);
                     break;
@@ -313,6 +322,44 @@
     }
 
     /**
+     * Sets the class name of a fragment to be shown when this Preference is clicked.
+     *
+     * @param fragment The class name of the fragment associated with this Preference.
+     */
+    public void setFragment(String fragment) {
+        mFragment = fragment;
+    }
+
+    /**
+     * Return the fragment class name associated with this Preference.
+     *
+     * @return The fragment class name last set via {@link #setFragment} or XML.
+     */
+    public String getFragment() {
+        return mFragment;
+    }
+
+    /**
+     * Return the extras Bundle object associated with this preference, creating
+     * a new Bundle if there currently isn't one.  You can use this to get and
+     * set individual extra key/value pairs.
+     */
+    public Bundle getExtras() {
+        if (mExtras == null) {
+            mExtras = new Bundle();
+        }
+        return mExtras;
+    }
+
+    /**
+     * Return the extras Bundle object associated with this preference,
+     * returning null if there is not currently one.
+     */
+    public Bundle peekExtras() {
+        return mExtras;
+    }
+
+    /**
      * Sets the layout resource that is inflated as the {@link View} to be shown
      * for this Preference. In most cases, the default layout is sufficient for
      * custom Preference objects and only the widget layout needs to be changed.
@@ -1257,6 +1304,61 @@
     }
     
     /**
+     * Attempts to persist a set of Strings to the {@link android.content.SharedPreferences}.
+     * <p>
+     * This will check if this Preference is persistent, get an editor from
+     * the {@link PreferenceManager}, put in the strings, and check if we should commit (and
+     * commit if so).
+     * 
+     * @param values The values to persist.
+     * @return True if the Preference is persistent. (This is not whether the
+     *         value was persisted, since we may not necessarily commit if there
+     *         will be a batch commit later.)
+     * @see #getPersistedString(Set)
+     * 
+     * @hide Pending API approval
+     */
+    protected boolean persistStringSet(Set<String> values) {
+        if (shouldPersist()) {
+            // Shouldn't store null
+            if (values.equals(getPersistedStringSet(null))) {
+                // It's already there, so the same as persisting
+                return true;
+            }
+            
+            SharedPreferences.Editor editor = mPreferenceManager.getEditor();
+            editor.putStringSet(mKey, values);
+            tryCommit(editor);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Attempts to get a persisted set of Strings from the
+     * {@link android.content.SharedPreferences}.
+     * <p>
+     * This will check if this Preference is persistent, get the SharedPreferences
+     * from the {@link PreferenceManager}, and get the value.
+     * 
+     * @param defaultReturnValue The default value to return if either the
+     *            Preference is not persistent or the Preference is not in the
+     *            shared preferences.
+     * @return The value from the SharedPreferences or the default return
+     *         value.
+     * @see #persistStringSet(Set)
+     * 
+     * @hide Pending API approval
+     */
+    protected Set<String> getPersistedStringSet(Set<String> defaultReturnValue) {
+        if (!shouldPersist()) {
+            return defaultReturnValue;
+        }
+        
+        return mPreferenceManager.getSharedPreferences().getStringSet(mKey, defaultReturnValue);
+    }
+    
+    /**
      * Attempts to persist an int to the {@link android.content.SharedPreferences}.
      * 
      * @param value The value to persist.
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 726793d..d3ab844 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -16,135 +16,780 @@
 
 package android.preference;
 
-import android.app.Activity;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.Fragment;
+import android.app.FragmentBreadCrumbs;
+import android.app.FragmentTransaction;
 import android.app.ListActivity;
+import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Xml;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * Shows a hierarchy of {@link Preference} objects as
- * lists, possibly spanning multiple screens. These preferences will
- * automatically save to {@link SharedPreferences} as the user interacts with
- * them. To retrieve an instance of {@link SharedPreferences} that the
- * preference hierarchy in this activity will use, call
- * {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)}
- * with a context in the same package as this activity.
- * <p>
- * Furthermore, the preferences shown will follow the visual style of system
- * preferences. It is easy to create a hierarchy of preferences (that can be
- * shown on multiple screens) via XML. For these reasons, it is recommended to
- * use this activity (as a superclass) to deal with preferences in applications.
- * <p>
- * A {@link PreferenceScreen} object should be at the top of the preference
- * hierarchy. Furthermore, subsequent {@link PreferenceScreen} in the hierarchy
- * denote a screen break--that is the preferences contained within subsequent
- * {@link PreferenceScreen} should be shown on another screen. The preference
- * framework handles showing these other screens from the preference hierarchy.
- * <p>
- * The preference hierarchy can be formed in multiple ways:
- * <li> From an XML file specifying the hierarchy
- * <li> From different {@link Activity Activities} that each specify its own
- * preferences in an XML file via {@link Activity} meta-data
- * <li> From an object hierarchy rooted with {@link PreferenceScreen}
- * <p>
- * To inflate from XML, use the {@link #addPreferencesFromResource(int)}. The
- * root element should be a {@link PreferenceScreen}. Subsequent elements can point
- * to actual {@link Preference} subclasses. As mentioned above, subsequent
- * {@link PreferenceScreen} in the hierarchy will result in the screen break.
- * <p>
- * To specify an {@link Intent} to query {@link Activity Activities} that each
- * have preferences, use {@link #addPreferencesFromIntent}. Each
- * {@link Activity} can specify meta-data in the manifest (via the key
- * {@link PreferenceManager#METADATA_KEY_PREFERENCES}) that points to an XML
- * resource. These XML resources will be inflated into a single preference
- * hierarchy and shown by this activity.
- * <p>
- * To specify an object hierarchy rooted with {@link PreferenceScreen}, use
- * {@link #setPreferenceScreen(PreferenceScreen)}.
- * <p>
- * As a convenience, this activity implements a click listener for any
- * preference in the current hierarchy, see
- * {@link #onPreferenceTreeClick(PreferenceScreen, Preference)}.
- * 
- * @see Preference
- * @see PreferenceScreen
+ * This is the base class for an activity to show a hierarchy of preferences
+ * to the user.  Prior to {@link android.os.Build.VERSION_CODES#HONEYCOMB}
+ * this class only allowed the display of a single set of preference; this
+ * functionality should now be found in the new {@link PreferenceFragment}
+ * class.  If you are using PreferenceActivity in its old mode, the documentation
+ * there applies to the deprecated APIs here.
+ *
+ * <p>This activity shows one or more headers of preferences, each of with
+ * is associated with a {@link PreferenceFragment} to display the preferences
+ * of that header.  The actual layout and display of these associations can
+ * however vary; currently there are two major approaches it may take:
+ *
+ * <ul>
+ * <li>On a small screen it may display only the headers as a single list
+ * when first launched.  Selecting one of the header items will re-launch
+ * the activity with it only showing the PreferenceFragment of that header.
+ * <li>On a large screen in may display both the headers and current
+ * PreferenceFragment together as panes.  Selecting a header item switches
+ * to showing the correct PreferenceFragment for that item.
+ * </ul>
+ *
+ * <p>Subclasses of PreferenceActivity should implement
+ * {@link #onBuildHeaders} to populate the header list with the desired
+ * items.  Doing this implicitly switches the class into its new "headers
+ * + fragments" mode rather than the old style of just showing a single
+ * preferences list.
+ *
+ * <a name="SampleCode"></a>
+ * <h3>Sample Code</h3>
+ *
+ * <p>The following sample code shows a simple preference activity that
+ * has two different sets of preferences.  The implementation, consisting
+ * of the activity itself as well as its two preference fragments is:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/PreferenceWithHeaders.java
+ *      activity}
+ *
+ * <p>The preference_headers resource describes the headers to be displayed
+ * and the fragments associated with them.  It is:
+ *
+ * {@sample development/samples/ApiDemos/res/xml/preference_headers.xml headers}
+ *
+ * <p>The first header is shown by Prefs1Fragment, which populates itself
+ * from the following XML resource:</p>
+ *
+ * {@sample development/samples/ApiDemos/res/xml/fragmented_preferences.xml preferences}
+ *
+ * <p>Note that this XML resource contains a preference screen holding another
+ * fragment, the Prefs1FragmentInner implemented here.  This allows the user
+ * to traverse down a hierarchy of preferences; pressing back will pop each
+ * fragment off the stack to return to the previous preferences.
+ *
+ * <p>See {@link PreferenceFragment} for information on implementing the
+ * fragments themselves.
  */
 public abstract class PreferenceActivity extends ListActivity implements
-        PreferenceManager.OnPreferenceTreeClickListener {
-    
-    private static final String PREFERENCES_TAG = "android:preferences";
-    
+        PreferenceManager.OnPreferenceTreeClickListener,
+        PreferenceFragment.OnPreferenceStartFragmentCallback {
+    private static final String TAG = "PreferenceActivity";
+
+    // Constants for state save/restore
+    private static final String HEADERS_TAG = ":android:headers";
+    private static final String CUR_HEADER_TAG = ":android:cur_header";
+    private static final String PREFERENCES_TAG = ":android:preferences";
+
+    /**
+     * When starting this activity, the invoking Intent can contain this extra
+     * string to specify which fragment should be initially displayed.
+     */
+    public static final String EXTRA_SHOW_FRAGMENT = ":android:show_fragment";
+
+    /**
+     * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
+     * this extra can also be specify to supply a Bundle of arguments to pass
+     * to that fragment when it is instantiated during the initial creation
+     * of PreferenceActivity.
+     */
+    public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":android:show_fragment_args";
+
+    /**
+     * When starting this activity, the invoking Intent can contain this extra
+     * boolean that the header list should not be displayed.  This is most often
+     * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
+     * the activity to display a specific fragment that the user has navigated
+     * to.
+     */
+    public static final String EXTRA_NO_HEADERS = ":android:no_headers";
+
+    private static final String BACK_STACK_PREFS = ":android:prefs";
+
+    // extras that allow any preference activity to be launched as part of a wizard
+
+    // show Back and Next buttons? takes boolean parameter
+    // Back will then return RESULT_CANCELED and Next RESULT_OK
+    private static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
+
+    // add a Skip button?
+    private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
+
+    // specify custom text for the Back or Next buttons, or cause a button to not appear
+    // at all by setting it to null
+    private static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
+    private static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
+
+    // --- State for new mode when showing a list of headers + prefs fragment
+
+    private final ArrayList<Header> mHeaders = new ArrayList<Header>();
+
+    private HeaderAdapter mAdapter;
+
+    private FrameLayout mListFooter;
+
+    private View mPrefsContainer;
+
+    private FragmentBreadCrumbs mFragmentBreadCrumbs;
+
+    private boolean mSinglePane;
+
+    private Header mCurHeader;
+
+    // --- State for old mode when showing a single preference list
+
     private PreferenceManager mPreferenceManager;
-    
+
     private Bundle mSavedInstanceState;
 
+    // --- Common state
+
+    private Button mNextButton;
+
     /**
      * The starting request code given out to preference framework.
      */
     private static final int FIRST_REQUEST_CODE = 100;
-    
-    private static final int MSG_BIND_PREFERENCES = 0;
+
+    private static final int MSG_BIND_PREFERENCES = 1;
+    private static final int MSG_BUILD_HEADERS = 2;
     private Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                
-                case MSG_BIND_PREFERENCES:
+                case MSG_BIND_PREFERENCES: {
                     bindPreferences();
-                    break;
+                } break;
+                case MSG_BUILD_HEADERS: {
+                    ArrayList<Header> oldHeaders = new ArrayList<Header>(mHeaders);
+                    mHeaders.clear();
+                    onBuildHeaders(mHeaders);
+                    if (mAdapter != null) {
+                        mAdapter.notifyDataSetChanged();
+                    }
+                    Header header = onGetNewHeader();
+                    if (header != null && header.fragment != null) {
+                        Header mappedHeader = findBestMatchingHeader(header, oldHeaders);
+                        if (mappedHeader == null || mCurHeader != mappedHeader) {
+                            switchToHeader(header);
+                        }
+                    } else if (mCurHeader != null) {
+                        Header mappedHeader = findBestMatchingHeader(mCurHeader, mHeaders);
+                        if (mappedHeader != null) {
+                            setSelectedHeader(mappedHeader);
+                        } else {
+                            switchToHeader(null);
+                        }
+                    }
+                } break;
             }
         }
     };
 
+    private static class HeaderAdapter extends ArrayAdapter<Header> {
+        private static class HeaderViewHolder {
+            ImageView icon;
+            TextView title;
+            TextView summary;
+        }
+
+        private LayoutInflater mInflater;
+
+        public HeaderAdapter(Context context, List<Header> objects) {
+            super(context, 0, objects);
+            mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            HeaderViewHolder holder;
+            View view;
+
+            if (convertView == null) {
+                view = mInflater.inflate(com.android.internal.R.layout.preference_header_item,
+                        parent, false);
+                holder = new HeaderViewHolder();
+                holder.icon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
+                holder.title = (TextView) view.findViewById(com.android.internal.R.id.title);
+                holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary);
+                view.setTag(holder);
+            } else {
+                view = convertView;
+                holder = (HeaderViewHolder) view.getTag();
+            }
+
+            // All view fields must be updated every time, because the view may be recycled 
+            Header header = getItem(position);
+            holder.icon.setImageResource(header.iconRes);
+            holder.title.setText(header.title);
+            if (TextUtils.isEmpty(header.summary)) {
+                holder.summary.setVisibility(View.GONE);
+            } else {
+                holder.summary.setVisibility(View.VISIBLE);
+                holder.summary.setText(header.summary);
+            }
+
+            return view;
+        }
+    }
+
+    /**
+     * Default value for {@link Header#id Header.id} indicating that no
+     * identifier value is set.  All other values (including those below -1)
+     * are valid.
+     */
+    public static final long HEADER_ID_UNDEFINED = -1;
+    
+    /**
+     * Description of a single Header item that the user can select.
+     */
+    public static final class Header implements Parcelable {
+        /**
+         * Identifier for this header, to correlate with a new list when
+         * it is updated.  The default value is
+         * {@link PreferenceActivity#HEADER_ID_UNDEFINED}, meaning no id.
+         * @attr ref android.R.styleable#PreferenceHeader_id
+         */
+        public long id = HEADER_ID_UNDEFINED;
+
+        /**
+         * Title of the header that is shown to the user.
+         * @attr ref android.R.styleable#PreferenceHeader_title
+         */
+        public CharSequence title;
+
+        /**
+         * Optional summary describing what this header controls.
+         * @attr ref android.R.styleable#PreferenceHeader_summary
+         */
+        public CharSequence summary;
+
+        /**
+         * Optional text to show as the title in the bread crumb.
+         * @attr ref android.R.styleable#PreferenceHeader_breadCrumbTitle
+         */
+        public CharSequence breadCrumbTitle;
+
+        /**
+         * Optional text to show as the short title in the bread crumb.
+         * @attr ref android.R.styleable#PreferenceHeader_breadCrumbShortTitle
+         */
+        public CharSequence breadCrumbShortTitle;
+
+        /**
+         * Optional icon resource to show for this header.
+         * @attr ref android.R.styleable#PreferenceHeader_icon
+         */
+        public int iconRes;
+
+        /**
+         * Full class name of the fragment to display when this header is
+         * selected.
+         * @attr ref android.R.styleable#PreferenceHeader_fragment
+         */
+        public String fragment;
+
+        /**
+         * Optional arguments to supply to the fragment when it is
+         * instantiated.
+         */
+        public Bundle fragmentArguments;
+
+        /**
+         * Intent to launch when the preference is selected.
+         */
+        public Intent intent;
+
+        /**
+         * Optional additional data for use by subclasses of PreferenceActivity.
+         */
+        public Bundle extras;
+
+        public Header() {
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeLong(id);
+            TextUtils.writeToParcel(title, dest, flags);
+            TextUtils.writeToParcel(summary, dest, flags);
+            TextUtils.writeToParcel(breadCrumbTitle, dest, flags);
+            TextUtils.writeToParcel(breadCrumbShortTitle, dest, flags);
+            dest.writeInt(iconRes);
+            dest.writeString(fragment);
+            dest.writeBundle(fragmentArguments);
+            if (intent != null) {
+                dest.writeInt(1);
+                intent.writeToParcel(dest, flags);
+            } else {
+                dest.writeInt(0);
+            }
+            dest.writeBundle(extras);
+        }
+
+        public void readFromParcel(Parcel in) {
+            id = in.readLong();
+            title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            breadCrumbTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            breadCrumbShortTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            iconRes = in.readInt();
+            fragment = in.readString();
+            fragmentArguments = in.readBundle();
+            if (in.readInt() != 0) {
+                intent = Intent.CREATOR.createFromParcel(in);
+            }
+            extras = in.readBundle();
+        }
+
+        Header(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public static final Creator<Header> CREATOR = new Creator<Header>() {
+            public Header createFromParcel(Parcel source) {
+                return new Header(source);
+            }
+            public Header[] newArray(int size) {
+                return new Header[size];
+            }
+        };
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         setContentView(com.android.internal.R.layout.preference_list_content);
-        
-        mPreferenceManager = onCreatePreferenceManager();
+
+        mListFooter = (FrameLayout)findViewById(com.android.internal.R.id.list_footer);
+        mPrefsContainer = findViewById(com.android.internal.R.id.prefs);
+        boolean hidingHeaders = onIsHidingHeaders();
+        mSinglePane = hidingHeaders || !onIsMultiPane();
+        String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
+        Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+
+        if (savedInstanceState != null) {
+            // We are restarting from a previous saved state; used that to
+            // initialize, instead of starting fresh.
+            ArrayList<Header> headers = savedInstanceState.getParcelableArrayList(HEADERS_TAG);
+            if (headers != null) {
+                mHeaders.addAll(headers);
+                int curHeader = savedInstanceState.getInt(CUR_HEADER_TAG,
+                        (int)HEADER_ID_UNDEFINED);
+                if (curHeader >= 0 && curHeader < mHeaders.size()) {
+                    setSelectedHeader(mHeaders.get(curHeader));
+                }
+            }
+
+        } else {
+            if (initialFragment != null && mSinglePane) {
+                // If we are just showing a fragment, we want to run in
+                // new fragment mode, but don't need to compute and show
+                // the headers.
+                switchToHeader(initialFragment, initialArguments);
+
+            } else {
+                // We need to try to build the headers.
+                onBuildHeaders(mHeaders);
+
+                // If there are headers, then at this point we need to show
+                // them and, depending on the screen, we may also show in-line
+                // the currently selected preference fragment.
+                if (mHeaders.size() > 0) {
+                    if (!mSinglePane) {
+                        if (initialFragment == null) {
+                            Header h = onGetInitialHeader();
+                            switchToHeader(h);
+                        } else {
+                            switchToHeader(initialFragment, initialArguments);
+                        }
+                    }
+                }
+            }
+        }
+
+        // The default configuration is to only show the list view.  Adjust
+        // visibility for other configurations.
+        if (initialFragment != null && mSinglePane) {
+            // Single pane, showing just a prefs fragment.
+            findViewById(com.android.internal.R.id.headers).setVisibility(View.GONE);
+            mPrefsContainer.setVisibility(View.VISIBLE);
+        } else if (mHeaders.size() > 0) {
+            mAdapter = new HeaderAdapter(this, mHeaders);
+            setListAdapter(mAdapter);
+            if (!mSinglePane) {
+                // Multi-pane.
+                getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
+                if (mCurHeader != null) {
+                    setSelectedHeader(mCurHeader);
+                }
+                mPrefsContainer.setVisibility(View.VISIBLE);
+            }
+        } else {
+            // If there are no headers, we are in the old "just show a screen
+            // of preferences" mode.
+            mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
+            mPreferenceManager.setOnPreferenceTreeClickListener(this);
+        }
+
         getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
+
+        // see if we should show Back/Next buttons
+        Intent intent = getIntent();
+        if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
+
+            findViewById(com.android.internal.R.id.button_bar).setVisibility(View.VISIBLE);
+
+            Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
+            backButton.setOnClickListener(new OnClickListener() {
+                public void onClick(View v) {
+                    setResult(RESULT_CANCELED);
+                    finish();
+                }
+            });
+            Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
+            skipButton.setOnClickListener(new OnClickListener() {
+                public void onClick(View v) {
+                    setResult(RESULT_OK);
+                    finish();
+                }
+            });
+            mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
+            mNextButton.setOnClickListener(new OnClickListener() {
+                public void onClick(View v) {
+                    setResult(RESULT_OK);
+                    finish();
+                }
+            });
+
+            // set our various button parameters
+            if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
+                String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
+                if (TextUtils.isEmpty(buttonText)) {
+                    mNextButton.setVisibility(View.GONE);
+                }
+                else {
+                    mNextButton.setText(buttonText);
+                }
+            }
+            if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
+                String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
+                if (TextUtils.isEmpty(buttonText)) {
+                    backButton.setVisibility(View.GONE);
+                }
+                else {
+                    backButton.setText(buttonText);
+                }
+            }
+            if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
+                skipButton.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+
+    /**
+     * Returns true if this activity is currently showing the header list.
+     */
+    public boolean hasHeaders() {
+        return getListView().getVisibility() == View.VISIBLE
+                && mPreferenceManager == null;
+    }
+
+    /**
+     * Returns true if this activity is showing multiple panes -- the headers
+     * and a preference fragment.
+     */
+    public boolean isMultiPane() {
+        return hasHeaders() && mPrefsContainer.getVisibility() == View.VISIBLE;
+    }
+
+    /**
+     * Called to determine if the activity should run in multi-pane mode.
+     * The default implementation returns true if the screen is large
+     * enough.
+     */
+    public boolean onIsMultiPane() {
+        Configuration config = getResources().getConfiguration();
+        if ((config.screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK)
+                == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
+            return true;
+        }
+        if ((config.screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK)
+                == Configuration.SCREENLAYOUT_SIZE_LARGE
+                && config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Called to determine whether the header list should be hidden.
+     * The default implementation returns the
+     * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
+     * This is set to false, for example, when the activity is being re-launched
+     * to show a particular preference activity.
+     */
+    public boolean onIsHidingHeaders() {
+        return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
+    }
+
+    /**
+     * Called to determine the initial header to be shown.  The default
+     * implementation simply returns the fragment of the first header.  Note
+     * that the returned Header object does not actually need to exist in
+     * your header list -- whatever its fragment is will simply be used to
+     * show for the initial UI.
+     */
+    public Header onGetInitialHeader() {
+        return mHeaders.get(0);
+    }
+
+    /**
+     * Called after the header list has been updated ({@link #onBuildHeaders}
+     * has been called and returned due to {@link #invalidateHeaders()}) to
+     * specify the header that should now be selected.  The default implementation
+     * returns null to keep whatever header is currently selected.
+     */
+    public Header onGetNewHeader() {
+        return null;
+    }
+
+    /**
+     * Called when the activity needs its list of headers build.  By
+     * implementing this and adding at least one item to the list, you
+     * will cause the activity to run in its modern fragment mode.  Note
+     * that this function may not always be called; for example, if the
+     * activity has been asked to display a particular fragment without
+     * the header list, there is no need to build the headers.
+     *
+     * <p>Typical implementations will use {@link #loadHeadersFromResource}
+     * to fill in the list from a resource.
+     *
+     * @param target The list in which to place the headers.
+     */
+    public void onBuildHeaders(List<Header> target) {
+    }
+
+    /**
+     * Call when you need to change the headers being displayed.  Will result
+     * in onBuildHeaders() later being called to retrieve the new list.
+     */
+    public void invalidateHeaders() {
+        if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
+            mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
+        }
+    }
+
+    /**
+     * Parse the given XML file as a header description, adding each
+     * parsed Header into the target list.
+     *
+     * @param resid The XML resource to load and parse.
+     * @param target The list in which the parsed headers should be placed.
+     */
+    public void loadHeadersFromResource(int resid, List<Header> target) {
+        XmlResourceParser parser = null;
+        try {
+            parser = getResources().getXml(resid);
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+
+            String nodeName = parser.getName();
+            if (!"preference-headers".equals(nodeName)) {
+                throw new RuntimeException(
+                        "XML document must start with <preference-headers> tag; found"
+                        + nodeName + " at " + parser.getPositionDescription());
+            }
+
+            Bundle curBundle = null;
+
+            final int outerDepth = parser.getDepth();
+            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                   && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+
+                nodeName = parser.getName();
+                if ("header".equals(nodeName)) {
+                    Header header = new Header();
+
+                    TypedArray sa = getResources().obtainAttributes(attrs,
+                            com.android.internal.R.styleable.PreferenceHeader);
+                    header.id = sa.getResourceId(
+                            com.android.internal.R.styleable.PreferenceHeader_id,
+                            (int)HEADER_ID_UNDEFINED);
+                    header.title = sa.getText(
+                            com.android.internal.R.styleable.PreferenceHeader_title);
+                    header.summary = sa.getText(
+                            com.android.internal.R.styleable.PreferenceHeader_summary);
+                    header.breadCrumbTitle = sa.getText(
+                            com.android.internal.R.styleable.PreferenceHeader_breadCrumbTitle);
+                    header.breadCrumbShortTitle = sa.getText(
+                            com.android.internal.R.styleable.PreferenceHeader_breadCrumbShortTitle);
+                    header.iconRes = sa.getResourceId(
+                            com.android.internal.R.styleable.PreferenceHeader_icon, 0);
+                    header.fragment = sa.getString(
+                            com.android.internal.R.styleable.PreferenceHeader_fragment);
+                    sa.recycle();
+
+                    if (curBundle == null) {
+                        curBundle = new Bundle();
+                    }
+
+                    final int innerDepth = parser.getDepth();
+                    while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                           && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+                        if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                            continue;
+                        }
+
+                        String innerNodeName = parser.getName();
+                        if (innerNodeName.equals("extra")) {
+                            getResources().parseBundleExtra("extra", attrs, curBundle);
+                            XmlUtils.skipCurrentTag(parser);
+
+                        } else if (innerNodeName.equals("intent")) {
+                            header.intent = Intent.parseIntent(getResources(), parser, attrs);
+
+                        } else {
+                            XmlUtils.skipCurrentTag(parser);
+                        }
+                    }
+
+                    if (curBundle.size() > 0) {
+                        header.fragmentArguments = curBundle;
+                        curBundle = null;
+                    }
+
+                    target.add(header);
+                } else {
+                    XmlUtils.skipCurrentTag(parser);
+                }
+            }
+
+        } catch (XmlPullParserException e) {
+            throw new RuntimeException("Error parsing headers", e);
+        } catch (IOException e) {
+            throw new RuntimeException("Error parsing headers", e);
+        } finally {
+            if (parser != null) parser.close();
+        }
+
+    }
+
+    /**
+     * Set a footer that should be shown at the bottom of the header list.
+     */
+    public void setListFooter(View view) {
+        mListFooter.removeAllViews();
+        mListFooter.addView(view, new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.MATCH_PARENT,
+                FrameLayout.LayoutParams.WRAP_CONTENT));
     }
 
     @Override
     protected void onStop() {
         super.onStop();
-        
-        mPreferenceManager.dispatchActivityStop();
+
+        if (mPreferenceManager != null) {
+            mPreferenceManager.dispatchActivityStop();
+        }
     }
 
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        
-        mPreferenceManager.dispatchActivityDestroy();
+
+        if (mPreferenceManager != null) {
+            mPreferenceManager.dispatchActivityDestroy();
+        }
     }
 
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
 
-        final PreferenceScreen preferenceScreen = getPreferenceScreen();
-        if (preferenceScreen != null) {
-            Bundle container = new Bundle();
-            preferenceScreen.saveHierarchyState(container);
-            outState.putBundle(PREFERENCES_TAG, container);
+        if (mHeaders.size() > 0) {
+            outState.putParcelableArrayList(HEADERS_TAG, mHeaders);
+            if (mCurHeader != null) {
+                int index = mHeaders.indexOf(mCurHeader);
+                if (index >= 0) {
+                    outState.putInt(CUR_HEADER_TAG, index);
+                }
+            }
+        }
+
+        if (mPreferenceManager != null) {
+            final PreferenceScreen preferenceScreen = getPreferenceScreen();
+            if (preferenceScreen != null) {
+                Bundle container = new Bundle();
+                preferenceScreen.saveHierarchyState(container);
+                outState.putBundle(PREFERENCES_TAG, container);
+            }
         }
     }
 
     @Override
     protected void onRestoreInstanceState(Bundle state) {
-        Bundle container = state.getBundle(PREFERENCES_TAG);
-        if (container != null) {
-            final PreferenceScreen preferenceScreen = getPreferenceScreen();
-            if (preferenceScreen != null) {
-                preferenceScreen.restoreHierarchyState(container);
-                mSavedInstanceState = state;
-                return;
+        if (mPreferenceManager != null) {
+            Bundle container = state.getBundle(PREFERENCES_TAG);
+            if (container != null) {
+                final PreferenceScreen preferenceScreen = getPreferenceScreen();
+                if (preferenceScreen != null) {
+                    preferenceScreen.restoreHierarchyState(container);
+                    mSavedInstanceState = state;
+                    return;
+                }
             }
         }
 
@@ -156,14 +801,280 @@
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
-        
-        mPreferenceManager.dispatchActivityResult(requestCode, resultCode, data);
+
+        if (mPreferenceManager != null) {
+            mPreferenceManager.dispatchActivityResult(requestCode, resultCode, data);
+        }
     }
 
     @Override
     public void onContentChanged() {
         super.onContentChanged();
-        postBindPreferences();
+
+        if (mPreferenceManager != null) {
+            postBindPreferences();
+        }
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        super.onListItemClick(l, v, position, id);
+
+        if (mAdapter != null) {
+            onHeaderClick(mHeaders.get(position), position);
+        }
+    }
+
+    /**
+     * Called when the user selects an item in the header list.  The default
+     * implementation will call either {@link #startWithFragment(String, Bundle, Fragment, int)}
+     * or {@link #switchToHeader(Header)} as appropriate.
+     *
+     * @param header The header that was selected.
+     * @param position The header's position in the list.
+     */
+    public void onHeaderClick(Header header, int position) {
+        if (header.fragment != null) {
+            if (mSinglePane) {
+                startWithFragment(header.fragment, header.fragmentArguments, null, 0);
+            } else {
+                switchToHeader(header);
+            }
+        } else if (header.intent != null) {
+            startActivity(header.intent);
+        }
+    }
+
+    /**
+     * Start a new instance of this activity, showing only the given
+     * preference fragment.  When launched in this mode, the header list
+     * will be hidden and the given preference fragment will be instantiated
+     * and fill the entire activity.
+     *
+     * @param fragmentName The name of the fragment to display.
+     * @param args Optional arguments to supply to the fragment.
+     */
+    public void startWithFragment(String fragmentName, Bundle args,
+            Fragment resultTo, int resultRequestCode) {
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setClass(this, getClass());
+        intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
+        intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
+        intent.putExtra(EXTRA_NO_HEADERS, true);
+        if (resultTo == null) {
+            startActivity(intent);
+        } else {
+            resultTo.startActivityForResult(intent, resultRequestCode);
+        }
+    }
+
+    /**
+     * Change the base title of the bread crumbs for the current preferences.
+     * This will normally be called for you.  See
+     * {@link android.app.FragmentBreadCrumbs} for more information.
+     */
+    public void showBreadCrumbs(CharSequence title, CharSequence shortTitle) {
+        if (mFragmentBreadCrumbs == null) {
+            mFragmentBreadCrumbs = new FragmentBreadCrumbs(this);
+            mFragmentBreadCrumbs.setActivity(this);
+            getActionBar().setCustomNavigationMode(mFragmentBreadCrumbs);
+        }
+        mFragmentBreadCrumbs.setTitle(title, shortTitle);
+    }
+
+    void setSelectedHeader(Header header) {
+        mCurHeader = header;
+        int index = mHeaders.indexOf(header);
+        if (index >= 0) {
+            getListView().setItemChecked(index, true);
+        } else {
+            getListView().clearChoices();
+        }
+        if (header != null) {
+            CharSequence title = header.breadCrumbTitle;
+            if (title == null) title = header.title;
+            if (title == null) title = getTitle();
+            showBreadCrumbs(title, header.breadCrumbShortTitle);
+        } else {
+            showBreadCrumbs(getTitle(), null);
+        }
+    }
+
+    private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {
+        getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
+        Fragment f = Fragment.instantiate(this, fragmentName, args);
+        FragmentTransaction transaction = getFragmentManager().openTransaction();
+        transaction.setTransition(direction == 0 ? FragmentTransaction.TRANSIT_NONE
+                : direction > 0 ? FragmentTransaction.TRANSIT_FRAGMENT_NEXT
+                        : FragmentTransaction.TRANSIT_FRAGMENT_PREV);
+        transaction.replace(com.android.internal.R.id.prefs, f);
+        transaction.commit();
+    }
+
+    /**
+     * When in two-pane mode, switch the fragment pane to show the given
+     * preference fragment.
+     *
+     * @param fragmentName The name of the fragment to display.
+     * @param args Optional arguments to supply to the fragment.
+     */
+    public void switchToHeader(String fragmentName, Bundle args) {
+        setSelectedHeader(null);
+        switchToHeaderInner(fragmentName, args, 0);
+    }
+
+    /**
+     * When in two-pane mode, switch to the fragment pane to show the given
+     * preference fragment.
+     *
+     * @param header The new header to display.
+     */
+    public void switchToHeader(Header header) {
+        if (mCurHeader == header) {
+            // This is the header we are currently displaying.  Just make sure
+            // to pop the stack up to its root state.
+            getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
+        } else {
+            int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader);
+            switchToHeaderInner(header.fragment, header.fragmentArguments, direction);
+            setSelectedHeader(header);
+        }
+    }
+
+    Header findBestMatchingHeader(Header cur, ArrayList<Header> from) {
+        ArrayList<Header> matches = new ArrayList<Header>();
+        for (int j=0; j<from.size(); j++) {
+            Header oh = from.get(j);
+            if (cur == oh || (cur.id != HEADER_ID_UNDEFINED && cur.id == oh.id)) {
+                // Must be this one.
+                matches.clear();
+                matches.add(oh);
+                break;
+            }
+            if (cur.fragment != null) {
+                if (cur.fragment.equals(oh.fragment)) {
+                    matches.add(oh);
+                }
+            } else if (cur.intent != null) {
+                if (cur.intent.equals(oh.intent)) {
+                    matches.add(oh);
+                }
+            } else if (cur.title != null) {
+                if (cur.title.equals(oh.title)) {
+                    matches.add(oh);
+                }
+            }
+        }
+        final int NM = matches.size();
+        if (NM == 1) {
+            return matches.get(0);
+        } else if (NM > 1) {
+            for (int j=0; j<NM; j++) {
+                Header oh = matches.get(j);
+                if (cur.fragmentArguments != null &&
+                        cur.fragmentArguments.equals(oh.fragmentArguments)) {
+                    return oh;
+                }
+                if (cur.extras != null && cur.extras.equals(oh.extras)) {
+                    return oh;
+                }
+                if (cur.title != null && cur.title.equals(oh.title)) {
+                    return oh;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Start a new fragment.
+     *
+     * @param fragment The fragment to start
+     * @param push If true, the current fragment will be pushed onto the back stack.  If false,
+     * the current fragment will be replaced.
+     */
+    public void startPreferenceFragment(Fragment fragment, boolean push) {
+        FragmentTransaction transaction = getFragmentManager().openTransaction();
+        transaction.replace(com.android.internal.R.id.prefs, fragment);
+        if (push) {
+            transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+            transaction.addToBackStack(BACK_STACK_PREFS);
+        } else {
+            transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_NEXT);
+        }
+        transaction.commit();
+    }
+
+    /**
+     * Start a new fragment containing a preference panel.  If the prefences
+     * are being displayed in multi-pane mode, the given fragment class will
+     * be instantiated and placed in the appropriate pane.  If running in
+     * single-pane mode, a new activity will be launched in which to show the
+     * fragment.
+     * 
+     * @param fragmentClass Full name of the class implementing the fragment.
+     * @param args Any desired arguments to supply to the fragment.
+     * @param titleRes Optional resource identifier of the title of this
+     * fragment.
+     * @param titleText Optional text of the title of this fragment.
+     * @param resultTo Optional fragment that result data should be sent to.
+     * If non-null, resultTo.onActivityResult() will be called when this
+     * preference panel is done.  The launched panel must use
+     * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
+     * @param resultRequestCode If resultTo is non-null, this is the caller's
+     * request code to be received with the resut.
+     */
+    public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
+            CharSequence titleText, Fragment resultTo, int resultRequestCode) {
+        if (mSinglePane) {
+            startWithFragment(fragmentClass, args, resultTo, resultRequestCode);
+        } else {
+            Fragment f = Fragment.instantiate(this, fragmentClass, args);
+            if (resultTo != null) {
+                f.setTargetFragment(resultTo, resultRequestCode);
+            }
+            FragmentTransaction transaction = getFragmentManager().openTransaction();
+            transaction.replace(com.android.internal.R.id.prefs, f);
+            if (titleRes != 0) {
+                transaction.setBreadCrumbTitle(titleRes);
+            } else if (titleText != null) {
+                transaction.setBreadCrumbTitle(titleText);
+            }
+            transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+            transaction.addToBackStack(BACK_STACK_PREFS);
+            transaction.commit();
+        }
+    }
+    
+    /**
+     * Called by a preference panel fragment to finish itself.
+     * 
+     * @param caller The fragment that is asking to be finished.
+     * @param resultCode Optional result code to send back to the original
+     * launching fragment.
+     * @param resultData Optional result data to send back to the original
+     * launching fragment.
+     */
+    public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
+        if (mSinglePane) {
+            setResult(resultCode, resultData);
+            finish();
+        } else {
+            if (caller != null) {
+                if (caller.getTargetFragment() != null) {
+                    caller.getTargetFragment().onActivityResult(caller.getTargetRequestCode(),
+                            resultCode, resultData);
+                }
+            }
+            // XXX be smarter about popping the stack.
+            onBackPressed();
+        }
+    }
+    
+    @Override
+    public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
+        startPreferencePanel(pref.getFragment(), pref.getExtras(), 0, pref.getTitle(), null, 0);
+        return true;
     }
 
     /**
@@ -176,7 +1087,7 @@
         if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
         mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
     }
-    
+
     private void bindPreferences() {
         final PreferenceScreen preferenceScreen = getPreferenceScreen();
         if (preferenceScreen != null) {
@@ -187,38 +1098,41 @@
             }
         }
     }
-    
-    /**
-     * Creates the {@link PreferenceManager}.
-     * 
-     * @return The {@link PreferenceManager} used by this activity.
-     */
-    private PreferenceManager onCreatePreferenceManager() {
-        PreferenceManager preferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
-        preferenceManager.setOnPreferenceTreeClickListener(this);
-        return preferenceManager;
-    }
-    
+
     /**
      * Returns the {@link PreferenceManager} used by this activity.
      * @return The {@link PreferenceManager}.
+     *
+     * @deprecated This function is not relevant for a modern fragment-based
+     * PreferenceActivity.
      */
+    @Deprecated
     public PreferenceManager getPreferenceManager() {
         return mPreferenceManager;
     }
-    
+
     private void requirePreferenceManager() {
         if (mPreferenceManager == null) {
-            throw new RuntimeException("This should be called after super.onCreate.");
+            if (mAdapter == null) {
+                throw new RuntimeException("This should be called after super.onCreate.");
+            }
+            throw new RuntimeException(
+                    "Modern two-pane PreferenceActivity requires use of a PreferenceFragment");
         }
     }
 
     /**
      * Sets the root of the preference hierarchy that this activity is showing.
-     * 
+     *
      * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
+     *
+     * @deprecated This function is not relevant for a modern fragment-based
+     * PreferenceActivity.
      */
+    @Deprecated
     public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
+        requirePreferenceManager();
+
         if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
             postBindPreferences();
             CharSequence title = getPreferenceScreen().getTitle();
@@ -228,61 +1142,84 @@
             }
         }
     }
-    
+
     /**
      * Gets the root of the preference hierarchy that this activity is showing.
-     * 
+     *
      * @return The {@link PreferenceScreen} that is the root of the preference
      *         hierarchy.
+     *
+     * @deprecated This function is not relevant for a modern fragment-based
+     * PreferenceActivity.
      */
+    @Deprecated
     public PreferenceScreen getPreferenceScreen() {
-        return mPreferenceManager.getPreferenceScreen();
+        if (mPreferenceManager != null) {
+            return mPreferenceManager.getPreferenceScreen();
+        }
+        return null;
     }
-    
+
     /**
      * Adds preferences from activities that match the given {@link Intent}.
-     * 
+     *
      * @param intent The {@link Intent} to query activities.
+     *
+     * @deprecated This function is not relevant for a modern fragment-based
+     * PreferenceActivity.
      */
+    @Deprecated
     public void addPreferencesFromIntent(Intent intent) {
         requirePreferenceManager();
-        
+
         setPreferenceScreen(mPreferenceManager.inflateFromIntent(intent, getPreferenceScreen()));
     }
-    
+
     /**
      * Inflates the given XML resource and adds the preference hierarchy to the current
      * preference hierarchy.
-     * 
+     *
      * @param preferencesResId The XML resource ID to inflate.
+     *
+     * @deprecated This function is not relevant for a modern fragment-based
+     * PreferenceActivity.
      */
+    @Deprecated
     public void addPreferencesFromResource(int preferencesResId) {
         requirePreferenceManager();
-        
+
         setPreferenceScreen(mPreferenceManager.inflateFromResource(this, preferencesResId,
                 getPreferenceScreen()));
     }
 
     /**
      * {@inheritDoc}
+     *
+     * @deprecated This function is not relevant for a modern fragment-based
+     * PreferenceActivity.
      */
+    @Deprecated
     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
         return false;
     }
-    
+
     /**
      * Finds a {@link Preference} based on its key.
-     * 
+     *
      * @param key The key of the preference to retrieve.
      * @return The {@link Preference} with the key, or null.
      * @see PreferenceGroup#findPreference(CharSequence)
+     *
+     * @deprecated This function is not relevant for a modern fragment-based
+     * PreferenceActivity.
      */
+    @Deprecated
     public Preference findPreference(CharSequence key) {
-        
+
         if (mPreferenceManager == null) {
             return null;
         }
-        
+
         return mPreferenceManager.findPreference(key);
     }
 
@@ -292,5 +1229,14 @@
             mPreferenceManager.dispatchNewIntent(intent);
         }
     }
-    
+
+    // give subclasses access to the Next button
+    /** @hide */
+    protected boolean hasNextButton() {
+        return mNextButton != null;
+    }
+    /** @hide */
+    protected Button getNextButton() {
+        return mNextButton;
+    }
 }
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
new file mode 100644
index 0000000..33f37f8
--- /dev/null
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.preference;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+/**
+ * Shows a hierarchy of {@link Preference} objects as
+ * lists. These preferences will
+ * automatically save to {@link SharedPreferences} as the user interacts with
+ * them. To retrieve an instance of {@link SharedPreferences} that the
+ * preference hierarchy in this fragment will use, call
+ * {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)}
+ * with a context in the same package as this fragment.
+ * <p>
+ * Furthermore, the preferences shown will follow the visual style of system
+ * preferences. It is easy to create a hierarchy of preferences (that can be
+ * shown on multiple screens) via XML. For these reasons, it is recommended to
+ * use this fragment (as a superclass) to deal with preferences in applications.
+ * <p>
+ * A {@link PreferenceScreen} object should be at the top of the preference
+ * hierarchy. Furthermore, subsequent {@link PreferenceScreen} in the hierarchy
+ * denote a screen break--that is the preferences contained within subsequent
+ * {@link PreferenceScreen} should be shown on another screen. The preference
+ * framework handles showing these other screens from the preference hierarchy.
+ * <p>
+ * The preference hierarchy can be formed in multiple ways:
+ * <li> From an XML file specifying the hierarchy
+ * <li> From different {@link Activity Activities} that each specify its own
+ * preferences in an XML file via {@link Activity} meta-data
+ * <li> From an object hierarchy rooted with {@link PreferenceScreen}
+ * <p>
+ * To inflate from XML, use the {@link #addPreferencesFromResource(int)}. The
+ * root element should be a {@link PreferenceScreen}. Subsequent elements can point
+ * to actual {@link Preference} subclasses. As mentioned above, subsequent
+ * {@link PreferenceScreen} in the hierarchy will result in the screen break.
+ * <p>
+ * To specify an {@link Intent} to query {@link Activity Activities} that each
+ * have preferences, use {@link #addPreferencesFromIntent}. Each
+ * {@link Activity} can specify meta-data in the manifest (via the key
+ * {@link PreferenceManager#METADATA_KEY_PREFERENCES}) that points to an XML
+ * resource. These XML resources will be inflated into a single preference
+ * hierarchy and shown by this fragment.
+ * <p>
+ * To specify an object hierarchy rooted with {@link PreferenceScreen}, use
+ * {@link #setPreferenceScreen(PreferenceScreen)}.
+ * <p>
+ * As a convenience, this fragment implements a click listener for any
+ * preference in the current hierarchy, see
+ * {@link #onPreferenceTreeClick(PreferenceScreen, Preference)}.
+ *
+ * <a name="SampleCode"></a>
+ * <h3>Sample Code</h3>
+ *
+ * <p>The following sample code shows a simple preference fragment that is
+ * populated from a resource.  The resource it loads is:</p>
+ *
+ * {@sample development/samples/ApiDemos/res/xml/preferences.xml preferences}
+ *
+ * <p>The fragment implementation itself simply populates the preferences
+ * when created.  Note that the preferences framework takes care of loading
+ * the current values out of the app preferences and writing them when changed:</p>
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/FragmentPreferences.java
+ *      fragment}
+ *
+ * @see Preference
+ * @see PreferenceScreen
+ */
+public abstract class PreferenceFragment extends Fragment implements
+        PreferenceManager.OnPreferenceTreeClickListener {
+
+    private static final String PREFERENCES_TAG = "android:preferences";
+
+    private PreferenceManager mPreferenceManager;
+    private ListView mList;
+    private boolean mHavePrefs;
+    private boolean mInitDone;
+
+    /**
+     * The starting request code given out to preference framework.
+     */
+    private static final int FIRST_REQUEST_CODE = 100;
+
+    private static final int MSG_BIND_PREFERENCES = 1;
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+
+                case MSG_BIND_PREFERENCES:
+                    bindPreferences();
+                    break;
+            }
+        }
+    };
+
+    final private Runnable mRequestFocus = new Runnable() {
+        public void run() {
+            mList.focusableViewAvailable(mList);
+        }
+    };
+
+    /**
+     * Interface that PreferenceFragment's containing activity should
+     * implement to be able to process preference items that wish to
+     * switch to a new fragment.
+     */
+    public interface OnPreferenceStartFragmentCallback {
+        /**
+         * Called when the user has clicked on a Preference that has
+         * a fragment class name associated with it.  The implementation
+         * to should instantiate and switch to an instance of the given
+         * fragment.
+         */
+        boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mPreferenceManager = new PreferenceManager(getActivity(), FIRST_REQUEST_CODE);
+        mPreferenceManager.setFragment(this);
+        mPreferenceManager.setOnPreferenceTreeClickListener(this);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(com.android.internal.R.layout.preference_list_fragment,
+                container, false);
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
+
+        if (mHavePrefs) {
+            bindPreferences();
+        }
+
+        mInitDone = true;
+
+        if (savedInstanceState != null) {
+            Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG);
+            if (container != null) {
+                final PreferenceScreen preferenceScreen = getPreferenceScreen();
+                if (preferenceScreen != null) {
+                    preferenceScreen.restoreHierarchyState(container);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        mPreferenceManager.dispatchActivityStop();
+    }
+
+    @Override
+    public void onDestroyView() {
+        mList = null;
+        mHandler.removeCallbacks(mRequestFocus);
+        super.onDestroyView();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mPreferenceManager.dispatchActivityDestroy();
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        final PreferenceScreen preferenceScreen = getPreferenceScreen();
+        if (preferenceScreen != null) {
+            Bundle container = new Bundle();
+            preferenceScreen.saveHierarchyState(container);
+            outState.putBundle(PREFERENCES_TAG, container);
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        mPreferenceManager.dispatchActivityResult(requestCode, resultCode, data);
+    }
+
+    /**
+     * Returns the {@link PreferenceManager} used by this fragment.
+     * @return The {@link PreferenceManager}.
+     */
+    public PreferenceManager getPreferenceManager() {
+        return mPreferenceManager;
+    }
+
+    /**
+     * Sets the root of the preference hierarchy that this fragment is showing.
+     *
+     * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
+     */
+    public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
+        if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
+            mHavePrefs = true;
+            if (mInitDone) {
+                postBindPreferences();
+            }
+        }
+    }
+
+    /**
+     * Gets the root of the preference hierarchy that this fragment is showing.
+     *
+     * @return The {@link PreferenceScreen} that is the root of the preference
+     *         hierarchy.
+     */
+    public PreferenceScreen getPreferenceScreen() {
+        return mPreferenceManager.getPreferenceScreen();
+    }
+
+    /**
+     * Adds preferences from activities that match the given {@link Intent}.
+     *
+     * @param intent The {@link Intent} to query activities.
+     */
+    public void addPreferencesFromIntent(Intent intent) {
+        requirePreferenceManager();
+
+        setPreferenceScreen(mPreferenceManager.inflateFromIntent(intent, getPreferenceScreen()));
+    }
+
+    /**
+     * Inflates the given XML resource and adds the preference hierarchy to the current
+     * preference hierarchy.
+     *
+     * @param preferencesResId The XML resource ID to inflate.
+     */
+    public void addPreferencesFromResource(int preferencesResId) {
+        requirePreferenceManager();
+
+        setPreferenceScreen(mPreferenceManager.inflateFromResource(getActivity(),
+                preferencesResId, getPreferenceScreen()));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
+            Preference preference) {
+        if (preference.getFragment() != null &&
+                getActivity() instanceof OnPreferenceStartFragmentCallback) {
+            return ((OnPreferenceStartFragmentCallback)getActivity()).onPreferenceStartFragment(
+                    this, preference);
+        }
+        return false;
+    }
+
+    /**
+     * Finds a {@link Preference} based on its key.
+     *
+     * @param key The key of the preference to retrieve.
+     * @return The {@link Preference} with the key, or null.
+     * @see PreferenceGroup#findPreference(CharSequence)
+     */
+    public Preference findPreference(CharSequence key) {
+        if (mPreferenceManager == null) {
+            return null;
+        }
+        return mPreferenceManager.findPreference(key);
+    }
+
+    private void requirePreferenceManager() {
+        if (mPreferenceManager == null) {
+            throw new RuntimeException("This should be called after super.onCreate.");
+        }
+    }
+
+    private void postBindPreferences() {
+        if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
+        mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
+    }
+
+    private void bindPreferences() {
+        final PreferenceScreen preferenceScreen = getPreferenceScreen();
+        if (preferenceScreen != null) {
+            preferenceScreen.bind(getListView());
+        }
+    }
+
+    /** @hide */
+    public ListView getListView() {
+        ensureList();
+        return mList;
+    }
+
+    private void ensureList() {
+        if (mList != null) {
+            return;
+        }
+        View root = getView();
+        if (root == null) {
+            throw new IllegalStateException("Content view not yet created");
+        }
+        View rawListView = root.findViewById(android.R.id.list);
+        if (!(rawListView instanceof ListView)) {
+            throw new RuntimeException(
+                    "Content has view with id attribute 'android.R.id.list' "
+                    + "that is not a ListView class");
+        }
+        mList = (ListView)rawListView;
+        if (mList == null) {
+            throw new RuntimeException(
+                    "Your content must have a ListView whose id attribute is " +
+                    "'android.R.id.list'");
+        }
+        mHandler.post(mRequestFocus);
+    }
+}
diff --git a/core/java/android/preference/PreferenceInflater.java b/core/java/android/preference/PreferenceInflater.java
index 779e746..c21aa18 100644
--- a/core/java/android/preference/PreferenceInflater.java
+++ b/core/java/android/preference/PreferenceInflater.java
@@ -16,6 +16,8 @@
 
 package android.preference;
 
+import com.android.internal.util.XmlUtils;
+
 import java.io.IOException;
 import java.util.Map;
 
@@ -39,6 +41,7 @@
 class PreferenceInflater extends GenericInflater<Preference, PreferenceGroup> {
     private static final String TAG = "PreferenceInflater";
     private static final String INTENT_TAG_NAME = "intent";
+    private static final String EXTRA_TAG_NAME = "extra";
 
     private PreferenceManager mPreferenceManager;
     
@@ -73,8 +76,10 @@
             try {
                 intent = Intent.parseIntent(getContext().getResources(), parser, attrs);
             } catch (IOException e) {
-                Log.w(TAG, "Could not parse Intent.");
-                Log.w(TAG, e);
+                XmlPullParserException ex = new XmlPullParserException(
+                        "Error parsing preference");
+                ex.initCause(e);
+                throw ex;
             }
             
             if (intent != null) {
@@ -82,6 +87,18 @@
             }
             
             return true;
+        } else if (tag.equals(EXTRA_TAG_NAME)) {
+            getContext().getResources().parseBundleExtra(EXTRA_TAG_NAME, attrs,
+                    parentPreference.getExtras());
+            try {
+                XmlUtils.skipCurrentTag(parser);
+            } catch (IOException e) {
+                XmlPullParserException ex = new XmlPullParserException(
+                        "Error parsing preference");
+                ex.initCause(e);
+                throw ex;
+            }
+            return true;
         }
         
         return false;
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index e44a090..6562de90 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -16,12 +16,7 @@
 
 package android.preference;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-
 import android.app.Activity;
-import android.app.Dialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -34,6 +29,10 @@
 import android.os.Bundle;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
 /**
  * Used to help create {@link Preference} hierarchies
  * from activities or XML.
@@ -61,6 +60,11 @@
     private Activity mActivity;
 
     /**
+     * Fragment that owns this instance.
+     */
+    private PreferenceFragment mFragment;
+
+    /**
      * The context to use. This should always be set.
      * 
      * @see #mActivity
@@ -158,7 +162,21 @@
         
         setSharedPreferencesName(getDefaultSharedPreferencesName(context));
     }
-    
+
+    /**
+     * Sets the owning preference fragment
+     */
+    void setFragment(PreferenceFragment fragment) {
+        mFragment = fragment;
+    }
+
+    /**
+     * Returns the owning preference fragment, if any.
+     */
+    PreferenceFragment getFragment() {
+        return mFragment;
+    }
+
     /**
      * Returns a list of {@link Activity} (indirectly) that match a given
      * {@link Intent}.
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index 95e54324..c7f8ab2 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -91,7 +91,8 @@
 
     /**
      * Returns an adapter that can be attached to a {@link PreferenceActivity}
-     * to show the preferences contained in this {@link PreferenceScreen}.
+     * or {@link PreferenceFragment} to show the preferences contained in this
+     * {@link PreferenceScreen}.
      * <p>
      * This {@link PreferenceScreen} will NOT appear in the returned adapter, instead
      * it appears in the hierarchy above this {@link PreferenceScreen}.
@@ -136,7 +137,7 @@
     
     @Override
     protected void onClick() {
-        if (getIntent() != null || getPreferenceCount() == 0) {
+        if (getIntent() != null || getFragment() != null || getPreferenceCount() == 0) {
             return;
         }
         
diff --git a/core/java/android/preference/RingtonePreference.java b/core/java/android/preference/RingtonePreference.java
index b46f180..cf14097 100644
--- a/core/java/android/preference/RingtonePreference.java
+++ b/core/java/android/preference/RingtonePreference.java
@@ -24,7 +24,6 @@
 import android.provider.Settings.System;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.Log;
 
 /**
  * A {@link Preference} that allows the user to choose a ringtone from those on the device. 
@@ -137,7 +136,12 @@
         // Launch the ringtone picker
         Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
         onPrepareRingtonePickerIntent(intent);
-        getPreferenceManager().getActivity().startActivityForResult(intent, mRequestCode);
+        PreferenceFragment owningFragment = getPreferenceManager().getFragment();
+        if (owningFragment != null) {
+            owningFragment.startActivityForResult(intent, mRequestCode);
+        } else {
+            getPreferenceManager().getActivity().startActivityForResult(intent, mRequestCode);
+        }
     }
 
     /**
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index 970d520..66c9ba1 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -92,6 +92,11 @@
                     mSeekBarVolumizer.changeVolumeBy(1);
                 }
                 return true;
+            case KeyEvent.KEYCODE_VOLUME_MUTE:
+                if (isdown) {
+                    mSeekBarVolumizer.muteVolume();
+                }
+                return true;
             default:
                 return false;
         }
@@ -225,6 +230,7 @@
     
         private int mLastProgress = -1;
         private SeekBar mSeekBar;
+        private int mVolumeBeforeMute = -1;
         
         private ContentObserver mVolumeObserver = new ContentObserver(mHandler) {
             @Override
@@ -336,6 +342,21 @@
                 sample();
             }
             postSetVolume(mSeekBar.getProgress());
+            mVolumeBeforeMute = -1;
+        }
+
+        public void muteVolume() {
+            if (mVolumeBeforeMute != -1) {
+                mSeekBar.setProgress(mVolumeBeforeMute);
+                sample();
+                postSetVolume(mVolumeBeforeMute);
+                mVolumeBeforeMute = -1;
+            } else {
+                mVolumeBeforeMute = mSeekBar.getProgress();
+                mSeekBar.setProgress(0);
+                stopSample();
+                postSetVolume(0);
+            }
         }
 
         public void onSaveInstanceState(VolumeStore volumeStore) {
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index 2fba1d79..14d6485 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -17,22 +17,30 @@
 package android.provider;
 
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
+import android.graphics.BitmapFactory;
 import android.net.Uri;
-import android.os.AsyncTask;
+import android.provider.BrowserContract.Bookmarks;
+import android.provider.BrowserContract.History;
+import android.provider.BrowserContract.Searches;
 import android.util.Log;
 import android.webkit.WebIconDatabase;
 
-import java.util.Date;
-
 public class Browser {
     private static final String LOGTAG = "browser";
-    public static final Uri BOOKMARKS_URI =
-        Uri.parse("content://browser/bookmarks");
+
+    /**
+     * A table containing both bookmarks and history items. The columns of the table are defined in
+     * {@link BookmarkColumns}. Reading this table requires the
+     * {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} permission and writing to it
+     * requires the {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} permission.
+     */
+    public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
 
     /**
      * The name of extra data when starting Browser with ACTION_VIEW or
@@ -49,12 +57,11 @@
      * application.
      * <p>
      * The value is a unique identification string that will be used to
-     * indentify the calling application. The Browser will attempt to reuse the
+     * identify the calling application. The Browser will attempt to reuse the
      * same window each time the application launches the Browser with the same
      * identifier.
      */
-    public static final String EXTRA_APPLICATION_ID =
-        "com.android.browser.application_id";
+    public static final String EXTRA_APPLICATION_ID = "com.android.browser.application_id";
 
     /**
      * The name of the extra data in the VIEW intent. The data are key/value
@@ -68,10 +75,17 @@
     /* if you change column order you must also change indices
        below */
     public static final String[] HISTORY_PROJECTION = new String[] {
-        BookmarkColumns._ID, BookmarkColumns.URL, BookmarkColumns.VISITS,
-        BookmarkColumns.DATE, BookmarkColumns.BOOKMARK, BookmarkColumns.TITLE,
-        BookmarkColumns.FAVICON, BookmarkColumns.THUMBNAIL,
-        BookmarkColumns.TOUCH_ICON, BookmarkColumns.USER_ENTERED };
+            BookmarkColumns._ID, // 0
+            BookmarkColumns.URL, // 1
+            BookmarkColumns.VISITS, // 2
+            BookmarkColumns.DATE, // 3
+            BookmarkColumns.BOOKMARK, // 4
+            BookmarkColumns.TITLE, // 5
+            BookmarkColumns.FAVICON, // 6
+            BookmarkColumns.THUMBNAIL, // 7
+            BookmarkColumns.TOUCH_ICON, // 8
+            BookmarkColumns.USER_ENTERED, // 9
+    };
 
     /* these indices dependent on HISTORY_PROJECTION */
     public static final int HISTORY_PROJECTION_ID_INDEX = 0;
@@ -92,19 +106,33 @@
 
     /* columns needed to determine whether to truncate history */
     public static final String[] TRUNCATE_HISTORY_PROJECTION = new String[] {
-        BookmarkColumns._ID, BookmarkColumns.DATE, };
+            BookmarkColumns._ID,
+            BookmarkColumns.DATE,
+    };
+
     public static final int TRUNCATE_HISTORY_PROJECTION_ID_INDEX = 0;
 
     /* truncate this many history items at a time */
     public static final int TRUNCATE_N_OLDEST = 5;
 
-    public static final Uri SEARCHES_URI =
-        Uri.parse("content://browser/searches");
+    /**
+     * A table containing a log of browser searches. The columns of the table are defined in
+     * {@link SearchColumns}. Reading this table requires the
+     * {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} permission and writing to it
+     * requires the {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} permission.
+     */
+    public static final Uri SEARCHES_URI = Uri.parse("content://browser/searches");
 
-    /* if you change column order you must also change indices
-       below */
+    /**
+     * A projection of {@link #SEARCHES_URI} that contains {@link SearchColumns#_ID},
+     * {@link SearchColumns#SEARCH}, and {@link SearchColumns#DATE}.
+     */
     public static final String[] SEARCHES_PROJECTION = new String[] {
-        SearchColumns._ID, SearchColumns.SEARCH, SearchColumns.DATE };
+            // if you change column order you must also change indices below
+            SearchColumns._ID, // 0
+            SearchColumns.SEARCH, // 1
+            SearchColumns.DATE, // 2
+    };
 
     /* these indices dependent on SEARCHES_PROJECTION */
     public static final int SEARCHES_PROJECTION_SEARCH_INDEX = 1;
@@ -121,9 +149,10 @@
     private static final int MAX_HISTORY_COUNT = 250;
 
     /**
-     *  Open the AddBookmark activity to save a bookmark.  Launch with
-     *  and/or url, which can be edited by the user before saving.
-     *  @param c        Context used to launch the AddBookmark activity.
+     *  Open an activity to save a bookmark. Launch with a title
+     *  and/or a url, both of which can be edited by the user before saving.
+     *
+     *  @param c        Context used to launch the activity to add a bookmark.
      *  @param title    Title for the bookmark. Can be null or empty string.
      *  @param url      Url for the bookmark. Can be null or empty string.
      */
@@ -152,8 +181,15 @@
      */
     public final static String EXTRA_SHARE_FAVICON = "share_favicon";
 
-    public static final void sendString(Context c, String s) {
-        sendString(c, s, c.getString(com.android.internal.R.string.sendText));
+    /**
+     * Sends the given string using an Intent with {@link Intent#ACTION_SEND} and a mime type
+     * of text/plain. The string is put into {@link Intent#EXTRA_TEXT}.
+     *
+     * @param context the context used to start the activity
+     * @param string the string to send
+     */
+    public static final void sendString(Context context, String string) {
+        sendString(context, string, context.getString(com.android.internal.R.string.sendText));
     }
 
     /**
@@ -174,48 +210,49 @@
         send.putExtra(Intent.EXTRA_TEXT, stringToSend);
 
         try {
-            c.startActivity(Intent.createChooser(send, chooserDialogTitle));
+            Intent i = Intent.createChooser(send, chooserDialogTitle);
+            // In case this is called from outside an Activity
+            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            c.startActivity(i);
         } catch(android.content.ActivityNotFoundException ex) {
             // if no app handles it, do nothing
         }
     }
 
     /**
-     *  Return a cursor pointing to a list of all the bookmarks.
+     *  Return a cursor pointing to a list of all the bookmarks. The cursor will have a single
+     *  column, {@link BookmarkColumns#URL}.
+     *  <p>
      *  Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
+     *
      *  @param cr   The ContentResolver used to access the database.
      */
     public static final Cursor getAllBookmarks(ContentResolver cr) throws 
             IllegalStateException {
-        return cr.query(BOOKMARKS_URI,
-                new String[] { BookmarkColumns.URL }, 
-                "bookmark = 1", null, null);
+        return cr.query(Bookmarks.CONTENT_URI,
+                new String[] { Bookmarks.URL }, 
+                Bookmarks.IS_FOLDER + " = 0", null, null);
     }
 
     /**
-     *  Return a cursor pointing to a list of all visited site urls.
+     *  Return a cursor pointing to a list of all visited site urls. The cursor will
+     *  have a single column, {@link BookmarkColumns#URL}.
+     *  <p>
      *  Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
+     *
      *  @param cr   The ContentResolver used to access the database.
      */
     public static final Cursor getAllVisitedUrls(ContentResolver cr) throws
             IllegalStateException {
-        return cr.query(BOOKMARKS_URI,
-                new String[] { BookmarkColumns.URL }, null, null, null);
+        return cr.query(History.CONTENT_URI,
+                new String[] { History.URL }, null, null, null);
     }
 
     private static final void addOrUrlEquals(StringBuilder sb) {
         sb.append(" OR " + BookmarkColumns.URL + " = ");
     }
 
-    /**
-     *  Return a Cursor with all history/bookmarks that are similar to url,
-     *  where similar means 'http(s)://' and 'www.' are optional, but the rest
-     *  of the url is the same.
-     *  @param cr   The ContentResolver used to access the database.
-     *  @param url  The url to compare to.
-     *  @hide
-     */
-    public static final Cursor getVisitedLike(ContentResolver cr, String url) {
+    private static final Cursor getVisitedLike(ContentResolver cr, String url) {
         boolean secure = false;
         String compareString = url;
         if (compareString.startsWith("http://")) {
@@ -229,14 +266,14 @@
         }
         StringBuilder whereClause = null;
         if (secure) {
-            whereClause = new StringBuilder(BookmarkColumns.URL + " = ");
+            whereClause = new StringBuilder(Bookmarks.URL + " = ");
             DatabaseUtils.appendEscapedSQLString(whereClause,
                     "https://" + compareString);
             addOrUrlEquals(whereClause);
             DatabaseUtils.appendEscapedSQLString(whereClause,
                     "https://www." + compareString);
         } else {
-            whereClause = new StringBuilder(BookmarkColumns.URL + " = ");
+            whereClause = new StringBuilder(Bookmarks.URL + " = ");
             DatabaseUtils.appendEscapedSQLString(whereClause,
                     compareString);
             addOrUrlEquals(whereClause);
@@ -250,7 +287,7 @@
             DatabaseUtils.appendEscapedSQLString(whereClause,
                     "http://" + wwwString);
         }
-        return cr.query(BOOKMARKS_URI, HISTORY_PROJECTION,
+        return cr.query(History.CONTENT_URI, new String[] { History._ID, History.VISITS },
                 whereClause.toString(), null, null);
     }
 
@@ -266,26 +303,24 @@
      */
     public static final void updateVisitedHistory(ContentResolver cr,
                                                   String url, boolean real) {
-        long now = new Date().getTime();
+        long now = System.currentTimeMillis();
         Cursor c = null;
         try {
             c = getVisitedLike(cr, url);
             /* We should only get one answer that is exactly the same. */
             if (c.moveToFirst()) {
-                ContentValues map = new ContentValues();
+                ContentValues values = new ContentValues();
                 if (real) {
-                    map.put(BookmarkColumns.VISITS, c
-                            .getInt(HISTORY_PROJECTION_VISITS_INDEX) + 1);
+                    values.put(History.VISITS, c.getInt(1) + 1);
                 } else {
-                    map.put(BookmarkColumns.USER_ENTERED, 1);
+                    values.put(History.USER_ENTERED, 1);
                 }
-                map.put(BookmarkColumns.DATE, now);
-                String[] projection = new String[]
-                        { Integer.valueOf(c.getInt(0)).toString() };
-                cr.update(BOOKMARKS_URI, map, "_id = ?", projection);
+                values.put(History.DATE_LAST_VISITED, now);
+                cr.update(ContentUris.withAppendedId(History.CONTENT_URI, c.getLong(0)),
+                        values, null, null);
             } else {
                 truncateHistory(cr);
-                ContentValues map = new ContentValues();
+                ContentValues values = new ContentValues();
                 int visits;
                 int user_entered;
                 if (real) {
@@ -295,14 +330,13 @@
                     visits = 0;
                     user_entered = 1;
                 }
-                map.put(BookmarkColumns.URL, url);
-                map.put(BookmarkColumns.VISITS, visits);
-                map.put(BookmarkColumns.DATE, now);
-                map.put(BookmarkColumns.BOOKMARK, 0);
-                map.put(BookmarkColumns.TITLE, url);
-                map.put(BookmarkColumns.CREATED, 0);
-                map.put(BookmarkColumns.USER_ENTERED, user_entered);
-                cr.insert(BOOKMARKS_URI, map);
+                values.put(History.URL, url);
+                values.put(History.VISITS, visits);
+                values.put(History.DATE_LAST_VISITED, now);
+                values.put(History.TITLE, url);
+                values.put(History.DATE_CREATED, 0);
+                values.put(History.USER_ENTERED, user_entered);
+                cr.insert(History.CONTENT_URI, values);
             }
         } catch (IllegalStateException e) {
             Log.e(LOGTAG, "updateVisitedHistory", e);
@@ -322,10 +356,10 @@
         String[] str = null;
         try {
             String[] projection = new String[] {
-                "url"
+                    History.URL,
             };
-            c = cr.query(BOOKMARKS_URI, projection, "visits > 0", null,
-                    null);
+            c = cr.query(History.CONTENT_URI, projection, History.VISITS + " > 0", null, null);
+            if (c == null) return new String[0];
             str = new String[c.getCount()];
             int i = 0;
             while (c.moveToNext()) {
@@ -353,31 +387,29 @@
      * @param cr The ContentResolver used to access the database.
      */
     public static final void truncateHistory(ContentResolver cr) {
-        Cursor c = null;
+        // TODO make a single request to the provider to do this in a single transaction
+        Cursor cursor = null;
         try {
+            
             // Select non-bookmark history, ordered by date
-            c = cr.query(
-                    BOOKMARKS_URI,
-                    TRUNCATE_HISTORY_PROJECTION,
-                    "bookmark = 0",
-                    null,
-                    BookmarkColumns.DATE);
-            // Log.v(LOGTAG, "history count " + c.count());
-            if (c.moveToFirst() && c.getCount() >= MAX_HISTORY_COUNT) {
+            cursor = cr.query(History.CONTENT_URI,
+                    new String[] { History._ID, History.URL, History.DATE_LAST_VISITED },
+                    null, null, History.DATE_LAST_VISITED + " ASC");
+
+            if (cursor.moveToFirst() && cursor.getCount() >= MAX_HISTORY_COUNT) {
+                final WebIconDatabase iconDb = WebIconDatabase.getInstance();
                 /* eliminate oldest history items */
                 for (int i = 0; i < TRUNCATE_N_OLDEST; i++) {
-                    // Log.v(LOGTAG, "truncate history " +
-                    // c.getInt(TRUNCATE_HISTORY_PROJECTION_ID_INDEX));
-                    cr.delete(BOOKMARKS_URI, "_id = " +
-                            c.getInt(TRUNCATE_HISTORY_PROJECTION_ID_INDEX),
-                            null);
-                    if (!c.moveToNext()) break;
+                    cr.delete(ContentUris.withAppendedId(History.CONTENT_URI, cursor.getLong(0)),
+                            null, null);
+                    iconDb.releaseIconForPageUrl(cursor.getString(1));
+                    if (!cursor.moveToNext()) break;
                 }
             }
         } catch (IllegalStateException e) {
             Log.e(LOGTAG, "truncateHistory", e);
         } finally {
-            if (c != null) c.close();
+            if (cursor != null) cursor.close();
         }
     }
 
@@ -388,23 +420,17 @@
      * @return boolean  True if the history can be cleared.
      */
     public static final boolean canClearHistory(ContentResolver cr) {
-        Cursor c = null;
+        Cursor cursor = null;
         boolean ret = false;
         try {
-            c = cr.query(
-                BOOKMARKS_URI,
-                new String [] { BookmarkColumns._ID, 
-                                BookmarkColumns.BOOKMARK,
-                                BookmarkColumns.VISITS },
-                "bookmark = 0 OR visits > 0", 
-                null,
-                null
-                );
-            ret = c.moveToFirst();
+            cursor = cr.query(History.CONTENT_URI,
+                new String [] { History._ID, History.VISITS },
+                null, null, null);
+            ret = cursor.getCount() > 0;
         } catch (IllegalStateException e) {
             Log.e(LOGTAG, "canClearHistory", e);
         } finally {
-            if (c != null) c.close();
+            if (cursor != null) cursor.close();
         }
         return ret;
     }
@@ -420,67 +446,36 @@
     }
 
     /**
-     * Helper function to delete all history items and revert all
-     * bookmarks to zero visits which meet the criteria provided.
-     *  Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
-     *  Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
+     * Helper function to delete all history items and release the icons for them in the
+     * {@link WebIconDatabase}.
+     *
+     * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
+     * Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
+     *
      * @param cr   The ContentResolver used to access the database.
      * @param whereClause   String to limit the items affected.
      *                      null means all items.
      */
-    private static final void deleteHistoryWhere(ContentResolver cr,
-            String whereClause) {
-        Cursor c = null;
+    private static final void deleteHistoryWhere(ContentResolver cr, String whereClause) {
+        Cursor cursor = null;
         try {
-            c = cr.query(BOOKMARKS_URI,
-                HISTORY_PROJECTION,
-                whereClause,
-                null,
-                null);
-            if (c.moveToFirst()) {
+            cursor = cr.query(History.CONTENT_URI, new String[] { History.URL }, whereClause,
+                    null, null);
+            if (cursor.moveToFirst()) {
                 final WebIconDatabase iconDb = WebIconDatabase.getInstance();
-                /* Delete favicons, and revert bookmarks which have been visited
-                 * to simply bookmarks.
-                 */
-                StringBuffer sb = new StringBuffer();
-                boolean firstTime = true;
                 do {
-                    String url = c.getString(HISTORY_PROJECTION_URL_INDEX);
-                    boolean isBookmark =
-                        c.getInt(HISTORY_PROJECTION_BOOKMARK_INDEX) == 1;
-                    if (isBookmark) {
-                        if (firstTime) {
-                            firstTime = false;
-                        } else {
-                            sb.append(" OR ");
-                        }
-                        sb.append("( _id = ");
-                        sb.append(c.getInt(0));
-                        sb.append(" )");
-                    } else {
-                        iconDb.releaseIconForPageUrl(url);
-                    }
-                } while (c.moveToNext());
+                    // Delete favicons
+                    // TODO don't release if the URL is bookmarked
+                    iconDb.releaseIconForPageUrl(cursor.getString(0));
+                } while (cursor.moveToNext());
 
-                if (!firstTime) {
-                    ContentValues map = new ContentValues();
-                    map.put(BookmarkColumns.VISITS, 0);
-                    map.put(BookmarkColumns.DATE, 0);
-                    /* FIXME: Should I also remove the title? */
-                    cr.update(BOOKMARKS_URI, map, sb.toString(), null);
-                }
-
-                String deleteWhereClause = BookmarkColumns.BOOKMARK + " = 0";
-                if (whereClause != null) {
-                    deleteWhereClause += " AND " + whereClause;
-                }
-                cr.delete(BOOKMARKS_URI, deleteWhereClause, null);
+                cr.delete(History.CONTENT_URI, whereClause, null);
             }
         } catch (IllegalStateException e) {
             Log.e(LOGTAG, "deleteHistoryWhere", e);
             return;
         } finally {
-            if (c != null) c.close();
+            if (cursor != null) cursor.close();
         }
     }
 
@@ -520,10 +515,7 @@
      */
     public static final void deleteFromHistory(ContentResolver cr, 
                                                String url) {
-        StringBuilder sb = new StringBuilder(BookmarkColumns.URL + " = ");
-        DatabaseUtils.appendEscapedSQLString(sb, url);
-        String matchesUrl = sb.toString();
-        deleteHistoryWhere(cr, matchesUrl);
+        cr.delete(History.CONTENT_URI, History.URL + "=?", new String[] { url });
     }
 
     /**
@@ -534,30 +526,13 @@
      * @param search    The string to add to the searches database.
      */
     public static final void addSearchUrl(ContentResolver cr, String search) {
-        long now = new Date().getTime();
-        Cursor c = null;
-        try {
-            c = cr.query(
-                SEARCHES_URI,
-                SEARCHES_PROJECTION,
-                SEARCHES_WHERE_CLAUSE,
-                new String [] { search },
-                null);
-            ContentValues map = new ContentValues();
-            map.put(SearchColumns.SEARCH, search);
-            map.put(SearchColumns.DATE, now);
-            /* We should only get one answer that is exactly the same. */
-            if (c.moveToFirst()) {
-                cr.update(SEARCHES_URI, map, "_id = " + c.getInt(0), null);
-            } else {
-                cr.insert(SEARCHES_URI, map);
-            }
-        } catch (IllegalStateException e) {
-            Log.e(LOGTAG, "addSearchUrl", e);
-        } finally {
-            if (c != null) c.close();
-        }
+        // The content provider will take care of updating existing searches instead of duplicating
+        ContentValues values = new ContentValues();
+        values.put(Searches.SEARCH, search);
+        values.put(Searches.DATE, System.currentTimeMillis());
+        cr.insert(Searches.CONTENT_URI, values);
     }
+
     /**
      * Remove all searches from the search database.
      *  Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
@@ -567,7 +542,7 @@
         // FIXME: Should this clear the urls to which these searches lead?
         // (i.e. remove google.com/query= blah blah blah)
         try {
-            cr.delete(SEARCHES_URI, null, null);
+            cr.delete(Searches.CONTENT_URI, null, null);
         } catch (IllegalStateException e) {
             Log.e(LOGTAG, "clearSearches", e);
         }
@@ -586,35 +561,92 @@
      */
     public static final void requestAllIcons(ContentResolver cr, String where,
             WebIconDatabase.IconListener listener) {
-        WebIconDatabase.getInstance()
-                .bulkRequestIconForPageUrl(cr, where, listener);
+        WebIconDatabase.getInstance().bulkRequestIconForPageUrl(cr, where, listener);
     }
 
+    /**
+     * Column definitions for the mixed bookmark and history items available
+     * at {@link #BOOKMARKS_URI}.
+     */
     public static class BookmarkColumns implements BaseColumns {
+        /**
+         * The URL of the bookmark or history item.
+         * <p>Type: TEXT (URL)</p>
+         */
         public static final String URL = "url";
+
+        /**
+         * The number of time the item has been visited.
+         * <p>Type: NUMBER</p>
+         */
         public static final String VISITS = "visits";
+
+        /**
+         * The date the item was last visited, in milliseconds since the epoch.
+         * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p>
+         */
         public static final String DATE = "date";
+
+        /**
+         * Flag indicating that an item is a bookmark. A value of 1 indicates a bookmark, a value
+         * of 0 indicates a history item.
+         * <p>Type: INTEGER (boolean)</p>
+         */
         public static final String BOOKMARK = "bookmark";
+
+        /**
+         * The user visible title of the bookmark or history item.
+         * <p>Type: TEXT</p>
+         */
         public static final String TITLE = "title";
+
+        /**
+         * The date the item created, in milliseconds since the epoch.
+         * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p>
+         */
         public static final String CREATED = "created";
+
+        /**
+         * The favicon of the bookmark. Must decode via {@link BitmapFactory#decodeByteArray}.
+         * <p>Type: BLOB (image)</p>
+         */
         public static final String FAVICON = "favicon";
+
         /**
          * @hide
          */
         public static final String THUMBNAIL = "thumbnail";
+
         /**
          * @hide
          */
         public static final String TOUCH_ICON = "touch_icon";
+
         /**
          * @hide
          */
         public static final String USER_ENTERED = "user_entered";
     }
 
+    /**
+     * Column definitions for the search history table, available at {@link #SEARCHES_URI}.
+     */
     public static class SearchColumns implements BaseColumns {
+        /**
+         * @deprecated Not used.
+         */
+        @Deprecated
         public static final String URL = "url";
+
+        /**
+         * The user entered search term.
+         */
         public static final String SEARCH = "search";
+
+        /**
+         * The date the search was performed, in milliseconds since the epoch.
+         * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p>
+         */
         public static final String DATE = "date";
     }
 }
diff --git a/core/java/android/provider/BrowserContract.java b/core/java/android/provider/BrowserContract.java
new file mode 100644
index 0000000..03bc41a
--- /dev/null
+++ b/core/java/android/provider/BrowserContract.java
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.provider;
+
+import android.accounts.Account;
+import android.content.ContentProviderClient;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.util.Pair;
+
+/**
+ * @hide
+ */
+public class BrowserContract {
+    /** The authority for the browser provider */
+    public static final String AUTHORITY = "com.android.browser";
+
+    /** A content:// style uri to the authority for the browser provider */
+    public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+    /**
+     * An optional insert, update or delete URI parameter that allows the caller
+     * to specify that it is a sync adapter. The default value is false. If true
+     * the dirty flag is not automatically set and the "syncToNetwork" parameter
+     * is set to false when calling
+     * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}.
+     */
+    public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
+
+    /**
+     * A parameter for use when querying any table that allows specifying a limit on the number
+     * of rows returned.
+     */
+    public static final String PARAM_LIMIT = "limit";
+
+    /**
+     * Generic columns for use by sync adapters. The specific functions of
+     * these columns are private to the sync adapter. Other clients of the API
+     * should not attempt to either read or write these columns.
+     */
+    interface BaseSyncColumns {
+        /** Generic column for use by sync adapters. */
+        public static final String SYNC1 = "sync1";
+        /** Generic column for use by sync adapters. */
+        public static final String SYNC2 = "sync2";
+        /** Generic column for use by sync adapters. */
+        public static final String SYNC3 = "sync3";
+        /** Generic column for use by sync adapters. */
+        public static final String SYNC4 = "sync4";
+        /** Generic column for use by sync adapters. */
+        public static final String SYNC5 = "sync5";
+    }
+
+    /**
+     * Convenience definitions for use in implementing chrome bookmarks sync in the Bookmarks table.
+     */
+    public static final class ChromeSyncColumns {
+        private ChromeSyncColumns() {}
+
+        /** The server unique ID for an item */
+        public static final String SERVER_UNIQUE = BaseSyncColumns.SYNC3;
+
+        public static final String FOLDER_NAME_ROOT = "google_chrome";
+        public static final String FOLDER_NAME_BOOKMARKS = "google_chrome_bookmarks";
+        public static final String FOLDER_NAME_BOOKMARKS_BAR = "bookmark_bar";
+        public static final String FOLDER_NAME_OTHER_BOOKMARKS = "other_bookmarks";
+
+        /** The client unique ID for an item */
+        public static final String CLIENT_UNIQUE = BaseSyncColumns.SYNC4;
+    }
+
+    /**
+     * Columns that appear when each row of a table belongs to a specific
+     * account, including sync information that an account may need.
+     */
+    interface SyncColumns extends BaseSyncColumns {
+        /**
+         * The name of the account instance to which this row belongs, which when paired with
+         * {@link #ACCOUNT_TYPE} identifies a specific account.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ACCOUNT_NAME = "account_name";
+
+        /**
+         * The type of account to which this row belongs, which when paired with
+         * {@link #ACCOUNT_NAME} identifies a specific account.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ACCOUNT_TYPE = "account_type";
+
+        /**
+         * String that uniquely identifies this row to its source account.
+         * <P>Type: TEXT</P>
+         */
+        public static final String SOURCE_ID = "sourceid";
+
+        /**
+         * Version number that is updated whenever this row or its related data
+         * changes.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String VERSION = "version";
+
+        /**
+         * Flag indicating that {@link #VERSION} has changed, and this row needs
+         * to be synchronized by its owning account.
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String DIRTY = "dirty";
+
+        /**
+         * The time that this row was last modified by a client (msecs since the epoch).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATE_MODIFIED = "modified";
+    }
+
+    interface CommonColumns {
+        /**
+         * The unique ID for a row.
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String _ID = "_id";
+
+        /**
+         * The URL of the bookmark.
+         * <P>Type: TEXT (URL)</P>
+         */
+        public static final String URL = "url";
+
+        /**
+         * The user visible title of the bookmark.
+         * <P>Type: TEXT</P>
+         */
+        public static final String TITLE = "title";
+
+        /**
+         * The time that this row was created on its originating client (msecs
+         * since the epoch).
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATE_CREATED = "created";
+    }
+
+    interface ImageColumns {
+        /**
+         * The favicon of the bookmark, may be NULL.
+         * Must decode via {@link BitmapFactory#decodeByteArray}.
+         * <p>Type: BLOB (image)</p>
+         */
+        public static final String FAVICON = "favicon";
+
+        /**
+         * A thumbnail of the page,may be NULL.
+         * Must decode via {@link BitmapFactory#decodeByteArray}.
+         * <p>Type: BLOB (image)</p>
+         */
+        public static final String THUMBNAIL = "thumbnail";
+
+        /**
+         * The touch icon for the web page, may be NULL.
+         * Must decode via {@link BitmapFactory#decodeByteArray}.
+         * <p>Type: BLOB (image)</p>
+         * @hide
+         */
+        public static final String TOUCH_ICON = "touch_icon";
+    }
+
+    interface HistoryColumns {
+        /**
+         * The date the item was last visited, in milliseconds since the epoch.
+         * <p>Type: INTEGER (date in milliseconds since January 1, 1970)</p>
+         */
+        public static final String DATE_LAST_VISITED = "date";
+
+        /**
+         * The number of times the item has been visited.
+         * <p>Type: INTEGER</p>
+         */
+        public static final String VISITS = "visits";
+
+        public static final String USER_ENTERED = "user_entered";
+    }
+
+    /**
+     * The bookmarks table, which holds the user's browser bookmarks.
+     */
+    public static final class Bookmarks implements CommonColumns, ImageColumns, SyncColumns {
+        /**
+         * This utility class cannot be instantiated.
+         */
+        private Bookmarks() {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "bookmarks");
+
+        /**
+         * The content:// style URI for the default folder
+         */
+        public static final Uri CONTENT_URI_DEFAULT_FOLDER =
+                Uri.withAppendedPath(CONTENT_URI, "folder");
+
+        /**
+         * Query parameter used to specify an account name
+         */
+        public static final String PARAM_ACCOUNT_NAME = "acct_name";
+
+        /**
+         * Query parameter used to specify an account type
+         */
+        public static final String PARAM_ACCOUNT_TYPE = "acct_type";
+
+        /**
+         * Builds a URI that points to a specific folder.
+         * @param folderId the ID of the folder to point to
+         */
+        public static final Uri buildFolderUri(long folderId) {
+            return ContentUris.withAppendedId(CONTENT_URI_DEFAULT_FOLDER, folderId);
+        }
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of bookmarks.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/bookmark";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} of a single bookmark.
+         */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/bookmark";
+
+        /**
+         * Query parameter to use if you want to see deleted bookmarks that are still
+         * around on the device and haven't been synced yet.
+         * @see #IS_DELETED
+         */
+        public static final String QUERY_PARAMETER_SHOW_DELETED = "show_deleted";
+
+        /**
+         * Flag indicating if an item is a folder or bookmark. Non-zero values indicate
+         * a folder and zero indicates a bookmark.
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String IS_FOLDER = "folder";
+
+        /**
+         * The ID of the parent folder. ID 0 is the root folder.
+         * <P>Type: INTEGER (reference to item in the same table)</P>
+         */
+        public static final String PARENT = "parent";
+
+        /**
+         * The source ID for an item's parent. Read-only.
+         * @see #PARENT
+         */
+        public static final String PARENT_SOURCE_ID = "parent_source";
+
+        /**
+         * The position of the bookmark in relation to it's siblings that share the same
+         * {@link #PARENT}. May be negative.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String POSITION = "position";
+
+        /**
+         * The item that the bookmark should be inserted after.
+         * May be negative.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String INSERT_AFTER = "insert_after";
+
+        /**
+         * The source ID for the item that the bookmark should be inserted after. Read-only.
+         * May be negative.
+         * <P>Type: INTEGER</P>
+         * @see #INSERT_AFTER
+         */
+        public static final String INSERT_AFTER_SOURCE_ID = "insert_after_source";
+
+        /**
+         * A flag to indicate if an item has been deleted. Queries will not return deleted
+         * entries unless you add the {@link #QUERY_PARAMETER_SHOW_DELETED} query paramter
+         * to the URI when performing your query.
+         * <p>Type: INTEGER (non-zero if the item has been deleted, zero if it hasn't)
+         * @see #QUERY_PARAMETER_SHOW_DELETED
+         */
+        public static final String IS_DELETED = "deleted";
+    }
+
+    /**
+     * Read-only table that lists all the accounts that are used to provide bookmarks.
+     */
+    public static final class Accounts {
+        /**
+         * Directory under {@link Bookmarks#CONTENT_URI}
+         */
+        public static final Uri CONTENT_URI =
+                AUTHORITY_URI.buildUpon().appendPath("accounts").build();
+
+        /**
+         * The name of the account instance to which this row belongs, which when paired with
+         * {@link #ACCOUNT_TYPE} identifies a specific account.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ACCOUNT_NAME = "account_name";
+
+        /**
+         * The type of account to which this row belongs, which when paired with
+         * {@link #ACCOUNT_NAME} identifies a specific account.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ACCOUNT_TYPE = "account_type";
+    }
+
+    /**
+     * The history table, which holds the browsing history.
+     */
+    public static final class History implements CommonColumns, HistoryColumns, ImageColumns {
+        /**
+         * This utility class cannot be instantiated.
+         */
+        private History() {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "history");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of browser history items.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/browser-history";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} of a single browser history item.
+         */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/browser-history";
+    }
+
+    /**
+     * The search history table.
+     * @hide
+     */
+    public static final class Searches {
+        private Searches() {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "searches");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of browser search items.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/searches";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} of a single browser search item.
+         */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/searches";
+
+        /**
+         * The unique ID for a row.
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String _ID = "_id";
+
+        /**
+         * The user entered search term.
+         */
+        public static final String SEARCH = "search";
+
+        /**
+         * The date the search was performed, in milliseconds since the epoch.
+         * <p>Type: NUMBER (date in milliseconds since January 1, 1970)</p>
+         */
+        public static final String DATE = "date";
+    }
+
+    /**
+     * A table provided for sync adapters to use for storing private sync state data.
+     *
+     * @see SyncStateContract
+     */
+    public static final class SyncState implements SyncStateContract.Columns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private SyncState() {}
+
+        public static final String CONTENT_DIRECTORY =
+                SyncStateContract.Constants.CONTENT_DIRECTORY;
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI =
+                Uri.withAppendedPath(AUTHORITY_URI, CONTENT_DIRECTORY);
+
+        /**
+         * @see android.provider.SyncStateContract.Helpers#get
+         */
+        public static byte[] get(ContentProviderClient provider, Account account)
+                throws RemoteException {
+            return SyncStateContract.Helpers.get(provider, CONTENT_URI, account);
+        }
+
+        /**
+         * @see android.provider.SyncStateContract.Helpers#get
+         */
+        public static Pair<Uri, byte[]> getWithUri(ContentProviderClient provider, Account account)
+                throws RemoteException {
+            return SyncStateContract.Helpers.getWithUri(provider, CONTENT_URI, account);
+        }
+
+        /**
+         * @see android.provider.SyncStateContract.Helpers#set
+         */
+        public static void set(ContentProviderClient provider, Account account, byte[] data)
+                throws RemoteException {
+            SyncStateContract.Helpers.set(provider, CONTENT_URI, account, data);
+        }
+
+        /**
+         * @see android.provider.SyncStateContract.Helpers#newSetOperation
+         */
+        public static ContentProviderOperation newSetOperation(Account account, byte[] data) {
+            return SyncStateContract.Helpers.newSetOperation(CONTENT_URI, account, data);
+        }
+    }
+
+    /**
+     * Stores images for URLs. Only support query() and update().
+     * @hide
+     */
+    public static final class Images implements ImageColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private Images() {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "images");
+
+        /**
+         * The URL the images came from.
+         * <P>Type: TEXT (URL)</P>
+         */
+        public static final String URL = "url_key";
+    }
+
+    /**
+     * A combined view of bookmarks and history. All bookmarks in all folders are included and
+     * no folders are included.
+     */
+    public static final class Combined implements CommonColumns, HistoryColumns, ImageColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private Combined() {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "combined");
+
+        /**
+         * Flag indicating that an item is a bookmark. A value of 1 indicates a bookmark, a value
+         * of 0 indicates a history item.
+         * <p>Type: INTEGER (boolean)</p>
+         */
+        public static final String IS_BOOKMARK = "bookmark";
+    }
+
+    /**
+     * A table that stores settings specific to the browser. Only support query and insert.
+     */
+    public static final class Settings {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private Settings() {}
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "settings");
+
+        /**
+         * Key for a setting value.
+         */
+        public static final String KEY = "key";
+
+        /**
+         * Value for a setting.
+         */
+        public static final String VALUE = "value";
+
+        /**
+         * If set to non-0 the user has opted into bookmark sync.
+         */
+        public static final String KEY_SYNC_ENABLED = "sync_enabled";
+
+        /**
+         * Returns true if bookmark sync is enabled
+         */
+        static public boolean isSyncEnabled(Context context) {
+            Cursor cursor = null;
+            try {
+                cursor = context.getContentResolver().query(CONTENT_URI, new String[] { VALUE },
+                        KEY + "=?", new String[] { KEY_SYNC_ENABLED }, null);
+                if (cursor == null || !cursor.moveToFirst()) {
+                    return false;
+                }
+                return cursor.getInt(0) != 0;
+            } finally {
+                if (cursor != null) cursor.close();
+            }
+        }
+
+        /**
+         * Sets the bookmark sync enabled setting.
+         */
+        static public void setSyncEnabled(Context context, boolean enabled) {
+            ContentValues values = new ContentValues();
+            values.put(KEY, KEY_SYNC_ENABLED);
+            values.put(VALUE, enabled ? 1 : 0);
+            context.getContentResolver().insert(CONTENT_URI, values);
+        }
+    }
+}
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 6355a9c..88ce0f0 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -76,11 +76,106 @@
      */
     public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
 
+
+    /**
+     * Generic columns for use by sync adapters. The specific functions of
+     * these columns are private to the sync adapter. Other clients of the API
+     * should not attempt to either read or write this column.
+     */
+    protected interface BaseSyncColumns {
+
+        /** Generic column for use by sync adapters. */
+        public static final String SYNC1 = "sync1";
+        /** Generic column for use by sync adapters. */
+        public static final String SYNC2 = "sync2";
+        /** Generic column for use by sync adapters. */
+        public static final String SYNC3 = "sync3";
+        /** Generic column for use by sync adapters. */
+        public static final String SYNC4 = "sync4";
+        /** Generic column for use by sync adapters. */
+        public static final String SYNC5 = "sync5";
+    }
+
+    /**
+     * Columns for Sync information used by Calendars and Events tables.
+     */
+    public interface SyncColumns extends BaseSyncColumns {
+        /**
+         * The account that was used to sync the entry to the device.
+         * <P>Type: TEXT</P>
+         */
+        public static final String _SYNC_ACCOUNT = "_sync_account";
+
+        /**
+         * The type of the account that was used to sync the entry to the device.
+         * <P>Type: TEXT</P>
+         */
+        public static final String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
+
+        /**
+         * The unique ID for a row assigned by the sync source. NULL if the row has never been synced.
+         * <P>Type: TEXT</P>
+         */
+        public static final String _SYNC_ID = "_sync_id";
+
+        /**
+         * The last time, from the sync source's point of view, that this row has been synchronized.
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String _SYNC_TIME = "_sync_time";
+
+        /**
+         * The version of the row, as assigned by the server.
+         * <P>Type: TEXT</P>
+         */
+        public static final String _SYNC_VERSION = "_sync_version";
+
+        /**
+         * For use by sync adapter at its discretion; not modified by CalendarProvider
+         * Note that this column was formerly named _SYNC_LOCAL_ID.  We are using it to avoid a
+         * schema change.
+         * TODO Replace this with something more general in the future.
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String _SYNC_DATA = "_sync_local_id";
+
+        /**
+         * Used only in persistent providers, and only during merging.
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String _SYNC_MARK = "_sync_mark";
+
+        /**
+         * Used to indicate that local, unsynced, changes are present.
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String _SYNC_DIRTY = "_sync_dirty";
+
+    }
+
+    /**
+     * Columns from the Account information used by Calendars and Events tables.
+     */
+    public interface AccountColumns {
+        /**
+         * The name of the account instance to which this row belongs, which when paired with
+         * {@link #ACCOUNT_TYPE} identifies a specific account.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ACCOUNT_NAME = "account_name";
+
+        /**
+         * The type of account to which this row belongs, which when paired with
+         * {@link #ACCOUNT_NAME} identifies a specific account.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ACCOUNT_TYPE = "account_type";
+    }
+
     /**
      * Columns from the Calendars table that other tables join into themselves.
      */
-    public interface CalendarsColumns
-    {
+    public interface CalendarsColumns {
         /**
          * The color of the calendar
          * <P>Type: INTEGER (color value)</P>
@@ -135,75 +230,103 @@
         public static final String SYNC_STATE = "sync_state";
 
         /**
-         * The account that was used to sync the entry to the device.
-         * <P>Type: TEXT</P>
+         * Whether the row has been deleted.  A deleted row should be ignored.
+         * <P>Type: INTEGER (boolean)</P>
          */
-        public static final String _SYNC_ACCOUNT = "_sync_account";
-
-        /**
-         * The type of the account that was used to sync the entry to the device.
-         * <P>Type: TEXT</P>
-         */
-        public static final String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
-
-        /**
-         * The unique ID for a row assigned by the sync source. NULL if the row has never been synced.
-         * <P>Type: TEXT</P>
-         */
-        public static final String _SYNC_ID = "_sync_id";
-
-        /**
-         * The last time, from the sync source's point of view, that this row has been synchronized.
-         * <P>Type: INTEGER (long)</P>
-         */
-        public static final String _SYNC_TIME = "_sync_time";
-
-        /**
-         * The version of the row, as assigned by the server.
-         * <P>Type: TEXT</P>
-         */
-        public static final String _SYNC_VERSION = "_sync_version";
-
-        /**
-         * For use by sync adapter at its discretion; not modified by CalendarProvider
-         * Note that this column was formerly named _SYNC_LOCAL_ID.  We are using it to avoid a
-         * schema change.
-         * TODO Replace this with something more general in the future.
-         * <P>Type: INTEGER (long)</P>
-         */
-        public static final String _SYNC_DATA = "_sync_local_id";
-
-        /**
-         * Used only in persistent providers, and only during merging.
-         * <P>Type: INTEGER (long)</P>
-         */
-        public static final String _SYNC_MARK = "_sync_mark";
-
-        /**
-         * Used to indicate that local, unsynced, changes are present.
-         * <P>Type: INTEGER (long)</P>
-         */
-        public static final String _SYNC_DIRTY = "_sync_dirty";
-
-        /**
-         * The name of the account instance to which this row belongs, which when paired with
-         * {@link #ACCOUNT_TYPE} identifies a specific account.
-         * <P>Type: TEXT</P>
-         */
-        public static final String ACCOUNT_NAME = "account_name";
-
-        /**
-         * The type of account to which this row belongs, which when paired with
-         * {@link #ACCOUNT_NAME} identifies a specific account.
-         * <P>Type: TEXT</P>
-         */
-        public static final String ACCOUNT_TYPE = "account_type";
+        public static final String DELETED = "deleted";
     }
 
     /**
+     * Class that represents a Calendar Entity. There is one entry per calendar.
+     */
+    public static class CalendarsEntity implements BaseColumns, SyncColumns, CalendarsColumns {
+
+        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
+                "/calendar_entities");
+
+        public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) {
+            return new EntityIteratorImpl(cursor, resolver);
+        }
+
+        public static EntityIterator newEntityIterator(Cursor cursor,
+                ContentProviderClient provider) {
+            return new EntityIteratorImpl(cursor, provider);
+        }
+
+        private static class EntityIteratorImpl extends CursorEntityIterator {
+            private final ContentResolver mResolver;
+            private final ContentProviderClient mProvider;
+
+            public EntityIteratorImpl(Cursor cursor, ContentResolver resolver) {
+                super(cursor);
+                mResolver = resolver;
+                mProvider = null;
+            }
+
+            public EntityIteratorImpl(Cursor cursor, ContentProviderClient provider) {
+                super(cursor);
+                mResolver = null;
+                mProvider = provider;
+            }
+
+            @Override
+            public Entity getEntityAndIncrementCursor(Cursor cursor) throws RemoteException {
+                // we expect the cursor is already at the row we need to read from
+                final long calendarId = cursor.getLong(cursor.getColumnIndexOrThrow(_ID));
+
+                // Create the content value
+                ContentValues cv = new ContentValues();
+                cv.put(_ID, calendarId);
+
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ACCOUNT);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ACCOUNT_TYPE);
+
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_TIME);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_DATA);
+                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _SYNC_DIRTY);
+                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _SYNC_MARK);
+
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC2);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC3);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC4);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC5);
+
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
+                        Calendars.DISPLAY_NAME);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, Calendars.COLOR);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SELECTED);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SYNC_EVENTS);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.LOCATION);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, TIMEZONE);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
+                        Calendars.OWNER_ACCOUNT);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
+                        Calendars.ORGANIZER_CAN_RESPOND);
+
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
+
+                // Create the Entity from the ContentValue
+                Entity entity = new Entity(cv);
+
+                // Set cursor to next row
+                cursor.moveToNext();
+
+                // Return the created Entity
+                return entity;
+            }
+        }
+     }
+
+    /**
      * Contains a list of available calendars.
      */
-    public static class Calendars implements BaseColumns, CalendarsColumns
+    public static class Calendars implements BaseColumns, SyncColumns, AccountColumns,
+            CalendarsColumns
     {
         private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars._SYNC_ACCOUNT + "=?"
                 + " AND " + Calendars._SYNC_ACCOUNT_TYPE + "=?";
@@ -258,6 +381,24 @@
         public static final String URL = "url";
 
         /**
+         * The URL for the calendar itself
+         * <P>Type: TEXT (URL)</P>
+         */
+        public static final String SELF_URL = "selfUrl";
+
+        /**
+         * The URL for the calendar to be edited
+         * <P>Type: TEXT (URL)</P>
+         */
+        public static final String EDIT_URL = "editUrl";
+
+        /**
+         * The URL for the calendar events
+         * <P>Type: TEXT (URL)</P>
+         */
+        public static final String EVENTS_URL = "eventsUrl";
+
+        /**
          * The name of the calendar
          * <P>Type: TEXT</P>
          */
@@ -276,12 +417,6 @@
         public static final String LOCATION = "location";
 
         /**
-         * Should the calendar be hidden in the calendar selection panel?
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String HIDDEN = "hidden";
-
-        /**
          * The owner account for this calendar, based on the calendar feed.
          * This will be different from the _SYNC_ACCOUNT for delegated calendars.
          * <P>Type: String</P>
@@ -296,6 +431,9 @@
         public static final String ORGANIZER_CAN_RESPOND = "organizerCanRespond";
     }
 
+    /**
+     * Columns from the Attendees table that other tables join into themselves.
+     */
     public interface AttendeesColumns {
 
         /**
@@ -361,8 +499,7 @@
     /**
      * Columns from the Events table that other tables join into themselves.
      */
-    public interface EventsColumns
-    {
+    public interface EventsColumns {
         /**
          * The calendar the event belongs to
          * <P>Type: INTEGER (foreign key to the Calendars table)</P>
@@ -438,6 +575,18 @@
         public static final String DTEND = "dtend";
 
         /**
+         * The time the event starts with allDay events in a local tz
+         * <P>Type: INTEGER (long; millis since epoch)</P>
+         */
+        public static final String DTSTART2 = "dtstart2";
+
+        /**
+         * The time the event ends with allDay events in a local tz
+         * <P>Type: INTEGER (long; millis since epoch)</P>
+         */
+        public static final String DTEND2 = "dtend2";
+
+        /**
          * The duration of the event
          * <P>Type: TEXT (duration in RFC2445 format)</P>
          */
@@ -450,6 +599,12 @@
         public static final String EVENT_TIMEZONE = "eventTimezone";
 
         /**
+         * The timezone for the event, allDay events will have a local tz instead of UTC
+         * <P>Type: TEXT
+         */
+        public static final String EVENT_TIMEZONE2 = "eventTimezone2";
+
+        /**
          * Whether the event lasts all day or not
          * <P>Type: INTEGER (boolean)</P>
          */
@@ -598,7 +753,8 @@
     /**
      * Contains one entry per calendar event. Recurring events show up as a single entry.
      */
-    public static final class EventsEntity implements BaseColumns, EventsColumns, CalendarsColumns {
+    public static final class EventsEntity implements BaseColumns, SyncColumns, AccountColumns,
+            EventsColumns {
         /**
          * The content:// style URL for this table
          */
@@ -703,8 +859,8 @@
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_DATA);
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, _SYNC_DIRTY);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
-                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
-                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.URL);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
 
                 Entity entity = new Entity(cv);
                 Cursor subCursor;
@@ -795,7 +951,8 @@
     /**
      * Contains one entry per calendar event. Recurring events show up as a single entry.
      */
-    public static final class Events implements BaseColumns, EventsColumns, CalendarsColumns {
+    public static final class Events implements BaseColumns, SyncColumns, AccountColumns,
+            EventsColumns {
 
         private static final String[] FETCH_ENTRY_COLUMNS =
                 new String[] { Events._SYNC_ACCOUNT, Events._SYNC_ID };
@@ -860,6 +1017,15 @@
         }
 
         public static final Cursor query(ContentResolver cr, String[] projection,
+                                         long begin, long end, String searchQuery) {
+            Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
+            ContentUris.appendId(builder, begin);
+            ContentUris.appendId(builder, end);
+            return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED,
+                         new String[] { searchQuery }, DEFAULT_SORT_ORDER);
+        }
+
+        public static final Cursor query(ContentResolver cr, String[] projection,
                                          long begin, long end, String where, String orderBy) {
             Uri.Builder builder = CONTENT_URI.buildUpon();
             ContentUris.appendId(builder, begin);
@@ -873,6 +1039,21 @@
                          null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
         }
 
+        public static final Cursor query(ContentResolver cr, String[] projection, long begin,
+                long end, String searchQuery, String where, String orderBy) {
+            Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
+            ContentUris.appendId(builder, begin);
+            ContentUris.appendId(builder, end);
+            builder = builder.appendPath(searchQuery);
+            if (TextUtils.isEmpty(where)) {
+                where = WHERE_CALENDARS_SELECTED;
+            } else {
+                where = "(" + where + ") AND " + WHERE_CALENDARS_SELECTED;
+            }
+            return cr.query(builder.build(), projection, where, null,
+                    orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+        }
+
         /**
          * The content:// style URL for this table
          */
@@ -880,6 +1061,10 @@
                 "/instances/when");
         public static final Uri CONTENT_BY_DAY_URI =
             Uri.parse("content://" + AUTHORITY + "/instances/whenbyday");
+        public static final Uri CONTENT_SEARCH_URI = Uri.parse("content://" + AUTHORITY +
+                "/instances/search");
+        public static final Uri CONTENT_SEARCH_BY_DAY_URI =
+            Uri.parse("content://" + AUTHORITY + "/instances/searchbyday");
 
         /**
          * The default sort order for this table.
@@ -896,7 +1081,6 @@
          * required for correctness, it just adds a nice touch.
          */
         public static final String SORT_CALENDAR_VIEW = "begin ASC, end DESC, title ASC";
-
         /**
          * The beginning time of the instance, in UTC milliseconds
          * <P>Type: INTEGER (long; millis since epoch)</P>
@@ -1055,7 +1239,7 @@
         public static final String MAX_EVENTDAYS = "maxEventDays";
     }
 
-    public static final class CalendarMetaData implements CalendarMetaDataColumns {
+    public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns {
     }
 
     public interface EventDaysColumns {
@@ -1453,4 +1637,43 @@
         public static final Uri CONTENT_URI =
                 Uri.withAppendedPath(Calendar.CONTENT_URI, CONTENT_DIRECTORY);
     }
+
+    /**
+     * Columns from the EventsRawTimes table
+     */
+    public interface EventsRawTimesColumns {
+        /**
+         * The corresponding event id
+         * <P>Type: INTEGER (long)</P>
+         */
+        public static final String EVENT_ID = "event_id";
+
+        /**
+         * The RFC2445 compliant time the event starts
+         * <P>Type: TEXT</P>
+         */
+        public static final String DTSTART_2445 = "dtstart2445";
+
+        /**
+         * The RFC2445 compliant time the event ends
+         * <P>Type: TEXT</P>
+         */
+        public static final String DTEND_2445 = "dtend2445";
+
+        /**
+         * The RFC2445 compliant original instance time of the recurring event for which this
+         * event is an exception.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ORIGINAL_INSTANCE_TIME_2445 = "originalInstanceTime2445";
+
+        /**
+         * The RFC2445 compliant last date this event repeats on, or NULL if it never ends
+         * <P>Type: TEXT</P>
+         */
+        public static final String LAST_DATE_2445 = "lastDate2445";
+    }
+
+    public static final class EventsRawTimes implements BaseColumns, EventsRawTimesColumns {
+    }
 }
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index d52632b..bf051f5 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -88,6 +88,17 @@
         public static final String NUMBER = "number";
 
         /**
+         * The ISO 3166-1 two letters country code of the country where the
+         * user received or made the call.
+         * <P>
+         * Type: TEXT
+         * </P>
+         *
+         * @hide
+         */
+        public static final String COUNTRY_ISO = "countryiso";
+
+        /**
          * The date the call occured, in milliseconds since the epoch
          * <P>Type: INTEGER (long)</P>
          */
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 9138da2..f0aa878 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -30,7 +30,6 @@
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteException;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.RemoteException;
@@ -41,6 +40,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.util.ArrayList;
 
 /**
  * <p>
@@ -130,6 +130,20 @@
     public static final String REQUESTING_PACKAGE_PARAM_KEY = "requesting_package";
 
     /**
+     * Query parameter that should be used by the client to access a specific
+     * {@link Directory}. The parameter value should be the _ID of the corresponding
+     * directory, e.g.
+     * {@code content://com.android.contacts/data/emails/filter/acme?directory=3}
+     */
+    public static final String DIRECTORY_PARAM_KEY = "directory";
+
+    /**
+     * A query parameter that limits the number of results returned. The
+     * parameter value should be an integer.
+     */
+    public static final String LIMIT_PARAM_KEY = "limit";
+
+    /**
      * @hide
      */
     public static final class Preferences {
@@ -181,6 +195,326 @@
     }
 
     /**
+     * A Directory represents a contacts corpus, e.g. Local contacts,
+     * Google Apps Global Address List or Corporate Global Address List.
+     * <p>
+     * A Directory is implemented as a content provider with its unique authority and
+     * the same API as the main Contacts Provider.  However, there is no expectation that
+     * every directory provider will implement this Contract in its entirety.  If a
+     * directory provider does not have an implementation for a specific request, it
+     * should throw an UnsupportedOperationException.
+     * </p>
+     * <p>
+     * The most important use case for Directories is search.  A Directory provider is
+     * expected to support at least {@link ContactsContract.Contacts#CONTENT_FILTER_URI
+     * Contacts.CONTENT_FILTER_URI}.  If a Directory provider wants to participate
+     * in email and phone lookup functionalities, it should also implement
+     * {@link CommonDataKinds.Email#CONTENT_FILTER_URI CommonDataKinds.Email.CONTENT_FILTER_URI}
+     * and
+     * {@link CommonDataKinds.Phone#CONTENT_FILTER_URI CommonDataKinds.Phone.CONTENT_FILTER_URI}.
+     * </p>
+     * <p>
+     * A directory provider should return NULL for every projection field it does not
+     * recognize, rather than throwing an exception.  This way it will not be broken
+     * if ContactsContract is extended with new fields in the future.
+     * </p>
+     * <p>
+     * The client interacts with a directory via Contacts Provider by supplying an
+     * optional {@code directory=} query parameter.
+     * <p>
+     * <p>
+     * When the Contacts Provider receives the request, it transforms the URI and forwards
+     * the request to the corresponding directory content provider.
+     * The URI is transformed in the following fashion:
+     * <ul>
+     * <li>The URI authority is replaced with the corresponding {@link #DIRECTORY_AUTHORITY}.</li>
+     * <li>The {@code accountName=} and {@code accountType=} parameters are added or
+     * replaced using the corresponding {@link #ACCOUNT_TYPE} and {@link #ACCOUNT_NAME} values.</li>
+     * <li>If the URI is missing a ContactsContract.REQUESTING_PACKAGE_PARAM_KEY
+     * parameter, this parameter is added.</li>
+     * </ul>
+     * </p>
+     * <p>
+     * Clients should send directory requests to Contacts Provider and let it
+     * forward them to the respective providers rather than constructing
+     * directory provider URIs by themselves. This level of indirection allows
+     * Contacts Provider to implement additional system-level features and
+     * optimizations. Access to Contacts Provider is protected by the
+     * READ_CONTACTS permission, but access to the directory provider is not.
+     * Therefore directory providers must reject requests coming from clients
+     * other than the Contacts Provider itself. An easy way to prevent such
+     * unauthorized access is to check the name of the calling package:
+     * <pre>
+     * private boolean isCallerAllowed() {
+     *   PackageManager pm = getContext().getPackageManager();
+     *   for (String packageName: pm.getPackagesForUid(Binder.getCallingUid())) {
+     *     if (packageName.equals("com.android.providers.contacts")) {
+     *       return true;
+     *     }
+     *   }
+     *   return false;
+     * }
+     * </pre>
+     * </p>
+     * <p>
+     * The Directory table is read-only and is maintained by the Contacts Provider
+     * automatically.
+     * </p>
+     * <p>It always has at least these two rows:
+     * <ul>
+     * <li>
+     * The local directory. It has {@link Directory#_ID Directory._ID} =
+     * {@link Directory#DEFAULT Directory.DEFAULT}. This directory can be used to access locally
+     * stored contacts. The same can be achieved by omitting the {@code directory=}
+     * parameter altogether.
+     * </li>
+     * <li>
+     * The local invisible contacts. The corresponding directory ID is
+     * {@link Directory#LOCAL_INVISIBLE Directory.LOCAL_INVISIBLE}.
+     * </li>
+     * </ul>
+     * </p>
+     * <p>Custom Directories are discovered by the Contacts Provider following this procedure:
+     * <ul>
+     * <li>It finds all installed content providers with meta data identifying them
+     * as directory providers in AndroidManifest.xml:
+     * <code>
+     * &lt;meta-data android:name="android.content.ContactDirectory"
+     *               android:value="true" /&gt;
+     * </code>
+     * <p>
+     * This tag should be placed inside the corresponding content provider declaration.
+     * </p>
+     * </li>
+     * <li>
+     * Then Contacts Provider sends a {@link Directory#CONTENT_URI Directory.CONTENT_URI}
+     * query to each of the directory authorities.  A directory provider must implement
+     * this query and return a list of directories.  Each directory returned by
+     * the provider must have a unique combination for the {@link #ACCOUNT_NAME} and
+     * {@link #ACCOUNT_TYPE} columns (nulls are allowed).  Since directory IDs are assigned
+     * automatically, the _ID field will not be part of the query projection.
+     * </li>
+     * <li>Contacts Provider compiles directory lists received from all directory
+     * providers into one, assigns each individual directory a globally unique ID and
+     * stores all directory records in the Directory table.
+     * </li>
+     * </ul>
+     * </p>
+     * <p>Contacts Provider automatically interrogates newly installed or replaced packages.
+     * Thus simply installing a package containing a directory provider is sufficient
+     * to have that provider registered.  A package supplying a directory provider does
+     * not have to contain launchable activities.
+     * </p>
+     * <p>
+     * Every row in the Directory table is automatically associated with the corresponding package
+     * (apk).  If the package is later uninstalled, all corresponding directory rows
+     * are automatically removed from the Contacts Provider.
+     * </p>
+     * <p>
+     * When the list of directories handled by a directory provider changes
+     * (for instance when the user adds a new Directory account), the directory provider
+     * should call {@link #notifyDirectoryChange} to notify the Contacts Provider of the change.
+     * In response, the Contacts Provider will requery the directory provider to obtain the
+     * new list of directories.
+     * </p>
+     * <p>
+     * A directory row can be optionally associated with an existing account
+     * (see {@link android.accounts.AccountManager}). If the account is later removed,
+     * the corresponding directory rows are automatically removed from the Contacts Provider.
+     * </p>
+     */
+    public static final class Directory implements BaseColumns {
+
+        /**
+         * Not instantiable.
+         */
+        private Directory() {
+        }
+
+        /**
+         * The content:// style URI for this table.  Requests to this URI can be
+         * performed on the UI thread because they are always unblocking.
+         */
+        public static final Uri CONTENT_URI =
+                Uri.withAppendedPath(AUTHORITY_URI, "directories");
+
+        /**
+         * The MIME-type of {@link #CONTENT_URI} providing a directory of
+         * contact directories.
+         */
+        public static final String CONTENT_TYPE =
+                "vnd.android.cursor.dir/contact_directories";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} item.
+         */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/contact_directory";
+
+        /**
+         * _ID of the default directory, which represents locally stored contacts.
+         */
+        public static final long DEFAULT = 0;
+
+        /**
+         * _ID of the directory that represents locally stored invisible contacts.
+         */
+        public static final long LOCAL_INVISIBLE = 1;
+
+        /**
+         * The name of the package that owns this directory. Contacts Provider
+         * fill it in with the name of the package containing the directory provider.
+         * If the package is later uninstalled, the directories it owns are
+         * automatically removed from this table.
+         *
+         * <p>TYPE: TEXT</p>
+         */
+        public static final String PACKAGE_NAME = "packageName";
+
+        /**
+         * The type of directory captured as a resource ID in the context of the
+         * package {@link #PACKAGE_NAME}, e.g. "Corporate Directory"
+         *
+         * <p>TYPE: INTEGER</p>
+         */
+        public static final String TYPE_RESOURCE_ID = "typeResourceId";
+
+        /**
+         * An optional name that can be used in the UI to represent this directory,
+         * e.g. "Acme Corp"
+         * <p>TYPE: text</p>
+         */
+        public static final String DISPLAY_NAME = "displayName";
+
+        /**
+         * <p>
+         * The authority of the Directory Provider. Contacts Provider will
+         * use this authority to forward requests to the directory provider.
+         * A directory provider can leave this column empty - Contacts Provider will fill it in.
+         * </p>
+         * <p>
+         * Clients of this API should not send requests directly to this authority.
+         * All directory requests must be routed through Contacts Provider.
+         * </p>
+         *
+         * <p>TYPE: text</p>
+         */
+        public static final String DIRECTORY_AUTHORITY = "authority";
+
+        /**
+         * The account type which this directory is associated.
+         *
+         * <p>TYPE: text</p>
+         */
+        public static final String ACCOUNT_TYPE = "accountType";
+
+        /**
+         * The account with which this directory is associated. If the account is later
+         * removed, the directories it owns are automatically removed from this table.
+         *
+         * <p>TYPE: text</p>
+         */
+        public static final String ACCOUNT_NAME = "accountName";
+
+        /**
+         * One of {@link #EXPORT_SUPPORT_NONE}, {@link #EXPORT_SUPPORT_ANY_ACCOUNT},
+         * {@link #EXPORT_SUPPORT_SAME_ACCOUNT_ONLY}. This is the expectation the
+         * directory has for data exported from it.  Clients must obey this setting.
+         */
+        public static final String EXPORT_SUPPORT = "exportSupport";
+
+        /**
+         * An {@link #EXPORT_SUPPORT} setting that indicates that the directory
+         * does not allow any data to be copied out of it.
+         */
+        public static final int EXPORT_SUPPORT_NONE = 0;
+
+        /**
+         * An {@link #EXPORT_SUPPORT} setting that indicates that the directory
+         * allow its data copied only to the account specified by
+         * {@link #ACCOUNT_TYPE}/{@link #ACCOUNT_NAME}.
+         */
+        public static final int EXPORT_SUPPORT_SAME_ACCOUNT_ONLY = 1;
+
+        /**
+         * An {@link #EXPORT_SUPPORT} setting that indicates that the directory
+         * allow its data copied to any contacts account.
+         */
+        public static final int EXPORT_SUPPORT_ANY_ACCOUNT = 2;
+
+        /**
+         * One of {@link #SHORTCUT_SUPPORT_NONE}, {@link #SHORTCUT_SUPPORT_DATA_ITEMS_ONLY},
+         * {@link #SHORTCUT_SUPPORT_FULL}. This is the expectation the directory
+         * has for shortcuts created for its elements. Clients must obey this setting.
+         */
+        public static final String SHORTCUT_SUPPORT = "shortcutSupport";
+
+        /**
+         * An {@link #SHORTCUT_SUPPORT} setting that indicates that the directory
+         * does not allow any shortcuts created for its contacts.
+         */
+        public static final int SHORTCUT_SUPPORT_NONE = 0;
+
+        /**
+         * An {@link #SHORTCUT_SUPPORT} setting that indicates that the directory
+         * allow creation of shortcuts for data items like email, phone or postal address,
+         * but not the entire contact.
+         */
+        public static final int SHORTCUT_SUPPORT_DATA_ITEMS_ONLY = 1;
+
+        /**
+         * An {@link #SHORTCUT_SUPPORT} setting that indicates that the directory
+         * allow creation of shortcuts for contact as well as their constituent elements.
+         */
+        public static final int SHORTCUT_SUPPORT_FULL = 2;
+
+        /**
+         * One of {@link #PHOTO_SUPPORT_NONE}, {@link #PHOTO_SUPPORT_THUMBNAIL_ONLY},
+         * {@link #PHOTO_SUPPORT_FULL}. This is a feature flag indicating the extent
+         * to which the directory supports contact photos.
+         */
+        public static final String PHOTO_SUPPORT = "photoSupport";
+
+        /**
+         * An {@link #PHOTO_SUPPORT} setting that indicates that the directory
+         * does not provide any photos.
+         */
+        public static final int PHOTO_SUPPORT_NONE = 0;
+
+        /**
+         * An {@link #PHOTO_SUPPORT} setting that indicates that the directory
+         * can only produce small size thumbnails of contact photos.
+         */
+        public static final int PHOTO_SUPPORT_THUMBNAIL_ONLY = 1;
+
+        /**
+         * An {@link #PHOTO_SUPPORT} setting that indicates that the directory
+         * has full-size contact photos, but cannot provide scaled thumbnails.
+         */
+        public static final int PHOTO_SUPPORT_FULL_SIZE_ONLY = 2;
+
+        /**
+         * An {@link #PHOTO_SUPPORT} setting that indicates that the directory
+         * can produce thumbnails as well as full-size contact photos.
+         */
+        public static final int PHOTO_SUPPORT_FULL = 3;
+
+        /**
+         * Notifies the system of a change in the list of directories handled by
+         * a particular directory provider. The Contacts provider will turn around
+         * and send a query to the directory provider for the full list of directories,
+         * which will replace the previous list.
+         */
+        public static void notifyDirectoryChange(ContentResolver resolver) {
+            // This is done to trigger a query by Contacts Provider back to the directory provider.
+            // No data needs to be sent back, because the provider can infer the calling
+            // package from binder.
+            ContentValues contentValues = new ContentValues();
+            resolver.update(Directory.CONTENT_URI, contentValues, null, null);
+        }
+    }
+
+    /**
      * @hide should be removed when users are updated to refer to SyncState
      * @deprecated use SyncState instead
      */
@@ -372,12 +706,42 @@
         public static final String NAME_RAW_CONTACT_ID = "name_raw_contact_id";
 
         /**
-         * Reference to the row in the data table holding the photo.
+         * Reference to the row in the data table holding the photo.  A photo can
+         * be referred to either by ID (this field) or by URI (see {@link #PHOTO_THUMBNAIL_URI}
+         * and {@link #PHOTO_URI}).
+         * If PHOTO_ID is null, consult {@link #PHOTO_URI} or {@link #PHOTO_THUMBNAIL_URI},
+         * which is a more generic mechanism for referencing the contact photo, especially for
+         * contacts returned by non-local directories (see {@link Directory}).
+         *
          * <P>Type: INTEGER REFERENCES data(_id)</P>
          */
         public static final String PHOTO_ID = "photo_id";
 
         /**
+         * A URI that can be used to retrieve the contact's full-size photo.
+         * A photo can be referred to either by a URI (this field) or by ID
+         * (see {@link #PHOTO_ID}). If PHOTO_ID is not null, PHOTO_URI and
+         * PHOTO_THUMBNAIL_URI shall not be null (but not necessarily vice versa).
+         * Thus using PHOTO_URI is a more robust method of retrieving contact photos.
+         *
+         * <P>Type: TEXT</P>
+         */
+        public static final String PHOTO_URI = "photo_uri";
+
+        /**
+         * A URI that can be used to retrieve a thumbnail of the contact's photo.
+         * A photo can be referred to either by a URI (this field or {@link #PHOTO_URI})
+         * or by ID (see {@link #PHOTO_ID}). If PHOTO_ID is not null, PHOTO_URI and
+         * PHOTO_THUMBNAIL_URI shall not be null (but not necessarily vice versa).
+         * If the content provider does not differentiate between full-size photos
+         * and thumbnail photos, PHOTO_THUMBNAIL_URI and {@link #PHOTO_URI} can contain
+         * the same value, but either both shell be null or both not null.
+         *
+         * <P>Type: TEXT</P>
+         */
+        public static final String PHOTO_THUMBNAIL_URI = "photo_thumb_uri";
+
+        /**
          * Lookup value that reflects the {@link Groups#GROUP_VISIBLE} state of
          * any {@link CommonDataKinds.GroupMembership} for this contact.
          */
@@ -412,7 +776,6 @@
          * Contact Chat Capabilities. See {@link StatusUpdates} for individual
          * definitions.
          * <p>Type: NUMBER</p>
-         * @hide
          */
         public static final String CONTACT_CHAT_CAPABILITY = "contact_chat_capability";
 
@@ -458,7 +821,6 @@
      * 'family name', 'given name' 'middle name'.  The CJK tradition is
      * 'family name' 'middle name' 'given name', with Japanese favoring a space between
      * the names and Chinese omitting the space.
-     * @hide
      */
     public interface FullNameStyle {
         public static final int UNDEFINED = 0;
@@ -477,7 +839,6 @@
 
     /**
      * Constants for various styles of capturing the pronunciation of a person's name.
-     * @hide
      */
     public interface PhoneticNameStyle {
         public static final int UNDEFINED = 0;
@@ -503,8 +864,6 @@
     /**
      * Types of data used to produce the display name for a contact. Listed in the order
      * of increasing priority.
-     *
-     * @hide
      */
     public interface DisplayNameSources {
         public static final int UNDEFINED = 0;
@@ -520,15 +879,12 @@
      *
      * @see Contacts
      * @see RawContacts
-     * @hide
      */
     protected interface ContactNameColumns {
 
         /**
          * The kind of data that is used as the display name for the contact, such as
-         * structured name or email address.  See DisplayNameSources.
-         *
-         * TODO: convert DisplayNameSources to a link after it is un-hidden
+         * structured name or email address.  See {@link DisplayNameSources}.
          */
         public static final String DISPLAY_NAME_SOURCE = "display_name_source";
 
@@ -576,9 +932,7 @@
 
         /**
          * The phonetic alphabet used to represent the {@link #PHONETIC_NAME}.  See
-         * PhoneticNameStyle.
-         *
-         * TODO: convert PhoneticNameStyle to a link after it is un-hidden
+         * {@link PhoneticNameStyle}.
          */
         public static final String PHONETIC_NAME_STYLE = "phonetic_name_style";
 
@@ -588,13 +942,10 @@
          * {@link #PHONETIC_NAME_STYLE}.
          * </p>
          * <p>
-         * The value may be set manually by the user.
-         * This capability is is of interest only in countries
-         * with commonly used phonetic
-         * alphabets, such as Japan and Korea.  See PhoneticNameStyle.
+         * The value may be set manually by the user. This capability is of
+         * interest only in countries with commonly used phonetic alphabets,
+         * such as Japan and Korea. See {@link PhoneticNameStyle}.
          * </p>
-         *
-         * TODO: convert PhoneticNameStyle to a link after it is un-hidden
          */
         public static final String PHONETIC_NAME = "phonetic_name";
 
@@ -741,6 +1092,20 @@
      * that mime type.</td>
      * </tr>
      * <tr>
+     * <td>long</td>
+     * <td>{@link #PHOTO_URI}</td>
+     * <td>read-only</td>
+     * <td>A URI that can be used to retrieve the contact's full-size photo. This
+     * column is the preferred method of retrieving the contact photo.</td>
+     * </tr>
+     * <tr>
+     * <td>long</td>
+     * <td>{@link #PHOTO_THUMBNAIL_URI}</td>
+     * <td>read-only</td>
+     * <td>A URI that can be used to retrieve the thumbnail of contact's photo.  This
+     * column is the preferred method of retrieving the contact photo.</td>
+     * </tr>
+     * <tr>
      * <td>int</td>
      * <td>{@link #IN_VISIBLE_GROUP}</td>
      * <td>read-only</td>
@@ -976,10 +1341,10 @@
         }
 
         /**
-         * Mark a contact as having been contacted.  This updates the
-         * {@link #TIMES_CONTACTED} and {@link #LAST_TIME_CONTACTED} for the
-         * contact, plus the corresponding values of any associated raw
-         * contacts.
+         * Mark a contact as having been contacted. Updates two fields:
+         * {@link #TIMES_CONTACTED} and {@link #LAST_TIME_CONTACTED}. The
+         * TIMES_CONTACTED field is incremented by 1 and the LAST_TIME_CONTACTED
+         * field is populated with the current system time.
          *
          * @param resolver the ContentResolver to use
          * @param contactId the person who was contacted
@@ -1041,7 +1406,8 @@
 
         /**
          * A sub-directory of a single contact that contains all of the constituent raw contact
-         * {@link ContactsContract.Data} rows.
+         * {@link ContactsContract.Data} rows.  This directory can be used either
+         * with a {@link #CONTENT_URI} or {@link #CONTENT_LOOKUP_URI}.
          */
         public static final class Data implements BaseColumns, DataColumns {
             /**
@@ -1057,6 +1423,63 @@
 
         /**
          * <p>
+         * A sub-directory of a contact that contains all of its
+         * {@link ContactsContract.RawContacts} as well as
+         * {@link ContactsContract.Data} rows. To access this directory append
+         * {@link #CONTENT_DIRECTORY} to the contact URI.
+         * </p>
+         * <p>
+         * Entity has three ID fields: {@link #CONTACT_ID} for the contact,
+         * {@link #RAW_CONTACT_ID} for the raw contact and {@link #DATA_ID} for
+         * the data rows. Entity always contains at least one row per
+         * constituent raw contact, even if there are no actual data rows. In
+         * this case the {@link #DATA_ID} field will be null.
+         * </p>
+         * <p>
+         * Entity reads all data for the entire contact in one transaction, to
+         * guarantee consistency.  There is significant data duplication
+         * in the Entity (each row repeats all Contact columns and all RawContact
+         * columns), so the benefits of transactional consistency should be weighed
+         * against the cost of transferring large amounts of denormalized data
+         * from the Provider.
+         * </p>
+         * <p>
+         * To reduce the amount of data duplication the contacts provider and directory
+         * providers implementing this protocol are allowed to provide common Contacts
+         * and RawContacts fields in the first row returned for each raw contact only and
+         * leave them as null in subsequent rows.
+         * </p>
+         */
+        public static final class Entity implements BaseColumns, ContactsColumns,
+                ContactNameColumns, RawContactsColumns, BaseSyncColumns, SyncColumns, DataColumns,
+                StatusColumns, ContactOptionsColumns, ContactStatusColumns {
+            /**
+             * no public constructor since this is a utility class
+             */
+            private Entity() {
+            }
+
+            /**
+             * The directory twig for this sub-table
+             */
+            public static final String CONTENT_DIRECTORY = "entities";
+
+            /**
+             * The ID of the raw contact row.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String RAW_CONTACT_ID = "raw_contact_id";
+
+            /**
+             * The ID of the data row. The value will be null if this raw contact has no
+             * data rows.
+             * <P>Type: INTEGER</P>
+             */
+            public static final String DATA_ID = "data_id";
+        }
+
+        /**
+         * <p>
          * A <i>read-only</i> sub-directory of a single contact aggregate that
          * contains all aggregation suggestions (other contacts). The
          * aggregation suggestions are computed based on approximate data
@@ -1081,9 +1504,13 @@
          * </pre>
          *
          * </p>
+         * <p>
+         * This directory can be used either with a {@link #CONTENT_URI} or
+         * {@link #CONTENT_LOOKUP_URI}.
+         * </p>
          */
-        // TODO: add ContactOptionsColumns, ContactStatusColumns
-        public static final class AggregationSuggestions implements BaseColumns, ContactsColumns {
+        public static final class AggregationSuggestions implements BaseColumns, ContactsColumns,
+                ContactOptionsColumns, ContactStatusColumns {
             /**
              * No public constructor since this is a utility class
              */
@@ -1095,6 +1522,106 @@
              * {@link android.provider.ContactsContract.Contacts#CONTENT_FILTER_URI}.
              */
             public static final String CONTENT_DIRECTORY = "suggestions";
+
+            /**
+             * Used with {@link Builder#addParameter} to specify what kind of data is
+             * supplied for the suggestion query.
+             *
+             * @hide
+             */
+            public static final String PARAMETER_MATCH_NAME = "name";
+
+            /**
+             * Used with {@link Builder#addParameter} to specify what kind of data is
+             * supplied for the suggestion query.
+             *
+             * @hide
+             */
+            public static final String PARAMETER_MATCH_EMAIL = "email";
+
+            /**
+             * Used with {@link Builder#addParameter} to specify what kind of data is
+             * supplied for the suggestion query.
+             *
+             * @hide
+             */
+            public static final String PARAMETER_MATCH_PHONE = "phone";
+
+            /**
+             * Used with {@link Builder#addParameter} to specify what kind of data is
+             * supplied for the suggestion query.
+             *
+             * @hide
+             */
+            public static final String PARAMETER_MATCH_NICKNAME = "nickname";
+
+            /**
+             * A convenience builder for aggregation suggestion content URIs.
+             *
+             * TODO: change documentation for this class to use the builder.
+             * @hide
+             */
+            public static final class Builder {
+                private long mContactId;
+                private ArrayList<String> mKinds = new ArrayList<String>();
+                private ArrayList<String> mValues = new ArrayList<String>();
+                private int mLimit;
+
+                /**
+                 * Optional existing contact ID.  If it is not provided, the search
+                 * will be based exclusively on the values supplied with {@link #addParameter}.
+                 */
+                public Builder setContactId(long contactId) {
+                    this.mContactId = contactId;
+                    return this;
+                }
+
+                /**
+                 * A value that can be used when searching for an aggregation
+                 * suggestion.
+                 *
+                 * @param kind can be one of
+                 *            {@link AggregationSuggestions#PARAMETER_MATCH_NAME},
+                 *            {@link AggregationSuggestions#PARAMETER_MATCH_EMAIL},
+                 *            {@link AggregationSuggestions#PARAMETER_MATCH_NICKNAME},
+                 *            {@link AggregationSuggestions#PARAMETER_MATCH_PHONE}
+                 */
+                public Builder addParameter(String kind, String value) {
+                    if (!TextUtils.isEmpty(value)) {
+                        mKinds.add(kind);
+                        mValues.add(value);
+                    }
+                    return this;
+                }
+
+                public Builder setLimit(int limit) {
+                    mLimit = limit;
+                    return this;
+                }
+
+                public Uri build() {
+                    android.net.Uri.Builder builder = Contacts.CONTENT_URI.buildUpon();
+                    builder.appendEncodedPath(String.valueOf(mContactId));
+                    builder.appendPath(Contacts.AggregationSuggestions.CONTENT_DIRECTORY);
+                    if (mLimit != 0) {
+                        builder.appendQueryParameter("limit", String.valueOf(mLimit));
+                    }
+
+                    int count = mKinds.size();
+                    for (int i = 0; i < count; i++) {
+                        builder.appendQueryParameter("query", mKinds.get(i) + ":" + mValues.get(i));
+                    }
+
+                    return builder.build();
+                }
+            }
+
+            /**
+             * @hide
+             */
+            public static final Builder builder() {
+                return new Builder();
+            }
         }
 
         /**
@@ -1130,9 +1657,12 @@
          * <p>You should also consider using the convenience method
          * {@link ContactsContract.Contacts#openContactPhotoInputStream(ContentResolver, Uri)}
          * </p>
+         * <p>
+         * This directory can be used either with a {@link #CONTENT_URI} or
+         * {@link #CONTENT_LOOKUP_URI}.
+         * </p>
          */
-        // TODO: change DataColumns to DataColumnsWithJoins
-        public static final class Photo implements BaseColumns, DataColumns {
+        public static final class Photo implements BaseColumns, DataColumnsWithJoins {
             /**
              * no public constructor since this is a utility class
              */
@@ -1148,7 +1678,6 @@
              * that could be inflated using {@link android.graphics.BitmapFactory}.
              * <p>
              * Type: BLOB
-             * @hide TODO: Unhide in a separate CL
              */
             public static final String PHOTO = DATA15;
         }
@@ -1157,7 +1686,10 @@
          * Opens an InputStream for the contacts's default photo and returns the
          * photo as a byte stream. If there is not photo null will be returned.
          *
-         * @param contactUri the contact whose photo should be used
+         * @param contactUri the contact whose photo should be used. This can be used with
+         * either a {@link #CONTENT_URI} or a {@link #CONTENT_LOOKUP_URI} URI.
+         * </p>
+
          * @return an InputStream of the photo, or null if no photo is present
          */
         public static InputStream openContactPhotoInputStream(ContentResolver cr, Uri contactUri) {
@@ -1245,6 +1777,13 @@
          * @hide
          */
         public static final String NAME_VERIFIED = "name_verified";
+
+        /**
+         * The "read-only" flag: "0" by default, "1" if the row cannot be modified or
+         * deleted except by a sync adapter.  See {@link ContactsContract#CALLER_IS_SYNCADAPTER}.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String RAW_CONTACT_IS_READ_ONLY = "raw_contact_is_read_only";
     }
 
     /**
@@ -1304,7 +1843,8 @@
      * constituent data rows in a single database transaction
      * and causes at most one aggregation pass.
      * <pre>
-     * ArrayList&lt;ContentProviderOperation&gt; ops = Lists.newArrayList();
+     * ArrayList&lt;ContentProviderOperation&gt; ops =
+     *          new ArrayList&lt;ContentProviderOperation&gt;();
      * ...
      * int rawContactInsertIndex = ops.size();
      * ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
@@ -1630,10 +2170,10 @@
         public static final int AGGREGATION_MODE_DEFAULT = 0;
 
         /**
-         * Do not use.
-         *
-         * TODO: deprecate in favor of {@link #AGGREGATION_MODE_DEFAULT}
+         * Aggregation mode: aggregate at the time the raw contact is inserted/updated.
+         * @deprecated Aggregation is synchronous, this historic value is a no-op
          */
+        @Deprecated
         public static final int AGGREGATION_MODE_IMMEDIATE = 1;
 
         /**
@@ -1701,9 +2241,7 @@
         /**
          * A sub-directory of a single raw contact that contains all of its
          * {@link ContactsContract.Data} rows. To access this directory
-         * append {@link Data#CONTENT_DIRECTORY} to the contact URI.
-         *
-         * TODO: deprecate in favor of {@link RawContacts.Entity}.
+         * append {@link Data#CONTENT_DIRECTORY} to the raw contact URI.
          */
         public static final class Data implements BaseColumns, DataColumns {
             /**
@@ -1722,7 +2260,7 @@
          * <p>
          * A sub-directory of a single raw contact that contains all of its
          * {@link ContactsContract.Data} rows. To access this directory append
-         * {@link #CONTENT_DIRECTORY} to the contact URI. See
+         * {@link RawContacts.Entity#CONTENT_DIRECTORY} to the raw contact URI. See
          * {@link RawContactsEntity} for a stand-alone table containing the same
          * data.
          * </p>
@@ -1734,10 +2272,10 @@
          * null.
          * </p>
          * <p>
-         * Entity reads all
-         * data for a raw contact in one transaction, to guarantee
-         * consistency.
-         * </p>
+         * Using Entity should be preferred to using two separate queries:
+         * RawContacts followed by Data. The reason is that Entity reads all
+         * data for a raw contact in one transaction, so there is no possibility
+         * of the data changing between the two queries.
          */
         public static final class Entity implements BaseColumns, DataColumns {
             /**
@@ -1752,7 +2290,7 @@
             public static final String CONTENT_DIRECTORY = "entity";
 
             /**
-             * The ID of the data column. The value will be null if this raw contact has no
+             * The ID of the data row. The value will be null if this raw contact has no
              * data rows.
              * <P>Type: INTEGER</P>
              */
@@ -1840,28 +2378,21 @@
                             Data.DATA_VERSION);
                     for (String key : DATA_KEYS) {
                         final int columnIndex = cursor.getColumnIndexOrThrow(key);
-                        if (cursor.isNull(columnIndex)) {
-                            // don't put anything
-                        } else {
-                            try {
+                        switch (cursor.getType(columnIndex)) {
+                            case Cursor.FIELD_TYPE_NULL:
+                                // don't put anything
+                                break;
+                            case Cursor.FIELD_TYPE_INTEGER:
+                            case Cursor.FIELD_TYPE_FLOAT:
+                            case Cursor.FIELD_TYPE_STRING:
                                 cv.put(key, cursor.getString(columnIndex));
-                            } catch (SQLiteException e) {
+                                break;
+                            case Cursor.FIELD_TYPE_BLOB:
                                 cv.put(key, cursor.getBlob(columnIndex));
-                            }
+                                break;
+                            default:
+                                throw new IllegalStateException("Invalid or unhandled data type");
                         }
-                        // TODO: go back to this version of the code when bug
-                        // http://b/issue?id=2306370 is fixed.
-//                        if (cursor.isNull(columnIndex)) {
-//                            // don't put anything
-//                        } else if (cursor.isLong(columnIndex)) {
-//                            values.put(key, cursor.getLong(columnIndex));
-//                        } else if (cursor.isFloat(columnIndex)) {
-//                            values.put(key, cursor.getFloat(columnIndex));
-//                        } else if (cursor.isString(columnIndex)) {
-//                            values.put(key, cursor.getString(columnIndex));
-//                        } else if (cursor.isBlob(columnIndex)) {
-//                            values.put(key, cursor.getBlob(columnIndex));
-//                        }
                     }
                     contact.addSubValue(ContactsContract.Data.CONTENT_URI, cv);
                 } while (cursor.moveToNext());
@@ -1962,27 +2493,24 @@
         /**
          * Contact's audio/video chat capability level.
          * <P>Type: INTEGER (one of the values below)</P>
-         * @hide
          */
         public static final String CHAT_CAPABILITY = "chat_capability";
 
         /**
-         * An allowed value of {@link #CHAT_CAPABILITY}. Indicates that the contact's device can
+         * An allowed flag of {@link #CHAT_CAPABILITY}. Indicates audio-chat capability (microphone
+         * and speaker)
+         */
+        public static final int CAPABILITY_HAS_VOICE = 1;
+
+        /**
+         * An allowed flag of {@link #CHAT_CAPABILITY}. Indicates that the contact's device can
          * display a video feed.
-         * @hide
          */
-        public static final int CAPABILITY_HAS_VIDEO_PLAYBACK_ONLY = 1;
+        public static final int CAPABILITY_HAS_VIDEO = 2;
 
         /**
-         * An allowed value of {@link #CHAT_CAPABILITY}. Indicates audio-chat capability.
-         * @hide
-         */
-        public static final int CAPABILITY_HAS_VOICE = 2;
-
-        /**
-         * An allowed value of {@link #CHAT_CAPABILITY}. Indicates that the contact's device has a
+         * An allowed flag of {@link #CHAT_CAPABILITY}. Indicates that the contact's device has a
          * camera that can be used for video chat (e.g. a front-facing camera on a phone).
-         * @hide
          */
         public static final int CAPABILITY_HAS_CAMERA = 4;
     }
@@ -2028,6 +2556,13 @@
         public static final String IS_SUPER_PRIMARY = "is_super_primary";
 
         /**
+         * The "read-only" flag: "0" by default, "1" if the row cannot be modified or
+         * deleted except by a sync adapter.  See {@link ContactsContract#CALLER_IS_SYNCADAPTER}.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String IS_READ_ONLY = "is_read_only";
+
+        /**
          * The version of this data record. This is a read-only value. The data column is
          * guaranteed to not change without the version going up. This value is monotonically
          * increasing.
@@ -2196,7 +2731,9 @@
      * <p>
      * The same done using ContentProviderOperations:
      * <pre>
-     * ArrayList&lt;ContentProviderOperation&gt; ops = Lists.newArrayList();
+     * ArrayList&lt;ContentProviderOperation&gt; ops =
+     *          new ArrayList&lt;ContentProviderOperation&gt;();
+     *
      * ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
      *          .withValue(Data.RAW_CONTACT_ID, rawContactId)
      *          .withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
@@ -2213,7 +2750,9 @@
      * Just as with insert, update can be done incrementally or as a batch,
      * the batch mode being the preferred method:
      * <pre>
-     * ArrayList&lt;ContentProviderOperation&gt; ops = Lists.newArrayList();
+     * ArrayList&lt;ContentProviderOperation&gt; ops =
+     *          new ArrayList&lt;ContentProviderOperation&gt;();
+     *
      * ops.add(ContentProviderOperation.newUpdate(Data.CONTENT_URI)
      *          .withSelection(Data._ID + "=?", new String[]{String.valueOf(dataId)})
      *          .withValue(Email.DATA, "somebody@android.com")
@@ -2228,7 +2767,9 @@
      * Just as with insert and update, deletion can be done either using the
      * {@link ContentResolver#delete} method or using a ContentProviderOperation:
      * <pre>
-     * ArrayList&lt;ContentProviderOperation&gt; ops = Lists.newArrayList();
+     * ArrayList&lt;ContentProviderOperation&gt; ops =
+     *          new ArrayList&lt;ContentProviderOperation&gt;();
+     *
      * ops.add(ContentProviderOperation.newDelete(Data.CONTENT_URI)
      *          .withSelection(Data._ID + "=?", new String[]{String.valueOf(dataId)})
      *          .build());
@@ -2862,6 +3403,14 @@
          * <P>Type: TEXT</P>
          */
         public static final String LABEL = "label";
+
+        /**
+         * The phone number's E164 representation.
+         * <P>Type: TEXT</P>
+         *
+         * @hide
+         */
+        public static final String NORMALIZED_NUMBER = "normalized_number";
     }
 
     /**
@@ -3124,6 +3673,32 @@
      * <li>{@link #AVAILABLE}</li>
      * </ul>
      * </p>
+     * <p>
+     * Since presence status is inherently volatile, the content provider
+     * may choose not to store this field in long-term storage.
+     * </p>
+     * </td>
+     * </tr>
+     * <tr>
+     * <td>int</td>
+     * <td>{@link #CHAT_CAPABILITY}</td>
+     * <td>read/write</td>
+     * <td>Contact IM chat compatibility value. The allowed values combinations of the following
+     * flags. If None of these flags is set, the device can only do text messaging.
+     * <p>
+     * <ul>
+     * <li>{@link #CAPABILITY_HAS_VIDEO}</li>
+     * <li>{@link #CAPABILITY_HAS_VOICE}</li>
+     * <li>{@link #CAPABILITY_HAS_CAMERA}</li>
+     * </ul>
+     * </p>
+     * <p>
+     * Since chat compatibility is inherently volatile as the contact's availability moves from
+     * one device to another, the content provider may choose not to store this field in long-term
+     * storage.
+     * </p>
+     * </td>
+     * </tr>
      * <tr>
      * <td>String</td>
      * <td>{@link #STATUS}</td>
@@ -3487,7 +4062,9 @@
          * <p>A data kind representing the contact's nickname. For example, for
          * Bob Parr ("Mr. Incredible"):
          * <pre>
-         * ArrayList&lt;ContentProviderOperation&gt; ops = Lists.newArrayList();
+         * ArrayList&lt;ContentProviderOperation&gt; ops =
+         *          new ArrayList&lt;ContentProviderOperation&gt;();
+         *
          * ops.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
          *          .withValue(Data.RAW_CONTACT_ID, rawContactId)
          *          .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
@@ -3691,6 +4268,14 @@
             public static final String NUMBER = DATA;
 
             /**
+             * The phone number's E164 representation.
+             * <P>Type: TEXT</P>
+             *
+             * @hide
+             */
+            public static final String NORMALIZED_NUMBER = DATA4;
+
+            /**
              * @deprecated use {@link #getTypeLabel(Resources, int, CharSequence)} instead.
              * @hide
              */
@@ -3772,7 +4357,7 @@
          * </tr>
          * <tr>
          * <td>String</td>
-         * <td>{@link #DATA}</td>
+         * <td>{@link #ADDRESS}</td>
          * <td>{@link #DATA1}</td>
          * <td>Email address itself.</td>
          * </tr>
@@ -3863,7 +4448,6 @@
             /**
              * The email address.
              * <P>Type: TEXT</P>
-             * @hide TODO: Unhide in a separate CL
              */
             public static final String ADDRESS = DATA1;
 
@@ -4543,6 +5127,47 @@
              * <P>Type: TEXT</P>
              */
             public static final String NAME = DATA;
+
+            /**
+             * Return the string resource that best describes the given
+             * {@link #TYPE}. Will always return a valid resource.
+             */
+            public static final int getTypeLabelResource(int type) {
+                switch (type) {
+                    case TYPE_ASSISTANT: return com.android.internal.R.string.relationTypeAssistant;
+                    case TYPE_BROTHER: return com.android.internal.R.string.relationTypeBrother;
+                    case TYPE_CHILD: return com.android.internal.R.string.relationTypeChild;
+                    case TYPE_DOMESTIC_PARTNER:
+                            return com.android.internal.R.string.relationTypeDomesticPartner;
+                    case TYPE_FATHER: return com.android.internal.R.string.relationTypeFather;
+                    case TYPE_FRIEND: return com.android.internal.R.string.relationTypeFriend;
+                    case TYPE_MANAGER: return com.android.internal.R.string.relationTypeManager;
+                    case TYPE_MOTHER: return com.android.internal.R.string.relationTypeMother;
+                    case TYPE_PARENT: return com.android.internal.R.string.relationTypeParent;
+                    case TYPE_PARTNER: return com.android.internal.R.string.relationTypePartner;
+                    case TYPE_REFERRED_BY:
+                            return com.android.internal.R.string.relationTypeReferredBy;
+                    case TYPE_RELATIVE: return com.android.internal.R.string.relationTypeRelative;
+                    case TYPE_SISTER: return com.android.internal.R.string.relationTypeSister;
+                    case TYPE_SPOUSE: return com.android.internal.R.string.relationTypeSpouse;
+                    default: return com.android.internal.R.string.orgTypeCustom;
+                }
+            }
+
+            /**
+             * Return a {@link CharSequence} that best describes the given type,
+             * possibly substituting the given {@link #LABEL} value
+             * for {@link #TYPE_CUSTOM}.
+             */
+            public static final CharSequence getTypeLabel(Resources res, int type,
+                    CharSequence label) {
+                if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
+                    return label;
+                } else {
+                    final int labelRes = getTypeLabelResource(type);
+                    return res.getText(labelRes);
+                }
+            }
         }
 
         /**
@@ -4620,7 +5245,7 @@
                         return com.android.internal.R.string.eventTypeAnniversary;
                     case TYPE_BIRTHDAY: return com.android.internal.R.string.eventTypeBirthday;
                     case TYPE_OTHER: return com.android.internal.R.string.eventTypeOther;
-                    default: return com.android.internal.R.string.eventTypeOther;
+                    default: return com.android.internal.R.string.eventTypeCustom;
                 }
             }
         }
@@ -5027,6 +5652,23 @@
          * Type: INTEGER (boolean)
          */
         public static final String SHOULD_SYNC = "should_sync";
+
+        /**
+         * Any newly created contacts will automatically be added to groups that have this
+         * flag set to true.
+         * <p>
+         * Type: INTEGER (boolean)
+         */
+        public static final String AUTO_ADD = "auto_add";
+
+        /**
+         * When a contacts is marked as a favorites it will be automatically added
+         * to the groups that have this flag set, and when it is removed from favorites
+         * it will be removed from these groups.
+         * <p>
+         * Type: INTEGER (boolean)
+         */
+        public static final String FAVORITES = "favorites";
     }
 
     /**
@@ -5167,6 +5809,8 @@
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, values, DELETED);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, NOTES);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, SHOULD_SYNC);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, FAVORITES);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, values, AUTO_ADD);
                 cursor.moveToNext();
                 return new Entity(values);
             }
@@ -5543,14 +6187,6 @@
         public static final int MODE_LARGE = 3;
 
         /**
-         * Extra used to specify the last selected tab index of the Contacts app.
-         * If this is not given or -1
-         * @hide
-         */
-        public static final String EXTRA_SELECTED_CONTACTS_APP_TAB_INDEX =
-                "SELECTED_TAB_INDEX";
-
-        /**
          * Trigger a dialog that lists the various methods of interacting with
          * the requested {@link Contacts} entry. This may be based on available
          * {@link ContactsContract.Data} rows under that contact, and may also
@@ -5575,16 +6211,6 @@
          */
         public static void showQuickContact(Context context, View target, Uri lookupUri, int mode,
                 String[] excludeMimes) {
-            context.startActivity(getQuickContactIntent(context, target, lookupUri, mode,
-                    excludeMimes));
-        }
-
-        /**
-         * Creates the Intent to launch Quick Contacts
-         * @hide
-         */
-        public static Intent getQuickContactIntent(Context context, View target, Uri lookupUri,
-                int mode, String[] excludeMimes) {
             // Find location and bounds of target view, adjusting based on the
             // assumed local density.
             final float appScale = context.getResources().getCompatibilityInfo().applicationScale;
@@ -5598,7 +6224,7 @@
             rect.bottom = (int) ((pos[1] + target.getHeight()) * appScale + 0.5f);
 
             // Trigger with obtained rectangle
-            return getQuickContactIntent(context, rect, lookupUri, mode, excludeMimes);
+            showQuickContact(context, rect, lookupUri, mode, excludeMimes);
         }
 
         /**
@@ -5629,16 +6255,6 @@
          */
         public static void showQuickContact(Context context, Rect target, Uri lookupUri, int mode,
                 String[] excludeMimes) {
-            context.startActivity(getQuickContactIntent(context, target, lookupUri, mode,
-                    excludeMimes));
-        }
-
-        /**
-         * Creates the Intent to launch Quick Contacts
-         * @hide
-         */
-        public static Intent getQuickContactIntent(Context context, Rect target, Uri lookupUri,
-                int mode, String[] excludeMimes) {
             // Launch pivot dialog through intent for now
             final Intent intent = new Intent(ACTION_QUICK_CONTACT);
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP
@@ -5648,7 +6264,7 @@
             intent.setSourceBounds(target);
             intent.putExtra(EXTRA_MODE, mode);
             intent.putExtra(EXTRA_EXCLUDE_MIMES, excludeMimes);
-            return intent;
+            context.startActivity(intent);
         }
     }
 
@@ -5711,6 +6327,28 @@
                 "com.android.contacts.action.SHOW_OR_CREATE_CONTACT";
 
         /**
+         * Starts an Activity that lets the user select the multiple phones from a
+         * list of phone numbers which come from the contacts or
+         * {@link #EXTRA_PHONE_URIS}.
+         * <p>
+         * The phone numbers being passed in through {@link #EXTRA_PHONE_URIS}
+         * could belong to the contacts or not, and will be selected by default.
+         * <p>
+         * The user's selection will be returned from
+         * {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)}
+         * if the resultCode is
+         * {@link android.app.Activity#RESULT_OK}, the array of picked phone
+         * numbers are in the Intent's
+         * {@link #EXTRA_PHONE_URIS}; otherwise, the
+         * {@link android.app.Activity#RESULT_CANCELED} is returned if the user
+         * left the Activity without changing the selection.
+         *
+         * @hide
+         */
+        public static final String ACTION_GET_MULTIPLE_PHONES =
+                "com.android.contacts.action.GET_MULTIPLE_PHONES";
+
+        /**
          * Used with {@link #SHOW_OR_CREATE_CONTACT} to force creating a new
          * contact if no matching contact found. Otherwise, default behavior is
          * to prompt user with dialog before creating.
@@ -5731,6 +6369,23 @@
             "com.android.contacts.action.CREATE_DESCRIPTION";
 
         /**
+         * Used with {@link #ACTION_GET_MULTIPLE_PHONES} as the input or output value.
+         * <p>
+         * The phone numbers want to be picked by default should be passed in as
+         * input value. These phone numbers could belong to the contacts or not.
+         * <p>
+         * The phone numbers which were picked by the user are returned as output
+         * value.
+         * <p>
+         * Type: array of URIs, the tel URI is used for the phone numbers which don't
+         * belong to any contact, the content URI is used for phone id in contacts.
+         *
+         * @hide
+         */
+        public static final String EXTRA_PHONE_URIS =
+            "com.android.contacts.extra.PHONE_URIS";
+
+        /**
          * Optional extra used with {@link #SHOW_OR_CREATE_CONTACT} to specify a
          * dialog location using screen coordinates. When not specified, the
          * dialog will be centered.
@@ -6042,6 +6697,58 @@
              * <P>Type: boolean</P>
              */
             public static final String IM_ISPRIMARY = "im_isprimary";
+
+            /**
+             * The extra field that allows the client to supply multiple rows of
+             * arbitrary data for a single contact created using the {@link Intent#ACTION_INSERT}
+             * or edited using {@link Intent#ACTION_EDIT}. It is an ArrayList of
+             * {@link ContentValues}, one per data row. Supplying this extra is
+             * similar to inserting multiple rows into the {@link Data} table,
+             * except the user gets a chance to see and edit them before saving.
+             * Each ContentValues object must have a value for {@link Data#MIMETYPE}.
+             * If supplied values are not visible in the editor UI, they will be
+             * dropped.  Duplicate data will dropped.  Some fields
+             * like {@link CommonDataKinds.Email#TYPE Email.TYPE} may be automatically
+             * adjusted to comply with the constraints of the specific account type.
+             * For example, an Exchange contact can only have one phone numbers of type Home,
+             * so the contact editor may choose a different type for this phone number to
+             * avoid dropping the valueable part of the row, which is the phone number.
+             * <p>
+             * Example:
+             * <pre>
+             *  ArrayList&lt;ContentValues&gt; data = new ArrayList&lt;ContentValues&gt;();
+             *
+             *  ContentValues row1 = new ContentValues();
+             *  row1.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
+             *  row1.put(Organization.COMPANY, "Android");
+             *  data.add(row1);
+             *
+             *  ContentValues row2 = new ContentValues();
+             *  row2.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+             *  row2.put(Email.TYPE, Email.TYPE_CUSTOM);
+             *  row2.put(Email.LABEL, "Green Bot");
+             *  row2.put(Email.ADDRESS, "android@android.com");
+             *  data.add(row2);
+             *
+             *  Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+             *  intent.putParcelableArrayListExtra(Insert.DATA, data);
+             *
+             *  startActivity(intent);
+             * </pre>
+             */
+            public static final String DATA = "data";
+
+            /**
+             * Used to specify the account in which to create the new contact.
+             * <p>
+             * If this value is not provided, the user is presented with a disambiguation
+             * dialog to chose an account
+             * <p>
+             * Type: {@link Account}
+             *
+             * @hide
+             */
+            public static final String ACCOUNT = "com.android.contacts.extra.ACCOUNT";
         }
     }
 }
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 72bf6b0..65adef9 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.app.DownloadManager;
 import android.net.Uri;
 
 /**
@@ -24,559 +25,9 @@
  * @pending
  */
 public final class Downloads {
-    /**
-     * @hide
-     */
     private Downloads() {}
 
     /**
-     * The permission to access the download manager
-     * @hide
-     */
-    public static final String PERMISSION_ACCESS = "android.permission.ACCESS_DOWNLOAD_MANAGER";
-
-    /**
-     * The permission to access the download manager's advanced functions
-     * @hide
-     */
-    public static final String PERMISSION_ACCESS_ADVANCED =
-            "android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED";
-
-    /**
-     * The permission to directly access the download manager's cache directory
-     * @hide
-     */
-    public static final String PERMISSION_CACHE = "android.permission.ACCESS_CACHE_FILESYSTEM";
-
-    /**
-     * The permission to send broadcasts on download completion
-     * @hide
-     */
-    public static final String PERMISSION_SEND_INTENTS =
-            "android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS";
-
-    /**
-     * The content:// URI for the data table in the provider
-     * @hide
-     */
-    public static final Uri CONTENT_URI =
-        Uri.parse("content://downloads/my_downloads");
-
-    /**
-     * Broadcast Action: this is sent by the download manager to the app
-     * that had initiated a download when that download completes. The
-     * download's content: uri is specified in the intent's data.
-     * @hide
-     */
-    public static final String ACTION_DOWNLOAD_COMPLETED =
-            "android.intent.action.DOWNLOAD_COMPLETED";
-
-    /**
-     * Broadcast Action: this is sent by the download manager to the app
-     * that had initiated a download when the user selects the notification
-     * associated with that download. The download's content: uri is specified
-     * in the intent's data if the click is associated with a single download,
-     * or Downloads.CONTENT_URI if the notification is associated with
-     * multiple downloads.
-     * Note: this is not currently sent for downloads that have completed
-     * successfully.
-     * @hide
-     */
-    public static final String ACTION_NOTIFICATION_CLICKED =
-            "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
-
-    /**
-     * The name of the column containing the URI of the data being downloaded.
-     * <P>Type: TEXT</P>
-     * <P>Owner can Init/Read</P>
-     * @hide
-     */
-    public static final String COLUMN_URI = "uri";
-
-    /**
-     * The name of the column containing application-specific data.
-     * <P>Type: TEXT</P>
-     * <P>Owner can Init/Read/Write</P>
-     * @hide
-     */
-    public static final String COLUMN_APP_DATA = "entity";
-
-    /**
-     * The name of the column containing the flags that indicates whether
-     * the initiating application is capable of verifying the integrity of
-     * the downloaded file. When this flag is set, the download manager
-     * performs downloads and reports success even in some situations where
-     * it can't guarantee that the download has completed (e.g. when doing
-     * a byte-range request without an ETag, or when it can't determine
-     * whether a download fully completed).
-     * <P>Type: BOOLEAN</P>
-     * <P>Owner can Init</P>
-     * @hide
-     */
-    public static final String COLUMN_NO_INTEGRITY = "no_integrity";
-
-    /**
-     * The name of the column containing the filename that the initiating
-     * application recommends. When possible, the download manager will attempt
-     * to use this filename, or a variation, as the actual name for the file.
-     * <P>Type: TEXT</P>
-     * <P>Owner can Init</P>
-     * @hide
-     */
-    public static final String COLUMN_FILE_NAME_HINT = "hint";
-
-    /**
-     * The name of the column containing the filename where the downloaded data
-     * was actually stored.
-     * <P>Type: TEXT</P>
-     * <P>Owner can Read</P>
-     * @hide
-     */
-    public static final String _DATA = "_data";
-
-    /**
-     * The name of the column containing the MIME type of the downloaded data.
-     * <P>Type: TEXT</P>
-     * <P>Owner can Init/Read</P>
-     * @hide
-     */
-    public static final String COLUMN_MIME_TYPE = "mimetype";
-
-    /**
-     * The name of the column containing the flag that controls the destination
-     * of the download. See the DESTINATION_* constants for a list of legal values.
-     * <P>Type: INTEGER</P>
-     * <P>Owner can Init</P>
-     * @hide
-     */
-    public static final String COLUMN_DESTINATION = "destination";
-
-    /**
-     * The name of the column containing the flags that controls whether the
-     * download is displayed by the UI. See the VISIBILITY_* constants for
-     * a list of legal values.
-     * <P>Type: INTEGER</P>
-     * <P>Owner can Init/Read/Write</P>
-     * @hide
-     */
-    public static final String COLUMN_VISIBILITY = "visibility";
-
-    /**
-     * The name of the column containing the current control state  of the download.
-     * Applications can write to this to control (pause/resume) the download.
-     * the CONTROL_* constants for a list of legal values.
-     * <P>Type: INTEGER</P>
-     * <P>Owner can Read</P>
-     * @hide
-     */
-    public static final String COLUMN_CONTROL = "control";
-
-    /**
-     * The name of the column containing the current status of the download.
-     * Applications can read this to follow the progress of each download. See
-     * the STATUS_* constants for a list of legal values.
-     * <P>Type: INTEGER</P>
-     * <P>Owner can Read</P>
-     * @hide
-     */
-    public static final String COLUMN_STATUS = "status";
-
-    /**
-     * The name of the column containing the date at which some interesting
-     * status changed in the download. Stored as a System.currentTimeMillis()
-     * value.
-     * <P>Type: BIGINT</P>
-     * <P>Owner can Read</P>
-     * @hide
-     */
-    public static final String COLUMN_LAST_MODIFICATION = "lastmod";
-
-    /**
-     * The name of the column containing the package name of the application
-     * that initiating the download. The download manager will send
-     * notifications to a component in this package when the download completes.
-     * <P>Type: TEXT</P>
-     * <P>Owner can Init/Read</P>
-     * @hide
-     */
-    public static final String COLUMN_NOTIFICATION_PACKAGE = "notificationpackage";
-
-    /**
-     * The name of the column containing the component name of the class that
-     * will receive notifications associated with the download. The
-     * package/class combination is passed to
-     * Intent.setClassName(String,String).
-     * <P>Type: TEXT</P>
-     * <P>Owner can Init/Read</P>
-     * @hide
-     */
-    public static final String COLUMN_NOTIFICATION_CLASS = "notificationclass";
-
-    /**
-     * If extras are specified when requesting a download they will be provided in the intent that
-     * is sent to the specified class and package when a download has finished.
-     * <P>Type: TEXT</P>
-     * <P>Owner can Init</P>
-     * @hide
-     */
-    public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras";
-
-    /**
-     * The name of the column contain the values of the cookie to be used for
-     * the download. This is used directly as the value for the Cookie: HTTP
-     * header that gets sent with the request.
-     * <P>Type: TEXT</P>
-     * <P>Owner can Init</P>
-     * @hide
-     */
-    public static final String COLUMN_COOKIE_DATA = "cookiedata";
-
-    /**
-     * The name of the column containing the user agent that the initiating
-     * application wants the download manager to use for this download.
-     * <P>Type: TEXT</P>
-     * <P>Owner can Init</P>
-     * @hide
-     */
-    public static final String COLUMN_USER_AGENT = "useragent";
-
-    /**
-     * The name of the column containing the referer (sic) that the initiating
-     * application wants the download manager to use for this download.
-     * <P>Type: TEXT</P>
-     * <P>Owner can Init</P>
-     * @hide
-     */
-    public static final String COLUMN_REFERER = "referer";
-
-    /**
-     * The name of the column containing the total size of the file being
-     * downloaded.
-     * <P>Type: INTEGER</P>
-     * <P>Owner can Read</P>
-     * @hide
-     */
-    public static final String COLUMN_TOTAL_BYTES = "total_bytes";
-
-    /**
-     * The name of the column containing the size of the part of the file that
-     * has been downloaded so far.
-     * <P>Type: INTEGER</P>
-     * <P>Owner can Read</P>
-     * @hide
-     */
-    public static final String COLUMN_CURRENT_BYTES = "current_bytes";
-
-    /**
-     * The name of the column where the initiating application can provide the
-     * UID of another application that is allowed to access this download. If
-     * multiple applications share the same UID, all those applications will be
-     * allowed to access this download. This column can be updated after the
-     * download is initiated. This requires the permission
-     * android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED.
-     * <P>Type: INTEGER</P>
-     * <P>Owner can Init</P>
-     * @hide
-     */
-    public static final String COLUMN_OTHER_UID = "otheruid";
-
-    /**
-     * The name of the column where the initiating application can provided the
-     * title of this download. The title will be displayed ito the user in the
-     * list of downloads.
-     * <P>Type: TEXT</P>
-     * <P>Owner can Init/Read/Write</P>
-     * @hide
-     */
-    public static final String COLUMN_TITLE = "title";
-
-    /**
-     * The name of the column where the initiating application can provide the
-     * description of this download. The description will be displayed to the
-     * user in the list of downloads.
-     * <P>Type: TEXT</P>
-     * <P>Owner can Init/Read/Write</P>
-     * @hide
-     */
-    public static final String COLUMN_DESCRIPTION = "description";
-
-    /**
-     * Set to true if this download is deleted. It is completely removed from the database
-     * when MediaProvider database also deletes the metadata asociated with this downloaded file.
-     * <P>Type: BOOLEAN</P>
-     * <P>Owner can Read</P>
-     * @hide
-     */
-    public static final String COLUMN_DELETED = "deleted";
-
-    /*
-     * Lists the destinations that an application can specify for a download.
-     */
-
-    /**
-     * This download will be saved to the external storage. This is the
-     * default behavior, and should be used for any file that the user
-     * can freely access, copy, delete. Even with that destination,
-     * unencrypted DRM files are saved in secure internal storage.
-     * Downloads to the external destination only write files for which
-     * there is a registered handler. The resulting files are accessible
-     * by filename to all applications.
-     * @hide
-     */
-    public static final int DESTINATION_EXTERNAL = 0;
-
-    /**
-     * This download will be saved to the download manager's private
-     * partition. This is the behavior used by applications that want to
-     * download private files that are used and deleted soon after they
-     * get downloaded. All file types are allowed, and only the initiating
-     * application can access the file (indirectly through a content
-     * provider). This requires the
-     * android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED permission.
-     * @hide
-     */
-    public static final int DESTINATION_CACHE_PARTITION = 1;
-
-    /**
-     * This download will be saved to the download manager's private
-     * partition and will be purged as necessary to make space. This is
-     * for private files (similar to CACHE_PARTITION) that aren't deleted
-     * immediately after they are used, and are kept around by the download
-     * manager as long as space is available.
-     * @hide
-     */
-    public static final int DESTINATION_CACHE_PARTITION_PURGEABLE = 2;
-
-    /**
-     * This download will be saved to the download manager's private
-     * partition, as with DESTINATION_CACHE_PARTITION, but the download
-     * will not proceed if the user is on a roaming data connection.
-     * @hide
-     */
-    public static final int DESTINATION_CACHE_PARTITION_NOROAMING = 3;
-
-    /**
-     * This download is allowed to run.
-     * @hide
-     */
-    public static final int CONTROL_RUN = 0;
-
-    /**
-     * This download must pause at the first opportunity.
-     * @hide
-     */
-    public static final int CONTROL_PAUSED = 1;
-
-    /*
-     * Lists the states that the download manager can set on a download
-     * to notify applications of the download progress.
-     * The codes follow the HTTP families:<br>
-     * 1xx: informational<br>
-     * 2xx: success<br>
-     * 3xx: redirects (not used by the download manager)<br>
-     * 4xx: client errors<br>
-     * 5xx: server errors
-     */
-
-    /**
-     * Returns whether the status is informational (i.e. 1xx).
-     * @hide
-     */
-    public static boolean isStatusInformational(int status) {
-        return (status >= 100 && status < 200);
-    }
-
-    /**
-     * Returns whether the status is a success (i.e. 2xx).
-     * @hide
-     */
-    public static boolean isStatusSuccess(int status) {
-        return (status >= 200 && status < 300);
-    }
-
-    /**
-     * Returns whether the status is an error (i.e. 4xx or 5xx).
-     * @hide
-     */
-    public static boolean isStatusError(int status) {
-        return (status >= 400 && status < 600);
-    }
-
-    /**
-     * Returns whether the status is a client error (i.e. 4xx).
-     * @hide
-     */
-    public static boolean isStatusClientError(int status) {
-        return (status >= 400 && status < 500);
-    }
-
-    /**
-     * Returns whether the status is a server error (i.e. 5xx).
-     * @hide
-     */
-    public static boolean isStatusServerError(int status) {
-        return (status >= 500 && status < 600);
-    }
-
-    /**
-     * Returns whether the download has completed (either with success or
-     * error).
-     * @hide
-     */
-    public static boolean isStatusCompleted(int status) {
-        return (status >= 200 && status < 300) || (status >= 400 && status < 600);
-    }
-
-    /**
-     * This download hasn't stated yet
-     * @hide
-     */
-    public static final int STATUS_PENDING = 190;
-
-    /**
-     * This download has started
-     * @hide
-     */
-    public static final int STATUS_RUNNING = 192;
-
-    /**
-     * This download has successfully completed.
-     * Warning: there might be other status values that indicate success
-     * in the future.
-     * Use isSucccess() to capture the entire category.
-     * @hide
-     */
-    public static final int STATUS_SUCCESS = 200;
-
-    /**
-     * This request couldn't be parsed. This is also used when processing
-     * requests with unknown/unsupported URI schemes.
-     * @hide
-     */
-    public static final int STATUS_BAD_REQUEST = 400;
-
-    /**
-     * This download can't be performed because the content type cannot be
-     * handled.
-     * @hide
-     */
-    public static final int STATUS_NOT_ACCEPTABLE = 406;
-
-    /**
-     * This download cannot be performed because the length cannot be
-     * determined accurately. This is the code for the HTTP error "Length
-     * Required", which is typically used when making requests that require
-     * a content length but don't have one, and it is also used in the
-     * client when a response is received whose length cannot be determined
-     * accurately (therefore making it impossible to know when a download
-     * completes).
-     * @hide
-     */
-    public static final int STATUS_LENGTH_REQUIRED = 411;
-
-    /**
-     * This download was interrupted and cannot be resumed.
-     * This is the code for the HTTP error "Precondition Failed", and it is
-     * also used in situations where the client doesn't have an ETag at all.
-     * @hide
-     */
-    public static final int STATUS_PRECONDITION_FAILED = 412;
-
-    /**
-     * This download was canceled
-     * @hide
-     */
-    public static final int STATUS_CANCELED = 490;
-
-    /**
-     * This download has completed with an error.
-     * Warning: there will be other status values that indicate errors in
-     * the future. Use isStatusError() to capture the entire category.
-     * @hide
-     */
-    public static final int STATUS_UNKNOWN_ERROR = 491;
-
-    /**
-     * This download couldn't be completed because of a storage issue.
-     * Typically, that's because the filesystem is missing or full.
-     * Use the more specific {@link #STATUS_INSUFFICIENT_SPACE_ERROR}
-     * and {@link #STATUS_DEVICE_NOT_FOUND_ERROR} when appropriate.
-     * @hide
-     */
-    public static final int STATUS_FILE_ERROR = 492;
-
-    /**
-     * This download couldn't be completed because of an HTTP
-     * redirect response that the download manager couldn't
-     * handle.
-     * @hide
-     */
-    public static final int STATUS_UNHANDLED_REDIRECT = 493;
-
-    /**
-     * This download couldn't be completed because of an
-     * unspecified unhandled HTTP code.
-     * @hide
-     */
-    public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
-
-    /**
-     * This download couldn't be completed because of an
-     * error receiving or processing data at the HTTP level.
-     * @hide
-     */
-    public static final int STATUS_HTTP_DATA_ERROR = 495;
-
-    /**
-     * This download couldn't be completed because of an
-     * HttpException while setting up the request.
-     * @hide
-     */
-    public static final int STATUS_HTTP_EXCEPTION = 496;
-
-    /**
-     * This download couldn't be completed because there were
-     * too many redirects.
-     * @hide
-     */
-    public static final int STATUS_TOO_MANY_REDIRECTS = 497;
-
-    /**
-     * This download couldn't be completed due to insufficient storage
-     * space.  Typically, this is because the SD card is full.
-     * @hide
-     */
-    public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498;
-
-    /**
-     * This download couldn't be completed because no external storage
-     * device was found.  Typically, this is because the SD card is not
-     * mounted.
-     * @hide
-     */
-    public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499;
-
-    /**
-     * This download is visible but only shows in the notifications
-     * while it's in progress.
-     * @hide
-     */
-    public static final int VISIBILITY_VISIBLE = 0;
-
-    /**
-     * This download is visible and shows in the notifications while
-     * in progress and after completion.
-     * @hide
-     */
-    public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
-
-    /**
-     * This download doesn't show in the UI or in the notifications.
-     * @hide
-     */
-    public static final int VISIBILITY_HIDDEN = 2;
-
-    /**
      * Implementation details
      *
      * Exposes constants used to interact with the download manager's
@@ -636,6 +87,16 @@
         public static final Uri ALL_DOWNLOADS_CONTENT_URI =
                 Uri.parse("content://downloads/all_downloads");
 
+        /** URI segment to access a publicly accessible downloaded file */
+        public static final String PUBLICLY_ACCESSIBLE_DOWNLOADS_URI_SEGMENT = "public_downloads";
+
+        /**
+         * The content URI for accessing publicly accessible downloads (i.e., it requires no
+         * permissions to access this downloaded file)
+         */
+        public static final Uri PUBLICLY_ACCESSIBLE_DOWNLOADS_URI =
+                Uri.parse("content://downloads/" + PUBLICLY_ACCESSIBLE_DOWNLOADS_URI_SEGMENT);
+
         /**
          * Broadcast Action: this is sent by the download manager to the app
          * that had initiated a download when that download completes. The
@@ -907,6 +368,13 @@
          */
         public static final String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
 
+        /**
+         * The column that is used to remember whether the media scanner was invoked.
+         * It can take the values: null or 0(not scanned), 1(scanned), 2 (not scannable).
+         * <P>Type: TEXT</P>
+         */
+        public static final String COLUMN_MEDIA_SCANNED = "scanned";
+
         /*
          * Lists the destinations that an application can specify for a download.
          */
@@ -1171,18 +639,19 @@
          * This download is visible but only shows in the notifications
          * while it's in progress.
          */
-        public static final int VISIBILITY_VISIBLE = 0;
+        public static final int VISIBILITY_VISIBLE = DownloadManager.Request.VISIBILITY_VISIBLE;
 
         /**
          * This download is visible and shows in the notifications while
          * in progress and after completion.
          */
-        public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
+        public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED =
+                DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
 
         /**
          * This download doesn't show in the UI or in the notifications.
          */
-        public static final int VISIBILITY_HIDDEN = 2;
+        public static final int VISIBILITY_HIDDEN = DownloadManager.Request.VISIBILITY_HIDDEN;
 
         /**
          * Constants related to HTTP request headers associated with each download.
diff --git a/core/java/android/provider/DrmStore.java b/core/java/android/provider/DrmStore.java
index c438ac4..34f2f0d 100644
--- a/core/java/android/provider/DrmStore.java
+++ b/core/java/android/provider/DrmStore.java
@@ -131,7 +131,7 @@
      * Utility function for inserting a file stream into the DRM content provider.
      *
      * @param cr The content resolver to use
-     * @param fileStream The FileInputStream to insert
+     * @param fis The FileInputStream to insert
      * @param title The title for the content (or null)
      * @return uri to the DRM record or null
      */
@@ -143,11 +143,11 @@
             DrmRawContent content = new DrmRawContent(fis, (int) fis.available(),
                     DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING);
             String mimeType = content.getContentType();
+            long size = fis.getChannel().size();
 
             DrmRightsManager manager = manager = DrmRightsManager.getInstance();
             DrmRights rights = manager.queryRights(content);
             InputStream stream = content.getContentInputStream(rights);
-            long size = stream.available();
 
             Uri contentUri = null;
             if (mimeType.startsWith("audio/")) {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 075da33..f111ef2 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -253,9 +253,130 @@
          * <P>Type: TEXT</P>
          */
         public static final String MIME_TYPE = "mime_type";
+
+        /**
+         * The MTP object handle of a newly transfered file.
+         * Used to pass the new file's object handle through the media scanner
+         * from MTP to the media provider
+         * For internal use only by MTP, media scanner and media provider.
+         * <P>Type: INTEGER</P>
+         * @hide
+         */
+        public static final String MEDIA_SCANNER_NEW_OBJECT_ID = "media_scanner_new_object_id";
      }
 
     /**
+     * Media provider table containing an index of all files in the media storage,
+     * including non-media files.  This should be used by applications that work with
+     * non-media file types (text, HTML, PDF, etc) as well as applications that need to
+     * work with multiple media file types in a single query.
+     */
+    public static final class Files {
+
+        /**
+         * Get the content:// style URI for the files table on the
+         * given volume.
+         *
+         * @param volumeName the name of the volume to get the URI for
+         * @return the URI to the files table on the given volume
+         */
+        public static Uri getContentUri(String volumeName) {
+            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
+                    "/file");
+        }
+
+        /**
+         * Get the content:// style URI for a single row in the files table on the
+         * given volume.
+         *
+         * @param volumeName the name of the volume to get the URI for
+         * @param rowId the file to get the URI for
+         * @return the URI to the files table on the given volume
+         */
+        public static final Uri getContentUri(String volumeName,
+                long rowId) {
+            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
+                    + "/file/" + rowId);
+        }
+
+        /**
+         * For use only by the MTP implementation.
+         * @hide
+         */
+        public static Uri getMtpObjectsUri(String volumeName) {
+            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
+                    "/object");
+        }
+
+        /**
+         * For use only by the MTP implementation.
+         * @hide
+         */
+        public static final Uri getMtpObjectsUri(String volumeName,
+                long fileId) {
+            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
+                    + "/object/" + fileId);
+        }
+
+        /**
+         * Used to implement the MTP GetObjectReferences and SetObjectReferences commands.
+         * @hide
+         */
+        public static final Uri getMtpReferencesUri(String volumeName,
+                long fileId) {
+            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
+                    + "/object/" + fileId + "/references");
+        }
+
+        /**
+         * Fields for master table for all media files.
+         * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
+         */
+        public interface FileColumns extends MediaColumns {
+            /**
+             * The MTP format code of the file
+             * <P>Type: INTEGER</P>
+             * @hide
+             */
+            public static final String FORMAT = "format";
+
+            /**
+             * The index of the parent directory of the file
+             * <P>Type: INTEGER</P>
+             */
+            public static final String PARENT = "parent";
+
+            /**
+             * The MIME type of the file
+             * <P>Type: TEXT</P>
+             */
+            public static final String MIME_TYPE = "mime_type";
+
+            /**
+             * The title of the content
+             * <P>Type: TEXT</P>
+             */
+            public static final String TITLE = "title";
+
+            /**
+             * The media type (audio, video, image or playlist)
+             * of the file, or 0 for not a media file
+             * <P>Type: TEXT</P>
+             */
+            public static final String MEDIA_TYPE = "media_type";
+
+            /**
+             * Constants for MEDIA_TYPE
+             */
+            public static final int MEDIA_TYPE_NONE = 0;
+            public static final int MEDIA_TYPE_IMAGE = 1;
+            public static final int MEDIA_TYPE_AUDIO = 2;
+            public static final int MEDIA_TYPE_VIDEO = 3;
+            public static final int MEDIA_TYPE_PLAYLIST = 4;
+        }
+    }
+
+    /**
      * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
      * to be accessed elsewhere.
      */
@@ -335,26 +456,27 @@
             // Log.v(TAG, "getThumbnail: origId="+origId+", kind="+kind+", isVideo="+isVideo);
             // If the magic is non-zero, we simply return thumbnail if it does exist.
             // querying MediaProvider and simply return thumbnail.
-            MiniThumbFile thumbFile = MiniThumbFile.instance(baseUri);
-            long magic = thumbFile.getMagic(origId);
-            if (magic != 0) {
-                if (kind == MICRO_KIND) {
-                    synchronized (sThumbBufLock) {
-                        if (sThumbBuf == null) {
-                            sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
-                        }
-                        if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
-                            bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
-                            if (bitmap == null) {
-                                Log.w(TAG, "couldn't decode byte array.");
+            MiniThumbFile thumbFile = new MiniThumbFile(isVideo ? Video.Media.EXTERNAL_CONTENT_URI
+                    : Images.Media.EXTERNAL_CONTENT_URI);
+            Cursor c = null;
+            try {
+                long magic = thumbFile.getMagic(origId);
+                if (magic != 0) {
+                    if (kind == MICRO_KIND) {
+                        synchronized (sThumbBufLock) {
+                            if (sThumbBuf == null) {
+                                sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
+                            }
+                            if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
+                                bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
+                                if (bitmap == null) {
+                                    Log.w(TAG, "couldn't decode byte array.");
+                                }
                             }
                         }
-                    }
-                    return bitmap;
-                } else if (kind == MINI_KIND) {
-                    String column = isVideo ? "video_id=" : "image_id=";
-                    Cursor c = null;
-                    try {
+                        return bitmap;
+                    } else if (kind == MINI_KIND) {
+                        String column = isVideo ? "video_id=" : "image_id=";
                         c = cr.query(baseUri, PROJECTION, column + origId, null, null);
                         if (c != null && c.moveToFirst()) {
                             bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
@@ -362,17 +484,13 @@
                                 return bitmap;
                             }
                         }
-                    } finally {
-                        if (c != null) c.close();
                     }
                 }
-            }
 
-            Cursor c = null;
-            try {
                 Uri blockingUri = baseUri.buildUpon().appendQueryParameter("blocking", "1")
                         .appendQueryParameter("orig_id", String.valueOf(origId))
                         .appendQueryParameter("group_id", String.valueOf(groupId)).build();
+                if (c != null) c.close();
                 c = cr.query(blockingUri, PROJECTION, null, null, null);
                 // This happens when original image/video doesn't exist.
                 if (c == null) return null;
@@ -423,6 +541,9 @@
                 Log.w(TAG, ex);
             } finally {
                 if (c != null) c.close();
+                // To avoid file descriptor leak in application process.
+                thumbFile.deactivate();
+                thumbFile = null;
             }
             return bitmap;
         }
@@ -1140,6 +1261,19 @@
             }
 
             /**
+             * Get the content:// style URI for querying the genres of an audio file.
+             *
+             * @param volumeName the name of the volume to get the URI for
+             * @param audioId the ID of the audio file for which to retrieve the genres
+             * @return the URI to for querying the genres for the audio file
+             * with the given the volume and audioID
+             */
+            public static Uri getContentUriForAudioId(String volumeName, int audioId) {
+                return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
+                        "/audio/media/" + audioId + "/genres");
+            }
+
+            /**
              * The content:// style URI for the internal storage.
              */
             public static final Uri INTERNAL_CONTENT_URI =
diff --git a/core/java/android/provider/Mtp.java b/core/java/android/provider/Mtp.java
new file mode 100644
index 0000000..de161e7
--- /dev/null
+++ b/core/java/android/provider/Mtp.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider;
+
+import android.content.ContentUris;
+import android.net.Uri;
+import android.util.Log;
+
+
+/**
+ * The MTP provider supports accessing content on MTP and PTP devices.
+ * @hide
+ */
+public final class Mtp
+{
+    private final static String TAG = "Mtp";
+
+    public static final String AUTHORITY = "mtp";
+
+    private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
+    private static final String CONTENT_AUTHORITY_DEVICE_SLASH = "content://" + AUTHORITY + "/device/";
+
+
+   /**
+     * Broadcast Action:  A broadcast to indicate the end of an MTP session with the host.
+     * This broadcast is only sent if MTP activity has modified the media database during the
+     * most recent MTP session
+     */
+    public static final String ACTION_MTP_SESSION_END = "android.provider.action.MTP_SESSION_END";
+
+    /**
+     * Contains list of all MTP/PTP devices
+     */
+    public static final class Device implements BaseColumns {
+
+        public static final Uri CONTENT_URI = Uri.parse(CONTENT_AUTHORITY_SLASH + "device");
+
+        public static Uri getContentUri(int deviceID) {
+            return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID);
+        }
+
+        /**
+         * The manufacturer of the device
+         * <P>Type: TEXT</P>
+         */
+        public static final String MANUFACTURER = "manufacturer";
+
+        /**
+         * The model name of the device
+         * <P>Type: TEXT</P>
+         */
+        public static final String MODEL = "model";
+    }
+
+    /**
+     * Contains list of storage units for an MTP/PTP device
+     */
+    public static final class Storage implements BaseColumns {
+
+        public static Uri getContentUri(int deviceID) {
+            return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID + "/storage");
+        }
+
+        public static Uri getContentUri(int deviceID, long storageID) {
+            return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID + "/storage/" + storageID);
+        }
+
+        /**
+         * Storage unit identifier
+         * <P>Type: TEXT</P>
+         */
+        public static final String IDENTIFIER = "identifier";
+
+        /**
+         * Storage unit description
+         * <P>Type: TEXT</P>
+         */
+        public static final String DESCRIPTION = "description";
+    }
+
+    /**
+     * Contains list of objects on an MTP/PTP device
+     */
+    public static final class Object implements BaseColumns {
+
+        public static Uri getContentUri(int deviceID, long objectID) {
+            return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID
+                    + "/object/" + objectID);
+        }
+
+        public static Uri getContentUriForObjectChildren(int deviceID, long objectID) {
+            return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID
+                    + "/object/" + objectID + "/child");
+        }
+
+        public static Uri getContentUriForStorageChildren(int deviceID, long storageID) {
+            return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID
+                    + "/storage/" + storageID + "/child");
+        }
+
+        /**
+         * The following columns correspond to the fields in the ObjectInfo dataset
+         * as described in the MTP specification.
+         */
+
+        /**
+         * The ID of the storage unit containing the object.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String STORAGE_ID = "storage_id";
+
+        /**
+         * The object's format.  Can be one of the FORMAT_* symbols below,
+         * or any of the valid MTP object formats as defined in the MTP specification.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String FORMAT = "format";
+
+        /**
+         * The protection status of the object.  See the PROTECTION_STATUS_*symbols below.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String PROTECTION_STATUS = "protection_status";
+
+        /**
+         * The size of the object in bytes.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String SIZE = "size";
+
+        /**
+         * The object's thumbnail format.  Can be one of the FORMAT_* symbols below,
+         * or any of the valid MTP object formats as defined in the MTP specification.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String THUMB_FORMAT = "thumb_format";
+
+        /**
+         * The size of the object's thumbnail in bytes.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String THUMB_SIZE = "thumb_size";
+
+        /**
+         * The width of the object's thumbnail in pixels.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String THUMB_WIDTH = "thumb_width";
+
+        /**
+         * The height of the object's thumbnail in pixels.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String THUMB_HEIGHT = "thumb_height";
+
+        /**
+         * The object's thumbnail.
+         * <P>Type: BLOB</P>
+         */
+        public static final String THUMB = "thumb";
+
+        /**
+         * The width of the object in pixels.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String IMAGE_WIDTH = "image_width";
+
+        /**
+         * The height of the object in pixels.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String IMAGE_HEIGHT = "image_height";
+
+        /**
+         * The depth of the object in bits per pixel.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String IMAGE_DEPTH = "image_depth";
+
+        /**
+         * The ID of the object's parent, or zero if the object
+         * is in the root of its storage unit.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String PARENT = "parent";
+
+        /**
+         * The association type for a container object.
+         * For folders this is typically {@link #ASSOCIATION_TYPE_GENERIC_FOLDER}
+         * <P>Type: INTEGER</P>
+         */
+        public static final String ASSOCIATION_TYPE = "association_type";
+
+        /**
+         * Contains additional information about container objects.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String ASSOCIATION_DESC = "association_desc";
+
+        /**
+         * The sequence number of the object, typically used for an association
+         * containing images taken in sequence.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String SEQUENCE_NUMBER = "sequence_number";
+
+        /**
+         * The name of the object.
+         * <P>Type: TEXT</P>
+         */
+        public static final String NAME = "name";
+
+        /**
+         * The date the object was created, in seconds since January 1, 1970.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATE_CREATED = "date_created";
+
+        /**
+         * The date the object was last modified, in seconds since January 1, 1970.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DATE_MODIFIED = "date_modified";
+
+        /**
+         * A list of keywords associated with an object, separated by spaces.
+         * <P>Type: TEXT</P>
+         */
+        public static final String KEYWORDS = "keywords";
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2a13d520..9c72dec 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16,14 +16,11 @@
 
 package android.provider;
 
-import com.google.android.collect.Maps;
 
-import org.apache.commons.codec.binary.Base64;
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ComponentName;
-import android.content.ContentQueryMap;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -37,20 +34,18 @@
 import android.database.Cursor;
 import android.database.SQLException;
 import android.net.Uri;
-import android.os.*;
-import android.telephony.TelephonyManager;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.AndroidException;
 import android.util.Config;
 import android.util.Log;
 
 import java.net.URISyntaxException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Map;
 
 
 /**
@@ -284,6 +279,20 @@
             "android.settings.INPUT_METHOD_SETTINGS";
 
     /**
+     * Activity Action: Show enabler activity to enable/disable input methods and subtypes.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_INPUT_METHOD_AND_SUBTYPE_ENABLER =
+            "android.settings.INPUT_METHOD_AND_SUBTYPE_ENABLER";
+
+    /**
      * Activity Action: Show settings to manage the user input dictionary.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -1023,7 +1032,7 @@
         public static boolean hasInterestingConfigurationChanges(int changes) {
             return (changes&ActivityInfo.CONFIG_FONT_SCALE) != 0;
         }
-        
+
         public static boolean getShowGTalkServiceStatus(ContentResolver cr) {
             return getInt(cr, SHOW_GTALK_SERVICE_STATUS, 0) != 0;
         }
@@ -1147,6 +1156,7 @@
          */
         public static final int WIFI_SLEEP_POLICY_NEVER = 2;
 
+        //TODO: deprecate static IP constants
         /**
          * Whether to use static IP and other static network attributes.
          * <p>
@@ -1189,12 +1199,6 @@
          */
         public static final String WIFI_STATIC_DNS2 = "wifi_static_dns2";
 
-        /**
-         * The number of radio channels that are allowed in the local
-         * 802.11 regulatory domain.
-         * @hide
-         */
-        public static final String WIFI_NUM_ALLOWED_CHANNELS = "wifi_num_allowed_channels";
 
         /**
          * Determines whether remote devices may discover and/or connect to
@@ -1230,7 +1234,7 @@
         public static final String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
 
         /**
-         * @deprecated Use 
+         * @deprecated Use
          * {@link android.provider.Settings.Secure#LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED}
          * instead
          */
@@ -1512,6 +1516,12 @@
         public static final String AUTO_TIME = "auto_time";
 
         /**
+         * Value to specify if the user prefers the time zone
+         * to be automatically fetched from the network (NITZ). 1=yes, 0=no
+         */
+        public static final String AUTO_TIME_ZONE = "auto_time_zone";
+
+        /**
          * Display times as 12 or 24 hours
          *   12
          *   24
@@ -1563,6 +1573,16 @@
         public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation";
 
         /**
+         * Default screen rotation when no other policy applies.
+         * When {@link #ACCELEROMETER_ROTATION} is zero and no on-screen Activity expresses a
+         * preference, this rotation value will be used. Must be one of the
+         * {@link android.view.Surface#ROTATION_0 Surface rotation constants}. 
+         *
+         * @see Display#getRotation
+         */
+        public static final String USER_ROTATION = "user_rotation";
+
+        /**
          * Whether the audible DTMF tones are played by the dialer when dialing. The value is
          * boolean (1 or 0).
          */
@@ -1625,11 +1645,10 @@
         public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled";
 
         /**
-         * Whether live web suggestions while the user types into search dialogs are
-         * enabled. Browsers and other search UIs should respect this, as it allows
-         * a user to avoid sending partial queries to a search engine, if it poses
-         * any privacy concern. The value is boolean (1 or 0).
+         * @deprecated Each application that shows web suggestions should have its own
+         * setting for this.
          */
+        @Deprecated
         public static final String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
 
         /**
@@ -1708,6 +1727,12 @@
         public static final String UNLOCK_SOUND = "unlock_sound";
 
         /**
+         * True if we should appear as a PTP device instead of MTP.
+         * @hide
+         */
+        public static final String USE_PTP_INTERFACE = "use_ptp_interface";
+
+        /**
          * Receive incoming SIP calls?
          * 0 = no
          * 1 = yes
@@ -1787,9 +1812,11 @@
             TEXT_AUTO_PUNCTUATE,
             TEXT_SHOW_PASSWORD,
             AUTO_TIME,
+            AUTO_TIME_ZONE,
             TIME_12_24,
             DATE_FORMAT,
             ACCELEROMETER_ROTATION,
+            USER_ROTATION,
             DTMF_TONE_WHEN_DIALING,
             DTMF_TONE_TYPE_WHEN_DIALING,
             EMERGENCY_TONE,
@@ -1803,6 +1830,7 @@
             LOCKSCREEN_SOUNDS_ENABLED,
             SHOW_WEB_SUGGESTIONS,
             NOTIFICATION_LIGHT_PULSE,
+            USE_PTP_INTERFACE,
             SIP_CALL_OPTIONS,
             SIP_RECEIVE_CALLS,
         };
@@ -2341,6 +2369,14 @@
         }
 
         /**
+         * Get the key that retrieves a bluetooth Input Device's priority.
+         * @hide
+         */
+        public static final String getBluetoothInputDevicePriorityKey(String address) {
+            return ("bluetooth_input_device_priority_" + address.toUpperCase());
+        }
+
+        /**
          * Whether or not data roaming is enabled. (0 = false, 1 = true)
          */
         public static final String DATA_ROAMING = "data_roaming";
@@ -2352,6 +2388,13 @@
         public static final String DEFAULT_INPUT_METHOD = "default_input_method";
 
         /**
+         * Setting to record the input method subtype used by default, holding the ID
+         * of the desired method.
+         */
+        public static final String SELECTED_INPUT_METHOD_SUBTYPE =
+                "selected_input_method_subtype";
+
+        /**
          * Whether the device has been provisioned (0 = false, 1 = true)
          */
         public static final String DEVICE_PROVISIONED = "device_provisioned";
@@ -2372,11 +2415,32 @@
         public static final String DISABLED_SYSTEM_INPUT_METHODS = "disabled_system_input_methods";
 
         /**
-         * Host name and port for a user-selected proxy.
+         * Host name and port for global proxy.
          */
         public static final String HTTP_PROXY = "http_proxy";
 
         /**
+         * Exclusion list for global proxy. This string contains a list of comma-separated
+         * domains where the global proxy does not apply. Domains should be listed in a comma-
+         * separated list. Example of acceptable formats: ".domain1.com,my.domain2.com"
+         * @hide
+         */
+        public static final String HTTP_PROXY_EXCLUSION_LIST = "http_proxy_exclusion_list";
+
+        /**
+         * Enables the UI setting to allow the user to specify the global HTTP proxy
+         * and associated exclusion list.
+         * @hide
+         */
+        public static final String SET_GLOBAL_HTTP_PROXY = "set_global_http_proxy";
+
+        /**
+         * Setting for default DNS in case nobody suggests one
+         * @hide
+         */
+        public static final String DEFAULT_DNS_SERVER = "default_dns_server";
+
+        /**
          * Whether the package installer should allow installation of apps downloaded from
          * sources other than the Android Market (vending machine).
          *
@@ -2407,6 +2471,27 @@
             "lock_pattern_tactile_feedback_enabled";
 
         /**
+         * This preference allows the device to be locked given time after screen goes off,
+         * subject to current DeviceAdmin policy limits.
+         * @hide
+         */
+        public static final String LOCK_SCREEN_LOCK_AFTER_TIMEOUT = "lock_screen_lock_after_timeout";
+
+
+        /**
+         * This preference contains the string that shows for owner info on LockScren.
+         * @hide
+         */
+        public static final String LOCK_SCREEN_OWNER_INFO = "lock_screen_owner_info";
+
+        /**
+         * This preference enables showing the owner info on LockScren.
+         * @hide
+         */
+        public static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
+            "lock_screen_owner_info_enabled";
+
+        /**
          * Whether assisted GPS should be enabled or not.
          * @hide
          */
@@ -2467,6 +2552,14 @@
         public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
 
         /**
+         * A positive value indicates the frequency of SamplingProfiler
+         * taking snapshots in hertz. Zero value means SamplingProfiler is disabled.
+         *
+         * @hide
+         */
+        public static final String SAMPLING_PROFILER_HZ = "sampling_profiler_hz";
+
+        /**
          * Settings classname to launch when Settings is clicked from All
          * Applications.  Needed because of user testing between the old
          * and new Settings apps.
@@ -2497,6 +2590,60 @@
             "enabled_accessibility_services";
 
         /**
+         * If injection of accessibility enhancing JavaScript scripts
+         * is enabled.
+         * <p>
+         *   Note: Accessibility injecting scripts are served by the
+         *   Google infrastructure and enable users with disabilities to
+         *   efficiantly navigate in and explore web content.
+         * </p>
+         * <p>
+         *   This property represents a boolean value.
+         * </p>
+         * @hide
+         */
+        public static final String ACCESSIBILITY_SCRIPT_INJECTION =
+            "accessibility_script_injection";
+
+        /**
+         * Key bindings for navigation in built-in accessibility support for web content.
+         * <p>
+         *   Note: These key bindings are for the built-in accessibility navigation for
+         *   web content which is used as a fall back solution if JavaScript in a WebView
+         *   is not enabled or the user has not opted-in script injection from Google.
+         * </p>
+         * <p>
+         *   The bindings are separated by semi-colon. A binding is a mapping from
+         *   a key to a sequence of actions (for more details look at
+         *   android.webkit.AccessibilityInjector). A key is represented as the hexademical
+         *   string representation of an integer obtained from a meta state (optional) shifted
+         *   sixteen times left and bitwise ored with a key code. An action is represented
+         *   as a hexademical string representation of an integer where the first two digits
+         *   are navigation action index, the second, the third, and the fourth digit pairs
+         *   represent the action arguments. The separate actions in a binding are colon
+         *   separated. The key and the action sequence it maps to are separated by equals.
+         * </p>
+         * <p>
+         *   For example, the binding below maps the DPAD right button to traverse the
+         *   current navigation axis once without firing an accessibility event and to
+         *   perform the same traversal again but to fire an event:
+         *   <code>
+         *     0x16=0x01000100:0x01000101;
+         *   </code>
+         * </p>
+         * <p>
+         *   The goal of this binding is to enable dynamic rebinding of keys to
+         *   navigation actions for web content without requiring a framework change.
+         * </p>
+         * <p>
+         *   This property represents a string value.
+         * </p>
+         * @hide
+         */
+        public static final String ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS =
+            "accessibility_web_content_key_bindings";
+
+        /**
          * Setting to always use the default text-to-speech settings regardless
          * of the application settings.
          * 1 = override application settings,
@@ -2559,11 +2706,11 @@
                 "wifi_networks_available_repeat_delay";
 
         /**
-         * The number of radio channels that are allowed in the local
-         * 802.11 regulatory domain.
+         * 802.11 country code in ISO 3166 format
          * @hide
          */
-        public static final String WIFI_NUM_ALLOWED_CHANNELS = "wifi_num_allowed_channels";
+        public static final String WIFI_COUNTRY_CODE = "wifi_country_code";
+
 
         /**
          * When the number of open networks exceeds this number, the
@@ -3076,6 +3223,14 @@
         public static final String WIFI_IDLE_MS = "wifi_idle_ms";
 
         /**
+         * The interval in milliseconds to issue scans when the driver is
+         * started. This is necessary to allow wifi to connect to an
+         * access point when the driver is suspended.
+         * @hide
+         */
+        public static final String WIFI_SCAN_INTERVAL_MS = "wifi_scan_interval_ms";
+
+        /**
          * The interval in milliseconds at which to check packet counts on the
          * mobile data interface when screen is on, to detect possible data
          * connection problems.
@@ -3476,6 +3631,7 @@
             PARENTAL_CONTROL_REDIRECT_URL,
             USB_MASS_STORAGE_ENABLED,
             ACCESSIBILITY_ENABLED,
+            ACCESSIBILITY_SCRIPT_INJECTION,
             BACKUP_AUTO_RESTORE,
             ENABLED_ACCESSIBILITY_SERVICES,
             TTS_USE_DEFAULTS,
@@ -3487,7 +3643,7 @@
             TTS_ENABLED_PLUGINS,
             WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
             WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
-            WIFI_NUM_ALLOWED_CHANNELS,
+            WIFI_COUNTRY_CODE,
             WIFI_NUM_OPEN_NETWORKS_KEPT,
             MOUNT_PLAY_NOTIFICATION_SND,
             MOUNT_UMS_AUTOSTART,
@@ -3659,20 +3815,8 @@
             // If a shortcut is supplied, and it is already defined for
             // another bookmark, then remove the old definition.
             if (shortcut != 0) {
-                Cursor c = cr.query(CONTENT_URI,
-                        sShortcutProjection, sShortcutSelection,
-                        new String[] { String.valueOf((int) shortcut) }, null);
-                try {
-                    if (c.moveToFirst()) {
-                        while (c.getCount() > 0) {
-                            if (!c.deleteRow()) {
-                                Log.w(TAG, "Could not delete existing shortcut row");
-                            }
-                        }
-                    }
-                } finally {
-                    if (c != null) c.close();
-                }
+                cr.delete(CONTENT_URI, sShortcutSelection,
+                        new String[] { String.valueOf((int) shortcut) });
             }
 
             ContentValues values = new ContentValues();
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 6e221c8..b5e85a0 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -25,6 +25,7 @@
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.IBluetoothA2dp;
 import android.content.BroadcastReceiver;
@@ -32,17 +33,16 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.media.AudioManager;
-import android.os.Handler;
-import android.os.Message;
 import android.os.ParcelUuid;
 import android.provider.Settings;
 import android.util.Log;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.List;
+
 
 public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
     private static final String TAG = "BluetoothA2dpService";
@@ -57,11 +57,6 @@
 
     private static final String PROPERTY_STATE = "State";
 
-    private static final String SINK_STATE_DISCONNECTED = "disconnected";
-    private static final String SINK_STATE_CONNECTING = "connecting";
-    private static final String SINK_STATE_CONNECTED = "connected";
-    private static final String SINK_STATE_PLAYING = "playing";
-
     private static int mSinkCount;
 
     private final Context mContext;
@@ -71,6 +66,7 @@
     private final BluetoothService mBluetoothService;
     private final BluetoothAdapter mAdapter;
     private int   mTargetA2dpState;
+    private BluetoothDevice mPlayingA2dpDevice;
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -94,12 +90,12 @@
                                                    BluetoothDevice.ERROR);
                 switch(bondState) {
                 case BluetoothDevice.BOND_BONDED:
-                    if (getSinkPriority(device) == BluetoothA2dp.PRIORITY_UNDEFINED) {
-                        setSinkPriority(device, BluetoothA2dp.PRIORITY_ON);
+                    if (getPriority(device) == BluetoothA2dp.PRIORITY_UNDEFINED) {
+                        setPriority(device, BluetoothA2dp.PRIORITY_ON);
                     }
                     break;
                 case BluetoothDevice.BOND_NONE:
-                    setSinkPriority(device, BluetoothA2dp.PRIORITY_UNDEFINED);
+                    setPriority(device, BluetoothA2dp.PRIORITY_UNDEFINED);
                     break;
                 }
             } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
@@ -112,9 +108,10 @@
             } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
                 if (streamType == AudioManager.STREAM_MUSIC) {
-                    BluetoothDevice sinks[] = getConnectedSinks();
-                    if (sinks.length != 0 && isPhoneDocked(sinks[0])) {
-                        String address = sinks[0].getAddress();
+                    List<BluetoothDevice> sinks = getConnectedDevices();
+
+                    if (sinks.size() != 0 && isPhoneDocked(sinks.get(0))) {
+                        String address = sinks.get(0).getAddress();
                         int newVolLevel =
                           intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
                         int oldVolLevel =
@@ -253,7 +250,7 @@
             BluetoothDevice[] devices = new BluetoothDevice[mAudioDevices.size()];
             devices = mAudioDevices.keySet().toArray(devices);
             for (BluetoothDevice device : devices) {
-                int state = getSinkState(device);
+                int state = getConnectionState(device);
                 switch (state) {
                     case BluetoothA2dp.STATE_CONNECTING:
                     case BluetoothA2dp.STATE_CONNECTED:
@@ -276,7 +273,7 @@
 
     private synchronized boolean isConnectSinkFeasible(BluetoothDevice device) {
         if (!mBluetoothService.isEnabled() || !isSinkDevice(device) ||
-                getSinkPriority(device) == BluetoothA2dp.PRIORITY_OFF) {
+                getPriority(device) == BluetoothA2dp.PRIORITY_OFF) {
                 return false;
             }
 
@@ -291,12 +288,26 @@
             return true;
     }
 
-    public synchronized boolean connectSink(BluetoothDevice device) {
+    public synchronized boolean isA2dpPlaying(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+            "Need BLUETOOTH_ADMIN permission");
+        if (DBG) log("isA2dpPlaying(" + device + ")");
+        if (device.equals(mPlayingA2dpDevice)) return true;
+        return false;
+    }
+
+    public synchronized boolean connect(BluetoothDevice device) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
         if (DBG) log("connectSink(" + device + ")");
         if (!isConnectSinkFeasible(device)) return false;
 
+        for (BluetoothDevice sinkDevice : mAudioDevices.keySet()) {
+            if (getConnectionState(sinkDevice) != BluetoothProfile.STATE_DISCONNECTED) {
+                disconnect(sinkDevice);
+            }
+        }
+
         return mBluetoothService.connectSink(device.getAddress());
     }
 
@@ -306,17 +317,15 @@
         int state = mAudioDevices.get(device);
 
         // ignore if there are any active sinks
-        if (lookupSinksMatchingStates(new int[] {
+        if (getDevicesMatchingConnectionStates(new int[] {
                 BluetoothA2dp.STATE_CONNECTING,
                 BluetoothA2dp.STATE_CONNECTED,
-                BluetoothA2dp.STATE_PLAYING,
                 BluetoothA2dp.STATE_DISCONNECTING}).size() != 0) {
             return false;
         }
 
         switch (state) {
         case BluetoothA2dp.STATE_CONNECTED:
-        case BluetoothA2dp.STATE_PLAYING:
         case BluetoothA2dp.STATE_DISCONNECTING:
             return false;
         case BluetoothA2dp.STATE_CONNECTING:
@@ -326,8 +335,8 @@
         String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
 
         // State is DISCONNECTED and we are connecting.
-        if (getSinkPriority(device) < BluetoothA2dp.PRIORITY_AUTO_CONNECT) {
-            setSinkPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
+        if (getPriority(device) < BluetoothA2dp.PRIORITY_AUTO_CONNECT) {
+            setPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
         }
         handleSinkStateChange(device, state, BluetoothA2dp.STATE_CONNECTING);
 
@@ -345,17 +354,16 @@
             return false;
         }
 
-        int state = getSinkState(device);
+        int state = getConnectionState(device);
         switch (state) {
         case BluetoothA2dp.STATE_DISCONNECTED:
-            return false;
         case BluetoothA2dp.STATE_DISCONNECTING:
-            return true;
+            return false;
         }
         return true;
     }
 
-    public synchronized boolean disconnectSink(BluetoothDevice device) {
+    public synchronized boolean disconnect(BluetoothDevice device) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
         if (DBG) log("disconnectSink(" + device + ")");
@@ -364,7 +372,7 @@
     }
 
     public synchronized boolean disconnectSinkInternal(BluetoothDevice device) {
-        int state = getSinkState(device);
+        int state = getConnectionState(device);
         String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
 
         switch (state) {
@@ -415,24 +423,7 @@
         return checkSinkSuspendState(state.intValue());
     }
 
-    public synchronized BluetoothDevice[] getConnectedSinks() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        Set<BluetoothDevice> sinks = lookupSinksMatchingStates(
-                new int[] {BluetoothA2dp.STATE_CONNECTED, BluetoothA2dp.STATE_PLAYING});
-        return sinks.toArray(new BluetoothDevice[sinks.size()]);
-    }
-
-    public synchronized BluetoothDevice[] getNonDisconnectedSinks() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        Set<BluetoothDevice> sinks = lookupSinksMatchingStates(
-                new int[] {BluetoothA2dp.STATE_CONNECTED,
-                           BluetoothA2dp.STATE_PLAYING,
-                           BluetoothA2dp.STATE_CONNECTING,
-                           BluetoothA2dp.STATE_DISCONNECTING});
-        return sinks.toArray(new BluetoothDevice[sinks.size()]);
-    }
-
-    public synchronized int getSinkState(BluetoothDevice device) {
+    public synchronized int getConnectionState(BluetoothDevice device) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         Integer state = mAudioDevices.get(device);
         if (state == null)
@@ -440,19 +431,38 @@
         return state;
     }
 
-    public synchronized int getSinkPriority(BluetoothDevice device) {
+    public synchronized List<BluetoothDevice> getConnectedDevices() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        List<BluetoothDevice> sinks = getDevicesMatchingConnectionStates(
+                new int[] {BluetoothA2dp.STATE_CONNECTED});
+        return sinks;
+    }
+
+    public synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        ArrayList<BluetoothDevice> sinks = new ArrayList<BluetoothDevice>();
+        for (BluetoothDevice device: mAudioDevices.keySet()) {
+            int sinkState = getConnectionState(device);
+            for (int state : states) {
+                if (state == sinkState) {
+                    sinks.add(device);
+                    break;
+                }
+            }
+        }
+        return sinks;
+    }
+
+    public synchronized int getPriority(BluetoothDevice device) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         return Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
                 BluetoothA2dp.PRIORITY_UNDEFINED);
     }
 
-    public synchronized boolean setSinkPriority(BluetoothDevice device, int priority) {
+    public synchronized boolean setPriority(BluetoothDevice device, int priority) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
-        if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
-            return false;
-        }
         return Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority);
     }
@@ -473,13 +483,25 @@
 
         if (name.equals(PROPERTY_STATE)) {
             int state = convertBluezSinkStringtoState(propValues[1]);
+            log("A2DP: onSinkPropertyChanged newState is: " + state + "mPlayingA2dpDevice: " + mPlayingA2dpDevice);
+
             if (mAudioDevices.get(device) == null) {
                 // This is for an incoming connection for a device not known to us.
                 // We have authorized it and bluez state has changed.
                 addAudioSink(device);
             } else {
-                int prevState = mAudioDevices.get(device);
-                handleSinkStateChange(device, prevState, state);
+                if (state == BluetoothA2dp.STATE_PLAYING && mPlayingA2dpDevice == null) {
+                   mPlayingA2dpDevice = device;
+                   handleSinkPlayingStateChange(device, state, BluetoothA2dp.STATE_NOT_PLAYING);
+                } else if (state == BluetoothA2dp.STATE_CONNECTED && mPlayingA2dpDevice != null) {
+                    mPlayingA2dpDevice = null;
+                    handleSinkPlayingStateChange(device, BluetoothA2dp.STATE_NOT_PLAYING,
+                        BluetoothA2dp.STATE_PLAYING);
+                } else {
+                   mPlayingA2dpDevice = null;
+                   int prevState = mAudioDevices.get(device);
+                   handleSinkStateChange(device, prevState, state);
+                }
             }
         }
     }
@@ -497,52 +519,47 @@
             checkSinkSuspendState(state);
             mTargetA2dpState = -1;
 
-            if (getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF &&
+            if (getPriority(device) > BluetoothA2dp.PRIORITY_OFF &&
                     state == BluetoothA2dp.STATE_CONNECTED) {
                 // We have connected or attempting to connect.
                 // Bump priority
-                setSinkPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
+                setPriority(device, BluetoothA2dp.PRIORITY_AUTO_CONNECT);
                 // We will only have 1 device with AUTO_CONNECT priority
                 // To be backward compatible set everyone else to have PRIORITY_ON
                 adjustOtherSinkPriorities(device);
             }
 
-            Intent intent = new Intent(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
+            Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-            intent.putExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, prevState);
-            intent.putExtra(BluetoothA2dp.EXTRA_SINK_STATE, state);
+            intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+            intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
 
             if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state);
+
+            mBluetoothService.sendConnectionStateChange(device, state, prevState);
         }
     }
 
+    private void handleSinkPlayingStateChange(BluetoothDevice device, int state, int prevState) {
+        Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
+        intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
+        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+
+        if (DBG) log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state);
+    }
+
     private void adjustOtherSinkPriorities(BluetoothDevice connectedDevice) {
         for (BluetoothDevice device : mAdapter.getBondedDevices()) {
-            if (getSinkPriority(device) >= BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
+            if (getPriority(device) >= BluetoothA2dp.PRIORITY_AUTO_CONNECT &&
                 !device.equals(connectedDevice)) {
-                setSinkPriority(device, BluetoothA2dp.PRIORITY_ON);
+                setPriority(device, BluetoothA2dp.PRIORITY_ON);
             }
         }
     }
 
-    private synchronized Set<BluetoothDevice> lookupSinksMatchingStates(int[] states) {
-        Set<BluetoothDevice> sinks = new HashSet<BluetoothDevice>();
-        if (mAudioDevices.isEmpty()) {
-            return sinks;
-        }
-        for (BluetoothDevice device: mAudioDevices.keySet()) {
-            int sinkState = getSinkState(device);
-            for (int state : states) {
-                if (state == sinkState) {
-                    sinks.add(device);
-                    break;
-                }
-            }
-        }
-        return sinks;
-    }
-
     private boolean checkSinkSuspendState(int state) {
         boolean result = true;
 
@@ -568,7 +585,7 @@
                 String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
                 if (address == null) return;
                 BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                int state = getSinkState(device);
+                int state = getConnectionState(device);
                 handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTED);
             }
         }
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 80d6d30..4f56281 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -20,17 +20,21 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Message;
 import android.os.ParcelUuid;
+import android.os.PowerManager;
 import android.util.Log;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Set;
-import android.os.PowerManager;
 
 
 /**
@@ -52,6 +56,7 @@
     private final HashMap<String, Integer> mPasskeyAgentRequestData;
     private final BluetoothService mBluetoothService;
     private final BluetoothAdapter mAdapter;
+    private BluetoothA2dp mA2dp;
     private final Context mContext;
     // The WakeLock is used for bringing up the LCD during a pairing request
     // from remote device when Android is in Suspend state.
@@ -118,6 +123,21 @@
         initializeNativeDataNative();
     }
 
+    /*package*/ void getProfileProxy() {
+        mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
+    }
+
+    private BluetoothProfile.ServiceListener mProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            mA2dp = (BluetoothA2dp) proxy;
+        }
+        public void onServiceDisconnected(int profile) {
+            mA2dp = null;
+        }
+    };
+
+
     protected void finalize() throws Throwable {
         try {
             cleanupNativeDataNative();
@@ -233,6 +253,7 @@
             // we filled up our cache.
             mBluetoothService.getAllProperties();
         }
+        log("Property Changed: " + propValues[0] + " : " + propValues[1]);
         String name = propValues[0];
         if (name.equals("Name")) {
             mBluetoothService.setProperty(name, propValues[1]);
@@ -289,6 +310,8 @@
             // Note: bluez only sends this property change when it restarts.
             if (propValues[1].equals("true"))
                 onRestartRequired();
+        } else if (name.equals("DiscoverableTimeout")) {
+            mBluetoothService.setProperty(name, propValues[1]);
         }
     }
 
@@ -299,9 +322,9 @@
             Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null");
             return;
         }
-        if (DBG) {
-            log("Device property changed:" + address + "property:" + name);
-        }
+        log("Device property changed: " + address + " property: "
+            + name + " value: " + propValues[1]);
+
         BluetoothDevice device = mAdapter.getRemoteDevice(address);
         if (name.equals("Name")) {
             mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
@@ -367,6 +390,44 @@
         }
     }
 
+    private void onInputDevicePropertyChanged(String path, String[] propValues) {
+        String address = mBluetoothService.getAddressFromObjectPath(path);
+        if (address == null) {
+            Log.e(TAG, "onInputDevicePropertyChanged: Address of the remote device in null");
+            return;
+        }
+        log(" Input Device : Name of Property is:" + propValues[0]);
+        boolean state = false;
+        if (propValues[1].equals("true")) {
+            state = true;
+        }
+        mBluetoothService.handleInputDevicePropertyChange(address, state);
+    }
+
+    private void onPanDevicePropertyChanged(String deviceObjectPath, String[] propValues) {
+        String name = propValues[0];
+        String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+        if (address == null) {
+            Log.e(TAG, "onPanDevicePropertyChanged: Address of the remote device in null");
+            return;
+        }
+        if (DBG) {
+            log("Pan Device property changed: " + address + "  property: "
+                    + name + " value: "+ propValues[1]);
+        }
+        BluetoothDevice device = mAdapter.getRemoteDevice(address);
+        if (name.equals("Connected")) {
+            if (propValues[1].equals("false")) {
+                mBluetoothService.handlePanDeviceStateChange(device,
+                                          BluetoothInputDevice.STATE_DISCONNECTED);
+            }
+        } else if (name.equals("Interface")) {
+            String iface = propValues[1];
+            mBluetoothService.handlePanDeviceStateChange(device, iface,
+                                            BluetoothInputDevice.STATE_CONNECTED);
+        }
+    }
+
     private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
         String address = mBluetoothService.getAddressFromObjectPath(objectPath);
         if (address == null) {
@@ -519,6 +580,8 @@
     }
 
     private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
+        if (!mBluetoothService.isEnabled()) return false;
+
         String address = mBluetoothService.getAddressFromObjectPath(objectPath);
         if (address == null) {
             Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
@@ -527,16 +590,15 @@
 
         boolean authorized = false;
         ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
-        BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
+        BluetoothDevice device = mAdapter.getRemoteDevice(address);
 
         // Bluez sends the UUID of the local service being accessed, _not_ the
         // remote service
-        if (mBluetoothService.isEnabled() &&
-                (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
-                        || BluetoothUuid.isAdvAudioDist(uuid)) &&
-                        !isOtherSinkInNonDisconnectingState(address)) {
-            BluetoothDevice device = mAdapter.getRemoteDevice(address);
-            authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
+        if (mA2dp != null &&
+            (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
+              || BluetoothUuid.isAdvAudioDist(uuid)) &&
+              !isOtherSinkInNonDisconnectedState(address)) {
+            authorized = mA2dp.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
             if (authorized) {
                 Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
                 // Some headsets try to connect AVCTP before AVDTP - against the recommendation
@@ -550,6 +612,18 @@
             } else {
                 Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
             }
+        } else if (BluetoothUuid.isInputDevice(uuid) && !isOtherInputDeviceConnected(address)) {
+            BluetoothInputDevice inputDevice = new BluetoothInputDevice(mContext);
+            authorized = inputDevice.getInputDevicePriority(device) >
+                         BluetoothInputDevice.PRIORITY_OFF;
+             if (authorized) {
+                 Log.i(TAG, "Allowing incoming HID connection from " + address);
+             } else {
+                 Log.i(TAG, "Rejecting incoming HID connection from " + address);
+             }
+        } else if (BluetoothUuid.isBnep(uuid) || BluetoothUuid.isNap(uuid) &&
+                mBluetoothService.allowIncomingTethering()){
+            authorized = true;
         } else {
             Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
         }
@@ -557,6 +631,18 @@
         return authorized;
     }
 
+    private boolean isOtherInputDeviceConnected(String address) {
+        List<BluetoothDevice> devices =
+            mBluetoothService.lookupInputDevicesMatchingStates(new int[] {
+                                                BluetoothInputDevice.STATE_CONNECTING,
+                                                BluetoothInputDevice.STATE_CONNECTED});
+
+        for (BluetoothDevice device : devices) {
+            if (!device.getAddress().equals(address)) return true;
+        }
+        return false;
+    }
+
     private boolean onAgentOutOfBandDataAvailable(String objectPath) {
         if (!mBluetoothService.isEnabled()) return false;
 
@@ -568,14 +654,16 @@
             return true;
         }
         return false;
-
     }
 
-    private boolean isOtherSinkInNonDisconnectingState(String address) {
-        BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
-        Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks();
+    private boolean isOtherSinkInNonDisconnectedState(String address) {
+        List<BluetoothDevice> devices =
+            mA2dp.getDevicesMatchingConnectionStates(new int[] {BluetoothA2dp.STATE_CONNECTED,
+                                                     BluetoothA2dp.STATE_CONNECTING,
+                                                     BluetoothA2dp.STATE_DISCONNECTING});
+
         if (devices.size() == 0) return false;
-        for(BluetoothDevice dev: devices) {
+        for (BluetoothDevice dev: devices) {
             if (!dev.getAddress().equals(address)) return true;
         }
         return false;
@@ -622,6 +710,60 @@
         }
     }
 
+    private void onInputDeviceConnectionResult(String path, boolean result) {
+        // Success case gets handled by Property Change signal
+        if (!result) {
+            String address = mBluetoothService.getAddressFromObjectPath(path);
+            if (address == null) return;
+
+            boolean connected = false;
+            BluetoothDevice device = mAdapter.getRemoteDevice(address);
+            int state = mBluetoothService.getInputDeviceState(device);
+            if (state == BluetoothInputDevice.STATE_CONNECTING) {
+                connected = false;
+            } else if (state == BluetoothInputDevice.STATE_DISCONNECTING) {
+                connected = true;
+            } else {
+                Log.e(TAG, "Error onInputDeviceConnectionResult. State is:" + state);
+            }
+            mBluetoothService.handleInputDevicePropertyChange(address, connected);
+        }
+    }
+
+    private void onPanDeviceConnectionResult(String path, boolean result) {
+        log ("onPanDeviceConnectionResult " + path + " " + result);
+        // Success case gets handled by Property Change signal
+        if (!result) {
+            String address = mBluetoothService.getAddressFromObjectPath(path);
+            if (address == null) return;
+
+            boolean connected = false;
+            BluetoothDevice device = mAdapter.getRemoteDevice(address);
+            int state = mBluetoothService.getPanDeviceState(device);
+            if (state == BluetoothPan.STATE_CONNECTING) {
+                connected = false;
+            } else if (state == BluetoothPan.STATE_DISCONNECTING) {
+                connected = true;
+            } else {
+                Log.e(TAG, "Error onPanDeviceConnectionResult. State is: "
+                        + state + " result: "+ result);
+            }
+            int newState = connected? BluetoothPan.STATE_CONNECTED :
+                BluetoothPan.STATE_DISCONNECTED;
+            mBluetoothService.handlePanDeviceStateChange(device, newState);
+        }
+    }
+
+    private void onNetworkDeviceDisconnected(String address) {
+        BluetoothDevice device = mAdapter.getRemoteDevice(address);
+        mBluetoothService.handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED);
+    }
+
+    private void onNetworkDeviceConnected(String address, String iface, int destUuid) {
+        BluetoothDevice device = mAdapter.getRemoteDevice(address);
+        mBluetoothService.handlePanDeviceStateChange(device, iface, BluetoothPan.STATE_CONNECTED);
+    }
+
     private void onRestartRequired() {
         if (mBluetoothService.isEnabled()) {
             Log.e(TAG, "*** A serious error occurred (did bluetoothd crash?) - " +
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 4d4d309..ec8f031 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -27,8 +27,11 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothDeviceProfileState;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProfileState;
 import android.bluetooth.BluetoothSocket;
 import android.bluetooth.BluetoothUuid;
@@ -40,9 +43,13 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.res.Resources.NotFoundException;
+import android.net.ConnectivityManager;
+import android.net.InterfaceConfiguration;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.INetworkManagementService;
 import android.os.Message;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
@@ -71,8 +78,11 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 public class BluetoothService extends IBluetooth.Stub {
     private static final String TAG = "BluetoothService";
@@ -80,11 +90,13 @@
 
     private int mNativeData;
     private BluetoothEventLoop mEventLoop;
+    private BluetoothHeadset mBluetoothHeadset;
     private boolean mIsAirplaneSensitive;
     private boolean mIsAirplaneToggleable;
     private int mBluetoothState;
     private boolean mRestart = false;  // need to call enable() after disable()
     private boolean mIsDiscovering;
+    private boolean mTetheringOn;
 
     private BluetoothAdapter mAdapter;  // constant after init()
     private final BondState mBondState = new BondState();  // local cache of bondings
@@ -113,6 +125,13 @@
     private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
     private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
 
+    private ArrayList<String> mBluetoothIfaceAddresses;
+    private int mMaxPanDevices;
+
+    private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1";
+    private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
+    private static final String BLUETOOTH_NETMASK        = "255.255.255.0";
+
     // The timeout used to sent the UUIDs Intent
     // This timeout should be greater than the page timeout
     private static final int UUID_INTENT_DELAY = 6000;
@@ -136,13 +155,20 @@
     private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
     private final BluetoothProfileState mA2dpProfileState;
     private final BluetoothProfileState mHfpProfileState;
+    private final BluetoothProfileState mHidProfileState;
 
     private BluetoothA2dpService mA2dpService;
+    private final HashMap<BluetoothDevice, Integer> mInputDevices;
+    private final HashMap<BluetoothDevice, Pair<Integer, String>> mPanDevices;
     private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
 
+    private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0;
+
     private static String mDockAddress;
     private String mDockPin;
 
+    private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
+
     private static class RemoteService {
         public String address;
         public ParcelUuid uuid;
@@ -190,6 +216,7 @@
 
         mBluetoothState = BluetoothAdapter.STATE_OFF;
         mIsDiscovering = false;
+        mTetheringOn = false;
         mAdapterProperties = new HashMap<String, String>();
         mDeviceProperties = new HashMap<String, Map<String,String>>();
 
@@ -201,15 +228,27 @@
         mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
         mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
         mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
+        mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID);
+
+        mBluetoothIfaceAddresses = new ArrayList<String>();
+        try {
+            mMaxPanDevices = context.getResources().getInteger(
+                            com.android.internal.R.integer.config_max_pan_devices);
+        } catch (NotFoundException e) {
+            mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
+        }
 
         mHfpProfileState.start();
         mA2dpProfileState.start();
+        mHidProfileState.start();
 
         IntentFilter filter = new IntentFilter();
         registerForAirplaneMode(filter);
 
         filter.addAction(Intent.ACTION_DOCK_EVENT);
         mContext.registerReceiver(mReceiver, filter);
+        mInputDevices = new HashMap<BluetoothDevice, Integer>();
+        mPanDevices = new HashMap<BluetoothDevice, Pair<Integer, String>>();
     }
 
     public static synchronized String readDockBluetoothAddress() {
@@ -284,6 +323,10 @@
         mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
     }
 
+    public synchronized void initAfterA2dpRegistration() {
+        mEventLoop.getProfileProxy();
+    }
+
     @Override
     protected void finalize() throws Throwable {
         mContext.unregisterReceiver(mReceiver);
@@ -337,15 +380,28 @@
         }
         setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
         mHandler.removeMessages(MESSAGE_REGISTER_SDP_RECORDS);
+        setBluetoothTetheringNative(false, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
 
         // Allow 3 seconds for profiles to gracefully disconnect
         // TODO: Introduce a callback mechanism so that each profile can notify
         // BluetoothService when it is done shutting down
+        disconnectDevices();
+
         mHandler.sendMessageDelayed(
                 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
         return true;
     }
 
+    private synchronized void disconnectDevices() {
+        // Disconnect devices handled by BluetoothService.
+        for (BluetoothDevice device: getConnectedInputDevices()) {
+            disconnectInputDevice(device);
+        }
+
+        for (BluetoothDevice device: getConnectedPanDevices()) {
+            disconnectPanDevice(device);
+        }
+    }
 
     private synchronized void finishDisable(boolean saveSetting) {
         if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
@@ -370,6 +426,11 @@
         mAdapterProperties.clear();
         mServiceRecordToPid.clear();
 
+        mProfilesConnected = 0;
+        mProfilesConnecting = 0;
+        mProfilesDisconnecting = 0;
+        mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
+
         if (saveSetting) {
             persistBluetoothOnSetting(false);
         }
@@ -567,8 +628,12 @@
                 mBondState.readAutoPairingData();
                 mBondState.loadBondState();
                 initProfileState();
+
+                //Register SDP records.
                 mHandler.sendMessageDelayed(
                         mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 1, -1), 3000);
+                setBluetoothTetheringNative(true, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
+
 
                 // Log bluetooth on to battery stats.
                 long ident = Binder.clearCallingIdentity();
@@ -747,6 +812,10 @@
                 removeProfileState(address);
             }
 
+            // HID is handled by BluetoothService, other profiles
+            // will be handled by their respective services.
+            setInitialInputDevicePriority(mAdapter.getRemoteDevice(address), state);
+
             if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
                          reason + ")");
             Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
@@ -1362,6 +1431,409 @@
         return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
     }
 
+    public synchronized boolean isTetheringOn() {
+        return mTetheringOn;
+    }
+
+    /*package*/ synchronized boolean allowIncomingTethering() {
+        if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices)
+            return true;
+        return false;
+    }
+
+    private BroadcastReceiver mTetheringReceiver = null;
+
+    public synchronized void setBluetoothTethering(boolean value) {
+        if (!value) {
+            disconnectPan();
+        }
+
+        if (getBluetoothState() != BluetoothAdapter.STATE_ON && value) {
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+            mTetheringReceiver = new BroadcastReceiver() {
+                @Override
+                public synchronized void onReceive(Context context, Intent intent) {
+                    if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
+                            == BluetoothAdapter.STATE_ON) {
+                        mTetheringOn = true;
+                        mContext.unregisterReceiver(mTetheringReceiver);
+                    }
+                }
+            };
+            mContext.registerReceiver(mTetheringReceiver, filter);
+        } else {
+            mTetheringOn = value;
+        }
+    }
+
+    public synchronized int getPanDeviceState(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+
+        Pair<Integer, String> panDevice = mPanDevices.get(device);
+        if (panDevice == null) {
+            return BluetoothPan.STATE_DISCONNECTED;
+        }
+        return panDevice.first;
+    }
+
+    public synchronized boolean connectPanDevice(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+
+        String objectPath = getObjectPathFromAddress(device.getAddress());
+        if (DBG) log("connect PAN(" + objectPath + ")");
+        if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) {
+            log (device + " already connected to PAN");
+        }
+
+        int connectedCount = 0;
+        for (BluetoothDevice panDevice: mPanDevices.keySet()) {
+            if (getPanDeviceState(panDevice) == BluetoothPan.STATE_CONNECTED) {
+                connectedCount ++;
+            }
+        }
+        if (connectedCount > 8) {
+            log (device + " could not connect to PAN because 8 other devices are already connected");
+            return false;
+        }
+
+        handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING);
+        if (connectPanDeviceNative(objectPath, "nap", "panu")) {
+            log ("connecting to PAN");
+            return true;
+        } else {
+            handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED);
+            log ("could not connect to PAN");
+            return false;
+        }
+    }
+
+    private synchronized boolean disconnectPan() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        if (DBG) log("disconnect all PAN devices");
+
+        for (BluetoothDevice device: mPanDevices.keySet()) {
+            if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
+                if (!disconnectPanDevice(device)) {
+                    log ("could not disconnect Pan Device "+device.getAddress());
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public synchronized List<BluetoothDevice> getConnectedPanDevices() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+
+        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
+
+        for (BluetoothDevice device: mPanDevices.keySet()) {
+            if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
+                devices.add(device);
+            }
+        }
+        return devices;
+    }
+
+    public synchronized boolean disconnectPanDevice(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+        String objectPath = getObjectPathFromAddress(device.getAddress());
+        if (DBG) log("disconnect PAN(" + objectPath + ")");
+        if (getPanDeviceState(device) != BluetoothPan.STATE_CONNECTED) {
+            log (device + " already disconnected from PAN");
+        }
+        handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING);
+        return disconnectPanDeviceNative(objectPath);
+    }
+
+    /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+                                                             String iface,
+                                                             int state) {
+        int prevState;
+        String ifaceAddr = null;
+
+        if (mPanDevices.get(device) == null) {
+            prevState = BluetoothPan.STATE_DISCONNECTED;
+        } else {
+            prevState = mPanDevices.get(device).first;
+            ifaceAddr = mPanDevices.get(device).second;
+        }
+        if (prevState == state) return;
+
+        if (state == BluetoothPan.STATE_CONNECTED) {
+            ifaceAddr = enableTethering(iface);
+            if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
+        } else if (state == BluetoothPan.STATE_DISCONNECTED) {
+            if (ifaceAddr != null) {
+                mBluetoothIfaceAddresses.remove(ifaceAddr);
+                ifaceAddr = null;
+            }
+        }
+
+        Pair<Integer, String> value = new Pair<Integer, String>(state, ifaceAddr);
+        mPanDevices.put(device, value);
+
+        Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState);
+        intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state);
+        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+
+        if (DBG) log("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
+        sendConnectionStateChange(device, state, prevState);
+    }
+
+    /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
+                                                             int state) {
+        handlePanDeviceStateChange(device, null, state);
+    }
+
+    private String createNewTetheringAddressLocked() {
+        if (getConnectedPanDevices().size() == mMaxPanDevices) {
+            log("Max PAN device connections reached");
+            return null;
+        }
+        String address = BLUETOOTH_IFACE_ADDR_START;
+        while (true) {
+            if (mBluetoothIfaceAddresses.contains(address)) {
+                String[] addr = address.split("\\.");
+                Integer newIp = Integer.parseInt(addr[2]) + 1;
+                address = address.replace(addr[2], newIp.toString());
+            } else {
+                break;
+            }
+        }
+        mBluetoothIfaceAddresses.add(address);
+        return address;
+    }
+
+    // configured when we start tethering
+    private synchronized String enableTethering(String iface) {
+        log("updateTetherState:" + iface);
+
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+        ConnectivityManager cm =
+            (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
+
+        // bring toggle the interfaces
+        String[] currentIfaces = new String[0];
+        try {
+            currentIfaces = service.listInterfaces();
+        } catch (Exception e) {
+            Log.e(TAG, "Error listing Interfaces :" + e);
+            return null;
+        }
+
+        boolean found = false;
+        for (String currIface: currentIfaces) {
+            if (currIface.equals(iface)) {
+                found = true;
+                break;
+            }
+        }
+
+        if (!found) return null;
+
+        String address = createNewTetheringAddressLocked();
+        if (address == null) return null;
+
+        InterfaceConfiguration ifcg = null;
+        try {
+            ifcg = service.getInterfaceConfig(iface);
+            if (ifcg != null) {
+                String[] addr = BLUETOOTH_NETMASK.split("\\.");
+                ifcg.netmask = (Integer.parseInt(addr[0]) << 24) +
+                        (Integer.parseInt(addr[1]) << 16) +
+                        (Integer.parseInt(addr[2]) << 8) +
+                        (Integer.parseInt(addr[3]));
+                if (ifcg.ipAddr == 0) {
+                    addr = address.split("\\.");
+
+                    ifcg.ipAddr = (Integer.parseInt(addr[0]) << 24) +
+                            (Integer.parseInt(addr[1]) << 16) +
+                            (Integer.parseInt(addr[2]) << 8) +
+                            (Integer.parseInt(addr[3]));
+                    ifcg.interfaceFlags =
+                        ifcg.interfaceFlags.replace("down", "up");
+                }
+                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
+                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("  "," ");
+                service.setInterfaceConfig(iface, ifcg);
+                if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                    Log.e(TAG, "Error tethering "+iface);
+                }
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
+            return null;
+        }
+        return address;
+    }
+
+    public synchronized boolean connectInputDevice(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+
+        String objectPath = getObjectPathFromAddress(device.getAddress());
+        if (objectPath == null ||
+            getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED ||
+            getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
+            return false;
+        }
+        BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
+        if (state != null) {
+            Message msg = new Message();
+            msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING;
+            msg.obj = state;
+            mHidProfileState.sendMessage(msg);
+            return true;
+        }
+        return false;
+    }
+
+    public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
+        String objectPath = getObjectPathFromAddress(device.getAddress());
+        handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
+        if (!connectInputDeviceNative(objectPath)) {
+            handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED);
+            return false;
+        }
+        return true;
+    }
+
+    public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+
+        String objectPath = getObjectPathFromAddress(device.getAddress());
+        if (objectPath == null || getConnectedInputDevices().size() == 0) {
+            return false;
+        }
+        BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
+        if (state != null) {
+            Message msg = new Message();
+            msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING;
+            msg.obj = state;
+            mHidProfileState.sendMessage(msg);
+            return true;
+        }
+        return false;
+    }
+
+    public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
+        String objectPath = getObjectPathFromAddress(device.getAddress());
+        handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
+        if (!disconnectInputDeviceNative(objectPath)) {
+            handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED);
+            return false;
+        }
+        return true;
+    }
+
+    public synchronized int getInputDeviceState(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+
+        if (mInputDevices.get(device) == null) {
+            return BluetoothInputDevice.STATE_DISCONNECTED;
+        }
+        return mInputDevices.get(device);
+    }
+
+    public synchronized List<BluetoothDevice> getConnectedInputDevices() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
+            new int[] {BluetoothInputDevice.STATE_CONNECTED});
+        return devices;
+    }
+
+    public synchronized int getInputDevicePriority(BluetoothDevice device) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
+                BluetoothInputDevice.PRIORITY_UNDEFINED);
+    }
+
+    public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+        if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
+            return false;
+        }
+        return Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
+                priority);
+    }
+
+    /*package*/synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
+        List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
+
+        for (BluetoothDevice device: mInputDevices.keySet()) {
+            int inputDeviceState = getInputDeviceState(device);
+            for (int state : states) {
+                if (state == inputDeviceState) {
+                    inputDevices.add(device);
+                    break;
+                }
+            }
+        }
+        return inputDevices;
+    }
+
+    private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
+        int prevState;
+        if (mInputDevices.get(device) == null) {
+            prevState = BluetoothInputDevice.STATE_DISCONNECTED;
+        } else {
+            prevState = mInputDevices.get(device);
+        }
+        if (prevState == state) return;
+
+        mInputDevices.put(device, state);
+
+        if (getInputDevicePriority(device) >
+              BluetoothInputDevice.PRIORITY_OFF &&
+            state == BluetoothInputDevice.STATE_CONNECTING ||
+            state == BluetoothInputDevice.STATE_CONNECTED) {
+            // We have connected or attempting to connect.
+            // Bump priority
+            setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT);
+        }
+
+        Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState);
+        intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state);
+        mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+
+        if (DBG) log("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
+        sendConnectionStateChange(device, state, prevState);
+    }
+
+    /*package*/ void handleInputDevicePropertyChange(String address, boolean connected) {
+        int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
+            BluetoothInputDevice.STATE_DISCONNECTED;
+        BluetoothDevice device = mAdapter.getRemoteDevice(address);
+        handleInputDeviceStateChange(device, state);
+    }
+
+    private void setInitialInputDevicePriority(BluetoothDevice device, int state) {
+        switch (state) {
+            case BluetoothDevice.BOND_BONDED:
+                if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) {
+                    setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
+                }
+                break;
+            case BluetoothDevice.BOND_NONE:
+                setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED);
+                break;
+        }
+    }
+
     /*package*/ boolean isRemoteDeviceInCache(String address) {
         return (mDeviceProperties.get(address) != null);
     }
@@ -1384,7 +1856,7 @@
             if (updateRemoteDevicePropertiesCache(address))
                 return getRemoteDeviceProperty(address, property);
         }
-        Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
+        Log.e(TAG, "getRemoteDeviceProperty: " + property + " not present: " + address);
         return null;
     }
 
@@ -1989,7 +2461,8 @@
         pw.println("Local name = " + getName());
         pw.println("isDiscovering() = " + isDiscovering());
 
-        BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
+        mAdapter.getProfileProxy(mContext,
+                                 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
 
         pw.println("\n--Known devices--");
         for (String address : mDeviceProperties.keySet()) {
@@ -2034,24 +2507,48 @@
         // Rather not do this from here, but no-where else and I need this
         // dump
         pw.println("\n--Headset Service--");
-        switch (headset.getState(headset.getCurrentHeadset())) {
-        case BluetoothHeadset.STATE_DISCONNECTED:
-            pw.println("getState() = STATE_DISCONNECTED");
-            break;
-        case BluetoothHeadset.STATE_CONNECTING:
-            pw.println("getState() = STATE_CONNECTING");
-            break;
-        case BluetoothHeadset.STATE_CONNECTED:
-            pw.println("getState() = STATE_CONNECTED");
-            break;
-        case BluetoothHeadset.STATE_ERROR:
-            pw.println("getState() = STATE_ERROR");
-            break;
+        if (mBluetoothHeadset != null) {
+           List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
+           if (deviceList.size() == 0) {
+              pw.println("\n--No headsets connected--");
+           }
+           BluetoothDevice device = deviceList.get(0);
+
+            switch (mBluetoothHeadset.getConnectionState(device)) {
+                case BluetoothHeadset.STATE_DISCONNECTED:
+                    pw.println("getConnectionState() = STATE_DISCONNECTED");
+                    break;
+                case BluetoothHeadset.STATE_CONNECTING:
+                    pw.println("getConnectionState() = STATE_CONNECTING");
+                    break;
+                case BluetoothHeadset.STATE_CONNECTED:
+                    pw.println("getConnectionState() = STATE_CONNECTED");
+                    break;
+                case BluetoothHeadset.STATE_DISCONNECTING:
+                    pw.println("getConnectionState() = STATE_DISCONNECTING");
+                    break;
+                case BluetoothHeadset.STATE_AUDIO_CONNECTED:
+                    pw.println("getConnectionState() = STATE_AUDIO_CONNECTED");
+                    break;
+            }
+
+            deviceList.clear();
+            deviceList = mBluetoothHeadset.getDevicesMatchingConnectionStates(new int[] {
+                     BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
+            pw.println("\n--Connected and Disconnected Headsets");
+            for (BluetoothDevice dev: deviceList) {
+                pw.println(device);
+                if (mBluetoothHeadset.isAudioConnected(device)) {
+                    pw.println("SCO audio connected to device:" + device);
+                }
+            }
+
+            pw.println("\ngetCurrentHeadset() = " + device);
+            pw.println("getBatteryUsageHint() = " +
+                       mBluetoothHeadset.getBatteryUsageHint(device));
+            mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
         }
 
-        pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset());
-        pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
-        headset.close();
         pw.println("\n--Application Service Records--");
         for (Integer handle : mServiceRecordToPid.keySet()) {
             Integer pid = mServiceRecordToPid.get(handle);
@@ -2059,6 +2556,16 @@
         }
     }
 
+    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            mBluetoothHeadset = (BluetoothHeadset) proxy;
+    }
+        public void onServiceDisconnected(int profile) {
+            mBluetoothHeadset = null;
+        }
+    };
+
     /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
         if (pairable && discoverable)
             return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
@@ -2118,6 +2625,8 @@
     }
 
     public boolean connectHeadset(String address) {
+        if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
+
         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
         if (state != null) {
             Message msg = new Message();
@@ -2130,6 +2639,8 @@
     }
 
     public boolean disconnectHeadset(String address) {
+        if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
+
         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
         if (state != null) {
             Message msg = new Message();
@@ -2142,6 +2653,8 @@
     }
 
     public boolean connectSink(String address) {
+        if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
+
         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
         if (state != null) {
             Message msg = new Message();
@@ -2154,6 +2667,8 @@
     }
 
     public boolean disconnectSink(String address) {
+        if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
+
         BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
         if (state != null) {
             Message msg = new Message();
@@ -2237,6 +2752,82 @@
         }
     }
 
+    public int getAdapterConnectionState() {
+        return mAdapterConnectionState;
+    }
+
+    public synchronized void sendConnectionStateChange(BluetoothDevice device, int state,
+                                                        int prevState) {
+        if (updateCountersAndCheckForConnectionStateChange(device, state, prevState)) {
+            state = translateToAdapterConnectionState(state);
+            prevState = translateToAdapterConnectionState(prevState);
+
+            mAdapterConnectionState = state;
+
+            Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
+            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+            intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, state);
+            intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE, prevState);
+            mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+            log("CONNECTION_STATE_CHANGE: " + device + ": " + prevState + "-> " + state);
+        }
+    }
+
+    private int translateToAdapterConnectionState(int state) {
+        switch (state) {
+            case BluetoothProfile.STATE_CONNECTING:
+                return BluetoothAdapter.STATE_CONNECTING;
+            case BluetoothProfile.STATE_CONNECTED:
+              return BluetoothAdapter.STATE_CONNECTED;
+            case BluetoothProfile.STATE_DISCONNECTING:
+              return BluetoothAdapter.STATE_DISCONNECTING;
+            case BluetoothProfile.STATE_DISCONNECTED:
+              return BluetoothAdapter.STATE_DISCONNECTED;
+            default:
+              Log.e(TAG, "Error in getAdapterConnectionState");
+              return -1;
+        }
+    }
+
+    private boolean updateCountersAndCheckForConnectionStateChange(BluetoothDevice device,
+                                                                   int state,
+                                                                   int prevState) {
+        switch (state) {
+            case BluetoothProfile.STATE_CONNECTING:
+                mProfilesConnecting++;
+                if (prevState == BluetoothAdapter.STATE_DISCONNECTING) mProfilesDisconnecting--;
+                if (mProfilesConnected > 0 || mProfilesConnecting > 1) return false;
+
+                break;
+            case BluetoothProfile.STATE_CONNECTED:
+                if (prevState == BluetoothAdapter.STATE_CONNECTING) mProfilesConnecting--;
+                if (prevState == BluetoothAdapter.STATE_DISCONNECTING) mProfilesDisconnecting--;
+
+                mProfilesConnected++;
+
+                if (mProfilesConnected > 1) return false;
+                break;
+            case BluetoothProfile.STATE_DISCONNECTING:
+                mProfilesDisconnecting++;
+                mProfilesConnected--;
+
+                if (mProfilesConnected > 0 || mProfilesDisconnecting > 1) return false;
+
+                break;
+            case BluetoothProfile.STATE_DISCONNECTED:
+                if (prevState == BluetoothAdapter.STATE_CONNECTING) mProfilesConnecting--;
+                if (prevState == BluetoothAdapter.STATE_DISCONNECTING) mProfilesDisconnecting--;
+
+                if (prevState == BluetoothAdapter.STATE_CONNECTED) {
+                    mProfilesConnected--;
+                }
+
+                if (mProfilesConnected  > 0 || mProfilesConnecting > 0) return false;
+                break;
+        }
+        return true;
+    }
+
     private static void log(String msg) {
         Log.d(TAG, msg);
     }
@@ -2287,4 +2878,10 @@
             short channel);
     private native boolean removeServiceRecordNative(int handle);
     private native boolean setLinkTimeoutNative(String path, int num_slots);
+    private native boolean connectInputDeviceNative(String path);
+    private native boolean disconnectInputDeviceNative(String path);
+
+    private native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
+    private native boolean connectPanDeviceNative(String path, String srcRole, String dstRole);
+    private native boolean disconnectPanDeviceNative(String path);
 }
diff --git a/core/java/android/service/urlrenderer/IUrlRendererCallback.aidl b/core/java/android/service/urlrenderer/IUrlRendererCallback.aidl
deleted file mode 100644
index 004aca7..0000000
--- a/core/java/android/service/urlrenderer/IUrlRendererCallback.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (c) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.urlrenderer;
-
-import android.os.ParcelFileDescriptor;
-
-/**
- * {@hide}
- */
-oneway interface IUrlRendererCallback {
-    void complete(String url, in ParcelFileDescriptor result);
-}
diff --git a/core/java/android/service/urlrenderer/IUrlRendererService.aidl b/core/java/android/service/urlrenderer/IUrlRendererService.aidl
deleted file mode 100644
index d561fdc..0000000
--- a/core/java/android/service/urlrenderer/IUrlRendererService.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.urlrenderer;
-
-import android.service.urlrenderer.IUrlRendererCallback;
-
-/**
- * {@hide}
- */
-interface IUrlRendererService {
-    void render(in List<String> urls, int width, int height,
-            IUrlRendererCallback cb);
-}
diff --git a/core/java/android/service/urlrenderer/UrlRenderer.java b/core/java/android/service/urlrenderer/UrlRenderer.java
deleted file mode 100644
index 6057d6c..0000000
--- a/core/java/android/service/urlrenderer/UrlRenderer.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.urlrenderer;
-
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-import java.util.List;
-
-/**
- * TODO(phanna): Document this class.
- * {@hide} while developing
- */
-public final class UrlRenderer {
-    /**
-     * Interface for clients to receive the result of calls to
-     * {@link UrlRenderer#render}.
-     * {@hide} while developing
-     */
-    public interface Callback {
-        /**
-         * Calls to {@link render} will result in multiple invokations of this
-         * method for each url.  A null result means that there was a server
-         * error or a problem rendering the url.
-         * @param url  The url that has been rendered.
-         * @param result  A ParcelFileDescriptor containing the encoded image
-         *                data. The client is responsible for closing the stream
-         *                to free resources.  A null result indicates a failure
-         *                to render.
-         */
-        public void complete(String url, ParcelFileDescriptor result);
-    }
-
-    private IUrlRendererService mService;
-
-    /**
-     * Create a new UrlRenderer to remotely render urls.
-     * @param service  An IBinder service usually obtained through
-     *                 {@link ServiceConnection#onServiceConnected}
-     */
-    public UrlRenderer(IBinder service) {
-        mService = IUrlRendererService.Stub.asInterface(service);
-    }
-
-    private static class InternalCallback extends IUrlRendererCallback.Stub {
-        private final Callback mCallback;
-        InternalCallback(Callback cb) {
-            mCallback = cb;
-        }
-
-        public void complete(String url, ParcelFileDescriptor result) {
-            mCallback.complete(url, result);
-        }
-    }
-
-    /**
-     * Render the list of <var>urls</var> and invoke the <var>callback</var>
-     * for each result.
-     * @param urls  A List of urls to render.
-     * @param width  The desired width of the result.
-     * @param height  The desired height of the result.
-     * @param callback  An instance of {@link Callback} invoked for each url.
-     */
-    public void render(List<String> urls, int width, int height,
-            Callback callback) {
-        if (mService != null) {
-            try {
-                mService.render(urls, width, height,
-                        new InternalCallback(callback));
-            } catch (RemoteException ex) {
-            }
-        }
-    }
-}
diff --git a/core/java/android/service/urlrenderer/UrlRendererService.java b/core/java/android/service/urlrenderer/UrlRendererService.java
deleted file mode 100644
index f7bf7d7..0000000
--- a/core/java/android/service/urlrenderer/UrlRendererService.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.urlrenderer;
-
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.app.Service;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-import java.util.List;
-
-/**
- * TODO(phanna): Complete documentation.
- * {@hide} while developing
- */
-public abstract class UrlRendererService extends Service {
-    /**
-     * The {@link Intent} that must be declared as handled by the service.
-     */
-    @SdkConstant(SdkConstantType.SERVICE_ACTION)
-    public static final String SERVICE_INTERFACE =
-            "android.service.urlrenderer.UrlRendererService";
-
-    static final String TAG = "UrlRendererService";
-
-    private static class InternalCallback implements UrlRenderer.Callback {
-        private final IUrlRendererCallback mCallback;
-        InternalCallback(IUrlRendererCallback cb) {
-            mCallback = cb;
-        }
-
-        public void complete(String url, ParcelFileDescriptor result) {
-            try {
-                mCallback.complete(url, result);
-            } catch (RemoteException ex) {
-            }
-        }
-    }
-
-    private final IUrlRendererService.Stub mBinderInterface =
-            new IUrlRendererService.Stub() {
-                public void render(List<String> urls, int width, int height,
-                        IUrlRendererCallback cb) {
-                    processRequest(urls, width, height,
-                            new InternalCallback(cb));
-                }
-            };
-
-    /**
-     * Implement to return the implementation of the internal accessibility
-     * service interface.  Subclasses should not override.
-     */
-    @Override
-    public final android.os.IBinder onBind(android.content.Intent intent) {
-        return mBinderInterface;
-    }
-
-    /**
-     * When all clients unbind from the service, stop the service.  Subclasses
-     * should not override.
-     */
-    @Override
-    public final boolean onUnbind(android.content.Intent intent) {
-        stopSelf();
-        return false;
-    }
-
-    /**
-     * Subclasses implement this function to process the given urls.  When each
-     * url is complete, the subclass must invoke the callback with the result.
-     * @param urls  A list of urls to render at the given dimensions.
-     * @param width  The desired width of the result.
-     * @param height  The desired height of the result.
-     * @param cb  The callback to invoke when each url is complete.
-     */
-    public abstract void processRequest(List<String> urls, int width,
-            int height, UrlRenderer.Callback cb);
-}
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 8fa0d59..cd73ba8 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -50,7 +50,7 @@
     private static final String TAG = "SpeechRecognizer";
 
     /**
-     * Used to retrieve an {@code ArrayList&lt;String&gt;} from the {@link Bundle} passed to the
+     * Used to retrieve an {@code ArrayList<String>} from the {@link Bundle} passed to the
      * {@link RecognitionListener#onResults(Bundle)} and
      * {@link RecognitionListener#onPartialResults(Bundle)} methods. These strings are the possible
      * recognition results, where the first element is the most likely candidate.
diff --git a/core/java/android/test/InstrumentationTestCase.java b/core/java/android/test/InstrumentationTestCase.java
index cd33d8a..ca427ea 100644
--- a/core/java/android/test/InstrumentationTestCase.java
+++ b/core/java/android/test/InstrumentationTestCase.java
@@ -54,7 +54,7 @@
      * @param instrumentation the instrumentation to use with this instance
      *
      * @deprecated Incorrect spelling,
-     * use {@link #injectInstrumentation(android.app.Instrumentation) instead.
+     * use {@link #injectInstrumentation(android.app.Instrumentation)} instead.
      */
     @Deprecated
     public void injectInsrumentation(Instrumentation instrumentation) {
@@ -170,18 +170,23 @@
         }
 
         int runCount = 1;
+        boolean isRepetitive = false;
         if (method.isAnnotationPresent(FlakyTest.class)) {
             runCount = method.getAnnotation(FlakyTest.class).tolerance();
+        } else if (method.isAnnotationPresent(RepetitiveTest.class)) {
+            runCount = method.getAnnotation(RepetitiveTest.class).numIterations();
+            isRepetitive = true;
         }
 
         if (method.isAnnotationPresent(UiThreadTest.class)) {
             final int tolerance = runCount;
+            final boolean repetitive = isRepetitive;
             final Method testMethod = method;
             final Throwable[] exceptions = new Throwable[1];
             getInstrumentation().runOnMainSync(new Runnable() {
                 public void run() {
                     try {
-                        runMethod(testMethod, tolerance);
+                        runMethod(testMethod, tolerance, repetitive);
                     } catch (Throwable throwable) {
                         exceptions[0] = throwable;
                     }
@@ -191,11 +196,16 @@
                 throw exceptions[0];
             }
         } else {
-            runMethod(method, runCount);
+            runMethod(method, runCount, isRepetitive);
         }
     }
 
+    // For backwards-compatibility after adding isRepetitive
     private void runMethod(Method runMethod, int tolerance) throws Throwable {
+        runMethod(runMethod, tolerance, false);
+    }
+
+    private void runMethod(Method runMethod, int tolerance, boolean isRepetitive) throws Throwable {
         Throwable exception = null;
 
         int runCount = 0;
@@ -211,8 +221,14 @@
                 exception = e;
             } finally {
                 runCount++;
+                // Report current iteration number, if test is repetitive
+                if (isRepetitive) {
+                    Bundle iterations = new Bundle();
+                    iterations.putInt("currentiterations", runCount);
+                    getInstrumentation().sendStatus(2, iterations);
+                }
             }
-        } while ((runCount < tolerance) && (exception != null));
+        } while ((runCount < tolerance) && (isRepetitive || exception != null));
 
         if (exception != null) {
             throw exception;
@@ -344,4 +360,4 @@
         Runtime.getRuntime().gc();
         super.tearDown();
     }
-}
+}
\ No newline at end of file
diff --git a/core/java/android/test/RepetitiveTest.java b/core/java/android/test/RepetitiveTest.java
new file mode 100644
index 0000000..6a7130e
--- /dev/null
+++ b/core/java/android/test/RepetitiveTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation can be used on an {@link android.test.InstrumentationTestCase}'s test methods.
+ * When the annotation is present, the test method is executed the number of times specified by
+ * numIterations and defaults to 1.
+ *
+ * {@hide} Not needed for public API.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RepetitiveTest {
+    /**
+     * Indicates the number of times a test case should be run.
+     *
+     * @return The total number of iterations, the default is 1.
+     */
+    int numIterations() default 1;
+}
\ No newline at end of file
diff --git a/core/java/android/text/AndroidBidi.java b/core/java/android/text/AndroidBidi.java
index e4f934e..eacd40d 100644
--- a/core/java/android/text/AndroidBidi.java
+++ b/core/java/android/text/AndroidBidi.java
@@ -16,6 +16,8 @@
 
 package android.text;
 
+import android.text.Layout.Directions;
+
 /**
  * Access the ICU bidi implementation.
  * @hide
@@ -44,5 +46,132 @@
         return result;
     }
 
+    /**
+     * Returns run direction information for a line within a paragraph.
+     *
+     * @param dir base line direction, either Layout.DIR_LEFT_TO_RIGHT or
+     *     Layout.DIR_RIGHT_TO_LEFT
+     * @param levels levels as returned from {@link #bidi}
+     * @param lstart start of the line in the levels array
+     * @param chars the character array (used to determine whitespace)
+     * @param cstart the start of the line in the chars array
+     * @param len the length of the line
+     * @return the directions
+     */
+    public static Directions directions(int dir, byte[] levels, int lstart,
+            char[] chars, int cstart, int len) {
+
+        int baseLevel = dir == Layout.DIR_LEFT_TO_RIGHT ? 0 : 1;
+        int curLevel = levels[lstart];
+        int minLevel = curLevel;
+        int runCount = 1;
+        for (int i = lstart + 1, e = lstart + len; i < e; ++i) {
+            int level = levels[i];
+            if (level != curLevel) {
+                curLevel = level;
+                ++runCount;
+            }
+        }
+
+        // add final run for trailing counter-directional whitespace
+        int visLen = len;
+        if ((curLevel & 1) != (baseLevel & 1)) {
+            // look for visible end
+            while (--visLen >= 0) {
+                char ch = chars[cstart + visLen];
+
+                if (ch == '\n') {
+                    --visLen;
+                    break;
+                }
+
+                if (ch != ' ' && ch != '\t') {
+                    break;
+                }
+            }
+            ++visLen;
+            if (visLen != len) {
+                ++runCount;
+            }
+        }
+
+        if (runCount == 1 && minLevel == baseLevel) {
+            // we're done, only one run on this line
+            if ((minLevel & 1) != 0) {
+                return Layout.DIRS_ALL_RIGHT_TO_LEFT;
+            }
+            return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+        }
+
+        int[] ld = new int[runCount * 2];
+        int maxLevel = minLevel;
+        int levelBits = minLevel << Layout.RUN_LEVEL_SHIFT;
+        {
+            // Start of first pair is always 0, we write
+            // length then start at each new run, and the
+            // last run length after we're done.
+            int n = 1;
+            int prev = lstart;
+            curLevel = minLevel;
+            for (int i = lstart, e = lstart + visLen; i < e; ++i) {
+                int level = levels[i];
+                if (level != curLevel) {
+                    curLevel = level;
+                    if (level > maxLevel) {
+                        maxLevel = level;
+                    } else if (level < minLevel) {
+                        minLevel = level;
+                    }
+                    // XXX ignore run length limit of 2^RUN_LEVEL_SHIFT
+                    ld[n++] = (i - prev) | levelBits;
+                    ld[n++] = i - lstart;
+                    levelBits = curLevel << Layout.RUN_LEVEL_SHIFT;
+                    prev = i;
+                }
+            }
+            ld[n] = (lstart + visLen - prev) | levelBits;
+            if (visLen < len) {
+                ld[++n] = visLen;
+                ld[++n] = (len - visLen) | (baseLevel << Layout.RUN_LEVEL_SHIFT);
+            }
+        }
+
+        // See if we need to swap any runs.
+        // If the min level run direction doesn't match the base
+        // direction, we always need to swap (at this point
+        // we have more than one run).
+        // Otherwise, we don't need to swap the lowest level.
+        // Since there are no logically adjacent runs at the same
+        // level, if the max level is the same as the (new) min
+        // level, we have a series of alternating levels that
+        // is already in order, so there's no more to do.
+        //
+        boolean swap;
+        if ((minLevel & 1) == baseLevel) {
+            minLevel += 1;
+            swap = maxLevel > minLevel;
+        } else {
+            swap = runCount > 1;
+        }
+        if (swap) {
+            for (int level = maxLevel - 1; level >= minLevel; --level) {
+                for (int i = 0; i < ld.length; i += 2) {
+                    if (levels[ld[i]] >= level) {
+                        int e = i + 2;
+                        while (e < ld.length && levels[ld[e]] >= level) {
+                            e += 2;
+                        }
+                        for (int low = i, hi = e - 2; low < hi; low += 2, hi -= 2) {
+                            int x = ld[low]; ld[low] = ld[hi]; ld[hi] = x;
+                            x = ld[low+1]; ld[low+1] = ld[hi+1]; ld[hi+1] = x;
+                        }
+                        i = e + 2;
+                    }
+                }
+            }
+        }
+        return new Directions(ld);
+    }
+
     private native static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo);
 }
\ No newline at end of file
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 944f735..9309b05 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -208,11 +208,11 @@
              * width because the width that was passed in was for the
              * full text, not the ellipsized form.
              */
-            synchronized (sTemp) {
-                mMax = (int) (FloatMath.ceil(Styled.measureText(paint, sTemp,
-                                                source, 0, source.length(),
-                                                null)));
-            }
+            TextLine line = TextLine.obtain();
+            line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT,
+                    Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
+            mMax = (int) FloatMath.ceil(line.metrics(null));
+            TextLine.recycle(line);
         }
 
         if (includepad) {
@@ -276,14 +276,13 @@
             if (fm == null) {
                 fm = new Metrics();
             }
-    
-            int wid;
 
-            synchronized (sTemp) {
-                wid = (int) (FloatMath.ceil(Styled.measureText(paint, sTemp,
-                                                text, 0, text.length(), fm)));
-            }
-            fm.width = wid;
+            TextLine line = TextLine.obtain();
+            line.set(paint, text, 0, text.length(), Layout.DIR_LEFT_TO_RIGHT,
+                    Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
+            fm.width = (int) FloatMath.ceil(line.metrics(fm));
+            TextLine.recycle(line);
+
             return fm;
         } else {
             return null;
@@ -389,7 +388,7 @@
 
     public static class Metrics extends Paint.FontMetricsInt {
         public int width;
-        
+
         @Override public String toString() {
             return super.toString() + " width=" + width;
         }
diff --git a/core/java/android/text/ClipboardManager.java b/core/java/android/text/ClipboardManager.java
index 52039af..0b239cf 100644
--- a/core/java/android/text/ClipboardManager.java
+++ b/core/java/android/text/ClipboardManager.java
@@ -16,73 +16,26 @@
 
 package android.text;
 
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.ServiceManager;
-import android.util.Log;
-
 /**
- * Interface to the clipboard service, for placing and retrieving text in
- * the global clipboard.
- *
- * <p>
- * You do not instantiate this class directly; instead, retrieve it through
- * {@link android.content.Context#getSystemService}.
- *
- * @see android.content.Context#getSystemService
+ * @deprecated Old text-only interace to the clipboard.  See
+ * {@link android.content.ClipboardManager} for the modern API.
  */
-public class ClipboardManager {
-    private static IClipboard sService;
-
-    private Context mContext;
-
-    static private IClipboard getService() {
-        if (sService != null) {
-            return sService;
-        }
-        IBinder b = ServiceManager.getService("clipboard");
-        sService = IClipboard.Stub.asInterface(b);
-        return sService;
-    }
-
-    /** {@hide} */
-    public ClipboardManager(Context context, Handler handler) {
-        mContext = context;
-    }
-
+@Deprecated
+public abstract class ClipboardManager {
     /**
      * Returns the text on the clipboard.  It will eventually be possible
      * to store types other than text too, in which case this will return
      * null if the type cannot be coerced to text.
      */
-    public CharSequence getText() {
-        try {
-            return getService().getClipboardText();
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
+    public abstract CharSequence getText();
 
     /**
      * Sets the contents of the clipboard to the specified text.
      */
-    public void setText(CharSequence text) {
-        try {
-            getService().setClipboardText(text);
-        } catch (RemoteException e) {
-        }
-    }
+    public abstract void setText(CharSequence text);
 
     /**
      * Returns true if the clipboard contains text; false otherwise.
      */
-    public boolean hasText() {
-        try {
-            return getService().hasClipboardText();
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
+    public abstract boolean hasText();
 }
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 14e5655..b6aa03a 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -310,7 +310,6 @@
 
         Directions[] objects = new Directions[1];
 
-
         for (int i = 0; i < n; i++) {
             ints[START] = reflowed.getLineStart(i) |
                           (reflowed.getParagraphDirection(i) << DIR_SHIFT) |
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index c3bd0ae..d426d124 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -34,13 +34,33 @@
                          float x, float y, Paint p);
 
     /**
+     * Just like {@link Canvas#drawTextRun}.
+     * {@hide}
+     */
+    void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd,
+            float x, float y, int flags, Paint p);
+
+   /**
      * Just like {@link Paint#measureText}.
      */
     float measureText(int start, int end, Paint p);
 
-
     /**
      * Just like {@link Paint#getTextWidths}.
      */
     public int getTextWidths(int start, int end, float[] widths, Paint p);
+
+    /**
+     * Just like {@link Paint#getTextRunAdvances}.
+     * @hide
+     */
+    float getTextRunAdvances(int start, int end, int contextStart, int contextEnd,
+            int flags, float[] advances, int advancesIndex, Paint paint);
+
+    /**
+     * Just like {@link Paint#getTextRunCursor}.
+     * @hide
+     */
+    int getTextRunCursor(int contextStart, int contextEnd, int flags, int offset,
+            int cursorOpt, Paint p);
 }
diff --git a/core/java/android/text/IClipboard.aidl b/core/java/android/text/IClipboard.aidl
deleted file mode 100644
index 4deb5c8..0000000
--- a/core/java/android/text/IClipboard.aidl
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * Copyright (c) 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.text;
-
-/**
- * Programming interface to the clipboard, which allows copying and pasting
- * between applications.
- * {@hide}
- */
-interface IClipboard {
-    /**
-     * Returns the text on the clipboard.  It will eventually be possible
-     * to store types other than text too, in which case this will return
-     * null if the type cannot be coerced to text.
-     */
-    CharSequence getClipboardText();
-
-    /**
-     * Sets the contents of the clipboard to the specified text.
-     */
-    void setClipboardText(CharSequence text);
-
-    /**
-     * Returns true if the clipboard contains text; false otherwise.
-     */
-    boolean hasClipboardText();
-}
-
diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java
index 8592221..7b8acd9 100644
--- a/core/java/android/text/InputType.java
+++ b/core/java/android/text/InputType.java
@@ -228,6 +228,28 @@
      */
     public static final int TYPE_TEXT_VARIATION_PHONETIC = 0x000000c0;
     
+    /**
+     * Variation of {@link #TYPE_CLASS_TEXT}: entering e-mail address inside
+     * of a web form.  This was added in
+     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}.  An IME must target
+     * this API version or later to see this input type; if it doesn't, a request
+     * for this type will be seen as {@link #TYPE_TEXT_VARIATION_EMAIL_ADDRESS}
+     * when passed through {@link android.view.inputmethod.EditorInfo#makeCompatible(int)
+     * EditorInfo.makeCompatible(int)}.
+     */
+    public static final int TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS = 0x000000d0;
+
+    /**
+     * Variation of {@link #TYPE_CLASS_TEXT}: entering password inside
+     * of a web form.  This was added in
+     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}.  An IME must target
+     * this API version or later to see this input type; if it doesn't, a request
+     * for this type will be seen as {@link #TYPE_TEXT_VARIATION_PASSWORD}
+     * when passed through {@link android.view.inputmethod.EditorInfo#makeCompatible(int)
+     * EditorInfo.makeCompatible(int)}.
+     */
+    public static final int TYPE_TEXT_VARIATION_WEB_PASSWORD = 0x000000e0;
+
     // ----------------------------------------------------------------------
     // ----------------------------------------------------------------------
     // ----------------------------------------------------------------------
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
old mode 100755
new mode 100644
index 4e197cd..dd25eaa
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -16,29 +16,33 @@
 
 package android.text;
 
-import android.emoji.EmojiFactory;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Path;
 import com.android.internal.util.ArrayUtils;
 
-import junit.framework.Assert;
-import android.text.style.*;
+import android.emoji.EmojiFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
 import android.text.method.TextKeyListener;
+import android.text.style.AlignmentSpan;
+import android.text.style.LeadingMarginSpan;
+import android.text.style.LineBackgroundSpan;
+import android.text.style.ParagraphStyle;
+import android.text.style.ReplacementSpan;
+import android.text.style.TabStopSpan;
+import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
 import android.view.KeyEvent;
 
+import java.util.Arrays;
+
 /**
- * A base class that manages text layout in visual elements on 
- * the screen. 
- * <p>For text that will be edited, use a {@link DynamicLayout}, 
- * which will be updated as the text changes.  
+ * A base class that manages text layout in visual elements on
+ * the screen.
+ * <p>For text that will be edited, use a {@link DynamicLayout},
+ * which will be updated as the text changes.
  * For text that will not change, use a {@link StaticLayout}.
  */
 public abstract class Layout {
-    private static final boolean DEBUG = false;
     private static final ParagraphStyle[] NO_PARA_SPANS =
         ArrayUtils.emptyArray(ParagraphStyle.class);
 
@@ -54,9 +58,7 @@
             MIN_EMOJI = -1;
             MAX_EMOJI = -1;
         }
-    };
-
-    private RectF mEmojiRect;
+    }
 
     /**
      * Return how wide a layout must be in order to display the
@@ -66,7 +68,7 @@
                                         TextPaint paint) {
         return getDesiredWidth(source, 0, source.length(), paint);
     }
-    
+
     /**
      * Return how wide a layout must be in order to display the
      * specified text slice with one line per paragraph.
@@ -85,8 +87,7 @@
                 next = end;
 
             // note, omits trailing paragraph char
-            float w = measureText(paint, workPaint,
-                                  source, i, next, null, true, null);
+            float w = measurePara(paint, workPaint, source, i, next);
 
             if (w > need)
                 need = w;
@@ -116,6 +117,15 @@
         if (width < 0)
             throw new IllegalArgumentException("Layout: " + width + " < 0");
 
+        // Ensure paint doesn't have baselineShift set.
+        // While normally we don't modify the paint the user passed in,
+        // we were already doing this in Styled.drawUniformRun with both
+        // baselineShift and bgColor.  We probably should reevaluate bgColor.
+        if (paint != null) {
+            paint.bgColor = 0;
+            paint.baselineShift = 0;
+        }
+
         mText = text;
         mPaint = paint;
         mWorkPaint = new TextPaint();
@@ -175,7 +185,6 @@
             dbottom = sTempRect.bottom;
         }
 
-
         int top = 0;
         int bottom = getLineTop(getLineCount());
 
@@ -185,26 +194,28 @@
         if (dbottom < bottom) {
             bottom = dbottom;
         }
-        
-        int first = getLineForVertical(top); 
+
+        int first = getLineForVertical(top);
         int last = getLineForVertical(bottom);
-        
+
         int previousLineBottom = getLineTop(first);
         int previousLineEnd = getLineStart(first);
-        
+
         TextPaint paint = mPaint;
         CharSequence buf = mText;
         int width = mWidth;
         boolean spannedText = mSpannedText;
 
         ParagraphStyle[] spans = NO_PARA_SPANS;
-        int spanend = 0;
+        int spanEnd = 0;
         int textLength = 0;
 
         // First, draw LineBackgroundSpans.
-        // LineBackgroundSpans know nothing about the alignment or direction of
-        // the layout or line.  XXX: Should they?
+        // LineBackgroundSpans know nothing about the alignment, margins, or
+        // direction of the layout or line.  XXX: Should they?
+        // They are evaluated at each line.
         if (spannedText) {
+            Spanned sp = (Spanned) buf;
             textLength = buf.length();
             for (int i = first; i <= last; i++) {
                 int start = previousLineEnd;
@@ -216,12 +227,14 @@
                 previousLineBottom = lbottom;
                 int lbaseline = lbottom - getLineDescent(i);
 
-                if (start >= spanend) {
-                   Spanned sp = (Spanned) buf;
-                   spanend = sp.nextSpanTransition(start, textLength,
-                                                   LineBackgroundSpan.class);
-                   spans = sp.getSpans(start, spanend,
-                                       LineBackgroundSpan.class);
+                if (start >= spanEnd) {
+                    // These should be infrequent, so we'll use this so that
+                    // we don't have to check as often.
+                    spanEnd = sp.nextSpanTransition(start, textLength,
+                            LineBackgroundSpan.class);
+                    // All LineBackgroundSpans on a line contribute to its
+                    // background.
+                   spans = getParagraphSpans(sp, start, end, LineBackgroundSpan.class);
                 }
 
                 for (int n = 0; n < spans.length; n++) {
@@ -234,11 +247,11 @@
                 }
             }
             // reset to their original values
-            spanend = 0;
+            spanEnd = 0;
             previousLineBottom = getLineTop(first);
             previousLineEnd = getLineStart(first);
             spans = NO_PARA_SPANS;
-        } 
+        }
 
         // There can be a highlight even without spans if we are drawing
         // a non-spanned transformation of a spanned editing buffer.
@@ -255,7 +268,11 @@
         }
 
         Alignment align = mAlignment;
-        
+        TabStops tabStops = null;
+        boolean tabStopsIsInitialized = false;
+
+        TextLine tl = TextLine.obtain();
+
         // Next draw the lines, one at a time.
         // the baseline is the top of the following line minus the current
         // line's descent.
@@ -270,19 +287,30 @@
             previousLineBottom = lbottom;
             int lbaseline = lbottom - getLineDescent(i);
 
-            boolean isFirstParaLine = false;
-            if (spannedText) { 
-                if (start == 0 || buf.charAt(start - 1) == '\n') {
-                    isFirstParaLine = true;
-                }
-                // New batch of paragraph styles, compute the alignment.
-                // Last alignment style wins.
-                if (start >= spanend) {
-                    Spanned sp = (Spanned) buf;
-                    spanend = sp.nextSpanTransition(start, textLength,
+            int dir = getParagraphDirection(i);
+            int left = 0;
+            int right = mWidth;
+
+            if (spannedText) {
+                Spanned sp = (Spanned) buf;
+                boolean isFirstParaLine = (start == 0 ||
+                        buf.charAt(start - 1) == '\n');
+
+                // New batch of paragraph styles, collect into spans array.
+                // Compute the alignment, last alignment style wins.
+                // Reset tabStops, we'll rebuild if we encounter a line with
+                // tabs.
+                // We expect paragraph spans to be relatively infrequent, use
+                // spanEnd so that we can check less frequently.  Since
+                // paragraph styles ought to apply to entire paragraphs, we can
+                // just collect the ones present at the start of the paragraph.
+                // If spanEnd is before the end of the paragraph, that's not
+                // our problem.
+                if (start >= spanEnd && (i == first || isFirstParaLine)) {
+                    spanEnd = sp.nextSpanTransition(start, textLength,
                                                     ParagraphStyle.class);
-                    spans = sp.getSpans(start, spanend, ParagraphStyle.class);
-                    
+                    spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
+
                     align = mAlignment;
                     for (int n = spans.length-1; n >= 0; n--) {
                         if (spans[n] instanceof AlignmentSpan) {
@@ -290,45 +318,49 @@
                             break;
                         }
                     }
-                }
-            }
-            
-            int dir = getParagraphDirection(i);
-            int left = 0;
-            int right = mWidth;
 
-            // Draw all leading margin spans.  Adjust left or right according
-            // to the paragraph direction of the line.
-            if (spannedText) {
+                    tabStopsIsInitialized = false;
+                }
+
+                // Draw all leading margin spans.  Adjust left or right according
+                // to the paragraph direction of the line.
                 final int length = spans.length;
                 for (int n = 0; n < length; n++) {
                     if (spans[n] instanceof LeadingMarginSpan) {
                         LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
+                        boolean useFirstLineMargin = isFirstParaLine;
+                        if (margin instanceof LeadingMarginSpan2) {
+                            int count = ((LeadingMarginSpan2) margin).getLeadingMarginLineCount();
+                            int startLine = getLineForOffset(sp.getSpanStart(margin));
+                            useFirstLineMargin = i < startLine + count;
+                        }
 
                         if (dir == DIR_RIGHT_TO_LEFT) {
                             margin.drawLeadingMargin(c, paint, right, dir, ltop,
                                                      lbaseline, lbottom, buf,
                                                      start, end, isFirstParaLine, this);
-                                
-                            right -= margin.getLeadingMargin(isFirstParaLine);
+                            right -= margin.getLeadingMargin(useFirstLineMargin);
                         } else {
                             margin.drawLeadingMargin(c, paint, left, dir, ltop,
                                                      lbaseline, lbottom, buf,
                                                      start, end, isFirstParaLine, this);
-
-                            boolean useMargin = isFirstParaLine;
-                            if (margin instanceof LeadingMarginSpan.LeadingMarginSpan2) {
-                                int count = ((LeadingMarginSpan.LeadingMarginSpan2)margin).getLeadingMarginLineCount();
-                                useMargin = count > i;
-                            }
-                            left += margin.getLeadingMargin(useMargin);
+                            left += margin.getLeadingMargin(useFirstLineMargin);
                         }
                     }
                 }
             }
 
-            // Adjust the point at which to start rendering depending on the
-            // alignment of the paragraph.
+            boolean hasTabOrEmoji = getLineContainsTab(i);
+            // Can't tell if we have tabs for sure, currently
+            if (hasTabOrEmoji && !tabStopsIsInitialized) {
+                if (tabStops == null) {
+                    tabStops = new TabStops(TAB_INCREMENT, spans);
+                } else {
+                    tabStops.reset(TAB_INCREMENT, spans);
+                }
+                tabStopsIsInitialized = true;
+            }
+
             int x;
             if (align == Alignment.ALIGN_NORMAL) {
                 if (dir == DIR_LEFT_TO_RIGHT) {
@@ -337,41 +369,80 @@
                     x = right;
                 }
             } else {
-                int max = (int)getLineMax(i, spans, false);
+                int max = (int)getLineExtent(i, tabStops, false);
                 if (align == Alignment.ALIGN_OPPOSITE) {
-                    if (dir == DIR_RIGHT_TO_LEFT) {
-                        x = left + max;
-                    } else {
+                    if (dir == DIR_LEFT_TO_RIGHT) {
                         x = right - max;
-                    }
-                } else {
-                    // Alignment.ALIGN_CENTER
-                    max = max & ~1;
-                    int half = (right - left - max) >> 1;
-                    if (dir == DIR_RIGHT_TO_LEFT) {
-                        x = right - half;
                     } else {
-                        x = left + half;
+                        x = left - max;
                     }
+                } else { // Alignment.ALIGN_CENTER
+                    max = max & ~1;
+                    x = (right + left - max) >> 1;
                 }
             }
 
             Directions directions = getLineDirections(i);
-            boolean hasTab = getLineContainsTab(i);
             if (directions == DIRS_ALL_LEFT_TO_RIGHT &&
-                    !spannedText && !hasTab) {
-                if (DEBUG) {
-                    Assert.assertTrue(dir == DIR_LEFT_TO_RIGHT);
-                    Assert.assertNotNull(c);
-                }
+                    !spannedText && !hasTabOrEmoji) {
                 // XXX: assumes there's nothing additional to be done
                 c.drawText(buf, start, end, x, lbaseline, paint);
             } else {
-                drawText(c, buf, start, end, dir, directions,
-                    x, ltop, lbaseline, lbottom, paint, mWorkPaint,
-                    hasTab, spans);
+                tl.set(paint, buf, start, end, dir, directions, hasTabOrEmoji, tabStops);
+                tl.draw(c, x, ltop, lbaseline, lbottom);
             }
         }
+
+        TextLine.recycle(tl);
+    }
+
+    /**
+     * Return the start position of the line, given the left and right bounds
+     * of the margins.
+     *
+     * @param line the line index
+     * @param left the left bounds (0, or leading margin if ltr para)
+     * @param right the right bounds (width, minus leading margin if rtl para)
+     * @return the start position of the line (to right of line if rtl para)
+     */
+    private int getLineStartPos(int line, int left, int right) {
+        // Adjust the point at which to start rendering depending on the
+        // alignment of the paragraph.
+        Alignment align = getParagraphAlignment(line);
+        int dir = getParagraphDirection(line);
+
+        int x;
+        if (align == Alignment.ALIGN_NORMAL) {
+            if (dir == DIR_LEFT_TO_RIGHT) {
+                x = left;
+            } else {
+                x = right;
+            }
+        } else {
+            TabStops tabStops = null;
+            if (mSpannedText && getLineContainsTab(line)) {
+                Spanned spanned = (Spanned) mText;
+                int start = getLineStart(line);
+                int spanEnd = spanned.nextSpanTransition(start, spanned.length(),
+                        TabStopSpan.class);
+                TabStopSpan[] tabSpans = getParagraphSpans(spanned, start, spanEnd, TabStopSpan.class);
+                if (tabSpans.length > 0) {
+                    tabStops = new TabStops(TAB_INCREMENT, tabSpans);
+                }
+            }
+            int max = (int)getLineExtent(line, tabStops, false);
+            if (align == Alignment.ALIGN_OPPOSITE) {
+                if (dir == DIR_LEFT_TO_RIGHT) {
+                    x = right - max;
+                } else {
+                    x = left - max;
+                }
+            } else { // Alignment.ALIGN_CENTER
+                max = max & ~1;
+                x = (left + right - max) >> 1;
+            }
+        }
+        return x;
     }
 
     /**
@@ -417,7 +488,7 @@
 
         mWidth = wid;
     }
-    
+
     /**
      * Return the total height of this layout.
      */
@@ -450,7 +521,7 @@
      * Return the number of lines of text in this layout.
      */
     public abstract int getLineCount();
-    
+
     /**
      * Return the baseline for the specified line (0&hellip;getLineCount() - 1)
      * If bounds is not null, return the top, left, right, bottom extents
@@ -524,13 +595,95 @@
      */
     public abstract int getBottomPadding();
 
+
+    /**
+     * Returns true if the character at offset and the preceding character
+     * are at different run levels (and thus there's a split caret).
+     * @param offset the offset
+     * @return true if at a level boundary
+     */
+    private boolean isLevelBoundary(int offset) {
+        int line = getLineForOffset(offset);
+        Directions dirs = getLineDirections(line);
+        if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) {
+            return false;
+        }
+
+        int[] runs = dirs.mDirections;
+        int lineStart = getLineStart(line);
+        int lineEnd = getLineEnd(line);
+        if (offset == lineStart || offset == lineEnd) {
+            int paraLevel = getParagraphDirection(line) == 1 ? 0 : 1;
+            int runIndex = offset == lineStart ? 0 : runs.length - 2;
+            return ((runs[runIndex + 1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK) != paraLevel;
+        }
+
+        offset -= lineStart;
+        for (int i = 0; i < runs.length; i += 2) {
+            if (offset == runs[i]) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean primaryIsTrailingPrevious(int offset) {
+        int line = getLineForOffset(offset);
+        int lineStart = getLineStart(line);
+        int lineEnd = getLineEnd(line);
+        int[] runs = getLineDirections(line).mDirections;
+
+        int levelAt = -1;
+        for (int i = 0; i < runs.length; i += 2) {
+            int start = lineStart + runs[i];
+            int limit = start + (runs[i+1] & RUN_LENGTH_MASK);
+            if (limit > lineEnd) {
+                limit = lineEnd;
+            }
+            if (offset >= start && offset < limit) {
+                if (offset > start) {
+                    // Previous character is at same level, so don't use trailing.
+                    return false;
+                }
+                levelAt = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK;
+                break;
+            }
+        }
+        if (levelAt == -1) {
+            // Offset was limit of line.
+            levelAt = getParagraphDirection(line) == 1 ? 0 : 1;
+        }
+
+        // At level boundary, check previous level.
+        int levelBefore = -1;
+        if (offset == lineStart) {
+            levelBefore = getParagraphDirection(line) == 1 ? 0 : 1;
+        } else {
+            offset -= 1;
+            for (int i = 0; i < runs.length; i += 2) {
+                int start = lineStart + runs[i];
+                int limit = start + (runs[i+1] & RUN_LENGTH_MASK);
+                if (limit > lineEnd) {
+                    limit = lineEnd;
+                }
+                if (offset >= start && offset < limit) {
+                    levelBefore = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK;
+                    break;
+                }
+            }
+        }
+
+        return levelBefore < levelAt;
+    }
+
     /**
      * Get the primary horizontal position for the specified text offset.
      * This is the location where a new character would be inserted in
      * the paragraph's primary direction.
      */
     public float getPrimaryHorizontal(int offset) {
-        return getHorizontal(offset, false, true);
+        boolean trailing = primaryIsTrailingPrevious(offset);
+        return getHorizontal(offset, trailing);
     }
 
     /**
@@ -539,66 +692,42 @@
      * the direction other than the paragraph's primary direction.
      */
     public float getSecondaryHorizontal(int offset) {
-        return getHorizontal(offset, true, true);
+        boolean trailing = primaryIsTrailingPrevious(offset);
+        return getHorizontal(offset, !trailing);
     }
 
-    private float getHorizontal(int offset, boolean trailing, boolean alt) {
+    private float getHorizontal(int offset, boolean trailing) {
         int line = getLineForOffset(offset);
 
-        return getHorizontal(offset, trailing, alt, line);
+        return getHorizontal(offset, trailing, line);
     }
 
-    private float getHorizontal(int offset, boolean trailing, boolean alt,
-                                int line) {
+    private float getHorizontal(int offset, boolean trailing, int line) {
         int start = getLineStart(line);
-        int end = getLineVisibleEnd(line);
+        int end = getLineEnd(line);
         int dir = getParagraphDirection(line);
-        boolean tab = getLineContainsTab(line);
+        boolean hasTabOrEmoji = getLineContainsTab(line);
         Directions directions = getLineDirections(line);
 
-        TabStopSpan[] tabs = null;
-        if (tab && mText instanceof Spanned) {
-            tabs = ((Spanned) mText).getSpans(start, end, TabStopSpan.class);
+        TabStops tabStops = null;
+        if (hasTabOrEmoji && mText instanceof Spanned) {
+            // Just checking this line should be good enough, tabs should be
+            // consistent across all lines in a paragraph.
+            TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class);
+            if (tabs.length > 0) {
+                tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse
+            }
         }
 
-        float wid = measureText(mPaint, mWorkPaint, mText, start, offset, end,
-                                dir, directions, trailing, alt, tab, tabs);
+        TextLine tl = TextLine.obtain();
+        tl.set(mPaint, mText, start, end, dir, directions, hasTabOrEmoji, tabStops);
+        float wid = tl.measure(offset - start, trailing, null);
+        TextLine.recycle(tl);
 
-        if (offset > end) {
-            if (dir == DIR_RIGHT_TO_LEFT)
-                wid -= measureText(mPaint, mWorkPaint,
-                                   mText, end, offset, null, tab, tabs);
-            else
-                wid += measureText(mPaint, mWorkPaint,
-                                   mText, end, offset, null, tab, tabs);
-        }
-
-        Alignment align = getParagraphAlignment(line);
         int left = getParagraphLeft(line);
         int right = getParagraphRight(line);
 
-        if (align == Alignment.ALIGN_NORMAL) {
-            if (dir == DIR_RIGHT_TO_LEFT)
-                return right + wid;
-            else
-                return left + wid;
-        }
-
-        float max = getLineMax(line);
-
-        if (align == Alignment.ALIGN_OPPOSITE) {
-            if (dir == DIR_RIGHT_TO_LEFT)
-                return left + max + wid;
-            else
-                return right - max + wid;
-        } else { /* align == Alignment.ALIGN_CENTER */
-            int imax = ((int) max) & ~1;
-
-            if (dir == DIR_RIGHT_TO_LEFT)
-                return right - (((right - left) - imax) / 2) + wid;
-            else
-                return left + ((right - left) - imax) / 2 + wid;
-        }
+        return getLineStartPos(line, left, right) + wid;
     }
 
     /**
@@ -656,38 +785,76 @@
     }
 
     /**
-     * Gets the horizontal extent of the specified line, excluding
-     * trailing whitespace.
+     * Gets the unsigned horizontal extent of the specified line, including
+     * leading margin indent, but excluding trailing whitespace.
      */
     public float getLineMax(int line) {
-        return getLineMax(line, null, false);
+        float margin = getParagraphLeadingMargin(line);
+        float signedExtent = getLineExtent(line, false);
+        return margin + signedExtent >= 0 ? signedExtent : -signedExtent;
     }
 
     /**
-     * Gets the horizontal extent of the specified line, including
-     * trailing whitespace.
+     * Gets the unsigned horizontal extent of the specified line, including
+     * leading margin indent and trailing whitespace.
      */
     public float getLineWidth(int line) {
-        return getLineMax(line, null, true);
+        float margin = getParagraphLeadingMargin(line);
+        float signedExtent = getLineExtent(line, true);
+        return margin + signedExtent >= 0 ? signedExtent : -signedExtent;
     }
 
-    private float getLineMax(int line, Object[] tabs, boolean full) {
+    /**
+     * Like {@link #getLineExtent(int,TabStops,boolean)} but determines the
+     * tab stops instead of using the ones passed in.
+     * @param line the index of the line
+     * @param full whether to include trailing whitespace
+     * @return the extent of the line
+     */
+    private float getLineExtent(int line, boolean full) {
         int start = getLineStart(line);
-        int end;
+        int end = full ? getLineEnd(line) : getLineVisibleEnd(line);
 
-        if (full) {
-            end = getLineEnd(line);
-        } else {
-            end = getLineVisibleEnd(line);
-        } 
-        boolean tab = getLineContainsTab(line);
-
-        if (tabs == null && tab && mText instanceof Spanned) {
-            tabs = ((Spanned) mText).getSpans(start, end, TabStopSpan.class);
+        boolean hasTabsOrEmoji = getLineContainsTab(line);
+        TabStops tabStops = null;
+        if (hasTabsOrEmoji && mText instanceof Spanned) {
+            // Just checking this line should be good enough, tabs should be
+            // consistent across all lines in a paragraph.
+            TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class);
+            if (tabs.length > 0) {
+                tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse
+            }
         }
+        Directions directions = getLineDirections(line);
+        int dir = getParagraphDirection(line);
 
-        return measureText(mPaint, mWorkPaint,
-                           mText, start, end, null, tab, tabs);
+        TextLine tl = TextLine.obtain();
+        tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops);
+        float width = tl.metrics(null);
+        TextLine.recycle(tl);
+        return width;
+    }
+
+    /**
+     * Returns the signed horizontal extent of the specified line, excluding
+     * leading margin.  If full is false, excludes trailing whitespace.
+     * @param line the index of the line
+     * @param tabStops the tab stops, can be null if we know they're not used.
+     * @param full whether to include trailing whitespace
+     * @return the extent of the text on this line
+     */
+    private float getLineExtent(int line, TabStops tabStops, boolean full) {
+        int start = getLineStart(line);
+        int end = full ? getLineEnd(line) : getLineVisibleEnd(line);
+        boolean hasTabsOrEmoji = getLineContainsTab(line);
+        Directions directions = getLineDirections(line);
+        int dir = getParagraphDirection(line);
+
+        TextLine tl = TextLine.obtain();
+        tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops);
+        float width = tl.metrics(null);
+        TextLine.recycle(tl);
+        return width;
     }
 
     /**
@@ -738,7 +905,7 @@
     }
 
     /**
-     * Get the character offset on the specfied line whose position is
+     * Get the character offset on the specified line whose position is
      * closest to the specified horizontal position.
      */
     public int getOffsetForHorizontal(int line, float horiz) {
@@ -749,20 +916,16 @@
         if (line == getLineCount() - 1)
             max++;
 
-        if (line != getLineCount() - 1)
-            max = TextUtils.getOffsetBefore(mText, getLineEnd(line));
-
         int best = min;
         float bestdist = Math.abs(getPrimaryHorizontal(best) - horiz);
 
-        int here = min;
-        for (int i = 0; i < dirs.mDirections.length; i++) {
-            int there = here + dirs.mDirections[i];
-            int swap = ((i & 1) == 0) ? 1 : -1;
+        for (int i = 0; i < dirs.mDirections.length; i += 2) {
+            int here = min + dirs.mDirections[i];
+            int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK);
+            int swap = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0 ? -1 : 1;
 
             if (there > max)
                 there = max;
-
             int high = there - 1 + 1, low = here + 1 - 1, guess;
 
             while (high - low > 1) {
@@ -795,7 +958,7 @@
 
                 if (dist < bestdist) {
                     bestdist = dist;
-                    best = low;   
+                    best = low;
                 }
             }
 
@@ -805,8 +968,6 @@
                 bestdist = dist;
                 best = here;
             }
-
-            here = there;
         }
 
         float dist = Math.abs(getPrimaryHorizontal(max) - horiz);
@@ -826,19 +987,15 @@
         return getLineStart(line + 1);
     }
 
-    /** 
+    /**
      * Return the text offset after the last visible character (so whitespace
      * is not counted) on the specified line.
      */
     public int getLineVisibleEnd(int line) {
         return getLineVisibleEnd(line, getLineStart(line), getLineStart(line+1));
     }
-    
-    private int getLineVisibleEnd(int line, int start, int end) {
-        if (DEBUG) {
-            Assert.assertTrue(getLineStart(line) == start && getLineStart(line+1) == end);
-        }
 
+    private int getLineVisibleEnd(int line, int start, int end) {
         CharSequence text = mText;
         char ch;
         if (line == getLineCount() - 1) {
@@ -885,207 +1042,62 @@
         return getLineTop(line) - (getLineTop(line+1) - getLineDescent(line));
     }
 
-    /**
-     * Return the text offset that would be reached by moving left
-     * (possibly onto another line) from the specified offset.
-     */
     public int getOffsetToLeftOf(int offset) {
-        int line = getLineForOffset(offset);
-        int start = getLineStart(line);
-        int end = getLineEnd(line);
-        Directions dirs = getLineDirections(line);
-
-        if (line != getLineCount() - 1)
-            end = TextUtils.getOffsetBefore(mText, end);
-
-        float horiz = getPrimaryHorizontal(offset);
-
-        int best = offset;
-        float besth = Integer.MIN_VALUE;
-        int candidate;
-
-        candidate = TextUtils.getOffsetBefore(mText, offset);
-        if (candidate >= start && candidate <= end) {
-            float h = getPrimaryHorizontal(candidate);
-
-            if (h < horiz && h > besth) {
-                best = candidate;
-                besth = h;
-            }
-        }
-
-        candidate = TextUtils.getOffsetAfter(mText, offset);
-        if (candidate >= start && candidate <= end) {
-            float h = getPrimaryHorizontal(candidate);
-
-            if (h < horiz && h > besth) {
-                best = candidate;
-                besth = h;
-            }
-        }
-
-        int here = start;
-        for (int i = 0; i < dirs.mDirections.length; i++) {
-            int there = here + dirs.mDirections[i];
-            if (there > end)
-                there = end;
-
-            float h = getPrimaryHorizontal(here);
-
-            if (h < horiz && h > besth) {
-                best = here;
-                besth = h;
-            }
-
-            candidate = TextUtils.getOffsetAfter(mText, here);
-            if (candidate >= start && candidate <= end) {
-                h = getPrimaryHorizontal(candidate);
-
-                if (h < horiz && h > besth) {
-                    best = candidate;
-                    besth = h;
-                }
-            }
-
-            candidate = TextUtils.getOffsetBefore(mText, there);
-            if (candidate >= start && candidate <= end) {
-                h = getPrimaryHorizontal(candidate);
-
-                if (h < horiz && h > besth) {
-                    best = candidate;
-                    besth = h;
-                }
-            }
-
-            here = there;
-        }
-
-        float h = getPrimaryHorizontal(end);
-
-        if (h < horiz && h > besth) {
-            best = end;
-            besth = h;
-        }
-
-        if (best != offset)
-            return best;
-
-        int dir = getParagraphDirection(line);
-
-        if (dir > 0) {
-            if (line == 0)
-                return best;
-            else
-                return getOffsetForHorizontal(line - 1, 10000);
-        } else {
-            if (line == getLineCount() - 1)
-                return best;
-            else
-                return getOffsetForHorizontal(line + 1, 10000);
-        }
+        return getOffsetToLeftRightOf(offset, true);
     }
 
-    /**
-     * Return the text offset that would be reached by moving right
-     * (possibly onto another line) from the specified offset.
-     */
     public int getOffsetToRightOf(int offset) {
-        int line = getLineForOffset(offset);
-        int start = getLineStart(line);
-        int end = getLineEnd(line);
-        Directions dirs = getLineDirections(line);
+        return getOffsetToLeftRightOf(offset, false);
+    }
 
-        if (line != getLineCount() - 1)
-            end = TextUtils.getOffsetBefore(mText, end);
+    private int getOffsetToLeftRightOf(int caret, boolean toLeft) {
+        int line = getLineForOffset(caret);
+        int lineStart = getLineStart(line);
+        int lineEnd = getLineEnd(line);
+        int lineDir = getParagraphDirection(line);
 
-        float horiz = getPrimaryHorizontal(offset);
-
-        int best = offset;
-        float besth = Integer.MAX_VALUE;
-        int candidate;
-
-        candidate = TextUtils.getOffsetBefore(mText, offset);
-        if (candidate >= start && candidate <= end) {
-            float h = getPrimaryHorizontal(candidate);
-
-            if (h > horiz && h < besth) {
-                best = candidate;
-                besth = h;
-            }
-        }
-
-        candidate = TextUtils.getOffsetAfter(mText, offset);
-        if (candidate >= start && candidate <= end) {
-            float h = getPrimaryHorizontal(candidate);
-
-            if (h > horiz && h < besth) {
-                best = candidate;
-                besth = h;
-            }
-        }
-
-        int here = start;
-        for (int i = 0; i < dirs.mDirections.length; i++) {
-            int there = here + dirs.mDirections[i];
-            if (there > end)
-                there = end;
-
-            float h = getPrimaryHorizontal(here);
-
-            if (h > horiz && h < besth) {
-                best = here;
-                besth = h;
-            }
-
-            candidate = TextUtils.getOffsetAfter(mText, here);
-            if (candidate >= start && candidate <= end) {
-                h = getPrimaryHorizontal(candidate);
-
-                if (h > horiz && h < besth) {
-                    best = candidate;
-                    besth = h;
+        boolean advance = toLeft == (lineDir == DIR_RIGHT_TO_LEFT);
+        if (caret == (advance ? lineEnd : lineStart)) {
+            // walking off line, so look at the line we're headed to
+            if (caret == lineStart) {
+                if (line > 0) {
+                    --line;
+                } else {
+                    return caret; // at very start, don't move
+                }
+            } else {
+                if (line < getLineCount() - 1) {
+                    ++line;
+                } else {
+                    return caret; // at very end, don't move
                 }
             }
 
-            candidate = TextUtils.getOffsetBefore(mText, there);
-            if (candidate >= start && candidate <= end) {
-                h = getPrimaryHorizontal(candidate);
-
-                if (h > horiz && h < besth) {
-                    best = candidate;
-                    besth = h;
-                }
+            lineStart = getLineStart(line);
+            lineEnd = getLineEnd(line);
+            int newDir = getParagraphDirection(line);
+            if (newDir != lineDir) {
+                // unusual case.  we want to walk onto the line, but it runs
+                // in a different direction than this one, so we fake movement
+                // in the opposite direction.
+                toLeft = !toLeft;
+                lineDir = newDir;
             }
-
-            here = there;
         }
 
-        float h = getPrimaryHorizontal(end);
+        Directions directions = getLineDirections(line);
 
-        if (h > horiz && h < besth) {
-            best = end;
-            besth = h;
-        }
-
-        if (best != offset)
-            return best;
-
-        int dir = getParagraphDirection(line);
-
-        if (dir > 0) {
-            if (line == getLineCount() - 1)
-                return best;
-            else
-                return getOffsetForHorizontal(line + 1, -10000);
-        } else {
-            if (line == 0)
-                return best;
-            else
-                return getOffsetForHorizontal(line - 1, -10000);
-        }
+        TextLine tl = TextLine.obtain();
+        // XXX: we don't care about tabs
+        tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null);
+        caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft);
+        tl = TextLine.recycle(tl);
+        return caret;
     }
 
     private int getOffsetAtStartOf(int offset) {
+        // XXX this probably should skip local reorderings and
+        // zero-width characters, look at callers
         if (offset == 0)
             return 0;
 
@@ -1118,7 +1130,7 @@
     /**
      * Fills in the specified Path with a representation of a cursor
      * at the specified offset.  This will often be a vertical line
-     * but can be multiple discontinous lines in text with multiple
+     * but can be multiple discontinuous lines in text with multiple
      * directionalities.
      */
     public void getCursorPath(int point, Path dest,
@@ -1130,14 +1142,12 @@
         int bottom = getLineTop(line+1);
 
         float h1 = getPrimaryHorizontal(point) - 0.5f;
-        float h2 = getSecondaryHorizontal(point) - 0.5f;
+        float h2 = isLevelBoundary(point) ?
+                    getSecondaryHorizontal(point) - 0.5f : h1;
 
-        int caps = TextKeyListener.getMetaState(editingBuffer,
-                                                KeyEvent.META_SHIFT_ON) |
-                   TextKeyListener.getMetaState(editingBuffer,
-                                                TextKeyListener.META_SELECTING);
-        int fn = TextKeyListener.getMetaState(editingBuffer,
-                                              KeyEvent.META_ALT_ON);
+        int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) |
+                   TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING);
+        int fn = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_ALT_ON);
         int dist = 0;
 
         if (caps != 0 || fn != 0) {
@@ -1207,9 +1217,10 @@
         if (lineend > linestart && mText.charAt(lineend - 1) == '\n')
             lineend--;
 
-        int here = linestart;
-        for (int i = 0; i < dirs.mDirections.length; i++) {
-            int there = here + dirs.mDirections[i];
+        for (int i = 0; i < dirs.mDirections.length; i += 2) {
+            int here = linestart + dirs.mDirections[i];
+            int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK);
+
             if (there > lineend)
                 there = lineend;
 
@@ -1218,14 +1229,12 @@
                 int en = Math.min(end, there);
 
                 if (st != en) {
-                    float h1 = getHorizontal(st, false, false, line);
-                    float h2 = getHorizontal(en, true, false, line);
+                    float h1 = getHorizontal(st, false, line);
+                    float h2 = getHorizontal(en, true, line);
 
                     dest.addRect(h1, top, h2, bottom, Path.Direction.CW);
                 }
             }
-
-            here = there;
         }
     }
 
@@ -1260,7 +1269,7 @@
 
             addSelection(startline, start, getLineEnd(startline),
                          top, getLineBottom(startline), dest);
-            
+
             if (getParagraphDirection(startline) == DIR_RIGHT_TO_LEFT)
                 dest.addRect(getLineLeft(startline), top,
                               0, getLineBottom(startline), Path.Direction.CW);
@@ -1296,7 +1305,7 @@
 
         if (mSpannedText) {
             Spanned sp = (Spanned) mText;
-            AlignmentSpan[] spans = sp.getSpans(getLineStart(line),
+            AlignmentSpan[] spans = getParagraphSpans(sp, getLineStart(line),
                                                 getLineEnd(line),
                                                 AlignmentSpan.class);
 
@@ -1313,423 +1322,173 @@
      * Get the left edge of the specified paragraph, inset by left margins.
      */
     public final int getParagraphLeft(int line) {
-        int dir = getParagraphDirection(line);
-
         int left = 0;
-
-        boolean par = false;
-        int off = getLineStart(line);
-        if (off == 0 || mText.charAt(off - 1) == '\n')
-            par = true;
-
-        if (dir == DIR_LEFT_TO_RIGHT) {
-            if (mSpannedText) {
-                Spanned sp = (Spanned) mText;
-                LeadingMarginSpan[] spans = sp.getSpans(getLineStart(line),
-                                                        getLineEnd(line),
-                                                        LeadingMarginSpan.class);
-
-                for (int i = 0; i < spans.length; i++) {
-                    boolean margin = par;
-                    LeadingMarginSpan span = spans[i];
-                    if (span instanceof LeadingMarginSpan.LeadingMarginSpan2) {
-                        int count = ((LeadingMarginSpan.LeadingMarginSpan2)span).getLeadingMarginLineCount();
-                        margin = count >= line;
-                    }
-                    left += span.getLeadingMargin(margin);
-                }
-            }
+        int dir = getParagraphDirection(line);
+        if (dir == DIR_RIGHT_TO_LEFT || !mSpannedText) {
+            return left; // leading margin has no impact, or no styles
         }
-
-        return left;
+        return getParagraphLeadingMargin(line);
     }
 
     /**
      * Get the right edge of the specified paragraph, inset by right margins.
      */
     public final int getParagraphRight(int line) {
-        int dir = getParagraphDirection(line);
-
         int right = mWidth;
-
-        boolean par = false;
-        int off = getLineStart(line);
-        if (off == 0 || mText.charAt(off - 1) == '\n')
-            par = true;
-
-
-        if (dir == DIR_RIGHT_TO_LEFT) {
-            if (mSpannedText) {
-                Spanned sp = (Spanned) mText;
-                LeadingMarginSpan[] spans = sp.getSpans(getLineStart(line),
-                                                        getLineEnd(line),
-                                                        LeadingMarginSpan.class);
-
-                for (int i = 0; i < spans.length; i++) {
-                    right -= spans[i].getLeadingMargin(par);
-                }
-            }
+        int dir = getParagraphDirection(line);
+        if (dir == DIR_LEFT_TO_RIGHT || !mSpannedText) {
+            return right; // leading margin has no impact, or no styles
         }
-
-        return right;
-    }
-
-    private void drawText(Canvas canvas,
-                                 CharSequence text, int start, int end,
-                                 int dir, Directions directions,
-                                 float x, int top, int y, int bottom,
-                                 TextPaint paint,
-                                 TextPaint workPaint,
-                                 boolean hasTabs, Object[] parspans) {
-        char[] buf;
-        if (!hasTabs) {
-            if (directions == DIRS_ALL_LEFT_TO_RIGHT) {
-                if (DEBUG) {
-                    Assert.assertTrue(DIR_LEFT_TO_RIGHT == dir);
-                }
-                Styled.drawText(canvas, text, start, end, dir, false, x, top, y, bottom, paint, workPaint, false);
-                return;
-            }
-            buf = null;
-        } else {
-            buf = TextUtils.obtain(end - start);
-            TextUtils.getChars(text, start, end, buf, 0);
-        }
-
-        float h = 0;
-
-        int here = 0;
-        for (int i = 0; i < directions.mDirections.length; i++) {
-            int there = here + directions.mDirections[i];
-            if (there > end - start)
-                there = end - start;
-
-            int segstart = here;
-            for (int j = hasTabs ? here : there; j <= there; j++) {
-                if (j == there || buf[j] == '\t') {
-                    h += Styled.drawText(canvas, text,
-                                         start + segstart, start + j,
-                                         dir, (i & 1) != 0, x + h,
-                                         top, y, bottom, paint, workPaint,
-                                         start + j != end);
-
-                    if (j != there && buf[j] == '\t')
-                        h = dir * nextTab(text, start, end, h * dir, parspans);
-
-                    segstart = j + 1;
-                } else if (hasTabs && buf[j] >= 0xD800 && buf[j] <= 0xDFFF && j + 1 < there) {
-                    int emoji = Character.codePointAt(buf, j);
-
-                    if (emoji >= MIN_EMOJI && emoji <= MAX_EMOJI) {
-                        Bitmap bm = EMOJI_FACTORY.
-                            getBitmapFromAndroidPua(emoji);
-
-                        if (bm != null) {
-                            h += Styled.drawText(canvas, text,
-                                                 start + segstart, start + j,
-                                                 dir, (i & 1) != 0, x + h,
-                                                 top, y, bottom, paint, workPaint,
-                                                 start + j != end);
-
-                            if (mEmojiRect == null) {
-                                mEmojiRect = new RectF();
-                            }
-
-                            workPaint.set(paint);
-                            Styled.measureText(paint, workPaint, text,
-                                               start + j, start + j + 1,
-                                               null);
-                                        
-                            float bitmapHeight = bm.getHeight();
-                            float textHeight = -workPaint.ascent();
-                            float scale = textHeight / bitmapHeight;
-                            float width = bm.getWidth() * scale;
-
-                            mEmojiRect.set(x + h, y - textHeight,
-                                           x + h + width, y);
-
-                            canvas.drawBitmap(bm, null, mEmojiRect, paint);
-                            h += width;
-
-                            j++;
-                            segstart = j + 1;
-                        }
-                    }
-                }
-            }
-
-            here = there;
-        }
-
-        if (hasTabs)
-            TextUtils.recycle(buf);
-    }
-
-    private static float measureText(TextPaint paint,
-                                     TextPaint workPaint,
-                                     CharSequence text,
-                                     int start, int offset, int end,
-                                     int dir, Directions directions,
-                                     boolean trailing, boolean alt,
-                                     boolean hasTabs, Object[] tabs) {
-        char[] buf = null;
-
-        if (hasTabs) {
-            buf = TextUtils.obtain(end - start);
-            TextUtils.getChars(text, start, end, buf, 0);
-        }
-
-        float h = 0;
-
-        if (alt) {
-            if (dir == DIR_RIGHT_TO_LEFT)
-                trailing = !trailing;
-        }
-
-        int here = 0;
-        for (int i = 0; i < directions.mDirections.length; i++) {
-            if (alt)
-                trailing = !trailing;
-
-            int there = here + directions.mDirections[i];
-            if (there > end - start)
-                there = end - start;
-
-            int segstart = here;
-            for (int j = hasTabs ? here : there; j <= there; j++) {
-                int codept = 0;
-                Bitmap bm = null;
-
-                if (hasTabs && j < there) {
-                    codept = buf[j];
-                }
-
-                if (codept >= 0xD800 && codept <= 0xDFFF && j + 1 < there) {
-                    codept = Character.codePointAt(buf, j);
-
-                    if (codept >= MIN_EMOJI && codept <= MAX_EMOJI) {
-                        bm = EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
-                    }
-                }
-
-                if (j == there || codept == '\t' || bm != null) {
-                    float segw;
-
-                    if (offset < start + j ||
-                       (trailing && offset <= start + j)) {
-                        if (dir == DIR_LEFT_TO_RIGHT && (i & 1) == 0) {
-                            h += Styled.measureText(paint, workPaint, text,
-                                                    start + segstart, offset,
-                                                    null);
-                            return h;
-                        }
-
-                        if (dir == DIR_RIGHT_TO_LEFT && (i & 1) != 0) {
-                            h -= Styled.measureText(paint, workPaint, text,
-                                                    start + segstart, offset,
-                                                    null);
-                            return h;
-                        }
-                    }
-
-                    segw = Styled.measureText(paint, workPaint, text,
-                                              start + segstart, start + j,
-                                              null);
-
-                    if (offset < start + j ||
-                        (trailing && offset <= start + j)) {
-                        if (dir == DIR_LEFT_TO_RIGHT) {
-                            h += segw - Styled.measureText(paint, workPaint,
-                                                           text,
-                                                           start + segstart,
-                                                           offset, null);
-                            return h;
-                        }
-
-                        if (dir == DIR_RIGHT_TO_LEFT) {
-                            h -= segw - Styled.measureText(paint, workPaint,
-                                                           text,
-                                                           start + segstart,
-                                                           offset, null);
-                            return h;
-                        }
-                    }
-
-                    if (dir == DIR_RIGHT_TO_LEFT)
-                        h -= segw;
-                    else
-                        h += segw;
-
-                    if (j != there && buf[j] == '\t') {
-                        if (offset == start + j)
-                            return h;
-
-                        h = dir * nextTab(text, start, end, h * dir, tabs);
-                    }
-
-                    if (j != there && bm != null) {
-                        if (offset == start + j) return h;
-                        workPaint.set(paint);
-                        Styled.measureText(paint, workPaint, text,
-                                           j, j + 2, null);
-
-                        float wid = (float) bm.getWidth() *
-                                    -workPaint.ascent() / bm.getHeight();
-
-                        if (dir == DIR_RIGHT_TO_LEFT) {
-                            h -= wid;
-                        } else {
-                            h += wid;
-                        }
-
-                        j++;
-                    }
-
-                    segstart = j + 1;
-                }
-            }
-
-            here = there;
-        }
-
-        if (hasTabs)
-            TextUtils.recycle(buf);
-
-        return h;
+        return right - getParagraphLeadingMargin(line);
     }
 
     /**
-     * Measure width of a run of text on a single line that is known to all be
-     * in the same direction as the paragraph base direction. Returns the width,
-     * and the line metrics in fm if fm is not null.
-     *
-     * @param paint the paint for the text; will not be modified
-     * @param workPaint paint available for modification
-     * @param text text
-     * @param start start of the line
-     * @param end limit of the line
-     * @param fm object to return integer metrics in, can be null
-     * @param hasTabs true if it is known that the line has tabs
-     * @param tabs tab position information
-     * @return the width of the text from start to end
+     * Returns the effective leading margin (unsigned) for this line,
+     * taking into account LeadingMarginSpan and LeadingMarginSpan2.
+     * @param line the line index
+     * @return the leading margin of this line
      */
-    /* package */ static float measureText(TextPaint paint,
-                                           TextPaint workPaint,
-                                           CharSequence text,
-                                           int start, int end,
-                                           Paint.FontMetricsInt fm,
-                                           boolean hasTabs, Object[] tabs) {
-        char[] buf = null;
-  
-        if (hasTabs) {
-            buf = TextUtils.obtain(end - start);
-            TextUtils.getChars(text, start, end, buf, 0);
+    private int getParagraphLeadingMargin(int line) {
+        if (!mSpannedText) {
+            return 0;
+        }
+        Spanned spanned = (Spanned) mText;
+
+        int lineStart = getLineStart(line);
+        int lineEnd = getLineEnd(line);
+        int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd,
+                LeadingMarginSpan.class);
+        LeadingMarginSpan[] spans = getParagraphSpans(spanned, lineStart, spanEnd,
+                                                LeadingMarginSpan.class);
+        if (spans.length == 0) {
+            return 0; // no leading margin span;
         }
 
-        int len = end - start;
+        int margin = 0;
 
-        int lastPos = 0;
-        float width = 0;
-        int ascent = 0, descent = 0, top = 0, bottom = 0;
+        boolean isFirstParaLine = lineStart == 0 ||
+            spanned.charAt(lineStart - 1) == '\n';
 
-        if (fm != null) {
-            fm.ascent = 0;
-            fm.descent = 0;
-        }
-
-        for (int pos = hasTabs ? 0 : len; pos <= len; pos++) {
-            int codept = 0;
-            Bitmap bm = null;
-
-            if (hasTabs && pos < len) {
-                codept = buf[pos];
+        for (int i = 0; i < spans.length; i++) {
+            LeadingMarginSpan span = spans[i];
+            boolean useFirstLineMargin = isFirstParaLine;
+            if (span instanceof LeadingMarginSpan2) {
+                int spStart = spanned.getSpanStart(span);
+                int spanLine = getLineForOffset(spStart);
+                int count = ((LeadingMarginSpan2)span).getLeadingMarginLineCount();
+                useFirstLineMargin = line < spanLine + count;
             }
+            margin += span.getLeadingMargin(useFirstLineMargin);
+        }
 
-            if (codept >= 0xD800 && codept <= 0xDFFF && pos < len) {
-                codept = Character.codePointAt(buf, pos);
+        return margin;
+    }
 
-                if (codept >= MIN_EMOJI && codept <= MAX_EMOJI) {
-                    bm = EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
+    /* package */
+    static float measurePara(TextPaint paint, TextPaint workPaint,
+            CharSequence text, int start, int end) {
+
+        MeasuredText mt = MeasuredText.obtain();
+        TextLine tl = TextLine.obtain();
+        try {
+            mt.setPara(text, start, end, DIR_REQUEST_LTR);
+            Directions directions;
+            int dir;
+            if (mt.mEasy) {
+                directions = DIRS_ALL_LEFT_TO_RIGHT;
+                dir = Layout.DIR_LEFT_TO_RIGHT;
+            } else {
+                directions = AndroidBidi.directions(mt.mDir, mt.mLevels,
+                    0, mt.mChars, 0, mt.mLen);
+                dir = mt.mDir;
+            }
+            char[] chars = mt.mChars;
+            int len = mt.mLen;
+            boolean hasTabs = false;
+            TabStops tabStops = null;
+            for (int i = 0; i < len; ++i) {
+                if (chars[i] == '\t') {
+                    hasTabs = true;
+                    if (text instanceof Spanned) {
+                        Spanned spanned = (Spanned) text;
+                        int spanEnd = spanned.nextSpanTransition(start, end,
+                                TabStopSpan.class);
+                        TabStopSpan[] spans = getParagraphSpans(spanned, start, spanEnd,
+                                TabStopSpan.class);
+                        if (spans.length > 0) {
+                            tabStops = new TabStops(TAB_INCREMENT, spans);
+                        }
+                    }
+                    break;
                 }
             }
+            tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops);
+            return tl.metrics(null);
+        } finally {
+            TextLine.recycle(tl);
+            MeasuredText.recycle(mt);
+        }
+    }
 
-            if (pos == len || codept == '\t' || bm != null) {
-                workPaint.baselineShift = 0;
+    /**
+     * @hide
+     */
+    /* package */ static class TabStops {
+        private int[] mStops;
+        private int mNumStops;
+        private int mIncrement;
 
-                width += Styled.measureText(paint, workPaint, text,
-                                        start + lastPos, start + pos,
-                                        fm);
+        TabStops(int increment, Object[] spans) {
+            reset(increment, spans);
+        }
 
-                if (fm != null) {
-                    if (workPaint.baselineShift < 0) {
-                        fm.ascent += workPaint.baselineShift;
-                        fm.top += workPaint.baselineShift;
-                    } else {
-                        fm.descent += workPaint.baselineShift;
-                        fm.bottom += workPaint.baselineShift;
+        void reset(int increment, Object[] spans) {
+            this.mIncrement = increment;
+
+            int ns = 0;
+            if (spans != null) {
+                int[] stops = this.mStops;
+                for (Object o : spans) {
+                    if (o instanceof TabStopSpan) {
+                        if (stops == null) {
+                            stops = new int[10];
+                        } else if (ns == stops.length) {
+                            int[] nstops = new int[ns * 2];
+                            for (int i = 0; i < ns; ++i) {
+                                nstops[i] = stops[i];
+                            }
+                            stops = nstops;
+                        }
+                        stops[ns++] = ((TabStopSpan) o).getTabStop();
                     }
                 }
-
-                if (pos != len) {
-                    if (bm == null) {
-                        // no emoji, must have hit a tab
-                        width = nextTab(text, start, end, width, tabs);
-                    } else {
-                        // This sets up workPaint with the font on the emoji
-                        // text, so that we can extract the ascent and scale.
-
-                        // We can't use the result of the previous call to
-                        // measureText because the emoji might have its own style.
-                        // We have to initialize workPaint here because if the
-                        // text is unstyled measureText might not use workPaint
-                        // at all.
-                        workPaint.set(paint);
-                        Styled.measureText(paint, workPaint, text,
-                                           start + pos, start + pos + 1, null);
-
-                        width += (float) bm.getWidth() *
-                                    -workPaint.ascent() / bm.getHeight();
-
-                        // Since we had an emoji, we bump past the second half
-                        // of the surrogate pair.
-                        pos++;
-                    }
+                if (ns > 1) {
+                    Arrays.sort(stops, 0, ns);
                 }
-
-                if (fm != null) {
-                    if (fm.ascent < ascent) {
-                        ascent = fm.ascent;
-                    }
-                    if (fm.descent > descent) {
-                        descent = fm.descent;
-                    }
-
-                    if (fm.top < top) {
-                        top = fm.top;
-                    }
-                    if (fm.bottom > bottom) {
-                        bottom = fm.bottom;
-                    }
-
-                    // No need to take bitmap height into account here,
-                    // since it is scaled to match the text height.
+                if (stops != this.mStops) {
+                    this.mStops = stops;
                 }
-
-                lastPos = pos + 1;
             }
+            this.mNumStops = ns;
         }
 
-        if (fm != null) {
-            fm.ascent = ascent;
-            fm.descent = descent;
-            fm.top = top;
-            fm.bottom = bottom;
+        float nextTab(float h) {
+            int ns = this.mNumStops;
+            if (ns > 0) {
+                int[] stops = this.mStops;
+                for (int i = 0; i < ns; ++i) {
+                    int stop = stops[i];
+                    if (stop > h) {
+                        return stop;
+                    }
+                }
+            }
+            return nextDefaultStop(h, mIncrement);
         }
 
-        if (hasTabs)
-            TextUtils.recycle(buf);
-
-        return width;
+        public static float nextDefaultStop(float h, int inc) {
+            return ((int) ((h + inc) / inc)) * inc;
+        }
     }
 
     /**
@@ -1751,7 +1510,7 @@
 
         if (text instanceof Spanned) {
             if (tabs == null) {
-                tabs = ((Spanned) text).getSpans(start, end, TabStopSpan.class);
+                tabs = getParagraphSpans((Spanned) text, start, end, TabStopSpan.class);
                 alltabs = true;
             }
 
@@ -1778,6 +1537,38 @@
         return mSpannedText;
     }
 
+    /**
+     * Returns the same as <code>text.getSpans()</code>, except where
+     * <code>start</code> and <code>end</code> are the same and are not
+     * at the very beginning of the text, in which case an empty array
+     * is returned instead.
+     * <p>
+     * This is needed because of the special case that <code>getSpans()</code>
+     * on an empty range returns the spans adjacent to that range, which is
+     * primarily for the sake of <code>TextWatchers</code> so they will get
+     * notifications when text goes from empty to non-empty.  But it also
+     * has the unfortunate side effect that if the text ends with an empty
+     * paragraph, that paragraph accidentally picks up the styles of the
+     * preceding paragraph (even though those styles will not be picked up
+     * by new text that is inserted into the empty paragraph).
+     * <p>
+     * The reason it just checks whether <code>start</code> and <code>end</code>
+     * is the same is that the only time a line can contain 0 characters
+     * is if it is the final paragraph of the Layout; otherwise any line will
+     * contain at least one printing or newline character.  The reason for the
+     * additional check if <code>start</code> is greater than 0 is that
+     * if the empty paragraph is the entire content of the buffer, paragraph
+     * styles that are already applied to the buffer will apply to text that
+     * is inserted into it.
+     */
+    /* package */ static <T> T[] getParagraphSpans(Spanned text, int start, int end, Class<T> type) {
+        if (start == end && start > 0) {
+            return (T[]) ArrayUtils.emptyArray(type);
+        }
+
+        return text.getSpans(start, end, type);
+    }
+
     private void ellipsize(int start, int end, int line,
                            char[] dest, int destoff) {
         int ellipsisCount = getEllipsisCount(line);
@@ -1808,23 +1599,22 @@
 
     /**
      * Stores information about bidirectional (left-to-right or right-to-left)
-     * text within the layout of a line.  TODO: This work is not complete
-     * or correct and will be fleshed out in a later revision.
+     * text within the layout of a line.
      */
     public static class Directions {
-        private short[] mDirections;
+        // Directions represents directional runs within a line of text.
+        // Runs are pairs of ints listed in visual order, starting from the
+        // leading margin.  The first int of each pair is the offset from
+        // the first character of the line to the start of the run.  The
+        // second int represents both the length and level of the run.
+        // The length is in the lower bits, accessed by masking with
+        // DIR_LENGTH_MASK.  The level is in the higher bits, accessed
+        // by shifting by DIR_LEVEL_SHIFT and masking by DIR_LEVEL_MASK.
+        // To simply test for an RTL direction, test the bit using
+        // DIR_RTL_FLAG, if set then the direction is rtl.
 
-        // The values in mDirections are the offsets from the first character
-        // in the line to the next flip in direction.  Runs at even indices
-        // are left-to-right, the others are right-to-left.  So, for example,
-        // a line that starts with a right-to-left run has 0 at mDirections[0],
-        // since the 'first' (ltr) run is zero length.
-        //
-        // The code currently assumes that each run is adjacent to the previous
-        // one, progressing in the base line direction.  This isn't sufficient
-        // to handle nested runs, for example numeric text in an rtl context
-        // in an ltr paragraph.
-        /* package */ Directions(short[] dirs) {
+        /* package */ int[] mDirections;
+        /* package */ Directions(int[] dirs) {
             mDirections = dirs;
         }
     }
@@ -1835,6 +1625,7 @@
      * line is ellipsized, not getLineStart().)
      */
     public abstract int getEllipsisStart(int line);
+
     /**
      * Returns the number of characters to be ellipsized away, or 0 if
      * no ellipsis is to take place.
@@ -1874,7 +1665,7 @@
         public int length() {
             return mText.length();
         }
-    
+
         public CharSequence subSequence(int start, int end) {
             char[] s = new char[end - start];
             getChars(start, end, s, 0);
@@ -1935,17 +1726,22 @@
     private Alignment mAlignment = Alignment.ALIGN_NORMAL;
     private float mSpacingMult;
     private float mSpacingAdd;
-    private static Rect sTempRect = new Rect();
+    private static final Rect sTempRect = new Rect();
     private boolean mSpannedText;
 
     public static final int DIR_LEFT_TO_RIGHT = 1;
     public static final int DIR_RIGHT_TO_LEFT = -1;
-    
+
     /* package */ static final int DIR_REQUEST_LTR = 1;
     /* package */ static final int DIR_REQUEST_RTL = -1;
     /* package */ static final int DIR_REQUEST_DEFAULT_LTR = 2;
     /* package */ static final int DIR_REQUEST_DEFAULT_RTL = -2;
 
+    /* package */ static final int RUN_LENGTH_MASK = 0x03ffffff;
+    /* package */ static final int RUN_LEVEL_SHIFT = 26;
+    /* package */ static final int RUN_LEVEL_MASK = 0x3f;
+    /* package */ static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT;
+
     public enum Alignment {
         ALIGN_NORMAL,
         ALIGN_OPPOSITE,
@@ -1957,8 +1753,7 @@
     private static final int TAB_INCREMENT = 20;
 
     /* package */ static final Directions DIRS_ALL_LEFT_TO_RIGHT =
-                                       new Directions(new short[] { 32767 });
+        new Directions(new int[] { 0, RUN_LENGTH_MASK });
     /* package */ static final Directions DIRS_ALL_RIGHT_TO_LEFT =
-                                       new Directions(new short[] { 0, 32767 });
-
+        new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG });
 }
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
new file mode 100644
index 0000000..d5699f1
--- /dev/null
+++ b/core/java/android/text/MeasuredText.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import com.android.internal.util.ArrayUtils;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.text.style.MetricAffectingSpan;
+import android.text.style.ReplacementSpan;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+class MeasuredText {
+    /* package */ CharSequence mText;
+    /* package */ int mTextStart;
+    /* package */ float[] mWidths;
+    /* package */ char[] mChars;
+    /* package */ byte[] mLevels;
+    /* package */ int mDir;
+    /* package */ boolean mEasy;
+    /* package */ int mLen;
+    private int mPos;
+    private TextPaint mWorkPaint;
+
+    private MeasuredText() {
+        mWorkPaint = new TextPaint();
+    }
+
+    private static MeasuredText[] cached = new MeasuredText[3];
+
+    /* package */
+    static MeasuredText obtain() {
+        MeasuredText mt;
+        synchronized (cached) {
+            for (int i = cached.length; --i >= 0;) {
+                if (cached[i] != null) {
+                    mt = cached[i];
+                    cached[i] = null;
+                    return mt;
+                }
+            }
+        }
+        mt = new MeasuredText();
+        Log.e("MEAS", "new: " + mt);
+        return mt;
+    }
+
+    /* package */
+    static MeasuredText recycle(MeasuredText mt) {
+        mt.mText = null;
+        if (mt.mLen < 1000) {
+            synchronized(cached) {
+                for (int i = 0; i < cached.length; ++i) {
+                    if (cached[i] == null) {
+                        cached[i] = mt;
+                        break;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Analyzes text for bidirectional runs.  Allocates working buffers.
+     */
+    /* package */
+    void setPara(CharSequence text, int start, int end, int bidiRequest) {
+        mText = text;
+        mTextStart = start;
+
+        int len = end - start;
+        mLen = len;
+        mPos = 0;
+
+        if (mWidths == null || mWidths.length < len) {
+            mWidths = new float[ArrayUtils.idealFloatArraySize(len)];
+        }
+        if (mChars == null || mChars.length < len) {
+            mChars = new char[ArrayUtils.idealCharArraySize(len)];
+        }
+        TextUtils.getChars(text, start, end, mChars, 0);
+
+        if (text instanceof Spanned) {
+            Spanned spanned = (Spanned) text;
+            ReplacementSpan[] spans = spanned.getSpans(start, end,
+                    ReplacementSpan.class);
+
+            for (int i = 0; i < spans.length; i++) {
+                int startInPara = spanned.getSpanStart(spans[i]) - start;
+                int endInPara = spanned.getSpanEnd(spans[i]) - start;
+                for (int j = startInPara; j < endInPara; j++) {
+                    mChars[j] = '\uFFFC';
+                }
+            }
+        }
+
+        if (TextUtils.doesNotNeedBidi(mChars, 0, len)) {
+            mDir = Layout.DIR_LEFT_TO_RIGHT;
+            mEasy = true;
+        } else {
+            if (mLevels == null || mLevels.length < len) {
+                mLevels = new byte[ArrayUtils.idealByteArraySize(len)];
+            }
+            mDir = AndroidBidi.bidi(bidiRequest, mChars, mLevels, len, false);
+            mEasy = false;
+        }
+    }
+
+    float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) {
+        if (fm != null) {
+            paint.getFontMetricsInt(fm);
+        }
+
+        int p = mPos;
+        mPos = p + len;
+
+        if (mEasy) {
+            int flags = mDir == Layout.DIR_LEFT_TO_RIGHT
+                ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL;
+            return paint.getTextRunAdvances(mChars, p, len, p, len, flags, mWidths, p);
+        }
+
+        float totalAdvance = 0;
+        int level = mLevels[p];
+        for (int q = p, i = p + 1, e = p + len;; ++i) {
+            if (i == e || mLevels[i] != level) {
+                int flags = (level & 0x1) == 0 ? Canvas.DIRECTION_LTR : Canvas.DIRECTION_RTL;
+                totalAdvance +=
+                        paint.getTextRunAdvances(mChars, q, i - q, q, i - q, flags, mWidths, q);
+                if (i == e) {
+                    break;
+                }
+                q = i;
+                level = mLevels[i];
+            }
+        }
+        return totalAdvance;
+    }
+
+    float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len,
+            Paint.FontMetricsInt fm) {
+
+        TextPaint workPaint = mWorkPaint;
+        workPaint.set(paint);
+        // XXX paint should not have a baseline shift, but...
+        workPaint.baselineShift = 0;
+
+        ReplacementSpan replacement = null;
+        for (int i = 0; i < spans.length; i++) {
+            MetricAffectingSpan span = spans[i];
+            if (span instanceof ReplacementSpan) {
+                replacement = (ReplacementSpan)span;
+            } else {
+                span.updateMeasureState(workPaint);
+            }
+        }
+
+        float wid;
+        if (replacement == null) {
+            wid = addStyleRun(workPaint, len, fm);
+        } else {
+            // Use original text.  Shouldn't matter.
+            wid = replacement.getSize(workPaint, mText, mTextStart + mPos,
+                    mTextStart + mPos + len, fm);
+            float[] w = mWidths;
+            w[mPos] = wid;
+            for (int i = mPos + 1, e = mPos + len; i < e; i++)
+                w[i] = 0;
+        }
+
+        if (fm != null) {
+            if (workPaint.baselineShift < 0) {
+                fm.ascent += workPaint.baselineShift;
+                fm.top += workPaint.baselineShift;
+            } else {
+                fm.descent += workPaint.baselineShift;
+                fm.bottom += workPaint.baselineShift;
+            }
+        }
+
+        return wid;
+    }
+
+    int breakText(int start, int limit, boolean forwards, float width) {
+        float[] w = mWidths;
+        if (forwards) {
+            for (int i = start; i < limit; ++i) {
+                if ((width -= w[i]) < 0) {
+                    return i - start;
+                }
+            }
+        } else {
+            for (int i = limit; --i >= start;) {
+                if ((width -= w[i]) < 0) {
+                    return limit - i -1;
+                }
+            }
+        }
+
+        return limit - start;
+    }
+
+    float measure(int start, int limit) {
+        float width = 0;
+        float[] w = mWidths;
+        for (int i = start; i < limit; ++i) {
+            width += w[i];
+        }
+        return width;
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index caaafa1..fc01ef2 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -17,8 +17,9 @@
 package android.text;
 
 import com.android.internal.util.ArrayUtils;
-import android.graphics.Paint;
+
 import android.graphics.Canvas;
+import android.graphics.Paint;
 
 import java.lang.reflect.Array;
 
@@ -312,12 +313,15 @@
 
         moveGapTo(end);
 
-        if (tbend - tbstart >= mGapLength + (end - start))
-            resizeFor(mText.length - mGapLength +
-                      tbend - tbstart - (end - start));
+        // Can be negative
+        final int nbNewChars = (tbend - tbstart) - (end - start);
 
-        mGapStart += tbend - tbstart - (end - start);
-        mGapLength -= tbend - tbstart - (end - start);
+        if (nbNewChars >= mGapLength) {
+            resizeFor(mText.length + nbNewChars - mGapLength);
+        }
+
+        mGapStart += nbNewChars;
+        mGapLength -= nbNewChars;
 
         if (mGapLength < 1)
             new Exception("mGapLength < 1").printStackTrace();
@@ -707,6 +711,7 @@
      * the specified range of the buffer.  The kind may be Object.class to get
      * a list of all the spans regardless of type.
      */
+    @SuppressWarnings("unchecked")
     public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
         int spanCount = mSpanCount;
         Object[] spans = mSpans;
@@ -717,8 +722,8 @@
         int gaplen = mGapLength;
 
         int count = 0;
-        Object[] ret = null;
-        Object ret1 = null;
+        T[] ret = null;
+        T ret1 = null;
 
         for (int i = 0; i < spanCount; i++) {
             int spanStart = starts[i];
@@ -750,11 +755,13 @@
             }
 
             if (count == 0) {
-                ret1 = spans[i];
+                // Safe conversion thanks to the isInstance test above
+                ret1 = (T) spans[i];
                 count++;
             } else {
                 if (count == 1) {
-                    ret = (Object[]) Array.newInstance(kind, spanCount - i + 1);
+                    // Safe conversion, but requires a suppressWarning
+                    ret = (T[]) Array.newInstance(kind, spanCount - i + 1);
                     ret[0] = ret1;
                 }
 
@@ -771,29 +778,33 @@
                     }
 
                     System.arraycopy(ret, j, ret, j + 1, count - j);
-                    ret[j] = spans[i];
+                    // Safe conversion thanks to the isInstance test above
+                    ret[j] = (T) spans[i];
                     count++;
                 } else {
-                    ret[count++] = spans[i];
+                    // Safe conversion thanks to the isInstance test above
+                    ret[count++] = (T) spans[i];
                 }
             }
         }
 
         if (count == 0) {
-            return (T[]) ArrayUtils.emptyArray(kind);
+            return ArrayUtils.emptyArray(kind);
         }
         if (count == 1) {
-            ret = (Object[]) Array.newInstance(kind, 1);
+            // Safe conversion, but requires a suppressWarning
+            ret = (T[]) Array.newInstance(kind, 1);
             ret[0] = ret1;
-            return (T[]) ret;
+            return ret;
         }
         if (count == ret.length) {
-            return (T[]) ret;
+            return ret;
         }
 
-        Object[] nret = (Object[]) Array.newInstance(kind, count);
+        // Safe conversion, but requires a suppressWarning
+        T[] nret = (T[]) Array.newInstance(kind, count);
         System.arraycopy(ret, 0, nret, 0, count);
-        return (T[]) nret;
+        return nret;
     }
 
     /**
@@ -862,6 +873,7 @@
     /**
      * Return a String containing a copy of the chars in this buffer.
      */
+    @Override
     public String toString() {
         int len = length();
         char[] buf = new char[len];
@@ -952,6 +964,7 @@
         }
     }
 
+/*
     private boolean isprint(char c) { // XXX
         if (c >= ' ' && c <= '~')
             return true;
@@ -959,7 +972,6 @@
             return false;
     }
 
-/*
     private static final int startFlag(int flag) {
         return (flag >> 4) & 0x0F;
     }
@@ -1054,7 +1066,32 @@
         }
     }
 
+
     /**
+     * Don't call this yourself -- exists for Canvas to use internally.
+     * {@hide}
+     */
+    public void drawTextRun(Canvas c, int start, int end,
+            int contextStart, int contextEnd,
+            float x, float y, int flags, Paint p) {
+        checkRange("drawTextRun", start, end);
+
+        int contextLen = contextEnd - contextStart;
+        int len = end - start;
+        if (contextEnd <= mGapStart) {
+            c.drawTextRun(mText, start, len, contextStart, contextLen, x, y, flags, p);
+        } else if (contextStart >= mGapStart) {
+            c.drawTextRun(mText, start + mGapLength, len, contextStart + mGapLength,
+                    contextLen, x, y, flags, p);
+        } else {
+            char[] buf = TextUtils.obtain(contextLen);
+            getChars(contextStart, contextEnd, buf, 0);
+            c.drawTextRun(buf, start - contextStart, len, 0, contextLen, x, y, flags, p);
+            TextUtils.recycle(buf);
+        }
+    }
+
+   /**
      * Don't call this yourself -- exists for Paint to use internally.
      * {@hide}
      */
@@ -1103,6 +1140,58 @@
         return ret;
     }
 
+    /**
+     * Don't call this yourself -- exists for Paint to use internally.
+     * {@hide}
+     */
+    public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, int flags,
+            float[] advances, int advancesPos, Paint p) {
+
+        float ret;
+
+        int contextLen = contextEnd - contextStart;
+        int len = end - start;
+
+        if (end <= mGapStart) {
+            ret = p.getTextRunAdvances(mText, start, len, contextStart, contextLen,
+                    flags, advances, advancesPos);
+        } else if (start >= mGapStart) {
+            ret = p.getTextRunAdvances(mText, start + mGapLength, len,
+                    contextStart + mGapLength, contextLen, flags, advances, advancesPos);
+        } else {
+            char[] buf = TextUtils.obtain(contextLen);
+            getChars(contextStart, contextEnd, buf, 0);
+            ret = p.getTextRunAdvances(buf, start - contextStart, len,
+                    0, contextLen, flags, advances, advancesPos);
+            TextUtils.recycle(buf);
+        }
+
+        return ret;
+    }
+
+    public int getTextRunCursor(int contextStart, int contextEnd, int flags, int offset,
+            int cursorOpt, Paint p) {
+
+        int ret;
+
+        int contextLen = contextEnd - contextStart;
+        if (contextEnd <= mGapStart) {
+            ret = p.getTextRunCursor(mText, contextStart, contextLen,
+                    flags, offset, cursorOpt);
+        } else if (contextStart >= mGapStart) {
+            ret = p.getTextRunCursor(mText, contextStart + mGapLength, contextLen,
+                    flags, offset + mGapLength, cursorOpt) - mGapLength;
+        } else {
+            char[] buf = TextUtils.obtain(contextLen);
+            getChars(contextStart, contextEnd, buf, 0);
+            ret = p.getTextRunCursor(buf, 0, contextLen,
+                    flags, offset - contextStart, cursorOpt) + contextStart;
+            TextUtils.recycle(buf);
+        }
+
+        return ret;
+    }
+
     // Documentation from interface
     public void setFilters(InputFilter[] filters) {
         if (filters == null) {
diff --git a/core/java/android/text/Spanned.java b/core/java/android/text/Spanned.java
index 154497d..d14fcbc 100644
--- a/core/java/android/text/Spanned.java
+++ b/core/java/android/text/Spanned.java
@@ -91,7 +91,7 @@
     public static final int SPAN_EXCLUSIVE_EXCLUSIVE = SPAN_POINT_MARK;
 
     /**
-     * Non-0-length spans of type SPAN_INCLUSIVE_EXCLUSIVE expand
+     * Non-0-length spans of type SPAN_EXCLUSIVE_INCLUSIVE expand
      * to include text inserted at their ending point but not at their
      * starting point.  When 0-length, they behave like points.
      */
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
old mode 100755
new mode 100644
index d0d2482..cc969cb
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -16,14 +16,15 @@
 
 package android.text;
 
+import com.android.internal.util.ArrayUtils;
+
 import android.graphics.Bitmap;
 import android.graphics.Paint;
-import com.android.internal.util.ArrayUtils;
-import android.util.Log;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LineHeightSpan;
 import android.text.style.MetricAffectingSpan;
-import android.text.style.ReplacementSpan;
+import android.text.style.TabStopSpan;
+import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
 
 /**
  * StaticLayout is a Layout for text that will not be edited after it
@@ -31,8 +32,9 @@
  * <p>This is used by widgets to control text layout. You should not need
  * to use this class directly unless you are implementing your own widget
  * or custom display object, or would be tempted to call
- * {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
- *  Canvas.drawText()} directly.</p>
+ * {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int,
+ * float, float, android.graphics.Paint)
+ * Canvas.drawText()} directly.</p>
  */
 public class
 StaticLayout
@@ -62,7 +64,7 @@
                         boolean includepad,
                         TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
         super((ellipsize == null)
-                ? source 
+                ? source
                 : (source instanceof Spanned)
                     ? new SpannedEllipsizer(source)
                     : new Ellipsizer(source),
@@ -72,7 +74,7 @@
          * This is annoying, but we can't refer to the layout until
          * superclass construction is finished, and the superclass
          * constructor wants the reference to the display text.
-         * 
+         *
          * This will break if the superclass constructor ever actually
          * cares about the content instead of just holding the reference.
          */
@@ -94,13 +96,13 @@
         mLineDirections = new Directions[
                              ArrayUtils.idealIntArraySize(2 * mColumns)];
 
+        mMeasured = MeasuredText.obtain();
+
         generate(source, bufstart, bufend, paint, outerwidth, align,
                  spacingmult, spacingadd, includepad, includepad,
                  ellipsize != null, ellipsizedWidth, ellipsize);
 
-        mChdirs = null;
-        mChs = null;
-        mWidths = null;
+        mMeasured = MeasuredText.recycle(mMeasured);
         mFontMetricsInt = null;
     }
 
@@ -111,6 +113,7 @@
         mLines = new int[ArrayUtils.idealIntArraySize(2 * mColumns)];
         mLineDirections = new Directions[
                              ArrayUtils.idealIntArraySize(2 * mColumns)];
+        mMeasured = MeasuredText.obtain();
     }
 
     /* package */ void generate(CharSequence source, int bufstart, int bufend,
@@ -128,59 +131,50 @@
         Paint.FontMetricsInt fm = mFontMetricsInt;
         int[] choosehtv = null;
 
-        int end = TextUtils.indexOf(source, '\n', bufstart, bufend);
-        int bufsiz = end >= 0 ? end - bufstart : bufend - bufstart;
-        boolean first = true;
+        MeasuredText measured = mMeasured;
 
-        if (mChdirs == null) {
-            mChdirs = new byte[ArrayUtils.idealByteArraySize(bufsiz + 1)];
-            mChs = new char[ArrayUtils.idealCharArraySize(bufsiz + 1)];
-            mWidths = new float[ArrayUtils.idealIntArraySize((bufsiz + 1) * 2)];
-        }
-
-        byte[] chdirs = mChdirs;
-        char[] chs = mChs;
-        float[] widths = mWidths;
-
-        AlteredCharSequence alter = null;
         Spanned spanned = null;
-
         if (source instanceof Spanned)
             spanned = (Spanned) source;
 
         int DEFAULT_DIR = DIR_LEFT_TO_RIGHT; // XXX
 
-        for (int start = bufstart; start <= bufend; start = end) {
-            if (first)
-                first = false;
+        int paraEnd;
+        for (int paraStart = bufstart; paraStart <= bufend; paraStart = paraEnd) {
+            paraEnd = TextUtils.indexOf(source, '\n', paraStart, bufend);
+            if (paraEnd < 0)
+                paraEnd = bufend;
             else
-                end = TextUtils.indexOf(source, '\n', start, bufend);
+                paraEnd++;
+            int paraLen = paraEnd - paraStart;
 
-            if (end < 0)
-                end = bufend;
-            else
-                end++;
-
-            int firstWidthLineCount = 1;
+            int firstWidthLineLimit = mLineCount + 1;
             int firstwidth = outerwidth;
             int restwidth = outerwidth;
 
             LineHeightSpan[] chooseht = null;
 
             if (spanned != null) {
-                LeadingMarginSpan[] sp;
-
-                sp = spanned.getSpans(start, end, LeadingMarginSpan.class);
+                LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
+                        LeadingMarginSpan.class);
                 for (int i = 0; i < sp.length; i++) {
                     LeadingMarginSpan lms = sp[i];
                     firstwidth -= sp[i].getLeadingMargin(true);
                     restwidth -= sp[i].getLeadingMargin(false);
-                    if (lms instanceof LeadingMarginSpan.LeadingMarginSpan2) {
-                        firstWidthLineCount = ((LeadingMarginSpan.LeadingMarginSpan2)lms).getLeadingMarginLineCount();
+                    
+                    // LeadingMarginSpan2 is odd.  The count affects all
+                    // leading margin spans, not just this particular one,
+                    // and start from the top of the span, not the top of the
+                    // paragraph.
+                    if (lms instanceof LeadingMarginSpan2) {
+                        LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
+                        int lmsFirstLine = getLineForOffset(spanned.getSpanStart(lms2));
+                        firstWidthLineLimit = lmsFirstLine + 
+                            lms2.getLeadingMarginLineCount();
                     }
                 }
 
-                chooseht = spanned.getSpans(start, end, LineHeightSpan.class);
+                chooseht = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
 
                 if (chooseht.length != 0) {
                     if (choosehtv == null ||
@@ -192,11 +186,11 @@
                     for (int i = 0; i < chooseht.length; i++) {
                         int o = spanned.getSpanStart(chooseht[i]);
 
-                        if (o < start) {
+                        if (o < paraStart) {
                             // starts in this layout, before the
                             // current paragraph
 
-                            choosehtv[i] = getLineTop(getLineForOffset(o)); 
+                            choosehtv[i] = getLineTop(getLineForOffset(o));
                         } else {
                             // starts in this paragraph
 
@@ -206,166 +200,87 @@
                 }
             }
 
-            if (end - start > chdirs.length) {
-                chdirs = new byte[ArrayUtils.idealByteArraySize(end - start)];
-                mChdirs = chdirs;
-            }
-            if (end - start > chs.length) {
-                chs = new char[ArrayUtils.idealCharArraySize(end - start)];
-                mChs = chs;
-            }
-            if ((end - start) * 2 > widths.length) {
-                widths = new float[ArrayUtils.idealIntArraySize((end - start) * 2)];
-                mWidths = widths;
-            }
+            measured.setPara(source, paraStart, paraEnd, DIR_REQUEST_DEFAULT_LTR);
+            char[] chs = measured.mChars;
+            float[] widths = measured.mWidths;
+            byte[] chdirs = measured.mLevels;
+            int dir = measured.mDir;
+            boolean easy = measured.mEasy;
 
-            TextUtils.getChars(source, start, end, chs, 0);
-            final int n = end - start;
-
-            boolean easy = true;
-            boolean altered = false;
-            int dir = DEFAULT_DIR; // XXX
-
-            for (int i = 0; i < n; i++) {
-                if (chs[i] >= FIRST_RIGHT_TO_LEFT) {
-                    easy = false;
-                    break;
-                }
-            }
-
-            // Ensure that none of the underlying characters are treated
-            // as viable breakpoints, and that the entire run gets the
-            // same bidi direction.
-
-            if (source instanceof Spanned) {
-                Spanned sp = (Spanned) source;
-                ReplacementSpan[] spans = sp.getSpans(start, end, ReplacementSpan.class);
-
-                for (int y = 0; y < spans.length; y++) {
-                    int a = sp.getSpanStart(spans[y]);
-                    int b = sp.getSpanEnd(spans[y]);
-
-                    for (int x = a; x < b; x++) {
-                        chs[x - start] = '\uFFFC';
-                    }
-                }
-            }
-
-            if (!easy) {
-                // XXX put override flags, etc. into chdirs
-                dir = bidi(dir, chs, chdirs, n, false);
-
-                // Do mirroring for right-to-left segments
-
-                for (int i = 0; i < n; i++) {
-                    if (chdirs[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
-                        int j;
-
-                        for (j = i; j < n; j++) {
-                            if (chdirs[j] !=
-                                Character.DIRECTIONALITY_RIGHT_TO_LEFT)
-                                break;
-                        }
-
-                        if (AndroidCharacter.mirror(chs, i, j - i))
-                            altered = true;
-
-                        i = j - 1;
-                    }
-                }
-            }
-
-            CharSequence sub;
-
-            if (altered) {
-                if (alter == null)
-                    alter = AlteredCharSequence.make(source, chs, start, end);
-                else
-                    alter.update(chs, start, end);
-
-                sub = alter;
-            } else {
-                sub = source;
-            }
+            CharSequence sub = source;
 
             int width = firstwidth;
 
             float w = 0;
-            int here = start;
+            int here = paraStart;
 
-            int ok = start;
+            int ok = paraStart;
             float okwidth = w;
             int okascent = 0, okdescent = 0, oktop = 0, okbottom = 0;
 
-            int fit = start;
+            int fit = paraStart;
             float fitwidth = w;
             int fitascent = 0, fitdescent = 0, fittop = 0, fitbottom = 0;
 
-            boolean tab = false;
+            boolean hasTabOrEmoji = false;
+            boolean hasTab = false;
+            TabStops tabStops = null;
 
-            int next;
-            for (int i = start; i < end; i = next) {
-                if (spanned == null)
-                    next = end;
-                else
-                    next = spanned.nextSpanTransition(i, end,
-                                                      MetricAffectingSpan.
-                                                      class);
+            for (int spanStart = paraStart, spanEnd = spanStart, nextSpanStart;
+                    spanStart < paraEnd; spanStart = nextSpanStart) {
 
-                if (spanned == null) {
-                    final int actualNum = paint.getTextWidths(sub, i, next, widths);
-                    if (next - i > actualNum)
-                        adjustTextWidths(widths, sub, i, next, actualNum);
-                    System.arraycopy(widths, 0, widths,
-                                     end - start + (i - start), next - i);
-                                     
-                    paint.getFontMetricsInt(fm);
-                } else {
-                    mWorkPaint.baselineShift = 0;
+                if (spanStart == spanEnd) {
+                    if (spanned == null)
+                        spanEnd = paraEnd;
+                    else
+                        spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
+                                MetricAffectingSpan.class);
 
-                    final int actualNum = Styled.getTextWidths(paint, mWorkPaint,
-                            spanned, i, next,
-                            widths, fm);
-                    if (next - i > actualNum)
-                        adjustTextWidths(widths, spanned, i, next, actualNum);
-                    System.arraycopy(widths, 0, widths,
-                                     end - start + (i - start), next - i);
-
-                    if (mWorkPaint.baselineShift < 0) {
-                        fm.ascent += mWorkPaint.baselineShift;
-                        fm.top += mWorkPaint.baselineShift;
+                    int spanLen = spanEnd - spanStart;
+                    if (spanned == null) {
+                        measured.addStyleRun(paint, spanLen, fm);
                     } else {
-                        fm.descent += mWorkPaint.baselineShift;
-                        fm.bottom += mWorkPaint.baselineShift;
+                        MetricAffectingSpan[] spans =
+                            spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
+                        measured.addStyleRun(paint, spans, spanLen, fm);
                     }
                 }
 
+                nextSpanStart = spanEnd;
+                int startInPara = spanStart - paraStart;
+                int endInPara = spanEnd - paraStart;
+
                 int fmtop = fm.top;
                 int fmbottom = fm.bottom;
                 int fmascent = fm.ascent;
                 int fmdescent = fm.descent;
 
-                if (false) {
-                    StringBuilder sb = new StringBuilder();
-                    for (int j = i; j < next; j++) {
-                        sb.append(widths[j - start + (end - start)]);
-                        sb.append(' ');
-                    }
-
-                    Log.e("text", sb.toString());
-                }
-
-                for (int j = i; j < next; j++) {
-                    char c = chs[j - start];
+                for (int j = spanStart; j < spanEnd; j++) {
+                    char c = chs[j - paraStart];
                     float before = w;
 
                     if (c == '\n') {
                         ;
                     } else if (c == '\t') {
-                        w = Layout.nextTab(sub, start, end, w, null);
-                        tab = true;
-                    } else if (c >= 0xD800 && c <= 0xDFFF && j + 1 < next) {
-                        int emoji = Character.codePointAt(chs, j - start);
+                        if (hasTab == false) {
+                            hasTab = true;
+                            hasTabOrEmoji = true;
+                            if (spanned != null) {
+                                // First tab this para, check for tabstops
+                                TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
+                                        paraEnd, TabStopSpan.class);
+                                if (spans.length > 0) {
+                                    tabStops = new TabStops(TAB_INCREMENT, spans);
+                                }
+                            }
+                        }
+                        if (tabStops != null) {
+                            w = tabStops.nextTab(w);
+                        } else {
+                            w = TabStops.nextDefaultStop(w, TAB_INCREMENT);
+                        }
+                    } else if (c >= 0xD800 && c <= 0xDFFF && j + 1 < spanEnd) {
+                        int emoji = Character.codePointAt(chs, j - paraStart);
 
                         if (emoji >= MIN_EMOJI && emoji <= MAX_EMOJI) {
                             Bitmap bm = EMOJI_FACTORY.
@@ -380,21 +295,21 @@
                                     whichPaint = mWorkPaint;
                                 }
 
-                                float wid = (float) bm.getWidth() *
+                                float wid = bm.getWidth() *
                                             -whichPaint.ascent() /
                                             bm.getHeight();
 
                                 w += wid;
-                                tab = true;
+                                hasTabOrEmoji = true;
                                 j++;
                             } else {
-                                w += widths[j - start + (end - start)];
+                                w += widths[j - paraStart];
                             }
                         } else {
-                            w += widths[j - start + (end - start)];
+                            w += widths[j - paraStart];
                         }
                     } else {
-                        w += widths[j - start + (end - start)];
+                        w += widths[j - paraStart];
                     }
 
                     // Log.e("text", "was " + before + " now " + w + " after " + c + " within " + width);
@@ -415,7 +330,7 @@
                         /*
                          * From the Unicode Line Breaking Algorithm:
                          * (at least approximately)
-                         *  
+                         *
                          * .,:; are class IS: breakpoints
                          *      except when adjacent to digits
                          * /    is class SY: a breakpoint
@@ -430,12 +345,12 @@
 
                         if (c == ' ' || c == '\t' ||
                             ((c == '.'  || c == ',' || c == ':' || c == ';') &&
-                             (j - 1 < here || !Character.isDigit(chs[j - 1 - start])) &&
-                             (j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) ||
+                             (j - 1 < here || !Character.isDigit(chs[j - 1 - paraStart])) &&
+                             (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) ||
                             ((c == '/' || c == '-') &&
-                             (j + 1 >= next || !Character.isDigit(chs[j + 1 - start]))) ||
+                             (j + 1 >= spanEnd || !Character.isDigit(chs[j + 1 - paraStart]))) ||
                             (c >= FIRST_CJK && isIdeographic(c, true) &&
-                             j + 1 < next && isIdeographic(chs[j + 1 - start], false))) {
+                             j + 1 < spanEnd && isIdeographic(chs[j + 1 - paraStart], false))) {
                             okwidth = w;
                             ok = j + 1;
 
@@ -452,7 +367,7 @@
                         if (ok != here) {
                             // Log.e("text", "output ok " + here + " to " +ok);
 
-                            while (ok < next && chs[ok - start] == ' ') {
+                            while (ok < spanEnd && chs[ok - paraStart] == ' ') {
                                 ok++;
                             }
 
@@ -461,10 +376,10 @@
                                     okascent, okdescent, oktop, okbottom,
                                     v,
                                     spacingmult, spacingadd, chooseht,
-                                    choosehtv, fm, tab,
-                                    needMultiply, start, chdirs, dir, easy,
+                                    choosehtv, fm, hasTabOrEmoji,
+                                    needMultiply, paraStart, chdirs, dir, easy,
                                     ok == bufend, includepad, trackpad,
-                                    widths, start, end - start,
+                                    chs, widths, here - paraStart,
                                     where, ellipsizedWidth, okwidth,
                                     paint);
 
@@ -488,7 +403,7 @@
                         if (ok != here) {
                             // Log.e("text", "output ok " + here + " to " +ok);
 
-                            while (ok < next && chs[ok - start] == ' ') {
+                            while (ok < spanEnd && chs[ok - paraStart] == ' ') {
                                 ok++;
                             }
 
@@ -497,10 +412,10 @@
                                     okascent, okdescent, oktop, okbottom,
                                     v,
                                     spacingmult, spacingadd, chooseht,
-                                    choosehtv, fm, tab,
-                                    needMultiply, start, chdirs, dir, easy,
+                                    choosehtv, fm, hasTabOrEmoji,
+                                    needMultiply, paraStart, chdirs, dir, easy,
                                     ok == bufend, includepad, trackpad,
-                                    widths, start, end - start,
+                                    chs, widths, here - paraStart,
                                     where, ellipsizedWidth, okwidth,
                                     paint);
 
@@ -513,19 +428,20 @@
                                     fittop, fitbottom,
                                     v,
                                     spacingmult, spacingadd, chooseht,
-                                    choosehtv, fm, tab,
-                                    needMultiply, start, chdirs, dir, easy,
+                                    choosehtv, fm, hasTabOrEmoji,
+                                    needMultiply, paraStart, chdirs, dir, easy,
                                     fit == bufend, includepad, trackpad,
-                                    widths, start, end - start,
+                                    chs, widths, here - paraStart,
                                     where, ellipsizedWidth, fitwidth,
                                     paint);
 
                             here = fit;
                         } else {
                             // Log.e("text", "output one " + here + " to " +(here + 1));
-                            measureText(paint, mWorkPaint,
-                                        source, here, here + 1, fm, tab,
-                                        null);
+                            // XXX not sure why the existing fm wasn't ok.
+                            // measureText(paint, mWorkPaint,
+                            //             source, here, here + 1, fm, tab,
+                            //             null);
 
                             v = out(source,
                                     here, here+1,
@@ -533,19 +449,22 @@
                                     fm.top, fm.bottom,
                                     v,
                                     spacingmult, spacingadd, chooseht,
-                                    choosehtv, fm, tab,
-                                    needMultiply, start, chdirs, dir, easy,
+                                    choosehtv, fm, hasTabOrEmoji,
+                                    needMultiply, paraStart, chdirs, dir, easy,
                                     here + 1 == bufend, includepad,
                                     trackpad,
-                                    widths, start, end - start,
+                                    chs, widths, here - paraStart,
                                     where, ellipsizedWidth,
-                                    widths[here - start], paint);
+                                    widths[here - paraStart], paint);
 
                             here = here + 1;
                         }
 
-                        if (here < i) {
-                            j = next = here; // must remeasure
+                        if (here < spanStart) {
+                            // didn't output all the text for this span
+                            // we've measured the raw widths, though, so
+                            // just reset the start point
+                            j = nextSpanStart = here;
                         } else {
                             j = here - 1;    // continue looping
                         }
@@ -555,14 +474,14 @@
                         fitascent = fitdescent = fittop = fitbottom = 0;
                         okascent = okdescent = oktop = okbottom = 0;
 
-                        if (--firstWidthLineCount <= 0) {
+                        if (--firstWidthLineLimit <= 0) {
                             width = restwidth;
                         }
                     }
                 }
             }
 
-            if (end != here) {
+            if (paraEnd != here) {
                 if ((fittop | fitbottom | fitdescent | fitascent) == 0) {
                     paint.getFontMetricsInt(fm);
 
@@ -575,20 +494,20 @@
                 // Log.e("text", "output rest " + here + " to " + end);
 
                 v = out(source,
-                        here, end, fitascent, fitdescent,
+                        here, paraEnd, fitascent, fitdescent,
                         fittop, fitbottom,
                         v,
                         spacingmult, spacingadd, chooseht,
-                        choosehtv, fm, tab,
-                        needMultiply, start, chdirs, dir, easy,
-                        end == bufend, includepad, trackpad,
-                        widths, start, end - start,
+                        choosehtv, fm, hasTabOrEmoji,
+                        needMultiply, paraStart, chdirs, dir, easy,
+                        paraEnd == bufend, includepad, trackpad,
+                        chs, widths, here - paraStart,
                         where, ellipsizedWidth, w, paint);
             }
 
-            start = end;
+            paraStart = paraEnd;
 
-            if (end == bufend)
+            if (paraEnd == bufend)
                 break;
         }
 
@@ -603,246 +522,13 @@
                     v,
                     spacingmult, spacingadd, null,
                     null, fm, false,
-                    needMultiply, bufend, chdirs, DEFAULT_DIR, true,
+                    needMultiply, bufend, null, DEFAULT_DIR, true,
                     true, includepad, trackpad,
-                    widths, bufstart, 0,
+                    null, null, bufstart,
                     where, ellipsizedWidth, 0, paint);
         }
     }
 
-    /**
-     * Runs the unicode bidi algorithm on the first n chars in chs, returning
-     * the char dirs in chInfo and the base line direction of the first
-     * paragraph.
-     * 
-     * XXX change result from dirs to levels
-     *  
-     * @param dir the direction flag, either DIR_REQUEST_LTR,
-     * DIR_REQUEST_RTL, DIR_REQUEST_DEFAULT_LTR, or DIR_REQUEST_DEFAULT_RTL.
-     * @param chs the text to examine
-     * @param chInfo on input, if hasInfo is true, override and other flags 
-     * representing out-of-band embedding information. On output, the generated 
-     * dirs of the text.
-     * @param n the length of the text/information in chs and chInfo
-     * @param hasInfo true if chInfo has input information, otherwise the
-     * input data in chInfo is ignored.
-     * @return the resolved direction level of the first paragraph, either
-     * DIR_LEFT_TO_RIGHT or DIR_RIGHT_TO_LEFT.
-     */
-    /* package */ static int bidi(int dir, char[] chs, byte[] chInfo, int n, 
-            boolean hasInfo) {
-        
-        AndroidCharacter.getDirectionalities(chs, chInfo, n);
-
-        /*
-         * Determine primary paragraph direction if not specified
-         */
-        if (dir != DIR_REQUEST_LTR && dir != DIR_REQUEST_RTL) {
-            // set up default
-            dir = dir >= 0 ? DIR_LEFT_TO_RIGHT : DIR_RIGHT_TO_LEFT;
-            for (int j = 0; j < n; j++) {
-                int d = chInfo[j];
-
-                if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT) {
-                    dir = DIR_LEFT_TO_RIGHT;
-                    break;
-                }
-                if (d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
-                    dir = DIR_RIGHT_TO_LEFT;
-                    break;
-                }
-            }
-        }
-
-        final byte SOR = dir == DIR_LEFT_TO_RIGHT ?
-                Character.DIRECTIONALITY_LEFT_TO_RIGHT :
-                Character.DIRECTIONALITY_RIGHT_TO_LEFT;
-
-        /*
-         * XXX Explicit overrides should go here
-         */
-
-        /*
-         * Weak type resolution
-         */
-
-        // dump(chdirs, n, "initial");
-
-        // W1 non spacing marks
-        for (int j = 0; j < n; j++) {
-            if (chInfo[j] == Character.NON_SPACING_MARK) {
-                if (j == 0)
-                    chInfo[j] = SOR;
-                else
-                    chInfo[j] = chInfo[j - 1];
-            }
-        }
-
-        // dump(chdirs, n, "W1");
-
-        // W2 european numbers
-        byte cur = SOR;
-        for (int j = 0; j < n; j++) {
-            byte d = chInfo[j];
-
-            if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
-                d == Character.DIRECTIONALITY_RIGHT_TO_LEFT ||
-                d == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
-                cur = d;
-            else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) {
-                 if (cur ==
-                    Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
-                    chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER;
-            }
-        }
-
-        // dump(chdirs, n, "W2");
-
-        // W3 arabic letters
-        for (int j = 0; j < n; j++) {
-            if (chInfo[j] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
-                chInfo[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
-        }
-
-        // dump(chdirs, n, "W3");
-
-        // W4 single separator between numbers
-        for (int j = 1; j < n - 1; j++) {
-            byte d = chInfo[j];
-            byte prev = chInfo[j - 1];
-            byte next = chInfo[j + 1];
-
-            if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) {
-                if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER &&
-                    next == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
-                    chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
-            } else if (d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) {
-                if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER &&
-                    next == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
-                    chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
-                if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER &&
-                    next == Character.DIRECTIONALITY_ARABIC_NUMBER)
-                    chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER;
-            }
-        }
-
-        // dump(chdirs, n, "W4");
-
-        // W5 european number terminators
-        boolean adjacent = false;
-        for (int j = 0; j < n; j++) {
-            byte d = chInfo[j];
-
-            if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
-                adjacent = true;
-            else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR && adjacent)
-                chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
-            else
-                adjacent = false;
-        }
-
-        //dump(chdirs, n, "W5");
-
-        // W5 european number terminators part 2,
-        // W6 separators and terminators
-        adjacent = false;
-        for (int j = n - 1; j >= 0; j--) {
-            byte d = chInfo[j];
-
-            if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
-                adjacent = true;
-            else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR) {
-                if (adjacent)
-                    chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
-                else
-                    chInfo[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS;
-            }
-            else {
-                adjacent = false;
-
-                if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR ||
-                    d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR ||
-                    d == Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR ||
-                    d == Character.DIRECTIONALITY_SEGMENT_SEPARATOR)
-                    chInfo[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS;
-            }
-        }
-
-        // dump(chdirs, n, "W6");
-
-        // W7 strong direction of european numbers
-        cur = SOR;
-        for (int j = 0; j < n; j++) {
-            byte d = chInfo[j];
-
-            if (d == SOR ||
-                d == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
-                d == Character.DIRECTIONALITY_RIGHT_TO_LEFT)
-                cur = d;
-
-            if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
-                chInfo[j] = cur;
-        }
-
-        // dump(chdirs, n, "W7");
-
-        // N1, N2 neutrals
-        cur = SOR;
-        for (int j = 0; j < n; j++) {
-            byte d = chInfo[j];
-
-            if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
-                d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
-                cur = d;
-            } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER ||
-                       d == Character.DIRECTIONALITY_ARABIC_NUMBER) {
-                cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
-            } else {
-                byte dd = SOR;
-                int k;
-
-                for (k = j + 1; k < n; k++) {
-                    dd = chInfo[k];
-
-                    if (dd == Character.DIRECTIONALITY_LEFT_TO_RIGHT ||
-                        dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) {
-                        break;
-                    }
-                    if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER ||
-                        dd == Character.DIRECTIONALITY_ARABIC_NUMBER) {
-                        dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
-                        break;
-                    }
-                }
-
-                for (int y = j; y < k; y++) {
-                    if (dd == cur)
-                        chInfo[y] = cur;
-                    else
-                        chInfo[y] = SOR;
-                }
-
-                j = k - 1;
-            }
-        }
-
-        // dump(chdirs, n, "final");
-
-        // extra: enforce that all tabs and surrogate characters go the
-        // primary direction
-        // TODO: actually do directions right for surrogates
-
-        for (int j = 0; j < n; j++) {
-            char c = chs[j];
-
-            if (c == '\t' || (c >= 0xD800 && c <= 0xDFFF)) {
-                chInfo[j] = SOR;
-            }
-        }
-        
-        return dir;
-    }
-
     private static final char FIRST_CJK = '\u2E80';
     /**
      * Returns true if the specified character is one of those specified
@@ -948,53 +634,15 @@
     }
 */
 
-    private static int getFit(TextPaint paint,
-                              TextPaint workPaint,
-                       CharSequence text, int start, int end,
-                       float wid) {
-        int high = end + 1, low = start - 1, guess;
-
-        while (high - low > 1) {
-            guess = (high + low) / 2;
-
-            if (measureText(paint, workPaint,
-                            text, start, guess, null, true, null) > wid)
-                high = guess;
-            else
-                low = guess;
-        }
-
-        if (low < start)
-            return start;
-        else
-            return low;
-    }
-
-    private static void adjustTextWidths(float[] widths, CharSequence text,
-                              int curPos, int nextPos, int actualNum) {
-        try {
-            int dstIndex = nextPos - curPos - 1;
-            for (int srcIndex = actualNum - 1; srcIndex >= 0; srcIndex--) {
-                final char c = text.charAt(dstIndex + curPos);
-                if (c >= 0xD800 && c <= 0xDFFF) {
-                    widths[dstIndex--] = 0.0f;
-                }
-                widths[dstIndex--] = widths[srcIndex];
-            }
-        } catch (IndexOutOfBoundsException e) {
-            Log.e("text", "adjust text widths failed");
-        }
-    }
-
     private int out(CharSequence text, int start, int end,
                       int above, int below, int top, int bottom, int v,
                       float spacingmult, float spacingadd,
                       LineHeightSpan[] chooseht, int[] choosehtv,
-                      Paint.FontMetricsInt fm, boolean tab,
+                      Paint.FontMetricsInt fm, boolean hasTabOrEmoji,
                       boolean needMultiply, int pstart, byte[] chdirs,
                       int dir, boolean easy, boolean last,
                       boolean includepad, boolean trackpad,
-                      float[] widths, int widstart, int widoff,
+                      char[] chs, float[] widths, int widstart,
                       TextUtils.TruncateAt ellipsize, float ellipsiswidth,
                       float textwidth, TextPaint paint) {
         int j = mLineCount;
@@ -1002,8 +650,6 @@
         int want = off + mColumns + TOP;
         int[] lines = mLines;
 
-        // Log.e("text", "line " + start + " to " + end + (last ? "===" : ""));
-
         if (want >= lines.length) {
             int nlen = ArrayUtils.idealIntArraySize(want + 1);
             int[] grow = new int[nlen];
@@ -1079,59 +725,23 @@
         lines[off + mColumns + START] = end;
         lines[off + mColumns + TOP] = v;
 
-        if (tab)
+        if (hasTabOrEmoji)
             lines[off + TAB] |= TAB_MASK;
 
-        {
-            lines[off + DIR] |= dir << DIR_SHIFT;
-
-            int cur = Character.DIRECTIONALITY_LEFT_TO_RIGHT;
-            int count = 0;
-
-            if (!easy) {
-                for (int k = start; k < end; k++) {
-                    if (chdirs[k - pstart] != cur) {
-                        count++;
-                        cur = chdirs[k - pstart];
-                    }
-                }
-            }
-
-            Directions linedirs;
-
-            if (count == 0) {
-                linedirs = DIRS_ALL_LEFT_TO_RIGHT;
-            } else {
-                short[] ld = new short[count + 1];
-
-                cur = Character.DIRECTIONALITY_LEFT_TO_RIGHT;
-                count = 0;
-                int here = start;
-
-                for (int k = start; k < end; k++) {
-                    if (chdirs[k - pstart] != cur) {
-                        // XXX check to make sure we don't
-                        //     overflow short
-                        ld[count++] = (short) (k - here);
-                        cur = chdirs[k - pstart];
-                        here = k;
-                    }
-                }
-
-                ld[count] = (short) (end - here);
-
-                if (count == 1 && ld[0] == 0) {
-                    linedirs = DIRS_ALL_RIGHT_TO_LEFT;
-                } else {
-                    linedirs = new Directions(ld);
-                }
-            }
-
+        lines[off + DIR] |= dir << DIR_SHIFT;
+        Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT;
+        // easy means all chars < the first RTL, so no emoji, no nothing
+        // XXX a run with no text or all spaces is easy but might be an empty
+        // RTL paragraph.  Make sure easy is false if this is the case.
+        if (easy) {
             mLineDirections[j] = linedirs;
+        } else {
+            mLineDirections[j] = AndroidBidi.directions(dir, chdirs, widstart, chs,
+                    widstart, end - start);
 
             // If ellipsize is in marquee mode, do not apply ellipsis on the first line
             if (ellipsize != null && (ellipsize != TextUtils.TruncateAt.MARQUEE || j != 0)) {
-                calculateEllipsis(start, end, widths, widstart, widoff,
+                calculateEllipsis(start, end, widths, widstart,
                                   ellipsiswidth, ellipsize, j,
                                   textwidth, paint);
             }
@@ -1142,7 +752,7 @@
     }
 
     private void calculateEllipsis(int linestart, int lineend,
-                                   float[] widths, int widstart, int widoff,
+                                   float[] widths, int widstart,
                                    float avail, TextUtils.TruncateAt where,
                                    int line, float textwidth, TextPaint paint) {
         int len = lineend - linestart;
@@ -1162,7 +772,7 @@
             int i;
 
             for (i = len; i >= 0; i--) {
-                float w = widths[i - 1 + linestart - widstart + widoff];
+                float w = widths[i - 1 + linestart - widstart];
 
                 if (w + sum + ellipsiswid > avail) {
                     break;
@@ -1178,7 +788,7 @@
             int i;
 
             for (i = 0; i < len; i++) {
-                float w = widths[i + linestart - widstart + widoff];
+                float w = widths[i + linestart - widstart];
 
                 if (w + sum + ellipsiswid > avail) {
                     break;
@@ -1195,7 +805,7 @@
 
             float ravail = (avail - ellipsiswid) / 2;
             for (right = len; right >= 0; right--) {
-                float w = widths[right - 1 + linestart - widstart + widoff];
+                float w = widths[right - 1 + linestart - widstart];
 
                 if (w + rsum > ravail) {
                     break;
@@ -1206,7 +816,7 @@
 
             float lavail = avail - ellipsiswid - rsum;
             for (left = 0; left < right; left++) {
-                float w = widths[left + linestart - widstart + widoff];
+                float w = widths[left + linestart - widstart];
 
                 if (w + lsum > lavail) {
                     break;
@@ -1223,7 +833,7 @@
         mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;
     }
 
-    // Override the baseclass so we can directly access our members,
+    // Override the base class so we can directly access our members,
     // rather than relying on member functions.
     // The logic mirrors that of Layout.getLineForVertical
     // FIXME: It may be faster to do a linear search for layouts without many lines.
@@ -1252,11 +862,11 @@
     }
 
     public int getLineTop(int line) {
-        return mLines[mColumns * line + TOP];    
+        return mLines[mColumns * line + TOP];
     }
 
     public int getLineDescent(int line) {
-        return mLines[mColumns * line + DESCENT];   
+        return mLines[mColumns * line + DESCENT];
     }
 
     public int getLineStart(int line) {
@@ -1329,13 +939,11 @@
     private static final int DIR_SHIFT  = 30;
     private static final int TAB_MASK   = 0x20000000;
 
-    private static final char FIRST_RIGHT_TO_LEFT = '\u0590';
+    private static final int TAB_INCREMENT = 20; // same as Layout, but that's private
 
     /*
-     * These are reused across calls to generate()
+     * This is reused across calls to generate()
      */
-    private byte[] mChdirs;
-    private char[] mChs;
-    private float[] mWidths;
+    private MeasuredText mMeasured;
     private Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
 }
diff --git a/core/java/android/text/Styled.java b/core/java/android/text/Styled.java
deleted file mode 100755
index 13cc42c..0000000
--- a/core/java/android/text/Styled.java
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
- * 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.graphics.Canvas;
-import android.graphics.Paint;
-import android.text.style.CharacterStyle;
-import android.text.style.MetricAffectingSpan;
-import android.text.style.ReplacementSpan;
-
-/**
- * This class provides static methods for drawing and measuring styled text,
- * like {@link android.text.Spanned} object with
- * {@link android.text.style.ReplacementSpan}.
- *
- * @hide
- */
-public class Styled
-{
-    /**
-     * Draws and/or measures a uniform run of text on a single line. No span of
-     * interest should start or end in the middle of this run (if not
-     * drawing, character spans that don't affect metrics can be ignored).
-     * Neither should the run direction change in the middle of the run.
-     *
-     * <p>The x position is the leading edge of the text. In a right-to-left
-     * paragraph, this will be to the right of the text to be drawn. Paint
-     * should not have an Align value other than LEFT or positioning will get
-     * confused.
-     *
-     * <p>On return, workPaint will reflect the original paint plus any
-     * modifications made by character styles on the run.
-     *
-     * <p>The returned width is signed and will be < 0 if the paragraph
-     * direction is right-to-left.
-     */
-    private static float drawUniformRun(Canvas canvas,
-                              Spanned text, int start, int end,
-                              int dir, boolean runIsRtl,
-                              float x, int top, int y, int bottom,
-                              Paint.FontMetricsInt fmi,
-                              TextPaint paint,
-                              TextPaint workPaint,
-                              boolean needWidth) {
-
-        boolean haveWidth = false;
-        float ret = 0;
-        CharacterStyle[] spans = text.getSpans(start, end, CharacterStyle.class);
-
-        ReplacementSpan replacement = null;
-
-        // XXX: This shouldn't be modifying paint, only workPaint.
-        // However, the members belonging to TextPaint should have default
-        // values anyway.  Better to ensure this in the Layout constructor.
-        paint.bgColor = 0;
-        paint.baselineShift = 0;
-        workPaint.set(paint);
-
-		if (spans.length > 0) {
-			for (int i = 0; i < spans.length; i++) {
-				CharacterStyle span = spans[i];
-
-				if (span instanceof ReplacementSpan) {
-					replacement = (ReplacementSpan)span;
-				}
-				else {
-					span.updateDrawState(workPaint);
-				}
-			}
-		}
-
-        if (replacement == null) {
-            CharSequence tmp;
-            int tmpstart, tmpend;
-
-            if (runIsRtl) {
-                tmp = TextUtils.getReverse(text, start, end);
-                tmpstart = 0;
-                // XXX: assumes getReverse doesn't change the length of the text
-                tmpend = end - start;
-            } else {
-                tmp = text;
-                tmpstart = start;
-                tmpend = end;
-            }
-
-            if (fmi != null) {
-                workPaint.getFontMetricsInt(fmi);
-            }
-
-            if (canvas != null) {
-                if (workPaint.bgColor != 0) {
-                    int c = workPaint.getColor();
-                    Paint.Style s = workPaint.getStyle();
-                    workPaint.setColor(workPaint.bgColor);
-                    workPaint.setStyle(Paint.Style.FILL);
-
-                    if (!haveWidth) {
-                        ret = workPaint.measureText(tmp, tmpstart, tmpend);
-                        haveWidth = true;
-                    }
-
-                    if (dir == Layout.DIR_RIGHT_TO_LEFT)
-                        canvas.drawRect(x - ret, top, x, bottom, workPaint);
-                    else
-                        canvas.drawRect(x, top, x + ret, bottom, workPaint);
-
-                    workPaint.setStyle(s);
-                    workPaint.setColor(c);
-                }
-
-                if (dir == Layout.DIR_RIGHT_TO_LEFT) {
-                    if (!haveWidth) {
-                        ret = workPaint.measureText(tmp, tmpstart, tmpend);
-                        haveWidth = true;
-                    }
-
-                    canvas.drawText(tmp, tmpstart, tmpend,
-                                    x - ret, y + workPaint.baselineShift, workPaint);
-                } else {
-                    if (needWidth) {
-                        if (!haveWidth) {
-                            ret = workPaint.measureText(tmp, tmpstart, tmpend);
-                            haveWidth = true;
-                        }
-                    }
-
-                    canvas.drawText(tmp, tmpstart, tmpend,
-                                    x, y + workPaint.baselineShift, workPaint);
-                }
-            } else {
-                if (needWidth && !haveWidth) {
-                    ret = workPaint.measureText(tmp, tmpstart, tmpend);
-                    haveWidth = true;
-                }
-            }
-        } else {
-            ret = replacement.getSize(workPaint, text, start, end, fmi);
-
-            if (canvas != null) {
-                if (dir == Layout.DIR_RIGHT_TO_LEFT)
-                    replacement.draw(canvas, text, start, end,
-                                     x - ret, top, y, bottom, workPaint);
-                else
-                    replacement.draw(canvas, text, start, end,
-                                     x, top, y, bottom, workPaint);
-            }
-        }
-
-        if (dir == Layout.DIR_RIGHT_TO_LEFT)
-            return -ret;
-        else
-            return ret;
-    }
-
-    /**
-     * Returns the advance widths for a uniform left-to-right run of text with
-     * no style changes in the middle of the run. If any style is replacement
-     * text, the first character will get the width of the replacement and the
-     * remaining characters will get a width of 0.
-     * 
-     * @param paint the paint, will not be modified
-     * @param workPaint a paint to modify; on return will reflect the original
-     *        paint plus the effect of all spans on the run
-     * @param text the text
-     * @param start the start of the run
-     * @param end the limit of the run
-     * @param widths array to receive the advance widths of the characters. Must
-     *        be at least a large as (end - start).
-     * @param fmi FontMetrics information; can be null
-     * @return the actual number of widths returned
-     */
-    public static int getTextWidths(TextPaint paint,
-                                    TextPaint workPaint,
-                                    Spanned text, int start, int end,
-                                    float[] widths, Paint.FontMetricsInt fmi) {
-        MetricAffectingSpan[] spans =
-            text.getSpans(start, end, MetricAffectingSpan.class);
-
-		ReplacementSpan replacement = null;
-        workPaint.set(paint);
-		
-		for (int i = 0; i < spans.length; i++) {
-			MetricAffectingSpan span = spans[i];
-			if (span instanceof ReplacementSpan) {
-				replacement = (ReplacementSpan)span;
-			}
-			else {
-				span.updateMeasureState(workPaint);
-			}
-		}
-	
-        int result;
-        if (replacement == null) {
-            workPaint.getFontMetricsInt(fmi);
-            result = workPaint.getTextWidths(text, start, end, widths);
-        } else {
-            int wid = replacement.getSize(workPaint, text, start, end, fmi);
-
-            if (end > start) {
-                widths[0] = wid;
-                for (int i = start + 1; i < end; i++)
-                    widths[i - start] = 0;
-            }
-            result = end - start;
-        }
-        return result;
-    }
-
-    /**
-     * Renders and/or measures a directional run of text on a single line.
-     * Unlike {@link #drawUniformRun}, this can render runs that cross style
-     * boundaries.  Returns the signed advance width, if requested.
-     *
-     * <p>The x position is the leading edge of the text. In a right-to-left
-     * paragraph, this will be to the right of the text to be drawn. Paint
-     * should not have an Align value other than LEFT or positioning will get
-     * confused.
-     *
-     * <p>This optimizes for unstyled text and so workPaint might not be
-     * modified by this call.
-     *
-     * <p>The returned advance width will be < 0 if the paragraph
-     * direction is right-to-left.
-     */
-    private static float drawDirectionalRun(Canvas canvas,
-                                 CharSequence text, int start, int end,
-                                 int dir, boolean runIsRtl,
-                                 float x, int top, int y, int bottom,
-                                 Paint.FontMetricsInt fmi,
-                                 TextPaint paint,
-                                 TextPaint workPaint,
-                                 boolean needWidth) {
-
-        // XXX: It looks like all calls to this API match dir and runIsRtl, so
-        // having both parameters is redundant and confusing.
-
-        // fast path for unstyled text
-        if (!(text instanceof Spanned)) {
-            float ret = 0;
-
-            if (runIsRtl) {
-                CharSequence tmp = TextUtils.getReverse(text, start, end);
-                // XXX: this assumes getReverse doesn't tweak the length of
-                // the text
-                int tmpend = end - start;
-
-                if (canvas != null || needWidth)
-                    ret = paint.measureText(tmp, 0, tmpend);
-
-                if (canvas != null)
-                    canvas.drawText(tmp, 0, tmpend,
-                                    x - ret, y, paint);
-            } else {
-                if (needWidth)
-                    ret = paint.measureText(text, start, end);
-
-                if (canvas != null)
-                    canvas.drawText(text, start, end, x, y, paint);
-            }
-
-            if (fmi != null) {
-                paint.getFontMetricsInt(fmi);
-            }
-
-            return ret * dir;   // Layout.DIR_RIGHT_TO_LEFT == -1
-        }
-        
-        float ox = x;
-        int minAscent = 0, maxDescent = 0, minTop = 0, maxBottom = 0;
-
-        Spanned sp = (Spanned) text;
-        Class<?> division;
-
-        if (canvas == null)
-            division = MetricAffectingSpan.class;
-        else
-            division = CharacterStyle.class;
-
-        int next;
-        for (int i = start; i < end; i = next) {
-            next = sp.nextSpanTransition(i, end, division);
-
-            // XXX: if dir and runIsRtl were not the same, this would draw
-            // spans in the wrong order, but no one appears to call it this
-            // way.
-            x += drawUniformRun(canvas, sp, i, next, dir, runIsRtl,
-                  x, top, y, bottom, fmi, paint, workPaint,
-                  needWidth || next != end);
-
-            if (fmi != null) {
-                if (fmi.ascent < minAscent)
-                    minAscent = fmi.ascent;
-                if (fmi.descent > maxDescent)
-                    maxDescent = fmi.descent;
-
-                if (fmi.top < minTop)
-                    minTop = fmi.top;
-                if (fmi.bottom > maxBottom)
-                    maxBottom = fmi.bottom;
-            }
-        }
-
-        if (fmi != null) {
-            if (start == end) {
-                paint.getFontMetricsInt(fmi);
-            } else {
-                fmi.ascent = minAscent;
-                fmi.descent = maxDescent;
-                fmi.top = minTop;
-                fmi.bottom = maxBottom;
-            }
-        }
-
-        return x - ox;
-    }
-
-    /**
-     * Draws a unidirectional run of text on a single line, and optionally
-     * returns the signed advance.  Unlike drawDirectionalRun, the paragraph
-     * direction and run direction can be different.
-     */
-    /* package */ static float drawText(Canvas canvas,
-                                       CharSequence text, int start, int end,
-                                       int dir, boolean runIsRtl,
-                                       float x, int top, int y, int bottom,
-                                       TextPaint paint,
-                                       TextPaint workPaint,
-                                       boolean needWidth) {
-        // XXX this logic is (dir == DIR_LEFT_TO_RIGHT) == runIsRtl
-        if ((dir == Layout.DIR_RIGHT_TO_LEFT && !runIsRtl) ||
-            (runIsRtl && dir == Layout.DIR_LEFT_TO_RIGHT)) {
-            // TODO: this needs the real direction
-            float ch = drawDirectionalRun(null, text, start, end,
-                    Layout.DIR_LEFT_TO_RIGHT, false, 0, 0, 0, 0, null, paint,
-                    workPaint, true);
-
-            ch *= dir;  // DIR_RIGHT_TO_LEFT == -1
-            drawDirectionalRun(canvas, text, start, end, -dir,
-                    runIsRtl, x + ch, top, y, bottom, null, paint,
-                    workPaint, true);
-
-            return ch;
-        }
-
-        return drawDirectionalRun(canvas, text, start, end, dir, runIsRtl,
-                       x, top, y, bottom, null, paint, workPaint,
-                       needWidth);
-    }
-    
-    /**
-     * Draws a run of text on a single line, with its
-     * origin at (x,y), in the specified Paint. The origin is interpreted based
-     * on the Align setting in the Paint.
-     *
-     * This method considers style information in the text (e.g. even when text
-     * is an instance of {@link android.text.Spanned}, this method correctly
-     * draws the text). See also
-     * {@link android.graphics.Canvas#drawText(CharSequence, int, int, float,
-     * float, Paint)} and
-     * {@link android.graphics.Canvas#drawRect(float, float, float, float,
-     * Paint)}.
-     * 
-     * @param canvas The target canvas
-     * @param text The text to be drawn
-     * @param start The index of the first character in text to draw
-     * @param end (end - 1) is the index of the last character in text to draw
-     * @param direction The direction of the text. This must be
-     *        {@link android.text.Layout#DIR_LEFT_TO_RIGHT} or
-     *        {@link android.text.Layout#DIR_RIGHT_TO_LEFT}.
-     * @param x The x-coordinate of origin for where to draw the text
-     * @param top The top side of the rectangle to be drawn
-     * @param y The y-coordinate of origin for where to draw the text
-     * @param bottom The bottom side of the rectangle to be drawn
-     * @param paint The main {@link TextPaint} object.
-     * @param workPaint The {@link TextPaint} object used for temporal
-     *        workspace.
-     * @param needWidth If true, this method returns the width of drawn text
-     * @return Width of the drawn text if needWidth is true
-     */
-    public static float drawText(Canvas canvas,
-                                 CharSequence text, int start, int end,
-                                 int direction,
-                                 float x, int top, int y, int bottom,
-                                 TextPaint paint,
-                                 TextPaint workPaint,
-                                 boolean needWidth) {
-        // For safety.
-        direction = direction >= 0 ? Layout.DIR_LEFT_TO_RIGHT
-                : Layout.DIR_RIGHT_TO_LEFT;
-
-        // Hide runIsRtl parameter since it is meaningless for external
-        // developers.
-        // XXX: the runIsRtl probably ought to be the same as direction, then
-        // this could draw rtl text.
-        return drawText(canvas, text, start, end, direction, false,
-                        x, top, y, bottom, paint, workPaint, needWidth);
-    }
-    
-    /**
-     * Returns the width of a run of left-to-right text on a single line,
-     * considering style information in the text (e.g. even when text is an
-     * instance of {@link android.text.Spanned}, this method correctly measures
-     * the width of the text).
-     * 
-     * @param paint the main {@link TextPaint} object; will not be modified
-     * @param workPaint the {@link TextPaint} object available for modification;
-     *        will not necessarily be used
-     * @param text the text to measure
-     * @param start the index of the first character to start measuring
-     * @param end 1 beyond the index of the last character to measure
-     * @param fmi FontMetrics information; can be null
-     * @return The width of the text
-     */
-    public static float measureText(TextPaint paint,
-                                    TextPaint workPaint,
-                                    CharSequence text, int start, int end,
-                                    Paint.FontMetricsInt fmi) {
-        return drawDirectionalRun(null, text, start, end,
-                       Layout.DIR_LEFT_TO_RIGHT, false,
-                       0, 0, 0, 0, fmi, paint, workPaint, true);
-    }
-}
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
new file mode 100644
index 0000000..2f7482c
--- /dev/null
+++ b/core/java/android/text/TextLine.java
@@ -0,0 +1,940 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import com.android.internal.util.ArrayUtils;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
+import android.graphics.RectF;
+import android.text.Layout.Directions;
+import android.text.Layout.TabStops;
+import android.text.style.CharacterStyle;
+import android.text.style.MetricAffectingSpan;
+import android.text.style.ReplacementSpan;
+import android.util.Log;
+
+/**
+ * Represents a line of styled text, for measuring in visual order and
+ * for rendering.
+ *
+ * <p>Get a new instance using obtain(), and when finished with it, return it
+ * to the pool using recycle().
+ *
+ * <p>Call set to prepare the instance for use, then either draw, measure,
+ * metrics, or caretToLeftRightOf.
+ *
+ * @hide
+ */
+class TextLine {
+    private TextPaint mPaint;
+    private CharSequence mText;
+    private int mStart;
+    private int mLen;
+    private int mDir;
+    private Directions mDirections;
+    private boolean mHasTabs;
+    private TabStops mTabs;
+    private char[] mChars;
+    private boolean mCharsValid;
+    private Spanned mSpanned;
+    private final TextPaint mWorkPaint = new TextPaint();
+
+    private static TextLine[] cached = new TextLine[3];
+
+    /**
+     * Returns a new TextLine from the shared pool.
+     *
+     * @return an uninitialized TextLine
+     */
+    static TextLine obtain() {
+        TextLine tl;
+        synchronized (cached) {
+            for (int i = cached.length; --i >= 0;) {
+                if (cached[i] != null) {
+                    tl = cached[i];
+                    cached[i] = null;
+                    return tl;
+                }
+            }
+        }
+        tl = new TextLine();
+        Log.v("TLINE", "new: " + tl);
+        return tl;
+    }
+
+    /**
+     * Puts a TextLine back into the shared pool. Do not use this TextLine once
+     * it has been returned.
+     * @param tl the textLine
+     * @return null, as a convenience from clearing references to the provided
+     * TextLine
+     */
+    static TextLine recycle(TextLine tl) {
+        tl.mText = null;
+        tl.mPaint = null;
+        tl.mDirections = null;
+        if (tl.mLen < 250) {
+            synchronized(cached) {
+                for (int i = 0; i < cached.length; ++i) {
+                    if (cached[i] == null) {
+                        cached[i] = tl;
+                        break;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Initializes a TextLine and prepares it for use.
+     *
+     * @param paint the base paint for the line
+     * @param text the text, can be Styled
+     * @param start the start of the line relative to the text
+     * @param limit the limit of the line relative to the text
+     * @param dir the paragraph direction of this line
+     * @param directions the directions information of this line
+     * @param hasTabs true if the line might contain tabs or emoji
+     * @param tabStops the tabStops. Can be null.
+     */
+    void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
+            Directions directions, boolean hasTabs, TabStops tabStops) {
+        mPaint = paint;
+        mText = text;
+        mStart = start;
+        mLen = limit - start;
+        mDir = dir;
+        mDirections = directions;
+        mHasTabs = hasTabs;
+        mSpanned = null;
+
+        boolean hasReplacement = false;
+        if (text instanceof Spanned) {
+            mSpanned = (Spanned) text;
+            hasReplacement = mSpanned.getSpans(start, limit,
+                    ReplacementSpan.class).length > 0;
+        }
+
+        mCharsValid = hasReplacement || hasTabs ||
+            directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
+
+        if (mCharsValid) {
+            if (mChars == null || mChars.length < mLen) {
+                mChars = new char[ArrayUtils.idealCharArraySize(mLen)];
+            }
+            TextUtils.getChars(text, start, limit, mChars, 0);
+            if (hasReplacement) {
+                // Handle these all at once so we don't have to do it as we go.
+                // Replace the first character of each replacement run with the
+                // object-replacement character and the remainder with zero width
+                // non-break space aka BOM.  Cursor movement code skips these
+                // zero-width characters.
+                char[] chars = mChars;
+                for (int i = start, inext; i < limit; i = inext) {
+                    inext = mSpanned.nextSpanTransition(i, limit,
+                            ReplacementSpan.class);
+                    if (mSpanned.getSpans(i, inext, ReplacementSpan.class)
+                            .length > 0) { // transition into a span
+                        chars[i - start] = '\ufffc';
+                        for (int j = i - start + 1, e = inext - start; j < e; ++j) {
+                            chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip
+                        }
+                    }
+                }
+            }
+        }
+        mTabs = tabStops;
+    }
+
+    /**
+     * Renders the TextLine.
+     *
+     * @param c the canvas to render on
+     * @param x the leading margin position
+     * @param top the top of the line
+     * @param y the baseline
+     * @param bottom the bottom of the line
+     */
+    void draw(Canvas c, float x, int top, int y, int bottom) {
+        if (!mHasTabs) {
+            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
+                drawRun(c, 0, 0, mLen, false, x, top, y, bottom, false);
+                return;
+            }
+            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
+                drawRun(c, 0, 0, mLen, true, x, top, y, bottom, false);
+                return;
+            }
+        }
+
+        float h = 0;
+        int[] runs = mDirections.mDirections;
+        RectF emojiRect = null;
+
+        int lastRunIndex = runs.length - 2;
+        for (int i = 0; i < runs.length; i += 2) {
+            int runStart = runs[i];
+            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
+            if (runLimit > mLen) {
+                runLimit = mLen;
+            }
+            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
+
+            int segstart = runStart;
+            char[] chars = mChars;
+            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
+                int codept = 0;
+                Bitmap bm = null;
+
+                if (mHasTabs && j < runLimit) {
+                    codept = mChars[j];
+                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
+                        codept = Character.codePointAt(mChars, j);
+                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
+                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
+                        } else if (codept > 0xffff) {
+                            ++j;
+                            continue;
+                        }
+                    }
+                }
+
+                if (j == runLimit || codept == '\t' || bm != null) {
+                    h += drawRun(c, i, segstart, j, runIsRtl, x+h, top, y, bottom,
+                            i != lastRunIndex || j != mLen);
+
+                    if (codept == '\t') {
+                        h = mDir * nextTab(h * mDir);
+                    } else if (bm != null) {
+                        float bmAscent = ascent(j);
+                        float bitmapHeight = bm.getHeight();
+                        float scale = -bmAscent / bitmapHeight;
+                        float width = bm.getWidth() * scale;
+
+                        if (emojiRect == null) {
+                            emojiRect = new RectF();
+                        }
+                        emojiRect.set(x + h, y + bmAscent,
+                                x + h + width, y);
+                        c.drawBitmap(bm, null, emojiRect, mPaint);
+                        h += width;
+                        j++;
+                    }
+                    segstart = j + 1;
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns metrics information for the entire line.
+     *
+     * @param fmi receives font metrics information, can be null
+     * @return the signed width of the line
+     */
+    float metrics(FontMetricsInt fmi) {
+        return measure(mLen, false, fmi);
+    }
+
+    /**
+     * Returns information about a position on the line.
+     *
+     * @param offset the line-relative character offset, between 0 and the
+     * line length, inclusive
+     * @param trailing true to measure the trailing edge of the character
+     * before offset, false to measure the leading edge of the character
+     * at offset.
+     * @param fmi receives metrics information about the requested
+     * character, can be null.
+     * @return the signed offset from the leading margin to the requested
+     * character edge.
+     */
+    float measure(int offset, boolean trailing, FontMetricsInt fmi) {
+        int target = trailing ? offset - 1 : offset;
+        if (target < 0) {
+            return 0;
+        }
+
+        float h = 0;
+
+        if (!mHasTabs) {
+            if (mDirections == Layout.DIRS_ALL_LEFT_TO_RIGHT) {
+                return measureRun(0, 0, offset, mLen, false, fmi);
+            }
+            if (mDirections == Layout.DIRS_ALL_RIGHT_TO_LEFT) {
+                return measureRun(0, 0, offset, mLen, true, fmi);
+            }
+        }
+
+        char[] chars = mChars;
+        int[] runs = mDirections.mDirections;
+        for (int i = 0; i < runs.length; i += 2) {
+            int runStart = runs[i];
+            int runLimit = runStart + (runs[i+1] & Layout.RUN_LENGTH_MASK);
+            if (runLimit > mLen) {
+                runLimit = mLen;
+            }
+            boolean runIsRtl = (runs[i+1] & Layout.RUN_RTL_FLAG) != 0;
+
+            int segstart = runStart;
+            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
+                int codept = 0;
+                Bitmap bm = null;
+
+                if (mHasTabs && j < runLimit) {
+                    codept = chars[j];
+                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
+                        codept = Character.codePointAt(chars, j);
+                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
+                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
+                        } else if (codept > 0xffff) {
+                            ++j;
+                            continue;
+                        }
+                    }
+                }
+
+                if (j == runLimit || codept == '\t' || bm != null) {
+                    boolean inSegment = target >= segstart && target < j;
+
+                    boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
+                    if (inSegment && advance) {
+                        return h += measureRun(i, segstart, offset, j, runIsRtl, fmi);
+                    }
+
+                    float w = measureRun(i, segstart, j, j, runIsRtl, fmi);
+                    h += advance ? w : -w;
+
+                    if (inSegment) {
+                        return h += measureRun(i, segstart, offset, j, runIsRtl, null);
+                    }
+
+                    if (codept == '\t') {
+                        if (offset == j) {
+                            return h;
+                        }
+                        h = mDir * nextTab(h * mDir);
+                        if (target == j) {
+                            return h;
+                        }
+                    }
+
+                    if (bm != null) {
+                        float bmAscent = ascent(j);
+                        float wid = bm.getWidth() * -bmAscent / bm.getHeight();
+                        h += mDir * wid;
+                        j++;
+                    }
+
+                    segstart = j + 1;
+                }
+            }
+        }
+
+        return h;
+    }
+
+    /**
+     * Draws a unidirectional (but possibly multi-styled) run of text.
+     *
+     * @param c the canvas to draw on
+     * @param runIndex the index of this directional run
+     * @param start the line-relative start
+     * @param limit the line-relative limit
+     * @param runIsRtl true if the run is right-to-left
+     * @param x the position of the run that is closest to the leading margin
+     * @param top the top of the line
+     * @param y the baseline
+     * @param bottom the bottom of the line
+     * @param needWidth true if the width value is required.
+     * @return the signed width of the run, based on the paragraph direction.
+     * Only valid if needWidth is true.
+     */
+    private float drawRun(Canvas c, int runIndex, int start,
+            int limit, boolean runIsRtl, float x, int top, int y, int bottom,
+            boolean needWidth) {
+
+        if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
+            float w = -measureRun(runIndex, start, limit, limit, runIsRtl, null);
+            handleRun(runIndex, start, limit, limit, runIsRtl, c, x + w, top,
+                    y, bottom, null, false);
+            return w;
+        }
+
+        return handleRun(runIndex, start, limit, limit, runIsRtl, c, x, top,
+                y, bottom, null, needWidth);
+    }
+
+    /**
+     * Measures a unidirectional (but possibly multi-styled) run of text.
+     *
+     * @param runIndex the run index
+     * @param start the line-relative start of the run
+     * @param offset the offset to measure to, between start and limit inclusive
+     * @param limit the line-relative limit of the run
+     * @param runIsRtl true if the run is right-to-left
+     * @param fmi receives metrics information about the requested
+     * run, can be null.
+     * @return the signed width from the start of the run to the leading edge
+     * of the character at offset, based on the run (not paragraph) direction
+     */
+    private float measureRun(int runIndex, int start,
+            int offset, int limit, boolean runIsRtl, FontMetricsInt fmi) {
+        return handleRun(runIndex, start, offset, limit, runIsRtl, null,
+                0, 0, 0, 0, fmi, true);
+    }
+
+    /**
+     * Walk the cursor through this line, skipping conjuncts and
+     * zero-width characters.
+     *
+     * <p>This function cannot properly walk the cursor off the ends of the line
+     * since it does not know about any shaping on the previous/following line
+     * that might affect the cursor position. Callers must either avoid these
+     * situations or handle the result specially.
+     *
+     * @param cursor the starting position of the cursor, between 0 and the
+     * length of the line, inclusive
+     * @param toLeft true if the caret is moving to the left.
+     * @return the new offset.  If it is less than 0 or greater than the length
+     * of the line, the previous/following line should be examined to get the
+     * actual offset.
+     */
+    int getOffsetToLeftRightOf(int cursor, boolean toLeft) {
+        // 1) The caret marks the leading edge of a character. The character
+        // logically before it might be on a different level, and the active caret
+        // position is on the character at the lower level. If that character
+        // was the previous character, the caret is on its trailing edge.
+        // 2) Take this character/edge and move it in the indicated direction.
+        // This gives you a new character and a new edge.
+        // 3) This position is between two visually adjacent characters.  One of
+        // these might be at a lower level.  The active position is on the
+        // character at the lower level.
+        // 4) If the active position is on the trailing edge of the character,
+        // the new caret position is the following logical character, else it
+        // is the character.
+
+        int lineStart = 0;
+        int lineEnd = mLen;
+        boolean paraIsRtl = mDir == -1;
+        int[] runs = mDirections.mDirections;
+
+        int runIndex, runLevel = 0, runStart = lineStart, runLimit = lineEnd, newCaret = -1;
+        boolean trailing = false;
+
+        if (cursor == lineStart) {
+            runIndex = -2;
+        } else if (cursor == lineEnd) {
+            runIndex = runs.length;
+        } else {
+          // First, get information about the run containing the character with
+          // the active caret.
+          for (runIndex = 0; runIndex < runs.length; runIndex += 2) {
+            runStart = lineStart + runs[runIndex];
+            if (cursor >= runStart) {
+              runLimit = runStart + (runs[runIndex+1] & Layout.RUN_LENGTH_MASK);
+              if (runLimit > lineEnd) {
+                  runLimit = lineEnd;
+              }
+              if (cursor < runLimit) {
+                runLevel = (runs[runIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
+                    Layout.RUN_LEVEL_MASK;
+                if (cursor == runStart) {
+                  // The caret is on a run boundary, see if we should
+                  // use the position on the trailing edge of the previous
+                  // logical character instead.
+                  int prevRunIndex, prevRunLevel, prevRunStart, prevRunLimit;
+                  int pos = cursor - 1;
+                  for (prevRunIndex = 0; prevRunIndex < runs.length; prevRunIndex += 2) {
+                    prevRunStart = lineStart + runs[prevRunIndex];
+                    if (pos >= prevRunStart) {
+                      prevRunLimit = prevRunStart +
+                          (runs[prevRunIndex+1] & Layout.RUN_LENGTH_MASK);
+                      if (prevRunLimit > lineEnd) {
+                          prevRunLimit = lineEnd;
+                      }
+                      if (pos < prevRunLimit) {
+                        prevRunLevel = (runs[prevRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT)
+                            & Layout.RUN_LEVEL_MASK;
+                        if (prevRunLevel < runLevel) {
+                          // Start from logically previous character.
+                          runIndex = prevRunIndex;
+                          runLevel = prevRunLevel;
+                          runStart = prevRunStart;
+                          runLimit = prevRunLimit;
+                          trailing = true;
+                          break;
+                        }
+                      }
+                    }
+                  }
+                }
+                break;
+              }
+            }
+          }
+
+          // caret might be == lineEnd.  This is generally a space or paragraph
+          // separator and has an associated run, but might be the end of
+          // text, in which case it doesn't.  If that happens, we ran off the
+          // end of the run list, and runIndex == runs.length.  In this case,
+          // we are at a run boundary so we skip the below test.
+          if (runIndex != runs.length) {
+              boolean runIsRtl = (runLevel & 0x1) != 0;
+              boolean advance = toLeft == runIsRtl;
+              if (cursor != (advance ? runLimit : runStart) || advance != trailing) {
+                  // Moving within or into the run, so we can move logically.
+                  newCaret = getOffsetBeforeAfter(runIndex, runStart, runLimit,
+                          runIsRtl, cursor, advance);
+                  // If the new position is internal to the run, we're at the strong
+                  // position already so we're finished.
+                  if (newCaret != (advance ? runLimit : runStart)) {
+                      return newCaret;
+                  }
+              }
+          }
+        }
+
+        // If newCaret is -1, we're starting at a run boundary and crossing
+        // into another run. Otherwise we've arrived at a run boundary, and
+        // need to figure out which character to attach to.  Note we might
+        // need to run this twice, if we cross a run boundary and end up at
+        // another run boundary.
+        while (true) {
+          boolean advance = toLeft == paraIsRtl;
+          int otherRunIndex = runIndex + (advance ? 2 : -2);
+          if (otherRunIndex >= 0 && otherRunIndex < runs.length) {
+            int otherRunStart = lineStart + runs[otherRunIndex];
+            int otherRunLimit = otherRunStart +
+            (runs[otherRunIndex+1] & Layout.RUN_LENGTH_MASK);
+            if (otherRunLimit > lineEnd) {
+                otherRunLimit = lineEnd;
+            }
+            int otherRunLevel = (runs[otherRunIndex+1] >>> Layout.RUN_LEVEL_SHIFT) &
+                Layout.RUN_LEVEL_MASK;
+            boolean otherRunIsRtl = (otherRunLevel & 1) != 0;
+
+            advance = toLeft == otherRunIsRtl;
+            if (newCaret == -1) {
+                newCaret = getOffsetBeforeAfter(otherRunIndex, otherRunStart,
+                        otherRunLimit, otherRunIsRtl,
+                        advance ? otherRunStart : otherRunLimit, advance);
+                if (newCaret == (advance ? otherRunLimit : otherRunStart)) {
+                    // Crossed and ended up at a new boundary,
+                    // repeat a second and final time.
+                    runIndex = otherRunIndex;
+                    runLevel = otherRunLevel;
+                    continue;
+                }
+                break;
+            }
+
+            // The new caret is at a boundary.
+            if (otherRunLevel < runLevel) {
+              // The strong character is in the other run.
+              newCaret = advance ? otherRunStart : otherRunLimit;
+            }
+            break;
+          }
+
+          if (newCaret == -1) {
+              // We're walking off the end of the line.  The paragraph
+              // level is always equal to or lower than any internal level, so
+              // the boundaries get the strong caret.
+              newCaret = advance ? mLen + 1 : -1;
+              break;
+          }
+
+          // Else we've arrived at the end of the line.  That's a strong position.
+          // We might have arrived here by crossing over a run with no internal
+          // breaks and dropping out of the above loop before advancing one final
+          // time, so reset the caret.
+          // Note, we use '<=' below to handle a situation where the only run
+          // on the line is a counter-directional run.  If we're not advancing,
+          // we can end up at the 'lineEnd' position but the caret we want is at
+          // the lineStart.
+          if (newCaret <= lineEnd) {
+              newCaret = advance ? lineEnd : lineStart;
+          }
+          break;
+        }
+
+        return newCaret;
+    }
+
+    /**
+     * Returns the next valid offset within this directional run, skipping
+     * conjuncts and zero-width characters.  This should not be called to walk
+     * off the end of the line, since the returned values might not be valid
+     * on neighboring lines.  If the returned offset is less than zero or
+     * greater than the line length, the offset should be recomputed on the
+     * preceding or following line, respectively.
+     *
+     * @param runIndex the run index
+     * @param runStart the start of the run
+     * @param runLimit the limit of the run
+     * @param runIsRtl true if the run is right-to-left
+     * @param offset the offset
+     * @param after true if the new offset should logically follow the provided
+     * offset
+     * @return the new offset
+     */
+    private int getOffsetBeforeAfter(int runIndex, int runStart, int runLimit,
+            boolean runIsRtl, int offset, boolean after) {
+
+        if (runIndex < 0 || offset == (after ? mLen : 0)) {
+            // Walking off end of line.  Since we don't know
+            // what cursor positions are available on other lines, we can't
+            // return accurate values.  These are a guess.
+            if (after) {
+                return TextUtils.getOffsetAfter(mText, offset + mStart) - mStart;
+            }
+            return TextUtils.getOffsetBefore(mText, offset + mStart) - mStart;
+        }
+
+        TextPaint wp = mWorkPaint;
+        wp.set(mPaint);
+
+        int spanStart = runStart;
+        int spanLimit;
+        if (mSpanned == null) {
+            spanLimit = runLimit;
+        } else {
+            int target = after ? offset + 1 : offset;
+            int limit = mStart + runLimit;
+            while (true) {
+                spanLimit = mSpanned.nextSpanTransition(mStart + spanStart, limit,
+                        MetricAffectingSpan.class) - mStart;
+                if (spanLimit >= target) {
+                    break;
+                }
+                spanStart = spanLimit;
+            }
+
+            MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + spanStart,
+                    mStart + spanLimit, MetricAffectingSpan.class);
+
+            if (spans.length > 0) {
+                ReplacementSpan replacement = null;
+                for (int j = 0; j < spans.length; j++) {
+                    MetricAffectingSpan span = spans[j];
+                    if (span instanceof ReplacementSpan) {
+                        replacement = (ReplacementSpan)span;
+                    } else {
+                        span.updateMeasureState(wp);
+                    }
+                }
+
+                if (replacement != null) {
+                    // If we have a replacement span, we're moving either to
+                    // the start or end of this span.
+                    return after ? spanLimit : spanStart;
+                }
+            }
+        }
+
+        int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
+        int cursorOpt = after ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE;
+        if (mCharsValid) {
+            return wp.getTextRunCursor(mChars, spanStart, spanLimit - spanStart,
+                    flags, offset, cursorOpt);
+        } else {
+            return wp.getTextRunCursor(mText, mStart + spanStart,
+                    mStart + spanLimit, flags, mStart + offset, cursorOpt) - mStart;
+        }
+    }
+
+    /**
+     * Utility function for measuring and rendering text.  The text must
+     * not include a tab or emoji.
+     *
+     * @param wp the working paint
+     * @param start the start of the text
+     * @param end the end of the text
+     * @param runIsRtl true if the run is right-to-left
+     * @param c the canvas, can be null if rendering is not needed
+     * @param x the edge of the run closest to the leading margin
+     * @param top the top of the line
+     * @param y the baseline
+     * @param bottom the bottom of the line
+     * @param fmi receives metrics information, can be null
+     * @param needWidth true if the width of the run is needed
+     * @return the signed width of the run based on the run direction; only
+     * valid if needWidth is true
+     */
+    private float handleText(TextPaint wp, int start, int end,
+            int contextStart, int contextEnd, boolean runIsRtl,
+            Canvas c, float x, int top, int y, int bottom,
+            FontMetricsInt fmi, boolean needWidth) {
+
+        float ret = 0;
+
+        int runLen = end - start;
+        int contextLen = contextEnd - contextStart;
+        if (needWidth || (c != null && (wp.bgColor != 0 || runIsRtl))) {
+            int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
+            if (mCharsValid) {
+                ret = wp.getTextRunAdvances(mChars, start, runLen,
+                        contextStart, contextLen, flags, null, 0);
+            } else {
+                int delta = mStart;
+                ret = wp.getTextRunAdvances(mText, delta + start,
+                        delta + end, delta + contextStart, delta + contextEnd,
+                        flags, null, 0);
+            }
+        }
+
+        if (fmi != null) {
+            wp.getFontMetricsInt(fmi);
+        }
+
+        if (c != null) {
+            if (runIsRtl) {
+                x -= ret;
+            }
+
+            if (wp.bgColor != 0) {
+                int color = wp.getColor();
+                Paint.Style s = wp.getStyle();
+                wp.setColor(wp.bgColor);
+                wp.setStyle(Paint.Style.FILL);
+
+                c.drawRect(x, top, x + ret, bottom, wp);
+
+                wp.setStyle(s);
+                wp.setColor(color);
+            }
+
+            drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl,
+                    x, y + wp.baselineShift);
+        }
+
+        return runIsRtl ? -ret : ret;
+    }
+
+    /**
+     * Utility function for measuring and rendering a replacement.
+     *
+     * @param replacement the replacement
+     * @param wp the work paint
+     * @param runIndex the run index
+     * @param start the start of the run
+     * @param limit the limit of the run
+     * @param runIsRtl true if the run is right-to-left
+     * @param c the canvas, can be null if not rendering
+     * @param x the edge of the replacement closest to the leading margin
+     * @param top the top of the line
+     * @param y the baseline
+     * @param bottom the bottom of the line
+     * @param fmi receives metrics information, can be null
+     * @param needWidth true if the width of the replacement is needed
+     * @return the signed width of the run based on the run direction; only
+     * valid if needWidth is true
+     */
+    private float handleReplacement(ReplacementSpan replacement, TextPaint wp,
+            int runIndex, int start, int limit, boolean runIsRtl, Canvas c,
+            float x, int top, int y, int bottom, FontMetricsInt fmi,
+            boolean needWidth) {
+
+        float ret = 0;
+
+        int textStart = mStart + start;
+        int textLimit = mStart + limit;
+
+        if (needWidth || (c != null && runIsRtl)) {
+            ret = replacement.getSize(wp, mText, textStart, textLimit, fmi);
+        }
+
+        if (c != null) {
+            if (runIsRtl) {
+                x -= ret;
+            }
+            replacement.draw(c, mText, textStart, textLimit,
+                    x, top, y, bottom, wp);
+        }
+
+        return runIsRtl ? -ret : ret;
+    }
+
+    /**
+     * Utility function for handling a unidirectional run.  The run must not
+     * contain tabs or emoji but can contain styles.
+     *
+     * @param runIndex the run index
+     * @param start the line-relative start of the run
+     * @param measureLimit the offset to measure to, between start and limit inclusive
+     * @param limit the limit of the run
+     * @param runIsRtl true if the run is right-to-left
+     * @param c the canvas, can be null
+     * @param x the end of the run closest to the leading margin
+     * @param top the top of the line
+     * @param y the baseline
+     * @param bottom the bottom of the line
+     * @param fmi receives metrics information, can be null
+     * @param needWidth true if the width is required
+     * @return the signed width of the run based on the run direction; only
+     * valid if needWidth is true
+     */
+    private float handleRun(int runIndex, int start, int measureLimit,
+            int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
+            int bottom, FontMetricsInt fmi, boolean needWidth) {
+
+        // Shaping needs to take into account context up to metric boundaries,
+        // but rendering needs to take into account character style boundaries.
+        // So we iterate through metric runs to get metric bounds,
+        // then within each metric run iterate through character style runs
+        // for the run bounds.
+        float ox = x;
+        for (int i = start, inext; i < measureLimit; i = inext) {
+            TextPaint wp = mWorkPaint;
+            wp.set(mPaint);
+
+            int mlimit;
+            if (mSpanned == null) {
+                inext = limit;
+                mlimit = measureLimit;
+            } else {
+                inext = mSpanned.nextSpanTransition(mStart + i, mStart + limit,
+                        MetricAffectingSpan.class) - mStart;
+
+                mlimit = inext < measureLimit ? inext : measureLimit;
+                MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + i,
+                        mStart + mlimit, MetricAffectingSpan.class);
+
+                if (spans.length > 0) {
+                    ReplacementSpan replacement = null;
+                    for (int j = 0; j < spans.length; j++) {
+                        MetricAffectingSpan span = spans[j];
+                        if (span instanceof ReplacementSpan) {
+                            replacement = (ReplacementSpan)span;
+                        } else {
+                            // We might have a replacement that uses the draw
+                            // state, otherwise measure state would suffice.
+                            span.updateDrawState(wp);
+                        }
+                    }
+
+                    if (replacement != null) {
+                        x += handleReplacement(replacement, wp, runIndex, i,
+                                mlimit, runIsRtl, c, x, top, y, bottom, fmi,
+                                needWidth || mlimit < measureLimit);
+                        continue;
+                    }
+                }
+            }
+
+            if (mSpanned == null || c == null) {
+                x += handleText(wp, i, mlimit, i, inext, runIsRtl, c, x, top,
+                        y, bottom, fmi, needWidth || mlimit < measureLimit);
+            } else {
+                for (int j = i, jnext; j < mlimit; j = jnext) {
+                    jnext = mSpanned.nextSpanTransition(mStart + j,
+                            mStart + mlimit, CharacterStyle.class) - mStart;
+
+                    CharacterStyle[] spans = mSpanned.getSpans(mStart + j,
+                            mStart + jnext, CharacterStyle.class);
+
+                    wp.set(mPaint);
+                    for (int k = 0; k < spans.length; k++) {
+                        CharacterStyle span = spans[k];
+                        span.updateDrawState(wp);
+                    }
+
+                    x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
+                            top, y, bottom, fmi, needWidth || jnext < measureLimit);
+                }
+            }
+        }
+
+        return x - ox;
+    }
+
+    /**
+     * Render a text run with the set-up paint.
+     *
+     * @param c the canvas
+     * @param wp the paint used to render the text
+     * @param start the start of the run
+     * @param end the end of the run
+     * @param contextStart the start of context for the run
+     * @param contextEnd the end of the context for the run
+     * @param runIsRtl true if the run is right-to-left
+     * @param x the x position of the left edge of the run
+     * @param y the baseline of the run
+     */
+    private void drawTextRun(Canvas c, TextPaint wp, int start, int end,
+            int contextStart, int contextEnd, boolean runIsRtl, float x, int y) {
+
+        int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR;
+        if (mCharsValid) {
+            int count = end - start;
+            int contextCount = contextEnd - contextStart;
+            c.drawTextRun(mChars, start, count, contextStart, contextCount,
+                    x, y, flags, wp);
+        } else {
+            int delta = mStart;
+            c.drawTextRun(mText, delta + start, delta + end,
+                    delta + contextStart, delta + contextEnd, x, y, flags, wp);
+        }
+    }
+
+    /**
+     * Returns the ascent of the text at start.  This is used for scaling
+     * emoji.
+     *
+     * @param pos the line-relative position
+     * @return the ascent of the text at start
+     */
+    float ascent(int pos) {
+        if (mSpanned == null) {
+            return mPaint.ascent();
+        }
+
+        pos += mStart;
+        MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1,
+                MetricAffectingSpan.class);
+        if (spans.length == 0) {
+            return mPaint.ascent();
+        }
+
+        TextPaint wp = mWorkPaint;
+        wp.set(mPaint);
+        for (MetricAffectingSpan span : spans) {
+            span.updateMeasureState(wp);
+        }
+        return wp.ascent();
+    }
+
+    /**
+     * Returns the next tab position.
+     *
+     * @param h the (unsigned) offset from the leading margin
+     * @return the (unsigned) tab position after this offset
+     */
+    float nextTab(float h) {
+        if (mTabs != null) {
+            return mTabs.nextTab(h);
+        }
+        return TabStops.nextDefaultStop(h, TAB_INCREMENT);
+    }
+
+    private static final int TAB_INCREMENT = 20;
+}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 8675d05..7748265 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -17,12 +17,11 @@
 package android.text;
 
 import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
 
-import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.method.TextKeyListener.Capitalize;
 import android.text.style.AbsoluteSizeSpan;
 import android.text.style.AlignmentSpan;
 import android.text.style.BackgroundColorSpan;
@@ -45,10 +44,8 @@
 import android.text.style.UnderlineSpan;
 import android.util.Printer;
 
-import com.android.internal.util.ArrayUtils;
-
-import java.util.regex.Pattern;
 import java.util.Iterator;
+import java.util.regex.Pattern;
 
 public class TextUtils {
     private TextUtils() { /* cannot be instantiated */ }
@@ -983,7 +980,7 @@
     /**
      * Returns the original text if it fits in the specified width
      * given the properties of the specified Paint,
-     * or, if it does not fit, a copy with ellipsis character added 
+     * or, if it does not fit, a copy with ellipsis character added
      * at the specified edge or center.
      * If <code>preserveLength</code> is specified, the returned copy
      * will be padded with zero-width spaces to preserve the original
@@ -992,7 +989,7 @@
      * report the start and end of the ellipsized range.
      */
     public static CharSequence ellipsize(CharSequence text,
-                                         TextPaint p,
+                                         TextPaint paint,
                                          float avail, TruncateAt where,
                                          boolean preserveLength,
                                          EllipsizeCallback callback) {
@@ -1003,13 +1000,12 @@
 
         int len = text.length();
 
-        // Use Paint.breakText() for the non-Spanned case to avoid having
-        // to allocate memory and accumulate the character widths ourselves.
+        MeasuredText mt = MeasuredText.obtain();
+        try {
+            float width = setPara(mt, paint, text, 0, text.length(),
+                    Layout.DIR_REQUEST_DEFAULT_LTR);
 
-        if (!(text instanceof Spanned)) {
-            float wid = p.measureText(text, 0, len);
-
-            if (wid <= avail) {
+            if (width <= avail) {
                 if (callback != null) {
                     callback.ellipsized(0, 0);
                 }
@@ -1017,250 +1013,69 @@
                 return text;
             }
 
-            float ellipsiswid = p.measureText(sEllipsis);
+            // XXX assumes ellipsis string does not require shaping and
+            // is unaffected by style
+            float ellipsiswid = paint.measureText(sEllipsis);
+            avail -= ellipsiswid;
 
-            if (ellipsiswid > avail) {
-                if (callback != null) {
-                    callback.ellipsized(0, len);
-                }
-
-                if (preserveLength) {
-                    char[] buf = obtain(len);
-                    for (int i = 0; i < len; i++) {
-                        buf[i] = '\uFEFF';
-                    }
-                    String ret = new String(buf, 0, len);
-                    recycle(buf);
-                    return ret;
-                } else {
-                    return "";
-                }
-            }
-
-            if (where == TruncateAt.START) {
-                int fit = p.breakText(text, 0, len, false,
-                                      avail - ellipsiswid, null);
-
-                if (callback != null) {
-                    callback.ellipsized(0, len - fit);
-                }
-
-                if (preserveLength) {
-                    return blank(text, 0, len - fit);
-                } else {
-                    return sEllipsis + text.toString().substring(len - fit, len);
-                }
+            int left = 0;
+            int right = len;
+            if (avail < 0) {
+                // it all goes
+            } else if (where == TruncateAt.START) {
+                right = len - mt.breakText(0, len, false, avail);
             } else if (where == TruncateAt.END) {
-                int fit = p.breakText(text, 0, len, true,
-                                      avail - ellipsiswid, null);
-
-                if (callback != null) {
-                    callback.ellipsized(fit, len);
-                }
-
-                if (preserveLength) {
-                    return blank(text, fit, len);
-                } else {
-                    return text.toString().substring(0, fit) + sEllipsis;
-                } 
-            } else /* where == TruncateAt.MIDDLE */ {
-                int right = p.breakText(text, 0, len, false,
-                                        (avail - ellipsiswid) / 2, null);
-                float used = p.measureText(text, len - right, len);
-                int left = p.breakText(text, 0, len - right, true,
-                                       avail - ellipsiswid - used, null);
-
-                if (callback != null) {
-                    callback.ellipsized(left, len - right);
-                }
-
-                if (preserveLength) {
-                    return blank(text, left, len - right);
-                } else {
-                    String s = text.toString();
-                    return s.substring(0, left) + sEllipsis +
-                           s.substring(len - right, len);
-                }
-            }
-        }
-
-        // But do the Spanned cases by hand, because it's such a pain
-        // to iterate the span transitions backwards and getTextWidths()
-        // will give us the information we need.
-
-        // getTextWidths() always writes into the start of the array,
-        // so measure each span into the first half and then copy the
-        // results into the second half to use later.
-
-        float[] wid = new float[len * 2];
-        TextPaint temppaint = new TextPaint();
-        Spanned sp = (Spanned) text;
-
-        int next;
-        for (int i = 0; i < len; i = next) {
-            next = sp.nextSpanTransition(i, len, MetricAffectingSpan.class);
-
-            Styled.getTextWidths(p, temppaint, sp, i, next, wid, null);
-            System.arraycopy(wid, 0, wid, len + i, next - i);
-        }
-
-        float sum = 0;
-        for (int i = 0; i < len; i++) {
-            sum += wid[len + i];
-        }
-
-        if (sum <= avail) {
-            if (callback != null) {
-                callback.ellipsized(0, 0);
-            }
-
-            return text;
-        }
-
-        float ellipsiswid = p.measureText(sEllipsis);
-
-        if (ellipsiswid > avail) {
-            if (callback != null) {
-                callback.ellipsized(0, len);
-            }
-
-            if (preserveLength) {
-                char[] buf = obtain(len);
-                for (int i = 0; i < len; i++) {
-                    buf[i] = '\uFEFF';
-                }
-                SpannableString ss = new SpannableString(new String(buf, 0, len));
-                recycle(buf);
-                copySpansFrom(sp, 0, len, Object.class, ss, 0);
-                return ss;
+                left = mt.breakText(0, len, true, avail);
             } else {
-                return "";
-            }
-        }
-
-        if (where == TruncateAt.START) {
-            sum = 0;
-            int i;
-
-            for (i = len; i >= 0; i--) {
-                float w = wid[len + i - 1];
-
-                if (w + sum + ellipsiswid > avail) {
-                    break;
-                }
-
-                sum += w;
-            }
-
-            if (callback != null) {
-                callback.ellipsized(0, i);
-            }
-
-            if (preserveLength) {
-                SpannableString ss = new SpannableString(blank(text, 0, i));
-                copySpansFrom(sp, 0, len, Object.class, ss, 0);
-                return ss;
-            } else {
-                SpannableStringBuilder out = new SpannableStringBuilder(sEllipsis);
-                out.insert(1, text, i, len);
-
-                return out;
-            }
-        } else if (where == TruncateAt.END) {
-            sum = 0;
-            int i;
-
-            for (i = 0; i < len; i++) {
-                float w = wid[len + i];
-
-                if (w + sum + ellipsiswid > avail) {
-                    break;
-                }
-
-                sum += w;
-            }
-
-            if (callback != null) {
-                callback.ellipsized(i, len);
-            }
-
-            if (preserveLength) {
-                SpannableString ss = new SpannableString(blank(text, i, len));
-                copySpansFrom(sp, 0, len, Object.class, ss, 0);
-                return ss;
-            } else {
-                SpannableStringBuilder out = new SpannableStringBuilder(sEllipsis);
-                out.insert(0, text, 0, i);
-
-                return out;
-            }
-        } else /* where = TruncateAt.MIDDLE */ {
-            float lsum = 0, rsum = 0;
-            int left = 0, right = len;
-
-            float ravail = (avail - ellipsiswid) / 2;
-            for (right = len; right >= 0; right--) {
-                float w = wid[len + right - 1];
-
-                if (w + rsum > ravail) {
-                    break;
-                }
-
-                rsum += w;
-            }
-
-            float lavail = avail - ellipsiswid - rsum;
-            for (left = 0; left < right; left++) {
-                float w = wid[len + left];
-
-                if (w + lsum > lavail) {
-                    break;
-                }
-
-                lsum += w;
+                right = len - mt.breakText(0, len, false, avail / 2);
+                avail -= mt.measure(right, len);
+                left = mt.breakText(0, right, true, avail);
             }
 
             if (callback != null) {
                 callback.ellipsized(left, right);
             }
 
+            char[] buf = mt.mChars;
+            Spanned sp = text instanceof Spanned ? (Spanned) text : null;
+
+            int remaining = len - (right - left);
             if (preserveLength) {
-                SpannableString ss = new SpannableString(blank(text, left, right));
+                if (remaining > 0) { // else eliminate the ellipsis too
+                    buf[left++] = '\u2026';
+                }
+                for (int i = left; i < right; i++) {
+                    buf[i] = '\uFEFF';
+                }
+                String s = new String(buf, 0, len);
+                if (sp == null) {
+                    return s;
+                }
+                SpannableString ss = new SpannableString(s);
                 copySpansFrom(sp, 0, len, Object.class, ss, 0);
                 return ss;
-            } else {
-                SpannableStringBuilder out = new SpannableStringBuilder(sEllipsis);
-                out.insert(0, text, 0, left);
-                out.insert(out.length(), text, right, len);
-
-                return out;
             }
-        }
-    }
 
-    private static String blank(CharSequence source, int start, int end) {
-        int len = source.length();
-        char[] buf = obtain(len);
-
-        if (start != 0) {
-            getChars(source, 0, start, buf, 0);
-        }
-        if (end != len) {
-            getChars(source, end, len, buf, end);
-        }
-
-        if (start != end) {
-            buf[start] = '\u2026';
-
-            for (int i = start + 1; i < end; i++) {
-                buf[i] = '\uFEFF';
+            if (remaining == 0) {
+                return "";
             }
-        }
-    
-        String ret = new String(buf, 0, len);
-        recycle(buf);
 
-        return ret;
+            if (sp == null) {
+                StringBuilder sb = new StringBuilder(remaining + sEllipsis.length());
+                sb.append(buf, 0, left);
+                sb.append(sEllipsis);
+                sb.append(buf, right, len - right);
+                return sb.toString();
+            }
+
+            SpannableStringBuilder ssb = new SpannableStringBuilder();
+            ssb.append(text, 0, left);
+            ssb.append(sEllipsis);
+            ssb.append(text, right, len);
+            return ssb;
+        } finally {
+            MeasuredText.recycle(mt);
+        }
     }
 
     /**
@@ -1278,80 +1093,121 @@
                                               TextPaint p, float avail,
                                               String oneMore,
                                               String more) {
-        int len = text.length();
-        char[] buf = new char[len];
-        TextUtils.getChars(text, 0, len, buf, 0);
 
-        int commaCount = 0;
-        for (int i = 0; i < len; i++) {
-            if (buf[i] == ',') {
-                commaCount++;
-            }
-        }
-
-        float[] wid;
-
-        if (text instanceof Spanned) {
-            Spanned sp = (Spanned) text;
-            TextPaint temppaint = new TextPaint();
-            wid = new float[len * 2];
-
-            int next;
-            for (int i = 0; i < len; i = next) {
-                next = sp.nextSpanTransition(i, len, MetricAffectingSpan.class);
-
-                Styled.getTextWidths(p, temppaint, sp, i, next, wid, null);
-                System.arraycopy(wid, 0, wid, len + i, next - i);
+        MeasuredText mt = MeasuredText.obtain();
+        try {
+            int len = text.length();
+            float width = setPara(mt, p, text, 0, len, Layout.DIR_REQUEST_DEFAULT_LTR);
+            if (width <= avail) {
+                return text;
             }
 
-            System.arraycopy(wid, len, wid, 0, len);
-        } else {
-            wid = new float[len];
-            p.getTextWidths(text, 0, len, wid);
-        }
+            char[] buf = mt.mChars;
 
-        int ok = 0;
-        int okRemaining = commaCount + 1;
-        String okFormat = "";
-
-        int w = 0;
-        int count = 0;
-
-        for (int i = 0; i < len; i++) {
-            w += wid[i];
-
-            if (buf[i] == ',') {
-                count++;
-
-                int remaining = commaCount - count + 1;
-                float moreWid;
-                String format;
-
-                if (remaining == 1) {
-                    format = " " + oneMore;
-                } else {
-                    format = " " + String.format(more, remaining);
-                }
-
-                moreWid = p.measureText(format);
-
-                if (w + moreWid <= avail) {
-                    ok = i + 1;
-                    okRemaining = remaining;
-                    okFormat = format;
+            int commaCount = 0;
+            for (int i = 0; i < len; i++) {
+                if (buf[i] == ',') {
+                    commaCount++;
                 }
             }
-        }
 
-        if (w <= avail) {
-            return text;
-        } else {
+            int remaining = commaCount + 1;
+
+            int ok = 0;
+            int okRemaining = remaining;
+            String okFormat = "";
+
+            int w = 0;
+            int count = 0;
+            float[] widths = mt.mWidths;
+
+            int request = mt.mDir == 1 ? Layout.DIR_REQUEST_LTR :
+                Layout.DIR_REQUEST_RTL;
+
+            MeasuredText tempMt = MeasuredText.obtain();
+            for (int i = 0; i < len; i++) {
+                w += widths[i];
+
+                if (buf[i] == ',') {
+                    count++;
+
+                    String format;
+                    // XXX should not insert spaces, should be part of string
+                    // XXX should use plural rules and not assume English plurals
+                    if (--remaining == 1) {
+                        format = " " + oneMore;
+                    } else {
+                        format = " " + String.format(more, remaining);
+                    }
+
+                    // XXX this is probably ok, but need to look at it more
+                    tempMt.setPara(format, 0, format.length(), request);
+                    float moreWid = mt.addStyleRun(p, mt.mLen, null);
+
+                    if (w + moreWid <= avail) {
+                        ok = i + 1;
+                        okRemaining = remaining;
+                        okFormat = format;
+                    }
+                }
+            }
+            MeasuredText.recycle(tempMt);
+
             SpannableStringBuilder out = new SpannableStringBuilder(okFormat);
             out.insert(0, text, 0, ok);
             return out;
+        } finally {
+            MeasuredText.recycle(mt);
         }
     }
 
+    private static float setPara(MeasuredText mt, TextPaint paint,
+            CharSequence text, int start, int end, int bidiRequest) {
+
+        mt.setPara(text, start, end, bidiRequest);
+
+        float width;
+        Spanned sp = text instanceof Spanned ? (Spanned) text : null;
+        int len = end - start;
+        if (sp == null) {
+            width = mt.addStyleRun(paint, len, null);
+        } else {
+            width = 0;
+            int spanEnd;
+            for (int spanStart = 0; spanStart < len; spanStart = spanEnd) {
+                spanEnd = sp.nextSpanTransition(spanStart, len,
+                        MetricAffectingSpan.class);
+                MetricAffectingSpan[] spans = sp.getSpans(
+                        spanStart, spanEnd, MetricAffectingSpan.class);
+                width += mt.addStyleRun(paint, spans, spanEnd - spanStart, null);
+            }
+        }
+
+        return width;
+    }
+
+    private static final char FIRST_RIGHT_TO_LEFT = '\u0590';
+
+    /* package */
+    static boolean doesNotNeedBidi(CharSequence s, int start, int end) {
+        for (int i = start; i < end; i++) {
+            if (s.charAt(i) >= FIRST_RIGHT_TO_LEFT) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /* package */
+    static boolean doesNotNeedBidi(char[] text, int start, int len) {
+        for (int i = start, e = i + len; i < e; i++) {
+            if (text[i] >= FIRST_RIGHT_TO_LEFT) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /* package */ static char[] obtain(int len) {
         char[] buf;
 
@@ -1529,7 +1385,7 @@
      */
     public static final int CAP_MODE_CHARACTERS
             = InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
-    
+
     /**
      * Capitalization mode for {@link #getCapsMode}: capitalize the first
      * character of all words.  This value is explicitly defined to be the same as
@@ -1537,7 +1393,7 @@
      */
     public static final int CAP_MODE_WORDS
             = InputType.TYPE_TEXT_FLAG_CAP_WORDS;
-    
+
     /**
      * Capitalization mode for {@link #getCapsMode}: capitalize the first
      * character of each sentence.  This value is explicitly defined to be the same as
@@ -1545,13 +1401,13 @@
      */
     public static final int CAP_MODE_SENTENCES
             = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
-    
+
     /**
      * Determine what caps mode should be in effect at the current offset in
      * the text.  Only the mode bits set in <var>reqModes</var> will be
      * checked.  Note that the caps mode flags here are explicitly defined
      * to match those in {@link InputType}.
-     * 
+     *
      * @param cs The text that should be checked for caps modes.
      * @param off Location in the text at which to check.
      * @param reqModes The modes to be checked: may be any combination of
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 220e023..0d012d6 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -30,12 +30,12 @@
 
 public class ArrowKeyMovementMethod implements MovementMethod {
     private boolean isCap(Spannable buffer) {
-        return ((MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) ||
+        return ((MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SHIFT_ON) == 1) ||
                 (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0));
     }
 
     private boolean isAlt(Spannable buffer) {
-        return MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_ALT_ON) == 1;
+        return MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_ALT_ON) == 1;
     }
 
     private boolean up(TextView widget, Spannable buffer) {
diff --git a/core/java/android/text/method/DialerKeyListener.java b/core/java/android/text/method/DialerKeyListener.java
index 584e83f..1e1812a 100644
--- a/core/java/android/text/method/DialerKeyListener.java
+++ b/core/java/android/text/method/DialerKeyListener.java
@@ -56,7 +56,7 @@
          * Prefer number if no meta key is active, or if it produces something
          * valid and the meta lookup does not.
          */
-        if ((meta & (KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)) == 0) {
+        if ((meta & (MetaKeyKeyListener.META_ALT_ON | MetaKeyKeyListener.META_SHIFT_ON)) == 0) {
             if (number != 0) {
                 return number;
             }
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index 61ec67f..e7b36d7 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -16,9 +16,12 @@
 
 package android.text.method;
 
+import android.text.Editable;
+import android.text.NoCopySpan;
+import android.text.Spannable;
+import android.text.Spanned;
 import android.view.KeyEvent;
 import android.view.View;
-import android.text.*;
 
 /**
  * This base class encapsulates the behavior for handling the meta keys
@@ -28,38 +31,58 @@
  */
 
 public abstract class MetaKeyKeyListener {
+    /**
+     * Flag that indicates that the SHIFT key is on.
+     * Value equals {@link KeyEvent#META_SHIFT_ON}.
+     */
     public static final int META_SHIFT_ON = KeyEvent.META_SHIFT_ON;
+    /**
+     * Flag that indicates that the ALT key is on.
+     * Value equals {@link KeyEvent#META_ALT_ON}.
+     */
     public static final int META_ALT_ON = KeyEvent.META_ALT_ON;
+    /**
+     * Flag that indicates that the SYM key is on.
+     * Value equals {@link KeyEvent#META_SYM_ON}.
+     */
     public static final int META_SYM_ON = KeyEvent.META_SYM_ON;
-
-    private static final int LOCKED_SHIFT = 8;
     
-    public static final int META_CAP_LOCKED = KeyEvent.META_SHIFT_ON << LOCKED_SHIFT;
-    public static final int META_ALT_LOCKED = KeyEvent.META_ALT_ON << LOCKED_SHIFT;
-    public static final int META_SYM_LOCKED = KeyEvent.META_SYM_ON << LOCKED_SHIFT;
+    /**
+     * Flag that indicates that the SHIFT key is locked in CAPS mode.
+     */
+    public static final int META_CAP_LOCKED = KeyEvent.META_CAP_LOCKED;
+    /**
+     * Flag that indicates that the ALT key is locked.
+     */
+    public static final int META_ALT_LOCKED = KeyEvent.META_ALT_LOCKED;
+    /**
+     * Flag that indicates that the SYM key is locked.
+     */
+    public static final int META_SYM_LOCKED = KeyEvent.META_SYM_LOCKED;
 
     /**
      * @hide pending API review
      */
-    public static final int META_SELECTING = 1 << 16;
+    public static final int META_SELECTING = KeyEvent.META_SELECTING;
 
-    private static final int USED_SHIFT = 24;
-    
-    private static final long META_CAP_USED = ((long)KeyEvent.META_SHIFT_ON) << USED_SHIFT;
-    private static final long META_ALT_USED = ((long)KeyEvent.META_ALT_ON) << USED_SHIFT;
-    private static final long META_SYM_USED = ((long)KeyEvent.META_SYM_ON) << USED_SHIFT;
+    private static final int META_SHIFT_ON_AND_LOCKED = META_SHIFT_ON | META_CAP_LOCKED;
+    private static final int META_ALT_ON_AND_LOCKED = META_ALT_ON | META_ALT_LOCKED;
+    private static final int META_SYM_ON_AND_LOCKED = META_SYM_ON | META_SYM_LOCKED;
 
-    private static final int PRESSED_SHIFT = 32;
+    // These bits are privately used by the meta key key listener.
+    // They are deliberately assigned values outside of the representable range of an 'int'
+    // so as not to conflict with any meta key states publicly defined by KeyEvent.
+    private static final long META_CAP_USED = 1L << 32;
+    private static final long META_ALT_USED = 1L << 33;
+    private static final long META_SYM_USED = 1L << 34;
     
-    private static final long META_CAP_PRESSED = ((long)KeyEvent.META_SHIFT_ON) << PRESSED_SHIFT;
-    private static final long META_ALT_PRESSED = ((long)KeyEvent.META_ALT_ON) << PRESSED_SHIFT;
-    private static final long META_SYM_PRESSED = ((long)KeyEvent.META_SYM_ON) << PRESSED_SHIFT;
-
-    private static final int RELEASED_SHIFT = 40;
+    private static final long META_CAP_PRESSED = 1L << 40;
+    private static final long META_ALT_PRESSED = 1L << 41;
+    private static final long META_SYM_PRESSED = 1L << 42;
     
-    private static final long META_CAP_RELEASED = ((long)KeyEvent.META_SHIFT_ON) << RELEASED_SHIFT;
-    private static final long META_ALT_RELEASED = ((long)KeyEvent.META_ALT_ON) << RELEASED_SHIFT;
-    private static final long META_SYM_RELEASED = ((long)KeyEvent.META_SYM_ON) << RELEASED_SHIFT;
+    private static final long META_CAP_RELEASED = 1L << 48;
+    private static final long META_ALT_RELEASED = 1L << 49;
+    private static final long META_SYM_RELEASED = 1L << 50;
 
     private static final long META_SHIFT_MASK = META_SHIFT_ON
             | META_CAP_LOCKED | META_CAP_USED
@@ -306,15 +329,14 @@
      * (arrow keys, for example) and you handle a key.
      */
     public static long resetLockedMeta(long state) {
-        state = resetLock(state, META_SHIFT_ON, META_SHIFT_MASK);
-        state = resetLock(state, META_ALT_ON, META_ALT_MASK);
-        state = resetLock(state, META_SYM_ON, META_SYM_MASK);
-        return state;
-    }
-
-    private static long resetLock(long state, int what, long mask) {
-        if ((state&(((long)what)<<LOCKED_SHIFT)) != 0) {
-            state &= ~mask;
+        if ((state & META_CAP_LOCKED) != 0) {
+            state &= ~META_SHIFT_MASK;
+        }
+        if ((state & META_ALT_LOCKED) != 0) {
+            state &= ~META_ALT_MASK;
+        }
+        if ((state & META_SYM_LOCKED) != 0) {
+            state &= ~META_SYM_MASK;
         }
         return state;
     }
@@ -332,9 +354,27 @@
      *         or locked meta key.
      */
     public static final int getMetaState(long state) {
-        return getActive(state, META_SHIFT_ON, META_SHIFT_ON, META_CAP_LOCKED) |
-               getActive(state, META_ALT_ON, META_ALT_ON, META_ALT_LOCKED) |
-               getActive(state, META_SYM_ON, META_SYM_ON, META_SYM_LOCKED);
+        int result = 0;
+
+        if ((state & META_CAP_LOCKED) != 0) {
+            result |= META_CAP_LOCKED;
+        } else if ((state & META_SHIFT_ON) != 0) {
+            result |= META_SHIFT_ON;
+        }
+
+        if ((state & META_ALT_LOCKED) != 0) {
+            result |= META_ALT_LOCKED;
+        } else if ((state & META_ALT_ON) != 0) {
+            result |= META_ALT_ON;
+        }
+
+        if ((state & META_SYM_LOCKED) != 0) {
+            result |= META_SYM_LOCKED;
+        } else if ((state & META_SYM_ON) != 0) {
+            result |= META_SYM_ON;
+        }
+
+        return result;
     }
 
     /**
@@ -348,29 +388,25 @@
     public static final int getMetaState(long state, int meta) {
         switch (meta) {
             case META_SHIFT_ON:
-                return getActive(state, meta, 1, 2);
+                if ((state & META_CAP_LOCKED) != 0) return 2;
+                if ((state & META_SHIFT_ON) != 0) return 1;
+                return 0;
 
             case META_ALT_ON:
-                return getActive(state, meta, 1, 2);
+                if ((state & META_ALT_LOCKED) != 0) return 2;
+                if ((state & META_ALT_ON) != 0) return 1;
+                return 0;
 
             case META_SYM_ON:
-                return getActive(state, meta, 1, 2);
+                if ((state & META_SYM_LOCKED) != 0) return 2;
+                if ((state & META_SYM_ON) != 0) return 1;
+                return 0;
 
             default:
                 return 0;
         }
     }
 
-    private static int getActive(long state, int meta, int on, int lock) {
-        if ((state&(meta<<LOCKED_SHIFT)) != 0) {
-            return lock;
-        } else if ((state&meta) != 0) {
-            return on;
-        } else {
-            return 0;
-        }
-    }
-
     /**
      * Call this method after you handle a keypress so that the meta
      * state will be reset to unshifted (if it is not still down)
@@ -378,17 +414,23 @@
      * the current state, returns the new state.
      */
     public static long adjustMetaAfterKeypress(long state) {
-        state = adjust(state, META_SHIFT_ON, META_SHIFT_MASK);
-        state = adjust(state, META_ALT_ON, META_ALT_MASK);
-        state = adjust(state, META_SYM_ON, META_SYM_MASK);
-        return state;
-    }
+        if ((state & META_CAP_PRESSED) != 0) {
+            state = (state & ~META_SHIFT_MASK) | META_SHIFT_ON | META_CAP_USED;
+        } else if ((state & META_CAP_RELEASED) != 0) {
+            state &= ~META_SHIFT_MASK;
+        }
 
-    private static long adjust(long state, int what, long mask) {
-        if ((state&(((long)what)<<PRESSED_SHIFT)) != 0)
-            return (state&~mask) | what | ((long)what)<<USED_SHIFT;
-        else if ((state&(((long)what)<<RELEASED_SHIFT)) != 0)
-            return state & ~mask;
+        if ((state & META_ALT_PRESSED) != 0) {
+            state = (state & ~META_ALT_MASK) | META_ALT_ON | META_ALT_USED;
+        } else if ((state & META_ALT_RELEASED) != 0) {
+            state &= ~META_ALT_MASK;
+        }
+
+        if ((state & META_SYM_PRESSED) != 0) {
+            state = (state & ~META_SYM_MASK) | META_SYM_ON | META_SYM_USED;
+        } else if ((state & META_SYM_RELEASED) != 0) {
+            state &= ~META_SYM_MASK;
+        }
         return state;
     }
 
@@ -397,32 +439,36 @@
      */
     public static long handleKeyDown(long state, int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
-            return press(state, META_SHIFT_ON, META_SHIFT_MASK);
+            return press(state, META_SHIFT_ON, META_SHIFT_MASK,
+                    META_CAP_LOCKED, META_CAP_PRESSED, META_CAP_RELEASED, META_CAP_USED);
         }
 
         if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT
                 || keyCode == KeyEvent.KEYCODE_NUM) {
-            return press(state, META_ALT_ON, META_ALT_MASK);
+            return press(state, META_ALT_ON, META_ALT_MASK,
+                    META_ALT_LOCKED, META_ALT_PRESSED, META_ALT_RELEASED, META_ALT_USED);
         }
 
         if (keyCode == KeyEvent.KEYCODE_SYM) {
-            return press(state, META_SYM_ON, META_SYM_MASK);
+            return press(state, META_SYM_ON, META_SYM_MASK,
+                    META_SYM_LOCKED, META_SYM_PRESSED, META_SYM_RELEASED, META_SYM_USED);
         }
-
         return state;
     }
 
-    private static long press(long state, int what, long mask) {
-        if ((state&(((long)what)<<PRESSED_SHIFT)) != 0)
-            ; // repeat before use
-        else if ((state&(((long)what)<<RELEASED_SHIFT)) != 0)
-            state = (state&~mask) | what | (((long)what) << LOCKED_SHIFT);
-        else if ((state&(((long)what)<<USED_SHIFT)) != 0)
-            ; // repeat after use
-        else if ((state&(((long)what)<<LOCKED_SHIFT)) != 0)
-            state = state&~mask;
-        else
-            state = state | what | (((long)what)<<PRESSED_SHIFT);
+    private static long press(long state, int what, long mask,
+            long locked, long pressed, long released, long used) {
+        if ((state & pressed) != 0) {
+            // repeat before use
+        } else if ((state & released) != 0) {
+            state = (state &~ mask) | what | locked;
+        } else if ((state & used) != 0) {
+            // repeat after use
+        } else if ((state & locked) != 0) {
+            state &= ~mask;
+        } else {
+            state |= what | pressed;
+        }
         return state;
     }
 
@@ -431,39 +477,52 @@
      */
     public static long handleKeyUp(long state, int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
-            return release(state, META_SHIFT_ON, META_SHIFT_MASK);
+            return release(state, META_SHIFT_ON, META_SHIFT_MASK,
+                    META_CAP_PRESSED, META_CAP_RELEASED, META_CAP_USED);
         }
 
         if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT
                 || keyCode == KeyEvent.KEYCODE_NUM) {
-            return release(state, META_ALT_ON, META_ALT_MASK);
+            return release(state, META_ALT_ON, META_ALT_MASK,
+                    META_ALT_PRESSED, META_ALT_RELEASED, META_ALT_USED);
         }
 
         if (keyCode == KeyEvent.KEYCODE_SYM) {
-            return release(state, META_SYM_ON, META_SYM_MASK);
+            return release(state, META_SYM_ON, META_SYM_MASK,
+                    META_SYM_PRESSED, META_SYM_RELEASED, META_SYM_USED);
         }
-
         return state;
     }
 
-    private static long release(long state, int what, long mask) {
-        if ((state&(((long)what)<<USED_SHIFT)) != 0)
-            state = state&~mask;
-        else if ((state&(((long)what)<<PRESSED_SHIFT)) != 0)
-            state = state | what | (((long)what)<<RELEASED_SHIFT);
+    private static long release(long state, int what, long mask,
+            long pressed, long released, long used) {
+        if ((state & used) != 0) {
+            state &= ~mask;
+        } else if ((state & pressed) != 0) {
+            state |= what | released;
+        }
         return state;
     }
 
+    /**
+     * Clears the state of the specified meta key if it is locked.
+     * @param state the meta key state
+     * @param which meta keys to clear, may be a combination of {@link #META_SHIFT_ON},
+     * {@link #META_ALT_ON} or {@link #META_SYM_ON}.
+     */
     public long clearMetaKeyState(long state, int which) {
-        if ((which&META_SHIFT_ON) != 0)
-            state = resetLock(state, META_SHIFT_ON, META_SHIFT_MASK);
-        if ((which&META_ALT_ON) != 0)
-            state = resetLock(state, META_ALT_ON, META_ALT_MASK);
-        if ((which&META_SYM_ON) != 0)
-            state = resetLock(state, META_SYM_ON, META_SYM_MASK);
+        if ((which & META_SHIFT_ON_AND_LOCKED) == META_SHIFT_ON_AND_LOCKED) {
+            state &= ~META_SHIFT_MASK;
+        }
+        if ((which & META_ALT_ON_AND_LOCKED) == META_ALT_ON_AND_LOCKED) {
+            state &= ~META_ALT_MASK;
+        }
+        if ((which & META_SYM_ON_AND_LOCKED) == META_SYM_ON_AND_LOCKED) {
+            state &= ~META_SYM_MASK;
+        }
         return state;
     }
-    
+
     /**
      * The meta key has been pressed but has not yet been used.
      */
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index a19a78e..9eaab17 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -44,6 +44,7 @@
         int left = Integer.MAX_VALUE;
         int right = 0;
         Alignment a = null;
+        boolean ltr = true;
 
         for (int i = top; i <= bottom; i++) {
             left = (int) Math.min(left, layout.getLineLeft(i));
@@ -51,6 +52,7 @@
 
             if (a == null) {
                 a = layout.getParagraphAlignment(i);
+                ltr = layout.getParagraphDirection(i) > 0;
             }
         }
 
@@ -58,10 +60,12 @@
         int width = widget.getWidth();
         int diff = 0;
 
+        // align_opposite does NOT mean align_right, we need the paragraph
+        // direction to resolve it to left or right
         if (right - left < width - padding) {
             if (a == Alignment.ALIGN_CENTER) {
                 diff = (width - padding - (right - left)) / 2;
-            } else if (a == Alignment.ALIGN_OPPOSITE) {
+            } else if (ltr == (a == Alignment.ALIGN_OPPOSITE)) {
                 diff = width - padding - (right - left);
             }
         }
@@ -140,7 +144,7 @@
                 if (ds[0].mFarEnough) {
                     ds[0].mUsed = true;
                     boolean cap = (MetaKeyKeyListener.getMetaState(buffer,
-                                   KeyEvent.META_SHIFT_ON) == 1) ||
+                                   MetaKeyKeyListener.META_SHIFT_ON) == 1) ||
                                    (MetaKeyKeyListener.getMetaState(buffer,
                                     MetaKeyKeyListener.META_SELECTING) != 0);
                     float dx;
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 655bd81..3f86b08 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -18,6 +18,8 @@
 
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Path.Direction;
 import android.os.Parcel;
 import android.text.Layout;
 import android.text.ParcelableSpan;
@@ -30,6 +32,7 @@
     private final int mColor;
 
     private static final int BULLET_RADIUS = 3;
+    private static Path sBulletPath = null;
     public static final int STANDARD_GAP_WIDTH = 2;
 
     public BulletSpan() {
@@ -55,11 +58,11 @@
         mWantColor = src.readInt() != 0;
         mColor = src.readInt();
     }
-    
+
     public int getSpanTypeId() {
         return TextUtils.BULLET_SPAN;
     }
-    
+
     public int describeContents() {
         return 0;
     }
@@ -89,8 +92,20 @@
 
             p.setStyle(Paint.Style.FILL);
 
-            c.drawCircle(x + dir * BULLET_RADIUS, (top + bottom) / 2.0f,
-                         BULLET_RADIUS, p);
+            if (c.isHardwareAccelerated()) {
+                if (sBulletPath == null) {
+                    sBulletPath = new Path();
+                    // Bullet is slightly better to avoid aliasing artifacts on mdpi devices.
+                    sBulletPath.addCircle(0.0f, 0.0f, 1.2f * BULLET_RADIUS, Direction.CW);
+                }
+
+                c.save();
+                c.translate(x + dir * BULLET_RADIUS, (top + bottom) / 2.0f);
+                c.drawPath(sBulletPath, p);
+                c.restore();
+            } else {
+                c.drawCircle(x + dir * BULLET_RADIUS, (top + bottom) / 2.0f, BULLET_RADIUS, p);
+            }
 
             if (mWantColor) {
                 p.setColor(oldcolor);
diff --git a/core/java/android/util/AndroidException.java b/core/java/android/util/AndroidException.java
index a767ea1..dfe00c9b 100644
--- a/core/java/android/util/AndroidException.java
+++ b/core/java/android/util/AndroidException.java
@@ -27,6 +27,10 @@
         super(name);
     }
 
+    public AndroidException(String name, Throwable cause) {
+        super(name, cause);
+    }
+
     public AndroidException(Exception cause) {
         super(cause);
     }
diff --git a/core/java/android/util/AndroidRuntimeException.java b/core/java/android/util/AndroidRuntimeException.java
index 4ed17bc..2b824bf 100644
--- a/core/java/android/util/AndroidRuntimeException.java
+++ b/core/java/android/util/AndroidRuntimeException.java
@@ -27,6 +27,10 @@
         super(name);
     }
 
+    public AndroidRuntimeException(String name, Throwable cause) {
+        super(name, cause);
+    }
+
     public AndroidRuntimeException(Exception cause) {
         super(cause);
     }
diff --git a/core/java/android/util/CharsetUtils.java b/core/java/android/util/CharsetUtils.java
index 9d91aca..a763a69 100644
--- a/core/java/android/util/CharsetUtils.java
+++ b/core/java/android/util/CharsetUtils.java
@@ -17,36 +17,58 @@
 package android.util;
 
 import android.os.Build;
+import android.text.TextUtils;
 
 import java.nio.charset.Charset;
 import java.nio.charset.IllegalCharsetNameException;
 import java.nio.charset.UnsupportedCharsetException;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
+ * <p>
  * A class containing utility methods related to character sets. This
  * class is primarily useful for code that wishes to be vendor-aware
- * in its interpretation of Japanese encoding names.
- * 
- * <p>As of this writing, the only vendor that is recognized by this
- * class is Docomo (identified case-insensitively as {@code "docomo"}).</p>
- * 
- * <b>Note:</b> This class is hidden in Cupcake, with a plan to
- * un-hide in Donut. This was done because the first deployment to use
- * this code is based on Cupcake, but the API had to be introduced
- * after the public API freeze for that release. The upshot is that
- * only system applications can safely use this class until Donut is
- * available.
- * 
+ * in its interpretation of Japanese charset names (used in DoCoMo,
+ * KDDI, and SoftBank).
+ * </p>
+ *
+ * <p>
+ * <b>Note:</b> Developers will need to add an appropriate mapping for
+ * each vendor-specific charset. You may need to modify the C libraries
+ * like icu4c in order to let Android support an additional charset.
+ * </p>
+ *
  * @hide
  */
 public final class CharsetUtils {
     /**
-     * name of the vendor "Docomo". <b>Note:</b> This isn't a public
+     * name of the vendor "DoCoMo". <b>Note:</b> This isn't a public
      * constant, in order to keep this class from becoming a de facto
      * reference list of vendor names.
      */
     private static final String VENDOR_DOCOMO = "docomo";
-    
+    /**
+     * Name of the vendor "KDDI".
+     */
+    private static final String VENDOR_KDDI = "kddi";
+    /**
+     * Name of the vendor "SoftBank".
+     */
+    private static final String VENDOR_SOFTBANK = "softbank";
+
+    /**
+     * Represents one-to-one mapping from a vendor name to a charset specific to the vendor.
+     */
+    private static final Map<String, String> sVendorShiftJisMap = new HashMap<String, String>();
+
+    static {
+        // These variants of Shift_JIS come from icu's mapping data (convrtrs.txt)
+        sVendorShiftJisMap.put(VENDOR_DOCOMO, "docomo-shift_jis-2007");
+        sVendorShiftJisMap.put(VENDOR_KDDI, "kddi-shift_jis-2007");
+        sVendorShiftJisMap.put(VENDOR_SOFTBANK, "softbank-shift_jis-2007");
+    }
+
     /**
      * This class is uninstantiable.
      */
@@ -58,20 +80,22 @@
      * Returns the name of the vendor-specific character set
      * corresponding to the given original character set name and
      * vendor. If there is no vendor-specific character set for the
-     * given name/vendor pair, this returns the original character set
-     * name. The vendor name is matched case-insensitively.
-     * 
+     * given name/vendor pair, this returns the original character set name.
+     *
      * @param charsetName the base character set name
-     * @param vendor the vendor to specialize for
+     * @param vendor the vendor to specialize for. All characters should be lower-cased.
      * @return the specialized character set name, or {@code charsetName} if
      * there is no specialized name
      */
     public static String nameForVendor(String charsetName, String vendor) {
-        // TODO: Eventually, this may want to be table-driven.
-
-        if (vendor.equalsIgnoreCase(VENDOR_DOCOMO)
-                && isShiftJis(charsetName)) {
-            return "docomo-shift_jis-2007";
+        if (!TextUtils.isEmpty(charsetName) && !TextUtils.isEmpty(vendor)) {
+            // You can add your own mapping here.
+            if (isShiftJis(charsetName)) {
+                final String vendorShiftJis = sVendorShiftJisMap.get(vendor);
+                if (vendorShiftJis != null) {
+                    return vendorShiftJis;
+                }
+            }
         }
 
         return charsetName;
diff --git a/core/java/android/util/Finalizers.java b/core/java/android/util/Finalizers.java
new file mode 100644
index 0000000..671f2d4
--- /dev/null
+++ b/core/java/android/util/Finalizers.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+
+/**
+ * This class can be used to implement reliable finalizers.
+ * 
+ * @hide
+ */
+public final class Finalizers {
+    private static final String LOG_TAG = "Finalizers";
+    
+    private static final Object[] sLock = new Object[0];
+    private static boolean sInit;
+    private static Reclaimer sReclaimer;
+
+    /**
+     * Subclass of PhantomReference used to reclaim resources.
+     */
+    public static abstract class ReclaimableReference<T> extends PhantomReference<T> {
+        public ReclaimableReference(T r, ReferenceQueue<Object> q) {
+            super(r, q);
+        }
+        
+        public abstract void reclaim();
+    }
+
+    /**
+     * Returns the queue used to reclaim ReclaimableReferences.
+     * 
+     * @return A reference queue or null before initialization
+     */
+    public static ReferenceQueue<Object> getQueue() {
+        synchronized (sLock) {
+            if (!sInit) {
+                return null;
+            }
+            if (!sReclaimer.isRunning()) {
+                sReclaimer = new Reclaimer(sReclaimer.mQueue);
+                sReclaimer.start();
+            }
+            return sReclaimer.mQueue;
+        }
+    }
+
+    /**
+     * Invoked by Zygote. Don't touch!
+     */
+    public static void init() {
+        synchronized (sLock) {
+            if (!sInit && sReclaimer == null) {
+                sReclaimer = new Reclaimer();
+                sReclaimer.start();
+                sInit = true;
+            }
+        }
+    }
+    
+    private static class Reclaimer extends Thread {
+        ReferenceQueue<Object> mQueue;
+
+        private volatile boolean mRunning = false;
+
+        Reclaimer() {
+            this(new ReferenceQueue<Object>());
+        }
+
+        Reclaimer(ReferenceQueue<Object> queue) {
+            super("Reclaimer");
+            setDaemon(true);
+            mQueue = queue;            
+        }
+
+        @Override
+        public void start() {
+            mRunning = true;
+            super.start();
+        }
+
+        boolean isRunning() {
+            return mRunning;
+        }
+
+        @SuppressWarnings({"InfiniteLoopStatement"})
+        @Override
+        public void run() {
+            try {
+                while (true) {
+                    try {
+                        cleanUp(mQueue.remove());
+                    } catch (InterruptedException e) {
+                        // Ignore
+                    }
+                }
+            } catch (Exception e) {
+                Log.e(LOG_TAG, "Reclaimer thread exiting: ", e);
+            } finally {
+                mRunning = false;
+            }
+        }
+
+        private void cleanUp(Reference<?> reference) {
+            do {
+                reference.clear();
+                ((ReclaimableReference<?>) reference).reclaim();
+            } while ((reference = mQueue.poll()) != null);
+        }
+    }
+}
diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java
new file mode 100644
index 0000000..d13d97a
--- /dev/null
+++ b/core/java/android/util/JsonReader.java
@@ -0,0 +1,1058 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
+ * encoded value as a stream of tokens. This stream includes both literal
+ * values (strings, numbers, booleans, and nulls) as well as the begin and
+ * end delimiters of objects and arrays. The tokens are traversed in
+ * depth-first order, the same order that they appear in the JSON document.
+ * Within JSON objects, name/value pairs are represented by a single token.
+ *
+ * <h3>Parsing JSON</h3>
+ * To create a recursive descent parser for your own JSON streams, first create
+ * an entry point method that creates a {@code JsonReader}.
+ *
+ * <p>Next, create handler methods for each structure in your JSON text. You'll
+ * need a method for each object type and for each array type.
+ * <ul>
+ *   <li>Within <strong>array handling</strong> methods, first call {@link
+ *       #beginArray} to consume the array's opening bracket. Then create a
+ *       while loop that accumulates values, terminating when {@link #hasNext}
+ *       is false. Finally, read the array's closing bracket by calling {@link
+ *       #endArray}.
+ *   <li>Within <strong>object handling</strong> methods, first call {@link
+ *       #beginObject} to consume the object's opening brace. Then create a
+ *       while loop that assigns values to local variables based on their name.
+ *       This loop should terminate when {@link #hasNext} is false. Finally,
+ *       read the object's closing brace by calling {@link #endObject}.
+ * </ul>
+ * <p>When a nested object or array is encountered, delegate to the
+ * corresponding handler method.
+ *
+ * <p>When an unknown name is encountered, strict parsers should fail with an
+ * exception. Lenient parsers should call {@link #skipValue()} to recursively
+ * skip the value's nested tokens, which may otherwise conflict.
+ *
+ * <p>If a value may be null, you should first check using {@link #peek()}.
+ * Null literals can be consumed using either {@link #nextNull()} or {@link
+ * #skipValue()}.
+ *
+ * <h3>Example</h3>
+ * Suppose we'd like to parse a stream of messages such as the following: <pre> {@code
+ * [
+ *   {
+ *     "id": 912345678901,
+ *     "text": "How do I read JSON on Android?",
+ *     "geo": null,
+ *     "user": {
+ *       "name": "android_newb",
+ *       "followers_count": 41
+ *      }
+ *   },
+ *   {
+ *     "id": 912345678902,
+ *     "text": "@android_newb just use android.util.JsonReader!",
+ *     "geo": [50.454722, -104.606667],
+ *     "user": {
+ *       "name": "jesse",
+ *       "followers_count": 2
+ *     }
+ *   }
+ * ]}</pre>
+ * This code implements the parser for the above structure: <pre>   {@code
+ *
+ *   public List<Message> readJsonStream(InputStream in) throws IOException {
+ *     JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
+ *     return readMessagesArray(reader);
+ *   }
+ *
+ *   public List<Message> readMessagesArray(JsonReader reader) throws IOException {
+ *     List<Message> messages = new ArrayList<Message>();
+ *
+ *     reader.beginArray();
+ *     while (reader.hasNext()) {
+ *       messages.add(readMessage(reader));
+ *     }
+ *     reader.endArray();
+ *     return messages;
+ *   }
+ *
+ *   public Message readMessage(JsonReader reader) throws IOException {
+ *     long id = -1;
+ *     String text = null;
+ *     User user = null;
+ *     List<Double> geo = null;
+ *
+ *     reader.beginObject();
+ *     while (reader.hasNext()) {
+ *       String name = reader.nextName();
+ *       if (name.equals("id")) {
+ *         id = reader.nextLong();
+ *       } else if (name.equals("text")) {
+ *         text = reader.nextString();
+ *       } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
+ *         geo = readDoublesArray(reader);
+ *       } else if (name.equals("user")) {
+ *         user = readUser(reader);
+ *       } else {
+ *         reader.skipValue();
+ *       }
+ *     }
+ *     reader.endObject();
+ *     return new Message(id, text, user, geo);
+ *   }
+ *
+ *   public List<Double> readDoublesArray(JsonReader reader) throws IOException {
+ *     List<Double> doubles = new ArrayList<Double>();
+ *
+ *     reader.beginArray();
+ *     while (reader.hasNext()) {
+ *       doubles.add(reader.nextDouble());
+ *     }
+ *     reader.endArray();
+ *     return doubles;
+ *   }
+ *
+ *   public User readUser(JsonReader reader) throws IOException {
+ *     String username = null;
+ *     int followersCount = -1;
+ *
+ *     reader.beginObject();
+ *     while (reader.hasNext()) {
+ *       String name = reader.nextName();
+ *       if (name.equals("name")) {
+ *         username = reader.nextString();
+ *       } else if (name.equals("followers_count")) {
+ *         followersCount = reader.nextInt();
+ *       } else {
+ *         reader.skipValue();
+ *       }
+ *     }
+ *     reader.endObject();
+ *     return new User(username, followersCount);
+ *   }}</pre>
+ *
+ * <h3>Number Handling</h3>
+ * This reader permits numeric values to be read as strings and string values to
+ * be read as numbers. For example, both elements of the JSON array {@code
+ * [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}.
+ * This behavior is intended to prevent lossy numeric conversions: double is
+ * JavaScript's only numeric type and very large values like {@code
+ * 9007199254740993} cannot be represented exactly on that platform. To minimize
+ * precision loss, extremely large values should be written and read as strings
+ * in JSON.
+ *
+ * <p>Each {@code JsonReader} may be used to read a single JSON stream. Instances
+ * of this class are not thread safe.
+ */
+public final class JsonReader implements Closeable {
+
+    /** The input JSON. */
+    private final Reader in;
+
+    /** True to accept non-spec compliant JSON */
+    private boolean lenient = false;
+
+    /**
+     * Use a manual buffer to easily read and unread upcoming characters, and
+     * also so we can create strings without an intermediate StringBuilder.
+     */
+    private final char[] buffer = new char[1024];
+    private int pos = 0;
+    private int limit = 0;
+
+    private final List<JsonScope> stack = new ArrayList<JsonScope>();
+    {
+        push(JsonScope.EMPTY_DOCUMENT);
+    }
+
+    /**
+     * True if we've already read the next token. If we have, the string value
+     * for that token will be assigned to {@code value} if such a string value
+     * exists. And the token type will be assigned to {@code token} if the token
+     * type is known. The token type may be null for literals, since we derive
+     * that lazily.
+     */
+    private boolean hasToken;
+
+    /**
+     * The type of the next token to be returned by {@link #peek} and {@link
+     * #advance}, or {@code null} if it is unknown and must first be derived
+     * from {@code value}. This value is undefined if {@code hasToken} is false.
+     */
+    private JsonToken token;
+
+    /** The text of the next name. */
+    private String name;
+
+    /** The text of the next literal value. */
+    private String value;
+
+    /** True if we're currently handling a skipValue() call. */
+    private boolean skipping = false;
+
+    /**
+     * Creates a new instance that reads a JSON-encoded stream from {@code in}.
+     */
+    public JsonReader(Reader in) {
+        if (in == null) {
+            throw new NullPointerException("in == null");
+        }
+        this.in = in;
+    }
+
+    /**
+     * Configure this parser to be  be liberal in what it accepts. By default,
+     * this parser is strict and only accepts JSON as specified by <a
+     * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the
+     * parser to lenient causes it to ignore the following syntax errors:
+     *
+     * <ul>
+     *   <li>End of line comments starting with {@code //} or {@code #} and
+     *       ending with a newline character.
+     *   <li>C-style comments starting with {@code /*} and ending with
+     *       {@code *}{@code /}. Such comments may not be nested.
+     *   <li>Names that are unquoted or {@code 'single quoted'}.
+     *   <li>Strings that are unquoted or {@code 'single quoted'}.
+     *   <li>Array elements separated by {@code ;} instead of {@code ,}.
+     *   <li>Unnecessary array separators. These are interpreted as if null
+     *       was the omitted value.
+     *   <li>Names and values separated by {@code =} or {@code =>} instead of
+     *       {@code :}.
+     *   <li>Name/value pairs separated by {@code ;} instead of {@code ,}.
+     * </ul>
+     */
+    public void setLenient(boolean lenient) {
+        this.lenient = lenient;
+    }
+
+    /**
+     * Consumes the next token from the JSON stream and asserts that it is the
+     * beginning of a new array.
+     */
+    public void beginArray() throws IOException {
+        expect(JsonToken.BEGIN_ARRAY);
+    }
+
+    /**
+     * Consumes the next token from the JSON stream and asserts that it is the
+     * end of the current array.
+     */
+    public void endArray() throws IOException {
+        expect(JsonToken.END_ARRAY);
+    }
+
+    /**
+     * Consumes the next token from the JSON stream and asserts that it is the
+     * beginning of a new object.
+     */
+    public void beginObject() throws IOException {
+        expect(JsonToken.BEGIN_OBJECT);
+    }
+
+    /**
+     * Consumes the next token from the JSON stream and asserts that it is the
+     * end of the current array.
+     */
+    public void endObject() throws IOException {
+        expect(JsonToken.END_OBJECT);
+    }
+
+    /**
+     * Consumes {@code expected}.
+     */
+    private void expect(JsonToken expected) throws IOException {
+        quickPeek();
+        if (token != expected) {
+            throw new IllegalStateException("Expected " + expected + " but was " + peek());
+        }
+        advance();
+    }
+
+    /**
+     * Returns true if the current array or object has another element.
+     */
+    public boolean hasNext() throws IOException {
+        quickPeek();
+        return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY;
+    }
+
+    /**
+     * Returns the type of the next token without consuming it.
+     */
+    public JsonToken peek() throws IOException {
+        quickPeek();
+
+        if (token == null) {
+            decodeLiteral();
+        }
+
+        return token;
+    }
+
+    /**
+     * Ensures that a token is ready. After this call either {@code token} or
+     * {@code value} will be non-null. To ensure {@code token} has a definitive
+     * value, use {@link #peek()}
+     */
+    private JsonToken quickPeek() throws IOException {
+        if (hasToken) {
+            return token;
+        }
+
+        switch (peekStack()) {
+            case EMPTY_DOCUMENT:
+                replaceTop(JsonScope.NONEMPTY_DOCUMENT);
+                JsonToken firstToken = nextValue();
+                if (token != JsonToken.BEGIN_ARRAY && token != JsonToken.BEGIN_OBJECT) {
+                    throw new IOException(
+                            "Expected JSON document to start with '[' or '{' but was " + token);
+                }
+                return firstToken;
+            case EMPTY_ARRAY:
+                return nextInArray(true);
+            case NONEMPTY_ARRAY:
+                return nextInArray(false);
+            case EMPTY_OBJECT:
+                return nextInObject(true);
+            case DANGLING_NAME:
+                return objectValue();
+            case NONEMPTY_OBJECT:
+                return nextInObject(false);
+            case NONEMPTY_DOCUMENT:
+                hasToken = true;
+                return token = JsonToken.END_DOCUMENT;
+            case CLOSED:
+                throw new IllegalStateException("JsonReader is closed");
+            default:
+                throw new AssertionError();
+        }
+    }
+
+    /**
+     * Advances the cursor in the JSON stream to the next token.
+     */
+    private JsonToken advance() throws IOException {
+        quickPeek();
+
+        JsonToken result = token;
+        hasToken = false;
+        token = null;
+        value = null;
+        name = null;
+        return result;
+    }
+
+    /**
+     * Returns the next token, a {@link JsonToken#NAME property name}, and
+     * consumes it.
+     *
+     * @throws IOException if the next token in the stream is not a property
+     *     name.
+     */
+    public String nextName() throws IOException {
+        quickPeek();
+        if (token != JsonToken.NAME) {
+            throw new IllegalStateException("Expected a name but was " + peek());
+        }
+        String result = name;
+        advance();
+        return result;
+    }
+
+    /**
+     * Returns the {@link JsonToken#STRING string} value of the next token,
+     * consuming it. If the next token is a number, this method will return its
+     * string form.
+     *
+     * @throws IllegalStateException if the next token is not a string or if
+     *     this reader is closed.
+     */
+    public String nextString() throws IOException {
+        peek();
+        if (value == null || (token != JsonToken.STRING && token != JsonToken.NUMBER)) {
+            throw new IllegalStateException("Expected a string but was " + peek());
+        }
+
+        String result = value;
+        advance();
+        return result;
+    }
+
+    /**
+     * Returns the {@link JsonToken#BOOLEAN boolean} value of the next token,
+     * consuming it.
+     *
+     * @throws IllegalStateException if the next token is not a boolean or if
+     *     this reader is closed.
+     */
+    public boolean nextBoolean() throws IOException {
+        quickPeek();
+        if (value == null || token == JsonToken.STRING) {
+            throw new IllegalStateException("Expected a boolean but was " + peek());
+        }
+
+        boolean result;
+        if (value.equalsIgnoreCase("true")) {
+            result = true;
+        } else if (value.equalsIgnoreCase("false")) {
+            result = false;
+        } else {
+            throw new IllegalStateException("Not a boolean: " + value);
+        }
+
+        advance();
+        return result;
+    }
+
+    /**
+     * Consumes the next token from the JSON stream and asserts that it is a
+     * literal null.
+     *
+     * @throws IllegalStateException if the next token is not null or if this
+     *     reader is closed.
+     */
+    public void nextNull() throws IOException {
+        quickPeek();
+        if (value == null || token == JsonToken.STRING) {
+            throw new IllegalStateException("Expected null but was " + peek());
+        }
+
+        if (!value.equalsIgnoreCase("null")) {
+            throw new IllegalStateException("Not a null: " + value);
+        }
+
+        advance();
+    }
+
+    /**
+     * Returns the {@link JsonToken#NUMBER double} value of the next token,
+     * consuming it. If the next token is a string, this method will attempt to
+     * parse it as a double.
+     *
+     * @throws IllegalStateException if the next token is not a literal value.
+     * @throws NumberFormatException if the next literal value cannot be parsed
+     *     as a double, or is non-finite.
+     */
+    public double nextDouble() throws IOException {
+        quickPeek();
+        if (value == null) {
+            throw new IllegalStateException("Expected a double but was " + peek());
+        }
+
+        double result = Double.parseDouble(value);
+
+        if ((result >= 1.0d && value.startsWith("0"))
+                || Double.isNaN(result)
+                || Double.isInfinite(result)) {
+            throw new NumberFormatException(
+                    "JSON forbids octal prefixes, NaN and infinities: " + value);
+        }
+
+        advance();
+        return result;
+    }
+
+    /**
+     * Returns the {@link JsonToken#NUMBER long} value of the next token,
+     * consuming it. If the next token is a string, this method will attempt to
+     * parse it as a long. If the next token's numeric value cannot be exactly
+     * represented by a Java {@code long}, this method throws.
+     *
+     * @throws IllegalStateException if the next token is not a literal value.
+     * @throws NumberFormatException if the next literal value cannot be parsed
+     *     as a number, or exactly represented as a long.
+     */
+    public long nextLong() throws IOException {
+        quickPeek();
+        if (value == null) {
+            throw new IllegalStateException("Expected a long but was " + peek());
+        }
+
+        long result;
+        try {
+            result = Long.parseLong(value);
+        } catch (NumberFormatException ignored) {
+            double asDouble = Double.parseDouble(value); // don't catch this NumberFormatException
+            result = (long) asDouble;
+            if ((double) result != asDouble) {
+                throw new NumberFormatException(value);
+            }
+        }
+
+        if (result >= 1L && value.startsWith("0")) {
+            throw new NumberFormatException("JSON forbids octal prefixes: " + value);
+        }
+
+        advance();
+        return result;
+    }
+
+    /**
+     * Returns the {@link JsonToken#NUMBER int} value of the next token,
+     * consuming it. If the next token is a string, this method will attempt to
+     * parse it as an int. If the next token's numeric value cannot be exactly
+     * represented by a Java {@code int}, this method throws.
+     *
+     * @throws IllegalStateException if the next token is not a literal value.
+     * @throws NumberFormatException if the next literal value cannot be parsed
+     *     as a number, or exactly represented as an int.
+     */
+    public int nextInt() throws IOException {
+        quickPeek();
+        if (value == null) {
+            throw new IllegalStateException("Expected an int but was " + peek());
+        }
+
+        int result;
+        try {
+            result = Integer.parseInt(value);
+        } catch (NumberFormatException ignored) {
+            double asDouble = Double.parseDouble(value); // don't catch this NumberFormatException
+            result = (int) asDouble;
+            if ((double) result != asDouble) {
+                throw new NumberFormatException(value);
+            }
+        }
+
+        if (result >= 1L && value.startsWith("0")) {
+            throw new NumberFormatException("JSON forbids octal prefixes: " + value);
+        }
+
+        advance();
+        return result;
+    }
+
+    /**
+     * Closes this JSON reader and the underlying {@link Reader}.
+     */
+    public void close() throws IOException {
+        hasToken = false;
+        value = null;
+        token = null;
+        stack.clear();
+        stack.add(JsonScope.CLOSED);
+        in.close();
+    }
+
+    /**
+     * Skips the next value recursively. If it is an object or array, all nested
+     * elements are skipped. This method is intended for use when the JSON token
+     * stream contains unrecognized or unhandled values.
+     */
+    public void skipValue() throws IOException {
+        skipping = true;
+        try {
+            int count = 0;
+            do {
+                JsonToken token = advance();
+                if (token == JsonToken.BEGIN_ARRAY || token == JsonToken.BEGIN_OBJECT) {
+                    count++;
+                } else if (token == JsonToken.END_ARRAY || token == JsonToken.END_OBJECT) {
+                    count--;
+                }
+            } while (count != 0);
+        } finally {
+            skipping = false;
+        }
+    }
+
+    private JsonScope peekStack() {
+        return stack.get(stack.size() - 1);
+    }
+
+    private JsonScope pop() {
+        return stack.remove(stack.size() - 1);
+    }
+
+    private void push(JsonScope newTop) {
+        stack.add(newTop);
+    }
+
+    /**
+     * Replace the value on the top of the stack with the given value.
+     */
+    private void replaceTop(JsonScope newTop) {
+        stack.set(stack.size() - 1, newTop);
+    }
+
+    private JsonToken nextInArray(boolean firstElement) throws IOException {
+        if (firstElement) {
+            replaceTop(JsonScope.NONEMPTY_ARRAY);
+        } else {
+            /* Look for a comma before each element after the first element. */
+            switch (nextNonWhitespace()) {
+                case ']':
+                    pop();
+                    hasToken = true;
+                    return token = JsonToken.END_ARRAY;
+                case ';':
+                    checkLenient(); // fall-through
+                case ',':
+                    break;
+                default:
+                    throw syntaxError("Unterminated array");
+            }
+        }
+
+        switch (nextNonWhitespace()) {
+            case ']':
+                if (firstElement) {
+                    pop();
+                    hasToken = true;
+                    return token = JsonToken.END_ARRAY;
+                }
+                // fall-through to handle ",]"
+            case ';':
+            case ',':
+                /* In lenient mode, a 0-length literal means 'null' */
+                checkLenient();
+                pos--;
+                hasToken = true;
+                value = "null";
+                return token = JsonToken.NULL;
+            default:
+                pos--;
+                return nextValue();
+        }
+    }
+
+    private JsonToken nextInObject(boolean firstElement) throws IOException {
+        /*
+         * Read delimiters. Either a comma/semicolon separating this and the
+         * previous name-value pair, or a close brace to denote the end of the
+         * object.
+         */
+        if (firstElement) {
+            /* Peek to see if this is the empty object. */
+            switch (nextNonWhitespace()) {
+                case '}':
+                    pop();
+                    hasToken = true;
+                    return token = JsonToken.END_OBJECT;
+                default:
+                    pos--;
+            }
+        } else {
+            switch (nextNonWhitespace()) {
+                case '}':
+                    pop();
+                    hasToken = true;
+                    return token = JsonToken.END_OBJECT;
+                case ';':
+                case ',':
+                    break;
+                default:
+                    throw syntaxError("Unterminated object");
+            }
+        }
+
+        /* Read the name. */
+        int quote = nextNonWhitespace();
+        switch (quote) {
+            case '\'':
+                checkLenient(); // fall-through
+            case '"':
+                name = nextString((char) quote);
+                break;
+            default:
+                checkLenient();
+                pos--;
+                name = nextLiteral();
+                if (name.isEmpty()) {
+                    throw syntaxError("Expected name");
+                }
+        }
+
+        replaceTop(JsonScope.DANGLING_NAME);
+        hasToken = true;
+        return token = JsonToken.NAME;
+    }
+
+    private JsonToken objectValue() throws IOException {
+        /*
+         * Read the name/value separator. Usually a colon ':'. In lenient mode
+         * we also accept an equals sign '=', or an arrow "=>".
+         */
+        switch (nextNonWhitespace()) {
+            case ':':
+                break;
+            case '=':
+                checkLenient();
+                if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') {
+                    pos++;
+                }
+                break;
+            default:
+                throw syntaxError("Expected ':'");
+        }
+
+        replaceTop(JsonScope.NONEMPTY_OBJECT);
+        return nextValue();
+    }
+
+    private JsonToken nextValue() throws IOException {
+        int c = nextNonWhitespace();
+        switch (c) {
+            case '{':
+                push(JsonScope.EMPTY_OBJECT);
+                hasToken = true;
+                return token = JsonToken.BEGIN_OBJECT;
+
+            case '[':
+                push(JsonScope.EMPTY_ARRAY);
+                hasToken = true;
+                return token = JsonToken.BEGIN_ARRAY;
+
+            case '\'':
+                checkLenient(); // fall-through
+            case '"':
+                value = nextString((char) c);
+                hasToken = true;
+                return token = JsonToken.STRING;
+
+            default:
+                pos--;
+                return readLiteral();
+        }
+    }
+
+    /**
+     * Returns true once {@code limit - pos >= minimum}. If the data is
+     * exhausted before that many characters are available, this returns
+     * false.
+     */
+    private boolean fillBuffer(int minimum) throws IOException {
+        if (limit != pos) {
+            limit -= pos;
+            System.arraycopy(buffer, pos, buffer, 0, limit);
+        } else {
+            limit = 0;
+        }
+
+        pos = 0;
+        int total;
+        while ((total = in.read(buffer, limit, buffer.length - limit)) != -1) {
+            limit += total;
+            if (limit >= minimum) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private int nextNonWhitespace() throws IOException {
+        while (pos < limit || fillBuffer(1)) {
+            int c = buffer[pos++];
+            switch (c) {
+                case '\t':
+                case ' ':
+                case '\n':
+                case '\r':
+                    continue;
+
+                case '/':
+                    if (pos == limit && !fillBuffer(1)) {
+                        return c;
+                    }
+
+                    checkLenient();
+                    char peek = buffer[pos];
+                    switch (peek) {
+                        case '*':
+                            // skip a /* c-style comment */
+                            pos++;
+                            if (!skipTo("*/")) {
+                                throw syntaxError("Unterminated comment");
+                            }
+                            pos += 2;
+                            continue;
+
+                        case '/':
+                            // skip a // end-of-line comment
+                            pos++;
+                            skipToEndOfLine();
+                            continue;
+
+                        default:
+                            return c;
+                    }
+
+                case '#':
+                    /*
+                     * Skip a # hash end-of-line comment. The JSON RFC doesn't
+                     * specify this behaviour, but it's required to parse
+                     * existing documents. See http://b/2571423.
+                     */
+                    checkLenient();
+                    skipToEndOfLine();
+                    continue;
+
+                default:
+                    return c;
+            }
+        }
+
+        throw syntaxError("End of input");
+    }
+
+    private void checkLenient() throws IOException {
+        if (!lenient) {
+            throw syntaxError("Use JsonReader.setLenient(true) to accept malformed JSON");
+        }
+    }
+
+    /**
+     * Advances the position until after the next newline character. If the line
+     * is terminated by "\r\n", the '\n' must be consumed as whitespace by the
+     * caller.
+     */
+    private void skipToEndOfLine() throws IOException {
+        while (pos < limit || fillBuffer(1)) {
+            char c = buffer[pos++];
+            if (c == '\r' || c == '\n') {
+                break;
+            }
+        }
+    }
+
+    private boolean skipTo(String toFind) throws IOException {
+        outer:
+        for (; pos + toFind.length() < limit || fillBuffer(toFind.length()); pos++) {
+            for (int c = 0; c < toFind.length(); c++) {
+                if (buffer[pos + c] != toFind.charAt(c)) {
+                    continue outer;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the string up to but not including {@code quote}, unescaping any
+     * character escape sequences encountered along the way. The opening quote
+     * should have already been read. This consumes the closing quote, but does
+     * not include it in the returned string.
+     *
+     * @param quote either ' or ".
+     * @throws NumberFormatException if any unicode escape sequences are
+     *     malformed.
+     */
+    private String nextString(char quote) throws IOException {
+        StringBuilder builder = null;
+        do {
+            /* the index of the first character not yet appended to the builder. */
+            int start = pos;
+            while (pos < limit) {
+                int c = buffer[pos++];
+
+                if (c == quote) {
+                    if (skipping) {
+                        return "skipped!";
+                    } else if (builder == null) {
+                        return new String(buffer, start, pos - start - 1);
+                    } else {
+                        builder.append(buffer, start, pos - start - 1);
+                        return builder.toString();
+                    }
+
+                } else if (c == '\\') {
+                    if (builder == null) {
+                        builder = new StringBuilder();
+                    }
+                    builder.append(buffer, start, pos - start - 1);
+                    builder.append(readEscapeCharacter());
+                    start = pos;
+                }
+            }
+
+            if (builder == null) {
+                builder = new StringBuilder();
+            }
+            builder.append(buffer, start, pos - start);
+        } while (fillBuffer(1));
+
+        throw syntaxError("Unterminated string");
+    }
+
+    /**
+     * Returns the string up to but not including any delimiter characters. This
+     * does not consume the delimiter character.
+     */
+    private String nextLiteral() throws IOException {
+        StringBuilder builder = null;
+        do {
+            /* the index of the first character not yet appended to the builder. */
+            int start = pos;
+            while (pos < limit) {
+                int c = buffer[pos++];
+                switch (c) {
+                    case '/':
+                    case '\\':
+                    case ';':
+                    case '#':
+                    case '=':
+                        checkLenient(); // fall-through
+
+                    case '{':
+                    case '}':
+                    case '[':
+                    case ']':
+                    case ':':
+                    case ',':
+                    case ' ':
+                    case '\t':
+                    case '\f':
+                    case '\r':
+                    case '\n':
+                        pos--;
+                        if (skipping) {
+                            return "skipped!";
+                        } else if (builder == null) {
+                            return new String(buffer, start, pos - start);
+                        } else {
+                            builder.append(buffer, start, pos - start);
+                            return builder.toString();
+                        }
+                }
+            }
+
+            if (builder == null) {
+                builder = new StringBuilder();
+            }
+            builder.append(buffer, start, pos - start);
+        } while (fillBuffer(1));
+
+        return builder.toString();
+    }
+
+    @Override public String toString() {
+        return getClass().getSimpleName() + " near " + getSnippet();
+    }
+
+    /**
+     * Unescapes the character identified by the character or characters that
+     * immediately follow a backslash. The backslash '\' should have already
+     * been read. This supports both unicode escapes "u000A" and two-character
+     * escapes "\n".
+     *
+     * @throws NumberFormatException if any unicode escape sequences are
+     *     malformed.
+     */
+    private char readEscapeCharacter() throws IOException {
+        if (pos == limit && !fillBuffer(1)) {
+            throw syntaxError("Unterminated escape sequence");
+        }
+
+        char escaped = buffer[pos++];
+        switch (escaped) {
+            case 'u':
+                if (pos + 4 > limit && !fillBuffer(4)) {
+                    throw syntaxError("Unterminated escape sequence");
+                }
+                String hex = new String(buffer, pos, 4);
+                pos += 4;
+                return (char) Integer.parseInt(hex, 16);
+
+            case 't':
+                return '\t';
+
+            case 'b':
+                return '\b';
+
+            case 'n':
+                return '\n';
+
+            case 'r':
+                return '\r';
+
+            case 'f':
+                return '\f';
+
+            case '\'':
+            case '"':
+            case '\\':
+            default:
+                return escaped;
+        }
+    }
+
+    /**
+     * Reads a null, boolean, numeric or unquoted string literal value.
+     */
+    private JsonToken readLiteral() throws IOException {
+        String literal = nextLiteral();
+        if (literal.isEmpty()) {
+            throw syntaxError("Expected literal value");
+        }
+        value = literal;
+        hasToken = true;
+        return token = null; // use decodeLiteral() to get the token type
+    }
+
+    /**
+     * Assigns {@code nextToken} based on the value of {@code nextValue}.
+     */
+    private void decodeLiteral() throws IOException {
+        if (value.equalsIgnoreCase("null")) {
+            token = JsonToken.NULL;
+        } else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
+            token = JsonToken.BOOLEAN;
+        } else {
+            try {
+                Double.parseDouble(value); // this work could potentially be cached
+                token = JsonToken.NUMBER;
+            } catch (NumberFormatException ignored) {
+                // this must be an unquoted string
+                checkLenient();
+                token = JsonToken.STRING;
+            }
+        }
+    }
+
+    /**
+     * Throws a new IO exception with the given message and a context snippet
+     * with this reader's content.
+     */
+    public IOException syntaxError(String message) throws IOException {
+        throw new JsonSyntaxException(message + " near " + getSnippet());
+    }
+
+    private CharSequence getSnippet() {
+        StringBuilder snippet = new StringBuilder();
+        int beforePos = Math.min(pos, 20);
+        snippet.append(buffer, pos - beforePos, beforePos);
+        int afterPos = Math.min(limit - pos, 20);
+        snippet.append(buffer, pos, afterPos);
+        return snippet;
+    }
+
+    private static class JsonSyntaxException extends IOException {
+        private JsonSyntaxException(String s) {
+            super(s);
+        }
+    }
+}
diff --git a/core/java/android/util/JsonScope.java b/core/java/android/util/JsonScope.java
new file mode 100644
index 0000000..ca534e9
--- /dev/null
+++ b/core/java/android/util/JsonScope.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Lexical scoping elements within a JSON reader or writer.
+ */
+enum JsonScope {
+
+    /**
+     * An array with no elements requires no separators or newlines before
+     * it is closed.
+     */
+    EMPTY_ARRAY,
+
+    /**
+     * A array with at least one value requires a comma and newline before
+     * the next element.
+     */
+    NONEMPTY_ARRAY,
+
+    /**
+     * An object with no name/value pairs requires no separators or newlines
+     * before it is closed.
+     */
+    EMPTY_OBJECT,
+
+    /**
+     * An object whose most recent element is a key. The next element must
+     * be a value.
+     */
+    DANGLING_NAME,
+
+    /**
+     * An object with at least one name/value pair requires a comma and
+     * newline before the next element.
+     */
+    NONEMPTY_OBJECT,
+
+    /**
+     * No object or array has been started.
+     */
+    EMPTY_DOCUMENT,
+
+    /**
+     * A document with at an array or object.
+     */
+    NONEMPTY_DOCUMENT,
+
+    /**
+     * A document that's been closed and cannot be accessed.
+     */
+    CLOSED,
+}
diff --git a/core/java/android/util/JsonToken.java b/core/java/android/util/JsonToken.java
new file mode 100644
index 0000000..45bc6ca
--- /dev/null
+++ b/core/java/android/util/JsonToken.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * A structure, name or value type in a JSON-encoded string.
+ */
+public enum JsonToken {
+
+    /**
+     * The opening of a JSON array. Written using {@link JsonWriter#beginObject}
+     * and read using {@link JsonReader#beginObject}.
+     */
+    BEGIN_ARRAY,
+
+    /**
+     * The closing of a JSON array. Written using {@link JsonWriter#endArray}
+     * and read using {@link JsonReader#endArray}.
+     */
+    END_ARRAY,
+
+    /**
+     * The opening of a JSON object. Written using {@link JsonWriter#beginObject}
+     * and read using {@link JsonReader#beginObject}.
+     */
+    BEGIN_OBJECT,
+
+    /**
+     * The closing of a JSON object. Written using {@link JsonWriter#endObject}
+     * and read using {@link JsonReader#endObject}.
+     */
+    END_OBJECT,
+
+    /**
+     * A JSON property name. Within objects, tokens alternate between names and
+     * their values. Written using {@link JsonWriter#name} and read using {@link
+     * JsonReader#nextName}
+     */
+    NAME,
+
+    /**
+     * A JSON string.
+     */
+    STRING,
+
+    /**
+     * A JSON number represented in this API by a Java {@code double}, {@code
+     * long}, or {@code int}.
+     */
+    NUMBER,
+
+    /**
+     * A JSON {@code true} or {@code false}.
+     */
+    BOOLEAN,
+
+    /**
+     * A JSON {@code null}.
+     */
+    NULL,
+
+    /**
+     * The end of the JSON stream. This sentinel value is returned by {@link
+     * JsonReader#peek()} to signal that the JSON-encoded value has no more
+     * tokens.
+     */
+    END_DOCUMENT
+}
diff --git a/core/java/android/util/JsonWriter.java b/core/java/android/util/JsonWriter.java
new file mode 100644
index 0000000..89cad79
--- /dev/null
+++ b/core/java/android/util/JsonWriter.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Writes a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
+ * encoded value to a stream, one token at a time. The stream includes both
+ * literal values (strings, numbers, booleans and nulls) as well as the begin
+ * and end delimiters of objects and arrays.
+ *
+ * <h3>Encoding JSON</h3>
+ * To encode your data as JSON, create a new {@code JsonWriter}. Each JSON
+ * document must contain one top-level array or object. Call methods on the
+ * writer as you walk the structure's contents, nesting arrays and objects as
+ * necessary:
+ * <ul>
+ *   <li>To write <strong>arrays</strong>, first call {@link #beginArray()}.
+ *       Write each of the array's elements with the appropriate {@link #value}
+ *       methods or by nesting other arrays and objects. Finally close the array
+ *       using {@link #endArray()}.
+ *   <li>To write <strong>objects</strong>, first call {@link #beginObject()}.
+ *       Write each of the object's properties by alternating calls to
+ *       {@link #name} with the property's value. Write property values with the
+ *       appropriate {@link #value} method or by nesting other objects or arrays.
+ *       Finally close the object using {@link #endObject()}.
+ * </ul>
+ *
+ * <h3>Example</h3>
+ * Suppose we'd like to encode a stream of messages such as the following: <pre> {@code
+ * [
+ *   {
+ *     "id": 912345678901,
+ *     "text": "How do I write JSON on Android?",
+ *     "geo": null,
+ *     "user": {
+ *       "name": "android_newb",
+ *       "followers_count": 41
+ *      }
+ *   },
+ *   {
+ *     "id": 912345678902,
+ *     "text": "@android_newb just use android.util.JsonWriter!",
+ *     "geo": [50.454722, -104.606667],
+ *     "user": {
+ *       "name": "jesse",
+ *       "followers_count": 2
+ *     }
+ *   }
+ * ]}</pre>
+ * This code encodes the above structure: <pre>   {@code
+ *   public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException {
+ *     JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
+ *     writer.setIndent("  ");
+ *     writeMessagesArray(writer, messages);
+ *     writer.close();
+ *   }
+ *
+ *   public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException {
+ *     writer.beginArray();
+ *     for (Message message : messages) {
+ *       writeMessage(writer, message);
+ *     }
+ *     writer.endArray();
+ *   }
+ *
+ *   public void writeMessage(JsonWriter writer, Message message) throws IOException {
+ *     writer.beginObject();
+ *     writer.name("id").value(message.getId());
+ *     writer.name("text").value(message.getText());
+ *     if (message.getGeo() != null) {
+ *       writer.name("geo");
+ *       writeDoublesArray(writer, message.getGeo());
+ *     } else {
+ *       writer.name("geo").nullValue();
+ *     }
+ *     writer.name("user");
+ *     writeUser(writer, message.getUser());
+ *     writer.endObject();
+ *   }
+ *
+ *   public void writeUser(JsonWriter writer, User user) throws IOException {
+ *     writer.beginObject();
+ *     writer.name("name").value(user.getName());
+ *     writer.name("followers_count").value(user.getFollowersCount());
+ *     writer.endObject();
+ *   }
+ *
+ *   public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
+ *     writer.beginArray();
+ *     for (Double value : doubles) {
+ *       writer.value(value);
+ *     }
+ *     writer.endArray();
+ *   }}</pre>
+ *
+ * <p>Each {@code JsonWriter} may be used to write a single JSON stream.
+ * Instances of this class are not thread safe. Calls that would result in a
+ * malformed JSON string will fail with an {@link IllegalStateException}.
+ */
+public final class JsonWriter implements Closeable {
+
+    /** The output data, containing at most one top-level array or object. */
+    private final Writer out;
+
+    private final List<JsonScope> stack = new ArrayList<JsonScope>();
+    {
+        stack.add(JsonScope.EMPTY_DOCUMENT);
+    }
+
+    /**
+     * A string containing a full set of spaces for a single level of
+     * indentation, or null for no pretty printing.
+     */
+    private String indent;
+
+    /**
+     * The name/value separator; either ":" or ": ".
+     */
+    private String separator = ":";
+
+    /**
+     * Creates a new instance that writes a JSON-encoded stream to {@code out}.
+     * For best performance, ensure {@link Writer} is buffered; wrapping in
+     * {@link java.io.BufferedWriter BufferedWriter} if necessary.
+     */
+    public JsonWriter(Writer out) {
+        if (out == null) {
+            throw new NullPointerException("out == null");
+        }
+        this.out = out;
+    }
+
+    /**
+     * Sets the indentation string to be repeated for each level of indentation
+     * in the encoded document. If {@code indent.isEmpty()} the encoded document
+     * will be compact. Otherwise the encoded document will be more
+     * human-readable.
+     *
+     * @param indent a string containing only whitespace.
+     */
+    public void setIndent(String indent) {
+        if (indent.isEmpty()) {
+            this.indent = null;
+            this.separator = ":";
+        } else {
+            this.indent = indent;
+            this.separator = ": ";
+        }
+    }
+
+    /**
+     * Begins encoding a new array. Each call to this method must be paired with
+     * a call to {@link #endArray}.
+     *
+     * @return this writer.
+     */
+    public JsonWriter beginArray() throws IOException {
+        return open(JsonScope.EMPTY_ARRAY, "[");
+    }
+
+    /**
+     * Ends encoding the current array.
+     *
+     * @return this writer.
+     */
+    public JsonWriter endArray() throws IOException {
+        return close(JsonScope.EMPTY_ARRAY, JsonScope.NONEMPTY_ARRAY, "]");
+    }
+
+    /**
+     * Begins encoding a new object. Each call to this method must be paired
+     * with a call to {@link #endObject}.
+     *
+     * @return this writer.
+     */
+    public JsonWriter beginObject() throws IOException {
+        return open(JsonScope.EMPTY_OBJECT, "{");
+    }
+
+    /**
+     * Ends encoding the current object.
+     *
+     * @return this writer.
+     */
+    public JsonWriter endObject() throws IOException {
+        return close(JsonScope.EMPTY_OBJECT, JsonScope.NONEMPTY_OBJECT, "}");
+    }
+
+    /**
+     * Enters a new scope by appending any necessary whitespace and the given
+     * bracket.
+     */
+    private JsonWriter open(JsonScope empty, String openBracket) throws IOException {
+        beforeValue(true);
+        stack.add(empty);
+        out.write(openBracket);
+        return this;
+    }
+
+    /**
+     * Closes the current scope by appending any necessary whitespace and the
+     * given bracket.
+     */
+    private JsonWriter close(JsonScope empty, JsonScope nonempty, String closeBracket)
+            throws IOException {
+        JsonScope context = peek();
+        if (context != nonempty && context != empty) {
+            throw new IllegalStateException("Nesting problem: " + stack);
+        }
+
+        stack.remove(stack.size() - 1);
+        if (context == nonempty) {
+            newline();
+        }
+        out.write(closeBracket);
+        return this;
+    }
+
+    /**
+     * Returns the value on the top of the stack.
+     */
+    private JsonScope peek() {
+        return stack.get(stack.size() - 1);
+    }
+
+    /**
+     * Replace the value on the top of the stack with the given value.
+     */
+    private void replaceTop(JsonScope topOfStack) {
+        stack.set(stack.size() - 1, topOfStack);
+    }
+
+    /**
+     * Encodes the property name.
+     *
+     * @param name the name of the forthcoming value. May not be null.
+     * @return this writer.
+     */
+    public JsonWriter name(String name) throws IOException {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+        beforeName();
+        string(name);
+        return this;
+    }
+
+    /**
+     * Encodes {@code value}.
+     *
+     * @param value the literal string value, or null to encode a null literal.
+     * @return this writer.
+     */
+    public JsonWriter value(String value) throws IOException {
+        if (value == null) {
+            return nullValue();
+        }
+        beforeValue(false);
+        string(value);
+        return this;
+    }
+
+    /**
+     * Encodes {@code null}.
+     *
+     * @return this writer.
+     */
+    public JsonWriter nullValue() throws IOException {
+        beforeValue(false);
+        out.write("null");
+        return this;
+    }
+
+    /**
+     * Encodes {@code value}.
+     *
+     * @return this writer.
+     */
+    public JsonWriter value(boolean value) throws IOException {
+        beforeValue(false);
+        out.write(value ? "true" : "false");
+        return this;
+    }
+
+    /**
+     * Encodes {@code value}.
+     *
+     * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
+     *     {@link Double#isInfinite() infinities}.
+     * @return this writer.
+     */
+    public JsonWriter value(double value) throws IOException {
+        if (Double.isNaN(value) || Double.isInfinite(value)) {
+            throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
+        }
+        beforeValue(false);
+        out.append(Double.toString(value));
+        return this;
+    }
+
+    /**
+     * Encodes {@code value}.
+     *
+     * @return this writer.
+     */
+    public JsonWriter value(long value) throws IOException {
+        beforeValue(false);
+        out.write(Long.toString(value));
+        return this;
+    }
+
+    /**
+     * Ensures all buffered data is written to the underlying {@link Writer}
+     * and flushes that writer.
+     */
+    public void flush() throws IOException {
+        out.flush();
+    }
+
+    /**
+     * Flushes and closes this writer and the underlying {@link Writer}.
+     *
+     * @throws IOException if the JSON document is incomplete.
+     */
+    public void close() throws IOException {
+        out.close();
+
+        if (peek() != JsonScope.NONEMPTY_DOCUMENT) {
+            throw new IOException("Incomplete document");
+        }
+    }
+
+    private void string(String value) throws IOException {
+        out.write("\"");
+        for (int i = 0, length = value.length(); i < length; i++) {
+            char c = value.charAt(i);
+
+            /*
+             * From RFC 4627, "All Unicode characters may be placed within the
+             * quotation marks except for the characters that must be escaped:
+             * quotation mark, reverse solidus, and the control characters
+             * (U+0000 through U+001F)."
+             */
+            switch (c) {
+                case '"':
+                case '\\':
+                case '/':
+                    out.write('\\');
+                    out.write(c);
+                    break;
+
+                case '\t':
+                    out.write("\\t");
+                    break;
+
+                case '\b':
+                    out.write("\\b");
+                    break;
+
+                case '\n':
+                    out.write("\\n");
+                    break;
+
+                case '\r':
+                    out.write("\\r");
+                    break;
+
+                case '\f':
+                    out.write("\\f");
+                    break;
+
+                default:
+                    if (c <= 0x1F) {
+                        out.write(String.format("\\u%04x", (int) c));
+                    } else {
+                        out.write(c);
+                    }
+                    break;
+            }
+
+        }
+        out.write("\"");
+    }
+
+    private void newline() throws IOException {
+        if (indent == null) {
+            return;
+        }
+
+        out.write("\n");
+        for (int i = 1; i < stack.size(); i++) {
+            out.write(indent);
+        }
+    }
+
+    /**
+     * Inserts any necessary separators and whitespace before a name. Also
+     * adjusts the stack to expect the name's value.
+     */
+    private void beforeName() throws IOException {
+        JsonScope context = peek();
+        if (context == JsonScope.NONEMPTY_OBJECT) { // first in object
+            out.write(',');
+        } else if (context != JsonScope.EMPTY_OBJECT) { // not in an object!
+            throw new IllegalStateException("Nesting problem: " + stack);
+        }
+        newline();
+        replaceTop(JsonScope.DANGLING_NAME);
+    }
+
+    /**
+     * Inserts any necessary separators and whitespace before a literal value,
+     * inline array, or inline object. Also adjusts the stack to expect either a
+     * closing bracket or another element.
+     *
+     * @param root true if the value is a new array or object, the two values
+     *     permitted as top-level elements.
+     */
+    private void beforeValue(boolean root) throws IOException {
+        switch (peek()) {
+            case EMPTY_DOCUMENT: // first in document
+                if (!root) {
+                    throw new IllegalStateException(
+                            "JSON must start with an array or an object.");
+                }
+                replaceTop(JsonScope.NONEMPTY_DOCUMENT);
+                break;
+
+            case EMPTY_ARRAY: // first in array
+                replaceTop(JsonScope.NONEMPTY_ARRAY);
+                newline();
+                break;
+
+            case NONEMPTY_ARRAY: // another in array
+                out.append(',');
+                newline();
+                break;
+
+            case DANGLING_NAME: // value for name
+                out.append(separator);
+                replaceTop(JsonScope.NONEMPTY_OBJECT);
+                break;
+
+            case NONEMPTY_DOCUMENT:
+                throw new IllegalStateException(
+                        "JSON must have only one top-level value.");
+
+            default:
+                throw new IllegalStateException("Nesting problem: " + stack);
+        }
+    }
+}
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 5cbfd29..3bcd266 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -25,7 +25,7 @@
 public class Patterns {
     /**
      *  Regular expression to match all IANA top-level domains.
-     *  List accurate as of 2010/02/05.  List taken from:
+     *  List accurate as of 2010/05/06.  List taken from:
      *  http://data.iana.org/TLD/tlds-alpha-by-domain.txt
      *  This pattern is auto-generated by frameworks/base/common/tools/make-iana-tld-pattern.py
      */
@@ -53,8 +53,8 @@
         + "|u[agksyz]"
         + "|v[aceginu]"
         + "|w[fs]"
-        + "|(xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)"
-        + "|y[etu]"
+        + "|(xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-mgbaam7a8h|xn\\-\\-mgberp4a5d4ar|xn\\-\\-wgbh1c|xn\\-\\-zckzah)"
+        + "|y[et]"
         + "|z[amw])";
 
     /**
@@ -65,7 +65,7 @@
 
     /**
      *  Regular expression to match all IANA top-level domains for WEB_URL.
-     *  List accurate as of 2010/02/05.  List taken from:
+     *  List accurate as of 2010/05/06.  List taken from:
      *  http://data.iana.org/TLD/tlds-alpha-by-domain.txt
      *  This pattern is auto-generated by frameworks/base/common/tools/make-iana-tld-pattern.py
      */
@@ -94,8 +94,8 @@
         + "|u[agksyz]"
         + "|v[aceginu]"
         + "|w[fs]"
-        + "|(?:xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-zckzah)"
-        + "|y[etu]"
+        + "|(?:xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-80akhbyknj4f|xn\\-\\-9t4b11yi5a|xn\\-\\-deba0ad|xn\\-\\-g6w251d|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-mgbaam7a8h|xn\\-\\-mgberp4a5d4ar|xn\\-\\-wgbh1c|xn\\-\\-zckzah)"
+        + "|y[et]"
         + "|z[amw]))";
 
     /**
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index 1c8b330..7fc43b9 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -90,6 +90,16 @@
         delete(key);
     }
 
+    /**
+     * Removes the mapping at the specified index.
+     */
+    public void removeAt(int index) {
+        if (mValues[index] != DELETED) {
+            mValues[index] = DELETED;
+            mGarbage = true;
+        }
+    }
+    
     private void gc() {
         // Log.e("SparseArray", "gc start with " + mSize);
 
diff --git a/core/java/android/util/StateSet.java b/core/java/android/util/StateSet.java
index f3d8159..21d8e45 100644
--- a/core/java/android/util/StateSet.java
+++ b/core/java/android/util/StateSet.java
@@ -38,6 +38,7 @@
 public class StateSet {
 
     public static final int[] WILD_CARD = new int[0];
+    public static final int[] NOTHING = new int[] { 0 };
 
     /**
      * Return whether the stateSetOrSpec is matched by all StateSets.
diff --git a/core/java/android/util/TimingLogger.java b/core/java/android/util/TimingLogger.java
index 0f39c97..be442da 100644
--- a/core/java/android/util/TimingLogger.java
+++ b/core/java/android/util/TimingLogger.java
@@ -24,22 +24,26 @@
  * A utility class to help log timings splits throughout a method call.
  * Typical usage is:
  *
- * TimingLogger timings = new TimingLogger(TAG, "methodA");
- * ... do some work A ...
- * timings.addSplit("work A");
- * ... do some work B ...
- * timings.addSplit("work B");
- * ... do some work C ...
- * timings.addSplit("work C");
- * timings.dumpToLog();
+ * <pre>
+ *     TimingLogger timings = new TimingLogger(TAG, "methodA");
+ *     // ... do some work A ...
+ *     timings.addSplit("work A");
+ *     // ... do some work B ...
+ *     timings.addSplit("work B");
+ *     // ... do some work C ...
+ *     timings.addSplit("work C");
+ *     timings.dumpToLog();
+ * </pre>
  *
- * The dumpToLog call would add the following to the log:
+ * <p>The dumpToLog call would add the following to the log:</p>
  *
- * D/TAG     ( 3459): methodA: begin
- * D/TAG     ( 3459): methodA:      9 ms, work A
- * D/TAG     ( 3459): methodA:      1 ms, work B
- * D/TAG     ( 3459): methodA:      6 ms, work C
- * D/TAG     ( 3459): methodA: end, 16 ms
+ * <pre>
+ *     D/TAG     ( 3459): methodA: begin
+ *     D/TAG     ( 3459): methodA:      9 ms, work A
+ *     D/TAG     ( 3459): methodA:      1 ms, work B
+ *     D/TAG     ( 3459): methodA:      6 ms, work C
+ *     D/TAG     ( 3459): methodA: end, 16 ms
+ * </pre>
  */
 public class TimingLogger {
 
diff --git a/core/java/android/view/ActionMode.java b/core/java/android/view/ActionMode.java
new file mode 100644
index 0000000..bfafa98
--- /dev/null
+++ b/core/java/android/view/ActionMode.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+
+/**
+ * Represents a contextual mode of the user interface. Action modes can be used for
+ * modal interactions with content and replace parts of the normal UI until finished.
+ * Examples of good action modes include selection modes, search, content editing, etc.
+ */
+public abstract class ActionMode {
+    /**
+     * Set the title of the action mode. This method will have no visible effect if
+     * a custom view has been set.
+     *
+     * @param title Title string to set
+     *
+     * @see #setTitle(int)
+     * @see #setCustomView(View)
+     */
+    public abstract void setTitle(CharSequence title);
+
+    /**
+     * Set the title of the action mode. This method will have no visible effect if
+     * a custom view has been set.
+     *
+     * @param resId Resource ID of a string to set as the title
+     *
+     * @see #setTitle(CharSequence)
+     * @see #setCustomView(View)
+     */
+    public abstract void setTitle(int resId);
+
+    /**
+     * Set the subtitle of the action mode. This method will have no visible effect if
+     * a custom view has been set.
+     *
+     * @param subtitle Subtitle string to set
+     *
+     * @see #setSubtitle(int)
+     * @see #setCustomView(View)
+     */
+    public abstract void setSubtitle(CharSequence subtitle);
+
+    /**
+     * Set the subtitle of the action mode. This method will have no visible effect if
+     * a custom view has been set.
+     *
+     * @param resId Resource ID of a string to set as the subtitle
+     *
+     * @see #setSubtitle(CharSequence)
+     * @see #setCustomView(View)
+     */
+    public abstract void setSubtitle(int resId);
+
+    /**
+     * Set a custom view for this action mode. The custom view will take the place of
+     * the title and subtitle. Useful for things like search boxes.
+     *
+     * @param view Custom view to use in place of the title/subtitle.
+     *
+     * @see #setTitle(CharSequence)
+     * @see #setSubtitle(CharSequence)
+     */
+    public abstract void setCustomView(View view);
+
+    /**
+     * Invalidate the action mode and refresh menu content. The mode's
+     * {@link ActionMode.Callback} will have its
+     * {@link Callback#onPrepareActionMode(ActionMode, Menu)} method called.
+     * If it returns true the menu will be scanned for updated content and any relevant changes
+     * will be reflected to the user.
+     */
+    public abstract void invalidate();
+
+    /**
+     * Finish and close this action mode. The action mode's {@link ActionMode.Callback} will
+     * have its {@link Callback#onDestroyActionMode(ActionMode)} method called.
+     */
+    public abstract void finish();
+
+    /**
+     * Returns the menu of actions that this action mode presents.
+     * @return The action mode's menu.
+     */
+    public abstract Menu getMenu();
+
+    /**
+     * Returns the current title of this action mode.
+     * @return Title text
+     */
+    public abstract CharSequence getTitle();
+
+    /**
+     * Returns the current subtitle of this action mode.
+     * @return Subtitle text
+     */
+    public abstract CharSequence getSubtitle();
+
+    /**
+     * Returns the current custom view for this action mode.
+     * @return The current custom view
+     */
+    public abstract View getCustomView();
+
+    /**
+     * Returns a {@link MenuInflater} with the ActionMode's context.
+     */
+    public abstract MenuInflater getMenuInflater();
+
+    /**
+     * Callback interface for action modes. Supplied to
+     * {@link View#startActionMode(Callback)}, a Callback
+     * configures and handles events raised by a user's interaction with an action mode.
+     *
+     * <p>An action mode's lifecycle is as follows:
+     * <ul>
+     * <li>{@link Callback#onCreateActionMode(ActionMode, Menu)} once on initial
+     * creation</li>
+     * <li>{@link Callback#onPrepareActionMode(ActionMode, Menu)} after creation
+     * and any time the {@link ActionMode} is invalidated</li>
+     * <li>{@link Callback#onActionItemClicked(ActionMode, MenuItem)} any time a
+     * contextual action button is clicked</li>
+     * <li>{@link Callback#onDestroyActionMode(ActionMode)} when the action mode
+     * is closed</li>
+     * </ul>
+     */
+    public interface Callback {
+        /**
+         * Called when action mode is first created. The menu supplied will be used to
+         * generate action buttons for the action mode.
+         *
+         * @param mode ActionMode being created
+         * @param menu Menu used to populate action buttons
+         * @return true if the action mode should be created, false if entering this
+         *              mode should be aborted.
+         */
+        public boolean onCreateActionMode(ActionMode mode, Menu menu);
+
+        /**
+         * Called to refresh an action mode's action menu whenever it is invalidated.
+         *
+         * @param mode ActionMode being prepared
+         * @param menu Menu used to populate action buttons
+         * @return true if the menu or action mode was updated, false otherwise.
+         */
+        public boolean onPrepareActionMode(ActionMode mode, Menu menu);
+
+        /**
+         * Called to report a user click on an action button.
+         *
+         * @param mode The current ActionMode
+         * @param item The item that was clicked
+         * @return true if this callback handled the event, false if the standard MenuItem
+         *          invocation should continue.
+         */
+        public boolean onActionItemClicked(ActionMode mode, MenuItem item);
+
+        /**
+         * Called when an action mode is about to be exited and destroyed.
+         *
+         * @param mode The current ActionMode being destroyed
+         */
+        public void onDestroyActionMode(ActionMode mode);
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
new file mode 100644
index 0000000..e7c2231
--- /dev/null
+++ b/core/java/android/view/DisplayList.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+/**
+ * A display lists records a series of graphics related operation and can replay
+ * them later. Display lists are usually built by recording operations on a
+ * {@link android.graphics.Canvas}. Replaying the operations from a display list
+ * avoids executing views drawing code on every frame, and is thus much more
+ * efficient. 
+ */
+abstract class DisplayList {
+    /**
+     * Starts recording the display list. All operations performed on the
+     * returned canvas are recorded and stored in this display list.
+     * 
+     * @return A canvas to record drawing operations.
+     */
+    abstract HardwareCanvas start();
+
+    /**
+     * Ends the recording for this display list. A display list cannot be
+     * replayed if recording is not finished. 
+     */
+    abstract void end();
+
+    /**
+     * Indicates whether this display list can be replayed or not.
+     * 
+     * @return True if the display list can be replayed, false otherwise.
+     * 
+     * @see android.view.HardwareCanvas#drawDisplayList(DisplayList) 
+     */
+    abstract boolean isReady();
+}
diff --git a/core/java/android/view/DragEvent.aidl b/core/java/android/view/DragEvent.aidl
new file mode 100644
index 0000000..f08943f
--- /dev/null
+++ b/core/java/android/view/DragEvent.aidl
@@ -0,0 +1,19 @@
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+
+package android.view;
+
+parcelable DragEvent;
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
new file mode 100644
index 0000000..bbac14c
--- /dev/null
+++ b/core/java/android/view/DragEvent.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** !!! TODO: real docs */
+public class DragEvent implements Parcelable {
+    private static final boolean TRACK_RECYCLED_LOCATION = false;
+
+    int mAction;
+    float mX, mY;
+    ClipDescription mClipDescription;
+    ClipData mClipData;
+    boolean mDragResult;
+
+    private DragEvent mNext;
+    private RuntimeException mRecycledLocation;
+    private boolean mRecycled;
+
+    private static final int MAX_RECYCLED = 10;
+    private static final Object gRecyclerLock = new Object();
+    private static int gRecyclerUsed = 0;
+    private static DragEvent gRecyclerTop = null;
+
+    /**
+     * action constants for DragEvent dispatch
+     */
+    public static final int ACTION_DRAG_STARTED = 1;
+    public static final int ACTION_DRAG_LOCATION = 2;
+    public static final int ACTION_DROP = 3;
+    public static final int ACTION_DRAG_ENDED = 4;
+    public static final int ACTION_DRAG_ENTERED = 5;
+    public static final int ACTION_DRAG_EXITED = 6;
+
+    /* hide the constructor behind package scope */
+    private DragEvent() {
+    }
+
+    private void init(int action, float x, float y, ClipDescription description, ClipData data,
+            boolean result) {
+        mAction = action;
+        mX = x;
+        mY = y;
+        mClipDescription = description;
+        mClipData = data;
+        mDragResult = result;
+    }
+
+    static DragEvent obtain() {
+        return DragEvent.obtain(0, 0f, 0f, null, null, false);
+    }
+
+    public static DragEvent obtain(int action, float x, float y,
+            ClipDescription description, ClipData data, boolean result) {
+        final DragEvent ev;
+        synchronized (gRecyclerLock) {
+            if (gRecyclerTop == null) {
+                ev = new DragEvent();
+                ev.init(action, x, y, description, data, result);
+                return ev;
+            }
+            ev = gRecyclerTop;
+            gRecyclerTop = ev.mNext;
+            gRecyclerUsed -= 1;
+        }
+        ev.mRecycledLocation = null;
+        ev.mRecycled = false;
+        ev.mNext = null;
+
+        ev.init(action, x, y, description, data, result);
+
+        return ev;
+    }
+
+    public static DragEvent obtain(DragEvent source) {
+        return obtain(source.mAction, source.mX, source.mY,
+                source.mClipDescription, source.mClipData, source.mDragResult);
+    }
+
+    public int getAction() {
+        return mAction;
+    }
+
+    public float getX() {
+        return mX;
+    }
+
+    public float getY() {
+        return mY;
+    }
+
+    public ClipData getClipData() {
+        return mClipData;
+    }
+
+    public ClipDescription getClipDescription() {
+        return mClipDescription;
+    }
+
+    public boolean getResult() {
+        return mDragResult;
+    }
+
+    /**
+     * Recycle the DragEvent, to be re-used by a later caller.  After calling
+     * this function you must never touch the event again.
+     */
+    public final void recycle() {
+        // Ensure recycle is only called once!
+        if (TRACK_RECYCLED_LOCATION) {
+            if (mRecycledLocation != null) {
+                throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
+            }
+            mRecycledLocation = new RuntimeException("Last recycled here");
+        } else {
+            if (mRecycled) {
+                throw new RuntimeException(toString() + " recycled twice!");
+            }
+            mRecycled = true;
+        }
+
+        mClipData = null;
+        mClipDescription = null;
+
+        synchronized (gRecyclerLock) {
+            if (gRecyclerUsed < MAX_RECYCLED) {
+                gRecyclerUsed++;
+                mNext = gRecyclerTop;
+                gRecyclerTop = this;
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "DragEvent{" + Integer.toHexString(System.identityHashCode(this))
+        + " action=" + mAction + " @ (" + mX + ", " + mY + ") desc=" + mClipDescription
+        + " data=" + mClipData + " result=" + mDragResult
+        + "}";
+    }
+
+    /* Parcelable interface */
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mAction);
+        dest.writeFloat(mX);
+        dest.writeFloat(mY);
+        dest.writeInt(mDragResult ? 1 : 0);
+        if (mClipData == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            mClipData.writeToParcel(dest, flags);
+        }
+        if (mClipDescription == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            mClipDescription.writeToParcel(dest, flags);
+        }
+    }
+
+    public static final Parcelable.Creator<DragEvent> CREATOR =
+        new Parcelable.Creator<DragEvent>() {
+        public DragEvent createFromParcel(Parcel in) {
+            DragEvent event = DragEvent.obtain();
+            event.mAction = in.readInt();
+            event.mX = in.readFloat();
+            event.mY = in.readFloat();
+            event.mDragResult = (in.readInt() != 0);
+            if (in.readInt() != 0) {
+                event.mClipData = ClipData.CREATOR.createFromParcel(in);
+            }
+            if (in.readInt() != 0) {
+                event.mClipDescription = ClipDescription.CREATOR.createFromParcel(in);
+            }
+            return event;
+        }
+
+        public DragEvent[] newArray(int size) {
+            return new DragEvent[size];
+        }
+    };
+}
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
new file mode 100644
index 0000000..5d9bd1e
--- /dev/null
+++ b/core/java/android/view/GLES20Canvas.java
@@ -0,0 +1,877 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.DrawFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Picture;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.TemporaryBuffer;
+import android.text.GraphicsOperations;
+import android.text.SpannableString;
+import android.text.SpannedString;
+import android.text.TextUtils;
+import android.util.Finalizers;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An implementation of Canvas on top of OpenGL ES 2.0.
+ */
+class GLES20Canvas extends HardwareCanvas {
+    private final boolean mOpaque;
+    private int mRenderer;
+    
+    private int mWidth;
+    private int mHeight;
+    
+    private final float[] mPoint = new float[2];
+    private final float[] mLine = new float[4];
+    
+    private final Rect mClipBounds = new Rect();
+
+    private DrawFilter mFilter;
+
+    private boolean mContextLocked;
+
+    ///////////////////////////////////////////////////////////////////////////
+    // JNI
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static native boolean nIsAvailable();
+    private static boolean sIsAvailable = nIsAvailable();
+
+    static boolean isAvailable() {
+        return sIsAvailable;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Constructors
+    ///////////////////////////////////////////////////////////////////////////
+
+    GLES20Canvas(boolean translucent) {
+        this(false, translucent);
+    }
+    
+    GLES20Canvas(boolean record, boolean translucent) {
+        mOpaque = !translucent;
+
+        if (record) {
+            mRenderer = nCreateDisplayListRenderer();
+        } else {
+            mRenderer = nCreateRenderer();
+        }
+       
+        if (mRenderer == 0) {
+            throw new IllegalStateException("Could not create GLES20Canvas renderer");
+        } else {
+            new CanvasFinalizer(this);
+        }
+    }
+
+    private native int nCreateRenderer();    
+    private native int nCreateDisplayListRenderer();    
+
+    private static native void nDestroyRenderer(int renderer);
+
+    private static class CanvasFinalizer extends Finalizers.ReclaimableReference<GLES20Canvas> {
+        private static final Set<CanvasFinalizer> sFinalizers = Collections.synchronizedSet(
+                new HashSet<CanvasFinalizer>());
+
+        private int mRenderer;
+
+        CanvasFinalizer(GLES20Canvas canvas) {
+            super(canvas, Finalizers.getQueue());
+            mRenderer = canvas.mRenderer;
+            sFinalizers.add(this);
+        }
+
+        @Override
+        public void reclaim() {
+            nDestroyRenderer(mRenderer);
+            sFinalizers.remove(this);
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Canvas management
+    ///////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public boolean isOpaque() {
+        return mOpaque;
+    }
+
+    @Override
+    public int getWidth() {
+        return mWidth;
+    }
+
+    @Override
+    public int getHeight() {
+        return mHeight;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Setup
+    ///////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public void setViewport(int width, int height) {
+        mWidth = width;
+        mHeight = height;
+
+        nSetViewport(mRenderer, width, height);
+    }
+    
+    private native void nSetViewport(int renderer, int width, int height);
+
+    @Override
+    void onPreDraw() {
+        nPrepare(mRenderer, mOpaque);
+    }
+
+    private native void nPrepare(int renderer, boolean opaque);
+
+    @Override
+    void onPostDraw() {
+        nFinish(mRenderer);
+    }
+    
+    private native void nFinish(int renderer);
+
+    @Override
+    public boolean acquireContext() {
+        if (!mContextLocked) {
+            nAcquireContext(mRenderer);
+            mContextLocked = true;
+        }
+        return mContextLocked;
+    }
+
+    private native void nAcquireContext(int renderer);
+
+    @Override
+    public void releaseContext() {
+        if (mContextLocked) {
+            nReleaseContext(mRenderer);
+            mContextLocked = false;
+        }
+    }
+
+    private native void nReleaseContext(int renderer);
+    
+    ///////////////////////////////////////////////////////////////////////////
+    // Display list
+    ///////////////////////////////////////////////////////////////////////////
+
+    int getDisplayList() {
+        return nCreateDisplayList(mRenderer);
+    }
+
+    private native int nCreateDisplayList(int renderer);
+    
+    static void destroyDisplayList(int displayList) {
+        nDestroyDisplayList(displayList);
+    }
+
+    private static native void nDestroyDisplayList(int displayList);
+
+    @Override
+    public void drawDisplayList(DisplayList displayList) {
+        nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).mNativeDisplayList);
+    }
+
+    private native void nDrawDisplayList(int renderer, int displayList);
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Clipping
+    ///////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public boolean clipPath(Path path) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean clipPath(Path path, Region.Op op) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean clipRect(float left, float top, float right, float bottom) {
+        return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
+    }
+    
+    private native boolean nClipRect(int renderer, float left, float top,
+            float right, float bottom, int op);
+
+    @Override
+    public boolean clipRect(float left, float top, float right, float bottom, Region.Op op) {
+        return nClipRect(mRenderer, left, top, right, bottom, op.nativeInt);
+    }
+
+    @Override
+    public boolean clipRect(int left, int top, int right, int bottom) {
+        return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);        
+    }
+    
+    private native boolean nClipRect(int renderer, int left, int top, int right, int bottom, int op);
+
+    @Override
+    public boolean clipRect(Rect rect) {
+        return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
+                Region.Op.INTERSECT.nativeInt);        
+    }
+
+    @Override
+    public boolean clipRect(Rect rect, Region.Op op) {
+        return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt);
+    }
+
+    @Override
+    public boolean clipRect(RectF rect) {
+        return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
+                Region.Op.INTERSECT.nativeInt);
+    }
+
+    @Override
+    public boolean clipRect(RectF rect, Region.Op op) {
+        return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt);
+    }
+
+    @Override
+    public boolean clipRegion(Region region) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean clipRegion(Region region, Region.Op op) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean getClipBounds(Rect bounds) {
+        return nGetClipBounds(mRenderer, bounds);
+    }
+
+    private native boolean nGetClipBounds(int renderer, Rect bounds);
+
+    @Override
+    public boolean quickReject(float left, float top, float right, float bottom, EdgeType type) {
+        return nQuickReject(mRenderer, left, top, right, bottom, type.nativeInt);
+    }
+    
+    private native boolean nQuickReject(int renderer, float left, float top,
+            float right, float bottom, int edge);
+
+    @Override
+    public boolean quickReject(Path path, EdgeType type) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean quickReject(RectF rect, EdgeType type) {
+        return quickReject(rect.left, rect.top, rect.right, rect.bottom, type);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Transformations
+    ///////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public void translate(float dx, float dy) {
+        nTranslate(mRenderer, dx, dy);
+    }
+    
+    private native void nTranslate(int renderer, float dx, float dy);
+
+    @Override
+    public void skew(float sx, float sy) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void rotate(float degrees) {
+        nRotate(mRenderer, degrees);
+    }
+    
+    private native void nRotate(int renderer, float degrees);
+
+    @Override
+    public void scale(float sx, float sy) {
+        nScale(mRenderer, sx, sy);
+    }
+    
+    private native void nScale(int renderer, float sx, float sy);
+
+    @Override
+    public void setMatrix(Matrix matrix) {
+        nSetMatrix(mRenderer, matrix.native_instance);
+    }
+    
+    private native void nSetMatrix(int renderer, int matrix);
+
+    @Override
+    public int getNativeMatrix() {
+        return nGetMatrix(mRenderer);
+    }
+
+    private native int nGetMatrix(int renderer);    
+
+    @Override
+    public void getMatrix(Matrix matrix) {
+        nGetMatrix(mRenderer, matrix.native_instance);
+    }
+    
+    private native void nGetMatrix(int renderer, int matrix);
+
+    @Override
+    public void concat(Matrix matrix) {
+        nConcatMatrix(mRenderer, matrix.native_instance);
+    }
+    
+    private native void nConcatMatrix(int renderer, int matrix);
+    
+    ///////////////////////////////////////////////////////////////////////////
+    // State management
+    ///////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public int save() {
+        return nSave(mRenderer, Canvas.CLIP_SAVE_FLAG | Canvas.MATRIX_SAVE_FLAG);
+    }
+    
+    @Override
+    public int save(int saveFlags) {
+        return nSave(mRenderer, saveFlags);
+    }
+
+    private native int nSave(int renderer, int flags);
+    
+    @Override
+    public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
+        return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint, saveFlags);
+    }
+
+    @Override
+    public int saveLayer(float left, float top, float right, float bottom, Paint paint,
+            int saveFlags) {
+        int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        return nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags);
+    }
+
+    private native int nSaveLayer(int renderer, float left, float top, float right, float bottom,
+            int paint, int saveFlags);
+
+    @Override
+    public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) {
+        return saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom,
+                alpha, saveFlags);
+    }
+
+    @Override
+    public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
+            int saveFlags) {
+        return nSaveLayerAlpha(mRenderer, left, top, right, bottom, alpha, saveFlags);
+    }
+
+    private native int nSaveLayerAlpha(int renderer, float left, float top, float right,
+            float bottom, int alpha, int saveFlags);
+    
+    @Override
+    public void restore() {
+        nRestore(mRenderer);
+    }
+    
+    private native void nRestore(int renderer);
+
+    @Override
+    public void restoreToCount(int saveCount) {
+        nRestoreToCount(mRenderer, saveCount);
+    }
+
+    private native void nRestoreToCount(int renderer, int saveCount);
+    
+    @Override
+    public int getSaveCount() {
+        return nGetSaveCount(mRenderer);
+    }
+    
+    private native int nGetSaveCount(int renderer);
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Filtering
+    ///////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public void setDrawFilter(DrawFilter filter) {
+        mFilter = filter;
+    }
+
+    @Override
+    public DrawFilter getDrawFilter() {
+        return mFilter;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Drawing
+    ///////////////////////////////////////////////////////////////////////////
+
+    @Override
+    public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
+            Paint paint) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void drawARGB(int a, int r, int g, int b) {
+        drawColor((a & 0xFF) << 24 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF));
+    }
+
+    @Override
+    public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
+        // Shaders are ignored when drawing patches
+        boolean hasColorFilter = paint != null && setupColorFilter(paint);
+        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawPatch(mRenderer, bitmap.mNativeBitmap, chunks, dst.left, dst.top,
+                dst.right, dst.bottom, nativePaint);
+        if (hasColorFilter) nResetModifiers(mRenderer);
+    }
+
+    private native void nDrawPatch(int renderer, int bitmap, byte[] chunks, float left, float top,
+            float right, float bottom, int paint);
+
+    @Override
+    public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
+        // Shaders are ignored when drawing bitmaps
+        boolean hasColorFilter = paint != null && setupColorFilter(paint);
+        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint);
+        if (hasColorFilter) nResetModifiers(mRenderer);
+    }
+
+    private native void nDrawBitmap(int renderer, int bitmap, float left, float top, int paint);
+
+    @Override
+    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
+        // Shaders are ignored when drawing bitmaps
+        boolean hasColorFilter = paint != null && setupColorFilter(paint);
+        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint);
+        if (hasColorFilter) nResetModifiers(mRenderer);
+    }
+
+    private native void nDrawBitmap(int renderer, int bitmap, int matrix, int paint);
+
+    @Override
+    public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
+        // Shaders are ignored when drawing bitmaps
+        boolean hasColorFilter = paint != null && setupColorFilter(paint);
+        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+
+        int left, top, right, bottom;
+        if (src == null) {
+            left = top = 0;
+            right = bitmap.getWidth();
+            bottom = bitmap.getHeight();
+        } else {
+            left = src.left;
+            right = src.right;
+            top = src.top;
+            bottom = src.bottom;
+        }
+
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
+                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
+        if (hasColorFilter) nResetModifiers(mRenderer);
+    }
+
+    @Override
+    public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
+        // Shaders are ignored when drawing bitmaps
+        boolean hasColorFilter = paint != null && setupColorFilter(paint);
+        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
+                dst.left, dst.top, dst.right, dst.bottom, nativePaint);
+        if (hasColorFilter) nResetModifiers(mRenderer);
+    }
+
+    private native void nDrawBitmap(int renderer, int bitmap,
+            float srcLeft, float srcTop, float srcRight, float srcBottom,
+            float left, float top, float right, float bottom, int paint);
+
+    @Override
+    public void drawBitmap(int[] colors, int offset, int stride, float x, float y,
+            int width, int height, boolean hasAlpha, Paint paint) {
+        // Shaders are ignored when drawing bitmaps
+        boolean hasColorFilter = paint != null && setupColorFilter(paint);
+        final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
+        final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config);
+        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        nDrawBitmap(mRenderer, b.mNativeBitmap, x, y, nativePaint);
+        b.recycle();
+        if (hasColorFilter) nResetModifiers(mRenderer);
+    }
+
+    @Override
+    public void drawBitmap(int[] colors, int offset, int stride, int x, int y,
+            int width, int height, boolean hasAlpha, Paint paint) {
+        // Shaders are ignored when drawing bitmaps
+        drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, hasAlpha, paint);
+    }
+
+    @Override
+    public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
+            int vertOffset, int[] colors, int colorOffset, Paint paint) {
+        // TODO: Implement
+    }
+
+    @Override
+    public void drawCircle(float cx, float cy, float radius, Paint paint) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void drawColor(int color) {
+        drawColor(color, PorterDuff.Mode.SRC_OVER);
+    }
+
+    @Override
+    public void drawColor(int color, PorterDuff.Mode mode) {
+        nDrawColor(mRenderer, color, mode.nativeInt);
+    }
+    
+    private native void nDrawColor(int renderer, int color, int mode);
+
+    @Override
+    public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
+        mLine[0] = startX;
+        mLine[1] = startY;
+        mLine[2] = stopX;
+        mLine[3] = stopY;
+        drawLines(mLine, 0, 4, paint);
+    }
+
+    @Override
+    public void drawLines(float[] pts, int offset, int count, Paint paint) {
+        if ((offset | count) < 0 || offset + count > pts.length) {
+            throw new IllegalArgumentException("The lines array must contain 4 elements per line.");
+        }
+        boolean hasModifier = setupModifiers(paint);
+        nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint);
+        if (hasModifier) nResetModifiers(mRenderer);
+    }
+
+    private native void nDrawLines(int renderer, float[] points, int offset, int count, int paint);
+
+    @Override
+    public void drawLines(float[] pts, Paint paint) {
+        drawLines(pts, 0, pts.length, paint);
+    }
+
+    @Override
+    public void drawOval(RectF oval, Paint paint) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void drawPaint(Paint paint) {
+        final Rect r = mClipBounds;
+        nGetClipBounds(mRenderer, r);
+        drawRect(r.left, r.top, r.right, r.bottom, paint);
+    }
+
+    @Override
+    public void drawPath(Path path, Paint paint) {
+        boolean hasModifier = setupModifiers(paint);
+        if (path.isSimplePath) {
+            if (path.rects != null) {
+                nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
+            }
+        } else {
+            nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
+        }
+        if (hasModifier) nResetModifiers(mRenderer);
+    }
+
+    private native void nDrawPath(int renderer, int path, int paint);
+    private native void nDrawRects(int renderer, int region, int paint);
+
+    @Override
+    public void drawPicture(Picture picture) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void drawPicture(Picture picture, Rect dst) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void drawPicture(Picture picture, RectF dst) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void drawPoint(float x, float y, Paint paint) {
+        mPoint[0] = x;
+        mPoint[1] = y;
+        drawPoints(mPoint, 0, 1, paint);
+    }
+
+    @Override
+    public void drawPoints(float[] pts, int offset, int count, Paint paint) {
+        // TODO: Implement
+    }
+
+    @Override
+    public void drawPoints(float[] pts, Paint paint) {
+        drawPoints(pts, 0, pts.length / 2, paint);
+    }
+
+    @Override
+    public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void drawPosText(String text, float[] pos, Paint paint) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void drawRect(float left, float top, float right, float bottom, Paint paint) {
+        boolean hasModifier = setupModifiers(paint);
+        nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
+        if (hasModifier) nResetModifiers(mRenderer);
+    }
+
+    private native void nDrawRect(int renderer, float left, float top, float right, float bottom,
+            int paint);
+
+    @Override
+    public void drawRect(Rect r, Paint paint) {
+        drawRect(r.left, r.top, r.right, r.bottom, paint);
+    }
+
+    @Override
+    public void drawRect(RectF r, Paint paint) {
+        drawRect(r.left, r.top, r.right, r.bottom, paint);
+    }
+
+    @Override
+    public void drawRGB(int r, int g, int b) {
+        drawColor(0xFF000000 | (r & 0xFF) << 16 | (g & 0xFF) << 8 | (b & 0xFF));
+    }
+
+    @Override
+    public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
+        // TODO: Implement
+    }
+
+    @Override
+    public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
+        if ((index | count | (index + count) | (text.length - index - count)) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        boolean hasModifier = setupModifiers(paint);
+        try {
+            nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint);
+        } finally {
+            if (hasModifier) nResetModifiers(mRenderer);
+        }
+    }
+    
+    private native void nDrawText(int renderer, char[] text, int index, int count, float x, float y,
+            int bidiFlags, int paint);
+
+    @Override
+    public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
+        boolean hasModifier = setupModifiers(paint);
+        try {
+            if (text instanceof String || text instanceof SpannedString ||
+                    text instanceof SpannableString) {
+                nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
+                        paint.mNativePaint);
+            } else if (text instanceof GraphicsOperations) {
+                ((GraphicsOperations) text).drawText(this, start, end, x, y,
+                                                         paint);
+            } else {
+                char[] buf = TemporaryBuffer.obtain(end - start);
+                TextUtils.getChars(text, start, end, buf, 0);
+                nDrawText(mRenderer, buf, 0, end - start, x, y, paint.mBidiFlags, paint.mNativePaint);
+                TemporaryBuffer.recycle(buf);
+            }
+        } finally {
+            if (hasModifier) nResetModifiers(mRenderer);
+        }
+    }
+
+    @Override
+    public void drawText(String text, int start, int end, float x, float y, Paint paint) {
+        if ((start | end | (end - start) | (text.length() - end)) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        boolean hasModifier = setupModifiers(paint);
+        try {
+            nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint);
+        } finally {
+            if (hasModifier) nResetModifiers(mRenderer);
+        }
+    }
+
+    private native void nDrawText(int renderer, String text, int start, int end, float x, float y,
+            int bidiFlags, int paint);
+
+    @Override
+    public void drawText(String text, float x, float y, Paint paint) {
+        boolean hasModifier = setupModifiers(paint);
+        try {
+            nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags,
+                    paint.mNativePaint);
+        } finally {
+            if (hasModifier) nResetModifiers(mRenderer);
+        }
+    }
+
+    @Override
+    public void drawTextOnPath(char[] text, int index, int count, Path path, float hOffset,
+            float vOffset, Paint paint) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
+            float x, float y, int dir, Paint paint) {
+        if ((index | count | text.length - index - count) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) {
+            throw new IllegalArgumentException("Unknown direction: " + dir);
+        }
+
+        boolean hasModifier = setupModifiers(paint);
+        try {
+            nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
+                    paint.mNativePaint);
+        } finally {
+            if (hasModifier) nResetModifiers(mRenderer);
+        }
+    }
+
+    private native void nDrawTextRun(int renderer, char[] text, int index, int count,
+            int contextIndex, int contextCount, float x, float y, int dir, int nativePaint);
+
+    @Override
+    public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd,
+            float x, float y, int dir, Paint paint) {
+        if ((start | end | end - start | text.length() - end) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        boolean hasModifier = setupModifiers(paint);
+        try {
+            int flags = dir == 0 ? 0 : 1;
+            if (text instanceof String || text instanceof SpannedString ||
+                    text instanceof SpannableString) {
+                nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
+                        contextEnd, x, y, flags, paint.mNativePaint);
+            } else if (text instanceof GraphicsOperations) {
+                ((GraphicsOperations) text).drawTextRun(this, start, end,
+                        contextStart, contextEnd, x, y, flags, paint);
+            } else {
+                int contextLen = contextEnd - contextStart;
+                int len = end - start;
+                char[] buf = TemporaryBuffer.obtain(contextLen);
+                TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
+                nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
+                        x, y, flags, paint.mNativePaint);
+                TemporaryBuffer.recycle(buf);
+            }
+        } finally {
+            if (hasModifier) nResetModifiers(mRenderer);
+        }
+    }
+
+    private native void nDrawTextRun(int renderer, String text, int start, int end,
+            int contextStart, int contextEnd, float x, float y, int flags, int nativePaint);
+
+    @Override
+    public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
+            float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
+            int indexOffset, int indexCount, Paint paint) {
+        // TODO: Implement
+    }
+
+    private boolean setupModifiers(Paint paint) {
+        boolean hasModifier = false;
+
+        if (paint.hasShadow) {
+            nSetupShadow(mRenderer, paint.shadowRadius, paint.shadowDx, paint.shadowDy,
+                    paint.shadowColor);
+            hasModifier = true;
+        }
+
+        final Shader shader = paint.getShader();
+        if (shader != null) {
+            nSetupShader(mRenderer, shader.native_shader);
+            hasModifier = true;
+        }
+
+        final ColorFilter filter = paint.getColorFilter();
+        if (filter != null) {
+            nSetupColorFilter(mRenderer, filter.nativeColorFilter);
+            hasModifier = true;
+        }
+
+        return hasModifier;
+    }
+
+    private boolean setupColorFilter(Paint paint) {
+        final ColorFilter filter = paint.getColorFilter();
+        if (filter != null) {
+            nSetupColorFilter(mRenderer, filter.nativeColorFilter);
+            return true;
+        }
+        return false;        
+    }
+    
+    private native void nSetupShader(int renderer, int shader);
+    private native void nSetupColorFilter(int renderer, int colorFilter);
+    private native void nSetupShadow(int renderer, float radius, float dx, float dy, int color);
+
+    private native void nResetModifiers(int renderer);
+}
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
new file mode 100644
index 0000000..11e6d30
--- /dev/null
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.util.Finalizers;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An implementation of display list for OpenGL ES 2.0.
+ */
+class GLES20DisplayList extends DisplayList {
+    private GLES20Canvas mCanvas;
+
+    private boolean mStarted = false;
+    private boolean mRecorded = false;
+
+    int mNativeDisplayList;
+
+    @Override
+    HardwareCanvas start() {
+        if (mStarted) {
+            throw new IllegalStateException("Recording has already started");
+        }
+
+        mCanvas = new GLES20Canvas(true, true);
+        mStarted = true;
+        mRecorded = false;
+
+        return mCanvas;
+    }
+
+    @Override
+    void end() {
+        if (mCanvas != null) {
+            mStarted = false;
+            mRecorded = true;
+
+            mNativeDisplayList = mCanvas.getDisplayList();
+            new DisplayListFinalizer(this);
+        }
+    }
+
+    @Override
+    boolean isReady() {
+        return !mStarted && mRecorded;
+    }
+
+    private static class DisplayListFinalizer extends Finalizers.ReclaimableReference<DisplayList> {
+        private static final Set<DisplayListFinalizer> sFinalizers = Collections.synchronizedSet(
+                new HashSet<DisplayListFinalizer>());
+
+        private int mNativeDisplayList;
+
+        DisplayListFinalizer(GLES20DisplayList displayList) {
+            super(displayList, Finalizers.getQueue());
+            mNativeDisplayList = displayList.mNativeDisplayList;
+            sFinalizers.add(this);
+        }
+
+        @Override
+        public void reclaim() {
+            GLES20Canvas.destroyDisplayList(mNativeDisplayList);
+            sFinalizers.remove(this);
+        }
+    }
+}
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
new file mode 100644
index 0000000..8b8d15e
--- /dev/null
+++ b/core/java/android/view/HardwareCanvas.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+/**
+ * Hardware accelerated canvas. 
+ */
+abstract class HardwareCanvas extends Canvas {
+    @Override
+    public boolean isHardwareAccelerated() {
+        return true;
+    }
+
+    @Override
+    public void setBitmap(Bitmap bitmap) {
+        throw new UnsupportedOperationException();
+    }
+    
+    /**
+     * Invoked before any drawing operation is performed in this canvas.
+     */
+    abstract void onPreDraw();
+
+    /**
+     * Invoked after all drawing operation have been performed.
+     */
+    abstract void onPostDraw();
+    
+    /**
+     * Draws the specified display list onto this canvas.
+     * 
+     * @param displayList The display list to replay.
+     */
+    public abstract void drawDisplayList(DisplayList displayList);
+}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
new file mode 100644
index 0000000..6f4abef
--- /dev/null
+++ b/core/java/android/view/HardwareRenderer.java
@@ -0,0 +1,662 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.view;
+
+import android.graphics.Canvas;
+import android.os.SystemClock;
+import android.util.EventLog;
+import android.util.Log;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+
+/**
+ * Interface for rendering a ViewRoot using hardware acceleration.
+ * 
+ * @hide
+ */
+public abstract class HardwareRenderer {
+    private static final String LOG_TAG = "HardwareRenderer";
+
+    /**
+     * A process can set this flag to false to prevent the use of hardware
+     * rendering.
+     * 
+     * @hide
+     */
+    public static boolean sRendererDisabled = false;
+
+    private boolean mEnabled;
+    private boolean mRequested = true;
+
+    /**
+     * Indicates that the current process cannot use hardware rendering.
+     * 
+     * @hide
+     */
+    public static void disable() {
+        sRendererDisabled = true;
+    }
+
+    /**
+     * Indicates whether hardware acceleration is available under any form for
+     * the view hierarchy.
+     * 
+     * @return True if the view hierarchy can potentially be hardware accelerated,
+     *         false otherwise
+     */
+    public static boolean isAvailable() {
+        return GLES20Canvas.isAvailable();
+    }
+
+    /**
+     * Destroys the hardware rendering context.
+     * 
+     * @param full If true, destroys all associated resources.
+     */
+    abstract void destroy(boolean full);
+
+    /**
+     * Initializes the hardware renderer for the specified surface.
+     * 
+     * @param holder The holder for the surface to hardware accelerate.
+     * 
+     * @return True if the initialization was successful, false otherwise.
+     */
+    abstract boolean initialize(SurfaceHolder holder);
+
+    /**
+     * Setup the hardware renderer for drawing. This is called for every
+     * frame to draw.
+     * 
+     * @param width Width of the drawing surface.
+     * @param height Height of the drawing surface.
+     */
+    abstract void setup(int width, int height);
+
+    /**
+     * Draws the specified view.
+     * 
+     * @param view The view to draw.
+     * @param attachInfo AttachInfo tied to the specified view.
+     */
+    abstract void draw(View view, View.AttachInfo attachInfo, int yOffset);
+
+    /**
+     * Creates a new display list that can be used to record batches of
+     * drawing operations.
+     * 
+     * @return A new display list.
+     */
+    abstract DisplayList createDisplayList();
+
+    /**
+     * Initializes the hardware renderer for the specified surface and setup the
+     * renderer for drawing, if needed. This is invoked when the ViewRoot has
+     * potentially lost the hardware renderer. The hardware renderer should be
+     * reinitialized and setup when the render {@link #isRequested()} and
+     * {@link #isEnabled()}.
+     * 
+     * @param width The width of the drawing surface.
+     * @param height The height of the drawing surface.
+     * @param attachInfo The 
+     * @param holder
+     */
+    void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
+            SurfaceHolder holder) {
+        if (isRequested()) {
+            // We lost the gl context, so recreate it.
+            if (!isEnabled()) {
+                if (initialize(holder)) {
+                    setup(width, height);
+                }
+            }
+        }        
+    }
+
+    /**
+     * Creates a hardware renderer using OpenGL.
+     * 
+     * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.)
+     * @param translucent True if the surface is translucent, false otherwise
+     * 
+     * @return A hardware renderer backed by OpenGL.
+     */
+    static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {
+        switch (glVersion) {
+            case 2:
+                return Gl20Renderer.create(translucent);
+        }
+        throw new IllegalArgumentException("Unknown GL version: " + glVersion);
+    }
+
+    /**
+     * Indicates whether hardware acceleration is currently enabled.
+     * 
+     * @return True if hardware acceleration is in use, false otherwise.
+     */
+    boolean isEnabled() {
+        return mEnabled;
+    }
+
+    /**
+     * Indicates whether hardware acceleration is currently enabled.
+     * 
+     * @param enabled True if the hardware renderer is in use, false otherwise.
+     */
+    void setEnabled(boolean enabled) {
+        mEnabled = enabled;
+    }
+
+    /**
+     * Indicates whether hardware acceleration is currently request but not
+     * necessarily enabled yet.
+     * 
+     * @return True if requested, false otherwise.
+     */
+    boolean isRequested() {
+        return mRequested;
+    }
+
+    /**
+     * Indicates whether hardware acceleration is currently request but not
+     * necessarily enabled yet.
+     * 
+     * @return True to request hardware acceleration, false otherwise.
+     */
+    void setRequested(boolean requested) {
+        mRequested = requested;
+    }
+
+    @SuppressWarnings({"deprecation"})
+    static abstract class GlRenderer extends HardwareRenderer {
+        private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+
+        static EGLContext sEglContext;
+        static EGL10 sEgl;
+        static EGLDisplay sEglDisplay;
+        static EGLConfig sEglConfig;
+
+        private static Thread sEglThread;        
+
+        EGLSurface mEglSurface;
+        
+        GL mGl;
+        GLES20Canvas mCanvas;
+
+        final int mGlVersion;
+        final boolean mTranslucent;
+
+        private boolean mDestroyed;
+
+        GlRenderer(int glVersion, boolean translucent) {
+            mGlVersion = glVersion;
+            mTranslucent = translucent;
+        }
+
+        /**
+         * Return a string for the EGL error code, or the hex representation
+         * if the error is unknown.
+         * 
+         * @param error The EGL error to convert into a String.
+         * 
+         * @return An error string correponding to the EGL error code.
+         */
+        static String getEGLErrorString(int error) {
+            switch (error) {
+                case EGL10.EGL_SUCCESS:
+                    return "EGL_SUCCESS";
+                case EGL10.EGL_NOT_INITIALIZED:
+                    return "EGL_NOT_INITIALIZED";
+                case EGL10.EGL_BAD_ACCESS:
+                    return "EGL_BAD_ACCESS";
+                case EGL10.EGL_BAD_ALLOC:
+                    return "EGL_BAD_ALLOC";
+                case EGL10.EGL_BAD_ATTRIBUTE:
+                    return "EGL_BAD_ATTRIBUTE";
+                case EGL10.EGL_BAD_CONFIG:
+                    return "EGL_BAD_CONFIG";
+                case EGL10.EGL_BAD_CONTEXT:
+                    return "EGL_BAD_CONTEXT";
+                case EGL10.EGL_BAD_CURRENT_SURFACE:
+                    return "EGL_BAD_CURRENT_SURFACE";
+                case EGL10.EGL_BAD_DISPLAY:
+                    return "EGL_BAD_DISPLAY";
+                case EGL10.EGL_BAD_MATCH:
+                    return "EGL_BAD_MATCH";
+                case EGL10.EGL_BAD_NATIVE_PIXMAP:
+                    return "EGL_BAD_NATIVE_PIXMAP";
+                case EGL10.EGL_BAD_NATIVE_WINDOW:
+                    return "EGL_BAD_NATIVE_WINDOW";
+                case EGL10.EGL_BAD_PARAMETER:
+                    return "EGL_BAD_PARAMETER";
+                case EGL10.EGL_BAD_SURFACE:
+                    return "EGL_BAD_SURFACE";
+                case EGL11.EGL_CONTEXT_LOST:
+                    return "EGL_CONTEXT_LOST";
+                default:
+                    return "0x" + Integer.toHexString(error);
+            }
+        }
+
+        /**
+         * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
+         * is invoked and the requested flag is turned off. The error code is
+         * also logged as a warning.
+         */
+        void checkEglErrors() {
+            if (isEnabled()) {
+                int error = sEgl.eglGetError();
+                if (error != EGL10.EGL_SUCCESS) {
+                    // something bad has happened revert to
+                    // normal rendering.
+                    destroy(true);
+                    if (error != EGL11.EGL_CONTEXT_LOST) {
+                        // we'll try again if it was context lost
+                        setRequested(false);
+                    }
+                    Log.w(LOG_TAG, "EGL error: " + getEGLErrorString(error));
+                }
+            }
+        }
+        
+        @Override
+        boolean initialize(SurfaceHolder holder) {
+            if (isRequested() && !isEnabled()) {
+                initializeEgl();
+                mGl = createEglSurface(holder);
+                mDestroyed = false;
+
+                if (mGl != null) {
+                    int err = sEgl.eglGetError();
+                    if (err != EGL10.EGL_SUCCESS) {
+                        destroy(true);
+                        setRequested(false);
+                    } else {
+                        if (mCanvas == null) {
+                            mCanvas = createCanvas();
+                        }
+                        if (mCanvas != null) {
+                            setEnabled(true);
+                        } else {
+                            Log.w(LOG_TAG, "Hardware accelerated Canvas could not be created");
+                        }
+                    }
+
+                    return mCanvas != null;
+                }
+            }
+            return false;
+        }
+
+        abstract GLES20Canvas createCanvas();
+
+        void initializeEgl() {
+            if (sEglContext != null) return;
+
+            sEglThread = Thread.currentThread();
+            sEgl = (EGL10) EGLContext.getEGL();
+            
+            // Get to the default display.
+            sEglDisplay = sEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+            
+            if (sEglDisplay == EGL10.EGL_NO_DISPLAY) {
+                throw new RuntimeException("eglGetDisplay failed "
+                        + getEGLErrorString(sEgl.eglGetError()));
+            }
+            
+            // We can now initialize EGL for that display
+            int[] version = new int[2];
+            if (!sEgl.eglInitialize(sEglDisplay, version)) {
+                throw new RuntimeException("eglInitialize failed "
+                        + getEGLErrorString(sEgl.eglGetError()));
+            }
+            sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
+            
+            /*
+            * Create an EGL context. We want to do this as rarely as we can, because an
+            * EGL context is a somewhat heavy object.
+            */
+            sEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
+        }
+
+        GL createEglSurface(SurfaceHolder holder) {
+            // Check preconditions.
+            if (sEgl == null) {
+                throw new RuntimeException("egl not initialized");
+            }
+            if (sEglDisplay == null) {
+                throw new RuntimeException("eglDisplay not initialized");
+            }
+            if (sEglConfig == null) {
+                throw new RuntimeException("mEglConfig not initialized");
+            }
+            if (Thread.currentThread() != sEglThread) {
+                throw new IllegalStateException("HardwareRenderer cannot be used " 
+                        + "from multiple threads");
+            }
+
+            /*
+             *  The window size has changed, so we need to create a new
+             *  surface.
+             */
+            if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+                /*
+                 * Unbind and destroy the old EGL surface, if
+                 * there is one.
+                 */
+                sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
+                        EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+                sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
+            }
+
+            // Create an EGL surface we can render into.
+            mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null);
+
+            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+                int error = sEgl.eglGetError();
+                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
+                    Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+                    return null;
+                }
+                throw new RuntimeException("createWindowSurface failed "
+                        + getEGLErrorString(error));
+            }
+
+            /*
+             * Before we can issue GL commands, we need to make sure
+             * the context is current and bound to a surface.
+             */
+            if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
+                throw new RuntimeException("eglMakeCurrent failed "
+                        + getEGLErrorString(sEgl.eglGetError()));
+            }
+
+            return sEglContext.getGL();
+        }
+
+        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
+            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL10.EGL_NONE };
+
+            return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT,
+                    mGlVersion != 0 ? attrib_list : null);            
+        }
+
+        @Override
+        void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
+                SurfaceHolder holder) {
+            if (isRequested()) {
+                checkEglErrors();
+                super.initializeIfNeeded(width, height, attachInfo, holder);
+            }
+        }
+        
+        @Override
+        void destroy(boolean full) {
+            if (full && mCanvas != null) {
+                mCanvas = null;
+            }
+            
+            if (!isEnabled() || mDestroyed) return;
+
+            mDestroyed = true;
+
+            sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
+                    EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+            sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
+
+            mEglSurface = null;
+            mGl = null;
+
+            setEnabled(false);
+        }
+
+        @Override
+        void setup(int width, int height) {
+            mCanvas.setViewport(width, height);
+        }
+        
+        boolean canDraw() {
+            return mGl != null && mCanvas != null;
+        }        
+        
+        void onPreDraw() {
+        }
+
+        void onPostDraw() {
+        }
+        
+        /**
+         * Defines the EGL configuration for this renderer.
+         * 
+         * @return An {@link android.view.HardwareRenderer.GlRenderer.EglConfigChooser}.
+         */
+        EglConfigChooser getConfigChooser(int glVersion) {
+            return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0);
+        }
+
+        @Override
+        void draw(View view, View.AttachInfo attachInfo, int yOffset) {
+            if (canDraw()) {
+                attachInfo.mDrawingTime = SystemClock.uptimeMillis();
+                attachInfo.mIgnoreDirtyState = true;
+                view.mPrivateFlags |= View.DRAWN;
+                
+                long startTime;
+                if (ViewDebug.DEBUG_PROFILE_DRAWING) {
+                    startTime = SystemClock.elapsedRealtime();
+                }
+
+                checkCurrent();
+
+                onPreDraw();
+
+                Canvas canvas = mCanvas;
+                int saveCount = canvas.save();
+                canvas.translate(0, -yOffset);
+
+                try {
+                    view.draw(canvas);
+                } finally {
+                    canvas.restoreToCount(saveCount);
+                }
+
+                onPostDraw();
+
+                if (ViewDebug.DEBUG_PROFILE_DRAWING) {
+                    EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
+                }
+
+                attachInfo.mIgnoreDirtyState = false;
+
+                sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
+                checkEglErrors();
+            }
+        }
+
+        private void checkCurrent() {
+            // TODO: Don't check the current context when we have one per UI thread
+            // TODO: Use a threadlocal flag to know whether the surface has changed
+            if (sEgl.eglGetCurrentContext() != sEglContext ||
+                    sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) {
+                if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
+                    throw new RuntimeException("eglMakeCurrent failed "
+                            + getEGLErrorString(sEgl.eglGetError()));
+                }
+            }
+        }
+
+        static abstract class EglConfigChooser {
+            final int[] mConfigSpec;
+            private final int mGlVersion;
+
+            EglConfigChooser(int glVersion, int[] configSpec) {
+                mGlVersion = glVersion;
+                mConfigSpec = filterConfigSpec(configSpec);
+            }
+
+            EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+                int[] index = new int[1];
+                if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, index)) {
+                    throw new IllegalArgumentException("eglChooseConfig failed "
+                            + getEGLErrorString(egl.eglGetError()));
+                }
+
+                int numConfigs = index[0];
+                if (numConfigs <= 0) {
+                    throw new IllegalArgumentException("No configs match configSpec");
+                }
+
+                EGLConfig[] configs = new EGLConfig[numConfigs];
+                if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, index)) {
+                    throw new IllegalArgumentException("eglChooseConfig failed "
+                            + getEGLErrorString(egl.eglGetError()));
+                }
+
+                EGLConfig config = chooseConfig(egl, display, configs);
+                if (config == null) {
+                    throw new IllegalArgumentException("No config chosen");
+                }
+
+                return config;
+            }
+
+            abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);
+
+            private int[] filterConfigSpec(int[] configSpec) {
+                if (mGlVersion != 2) {
+                    return configSpec;
+                }
+                /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
+                 * And we know the configSpec is well formed.
+                 */
+                int len = configSpec.length;
+                int[] newConfigSpec = new int[len + 2];
+                System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
+                newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
+                newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
+                newConfigSpec[len + 1] = EGL10.EGL_NONE;
+                return newConfigSpec;
+            }
+        }
+
+        /**
+         * Choose a configuration with exactly the specified r,g,b,a sizes,
+         * and at least the specified depth and stencil sizes.
+         */
+        static class ComponentSizeChooser extends EglConfigChooser {
+            private int[] mValue;
+
+            private int mRedSize;
+            private int mGreenSize;
+            private int mBlueSize;
+            private int mAlphaSize;
+            private int mDepthSize;
+            private int mStencilSize;
+
+            ComponentSizeChooser(int glVersion, int redSize, int greenSize, int blueSize,
+                    int alphaSize, int depthSize, int stencilSize) {
+                super(glVersion, new int[] {
+                        EGL10.EGL_RED_SIZE, redSize,
+                        EGL10.EGL_GREEN_SIZE, greenSize,
+                        EGL10.EGL_BLUE_SIZE, blueSize,
+                        EGL10.EGL_ALPHA_SIZE, alphaSize,
+                        EGL10.EGL_DEPTH_SIZE, depthSize,
+                        EGL10.EGL_STENCIL_SIZE, stencilSize,
+                        EGL10.EGL_NONE });
+                mValue = new int[1];
+                mRedSize = redSize;
+                mGreenSize = greenSize;
+                mBlueSize = blueSize;
+                mAlphaSize = alphaSize;
+                mDepthSize = depthSize;
+                mStencilSize = stencilSize;
+           }
+
+            @Override
+            EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
+                for (EGLConfig config : configs) {
+                    int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
+                    int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
+                    if (d >= mDepthSize && s >= mStencilSize) {
+                        int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
+                        int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
+                        int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
+                        int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
+                        if (r >= mRedSize && g >= mGreenSize && b >= mBlueSize && a >= mAlphaSize) {
+                            return config;
+                        }
+                    }
+                }
+                return null;
+            }
+
+            private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config,
+                    int attribute, int defaultValue) {
+                if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+                    return mValue[0];
+                }
+
+                return defaultValue;
+            }
+        }
+    }
+
+    /**
+     * Hardware renderer using OpenGL ES 2.0.
+     */
+    static class Gl20Renderer extends GlRenderer {
+        private GLES20Canvas mGlCanvas;
+
+        Gl20Renderer(boolean translucent) {
+            super(2, translucent);
+        }
+
+        @Override
+        GLES20Canvas createCanvas() {
+            return mGlCanvas = new GLES20Canvas(mTranslucent);
+        }
+
+        @Override
+        void onPreDraw() {
+            mGlCanvas.onPreDraw();
+        }
+
+        @Override
+        void onPostDraw() {
+            mGlCanvas.onPostDraw();
+        }
+
+        @Override
+        DisplayList createDisplayList() {
+            return new GLES20DisplayList();
+        }
+
+        static HardwareRenderer create(boolean translucent) {
+            if (GLES20Canvas.isAvailable()) {
+                return new Gl20Renderer(translucent);
+            }
+            return null;
+        }
+    }
+}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 921018a..85a8c1a 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -21,6 +21,7 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.view.DragEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
@@ -64,4 +65,9 @@
     
     void dispatchWallpaperCommand(String action, int x, int y,
             int z, in Bundle extras, boolean sync);
+
+    /**
+     * Drag/drop events
+     */
+     void dispatchDragEvent(in DragEvent event);
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index d4dd05c..a54f342 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -156,4 +156,17 @@
      * calls back when it changes.
      */
     int watchRotation(IRotationWatcher watcher);
+
+	/**
+	 * Lock the device orientation to the current rotation. Sensor input will
+	 * be ignored until thawRotation() is called.
+	 * @hide
+	 */
+	void freezeRotation();
+
+	/**
+	 * Release the orientation lock imposed by freezeRotation().
+	 * @hide
+	 */
+	void thawRotation();
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7f10b76..23fae42 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -17,6 +17,7 @@
 
 package android.view;
 
+import android.content.ClipData;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -116,6 +117,37 @@
     boolean performHapticFeedback(IWindow window, int effectId, boolean always);
     
     /**
+     * Allocate the drag's thumbnail surface.  Also assigns a token that identifies
+     * the drag to the OS and passes that as the return value.  A return value of
+     * null indicates failure.
+     */
+    IBinder prepareDrag(IWindow window, boolean localOnly,
+            int thumbnailWidth, int thumbnailHeight, out Surface outSurface);
+
+    /**
+     * Initiate the drag operation itself
+     */
+    boolean performDrag(IWindow window, IBinder dragToken, float touchX, float touchY,
+            float thumbCenterX, float thumbCenterY, in ClipData data);
+
+	/**
+	 * Report the result of a drop action targeted to the given window.
+	 * consumed is 'true' when the drop was accepted by a valid recipient,
+	 * 'false' otherwise.
+	 */
+	void reportDropResult(IWindow window, boolean consumed);
+
+    /**
+     * Tell the OS that we've just dragged into a View that is willing to accept the drop
+     */
+    void dragRecipientEntered(IWindow window);
+
+    /**
+     * Tell the OS that we've just dragged *off* of a View that was willing to accept the drop
+     */
+    void dragRecipientExited(IWindow window);
+
+    /**
      * For windows with the wallpaper behind them, and the wallpaper is
      * larger than the screen, set the offset within the screen.
      * For multi screen launcher type applications, xstep and ystep indicate
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 0e5ece1..e7a16a2 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -42,6 +42,13 @@
  * Meta states describe the pressed state of key modifiers
  * such as {@link #META_SHIFT_ON} or {@link #META_ALT_ON}.
  * </p><p>
+ * Key codes typically correspond one-to-one with individual keys on an input device.
+ * Many keys and key combinations serve quite different functions on different
+ * input devices so care must be taken when interpreting them.  Always use the
+ * {@link KeyCharacterMap} associated with the input device when mapping keys
+ * to characters.  Be aware that there may be multiple key input devices active
+ * at the same time and each will have its own key character map.
+ * </p><p>
  * When interacting with an IME, the framework may deliver key events
  * with the special action {@link #ACTION_MULTIPLE} that either specifies
  * that single repeated key code or a sequence of characters to insert.
@@ -118,9 +125,11 @@
     /** Key code constant: Directional Pad Center key.
      * May also be synthesized from trackball motions. */
     public static final int KEYCODE_DPAD_CENTER     = 23;
-    /** Key code constant: Volume Up key. */
+    /** Key code constant: Volume Up key.
+     * Adjusts the speaker volume up. */
     public static final int KEYCODE_VOLUME_UP       = 24;
-    /** Key code constant: Volume Down key. */
+    /** Key code constant: Volume Down key.
+     * Adjusts the speaker volume down. */
     public static final int KEYCODE_VOLUME_DOWN     = 25;
     /** Key code constant: Power key. */
     public static final int KEYCODE_POWER           = 26;
@@ -209,7 +218,7 @@
     /** Key code constant: Enter key. */
     public static final int KEYCODE_ENTER           = 66;
     /** Key code constant: Backspace key.
-     * Deletes characters before the insertion point. */
+     * Deletes characters before the insertion point, unlike {@link #KEYCODE_FORWARD_DEL}. */
     public static final int KEYCODE_DEL             = 67;
     /** Key code constant: '`' (backtick) key. */
     public static final int KEYCODE_GRAVE           = 68;
@@ -262,7 +271,8 @@
     public static final int KEYCODE_MEDIA_REWIND    = 89;
     /** Key code constant: Fast Forward media key. */
     public static final int KEYCODE_MEDIA_FAST_FORWARD = 90;
-    /** Key code constant: Mute key. */
+    /** Key code constant: Mute key.
+     * Mutes the microphone, unlike {@link #KEYCODE_VOLUME_MUTE}. */
     public static final int KEYCODE_MUTE            = 91;
     /** Key code constant: Page Up key. */
     public static final int KEYCODE_PAGE_UP         = 92;
@@ -331,6 +341,130 @@
     /** Key code constant: Mode Button key.
      * On a game controller, the button labeled Mode. */
     public static final int KEYCODE_BUTTON_MODE     = 110;
+    /** Key code constant: Escape key. */
+    public static final int KEYCODE_ESCAPE          = 111;
+    /** Key code constant: Forward Delete key.
+     * Deletes characters ahead of the insertion point, unlike {@link #KEYCODE_DEL}. */
+    public static final int KEYCODE_FORWARD_DEL     = 112;
+    /** Key code constant: Left Control modifier key. */
+    public static final int KEYCODE_CTRL_LEFT       = 113;
+    /** Key code constant: Right Control modifier key. */
+    public static final int KEYCODE_CTRL_RIGHT      = 114;
+    /** Key code constant: Caps Lock modifier key. */
+    public static final int KEYCODE_CAPS_LOCK       = 115;
+    /** Key code constant: Scroll Lock key. */
+    public static final int KEYCODE_SCROLL_LOCK     = 116;
+    /** Key code constant: Left Meta modifier key. */
+    public static final int KEYCODE_META_LEFT       = 117;
+    /** Key code constant: Right Meta modifier key. */
+    public static final int KEYCODE_META_RIGHT      = 118;
+    /** Key code constant: Function modifier key. */
+    public static final int KEYCODE_FUNCTION        = 119;
+    /** Key code constant: System Request / Print Screen key. */
+    public static final int KEYCODE_SYSRQ           = 120;
+    /** Key code constant: Break / Pause key. */
+    public static final int KEYCODE_BREAK           = 121;
+    /** Key code constant: Home Movement key.
+     * Used for scrolling or moving the cursor around to the start of a line
+     * or to the top of a list. */
+    public static final int KEYCODE_MOVE_HOME       = 122;
+    /** Key code constant: End Movement key.
+     * Used for scrolling or moving the cursor around to the end of a line
+     * or to the bottom of a list. */
+    public static final int KEYCODE_MOVE_END        = 123;
+    /** Key code constant: Insert key.
+     * Toggles insert / overwrite edit mode. */
+    public static final int KEYCODE_INSERT          = 124;
+    /** Key code constant: Forward key.
+     * Navigates forward in the history stack.  Complement of {@link #KEYCODE_BACK}. */
+    public static final int KEYCODE_FORWARD         = 125;
+    /** Key code constant: Play media key. */
+    public static final int KEYCODE_MEDIA_PLAY      = 126;
+    /** Key code constant: Pause media key. */
+    public static final int KEYCODE_MEDIA_PAUSE     = 127;
+    /** Key code constant: Close media key.
+     * May be used to close a CD tray, for example. */
+    public static final int KEYCODE_MEDIA_CLOSE     = 128;
+    /** Key code constant: Eject media key.
+     * May be used to eject a CD tray, for example. */
+    public static final int KEYCODE_MEDIA_EJECT     = 129;
+    /** Key code constant: Record media key. */
+    public static final int KEYCODE_MEDIA_RECORD    = 130;
+    /** Key code constant: F1 key. */
+    public static final int KEYCODE_F1              = 131;
+    /** Key code constant: F2 key. */
+    public static final int KEYCODE_F2              = 132;
+    /** Key code constant: F3 key. */
+    public static final int KEYCODE_F3              = 133;
+    /** Key code constant: F4 key. */
+    public static final int KEYCODE_F4              = 134;
+    /** Key code constant: F5 key. */
+    public static final int KEYCODE_F5              = 135;
+    /** Key code constant: F6 key. */
+    public static final int KEYCODE_F6              = 136;
+    /** Key code constant: F7 key. */
+    public static final int KEYCODE_F7              = 137;
+    /** Key code constant: F8 key. */
+    public static final int KEYCODE_F8              = 138;
+    /** Key code constant: F9 key. */
+    public static final int KEYCODE_F9              = 139;
+    /** Key code constant: F10 key. */
+    public static final int KEYCODE_F10             = 140;
+    /** Key code constant: F11 key. */
+    public static final int KEYCODE_F11             = 141;
+    /** Key code constant: F12 key. */
+    public static final int KEYCODE_F12             = 142;
+    /** Key code constant: Num Lock modifier key.
+     * This is the Num Lock key; it is different from {@link #KEYCODE_NUM}.
+     * This key generally modifies the behavior of other keys on the numeric keypad. */
+    public static final int KEYCODE_NUM_LOCK        = 143;
+    /** Key code constant: Numeric keypad '0' key. */
+    public static final int KEYCODE_NUMPAD_0        = 144;
+    /** Key code constant: Numeric keypad '1' key. */
+    public static final int KEYCODE_NUMPAD_1        = 145;
+    /** Key code constant: Numeric keypad '2' key. */
+    public static final int KEYCODE_NUMPAD_2        = 146;
+    /** Key code constant: Numeric keypad '3' key. */
+    public static final int KEYCODE_NUMPAD_3        = 147;
+    /** Key code constant: Numeric keypad '4' key. */
+    public static final int KEYCODE_NUMPAD_4        = 148;
+    /** Key code constant: Numeric keypad '5' key. */
+    public static final int KEYCODE_NUMPAD_5        = 149;
+    /** Key code constant: Numeric keypad '6' key. */
+    public static final int KEYCODE_NUMPAD_6        = 150;
+    /** Key code constant: Numeric keypad '7' key. */
+    public static final int KEYCODE_NUMPAD_7        = 151;
+    /** Key code constant: Numeric keypad '8' key. */
+    public static final int KEYCODE_NUMPAD_8        = 152;
+    /** Key code constant: Numeric keypad '9' key. */
+    public static final int KEYCODE_NUMPAD_9        = 153;
+    /** Key code constant: Numeric keypad '/' key (for division). */
+    public static final int KEYCODE_NUMPAD_DIVIDE   = 154;
+    /** Key code constant: Numeric keypad '*' key (for multiplication). */
+    public static final int KEYCODE_NUMPAD_MULTIPLY = 155;
+    /** Key code constant: Numeric keypad '-' key (for subtraction). */
+    public static final int KEYCODE_NUMPAD_SUBTRACT = 156;
+    /** Key code constant: Numeric keypad '+' key (for addition). */
+    public static final int KEYCODE_NUMPAD_ADD      = 157;
+    /** Key code constant: Numeric keypad '.' key (for decimals or digit grouping). */
+    public static final int KEYCODE_NUMPAD_DOT      = 158;
+    /** Key code constant: Numeric keypad ',' key (for decimals or digit grouping). */
+    public static final int KEYCODE_NUMPAD_COMMA    = 159;
+    /** Key code constant: Numeric keypad Enter key. */
+    public static final int KEYCODE_NUMPAD_ENTER    = 160;
+    /** Key code constant: Numeric keypad '=' key. */
+    public static final int KEYCODE_NUMPAD_EQUALS   = 161;
+    /** Key code constant: Numeric keypad '(' key. */
+    public static final int KEYCODE_NUMPAD_LEFT_PAREN = 162;
+    /** Key code constant: Numeric keypad ')' key. */
+    public static final int KEYCODE_NUMPAD_RIGHT_PAREN = 163;
+    /** Key code constant: Volume Mute key.
+     * Mutes the speaker, unlike {@link #KEYCODE_MUTE}.
+     * This key should normally be implemented as a toggle such that the first press
+     * mutes the speaker and the second press restores the original volume. */
+    public static final int KEYCODE_VOLUME_MUTE     = 164;
+
+    private static final int LAST_KEYCODE           = KEYCODE_VOLUME_MUTE;
 
     // NOTE: If you add a new keycode here you must also add it to:
     //  isSystem()
@@ -339,7 +473,6 @@
     //  external/webkit/WebKit/android/plugins/ANPKeyCodes.h
     //  tools/puppet_master/PuppetMaster/nav_keys.py
     //  frameworks/base/core/res/res/values/attrs.xml
-    //  commands/monkey/Monkey.java
     //  emulator?
     //
     //  Also Android currently does not reserve code ranges for vendor-
@@ -347,9 +480,214 @@
     //  MUST contribute a patch to the open source project to define
     //  those new codes.  This is intended to maintain a consistent
     //  set of key code definitions across all Android devices.
-   
-    private static final int LAST_KEYCODE           = KEYCODE_BUTTON_MODE;
-    
+
+    // Symbolic names of all keys indexed by keycode.
+    // There should be exactly LAST_KEYCODE + 1 entries in this table.
+    private static final String[] KEYCODE_SYMBOLIC_NAMES = new String[] {
+        "KEYCODE_UNKNOWN",
+        "KEYCODE_SOFT_LEFT",
+        "KEYCODE_SOFT_RIGHT",
+        "KEYCODE_HOME",
+        "KEYCODE_BACK",
+        "KEYCODE_CALL",
+        "KEYCODE_ENDCALL",
+        "KEYCODE_0",
+        "KEYCODE_1",
+        "KEYCODE_2",
+        "KEYCODE_3",
+        "KEYCODE_4",
+        "KEYCODE_5",
+        "KEYCODE_6",
+        "KEYCODE_7",
+        "KEYCODE_8",
+        "KEYCODE_9",
+        "KEYCODE_STAR",
+        "KEYCODE_POUND",
+        "KEYCODE_DPAD_UP",
+        "KEYCODE_DPAD_DOWN",
+        "KEYCODE_DPAD_LEFT",
+        "KEYCODE_DPAD_RIGHT",
+        "KEYCODE_DPAD_CENTER",
+        "KEYCODE_VOLUME_UP",
+        "KEYCODE_VOLUME_DOWN",
+        "KEYCODE_POWER",
+        "KEYCODE_CAMERA",
+        "KEYCODE_CLEAR",
+        "KEYCODE_A",
+        "KEYCODE_B",
+        "KEYCODE_C",
+        "KEYCODE_D",
+        "KEYCODE_E",
+        "KEYCODE_F",
+        "KEYCODE_G",
+        "KEYCODE_H",
+        "KEYCODE_I",
+        "KEYCODE_J",
+        "KEYCODE_K",
+        "KEYCODE_L",
+        "KEYCODE_M",
+        "KEYCODE_N",
+        "KEYCODE_O",
+        "KEYCODE_P",
+        "KEYCODE_Q",
+        "KEYCODE_R",
+        "KEYCODE_S",
+        "KEYCODE_T",
+        "KEYCODE_U",
+        "KEYCODE_V",
+        "KEYCODE_W",
+        "KEYCODE_X",
+        "KEYCODE_Y",
+        "KEYCODE_Z",
+        "KEYCODE_COMMA",
+        "KEYCODE_PERIOD",
+        "KEYCODE_ALT_LEFT",
+        "KEYCODE_ALT_RIGHT",
+        "KEYCODE_SHIFT_LEFT",
+        "KEYCODE_SHIFT_RIGHT",
+        "KEYCODE_TAB",
+        "KEYCODE_SPACE",
+        "KEYCODE_SYM",
+        "KEYCODE_EXPLORER",
+        "KEYCODE_ENVELOPE",
+        "KEYCODE_ENTER",
+        "KEYCODE_DEL",
+        "KEYCODE_GRAVE",
+        "KEYCODE_MINUS",
+        "KEYCODE_EQUALS",
+        "KEYCODE_LEFT_BRACKET",
+        "KEYCODE_RIGHT_BRACKET",
+        "KEYCODE_BACKSLASH",
+        "KEYCODE_SEMICOLON",
+        "KEYCODE_APOSTROPHE",
+        "KEYCODE_SLASH",
+        "KEYCODE_AT",
+        "KEYCODE_NUM",
+        "KEYCODE_HEADSETHOOK",
+        "KEYCODE_FOCUS",
+        "KEYCODE_PLUS",
+        "KEYCODE_MENU",
+        "KEYCODE_NOTIFICATION",
+        "KEYCODE_SEARCH",
+        "KEYCODE_MEDIA_PLAY_PAUSE",
+        "KEYCODE_MEDIA_STOP",
+        "KEYCODE_MEDIA_NEXT",
+        "KEYCODE_MEDIA_PREVIOUS",
+        "KEYCODE_MEDIA_REWIND",
+        "KEYCODE_MEDIA_FAST_FORWARD",
+        "KEYCODE_MUTE",
+        "KEYCODE_PAGE_UP",
+        "KEYCODE_PAGE_DOWN",
+        "KEYCODE_PICTSYMBOLS",
+        "KEYCODE_SWITCH_CHARSET",
+        "KEYCODE_BUTTON_A",
+        "KEYCODE_BUTTON_B",
+        "KEYCODE_BUTTON_C",
+        "KEYCODE_BUTTON_X",
+        "KEYCODE_BUTTON_Y",
+        "KEYCODE_BUTTON_Z",
+        "KEYCODE_BUTTON_L1",
+        "KEYCODE_BUTTON_R1",
+        "KEYCODE_BUTTON_L2",
+        "KEYCODE_BUTTON_R2",
+        "KEYCODE_BUTTON_THUMBL",
+        "KEYCODE_BUTTON_THUMBR",
+        "KEYCODE_BUTTON_START",
+        "KEYCODE_BUTTON_SELECT",
+        "KEYCODE_BUTTON_MODE",
+        "KEYCODE_ESCAPE",
+        "KEYCODE_FORWARD_DEL",
+        "KEYCODE_CTRL_LEFT",
+        "KEYCODE_CTRL_RIGHT",
+        "KEYCODE_CAPS_LOCK",
+        "KEYCODE_SCROLL_LOCK",
+        "KEYCODE_META_LEFT",
+        "KEYCODE_META_RIGHT",
+        "KEYCODE_FUNCTION",
+        "KEYCODE_SYSRQ",
+        "KEYCODE_BREAK",
+        "KEYCODE_MOVE_HOME",
+        "KEYCODE_MOVE_END",
+        "KEYCODE_INSERT",
+        "KEYCODE_FORWARD",
+        "KEYCODE_MEDIA_PLAY",
+        "KEYCODE_MEDIA_PAUSE",
+        "KEYCODE_MEDIA_CLOSE",
+        "KEYCODE_MEDIA_EJECT",
+        "KEYCODE_MEDIA_RECORD",
+        "KEYCODE_F1",
+        "KEYCODE_F2",
+        "KEYCODE_F3",
+        "KEYCODE_F4",
+        "KEYCODE_F5",
+        "KEYCODE_F6",
+        "KEYCODE_F7",
+        "KEYCODE_F8",
+        "KEYCODE_F9",
+        "KEYCODE_F10",
+        "KEYCODE_F11",
+        "KEYCODE_F12",
+        "KEYCODE_NUM_LOCK",
+        "KEYCODE_NUMPAD_0",
+        "KEYCODE_NUMPAD_1",
+        "KEYCODE_NUMPAD_2",
+        "KEYCODE_NUMPAD_3",
+        "KEYCODE_NUMPAD_4",
+        "KEYCODE_NUMPAD_5",
+        "KEYCODE_NUMPAD_6",
+        "KEYCODE_NUMPAD_7",
+        "KEYCODE_NUMPAD_8",
+        "KEYCODE_NUMPAD_9",
+        "KEYCODE_NUMPAD_DIVIDE",
+        "KEYCODE_NUMPAD_MULTIPLY",
+        "KEYCODE_MUMPAD_SUBTRACT",
+        "KEYCODE_NUMPAD_ADD",
+        "KEYCODE_NUMPAD_DOT",
+        "KEYCODE_NUMPAD_COMMA",
+        "KEYCODE_NUMPAD_ENTER",
+        "KEYCODE_NUMPAD_EQUALS",
+        "KEYCODE_NUMPAD_LEFT_PAREN",
+        "KEYCODE_NUMPAD_RIGHT_PAREN",
+        "KEYCODE_VOLUME_MUTE",
+    };
+
+    // Symbolic names of all metakeys in bit order from least significant to most significant.
+    // Accordingly there are exactly 32 values in this table.
+    private static final String[] META_SYMBOLIC_NAMES = new String[] {
+        "META_SHIFT_ON",
+        "META_ALT_ON",
+        "META_SYM_ON",
+        "META_FUNCTION_ON",
+        "META_ALT_LEFT_ON",
+        "META_ALT_RIGHT_ON",
+        "META_SHIFT_LEFT_ON",
+        "META_SHIFT_RIGHT_ON",
+        "META_CAP_LOCKED",
+        "META_ALT_LOCKED",
+        "META_SYM_LOCKED",
+        "0x00000800",
+        "META_CTRL_ON",
+        "META_CTRL_LEFT_ON",
+        "META_CTRL_RIGHT_ON",
+        "0x00008000",
+        "META_META_ON",
+        "META_META_LEFT_ON",
+        "META_META_RIGHT_ON",
+        "0x00080000",
+        "META_CAPS_LOCK_ON",
+        "META_NUM_LOCK_ON",
+        "META_SCROLL_LOCK_ON",
+        "0x00800000",
+        "0x01000000",
+        "0x02000000",
+        "0x04000000",
+        "0x08000000",
+        "0x10000000",
+        "0x20000000",
+        "0x40000000",
+        "0x80000000",
+    };
+
     /**
      * @deprecated There are now more than MAX_KEYCODE keycodes.
      * Use {@link #getMaxKeyCode()} instead.
@@ -377,6 +715,35 @@
     public static final int ACTION_MULTIPLE         = 2;
 
     /**
+     * SHIFT key locked in CAPS mode.
+     * Reserved for use by {@link MetaKeyKeyListener} for a published constant in its API.
+     * @hide
+     */
+    public static final int META_CAP_LOCKED = 0x100;
+
+    /**
+     * ALT key locked.
+     * Reserved for use by {@link MetaKeyKeyListener} for a published constant in its API.
+     * @hide
+     */
+    public static final int META_ALT_LOCKED = 0x200;
+
+    /**
+     * SYM key locked.
+     * Reserved for use by {@link MetaKeyKeyListener} for a published constant in its API.
+     * @hide
+     */
+    public static final int META_SYM_LOCKED = 0x400;
+
+    /**
+     * Text is in selection mode.
+     * Reserved for use by {@link MetaKeyKeyListener} for a private unpublished constant
+     * in its API that is currently being retained for legacy reasons.
+     * @hide
+     */
+    public static final int META_SELECTING = 0x800;
+
+    /**
      * <p>This mask is used to check whether one of the ALT meta keys is pressed.</p>
      *
      * @see #isAltPressed()
@@ -441,6 +808,97 @@
     public static final int META_SYM_ON = 0x4;
 
     /**
+     * <p>This mask is used to check whether the FUNCTION meta key is pressed.</p>
+     *
+     * @see #isFunctionPressed()
+     * @see #getMetaState()
+     */
+    public static final int META_FUNCTION_ON = 0x8;
+
+    /**
+     * <p>This mask is used to check whether one of the CTRL meta keys is pressed.</p>
+     *
+     * @see #isCtrlPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_CTRL_LEFT
+     * @see #KEYCODE_CTRL_RIGHT
+     */
+    public static final int META_CTRL_ON = 0x1000;
+
+    /**
+     * <p>This mask is used to check whether the left CTRL meta key is pressed.</p>
+     *
+     * @see #isCtrlPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_CTRL_LEFT
+     */
+    public static final int META_CTRL_LEFT_ON = 0x2000;
+
+    /**
+     * <p>This mask is used to check whether the right CTRL meta key is pressed.</p>
+     *
+     * @see #isCtrlPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_CTRL_RIGHT
+     */
+    public static final int META_CTRL_RIGHT_ON = 0x4000;
+
+    /**
+     * <p>This mask is used to check whether one of the META meta keys is pressed.</p>
+     *
+     * @see #isMetaPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_META_LEFT
+     * @see #KEYCODE_META_RIGHT
+     */
+    public static final int META_META_ON = 0x10000;
+
+    /**
+     * <p>This mask is used to check whether the left META meta key is pressed.</p>
+     *
+     * @see #isMetaPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_META_LEFT
+     */
+    public static final int META_META_LEFT_ON = 0x20000;
+
+    /**
+     * <p>This mask is used to check whether the right META meta key is pressed.</p>
+     *
+     * @see #isMetaPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_META_RIGHT
+     */
+    public static final int META_META_RIGHT_ON = 0x40000;
+
+    /**
+     * <p>This mask is used to check whether the CAPS LOCK meta key is on.</p>
+     *
+     * @see #isCapsLockOn()
+     * @see #getMetaState()
+     * @see #KEYCODE_CAPS_LOCK
+     */
+    public static final int META_CAPS_LOCK_ON = 0x100000;
+
+    /**
+     * <p>This mask is used to check whether the NUM LOCK meta key is on.</p>
+     *
+     * @see #isNumLockOn()
+     * @see #getMetaState()
+     * @see #KEYCODE_NUM_LOCK
+     */
+    public static final int META_NUM_LOCK_ON = 0x200000;
+
+    /**
+     * <p>This mask is used to check whether the SCROLL LOCK meta key is on.</p>
+     *
+     * @see #isScrollLockOn()
+     * @see #getMetaState()
+     * @see #KEYCODE_SCROLL_LOCK
+     */
+    public static final int META_SCROLL_LOCK_ON = 0x400000;
+
+    /**
      * This mask is set if the device woke because of this key event.
      */
     public static final int FLAG_WOKE_HERE = 0x1;
@@ -603,6 +1061,17 @@
         boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
     }
 
+    static {
+        if (META_SYMBOLIC_NAMES.length != 32) {
+            throw new IllegalStateException(
+                    "META_SYMBOLIC_NAMES array should contain exactly 32 entries.");
+        }
+        if (KEYCODE_SYMBOLIC_NAMES.length != LAST_KEYCODE + 1) {
+            throw new IllegalStateException(
+                    "KEYCODE_SYMBOLIC_NAMES array is out of sync with the keycode constants.");
+        }
+    }
+
     /**
      * Create a new key event.
      * 
@@ -939,9 +1408,29 @@
      * @see #isAltPressed()
      * @see #isShiftPressed()
      * @see #isSymPressed()
+     * @see #isCtrlPressed()
+     * @see #isMetaPressed()
+     * @see #isFunctionPressed()
+     * @see #isCapsLockOn()
+     * @see #isNumLockOn()
+     * @see #isScrollLockOn()
      * @see #META_ALT_ON
+     * @see #META_ALT_LEFT_ON
+     * @see #META_ALT_RIGHT_ON
      * @see #META_SHIFT_ON
+     * @see #META_SHIFT_LEFT_ON
+     * @see #META_SHIFT_RIGHT_ON
      * @see #META_SYM_ON
+     * @see #META_FUNCTION_ON
+     * @see #META_CTRL_ON
+     * @see #META_CTRL_LEFT_ON
+     * @see #META_CTRL_RIGHT_ON
+     * @see #META_META_ON
+     * @see #META_META_LEFT_ON
+     * @see #META_META_RIGHT_ON
+     * @see #META_CAPS_LOCK_ON
+     * @see #META_NUM_LOCK_ON
+     * @see #META_SCROLL_LOCK_ON
      */
     public final int getMetaState() {
         return mMetaState;
@@ -961,13 +1450,28 @@
      *
      * @return whether the provided keyCode is one of
      * {@link #KEYCODE_SHIFT_LEFT} {@link #KEYCODE_SHIFT_RIGHT},
-     * {@link #KEYCODE_ALT_LEFT}, {@link #KEYCODE_ALT_RIGHT}
-     * or {@link #KEYCODE_SYM}.
+     * {@link #KEYCODE_ALT_LEFT}, {@link #KEYCODE_ALT_RIGHT},
+     * {@link #KEYCODE_SYM}, {@link #KEYCODE_NUM}, {@link #KEYCODE_FUNCTION},
+     * {@link #KEYCODE_CTRL_LEFT}, {@link #KEYCODE_CTRL_RIGHT},
+     * {@link #KEYCODE_META_LEFT}, or {@link #KEYCODE_META_RIGHT}.
      */
     public static boolean isModifierKey(int keyCode) {
-        return keyCode == KEYCODE_SHIFT_LEFT || keyCode == KEYCODE_SHIFT_RIGHT
-                || keyCode == KEYCODE_ALT_LEFT || keyCode == KEYCODE_ALT_RIGHT
-                || keyCode == KEYCODE_SYM;
+        switch (keyCode) {
+            case KEYCODE_SHIFT_LEFT:
+            case KEYCODE_SHIFT_RIGHT:
+            case KEYCODE_ALT_LEFT:
+            case KEYCODE_ALT_RIGHT:
+            case KEYCODE_SYM:
+            case KEYCODE_NUM:
+            case KEYCODE_FUNCTION:
+            case KEYCODE_CTRL_LEFT:
+            case KEYCODE_CTRL_RIGHT:
+            case KEYCODE_META_LEFT:
+            case KEYCODE_META_RIGHT:
+                return true;
+            default:
+                return false;
+        }
     }
 
     /**
@@ -1009,6 +1513,80 @@
     }
 
     /**
+     * <p>Returns the pressed state of the CTRL meta key.</p>
+     *
+     * @return true if the CTRL key is pressed, false otherwise
+     *
+     * @see #KEYCODE_CTRL_LEFT
+     * @see #KEYCODE_CTRL_RIGHT
+     * @see #META_CTRL_ON
+     */
+    public final boolean isCtrlPressed() {
+        return (mMetaState & META_CTRL_ON) != 0;
+    }
+
+    /**
+     * <p>Returns the pressed state of the META meta key.</p>
+     *
+     * @return true if the META key is pressed, false otherwise
+     *
+     * @see #KEYCODE_META_LEFT
+     * @see #KEYCODE_META_RIGHT
+     * @see #META_META_ON
+     */
+    public final boolean isMetaPressed() {
+        return (mMetaState & META_META_ON) != 0;
+    }
+
+    /**
+     * <p>Returns the pressed state of the FUNCTION meta key.</p>
+     *
+     * @return true if the FUNCTION key is pressed, false otherwise
+     *
+     * @see #KEYCODE_FUNCTION
+     * @see #META_FUNCTION_ON
+     */
+    public final boolean isFunctionPressed() {
+        return (mMetaState & META_FUNCTION_ON) != 0;
+    }
+
+    /**
+     * <p>Returns the locked state of the CAPS LOCK meta key.</p>
+     *
+     * @return true if the CAPS LOCK key is on, false otherwise
+     *
+     * @see #KEYCODE_CAPS_LOCK
+     * @see #META_CAPS_LOCK_ON
+     */
+    public final boolean isCapsLockOn() {
+        return (mMetaState & META_CAPS_LOCK_ON) != 0;
+    }
+
+    /**
+     * <p>Returns the locked state of the NUM LOCK meta key.</p>
+     *
+     * @return true if the NUM LOCK key is on, false otherwise
+     *
+     * @see #KEYCODE_NUM_LOCK
+     * @see #META_NUM_LOCK_ON
+     */
+    public final boolean isNumLockOn() {
+        return (mMetaState & META_NUM_LOCK_ON) != 0;
+    }
+
+    /**
+     * <p>Returns the locked state of the SCROLL LOCK meta key.</p>
+     *
+     * @return true if the SCROLL LOCK key is on, false otherwise
+     *
+     * @see #KEYCODE_SCROLL_LOCK
+     * @see #META_SCROLL_LOCK_ON
+     */
+    public final boolean isScrollLockOn() {
+        return (mMetaState & META_SCROLL_LOCK_ON) != 0;
+    }
+
+    /**
      * Retrieve the action of this key event.  May be either
      * {@link #ACTION_DOWN}, {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}.
      * 
@@ -1392,12 +1970,118 @@
             }
         }
     }
-    
+
+    @Override
     public String toString() {
-        return "KeyEvent{action=" + mAction + " code=" + mKeyCode
-            + " repeat=" + mRepeatCount
-            + " meta=" + mMetaState + " scancode=" + mScanCode
-            + " mFlags=" + mFlags + "}";
+        return "KeyEvent{action=" + actionToString(mAction)
+                + " keycode=" + keyCodeToString(mKeyCode)
+                + " scancode=" + mScanCode
+                + " meta=" + metaStateToString(mMetaState)
+                + " flags=0x" + Integer.toHexString(mFlags)
+                + " repeat=" + mRepeatCount
+                + " device=" + mDeviceId
+                + " source=0x" + Integer.toHexString(mSource)
+                + "}";
+    }
+
+    /**
+     * Returns a string that represents the symbolic name of the specified action
+     * such as "ACTION_DOWN", or "35" (if unknown).
+     *
+     * @param action The action.
+     * @return The symbolic name of the specified action.
+     * @hide
+     */
+    public static String actionToString(int action) {
+        switch (action) {
+            case ACTION_DOWN:
+                return "ACTION_DOWN";
+            case ACTION_UP:
+                return "ACTION_UP";
+            case ACTION_MULTIPLE:
+                return "ACTION_MULTIPLE";
+            default:
+                return Integer.toString(action);
+        }
+    }
+
+    /**
+     * Returns a string that represents the symbolic name of the specified keycode
+     * such as "KEYCODE_A", "KEYCODE_DPAD_UP", or "1001" (if unknown).
+     *
+     * @param keyCode The key code.
+     * @return The symbolic name of the specified keycode.
+     *
+     * @see KeyCharacterMap#getDisplayLabel
+     * @hide
+     */
+    public static String keyCodeToString(int keyCode) {
+        if (keyCode >= 0 && keyCode < KEYCODE_SYMBOLIC_NAMES.length) {
+            return KEYCODE_SYMBOLIC_NAMES[keyCode];
+        }
+        return Integer.toString(keyCode);
+    }
+
+    /**
+     * Gets a keycode by its symbolic name such as "KEYCODE_A" or "1001" (if unknown).
+     *
+     * @param symbolicName The symbolic name of the keycode.
+     * @return The keycode or -1 if not found.
+     * @see #keycodeToString
+     * @hide
+     */
+    public static int keyCodeFromString(String symbolicName) {
+        if (symbolicName == null) {
+            throw new IllegalArgumentException("symbolicName must not be null");
+        }
+
+        final int count = KEYCODE_SYMBOLIC_NAMES.length;
+        for (int i = 0; i < count; i++) {
+            if (symbolicName.equals(KEYCODE_SYMBOLIC_NAMES[i])) {
+                return i;
+            }
+        }
+
+        try {
+            return Integer.parseInt(symbolicName,10);
+        } catch (NumberFormatException ex) {
+            return -1;
+        }
+    }
+
+    /**
+     * Returns a string that represents the symbolic name of the specified combined meta
+     * key modifier state flags such as "0", "META_SHIFT_ON",
+     * "META_ALT_ON|META_SHIFT_ON" or "0x10000000" (if unknown).
+     *
+     * @param metaState The meta state.
+     * @return The symbolic name of the specified combined meta state flags.
+     * @hide
+     */
+    public static String metaStateToString(int metaState) {
+        if (metaState == 0) {
+            return "0";
+        }
+        StringBuilder result = null;
+        int i = 0;
+        while (metaState != 0) {
+            final boolean isSet = (metaState & 1) != 0;
+            metaState >>>= 1; // unsigned shift!
+            if (isSet) {
+                final String name = META_SYMBOLIC_NAMES[i];
+                if (result == null) {
+                    if (metaState == 0) {
+                        return name;
+                    }
+                    result = new StringBuilder(name);
+                } else {
+                    result.append('|');
+                    result.append(name);
+                }
+            }
+            i += 1;
+        }
+        return result.toString();
     }
 
     public static final Parcelable.Creator<KeyEvent> CREATOR
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 194c013..d24af52 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -16,15 +16,15 @@
 
 package android.view;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.util.AttributeSet;
 import android.util.Xml;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.util.HashMap;
@@ -67,15 +67,16 @@
     // these are optional, set by the caller
     private boolean mFactorySet;
     private Factory mFactory;
+    private Factory2 mFactory2;
     private Filter mFilter;
 
     private final Object[] mConstructorArgs = new Object[2];
 
-    private static final Class[] mConstructorSignature = new Class[] {
+    private static final Class<?>[] mConstructorSignature = new Class[] {
             Context.class, AttributeSet.class};
 
-    private static final HashMap<String, Constructor> sConstructorMap =
-            new HashMap<String, Constructor>();
+    private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
+            new HashMap<String, Constructor<? extends View>>();
     
     private HashMap<String, Boolean> mFilterMap;
 
@@ -97,6 +98,7 @@
          * 
          * @return True if this class is allowed to be inflated, or false otherwise
          */
+        @SuppressWarnings("unchecked")
         boolean onLoadClass(Class clazz);
     }
     
@@ -121,12 +123,33 @@
         public View onCreateView(String name, Context context, AttributeSet attrs);
     }
 
-    private static class FactoryMerger implements Factory {
+    public interface Factory2 extends Factory {
+        /**
+         * Version of {@link #onCreateView(String, Context, AttributeSet)}
+         * that also supplies the parent that the view created view will be
+         * placed in.
+         *
+         * @param parent The parent that the created view will be placed
+         * in; <em>note that this may be null</em>.
+         * @param name Tag name to be inflated.
+         * @param context The context the view is being created in.
+         * @param attrs Inflation attributes as specified in XML file.
+         *
+         * @return View Newly created view. Return null for the default
+         *         behavior.
+         */
+        public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
+    }
+
+    private static class FactoryMerger implements Factory2 {
         private final Factory mF1, mF2;
+        private final Factory2 mF12, mF22;
         
-        FactoryMerger(Factory f1, Factory f2) {
+        FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
             mF1 = f1;
             mF2 = f2;
+            mF12 = f12;
+            mF22 = f22;
         }
         
         public View onCreateView(String name, Context context, AttributeSet attrs) {
@@ -134,6 +157,14 @@
             if (v != null) return v;
             return mF2.onCreateView(name, context, attrs);
         }
+
+        public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+            View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs)
+                    : mF1.onCreateView(name, context, attrs);
+            if (v != null) return v;
+            return mF22 != null ? mF22.onCreateView(parent, name, context, attrs)
+                    : mF2.onCreateView(name, context, attrs);
+        }
     }
     
     /**
@@ -161,6 +192,7 @@
     protected LayoutInflater(LayoutInflater original, Context newContext) {
         mContext = newContext;
         mFactory = original.mFactory;
+        mFactory2 = original.mFactory2;
         mFilter = original.mFilter;
     }
     
@@ -199,7 +231,7 @@
     }
 
     /**
-     * Return the current factory (or null). This is called on each element
+     * Return the current {@link Factory} (or null). This is called on each element
      * name. If the factory returns a View, add that to the hierarchy. If it
      * returns null, proceed to call onCreateView(name).
      */
@@ -208,6 +240,17 @@
     }
 
     /**
+     * Return the current {@link Factory2}.  Returns null if no factory is set
+     * or the set factory does not implement the {@link Factory2} interface.
+     * This is called on each element
+     * name. If the factory returns a View, add that to the hierarchy. If it
+     * returns null, proceed to call onCreateView(name).
+     */
+    public final Factory2 getFactory2() {
+        return mFactory2;
+    }
+
+    /**
      * Attach a custom Factory interface for creating views while using
      * this LayoutInflater.  This must not be null, and can only be set once;
      * after setting, you can not change the factory.  This is
@@ -233,7 +276,26 @@
         if (mFactory == null) {
             mFactory = factory;
         } else {
-            mFactory = new FactoryMerger(factory, mFactory);
+            mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
+        }
+    }
+
+    /**
+     * Like {@link #setFactory}, but allows you to set a {@link Factory2}
+     * interface.
+     */
+    public void setFactory2(Factory2 factory) {
+        if (mFactorySet) {
+            throw new IllegalStateException("A factory has already been set on this LayoutInflater");
+        }
+        if (factory == null) {
+            throw new NullPointerException("Given factory can not be null");
+        }
+        mFactorySet = true;
+        if (mFactory == null) {
+            mFactory = mFactory2 = factory;
+        } else {
+            mFactory = new FactoryMerger(factory, factory, mFactory, mFactory2);
         }
     }
 
@@ -380,10 +442,10 @@
                                 + "ViewGroup root and attachToRoot=true");
                     }
 
-                    rInflate(parser, root, attrs);
+                    rInflate(parser, root, attrs, false);
                 } else {
                     // Temp is the root view that was found in the xml
-                    View temp = createViewFromTag(name, attrs);
+                    View temp = createViewFromTag(root, name, attrs);
 
                     ViewGroup.LayoutParams params = null;
 
@@ -405,7 +467,7 @@
                         System.out.println("-----> start inflating children");
                     }
                     // Inflate all children under temp
-                    rInflate(parser, temp, attrs);
+                    rInflate(parser, temp, attrs, true);
                     if (DEBUG) {
                         System.out.println("-----> done inflating children");
                     }
@@ -458,18 +520,18 @@
      * @param name The full name of the class to be instantiated.
      * @param attrs The XML attributes supplied for this instance.
      * 
-     * @return View The newly instantied view, or null.
+     * @return View The newly instantiated view, or null.
      */
     public final View createView(String name, String prefix, AttributeSet attrs)
             throws ClassNotFoundException, InflateException {
-        Constructor constructor = sConstructorMap.get(name);
-        Class clazz = null;
+        Constructor<? extends View> constructor = sConstructorMap.get(name);
+        Class<? extends View> clazz = null;
 
         try {
             if (constructor == null) {
                 // Class not found in the cache, see if it's real, and try to add it
                 clazz = mContext.getClassLoader().loadClass(
-                        prefix != null ? (prefix + name) : name);
+                        prefix != null ? (prefix + name) : name).asSubclass(View.class);
                 
                 if (mFilter != null && clazz != null) {
                     boolean allowed = mFilter.onLoadClass(clazz);
@@ -487,7 +549,7 @@
                     if (allowedState == null) {
                         // New class -- remember whether it is allowed
                         clazz = mContext.getClassLoader().loadClass(
-                                prefix != null ? (prefix + name) : name);
+                                prefix != null ? (prefix + name) : name).asSubclass(View.class);
                         
                         boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
                         mFilterMap.put(name, allowed);
@@ -502,7 +564,7 @@
 
             Object[] args = mConstructorArgs;
             args[1] = attrs;
-            return (View) constructor.newInstance(args);
+            return constructor.newInstance(args);
 
         } catch (NoSuchMethodException e) {
             InflateException ie = new InflateException(attrs.getPositionDescription()
@@ -511,6 +573,13 @@
             ie.initCause(e);
             throw ie;
 
+        } catch (ClassCastException e) {
+            // If loaded class is not a View subclass
+            InflateException ie = new InflateException(attrs.getPositionDescription()
+                    + ": Class is not a View "
+                    + (prefix != null ? (prefix + name) : name));
+            ie.initCause(e);
+            throw ie;
         } catch (ClassNotFoundException e) {
             // If loadClass fails, we should propagate the exception.
             throw e;
@@ -524,7 +593,7 @@
     }
 
     /**
-     * Throw an excpetion because the specified class is not allowed to be inflated.
+     * Throw an exception because the specified class is not allowed to be inflated.
      */
     private void failNotAllowed(String name, String prefix, AttributeSet attrs) {
         InflateException ie = new InflateException(attrs.getPositionDescription()
@@ -549,10 +618,27 @@
         return createView(name, "android.view.", attrs);
     }
 
+    /**
+     * Version of {@link #onCreateView(String, AttributeSet)} that also
+     * takes the future parent of the view being constructure.  The default
+     * implementation simply calls {@link #onCreateView(String, AttributeSet)}.
+     *
+     * @param parent The future parent of the returned view.  <em>Note that
+     * this may be null.</em>
+     * @param name The fully qualified class name of the View to be create.
+     * @param attrs An AttributeSet of attributes to apply to the View.
+     *
+     * @return View The View created.
+     */
+    protected View onCreateView(View parent, String name, AttributeSet attrs)
+            throws ClassNotFoundException {
+        return onCreateView(name, attrs);
+    }
+
     /*
      * default visibility so the BridgeInflater can override it.
      */
-    View createViewFromTag(String name, AttributeSet attrs) {
+    View createViewFromTag(View parent, String name, AttributeSet attrs) {
         if (name.equals("view")) {
             name = attrs.getAttributeValue(null, "class");
         }
@@ -560,12 +646,14 @@
         if (DEBUG) System.out.println("******** Creating view: " + name);
 
         try {
-            View view = (mFactory == null) ? null : mFactory.onCreateView(name,
-                    mContext, attrs);
+            View view;
+            if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs);
+            else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs);
+            else view = null;
 
             if (view == null) {
                 if (-1 == name.indexOf('.')) {
-                    view = onCreateView(name, attrs);
+                    view = onCreateView(parent, name, attrs);
                 } else {
                     view = createView(name, null, attrs);
                 }
@@ -595,8 +683,8 @@
      * Recursive method used to descend down the xml hierarchy and instantiate
      * views, instantiate their children, and then call onFinishInflate().
      */
-    private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)
-            throws XmlPullParserException, IOException {
+    private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
+            boolean finishInflate) throws XmlPullParserException, IOException {
 
         final int depth = parser.getDepth();
         int type;
@@ -620,15 +708,15 @@
             } else if (TAG_MERGE.equals(name)) {
                 throw new InflateException("<merge /> must be the root element");
             } else {
-                final View view = createViewFromTag(name, attrs);
+                final View view = createViewFromTag(parent, name, attrs);
                 final ViewGroup viewGroup = (ViewGroup) parent;
                 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
-                rInflate(parser, view, attrs);
+                rInflate(parser, view, attrs, true);
                 viewGroup.addView(view, params);
             }
         }
 
-        parent.onFinishInflate();
+        if (finishInflate) parent.onFinishInflate();
     }
 
     private void parseRequestFocus(XmlPullParser parser, View parent)
@@ -679,9 +767,9 @@
 
                     if (TAG_MERGE.equals(childName)) {
                         // Inflate all children.
-                        rInflate(childParser, parent, childAttrs);
+                        rInflate(childParser, parent, childAttrs, false);
                     } else {
-                        final View view = createViewFromTag(childName, childAttrs);
+                        final View view = createViewFromTag(parent, childName, childAttrs);
                         final ViewGroup group = (ViewGroup) parent;
 
                         // We try to load the layout params set in the <include /> tag. If
@@ -704,7 +792,7 @@
                         }
 
                         // Inflate all children.
-                        rInflate(childParser, view, childAttrs);
+                        rInflate(childParser, view, childAttrs, true);
 
                         // Attempt to override the included layout's android:id with the
                         // one set on the <include /> tag itself.
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index 46c805c..7d5dcd8 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -18,8 +18,6 @@
 
 import com.android.internal.view.menu.MenuItemImpl;
 
-import java.io.IOException;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -30,6 +28,10 @@
 import android.util.AttributeSet;
 import android.util.Xml;
 
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
 /**
  * This class is used to instantiate menu XML files into Menu objects.
  * <p>
@@ -51,6 +53,8 @@
 
     private static final int NO_ID = 0;
     
+    private static final Class<?>[] ACTION_VIEW_CONSTRUCTOR_SIGNATURE = new Class[]{Context.class};
+
     private Context mContext;
     
     /**
@@ -166,6 +170,41 @@
         }
     }
     
+    private static class InflatedOnMenuItemClickListener
+            implements MenuItem.OnMenuItemClickListener {
+        private static final Class[] PARAM_TYPES = new Class[] { MenuItem.class };
+        
+        private Context mContext;
+        private Method mMethod;
+        
+        public InflatedOnMenuItemClickListener(Context context, String methodName) {
+            mContext = context;
+            Class c = context.getClass();
+            try {
+                mMethod = c.getMethod(methodName, PARAM_TYPES);
+            } catch (Exception e) {
+                InflateException ex = new InflateException(
+                        "Couldn't resolve menu item onClick handler " + methodName +
+                        " in class " + c.getName());
+                ex.initCause(e);
+                throw ex;
+            }
+        }
+        
+        public boolean onMenuItemClick(MenuItem item) {
+            try {
+                if (mMethod.getReturnType() == Boolean.TYPE) {
+                    return (Boolean) mMethod.invoke(mContext, item);
+                } else {
+                    mMethod.invoke(mContext, item);
+                    return true;
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+    
     /**
      * State for the current menu.
      * <p>
@@ -205,6 +244,20 @@
         private boolean itemVisible;
         private boolean itemEnabled;
         
+        /**
+         * Sync to attrs.xml enum, values in MenuItem:
+         * - 0: never
+         * - 1: ifRoom
+         * - 2: always
+         * - -1: Safe sentinel for "no value".
+         */
+        private int itemShowAsAction;
+
+        private int itemActionViewLayout;
+        private String itemActionViewClassName;
+        
+        private String itemListenerMethodName;
+        
         private static final int defaultGroupId = NO_ID;
         private static final int defaultItemId = NO_ID;
         private static final int defaultItemCategory = 0;
@@ -276,6 +329,10 @@
             itemChecked = a.getBoolean(com.android.internal.R.styleable.MenuItem_checked, defaultItemChecked);
             itemVisible = a.getBoolean(com.android.internal.R.styleable.MenuItem_visible, groupVisible);
             itemEnabled = a.getBoolean(com.android.internal.R.styleable.MenuItem_enabled, groupEnabled);
+            itemShowAsAction = a.getInt(com.android.internal.R.styleable.MenuItem_showAsAction, -1);
+            itemListenerMethodName = a.getString(com.android.internal.R.styleable.MenuItem_onClick);
+            itemActionViewLayout = a.getResourceId(com.android.internal.R.styleable.MenuItem_actionLayout, 0);
+            itemActionViewClassName = a.getString(com.android.internal.R.styleable.MenuItem_actionViewClass);
             
             a.recycle();
             
@@ -299,9 +356,38 @@
                 .setIcon(itemIconResId)
                 .setAlphabeticShortcut(itemAlphabeticShortcut)
                 .setNumericShortcut(itemNumericShortcut);
+            
+            if (itemShowAsAction >= 0) {
+                item.setShowAsAction(itemShowAsAction);
+            }
+            
+            if (itemListenerMethodName != null) {
+                if (mContext.isRestricted()) {
+                    throw new IllegalStateException("The android:onClick attribute cannot "
+                            + "be used within a restricted context");
+                }
+                item.setOnMenuItemClickListener(
+                        new InflatedOnMenuItemClickListener(mContext, itemListenerMethodName));
+            }
 
-            if (itemCheckable >= 2) {
-                ((MenuItemImpl) item).setExclusiveCheckable(true);
+            if (item instanceof MenuItemImpl) {
+                MenuItemImpl impl = (MenuItemImpl) item;
+                if (itemCheckable >= 2) {
+                    impl.setExclusiveCheckable(true);
+                }
+            }
+
+            if (itemActionViewClassName != null) {
+                try {
+                    final Class<?> clazz = Class.forName(itemActionViewClassName);
+                    Constructor<?> c = clazz.getConstructor(ACTION_VIEW_CONSTRUCTOR_SIGNATURE);
+                    item.setActionView((View) c.newInstance(mContext));
+                } catch (Exception e) {
+                    throw new InflateException(e);
+                }
+            } else if (itemActionViewLayout > 0) {
+                final LayoutInflater inflater = LayoutInflater.from(mContext);
+                item.setActionView(inflater.inflate(itemActionViewLayout, null));
             }
         }
         
diff --git a/core/java/android/view/MenuItem.java b/core/java/android/view/MenuItem.java
index fcebec5..99da43b 100644
--- a/core/java/android/view/MenuItem.java
+++ b/core/java/android/view/MenuItem.java
@@ -31,6 +31,27 @@
  * For a feature set of specific menu types, see {@link Menu}.
  */
 public interface MenuItem {
+    /*
+     * These should be kept in sync with attrs.xml enum constants for showAsAction
+     */
+    /** Never show this item as a button in an Action Bar. */
+    public static final int SHOW_AS_ACTION_NEVER = 0;
+    /** Show this item as a button in an Action Bar if the system decides there is room for it. */
+    public static final int SHOW_AS_ACTION_IF_ROOM = 1;
+    /**
+     * Always show this item as a button in an Action Bar.
+     * Use sparingly! If too many items are set to always show in the Action Bar it can
+     * crowd the Action Bar and degrade the user experience on devices with smaller screens.
+     * A good rule of thumb is to have no more than 2 items set to always show at a time.
+     */
+    public static final int SHOW_AS_ACTION_ALWAYS = 2;
+
+    /**
+     * When this item is in the action bar, always show it with a text label even if
+     * it also has an icon specified.
+     */
+    public static final int SHOW_AS_ACTION_WITH_TEXT = 4;
+    
     /**
      * Interface definition for a callback to be invoked when a menu item is
      * clicked.
@@ -381,4 +402,38 @@
      *         menu item to the menu. This can be null.
      */
     public ContextMenuInfo getMenuInfo();
+    
+    /**
+     * Sets how this item should display in the presence of an Action Bar.
+     *
+     * @param actionEnum How the item should display. One of
+     * {@link #SHOW_AS_ACTION_ALWAYS}, {@link #SHOW_AS_ACTION_IF_ROOM}, or
+     * {@link #SHOW_AS_ACTION_NEVER}. SHOW_AS_ACTION_NEVER is the default.
+     * 
+     * @see android.app.ActionBar
+     * @see #setActionView(View)
+     */
+    public void setShowAsAction(int actionEnum);
+
+    /**
+     * Set an action view for this menu item. An action view will be displayed in place
+     * of an automatically generated menu item element in the UI when this item is shown
+     * as an action within a parent.
+     *
+     * @param view View to use for presenting this item to the user.
+     * @return This Item so additional setters can be called.
+     *
+     * @see #setShowAsAction(int)
+     */
+    public MenuItem setActionView(View view);
+
+    /**
+     * Returns the currently set action view for this menu item.
+     *
+     * @return This item's action view
+     *
+     * @see #setActionView(View)
+     * @see #setShowAsAction(int)
+     */
+    public View getActionView();
 }
\ No newline at end of file
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index c2fec96..195d689 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.graphics.Matrix;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -349,6 +350,8 @@
     private RuntimeException mRecycledLocation;
     private boolean mRecycled;
 
+    private native void nativeTransform(Matrix matrix);
+
     private MotionEvent(int pointerCount, int sampleCount) {
         mPointerIdentifiers = new int[pointerCount];
         mDataSamples = new float[pointerCount * sampleCount * NUM_SAMPLE_DATA];
@@ -1415,6 +1418,19 @@
         mYOffset = y - dataSamples[lastDataSampleIndex + SAMPLE_Y];
     }
     
+    /**
+     * Applies a transformation matrix to all of the points in the event.
+     *
+     * @param matrix The transformation matrix to apply.
+     */
+    public final void transform(Matrix matrix) {
+        if (matrix == null) {
+            throw new IllegalArgumentException("matrix must not be null");
+        }
+
+        nativeTransform(matrix);
+    }
+
     private final void getPointerCoordsAtSampleIndex(int sampleIndex,
             PointerCoords outPointerCoords) {
         final float[] dataSamples = mDataSamples;
@@ -1534,8 +1550,54 @@
     @Override
     public String toString() {
         return "MotionEvent{" + Integer.toHexString(System.identityHashCode(this))
-            + " action=" + mAction + " x=" + getX()
-            + " y=" + getY() + " pressure=" + getPressure() + " size=" + getSize() + "}";
+            + " action=" + actionToString(mAction)
+            + " x=" + getX()
+            + " y=" + getY()
+            + " pressure=" + getPressure()
+            + " size=" + getSize()
+            + " touchMajor=" + getTouchMajor()
+            + " touchMinor=" + getTouchMinor()
+            + " toolMajor=" + getToolMajor()
+            + " toolMinor=" + getToolMinor()
+            + " orientation=" + getOrientation()
+            + " meta=" + KeyEvent.metaStateToString(mMetaState)
+            + " pointerCount=" + getPointerCount()
+            + " historySize=" + getHistorySize()
+            + " flags=0x" + Integer.toHexString(mFlags)
+            + " edgeFlags=0x" + Integer.toHexString(mEdgeFlags)
+            + " device=" + mDeviceId
+            + " source=0x" + Integer.toHexString(mSource)
+            + "}";
+    }
+
+    /**
+     * Returns a string that represents the symbolic name of the specified action
+     * such as "ACTION_DOWN", "ACTION_POINTER_DOWN(3)" or "35" (if unknown).
+     *
+     * @param action The action.
+     * @return The symbolic name of the specified action.
+     * @hide
+     */
+    public static String actionToString(int action) {
+        switch (action) {
+            case ACTION_DOWN:
+                return "ACTION_DOWN";
+            case ACTION_UP:
+                return "ACTION_UP";
+            case ACTION_CANCEL:
+                return "ACTION_CANCEL";
+            case ACTION_MOVE:
+                return "ACTION_MOVE";
+        }
+        int index = (action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
+        switch (action & ACTION_MASK) {
+            case ACTION_POINTER_DOWN:
+                return "ACTION_POINTER_DOWN(" + index + ")";
+            case ACTION_POINTER_UP:
+                return "ACTION_POINTER_UP(" + index + ")";
+            default:
+                return Integer.toString(action);
+        }
     }
 
     public static final Parcelable.Creator<MotionEvent> CREATOR
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 0999598..17b5dd7 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -25,7 +25,7 @@
  * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
  * callback will notify users when a particular gesture event has occurred.
  * This class should only be used with {@link MotionEvent}s reported via touch.
- * 
+ *
  * To use this class:
  * <ul>
  *  <li>Create an instance of the {@code ScaleGestureDetector} for your
@@ -41,7 +41,7 @@
      * If you want to listen for all the different gestures then implement
      * this interface. If you only want to listen for a subset it might
      * be easier to extend {@link SimpleOnScaleGestureListener}.
-     * 
+     *
      * An application will receive events in the following order:
      * <ul>
      *  <li>One {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)}
@@ -53,7 +53,7 @@
         /**
          * Responds to scaling events for a gesture in progress.
          * Reported by pointer motion.
-         * 
+         *
          * @param detector The detector reporting the event - use this to
          *          retrieve extended info about event state.
          * @return Whether or not the detector should consider this event
@@ -68,7 +68,7 @@
         /**
          * Responds to the beginning of a scaling gesture. Reported by
          * new pointers going down.
-         * 
+         *
          * @param detector The detector reporting the event - use this to
          *          retrieve extended info about event state.
          * @return Whether or not the detector should continue recognizing
@@ -82,17 +82,17 @@
         /**
          * Responds to the end of a scale gesture. Reported by existing
          * pointers going up.
-         * 
+         *
          * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
          * and {@link ScaleGestureDetector#getFocusY()} will return the location
          * of the pointer remaining on the screen.
-         * 
+         *
          * @param detector The detector reporting the event - use this to
          *          retrieve extended info about event state.
          */
         public void onScaleEnd(ScaleGestureDetector detector);
     }
-    
+
     /**
      * A convenience class to extend when you only want to listen for a subset
      * of scaling-related events. This implements all methods in
@@ -101,7 +101,7 @@
      * {@code false} so that a subclass can retrieve the accumulated scale
      * factor in an overridden onScaleEnd.
      * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} returns
-     * {@code true}. 
+     * {@code true}.
      */
     public static class SimpleOnScaleGestureListener implements OnScaleGestureListener {
 
@@ -148,7 +148,7 @@
     private float mCurrPressure;
     private float mPrevPressure;
     private long mTimeDelta;
-    
+
     private final float mEdgeSlop;
     private float mRightSlopEdge;
     private float mBottomSlopEdge;
@@ -217,7 +217,7 @@
                 }
             }
             break;
-            
+
             case MotionEvent.ACTION_MOVE:
                 if (mSloppyGesture) {
                     // Initiate sloppy gestures if we've moved outside of the slop area.
@@ -249,7 +249,7 @@
                     }
                 }
                 break;
-                
+
             case MotionEvent.ACTION_POINTER_UP:
                 if (mSloppyGesture) {
                     // Set focus point to the remaining finger
@@ -307,17 +307,17 @@
         }
         return handled;
     }
-    
+
     /**
-     * MotionEvent has no getRawX(int) method; simulate it pending future API approval. 
+     * MotionEvent has no getRawX(int) method; simulate it pending future API approval.
      */
     private static float getRawX(MotionEvent event, int pointerIndex) {
         float offset = event.getRawX() - event.getX();
         return event.getX(pointerIndex) + offset;
     }
-    
+
     /**
-     * MotionEvent has no getRawY(int) method; simulate it pending future API approval. 
+     * MotionEvent has no getRawY(int) method; simulate it pending future API approval.
      */
     private static float getRawY(MotionEvent event, int pointerIndex) {
         float offset = event.getRawY() - event.getY();
@@ -390,7 +390,7 @@
      * remaining pointer on the screen.
      * If {@link #isInProgress()} would return false, the result of this
      * function is undefined.
-     * 
+     *
      * @return X coordinate of the focal point in pixels.
      */
     public float getFocusX() {
@@ -405,7 +405,7 @@
      * remaining pointer on the screen.
      * If {@link #isInProgress()} would return false, the result of this
      * function is undefined.
-     * 
+     *
      * @return Y coordinate of the focal point in pixels.
      */
     public float getFocusY() {
@@ -415,7 +415,7 @@
     /**
      * Return the current distance between the two pointers forming the
      * gesture in progress.
-     * 
+     *
      * @return Distance between pointers in pixels.
      */
     public float getCurrentSpan() {
@@ -428,9 +428,29 @@
     }
 
     /**
+     * Return the current x distance between the two pointers forming the
+     * gesture in progress.
+     *
+     * @return Distance between pointers in pixels.
+     */
+    public float getCurrentSpanX() {
+        return mCurrFingerDiffX;
+    }
+
+    /**
+     * Return the current y distance between the two pointers forming the
+     * gesture in progress.
+     *
+     * @return Distance between pointers in pixels.
+     */
+    public float getCurrentSpanY() {
+        return mCurrFingerDiffY;
+    }
+
+    /**
      * Return the previous distance between the two pointers forming the
      * gesture in progress.
-     * 
+     *
      * @return Previous distance between pointers in pixels.
      */
     public float getPreviousSpan() {
@@ -443,10 +463,30 @@
     }
 
     /**
+     * Return the previous x distance between the two pointers forming the
+     * gesture in progress.
+     *
+     * @return Previous distance between pointers in pixels.
+     */
+    public float getPreviousSpanX() {
+        return mPrevFingerDiffX;
+    }
+
+    /**
+     * Return the previous y distance between the two pointers forming the
+     * gesture in progress.
+     *
+     * @return Previous distance between pointers in pixels.
+     */
+    public float getPreviousSpanY() {
+        return mPrevFingerDiffY;
+    }
+
+    /**
      * Return the scaling factor from the previous scale event to the current
      * event. This value is defined as
      * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}).
-     * 
+     *
      * @return The current scaling factor.
      */
     public float getScaleFactor() {
@@ -455,20 +495,20 @@
         }
         return mScaleFactor;
     }
-    
+
     /**
      * Return the time difference in milliseconds between the previous
      * accepted scaling event and the current scaling event.
-     * 
+     *
      * @return Time difference since the last scaling event in milliseconds.
      */
     public long getTimeDelta() {
         return mTimeDelta;
     }
-    
+
     /**
      * Return the event time of the current event being processed.
-     * 
+     *
      * @return Current event time in milliseconds.
      */
     public long getEventTime() {
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index cd0ae3b..d596a7f 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -86,6 +86,15 @@
      * play back.
      */
     public static final int PUSH_BUFFERS        = 0x00000200;
+    /**
+     * Indicates that the surface must be considered opaque, even if its
+     * pixel format is set to translucent. This can be useful if an
+     * application needs full RGBA 8888 support for instance but will
+     * still draw every pixel opaque.
+     * 
+     * @hide
+     */
+    public static final int OPAQUE              = 0x00000400;
     
     /** Creates a normal surface. This is the default */
     public static final int FX_SURFACE_NORMAL   = 0x00000000;
@@ -269,7 +278,7 @@
             }
             mOrigMatrix.set(m);
         }
-    };
+    }
 
     /**
      * Sets the display metrics used to provide canva's width/height in compatibility mode.
@@ -422,16 +431,20 @@
     /* no user serviceable parts here ... */
     @Override
     protected void finalize() throws Throwable {
-        if (mNativeSurface != 0 || mSurfaceControl != 0) {
-            if (DEBUG_RELEASE) {
-                Log.w(LOG_TAG, "Surface.finalize() has work. You should have called release() (" 
-                        + mNativeSurface + ", " + mSurfaceControl + ")", mCreationStack);
-            } else {
-                Log.w(LOG_TAG, "Surface.finalize() has work. You should have called release() (" 
-                        + mNativeSurface + ", " + mSurfaceControl + ")");
+        try {
+            super.finalize();
+        } finally {
+            if (mNativeSurface != 0 || mSurfaceControl != 0) {
+                if (DEBUG_RELEASE) {
+                    Log.w(LOG_TAG, "Surface.finalize() has work. You should have called release() (" 
+                            + mNativeSurface + ", " + mSurfaceControl + ")", mCreationStack);
+                } else {
+                    Log.w(LOG_TAG, "Surface.finalize() has work. You should have called release() (" 
+                            + mNativeSurface + ", " + mSurfaceControl + ")");
+                }
             }
+            release();            
         }
-        release();
     }
     
     private native void init(SurfaceSession s,
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index fb88c71..4ab2881 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -41,6 +41,7 @@
     private static final int MAX_AGE_MILLISECONDS = 200;
     
     private static final int POINTER_POOL_CAPACITY = 20;
+    private static final int INVALID_POINTER = -1;
 
     private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
             Pools.finitePool(new PoolableManager<VelocityTracker>() {
@@ -76,6 +77,7 @@
     private Pointer mPointerListHead; // sorted by id in increasing order
     private int mLastTouchIndex;
     private int mGeneration;
+    private int mActivePointerId;
 
     private VelocityTracker mNext;
 
@@ -125,6 +127,7 @@
         
         mPointerListHead = null;
         mLastTouchIndex = 0;
+        mActivePointerId = INVALID_POINTER;
     }
     
     /**
@@ -180,6 +183,10 @@
                 // Pointer went down.  Add it to the list.
                 // Write a sentinel at the end of the pastTime trace so we will be able to
                 // tell when the trace started.
+                if (mActivePointerId == INVALID_POINTER) {
+                    // Congratulations! You're the new active pointer!
+                    mActivePointerId = pointerId;
+                }
                 pointer = obtainPointer();
                 pointer.id = pointerId;
                 pointer.pastTime[lastTouchIndex] = Long.MIN_VALUE;
@@ -214,6 +221,7 @@
         previousPointer = null;
         for (Pointer pointer = mPointerListHead; pointer != null; ) {
             final Pointer nextPointer = pointer.next;
+            final int pointerId = pointer.id;
             if (pointer.generation != generation) {
                 // Pointer went up.  Remove it from the list.
                 if (previousPointer == null) {
@@ -222,6 +230,12 @@
                     previousPointer.next = nextPointer;
                 }
                 releasePointer(pointer);
+
+                if (pointerId == mActivePointerId) {
+                    // Pick a new active pointer. How is arbitrary.
+                    mActivePointerId = mPointerListHead != null ?
+                            mPointerListHead.id : INVALID_POINTER;
+                }
             } else {
                 previousPointer = pointer;
             }
@@ -334,7 +348,7 @@
      * @return The previously computed X velocity.
      */
     public float getXVelocity() {
-        Pointer pointer = getPointer(0);
+        Pointer pointer = getPointer(mActivePointerId);
         return pointer != null ? pointer.xVelocity : 0;
     }
     
@@ -345,7 +359,7 @@
      * @return The previously computed Y velocity.
      */
     public float getYVelocity() {
-        Pointer pointer = getPointer(0);
+        Pointer pointer = getPointer(mActivePointerId);
         return pointer != null ? pointer.yVelocity : 0;
     }
     
@@ -373,7 +387,7 @@
         return pointer != null ? pointer.yVelocity : 0;
     }
     
-    private final Pointer getPointer(int id) {
+    private Pointer getPointer(int id) {
         for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
             if (pointer.id == id) {
                 return pointer;
@@ -382,7 +396,7 @@
         return null;
     }
     
-    private static final Pointer obtainPointer() {
+    private static Pointer obtainPointer() {
         synchronized (sPool) {
             if (sRecycledPointerCount != 0) {
                 Pointer element = sRecycledPointerListHead;
@@ -395,7 +409,7 @@
         return new Pointer();
     }
     
-    private static final void releasePointer(Pointer pointer) {
+    private static void releasePointer(Pointer pointer) {
         synchronized (sPool) {
             if (sRecycledPointerCount < POINTER_POOL_CAPACITY) {
                 pointer.next = sRecycledPointerListHead;
@@ -405,7 +419,7 @@
         }
     }
     
-    private static final void releasePointerList(Pointer pointer) {
+    private static void releasePointerList(Pointer pointer) {
         if (pointer != null) {
             synchronized (sPool) {
                 int count = sRecycledPointerCount;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 54cedfb..340678d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,14 +16,13 @@
 
 package android.view;
 
-import com.android.internal.R;
-import com.android.internal.view.menu.MenuBuilder;
-
+import android.content.ClipData;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
+import android.graphics.Camera;
 import android.graphics.Canvas;
 import android.graphics.Interpolator;
 import android.graphics.LinearGradient;
@@ -34,6 +33,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.Region;
 import android.graphics.Shader;
 import android.graphics.drawable.ColorDrawable;
@@ -47,8 +47,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.util.AttributeSet;
-import android.util.Config;
-import android.util.EventLog;
 import android.util.Log;
 import android.util.Pool;
 import android.util.Poolable;
@@ -65,12 +63,15 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.ScrollBarDrawable;
+import com.android.internal.R;
+import com.android.internal.view.menu.MenuBuilder;
 
-import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.WeakHashMap;
 
 /**
@@ -260,7 +261,8 @@
  * <ul>
  * <li>Define a Button in the layout file and assign it a unique ID.
  * <pre>
- * &lt;Button id="@+id/my_button"
+ * &lt;Button
+ *     android:id="@+id/my_button"
  *     android:layout_width="wrap_content"
  *     android:layout_height="wrap_content"
  *     android:text="@string/my_button_text"/&gt;
@@ -917,6 +919,20 @@
     public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000;
 
     /**
+     * <p>Indicates that the view hierarchy should stop saving state when
+     * it reaches this view.  If state saving is initiated immediately at
+     * the view, it will be allowed.
+     * {@hide}
+     */
+    static final int PARENT_SAVE_DISABLED = 0x20000000;
+
+    /**
+     * <p>Mask for use with setFlags indicating bits used for PARENT_SAVE_DISABLED.</p>
+     * {@hide}
+     */
+    static final int PARENT_SAVE_DISABLED_MASK = 0x20000000;
+
+    /**
      * View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
      * should add all focusable Views regardless if they are focusable in touch mode.
      */
@@ -972,7 +988,7 @@
      * @see android.graphics.drawable.Drawable
      * @see #getDrawableState()
      */
-    protected static final int[] EMPTY_STATE_SET = {};
+    protected static final int[] EMPTY_STATE_SET;
     /**
      * Indicates the view is enabled. States are used with
      * {@link android.graphics.drawable.Drawable} to change the drawing of the
@@ -981,7 +997,7 @@
      * @see android.graphics.drawable.Drawable
      * @see #getDrawableState()
      */
-    protected static final int[] ENABLED_STATE_SET = {R.attr.state_enabled};
+    protected static final int[] ENABLED_STATE_SET;
     /**
      * Indicates the view is focused. States are used with
      * {@link android.graphics.drawable.Drawable} to change the drawing of the
@@ -990,7 +1006,7 @@
      * @see android.graphics.drawable.Drawable
      * @see #getDrawableState()
      */
-    protected static final int[] FOCUSED_STATE_SET = {R.attr.state_focused};
+    protected static final int[] FOCUSED_STATE_SET;
     /**
      * Indicates the view is selected. States are used with
      * {@link android.graphics.drawable.Drawable} to change the drawing of the
@@ -999,7 +1015,7 @@
      * @see android.graphics.drawable.Drawable
      * @see #getDrawableState()
      */
-    protected static final int[] SELECTED_STATE_SET = {R.attr.state_selected};
+    protected static final int[] SELECTED_STATE_SET;
     /**
      * Indicates the view is pressed. States are used with
      * {@link android.graphics.drawable.Drawable} to change the drawing of the
@@ -1009,7 +1025,7 @@
      * @see #getDrawableState()
      * @hide
      */
-    protected static final int[] PRESSED_STATE_SET = {R.attr.state_pressed};
+    protected static final int[] PRESSED_STATE_SET;
     /**
      * Indicates the view's window has focus. States are used with
      * {@link android.graphics.drawable.Drawable} to change the drawing of the
@@ -1018,8 +1034,7 @@
      * @see android.graphics.drawable.Drawable
      * @see #getDrawableState()
      */
-    protected static final int[] WINDOW_FOCUSED_STATE_SET =
-            {R.attr.state_window_focused};
+    protected static final int[] WINDOW_FOCUSED_STATE_SET;
     // Doubles
     /**
      * Indicates the view is enabled and has the focus.
@@ -1027,48 +1042,42 @@
      * @see #ENABLED_STATE_SET
      * @see #FOCUSED_STATE_SET
      */
-    protected static final int[] ENABLED_FOCUSED_STATE_SET =
-            stateSetUnion(ENABLED_STATE_SET, FOCUSED_STATE_SET);
+    protected static final int[] ENABLED_FOCUSED_STATE_SET;
     /**
      * Indicates the view is enabled and selected.
      *
      * @see #ENABLED_STATE_SET
      * @see #SELECTED_STATE_SET
      */
-    protected static final int[] ENABLED_SELECTED_STATE_SET =
-            stateSetUnion(ENABLED_STATE_SET, SELECTED_STATE_SET);
+    protected static final int[] ENABLED_SELECTED_STATE_SET;
     /**
      * Indicates the view is enabled and that its window has focus.
      *
      * @see #ENABLED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(ENABLED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is focused and selected.
      *
      * @see #FOCUSED_STATE_SET
      * @see #SELECTED_STATE_SET
      */
-    protected static final int[] FOCUSED_SELECTED_STATE_SET =
-            stateSetUnion(FOCUSED_STATE_SET, SELECTED_STATE_SET);
+    protected static final int[] FOCUSED_SELECTED_STATE_SET;
     /**
      * Indicates the view has the focus and that its window has the focus.
      *
      * @see #FOCUSED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is selected and that its window has the focus.
      *
      * @see #SELECTED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] SELECTED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    protected static final int[] SELECTED_WINDOW_FOCUSED_STATE_SET;
     // Triples
     /**
      * Indicates the view is enabled, focused and selected.
@@ -1077,8 +1086,7 @@
      * @see #FOCUSED_STATE_SET
      * @see #SELECTED_STATE_SET
      */
-    protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET =
-            stateSetUnion(ENABLED_FOCUSED_STATE_SET, SELECTED_STATE_SET);
+    protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET;
     /**
      * Indicates the view is enabled, focused and its window has the focus.
      *
@@ -1086,8 +1094,7 @@
      * @see #FOCUSED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(ENABLED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    protected static final int[] ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is enabled, selected and its window has the focus.
      *
@@ -1095,8 +1102,7 @@
      * @see #SELECTED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(ENABLED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    protected static final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is focused, selected and its window has the focus.
      *
@@ -1104,8 +1110,7 @@
      * @see #SELECTED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is enabled, focused, selected and its window
      * has the focus.
@@ -1115,28 +1120,21 @@
      * @see #SELECTED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(ENABLED_FOCUSED_SELECTED_STATE_SET,
-                          WINDOW_FOCUSED_STATE_SET);
-
+    protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is pressed and its window has the focus.
      *
      * @see #PRESSED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(PRESSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
-
+    protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is pressed and selected.
      *
      * @see #PRESSED_STATE_SET
      * @see #SELECTED_STATE_SET
      */
-    protected static final int[] PRESSED_SELECTED_STATE_SET =
-            stateSetUnion(PRESSED_STATE_SET, SELECTED_STATE_SET);
-
+    protected static final int[] PRESSED_SELECTED_STATE_SET;
     /**
      * Indicates the view is pressed, selected and its window has the focus.
      *
@@ -1144,18 +1142,14 @@
      * @see #SELECTED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(PRESSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
-
+    protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is pressed and focused.
      *
      * @see #PRESSED_STATE_SET
      * @see #FOCUSED_STATE_SET
      */
-    protected static final int[] PRESSED_FOCUSED_STATE_SET =
-            stateSetUnion(PRESSED_STATE_SET, FOCUSED_STATE_SET);
-
+    protected static final int[] PRESSED_FOCUSED_STATE_SET;
     /**
      * Indicates the view is pressed, focused and its window has the focus.
      *
@@ -1163,9 +1157,7 @@
      * @see #FOCUSED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(PRESSED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
-
+    protected static final int[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is pressed, focused and selected.
      *
@@ -1173,9 +1165,7 @@
      * @see #SELECTED_STATE_SET
      * @see #FOCUSED_STATE_SET
      */
-    protected static final int[] PRESSED_FOCUSED_SELECTED_STATE_SET =
-            stateSetUnion(PRESSED_FOCUSED_STATE_SET, SELECTED_STATE_SET);
-
+    protected static final int[] PRESSED_FOCUSED_SELECTED_STATE_SET;
     /**
      * Indicates the view is pressed, focused, selected and its window has the focus.
      *
@@ -1184,18 +1174,14 @@
      * @see #SELECTED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(PRESSED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
-
+    protected static final int[] PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is pressed and enabled.
      *
      * @see #PRESSED_STATE_SET
      * @see #ENABLED_STATE_SET
      */
-    protected static final int[] PRESSED_ENABLED_STATE_SET =
-            stateSetUnion(PRESSED_STATE_SET, ENABLED_STATE_SET);
-
+    protected static final int[] PRESSED_ENABLED_STATE_SET;
     /**
      * Indicates the view is pressed, enabled and its window has the focus.
      *
@@ -1203,9 +1189,7 @@
      * @see #ENABLED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(PRESSED_ENABLED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
-
+    protected static final int[] PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is pressed, enabled and selected.
      *
@@ -1213,9 +1197,7 @@
      * @see #ENABLED_STATE_SET
      * @see #SELECTED_STATE_SET
      */
-    protected static final int[] PRESSED_ENABLED_SELECTED_STATE_SET =
-            stateSetUnion(PRESSED_ENABLED_STATE_SET, SELECTED_STATE_SET);
-
+    protected static final int[] PRESSED_ENABLED_SELECTED_STATE_SET;
     /**
      * Indicates the view is pressed, enabled, selected and its window has the
      * focus.
@@ -1225,9 +1207,7 @@
      * @see #SELECTED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(PRESSED_ENABLED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
-
+    protected static final int[] PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is pressed, enabled and focused.
      *
@@ -1235,9 +1215,7 @@
      * @see #ENABLED_STATE_SET
      * @see #FOCUSED_STATE_SET
      */
-    protected static final int[] PRESSED_ENABLED_FOCUSED_STATE_SET =
-            stateSetUnion(PRESSED_ENABLED_STATE_SET, FOCUSED_STATE_SET);
-
+    protected static final int[] PRESSED_ENABLED_FOCUSED_STATE_SET;
     /**
      * Indicates the view is pressed, enabled, focused and its window has the
      * focus.
@@ -1247,9 +1225,7 @@
      * @see #FOCUSED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(PRESSED_ENABLED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
-
+    protected static final int[] PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET;
     /**
      * Indicates the view is pressed, enabled, focused and selected.
      *
@@ -1258,9 +1234,7 @@
      * @see #SELECTED_STATE_SET
      * @see #FOCUSED_STATE_SET
      */
-    protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET =
-            stateSetUnion(PRESSED_ENABLED_FOCUSED_STATE_SET, SELECTED_STATE_SET);
-
+    protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET;
     /**
      * Indicates the view is pressed, enabled, focused, selected and its window
      * has the focus.
@@ -1271,47 +1245,132 @@
      * @see #FOCUSED_STATE_SET
      * @see #WINDOW_FOCUSED_STATE_SET
      */
-    protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET =
-            stateSetUnion(PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
 
     /**
      * The order here is very important to {@link #getDrawableState()}
      */
-    private static final int[][] VIEW_STATE_SETS = {
-        EMPTY_STATE_SET,                                           // 0 0 0 0 0
-        WINDOW_FOCUSED_STATE_SET,                                  // 0 0 0 0 1
-        SELECTED_STATE_SET,                                        // 0 0 0 1 0
-        SELECTED_WINDOW_FOCUSED_STATE_SET,                         // 0 0 0 1 1
-        FOCUSED_STATE_SET,                                         // 0 0 1 0 0
-        FOCUSED_WINDOW_FOCUSED_STATE_SET,                          // 0 0 1 0 1
-        FOCUSED_SELECTED_STATE_SET,                                // 0 0 1 1 0
-        FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,                 // 0 0 1 1 1
-        ENABLED_STATE_SET,                                         // 0 1 0 0 0
-        ENABLED_WINDOW_FOCUSED_STATE_SET,                          // 0 1 0 0 1
-        ENABLED_SELECTED_STATE_SET,                                // 0 1 0 1 0
-        ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET,                 // 0 1 0 1 1
-        ENABLED_FOCUSED_STATE_SET,                                 // 0 1 1 0 0
-        ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET,                  // 0 1 1 0 1
-        ENABLED_FOCUSED_SELECTED_STATE_SET,                        // 0 1 1 1 0
-        ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,         // 0 1 1 1 1
-        PRESSED_STATE_SET,                                         // 1 0 0 0 0
-        PRESSED_WINDOW_FOCUSED_STATE_SET,                          // 1 0 0 0 1
-        PRESSED_SELECTED_STATE_SET,                                // 1 0 0 1 0
-        PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET,                 // 1 0 0 1 1
-        PRESSED_FOCUSED_STATE_SET,                                 // 1 0 1 0 0
-        PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET,                  // 1 0 1 0 1
-        PRESSED_FOCUSED_SELECTED_STATE_SET,                        // 1 0 1 1 0
-        PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,         // 1 0 1 1 1
-        PRESSED_ENABLED_STATE_SET,                                 // 1 1 0 0 0
-        PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET,                  // 1 1 0 0 1
-        PRESSED_ENABLED_SELECTED_STATE_SET,                        // 1 1 0 1 0
-        PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET,         // 1 1 0 1 1
-        PRESSED_ENABLED_FOCUSED_STATE_SET,                         // 1 1 1 0 0
-        PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET,          // 1 1 1 0 1
-        PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET,                // 1 1 1 1 0
-        PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 1 1 1 1 1
+    private static final int[][] VIEW_STATE_SETS;
+
+    static final int VIEW_STATE_WINDOW_FOCUSED = 1;
+    static final int VIEW_STATE_SELECTED = 1 << 1;
+    static final int VIEW_STATE_FOCUSED = 1 << 2;
+    static final int VIEW_STATE_ENABLED = 1 << 3;
+    static final int VIEW_STATE_PRESSED = 1 << 4;
+    static final int VIEW_STATE_ACTIVATED = 1 << 5;
+
+    static final int[] VIEW_STATE_IDS = new int[] {
+        R.attr.state_window_focused,    VIEW_STATE_WINDOW_FOCUSED,
+        R.attr.state_selected,          VIEW_STATE_SELECTED,
+        R.attr.state_focused,           VIEW_STATE_FOCUSED,
+        R.attr.state_enabled,           VIEW_STATE_ENABLED,
+        R.attr.state_pressed,           VIEW_STATE_PRESSED,
+        R.attr.state_activated,         VIEW_STATE_ACTIVATED,
     };
 
+    static {
+        int[] orderedIds = new int[VIEW_STATE_IDS.length];
+        for (int i = 0; i < R.styleable.ViewDrawableStates.length; i++) {
+            int viewState = R.styleable.ViewDrawableStates[i];
+            for (int j = 0; j<VIEW_STATE_IDS.length; j += 2) {
+                if (VIEW_STATE_IDS[j] == viewState) {
+                    orderedIds[i * 2] = viewState;
+                    orderedIds[i * 2 + 1] = VIEW_STATE_IDS[j + 1];
+                }
+            }
+        }
+        final int NUM_BITS = VIEW_STATE_IDS.length / 2;
+        VIEW_STATE_SETS = new int[1 << NUM_BITS][];
+        for (int i = 0; i < VIEW_STATE_SETS.length; i++) {
+            int numBits = Integer.bitCount(i);
+            int[] set = new int[numBits];
+            int pos = 0;
+            for (int j = 0; j < orderedIds.length; j += 2) {
+                if ((i & orderedIds[j+1]) != 0) {
+                    set[pos++] = orderedIds[j];
+                }
+            }
+            VIEW_STATE_SETS[i] = set;
+        }
+
+        EMPTY_STATE_SET = VIEW_STATE_SETS[0];
+        WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[VIEW_STATE_WINDOW_FOCUSED];
+        SELECTED_STATE_SET = VIEW_STATE_SETS[VIEW_STATE_SELECTED];
+        SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED];
+        FOCUSED_STATE_SET = VIEW_STATE_SETS[VIEW_STATE_FOCUSED];
+        FOCUSED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_FOCUSED];
+        FOCUSED_SELECTED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_SELECTED | VIEW_STATE_FOCUSED];
+        FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
+                | VIEW_STATE_FOCUSED];
+        ENABLED_STATE_SET = VIEW_STATE_SETS[VIEW_STATE_ENABLED];
+        ENABLED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_ENABLED];
+        ENABLED_SELECTED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_SELECTED | VIEW_STATE_ENABLED];
+        ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
+                | VIEW_STATE_ENABLED];
+        ENABLED_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_FOCUSED | VIEW_STATE_ENABLED];
+        ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_FOCUSED
+                | VIEW_STATE_ENABLED];
+        ENABLED_FOCUSED_SELECTED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_SELECTED | VIEW_STATE_FOCUSED
+                | VIEW_STATE_ENABLED];
+        ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
+                | VIEW_STATE_FOCUSED| VIEW_STATE_ENABLED];
+
+        PRESSED_STATE_SET = VIEW_STATE_SETS[VIEW_STATE_PRESSED];
+        PRESSED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_PRESSED];
+        PRESSED_SELECTED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_SELECTED | VIEW_STATE_PRESSED];
+        PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
+                | VIEW_STATE_PRESSED];
+        PRESSED_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_FOCUSED | VIEW_STATE_PRESSED];
+        PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_FOCUSED
+                | VIEW_STATE_PRESSED];
+        PRESSED_FOCUSED_SELECTED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_SELECTED | VIEW_STATE_FOCUSED
+                | VIEW_STATE_PRESSED];
+        PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
+                | VIEW_STATE_FOCUSED | VIEW_STATE_PRESSED];
+        PRESSED_ENABLED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_ENABLED | VIEW_STATE_PRESSED];
+        PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_ENABLED
+                | VIEW_STATE_PRESSED];
+        PRESSED_ENABLED_SELECTED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_SELECTED | VIEW_STATE_ENABLED
+                | VIEW_STATE_PRESSED];
+        PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
+                | VIEW_STATE_ENABLED | VIEW_STATE_PRESSED];
+        PRESSED_ENABLED_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_FOCUSED | VIEW_STATE_ENABLED
+                | VIEW_STATE_PRESSED];
+        PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_FOCUSED
+                | VIEW_STATE_ENABLED | VIEW_STATE_PRESSED];
+        PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_SELECTED | VIEW_STATE_FOCUSED
+                | VIEW_STATE_ENABLED | VIEW_STATE_PRESSED];
+        PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET = VIEW_STATE_SETS[
+                VIEW_STATE_WINDOW_FOCUSED | VIEW_STATE_SELECTED
+                | VIEW_STATE_FOCUSED| VIEW_STATE_ENABLED
+                | VIEW_STATE_PRESSED];
+    }
+
     /**
      * Used by views that contain lists of items. This state indicates that
      * the view is showing the last item.
@@ -1551,38 +1610,15 @@
     private static final int AWAKEN_SCROLL_BARS_ON_ATTACH = 0x08000000;
 
     /**
-     * Always allow a user to over-scroll this view, provided it is a
-     * view that can scroll.
+     * Indicates that pivotX or pivotY were explicitly set and we should not assume the center
+     * for transform operations
      *
-     * @see #getOverScrollMode()
-     * @see #setOverScrollMode(int)
+     * @hide
      */
-    public static final int OVER_SCROLL_ALWAYS = 0;
+    private static final int PIVOT_EXPLICITLY_SET = 0x20000000;
 
-    /**
-     * Allow a user to over-scroll this view only if the content is large
-     * enough to meaningfully scroll, provided it is a view that can scroll.
-     *
-     * @see #getOverScrollMode()
-     * @see #setOverScrollMode(int)
-     */
-    public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1;
-
-    /**
-     * Never allow a user to over-scroll this view.
-     *
-     * @see #getOverScrollMode()
-     * @see #setOverScrollMode(int)
-     */
-    public static final int OVER_SCROLL_NEVER = 2;
-
-    /**
-     * Controls the over-scroll mode for this view.
-     * See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
-     * {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS},
-     * and {@link #OVER_SCROLL_NEVER}.
-     */
-    private int mOverScrollMode;
+    /** {@hide} */
+    static final int ACTIVATED                    = 0x40000000;
 
     /**
      * The parent this view is attached to.
@@ -1635,6 +1671,136 @@
     int mViewFlags;
 
     /**
+     * The transform matrix for the View. This transform is calculated internally
+     * based on the rotation, scaleX, and scaleY properties. The identity matrix
+     * is used by default. Do *not* use this variable directly; instead call
+     * getMatrix(), which will automatically recalculate the matrix if necessary
+     * to get the correct matrix based on the latest rotation and scale properties.
+     */
+    private final Matrix mMatrix = new Matrix();
+
+    /**
+     * The transform matrix for the View. This transform is calculated internally
+     * based on the rotation, scaleX, and scaleY properties. The identity matrix
+     * is used by default. Do *not* use this variable directly; instead call
+     * getInverseMatrix(), which will automatically recalculate the matrix if necessary
+     * to get the correct matrix based on the latest rotation and scale properties.
+     */
+    private Matrix mInverseMatrix;
+
+    /**
+     * An internal variable that tracks whether we need to recalculate the
+     * transform matrix, based on whether the rotation or scaleX/Y properties
+     * have changed since the matrix was last calculated.
+     */
+    private boolean mMatrixDirty = false;
+
+    /**
+     * An internal variable that tracks whether we need to recalculate the
+     * transform matrix, based on whether the rotation or scaleX/Y properties
+     * have changed since the matrix was last calculated.
+     */
+    private boolean mInverseMatrixDirty = true;
+
+    /**
+     * A variable that tracks whether we need to recalculate the
+     * transform matrix, based on whether the rotation or scaleX/Y properties
+     * have changed since the matrix was last calculated. This variable
+     * is only valid after a call to updateMatrix() or to a function that
+     * calls it such as getMatrix(), hasIdentityMatrix() and getInverseMatrix().
+     */
+    private boolean mMatrixIsIdentity = true;
+
+    /**
+     * The Camera object is used to compute a 3D matrix when rotationX or rotationY are set.
+     */
+    private Camera mCamera = null;
+
+    /**
+     * This matrix is used when computing the matrix for 3D rotations.
+     */
+    private Matrix matrix3D = null;
+
+    /**
+     * These prev values are used to recalculate a centered pivot point when necessary. The
+     * pivot point is only used in matrix operations (when rotation, scale, or translation are
+     * set), so thes values are only used then as well.
+     */
+    private int mPrevWidth = -1;
+    private int mPrevHeight = -1;
+
+    /**
+     * Convenience value to check for float values that are close enough to zero to be considered
+     * zero.
+     */
+    private static final float NONZERO_EPSILON = .001f;
+
+    /**
+     * The degrees rotation around the vertical axis through the pivot point.
+     */
+    @ViewDebug.ExportedProperty
+    private float mRotationY = 0f;
+
+    /**
+     * The degrees rotation around the horizontal axis through the pivot point.
+     */
+    @ViewDebug.ExportedProperty
+    private float mRotationX = 0f;
+
+    /**
+     * The degrees rotation around the pivot point.
+     */
+    @ViewDebug.ExportedProperty
+    private float mRotation = 0f;
+
+    /**
+     * The amount of translation of the object away from its left property (post-layout).
+     */
+    @ViewDebug.ExportedProperty
+    private float mTranslationX = 0f;
+
+    /**
+     * The amount of translation of the object away from its top property (post-layout).
+     */
+    @ViewDebug.ExportedProperty
+    private float mTranslationY = 0f;
+
+    /**
+     * The amount of scale in the x direction around the pivot point. A
+     * value of 1 means no scaling is applied.
+     */
+    @ViewDebug.ExportedProperty
+    private float mScaleX = 1f;
+
+    /**
+     * The amount of scale in the y direction around the pivot point. A
+     * value of 1 means no scaling is applied.
+     */
+    @ViewDebug.ExportedProperty
+    private float mScaleY = 1f;
+
+    /**
+     * The amount of scale in the x direction around the pivot point. A
+     * value of 1 means no scaling is applied.
+     */
+    @ViewDebug.ExportedProperty
+    private float mPivotX = 0f;
+
+    /**
+     * The amount of scale in the y direction around the pivot point. A
+     * value of 1 means no scaling is applied.
+     */
+    @ViewDebug.ExportedProperty
+    private float mPivotY = 0f;
+
+    /**
+     * The opacity of the View. This is a value from 0 to 1, where 0 means
+     * completely transparent and 1 means completely opaque.
+     */
+    @ViewDebug.ExportedProperty
+    private float mAlpha = 1f;
+
+    /**
      * The distance in pixels from the left edge of this view's parent
      * to the left edge of this view.
      * {@hide}
@@ -1748,6 +1914,11 @@
     protected OnFocusChangeListener mOnFocusChangeListener;
 
     /**
+     * Listeners for layout change events.
+     */
+    private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
+
+    /**
      * Listener used to dispatch click events.
      * This field should be made private, so it is hidden from the SDK.
      * {@hide}
@@ -1772,6 +1943,8 @@
 
     private OnTouchListener mOnTouchListener;
 
+    private OnDragListener mOnDragListener;
+
     /**
      * The application environment this view lives in.
      * This field should be made private, so it is hidden from the SDK.
@@ -1783,8 +1956,9 @@
 
     private int[] mDrawableState = null;
 
-    private SoftReference<Bitmap> mDrawingCache;
-    private SoftReference<Bitmap> mUnscaledDrawingCache;
+    private Bitmap mDrawingCache;
+    private Bitmap mUnscaledDrawingCache;
+    private DisplayList mDisplayList;
 
     /**
      * When this view has focus and the next focus is {@link #FOCUS_LEFT},
@@ -1860,8 +2034,11 @@
      */
     private int mTouchSlop;
 
-    // Used for debug only
-    static long sInstanceCount = 0;
+    /**
+     * Cache drag/drop state
+     *
+     */
+    boolean mCanAcceptDrop;
 
     /**
      * Simple constructor to use when creating a view from code.
@@ -1873,10 +2050,7 @@
         mContext = context;
         mResources = context != null ? context.getResources() : null;
         mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
-        // Used for debug only
-        //++sInstanceCount;
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
-        setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
     }
 
     /**
@@ -1942,7 +2116,6 @@
 
         int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
 
-        int overScrollMode = mOverScrollMode;
         final int N = a.getIndexCount();
         for (int i = 0; i < N; i++) {
             int attr = a.getIndex(i);
@@ -2148,14 +2321,9 @@
                         });
                     }
                     break;
-                case R.styleable.View_overScrollMode:
-                    overScrollMode = a.getInt(attr, OVER_SCROLL_IF_CONTENT_SCROLLS);
-                    break;
             }
         }
 
-        setOverScrollMode(overScrollMode);
-
         if (background != null) {
             setBackgroundDrawable(background);
         }
@@ -2204,15 +2372,6 @@
     View() {
     }
 
-    // Used for debug only
-    /*
-    @Override
-    protected void finalize() throws Throwable {
-        super.finalize();
-        --sInstanceCount;
-    }
-    */
-
     /**
      * <p>
      * Initializes the fading edges from a given set of styled attributes. This
@@ -2419,6 +2578,39 @@
     }
 
     /**
+     * Add a listener that will be called when the bounds of the view change due to
+     * layout processing.
+     *
+     * @param listener The listener that will be called when layout bounds change.
+     */
+    public void addOnLayoutChangeListener(OnLayoutChangeListener listener) {
+        if (mOnLayoutChangeListeners == null) {
+            mOnLayoutChangeListeners = new ArrayList<OnLayoutChangeListener>();
+        }
+        mOnLayoutChangeListeners.add(listener);
+    }
+
+    /**
+     * Remove a listener for layout changes.
+     *
+     * @param listener The listener for layout bounds change.
+     */
+    public void removeOnLayoutChangeListener(OnLayoutChangeListener listener) {
+        if (mOnLayoutChangeListeners == null) {
+            return;
+        }
+        mOnLayoutChangeListeners.remove(listener);
+    }
+
+    /**
+     * Gets the current list of listeners for layout changes.
+     * @return
+     */
+    public List<OnLayoutChangeListener> getOnLayoutChangeListeners() {
+        return mOnLayoutChangeListeners;
+    }
+
+    /**
      * Returns the focus-change callback registered for this view.
      *
      * @return The callback, or null if one is not registered.
@@ -2521,6 +2713,18 @@
     }
 
     /**
+     * Start an action mode.
+     *
+     * @param callback Callback that will control the lifecycle of the action mode
+     * @return The new action mode if it is started, null otherwise
+     *
+     * @see ActionMode
+     */
+    public ActionMode startActionMode(ActionMode.Callback callback) {
+        return getParent().startActionModeForChild(this, callback);
+    }
+
+    /**
      * Register a callback to be invoked when a key is pressed in this view.
      * @param l the key listener to attach to this view
      */
@@ -2537,6 +2741,14 @@
     }
 
     /**
+     * Register a callback to be invoked when a drag event is sent to this view.
+     * @param l The drag listener to attach to this view
+     */
+    public void setOnDragListener(OnDragListener l) {
+        mOnDragListener = l;
+    }
+
+    /**
      * Give this view focus. This will cause {@link #onFocusChanged} to be called.
      *
      * Note: this does not check whether this {@link View} should get focus, it just
@@ -3350,7 +3562,7 @@
     }
 
     /**
-     * Sets the pressed that for this view.
+     * Sets the pressed state for this view.
      *
      * @see #isClickable()
      * @see #setClickable(boolean)
@@ -3457,6 +3669,38 @@
     }
 
     /**
+     * Indicates whether the entire hierarchy under this view will save its
+     * state when a state saving traversal occurs from its parent.  The default
+     * is true; if false, these views will not be saved unless
+     * {@link #saveHierarchyState(SparseArray)} is called directly on this view.
+     *
+     * @return Returns true if the view state saving from parent is enabled, else false.
+     *
+     * @see #setSaveFromParentEnabled(boolean)
+     */
+    public boolean isSaveFromParentEnabled() {
+        return (mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED;
+    }
+
+    /**
+     * Controls whether the entire hierarchy under this view will save its
+     * state when a state saving traversal occurs from its parent.  The default
+     * is true; if false, these views will not be saved unless
+     * {@link #saveHierarchyState(SparseArray)} is called directly on this view.
+     *
+     * @param enabled Set to false to <em>disable</em> state saving, or true
+     * (the default) to allow it.
+     *
+     * @see #isSaveFromParentEnabled()
+     * @see #setId(int)
+     * @see #onSaveInstanceState()
+     */
+    public void setSaveFromParentEnabled(boolean enabled) {
+        setFlags(enabled ? 0 : PARENT_SAVE_DISABLED, PARENT_SAVE_DISABLED_MASK);
+    }
+
+
+    /**
      * Returns whether this View is able to take focus.
      *
      * @return True if this view can take focus, or false otherwise.
@@ -3721,6 +3965,12 @@
         return true;
     }
 
+    /** Gets the ViewRoot, or null if not attached. */
+    /*package*/ ViewRoot getViewRoot() {
+        View root = getRootView();
+        return root != null ? (ViewRoot)root.getParent() : null;
+    }
+
     /**
      * Call this to try to give focus to a specific view or to one of its descendants. This is a
      * special variant of {@link #requestFocus() } that will allow views that are not focuable in
@@ -3734,12 +3984,9 @@
     public final boolean requestFocusFromTouch() {
         // Leave touch mode if we need to
         if (isInTouchMode()) {
-            View root = getRootView();
-            if (root != null) {
-               ViewRoot viewRoot = (ViewRoot)root.getParent();
-               if (viewRoot != null) {
-                   viewRoot.ensureTouchMode(false);
-               }
+            ViewRoot viewRoot = getViewRoot();
+            if (viewRoot != null) {
+                viewRoot.ensureTouchMode(false);
             }
         }
         return requestFocus(View.FOCUS_DOWN);
@@ -3841,12 +4088,13 @@
      */
     public boolean dispatchKeyEvent(KeyEvent event) {
         // If any attached key listener a first crack at the event.
-        //noinspection SimplifiableIfStatement
 
+        //noinspection SimplifiableIfStatement,deprecation
         if (android.util.Config.LOGV) {
             captureViewInfo("captureViewKeyEvent", this);
         }
 
+        //noinspection SimplifiableIfStatement
         if (mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                 && mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
             return true;
@@ -3878,6 +4126,7 @@
             return false;
         }
 
+        //noinspection SimplifiableIfStatement
         if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
                 mOnTouchListener.onTouch(this, event)) {
             return true;
@@ -3894,6 +4143,7 @@
      * @see #getFilterTouchesWhenObscured
      */
     public boolean onFilterTouchEventForSecurity(MotionEvent event) {
+        //noinspection RedundantIfStatement
         if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
                 && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
             // Window is obscured, drop this touch.
@@ -4501,9 +4751,7 @@
                     final int y = (int) event.getY();
 
                     // Be lenient about moving outside of buttons
-                    int slop = mTouchSlop;
-                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||
-                            (y < 0 - slop) || (y >= getHeight() + slop)) {
+                    if (!pointInView(x, y, mTouchSlop)) {
                         // Outside button
                         removeTapCallback();
                         if ((mPrivateFlags & PRESSED) != 0) {
@@ -4668,6 +4916,9 @@
         }
 
         if ((changed & VISIBILITY_MASK) != 0) {
+            if (mParent instanceof ViewGroup) {
+                ((ViewGroup)mParent).onChildVisibilityChanged(this, (flags & VISIBILITY_MASK));
+            }
             dispatchVisibilityChanged(this, (flags & VISIBILITY_MASK));
         }
 
@@ -4738,6 +4989,28 @@
     }
 
     /**
+     * Interface definition for a callback to be invoked when the layout bounds of a view
+     * changes due to layout processing.
+     */
+    public interface OnLayoutChangeListener {
+        /**
+         * Called when the focus state of a view has changed.
+         *
+         * @param v The view whose state has changed.
+         * @param left The new value of the view's left property.
+         * @param top The new value of the view's top property.
+         * @param right The new value of the view's right property.
+         * @param bottom The new value of the view's bottom property.
+         * @param oldLeft The previous value of the view's left property.
+         * @param oldTop The previous value of the view's top property.
+         * @param oldRight The previous value of the view's right property.
+         * @param oldBottom The previous value of the view's bottom property.
+         */
+        void onLayoutChange(View v, int left, int top, int right, int bottom,
+            int oldLeft, int oldTop, int oldRight, int oldBottom);
+    }
+
+    /**
      * This is called during layout when the size of this view has changed. If
      * you were just added to the view hierarchy, you're called with the old
      * values of 0.
@@ -4849,6 +5122,370 @@
     }
 
     /**
+     * The transform matrix of this view, which is calculated based on the current
+     * roation, scale, and pivot properties.
+     *
+     * @see #getRotation()
+     * @see #getScaleX()
+     * @see #getScaleY()
+     * @see #getPivotX()
+     * @see #getPivotY()
+     * @return The current transform matrix for the view
+     */
+    public Matrix getMatrix() {
+        updateMatrix();
+        return mMatrix;
+    }
+
+    /**
+     * Utility function to determine if the value is far enough away from zero to be
+     * considered non-zero.
+     * @param value A floating point value to check for zero-ness
+     * @return whether the passed-in value is far enough away from zero to be considered non-zero
+     */
+    private static boolean nonzero(float value) {
+        return (value < -NONZERO_EPSILON || value > NONZERO_EPSILON);
+    }
+
+    /**
+     * Returns true if the transform matrix is the identity matrix.
+     * Recomputes the matrix if necessary.
+     * 
+     * @return True if the transform matrix is the identity matrix, false otherwise.
+     */
+    final boolean hasIdentityMatrix() {
+        updateMatrix();
+        return mMatrixIsIdentity;
+    }
+
+    /**
+     * Recomputes the transform matrix if necessary.
+     */
+    private void updateMatrix() {
+        if (mMatrixDirty) {
+            // transform-related properties have changed since the last time someone
+            // asked for the matrix; recalculate it with the current values
+
+            // Figure out if we need to update the pivot point
+            if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {
+                if ((mRight - mLeft) != mPrevWidth && (mBottom - mTop) != mPrevHeight) {
+                    mPrevWidth = mRight - mLeft;
+                    mPrevHeight = mBottom - mTop;
+                    mPivotX = (float) mPrevWidth / 2f;
+                    mPivotY = (float) mPrevHeight / 2f;
+                }
+            }
+            mMatrix.reset();
+            if (!nonzero(mRotationX) && !nonzero(mRotationY)) {
+                mMatrix.setTranslate(mTranslationX, mTranslationY);
+                mMatrix.preRotate(mRotation, mPivotX, mPivotY);
+                mMatrix.preScale(mScaleX, mScaleY, mPivotX, mPivotY);
+            } else {
+                if (mCamera == null) {
+                    mCamera = new Camera();
+                    matrix3D = new Matrix();
+                }
+                mCamera.save();
+                mMatrix.preScale(mScaleX, mScaleY, mPivotX, mPivotY);
+                mCamera.rotateX(mRotationX);
+                mCamera.rotateY(mRotationY);
+                mCamera.rotateZ(-mRotation);
+                mCamera.getMatrix(matrix3D);
+                matrix3D.preTranslate(-mPivotX, -mPivotY);
+                matrix3D.postTranslate(mPivotX + mTranslationX, mPivotY + mTranslationY);
+                mMatrix.postConcat(matrix3D);
+                mCamera.restore();
+            }
+            mMatrixDirty = false;
+            mMatrixIsIdentity = mMatrix.isIdentity();
+            mInverseMatrixDirty = true;
+        }
+    }
+
+    /**
+     * Utility method to retrieve the inverse of the current mMatrix property.
+     * We cache the matrix to avoid recalculating it when transform properties
+     * have not changed.
+     *
+     * @return The inverse of the current matrix of this view.
+     */
+    final Matrix getInverseMatrix() {
+        updateMatrix();
+        if (mInverseMatrixDirty) {
+            if (mInverseMatrix == null) {
+                mInverseMatrix = new Matrix();
+            }
+            mMatrix.invert(mInverseMatrix);
+            mInverseMatrixDirty = false;
+        }
+        return mInverseMatrix;
+    }
+
+    /**
+     * The degrees that the view is rotated around the pivot point.
+     *
+     * @see #getPivotX()
+     * @see #getPivotY()
+     * @return The degrees of rotation.
+     */
+    public float getRotation() {
+        return mRotation;
+    }
+
+    /**
+     * Sets the degrees that the view is rotated around the pivot point. Increasing values
+     * result in clockwise rotation.
+     *
+     * @param rotation The degrees of rotation.
+     * @see #getPivotX()
+     * @see #getPivotY()
+     */
+    public void setRotation(float rotation) {
+        if (mRotation != rotation) {
+            // Double-invalidation is necessary to capture view's old and new areas
+            invalidate(false);
+            mRotation = rotation;
+            mMatrixDirty = true;
+            mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+            invalidate(false);
+        }
+    }
+
+    /**
+     * The degrees that the view is rotated around the vertical axis through the pivot point.
+     *
+     * @see #getPivotX()
+     * @see #getPivotY()
+     * @return The degrees of Y rotation.
+     */
+    public float getRotationY() {
+        return mRotationY;
+    }
+
+    /**
+     * Sets the degrees that the view is rotated around the vertical axis through the pivot point.
+     * Increasing values result in counter-clockwise rotation from the viewpoint of looking
+     * down the y axis.
+     *
+     * @param rotationY The degrees of Y rotation.
+     * @see #getPivotX()
+     * @see #getPivotY()
+     */
+    public void setRotationY(float rotationY) {
+        if (mRotationY != rotationY) {
+            // Double-invalidation is necessary to capture view's old and new areas
+            invalidate(false);
+            mRotationY = rotationY;
+            mMatrixDirty = true;
+            mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+            invalidate(false);
+        }
+    }
+
+    /**
+     * The degrees that the view is rotated around the horizontal axis through the pivot point.
+     *
+     * @see #getPivotX()
+     * @see #getPivotY()
+     * @return The degrees of X rotation.
+     */
+    public float getRotationX() {
+        return mRotationX;
+    }
+
+    /**
+     * Sets the degrees that the view is rotated around the horizontal axis through the pivot point.
+     * Increasing values result in clockwise rotation from the viewpoint of looking down the
+     * x axis.
+     *
+     * @param rotationX The degrees of X rotation.
+     * @see #getPivotX()
+     * @see #getPivotY()
+     */
+    public void setRotationX(float rotationX) {
+        if (mRotationX != rotationX) {
+            // Double-invalidation is necessary to capture view's old and new areas
+            invalidate(false);
+            mRotationX = rotationX;
+            mMatrixDirty = true;
+            mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+            invalidate(false);
+        }
+    }
+
+    /**
+     * The amount that the view is scaled in x around the pivot point, as a proportion of
+     * the view's unscaled width. A value of 1, the default, means that no scaling is applied.
+     *
+     * <p>By default, this is 1.0f.
+     *
+     * @see #getPivotX()
+     * @see #getPivotY()
+     * @return The scaling factor.
+     */
+    public float getScaleX() {
+        return mScaleX;
+    }
+
+    /**
+     * Sets the amount that the view is scaled in x around the pivot point, as a proportion of
+     * the view's unscaled width. A value of 1 means that no scaling is applied.
+     *
+     * @param scaleX The scaling factor.
+     * @see #getPivotX()
+     * @see #getPivotY()
+     */
+    public void setScaleX(float scaleX) {
+        if (mScaleX != scaleX) {
+            // Double-invalidation is necessary to capture view's old and new areas
+            invalidate(false);
+            mScaleX = scaleX;
+            mMatrixDirty = true;
+            mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+            invalidate(false);
+        }
+    }
+
+    /**
+     * The amount that the view is scaled in y around the pivot point, as a proportion of
+     * the view's unscaled height. A value of 1, the default, means that no scaling is applied.
+     *
+     * <p>By default, this is 1.0f.
+     *
+     * @see #getPivotX()
+     * @see #getPivotY()
+     * @return The scaling factor.
+     */
+    public float getScaleY() {
+        return mScaleY;
+    }
+
+    /**
+     * Sets the amount that the view is scaled in Y around the pivot point, as a proportion of
+     * the view's unscaled width. A value of 1 means that no scaling is applied.
+     *
+     * @param scaleY The scaling factor.
+     * @see #getPivotX()
+     * @see #getPivotY()
+     */
+    public void setScaleY(float scaleY) {
+        if (mScaleY != scaleY) {
+            // Double-invalidation is necessary to capture view's old and new areas
+            invalidate(false);
+            mScaleY = scaleY;
+            mMatrixDirty = true;
+            mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+            invalidate(false);
+        }
+    }
+
+    /**
+     * The x location of the point around which the view is {@link #setRotation(float) rotated}
+     * and {@link #setScaleX(float) scaled}.
+     *
+     * @see #getRotation()
+     * @see #getScaleX()
+     * @see #getScaleY()
+     * @see #getPivotY()
+     * @return The x location of the pivot point.
+     */
+    public float getPivotX() {
+        return mPivotX;
+    }
+
+    /**
+     * Sets the x location of the point around which the view is
+     * {@link #setRotation(float) rotated} and {@link #setScaleX(float) scaled}.
+     * By default, the pivot point is centered on the object.
+     * Setting this property disables this behavior and causes the view to use only the
+     * explicitly set pivotX and pivotY values.
+     *
+     * @param pivotX The x location of the pivot point.
+     * @see #getRotation()
+     * @see #getScaleX()
+     * @see #getScaleY()
+     * @see #getPivotY()
+     */
+    public void setPivotX(float pivotX) {
+        mPrivateFlags |= PIVOT_EXPLICITLY_SET;
+        if (mPivotX != pivotX) {
+            // Double-invalidation is necessary to capture view's old and new areas
+            invalidate(false);
+            mPivotX = pivotX;
+            mMatrixDirty = true;
+            mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+            invalidate(false);
+        }
+    }
+
+    /**
+     * The y location of the point around which the view is {@link #setRotation(float) rotated}
+     * and {@link #setScaleY(float) scaled}.
+     *
+     * @see #getRotation()
+     * @see #getScaleX()
+     * @see #getScaleY()
+     * @see #getPivotY()
+     * @return The y location of the pivot point.
+     */
+    public float getPivotY() {
+        return mPivotY;
+    }
+
+    /**
+     * Sets the y location of the point around which the view is {@link #setRotation(float) rotated}
+     * and {@link #setScaleY(float) scaled}. By default, the pivot point is centered on the object.
+     * Setting this property disables this behavior and causes the view to use only the
+     * explicitly set pivotX and pivotY values.
+     *
+     * @param pivotY The y location of the pivot point.
+     * @see #getRotation()
+     * @see #getScaleX()
+     * @see #getScaleY()
+     * @see #getPivotY()
+     */
+    public void setPivotY(float pivotY) {
+        mPrivateFlags |= PIVOT_EXPLICITLY_SET;
+        if (mPivotY != pivotY) {
+            // Double-invalidation is necessary to capture view's old and new areas
+            invalidate(false);
+            mPivotY = pivotY;
+            mMatrixDirty = true;
+            mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+            invalidate(false);
+        }
+    }
+
+    /**
+     * The opacity of the view. This is a value from 0 to 1, where 0 means the view is
+     * completely transparent and 1 means the view is completely opaque.
+     *
+     * <p>By default this is 1.0f.
+     * @return The opacity of the view.
+     */
+    public float getAlpha() {
+        return mAlpha;
+    }
+
+    /**
+     * Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is
+     * completely transparent and 1 means the view is completely opaque.
+     *
+     * @param alpha The opacity of the view.
+     */
+    public void setAlpha(float alpha) {
+        mAlpha = alpha;
+        if (onSetAlpha((int) (alpha * 255))) {
+            mPrivateFlags |= ALPHA_SET;
+            // subclass is handling alpha - don't optimize rendering cache invalidation
+            invalidate();
+        } else {
+            mPrivateFlags &= ~ALPHA_SET;
+            invalidate(false);
+        }
+    }
+
+    /**
      * Top position of this view relative to its parent.
      *
      * @return The top of this view, in pixels.
@@ -4859,6 +5496,51 @@
     }
 
     /**
+     * Sets the top position of this view relative to its parent. This method is meant to be called
+     * by the layout system and should not generally be called otherwise, because the property
+     * may be changed at any time by the layout.
+     *
+     * @param top The top of this view, in pixels.
+     */
+    public final void setTop(int top) {
+        if (top != mTop) {
+            updateMatrix();
+            if (mMatrixIsIdentity) {
+                final ViewParent p = mParent;
+                if (p != null && mAttachInfo != null) {
+                    final Rect r = mAttachInfo.mTmpInvalRect;
+                    int minTop;
+                    int yLoc;
+                    if (top < mTop) {
+                        minTop = top;
+                        yLoc = top - mTop;
+                    } else {
+                        minTop = mTop;
+                        yLoc = 0;
+                    }
+                    r.set(0, yLoc, mRight - mLeft, mBottom - minTop);
+                    p.invalidateChild(this, r);
+                }
+            } else {
+                // Double-invalidation is necessary to capture view's old and new areas
+                invalidate();
+            }
+
+            int width = mRight - mLeft;
+            int oldHeight = mBottom - mTop;
+
+            mTop = top;
+
+            onSizeChanged(width, mBottom - mTop, width, oldHeight);
+
+            if (!mMatrixIsIdentity) {
+                mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+                invalidate();
+            }
+        }
+    }
+
+    /**
      * Bottom position of this view relative to its parent.
      *
      * @return The bottom of this view, in pixels.
@@ -4869,6 +5551,48 @@
     }
 
     /**
+     * Sets the bottom position of this view relative to its parent. This method is meant to be
+     * called by the layout system and should not generally be called otherwise, because the
+     * property may be changed at any time by the layout.
+     *
+     * @param bottom The bottom of this view, in pixels.
+     */
+    public final void setBottom(int bottom) {
+        if (bottom != mBottom) {
+            updateMatrix();
+            if (mMatrixIsIdentity) {
+                final ViewParent p = mParent;
+                if (p != null && mAttachInfo != null) {
+                    final Rect r = mAttachInfo.mTmpInvalRect;
+                    int maxBottom;
+                    if (bottom < mBottom) {
+                        maxBottom = mBottom;
+                    } else {
+                        maxBottom = bottom;
+                    }
+                    r.set(0, 0, mRight - mLeft, maxBottom - mTop);
+                    p.invalidateChild(this, r);
+                }
+            } else {
+                // Double-invalidation is necessary to capture view's old and new areas
+                invalidate();
+            }
+
+            int width = mRight - mLeft;
+            int oldHeight = mBottom - mTop;
+
+            mBottom = bottom;
+
+            onSizeChanged(width, mBottom - mTop, width, oldHeight);
+
+            if (!mMatrixIsIdentity) {
+                mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+                invalidate();
+            }
+        }
+    }
+
+    /**
      * Left position of this view relative to its parent.
      *
      * @return The left edge of this view, in pixels.
@@ -4879,6 +5603,52 @@
     }
 
     /**
+     * Sets the left position of this view relative to its parent. This method is meant to be called
+     * by the layout system and should not generally be called otherwise, because the property
+     * may be changed at any time by the layout.
+     *
+     * @param left The bottom of this view, in pixels.
+     */
+    public final void setLeft(int left) {
+        if (left != mLeft) {
+            updateMatrix();
+            if (mMatrixIsIdentity) {
+                final ViewParent p = mParent;
+                if (p != null && mAttachInfo != null) {
+                    final Rect r = mAttachInfo.mTmpInvalRect;
+                    int minLeft;
+                    int xLoc;
+                    if (left < mLeft) {
+                        minLeft = left;
+                        xLoc = left - mLeft;
+                    } else {
+                        minLeft = mLeft;
+                        xLoc = 0;
+                    }
+                    r.set(xLoc, 0, mRight - minLeft, mBottom - mTop);
+                    p.invalidateChild(this, r);
+                }
+            } else {
+                // Double-invalidation is necessary to capture view's old and new areas
+                invalidate();
+            }
+
+            int oldWidth = mRight - mLeft;
+            int height = mBottom - mTop;
+
+            mLeft = left;
+
+            onSizeChanged(mRight - mLeft, height, oldWidth, height);
+
+            if (!mMatrixIsIdentity) {
+                mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+                invalidate();
+            }
+
+        }
+    }
+
+    /**
      * Right position of this view relative to its parent.
      *
      * @return The right edge of this view, in pixels.
@@ -4889,12 +5659,188 @@
     }
 
     /**
+     * Sets the right position of this view relative to its parent. This method is meant to be called
+     * by the layout system and should not generally be called otherwise, because the property
+     * may be changed at any time by the layout.
+     *
+     * @param right The bottom of this view, in pixels.
+     */
+    public final void setRight(int right) {
+        if (right != mRight) {
+            updateMatrix();
+            if (mMatrixIsIdentity) {
+                final ViewParent p = mParent;
+                if (p != null && mAttachInfo != null) {
+                    final Rect r = mAttachInfo.mTmpInvalRect;
+                    int maxRight;
+                    if (right < mRight) {
+                        maxRight = mRight;
+                    } else {
+                        maxRight = right;
+                    }
+                    r.set(0, 0, maxRight - mLeft, mBottom - mTop);
+                    p.invalidateChild(this, r);
+                }
+            } else {
+                // Double-invalidation is necessary to capture view's old and new areas
+                invalidate();
+            }
+
+            int oldWidth = mRight - mLeft;
+            int height = mBottom - mTop;
+
+            mRight = right;
+
+            onSizeChanged(mRight - mLeft, height, oldWidth, height);
+
+            if (!mMatrixIsIdentity) {
+                mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+                invalidate();
+            }
+        }
+    }
+
+    /**
+     * The visual x position of this view, in pixels. This is equivalent to the
+     * {@link #setTranslationX(float) translationX} property plus the current
+     * {@link #getLeft() left} property. 
+     *
+     * @return The visual x position of this view, in pixels.
+     */
+    public float getX() {
+        return mLeft + mTranslationX;
+    }
+
+    /**
+     * Sets the visual x position of this view, in pixels. This is equivalent to setting the
+     * {@link #setTranslationX(float) translationX} property to be the difference between
+     * the x value passed in and the current {@link #getLeft() left} property.
+     *
+     * @param x The visual x position of this view, in pixels.
+     */
+    public void setX(float x) {
+        setTranslationX(x - mLeft);
+    }
+
+    /**
+     * The visual y position of this view, in pixels. This is equivalent to the
+     * {@link #setTranslationY(float) translationY} property plus the current
+     * {@link #getTop() top} property.
+     *
+     * @return The visual y position of this view, in pixels.
+     */
+    public float getY() {
+        return mTop + mTranslationY;
+    }
+
+    /**
+     * Sets the visual y position of this view, in pixels. This is equivalent to setting the
+     * {@link #setTranslationY(float) translationY} property to be the difference between
+     * the y value passed in and the current {@link #getTop() top} property.
+     *
+     * @param y The visual y position of this view, in pixels.
+     */
+    public void setY(float y) {
+        setTranslationY(y - mTop);
+    }
+
+
+    /**
+     * The horizontal location of this view relative to its {@link #getLeft() left} position.
+     * This position is post-layout, in addition to wherever the object's
+     * layout placed it.
+     *
+     * @return The horizontal position of this view relative to its left position, in pixels.
+     */
+    public float getTranslationX() {
+        return mTranslationX;
+    }
+
+    /**
+     * Sets the horizontal location of this view relative to its {@link #getLeft() left} position.
+     * This effectively positions the object post-layout, in addition to wherever the object's
+     * layout placed it.
+     *
+     * @param translationX The horizontal position of this view relative to its left position,
+     * in pixels.
+     */
+    public void setTranslationX(float translationX) {
+        if (mTranslationX != translationX) {
+            // Double-invalidation is necessary to capture view's old and new areas
+            invalidate(false);
+            mTranslationX = translationX;
+            mMatrixDirty = true;
+            mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+            invalidate(false);
+        }
+    }
+
+    /**
+     * The horizontal location of this view relative to its {@link #getTop() top} position.
+     * This position is post-layout, in addition to wherever the object's
+     * layout placed it.
+     *
+     * @return The vertical position of this view relative to its top position,
+     * in pixels.
+     */
+    public float getTranslationY() {
+        return mTranslationY;
+    }
+
+    /**
+     * Sets the vertical location of this view relative to its {@link #getTop() top} position.
+     * This effectively positions the object post-layout, in addition to wherever the object's
+     * layout placed it.
+     *
+     * @param translationY The vertical position of this view relative to its top position,
+     * in pixels.
+     */
+    public void setTranslationY(float translationY) {
+        if (mTranslationY != translationY) {
+            // Double-invalidation is necessary to capture view's old and new areas
+            invalidate(false);
+            mTranslationY = translationY;
+            mMatrixDirty = true;
+            mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+            invalidate(false);
+        }
+    }
+
+    /**
      * Hit rectangle in parent's coordinates
      *
      * @param outRect The hit rectangle of the view.
      */
     public void getHitRect(Rect outRect) {
-        outRect.set(mLeft, mTop, mRight, mBottom);
+        updateMatrix();
+        if (mMatrixIsIdentity || mAttachInfo == null) {
+            outRect.set(mLeft, mTop, mRight, mBottom);
+        } else {
+            final RectF tmpRect = mAttachInfo.mTmpTransformRect;
+            tmpRect.set(-mPivotX, -mPivotY, getWidth() - mPivotX, getHeight() - mPivotY);
+            mMatrix.mapRect(tmpRect);
+            outRect.set((int) tmpRect.left + mLeft, (int) tmpRect.top + mTop,
+                    (int) tmpRect.right + mLeft, (int) tmpRect.bottom + mTop);
+        }
+    }
+
+    /**
+     * Determines whether the given point, in local coordinates is inside the view.
+     */
+    /*package*/ final boolean pointInView(float localX, float localY) {
+        return localX >= 0 && localX < (mRight - mLeft)
+                && localY >= 0 && localY < (mBottom - mTop);
+    }
+
+    /**
+     * Utility method to determine whether the given point, in local coordinates,
+     * is inside the view, where the area of the view is expanded by the slop factor.
+     * This method is called while processing touch-move events to determine if the event
+     * is still within the view.
+     */
+    private boolean pointInView(float localX, float localY, float slop) {
+        return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
+                localY < ((mBottom - mTop) + slop);
     }
 
     /**
@@ -4957,8 +5903,39 @@
      * @param offset the number of pixels to offset the view by
      */
     public void offsetTopAndBottom(int offset) {
-        mTop += offset;
-        mBottom += offset;
+        if (offset != 0) {
+            updateMatrix();
+            if (mMatrixIsIdentity) {
+                final ViewParent p = mParent;
+                if (p != null && mAttachInfo != null) {
+                    final Rect r = mAttachInfo.mTmpInvalRect;
+                    int minTop;
+                    int maxBottom;
+                    int yLoc;
+                    if (offset < 0) {
+                        minTop = mTop + offset;
+                        maxBottom = mBottom;
+                        yLoc = offset;
+                    } else {
+                        minTop = mTop;
+                        maxBottom = mBottom + offset;
+                        yLoc = 0;
+                    }
+                    r.set(0, yLoc, mRight - mLeft, maxBottom - minTop);
+                    p.invalidateChild(this, r);
+                }
+            } else {
+                invalidate(false);
+            }
+
+            mTop += offset;
+            mBottom += offset;
+
+            if (!mMatrixIsIdentity) {
+                mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+                invalidate(false);
+            }
+        }
     }
 
     /**
@@ -4967,8 +5944,36 @@
      * @param offset the numer of pixels to offset the view by
      */
     public void offsetLeftAndRight(int offset) {
-        mLeft += offset;
-        mRight += offset;
+        if (offset != 0) {
+            updateMatrix();
+            if (mMatrixIsIdentity) {
+                final ViewParent p = mParent;
+                if (p != null && mAttachInfo != null) {
+                    final Rect r = mAttachInfo.mTmpInvalRect;
+                    int minLeft;
+                    int maxRight;
+                    if (offset < 0) {
+                        minLeft = mLeft + offset;
+                        maxRight = mRight;
+                    } else {
+                        minLeft = mLeft;
+                        maxRight = mRight + offset;
+                    }
+                    r.set(0, 0, maxRight - minLeft, mBottom - mTop);
+                    p.invalidateChild(this, r);
+                }
+            } else {
+                invalidate(false);
+            }
+
+            mLeft += offset;
+            mRight += offset;
+
+            if (!mMatrixIsIdentity) {
+                mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+                invalidate(false);
+            }
+        }
     }
 
     /**
@@ -5213,7 +6218,8 @@
             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
         }
 
-        if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) {
+        if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||
+                (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
             mPrivateFlags &= ~DRAWING_CACHE_VALID;
             final ViewParent p = mParent;
             final AttachInfo ai = mAttachInfo;
@@ -5244,7 +6250,8 @@
             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
         }
 
-        if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) {
+        if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||
+                (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) {
             mPrivateFlags &= ~DRAWING_CACHE_VALID;
             final ViewParent p = mParent;
             final AttachInfo ai = mAttachInfo;
@@ -5264,14 +6271,39 @@
      * UI thread. To call from a non-UI thread, call {@link #postInvalidate()}.
      */
     public void invalidate() {
+        invalidate(true);
+    }
+
+    /**
+     * This is where the invalidate() work actually happens. A full invalidate()
+     * causes the drawing cache to be invalidated, but this function can be called with
+     * invalidateCache set to false to skip that invalidation step for cases that do not
+     * need it (for example, a component that remains at the same dimensions with the same
+     * content).
+     *
+     * @param invalidateCache Whether the drawing cache for this view should be invalidated as
+     * well. This is usually true for a full invalidate, but may be set to false if the
+     * View's contents or dimensions have not changed.
+     */
+    private void invalidate(boolean invalidateCache) {
         if (ViewDebug.TRACE_HIERARCHY) {
             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
         }
 
-        if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) {
-            mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
-            final ViewParent p = mParent;
+        if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||
+                (invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID)) {
+            mPrivateFlags &= ~DRAWN;
+            if (invalidateCache) {
+                mPrivateFlags &= ~DRAWING_CACHE_VALID;
+            }
             final AttachInfo ai = mAttachInfo;
+            final ViewParent p = mParent;
+            if (p != null && ai != null && ai.mHardwareAccelerated) {
+                // fast-track for GL-enabled applications; just invalidate the whole hierarchy
+                // with a null dirty rect, which tells the ViewRoot to redraw everything
+                p.invalidateChild(this, null);
+                return;
+            }
             if (p != null && ai != null) {
                 final Rect r = ai.mTmpInvalRect;
                 r.set(0, 0, mRight - mLeft, mBottom - mTop);
@@ -5295,7 +6327,8 @@
      */
     @ViewDebug.ExportedProperty(category = "drawing")
     public boolean isOpaque() {
-        return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK;
+        return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK &&
+                (mAlpha >= 1.0f - ViewConfiguration.ALPHA_THRESHOLD);
     }
 
     private void computeOpaqueFlags() {
@@ -6007,9 +7040,8 @@
      * @see android.widget.ScrollBarDrawable
      * @hide
      */
-    protected void onDrawHorizontalScrollBar(Canvas canvas,
-                                             Drawable scrollBar,
-                                             int l, int t, int r, int b) {
+    protected void onDrawHorizontalScrollBar(Canvas canvas, Drawable scrollBar,
+            int l, int t, int r, int b) {
         scrollBar.setBounds(l, t, r, b);
         scrollBar.draw(canvas);
     }
@@ -6028,9 +7060,8 @@
      * @see android.widget.ScrollBarDrawable
      * @hide
      */
-    protected void onDrawVerticalScrollBar(Canvas canvas,
-                                           Drawable scrollBar,
-                                           int l, int t, int r, int b) {
+    protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
+            int l, int t, int r, int b) {
         scrollBar.setBounds(l, t, r, b);
         scrollBar.draw(canvas);
     }
@@ -6384,6 +7415,56 @@
     }
 
     /**
+     * <p>Returns a display list that can be used to draw this view again
+     * without executing its draw method.</p>
+     * 
+     * @return A DisplayList ready to replay, or null if caching is not enabled.
+     */
+    DisplayList getDisplayList() {
+        if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) {
+            return null;
+        }
+        if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
+            return null;
+        }
+
+        if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED &&
+                ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDisplayList == null)) {
+
+            mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList();
+
+            final HardwareCanvas canvas = mDisplayList.start();
+            try {
+                int width = mRight - mLeft;
+                int height = mBottom - mTop;
+
+                canvas.setViewport(width, height);
+                canvas.onPreDraw();
+
+                final int restoreCount = canvas.save();
+
+                mPrivateFlags |= DRAWN | DRAWING_CACHE_VALID;
+    
+                // Fast path for layouts with no backgrounds
+                if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
+                    mPrivateFlags &= ~DIRTY_MASK;
+                    dispatchDraw(canvas);
+                } else {
+                    draw(canvas);
+                }
+    
+                canvas.restoreToCount(restoreCount);
+            } finally {
+                canvas.onPostDraw();
+
+                mDisplayList.end();
+            }
+        }
+
+        return mDisplayList;
+    }
+
+    /**
      * <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p>
      * 
      * @return A non-scaled bitmap representing this view or null if cache is disabled.
@@ -6428,8 +7509,7 @@
         if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) {
             buildDrawingCache(autoScale);
         }
-        return autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) :
-                (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get());
+        return autoScale ? mDrawingCache : mUnscaledDrawingCache;
     }
 
     /**
@@ -6444,15 +7524,16 @@
      */
     public void destroyDrawingCache() {
         if (mDrawingCache != null) {
-            final Bitmap bitmap = mDrawingCache.get();
-            if (bitmap != null) bitmap.recycle();
+            mDrawingCache.recycle();
             mDrawingCache = null;
         }
         if (mUnscaledDrawingCache != null) {
-            final Bitmap bitmap = mUnscaledDrawingCache.get();
-            if (bitmap != null) bitmap.recycle();
+            mUnscaledDrawingCache.recycle();
             mUnscaledDrawingCache = null;
         }
+        if (mDisplayList != null) {
+            mDisplayList = null;
+        }
     }
 
     /**
@@ -6505,21 +7586,22 @@
      * the auto scaling to true. Doing so, however, will generate a bitmap of a different
      * size than the view. This implies that your application must be able to handle this
      * size.</p>
+     * 
+     * <p>You should avoid calling this method when hardware acceleration is enabled. If
+     * you do not need the drawing cache bitmap, calling this method will increase memory
+     * usage and cause the view to be rendered in software once, thus negatively impacting 
+     * performance.</p>
      *
      * @see #getDrawingCache()
      * @see #destroyDrawingCache()
      */
     public void buildDrawingCache(boolean autoScale) {
         if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ?
-                (mDrawingCache == null || mDrawingCache.get() == null) :
-                (mUnscaledDrawingCache == null || mUnscaledDrawingCache.get() == null))) {
+                mDrawingCache == null : mUnscaledDrawingCache == null)) {
 
             if (ViewDebug.TRACE_HIERARCHY) {
                 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
             }
-            if (Config.DEBUG && ViewDebug.profileDrawing) {
-                EventLog.writeEvent(60002, hashCode());
-            }
 
             int width = mRight - mLeft;
             int height = mBottom - mTop;
@@ -6545,8 +7627,7 @@
             }
 
             boolean clear = true;
-            Bitmap bitmap = autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) :
-                    (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get());
+            Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
 
             if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
                 Bitmap.Config quality;
@@ -6578,9 +7659,9 @@
                     bitmap = Bitmap.createBitmap(width, height, quality);
                     bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
                     if (autoScale) {
-                        mDrawingCache = new SoftReference<Bitmap>(bitmap);
+                        mDrawingCache = bitmap;
                     } else {
-                        mUnscaledDrawingCache = new SoftReference<Bitmap>(bitmap);
+                        mUnscaledDrawingCache = bitmap;
                     }
                     if (opaque && translucentWindow) bitmap.setHasAlpha(false);
                 } catch (OutOfMemoryError e) {
@@ -6630,7 +7711,9 @@
             canvas.translate(-mScrollX, -mScrollY);
 
             mPrivateFlags |= DRAWN;
-            mPrivateFlags |= DRAWING_CACHE_VALID;
+            if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated) {
+                mPrivateFlags |= DRAWING_CACHE_VALID;
+            }
 
             // Fast path for layouts with no backgrounds
             if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
@@ -6819,6 +7902,25 @@
     }
 
     /**
+     * <p>Indicates whether this view is attached to an hardware accelerated
+     * window or not.</p>
+     * 
+     * <p>Even if this method returns true, it does not mean that every call
+     * to {@link #draw(android.graphics.Canvas)} will be made with an hardware
+     * accelerated {@link android.graphics.Canvas}. For instance, if this view
+     * is drawn onto an offscren {@link android.graphics.Bitmap} and its
+     * window is hardware accelerated,
+     * {@link android.graphics.Canvas#isHardwareAccelerated()} will likely
+     * return false, and this method will return true.</p>
+     * 
+     * @return True if the view is attached to a window and the window is
+     *         hardware accelerated; false in any other case.
+     */
+    public boolean isHardwareAccelerated() {
+        return mAttachInfo != null && mAttachInfo.mHardwareAccelerated;
+    }
+    
+    /**
      * Manually render this view (and all of its children) to the given Canvas.
      * The view must have already done a full layout before this function is
      * called.  When implementing a view, do not override this method; instead,
@@ -6943,16 +8045,16 @@
 
         if (verticalEdges) {
             topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
-            drawTop = topFadeStrength >= 0.0f;
+            drawTop = topFadeStrength > 0.0f;
             bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
-            drawBottom = bottomFadeStrength >= 0.0f;
+            drawBottom = bottomFadeStrength > 0.0f;
         }
 
         if (horizontalEdges) {
             leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
-            drawLeft = leftFadeStrength >= 0.0f;
+            drawLeft = leftFadeStrength > 0.0f;
             rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
-            drawRight = rightFadeStrength >= 0.0f;
+            drawRight = rightFadeStrength > 0.0f;
         }
 
         saveCount = canvas.getSaveCount();
@@ -7159,14 +8261,19 @@
      *
      * Derived classes with children should override
      * onLayout. In that method, they should
-     * call layout on each of their their children.
+     * call layout on each of their children.
      *
      * @param l Left position, relative to parent
      * @param t Top position, relative to parent
      * @param r Right position, relative to parent
      * @param b Bottom position, relative to parent
      */
+    @SuppressWarnings({"unchecked"})
     public final void layout(int l, int t, int r, int b) {
+        int oldL = mLeft;
+        int oldT = mTop;
+        int oldB = mBottom;
+        int oldR = mRight;
         boolean changed = setFrame(l, t, r, b);
         if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
             if (ViewDebug.TRACE_HIERARCHY) {
@@ -7175,6 +8282,15 @@
 
             onLayout(changed, l, t, r, b);
             mPrivateFlags &= ~LAYOUT_REQUIRED;
+
+            if (mOnLayoutChangeListeners != null) {
+                ArrayList<OnLayoutChangeListener> listenersCopy =
+                        (ArrayList<OnLayoutChangeListener>) mOnLayoutChangeListeners.clone();
+                int numListeners = listenersCopy.size();
+                for (int i = 0; i < numListeners; ++i) {
+                    listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
+                }
+            }
         }
         mPrivateFlags &= ~FORCE_LAYOUT;
     }
@@ -7185,7 +8301,7 @@
      *
      * Derived classes with children should override
      * this method and call layout on each of
-     * their their children.
+     * their children.
      * @param changed This is a new size or position for this view
      * @param left Left position, relative to parent
      * @param top Top position, relative to parent
@@ -7439,18 +8555,13 @@
 
         int privateFlags = mPrivateFlags;
 
-        int viewStateIndex = (((privateFlags & PRESSED) != 0) ? 1 : 0);
-
-        viewStateIndex = (viewStateIndex << 1)
-                + (((mViewFlags & ENABLED_MASK) == ENABLED) ? 1 : 0);
-
-        viewStateIndex = (viewStateIndex << 1) + (isFocused() ? 1 : 0);
-
-        viewStateIndex = (viewStateIndex << 1)
-                + (((privateFlags & SELECTED) != 0) ? 1 : 0);
-
-        final boolean hasWindowFocus = hasWindowFocus();
-        viewStateIndex = (viewStateIndex << 1) + (hasWindowFocus ? 1 : 0);
+        int viewStateIndex = 0;
+        if ((privateFlags & PRESSED) != 0) viewStateIndex |= VIEW_STATE_PRESSED;
+        if ((mViewFlags & ENABLED_MASK) == ENABLED) viewStateIndex |= VIEW_STATE_ENABLED;
+        if (isFocused()) viewStateIndex |= VIEW_STATE_FOCUSED;
+        if ((privateFlags & SELECTED) != 0) viewStateIndex |= VIEW_STATE_SELECTED;
+        if (hasWindowFocus()) viewStateIndex |= VIEW_STATE_WINDOW_FOCUSED;
+        if ((privateFlags & ACTIVATED) != 0) viewStateIndex |= VIEW_STATE_ACTIVATED;
 
         drawableState = VIEW_STATE_SETS[viewStateIndex];
 
@@ -7462,7 +8573,7 @@
                     + " en=" + ((mViewFlags & ENABLED_MASK) == ENABLED)
                     + " fo=" + hasFocus()
                     + " sl=" + ((privateFlags & SELECTED) != 0)
-                    + " wf=" + hasWindowFocus
+                    + " wf=" + hasWindowFocus()
                     + ": " + Arrays.toString(drawableState));
         }
 
@@ -7509,12 +8620,27 @@
     }
 
     /**
+     * Call {@link Drawable#jumpToCurrentState() Drawable.jumpToCurrentState()}
+     * on all Drawable objects associated with this view.
+     */
+    public void jumpDrawablesToCurrentState() {
+        if (mBGDrawable != null) {
+            mBGDrawable.jumpToCurrentState();
+        }
+    }
+
+    /**
      * Sets the background color for this view.
      * @param color the color of the background
      */
     @RemotableViewMethod
     public void setBackgroundColor(int color) {
-        setBackgroundDrawable(new ColorDrawable(color));
+        if (mBGDrawable instanceof ColorDrawable) {
+            ((ColorDrawable) mBGDrawable).setColor(color);
+            invalidate();
+        } else {
+            setBackgroundDrawable(new ColorDrawable(color));
+        }
     }
 
     /**
@@ -7776,6 +8902,48 @@
     }
 
     /**
+     * Changes the activated state of this view. A view can be activated or not.
+     * Note that activation is not the same as selection.  Selection is
+     * a transient property, representing the view (hierarchy) the user is
+     * currently interacting with.  Activation is a longer-term state that the
+     * user can move views in and out of.  For example, in a list view with
+     * single or multiple selection enabled, the views in the current selection
+     * set are activated.  (Um, yeah, we are deeply sorry about the terminology
+     * here.)  The activated state is propagated down to children of the view it
+     * is set on.
+     *
+     * @param activated true if the view must be activated, false otherwise
+     */
+    public void setActivated(boolean activated) {
+        if (((mPrivateFlags & ACTIVATED) != 0) != activated) {
+            mPrivateFlags = (mPrivateFlags & ~ACTIVATED) | (activated ? ACTIVATED : 0);
+            invalidate();
+            refreshDrawableState();
+            dispatchSetActivated(activated);
+        }
+    }
+
+    /**
+     * Dispatch setActivated to all of this View's children.
+     *
+     * @see #setActivated(boolean)
+     *
+     * @param activated The new activated state
+     */
+    protected void dispatchSetActivated(boolean activated) {
+    }
+
+    /**
+     * Indicates the activation state of this view.
+     *
+     * @return true if the view is activated, false otherwise
+     */
+    @ViewDebug.ExportedProperty
+    public boolean isActivated() {
+        return (mPrivateFlags & ACTIVATED) != 0;
+    }
+
+    /**
      * Returns the ViewTreeObserver for this view's hierarchy. The view tree
      * observer can be used to get notifications when global events, like
      * layout, happen.
@@ -8691,13 +9859,180 @@
         if (mAttachInfo == null) {
             return false;
         }
-        if ((flags&HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING) == 0
+        //noinspection SimplifiableIfStatement
+        if ((flags & HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING) == 0
                 && !isHapticFeedbackEnabled()) {
             return false;
         }
-        return mAttachInfo.mRootCallbacks.performHapticFeedback(
-                feedbackConstant,
-                (flags&HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0);
+        return mAttachInfo.mRootCallbacks.performHapticFeedback(feedbackConstant,
+                (flags & HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0);
+    }
+
+    /**
+     * !!! TODO: real docs
+     *
+     * The base class implementation makes the thumbnail the same size and appearance
+     * as the view itself, and positions it with its center at the touch point.
+     */
+    public static class DragThumbnailBuilder {
+        private final WeakReference<View> mView;
+
+        /**
+         * Construct a thumbnail builder object for use with the given view.
+         * @param view
+         */
+        public DragThumbnailBuilder(View view) {
+            mView = new WeakReference<View>(view);
+        }
+
+        final public View getView() {
+            return mView.get();
+        }
+
+        /**
+         * Provide the draggable-thumbnail metrics for the operation: the dimensions of
+         * the thumbnail image itself, and the point within that thumbnail that should
+         * be centered under the touch location while dragging.
+         * <p>
+         * The default implementation sets the dimensions of the thumbnail to be the
+         * same as the dimensions of the View itself and centers the thumbnail under
+         * the touch point.
+         *
+         * @param thumbnailSize The application should set the {@code x} member of this
+         *        parameter to the desired thumbnail width, and the {@code y} member to
+         *        the desired height.
+         * @param thumbnailTouchPoint The application should set this point to be the
+         *        location within the thumbnail that should track directly underneath
+         *        the touch point on the screen during a drag.
+         */
+        public void onProvideThumbnailMetrics(Point thumbnailSize, Point thumbnailTouchPoint) {
+            final View view = mView.get();
+            if (view != null) {
+                thumbnailSize.set(view.getWidth(), view.getHeight());
+                thumbnailTouchPoint.set(thumbnailSize.x / 2, thumbnailSize.y / 2);
+            } else {
+                Log.e(View.VIEW_LOG_TAG, "Asked for drag thumb metrics but no view");
+            }
+        }
+
+        /**
+         * Draw the thumbnail image for the upcoming drag.  The thumbnail canvas was
+         * created with the dimensions supplied by the onProvideThumbnailMetrics()
+         * callback.
+         *
+         * @param canvas
+         */
+        public void onDrawThumbnail(Canvas canvas) {
+            final View view = mView.get();
+            if (view != null) {
+                view.draw(canvas);
+            } else {
+                Log.e(View.VIEW_LOG_TAG, "Asked to draw drag thumb but no view");
+            }
+        }
+    }
+
+    /**
+     * Drag and drop.  App calls startDrag(), then callbacks to the thumbnail builder's
+     * onProvideThumbnailMetrics() and onDrawThumbnail() methods happen, then the drag
+     * operation is handed over to the OS.
+     * !!! TODO: real docs
+     */
+    public final boolean startDrag(ClipData data, DragThumbnailBuilder thumbBuilder,
+            boolean myWindowOnly) {
+        if (ViewDebug.DEBUG_DRAG) {
+            Log.d(VIEW_LOG_TAG, "startDrag: data=" + data + " local=" + myWindowOnly);
+        }
+        boolean okay = false;
+
+        Point thumbSize = new Point();
+        Point thumbTouchPoint = new Point();
+        thumbBuilder.onProvideThumbnailMetrics(thumbSize, thumbTouchPoint);
+
+        if ((thumbSize.x < 0) || (thumbSize.y < 0) ||
+                (thumbTouchPoint.x < 0) || (thumbTouchPoint.y < 0)) {
+            throw new IllegalStateException("Drag thumb dimensions must not be negative");
+        }
+
+        if (ViewDebug.DEBUG_DRAG) {
+            Log.d(VIEW_LOG_TAG, "drag thumb: width=" + thumbSize.x + " height=" + thumbSize.y
+                    + " thumbX=" + thumbTouchPoint.x + " thumbY=" + thumbTouchPoint.y);
+        }
+        Surface surface = new Surface();
+        try {
+            IBinder token = mAttachInfo.mSession.prepareDrag(mAttachInfo.mWindow,
+                    myWindowOnly, thumbSize.x, thumbSize.y, surface);
+            if (ViewDebug.DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "prepareDrag returned token=" + token
+                    + " surface=" + surface);
+            if (token != null) {
+                Canvas canvas = surface.lockCanvas(null);
+                try {
+                    canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+                    thumbBuilder.onDrawThumbnail(canvas);
+                } finally {
+                    surface.unlockCanvasAndPost(canvas);
+                }
+
+                // repurpose 'thumbSize' for the last touch point
+                getViewRoot().getLastTouchPoint(thumbSize);
+
+                okay = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, token,
+                        (float) thumbSize.x, (float) thumbSize.y,
+                        (float) thumbTouchPoint.x, (float) thumbTouchPoint.y, data);
+                if (ViewDebug.DEBUG_DRAG) Log.d(VIEW_LOG_TAG, "performDrag returned " + okay);
+            }
+        } catch (Exception e) {
+            Log.e(VIEW_LOG_TAG, "Unable to initiate drag", e);
+            surface.destroy();
+        }
+
+        return okay;
+    }
+
+    /**
+     * Drag-and-drop event dispatch.  The event.getAction() verb is one of the DragEvent
+     * constants DRAG_STARTED_EVENT, DRAG_EVENT, DROP_EVENT, and DRAG_ENDED_EVENT.
+     *
+     * For DRAG_STARTED_EVENT, event.getClipDescription() describes the content
+     * being dragged.  onDragEvent() should return 'true' if the view can handle
+     * a drop of that content.  A view that returns 'false' here will receive no
+     * further calls to onDragEvent() about the drag/drop operation.
+     *
+     * For DRAG_ENTERED, event.getClipDescription() describes the content being
+     * dragged.  This will be the same content description passed in the
+     * DRAG_STARTED_EVENT invocation.
+     *
+     * For DRAG_EXITED, event.getClipDescription() describes the content being
+     * dragged.  This will be the same content description passed in the
+     * DRAG_STARTED_EVENT invocation.  The view should return to its approriate
+     * drag-acceptance visual state.
+     *
+     * For DRAG_LOCATION_EVENT, event.getX() and event.getY() give the location in View
+     * coordinates of the current drag point.  The view must return 'true' if it
+     * can accept a drop of the current drag content, false otherwise.
+     *
+     * For DROP_EVENT, event.getX() and event.getY() give the location of the drop
+     * within the view; also, event.getClipData() returns the full data payload
+     * being dropped.  The view should return 'true' if it consumed the dropped
+     * content, 'false' if it did not.
+     *
+     * For DRAG_ENDED_EVENT, the 'event' argument may be null.  The view should return
+     * to its normal visual state.
+     */
+    public boolean onDragEvent(DragEvent event) {
+        return false;
+    }
+
+    /**
+     * Views typically don't need to override dispatchDragEvent(); it just calls
+     * onDragEvent(event) and passes the result up appropriately.
+     */
+    public boolean dispatchDragEvent(DragEvent event) {
+        if (mOnDragListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
+                && mOnDragListener.onDrag(this, event)) {
+            return true;
+        }
+        return onDragEvent(event);
     }
 
     /**
@@ -8768,31 +10103,6 @@
                 ViewConfiguration.getLongPressTimeout() - delayOffset);
     }
 
-    private static int[] stateSetUnion(final int[] stateSet1,
-                                       final int[] stateSet2) {
-        final int stateSet1Length = stateSet1.length;
-        final int stateSet2Length = stateSet2.length;
-        final int[] newSet = new int[stateSet1Length + stateSet2Length];
-        int k = 0;
-        int i = 0;
-        int j = 0;
-        // This is a merge of the two input state sets and assumes that the
-        // input sets are sorted by the order imposed by ViewDrawableStates.
-        for (int viewState : R.styleable.ViewDrawableStates) {
-            if (i < stateSet1Length && stateSet1[i] == viewState) {
-                newSet[k++] = viewState;
-                i++;
-            } else if (j < stateSet2Length && stateSet2[j] == viewState) {
-                newSet[k++] = viewState;
-                j++;
-            }
-            if (k > 1) {
-                assert(newSet[k - 1] > newSet[k - 2]);
-            }
-        }
-        return newSet;
-    }
-
     /**
      * Inflate a view from an XML resource.  This convenience method wraps the {@link
      * LayoutInflater} class, which provides a full range of options for view inflation.
@@ -8809,128 +10119,6 @@
     }
 
     /**
-     * Scroll the view with standard behavior for scrolling beyond the normal
-     * content boundaries. Views that call this method should override
-     * {@link #onOverScrolled(int, int, boolean, boolean)} to respond to the
-     * results of an over-scroll operation.
-     *
-     * Views can use this method to handle any touch or fling-based scrolling.
-     *
-     * @param deltaX Change in X in pixels
-     * @param deltaY Change in Y in pixels
-     * @param scrollX Current X scroll value in pixels before applying deltaX
-     * @param scrollY Current Y scroll value in pixels before applying deltaY
-     * @param scrollRangeX Maximum content scroll range along the X axis
-     * @param scrollRangeY Maximum content scroll range along the Y axis
-     * @param maxOverScrollX Number of pixels to overscroll by in either direction
-     *          along the X axis.
-     * @param maxOverScrollY Number of pixels to overscroll by in either direction
-     *          along the Y axis.
-     * @param isTouchEvent true if this scroll operation is the result of a touch event.
-     * @return true if scrolling was clamped to an over-scroll boundary along either
-     *          axis, false otherwise.
-     */
-    protected boolean overScrollBy(int deltaX, int deltaY,
-            int scrollX, int scrollY,
-            int scrollRangeX, int scrollRangeY,
-            int maxOverScrollX, int maxOverScrollY,
-            boolean isTouchEvent) {
-        final int overScrollMode = mOverScrollMode;
-        final boolean canScrollHorizontal =
-                computeHorizontalScrollRange() > computeHorizontalScrollExtent();
-        final boolean canScrollVertical =
-                computeVerticalScrollRange() > computeVerticalScrollExtent();
-        final boolean overScrollHorizontal = overScrollMode == OVER_SCROLL_ALWAYS ||
-                (overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
-        final boolean overScrollVertical = overScrollMode == OVER_SCROLL_ALWAYS ||
-                (overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
-
-        int newScrollX = scrollX + deltaX;
-        if (!overScrollHorizontal) {
-            maxOverScrollX = 0;
-        }
-
-        int newScrollY = scrollY + deltaY;
-        if (!overScrollVertical) {
-            maxOverScrollY = 0;
-        }
-
-        // Clamp values if at the limits and record
-        final int left = -maxOverScrollX;
-        final int right = maxOverScrollX + scrollRangeX;
-        final int top = -maxOverScrollY;
-        final int bottom = maxOverScrollY + scrollRangeY;
-
-        boolean clampedX = false;
-        if (newScrollX > right) {
-            newScrollX = right;
-            clampedX = true;
-        } else if (newScrollX < left) {
-            newScrollX = left;
-            clampedX = true;
-        }
-
-        boolean clampedY = false;
-        if (newScrollY > bottom) {
-            newScrollY = bottom;
-            clampedY = true;
-        } else if (newScrollY < top) {
-            newScrollY = top;
-            clampedY = true;
-        }
-
-        onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
-
-        return clampedX || clampedY;
-    }
-
-    /**
-     * Called by {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)} to
-     * respond to the results of an over-scroll operation.
-     *
-     * @param scrollX New X scroll value in pixels
-     * @param scrollY New Y scroll value in pixels
-     * @param clampedX True if scrollX was clamped to an over-scroll boundary
-     * @param clampedY True if scrollY was clamped to an over-scroll boundary
-     */
-    protected void onOverScrolled(int scrollX, int scrollY,
-            boolean clampedX, boolean clampedY) {
-        // Intentionally empty.
-    }
-
-    /**
-     * Returns the over-scroll mode for this view. The result will be
-     * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
-     * (allow over-scrolling only if the view content is larger than the container),
-     * or {@link #OVER_SCROLL_NEVER}.
-     *
-     * @return This view's over-scroll mode.
-     */
-    public int getOverScrollMode() {
-        return mOverScrollMode;
-    }
-
-    /**
-     * Set the over-scroll mode for this view. Valid over-scroll modes are
-     * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
-     * (allow over-scrolling only if the view content is larger than the container),
-     * or {@link #OVER_SCROLL_NEVER}.
-     *
-     * Setting the over-scroll mode of a view will have an effect only if the
-     * view is capable of scrolling.
-     *
-     * @param overScrollMode The new over-scroll mode for this view.
-     */
-    public void setOverScrollMode(int overScrollMode) {
-        if (overScrollMode != OVER_SCROLL_ALWAYS &&
-                overScrollMode != OVER_SCROLL_IF_CONTENT_SCROLLS &&
-                overScrollMode != OVER_SCROLL_NEVER) {
-            throw new IllegalArgumentException("Invalid overscroll mode " + overScrollMode);
-        }
-        mOverScrollMode = overScrollMode;
-    }
-
-    /**
      * A MeasureSpec encapsulates the layout requirements passed from parent to child.
      * Each MeasureSpec represents a requirement for either the width or the height.
      * A MeasureSpec is comprised of a size and a mode. There are three possible
@@ -9134,6 +10322,26 @@
     }
 
     /**
+     * Interface definition for a callback to be invoked when a drag is being dispatched
+     * to this view.  The callback will be invoked before the hosting view's own
+     * onDrag(event) method.  If the listener wants to fall back to the hosting view's
+     * onDrag(event) behavior, it should return 'false' from this callback.
+     */
+    public interface OnDragListener {
+        /**
+         * Called when a drag event is dispatched to a view. This allows listeners
+         * to get a chance to override base View behavior.
+         *
+         * @param v The view the drag has been dispatched to.
+         * @param event The DragEvent object containing full information
+         *        about the event.
+         * @return true if the listener consumed the DragEvent, false in order to fall
+         *         back to the view's default handling.
+         */
+        boolean onDrag(View v, DragEvent event);
+    }
+
+    /**
      * Interface definition for a callback to be invoked when the focus state of
      * a view changed.
      */
@@ -9295,6 +10503,9 @@
         IBinder mPanelParentWindowToken;
         Surface mSurface;
 
+        boolean mHardwareAccelerated;
+        HardwareRenderer mHardwareRenderer;
+        
         /**
          * Scale factor used by the compatibility mode
          */
@@ -9412,6 +10623,13 @@
          */
         final int[] mInvalidateChildLocation = new int[2];
 
+
+        /**
+         * Global to the view hierarchy used as a temporary for dealing with
+         * x/y location when view is transformed.
+         */
+        final float[] mTmpTransformLocation = new float[2];
+
         /**
          * The view tree observer used to dispatch global events like
          * layout, pre-draw, touch mode change, etc.
@@ -9448,6 +10666,11 @@
         final Rect mTmpInvalRect = new Rect();
 
         /**
+         * Temporary for use in computing hit areas with transformed views
+         */
+        final RectF mTmpTransformRect = new RectF();
+
+        /**
          * Temporary list for use in collecting focusable descendents of a view.
          */
         final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24);
@@ -9507,8 +10730,8 @@
 
         public final Interpolator scrollBarInterpolator = new Interpolator(1, 2);
 
-        private final float[] mOpaque = {255.0f};
-        private final float[] mTransparent = {0.0f};
+        private final float[] mOpaque = { 255.0f };
+        private final float[] mTransparent = { 0.0f };
         
         /**
          * When fading should start. This time moves into the future every time
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 924c9d4..85981d2 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -25,6 +25,24 @@
  */
 public class ViewConfiguration {
     /**
+     * Expected bit depth of the display panel.
+     * 
+     * @hide
+     */
+    public static final float PANEL_BIT_DEPTH = 24;
+
+    /**
+     * Minimum alpha required for a view to draw.
+     * 
+     * @hide
+     */
+    public static final float ALPHA_THRESHOLD = 0.5f / PANEL_BIT_DEPTH;
+    /**
+     * @hide
+     */
+    public static final float ALPHA_THRESHOLD_INT = 0x7f / PANEL_BIT_DEPTH;
+
+    /**
      * Defines the width of the horizontal scrollbar and the height of the vertical scrollbar in
      * pixels
      */
@@ -140,16 +158,6 @@
      */
     private static float SCROLL_FRICTION = 0.015f;
 
-    /**
-     * Max distance to overscroll for edge effects
-     */
-    private static final int OVERSCROLL_DISTANCE = 0;
-
-    /**
-     * Max distance to overfling for edge effects
-     */
-    private static final int OVERFLING_DISTANCE = 4;
-
     private final int mEdgeSlop;
     private final int mFadingEdgeLength;
     private final int mMinimumFlingVelocity;
@@ -160,8 +168,6 @@
     private final int mDoubleTapSlop;
     private final int mWindowTouchSlop;
     private final int mMaximumDrawingCacheSize;
-    private final int mOverscrollDistance;
-    private final int mOverflingDistance;
 
     private static final SparseArray<ViewConfiguration> sConfigurations =
             new SparseArray<ViewConfiguration>(2);
@@ -182,8 +188,6 @@
         mWindowTouchSlop = WINDOW_TOUCH_SLOP;
         //noinspection deprecation
         mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
-        mOverscrollDistance = OVERSCROLL_DISTANCE;
-        mOverflingDistance = OVERFLING_DISTANCE;
     }
 
     /**
@@ -212,9 +216,6 @@
 
         // Size of the screen in bytes, in ARGB_8888 format
         mMaximumDrawingCacheSize = 4 * metrics.widthPixels * metrics.heightPixels;
-
-        mOverscrollDistance = (int) (density * OVERSCROLL_DISTANCE + 0.5f);
-        mOverflingDistance = (int) (density * OVERFLING_DISTANCE + 0.5f);
     }
 
     /**
@@ -472,20 +473,6 @@
     }
 
     /**
-     * @return The maximum distance a View should overscroll by when showing edge effects.
-     */
-    public int getScaledOverscrollDistance() {
-        return mOverscrollDistance;
-    }
-
-    /**
-     * @return The maximum distance a View should overfling by when showing edge effects.
-     */
-    public int getScaledOverflingDistance() {
-        return mOverflingDistance;
-    }
-
-    /**
      * The amount of time that the zoom controls should be
      * displayed on the screen expressed in milliseconds.
      * 
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 7b6991f..11e68c5 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -118,24 +118,27 @@
      *
      * @hide
      */
-    @Debug.DebugProperty
-    public static boolean profileDrawing = false;
+    public static final boolean DEBUG_PROFILE_DRAWING = false;
 
     /**
      * Profiles layout times in the events log.
      *
      * @hide
      */
-    @Debug.DebugProperty
-    public static boolean profileLayout = false;
+    public static final boolean DEBUG_PROFILE_LAYOUT = false;
 
     /**
      * Profiles real fps (times between draws) and displays the result.
      *
      * @hide
      */
-    @Debug.DebugProperty
-    public static boolean showFps = false;
+    public static final boolean DEBUG_SHOW_FPS = false;
+
+    /**
+     * Enables detailed logging of drag/drop operations.
+     * @hide
+     */
+    public static final boolean DEBUG_DRAG = true;
 
     /**
      * <p>Enables or disables views consistency check. Even when this property is enabled,
@@ -427,7 +430,7 @@
      * @hide
      */
     public static long getViewInstanceCount() {
-        return View.sInstanceCount;
+        return Debug.countInstancesOfClass(View.class);
     }
 
     /**
@@ -438,7 +441,7 @@
      * @hide
      */
     public static long getViewRootInstanceCount() {
-        return ViewRoot.getInstanceCount();
+        return Debug.countInstancesOfClass(ViewRoot.class);
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b9864ba..17384c1 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.animation.LayoutTransition;
 import com.android.internal.R;
 
 import android.content.Context;
@@ -23,15 +24,15 @@
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.util.AttributeSet;
-import android.util.Config;
-import android.util.EventLog;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.accessibility.AccessibilityEvent;
@@ -63,10 +64,12 @@
  * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
  * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
  * @attr ref android.R.styleable#ViewGroup_descendantFocusability
+ * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
  */
 public abstract class ViewGroup extends View implements ViewParent, ViewManager {
-    private static final boolean DBG = false;
 
+    private static final boolean DBG = false;
+    
     /**
      * Views which have been hidden or removed which need to be animated on
      * their way out.
@@ -86,18 +89,44 @@
     // The view contained within this ViewGroup that has or contains focus.
     private View mFocused;
 
-    // The current transformation to apply on the child being drawn
-    private Transformation mChildTransformation;
+    /**
+     * A Transformation used when drawing children, to
+     * apply on the child being drawn.
+     */
+    private final Transformation mChildTransformation = new Transformation();
+
+    /**
+     * Used to track the current invalidation region.
+     */
     private RectF mInvalidateRegion;
 
-    // Target of Motion events
-    private View mMotionTarget;
-    private final Rect mTempRect = new Rect();
+    /**
+     * A Transformation used to calculate a correct
+     * invalidation area when the application is autoscaled.
+     */
+    private Transformation mInvalidationTransformation;
+
+    // View currently under an ongoing drag
+    private View mCurrentDragView;
+
+    // Does this group have a child that can accept the current drag payload?
+    private boolean mChildAcceptsDrag;
+
+    // Used during drag dispatch
+    private final PointF mLocalPoint = new PointF();
 
     // Layout animation
     private LayoutAnimationController mLayoutAnimationController;
     private Animation.AnimationListener mAnimationListener;
 
+    // First touch target in the linked list of touch targets.
+    private TouchTarget mFirstTouchTarget;
+
+    // Temporary arrays for splitting pointers.
+    private int[] mTmpPointerIndexMap;
+    private int[] mTmpPointerIds;
+    private MotionEvent.PointerCoords[] mTmpPointerCoords;
+
     /**
      * Internal flags.
      *
@@ -228,6 +257,11 @@
     protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
 
     /**
+     * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
+     */
+    private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
+
+    /**
      * Indicates which types of drawing caches are to be kept in memory.
      * This field should be made private, so it is hidden from the SDK.
      * {@hide}
@@ -277,6 +311,18 @@
     // Used to draw cached views
     private final Paint mCachePaint = new Paint();
 
+    // Used to animate add/remove changes in layout
+    private LayoutTransition mTransition;
+
+    // The set of views that are currently being transitioned. This list is used to track views
+    // being removed that should not actually be removed from the parent yet because they are
+    // being animated.
+    private ArrayList<View> mTransitioningViews;
+
+    // List of children changing visibility. This is used to potentially keep rendering
+    // views during a transition when they otherwise would have become gone/invisible
+    private ArrayList<View> mVisibilityChangingChildren;
+
     public ViewGroup(Context context) {
         super(context);
         initViewGroup();
@@ -348,6 +394,15 @@
                 case R.styleable.ViewGroup_descendantFocusability:
                     setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
                     break;
+                case R.styleable.ViewGroup_splitMotionEvents:
+                    setMotionEventSplittingEnabled(a.getBoolean(attr, false));
+                    break;
+                case R.styleable.ViewGroup_animateLayoutChanges:
+                    boolean animateLayoutChanges = a.getBoolean(attr, false);
+                    if (animateLayoutChanges) {
+                        setLayoutTransition(new LayoutTransition());
+                    }
+                    break;
             }
         }
 
@@ -461,6 +516,13 @@
     }
 
     /**
+     * {@inheritDoc}
+     */
+    public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
+        return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null;
+    }
+
+    /**
      * Find the nearest view in the specified direction that wants to take
      * focus.
      *
@@ -696,6 +758,32 @@
     }
 
     /**
+     * @hide
+     * @param child
+     * @param visibility
+     */
+    void onChildVisibilityChanged(View child, int visibility) {
+        if (mTransition != null) {
+            if (visibility == VISIBLE) {
+                mTransition.showChild(this, child);
+            } else {
+                mTransition.hideChild(this, child);
+            }
+            if (visibility != VISIBLE) {
+                // Only track this on disappearing views - appearing views are already visible
+                // and don't need special handling during drawChild()
+                if (mVisibilityChangingChildren == null) {
+                    mVisibilityChangingChildren = new ArrayList<View>();
+                }
+                mVisibilityChangingChildren.add(child);
+                if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
+                    addDisappearingView(child);
+                }
+            }
+        }
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -767,6 +855,167 @@
 
     /**
      * {@inheritDoc}
+     *
+     * !!! TODO: write real docs
+     */
+    @Override
+    public boolean dispatchDragEvent(DragEvent event) {
+        boolean retval = false;
+        final float tx = event.mX;
+        final float ty = event.mY;
+
+        ViewRoot root = getViewRoot();
+
+        // Dispatch down the view hierarchy
+        switch (event.mAction) {
+        case DragEvent.ACTION_DRAG_STARTED: {
+            // clear state to recalculate which views we drag over
+            mCurrentDragView = null;
+
+            // Now dispatch down to our children, caching the responses
+            mChildAcceptsDrag = false;
+            final int count = mChildrenCount;
+            final View[] children = mChildren;
+            for (int i = 0; i < count; i++) {
+                final View child = children[i];
+                if (child.getVisibility() == VISIBLE) {
+                    final boolean handled = children[i].dispatchDragEvent(event);
+                    children[i].mCanAcceptDrop = handled;
+                    if (handled) {
+                        mChildAcceptsDrag = true;
+                    }
+                }
+            }
+
+            // Return HANDLED if one of our children can accept the drag
+            if (mChildAcceptsDrag) {
+                retval = true;
+            }
+        } break;
+
+        case DragEvent.ACTION_DRAG_ENDED: {
+            // Notify all of our children that the drag is over
+            final int count = mChildrenCount;
+            final View[] children = mChildren;
+            for (int i = 0; i < count; i++) {
+                final View child = children[i];
+                if (child.getVisibility() == VISIBLE) {
+                    child.dispatchDragEvent(event);
+                }
+            }
+            // We consider drag-ended to have been handled if one of our children
+            // had offered to handle the drag.
+            if (mChildAcceptsDrag) {
+                retval = true;
+            }
+        } break;
+
+        case DragEvent.ACTION_DRAG_LOCATION: {
+            // Find the [possibly new] drag target
+            final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
+
+            // If we've changed apparent drag target, tell the view root which view
+            // we're over now [for purposes of the eventual drag-recipient-changed
+            // notifications to the framework] and tell the new target that the drag
+            // has entered its bounds.  The root will see setDragFocus() calls all
+            // the way down to the final leaf view that is handling the LOCATION event
+            // before reporting the new potential recipient to the framework.
+            if (mCurrentDragView != target) {
+                root.setDragFocus(target);
+
+                final int action = event.mAction;
+                // If we've dragged off of a child view, send it the EXITED message
+                if (mCurrentDragView != null) {
+                    event.mAction = DragEvent.ACTION_DRAG_EXITED;
+                    mCurrentDragView.dispatchDragEvent(event);
+                }
+                mCurrentDragView = target;
+
+                // If we've dragged over a new child view, send it the ENTERED message
+                if (target != null) {
+                    event.mAction = DragEvent.ACTION_DRAG_ENTERED;
+                    target.dispatchDragEvent(event);
+                }
+                event.mAction = action;  // restore the event's original state
+            }
+
+            // Dispatch the actual drag location notice, localized into its coordinates
+            if (target != null) {
+                event.mX = mLocalPoint.x;
+                event.mY = mLocalPoint.y;
+
+                retval = target.dispatchDragEvent(event);
+
+                event.mX = tx;
+                event.mY = ty;
+            }
+        } break;
+
+        /* Entered / exited dispatch
+         *
+         * DRAG_ENTERED is not dispatched downwards from ViewGroup.  The reason for this is
+         * that we're about to get the corresponding LOCATION event, which we will use to
+         * determine which of our children is the new target; at that point we will
+         * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
+         *
+         * DRAG_EXITED *is* dispatched all the way down immediately: once we know the
+         * drag has left this ViewGroup, we know by definition that every contained subview
+         * is also no longer under the drag point.
+         */
+
+        case DragEvent.ACTION_DRAG_EXITED: {
+            if (mCurrentDragView != null) {
+                mCurrentDragView.dispatchDragEvent(event);
+                mCurrentDragView = null;
+            }
+        } break;
+
+        case DragEvent.ACTION_DROP: {
+            if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
+            View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
+            if (target != null) {
+                if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "   dispatch drop to " + target);
+                event.mX = mLocalPoint.x;
+                event.mY = mLocalPoint.y;
+                retval = target.dispatchDragEvent(event);
+                event.mX = tx;
+                event.mY = ty;
+            } else {
+                if (ViewDebug.DEBUG_DRAG) {
+                    Log.d(View.VIEW_LOG_TAG, "   not dropped on an accepting view");
+                }
+            }
+        } break;
+        }
+
+        // If none of our children could handle the event, try here
+        if (!retval) {
+            // Call up to the View implementation that dispatches to installed listeners
+            retval = super.dispatchDragEvent(event);
+        }
+        return retval;
+    }
+
+    // Find the frontmost child view that lies under the given point, and calculate
+    // the position within its own local coordinate system.
+    View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = count - 1; i >= 0; i--) {
+            final View child = children[i];
+            if (!child.mCanAcceptDrop) {
+                continue;
+            }
+
+            if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
      */
     @Override
     public boolean dispatchKeyEventPreIme(KeyEvent event) {
@@ -827,119 +1076,524 @@
         }
 
         final int action = ev.getAction();
-        final float xf = ev.getX();
-        final float yf = ev.getY();
-        final float scrolledXFloat = xf + mScrollX;
-        final float scrolledYFloat = yf + mScrollY;
-        final Rect frame = mTempRect;
+        final int actionMasked = action & MotionEvent.ACTION_MASK;
 
-        boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
+        // Handle an initial down.
+        if (actionMasked == MotionEvent.ACTION_DOWN) {
+            // Throw away all previous state when starting a new touch gesture.
+            // The framework may have dropped the up or cancel event for the previous gesture
+            // due to an app switch, ANR, or some other state change.
+            cancelAndClearTouchTargets(ev);
+            resetTouchState();
+        }
 
-        if (action == MotionEvent.ACTION_DOWN) {
-            if (mMotionTarget != null) {
-                // this is weird, we got a pen down, but we thought it was
-                // already down!
-                // XXX: We should probably send an ACTION_UP to the current
-                // target.
-                mMotionTarget = null;
+        // Check for interception.
+        final boolean intercepted;
+        if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
+            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
+            if (!disallowIntercept) {
+                intercepted = onInterceptTouchEvent(ev);
+                ev.setAction(action); // restore action in case onInterceptTouchEvent() changed it
+            } else {
+                intercepted = false;
             }
-            // If we're disallowing intercept or if we're allowing and we didn't
-            // intercept
-            if (disallowIntercept || !onInterceptTouchEvent(ev)) {
-                // reset this event's action (just to protect ourselves)
-                ev.setAction(MotionEvent.ACTION_DOWN);
-                // We know we want to dispatch the event down, find a child
-                // who can handle it, start with the front-most child.
-                final int scrolledXInt = (int) scrolledXFloat;
-                final int scrolledYInt = (int) scrolledYFloat;
-                final View[] children = mChildren;
-                final int count = mChildrenCount;
+        } else {
+            intercepted = true;
+        }
 
-                for (int i = count - 1; i >= 0; i--) {
-                    final View child = children[i];
-                    if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
-                            || child.getAnimation() != null) {
-                        child.getHitRect(frame);
-                        if (frame.contains(scrolledXInt, scrolledYInt)) {
-                            // offset the event to the view's coordinate system
-                            final float xc = scrolledXFloat - child.mLeft;
-                            final float yc = scrolledYFloat - child.mTop;
-                            ev.setLocation(xc, yc);
-                            child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
-                            if (child.dispatchTouchEvent(ev))  {
-                                // Event handled, we have a target now.
-                                mMotionTarget = child;
-                                return true;
-                            }
-                            // The event didn't get handled, try the next view.
-                            // Don't reset the event's location, it's not
-                            // necessary here.
+        // Check for cancelation.
+        final boolean canceled = resetCancelNextUpFlag(this)
+                || actionMasked == MotionEvent.ACTION_CANCEL;
+
+        // Update list of touch targets for pointer down, if needed.
+        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
+        TouchTarget newTouchTarget = null;
+        boolean alreadyDispatchedToNewTouchTarget = false;
+        if (!canceled && !intercepted) {
+            if (actionMasked == MotionEvent.ACTION_DOWN
+                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)) {
+                final int actionIndex = ev.getActionIndex(); // always 0 for down
+                final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
+                        : TouchTarget.ALL_POINTER_IDS;
+
+                // Clean up earlier touch targets for this pointer id in case they
+                // have become out of sync.
+                removePointersFromTouchTargets(idBitsToAssign);
+
+                final int childrenCount = mChildrenCount;
+                if (childrenCount != 0) {
+                    // Find a child that can receive the event.  Scan children from front to back.
+                    final View[] children = mChildren;
+                    final float x = ev.getX(actionIndex);
+                    final float y = ev.getY(actionIndex);
+
+                    for (int i = childrenCount - 1; i >= 0; i--) {
+                        final View child = children[i];
+                        if ((child.mViewFlags & VISIBILITY_MASK) != VISIBLE
+                                && child.getAnimation() == null) {
+                            // Skip invisible child unless it is animating.
+                            continue;
+                        }
+
+                        if (!isTransformedTouchPointInView(x, y, child, null)) {
+                            // New pointer is out of child's bounds.
+                            continue;
+                        }
+
+                        newTouchTarget = getTouchTarget(child);
+                        if (newTouchTarget != null) {
+                            // Child is already receiving touch within its bounds.
+                            // Give it the new pointer in addition to the ones it is handling.
+                            newTouchTarget.pointerIdBits |= idBitsToAssign;
+                            break;
+                        }
+
+                        resetCancelNextUpFlag(child);
+                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
+                            // Child wants to receive touch within its bounds.
+                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
+                            alreadyDispatchedToNewTouchTarget = true;
+                            break;
                         }
                     }
                 }
+
+                if (newTouchTarget == null && mFirstTouchTarget != null) {
+                    // Did not find a child to receive the event.
+                    // Assign the pointer to the least recently added target.
+                    newTouchTarget = mFirstTouchTarget;
+                    while (newTouchTarget.next != null) {
+                        newTouchTarget = newTouchTarget.next;
+                    }
+                    newTouchTarget.pointerIdBits |= idBitsToAssign;
+                }
             }
         }
 
-        boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
-                (action == MotionEvent.ACTION_CANCEL);
-
-        if (isUpOrCancel) {
-            // Note, we've already copied the previous state to our local
-            // variable, so this takes effect on the next event
-            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
+        // Dispatch to touch targets.
+        boolean handled = false;
+        if (mFirstTouchTarget == null) {
+            // No touch targets so treat this as an ordinary view.
+            handled = dispatchTransformedTouchEvent(ev, canceled, null,
+                    TouchTarget.ALL_POINTER_IDS);
+        } else {
+            // Dispatch to touch targets, excluding the new touch target if we already
+            // dispatched to it.  Cancel touch targets if necessary.
+            TouchTarget predecessor = null;
+            TouchTarget target = mFirstTouchTarget;
+            while (target != null) {
+                final TouchTarget next = target.next;
+                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
+                    handled = true;
+                } else {
+                    final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
+                    if (dispatchTransformedTouchEvent(ev, cancelChild,
+                            target.child, target.pointerIdBits)) {
+                        handled = true;
+                    }
+                    if (cancelChild) {
+                        if (predecessor == null) {
+                            mFirstTouchTarget = next;
+                        } else {
+                            predecessor.next = next;
+                        }
+                        target.recycle();
+                        target = next;
+                        continue;
+                    }
+                }
+                predecessor = target;
+                target = next;
+            }
         }
 
-        // The event wasn't an ACTION_DOWN, dispatch it to our target if
-        // we have one.
-        final View target = mMotionTarget;
-        if (target == null) {
-            // We don't have a target, this means we're handling the
-            // event as a regular view.
-            ev.setLocation(xf, yf);
-            if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
-                ev.setAction(MotionEvent.ACTION_CANCEL);
-                mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
-            }
-            return super.dispatchTouchEvent(ev);
+        // Update list of touch targets for pointer up or cancel, if needed.
+        if (canceled || actionMasked == MotionEvent.ACTION_UP) {
+            resetTouchState();
+        } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
+            final int actionIndex = ev.getActionIndex();
+            final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
+            removePointersFromTouchTargets(idBitsToRemove);
         }
 
-        // if have a target, see if we're allowed to and want to intercept its
-        // events
-        if (!disallowIntercept && onInterceptTouchEvent(ev)) {
-            final float xc = scrolledXFloat - (float) target.mLeft;
-            final float yc = scrolledYFloat - (float) target.mTop;
-            mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
-            ev.setAction(MotionEvent.ACTION_CANCEL);
-            ev.setLocation(xc, yc);
-            if (!target.dispatchTouchEvent(ev)) {
-                // target didn't handle ACTION_CANCEL. not much we can do
-                // but they should have.
-            }
-            // clear the target
-            mMotionTarget = null;
-            // Don't dispatch this event to our own view, because we already
-            // saw it when intercepting; we just want to give the following
-            // event to the normal onTouchEvent().
+        return handled;
+    }
+
+    /**
+     * Resets all touch state in preparation for a new cycle.
+     */
+    private void resetTouchState() {
+        clearTouchTargets();
+        resetCancelNextUpFlag(this);
+        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
+    }
+
+    /**
+     * Resets the cancel next up flag.
+     * Returns true if the flag was previously set.
+     */
+    private boolean resetCancelNextUpFlag(View view) {
+        if ((view.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
+            view.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
             return true;
         }
+        return false;
+    }
 
-        if (isUpOrCancel) {
-            mMotionTarget = null;
+    /**
+     * Clears all touch targets.
+     */
+    private void clearTouchTargets() {
+        TouchTarget target = mFirstTouchTarget;
+        if (target != null) {
+            do {
+                TouchTarget next = target.next;
+                target.recycle();
+                target = next;
+            } while (target != null);
+            mFirstTouchTarget = null;
+        }
+    }
+
+    /**
+     * Cancels and clears all touch targets.
+     */
+    private void cancelAndClearTouchTargets(MotionEvent event) {
+        if (mFirstTouchTarget != null) {
+            boolean syntheticEvent = false;
+            if (event == null) {
+                final long now = SystemClock.uptimeMillis();
+                event = MotionEvent.obtain(now, now,
+                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+                syntheticEvent = true;
+            }
+
+            for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
+                resetCancelNextUpFlag(target.child);
+                dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
+            }
+            clearTouchTargets();
+
+            if (syntheticEvent) {
+                event.recycle();
+            }
+        }
+    }
+
+    /**
+     * Gets the touch target for specified child view.
+     * Returns null if not found.
+     */
+    private TouchTarget getTouchTarget(View child) {
+        for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
+            if (target.child == child) {
+                return target;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Adds a touch target for specified child to the beginning of the list.
+     * Assumes the target child is not already present.
+     */
+    private TouchTarget addTouchTarget(View child, int pointerIdBits) {
+        TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
+        target.next = mFirstTouchTarget;
+        mFirstTouchTarget = target;
+        return target;
+    }
+
+    /**
+     * Removes the pointer ids from consideration.
+     */
+    private void removePointersFromTouchTargets(int pointerIdBits) {
+        TouchTarget predecessor = null;
+        TouchTarget target = mFirstTouchTarget;
+        while (target != null) {
+            final TouchTarget next = target.next;
+            if ((target.pointerIdBits & pointerIdBits) != 0) {
+                target.pointerIdBits &= ~pointerIdBits;
+                if (target.pointerIdBits == 0) {
+                    if (predecessor == null) {
+                        mFirstTouchTarget = next;
+                    } else {
+                        predecessor.next = next;
+                    }
+                    target.recycle();
+                    target = next;
+                    continue;
+                }
+            }
+            predecessor = target;
+            target = next;
+        }
+    }
+
+    /**
+     * Returns true if a child view contains the specified point when transformed
+     * into its coordinate space.
+     * Child must not be null.
+     * @hide
+     */
+    protected boolean isTransformedTouchPointInView(float x, float y, View child,
+            PointF outLocalPoint) {
+        float localX = x + mScrollX - child.mLeft;
+        float localY = y + mScrollY - child.mTop;
+        if (! child.hasIdentityMatrix() && mAttachInfo != null) {
+            final float[] localXY = mAttachInfo.mTmpTransformLocation;
+            localXY[0] = localX;
+            localXY[1] = localY;
+            child.getInverseMatrix().mapPoints(localXY);
+            localX = localXY[0];
+            localY = localXY[1];
+        }
+        final boolean isInView = child.pointInView(localX, localY);
+        if (isInView && outLocalPoint != null) {
+            outLocalPoint.set(localX, localY);
+        }
+        return isInView;
+    }
+
+    /**
+     * Transforms a motion event into the coordinate space of a particular child view,
+     * filters out irrelevant pointer ids, and overrides its action if necessary.
+     * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
+     */
+    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
+            View child, int desiredPointerIdBits) {
+        final boolean handled;
+
+        // Canceling motions is a special case.  We don't need to perform any transformations
+        // or filtering.  The important part is the action, not the contents.
+        final int oldAction = event.getAction();
+        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
+            event.setAction(MotionEvent.ACTION_CANCEL);
+            if (child == null) {
+                handled = super.dispatchTouchEvent(event);
+            } else {
+                handled = child.dispatchTouchEvent(event);
+            }
+            event.setAction(oldAction);
+            return handled;
         }
 
-        // finally offset the event to the target's coordinate system and
-        // dispatch the event.
-        final float xc = scrolledXFloat - (float) target.mLeft;
-        final float yc = scrolledYFloat - (float) target.mTop;
-        ev.setLocation(xc, yc);
-
-        if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
-            ev.setAction(MotionEvent.ACTION_CANCEL);
-            target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
-            mMotionTarget = null;
+        // Calculate the number of pointers to deliver.
+        final int oldPointerCount = event.getPointerCount();
+        int newPointerCount = 0;
+        if (desiredPointerIdBits == TouchTarget.ALL_POINTER_IDS) {
+            newPointerCount = oldPointerCount;
+        } else {
+            for (int i = 0; i < oldPointerCount; i++) {
+                final int pointerId = event.getPointerId(i);
+                final int pointerIdBit = 1 << pointerId;
+                if ((pointerIdBit & desiredPointerIdBits) != 0) {
+                    newPointerCount += 1;
+                }
+            }
         }
 
-        return target.dispatchTouchEvent(ev);
+        // If for some reason we ended up in an inconsistent state where it looks like we
+        // might produce a motion event with no pointers in it, then drop the event.
+        if (newPointerCount == 0) {
+            return false;
+        }
+
+        // If the number of pointers is the same and we don't need to perform any fancy
+        // irreversible transformations, then we can reuse the motion event for this
+        // dispatch as long as we are careful to revert any changes we make.
+        final boolean reuse = newPointerCount == oldPointerCount
+                && (child == null || child.hasIdentityMatrix());
+        if (reuse) {
+            if (child == null) {
+                handled = super.dispatchTouchEvent(event);
+            } else {
+                final float offsetX = mScrollX - child.mLeft;
+                final float offsetY = mScrollY - child.mTop;
+                event.offsetLocation(offsetX, offsetY);
+
+                handled = child.dispatchTouchEvent(event);
+
+                event.offsetLocation(-offsetX, -offsetY);
+            }
+            return handled;
+        }
+
+        // Make a copy of the event.
+        // If the number of pointers is different, then we need to filter out irrelevant pointers
+        // as we make a copy of the motion event.
+        MotionEvent transformedEvent;
+        if (newPointerCount == oldPointerCount) {
+            transformedEvent = MotionEvent.obtain(event);
+        } else {
+            growTmpPointerArrays(newPointerCount);
+            final int[] newPointerIndexMap = mTmpPointerIndexMap;
+            final int[] newPointerIds = mTmpPointerIds;
+            final MotionEvent.PointerCoords[] newPointerCoords = mTmpPointerCoords;
+
+            int newPointerIndex = 0;
+            int oldPointerIndex = 0;
+            while (newPointerIndex < newPointerCount) {
+                final int pointerId = event.getPointerId(oldPointerIndex);
+                final int pointerIdBits = 1 << pointerId;
+                if ((pointerIdBits & desiredPointerIdBits) != 0) {
+                    newPointerIndexMap[newPointerIndex] = oldPointerIndex;
+                    newPointerIds[newPointerIndex] = pointerId;
+                    if (newPointerCoords[newPointerIndex] == null) {
+                        newPointerCoords[newPointerIndex] = new MotionEvent.PointerCoords();
+                    }
+
+                    newPointerIndex += 1;
+                }
+                oldPointerIndex += 1;
+            }
+
+            final int newAction;
+            if (cancel) {
+                newAction = MotionEvent.ACTION_CANCEL;
+            } else {
+                final int oldMaskedAction = oldAction & MotionEvent.ACTION_MASK;
+                if (oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN
+                        || oldMaskedAction == MotionEvent.ACTION_POINTER_UP) {
+                    final int changedPointerId = event.getPointerId(
+                            (oldAction & MotionEvent.ACTION_POINTER_INDEX_MASK)
+                                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT);
+                    final int changedPointerIdBits = 1 << changedPointerId;
+                    if ((changedPointerIdBits & desiredPointerIdBits) != 0) {
+                        if (newPointerCount == 1) {
+                            // The first/last pointer went down/up.
+                            newAction = oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN
+                                    ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP;
+                        } else {
+                            // A secondary pointer went down/up.
+                            int newChangedPointerIndex = 0;
+                            while (newPointerIds[newChangedPointerIndex] != changedPointerId) {
+                                newChangedPointerIndex += 1;
+                            }
+                            newAction = oldMaskedAction | (newChangedPointerIndex
+                                    << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
+                        }
+                    } else {
+                        // An unrelated pointer changed.
+                        newAction = MotionEvent.ACTION_MOVE;
+                    }
+                } else {
+                    // Simple up/down/cancel/move motion action.
+                    newAction = oldMaskedAction;
+                }
+            }
+
+            transformedEvent = null;
+            final int historySize = event.getHistorySize();
+            for (int historyIndex = 0; historyIndex <= historySize; historyIndex++) {
+                for (newPointerIndex = 0; newPointerIndex < newPointerCount; newPointerIndex++) {
+                    final MotionEvent.PointerCoords c = newPointerCoords[newPointerIndex];
+                    oldPointerIndex = newPointerIndexMap[newPointerIndex];
+                    if (historyIndex != historySize) {
+                        event.getHistoricalPointerCoords(oldPointerIndex, historyIndex, c);
+                    } else {
+                        event.getPointerCoords(oldPointerIndex, c);
+                    }
+                }
+
+                final long eventTime;
+                if (historyIndex != historySize) {
+                    eventTime = event.getHistoricalEventTime(historyIndex);
+                } else {
+                    eventTime = event.getEventTime();
+                }
+
+                if (transformedEvent == null) {
+                    transformedEvent = MotionEvent.obtain(
+                            event.getDownTime(), eventTime, newAction,
+                            newPointerCount, newPointerIds, newPointerCoords,
+                            event.getMetaState(), event.getXPrecision(), event.getYPrecision(),
+                            event.getDeviceId(), event.getEdgeFlags(), event.getSource(),
+                            event.getFlags());
+                } else {
+                    transformedEvent.addBatch(eventTime, newPointerCoords, 0);
+                }
+            }
+        }
+
+        // Perform any necessary transformations and dispatch.
+        if (child == null) {
+            handled = super.dispatchTouchEvent(transformedEvent);
+        } else {
+            final float offsetX = mScrollX - child.mLeft;
+            final float offsetY = mScrollY - child.mTop;
+            transformedEvent.offsetLocation(offsetX, offsetY);
+            if (! child.hasIdentityMatrix()) {
+                transformedEvent.transform(child.getInverseMatrix());
+            }
+
+            handled = child.dispatchTouchEvent(transformedEvent);
+        }
+
+        // Done.
+        transformedEvent.recycle();
+        return handled;
+    }
+
+    /**
+     * Enlarge the temporary pointer arrays for splitting pointers.
+     * May discard contents (but keeps PointerCoords objects to avoid reallocating them).
+     */
+    private void growTmpPointerArrays(int desiredCapacity) {
+        final MotionEvent.PointerCoords[] oldTmpPointerCoords = mTmpPointerCoords;
+        int capacity;
+        if (oldTmpPointerCoords != null) {
+            capacity = oldTmpPointerCoords.length;
+            if (desiredCapacity <= capacity) {
+                return;
+            }
+        } else {
+            capacity = 4;
+        }
+
+        while (capacity < desiredCapacity) {
+            capacity *= 2;
+        }
+
+        mTmpPointerIndexMap = new int[capacity];
+        mTmpPointerIds = new int[capacity];
+        mTmpPointerCoords = new MotionEvent.PointerCoords[capacity];
+
+        if (oldTmpPointerCoords != null) {
+            System.arraycopy(oldTmpPointerCoords, 0, mTmpPointerCoords, 0,
+                    oldTmpPointerCoords.length);
+        }
+    }
+
+    /**
+     * Enable or disable the splitting of MotionEvents to multiple children during touch event
+     * dispatch. This behavior is disabled by default.
+     *
+     * <p>When this option is enabled MotionEvents may be split and dispatched to different child
+     * views depending on where each pointer initially went down. This allows for user interactions
+     * such as scrolling two panes of content independently, chording of buttons, and performing
+     * independent gestures on different pieces of content.
+     *
+     * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
+     *              child views. <code>false</code> to only allow one child view to be the target of
+     *              any MotionEvent received by this ViewGroup.
+     */
+    public void setMotionEventSplittingEnabled(boolean split) {
+        // TODO Applications really shouldn't change this setting mid-touch event,
+        // but perhaps this should handle that case and send ACTION_CANCELs to any child views
+        // with gestures in progress when this is changed.
+        if (split) {
+            mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
+        } else {
+            mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
+        }
+    }
+
+    /**
+     * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
+     */
+    public boolean isMotionEventSplittingEnabled() {
+        return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
     }
 
     /**
@@ -1083,10 +1737,10 @@
         }
         return false;
     }
-    
+
     /**
      * {@inheritDoc}
-     * 
+     *
      * @hide
      */
     @Override
@@ -1098,10 +1752,10 @@
             children[i].dispatchStartTemporaryDetach();
         }
     }
-    
+
     /**
      * {@inheritDoc}
-     * 
+     *
      * @hide
      */
     @Override
@@ -1142,19 +1796,12 @@
      */
     @Override
     void dispatchDetachedFromWindow() {
-        // If we still have a motion target, we are still in the process of
+        // If we still have a touch target, we are still in the process of
         // dispatching motion events to a child; we need to get rid of that
         // child to avoid dispatching events to it after the window is torn
         // down. To make sure we keep the child in a consistent state, we
         // first send it an ACTION_CANCEL motion event.
-        if (mMotionTarget != null) {
-            final long now = SystemClock.uptimeMillis();
-            final MotionEvent event = MotionEvent.obtain(now, now,
-                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
-            mMotionTarget.dispatchTouchEvent(event);
-            event.recycle();
-            mMotionTarget = null;
-        }
+        cancelAndClearTouchTargets(null);
 
         final int count = mChildrenCount;
         final View[] children = mChildren;
@@ -1187,7 +1834,10 @@
         final int count = mChildrenCount;
         final View[] children = mChildren;
         for (int i = 0; i < count; i++) {
-            children[i].dispatchSaveInstanceState(container);
+            View c = children[i];
+            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
+                c.dispatchSaveInstanceState(container);
+            }
         }
     }
 
@@ -1212,7 +1862,10 @@
         final int count = mChildrenCount;
         final View[] children = mChildren;
         for (int i = 0; i < count; i++) {
-            children[i].dispatchRestoreInstanceState(container);
+            View c = children[i];
+            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
+                c.dispatchRestoreInstanceState(container);
+            }
         }
     }
 
@@ -1251,12 +1904,15 @@
         if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
             final int count = mChildrenCount;
             final View[] children = mChildren;
+            final boolean buildCache = !isHardwareAccelerated();
 
             for (int i = 0; i < count; i++) {
                 final View child = children[i];
                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                     child.setDrawingCacheEnabled(true);
-                    child.buildDrawingCache(true);
+                    if (buildCache) {
+                        child.buildDrawingCache(true);
+                    }
                 }
             }
 
@@ -1299,7 +1955,7 @@
         if (skipChildren) {
             for (int i = 0; i < count; i++) {
                 getChildAt(i).setVisibility(visibilities[i]);
-            }        
+            }
         }
 
         return b;
@@ -1317,6 +1973,7 @@
         if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
             final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
 
+            final boolean buildCache = !isHardwareAccelerated();
             for (int i = 0; i < count; i++) {
                 final View child = children[i];
                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
@@ -1325,7 +1982,9 @@
                     bindLayoutAnimation(child);
                     if (cache) {
                         child.setDrawingCacheEnabled(true);
-                        child.buildDrawingCache(true);
+                        if (buildCache) {                        
+                            child.buildDrawingCache(true);
+                        }
                     }
                 }
             }
@@ -1429,7 +2088,7 @@
      *
      * @param i The current iteration.
      * @return The index of the child to draw this iteration.
-     * 
+     *
      * @see #setChildrenDrawingOrderEnabled(boolean)
      * @see #isChildrenDrawingOrderEnabled()
      */
@@ -1479,25 +2138,29 @@
         final int cr = child.mRight;
         final int cb = child.mBottom;
 
+        final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
+
         final int flags = mGroupFlags;
 
         if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
-            if (mChildTransformation != null) {
-                mChildTransformation.clear();
-            }
+            mChildTransformation.clear();
             mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION;
         }
 
         Transformation transformToApply = null;
+        Transformation invalidationTransform;
         final Animation a = child.getAnimation();
         boolean concatMatrix = false;
 
-        if (a != null) {
-            if (mInvalidateRegion == null) {
-                mInvalidateRegion = new RectF();
-            }
-            final RectF region = mInvalidateRegion;
+        boolean scalingRequired = false;
+        boolean caching = false;
+        if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
+                (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
+            caching = true;
+            if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
+        }
 
+        if (a != null) {
             final boolean initialized = a.isInitialized();
             if (!initialized) {
                 a.initialize(cr - cl, cb - ct, getWidth(), getHeight());
@@ -1505,10 +2168,17 @@
                 child.onAnimationStart();
             }
 
-            if (mChildTransformation == null) {
-                mChildTransformation = new Transformation();
+            more = a.getTransformation(drawingTime, mChildTransformation,
+                    scalingRequired ? mAttachInfo.mApplicationScale : 1f);
+            if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
+                if (mInvalidationTransformation == null) {
+                    mInvalidationTransformation = new Transformation();
+                }
+                invalidationTransform = mInvalidationTransformation;
+                a.getTransformation(drawingTime, invalidationTransform, 1f);
+            } else {
+                invalidationTransform = mChildTransformation;
             }
-            more = a.getTransformation(drawingTime, mChildTransformation);
             transformToApply = mChildTransformation;
 
             concatMatrix = a.willChangeTransformationMatrix();
@@ -1525,7 +2195,11 @@
                         invalidate(cl, ct, cr, cb);
                     }
                 } else {
-                    a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, transformToApply);
+                    if (mInvalidateRegion == null) {
+                        mInvalidateRegion = new RectF();
+                    }
+                    final RectF region = mInvalidateRegion;
+                    a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, invalidationTransform);
 
                     // The child need to draw an animation, potentially offscreen, so
                     // make sure we do not cancel invalidate requests
@@ -1538,9 +2212,6 @@
             }
         } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
                 FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
-            if (mChildTransformation == null) {
-                mChildTransformation = new Transformation();
-            }
             final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation);
             if (hasTransform) {
                 final int transformType = mChildTransformation.getTransformationType();
@@ -1550,6 +2221,8 @@
             }
         }
 
+        concatMatrix |= !childHasIdentityMatrix;
+
         // Sets the flag as early as possible to allow draw() implementations
         // to call invalidate() successfully when doing animations
         child.mPrivateFlags |= DRAWN;
@@ -1559,20 +2232,30 @@
             return more;
         }
 
+        float alpha = child.getAlpha();
+        // Bail out early if the view does not need to be drawn
+        if (alpha <= ViewConfiguration.ALPHA_THRESHOLD && (child.mPrivateFlags & ALPHA_SET) == 0 &&
+                !(child instanceof SurfaceView)) {
+            return more;
+        }
+
         child.computeScroll();
 
         final int sx = child.mScrollX;
         final int sy = child.mScrollY;
 
-        boolean scalingRequired = false;
+        DisplayList displayList = null;
         Bitmap cache = null;
-        if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
-                (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
-            cache = child.getDrawingCache(true);
-            if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
+        if (caching) {
+            if (!canvas.isHardwareAccelerated()) {
+                cache = child.getDrawingCache(true);
+            } else {
+                displayList = child.getDisplayList();
+            }
         }
 
-        final boolean hasNoCache = cache == null;
+        final boolean hasDisplayList = displayList != null && displayList.isReady();
+        final boolean hasNoCache = cache == null || hasDisplayList;
 
         final int restoreTo = canvas.save();
         if (hasNoCache) {
@@ -1586,40 +2269,59 @@
             }
         }
 
-        float alpha = 1.0f;
-
-        if (transformToApply != null) {
-            if (concatMatrix) {
+        if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
+            if (transformToApply != null || !childHasIdentityMatrix) {
                 int transX = 0;
                 int transY = 0;
+
                 if (hasNoCache) {
                     transX = -sx;
                     transY = -sy;
                 }
-                // Undo the scroll translation, apply the transformation matrix,
-                // then redo the scroll translate to get the correct result.
-                canvas.translate(-transX, -transY);
-                canvas.concat(transformToApply.getMatrix());
-                canvas.translate(transX, transY);
-                mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
+
+                if (transformToApply != null) {
+                    if (concatMatrix) {
+                        // Undo the scroll translation, apply the transformation matrix,
+                        // then redo the scroll translate to get the correct result.
+                        canvas.translate(-transX, -transY);
+                        canvas.concat(transformToApply.getMatrix());
+                        canvas.translate(transX, transY);
+                        mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
+                    }
+
+                    float transformAlpha = transformToApply.getAlpha();
+                    if (transformAlpha < 1.0f) {
+                        alpha *= transformToApply.getAlpha();
+                        mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
+                    }
+                }
+
+                if (!childHasIdentityMatrix) {
+                    canvas.translate(-transX, -transY);
+                    canvas.concat(child.getMatrix());
+                    canvas.translate(transX, transY);
+                }
             }
 
-            alpha = transformToApply.getAlpha();
             if (alpha < 1.0f) {
                 mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
-            }
-
-            if (alpha < 1.0f && hasNoCache) {
-                final int multipliedAlpha = (int) (255 * alpha);
-                if (!child.onSetAlpha(multipliedAlpha)) {
-                    canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, multipliedAlpha,
-                            Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
-                } else {
-                    child.mPrivateFlags |= ALPHA_SET;
+                if (hasNoCache) {
+                    final int multipliedAlpha = (int) (255 * alpha);
+                    if (!child.onSetAlpha(multipliedAlpha)) {
+                        int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
+                        if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
+                            layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
+                        }
+                        canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, multipliedAlpha,
+                                layerFlags);
+                    } else {
+                        child.mPrivateFlags |= ALPHA_SET;
+                    }
                 }
             }
         } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
             child.onSetAlpha(255);
+            child.mPrivateFlags &= ~ALPHA_SET;
         }
 
         if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
@@ -1635,17 +2337,21 @@
         }
 
         if (hasNoCache) {
-            // Fast path for layouts with no backgrounds
-            if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
-                if (ViewDebug.TRACE_HIERARCHY) {
-                    ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
+            if (!hasDisplayList) {
+                // Fast path for layouts with no backgrounds
+                if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
+                    if (ViewDebug.TRACE_HIERARCHY) {
+                        ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
+                    }
+                    child.mPrivateFlags &= ~DIRTY_MASK;
+                    child.dispatchDraw(canvas);
+                } else {
+                    child.draw(canvas);
                 }
-                child.mPrivateFlags &= ~DIRTY_MASK;
-                child.dispatchDraw(canvas);
             } else {
-                child.draw(canvas);
+                ((HardwareCanvas) canvas).drawDisplayList(displayList);
             }
-        } else {
+        } else if (cache != null) {
             final Paint cachePaint = mCachePaint;
             if (alpha < 1.0f) {
                 cachePaint.setAlpha((int) (alpha * 255));
@@ -1654,9 +2360,6 @@
                 cachePaint.setAlpha(255);
                 mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
             }
-            if (Config.DEBUG && ViewDebug.profileDrawing) {
-                EventLog.writeEvent(60003, hashCode());
-            }
             canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
         }
 
@@ -1702,10 +2405,24 @@
         final View[] children = mChildren;
         final int count = mChildrenCount;
         for (int i = 0; i < count; i++) {
+
             children[i].setSelected(selected);
         }
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void dispatchSetActivated(boolean activated) {
+        final View[] children = mChildren;
+        final int count = mChildrenCount;
+        for (int i = 0; i < count; i++) {
+
+            children[i].setActivated(activated);
+        }
+    }
+
     @Override
     protected void dispatchSetPressed(boolean pressed) {
         final View[] children = mChildren;
@@ -1977,6 +2694,10 @@
                     "You must call removeView() on the child's parent first.");
         }
 
+        if (mTransition != null) {
+            mTransition.addChild(this, child);
+        }
+
         if (!checkLayoutParams(params)) {
             params = generateLayoutParams(params);
         }
@@ -2054,7 +2775,9 @@
     // This method also sets the child's mParent to null
     private void removeFromArray(int index) {
         final View[] children = mChildren;
-        children[index].mParent = null;
+        if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
+            children[index].mParent = null;
+        }
         final int count = mChildrenCount;
         if (index == count - 1) {
             children[--mChildrenCount] = null;
@@ -2189,13 +2912,19 @@
     }
 
     private void removeViewInternal(int index, View view) {
+
+        if (mTransition != null) {
+            mTransition.removeChild(this, view);
+        }
+
         boolean clearChildFocus = false;
         if (view == mFocused) {
             view.clearFocusForRemoval();
             clearChildFocus = true;
         }
 
-        if (view.getAnimation() != null) {
+        if (view.getAnimation() != null ||
+                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
             addDisappearingView(view);
         } else if (view.mAttachInfo != null) {
            view.dispatchDetachedFromWindow();
@@ -2214,6 +2943,39 @@
         }
     }
 
+    /**
+     * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
+     * not null, changes in layout which occur because of children being added to or removed from
+     * the ViewGroup will be animated according to the animations defined in that LayoutTransition
+     * object. By default, the transition object is null (so layout changes are not animated).
+     *
+     * @param transition The LayoutTransition object that will animated changes in layout. A value
+     * of <code>null</code> means no transition will run on layout changes.
+     * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
+     */
+    public void setLayoutTransition(LayoutTransition transition) {
+        if (mTransition != null) {
+            mTransition.removeTransitionListener(mLayoutTransitionListener);
+        }
+        mTransition = transition;
+        if (mTransition != null) {
+            mTransition.addTransitionListener(mLayoutTransitionListener);
+        }
+    }
+
+    /**
+     * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
+     * not null, changes in layout which occur because of children being added to or removed from
+     * the ViewGroup will be animated according to the animations defined in that LayoutTransition
+     * object. By default, the transition object is null (so layout changes are not animated).
+     *
+     * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
+     * A value of <code>null</code> means no transition will run on layout changes.
+     */
+    public LayoutTransition getLayoutTransition() {
+        return mTransition;
+    }
+
     private void removeViewsInternal(int start, int count) {
         final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
         final boolean notifyListener = onHierarchyChangeListener != null;
@@ -2227,12 +2989,17 @@
         for (int i = start; i < end; i++) {
             final View view = children[i];
 
+            if (mTransition != null) {
+                mTransition.removeChild(this, view);
+            }
+
             if (view == focused) {
                 view.clearFocusForRemoval();
                 clearChildFocus = view;
             }
 
-            if (view.getAnimation() != null) {
+            if (view.getAnimation() != null ||
+                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
                 addDisappearingView(view);
             } else if (detach) {
                view.dispatchDetachedFromWindow();
@@ -2291,12 +3058,17 @@
         for (int i = count - 1; i >= 0; i--) {
             final View view = children[i];
 
+            if (mTransition != null) {
+                mTransition.removeChild(this, view);
+            }
+
             if (view == focused) {
                 view.clearFocusForRemoval();
                 clearChildFocus = view;
             }
 
-            if (view.getAnimation() != null) {
+            if (view.getAnimation() != null ||
+                    (mTransitioningViews != null && mTransitioningViews.contains(view))) {
                 addDisappearingView(view);
             } else if (detach) {
                view.dispatchDetachedFromWindow();
@@ -2329,11 +3101,16 @@
      * @see #detachViewFromParent(int)
      */
     protected void removeDetachedView(View child, boolean animate) {
+        if (mTransition != null) {
+            mTransition.removeChild(this, child);
+        }
+
         if (child == mFocused) {
             child.clearFocus();
         }
 
-        if (animate && child.getAnimation() != null) {
+        if ((animate && child.getAnimation() != null) ||
+                (mTransitioningViews != null && mTransitioningViews.contains(child))) {
             addDisappearingView(child);
         } else if (child.mAttachInfo != null) {
             child.dispatchDetachedFromWindow();
@@ -2472,10 +3249,6 @@
 
         final AttachInfo attachInfo = mAttachInfo;
         if (attachInfo != null) {
-            final int[] location = attachInfo.mInvalidateChildLocation;
-            location[CHILD_LEFT_INDEX] = child.mLeft;
-            location[CHILD_TOP_INDEX] = child.mTop;
-
             // If the child is drawing an animation, we want to copy this flag onto
             // ourselves and the parent to make sure the invalidate request goes
             // through
@@ -2483,33 +3256,95 @@
 
             // Check whether the child that requests the invalidate is fully opaque
             final boolean isOpaque = child.isOpaque() && !drawAnimation &&
-                    child.getAnimation() != null;
+                    child.getAnimation() == null;
             // Mark the child as dirty, using the appropriate flag
             // Make sure we do not set both flags at the same time
             final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
 
-            do {
-                View view = null;
-                if (parent instanceof View) {
-                    view = (View) parent;
-                }
-
-                if (drawAnimation) {
-                    if (view != null) {
-                        view.mPrivateFlags |= DRAW_ANIMATION;
-                    } else if (parent instanceof ViewRoot) {
-                        ((ViewRoot) parent).mIsAnimating = true;
+            if (dirty == null) {
+                do {
+                    View view = null;
+                    if (parent instanceof View) {
+                        view = (View) parent;
+                        if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
+                            // already marked dirty - we're done
+                            break;
+                        }
                     }
+
+                    if (drawAnimation) {
+                        if (view != null) {
+                            view.mPrivateFlags |= DRAW_ANIMATION;
+                        } else if (parent instanceof ViewRoot) {
+                            ((ViewRoot) parent).mIsAnimating = true;
+                        }
+                    }
+
+                    if (parent instanceof ViewRoot) {
+                        ((ViewRoot) parent).invalidate();
+                        parent = null;
+                    } else if (view != null) {
+                        if ((mPrivateFlags & DRAWN) == DRAWN) {
+                            view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
+                            if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
+                                view.mPrivateFlags =
+                                        (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
+                            }
+                            parent = view.mParent;
+                        } else {
+                            parent = null;
+                        }
+                    }
+                } while (parent != null);
+            } else {
+                final int[] location = attachInfo.mInvalidateChildLocation;
+                location[CHILD_LEFT_INDEX] = child.mLeft;
+                location[CHILD_TOP_INDEX] = child.mTop;
+                Matrix childMatrix = child.getMatrix();
+                if (!childMatrix.isIdentity()) {
+                    RectF boundingRect = attachInfo.mTmpTransformRect;
+                    boundingRect.set(dirty);
+                    childMatrix.mapRect(boundingRect);
+                    dirty.set((int) boundingRect.left, (int) boundingRect.top,
+                            (int) (boundingRect.right + 0.5f),
+                            (int) (boundingRect.bottom + 0.5f));
                 }
 
-                // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
-                // flag coming from the child that initiated the invalidate
-                if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
-                    view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
-                }
+                do {
+                    View view = null;
+                    if (parent instanceof View) {
+                        view = (View) parent;
+                    }
 
-                parent = parent.invalidateChildInParent(location, dirty);
-            } while (parent != null);
+                    if (drawAnimation) {
+                        if (view != null) {
+                            view.mPrivateFlags |= DRAW_ANIMATION;
+                        } else if (parent instanceof ViewRoot) {
+                            ((ViewRoot) parent).mIsAnimating = true;
+                        }
+                    }
+
+                    // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
+                    // flag coming from the child that initiated the invalidate
+                    if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
+                        view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
+                    }
+
+                    parent = parent.invalidateChildInParent(location, dirty);
+                    if (view != null) {
+                        // Account for transform on current parent
+                        Matrix m = view.getMatrix();
+                        if (!m.isIdentity()) {
+                            RectF boundingRect = attachInfo.mTmpTransformRect;
+                            boundingRect.set(dirty);
+                            m.mapRect(boundingRect);
+                            dirty.set((int) boundingRect.left, (int) boundingRect.top,
+                                    (int) (boundingRect.right + 0.5f),
+                                    (int) (boundingRect.bottom + 0.5f));
+                        }
+                    }
+                } while (parent != null);
+            }
         }
     }
 
@@ -2875,7 +3710,7 @@
      */
     @ViewDebug.ExportedProperty(category = "drawing", mapping = {
         @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE,        to = "NONE"),
-        @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ANIMATION"),
+        @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
         @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
         @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ALL")
     })
@@ -3288,6 +4123,80 @@
     }
 
     /**
+     * This method tells the ViewGroup that the given View object, which should have this
+     * ViewGroup as its parent,
+     * should be kept around  (re-displayed when the ViewGroup draws its children) even if it
+     * is removed from its parent. This allows animations, such as those used by
+     * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
+     * the removal of views. A call to this method should always be accompanied by a later call
+     * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
+     * so that the View finally gets removed.
+     *
+     * @param view The View object to be kept visible even if it gets removed from its parent.
+     */
+    public void startViewTransition(View view) {
+        if (view.mParent == this) {
+            if (mTransitioningViews == null) {
+                mTransitioningViews = new ArrayList<View>();
+            }
+            mTransitioningViews.add(view);
+        }
+    }
+
+    /**
+     * This method should always be called following an earlier call to
+     * {@link #startViewTransition(View)}. The given View is finally removed from its parent
+     * and will no longer be displayed. Note that this method does not perform the functionality
+     * of removing a view from its parent; it just discontinues the display of a View that
+     * has previously been removed.
+     *
+     * @return view The View object that has been removed but is being kept around in the visible
+     * hierarchy by an earlier call to {@link #startViewTransition(View)}.
+     */
+    public void endViewTransition(View view) {
+        if (mTransitioningViews != null) {
+            mTransitioningViews.remove(view);
+            final ArrayList<View> disappearingChildren = mDisappearingChildren;
+            if (disappearingChildren != null && disappearingChildren.contains(view)) {
+                disappearingChildren.remove(view);
+                if (mVisibilityChangingChildren != null &&
+                        mVisibilityChangingChildren.contains(view)) {
+                    mVisibilityChangingChildren.remove(view);
+                } else {
+                    if (view.mAttachInfo != null) {
+                        view.dispatchDetachedFromWindow();
+                    }
+                    if (view.mParent != null) {
+                        view.mParent = null;
+                    }
+                }
+                mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
+            }
+        }
+    }
+
+    private LayoutTransition.TransitionListener mLayoutTransitionListener =
+            new LayoutTransition.TransitionListener() {
+        @Override
+        public void startTransition(LayoutTransition transition, ViewGroup container,
+                View view, int transitionType) {
+            // We only care about disappearing items, since we need special logic to keep
+            // those items visible after they've been 'removed'
+            if (transitionType == LayoutTransition.DISAPPEARING) {
+                startViewTransition(view);
+            }
+        }
+
+        @Override
+        public void endTransition(LayoutTransition transition, ViewGroup container,
+                View view, int transitionType) {
+            if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
+                endViewTransition(view);
+            }
+        }
+    };
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -3375,6 +4284,16 @@
     }
 
     @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+        final View[] children = mChildren;
+        final int count = mChildrenCount;
+        for (int i = 0; i < count; i++) {
+            children[i].jumpDrawablesToCurrentState();
+        }
+    }
+
+    @Override
     protected int[] onCreateDrawableState(int extraSpace) {
         if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
             return super.onCreateDrawableState(extraSpace);
@@ -3749,4 +4668,62 @@
             bottomMargin = bottom;
         }
     }
+
+    /* Describes a touched view and the ids of the pointers that it has captured.
+     *
+     * This code assumes that pointer ids are always in the range 0..31 such that
+     * it can use a bitfield to track which pointer ids are present.
+     * As it happens, the lower layers of the input dispatch pipeline also use the
+     * same trick so the assumption should be safe here...
+     */
+    private static final class TouchTarget {
+        private static final int MAX_RECYCLED = 32;
+        private static final Object sRecycleLock = new Object();
+        private static TouchTarget sRecycleBin;
+        private static int sRecycledCount;
+
+        public static final int ALL_POINTER_IDS = -1; // all ones
+
+        // The touched child view.
+        public View child;
+
+        // The combined bit mask of pointer ids for all pointers captured by the target.
+        public int pointerIdBits;
+
+        // The next target in the target list.
+        public TouchTarget next;
+
+        private TouchTarget() {
+        }
+
+        public static TouchTarget obtain(View child, int pointerIdBits) {
+            final TouchTarget target;
+            synchronized (sRecycleLock) {
+                if (sRecycleBin == null) {
+                    target = new TouchTarget();
+                } else {
+                    target = sRecycleBin;
+                    sRecycleBin = target.next;
+                     sRecycledCount--;
+                    target.next = null;
+                }
+            }
+            target.child = child;
+            target.pointerIdBits = pointerIdBits;
+            return target;
+        }
+
+        public void recycle() {
+            synchronized (sRecycleLock) {
+                if (sRecycledCount < MAX_RECYCLED) {
+                    next = sRecycleBin;
+                    sRecycleBin = this;
+                    sRecycledCount += 1;
+                } else {
+                    next = null;
+                }
+                child = null;
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index b456c5d..d7d4c3f 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -162,6 +162,20 @@
     public void createContextMenu(ContextMenu menu);
 
     /**
+     * Start an action mode for the specified view.
+     * <p>
+     * In most cases, a subclass does not need to override this. However, if the
+     * subclass is added directly to the window manager (for example,
+     * {@link ViewManager#addView(View, android.view.ViewGroup.LayoutParams)})
+     * then it should override this and start the action mode.
+     *
+     * @param originalView The source view where the action mode was first invoked
+     * @param callback The callback that will handle lifecycle events for the action mode
+     * @return The new action mode if it was started, null otherwise
+     */
+    public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback);
+
+    /**
      * This method is called on the parent when a child's drawable state
      * has changed.
      *
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 9c249ce..c7c2071 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -16,23 +16,41 @@
 
 package android.view;
 
-import com.android.internal.view.BaseSurfaceHolder;
-import com.android.internal.view.IInputMethodCallback;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.RootViewSurfaceTaker;
-
+import android.Manifest;
+import android.app.ActivityManagerNative;
+import android.content.ClipDescription;
+import android.content.ComponentCallbacks;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.*;
+import android.media.AudioManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.LatencyTimer;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.util.AndroidRuntimeException;
 import android.util.Config;
 import android.util.DisplayMetrics;
-import android.util.Log;
 import android.util.EventLog;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.View.MeasureSpec;
@@ -41,25 +59,16 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
-import android.content.pm.PackageManager;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.ComponentCallbacks;
-import android.content.Context;
-import android.app.ActivityManagerNative;
-import android.Manifest;
-import android.media.AudioManager;
+import com.android.internal.view.BaseSurfaceHolder;
+import com.android.internal.view.IInputMethodCallback;
+import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.RootViewSurfaceTaker;
 
-import java.lang.ref.WeakReference;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
-import javax.microedition.khronos.egl.*;
-import javax.microedition.khronos.opengles.*;
-import static javax.microedition.khronos.opengles.GL10.*;
-
 /**
  * The top of a view hierarchy, implementing the needed protocol between View
  * and the WindowManager.  This is for the most part an internal implementation
@@ -67,14 +76,12 @@
  *
  * {@hide}
  */
-@SuppressWarnings({"EmptyCatchBlock"})
-public final class ViewRoot extends Handler implements ViewParent,
-        View.AttachInfo.Callbacks {
+@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
+public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks {
     private static final String TAG = "ViewRoot";
     private static final boolean DBG = false;
     private static final boolean SHOW_FPS = false;
-    @SuppressWarnings({"ConstantConditionalExpression"})
-    private static final boolean LOCAL_LOGV = false ? Config.LOGD : Config.LOGV;
+    private static final boolean LOCAL_LOGV = false;
     /** @noinspection PointlessBooleanExpression*/
     private static final boolean DEBUG_DRAW = false || LOCAL_LOGV;
     private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV;
@@ -95,8 +102,6 @@
      */
     static final int MAX_TRACKBALL_DELAY = 250;
 
-    static long sInstanceCount = 0;
-
     static IWindowSession sWindowSession;
 
     static final Object mStaticInit = new Object();
@@ -204,17 +209,14 @@
     int mCurScrollY;
     Scroller mScroller;
 
-    EGL10 mEgl;
-    EGLDisplay mEglDisplay;
-    EGLContext mEglContext;
-    EGLSurface mEglSurface;
-    GL11 mGL;
-    Canvas mGlCanvas;
-    boolean mUseGL;
-    boolean mGlWanted;
-
     final ViewConfiguration mViewConfiguration;
 
+    /* Drag/drop */
+    ClipDescription mDragDescription;
+    View mCurrentDragView;
+    final PointF mDragPoint = new PointF();
+    final PointF mLastTouchPoint = new PointF();
+
     /**
      * see {@link #playSoundEffect(int)}
      */
@@ -241,13 +243,12 @@
     public ViewRoot(Context context) {
         super();
 
-        if (MEASURE_LATENCY && lt == null) {
-            lt = new LatencyTimer(100, 1000);
+        if (MEASURE_LATENCY) {
+            if (lt == null) {
+                lt = new LatencyTimer(100, 1000);
+            }
         }
 
-        // For debug only
-        //++sInstanceCount;
-
         // Initialize the statics when this class is first instantiated. This is
         // done here instead of in the static block because Zygote does not
         // allow the spawning of threads.
@@ -262,7 +263,7 @@
         mTempRect = new Rect();
         mVisRect = new Rect();
         mWinFrame = new Rect();
-        mWindow = new W(this, context);
+        mWindow = new W(this);
         mInputMethodCallback = new InputMethodCallback(this);
         mViewVisibility = View.GONE;
         mTransparentRegion = new Region();
@@ -274,19 +275,6 @@
         mDensity = context.getResources().getDisplayMetrics().densityDpi;
     }
 
-    // For debug only
-    /*
-    @Override
-    protected void finalize() throws Throwable {
-        super.finalize();
-        --sInstanceCount;
-    }
-    */
-
-    public static long getInstanceCount() {
-        return sInstanceCount;
-    }
-
     public static void addFirstDrawHandler(Runnable callback) {
         synchronized (sFirstDrawHandlers) {
             if (!sFirstDrawComplete) {
@@ -330,122 +318,18 @@
         return false;
     }
 
-    private void initializeGL() {
-        initializeGLInner();
-        int err = mEgl.eglGetError();
-        if (err != EGL10.EGL_SUCCESS) {
-            // give-up on using GL
-            destroyGL();
-            mGlWanted = false;
-        }
-    }
-
-    private void initializeGLInner() {
-        final EGL10 egl = (EGL10) EGLContext.getEGL();
-        mEgl = egl;
-
-        /*
-         * Get to the default display.
-         */
-        final EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
-        mEglDisplay = eglDisplay;
-
-        /*
-         * We can now initialize EGL for that display
-         */
-        int[] version = new int[2];
-        egl.eglInitialize(eglDisplay, version);
-
-        /*
-         * Specify a configuration for our opengl session
-         * and grab the first configuration that matches is
-         */
-        final int[] configSpec = {
-                EGL10.EGL_RED_SIZE,      5,
-                EGL10.EGL_GREEN_SIZE,    6,
-                EGL10.EGL_BLUE_SIZE,     5,
-                EGL10.EGL_DEPTH_SIZE,    0,
-                EGL10.EGL_NONE
-        };
-        final EGLConfig[] configs = new EGLConfig[1];
-        final int[] num_config = new int[1];
-        egl.eglChooseConfig(eglDisplay, configSpec, configs, 1, num_config);
-        final EGLConfig config = configs[0];
-
-        /*
-         * Create an OpenGL ES context. This must be done only once, an
-         * OpenGL context is a somewhat heavy object.
-         */
-        final EGLContext context = egl.eglCreateContext(eglDisplay, config,
-                EGL10.EGL_NO_CONTEXT, null);
-        mEglContext = context;
-
-        /*
-         * Create an EGL surface we can render into.
-         */
-        final EGLSurface surface = egl.eglCreateWindowSurface(eglDisplay, config, mHolder, null);
-        mEglSurface = surface;
-
-        /*
-         * Before we can issue GL commands, we need to make sure
-         * the context is current and bound to a surface.
-         */
-        egl.eglMakeCurrent(eglDisplay, surface, surface, context);
-
-        /*
-         * Get to the appropriate GL interface.
-         * This is simply done by casting the GL context to either
-         * GL10 or GL11.
-         */
-        final GL11 gl = (GL11) context.getGL();
-        mGL = gl;
-        mGlCanvas = new Canvas(gl);
-        mUseGL = true;
-    }
-
-    private void destroyGL() {
-        // inform skia that the context is gone
-        nativeAbandonGlCaches();
-
-        mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
-                EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
-        mEgl.eglDestroyContext(mEglDisplay, mEglContext);
-        mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
-        mEgl.eglTerminate(mEglDisplay);
-        mEglContext = null;
-        mEglSurface = null;
-        mEglDisplay = null;
-        mEgl = null;
-        mGlCanvas = null;
-        mGL = null;
-        mUseGL = false;
-    }
-
-    private void checkEglErrors() {
-        if (mUseGL) {
-            int err = mEgl.eglGetError();
-            if (err != EGL10.EGL_SUCCESS) {
-                // something bad has happened revert to
-                // normal rendering.
-                destroyGL();
-                if (err != EGL11.EGL_CONTEXT_LOST) {
-                    // we'll try again if it was context lost
-                    mGlWanted = false;
-                }
-            }
-        }
-    }
-
     /**
      * We have one child
      */
-    public void setView(View view, WindowManager.LayoutParams attrs,
-            View panelParentView) {
+    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
         synchronized (this) {
             if (mView == null) {
                 mView = view;
                 mWindowAttributes.copyFrom(attrs);
                 attrs = mWindowAttributes;
+                
+                enableHardwareAcceleration(attrs);
+
                 if (view instanceof RootViewSurfaceTaker) {
                     mSurfaceHolderCallback =
                             ((RootViewSurfaceTaker)view).willYouTakeTheSurface();
@@ -575,6 +459,24 @@
         }
     }
 
+    private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
+        // Only enable hardware acceleration if we are not in the system process
+        // The window manager creates ViewRoots to display animated preview windows
+        // of launching apps and we don't want those to be hardware accelerated
+        if (!HardwareRenderer.sRendererDisabled) {
+            // Try to enable hardware acceleration if requested
+            if (attrs != null &&
+                    (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) {
+                final boolean translucent = attrs.format != PixelFormat.OPAQUE;
+                if (mAttachInfo.mHardwareRenderer != null) {
+                    mAttachInfo.mHardwareRenderer.destroy(true);
+                }                
+                mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent);
+                mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareRenderer != null;
+            }
+        }
+    }
+
     public View getView() {
         return mView;
     }
@@ -641,6 +543,11 @@
     public void invalidateChild(View child, Rect dirty) {
         checkThread();
         if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
+        if (dirty == null) {
+            // Fast invalidation for GL-enabled applications; GL must redraw everything
+            invalidate();
+            return;
+        }
         if (mCurScrollY != 0 || mTranslator != null) {
             mTempRect.set(dirty);
             dirty = mTempRect;
@@ -659,6 +566,11 @@
             scheduleTraversals();
         }
     }
+    
+    void invalidate() {
+        mDirty.set(0, 0, mWidth, mHeight);
+        scheduleTraversals();
+    }
 
     public ViewParent getParent() {
         return null;
@@ -731,8 +643,6 @@
         boolean viewVisibilityChanged = mViewVisibility != viewVisibility
                 || mNewSurfaceNeeded;
 
-        float appScale = mAttachInfo.mApplicationScale;
-
         WindowManager.LayoutParams params = null;
         if (mWindowAttributesChanged) {
             mWindowAttributesChanged = false;
@@ -780,9 +690,9 @@
             attachInfo.mWindowVisibility = viewVisibility;
             host.dispatchWindowVisibilityChanged(viewVisibility);
             if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
-                if (mUseGL) {
-                    destroyGL();
-                }
+                if (mAttachInfo.mHardwareRenderer != null) {
+                    mAttachInfo.mHardwareRenderer.destroy(false);
+                }                
             }
             if (viewVisibility == View.GONE) {
                 // After making a window gone, we will count it as being
@@ -897,10 +807,12 @@
 
         final boolean computesInternalInsets =
                 attachInfo.mTreeObserver.hasComputeInternalInsetsListeners();
+
         boolean insetsPending = false;
         int relayoutResult = 0;
-        if (mFirst || windowShouldResize || insetsChanged
-                || viewVisibilityChanged || params != null) {
+
+        if (mFirst || windowShouldResize || insetsChanged ||
+                viewVisibilityChanged || params != null) {
 
             if (viewVisibility == View.VISIBLE) {
                 // If this window is giving internal insets to the window
@@ -912,26 +824,19 @@
                 // window, waiting until we can finish laying out this window
                 // and get back to the window manager with the ultimately
                 // computed insets.
-                insetsPending = computesInternalInsets
-                        && (mFirst || viewVisibilityChanged);
-
-                if (mWindowAttributes.memoryType == WindowManager.LayoutParams.MEMORY_TYPE_GPU) {
-                    if (params == null) {
-                        params = mWindowAttributes;
-                    }
-                    mGlWanted = true;
-                }
+                insetsPending = computesInternalInsets && (mFirst || viewVisibilityChanged);
             }
 
             if (mSurfaceHolder != null) {
                 mSurfaceHolder.mSurfaceLock.lock();
                 mDrawingAllowed = true;
             }
-            
-            boolean initialized = false;
+
+            boolean hwInitialized = false;
             boolean contentInsetsChanged = false;
             boolean visibleInsetsChanged;
             boolean hadSurface = mSurface.isValid();
+
             try {
                 int fl = 0;
                 if (params != null) {
@@ -991,9 +896,8 @@
                         fullRedrawNeeded = true;
                         mPreviousTransparentRegion.setEmpty();
 
-                        if (mGlWanted && !mUseGL) {
-                            initializeGL();
-                            initialized = mGlCanvas != null;
+                        if (mAttachInfo.mHardwareRenderer != null) {
+                            hwInitialized = mAttachInfo.mHardwareRenderer.initialize(mHolder);
                         }
                     }
                 } else if (!mSurface.isValid()) {
@@ -1041,6 +945,11 @@
                             }
                         }
                         surfaceChanged = true;
+
+                        if (mAttachInfo.mHardwareRenderer != null) {
+                            // This will bail out early if already initialized
+                            mAttachInfo.mHardwareRenderer.initialize(mHolder);
+                        }
                     }
                     if (surfaceChanged) {
                         mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder,
@@ -1070,10 +979,9 @@
                     mSurfaceHolder.mSurfaceLock.unlock();
                 }
             }
-            
-            if (initialized) {
-                mGlCanvas.setViewport((int) (mWidth * appScale + 0.5f),
-                        (int) (mHeight * appScale + 0.5f));
+
+            if (hwInitialized || (windowShouldResize && mAttachInfo.mHardwareRenderer != null)) {
+                mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight);
             }
 
             boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
@@ -1133,7 +1041,7 @@
                 TAG, "Laying out " + host + " to (" +
                 host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")");
             long startTime = 0L;
-            if (Config.DEBUG && ViewDebug.profileLayout) {
+            if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
                 startTime = SystemClock.elapsedRealtime();
             }
             host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
@@ -1146,7 +1054,7 @@
                 }
             }
 
-            if (Config.DEBUG && ViewDebug.profileLayout) {
+            if (ViewDebug.DEBUG_PROFILE_LAYOUT) {
                 EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime);
             }
 
@@ -1346,7 +1254,8 @@
         if (!sFirstDrawComplete) {
             synchronized (sFirstDrawHandlers) {
                 sFirstDrawComplete = true;
-                for (int i=0; i<sFirstDrawHandlers.size(); i++) {
+                final int count = sFirstDrawHandlers.size();
+                for (int i = 0; i< count; i++) {
                     post(sFirstDrawHandlers.get(i));
                 }
             }
@@ -1379,61 +1288,26 @@
             dirty.setEmpty();
             return;
         }
-        
-        if (mUseGL) {
-            if (!dirty.isEmpty()) {
-                Canvas canvas = mGlCanvas;
-                if (mGL != null && canvas != null) {
-                    mGL.glDisable(GL_SCISSOR_TEST);
-                    mGL.glClearColor(0, 0, 0, 0);
-                    mGL.glClear(GL_COLOR_BUFFER_BIT);
-                    mGL.glEnable(GL_SCISSOR_TEST);
-
-                    mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
-                    mAttachInfo.mIgnoreDirtyState = true;
-                    mView.mPrivateFlags |= View.DRAWN;
-
-                    int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-                    try {
-                        canvas.translate(0, -yoff);
-                        if (mTranslator != null) {
-                            mTranslator.translateCanvas(canvas);
-                        }
-                        canvas.setScreenDensity(scalingRequired
-                                ? DisplayMetrics.DENSITY_DEVICE : 0);
-                        mView.draw(canvas);
-                        if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
-                            mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
-                        }
-                    } finally {
-                        canvas.restoreToCount(saveCount);
-                    }
-
-                    mAttachInfo.mIgnoreDirtyState = false;
-
-                    mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
-                    checkEglErrors();
-
-                    if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
-                        int now = (int)SystemClock.elapsedRealtime();
-                        if (sDrawTime != 0) {
-                            nativeShowFPS(canvas, now - sDrawTime);
-                        }
-                        sDrawTime = now;
-                    }
-                }
-            }
-            if (scrolling) {
-                mFullRedrawNeeded = true;
-                scheduleTraversals();
-            }
-            return;
-        }
 
         if (fullRedrawNeeded) {
             mAttachInfo.mIgnoreDirtyState = true;
             dirty.union(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
         }
+        
+        if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
+            if (!dirty.isEmpty() || mIsAnimating) {
+                mIsAnimating = false;
+                dirty.setEmpty();
+                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, yoff);
+            }
+
+            if (scrolling) {
+                mFullRedrawNeeded = true;
+                scheduleTraversals();
+            }
+
+            return;
+        }
 
         if (DEBUG_ORIENTATION || DEBUG_DRAW) {
             Log.v(TAG, "Draw " + mView + "/"
@@ -1482,7 +1356,7 @@
                         //canvas.drawARGB(255, 255, 0, 0);
                     }
 
-                    if (Config.DEBUG && ViewDebug.profileDrawing) {
+                    if (ViewDebug.DEBUG_PROFILE_DRAWING) {
                         startTime = SystemClock.elapsedRealtime();
                     }
 
@@ -1509,7 +1383,6 @@
                                 ", metrics=" + cxt.getResources().getDisplayMetrics() +
                                 ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
                     }
-                    int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
                     try {
                         canvas.translate(0, -yoff);
                         if (mTranslator != null) {
@@ -1520,14 +1393,13 @@
                         mView.draw(canvas);
                     } finally {
                         mAttachInfo.mIgnoreDirtyState = false;
-                        canvas.restoreToCount(saveCount);
                     }
 
                     if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
                         mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
                     }
 
-                    if (SHOW_FPS || Config.DEBUG && ViewDebug.showFps) {
+                    if (SHOW_FPS || ViewDebug.DEBUG_SHOW_FPS) {
                         int now = (int)SystemClock.elapsedRealtime();
                         if (sDrawTime != 0) {
                             nativeShowFPS(canvas, now - sDrawTime);
@@ -1535,7 +1407,7 @@
                         sDrawTime = now;
                     }
 
-                    if (Config.DEBUG && ViewDebug.profileDrawing) {
+                    if (ViewDebug.DEBUG_PROFILE_DRAWING) {
                         EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
                     }
                 }
@@ -1738,8 +1610,6 @@
     }
 
     void dispatchDetachedFromWindow() {
-        if (Config.LOGV) Log.v(TAG, "Detaching in " + this + " of " + mSurface);
-
         if (mView != null) {
             mView.dispatchDetachedFromWindow();
         }
@@ -1748,9 +1618,8 @@
         mAttachInfo.mRootView = null;
         mAttachInfo.mSurface = null;
 
-        if (mUseGL) {
-            destroyGL();
-        }
+        destroyHardwareRenderer();
+
         mSurface.release();
 
         if (mInputChannel != null) {
@@ -1837,6 +1706,8 @@
     public final static int FINISH_INPUT_CONNECTION = 1012;
     public final static int CHECK_FOCUS = 1013;
     public final static int CLOSE_SYSTEM_DIALOGS = 1014;
+    public final static int DISPATCH_DRAG_EVENT = 1015;
+    public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016;
 
     @Override
     public void handleMessage(Message msg) {
@@ -1939,18 +1810,10 @@
                     boolean inTouchMode = msg.arg2 != 0;
                     ensureTouchModeLocally(inTouchMode);
 
-                    if (mGlWanted) {
-                        checkEglErrors();
-                        // we lost the gl context, so recreate it.
-                        if (mGlWanted && !mUseGL) {
-                            initializeGL();
-                            if (mGlCanvas != null) {
-                                float appScale = mAttachInfo.mApplicationScale;
-                                mGlCanvas.setViewport(
-                                        (int) (mWidth * appScale + 0.5f),
-                                        (int) (mHeight * appScale + 0.5f));
-                            }
-                        }
+                    if (mAttachInfo.mHardwareRenderer != null &&
+                            mSurface != null && mSurface.isValid()) {
+                        mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight,
+                                mAttachInfo, mHolder);
                     }
                 }
 
@@ -2000,8 +1863,7 @@
             if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
                 // The IME is trying to say this event is from the
                 // system!  Bad bad bad!
-                event = KeyEvent.changeFlags(event,
-                        event.getFlags()&~KeyEvent.FLAG_FROM_SYSTEM);
+                event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
             }
             deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false);
         } break;
@@ -2022,6 +1884,10 @@
                 mView.onCloseSystemDialogs((String)msg.obj);
             }
         } break;
+        case DISPATCH_DRAG_EVENT:
+        case DISPATCH_DRAG_LOCATION_EVENT: {
+            handleDragEvent((DragEvent)msg.obj);
+        } break;
         }
     }
     
@@ -2189,6 +2055,9 @@
             if (MEASURE_LATENCY) {
                 lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
             }
+            // cache for possible drag-initiation
+            mLastTouchPoint.x = event.getRawX();
+            mLastTouchPoint.y = event.getRawY();
             handled = mView.dispatchTouchEvent(event);
             if (MEASURE_LATENCY) {
                 lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
@@ -2498,8 +2367,7 @@
     private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
         // If mView is null, we just consume the key event because it doesn't
         // make sense to do anything else with it.
-        boolean handled = mView != null
-                ? mView.dispatchKeyEventPreIme(event) : true;
+        boolean handled = mView == null || mView.dispatchKeyEventPreIme(event);
         if (handled) {
             if (sendDone) {
                 finishInputEvent();
@@ -2531,7 +2399,6 @@
             final boolean sendDone = seq >= 0;
             if (!handled) {
                 deliverKeyEventToViewHierarchy(event, sendDone);
-                return;
             } else if (sendDone) {
                 finishInputEvent();
             } else {
@@ -2613,6 +2480,90 @@
         }
     }
 
+    /* drag/drop */
+    private void handleDragEvent(DragEvent event) {
+        // From the root, only drag start/end/location are dispatched.  entered/exited
+        // are determined and dispatched by the viewgroup hierarchy, who then report
+        // that back here for ultimate reporting back to the framework.
+        if (mView != null && mAdded) {
+            final int what = event.mAction;
+
+            if (what == DragEvent.ACTION_DRAG_EXITED) {
+                // A direct EXITED event means that the window manager knows we've just crossed
+                // a window boundary, so the current drag target within this one must have
+                // just been exited.  Send it the usual notifications and then we're done
+                // for now.
+                mView.dispatchDragEvent(event);
+            } else {
+                // Cache the drag description when the operation starts, then fill it in
+                // on subsequent calls as a convenience
+                if (what == DragEvent.ACTION_DRAG_STARTED) {
+                    mCurrentDragView = null;    // Start the current-recipient tracking
+                    mDragDescription = event.mClipDescription;
+                } else {
+                    event.mClipDescription = mDragDescription;
+                }
+
+                // For events with a [screen] location, translate into window coordinates
+                if ((what == DragEvent.ACTION_DRAG_LOCATION) || (what == DragEvent.ACTION_DROP)) {
+                    mDragPoint.set(event.mX, event.mY);
+                    if (mTranslator != null) {
+                        mTranslator.translatePointInScreenToAppWindow(mDragPoint);
+                    }
+
+                    if (mCurScrollY != 0) {
+                        mDragPoint.offset(0, mCurScrollY);
+                    }
+
+                    event.mX = mDragPoint.x;
+                    event.mY = mDragPoint.y;
+                }
+
+                // Remember who the current drag target is pre-dispatch
+                final View prevDragView = mCurrentDragView;
+
+                // Now dispatch the drag/drop event
+                boolean result = mView.dispatchDragEvent(event);
+
+                // If we changed apparent drag target, tell the OS about it
+                if (prevDragView != mCurrentDragView) {
+                    try {
+                        if (prevDragView != null) {
+                            sWindowSession.dragRecipientExited(mWindow);
+                        }
+                        if (mCurrentDragView != null) {
+                            sWindowSession.dragRecipientEntered(mWindow);
+                        }
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Unable to note drag target change");
+                    }
+                }
+
+                // Report the drop result if necessary
+                if (what == DragEvent.ACTION_DROP) {
+                    try {
+                        Log.i(TAG, "Reporting drop result: " + result);
+                        sWindowSession.reportDropResult(mWindow, result);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Unable to report drop result");
+                    }
+                }
+            }
+        }
+        event.recycle();
+    }
+
+    public void getLastTouchPoint(Point outLocation) {
+        outLocation.x = (int) mLastTouchPoint.x;
+        outLocation.y = (int) mLastTouchPoint.y;
+    }
+
+    public void setDragFocus(View newDragTarget) {
+        if (mCurrentDragView != newDragTarget) {
+            mCurrentDragView = newDragTarget;
+        }
+    }
+
     private AudioManager getAudioManager() {
         if (mView == null) {
             throw new IllegalStateException("getAudioManager called when there is no mView");
@@ -2730,9 +2681,11 @@
 
     void doDie() {
         checkThread();
-        if (Config.LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
+        if (LOCAL_LOGV) Log.v(TAG, "DIE in " + this + " of " + mSurface);
         synchronized (this) {
             if (mAdded && !mFirst) {
+                destroyHardwareRenderer();
+
                 int viewVisibility = mView.getVisibility();
                 boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
                 if (mWindowAttributesChanged || viewVisibilityChanged) {
@@ -2757,6 +2710,14 @@
         }
     }
 
+    private void destroyHardwareRenderer() {
+        if (mAttachInfo.mHardwareRenderer != null) {
+            mAttachInfo.mHardwareRenderer.destroy(true);
+            mAttachInfo.mHardwareRenderer = null;
+            mAttachInfo.mHardwareAccelerated = false;
+        }
+    }
+
     public void dispatchFinishedEvent(int seq, boolean handled) {
         Message msg = obtainMessage(FINISHED_EVENT);
         msg.arg1 = seq;
@@ -2809,13 +2770,12 @@
         //noinspection ConstantConditions
         if (false && event.getAction() == KeyEvent.ACTION_DOWN) {
             if (event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
-                if (Config.LOGD) Log.d("keydisp",
-                        "===================================================");
-                if (Config.LOGD) Log.d("keydisp", "Focused view Hierarchy is:");
+                if (DBG) Log.d("keydisp", "===================================================");
+                if (DBG) Log.d("keydisp", "Focused view Hierarchy is:");
+
                 debug();
 
-                if (Config.LOGD) Log.d("keydisp",
-                        "===================================================");
+                if (DBG) Log.d("keydisp", "===================================================");
             }
         }
 
@@ -2895,7 +2855,19 @@
         msg.obj = reason;
         sendMessage(msg);
     }
-    
+
+    public void dispatchDragEvent(DragEvent event) {
+        final int what;
+        if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
+            what = DISPATCH_DRAG_LOCATION_EVENT;
+            removeMessages(what);
+        } else {
+            what = DISPATCH_DRAG_EVENT;
+        }
+        Message msg = obtainMessage(what, event);
+        sendMessage(msg);
+    }
+
     /**
      * The window is getting focus so if there is anything focused/selected
      * send an {@link AccessibilityEvent} to announce that.
@@ -2915,16 +2887,16 @@
         return false;
     }
 
+    public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
+        return null;
+    }
+
     public void createContextMenu(ContextMenu menu) {
     }
 
     public void childDrawableStateChanged(View child) {
     }
 
-    protected Rect getWindowFrame() {
-        return mWinFrame;
-    }
-
     void checkThread() {
         if (mThread != Thread.currentThread()) {
             throw new CalledFromWrongThreadException(
@@ -3003,16 +2975,15 @@
     static class W extends IWindow.Stub {
         private final WeakReference<ViewRoot> mViewRoot;
 
-        public W(ViewRoot viewRoot, Context context) {
+        W(ViewRoot viewRoot) {
             mViewRoot = new WeakReference<ViewRoot>(viewRoot);
         }
 
-        public void resized(int w, int h, Rect coveredInsets,
-                Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+        public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets,
+                boolean reportDraw, Configuration newConfig) {
             final ViewRoot viewRoot = mViewRoot.get();
             if (viewRoot != null) {
-                viewRoot.dispatchResized(w, h, coveredInsets,
-                        visibleInsets, reportDraw, newConfig);
+                viewRoot.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, newConfig);
             }
         }
 
@@ -3107,6 +3078,14 @@
                 }
             }
         }
+
+        /* Drag/drop */
+        public void dispatchDragEvent(DragEvent event) {
+            final ViewRoot viewRoot = mViewRoot.get();
+            if (viewRoot != null) {
+                viewRoot.dispatchDragEvent(event);
+            }
+        }
     }
 
     /**
@@ -3415,8 +3394,4 @@
     }
 
     private static native void nativeShowFPS(Canvas canvas, int durationMillis);
-
-    // inform skia to just abandon its texture cache IDs
-    // doesn't call glDeleteTextures
-    private static native void nativeAbandonGlCaches();
 }
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 26e5cbc..06a0fa6 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Rect;
 
+import java.util.ArrayList;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -32,10 +33,10 @@
 public final class ViewTreeObserver {
     private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
     private CopyOnWriteArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
-    private CopyOnWriteArrayList<OnPreDrawListener> mOnPreDrawListeners;
     private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
     private CopyOnWriteArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
     private CopyOnWriteArrayList<OnScrollChangedListener> mOnScrollChangedListeners;
+    private ArrayList<OnPreDrawListener> mOnPreDrawListeners;
 
     private boolean mAlive = true;
 
@@ -354,7 +355,7 @@
         checkIsAlive();
 
         if (mOnPreDrawListeners == null) {
-            mOnPreDrawListeners = new CopyOnWriteArrayList<OnPreDrawListener>();
+            mOnPreDrawListeners = new ArrayList<OnPreDrawListener>();
         }
 
         mOnPreDrawListeners.add(listener);
@@ -526,7 +527,7 @@
         // could mutate the list by calling the various add/remove methods. This prevents
         // the array from being modified while we iterate it.
         final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
-        if (listeners != null) {
+        if (listeners != null && listeners.size() > 0) {
             for (OnGlobalFocusChangeListener listener : listeners) {
                 listener.onGlobalFocusChanged(oldFocus, newFocus);
             }
@@ -544,7 +545,7 @@
         // could mutate the list by calling the various add/remove methods. This prevents
         // the array from being modified while we iterate it.
         final CopyOnWriteArrayList<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
-        if (listeners != null) {
+        if (listeners != null && listeners.size() > 0) {
             for (OnGlobalLayoutListener listener : listeners) {
                 listener.onGlobalLayout();
             }
@@ -560,15 +561,17 @@
      * @return True if the current draw should be canceled and resceduled, false otherwise.
      */
     public final boolean dispatchOnPreDraw() {
-        // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
-        // perform the dispatching. The iterator is a safe guard against listeners that
+        // NOTE: we *must* clone the listener list to perform the dispatching.
+        // The clone is a safe guard against listeners that
         // could mutate the list by calling the various add/remove methods. This prevents
-        // the array from being modified while we iterate it.
+        // the array from being modified while we process it.
         boolean cancelDraw = false;
-        final CopyOnWriteArrayList<OnPreDrawListener> listeners = mOnPreDrawListeners;
-        if (listeners != null) {
-            for (OnPreDrawListener listener : listeners) {
-                cancelDraw |= !listener.onPreDraw();
+        if (mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0) {
+            final ArrayList<OnPreDrawListener> listeners =
+                    (ArrayList<OnPreDrawListener>) mOnPreDrawListeners.clone();
+            int numListeners = listeners.size();
+            for (int i = 0; i < numListeners; ++i) {
+                cancelDraw |= !(listeners.get(i).onPreDraw());
             }
         }
         return cancelDraw;
@@ -580,13 +583,9 @@
      * @param inTouchMode True if the touch mode is now enabled, false otherwise.
      */
     final void dispatchOnTouchModeChanged(boolean inTouchMode) {
-        // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
-        // perform the dispatching. The iterator is a safe guard against listeners that
-        // could mutate the list by calling the various add/remove methods. This prevents
-        // the array from being modified while we iterate it.
         final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners =
                 mOnTouchModeChangeListeners;
-        if (listeners != null) {
+        if (listeners != null && listeners.size() > 0) {
             for (OnTouchModeChangeListener listener : listeners) {
                 listener.onTouchModeChanged(inTouchMode);
             }
@@ -602,7 +601,7 @@
         // could mutate the list by calling the various add/remove methods. This prevents
         // the array from being modified while we iterate it.
         final CopyOnWriteArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
-        if (listeners != null) {
+        if (listeners != null && listeners.size() > 0) {
             for (OnScrollChangedListener listener : listeners) {
                 listener.onScrollChanged();
             }
@@ -628,7 +627,7 @@
         // the array from being modified while we iterate it.
         final CopyOnWriteArrayList<OnComputeInternalInsetsListener> listeners =
                 mOnComputeInternalInsetsListeners;
-        if (listeners != null) {
+        if (listeners != null && listeners.size() > 0) {
             for (OnComputeInternalInsetsListener listener : listeners) {
                 listener.onComputeInternalInsets(inoutInfo);
             }
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 11c09c1..9fadc58 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -56,11 +56,28 @@
     public static final int FEATURE_CONTEXT_MENU = 6;
     /** Flag for custom title. You cannot combine this feature with other title features. */
     public static final int FEATURE_CUSTOM_TITLE = 7;
-    /** Flag for asking for an OpenGL enabled window.
-        All 2D graphics will be handled by OpenGL ES.
-        @hide
-    */
-    public static final int FEATURE_OPENGL = 8;
+    /**
+     * Flag for enabling the Action Bar.
+     * This is enabled by default for some devices. The Action Bar
+     * replaces the title bar and provides an alternate location
+     * for an on-screen menu button on some devices.
+     */
+    public static final int FEATURE_ACTION_BAR = 8;
+    /**
+     * Flag for requesting an Action Bar that overlays window content.
+     * Normally an Action Bar will sit in the space above window content, but if this
+     * feature is requested along with {@link #FEATURE_ACTION_BAR} it will be layered over
+     * the window content itself. This is useful if you would like your app to have more control
+     * over how the Action Bar is displayed, such as letting application content scroll beneath
+     * an Action Bar with a transparent background or otherwise displaying a transparent/translucent
+     * Action Bar over application content.
+     */
+    public static final int FEATURE_ACTION_BAR_OVERLAY = 9;
+    /**
+     * Flag for specifying the behavior of action modes when an Action Bar is not present.
+     * If overlay is enabled, the action mode UI will be allowed to cover existing window content.
+     */
+    public static final int FEATURE_ACTION_MODE_OVERLAY = 10;
     /** Flag for setting the progress bar's visibility to VISIBLE */
     public static final int PROGRESS_VISIBILITY_ON = -1;
     /** Flag for setting the progress bar's visibility to GONE */
@@ -109,6 +126,8 @@
 
     private boolean mHasSoftInputMode = false;
     
+    private boolean mDestroyed;
+
     // The current window attributes.
     private final WindowManager.LayoutParams mWindowAttributes =
         new WindowManager.LayoutParams();
@@ -292,6 +311,14 @@
          * @see android.app.Activity#onSearchRequested() 
          */
         public boolean onSearchRequested();
+
+        /**
+         * Called when an action mode is being started.
+         *
+         * @param callback Callback to control the lifecycle of this action mode
+         * @return The ActionMode that was started, or null if it was canceled
+         */
+        public ActionMode onStartActionMode(ActionMode.Callback callback);
     }
 
     public Window(Context context) {
@@ -353,6 +380,16 @@
         return mHasChildren;
     }
     
+    /** @hide */
+    public final void destroy() {
+        mDestroyed = true;
+    }
+
+    /** @hide */
+    public final boolean isDestroyed() {
+        return mDestroyed;
+    }
+
     /**
      * Set the window manager for use by this Window to, for example,
      * display panels.  This is <em>not</em> used for displaying the
@@ -360,21 +397,35 @@
      *
      * @param wm The ViewManager for adding new windows.
      */
-    public void setWindowManager(WindowManager wm,
-            IBinder appToken, String appName) {
+    public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
+        setWindowManager(wm, appToken, appName, false);
+    }
+
+    /**
+     * Set the window manager for use by this Window to, for example,
+     * display panels.  This is <em>not</em> used for displaying the
+     * Window itself -- that must be done by the client.
+     *
+     * @param wm The ViewManager for adding new windows.
+     */
+    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
+            boolean hardwareAccelerated) {
         mAppToken = appToken;
         mAppName = appName;
         if (wm == null) {
             wm = WindowManagerImpl.getDefault();
         }
-        mWindowManager = new LocalWindowManager(wm);
+        mWindowManager = new LocalWindowManager(wm, hardwareAccelerated);
     }
 
     private class LocalWindowManager implements WindowManager {
-        LocalWindowManager(WindowManager wm) {
+        private boolean mHardwareAccelerated;
+
+        LocalWindowManager(WindowManager wm, boolean hardwareAccelerated) {
             mWindowManager = wm;
             mDefaultDisplay = mContext.getResources().getDefaultDisplay(
                     mWindowManager.getDefaultDisplay());
+            mHardwareAccelerated = hardwareAccelerated;
         }
 
         public final void addView(View view, ViewGroup.LayoutParams params) {
@@ -421,6 +472,9 @@
             if (wp.packageName == null) {
                 wp.packageName = mContext.getPackageName();
             }
+            if (mHardwareAccelerated) {
+                wp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+            }
             mWindowManager.addView(view, params);
         }
 
@@ -713,6 +767,15 @@
         return (mFeatures&flag) != 0;
     }
 
+    /**
+     * @hide Used internally to help resolve conflicting features.
+     */
+    protected void removeFeature(int featureId) {
+        final int flag = 1<<featureId;
+        mFeatures &= ~flag;
+        mLocalFeatures &= ~(mContainer != null ? (flag&~mContainer.mFeatures) : flag);
+    }
+
     public final void makeActive() {
         if (mContainer != null) {
             if (mContainer.mActiveChild != null) {
@@ -817,6 +880,8 @@
 
     public abstract void togglePanel(int featureId, KeyEvent event);
 
+    public abstract void invalidatePanelMenu(int featureId);
+    
     public abstract boolean performPanelShortcut(int featureId,
                                                  int keyCode,
                                                  KeyEvent event,
@@ -996,6 +1061,16 @@
     {
         return mFeatures;
     }
+    
+    /**
+     * Query for the availability of a certain feature.
+     * 
+     * @param feature The feature ID to check
+     * @return true if the feature is enabled, false otherwise.
+     */
+    public boolean hasFeature(int feature) {
+        return (getFeatures() & (1 << feature)) != 0;
+    }
 
     /**
      * Return the feature bits that are being implemented by this Window.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 091844e..c657a1c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -72,6 +72,7 @@
          * When using {@link Gravity#LEFT} or {@link Gravity#RIGHT} it provides
          * an offset from the given edge.
          */
+        @ViewDebug.ExportedProperty
         public int x;
         
         /**
@@ -79,6 +80,7 @@
          * When using {@link Gravity#TOP} or {@link Gravity#BOTTOM} it provides
          * an offset from the given edge.
          */
+        @ViewDebug.ExportedProperty
         public int y;
 
         /**
@@ -87,6 +89,7 @@
          * should not be stretched. Otherwise the extra pixels will be pro-rated
          * among all views whose weight is greater than 0.
          */
+        @ViewDebug.ExportedProperty
         public float horizontalWeight;
 
         /**
@@ -95,8 +98,9 @@
          * should not be stretched. Otherwise the extra pixels will be pro-rated
          * among all views whose weight is greater than 0.
          */
+        @ViewDebug.ExportedProperty
         public float verticalWeight;
-        
+
         /**
          * The general type of window.  There are three main classes of
          * window types:
@@ -356,6 +360,13 @@
         public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
 
         /**
+         * Window type: the drag-and-drop pseudowindow.  There is only one
+         * drag layer (at most), and it is placed on top of all other windows.
+         * @hide
+         */
+        public static final int TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
@@ -402,6 +413,7 @@
          * @see #FLAG_FULLSCREEN
          * @see #FLAG_FORCE_NOT_FULLSCREEN
          * @see #FLAG_IGNORE_CHEEK_PRESSES
+         * @see #FLAG_HARDWARE_ACCELERATED
          */
         @ViewDebug.ExportedProperty(flagMapping = {
             @ViewDebug.FlagToString(mask = FLAG_BLUR_BEHIND, equals = FLAG_BLUR_BEHIND,
@@ -433,7 +445,9 @@
             @ViewDebug.FlagToString(mask = FLAG_FORCE_NOT_FULLSCREEN,
                     equals = FLAG_FORCE_NOT_FULLSCREEN, name = "FLAG_FORCE_NOT_FULLSCREEN"),
             @ViewDebug.FlagToString(mask = FLAG_IGNORE_CHEEK_PRESSES,
-                    equals = FLAG_IGNORE_CHEEK_PRESSES, name = "FLAG_IGNORE_CHEEK_PRESSES")
+                    equals = FLAG_IGNORE_CHEEK_PRESSES, name = "FLAG_IGNORE_CHEEK_PRESSES"),
+            @ViewDebug.FlagToString(mask = FLAG_HARDWARE_ACCELERATED,
+                    equals = FLAG_HARDWARE_ACCELERATED, name = "FLAG_HARDWARE_ACCELERATED")
         })
         public int flags;
         
@@ -610,6 +624,19 @@
          * {@hide} */
         public static final int FLAG_SPLIT_TOUCH = 0x00800000;
 
+        /**
+         * Flag for a window belonging to an activity that responds to {@link KeyEvent#KEYCODE_MENU}
+         * and therefore needs a Menu key. For devices where Menu is a physical button this flag is
+         * ignored, but on devices where the Menu key is drawn in software it may be hidden unless
+         * this flag is set.
+         *
+         * (Note that Action Bars, when available, are the preferred way to offer additional
+         * functions otherwise accessed via an options menu.)
+         *
+         * {@hide}
+         */
+        public static final int FLAG_NEEDS_MENU_KEY = 0x01000000;
+
         /** Window flag: *sigh* The lock screen wants to continue running its
          * animation while it is fading.  A kind-of hack to allow this.  Maybe
          * in the future we just make this the default behavior.
@@ -629,6 +656,12 @@
          * it is created.
          * {@hide} */
         public static final int FLAG_SYSTEM_ERROR = 0x40000000;
+        
+        /**
+         * Indicates whether this window should be hardware accelerated.
+         * Requesting hardware acceleration does not guarantee it will happen.
+         */
+        public static final int FLAG_HARDWARE_ACCELERATED = 0x80000000;        
 
         /**
          * Given a particular set of window manager flags, determine whether
@@ -729,6 +762,12 @@
          */
         public static final int SOFT_INPUT_ADJUST_PAN = 0x20;
         
+        /** Adjustment option for {@link #softInputMode}: set to have a window
+         * not adjust for a shown input method.  The window will not be resized,
+         * and it will not be panned to make its focus visible.
+         */
+        public static final int SOFT_INPUT_ADJUST_NOTHING = 0x30;
+
         /**
          * Bit for {@link #softInputMode}: set when the user has navigated
          * forward to the window.  This is normally set automatically for
@@ -739,27 +778,6 @@
         public static final int SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x100;
 
         /**
-         * Default value for {@link #screenBrightness} and {@link #buttonBrightness}
-         * indicating that the brightness value is not overridden for this window
-         * and normal brightness policy should be used.
-         */
-        public static final float BRIGHTNESS_OVERRIDE_NONE = -1.0f;
-
-        /**
-         * Value for {@link #screenBrightness} and {@link #buttonBrightness}
-         * indicating that the screen or button backlight brightness should be set
-         * to the lowest value when this window is in front.
-         */
-        public static final float BRIGHTNESS_OVERRIDE_OFF = 0.0f;
-
-        /**
-         * Value for {@link #screenBrightness} and {@link #buttonBrightness}
-         * indicating that the screen or button backlight brightness should be set
-         * to the hightest value when this window is in front.
-         */
-        public static final float BRIGHTNESS_OVERRIDE_FULL = 1.0f;
-
-        /**
          * Desired operating mode for any soft input area.  May any combination
          * of:
          * 
@@ -776,7 +794,17 @@
         public int softInputMode;
         
         /**
-         * Placement of window within the screen as per {@link Gravity}
+         * Placement of window within the screen as per {@link Gravity}.  Both
+         * {@link Gravity#apply(int, int, int, android.graphics.Rect, int, int,
+         * android.graphics.Rect) Gravity.apply} and
+         * {@link Gravity#applyDisplay(int, android.graphics.Rect, android.graphics.Rect)
+         * Gravity.applyDisplay} are used during window layout, with this value
+         * given as the desired gravity.  For example you can specify
+         * {@link Gravity#DISPLAY_CLIP_HORIZONTAL Gravity.DISPLAY_CLIP_HORIZONTAL} and
+         * {@link Gravity#DISPLAY_CLIP_VERTICAL Gravity.DISPLAY_CLIP_VERTICAL} here
+         * to control the behavior of
+         * {@link Gravity#applyDisplay(int, android.graphics.Rect, android.graphics.Rect)
+         * Gravity.applyDisplay}.
          *
          * @see Gravity
          */
@@ -784,13 +812,19 @@
     
         /**
          * The horizontal margin, as a percentage of the container's width,
-         * between the container and the widget.
+         * between the container and the widget.  See
+         * {@link Gravity#apply(int, int, int, android.graphics.Rect, int, int,
+         * android.graphics.Rect) Gravity.apply} for how this is used.  This
+         * field is added with {@link #x} to supply the <var>xAdj</var> parameter.
          */
         public float horizontalMargin;
     
         /**
          * The vertical margin, as a percentage of the container's height,
-         * between the container and the widget.
+         * between the container and the widget.  See
+         * {@link Gravity#apply(int, int, int, android.graphics.Rect, int, int,
+         * android.graphics.Rect) Gravity.apply} for how this is used.  This
+         * field is added with {@link #y} to supply the <var>yAdj</var> parameter.
          */
         public float verticalMargin;
     
@@ -819,6 +853,27 @@
          * dim.
          */
         public float dimAmount = 1.0f;
+
+        /**
+         * Default value for {@link #screenBrightness} and {@link #buttonBrightness}
+         * indicating that the brightness value is not overridden for this window
+         * and normal brightness policy should be used.
+         */
+        public static final float BRIGHTNESS_OVERRIDE_NONE = -1.0f;
+
+        /**
+         * Value for {@link #screenBrightness} and {@link #buttonBrightness}
+         * indicating that the screen or button backlight brightness should be set
+         * to the lowest value when this window is in front.
+         */
+        public static final float BRIGHTNESS_OVERRIDE_OFF = 0.0f;
+
+        /**
+         * Value for {@link #screenBrightness} and {@link #buttonBrightness}
+         * indicating that the screen or button backlight brightness should be set
+         * to the hightest value when this window is in front.
+         */
+        public static final float BRIGHTNESS_OVERRIDE_FULL = 1.0f;
     
         /**
          * This can be used to override the user's preferred brightness of
@@ -1101,6 +1156,7 @@
                 screenOrientation = o.screenOrientation;
                 changes |= SCREEN_ORIENTATION_CHANGED;
             }
+
             return changes;
         }
     
@@ -1128,14 +1184,22 @@
             sb.append('x');
             sb.append((height== MATCH_PARENT ?"fill":(height==WRAP_CONTENT?"wrap":height)));
             sb.append(")");
-            if (softInputMode != 0) {
-                sb.append(" sim=#");
-                sb.append(Integer.toHexString(softInputMode));
+            if (horizontalMargin != 0) {
+                sb.append(" hm=");
+                sb.append(horizontalMargin);
+            }
+            if (verticalMargin != 0) {
+                sb.append(" vm=");
+                sb.append(verticalMargin);
             }
             if (gravity != 0) {
                 sb.append(" gr=#");
                 sb.append(Integer.toHexString(gravity));
             }
+            if (softInputMode != 0) {
+                sb.append(" sim=#");
+                sb.append(Integer.toHexString(softInputMode));
+            }
             sb.append(" ty=");
             sb.append(type);
             sb.append(" fl=#");
@@ -1150,6 +1214,18 @@
                 sb.append(" or=");
                 sb.append(screenOrientation);
             }
+            if (alpha != 1.0f) {
+                sb.append(" alpha=");
+                sb.append(alpha);
+            }
+            if (screenBrightness != BRIGHTNESS_OVERRIDE_NONE) {
+                sb.append(" sbrt=");
+                sb.append(screenBrightness);
+            }
+            if (buttonBrightness != BRIGHTNESS_OVERRIDE_NONE) {
+                sb.append(" bbrt=");
+                sb.append(buttonBrightness);
+            }
             if ((flags & FLAG_COMPATIBLE_WINDOW) != 0) {
                 sb.append(" compatible=true");
             }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 1fd31a3..e78d6a8 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -362,6 +362,13 @@
      * modify the rotation.
      */
     public final int USE_LAST_ROTATION = -1000;
+
+    /** When not otherwise specified by the activity's screenOrientation, rotation should be
+     * determined by the system (that is, using sensors). */
+    public final int USER_ROTATION_FREE = 0;
+    /** When not otherwise specified by the activity's screenOrientation, rotation is set by
+     * the user. */
+    public final int USER_ROTATION_LOCKED = 1;
     
     /**
      * Perform initialization of the policy.
@@ -544,16 +551,18 @@
      * Generally, it's best to keep as little as possible in the queue thread
      * because it's the most fragile.
      * @param whenNanos The event time in uptime nanoseconds.
+     * @param action The key event action.
+     * @param flags The key event flags.
      * @param keyCode The key code.
-     * @param down True if the key is down.
+     * @param scanCode The key's scan code.
      * @param policyFlags The policy flags associated with the key.
      * @param isScreenOn True if the screen is already on
      *
      * @return The bitwise or of the {@link #ACTION_PASS_TO_USER},
      *          {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags.
      */
-    public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, int policyFlags,
-            boolean isScreenOn);
+    public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
+            int keyCode, int scanCode, int policyFlags, boolean isScreenOn);
     
     /**
      * Called from the input dispatcher thread before a key is dispatched to a window.
@@ -571,6 +580,7 @@
      * @param action The key event action.
      * @param flags The key event flags.
      * @param keyCode The key code.
+     * @param scanCode The key's scan code.
      * @param metaState bit mask of meta keys that are held.
      * @param repeatCount Number of times a key down has repeated.
      * @param policyFlags The policy flags associated with the key.
@@ -578,7 +588,7 @@
      * not be further dispatched.
      */
     public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,
-            int keyCode, int metaState, int repeatCount, int policyFlags);
+            int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags);
 
     /**
      * Called when layout of the windows is about to start.
@@ -784,4 +794,14 @@
      * Return false to disable key repeat events from being generated.
      */
     public boolean allowKeyRepeat();
+
+    /**
+     * Inform the policy that the user has chosen a preferred orientation ("rotation lock"). 
+     *
+     * @param mode One of {@link WindowManagerPolicy#USER_ROTATION_LOCKED} or
+     *             {@link * WindowManagerPolicy#USER_ROTATION_FREE}. 
+     * @param rotation One of {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
+     *                 {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}. 
+     */
+    public void setUserRotationMode(int mode, int rotation);
 }
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 0186270..f406da9 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -94,7 +94,9 @@
     public static AccessibilityManager getInstance(Context context) {
         synchronized (sInstanceSync) {
             if (sInstance == null) {
-                sInstance = new AccessibilityManager(context);
+                IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+                IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
+                sInstance = new AccessibilityManager(context, service);
             }
         }
         return sInstance;
@@ -104,13 +106,16 @@
      * Create an instance.
      *
      * @param context A {@link Context}.
+     * @param service An interface to the backing service.
+     *
+     * @hide
      */
-    private AccessibilityManager(Context context) {
+    public AccessibilityManager(Context context, IAccessibilityManager service) {
         mHandler = new MyHandler(context.getMainLooper());
-        IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
-        mService = IAccessibilityManager.Stub.asInterface(iBinder);
+        mService = service;
+
         try {
-            mService.addClient(mClient);
+            mIsEnabled = mService.addClient(mClient);
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
         }
@@ -128,6 +133,18 @@
     }
 
     /**
+     * Returns the client interface this instance registers in
+     * the centralized accessibility manager service.
+     *
+     * @return The client.
+     *
+     * @hide
+     */
+    public IAccessibilityManagerClient getClient() {
+       return (IAccessibilityManagerClient) mClient.asBinder(); 
+    }
+
+    /**
      * Sends an {@link AccessibilityEvent}. If this {@link AccessibilityManager} is not
      * enabled the call is a NOOP.
      *
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 32788be..7633569 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -29,7 +29,7 @@
  */
 interface IAccessibilityManager {
 
-    void addClient(IAccessibilityManagerClient client);
+    boolean addClient(IAccessibilityManagerClient client);
 
     boolean sendAccessibilityEvent(in AccessibilityEvent uiEvent);
 
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 349b7e5..9a1e41c 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -174,7 +174,13 @@
      * Desired Z order mode during animation.
      */
     private int mZAdjustment;
-    
+
+    /**
+     * scalefactor to apply to pivot points, etc. during animation. Subclasses retrieve the
+     * value via getScaleFactor().
+     */
+    private float mScaleFactor = 1f;
+
     /**
      * Don't animate the wallpaper.
      */
@@ -301,7 +307,7 @@
      * animated as well as the objects parents. (This is to support animation
      * sizes being specifed relative to these dimensions.)
      *
-     * <p>Objects that interpret a Animations should call this method when
+     * <p>Objects that interpret Animations should call this method when
      * the sizes of the object being animated and its parent are known, and
      * before calling {@link #getTransformation}.
      *
@@ -553,6 +559,19 @@
     }
     
     /**
+     * The scale factor is set by the call to <code>getTransformation</code>. Overrides of 
+     * {@link #getTransformation(long, Transformation, float)} will get this value
+     * directly. Overrides of {@link #applyTransformation(float, Transformation)} can
+     * call this method to get the value.
+     * 
+     * @return float The scale factor that should be applied to pre-scaled values in
+     * an Animation such as the pivot points in {@link ScaleAnimation} and {@link RotateAnimation}.
+     */
+    protected float getScaleFactor() {
+        return mScaleFactor;
+    }
+
+    /**
      * If detachWallpaper is true, and this is a window animation of a window
      * that has a wallpaper background, then the window will be detached from
      * the wallpaper while it runs.  That is, the animation will only be applied
@@ -735,6 +754,7 @@
      * @return True if the animation is still running
      */
     public boolean getTransformation(long currentTime, Transformation outTransformation) {
+
         if (mStartTime == -1) {
             mStartTime = currentTime;
         }
@@ -806,6 +826,24 @@
 
         return mMore;
     }
+    
+    /**
+     * Gets the transformation to apply at a specified point in time. Implementations of this
+     * method should always replace the specified Transformation or document they are doing
+     * otherwise.
+     *
+     * @param currentTime Where we are in the animation. This is wall clock time.
+     * @param outTransformation A tranformation object that is provided by the
+     *        caller and will be filled in by the animation.
+     * @param scale Scaling factor to apply to any inputs to the transform operation, such
+     *        pivot points being rotated or scaled around.
+     * @return True if the animation is still running
+     */
+    public boolean getTransformation(long currentTime, Transformation outTransformation,
+            float scale) {
+        mScaleFactor = scale;
+        return getTransformation(currentTime, outTransformation);
+    }
 
     /**
      * <p>Indicates whether this animation has started or not.</p>
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 1546dcd..873ce53 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -312,7 +312,7 @@
             final Animation a = animations.get(i);
 
             temp.clear();
-            more = a.getTransformation(currentTime, temp) || more;
+            more = a.getTransformation(currentTime, temp, getScaleFactor()) || more;
             t.compose(temp);
 
             started = started || a.hasStarted();
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 3088382..32ff647 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -33,6 +33,14 @@
  *
  */
 public class AnimationUtils {
+
+    /**
+     * These flags are used when parsing AnimatorSet objects
+     */
+    private static final int TOGETHER = 0;
+    private static final int SEQUENTIALLY = 1;
+
+
     /**
      * Returns the current animation time in milliseconds. This time should be used when invoking
      * {@link Animation#setStartTime(long)}. Refer to {@link android.os.SystemClock} for more
@@ -49,7 +57,7 @@
 
     /**
      * Loads an {@link Animation} object from a resource
-     * 
+     *
      * @param context Application context used to access resources
      * @param id The resource id of the animation to load
      * @return The animation object reference by the specified id
@@ -82,12 +90,12 @@
 
         return createAnimationFromXml(c, parser, null, Xml.asAttributeSet(parser));
     }
-    
+
     private static Animation createAnimationFromXml(Context c, XmlPullParser parser,
             AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException {
-        
+
         Animation anim = null;
- 
+
         // Make sure we are on a start tag.
         int type;
         int depth = parser.getDepth();
@@ -100,7 +108,7 @@
             }
 
             String  name = parser.getName();
-    
+
             if (name.equals("set")) {
                 anim = new AnimationSet(c, attrs);
                 createAnimationFromXml(c, parser, (AnimationSet)anim, attrs);
@@ -120,7 +128,7 @@
                 parent.addAnimation(anim);
             }
         }
-    
+
         return anim;
 
     }
diff --git a/core/java/android/view/animation/Interpolator.java b/core/java/android/view/animation/Interpolator.java
index d14c3e33..5d0fe7e 100644
--- a/core/java/android/view/animation/Interpolator.java
+++ b/core/java/android/view/animation/Interpolator.java
@@ -16,24 +16,16 @@
 
 package android.view.animation;
 
+import android.animation.TimeInterpolator;
+
 /**
  * An interpolator defines the rate of change of an animation. This allows
  * the basic animation effects (alpha, scale, translate, rotate) to be 
  * accelerated, decelerated, repeated, etc.
  */
-public interface Interpolator {
-    
-    /**
-     * Maps a point on the timeline to a multiplier to be applied to the
-     * transformations of an animation.
-     * 
-     * @param input A value between 0 and 1.0 indicating our current point
-     *        in the animation where 0 represents the start and 1.0 represents
-     *        the end
-     * @return The interpolation value. This value can be more than 1.0 for
-     *         Interpolators which overshoot their targets, or less than 0 for
-     *         Interpolators that undershoot their targets.
-     */
-    float getInterpolation(float input);
-    
+public interface Interpolator extends TimeInterpolator {
+    // A new interface, TimeInterpolator, was introduced for the new android.animation
+    // package. This older Interpolator interface extends TimeInterpolator so that users of
+    // the new Animator-based animations can use either the old Interpolator implementations or
+    // new classes that implement TimeInterpolator directly.
 }
diff --git a/core/java/android/view/animation/RotateAnimation.java b/core/java/android/view/animation/RotateAnimation.java
index 284ccce..58bf084 100644
--- a/core/java/android/view/animation/RotateAnimation.java
+++ b/core/java/android/view/animation/RotateAnimation.java
@@ -148,11 +148,12 @@
     @Override
     protected void applyTransformation(float interpolatedTime, Transformation t) {
         float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime);
-
+        float scale = getScaleFactor();
+        
         if (mPivotX == 0.0f && mPivotY == 0.0f) {
             t.getMatrix().setRotate(degrees);
         } else {
-            t.getMatrix().setRotate(degrees, mPivotX, mPivotY);
+            t.getMatrix().setRotate(degrees, mPivotX * scale, mPivotY * scale);
         }
     }
 
diff --git a/core/java/android/view/animation/ScaleAnimation.java b/core/java/android/view/animation/ScaleAnimation.java
index 1a56c8b..8537d42 100644
--- a/core/java/android/view/animation/ScaleAnimation.java
+++ b/core/java/android/view/animation/ScaleAnimation.java
@@ -161,6 +161,7 @@
     protected void applyTransformation(float interpolatedTime, Transformation t) {
         float sx = 1.0f;
         float sy = 1.0f;
+        float scale = getScaleFactor();
 
         if (mFromX != 1.0f || mToX != 1.0f) {
             sx = mFromX + ((mToX - mFromX) * interpolatedTime);
@@ -172,7 +173,7 @@
         if (mPivotX == 0 && mPivotY == 0) {
             t.getMatrix().setScale(sx, sy);
         } else {
-            t.getMatrix().setScale(sx, sy, mPivotX, mPivotY);
+            t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY);
         }
     }
 
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index b98dcd2..300e5d6 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -94,6 +94,45 @@
     public static final int IME_ACTION_DONE = 0x00000006;
     
     /**
+     * Bits of {@link #IME_MASK_ACTION}: Like {@link #IME_ACTION_NEXT}, but
+     * for moving to the previous field.  This will normally not be used to
+     * specify an action (since it precludes {@link #IME_ACTION_NEXT}, but
+     * can be returned to the app if it sets {@link #IME_FLAG_NAVIGATE_PREVIOUS}.
+     */
+    public static final int IME_ACTION_PREVIOUS = 0x00000007;
+
+    /**
+     * Flag of {@link #imeOptions}: used to request that the IME never go
+     * into fullscreen mode.  Applications need to be aware that the flag is not
+     * a guarantee, and not all IMEs will respect it.
+     */
+    public static final int IME_FLAG_NO_FULLSCREEN = 0x2000000;
+
+    /**
+     * Flag of {@link #imeOptions}: like {@link #IME_FLAG_NAVIGATE_NEXT}, but
+     * specifies there is something interesting that a backward navigation
+     * can focus on.  If the user selects the IME's facility to backward
+     * navigate, this will show up in the application as an {@link #IME_ACTION_PREVIOUS}
+     * at {@link InputConnection#performEditorAction(int)
+     * InputConnection.performEditorAction(int)}.
+     */
+    public static final int IME_FLAG_NAVIGATE_PREVIOUS = 0x4000000;
+
+    /**
+     * Flag of {@link #imeOptions}: used to specify that there is something
+     * interesting that a forward navigation can focus on. This is like using
+     * {@link #IME_ACTION_NEXT}, except allows the IME to be multiline (with
+     * an enter key) as well as provide forward navigation.  Note that some
+     * IMEs may not be able to do this, especially when running on a small
+     * screen where there is little space.  In that case it does not need to
+     * present a UI for this option.  Like {@link #IME_ACTION_NEXT}, if the
+     * user selects the IME's facility to forward navigate, this will show up
+     * in the application at {@link InputConnection#performEditorAction(int)
+     * InputConnection.performEditorAction(int)}.
+     */
+    public static final int IME_FLAG_NAVIGATE_NEXT = 0x8000000;
+
+    /**
      * Flag of {@link #imeOptions}: used to specify that the IME does not need
      * to show its extracted text UI.  For input methods that may be fullscreen,
      * often when in landscape mode, this allows them to be smaller and let part
@@ -129,14 +168,6 @@
     public static final int IME_FLAG_NO_ENTER_ACTION = 0x40000000;
 
     /**
-     * Flag of {@link #imeOptions}: used to request that the IME never go
-     * into fullscreen mode.  Applications need to be aware that the flag is not
-     * a guarantee, and not all IMEs will respect it.
-     * @hide
-     */
-    public static final int IME_FLAG_NO_FULLSCREEN = 0x80000000;
-
-    /**
      * Generic unspecified type for {@link #imeOptions}.
      */
     public static final int IME_NULL = 0x00000000;
@@ -240,6 +271,34 @@
     public Bundle extras;
     
     /**
+     * Ensure that the data in this EditorInfo is compatible with an application
+     * that was developed against the given target API version.  This can
+     * impact the following input types:
+     * {@link InputType#TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS},
+     * {@link InputType#TYPE_TEXT_VARIATION_WEB_PASSWORD}.
+     *
+     * <p>This is called by the framework for input method implementations;
+     * you should not generally need to call it yourself.
+     *
+     * @param targetSdkVersion The API version number that the compatible
+     * application was developed against.
+     */
+    public final void makeCompatible(int targetSdkVersion) {
+        if (targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
+            switch (inputType&(TYPE_MASK_CLASS|TYPE_MASK_VARIATION)) {
+                case TYPE_CLASS_TEXT|TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS:
+                    inputType = TYPE_CLASS_TEXT|TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+                            | (inputType&(~TYPE_MASK_FLAGS));
+                    break;
+                case TYPE_CLASS_TEXT|TYPE_TEXT_VARIATION_WEB_PASSWORD:
+                    inputType = TYPE_CLASS_TEXT|TYPE_TEXT_VARIATION_PASSWORD
+                            | (inputType&(~TYPE_MASK_FLAGS));
+                    break;
+            }
+        }
+    }
+
+    /**
      * Write debug output of this object.
      */
     public void dump(Printer pw, String prefix) {
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 2ddf5f8..5ab3d34 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -219,4 +219,10 @@
      * {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}.
      */
     public void hideSoftInput(int flags, ResultReceiver resultReceiver);
+
+    /**
+     * Notify that the input method subtype is being changed in the same input method.
+     * @param subtype New subtype of the notified input method
+     */
+    public void changeInputMethodSubtype(InputMethodSubtype subtype);
 }
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 357cb5fe..defd104 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -37,13 +37,14 @@
 import android.util.Xml;
 
 import java.io.IOException;
+import java.util.ArrayList;
 
 /**
  * This class is used to specify meta information of an input method.
  */
 public final class InputMethodInfo implements Parcelable {
     static final String TAG = "InputMethodInfo";
-    
+
     /**
      * The Service that implements this input method component.
      */
@@ -68,7 +69,12 @@
      * can change based on the configuration (in particular locale).
      */
     final int mIsDefaultResId;
-    
+
+    /**
+     * The array of the subtypes.
+     */
+    private final ArrayList<InputMethodSubtype> mSubtypes = new ArrayList<InputMethodSubtype>();
+
     /**
      * Constructor.
      * 
@@ -81,11 +87,11 @@
         mService = service;
         ServiceInfo si = service.serviceInfo;
         mId = new ComponentName(si.packageName, si.name).flattenToShortString();
-        
+
         PackageManager pm = context.getPackageManager();
         String settingsActivityComponent = null;
         int isDefaultResId = 0;
-        
+
         XmlResourceParser parser = null;
         try {
             parser = si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA);
@@ -116,13 +122,39 @@
             isDefaultResId = sa.getResourceId(
                     com.android.internal.R.styleable.InputMethod_isDefault, 0);
             sa.recycle();
+
+            final int depth = parser.getDepth();
+            // Parse all subtypes
+            while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+                    && type != XmlPullParser.END_DOCUMENT) {
+                if (type == XmlPullParser.START_TAG) {
+                    nodeName = parser.getName();
+                    if (!"subtype".equals(nodeName)) {
+                        throw new XmlPullParserException(
+                                "Meta-data in input-method does not start with subtype tag");
+                    }
+                    final TypedArray a = res.obtainAttributes(
+                            attrs, com.android.internal.R.styleable.InputMethod_Subtype);
+                    InputMethodSubtype subtype = new InputMethodSubtype(
+                            a.getResourceId(com.android.internal.R.styleable
+                                    .InputMethod_Subtype_label, 0),
+                            a.getResourceId(com.android.internal.R.styleable
+                                    .InputMethod_Subtype_icon, 0),
+                            a.getString(com.android.internal.R.styleable
+                                    .InputMethod_Subtype_imeSubtypeLocale),
+                            a.getString(com.android.internal.R.styleable
+                                    .InputMethod_Subtype_imeSubtypeMode),
+                            a.getString(com.android.internal.R.styleable
+                                    .InputMethod_Subtype_imeSubtypeExtraValue));
+                    mSubtypes.add(subtype);
+                }
+            }
         } catch (NameNotFoundException e) {
             throw new XmlPullParserException(
                     "Unable to create context for: " + si.packageName);
         } finally {
             if (parser != null) parser.close();
         }
-        
         mSettingsActivityName = settingsActivityComponent;
         mIsDefaultResId = isDefaultResId;
     }
@@ -132,8 +164,9 @@
         mSettingsActivityName = source.readString();
         mIsDefaultResId = source.readInt();
         mService = ResolveInfo.CREATOR.createFromParcel(source);
+        source.readTypedList(mSubtypes, InputMethodSubtype.CREATOR);
     }
-    
+
     /**
      * Temporary API for creating a built-in input method.
      */
@@ -156,7 +189,7 @@
         mSettingsActivityName = settingsActivity;
         mIsDefaultResId = 0;
     }
-    
+
     /**
      * Return a unique ID for this input method.  The ID is generated from
      * the package and class name implementing the method.
@@ -164,14 +197,14 @@
     public String getId() {
         return mId;
     }
-    
+
     /**
      * Return the .apk package that implements this input method.
      */
     public String getPackageName() {
         return mService.serviceInfo.packageName;
     }
-    
+
     /**
      * Return the class name of the service component that implements
      * this input method.
@@ -196,7 +229,7 @@
         return new ComponentName(mService.serviceInfo.packageName,
                 mService.serviceInfo.name);
     }
-    
+
     /**
      * Load the user-displayed label for this input method.
      * 
@@ -206,7 +239,7 @@
     public CharSequence loadLabel(PackageManager pm) {
         return mService.loadLabel(pm);
     }
-    
+
     /**
      * Load the user-displayed icon for this input method.
      * 
@@ -216,7 +249,7 @@
     public Drawable loadIcon(PackageManager pm) {
         return mService.loadIcon(pm);
     }
-    
+
     /**
      * Return the class name of an activity that provides a settings UI for
      * the input method.  You can launch this activity be starting it with
@@ -230,7 +263,14 @@
     public String getSettingsActivity() {
         return mSettingsActivityName;
     }
-    
+
+    /**
+     * Return the subtypes of Input Method.
+     */
+    public ArrayList<InputMethodSubtype> getSubtypes() {
+        return mSubtypes;
+    }
+
     /**
      * Return the resource identifier of a resource inside of this input
      * method's .apk that determines whether it should be considered a
@@ -239,7 +279,7 @@
     public int getIsDefaultResourceId() {
         return mIsDefaultResId;
     }
-    
+
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "mId=" + mId
                 + " mSettingsActivityName=" + mSettingsActivityName);
@@ -285,12 +325,14 @@
         dest.writeString(mSettingsActivityName);
         dest.writeInt(mIsDefaultResId);
         mService.writeToParcel(dest, flags);
+        dest.writeTypedList(mSubtypes);
     }
 
     /**
      * Used to make this class parcelable.
      */
-    public static final Parcelable.Creator<InputMethodInfo> CREATOR = new Parcelable.Creator<InputMethodInfo>() {
+    public static final Parcelable.Creator<InputMethodInfo> CREATOR
+            = new Parcelable.Creator<InputMethodInfo>() {
         public InputMethodInfo createFromParcel(Parcel source) {
             return new InputMethodInfo(source);
         }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e30687f..033ee7c 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -33,6 +33,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewRoot;
+import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.view.IInputConnectionWrapper;
@@ -518,6 +519,15 @@
     }
 
     /** @hide */
+    public void setIMEButtonVisible(IBinder imeToken, boolean visible) {
+        try {
+            mService.setIMEButtonVisible(imeToken, visible);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** @hide */
     public void setFullscreenMode(boolean fullScreen) {
         mFullscreenMode = fullScreen;
     }
@@ -1380,7 +1390,7 @@
             }
         }
     }
-    
+
     public void showInputMethodPicker() {
         synchronized (mH) {
             try {
@@ -1391,6 +1401,37 @@
         }
     }
 
+    public void showInputMethodSubtypePicker() {
+        synchronized (mH) {
+            try {
+                mService.showInputMethodSubtypePickerFromClient(mClient);
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId, e);
+            }
+        }
+    }
+
+    public void showInputMethodAndSubtypeEnabler(String topId) {
+        synchronized (mH) {
+            try {
+                mService.showInputMethodAndSubtypeEnablerFromClient(mClient, topId);
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId, e);
+            }
+        }
+    }
+
+    public InputMethodSubtype getCurrentInputMethodSubtype() {
+        synchronized (mH) {
+            try {
+                return mService.getCurrentInputMethodSubtype();
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId, e);
+                return null;
+            }
+        }
+    }
+
     void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
         final Printer p = new PrintWriterPrinter(fout);
         p.println("Input method client state for " + this + ":");
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.aidl b/core/java/android/view/inputmethod/InputMethodSubtype.aidl
new file mode 100644
index 0000000..ed82b84
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+parcelable InputMethodSubtype;
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
new file mode 100644
index 0000000..0925940
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Information given to an {@link InputMethod} about a client connecting
+ * to it.
+ */
+/**
+ * InputMethodSubtype is a subtype contained in the input method. Subtype can describe
+ * locales (e.g. en_US, fr_FR...) and modes (e.g. voice, keyboard...), and is used for
+ * IME switch. The subtype allows the system to call the specified subtype of IME directly.
+ */
+public final class InputMethodSubtype implements Parcelable {
+    private final int mSubtypeNameResId;
+    private final int mSubtypeIconResId;
+    private final String mSubtypeLocale;
+    private final String mSubtypeMode;
+    private final String mSubtypeExtraValue;
+    private final int mSubtypeHashCode;
+
+    /**
+     * Constructor
+     * @param nameId The name of the subtype
+     * @param iconId The icon of the subtype
+     * @param locale The locale supported by the subtype
+     * @param modeId The mode supported by the subtype
+     * @param extraValue The extra value of the subtype
+     */
+    InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue) {
+        mSubtypeNameResId = nameId;
+        mSubtypeIconResId = iconId;
+        mSubtypeLocale = locale;
+        mSubtypeMode = mode;
+        mSubtypeExtraValue = extraValue;
+        mSubtypeHashCode = hashCodeInternal(mSubtypeNameResId, mSubtypeIconResId, mSubtypeLocale,
+                mSubtypeMode, mSubtypeExtraValue);
+    }
+
+    InputMethodSubtype(Parcel source) {
+        mSubtypeNameResId = source.readInt();
+        mSubtypeIconResId = source.readInt();
+        mSubtypeLocale = source.readString();
+        mSubtypeMode = source.readString();
+        mSubtypeExtraValue = source.readString();
+        mSubtypeHashCode = hashCodeInternal(mSubtypeNameResId, mSubtypeIconResId, mSubtypeLocale,
+                mSubtypeMode, mSubtypeExtraValue);
+    }
+
+    /**
+     * @return the name of the subtype
+     */
+    public int getNameResId() {
+        return mSubtypeNameResId;
+    }
+
+    /**
+     * @return the icon of the subtype
+     */
+    public int getIconResId() {
+        return mSubtypeIconResId;
+    }
+
+    /**
+     * @return the locale of the subtype
+     */
+    public String getLocale() {
+        return mSubtypeLocale;
+    }
+
+    /**
+     * @return the mode of the subtype
+     */
+    public String getMode() {
+        return mSubtypeMode;
+    }
+
+    /**
+     * @return the extra value of the subtype
+     */
+    public String getExtraValue() {
+        return mSubtypeExtraValue;
+    }
+
+    @Override
+    public int hashCode() {
+        return mSubtypeHashCode;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof InputMethodSubtype) {
+            InputMethodSubtype subtype = (InputMethodSubtype) o;
+            return (subtype.getNameResId() == getNameResId())
+                && (subtype.getMode() == getMode())
+                && (subtype.getIconResId() == getIconResId())
+                && (subtype.getLocale().equals(getLocale()))
+                && (subtype.getExtraValue().equals(getExtraValue()));
+        }
+        return false;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeInt(mSubtypeNameResId);
+        dest.writeInt(mSubtypeIconResId);
+        dest.writeString(mSubtypeLocale);
+        dest.writeString(mSubtypeMode);
+        dest.writeString(mSubtypeExtraValue);
+    }
+
+    public static final Parcelable.Creator<InputMethodSubtype> CREATOR
+            = new Parcelable.Creator<InputMethodSubtype>() {
+        public InputMethodSubtype createFromParcel(Parcel source) {
+            return new InputMethodSubtype(source);
+        }
+
+        public InputMethodSubtype[] newArray(int size) {
+            return new InputMethodSubtype[size];
+        }
+    };
+
+    private static int hashCodeInternal(int nameResId, int iconResId, String locale,
+            String mode, String extraValue) {
+        return Arrays.hashCode(new Object[] {nameResId, iconResId, locale, mode, extraValue});
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
new file mode 100644
index 0000000..ba16c8a
--- /dev/null
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.KeyEvent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.webkit.WebViewCore.EventHub;
+
+import java.util.ArrayList;
+import java.util.Stack;
+
+/**
+ * This class injects accessibility into WebViews with disabled JavaScript or
+ * WebViews with enabled JavaScript but for which we have no accessibility
+ * script to inject.
+ * </p>
+ * Note: To avoid changes in the framework upon changing the available
+ *       navigation axis, or reordering the navigation axis, or changing
+ *       the key bindings, or defining sequence of actions to be bound to
+ *       a given key this class is navigation axis agnostic. It is only
+ *       aware of one navigation axis which is in fact the default behavior
+ *       of webViews while using the DPAD/TrackBall.
+ * </p>
+ * In general a key binding is a mapping from meta state + key code to
+ * a sequence of actions. For more detail how to specify key bindings refer to
+ * {@link android.provider.Settings.Secure#ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS}.
+ * </p>
+ * The possible actions are invocations to
+ * {@link #setCurrentAxis(int, boolean, String)}, or
+ * {@link #traverseCurrentAxis(int, boolean, String)}
+ * {@link #traverseGivenAxis(int, int, boolean, String)}
+ * {@link #prefromAxisTransition(int, int, boolean, String)}
+ * referred via the values of:
+ * {@link #ACTION_SET_CURRENT_AXIS},
+ * {@link #ACTION_TRAVERSE_CURRENT_AXIS},
+ * {@link #ACTION_TRAVERSE_GIVEN_AXIS},
+ * {@link #ACTION_PERFORM_AXIS_TRANSITION},
+ * respectively.
+ * The arguments for the action invocation are specified as offset
+ * hexademical pairs. Note the last argument of the invocation
+ * should NOT be specified in the binding as it is provided by
+ * this class. For details about the key binding implementation
+ * refer to {@link AccessibilityWebContentKeyBinding}.
+ */
+class AccessibilityInjector {
+    private static final String LOG_TAG = "AccessibilityInjector";
+
+    private static final boolean DEBUG = true;
+
+    private static final int ACTION_SET_CURRENT_AXIS = 0;
+    private static final int ACTION_TRAVERSE_CURRENT_AXIS = 1;
+    private static final int ACTION_TRAVERSE_GIVEN_AXIS = 2;
+    private static final int ACTION_PERFORM_AXIS_TRANSITION = 3;
+
+    // the default WebView behavior abstracted as a navigation axis
+    private static final int NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR = 7;
+
+    // these are the same for all instances so make them process wide
+    private static SparseArray<AccessibilityWebContentKeyBinding> sBindings =
+        new SparseArray<AccessibilityWebContentKeyBinding>();
+
+    // handle to the WebView this injector is associated with.
+    private final WebView mWebView;
+
+    // events scheduled for sending as soon as we receive the selected text
+    private final Stack<AccessibilityEvent> mScheduledEventStack = new Stack<AccessibilityEvent>();
+
+    // the current traversal axis
+    private int mCurrentAxis = 2; // sentence
+
+    // we need to consume the up if we have handled the last down
+    private boolean mLastDownEventHandled;
+
+    // getting two empty selection strings in a row we let the WebView handle the event
+    private boolean mIsLastSelectionStringNull;
+
+    // keep track of last direction
+    private int mLastDirection;
+
+    /**
+     * Creates a new injector associated with a given {@link WebView}.
+     *
+     * @param webView The associated WebView.
+     */
+    public AccessibilityInjector(WebView webView) {
+        mWebView = webView;
+        ensureWebContentKeyBindings();
+    }
+
+    /**
+     * Processes a key down <code>event</code>.
+     *
+     * @return True if the event was processed.
+     */
+    public boolean onKeyEvent(KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_UP) {
+            return mLastDownEventHandled;
+        }
+
+        mLastDownEventHandled = false;
+
+        int key = event.getMetaState() << AccessibilityWebContentKeyBinding.OFFSET_META_STATE |
+            event.getKeyCode() << AccessibilityWebContentKeyBinding.OFFSET_KEY_CODE;
+
+        AccessibilityWebContentKeyBinding binding = sBindings.get(key);
+        if (binding == null) {
+            return false;
+        }
+
+        for (int i = 0, count = binding.getActionCount(); i < count; i++) {
+            int actionCode = binding.getActionCode(i);
+            String contentDescription = Integer.toHexString(binding.getAction(i));
+            switch (actionCode) {
+                case ACTION_SET_CURRENT_AXIS:
+                    int axis = binding.getFirstArgument(i);
+                    boolean sendEvent = (binding.getSecondArgument(i) == 1);
+                    setCurrentAxis(axis, sendEvent, contentDescription);
+                    mLastDownEventHandled = true;
+                    break;
+                case ACTION_TRAVERSE_CURRENT_AXIS:
+                    int direction = binding.getFirstArgument(i);
+                    // on second null selection string in same direction => WebView handle the event
+                    if (direction == mLastDirection && mIsLastSelectionStringNull) {
+                        mLastDirection = direction;
+                        mIsLastSelectionStringNull = false;
+                        return false;
+                    }
+                    mLastDirection = direction;
+                    sendEvent = (binding.getSecondArgument(i) == 1);
+                    mLastDownEventHandled = traverseCurrentAxis(direction, sendEvent,
+                            contentDescription);
+                    break;
+                case ACTION_TRAVERSE_GIVEN_AXIS:
+                    direction = binding.getFirstArgument(i);
+                    // on second null selection string in same direction => WebView handle the event
+                    if (direction == mLastDirection && mIsLastSelectionStringNull) {
+                        mLastDirection = direction;
+                        mIsLastSelectionStringNull = false;
+                        return false;
+                    }
+                    mLastDirection = direction;
+                    axis =  binding.getSecondArgument(i);
+                    sendEvent = (binding.getThirdArgument(i) == 1);
+                    traverseGivenAxis(direction, axis, sendEvent, contentDescription);
+                    mLastDownEventHandled = true;
+                    break;
+                case ACTION_PERFORM_AXIS_TRANSITION:
+                    int fromAxis = binding.getFirstArgument(i);
+                    int toAxis = binding.getSecondArgument(i);
+                    sendEvent = (binding.getThirdArgument(i) == 1);
+                    prefromAxisTransition(fromAxis, toAxis, sendEvent, contentDescription);
+                    mLastDownEventHandled = true;
+                    break;
+                default:
+                    Log.w(LOG_TAG, "Unknown action code: " + actionCode);
+            }
+        }
+
+        return mLastDownEventHandled;
+    }
+
+    /**
+     * Set the current navigation axis which will be used while
+     * calling {@link #traverseCurrentAxis(int, boolean, String)}.
+     *
+     * @param axis The axis to set.
+     * @param sendEvent Whether to send an accessibility event to
+     *        announce the change.
+     */
+    private void setCurrentAxis(int axis, boolean sendEvent, String contentDescription) {
+        mCurrentAxis = axis;
+        if (sendEvent) {
+            AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent();
+            event.getText().add(String.valueOf(axis));
+            event.setContentDescription(contentDescription);
+            sendAccessibilityEvent(event);
+        }
+    }
+
+    /**
+     * Performs conditional transition one axis to another.
+     *
+     * @param fromAxis The axis which must be the current for the transition to occur.
+     * @param toAxis The axis to which to transition.
+     * @param sendEvent Flag if to send an event to announce successful transition.
+     * @param contentDescription A description of the performed action.
+     */
+    private void prefromAxisTransition(int fromAxis, int toAxis, boolean sendEvent,
+            String contentDescription) {
+        if (mCurrentAxis == fromAxis) {
+            setCurrentAxis(toAxis, sendEvent, contentDescription);
+        }
+    }
+
+    /**
+     * Traverse the document along the current navigation axis.
+     *
+     * @param direction The direction of traversal.
+     * @param sendEvent Whether to send an accessibility event to
+     *        announce the change.
+     * @param contentDescription A description of the performed action.
+     * @see #setCurrentAxis(int, boolean, String)
+     */
+    private boolean traverseCurrentAxis(int direction, boolean sendEvent,
+            String contentDescription) {
+        return traverseGivenAxis(direction, mCurrentAxis, sendEvent, contentDescription);
+    }
+
+    /**
+     * Traverse the document along the given navigation axis.
+     *
+     * @param direction The direction of traversal.
+     * @param axis The axis along which to traverse.
+     * @param sendEvent Whether to send an accessibility event to
+     *        announce the change.
+     * @param contentDescription A description of the performed action.
+     */
+    private boolean traverseGivenAxis(int direction, int axis, boolean sendEvent,
+            String contentDescription) {
+        // if the axis is the default let WebView handle the event
+        if (axis == NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR) {
+            return false;
+        }
+        WebViewCore webViewCore = mWebView.getWebViewCore();
+        if (webViewCore != null) {
+            AccessibilityEvent event = null;
+            if (sendEvent) {
+                event = getPartialyPopulatedAccessibilityEvent();
+                // the text will be set upon receiving the selection string
+                event.setContentDescription(contentDescription);
+            }
+            mScheduledEventStack.push(event);
+            webViewCore.sendMessage(EventHub.MODIFY_SELECTION, direction, axis);
+        }
+        return true;
+    }
+
+    /**
+     * Called when the <code>selectionString</code> has changed.
+     */
+    public void onSelectionStringChange(String selectionString) {
+        mIsLastSelectionStringNull = (selectionString == null);
+        AccessibilityEvent event = mScheduledEventStack.pop();
+        if (event != null) {
+            event.getText().add(selectionString);
+            sendAccessibilityEvent(event);
+        }
+    }
+
+    /**
+     * Sends an {@link AccessibilityEvent}.
+     *
+     * @param event The event to send.
+     */
+    private void sendAccessibilityEvent(AccessibilityEvent event) {
+        if (DEBUG) {
+            Log.d(LOG_TAG, "Dispatching: " + event);
+        }
+        AccessibilityManager.getInstance(mWebView.getContext()).sendAccessibilityEvent(event);
+    }
+
+    /**
+     * @return An accessibility event whose members are populated except its
+     *         text and content description.
+     */
+    private AccessibilityEvent getPartialyPopulatedAccessibilityEvent() {
+        AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SELECTED);
+        event.setClassName(mWebView.getClass().getName());
+        event.setPackageName(mWebView.getContext().getPackageName());
+        event.setEnabled(mWebView.isEnabled());
+        return event;
+    }
+
+    /**
+     * Ensures that the Web content key bindings are loaded.
+     */
+    private void ensureWebContentKeyBindings() {
+        if (sBindings.size() > 0) {
+            return;
+        }
+
+        String webContentKeyBindingsString  = Settings.Secure.getString(
+                mWebView.getContext().getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS);
+
+        SimpleStringSplitter semiColonSplitter = new SimpleStringSplitter(';');
+        semiColonSplitter.setString(webContentKeyBindingsString);
+
+        ArrayList<AccessibilityWebContentKeyBinding> bindings =
+            new ArrayList<AccessibilityWebContentKeyBinding>();
+
+        while (semiColonSplitter.hasNext()) {
+            String bindingString = semiColonSplitter.next();
+            if (TextUtils.isEmpty(bindingString)) {
+                Log.e(LOG_TAG, "Malformed Web content key binding: "
+                        + webContentKeyBindingsString);
+                continue;
+            }
+            String[] keyValueArray = bindingString.split("=");
+            if (keyValueArray.length != 2) {
+                Log.e(LOG_TAG, "Disregarding malformed Web content key binding: " +
+                        bindingString);
+                continue;
+            }
+            try {
+                SimpleStringSplitter colonSplitter = new SimpleStringSplitter(':');//remove
+                int key = Integer.decode(keyValueArray[0].trim());
+                String[] actionStrings = keyValueArray[1].split(":");
+                int[] actions = new int[actionStrings.length];
+                for (int i = 0, count = actions.length; i < count; i++) {
+                    actions[i] = Integer.decode(actionStrings[i].trim());
+                }
+
+                bindings.add(new AccessibilityWebContentKeyBinding(key, actions));
+            } catch (NumberFormatException nfe) {
+                Log.e(LOG_TAG, "Disregarding malformed key binding: " + bindingString);
+            }
+        }
+
+        for (AccessibilityWebContentKeyBinding binding : bindings) {
+            sBindings.put(binding.getKey(), binding);
+        }
+    }
+
+    /**
+     * Represents a web content key-binding.
+     */
+    private class AccessibilityWebContentKeyBinding {
+
+        private static final int OFFSET_META_STATE = 0x00000010;
+
+        private static final int MASK_META_STATE = 0xFFFF0000;
+
+        private static final int OFFSET_KEY_CODE = 0x00000000;
+
+        private static final int MASK_KEY_CODE = 0x0000FFFF;
+
+        private static final int OFFSET_ACTION = 0x00000018;
+
+        private static final int MASK_ACTION = 0xFF000000;
+
+        private static final int OFFSET_FIRST_ARGUMENT = 0x00000010;
+
+        private static final int MASK_FIRST_ARGUMENT = 0x00FF0000;
+
+        private static final int OFFSET_SECOND_ARGUMENT = 0x00000008;
+
+        private static final int MASK_SECOND_ARGUMENT = 0x0000FF00;
+
+        private static final int OFFSET_THIRD_ARGUMENT = 0x00000000;
+
+        private static final int MASK_THIRD_ARGUMENT = 0x000000FF;
+
+        private int mKey;
+
+        private int [] mActionSequence;
+
+        /**
+         * @return The binding key with key code and meta state.
+         *
+         * @see #MASK_KEY_CODE
+         * @see #MASK_META_STATE
+         * @see #OFFSET_KEY_CODE
+         * @see #OFFSET_META_STATE
+         */
+        public int getKey() {
+            return mKey;
+        }
+
+        /**
+         * @return The key code of the binding key.
+         */
+        public int getKeyCode() {
+            return (mKey & MASK_KEY_CODE) >> OFFSET_KEY_CODE;
+        }
+
+        /**
+         * @return The meta state of the binding key.
+         */
+        public int getMetaState() {
+            return (mKey & MASK_META_STATE) >> OFFSET_META_STATE;
+        }
+
+        /**
+         * @return The number of actions in the key binding.
+         */
+        public int getActionCount() {
+            return mActionSequence.length;
+        }
+
+        /**
+         * @param index The action for a given action <code>index</code>.
+         */
+        public int getAction(int index) {
+            return mActionSequence[index];
+        }
+
+        /**
+         * @param index The action code for a given action <code>index</code>.
+         */
+        public int getActionCode(int index) {
+            return (mActionSequence[index] & MASK_ACTION) >> OFFSET_ACTION;
+        }
+
+        /**
+         * @param index The first argument for a given action <code>index</code>.
+         */
+        public int getFirstArgument(int index) {
+            return (mActionSequence[index] & MASK_FIRST_ARGUMENT) >> OFFSET_FIRST_ARGUMENT;
+        }
+
+        /**
+         * @param index The second argument for a given action <code>index</code>.
+         */
+        public int getSecondArgument(int index) {
+            return (mActionSequence[index] & MASK_SECOND_ARGUMENT) >> OFFSET_SECOND_ARGUMENT;
+        }
+
+        /**
+         * @param index The third argument for a given action <code>index</code>.
+         */
+        public int getThirdArgument(int index) {
+            return (mActionSequence[index] & MASK_THIRD_ARGUMENT) >> OFFSET_THIRD_ARGUMENT;
+        }
+
+        /**
+         * Creates a new instance.
+         * @param key The key for the binding (key and meta state)
+         * @param actionSequence The sequence of action for the binding.
+         * @see #getKey()
+         */
+        public AccessibilityWebContentKeyBinding(int key, int[] actionSequence) {
+            mKey = key;
+            mActionSequence = actionSequence;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("key: ");
+            builder.append(getKey());
+            builder.append(", metaState: ");
+            builder.append(getMetaState());
+            builder.append(", keyCode: ");
+            builder.append(getKeyCode());
+            builder.append(", actions[");
+            for (int i = 0, count = getActionCount(); i < count; i++) {
+                builder.append("{actionCode");
+                builder.append(i);
+                builder.append(": ");
+                builder.append(getActionCode(i));
+                builder.append(", firstArgument: ");
+                builder.append(getFirstArgument(i));
+                builder.append(", secondArgument: ");
+                builder.append(getSecondArgument(i));
+                builder.append(", thirdArgument: ");
+                builder.append(getThirdArgument(i));
+                builder.append("}");
+            }
+            builder.append("]");
+            return builder.toString();
+        }
+    }
+}
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 84deeb0..cce7914 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -38,6 +38,8 @@
 
 import junit.framework.Assert;
 
+import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
 import java.net.URLEncoder;
@@ -295,6 +297,18 @@
     }
 
     /**
+     * Saves the contents of the frame as a web archive.
+     *
+     * @param basename The filename where the archive should be placed.
+     * @param autoname If false, takes filename to be a file. If true, filename
+     *                 is assumed to be a directory in which a filename will be
+     *                 chosen according to the url of the current page.
+     */
+    /* package */ String saveWebArchive(String basename, boolean autoname) {
+        return nativeSaveWebArchive(basename, autoname);
+    }
+
+    /**
      * Go back or forward the number of steps given.
      * @param steps A negative or positive number indicating the direction
      *              and number of steps to move.
@@ -455,7 +469,7 @@
                             .getCurrentItem();
                     if (item != null) {
                         WebAddress uri = new WebAddress(item.getUrl());
-                        String schemePlusHost = uri.mScheme + uri.mHost;
+                        String schemePlusHost = uri.getScheme() + uri.getHost();
                         String[] up =
                                 mDatabase.getUsernamePassword(schemePlusHost);
                         if (up != null && up[0] != null) {
@@ -514,12 +528,21 @@
     private native String externalRepresentation();
 
     /**
-     * Retrieves the visual text of the current frame, puts it as the object for
+     * Retrieves the visual text of the frames, puts it as the object for
      * the message and sends the message.
      * @param callback the message to use to send the visual text
      */
     public void documentAsText(Message callback) {
-        callback.obj = documentAsText();;
+        StringBuilder text = new StringBuilder();
+        if (callback.arg1 != 0) {
+            // Dump top frame as text.
+            text.append(documentAsText());
+        }
+        if (callback.arg2 != 0) {
+            // Dump child frames as text.
+            text.append(childFramesAsText());
+        }
+        callback.obj = text.toString();
         callback.sendToTarget();
     }
 
@@ -528,6 +551,11 @@
      */
     private native String documentAsText();
 
+    /**
+     * Return the text drawn on the child frames as a string
+     */
+    private native String childFramesAsText();
+
     /*
      * This method is called by WebCore to inform the frame that
      * the Javascript window object has been cleared.
@@ -621,6 +649,110 @@
     }
 
     /**
+     * Called by JNI.
+     * Read from an InputStream into a supplied byte[]
+     * This method catches any exceptions so they don't crash the JVM.
+     * @param inputStream InputStream to read from.
+     * @param output Bytearray that gets the output.
+     * @return the number of bytes read, or -i if then end of stream has been reached
+     */
+    private static int readFromStream(InputStream inputStream, byte[] output) {
+        try {
+            return inputStream.read(output);
+        } catch(java.io.IOException e) {
+            // If we get an exception, return end of stream
+            return -1;
+        }
+    }
+
+    /**
+     * Get the InputStream for an Android resource
+     * There are three different kinds of android resources:
+     * - file:///android_res
+     * - file:///android_asset
+     * - content://
+     * @param url The url to load.
+     * @return An InputStream to the android resource
+     */
+    private InputStream inputStreamForAndroidResource(String url) {
+        // This list needs to be kept in sync with the list in
+        // external/webkit/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp
+        final String ANDROID_ASSET = "file:///android_asset/";
+        final String ANDROID_RESOURCE = "file:///android_res/";
+        final String ANDROID_CONTENT = "content:";
+
+        // file:///android_res
+        if (url.startsWith(ANDROID_RESOURCE)) {
+            url = url.replaceFirst(ANDROID_RESOURCE, "");
+            if (url == null || url.length() == 0) {
+                Log.e(LOGTAG, "url has length 0 " + url);
+                return null;
+            }
+            int slash = url.indexOf('/');
+            int dot = url.indexOf('.', slash);
+            if (slash == -1 || dot == -1) {
+                Log.e(LOGTAG, "Incorrect res path: " + url);
+                return null;
+            }
+            String subClassName = url.substring(0, slash);
+            String fieldName = url.substring(slash + 1, dot);
+            String errorMsg = null;
+            try {
+                final Class<?> d = mContext.getApplicationContext()
+                        .getClassLoader().loadClass(
+                                mContext.getPackageName() + ".R$"
+                                        + subClassName);
+                final java.lang.reflect.Field field = d.getField(fieldName);
+                final int id = field.getInt(null);
+                TypedValue value = new TypedValue();
+                mContext.getResources().getValue(id, value, true);
+                if (value.type == TypedValue.TYPE_STRING) {
+                    return mContext.getAssets().openNonAsset(
+                            value.assetCookie, value.string.toString(),
+                            AssetManager.ACCESS_STREAMING);
+                } else {
+                    // Old stack only supports TYPE_STRING for res files
+                    Log.e(LOGTAG, "not of type string: " + url);
+                    return null;
+                }
+            } catch (Exception e) {
+                Log.e(LOGTAG, "Exception: " + url);
+                return null;
+            }
+
+        // file:///android_asset
+        } else if (url.startsWith(ANDROID_ASSET)) {
+            url = url.replaceFirst(ANDROID_ASSET, "");
+            try {
+                AssetManager assets = mContext.getAssets();
+                return assets.open(url, AssetManager.ACCESS_STREAMING);
+            } catch (IOException e) {
+                return null;
+            }
+
+        // content://
+        } else if (url.startsWith(ANDROID_CONTENT)) {
+            try {
+                // Strip off mimetype, for compatibility with ContentLoader.java
+                // If we don't do this, we can fail to load Gmail attachments,
+                // because the URL being loaded doesn't exactly match the URL we
+                // have permission to read.
+                int mimeIndex = url.lastIndexOf('?');
+                if (mimeIndex != -1) {
+                    url = url.substring(0, mimeIndex);
+                }
+                Uri uri = Uri.parse(url);
+                return mContext.getContentResolver().openInputStream(uri);
+            } catch (Exception e) {
+                Log.e(LOGTAG, "Exception: " + url);
+                return null;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * Start loading a resource.
      * @param loaderHandle The native ResourceLoader that is the target of the
      *                     data.
@@ -668,7 +800,7 @@
                     }
                     WebAddress uri = new WebAddress(mCallbackProxy
                             .getBackForwardList().getCurrentItem().getUrl());
-                    String schemePlusHost = uri.mScheme + uri.mHost;
+                    String schemePlusHost = uri.getScheme() + uri.getHost();
                     String[] ret = getUsernamePassword();
                     // Has the user entered a username/password pair and is
                     // there some POST data
@@ -846,6 +978,7 @@
     private static final int FILE_UPLOAD_LABEL = 4;
     private static final int RESET_LABEL = 5;
     private static final int SUBMIT_LABEL = 6;
+    private static final int FILE_UPLOAD_NO_FILE_CHOSEN = 7;
 
     String getRawResFilename(int id) {
         int resid;
@@ -875,6 +1008,10 @@
                 return mContext.getResources().getString(
                         com.android.internal.R.string.submit);
 
+            case FILE_UPLOAD_NO_FILE_CHOSEN:
+                return mContext.getResources().getString(
+                        com.android.internal.R.string.no_file_chosen);
+
             default:
                 Log.e(LOGTAG, "getRawResFilename got incompatible resource ID");
                 return "";
@@ -897,6 +1034,54 @@
         return mContext.getResources().getDisplayMetrics().density;
     }
 
+    /**
+     * Called by JNI when the native HTTP stack gets an authentication request.
+     *
+     * We delegate the request to CallbackProxy, and route its response to
+     * {@link #nativeAuthenticationProceed(int, String, String)} or
+     * {@link #nativeAuthenticationCancel(int)}.
+     *
+     * We don't care what thread the callback is invoked on. All threading is
+     * handled on the C++ side, because the WebKit thread may be blocked on a
+     * synchronous call and unable to pump our MessageQueue.
+     */
+    private void didReceiveAuthenticationChallenge(
+            final int handle, String host, String realm, final boolean useCachedCredentials) {
+
+        HttpAuthHandler handler = new HttpAuthHandler() {
+
+            @Override
+            public boolean useHttpAuthUsernamePassword() {
+                return useCachedCredentials;
+            }
+
+            @Override
+            public void proceed(String username, String password) {
+                nativeAuthenticationProceed(handle, username, password);
+            }
+
+            @Override
+            public void cancel() {
+                nativeAuthenticationCancel(handle);
+            }
+        };
+        mCallbackProxy.onReceivedHttpAuthRequest(handler, host, realm);
+    }
+
+    /**
+     * Called by JNI when the native HTTP stack needs to download a file.
+     *
+     * We delegate the request to CallbackProxy, which owns the current app's
+     * DownloadListener.
+     */
+    private void downloadStart(String url, String userAgent,
+            String contentDisposition, String mimeType, long contentLength) {
+        mimeType = MimeTypeMap.getSingleton().remapGenericMimeType(
+                mimeType, url, contentDisposition);
+        mCallbackProxy.onDownloadStart(url, userAgent,
+                contentDisposition, mimeType, contentLength);
+    }
+
     //==========================================================================
     // native functions
     //==========================================================================
@@ -1010,5 +1195,10 @@
      */
     private native HashMap getFormTextData();
 
+    private native String nativeSaveWebArchive(String basename, boolean autoname);
+
     private native void nativeOrientationChanged(int orientation);
+
+    private native void nativeAuthenticationProceed(int handle, String username, String password);
+    private native void nativeAuthenticationCancel(int handle);
 }
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index d65c106..0078e7a 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
@@ -41,6 +42,7 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.HashMap;
+import java.util.Map;
 
 /**
  * This class is a proxy class for handling WebCore -> UI thread messaging. All
@@ -112,6 +114,7 @@
     private static final int ADD_HISTORY_ITEM                    = 135;
     private static final int HISTORY_INDEX_CHANGED               = 136;
     private static final int AUTH_CREDENTIALS                    = 137;
+    private static final int SET_INSTALLABLE_WEBAPP              = 138;
 
     // Message triggered by the client to resume execution
     private static final int NOTIFY                              = 200;
@@ -253,17 +256,10 @@
         // 32-bit reads and writes.
         switch (msg.what) {
             case PAGE_STARTED:
-                // every time we start a new page, we want to reset the
-                // WebView certificate:
-                // if the new site is secure, we will reload it and get a
-                // new certificate set;
-                // if the new site is not secure, the certificate must be
-                // null, and that will be the case
-                mWebView.setCertificate(null);
+                String startedUrl = msg.getData().getString("url");
+                mWebView.onPageStarted(startedUrl);
                 if (mWebViewClient != null) {
-                    mWebViewClient.onPageStarted(mWebView,
-                            msg.getData().getString("url"),
-                            (Bitmap) msg.obj);
+                    mWebViewClient.onPageStarted(mWebView, startedUrl, (Bitmap) msg.obj);
                 }
                 break;
 
@@ -500,18 +496,29 @@
                     String url = msg.getData().getString("url");
                     if (!mWebChromeClient.onJsAlert(mWebView, url, message,
                             res)) {
+                        if (!canShowAlertDialog()) {
+                            res.cancel();
+                            res.setReady();
+                            break;
+                        }
                         new AlertDialog.Builder(mContext)
                                 .setTitle(getJsDialogTitle(url))
                                 .setMessage(message)
                                 .setPositiveButton(R.string.ok,
-                                        new AlertDialog.OnClickListener() {
+                                        new DialogInterface.OnClickListener() {
                                             public void onClick(
                                                     DialogInterface dialog,
                                                     int which) {
                                                 res.confirm();
                                             }
                                         })
-                                .setCancelable(false)
+                                .setOnCancelListener(
+                                        new DialogInterface.OnCancelListener() {
+                                            public void onCancel(
+                                                    DialogInterface dialog) {
+                                                res.cancel();
+                                            }
+                                        })
                                 .show();
                     }
                     res.setReady();
@@ -525,6 +532,11 @@
                     String url = msg.getData().getString("url");
                     if (!mWebChromeClient.onJsConfirm(mWebView, url, message,
                             res)) {
+                        if (!canShowAlertDialog()) {
+                            res.cancel();
+                            res.setReady();
+                            break;
+                        }
                         new AlertDialog.Builder(mContext)
                                 .setTitle(getJsDialogTitle(url))
                                 .setMessage(message)
@@ -565,6 +577,11 @@
                     String url = msg.getData().getString("url");
                     if (!mWebChromeClient.onJsPrompt(mWebView, url, message,
                                 defaultVal, res)) {
+                        if (!canShowAlertDialog()) {
+                            res.cancel();
+                            res.setReady();
+                            break;
+                        }
                         final LayoutInflater factory = LayoutInflater
                                 .from(mContext);
                         final View view = factory.inflate(R.layout.js_prompt,
@@ -616,6 +633,11 @@
                     String url = msg.getData().getString("url");
                     if (!mWebChromeClient.onJsBeforeUnload(mWebView, url,
                             message, res)) {
+                        if (!canShowAlertDialog()) {
+                            res.cancel();
+                            res.setReady();
+                            break;
+                        }
                         final String m = mContext.getString(
                                 R.string.js_dialog_before_unload, message);
                         new AlertDialog.Builder(mContext)
@@ -676,6 +698,9 @@
                 break;
 
             case ADD_MESSAGE_TO_CONSOLE:
+                if (mWebChromeClient == null) {
+                    break;
+                }
                 String message = msg.getData().getString("message");
                 String sourceID = msg.getData().getString("sourceID");
                 int lineNumber = msg.getData().getInt("lineNumber");
@@ -725,7 +750,8 @@
 
             case OPEN_FILE_CHOOSER:
                 if (mWebChromeClient != null) {
-                    mWebChromeClient.openFileChooser((UploadFile) msg.obj);
+                    UploadFileMessageData data = (UploadFileMessageData)msg.obj;
+                    mWebChromeClient.openFileChooser(data.getUploadFile(), data.getAcceptType());
                 }
                 break;
 
@@ -750,6 +776,11 @@
                 mWebView.setHttpAuthUsernamePassword(
                         host, realm, username, password);
                 break;
+            case SET_INSTALLABLE_WEBAPP:
+                if (mWebChromeClient != null) {
+                    mWebChromeClient.setInstallableWebApp();
+                }
+                break;
         }
     }
 
@@ -1087,10 +1118,15 @@
     public void onProgressChanged(int newProgress) {
         // Synchronize so that mLatestProgress is up-to-date.
         synchronized (this) {
-            if (mWebChromeClient == null || mLatestProgress == newProgress) {
+            // update mLatestProgress even mWebChromeClient is null as
+            // WebView.getProgress() needs it
+            if (mLatestProgress == newProgress) {
                 return;
             }
             mLatestProgress = newProgress;
+            if (mWebChromeClient == null) {
+                return;
+            }
             if (!mProgressUpdatePending) {
                 sendEmptyMessage(PROGRESS);
                 mProgressUpdatePending = true;
@@ -1172,9 +1208,7 @@
         // for null.
         WebHistoryItem i = mBackForwardList.getCurrentItem();
         if (i != null) {
-            if (precomposed || i.getTouchIconUrl() == null) {
-                i.setTouchIconUrl(url);
-            }
+            i.setTouchIconUrl(url, precomposed);
         }
         // Do an unsynchronized quick check to avoid posting if no callback has
         // been set.
@@ -1432,6 +1466,24 @@
         sendMessage(msg);
     }
 
+    private static class UploadFileMessageData {
+        private UploadFile mCallback;
+        private String mAcceptType;
+
+        public UploadFileMessageData(UploadFile uploadFile, String acceptType) {
+            mCallback = uploadFile;
+            mAcceptType = acceptType;
+        }
+
+        public UploadFile getUploadFile() {
+            return mCallback;
+        }
+
+        public String getAcceptType() {
+            return mAcceptType;
+        }
+    }
+
     private class UploadFile implements ValueCallback<Uri> {
         private Uri mValue;
         public void onReceiveValue(Uri value) {
@@ -1448,13 +1500,14 @@
     /**
      * Called by WebViewCore to open a file chooser.
      */
-    /* package */ Uri openFileChooser() {
+    /* package */ Uri openFileChooser(String acceptType) {
         if (mWebChromeClient == null) {
             return null;
         }
         Message myMessage = obtainMessage(OPEN_FILE_CHOOSER);
         UploadFile uploadFile = new UploadFile();
-        myMessage.obj = uploadFile;
+        UploadFileMessageData data = new UploadFileMessageData(uploadFile, acceptType);
+        myMessage.obj = data;
         synchronized (this) {
             sendMessage(myMessage);
             try {
@@ -1483,4 +1536,21 @@
         Message msg = obtainMessage(HISTORY_INDEX_CHANGED, index, 0, item);
         sendMessage(msg);
     }
+
+    void setInstallableWebApp() {
+        if (mWebChromeClient == null) {
+            return;
+        }
+        sendMessage(obtainMessage(SET_INSTALLABLE_WEBAPP));
+    }
+
+    boolean canShowAlertDialog() {
+        // We can only display the alert dialog if mContext is
+        // an Activity context.
+        // FIXME: Should we display dialogs if mContext does
+        // not have the window focus (e.g. if the user is viewing
+        // another Activity when the alert should be displayed?
+        // See bug 3166409
+        return mContext instanceof Activity;
+    }
 }
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index ac20791..8691714 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -98,6 +98,10 @@
 
     private boolean mAcceptCookie = true;
 
+    // TODO: Remove this if/when we permanently switch to the Chromium HTTP stack
+    // http:/b/3118772
+    private static Boolean sUseChromiumHttpStack;
+
     /**
      * This contains a list of 2nd-level domains that aren't allowed to have
      * wildcards when combined with country-codes. For example: [.co.uk].
@@ -257,11 +261,23 @@
         return sRef;
     }
 
+    private static boolean useChromiumHttpStack() {
+        if (sUseChromiumHttpStack == null) {
+            sUseChromiumHttpStack = nativeUseChromiumHttpStack();
+        }
+        return sUseChromiumHttpStack;
+    }
+
     /**
      * Control whether cookie is enabled or disabled
      * @param accept TRUE if accept cookie
      */
     public synchronized void setAcceptCookie(boolean accept) {
+        if (useChromiumHttpStack()) {
+            nativeSetAcceptCookie(accept);
+            return;
+        }
+
         mAcceptCookie = accept;
     }
 
@@ -270,6 +286,10 @@
      * @return TRUE if accept cookie
      */
     public synchronized boolean acceptCookie() {
+        if (useChromiumHttpStack()) {
+            return nativeAcceptCookie();
+        }
+
         return mAcceptCookie;
     }
 
@@ -281,6 +301,11 @@
      * @param value The value for set-cookie: in http response header
      */
     public void setCookie(String url, String value) {
+        if (useChromiumHttpStack()) {
+            nativeSetCookie(url, value);
+            return;
+        }
+
         WebAddress uri;
         try {
             uri = new WebAddress(url);
@@ -359,7 +384,7 @@
                     // negative means far future
                     if (cookie.expires < 0 || cookie.expires > now) {
                         // secure cookies can't be overwritten by non-HTTPS url
-                        if (!cookieEntry.secure || HTTPS.equals(uri.mScheme)) {
+                        if (!cookieEntry.secure || HTTPS.equals(uri.getScheme())) {
                             cookieEntry.value = cookie.value;
                             cookieEntry.expires = cookie.expires;
                             cookieEntry.secure = cookie.secure;
@@ -407,6 +432,10 @@
      * @return The cookies in the format of NAME=VALUE [; NAME=VALUE]
      */
     public String getCookie(String url) {
+        if (useChromiumHttpStack()) {
+            return nativeGetCookie(url);
+        }
+
         WebAddress uri;
         try {
             uri = new WebAddress(url);
@@ -444,7 +473,7 @@
         }
 
         long now = System.currentTimeMillis();
-        boolean secure = HTTPS.equals(uri.mScheme);
+        boolean secure = HTTPS.equals(uri.getScheme());
         Iterator<Cookie> iter = cookieList.iterator();
 
         SortedSet<Cookie> cookieSet = new TreeSet<Cookie>(COMPARATOR);
@@ -498,6 +527,11 @@
      * Remove all session cookies, which are cookies without expiration date
      */
     public void removeSessionCookie() {
+        if (useChromiumHttpStack()) {
+            nativeRemoveSessionCookie();
+            return;
+        }
+
         final Runnable clearCache = new Runnable() {
             public void run() {
                 synchronized(CookieManager.this) {
@@ -524,6 +558,11 @@
      * Remove all cookies
      */
     public void removeAllCookie() {
+        if (useChromiumHttpStack()) {
+            nativeRemoveAllCookie();
+            return;
+        }
+
         final Runnable clearCache = new Runnable() {
             public void run() {
                 synchronized(CookieManager.this) {
@@ -540,6 +579,10 @@
      *  Return true if there are stored cookies.
      */
     public synchronized boolean hasCookies() {
+        if (useChromiumHttpStack()) {
+            return nativeHasCookies();
+        }
+
         return CookieSyncManager.getInstance().hasCookies();
     }
 
@@ -547,6 +590,11 @@
      * Remove all expired cookies
      */
     public void removeExpiredCookie() {
+        if (useChromiumHttpStack()) {
+            nativeRemoveExpiredCookie();
+            return;
+        }
+
         final Runnable clearCache = new Runnable() {
             public void run() {
                 synchronized(CookieManager.this) {
@@ -692,7 +740,7 @@
      *          ended with "/"
      */
     private String[] getHostAndPath(WebAddress uri) {
-        if (uri.mHost != null && uri.mPath != null) {
+        if (uri.getHost() != null && uri.getPath() != null) {
 
             /*
              * The domain (i.e. host) portion of the cookie is supposed to be
@@ -703,12 +751,12 @@
              * See: http://www.ieft.org/rfc/rfc2965.txt (Section 3.3.3)
              */
             String[] ret = new String[2];
-            ret[0] = uri.mHost.toLowerCase();
-            ret[1] = uri.mPath;
+            ret[0] = uri.getHost().toLowerCase();
+            ret[1] = uri.getPath();
 
             int index = ret[0].indexOf(PERIOD);
             if (index == -1) {
-                if (uri.mScheme.equalsIgnoreCase("file")) {
+                if (uri.getScheme().equalsIgnoreCase("file")) {
                     // There is a potential bug where a local file path matches
                     // another file in the local web server directory. Still
                     // "localhost" is the best pseudo domain name.
@@ -1016,4 +1064,15 @@
         }
         return ret;
     }
+
+    // Native functions
+    private static native boolean nativeUseChromiumHttpStack();
+    private static native boolean nativeAcceptCookie();
+    private static native String nativeGetCookie(String url);
+    private static native boolean nativeHasCookies();
+    private static native void nativeRemoveAllCookie();
+    private static native void nativeRemoveExpiredCookie();
+    private static native void nativeRemoveSessionCookie();
+    private static native void nativeSetAcceptCookie(boolean accept);
+    private static native void nativeSetCookie(String url, String value);
 }
diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java
index abe9178..8b76a3b 100644
--- a/core/java/android/webkit/CookieSyncManager.java
+++ b/core/java/android/webkit/CookieSyncManager.java
@@ -65,6 +65,11 @@
     // time when last update happened
     private long mLastUpdate;
 
+    // Used by the Chromium HTTP stack. Everything else in this class is used only by the Android
+    // Java HTTP stack.
+    private static String sDatabaseDirectory;
+    private static String sCacheDirectory;
+
     private CookieSyncManager(Context context) {
         super(context, "CookieSyncManager");
     }
@@ -77,11 +82,7 @@
      * @return CookieSyncManager
      */
     public static synchronized CookieSyncManager getInstance() {
-        if (sRef == null) {
-            throw new IllegalStateException(
-                    "CookieSyncManager::createInstance() needs to be called "
-                            + "before CookieSyncManager::getInstance()");
-        }
+        checkInstanceIsCreated();
         return sRef;
     }
 
@@ -92,8 +93,11 @@
      */
     public static synchronized CookieSyncManager createInstance(
             Context context) {
+        Context appContext = context.getApplicationContext();
         if (sRef == null) {
-            sRef = new CookieSyncManager(context.getApplicationContext());
+            sRef = new CookieSyncManager(appContext);
+            sDatabaseDirectory = appContext.getDatabasePath("dummy").getParent();
+            sCacheDirectory = appContext.getCacheDir().getAbsolutePath();
         }
         return sRef;
     }
@@ -210,4 +214,30 @@
             }
         }
     }
+
+    private static void checkInstanceIsCreated() {
+        if (sRef == null) {
+            throw new IllegalStateException(
+                    "CookieSyncManager::createInstance() needs to be called "
+                            + "before CookieSyncManager::getInstance()");
+        }
+    }
+
+    /**
+     * Called by JNI. Gets the application's database directory, excluding the trailing slash.
+     * @return String The application's database directory
+     */
+    private static synchronized String getDatabaseDirectory() {
+        checkInstanceIsCreated();
+        return sDatabaseDirectory;
+    }
+
+    /**
+     * Called by JNI. Gets the application's cache directory, excluding the trailing slash.
+     * @return String The application's cache directory
+     */
+    private static synchronized String getCacheDirectory() {
+        checkInstanceIsCreated();
+        return sCacheDirectory;
+    }
 }
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
index dca52f6..8e25395 100644
--- a/core/java/android/webkit/DebugFlags.java
+++ b/core/java/android/webkit/DebugFlags.java
@@ -32,8 +32,6 @@
     public static final boolean CALLBACK_PROXY = false;
     public static final boolean COOKIE_MANAGER = false;
     public static final boolean COOKIE_SYNC_MANAGER = false;
-    public static final boolean DRAG_TRACKER = false;
-    public static final String DRAG_TRACKER_LOGTAG = "skia";
     public static final boolean FRAME_LOADER = false;
     public static final boolean J_WEB_CORE_JAVA_BRIDGE = false;// HIGHLY VERBOSE
     public static final boolean LOAD_LISTENER = false;
diff --git a/core/java/android/webkit/DeviceMotionAndOrientationManager.java b/core/java/android/webkit/DeviceMotionAndOrientationManager.java
new file mode 100644
index 0000000..79b78d8
--- /dev/null
+++ b/core/java/android/webkit/DeviceMotionAndOrientationManager.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+/**
+ * This class is simply a container for the methods used to implement DeviceMotion and
+ * DeviceOrientation, including the mock DeviceOrientationClient for use in LayoutTests.
+ *
+ * This could be part of WebViewCore, but have moved it to its own class to
+ * avoid bloat there.
+ * @hide
+ */
+public final class DeviceMotionAndOrientationManager {
+    private WebViewCore mWebViewCore;
+
+    public DeviceMotionAndOrientationManager(WebViewCore webViewCore) {
+        mWebViewCore = webViewCore;
+    }
+
+    /**
+     * Sets whether the Page for this WebViewCore should use a mock DeviceOrientation
+     * client.
+     */
+    public void useMock() {
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+        nativeUseMock(mWebViewCore);
+    }
+
+    /**
+     * Set the position for the mock DeviceOrientation service for this WebViewCore.
+     */
+    public void setMockOrientation(boolean canProvideAlpha, double alpha, boolean canProvideBeta,
+            double beta, boolean canProvideGamma, double gamma) {
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+        nativeSetMockOrientation(mWebViewCore, canProvideAlpha, alpha, canProvideBeta, beta,
+                canProvideGamma, gamma);
+    }
+
+    // We only provide accelerationIncludingGravity.
+    public void onMotionChange(Double x, Double y, Double z, double interval) {
+        nativeOnMotionChange(mWebViewCore,
+                x != null, x != null ? x.doubleValue() : 0.0,
+                y != null, y != null ? y.doubleValue() : 0.0,
+                z != null, z != null ? z.doubleValue() : 0.0,
+                interval);
+    }
+    public void onOrientationChange(Double alpha, Double beta, Double gamma) {
+        nativeOnOrientationChange(mWebViewCore,
+                alpha != null, alpha != null ? alpha.doubleValue() : 0.0,
+                beta != null, beta != null ? beta.doubleValue() : 0.0,
+                gamma != null, gamma != null ? gamma.doubleValue() : 0.0);
+    }
+
+    // Native functions
+    private static native void nativeUseMock(WebViewCore webViewCore);
+    private static native void nativeSetMockOrientation(WebViewCore webViewCore,
+            boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta,
+            boolean canProvideGamma, double gamma);
+    private static native void nativeOnMotionChange(WebViewCore webViewCore,
+            boolean canProvideX, double x, boolean canProvideY, double y,
+            boolean canProvideZ, double z, double interval);
+    private static native void nativeOnOrientationChange(WebViewCore webViewCore,
+            boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta,
+            boolean canProvideGamma, double gamma);
+}
diff --git a/core/java/android/webkit/DeviceMotionService.java b/core/java/android/webkit/DeviceMotionService.java
new file mode 100755
index 0000000..7d7a0f0
--- /dev/null
+++ b/core/java/android/webkit/DeviceMotionService.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.Message;
+import android.webkit.DeviceMotionAndOrientationManager;
+import java.lang.Runnable;
+import java.util.List;
+
+
+final class DeviceMotionService implements SensorEventListener {
+    private DeviceMotionAndOrientationManager mManager;
+    private boolean mIsRunning;
+    private Handler mHandler;
+    private SensorManager mSensorManager;
+    private Context mContext;
+    private boolean mHaveSentErrorEvent;
+    private Runnable mUpdateRunnable;
+    private float mLastAcceleration[];
+
+    private static final int INTERVAL_MILLIS = 100;
+
+    public DeviceMotionService(DeviceMotionAndOrientationManager manager, Context context) {
+        mManager = manager;
+        assert(mManager != null);
+        mContext = context;
+        assert(mContext != null);
+     }
+
+    public void start() {
+        mIsRunning = true;
+        registerForSensor();
+    }
+
+    public void stop() {
+        mIsRunning = false;
+        stopSendingUpdates();
+        unregisterFromSensor();
+    }
+
+    public void suspend() {
+        if (mIsRunning) {
+            stopSendingUpdates();
+            unregisterFromSensor();
+        }
+    }
+
+    public void resume() {
+        if (mIsRunning) {
+            registerForSensor();
+        }
+    }
+
+    private void sendErrorEvent() {
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+        // The spec requires that each listener receives the error event only once.
+        if (mHaveSentErrorEvent)
+            return;
+        mHaveSentErrorEvent = true;
+        createHandler();
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+                if (mIsRunning) {
+                    // The special case of all nulls is used to signify a failure to get data.
+                    mManager.onMotionChange(null, null, null, 0.0);
+                }
+            }
+        });
+    }
+
+    private void createHandler() {
+        if (mHandler != null) {
+            return;
+        }
+
+        mHandler = new Handler();
+        mUpdateRunnable = new Runnable() {
+            @Override
+            public void run() {
+                mManager.onMotionChange(new Double(mLastAcceleration[0]),
+                        new Double(mLastAcceleration[1]), new Double(mLastAcceleration[2]),
+                        INTERVAL_MILLIS);
+                mHandler.postDelayed(mUpdateRunnable, INTERVAL_MILLIS);
+                // Now that we have successfully sent some data, reset whether we've sent an error.
+                mHaveSentErrorEvent = false;
+            }
+        };
+    }
+
+    private void startSendingUpdates() {
+        createHandler();
+        mUpdateRunnable.run();
+    }
+
+    private void stopSendingUpdates() {
+        mHandler.removeCallbacks(mUpdateRunnable);
+        mLastAcceleration = null;
+    }
+
+    private void registerForSensor() {
+        if (!registerForAccelerometerSensor()) {
+            sendErrorEvent();
+        }
+    }
+
+    private SensorManager getSensorManager() {
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+        if (mSensorManager == null) {
+            mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+        }
+        return mSensorManager;
+    }
+
+    private boolean registerForAccelerometerSensor() {
+        List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_ACCELEROMETER);
+        if (sensors.isEmpty()) {
+            return false;
+        }
+        createHandler();
+        // TODO: Consider handling multiple sensors.
+        return getSensorManager().registerListener(
+                this, sensors.get(0), SensorManager.SENSOR_DELAY_UI, mHandler);
+    }
+
+    private void unregisterFromSensor() {
+        getSensorManager().unregisterListener(this);
+    }
+
+    /**
+     * SensorEventListener implementation.
+     * Callbacks happen on the thread on which we registered - the WebCore thread.
+     */
+    public void onSensorChanged(SensorEvent event) {
+        assert(event.values.length == 3);
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+        assert(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER);
+
+        boolean firstData = mLastAcceleration == null;
+        mLastAcceleration = event.values;
+        if (firstData) {
+            startSendingUpdates();
+        }
+    }
+
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+    }
+}
diff --git a/core/java/android/webkit/DeviceOrientationService.java b/core/java/android/webkit/DeviceOrientationService.java
new file mode 100755
index 0000000..f3c0576
--- /dev/null
+++ b/core/java/android/webkit/DeviceOrientationService.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.webkit.DeviceMotionAndOrientationManager;
+import java.lang.Runnable;
+import java.util.List;
+
+
+final class DeviceOrientationService implements SensorEventListener {
+    // The gravity vector expressed in the body frame.
+    private float[] mGravityVector;
+    // The geomagnetic vector expressed in the body frame.
+    private float[] mMagneticFieldVector;
+
+    private DeviceMotionAndOrientationManager mManager;
+    private boolean mIsRunning;
+    private Handler mHandler;
+    private SensorManager mSensorManager;
+    private Context mContext;
+    private Double mAlpha;
+    private Double mBeta;
+    private Double mGamma;
+    private boolean mHaveSentErrorEvent;
+
+    private static final double DELTA_DEGRESS = 1.0;
+
+    public DeviceOrientationService(DeviceMotionAndOrientationManager manager, Context context) {
+        mManager = manager;
+        assert(mManager != null);
+        mContext = context;
+        assert(mContext != null);
+     }
+
+    public void start() {
+        mIsRunning = true;
+        registerForSensors();
+    }
+
+    public void stop() {
+        mIsRunning = false;
+        unregisterFromSensors();
+    }
+
+    public void suspend() {
+        if (mIsRunning) {
+            unregisterFromSensors();
+        }
+    }
+
+    public void resume() {
+        if (mIsRunning) {
+            registerForSensors();
+        }
+    }
+
+    private void sendErrorEvent() {
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+        // The spec requires that each listener receives the error event only once.
+        if (mHaveSentErrorEvent)
+            return;
+        mHaveSentErrorEvent = true;
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+                if (mIsRunning) {
+                    // The special case of all nulls is used to signify a failure to get data.
+                    mManager.onOrientationChange(null, null, null);
+                }
+            }
+        });
+    }
+
+    private void registerForSensors() {
+        if (mHandler == null) {
+            mHandler = new Handler();
+        }
+        if (!registerForAccelerometerSensor() || !registerForMagneticFieldSensor()) {
+            unregisterFromSensors();
+            sendErrorEvent();
+        }
+    }
+
+    private void getOrientationUsingGetRotationMatrix() {
+        if (mGravityVector == null || mMagneticFieldVector == null) {
+            return;
+        }
+
+        // Get the rotation matrix.
+        // The rotation matrix that transforms from the body frame to the earth frame.
+        float[] deviceRotationMatrix = new float[9];
+        if (!SensorManager.getRotationMatrix(
+                deviceRotationMatrix, null, mGravityVector, mMagneticFieldVector)) {
+            return;
+        }
+
+        // Convert rotation matrix to rotation angles.
+        // Assuming that the rotations are appied in the order listed at
+        // http://developer.android.com/reference/android/hardware/SensorEvent.html#values
+        // the rotations are applied about the same axes and in the same order as required by the
+        // API. The only conversions are sign changes as follows.
+        // The angles are in radians
+        float[] rotationAngles = new float[3];
+        SensorManager.getOrientation(deviceRotationMatrix, rotationAngles);
+        double alpha = Math.toDegrees(-rotationAngles[0]) - 90.0;
+        while (alpha < 0.0) { alpha += 360.0; } // [0, 360)
+        double beta = Math.toDegrees(-rotationAngles[1]);
+        while (beta < -180.0) { beta += 360.0; } // [-180, 180)
+        double gamma = Math.toDegrees(rotationAngles[2]);
+        while (gamma < -90.0) { gamma += 360.0; } // [-90, 90)
+
+        maybeSendChange(alpha, beta, gamma);
+    }
+
+    private SensorManager getSensorManager() {
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+        if (mSensorManager == null) {
+            mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+        }
+        return mSensorManager;
+    }
+
+    private boolean registerForAccelerometerSensor() {
+        List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_ACCELEROMETER);
+        if (sensors.isEmpty()) {
+            return false;
+        }
+        // TODO: Consider handling multiple sensors.
+        return getSensorManager().registerListener(
+                this, sensors.get(0), SensorManager.SENSOR_DELAY_FASTEST, mHandler);
+    }
+
+    private boolean registerForMagneticFieldSensor() {
+        List<Sensor> sensors = getSensorManager().getSensorList(Sensor.TYPE_MAGNETIC_FIELD);
+        if (sensors.isEmpty()) {
+            return false;
+        }
+        // TODO: Consider handling multiple sensors.
+        return getSensorManager().registerListener(
+                this, sensors.get(0), SensorManager.SENSOR_DELAY_FASTEST, mHandler);
+    }
+
+    private void unregisterFromSensors() {
+        getSensorManager().unregisterListener(this);
+    }
+
+    private void maybeSendChange(double alpha, double beta, double gamma) {
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+        if (mAlpha == null || mBeta == null || mGamma == null
+                || Math.abs(alpha - mAlpha) > DELTA_DEGRESS
+                || Math.abs(beta - mBeta) > DELTA_DEGRESS
+                || Math.abs(gamma - mGamma) > DELTA_DEGRESS) {
+            mAlpha = alpha;
+            mBeta = beta;
+            mGamma = gamma;
+            mManager.onOrientationChange(mAlpha, mBeta, mGamma);
+            // Now that we have successfully sent some data, reset whether we've sent an error.
+            mHaveSentErrorEvent = false;
+        }
+    }
+
+    /**
+     * SensorEventListener implementation.
+     * Callbacks happen on the thread on which we registered - the WebCore thread.
+     */
+    public void onSensorChanged(SensorEvent event) {
+        assert(event.values.length == 3);
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+
+        if (!mIsRunning) {
+            return;
+        }
+
+        switch (event.sensor.getType()) {
+          case Sensor.TYPE_ACCELEROMETER:
+            if (mGravityVector == null) {
+                mGravityVector = new float[3];
+            }
+            mGravityVector[0] = event.values[0];
+            mGravityVector[1] = event.values[1];
+            mGravityVector[2] = event.values[2];
+            getOrientationUsingGetRotationMatrix();
+            break;
+          case Sensor.TYPE_MAGNETIC_FIELD:
+            if (mMagneticFieldVector == null) {
+                mMagneticFieldVector = new float[3];
+            }
+            mMagneticFieldVector[0] = event.values[0];
+            mMagneticFieldVector[1] = event.values[1];
+            mMagneticFieldVector[2] = event.values[2];
+            getOrientationUsingGetRotationMatrix();
+            break;
+          default:
+            assert(false);
+        }
+    }
+
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+    }
+}
diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java
new file mode 100644
index 0000000..27043e0
--- /dev/null
+++ b/core/java/android/webkit/FindActionModeCallback.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.TextWatcher;
+import android.webkit.WebView;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.view.ActionMode;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+
+class FindActionModeCallback implements ActionMode.Callback, TextWatcher,
+        View.OnLongClickListener {
+    private View mCustomView;
+    private EditText mEditText;
+    private TextView mMatches;
+    private WebView mWebView;
+    private InputMethodManager mInput;
+    private Resources mResources;
+    private boolean mMatchesFound;
+    private int mNumberOfMatches;
+    private View mTitleBar;
+    private ActionMode mActionMode;
+
+    FindActionModeCallback(Context context) {
+        mCustomView = LayoutInflater.from(context).inflate(
+                com.android.internal.R.layout.webview_find, null);
+        mEditText = (EditText) mCustomView.findViewById(
+                com.android.internal.R.id.edit);
+        // Override long click so that select ActionMode is not opened, which
+        // would exit find ActionMode.
+        mEditText.setOnLongClickListener(this);
+        setText("");
+        mMatches = (TextView) mCustomView.findViewById(
+                com.android.internal.R.id.matches);
+        mInput = (InputMethodManager)
+                context.getSystemService(Context.INPUT_METHOD_SERVICE);
+        mResources = context.getResources();
+    }
+
+    void setTitleBar(View v) { mTitleBar = v; }
+
+    void finish() {
+        mActionMode.finish();
+    }
+
+    /*
+     * Place text in the text field so it can be searched for.  Need to press
+     * the find next or find previous button to find all of the matches.
+     */
+    void setText(String text) {
+        mEditText.setText(text);
+        Spannable span = (Spannable) mEditText.getText();
+        int length = span.length();
+        // Ideally, we would like to set the selection to the whole field,
+        // but this brings up the Text selection CAB, which dismisses this
+        // one.
+        Selection.setSelection(span, length, length);
+        // Necessary each time we set the text, so that this will watch
+        // changes to it.
+        span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+        mMatchesFound = false;
+    }
+
+    /*
+     * Set the WebView to search.  Must be non null, and set before calling
+     * startActionMode.
+     */
+    void setWebView(WebView webView) {
+        if (null == webView) {
+            throw new AssertionError("WebView supplied to "
+                    + "FindActionModeCallback cannot be null");
+        }
+        mWebView = webView;
+    }
+
+    /*
+     * Move the highlight to the next match.
+     * @param next If true, find the next match further down in the document.
+     *             If false, find the previous match, up in the document.
+     */
+    private void findNext(boolean next) {
+        if (mWebView == null) {
+            throw new AssertionError(
+                    "No WebView for FindActionModeCallback::findNext");
+        }
+        mWebView.findNext(next);
+    }
+
+    /*
+     * Highlight all the instances of the string from mEditText in mWebView.
+     */
+    void findAll() {
+        if (mWebView == null) {
+            throw new AssertionError(
+                    "No WebView for FindActionModeCallback::findAll");
+        }
+        CharSequence find = mEditText.getText();
+        if (0 == find.length()) {
+            mWebView.clearMatches();
+            mMatches.setVisibility(View.GONE);
+            mMatchesFound = false;
+        } else {
+            mMatchesFound = true;
+            mMatches.setVisibility(View.VISIBLE);
+            mNumberOfMatches = mWebView.findAll(find.toString());
+            if (0 == mNumberOfMatches) {
+                mMatches.setText(mResources.getString(
+                        com.android.internal.R.string.no_matches));
+            } else {
+                updateMatchesString();
+            }
+        }
+    }
+
+    /*
+     * Update the string which tells the user how many matches were found, and
+     * which match is currently highlighted.
+     */
+    private void updateMatchesString() {
+        String template = mResources.getQuantityString(
+                com.android.internal.R.plurals.matches_found, mNumberOfMatches,
+                mWebView.findIndex() + 1, mNumberOfMatches);
+
+        mMatches.setText(template);
+    }
+
+    // OnLongClickListener implementation
+
+    @Override
+    public boolean onLongClick(View v) { return true; }
+
+    // ActionMode.Callback implementation
+
+    @Override
+    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+        mode.setCustomView(mCustomView);
+        mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_find,
+                menu);
+        mActionMode = mode;
+        Editable edit = mEditText.getText();
+        Selection.setSelection(edit, edit.length());
+        mMatches.setVisibility(View.GONE);
+        mMatchesFound = false;
+        mMatches.setText("0");
+        mEditText.requestFocus();
+        mInput.showSoftInput(mEditText, 0);
+        return true;
+    }
+
+    @Override
+    public void onDestroyActionMode(ActionMode mode) {
+        if (mTitleBar != null) mWebView.setEmbeddedTitleBar(mTitleBar);
+        mWebView.notifyFindDialogDismissed();
+        mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
+    }
+
+    @Override
+    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+        return false;
+    }
+
+    @Override
+    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+        if (!mMatchesFound) {
+            findAll();
+            return true;
+        }
+        switch(item.getItemId()) {
+            case com.android.internal.R.id.find_prev:
+                findNext(false);
+                break;
+            case com.android.internal.R.id.find_next:
+                findNext(true);
+                break;
+            default:
+                return false;
+        }
+        updateMatchesString();
+        return true;
+    }
+
+    // TextWatcher implementation
+
+    @Override
+    public void beforeTextChanged(CharSequence s,
+                                  int start,
+                                  int count,
+                                  int after) {
+        // Does nothing.  Needed to implement TextWatcher.
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s,
+                              int start,
+                              int before,
+                              int count) {
+        findAll();
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+        // Does nothing.  Needed to implement TextWatcher.
+    }
+
+}
diff --git a/core/java/android/webkit/GeolocationService.java b/core/java/android/webkit/GeolocationService.java
index 24306f4..91de1d8 100755
--- a/core/java/android/webkit/GeolocationService.java
+++ b/core/java/android/webkit/GeolocationService.java
@@ -45,14 +45,13 @@
 
     /**
      * Constructor
+     * @param context The context from which we obtain the system service.
      * @param nativeObject The native object to which this object will report position updates and
      *     errors.
      */
-    public GeolocationService(long nativeObject) {
+    public GeolocationService(Context context, long nativeObject) {
         mNativeObject = nativeObject;
         // Register newLocationAvailable with platform service.
-        ActivityThread thread = ActivityThread.systemMain();
-        Context context = thread.getApplication();
         mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
         if (mLocationManager == null) {
             Log.e(TAG, "Could not get location manager.");
@@ -62,9 +61,10 @@
     /**
      * Start listening for location updates.
      */
-    public void start() {
+    public boolean start() {
         registerForLocationUpdates();
         mIsRunning = true;
+        return mIsNetworkProviderAvailable || mIsGpsProviderAvailable;
     }
 
     /**
@@ -87,6 +87,8 @@
                 // only unregister from all, then reregister with all but the GPS.
                 unregisterFromLocationUpdates();
                 registerForLocationUpdates();
+                // Check that the providers are still available after we re-register.
+                maybeReportError("The last location provider is no longer available");
             }
         }
     }
@@ -156,11 +158,16 @@
      */
     private void registerForLocationUpdates() {
         try {
-            mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
-            mIsNetworkProviderAvailable = true;
+            // Registration may fail if providers are not present on the device.
+            try {
+                mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
+                mIsNetworkProviderAvailable = true;
+            } catch(IllegalArgumentException e) { }
             if (mIsGpsEnabled) {
-                mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
-                mIsGpsProviderAvailable = true;
+                try {
+                    mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
+                    mIsGpsProviderAvailable = true;
+                } catch(IllegalArgumentException e) { }
             }
         } catch(SecurityException e) {
             Log.e(TAG, "Caught security exception registering for location updates from system. " +
@@ -173,6 +180,8 @@
      */
     private void unregisterFromLocationUpdates() {
         mLocationManager.removeUpdates(this);
+        mIsNetworkProviderAvailable = false;
+        mIsGpsProviderAvailable = false;
     }
 
     /**
diff --git a/core/java/android/webkit/HttpAuthHandler.java b/core/java/android/webkit/HttpAuthHandler.java
index ed85da6..1797eb4 100644
--- a/core/java/android/webkit/HttpAuthHandler.java
+++ b/core/java/android/webkit/HttpAuthHandler.java
@@ -16,151 +16,19 @@
 
 package android.webkit;
 
-import android.os.Bundle;
 import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-
-import java.util.ListIterator;
-import java.util.LinkedList;
 
 /**
- * HTTP authentication handler: local handler that takes care
- * of HTTP authentication requests. This class is passed as a
- * parameter to BrowserCallback.displayHttpAuthDialog and is
- * meant to receive the user's response.
+ * HTTP authentication request that must be handled by the user interface.
+ * WebView creates the object and hands it to the current {@link WebViewClient},
+ * which must call either {@link #proceed(String, String)} or {@link #cancel()}.
  */
 public class HttpAuthHandler extends Handler {
-    /* It is important that the handler is in Network, because
-     * we want to share it accross multiple loaders and windows
-     * (like our subwindow and the main window).
-     */
-
-    private static final String LOGTAG = "network";
 
     /**
-     * Network.
+     * Package-private constructor needed for API compatibility.
      */
-    private Network mNetwork;
-
-    /**
-     * Loader queue.
-     */
-    private LinkedList<LoadListener> mLoaderQueue;
-
-
-    // Message id for handling the user response
-    private static final int AUTH_PROCEED = 100;
-    private static final int AUTH_CANCEL = 200;
-
-    // Use to synchronize when making synchronous calls to
-    // onReceivedHttpAuthRequest(). We can't use a single Boolean object for
-    // both the lock and the state, because Boolean is immutable.
-    Object mRequestInFlightLock = new Object();
-    boolean mRequestInFlight;
-    String mUsername;
-    String mPassword;
-
-    /**
-     * Creates a new HTTP authentication handler with an empty
-     * loader queue
-     *
-     * @param network The parent network object
-     */
-    /* package */ HttpAuthHandler(Network network) {
-        mNetwork = network;
-        mLoaderQueue = new LinkedList<LoadListener>();
-    }
-
-
-    @Override
-    public void handleMessage(Message msg) {
-        LoadListener loader = null;
-        synchronized (mLoaderQueue) {
-            loader = mLoaderQueue.poll();
-        }
-        assert(loader.isSynchronous() == false);
-
-        switch (msg.what) {
-            case AUTH_PROCEED:
-                String username = msg.getData().getString("username");
-                String password = msg.getData().getString("password");
-
-                loader.handleAuthResponse(username, password);
-                break;
-
-            case AUTH_CANCEL:
-                loader.handleAuthResponse(null, null);
-                break;
-        }
-
-        processNextLoader();
-    }
-
-    /**
-     * Helper method used to unblock handleAuthRequest(), which in the case of a
-     * synchronous request will wait for proxy.onReceivedHttpAuthRequest() to
-     * call back to either proceed() or cancel().
-     *
-     * @param username The username to use for authentication
-     * @param password The password to use for authentication
-     * @return True if the request is synchronous and handleAuthRequest() has
-     * been unblocked
-     */
-    private boolean handleResponseForSynchronousRequest(String username, String password) {
-        LoadListener loader = null;
-        synchronized (mLoaderQueue) {
-            loader = mLoaderQueue.peek();
-        }
-        if (loader.isSynchronous()) {
-            mUsername = username;
-            mPassword = password;
-            return true;
-        }
-        return false;
-    }
-
-    private void signalRequestComplete() {
-        synchronized (mRequestInFlightLock) {
-            assert(mRequestInFlight);
-            mRequestInFlight = false;
-            mRequestInFlightLock.notify();
-        }
-    }
-
-    /**
-     * Proceed with the authorization with the given credentials
-     *
-     * May be called on the UI thread, rather than the WebCore thread.
-     *
-     * @param username The username to use for authentication
-     * @param password The password to use for authentication
-     */
-    public void proceed(String username, String password) {
-        if (handleResponseForSynchronousRequest(username, password)) {
-            signalRequestComplete();
-            return;
-        }
-        Message msg = obtainMessage(AUTH_PROCEED);
-        msg.getData().putString("username", username);
-        msg.getData().putString("password", password);
-        sendMessage(msg);
-        signalRequestComplete();
-    }
-
-    /**
-     * Cancel the authorization request
-     *
-     * May be called on the UI thread, rather than the WebCore thread.
-     *
-     */
-    public void cancel() {
-        if (handleResponseForSynchronousRequest(null, null)) {
-            signalRequestComplete();
-            return;
-        }
-        sendMessage(obtainMessage(AUTH_CANCEL));
-        signalRequestComplete();
+    HttpAuthHandler() {
     }
 
     /**
@@ -168,113 +36,18 @@
      * (ie, if we did not fail trying to use them last time)
      */
     public boolean useHttpAuthUsernamePassword() {
-        LoadListener loader = null;
-        synchronized (mLoaderQueue) {
-            loader = mLoaderQueue.peek();
-        }
-        if (loader != null) {
-            return !loader.authCredentialsInvalid();
-        }
-
         return false;
     }
 
     /**
-     * Enqueues the loader, if the loader is the only element
-     * in the queue, starts processing the loader
-     *
-     * @param loader The loader that resulted in this http
-     * authentication request
+     * Cancel the authorization request.
      */
-    /* package */ void handleAuthRequest(LoadListener loader) {
-        // The call to proxy.onReceivedHttpAuthRequest() may be asynchronous. If
-        // the request is synchronous, we must block here until we have a
-        // response.
-        if (loader.isSynchronous()) {
-            // If there's a request in flight, wait for it to complete. The
-            // response will queue a message on this thread.
-            waitForRequestToComplete();
-            // Make a request to the proxy for this request, jumping the queue.
-            // We use the queue so that the loader is present in
-            // useHttpAuthUsernamePassword().
-            synchronized (mLoaderQueue) {
-                mLoaderQueue.addFirst(loader);
-            }
-            processNextLoader();
-            // Wait for this request to complete.
-            waitForRequestToComplete();
-            // Pop the loader from the queue.
-            synchronized (mLoaderQueue) {
-                assert(mLoaderQueue.peek() == loader);
-                mLoaderQueue.poll();
-            }
-            // Call back.
-            loader.handleAuthResponse(mUsername, mPassword);
-            // The message queued by the response from the last asynchronous
-            // request, if present, will start the next request.
-            return;
-        }
-
-        boolean processNext = false;
-
-        synchronized (mLoaderQueue) {
-            mLoaderQueue.offer(loader);
-            processNext =
-                (mLoaderQueue.size() == 1);
-        }
-
-        if (processNext) {
-            processNextLoader();
-        }
+    public void cancel() {
     }
 
     /**
-     * Wait for the request in flight, if any, to complete
+     * Proceed with the authorization with the given credentials.
      */
-    private void waitForRequestToComplete() {
-        synchronized (mRequestInFlightLock) {
-            while (mRequestInFlight) {
-                try {
-                    mRequestInFlightLock.wait();
-                } catch(InterruptedException e) {
-                    Log.e(LOGTAG, "Interrupted while waiting for request to complete");
-                }
-            }
-        }
-    }
-
-    /**
-     * Process the next loader in the queue (helper method)
-     */
-    private void processNextLoader() {
-        LoadListener loader = null;
-        synchronized (mLoaderQueue) {
-            loader = mLoaderQueue.peek();
-        }
-        if (loader != null) {
-            synchronized (mRequestInFlightLock) {
-                assert(mRequestInFlight == false);
-                mRequestInFlight = true;
-            }
-
-            CallbackProxy proxy = loader.getFrame().getCallbackProxy();
-
-            String hostname = loader.proxyAuthenticate() ?
-                mNetwork.getProxyHostname() : loader.host();
-
-            String realm = loader.realm();
-
-            proxy.onReceivedHttpAuthRequest(this, hostname, realm);
-        }
-    }
-
-    /**
-     * Informs the WebView of a new set of credentials.
-     * @hide Pending API council review
-     */
-    public static void onReceivedCredentials(LoadListener loader,
-            String host, String realm, String username, String password) {
-        CallbackProxy proxy = loader.getFrame().getCallbackProxy();
-        proxy.onReceivedHttpAuthCredentials(host, realm, username, password);
+    public void proceed(String username, String password) {
     }
 }
diff --git a/core/java/android/webkit/HttpAuthHandlerImpl.java b/core/java/android/webkit/HttpAuthHandlerImpl.java
new file mode 100644
index 0000000..ac05125
--- /dev/null
+++ b/core/java/android/webkit/HttpAuthHandlerImpl.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.ListIterator;
+import java.util.LinkedList;
+
+/**
+ * HttpAuthHandler implementation is used only by the Android Java HTTP stack.
+ * <p>
+ * This class is not needed when we're using the Chromium HTTP stack.
+ */
+class HttpAuthHandlerImpl extends HttpAuthHandler {
+    /*
+     * It is important that the handler is in Network, because we want to share
+     * it accross multiple loaders and windows (like our subwindow and the main
+     * window).
+     */
+
+    private static final String LOGTAG = "network";
+
+    /**
+     * Network.
+     */
+    private Network mNetwork;
+
+    /**
+     * Loader queue.
+     */
+    private LinkedList<LoadListener> mLoaderQueue;
+
+
+    // Message id for handling the user response
+    private static final int AUTH_PROCEED = 100;
+    private static final int AUTH_CANCEL = 200;
+
+    // Use to synchronize when making synchronous calls to
+    // onReceivedHttpAuthRequest(). We can't use a single Boolean object for
+    // both the lock and the state, because Boolean is immutable.
+    Object mRequestInFlightLock = new Object();
+    boolean mRequestInFlight;
+    String mUsername;
+    String mPassword;
+
+    /**
+     * Creates a new HTTP authentication handler with an empty
+     * loader queue
+     *
+     * @param network The parent network object
+     */
+    /* package */ HttpAuthHandlerImpl(Network network) {
+        mNetwork = network;
+        mLoaderQueue = new LinkedList<LoadListener>();
+    }
+
+
+    @Override
+    public void handleMessage(Message msg) {
+        LoadListener loader = null;
+        synchronized (mLoaderQueue) {
+            loader = mLoaderQueue.poll();
+        }
+        assert(loader.isSynchronous() == false);
+
+        switch (msg.what) {
+            case AUTH_PROCEED:
+                String username = msg.getData().getString("username");
+                String password = msg.getData().getString("password");
+
+                loader.handleAuthResponse(username, password);
+                break;
+
+            case AUTH_CANCEL:
+                loader.handleAuthResponse(null, null);
+                break;
+        }
+
+        processNextLoader();
+    }
+
+    /**
+     * Helper method used to unblock handleAuthRequest(), which in the case of a
+     * synchronous request will wait for proxy.onReceivedHttpAuthRequest() to
+     * call back to either proceed() or cancel().
+     *
+     * @param username The username to use for authentication
+     * @param password The password to use for authentication
+     * @return True if the request is synchronous and handleAuthRequest() has
+     * been unblocked
+     */
+    private boolean handleResponseForSynchronousRequest(String username, String password) {
+        LoadListener loader = null;
+        synchronized (mLoaderQueue) {
+            loader = mLoaderQueue.peek();
+        }
+        if (loader.isSynchronous()) {
+            mUsername = username;
+            mPassword = password;
+            return true;
+        }
+        return false;
+    }
+
+    private void signalRequestComplete() {
+        synchronized (mRequestInFlightLock) {
+            assert(mRequestInFlight);
+            mRequestInFlight = false;
+            mRequestInFlightLock.notify();
+        }
+    }
+
+    /**
+     * Proceed with the authorization with the given credentials
+     *
+     * May be called on the UI thread, rather than the WebCore thread.
+     *
+     * @param username The username to use for authentication
+     * @param password The password to use for authentication
+     */
+    public void proceed(String username, String password) {
+        if (handleResponseForSynchronousRequest(username, password)) {
+            signalRequestComplete();
+            return;
+        }
+        Message msg = obtainMessage(AUTH_PROCEED);
+        msg.getData().putString("username", username);
+        msg.getData().putString("password", password);
+        sendMessage(msg);
+        signalRequestComplete();
+    }
+
+    /**
+     * Cancel the authorization request
+     *
+     * May be called on the UI thread, rather than the WebCore thread.
+     *
+     */
+    public void cancel() {
+        if (handleResponseForSynchronousRequest(null, null)) {
+            signalRequestComplete();
+            return;
+        }
+        sendMessage(obtainMessage(AUTH_CANCEL));
+        signalRequestComplete();
+    }
+
+    /**
+     * @return True if we can use user credentials on record
+     * (ie, if we did not fail trying to use them last time)
+     */
+    public boolean useHttpAuthUsernamePassword() {
+        LoadListener loader = null;
+        synchronized (mLoaderQueue) {
+            loader = mLoaderQueue.peek();
+        }
+        if (loader != null) {
+            return !loader.authCredentialsInvalid();
+        }
+
+        return false;
+    }
+
+    /**
+     * Enqueues the loader, if the loader is the only element
+     * in the queue, starts processing the loader
+     *
+     * @param loader The loader that resulted in this http
+     * authentication request
+     */
+    /* package */ void handleAuthRequest(LoadListener loader) {
+        // The call to proxy.onReceivedHttpAuthRequest() may be asynchronous. If
+        // the request is synchronous, we must block here until we have a
+        // response.
+        if (loader.isSynchronous()) {
+            // If there's a request in flight, wait for it to complete. The
+            // response will queue a message on this thread.
+            waitForRequestToComplete();
+            // Make a request to the proxy for this request, jumping the queue.
+            // We use the queue so that the loader is present in
+            // useHttpAuthUsernamePassword().
+            synchronized (mLoaderQueue) {
+                mLoaderQueue.addFirst(loader);
+            }
+            processNextLoader();
+            // Wait for this request to complete.
+            waitForRequestToComplete();
+            // Pop the loader from the queue.
+            synchronized (mLoaderQueue) {
+                assert(mLoaderQueue.peek() == loader);
+                mLoaderQueue.poll();
+            }
+            // Call back.
+            loader.handleAuthResponse(mUsername, mPassword);
+            // The message queued by the response from the last asynchronous
+            // request, if present, will start the next request.
+            return;
+        }
+
+        boolean processNext = false;
+
+        synchronized (mLoaderQueue) {
+            mLoaderQueue.offer(loader);
+            processNext =
+                (mLoaderQueue.size() == 1);
+        }
+
+        if (processNext) {
+            processNextLoader();
+        }
+    }
+
+    /**
+     * Wait for the request in flight, if any, to complete
+     */
+    private void waitForRequestToComplete() {
+        synchronized (mRequestInFlightLock) {
+            while (mRequestInFlight) {
+                try {
+                    mRequestInFlightLock.wait();
+                } catch(InterruptedException e) {
+                    Log.e(LOGTAG, "Interrupted while waiting for request to complete");
+                }
+            }
+        }
+    }
+
+    /**
+     * Process the next loader in the queue (helper method)
+     */
+    private void processNextLoader() {
+        LoadListener loader = null;
+        synchronized (mLoaderQueue) {
+            loader = mLoaderQueue.peek();
+        }
+        if (loader != null) {
+            synchronized (mRequestInFlightLock) {
+                assert(mRequestInFlight == false);
+                mRequestInFlight = true;
+            }
+
+            CallbackProxy proxy = loader.getFrame().getCallbackProxy();
+
+            String hostname = loader.proxyAuthenticate() ?
+                mNetwork.getProxyHostname() : loader.host();
+
+            String realm = loader.realm();
+
+            proxy.onReceivedHttpAuthRequest(this, hostname, realm);
+        }
+    }
+
+    /**
+     * Informs the WebView of a new set of credentials.
+     * @hide Pending API council review
+     */
+    public static void onReceivedCredentials(LoadListener loader,
+            String host, String realm, String username, String password) {
+        CallbackProxy proxy = loader.getFrame().getCallbackProxy();
+        proxy.onReceivedHttpAuthCredentials(host, realm, username, password);
+    }
+}
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index 1f8d53f..908526f 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
 import android.util.Log;
@@ -52,12 +53,15 @@
     /* package */
     static final int REFRESH_PLUGINS = 100;
 
+    private HashMap<String, String> mContentUriToFilePathMap;
+
     /**
      * Construct a new JWebCoreJavaBridge to interface with
      * WebCore timers and cookies.
      */
     public JWebCoreJavaBridge() {
         nativeConstructor();
+
     }
 
     @Override
@@ -271,6 +275,28 @@
         }
     }
 
+    // Called on the WebCore thread through JNI.
+    private String resolveFilePathForContentUri(String uri) {
+        if (mContentUriToFilePathMap != null) {
+            String fileName = mContentUriToFilePathMap.get(uri);
+            if (fileName != null) {
+                return fileName;
+            }
+        }
+
+        // Failsafe fallback to just use the last path segment.
+        // (See OpenableColumns documentation in the SDK)
+        Uri jUri = Uri.parse(uri);
+        return jUri.getLastPathSegment();
+    }
+
+    public void storeFilePathForContentUri(String path, String contentUri) {
+        if (mContentUriToFilePathMap == null) {
+            mContentUriToFilePathMap = new HashMap<String, String>();
+        }
+        mContentUriToFilePathMap.put(contentUri, path);
+    }
+
     private native void nativeConstructor();
     private native void nativeFinalize();
     private native void sharedTimerFired();
diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java
new file mode 100644
index 0000000..962492e
--- /dev/null
+++ b/core/java/android/webkit/L10nUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.Context;
+
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * @hide
+ */
+public class L10nUtils {
+
+    // These array elements must be kept in sync with those defined in
+    // external/chromium/android/app/l10n_utils.h
+    private static int[] mIdsArray = {
+        com.android.internal.R.string.autofill_address_name_separator,      // IDS_AUTOFILL_DIALOG_ADDRESS_NAME_SEPARATOR
+        com.android.internal.R.string.autofill_address_summary_name_format, // IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_NAME_FORMAT
+        com.android.internal.R.string.autofill_address_summary_separator,   // IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_SEPARATOR
+        com.android.internal.R.string.autofill_address_summary_format       // IDS_AUTOFILL_DIALOG_ADDRESS_SUMMARY_FORMAT
+    };
+
+    private static List<String> mStrings;
+
+    public static void loadStrings(Context context) {
+        if (mStrings != null) {
+            return;
+        }
+
+        mStrings = new Vector<String>(mIdsArray.length);
+        for (int i = 0; i < mIdsArray.length; i++) {
+            mStrings.add(context.getResources().getString(mIdsArray[i]));
+        }
+    }
+
+    public static String getLocalisedString(int id) {
+        return mStrings.get(id);
+    }
+}
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index e6fa405..33f5655 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -110,6 +110,7 @@
     private RequestHandle mRequestHandle;
     private RequestHandle mSslErrorRequestHandle;
     private long     mPostIdentifier;
+    private boolean  mSetNativeResponse;
 
     // Request data. It is only valid when we are doing a load from the
     // cache. It is needed if the cache returns a redirect
@@ -181,6 +182,7 @@
     private void clearNativeLoader() {
         sNativeLoaderCount -= 1;
         mNativeLoader = 0;
+        mSetNativeResponse = false;
     }
 
     /*
@@ -352,36 +354,8 @@
         String contentType = headers.getContentType();
         if (contentType != null) {
             parseContentTypeHeader(contentType);
-
-            // If we have one of "generic" MIME types, try to deduce
-            // the right MIME type from the file extension (if any):
-            if (mMimeType.equals("text/plain") ||
-                    mMimeType.equals("application/octet-stream")) {
-
-                // for attachment, use the filename in the Content-Disposition
-                // to guess the mimetype
-                String contentDisposition = headers.getContentDisposition();
-                String url = null;
-                if (contentDisposition != null) {
-                    url = URLUtil.parseContentDisposition(contentDisposition);
-                }
-                if (url == null) {
-                    url = mUrl;
-                }
-                String newMimeType = guessMimeTypeFromExtension(url);
-                if (newMimeType != null) {
-                    mMimeType = newMimeType;
-                }
-            } else if (mMimeType.equals("text/vnd.wap.wml")) {
-                // As we don't support wml, render it as plain text
-                mMimeType = "text/plain";
-            } else {
-                // It seems that xhtml+xml and vnd.wap.xhtml+xml mime
-                // subtypes are used interchangeably. So treat them the same.
-                if (mMimeType.equals("application/vnd.wap.xhtml+xml")) {
-                    mMimeType = "application/xhtml+xml";
-                }
-            }
+            mMimeType = MimeTypeMap.getSingleton().remapGenericMimeType(
+                    mMimeType, mUrl, headers.getContentDisposition());
         } else {
             /* Often when servers respond with 304 Not Modified or a
                Redirect, then they don't specify a MIMEType. When this
@@ -680,8 +654,8 @@
                     if (!mAuthFailed && mUsername != null && mPassword != null) {
                         String host = mAuthHeader.isProxy() ?
                                 Network.getInstance(mContext).getProxyHostname() :
-                                mUri.mHost;
-                        HttpAuthHandler.onReceivedCredentials(this, host,
+                                mUri.getHost();
+                        HttpAuthHandlerImpl.onReceivedCredentials(this, host,
                                 mAuthHeader.getRealm(), mUsername, mPassword);
                         makeAuthResponse(mUsername, mPassword);
                     } else {
@@ -950,7 +924,7 @@
      */
     String host() {
         if (mUri != null) {
-            return mUri.mHost;
+            return mUri.getHost();
         }
 
         return null;
@@ -1086,13 +1060,18 @@
         // request with some credentials then don't commit the headers
         // of this response; wait for the response to the request with the
         // credentials.
-        if (mAuthHeader != null)
+        if (mAuthHeader != null) {
             return;
+        }
 
-        // Commit the headers to WebCore
+        setNativeResponse();
+    }
+
+    private void setNativeResponse() {
         int nativeResponse = createNativeResponse();
         // The native code deletes the native response object.
         nativeReceivedResponse(nativeResponse);
+        mSetNativeResponse = true;
     }
 
     /**
@@ -1127,6 +1106,9 @@
      */
     private void commitLoad() {
         if (mCancelled) return;
+        if (!mSetNativeResponse) {
+            setNativeResponse();
+        }
 
         if (mIsMainPageLoader) {
             String type = sCertificateTypeMap.get(mMimeType);
@@ -1197,6 +1179,10 @@
         }
         if (mNativeLoader != 0) {
             PerfChecker checker = new PerfChecker();
+            if (!mSetNativeResponse) {
+                setNativeResponse();
+            }
+
             nativeFinished();
             checker.responseAlert("res nativeFinished");
             clearNativeLoader();
@@ -1312,6 +1298,9 @@
                 final String text = mContext
                         .getString(R.string.open_permission_deny)
                         + "\n" + redirectTo;
+                if (!mSetNativeResponse) {
+                    setNativeResponse();
+                }
                 nativeAddData(text.getBytes(), text.length());
                 nativeFinished();
                 clearNativeLoader();
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index c1ac180..ecce2ce 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -16,35 +16,22 @@
 
 package android.webkit;
 
+import android.text.TextUtils;
 import java.util.HashMap;
 import java.util.regex.Pattern;
+import libcore.net.MimeUtils;
 
 /**
  * Two-way map that maps MIME-types to file extensions and vice versa.
+ *
+ * <p>See also {@link java.net.URLConnection#guessContentTypeFromName}
+ * and {@link java.net.URLConnection#guessContentTypeFromStream}. This
+ * class and {@code URLConnection} share the same MIME-type database.
  */
 public class MimeTypeMap {
+    private static final MimeTypeMap sMimeTypeMap = new MimeTypeMap();
 
-    /**
-     * Singleton MIME-type map instance:
-     */
-    private static MimeTypeMap sMimeTypeMap;
-
-    /**
-     * MIME-type to file extension mapping:
-     */
-    private HashMap<String, String> mMimeTypeToExtensionMap;
-
-    /**
-     * File extension to MIME type mapping:
-     */
-    private HashMap<String, String> mExtensionToMimeTypeMap;
-
-    /**
-     * Creates a new MIME-type map.
-     */
     private MimeTypeMap() {
-        mMimeTypeToExtensionMap = new HashMap<String, String>();
-        mExtensionToMimeTypeMap = new HashMap<String, String>();
     }
 
     /**
@@ -55,7 +42,7 @@
      * @return The file extension of the given url.
      */
     public static String getFileExtensionFromUrl(String url) {
-        if (url != null && url.length() > 0) {
+        if (!TextUtils.isEmpty(url)) {
             int query = url.lastIndexOf('?');
             if (query > 0) {
                 url = url.substring(0, query);
@@ -66,7 +53,7 @@
 
             // if the filename contains special characters, we don't
             // consider it valid for our matching purposes:
-            if (filename.length() > 0 &&
+            if (!filename.isEmpty() &&
                 Pattern.matches("[a-zA-Z_0-9\\.\\-\\(\\)\\%]+", filename)) {
                 int dotPos = filename.lastIndexOf('.');
                 if (0 <= dotPos) {
@@ -79,36 +66,12 @@
     }
 
     /**
-     * Load an entry into the map. This does not check if the item already
-     * exists, it trusts the caller!
-     */
-    private void loadEntry(String mimeType, String extension) {
-        //
-        // if we have an existing x --> y mapping, we do not want to
-        // override it with another mapping x --> ?
-        // this is mostly because of the way the mime-type map below
-        // is constructed (if a mime type maps to several extensions
-        // the first extension is considered the most popular and is
-        // added first; we do not want to overwrite it later).
-        //
-        if (!mMimeTypeToExtensionMap.containsKey(mimeType)) {
-            mMimeTypeToExtensionMap.put(mimeType, extension);
-        }
-
-        mExtensionToMimeTypeMap.put(extension, mimeType);
-    }
-
-    /**
      * Return true if the given MIME type has an entry in the map.
      * @param mimeType A MIME type (i.e. text/plain)
      * @return True iff there is a mimeType entry in the map.
      */
     public boolean hasMimeType(String mimeType) {
-        if (mimeType != null && mimeType.length() > 0) {
-            return mMimeTypeToExtensionMap.containsKey(mimeType);
-        }
-
-        return false;
+        return MimeUtils.hasMimeType(mimeType);
     }
 
     /**
@@ -117,16 +80,12 @@
      * @return The MIME type for the given extension or null iff there is none.
      */
     public String getMimeTypeFromExtension(String extension) {
-        if (extension != null && extension.length() > 0) {
-            return mExtensionToMimeTypeMap.get(extension);
-        }
-
-        return null;
+        return MimeUtils.guessMimeTypeFromExtension(extension);
     }
 
     // Static method called by jni.
     private static String mimeTypeFromExtension(String extension) {
-        return getSingleton().getMimeTypeFromExtension(extension);
+        return MimeUtils.guessMimeTypeFromExtension(extension);
     }
 
     /**
@@ -135,10 +94,7 @@
      * @return True iff there is an extension entry in the map.
      */
     public boolean hasExtension(String extension) {
-        if (extension != null && extension.length() > 0) {
-            return mExtensionToMimeTypeMap.containsKey(extension);
-        }
-        return false;
+        return MimeUtils.hasExtension(extension);
     }
 
     /**
@@ -149,11 +105,51 @@
      * @return The extension for the given MIME type or null iff there is none.
      */
     public String getExtensionFromMimeType(String mimeType) {
-        if (mimeType != null && mimeType.length() > 0) {
-            return mMimeTypeToExtensionMap.get(mimeType);
-        }
+        return MimeUtils.guessExtensionFromMimeType(mimeType);
+    }
 
-        return null;
+    /**
+     * If the given MIME type is null, or one of the "generic" types (text/plain
+     * or application/octet-stream) map it to a type that Android can deal with.
+     * If the given type is not generic, return it unchanged.
+     *
+     * @param mimeType MIME type provided by the server.
+     * @param url URL of the data being loaded.
+     * @param contentDisposition Content-disposition header given by the server.
+     * @return The MIME type that should be used for this data.
+     */
+    /* package */ String remapGenericMimeType(String mimeType, String url,
+            String contentDisposition) {
+        // If we have one of "generic" MIME types, try to deduce
+        // the right MIME type from the file extension (if any):
+        if ("text/plain".equals(mimeType) ||
+                "application/octet-stream".equals(mimeType)) {
+
+            // for attachment, use the filename in the Content-Disposition
+            // to guess the mimetype
+            String filename = null;
+            if (contentDisposition != null) {
+                filename = URLUtil.parseContentDisposition(contentDisposition);
+            }
+            if (filename != null) {
+                url = filename;
+            }
+            String extension = getFileExtensionFromUrl(url);
+            String newMimeType = getMimeTypeFromExtension(extension);
+            if (newMimeType != null) {
+                mimeType = newMimeType;
+            }
+        } else if ("text/vnd.wap.wml".equals(mimeType)) {
+            // As we don't support wml, render it as plain text
+            mimeType = "text/plain";
+        } else {
+            // It seems that xhtml+xml and vnd.wap.xhtml+xml mime
+            // subtypes are used interchangeably. So treat them the same.
+            if ("application/vnd.wap.xhtml+xml".equals(mimeType)) {
+                mimeType = "application/xhtml+xml";
+            }
+        }
+        return mimeType;
     }
 
     /**
@@ -161,363 +157,6 @@
      * @return The singleton instance of the MIME-type map.
      */
     public static MimeTypeMap getSingleton() {
-        if (sMimeTypeMap == null) {
-            sMimeTypeMap = new MimeTypeMap();
-
-            // The following table is based on /etc/mime.types data minus
-            // chemical/* MIME types and MIME types that don't map to any
-            // file extensions. We also exclude top-level domain names to
-            // deal with cases like:
-            //
-            // mail.google.com/a/google.com
-            //
-            // and "active" MIME types (due to potential security issues).
-
-            sMimeTypeMap.loadEntry("application/andrew-inset", "ez");
-            sMimeTypeMap.loadEntry("application/dsptype", "tsp");
-            sMimeTypeMap.loadEntry("application/futuresplash", "spl");
-            sMimeTypeMap.loadEntry("application/hta", "hta");
-            sMimeTypeMap.loadEntry("application/mac-binhex40", "hqx");
-            sMimeTypeMap.loadEntry("application/mac-compactpro", "cpt");
-            sMimeTypeMap.loadEntry("application/mathematica", "nb");
-            sMimeTypeMap.loadEntry("application/msaccess", "mdb");
-            sMimeTypeMap.loadEntry("application/oda", "oda");
-            sMimeTypeMap.loadEntry("application/ogg", "ogg");
-            sMimeTypeMap.loadEntry("application/pdf", "pdf");
-            sMimeTypeMap.loadEntry("application/pgp-keys", "key");
-            sMimeTypeMap.loadEntry("application/pgp-signature", "pgp");
-            sMimeTypeMap.loadEntry("application/pics-rules", "prf");
-            sMimeTypeMap.loadEntry("application/rar", "rar");
-            sMimeTypeMap.loadEntry("application/rdf+xml", "rdf");
-            sMimeTypeMap.loadEntry("application/rss+xml", "rss");
-            sMimeTypeMap.loadEntry("application/zip", "zip");
-            sMimeTypeMap.loadEntry("application/vnd.android.package-archive", 
-                    "apk");
-            sMimeTypeMap.loadEntry("application/vnd.cinderella", "cdy");
-            sMimeTypeMap.loadEntry("application/vnd.ms-pki.stl", "stl");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.oasis.opendocument.database", "odb");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.oasis.opendocument.formula", "odf");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.oasis.opendocument.graphics", "odg");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.oasis.opendocument.graphics-template",
-                    "otg");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.oasis.opendocument.image", "odi");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.oasis.opendocument.spreadsheet", "ods");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.oasis.opendocument.spreadsheet-template",
-                    "ots");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.oasis.opendocument.text", "odt");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.oasis.opendocument.text-master", "odm");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.oasis.opendocument.text-template", "ott");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.oasis.opendocument.text-web", "oth");
-            sMimeTypeMap.loadEntry("application/msword", "doc");
-            sMimeTypeMap.loadEntry("application/msword", "dot");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
-                    "docx");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
-                    "dotx");
-            sMimeTypeMap.loadEntry("application/vnd.ms-excel", "xls");
-            sMimeTypeMap.loadEntry("application/vnd.ms-excel", "xlt");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
-                    "xlsx");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
-                    "xltx");
-            sMimeTypeMap.loadEntry("application/vnd.ms-powerpoint", "ppt");
-            sMimeTypeMap.loadEntry("application/vnd.ms-powerpoint", "pot");
-            sMimeTypeMap.loadEntry("application/vnd.ms-powerpoint", "pps");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.openxmlformats-officedocument.presentationml.presentation",
-                    "pptx");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.openxmlformats-officedocument.presentationml.template",
-                    "potx");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
-                    "ppsx");
-            sMimeTypeMap.loadEntry("application/vnd.rim.cod", "cod");
-            sMimeTypeMap.loadEntry("application/vnd.smaf", "mmf");
-            sMimeTypeMap.loadEntry("application/vnd.stardivision.calc", "sdc");
-            sMimeTypeMap.loadEntry("application/vnd.stardivision.draw", "sda");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.stardivision.impress", "sdd");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.stardivision.impress", "sdp");
-            sMimeTypeMap.loadEntry("application/vnd.stardivision.math", "smf");
-            sMimeTypeMap.loadEntry("application/vnd.stardivision.writer",
-                    "sdw");
-            sMimeTypeMap.loadEntry("application/vnd.stardivision.writer",
-                    "vor");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.stardivision.writer-global", "sgl");
-            sMimeTypeMap.loadEntry("application/vnd.sun.xml.calc", "sxc");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.sun.xml.calc.template", "stc");
-            sMimeTypeMap.loadEntry("application/vnd.sun.xml.draw", "sxd");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.sun.xml.draw.template", "std");
-            sMimeTypeMap.loadEntry("application/vnd.sun.xml.impress", "sxi");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.sun.xml.impress.template", "sti");
-            sMimeTypeMap.loadEntry("application/vnd.sun.xml.math", "sxm");
-            sMimeTypeMap.loadEntry("application/vnd.sun.xml.writer", "sxw");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.sun.xml.writer.global", "sxg");
-            sMimeTypeMap.loadEntry(
-                    "application/vnd.sun.xml.writer.template", "stw");
-            sMimeTypeMap.loadEntry("application/vnd.visio", "vsd");
-            sMimeTypeMap.loadEntry("application/x-abiword", "abw");
-            sMimeTypeMap.loadEntry("application/x-apple-diskimage", "dmg");
-            sMimeTypeMap.loadEntry("application/x-bcpio", "bcpio");
-            sMimeTypeMap.loadEntry("application/x-bittorrent", "torrent");
-            sMimeTypeMap.loadEntry("application/x-cdf", "cdf");
-            sMimeTypeMap.loadEntry("application/x-cdlink", "vcd");
-            sMimeTypeMap.loadEntry("application/x-chess-pgn", "pgn");
-            sMimeTypeMap.loadEntry("application/x-cpio", "cpio");
-            sMimeTypeMap.loadEntry("application/x-debian-package", "deb");
-            sMimeTypeMap.loadEntry("application/x-debian-package", "udeb");
-            sMimeTypeMap.loadEntry("application/x-director", "dcr");
-            sMimeTypeMap.loadEntry("application/x-director", "dir");
-            sMimeTypeMap.loadEntry("application/x-director", "dxr");
-            sMimeTypeMap.loadEntry("application/x-dms", "dms");
-            sMimeTypeMap.loadEntry("application/x-doom", "wad");
-            sMimeTypeMap.loadEntry("application/x-dvi", "dvi");
-            sMimeTypeMap.loadEntry("application/x-flac", "flac");
-            sMimeTypeMap.loadEntry("application/x-font", "pfa");
-            sMimeTypeMap.loadEntry("application/x-font", "pfb");
-            sMimeTypeMap.loadEntry("application/x-font", "gsf");
-            sMimeTypeMap.loadEntry("application/x-font", "pcf");
-            sMimeTypeMap.loadEntry("application/x-font", "pcf.Z");
-            sMimeTypeMap.loadEntry("application/x-freemind", "mm");
-            sMimeTypeMap.loadEntry("application/x-futuresplash", "spl");
-            sMimeTypeMap.loadEntry("application/x-gnumeric", "gnumeric");
-            sMimeTypeMap.loadEntry("application/x-go-sgf", "sgf");
-            sMimeTypeMap.loadEntry("application/x-graphing-calculator", "gcf");
-            sMimeTypeMap.loadEntry("application/x-gtar", "gtar");
-            sMimeTypeMap.loadEntry("application/x-gtar", "tgz");
-            sMimeTypeMap.loadEntry("application/x-gtar", "taz");
-            sMimeTypeMap.loadEntry("application/x-hdf", "hdf");
-            sMimeTypeMap.loadEntry("application/x-ica", "ica");
-            sMimeTypeMap.loadEntry("application/x-internet-signup", "ins");
-            sMimeTypeMap.loadEntry("application/x-internet-signup", "isp");
-            sMimeTypeMap.loadEntry("application/x-iphone", "iii");
-            sMimeTypeMap.loadEntry("application/x-iso9660-image", "iso");
-            sMimeTypeMap.loadEntry("application/x-jmol", "jmz");
-            sMimeTypeMap.loadEntry("application/x-kchart", "chrt");
-            sMimeTypeMap.loadEntry("application/x-killustrator", "kil");
-            sMimeTypeMap.loadEntry("application/x-koan", "skp");
-            sMimeTypeMap.loadEntry("application/x-koan", "skd");
-            sMimeTypeMap.loadEntry("application/x-koan", "skt");
-            sMimeTypeMap.loadEntry("application/x-koan", "skm");
-            sMimeTypeMap.loadEntry("application/x-kpresenter", "kpr");
-            sMimeTypeMap.loadEntry("application/x-kpresenter", "kpt");
-            sMimeTypeMap.loadEntry("application/x-kspread", "ksp");
-            sMimeTypeMap.loadEntry("application/x-kword", "kwd");
-            sMimeTypeMap.loadEntry("application/x-kword", "kwt");
-            sMimeTypeMap.loadEntry("application/x-latex", "latex");
-            sMimeTypeMap.loadEntry("application/x-lha", "lha");
-            sMimeTypeMap.loadEntry("application/x-lzh", "lzh");
-            sMimeTypeMap.loadEntry("application/x-lzx", "lzx");
-            sMimeTypeMap.loadEntry("application/x-maker", "frm");
-            sMimeTypeMap.loadEntry("application/x-maker", "maker");
-            sMimeTypeMap.loadEntry("application/x-maker", "frame");
-            sMimeTypeMap.loadEntry("application/x-maker", "fb");
-            sMimeTypeMap.loadEntry("application/x-maker", "book");
-            sMimeTypeMap.loadEntry("application/x-maker", "fbdoc");
-            sMimeTypeMap.loadEntry("application/x-mif", "mif");
-            sMimeTypeMap.loadEntry("application/x-ms-wmd", "wmd");
-            sMimeTypeMap.loadEntry("application/x-ms-wmz", "wmz");
-            sMimeTypeMap.loadEntry("application/x-msi", "msi");
-            sMimeTypeMap.loadEntry("application/x-ns-proxy-autoconfig", "pac");
-            sMimeTypeMap.loadEntry("application/x-nwc", "nwc");
-            sMimeTypeMap.loadEntry("application/x-object", "o");
-            sMimeTypeMap.loadEntry("application/x-oz-application", "oza");
-            sMimeTypeMap.loadEntry("application/x-pkcs12", "p12");
-            sMimeTypeMap.loadEntry("application/x-pkcs7-certreqresp", "p7r");
-            sMimeTypeMap.loadEntry("application/x-pkcs7-crl", "crl");
-            sMimeTypeMap.loadEntry("application/x-quicktimeplayer", "qtl");
-            sMimeTypeMap.loadEntry("application/x-shar", "shar");
-            sMimeTypeMap.loadEntry("application/x-shockwave-flash", "swf");
-            sMimeTypeMap.loadEntry("application/x-stuffit", "sit");
-            sMimeTypeMap.loadEntry("application/x-sv4cpio", "sv4cpio");
-            sMimeTypeMap.loadEntry("application/x-sv4crc", "sv4crc");
-            sMimeTypeMap.loadEntry("application/x-tar", "tar");
-            sMimeTypeMap.loadEntry("application/x-texinfo", "texinfo");
-            sMimeTypeMap.loadEntry("application/x-texinfo", "texi");
-            sMimeTypeMap.loadEntry("application/x-troff", "t");
-            sMimeTypeMap.loadEntry("application/x-troff", "roff");
-            sMimeTypeMap.loadEntry("application/x-troff-man", "man");
-            sMimeTypeMap.loadEntry("application/x-ustar", "ustar");
-            sMimeTypeMap.loadEntry("application/x-wais-source", "src");
-            sMimeTypeMap.loadEntry("application/x-wingz", "wz");
-            sMimeTypeMap.loadEntry("application/x-webarchive", "webarchive");
-            sMimeTypeMap.loadEntry("application/x-x509-ca-cert", "crt");
-            sMimeTypeMap.loadEntry("application/x-x509-user-cert", "crt");
-            sMimeTypeMap.loadEntry("application/x-xcf", "xcf");
-            sMimeTypeMap.loadEntry("application/x-xfig", "fig");
-            sMimeTypeMap.loadEntry("application/xhtml+xml", "xhtml");
-            sMimeTypeMap.loadEntry("audio/3gpp", "3gpp");
-            sMimeTypeMap.loadEntry("audio/amr", "amr");
-            sMimeTypeMap.loadEntry("audio/basic", "snd");
-            sMimeTypeMap.loadEntry("audio/midi", "mid");
-            sMimeTypeMap.loadEntry("audio/midi", "midi");
-            sMimeTypeMap.loadEntry("audio/midi", "kar");
-            sMimeTypeMap.loadEntry("audio/midi", "xmf");
-            sMimeTypeMap.loadEntry("audio/mobile-xmf", "mxmf");
-            sMimeTypeMap.loadEntry("audio/mpeg", "mpga");
-            sMimeTypeMap.loadEntry("audio/mpeg", "mpega");
-            sMimeTypeMap.loadEntry("audio/mpeg", "mp2");
-            sMimeTypeMap.loadEntry("audio/mpeg", "mp3");
-            sMimeTypeMap.loadEntry("audio/mpeg", "m4a");
-            sMimeTypeMap.loadEntry("audio/mpegurl", "m3u");
-            sMimeTypeMap.loadEntry("audio/prs.sid", "sid");
-            sMimeTypeMap.loadEntry("audio/x-aiff", "aif");
-            sMimeTypeMap.loadEntry("audio/x-aiff", "aiff");
-            sMimeTypeMap.loadEntry("audio/x-aiff", "aifc");
-            sMimeTypeMap.loadEntry("audio/x-gsm", "gsm");
-            sMimeTypeMap.loadEntry("audio/x-mpegurl", "m3u");
-            sMimeTypeMap.loadEntry("audio/x-ms-wma", "wma");
-            sMimeTypeMap.loadEntry("audio/x-ms-wax", "wax");
-            sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "ra");
-            sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "rm");
-            sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "ram");
-            sMimeTypeMap.loadEntry("audio/x-realaudio", "ra");
-            sMimeTypeMap.loadEntry("audio/x-scpls", "pls");
-            sMimeTypeMap.loadEntry("audio/x-sd2", "sd2");
-            sMimeTypeMap.loadEntry("audio/x-wav", "wav");
-            sMimeTypeMap.loadEntry("image/bmp", "bmp");
-            sMimeTypeMap.loadEntry("image/gif", "gif");
-            sMimeTypeMap.loadEntry("image/ico", "cur");
-            sMimeTypeMap.loadEntry("image/ico", "ico");
-            sMimeTypeMap.loadEntry("image/ief", "ief");
-            sMimeTypeMap.loadEntry("image/jpeg", "jpeg");
-            sMimeTypeMap.loadEntry("image/jpeg", "jpg");
-            sMimeTypeMap.loadEntry("image/jpeg", "jpe");
-            sMimeTypeMap.loadEntry("image/pcx", "pcx");
-            sMimeTypeMap.loadEntry("image/png", "png");
-            sMimeTypeMap.loadEntry("image/svg+xml", "svg");
-            sMimeTypeMap.loadEntry("image/svg+xml", "svgz");
-            sMimeTypeMap.loadEntry("image/tiff", "tiff");
-            sMimeTypeMap.loadEntry("image/tiff", "tif");
-            sMimeTypeMap.loadEntry("image/vnd.djvu", "djvu");
-            sMimeTypeMap.loadEntry("image/vnd.djvu", "djv");
-            sMimeTypeMap.loadEntry("image/vnd.wap.wbmp", "wbmp");
-            sMimeTypeMap.loadEntry("image/x-cmu-raster", "ras");
-            sMimeTypeMap.loadEntry("image/x-coreldraw", "cdr");
-            sMimeTypeMap.loadEntry("image/x-coreldrawpattern", "pat");
-            sMimeTypeMap.loadEntry("image/x-coreldrawtemplate", "cdt");
-            sMimeTypeMap.loadEntry("image/x-corelphotopaint", "cpt");
-            sMimeTypeMap.loadEntry("image/x-icon", "ico");
-            sMimeTypeMap.loadEntry("image/x-jg", "art");
-            sMimeTypeMap.loadEntry("image/x-jng", "jng");
-            sMimeTypeMap.loadEntry("image/x-ms-bmp", "bmp");
-            sMimeTypeMap.loadEntry("image/x-photoshop", "psd");
-            sMimeTypeMap.loadEntry("image/x-portable-anymap", "pnm");
-            sMimeTypeMap.loadEntry("image/x-portable-bitmap", "pbm");
-            sMimeTypeMap.loadEntry("image/x-portable-graymap", "pgm");
-            sMimeTypeMap.loadEntry("image/x-portable-pixmap", "ppm");
-            sMimeTypeMap.loadEntry("image/x-rgb", "rgb");
-            sMimeTypeMap.loadEntry("image/x-xbitmap", "xbm");
-            sMimeTypeMap.loadEntry("image/x-xpixmap", "xpm");
-            sMimeTypeMap.loadEntry("image/x-xwindowdump", "xwd");
-            sMimeTypeMap.loadEntry("model/iges", "igs");
-            sMimeTypeMap.loadEntry("model/iges", "iges");
-            sMimeTypeMap.loadEntry("model/mesh", "msh");
-            sMimeTypeMap.loadEntry("model/mesh", "mesh");
-            sMimeTypeMap.loadEntry("model/mesh", "silo");
-            sMimeTypeMap.loadEntry("text/calendar", "ics");
-            sMimeTypeMap.loadEntry("text/calendar", "icz");
-            sMimeTypeMap.loadEntry("text/comma-separated-values", "csv");
-            sMimeTypeMap.loadEntry("text/css", "css");
-            sMimeTypeMap.loadEntry("text/html", "htm");
-            sMimeTypeMap.loadEntry("text/html", "html");
-            sMimeTypeMap.loadEntry("text/h323", "323");
-            sMimeTypeMap.loadEntry("text/iuls", "uls");
-            sMimeTypeMap.loadEntry("text/mathml", "mml");
-            // add it first so it will be the default for ExtensionFromMimeType
-            sMimeTypeMap.loadEntry("text/plain", "txt");
-            sMimeTypeMap.loadEntry("text/plain", "asc");
-            sMimeTypeMap.loadEntry("text/plain", "text");
-            sMimeTypeMap.loadEntry("text/plain", "diff");
-            sMimeTypeMap.loadEntry("text/plain", "po");     // reserve "pot" for vnd.ms-powerpoint
-            sMimeTypeMap.loadEntry("text/richtext", "rtx");
-            sMimeTypeMap.loadEntry("text/rtf", "rtf");
-            sMimeTypeMap.loadEntry("text/texmacs", "ts");
-            sMimeTypeMap.loadEntry("text/text", "phps");
-            sMimeTypeMap.loadEntry("text/tab-separated-values", "tsv");
-            sMimeTypeMap.loadEntry("text/xml", "xml");
-            sMimeTypeMap.loadEntry("text/x-bibtex", "bib");
-            sMimeTypeMap.loadEntry("text/x-boo", "boo");
-            sMimeTypeMap.loadEntry("text/x-c++hdr", "h++");
-            sMimeTypeMap.loadEntry("text/x-c++hdr", "hpp");
-            sMimeTypeMap.loadEntry("text/x-c++hdr", "hxx");
-            sMimeTypeMap.loadEntry("text/x-c++hdr", "hh");
-            sMimeTypeMap.loadEntry("text/x-c++src", "c++");
-            sMimeTypeMap.loadEntry("text/x-c++src", "cpp");
-            sMimeTypeMap.loadEntry("text/x-c++src", "cxx");
-            sMimeTypeMap.loadEntry("text/x-chdr", "h");
-            sMimeTypeMap.loadEntry("text/x-component", "htc");
-            sMimeTypeMap.loadEntry("text/x-csh", "csh");
-            sMimeTypeMap.loadEntry("text/x-csrc", "c");
-            sMimeTypeMap.loadEntry("text/x-dsrc", "d");
-            sMimeTypeMap.loadEntry("text/x-haskell", "hs");
-            sMimeTypeMap.loadEntry("text/x-java", "java");
-            sMimeTypeMap.loadEntry("text/x-literate-haskell", "lhs");
-            sMimeTypeMap.loadEntry("text/x-moc", "moc");
-            sMimeTypeMap.loadEntry("text/x-pascal", "p");
-            sMimeTypeMap.loadEntry("text/x-pascal", "pas");
-            sMimeTypeMap.loadEntry("text/x-pcs-gcd", "gcd");
-            sMimeTypeMap.loadEntry("text/x-setext", "etx");
-            sMimeTypeMap.loadEntry("text/x-tcl", "tcl");
-            sMimeTypeMap.loadEntry("text/x-tex", "tex");
-            sMimeTypeMap.loadEntry("text/x-tex", "ltx");
-            sMimeTypeMap.loadEntry("text/x-tex", "sty");
-            sMimeTypeMap.loadEntry("text/x-tex", "cls");
-            sMimeTypeMap.loadEntry("text/x-vcalendar", "vcs");
-            sMimeTypeMap.loadEntry("text/x-vcard", "vcf");
-            sMimeTypeMap.loadEntry("video/3gpp", "3gpp");
-            sMimeTypeMap.loadEntry("video/3gpp", "3gp");
-            sMimeTypeMap.loadEntry("video/3gpp", "3g2");
-            sMimeTypeMap.loadEntry("video/dl", "dl");
-            sMimeTypeMap.loadEntry("video/dv", "dif");
-            sMimeTypeMap.loadEntry("video/dv", "dv");
-            sMimeTypeMap.loadEntry("video/fli", "fli");
-            sMimeTypeMap.loadEntry("video/m4v", "m4v");
-            sMimeTypeMap.loadEntry("video/mpeg", "mpeg");
-            sMimeTypeMap.loadEntry("video/mpeg", "mpg");
-            sMimeTypeMap.loadEntry("video/mpeg", "mpe");
-            sMimeTypeMap.loadEntry("video/mp4", "mp4");
-            sMimeTypeMap.loadEntry("video/mpeg", "VOB");
-            sMimeTypeMap.loadEntry("video/quicktime", "qt");
-            sMimeTypeMap.loadEntry("video/quicktime", "mov");
-            sMimeTypeMap.loadEntry("video/vnd.mpegurl", "mxu");
-            sMimeTypeMap.loadEntry("video/x-la-asf", "lsf");
-            sMimeTypeMap.loadEntry("video/x-la-asf", "lsx");
-            sMimeTypeMap.loadEntry("video/x-mng", "mng");
-            sMimeTypeMap.loadEntry("video/x-ms-asf", "asf");
-            sMimeTypeMap.loadEntry("video/x-ms-asf", "asx");
-            sMimeTypeMap.loadEntry("video/x-ms-wm", "wm");
-            sMimeTypeMap.loadEntry("video/x-ms-wmv", "wmv");
-            sMimeTypeMap.loadEntry("video/x-ms-wmx", "wmx");
-            sMimeTypeMap.loadEntry("video/x-ms-wvx", "wvx");
-            sMimeTypeMap.loadEntry("video/x-msvideo", "avi");
-            sMimeTypeMap.loadEntry("video/x-sgi-movie", "movie");
-            sMimeTypeMap.loadEntry("x-conference/x-cooltalk", "ice");
-            sMimeTypeMap.loadEntry("x-epoc/x-sisx-app", "sisx");
-        }
-
         return sMimeTypeMap;
     }
 }
diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java
index 598f20d..59bec24 100644
--- a/core/java/android/webkit/Network.java
+++ b/core/java/android/webkit/Network.java
@@ -16,7 +16,12 @@
 
 package android.webkit;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
 import android.net.http.*;
 import android.os.*;
 import android.util.Log;
@@ -74,7 +79,20 @@
      * HTTP authentication handler: takes care of synchronization of HTTP
      * authentication requests.
      */
-    private HttpAuthHandler mHttpAuthHandler;
+    private HttpAuthHandlerImpl mHttpAuthHandler;
+
+    private Context mContext;
+
+    /**
+     * True if the currently used network connection is a roaming phone
+     * connection.
+     */
+    private boolean mRoaming;
+
+    /**
+     * Tracks if we are roaming.
+     */
+    private RoamingMonitor mRoamingMonitor;
 
     /**
      * @return The singleton instance of the network.
@@ -107,6 +125,7 @@
         if (++sPlatformNotificationEnableRefCount == 1) {
             if (sNetwork != null) {
                 sNetwork.mRequestQueue.enablePlatformNotifications();
+                sNetwork.monitorRoaming();
             } else {
                 sPlatformNotifications = true;
             }
@@ -121,6 +140,7 @@
         if (--sPlatformNotificationEnableRefCount == 0) {
             if (sNetwork != null) {
                 sNetwork.mRequestQueue.disablePlatformNotifications();
+                sNetwork.stopMonitoringRoaming();
             } else {
                 sPlatformNotifications = false;
             }
@@ -136,12 +156,39 @@
             Assert.assertTrue(Thread.currentThread().
                     getName().equals(WebViewCore.THREAD_NAME));
         }
+        mContext = context;
         mSslErrorHandler = new SslErrorHandler();
-        mHttpAuthHandler = new HttpAuthHandler(this);
+        mHttpAuthHandler = new HttpAuthHandlerImpl(this);
 
         mRequestQueue = new RequestQueue(context);
     }
 
+    private class RoamingMonitor extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction()))
+                return;
+
+            NetworkInfo info = (NetworkInfo)intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+            if (info != null)
+                mRoaming = info.isRoaming();
+        };
+    };
+
+    private void monitorRoaming() {
+        mRoamingMonitor = new RoamingMonitor();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        mContext.registerReceiver(sNetwork.mRoamingMonitor, filter);
+    }
+
+    private void stopMonitoringRoaming() {
+        if (mRoamingMonitor != null) {
+            mContext.unregisterReceiver(mRoamingMonitor);
+            mRoamingMonitor = null;
+        }
+    }
+
     /**
      * Request a url from either the network or the file system.
      * @param url The url to load.
@@ -170,6 +217,11 @@
             return false;
         }
 
+        // If this is a prefetch, abort it if we're roaming.
+        if (mRoaming && headers.containsKey("X-Moz") && "prefetch".equals(headers.get("X-Moz"))) {
+            return false;
+        }
+
         /* FIXME: this is lame.  Pass an InputStream in, rather than
            making this lame one here */
         InputStream bodyProvider = null;
diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java
index e5eeb8c..ab3b6d5 100644
--- a/core/java/android/webkit/PluginManager.java
+++ b/core/java/android/webkit/PluginManager.java
@@ -159,41 +159,11 @@
                     directory = PLUGIN_SYSTEM_LIB + pkgInfo.packageName;
                 }
 
-                // check if the plugin has the required permissions
-                String permissions[] = pkgInfo.requestedPermissions;
-                if (permissions == null) {
+                // check if the plugin has the required permissions and
+                // signatures
+                if (!containsPluginPermissionAndSignatures(pkgInfo)) {
                     continue;
                 }
-                boolean permissionOk = false;
-                for (String permit : permissions) {
-                    if (PLUGIN_PERMISSION.equals(permit)) {
-                        permissionOk = true;
-                        break;
-                    }
-                }
-                if (!permissionOk) {
-                    continue;
-                }
-
-                // check to ensure the plugin is properly signed
-                Signature signatures[] = pkgInfo.signatures;
-                if (signatures == null) {
-                    continue;
-                }
-                if (SystemProperties.getBoolean("ro.secure", false)) {
-                    boolean signatureMatch = false;
-                    for (Signature signature : signatures) {
-                        for (int i = 0; i < SIGNATURES.length; i++) {
-                            if (SIGNATURES[i].equals(signature)) {
-                                signatureMatch = true;
-                                break;
-                            }
-                        }
-                    }
-                    if (!signatureMatch) {
-                        continue;
-                    }
-                }
 
                 // determine the type of plugin from the manifest
                 if (serviceInfo.metaData == null) {
@@ -236,6 +206,64 @@
     }
 
     /* package */
+    boolean containsPluginPermissionAndSignatures(String pluginAPKName) {
+        PackageManager pm = mContext.getPackageManager();
+
+        // retrieve information from the plugin's manifest
+        try {
+            PackageInfo pkgInfo = pm.getPackageInfo(pluginAPKName, PackageManager.GET_PERMISSIONS
+                    | PackageManager.GET_SIGNATURES);
+            if (pkgInfo != null) {
+                return containsPluginPermissionAndSignatures(pkgInfo);
+            }
+        } catch (NameNotFoundException e) {
+            Log.w(LOGTAG, "Can't find plugin: " + pluginAPKName);
+        }
+        return false;
+    }
+
+    private static boolean containsPluginPermissionAndSignatures(PackageInfo pkgInfo) {
+
+        // check if the plugin has the required permissions
+        String permissions[] = pkgInfo.requestedPermissions;
+        if (permissions == null) {
+            return false;
+        }
+        boolean permissionOk = false;
+        for (String permit : permissions) {
+            if (PLUGIN_PERMISSION.equals(permit)) {
+                permissionOk = true;
+                break;
+            }
+        }
+        if (!permissionOk) {
+            return false;
+        }
+
+        // check to ensure the plugin is properly signed
+        Signature signatures[] = pkgInfo.signatures;
+        if (signatures == null) {
+            return false;
+        }
+        if (SystemProperties.getBoolean("ro.secure", false)) {
+            boolean signatureMatch = false;
+            for (Signature signature : signatures) {
+                for (int i = 0; i < SIGNATURES.length; i++) {
+                    if (SIGNATURES[i].equals(signature)) {
+                        signatureMatch = true;
+                        break;
+                    }
+                }
+            }
+            if (!signatureMatch) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /* package */
     String getPluginsAPKName(String pluginLib) {
 
         // basic error checking on input params
diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java
new file mode 100644
index 0000000..cf91902
--- /dev/null
+++ b/core/java/android/webkit/SelectActionModeCallback.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.provider.Browser;
+import android.webkit.WebView;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+class SelectActionModeCallback implements ActionMode.Callback {
+    private WebView mWebView;
+    private View mTitleBar;
+    private ActionMode mActionMode;
+
+    void setWebView(WebView webView) {
+        mWebView = webView;
+    }
+
+    void setTitleBar(View v) { mTitleBar = v; }
+
+    void finish() {
+        mActionMode.finish();
+    }
+
+    // ActionMode.Callback implementation
+
+    @Override
+    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+        mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_copy,
+                menu);
+        mode.setTitle(com.android.internal.R.string.textSelectionCABTitle);
+        mActionMode = mode;
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+        return true;
+    }
+
+    @Override
+    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+        switch(item.getItemId()) {
+            case android.R.id.copy:
+                mWebView.copySelection();
+                mode.finish();
+                break;
+
+            case com.android.internal.R.id.share:
+                String selection = mWebView.getSelection();
+                Browser.sendString(mWebView.getContext(), selection);
+                mode.finish();
+                break;
+
+            case com.android.internal.R.id.select_all:
+                mWebView.selectAll();
+                break;
+
+            case com.android.internal.R.id.find:
+                String sel= mWebView.getSelection();
+                mode.finish();
+                mWebView.showFindDialog(sel);
+                break;
+
+            default:
+                return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void onDestroyActionMode(ActionMode mode) {
+        if (mTitleBar != null) mWebView.setEmbeddedTitleBar(mTitleBar);
+        mWebView.selectionDone();
+    }
+}
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index 7c5f2b0..542dd21 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -47,7 +47,7 @@
         String retVal = inUrl;
         WebAddress webAddress;
 
-        Log.v(LOGTAG, "guessURL before queueRequest: " + inUrl);
+        if (DebugFlags.URL_UTIL) Log.v(LOGTAG, "guessURL before queueRequest: " + inUrl);
 
         if (inUrl.length() == 0) return inUrl;
         if (inUrl.startsWith("about:")) return inUrl;
@@ -74,9 +74,9 @@
         }
 
         // Check host
-        if (webAddress.mHost.indexOf('.') == -1) {
+        if (webAddress.getHost().indexOf('.') == -1) {
             // no dot: user probably entered a bare domain.  try .com
-            webAddress.mHost = "www." + webAddress.mHost + ".com";
+            webAddress.setHost("www." + webAddress.getHost() + ".com");
         }
         return webAddress.toString();
     }
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 9f642c0..b2ba7e23 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -314,28 +314,20 @@
     /**
      * Tell the client to open a file chooser.
      * @param uploadFile A ValueCallback to set the URI of the file to upload.
-     *      onReceiveValue must be called to wake up the thread.
+     *      onReceiveValue must be called to wake up the thread.a
+     * @param acceptType The value of the 'accept' attribute of the input tag
+     *         associated with this file picker.
      * @hide
      */
-    public void openFileChooser(ValueCallback<Uri> uploadFile) {
+    public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) {
         uploadFile.onReceiveValue(null);
     }
 
     /**
-     * Tell the client that the selection has been initiated.
+     * Tell the client that the page being viewed is web app capable,
+     * i.e. has specified the fullscreen-web-app-capable meta tag.
      * @hide
      */
-    public void onSelectionStart(WebView view) {
-        // By default we cancel the selection again, thus disabling
-        // text selection unless the chrome client supports it.
-        view.notifySelectDialogDismissed();
-    }
-
-    /**
-     * Tell the client that the selection has been copied or canceled.
-     * @hide
-     */
-    public void onSelectionDone(WebView view) {
-    }
+    public void setInstallableWebApp() { }
 
 }
diff --git a/core/java/android/webkit/WebHistoryItem.java b/core/java/android/webkit/WebHistoryItem.java
index 428a59c..7c0e478 100644
--- a/core/java/android/webkit/WebHistoryItem.java
+++ b/core/java/android/webkit/WebHistoryItem.java
@@ -18,6 +18,9 @@
 
 import android.graphics.Bitmap;
 
+import java.net.MalformedURLException;
+import java.net.URL;
+
 /**
  * A convenience class for accessing fields in an entry in the back/forward list
  * of a WebView. Each WebHistoryItem is a snapshot of the requested history
@@ -39,8 +42,12 @@
     private Bitmap mFavicon;
     // The pre-flattened data used for saving the state.
     private byte[] mFlattenedData;
-    // The apple-touch-icon url for use when adding the site to the home screen
-    private String mTouchIconUrl;
+    // The apple-touch-icon url for use when adding the site to the home screen,
+    // as obtained from a <link> element in the page.
+    private String mTouchIconUrlFromLink;
+    // If no <link> is specified, this holds the default location of the
+    // apple-touch-icon.
+    private String mTouchIconUrlServerDefault;
     // Custom client data that is not flattened or read by native code.
     private Object mCustomData;
 
@@ -132,10 +139,28 @@
 
     /**
      * Return the touch icon url.
+     * If no touch icon <link> tag was specified, returns
+     * <host>/apple-touch-icon.png. The DownloadTouchIcon class that
+     * attempts to retrieve the touch icon will handle the case where
+     * that file does not exist. An icon set by a <link> tag is always
+     * used in preference to an icon saved on the server.
      * @hide
      */
     public String getTouchIconUrl() {
-        return mTouchIconUrl;
+        if (mTouchIconUrlFromLink != null) {
+            return mTouchIconUrlFromLink;
+        } else if (mTouchIconUrlServerDefault != null) {
+            return mTouchIconUrlServerDefault;
+        }
+
+        try {
+            URL url = new URL(mOriginalUrl);
+            mTouchIconUrlServerDefault = new URL(url.getProtocol(), url.getHost(), url.getPort(),
+                    "/apple-touch-icon.png").toString();
+        } catch (MalformedURLException e) {
+            return null;
+        }
+        return mTouchIconUrlServerDefault;
     }
 
     /**
@@ -171,11 +196,14 @@
     }
 
     /**
-     * Set the touch icon url.
+     * Set the touch icon url. Will not overwrite an icon that has been
+     * set already from a <link> tag, unless the new icon is precomposed.
      * @hide
      */
-    /*package*/ void setTouchIconUrl(String url) {
-        mTouchIconUrl = url;
+    /*package*/ void setTouchIconUrl(String url, boolean precomposed) {
+        if (precomposed || mTouchIconUrlFromLink == null) {
+            mTouchIconUrlFromLink = url;
+        }
     }
 
     /**
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index da0c61b..4d582ef 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -19,11 +19,13 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
+import android.util.DisplayMetrics;
 import android.util.EventLog;
-import java.lang.SecurityException;
+
 import java.util.Locale;
 
 /**
@@ -107,7 +109,7 @@
      * Use with {@link #setCacheMode}.
      */
     public static final int LOAD_NO_CACHE = 2;
-    
+
     /**
      * Don't use the network, load from cache only.
      * Use with {@link #setCacheMode}.
@@ -176,14 +178,19 @@
     private boolean         mJavaScriptCanOpenWindowsAutomatically = false;
     private boolean         mUseDoubleTree = false;
     private boolean         mUseWideViewport = false;
+    private boolean         mUseFixedViewport = false;
     private boolean         mSupportMultipleWindows = false;
     private boolean         mShrinksStandaloneImagesToFit = false;
+    private long            mMaximumDecodedImageSize = 0; // 0 means default
+    private boolean         mPrivateBrowsingEnabled = false;
+    private boolean         mSyntheticLinksEnabled = true;
     // HTML5 API flags
     private boolean         mAppCacheEnabled = false;
     private boolean         mDatabaseEnabled = false;
     private boolean         mDomStorageEnabled = false;
     private boolean         mWorkersEnabled = false;  // only affects V8.
     private boolean         mGeolocationEnabled = true;
+    private boolean         mXSSAuditorEnabled = false;
     // HTML5 configuration parameters
     private long            mAppCacheMaxSize = Long.MAX_VALUE;
     private String          mAppCachePath = "";
@@ -199,15 +206,67 @@
     private RenderPriority  mRenderPriority = RenderPriority.NORMAL;
     private int             mOverrideCacheMode = LOAD_DEFAULT;
     private boolean         mSaveFormData = true;
+    private boolean         mAutoFillEnabled = false;
     private boolean         mSavePassword = true;
     private boolean         mLightTouchEnabled = false;
     private boolean         mNeedInitialFocus = true;
     private boolean         mNavDump = false;
     private boolean         mSupportZoom = true;
     private boolean         mBuiltInZoomControls = false;
+    private boolean         mDisplayZoomControls = true;
     private boolean         mAllowFileAccess = true;
     private boolean         mLoadWithOverviewMode = false;
-    private boolean         mUseWebViewBackgroundOverscrollBackground = true;
+    private boolean         mEnableSmoothTransition = false;
+
+    // AutoFill Profile data
+    /**
+     * @hide for now, pending API council approval.
+     */
+    public static class AutoFillProfile {
+        private int mUniqueId;
+        private String mFullName;
+        private String mEmailAddress;
+        private String mCompanyName;
+        private String mAddressLine1;
+        private String mAddressLine2;
+        private String mCity;
+        private String mState;
+        private String mZipCode;
+        private String mCountry;
+        private String mPhoneNumber;
+
+        public AutoFillProfile(int uniqueId, String fullName, String email,
+                String companyName, String addressLine1, String addressLine2,
+                String city, String state, String zipCode, String country,
+                String phoneNumber) {
+            mUniqueId = uniqueId;
+            mFullName = fullName;
+            mEmailAddress = email;
+            mCompanyName = companyName;
+            mAddressLine1 = addressLine1;
+            mAddressLine2 = addressLine2;
+            mCity = city;
+            mState = state;
+            mZipCode = zipCode;
+            mCountry = country;
+            mPhoneNumber = phoneNumber;
+        }
+
+        public int getUniqueId() { return mUniqueId; }
+        public String getFullName() { return mFullName; }
+        public String getEmailAddress() { return mEmailAddress; }
+        public String getCompanyName() { return mCompanyName; }
+        public String getAddressLine1() { return mAddressLine1; }
+        public String getAddressLine2() { return mAddressLine2; }
+        public String getCity() { return mCity; }
+        public String getState() { return mState; }
+        public String getZipCode() { return mZipCode; }
+        public String getCountry() { return mCountry; }
+        public String getPhoneNumber() { return mPhoneNumber; }
+    }
+
+
+    private AutoFillProfile mAutoFillProfile;
 
     // private WebSettings, not accessible by the host activity
     static private int      mDoubleTapToastCount = 3;
@@ -297,13 +356,13 @@
             "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)"
             + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0"
             + " Safari/530.17";
-    private static final String IPHONE_USERAGENT = 
+    private static final String IPHONE_USERAGENT =
             "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
             + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
             + " Mobile/7A341 Safari/528.16";
     private static Locale sLocale;
     private static Object sLockForLocaleSettings;
-    
+
     /**
      * Package constructor to prevent clients from creating a new settings
      * instance.
@@ -315,6 +374,13 @@
         mDefaultTextEncoding = context.getString(com.android.internal.
                                                  R.string.default_text_encoding);
 
+        // Detect tablet device for fixed viewport mode.
+        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        final int landscapeWidth = Math.max(metrics.widthPixels, metrics.heightPixels);
+        final int minTabletWidth = context.getResources().getDimensionPixelSize(
+            com.android.internal.R.dimen.min_xlarge_screen_width);
+        mUseFixedViewport = (metrics.density == 1.0f && landscapeWidth >= minTabletWidth);
+
         if (sLockForLocaleSettings == null) {
             sLockForLocaleSettings = new Object();
             sLocale = Locale.getDefault();
@@ -328,6 +394,8 @@
                 android.os.Process.myUid()) != PackageManager.PERMISSION_GRANTED;
     }
 
+    private static final String ACCEPT_LANG_FOR_US_LOCALE = "en-US";
+
     /**
      * Looks at sLocale and returns current AcceptLanguage String.
      * @return Current AcceptLanguage String.
@@ -337,32 +405,53 @@
         synchronized(sLockForLocaleSettings) {
             locale = sLocale;
         }
-        StringBuffer buffer = new StringBuffer();
-        final String language = locale.getLanguage();
-        if (language != null) {
-            buffer.append(language);
-            final String country = locale.getCountry();
-            if (country != null) {
-                buffer.append("-");
-                buffer.append(country);
+        StringBuilder buffer = new StringBuilder();
+        addLocaleToHttpAcceptLanguage(buffer, locale);
+
+        if (!Locale.US.equals(locale)) {
+            if (buffer.length() > 0) {
+                buffer.append(", ");
             }
-        }
-        if (!locale.equals(Locale.US)) {
-            buffer.append(", ");
-            java.util.Locale us = Locale.US;
-            if (us.getLanguage() != null) {
-                buffer.append(us.getLanguage());
-                final String country = us.getCountry();
-                if (country != null) {
-                    buffer.append("-");
-                    buffer.append(country);
-                }
-            }
+            buffer.append(ACCEPT_LANG_FOR_US_LOCALE);
         }
 
         return buffer.toString();
     }
-    
+
+    /**
+     * Convert obsolete language codes, including Hebrew/Indonesian/Yiddish,
+     * to new standard.
+     */
+    private static String convertObsoleteLanguageCodeToNew(String langCode) {
+        if (langCode == null) {
+            return null;
+        }
+        if ("iw".equals(langCode)) {
+            // Hebrew
+            return "he";
+        } else if ("in".equals(langCode)) {
+            // Indonesian
+            return "id";
+        } else if ("ji".equals(langCode)) {
+            // Yiddish
+            return "yi";
+        }
+        return langCode;
+    }
+
+    private static void addLocaleToHttpAcceptLanguage(StringBuilder builder,
+                                                      Locale locale) {
+        String language = convertObsoleteLanguageCodeToNew(locale.getLanguage());
+        if (language != null) {
+            builder.append(language);
+            String country = locale.getCountry();
+            if (country != null) {
+                builder.append("-");
+                builder.append(country);
+            }
+        }
+    }
+
     /**
      * Looks at sLocale and mContext and returns current UserAgent String.
      * @return Current UserAgent String.
@@ -380,11 +469,11 @@
         } else {
             // default to "1.0"
             buffer.append("1.0");
-        }  
+        }
         buffer.append("; ");
         final String language = locale.getLanguage();
         if (language != null) {
-            buffer.append(language.toLowerCase());
+            buffer.append(convertObsoleteLanguageCodeToNew(language));
             final String country = locale.getCountry();
             if (country != null) {
                 buffer.append("-");
@@ -407,11 +496,13 @@
             buffer.append(" Build/");
             buffer.append(id);
         }
+        String mobile = mContext.getResources().getText(
+            com.android.internal.R.string.web_user_agent_target_content).toString();
         final String base = mContext.getResources().getText(
                 com.android.internal.R.string.web_user_agent).toString();
-        return String.format(base, buffer);
+        return String.format(base, buffer, mobile);
     }
-    
+
     /**
      * Enables dumping the pages navigation cache to a text file.
      */
@@ -427,6 +518,21 @@
     }
 
     /**
+     * If WebView only supports touch, a different navigation model will be
+     * applied. Otherwise, the navigation to support both touch and keyboard
+     * will be used.
+     * @hide
+    public void setSupportTouchOnly(boolean touchOnly) {
+        mSupportTounchOnly = touchOnly;
+    }
+     */
+
+    boolean supportTouchOnly() {
+        // for debug only, use mLightTouchEnabled for mSupportTounchOnly
+        return mLightTouchEnabled;
+    }
+
+    /**
      * Set whether the WebView supports zoom
      */
     public void setSupportZoom(boolean support) {
@@ -448,14 +554,34 @@
         mBuiltInZoomControls = enabled;
         mWebView.updateMultiTouchSupport(mContext);
     }
-    
+
     /**
      * Returns true if the zoom mechanism built into WebView is being used.
      */
     public boolean getBuiltInZoomControls() {
         return mBuiltInZoomControls;
     }
-    
+
+    /**
+     * Sets whether the on screen zoom buttons are used.
+     * A combination of built in zoom controls enabled
+     * and on screen zoom controls disabled allows for pinch to zoom
+     * to work without the on screen controls
+     * @hide
+     */
+    public void setDisplayZoomControls(boolean enabled) {
+        mDisplayZoomControls = enabled;
+        mWebView.updateMultiTouchSupport(mContext);
+    }
+
+    /**
+     * Returns true if the on screen zoom buttons are being used.
+     * @hide
+     */
+    public boolean getDisplayZoomControls() {
+        return mDisplayZoomControls;
+    }
+
     /**
      * Enable or disable file access within WebView. File access is enabled by
      * default.
@@ -486,20 +612,22 @@
     }
 
     /**
-     * Set whether the WebView uses its background for over scroll background.
-     * If true, it will use the WebView's background. If false, it will use an
-     * internal pattern. Default is true.
+     * Set whether the WebView will enable smooth transition while panning or
+     * zooming. If it is true, WebView will choose a solution to maximize the
+     * performance. e.g. the WebView's content may not be updated during the
+     * transition. If it is false, WebView will keep its fidelity. The default
+     * value is false.
      */
-    public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
-        mUseWebViewBackgroundOverscrollBackground = view;
+    public void setEnableSmoothTransition(boolean enable) {
+        mEnableSmoothTransition = enable;
     }
 
     /**
-     * Returns true if this WebView uses WebView's background instead of
-     * internal pattern for over scroll background.
+     * Returns true if the WebView enables smooth transition while panning or
+     * zooming.
      */
-    public boolean getUseWebViewBackgroundForOverscrollBackground() {
-        return mUseWebViewBackgroundOverscrollBackground;
+    public boolean enableSmoothTransition() {
+        return mEnableSmoothTransition;
     }
 
     /**
@@ -987,6 +1115,7 @@
         if (mBlockNetworkLoads != flag) {
             mBlockNetworkLoads = flag;
             verifyNetworkAccess();
+            postSync();
         }
     }
 
@@ -1002,8 +1131,8 @@
 
     private void verifyNetworkAccess() {
         if (!mBlockNetworkLoads) {
-            if (mContext.checkPermission("android.permission.INTERNET", 
-                    android.os.Process.myPid(), android.os.Process.myUid()) != 
+            if (mContext.checkPermission("android.permission.INTERNET",
+                    android.os.Process.myPid(), android.os.Process.myUid()) !=
                         PackageManager.PERMISSION_GRANTED) {
                 throw new SecurityException
                         ("Permission denied - " +
@@ -1029,6 +1158,7 @@
      * @deprecated This method has been deprecated in favor of
      *             {@link #setPluginState}
      */
+    @Deprecated
     public synchronized void setPluginsEnabled(boolean flag) {
         setPluginState(PluginState.ON);
     }
@@ -1199,6 +1329,18 @@
     }
 
     /**
+     * Sets whether XSS Auditor is enabled.
+     * @param flag Whether XSS Auditor should be enabled.
+     * @hide Only used by LayoutTestController.
+     */
+    public synchronized void setXSSAuditorEnabled(boolean flag) {
+        if (mXSSAuditorEnabled != flag) {
+            mXSSAuditorEnabled = flag;
+            postSync();
+        }
+    }
+
+    /**
      * Return true if javascript is enabled. <b>Note: The default is false.</b>
      * @return True if javascript is enabled.
      */
@@ -1211,6 +1353,7 @@
      * @return True if plugins are enabled.
      * @deprecated This method has been replaced by {@link #getPluginState}
      */
+    @Deprecated
     public synchronized boolean getPluginsEnabled() {
         return mPluginState == PluginState.ON;
     }
@@ -1284,7 +1427,7 @@
     public synchronized void setUserAgentString(String ua) {
         if (ua == null || ua.length() == 0) {
             synchronized(sLockForLocaleSettings) {
-                Locale currentLocale = Locale.getDefault(); 
+                Locale currentLocale = Locale.getDefault();
                 if (!sLocale.equals(currentLocale)) {
                     sLocale = currentLocale;
                     mAcceptLanguage = getCurrentAcceptLanguage();
@@ -1339,11 +1482,15 @@
         }
         return mAcceptLanguage;
     }
-    
+
+    /* package */ boolean isNarrowColumnLayout() {
+        return getLayoutAlgorithm() == LayoutAlgorithm.NARROW_COLUMNS;
+    }
+
     /**
      * Tell the WebView whether it needs to set a node to have focus when
      * {@link WebView#requestFocus(int, android.graphics.Rect)} is called.
-     * 
+     *
      * @param flag
      */
     public void setNeedInitialFocus(boolean flag) {
@@ -1370,7 +1517,7 @@
                     EventHandler.PRIORITY));
         }
     }
-    
+
     /**
      * Override the way the cache is used. The way the cache is used is based
      * on the navigation option. For a normal page load, the cache is checked
@@ -1384,7 +1531,7 @@
             mOverrideCacheMode = mode;
         }
     }
-    
+
     /**
      * Return the current setting for overriding the cache mode. For a full
      * description, see the {@link #setCacheMode(int)} function.
@@ -1392,7 +1539,7 @@
     public int getCacheMode() {
         return mOverrideCacheMode;
     }
-    
+
     /**
      * If set, webkit alternately shrinks and expands images viewed outside
      * of an HTML page to fit the screen. This conflicts with attempts by
@@ -1407,6 +1554,78 @@
         }
      }
 
+    /**
+     * Specify the maximum decoded image size. The default is
+     * 2 megs for small memory devices and 8 megs for large memory devices.
+     * @param size The maximum decoded size, or zero to set to the default.
+     * @hide pending api council approval
+     */
+    public void setMaximumDecodedImageSize(long size) {
+        if (mMaximumDecodedImageSize != size) {
+            mMaximumDecodedImageSize = size;
+            postSync();
+        }
+    }
+
+    /**
+     * Returns whether to use fixed viewport.
+     */
+    /* package */ boolean getUseFixedViewport() {
+        return mUseFixedViewport;
+    }
+
+    /**
+     * Returns whether private browsing is enabled.
+     */
+    /* package */ boolean isPrivateBrowsingEnabled() {
+        return mPrivateBrowsingEnabled;
+    }
+
+    /**
+     * Sets whether private browsing is enabled.
+     * @param flag Whether private browsing should be enabled.
+     */
+    /* package */ synchronized void setPrivateBrowsingEnabled(boolean flag) {
+        if (mPrivateBrowsingEnabled != flag) {
+            mPrivateBrowsingEnabled = flag;
+            postSync();
+        }
+    }
+
+    synchronized void setSyntheticLinksEnabled(boolean flag) {
+        if (mSyntheticLinksEnabled != flag) {
+            mSyntheticLinksEnabled = flag;
+            postSync();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public synchronized void setAutoFillEnabled(boolean enabled) {
+        if (mAutoFillEnabled != enabled) {
+            mAutoFillEnabled = enabled;
+            postSync();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public synchronized boolean getAutoFillEnabled() {
+        return mAutoFillEnabled;
+    }
+
+    /**
+     * @hide
+     */
+    public synchronized void setAutoFillProfile(AutoFillProfile profile) {
+        if (mAutoFillProfile != profile) {
+            mAutoFillProfile = profile;
+            postSync();
+        }
+    }
+
     int getDoubleTapToastCount() {
         return mDoubleTapToastCount;
     }
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 1a9ccea..1538bd0 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -47,6 +47,7 @@
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputConnection;
 import android.widget.AbsoluteLayout.LayoutParams;
+import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.AutoCompleteTextView;
 import android.widget.TextView;
@@ -110,16 +111,45 @@
     // FIXME: This can be replaced with TextView.NO_FILTERS if that
     // is made public/protected.
     private static final InputFilter[] NO_FILTERS = new InputFilter[0];
+    // For keeping track of the fact that the delete key was pressed, so
+    // we can simply pass a delete key instead of calling deleteSelection.
+    private boolean mGotDelete;
+    private int mDelSelStart;
+    private int mDelSelEnd;
+
+    // Keep in sync with native constant in
+    // external/webkit/WebKit/android/WebCoreSupport/autofill/WebAutoFill.cpp
+    /* package */ static final int FORM_NOT_AUTOFILLABLE = -1;
+
+    private boolean mAutoFillable; // Is this textview part of an autofillable form?
+    private int mQueryId;
+
+    // Types used with setType.  Keep in sync with CachedInput.h
+    private static final int NORMAL_TEXT_FIELD = 0;
+    private static final int TEXT_AREA = 1;
+    private static final int PASSWORD = 2;
+    private static final int SEARCH = 3;
+    private static final int EMAIL = 4;
+    private static final int NUMBER = 5;
+    private static final int TELEPHONE = 6;
+    private static final int URL = 7;
 
     /**
      * Create a new WebTextView.
      * @param   context The Context for this WebTextView.
      * @param   webView The WebView that created this.
      */
-    /* package */ WebTextView(Context context, WebView webView) {
+    /* package */ WebTextView(Context context, WebView webView, int autoFillQueryId) {
         super(context, null, com.android.internal.R.attr.webTextViewStyle);
         mWebView = webView;
         mMaxLength = -1;
+        setAutoFillable(autoFillQueryId);
+    }
+
+    public void setAutoFillable(int queryId) {
+        mAutoFillable = mWebView.getSettings().getAutoFillEnabled()
+                && (queryId != FORM_NOT_AUTOFILLABLE);
+        mQueryId = queryId;
     }
 
     @Override
@@ -153,13 +183,22 @@
             return true;
         }
         Spannable text = (Spannable) getText();
-        int oldLength = text.length();
+        int oldStart = Selection.getSelectionStart(text);
+        int oldEnd = Selection.getSelectionEnd(text);
         // Normally the delete key's dom events are sent via onTextChanged.
-        // However, if the length is zero, the text did not change, so we
-        // go ahead and pass the key down immediately.
-        if (KeyEvent.KEYCODE_DEL == keyCode && 0 == oldLength) {
-            sendDomEvent(event);
-            return true;
+        // However, if the cursor is at the beginning of the field, which
+        // includes the case where it has zero length, then the text is not
+        // changed, so send the events immediately.
+        if (KeyEvent.KEYCODE_DEL == keyCode) {
+            if (oldStart == 0 && oldEnd == 0) {
+                sendDomEvent(event);
+                return true;
+            }
+            if (down) {
+                mGotDelete = true;
+                mDelSelStart = oldStart;
+                mDelSelEnd = oldEnd;
+            }
         }
 
         if ((mSingle && KeyEvent.KEYCODE_ENTER == keyCode)) {
@@ -197,9 +236,8 @@
         if (getLayout() == null) {
             measure(mWidthSpec, mHeightSpec);
         }
-        int oldStart = Selection.getSelectionStart(text);
-        int oldEnd = Selection.getSelectionEnd(text);
 
+        int oldLength = text.length();
         boolean maxedOut = mMaxLength != -1 && oldLength == mMaxLength;
         // If we are at max length, and there is a selection rather than a
         // cursor, we need to store the text to compare later, since the key
@@ -317,6 +355,34 @@
         return connection;
     }
 
+    /**
+     * In general, TextView makes a call to InputMethodManager.updateSelection
+     * in onDraw.  However, in the general case of WebTextView, we do not draw.
+     * This method is called by WebView.onDraw to take care of the part that
+     * needs to be called.
+     */
+    /* package */ void onDrawSubstitute() {
+        if (!willNotDraw()) {
+            // If the WebTextView is set to draw, such as in the case of a
+            // password, onDraw calls updateSelection(), so this code path is
+            // unnecessary.
+            return;
+        }
+        // This code is copied from TextView.onDraw().  That code does not get
+        // executed, however, because the WebTextView does not draw, allowing
+        // webkit's drawing to show through.
+        InputMethodManager imm = InputMethodManager.peekInstance();
+        if (imm != null && imm.isActive(this)) {
+            Spannable sp = (Spannable) getText();
+            int selStart = Selection.getSelectionStart(sp);
+            int selEnd = Selection.getSelectionEnd(sp);
+            int candStart = EditableInputConnection.getComposingSpanStart(sp);
+            int candEnd = EditableInputConnection.getComposingSpanEnd(sp);
+            imm.updateSelection(this, selStart, selEnd, candStart, candEnd);
+        }
+        updateCursorControllerPositions();
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         // onDraw should only be called for password fields.  If WebTextView is
@@ -334,18 +400,11 @@
         }
     }
 
-    public void onDrawSubstitute() {
-      updateCursorControllerPositions();
-    }
-
     @Override
     public void onEditorAction(int actionCode) {
         switch (actionCode) {
         case EditorInfo.IME_ACTION_NEXT:
             if (mWebView.nativeMoveCursorToNextTextInput()) {
-                // Since the cursor will no longer be in the same place as the
-                // focus, set the focus controller back to inactive
-                mWebView.setFocusControllerInactive();
                 // Preemptively rebuild the WebTextView, so that the action will
                 // be set properly.
                 mWebView.rebuildWebTextView();
@@ -387,19 +446,8 @@
 
     @Override
     protected void onSelectionChanged(int selStart, int selEnd) {
-        if (mInSetTextAndKeepSelection) return;
-        // This code is copied from TextView.onDraw().  That code does not get
-        // executed, however, because the WebTextView does not draw, allowing
-        // webkit's drawing to show through.
-        InputMethodManager imm = InputMethodManager.peekInstance();
-        if (imm != null && imm.isActive(this)) {
-            Spannable sp = (Spannable) getText();
-            int candStart = EditableInputConnection.getComposingSpanStart(sp);
-            int candEnd = EditableInputConnection.getComposingSpanEnd(sp);
-            imm.updateSelection(this, selStart, selEnd, candStart, candEnd);
-        }
         if (!mFromWebKit && !mFromFocusChange && !mFromSetInputType
-                && mWebView != null) {
+                && mWebView != null && !mInSetTextAndKeepSelection) {
             if (DebugFlags.WEB_TEXT_VIEW) {
                 Log.v(LOGTAG, "onSelectionChanged selStart=" + selStart
                         + " selEnd=" + selEnd);
@@ -425,17 +473,38 @@
         mPreChange = postChange;
         if (0 == count) {
             if (before > 0) {
+                // For this and all changes to the text, update our cache
+                updateCachedTextfield();
+                if (mGotDelete) {
+                    mGotDelete = false;
+                    int oldEnd = start + before;
+                    if (mDelSelEnd == oldEnd
+                            && (mDelSelStart == start
+                            || (mDelSelStart == oldEnd && before == 1))) {
+                        // If the selection is set up properly before the
+                        // delete, send the DOM events.
+                        sendDomEvent(new KeyEvent(KeyEvent.ACTION_DOWN,
+                                KeyEvent.KEYCODE_DEL));
+                        sendDomEvent(new KeyEvent(KeyEvent.ACTION_UP,
+                                KeyEvent.KEYCODE_DEL));
+                        return;
+                    }
+                }
                 // This was simply a delete or a cut, so just delete the
                 // selection.
                 mWebView.deleteSelection(start, start + before);
-                // For this and all changes to the text, update our cache
-                updateCachedTextfield();
             }
+            mGotDelete = false;
             // before should never be negative, so whether it was a cut
             // (handled above), or before is 0, in which case nothing has
             // changed, we should return.
             return;
         }
+        // Ensure that this flag gets cleared, since with autocorrect on, a
+        // delete key press may have a more complex result than deleting one
+        // character or the existing selection, so it will not get cleared
+        // above.
+        mGotDelete = false;
         // Find the last character being replaced.  If it can be represented by
         // events, we will pass them to native (after replacing the beginning
         // of the changed text), so we can see javascript events.
@@ -598,16 +667,31 @@
      */
     /* package */ void remove() {
         // hide the soft keyboard when the edit text is out of focus
-        InputMethodManager.getInstance(mContext).hideSoftInputFromWindow(
-                getWindowToken(), 0);
+        InputMethodManager imm = InputMethodManager.getInstance(mContext);
+        if (imm.isActive(this)) {
+            imm.hideSoftInputFromWindow(getWindowToken(), 0);
+        }
         mWebView.removeView(this);
         mWebView.requestFocus();
     }
 
+    /**
+     * Move the caret/selection into view.
+     */
     /* package */ void bringIntoView() {
-        if (getLayout() != null) {
-            bringPointIntoView(Selection.getSelectionEnd(getText()));
+        bringPointIntoView(Selection.getSelectionEnd(getText()));
+    }
+
+    @Override
+    public boolean bringPointIntoView(int offset) {
+        if (mWebView == null) return false;
+        if (mWebView.nativeFocusCandidateIsPassword()) {
+            return getLayout() != null && super.bringPointIntoView(offset);
         }
+        // For non password text input, tell webkit to move the caret/selection
+        // on screen, since webkit draws them.
+        mWebView.revealSelection();
+        return true;
     }
 
     /**
@@ -629,6 +713,22 @@
             adapter.setTextView(this);
         }
         super.setAdapter(adapter);
+        if (mAutoFillable) {
+            setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                @Override
+                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                    if (id == 0 && position == 0) {
+                        // Blank out the text box while we wait for WebCore to fill the form.
+                        replaceText("");
+                        // Call a webview method to tell WebCore to autofill the form.
+                        mWebView.autoFillForm(mQueryId);
+                    }
+                }
+            });
+        } else {
+            setOnItemClickListener(null);
+        }
+        showDropDown();
     }
 
     /**
@@ -687,6 +787,7 @@
         } else {
             Selection.setSelection(text, selection, selection);
         }
+        if (mWebView != null) mWebView.incrementTextGeneration();
     }
 
     /**
@@ -697,7 +798,7 @@
     /* package */ void setInPassword(boolean inPassword) {
         if (inPassword) {
             setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.
-                    TYPE_TEXT_VARIATION_PASSWORD);
+                TYPE_TEXT_VARIATION_WEB_PASSWORD);
             createBackground();
         }
         // For password fields, draw the WebTextView.  For others, just show
@@ -849,7 +950,7 @@
     /**
      * Called by WebView.rebuildWebTextView().  Based on the type of the <input>
      * element, set up the WebTextView, its InputType, and IME Options properly.
-     * @param type int corresponding to enum "type" defined in WebView.cpp.
+     * @param type int corresponding to enum "Type" defined in CachedInput.h.
      *              Does not correspond to HTMLInputElement::InputType so this
      *              is unaffected if that changes, and also because that has no
      *              type corresponding to textarea (which is its own tag).
@@ -859,47 +960,47 @@
         boolean single = true;
         boolean inPassword = false;
         int maxLength = -1;
-        int inputType = EditorInfo.TYPE_CLASS_TEXT;
-        if (mWebView.nativeFocusCandidateHasNextTextfield()) {
-            inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
-        }
+        int inputType = EditorInfo.TYPE_CLASS_TEXT
+                | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
         int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
                 | EditorInfo.IME_FLAG_NO_FULLSCREEN;
+        if (TEXT_AREA != type
+                && mWebView.nativeFocusCandidateHasNextTextfield()) {
+            imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
+        }
         switch (type) {
-            case 0: // NORMAL_TEXT_FIELD
+            case NORMAL_TEXT_FIELD:
                 imeOptions |= EditorInfo.IME_ACTION_GO;
                 break;
-            case 1: // TEXT_AREA
+            case TEXT_AREA:
                 single = false;
-                inputType = EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
+                inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
                         | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
-                        | EditorInfo.TYPE_CLASS_TEXT
                         | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT;
                 imeOptions |= EditorInfo.IME_ACTION_NONE;
                 break;
-            case 2: // PASSWORD
+            case PASSWORD:
                 inPassword = true;
                 imeOptions |= EditorInfo.IME_ACTION_GO;
                 break;
-            case 3: // SEARCH
+            case SEARCH:
                 imeOptions |= EditorInfo.IME_ACTION_SEARCH;
                 break;
-            case 4: // EMAIL
-                // TYPE_TEXT_VARIATION_WEB_EDIT_TEXT prevents EMAIL_ADDRESS
-                // from working, so exclude it for now.
+            case EMAIL:
+                inputType = EditorInfo.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
                 imeOptions |= EditorInfo.IME_ACTION_GO;
                 break;
-            case 5: // NUMBER
+            case NUMBER:
                 inputType |= EditorInfo.TYPE_CLASS_NUMBER;
                 // Number and telephone do not have both a Tab key and an
                 // action, so set the action to NEXT
                 imeOptions |= EditorInfo.IME_ACTION_NEXT;
                 break;
-            case 6: // TELEPHONE
+            case TELEPHONE:
                 inputType |= EditorInfo.TYPE_CLASS_PHONE;
                 imeOptions |= EditorInfo.IME_ACTION_NEXT;
                 break;
-            case 7: // URL
+            case URL:
                 // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
                 // exclude it for now.
                 imeOptions |= EditorInfo.IME_ACTION_GO;
@@ -913,10 +1014,10 @@
             mWebView.requestLabel(mWebView.nativeFocusCandidateFramePointer(),
                     mNodePointer);
             maxLength = mWebView.nativeFocusCandidateMaxLength();
-            if (type != 2 /* PASSWORD */) {
+            if (type != PASSWORD) {
                 String name = mWebView.nativeFocusCandidateName();
                 if (name != null && name.length() > 0) {
-                    mWebView.requestFormData(name, mNodePointer);
+                    mWebView.requestFormData(name, mNodePointer, mAutoFillable);
                 }
             }
         }
@@ -936,14 +1037,4 @@
     /* package */ void updateCachedTextfield() {
         mWebView.updateCachedTextfield(getText().toString());
     }
-
-    @Override
-    public boolean requestRectangleOnScreen(Rect rectangle) {
-        // don't scroll while in zoom animation. When it is done, we will adjust
-        // the WebTextView if it is in editing mode.
-        if (!mWebView.inAnimateZoom()) {
-            return super.requestRectangleOnScreen(rectangle);
-        }
-        return false;
-    }
 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 88516ce..9e4f6cfc 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -16,39 +16,39 @@
 
 package android.webkit;
 
-import com.android.internal.R;
-
 import android.annotation.Widget;
 import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.ClipboardManager;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.IntentFilter;
 import android.content.DialogInterface.OnCancelListener;
-import android.content.Intent;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
+import android.content.Intent;
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.Interpolator;
+import android.graphics.CornerPathEffect;
+import android.graphics.DrawFilter;
 import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
 import android.graphics.Picture;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
-import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.net.http.SslCertificate;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.text.IClipboard;
+import android.provider.Settings;
+import android.speech.tts.TextToSpeech;
 import android.text.Selection;
 import android.text.Spannable;
 import android.util.AttributeSet;
@@ -66,27 +66,26 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
-import android.view.animation.AlphaAnimation;
+import android.view.accessibility.AccessibilityManager;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.webkit.WebTextView.AutoCompleteAdapter;
 import android.webkit.WebViewCore.EventHub;
 import android.webkit.WebViewCore.TouchEventData;
+import android.webkit.WebViewCore.TouchHighlightData;
 import android.widget.AbsoluteLayout;
 import android.widget.Adapter;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ArrayAdapter;
 import android.widget.CheckedTextView;
-import android.widget.EdgeGlow;
-import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.ListView;
-import android.widget.OverScroller;
+import android.widget.Scroller;
 import android.widget.Toast;
-import android.widget.ZoomButtonsController;
-import android.widget.ZoomControls;
+
+import junit.framework.Assert;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -95,11 +94,12 @@
 import java.net.URLDecoder;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
-import junit.framework.Assert;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * <p>A View that displays web pages. This class is the basis upon which you
@@ -116,7 +116,7 @@
  * <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
  *
  * <p>This must be a child of the <a
- * href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code &lt;manifest&gt;}</a>
+ * href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code <manifest>}</a>
  * element.</p>
  *
  * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-webview.html">Web View
@@ -139,7 +139,7 @@
  * </pre>
  * <p>See {@link android.content.Intent} for more information.</p>
  *
- * <p>To provide a WebView in your own Activity, include a {@code &lt;WebView&gt;} in your layout,
+ * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
  * or set the entire Activity window as a WebView during {@link
  * android.app.Activity#onCreate(Bundle) onCreate()}:</p>
  * <pre class="prettyprint">
@@ -286,7 +286,7 @@
  * low density screens scale down. This is also the default behavior.</li>
  * <li>{@code low-dpi} - Use ldpi as the target dpi. Medium and high density screens scale up
  * as appropriate.</li>
- * <li><em>{@code &lt;value&gt;}</em> - Specify a dpi value to use as the target dpi (accepted
+ * <li><em>{@code <value>}</em> - Specify a dpi value to use as the target dpi (accepted
  * values are 70-400).</li>
  * </ul>
  * <p>Here's an example meta tag to specify the target density:</p>
@@ -298,15 +298,13 @@
  * property to {@code device-dpi}. This stops Android from performing scaling in your web page and
  * allows you to make the necessary adjustments for each density via CSS and JavaScript.</p>
  *
- * 
+ *
  */
 @Widget
 public class WebView extends AbsoluteLayout
         implements ViewTreeObserver.OnGlobalFocusChangeListener,
         ViewGroup.OnHierarchyChangeListener {
 
-    // enable debug output for drag trackers
-    private static final boolean DEBUG_DRAG_TRACKER = false;
     // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
     // the screen all-the-time. Good for profiling our drawing code
     static private final boolean AUTO_REDRAW_HACK = false;
@@ -319,49 +317,9 @@
 
     static final String LOGTAG = "webview";
 
-    private static class ExtendedZoomControls extends FrameLayout {
-        public ExtendedZoomControls(Context context, AttributeSet attrs) {
-            super(context, attrs);
-            LayoutInflater inflater = (LayoutInflater)
-                    context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-            inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true);
-            mPlusMinusZoomControls = (ZoomControls) findViewById(
-                    com.android.internal.R.id.zoomControls);
-            findViewById(com.android.internal.R.id.zoomMagnify).setVisibility(
-                    View.GONE);
-        }
+    private ZoomManager mZoomManager;
 
-        public void show(boolean showZoom, boolean canZoomOut) {
-            mPlusMinusZoomControls.setVisibility(
-                    showZoom ? View.VISIBLE : View.GONE);
-            fade(View.VISIBLE, 0.0f, 1.0f);
-        }
-
-        public void hide() {
-            fade(View.GONE, 1.0f, 0.0f);
-        }
-
-        private void fade(int visibility, float startAlpha, float endAlpha) {
-            AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
-            anim.setDuration(500);
-            startAnimation(anim);
-            setVisibility(visibility);
-        }
-
-        public boolean hasFocus() {
-            return mPlusMinusZoomControls.hasFocus();
-        }
-
-        public void setOnZoomInClickListener(OnClickListener listener) {
-            mPlusMinusZoomControls.setOnZoomInClickListener(listener);
-        }
-
-        public void setOnZoomOutClickListener(OnClickListener listener) {
-            mPlusMinusZoomControls.setOnZoomOutClickListener(listener);
-        }
-
-        ZoomControls    mPlusMinusZoomControls;
-    }
+    private Rect mGLRectViewport;
 
     /**
      *  Transportation object for returning WebView across thread boundaries.
@@ -407,6 +365,8 @@
     // more key events.
     private int mTextGeneration;
 
+    /* package */ void incrementTextGeneration() { mTextGeneration++; }
+
     // Used by WebViewCore to create child views.
     /* package */ final ViewManager mViewManager;
 
@@ -454,6 +414,14 @@
     private float mLastVelX;
     private float mLastVelY;
 
+    // A pointer to the native scrollable layer when dragging layers.  Only
+    // valid when mTouchMode is TOUCH_DRAG_LAYER_MODE.
+    private int mScrollingLayer;
+
+    // only trigger accelerated fling if the new velocity is at least
+    // MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION times of the previous velocity
+    private static final float MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION = 0.2f;
+
     /**
      * Touch mode
      */
@@ -466,11 +434,7 @@
     private static final int TOUCH_DOUBLE_TAP_MODE = 6;
     private static final int TOUCH_DONE_MODE = 7;
     private static final int TOUCH_PINCH_DRAG = 8;
-
-    /**
-     * True if we have a touch panel capable of detecting smooth pan/scale at the same time
-     */
-    private boolean mAllowPanAndScale;
+    private static final int TOUCH_DRAG_LAYER_MODE = 9;
 
     // Whether to forward the touch events to WebCore
     private boolean mForwardTouchEvents = false;
@@ -493,6 +457,9 @@
     // default is not set, the UI will continue handle them.
     private boolean mDeferTouchProcess;
 
+    // if true, multi-touch events will be passed to webkit directly before UI
+    private boolean mDeferMultitouch = false;
+
     // to avoid interfering with the current touch events, track them
     // separately. Currently no snapping or fling in the deferred process mode
     private int mDeferTouchMode = TOUCH_DONE_MODE;
@@ -509,10 +476,6 @@
     // true if onPause has been called (and not onResume)
     private boolean mIsPaused;
 
-    // true if, during a transition to a new page, we're delaying
-    // deleting a root layer until there's something to draw of the new page.
-    private boolean mDelayedDeleteRootLayer;
-
     /**
      * Customizable constant
      */
@@ -534,9 +497,6 @@
     private static final int MIN_FLING_TIME = 250;
     // draw unfiltered after drag is held without movement
     private static final int MOTIONLESS_TIME = 100;
-    // The time that the Zoom Controls are visible before fading away
-    private static final long ZOOM_CONTROLS_TIMEOUT =
-            ViewConfiguration.getZoomControlsTimeout();
     // The amount of content to overlap between two screens when going through
     // pages with the space bar, in pixels.
     private static final int PAGE_SCROLL_OVERLAP = 24;
@@ -568,10 +528,7 @@
     // time for the longest scroll animation
     private static final int MAX_DURATION = 750;   // milliseconds
     private static final int SLIDE_TITLE_DURATION = 500;   // milliseconds
-    private OverScroller mScroller;
-    private boolean mInOverScrollMode = false;
-    private static Paint mOverScrollBackground;
-    private static Paint mOverScrollBorder;
+    private Scroller mScroller;
 
     private boolean mWrapContent;
     private static final int MOTIONLESS_FALSE           = 0;
@@ -580,15 +537,28 @@
     private static final int MOTIONLESS_IGNORE          = 3;
     private int mHeldMotionless;
 
-    // whether support multi-touch
-    private boolean mSupportMultiTouch;
-    // use the framework's ScaleGestureDetector to handle multi-touch
-    private ScaleGestureDetector mScaleDetector;
+    // An instance for injecting accessibility in WebViews with disabled
+    // JavaScript or ones for which no accessibility script exists
+    private AccessibilityInjector mAccessibilityInjector;
 
-    // the anchor point in the document space where VIEW_SIZE_CHANGED should
-    // apply to
-    private int mAnchorX;
-    private int mAnchorY;
+    // flag indicating if accessibility script is injected so we
+    // know to handle Shift and arrows natively first
+    private boolean mAccessibilityScriptInjected;
+
+    // the color used to highlight the touch rectangles
+    private static final int mHightlightColor = 0x33000000;
+    // the round corner for the highlight path
+    private static final float TOUCH_HIGHLIGHT_ARC = 5.0f;
+    // the region indicating where the user touched on the screen
+    private Region mTouchHighlightRegion = new Region();
+    // the paint for the touch highlight
+    private Paint mTouchHightlightPaint;
+    // debug only
+    private static final boolean DEBUG_TOUCH_HIGHLIGHT = true;
+    private static final int TOUCH_HIGHLIGHT_ELAPSE_TIME = 2000;
+    private Paint mTouchCrossHairColor;
+    private int mTouchHighlightX;
+    private int mTouchHighlightY;
 
     /*
      * Private message ids
@@ -622,7 +592,7 @@
     static final int WEBCORE_INITIALIZED_MSG_ID         = 107;
     static final int UPDATE_TEXTFIELD_TEXT_MSG_ID       = 108;
     static final int UPDATE_ZOOM_RANGE                  = 109;
-    static final int MOVE_OUT_OF_PLUGIN                 = 110;
+    static final int UNHANDLED_NAV_KEY                  = 110;
     static final int CLEAR_TEXT_ENTRY                   = 111;
     static final int UPDATE_TEXT_SELECTION_MSG_ID       = 112;
     static final int SHOW_RECT_MSG_ID                   = 113;
@@ -636,16 +606,21 @@
     static final int SHOW_FULLSCREEN                    = 120;
     static final int HIDE_FULLSCREEN                    = 121;
     static final int DOM_FOCUS_CHANGED                  = 122;
-    static final int IMMEDIATE_REPAINT_MSG_ID           = 123;
-    static final int SET_ROOT_LAYER_MSG_ID              = 124;
+    static final int REPLACE_BASE_CONTENT               = 123;
+    static final int FORM_DID_BLUR                      = 124;
     static final int RETURN_LABEL                       = 125;
     static final int FIND_AGAIN                         = 126;
     static final int CENTER_FIT_RECT                    = 127;
     static final int REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID = 128;
     static final int SET_SCROLLBAR_MODES                = 129;
+    static final int SELECTION_STRING_CHANGED           = 130;
+    static final int SET_TOUCH_HIGHLIGHT_RECTS          = 131;
+    static final int SAVE_WEBARCHIVE_FINISHED           = 132;
+
+    static final int SET_AUTOFILLABLE                   = 133;
 
     private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
-    private static final int LAST_PACKAGE_MSG_ID = SET_SCROLLBAR_MODES;
+    private static final int LAST_PACKAGE_MSG_ID = SET_TOUCH_HIGHLIGHT_RECTS;
 
     static final String[] HandlerPrivateDebugString = {
         "REMEMBER_PASSWORD", //              = 1;
@@ -670,7 +645,7 @@
         "WEBCORE_INITIALIZED_MSG_ID", //     = 107;
         "UPDATE_TEXTFIELD_TEXT_MSG_ID", //   = 108;
         "UPDATE_ZOOM_RANGE", //              = 109;
-        "MOVE_OUT_OF_PLUGIN", //             = 110;
+        "UNHANDLED_NAV_KEY", //              = 110;
         "CLEAR_TEXT_ENTRY", //               = 111;
         "UPDATE_TEXT_SELECTION_MSG_ID", //   = 112;
         "SHOW_RECT_MSG_ID", //               = 113;
@@ -683,18 +658,22 @@
         "SHOW_FULLSCREEN", //                = 120;
         "HIDE_FULLSCREEN", //                = 121;
         "DOM_FOCUS_CHANGED", //              = 122;
-        "IMMEDIATE_REPAINT_MSG_ID", //       = 123;
-        "SET_ROOT_LAYER_MSG_ID", //          = 124;
+        "REPLACE_BASE_CONTENT", //           = 123;
+        "FORM_DID_BLUR", //                  = 124;
         "RETURN_LABEL", //                   = 125;
         "FIND_AGAIN", //                     = 126;
         "CENTER_FIT_RECT", //                = 127;
         "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID", // = 128;
-        "SET_SCROLLBAR_MODES" //             = 129;
+        "SET_SCROLLBAR_MODES", //            = 129;
+        "SELECTION_STRING_CHANGED", //       = 130;
+        "SET_TOUCH_HIGHLIGHT_RECTS", //      = 131;
+        "SAVE_WEBARCHIVE_FINISHED", //       = 132;
+        "SET_AUTOFILLABLE" //                = 133;
     };
 
     // If the site doesn't use the viewport meta tag to specify the viewport,
     // use DEFAULT_VIEWPORT_WIDTH as the default viewport width
-    static final int DEFAULT_VIEWPORT_WIDTH = 800;
+    static final int DEFAULT_VIEWPORT_WIDTH = 980;
 
     // normally we try to fit the content to the minimum preferred width
     // calculated by the Webkit. To avoid the bad behavior when some site's
@@ -702,49 +681,9 @@
     // the minimum preferred width is huge, an upper limit is needed.
     static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH;
 
-    // default scale limit. Depending on the display density
-    private static float DEFAULT_MAX_ZOOM_SCALE;
-    private static float DEFAULT_MIN_ZOOM_SCALE;
-    // scale limit, which can be set through viewport meta tag in the web page
-    private float mMaxZoomScale;
-    private float mMinZoomScale;
-    private boolean mMinZoomScaleFixed = true;
-
     // initial scale in percent. 0 means using default.
     private int mInitialScaleInPercent = 0;
 
-    // while in the zoom overview mode, the page's width is fully fit to the
-    // current window. The page is alive, in another words, you can click to
-    // follow the links. Double tap will toggle between zoom overview mode and
-    // the last zoom scale.
-    boolean mInZoomOverview = false;
-
-    // ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
-    // engadget always have wider mContentWidth no matter what viewport size is.
-    int mZoomOverviewWidth = DEFAULT_VIEWPORT_WIDTH;
-    float mTextWrapScale;
-
-    // default scale. Depending on the display density.
-    static int DEFAULT_SCALE_PERCENT;
-    private float mDefaultScale;
-
-    private static float MINIMUM_SCALE_INCREMENT = 0.01f;
-
-    // set to true temporarily during ScaleGesture triggered zoom
-    private boolean mPreviewZoomOnly = false;
-
-    // computed scale and inverse, from mZoomWidth.
-    private float mActualScale;
-    private float mInvActualScale;
-    // if this is non-zero, it is used on drawing rather than mActualScale
-    private float mZoomScale;
-    private float mInvInitialZoomScale;
-    private float mInvFinalZoomScale;
-    private int mInitialScrollX;
-    private int mInitialScrollY;
-    private long mZoomStart;
-    private static final int ZOOM_ANIMATION_LENGTH = 500;
-
     private boolean mUserScroll = false;
 
     private int mSnapScrollMode = SNAP_NONE;
@@ -768,31 +707,31 @@
     private int mHorizontalScrollBarMode = SCROLLBAR_AUTO;
     private int mVerticalScrollBarMode = SCROLLBAR_AUTO;
 
-    /**
-     * Max distance to overscroll by in pixels.
-     * This how far content can be pulled beyond its normal bounds by the user.
-     */
-    private int mOverscrollDistance;
+    // constants for determining script injection strategy
+    private static final int ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED = -1;
+    private static final int ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT = 0;
+    private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1;
 
-    /**
-     * Max distance to overfling by in pixels.
-     * This is how far flinged content can move beyond the end of its normal bounds.
-     */
-    private int mOverflingDistance;
+    // the alias via which accessibility JavaScript interface is exposed
+    private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE = "accessibility";
 
-    /*
-     * These manage the edge glow effect when flung or pulled beyond the edges.
-     * If one is not null, all are not null. Checking one for null is as good as checking each.
-     */
-    private EdgeGlow mEdgeGlowTop;
-    private EdgeGlow mEdgeGlowBottom;
-    private EdgeGlow mEdgeGlowLeft;
-    private EdgeGlow mEdgeGlowRight;
-    /*
-     * These manage the delta the user has pulled beyond the edges.
-     */
-    private int mOverscrollDeltaX;
-    private int mOverscrollDeltaY;
+    // JavaScript to inject the script chooser which will
+    // pick the right script for the current URL
+    private static final String ACCESSIBILITY_SCRIPT_CHOOSER_JAVASCRIPT =
+        "javascript:(function() {" +
+        "    var chooser = document.createElement('script');" +
+        "    chooser.type = 'text/javascript';" +
+        "    chooser.src = 'https://ssl.gstatic.com/accessibility/javascript/android/AndroidScriptChooser.user.js';" +
+        "    document.getElementsByTagName('head')[0].appendChild(chooser);" +
+        "  })();";
+
+    // Regular expression that matches the "axs" URL parameter.
+    // The value of 0 means the accessibility script is opted out
+    // The value of 1 means the accessibility script is already injected
+    private static final String PATTERN_MATCH_AXS_URL_PARAMETER = "(\\?axs=(0|1))|(&axs=(0|1))";
+
+    // variable to cache the above pattern in case accessibility is enabled.
+    private Pattern mMatchAxsUrlParameterPattern;
 
     // Used to match key downs and key ups
     private boolean mGotKeyDown;
@@ -802,6 +741,8 @@
     // for event log
     private long mLastTouchUpTime = 0;
 
+    private WebViewCore.AutoFillData mAutoFillData;
+
     /**
      * URI scheme for telephone number
      */
@@ -898,43 +839,6 @@
         }
     }
 
-    // The View containing the zoom controls
-    private ExtendedZoomControls mZoomControls;
-    private Runnable mZoomControlRunnable;
-
-    // mZoomButtonsController will be lazy initialized in
-    // getZoomButtonsController() to get better performance.
-    private ZoomButtonsController mZoomButtonsController;
-
-    // These keep track of the center point of the zoom.  They are used to
-    // determine the point around which we should zoom.
-    private float mZoomCenterX;
-    private float mZoomCenterY;
-
-    private ZoomButtonsController.OnZoomListener mZoomListener =
-            new ZoomButtonsController.OnZoomListener() {
-
-        public void onVisibilityChanged(boolean visible) {
-            if (visible) {
-                switchOutDrawHistory();
-                // Bring back the hidden zoom controls.
-                mZoomButtonsController.getZoomControls().setVisibility(
-                        View.VISIBLE);
-                updateZoomButtonsEnabled();
-            }
-        }
-
-        public void onZoom(boolean zoomIn) {
-            if (zoomIn) {
-                zoomIn();
-            } else {
-                zoomOut();
-            }
-
-            updateZoomButtonsEnabled();
-        }
-    };
-
     /**
      * Construct a new WebView with a Context object.
      * @param context A Context object used to access application assets.
@@ -959,7 +863,18 @@
      * @param defStyle The default style resource ID.
      */
     public WebView(Context context, AttributeSet attrs, int defStyle) {
-        this(context, attrs, defStyle, null);
+        this(context, attrs, defStyle, false);
+    }
+
+    /**
+     * Construct a new WebView with layout parameters and a default style.
+     * @param context A Context object used to access application assets.
+     * @param attrs An AttributeSet passed to our parent.
+     * @param defStyle The default style resource ID.
+     */
+    public WebView(Context context, AttributeSet attrs, int defStyle,
+            boolean privateBrowsing) {
+        this(context, attrs, defStyle, null, privateBrowsing);
     }
 
     /**
@@ -970,53 +885,136 @@
      * @param context A Context object used to access application assets.
      * @param attrs An AttributeSet passed to our parent.
      * @param defStyle The default style resource ID.
-     * @param javascriptInterfaces is a Map of intareface names, as keys, and
+     * @param javascriptInterfaces is a Map of interface names, as keys, and
      * object implementing those interfaces, as values.
      * @hide pending API council approval.
      */
     protected WebView(Context context, AttributeSet attrs, int defStyle,
-            Map<String, Object> javascriptInterfaces) {
+            Map<String, Object> javascriptInterfaces, boolean privateBrowsing) {
         super(context, attrs, defStyle);
-        init();
+
+        if (AccessibilityManager.getInstance(context).isEnabled()) {
+            if (javascriptInterfaces == null) {
+                javascriptInterfaces = new HashMap<String, Object>();
+            }
+            exposeAccessibilityJavaScriptApi(javascriptInterfaces);
+        }
 
         mCallbackProxy = new CallbackProxy(context, this);
         mViewManager = new ViewManager(this);
         mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
         mDatabase = WebViewDatabase.getInstance(context);
-        mScroller = new OverScroller(context);
+        mScroller = new Scroller(context);
+        mZoomManager = new ZoomManager(this, mCallbackProxy);
 
+        /* The init method must follow the creation of certain member variables,
+         * such as the mZoomManager.
+         */
+        init();
+        setupPackageListener(context);
         updateMultiTouchSupport(context);
+
+        if (privateBrowsing) {
+            startPrivateBrowsing();
+        }
+
+        L10nUtils.loadStrings(context);
+    }
+
+    /*
+     * The intent receiver that monitors for changes to relevant packages (e.g.,
+     * sGoogleApps) and notifies WebViewCore of their existence.
+     */
+    private static BroadcastReceiver sPackageInstallationReceiver = null;
+
+    /*
+     * A set of Google packages we monitor for the
+     * navigator.isApplicationInstalled() API. Add additional packages as
+     * needed.
+     */
+    private static Set<String> sGoogleApps;
+    static {
+        sGoogleApps = new HashSet<String>();
+        sGoogleApps.add("com.google.android.youtube");
+    }
+
+    private void setupPackageListener(Context context) {
+
+        /*
+         * we must synchronize the instance check and the creation of the
+         * receiver to ensure that only ONE receiver exists for all WebView
+         * instances.
+         */
+        synchronized (WebView.class) {
+
+            // if the receiver already exists then we do not need to register it
+            // again
+            if (sPackageInstallationReceiver != null) {
+                return;
+            }
+
+            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addDataScheme("package");
+            sPackageInstallationReceiver = new BroadcastReceiver() {
+
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    final String action = intent.getAction();
+                    final String packageName = intent.getData().getSchemeSpecificPart();
+                    final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                    if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
+                        // if it is replacing, refreshPlugins() when adding
+                        return;
+                    }
+
+                    if (sGoogleApps.contains(packageName) && mWebViewCore != null) {
+                        if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                            mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAME, packageName);
+                        } else {
+                            mWebViewCore.sendMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
+                        }
+                    }
+
+                    PluginManager pm = PluginManager.getInstance(context);
+                    if (pm.containsPluginPermissionAndSignatures(packageName)) {
+                        pm.refreshPlugins(Intent.ACTION_PACKAGE_ADDED.equals(action));
+                    }
+                }
+            };
+
+            context.getApplicationContext().registerReceiver(sPackageInstallationReceiver, filter);
+        }
+
+        // check if any of the monitored apps are already installed
+        AsyncTask<Void, Void, Set<String>> task = new AsyncTask<Void, Void, Set<String>>() {
+
+            @Override
+            protected Set<String> doInBackground(Void... unused) {
+                Set<String> installedPackages = new HashSet<String>();
+                PackageManager pm = mContext.getPackageManager();
+                List<PackageInfo> packages = pm.getInstalledPackages(0);
+                for (PackageInfo p : packages) {
+                    if (sGoogleApps.contains(p.packageName)) {
+                        installedPackages.add(p.packageName);
+                    }
+                }
+                return installedPackages;
+            }
+
+            // Executes on the UI thread
+            @Override
+            protected void onPostExecute(Set<String> installedPackages) {
+                if (mWebViewCore != null) {
+                    mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, installedPackages);
+                }
+            }
+        };
+        task.execute();
     }
 
     void updateMultiTouchSupport(Context context) {
-        WebSettings settings = getSettings();
-        final PackageManager pm = context.getPackageManager();
-        mSupportMultiTouch = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
-                && settings.supportZoom() && settings.getBuiltInZoomControls();
-        mAllowPanAndScale = pm.hasSystemFeature(
-                PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
-        if (mSupportMultiTouch && (mScaleDetector == null)) {
-            mScaleDetector = new ScaleGestureDetector(context,
-                    new ScaleDetectorListener());
-        } else if (!mSupportMultiTouch && (mScaleDetector != null)) {
-            mScaleDetector = null;
-        }
-    }
-
-    private void updateZoomButtonsEnabled() {
-        if (mZoomButtonsController == null) return;
-        boolean canZoomIn = mActualScale < mMaxZoomScale;
-        boolean canZoomOut = mActualScale > mMinZoomScale && !mInZoomOverview;
-        if (!canZoomIn && !canZoomOut) {
-            // Hide the zoom in and out buttons, as well as the fit to page
-            // button, if the page cannot zoom
-            mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
-        } else {
-            // Set each one individually, as a page may be able to zoom in
-            // or out.
-            mZoomButtonsController.setZoomInEnabled(canZoomIn);
-            mZoomButtonsController.setZoomOutEnabled(canZoomOut);
-        }
+        mZoomManager.updateMultiTouchSupport(context);
     }
 
     private void init() {
@@ -1036,57 +1034,38 @@
         // use one line height, 16 based on our current default font, for how
         // far we allow a touch be away from the edge of a link
         mNavSlop = (int) (16 * density);
-        // density adjusted scale factors
-        DEFAULT_SCALE_PERCENT = (int) (100 * density);
-        mDefaultScale = density;
-        mActualScale = density;
-        mInvActualScale = 1 / density;
-        mTextWrapScale = density;
-        DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
-        DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
-        mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
-        mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
+        mZoomManager.init(density);
         mMaximumFling = configuration.getScaledMaximumFlingVelocity();
-        mOverscrollDistance = configuration.getScaledOverscrollDistance();
-        mOverflingDistance = configuration.getScaledOverflingDistance();
+
+        // Compute the inverse of the density squared.
+        DRAG_LAYER_INVERSE_DENSITY_SQUARED = 1 / (density * density);
     }
 
-    @Override
-    public void setOverScrollMode(int mode) {
-        super.setOverScrollMode(mode);
-        if (mode != OVER_SCROLL_NEVER) {
-            if (mEdgeGlowTop == null) {
-                final Resources res = getContext().getResources();
-                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
-                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
-                mEdgeGlowTop = new EdgeGlow(edge, glow);
-                mEdgeGlowBottom = new EdgeGlow(edge, glow);
-                mEdgeGlowLeft = new EdgeGlow(edge, glow);
-                mEdgeGlowRight = new EdgeGlow(edge, glow);
-            }
-        } else {
-            mEdgeGlowTop = null;
-            mEdgeGlowBottom = null;
-            mEdgeGlowLeft = null;
-            mEdgeGlowRight = null;
+    /**
+     * Exposes accessibility APIs to JavaScript by appending them to the JavaScript
+     * interfaces map provided by the WebView client. In case of conflicting
+     * alias with the one of the accessibility API the user specified one wins.
+     *
+     * @param javascriptInterfaces A map with interfaces to be exposed to JavaScript.
+     */
+    private void exposeAccessibilityJavaScriptApi(Map<String, Object> javascriptInterfaces) {
+        if (javascriptInterfaces.containsKey(ALIAS_ACCESSIBILITY_JS_INTERFACE)) {
+            Log.w(LOGTAG, "JavaScript interface mapped to \"" + ALIAS_ACCESSIBILITY_JS_INTERFACE
+                    + "\" overrides the accessibility API JavaScript interface. No accessibility"
+                    + "API will be exposed to JavaScript!");
+            return;
         }
+
+        // expose the TTS for now ...
+        javascriptInterfaces.put(ALIAS_ACCESSIBILITY_JS_INTERFACE,
+                new TextToSpeech(getContext(), null));
     }
 
     /* package */void updateDefaultZoomDensity(int zoomDensity) {
-        final float density = getContext().getResources().getDisplayMetrics().density
+        final float density = mContext.getResources().getDisplayMetrics().density
                 * 100 / zoomDensity;
-        if (Math.abs(density - mDefaultScale) > 0.01) {
-            float scaleFactor = density / mDefaultScale;
-            // adjust the limits
-            mNavSlop = (int) (16 * density);
-            DEFAULT_SCALE_PERCENT = (int) (100 * density);
-            DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
-            DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
-            mDefaultScale = density;
-            mMaxZoomScale *= scaleFactor;
-            mMinZoomScale *= scaleFactor;
-            setNewZoomScale(mActualScale * scaleFactor, true, false);
-        }
+        mNavSlop = (int) (16 * density);
+        mZoomManager.updateDefaultZoomDensity(density);
     }
 
     /* package */ boolean onSavePassword(String schemePlusHost, String username,
@@ -1203,16 +1182,17 @@
      * returns the height of the titlebarview (if any). Does not care about
      * scrolling
      */
-    private int getTitleHeight() {
+    int getTitleHeight() {
         return mTitleBar != null ? mTitleBar.getHeight() : 0;
     }
 
     /*
      * Return the amount of the titlebarview (if any) that is visible
+     *
+     * @hide
      */
-    private int getVisibleTitleHeight() {
-        // need to restrict mScrollY due to over scroll
-        return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0);
+    public int getVisibleTitleHeight() {
+        return Math.max(getTitleHeight() - mScrollY, 0);
     }
 
     /*
@@ -1293,9 +1273,27 @@
         return mDatabase.getHttpAuthUsernamePassword(host, realm);
     }
 
+    /**
+     * Remove Find or Select ActionModes, if active.
+     */
+    private void clearActionModes() {
+        if (mSelectCallback != null) {
+            mSelectCallback.finish();
+        }
+        if (mFindCallback != null) {
+            mFindCallback.finish();
+        }
+    }
+
+    /**
+     * Called to clear state when moving from one page to another, or changing
+     * in some other way that makes elements associated with the current page
+     * (such as WebTextView or ActionModes) no longer relevant.
+     */
     private void clearHelpers() {
-        clearTextEntry(false);
-        selectionDone();
+        clearTextEntry();
+        clearActionModes();
+        dismissFullScreenMode();
     }
 
     /**
@@ -1436,6 +1434,8 @@
             outState.putBundle("certificate",
                                SslCertificate.saveState(mCertificate));
         }
+        outState.putBoolean("privateBrowsingEnabled", isPrivateBrowsingEnabled());
+        mZoomManager.saveZoomState(outState);
         return list;
     }
 
@@ -1481,29 +1481,23 @@
         // now update the bundle
         b.putInt("scrollX", mScrollX);
         b.putInt("scrollY", mScrollY);
-        b.putFloat("scale", mActualScale);
-        b.putFloat("textwrapScale", mTextWrapScale);
-        b.putBoolean("overview", mInZoomOverview);
+        mZoomManager.saveZoomState(b);
         return true;
     }
 
     private void restoreHistoryPictureFields(Picture p, Bundle b) {
         int sx = b.getInt("scrollX", 0);
         int sy = b.getInt("scrollY", 0);
-        float scale = b.getFloat("scale", 1.0f);
+
         mDrawHistory = true;
         mHistoryPicture = p;
         mScrollX = sx;
         mScrollY = sy;
+        mZoomManager.restoreZoomState(b);
+        final float scale = mZoomManager.getScale();
         mHistoryWidth = Math.round(p.getWidth() * scale);
         mHistoryHeight = Math.round(p.getHeight() * scale);
-        // as getWidth() / getHeight() of the view are not available yet, set up
-        // mActualScale, so that when onSizeChanged() is called, the rest will
-        // be set correctly
-        mActualScale = scale;
-        mInvActualScale = 1 / scale;
-        mTextWrapScale = b.getFloat("textwrapScale", scale);
-        mInZoomOverview = b.getBoolean("overview");
+
         invalidate();
     }
 
@@ -1604,6 +1598,11 @@
                 // Update the copy to have the correct index.
                 returnList.setCurrentIndex(index);
             }
+            // Restore private browsing setting.
+            if (inState.getBoolean("privateBrowsingEnabled")) {
+                getSettings().setPrivateBrowsingEnabled(true);
+            }
+            mZoomManager.restoreZoomState(inState);
             // Remove all pending messages because we are restoring previous
             // state.
             mWebViewCore.removeMessages();
@@ -1715,6 +1714,45 @@
     }
 
     /**
+     * Saves the current view as a web archive.
+     *
+     * @param filename The filename where the archive should be placed.
+     */
+    public void saveWebArchive(String filename) {
+        saveWebArchive(filename, false, null);
+    }
+
+    /* package */ static class SaveWebArchiveMessage {
+        SaveWebArchiveMessage (String basename, boolean autoname, ValueCallback<String> callback) {
+            mBasename = basename;
+            mAutoname = autoname;
+            mCallback = callback;
+        }
+
+        /* package */ final String mBasename;
+        /* package */ final boolean mAutoname;
+        /* package */ final ValueCallback<String> mCallback;
+        /* package */ String mResultFile;
+    }
+
+    /**
+     * Saves the current view as a web archive.
+     *
+     * @param basename The filename where the archive should be placed.
+     * @param autoname If false, takes basename to be a file. If true, basename
+     *                 is assumed to be a directory in which a filename will be
+     *                 chosen according to the url of the current page.
+     * @param callback Called after the web archive has been saved. The
+     *                 parameter for onReceiveValue will either be the filename
+     *                 under which the file was saved, or null if saving the
+     *                 file failed.
+     */
+    public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
+        mWebViewCore.sendMessage(EventHub.SAVE_WEBARCHIVE,
+            new SaveWebArchiveMessage(basename, autoname, callback));
+    }
+
+    /**
      * Stop the current load.
      */
     public void stopLoading() {
@@ -1814,6 +1852,48 @@
         }
     }
 
+    /**
+     * Returns true if private browsing is enabled in this WebView.
+     */
+    public boolean isPrivateBrowsingEnabled() {
+        return getSettings().isPrivateBrowsingEnabled();
+    }
+
+    private void startPrivateBrowsing() {
+        boolean wasPrivateBrowsingEnabled = isPrivateBrowsingEnabled();
+
+        getSettings().setPrivateBrowsingEnabled(true);
+
+        if (!wasPrivateBrowsingEnabled) {
+            loadUrl("browser:incognito");
+        }
+    }
+
+    /**
+     * Deletes any files that were created as a part of the last private
+     * browsing session and clears any internal state associated with that
+     * session. The consequences of calling this method while a private
+     * browsing session is active are unspecified.
+     * @param context The same context which was used to create the private
+     *                browsing WebView.
+     * @return True if the private browsing files were successfully deleted,
+     *         false otherwise.
+     * @hide pending API council approval.
+     */
+    public static boolean cleanupPrivateBrowsingFiles(Context context) {
+        // It seems wrong that we have to pass the storage locations here, given
+        // that the storage files are created native-side in WebRequestContext
+        // (albeit using a dumb getter on BrowserFrame to get the paths from
+        // Java). It looks like this is required because we may need to call
+        // this method before the BrowserFrame has been set up.
+        // TODO: Investigate whether this can be avoided.
+        return nativeCleanupPrivateBrowsingFiles(context.getDatabasePath("dummy").getParent(),
+                                                 context.getCacheDir().getAbsolutePath());
+    }
+
+    private static native boolean nativeCleanupPrivateBrowsingFiles(String databaseDirectory,
+                                                                    String cacheDirectory);
+
     private boolean extendScroll(int y) {
         int finalY = mScroller.getFinalY();
         int newY = pinLocY(finalY + y);
@@ -1861,7 +1941,7 @@
         }
         nativeClearCursor(); // start next trackball movement from page edge
         if (bottom) {
-            return pinScrollTo(mScrollX, computeRealVerticalScrollRange(), true, 0);
+            return pinScrollTo(mScrollX, computeVerticalScrollRange(), true, 0);
         }
         // Page down.
         int h = getHeight();
@@ -1883,6 +1963,7 @@
     public void clearView() {
         mContentWidth = 0;
         mContentHeight = 0;
+        setBaseLayer(0, null);
         mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
     }
 
@@ -1896,8 +1977,10 @@
      *         bounds of the view.
      */
     public Picture capturePicture() {
-        if (null == mWebViewCore) return null; // check for out of memory tab
-        return mWebViewCore.copyContentPicture();
+        if (mNativeClass == 0) return null;
+        Picture result = new Picture();
+        nativeCopyBaseContentToPicture(result);
+        return result;
     }
 
     /**
@@ -1909,15 +1992,13 @@
 
     /**
      * Remove the WebTextView.
-     * @param disableFocusController If true, send a message to webkit
-     *     disabling the focus controller, so the caret stops blinking.
      */
-    private void clearTextEntry(boolean disableFocusController) {
+    private void clearTextEntry() {
         if (inEditingMode()) {
             mWebTextView.remove();
-            if (disableFocusController) {
-                setFocusControllerInactive();
-            }
+        } else {
+            // The keyboard may be open with the WebView as the served view
+            hideSoftKeyboard();
         }
     }
 
@@ -1926,7 +2007,7 @@
      * @return The current scale.
      */
     public float getScale() {
-        return mActualScale;
+        return mZoomManager.getScale();
     }
 
     /**
@@ -1938,7 +2019,7 @@
      * @param scaleInPercent The initial scale in percent.
      */
     public void setInitialScale(int scaleInPercent) {
-        mInitialScaleInPercent = scaleInPercent;
+        mZoomManager.setInitialScaleInPercent(scaleInPercent);
     }
 
     /**
@@ -1951,14 +2032,8 @@
             Log.w(LOGTAG, "This WebView doesn't support zoom.");
             return;
         }
-        clearTextEntry(false);
-        if (getSettings().getBuiltInZoomControls()) {
-            getZoomButtonsController().setVisible(true);
-        } else {
-            mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-            mPrivateHandler.postDelayed(mZoomControlRunnable,
-                    ZOOM_CONTROLS_TIMEOUT);
-        }
+        clearHelpers();
+        mZoomManager.invokeZoomPicker();
     }
 
     /**
@@ -2073,7 +2148,7 @@
         msg.sendToTarget();
     }
 
-    private static int pinLoc(int x, int viewMax, int docMax) {
+    static int pinLoc(int x, int viewMax, int docMax) {
 //        Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
         if (docMax < viewMax) {   // the doc has room on the sides for "blank"
             // pin the short document to the top/left of the screen
@@ -2090,16 +2165,14 @@
     }
 
     // Expects x in view coordinates
-    private int pinLocX(int x) {
-        if (mInOverScrollMode) return x;
-        return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange());
+    int pinLocX(int x) {
+        return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
     }
 
     // Expects y in view coordinates
-    private int pinLocY(int y) {
-        if (mInOverScrollMode) return y;
+    int pinLocY(int y) {
         return pinLoc(y, getViewHeightWithTitle(),
-                      computeRealVerticalScrollRange() + getTitleHeight());
+                      computeVerticalScrollRange() + getTitleHeight());
     }
 
     /**
@@ -2124,6 +2197,16 @@
      * @hide
      */
     public void setEmbeddedTitleBar(View v) {
+        if (null == v) {
+            // If one of our callbacks is holding onto the titlebar to replace
+            // it when its ActionMode ends, remove it.
+            if (mSelectCallback != null) {
+                mSelectCallback.setTitleBar(null);
+            }
+            if (mFindCallback != null) {
+                mFindCallback.setTitleBar(null);
+            }
+        }
         if (mTitleBar == v) return;
         if (mTitleBar != null) {
             removeView(mTitleBar);
@@ -2147,7 +2230,7 @@
      * height.
      */
     private int viewToContentDimension(int d) {
-        return Math.round(d * mInvActualScale);
+        return Math.round(d * mZoomManager.getInvScale());
     }
 
     /**
@@ -2173,7 +2256,7 @@
      * Returns the result as a float.
      */
     private float viewToContentXf(int x) {
-        return x * mInvActualScale;
+        return x * mZoomManager.getInvScale();
     }
 
     /**
@@ -2182,7 +2265,7 @@
      * embedded into the WebView. Returns the result as a float.
      */
     private float viewToContentYf(int y) {
-        return (y - getTitleHeight()) * mInvActualScale;
+        return (y - getTitleHeight()) * mZoomManager.getInvScale();
     }
 
     /**
@@ -2192,7 +2275,7 @@
      * height.
      */
     /*package*/ int contentToViewDimension(int d) {
-        return Math.round(d * mActualScale);
+        return Math.round(d * mZoomManager.getScale());
     }
 
     /**
@@ -2233,7 +2316,7 @@
     // Called by JNI to invalidate the View, given rectangle coordinates in
     // content space
     private void viewInvalidate(int l, int t, int r, int b) {
-        final float scale = mActualScale;
+        final float scale = mZoomManager.getScale();
         final int dy = getTitleHeight();
         invalidate((int)Math.floor(l * scale),
                    (int)Math.floor(t * scale) + dy,
@@ -2244,7 +2327,7 @@
     // Called by JNI to invalidate the View after a delay, given rectangle
     // coordinates in content space
     private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
-        final float scale = mActualScale;
+        final float scale = mZoomManager.getScale();
         final int dy = getTitleHeight();
         postInvalidateDelayed(delay,
                               (int)Math.floor(l * scale),
@@ -2282,13 +2365,7 @@
             // updated when we get out of that mode.
             if (!mDrawHistory) {
                 // repin our scroll, taking into account the new content size
-                int oldX = mScrollX;
-                int oldY = mScrollY;
-                mScrollX = pinLocX(mScrollX);
-                mScrollY = pinLocY(mScrollY);
-                if (oldX != mScrollX || oldY != mScrollY) {
-                    onScrollChanged(mScrollX, mScrollY, oldX, oldY);
-                }
+                updateScrollCoordinates(pinLocX(mScrollX), pinLocY(mScrollY));
                 if (!mScroller.isFinished()) {
                     // We are in the middle of a scroll.  Repin the final scroll
                     // position.
@@ -2300,77 +2377,12 @@
         contentSizeChanged(updateLayout);
     }
 
-    private void setNewZoomScale(float scale, boolean updateTextWrapScale,
-            boolean force) {
-        if (scale < mMinZoomScale) {
-            scale = mMinZoomScale;
-            // set mInZoomOverview for non mobile sites
-            if (scale < mDefaultScale) mInZoomOverview = true;
-        } else if (scale > mMaxZoomScale) {
-            scale = mMaxZoomScale;
-        }
-        if (updateTextWrapScale) {
-            mTextWrapScale = scale;
-            // reset mLastHeightSent to force VIEW_SIZE_CHANGED sent to WebKit
-            mLastHeightSent = 0;
-        }
-        if (scale != mActualScale || force) {
-            if (mDrawHistory) {
-                // If history Picture is drawn, don't update scroll. They will
-                // be updated when we get out of that mode.
-                if (scale != mActualScale && !mPreviewZoomOnly) {
-                    mCallbackProxy.onScaleChanged(mActualScale, scale);
-                }
-                mActualScale = scale;
-                mInvActualScale = 1 / scale;
-                sendViewSizeZoom();
-            } else {
-                // update our scroll so we don't appear to jump
-                // i.e. keep the center of the doc in the center of the view
-
-                int oldX = mScrollX;
-                int oldY = mScrollY;
-                float ratio = scale * mInvActualScale;   // old inverse
-                float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
-                float sy = ratio * oldY + (ratio - 1)
-                        * (mZoomCenterY - getTitleHeight());
-
-                // now update our new scale and inverse
-                if (scale != mActualScale && !mPreviewZoomOnly) {
-                    mCallbackProxy.onScaleChanged(mActualScale, scale);
-                }
-                mActualScale = scale;
-                mInvActualScale = 1 / scale;
-
-                // Scale all the child views
-                mViewManager.scaleAll();
-
-                // as we don't have animation for scaling, don't do animation
-                // for scrolling, as it causes weird intermediate state
-                //        pinScrollTo(Math.round(sx), Math.round(sy));
-                mScrollX = pinLocX(Math.round(sx));
-                mScrollY = pinLocY(Math.round(sy));
-
-                // update webkit
-                if (oldX != mScrollX || oldY != mScrollY) {
-                    onScrollChanged(mScrollX, mScrollY, oldX, oldY);
-                } else {
-                    // the scroll position is adjusted at the beginning of the
-                    // zoom animation. But we want to update the WebKit at the
-                    // end of the zoom animation. See comments in onScaleEnd().
-                    sendOurVisibleRect();
-                }
-                sendViewSizeZoom();
-            }
-        }
-    }
-
     // Used to avoid sending many visible rect messages.
     private Rect mLastVisibleRectSent;
     private Rect mLastGlobalRect;
 
-    private Rect sendOurVisibleRect() {
-        if (mPreviewZoomOnly) return mLastVisibleRectSent;
+    Rect sendOurVisibleRect() {
+        if (mZoomManager.isPreventingWebkitUpdates()) return mLastVisibleRectSent;
 
         Rect rect = new Rect();
         calcOurContentVisibleRect(rect);
@@ -2378,7 +2390,7 @@
         if (!rect.equals(mLastVisibleRectSent)) {
             Point pos = new Point(rect.left, rect.top);
             mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
-                    nativeMoveGeneration(), 0, pos);
+                    nativeMoveGeneration(), mUserScroll ? 1 : 0, pos);
             mLastVisibleRectSent = rect;
         }
         Rect globalRect = new Rect();
@@ -2403,15 +2415,12 @@
         Point p = new Point();
         getGlobalVisibleRect(r, p);
         r.offset(-p.x, -p.y);
-        if (mFindIsUp) {
-            r.bottom -= mFindHeight;
-        }
     }
 
     // Sets r to be our visible rectangle in content coordinates
     private void calcOurContentVisibleRect(Rect r) {
         calcOurVisibleRect(r);
-        // since we might overscroll, pin the rect to the bounds of the content
+        // pin the rect to the bounds of the content
         r.left = Math.max(viewToContentX(r.left), 0);
         // viewToContentY will remove the total height of the title bar.  Add
         // the visible height back in to account for the fact that if the title
@@ -2452,16 +2461,19 @@
 
     /**
      * Compute unzoomed width and height, and if they differ from the last
-     * values we sent, send them to webkit (to be used has new viewport)
+     * values we sent, send them to webkit (to be used as new viewport)
+     *
+     * @param force ensures that the message is sent to webkit even if the width
+     * or height has not changed since the last message
      *
      * @return true if new values were sent
      */
-    private boolean sendViewSizeZoom() {
-        if (mPreviewZoomOnly) return false;
+    boolean sendViewSizeZoom(boolean force) {
+        if (mZoomManager.isPreventingWebkitUpdates()) return false;
 
         int viewWidth = getViewWidth();
-        int newWidth = Math.round(viewWidth * mInvActualScale);
-        int newHeight = Math.round(getViewHeight() * mInvActualScale);
+        int newWidth = Math.round(viewWidth * mZoomManager.getInvScale());
+        int newHeight = Math.round((getViewHeightWithTitle() - getTitleHeight()) * mZoomManager.getInvScale());
         /*
          * Because the native side may have already done a layout before the
          * View system was able to measure us, we have to send a height of 0 to
@@ -2474,85 +2486,51 @@
             newHeight = 0;
         }
         // Avoid sending another message if the dimensions have not changed.
-        if (newWidth != mLastWidthSent || newHeight != mLastHeightSent) {
+        if (newWidth != mLastWidthSent || newHeight != mLastHeightSent || force) {
             ViewSizeData data = new ViewSizeData();
             data.mWidth = newWidth;
             data.mHeight = newHeight;
-            data.mTextWrapWidth = Math.round(viewWidth / mTextWrapScale);;
-            data.mScale = mActualScale;
-            data.mIgnoreHeight = mZoomScale != 0 && !mHeightCanMeasure;
-            data.mAnchorX = mAnchorX;
-            data.mAnchorY = mAnchorY;
+            data.mTextWrapWidth = Math.round(viewWidth / mZoomManager.getTextWrapScale());
+            data.mScale = mZoomManager.getScale();
+            data.mIgnoreHeight = mZoomManager.isFixedLengthAnimationInProgress()
+                    && !mHeightCanMeasure;
+            data.mAnchorX = mZoomManager.getDocumentAnchorX();
+            data.mAnchorY = mZoomManager.getDocumentAnchorY();
             mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
             mLastWidthSent = newWidth;
             mLastHeightSent = newHeight;
-            mAnchorX = mAnchorY = 0;
+            mZoomManager.clearDocumentAnchor();
             return true;
         }
         return false;
     }
 
-    private int computeRealHorizontalScrollRange() {
+    @Override
+    protected int computeHorizontalScrollRange() {
         if (mDrawHistory) {
             return mHistoryWidth;
         } else if (mHorizontalScrollBarMode == SCROLLBAR_ALWAYSOFF
-                && (mActualScale - mMinZoomScale <= MINIMUM_SCALE_INCREMENT)) {
+                && !mZoomManager.canZoomOut()) {
             // only honor the scrollbar mode when it is at minimum zoom level
             return computeHorizontalScrollExtent();
         } else {
             // to avoid rounding error caused unnecessary scrollbar, use floor
-            return (int) Math.floor(mContentWidth * mActualScale);
-        }
-    }
-
-    @Override
-    protected int computeHorizontalScrollRange() {
-        int range = computeRealHorizontalScrollRange();
-
-        // Adjust reported range if overscrolled to compress the scroll bars
-        final int scrollX = mScrollX;
-        final int overscrollRight = computeMaxScrollX();
-        if (scrollX < 0) {
-            range -= scrollX;
-        } else if (scrollX > overscrollRight) {
-            range += scrollX - overscrollRight;
-        }
-
-        return range;
-    }
-
-    @Override
-    protected int computeHorizontalScrollOffset() {
-        return Math.max(mScrollX, 0);
-    }
-
-    private int computeRealVerticalScrollRange() {
-        if (mDrawHistory) {
-            return mHistoryHeight;
-        } else if (mVerticalScrollBarMode == SCROLLBAR_ALWAYSOFF
-                && (mActualScale - mMinZoomScale <= MINIMUM_SCALE_INCREMENT)) {
-            // only honor the scrollbar mode when it is at minimum zoom level
-            return computeVerticalScrollExtent();
-        } else {
-            // to avoid rounding error caused unnecessary scrollbar, use floor
-            return (int) Math.floor(mContentHeight * mActualScale);
+            return (int) Math.floor(mContentWidth * mZoomManager.getScale());
         }
     }
 
     @Override
     protected int computeVerticalScrollRange() {
-        int range = computeRealVerticalScrollRange();
-
-        // Adjust reported range if overscrolled to compress the scroll bars
-        final int scrollY = mScrollY;
-        final int overscrollBottom = computeMaxScrollY();
-        if (scrollY < 0) {
-            range -= scrollY;
-        } else if (scrollY > overscrollBottom) {
-            range += scrollY - overscrollBottom;
+        if (mDrawHistory) {
+            return mHistoryHeight;
+        } else if (mVerticalScrollBarMode == SCROLLBAR_ALWAYSOFF
+                && !mZoomManager.canZoomOut()) {
+            // only honor the scrollbar mode when it is at minimum zoom level
+            return computeVerticalScrollExtent();
+        } else {
+            // to avoid rounding error caused unnecessary scrollbar, use floor
+            return (int) Math.floor(mContentHeight * mZoomManager.getScale());
         }
-
-        return range;
     }
 
     @Override
@@ -2570,73 +2548,10 @@
     protected void onDrawVerticalScrollBar(Canvas canvas,
                                            Drawable scrollBar,
                                            int l, int t, int r, int b) {
-        if (mScrollY < 0) {
-            t -= mScrollY;
-        }
         scrollBar.setBounds(l, t + getVisibleTitleHeight(), r, b);
         scrollBar.draw(canvas);
     }
 
-    @Override
-    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
-            boolean clampedY) {
-        mInOverScrollMode = false;
-        int maxX = computeMaxScrollX();
-        int maxY = computeMaxScrollY();
-        if (maxX == 0) {
-            // do not over scroll x if the page just fits the screen
-            scrollX = pinLocX(scrollX);
-        } else if (scrollX < 0 || scrollX > maxX) {
-            mInOverScrollMode = true;
-        }
-        if (scrollY < 0 || scrollY > maxY) {
-            mInOverScrollMode = true;
-        }
-
-        int oldX = mScrollX;
-        int oldY = mScrollY;
-
-        super.scrollTo(scrollX, scrollY);
-
-        // Only show overscroll bars if there was no movement in any direction
-        // as a result of scrolling.
-        if (mEdgeGlowTop != null && oldY == mScrollY && oldX == mScrollX) {
-            // Don't show left/right glows if we fit the whole content.
-            // Also don't show if there was vertical movement.
-            if (maxX > 0) {
-                final int pulledToX = oldX + mOverscrollDeltaX;
-                if (pulledToX < 0) {
-                    mEdgeGlowLeft.onPull((float) mOverscrollDeltaX / getWidth());
-                    if (!mEdgeGlowRight.isFinished()) {
-                        mEdgeGlowRight.onRelease();
-                    }
-                } else if (pulledToX > maxX) {
-                    mEdgeGlowRight.onPull((float) mOverscrollDeltaX / getWidth());
-                    if (!mEdgeGlowLeft.isFinished()) {
-                        mEdgeGlowLeft.onRelease();
-                    }
-                }
-                mOverscrollDeltaX = 0;
-            }
-
-            if (maxY > 0 || getOverScrollMode() == OVER_SCROLL_ALWAYS) {
-                final int pulledToY = oldY + mOverscrollDeltaY;
-                if (pulledToY < 0) {
-                    mEdgeGlowTop.onPull((float) mOverscrollDeltaY / getHeight());
-                    if (!mEdgeGlowBottom.isFinished()) {
-                        mEdgeGlowBottom.onRelease();
-                    }
-                } else if (pulledToY > maxY) {
-                    mEdgeGlowBottom.onPull((float) mOverscrollDeltaY / getHeight());
-                    if (!mEdgeGlowTop.isFinished()) {
-                        mEdgeGlowTop.onRelease();
-                    }
-                }
-                mOverscrollDeltaY = 0;
-            }
-        }
-    }
-
     /**
      * Get the url for the current page. This is not always the same as the url
      * passed to WebViewClient.onPageStarted because although the load for
@@ -2682,7 +2597,9 @@
     }
 
     /**
-     * Get the touch icon url for the apple-touch-icon <link> element.
+     * Get the touch icon url for the apple-touch-icon <link> element, or
+     * a URL on this site's server pointing to the standard location of a
+     * touch icon.
      * @hide
      */
     public String getTouchIconUrl() {
@@ -2848,29 +2765,56 @@
     public int findAll(String find) {
         if (0 == mNativeClass) return 0; // client isn't initialized
         int result = find != null ? nativeFindAll(find.toLowerCase(),
-                find.toUpperCase()) : 0;
+                find.toUpperCase(), find.equalsIgnoreCase(mLastFind)) : 0;
         invalidate();
         mLastFind = find;
         return result;
     }
 
     /**
-     * @hide
+     * Start an ActionMode for finding text in this WebView.
+     * @param text If non-null, will be the initial text to search for.
+     *             Otherwise, the last String searched for in this WebView will
+     *             be used to start.
      */
-    public void setFindIsUp(boolean isUp) {
-        mFindIsUp = isUp;
-        if (isUp) {
-            recordNewContentSize(mContentWidth, mContentHeight + mFindHeight,
-                    false);
+    public void showFindDialog(String text) {
+        mFindCallback = new FindActionModeCallback(mContext);
+        setFindIsUp(true);
+        mFindCallback.setWebView(this);
+        View titleBar = mTitleBar;
+        // We do not want to show the embedded title bar during find or
+        // select, but keep track of it so that it can be replaced when the
+        // mode is exited.
+        setEmbeddedTitleBar(null);
+        mFindCallback.setTitleBar(titleBar);
+        startActionMode(mFindCallback);
+        if (text == null) {
+            text = mLastFind;
         }
+        if (text != null) {
+            mFindCallback.setText(text);
+        }
+    }
+
+    /**
+     * Keep track of the find callback so that we can remove its titlebar if
+     * necessary.
+     */
+    private FindActionModeCallback mFindCallback;
+
+    /**
+     * Toggle whether the find dialog is showing, for both native and Java.
+     */
+    private void setFindIsUp(boolean isUp) {
+        mFindIsUp = isUp;
         if (0 == mNativeClass) return; // client isn't initialized
         nativeSetFindIsUp(isUp);
     }
 
     /**
-     * @hide
+     * Return the index of the currently highlighted match.
      */
-    public int findIndex() {
+    int findIndex() {
         if (0 == mNativeClass) return -1;
         return nativeFindIndex();
     }
@@ -2879,9 +2823,8 @@
     // or not we draw the highlights for matches.
     private boolean mFindIsUp;
 
-    private int mFindHeight;
-    // Keep track of the last string sent, so we can search again after an
-    // orientation change or the dismissal of the soft keyboard.
+    // Keep track of the last string sent, so we can search again when find is
+    // reopened.
     private String mLastFind;
 
     /**
@@ -2938,7 +2881,6 @@
      * Clear the highlighting surrounding text matches created by findAll.
      */
     public void clearMatches() {
-        mLastFind = "";
         if (mNativeClass == 0)
             return;
         nativeSetFindIsEmpty();
@@ -2946,16 +2888,15 @@
     }
 
     /**
-     * @hide
+     * Called when the find ActionMode ends.
      */
-    public void notifyFindDialogDismissed() {
+    void notifyFindDialogDismissed() {
+        mFindCallback = null;
         if (mWebViewCore == null) {
             return;
         }
         clearMatches();
         setFindIsUp(false);
-        recordNewContentSize(mContentWidth, mContentHeight - mFindHeight,
-                false);
         // Now that the dialog has been removed, ensure that we scroll to a
         // location that is not beyond the end of the page.
         pinScrollTo(mScrollX, mScrollY, false, 0);
@@ -2963,16 +2904,6 @@
     }
 
     /**
-     * @hide
-     */
-    public void setFindDialogHeight(int height) {
-        if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, "setFindDialogHeight height=" + height);
-        }
-        mFindHeight = height;
-    }
-
-    /**
      * Query the document to see if it contains any image references. The
      * message object will be dispatched with arg1 being set to 1 if images
      * were found and 0 if the document does not reference any images.
@@ -2985,54 +2916,31 @@
         mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
     }
 
+    /**
+     * Request the scroller to abort any ongoing animation
+     *
+     * @hide
+     */
+    public void stopScroll() {
+        mScroller.forceFinished(true);
+        mLastVelocity = 0;
+    }
+
     @Override
     public void computeScroll() {
         if (mScroller.computeScrollOffset()) {
             int oldX = mScrollX;
             int oldY = mScrollY;
-            int x = mScroller.getCurrX();
-            int y = mScroller.getCurrY();
-            invalidate();  // So we draw again
-
-            if (oldX != x || oldY != y) {
-                final int rangeX = computeMaxScrollX();
-                final int rangeY = computeMaxScrollY();
-                overScrollBy(x - oldX, y - oldY, oldX, oldY,
-                        rangeX, rangeY,
-                        mOverflingDistance, mOverflingDistance, false);
-
-                if (mEdgeGlowTop != null) {
-                    if (rangeY > 0 || getOverScrollMode() == OVER_SCROLL_ALWAYS) {
-                        if (y < 0 && oldY >= 0) {
-                            mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
-                            if (!mEdgeGlowBottom.isFinished()) {
-                                mEdgeGlowBottom.onRelease();
-                            }
-                        } else if (y > rangeY && oldY <= rangeY) {
-                            mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
-                            if (!mEdgeGlowTop.isFinished()) {
-                                mEdgeGlowTop.onRelease();
-                            }
-                        }
-                    }
-
-                    if (rangeX > 0) {
-                        if (x < 0 && oldX >= 0) {
-                            mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
-                            if (!mEdgeGlowRight.isFinished()) {
-                                mEdgeGlowRight.onRelease();
-                            }
-                        } else if (x > rangeX && oldX <= rangeX) {
-                            mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
-                            if (!mEdgeGlowLeft.isFinished()) {
-                                mEdgeGlowLeft.onRelease();
-                            }
-                        }
-                    }
-                }
-            }
-            if (mScroller.isFinished()) {
-                mPrivateHandler.sendEmptyMessage(RESUME_WEBCORE_PRIORITY);
+            mScrollX = mScroller.getCurrX();
+            mScrollY = mScroller.getCurrY();
+            postInvalidate();  // So we draw again
+            if (oldX != mScrollX || oldY != mScrollY) {
+                onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+            } else {
+                abortAnimation();
+                mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
+                WebViewCore.resumePriority();
+                WebViewCore.resumeUpdatePicture(mWebViewCore);
             }
         } else {
             super.computeScroll();
@@ -3106,6 +3014,23 @@
     }
 
     /**
+     * Called by CallbackProxy when the page starts loading.
+     * @param url The URL of the page which has started loading.
+     */
+    /* package */ void onPageStarted(String url) {
+        // every time we start a new page, we want to reset the
+        // WebView certificate:  if the new site is secure, we
+        // will reload it and get a new certificate set;
+        // if the new site is not secure, the certificate must be
+        // null, and that will be the case
+        setCertificate(null);
+
+        // reset the flag since we set to true in if need after
+        // loading is see onPageFinished(Url)
+        mAccessibilityScriptInjected = false;
+    }
+
+    /**
      * Called by CallbackProxy when the page finishes loading.
      * @param url The URL of the page which has finished loading.
      */
@@ -3121,6 +3046,99 @@
             }
             mPageThatNeedsToSlideTitleBarOffScreen = null;
         }
+
+        injectAccessibilityForUrl(url);
+    }
+
+    /**
+     * This method injects accessibility in the loaded document if accessibility
+     * is enabled. If JavaScript is enabled we try to inject a URL specific script.
+     * If no URL specific script is found or JavaScript is disabled we fallback to
+     * the default {@link AccessibilityInjector} implementation.
+     * </p>
+     * If the URL has the "axs" paramter set to 1 it has already done the
+     * script injection so we do nothing. If the parameter is set to 0
+     * the URL opts out accessibility script injection so we fall back to
+     * the default {@link AccessibilityInjector}.
+     * </p>
+     * Note: If the user has not opted-in the accessibility script injection no scripts
+     * are injected rather the default {@link AccessibilityInjector} implementation
+     * is used.
+     *
+     * @param url The URL loaded by this {@link WebView}.
+     */
+    private void injectAccessibilityForUrl(String url) {
+        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
+
+        if (!accessibilityManager.isEnabled()) {
+            // it is possible that accessibility was turned off between reloads
+            ensureAccessibilityScriptInjectorInstance(false);
+            return;
+        }
+
+        if (!getSettings().getJavaScriptEnabled()) {
+            // no JS so we fallback to the basic buil-in support
+            ensureAccessibilityScriptInjectorInstance(true);
+            return;
+        }
+
+        // check the URL "axs" parameter to choose appropriate action
+        int axsParameterValue = getAxsUrlParameterValue(url);
+        if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED) {
+            boolean onDeviceScriptInjectionEnabled = (Settings.Secure.getInt(mContext
+                    .getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0) == 1);
+            if (onDeviceScriptInjectionEnabled) {
+                ensureAccessibilityScriptInjectorInstance(false);
+                // neither script injected nor script injection opted out => we inject
+                loadUrl(ACCESSIBILITY_SCRIPT_CHOOSER_JAVASCRIPT);
+                // TODO: Set this flag after successfull script injection. Maybe upon injection
+                // the chooser should update the meta tag and we check it to declare success
+                mAccessibilityScriptInjected = true;
+            } else {
+                // injection disabled so we fallback to the basic built-in support
+                ensureAccessibilityScriptInjectorInstance(true);
+            }
+        } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT) {
+            // injection opted out so we fallback to the basic buil-in support
+            ensureAccessibilityScriptInjectorInstance(true);
+        } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED) {
+            ensureAccessibilityScriptInjectorInstance(false);
+            // the URL provides accessibility but we still need to add our generic script
+            loadUrl(ACCESSIBILITY_SCRIPT_CHOOSER_JAVASCRIPT);
+        } else {
+            Log.e(LOGTAG, "Unknown URL value for the \"axs\" URL parameter: " + axsParameterValue);
+        }
+    }
+
+    /**
+     * Ensures the instance of the {@link AccessibilityInjector} to be present ot not.
+     *
+     * @param present True to ensure an insance, false to ensure no instance.
+     */
+    private void ensureAccessibilityScriptInjectorInstance(boolean present) {
+        if (present && mAccessibilityInjector == null) {
+            mAccessibilityInjector = new AccessibilityInjector(this);
+        } else {
+            mAccessibilityInjector = null;
+        }
+    }
+
+    /**
+     * Gets the "axs" URL parameter value.
+     *
+     * @param url A url to fetch the paramter from.
+     * @return The parameter value if such, -1 otherwise.
+     */
+    private int getAxsUrlParameterValue(String url) {
+        if (mMatchAxsUrlParameterPattern == null) {
+            mMatchAxsUrlParameterPattern = Pattern.compile(PATTERN_MATCH_AXS_URL_PARAMETER);
+        }
+        Matcher matcher = mMatchAxsUrlParameterPattern.matcher(url);
+        if (matcher.find()) {
+            String keyValuePair = url.substring(matcher.start(), matcher.end());
+            return Integer.parseInt(keyValuePair.split("=")[1]);
+        }
+        return -1;
     }
 
     /**
@@ -3237,7 +3255,7 @@
         } else {
             // If we don't request a layout, try to send our view size to the
             // native side to ensure that WebCore has the correct dimensions.
-            sendViewSizeZoom();
+            sendViewSizeZoom(false);
         }
     }
 
@@ -3367,46 +3385,7 @@
      *         settings.
      */
     public WebSettings getSettings() {
-        return mWebViewCore.getSettings();
-    }
-
-    /**
-     * Use this method to inform the webview about packages that are installed
-     * in the system. This information will be used by the
-     * navigator.isApplicationInstalled() API.
-     * @param packageNames is a set of package names that are known to be
-     * installed in the system.
-     *
-     * @hide not a public API
-     */
-    public void addPackageNames(Set<String> packageNames) {
-        mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, packageNames);
-    }
-
-    /**
-     * Use this method to inform the webview about single packages that are
-     * installed in the system. This information will be used by the
-     * navigator.isApplicationInstalled() API.
-     * @param packageName is the name of a package that is known to be
-     * installed in the system.
-     *
-     * @hide not a public API
-     */
-    public void addPackageName(String packageName) {
-        mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAME, packageName);
-    }
-
-    /**
-     * Use this method to inform the webview about packages that are uninstalled
-     * in the system. This information will be used by the
-     * navigator.isApplicationInstalled() API.
-     * @param packageName is the name of a package that has been uninstalled in
-     * the system.
-     *
-     * @hide not a public API
-     */
-    public void removePackageName(String packageName) {
-        mWebViewCore.sendMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
+        return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
     }
 
    /**
@@ -3480,50 +3459,10 @@
         }
 
         int saveCount = canvas.save();
-        if (mInOverScrollMode && !getSettings()
-                .getUseWebViewBackgroundForOverscrollBackground()) {
-            if (mOverScrollBackground == null) {
-                mOverScrollBackground = new Paint();
-                Bitmap bm = BitmapFactory.decodeResource(
-                        mContext.getResources(),
-                        com.android.internal.R.drawable.status_bar_background);
-                mOverScrollBackground.setShader(new BitmapShader(bm,
-                        Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
-                mOverScrollBorder = new Paint();
-                mOverScrollBorder.setStyle(Paint.Style.STROKE);
-                mOverScrollBorder.setStrokeWidth(0);
-                mOverScrollBorder.setColor(0xffbbbbbb);
-            }
-
-            int top = 0;
-            int right = computeRealHorizontalScrollRange();
-            int bottom = top + computeRealVerticalScrollRange();
-            // first draw the background and anchor to the top of the view
-            canvas.save();
-            canvas.translate(mScrollX, mScrollY);
-            canvas.clipRect(-mScrollX, top - mScrollY, right - mScrollX, bottom
-                    - mScrollY, Region.Op.DIFFERENCE);
-            canvas.drawPaint(mOverScrollBackground);
-            canvas.restore();
-            // then draw the border
-            canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder);
-            // next clip the region for the content
-            canvas.clipRect(0, top, right, bottom);
-        }
         if (mTitleBar != null) {
             canvas.translate(0, (int) mTitleBar.getHeight());
         }
-        if (mDragTrackerHandler == null) {
-            drawContent(canvas);
-        } else {
-            if (!mDragTrackerHandler.draw(canvas)) {
-                // sometimes the tracker doesn't draw, even though its active
-                drawContent(canvas);
-            }
-            if (mDragTrackerHandler.isFinished()) {
-                mDragTrackerHandler = null;
-            }
-        }
+        drawContent(canvas);
         canvas.restoreToCount(saveCount);
 
         // Now draw the shadow.
@@ -3535,7 +3474,6 @@
                     mScrollY + height);
             mTitleShadow.draw(canvas);
         }
-
         if (AUTO_REDRAW_HACK && mAutoRedraw) {
             invalidate();
         }
@@ -3543,66 +3481,45 @@
             mWebTextView.onDrawSubstitute();
         }
         mWebViewCore.signalRepaintDone();
-    }
 
-    @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        if (mEdgeGlowTop != null && drawEdgeGlows(canvas)) {
-            invalidate();
+        // paint the highlight in the end
+        if (!mTouchHighlightRegion.isEmpty()) {
+            if (mTouchHightlightPaint == null) {
+                mTouchHightlightPaint = new Paint();
+                mTouchHightlightPaint.setColor(mHightlightColor);
+                mTouchHightlightPaint.setAntiAlias(true);
+                mTouchHightlightPaint.setPathEffect(new CornerPathEffect(
+                        TOUCH_HIGHLIGHT_ARC));
+            }
+            canvas.drawPath(mTouchHighlightRegion.getBoundaryPath(),
+                    mTouchHightlightPaint);
+        }
+        if (DEBUG_TOUCH_HIGHLIGHT) {
+            if (getSettings().getNavDump()) {
+                if ((mTouchHighlightX | mTouchHighlightY) != 0) {
+                    if (mTouchCrossHairColor == null) {
+                        mTouchCrossHairColor = new Paint();
+                        mTouchCrossHairColor.setColor(Color.RED);
+                    }
+                    canvas.drawLine(mTouchHighlightX - mNavSlop,
+                            mTouchHighlightY - mNavSlop, mTouchHighlightX
+                                    + mNavSlop + 1, mTouchHighlightY + mNavSlop
+                                    + 1, mTouchCrossHairColor);
+                    canvas.drawLine(mTouchHighlightX + mNavSlop + 1,
+                            mTouchHighlightY - mNavSlop, mTouchHighlightX
+                                    - mNavSlop,
+                            mTouchHighlightY + mNavSlop + 1,
+                            mTouchCrossHairColor);
+                }
+            }
         }
     }
 
-    /**
-     * Draw the glow effect along the sides of the widget. mEdgeGlow* must be non-null.
-     *
-     * @param canvas Canvas to draw into, transformed into view coordinates.
-     * @return true if glow effects are still animating and the view should invalidate again.
-     */
-    private boolean drawEdgeGlows(Canvas canvas) {
-        final int scrollX = mScrollX;
-        final int scrollY = mScrollY;
-        final int width = getWidth();
-        int height = getHeight();
-
-        boolean invalidateForGlow = false;
-        if (!mEdgeGlowTop.isFinished()) {
-            final int restoreCount = canvas.save();
-
-            canvas.translate(-width / 2 + scrollX, Math.min(0, scrollY));
-            mEdgeGlowTop.setSize(width * 2, height);
-            invalidateForGlow |= mEdgeGlowTop.draw(canvas);
-            canvas.restoreToCount(restoreCount);
+    private void removeTouchHighlight(boolean removePendingMessage) {
+        if (removePendingMessage) {
+            mWebViewCore.removeMessages(EventHub.GET_TOUCH_HIGHLIGHT_RECTS);
         }
-        if (!mEdgeGlowBottom.isFinished()) {
-            final int restoreCount = canvas.save();
-
-            canvas.translate(-width / 2 + scrollX, Math.max(computeMaxScrollY(), scrollY) + height);
-            canvas.rotate(180, width, 0);
-            mEdgeGlowBottom.setSize(width * 2, height);
-            invalidateForGlow |= mEdgeGlowBottom.draw(canvas);
-            canvas.restoreToCount(restoreCount);
-        }
-        if (!mEdgeGlowLeft.isFinished()) {
-            final int restoreCount = canvas.save();
-
-            canvas.rotate(270);
-            canvas.translate(-height * 1.5f - scrollY, Math.min(0, scrollX));
-            mEdgeGlowLeft.setSize(height * 2, width);
-            invalidateForGlow |= mEdgeGlowLeft.draw(canvas);
-            canvas.restoreToCount(restoreCount);
-        }
-        if (!mEdgeGlowRight.isFinished()) {
-            final int restoreCount = canvas.save();
-
-            canvas.rotate(90);
-            canvas.translate(-height / 2 + scrollY,
-                    -(Math.max(computeMaxScrollX(), scrollX) + width));
-            mEdgeGlowRight.setSize(height * 2, width);
-            invalidateForGlow |= mEdgeGlowRight.draw(canvas);
-            canvas.restoreToCount(restoreCount);
-        }
-        return invalidateForGlow;
+        mWebViewCore.sendMessage(EventHub.REMOVE_TOUCH_HIGHLIGHT_RECTS);
     }
 
     @Override
@@ -3619,12 +3536,20 @@
         // to windows overview, the WebView will be temporarily removed from the
         // view system. In that case, do nothing.
         if (getParent() == null) return false;
+
+        // A multi-finger gesture can look like a long press; make sure we don't take
+        // long press actions if we're scaling.
+        final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+        if (detector != null && detector.isInProgress()) {
+            return false;
+        }
+        
         if (mNativeClass != 0 && nativeCursorIsTextInput()) {
             // Send the click so that the textfield is in focus
             centerKeyPressOnTextField();
             rebuildWebTextView();
         } else {
-            clearTextEntry(true);
+            clearTextEntry();
         }
         if (inEditingMode()) {
             return mWebTextView.performLongClick();
@@ -3645,26 +3570,18 @@
         setUpSelect();
         if (mNativeClass != 0 && nativeWordSelection(x, y)) {
             nativeSetExtendSelection();
-            WebChromeClient client = getWebChromeClient();
-            if (client != null) client.onSelectionStart(this);
+            mDrawSelectionPointer = false;
             return true;
         }
-        notifySelectDialogDismissed();
+        selectionDone();
         return false;
     }
 
-    boolean inAnimateZoom() {
-        return mZoomScale != 0;
-    }
-
     /**
-     * Need to adjust the WebTextView after a change in zoom, since mActualScale
-     * has changed.  This is especially important for password fields, which are
-     * drawn by the WebTextView, since it conveys more information than what
-     * webkit draws.  Thus we need to reposition it to show in the correct
-     * place.
+     * Keep track of the Callback so we can end its ActionMode or remove its
+     * titlebar.
      */
-    private boolean mNeedToAdjustWebTextView;
+    private SelectActionModeCallback mSelectCallback;
 
     private boolean didUpdateTextViewBounds(boolean allowIntersect) {
         Rect contentBounds = nativeFocusCandidateNodeBounds();
@@ -3693,25 +3610,65 @@
         }
     }
 
-    private void drawExtras(Canvas canvas, int extras, boolean animationsRunning) {
-        // If mNativeClass is 0, we should not reach here, so we do not
-        // need to check it again.
-        if (animationsRunning) {
-            canvas.setDrawFilter(mWebViewCore.mZoomFilter);
+    void setBaseLayer(int layer, Rect invalRect) {
+        if (mNativeClass == 0)
+            return;
+        if (invalRect == null) {
+            Rect rect = new Rect(0, 0, mContentWidth, mContentHeight);
+            nativeSetBaseLayer(layer, rect);
+        } else {
+            nativeSetBaseLayer(layer, invalRect);
         }
-        nativeDrawExtras(canvas, extras);
-        canvas.setDrawFilter(null);
     }
 
+    private void onZoomAnimationStart() {
+        // If it is in password mode, turn it off so it does not draw misplaced.
+        if (inEditingMode() && nativeFocusCandidateIsPassword()) {
+            mWebTextView.setInPassword(false);
+        }
+    }
+
+    private void onZoomAnimationEnd() {
+        // adjust the edit text view if needed
+        if (inEditingMode() && didUpdateTextViewBounds(false) && nativeFocusCandidateIsPassword()) {
+            // If it is a password field, start drawing the WebTextView once
+            // again.
+            mWebTextView.setInPassword(true);
+        }
+    }
+
+    void onFixedLengthZoomAnimationStart() {
+        WebViewCore.pauseUpdatePicture(getWebViewCore());
+        onZoomAnimationStart();
+    }
+
+    void onFixedLengthZoomAnimationEnd() {
+        WebViewCore.resumeUpdatePicture(mWebViewCore);
+        onZoomAnimationEnd();
+    }
+
+    private static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
+                                         Paint.DITHER_FLAG |
+                                         Paint.SUBPIXEL_TEXT_FLAG;
+    private static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
+                                           Paint.DITHER_FLAG;
+
+    private final DrawFilter mZoomFilter =
+            new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
+    // If we need to trade better quality for speed, set mScrollFilter to null
+    private final DrawFilter mScrollFilter =
+            new PaintFlagsDrawFilter(SCROLL_BITS, 0);
+
     private void drawCoreAndCursorRing(Canvas canvas, int color,
         boolean drawCursorRing) {
         if (mDrawHistory) {
-            canvas.scale(mActualScale, mActualScale);
+            canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
             canvas.drawPicture(mHistoryPicture);
             return;
         }
+        if (mNativeClass == 0) return;
 
-        boolean animateZoom = mZoomScale != 0;
+        boolean animateZoom = mZoomManager.isFixedLengthAnimationInProgress();
         boolean animateScroll = ((!mScroller.isFinished()
                 || mVelocityTracker != null)
                 && (mTouchMode != TOUCH_DRAG_MODE ||
@@ -3730,59 +3687,9 @@
             }
         }
         if (animateZoom) {
-            float zoomScale;
-            int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
-            if (interval < ZOOM_ANIMATION_LENGTH) {
-                float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
-                zoomScale = 1.0f / (mInvInitialZoomScale
-                        + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
-                invalidate();
-            } else {
-                zoomScale = mZoomScale;
-                // set mZoomScale to be 0 as we have done animation
-                mZoomScale = 0;
-                WebViewCore.resumeUpdatePicture(mWebViewCore);
-                // call invalidate() again to draw with the final filters
-                invalidate();
-                if (mNeedToAdjustWebTextView) {
-                    mNeedToAdjustWebTextView = false;
-                    if (didUpdateTextViewBounds(false)
-                            && nativeFocusCandidateIsPassword()) {
-                        // If it is a password field, start drawing the
-                        // WebTextView once again.
-                        mWebTextView.setInPassword(true);
-                    }
-                }
-            }
-            // calculate the intermediate scroll position. As we need to use
-            // zoomScale, we can't use pinLocX/Y directly. Copy the logic here.
-            float scale = zoomScale * mInvInitialZoomScale;
-            int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX)
-                    - mZoomCenterX);
-            tx = -pinLoc(tx, getViewWidth(), Math.round(mContentWidth
-                    * zoomScale)) + mScrollX;
-            int titleHeight = getTitleHeight();
-            int ty = Math.round(scale
-                    * (mInitialScrollY + mZoomCenterY - titleHeight)
-                    - (mZoomCenterY - titleHeight));
-            ty = -(ty <= titleHeight ? Math.max(ty, 0) : pinLoc(ty
-                    - titleHeight, getViewHeight(), Math.round(mContentHeight
-                    * zoomScale)) + titleHeight) + mScrollY;
-            canvas.translate(tx, ty);
-            canvas.scale(zoomScale, zoomScale);
-            if (inEditingMode() && !mNeedToAdjustWebTextView
-                    && mZoomScale != 0) {
-                // The WebTextView is up.  Keep track of this so we can adjust
-                // its size and placement when we finish zooming
-                mNeedToAdjustWebTextView = true;
-                // If it is in password mode, turn it off so it does not draw
-                // misplaced.
-                if (nativeFocusCandidateIsPassword()) {
-                    mWebTextView.setInPassword(false);
-                }
-            }
+            mZoomManager.animateZoom(canvas);
         } else {
-            canvas.scale(mActualScale, mActualScale);
+            canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
         }
 
         boolean UIAnimationsRunning = false;
@@ -3794,23 +3701,56 @@
             // we ask for a repaint.
             invalidate();
         }
-        mWebViewCore.drawContentPicture(canvas, color,
-                (animateZoom || mPreviewZoomOnly || UIAnimationsRunning),
-                animateScroll);
-        if (mNativeClass == 0) return;
+
         // decide which adornments to draw
         int extras = DRAW_EXTRAS_NONE;
+        if (DebugFlags.WEB_VIEW) {
+            Log.v(LOGTAG, "mFindIsUp=" + mFindIsUp
+                    + " mSelectingText=" + mSelectingText
+                    + " nativePageShouldHandleShiftAndArrows()="
+                    + nativePageShouldHandleShiftAndArrows()
+                    + " animateZoom=" + animateZoom);
+        }
         if (mFindIsUp) {
-                extras = DRAW_EXTRAS_FIND;
+            extras = DRAW_EXTRAS_FIND;
         } else if (mSelectingText) {
             extras = DRAW_EXTRAS_SELECTION;
             nativeSetSelectionPointer(mDrawSelectionPointer,
-                    mInvActualScale,
+                    mZoomManager.getInvScale(),
                     mSelectX, mSelectY - getTitleHeight());
         } else if (drawCursorRing) {
             extras = DRAW_EXTRAS_CURSOR_RING;
         }
-        drawExtras(canvas, extras, UIAnimationsRunning);
+
+        if (canvas.isHardwareAccelerated()) {
+            try {
+                if (canvas.acquireContext()) {
+                      Rect rect = new Rect(mGLRectViewport.left,
+                                           mGLRectViewport.top,
+                                           mGLRectViewport.right,
+                                           mGLRectViewport.bottom
+                                           - getVisibleTitleHeight());
+                      if (nativeDrawGL(rect, getScale(), extras)) {
+                          invalidate();
+                      }
+                }
+            } finally {
+                canvas.releaseContext();
+            }
+        } else {
+            DrawFilter df = null;
+            if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
+                df = mZoomFilter;
+            } else if (animateScroll) {
+                df = mScrollFilter;
+            }
+            canvas.setDrawFilter(df);
+            int content = nativeDraw(canvas, color, extras, true);
+            canvas.setDrawFilter(null);
+            if (content != 0) {
+                mWebViewCore.sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0);
+            }
+        }
 
         if (extras == DRAW_EXTRAS_CURSOR_RING) {
             if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
@@ -3839,10 +3779,14 @@
         return mDrawHistory;
     }
 
+    int getHistoryPictureWidth() {
+        return (mHistoryPicture != null) ? mHistoryPicture.getWidth() : 0;
+    }
+
     // Should only be called in UI thread
     void switchOutDrawHistory() {
         if (null == mWebViewCore) return; // CallbackProxy may trigger this
-        if (mDrawHistory && mWebViewCore.pictureReady()) {
+        if (mDrawHistory && (getProgress() == 100 || nativeHasContent())) {
             mDrawHistory = false;
             mHistoryPicture = null;
             invalidate();
@@ -3893,7 +3837,9 @@
      *  @param  end     End of selection.
      */
     /* package */ void setSelection(int start, int end) {
-        mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
+        if (mWebViewCore != null) {
+            mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
+        }
     }
 
     @Override
@@ -3911,14 +3857,11 @@
         InputMethodManager imm = (InputMethodManager)
                 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
 
-        // bring it back to the default scale so that user can enter text
-        boolean zoom = mActualScale < mDefaultScale;
+        // bring it back to the default level scale so that user can enter text
+        boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale();
         if (zoom) {
-            mInZoomOverview = false;
-            mZoomCenterX = mLastTouchX;
-            mZoomCenterY = mLastTouchY;
-            // do not change text wrap scale so that there is no reflow
-            setNewZoomScale(mDefaultScale, false, false);
+            mZoomManager.setZoomCenter(mLastTouchX, mLastTouchY);
+            mZoomManager.setZoomScale(mZoomManager.getDefaultScale(), false);
         }
         if (isTextView) {
             rebuildWebTextView();
@@ -3930,7 +3873,7 @@
                 return;
             }
         }
-        // Used by plugins.
+        // Used by plugins and contentEditable.
         // Also used if the navigation cache is out of date, and
         // does not recognize that a textfield is in focus.  In that
         // case, use WebView as the targeted view.
@@ -3940,10 +3883,11 @@
 
     // Called by WebKit to instruct the UI to hide the keyboard
     private void hideSoftKeyboard() {
-        InputMethodManager imm = (InputMethodManager)
-                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-
-        imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
+        InputMethodManager imm = InputMethodManager.peekInstance();
+        if (imm != null && (imm.isActive(this)
+                || (inEditingMode() && imm.isActive(mWebTextView)))) {
+            imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
+        }
     }
 
     /*
@@ -3968,7 +3912,7 @@
         // At this point, we know we have found an input field, so go ahead
         // and create the WebTextView if necessary.
         if (mWebTextView == null) {
-            mWebTextView = new WebTextView(mContext, WebView.this);
+            mWebTextView = new WebTextView(mContext, WebView.this, mAutoFillData.getQueryId());
             // Initialize our generation number.
             mTextGeneration = 0;
         }
@@ -4027,7 +3971,18 @@
                 imm.restartInput(mWebTextView);
             }
         }
-        mWebTextView.requestFocus();
+        if (isFocused()) {
+            mWebTextView.requestFocus();
+        }
+    }
+
+    /**
+     * Tell webkit to put the cursor on screen.
+     */
+    /* package */ void revealSelection() {
+        if (mWebViewCore != null) {
+            mWebViewCore.sendMessage(EventHub.REVEAL_SELECTION);
+        }
     }
 
     /**
@@ -4037,13 +3992,15 @@
      * @param nodePointer Pointer to the node of the textfield, so it can be
      *          compared to the currently focused textfield when the data is
      *          retrieved.
+     * @param autoFillable true if WebKit has determined this field is part of
+     *          a form that can be auto filled.
      */
-    /* package */ void requestFormData(String name, int nodePointer) {
+    /* package */ void requestFormData(String name, int nodePointer, boolean autoFillable) {
         if (mWebViewCore.getSettings().getSaveFormData()) {
             Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
             update.arg1 = nodePointer;
             RequestFormData updater = new RequestFormData(name, getUrl(),
-                    update);
+                    update, autoFillable);
             Thread t = new Thread(updater);
             t.start();
         }
@@ -4069,15 +4026,30 @@
         private String mName;
         private String mUrl;
         private Message mUpdateMessage;
+        private boolean mAutoFillable;
 
-        public RequestFormData(String name, String url, Message msg) {
+        public RequestFormData(String name, String url, Message msg, boolean autoFillable) {
             mName = name;
             mUrl = url;
             mUpdateMessage = msg;
+            mAutoFillable = autoFillable;
         }
 
         public void run() {
-            ArrayList<String> pastEntries = mDatabase.getFormData(mUrl, mName);
+            ArrayList<String> pastEntries = new ArrayList();
+
+            if (mAutoFillable) {
+                // Note that code inside the adapter click handler in WebTextView depends
+                // on the AutoFill item being at the top of the drop down list. If you change
+                // the order, make sure to do it there too!
+                pastEntries.add(getResources().getText(
+                        com.android.internal.R.string.autofill_this_form).toString() +
+                        " " +
+                        mAutoFillData.getPreviewString());
+            }
+
+            pastEntries.addAll(mDatabase.getFormData(mUrl, mName));
+
             if (pastEntries.size() > 0) {
                 AutoCompleteAdapter adapter = new
                         AutoCompleteAdapter(mContext, pastEntries);
@@ -4117,6 +4089,26 @@
     }
 
     /**
+     * Called by DRT on UI thread, need to proxy to WebCore thread.
+     *
+     * @hide debug only
+     */
+    public void useMockDeviceOrientation() {
+        mWebViewCore.sendMessage(EventHub.USE_MOCK_DEVICE_ORIENTATION);
+    }
+
+    /**
+     * Called by DRT on WebCore thread.
+     *
+     * @hide debug only
+     */
+    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+        mWebViewCore.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+                canProvideGamma, gamma);
+    }
+
+    /**
      * Dump the V8 counters to standard output.
      * Note that you need a build with V8 and WEBCORE_INSTRUMENTATION set to
      * true. Otherwise, this will do nothing.
@@ -4167,14 +4159,16 @@
         // Bubble up the key event if
         // 1. it is a system key; or
         // 2. the host application wants to handle it;
+        // 3. the accessibility injector is present and wants to handle it;
         if (event.isSystem()
-                || mCallbackProxy.uiOverrideKeyEvent(event)) {
+                || mCallbackProxy.uiOverrideKeyEvent(event)
+                || (mAccessibilityInjector != null && mAccessibilityInjector.onKeyEvent(event))) {
             return false;
         }
 
         if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
-            if (nativeFocusIsPlugin()) {
+            if (pageShouldHandleShiftAndArrows()) {
                 mShiftIsPressed = true;
             } else if (!nativeCursorWantsKeyEvents() && !mSelectingText) {
                 setUpSelect();
@@ -4194,8 +4188,8 @@
         if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
                 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
             switchOutDrawHistory();
-            if (nativeFocusIsPlugin()) {
-                letPluginHandleNavKey(keyCode, event.getEventTime(), true);
+            if (pageShouldHandleShiftAndArrows()) {
+                letPageHandleNavKey(keyCode, event.getEventTime(), true);
                 return true;
             }
             if (mSelectingText) {
@@ -4319,13 +4313,16 @@
         // Bubble up the key event if
         // 1. it is a system key; or
         // 2. the host application wants to handle it;
-        if (event.isSystem() || mCallbackProxy.uiOverrideKeyEvent(event)) {
+        // 3. the accessibility injector is present and wants to handle it;
+        if (event.isSystem()
+                || mCallbackProxy.uiOverrideKeyEvent(event)
+                || (mAccessibilityInjector != null && mAccessibilityInjector.onKeyEvent(event))) {
             return false;
         }
 
         if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
-            if (nativeFocusIsPlugin()) {
+            if (pageShouldHandleShiftAndArrows()) {
                 mShiftIsPressed = false;
             } else if (copySelection()) {
                 selectionDone();
@@ -4335,8 +4332,8 @@
 
         if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
                 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
-            if (nativeFocusIsPlugin()) {
-                letPluginHandleNavKey(keyCode, event.getEventTime(), false);
+            if (pageShouldHandleShiftAndArrows()) {
+                letPageHandleNavKey(keyCode, event.getEventTime(), false);
                 return true;
             }
             // always handle the navigation keys in the UI thread
@@ -4379,8 +4376,8 @@
                 }
                 return true;
             }
-            clearTextEntry(true);
-            nativeSetFollowedLink(true);
+            clearTextEntry();
+            nativeShowCursorTimed();
             if (!mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
                 mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
                         nativeCursorNodePointer());
@@ -4400,10 +4397,7 @@
         return false;
     }
 
-    /**
-     * @hide pending API council approval.
-     */
-    public void setUpSelect() {
+    private void setUpSelect() {
         if (0 == mNativeClass) return; // client isn't initialized
         if (inFullScreenMode()) return;
         if (mSelectingText) return;
@@ -4424,6 +4418,15 @@
             mSelectY = mScrollY + getViewHeightWithTitle() / 2;
         }
         nativeHideCursor();
+        mSelectCallback = new SelectActionModeCallback();
+        mSelectCallback.setWebView(this);
+        View titleBar = mTitleBar;
+        // We do not want to show the embedded title bar during find or
+        // select, but keep track of it so that it can be replaced when the
+        // mode is exited.
+        setEmbeddedTitleBar(null);
+        mSelectCallback.setTitleBar(titleBar);
+        startActionMode(mSelectCallback);
     }
 
     /**
@@ -4435,9 +4438,9 @@
     }
 
     /**
-     * @hide pending API council approval.
+     * Select all of the text in this WebView.
      */
-    public void selectAll() {
+    void selectAll() {
         if (0 == mNativeClass) return; // client isn't initialized
         if (inFullScreenMode()) return;
         if (!mSelectingText) setUpSelect();
@@ -4448,36 +4451,24 @@
     }
 
     /**
-     * @hide pending API council approval.
+     * Called when the selection has been removed.
      */
-    public boolean selectDialogIsUp() {
-        return mSelectingText;
-    }
-
-    /**
-     * @hide pending API council approval.
-     */
-    public void notifySelectDialogDismissed() {
-        mSelectingText = false;
-        WebViewCore.resumeUpdatePicture(mWebViewCore);
-    }
-
-    /**
-     * @hide pending API council approval.
-     */
-    public void selectionDone() {
+    void selectionDone() {
         if (mSelectingText) {
-            WebChromeClient client = getWebChromeClient();
-            if (client != null) client.onSelectionDone(this);
+            mSelectingText = false;
+            // finish is idempotent, so this is fine even if selectionDone was
+            // called by mSelectCallback.onDestroyActionMode
+            mSelectCallback.finish();
+            mSelectCallback = null;
+            WebViewCore.resumeUpdatePicture(mWebViewCore);
             invalidate(); // redraw without selection
-            notifySelectDialogDismissed();
         }
     }
 
     /**
-     * @hide pending API council approval.
+     * Copy the selection to the clipboard
      */
-    public boolean copySelection() {
+    boolean copySelection() {
         boolean copiedSomething = false;
         String selection = getSelection();
         if (selection != "") {
@@ -4488,22 +4479,18 @@
                     , com.android.internal.R.string.text_copied
                     , Toast.LENGTH_SHORT).show();
             copiedSomething = true;
-            try {
-                IClipboard clip = IClipboard.Stub.asInterface(
-                        ServiceManager.getService("clipboard"));
-                clip.setClipboardText(selection);
-            } catch (android.os.RemoteException e) {
-                Log.e(LOGTAG, "Clipboard failed", e);
-            }
+            ClipboardManager cm = (ClipboardManager)getContext()
+                    .getSystemService(Context.CLIPBOARD_SERVICE);
+            cm.setText(selection);
         }
         invalidate(); // remove selection region and pointer
         return copiedSomething;
     }
 
     /**
-     * @hide pending API council approval.
+     * Returns the currently highlighted text as a string.
      */
-    public String getSelection() {
+    String getSelection() {
         if (mNativeClass == 0) return "";
         return nativeGetSelection();
     }
@@ -4517,11 +4504,21 @@
     @Override
     protected void onDetachedFromWindow() {
         clearHelpers();
-        dismissZoomControl();
+        mZoomManager.dismissZoomPicker();
         if (hasWindowFocus()) setActive(false);
         super.onDetachedFromWindow();
     }
 
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        // The zoomManager may be null if the webview is created from XML that
+        // specifies the view's visibility param as not visible (see http://b/2794841)
+        if (visibility != View.VISIBLE && mZoomManager != null) {
+            mZoomManager.dismissZoomPicker();
+        }
+    }
+
     /**
      * @deprecated WebView no longer needs to implement
      * ViewGroup.OnHierarchyChangeListener.  This method does nothing now.
@@ -4551,32 +4548,30 @@
                 // If our window regained focus, and we have focus, then begin
                 // drawing the cursor ring
                 mDrawCursorRing = true;
+                setFocusControllerActive(true);
                 if (mNativeClass != 0) {
                     nativeRecordButtons(true, false, true);
-                    if (inEditingMode()) {
-                        mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 1, 0);
-                    }
                 }
             } else {
-                // If our window gained focus, but we do not have it, do not
-                // draw the cursor ring.
-                mDrawCursorRing = false;
+                if (!inEditingMode()) {
+                    // If our window gained focus, but we do not have it, do not
+                    // draw the cursor ring.
+                    mDrawCursorRing = false;
+                    setFocusControllerActive(false);
+                }
                 // We do not call nativeRecordButtons here because we assume
                 // that when we lost focus, or window focus, it got called with
                 // false for the first parameter
             }
         } else {
-            if (mWebViewCore != null && getSettings().getBuiltInZoomControls()
-                    && (mZoomButtonsController == null ||
-                            !mZoomButtonsController.isVisible())) {
+            if (!mZoomManager.isZoomPickerVisible()) {
                 /*
-                 * The zoom controls come in their own window, so our window
-                 * loses focus. Our policy is to not draw the cursor ring if
-                 * our window is not focused, but this is an exception since
+                 * The external zoom controls come in their own window, so our
+                 * window loses focus. Our policy is to not draw the cursor ring
+                 * if our window is not focused, but this is an exception since
                  * the user can still navigate the web page with the zoom
                  * controls showing.
                  */
-                // If our window has lost focus, stop drawing the cursor ring
                 mDrawCursorRing = false;
             }
             mGotKeyDown = false;
@@ -4586,7 +4581,7 @@
             if (mNativeClass != 0) {
                 nativeRecordButtons(false, false, true);
             }
-            setFocusControllerInactive();
+            setFocusControllerActive(false);
         }
         invalidate();
     }
@@ -4610,11 +4605,9 @@
      * not draw the blinking cursor.  It gets set to "active" to draw the cursor
      * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
      */
-    /* package */ void setFocusControllerInactive() {
-        // Do not need to also check whether mWebViewCore is null, because
-        // mNativeClass is only set if mWebViewCore is non null
-        if (mNativeClass == 0) return;
-        mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 0, 0);
+    /* package */ void setFocusControllerActive(boolean active) {
+        if (mWebViewCore == null) return;
+        mWebViewCore.sendMessage(EventHub.SET_ACTIVE, active ? 1 : 0, 0);
     }
 
     @Override
@@ -4631,6 +4624,7 @@
                 if (mNativeClass != 0) {
                     nativeRecordButtons(true, false, true);
                 }
+                setFocusControllerActive(true);
             //} else {
                 // The WebView has gained focus while we do not have
                 // windowfocus.  When our window lost focus, we should have
@@ -4644,7 +4638,7 @@
                 if (mNativeClass != 0) {
                     nativeRecordButtons(false, false, true);
                 }
-                setFocusControllerInactive();
+                setFocusControllerActive(false);
             }
             mGotKeyDown = false;
         }
@@ -4652,6 +4646,16 @@
         super.onFocusChanged(focused, direction, previouslyFocusedRect);
     }
 
+    void setGLRectViewport() {
+        View window = getRootView();
+        int[] location = new int[2];
+        getLocationInWindow(location);
+        mGLRectViewport = new Rect(location[0], window.getHeight()
+                             - (location[1] + getHeight()),
+                             location[0] + getWidth(),
+                             window.getHeight() - location[1]);
+    }
+
     /**
      * @hide
      */
@@ -4665,94 +4669,36 @@
             // system won't call onSizeChanged if the dimension is not changed.
             // In this case, we need to call sendViewSizeZoom() explicitly to
             // notify the WebKit about the new dimensions.
-            sendViewSizeZoom();
+            sendViewSizeZoom(false);
         }
+        setGLRectViewport();
         return changed;
     }
 
-    private static class PostScale implements Runnable {
-        final WebView mWebView;
-        final boolean mUpdateTextWrap;
-
-        public PostScale(WebView webView, boolean updateTextWrap) {
-            mWebView = webView;
-            mUpdateTextWrap = updateTextWrap;
-        }
-
-        public void run() {
-            if (mWebView.mWebViewCore != null) {
-                // we always force, in case our height changed, in which case we
-                // still want to send the notification over to webkit.
-                mWebView.setNewZoomScale(mWebView.mActualScale,
-                        mUpdateTextWrap, true);
-                // update the zoom buttons as the scale can be changed
-                if (mWebView.getSettings().getBuiltInZoomControls()) {
-                    mWebView.updateZoomButtonsEnabled();
-                }
-            }
-        }
-    }
-
     @Override
     protected void onSizeChanged(int w, int h, int ow, int oh) {
         super.onSizeChanged(w, h, ow, oh);
-        // Center zooming to the center of the screen.
-        if (mZoomScale == 0) { // unless we're already zooming
-            // To anchor at top left corner.
-            mZoomCenterX = 0;
-            mZoomCenterY = getVisibleTitleHeight();
-            mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
-            mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
-        }
 
         // adjust the max viewport width depending on the view dimensions. This
         // is to ensure the scaling is not going insane. So do not shrink it if
         // the view size is temporarily smaller, e.g. when soft keyboard is up.
-        int newMaxViewportWidth = (int) (Math.max(w, h) / DEFAULT_MIN_ZOOM_SCALE);
+        int newMaxViewportWidth = (int) (Math.max(w, h) / mZoomManager.getDefaultMinZoomScale());
         if (newMaxViewportWidth > sMaxViewportWidth) {
             sMaxViewportWidth = newMaxViewportWidth;
         }
 
-        // update mMinZoomScale if the minimum zoom scale is not fixed
-        if (!mMinZoomScaleFixed) {
-            // when change from narrow screen to wide screen, the new viewWidth
-            // can be wider than the old content width. We limit the minimum
-            // scale to 1.0f. The proper minimum scale will be calculated when
-            // the new picture shows up.
-            mMinZoomScale = Math.min(1.0f, (float) getViewWidth()
-                    / (mDrawHistory ? mHistoryPicture.getWidth()
-                            : mZoomOverviewWidth));
-            if (mInitialScaleInPercent > 0) {
-                // limit the minZoomScale to the initialScale if it is set
-                float initialScale = mInitialScaleInPercent / 100.0f;
-                if (mMinZoomScale > initialScale) {
-                    mMinZoomScale = initialScale;
-                }
-            }
-        }
-
-        dismissZoomControl();
-
-        // onSizeChanged() is called during WebView layout. And any
-        // requestLayout() is blocked during layout. As setNewZoomScale() will
-        // call its child View to reposition itself through ViewManager's
-        // scaleAll(), we need to post a Runnable to ensure requestLayout().
-        // <b/>
-        // only update the text wrap scale if width changed.
-        post(new PostScale(this, w != ow));
+        mZoomManager.onSizeChanged(w, h, ow, oh);
     }
 
     @Override
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
         super.onScrollChanged(l, t, oldl, oldt);
-        if (!mInOverScrollMode) {
-            sendOurVisibleRect();
-            // update WebKit if visible title bar height changed. The logic is same
-            // as getVisibleTitleHeight.
-            int titleHeight = getTitleHeight();
-            if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
-                sendViewSizeZoom();
-            }
+        sendOurVisibleRect();
+        // update WebKit if visible title bar height changed. The logic is same
+        // as getVisibleTitleHeight.
+        int titleHeight = getTitleHeight();
+        if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
+            sendViewSizeZoom(false);
         }
     }
 
@@ -4760,9 +4706,11 @@
     public boolean dispatchKeyEvent(KeyEvent event) {
         boolean dispatch = true;
 
-        // Textfields and plugins need to receive the shift up key even if
-        // another key was released while the shift key was held down.
-        if (!inEditingMode() && (mNativeClass == 0 || !nativeFocusIsPlugin())) {
+        // Textfields, plugins, and contentEditable nodes need to receive the
+        // shift up key even if another key was released while the shift key
+        // was held down.
+        if (!inEditingMode() && (mNativeClass == 0
+                || !nativePageShouldHandleShiftAndArrows())) {
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
                 mGotKeyDown = true;
             } else {
@@ -4795,282 +4743,6 @@
     private static final float MAX_SLOPE_FOR_DIAG = 1.5f;
     private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80;
 
-    private static int sign(float x) {
-        return x > 0 ? 1 : (x < 0 ? -1 : 0);
-    }
-
-    // if the page can scroll <= this value, we won't allow the drag tracker
-    // to have any effect.
-    private static final int MIN_SCROLL_AMOUNT_TO_DISABLE_DRAG_TRACKER = 4;
-
-    private class DragTrackerHandler {
-        private final DragTracker mProxy;
-        private final float mStartY, mStartX;
-        private final float mMinDY, mMinDX;
-        private final float mMaxDY, mMaxDX;
-        private float mCurrStretchY, mCurrStretchX;
-        private int mSX, mSY;
-        private Interpolator mInterp;
-        private float[] mXY = new float[2];
-
-        // inner (non-state) classes can't have enums :(
-        private static final int DRAGGING_STATE = 0;
-        private static final int ANIMATING_STATE = 1;
-        private static final int FINISHED_STATE = 2;
-        private int mState;
-
-        public DragTrackerHandler(float x, float y, DragTracker proxy) {
-            mProxy = proxy;
-
-            int docBottom = computeRealVerticalScrollRange() + getTitleHeight();
-            int viewTop = getScrollY();
-            int viewBottom = viewTop + getHeight();
-
-            mStartY = y;
-            mMinDY = -viewTop;
-            mMaxDY = docBottom - viewBottom;
-
-            if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
-                Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, " dragtracker y= " + y +
-                      " up/down= " + mMinDY + " " + mMaxDY);
-            }
-
-            int docRight = computeRealHorizontalScrollRange();
-            int viewLeft = getScrollX();
-            int viewRight = viewLeft + getWidth();
-            mStartX = x;
-            mMinDX = -viewLeft;
-            mMaxDX = docRight - viewRight;
-
-            mState = DRAGGING_STATE;
-            mProxy.onStartDrag(x, y);
-
-            // ensure we buildBitmap at least once
-            mSX = -99999;
-        }
-
-        private float computeStretch(float delta, float min, float max) {
-            float stretch = 0;
-            if (max - min > MIN_SCROLL_AMOUNT_TO_DISABLE_DRAG_TRACKER) {
-                if (delta < min) {
-                    stretch = delta - min;
-                } else if (delta > max) {
-                    stretch = delta - max;
-                }
-            }
-            return stretch;
-        }
-
-        public void dragTo(float x, float y) {
-            float sy = computeStretch(mStartY - y, mMinDY, mMaxDY);
-            float sx = computeStretch(mStartX - x, mMinDX, mMaxDX);
-
-            if ((mSnapScrollMode & SNAP_X) != 0) {
-                sy = 0;
-            } else if ((mSnapScrollMode & SNAP_Y) != 0) {
-                sx = 0;
-            }
-
-            if (mCurrStretchX != sx || mCurrStretchY != sy) {
-                mCurrStretchX = sx;
-                mCurrStretchY = sy;
-                if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
-                    Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "---- stretch " + sx +
-                          " " + sy);
-                }
-                if (mProxy.onStretchChange(sx, sy)) {
-                    invalidate();
-                }
-            }
-        }
-
-        public void stopDrag() {
-            final int DURATION = 200;
-            int now = (int)SystemClock.uptimeMillis();
-            mInterp = new Interpolator(2);
-            mXY[0] = mCurrStretchX;
-            mXY[1] = mCurrStretchY;
-         //   float[] blend = new float[] { 0.5f, 0, 0.75f, 1 };
-            float[] blend = new float[] { 0, 0.5f, 0.75f, 1 };
-            mInterp.setKeyFrame(0, now, mXY, blend);
-            float[] zerozero = new float[] { 0, 0 };
-            mInterp.setKeyFrame(1, now + DURATION, zerozero, null);
-            mState = ANIMATING_STATE;
-
-            if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
-                Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "----- stopDrag, starting animation");
-            }
-        }
-
-        // Call this after each draw. If it ruturns null, the tracker is done
-        public boolean isFinished() {
-            return mState == FINISHED_STATE;
-        }
-
-        private int hiddenHeightOfTitleBar() {
-            return getTitleHeight() - getVisibleTitleHeight();
-        }
-
-        // need a way to know if 565 or 8888 is the right config for
-        // capturing the display and giving it to the drag proxy
-        private Bitmap.Config offscreenBitmapConfig() {
-            // hard code 565 for now
-            return Bitmap.Config.RGB_565;
-        }
-
-        /*  If the tracker draws, then this returns true, otherwise it will
-            return false, and draw nothing.
-         */
-        public boolean draw(Canvas canvas) {
-            if (mCurrStretchX != 0 || mCurrStretchY != 0) {
-                int sx = getScrollX();
-                int sy = getScrollY() - hiddenHeightOfTitleBar();
-                if (mSX != sx || mSY != sy) {
-                    buildBitmap(sx, sy);
-                    mSX = sx;
-                    mSY = sy;
-                }
-
-                if (mState == ANIMATING_STATE) {
-                    Interpolator.Result result = mInterp.timeToValues(mXY);
-                    if (result == Interpolator.Result.FREEZE_END) {
-                        mState = FINISHED_STATE;
-                        return false;
-                    } else {
-                        mProxy.onStretchChange(mXY[0], mXY[1]);
-                        invalidate();
-                        // fall through to the draw
-                    }
-                }
-                int count = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-                canvas.translate(sx, sy);
-                mProxy.onDraw(canvas);
-                canvas.restoreToCount(count);
-                return true;
-            }
-            if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
-                Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, " -- draw false " +
-                      mCurrStretchX + " " + mCurrStretchY);
-            }
-            return false;
-        }
-
-        private void buildBitmap(int sx, int sy) {
-            int w = getWidth();
-            int h = getViewHeight();
-            Bitmap bm = Bitmap.createBitmap(w, h, offscreenBitmapConfig());
-            Canvas canvas = new Canvas(bm);
-            canvas.translate(-sx, -sy);
-            drawContent(canvas);
-
-            if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
-                Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "--- buildBitmap " + sx +
-                      " " + sy + " " + w + " " + h);
-            }
-            mProxy.onBitmapChange(bm);
-        }
-    }
-
-    /** @hide */
-    public static class DragTracker {
-        public void onStartDrag(float x, float y) {}
-        public boolean onStretchChange(float sx, float sy) {
-            // return true to have us inval the view
-            return false;
-        }
-        public void onStopDrag() {}
-        public void onBitmapChange(Bitmap bm) {}
-        public void onDraw(Canvas canvas) {}
-    }
-
-    /** @hide */
-    public DragTracker getDragTracker() {
-        return mDragTracker;
-    }
-
-    /** @hide */
-    public void setDragTracker(DragTracker tracker) {
-        mDragTracker = tracker;
-    }
-
-    private DragTracker mDragTracker;
-    private DragTrackerHandler mDragTrackerHandler;
-
-    private class ScaleDetectorListener implements
-            ScaleGestureDetector.OnScaleGestureListener {
-
-        public boolean onScaleBegin(ScaleGestureDetector detector) {
-            // cancel the single touch handling
-            cancelTouch();
-            dismissZoomControl();
-            // reset the zoom overview mode so that the page won't auto grow
-            mInZoomOverview = false;
-            // If it is in password mode, turn it off so it does not draw
-            // misplaced.
-            if (inEditingMode() && nativeFocusCandidateIsPassword()) {
-                mWebTextView.setInPassword(false);
-            }
-
-            mViewManager.startZoom();
-
-            return true;
-        }
-
-        public void onScaleEnd(ScaleGestureDetector detector) {
-            if (mPreviewZoomOnly) {
-                mPreviewZoomOnly = false;
-                mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
-                mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
-                // don't reflow when zoom in; when zoom out, do reflow if the
-                // new scale is almost minimum scale;
-                boolean reflowNow = (mActualScale - mMinZoomScale
-                        <= MINIMUM_SCALE_INCREMENT)
-                        || ((mActualScale <= 0.8 * mTextWrapScale));
-                // force zoom after mPreviewZoomOnly is set to false so that the
-                // new view size will be passed to the WebKit
-                setNewZoomScale(mActualScale, reflowNow, true);
-                // call invalidate() to draw without zoom filter
-                invalidate();
-            }
-            // adjust the edit text view if needed
-            if (inEditingMode() && didUpdateTextViewBounds(false)
-                    && nativeFocusCandidateIsPassword()) {
-                // If it is a password field, start drawing the
-                // WebTextView once again.
-                mWebTextView.setInPassword(true);
-            }
-            // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as it
-            // may trigger the unwanted click, can't use TOUCH_DRAG_MODE as it
-            // may trigger the unwanted fling.
-            mTouchMode = TOUCH_PINCH_DRAG;
-            mConfirmMove = true;
-            startTouch(detector.getFocusX(), detector.getFocusY(),
-                    mLastTouchTime);
-
-            mViewManager.endZoom();
-        }
-
-        public boolean onScale(ScaleGestureDetector detector) {
-            float scale = (float) (Math.round(detector.getScaleFactor()
-                    * mActualScale * 100) / 100.0);
-            if (Math.abs(scale - mActualScale) >= MINIMUM_SCALE_INCREMENT) {
-                mPreviewZoomOnly = true;
-                // limit the scale change per step
-                if (scale > mActualScale) {
-                    scale = Math.min(scale, mActualScale * 1.25f);
-                } else {
-                    scale = Math.max(scale, mActualScale * 0.8f);
-                }
-                mZoomCenterX = detector.getFocusX();
-                mZoomCenterY = detector.getFocusY();
-                setNewZoomScale(scale, false, false);
-                invalidate();
-                return true;
-            }
-            return false;
-        }
-    }
-
     private boolean hitFocusedPlugin(int contentX, int contentY) {
         if (DebugFlags.WEB_VIEW) {
             Log.v(LOGTAG, "nativeFocusIsPlugin()=" + nativeFocusIsPlugin());
@@ -5092,6 +4764,48 @@
         return mFullScreenHolder != null;
     }
 
+    private void dismissFullScreenMode() {
+        if (inFullScreenMode()) {
+            mFullScreenHolder.dismiss();
+            mFullScreenHolder = null;
+        }
+    }
+
+    void onPinchToZoomAnimationStart() {
+        // cancel the single touch handling
+        cancelTouch();
+        onZoomAnimationStart();
+    }
+
+    void onPinchToZoomAnimationEnd(ScaleGestureDetector detector) {
+        onZoomAnimationEnd();
+        // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as
+        // it may trigger the unwanted click, can't use TOUCH_DRAG_MODE
+        // as it may trigger the unwanted fling.
+        mTouchMode = TOUCH_PINCH_DRAG;
+        mConfirmMove = true;
+        startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime);
+    }
+
+    private void startScrollingLayer(float gestureX, float gestureY) {
+        if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
+            int contentX = viewToContentX((int) gestureX + mScrollX);
+            int contentY = viewToContentY((int) gestureY + mScrollY);
+            mScrollingLayer = nativeScrollableLayer(contentX, contentY);
+            if (mScrollingLayer != 0) {
+                mTouchMode = TOUCH_DRAG_LAYER_MODE;
+            }
+        }
+    }
+
+    // 1/(density * density) used to compute the distance between points.
+    // Computed in init().
+    private float DRAG_LAYER_INVERSE_DENSITY_SQUARED;
+
+    // The distance between two points reported in onTouchEvent scaled by the
+    // density of the screen.
+    private static final int DRAG_LAYER_FINGER_DISTANCE = 20000;
+
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (mNativeClass == 0 || (!isClickable() && !isLongClickable())) {
@@ -5099,63 +4813,123 @@
         }
 
         if (DebugFlags.WEB_VIEW) {
-            Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
-                    + mTouchMode);
+            Log.v(LOGTAG, ev + " at " + ev.getEventTime()
+                + " mTouchMode=" + mTouchMode
+                + " numPointers=" + ev.getPointerCount());
         }
 
-        int action;
-        float x, y;
+        int action = ev.getAction();
+        float x = ev.getX();
+        float y = ev.getY();
         long eventTime = ev.getEventTime();
 
-        // FIXME: we may consider to give WebKit an option to handle multi-touch
-        // events later.
-        if (mSupportMultiTouch && ev.getPointerCount() > 1) {
-            if (mAllowPanAndScale || mMinZoomScale < mMaxZoomScale) {
-                mScaleDetector.onTouchEvent(ev);
-                if (mScaleDetector.isInProgress()) {
-                    mLastTouchTime = eventTime;
-                    if (!mAllowPanAndScale) {
-                        return true;
-                    }
-                    mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
-                    mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+        final ScaleGestureDetector detector =
+                mZoomManager.getMultiTouchGestureDetector();
+        boolean skipScaleGesture = false;
+        // Set to the mid-point of a two-finger gesture used to detect if the
+        // user has touched a layer.
+        float gestureX = x;
+        float gestureY = y;
+        if (detector == null || !detector.isInProgress()) {
+            // The gesture for scrolling a layer is two fingers close together.
+            // FIXME: we may consider giving WebKit an option to handle
+            // multi-touch events later.
+            if (ev.getPointerCount() > 1) {
+                float dx = ev.getX(1) - ev.getX(0);
+                float dy = ev.getY(1) - ev.getY(0);
+                float dist = (dx * dx + dy * dy) *
+                        DRAG_LAYER_INVERSE_DENSITY_SQUARED;
+                // Use the approximate center to determine if the gesture is in
+                // a layer.
+                gestureX = ev.getX(0) + (dx * .5f);
+                gestureY = ev.getY(0) + (dy * .5f);
+                // Now use a consistent point for tracking movement.
+                if (ev.getX(0) < ev.getX(1)) {
+                    x = ev.getX(0);
+                    y = ev.getY(0);
+                } else {
+                    x = ev.getX(1);
+                    y = ev.getY(1);
                 }
-                x = mScaleDetector.getFocusX();
-                y = mScaleDetector.getFocusY();
-                action = ev.getAction() & MotionEvent.ACTION_MASK;
-                if (action == MotionEvent.ACTION_POINTER_DOWN) {
-                    cancelTouch();
-                    action = MotionEvent.ACTION_DOWN;
-                } else if (action == MotionEvent.ACTION_POINTER_UP) {
-                    // set mLastTouchX/Y to the remaining point
-                    mLastTouchX = x;
-                    mLastTouchY = y;
-                } else if (action == MotionEvent.ACTION_MOVE) {
-                    // negative x or y indicate it is on the edge, skip it.
-                    if (x < 0 || y < 0) {
-                        return true;
-                    }
+                action = ev.getActionMasked();
+                if (dist < DRAG_LAYER_FINGER_DISTANCE) {
+                    skipScaleGesture = true;
+                } else if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
+                    // Fingers moved too far apart while dragging, the user
+                    // might be trying to zoom.
+                    mTouchMode = TOUCH_INIT_MODE;
                 }
-            } else {
-                // if the page disallow zoom, skip multi-pointer action
-                return true;
             }
-        } else {
-            action = ev.getAction();
-            x = ev.getX();
-            y = ev.getY();
+        }
+
+        // If the page disallows zoom, pass multi-pointer events to webkit.
+        if (!skipScaleGesture && ev.getPointerCount() > 1
+            && (mZoomManager.isZoomScaleFixed() || mDeferMultitouch)) {
+            if (DebugFlags.WEB_VIEW) {
+                Log.v(LOGTAG, "passing " + ev.getPointerCount() + " points to webkit");
+            }
+            passMultiTouchToWebKit(ev);
+            return true;
+        }
+
+        if (mZoomManager.supportsMultiTouchZoom() && ev.getPointerCount() > 1 &&
+                mTouchMode != TOUCH_DRAG_LAYER_MODE && !skipScaleGesture) {
+            if (!detector.isInProgress() &&
+                    ev.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN) {
+                // Insert a fake pointer down event in order to start
+                // the zoom scale detector.
+                MotionEvent temp = MotionEvent.obtain(ev);
+                // Clear the original event and set it to
+                // ACTION_POINTER_DOWN.
+                try {
+                    temp.setAction(temp.getAction() &
+                            ~MotionEvent.ACTION_MASK |
+                            MotionEvent.ACTION_POINTER_DOWN);
+                    detector.onTouchEvent(temp);
+                } finally {
+                    temp.recycle();
+                }
+            }
+
+            detector.onTouchEvent(ev);
+
+            if (detector.isInProgress()) {
+                mLastTouchTime = eventTime;
+                cancelLongPress();
+                mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+                if (!mZoomManager.supportsPanDuringZoom()) {
+                    return true;
+                }
+                mTouchMode = TOUCH_DRAG_MODE;
+                if (mVelocityTracker == null) {
+                    mVelocityTracker = VelocityTracker.obtain();
+                }
+            }
+
+            x = detector.getFocusX();
+            y = detector.getFocusY();
+            action = ev.getAction() & MotionEvent.ACTION_MASK;
+            if (action == MotionEvent.ACTION_POINTER_DOWN) {
+                cancelTouch();
+                action = MotionEvent.ACTION_DOWN;
+            } else if (action == MotionEvent.ACTION_POINTER_UP) {
+                // set mLastTouchX/Y to the remaining point
+                mLastTouchX = x;
+                mLastTouchY = y;
+            } else if (action == MotionEvent.ACTION_MOVE) {
+                // negative x or y indicate it is on the edge, skip it.
+                if (x < 0 || y < 0) {
+                    return true;
+                }
+            }
         }
 
         // Due to the touch screen edge effect, a touch closer to the edge
         // always snapped to the edge. As getViewWidth() can be different from
         // getWidth() due to the scrollbar, adjusting the point to match
         // getViewWidth(). Same applied to the height.
-        if (x > getViewWidth() - 1) {
-            x = getViewWidth() - 1;
-        }
-        if (y > getViewHeightWithTitle() - 1) {
-            y = getViewHeightWithTitle() - 1;
-        }
+        x = Math.min(x, getViewWidth() - 1);
+        y = Math.min(y, getViewHeightWithTitle() - 1);
 
         float fDeltaX = mLastTouchX - x;
         float fDeltaY = mLastTouchY - y;
@@ -5178,6 +4952,9 @@
                     mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
                 } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
                     mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
+                    if (getSettings().supportTouchOnly()) {
+                        removeTouchHighlight(true);
+                    }
                     if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
                         mTouchMode = TOUCH_DOUBLE_TAP_MODE;
                     } else {
@@ -5189,13 +4966,33 @@
                                 contentX, contentY) : false;
                     }
                 } else { // the normal case
-                    mPreviewZoomOnly = false;
                     mTouchMode = TOUCH_INIT_MODE;
                     mDeferTouchProcess = (!inFullScreenMode()
                             && mForwardTouchEvents) ? hitFocusedPlugin(
                             contentX, contentY) : false;
                     mWebViewCore.sendMessage(
                             EventHub.UPDATE_FRAME_CACHE_IF_LOADING);
+                    if (getSettings().supportTouchOnly()) {
+                        TouchHighlightData data = new TouchHighlightData();
+                        data.mX = contentX;
+                        data.mY = contentY;
+                        data.mSlop = viewToContentDimension(mNavSlop);
+                        mWebViewCore.sendMessageDelayed(
+                                EventHub.GET_TOUCH_HIGHLIGHT_RECTS, data,
+                                ViewConfiguration.getTapTimeout());
+                        if (DEBUG_TOUCH_HIGHLIGHT) {
+                            if (getSettings().getNavDump()) {
+                                mTouchHighlightX = (int) x + mScrollX;
+                                mTouchHighlightY = (int) y + mScrollY;
+                                mPrivateHandler.postDelayed(new Runnable() {
+                                    public void run() {
+                                        mTouchHighlightX = mTouchHighlightY = 0;
+                                        invalidate();
+                                    }
+                                }, TOUCH_HIGHLIGHT_ELAPSE_TIME);
+                            }
+                        }
+                    }
                     if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
                         EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
                                 (eventTime - mLastTouchUpTime), eventTime);
@@ -5227,21 +5024,19 @@
                     if (shouldForwardTouchEvent()) {
                         TouchEventData ted = new TouchEventData();
                         ted.mAction = action;
-                        ted.mX = contentX;
-                        ted.mY = contentY;
+                        ted.mPoints = new Point[1];
+                        ted.mPoints[0] = new Point(contentX, contentY);
                         ted.mMetaState = ev.getMetaState();
                         ted.mReprocess = mDeferTouchProcess;
+                        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                         if (mDeferTouchProcess) {
                             // still needs to set them for compute deltaX/Y
                             mLastTouchX = x;
                             mLastTouchY = y;
-                            ted.mViewX = x;
-                            ted.mViewY = y;
-                            mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                             break;
                         }
-                        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                         if (!inFullScreenMode()) {
+                            mPrivateHandler.removeMessages(PREVENT_DEFAULT_TIMEOUT);
                             mPrivateHandler.sendMessageDelayed(mPrivateHandler
                                     .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
                                             action, 0), TAP_TIMEOUT);
@@ -5262,24 +5057,24 @@
                     if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
                         mTouchMode = TOUCH_INIT_MODE;
                     }
+                    if (getSettings().supportTouchOnly()) {
+                        removeTouchHighlight(true);
+                    }
                 }
                 // pass the touch events from UI thread to WebCore thread
                 if (shouldForwardTouchEvent() && mConfirmMove && (firstMove
                         || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) {
-                    mLastSentTouchTime = eventTime;
                     TouchEventData ted = new TouchEventData();
                     ted.mAction = action;
-                    ted.mX = contentX;
-                    ted.mY = contentY;
+                    ted.mPoints = new Point[1];
+                    ted.mPoints[0] = new Point(contentX, contentY);
                     ted.mMetaState = ev.getMetaState();
                     ted.mReprocess = mDeferTouchProcess;
+                    mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
+                    mLastSentTouchTime = eventTime;
                     if (mDeferTouchProcess) {
-                        ted.mViewX = x;
-                        ted.mViewY = y;
-                        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                         break;
                     }
-                    mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                     if (firstMove && !inFullScreenMode()) {
                         mPrivateHandler.sendMessageDelayed(mPrivateHandler
                                 .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
@@ -5308,11 +5103,13 @@
                     break;
                 }
 
-                if (mTouchMode != TOUCH_DRAG_MODE) {
+                if (mTouchMode != TOUCH_DRAG_MODE &&
+                        mTouchMode != TOUCH_DRAG_LAYER_MODE) {
 
                     if (!mConfirmMove) {
                         break;
                     }
+
                     if (mPreventDefault == PREVENT_DEFAULT_MAYBE_YES
                             || mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
                         // track mLastTouchTime as we may need to do fling at
@@ -5322,9 +5119,9 @@
                     }
 
                     // Only lock dragging to one axis if we don't have a scale in progress.
-                    // Scaling implies free-roaming movement. Note we'll only ever get here
-                    // if mAllowPanAndScale is true.
-                    if (mScaleDetector != null && !mScaleDetector.isInProgress()) {
+                    // Scaling implies free-roaming movement. Note this is only ever a question
+                    // if mZoomManager.supportsPanDuringZoom() is true.
+                    if (detector != null && !detector.isInProgress()) {
                         // if it starts nearly horizontal or vertical, enforce it
                         int ax = Math.abs(deltaX);
                         int ay = Math.abs(deltaY);
@@ -5345,14 +5142,27 @@
                     deltaX = 0;
                     deltaY = 0;
 
+                    if (skipScaleGesture) {
+                        startScrollingLayer(gestureX, gestureY);
+                    }
                     startDrag();
                 }
 
-                if (mDragTrackerHandler != null) {
-                    mDragTrackerHandler.dragTo(x, y);
-                }
-
                 // do pan
+                if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
+                    int newScrollX = pinLocX(mScrollX + deltaX);
+                    int newDeltaX = newScrollX - mScrollX;
+                    if (deltaX != newDeltaX) {
+                        deltaX = newDeltaX;
+                        fDeltaX = (float) newDeltaX;
+                    }
+                    int newScrollY = pinLocY(mScrollY + deltaY);
+                    int newDeltaY = newScrollY - mScrollY;
+                    if (deltaY != newDeltaY) {
+                        deltaY = newDeltaY;
+                        fDeltaY = (float) newDeltaY;
+                    }
+                }
                 boolean done = false;
                 boolean keepScrollBarsVisible = false;
                 if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) {
@@ -5419,7 +5229,9 @@
 
                 doDrag(deltaX, deltaY);
 
-                if (keepScrollBarsVisible) {
+                // Turn off scrollbars when dragging a layer.
+                if (keepScrollBarsVisible &&
+                        mTouchMode != TOUCH_DRAG_LAYER_MODE) {
                     if (mHeldMotionless != MOTIONLESS_TRUE) {
                         mHeldMotionless = MOTIONLESS_TRUE;
                         invalidate();
@@ -5434,18 +5246,15 @@
                 break;
             }
             case MotionEvent.ACTION_UP: {
+                if (!isFocused()) requestFocus();
                 // pass the touch events from UI thread to WebCore thread
                 if (shouldForwardTouchEvent()) {
                     TouchEventData ted = new TouchEventData();
                     ted.mAction = action;
-                    ted.mX = contentX;
-                    ted.mY = contentY;
+                    ted.mPoints = new Point[1];
+                    ted.mPoints[0] = new Point(contentX, contentY);
                     ted.mMetaState = ev.getMetaState();
                     ted.mReprocess = mDeferTouchProcess;
-                    if (mDeferTouchProcess) {
-                        ted.mViewX = x;
-                        ted.mViewY = y;
-                    }
                     mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                 }
                 mLastTouchUpTime = eventTime;
@@ -5456,17 +5265,13 @@
                         if (inFullScreenMode() || mDeferTouchProcess) {
                             TouchEventData ted = new TouchEventData();
                             ted.mAction = WebViewCore.ACTION_DOUBLETAP;
-                            ted.mX = contentX;
-                            ted.mY = contentY;
+                            ted.mPoints = new Point[1];
+                            ted.mPoints[0] = new Point(contentX, contentY);
                             ted.mMetaState = ev.getMetaState();
                             ted.mReprocess = mDeferTouchProcess;
-                            if (mDeferTouchProcess) {
-                                ted.mViewX = x;
-                                ted.mViewY = y;
-                            }
                             mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                         } else if (mPreventDefault != PREVENT_DEFAULT_YES){
-                            doDoubleTap();
+                            mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
                             mTouchMode = TOUCH_DONE_MODE;
                         }
                         break;
@@ -5481,9 +5286,17 @@
                             if (mPreventDefault != PREVENT_DEFAULT_YES
                                     && (computeMaxScrollX() > 0
                                             || computeMaxScrollY() > 0)) {
-                                // UI takes control back, cancel WebCore touch
-                                cancelWebCoreTouchEvent(contentX, contentY,
-                                        true);
+                                // If the user has performed a very quick touch
+                                // sequence it is possible that we may get here
+                                // before WebCore has had a chance to process the events.
+                                // In this case, any call to preventDefault in the
+                                // JS touch handler will not have been executed yet.
+                                // Hence we will see both the UI (now) and WebCore
+                                // (when context switches) handling the event,
+                                // regardless of whether the web developer actually
+                                // doeses preventDefault in their touch handler. This
+                                // is the nature of our asynchronous touch model.
+
                                 // we will not rewrite drag code here, but we
                                 // will try fling if it applies.
                                 WebViewCore.reducePriority();
@@ -5501,13 +5314,16 @@
                             }
                         } else {
                             if (mSelectingText) {
-                                if (nativeHitSelection(contentX, contentY)) {
-                                    copySelection();
+                                // tapping on selection or controls does nothing
+                                if (!nativeHitSelection(contentX, contentY)) {
+                                    selectionDone();
                                 }
-                                selectionDone();
                                 break;
                             }
-                            if (mTouchMode == TOUCH_INIT_MODE) {
+                            // only trigger double tap if the WebView is
+                            // scalable
+                            if (mTouchMode == TOUCH_INIT_MODE
+                                    && (canZoomIn() || canZoomOut())) {
                                 mPrivateHandler.sendEmptyMessageDelayed(
                                         RELEASE_SINGLE_TAP, ViewConfiguration
                                                 .getDoubleTapTimeout());
@@ -5536,18 +5352,13 @@
                             mHeldMotionless = MOTIONLESS_IGNORE;
                             doFling();
                             break;
-                        } else {
-                            if (mScroller.springBack(mScrollX, mScrollY, 0,
-                                    computeMaxScrollX(), 0,
-                                    computeMaxScrollY())) {
-                                invalidate();
-                            }
                         }
                         // redraw in high-quality, as we're done dragging
                         mHeldMotionless = MOTIONLESS_TRUE;
                         invalidate();
                         // fall through
                     case TOUCH_DRAG_START_MODE:
+                    case TOUCH_DRAG_LAYER_MODE:
                         // TOUCH_DRAG_START_MODE should not happen for the real
                         // device as we almost certain will get a MOVE. But this
                         // is possible on emulator.
@@ -5561,8 +5372,6 @@
             }
             case MotionEvent.ACTION_CANCEL: {
                 if (mTouchMode == TOUCH_DRAG_MODE) {
-                    mScroller.springBack(mScrollX, mScrollY, 0,
-                            computeMaxScrollX(), 0, computeMaxScrollY());
                     invalidate();
                 }
                 cancelWebCoreTouchEvent(contentX, contentY, false);
@@ -5573,14 +5382,32 @@
         return true;
     }
 
+    private void passMultiTouchToWebKit(MotionEvent ev) {
+        TouchEventData ted = new TouchEventData();
+        ted.mAction = ev.getAction() & MotionEvent.ACTION_MASK;
+        final int count = ev.getPointerCount();
+        ted.mPoints = new Point[count];
+        for (int c = 0; c < count; c++) {
+            int x = viewToContentX((int) ev.getX(c) + mScrollX);
+            int y = viewToContentY((int) ev.getY(c) + mScrollY);
+            ted.mPoints[c] = new Point(x, y);
+        }
+        ted.mMetaState = ev.getMetaState();
+        ted.mReprocess = mDeferTouchProcess;
+        mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
+        cancelLongPress();
+        mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+        mPreventDefault = PREVENT_DEFAULT_IGNORE;
+    }
+
     private void cancelWebCoreTouchEvent(int x, int y, boolean removeEvents) {
         if (shouldForwardTouchEvent()) {
             if (removeEvents) {
                 mWebViewCore.removeMessages(EventHub.TOUCH_EVENT);
             }
             TouchEventData ted = new TouchEventData();
-            ted.mX = x;
-            ted.mY = y;
+            ted.mPoints = new Point[1];
+            ted.mPoints[0] = new Point(x, y);
             ted.mAction = MotionEvent.ACTION_CANCEL;
             mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
             mPreventDefault = PREVENT_DEFAULT_IGNORE;
@@ -5594,9 +5421,6 @@
         mLastTouchTime = eventTime;
         mVelocityTracker = VelocityTracker.obtain();
         mSnapScrollMode = SNAP_NONE;
-        if (mDragTracker != null) {
-            mDragTrackerHandler = new DragTrackerHandler(x, y, mDragTracker);
-        }
     }
 
     private void startDrag() {
@@ -5606,64 +5430,29 @@
         if (!mDragFromTextInput) {
             nativeHideCursor();
         }
-        WebSettings settings = getSettings();
-        if (settings.supportZoom()
-                && settings.getBuiltInZoomControls()
-                && !getZoomButtonsController().isVisible()
-                && mMinZoomScale < mMaxZoomScale
-                && (mHorizontalScrollBarMode != SCROLLBAR_ALWAYSOFF
-                        || mVerticalScrollBarMode != SCROLLBAR_ALWAYSOFF)) {
-            mZoomButtonsController.setVisible(true);
-            int count = settings.getDoubleTapToastCount();
-            if (mInZoomOverview && count > 0) {
-                settings.setDoubleTapToastCount(--count);
-                Toast.makeText(mContext,
-                        com.android.internal.R.string.double_tap_toast,
-                        Toast.LENGTH_LONG).show();
-            }
+
+        if (mHorizontalScrollBarMode != SCROLLBAR_ALWAYSOFF
+                || mVerticalScrollBarMode != SCROLLBAR_ALWAYSOFF) {
+            mZoomManager.invokeZoomPicker();
         }
     }
 
     private void doDrag(int deltaX, int deltaY) {
         if ((deltaX | deltaY) != 0) {
-            final int oldX = mScrollX;
-            final int oldY = mScrollY;
-            final int rangeX = computeMaxScrollX();
-            final int rangeY = computeMaxScrollY();
-
-            if (mEdgeGlowTop != null) {
-                // Save the deltas for overscroll glow.
-                mOverscrollDeltaX = deltaX;
-                mOverscrollDeltaY = deltaY;
-            }
-
-            overScrollBy(deltaX, deltaY, oldX, oldY,
-                    rangeX, rangeY,
-                    mOverscrollDistance, mOverscrollDistance, true);
-            if (mEdgeGlowTop != null &&
-                    (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished() ||
-                            !mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished())) {
-                invalidate();
-            }
-        }
-        if (!getSettings().getBuiltInZoomControls()) {
-            boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
-            if (mZoomControls != null && showPlusMinus) {
-                if (mZoomControls.getVisibility() == View.VISIBLE) {
-                    mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-                } else {
-                    mZoomControls.show(showPlusMinus, false);
+            if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
+                deltaX = viewToContentDimension(deltaX);
+                deltaY = viewToContentDimension(deltaY);
+                if (nativeScrollLayer(mScrollingLayer, deltaX, deltaY)) {
+                    invalidate();
                 }
-                mPrivateHandler.postDelayed(mZoomControlRunnable,
-                        ZOOM_CONTROLS_TIMEOUT);
+                return;
             }
+            scrollBy(deltaX, deltaY);
         }
+        mZoomManager.keepZoomPickerVisible();
     }
 
     private void stopTouch() {
-        if (mDragTrackerHandler != null) {
-            mDragTrackerHandler.stopDrag();
-        }
         // we also use mVelocityTracker == null to tell us that we are
         // not "moving around", so we can take the slower/prettier
         // mode in the drawing code
@@ -5671,20 +5460,9 @@
             mVelocityTracker.recycle();
             mVelocityTracker = null;
         }
-
-        // Release any pulled glows
-        if (mEdgeGlowTop != null) {
-            mEdgeGlowTop.onRelease();
-            mEdgeGlowBottom.onRelease();
-            mEdgeGlowLeft.onRelease();
-            mEdgeGlowRight.onRelease();
-        }
     }
 
     private void cancelTouch() {
-        if (mDragTrackerHandler != null) {
-            mDragTrackerHandler.stopDrag();
-        }
         // we also use mVelocityTracker == null to tell us that we are
         // not "moving around", so we can take the slower/prettier
         // mode in the drawing code
@@ -5692,16 +5470,8 @@
             mVelocityTracker.recycle();
             mVelocityTracker = null;
         }
-
-        // Release any pulled glows
-        if (mEdgeGlowTop != null) {
-            mEdgeGlowTop.onRelease();
-            mEdgeGlowBottom.onRelease();
-            mEdgeGlowLeft.onRelease();
-            mEdgeGlowRight.onRelease();
-        }
-
-        if (mTouchMode == TOUCH_DRAG_MODE) {
+        if (mTouchMode == TOUCH_DRAG_MODE ||
+                mTouchMode == TOUCH_DRAG_LAYER_MODE) {
             WebViewCore.resumePriority();
             WebViewCore.resumeUpdatePicture(mWebViewCore);
         }
@@ -5709,6 +5479,9 @@
         mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
         mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
         mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
+        if (getSettings().supportTouchOnly()) {
+            removeTouchHighlight(true);
+        }
         mHeldMotionless = MOTIONLESS_TRUE;
         mTouchMode = TOUCH_DONE_MODE;
         nativeHideCursor();
@@ -5806,7 +5579,8 @@
             }
             return false; // let common code in onKeyUp at it
         }
-        if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
+        if ((mMapTrackballToArrowKeys && mShiftIsPressed == false) ||
+                (mAccessibilityInjector != null || mAccessibilityScriptInjected)) {
             if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
             return false;
         }
@@ -5974,11 +5748,11 @@
                         + " mTrackballRemainsX=" + mTrackballRemainsX
                         + " mTrackballRemainsY=" + mTrackballRemainsY);
             }
-            if (mNativeClass != 0 && nativeFocusIsPlugin()) {
+            if (mNativeClass != 0 && nativePageShouldHandleShiftAndArrows()) {
                 for (int i = 0; i < count; i++) {
-                    letPluginHandleNavKey(selectKeyCode, time, true);
+                    letPageHandleNavKey(selectKeyCode, time, true);
                 }
-                letPluginHandleNavKey(selectKeyCode, time, false);
+                letPageHandleNavKey(selectKeyCode, time, false);
             } else if (navHandledKey(selectKeyCode, count, false, time)) {
                 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
             }
@@ -6009,17 +5783,30 @@
     }
 
     private int computeMaxScrollX() {
-        return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0);
+        return Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
     }
 
     private int computeMaxScrollY() {
-        return Math.max(computeRealVerticalScrollRange() + getTitleHeight()
+        return Math.max(computeVerticalScrollRange() + getTitleHeight()
                 - getViewHeightWithTitle(), 0);
     }
 
+    boolean updateScrollCoordinates(int x, int y) {
+        int oldX = mScrollX;
+        int oldY = mScrollY;
+        mScrollX = x;
+        mScrollY = y;
+        if (oldX != mScrollX || oldY != mScrollY) {
+            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     public void flingScroll(int vx, int vy) {
         mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
-                computeMaxScrollY(), mOverflingDistance, mOverflingDistance);
+                computeMaxScrollY());
         invalidate();
     }
 
@@ -6049,20 +5836,19 @@
         if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
             WebViewCore.resumePriority();
             WebViewCore.resumeUpdatePicture(mWebViewCore);
-            if (mScroller.springBack(mScrollX, mScrollY, 0, computeMaxScrollX(),
-                    0, computeMaxScrollY())) {
-                invalidate();
-            }
             return;
         }
         float currentVelocity = mScroller.getCurrVelocity();
-        if (mLastVelocity > 0 && currentVelocity > 0) {
+        float velocity = (float) Math.hypot(vx, vy);
+        if (mLastVelocity > 0 && currentVelocity > 0 && velocity
+                > mLastVelocity * MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION) {
             float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
                     - Math.atan2(vy, vx)));
             final float circle = (float) (Math.PI) * 2.0f;
             if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
                 vx += currentVelocity * mLastVelX / mLastVelocity;
                 vy += currentVelocity * mLastVelY / mLastVelocity;
+                velocity = (float) Math.hypot(vx, vy);
                 if (DebugFlags.WEB_VIEW) {
                     Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
                 }
@@ -6076,67 +5862,17 @@
                     + " maxX=" + maxX + " maxY=" + maxY
                     + " mScrollX=" + mScrollX + " mScrollY=" + mScrollY);
         }
-
-        // Allow sloppy flings without overscrolling at the edges.
-        if ((mScrollX == 0 || mScrollX == maxX) && Math.abs(vx) < Math.abs(vy)) {
-            vx = 0;
-        }
-        if ((mScrollY == 0 || mScrollY == maxY) && Math.abs(vy) < Math.abs(vx)) {
-            vy = 0;
-        }
-
-        if (mOverscrollDistance < mOverflingDistance) {
-            if (mScrollX == -mOverscrollDistance || mScrollX == maxX + mOverscrollDistance) {
-                vx = 0;
-            }
-            if (mScrollY == -mOverscrollDistance || mScrollY == maxY + mOverscrollDistance) {
-                vy = 0;
-            }
-        }
-
         mLastVelX = vx;
         mLastVelY = vy;
-        mLastVelocity = (float) Math.hypot(vx, vy);
+        mLastVelocity = velocity;
 
-        // no horizontal overscroll if the content just fits
-        mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY,
-                maxX == 0 ? 0 : mOverflingDistance, mOverflingDistance);
-        // Duration is calculated based on velocity. With range boundaries and overscroll
-        // we may not know how long the final animation will take. (Hence the deprecation
-        // warning on the call below.) It's not a big deal for scroll bars but if webcore
-        // resumes during this effect we will take a performance hit. See computeScroll;
-        // we resume webcore there when the animation is finished.
+        mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
         final int time = mScroller.getDuration();
+        mPrivateHandler.sendEmptyMessageDelayed(RESUME_WEBCORE_PRIORITY, time);
         awakenScrollBars(time);
         invalidate();
     }
 
-    private boolean zoomWithPreview(float scale, boolean updateTextWrapScale) {
-        float oldScale = mActualScale;
-        mInitialScrollX = mScrollX;
-        mInitialScrollY = mScrollY;
-
-        // snap to DEFAULT_SCALE if it is close
-        if (Math.abs(scale - mDefaultScale) < MINIMUM_SCALE_INCREMENT) {
-            scale = mDefaultScale;
-        }
-
-        setNewZoomScale(scale, updateTextWrapScale, false);
-
-        if (oldScale != mActualScale) {
-            // use mZoomPickerScale to see zoom preview first
-            mZoomStart = SystemClock.uptimeMillis();
-            mInvInitialZoomScale = 1.0f / oldScale;
-            mInvFinalZoomScale = 1.0f / mActualScale;
-            mZoomScale = mActualScale;
-            WebViewCore.pauseUpdatePicture(mWebViewCore);
-            invalidate();
-            return true;
-        } else {
-            return false;
-        }
-    }
-
     /**
      * Returns a view containing zoom controls i.e. +/- buttons. The caller is
      * in charge of installing this view to the view hierarchy. This view will
@@ -6156,81 +5892,29 @@
             Log.w(LOGTAG, "This WebView doesn't support zoom.");
             return null;
         }
-        if (mZoomControls == null) {
-            mZoomControls = createZoomControls();
-
-            /*
-             * need to be set to VISIBLE first so that getMeasuredHeight() in
-             * {@link #onSizeChanged()} can return the measured value for proper
-             * layout.
-             */
-            mZoomControls.setVisibility(View.VISIBLE);
-            mZoomControlRunnable = new Runnable() {
-                public void run() {
-
-                    /* Don't dismiss the controls if the user has
-                     * focus on them. Wait and check again later.
-                     */
-                    if (!mZoomControls.hasFocus()) {
-                        mZoomControls.hide();
-                    } else {
-                        mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-                        mPrivateHandler.postDelayed(mZoomControlRunnable,
-                                ZOOM_CONTROLS_TIMEOUT);
-                    }
-                }
-            };
-        }
-        return mZoomControls;
+        return mZoomManager.getExternalZoomPicker();
     }
 
-    private ExtendedZoomControls createZoomControls() {
-        ExtendedZoomControls zoomControls = new ExtendedZoomControls(mContext
-            , null);
-        zoomControls.setOnZoomInClickListener(new OnClickListener() {
-            public void onClick(View v) {
-                // reset time out
-                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-                mPrivateHandler.postDelayed(mZoomControlRunnable,
-                        ZOOM_CONTROLS_TIMEOUT);
-                zoomIn();
-            }
-        });
-        zoomControls.setOnZoomOutClickListener(new OnClickListener() {
-            public void onClick(View v) {
-                // reset time out
-                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-                mPrivateHandler.postDelayed(mZoomControlRunnable,
-                        ZOOM_CONTROLS_TIMEOUT);
-                zoomOut();
-            }
-        });
-        return zoomControls;
+    void dismissZoomControl() {
+        mZoomManager.dismissZoomPicker();
+    }
+
+    float getDefaultZoomScale() {
+        return mZoomManager.getDefaultScale();
     }
 
     /**
-     * Gets the {@link ZoomButtonsController} which can be used to add
-     * additional buttons to the zoom controls window.
-     *
-     * @return The instance of {@link ZoomButtonsController} used by this class,
-     *         or null if it is unavailable.
-     * @hide
+     * @return TRUE if the WebView can be zoomed in.
      */
-    public ZoomButtonsController getZoomButtonsController() {
-        if (mZoomButtonsController == null) {
-            mZoomButtonsController = new ZoomButtonsController(this);
-            mZoomButtonsController.setOnZoomListener(mZoomListener);
-            // ZoomButtonsController positions the buttons at the bottom, but in
-            // the middle. Change their layout parameters so they appear on the
-            // right.
-            View controls = mZoomButtonsController.getZoomControls();
-            ViewGroup.LayoutParams params = controls.getLayoutParams();
-            if (params instanceof FrameLayout.LayoutParams) {
-                FrameLayout.LayoutParams frameParams = (FrameLayout.LayoutParams) params;
-                frameParams.gravity = Gravity.RIGHT;
-            }
-        }
-        return mZoomButtonsController;
+    public boolean canZoomIn() {
+        return mZoomManager.canZoomIn();
+    }
+
+    /**
+     * @return TRUE if the WebView can be zoomed out.
+     */
+    public boolean canZoomOut() {
+        return mZoomManager.canZoomOut();
     }
 
     /**
@@ -6238,15 +5922,7 @@
      * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
      */
     public boolean zoomIn() {
-        // TODO: alternatively we can disallow this during draw history mode
-        switchOutDrawHistory();
-        mInZoomOverview = false;
-        // Center zooming to the center of the screen.
-        mZoomCenterX = getViewWidth() * .5f;
-        mZoomCenterY = getViewHeight() * .5f;
-        mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
-        mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
-        return zoomWithPreview(mActualScale * 1.25f, true);
+        return mZoomManager.zoomIn();
     }
 
     /**
@@ -6254,14 +5930,7 @@
      * @return TRUE if zoom out succeeds. FALSE if no zoom changes.
      */
     public boolean zoomOut() {
-        // TODO: alternatively we can disallow this during draw history mode
-        switchOutDrawHistory();
-        // Center zooming to the center of the screen.
-        mZoomCenterX = getViewWidth() * .5f;
-        mZoomCenterY = getViewHeight() * .5f;
-        mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
-        mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
-        return zoomWithPreview(mActualScale * 0.8f, true);
+        return mZoomManager.zoomOut();
     }
 
     private void updateSelection() {
@@ -6362,7 +6031,14 @@
         // mLastTouchX and mLastTouchY are the point in the current viewport
         int contentX = viewToContentX((int) mLastTouchX + mScrollX);
         int contentY = viewToContentY((int) mLastTouchY + mScrollY);
-        if (nativePointInNavCache(contentX, contentY, mNavSlop)) {
+        if (getSettings().supportTouchOnly()) {
+            removeTouchHighlight(false);
+            WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
+            // use "0" as generation id to inform WebKit to use the same x/y as
+            // it used when processing GET_TOUCH_HIGHLIGHT_RECTS
+            touchUpData.mMoveGeneration = 0;
+            mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
+        } else if (nativePointInNavCache(contentX, contentY, mNavSlop)) {
             WebViewCore.MotionUpData motionUpData = new WebViewCore
                     .MotionUpData();
             motionUpData.mFrame = nativeCacheHitFramePointer();
@@ -6378,7 +6054,7 @@
     }
 
     private void doMotionUp(int contentX, int contentY) {
-        if (mLogEvent && nativeMotionUp(contentX, contentY, mNavSlop)) {
+        if (nativeMotionUp(contentX, contentY, mNavSlop) && mLogEvent) {
             EventLog.writeEvent(EventLogTags.BROWSER_SNAP_CENTER);
         }
         if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
@@ -6390,27 +6066,16 @@
      * Return true if the view (Plugin) is fully visible and maximized inside
      * the WebView.
      */
-    private boolean isPluginFitOnScreen(ViewManager.ChildView view) {
-        int viewWidth = getViewWidth();
-        int viewHeight = getViewHeightWithTitle();
-        float scale = Math.min((float) viewWidth / view.width,
-                (float) viewHeight / view.height);
-        if (scale < mMinZoomScale) {
-            scale = mMinZoomScale;
-        } else if (scale > mMaxZoomScale) {
-            scale = mMaxZoomScale;
-        }
-        if (Math.abs(scale - mActualScale) < MINIMUM_SCALE_INCREMENT) {
-            if (contentToViewX(view.x) >= mScrollX
-                    && contentToViewX(view.x + view.width) <= mScrollX
-                            + viewWidth
-                    && contentToViewY(view.y) >= mScrollY
-                    && contentToViewY(view.y + view.height) <= mScrollY
-                            + viewHeight) {
-                return true;
-            }
-        }
-        return false;
+    boolean isPluginFitOnScreen(ViewManager.ChildView view) {
+        final int viewWidth = getViewWidth();
+        final int viewHeight = getViewHeightWithTitle();
+        float scale = Math.min((float) viewWidth / view.width, (float) viewHeight / view.height);
+        scale = mZoomManager.computeScaleWithLimits(scale);
+        return !mZoomManager.willScaleTriggerZoom(scale)
+                && contentToViewX(view.x) >= mScrollX
+                && contentToViewX(view.x + view.width) <= mScrollX + viewWidth
+                && contentToViewY(view.y) >= mScrollY
+                && contentToViewY(view.y + view.height) <= mScrollY + viewHeight;
     }
 
     /*
@@ -6419,22 +6084,19 @@
      * animated scroll to center it. If the zoom needs to be changed, find the
      * zoom center and do a smooth zoom transition.
      */
-    private void centerFitRect(int docX, int docY, int docWidth, int docHeight) {
+    void centerFitRect(int docX, int docY, int docWidth, int docHeight) {
         int viewWidth = getViewWidth();
         int viewHeight = getViewHeightWithTitle();
         float scale = Math.min((float) viewWidth / docWidth, (float) viewHeight
                 / docHeight);
-        if (scale < mMinZoomScale) {
-            scale = mMinZoomScale;
-        } else if (scale > mMaxZoomScale) {
-            scale = mMaxZoomScale;
-        }
-        if (Math.abs(scale - mActualScale) < MINIMUM_SCALE_INCREMENT) {
+        scale = mZoomManager.computeScaleWithLimits(scale);
+        if (!mZoomManager.willScaleTriggerZoom(scale)) {
             pinScrollTo(contentToViewX(docX + docWidth / 2) - viewWidth / 2,
                     contentToViewY(docY + docHeight / 2) - viewHeight / 2,
                     true, 0);
         } else {
-            float oldScreenX = docX * mActualScale - mScrollX;
+            float actualScale = mZoomManager.getScale();
+            float oldScreenX = docX * actualScale - mScrollX;
             float rectViewX = docX * scale;
             float rectViewWidth = docWidth * scale;
             float newMaxWidth = mContentWidth * scale;
@@ -6445,9 +6107,9 @@
             } else if (newScreenX > (newMaxWidth - rectViewX - rectViewWidth)) {
                 newScreenX = viewWidth - (newMaxWidth - rectViewX);
             }
-            mZoomCenterX = (oldScreenX * scale - newScreenX * mActualScale)
-                    / (scale - mActualScale);
-            float oldScreenY = docY * mActualScale + getTitleHeight()
+            float zoomCenterX = (oldScreenX * scale - newScreenX * actualScale)
+                    / (scale - actualScale);
+            float oldScreenY = docY * actualScale + getTitleHeight()
                     - mScrollY;
             float rectViewY = docY * scale + getTitleHeight();
             float rectViewHeight = docHeight * scale;
@@ -6459,109 +6121,10 @@
             } else if (newScreenY > (newMaxHeight - rectViewY - rectViewHeight)) {
                 newScreenY = viewHeight - (newMaxHeight - rectViewY);
             }
-            mZoomCenterY = (oldScreenY * scale - newScreenY * mActualScale)
-                    / (scale - mActualScale);
-            zoomWithPreview(scale, false);
-        }
-    }
-
-    void dismissZoomControl() {
-        if (mWebViewCore == null) {
-            // maybe called after WebView's destroy(). As we can't get settings,
-            // just hide zoom control for both styles.
-            if (mZoomButtonsController != null) {
-                mZoomButtonsController.setVisible(false);
-            }
-            if (mZoomControls != null) {
-                mZoomControls.hide();
-            }
-            return;
-        }
-        WebSettings settings = getSettings();
-        if (settings.getBuiltInZoomControls()) {
-            if (mZoomButtonsController != null) {
-                mZoomButtonsController.setVisible(false);
-            }
-        } else {
-            if (mZoomControlRunnable != null) {
-                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-            }
-            if (mZoomControls != null) {
-                mZoomControls.hide();
-            }
-        }
-    }
-
-    // Rule for double tap:
-    // 1. if the current scale is not same as the text wrap scale and layout
-    //    algorithm is NARROW_COLUMNS, fit to column;
-    // 2. if the current state is not overview mode, change to overview mode;
-    // 3. if the current state is overview mode, change to default scale.
-    private void doDoubleTap() {
-        if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
-            return;
-        }
-        mZoomCenterX = mLastTouchX;
-        mZoomCenterY = mLastTouchY;
-        mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
-        mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
-        WebSettings settings = getSettings();
-        settings.setDoubleTapToastCount(0);
-        // remove the zoom control after double tap
-        dismissZoomControl();
-        ViewManager.ChildView plugin = mViewManager.hitTest(mAnchorX, mAnchorY);
-        if (plugin != null) {
-            if (isPluginFitOnScreen(plugin)) {
-                mInZoomOverview = true;
-                // Force the titlebar fully reveal in overview mode
-                if (mScrollY < getTitleHeight()) mScrollY = 0;
-                zoomWithPreview((float) getViewWidth() / mZoomOverviewWidth,
-                        true);
-            } else {
-                mInZoomOverview = false;
-                centerFitRect(plugin.x, plugin.y, plugin.width, plugin.height);
-            }
-            return;
-        }
-        boolean zoomToDefault = false;
-        if ((settings.getLayoutAlgorithm() == WebSettings.LayoutAlgorithm.NARROW_COLUMNS)
-                && (Math.abs(mActualScale - mTextWrapScale) >= MINIMUM_SCALE_INCREMENT)) {
-            setNewZoomScale(mActualScale, true, true);
-            float overviewScale = (float) getViewWidth() / mZoomOverviewWidth;
-            if (Math.abs(mActualScale - overviewScale) < MINIMUM_SCALE_INCREMENT) {
-                mInZoomOverview = true;
-            }
-        } else if (!mInZoomOverview) {
-            float newScale = (float) getViewWidth() / mZoomOverviewWidth;
-            if (Math.abs(mActualScale - newScale) >= MINIMUM_SCALE_INCREMENT) {
-                mInZoomOverview = true;
-                // Force the titlebar fully reveal in overview mode
-                if (mScrollY < getTitleHeight()) mScrollY = 0;
-                zoomWithPreview(newScale, true);
-            } else if (Math.abs(mActualScale - mDefaultScale) >= MINIMUM_SCALE_INCREMENT) {
-                zoomToDefault = true;
-            }
-        } else {
-            zoomToDefault = true;
-        }
-        if (zoomToDefault) {
-            mInZoomOverview = false;
-            int left = nativeGetBlockLeftEdge(mAnchorX, mAnchorY, mActualScale);
-            if (left != NO_LEFTEDGE) {
-                // add a 5pt padding to the left edge.
-                int viewLeft = contentToViewX(left < 5 ? 0 : (left - 5))
-                        - mScrollX;
-                // Re-calculate the zoom center so that the new scroll x will be
-                // on the left edge.
-                if (viewLeft > 0) {
-                    mZoomCenterX = viewLeft * mDefaultScale
-                            / (mDefaultScale - mActualScale);
-                } else {
-                    scrollBy(viewLeft, 0);
-                    mZoomCenterX = 0;
-                }
-            }
-            zoomWithPreview(mDefaultScale, true);
+            float zoomCenterY = (oldScreenY * scale - newScreenY * actualScale)
+                    / (scale - actualScale);
+            mZoomManager.setZoomCenter(zoomCenterX, zoomCenterY);
+            mZoomManager.startZoomAnimation(scale, false);
         }
     }
 
@@ -6573,6 +6136,9 @@
 
     @Override
     public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+        // FIXME: If a subwindow is showing find, and the user touches the
+        // background window, it can steal focus.
+        if (mFindIsUp) return false;
         boolean result = false;
         if (inEditingMode()) {
             result = mWebTextView.requestFocus(direction,
@@ -6660,6 +6226,12 @@
     public boolean requestChildRectangleOnScreen(View child,
                                                  Rect rect,
                                                  boolean immediate) {
+        // don't scroll while in zoom animation. When it is done, we will adjust
+        // the necessary components (e.g., WebTextView if it is in editing mode)
+        if (mZoomManager.isFixedLengthAnimationInProgress()) {
+            return false;
+        }
+
         rect.offset(child.getLeft() - child.getScrollX(),
                 child.getTop() - child.getScrollY());
 
@@ -6802,7 +6374,8 @@
                 }
                 case SWITCH_TO_SHORTPRESS: {
                     if (mTouchMode == TOUCH_INIT_MODE) {
-                        if (mPreventDefault != PREVENT_DEFAULT_YES) {
+                        if (!getSettings().supportTouchOnly()
+                                && mPreventDefault != PREVENT_DEFAULT_YES) {
                             mTouchMode = TOUCH_SHORTPRESS_START_MODE;
                             updateSelection();
                         } else {
@@ -6816,26 +6389,25 @@
                     break;
                 }
                 case SWITCH_TO_LONGPRESS: {
+                    if (getSettings().supportTouchOnly()) {
+                        removeTouchHighlight(false);
+                    }
                     if (inFullScreenMode() || mDeferTouchProcess) {
                         TouchEventData ted = new TouchEventData();
                         ted.mAction = WebViewCore.ACTION_LONGPRESS;
-                        ted.mX = viewToContentX((int) mLastTouchX + mScrollX);
-                        ted.mY = viewToContentY((int) mLastTouchY + mScrollY);
+                        ted.mPoints = new Point[1];
+                        ted.mPoints[0] = new Point(viewToContentX((int) mLastTouchX + mScrollX),
+                                                   viewToContentY((int) mLastTouchY + mScrollY));
                         // metaState for long press is tricky. Should it be the
                         // state when the press started or when the press was
                         // released? Or some intermediary key state? For
                         // simplicity for now, we don't set it.
                         ted.mMetaState = 0;
                         ted.mReprocess = mDeferTouchProcess;
-                        if (mDeferTouchProcess) {
-                            ted.mViewX = mLastTouchX;
-                            ted.mViewY = mLastTouchY;
-                        }
                         mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                     } else if (mPreventDefault != PREVENT_DEFAULT_YES) {
                         mTouchMode = TOUCH_DONE_MODE;
                         performLongClick();
-                        rebuildWebTextView();
                     }
                     break;
                 }
@@ -6868,116 +6440,63 @@
                     spawnContentScrollTo(msg.arg1, msg.arg2);
                     break;
                 case UPDATE_ZOOM_RANGE: {
-                    WebViewCore.RestoreState restoreState
-                            = (WebViewCore.RestoreState) msg.obj;
+                    WebViewCore.ViewState viewState = (WebViewCore.ViewState) msg.obj;
                     // mScrollX contains the new minPrefWidth
-                    updateZoomRange(restoreState, getViewWidth(),
-                            restoreState.mScrollX, false);
+                    mZoomManager.updateZoomRange(viewState, getViewWidth(), viewState.mScrollX);
+                    break;
+                }
+                case REPLACE_BASE_CONTENT: {
+                    nativeReplaceBaseContent(msg.arg1);
                     break;
                 }
                 case NEW_PICTURE_MSG_ID: {
-                    // If we've previously delayed deleting a root
-                    // layer, do it now.
-                    if (mDelayedDeleteRootLayer) {
-                        mDelayedDeleteRootLayer = false;
-                        nativeSetRootLayer(0);
-                    }
-                    WebSettings settings = mWebViewCore.getSettings();
                     // called for new content
-                    final int viewWidth = getViewWidth();
-                    final WebViewCore.DrawData draw =
-                            (WebViewCore.DrawData) msg.obj;
-                    final Point viewSize = draw.mViewPoint;
-                    boolean useWideViewport = settings.getUseWideViewPort();
-                    WebViewCore.RestoreState restoreState = draw.mRestoreState;
-                    boolean hasRestoreState = restoreState != null;
-                    if (hasRestoreState) {
-                        updateZoomRange(restoreState, viewSize.x,
-                                draw.mMinPrefWidth, true);
+                    mUserScroll = false;
+                    final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
+                    setBaseLayer(draw.mBaseLayer, draw.mInvalRegion.getBounds());
+                    final Point viewSize = draw.mViewSize;
+                    WebViewCore.ViewState viewState = draw.mViewState;
+                    boolean isPictureAfterFirstLayout = viewState != null;
+                    if (isPictureAfterFirstLayout) {
+                        // Reset the last sent data here since dealing with new page.
+                        mLastWidthSent = 0;
+                        mZoomManager.onFirstLayout(draw);
                         if (!mDrawHistory) {
-                            mInZoomOverview = false;
-
-                            if (mInitialScaleInPercent > 0) {
-                                setNewZoomScale(mInitialScaleInPercent / 100.0f,
-                                    mInitialScaleInPercent != mTextWrapScale * 100,
-                                    false);
-                            } else if (restoreState.mViewScale > 0) {
-                                mTextWrapScale = restoreState.mTextWrapScale;
-                                setNewZoomScale(restoreState.mViewScale, false,
-                                    false);
-                            } else {
-                                mInZoomOverview = useWideViewport
-                                    && settings.getLoadWithOverviewMode();
-                                float scale;
-                                if (mInZoomOverview) {
-                                    scale = (float) viewWidth
-                                        / DEFAULT_VIEWPORT_WIDTH;
-                                } else {
-                                    scale = restoreState.mTextWrapScale;
-                                }
-                                setNewZoomScale(scale, Math.abs(scale
-                                    - mTextWrapScale) >= MINIMUM_SCALE_INCREMENT,
-                                    false);
-                            }
-                            setContentScrollTo(restoreState.mScrollX,
-                                restoreState.mScrollY);
+                            setContentScrollTo(viewState.mScrollX, viewState.mScrollY);
                             // As we are on a new page, remove the WebTextView. This
                             // is necessary for page loads driven by webkit, and in
                             // particular when the user was on a password field, so
                             // the WebTextView was visible.
-                            clearTextEntry(false);
-                            // update the zoom buttons as the scale can be changed
-                            if (getSettings().getBuiltInZoomControls()) {
-                                updateZoomButtonsEnabled();
-                            }
+                            clearTextEntry();
                         }
                     }
+
                     // We update the layout (i.e. request a layout from the
                     // view system) if the last view size that we sent to
                     // WebCore matches the view size of the picture we just
                     // received in the fixed dimension.
                     final boolean updateLayout = viewSize.x == mLastWidthSent
                             && viewSize.y == mLastHeightSent;
-                    recordNewContentSize(draw.mWidthHeight.x,
-                            draw.mWidthHeight.y
-                            + (mFindIsUp ? mFindHeight : 0), updateLayout);
+                    recordNewContentSize(draw.mContentSize.x,
+                            draw.mContentSize.y, updateLayout);
                     if (DebugFlags.WEB_VIEW) {
                         Rect b = draw.mInvalRegion.getBounds();
                         Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
                                 b.left+","+b.top+","+b.right+","+b.bottom+"}");
                     }
                     invalidateContentRect(draw.mInvalRegion.getBounds());
+
                     if (mPictureListener != null) {
                         mPictureListener.onNewPicture(WebView.this, capturePicture());
                     }
-                    if (useWideViewport) {
-                        // limit mZoomOverviewWidth upper bound to
-                        // sMaxViewportWidth so that if the page doesn't behave
-                        // well, the WebView won't go insane. limit the lower
-                        // bound to match the default scale for mobile sites.
-                        mZoomOverviewWidth = Math.min(sMaxViewportWidth, Math
-                                .max((int) (viewWidth / mDefaultScale), Math
-                                        .max(draw.mMinPrefWidth,
-                                                draw.mViewPoint.x)));
-                    }
-                    if (!mMinZoomScaleFixed) {
-                        mMinZoomScale = (float) viewWidth / mZoomOverviewWidth;
-                    }
-                    if (!mDrawHistory && mInZoomOverview) {
-                        // fit the content width to the current view. Ignore
-                        // the rounding error case.
-                        if (Math.abs((viewWidth * mInvActualScale)
-                                - mZoomOverviewWidth) > 1) {
-                            setNewZoomScale((float) viewWidth
-                                    / mZoomOverviewWidth, Math.abs(mActualScale
-                                    - mTextWrapScale) < MINIMUM_SCALE_INCREMENT,
-                                    false);
-                        }
-                    }
+
+                    // update the zoom information based on the new picture
+                    mZoomManager.onNewPicture(draw);
+
                     if (draw.mFocusSizeChanged && inEditingMode()) {
                         mFocusSizeChanged = true;
                     }
-                    if (hasRestoreState) {
+                    if (isPictureAfterFirstLayout) {
                         mViewManager.postReadyToDrawAll();
                     }
                     break;
@@ -7011,16 +6530,16 @@
                     break;
                 case REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID:
                     displaySoftKeyboard(true);
+                    // fall through to UPDATE_TEXT_SELECTION_MSG_ID
+                case UPDATE_TEXT_SELECTION_MSG_ID:
                     updateTextSelectionFromMessage(msg.arg1, msg.arg2,
                             (WebViewCore.TextSelectionData) msg.obj);
                     break;
-                case UPDATE_TEXT_SELECTION_MSG_ID:
-                    // If no textfield was in focus, and the user touched one,
-                    // causing it to send this message, then WebTextView has not
-                    // been set up yet.  Rebuild it so it can set its selection.
-                    rebuildWebTextView();
-                    updateTextSelectionFromMessage(msg.arg1, msg.arg2,
-                            (WebViewCore.TextSelectionData) msg.obj);
+                case FORM_DID_BLUR:
+                    if (inEditingMode()
+                            && mWebTextView.isSameTextField(msg.arg1)) {
+                        hideSoftKeyboard();
+                    }
                     break;
                 case RETURN_LABEL:
                     if (inEditingMode()
@@ -7036,20 +6555,19 @@
                         }
                     }
                     break;
-                case MOVE_OUT_OF_PLUGIN:
+                case UNHANDLED_NAV_KEY:
                     navHandledKey(msg.arg1, 1, false, 0);
                     break;
                 case UPDATE_TEXT_ENTRY_MSG_ID:
                     // this is sent after finishing resize in WebViewCore. Make
                     // sure the text edit box is still on the  screen.
-                    selectionDone();
                     if (inEditingMode() && nativeCursorIsTextInput()) {
                         mWebTextView.bringIntoView();
                         rebuildWebTextView();
                     }
                     break;
                 case CLEAR_TEXT_ENTRY:
-                    clearTextEntry(false);
+                    clearTextEntry();
                     break;
                 case INVAL_RECT_MSG_ID: {
                     Rect r = (Rect)msg.obj;
@@ -7062,23 +6580,6 @@
                     }
                     break;
                 }
-                case IMMEDIATE_REPAINT_MSG_ID: {
-                    invalidate();
-                    break;
-                }
-                case SET_ROOT_LAYER_MSG_ID: {
-                    if (0 == msg.arg1) {
-                        // Null indicates deleting the old layer, but
-                        // don't actually do so until we've got the
-                        // new page to display.
-                        mDelayedDeleteRootLayer = true;
-                    } else {
-                        mDelayedDeleteRootLayer = false;
-                        nativeSetRootLayer(msg.arg1);
-                        invalidate();
-                    }
-                    break;
-                }
                 case REQUEST_FORM_DATA:
                     AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
                     if (mWebTextView.isSameTextField(msg.arg1)) {
@@ -7122,43 +6623,46 @@
                             mPreventDefault = msg.arg2 == 1 ? PREVENT_DEFAULT_YES
                                     : PREVENT_DEFAULT_NO;
                         }
+                        if (mPreventDefault == PREVENT_DEFAULT_YES) {
+                            mTouchHighlightRegion.setEmpty();
+                        }
                     } else if (msg.arg2 == 0) {
                         // prevent default is not called in WebCore, so the
                         // message needs to be reprocessed in UI
                         TouchEventData ted = (TouchEventData) msg.obj;
                         switch (ted.mAction) {
                             case MotionEvent.ACTION_DOWN:
-                                mLastDeferTouchX = ted.mViewX;
-                                mLastDeferTouchY = ted.mViewY;
+                                mLastDeferTouchX = contentToViewX(ted.mPoints[0].x)
+                                        - mScrollX;
+                                mLastDeferTouchY = contentToViewY(ted.mPoints[0].y)
+                                        - mScrollY;
                                 mDeferTouchMode = TOUCH_INIT_MODE;
                                 break;
                             case MotionEvent.ACTION_MOVE: {
                                 // no snapping in defer process
+                                int x = contentToViewX(ted.mPoints[0].x) - mScrollX;
+                                int y = contentToViewY(ted.mPoints[0].y) - mScrollY;
                                 if (mDeferTouchMode != TOUCH_DRAG_MODE) {
                                     mDeferTouchMode = TOUCH_DRAG_MODE;
-                                    mLastDeferTouchX = ted.mViewX;
-                                    mLastDeferTouchY = ted.mViewY;
+                                    mLastDeferTouchX = x;
+                                    mLastDeferTouchY = y;
                                     startDrag();
                                 }
                                 int deltaX = pinLocX((int) (mScrollX
-                                        + mLastDeferTouchX - ted.mViewX))
+                                        + mLastDeferTouchX - x))
                                         - mScrollX;
                                 int deltaY = pinLocY((int) (mScrollY
-                                        + mLastDeferTouchY - ted.mViewY))
+                                        + mLastDeferTouchY - y))
                                         - mScrollY;
                                 doDrag(deltaX, deltaY);
-                                if (deltaX != 0) mLastDeferTouchX = ted.mViewX;
-                                if (deltaY != 0) mLastDeferTouchY = ted.mViewY;
+                                if (deltaX != 0) mLastDeferTouchX = x;
+                                if (deltaY != 0) mLastDeferTouchY = y;
                                 break;
                             }
                             case MotionEvent.ACTION_UP:
                             case MotionEvent.ACTION_CANCEL:
                                 if (mDeferTouchMode == TOUCH_DRAG_MODE) {
                                     // no fling in defer process
-                                    mScroller.springBack(mScrollX, mScrollY, 0,
-                                            computeMaxScrollX(), 0,
-                                            computeMaxScrollY());
-                                    invalidate();
                                     WebViewCore.resumePriority();
                                     WebViewCore.resumeUpdatePicture(mWebViewCore);
                                 }
@@ -7166,9 +6670,9 @@
                                 break;
                             case WebViewCore.ACTION_DOUBLETAP:
                                 // doDoubleTap() needs mLastTouchX/Y as anchor
-                                mLastTouchX = ted.mViewX;
-                                mLastTouchY = ted.mViewY;
-                                doDoubleTap();
+                                mLastTouchX = contentToViewX(ted.mPoints[0].x) - mScrollX;
+                                mLastTouchY = contentToViewY(ted.mPoints[0].y) - mScrollY;
+                                mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
                                 mDeferTouchMode = TOUCH_DONE_MODE;
                                 break;
                             case WebViewCore.ACTION_LONGPRESS:
@@ -7176,7 +6680,6 @@
                                 if (hitTest != null && hitTest.mType
                                         != HitTestResult.UNKNOWN_TYPE) {
                                     performLongClick();
-                                    rebuildWebTextView();
                                 }
                                 mDeferTouchMode = TOUCH_DONE_MODE;
                                 break;
@@ -7194,8 +6697,8 @@
 
                 case FIND_AGAIN:
                     // Ignore if find has been dismissed.
-                    if (mFindIsUp) {
-                        findAll(mLastFind);
+                    if (mFindIsUp && mFindCallback != null) {
+                        mFindCallback.findAll();
                     }
                     break;
 
@@ -7223,9 +6726,9 @@
                     View view = (View) msg.obj;
                     int npp = msg.arg1;
 
-                    if (mFullScreenHolder != null) {
+                    if (inFullScreenMode()) {
                         Log.w(LOGTAG, "Should not have another full screen.");
-                        mFullScreenHolder.dismiss();
+                        dismissFullScreenMode();
                     }
                     mFullScreenHolder = new PluginFullScreenHolder(WebView.this, npp);
                     mFullScreenHolder.setContentView(view);
@@ -7236,10 +6739,7 @@
                     break;
                 }
                 case HIDE_FULLSCREEN:
-                    if (inFullScreenMode()) {
-                        mFullScreenHolder.dismiss();
-                        mFullScreenHolder = null;
-                    }
+                    dismissFullScreenMode();
                     break;
 
                 case DOM_FOCUS_CHANGED:
@@ -7300,7 +6800,6 @@
 
                 case CENTER_FIT_RECT:
                     Rect r = (Rect)msg.obj;
-                    mInZoomOverview = false;
                     centerFitRect(r.left, r.top, r.width(), r.height());
                     break;
 
@@ -7309,6 +6808,51 @@
                     mVerticalScrollBarMode = msg.arg2;
                     break;
 
+                case SELECTION_STRING_CHANGED:
+                    if (mAccessibilityInjector != null) {
+                        String selectionString = (String) msg.obj;
+                        mAccessibilityInjector.onSelectionStringChange(selectionString);
+                    }
+                    break;
+
+                case SET_TOUCH_HIGHLIGHT_RECTS:
+                    invalidate(mTouchHighlightRegion.getBounds());
+                    mTouchHighlightRegion.setEmpty();
+                    if (msg.obj != null) {
+                        ArrayList<Rect> rects = (ArrayList<Rect>) msg.obj;
+                        for (Rect rect : rects) {
+                            Rect viewRect = contentToViewRect(rect);
+                            // some sites, like stories in nytimes.com, set
+                            // mouse event handler in the top div. It is not
+                            // user friendly to highlight the div if it covers
+                            // more than half of the screen.
+                            if (viewRect.width() < getWidth() >> 1
+                                    || viewRect.height() < getHeight() >> 1) {
+                                mTouchHighlightRegion.union(viewRect);
+                                invalidate(viewRect);
+                            } else {
+                                Log.w(LOGTAG, "Skip the huge selection rect:"
+                                        + viewRect);
+                            }
+                        }
+                    }
+                    break;
+
+                case SAVE_WEBARCHIVE_FINISHED:
+                    SaveWebArchiveMessage saveMessage = (SaveWebArchiveMessage)msg.obj;
+                    if (saveMessage.mCallback != null) {
+                        saveMessage.mCallback.onReceiveValue(saveMessage.mResultFile);
+                    }
+                    break;
+
+                case SET_AUTOFILLABLE:
+                    mAutoFillData = (WebViewCore.AutoFillData) msg.obj;
+                    if (mWebTextView != null) {
+                        mWebTextView.setAutoFillable(mAutoFillData.getQueryId());
+                        rebuildWebTextView();
+                    }
+                    break;
+
                 default:
                     super.handleMessage(msg);
                     break;
@@ -7618,37 +7162,6 @@
                 new InvokeListBox(array, enabledArray, selectedArray));
     }
 
-    private void updateZoomRange(WebViewCore.RestoreState restoreState,
-            int viewWidth, int minPrefWidth, boolean updateZoomOverview) {
-        if (restoreState.mMinScale == 0) {
-            if (restoreState.mMobileSite) {
-                if (minPrefWidth > Math.max(0, viewWidth)) {
-                    mMinZoomScale = (float) viewWidth / minPrefWidth;
-                    mMinZoomScaleFixed = false;
-                    if (updateZoomOverview) {
-                        WebSettings settings = getSettings();
-                        mInZoomOverview = settings.getUseWideViewPort() &&
-                                settings.getLoadWithOverviewMode();
-                    }
-                } else {
-                    mMinZoomScale = restoreState.mDefaultScale;
-                    mMinZoomScaleFixed = true;
-                }
-            } else {
-                mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
-                mMinZoomScaleFixed = false;
-            }
-        } else {
-            mMinZoomScale = restoreState.mMinScale;
-            mMinZoomScaleFixed = true;
-        }
-        if (restoreState.mMaxScale == 0) {
-            mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
-        } else {
-            mMaxZoomScale = restoreState.mMaxScale;
-        }
-    }
-
     /*
      * Request a dropdown menu for a listbox with single selection or a single
      * <select> element.
@@ -7687,7 +7200,7 @@
      */
     private void sendMoveMouseIfLatest(boolean removeFocus) {
         if (removeFocus) {
-            clearTextEntry(true);
+            clearTextEntry();
         }
         mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
                 cursorData());
@@ -7731,7 +7244,7 @@
         // FIXME the divisor should be retrieved from somewhere
         // the closest thing today is hard-coded into ScrollView.java
         // (from ScrollView.java, line 363)   int maxJump = height/2;
-        return Math.round(height * mInvActualScale);
+        return Math.round(height * mZoomManager.getInvScale());
     }
 
     /**
@@ -7742,10 +7255,10 @@
     }
 
     /**
-     * Pass the key to the plugin.  This assumes that nativeFocusIsPlugin()
-     * returned true.
+     * Pass the key directly to the page.  This assumes that
+     * nativePageShouldHandleShiftAndArrows() returned true.
      */
-    private void letPluginHandleNavKey(int keyCode, long time, boolean down) {
+    private void letPageHandleNavKey(int keyCode, long time, boolean down) {
         int keyEventAction;
         int eventHubAction;
         if (down) {
@@ -7820,6 +7333,16 @@
     }
 
     /**
+     * @return If the page should receive Shift and arrows.
+     */
+    private boolean pageShouldHandleShiftAndArrows() {
+        // TODO: Maybe the injected script should announce its presence in
+        // the page meta-tag so the nativePageShouldHandleShiftAndArrows
+        // will check that as one of the conditions it looks for
+        return (nativePageShouldHandleShiftAndArrows() || mAccessibilityScriptInjected);
+    }
+
+    /**
      * Set the background color. It's white by default. Pass
      * zero to make the view transparent.
      * @param color   the ARGB color described by Color.java
@@ -7842,7 +7365,7 @@
      * @hide only needs to be accessible to Browser and testing
      */
     public void drawPage(Canvas canvas) {
-        mWebViewCore.drawContentPicture(canvas, 0, false, false);
+        nativeDraw(canvas, 0, 0, false);
     }
 
     /**
@@ -7856,6 +7379,17 @@
     }
 
     /**
+     * Toggle whether multi touch events should be sent to webkit
+     * no matter if UI wants to handle it first.
+     *
+     * @hide This is only used by the webkit layout test.
+     */
+    public void setDeferMultiTouch(boolean value) {
+        mDeferMultitouch = value;
+        Log.v(LOGTAG, "set mDeferMultitouch to " + value);
+    }
+
+    /**
      *  Update our cache with updatedText.
      *  @param updatedText  The new text to put in our cache.
      */
@@ -7865,6 +7399,10 @@
         nativeUpdateCachedTextfield(updatedText, mTextGeneration);
     }
 
+    /*package*/ void autoFillForm(int autoFillQueryId) {
+        mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM, autoFillQueryId, /* unused */0);
+    }
+
     private native int nativeCacheHitFramePointer();
     private native Rect nativeCacheHitNodeBounds();
     private native int nativeCacheHitNodePointer();
@@ -7886,11 +7424,21 @@
     private native boolean  nativeCursorWantsKeyEvents();
     private native void     nativeDebugDump();
     private native void     nativeDestroy();
-    private native boolean  nativeEvaluateLayersAnimations();
-    private native void     nativeExtendSelection(int x, int y);
-    private native void     nativeDrawExtras(Canvas canvas, int extra);
+
+    /**
+     * Draw the picture set with a background color and extra. If
+     * "splitIfNeeded" is true and the return value is not 0, the return value
+     * MUST be passed to WebViewCore with SPLIT_PICTURE_SET message so that the
+     * native allocation can be freed.
+     */
+    private native int nativeDraw(Canvas canvas, int color, int extra,
+            boolean splitIfNeeded);
     private native void     nativeDumpDisplayTree(String urlOrNull);
-    private native int      nativeFindAll(String findLower, String findUpper);
+    private native boolean  nativeEvaluateLayersAnimations();
+    private native boolean  nativeDrawGL(Rect rect, float scale, int extras);
+    private native void     nativeExtendSelection(int x, int y);
+    private native int      nativeFindAll(String findLower, String findUpper,
+            boolean sameAsLastSearch);
     private native void     nativeFindNext(boolean forward);
     /* package */ native int      nativeFocusCandidateFramePointer();
     /* package */ native boolean  nativeFocusCandidateHasNextTextfield();
@@ -7934,6 +7482,14 @@
             boolean noScroll);
     private native int      nativeMoveGeneration();
     private native void     nativeMoveSelection(int x, int y);
+    /**
+     * @return true if the page should get the shift and arrow keys, rather
+     * than select text/navigation.
+     *
+     * If the focus is a plugin, or if the focus and cursor match and are
+     * a contentEditable element, then the page should handle these keys.
+     */
+    private native boolean  nativePageShouldHandleShiftAndArrows();
     private native boolean  nativePointInNavCache(int x, int y, int slop);
     // Like many other of our native methods, you must make sure that
     // mNativeClass is not null before calling this method.
@@ -7948,13 +7504,15 @@
     private native void     nativeSetExtendSelection();
     private native void     nativeSetFindIsEmpty();
     private native void     nativeSetFindIsUp(boolean isUp);
-    private native void     nativeSetFollowedLink(boolean followed);
     private native void     nativeSetHeightCanMeasure(boolean measure);
-    private native void     nativeSetRootLayer(int layer);
+    private native void     nativeSetBaseLayer(int layer, Rect invalRect);
+    private native void     nativeShowCursorTimed();
+    private native void     nativeReplaceBaseContent(int content);
+    private native void     nativeCopyBaseContentToPicture(Picture pict);
+    private native boolean  nativeHasContent();
     private native void     nativeSetSelectionPointer(boolean set,
             float scale, int x, int y);
     private native boolean  nativeStartSelection(int x, int y);
-    private native void     nativeSetSelectionRegion(boolean set);
     private native Rect     nativeSubtractLayers(Rect content);
     private native int      nativeTextGeneration();
     // Never call this version except by updateCachedTextfield(String) -
@@ -7963,6 +7521,10 @@
             int generation);
     private native boolean  nativeWordSelection(int x, int y);
     // return NO_LEFTEDGE means failure.
-    private static final int NO_LEFTEDGE = -1;
-    private native int      nativeGetBlockLeftEdge(int x, int y, float scale);
+    static final int NO_LEFTEDGE = -1;
+    native int nativeGetBlockLeftEdge(int x, int y, float scale);
+
+    // Returns a pointer to the scrollable LayerAndroid at the given point.
+    private native int      nativeScrollableLayer(int x, int y);
+    private native boolean  nativeScrollLayer(int layer, int dx, int dy);
 }
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 02c7210..1f8eeba 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -89,6 +89,7 @@
      * @deprecated This method is no longer called. When the WebView encounters
      *             a redirect loop, it will cancel the load.
      */
+    @Deprecated
     public void onTooManyRedirects(WebView view, Message cancelMsg,
             Message continueMsg) {
         cancelMsg.sendToTarget();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index f54b207..1b7eb2a 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -17,14 +17,8 @@
 package android.webkit;
 
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.Cursor;
-import android.graphics.Canvas;
-import android.graphics.DrawFilter;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
-import android.graphics.Picture;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -34,14 +28,15 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
-import android.provider.Browser;
-import android.provider.OpenableColumns;
+import android.provider.MediaStore;
 import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.view.KeyEvent;
-import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.View;
+import android.webkit.DeviceMotionService;
+import android.webkit.DeviceMotionAndOrientationManager;
+import android.webkit.DeviceOrientationService;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -118,13 +113,18 @@
     private int mViewportDensityDpi = -1;
 
     private int mRestoredScale = 0;
-    private int mRestoredScreenWidthScale = 0;
+    private int mRestoredTextWrapScale = 0;
     private int mRestoredX = 0;
     private int mRestoredY = 0;
 
     private int mWebkitScrollX = 0;
     private int mWebkitScrollY = 0;
 
+    private DeviceMotionAndOrientationManager mDeviceMotionAndOrientationManager =
+            new DeviceMotionAndOrientationManager(this);
+    private DeviceMotionService mDeviceMotionService;
+    private DeviceOrientationService mDeviceOrientationService;
+
     // The thread name used to identify the WebCore thread and for use in
     // debugging other classes that require operation within the WebCore thread.
     /* package */ static final String THREAD_NAME = "WebViewCoreThread";
@@ -260,9 +260,11 @@
      * Given mimeType, check whether it's supported in Android media framework.
      * mimeType could be such as "audio/ogg" and "video/mp4".
      */
-    /* package */ static boolean supportsMimeType(String mimeType) {
-        return MediaFile.getFileTypeForMimeType(mimeType) > 0;
+    /* package */ static boolean isSupportedMediaMimeType(String mimeType) {
+        int fileType = MediaFile.getFileTypeForMimeType(mimeType);
+        return MediaFile.isAudioFileType(fileType) || MediaFile.isVideoFileType(fileType);
     }
+
     /**
      * Add an error message to the client's console.
      * @param message The message to add
@@ -284,37 +286,49 @@
         mCallbackProxy.onJsAlert(url, message);
     }
 
+    /**
+     * Called by JNI.  Send a message to the UI thread to hide the soft keyboard
+     * if the node pointed to by nodePointer is still in focus.
+     * @param nodePointer The node which just blurred.
+     */
+    private void formDidBlur(int nodePointer) {
+        if (mWebView == null) return;
+        Message.obtain(mWebView.mPrivateHandler, WebView.FORM_DID_BLUR,
+                nodePointer, 0).sendToTarget();
+    }
 
     /**
      * Called by JNI.  Open a file chooser to upload a file.
-     * @return String version of the URI plus the name of the file.
-     * FIXME: Just return the URI here, and in FileSystem::pathGetFileName, call
-     * into Java to get the filename.
+     * @param acceptType The value of the 'accept' attribute of the
+     *         input tag associated with this file picker.
+     * @return String version of the URI.
      */
-    private String openFileChooser() {
-        Uri uri = mCallbackProxy.openFileChooser();
-        if (uri == null) return "";
-        // Find out the name, and append it to the URI.
-        // Webkit will treat the name as the filename, and
-        // the URI as the path.  The URI will be used
-        // in BrowserFrame to get the actual data.
-        Cursor cursor = mContext.getContentResolver().query(
-                uri,
-                new String[] { OpenableColumns.DISPLAY_NAME },
-                null,
-                null,
-                null);
-        String name = "";
-        if (cursor != null) {
-            try {
-                if (cursor.moveToNext()) {
-                    name = cursor.getString(0);
+    private String openFileChooser(String acceptType) {
+        Uri uri = mCallbackProxy.openFileChooser(acceptType);
+        if (uri != null) {
+            String filePath = "";
+            // Note - querying for MediaStore.Images.Media.DATA
+            // seems to work for all content URIs, not just images
+            Cursor cursor = mContext.getContentResolver().query(
+                    uri,
+                    new String[] { MediaStore.Images.Media.DATA },
+                    null, null, null);
+            if (cursor != null) {
+                try {
+                    if (cursor.moveToNext()) {
+                        filePath = cursor.getString(0);
+                    }
+                } finally {
+                    cursor.close();
                 }
-            } finally {
-                cursor.close();
+            } else {
+                filePath = uri.getLastPathSegment();
             }
+            String uriString = uri.toString();
+            BrowserFrame.sJavaBridge.storeFilePathForContentUri(filePath, uriString);
+            return uriString;
         }
-        return uri.toString() + "/" + name;
+        return "";
     }
 
     /**
@@ -432,6 +446,13 @@
         return mCallbackProxy.onJsTimeout();
     }
 
+    /**
+     * Notify the webview that this is an installable web app.
+     */
+    protected void setInstallableWebApp() {
+        mCallbackProxy.setInstallableWebApp();
+    }
+
     //-------------------------------------------------------------------------
     // JNI methods
     //-------------------------------------------------------------------------
@@ -443,36 +464,26 @@
      */
     private native void nativeClearContent();
 
-    /**
-     * Create a flat picture from the set of pictures.
-     */
-    private native void nativeCopyContentToPicture(Picture picture);
-
-    /**
-     * Draw the picture set with a background color. Returns true
-     * if some individual picture took too long to draw and can be
-     * split into parts. Called from the UI thread.
-     */
-    private native boolean nativeDrawContent(Canvas canvas, int color);
-
-    /**
-     * check to see if picture is blank and in progress
-     */
-    private native boolean nativePictureReady();
+    private native void nativeContentInvalidateAll();
 
     /**
      * Redraw a portion of the picture set. The Point wh returns the
      * width and height of the overall picture.
      */
-    private native boolean nativeRecordContent(Region invalRegion, Point wh);
+    private native int nativeRecordContent(Region invalRegion, Point wh);
+
+    /**
+     * Update the layers' content
+     */
+    private native int nativeUpdateLayers();
 
     private native boolean nativeFocusBoundsChanged();
 
     /**
-     * Splits slow parts of the picture set. Called from the webkit
-     * thread after nativeDrawContent returns true.
+     * Splits slow parts of the picture set. Called from the webkit thread after
+     * WebView.nativeDraw() returns content to be split.
      */
-    private native void nativeSplitContent();
+    private native void nativeSplitContent(int content);
 
     private native boolean nativeKey(int keyCode, int unichar,
             int repeatCount, boolean isShift, boolean isAlt, boolean isSym,
@@ -488,12 +499,12 @@
         of layout/line-breaking. These coordinates are in document space,
         which is the same as View coords unless we have zoomed the document
         (see nativeSetZoom).
-        screenWidth is used by layout to wrap column around. If viewport uses
-        fixed size, screenWidth can be different from width with zooming.
+        textWrapWidth is used by layout to wrap column around. If viewport uses
+        fixed size, textWrapWidth can be different from width with zooming.
         should this be called nativeSetViewPortSize?
     */
-    private native void nativeSetSize(int width, int height, int screenWidth,
-            float scale, int realScreenWidth, int screenHeight, int anchorX,
+    private native void nativeSetSize(int width, int height, int textWrapWidth,
+            float scale, int screenWidth, int screenHeight, int anchorX,
             int anchorY, boolean ignoreHeight);
 
     private native int nativeGetContentMinPrefWidth();
@@ -523,8 +534,8 @@
     private native void nativeTouchUp(int touchGeneration,
             int framePtr, int nodePtr, int x, int y);
 
-    private native boolean nativeHandleTouchEvent(int action, int x, int y,
-            int metaState);
+    private native boolean nativeHandleTouchEvent(int action, int[] x, int[] y,
+            int count, int metaState);
 
     private native void nativeUpdateFrameCache();
 
@@ -584,7 +595,17 @@
     /**
      * Provide WebCore with the previously visted links from the history database
      */
-    private native void  nativeProvideVisitedHistory(String[] history);
+    private native void nativeProvideVisitedHistory(String[] history);
+
+    /**
+     * Modifies the current selection.
+     *
+     * @param direction The direction in which to alter the selection.
+     * @param granularity The granularity of the selection modification.
+     *
+     * @return The selection string.
+     */
+    private native String nativeModifySelection(int direction, int granularity);
 
     // EventHub for processing messages
     private final EventHub mEventHub;
@@ -705,6 +726,35 @@
         int mY;
     }
 
+    static class TouchHighlightData {
+        int mX;
+        int mY;
+        int mSlop;
+    }
+
+    static class AutoFillData {
+        public AutoFillData() {
+            mQueryId = WebTextView.FORM_NOT_AUTOFILLABLE;
+            mPreview = "";
+        }
+
+        public AutoFillData(int queryId, String preview) {
+            mQueryId = queryId;
+            mPreview = preview;
+        }
+
+        public int getQueryId() {
+            return mQueryId;
+        }
+
+        public String getPreviewString() {
+            return mPreview;
+        }
+
+        private int mQueryId;
+        private String mPreview;
+    }
+
     // mAction of TouchEventData can be MotionEvent.getAction() which uses the
     // last two bytes or one of the following values
     static final int ACTION_LONGPRESS = 0x100;
@@ -712,12 +762,9 @@
 
     static class TouchEventData {
         int mAction;
-        int mX;
-        int mY;
+        Point[] mPoints;
         int mMetaState;
         boolean mReprocess;
-        float mViewX;
-        float mViewY;
     }
 
     static class GeolocationPermissionsData {
@@ -726,9 +773,8 @@
         boolean mRemember;
     }
 
-
-
         static final String[] HandlerDebugString = {
+            "REVEAL_SELECTION", // 96
             "REQUEST_LABEL", // 97
             "UPDATE_FRAME_CACHE_IF_LOADING", // = 98
             "SCROLL_TEXT_INPUT", // = 99
@@ -779,10 +825,13 @@
             "ON_RESUME",    // = 144
             "FREE_MEMORY",  // = 145
             "VALID_NODE_BOUNDS", // = 146
+            "SAVE_WEBARCHIVE", // = 147
+            "WEBKIT_DRAW_LAYERS", // = 148;
         };
 
     class EventHub {
         // Message Ids
+        static final int REVEAL_SELECTION = 96;
         static final int REQUEST_LABEL = 97;
         static final int UPDATE_FRAME_CACHE_IF_LOADING = 98;
         static final int SCROLL_TEXT_INPUT = 99;
@@ -845,6 +894,12 @@
         static final int FREE_MEMORY = 145;
         static final int VALID_NODE_BOUNDS = 146;
 
+        // Load and save web archives
+        static final int SAVE_WEBARCHIVE = 147;
+
+        // Update layers
+        static final int WEBKIT_DRAW_LAYERS = 148;
+
         // Network-based messaging
         static final int CLEAR_SSL_PREF_TABLE = 150;
 
@@ -873,6 +928,16 @@
         static final int ADD_PACKAGE_NAME = 185;
         static final int REMOVE_PACKAGE_NAME = 186;
 
+        static final int GET_TOUCH_HIGHLIGHT_RECTS = 187;
+        static final int REMOVE_TOUCH_HIGHLIGHT_RECTS = 188;
+
+        // accessibility support
+        static final int MODIFY_SELECTION = 190;
+
+        static final int USE_MOCK_DEVICE_ORIENTATION = 191;
+
+        static final int AUTOFILL_FORM = 192;
+
         // private message ids
         private static final int DESTROY =     200;
 
@@ -893,6 +958,9 @@
          */
         private EventHub() {}
 
+        private static final int FIRST_PACKAGE_MSG_ID = REVEAL_SELECTION;
+        private static final int LAST_PACKAGE_MSG_ID = VALID_NODE_BOUNDS;
+
         /**
          * Transfer all messages to the newly created webcore thread handler.
          */
@@ -904,11 +972,11 @@
                 @Override
                 public void handleMessage(Message msg) {
                     if (DebugFlags.WEB_VIEW_CORE) {
-                        Log.v(LOGTAG, (msg.what < REQUEST_LABEL
-                                || msg.what
-                                > VALID_NODE_BOUNDS ? Integer.toString(msg.what)
+                        Log.v(LOGTAG, (msg.what < FIRST_PACKAGE_MSG_ID
+                                || msg.what > LAST_PACKAGE_MSG_ID
+                                ? Integer.toString(msg.what)
                                 : HandlerDebugString[msg.what
-                                        - REQUEST_LABEL])
+                                        - FIRST_PACKAGE_MSG_ID])
                                 + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
                                 + " obj=" + msg.obj);
                     }
@@ -917,6 +985,10 @@
                             webkitDraw();
                             break;
 
+                        case WEBKIT_DRAW_LAYERS:
+                            webkitDrawLayers();
+                            break;
+
                         case DESTROY:
                             // Time to take down the world. Cancel all pending
                             // loads and destroy the native view and frame.
@@ -929,6 +1001,10 @@
                             }
                             break;
 
+                        case REVEAL_SELECTION:
+                            nativeRevealSelection();
+                            break;
+
                         case REQUEST_LABEL:
                             if (mWebView != null) {
                                 int nodePointer = msg.arg2;
@@ -1036,7 +1112,7 @@
                             // note: these are in document coordinates
                             // (inv-zoom)
                             Point pt = (Point) msg.obj;
-                            nativeSetScrollOffset(msg.arg1, pt.x, pt.y);
+                            nativeSetScrollOffset(msg.arg1, msg.arg2, pt.x, pt.y);
                             break;
 
                         case SET_GLOBAL_BOUNDS:
@@ -1162,12 +1238,19 @@
 
                         case TOUCH_EVENT: {
                             TouchEventData ted = (TouchEventData) msg.obj;
+                            final int count = ted.mPoints.length;
+                            int[] xArray = new int[count];
+                            int[] yArray = new int[count];
+                            for (int c = 0; c < count; c++) {
+                                xArray[c] = ted.mPoints[c].x;
+                                yArray[c] = ted.mPoints[c].y;
+                            }
                             Message.obtain(
                                     mWebView.mPrivateHandler,
                                     WebView.PREVENT_TOUCH_ID,
                                     ted.mAction,
-                                    nativeHandleTouchEvent(ted.mAction, ted.mX,
-                                            ted.mY, ted.mMetaState) ? 1 : 0,
+                                    nativeHandleTouchEvent(ted.mAction, xArray,
+                                            yArray, count, ted.mMetaState) ? 1 : 0,
                                     ted.mReprocess ? ted : null).sendToTarget();
                             break;
                         }
@@ -1246,6 +1329,12 @@
                             nativeSetSelection(msg.arg1, msg.arg2);
                             break;
 
+                        case MODIFY_SELECTION:
+                            String selectionString = nativeModifySelection(msg.arg1, msg.arg2);
+                            mWebView.mPrivateHandler.obtainMessage(WebView.SELECTION_STRING_CHANGED,
+                                    selectionString).sendToTarget();
+                            break;
+
                         case LISTBOX_CHOICES:
                             SparseBooleanArray choices = (SparseBooleanArray)
                                     msg.obj;
@@ -1286,6 +1375,15 @@
                             nativeSetJsFlags((String)msg.obj);
                             break;
 
+                        case SAVE_WEBARCHIVE:
+                            WebView.SaveWebArchiveMessage saveMessage =
+                                (WebView.SaveWebArchiveMessage)msg.obj;
+                            saveMessage.mResultFile =
+                                saveWebArchive(saveMessage.mBasename, saveMessage.mAutoname);
+                            mWebView.mPrivateHandler.obtainMessage(
+                                WebView.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget();
+                            break;
+
                         case GEOLOCATION_PERMISSIONS_PROVIDE:
                             GeolocationPermissionsData data =
                                     (GeolocationPermissionsData) msg.obj;
@@ -1299,7 +1397,9 @@
                             break;
 
                         case SPLIT_PICTURE_SET:
-                            nativeSplitContent();
+                            nativeSplitContent(msg.arg1);
+                            mWebView.mPrivateHandler.obtainMessage(
+                                    WebView.REPLACE_BASE_CONTENT, msg.arg1, 0);
                             mSplitPictureIsScheduled = false;
                             break;
 
@@ -1365,6 +1465,29 @@
                             BrowserFrame.sJavaBridge.removePackageName(
                                     (String) msg.obj);
                             break;
+
+                        case GET_TOUCH_HIGHLIGHT_RECTS:
+                            TouchHighlightData d = (TouchHighlightData) msg.obj;
+                            ArrayList<Rect> rects = nativeGetTouchHighlightRects
+                                    (d.mX, d.mY, d.mSlop);
+                            mWebView.mPrivateHandler.obtainMessage(
+                                    WebView.SET_TOUCH_HIGHLIGHT_RECTS, rects)
+                                    .sendToTarget();
+                            break;
+
+                        case REMOVE_TOUCH_HIGHLIGHT_RECTS:
+                            mWebView.mPrivateHandler.obtainMessage(
+                                    WebView.SET_TOUCH_HIGHLIGHT_RECTS, null)
+                                    .sendToTarget();
+                            break;
+
+                        case USE_MOCK_DEVICE_ORIENTATION:
+                            useMockDeviceOrientation();
+                            break;
+
+                        case AUTOFILL_FORM:
+                            nativeAutoFillForm(msg.arg1);
+                            break;
                     }
                 }
             };
@@ -1570,6 +1693,13 @@
         mBrowserFrame.loadUrl(url, extraHeaders);
     }
 
+    private String saveWebArchive(String filename, boolean autoname) {
+        if (DebugFlags.WEB_VIEW_CORE) {
+            Log.v(LOGTAG, " CORE saveWebArchive " + filename + " " + autoname);
+        }
+        return mBrowserFrame.saveWebArchive(filename, autoname);
+    }
+
     private void key(KeyEvent evt, boolean isDown) {
         if (DebugFlags.WEB_VIEW_CORE) {
             Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
@@ -1590,11 +1720,12 @@
             if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
                     && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
                 if (DebugFlags.WEB_VIEW_CORE) {
-                    Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
+                    Log.v(LOGTAG, "key: arrow unused by page: " + keyCode);
                 }
                 if (mWebView != null && evt.isDown()) {
                     Message.obtain(mWebView.mPrivateHandler,
-                            WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
+                            WebView.UNHANDLED_NAV_KEY, keyCode,
+                            0).sendToTarget();
                 }
                 return;
             }
@@ -1625,7 +1756,7 @@
         if (mSettings.getUseWideViewPort()) {
             if (mViewportWidth == -1) {
                 if (mSettings.getLayoutAlgorithm() ==
-                        WebSettings.LayoutAlgorithm.NORMAL) {
+                        WebSettings.LayoutAlgorithm.NORMAL || mSettings.getUseFixedViewport()) {
                     width = WebView.DEFAULT_VIEWPORT_WIDTH;
                 } else {
                     /*
@@ -1645,7 +1776,12 @@
                                     nativeGetContentMinPrefWidth())));
                 }
             } else if (mViewportWidth > 0) {
-                width = Math.max(w, mViewportWidth);
+                if (mSettings.getUseFixedViewport()) {
+                    // Use website specified or desired fixed viewport width.
+                    width = mViewportWidth;
+                } else {
+                    width = Math.max(w, mViewportWidth);
+                }
             } else {
                 width = textwrapWidth;
             }
@@ -1690,8 +1826,17 @@
         return usedQuota;
     }
 
+    // called from UI thread
+    void splitContent(int content) {
+        if (!mSplitPictureIsScheduled) {
+            mSplitPictureIsScheduled = true;
+            sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0);
+        }
+    }
+
     // Used to avoid posting more than one draw message.
     private boolean mDrawIsScheduled;
+    private boolean mDrawLayersIsScheduled;
 
     // Used to avoid posting more than one split picture message.
     private boolean mSplitPictureIsScheduled;
@@ -1699,11 +1844,11 @@
     // Used to suspend drawing.
     private boolean mDrawIsPaused;
 
-    // mRestoreState is set in didFirstLayout(), and reset in the next
-    // webkitDraw after passing it to the UI thread.
-    private RestoreState mRestoreState = null;
+    // mInitialViewState is set by didFirstLayout() and then reset in the
+    // next webkitDraw after passing the state to the UI thread.
+    private ViewState mInitialViewState = null;
 
-    static class RestoreState {
+    static class ViewState {
         float mMinScale;
         float mMaxScale;
         float mViewScale;
@@ -1716,32 +1861,51 @@
 
     static class DrawData {
         DrawData() {
+            mBaseLayer = 0;
             mInvalRegion = new Region();
-            mWidthHeight = new Point();
+            mContentSize = new Point();
         }
+        int mBaseLayer;
         Region mInvalRegion;
-        Point mViewPoint;
-        Point mWidthHeight;
+        // view size that was used by webkit during the most recent layout
+        Point mViewSize;
+        Point mContentSize;
         int mMinPrefWidth;
-        RestoreState mRestoreState; // only non-null if it is for the first
-                                    // picture set after the first layout
+        // only non-null if it is for the first picture set after the first layout
+        ViewState mViewState;
         boolean mFocusSizeChanged;
     }
 
+    // Only update the layers' content, not the base surface
+    // PictureSet.
+    private void webkitDrawLayers() {
+        mDrawLayersIsScheduled = false;
+        if (mDrawIsScheduled) {
+            removeMessages(EventHub.WEBKIT_DRAW);
+            webkitDraw();
+            return;
+        }
+        DrawData draw = new DrawData();
+        draw.mBaseLayer = nativeUpdateLayers();
+        webkitDraw(draw);
+    }
+
     private void webkitDraw() {
         mDrawIsScheduled = false;
         DrawData draw = new DrawData();
         if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
-        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
-                == false) {
+        draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mContentSize);
+        if (draw.mBaseLayer == 0) {
             if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
             return;
         }
+        webkitDraw(draw);
+    }
+
+    private void webkitDraw(DrawData draw) {
         if (mWebView != null) {
-            // Send the native view size that was used during the most recent
-            // layout.
             draw.mFocusSizeChanged = nativeFocusBoundsChanged();
-            draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
+            draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight);
             if (mSettings.getUseWideViewPort()) {
                 draw.mMinPrefWidth = Math.max(
                         mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH
@@ -1749,9 +1913,9 @@
                                         : mViewportWidth),
                         nativeGetContentMinPrefWidth());
             }
-            if (mRestoreState != null) {
-                draw.mRestoreState = mRestoreState;
-                mRestoreState = null;
+            if (mInitialViewState != null) {
+                draw.mViewState = mInitialViewState;
+                mInitialViewState = null;
             }
             if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
             Message.obtain(mWebView.mPrivateHandler,
@@ -1766,51 +1930,6 @@
         }
     }
 
-    ///////////////////////////////////////////////////////////////////////////
-    // These are called from the UI thread, not our thread
-
-    static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
-                                         Paint.DITHER_FLAG |
-                                         Paint.SUBPIXEL_TEXT_FLAG;
-    static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
-                                           Paint.DITHER_FLAG;
-
-    final DrawFilter mZoomFilter =
-                    new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
-    // If we need to trade better quality for speed, set mScrollFilter to null
-    final DrawFilter mScrollFilter =
-                new PaintFlagsDrawFilter(SCROLL_BITS, 0);
-
-    /* package */ void drawContentPicture(Canvas canvas, int color,
-                                          boolean animatingZoom,
-                                          boolean animatingScroll) {
-        DrawFilter df = null;
-        if (animatingZoom) {
-            df = mZoomFilter;
-        } else if (animatingScroll) {
-            df = mScrollFilter;
-        }
-        canvas.setDrawFilter(df);
-        boolean tookTooLong = nativeDrawContent(canvas, color);
-        canvas.setDrawFilter(null);
-        if (tookTooLong && mSplitPictureIsScheduled == false) {
-            mSplitPictureIsScheduled = true;
-            sendMessage(EventHub.SPLIT_PICTURE_SET);
-        }
-    }
-
-    /* package */ synchronized boolean pictureReady() {
-        return 0 != mNativeClass ? nativePictureReady() : false;
-    }
-
-    /*package*/ synchronized Picture copyContentPicture() {
-        Picture result = new Picture();
-        if (0 != mNativeClass) {
-            nativeCopyContentToPicture(result);
-        }
-        return result;
-    }
-
     static void reducePriority() {
         // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
         sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
@@ -1832,6 +1951,8 @@
         // called from UI thread while WEBKIT_DRAW is just pulled out of the
         // queue in WebCore thread to be executed. Then update won't be blocked.
         if (core != null) {
+            if (!core.getSettings().enableSmoothTransition()) return;
+
             synchronized (core) {
                 core.mDrawIsPaused = true;
                 if (core.mDrawIsScheduled) {
@@ -1844,12 +1965,16 @@
 
     static void resumeUpdatePicture(WebViewCore core) {
         if (core != null) {
+            // if mDrawIsPaused is true, ignore the setting, continue to resume
+            if (!core.mDrawIsPaused
+                    && !core.getSettings().enableSmoothTransition()) return;
+
             synchronized (core) {
                 core.mDrawIsPaused = false;
-                if (core.mDrawIsScheduled) {
-                    core.mDrawIsScheduled = false;
-                    core.contentDraw();
-                }
+                // always redraw on resume to reenable gif animations
+                core.mDrawIsScheduled = false;
+                core.nativeContentInvalidateAll();
+                core.contentDraw();
             }
         }
     }
@@ -1887,6 +2012,15 @@
         }
     }
 
+    // called from JNI
+    void layersDraw() {
+        synchronized (this) {
+            if (mDrawLayersIsScheduled) return;
+            mDrawLayersIsScheduled = true;
+            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW_LAYERS));
+        }
+    }
+
     // called by JNI
     private void contentScrollBy(int dx, int dy, boolean animate) {
         if (!mBrowserFrame.firstLayoutDone()) {
@@ -1986,24 +2120,6 @@
         mRepaintScheduled = false;
     }
 
-    // called by JNI
-    private void sendImmediateRepaint() {
-        if (mWebView != null && !mRepaintScheduled) {
-            mRepaintScheduled = true;
-            Message.obtain(mWebView.mPrivateHandler,
-                           WebView.IMMEDIATE_REPAINT_MSG_ID).sendToTarget();
-        }
-    }
-
-    // called by JNI
-    private void setRootLayer(int layer) {
-        if (mWebView != null) {
-            Message.obtain(mWebView.mPrivateHandler,
-                           WebView.SET_ROOT_LAYER_MSG_ID,
-                           layer, 0).sendToTarget();
-        }
-    }
-
     /* package */ WebView getWebView() {
         return mWebView;
     }
@@ -2020,18 +2136,24 @@
 
         if (mWebView == null) return;
 
-        boolean updateRestoreState = standardLoad || mRestoredScale > 0;
-        setupViewport(updateRestoreState);
+        boolean updateViewState = standardLoad || mRestoredScale > 0;
+        setupViewport(updateViewState);
         // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will
-        // be called after the WebView restore the state. If updateRestoreState
+        // be called after the WebView updates its state. If updateRestoreState
         // is false, start to draw now as it is ready.
-        if (!updateRestoreState) {
+        if (!updateViewState) {
             mWebView.mViewManager.postReadyToDrawAll();
         }
 
+        // remove the touch highlight when moving to a new page
+        if (getSettings().supportTouchOnly()) {
+            mEventHub.sendMessage(Message.obtain(null,
+                    EventHub.REMOVE_TOUCH_HIGHLIGHT_RECTS));
+        }
+
         // reset the scroll position, the restored offset and scales
         mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY
-                = mRestoredScale = mRestoredScreenWidthScale = 0;
+                = mRestoredScale = mRestoredTextWrapScale = 0;
     }
 
     // called by JNI
@@ -2045,15 +2167,17 @@
         }
     }
 
-    private void setupViewport(boolean updateRestoreState) {
+    private void setupViewport(boolean updateViewState) {
         // set the viewport settings from WebKit
         setViewportSettingsFromNative();
 
         // adjust the default scale to match the densityDpi
         float adjust = 1.0f;
         if (mViewportDensityDpi == -1) {
-            if (WebView.DEFAULT_SCALE_PERCENT != 100) {
-                adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f;
+            // convert default zoom scale to a integer (percentage) to avoid any
+            // issues with floating point comparisons
+            if (mWebView != null && (int)(mWebView.getDefaultZoomScale() * 100) != 100) {
+                adjust = mWebView.getDefaultZoomScale();
             }
         } else if (mViewportDensityDpi > 0) {
             adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
@@ -2095,17 +2219,17 @@
         }
 
         // if mViewportWidth is 0, it means device-width, always update.
-        if (mViewportWidth != 0 && !updateRestoreState) {
-            RestoreState restoreState = new RestoreState();
-            restoreState.mMinScale = mViewportMinimumScale / 100.0f;
-            restoreState.mMaxScale = mViewportMaximumScale / 100.0f;
-            restoreState.mDefaultScale = adjust;
+        if (mViewportWidth != 0 && !updateViewState) {
+            ViewState viewState = new ViewState();
+            viewState.mMinScale = mViewportMinimumScale / 100.0f;
+            viewState.mMaxScale = mViewportMaximumScale / 100.0f;
+            viewState.mDefaultScale = adjust;
             // as mViewportWidth is not 0, it is not mobile site.
-            restoreState.mMobileSite = false;
+            viewState.mMobileSite = false;
             // for non-mobile site, we don't need minPrefWidth, set it as 0
-            restoreState.mScrollX = 0;
+            viewState.mScrollX = 0;
             Message.obtain(mWebView.mPrivateHandler,
-                    WebView.UPDATE_ZOOM_RANGE, restoreState).sendToTarget();
+                    WebView.UPDATE_ZOOM_RANGE, viewState).sendToTarget();
             return;
         }
 
@@ -2126,32 +2250,32 @@
         } else {
             webViewWidth = Math.round(viewportWidth * mCurrentViewScale);
         }
-        mRestoreState = new RestoreState();
-        mRestoreState.mMinScale = mViewportMinimumScale / 100.0f;
-        mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f;
-        mRestoreState.mDefaultScale = adjust;
-        mRestoreState.mScrollX = mRestoredX;
-        mRestoreState.mScrollY = mRestoredY;
-        mRestoreState.mMobileSite = (0 == mViewportWidth);
+        mInitialViewState = new ViewState();
+        mInitialViewState.mMinScale = mViewportMinimumScale / 100.0f;
+        mInitialViewState.mMaxScale = mViewportMaximumScale / 100.0f;
+        mInitialViewState.mDefaultScale = adjust;
+        mInitialViewState.mScrollX = mRestoredX;
+        mInitialViewState.mScrollY = mRestoredY;
+        mInitialViewState.mMobileSite = (0 == mViewportWidth);
         if (mRestoredScale > 0) {
-            mRestoreState.mViewScale = mRestoredScale / 100.0f;
-            if (mRestoredScreenWidthScale > 0) {
-                mRestoreState.mTextWrapScale =
-                        mRestoredScreenWidthScale / 100.0f;
+            mInitialViewState.mViewScale = mRestoredScale / 100.0f;
+            if (mRestoredTextWrapScale > 0) {
+                mInitialViewState.mTextWrapScale = mRestoredTextWrapScale / 100.0f;
             } else {
-                mRestoreState.mTextWrapScale = mRestoreState.mViewScale;
+                mInitialViewState.mTextWrapScale = mInitialViewState.mViewScale;
             }
         } else {
             if (mViewportInitialScale > 0) {
-                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
+                mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
                         mViewportInitialScale / 100.0f;
-            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) {
-                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
+            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth &&
+                !mWebView.getSettings().getUseFixedViewport()) {
+                mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
                         (float) webViewWidth / mViewportWidth;
             } else {
-                mRestoreState.mTextWrapScale = adjust;
+                mInitialViewState.mTextWrapScale = adjust;
                 // 0 will trigger WebView to turn on zoom overview mode
-                mRestoreState.mViewScale = 0;
+                mInitialViewState.mViewScale = 0;
             }
         }
 
@@ -2192,15 +2316,15 @@
                 // mViewScale as 0 means it is in zoom overview mode. So we don't
                 // know the exact scale. If mRestoredScale is non-zero, use it;
                 // otherwise just use mTextWrapScale as the initial scale.
-                data.mScale = mRestoreState.mViewScale == 0
+                data.mScale = mInitialViewState.mViewScale == 0
                         ? (mRestoredScale > 0 ? mRestoredScale / 100.0f
-                                : mRestoreState.mTextWrapScale)
-                        : mRestoreState.mViewScale;
+                                : mInitialViewState.mTextWrapScale)
+                        : mInitialViewState.mViewScale;
                 if (DebugFlags.WEB_VIEW_CORE) {
                     Log.v(LOGTAG, "setupViewport"
                              + " mRestoredScale=" + mRestoredScale
-                             + " mViewScale=" + mRestoreState.mViewScale
-                             + " mTextWrapScale=" + mRestoreState.mTextWrapScale
+                             + " mViewScale=" + mInitialViewState.mViewScale
+                             + " mTextWrapScale=" + mInitialViewState.mTextWrapScale
                              );
                 }
                 data.mWidth = Math.round(webViewWidth / data.mScale);
@@ -2213,7 +2337,7 @@
                         Math.round(mWebView.getViewHeight() / data.mScale)
                         : mCurrentViewHeight * data.mWidth / viewportWidth;
                 data.mTextWrapWidth = Math.round(webViewWidth
-                        / mRestoreState.mTextWrapScale);
+                        / mInitialViewState.mTextWrapScale);
                 data.mIgnoreHeight = false;
                 data.mAnchorX = data.mAnchorY = 0;
                 // send VIEW_SIZE_CHANGED to the front of the queue so that we
@@ -2226,20 +2350,12 @@
     }
 
     // called by JNI
-    private void restoreScale(int scale) {
+    private void restoreScale(int scale, int textWrapScale) {
         if (mBrowserFrame.firstLayoutDone() == false) {
             mRestoredScale = scale;
-        }
-    }
-
-    // called by JNI
-    private void restoreScreenWidthScale(int scale) {
-        if (!mSettings.getUseWideViewPort()) {
-            return;
-        }
-
-        if (mBrowserFrame.firstLayoutDone() == false) {
-            mRestoredScreenWidthScale = scale;
+            if (mSettings.getUseWideViewPort()) {
+                mRestoredTextWrapScale = textWrapScale;
+            }
         }
     }
 
@@ -2289,6 +2405,7 @@
     }
 
     private native void nativeUpdateFrameCacheIfLoading();
+    private native void nativeRevealSelection();
     private native String nativeRequestLabel(int framePtr, int nodePtr);
     /**
      * Scroll the focused textfield to (xPercent, y) in document space
@@ -2296,7 +2413,7 @@
     private native void nativeScrollFocusedTextInput(float xPercent, int y);
 
     // these must be in document space (i.e. not scaled/zoomed).
-    private native void nativeSetScrollOffset(int gen, int dx, int dy);
+    private native void nativeSetScrollOffset(int gen, int userScrolled, int dx, int dy);
 
     private native void nativeSetGlobalBounds(int x, int y, int w, int h);
 
@@ -2337,6 +2454,14 @@
         }
     }
 
+    private void setWebTextViewAutoFillable(int queryId, String preview) {
+        if (mWebView != null) {
+            Message.obtain(mWebView.mPrivateHandler, WebView.SET_AUTOFILLABLE,
+                    new AutoFillData(queryId, preview))
+                    .sendToTarget();
+        }
+    }
+
     // called by JNI
     private Context getContext() {
         return mContext;
@@ -2477,6 +2602,32 @@
                 hMode, vMode).sendToTarget();
     }
 
+    private void useMockDeviceOrientation() {
+        mDeviceMotionAndOrientationManager.useMock();
+    }
+
+    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+        mDeviceMotionAndOrientationManager.setMockOrientation(canProvideAlpha, alpha,
+                canProvideBeta, beta, canProvideGamma, gamma);
+    }
+
+    protected DeviceMotionService getDeviceMotionService() {
+        if (mDeviceMotionService == null) {
+            mDeviceMotionService =
+                    new DeviceMotionService(mDeviceMotionAndOrientationManager, mContext);
+        }
+        return mDeviceMotionService;
+    }
+
+    protected DeviceOrientationService getDeviceOrientationService() {
+        if (mDeviceOrientationService == null) {
+            mDeviceOrientationService =
+                    new DeviceOrientationService(mDeviceMotionAndOrientationManager, mContext);
+        }
+        return mDeviceOrientationService;
+    }
+
     private native void nativePause();
     private native void nativeResume();
     private native void nativeFreeMemory();
@@ -2484,4 +2635,8 @@
     private native boolean nativeValidNodeAndBounds(int frame, int node,
             Rect bounds);
 
+    private native ArrayList<Rect> nativeGetTouchHighlightRects(int x, int y,
+            int slop);
+
+   private native void nativeAutoFillForm(int queryId);
 }
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index 7acd9ba..8f89678 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -67,6 +67,9 @@
     private final Object mFormLock = new Object();
     private final Object mHttpAuthLock = new Object();
 
+    // TODO: The Chromium HTTP stack handles cookies independently.
+    // We should consider removing the cookies table if and when we switch to
+    // the Chromium HTTP stack for good.
     private static final String mTableNames[] = {
         "cookies", "password", "formurl", "formdata", "httpauth"
     };
@@ -173,112 +176,140 @@
 
     private static int mCacheTransactionRefcount;
 
-    private WebViewDatabase() {
+    // Initially true until the background thread completes.
+    private boolean mInitialized = false;
+
+    private WebViewDatabase(final Context context) {
+        new Thread() {
+            @Override
+            public void run() {
+                init(context);
+            }
+        }.start();
+
         // Singleton only, use getInstance()
     }
 
     public static synchronized WebViewDatabase getInstance(Context context) {
         if (mInstance == null) {
-            mInstance = new WebViewDatabase();
-            try {
-                mDatabase = context
-                        .openOrCreateDatabase(DATABASE_FILE, 0, null);
-            } catch (SQLiteException e) {
-                // try again by deleting the old db and create a new one
-                if (context.deleteDatabase(DATABASE_FILE)) {
-                    mDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0,
-                            null);
-                }
-            }
+            mInstance = new WebViewDatabase(context);
+        }
+        return mInstance;
+    }
 
-            // mDatabase should not be null, 
-            // the only case is RequestAPI test has problem to create db 
-            if (mDatabase != null && mDatabase.getVersion() != DATABASE_VERSION) {
-                mDatabase.beginTransaction();
-                try {
-                    upgradeDatabase();
-                    mDatabase.setTransactionSuccessful();
-                } finally {
-                    mDatabase.endTransaction();
-                }
-            }
+    private synchronized void init(Context context) {
+        if (mInitialized) {
+            return;
+        }
 
-            if (mDatabase != null) {
-                // use per table Mutex lock, turn off database lock, this
-                // improves performance as database's ReentrantLock is expansive
-                mDatabase.setLockingEnabled(false);
-            }
-
-            try {
-                mCacheDatabase = context.openOrCreateDatabase(
-                        CACHE_DATABASE_FILE, 0, null);
-            } catch (SQLiteException e) {
-                // try again by deleting the old db and create a new one
-                if (context.deleteDatabase(CACHE_DATABASE_FILE)) {
-                    mCacheDatabase = context.openOrCreateDatabase(
-                            CACHE_DATABASE_FILE, 0, null);
-                }
-            }
-
-            // mCacheDatabase should not be null, 
-            // the only case is RequestAPI test has problem to create db 
-            if (mCacheDatabase != null
-                    && mCacheDatabase.getVersion() != CACHE_DATABASE_VERSION) {
-                mCacheDatabase.beginTransaction();
-                try {
-                    upgradeCacheDatabase();
-                    bootstrapCacheDatabase();
-                    mCacheDatabase.setTransactionSuccessful();
-                } finally {
-                    mCacheDatabase.endTransaction();
-                }
-                // Erase the files from the file system in the 
-                // case that the database was updated and the 
-                // there were existing cache content
-                CacheManager.removeAllCacheFiles();
-            }
-
-            if (mCacheDatabase != null) {
-                // use read_uncommitted to speed up READ
-                mCacheDatabase.execSQL("PRAGMA read_uncommitted = true;");
-                // as only READ can be called in the non-WebViewWorkerThread,
-                // and read_uncommitted is used, we can turn off database lock
-                // to use transaction.
-                mCacheDatabase.setLockingEnabled(false);
-
-                // use InsertHelper for faster insertion
-                mCacheInserter = new DatabaseUtils.InsertHelper(mCacheDatabase,
-                        "cache");
-                mCacheUrlColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_URL_COL);
-                mCacheFilePathColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_FILE_PATH_COL);
-                mCacheLastModifyColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_LAST_MODIFY_COL);
-                mCacheETagColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_ETAG_COL);
-                mCacheExpiresColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_EXPIRES_COL);
-                mCacheExpiresStringColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_EXPIRES_STRING_COL);
-                mCacheMimeTypeColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_MIMETYPE_COL);
-                mCacheEncodingColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_ENCODING_COL);
-                mCacheHttpStatusColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_HTTP_STATUS_COL);
-                mCacheLocationColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_LOCATION_COL);
-                mCacheContentLengthColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_CONTENTLENGTH_COL);
-                mCacheContentDispositionColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_CONTENTDISPOSITION_COL);
-                mCacheCrossDomainColIndex = mCacheInserter
-                        .getColumnIndex(CACHE_CROSSDOMAIN_COL);
+        try {
+            mDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0, null);
+        } catch (SQLiteException e) {
+            // try again by deleting the old db and create a new one
+            if (context.deleteDatabase(DATABASE_FILE)) {
+                mDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0,
+                        null);
             }
         }
 
-        return mInstance;
+        // mDatabase should not be null,
+        // the only case is RequestAPI test has problem to create db
+        if (mDatabase == null) {
+            mInitialized = true;
+            notify();
+            return;
+        }
+
+        if (mDatabase.getVersion() != DATABASE_VERSION) {
+            mDatabase.beginTransaction();
+            try {
+                upgradeDatabase();
+                mDatabase.setTransactionSuccessful();
+            } finally {
+                mDatabase.endTransaction();
+            }
+        }
+
+        // use per table Mutex lock, turn off database lock, this
+        // improves performance as database's ReentrantLock is
+        // expansive
+        mDatabase.setLockingEnabled(false);
+
+        try {
+            mCacheDatabase = context.openOrCreateDatabase(
+                    CACHE_DATABASE_FILE, 0, null);
+        } catch (SQLiteException e) {
+            // try again by deleting the old db and create a new one
+            if (context.deleteDatabase(CACHE_DATABASE_FILE)) {
+                mCacheDatabase = context.openOrCreateDatabase(
+                        CACHE_DATABASE_FILE, 0, null);
+            }
+        }
+
+        // mCacheDatabase should not be null,
+        // the only case is RequestAPI test has problem to create db
+        if (mCacheDatabase == null) {
+            mInitialized = true;
+            notify();
+            return;
+        }
+
+        if (mCacheDatabase.getVersion() != CACHE_DATABASE_VERSION) {
+            mCacheDatabase.beginTransaction();
+            try {
+                upgradeCacheDatabase();
+                bootstrapCacheDatabase();
+                mCacheDatabase.setTransactionSuccessful();
+            } finally {
+                mCacheDatabase.endTransaction();
+            }
+            // Erase the files from the file system in the
+            // case that the database was updated and the
+            // there were existing cache content
+            CacheManager.removeAllCacheFiles();
+        }
+
+        // use read_uncommitted to speed up READ
+        mCacheDatabase.execSQL("PRAGMA read_uncommitted = true;");
+        // as only READ can be called in the
+        // non-WebViewWorkerThread, and read_uncommitted is used,
+        // we can turn off database lock to use transaction.
+        mCacheDatabase.setLockingEnabled(false);
+
+        // use InsertHelper for faster insertion
+        mCacheInserter =
+                new DatabaseUtils.InsertHelper(mCacheDatabase,
+                        "cache");
+        mCacheUrlColIndex = mCacheInserter
+                            .getColumnIndex(CACHE_URL_COL);
+        mCacheFilePathColIndex = mCacheInserter
+                .getColumnIndex(CACHE_FILE_PATH_COL);
+        mCacheLastModifyColIndex = mCacheInserter
+                .getColumnIndex(CACHE_LAST_MODIFY_COL);
+        mCacheETagColIndex = mCacheInserter
+                .getColumnIndex(CACHE_ETAG_COL);
+        mCacheExpiresColIndex = mCacheInserter
+                .getColumnIndex(CACHE_EXPIRES_COL);
+        mCacheExpiresStringColIndex = mCacheInserter
+                .getColumnIndex(CACHE_EXPIRES_STRING_COL);
+        mCacheMimeTypeColIndex = mCacheInserter
+                .getColumnIndex(CACHE_MIMETYPE_COL);
+        mCacheEncodingColIndex = mCacheInserter
+                .getColumnIndex(CACHE_ENCODING_COL);
+        mCacheHttpStatusColIndex = mCacheInserter
+                .getColumnIndex(CACHE_HTTP_STATUS_COL);
+        mCacheLocationColIndex = mCacheInserter
+                .getColumnIndex(CACHE_LOCATION_COL);
+        mCacheContentLengthColIndex = mCacheInserter
+                .getColumnIndex(CACHE_CONTENTLENGTH_COL);
+        mCacheContentDispositionColIndex = mCacheInserter
+                .getColumnIndex(CACHE_CONTENTDISPOSITION_COL);
+        mCacheCrossDomainColIndex = mCacheInserter
+                .getColumnIndex(CACHE_CROSSDOMAIN_COL);
+
+        // Thread done, notify.
+        mInitialized = true;
+        notify();
     }
 
     private static void upgradeDatabase() {
@@ -391,8 +422,25 @@
         }
     }
 
+    // Wait for the background initialization thread to complete and check the
+    // database creation status.
+    private boolean checkInitialized() {
+        synchronized (this) {
+            while (!mInitialized) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                    Log.e(LOGTAG, "Caught exception while checking " +
+                                  "initialization");
+                    Log.e(LOGTAG, Log.getStackTraceString(e));
+                }
+            }
+        }
+        return mDatabase != null;
+    }
+
     private boolean hasEntries(int tableId) {
-        if (mDatabase == null) {
+        if (!checkInitialized()) {
             return false;
         }
 
@@ -422,7 +470,7 @@
      */
     ArrayList<Cookie> getCookiesForDomain(String domain) {
         ArrayList<Cookie> list = new ArrayList<Cookie>();
-        if (domain == null || mDatabase == null) {
+        if (domain == null || !checkInitialized()) {
             return list;
         }
 
@@ -481,7 +529,7 @@
      *            deleted.
      */
     void deleteCookies(String domain, String path, String name) {
-        if (domain == null || mDatabase == null) {
+        if (domain == null || !checkInitialized()) {
             return;
         }
 
@@ -501,7 +549,7 @@
      */
     void addCookie(Cookie cookie) {
         if (cookie.domain == null || cookie.path == null || cookie.name == null
-                || mDatabase == null) {
+                || !checkInitialized()) {
             return;
         }
 
@@ -534,7 +582,7 @@
      * Clear cookie database
      */
     void clearCookies() {
-        if (mDatabase == null) {
+        if (!checkInitialized()) {
             return;
         }
 
@@ -547,7 +595,7 @@
      * Clear session cookies, which means cookie doesn't have EXPIRES.
      */
     void clearSessionCookies() {
-        if (mDatabase == null) {
+        if (!checkInitialized()) {
             return;
         }
 
@@ -564,7 +612,7 @@
      * @param now Time for now
      */
     void clearExpiredCookies(long now) {
-        if (mDatabase == null) {
+        if (!checkInitialized()) {
             return;
         }
 
@@ -620,7 +668,7 @@
      * @return CacheResult The CacheManager.CacheResult
      */
     CacheResult getCache(String url) {
-        if (url == null || mCacheDatabase == null) {
+        if (url == null || !checkInitialized()) {
             return null;
         }
 
@@ -660,7 +708,7 @@
      * @param url The url
      */
     void removeCache(String url) {
-        if (url == null || mCacheDatabase == null) {
+        if (url == null || !checkInitialized()) {
             return;
         }
 
@@ -674,7 +722,7 @@
      * @param c The CacheManager.CacheResult
      */
     void addCache(String url, CacheResult c) {
-        if (url == null || mCacheDatabase == null) {
+        if (url == null || !checkInitialized()) {
             return;
         }
 
@@ -700,7 +748,7 @@
      * Clear cache database
      */
     void clearCache() {
-        if (mCacheDatabase == null) {
+        if (!checkInitialized()) {
             return;
         }
 
@@ -708,7 +756,7 @@
     }
 
     boolean hasCache() {
-        if (mCacheDatabase == null) {
+        if (!checkInitialized()) {
             return false;
         }
 
@@ -834,7 +882,7 @@
      */
     void setUsernamePassword(String schemePlusHost, String username,
                 String password) {
-        if (schemePlusHost == null || mDatabase == null) {
+        if (schemePlusHost == null || !checkInitialized()) {
             return;
         }
 
@@ -856,7 +904,7 @@
      *         String[1] is password. Return null if it can't find anything.
      */
     String[] getUsernamePassword(String schemePlusHost) {
-        if (schemePlusHost == null || mDatabase == null) {
+        if (schemePlusHost == null || !checkInitialized()) {
             return null;
         }
 
@@ -902,7 +950,7 @@
      * Clear password database
      */
     public void clearUsernamePassword() {
-        if (mDatabase == null) {
+        if (!checkInitialized()) {
             return;
         }
 
@@ -927,7 +975,7 @@
      */
     void setHttpAuthUsernamePassword(String host, String realm, String username,
             String password) {
-        if (host == null || realm == null || mDatabase == null) {
+        if (host == null || realm == null || !checkInitialized()) {
             return;
         }
 
@@ -952,7 +1000,7 @@
      *         String[1] is password. Return null if it can't find anything.
      */
     String[] getHttpAuthUsernamePassword(String host, String realm) {
-        if (host == null || realm == null || mDatabase == null){
+        if (host == null || realm == null || !checkInitialized()){
             return null;
         }
 
@@ -999,7 +1047,7 @@
      * Clear HTTP authentication password database
      */
     public void clearHttpAuthUsernamePassword() {
-        if (mDatabase == null) {
+        if (!checkInitialized()) {
             return;
         }
 
@@ -1020,7 +1068,7 @@
      * @param formdata The form data in HashMap
      */
     void setFormData(String url, HashMap<String, String> formdata) {
-        if (url == null || formdata == null || mDatabase == null) {
+        if (url == null || formdata == null || !checkInitialized()) {
             return;
         }
 
@@ -1069,7 +1117,7 @@
      */
     ArrayList<String> getFormData(String url, String name) {
         ArrayList<String> values = new ArrayList<String>();
-        if (url == null || name == null || mDatabase == null) {
+        if (url == null || name == null || !checkInitialized()) {
             return values;
         }
 
@@ -1129,7 +1177,7 @@
      * Clear form database
      */
     public void clearFormData() {
-        if (mDatabase == null) {
+        if (!checkInitialized()) {
             return;
         }
 
diff --git a/core/java/android/webkit/ZoomControlBase.java b/core/java/android/webkit/ZoomControlBase.java
new file mode 100644
index 0000000..be9e8f3
--- /dev/null
+++ b/core/java/android/webkit/ZoomControlBase.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.webkit;
+
+interface ZoomControlBase {
+
+    /**
+     * Causes the on-screen zoom control to be made visible
+     */
+    public void show();
+
+    /**
+     * Causes the on-screen zoom control to disappear
+     */
+    public void hide();
+
+    /**
+     * Enables the control to update its state if necessary in response to a
+     * change in the pages zoom level. For example, if the max zoom level is
+     * reached then the control can disable the button for zooming in.
+     */
+    public void update();
+
+    /**
+     * Checks to see if the control is currently visible to the user.
+     */
+    public boolean isVisible();
+}
diff --git a/core/java/android/webkit/ZoomControlEmbedded.java b/core/java/android/webkit/ZoomControlEmbedded.java
new file mode 100644
index 0000000..c29e72b
--- /dev/null
+++ b/core/java/android/webkit/ZoomControlEmbedded.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.webkit;
+
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.Toast;
+import android.widget.ZoomButtonsController;
+
+class ZoomControlEmbedded implements ZoomControlBase {
+
+    private final ZoomManager mZoomManager;
+    private final WebView mWebView;
+
+    // The controller is lazily initialized in getControls() for performance.
+    private ZoomButtonsController mZoomButtonsController;
+
+    public ZoomControlEmbedded(ZoomManager zoomManager, WebView webView) {
+        mZoomManager = zoomManager;
+        mWebView = webView;
+    }
+
+    public void show() {
+        if (!getControls().isVisible() && !mZoomManager.isZoomScaleFixed()) {
+
+            mZoomButtonsController.setVisible(true);
+
+            WebSettings settings = mWebView.getSettings();
+            int count = settings.getDoubleTapToastCount();
+            if (mZoomManager.isInZoomOverview() && count > 0) {
+                settings.setDoubleTapToastCount(--count);
+                Toast.makeText(mWebView.getContext(),
+                        com.android.internal.R.string.double_tap_toast,
+                        Toast.LENGTH_LONG).show();
+            }
+        }
+    }
+
+    public void hide() {
+        if (mZoomButtonsController != null) {
+            mZoomButtonsController.setVisible(false);
+        }
+    }
+
+    public boolean isVisible() {
+        return mZoomButtonsController != null && mZoomButtonsController.isVisible();
+    }
+
+    public void update() {
+        if (mZoomButtonsController == null) {
+            return;
+        }
+
+        boolean canZoomIn = mZoomManager.canZoomIn();
+        boolean canZoomOut = mZoomManager.canZoomOut() && !mZoomManager.isInZoomOverview();
+        if (!canZoomIn && !canZoomOut) {
+            // Hide the zoom in and out buttons if the page cannot zoom
+            mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
+        } else {
+            // Set each one individually, as a page may be able to zoom in or out
+            mZoomButtonsController.setZoomInEnabled(canZoomIn);
+            mZoomButtonsController.setZoomOutEnabled(canZoomOut);
+        }
+    }
+
+    private ZoomButtonsController getControls() {
+        if (mZoomButtonsController == null) {
+            mZoomButtonsController = new ZoomButtonsController(mWebView);
+            mZoomButtonsController.setOnZoomListener(new ZoomListener());
+            // ZoomButtonsController positions the buttons at the bottom, but in
+            // the middle. Change their layout parameters so they appear on the
+            // right.
+            View controls = mZoomButtonsController.getZoomControls();
+            ViewGroup.LayoutParams params = controls.getLayoutParams();
+            if (params instanceof FrameLayout.LayoutParams) {
+                ((FrameLayout.LayoutParams) params).gravity = Gravity.RIGHT;
+            }
+        }
+        return mZoomButtonsController;
+    }
+
+    private class ZoomListener implements ZoomButtonsController.OnZoomListener {
+
+        public void onVisibilityChanged(boolean visible) {
+            if (visible) {
+                mWebView.switchOutDrawHistory();
+                // Bring back the hidden zoom controls.
+                mZoomButtonsController.getZoomControls().setVisibility(View.VISIBLE);
+                update();
+            }
+        }
+
+        public void onZoom(boolean zoomIn) {
+            if (zoomIn) {
+                mWebView.zoomIn();
+            } else {
+                mWebView.zoomOut();
+            }
+            update();
+        }
+    }
+}
diff --git a/core/java/android/webkit/ZoomControlExternal.java b/core/java/android/webkit/ZoomControlExternal.java
new file mode 100644
index 0000000..d75313e
--- /dev/null
+++ b/core/java/android/webkit/ZoomControlExternal.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.webkit;
+
+import android.content.Context;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.View.OnClickListener;
+import android.view.animation.AlphaAnimation;
+import android.widget.FrameLayout;
+
+@Deprecated
+class ZoomControlExternal implements ZoomControlBase {
+
+    // The time that the external controls are visible before fading away
+    private static final long ZOOM_CONTROLS_TIMEOUT =
+            ViewConfiguration.getZoomControlsTimeout();
+    // The view containing the external zoom controls
+    private ExtendedZoomControls mZoomControls;
+    private Runnable mZoomControlRunnable;
+    private final Handler mPrivateHandler = new Handler();
+
+    private final WebView mWebView;
+
+    public ZoomControlExternal(WebView webView) {
+        mWebView = webView;
+    }
+
+    public void show() {
+        if(mZoomControlRunnable != null) {
+            mPrivateHandler.removeCallbacks(mZoomControlRunnable);
+        }
+        getControls().show(true);
+        mPrivateHandler.postDelayed(mZoomControlRunnable, ZOOM_CONTROLS_TIMEOUT);
+    }
+
+    public void hide() {
+        if (mZoomControlRunnable != null) {
+            mPrivateHandler.removeCallbacks(mZoomControlRunnable);
+        }
+        if (mZoomControls != null) {
+            mZoomControls.hide();
+        }
+    }
+
+    public boolean isVisible() {
+        return mZoomControls != null && mZoomControls.isShown();
+    }
+
+    public void update() { }
+
+    public ExtendedZoomControls getControls() {
+        if (mZoomControls == null) {
+            mZoomControls = createZoomControls();
+
+            /*
+             * need to be set to VISIBLE first so that getMeasuredHeight() in
+             * {@link #onSizeChanged()} can return the measured value for proper
+             * layout.
+             */
+            mZoomControls.setVisibility(View.VISIBLE);
+            mZoomControlRunnable = new Runnable() {
+                public void run() {
+                    /* Don't dismiss the controls if the user has
+                     * focus on them. Wait and check again later.
+                     */
+                    if (!mZoomControls.hasFocus()) {
+                        mZoomControls.hide();
+                    } else {
+                        mPrivateHandler.removeCallbacks(mZoomControlRunnable);
+                        mPrivateHandler.postDelayed(mZoomControlRunnable,
+                                ZOOM_CONTROLS_TIMEOUT);
+                    }
+                }
+            };
+        }
+        return mZoomControls;
+    }
+
+    private ExtendedZoomControls createZoomControls() {
+        ExtendedZoomControls zoomControls = new ExtendedZoomControls(mWebView.getContext());
+        zoomControls.setOnZoomInClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                // reset time out
+                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
+                mPrivateHandler.postDelayed(mZoomControlRunnable, ZOOM_CONTROLS_TIMEOUT);
+                mWebView.zoomIn();
+            }
+        });
+        zoomControls.setOnZoomOutClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                // reset time out
+                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
+                mPrivateHandler.postDelayed(mZoomControlRunnable, ZOOM_CONTROLS_TIMEOUT);
+                mWebView.zoomOut();
+            }
+        });
+        return zoomControls;
+    }
+
+    private static class ExtendedZoomControls extends FrameLayout {
+
+        private android.widget.ZoomControls mPlusMinusZoomControls;
+
+        public ExtendedZoomControls(Context context) {
+            super(context, null);
+            LayoutInflater inflater = (LayoutInflater)
+                    context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true);
+            mPlusMinusZoomControls = (android.widget.ZoomControls) findViewById(
+                    com.android.internal.R.id.zoomControls);
+            findViewById(com.android.internal.R.id.zoomMagnify).setVisibility(
+                    View.GONE);
+        }
+
+        public void show(boolean showZoom) {
+            mPlusMinusZoomControls.setVisibility(showZoom ? View.VISIBLE : View.GONE);
+            fade(View.VISIBLE, 0.0f, 1.0f);
+        }
+
+        public void hide() {
+            fade(View.GONE, 1.0f, 0.0f);
+        }
+
+        private void fade(int visibility, float startAlpha, float endAlpha) {
+            AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
+            anim.setDuration(500);
+            startAnimation(anim);
+            setVisibility(visibility);
+        }
+
+        public boolean hasFocus() {
+            return mPlusMinusZoomControls.hasFocus();
+        }
+
+        public void setOnZoomInClickListener(OnClickListener listener) {
+            mPlusMinusZoomControls.setOnZoomInClickListener(listener);
+        }
+
+        public void setOnZoomOutClickListener(OnClickListener listener) {
+            mPlusMinusZoomControls.setOnZoomOutClickListener(listener);
+        }
+    }
+}
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
new file mode 100644
index 0000000..3f6b10a
--- /dev/null
+++ b/core/java/android/webkit/ZoomManager.java
@@ -0,0 +1,969 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.ScaleGestureDetector;
+import android.view.View;
+
+/**
+ * The ZoomManager is responsible for maintaining the WebView's current zoom
+ * level state.  It is also responsible for managing the on-screen zoom controls
+ * as well as any animation of the WebView due to zooming.
+ *
+ * Currently, there are two methods for animating the zoom of a WebView.
+ *
+ * (1) The first method is triggered by startZoomAnimation(...) and is a fixed
+ * length animation where the final zoom scale is known at startup.  This type of
+ * animation notifies webkit of the final scale BEFORE it animates. The animation
+ * is then done by scaling the CANVAS incrementally based on a stepping function.
+ *
+ * (2) The second method is triggered by a multi-touch pinch and the new scale
+ * is determined dynamically based on the user's gesture. This type of animation
+ * only notifies webkit of new scale AFTER the gesture is complete. The animation
+ * effect is achieved by scaling the VIEWS (both WebView and ViewManager.ChildView)
+ * to the new scale in response to events related to the user's gesture.
+ */
+class ZoomManager {
+
+    static final String LOGTAG = "webviewZoom";
+
+    private final WebView mWebView;
+    private final CallbackProxy mCallbackProxy;
+
+    // Widgets responsible for the on-screen zoom functions of the WebView.
+    private ZoomControlEmbedded mEmbeddedZoomControl;
+    private ZoomControlExternal mExternalZoomControl;
+
+    /*
+     * For large screen devices, the defaultScale usually set to 1.0 and
+     * equal to the overview scale, to differentiate the zoom level for double tapping,
+     * a default reading level scale is used.
+     */
+    private static final float DEFAULT_READING_LEVEL_SCALE = 1.5f;
+
+    /*
+     * The scale factors that determine the upper and lower bounds for the
+     * default zoom scale.
+     */
+    protected static final float DEFAULT_MAX_ZOOM_SCALE_FACTOR = 4.00f;
+    protected static final float DEFAULT_MIN_ZOOM_SCALE_FACTOR = 0.25f;
+
+    // The default scale limits, which are dependent on the display density.
+    private float mDefaultMaxZoomScale;
+    private float mDefaultMinZoomScale;
+
+    // The actual scale limits, which can be set through a webpage's viewport
+    // meta-tag.
+    private float mMaxZoomScale;
+    private float mMinZoomScale;
+
+    // Locks the minimum ZoomScale to the value currently set in mMinZoomScale.
+    private boolean mMinZoomScaleFixed = true;
+
+    /*
+     * When loading a new page the WebView does not initially know the final
+     * width of the page. Therefore, when a new page is loaded in overview mode
+     * the overview scale is initialized to a default value. This flag is then
+     * set and used to notify the ZoomManager to take the width of the next
+     * picture from webkit and use that width to enter into zoom overview mode.
+     */
+    private boolean mInitialZoomOverview = false;
+
+    /*
+     * When in the zoom overview mode, the page's width is fully fit to the
+     * current window. Additionally while the page is in this state it is
+     * active, in other words, you can click to follow the links. We cache a
+     * boolean to enable us to quickly check whether or not we are in overview
+     * mode, but this value should only be modified by changes to the zoom
+     * scale.
+     */
+    private boolean mInZoomOverview = false;
+    private int mZoomOverviewWidth;
+    private float mInvZoomOverviewWidth;
+
+    /*
+     * These variables track the center point of the zoom and they are used to
+     * determine the point around which we should zoom. They are stored in view
+     * coordinates.
+     */
+    private float mZoomCenterX;
+    private float mZoomCenterY;
+
+    /*
+     * These values represent the point around which the screen should be
+     * centered after zooming. In other words it is used to determine the center
+     * point of the visible document after the page has finished zooming. This
+     * is important because the zoom may have potentially reflowed the text and
+     * we need to ensure the proper portion of the document remains on the
+     * screen.
+     */
+    private int mAnchorX;
+    private int mAnchorY;
+
+    // The scale factor that is used to determine the column width for text
+    private float mTextWrapScale;
+
+    /*
+     * The default zoom scale is the scale factor used when the user triggers a
+     * zoom in by double tapping on the WebView. The value is initially set
+     * based on the display density, but can be changed at any time via the
+     * WebSettings.
+     */
+    private float mDefaultScale;
+    private float mInvDefaultScale;
+
+    // the current computed zoom scale and its inverse.
+    private float mActualScale;
+    private float mInvActualScale;
+    
+    /*
+     * The initial scale for the WebView. 0 means default. If initial scale is
+     * greater than 0 the WebView starts with this value as its initial scale. The
+     * value is converted from an integer percentage so it is guarenteed to have
+     * no more than 2 significant digits after the decimal.  This restriction
+     * allows us to convert the scale back to the original percentage by simply
+     * multiplying the value by 100.
+     */
+    private float mInitialScale;
+
+    private static float MINIMUM_SCALE_INCREMENT = 0.01f;
+
+    /*
+     * The following member variables are only to be used for animating zoom. If
+     * mZoomScale is non-zero then we are in the middle of a zoom animation. The
+     * other variables are used as a cache (e.g. inverse) or as a way to store
+     * the state of the view prior to animating (e.g. initial scroll coords).
+     */
+    private float mZoomScale;
+    private float mInvInitialZoomScale;
+    private float mInvFinalZoomScale;
+    private int mInitialScrollX;
+    private int mInitialScrollY;
+    private long mZoomStart;
+
+    private static final int ZOOM_ANIMATION_LENGTH = 500;
+
+    // whether support multi-touch
+    private boolean mSupportMultiTouch;
+    
+    /**
+     * True if we have a touch panel capable of detecting smooth pan/scale at the same time
+     */
+    private boolean mAllowPanAndScale;
+
+    // use the framework's ScaleGestureDetector to handle multi-touch
+    private ScaleGestureDetector mScaleDetector;
+    private boolean mPinchToZoomAnimating = false;
+
+    public ZoomManager(WebView webView, CallbackProxy callbackProxy) {
+        mWebView = webView;
+        mCallbackProxy = callbackProxy;
+
+        /*
+         * Ideally mZoomOverviewWidth should be mContentWidth. But sites like
+         * ESPN and Engadget always have wider mContentWidth no matter what the
+         * viewport size is.
+         */
+        setZoomOverviewWidth(WebView.DEFAULT_VIEWPORT_WIDTH);
+    }
+
+    /**
+     * Initialize both the default and actual zoom scale to the given density.
+     *
+     * @param density The logical density of the display. This is a scaling factor
+     * for the Density Independent Pixel unit, where one DIP is one pixel on an
+     * approximately 160 dpi screen (see android.util.DisplayMetrics.density).
+     */
+    public void init(float density) {
+        assert density > 0;
+
+        setDefaultZoomScale(density);
+        mActualScale = density;
+        mInvActualScale = 1 / density;
+        mTextWrapScale = density;
+    }
+
+    /**
+     * Update the default zoom scale using the given density. It will also reset
+     * the current min and max zoom scales to the default boundaries as well as
+     * ensure that the actual scale falls within those boundaries.
+     *
+     * @param density The logical density of the display. This is a scaling factor
+     * for the Density Independent Pixel unit, where one DIP is one pixel on an
+     * approximately 160 dpi screen (see android.util.DisplayMetrics.density).
+     */
+    public void updateDefaultZoomDensity(float density) {
+        assert density > 0;
+
+        if (Math.abs(density - mDefaultScale) > MINIMUM_SCALE_INCREMENT) {
+            // set the new default density
+            setDefaultZoomScale(density);
+            // adjust the scale if it falls outside the new zoom bounds
+            setZoomScale(mActualScale, true);
+        }
+    }
+
+    private void setDefaultZoomScale(float defaultScale) {
+        mDefaultScale = defaultScale;
+        mInvDefaultScale = 1 / defaultScale;
+        mDefaultMaxZoomScale = defaultScale * DEFAULT_MAX_ZOOM_SCALE_FACTOR;
+        mDefaultMinZoomScale = defaultScale * DEFAULT_MIN_ZOOM_SCALE_FACTOR;
+        mMaxZoomScale = mDefaultMaxZoomScale;
+        mMinZoomScale = mDefaultMinZoomScale;
+    }
+
+    public final float getScale() {
+        return mActualScale;
+    }
+
+    public final float getInvScale() {
+        return mInvActualScale;
+    }
+
+    public final float getTextWrapScale() {
+        return mTextWrapScale;
+    }
+
+    public final float getMaxZoomScale() {
+        return mMaxZoomScale;
+    }
+
+    public final float getMinZoomScale() {
+        return mMinZoomScale;
+    }
+
+    public final float getDefaultScale() {
+        return mDefaultScale;
+    }
+
+    public final float getReadingLevelScale() {
+        // The reading scale is at least 0.5f apart from the overview scale.
+        final float MIN_SCALE_DIFF = 0.5f;
+        final float zoomOverviewScale = getZoomOverviewScale();
+        if (zoomOverviewScale > DEFAULT_READING_LEVEL_SCALE) {
+            return Math.min(DEFAULT_READING_LEVEL_SCALE,
+                zoomOverviewScale - MIN_SCALE_DIFF);
+        }
+        return Math.max(zoomOverviewScale + MIN_SCALE_DIFF,
+            DEFAULT_READING_LEVEL_SCALE);
+    }
+
+    public final float getInvDefaultScale() {
+        return mInvDefaultScale;
+    }
+
+    public final float getDefaultMaxZoomScale() {
+        return mDefaultMaxZoomScale;
+    }
+
+    public final float getDefaultMinZoomScale() {
+        return mDefaultMinZoomScale;
+    }
+
+    public final int getDocumentAnchorX() {
+        return mAnchorX;
+    }
+
+    public final int getDocumentAnchorY() {
+        return mAnchorY;
+    }
+
+    public final void clearDocumentAnchor() {
+        mAnchorX = mAnchorY = 0;
+    }
+
+    public final void setZoomCenter(float x, float y) {
+        mZoomCenterX = x;
+        mZoomCenterY = y;
+    }
+
+    public final void setInitialScaleInPercent(int scaleInPercent) {
+        mInitialScale = scaleInPercent * 0.01f;
+    }
+
+    public final float computeScaleWithLimits(float scale) {
+        if (scale < mMinZoomScale) {
+            scale = mMinZoomScale;
+        } else if (scale > mMaxZoomScale) {
+            scale = mMaxZoomScale;
+        }
+        return scale;
+    }
+
+    public final boolean isZoomScaleFixed() {
+        return mMinZoomScale >= mMaxZoomScale;
+    }
+
+    public static final boolean exceedsMinScaleIncrement(float scaleA, float scaleB) {
+        return Math.abs(scaleA - scaleB) >= MINIMUM_SCALE_INCREMENT;
+    }
+
+    public boolean willScaleTriggerZoom(float scale) {
+        return exceedsMinScaleIncrement(scale, mActualScale);
+    }
+
+    public final boolean canZoomIn() {
+        return mMaxZoomScale - mActualScale > MINIMUM_SCALE_INCREMENT;
+    }
+
+    public final boolean canZoomOut() {
+        return mActualScale - mMinZoomScale > MINIMUM_SCALE_INCREMENT;
+    }
+
+    public boolean zoomIn() {
+        return zoom(1.25f);
+    }
+
+    public boolean zoomOut() {
+        return zoom(0.8f);
+    }
+
+    // returns TRUE if zoom out succeeds and FALSE if no zoom changes.
+    private boolean zoom(float zoomMultiplier) {
+        // TODO: alternatively we can disallow this during draw history mode
+        mWebView.switchOutDrawHistory();
+        // Center zooming to the center of the screen.
+        mZoomCenterX = mWebView.getViewWidth() * .5f;
+        mZoomCenterY = mWebView.getViewHeight() * .5f;
+        mAnchorX = mWebView.viewToContentX((int) mZoomCenterX + mWebView.getScrollX());
+        mAnchorY = mWebView.viewToContentY((int) mZoomCenterY + mWebView.getScrollY());
+        return startZoomAnimation(mActualScale * zoomMultiplier, 
+            !mWebView.getSettings().getUseFixedViewport());
+    }
+
+    /**
+     * Initiates an animated zoom of the WebView.
+     *
+     * @return true if the new scale triggered an animation and false otherwise.
+     */
+    public boolean startZoomAnimation(float scale, boolean reflowText) {
+        float oldScale = mActualScale;
+        mInitialScrollX = mWebView.getScrollX();
+        mInitialScrollY = mWebView.getScrollY();
+
+        // snap to reading level scale if it is close
+        if (!exceedsMinScaleIncrement(scale, getReadingLevelScale())) {
+            scale = getReadingLevelScale();
+        }
+
+        setZoomScale(scale, reflowText);
+
+        if (oldScale != mActualScale) {
+            // use mZoomPickerScale to see zoom preview first
+            mZoomStart = SystemClock.uptimeMillis();
+            mInvInitialZoomScale = 1.0f / oldScale;
+            mInvFinalZoomScale = 1.0f / mActualScale;
+            mZoomScale = mActualScale;
+            mWebView.onFixedLengthZoomAnimationStart();
+            mWebView.invalidate();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * This method is called by the WebView's drawing code when a fixed length zoom
+     * animation is occurring. Its purpose is to animate the zooming of the canvas
+     * to the desired scale which was specified in startZoomAnimation(...).
+     *
+     * A fixed length animation begins when startZoomAnimation(...) is called and
+     * continues until the ZOOM_ANIMATION_LENGTH time has elapsed. During that
+     * interval each time the WebView draws it calls this function which is 
+     * responsible for generating the animation.
+     *
+     * Additionally, the WebView can check to see if such an animation is currently
+     * in progress by calling isFixedLengthAnimationInProgress().
+     */
+    public void animateZoom(Canvas canvas) {
+        if (mZoomScale == 0) {
+            Log.w(LOGTAG, "A WebView is attempting to perform a fixed length "
+                    + "zoom animation when no zoom is in progress");
+            return;
+        }
+
+        float zoomScale;
+        int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
+        if (interval < ZOOM_ANIMATION_LENGTH) {
+            float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
+            zoomScale = 1.0f / (mInvInitialZoomScale
+                    + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
+            mWebView.invalidate();
+        } else {
+            zoomScale = mZoomScale;
+            // set mZoomScale to be 0 as we have finished animating
+            mZoomScale = 0;
+            mWebView.onFixedLengthZoomAnimationEnd();
+        }
+        // calculate the intermediate scroll position. Since we need to use
+        // zoomScale, we can't use the WebView's pinLocX/Y functions directly.
+        float scale = zoomScale * mInvInitialZoomScale;
+        int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX) - mZoomCenterX);
+        tx = -WebView.pinLoc(tx, mWebView.getViewWidth(), Math.round(mWebView.getContentWidth()
+                * zoomScale)) + mWebView.getScrollX();
+        int titleHeight = mWebView.getTitleHeight();
+        int ty = Math.round(scale
+                * (mInitialScrollY + mZoomCenterY - titleHeight)
+                - (mZoomCenterY - titleHeight));
+        ty = -(ty <= titleHeight ? Math.max(ty, 0) : WebView.pinLoc(ty
+                - titleHeight, mWebView.getViewHeight(), Math.round(mWebView.getContentHeight()
+                * zoomScale)) + titleHeight) + mWebView.getScrollY();
+
+        canvas.translate(tx, ty);
+        canvas.scale(zoomScale, zoomScale);
+    }
+
+    public boolean isZoomAnimating() {
+        return isFixedLengthAnimationInProgress() || mPinchToZoomAnimating;
+    }
+
+    public boolean isFixedLengthAnimationInProgress() {
+        return mZoomScale != 0;
+    }
+
+    public void refreshZoomScale(boolean reflowText) {
+        setZoomScale(mActualScale, reflowText, true);
+    }
+
+    public void setZoomScale(float scale, boolean reflowText) {
+        setZoomScale(scale, reflowText, false);
+    }
+
+    private void setZoomScale(float scale, boolean reflowText, boolean force) {
+        final boolean isScaleLessThanMinZoom = scale < mMinZoomScale;
+        scale = computeScaleWithLimits(scale);
+
+        // determine whether or not we are in the zoom overview mode
+        if (isScaleLessThanMinZoom && mMinZoomScale < mDefaultScale) {
+            mInZoomOverview = true;
+        } else {
+            mInZoomOverview = !exceedsMinScaleIncrement(scale, getZoomOverviewScale());
+        }
+
+        if (reflowText && !mWebView.getSettings().getUseFixedViewport()) {
+            mTextWrapScale = scale;
+        }
+
+        if (scale != mActualScale || force) {
+            float oldScale = mActualScale;
+            float oldInvScale = mInvActualScale;
+
+            if (scale != mActualScale && !mPinchToZoomAnimating) {
+                mCallbackProxy.onScaleChanged(mActualScale, scale);
+            }
+
+            mActualScale = scale;
+            mInvActualScale = 1 / scale;
+
+            if (!mWebView.drawHistory()) {
+
+                // If history Picture is drawn, don't update scroll. They will
+                // be updated when we get out of that mode.
+                // update our scroll so we don't appear to jump
+                // i.e. keep the center of the doc in the center of the view
+                int oldX = mWebView.getScrollX();
+                int oldY = mWebView.getScrollY();
+                float ratio = scale * oldInvScale;
+                float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
+                float sy = ratio * oldY + (ratio - 1)
+                        * (mZoomCenterY - mWebView.getTitleHeight());
+
+                // Scale all the child views
+                mWebView.mViewManager.scaleAll();
+
+                // as we don't have animation for scaling, don't do animation
+                // for scrolling, as it causes weird intermediate state
+                int scrollX = mWebView.pinLocX(Math.round(sx));
+                int scrollY = mWebView.pinLocY(Math.round(sy));
+                if(!mWebView.updateScrollCoordinates(scrollX, scrollY)) {
+                    // the scroll position is adjusted at the beginning of the
+                    // zoom animation. But we want to update the WebKit at the
+                    // end of the zoom animation. See comments in onScaleEnd().
+                    mWebView.sendOurVisibleRect();
+                }
+            }
+
+            // if the we need to reflow the text then force the VIEW_SIZE_CHANGED
+            // event to be sent to WebKit
+            mWebView.sendViewSizeZoom(reflowText);
+        }
+    }
+
+    /**
+     * The double tap gesture can result in different behaviors depending on the
+     * content that is tapped.
+     *
+     * (1) PLUGINS: If the taps occur on a plugin then we maximize the plugin on
+     * the screen. If the plugin is already maximized then zoom the user into
+     * overview mode.
+     *
+     * (2) HTML/OTHER: If the taps occur outside a plugin then the following
+     * heuristic is used.
+     *   A. If the current text wrap scale differs from newly calculated and the
+     *      layout algorithm specifies the use of NARROW_COLUMNS, then fit to
+     *      column by reflowing the text.
+     *   B. If the page is not in overview mode then change to overview mode.
+     *   C. If the page is in overmode then change to the default scale.
+     */
+    public void handleDoubleTap(float lastTouchX, float lastTouchY) {
+        WebSettings settings = mWebView.getSettings();
+        if (settings == null || settings.getUseWideViewPort() == false) {
+            return;
+        }
+
+        setZoomCenter(lastTouchX, lastTouchY);
+        mAnchorX = mWebView.viewToContentX((int) lastTouchX + mWebView.getScrollX());
+        mAnchorY = mWebView.viewToContentY((int) lastTouchY + mWebView.getScrollY());
+        settings.setDoubleTapToastCount(0);
+
+        // remove the zoom control after double tap
+        dismissZoomPicker();
+
+        /*
+         * If the double tap was on a plugin then either zoom to maximize the
+         * plugin on the screen or scale to overview mode.
+         */
+        ViewManager.ChildView plugin = mWebView.mViewManager.hitTest(mAnchorX, mAnchorY);
+        if (plugin != null) {
+            if (mWebView.isPluginFitOnScreen(plugin)) {
+                zoomToOverview();
+            } else {
+                mWebView.centerFitRect(plugin.x, plugin.y, plugin.width, plugin.height);
+            }
+            return;
+        }
+
+        final float newTextWrapScale;
+        if (settings.getUseFixedViewport()) {
+            newTextWrapScale = Math.max(mActualScale, getReadingLevelScale());
+        } else {
+            newTextWrapScale = mActualScale;
+        }
+        if (settings.isNarrowColumnLayout()
+                && exceedsMinScaleIncrement(mTextWrapScale, newTextWrapScale)) {
+            mTextWrapScale = newTextWrapScale;
+            refreshZoomScale(true);
+        } else if (!mInZoomOverview) {
+            zoomToOverview();
+        } else {
+            zoomToReadingLevel();
+        }
+    }
+
+    private void setZoomOverviewWidth(int width) {
+        if (width == 0) {
+            mZoomOverviewWidth = WebView.DEFAULT_VIEWPORT_WIDTH;
+        } else {
+            mZoomOverviewWidth = width;
+        }
+        mInvZoomOverviewWidth = 1.0f / width;
+    }
+
+    private float getZoomOverviewScale() {
+        return mWebView.getViewWidth() * mInvZoomOverviewWidth;
+    }
+
+    public boolean isInZoomOverview() {
+        return mInZoomOverview;
+    }
+
+    private void zoomToOverview() {
+        if (!willScaleTriggerZoom(getZoomOverviewScale())) return;
+
+        // Force the titlebar fully reveal in overview mode
+        int scrollY = mWebView.getScrollY();
+        if (scrollY < mWebView.getTitleHeight()) {
+            mWebView.updateScrollCoordinates(mWebView.getScrollX(), 0);
+        }
+        startZoomAnimation(getZoomOverviewScale(), 
+            !mWebView.getSettings().getUseFixedViewport());
+    }
+
+    private void zoomToReadingLevel() {
+        final float readingScale = getReadingLevelScale();
+        int left = mWebView.nativeGetBlockLeftEdge(mAnchorX, mAnchorY, mActualScale);
+        if (left != WebView.NO_LEFTEDGE) {
+            // add a 5pt padding to the left edge.
+            int viewLeft = mWebView.contentToViewX(left < 5 ? 0 : (left - 5))
+                    - mWebView.getScrollX();
+            // Re-calculate the zoom center so that the new scroll x will be
+            // on the left edge.
+            if (viewLeft > 0) {
+                mZoomCenterX = viewLeft * readingScale / (readingScale - mActualScale);
+            } else {
+                mWebView.scrollBy(viewLeft, 0);
+                mZoomCenterX = 0;
+            }
+        }
+        startZoomAnimation(readingScale,
+            !mWebView.getSettings().getUseFixedViewport());
+    }
+
+    public void updateMultiTouchSupport(Context context) {
+        // check the preconditions
+        assert mWebView.getSettings() != null;
+
+        final WebSettings settings = mWebView.getSettings();
+        final PackageManager pm = context.getPackageManager();
+        mSupportMultiTouch = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
+                && settings.supportZoom() && settings.getBuiltInZoomControls();
+        mAllowPanAndScale = pm.hasSystemFeature(
+                PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
+        if (mSupportMultiTouch && (mScaleDetector == null)) {
+            mScaleDetector = new ScaleGestureDetector(context, new ScaleDetectorListener());
+        } else if (!mSupportMultiTouch && (mScaleDetector != null)) {
+            mScaleDetector = null;
+        }
+    }
+
+    public boolean supportsMultiTouchZoom() {
+        return mSupportMultiTouch;
+    }
+
+    public boolean supportsPanDuringZoom() {
+        return mAllowPanAndScale;
+    }
+
+    /**
+     * Notifies the caller that the ZoomManager is requesting that scale related
+     * updates should not be sent to webkit. This can occur in cases where the
+     * ZoomManager is performing an animation and does not want webkit to update
+     * until the animation is complete.
+     *
+     * @return true if scale related updates should not be sent to webkit and
+     *         false otherwise.
+     */
+    public boolean isPreventingWebkitUpdates() {
+        // currently only animating a multi-touch zoom prevents updates, but
+        // others can add their own conditions to this method if necessary.
+        return mPinchToZoomAnimating;
+    }
+
+    public ScaleGestureDetector getMultiTouchGestureDetector() {
+        return mScaleDetector;
+    }
+
+    private class ScaleDetectorListener implements ScaleGestureDetector.OnScaleGestureListener {
+
+        public boolean onScaleBegin(ScaleGestureDetector detector) {
+            dismissZoomPicker();
+            mWebView.mViewManager.startZoom();
+            mWebView.onPinchToZoomAnimationStart();
+            return true;
+        }
+
+        public boolean onScale(ScaleGestureDetector detector) {
+            float scale = Math.round(detector.getScaleFactor() * mActualScale * 100) * 0.01f;
+            if (willScaleTriggerZoom(scale)) {
+                mPinchToZoomAnimating = true;
+                // limit the scale change per step
+                if (scale > mActualScale) {
+                    scale = Math.min(scale, mActualScale * 1.25f);
+                } else {
+                    scale = Math.max(scale, mActualScale * 0.8f);
+                }
+                setZoomCenter(detector.getFocusX(), detector.getFocusY());
+                setZoomScale(scale, false);
+                mWebView.invalidate();
+                return true;
+            }
+            return false;
+        }
+
+        public void onScaleEnd(ScaleGestureDetector detector) {
+            if (mPinchToZoomAnimating) {
+                mPinchToZoomAnimating = false;
+                mAnchorX = mWebView.viewToContentX((int) mZoomCenterX + mWebView.getScrollX());
+                mAnchorY = mWebView.viewToContentY((int) mZoomCenterY + mWebView.getScrollY());
+                // don't reflow when zoom in; when zoom out, do reflow if the
+                // new scale is almost minimum scale.
+                boolean reflowNow = !canZoomOut() || (mActualScale <= 0.8 * mTextWrapScale);
+                // force zoom after mPreviewZoomOnly is set to false so that the
+                // new view size will be passed to the WebKit
+                refreshZoomScale(reflowNow &&
+                    !mWebView.getSettings().getUseFixedViewport());
+                // call invalidate() to draw without zoom filter
+                mWebView.invalidate();
+            }
+
+            mWebView.mViewManager.endZoom();
+            mWebView.onPinchToZoomAnimationEnd(detector);
+        }
+    }
+
+    public void onSizeChanged(int w, int h, int ow, int oh) {
+        // reset zoom and anchor to the top left corner of the screen
+        // unless we are already zooming
+        if (!isFixedLengthAnimationInProgress()) {
+            int visibleTitleHeight = mWebView.getVisibleTitleHeight();
+            mZoomCenterX = 0;
+            mZoomCenterY = visibleTitleHeight;
+            mAnchorX = mWebView.viewToContentX(mWebView.getScrollX());
+            mAnchorY = mWebView.viewToContentY(visibleTitleHeight + mWebView.getScrollY());
+        }
+
+        // update mMinZoomScale if the minimum zoom scale is not fixed
+        if (!mMinZoomScaleFixed) {
+            // when change from narrow screen to wide screen, the new viewWidth
+            // can be wider than the old content width. We limit the minimum
+            // scale to 1.0f. The proper minimum scale will be calculated when
+            // the new picture shows up.
+            mMinZoomScale = Math.min(1.0f, (float) mWebView.getViewWidth()
+                    / (mWebView.drawHistory() ? mWebView.getHistoryPictureWidth()
+                            : mZoomOverviewWidth));
+            // limit the minZoomScale to the initialScale if it is set
+            if (mInitialScale > 0 && mInitialScale < mMinZoomScale) {
+                mMinZoomScale = mInitialScale;
+            }
+        }
+
+        dismissZoomPicker();
+
+        // onSizeChanged() is called during WebView layout. And any
+        // requestLayout() is blocked during layout. As refreshZoomScale() will
+        // cause its child View to reposition itself through ViewManager's
+        // scaleAll(), we need to post a Runnable to ensure requestLayout().
+        // Additionally, only update the text wrap scale if the width changed.
+        mWebView.post(new PostScale(w != ow &&
+            !mWebView.getSettings().getUseFixedViewport()));
+    }
+
+    private class PostScale implements Runnable {
+        final boolean mUpdateTextWrap;
+
+        public PostScale(boolean updateTextWrap) {
+            mUpdateTextWrap = updateTextWrap;
+        }
+
+        public void run() {
+            if (mWebView.getWebViewCore() != null) {
+                // we always force, in case our height changed, in which case we
+                // still want to send the notification over to webkit.
+                setZoomScale(Math.max(mActualScale, getZoomOverviewScale()),
+                    mUpdateTextWrap, true);
+                // update the zoom buttons as the scale can be changed
+                updateZoomPicker();
+            }
+        }
+    }
+
+    public void updateZoomRange(WebViewCore.ViewState viewState,
+            int viewWidth, int minPrefWidth) {
+        if (viewState.mMinScale == 0) {
+            if (viewState.mMobileSite) {
+                if (minPrefWidth > Math.max(0, viewWidth)) {
+                    mMinZoomScale = (float) viewWidth / minPrefWidth;
+                    mMinZoomScaleFixed = false;
+                } else {
+                    mMinZoomScale = viewState.mDefaultScale;
+                    mMinZoomScaleFixed = true;
+                }
+            } else {
+                mMinZoomScale = mDefaultMinZoomScale;
+                mMinZoomScaleFixed = false;
+            }
+        } else {
+            mMinZoomScale = viewState.mMinScale;
+            mMinZoomScaleFixed = true;
+        }
+        if (viewState.mMaxScale == 0) {
+            mMaxZoomScale = mDefaultMaxZoomScale;
+        } else {
+            mMaxZoomScale = viewState.mMaxScale;
+        }
+    }
+
+    /**
+     * Updates zoom values when Webkit produces a new picture. This method
+     * should only be called from the UI thread's message handler.
+     */
+    public void onNewPicture(WebViewCore.DrawData drawData) {
+        final int viewWidth = mWebView.getViewWidth();
+
+        if (mWebView.getSettings().getUseWideViewPort()) {
+            if (!mWebView.getSettings().getUseFixedViewport()) {
+                // limit mZoomOverviewWidth upper bound to
+                // sMaxViewportWidth so that if the page doesn't behave
+                // well, the WebView won't go insane. limit the lower
+                // bound to match the default scale for mobile sites.
+                setZoomOverviewWidth(Math.min(WebView.sMaxViewportWidth,
+                    Math.max((int) (viewWidth * mInvDefaultScale),
+                            Math.max(drawData.mMinPrefWidth, drawData.mViewSize.x))));
+            } else {
+                final int contentWidth = Math.max(drawData.mContentSize.x, drawData.mMinPrefWidth);
+                setZoomOverviewWidth(Math.min(WebView.sMaxViewportWidth, contentWidth));
+            }
+        }
+
+        final float zoomOverviewScale = getZoomOverviewScale();
+        if (!mMinZoomScaleFixed) {
+            mMinZoomScale = zoomOverviewScale;
+        }
+        // fit the content width to the current view. Ignore the rounding error case.
+        if (!mWebView.drawHistory() && (mInitialZoomOverview || (mInZoomOverview
+                && Math.abs((viewWidth * mInvActualScale) - mZoomOverviewWidth) > 1))) {
+            mInitialZoomOverview = false;
+            setZoomScale(zoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) &&
+                !mWebView.getSettings().getUseFixedViewport());
+        }
+    }
+
+    /**
+     * Updates zoom values after Webkit completes the initial page layout. It
+     * is called when visiting a page for the first time as well as when the
+     * user navigates back to a page (in which case we may need to restore the
+     * zoom levels to the state they were when you left the page). This method
+     * should only be called from the UI thread's message handler.
+     */
+    public void onFirstLayout(WebViewCore.DrawData drawData) {
+        // precondition check
+        assert drawData != null;
+        assert drawData.mViewState != null;
+        assert mWebView.getSettings() != null;
+
+        WebViewCore.ViewState viewState = drawData.mViewState;
+        final Point viewSize = drawData.mViewSize;
+        updateZoomRange(viewState, viewSize.x, drawData.mMinPrefWidth);
+        if (mWebView.getSettings().getUseWideViewPort() &&
+            mWebView.getSettings().getUseFixedViewport()) {
+            final int contentWidth = Math.max(drawData.mContentSize.x, drawData.mMinPrefWidth);
+            setZoomOverviewWidth(Math.min(WebView.sMaxViewportWidth, contentWidth));
+        }
+
+        if (!mWebView.drawHistory()) {
+            float scale;
+            final boolean reflowText;
+            WebSettings settings = mWebView.getSettings();
+
+            if (mInitialScale > 0) {
+                scale = mInitialScale;
+                reflowText = exceedsMinScaleIncrement(mTextWrapScale, scale);
+            } else if (viewState.mViewScale > 0) {
+                mTextWrapScale = viewState.mTextWrapScale;
+                scale = viewState.mViewScale;
+                reflowText = false;
+            } else {
+                scale = getZoomOverviewScale();
+                if (settings.getUseWideViewPort()
+                    && settings.getLoadWithOverviewMode()) {
+                    mInitialZoomOverview = true;
+                } else {
+                    scale = Math.max(viewState.mTextWrapScale, scale);
+                    mInitialZoomOverview = !exceedsMinScaleIncrement(scale, getZoomOverviewScale());
+                }
+                if (settings.isNarrowColumnLayout() && settings.getUseFixedViewport()) {
+                    // When first layout, reflow using the reading level scale to avoid
+                    // reflow when double tapped.
+                    mTextWrapScale = getReadingLevelScale();
+                }
+                reflowText = exceedsMinScaleIncrement(mTextWrapScale, scale);
+            }
+            setZoomScale(scale, reflowText);
+
+            // update the zoom buttons as the scale can be changed
+            updateZoomPicker();
+        }
+    }
+
+    public void saveZoomState(Bundle b) {
+        b.putFloat("scale", mActualScale);
+        b.putFloat("textwrapScale", mTextWrapScale);
+        b.putBoolean("overview", mInZoomOverview);
+    }
+
+    public void restoreZoomState(Bundle b) {
+        // as getWidth() / getHeight() of the view are not available yet, set up
+        // mActualScale, so that when onSizeChanged() is called, the rest will
+        // be set correctly
+        mActualScale = b.getFloat("scale", 1.0f);
+        mInvActualScale = 1 / mActualScale;
+        mTextWrapScale = b.getFloat("textwrapScale", mActualScale);
+        mInZoomOverview = b.getBoolean("overview");
+    }
+
+    private ZoomControlBase getCurrentZoomControl() {
+        if (mWebView.getSettings() != null && mWebView.getSettings().supportZoom()) {
+            if (mWebView.getSettings().getBuiltInZoomControls()) {
+                if ((mEmbeddedZoomControl == null)
+                        && mWebView.getSettings().getDisplayZoomControls()) {
+                    mEmbeddedZoomControl = new ZoomControlEmbedded(this, mWebView);
+                }
+                return mEmbeddedZoomControl;
+            } else {
+                if (mExternalZoomControl == null) {
+                    mExternalZoomControl = new ZoomControlExternal(mWebView);
+                }
+                return mExternalZoomControl;
+            }
+        }
+        return null;
+    }
+
+    public void invokeZoomPicker() {
+        ZoomControlBase control = getCurrentZoomControl();
+        if (control != null) {
+            control.show();
+        }
+    }
+
+    public void dismissZoomPicker() {
+        ZoomControlBase control = getCurrentZoomControl();
+        if (control != null) {
+            control.hide();
+        }
+    }
+
+    public boolean isZoomPickerVisible() {
+        ZoomControlBase control = getCurrentZoomControl();
+        return (control != null) ? control.isVisible() : false;
+    }
+
+    public void updateZoomPicker() {
+        ZoomControlBase control = getCurrentZoomControl();
+        if (control != null) {
+            control.update();
+        }
+    }
+
+    /**
+     * The embedded zoom control intercepts touch events and automatically stays
+     * visible. The external control needs to constantly refresh its internal
+     * timer to stay visible.
+     */
+    public void keepZoomPickerVisible() {
+        ZoomControlBase control = getCurrentZoomControl();
+        if (control != null && control == mExternalZoomControl) {
+            control.show();
+        }
+    }
+
+    public View getExternalZoomPicker() {
+        ZoomControlBase control = getCurrentZoomControl();
+        if (control != null && control == mExternalZoomControl) {
+            return mExternalZoomControl.getControls();
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 9a38acc..d0ed23f 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -19,7 +19,7 @@
 import com.android.internal.R;
 
 import android.content.Context;
-import android.content.res.Resources;
+import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Rect;
@@ -34,11 +34,16 @@
 import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.ContextMenu.ContextMenuInfo;
+import android.util.LongSparseArray;
+import android.util.SparseBooleanArray;
+import android.util.StateSet;
+import android.view.ActionMode;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -46,7 +51,7 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
-import android.view.animation.AnimationUtils;
+import android.view.ContextMenu.ContextMenuInfo;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -70,10 +75,12 @@
  * @attr ref android.R.styleable#AbsListView_cacheColorHint
  * @attr ref android.R.styleable#AbsListView_fastScrollEnabled
  * @attr ref android.R.styleable#AbsListView_smoothScrollbar
+ * @attr ref android.R.styleable#AbsListView_choiceMode
  */
 public abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher,
         ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,
-        ViewTreeObserver.OnTouchModeChangeListener {
+        ViewTreeObserver.OnTouchModeChangeListener,
+        RemoteViewsAdapter.RemoteAdapterConnectionCallback {
 
     /**
      * Disables the transcript mode.
@@ -130,17 +137,6 @@
     static final int TOUCH_MODE_FLING = 4;
 
     /**
-     * Indicates the touch gesture is an overscroll - a scroll beyond the beginning or end.
-     */
-    static final int TOUCH_MODE_OVERSCROLL = 5;
-
-    /**
-     * Indicates the view is being flung outside of normal content bounds
-     * and will spring back.
-     */
-    static final int TOUCH_MODE_OVERFLING = 6;
-
-    /**
      * Regular layout - usually an unsolicited layout from the view system
      */
     static final int LAYOUT_NORMAL = 0;
@@ -178,6 +174,57 @@
     static final int LAYOUT_MOVE_SELECTION = 6;
 
     /**
+     * Normal list that does not indicate choices
+     */
+    public static final int CHOICE_MODE_NONE = 0;
+
+    /**
+     * The list allows up to one choice
+     */
+    public static final int CHOICE_MODE_SINGLE = 1;
+
+    /**
+     * The list allows multiple choices
+     */
+    public static final int CHOICE_MODE_MULTIPLE = 2;
+
+    /**
+     * The list allows multiple choices in a modal selection mode
+     */
+    public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
+
+    /**
+     * Controls if/how the user may choose/check items in the list
+     */
+    int mChoiceMode = CHOICE_MODE_NONE;
+
+    /**
+     * Controls CHOICE_MODE_MULTIPLE_MODAL. null when inactive.
+     */
+    ActionMode mChoiceActionMode;
+
+    /**
+     * Wrapper for the multiple choice mode callback; AbsListView needs to perform
+     * a few extra actions around what application code does.
+     */
+    MultiChoiceModeWrapper mMultiChoiceModeCallback;
+
+    /**
+     * Running count of how many items are currently checked
+     */
+    int mCheckedItemCount;
+
+    /**
+     * Running state of which positions are currently checked
+     */
+    SparseBooleanArray mCheckStates;
+
+    /**
+     * Running state of which IDs are currently checked
+     */
+    LongSparseArray<Boolean> mCheckedIdStates;
+
+    /**
      * Controls how the next layout will happen
      */
     int mLayoutMode = LAYOUT_NORMAL;
@@ -193,6 +240,11 @@
     ListAdapter mAdapter;
 
     /**
+     * The remote adapter containing the data to be displayed by this view to be set
+     */
+    private RemoteViewsAdapter mRemoteAdapter;
+
+    /**
      * Indicates whether the list selector should be drawn on top of the children or behind
      */
     boolean mDrawSelectorOnTop = false;
@@ -203,6 +255,17 @@
     Drawable mSelector;
 
     /**
+     * Set to true if we would like to have the selector showing itself.
+     * We still need to draw and position it even if this is false.
+     */
+    boolean mSelectorShowing;
+
+    /**
+     * The current position of the selector in the list.
+     */
+    int mSelectorPosition = INVALID_POSITION;
+
+    /**
      * Defines the selector's location and dimension at drawing time
      */
     Rect mSelectorRect = new Rect();
@@ -382,16 +445,6 @@
     private ContextMenuInfo mContextMenuInfo = null;
     
     /**
-     * Maximum distance to record overscroll
-     */
-    int mOverscrollMax;
-
-    /**
-     * Content height divided by this is the overscroll limit.
-     */
-    static final int OVERSCROLL_LIMIT_DIVISOR = 3;
-
-    /**
      * Used to request a layout when we changed touch mode
      */
     private static final int TOUCH_MODE_UNKNOWN = -1;
@@ -464,6 +517,7 @@
     private Runnable mClearScrollingCache;
     private int mMinimumVelocity;
     private int mMaximumVelocity;
+    private float mVelocityScale = 1.0f;
     
     final boolean[] mIsScrap = new boolean[1];
     
@@ -484,48 +538,6 @@
     private static final int INVALID_POINTER = -1;
 
     /**
-     * Maximum distance to overscroll by during edge effects
-     */
-    int mOverscrollDistance;
-
-    /**
-     * Maximum distance to overfling during edge effects
-     */
-    int mOverflingDistance;
-
-    // These two EdgeGlows are always set and used together.
-    // Checking one for null is as good as checking both.
-
-    /**
-     * Tracks the state of the top edge glow.
-     */
-    private EdgeGlow mEdgeGlowTop;
-
-    /**
-     * Tracks the state of the bottom edge glow.
-     */
-    private EdgeGlow mEdgeGlowBottom;
-
-    /**
-     * An estimate of how many pixels are between the top of the list and
-     * the top of the first position in the adapter, based on the last time
-     * we saw it. Used to hint where to draw edge glows.
-     */
-    private int mFirstPositionDistanceGuess;
-
-    /**
-     * An estimate of how many pixels are between the bottom of the list and
-     * the bottom of the last position in the adapter, based on the last time
-     * we saw it. Used to hint where to draw edge glows.
-     */
-    private int mLastPositionDistanceGuess;
-
-    /**
-     * Used for determining when to cancel out of overscroll.
-     */
-    private int mDirection = 0;
-
-    /**
      * Interface definition for a callback to be invoked when the list or grid
      * has been scrolled.
      */
@@ -574,6 +586,22 @@
                 int totalItemCount);
     }
 
+    /**
+     * The top-level view of a list item can implement this interface to allow
+     * itself to modify the bounds of the selection shown for that item.
+     */
+    public interface SelectionBoundsAdjuster {
+        /**
+         * Called to allow the list item to adjust the bounds shown for
+         * its selection.
+         *
+         * @param bounds On call, this contains the bounds the list has
+         * selected for the item (that is the bounds of the entire view).  The
+         * values can be modified as desired.
+         */
+        public void adjustListItemSelectionBounds(Rect bounds);
+    }
+
     public AbsListView(Context context) {
         super(context);
         initAbsListView();
@@ -624,6 +652,18 @@
 
         boolean smoothScrollbar = a.getBoolean(R.styleable.AbsListView_smoothScrollbar, true);
         setSmoothScrollbarEnabled(smoothScrollbar);
+        
+        final int adapterId = a.getResourceId(R.styleable.AbsListView_adapter, 0);
+        if (adapterId != 0) {
+            final Context c = context;
+            post(new Runnable() {
+                public void run() {
+                    setAdapter(Adapters.loadAdapter(c, adapterId));
+                }
+            });
+        }
+
+        setChoiceMode(a.getInt(R.styleable.AbsListView_choiceMode, CHOICE_MODE_NONE));
 
         a.recycle();
     }
@@ -640,39 +680,316 @@
         mTouchSlop = configuration.getScaledTouchSlop();
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-        mOverscrollDistance = configuration.getScaledOverscrollDistance();
-        mOverflingDistance = configuration.getScaledOverflingDistance();
-
         mDensityScale = getContext().getResources().getDisplayMetrics().density;
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public void setOverScrollMode(int mode) {
-        if (mode != OVER_SCROLL_NEVER) {
-            if (mEdgeGlowTop == null) {
-                final Resources res = getContext().getResources();
-                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
-                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
-                mEdgeGlowTop = new EdgeGlow(edge, glow);
-                mEdgeGlowBottom = new EdgeGlow(edge, glow);
+    public void setAdapter(ListAdapter adapter) {
+        if (adapter != null) {
+            if (mChoiceMode != CHOICE_MODE_NONE && mAdapter.hasStableIds() &&
+                    mCheckedIdStates == null) {
+                mCheckedIdStates = new LongSparseArray<Boolean>();
             }
-        } else {
-            mEdgeGlowTop = null;
-            mEdgeGlowBottom = null;
         }
-        super.setOverScrollMode(mode);
+
+        if (mCheckStates != null) {
+            mCheckStates.clear();
+        }
+
+        if (mCheckedIdStates != null) {
+            mCheckedIdStates.clear();
+        }
     }
 
     /**
-     * @return true if all list content currently fits within the view boundaries
+     * Returns the number of items currently selected. This will only be valid
+     * if the choice mode is not {@link #CHOICE_MODE_NONE} (default).
+     *
+     * <p>To determine the specific items that are currently selected, use one of
+     * the <code>getChecked*</code> methods.
+     *
+     * @return The number of items currently selected
+     *
+     * @see #getCheckedItemPosition()
+     * @see #getCheckedItemPositions()
+     * @see #getCheckedItemIds()
      */
-    private boolean contentFits() {
-        final int childCount = getChildCount();
-        if (childCount != mItemCount) {
-            return false;
+    public int getCheckedItemCount() {
+        return mCheckedItemCount;
+    }
+
+    /**
+     * Returns the checked state of the specified position. The result is only
+     * valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE}
+     * or {@link #CHOICE_MODE_MULTIPLE}.
+     *
+     * @param position The item whose checked state to return
+     * @return The item's checked state or <code>false</code> if choice mode
+     *         is invalid
+     *
+     * @see #setChoiceMode(int)
+     */
+    public boolean isItemChecked(int position) {
+        if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
+            return mCheckStates.get(position);
         }
 
-        return getChildAt(0).getTop() >= 0 && getChildAt(childCount - 1).getBottom() <= mBottom;
+        return false;
+    }
+
+    /**
+     * Returns the currently checked item. The result is only valid if the choice
+     * mode has been set to {@link #CHOICE_MODE_SINGLE}.
+     *
+     * @return The position of the currently checked item or
+     *         {@link #INVALID_POSITION} if nothing is selected
+     *
+     * @see #setChoiceMode(int)
+     */
+    public int getCheckedItemPosition() {
+        if (mChoiceMode == CHOICE_MODE_SINGLE && mCheckStates != null && mCheckStates.size() == 1) {
+            return mCheckStates.keyAt(0);
+        }
+
+        return INVALID_POSITION;
+    }
+
+    /**
+     * Returns the set of checked items in the list. The result is only valid if
+     * the choice mode has not been set to {@link #CHOICE_MODE_NONE}.
+     *
+     * @return  A SparseBooleanArray which will return true for each call to
+     *          get(int position) where position is a position in the list,
+     *          or <code>null</code> if the choice mode is set to
+     *          {@link #CHOICE_MODE_NONE}.
+     */
+    public SparseBooleanArray getCheckedItemPositions() {
+        if (mChoiceMode != CHOICE_MODE_NONE) {
+            return mCheckStates;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the set of checked items ids. The result is only valid if the
+     * choice mode has not been set to {@link #CHOICE_MODE_NONE} and the adapter
+     * has stable IDs. ({@link ListAdapter#hasStableIds()} == {@code true})
+     *
+     * @return A new array which contains the id of each checked item in the
+     *         list.
+     */
+    public long[] getCheckedItemIds() {
+        if (mChoiceMode == CHOICE_MODE_NONE || mCheckedIdStates == null || mAdapter == null) {
+            return new long[0];
+        }
+
+        final LongSparseArray<Boolean> idStates = mCheckedIdStates;
+        final int count = idStates.size();
+        final long[] ids = new long[count];
+
+        for (int i = 0; i < count; i++) {
+            ids[i] = idStates.keyAt(i);
+        }
+
+        return ids;
+    }
+
+    /**
+     * Clear any choices previously set
+     */
+    public void clearChoices() {
+        if (mCheckStates != null) {
+            mCheckStates.clear();
+        }
+        if (mCheckedIdStates != null) {
+            mCheckedIdStates.clear();
+        }
+        mCheckedItemCount = 0;
+    }
+
+    /**
+     * Sets the checked state of the specified position. The is only valid if
+     * the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or
+     * {@link #CHOICE_MODE_MULTIPLE}.
+     *
+     * @param position The item whose checked state is to be checked
+     * @param value The new checked state for the item
+     */
+    public void setItemChecked(int position, boolean value) {
+        if (mChoiceMode == CHOICE_MODE_NONE) {
+            return;
+        }
+
+        // Start selection mode if needed. We don't need to if we're unchecking something.
+        if (value && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode == null) {
+            mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
+        }
+
+        if (mChoiceMode == CHOICE_MODE_MULTIPLE || mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
+            boolean oldValue = mCheckStates.get(position);
+            mCheckStates.put(position, value);
+            if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
+                if (value) {
+                    mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+                } else {
+                    mCheckedIdStates.delete(mAdapter.getItemId(position));
+                }
+            }
+            if (oldValue != value) {
+                if (value) {
+                    mCheckedItemCount++;
+                } else {
+                    mCheckedItemCount--;
+                }
+            }
+            if (mChoiceActionMode != null) {
+                final long id = mAdapter.getItemId(position);
+                mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
+                        position, id, value);
+            }
+        } else {
+            boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds();
+            // Clear all values if we're checking something, or unchecking the currently
+            // selected item
+            if (value || isItemChecked(position)) {
+                mCheckStates.clear();
+                if (updateIds) {
+                    mCheckedIdStates.clear();
+                }
+            }
+            // this may end up selecting the value we just cleared but this way
+            // we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on
+            if (value) {
+                mCheckStates.put(position, true);
+                if (updateIds) {
+                    mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+                }
+                mCheckedItemCount = 1;
+            } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
+                mCheckedItemCount = 0;
+            }
+        }
+
+        // Do not generate a data change while we are in the layout phase
+        if (!mInLayout && !mBlockLayoutRequests) {
+            mDataChanged = true;
+            rememberSyncState();
+            requestLayout();
+        }
+    }
+
+    @Override
+    public boolean performItemClick(View view, int position, long id) {
+        boolean handled = false;
+        boolean dispatchItemClick = true;
+
+        if (mChoiceMode != CHOICE_MODE_NONE) {
+            handled = true;
+
+            if (mChoiceMode == CHOICE_MODE_MULTIPLE ||
+                    (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode != null)) {
+                boolean newValue = !mCheckStates.get(position, false);
+                mCheckStates.put(position, newValue);
+                if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
+                    if (newValue) {
+                        mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+                    } else {
+                        mCheckedIdStates.delete(mAdapter.getItemId(position));
+                    }
+                }
+                if (newValue) {
+                    mCheckedItemCount++;
+                } else {
+                    mCheckedItemCount--;
+                }
+                if (mChoiceActionMode != null) {
+                    mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode,
+                            position, id, newValue);
+                    dispatchItemClick = false;
+                }
+            } else if (mChoiceMode == CHOICE_MODE_SINGLE) {
+                boolean newValue = !mCheckStates.get(position, false);
+                if (newValue) {
+                    mCheckStates.clear();
+                    mCheckStates.put(position, true);
+                    if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
+                        mCheckedIdStates.clear();
+                        mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
+                    }
+                    mCheckedItemCount = 1;
+                } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) {
+                    mCheckedItemCount = 0;
+                }
+            }
+
+            mDataChanged = true;
+            rememberSyncState();
+            requestLayout();
+        }
+
+        if (dispatchItemClick) {
+            handled |= super.performItemClick(view, position, id);
+        }
+
+        return handled;
+    }
+
+    /**
+     * @see #setChoiceMode(int)
+     *
+     * @return The current choice mode
+     */
+    public int getChoiceMode() {
+        return mChoiceMode;
+    }
+
+    /**
+     * Defines the choice behavior for the List. By default, Lists do not have any choice behavior
+     * ({@link #CHOICE_MODE_NONE}). By setting the choiceMode to {@link #CHOICE_MODE_SINGLE}, the
+     * List allows up to one item to  be in a chosen state. By setting the choiceMode to
+     * {@link #CHOICE_MODE_MULTIPLE}, the list allows any number of items to be chosen.
+     *
+     * @param choiceMode One of {@link #CHOICE_MODE_NONE}, {@link #CHOICE_MODE_SINGLE}, or
+     * {@link #CHOICE_MODE_MULTIPLE}
+     */
+    public void setChoiceMode(int choiceMode) {
+        mChoiceMode = choiceMode;
+        if (mChoiceActionMode != null) {
+            mChoiceActionMode.finish();
+            mChoiceActionMode = null;
+        }
+        if (mChoiceMode != CHOICE_MODE_NONE) {
+            if (mCheckStates == null) {
+                mCheckStates = new SparseBooleanArray();
+            }
+            if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) {
+                mCheckedIdStates = new LongSparseArray<Boolean>();
+            }
+            // Modal multi-choice mode only has choices when the mode is active. Clear them.
+            if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
+                clearChoices();
+                setLongClickable(true);
+            }
+        }
+    }
+
+    /**
+     * Set a {@link MultiChoiceModeListener} that will manage the lifecycle of the
+     * selection {@link ActionMode}. Only used when the choice mode is set to
+     * {@link #CHOICE_MODE_MULTIPLE_MODAL}.
+     *
+     * @param listener Listener that will manage the selection mode
+     *
+     * @see #setChoiceMode(int)
+     */
+    public void setMultiChoiceModeListener(MultiChoiceModeListener listener) {
+        if (mMultiChoiceModeCallback == null) {
+            mMultiChoiceModeCallback = new MultiChoiceModeWrapper();
+        }
+        mMultiChoiceModeCallback.setWrapped(listener);
     }
 
     /**
@@ -893,6 +1210,10 @@
         int position;
         int height;
         String filter;
+        boolean inActionMode;
+        int checkedItemCount;
+        SparseBooleanArray checkState;
+        LongSparseArray<Boolean> checkIdState;
 
         /**
          * Constructor called from {@link AbsListView#onSaveInstanceState()}
@@ -912,6 +1233,15 @@
             position = in.readInt();
             height = in.readInt();
             filter = in.readString();
+            inActionMode = in.readByte() != 0;
+            checkedItemCount = in.readInt();
+            checkState = in.readSparseBooleanArray();
+            long[] idState = in.createLongArray();
+
+            if (idState.length > 0) {
+                checkIdState = new LongSparseArray<Boolean>();
+                checkIdState.setValues(idState, Boolean.TRUE);
+            }
         }
 
         @Override
@@ -923,6 +1253,10 @@
             out.writeInt(position);
             out.writeInt(height);
             out.writeString(filter);
+            out.writeByte((byte) (inActionMode ? 1 : 0));
+            out.writeInt(checkedItemCount);
+            out.writeSparseBooleanArray(checkState);
+            out.writeLongArray(checkIdState != null ? checkIdState.getKeys() : new long[0]);
         }
 
         @Override
@@ -934,7 +1268,8 @@
                     + " viewTop=" + viewTop
                     + " position=" + position
                     + " height=" + height
-                    + " filter=" + filter + "}";
+                    + " filter=" + filter
+                    + " checkState=" + checkState + "}";
         }
 
         public static final Parcelable.Creator<SavedState> CREATOR
@@ -998,6 +1333,12 @@
             }
         }
 
+        ss.inActionMode = mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode != null;
+
+        ss.checkState = mCheckStates;
+        ss.checkIdState = mCheckedIdStates;
+        ss.checkedItemCount = mCheckedItemCount;
+
         return ss;
     }
 
@@ -1020,6 +1361,7 @@
             setSelectedPositionInt(INVALID_POSITION);
             // Do this before setting mNeedSync since setNextSelectedPosition looks at mNeedSync
             setNextSelectedPositionInt(INVALID_POSITION);
+            mSelectorPosition = INVALID_POSITION;
             mNeedSync = true;
             mSyncRowId = ss.firstId;
             mSyncPosition = ss.position;
@@ -1029,6 +1371,21 @@
 
         setFilterText(ss.filter);
 
+        if (ss.checkState != null) {
+            mCheckStates = ss.checkState;
+        }
+
+        if (ss.checkIdState != null) {
+            mCheckedIdStates = ss.checkIdState;
+        }
+
+        mCheckedItemCount = ss.checkedItemCount;
+
+        if (ss.inActionMode && mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL &&
+                mMultiChoiceModeCallback != null) {
+            mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
+        }
+
         requestLayout();
     }
 
@@ -1104,6 +1461,8 @@
         setSelectedPositionInt(INVALID_POSITION);
         setNextSelectedPositionInt(INVALID_POSITION);
         mSelectedTop = 0;
+        mSelectorShowing = false;
+        mSelectorPosition = INVALID_POSITION;
         mSelectorRect.setEmpty();
         invalidate();
     }
@@ -1171,10 +1530,6 @@
         int result;
         if (mSmoothScrollbarEnabled) {
             result = Math.max(mItemCount * 100, 0);
-            if (mScrollY != 0) {
-                // Compensate for overscroll
-                result += Math.abs((int) ((float) mScrollY / getHeight() * mItemCount * 100));
-            }
         } else {
             result = mItemCount;
         }
@@ -1247,8 +1602,6 @@
 
         layoutChildren();
         mInLayout = false;
-
-        mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
     }
 
     /**
@@ -1402,7 +1755,7 @@
             }
 
             if (child != scrapView) {
-                mRecycler.addScrapView(scrapView);
+                mRecycler.addScrapView(scrapView, position);
                 if (mCacheColorHint != 0) {
                     child.setDrawingCacheBackgroundColor(mCacheColorHint);
                 }
@@ -1428,16 +1781,25 @@
         return child;
     }
 
-    void positionSelector(View sel) {
+    void positionSelector(int position, View sel) {
+        if (position != INVALID_POSITION) {
+            mSelectorPosition = position;
+        }
+
         final Rect selectorRect = mSelectorRect;
         selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom());
+        if (sel instanceof SelectionBoundsAdjuster) {
+            ((SelectionBoundsAdjuster)sel).adjustListItemSelectionBounds(selectorRect);
+        }
         positionSelector(selectorRect.left, selectorRect.top, selectorRect.right,
                 selectorRect.bottom);
 
         final boolean isChildViewEnabled = mIsChildViewEnabled;
         if (sel.isEnabled() != isChildViewEnabled) {
             mIsChildViewEnabled = !isChildViewEnabled;
-            refreshDrawableState();
+            if (mSelectorShowing) {
+                refreshDrawableState();
+            }
         }
     }
 
@@ -1516,7 +1878,7 @@
     }
 
     private void drawSelector(Canvas canvas) {
-        if (shouldShowSelector() && mSelectorRect != null && !mSelectorRect.isEmpty()) {
+        if (!mSelectorRect.isEmpty()) {
             final Drawable selector = mSelector;
             selector.setBounds(mSelectorRect);
             selector.draw(canvas);
@@ -1560,7 +1922,7 @@
         mSelectionRightPadding = padding.right;
         mSelectionBottomPadding = padding.bottom;
         sel.setCallback(this);
-        sel.setState(getDrawableState());
+        updateSelectorState();
     }
 
     /**
@@ -1585,7 +1947,7 @@
         Drawable selector = mSelector;
         Rect selectorRect = mSelectorRect;
         if (selector != null && (isFocused() || touchModeDrawsInPressedState())
-                && selectorRect != null && !selectorRect.isEmpty()) {
+                && !selectorRect.isEmpty()) {
 
             final View v = getChildAt(mSelectedPosition - mFirstPosition);
 
@@ -1620,12 +1982,20 @@
         mScrollDown = down;
     }
 
+    void updateSelectorState() {
+        if (mSelector != null) {
+            if (shouldShowSelector()) {
+                mSelector.setState(getDrawableState());
+            } else {
+                mSelector.setState(StateSet.NOTHING);
+            }
+        }
+    }
+
     @Override
     protected void drawableStateChanged() {
         super.drawableStateChanged();
-        if (mSelector != null) {
-            mSelector.setState(getDrawableState());
-        }
+        updateSelectorState();
     }
 
     @Override
@@ -1668,6 +2038,12 @@
     }
 
     @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+        if (mSelector != null) mSelector.jumpToCurrentState();
+    }
+
+    @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
@@ -1678,6 +2054,16 @@
                 treeObserver.addOnGlobalLayoutListener(this);
             }
         }
+        
+        if (mAdapter != null && mDataSetObserver == null) {
+            mDataSetObserver = new AdapterDataSetObserver();
+            mAdapter.registerDataSetObserver(mDataSetObserver);
+
+            // Data may have changed while we were detached. Refresh.
+            mDataChanged = true;
+            mOldItemCount = mItemCount;
+            mItemCount = mAdapter.getCount();
+        }
     }
 
     @Override
@@ -1698,6 +2084,11 @@
                 mGlobalLayoutListenerAddedFilter = false;
             }
         }
+
+        if (mAdapter != null) {
+            mAdapter.unregisterDataSetObserver(mDataSetObserver);
+            mDataSetObserver = null;
+        }
     }
 
     @Override
@@ -1715,7 +2106,6 @@
                 mFlingRunnable.endFling();
                 if (mScrollY != 0) {
                     mScrollY = 0;
-                    finishGlows();
                     invalidate();
                 }
             }
@@ -1821,7 +2211,6 @@
                 } else {
                     mTouchMode = TOUCH_MODE_DONE_WAITING;
                 }
-
             }
         }
     }
@@ -1849,10 +2238,21 @@
         }
     }
 
-    private boolean performLongPress(final View child,
+    boolean performLongPress(final View child,
             final int longPressPosition, final long longPressId) {
-        boolean handled = false;
+        // CHOICE_MODE_MULTIPLE_MODAL takes over long press.
+        if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
+            if (mChoiceActionMode == null) {
+                mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
+                setItemChecked(longPressPosition, true);
+            }
+            // TODO Should we select the long pressed item if we were already in
+            // selection mode? (i.e. treat it like an item click?)
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+            return true;
+        }
 
+        boolean handled = false;
         if (mOnItemLongClickListener != null) {
             handled = mOnItemLongClickListener.onItemLongClick(AbsListView.this, child,
                     longPressPosition, longPressId);
@@ -1985,10 +2385,10 @@
                     mLayoutMode = LAYOUT_NORMAL;
 
                     if (!mDataChanged) {
-                        layoutChildren();
                         child.setPressed(true);
-                        positionSelector(child);
                         setPressed(true);
+                        layoutChildren();
+                        positionSelector(mMotionPosition, child);
 
                         final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
                         final boolean longClickable = isLongClickable();
@@ -2025,10 +2425,9 @@
         // Check if we have moved far enough that it looks more like a
         // scroll than a tap
         final int distance = Math.abs(deltaY);
-        final boolean overscroll = mScrollY != 0;
-        if (overscroll || distance > mTouchSlop) {
+        if (distance > mTouchSlop) {
             createScrollingCache();
-            mTouchMode = overscroll ? TOUCH_MODE_OVERSCROLL : TOUCH_MODE_SCROLL;
+            mTouchMode = TOUCH_MODE_SCROLL;
             mMotionCorrection = deltaY;
             final Handler handler = getHandler();
             // Handler should not be null unless the AbsListView is not attached to a
@@ -2064,19 +2463,6 @@
                 // touch mode). Force an initial layout to get rid of the selection.
                 layoutChildren();
             }
-        } else {
-            int touchMode = mTouchMode;
-            if (touchMode == TOUCH_MODE_OVERSCROLL || touchMode == TOUCH_MODE_OVERFLING) {
-                if (mFlingRunnable != null) {
-                    mFlingRunnable.endFling();
-                }
-
-                if (mScrollY != 0) {
-                    mScrollY = 0;
-                    finishGlows();
-                    invalidate();
-                }
-            }
         }
     }
 
@@ -2107,63 +2493,49 @@
 
         switch (action & MotionEvent.ACTION_MASK) {
         case MotionEvent.ACTION_DOWN: {
-            switch (mTouchMode) {
-            case TOUCH_MODE_OVERFLING: {
-                mFlingRunnable.endFling();
-                mTouchMode = TOUCH_MODE_OVERSCROLL;
-                mMotionY = mLastY = (int) ev.getY();
-                mMotionCorrection = 0;
-                mActivePointerId = ev.getPointerId(0);
-                break;
-            }
+            mActivePointerId = ev.getPointerId(0);
+            final int x = (int) ev.getX();
+            final int y = (int) ev.getY();
+            int motionPosition = pointToPosition(x, y);
+            if (!mDataChanged) {
+                if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
+                        && (getAdapter().isEnabled(motionPosition))) {
+                    // User clicked on an actual view (and was not stopping a fling). It might be a
+                    // click or a scroll. Assume it is a click until proven otherwise
+                    mTouchMode = TOUCH_MODE_DOWN;
+                    // FIXME Debounce
+                    if (mPendingCheckForTap == null) {
+                        mPendingCheckForTap = new CheckForTap();
+                    }
+                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
+                } else {
+                    if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
+                        // If we couldn't find a view to click on, but the down event was touching
+                        // the edge, we will bail out and try again. This allows the edge correcting
+                        // code in ViewRoot to try to find a nearby view to select
+                        return false;
+                    }
 
-            default: {
-                mActivePointerId = ev.getPointerId(0);
-                final int x = (int) ev.getX();
-                final int y = (int) ev.getY();
-                int motionPosition = pointToPosition(x, y);
-                if (!mDataChanged) {
-                    if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
-                            && (getAdapter().isEnabled(motionPosition))) {
-                        // User clicked on an actual view (and was not stopping a fling). It might be a
-                        // click or a scroll. Assume it is a click until proven otherwise
-                        mTouchMode = TOUCH_MODE_DOWN;
-                        // FIXME Debounce
-                        if (mPendingCheckForTap == null) {
-                            mPendingCheckForTap = new CheckForTap();
-                        }
-                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
-                    } else {
-                        if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
-                            // If we couldn't find a view to click on, but the down event was touching
-                            // the edge, we will bail out and try again. This allows the edge correcting
-                            // code in ViewRoot to try to find a nearby view to select
-                            return false;
-                        }
-
-                        if (mTouchMode == TOUCH_MODE_FLING) {
-                            // Stopped a fling. It is a scroll.
-                            createScrollingCache();
-                            mTouchMode = TOUCH_MODE_SCROLL;
-                            mMotionCorrection = 0;
-                            motionPosition = findMotionRow(y);
-                            reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
-                        }
+                    if (mTouchMode == TOUCH_MODE_FLING) {
+                        // Stopped a fling. It is a scroll.
+                        createScrollingCache();
+                        mTouchMode = TOUCH_MODE_SCROLL;
+                        mMotionCorrection = 0;
+                        motionPosition = findMotionRow(y);
+                        reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
                     }
                 }
+            }
 
-                if (motionPosition >= 0) {
-                    // Remember where the motion event started
-                    v = getChildAt(motionPosition - mFirstPosition);
-                    mMotionViewOriginalTop = v.getTop();
-                }
-                mMotionX = x;
-                mMotionY = y;
-                mMotionPosition = motionPosition;
-                mLastY = Integer.MIN_VALUE;
-                break;
+            if (motionPosition >= 0) {
+                // Remember where the motion event started
+                v = getChildAt(motionPosition - mFirstPosition);
+                mMotionViewOriginalTop = v.getTop();
             }
-            }
+            mMotionX = x;
+            mMotionY = y;
+            mMotionPosition = motionPosition;
+            mLastY = Integer.MIN_VALUE;
             break;
         }
 
@@ -2196,24 +2568,8 @@
                         requestDisallowInterceptTouchEvent(true);
                     }
 
-                    final int rawDeltaY = deltaY;
                     deltaY -= mMotionCorrection;
                     int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
-                    
-                    final int motionIndex;
-                    if (mMotionPosition >= 0) {
-                        motionIndex = mMotionPosition - mFirstPosition;
-                    } else {
-                        // If we don't have a motion position that we can reliably track,
-                        // pick something in the middle to make a best guess at things below.
-                        motionIndex = getChildCount() / 2;
-                    }
-
-                    int motionViewPrevTop = 0;
-                    View motionView = this.getChildAt(motionIndex);
-                    if (motionView != null) {
-                        motionViewPrevTop = motionView.getTop();
-                    }
 
                     // No need to do all this work if we're not going to move anyway
                     boolean atEdge = false;
@@ -2222,117 +2578,23 @@
                     }
 
                     // Check to see if we have bumped into the scroll limit
-                    motionView = this.getChildAt(motionIndex);
-                    if (motionView != null) {
-                        // Check if the top of the motion view is where it is
-                        // supposed to be
-                        final int motionViewRealTop = motionView.getTop();
-                        if (atEdge) {
-                            // Apply overscroll
-
-                            int overscroll = -incrementalDeltaY -
-                                    (motionViewRealTop - motionViewPrevTop);
-                            overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
-                                    0, mOverscrollDistance, true);
-                            if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
-                                // Don't allow overfling if we're at the edge.
-                                mVelocityTracker.clear();
-                            }
-
-                            final int overscrollMode = getOverScrollMode();
-                            if (overscrollMode == OVER_SCROLL_ALWAYS ||
-                                    (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
-                                            !contentFits())) {
-                                mDirection = 0; // Reset when entering overscroll.
-                                mTouchMode = TOUCH_MODE_OVERSCROLL;
-                                if (rawDeltaY > 0) {
-                                    mEdgeGlowTop.onPull((float) overscroll / getHeight());
-                                    if (!mEdgeGlowBottom.isFinished()) {
-                                        mEdgeGlowBottom.onRelease();
-                                    }
-                                } else if (rawDeltaY < 0) {
-                                    mEdgeGlowBottom.onPull((float) overscroll / getHeight());
-                                    if (!mEdgeGlowTop.isFinished()) {
-                                        mEdgeGlowTop.onRelease();
-                                    }
-                                }
-                            }
+                    if (atEdge && getChildCount() > 0) {
+                        // Treat this like we're starting a new scroll from the current
+                        // position. This will let the user start scrolling back into
+                        // content immediately rather than needing to scroll back to the
+                        // point where they hit the limit first.
+                        int motionPosition = findMotionRow(y);
+                        if (motionPosition >= 0) {
+                            final View motionView = getChildAt(motionPosition - mFirstPosition);
+                            mMotionViewOriginalTop = motionView.getTop();
                         }
                         mMotionY = y;
+                        mMotionPosition = motionPosition;
                         invalidate();
                     }
                     mLastY = y;
                 }
                 break;
-
-            case TOUCH_MODE_OVERSCROLL:
-                if (y != mLastY) {
-                    final int rawDeltaY = deltaY;
-                    deltaY -= mMotionCorrection;
-                    int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
-
-                    final int oldScroll = mScrollY;
-                    final int newScroll = oldScroll - incrementalDeltaY;
-                    int newDirection = y > mLastY ? 1 : -1;
-
-                    if (mDirection == 0) {
-                        mDirection = newDirection;
-                    }
-
-                    if (mDirection != newDirection) {
-                        // Coming back to 'real' list scrolling
-                        incrementalDeltaY = -newScroll;
-                        mScrollY = 0;
-
-                        // No need to do all this work if we're not going to move anyway
-                        if (incrementalDeltaY != 0) {
-                            trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
-                        }
-
-                        // Check to see if we are back in
-                        View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
-                        if (motionView != null) {
-                            mTouchMode = TOUCH_MODE_SCROLL;
-
-                            // We did not scroll the full amount. Treat this essentially like the
-                            // start of a new touch scroll
-                            final int motionPosition = findClosestMotionRow(y);
-
-                            mMotionCorrection = 0;
-                            motionView = getChildAt(motionPosition - mFirstPosition);
-                            mMotionViewOriginalTop = motionView.getTop();
-                            mMotionY = y;
-                            mMotionPosition = motionPosition;
-                        }
-                    } else {
-                        overScrollBy(0, -incrementalDeltaY, 0, mScrollY, 0, 0,
-                                0, mOverscrollDistance, true);
-                        final int overscrollMode = getOverScrollMode();
-                        if (overscrollMode == OVER_SCROLL_ALWAYS ||
-                                (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
-                                        !contentFits())) {
-                            if (rawDeltaY > 0) {
-                                mEdgeGlowTop.onPull((float) -incrementalDeltaY / getHeight());
-                                if (!mEdgeGlowBottom.isFinished()) {
-                                    mEdgeGlowBottom.onRelease();
-                                }
-                            } else if (rawDeltaY < 0) {
-                                mEdgeGlowBottom.onPull((float) -incrementalDeltaY / getHeight());
-                                if (!mEdgeGlowTop.isFinished()) {
-                                    mEdgeGlowTop.onRelease();
-                                }
-                            }
-                            invalidate();
-                        }
-                        if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
-                            // Don't allow overfling if we're at the edge.
-                            mVelocityTracker.clear();
-                        }
-                    }
-                    mLastY = y;
-                    mDirection = newDirection;
-                }
-                break;
             }
 
             break;
@@ -2373,7 +2635,7 @@
                             setSelectedPositionInt(mMotionPosition);
                             layoutChildren();
                             child.setPressed(true);
-                            positionSelector(child);
+                            positionSelector(mMotionPosition, child);
                             setPressed(true);
                             if (mSelector != null) {
                                 Drawable d = mSelector.getCurrent();
@@ -2383,16 +2645,17 @@
                             }
                             postDelayed(new Runnable() {
                                 public void run() {
+                                    mTouchMode = TOUCH_MODE_REST;
                                     child.setPressed(false);
                                     setPressed(false);
                                     if (!mDataChanged) {
                                         post(performClick);
                                     }
-                                    mTouchMode = TOUCH_MODE_REST;
                                 }
                             }, ViewConfiguration.getPressedStateDuration());
                         } else {
                             mTouchMode = TOUCH_MODE_REST;
+                            updateSelectorState();
                         }
                         return true;
                     } else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
@@ -2400,33 +2663,24 @@
                     }
                 }
                 mTouchMode = TOUCH_MODE_REST;
+                updateSelectorState();
                 break;
             case TOUCH_MODE_SCROLL:
                 final int childCount = getChildCount();
                 if (childCount > 0) {
-                    final int firstChildTop = getChildAt(0).getTop();
-                    final int lastChildBottom = getChildAt(childCount - 1).getBottom();
-                    final int contentTop = mListPadding.top;
-                    final int contentBottom = getHeight() - mListPadding.bottom;
-                    if (mFirstPosition == 0 && firstChildTop >= contentTop &&
+                    if (mFirstPosition == 0 && getChildAt(0).getTop() >= mListPadding.top &&
                             mFirstPosition + childCount < mItemCount &&
-                            lastChildBottom <= getHeight() - contentBottom) {
+                            getChildAt(childCount - 1).getBottom() <=
+                                    getHeight() - mListPadding.bottom) {
                         mTouchMode = TOUCH_MODE_REST;
                         reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                     } else {
                         final VelocityTracker velocityTracker = mVelocityTracker;
                         velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                        final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
-
-                        // Fling if we have enough velocity and we aren't at a boundary.
-                        // Since we can potentially overfling more than we can overscroll, don't
-                        // allow the weird behavior where you can scroll to a boundary then
-                        // fling further.
-                        if (Math.abs(initialVelocity) > mMinimumVelocity &&
-                                !((mFirstPosition == 0 &&
-                                        firstChildTop == contentTop - mOverscrollDistance) ||
-                                  (mFirstPosition + childCount == mItemCount &&
-                                        lastChildBottom == contentBottom + mOverscrollDistance))) {
+                        final int initialVelocity = (int)
+                                (velocityTracker.getYVelocity(mActivePointerId) * mVelocityScale);
+    
+                        if (Math.abs(initialVelocity) > mMinimumVelocity) {
                             if (mFlingRunnable == null) {
                                 mFlingRunnable = new FlingRunnable();
                             }
@@ -2443,32 +2697,10 @@
                     reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                 }
                 break;
-
-            case TOUCH_MODE_OVERSCROLL:
-                if (mFlingRunnable == null) {
-                    mFlingRunnable = new FlingRunnable();
-                }
-                final VelocityTracker velocityTracker = mVelocityTracker;
-                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
-
-                reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
-                if (Math.abs(initialVelocity) > mMinimumVelocity) {
-                    mFlingRunnable.startOverfling(-initialVelocity);
-                } else {
-                    mFlingRunnable.startSpringback();
-                }
-
-                break;
             }
 
             setPressed(false);
 
-            if (mEdgeGlowTop != null) {
-                mEdgeGlowTop.onRelease();
-                mEdgeGlowBottom.onRelease();
-            }
-
             // Need to redraw since we probably aren't drawing the selector anymore
             invalidate();
 
@@ -2494,42 +2726,24 @@
         }
 
         case MotionEvent.ACTION_CANCEL: {
-            switch (mTouchMode) {
-            case TOUCH_MODE_OVERSCROLL:
-                if (mFlingRunnable == null) {
-                    mFlingRunnable = new FlingRunnable();
-                }
-                mFlingRunnable.startSpringback();
-                break;
+            mTouchMode = TOUCH_MODE_REST;
+            setPressed(false);
+            View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
+            if (motionView != null) {
+                motionView.setPressed(false);
+            }
+            clearScrollingCache();
 
-            case TOUCH_MODE_OVERFLING:
-                // Do nothing - let it play out.
-                break;
+            final Handler handler = getHandler();
+            if (handler != null) {
+                handler.removeCallbacks(mPendingCheckForLongPress);
+            }
 
-            default:
-                mTouchMode = TOUCH_MODE_REST;
-                setPressed(false);
-                View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
-                if (motionView != null) {
-                    motionView.setPressed(false);
-                }
-                clearScrollingCache();
-
-                final Handler handler = getHandler();
-                if (handler != null) {
-                    handler.removeCallbacks(mPendingCheckForLongPress);
-                }
-
-                if (mVelocityTracker != null) {
-                    mVelocityTracker.recycle();
-                    mVelocityTracker = null;
-                }
+            if (mVelocityTracker != null) {
+                mVelocityTracker.recycle();
+                mVelocityTracker = null;
             }
             
-            if (mEdgeGlowTop != null) {
-                mEdgeGlowTop.onRelease();
-                mEdgeGlowBottom.onRelease();
-            }
             mActivePointerId = INVALID_POINTER;
             break;
         }
@@ -2554,61 +2768,10 @@
     }
 
     @Override
-    protected void onOverScrolled(int scrollX, int scrollY,
-            boolean clampedX, boolean clampedY) {
-        mScrollY = scrollY;
-
-        if (clampedY) {
-            // Velocity is broken by hitting the limit; don't start a fling off of this.
-            if (mVelocityTracker != null) {
-                mVelocityTracker.clear();
-            }
-        }
-        awakenScrollBars();
-    }
-
-    @Override
     public void draw(Canvas canvas) {
         super.draw(canvas);
-        if (mEdgeGlowTop != null) {
-            final int scrollY = mScrollY;
-            if (!mEdgeGlowTop.isFinished()) {
-                final int restoreCount = canvas.save();
-                final int width = getWidth();
-
-                canvas.translate(-width / 2, Math.min(0, scrollY + mFirstPositionDistanceGuess));
-                mEdgeGlowTop.setSize(width * 2, getHeight());
-                if (mEdgeGlowTop.draw(canvas)) {
-                    invalidate();
-                }
-                canvas.restoreToCount(restoreCount);
-            }
-            if (!mEdgeGlowBottom.isFinished()) {
-                final int restoreCount = canvas.save();
-                final int width = getWidth();
-                final int height = getHeight();
-
-                canvas.translate(-width / 2,
-                        Math.max(height, scrollY + mLastPositionDistanceGuess));
-                canvas.rotate(180, width, 0);
-                mEdgeGlowBottom.setSize(width * 2, height);
-                if (mEdgeGlowBottom.draw(canvas)) {
-                    invalidate();
-                }
-                canvas.restoreToCount(restoreCount);
-            }
-        }
         if (mFastScroller != null) {
-            final int scrollY = mScrollY;
-            if (scrollY != 0) {
-                // Pin to the top/bottom during overscroll
-                int restoreCount = canvas.save();
-                canvas.translate(0, (float) scrollY);
-                mFastScroller.draw(canvas);
-                canvas.restoreToCount(restoreCount);
-            } else {
-                mFastScroller.draw(canvas);
-            }
+            mFastScroller.draw(canvas);
         }
     }
 
@@ -2627,10 +2790,6 @@
         switch (action & MotionEvent.ACTION_MASK) {
         case MotionEvent.ACTION_DOWN: {
             int touchMode = mTouchMode;
-            if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) {
-                mMotionCorrection = 0;
-                return true;
-            }
             
             final int x = (int) ev.getX();
             final int y = (int) ev.getY();
@@ -2695,7 +2854,6 @@
             final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
             mMotionX = (int) ev.getX(newPointerIndex);
             mMotionY = (int) ev.getY(newPointerIndex);
-            mMotionCorrection = 0;
             mActivePointerId = ev.getPointerId(newPointerIndex);
             if (mVelocityTracker != null) {
                 mVelocityTracker.clear();
@@ -2751,7 +2909,7 @@
         /**
          * Tracks the decay of a fling scroll
          */
-        private final OverScroller mScroller;
+        private final Scroller mScroller;
 
         /**
          * Y value reported by mScroller on the previous fling
@@ -2759,7 +2917,7 @@
         private int mLastFlingY;
 
         FlingRunnable() {
-            mScroller = new OverScroller(getContext());
+            mScroller = new Scroller(getContext());
         }
 
         void start(int initialVelocity) {
@@ -2778,42 +2936,6 @@
             }
         }
 
-        void startSpringback() {
-            if (mScroller.springBack(0, mScrollY, 0, 0, 0, 0)) {
-                mTouchMode = TOUCH_MODE_OVERFLING;
-                invalidate();
-                post(this);
-            } else {
-                mTouchMode = TOUCH_MODE_REST;
-            }
-        }
-
-        void startOverfling(int initialVelocity) {
-            final int min = mScrollY > 0 ? Integer.MIN_VALUE : 0;
-            final int max = mScrollY > 0 ? 0 : Integer.MAX_VALUE;
-            mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, min, max, 0, getHeight());
-            mTouchMode = TOUCH_MODE_OVERFLING;
-            invalidate();
-            post(this);
-        }
-
-        void edgeReached(int delta) {
-            mScroller.notifyVerticalEdgeReached(mScrollY, 0, mOverflingDistance);
-            final int overscrollMode = getOverScrollMode();
-            if (overscrollMode == OVER_SCROLL_ALWAYS ||
-                    (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits())) {
-                mTouchMode = TOUCH_MODE_OVERFLING;
-                final int vel = (int) mScroller.getCurrVelocity();
-                if (delta > 0) {
-                    mEdgeGlowTop.onAbsorb(vel);
-                } else {
-                    mEdgeGlowBottom.onAbsorb(vel);
-                }
-            }
-            invalidate();
-            post(this);
-        }
-
         void startScroll(int distance, int duration) {
             int initialY = distance < 0 ? Integer.MAX_VALUE : 0;
             mLastFlingY = initialY;
@@ -2825,14 +2947,13 @@
         private void endFling() {
             mTouchMode = TOUCH_MODE_REST;
 
-            reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
-            clearScrollingCache();
-
             removeCallbacks(this);
-
             if (mPositionScroller != null) {
                 removeCallbacks(mPositionScroller);
             }
+
+            reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+            clearScrollingCache();
         }
 
         public void run() {
@@ -2846,7 +2967,7 @@
                     return;
                 }
 
-                final OverScroller scroller = mScroller;
+                final Scroller scroller = mScroller;
                 boolean more = scroller.computeScrollOffset();
                 final int y = scroller.getCurrY();
 
@@ -2875,26 +2996,8 @@
                     delta = Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1), delta);
                 }
 
-                // Check to see if we have bumped into the scroll limit
-                View motionView = getChildAt(mMotionPosition - mFirstPosition);
-                int oldTop = 0;
-                if (motionView != null) {
-                    oldTop = motionView.getTop();
-                }
-
-                final boolean atEnd = trackMotionScroll(delta, delta);
-                if (atEnd) {
-                    if (motionView != null) {
-                        // Tweak the scroll for how far we overshot
-                        int overshoot = -(delta - (motionView.getTop() - oldTop));
-                        overScrollBy(0, overshoot, 0, mScrollY, 0, 0,
-                                0, mOverflingDistance, false);
-                    }
-                    if (more) {
-                        edgeReached(delta);
-                    }
-                    break;
-                }
+                // Don't stop just because delta is zero (it could have been rounded)
+                final boolean atEnd = trackMotionScroll(delta, delta) && (delta != 0);
 
                 if (more && !atEnd) {
                     invalidate();
@@ -2912,24 +3015,6 @@
                 }
                 break;
             }
-
-            case TOUCH_MODE_OVERFLING: {
-                final OverScroller scroller = mScroller;
-                if (scroller.computeScrollOffset()) {
-                    final int scrollY = mScrollY;
-                    final int deltaY = scroller.getCurrY() - scrollY;
-                    if (overScrollBy(0, deltaY, 0, scrollY, 0, 0,
-                            0, mOverflingDistance, false)) {
-                        startSpringback();
-                    } else {
-                        invalidate();
-                        post(this);
-                    }
-                } else {
-                    endFling();
-                }
-                break;
-            }
             }
 
         }
@@ -2943,6 +3028,7 @@
         private static final int MOVE_UP_POS = 2;
         private static final int MOVE_DOWN_BOUND = 3;
         private static final int MOVE_UP_BOUND = 4;
+        private static final int MOVE_OFFSET = 5;
         
         private int mMode;
         private int mTargetPos;
@@ -2950,6 +3036,8 @@
         private int mLastSeenPos;
         private int mScrollDuration;
         private final int mExtraScroll;
+
+        private int mOffsetFromTop;
         
         PositionScroller() {
             mExtraScroll = ViewConfiguration.get(mContext).getScaledFadingEdgeLength();
@@ -2959,7 +3047,7 @@
             final int firstPos = mFirstPosition;
             final int lastPos = firstPos + getChildCount() - 1;
             
-            int viewTravelCount = 0;
+            int viewTravelCount;
             if (position <= firstPos) {                
                 viewTravelCount = firstPos - position + 1;
                 mMode = MOVE_UP_POS;
@@ -2992,7 +3080,7 @@
             final int firstPos = mFirstPosition;
             final int lastPos = firstPos + getChildCount() - 1;
             
-            int viewTravelCount = 0;
+            int viewTravelCount;
             if (position <= firstPos) {
                 final int boundPosFromLast = lastPos - boundPosition;
                 if (boundPosFromLast < 1) {
@@ -3041,12 +3129,52 @@
             
             post(this);
         }
-        
+
+        void startWithOffset(int position, int offset) {
+            startWithOffset(position, offset, SCROLL_DURATION);
+        }
+
+        void startWithOffset(int position, int offset, int duration) {
+            mTargetPos = position;
+            mOffsetFromTop = offset;
+            mBoundPos = INVALID_POSITION;
+            mLastSeenPos = INVALID_POSITION;
+            mMode = MOVE_OFFSET;
+
+            final int firstPos = mFirstPosition;
+            final int childCount = getChildCount();
+            final int lastPos = firstPos + childCount - 1;
+
+            int viewTravelCount;
+            if (position < firstPos) {
+                viewTravelCount = firstPos - position;
+            } else if (position > lastPos) {
+                viewTravelCount = position - lastPos;
+            } else {
+                // On-screen, just scroll.
+                final int targetTop = getChildAt(position - firstPos).getTop();
+                smoothScrollBy(targetTop - offset, duration);
+                return;
+            }
+
+            // Estimate how many screens we should travel
+            final float screenTravelCount = (float) viewTravelCount / childCount;
+            mScrollDuration = screenTravelCount < 1 ? (int) (screenTravelCount * duration) :
+                    (int) (duration / screenTravelCount);
+            mLastSeenPos = INVALID_POSITION;
+
+            post(this);
+        }
+
         void stop() {
             removeCallbacks(this);
         }
-        
+
         public void run() {
+            if (mTouchMode != TOUCH_MODE_FLING && mLastSeenPos != INVALID_POSITION) {
+                return;
+            }
+
             final int listHeight = getHeight();
             final int firstPos = mFirstPosition;
             
@@ -3171,6 +3299,35 @@
                 break;
             }
 
+            case MOVE_OFFSET: {
+                if (mLastSeenPos == firstPos) {
+                    // No new views, let things keep going.
+                    post(this);
+                    return;
+                }
+
+                mLastSeenPos = firstPos;
+
+                final int childCount = getChildCount();
+                final int position = mTargetPos;
+                final int lastPos = firstPos + childCount - 1;
+
+                if (position < firstPos) {
+                    smoothScrollBy(-getHeight(), mScrollDuration);
+                    post(this);
+                } else if (position > lastPos) {
+                    smoothScrollBy(getHeight(), mScrollDuration);
+                    post(this);
+                } else {
+                    // On-screen, just scroll.
+                    final int targetTop = getChildAt(position - firstPos).getTop();
+                    final int distance = targetTop - mOffsetFromTop;
+                    smoothScrollBy(distance,
+                            (int) (mScrollDuration * ((float) distance / getHeight())));
+                }
+                break;
+            }
+
             default:
                 break;
             }
@@ -3178,6 +3335,30 @@
     }
     
     /**
+     * The amount of friction applied to flings. The default value
+     * is {@link ViewConfiguration#getScrollFriction}.
+     * 
+     * @return A scalar dimensionless value representing the coefficient of
+     *         friction.
+     */
+    public void setFriction(float friction) {
+        if (mFlingRunnable == null) {
+            mFlingRunnable = new FlingRunnable();
+        }
+        mFlingRunnable.mScroller.setFriction(friction);        
+    }
+
+    /**
+     * Sets a scale factor for the fling velocity. The initial scale
+     * factor is 1.0.
+     * 
+     * @param scale The scale factor to multiply the velocity by.
+     */
+    public void setVelocityScale(float scale) {
+        mVelocityScale = scale;
+    }
+    
+    /**
      * Smoothly scroll to the specified adapter position. The view will
      * scroll such that the indicated position is displayed.
      * @param position Scroll to this adapter position.
@@ -3188,7 +3369,44 @@
         }
         mPositionScroller.start(position);
     }
-    
+
+    /**
+     * Smoothly scroll to the specified adapter position. The view will scroll
+     * such that the indicated position is displayed <code>offset</code> pixels from
+     * the top edge of the view. If this is impossible, (e.g. the offset would scroll
+     * the first or last item beyond the boundaries of the list) it will get as close
+     * as possible. The scroll will take <code>duration</code> milliseconds to complete.
+     *
+     * @param position Position to scroll to
+     * @param offset Desired distance in pixels of <code>position</code> from the top
+     *               of the view when scrolling is finished
+     * @param duration Number of milliseconds to use for the scroll
+     */
+    public void smoothScrollToPositionFromTop(int position, int offset, int duration) {
+        if (mPositionScroller == null) {
+            mPositionScroller = new PositionScroller();
+        }
+        mPositionScroller.startWithOffset(position, offset, duration);
+    }
+
+    /**
+     * Smoothly scroll to the specified adapter position. The view will scroll
+     * such that the indicated position is displayed <code>offset</code> pixels from
+     * the top edge of the view. If this is impossible, (e.g. the offset would scroll
+     * the first or last item beyond the boundaries of the list) it will get as close
+     * as possible.
+     *
+     * @param position Position to scroll to
+     * @param offset Desired distance in pixels of <code>position</code> from the top
+     *               of the view when scrolling is finished
+     */
+    public void smoothScrollToPositionFromTop(int position, int offset) {
+        if (mPositionScroller == null) {
+            mPositionScroller = new PositionScroller();
+        }
+        mPositionScroller.startWithOffset(position, offset);
+    }
+
     /**
      * Smoothly scroll to the specified adapter position. The view will
      * scroll such that the indicated position is displayed, but it will
@@ -3216,7 +3434,46 @@
         } else {
             mFlingRunnable.endFling();
         }
-        mFlingRunnable.startScroll(distance, duration);
+        // No sense starting to scroll if we're not going anywhere
+        if (distance != 0) {
+            mFlingRunnable.startScroll(distance, duration);
+        }
+    }
+
+    /**
+     * Allows RemoteViews to scroll relatively to a position.
+     */
+    void smoothScrollByOffset(int position) {
+        int index = -1;
+        if (position < 0) {
+            index = getFirstVisiblePosition();
+        } else if (position > 0) {
+            index = getLastVisiblePosition();
+        }
+
+        if (index > -1) {
+            View child = getChildAt(index - getFirstVisiblePosition());
+            if (child != null) {
+                Rect visibleRect = new Rect();
+                if (child.getGlobalVisibleRect(visibleRect)) {
+                    // the child is partially visible
+                    int childRectArea = child.getWidth() * child.getHeight();
+                    int visibleRectArea = visibleRect.width() * visibleRect.height();
+                    float visibleArea = (visibleRectArea / (float) childRectArea);
+                    final float visibleThreshold = 0.75f;
+                    if ((position < 0) && (visibleArea < visibleThreshold)) {
+                        // the top index is not perceivably visible so offset
+                        // to account for showing that top index as well
+                        ++index;
+                    } else if ((position > 0) && (visibleArea < visibleThreshold)) {
+                        // the bottom index is not perceivably visible so offset
+                        // to account for showing that bottom index as well
+                        --index;
+                    }
+                }
+                smoothScrollToPosition(Math.max(0, Math.min(getCount(), index + position)));
+            }
+        }
     }
 
     private void createScrollingCache() {
@@ -3286,29 +3543,16 @@
 
         final int firstPosition = mFirstPosition;
 
-        // Update our guesses for where the first and last views are
-        if (firstPosition == 0) {
-            mFirstPositionDistanceGuess = firstTop - mListPadding.top;
-        } else {
-            mFirstPositionDistanceGuess += incrementalDeltaY;
-        }
-        if (firstPosition + childCount == mItemCount) {
-            mLastPositionDistanceGuess = lastBottom + mListPadding.bottom;
-        } else {
-            mLastPositionDistanceGuess += incrementalDeltaY;
-        }
-
-        if (firstPosition == 0 && firstTop >= listPadding.top && incrementalDeltaY >= 0) {
+        if (firstPosition == 0 && firstTop >= listPadding.top && deltaY >= 0) {
             // Don't need to move views down if the top of the first position
             // is already visible
-            return incrementalDeltaY != 0;
+            return true;
         }
 
-        if (firstPosition + childCount == mItemCount && lastBottom <= end &&
-                incrementalDeltaY <= 0) {
+        if (firstPosition + childCount == mItemCount && lastBottom <= end && deltaY <= 0) {
             // Don't need to move views up if the bottom of the last position
             // is already visible
-            return incrementalDeltaY != 0;
+            return true;
         }
 
         final boolean down = incrementalDeltaY < 0;
@@ -3334,7 +3578,7 @@
                     count++;
                     int position = firstPosition + i;
                     if (position >= headerViewsCount && position < footerViewsStart) {
-                        mRecycler.addScrapView(child);
+                        mRecycler.addScrapView(child, position);
 
                         if (ViewDebug.TRACE_RECYCLER) {
                             ViewDebug.trace(child,
@@ -3355,7 +3599,7 @@
                     count++;
                     int position = firstPosition + i;
                     if (position >= headerViewsCount && position < footerViewsStart) {
-                        mRecycler.addScrapView(child);
+                        mRecycler.addScrapView(child, position);
 
                         if (ViewDebug.TRACE_RECYCLER) {
                             ViewDebug.trace(child,
@@ -3390,8 +3634,15 @@
         if (!inTouchMode && mSelectedPosition != INVALID_POSITION) {
             final int childIndex = mSelectedPosition - mFirstPosition;
             if (childIndex >= 0 && childIndex < getChildCount()) {
-                positionSelector(getChildAt(childIndex));
+                positionSelector(mSelectedPosition, getChildAt(childIndex));
             }
+        } else if (mSelectorPosition != INVALID_POSITION) {
+            final int childIndex = mSelectorPosition - mFirstPosition;
+            if (childIndex >= 0 && childIndex < getChildCount()) {
+                positionSelector(INVALID_POSITION, getChildAt(childIndex));
+            }
+        } else {
+            mSelectorRect.setEmpty();
         }
 
         mBlockLayoutRequests = false;
@@ -3443,7 +3694,7 @@
             setSelectedPositionInt(INVALID_POSITION);
             setNextSelectedPositionInt(INVALID_POSITION);
             mSelectedTop = 0;
-            mSelectorRect.setEmpty();
+            mSelectorShowing = false;
         }
     }
 
@@ -3469,22 +3720,6 @@
      * @return The position of the first (or only) item in the row containing y
      */
     abstract int findMotionRow(int y);
-    
-    /**
-     * Find the row closest to y. This row will be used as the motion row when scrolling.
-     * 
-     * @param y Where the user touched
-     * @return The position of the first (or only) item in the row closest to y
-     */
-    int findClosestMotionRow(int y) {
-        final int childCount = getChildCount();
-        if (childCount == 0) {
-            return INVALID_POSITION;
-        }
-        
-        final int motionRow = findMotionRow(y);
-        return motionRow != INVALID_POSITION ? motionRow : mFirstPosition + childCount - 1;
-    }
 
     /**
      * Causes all the views to be rebuilt and redrawn.
@@ -3590,6 +3825,7 @@
 
         mResurrectToPosition = INVALID_POSITION;
         removeCallbacks(mFlingRunnable);
+        removeCallbacks(mPositionScroller);
         mTouchMode = TOUCH_MODE_REST;
         clearScrollingCache();
         mSpecificTop = selectedTop;
@@ -3718,6 +3954,7 @@
         mNextSelectedPosition = INVALID_POSITION;
         mNextSelectedRowId = INVALID_ROW_ID;
         mNeedSync = false;
+        mSelectorPosition = INVALID_POSITION;
         checkSelectionChanged();
     }
 
@@ -4132,7 +4369,11 @@
 
     /**
      * When set to a non-zero value, the cache color hint indicates that this list is always drawn
-     * on top of a solid, single-color, opaque background
+     * on top of a solid, single-color, opaque background.
+     *
+     * Zero means that what's behind this object is translucent (non solid) or is not made of a
+     * single color. This hint will not affect any existing background drawable set on this view (
+     * typically set via {@link #setBackgroundDrawable(Drawable)}).
      *
      * @param color The background color
      */
@@ -4242,10 +4483,43 @@
         return result;
     }
 
-    private void finishGlows() {
-        if (mEdgeGlowTop != null) {
-            mEdgeGlowTop.finish();
-            mEdgeGlowBottom.finish();
+    /**
+     * Sets up this AbsListView to use a remote views adapter which connects to a RemoteViewsService
+     * through the specified intent.
+     * @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
+     */
+    public void setRemoteViewsAdapter(Intent intent) {
+        // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
+        // service handling the specified intent.
+        if (mRemoteAdapter != null) {
+            Intent.FilterComparison fcNew = new Intent.FilterComparison(intent);
+            Intent.FilterComparison fcOld = new Intent.FilterComparison(
+                    mRemoteAdapter.getRemoteViewsServiceIntent());
+            if (fcNew.equals(fcOld)) {
+                return;
+            }
+        }
+
+        // Otherwise, create a new RemoteViewsAdapter for binding
+        mRemoteAdapter = new RemoteViewsAdapter(getContext(), intent, this);
+    }
+
+    /**
+     * Called back when the adapter connects to the RemoteViewsService.
+     */
+    public void onRemoteAdapterConnected() {
+        if (mRemoteAdapter != mAdapter) {
+            setAdapter(mRemoteAdapter);
+        }
+    }
+
+    /**
+     * Called back when the adapter disconnects from the RemoteViewsService.
+     */
+    public void onRemoteAdapterDisconnected() {
+        if (mRemoteAdapter == mAdapter) {
+            mRemoteAdapter = null;
+            setAdapter(null);
         }
     }
 
@@ -4265,6 +4539,75 @@
     }
 
     /**
+     * A MultiChoiceModeListener receives events for {@link AbsListView#CHOICE_MODE_MULTIPLE_MODAL}.
+     * It acts as the {@link ActionMode.Callback} for the selection mode and also receives
+     * {@link #onItemCheckedStateChanged(ActionMode, int, long, boolean)} events when the user
+     * selects and deselects list items.
+     */
+    public interface MultiChoiceModeListener extends ActionMode.Callback {
+        /**
+         * Called when an item is checked or unchecked during selection mode.
+         *
+         * @param mode The {@link ActionMode} providing the selection mode
+         * @param position Adapter position of the item that was checked or unchecked
+         * @param id Adapter ID of the item that was checked or unchecked
+         * @param checked <code>true</code> if the item is now checked, <code>false</code>
+         *                if the item is now unchecked.
+         */
+        public void onItemCheckedStateChanged(ActionMode mode,
+                int position, long id, boolean checked);
+    }
+
+    class MultiChoiceModeWrapper implements MultiChoiceModeListener {
+        private MultiChoiceModeListener mWrapped;
+
+        public void setWrapped(MultiChoiceModeListener wrapped) {
+            mWrapped = wrapped;
+        }
+
+        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+            if (mWrapped.onCreateActionMode(mode, menu)) {
+                // Initialize checked graphic state?
+                setLongClickable(false);
+                return true;
+            }
+            return false;
+        }
+
+        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+            return mWrapped.onPrepareActionMode(mode, menu);
+        }
+
+        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            return mWrapped.onActionItemClicked(mode, item);
+        }
+
+        public void onDestroyActionMode(ActionMode mode) {
+            mWrapped.onDestroyActionMode(mode);
+            mChoiceActionMode = null;
+
+            // Ending selection mode means deselecting everything.
+            clearChoices();
+
+            mDataChanged = true;
+            rememberSyncState();
+            requestLayout();
+
+            setLongClickable(true);
+        }
+
+        public void onItemCheckedStateChanged(ActionMode mode,
+                int position, long id, boolean checked) {
+            mWrapped.onItemCheckedStateChanged(mode, position, id, checked);
+
+            // If there are no items selected we no longer need the selection mode.
+            if (getCheckedItemCount() == 0) {
+                mode.finish();
+            }
+        }
+    }
+
+    /**
      * AbsListView extends LayoutParams to provide a place to hold the view type.
      */
     public static class LayoutParams extends ViewGroup.LayoutParams {
@@ -4298,6 +4641,13 @@
         @ViewDebug.ExportedProperty(category = "list")
         boolean forceAdd;
 
+        /**
+         * The position the view was removed from when pulled out of the
+         * scrap heap.
+         * @hide
+         */
+        int scrappedFromPosition;
+
         public LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
         }
@@ -4477,23 +4827,12 @@
          * @return A view from the ScrapViews collection. These are unordered.
          */
         View getScrapView(int position) {
-            ArrayList<View> scrapViews;
             if (mViewTypeCount == 1) {
-                scrapViews = mCurrentScrap;
-                int size = scrapViews.size();
-                if (size > 0) {
-                    return scrapViews.remove(size - 1);
-                } else {
-                    return null;
-                }
+                return retrieveFromScrap(mCurrentScrap, position);
             } else {
                 int whichScrap = mAdapter.getItemViewType(position);
                 if (whichScrap >= 0 && whichScrap < mScrapViews.length) {
-                    scrapViews = mScrapViews[whichScrap];
-                    int size = scrapViews.size();
-                    if (size > 0) {
-                        return scrapViews.remove(size - 1);
-                    }
+                    return retrieveFromScrap(mScrapViews[whichScrap], position);
                 }
             }
             return null;
@@ -4504,7 +4843,7 @@
          *
          * @param scrap The view to add
          */
-        void addScrapView(View scrap) {
+        void addScrapView(View scrap, int position) {
             AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
             if (lp == null) {
                 return;
@@ -4520,6 +4859,8 @@
                 return;
             }
 
+            lp.scrappedFromPosition = position;
+
             if (mViewTypeCount == 1) {
                 scrap.dispatchStartTemporaryDetach();
                 mCurrentScrap.add(scrap);
@@ -4546,7 +4887,9 @@
             for (int i = count - 1; i >= 0; i--) {
                 final View victim = activeViews[i];
                 if (victim != null) {
-                    int whichScrap = ((AbsListView.LayoutParams) victim.getLayoutParams()).viewType;
+                    final AbsListView.LayoutParams lp
+                            = (AbsListView.LayoutParams) victim.getLayoutParams();
+                    int whichScrap = lp.viewType;
 
                     activeViews[i] = null;
 
@@ -4562,6 +4905,7 @@
                         scrapViews = mScrapViews[whichScrap];
                     }
                     victim.dispatchStartTemporaryDetach();
+                    lp.scrappedFromPosition = mFirstActivePosition + i;
                     scrapViews.add(victim);
 
                     if (hasListener) {
@@ -4632,7 +4976,7 @@
                     final ArrayList<View> scrap = mScrapViews[i];
                     final int scrapCount = scrap.size();
                     for (int j = 0; j < scrapCount; j++) {
-                        scrap.get(i).setDrawingCacheBackgroundColor(color);
+                        scrap.get(j).setDrawingCacheBackgroundColor(color);
                     }
                 }
             }
@@ -4647,4 +4991,22 @@
             }
         }
     }
+
+    static View retrieveFromScrap(ArrayList<View> scrapViews, int position) {
+        int size = scrapViews.size();
+        if (size > 0) {
+            // See if we still have a view for this position.
+            for (int i=0; i<size; i++) {
+                View view = scrapViews.get(i);
+                if (((AbsListView.LayoutParams)view.getLayoutParams())
+                        .scrappedFromPosition == position) {
+                    scrapViews.remove(i);
+                    return view;
+                }
+            }
+            return scrapViews.remove(size - 1);
+        } else {
+            return null;
+        }
+    }
 }
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index d6dd872..dd71b3f 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -154,6 +154,12 @@
     }
 
     @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+        if (mThumb != null) mThumb.jumpToCurrentState();
+    }
+
+    @Override
     protected void drawableStateChanged() {
         super.drawableStateChanged();
         
diff --git a/core/java/android/widget/Adapter.java b/core/java/android/widget/Adapter.java
index f2b3e2a..9b6c5a4 100644
--- a/core/java/android/widget/Adapter.java
+++ b/core/java/android/widget/Adapter.java
@@ -72,7 +72,7 @@
     long getItemId(int position);
     
     /**
-     * Indicated whether the item ids are stable across changes to the
+     * Indicates whether the item ids are stable across changes to the
      * underlying data.
      * 
      * @return True if the same id always refers to the same object.
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 8fcc2e8..f16efbd 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.database.DataSetObserver;
-import android.os.Handler;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.util.AttributeSet;
@@ -172,7 +171,7 @@
     int mItemCount;
 
     /**
-     * The number of items in the adapter before a data changed event occured.
+     * The number of items in the adapter before a data changed event occurred.
      */
     int mOldItemCount;
 
@@ -228,7 +227,6 @@
         super(context, attrs, defStyle);
     }
 
-
     /**
      * Interface definition for a callback to be invoked when an item in this
      * AdapterView has been clicked.
@@ -558,7 +556,7 @@
     /**
      * @return The number of items owned by the Adapter associated with this
      *         AdapterView. (This is the number of data items, which may be
-     *         larger than the number of visible view.)
+     *         larger than the number of visible views.)
      */
     @ViewDebug.CapturedViewProperty
     public int getCount() {
@@ -629,6 +627,7 @@
     /**
      * Sets the view to show if the adapter is empty
      */
+    @android.view.RemotableViewMethod
     public void setEmptyView(View emptyView) {
         mEmptyView = emptyView;
 
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
new file mode 100644
index 0000000..1f98c88
--- /dev/null
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -0,0 +1,968 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import android.animation.AnimatorInflater;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+
+/**
+ * Base class for a {@link AdapterView} that will perform animations
+ * when switching between its views.
+ *
+ * @attr ref android.R.styleable#AdapterViewAnimator_inAnimation
+ * @attr ref android.R.styleable#AdapterViewAnimator_outAnimation
+ * @attr ref android.R.styleable#AdapterViewAnimator_animateFirstView
+ * @attr ref android.R.styleable#AdapterViewAnimator_loopViews
+ */
+public abstract class AdapterViewAnimator extends AdapterView<Adapter>
+        implements RemoteViewsAdapter.RemoteAdapterConnectionCallback {
+    private static final String TAG = "RemoteViewAnimator";
+
+    /**
+     * The index of the current child, which appears anywhere from the beginning
+     * to the end of the current set of children, as specified by {@link #mActiveOffset}
+     */
+    int mWhichChild = 0;
+
+    /**
+     * Whether or not the first view(s) should be animated in
+     */
+    boolean mAnimateFirstTime = true;
+
+    /**
+     *  Represents where the in the current window of
+     *  views the current <code>mDisplayedChild</code> sits
+     */
+    int mActiveOffset = 0;
+
+    /**
+     * The number of views that the {@link AdapterViewAnimator} keeps as children at any
+     * given time (not counting views that are pending removal, see {@link #mPreviousViews}).
+     */
+    int mMaxNumActiveViews = 1;
+
+    /**
+     * Map of the children of the {@link AdapterViewAnimator}.
+     */
+    HashMap<Integer, ViewAndIndex> mViewsMap = new HashMap<Integer, ViewAndIndex>();
+
+    /**
+     * List of views pending removal from the {@link AdapterViewAnimator}
+     */
+    ArrayList<Integer> mPreviousViews;
+
+    /**
+     * The index, relative to the adapter, of the beginning of the window of views
+     */
+    int mCurrentWindowStart = 0;
+
+    /**
+     * The index, relative to the adapter, of the end of the window of views
+     */
+    int mCurrentWindowEnd = -1;
+
+    /**
+     * The same as {@link #mCurrentWindowStart}, except when the we have bounded
+     * {@link #mCurrentWindowStart} to be non-negative
+     */
+    int mCurrentWindowStartUnbounded = 0;
+
+    /**
+     * Handler to post events to the main thread
+     */
+    Handler mMainQueue;
+
+    /**
+     * Listens for data changes from the adapter
+     */
+    AdapterDataSetObserver mDataSetObserver;
+
+    /**
+     * The {@link Adapter} for this {@link AdapterViewAnimator}
+     */
+    Adapter mAdapter;
+
+    /**
+     * The {@link RemoteViewsAdapter} for this {@link AdapterViewAnimator}
+     */
+    RemoteViewsAdapter mRemoteViewsAdapter;
+
+    /**
+     * Specifies whether this is the first time the animator is showing views
+     */
+    boolean mFirstTime = true;
+
+    /**
+     * Specifies if the animator should wrap from 0 to the end and vice versa
+     * or have hard boundaries at the beginning and end
+     */
+    boolean mLoopViews = true;
+
+    /**
+     * The width and height of some child, used as a size reference in-case our
+     * dimensions are unspecified by the parent.
+     */
+    int mReferenceChildWidth = -1;
+    int mReferenceChildHeight = -1;
+
+    /**
+     * In and out animations.
+     */
+    ObjectAnimator mInAnimation;
+    ObjectAnimator mOutAnimation;
+
+    /**
+     * Current touch state.
+     */
+    private int mTouchMode = TOUCH_MODE_NONE;
+
+    /**
+     * Private touch states.
+     */
+    static final int TOUCH_MODE_NONE = 0;
+    static final int TOUCH_MODE_DOWN_IN_CURRENT_VIEW = 1;
+    static final int TOUCH_MODE_HANDLED = 2;
+
+    private Runnable mPendingCheckForTap;
+
+    private static final int DEFAULT_ANIMATION_DURATION = 200;
+
+    public AdapterViewAnimator(Context context) {
+        super(context);
+        initViewAnimator();
+    }
+
+    public AdapterViewAnimator(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.AdapterViewAnimator);
+        int resource = a.getResourceId(
+                com.android.internal.R.styleable.AdapterViewAnimator_inAnimation, 0);
+        if (resource > 0) {
+            setInAnimation(context, resource);
+        } else {
+            setInAnimation(getDefaultInAnimation());
+        }
+
+        resource = a.getResourceId(com.android.internal.R.styleable.AdapterViewAnimator_outAnimation, 0);
+        if (resource > 0) {
+            setOutAnimation(context, resource);
+        } else {
+            setOutAnimation(getDefaultOutAnimation());
+        }
+
+        boolean flag = a.getBoolean(
+                com.android.internal.R.styleable.AdapterViewAnimator_animateFirstView, true);
+        setAnimateFirstView(flag);
+
+        mLoopViews = a.getBoolean(
+                com.android.internal.R.styleable.AdapterViewAnimator_loopViews, false);
+
+        a.recycle();
+
+        initViewAnimator();
+    }
+
+    /**
+     * Initialize this {@link AdapterViewAnimator}
+     */
+    private void initViewAnimator() {
+        mMainQueue = new Handler(Looper.myLooper());
+        mPreviousViews = new ArrayList<Integer>();
+    }
+
+    class ViewAndIndex {
+        ViewAndIndex(View v, int i) {
+            view = v;
+            index = i;
+        }
+        View view;
+        int index;
+    }
+
+    /**
+     * This method is used by subclasses to configure the animator to display the
+     * desired number of views, and specify the offset
+     *
+     * @param numVisibleViews The number of views the animator keeps in the {@link ViewGroup}
+     * @param activeOffset This parameter specifies where the current index ({@link #mWhichChild})
+     *        sits within the window. For example if activeOffset is 1, and numVisibleViews is 3,
+     *        and {@link #setDisplayedChild(int)} is called with 10, then the effective window will
+     *        be the indexes 9, 10, and 11. In the same example, if activeOffset were 0, then the
+     *        window would instead contain indexes 10, 11 and 12.
+     * @param shouldLoop If the animator is show view 0, and setPrevious() is called, do we
+     *        we loop back to the end, or do we do nothing
+     */
+     void configureViewAnimator(int numVisibleViews, int activeOffset) {
+        if (activeOffset > numVisibleViews - 1) {
+            // Throw an exception here.
+        }
+        mMaxNumActiveViews = numVisibleViews;
+        mActiveOffset = activeOffset;
+        mPreviousViews.clear();
+        mViewsMap.clear();
+        removeAllViewsInLayout();
+        mCurrentWindowStart = 0;
+        mCurrentWindowEnd = -1;
+    }
+
+    /**
+     * This class should be overridden by subclasses to customize view transitions within
+     * the set of visible views
+     *
+     * @param fromIndex The relative index within the window that the view was in, -1 if it wasn't
+     *        in the window
+     * @param toIndex The relative index within the window that the view is going to, -1 if it is
+     *        being removed
+     * @param view The view that is being animated
+     */
+    void animateViewForTransition(int fromIndex, int toIndex, View view) {
+        if (fromIndex == -1) {
+            mInAnimation.setTarget(view);
+            mInAnimation.start();
+        } else if (toIndex == -1) {
+            mOutAnimation.setTarget(view);
+            mOutAnimation.start();
+        }
+    }
+
+    ObjectAnimator getDefaultInAnimation() {
+        ObjectAnimator anim = ObjectAnimator.ofFloat(null, "alpha", 0.0f, 1.0f);
+        anim.setDuration(DEFAULT_ANIMATION_DURATION);
+        return anim;
+    }
+
+    ObjectAnimator getDefaultOutAnimation() {
+        ObjectAnimator anim = ObjectAnimator.ofFloat(null, "alpha", 1.0f, 0.0f);
+        anim.setDuration(DEFAULT_ANIMATION_DURATION);
+        return anim;
+    }
+
+    /**
+     * Sets which child view will be displayed.
+     *
+     * @param whichChild the index of the child view to display
+     */
+    public void setDisplayedChild(int whichChild) {
+        if (mAdapter != null) {
+            mWhichChild = whichChild;
+            if (whichChild >= getWindowSize()) {
+                mWhichChild = mLoopViews ? 0 : getWindowSize() - 1;
+            } else if (whichChild < 0) {
+                mWhichChild = mLoopViews ? getWindowSize() - 1 : 0;
+            }
+
+            boolean hasFocus = getFocusedChild() != null;
+            // This will clear old focus if we had it
+            showOnly(mWhichChild);
+            if (hasFocus) {
+                // Try to retake focus if we had it
+                requestFocus(FOCUS_FORWARD);
+            }
+        }
+    }
+
+    /**
+     * To be overridden by subclasses. This method applies a view / index specific
+     * transform to the child view.
+     *
+     * @param child
+     * @param relativeIndex
+     */
+    void applyTransformForChildAtIndex(View child, int relativeIndex) {
+    }
+
+    /**
+     * Returns the index of the currently displayed child view.
+     */
+    public int getDisplayedChild() {
+        return mWhichChild;
+    }
+
+    /**
+     * Manually shows the next child.
+     */
+    public void showNext() {
+        setDisplayedChild(mWhichChild + 1);
+    }
+
+    /**
+     * Manually shows the previous child.
+     */
+    public void showPrevious() {
+        setDisplayedChild(mWhichChild - 1);
+    }
+
+    /**
+     * Shows only the specified child. The other displays Views exit the screen,
+     * optionally with the with the {@link #getOutAnimation() out animation} and
+     * the specified child enters the screen, optionally with the
+     * {@link #getInAnimation() in animation}.
+     *
+     * @param childIndex The index of the child to be shown.
+     * @param animate Whether or not to use the in and out animations, defaults
+     *            to true.
+     */
+    void showOnly(int childIndex, boolean animate) {
+        showOnly(childIndex, animate, false);
+    }
+
+    int modulo(int pos, int size) {
+        if (size > 0) {
+            return (size + (pos % size)) % size;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Get the view at this index relative to the current window's start
+     *
+     * @param relativeIndex Position relative to the current window's start
+     * @return View at this index, null if the index is outside the bounds
+     */
+    View getViewAtRelativeIndex(int relativeIndex) {
+        if (relativeIndex >= 0 && relativeIndex <= getNumActiveViews() - 1 && mAdapter != null) {
+            int i = modulo(mCurrentWindowStartUnbounded + relativeIndex, getWindowSize());
+            if (mViewsMap.get(i) != null) {
+                return mViewsMap.get(i).view;
+            }
+        }
+        return null;
+    }
+
+    int getNumActiveViews() {
+        if (mAdapter != null) {
+            return Math.min(mAdapter.getCount() + 1, mMaxNumActiveViews);
+        } else {
+            return mMaxNumActiveViews;
+        }
+    }
+
+    int getWindowSize() {
+        if (mAdapter != null) {
+            int adapterCount = mAdapter.getCount();
+            if (adapterCount <= getNumActiveViews() && mLoopViews) {
+                return adapterCount*mMaxNumActiveViews;
+            } else {
+                return adapterCount;
+            }
+        } else {
+            return 0;
+        }
+    }
+
+    LayoutParams createOrReuseLayoutParams(View v) {
+        final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
+        if (currentLp instanceof ViewGroup.LayoutParams) {
+            LayoutParams lp = (LayoutParams) currentLp;
+            return lp;
+        }
+        return new ViewGroup.LayoutParams(0, 0);
+    }
+
+    void refreshChildren() {
+        if (mAdapter == null) return;
+        for (int i = mCurrentWindowStart; i <= mCurrentWindowEnd; i++) {
+            int index = modulo(i, getWindowSize());
+
+            int adapterCount = mAdapter.getCount();
+            // get the fresh child from the adapter
+            View updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this);
+
+            if (mViewsMap.containsKey(index)) {
+                FrameLayout fl = (FrameLayout) mViewsMap.get(index).view;
+                // flush out the old child
+                fl.removeAllViewsInLayout();
+                // add the new child to the frame, if it exists
+                if (updatedChild != null) {
+                    fl.addView(updatedChild);
+                }
+            }
+        }
+    }
+
+    /**
+     * This method can be overridden so that subclasses can provide a custom frame in which their
+     * children can live. For example, StackView adds padding to its childrens' frames so as to
+     * accomodate for the highlight effect.
+     *
+     * @return The FrameLayout into which children can be placed.
+     */
+    FrameLayout getFrameForChild() {
+        return new FrameLayout(mContext);
+    }
+
+    void showOnly(int childIndex, boolean animate, boolean onLayout) {
+        if (mAdapter == null) return;
+        final int adapterCount = mAdapter.getCount();
+        if (adapterCount == 0) return;
+
+        for (int i = 0; i < mPreviousViews.size(); i++) {
+            View viewToRemove = mViewsMap.get(mPreviousViews.get(i)).view;
+            mViewsMap.remove(mPreviousViews.get(i));
+            viewToRemove.clearAnimation();
+            if (viewToRemove instanceof ViewGroup) {
+                ViewGroup vg = (ViewGroup) viewToRemove;
+                vg.removeAllViewsInLayout();
+            }
+            // applyTransformForChildAtIndex here just allows for any cleanup
+            // associated with this view that may need to be done by a subclass
+            applyTransformForChildAtIndex(viewToRemove, -1);
+
+            removeViewInLayout(viewToRemove);
+        }
+        mPreviousViews.clear();
+        int newWindowStartUnbounded = childIndex - mActiveOffset;
+        int newWindowEndUnbounded = newWindowStartUnbounded + getNumActiveViews() - 1;
+        int newWindowStart = Math.max(0, newWindowStartUnbounded);
+        int newWindowEnd = Math.min(adapterCount - 1, newWindowEndUnbounded);
+
+        if (mLoopViews) {
+            newWindowStart = newWindowStartUnbounded;
+            newWindowEnd = newWindowEndUnbounded;
+        }
+        int rangeStart = modulo(newWindowStart, getWindowSize());
+        int rangeEnd = modulo(newWindowEnd, getWindowSize());
+
+        boolean wrap = false;
+        if (rangeStart > rangeEnd) {
+            wrap = true;
+        }
+
+        // This section clears out any items that are in our active views list
+        // but are outside the effective bounds of our window (this is becomes an issue
+        // at the extremities of the list, eg. where newWindowStartUnbounded < 0 or
+        // newWindowEndUnbounded > mAdapter.getCount() - 1
+        for (Integer index : mViewsMap.keySet()) {
+            boolean remove = false;
+            if (!wrap && (index < rangeStart || index > rangeEnd)) {
+                remove = true;
+            } else if (wrap && (index > rangeEnd && index < rangeStart)) {
+                remove = true;
+            }
+
+            if (remove) {
+                View previousView = mViewsMap.get(index).view;
+                int oldRelativeIndex = mViewsMap.get(index).index;
+
+                mPreviousViews.add(index);
+                animateViewForTransition(oldRelativeIndex, -1, previousView);
+            }
+        }
+
+        // If the window has changed
+        if (!(newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd &&
+              newWindowStartUnbounded == mCurrentWindowStartUnbounded)) {
+            // Run through the indices in the new range
+            for (int i = newWindowStart; i <= newWindowEnd; i++) {
+
+                int index = modulo(i, getWindowSize());
+                int oldRelativeIndex;
+                if (mViewsMap.containsKey(index)) {
+                    oldRelativeIndex = mViewsMap.get(index).index;
+                } else {
+                    oldRelativeIndex = -1;
+                }
+                int newRelativeIndex = i - newWindowStartUnbounded;
+
+                // If this item is in the current window, great, we just need to apply
+                // the transform for it's new relative position in the window, and animate
+                // between it's current and new relative positions
+                boolean inOldRange = mViewsMap.containsKey(index) && !mPreviousViews.contains(index);
+
+                if (inOldRange) {
+                    View view = mViewsMap.get(index).view;
+                    mViewsMap.get(index).index = newRelativeIndex;
+                    applyTransformForChildAtIndex(view, newRelativeIndex);
+                    animateViewForTransition(oldRelativeIndex, newRelativeIndex, view);
+
+                // Otherwise this view is new to the window
+                } else {
+                    // Get the new view from the adapter, add it and apply any transform / animation
+                    View newView = mAdapter.getView(modulo(i, adapterCount), null, this);
+
+                    // We wrap the new view in a FrameLayout so as to respect the contract
+                    // with the adapter, that is, that we don't modify this view directly
+                    FrameLayout fl = getFrameForChild();
+
+                    // If the view from the adapter is null, we still keep an empty frame in place
+                    if (newView != null) {
+                       fl.addView(newView);
+                    }
+                    mViewsMap.put(index, new ViewAndIndex(fl, newRelativeIndex));
+                    addChild(fl);
+                    applyTransformForChildAtIndex(fl, newRelativeIndex);
+                    animateViewForTransition(-1, newRelativeIndex, fl);
+                }
+                mViewsMap.get(index).view.bringToFront();
+            }
+            mCurrentWindowStart = newWindowStart;
+            mCurrentWindowEnd = newWindowEnd;
+            mCurrentWindowStartUnbounded = newWindowStartUnbounded;
+        }
+
+        mFirstTime = false;
+        if (!onLayout) {
+            requestLayout();
+            invalidate();
+        } else {
+            // If the Adapter tries to layout the current view when we get it using getView
+            // above the layout will end up being ignored since we are currently laying out, so
+            // we post a delayed requestLayout and invalidate
+            mMainQueue.post(new Runnable() {
+                public void run() {
+                    requestLayout();
+                    invalidate();
+                }
+            });
+        }
+    }
+
+    private void addChild(View child) {
+        addViewInLayout(child, -1, createOrReuseLayoutParams(child));
+
+        // This code is used to obtain a reference width and height of a child in case we need
+        // to decide our own size. TODO: Do we want to update the size of the child that we're
+        // using for reference size? If so, when?
+        if (mReferenceChildWidth == -1 || mReferenceChildHeight == -1) {
+            int measureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+            child.measure(measureSpec, measureSpec);
+            mReferenceChildWidth = child.getMeasuredWidth();
+            mReferenceChildHeight = child.getMeasuredHeight();
+        }
+    }
+
+    void showTapFeedback(View v) {
+        v.setPressed(true);
+    }
+
+    void hideTapFeedback(View v) {
+        v.setPressed(false);
+    }
+
+    void cancelHandleClick() {
+        View v = getCurrentView();
+        if (v != null) {
+            hideTapFeedback(v);
+        }
+        mTouchMode = TOUCH_MODE_NONE;
+    }
+
+    final class CheckForTap implements Runnable {
+        public void run() {
+            if (mTouchMode == TOUCH_MODE_DOWN_IN_CURRENT_VIEW) {
+                View v = getCurrentView();
+                showTapFeedback(v);
+            }
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        int action = ev.getAction();
+        boolean handled = false;
+        switch (action) {
+            case MotionEvent.ACTION_DOWN: {
+                View v = getCurrentView();
+                if (v != null) {
+                    if (isTransformedTouchPointInView(ev.getX(), ev.getY(), v, null)) {
+                        if (mPendingCheckForTap == null) {
+                            mPendingCheckForTap = new CheckForTap();
+                        }
+                        mTouchMode = TOUCH_MODE_DOWN_IN_CURRENT_VIEW;
+                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
+                    }
+                }
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: break;
+            case MotionEvent.ACTION_POINTER_UP: break;
+            case MotionEvent.ACTION_UP: {
+                if (mTouchMode == TOUCH_MODE_DOWN_IN_CURRENT_VIEW) {
+                    final View v = getCurrentView();
+                    if (v != null) {
+                        if (isTransformedTouchPointInView(ev.getX(), ev.getY(), v, null)) {
+                            final Handler handler = getHandler();
+                            if (handler != null) {
+                                handler.removeCallbacks(mPendingCheckForTap);
+                            }
+                            showTapFeedback(v);
+                            postDelayed(new Runnable() {
+                                public void run() {
+                                    hideTapFeedback(v);
+                                    post(new Runnable() {
+                                        public void run() {
+                                            performItemClick(v, 0, 0);
+                                        }
+                                    });
+                                }
+                            }, ViewConfiguration.getPressedStateDuration());
+                            handled = true;
+                        }
+                    }
+                }
+                mTouchMode = TOUCH_MODE_NONE;
+                break;
+            }
+            case MotionEvent.ACTION_CANCEL: {
+                View v = getCurrentView();
+                if (v != null) {
+                    hideTapFeedback(v);
+                }
+                mTouchMode = TOUCH_MODE_NONE;
+            }
+        }
+        return handled;
+    }
+
+    private void measureChildren() {
+        final int count = getChildCount();
+        final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight;
+        final int childHeight = mMeasuredHeight - mPaddingTop - mPaddingBottom;
+
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+        final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+
+        boolean haveChildRefSize = (mReferenceChildWidth != -1 && mReferenceChildHeight != -1);
+
+        // We need to deal with the case where our parent hasn't told us how
+        // big we should be. In this case we try to use the desired size of the first
+        // child added.
+        if (heightSpecMode == MeasureSpec.UNSPECIFIED) {
+            heightSpecSize = haveChildRefSize ? mReferenceChildHeight + mPaddingTop +
+                    mPaddingBottom : 0;
+        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+            heightSpecSize = haveChildRefSize ? Math.min(mReferenceChildHeight + mPaddingTop +
+                    mPaddingBottom, heightSpecSize) : 0;
+        }
+
+        if (widthSpecMode == MeasureSpec.UNSPECIFIED) {
+            widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft +
+                    mPaddingRight : 0;
+        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+            widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft +
+                    mPaddingRight, widthSpecSize) : 0;
+        }
+
+        setMeasuredDimension(widthSpecSize, heightSpecSize);
+        measureChildren();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        boolean dataChanged = mDataChanged;
+        if (dataChanged) {
+            handleDataChanged();
+
+            // if the data changes, mWhichChild might be out of the bounds of the adapter
+            // in this case, we reset mWhichChild to the beginning
+            if (mWhichChild >= mAdapter.getCount()) {
+                mWhichChild = 0;
+
+                showOnly(mWhichChild, true, true);
+            }
+            refreshChildren();
+        }
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+
+            int childRight = mPaddingLeft + child.getMeasuredWidth();
+            int childBottom = mPaddingTop + child.getMeasuredHeight();
+
+            child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
+        }
+        mDataChanged = false;
+    }
+
+    static class SavedState extends BaseSavedState {
+        int whichChild;
+
+        /**
+         * Constructor called from {@link AdapterViewAnimator#onSaveInstanceState()}
+         */
+        SavedState(Parcelable superState, int whichChild) {
+            super(superState);
+            this.whichChild = whichChild;
+        }
+
+        /**
+         * Constructor called from {@link #CREATOR}
+         */
+        private SavedState(Parcel in) {
+            super(in);
+            this.whichChild = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeInt(this.whichChild);
+        }
+
+        @Override
+        public String toString() {
+            return "AdapterViewAnimator.SavedState{ whichChild = " + this.whichChild + " }";
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR
+                = new Parcelable.Creator<SavedState>() {
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+        return new SavedState(superState, mWhichChild);
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(ss.getSuperState());
+
+        // Here we set mWhichChild in addition to setDisplayedChild
+        // We do the former in case mAdapter is null, and hence setDisplayedChild won't
+        // set mWhichChild
+        mWhichChild = ss.whichChild;
+        setDisplayedChild(mWhichChild);
+    }
+
+    /**
+     * Shows only the specified child. The other displays Views exit the screen
+     * with the {@link #getOutAnimation() out animation} and the specified child
+     * enters the screen with the {@link #getInAnimation() in animation}.
+     *
+     * @param childIndex The index of the child to be shown.
+     */
+    void showOnly(int childIndex) {
+        final boolean animate = (!mFirstTime || mAnimateFirstTime);
+        showOnly(childIndex, animate);
+    }
+
+    /**
+     * Returns the View corresponding to the currently displayed child.
+     *
+     * @return The View currently displayed.
+     *
+     * @see #getDisplayedChild()
+     */
+    public View getCurrentView() {
+        return getViewAtRelativeIndex(mActiveOffset);
+    }
+
+    /**
+     * Returns the current animation used to animate a View that enters the screen.
+     *
+     * @return An Animation or null if none is set.
+     *
+     * @see #setInAnimation(android.animation.ObjectAnimator)
+     * @see #setInAnimation(android.content.Context, int)
+     */
+    public ObjectAnimator getInAnimation() {
+        return mInAnimation;
+    }
+
+    /**
+     * Specifies the animation used to animate a View that enters the screen.
+     *
+     * @param inAnimation The animation started when a View enters the screen.
+     *
+     * @see #getInAnimation()
+     * @see #setInAnimation(android.content.Context, int)
+     */
+    public void setInAnimation(ObjectAnimator inAnimation) {
+        mInAnimation = inAnimation;
+    }
+
+    /**
+     * Returns the current animation used to animate a View that exits the screen.
+     *
+     * @return An Animation or null if none is set.
+     *
+     * @see #setOutAnimation(android.animation.ObjectAnimator)
+     * @see #setOutAnimation(android.content.Context, int)
+     */
+    public ObjectAnimator getOutAnimation() {
+        return mOutAnimation;
+    }
+
+    /**
+     * Specifies the animation used to animate a View that exit the screen.
+     *
+     * @param outAnimation The animation started when a View exit the screen.
+     *
+     * @see #getOutAnimation()
+     * @see #setOutAnimation(android.content.Context, int)
+     */
+    public void setOutAnimation(ObjectAnimator outAnimation) {
+        mOutAnimation = outAnimation;
+    }
+
+    /**
+     * Specifies the animation used to animate a View that enters the screen.
+     *
+     * @param context The application's environment.
+     * @param resourceID The resource id of the animation.
+     *
+     * @see #getInAnimation()
+     * @see #setInAnimation(android.animation.ObjectAnimator)
+     */
+    public void setInAnimation(Context context, int resourceID) {
+        setInAnimation((ObjectAnimator) AnimatorInflater.loadAnimator(context, resourceID));
+    }
+
+    /**
+     * Specifies the animation used to animate a View that exit the screen.
+     *
+     * @param context The application's environment.
+     * @param resourceID The resource id of the animation.
+     *
+     * @see #getOutAnimation()
+     * @see #setOutAnimation(android.animation.ObjectAnimator)
+     */
+    public void setOutAnimation(Context context, int resourceID) {
+        setOutAnimation((ObjectAnimator) AnimatorInflater.loadAnimator(context, resourceID));
+    }
+
+    /**
+     * Indicates whether the current View should be animated the first time
+     * the ViewAnimation is displayed.
+     *
+     * @param animate True to animate the current View the first time it is displayed,
+     *                false otherwise.
+     */
+    public void setAnimateFirstView(boolean animate) {
+        mAnimateFirstTime = animate;
+    }
+
+    @Override
+    public int getBaseline() {
+        return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline();
+    }
+
+    @Override
+    public Adapter getAdapter() {
+        return mAdapter;
+    }
+
+    @Override
+    public void setAdapter(Adapter adapter) {
+        if (mAdapter != null && mDataSetObserver != null) {
+            mAdapter.unregisterDataSetObserver(mDataSetObserver);
+        }
+
+        mAdapter = adapter;
+        checkFocus();
+
+        if (mAdapter != null) {
+            mDataSetObserver = new AdapterDataSetObserver();
+            mAdapter.registerDataSetObserver(mDataSetObserver);
+        }
+        setFocusable(true);
+    }
+
+    /**
+     * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a
+     * RemoteViewsService through the specified intent.
+     *
+     * @param intent the intent used to identify the RemoteViewsService for the adapter to
+     *        connect to.
+     */
+    @android.view.RemotableViewMethod
+    public void setRemoteViewsAdapter(Intent intent) {
+        // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing
+        // service handling the specified intent.
+        if (mRemoteViewsAdapter != null) {
+            Intent.FilterComparison fcNew = new Intent.FilterComparison(intent);
+            Intent.FilterComparison fcOld = new Intent.FilterComparison(
+                    mRemoteViewsAdapter.getRemoteViewsServiceIntent());
+            if (fcNew.equals(fcOld)) {
+                return;
+            }
+        }
+
+        // Otherwise, create a new RemoteViewsAdapter for binding
+        mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this);
+    }
+
+    @Override
+    public void setSelection(int position) {
+        setDisplayedChild(position);
+    }
+
+    @Override
+    public View getSelectedView() {
+        return getViewAtRelativeIndex(mActiveOffset);
+    }
+
+    /**
+     * Called back when the adapter connects to the RemoteViewsService.
+     */
+    public void onRemoteAdapterConnected() {
+        if (mRemoteViewsAdapter != mAdapter) {
+            setAdapter(mRemoteViewsAdapter);
+        }
+    }
+
+    /**
+     * Called back when the adapter disconnects from the RemoteViewsService.
+     */
+    public void onRemoteAdapterDisconnected() {
+        if (mRemoteViewsAdapter != mAdapter) {
+            mRemoteViewsAdapter = null;
+            setAdapter(mRemoteViewsAdapter);
+        }
+    }
+}
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
new file mode 100644
index 0000000..b09ade7
--- /dev/null
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.animation.ObjectAnimator;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.TypedArray;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.RemotableViewMethod;
+import android.view.animation.AlphaAnimation;
+import android.widget.RemoteViews.RemoteView;
+
+/**
+ * Simple {@link ViewAnimator} that will animate between two or more views
+ * that have been added to it.  Only one child is shown at a time.  If
+ * requested, can automatically flip between each child at a regular interval.
+ *
+ * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
+ * @attr ref android.R.styleable#AdapterViewFlipper_autoStart
+ */
+@RemoteView
+public class AdapterViewFlipper extends AdapterViewAnimator {
+    private static final String TAG = "ViewFlipper";
+    private static final boolean LOGD = false;
+
+    private static final int DEFAULT_INTERVAL = 10000;
+
+    private int mFlipInterval = DEFAULT_INTERVAL;
+    private boolean mAutoStart = false;
+
+    private boolean mRunning = false;
+    private boolean mStarted = false;
+    private boolean mVisible = false;
+    private boolean mUserPresent = true;
+
+    public AdapterViewFlipper(Context context) {
+        super(context);
+    }
+
+    public AdapterViewFlipper(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.ViewFlipper);
+        mFlipInterval = a.getInt(
+                com.android.internal.R.styleable.ViewFlipper_flipInterval, DEFAULT_INTERVAL);
+        mAutoStart = a.getBoolean(
+                com.android.internal.R.styleable.ViewFlipper_autoStart, false);
+
+        // By default we want the flipper to loop
+        mLoopViews = a.getBoolean(
+                com.android.internal.R.styleable.AdapterViewAnimator_loopViews, true);
+
+        a.recycle();
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+                mUserPresent = false;
+                updateRunning();
+            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
+                mUserPresent = true;
+                updateRunning(false);
+            }
+        }
+    };
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        // Listen for broadcasts related to user-presence
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.addAction(Intent.ACTION_USER_PRESENT);
+        getContext().registerReceiver(mReceiver, filter);
+
+        if (mAutoStart) {
+            // Automatically start when requested
+            startFlipping();
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mVisible = false;
+
+        getContext().unregisterReceiver(mReceiver);
+        updateRunning();
+    }
+
+    @Override
+    protected void onWindowVisibilityChanged(int visibility) {
+        super.onWindowVisibilityChanged(visibility);
+        mVisible = (visibility == VISIBLE);
+        updateRunning(false);
+    }
+
+    @Override
+    public void setAdapter(Adapter adapter) {
+        super.setAdapter(adapter);
+        updateRunning();
+    }
+
+    /**
+     * How long to wait before flipping to the next view
+     *
+     * @param milliseconds
+     *            time in milliseconds
+     */
+    public void setFlipInterval(int milliseconds) {
+        mFlipInterval = milliseconds;
+    }
+
+    /**
+     * Start a timer to cycle through child views
+     */
+    public void startFlipping() {
+        mStarted = true;
+        updateRunning();
+    }
+
+    /**
+     * No more flips
+     */
+    public void stopFlipping() {
+        mStarted = false;
+        updateRunning();
+    }
+
+    /**
+    * {@inheritDoc}
+    */
+   @Override
+   @RemotableViewMethod
+   public void showNext() {
+       // if the flipper is currently flipping automatically, and showNext() is called
+       // we should we should make sure to reset the timer
+       if (mRunning) {
+           mHandler.removeMessages(FLIP_MSG);
+           Message msg = mHandler.obtainMessage(FLIP_MSG);
+           mHandler.sendMessageDelayed(msg, mFlipInterval);
+       }
+       super.showNext();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   @RemotableViewMethod
+   public void showPrevious() {
+       // if the flipper is currently flipping automatically, and showPrevious() is called
+       // we should we should make sure to reset the timer
+       if (mRunning) {
+           mHandler.removeMessages(FLIP_MSG);
+           Message msg = mHandler.obtainMessage(FLIP_MSG);
+           mHandler.sendMessageDelayed(msg, mFlipInterval);
+       }
+       super.showPrevious();
+   }
+
+    /**
+     * Internal method to start or stop dispatching flip {@link Message} based
+     * on {@link #mRunning} and {@link #mVisible} state.
+     */
+    private void updateRunning() {
+        // by default when we update running, we want the
+        // current view to animate in
+        updateRunning(true);
+    }
+
+    /**
+     * Internal method to start or stop dispatching flip {@link Message} based
+     * on {@link #mRunning} and {@link #mVisible} state.
+     *
+     * @param flipNow Determines whether or not to execute the animation now, in
+     *            addition to queuing future flips. If omitted, defaults to
+     *            true.
+     */
+    private void updateRunning(boolean flipNow) {
+        boolean running = mVisible && mStarted && mUserPresent && mAdapter != null;
+        if (running != mRunning) {
+            if (running) {
+                showOnly(mWhichChild, flipNow);
+                Message msg = mHandler.obtainMessage(FLIP_MSG);
+                mHandler.sendMessageDelayed(msg, mFlipInterval);
+            } else {
+                mHandler.removeMessages(FLIP_MSG);
+            }
+            mRunning = running;
+        }
+        if (LOGD) {
+            Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
+                    + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
+        }
+    }
+
+    /**
+     * Returns true if the child views are flipping.
+     */
+    public boolean isFlipping() {
+        return mStarted;
+    }
+
+    /**
+     * Set if this view automatically calls {@link #startFlipping()} when it
+     * becomes attached to a window.
+     */
+    public void setAutoStart(boolean autoStart) {
+        mAutoStart = autoStart;
+    }
+
+    /**
+     * Returns true if this view automatically calls {@link #startFlipping()}
+     * when it becomes attached to a window.
+     */
+    public boolean isAutoStart() {
+        return mAutoStart;
+    }
+
+    private final int FLIP_MSG = 1;
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == FLIP_MSG) {
+                if (mRunning) {
+                    showNext();
+                }
+            }
+        }
+    };
+}
diff --git a/core/java/android/widget/Adapters.java b/core/java/android/widget/Adapters.java
new file mode 100644
index 0000000..a71d4ab
--- /dev/null
+++ b/core/java/android/widget/Adapters.java
@@ -0,0 +1,1233 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.database.Cursor;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.util.AttributeSet;
+import android.util.Xml;
+import android.view.View;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * <p>This class can be used to load {@link android.widget.Adapter adapters} defined in
+ * XML resources. XML-defined adapters can be used to easily create adapters in your
+ * own application or to pass adapters to other processes.</p>
+ * 
+ * <h2>Types of adapters</h2>
+ * <p>Adapters defined using XML resources can only be one of the following supported
+ * types. Arbitrary adapters are not supported to guarantee the safety of the loaded
+ * code when adapters are loaded across packages.</p>
+ * <ul>
+ *  <li><a href="#xml-cursor-adapter">Cursor adapter</a>: a cursor adapter can be used
+ *  to display the content of a cursor, most often coming from a content provider</li>
+ * </ul>
+ * <p>The complete XML format definition of each adapter type is available below.</p>
+ * 
+ * <a name="xml-cursor-adapter"></a>
+ * <h2>Cursor adapter</h2>
+ * <p>A cursor adapter XML definition starts with the
+ * <a href="#xml-cursor-adapter-tag"><code>&lt;cursor-adapter /&gt;</code></a>
+ * tag and may contain one or more instances of the following tags:</p>
+ * <ul>
+ *  <li><a href="#xml-cursor-adapter-select-tag"><code>&lt;select /&gt;</code></a></li>
+ *  <li><a href="#xml-cursor-adapter-bind-tag"><code>&lt;bind /&gt;</code></a></li>
+ * </ul>
+ * 
+ * <a name="xml-cursor-adapter-tag"></a>
+ * <h3>&lt;cursor-adapter /&gt;</h3>
+ * <p>The <code>&lt;cursor-adapter /&gt;</code> element defines the beginning of the
+ * document and supports the following attributes:</p>
+ * <ul>
+ *  <li><code>android:layout</code>: Reference to the XML layout to be inflated for
+ *  each item of the adapter. This attribute is mandatory.</li>
+ *  <li><code>android:selection</code>: Selection expression, used when the
+ *  <code>android:uri</code> attribute is defined or when the adapter is loaded with
+ *  {@link android.widget.Adapters#loadCursorAdapter(android.content.Context, int, String, Object[])}.
+ *  This attribute is optional.</li>
+ *  <li><code>android:sortOrder</code>: Sort expression, used when the
+ *  <code>android:uri</code> attribute is defined or when the adapter is loaded with
+ *  {@link android.widget.Adapters#loadCursorAdapter(android.content.Context, int, String, Object[])}.
+ *  This attribute is optional.</li>
+ *  <li><code>android:uri</code>: URI of the content provider to query to retrieve a cursor.
+ *  Specifying this attribute is equivalent to calling
+ *  {@link android.widget.Adapters#loadCursorAdapter(android.content.Context, int, String, Object[])}.
+ *  If you call this method, the value of the XML attribute is ignored. This attribute is
+ *  optional.</li>
+ * </ul>
+ * <p>In addition, you can specify one or more instances of
+ * <a href="#xml-cursor-adapter-select-tag"><code>&lt;select /&gt;</code></a> and
+ * <a href="#xml-cursor-adapter-bind-tag"><code>&lt;bind /&gt;</code></a> tags as children
+ * of <code>&lt;cursor-adapter /&gt;</code>.</p>
+ * 
+ * <a name="xml-cursor-adapter-select-tag"></a>
+ * <h3>&lt;select /&gt;</h3>
+ * <p>The <code>&lt;select /&gt;</code> tag is used to select columns from the cursor
+ * when doing the query. This can be very useful when using transformations in the
+ * <code>&lt;bind /&gt;</code> elements. It can also be very useful if you are providing
+ * your own <a href="#xml-cursor-adapter-bind-data-types">binder</a> or
+ * <a href="#xml-cursor-adapter-bind-data-types">transformation</a> classes.
+ * <code>&lt;select /&gt;</code> elements are ignored if you supply the cursor yourself.</p>
+ * <p>The <code>&lt;select /&gt;</code> supports the following attributes:</p>
+ * <ul>
+ *  <li><code>android:column</code>: Name of the column to select in the cursor during the
+ *  query operation</li>
+ * </ul>
+ * <p><strong>Note:</strong> The column named <code>_id</code> is always implicitly
+ * selected.</p>
+ * 
+ * <a name="xml-cursor-adapter-bind-tag"></a>
+ * <h3>&lt;bind /&gt;</h3>
+ * <p>The <code>&lt;bind /&gt;</code> tag is used to bind a column from the cursor to
+ * a {@link android.view.View}. A column bound using this tag is automatically selected
+ * during the query and a matching
+ * <a href="#xml-cursor-adapter-select-tag"><code>&lt;select /&gt;</code> tag is therefore
+ * not required.</p>
+ * 
+ * <p>Each binding is declared as a one to one matching but
+ * custom binder classes or special
+ * <a href="#xml-cursor-adapter-bind-data-transformation">data transformations</a> can
+ * allow you to bind several columns to a single view. In this case you must use the
+ * <a href="#xml-cursor-adapter-select-tag"><code>&lt;select /&gt;</code> tag to make
+ * sure any required column is part of the query.</p>
+ * 
+ * <p>The <code>&lt;bind /&gt;</code> tag supports the following attributes:</p>
+ * <ul>
+ *  <li><code>android:from</code>: The name of the column to bind from.
+ *  This attribute is mandatory. Note that <code>@</code> which are not used to reference resources
+ *  should be backslash protected as in <code>\@</code>.</li>
+ *  <li><code>android:to</code>: The id of the view to bind to. This attribute is mandatory.</li>
+ *  <li><code>android:as</code>: The <a href="#xml-cursor-adapter-bind-data-types">data type</a>
+ *  of the binding. This attribute is mandatory.</li>
+ * </ul>
+ * 
+ * <p>In addition, a <code>&lt;bind /&gt;</code> can contain zero or more instances of
+ * <a href="#xml-cursor-adapter-bind-data-transformation">data transformations</a> children
+ * tags.</p>
+ *
+ * <a name="xml-cursor-adapter-bind-data-types"></a>
+ * <h4>Binding data types</h4>
+ * <p>For a binding to occur the data type of the bound column/view pair must be specified.
+ * The following data types are currently supported:</p>
+ * <ul>
+ *  <li><code>string</code>: The content of the column is interpreted as a string and must be
+ *  bound to a {@link android.widget.TextView}</li>
+ *  <li><code>image</code>: The content of the column is interpreted as a blob describing an
+ *  image and must be bound to an {@link android.widget.ImageView}</li>
+ *  <li><code>image-uri</code>: The content of the column is interpreted as a URI to an image
+ *  and must be bound to an {@link android.widget.ImageView}</li>
+ *  <li><code>drawable</code>: The content of the column is interpreted as a resource id to a
+ *  drawable and must be bound to an {@link android.widget.ImageView}</li>
+ *  <li><code>tag</code>: The content of the column is interpreted as a string and will be set as
+ *  the tag (using {@link View#setTag(Object)} of the associated View. This can be used to
+ *  associate meta-data to your view, that can be used for instance by a listener.</li>
+ *  <li>A fully qualified class name: The name of a class corresponding to an implementation of
+ *  {@link android.widget.Adapters.CursorBinder}. Cursor binders can be used to provide
+ *  bindings not supported by default. Custom binders cannot be used with
+ *  {@link android.content.Context#isRestricted() restricted contexts}, for instance in an
+ *  application widget</li>
+ * </ul>
+ * 
+ * <a name="xml-cursor-adapter-bind-transformation"></a>
+ * <h4>Binding transformations</h4>
+ * <p>When defining a data binding you can specify an optional transformation by using one
+ * of the following tags as a child of a <code>&lt;bind /&gt;</code> elements:</p>
+ * <ul>
+ *  <li><code>&lt;map /&gt;</code>: Maps a constant string to a string or a resource. Use
+ *  one instance of this tag per value you want to map</li>
+ *  <li><code>&lt;transform /&gt;</code>: Transforms a column's value using an expression
+ *  or an instance of {@link android.widget.Adapters.CursorTransformation}</li>
+ * </ul>
+ * <p>While several <code>&lt;map /&gt;</code> tags can be used at the same time, you cannot
+ * mix <code>&lt;map /&gt;</code> and <code>&lt;transform /&gt;</code> tags. If several
+ * <code>&lt;transform /&gt;</code> tags are specified, only the last one is retained.</p>
+ * 
+ * <a name="xml-cursor-adapter-bind-transformation-map" />
+ * <p><strong>&lt;map /&gt;</strong></p>
+ * <p>A map element simply specifies a value to match from and a value to match to. When
+ * a column's value equals the value to match from, it is replaced with the value to match
+ * to. The following attributes are supported:</p>
+ * <ul>
+ *  <li><code>android:fromValue</code>: The value to match from. This attribute is mandatory</li>
+ *  <li><code>android:toValue</code>: The value to match to. This value can be either a string
+ *  or a resource identifier. This value is interpreted as a resource identifier when the
+ *  data binding is of type <code>drawable</code>. This attribute is mandatory</li>
+ * </ul>
+ * 
+ * <a name="xml-cursor-adapter-bind-transformation-transform"></a>
+ * <p><strong>&lt;transform /&gt;</strong></p>
+ * <p>A simple transform that occurs either by calling a specified class or by performing
+ * simple text substitution. The following attributes are supported:</p>
+ * <ul>
+ *  <li><code>android:withExpression</code>: The transformation expression. The expression is
+ *  a string containing column names surrounded with curly braces { and }. During the
+ *  transformation each column name is replaced by its value. All columns must have been
+ *  selected in the query. An example of expression is <code>"First name: {first_name},
+ *  last name: {last_name}"</code>. This attribute is mandatory
+ *  if <code>android:withClass</code> is not specified and ignored if <code>android:withClass</code>
+ *  is specified</li>
+ *  <li><code>android:withClass</code>: A fully qualified class name corresponding to an
+ *  implementation of {@link android.widget.Adapters.CursorTransformation}. Custom
+ *  transformations cannot be used with
+ *  {@link android.content.Context#isRestricted() restricted contexts}, for instance in
+ *  an app widget This attribute is mandatory if <code>android:withExpression</code> is
+ *  not specified</li>
+ * </ul>
+ * 
+ * <h3>Example</h3>
+ * <p>The following example defines a cursor adapter that queries all the contacts with
+ * a phone number using the contacts content provider. Each contact is displayed with
+ * its display name, its favorite status and its photo. To display photos, a custom data
+ * binder is declared:</p>
+ * 
+ * <pre class="prettyprint">
+ * &lt;cursor-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+ *     android:uri="content://com.android.contacts/contacts"
+ *     android:selection="has_phone_number=1"
+ *     android:layout="@layout/contact_item"&gt;
+ *
+ *     &lt;bind android:from="display_name" android:to="@id/name" android:as="string" /&gt;
+ *     &lt;bind android:from="starred" android:to="@id/star" android:as="drawable"&gt;
+ *         &lt;map android:fromValue="0" android:toValue="@android:drawable/star_big_off" /&gt;
+ *         &lt;map android:fromValue="1" android:toValue="@android:drawable/star_big_on" /&gt;
+ *     &lt;/bind&gt;
+ *     &lt;bind android:from="_id" android:to="@id/name"
+ *              android:as="com.google.android.test.adapters.ContactPhotoBinder" /&gt;
+ *
+ * &lt;/cursor-adapter&gt;
+ * </pre>
+ * 
+ * <h3>Related APIs</h3>
+ * <ul>
+ *  <li>{@link android.widget.Adapters#loadAdapter(android.content.Context, int, Object[])}</li>
+ *  <li>{@link android.widget.Adapters#loadCursorAdapter(android.content.Context, int, android.database.Cursor, Object[])}</li>
+ *  <li>{@link android.widget.Adapters#loadCursorAdapter(android.content.Context, int, String, Object[])}</li>
+ *  <li>{@link android.widget.Adapters.CursorBinder}</li>
+ *  <li>{@link android.widget.Adapters.CursorTransformation}</li>
+ *  <li>{@link android.widget.CursorAdapter}</li>
+ * </ul>
+ * 
+ * @see android.widget.Adapter
+ * @see android.content.ContentProvider
+ * 
+ * @attr ref android.R.styleable#CursorAdapter_layout 
+ * @attr ref android.R.styleable#CursorAdapter_selection 
+ * @attr ref android.R.styleable#CursorAdapter_sortOrder 
+ * @attr ref android.R.styleable#CursorAdapter_uri 
+ * @attr ref android.R.styleable#CursorAdapter_BindItem_as 
+ * @attr ref android.R.styleable#CursorAdapter_BindItem_from 
+ * @attr ref android.R.styleable#CursorAdapter_BindItem_to
+ * @attr ref android.R.styleable#CursorAdapter_MapItem_fromValue 
+ * @attr ref android.R.styleable#CursorAdapter_MapItem_toValue 
+ * @attr ref android.R.styleable#CursorAdapter_SelectItem_column 
+ * @attr ref android.R.styleable#CursorAdapter_TransformItem_withClass 
+ * @attr ref android.R.styleable#CursorAdapter_TransformItem_withExpression 
+ */
+@SuppressWarnings({"JavadocReference"})
+public class Adapters {
+    private static final String ADAPTER_CURSOR = "cursor-adapter";
+
+    /**
+     * <p>Interface used to bind a {@link android.database.Cursor} column to a View. This
+     * interface can be used to provide bindings for data types not supported by the
+     * standard implementation of {@link android.widget.Adapters}.</p>
+     * 
+     * <p>A binder is provided with a cursor transformation which may or may not be used
+     * to transform the value retrieved from the cursor. The transformation is guaranteed
+     * to never be null so it's always safe to apply the transformation.</p>
+     * 
+     * <p>The binder is associated with a Context but can be re-used with multiple cursors.
+     * As such, the implementation should make no assumption about the Cursor in use.</p>
+     *
+     * @see android.view.View 
+     * @see android.database.Cursor
+     * @see android.widget.Adapters.CursorTransformation
+     */
+    public static abstract class CursorBinder {
+        /**
+         * <p>The context associated with this binder.</p>
+         */
+        protected final Context mContext;
+
+        /**
+         * <p>The transformation associated with this binder. This transformation is never
+         * null and may or may not be applied to the Cursor data during the
+         * {@link #bind(android.view.View, android.database.Cursor, int)} operation.</p>
+         * 
+         * @see #bind(android.view.View, android.database.Cursor, int) 
+         */
+        protected final CursorTransformation mTransformation;
+
+        /**
+         * <p>Creates a new Cursor binder.</p> 
+         * 
+         * @param context The context associated with this binder.
+         * @param transformation The transformation associated with this binder. This
+         *        transformation may or may not be applied by the binder and is guaranteed
+         *        to not be null.
+         */
+        public CursorBinder(Context context, CursorTransformation transformation) {
+            mContext = context;
+            mTransformation = transformation;
+        }
+
+        /**
+         * <p>Binds the specified Cursor column to the supplied View. The binding operation
+         * can query other Cursor columns as needed. During the binding operation, values
+         * retrieved from the Cursor may or may not be transformed using this binder's
+         * cursor transformation.</p>
+         * 
+         * @param view The view to bind data to.
+         * @param cursor The cursor to bind data from.
+         * @param columnIndex The column index in the cursor where the data to bind resides.
+         * 
+         * @see #mTransformation
+         * 
+         * @return True if the column was successfully bound to the View, false otherwise.
+         */
+        public abstract boolean bind(View view, Cursor cursor, int columnIndex);
+    }
+
+    /**
+     * <p>Interface used to transform data coming out of a {@link android.database.Cursor}
+     * before it is bound to a {@link android.view.View}.</p>
+     * 
+     * <p>Transformations are used to transform text-based data (in the form of a String),
+     * or to transform data into a resource identifier. A default implementation is provided
+     * to generate resource identifiers.</p>
+     * 
+     * @see android.database.Cursor
+     * @see android.widget.Adapters.CursorBinder
+     */
+    public static abstract class CursorTransformation {
+        /**
+         * <p>The context associated with this transformation.</p>
+         */
+        protected final Context mContext;
+
+        /**
+         * <p>Creates a new Cursor transformation.</p>
+         * 
+         * @param context The context associated with this transformation.
+         */
+        public CursorTransformation(Context context) {
+            mContext = context;
+        }
+
+        /**
+         * <p>Transforms the specified Cursor column into a String. The transformation
+         * can simply return the content of the column as a String (this is known
+         * as the identity transformation) or manipulate the content. For instance,
+         * a transformation can perform text substitutions or concatenate other
+         * columns with the specified column.</p>
+         * 
+         * @param cursor The cursor that contains the data to transform. 
+         * @param columnIndex The index of the column to transform.
+         * 
+         * @return A String containing the transformed value of the column.
+         */
+        public abstract String transform(Cursor cursor, int columnIndex);
+
+        /**
+         * <p>Transforms the specified Cursor column into a resource identifier.
+         * The default implementation simply interprets the content of the column
+         * as an integer.</p>
+         * 
+         * @param cursor The cursor that contains the data to transform. 
+         * @param columnIndex The index of the column to transform.
+         * 
+         * @return A resource identifier.
+         */
+        public int transformToResource(Cursor cursor, int columnIndex) {
+            return cursor.getInt(columnIndex);
+        }        
+    }
+
+    /**
+     * <p>Loads the {@link android.widget.CursorAdapter} defined in the specified
+     * XML resource. The content of the adapter is loaded from the content provider
+     * identified by the supplied URI.</p>
+     * 
+     * <p><strong>Note:</strong> If the supplied {@link android.content.Context} is
+     * an {@link android.app.Activity}, the cursor returned by the content provider
+     * will be automatically managed. Otherwise, you are responsible for managing the
+     * cursor yourself.</p>
+     * 
+     * <p>The format of the XML definition of the cursor adapter is documented at
+     * the top of this page.</p>
+     * 
+     * @param context The context to load the XML resource from.
+     * @param id The identifier of the XML resource declaring the adapter.
+     * @param uri The URI of the content provider.
+     * @param parameters Optional parameters to pass to the CursorAdapter, used
+     *        to substitute values in the selection expression.
+     * 
+     * @return A {@link android.widget.CursorAdapter}
+     * 
+     * @throws IllegalArgumentException If the XML resource does not contain
+     *         a valid &lt;cursor-adapter /&gt; definition.
+     * 
+     * @see android.content.ContentProvider
+     * @see android.widget.CursorAdapter
+     * @see #loadAdapter(android.content.Context, int, Object[]) 
+     */
+    public static CursorAdapter loadCursorAdapter(Context context, int id, String uri,
+            Object... parameters) {
+
+        XmlCursorAdapter adapter = (XmlCursorAdapter) loadAdapter(context, id, ADAPTER_CURSOR,
+                parameters);
+
+        if (uri != null) {
+            adapter.setUri(uri);
+        }
+        adapter.load();
+
+        return adapter;
+    }
+
+    /**
+     * <p>Loads the {@link android.widget.CursorAdapter} defined in the specified
+     * XML resource. The content of the adapter is loaded from the specified cursor.
+     * You are responsible for managing the supplied cursor.</p>
+     * 
+     * <p>The format of the XML definition of the cursor adapter is documented at
+     * the top of this page.</p>
+     * 
+     * @param context The context to load the XML resource from.
+     * @param id The identifier of the XML resource declaring the adapter.
+     * @param cursor The cursor containing the data for the adapter.
+     * @param parameters Optional parameters to pass to the CursorAdapter, used
+     *        to substitute values in the selection expression.
+     * 
+     * @return A {@link android.widget.CursorAdapter}
+     * 
+     * @throws IllegalArgumentException If the XML resource does not contain
+     *         a valid &lt;cursor-adapter /&gt; definition.
+     * 
+     * @see android.content.ContentProvider
+     * @see android.widget.CursorAdapter
+     * @see android.database.Cursor
+     * @see #loadAdapter(android.content.Context, int, Object[]) 
+     */
+    public static CursorAdapter loadCursorAdapter(Context context, int id, Cursor cursor,
+            Object... parameters) {
+
+        XmlCursorAdapter adapter = (XmlCursorAdapter) loadAdapter(context, id, ADAPTER_CURSOR,
+                parameters);
+
+        if (cursor != null) {
+            adapter.changeCursor(cursor);
+        }
+
+        return adapter;
+    }
+
+    /**
+     * <p>Loads the adapter defined in the specified XML resource. The XML definition of
+     * the adapter must follow the format definition of one of the supported adapter
+     * types described at the top of this page.</p>
+     * 
+     * <p><strong>Note:</strong> If the loaded adapter is a {@link android.widget.CursorAdapter}
+     * and the supplied {@link android.content.Context} is an {@link android.app.Activity},
+     * the cursor returned by the content provider will be automatically managed. Otherwise,
+     * you are responsible for managing the cursor yourself.</p>
+     * 
+     * @param context The context to load the XML resource from.
+     * @param id The identifier of the XML resource declaring the adapter.
+     * @param parameters Optional parameters to pass to the adapter.
+     *  
+     * @return An adapter instance.
+     * 
+     * @see #loadCursorAdapter(android.content.Context, int, android.database.Cursor, Object[]) 
+     * @see #loadCursorAdapter(android.content.Context, int, String, Object[]) 
+     */
+    public static BaseAdapter loadAdapter(Context context, int id, Object... parameters) {
+        final BaseAdapter adapter = loadAdapter(context, id, null, parameters);
+        if (adapter instanceof ManagedAdapter) {
+            ((ManagedAdapter) adapter).load();
+        }
+        return adapter;
+    }
+
+    /**
+     * Loads an adapter from the specified XML resource. The optional assertName can
+     * be used to exit early if the adapter defined in the XML resource is not of the
+     * expected type.
+     * 
+     * @param context The context to associate with the adapter.
+     * @param id The resource id of the XML document defining the adapter.
+     * @param assertName The mandatory name of the adapter in the XML document.
+     *        Ignored if null.
+     * @param parameters Optional parameters passed to the adapter.
+     * 
+     * @return An instance of {@link android.widget.BaseAdapter}.
+     */
+    private static BaseAdapter loadAdapter(Context context, int id, String assertName,
+            Object... parameters) {
+
+        XmlResourceParser parser = null;
+        try {
+            parser = context.getResources().getXml(id);
+            return createAdapterFromXml(context, parser, Xml.asAttributeSet(parser),
+                    id, parameters, assertName);
+        } catch (XmlPullParserException ex) {
+            Resources.NotFoundException rnf = new Resources.NotFoundException(
+                    "Can't load adapter resource ID " +
+                    context.getResources().getResourceEntryName(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } catch (IOException ex) {
+            Resources.NotFoundException rnf = new Resources.NotFoundException(
+                    "Can't load adapter resource ID " +
+                    context.getResources().getResourceEntryName(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } finally {
+            if (parser != null) parser.close();
+        }
+    }
+
+    /**
+     * Generates an adapter using the specified XML parser. This method is responsible
+     * for choosing the type of the adapter to create based on the content of the
+     * XML parser.
+     * 
+     * This method will generate an {@link IllegalArgumentException} if
+     * <code>assertName</code> is not null and does not match the root tag of the XML
+     * document. 
+     */
+    private static BaseAdapter createAdapterFromXml(Context c,
+            XmlPullParser parser, AttributeSet attrs, int id, Object[] parameters,
+            String assertName) throws XmlPullParserException, IOException {
+
+        BaseAdapter adapter = null;
+
+        // Make sure we are on a start tag.
+        int type;
+        int depth = parser.getDepth();
+
+        while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) &&
+                type != XmlPullParser.END_DOCUMENT) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String name = parser.getName();
+            if (assertName != null && !assertName.equals(name)) {
+                throw new IllegalArgumentException("The adapter defined in " +
+                        c.getResources().getResourceEntryName(id) + " must be a <" +
+                        assertName + " />");
+            }
+
+            if (ADAPTER_CURSOR.equals(name)) {
+                adapter = createCursorAdapter(c, parser, attrs, id, parameters);
+            } else {
+                throw new IllegalArgumentException("Unknown adapter name " + parser.getName() +
+                        " in " + c.getResources().getResourceEntryName(id));
+            }
+        }
+
+        return adapter;
+
+    }
+
+    /**
+     * Creates an XmlCursorAdapter using an XmlCursorAdapterParser.
+     */
+    private static XmlCursorAdapter createCursorAdapter(Context c, XmlPullParser parser,
+            AttributeSet attrs, int id, Object[] parameters)
+            throws IOException, XmlPullParserException {
+
+        return new XmlCursorAdapterParser(c, parser, attrs, id).parse(parameters);
+    }
+
+    /**
+     * Parser that can generate XmlCursorAdapter instances. This parser is responsible for
+     * handling all the attributes and child nodes for a &lt;cursor-adapter /&gt;.
+     */
+    private static class XmlCursorAdapterParser {
+        private static final String ADAPTER_CURSOR_BIND = "bind";
+        private static final String ADAPTER_CURSOR_SELECT = "select";
+        private static final String ADAPTER_CURSOR_AS_STRING = "string";
+        private static final String ADAPTER_CURSOR_AS_IMAGE = "image";
+        private static final String ADAPTER_CURSOR_AS_TAG = "tag";
+        private static final String ADAPTER_CURSOR_AS_IMAGE_URI = "image-uri";
+        private static final String ADAPTER_CURSOR_AS_DRAWABLE = "drawable";
+        private static final String ADAPTER_CURSOR_MAP = "map";
+        private static final String ADAPTER_CURSOR_TRANSFORM = "transform";
+
+        private final Context mContext;
+        private final XmlPullParser mParser;
+        private final AttributeSet mAttrs;
+        private final int mId;
+
+        private final HashMap<String, CursorBinder> mBinders;
+        private final ArrayList<String> mFrom;
+        private final ArrayList<Integer> mTo;
+        private final CursorTransformation mIdentity;
+        private final Resources mResources;
+
+        public XmlCursorAdapterParser(Context c, XmlPullParser parser, AttributeSet attrs, int id) {
+            mContext = c;
+            mParser = parser;
+            mAttrs = attrs;
+            mId = id;
+
+            mResources = mContext.getResources();
+            mBinders = new HashMap<String, CursorBinder>();
+            mFrom = new ArrayList<String>();
+            mTo = new ArrayList<Integer>();
+            mIdentity = new IdentityTransformation(mContext);            
+        }
+
+        public XmlCursorAdapter parse(Object[] parameters)
+               throws IOException, XmlPullParserException {
+
+            Resources resources = mResources;
+            TypedArray a = resources.obtainAttributes(mAttrs, android.R.styleable.CursorAdapter);
+
+            String uri = a.getString(android.R.styleable.CursorAdapter_uri);
+            String selection = a.getString(android.R.styleable.CursorAdapter_selection);
+            String sortOrder = a.getString(android.R.styleable.CursorAdapter_sortOrder);
+            int layout = a.getResourceId(android.R.styleable.CursorAdapter_layout, 0);
+            if (layout == 0) {
+                throw new IllegalArgumentException("The layout specified in " +
+                        resources.getResourceEntryName(mId) + " does not exist");
+            }
+
+            a.recycle();
+
+            XmlPullParser parser = mParser;
+            int type;
+            int depth = parser.getDepth();
+
+            while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) &&
+                    type != XmlPullParser.END_DOCUMENT) {
+
+                if (type != XmlPullParser.START_TAG) {
+                    continue;
+                }
+
+                String name = parser.getName();
+
+                if (ADAPTER_CURSOR_BIND.equals(name)) {
+                    parseBindTag();
+                } else if (ADAPTER_CURSOR_SELECT.equals(name)) {
+                    parseSelectTag();
+                } else {
+                    throw new RuntimeException("Unknown tag name " + parser.getName() + " in " +
+                            resources.getResourceEntryName(mId));
+                }
+            }
+
+            String[] fromArray = mFrom.toArray(new String[mFrom.size()]);
+            int[] toArray = new int[mTo.size()];
+            for (int i = 0; i < toArray.length; i++) {
+                toArray[i] = mTo.get(i);
+            }
+
+            String[] selectionArgs = null;
+            if (parameters != null) {
+                selectionArgs = new String[parameters.length];
+                for (int i = 0; i < selectionArgs.length; i++) {
+                    selectionArgs[i] = (String) parameters[i];
+                }
+            }
+
+            return new XmlCursorAdapter(mContext, layout, uri, fromArray, toArray, selection,
+                    selectionArgs, sortOrder, mBinders);
+        }
+
+        private void parseSelectTag() {
+            TypedArray a = mResources.obtainAttributes(mAttrs,
+                    android.R.styleable.CursorAdapter_SelectItem);
+
+            String fromName = a.getString(android.R.styleable.CursorAdapter_SelectItem_column);
+            if (fromName == null) {
+                throw new IllegalArgumentException("A select item in " +
+                        mResources.getResourceEntryName(mId) +
+                        " does not have a 'column' attribute");
+            }
+
+            a.recycle();
+
+            mFrom.add(fromName);
+            mTo.add(View.NO_ID);
+        }
+
+        private void parseBindTag() throws IOException, XmlPullParserException {
+            Resources resources = mResources;
+            TypedArray a = resources.obtainAttributes(mAttrs,
+                    android.R.styleable.CursorAdapter_BindItem);
+
+            String fromName = a.getString(android.R.styleable.CursorAdapter_BindItem_from);
+            if (fromName == null) {
+                throw new IllegalArgumentException("A bind item in " +
+                        resources.getResourceEntryName(mId) + " does not have a 'from' attribute");
+            }
+
+            int toName = a.getResourceId(android.R.styleable.CursorAdapter_BindItem_to, 0);
+            if (toName == 0) {
+                throw new IllegalArgumentException("A bind item in " +
+                        resources.getResourceEntryName(mId) + " does not have a 'to' attribute");
+            }
+
+            String asType = a.getString(android.R.styleable.CursorAdapter_BindItem_as);
+            if (asType == null) {
+                throw new IllegalArgumentException("A bind item in " +
+                        resources.getResourceEntryName(mId) + " does not have an 'as' attribute");
+            }
+
+            mFrom.add(fromName);
+            mTo.add(toName);
+            mBinders.put(fromName, findBinder(asType));
+
+            a.recycle();
+        }
+
+        private CursorBinder findBinder(String type) throws IOException, XmlPullParserException {
+            final XmlPullParser parser = mParser;
+            final Context context = mContext;
+            CursorTransformation transformation = mIdentity;
+
+            int tagType;
+            int depth = parser.getDepth();
+
+            final boolean isDrawable = ADAPTER_CURSOR_AS_DRAWABLE.equals(type);            
+
+            while (((tagType = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+                    && tagType != XmlPullParser.END_DOCUMENT) {
+
+                if (tagType != XmlPullParser.START_TAG) {
+                    continue;
+                }
+
+                String name = parser.getName();
+
+                if (ADAPTER_CURSOR_TRANSFORM.equals(name)) {
+                    transformation = findTransformation();
+                } else if (ADAPTER_CURSOR_MAP.equals(name)) {
+                    if (!(transformation instanceof MapTransformation)) {
+                        transformation = new MapTransformation(context);
+                    }
+                    findMap(((MapTransformation) transformation), isDrawable);
+                } else {
+                    throw new RuntimeException("Unknown tag name " + parser.getName() + " in " +
+                            context.getResources().getResourceEntryName(mId));
+                }
+            }
+
+            if (ADAPTER_CURSOR_AS_STRING.equals(type)) {
+                return new StringBinder(context, transformation);
+            } else if (ADAPTER_CURSOR_AS_TAG.equals(type)) {
+                return new TagBinder(context, transformation);
+            } else if (ADAPTER_CURSOR_AS_IMAGE.equals(type)) {
+                return new ImageBinder(context, transformation);            
+            } else if (ADAPTER_CURSOR_AS_IMAGE_URI.equals(type)) {
+                return new ImageUriBinder(context, transformation);
+            } else if (isDrawable) {
+                return new DrawableBinder(context, transformation);
+            } else {
+                return createBinder(type, transformation);
+            }
+        }
+
+        private CursorBinder createBinder(String type, CursorTransformation transformation) {
+            if (mContext.isRestricted()) return null;
+
+            try {
+                final Class<?> klass = Class.forName(type, true, mContext.getClassLoader());
+                if (CursorBinder.class.isAssignableFrom(klass)) {
+                    final Constructor<?> c = klass.getDeclaredConstructor(
+                            Context.class, CursorTransformation.class);
+                    return (CursorBinder) c.newInstance(mContext, transformation);
+                }
+            } catch (ClassNotFoundException e) {
+                throw new IllegalArgumentException("Cannot instanciate binder type in " +
+                        mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
+            } catch (NoSuchMethodException e) {
+                throw new IllegalArgumentException("Cannot instanciate binder type in " +
+                        mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
+            } catch (InvocationTargetException e) {
+                throw new IllegalArgumentException("Cannot instanciate binder type in " +
+                        mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
+            } catch (InstantiationException e) {
+                throw new IllegalArgumentException("Cannot instanciate binder type in " +
+                        mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
+            } catch (IllegalAccessException e) {
+                throw new IllegalArgumentException("Cannot instanciate binder type in " +
+                        mContext.getResources().getResourceEntryName(mId) + ": " + type, e);
+            }
+
+            return null;
+        }
+
+        private void findMap(MapTransformation transformation, boolean drawable) {
+            Resources resources = mResources;
+
+            TypedArray a = resources.obtainAttributes(mAttrs,
+                    android.R.styleable.CursorAdapter_MapItem);
+
+            String from = a.getString(android.R.styleable.CursorAdapter_MapItem_fromValue);
+            if (from == null) {
+                throw new IllegalArgumentException("A map item in " +
+                        resources.getResourceEntryName(mId) +
+                        " does not have a 'fromValue' attribute");
+            }
+
+            if (!drawable) {
+                String to = a.getString(android.R.styleable.CursorAdapter_MapItem_toValue);
+                if (to == null) {
+                    throw new IllegalArgumentException("A map item in " +
+                            resources.getResourceEntryName(mId) +
+                            " does not have a 'toValue' attribute");
+                }
+                transformation.addStringMapping(from, to);
+            } else {
+                int to = a.getResourceId(android.R.styleable.CursorAdapter_MapItem_toValue, 0);
+                if (to == 0) {
+                    throw new IllegalArgumentException("A map item in " +
+                            resources.getResourceEntryName(mId) +
+                            " does not have a 'toValue' attribute");
+                }
+                transformation.addResourceMapping(from, to);
+            }
+
+            a.recycle();
+        }
+
+        private CursorTransformation findTransformation() {
+            Resources resources = mResources;
+            CursorTransformation transformation = null;
+            TypedArray a = resources.obtainAttributes(mAttrs,
+                    android.R.styleable.CursorAdapter_TransformItem);
+
+            String className = a.getString(android.R.styleable.CursorAdapter_TransformItem_withClass);
+            if (className == null) {
+                String expression = a.getString(
+                        android.R.styleable.CursorAdapter_TransformItem_withExpression);
+                transformation = createExpressionTransformation(expression);
+            } else if (!mContext.isRestricted()) {
+                try {
+                    final Class<?> klas = Class.forName(className, true, mContext.getClassLoader());
+                    if (CursorTransformation.class.isAssignableFrom(klas)) {
+                        final Constructor<?> c = klas.getDeclaredConstructor(Context.class);
+                        transformation = (CursorTransformation) c.newInstance(mContext);
+                    }
+                } catch (ClassNotFoundException e) {
+                    throw new IllegalArgumentException("Cannot instanciate transform type in " +
+                           mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
+                } catch (NoSuchMethodException e) {
+                    throw new IllegalArgumentException("Cannot instanciate transform type in " +
+                           mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
+                } catch (InvocationTargetException e) {
+                    throw new IllegalArgumentException("Cannot instanciate transform type in " +
+                           mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
+                } catch (InstantiationException e) {
+                    throw new IllegalArgumentException("Cannot instanciate transform type in " +
+                           mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
+                } catch (IllegalAccessException e) {
+                    throw new IllegalArgumentException("Cannot instanciate transform type in " +
+                           mContext.getResources().getResourceEntryName(mId) + ": " + className, e);
+                }
+            }
+
+            a.recycle();
+
+            if (transformation == null) {
+                throw new IllegalArgumentException("A transform item in " +
+                    resources.getResourceEntryName(mId) + " must have a 'withClass' or " +
+                    "'withExpression' attribute");
+            }
+
+            return transformation;
+        }
+
+        private CursorTransformation createExpressionTransformation(String expression) {
+            return new ExpressionTransformation(mContext, expression);
+        }
+    }
+
+    /**
+     * Interface used by adapters that require to be loaded after creation.
+     */
+    private static interface ManagedAdapter {
+        /**
+         * Loads the content of the adapter, asynchronously.
+         */
+        void load();
+    }
+
+    /**
+     * Implementation of a Cursor adapter defined in XML. This class is a thin wrapper
+     * of a SimpleCursorAdapter. The main difference is the ability to handle CursorBinders.
+     */
+    private static class XmlCursorAdapter extends SimpleCursorAdapter implements ManagedAdapter {
+        private String mUri;
+        private final String mSelection;
+        private final String[] mSelectionArgs;
+        private final String mSortOrder;
+        private final String[] mColumns;
+        private final CursorBinder[] mBinders;
+        private AsyncTask<Void,Void,Cursor> mLoadTask;
+
+        XmlCursorAdapter(Context context, int layout, String uri, String[] from, int[] to,
+                String selection, String[] selectionArgs, String sortOrder,
+                HashMap<String, CursorBinder> binders) {
+
+            super(context, layout, null, from, to);
+            mContext = context;
+            mUri = uri;
+            mSelection = selection;
+            mSelectionArgs = selectionArgs;
+            mSortOrder = sortOrder;
+            mColumns = new String[from.length + 1];
+            // This is mandatory in CursorAdapter
+            mColumns[0] = "_id";
+            System.arraycopy(from, 0, mColumns, 1, from.length);
+
+            CursorBinder basic = new StringBinder(context, new IdentityTransformation(context));
+            final int count = from.length;
+            mBinders = new CursorBinder[count];
+
+            for (int i = 0; i < count; i++) {
+                CursorBinder binder = binders.get(from[i]);
+                if (binder == null) binder = basic;
+                mBinders[i] = binder;
+            }
+        }
+
+        @Override
+        public void bindView(View view, Context context, Cursor cursor) {
+            final int count = mTo.length;
+            final int[] from = mFrom;
+            final int[] to = mTo;
+            final CursorBinder[] binders = mBinders;
+
+            for (int i = 0; i < count; i++) {
+                final View v = view.findViewById(to[i]);
+                if (v != null) {
+                    binders[i].bind(v, cursor, from[i]);
+                }
+            }
+        }
+
+        public void load() {
+            if (mUri != null) {
+                mLoadTask = new QueryTask().execute();
+            }
+        }
+
+        void setUri(String uri) {
+            mUri = uri;
+        }
+
+        @Override
+        public void changeCursor(Cursor c) {
+            if (mLoadTask != null && mLoadTask.getStatus() != QueryTask.Status.FINISHED) {
+                mLoadTask.cancel(true);
+                mLoadTask = null;
+            }
+            super.changeCursor(c);
+        }
+
+        class QueryTask extends AsyncTask<Void, Void, Cursor> {
+            @Override
+            protected Cursor doInBackground(Void... params) {
+                if (mContext instanceof Activity) {
+                    return ((Activity) mContext).managedQuery(
+                            Uri.parse(mUri), mColumns, mSelection, mSelectionArgs, mSortOrder);
+                } else {
+                    return mContext.getContentResolver().query(
+                            Uri.parse(mUri), mColumns, mSelection, mSelectionArgs, mSortOrder);
+                }
+            }
+
+            @Override
+            protected void onPostExecute(Cursor cursor) {
+                if (!isCancelled()) {
+                    XmlCursorAdapter.super.changeCursor(cursor);
+                }
+            }
+        }
+    }
+
+    /**
+     * Identity transformation, returns the content of the specified column as a String,
+     * without performing any manipulation. This is used when no transformation is specified.
+     */
+    private static class IdentityTransformation extends CursorTransformation {
+        public IdentityTransformation(Context context) {
+            super(context);
+        }
+
+        @Override
+        public String transform(Cursor cursor, int columnIndex) {
+            return cursor.getString(columnIndex);
+        }
+    }
+
+    /**
+     * An expression transformation is a simple template based replacement utility.
+     * In an expression, each segment of the form <code>{([^}]+)}</code> is replaced
+     * with the value of the column of name $1.
+     */
+    private static class ExpressionTransformation extends CursorTransformation {
+        private final ExpressionNode mFirstNode = new ConstantExpressionNode("");
+        private final StringBuilder mBuilder = new StringBuilder();
+
+        public ExpressionTransformation(Context context, String expression) {
+            super(context);
+
+            parse(expression);
+        }
+
+        private void parse(String expression) {
+            ExpressionNode node = mFirstNode;
+            int segmentStart;
+            int count = expression.length();
+
+            for (int i = 0; i < count; i++) {
+                char c = expression.charAt(i);
+                // Start a column name segment
+                segmentStart = i;
+                if (c == '{') {
+                    while (i < count && (c = expression.charAt(i)) != '}') {
+                        i++;
+                    }
+                    // We've reached the end, but the expression didn't close
+                    if (c != '}') {
+                        throw new IllegalStateException("The transform expression contains a " +
+                                "non-closed column name: " +
+                                expression.substring(segmentStart + 1, i));
+                    }
+                    node.next = new ColumnExpressionNode(expression.substring(segmentStart + 1, i));
+                } else {
+                    while (i < count && (c = expression.charAt(i)) != '{') {
+                        i++;
+                    }
+                    node.next = new ConstantExpressionNode(expression.substring(segmentStart, i));
+                    // Rewind if we've reached a column expression
+                    if (c == '{') i--;
+                }
+                node = node.next;
+            }
+        }
+
+        @Override
+        public String transform(Cursor cursor, int columnIndex) {
+            final StringBuilder builder = mBuilder;
+            builder.delete(0, builder.length());
+
+            ExpressionNode node = mFirstNode;
+            // Skip the first node
+            while ((node = node.next) != null) {
+                builder.append(node.asString(cursor));
+            }
+
+            return builder.toString();
+        }
+
+        static abstract class ExpressionNode {
+            public ExpressionNode next;
+
+            public abstract String asString(Cursor cursor);
+        }
+
+        static class ConstantExpressionNode extends ExpressionNode {
+            private final String mConstant;
+
+            ConstantExpressionNode(String constant) {
+                mConstant = constant;
+            }
+
+            @Override
+            public String asString(Cursor cursor) {
+                return mConstant;
+            }
+        }
+
+        static class ColumnExpressionNode extends ExpressionNode {
+            private final String mColumnName;
+            private Cursor mSignature;
+            private int mColumnIndex = -1;
+
+            ColumnExpressionNode(String columnName) {
+                mColumnName = columnName;
+            }
+
+            @Override
+            public String asString(Cursor cursor) {
+                if (cursor != mSignature || mColumnIndex == -1) {
+                    mColumnIndex = cursor.getColumnIndex(mColumnName);
+                    mSignature = cursor;
+                }
+
+                return cursor.getString(mColumnIndex);
+            }
+        }
+    }
+
+    /**
+     * A map transformation offers a simple mapping between specified String values
+     * to Strings or integers.
+     */
+    private static class MapTransformation extends CursorTransformation {
+        private final HashMap<String, String> mStringMappings;
+        private final HashMap<String, Integer> mResourceMappings;
+
+        public MapTransformation(Context context) {
+            super(context);
+            mStringMappings = new HashMap<String, String>();
+            mResourceMappings = new HashMap<String, Integer>();
+        }
+
+        void addStringMapping(String from, String to) {
+            mStringMappings.put(from, to);
+        }
+
+        void addResourceMapping(String from, int to) {
+            mResourceMappings.put(from, to);
+        }
+
+        @Override
+        public String transform(Cursor cursor, int columnIndex) {
+            final String value = cursor.getString(columnIndex);
+            final String transformed = mStringMappings.get(value);
+            return transformed == null ? value : transformed;
+        }
+
+        @Override
+        public int transformToResource(Cursor cursor, int columnIndex) {
+            final String value = cursor.getString(columnIndex);
+            final Integer transformed = mResourceMappings.get(value);
+            try {
+                return transformed == null ? Integer.parseInt(value) : transformed;
+            } catch (NumberFormatException e) {
+                return 0;
+            }
+        }
+    }
+
+    /**
+     * Binds a String to a TextView.
+     */
+    private static class StringBinder extends CursorBinder {
+        public StringBinder(Context context, CursorTransformation transformation) {
+            super(context, transformation);
+        }
+
+        @Override
+        public boolean bind(View view, Cursor cursor, int columnIndex) {
+            if (view instanceof TextView) {
+                final String text = mTransformation.transform(cursor, columnIndex);
+                ((TextView) view).setText(text);
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Binds an image blob to an ImageView.
+     */
+    private static class ImageBinder extends CursorBinder {
+        public ImageBinder(Context context, CursorTransformation transformation) {
+            super(context, transformation);
+        }
+
+        @Override
+        public boolean bind(View view, Cursor cursor, int columnIndex) {
+            if (view instanceof ImageView) {
+                final byte[] data = cursor.getBlob(columnIndex);
+                ((ImageView) view).setImageBitmap(BitmapFactory.decodeByteArray(data, 0,
+                        data.length));
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private static class TagBinder extends CursorBinder {
+        public TagBinder(Context context, CursorTransformation transformation) {
+            super(context, transformation);
+        }
+
+        @Override
+        public boolean bind(View view, Cursor cursor, int columnIndex) {
+            final String text = mTransformation.transform(cursor, columnIndex);
+            view.setTag(text);
+            return true;
+        }
+    }
+
+    /**
+     * Binds an image URI to an ImageView.
+     */
+    private static class ImageUriBinder extends CursorBinder {
+        public ImageUriBinder(Context context, CursorTransformation transformation) {
+            super(context, transformation);
+        }
+
+        @Override
+        public boolean bind(View view, Cursor cursor, int columnIndex) {
+            if (view instanceof ImageView) {
+                ((ImageView) view).setImageURI(Uri.parse(
+                        mTransformation.transform(cursor, columnIndex)));
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Binds a drawable resource identifier to an ImageView.
+     */
+    private static class DrawableBinder extends CursorBinder {
+        public DrawableBinder(Context context, CursorTransformation transformation) {
+            super(context, transformation);
+        }
+
+        @Override
+        public boolean bind(View view, Cursor cursor, int columnIndex) {
+            if (view instanceof ImageView) {
+                final int resource = mTransformation.transformToResource(cursor, columnIndex);
+                if (resource == 0) return false;
+
+                ((ImageView) view).setImageResource(resource);
+                return true;
+            }
+            return false;
+        }
+    }
+}
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java
index 03ada94..b4ece24 100644
--- a/core/java/android/widget/ArrayAdapter.java
+++ b/core/java/android/widget/ArrayAdapter.java
@@ -30,17 +30,17 @@
 import java.util.Collections;
 
 /**
- * A ListAdapter that manages a ListView backed by an array of arbitrary
+ * A concrete BaseAdapter that is backed by an array of arbitrary
  * objects.  By default this class expects that the provided resource id references
  * a single TextView.  If you want to use a more complex layout, use the constructors that
  * also takes a field id.  That field id should reference a TextView in the larger layout
  * resource.
  *
- * However the TextView is referenced, it will be filled with the toString() of each object in
+ * <p>However the TextView is referenced, it will be filled with the toString() of each object in
  * the array. You can add lists or arrays of custom objects. Override the toString() method
  * of your objects to determine what text will be displayed for the item in the list.
  *
- * To use something other than TextViews for the array display, for instance, ImageViews,
+ * <p>To use something other than TextViews for the array display, for instance, ImageViews,
  * or to have some of data besides toString() results fill the views,
  * override {@link #getView(int, View, ViewGroup)} to return the type of view you want.
  */
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 3971487..e07befa 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -29,13 +29,12 @@
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
 
 import com.android.internal.R;
 
@@ -93,45 +92,21 @@
     static final boolean DEBUG = false;
     static final String TAG = "AutoCompleteTextView";
 
-    private static final int HINT_VIEW_ID = 0x17;
-
-    /**
-     * This value controls the length of time that the user
-     * must leave a pointer down without scrolling to expand
-     * the autocomplete dropdown list to cover the IME.
-     */
-    private static final int EXPAND_LIST_TIMEOUT = 250;
-
     private CharSequence mHintText;
+    private TextView mHintView;
     private int mHintResource;
 
     private ListAdapter mAdapter;
     private Filter mFilter;
     private int mThreshold;
 
-    private PopupWindow mPopup;
-    private DropDownListView mDropDownList;
-    private int mDropDownVerticalOffset;
-    private int mDropDownHorizontalOffset;
+    private ListPopupWindow mPopup;
     private int mDropDownAnchorId;
-    private View mDropDownAnchorView;  // view is retrieved lazily from id once needed
-    private int mDropDownWidth;
-    private int mDropDownHeight;
-    private final Rect mTempRect = new Rect();
-
-    private Drawable mDropDownListHighlight;
 
     private AdapterView.OnItemClickListener mItemClickListener;
     private AdapterView.OnItemSelectedListener mItemSelectedListener;
 
-    private final DropDownItemClickListener mDropDownItemClickListener =
-            new DropDownItemClickListener();
-
-    private boolean mDropDownAlwaysVisible = false;
-
     private boolean mDropDownDismissedOnCompletion = true;
-    
-    private boolean mForceIgnoreOutsideTouch = false;
 
     private int mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN;
     private boolean mOpenBefore;
@@ -140,10 +115,6 @@
 
     private boolean mBlockCompletion;
 
-    private ListSelectorHider mHideSelector;
-    private Runnable mShowDropDownRunnable;
-    private Runnable mResizePopupRunnable = new ResizePopupRunnable();
-
     private PassThroughClickListener mPassThroughClickListener;
     private PopupDataSetObserver mObserver;
 
@@ -158,9 +129,10 @@
     public AutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        mPopup = new PopupWindow(context, attrs,
+        mPopup = new ListPopupWindow(context, attrs,
                 com.android.internal.R.attr.autoCompleteTextViewStyle);
         mPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
+        mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW);
 
         TypedArray a =
             context.obtainStyledAttributes(
@@ -169,14 +141,11 @@
         mThreshold = a.getInt(
                 R.styleable.AutoCompleteTextView_completionThreshold, 2);
 
-        mHintText = a.getText(R.styleable.AutoCompleteTextView_completionHint);
-
-        mDropDownListHighlight = a.getDrawable(
-                R.styleable.AutoCompleteTextView_dropDownSelector);
-        mDropDownVerticalOffset = (int)
-                a.getDimension(R.styleable.AutoCompleteTextView_dropDownVerticalOffset, 0.0f);
-        mDropDownHorizontalOffset = (int)
-                a.getDimension(R.styleable.AutoCompleteTextView_dropDownHorizontalOffset, 0.0f);
+        mPopup.setListSelector(a.getDrawable(R.styleable.AutoCompleteTextView_dropDownSelector));
+        mPopup.setVerticalOffset((int)
+                a.getDimension(R.styleable.AutoCompleteTextView_dropDownVerticalOffset, 0.0f));
+        mPopup.setHorizontalOffset((int)
+                a.getDimension(R.styleable.AutoCompleteTextView_dropDownHorizontalOffset, 0.0f));
         
         // Get the anchor's id now, but the view won't be ready, so wait to actually get the
         // view and store it in mDropDownAnchorView lazily in getDropDownAnchorView later.
@@ -187,13 +156,18 @@
         
         // For dropdown width, the developer can specify a specific width, or MATCH_PARENT
         // (for full screen width) or WRAP_CONTENT (to match the width of the anchored view).
-        mDropDownWidth = a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownWidth,
-                ViewGroup.LayoutParams.WRAP_CONTENT);
-        mDropDownHeight = a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownHeight,
-                ViewGroup.LayoutParams.WRAP_CONTENT);
+        mPopup.setWidth(a.getLayoutDimension(
+                R.styleable.AutoCompleteTextView_dropDownWidth,
+                ViewGroup.LayoutParams.WRAP_CONTENT));
+        mPopup.setHeight(a.getLayoutDimension(
+                R.styleable.AutoCompleteTextView_dropDownHeight,
+                ViewGroup.LayoutParams.WRAP_CONTENT));
 
         mHintResource = a.getResourceId(R.styleable.AutoCompleteTextView_completionHintView,
                 R.layout.simple_dropdown_hint);
+        
+        mPopup.setOnItemClickListener(new DropDownItemClickListener());
+        setCompletionHint(a.getText(R.styleable.AutoCompleteTextView_completionHint));
 
         // Always turn on the auto complete input type flag, since it
         // makes no sense to use this widget without it.
@@ -241,6 +215,20 @@
      */
     public void setCompletionHint(CharSequence hint) {
         mHintText = hint;
+        if (hint != null) {
+            if (mHintView == null) {
+                final TextView hintView = (TextView) LayoutInflater.from(getContext()).inflate(
+                        mHintResource, null).findViewById(com.android.internal.R.id.text1);
+                hintView.setText(mHintText);
+                mHintView = hintView;
+                mPopup.setPromptView(hintView);
+            } else {
+                mHintView.setText(hint);
+            }
+        } else {
+            mPopup.setPromptView(null);
+            mHintView = null;
+        }
     }
     
     /**
@@ -253,7 +241,7 @@
      * @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth
      */
     public int getDropDownWidth() {
-        return mDropDownWidth;
+        return mPopup.getWidth();
     }
     
     /**
@@ -266,7 +254,7 @@
      * @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth
      */
     public void setDropDownWidth(int width) {
-        mDropDownWidth = width;
+        mPopup.setWidth(width);
     }
 
     /**
@@ -280,7 +268,7 @@
      * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight
      */
     public int getDropDownHeight() {
-        return mDropDownHeight;
+        return mPopup.getHeight();
     }
 
     /**
@@ -294,7 +282,7 @@
      * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight
      */
     public void setDropDownHeight(int height) {
-        mDropDownHeight = height;
+        mPopup.setHeight(height);
     }
     
     /**
@@ -319,7 +307,7 @@
      */
     public void setDropDownAnchor(int id) {
         mDropDownAnchorId = id;
-        mDropDownAnchorView = null;
+        mPopup.setAnchorView(null);
     }
     
     /**
@@ -361,7 +349,7 @@
      * @param offset the vertical offset
      */
     public void setDropDownVerticalOffset(int offset) {
-        mDropDownVerticalOffset = offset;
+        mPopup.setVerticalOffset(offset);
     }
     
     /**
@@ -370,7 +358,7 @@
      * @return the vertical offset
      */
     public int getDropDownVerticalOffset() {
-        return mDropDownVerticalOffset;
+        return mPopup.getVerticalOffset();
     }
     
     /**
@@ -379,7 +367,7 @@
      * @param offset the horizontal offset
      */
     public void setDropDownHorizontalOffset(int offset) {
-        mDropDownHorizontalOffset = offset;
+        mPopup.setHorizontalOffset(offset);
     }
     
     /**
@@ -388,7 +376,7 @@
      * @return the horizontal offset
      */
     public int getDropDownHorizontalOffset() {
-        return mDropDownHorizontalOffset;
+        return mPopup.getHorizontalOffset();
     }
 
      /**
@@ -425,7 +413,7 @@
      * @hide Pending API council approval
      */
     public boolean isDropDownAlwaysVisible() {
-        return mDropDownAlwaysVisible;
+        return mPopup.isDropDownAlwaysVisible();
     }
 
     /**
@@ -442,7 +430,7 @@
      * @hide Pending API council approval
      */
     public void setDropDownAlwaysVisible(boolean dropDownAlwaysVisible) {
-        mDropDownAlwaysVisible = dropDownAlwaysVisible;
+        mPopup.setDropDownAlwaysVisible(dropDownAlwaysVisible);
     }
    
     /**
@@ -609,15 +597,13 @@
             mFilter = null;
         }
 
-        if (mDropDownList != null) {
-            mDropDownList.setAdapter(mAdapter);
-        }
+        mPopup.setAdapter(mAdapter);
     }
 
     @Override
     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_BACK && isPopupShowing()
-                && !mDropDownAlwaysVisible) {
+                && !mPopup.isDropDownAlwaysVisible()) {
             // special case for the back key, we do not even try to send it
             // to the drop down list but instead, consume it immediately
             if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
@@ -636,18 +622,16 @@
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (isPopupShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
-            boolean consumed = mDropDownList.onKeyUp(keyCode, event);
-            if (consumed) {
-                switch (keyCode) {
-                    // if the list accepts the key events and the key event
-                    // was a click, the text view gets the selected item
-                    // from the drop down as its content
-                    case KeyEvent.KEYCODE_ENTER:
-                    case KeyEvent.KEYCODE_DPAD_CENTER:
-                        performCompletion();
-                        return true;
-                }
+        boolean consumed = mPopup.onKeyUp(keyCode, event);
+        if (consumed) {
+            switch (keyCode) {
+            // if the list accepts the key events and the key event
+            // was a click, the text view gets the selected item
+            // from the drop down as its content
+            case KeyEvent.KEYCODE_ENTER:
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+                performCompletion();
+                return true;
             }
         }
         return super.onKeyUp(keyCode, event);
@@ -655,87 +639,11 @@
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
-        // when the drop down is shown, we drive it directly
-        if (isPopupShowing()) {
-            // the key events are forwarded to the list in the drop down view
-            // note that ListView handles space but we don't want that to happen
-            // also if selection is not currently in the drop down, then don't
-            // let center or enter presses go there since that would cause it
-            // to select one of its items
-            if (keyCode != KeyEvent.KEYCODE_SPACE
-                    && (mDropDownList.getSelectedItemPosition() >= 0
-                            || (keyCode != KeyEvent.KEYCODE_ENTER
-                                    && keyCode != KeyEvent.KEYCODE_DPAD_CENTER))) {
-                int curIndex = mDropDownList.getSelectedItemPosition();
-                boolean consumed;
-
-                final boolean below = !mPopup.isAboveAnchor();
-
-                final ListAdapter adapter = mAdapter;
-                
-                boolean allEnabled;
-                int firstItem = Integer.MAX_VALUE;
-                int lastItem = Integer.MIN_VALUE;
-
-                if (adapter != null) {
-                    allEnabled = adapter.areAllItemsEnabled();
-                    firstItem = allEnabled ? 0 :
-                            mDropDownList.lookForSelectablePosition(0, true);
-                    lastItem = allEnabled ? adapter.getCount() - 1 :
-                            mDropDownList.lookForSelectablePosition(adapter.getCount() - 1, false);                    
-                }
-                
-                if ((below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= firstItem) ||
-                        (!below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN && curIndex >= lastItem)) {
-                    // When the selection is at the top, we block the key
-                    // event to prevent focus from moving.
-                    clearListSelection();
-                    mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
-                    showDropDown();
-                    return true;
-                } else {
-                    // WARNING: Please read the comment where mListSelectionHidden
-                    //          is declared
-                    mDropDownList.mListSelectionHidden = false;
-                }
-
-                consumed = mDropDownList.onKeyDown(keyCode, event);
-                if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" + consumed);
-
-                if (consumed) {
-                    // If it handled the key event, then the user is
-                    // navigating in the list, so we should put it in front.
-                    mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-                    // Here's a little trick we need to do to make sure that
-                    // the list view is actually showing its focus indicator,
-                    // by ensuring it has focus and getting its window out
-                    // of touch mode.
-                    mDropDownList.requestFocusFromTouch();
-                    showDropDown();
-
-                    switch (keyCode) {
-                        // avoid passing the focus from the text view to the
-                        // next component
-                        case KeyEvent.KEYCODE_ENTER:
-                        case KeyEvent.KEYCODE_DPAD_CENTER:
-                        case KeyEvent.KEYCODE_DPAD_DOWN:
-                        case KeyEvent.KEYCODE_DPAD_UP:
-                            return true;
-                    }
-                } else {
-                    if (below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
-                        // when the selection is at the bottom, we block the
-                        // event to avoid going to the next focusable widget
-                        if (curIndex == lastItem) {
-                            return true;
-                        }
-                    } else if (!below && keyCode == KeyEvent.KEYCODE_DPAD_UP &&
-                            curIndex == firstItem) {
-                        return true;
-                    }
-                }
-            }
-        } else {
+        if (mPopup.onKeyDown(keyCode, event)) {
+            return true;
+        }
+        
+        if (!isPopupShowing()) {
             switch(keyCode) {
             case KeyEvent.KEYCODE_DPAD_DOWN:
                 performValidation();
@@ -746,7 +654,7 @@
         boolean handled = super.onKeyDown(keyCode, event);
         mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN;
 
-        if (handled && isPopupShowing() && mDropDownList != null) {
+        if (handled && isPopupShowing()) {
             clearListSelection();
         }
 
@@ -807,11 +715,12 @@
         if (enoughToFilter()) {
             if (mFilter != null) {
                 performFiltering(getText(), mLastKeyCode);
+                buildImeCompletions();
             }
         } else {
             // drop down is automatically dismissed when enough characters
             // are deleted from the text view
-            if (!mDropDownAlwaysVisible) dismissDropDown();
+            if (!mPopup.isDropDownAlwaysVisible()) dismissDropDown();
             if (mFilter != null) {
                 mFilter.filter(null);
             }
@@ -844,13 +753,7 @@
      * it back.
      */
     public void clearListSelection() {
-        final DropDownListView list = mDropDownList;
-        if (list != null) {
-            // WARNING: Please read the comment where mListSelectionHidden is declared
-            list.mListSelectionHidden = true;
-            list.hideSelector();
-            list.requestLayout();
-        }
+        mPopup.clearListSelection();
     }
     
     /**
@@ -859,11 +762,7 @@
      * @param position The position to move the selector to.
      */
     public void setListSelection(int position) {
-        if (mPopup.isShowing() && (mDropDownList != null)) {
-            mDropDownList.mListSelectionHidden = false;
-            mDropDownList.setSelection(position);
-            // ListView.setSelection() will call requestLayout()
-        }
+        mPopup.setSelection(position);
     }
 
     /**
@@ -877,10 +776,7 @@
      * @see ListView#getSelectedItemPosition()
      */
     public int getListSelection() {
-        if (mPopup.isShowing() && (mDropDownList != null)) {
-            return mDropDownList.getSelectedItemPosition();
-        }
-        return ListView.INVALID_POSITION;
+        return mPopup.getSelectedItemPosition();
     }
 
     /**
@@ -914,13 +810,7 @@
             replaceText(completion.getText());
             mBlockCompletion = false;
 
-            if (mItemClickListener != null) {
-                final DropDownListView list = mDropDownList;
-                // Note that we don't have a View here, so we will need to
-                // supply null.  Hopefully no existing apps crash...
-                mItemClickListener.onItemClick(list, null, completion.getPosition(),
-                        completion.getId());
-            }
+            mPopup.performItemClick(completion.getPosition());
         }
     }
 
@@ -928,7 +818,7 @@
         if (isPopupShowing()) {
             Object selectedItem;
             if (position < 0) {
-                selectedItem = mDropDownList.getSelectedItem();
+                selectedItem = mPopup.getSelectedItem();
             } else {
                 selectedItem = mAdapter.getItem(position);
             }
@@ -942,18 +832,18 @@
             mBlockCompletion = false;            
 
             if (mItemClickListener != null) {
-                final DropDownListView list = mDropDownList;
+                final ListPopupWindow list = mPopup;
 
                 if (selectedView == null || position < 0) {
                     selectedView = list.getSelectedView();
                     position = list.getSelectedItemPosition();
                     id = list.getSelectedItemId();
                 }
-                mItemClickListener.onItemClick(list, selectedView, position, id);
+                mItemClickListener.onItemClick(list.getListView(), selectedView, position, id);
             }
         }
 
-        if (mDropDownDismissedOnCompletion && !mDropDownAlwaysVisible) {
+        if (mDropDownDismissedOnCompletion && !mPopup.isDropDownAlwaysVisible()) {
             dismissDropDown();
         }
     }
@@ -1003,7 +893,6 @@
     /** {@inheritDoc} */
     public void onFilterComplete(int count) {
         updateDropDownForFilter(count);
-
     }
 
     private void updateDropDownForFilter(int count) {
@@ -1017,11 +906,12 @@
          * to filter.
          */
 
-        if ((count > 0 || mDropDownAlwaysVisible) && enoughToFilter()) {
+        final boolean dropDownAlwaysVisible = mPopup.isDropDownAlwaysVisible();
+        if ((count > 0 || dropDownAlwaysVisible) && enoughToFilter()) {
             if (hasFocus() && hasWindowFocus()) {
                 showDropDown();
             }
-        } else if (!mDropDownAlwaysVisible) {
+        } else if (!dropDownAlwaysVisible) {
             dismissDropDown();
         }
     }
@@ -1029,7 +919,7 @@
     @Override
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         super.onWindowFocusChanged(hasWindowFocus);
-        if (!hasWindowFocus && !mDropDownAlwaysVisible) {
+        if (!hasWindowFocus && !mPopup.isDropDownAlwaysVisible()) {
             dismissDropDown();
         }
     }
@@ -1039,7 +929,7 @@
         super.onDisplayHint(hint);
         switch (hint) {
             case INVISIBLE:
-                if (!mDropDownAlwaysVisible) {
+                if (!mPopup.isDropDownAlwaysVisible()) {
                     dismissDropDown();
                 }
                 break;
@@ -1053,7 +943,7 @@
         if (!focused) {
             performValidation();
         }
-        if (!focused && !mDropDownAlwaysVisible) {
+        if (!focused && !mPopup.isDropDownAlwaysVisible()) {
             dismissDropDown();
         }
     }
@@ -1078,8 +968,6 @@
             imm.displayCompletions(this, null);
         }
         mPopup.dismiss();
-        mPopup.setContentView(null);
-        mDropDownList = null;
     }
 
     @Override
@@ -1092,18 +980,6 @@
 
         return result;
     }
-    
-    /**
-     * <p>Used for lazy instantiation of the anchor view from the id we have. If the value of
-     * the id is NO_ID or we can't find a view for the given id, we return this TextView as
-     * the default anchoring point.</p>
-     */
-    private View getDropDownAnchorView() {
-        if (mDropDownAnchorView == null && mDropDownAnchorId != View.NO_ID) {
-            mDropDownAnchorView = getRootView().findViewById(mDropDownAnchorId);
-        }
-        return mDropDownAnchorView == null ? this : mDropDownAnchorView;
-    }
 
     /**
      * Issues a runnable to show the dropdown as soon as possible.
@@ -1111,7 +987,7 @@
      * @hide internal used only by SearchDialog
      */
     public void showDropDownAfterLayout() {
-        post(mShowDropDownRunnable);
+        mPopup.postShow();
     }
     
     /**
@@ -1122,7 +998,7 @@
      */
     public void ensureImeVisible(boolean visible) {
         mPopup.setInputMethodMode(visible
-                ? PopupWindow.INPUT_METHOD_NEEDED : PopupWindow.INPUT_METHOD_NOT_NEEDED);
+                ? ListPopupWindow.INPUT_METHOD_NEEDED : ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
         showDropDown();
     }
 
@@ -1130,89 +1006,21 @@
      * @hide internal used only here and SearchDialog
      */
     public boolean isInputMethodNotNeeded() {
-        return mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
+        return mPopup.getInputMethodMode() == ListPopupWindow.INPUT_METHOD_NOT_NEEDED;
     }
 
     /**
      * <p>Displays the drop down on screen.</p>
      */
     public void showDropDown() {
-        int height = buildDropDown();
-
-        int widthSpec = 0;
-        int heightSpec = 0;
-
-        boolean noInputMethod = isInputMethodNotNeeded();
-
-        if (mPopup.isShowing()) {
-            if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
-                // The call to PopupWindow's update method below can accept -1 for any
-                // value you do not want to update.
-                widthSpec = -1;
-            } else if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                widthSpec = getDropDownAnchorView().getWidth();
+        if (mPopup.getAnchorView() == null) {
+            if (mDropDownAnchorId != View.NO_ID) {
+                mPopup.setAnchorView(getRootView().findViewById(mDropDownAnchorId));
             } else {
-                widthSpec = mDropDownWidth;
+                mPopup.setAnchorView(this);
             }
-
-            if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
-                // The call to PopupWindow's update method below can accept -1 for any
-                // value you do not want to update.
-                heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
-                if (noInputMethod) {
-                    mPopup.setWindowLayoutMode(
-                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
-                                    ViewGroup.LayoutParams.MATCH_PARENT : 0, 0);
-                } else {
-                    mPopup.setWindowLayoutMode(
-                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
-                                    ViewGroup.LayoutParams.MATCH_PARENT : 0,
-                            ViewGroup.LayoutParams.MATCH_PARENT);
-                }
-            } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                heightSpec = height;
-            } else {
-                heightSpec = mDropDownHeight;
-            }
-
-            mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
-
-            mPopup.update(getDropDownAnchorView(), mDropDownHorizontalOffset,
-                    mDropDownVerticalOffset, widthSpec, heightSpec);
-        } else {
-            if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
-                widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
-            } else {
-                if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                    mPopup.setWidth(getDropDownAnchorView().getWidth());
-                } else {
-                    mPopup.setWidth(mDropDownWidth);
-                }
-            }
-
-            if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
-                heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
-            } else {
-                if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                    mPopup.setHeight(height);
-                } else {
-                    mPopup.setHeight(mDropDownHeight);
-                }
-            }
-
-            mPopup.setWindowLayoutMode(widthSpec, heightSpec);
-            mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
-            
-            // use outside touchable to dismiss drop down when touching outside of it, so
-            // only set this if the dropdown is not always visible
-            mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
-            mPopup.setTouchInterceptor(new PopupTouchInterceptor());
-            mPopup.showAsDropDown(getDropDownAnchorView(),
-                    mDropDownHorizontalOffset, mDropDownVerticalOffset);
-            mDropDownList.setSelection(ListView.INVALID_POSITION);
-            clearListSelection();
-            post(mHideSelector);
         }
+        mPopup.show();
     }
     
     /**
@@ -1223,19 +1031,10 @@
      * @hide used only by SearchDialog
      */
     public void setForceIgnoreOutsideTouch(boolean forceIgnoreOutsideTouch) {
-        mForceIgnoreOutsideTouch = forceIgnoreOutsideTouch;
+        mPopup.setForceIgnoreOutsideTouch(forceIgnoreOutsideTouch);
     }
-
-    /**
-     * <p>Builds the popup window's content and returns the height the popup
-     * should have. Returns -1 when the content already exists.</p>
-     *
-     * @return the content's height or -1 if content already exists
-     */
-    private int buildDropDown() {
-        ViewGroup dropDownView;
-        int otherHeights = 0;
-
+    
+    private void buildImeCompletions() {
         final ListAdapter adapter = mAdapter;
         if (adapter != null) {
             InputMethodManager imm = InputMethodManager.peekInstance();
@@ -1263,135 +1062,6 @@
                 imm.displayCompletions(this, completions);
             }
         }
-
-        if (mDropDownList == null) {
-            Context context = getContext();
-
-            mHideSelector = new ListSelectorHider();
-
-            /**
-             * This Runnable exists for the sole purpose of checking if the view layout has got
-             * completed and if so call showDropDown to display the drop down. This is used to show
-             * the drop down as soon as possible after user opens up the search dialog, without
-             * waiting for the normal UI pipeline to do it's job which is slower than this method.
-             */
-            mShowDropDownRunnable = new Runnable() {
-                public void run() {
-                    // View layout should be all done before displaying the drop down.
-                    View view = getDropDownAnchorView();
-                    if (view != null && view.getWindowToken() != null) {
-                        showDropDown();
-                    }
-                }
-            };
-
-            mDropDownList = new DropDownListView(context);
-            mDropDownList.setSelector(mDropDownListHighlight);
-            mDropDownList.setAdapter(adapter);
-            mDropDownList.setVerticalFadingEdgeEnabled(true);
-            mDropDownList.setOnItemClickListener(mDropDownItemClickListener);
-            mDropDownList.setFocusable(true);
-            mDropDownList.setFocusableInTouchMode(true);
-            mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
-                public void onItemSelected(AdapterView<?> parent, View view,
-                        int position, long id) {
-
-                    if (position != -1) {
-                        DropDownListView dropDownList = mDropDownList;
-
-                        if (dropDownList != null) {
-                            dropDownList.mListSelectionHidden = false;
-                        }
-                    }
-                }
-
-                public void onNothingSelected(AdapterView<?> parent) {
-                }
-            });
-            mDropDownList.setOnScrollListener(new PopupScrollListener());
-
-            if (mItemSelectedListener != null) {
-                mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
-            }
-
-            dropDownView = mDropDownList;
-
-            View hintView = getHintView(context);
-            if (hintView != null) {
-                // if an hint has been specified, we accomodate more space for it and
-                // add a text view in the drop down menu, at the bottom of the list
-                LinearLayout hintContainer = new LinearLayout(context);
-                hintContainer.setOrientation(LinearLayout.VERTICAL);
-
-                LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f
-                );
-                hintContainer.addView(dropDownView, hintParams);
-                hintContainer.addView(hintView);
-
-                // measure the hint's height to find how much more vertical space
-                // we need to add to the drop down's height
-                int widthSpec = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST);
-                int heightSpec = MeasureSpec.UNSPECIFIED;
-                hintView.measure(widthSpec, heightSpec);
-
-                hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams();
-                otherHeights = hintView.getMeasuredHeight() + hintParams.topMargin
-                        + hintParams.bottomMargin;
-
-                dropDownView = hintContainer;
-            }
-
-            mPopup.setContentView(dropDownView);
-        } else {
-            dropDownView = (ViewGroup) mPopup.getContentView();
-            final View view = dropDownView.findViewById(HINT_VIEW_ID);
-            if (view != null) {
-                LinearLayout.LayoutParams hintParams =
-                        (LinearLayout.LayoutParams) view.getLayoutParams();
-                otherHeights = view.getMeasuredHeight() + hintParams.topMargin
-                        + hintParams.bottomMargin;
-            }
-        }
-
-        // Max height available on the screen for a popup.
-        boolean ignoreBottomDecorations =
-                mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
-        final int maxHeight = mPopup.getMaxAvailableHeight(
-                getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations);
-
-        // getMaxAvailableHeight() subtracts the padding, so we put it back,
-        // to get the available height for the whole window
-        int padding = 0;
-        Drawable background = mPopup.getBackground();
-        if (background != null) {
-            background.getPadding(mTempRect);
-            padding = mTempRect.top + mTempRect.bottom;
-        }
-
-        if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
-            return maxHeight + padding;
-        }
-
-        final int listContent = mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
-                0, ListView.NO_POSITION, maxHeight - otherHeights, 2);
-        // add padding only if the list has items in it, that way we don't show
-        // the popup if it is not needed
-        if (listContent > 0) otherHeights += padding;
-
-        return listContent + otherHeights;
-    }
-
-    private View getHintView(Context context) {
-        if (mHintText != null && mHintText.length() > 0) {
-            final TextView hintView = (TextView) LayoutInflater.from(context).inflate(
-                    mHintResource, null).findViewById(com.android.internal.R.id.text1);
-            hintView.setText(mHintText);
-            hintView.setId(HINT_VIEW_ID);
-            return hintView;
-        } else {
-            return null;
-        }
     }
 
     /**
@@ -1443,47 +1113,6 @@
         return mFilter;
     }
 
-    private class ListSelectorHider implements Runnable {
-        public void run() {
-            clearListSelection();
-        }
-    }
-
-    private class ResizePopupRunnable implements Runnable {
-        public void run() {
-            mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-            showDropDown();
-        }
-    }
-
-    private class PopupTouchInterceptor implements OnTouchListener {
-        public boolean onTouch(View v, MotionEvent event) {
-            final int action = event.getAction();
-            if (action == MotionEvent.ACTION_DOWN &&
-                    mPopup != null && mPopup.isShowing()) {
-                postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT);
-            } else if (action == MotionEvent.ACTION_UP) {
-                removeCallbacks(mResizePopupRunnable);
-            }
-            return false;
-        }
-    }
-    
-    private class PopupScrollListener implements ListView.OnScrollListener {
-        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
-                int totalItemCount) {
-
-        }
-
-        public void onScrollStateChanged(AbsListView view, int scrollState) {
-            if (scrollState == SCROLL_STATE_TOUCH_SCROLL &&
-                    !isInputMethodNotNeeded() && mPopup.getContentView() != null) {
-                removeCallbacks(mResizePopupRunnable);
-                mResizePopupRunnable.run();
-            }
-        }
-    }
-
     private class DropDownItemClickListener implements AdapterView.OnItemClickListener {
         public void onItemClick(AdapterView parent, View v, int position, long id) {
             performCompletion(v, position, id);
@@ -1491,123 +1120,6 @@
     }
 
     /**
-     * <p>Wrapper class for a ListView. This wrapper hijacks the focus to
-     * make sure the list uses the appropriate drawables and states when
-     * displayed on screen within a drop down. The focus is never actually
-     * passed to the drop down; the list only looks focused.</p>
-     */
-    private static class DropDownListView extends ListView {
-        /*
-         * WARNING: This is a workaround for a touch mode issue.
-         *
-         * Touch mode is propagated lazily to windows. This causes problems in
-         * the following scenario:
-         * - Type something in the AutoCompleteTextView and get some results
-         * - Move down with the d-pad to select an item in the list
-         * - Move up with the d-pad until the selection disappears
-         * - Type more text in the AutoCompleteTextView *using the soft keyboard*
-         *   and get new results; you are now in touch mode
-         * - The selection comes back on the first item in the list, even though
-         *   the list is supposed to be in touch mode
-         *
-         * Using the soft keyboard triggers the touch mode change but that change
-         * is propagated to our window only after the first list layout, therefore
-         * after the list attempts to resurrect the selection.
-         *
-         * The trick to work around this issue is to pretend the list is in touch
-         * mode when we know that the selection should not appear, that is when
-         * we know the user moved the selection away from the list.
-         *
-         * This boolean is set to true whenever we explicitely hide the list's
-         * selection and reset to false whenver we know the user moved the
-         * selection back to the list.
-         *
-         * When this boolean is true, isInTouchMode() returns true, otherwise it
-         * returns super.isInTouchMode().
-         */
-        private boolean mListSelectionHidden;
-
-        /**
-         * <p>Creates a new list view wrapper.</p>
-         *
-         * @param context this view's context
-         */
-        public DropDownListView(Context context) {
-            super(context, null, com.android.internal.R.attr.dropDownListViewStyle);
-        }
-
-        /**
-         * <p>Avoids jarring scrolling effect by ensuring that list elements
-         * made of a text view fit on a single line.</p>
-         *
-         * @param position the item index in the list to get a view for
-         * @return the view for the specified item
-         */
-        @Override
-        View obtainView(int position, boolean[] isScrap) {
-            View view = super.obtainView(position, isScrap);
-
-            if (view instanceof TextView) {
-                ((TextView) view).setHorizontallyScrolling(true);
-            }
-
-            return view;
-        }
-
-        @Override
-        public boolean isInTouchMode() {
-            // WARNING: Please read the comment where mListSelectionHidden is declared
-            return mListSelectionHidden || super.isInTouchMode();
-        }
-
-        /**
-         * <p>Returns the focus state in the drop down.</p>
-         *
-         * @return true always
-         */
-        @Override
-        public boolean hasWindowFocus() {
-            return true;
-        }
-
-        /**
-         * <p>Returns the focus state in the drop down.</p>
-         *
-         * @return true always
-         */
-        @Override
-        public boolean isFocused() {
-            return true;
-        }
-
-        /**
-         * <p>Returns the focus state in the drop down.</p>
-         *
-         * @return true always
-         */
-        @Override
-        public boolean hasFocus() {
-            return true;
-        }
-        
-        protected int[] onCreateDrawableState(int extraSpace) {
-            int[] res = super.onCreateDrawableState(extraSpace);
-            //noinspection ConstantIfStatement
-            if (false) {
-                StringBuilder sb = new StringBuilder("Created drawable state: [");
-                for (int i=0; i<res.length; i++) {
-                    if (i > 0) sb.append(", ");
-                    sb.append("0x");
-                    sb.append(Integer.toHexString(res[i]));
-                }
-                sb.append("]");
-                Log.i(TAG, sb.toString());
-            }
-            return res;
-        }
-    }
-
-    /**
      * This interface is used to make sure that the text entered in this TextView complies to
      * a certain format.  Since there is no foolproof way to prevent the user from leaving
      * this View with an incorrect value in it, all we can do is try to fix it ourselves
@@ -1655,10 +1167,7 @@
     private class PopupDataSetObserver extends DataSetObserver {
         @Override
         public void onChanged() {
-            if (isPopupShowing()) {
-                // This will resize the popup to fit the new adapter's content
-                showDropDown();
-            } else if (mAdapter != null) {
+            if (mAdapter != null) {
                 // If the popup is not showing already, showing it will cause
                 // the list of data set observers attached to the adapter to
                 // change. We can't do it from here, because we are in the middle
@@ -1673,14 +1182,5 @@
                 });
             }
         }
-
-        @Override
-        public void onInvalidated() {
-            if (!mDropDownAlwaysVisible) {
-                // There's no data to display so make sure we're not showing
-                // the drop down and its list
-                dismissDropDown();
-            }
-        }
     }
 }
diff --git a/core/java/android/widget/ButtonGroup.java b/core/java/android/widget/ButtonGroup.java
new file mode 100644
index 0000000..6af1c7e
--- /dev/null
+++ b/core/java/android/widget/ButtonGroup.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * @hide
+ */
+public class ButtonGroup extends LinearLayout {
+    private static final String LOG = "ButtonGroup";
+
+    private Drawable mDivider;
+    private int mDividerWidth;
+    private int mDividerHeight;
+    private int mButtonBackgroundRes;
+    private int mShowDividers;
+
+    /**
+     * Don't show any dividers.
+     */
+    public static final int SHOW_DIVIDER_NONE = 0;
+    /**
+     * Show a divider at the beginning of the group.
+     */
+    public static final int SHOW_DIVIDER_BEGINNING = 1;
+    /**
+     * Show dividers between each item in the group.
+     */
+    public static final int SHOW_DIVIDER_MIDDLE = 2;
+    /**
+     * Show a divider at the end of the group.
+     */
+    public static final int SHOW_DIVIDER_END = 4;
+
+    private final Rect mTempRect = new Rect();
+
+    public ButtonGroup(Context context) {
+        this(context, null);
+    }
+    
+    public ButtonGroup(Context context, AttributeSet attrs) {
+        this(context, attrs, com.android.internal.R.attr.buttonGroupStyle);
+    }
+    
+    public ButtonGroup(Context context, AttributeSet attrs, int defStyleRes) {
+        super(context, attrs, defStyleRes);
+        
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.ButtonGroup, defStyleRes, 0);
+        
+        setDividerDrawable(a.getDrawable(com.android.internal.R.styleable.ButtonGroup_divider));
+        mButtonBackgroundRes = a.getResourceId(
+                com.android.internal.R.styleable.ButtonGroup_buttonBackground, 0);
+        setShowDividers(a.getInt(com.android.internal.R.styleable.ButtonGroup_showDividers,
+                SHOW_DIVIDER_MIDDLE));
+        a.recycle();
+    }
+
+    /**
+     * Set how dividers should be shown between items in this button group.
+     *
+     * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
+     *                     {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END},
+     *                     or {@link #SHOW_DIVIDER_NONE} to show no dividers.
+     */
+    public void setShowDividers(int showDividers) {
+        if (showDividers != mShowDividers) {
+            requestLayout();
+        }
+        mShowDividers = showDividers;
+    }
+
+    /**
+     * @return A flag set indicating how dividers should be shown around items.
+     * @see #setShowDividers(int)
+     */
+    public int getShowDividers() {
+        return mShowDividers;
+    }
+
+    /**
+     * Set a drawable to be used as a divider between items.
+     * @param divider Drawable that will divide each item.
+     */
+    public void setDividerDrawable(Drawable divider) {
+        if (divider == mDivider) {
+            return;
+        }
+        mDivider = divider;
+        if (divider != null) {
+            mDividerWidth = divider.getIntrinsicWidth();
+            mDividerHeight = divider.getIntrinsicHeight();
+        } else {
+            mDividerWidth = 0;
+            mDividerHeight = 0;
+        }
+        requestLayout();
+    }
+
+    /**
+     * Retrieve the drawable used to draw dividers between items.
+     * @return The divider drawable
+     */
+    public Drawable getDividerDrawable() {
+        return mDivider;
+    }
+
+    @Override
+    public void addView(View child, int index, ViewGroup.LayoutParams params) {
+        if (child instanceof Button && mButtonBackgroundRes != 0) {
+            // Preserve original padding as we change the background
+            final int paddingLeft = child.getPaddingLeft();
+            final int paddingRight = child.getPaddingRight();
+            final int paddingTop = child.getPaddingTop();
+            final int paddingBottom = child.getPaddingBottom();
+            child.setBackgroundResource(mButtonBackgroundRes);
+            child.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
+        }
+
+        super.addView(child, index, params);
+    }
+
+    @Override
+    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        // Add the extra size that dividers contribute.
+        int dividerCount = 0;
+        if ((mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE) {
+            final int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                if (getChildAt(i).getVisibility() != GONE) {
+                    dividerCount++;
+                }
+            }
+            dividerCount = Math.max(0, dividerCount);
+        }
+        if ((mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING) {
+            dividerCount++;
+        }
+        if ((mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END) {
+            dividerCount++;
+        }
+
+        if (getOrientation() == VERTICAL) {
+            final int dividerSize = mDividerHeight * dividerCount;
+            setMeasuredDimension(getMeasuredWidth(),
+                    resolveSize(getMeasuredHeight() + dividerSize, heightMeasureSpec));
+        } else {
+            final int dividerSize = mDividerWidth * dividerCount;
+            setMeasuredDimension(resolveSize(getMeasuredWidth() + dividerSize, widthMeasureSpec),
+                    getMeasuredHeight());
+        }
+    }
+
+    @Override
+    public void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+
+        final boolean begin = (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING;
+        final boolean middle = (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
+
+        // Offset children to leave space for dividers.
+        if (getOrientation() == VERTICAL) {
+            int offset = begin ? mDividerHeight : 0;
+            final int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                View child = getChildAt(i);
+                child.offsetTopAndBottom(offset);
+                if (middle && child.getVisibility() != GONE) {
+                    offset += mDividerHeight;
+                }
+            }
+        } else {
+            int offset = begin ? mDividerWidth : 0;
+            final int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                View child = getChildAt(i);
+                child.offsetLeftAndRight(offset);
+                if (middle && child.getVisibility() != GONE) {
+                    offset += mDividerWidth;
+                }
+            }
+        }
+    }
+
+    @Override
+    public void dispatchDraw(Canvas canvas) {
+        if (mDivider == null) {
+            super.dispatchDraw(canvas);
+            return;
+        }
+
+        final boolean begin = (mShowDividers & SHOW_DIVIDER_BEGINNING) == SHOW_DIVIDER_BEGINNING;
+        final boolean middle = (mShowDividers & SHOW_DIVIDER_MIDDLE) == SHOW_DIVIDER_MIDDLE;
+        final boolean end = (mShowDividers & SHOW_DIVIDER_END) == SHOW_DIVIDER_END;
+        final boolean vertical = getOrientation() == VERTICAL;
+
+        final Rect bounds = mTempRect;
+        bounds.left = mPaddingLeft;
+        bounds.right = mRight - mLeft - mPaddingRight;
+        bounds.top = mPaddingTop;
+        bounds.bottom = mBottom - mTop - mPaddingBottom;
+
+        if (begin) {
+            if (vertical) {
+                bounds.bottom = bounds.top + mDividerHeight;
+            } else {
+                bounds.right = bounds.left + mDividerWidth;
+            }
+            mDivider.setBounds(bounds);
+            mDivider.draw(canvas);
+        }
+
+        final int childCount = getChildCount();
+        int i = 0;
+        while (i < childCount) {
+            final View child = getChildAt(i);
+            i++;
+            if ((middle && i < childCount && child.getVisibility() != GONE) || end) {
+                if (vertical) {
+                    bounds.top = child.getBottom();
+                    bounds.bottom = bounds.top + mDividerHeight;
+                } else {
+                    bounds.left = child.getRight();
+                    bounds.right = bounds.left + mDividerWidth;
+                }
+                mDivider.setBounds(bounds);
+                mDivider.draw(canvas);
+            }
+        }
+
+        super.dispatchDraw(canvas);
+    }
+}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index bf02ad3..0df45cc 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -279,6 +279,12 @@
         return super.verifyDrawable(who) || who == mButtonDrawable;
     }
 
+    @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+        if (mButtonDrawable != null) mButtonDrawable.jumpToCurrentState();
+    }
+
     static class SavedState extends BaseSavedState {
         boolean checked;
 
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index baa6833..4cf8785 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -80,6 +80,18 @@
     protected FilterQueryProvider mFilterQueryProvider;
 
     /**
+     * If set the adapter will call requery() on the cursor whenever a content change
+     * notification is delivered. Implies {@link #FLAG_REGISTER_CONTENT_OBSERVER}
+     */
+    public static final int FLAG_AUTO_REQUERY = 0x01;
+
+    /**
+     * If set the adapter will register a content observer on the cursor and will call
+     * {@link #onContentChanged()} when a notification comes in.
+     */
+    public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02;
+
+    /**
      * Constructor. The adapter will call requery() on the cursor whenever
      * it changes so that the most recent data is always displayed.
      *
@@ -87,7 +99,7 @@
      * @param context The context
      */
     public CursorAdapter(Context context, Cursor c) {
-        init(context, c, true);
+        init(context, c, FLAG_AUTO_REQUERY);
     }
 
     /**
@@ -99,19 +111,43 @@
      *                    data is always displayed.
      */
     public CursorAdapter(Context context, Cursor c, boolean autoRequery) {
-        init(context, c, autoRequery);
+        init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
+    }
+
+    /**
+     * Constructor
+     * @param c The cursor from which to get the data.
+     * @param context The context
+     * @param flags flags used to determine the behavior of the adapter
+     */
+    public CursorAdapter(Context context, Cursor c, int flags) {
+        init(context, c, flags);
     }
 
     protected void init(Context context, Cursor c, boolean autoRequery) {
+        init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
+    }
+
+    protected void init(Context context, Cursor c, int flags) {
+        if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) {
+            flags |= FLAG_REGISTER_CONTENT_OBSERVER;
+            mAutoRequery = true;
+        } else {
+            mAutoRequery = false;
+        }
         boolean cursorPresent = c != null;
-        mAutoRequery = autoRequery;
         mCursor = c;
         mDataValid = cursorPresent;
         mContext = context;
         mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
-        mChangeObserver = new ChangeObserver();
+        if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
+            mChangeObserver = new ChangeObserver();
+        } else {
+            mChangeObserver = null;
+        }
+
         if (cursorPresent) {
-            c.registerContentObserver(mChangeObserver);
+            if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);
             c.registerDataSetObserver(mDataSetObserver);
         }
     }
@@ -246,13 +282,13 @@
             return;
         }
         if (mCursor != null) {
-            mCursor.unregisterContentObserver(mChangeObserver);
+            if (mChangeObserver != null) mCursor.unregisterContentObserver(mChangeObserver);
             mCursor.unregisterDataSetObserver(mDataSetObserver);
             mCursor.close();
         }
         mCursor = cursor;
         if (cursor != null) {
-            cursor.registerContentObserver(mChangeObserver);
+            if (mChangeObserver != null) cursor.registerContentObserver(mChangeObserver);
             cursor.registerDataSetObserver(mDataSetObserver);
             mRowIDColumn = cursor.getColumnIndexOrThrow("_id");
             mDataValid = true;
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeGlow.java
deleted file mode 100644
index 416be86..0000000
--- a/core/java/android/widget/EdgeGlow.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.widget;
-
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-/**
- * This class performs the glow effect used at the edges of scrollable widgets.
- * @hide
- */
-public class EdgeGlow {
-    private static final String TAG = "EdgeGlow";
-
-    // Time it will take the effect to fully recede in ms
-    private static final int RECEDE_TIME = 1000;
-
-    // Time it will take before a pulled glow begins receding
-    private static final int PULL_TIME = 167;
-
-    // Time it will take for a pulled glow to decay to partial strength before release
-    private static final int PULL_DECAY_TIME = 1000;
-
-    private static final float MAX_ALPHA = 0.8f;
-    private static final float HELD_EDGE_ALPHA = 0.7f;
-    private static final float HELD_EDGE_SCALE_Y = 0.5f;
-    private static final float HELD_GLOW_ALPHA = 0.5f;
-    private static final float HELD_GLOW_SCALE_Y = 0.5f;
-
-    private static final float MAX_GLOW_HEIGHT = 3.f;
-
-    private static final float PULL_GLOW_BEGIN = 1.f;
-    private static final float PULL_EDGE_BEGIN = 0.6f;
-
-    // Minimum velocity that will be absorbed
-    private static final int MIN_VELOCITY = 100;
-
-    private static final float EPSILON = 0.001f;
-
-    private final Drawable mEdge;
-    private final Drawable mGlow;
-    private int mWidth;
-    private int mHeight;
-
-    private float mEdgeAlpha;
-    private float mEdgeScaleY;
-    private float mGlowAlpha;
-    private float mGlowScaleY;
-
-    private float mEdgeAlphaStart;
-    private float mEdgeAlphaFinish;
-    private float mEdgeScaleYStart;
-    private float mEdgeScaleYFinish;
-    private float mGlowAlphaStart;
-    private float mGlowAlphaFinish;
-    private float mGlowScaleYStart;
-    private float mGlowScaleYFinish;
-
-    private long mStartTime;
-    private float mDuration;
-
-    private final Interpolator mInterpolator;
-
-    private static final int STATE_IDLE = 0;
-    private static final int STATE_PULL = 1;
-    private static final int STATE_ABSORB = 2;
-    private static final int STATE_RECEDE = 3;
-    private static final int STATE_PULL_DECAY = 4;
-
-    // How much dragging should effect the height of the edge image.
-    // Number determined by user testing.
-    private static final int PULL_DISTANCE_EDGE_FACTOR = 5;
-
-    // How much dragging should effect the height of the glow image.
-    // Number determined by user testing.
-    private static final int PULL_DISTANCE_GLOW_FACTOR = 5;
-    private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 0.8f;
-
-    private static final int VELOCITY_EDGE_FACTOR = 8;
-    private static final int VELOCITY_GLOW_FACTOR = 16;
-
-    private int mState = STATE_IDLE;
-
-    private float mPullDistance;
-
-    public EdgeGlow(Drawable edge, Drawable glow) {
-        mEdge = edge;
-        mGlow = glow;
-
-        mInterpolator = new DecelerateInterpolator();
-    }
-
-    public void setSize(int width, int height) {
-        mWidth = width;
-        mHeight = height;
-    }
-
-    public boolean isFinished() {
-        return mState == STATE_IDLE;
-    }
-
-    public void finish() {
-        mState = STATE_IDLE;
-    }
-
-    /**
-     * Call when the object is pulled by the user.
-     *
-     * @param deltaDistance Change in distance since the last call
-     */
-    public void onPull(float deltaDistance) {
-        final long now = AnimationUtils.currentAnimationTimeMillis();
-        if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
-            return;
-        }
-        if (mState != STATE_PULL) {
-            mGlowScaleY = PULL_GLOW_BEGIN;
-        }
-        mState = STATE_PULL;
-
-        mStartTime = now;
-        mDuration = PULL_TIME;
-
-        mPullDistance += deltaDistance;
-        float distance = Math.abs(mPullDistance);
-
-        mEdgeAlpha = mEdgeAlphaStart = Math.max(PULL_EDGE_BEGIN, Math.min(distance, MAX_ALPHA));
-        mEdgeScaleY = mEdgeScaleYStart = Math.max(
-                HELD_EDGE_SCALE_Y, Math.min(distance * PULL_DISTANCE_EDGE_FACTOR, 1.f));
-
-        mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
-                mGlowAlpha +
-                (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
-
-        float glowChange = Math.abs(deltaDistance);
-        if (deltaDistance > 0 && mPullDistance < 0) {
-            glowChange = -glowChange;
-        }
-        if (mPullDistance == 0) {
-            mGlowScaleY = 0;
-        }
-
-        // Do not allow glow to get larger than MAX_GLOW_HEIGHT.
-        mGlowScaleY = mGlowScaleYStart = Math.min(MAX_GLOW_HEIGHT, Math.max(
-                0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR));
-
-        mEdgeAlphaFinish = mEdgeAlpha;
-        mEdgeScaleYFinish = mEdgeScaleY;
-        mGlowAlphaFinish = mGlowAlpha;
-        mGlowScaleYFinish = mGlowScaleY;
-    }
-
-    /**
-     * Call when the object is released after being pulled.
-     */
-    public void onRelease() {
-        mPullDistance = 0;
-
-        if (mState != STATE_PULL && mState != STATE_PULL_DECAY) {
-            return;
-        }
-
-        mState = STATE_RECEDE;
-        mEdgeAlphaStart = mEdgeAlpha;
-        mEdgeScaleYStart = mEdgeScaleY;
-        mGlowAlphaStart = mGlowAlpha;
-        mGlowScaleYStart = mGlowScaleY;
-
-        mEdgeAlphaFinish = 0.f;
-        mEdgeScaleYFinish = 0.f;
-        mGlowAlphaFinish = 0.f;
-        mGlowScaleYFinish = 0.f;
-
-        mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        mDuration = RECEDE_TIME;
-    }
-
-    /**
-     * Call when the effect absorbs an impact at the given velocity.
-     *
-     * @param velocity Velocity at impact in pixels per second.
-     */
-    public void onAbsorb(int velocity) {
-        mState = STATE_ABSORB;
-        velocity = Math.max(MIN_VELOCITY, Math.abs(velocity));
-
-        mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        mDuration = 0.1f + (velocity * 0.03f);
-
-        // The edge should always be at least partially visible, regardless
-        // of velocity.
-        mEdgeAlphaStart = 0.f;
-        mEdgeScaleY = mEdgeScaleYStart = 0.f;
-        // The glow depends more on the velocity, and therefore starts out
-        // nearly invisible.
-        mGlowAlphaStart = 0.5f;
-        mGlowScaleYStart = 0.f;
-
-        // Factor the velocity by 8. Testing on device shows this works best to
-        // reflect the strength of the user's scrolling.
-        mEdgeAlphaFinish = Math.max(0, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1));
-        // Edge should never get larger than the size of its asset.
-        mEdgeScaleYFinish = Math.max(
-                HELD_EDGE_SCALE_Y, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1.f));
-
-        // Growth for the size of the glow should be quadratic to properly
-        // respond
-        // to a user's scrolling speed. The faster the scrolling speed, the more
-        // intense the effect should be for both the size and the saturation.
-        mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
-        // Alpha should change for the glow as well as size.
-        mGlowAlphaFinish = Math.max(
-                mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
-    }
-
-
-    /**
-     * Draw into the provided canvas. Assumes that the canvas has been rotated
-     * accordingly and the size has been set. The effect will be drawn the full
-     * width of X=0 to X=width, emitting from Y=0 and extending to some factor <
-     * 1.f of height.
-     *
-     * @param canvas Canvas to draw into
-     * @return true if drawing should continue beyond this frame to continue the
-     *         animation
-     */
-    public boolean draw(Canvas canvas) {
-        update();
-
-        final int edgeHeight = mEdge.getIntrinsicHeight();
-        final int glowHeight = mGlow.getIntrinsicHeight();
-
-        final float distScale = (float) mHeight / mWidth;
-
-        mGlow.setAlpha((int) (Math.max(0, Math.min(mGlowAlpha, 1)) * 255));
-        // Width of the image should be 3 * the width of the screen.
-        // Should start off screen to the left.
-        mGlow.setBounds(-mWidth, 0, mWidth * 2, (int) Math.min(
-                glowHeight * mGlowScaleY * distScale * 0.6f, mHeight * MAX_GLOW_HEIGHT));
-        mGlow.draw(canvas);
-
-        mEdge.setAlpha((int) (Math.max(0, Math.min(mEdgeAlpha, 1)) * 255));
-        mEdge.setBounds(0, 0, mWidth, (int) (edgeHeight * mEdgeScaleY));
-        mEdge.draw(canvas);
-
-        return mState != STATE_IDLE;
-    }
-
-    private void update() {
-        final long time = AnimationUtils.currentAnimationTimeMillis();
-        final float t = Math.min((time - mStartTime) / mDuration, 1.f);
-
-        final float interp = mInterpolator.getInterpolation(t);
-
-        mEdgeAlpha = mEdgeAlphaStart + (mEdgeAlphaFinish - mEdgeAlphaStart) * interp;
-        mEdgeScaleY = mEdgeScaleYStart + (mEdgeScaleYFinish - mEdgeScaleYStart) * interp;
-        mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
-        mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
-
-        if (t >= 1.f - EPSILON) {
-            switch (mState) {
-                case STATE_ABSORB:
-                    mState = STATE_RECEDE;
-                    mStartTime = AnimationUtils.currentAnimationTimeMillis();
-                    mDuration = RECEDE_TIME;
-
-                    mEdgeAlphaStart = mEdgeAlpha;
-                    mEdgeScaleYStart = mEdgeScaleY;
-                    mGlowAlphaStart = mGlowAlpha;
-                    mGlowScaleYStart = mGlowScaleY;
-
-                    // After absorb, the glow and edge should fade to nothing.
-                    mEdgeAlphaFinish = 0.f;
-                    mEdgeScaleYFinish = 0.f;
-                    mGlowAlphaFinish = 0.f;
-                    mGlowScaleYFinish = 0.f;
-                    break;
-                case STATE_PULL:
-                    mState = STATE_PULL_DECAY;
-                    mStartTime = AnimationUtils.currentAnimationTimeMillis();
-                    mDuration = PULL_DECAY_TIME;
-
-                    mEdgeAlphaStart = mEdgeAlpha;
-                    mEdgeScaleYStart = mEdgeScaleY;
-                    mGlowAlphaStart = mGlowAlpha;
-                    mGlowScaleYStart = mGlowScaleY;
-
-                    // After pull, the glow and edge should fade to nothing.
-                    mEdgeAlphaFinish = 0.f;
-                    mEdgeScaleYFinish = 0.f;
-                    mGlowAlphaFinish = 0.f;
-                    mGlowScaleYFinish = 0.f;
-                    break;
-                case STATE_PULL_DECAY:
-                    // When receding, we want edge to decrease more slowly
-                    // than the glow.
-                    float factor = mGlowScaleYFinish != 0 ? 1
-                            / (mGlowScaleYFinish * mGlowScaleYFinish)
-                            : Float.MAX_VALUE;
-                    mEdgeScaleY = mEdgeScaleYStart +
-                        (mEdgeScaleYFinish - mEdgeScaleYStart) *
-                            interp * factor;
-                    break;
-                case STATE_RECEDE:
-                    mState = STATE_IDLE;
-                    break;
-            }
-        }
-    }
-}
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index 8bd797b..3d21048 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -185,7 +185,6 @@
     
     /** Drawable to be used as a divider when it is adjacent to any children */
     private Drawable mChildDivider;
-    private boolean mClipChildDivider;
 
     // Bounds of the indicator to be drawn
     private final Rect mIndicatorRect = new Rect();
@@ -379,7 +378,6 @@
      */
     public void setChildDivider(Drawable childDivider) {
         mChildDivider = childDivider;
-        mClipChildDivider = childDivider != null && childDivider instanceof ColorDrawable;
     }
 
     @Override
@@ -396,17 +394,8 @@
                     pos.groupMetadata.lastChildFlPos != pos.groupMetadata.flPos)) {
                 // These are the cases where we draw the child divider
                 final Drawable divider = mChildDivider;
-                final boolean clip = mClipChildDivider;
-                if (!clip) {
-                    divider.setBounds(bounds);
-                } else {
-                    canvas.save();
-                    canvas.clipRect(bounds);
-                }
+                divider.setBounds(bounds);
                 divider.draw(canvas);
-                if (clip) {
-                    canvas.restore();
-                }
                 pos.recycle();
                 return;
             }
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index e445180..bcab7a9 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -32,8 +32,8 @@
 
 /**
  * FrameLayout is designed to block out an area on the screen to display
- * a single item. You can add multiple children to a FrameLayout, but all
- * children are pegged to the top left of the screen.
+ * a single item. You can add multiple children to a FrameLayout and control their
+ * position within the FrameLayout using {@link android.widget.FrameLayout.LayoutParams#gravity}.
  * Children are drawn in a stack, with the most recently added child on top.
  * The size of the frame layout is the size of its largest child (plus padding), visible
  * or not (if the FrameLayout's parent permits). Views that are GONE are used for sizing
@@ -156,6 +156,12 @@
         return super.verifyDrawable(who) || (who == mForeground);
     }
 
+    @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+        if (mForeground != null) mForeground.jumpToCurrentState();
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -440,6 +446,8 @@
      * Per-child layout information for layouts that support margins.
      * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
      * for a list of all child view attributes that this class supports.
+     * 
+     * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
      */
     public static class LayoutParams extends MarginLayoutParams {
         /**
@@ -447,6 +455,8 @@
          * are associated.
          *
          * @see android.view.Gravity
+         * 
+         * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
          */
         public int gravity = -1;
 
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index ccd37d3..9789658 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -1210,7 +1210,7 @@
 
         // We unfocus the old child down here so the above hasFocus check
         // returns true
-        if (oldSelectedChild != null) {
+        if (oldSelectedChild != null && oldSelectedChild != child) {
 
             // Make sure its drawable state doesn't contain 'selected'
             oldSelectedChild.setSelected(false);
@@ -1266,6 +1266,7 @@
          */
         if (gainFocus && mSelectedChild != null) {
             mSelectedChild.requestFocus(direction);
+            mSelectedChild.setSelected(true);
         }
 
     }
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index a7300c2..b963536 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -17,15 +17,18 @@
 package android.widget;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
 import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
 import android.view.animation.GridLayoutAnimationController;
+import android.widget.RemoteViews.RemoteView;
 
 
 /**
@@ -35,6 +38,7 @@
  * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-gridview.html">Grid
  * View tutorial</a>.</p>
  */
+@RemoteView
 public class GridView extends AbsListView {
     public static final int NO_STRETCH = 0;
     public static final int STRETCH_SPACING = 1;
@@ -109,13 +113,23 @@
     }
 
     /**
+     * Sets up this AbsListView to use a remote views adapter which connects to a RemoteViewsService
+     * through the specified intent.
+     * @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
+     */
+    @android.view.RemotableViewMethod
+    public void setRemoteViewsAdapter(Intent intent) {
+        super.setRemoteViewsAdapter(intent);
+    }
+
+    /**
      * Sets the data behind this GridView.
      *
      * @param adapter the adapter providing the grid's data
      */
     @Override
     public void setAdapter(ListAdapter adapter) {
-        if (null != mAdapter) {
+        if (mAdapter != null && mDataSetObserver != null) {
             mAdapter.unregisterDataSetObserver(mDataSetObserver);
         }
 
@@ -125,7 +139,10 @@
 
         mOldSelectedPosition = INVALID_POSITION;
         mOldSelectedRowId = INVALID_ROW_ID;
-        
+
+        // AbsListView#setAdapter will update choice mode states.
+        super.setAdapter(adapter);
+
         if (mAdapter != null) {
             mOldItemCount = mItemCount;
             mItemCount = mAdapter.getCount();
@@ -742,6 +759,26 @@
     }
 
     /**
+     * Smoothly scroll to the specified adapter position. The view will
+     * scroll such that the indicated position is displayed.
+     * @param position Scroll to this adapter position.
+     */
+    @android.view.RemotableViewMethod
+    public void smoothScrollToPosition(int position) {
+        super.smoothScrollToPosition(position);
+    }
+
+    /**
+     * Smoothly scroll to the specified adapter position offset. The view will
+     * scroll such that the indicated position is displayed.
+     * @param offset The amount to offset from the adapter position to scroll to.
+     */
+    @android.view.RemotableViewMethod
+    public void smoothScrollByOffset(int offset) {
+        super.smoothScrollByOffset(offset);
+    }
+
+    /**
      * Fills the grid based on positioning the new selection relative to the old
      * selection. The new selection will be placed at, above, or below the
      * location of the new selection depending on how the selection is moving.
@@ -952,7 +989,7 @@
             childHeight = child.getMeasuredHeight();
 
             if (mRecycler.shouldRecycleViewType(p.viewType)) {
-                mRecycler.addScrapView(child);
+                mRecycler.addScrapView(child, -1);
             }
         }
         
@@ -1091,7 +1128,7 @@
 
             if (dataChanged) {
                 for (int i = 0; i < childCount; i++) {
-                    recycleBin.addScrapView(getChildAt(i));
+                    recycleBin.addScrapView(getChildAt(i), firstPosition+i);
                 }
             } else {
                 recycleBin.fillActiveViews(childCount, firstPosition);
@@ -1158,11 +1195,11 @@
             recycleBin.scrapActiveViews();
 
             if (sel != null) {
-               positionSelector(sel);
+               positionSelector(INVALID_POSITION, sel);
                mSelectedTop = sel.getTop();
             } else if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) {
                 View child = getChildAt(mMotionPosition - mFirstPosition);
-                if (child != null) positionSelector(child);                
+                if (child != null) positionSelector(mMotionPosition, child);
             } else {
                 mSelectedTop = 0;
                 mSelectorRect.setEmpty();
@@ -1281,6 +1318,15 @@
             child.setPressed(isPressed);
         }
 
+        if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
+            if (child instanceof Checkable) {
+                ((Checkable) child).setChecked(mCheckStates.get(position));
+            } else if (getContext().getApplicationInfo().targetSdkVersion
+                    >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+                child.setActivated(mCheckStates.get(position));
+            }
+        }
+
         if (needToMeasure) {
             int childHeightSpec = ViewGroup.getChildMeasureSpec(
                     MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height);
@@ -1325,6 +1371,11 @@
         if (mCachingStarted) {
             child.setDrawingCacheEnabled(true);
         }
+
+        if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)
+                != position) {
+            child.jumpDrawablesToCurrentState();
+        }
     }
 
     /**
@@ -1491,9 +1542,9 @@
         int nextPage = -1;
 
         if (direction == FOCUS_UP) {
-            nextPage = Math.max(0, mSelectedPosition - getChildCount() - 1);
+            nextPage = Math.max(0, mSelectedPosition - getChildCount());
         } else if (direction == FOCUS_DOWN) {
-            nextPage = Math.min(mItemCount - 1, mSelectedPosition + getChildCount() - 1);
+            nextPage = Math.min(mItemCount - 1, mSelectedPosition + getChildCount());
         }
 
         if (nextPage >= 0) {
@@ -1777,6 +1828,19 @@
             requestLayoutIfNecessary();
         }
     }
+    
+    /**
+     * Get the number of columns in the grid. 
+     * Returns {@link #AUTO_FIT} if the Grid has never been laid out.
+     *
+     * @attr ref android.R.styleable#GridView_numColumns
+     * 
+     * @see #setNumColumns(int)
+     */
+    @ViewDebug.ExportedProperty
+    public int getNumColumns() {  
+        return mNumColumns;
+    }
 
     /**
      * Make sure views are touching the top or bottom edge, as appropriate for
@@ -1876,12 +1940,7 @@
         // TODO: Account for vertical spacing too
         final int numColumns = mNumColumns;
         final int rowCount = (mItemCount + numColumns - 1) / numColumns;
-        int result = Math.max(rowCount * 100, 0);
-        if (mScrollY != 0) {
-            // Compensate for overscroll
-            result += Math.abs((int) ((float) mScrollY / getHeight() * rowCount * 100));
-        }
-        return result;
+        return Math.max(rowCount * 100, 0);
     }
 }
 
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index fee3455..e30d4c8 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -16,24 +16,19 @@
 
 package android.widget;
 
-import com.android.internal.R;
-
-import android.util.AttributeSet;
-import android.graphics.Canvas;
+import android.content.Context;
+import android.content.res.TypedArray;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.view.View;
+import android.util.AttributeSet;
+import android.view.FocusFinder;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.VelocityTracker;
+import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
-import android.view.KeyEvent;
-import android.view.FocusFinder;
-import android.view.MotionEvent;
 import android.view.ViewParent;
 import android.view.animation.AnimationUtils;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
 
 import java.util.List;
 
@@ -58,6 +53,8 @@
  * within a larger container.
  *
  * <p>HorizontalScrollView only supports horizontal scrolling.
+ * 
+ * @attr ref android.R.styleable#HorizontalScrollView_fillViewport 
  */
 public class HorizontalScrollView extends FrameLayout {
     private static final int ANIMATED_SCROLL_GAP = ScrollView.ANIMATED_SCROLL_GAP;
@@ -68,9 +65,7 @@
     private long mLastScroll;
 
     private final Rect mTempRect = new Rect();
-    private OverScroller mScroller;
-    private EdgeGlow mEdgeGlowLeft;
-    private EdgeGlow mEdgeGlowRight;
+    private Scroller mScroller;
 
     /**
      * Flag to indicate that we are moving focus ourselves. This is so the
@@ -124,9 +119,6 @@
     private int mMinimumVelocity;
     private int mMaximumVelocity;
     
-    private int mOverscrollDistance;
-    private int mOverflingDistance;
-
     /**
      * ID of the active pointer. This is used to retain consistency during
      * drags/flings if multiple pointers are used.
@@ -199,7 +191,7 @@
 
 
     private void initScrollView() {
-        mScroller = new OverScroller(getContext());
+        mScroller = new Scroller(getContext());
         setFocusable(true);
         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
         setWillNotDraw(false);
@@ -207,8 +199,6 @@
         mTouchSlop = configuration.getScaledTouchSlop();
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-        mOverscrollDistance = configuration.getScaledOverscrollDistance();
-        mOverflingDistance = configuration.getScaledOverflingDistance();
     }
 
     @Override
@@ -260,20 +250,25 @@
     }
 
     /**
-     * Indicates whether this ScrollView's content is stretched to fill the viewport.
+     * Indicates whether this HorizontalScrollView's content is stretched to
+     * fill the viewport.
      *
      * @return True if the content fills the viewport, false otherwise.
+     *
+     * @attr ref android.R.styleable#HorizontalScrollView_fillViewport
      */
     public boolean isFillViewport() {
         return mFillViewport;
     }
 
     /**
-     * Indicates this ScrollView whether it should stretch its content width to fill
-     * the viewport or not.
+     * Indicates this HorizontalScrollView whether it should stretch its content width
+     * to fill the viewport or not.
      *
      * @param fillViewport True to stretch the content's width to the viewport's
      *        boundaries, false otherwise.
+     * 
+     * @attr ref android.R.styleable#HorizontalScrollView_fillViewport
      */
     public void setFillViewport(boolean fillViewport) {
         if (fillViewport != mFillViewport) {
@@ -468,9 +463,6 @@
                 /* Release the drag */
                 mIsBeingDragged = false;
                 mActivePointerId = INVALID_POINTER;
-                if (mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) {
-                    invalidate();
-                }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
                 onSecondaryPointerUp(ev);
@@ -503,7 +495,9 @@
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
                 final float x = ev.getX();
-                mIsBeingDragged = true;
+                if (!(mIsBeingDragged = inChild((int) x, (int) ev.getY()))) {
+                    return false;
+                }
 
                 /*
                  * If being flinged and user touches, stop the fling. isFinished
@@ -526,36 +520,7 @@
                     final int deltaX = (int) (mLastMotionX - x);
                     mLastMotionX = x;
 
-                    final int oldX = mScrollX;
-                    final int oldY = mScrollY;
-                    final int range = getScrollRange();
-                    if (overScrollBy(deltaX, 0, mScrollX, 0, range, 0,
-                            mOverscrollDistance, 0, true)) {
-                        // Break our velocity if we hit a scroll barrier.
-                        mVelocityTracker.clear();
-                    }
-                    onScrollChanged(mScrollX, mScrollY, oldX, oldY);
-
-                    final int overscrollMode = getOverScrollMode();
-                    if (overscrollMode == OVER_SCROLL_ALWAYS ||
-                            (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
-                        final int pulledToX = oldX + deltaX;
-                        if (pulledToX < 0) {
-                            mEdgeGlowLeft.onPull((float) deltaX / getWidth());
-                            if (!mEdgeGlowRight.isFinished()) {
-                                mEdgeGlowRight.onRelease();
-                            }
-                        } else if (pulledToX > range) {
-                            mEdgeGlowRight.onPull((float) deltaX / getWidth());
-                            if (!mEdgeGlowLeft.isFinished()) {
-                                mEdgeGlowLeft.onRelease();
-                            }
-                        }
-                        if (mEdgeGlowLeft != null
-                                && (!mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished())) {
-                            invalidate();
-                        }
-                    }
+                    scrollBy(deltaX, 0);
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -564,15 +529,8 @@
                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                     int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
 
-                    if (getChildCount() > 0) {
-                        if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
-                            fling(-initialVelocity);
-                        } else {
-                            final int right = getScrollRange();
-                            if (mScroller.springBack(mScrollX, mScrollY, 0, right, 0, 0)) {
-                                invalidate();
-                            }
-                        }
+                    if (getChildCount() > 0 && Math.abs(initialVelocity) > mMinimumVelocity) {
+                        fling(-initialVelocity);
                     }
                     
                     mActivePointerId = INVALID_POINTER;
@@ -582,27 +540,16 @@
                         mVelocityTracker.recycle();
                         mVelocityTracker = null;
                     }
-                    if (mEdgeGlowLeft != null) {
-                        mEdgeGlowLeft.onRelease();
-                        mEdgeGlowRight.onRelease();
-                    }
                 }
                 break;
             case MotionEvent.ACTION_CANCEL:
                 if (mIsBeingDragged && getChildCount() > 0) {
-                    if (mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) {
-                        invalidate();
-                    }
                     mActivePointerId = INVALID_POINTER;
                     mIsBeingDragged = false;
                     if (mVelocityTracker != null) {
                         mVelocityTracker.recycle();
                         mVelocityTracker = null;
                     }
-                    if (mEdgeGlowLeft != null) {
-                        mEdgeGlowLeft.onRelease();
-                        mEdgeGlowRight.onRelease();
-                    }
                 }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
@@ -629,28 +576,12 @@
         }
     }
     
-    @Override
-    protected void onOverScrolled(int scrollX, int scrollY,
-            boolean clampedX, boolean clampedY) {
-        // Treat animating scrolls differently; see #computeScroll() for why.
-        if (!mScroller.isFinished()) {
-            mScrollX = scrollX;
-            mScrollY = scrollY;
-            if (clampedX) {
-                mScroller.springBack(mScrollX, mScrollY, 0, getScrollRange(), 0, 0);
-            }
-        } else {
-            super.scrollTo(scrollX, scrollY);
-        }
-        awakenScrollBars();
-    }
-
     private int getScrollRange() {
         int scrollRange = 0;
         if (getChildCount() > 0) {
             View child = getChildAt(0);
             scrollRange = Math.max(0,
-                    child.getWidth() - (getWidth() - mPaddingLeft - mPaddingRight));
+                    child.getWidth() - getWidth() - mPaddingLeft - mPaddingRight);
         }
         return scrollRange;
     }
@@ -1027,16 +958,7 @@
             return contentWidth;
         }
         
-        int scrollRange = getChildAt(0).getRight();
-        final int scrollX = mScrollX;
-        final int overscrollRight = Math.max(0, scrollRange - contentWidth);
-        if (scrollX < 0) {
-            scrollRange -= scrollX;
-        } else if (scrollX > overscrollRight) {
-            scrollRange += scrollX - overscrollRight;
-        }
-
-        return scrollRange;
+        return getChildAt(0).getRight();
     }
     
     @Override
@@ -1097,20 +1019,14 @@
             int x = mScroller.getCurrX();
             int y = mScroller.getCurrY();
 
-            if (oldX != x || oldY != y) {
-                overScrollBy(x - oldX, y - oldY, oldX, oldY, getScrollRange(), 0,
-                        mOverflingDistance, 0, false);
-                onScrollChanged(mScrollX, mScrollY, oldX, oldY);
-
-                final int range = getScrollRange();
-                final int overscrollMode = getOverScrollMode();
-                if (overscrollMode == OVER_SCROLL_ALWAYS ||
-                        (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
-                    if (x < 0 && oldX >= 0) {
-                        mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
-                    } else if (x > range && oldX <= range) {
-                        mEdgeGlowRight.onAbsorb((int) mScroller.getCurrVelocity());
-                    }
+            if (getChildCount() > 0) {
+                View child = getChildAt(0);
+                x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth());
+                y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight());
+                if (x != oldX || y != oldY) {
+                    mScrollX = x;
+                    mScrollY = y;
+                    onScrollChanged(x, y, oldX, oldY);
                 }
             }
             awakenScrollBars();
@@ -1347,7 +1263,7 @@
             int right = getChildAt(0).getWidth();
     
             mScroller.fling(mScrollX, mScrollY, velocityX, 0, 0, 
-                    Math.max(0, right - width), 0, 0, width/2, 0);
+                    Math.max(0, right - width), 0, 0);
     
             final boolean movingRight = velocityX > 0;
     
@@ -1385,56 +1301,6 @@
         }
     }
 
-    @Override
-    public void setOverScrollMode(int mode) {
-        if (mode != OVER_SCROLL_NEVER) {
-            if (mEdgeGlowLeft == null) {
-                final Resources res = getContext().getResources();
-                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
-                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
-                mEdgeGlowLeft = new EdgeGlow(edge, glow);
-                mEdgeGlowRight = new EdgeGlow(edge, glow);
-            }
-        } else {
-            mEdgeGlowLeft = null;
-            mEdgeGlowRight = null;
-        }
-        super.setOverScrollMode(mode);
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        if (mEdgeGlowLeft != null) {
-            final int scrollX = mScrollX;
-            if (!mEdgeGlowLeft.isFinished()) {
-                final int restoreCount = canvas.save();
-                final int height = getHeight();
-
-                canvas.rotate(270);
-                canvas.translate(-height * 1.5f, Math.min(0, scrollX));
-                mEdgeGlowLeft.setSize(getHeight() * 2, getWidth());
-                if (mEdgeGlowLeft.draw(canvas)) {
-                    invalidate();
-                }
-                canvas.restoreToCount(restoreCount);
-            }
-            if (!mEdgeGlowRight.isFinished()) {
-                final int restoreCount = canvas.save();
-                final int width = getWidth();
-                final int height = getHeight();
-
-                canvas.rotate(90);
-                canvas.translate(-height / 2, -(Math.max(getScrollRange(), scrollX) + width));
-                mEdgeGlowRight.setSize(height * 2, width);
-                if (mEdgeGlowRight.draw(canvas)) {
-                    invalidate();
-                }
-                canvas.restoreToCount(restoreCount);
-            }
-        }
-    }
-
     private int clamp(int n, int my, int child) {
         if (my >= child || n < 0) {
             return 0;
diff --git a/core/java/android/widget/ImageButton.java b/core/java/android/widget/ImageButton.java
index 12a68db..d680fad 100644
--- a/core/java/android/widget/ImageButton.java
+++ b/core/java/android/widget/ImageButton.java
@@ -32,7 +32,7 @@
  * {@link android.widget.Button}, with the standard button background
  * that changes color during different button states. The image on the surface
  * of the button is defined either by the {@code android:src} attribute in the
- * {@code &lt;ImageButton&gt;} XML element or by the 
+ * {@code <ImageButton>} XML element or by the
  * {@link #setImageResource(int)} method.</p>
  * 
  * <p>To remove the standard button background image, define your own 
@@ -57,7 +57,7 @@
  * based on the state of the button and the corresponding images
  * defined in the XML.</p>
  *
- * <p>The order of the {@code &lt;item>} elements is important because they are
+ * <p>The order of the {@code <item>} elements is important because they are
  * evaluated in order. This is why the "normal" button image comes last, because
  * it will only be applied after {@code android:state_pressed} and {@code
  * android:state_focused} have both evaluated false.</p>
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 46f7db4..bad74d4 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -34,8 +34,6 @@
 import android.util.Log;
 import android.view.RemotableViewMethod;
 import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 import android.widget.RemoteViews.RemoteView;
 
 
@@ -162,6 +160,12 @@
     }
     
     @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+        if (mDrawable != null) mDrawable.jumpToCurrentState();
+    }
+
+    @Override
     public void invalidateDrawable(Drawable dr) {
         if (dr == mDrawable) {
             /* we invalidate the whole view in this case because it's very
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index faf082d..1e5489a 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -43,6 +43,13 @@
  * <p>
  * Also see {@link LinearLayout.LayoutParams android.widget.LinearLayout.LayoutParams}
  * for layout attributes </p>
+ *
+ * @attr ref android.R.styleable#LinearLayout_baselineAligned
+ * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
+ * @attr ref android.R.styleable#LinearLayout_gravity
+ * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
+ * @attr ref android.R.styleable#LinearLayout_orientation
+ * @attr ref android.R.styleable#LinearLayout_weightSum
  */
 @RemoteView
 public class LinearLayout extends ViewGroup {
@@ -117,7 +124,11 @@
     }
 
     public LinearLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
+        this(context, attrs, 0);
+    }
+    
+    public LinearLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
 
         TypedArray a = 
             context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout);
@@ -142,8 +153,7 @@
         mBaselineAlignedChildIndex =
                 a.getInt(com.android.internal.R.styleable.LinearLayout_baselineAlignedChildIndex, -1);
 
-        // TODO: Better name, add Java APIs, make it public
-        mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_useLargestChild, false);
+        mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false);
 
         a.recycle();
     }
@@ -172,6 +182,33 @@
         mBaselineAligned = baselineAligned;
     }
 
+    /**
+     * When true, all children with a weight will be considered having
+     * the minimum size of the largest child. If false, all children are
+     * measured normally.
+     * 
+     * @return True to measure children with a weight using the minimum
+     *         size of the largest child, false otherwise.
+     */
+    public boolean isMeasureWithLargestChildEnabled() {
+        return mUseLargestChild;
+    }
+
+    /**
+     * When set to true, all children with a weight will be considered having
+     * the minimum size of the largest child. If false, all children are
+     * measured normally.
+     * 
+     * Disabled by default.
+     * 
+     * @param enabled True to measure children with a weight using the
+     *        minimum size of the largest child, false otherwise.
+     */
+    @android.view.RemotableViewMethod
+    public void setMeasureWithLargestChildEnabled(boolean enabled) {
+        mUseLargestChild = enabled;
+    }
+
     @Override
     public int getBaseline() {
         if (mBaselineAlignedChildIndex < 0) {
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
new file mode 100644
index 0000000..9c483ee
--- /dev/null
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -0,0 +1,1241 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.View.MeasureSpec;
+import android.view.View.OnTouchListener;
+
+/**
+ * A ListPopupWindow anchors itself to a host view and displays a
+ * list of choices.
+ * 
+ * <p>ListPopupWindow contains a number of tricky behaviors surrounding
+ * positioning, scrolling parents to fit the dropdown, interacting
+ * sanely with the IME if present, and others.
+ * 
+ * @see android.widget.AutoCompleteTextView
+ * @see android.widget.Spinner
+ */
+public class ListPopupWindow {
+    private static final String TAG = "ListPopupWindow";
+    private static final boolean DEBUG = false;
+
+    /**
+     * This value controls the length of time that the user
+     * must leave a pointer down without scrolling to expand
+     * the autocomplete dropdown list to cover the IME.
+     */
+    private static final int EXPAND_LIST_TIMEOUT = 250;
+
+    private Context mContext;
+    private PopupWindow mPopup;
+    private ListAdapter mAdapter;
+    private DropDownListView mDropDownList;
+
+    private int mDropDownHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
+    private int mDropDownWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
+    private int mDropDownHorizontalOffset;
+    private int mDropDownVerticalOffset;
+
+    private boolean mDropDownAlwaysVisible = false;
+    private boolean mForceIgnoreOutsideTouch = false;
+
+    private View mPromptView;
+    private int mPromptPosition = POSITION_PROMPT_ABOVE;
+
+    private DataSetObserver mObserver;
+
+    private View mDropDownAnchorView;
+
+    private Drawable mDropDownListHighlight;
+
+    private AdapterView.OnItemClickListener mItemClickListener;
+    private AdapterView.OnItemSelectedListener mItemSelectedListener;
+
+    private final ResizePopupRunnable mResizePopupRunnable = new ResizePopupRunnable();
+    private final PopupTouchInterceptor mTouchInterceptor = new PopupTouchInterceptor();
+    private final PopupScrollListener mScrollListener = new PopupScrollListener();
+    private final ListSelectorHider mHideSelector = new ListSelectorHider();
+    private Runnable mShowDropDownRunnable;
+
+    private Handler mHandler = new Handler();
+
+    private Rect mTempRect = new Rect();
+
+    private boolean mModal;
+
+    /**
+     * The provided prompt view should appear above list content.
+     * 
+     * @see #setPromptPosition(int)
+     * @see #getPromptPosition()
+     * @see #setPromptView(View)
+     */
+    public static final int POSITION_PROMPT_ABOVE = 0;
+
+    /**
+     * The provided prompt view should appear below list content.
+     * 
+     * @see #setPromptPosition(int)
+     * @see #getPromptPosition()
+     * @see #setPromptView(View)
+     */
+    public static final int POSITION_PROMPT_BELOW = 1;
+
+    /**
+     * Alias for {@link ViewGroup.LayoutParams#MATCH_PARENT}.
+     * If used to specify a popup width, the popup will match the width of the anchor view.
+     * If used to specify a popup height, the popup will fill available space.
+     */
+    public static final int MATCH_PARENT = ViewGroup.LayoutParams.MATCH_PARENT;
+    
+    /**
+     * Alias for {@link ViewGroup.LayoutParams#WRAP_CONTENT}.
+     * If used to specify a popup width, the popup will use the width of its content.
+     */
+    public static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
+    
+    /**
+     * Mode for {@link #setInputMethodMode(int)}: the requirements for the
+     * input method should be based on the focusability of the popup.  That is
+     * if it is focusable than it needs to work with the input method, else
+     * it doesn't.
+     */
+    public static final int INPUT_METHOD_FROM_FOCUSABLE = PopupWindow.INPUT_METHOD_FROM_FOCUSABLE;
+    
+    /**
+     * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
+     * work with an input method, regardless of whether it is focusable.  This
+     * means that it will always be displayed so that the user can also operate
+     * the input method while it is shown.
+     */
+    public static final int INPUT_METHOD_NEEDED = PopupWindow.INPUT_METHOD_NEEDED;
+    
+    /**
+     * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
+     * work with an input method, regardless of whether it is focusable.  This
+     * means that it will always be displayed to use as much space on the
+     * screen as needed, regardless of whether this covers the input method.
+     */
+    public static final int INPUT_METHOD_NOT_NEEDED = PopupWindow.INPUT_METHOD_NOT_NEEDED;
+
+    /**
+     * Create a new, empty popup window capable of displaying items from a ListAdapter.
+     * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
+     * 
+     * @param context Context used for contained views.
+     */
+    public ListPopupWindow(Context context) {
+        this(context, null, com.android.internal.R.attr.listPopupWindowStyle, 0);
+    }
+
+    /**
+     * Create a new, empty popup window capable of displaying items from a ListAdapter.
+     * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
+     * 
+     * @param context Context used for contained views.
+     * @param attrs Attributes from inflating parent views used to style the popup.
+     */
+    public ListPopupWindow(Context context, AttributeSet attrs) {
+        this(context, attrs, com.android.internal.R.attr.listPopupWindowStyle, 0);
+    }
+
+    /**
+     * Create a new, empty popup window capable of displaying items from a ListAdapter.
+     * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
+     * 
+     * @param context Context used for contained views.
+     * @param attrs Attributes from inflating parent views used to style the popup.
+     * @param defStyleAttr Default style attribute to use for popup content.
+     */
+    public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    /**
+     * Create a new, empty popup window capable of displaying items from a ListAdapter.
+     * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
+     * 
+     * @param context Context used for contained views.
+     * @param attrs Attributes from inflating parent views used to style the popup.
+     * @param defStyleAttr Style attribute to read for default styling of popup content.
+     * @param defStyleRes Style resource ID to use for default styling of popup content.
+     */
+    public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        mContext = context;
+        mPopup = new PopupWindow(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    /**
+     * Sets the adapter that provides the data and the views to represent the data
+     * in this popup window.
+     *
+     * @param adapter The adapter to use to create this window's content.
+     */
+    public void setAdapter(ListAdapter adapter) {
+        if (mObserver == null) {
+            mObserver = new PopupDataSetObserver();
+        } else if (mAdapter != null) {
+            mAdapter.unregisterDataSetObserver(mObserver);
+        }
+        mAdapter = adapter;
+        if (mAdapter != null) {
+            adapter.registerDataSetObserver(mObserver);
+        }
+        
+        if (mDropDownList != null) {
+            mDropDownList.setAdapter(mAdapter);
+        }
+    }
+
+    /**
+     * Set where the optional prompt view should appear. The default is
+     * {@link #POSITION_PROMPT_ABOVE}.
+     * 
+     * @param position A position constant declaring where the prompt should be displayed.
+     * 
+     * @see #POSITION_PROMPT_ABOVE
+     * @see #POSITION_PROMPT_BELOW
+     */
+    public void setPromptPosition(int position) {
+        mPromptPosition = position;
+    }
+
+    /**
+     * @return Where the optional prompt view should appear.
+     * 
+     * @see #POSITION_PROMPT_ABOVE
+     * @see #POSITION_PROMPT_BELOW
+     */
+    public int getPromptPosition() {
+        return mPromptPosition;
+    }
+
+    /**
+     * Set whether this window should be modal when shown.
+     * 
+     * <p>If a popup window is modal, it will receive all touch and key input.
+     * If the user touches outside the popup window's content area the popup window
+     * will be dismissed.
+     * 
+     * @param modal {@code true} if the popup window should be modal, {@code false} otherwise.
+     */
+    public void setModal(boolean modal) {
+        mModal = true;
+        mPopup.setFocusable(modal);
+    }
+
+    /**
+     * Returns whether the popup window will be modal when shown.
+     * 
+     * @return {@code true} if the popup window will be modal, {@code false} otherwise.
+     */
+    public boolean isModal() {
+        return mModal;
+    }
+
+    /**
+     * Forces outside touches to be ignored. Normally if {@link #isDropDownAlwaysVisible()} is
+     * false, we allow outside touch to dismiss the dropdown. If this is set to true, then we
+     * ignore outside touch even when the drop down is not set to always visible.
+     * 
+     * @hide Used only by AutoCompleteTextView to handle some internal special cases.
+     */
+    public void setForceIgnoreOutsideTouch(boolean forceIgnoreOutsideTouch) {
+        mForceIgnoreOutsideTouch = forceIgnoreOutsideTouch;
+    }
+
+    /**
+     * Sets whether the drop-down should remain visible under certain conditions.
+     *
+     * The drop-down will occupy the entire screen below {@link #getAnchorView} regardless
+     * of the size or content of the list.  {@link #getBackground()} will fill any space
+     * that is not used by the list.
+     *
+     * @param dropDownAlwaysVisible Whether to keep the drop-down visible.
+     *
+     * @hide Only used by AutoCompleteTextView under special conditions.
+     */
+    public void setDropDownAlwaysVisible(boolean dropDownAlwaysVisible) {
+        mDropDownAlwaysVisible = dropDownAlwaysVisible;
+    }
+
+    /**
+     * @return Whether the drop-down is visible under special conditions.
+     *
+     * @hide Only used by AutoCompleteTextView under special conditions.
+     */
+    public boolean isDropDownAlwaysVisible() {
+        return mDropDownAlwaysVisible;
+    }
+
+    /**
+     * Sets the operating mode for the soft input area.
+     *
+     * @param mode The desired mode, see
+     *        {@link android.view.WindowManager.LayoutParams#softInputMode}
+     *        for the full list
+     *
+     * @see android.view.WindowManager.LayoutParams#softInputMode
+     * @see #getSoftInputMode()
+     */
+    public void setSoftInputMode(int mode) {
+        mPopup.setSoftInputMode(mode);
+    }
+
+    /**
+     * Returns the current value in {@link #setSoftInputMode(int)}.
+     *
+     * @see #setSoftInputMode(int)
+     * @see android.view.WindowManager.LayoutParams#softInputMode
+     */
+    public int getSoftInputMode() {
+        return mPopup.getSoftInputMode();
+    }
+
+    /**
+     * Sets a drawable to use as the list item selector.
+     * 
+     * @param selector List selector drawable to use in the popup.
+     */
+    public void setListSelector(Drawable selector) {
+        mDropDownListHighlight = selector;
+    }
+
+    /**
+     * @return The background drawable for the popup window.
+     */
+    public Drawable getBackground() {
+        return mPopup.getBackground();
+    }
+
+    /**
+     * Sets a drawable to be the background for the popup window.
+     * 
+     * @param d A drawable to set as the background.
+     */
+    public void setBackgroundDrawable(Drawable d) {
+        mPopup.setBackgroundDrawable(d);
+    }
+
+    /**
+     * Set an animation style to use when the popup window is shown or dismissed.
+     * 
+     * @param animationStyle Animation style to use.
+     */
+    public void setAnimationStyle(int animationStyle) {
+        mPopup.setAnimationStyle(animationStyle);
+    }
+
+    /**
+     * Returns the animation style that will be used when the popup window is
+     * shown or dismissed.
+     * 
+     * @return Animation style that will be used.
+     */
+    public int getAnimationStyle() {
+        return mPopup.getAnimationStyle();
+    }
+
+    /**
+     * Returns the view that will be used to anchor this popup.
+     * 
+     * @return The popup's anchor view
+     */
+    public View getAnchorView() {
+        return mDropDownAnchorView;
+    }
+
+    /**
+     * Sets the popup's anchor view. This popup will always be positioned relative to
+     * the anchor view when shown.
+     * 
+     * @param anchor The view to use as an anchor.
+     */
+    public void setAnchorView(View anchor) {
+        mDropDownAnchorView = anchor;
+    }
+
+    /**
+     * @return The horizontal offset of the popup from its anchor in pixels.
+     */
+    public int getHorizontalOffset() {
+        return mDropDownHorizontalOffset;
+    }
+
+    /**
+     * Set the horizontal offset of this popup from its anchor view in pixels.
+     * 
+     * @param offset The horizontal offset of the popup from its anchor.
+     */
+    public void setHorizontalOffset(int offset) {
+        mDropDownHorizontalOffset = offset;
+    }
+
+    /**
+     * @return The vertical offset of the popup from its anchor in pixels.
+     */
+    public int getVerticalOffset() {
+        return mDropDownVerticalOffset;
+    }
+
+    /**
+     * Set the vertical offset of this popup from its anchor view in pixels.
+     * 
+     * @param offset The vertical offset of the popup from its anchor.
+     */
+    public void setVerticalOffset(int offset) {
+        mDropDownVerticalOffset = offset;
+    }
+
+    /**
+     * @return The width of the popup window in pixels.
+     */
+    public int getWidth() {
+        return mDropDownWidth;
+    }
+
+    /**
+     * Sets the width of the popup window in pixels. Can also be {@link #MATCH_PARENT}
+     * or {@link #WRAP_CONTENT}.
+     * 
+     * @param width Width of the popup window.
+     */
+    public void setWidth(int width) {
+        mDropDownWidth = width;
+    }
+
+    /**
+     * Sets the width of the popup window by the size of its content. The final width may be
+     * larger to accommodate styled window dressing.
+     *
+     * @param width Desired width of content in pixels.
+     */
+    public void setContentWidth(int width) {
+        Drawable popupBackground = mPopup.getBackground();
+        if (popupBackground != null) {
+            mDropDownWidth = popupBackground.getIntrinsicWidth() + width;
+        }
+    }
+
+    /**
+     * @return The height of the popup window in pixels.
+     */
+    public int getHeight() {
+        return mDropDownHeight;
+    }
+
+    /**
+     * Sets the height of the popup window in pixels. Can also be {@link #MATCH_PARENT}.
+     * 
+     * @param height Height of the popup window.
+     */
+    public void setHeight(int height) {
+        mDropDownHeight = height;
+    }
+
+    /**
+     * Sets a listener to receive events when a list item is clicked.
+     * 
+     * @param clickListener Listener to register
+     * 
+     * @see ListView#setOnItemClickListener(android.widget.AdapterView.OnItemClickListener)
+     */
+    public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) {
+        mItemClickListener = clickListener;
+    }
+
+    /**
+     * Sets a listener to receive events when a list item is selected.
+     * 
+     * @param selectedListener Listener to register.
+     * 
+     * @see ListView#setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)
+     */
+    public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener selectedListener) {
+        mItemSelectedListener = selectedListener;
+    }
+
+    /**
+     * Set a view to act as a user prompt for this popup window. Where the prompt view will appear
+     * is controlled by {@link #setPromptPosition(int)}.
+     * 
+     * @param prompt View to use as an informational prompt.
+     */
+    public void setPromptView(View prompt) {
+        boolean showing = isShowing();
+        if (showing) {
+            removePromptView();
+        }
+        mPromptView = prompt;
+        if (showing) {
+            show();
+        }
+    }
+
+    /**
+     * Post a {@link #show()} call to the UI thread.
+     */
+    public void postShow() {
+        mHandler.post(mShowDropDownRunnable);
+    }
+
+    /**
+     * Show the popup list. If the list is already showing, this method
+     * will recalculate the popup's size and position.
+     */
+    public void show() {
+        int height = buildDropDown();
+
+        int widthSpec = 0;
+        int heightSpec = 0;
+
+        boolean noInputMethod = isInputMethodNotNeeded();
+
+        if (mPopup.isShowing()) {
+            if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
+                // The call to PopupWindow's update method below can accept -1 for any
+                // value you do not want to update.
+                widthSpec = -1;
+            } else if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
+                widthSpec = getAnchorView().getWidth();
+            } else {
+                widthSpec = mDropDownWidth;
+            }
+
+            if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
+                // The call to PopupWindow's update method below can accept -1 for any
+                // value you do not want to update.
+                heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
+                if (noInputMethod) {
+                    mPopup.setWindowLayoutMode(
+                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+                                    ViewGroup.LayoutParams.MATCH_PARENT : 0, 0);
+                } else {
+                    mPopup.setWindowLayoutMode(
+                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+                                    ViewGroup.LayoutParams.MATCH_PARENT : 0,
+                            ViewGroup.LayoutParams.MATCH_PARENT);
+                }
+            } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
+                heightSpec = height;
+            } else {
+                heightSpec = mDropDownHeight;
+            }
+
+            mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
+
+            mPopup.update(getAnchorView(), mDropDownHorizontalOffset,
+                    mDropDownVerticalOffset, widthSpec, heightSpec);
+        } else {
+            if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
+                widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
+            } else {
+                if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
+                    mPopup.setWidth(getAnchorView().getWidth());
+                } else {
+                    mPopup.setWidth(mDropDownWidth);
+                }
+            }
+
+            if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
+                heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
+            } else {
+                if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
+                    mPopup.setHeight(height);
+                } else {
+                    mPopup.setHeight(mDropDownHeight);
+                }
+            }
+
+            mPopup.setWindowLayoutMode(widthSpec, heightSpec);
+            mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
+            mPopup.setClipToScreenEnabled(true);
+            
+            // use outside touchable to dismiss drop down when touching outside of it, so
+            // only set this if the dropdown is not always visible
+            mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
+            mPopup.setTouchInterceptor(mTouchInterceptor);
+            mPopup.showAsDropDown(getAnchorView(),
+                    mDropDownHorizontalOffset, mDropDownVerticalOffset);
+            mDropDownList.setSelection(ListView.INVALID_POSITION);
+            
+            if (!mModal || mDropDownList.isInTouchMode()) {
+                clearListSelection();
+            }
+            if (!mModal) {
+                mHandler.post(mHideSelector);
+            }
+        }
+    }
+
+    /**
+     * Dismiss the popup window.
+     */
+    public void dismiss() {
+        mPopup.dismiss();
+        removePromptView();
+        mPopup.setContentView(null);
+        mDropDownList = null;
+    }
+
+    /**
+     * Set a listener to receive a callback when the popup is dismissed.
+     *
+     * @param listener Listener that will be notified when the popup is dismissed.
+     */
+    public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
+        mPopup.setOnDismissListener(listener);
+    }
+
+    private void removePromptView() {
+        if (mPromptView != null) {
+            final ViewParent parent = mPromptView.getParent();
+            if (parent instanceof ViewGroup) {
+                final ViewGroup group = (ViewGroup) parent;
+                group.removeView(mPromptView);
+            }
+        }
+    }
+
+    /**
+     * Control how the popup operates with an input method: one of
+     * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
+     * or {@link #INPUT_METHOD_NOT_NEEDED}.
+     * 
+     * <p>If the popup is showing, calling this method will take effect only
+     * the next time the popup is shown or through a manual call to the {@link #show()}
+     * method.</p>
+     * 
+     * @see #getInputMethodMode()
+     * @see #show()
+     */
+    public void setInputMethodMode(int mode) {
+        mPopup.setInputMethodMode(mode);
+    }
+
+    /**
+     * Return the current value in {@link #setInputMethodMode(int)}.
+     * 
+     * @see #setInputMethodMode(int)
+     */
+    public int getInputMethodMode() {
+        return mPopup.getInputMethodMode();
+    }
+
+    /**
+     * Set the selected position of the list.
+     * Only valid when {@link #isShowing()} == {@code true}.
+     * 
+     * @param position List position to set as selected.
+     */
+    public void setSelection(int position) {
+        DropDownListView list = mDropDownList;
+        if (isShowing() && list != null) {
+            list.mListSelectionHidden = false;
+            list.setSelection(position);
+            if (list.getChoiceMode() != ListView.CHOICE_MODE_NONE) {
+                list.setItemChecked(position, true);
+            }
+        }
+    }
+
+    /**
+     * Clear any current list selection.
+     * Only valid when {@link #isShowing()} == {@code true}.
+     */
+    public void clearListSelection() {
+        final DropDownListView list = mDropDownList;
+        if (list != null) {
+            // WARNING: Please read the comment where mListSelectionHidden is declared
+            list.mListSelectionHidden = true;
+            list.hideSelector();
+            list.requestLayout();
+        }
+    }
+
+    /**
+     * @return {@code true} if the popup is currently showing, {@code false} otherwise.
+     */
+    public boolean isShowing() {
+        return mPopup.isShowing();
+    }
+
+    /**
+     * @return {@code true} if this popup is configured to assume the user does not need
+     * to interact with the IME while it is showing, {@code false} otherwise.
+     */
+    public boolean isInputMethodNotNeeded() {
+        return mPopup.getInputMethodMode() == INPUT_METHOD_NOT_NEEDED;
+    }
+
+    /**
+     * Perform an item click operation on the specified list adapter position.
+     * 
+     * @param position Adapter position for performing the click
+     * @return true if the click action could be performed, false if not.
+     *         (e.g. if the popup was not showing, this method would return false.)
+     */
+    public boolean performItemClick(int position) {
+        if (isShowing()) {
+            if (mItemClickListener != null) {
+                final DropDownListView list = mDropDownList;
+                final View child = list.getChildAt(position - list.getFirstVisiblePosition());
+                final ListAdapter adapter = list.getAdapter();
+                mItemClickListener.onItemClick(list, child, position, adapter.getItemId(position));
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return The currently selected item or null if the popup is not showing.
+     */
+    public Object getSelectedItem() {
+        if (!isShowing()) {
+            return null;
+        }
+        return mDropDownList.getSelectedItem();
+    }
+
+    /**
+     * @return The position of the currently selected item or {@link ListView#INVALID_POSITION}
+     * if {@link #isShowing()} == {@code false}.
+     * 
+     * @see ListView#getSelectedItemPosition()
+     */
+    public int getSelectedItemPosition() {
+        if (!isShowing()) {
+            return ListView.INVALID_POSITION;
+        }
+        return mDropDownList.getSelectedItemPosition();
+    }
+
+    /**
+     * @return The ID of the currently selected item or {@link ListView#INVALID_ROW_ID}
+     * if {@link #isShowing()} == {@code false}.
+     * 
+     * @see ListView#getSelectedItemId()
+     */
+    public long getSelectedItemId() {
+        if (!isShowing()) {
+            return ListView.INVALID_ROW_ID;
+        }
+        return mDropDownList.getSelectedItemId();
+    }
+
+    /**
+     * @return The View for the currently selected item or null if
+     * {@link #isShowing()} == {@code false}.
+     * 
+     * @see ListView#getSelectedView()
+     */
+    public View getSelectedView() {
+        if (!isShowing()) {
+            return null;
+        }
+        return mDropDownList.getSelectedView();
+    }
+
+    /**
+     * @return The {@link ListView} displayed within the popup window.
+     * Only valid when {@link #isShowing()} == {@code true}.
+     */
+    public ListView getListView() {
+        return mDropDownList;
+    }
+
+    /**
+     * Filter key down events. By forwarding key up events to this function,
+     * views using non-modal ListPopupWindow can have it handle key selection of items.
+     *  
+     * @param keyCode keyCode param passed to the host view's onKeyDown
+     * @param event event param passed to the host view's onKeyDown
+     * @return true if the event was handled, false if it was ignored.
+     * 
+     * @see #setModal(boolean)
+     */
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        // when the drop down is shown, we drive it directly
+        if (isShowing()) {
+            // the key events are forwarded to the list in the drop down view
+            // note that ListView handles space but we don't want that to happen
+            // also if selection is not currently in the drop down, then don't
+            // let center or enter presses go there since that would cause it
+            // to select one of its items
+            if (keyCode != KeyEvent.KEYCODE_SPACE
+                    && (mDropDownList.getSelectedItemPosition() >= 0
+                            || (keyCode != KeyEvent.KEYCODE_ENTER
+                                    && keyCode != KeyEvent.KEYCODE_DPAD_CENTER))) {
+                int curIndex = mDropDownList.getSelectedItemPosition();
+                boolean consumed;
+
+                final boolean below = !mPopup.isAboveAnchor();
+
+                final ListAdapter adapter = mAdapter;
+                
+                boolean allEnabled;
+                int firstItem = Integer.MAX_VALUE;
+                int lastItem = Integer.MIN_VALUE;
+
+                if (adapter != null) {
+                    allEnabled = adapter.areAllItemsEnabled();
+                    firstItem = allEnabled ? 0 :
+                            mDropDownList.lookForSelectablePosition(0, true);
+                    lastItem = allEnabled ? adapter.getCount() - 1 :
+                            mDropDownList.lookForSelectablePosition(adapter.getCount() - 1, false);                    
+                }
+                
+                if ((below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= firstItem) ||
+                        (!below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN && curIndex >= lastItem)) {
+                    // When the selection is at the top, we block the key
+                    // event to prevent focus from moving.
+                    clearListSelection();
+                    mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
+                    show();
+                    return true;
+                } else {
+                    // WARNING: Please read the comment where mListSelectionHidden
+                    //          is declared
+                    mDropDownList.mListSelectionHidden = false;
+                }
+
+                consumed = mDropDownList.onKeyDown(keyCode, event);
+                if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" + consumed);
+
+                if (consumed) {
+                    // If it handled the key event, then the user is
+                    // navigating in the list, so we should put it in front.
+                    mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+                    // Here's a little trick we need to do to make sure that
+                    // the list view is actually showing its focus indicator,
+                    // by ensuring it has focus and getting its window out
+                    // of touch mode.
+                    mDropDownList.requestFocusFromTouch();
+                    show();
+
+                    switch (keyCode) {
+                        // avoid passing the focus from the text view to the
+                        // next component
+                        case KeyEvent.KEYCODE_ENTER:
+                        case KeyEvent.KEYCODE_DPAD_CENTER:
+                        case KeyEvent.KEYCODE_DPAD_DOWN:
+                        case KeyEvent.KEYCODE_DPAD_UP:
+                            return true;
+                    }
+                } else {
+                    if (below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+                        // when the selection is at the bottom, we block the
+                        // event to avoid going to the next focusable widget
+                        if (curIndex == lastItem) {
+                            return true;
+                        }
+                    } else if (!below && keyCode == KeyEvent.KEYCODE_DPAD_UP &&
+                            curIndex == firstItem) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Filter key down events. By forwarding key up events to this function,
+     * views using non-modal ListPopupWindow can have it handle key selection of items.
+     *  
+     * @param keyCode keyCode param passed to the host view's onKeyUp
+     * @param event event param passed to the host view's onKeyUp
+     * @return true if the event was handled, false if it was ignored.
+     * 
+     * @see #setModal(boolean)
+     */
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
+            boolean consumed = mDropDownList.onKeyUp(keyCode, event);
+            if (consumed) {
+                switch (keyCode) {
+                    // if the list accepts the key events and the key event
+                    // was a click, the text view gets the selected item
+                    // from the drop down as its content
+                    case KeyEvent.KEYCODE_ENTER:
+                    case KeyEvent.KEYCODE_DPAD_CENTER:
+                        dismiss();
+                        break;
+                }
+            }
+            return consumed;
+        }
+        return false;
+    }
+
+    /**
+     * Filter pre-IME key events. By forwarding {@link View#onKeyPreIme(int, KeyEvent)}
+     * events to this function, views using ListPopupWindow can have it dismiss the popup
+     * when the back key is pressed.
+     *  
+     * @param keyCode keyCode param passed to the host view's onKeyPreIme
+     * @param event event param passed to the host view's onKeyPreIme
+     * @return true if the event was handled, false if it was ignored.
+     * 
+     * @see #setModal(boolean)
+     */
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BACK && isShowing()) {
+            // special case for the back key, we do not even try to send it
+            // to the drop down list but instead, consume it immediately
+            final View anchorView = mDropDownAnchorView;
+            if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
+                anchorView.getKeyDispatcherState().startTracking(event, this);
+                return true;
+            } else if (event.getAction() == KeyEvent.ACTION_UP) {
+                anchorView.getKeyDispatcherState().handleUpEvent(event);
+                if (event.isTracking() && !event.isCanceled()) {
+                    dismiss();
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * <p>Builds the popup window's content and returns the height the popup
+     * should have. Returns -1 when the content already exists.</p>
+     *
+     * @return the content's height or -1 if content already exists
+     */
+    private int buildDropDown() {
+        ViewGroup dropDownView;
+        int otherHeights = 0;
+
+        if (mDropDownList == null) {
+            Context context = mContext;
+
+            /**
+             * This Runnable exists for the sole purpose of checking if the view layout has got
+             * completed and if so call showDropDown to display the drop down. This is used to show
+             * the drop down as soon as possible after user opens up the search dialog, without
+             * waiting for the normal UI pipeline to do it's job which is slower than this method.
+             */
+            mShowDropDownRunnable = new Runnable() {
+                public void run() {
+                    // View layout should be all done before displaying the drop down.
+                    View view = getAnchorView();
+                    if (view != null && view.getWindowToken() != null) {
+                        show();
+                    }
+                }
+            };
+
+            mDropDownList = new DropDownListView(context, !mModal);
+            if (mDropDownListHighlight != null) {
+                mDropDownList.setSelector(mDropDownListHighlight);
+            }
+            mDropDownList.setAdapter(mAdapter);
+            mDropDownList.setVerticalFadingEdgeEnabled(true);
+            mDropDownList.setOnItemClickListener(mItemClickListener);
+            mDropDownList.setFocusable(true);
+            mDropDownList.setFocusableInTouchMode(true);
+            mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+                public void onItemSelected(AdapterView<?> parent, View view,
+                        int position, long id) {
+
+                    if (position != -1) {
+                        DropDownListView dropDownList = mDropDownList;
+
+                        if (dropDownList != null) {
+                            dropDownList.mListSelectionHidden = false;
+                        }
+                    }
+                }
+
+                public void onNothingSelected(AdapterView<?> parent) {
+                }
+            });
+            mDropDownList.setOnScrollListener(mScrollListener);
+
+            if (mItemSelectedListener != null) {
+                mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
+            }
+
+            dropDownView = mDropDownList;
+
+            View hintView = mPromptView;
+            if (hintView != null) {
+                // if an hint has been specified, we accomodate more space for it and
+                // add a text view in the drop down menu, at the bottom of the list
+                LinearLayout hintContainer = new LinearLayout(context);
+                hintContainer.setOrientation(LinearLayout.VERTICAL);
+
+                LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f
+                );
+                
+                switch (mPromptPosition) {
+                case POSITION_PROMPT_BELOW:
+                    hintContainer.addView(dropDownView, hintParams);
+                    hintContainer.addView(hintView);
+                    break;
+                    
+                case POSITION_PROMPT_ABOVE:
+                    hintContainer.addView(hintView);
+                    hintContainer.addView(dropDownView, hintParams);
+                    break;
+                    
+                default:
+                    Log.e(TAG, "Invalid hint position " + mPromptPosition);
+                    break;
+                }
+
+                // measure the hint's height to find how much more vertical space
+                // we need to add to the drop down's height
+                int widthSpec = MeasureSpec.makeMeasureSpec(mDropDownWidth, MeasureSpec.AT_MOST);
+                int heightSpec = MeasureSpec.UNSPECIFIED;
+                hintView.measure(widthSpec, heightSpec);
+
+                hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams();
+                otherHeights = hintView.getMeasuredHeight() + hintParams.topMargin
+                        + hintParams.bottomMargin;
+
+                dropDownView = hintContainer;
+            }
+
+            mPopup.setContentView(dropDownView);
+        } else {
+            dropDownView = (ViewGroup) mPopup.getContentView();
+            final View view = mPromptView;
+            if (view != null) {
+                LinearLayout.LayoutParams hintParams =
+                        (LinearLayout.LayoutParams) view.getLayoutParams();
+                otherHeights = view.getMeasuredHeight() + hintParams.topMargin
+                        + hintParams.bottomMargin;
+            }
+        }
+
+        // Max height available on the screen for a popup.
+        boolean ignoreBottomDecorations =
+                mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
+        final int maxHeight = mPopup.getMaxAvailableHeight(
+                getAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations);
+
+        // getMaxAvailableHeight() subtracts the padding, so we put it back,
+        // to get the available height for the whole window
+        int padding = 0;
+        Drawable background = mPopup.getBackground();
+        if (background != null) {
+            background.getPadding(mTempRect);
+            padding = mTempRect.top + mTempRect.bottom;
+        }
+
+        if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
+            return maxHeight + padding;
+        }
+
+        final int listContent = mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
+                0, ListView.NO_POSITION, maxHeight - otherHeights, 2);
+        // add padding only if the list has items in it, that way we don't show
+        // the popup if it is not needed
+        if (listContent > 0) otherHeights += padding;
+
+        return listContent + otherHeights;
+    }
+
+    /**
+     * <p>Wrapper class for a ListView. This wrapper can hijack the focus to
+     * make sure the list uses the appropriate drawables and states when
+     * displayed on screen within a drop down. The focus is never actually
+     * passed to the drop down in this mode; the list only looks focused.</p>
+     */
+    private static class DropDownListView extends ListView {
+        private static final String TAG = ListPopupWindow.TAG + ".DropDownListView";
+        /*
+         * WARNING: This is a workaround for a touch mode issue.
+         *
+         * Touch mode is propagated lazily to windows. This causes problems in
+         * the following scenario:
+         * - Type something in the AutoCompleteTextView and get some results
+         * - Move down with the d-pad to select an item in the list
+         * - Move up with the d-pad until the selection disappears
+         * - Type more text in the AutoCompleteTextView *using the soft keyboard*
+         *   and get new results; you are now in touch mode
+         * - The selection comes back on the first item in the list, even though
+         *   the list is supposed to be in touch mode
+         *
+         * Using the soft keyboard triggers the touch mode change but that change
+         * is propagated to our window only after the first list layout, therefore
+         * after the list attempts to resurrect the selection.
+         *
+         * The trick to work around this issue is to pretend the list is in touch
+         * mode when we know that the selection should not appear, that is when
+         * we know the user moved the selection away from the list.
+         *
+         * This boolean is set to true whenever we explicitly hide the list's
+         * selection and reset to false whenever we know the user moved the
+         * selection back to the list.
+         *
+         * When this boolean is true, isInTouchMode() returns true, otherwise it
+         * returns super.isInTouchMode().
+         */
+        private boolean mListSelectionHidden;
+        
+        /**
+         * True if this wrapper should fake focus.
+         */
+        private boolean mHijackFocus;
+
+        /**
+         * <p>Creates a new list view wrapper.</p>
+         *
+         * @param context this view's context
+         */
+        public DropDownListView(Context context, boolean hijackFocus) {
+            super(context, null, com.android.internal.R.attr.dropDownListViewStyle);
+            mHijackFocus = hijackFocus;
+            // TODO: Add an API to control this
+            setCacheColorHint(0); // Transparent, since the background drawable could be anything.
+        }
+
+        /**
+         * <p>Avoids jarring scrolling effect by ensuring that list elements
+         * made of a text view fit on a single line.</p>
+         *
+         * @param position the item index in the list to get a view for
+         * @return the view for the specified item
+         */
+        @Override
+        View obtainView(int position, boolean[] isScrap) {
+            View view = super.obtainView(position, isScrap);
+
+            if (view instanceof TextView) {
+                ((TextView) view).setHorizontallyScrolling(true);
+            }
+
+            return view;
+        }
+
+        @Override
+        public boolean isInTouchMode() {
+            // WARNING: Please read the comment where mListSelectionHidden is declared
+            return (mHijackFocus && mListSelectionHidden) || super.isInTouchMode();
+        }
+
+        /**
+         * <p>Returns the focus state in the drop down.</p>
+         *
+         * @return true always if hijacking focus
+         */
+        @Override
+        public boolean hasWindowFocus() {
+            return mHijackFocus || super.hasWindowFocus();
+        }
+
+        /**
+         * <p>Returns the focus state in the drop down.</p>
+         *
+         * @return true always if hijacking focus
+         */
+        @Override
+        public boolean isFocused() {
+            return mHijackFocus || super.isFocused();
+        }
+
+        /**
+         * <p>Returns the focus state in the drop down.</p>
+         *
+         * @return true always if hijacking focus
+         */
+        @Override
+        public boolean hasFocus() {
+            return mHijackFocus || super.hasFocus();
+        }
+    }
+
+    private class PopupDataSetObserver extends DataSetObserver {
+        @Override
+        public void onChanged() {
+            if (isShowing()) {
+                // Resize the popup to fit new content
+                show();
+            }
+        }
+        
+        @Override
+        public void onInvalidated() {
+            dismiss();
+        }
+    }
+
+    private class ListSelectorHider implements Runnable {
+        public void run() {
+            clearListSelection();
+        }
+    }
+
+    private class ResizePopupRunnable implements Runnable {
+        public void run() {
+            mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+            show();
+        }
+    }
+
+    private class PopupTouchInterceptor implements OnTouchListener {
+        public boolean onTouch(View v, MotionEvent event) {
+            final int action = event.getAction();
+            final int x = (int) event.getX();
+            final int y = (int) event.getY();
+            
+            if (action == MotionEvent.ACTION_DOWN &&
+                    mPopup != null && mPopup.isShowing() &&
+                    (x >= 0 && x < getWidth() && y >= 0 && y < getHeight())) {
+                mHandler.postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT);
+            } else if (action == MotionEvent.ACTION_UP) {
+                mHandler.removeCallbacks(mResizePopupRunnable);
+            }
+            return false;
+        }
+    }
+
+    private class PopupScrollListener implements ListView.OnScrollListener {
+        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+                int totalItemCount) {
+
+        }
+
+        public void onScrollStateChanged(AbsListView view, int scrollState) {
+            if (scrollState == SCROLL_STATE_TOUCH_SCROLL &&
+                    !isInputMethodNotNeeded() && mPopup.getContentView() != null) {
+                mHandler.removeCallbacks(mResizePopupRunnable);
+                mResizePopupRunnable.run();
+            }
+        }
+    }
+}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 82b7c4f..e0119e9 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -20,17 +20,14 @@
 import com.google.android.collect.Lists;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.util.AttributeSet;
-import android.util.LongSparseArray;
 import android.util.SparseBooleanArray;
 import android.view.FocusFinder;
 import android.view.KeyEvent;
@@ -41,6 +38,7 @@
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
+import android.widget.RemoteViews.RemoteView;
 
 import java.util.ArrayList;
 
@@ -64,10 +62,10 @@
  * @attr ref android.R.styleable#ListView_entries
  * @attr ref android.R.styleable#ListView_divider
  * @attr ref android.R.styleable#ListView_dividerHeight
- * @attr ref android.R.styleable#ListView_choiceMode
  * @attr ref android.R.styleable#ListView_headerDividersEnabled
  * @attr ref android.R.styleable#ListView_footerDividersEnabled
  */
+@RemoteView
 public class ListView extends AbsListView {
     /**
      * Used to indicate a no preference for a position type.
@@ -75,21 +73,6 @@
     static final int NO_POSITION = -1;
 
     /**
-     * Normal list that does not indicate choices
-     */
-    public static final int CHOICE_MODE_NONE = 0;
-
-    /**
-     * The list allows up to one choice
-     */
-    public static final int CHOICE_MODE_SINGLE = 1;
-
-    /**
-     * The list allows multiple choices
-     */
-    public static final int CHOICE_MODE_MULTIPLE = 2;
-
-    /**
      * When arrow scrolling, ListView will never scroll more than this factor
      * times the height of the list.
      */
@@ -121,12 +104,8 @@
     Drawable mDivider;
     int mDividerHeight;
     
-    Drawable mOverScrollHeader;
-    Drawable mOverScrollFooter;
-
     private boolean mIsCacheColorOpaque;
     private boolean mDividerIsOpaque;
-    private boolean mClipDivider;
 
     private boolean mHeaderDividersEnabled;
     private boolean mFooterDividersEnabled;
@@ -135,11 +114,6 @@
 
     private boolean mItemsCanFocus = false;
 
-    private int mChoiceMode = CHOICE_MODE_NONE;
-
-    private SparseBooleanArray mCheckStates;
-    private LongSparseArray<Boolean> mCheckedIdStates;
-
     // used for temporary calculations.
     private final Rect mTempRect = new Rect();
     private Paint mDividerPaint;
@@ -150,7 +124,7 @@
 
     // Keeps focused children visible through resizes
     private FocusSelector mFocusSelector;
-    
+
     public ListView(Context context) {
         this(context, null);
     }
@@ -178,18 +152,6 @@
             setDivider(d);
         }
         
-        final Drawable osHeader = a.getDrawable(
-                com.android.internal.R.styleable.ListView_overScrollHeader);
-        if (osHeader != null) {
-            setOverscrollHeader(osHeader);
-        }
-
-        final Drawable osFooter = a.getDrawable(
-                com.android.internal.R.styleable.ListView_overScrollFooter);
-        if (osFooter != null) {
-            setOverscrollFooter(osFooter);
-        }
-
         // Use the height specified, zero being the default
         final int dividerHeight = a.getDimensionPixelSize(
                 com.android.internal.R.styleable.ListView_dividerHeight, 0);
@@ -197,8 +159,6 @@
             setDividerHeight(dividerHeight);
         }
 
-        setChoiceMode(a.getInt(R.styleable.ListView_choiceMode, CHOICE_MODE_NONE));
-        
         mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true);
         mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true);
 
@@ -419,6 +379,16 @@
     }
 
     /**
+     * Sets up this AbsListView to use a remote views adapter which connects to a RemoteViewsService
+     * through the specified intent.
+     * @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
+     */
+    @android.view.RemotableViewMethod
+    public void setRemoteViewsAdapter(Intent intent) {
+        super.setRemoteViewsAdapter(intent);
+    }
+
+    /**
      * Sets the data behind this ListView.
      *
      * The adapter passed to this method may be wrapped by a {@link WrapperListAdapter},
@@ -433,7 +403,7 @@
      */
     @Override
     public void setAdapter(ListAdapter adapter) {
-        if (null != mAdapter) {
+        if (mAdapter != null && mDataSetObserver != null) {
             mAdapter.unregisterDataSetObserver(mDataSetObserver);
         }
 
@@ -448,6 +418,10 @@
 
         mOldSelectedPosition = INVALID_POSITION;
         mOldSelectedRowId = INVALID_ROW_ID;
+
+        // AbsListView#setAdapter will update choice mode states.
+        super.setAdapter(adapter);
+
         if (mAdapter != null) {
             mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
             mOldItemCount = mItemCount;
@@ -472,13 +446,6 @@
                 // Nothing selected
                 checkSelectionChanged();
             }
-
-            if (mChoiceMode != CHOICE_MODE_NONE &&
-                    mAdapter.hasStableIds() &&
-                    mCheckedIdStates == null) {
-                mCheckedIdStates = new LongSparseArray<Boolean>();
-            }
-
         } else {
             mAreAllItemsSelectable = true;
             checkFocus();
@@ -486,14 +453,6 @@
             checkSelectionChanged();
         }
 
-        if (mCheckStates != null) {
-            mCheckStates.clear();
-        }
-        
-        if (mCheckedIdStates != null) {
-            mCheckedIdStates.clear();
-        }
-
         requestLayout();
     }
 
@@ -622,7 +581,7 @@
         final boolean scroll = scrollYDelta != 0;
         if (scroll) {
             scrollListItemsBy(-scrollYDelta);
-            positionSelector(child);
+            positionSelector(INVALID_POSITION, child);
             mSelectedTop = child.getTop();
             invalidate();
         }
@@ -881,6 +840,25 @@
         return topSelectionPixel;
     }
 
+    /**
+     * Smoothly scroll to the specified adapter position. The view will
+     * scroll such that the indicated position is displayed.
+     * @param position Scroll to this adapter position.
+     */
+    @android.view.RemotableViewMethod
+    public void smoothScrollToPosition(int position) {
+        super.smoothScrollToPosition(position);
+    }
+
+    /**
+     * Smoothly scroll to the specified adapter position offset. The view will
+     * scroll such that the indicated position is displayed.
+     * @param offset The amount to offset from the adapter position to scroll to.
+     */
+    @android.view.RemotableViewMethod
+    public void smoothScrollByOffset(int offset) {
+        super.smoothScrollByOffset(offset);
+    }
 
     /**
      * Fills the list based on positioning the new selection relative to the old
@@ -1108,7 +1086,7 @@
 
             if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(
                     ((LayoutParams) child.getLayoutParams()).viewType)) {
-                mRecycler.addScrapView(child);
+                mRecycler.addScrapView(child, -1);
             }
         }
 
@@ -1225,7 +1203,7 @@
             // Recycle the view before we possibly return from the method
             if (recyle && recycleBin.shouldRecycleViewType(
                     ((LayoutParams) child.getLayoutParams()).viewType)) {
-                recycleBin.addScrapView(child);
+                recycleBin.addScrapView(child, -1);
             }
 
             returnedHeight += child.getMeasuredHeight();
@@ -1529,7 +1507,7 @@
             // already cached in mHeaderViews;
             if (dataChanged) {
                 for (int i = 0; i < childCount; i++) {
-                    recycleBin.addScrapView(getChildAt(i));
+                    recycleBin.addScrapView(getChildAt(i), firstPosition+i);
                     if (ViewDebug.TRACE_RECYCLER) {
                         ViewDebug.trace(getChildAt(i),
                                 ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, index, i);
@@ -1632,19 +1610,19 @@
                         if (focused != null) {
                             focused.clearFocus();
                         }
-                        positionSelector(sel);
+                        positionSelector(INVALID_POSITION, sel);
                     } else {
                         sel.setSelected(false);
                         mSelectorRect.setEmpty();
                     }
                 } else {
-                    positionSelector(sel);
+                    positionSelector(INVALID_POSITION, sel);
                 }
                 mSelectedTop = sel.getTop();
             } else {
                 if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) {
                     View child = getChildAt(mMotionPosition - mFirstPosition);
-                    if (child != null) positionSelector(child);
+                    if (child != null) positionSelector(mMotionPosition, child);
                 } else {
                     mSelectedTop = 0;
                     mSelectorRect.setEmpty();
@@ -1725,7 +1703,7 @@
 
 
         if (!mDataChanged) {
-            // Try to use an exsiting view for this position
+            // Try to use an existing view for this position
             child = mRecycler.getActiveView(position);
             if (child != null) {
                 if (ViewDebug.TRACE_RECYCLER) {
@@ -1805,6 +1783,9 @@
         if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
             if (child instanceof Checkable) {
                 ((Checkable) child).setChecked(mCheckStates.get(position));
+            } else if (getContext().getApplicationInfo().targetSdkVersion
+                    >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+                child.setActivated(mCheckStates.get(position));
             }
         }
 
@@ -1839,6 +1820,11 @@
         if (mCachingStarted && !child.isDrawingCacheEnabled()) {
             child.setDrawingCacheEnabled(true);
         }
+
+        if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)
+                != position) {
+            child.jumpDrawablesToCurrentState();
+        }
     }
 
     @Override
@@ -2307,6 +2293,7 @@
         }
 
         View selectedView = getSelectedView();
+        int selectedPos = mSelectedPosition;
 
         int nextSelectedPosition = lookForSelectablePositionOnScreen(direction);
         int amountToScroll = amountToScroll(direction, nextSelectedPosition);
@@ -2324,6 +2311,7 @@
             setSelectedPositionInt(nextSelectedPosition);
             setNextSelectedPositionInt(nextSelectedPosition);
             selectedView = getSelectedView();
+            selectedPos = nextSelectedPosition;
             if (mItemsCanFocus && focusResult == null) {
                 // there was no new view found to take focus, make sure we
                 // don't leave focus with the old selection
@@ -2364,7 +2352,7 @@
 
         if (needToRedraw) {
             if (selectedView != null) {
-                positionSelector(selectedView);
+                positionSelector(selectedPos, selectedView);
                 mSelectedTop = selectedView.getTop();
             }
             if (!awakenScrollBars()) {
@@ -2860,7 +2848,7 @@
                 AbsListView.LayoutParams layoutParams = (LayoutParams) first.getLayoutParams();
                 if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) {
                     detachViewFromParent(first);
-                    recycleBin.addScrapView(first);
+                    recycleBin.addScrapView(first, mFirstPosition);
                 } else {
                     removeViewInLayout(first);
                 }
@@ -2891,7 +2879,7 @@
                 AbsListView.LayoutParams layoutParams = (LayoutParams) last.getLayoutParams();
                 if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) {
                     detachViewFromParent(last);
-                    recycleBin.addScrapView(last);
+                    recycleBin.addScrapView(last, mFirstPosition+lastIndex);
                 } else {
                     removeViewInLayout(last);
                 }
@@ -2960,52 +2948,14 @@
         }
         super.setCacheColorHint(color);
     }
-
-    void drawOverscrollHeader(Canvas canvas, Drawable drawable, Rect bounds) {
-        final int height = drawable.getMinimumHeight();
-
-        canvas.save();
-        canvas.clipRect(bounds);
-
-        final int span = bounds.bottom - bounds.top;
-        if (span < height) {
-            bounds.top = bounds.bottom - height;
-        }
-
-        drawable.setBounds(bounds);
-        drawable.draw(canvas);
-
-        canvas.restore();
-    }
-
-    void drawOverscrollFooter(Canvas canvas, Drawable drawable, Rect bounds) {
-        final int height = drawable.getMinimumHeight();
-
-        canvas.save();
-        canvas.clipRect(bounds);
-
-        final int span = bounds.bottom - bounds.top;
-        if (span < height) {
-            bounds.bottom = bounds.top + height;
-        }
-
-        drawable.setBounds(bounds);
-        drawable.draw(canvas);
-
-        canvas.restore();
-    }
-
+    
     @Override
     protected void dispatchDraw(Canvas canvas) {
         // Draw the dividers
         final int dividerHeight = mDividerHeight;
-        final Drawable overscrollHeader = mOverScrollHeader;
-        final Drawable overscrollFooter = mOverScrollFooter;
-        final boolean drawOverscrollHeader = overscrollHeader != null;
-        final boolean drawOverscrollFooter = overscrollFooter != null;
         final boolean drawDividers = dividerHeight > 0 && mDivider != null;
 
-        if (drawDividers || drawOverscrollHeader || drawOverscrollFooter) {
+        if (drawDividers) {
             // Only modify the top and bottom in the loop, we set the left and right here
             final Rect bounds = mTempRect;
             bounds.left = mPaddingLeft;
@@ -3024,7 +2974,7 @@
             // fill a rect where the dividers would be for non-selectable items
             // If the list is opaque and the background is also opaque, we don't
             // need to draw anything since the background will do it for us
-            final boolean fillForMissingDividers = drawDividers && isOpaque() && !super.isOpaque();
+            final boolean fillForMissingDividers = isOpaque() && !super.isOpaque();
 
             if (fillForMissingDividers && mDividerPaint == null && mIsCacheColorOpaque) {
                 mDividerPaint = new Paint();
@@ -3034,72 +2984,40 @@
 
             final int listBottom = mBottom - mTop - mListPadding.bottom + mScrollY;
             if (!mStackFromBottom) {
-                int bottom = 0;
+                int bottom;
                 
-                // Draw top divider or header for overscroll
-                final int scrollY = mScrollY;
-                if (count > 0 && scrollY < 0) {
-                    if (drawOverscrollHeader) {
-                        bounds.bottom = 0;
-                        bounds.top = scrollY;
-                        drawOverscrollHeader(canvas, overscrollHeader, bounds);
-                    } else if (drawDividers) {
-                        bounds.bottom = 0;
-                        bounds.top = -dividerHeight;
-                        drawDivider(canvas, bounds, -1);
-                    }
-                }
-
                 for (int i = 0; i < count; i++) {
                     if ((headerDividers || first + i >= headerCount) &&
                             (footerDividers || first + i < footerLimit)) {
                         View child = getChildAt(i);
                         bottom = child.getBottom();
                         // Don't draw dividers next to items that are not enabled
-                        if (drawDividers &&
-                                (bottom < listBottom && !(drawOverscrollFooter && i == count - 1))) {
-                            if ((areAllItemsSelectable ||
-                                    (adapter.isEnabled(first + i) && (i == count - 1 ||
-                                            adapter.isEnabled(first + i + 1))))) {
-                                bounds.top = bottom;
-                                bounds.bottom = bottom + dividerHeight;
-                                drawDivider(canvas, bounds, i);
-                            } else if (fillForMissingDividers) {
-                                bounds.top = bottom;
-                                bounds.bottom = bottom + dividerHeight;
-                                canvas.drawRect(bounds, paint);
-                            }
+                        if ((areAllItemsSelectable ||
+                                (adapter.isEnabled(first + i) && (i == count - 1 ||
+                                        adapter.isEnabled(first + i + 1))))) {
+                            bounds.top = bottom;
+                            bounds.bottom = bottom + dividerHeight;
+                            drawDivider(canvas, bounds, i);
+                        } else if (fillForMissingDividers) {
+                            bounds.top = bottom;
+                            bounds.bottom = bottom + dividerHeight;
+                            canvas.drawRect(bounds, paint);
                         }
                     }
                 }
-
-                final int overFooterBottom = mBottom + mScrollY;
-                if (drawOverscrollFooter && first + count == itemCount &&
-                        overFooterBottom > bottom) {
-                    bounds.top = bottom;
-                    bounds.bottom = overFooterBottom;
-                    drawOverscrollFooter(canvas, overscrollFooter, bounds);
-                }
             } else {
                 int top;
                 int listTop = mListPadding.top;
 
                 final int scrollY = mScrollY;
 
-                if (count > 0 && drawOverscrollHeader) {
-                    bounds.top = scrollY;
-                    bounds.bottom = getChildAt(0).getTop();
-                    drawOverscrollHeader(canvas, overscrollHeader, bounds);
-                }
-
-                final int start = drawOverscrollHeader ? 1 : 0;
-                for (int i = start; i < count; i++) {
+                for (int i = 0; i < count; i++) {
                     if ((headerDividers || first + i >= headerCount) &&
                             (footerDividers || first + i < footerLimit)) {
                         View child = getChildAt(i);
                         top = child.getTop();
                         // Don't draw dividers next to items that are not enabled
-                        if (drawDividers && top > listTop) {
+                        if (top > listTop) {
                             if ((areAllItemsSelectable ||
                                     (adapter.isEnabled(first + i) && (i == count - 1 ||
                                             adapter.isEnabled(first + i + 1))))) {
@@ -3120,16 +3038,9 @@
                 }
                 
                 if (count > 0 && scrollY > 0) {
-                    if (drawOverscrollFooter) {
-                        final int absListBottom = mBottom;
-                        bounds.top = absListBottom;
-                        bounds.bottom = absListBottom + scrollY;
-                        drawOverscrollFooter(canvas, overscrollFooter, bounds);
-                    } else if (drawDividers) {
-                        bounds.top = listBottom;
-                        bounds.bottom = listBottom + dividerHeight;
-                        drawDivider(canvas, bounds, -1);
-                    }
+                    bounds.top = listBottom;
+                    bounds.bottom = listBottom + dividerHeight;
+                    drawDivider(canvas, bounds, -1);
                 }
             }
         }
@@ -3150,20 +3061,9 @@
     void drawDivider(Canvas canvas, Rect bounds, int childIndex) {
         // This widget draws the same divider for all children
         final Drawable divider = mDivider;
-        final boolean clipDivider = mClipDivider;
 
-        if (!clipDivider) {
-            divider.setBounds(bounds);
-        } else {
-            canvas.save();
-            canvas.clipRect(bounds);
-        }
-
+        divider.setBounds(bounds);
         divider.draw(canvas);
-
-        if (clipDivider) {
-            canvas.restore();
-        }
     }
 
     /**
@@ -3184,10 +3084,8 @@
     public void setDivider(Drawable divider) {
         if (divider != null) {
             mDividerHeight = divider.getIntrinsicHeight();
-            mClipDivider = divider instanceof ColorDrawable;
         } else {
             mDividerHeight = 0;
-            mClipDivider = false;
         }
         mDivider = divider;
         mDividerIsOpaque = divider == null || divider.getOpacity() == PixelFormat.OPAQUE;
@@ -3238,45 +3136,6 @@
         invalidate();
     }
     
-    /**
-     * Sets the drawable that will be drawn above all other list content.
-     * This area can become visible when the user overscrolls the list.
-     *
-     * @param header The drawable to use
-     */
-    public void setOverscrollHeader(Drawable header) {
-        mOverScrollHeader = header;
-        if (mScrollY < 0) {
-            invalidate();
-        }
-    }
-
-    /**
-     * @return The drawable that will be drawn above all other list content
-     */
-    public Drawable getOverscrollHeader() {
-        return mOverScrollHeader;
-    }
-
-    /**
-     * Sets the drawable that will be drawn below all other list content.
-     * This area can become visible when the user overscrolls the list,
-     * or when the list's content does not fully fill the container area.
-     *
-     * @param footer The drawable to use
-     */
-    public void setOverscrollFooter(Drawable footer) {
-        mOverScrollFooter = footer;
-        invalidate();
-    }
-
-    /**
-     * @return The drawable that will be drawn below all other list content
-     */
-    public Drawable getOverscrollFooter() {
-        return mOverScrollFooter;
-    }
-
     @Override
     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
@@ -3447,177 +3306,6 @@
     }
 
     /**
-     * @see #setChoiceMode(int)
-     *
-     * @return The current choice mode
-     */
-    public int getChoiceMode() {
-        return mChoiceMode;
-    }
-
-    /**
-     * Defines the choice behavior for the List. By default, Lists do not have any choice behavior
-     * ({@link #CHOICE_MODE_NONE}). By setting the choiceMode to {@link #CHOICE_MODE_SINGLE}, the
-     * List allows up to one item to  be in a chosen state. By setting the choiceMode to
-     * {@link #CHOICE_MODE_MULTIPLE}, the list allows any number of items to be chosen.
-     *
-     * @param choiceMode One of {@link #CHOICE_MODE_NONE}, {@link #CHOICE_MODE_SINGLE}, or
-     * {@link #CHOICE_MODE_MULTIPLE}
-     */
-    public void setChoiceMode(int choiceMode) {
-        mChoiceMode = choiceMode;
-        if (mChoiceMode != CHOICE_MODE_NONE) {
-            if (mCheckStates == null) {
-                mCheckStates = new SparseBooleanArray();
-            }
-            if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) {
-                mCheckedIdStates = new LongSparseArray<Boolean>();
-            }
-        }
-    }
-
-    @Override
-    public boolean performItemClick(View view, int position, long id) {
-        boolean handled = false;
-
-        if (mChoiceMode != CHOICE_MODE_NONE) {
-            handled = true;
-
-            if (mChoiceMode == CHOICE_MODE_MULTIPLE) {
-                boolean newValue = !mCheckStates.get(position, false);
-                mCheckStates.put(position, newValue);
-                if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
-                    if (newValue) {
-                        mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
-                    } else {
-                        mCheckedIdStates.delete(mAdapter.getItemId(position));
-                    }
-                }
-            } else {
-                boolean newValue = !mCheckStates.get(position, false);
-                if (newValue) {
-                    mCheckStates.clear();
-                    mCheckStates.put(position, true);
-                    if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
-                        mCheckedIdStates.clear();
-                        mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
-                    }
-                } 
-            }
-
-            mDataChanged = true;
-            rememberSyncState();
-            requestLayout();
-        }
-
-        handled |= super.performItemClick(view, position, id);
-
-        return handled;
-    }
-
-    /**
-     * Sets the checked state of the specified position. The is only valid if
-     * the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or
-     * {@link #CHOICE_MODE_MULTIPLE}.
-     * 
-     * @param position The item whose checked state is to be checked
-     * @param value The new checked state for the item
-     */
-    public void setItemChecked(int position, boolean value) {
-        if (mChoiceMode == CHOICE_MODE_NONE) {
-            return;
-        }
-
-        if (mChoiceMode == CHOICE_MODE_MULTIPLE) {
-            mCheckStates.put(position, value);
-            if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
-                if (value) {
-                    mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
-                } else {
-                    mCheckedIdStates.delete(mAdapter.getItemId(position));
-                }
-            }
-        } else {
-            boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds();
-            // Clear all values if we're checking something, or unchecking the currently
-            // selected item
-            if (value || isItemChecked(position)) {
-                mCheckStates.clear();
-                if (updateIds) {
-                    mCheckedIdStates.clear();
-                }
-            }
-            // this may end up selecting the value we just cleared but this way
-            // we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on
-            if (value) {
-                mCheckStates.put(position, true);
-                if (updateIds) {
-                    mCheckedIdStates.put(mAdapter.getItemId(position), Boolean.TRUE);
-                }
-            }
-        }
-
-        // Do not generate a data change while we are in the layout phase
-        if (!mInLayout && !mBlockLayoutRequests) {
-            mDataChanged = true;
-            rememberSyncState();
-            requestLayout();
-        }
-    }
-
-    /**
-     * Returns the checked state of the specified position. The result is only
-     * valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE}
-     * or {@link #CHOICE_MODE_MULTIPLE}.
-     *
-     * @param position The item whose checked state to return
-     * @return The item's checked state or <code>false</code> if choice mode
-     *         is invalid
-     *
-     * @see #setChoiceMode(int)
-     */
-    public boolean isItemChecked(int position) {
-        if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
-            return mCheckStates.get(position);
-        }
-
-        return false;
-    }
-
-    /**
-     * Returns the currently checked item. The result is only valid if the choice
-     * mode has been set to {@link #CHOICE_MODE_SINGLE}.
-     *
-     * @return The position of the currently checked item or
-     *         {@link #INVALID_POSITION} if nothing is selected
-     *
-     * @see #setChoiceMode(int)
-     */
-    public int getCheckedItemPosition() {
-        if (mChoiceMode == CHOICE_MODE_SINGLE && mCheckStates != null && mCheckStates.size() == 1) {
-            return mCheckStates.keyAt(0);
-        }
-
-        return INVALID_POSITION;
-    }
-
-    /**
-     * Returns the set of checked items in the list. The result is only valid if
-     * the choice mode has not been set to {@link #CHOICE_MODE_NONE}.
-     *
-     * @return  A SparseBooleanArray which will return true for each call to
-     *          get(int position) where position is a position in the list,
-     *          or <code>null</code> if the choice mode is set to
-     *          {@link #CHOICE_MODE_NONE}.
-     */
-    public SparseBooleanArray getCheckedItemPositions() {
-        if (mChoiceMode != CHOICE_MODE_NONE) {
-            return mCheckStates;
-        }
-        return null;
-    }
-
-    /**
      * Returns the set of checked items ids. The result is only valid if the
      * choice mode has not been set to {@link #CHOICE_MODE_NONE}.
      * 
@@ -3626,6 +3314,7 @@
      *         
      * @deprecated Use {@link #getCheckedItemIds()} instead.
      */
+    @Deprecated
     public long[] getCheckItemIds() {
         // Use new behavior that correctly handles stable ID mapping.
         if (mAdapter != null && mAdapter.hasStableIds()) {
@@ -3660,115 +3349,4 @@
         }
         return new long[0];
     }
-    
-    /**
-     * Returns the set of checked items ids. The result is only valid if the
-     * choice mode has not been set to {@link #CHOICE_MODE_NONE} and the adapter
-     * has stable IDs. ({@link ListAdapter#hasStableIds()} == {@code true})
-     * 
-     * @return A new array which contains the id of each checked item in the
-     *         list.
-     */
-    public long[] getCheckedItemIds() {
-        if (mChoiceMode == CHOICE_MODE_NONE || mCheckedIdStates == null || mAdapter == null) {
-            return new long[0];
-        }
-        
-        final LongSparseArray<Boolean> idStates = mCheckedIdStates;
-        final int count = idStates.size();
-        final long[] ids = new long[count];
-        
-        for (int i = 0; i < count; i++) {
-            ids[i] = idStates.keyAt(i);
-        }
-        
-        return ids;
-    }
-
-    /**
-     * Clear any choices previously set
-     */
-    public void clearChoices() {
-        if (mCheckStates != null) {
-            mCheckStates.clear();
-        }
-        if (mCheckedIdStates != null) {
-            mCheckedIdStates.clear();
-        }
-    }
-
-    static class SavedState extends BaseSavedState {
-        SparseBooleanArray checkState;
-        LongSparseArray<Boolean> checkIdState;
-
-        /**
-         * Constructor called from {@link ListView#onSaveInstanceState()}
-         */
-        SavedState(Parcelable superState, SparseBooleanArray checkState,
-                LongSparseArray<Boolean> checkIdState) {
-            super(superState);
-            this.checkState = checkState;
-            this.checkIdState = checkIdState;
-        }
-
-        /**
-         * Constructor called from {@link #CREATOR}
-         */
-        private SavedState(Parcel in) {
-            super(in);
-            checkState = in.readSparseBooleanArray();
-            long[] idState = in.createLongArray();
-
-            if (idState.length > 0) {
-                checkIdState = new LongSparseArray<Boolean>();
-                checkIdState.setValues(idState, Boolean.TRUE);
-            }
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            super.writeToParcel(out, flags);
-            out.writeSparseBooleanArray(checkState);
-            out.writeLongArray(checkIdState != null ? checkIdState.getKeys() : new long[0]);
-        }
-
-        @Override
-        public String toString() {
-            return "ListView.SavedState{"
-                    + Integer.toHexString(System.identityHashCode(this))
-                    + " checkState=" + checkState + "}";
-        }
-
-        public static final Parcelable.Creator<SavedState> CREATOR
-                = new Parcelable.Creator<SavedState>() {
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in);
-            }
-
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
-    }
-
-    @Override
-    public Parcelable onSaveInstanceState() {
-        Parcelable superState = super.onSaveInstanceState();
-        return new SavedState(superState, mCheckStates, mCheckedIdStates);
-    }
-
-    @Override
-    public void onRestoreInstanceState(Parcelable state) {
-        SavedState ss = (SavedState) state;
-
-        super.onRestoreInstanceState(ss.getSuperState());
-
-        if (ss.checkState != null) {
-           mCheckStates = ss.checkState;
-        }
-
-        if (ss.checkIdState != null) {
-            mCheckedIdStates = ss.checkIdState;
-        }
-    }
 }
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 39b1377..27a6ad3 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -413,33 +413,47 @@
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         int keyCode = event.getKeyCode();
-        if (event.getRepeatCount() == 0 && event.isDown() && (
-                keyCode ==  KeyEvent.KEYCODE_HEADSETHOOK ||
-                keyCode ==  KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
-                keyCode ==  KeyEvent.KEYCODE_SPACE)) {
-            doPauseResume();
-            show(sDefaultTimeout);
-            if (mPauseButton != null) {
-                mPauseButton.requestFocus();
+        final boolean uniqueDown = event.getRepeatCount() == 0
+                && event.getAction() == KeyEvent.ACTION_DOWN;
+        if (keyCode ==  KeyEvent.KEYCODE_HEADSETHOOK
+                || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+                || keyCode == KeyEvent.KEYCODE_SPACE) {
+            if (uniqueDown) {
+                doPauseResume();
+                show(sDefaultTimeout);
+                if (mPauseButton != null) {
+                    mPauseButton.requestFocus();
+                }
             }
             return true;
-        } else if (keyCode ==  KeyEvent.KEYCODE_MEDIA_STOP) {
-            if (mPlayer.isPlaying()) {
+        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
+            if (uniqueDown && !mPlayer.isPlaying()) {
+                mPlayer.start();
+                updatePausePlay();
+                show(sDefaultTimeout);
+            }
+            return true;
+        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
+                || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
+            if (uniqueDown && mPlayer.isPlaying()) {
                 mPlayer.pause();
                 updatePausePlay();
+                show(sDefaultTimeout);
             }
             return true;
-        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
-                keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
+        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+                || keyCode == KeyEvent.KEYCODE_VOLUME_UP
+                || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
             // don't show the controls for volume adjustment
             return super.dispatchKeyEvent(event);
         } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
-            hide();
-
+            if (uniqueDown) {
+                hide();
+            }
             return true;
-        } else {
-            show(sDefaultTimeout);
         }
+
+        show(sDefaultTimeout);
         return super.dispatchKeyEvent(event);
     }
 
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
deleted file mode 100644
index f48eb89..0000000
--- a/core/java/android/widget/OverScroller.java
+++ /dev/null
@@ -1,849 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.widget;
-
-import android.content.Context;
-import android.hardware.SensorManager;
-import android.util.FloatMath;
-import android.view.ViewConfiguration;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-/**
- * This class encapsulates scrolling with the ability to overshoot the bounds
- * of a scrolling operation. This class is a drop-in replacement for
- * {@link android.widget.Scroller} in most cases.
- */
-public class OverScroller {
-    private int mMode;
-
-    private MagneticOverScroller mScrollerX;
-    private MagneticOverScroller mScrollerY;
-
-    private final Interpolator mInterpolator;
-
-    private static final int DEFAULT_DURATION = 250;
-    private static final int SCROLL_MODE = 0;
-    private static final int FLING_MODE = 1;
-
-    /**
-     * Creates an OverScroller with a viscous fluid scroll interpolator.
-     * @param context
-     */
-    public OverScroller(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Creates an OverScroller with default edge bounce coefficients.
-     * @param context The context of this application.
-     * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
-     * be used.
-     */
-    public OverScroller(Context context, Interpolator interpolator) {
-        this(context, interpolator, MagneticOverScroller.DEFAULT_BOUNCE_COEFFICIENT,
-                MagneticOverScroller.DEFAULT_BOUNCE_COEFFICIENT);
-    }
-
-    /**
-     * Creates an OverScroller.
-     * @param context The context of this application.
-     * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
-     * be used.
-     * @param bounceCoefficientX A value between 0 and 1 that will determine the proportion of the
-     * velocity which is preserved in the bounce when the horizontal edge is reached. A null value
-     * means no bounce.
-     * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction.
-     */
-    public OverScroller(Context context, Interpolator interpolator,
-            float bounceCoefficientX, float bounceCoefficientY) {
-        mInterpolator = interpolator;
-        mScrollerX = new MagneticOverScroller();
-        mScrollerY = new MagneticOverScroller();
-        MagneticOverScroller.initializeFromContext(context);
-
-        mScrollerX.setBounceCoefficient(bounceCoefficientX);
-        mScrollerY.setBounceCoefficient(bounceCoefficientY);
-    }
-
-    /**
-     *
-     * Returns whether the scroller has finished scrolling.
-     *
-     * @return True if the scroller has finished scrolling, false otherwise.
-     */
-    public final boolean isFinished() {
-        return mScrollerX.mFinished && mScrollerY.mFinished;
-    }
-
-    /**
-     * Force the finished field to a particular value. Contrary to
-     * {@link #abortAnimation()}, forcing the animation to finished
-     * does NOT cause the scroller to move to the final x and y
-     * position.
-     *
-     * @param finished The new finished value.
-     */
-    public final void forceFinished(boolean finished) {
-        mScrollerX.mFinished = mScrollerY.mFinished = finished;
-    }
-
-    /**
-     * Returns the current X offset in the scroll.
-     *
-     * @return The new X offset as an absolute distance from the origin.
-     */
-    public final int getCurrX() {
-        return mScrollerX.mCurrentPosition;
-    }
-
-    /**
-     * Returns the current Y offset in the scroll.
-     *
-     * @return The new Y offset as an absolute distance from the origin.
-     */
-    public final int getCurrY() {
-        return mScrollerY.mCurrentPosition;
-    }
-
-    /**
-     * @hide
-     * Returns the current velocity.
-     *
-     * @return The original velocity less the deceleration, norm of the X and Y velocity vector.
-     */
-    public float getCurrVelocity() {
-        float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity;
-        squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity;
-        return FloatMath.sqrt(squaredNorm);
-    }
-
-    /**
-     * Returns the start X offset in the scroll.
-     *
-     * @return The start X offset as an absolute distance from the origin.
-     */
-    public final int getStartX() {
-        return mScrollerX.mStart;
-    }
-
-    /**
-     * Returns the start Y offset in the scroll.
-     *
-     * @return The start Y offset as an absolute distance from the origin.
-     */
-    public final int getStartY() {
-        return mScrollerY.mStart;
-    }
-
-    /**
-     * Returns where the scroll will end. Valid only for "fling" scrolls.
-     *
-     * @return The final X offset as an absolute distance from the origin.
-     */
-    public final int getFinalX() {
-        return mScrollerX.mFinal;
-    }
-
-    /**
-     * Returns where the scroll will end. Valid only for "fling" scrolls.
-     *
-     * @return The final Y offset as an absolute distance from the origin.
-     */
-    public final int getFinalY() {
-        return mScrollerY.mFinal;
-    }
-
-    /**
-     * Returns how long the scroll event will take, in milliseconds.
-     *
-     * @return The duration of the scroll in milliseconds.
-     *
-     * @hide Pending removal once nothing depends on it
-     * @deprecated OverScrollers don't necessarily have a fixed duration.
-     *             This function will lie to the best of its ability.
-     */
-    public final int getDuration() {
-        return Math.max(mScrollerX.mDuration, mScrollerY.mDuration);
-    }
-
-    /**
-     * Extend the scroll animation. This allows a running animation to scroll
-     * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
-     *
-     * @param extend Additional time to scroll in milliseconds.
-     * @see #setFinalX(int)
-     * @see #setFinalY(int)
-     *
-     * @hide Pending removal once nothing depends on it
-     * @deprecated OverScrollers don't necessarily have a fixed duration.
-     *             Instead of setting a new final position and extending
-     *             the duration of an existing scroll, use startScroll
-     *             to begin a new animation.
-     */
-    public void extendDuration(int extend) {
-        mScrollerX.extendDuration(extend);
-        mScrollerY.extendDuration(extend);
-    }
-
-    /**
-     * Sets the final position (X) for this scroller.
-     *
-     * @param newX The new X offset as an absolute distance from the origin.
-     * @see #extendDuration(int)
-     * @see #setFinalY(int)
-     *
-     * @hide Pending removal once nothing depends on it
-     * @deprecated OverScroller's final position may change during an animation.
-     *             Instead of setting a new final position and extending
-     *             the duration of an existing scroll, use startScroll
-     *             to begin a new animation.
-     */
-    public void setFinalX(int newX) {
-        mScrollerX.setFinalPosition(newX);
-    }
-
-    /**
-     * Sets the final position (Y) for this scroller.
-     *
-     * @param newY The new Y offset as an absolute distance from the origin.
-     * @see #extendDuration(int)
-     * @see #setFinalX(int)
-     *
-     * @hide Pending removal once nothing depends on it
-     * @deprecated OverScroller's final position may change during an animation.
-     *             Instead of setting a new final position and extending
-     *             the duration of an existing scroll, use startScroll
-     *             to begin a new animation.
-     */
-    public void setFinalY(int newY) {
-        mScrollerY.setFinalPosition(newY);
-    }
-
-    /**
-     * Call this when you want to know the new location. If it returns true, the
-     * animation is not yet finished.
-     */
-    public boolean computeScrollOffset() {
-        if (isFinished()) {
-            return false;
-        }
-
-        switch (mMode) {
-            case SCROLL_MODE:
-                long time = AnimationUtils.currentAnimationTimeMillis();
-                // Any scroller can be used for time, since they were started
-                // together in scroll mode. We use X here.
-                final long elapsedTime = time - mScrollerX.mStartTime;
-
-                final int duration = mScrollerX.mDuration;
-                if (elapsedTime < duration) {
-                    float q = (float) (elapsedTime) / duration;
-
-                    if (mInterpolator == null)
-                        q = Scroller.viscousFluid(q);
-                    else
-                        q = mInterpolator.getInterpolation(q);
-
-                    mScrollerX.updateScroll(q);
-                    mScrollerY.updateScroll(q);
-                } else {
-                    abortAnimation();
-                }
-                break;
-
-            case FLING_MODE:
-                if (!mScrollerX.mFinished) {
-                    if (!mScrollerX.update()) {
-                        if (!mScrollerX.continueWhenFinished()) {
-                            mScrollerX.finish();
-                        }
-                    }
-                }
-
-                if (!mScrollerY.mFinished) {
-                    if (!mScrollerY.update()) {
-                        if (!mScrollerY.continueWhenFinished()) {
-                            mScrollerY.finish();
-                        }
-                    }
-                }
-
-                break;
-        }
-
-        return true;
-    }
-
-    /**
-     * Start scrolling by providing a starting point and the distance to travel.
-     * The scroll will use the default value of 250 milliseconds for the
-     * duration.
-     *
-     * @param startX Starting horizontal scroll offset in pixels. Positive
-     *        numbers will scroll the content to the left.
-     * @param startY Starting vertical scroll offset in pixels. Positive numbers
-     *        will scroll the content up.
-     * @param dx Horizontal distance to travel. Positive numbers will scroll the
-     *        content to the left.
-     * @param dy Vertical distance to travel. Positive numbers will scroll the
-     *        content up.
-     */
-    public void startScroll(int startX, int startY, int dx, int dy) {
-        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
-    }
-
-    /**
-     * Start scrolling by providing a starting point and the distance to travel.
-     *
-     * @param startX Starting horizontal scroll offset in pixels. Positive
-     *        numbers will scroll the content to the left.
-     * @param startY Starting vertical scroll offset in pixels. Positive numbers
-     *        will scroll the content up.
-     * @param dx Horizontal distance to travel. Positive numbers will scroll the
-     *        content to the left.
-     * @param dy Vertical distance to travel. Positive numbers will scroll the
-     *        content up.
-     * @param duration Duration of the scroll in milliseconds.
-     */
-    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
-        mMode = SCROLL_MODE;
-        mScrollerX.startScroll(startX, dx, duration);
-        mScrollerY.startScroll(startY, dy, duration);
-    }
-
-    /**
-     * Call this when you want to 'spring back' into a valid coordinate range.
-     *
-     * @param startX Starting X coordinate
-     * @param startY Starting Y coordinate
-     * @param minX Minimum valid X value
-     * @param maxX Maximum valid X value
-     * @param minY Minimum valid Y value
-     * @param maxY Minimum valid Y value
-     * @return true if a springback was initiated, false if startX and startY were
-     *          already within the valid range.
-     */
-    public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
-        mMode = FLING_MODE;
-
-        // Make sure both methods are called.
-        final boolean spingbackX = mScrollerX.springback(startX, minX, maxX);
-        final boolean spingbackY = mScrollerY.springback(startY, minY, maxY);
-        return spingbackX || spingbackY;
-    }
-
-    public void fling(int startX, int startY, int velocityX, int velocityY,
-            int minX, int maxX, int minY, int maxY) {
-        fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
-    }
-
-    /**
-     * Start scrolling based on a fling gesture. The distance traveled will
-     * depend on the initial velocity of the fling.
-     *
-     * @param startX Starting point of the scroll (X)
-     * @param startY Starting point of the scroll (Y)
-     * @param velocityX Initial velocity of the fling (X) measured in pixels per
-     *            second.
-     * @param velocityY Initial velocity of the fling (Y) measured in pixels per
-     *            second
-     * @param minX Minimum X value. The scroller will not scroll past this point
-     *            unless overX > 0. If overfling is allowed, it will use minX as
-     *            a springback boundary.
-     * @param maxX Maximum X value. The scroller will not scroll past this point
-     *            unless overX > 0. If overfling is allowed, it will use maxX as
-     *            a springback boundary.
-     * @param minY Minimum Y value. The scroller will not scroll past this point
-     *            unless overY > 0. If overfling is allowed, it will use minY as
-     *            a springback boundary.
-     * @param maxY Maximum Y value. The scroller will not scroll past this point
-     *            unless overY > 0. If overfling is allowed, it will use maxY as
-     *            a springback boundary.
-     * @param overX Overfling range. If > 0, horizontal overfling in either
-     *            direction will be possible.
-     * @param overY Overfling range. If > 0, vertical overfling in either
-     *            direction will be possible.
-     */
-    public void fling(int startX, int startY, int velocityX, int velocityY,
-            int minX, int maxX, int minY, int maxY, int overX, int overY) {
-        mMode = FLING_MODE;
-        mScrollerX.fling(startX, velocityX, minX, maxX, overX);
-        mScrollerY.fling(startY, velocityY, minY, maxY, overY);
-    }
-
-    /**
-     * Notify the scroller that we've reached a horizontal boundary.
-     * Normally the information to handle this will already be known
-     * when the animation is started, such as in a call to one of the
-     * fling functions. However there are cases where this cannot be known
-     * in advance. This function will transition the current motion and
-     * animate from startX to finalX as appropriate.
-     *
-     * @param startX Starting/current X position
-     * @param finalX Desired final X position
-     * @param overX Magnitude of overscroll allowed. This should be the maximum
-     *              desired distance from finalX. Absolute value - must be positive.
-     */
-    public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
-        mScrollerX.notifyEdgeReached(startX, finalX, overX);
-    }
-
-    /**
-     * Notify the scroller that we've reached a vertical boundary.
-     * Normally the information to handle this will already be known
-     * when the animation is started, such as in a call to one of the
-     * fling functions. However there are cases where this cannot be known
-     * in advance. This function will animate a parabolic motion from
-     * startY to finalY.
-     *
-     * @param startY Starting/current Y position
-     * @param finalY Desired final Y position
-     * @param overY Magnitude of overscroll allowed. This should be the maximum
-     *              desired distance from finalY.
-     */
-    public void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
-        mScrollerY.notifyEdgeReached(startY, finalY, overY);
-    }
-
-    /**
-     * Returns whether the current Scroller is currently returning to a valid position.
-     * Valid bounds were provided by the
-     * {@link #fling(int, int, int, int, int, int, int, int, int, int)} method.
-     *
-     * One should check this value before calling
-     * {@link #startScroll(int, int, int, int)} as the interpolation currently in progress
-     * to restore a valid position will then be stopped. The caller has to take into account
-     * the fact that the started scroll will start from an overscrolled position.
-     *
-     * @return true when the current position is overscrolled and in the process of
-     *         interpolating back to a valid value.
-     */
-    public boolean isOverScrolled() {
-        return ((!mScrollerX.mFinished &&
-                mScrollerX.mState != MagneticOverScroller.TO_EDGE) ||
-                (!mScrollerY.mFinished &&
-                        mScrollerY.mState != MagneticOverScroller.TO_EDGE));
-    }
-
-    /**
-     * Stops the animation. Contrary to {@link #forceFinished(boolean)},
-     * aborting the animating causes the scroller to move to the final x and y
-     * positions.
-     *
-     * @see #forceFinished(boolean)
-     */
-    public void abortAnimation() {
-        mScrollerX.finish();
-        mScrollerY.finish();
-    }
-
-    /**
-     * Returns the time elapsed since the beginning of the scrolling.
-     *
-     * @return The elapsed time in milliseconds.
-     *
-     * @hide
-     */
-    public int timePassed() {
-        final long time = AnimationUtils.currentAnimationTimeMillis();
-        final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime);
-        return (int) (time - startTime);
-    }
-
-    static class MagneticOverScroller {
-        // Initial position
-        int mStart;
-
-        // Current position
-        int mCurrentPosition;
-
-        // Final position
-        int mFinal;
-
-        // Initial velocity
-        int mVelocity;
-
-        // Current velocity
-        float mCurrVelocity;
-
-        // Constant current deceleration
-        float mDeceleration;
-
-        // Animation starting time, in system milliseconds
-        long mStartTime;
-
-        // Animation duration, in milliseconds
-        int mDuration;
-
-        // Whether the animation is currently in progress
-        boolean mFinished;
-
-        // Constant gravity value, used to scale deceleration
-        static float GRAVITY;
-
-        static void initializeFromContext(Context context) {
-            final float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
-            GRAVITY = SensorManager.GRAVITY_EARTH // g (m/s^2)
-                    * 39.37f // inch/meter
-                    * ppi // pixels per inch
-                    * ViewConfiguration.getScrollFriction();
-        }
-
-        private static final int TO_EDGE = 0;
-        private static final int TO_BOUNDARY = 1;
-        private static final int TO_BOUNCE = 2;
-
-        private int mState = TO_EDGE;
-
-        // The allowed overshot distance before boundary is reached.
-        private int mOver;
-
-        // Duration in milliseconds to go back from edge to edge. Springback is half of it.
-        private static final int OVERSCROLL_SPRINGBACK_DURATION = 200;
-
-        // Oscillation period
-        private static final float TIME_COEF =
-            1000.0f * (float) Math.PI / OVERSCROLL_SPRINGBACK_DURATION;
-
-        // If the velocity is smaller than this value, no bounce is triggered
-        // when the edge limits are reached (would result in a zero pixels
-        // displacement anyway).
-        private static final float MINIMUM_VELOCITY_FOR_BOUNCE = Float.MAX_VALUE;//140.0f;
-
-        // Proportion of the velocity that is preserved when the edge is reached.
-        private static final float DEFAULT_BOUNCE_COEFFICIENT = 0.16f;
-
-        private float mBounceCoefficient = DEFAULT_BOUNCE_COEFFICIENT;
-
-        MagneticOverScroller() {
-            mFinished = true;
-        }
-
-        void updateScroll(float q) {
-            mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
-        }
-
-        /*
-         * Get a signed deceleration that will reduce the velocity.
-         */
-        static float getDeceleration(int velocity) {
-            return velocity > 0 ? -GRAVITY : GRAVITY;
-        }
-
-        /*
-         * Returns the time (in milliseconds) it will take to go from start to end.
-         */
-        static int computeDuration(int start, int end, float initialVelocity, float deceleration) {
-            final int distance = start - end;
-            final float discriminant = initialVelocity * initialVelocity - 2.0f * deceleration
-                    * distance;
-            if (discriminant >= 0.0f) {
-                float delta = (float) Math.sqrt(discriminant);
-                if (deceleration < 0.0f) {
-                    delta = -delta;
-                }
-                return (int) (1000.0f * (-initialVelocity - delta) / deceleration);
-            }
-
-            // End position can not be reached
-            return 0;
-        }
-
-        void startScroll(int start, int distance, int duration) {
-            mFinished = false;
-
-            mStart = start;
-            mFinal = start + distance;
-
-            mStartTime = AnimationUtils.currentAnimationTimeMillis();
-            mDuration = duration;
-
-            // Unused
-            mDeceleration = 0.0f;
-            mVelocity = 0;
-        }
-
-        void fling(int start, int velocity, int min, int max) {
-            mFinished = false;
-
-            mStart = start;
-            mStartTime = AnimationUtils.currentAnimationTimeMillis();
-
-            mVelocity = velocity;
-
-            mDeceleration = getDeceleration(velocity);
-
-            // A start from an invalid position immediately brings back to a valid position
-            if (mStart < min) {
-                mDuration = 0;
-                mFinal = min;
-                return;
-            }
-
-            if (mStart > max) {
-                mDuration = 0;
-                mFinal = max;
-                return;
-            }
-
-            // Duration are expressed in milliseconds
-            mDuration = (int) (-1000.0f * velocity / mDeceleration);
-
-            mFinal = start - Math.round((velocity * velocity) / (2.0f * mDeceleration));
-
-            // Clamp to a valid final position
-            if (mFinal < min) {
-                mFinal = min;
-                mDuration = computeDuration(mStart, min, mVelocity, mDeceleration);
-            }
-
-            if (mFinal > max) {
-                mFinal = max;
-                mDuration = computeDuration(mStart, max, mVelocity, mDeceleration);
-            }
-        }
-
-        void finish() {
-            mCurrentPosition = mFinal;
-            // Not reset since WebView relies on this value for fast fling.
-            // mCurrVelocity = 0.0f;
-            mFinished = true;
-        }
-
-        void setFinalPosition(int position) {
-            mFinal = position;
-            mFinished = false;
-        }
-
-        void extendDuration(int extend) {
-            final long time = AnimationUtils.currentAnimationTimeMillis();
-            final int elapsedTime = (int) (time - mStartTime);
-            mDuration = elapsedTime + extend;
-            mFinished = false;
-        }
-
-        void setBounceCoefficient(float coefficient) {
-            mBounceCoefficient = coefficient;
-        }
-
-        boolean springback(int start, int min, int max) {
-            mFinished = true;
-
-            mStart = start;
-            mVelocity = 0;
-
-            mStartTime = AnimationUtils.currentAnimationTimeMillis();
-            mDuration = 0;
-
-            if (start < min) {
-                startSpringback(start, min, false);
-            } else if (start > max) {
-                startSpringback(start, max, true);
-            }
-
-            return !mFinished;
-        }
-
-        private void startSpringback(int start, int end, boolean positive) {
-            mFinished = false;
-            mState = TO_BOUNCE;
-            mStart = mFinal = end;
-            mDuration = OVERSCROLL_SPRINGBACK_DURATION;
-            mStartTime -= OVERSCROLL_SPRINGBACK_DURATION / 2;
-            mVelocity = (int) (Math.abs(end - start) * TIME_COEF * (positive ? 1.0 : -1.0f));
-        }
-
-        void fling(int start, int velocity, int min, int max, int over) {
-            mState = TO_EDGE;
-            mOver = over;
-
-            mFinished = false;
-
-            mStart = start;
-            mStartTime = AnimationUtils.currentAnimationTimeMillis();
-
-            mVelocity = velocity;
-
-            mDeceleration = getDeceleration(velocity);
-
-            // Duration are expressed in milliseconds
-            mDuration = (int) (-1000.0f * velocity / mDeceleration);
-
-            mFinal = start - Math.round((velocity * velocity) / (2.0f * mDeceleration));
-
-            // Clamp to a valid final position
-            if (mFinal < min) {
-                mFinal = min;
-                mDuration = computeDuration(mStart, min, mVelocity, mDeceleration);
-            }
-
-            if (mFinal > max) {
-                mFinal = max;
-                mDuration = computeDuration(mStart, max, mVelocity, mDeceleration);
-            }
-
-            if (start > max) {
-                if (start >= max + over) {
-                    springback(max + over, min, max);
-                } else {
-                    if (velocity <= 0) {
-                        springback(start, min, max);
-                    } else {
-                        long time = AnimationUtils.currentAnimationTimeMillis();
-                        final double durationSinceEdge =
-                            Math.atan((start-max) * TIME_COEF / velocity) / TIME_COEF;
-                        mStartTime = (int) (time - 1000.0f * durationSinceEdge);
-
-                        // Simulate a bounce that started from edge
-                        mStart = max;
-
-                        mVelocity = (int) (velocity / Math.cos(durationSinceEdge * TIME_COEF));
-
-                        onEdgeReached();
-                    }
-                }
-            } else {
-                if (start < min) {
-                    if (start <= min - over) {
-                        springback(min - over, min, max);
-                    } else {
-                        if (velocity >= 0) {
-                            springback(start, min, max);
-                        } else {
-                            long time = AnimationUtils.currentAnimationTimeMillis();
-                            final double durationSinceEdge =
-                                Math.atan((start-min) * TIME_COEF / velocity) / TIME_COEF;
-                            mStartTime = (int) (time - 1000.0f * durationSinceEdge);
-
-                            // Simulate a bounce that started from edge
-                            mStart = min;
-
-                            mVelocity = (int) (velocity / Math.cos(durationSinceEdge * TIME_COEF));
-
-                            onEdgeReached();
-                        }
-
-                    }
-                }
-            }
-        }
-
-        void notifyEdgeReached(int start, int end, int over) {
-            mDeceleration = getDeceleration(mVelocity);
-
-            // Local time, used to compute edge crossing time.
-            float timeCurrent = mCurrVelocity / mDeceleration;
-            final int distance = end - start;
-            float timeEdge = -(float) Math.sqrt((2.0f * distance / mDeceleration)
-                    + (timeCurrent * timeCurrent));
-
-            mVelocity = (int) (mDeceleration * timeEdge);
-
-            // Simulate a symmetric bounce that started from edge
-            mStart = end;
-
-            mOver = over;
-
-            long time = AnimationUtils.currentAnimationTimeMillis();
-            mStartTime = (int) (time - 1000.0f * (timeCurrent - timeEdge));
-
-            onEdgeReached();
-        }
-
-        private void onEdgeReached() {
-            // mStart, mVelocity and mStartTime were adjusted to their values when edge was reached.
-            final float distance = mVelocity / TIME_COEF;
-
-            if (Math.abs(distance) < mOver) {
-                // Spring force will bring us back to final position
-                mState = TO_BOUNCE;
-                mFinal = mStart;
-                mDuration = OVERSCROLL_SPRINGBACK_DURATION;
-            } else {
-                // Velocity is too high, we will hit the boundary limit
-                mState = TO_BOUNDARY;
-                int over = mVelocity > 0 ? mOver : -mOver;
-                mFinal = mStart + over;
-                mDuration = (int) (1000.0f * Math.asin(over / distance) / TIME_COEF);
-            }
-        }
-
-        boolean continueWhenFinished() {
-            switch (mState) {
-                case TO_EDGE:
-                    // Duration from start to null velocity
-                    int duration = (int) (-1000.0f * mVelocity / mDeceleration);
-                    if (mDuration < duration) {
-                        // If the animation was clamped, we reached the edge
-                        mStart = mFinal;
-                        // Speed when edge was reached
-                        mVelocity = (int) (mVelocity + mDeceleration * mDuration / 1000.0f);
-                        mStartTime += mDuration;
-                        onEdgeReached();
-                    } else {
-                        // Normal stop, no need to continue
-                        return false;
-                    }
-                    break;
-                case TO_BOUNDARY:
-                    mStartTime += mDuration;
-                    startSpringback(mFinal, mFinal - (mVelocity > 0 ? mOver:-mOver), mVelocity > 0);
-                    break;
-                case TO_BOUNCE:
-                    //mVelocity = (int) (mVelocity * BOUNCE_COEFFICIENT);
-                    mVelocity = (int) (mVelocity * mBounceCoefficient);
-                    if (Math.abs(mVelocity) < MINIMUM_VELOCITY_FOR_BOUNCE) {
-                        return false;
-                    }
-                    mStartTime += mDuration;
-                    break;
-            }
-
-            update();
-            return true;
-        }
-
-        /*
-         * Update the current position and velocity for current time. Returns
-         * true if update has been done and false if animation duration has been
-         * reached.
-         */
-        boolean update() {
-            final long time = AnimationUtils.currentAnimationTimeMillis();
-            final long duration = time - mStartTime;
-
-            if (duration > mDuration) {
-                return false;
-            }
-
-            double distance;
-            final float t = duration / 1000.0f;
-            if (mState == TO_EDGE) {
-                mCurrVelocity = mVelocity + mDeceleration * t;
-                distance = mVelocity * t + mDeceleration * t * t / 2.0f;
-            } else {
-                final float d = t * TIME_COEF;
-                mCurrVelocity = mVelocity * (float)Math.cos(d);
-                distance = mVelocity / TIME_COEF * Math.sin(d);
-            }
-
-            mCurrentPosition = mStart + (int) distance;
-            return true;
-        }
-    }
-}
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
new file mode 100644
index 0000000..82770ad
--- /dev/null
+++ b/core/java/android/widget/PopupMenu.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuPopupHelper;
+import com.android.internal.view.menu.SubMenuBuilder;
+
+import android.content.Context;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+/**
+ * A PopupMenu displays a {@link Menu} in a modal popup window anchored to a {@link View}.
+ * The popup will appear below the anchor view if there is room, or above it if there is not.
+ * If the IME is visible the popup will not overlap it until it is touched. Touching outside
+ * of the popup will dismiss it.
+ */
+public class PopupMenu implements MenuBuilder.Callback {
+    private Context mContext;
+    private MenuBuilder mMenu;
+    private View mAnchor;
+    private MenuPopupHelper mPopup;
+    private OnMenuItemClickListener mMenuItemClickListener;
+
+    /**
+     * Construct a new PopupMenu.
+     *
+     * @param context Context for the PopupMenu.
+     * @param anchor Anchor view for this popup. The popup will appear below the anchor if there
+     *               is room, or above it if there is not.
+     */
+    public PopupMenu(Context context, View anchor) {
+        // TODO Theme?
+        mContext = context;
+        mMenu = new MenuBuilder(context);
+        mMenu.setCallback(this);
+        mAnchor = anchor;
+        mPopup = new MenuPopupHelper(context, mMenu, anchor);
+    }
+
+    /**
+     * @return the {@link Menu} associated with this popup. Populate the returned Menu with
+     * items before calling {@link #show()}.
+     *
+     * @see #show()
+     * @see #getMenuInflater()
+     */
+    public Menu getMenu() {
+        return mMenu;
+    }
+
+    /**
+     * @return a {@link MenuInflater} that can be used to inflate menu items from XML into the
+     * menu returned by {@link #getMenu()}.
+     *
+     * @see #getMenu()
+     */
+    public MenuInflater getMenuInflater() {
+        return new MenuInflater(mContext);
+    }
+
+    /**
+     * Show the menu popup anchored to the view specified during construction.
+     * @see #dismiss()
+     */
+    public void show() {
+        mPopup.show();
+    }
+
+    /**
+     * Dismiss the menu popup.
+     * @see #show()
+     */
+    public void dismiss() {
+        mPopup.dismiss();
+    }
+
+    public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
+        mMenuItemClickListener = listener;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+        if (mMenuItemClickListener != null) {
+            return mMenuItemClickListener.onMenuItemClick(item);
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+    }
+
+    /**
+     * @hide
+     */
+    public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+        if (!subMenu.hasVisibleItems()) {
+            return true;
+        }
+
+        // Current menu will be dismissed by the normal helper, submenu will be shown in its place.
+        new MenuPopupHelper(mContext, subMenu, mAnchor).show();
+        return true;
+    }
+
+    /**
+     * @hide
+     */
+    public void onCloseSubMenu(SubMenuBuilder menu) {
+    }
+
+    /**
+     * @hide
+     */
+    public void onMenuModeChange(MenuBuilder menu) {
+    }
+
+    /**
+     * Interface responsible for receiving menu item click events if the items themselves
+     * do not have individual item click listeners.
+     */
+    public interface OnMenuItemClickListener {
+        /**
+         * This method will be invoked when a menu item is clicked if the item itself did
+         * not already handle the event.
+         *
+         * @param item {@link MenuItem} that was clicked
+         * @return <code>true</code> if the event was handled, <code>false</code> otherwise.
+         */
+        public boolean onMenuItemClick(MenuItem item);
+    }
+}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 76755de..4b7c071 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -19,22 +19,24 @@
 import com.android.internal.R;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.Gravity;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnScrollChangedListener;
-import android.view.View.OnTouchListener;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.StateListDrawable;
 import android.os.IBinder;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnScrollChangedListener;
+import android.view.WindowManager;
 
 import java.lang.ref.WeakReference;
 
@@ -87,6 +89,7 @@
     private boolean mClippingEnabled = true;
     private boolean mSplitTouchEnabled;
     private boolean mLayoutInScreen;
+    private boolean mClipToScreen;
 
     private OnTouchListener mTouchInterceptor;
     
@@ -99,7 +102,7 @@
 
     private int mPopupWidth;
     private int mPopupHeight;
-    
+
     private int[] mDrawingLocation = new int[2];
     private int[] mScreenLocation = new int[2];
     private Rect mTempRect = new Rect();
@@ -124,7 +127,7 @@
     private OnScrollChangedListener mOnScrollChangedListener =
         new OnScrollChangedListener() {
             public void onScrollChanged() {
-                View anchor = mAnchor.get();
+                View anchor = mAnchor != null ? mAnchor.get() : null;
                 if (anchor != null && mPopupView != null) {
                     WindowManager.LayoutParams p = (WindowManager.LayoutParams)
                             mPopupView.getLayoutParams();
@@ -160,12 +163,21 @@
      * <p>The popup does provide a background.</p>
      */
     public PopupWindow(Context context, AttributeSet attrs, int defStyle) {
+        this(context, attrs, defStyle, 0);
+    }
+    
+    /**
+     * <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
+     * 
+     * <p>The popup does not provide a background.</p>
+     */
+    public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         mContext = context;
         mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
 
         TypedArray a =
             context.obtainStyledAttributes(
-                attrs, com.android.internal.R.styleable.PopupWindow, defStyle, 0);
+                attrs, com.android.internal.R.styleable.PopupWindow, defStyleAttr, defStyleRes);
 
         mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground);
 
@@ -571,6 +583,17 @@
     }
 
     /**
+     * Clip this popup window to the screen, but not to the containing window.
+     *
+     * @param enabled True to clip to the screen.
+     * @hide
+     */
+    public void setClipToScreenEnabled(boolean enabled) {
+        mClipToScreen = enabled;
+        setClippingEnabled(!enabled);
+    }
+    
+    /**
      * <p>Indicates whether the popup window supports splitting touches.</p>
      * 
      * @return true if the touch splitting is enabled, false otherwise
@@ -1048,6 +1071,21 @@
             }
         }
 
+        if (mClipToScreen) {
+            final int displayFrameWidth = displayFrame.right - displayFrame.left;
+
+            int right = p.x + p.width;
+            if (right > displayFrameWidth) {
+                p.x -= right - displayFrameWidth;
+            }
+            if (p.x < displayFrame.left) {
+                p.x = displayFrame.left;
+                p.width = Math.min(p.width, displayFrameWidth);
+            }
+
+            p.y = Math.max(p.y, displayFrame.top);
+        }
+
         p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
         
         return onTop;
@@ -1108,7 +1146,9 @@
         
         int bottomEdge = displayFrame.bottom;
         if (ignoreBottomDecorations) {
-            bottomEdge = anchor.getContext().getResources().getDisplayMetrics().heightPixels;
+            Resources res = anchor.getContext().getResources();
+            bottomEdge = res.getDisplayMetrics().heightPixels -
+                    (int) res.getDimension(com.android.internal.R.dimen.screen_margin_bottom);
         }
         final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
         final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
@@ -1401,6 +1441,7 @@
     }
 
     private class PopupViewContainer extends FrameLayout {
+        private static final String TAG = "PopupWindow.PopupViewContainer";
 
         public PopupViewContainer(Context context) {
             super(context);
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index ec7d927..b7048fc 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -455,6 +455,13 @@
     }
 
     @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+        if (mProgressDrawable != null) mProgressDrawable.jumpToCurrentState();
+        if (mIndeterminateDrawable != null) mIndeterminateDrawable.jumpToCurrentState();
+    }
+
+    @Override
     public void postInvalidate() {
         if (!mNoInvalidate) {
             super.postInvalidate();
@@ -716,8 +723,8 @@
             mAnimation.setDuration(mDuration);
             mAnimation.setInterpolator(mInterpolator);
             mAnimation.setStartTime(Animation.START_ON_FIRST_FRAME);
-            postInvalidate();
         }
+        postInvalidate();
     }
 
     /**
@@ -730,6 +737,7 @@
             ((Animatable) mIndeterminateDrawable).stop();
             mShouldStartAnimationDrawable = false;
         }
+        postInvalidate();
     }
 
     /**
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index e7d44a0..5598c65 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -48,7 +48,7 @@
     private QueryHandler mQueryHandler;
     private Drawable mBadgeBackground;
     private Drawable mNoBadgeBackground;
-    private int mSelectedContactsAppTabIndex = -1;
+    private Drawable mDefaultAvatar;
 
     protected String[] mExcludeMimes = null;
 
@@ -118,6 +118,16 @@
     public void setMode(int size) {
         mMode = size;
     }
+    
+    /**
+     * Resets the contact photo to the default state.
+     */
+    public void setImageToDefault() {
+        if (mDefaultAvatar == null) {
+            mDefaultAvatar = getResources().getDrawable(R.drawable.ic_contact_picture);
+        }
+        setImageDrawable(mDefaultAvatar);
+    }
 
     /**
      * Assign the contact uri that this QuickContactBadge should be associated
@@ -134,15 +144,6 @@
         onContactUriChanged();
     }
 
-    /**
-     * Sets the currently selected tab of the Contacts application. If not set, this is -1
-     * and therefore does not save a tab selection when a phone call is being made
-     * @hide
-     */
-    public void setSelectedContactsAppTabIndex(int value) {
-        mSelectedContactsAppTabIndex = value;
-    }
-
     private void onContactUriChanged() {
         if (mContactUri == null && mContactEmail == null && mContactPhone == null) {
             if (mNoBadgeBackground == null) {
@@ -225,13 +226,7 @@
     }
 
     private void trigger(Uri lookupUri) {
-        final Intent intent = QuickContact.getQuickContactIntent(getContext(), this, lookupUri,
-                mMode, mExcludeMimes);
-        if (mSelectedContactsAppTabIndex != -1) {
-            intent.putExtra(QuickContact.EXTRA_SELECTED_CONTACTS_APP_TAB_INDEX,
-                    mSelectedContactsAppTabIndex);
-        }
-        getContext().startActivity(intent);
+        QuickContact.showQuickContact(getContext(), this, lookupUri, mMode, mExcludeMimes);
     }
 
     private class QueryHandler extends AsyncQueryHandler {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7a70c80..24165aa 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -16,7 +16,15 @@
 
 package android.widget;
 
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
 import android.app.PendingIntent;
+import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -37,13 +45,7 @@
 import android.view.ViewGroup;
 import android.view.LayoutInflater.Filter;
 import android.view.View.OnClickListener;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
+import android.widget.AdapterView.OnItemClickListener;
 
 
 /**
@@ -60,12 +62,12 @@
      * The package name of the package containing the layout 
      * resource. (Added to the parcel)
      */
-    private String mPackage;
+    private final String mPackage;
     
     /**
      * The resource ID of the layout file. (Added to the parcel)
      */
-    private int mLayoutId;
+    private final int mLayoutId;
 
     /**
      * An array of actions to perform on the view tree once it has been
@@ -73,8 +75,22 @@
      */
     private ArrayList<Action> mActions;
     
+    /**
+     * A class to keep track of memory usage by this RemoteViews
+     */
+    private MemoryUsageCounter mMemoryUsageCounter;
+
     
     /**
+     * This flag indicates whether this RemoteViews object is being created from a
+     * RemoteViewsService for use as a child of a widget collection. This flag is used
+     * to determine whether or not certain features are available, in particular,
+     * setting on click extras and setting on click pending intents. The former is enabled,
+     * and the latter disabled when this flag is true.
+     */
+     private boolean mIsWidgetCollectionChild = false;
+
+    /**
      * This annotation indicates that a subclass of View is alllowed to be used
      * with the {@link RemoteViews} mechanism.
      */
@@ -95,7 +111,7 @@
             super(message);
         }
     }
-    
+
     /**
      * Base class for all actions that can be performed on an 
      * inflated view.
@@ -108,6 +124,234 @@
         public int describeContents() {
             return 0;
         }
+
+        /**
+         * Overridden by each class to report on it's own memory usage
+         */
+        public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
+            // We currently only calculate Bitmap memory usage, so by default, don't do anything
+            // here
+            return;
+        }
+
+        protected boolean startIntentSafely(Context context, PendingIntent pendingIntent,
+                Intent fillInIntent) {
+            try {
+                // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
+                context.startIntentSender(
+                        pendingIntent.getIntentSender(), fillInIntent,
+                        Intent.FLAG_ACTIVITY_NEW_TASK,
+                        Intent.FLAG_ACTIVITY_NEW_TASK, 0);
+            } catch (IntentSender.SendIntentException e) {
+                android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
+                return false;
+            } catch (Exception e) {
+                android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " +
+                        "unknown exception: ", e);
+                return false;
+            }
+            return true;
+        }
+    }
+
+    private class SetEmptyView extends Action {
+        int viewId;
+        int emptyViewId;
+
+        public final static int TAG = 6;
+
+        SetEmptyView(int viewId, int emptyViewId) {
+            this.viewId = viewId;
+            this.emptyViewId = emptyViewId;
+        }
+
+        SetEmptyView(Parcel in) {
+            this.viewId = in.readInt();
+            this.emptyViewId = in.readInt();
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeInt(TAG);
+            out.writeInt(this.viewId);
+            out.writeInt(this.emptyViewId);
+        }
+
+        @Override
+        public void apply(View root) {
+            final View view = root.findViewById(viewId);
+            if (!(view instanceof AdapterView<?>)) return;
+
+            AdapterView<?> adapterView = (AdapterView<?>) view;
+
+            final View emptyView = root.findViewById(emptyViewId);
+            if (emptyView == null) return;
+
+            adapterView.setEmptyView(emptyView);
+        }
+    }
+
+    private class SetOnClickFillInIntent extends Action {
+        public SetOnClickFillInIntent(int id, Intent fillInIntent) {
+            this.viewId = id;
+            this.fillInIntent = fillInIntent;
+        }
+
+        public SetOnClickFillInIntent(Parcel parcel) {
+            viewId = parcel.readInt();
+            fillInIntent = Intent.CREATOR.createFromParcel(parcel);
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(TAG);
+            dest.writeInt(viewId);
+            fillInIntent.writeToParcel(dest, 0 /* no flags */);
+        }
+
+        @Override
+        public void apply(View root) {
+            final View target = root.findViewById(viewId);
+            if (target == null) return;
+
+            if (!mIsWidgetCollectionChild) {
+                Log.e("RemoteViews", "The method setOnClickFillInIntent is available " +
+                        "only from RemoteViewsFactory (ie. on collection items).");
+                return;
+            }
+            if (target == root) {
+                target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
+            } else if (target != null && fillInIntent != null) {
+                OnClickListener listener = new OnClickListener() {
+                    public void onClick(View v) {
+                        // Insure that this view is a child of an AdapterView
+                        View parent = (View) v.getParent();
+                        while (!(parent instanceof AdapterView<?>)
+                                && !(parent instanceof AppWidgetHostView)) {
+                            parent = (View) parent.getParent();
+                        }
+
+                        if (parent instanceof AppWidgetHostView) {
+                            // Somehow they've managed to get this far without having
+                            // and AdapterView as a parent.
+                            Log.e("RemoteViews", "Collection item doesn't have AdapterView parent");
+                            return;
+                        }
+
+                        // Insure that a template pending intent has been set on an ancestor
+                        if (!(parent.getTag() instanceof PendingIntent)) {
+                            Log.e("RemoteViews", "Attempting setOnClickFillInIntent without" +
+                                    " calling setPendingIntentTemplate on parent.");
+                            return;
+                        }
+
+                        PendingIntent pendingIntent = (PendingIntent) parent.getTag();
+
+                        final float appScale = v.getContext().getResources()
+                                .getCompatibilityInfo().applicationScale;
+                        final int[] pos = new int[2];
+                        v.getLocationOnScreen(pos);
+
+                        final Rect rect = new Rect();
+                        rect.left = (int) (pos[0] * appScale + 0.5f);
+                        rect.top = (int) (pos[1] * appScale + 0.5f);
+                        rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
+                        rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
+
+                        fillInIntent.setSourceBounds(rect);
+                        startIntentSafely(v.getContext(), pendingIntent, fillInIntent);
+                    }
+
+                };
+                target.setOnClickListener(listener);
+            }
+        }
+
+        int viewId;
+        Intent fillInIntent;
+
+        public final static int TAG = 9;
+    }
+
+    private class SetPendingIntentTemplate extends Action {
+        public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) {
+            this.viewId = id;
+            this.pendingIntentTemplate = pendingIntentTemplate;
+        }
+
+        public SetPendingIntentTemplate(Parcel parcel) {
+            viewId = parcel.readInt();
+            pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(TAG);
+            dest.writeInt(viewId);
+            pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
+        }
+
+        @Override
+        public void apply(View root) {
+            final View target = root.findViewById(viewId);
+            if (target == null) return;
+
+            // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense
+            if (target instanceof AdapterView<?>) {
+                AdapterView<?> av = (AdapterView<?>) target;
+                // The PendingIntent template is stored in the view's tag.
+                OnItemClickListener listener = new OnItemClickListener() {
+                    public void onItemClick(AdapterView<?> parent, View view,
+                            int position, long id) {
+                        // The view should be a frame layout
+                        if (view instanceof ViewGroup) {
+                            ViewGroup vg = (ViewGroup) view;
+
+                            // AdapterViews contain their children in a frame
+                            // so we need to go one layer deeper here.
+                            if (parent instanceof AdapterViewAnimator) {
+                                vg = (ViewGroup) vg.getChildAt(0);
+                            }
+                            if (vg == null) return;
+
+                            Intent fillInIntent = null;
+                            int childCount = vg.getChildCount();
+                            for (int i = 0; i < childCount; i++) {
+                                Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent);
+                                if (tag instanceof Intent) {
+                                    fillInIntent = (Intent) tag;
+                                    break;
+                                }
+                            }
+                            if (fillInIntent == null) return;
+
+                            final float appScale = view.getContext().getResources()
+                                    .getCompatibilityInfo().applicationScale;
+                            final int[] pos = new int[2];
+                            view.getLocationOnScreen(pos);
+
+                            final Rect rect = new Rect();
+                            rect.left = (int) (pos[0] * appScale + 0.5f);
+                            rect.top = (int) (pos[1] * appScale + 0.5f);
+                            rect.right = (int) ((pos[0] + view.getWidth()) * appScale + 0.5f);
+                            rect.bottom = (int) ((pos[1] + view.getHeight()) * appScale + 0.5f);
+
+                            final Intent intent = new Intent();
+                            intent.setSourceBounds(rect);
+                            startIntentSafely(view.getContext(), pendingIntentTemplate, fillInIntent);
+                        }
+                    }
+                };
+                av.setOnItemClickListener(listener);
+                av.setTag(pendingIntentTemplate);
+            } else {
+                Log.e("RemoteViews", "Cannot setPendingIntentTemplate on a view which is not" +
+                        "an AdapterView (id: " + viewId + ")");
+                return;
+            }
+        }
+
+        int viewId;
+        PendingIntent pendingIntentTemplate;
+
+        public final static int TAG = 8;
     }
 
     /**
@@ -120,21 +364,31 @@
             this.viewId = id;
             this.pendingIntent = pendingIntent;
         }
-        
+
         public SetOnClickPendingIntent(Parcel parcel) {
             viewId = parcel.readInt();
             pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
         }
-        
+
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(TAG);
             dest.writeInt(viewId);
             pendingIntent.writeToParcel(dest, 0 /* no flags */);
         }
-        
+
         @Override
         public void apply(View root) {
             final View target = root.findViewById(viewId);
+            if (target == null) return;
+
+            // If the view is an AdapterView, setting a PendingIntent on click doesn't make much
+            // sense, do they mean to set a PendingIntent template for the AdapterView's children?
+            if (mIsWidgetCollectionChild) {
+                Log.e("RemoteViews", "Cannot setOnClickPendingIntent for collection item " +
+				"(id: " + viewId + ")");
+                // TODO: return; We'll let this slide until apps are up to date.
+            }
+
             if (target != null && pendingIntent != null) {
                 OnClickListener listener = new OnClickListener() {
                     public void onClick(View v) {
@@ -153,15 +407,7 @@
 
                         final Intent intent = new Intent();
                         intent.setSourceBounds(rect);
-                        try {
-                            // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
-                            v.getContext().startIntentSender(
-                                    pendingIntent.getIntentSender(), intent,
-                                    Intent.FLAG_ACTIVITY_NEW_TASK,
-                                    Intent.FLAG_ACTIVITY_NEW_TASK, 0);
-                        } catch (IntentSender.SendIntentException e) {
-                            android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e);
-                        }
+                        startIntentSafely(v.getContext(), pendingIntent, intent);
                     }
                 };
                 target.setOnClickListener(listener);
@@ -229,9 +475,7 @@
         @Override
         public void apply(View root) {
             final View target = root.findViewById(viewId);
-            if (target == null) {
-                return;
-            }
+            if (target == null) return;
             
             // Pick the correct drawable to modify for this view
             Drawable targetDrawable = null;
@@ -255,7 +499,7 @@
                 }
             }
         }
-        
+
         int viewId;
         boolean targetBackground;
         int alpha;
@@ -266,6 +510,61 @@
         public final static int TAG = 3;
     }
     
+    private class ReflectionActionWithoutParams extends Action {
+        int viewId;
+        String methodName;
+
+        public final static int TAG = 5;
+
+        ReflectionActionWithoutParams(int viewId, String methodName) {
+            this.viewId = viewId;
+            this.methodName = methodName;
+        }
+
+        ReflectionActionWithoutParams(Parcel in) {
+            this.viewId = in.readInt();
+            this.methodName = in.readString();
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeInt(TAG);
+            out.writeInt(this.viewId);
+            out.writeString(this.methodName);
+        }
+
+        @Override
+        public void apply(View root) {
+            final View view = root.findViewById(viewId);
+            if (view == null) return;
+
+            Class klass = view.getClass();
+            Method method;
+            try {
+                method = klass.getMethod(this.methodName);
+            } catch (NoSuchMethodException ex) {
+                throw new ActionException("view: " + klass.getName() + " doesn't have method: "
+                        + this.methodName + "()");
+            }
+
+            if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
+                throw new ActionException("view: " + klass.getName()
+                        + " can't use method with RemoteViews: "
+                        + this.methodName + "()");
+            }
+
+            try {
+                //noinspection ConstantIfStatement
+                if (false) {
+                    Log.d("RemoteViews", "view: " + klass.getName() + " calling method: "
+                        + this.methodName + "()");
+                }
+                method.invoke(view);
+            } catch (Exception ex) {
+                throw new ActionException(ex);
+            }
+        }
+    }
+
     /**
      * Base class for the reflection actions.
      */
@@ -285,6 +584,7 @@
         static final int URI = 11;
         static final int BITMAP = 12;
         static final int BUNDLE = 13;
+        static final int INTENT = 14;
 
         int viewId;
         String methodName;
@@ -347,6 +647,9 @@
                 case BUNDLE:
                     this.value = in.readBundle();
                     break;
+                case INTENT:
+                    this.value = Intent.CREATOR.createFromParcel(in);
+                    break;
                 default:
                     break;
             }
@@ -402,6 +705,9 @@
                 case BUNDLE:
                     out.writeBundle((Bundle) this.value);
                     break;
+                case INTENT:
+                    ((Intent)this.value).writeToParcel(out, flags);
+                    break;
                 default:
                     break;
             }
@@ -435,6 +741,8 @@
                     return Bitmap.class;
                 case BUNDLE:
                     return Bundle.class;
+                case INTENT:
+                    return Intent.class;
                 default:
                     return null;
             }
@@ -443,9 +751,7 @@
         @Override
         public void apply(View root) {
             final View view = root.findViewById(viewId);
-            if (view == null) {
-                throw new ActionException("can't find view: 0x" + Integer.toHexString(viewId));
-            }
+            if (view == null) return;
 
             Class param = getParameterType();
             if (param == null) {
@@ -481,6 +787,35 @@
                 throw new ActionException(ex);
             }
         }
+
+        @Override
+        public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
+            // We currently only calculate Bitmap memory usage
+            switch (this.type) {
+                case BITMAP:
+                    if (this.value != null) {
+                        final Bitmap b = (Bitmap) this.value;
+                        final Bitmap.Config c = b.getConfig();
+                        int bpp = 4;
+                        switch (c) {
+                        case ALPHA_8:
+                            bpp = 1;
+                            break;
+                        case RGB_565:
+                        case ARGB_4444:
+                            bpp = 2;
+                            break;
+                        case ARGB_8888:
+                            bpp = 4;
+                            break;
+                        }
+                        counter.bitmapIncrement(b.getWidth() * b.getHeight() * bpp);
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
     }
 
     /**
@@ -509,15 +844,23 @@
         public void apply(View root) {
             final Context context = root.getContext();
             final ViewGroup target = (ViewGroup) root.findViewById(viewId);
+            if (target == null) return;
             if (nestedViews != null) {
                 // Inflate nested views and add as children
                 target.addView(nestedViews.apply(context, target));
-            } else if (target != null) {
+            } else {
                 // Clear all children when nested views omitted
                 target.removeAllViews();
             }
         }
 
+        @Override
+        public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
+            if (nestedViews != null) {
+                counter.bitmapIncrement(nestedViews.estimateBitmapMemoryUsage());
+            }
+        }
+
         int viewId;
         RemoteViews nestedViews;
 
@@ -525,6 +868,26 @@
     }
 
     /**
+     * Simple class used to keep track of memory usage in a RemoteViews.
+     *
+     */
+    private class MemoryUsageCounter {
+        public void clear() {
+            mBitmapHeapMemoryUsage = 0;
+        }
+
+        public void bitmapIncrement(int numBytes) {
+            mBitmapHeapMemoryUsage += numBytes;
+        }
+
+        public int getBitmapHeapMemoryUsage() {
+            return mBitmapHeapMemoryUsage;
+        }
+
+        int mBitmapHeapMemoryUsage;
+    }
+
+    /**
      * Create a new RemoteViews object that will display the views contained
      * in the specified layout file.
      * 
@@ -534,6 +897,10 @@
     public RemoteViews(String packageName, int layoutId) {
         mPackage = packageName;
         mLayoutId = layoutId;
+
+        // setup the memory usage statistics
+        mMemoryUsageCounter = new MemoryUsageCounter();
+        recalculateMemoryUsage();
     }
 
     /**
@@ -544,6 +911,8 @@
     public RemoteViews(Parcel parcel) {
         mPackage = parcel.readString();
         mLayoutId = parcel.readInt();
+        mIsWidgetCollectionChild = parcel.readInt() == 1 ? true : false;
+
         int count = parcel.readInt();
         if (count > 0) {
             mActions = new ArrayList<Action>(count);
@@ -562,18 +931,38 @@
                 case ViewGroupAction.TAG:
                     mActions.add(new ViewGroupAction(parcel));
                     break;
+                case ReflectionActionWithoutParams.TAG:
+                    mActions.add(new ReflectionActionWithoutParams(parcel));
+                    break;
+                case SetEmptyView.TAG:
+                    mActions.add(new SetEmptyView(parcel));
+                    break;
+                case SetPendingIntentTemplate.TAG:
+                    mActions.add(new SetPendingIntentTemplate(parcel));
+                    break;
+                case SetOnClickFillInIntent.TAG:
+                    mActions.add(new SetOnClickFillInIntent(parcel));
+                    break;
                 default:
                     throw new ActionException("Tag " + tag + " not found");
                 }
             }
         }
+
+        // setup the memory usage statistics
+        mMemoryUsageCounter = new MemoryUsageCounter();
+        recalculateMemoryUsage();
     }
 
+    @Override
     public RemoteViews clone() {
         final RemoteViews that = new RemoteViews(mPackage, mLayoutId);
         if (mActions != null) {
             that.mActions = (ArrayList<Action>)mActions.clone();
         }
+
+        // update the memory usage stats of the cloned RemoteViews
+        that.recalculateMemoryUsage();
         return that;
     }
 
@@ -585,6 +974,39 @@
         return mLayoutId;
     }
 
+    /*
+     * This flag indicates whether this RemoteViews object is being created from a
+     * RemoteViewsService for use as a child of a widget collection. This flag is used
+     * to determine whether or not certain features are available, in particular,
+     * setting on click extras and setting on click pending intents. The former is enabled,
+     * and the latter disabled when this flag is true.
+     */
+    void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
+        mIsWidgetCollectionChild = isWidgetCollectionChild;
+    }
+
+    /**
+     * Updates the memory usage statistics.
+     */
+    private void recalculateMemoryUsage() {
+        mMemoryUsageCounter.clear();
+
+        // Accumulate the memory usage for each action
+        if (mActions != null) {
+            final int count = mActions.size();
+            for (int i= 0; i < count; ++i) {
+                mActions.get(i).updateMemoryUsageEstimate(mMemoryUsageCounter);
+            }
+        }
+    }
+
+    /**
+     * Returns an estimate of the bitmap heap memory usage for this RemoteViews.
+     */
+    int estimateBitmapMemoryUsage() {
+        return mMemoryUsageCounter.getBitmapHeapMemoryUsage();
+    }
+
     /**
      * Add an action to be executed on the remote side when apply is called.
      * 
@@ -595,6 +1017,9 @@
             mActions = new ArrayList<Action>();
         }
         mActions.add(a);
+
+        // update the memory usage stats
+        a.updateMemoryUsageEstimate(mMemoryUsageCounter);
     }
 
     /**
@@ -622,6 +1047,24 @@
     }
 
     /**
+     * Equivalent to calling {@link AdapterViewFlipper#showNext()}
+     *
+     * @param viewId The id of the view on which to call {@link AdapterViewFlipper#showNext()}
+     */
+    public void showNext(int viewId) {
+        addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
+    }
+
+    /**
+     * Equivalent to calling {@link AdapterViewFlipper#showPrevious()}
+     *
+     * @param viewId The id of the view on which to call {@link AdapterViewFlipper#showPrevious()}
+     */
+    public void showPrevious(int viewId) {
+        addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
+    }
+
+    /**
      * Equivalent to calling View.setVisibility
      * 
      * @param viewId The id of the view whose visibility should change
@@ -630,7 +1073,7 @@
     public void setViewVisibility(int viewId, int visibility) {
         setInt(viewId, "setVisibility", visibility);
     }
-    
+
     /**
      * Equivalent to calling TextView.setText
      * 
@@ -672,6 +1115,16 @@
     }
 
     /**
+     * Equivalent to calling AdapterView.setEmptyView
+     *
+     * @param viewId The id of the view on which to set the empty view
+     * @param emptyViewId The view id of the empty view
+     */
+    public void setEmptyView(int viewId, int emptyViewId) {
+        addAction(new SetEmptyView(viewId, emptyViewId));
+    }
+
+    /**
      * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase},
      * {@link Chronometer#setFormat Chronometer.setFormat},
      * and {@link Chronometer#start Chronometer.start()} or
@@ -718,6 +1171,11 @@
      * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)}
      * to launch the provided {@link PendingIntent}.
      * 
+     * When setting the on-click action of items within collections (eg. {@link ListView},
+     * {@link StackView} etc.), this method will not work. Instead, use {@link
+     * RemoteViews#setPendingIntentTemplate(int, PendingIntent) in conjunction with
+     * RemoteViews#setOnClickFillInIntent(int, Intent).
+     *
      * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked
      * @param pendingIntent The {@link PendingIntent} to send when user clicks
      */
@@ -726,6 +1184,45 @@
     }
 
     /**
+     * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
+     * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
+     * this method should be used to set a single PendingIntent template on the collection, and
+     * individual items can differentiate their on-click behavior using
+     * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
+     *
+     * @param viewId The id of the collection who's children will use this PendingIntent template
+     *          when clicked
+     * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified
+     *          by a child of viewId and executed when that child is clicked
+     */
+    public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) {
+        addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate));
+    }
+
+    /**
+     * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
+     * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
+     * a single PendingIntent template can be set on the collection, see {@link
+     * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
+     * action of a given item can be distinguished by setting a fillInIntent on that item. The
+     * fillInIntent is then combined with the PendingIntent template in order to determine the final
+     * intent which will be executed when the item is clicked. This works as follows: any fields
+     * which are left blank in the PendingIntent template, but are provided by the fillInIntent
+     * will be overwritten, and the resulting PendingIntent will be used.
+     *
+     *
+     * of the PendingIntent template will then be filled in with the associated fields that are
+     * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details.
+     *
+     * @param viewId The id of the view on which to set the fillInIntent
+     * @param fillInIntent The intent which will be combined with the parent's PendingIntent
+     *        in order to determine the on-click behavior of the view specified by viewId
+     */
+    public void setOnClickFillInIntent(int viewId, Intent fillInIntent) {
+        addAction(new SetOnClickFillInIntent(viewId, fillInIntent));
+    }
+
+    /**
      * @hide
      * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
@@ -769,6 +1266,37 @@
     }
 
     /**
+     * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}.
+     *
+     * @param viewId The id of the view whose text should change
+     * @param intent The intent of the service which will be
+     *            providing data to the RemoteViewsAdapter
+     */
+    public void setRemoteAdapter(int viewId, Intent intent) {
+        setIntent(viewId, "setRemoteViewsAdapter", intent);
+    }
+
+    /**
+     * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
+     *
+     * @param viewId The id of the view whose text should change
+     * @param position Scroll to this adapter position
+     */
+    public void setScrollPosition(int viewId, int position) {
+        setInt(viewId, "smoothScrollToPosition", position);
+    }
+
+    /**
+     * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}.
+     *
+     * @param viewId The id of the view whose text should change
+     * @param offset Scroll by this adapter position offset
+     */
+    public void setRelativeScrollPosition(int viewId, int offset) {
+        setInt(viewId, "smoothScrollByOffset", offset);
+    }
+
+    /**
      * Call a method taking one boolean on a view in the layout for this RemoteViews.
      *
      * @param viewId The id of the view whose text should change
@@ -915,6 +1443,16 @@
     }
 
     /**
+     *
+     * @param viewId
+     * @param methodName
+     * @param value
+     */
+    public void setIntent(int viewId, String methodName, Intent value) {
+        addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value));
+    }
+
+    /**
      * Inflates the view hierarchy represented by this object and applies
      * all of the actions.
      * 
@@ -1000,6 +1538,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mPackage);
         dest.writeInt(mLayoutId);
+        dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
         int count;
         if (mActions != null) {
             count = mActions.size();
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
new file mode 100644
index 0000000..26eb57c
--- /dev/null
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -0,0 +1,887 @@
+/*
+ * 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 java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+
+import com.android.internal.widget.IRemoteViewsFactory;
+
+/**
+ * An adapter to a RemoteViewsService which fetches and caches RemoteViews
+ * to be later inflated as child views.
+ */
+/** @hide */
+public class RemoteViewsAdapter extends BaseAdapter {
+    private static final String TAG = "RemoteViewsAdapter";
+
+    private Context mContext;
+    private Intent mIntent;
+    private LayoutInflater mLayoutInflater;
+    private RemoteViewsAdapterServiceConnection mServiceConnection;
+    private WeakReference<RemoteAdapterConnectionCallback> mCallback;
+    private FixedSizeRemoteViewsCache mCache;
+
+    // The set of requested views that are to be notified when the associated RemoteViews are
+    // loaded.
+    private RemoteViewsFrameLayoutRefSet mRequestedViews;
+
+    private HandlerThread mWorkerThread;
+    // items may be interrupted within the normally processed queues
+    private Handler mWorkerQueue;
+    private Handler mMainQueue;
+
+    /**
+     * An interface for the RemoteAdapter to notify other classes when adapters
+     * are actually connected to/disconnected from their actual services.
+     */
+    public interface RemoteAdapterConnectionCallback {
+        public void onRemoteAdapterConnected();
+
+        public void onRemoteAdapterDisconnected();
+    }
+
+    /**
+     * The service connection that gets populated when the RemoteViewsService is
+     * bound.  This must be a static inner class to ensure that no references to the outer
+     * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being
+     * garbage collected, and would cause us to leak activities due to the caching mechanism for
+     * FrameLayouts in the adapter).
+     */
+    private static class RemoteViewsAdapterServiceConnection implements ServiceConnection {
+        private boolean mConnected;
+        private WeakReference<RemoteViewsAdapter> mAdapter;
+        private IRemoteViewsFactory mRemoteViewsFactory;
+
+        public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) {
+            mAdapter = new WeakReference<RemoteViewsAdapter>(adapter);
+        }
+
+        public void onServiceConnected(ComponentName name,
+                IBinder service) {
+            mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
+            mConnected = true;
+
+            // Queue up work that we need to do for the callback to run
+            final RemoteViewsAdapter adapter = mAdapter.get();
+            if (adapter == null) return;
+            adapter.mWorkerQueue.post(new Runnable() {
+                @Override
+                public void run() {
+                    // Call back to the service to notify that the data set changed
+                    if (adapter.mServiceConnection.isConnected()) {
+                        IRemoteViewsFactory factory =
+                            adapter.mServiceConnection.getRemoteViewsFactory();
+                        try {
+                            // call back to the factory
+                            factory.onDataSetChanged();
+                        } catch (Exception e) {
+                            Log.e(TAG, "Error notifying factory of data set changed in " +
+                                        "onServiceConnected(): " + e.getMessage());
+                            e.printStackTrace();
+
+                            // Return early to prevent anything further from being notified
+                            // (effectively nothing has changed)
+                            return;
+                        }
+
+                        // Request meta data so that we have up to date data when calling back to
+                        // the remote adapter callback
+                        adapter.updateMetaData();
+
+                        // Post a runnable to call back to the view to notify it that we have
+                        // connected
+                        adapter.mMainQueue.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                final RemoteAdapterConnectionCallback callback =
+                                    adapter.mCallback.get();
+                                if (callback != null) {
+                                    callback.onRemoteAdapterConnected();
+                                }
+                            }
+                        });
+                    }
+                }
+            });
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            mConnected = false;
+            mRemoteViewsFactory = null;
+
+            final RemoteViewsAdapter adapter = mAdapter.get();
+            if (adapter == null) return;
+            
+            // Clear the main/worker queues
+            adapter.mMainQueue.removeMessages(0);
+            adapter.mWorkerQueue.removeMessages(0);
+
+            // Clear the cache (the meta data will be re-requested on service re-connection)
+            synchronized (adapter.mCache) {
+                adapter.mCache.reset();
+            }
+
+            final RemoteAdapterConnectionCallback callback = adapter.mCallback.get();
+            if (callback != null) {
+                callback.onRemoteAdapterDisconnected();
+            }
+        }
+
+        public IRemoteViewsFactory getRemoteViewsFactory() {
+            return mRemoteViewsFactory;
+        }
+
+        public boolean isConnected() {
+            return mConnected;
+        }
+    }
+
+    /**
+     * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when
+     * they are loaded.
+     */
+    private class RemoteViewsFrameLayout extends FrameLayout {
+        public RemoteViewsFrameLayout(Context context) {
+            super(context);
+        }
+
+        /**
+         * Updates this RemoteViewsFrameLayout depending on the view that was loaded.
+         * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded
+         *             successfully.
+         */
+        public void onRemoteViewsLoaded(RemoteViews view) {
+            try {
+                // Remove all the children of this layout first
+                removeAllViews();
+                addView(view.apply(getContext(), this));
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to apply RemoteViews.");
+            }
+        }
+    }
+
+    /**
+     * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the
+     * adapter that have not yet had their RemoteViews loaded.
+     */
+    private class RemoteViewsFrameLayoutRefSet {
+        private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences;
+
+        public RemoteViewsFrameLayoutRefSet() {
+            mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>();
+        }
+
+        /**
+         * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter.
+         */
+        public void add(int position, RemoteViewsFrameLayout layout) {
+            final Integer pos = position;
+            LinkedList<RemoteViewsFrameLayout> refs;
+
+            // Create the list if necessary
+            if (mReferences.containsKey(pos)) {
+                refs = mReferences.get(pos);
+            } else {
+                refs = new LinkedList<RemoteViewsFrameLayout>();
+                mReferences.put(pos, refs);
+            }
+
+            // Add the references to the list
+            refs.add(layout);
+        }
+
+        /**
+         * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that
+         * the associated RemoteViews has loaded.
+         */
+        public void notifyOnRemoteViewsLoaded(int position, RemoteViews view, int typeId) {
+            if (view == null) return;
+
+            final Integer pos = position;
+            if (mReferences.containsKey(pos)) {
+                // Notify all the references for that position of the newly loaded RemoteViews
+                final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos);
+                for (final RemoteViewsFrameLayout ref : refs) {
+                    ref.onRemoteViewsLoaded(view);
+                }
+                refs.clear();
+
+                // Remove this set from the original mapping
+                mReferences.remove(pos);
+            }
+        }
+
+        /**
+         * Removes all references to all RemoteViewsFrameLayouts returned by the adapter.
+         */
+        public void clear() {
+            // We currently just clear the references, and leave all the previous layouts returned
+            // in their default state of the loading view.
+            mReferences.clear();
+        }
+    }
+
+    /**
+     * The meta-data associated with the cache in it's current state.
+     */
+    private class RemoteViewsMetaData {
+        int count;
+        int viewTypeCount;
+        boolean hasStableIds;
+        boolean isDataDirty;
+
+        // Used to determine how to construct loading views.  If a loading view is not specified
+        // by the user, then we try and load the first view, and use its height as the height for
+        // the default loading view.
+        RemoteViews mUserLoadingView;
+        RemoteViews mFirstView;
+        int mFirstViewHeight;
+
+        // A mapping from type id to a set of unique type ids
+        private Map<Integer, Integer> mTypeIdIndexMap;
+
+        public RemoteViewsMetaData() {
+            reset();
+        }
+
+        public void reset() {
+            count = 0;
+            // by default there is at least one dummy view type
+            viewTypeCount = 1;
+            hasStableIds = true;
+            isDataDirty = false;
+            mUserLoadingView = null;
+            mFirstView = null;
+            mFirstViewHeight = 0;
+            mTypeIdIndexMap = new HashMap<Integer, Integer>();
+        }
+
+        public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) {
+            mUserLoadingView = loadingView;
+            if (firstView != null) {
+                mFirstView = firstView;
+                mFirstViewHeight = -1;
+            }
+        }
+
+        public int getMappedViewType(int typeId) {
+            if (mTypeIdIndexMap.containsKey(typeId)) {
+                return mTypeIdIndexMap.get(typeId);
+            } else {
+                // We +1 because the loading view always has view type id of 0
+                int incrementalTypeId = mTypeIdIndexMap.size() + 1;
+                mTypeIdIndexMap.put(typeId, incrementalTypeId);
+                return incrementalTypeId;
+            }
+        }
+
+        private RemoteViewsFrameLayout createLoadingView(int position, View convertView,
+                ViewGroup parent) {
+            // Create and return a new FrameLayout, and setup the references for this position
+            final Context context = parent.getContext();
+            RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context);
+
+            // Create a new loading view
+            synchronized (mCache) {
+                if (mUserLoadingView != null) {
+                    // A user-specified loading view
+                    View loadingView = mUserLoadingView.apply(parent.getContext(), parent);
+                    loadingView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(0));
+                    layout.addView(loadingView);
+                } else {
+                    // A default loading view
+                    // Use the size of the first row as a guide for the size of the loading view
+                    if (mFirstViewHeight < 0) {
+                        View firstView = mFirstView.apply(parent.getContext(), parent);
+                        firstView.measure(
+                                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+                        mFirstViewHeight = firstView.getMeasuredHeight();
+                        mFirstView = null;
+                    }
+
+                    // Compose the loading view text
+                    TextView loadingTextView = (TextView) mLayoutInflater.inflate(
+				com.android.internal.R.layout.remote_views_adapter_default_loading_view,
+				layout, false);
+                    loadingTextView.setHeight(mFirstViewHeight);
+                    loadingTextView.setTag(new Integer(0));
+
+                    layout.addView(loadingTextView);
+                }
+            }
+
+            return layout;
+        }
+    }
+
+    /**
+     * The meta-data associated with a single item in the cache.
+     */
+    private class RemoteViewsIndexMetaData {
+        int typeId;
+        long itemId;
+
+        public RemoteViewsIndexMetaData(RemoteViews v, long itemId) {
+            set(v, itemId);
+        }
+
+        public void set(RemoteViews v, long id) {
+            itemId = id;
+            if (v != null)
+                typeId = v.getLayoutId();
+            else
+                typeId = 0;
+        }
+    }
+
+    /**
+     *
+     */
+    private class FixedSizeRemoteViewsCache {
+        private static final String TAG = "FixedSizeRemoteViewsCache";
+
+        // The meta data related to all the RemoteViews, ie. count, is stable, etc.
+        private RemoteViewsMetaData mMetaData;
+
+        // The cache/mapping of position to RemoteViewsMetaData.  This set is guaranteed to be
+        // greater than or equal to the set of RemoteViews.
+        // Note: The reason that we keep this separate from the RemoteViews cache below is that this
+        // we still need to be able to access the mapping of position to meta data, without keeping
+        // the heavy RemoteViews around.  The RemoteViews cache is trimmed to fixed constraints wrt.
+        // memory and size, but this metadata cache will retain information until the data at the
+        // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged).
+        private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData;
+
+        // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses
+        // too much memory.
+        private HashMap<Integer, RemoteViews> mIndexRemoteViews;
+
+        // The set of indices that have been explicitly requested by the collection view
+        private HashSet<Integer> mRequestedIndices;
+
+        // The set of indices to load, including those explicitly requested, as well as those
+        // determined by the preloading algorithm to be prefetched
+        private HashSet<Integer> mLoadIndices;
+
+        // The lower and upper bounds of the preloaded range
+        private int mPreloadLowerBound;
+        private int mPreloadUpperBound;
+
+        // The bounds of this fixed cache, we will try and fill as many items into the cache up to
+        // the maxCount number of items, or the maxSize memory usage.
+        // The maxCountSlack is used to determine if a new position in the cache to be loaded is
+        // sufficiently ouside the old set, prompting a shifting of the "window" of items to be
+        // preloaded.
+        private int mMaxCount;
+        private int mMaxCountSlack;
+        private static final float sMaxCountSlackPercent = 0.75f;
+        private static final int sMaxMemoryUsage = 1024 * 1024;
+
+        public FixedSizeRemoteViewsCache(int maxCacheSize) {
+            mMaxCount = maxCacheSize;
+            mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2));
+            mPreloadLowerBound = 0;
+            mPreloadUpperBound = -1;
+            mMetaData = new RemoteViewsMetaData();
+            mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>();
+            mIndexRemoteViews = new HashMap<Integer, RemoteViews>();
+            mRequestedIndices = new HashSet<Integer>();
+            mLoadIndices = new HashSet<Integer>();
+        }
+
+        public void insert(int position, RemoteViews v, long itemId) {
+            // Trim the cache if we go beyond the count
+            if (mIndexRemoteViews.size() >= mMaxCount) {
+                mIndexRemoteViews.remove(getFarthestPositionFrom(position));
+            }
+
+            // Trim the cache if we go beyond the available memory size constraints
+            while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryUsage) {
+                // Note: This is currently the most naive mechanism for deciding what to prune when
+                // we hit the memory limit.  In the future, we may want to calculate which index to
+                // remove based on both its position as well as it's current memory usage, as well
+                // as whether it was directly requested vs. whether it was preloaded by our caching
+                // mechanism.
+                mIndexRemoteViews.remove(getFarthestPositionFrom(position));
+            }
+
+            // Update the metadata cache
+            if (mIndexMetaData.containsKey(position)) {
+                final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position);
+                metaData.set(v, itemId);
+            } else {
+                mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId));
+            }
+            mIndexRemoteViews.put(position, v);
+        }
+
+        public RemoteViewsMetaData getMetaData() {
+            return mMetaData;
+        }
+        public RemoteViews getRemoteViewsAt(int position) {
+            if (mIndexRemoteViews.containsKey(position)) {
+                return mIndexRemoteViews.get(position);
+            }
+            return null;
+        }
+        public RemoteViewsIndexMetaData getMetaDataAt(int position) {
+            if (mIndexMetaData.containsKey(position)) {
+                return mIndexMetaData.get(position);
+            }
+            return null;
+        }
+
+        private int getRemoteViewsBitmapMemoryUsage() {
+            // Calculate the memory usage of all the RemoteViews bitmaps being cached
+            int mem = 0;
+            for (Integer i : mIndexRemoteViews.keySet()) {
+                final RemoteViews v = mIndexRemoteViews.get(i);
+                if (v != null) {
+                    mem += v.estimateBitmapMemoryUsage();
+                }
+            }
+            return mem;
+        }
+        private int getFarthestPositionFrom(int pos) {
+            // Find the index farthest away and remove that
+            int maxDist = 0;
+            int maxDistIndex = -1;
+            for (int i : mIndexRemoteViews.keySet()) {
+                int dist = Math.abs(i-pos);
+                if (dist > maxDist) {
+                    maxDistIndex = i;
+                    maxDist = dist;
+                }
+            }
+            return maxDistIndex;
+        }
+
+        public void queueRequestedPositionToLoad(int position) {
+            synchronized (mLoadIndices) {
+                mRequestedIndices.add(position);
+                mLoadIndices.add(position);
+            }
+        }
+        public void queuePositionsToBePreloadedFromRequestedPosition(int position) {
+            // Check if we need to preload any items
+            if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) {
+                int center = (mPreloadUpperBound + mPreloadLowerBound) / 2;
+                if (Math.abs(position - center) < mMaxCountSlack) {
+                    return;
+                }
+            }
+
+            int count = 0;
+            synchronized (mMetaData) {
+                count = mMetaData.count;
+            }
+            synchronized (mLoadIndices) {
+                mLoadIndices.clear();
+
+                // Add all the requested indices
+                mLoadIndices.addAll(mRequestedIndices);
+
+                // Add all the preload indices
+                int halfMaxCount = mMaxCount / 2;
+                mPreloadLowerBound = position - halfMaxCount;
+                mPreloadUpperBound = position + halfMaxCount;
+                int effectiveLowerBound = Math.max(0, mPreloadLowerBound);
+                int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1);
+                for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) {
+                    mLoadIndices.add(i);
+                }
+
+                // But remove all the indices that have already been loaded and are cached
+                mLoadIndices.removeAll(mIndexRemoteViews.keySet());
+            }
+        }
+        public int getNextIndexToLoad() {
+            // We try and prioritize items that have been requested directly, instead
+            // of items that are loaded as a result of the caching mechanism
+            synchronized (mLoadIndices) {
+                // Prioritize requested indices to be loaded first
+                if (!mRequestedIndices.isEmpty()) {
+                    Integer i = mRequestedIndices.iterator().next();
+                    mRequestedIndices.remove(i);
+                    mLoadIndices.remove(i);
+                    return i.intValue();
+                }
+
+                // Otherwise, preload other indices as necessary
+                if (!mLoadIndices.isEmpty()) {
+                    Integer i = mLoadIndices.iterator().next();
+                    mLoadIndices.remove(i);
+                    return i.intValue();
+                }
+
+                return -1;
+            }
+        }
+
+        public boolean containsRemoteViewAt(int position) {
+            return mIndexRemoteViews.containsKey(position);
+        }
+        public boolean containsMetaDataAt(int position) {
+            return mIndexMetaData.containsKey(position);
+        }
+
+        public void reset() {
+            // Note: We do not try and reset the meta data, since that information is still used by
+            // collection views to validate it's own contents (and will be re-requested if the data
+            // is invalidated through the notifyDataSetChanged() flow).
+
+            mPreloadLowerBound = 0;
+            mPreloadUpperBound = -1;
+            mIndexRemoteViews.clear();
+            mIndexMetaData.clear();
+            synchronized (mLoadIndices) {
+                mRequestedIndices.clear();
+                mLoadIndices.clear();
+            }
+        }
+    }
+
+    public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
+        mContext = context;
+        mIntent = intent;
+        mLayoutInflater = LayoutInflater.from(context);
+        if (mIntent == null) {
+            throw new IllegalArgumentException("Non-null Intent must be specified.");
+        }
+        mRequestedViews = new RemoteViewsFrameLayoutRefSet();
+
+        // initialize the worker thread
+        mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
+        mWorkerThread.start();
+        mWorkerQueue = new Handler(mWorkerThread.getLooper());
+        mMainQueue = new Handler(Looper.myLooper());
+
+        // initialize the cache and the service connection on startup
+        mCache = new FixedSizeRemoteViewsCache(50);
+        mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback);
+        mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
+        requestBindService();
+    }
+
+    private void loadNextIndexInBackground() {
+        mWorkerQueue.post(new Runnable() {
+            @Override
+            public void run() {
+                // Get the next index to load
+                int position = -1;
+                synchronized (mCache) {
+                    position = mCache.getNextIndexToLoad();
+                }
+                if (position > -1) {
+                    // Load the item, and notify any existing RemoteViewsFrameLayouts
+                    updateRemoteViews(position);
+
+                    // Queue up for the next one to load
+                    loadNextIndexInBackground();
+                }
+            }
+        });
+    }
+
+    private void updateMetaData() {
+        if (mServiceConnection.isConnected()) {
+            try {
+                IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
+
+                // get the properties/first view (so that we can use it to
+                // measure our dummy views)
+                boolean hasStableIds = factory.hasStableIds();
+                int viewTypeCount = factory.getViewTypeCount();
+                int count = factory.getCount();
+                RemoteViews loadingView = factory.getLoadingView();
+                RemoteViews firstView = null;
+                if ((count > 0) && (loadingView == null)) {
+                    firstView = factory.getViewAt(0);
+                }
+                final RemoteViewsMetaData metaData = mCache.getMetaData();
+                synchronized (metaData) {
+                    metaData.hasStableIds = hasStableIds;
+                    metaData.viewTypeCount = viewTypeCount + 1;
+                    metaData.count = count;
+                    metaData.setLoadingViewTemplates(loadingView, firstView);
+                }
+            } catch (Exception e) {
+                // print the error
+                Log.e(TAG, "Error in requestMetaData(): " + e.getMessage());
+
+                // reset any members after the failed call
+                final RemoteViewsMetaData metaData = mCache.getMetaData();
+                synchronized (metaData) {
+                    metaData.reset();
+                }
+            }
+        }
+    }
+
+    private void updateRemoteViews(final int position) {
+        if (mServiceConnection.isConnected()) {
+            IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
+
+            // Load the item information from the remote service
+            RemoteViews remoteViews = null;
+            long itemId = 0;
+            try {
+                remoteViews = factory.getViewAt(position);
+                itemId = factory.getItemId(position);
+            } catch (Exception e) {
+                // Print the error
+                Log.e(TAG, "Error in updateRemoteViewsInfo(" + position + "): " +
+                        e.getMessage());
+                e.printStackTrace();
+
+                // Return early to prevent additional work in re-centering the view cache, and
+                // swapping from the loading view
+                return;
+            }
+
+            synchronized (mCache) {
+                // Cache the RemoteViews we loaded
+                mCache.insert(position, remoteViews, itemId);
+
+                // Notify all the views that we have previously returned for this index that
+                // there is new data for it.
+                final RemoteViews rv = remoteViews;
+                final int typeId = mCache.getMetaDataAt(position).typeId;
+                mMainQueue.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId);
+                    }
+                });
+            }
+        }
+    }
+
+    public Intent getRemoteViewsServiceIntent() {
+        return mIntent;
+    }
+
+    public int getCount() {
+        requestBindService();
+        final RemoteViewsMetaData metaData = mCache.getMetaData();
+        synchronized (metaData) {
+            return metaData.count;
+        }
+    }
+
+    public Object getItem(int position) {
+        // Disallow arbitrary object to be associated with an item for the time being
+        return null;
+    }
+
+    public long getItemId(int position) {
+        requestBindService();
+        synchronized (mCache) {
+            if (mCache.containsMetaDataAt(position)) {
+                return mCache.getMetaDataAt(position).itemId;
+            }
+            return 0;
+        }
+    }
+
+    public int getItemViewType(int position) {
+        requestBindService();
+        int typeId = 0;
+        synchronized (mCache) {
+            if (mCache.containsMetaDataAt(position)) {
+                typeId = mCache.getMetaDataAt(position).typeId;
+            } else {
+                return 0;
+            }
+        }
+
+        final RemoteViewsMetaData metaData = mCache.getMetaData();
+        synchronized (metaData) {
+            return metaData.getMappedViewType(typeId);
+        }
+    }
+
+    /**
+     * Returns the item type id for the specified convert view.  Returns -1 if the convert view
+     * is invalid.
+     */
+    private int getConvertViewTypeId(View convertView) {
+        int typeId = -1;
+        if (convertView != null) {
+            Object tag = convertView.getTag(com.android.internal.R.id.rowTypeId);
+            if (tag != null) {
+                typeId = (Integer) tag;
+            }
+        }
+        return typeId;
+    }
+
+    public View getView(int position, View convertView, ViewGroup parent) {
+        requestBindService();
+        if (mServiceConnection.isConnected()) {
+            // "Request" an index so that we can queue it for loading, initiate subsequent
+            // preloading, etc.
+            synchronized (mCache) {
+                // Queue up other indices to be preloaded based on this position
+                mCache.queuePositionsToBePreloadedFromRequestedPosition(position);
+
+                RemoteViewsFrameLayout layout = (RemoteViewsFrameLayout) convertView;
+                View convertViewChild = null;
+                int convertViewTypeId = 0;
+                if (convertView != null) {
+                    convertViewChild = layout.getChildAt(0);
+                    convertViewTypeId = getConvertViewTypeId(convertViewChild);
+                }
+
+                // Second, we try and retrieve the RemoteViews from the cache, returning a loading
+                // view and queueing it to be loaded if it has not already been loaded.
+                if (mCache.containsRemoteViewAt(position)) {
+                    Context context = parent.getContext();
+                    RemoteViews rv = mCache.getRemoteViewsAt(position);
+                    int typeId = mCache.getMetaDataAt(position).typeId;
+
+                    // Reuse the convert view where possible
+                    if (convertView != null) {
+                        if (convertViewTypeId == typeId) {
+                            rv.reapply(context, convertViewChild);
+                            return convertView;
+                        }
+                    }
+
+                    // Otherwise, create a new view to be returned
+                    View newView = rv.apply(context, parent);
+                    newView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(typeId));
+                    if (convertView != null) {
+                        layout.removeAllViews();
+                    } else {
+                        layout = new RemoteViewsFrameLayout(context);
+                    }
+                    layout.addView(newView);
+                    return layout;
+                } else {
+                    // If the cache does not have the RemoteViews at this position, then create a
+                    // loading view and queue the actual position to be loaded in the background
+                    RemoteViewsFrameLayout loadingView = null;
+                    final RemoteViewsMetaData metaData = mCache.getMetaData();
+                    synchronized (metaData) {
+                        loadingView = metaData.createLoadingView(position, convertView, parent);
+                    }
+
+                    mRequestedViews.add(position, loadingView);
+                    mCache.queueRequestedPositionToLoad(position);
+                    loadNextIndexInBackground();
+
+                    return loadingView;
+                }
+            }
+        }
+        return new View(parent.getContext());
+    }
+
+    public int getViewTypeCount() {
+        requestBindService();
+        final RemoteViewsMetaData metaData = mCache.getMetaData();
+        synchronized (metaData) {
+            return metaData.viewTypeCount;
+        }
+    }
+
+    public boolean hasStableIds() {
+        requestBindService();
+        final RemoteViewsMetaData metaData = mCache.getMetaData();
+        synchronized (metaData) {
+            return metaData.hasStableIds;
+        }
+    }
+
+    public boolean isEmpty() {
+        return getCount() <= 0;
+    }
+
+    public void notifyDataSetChanged() {
+        mWorkerQueue.post(new Runnable() {
+            @Override
+            public void run() {
+                // Complete the actual notifyDataSetChanged() call initiated earlier
+                if (mServiceConnection.isConnected()) {
+                    IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
+                    try {
+                        factory.onDataSetChanged();
+                    } catch (Exception e) {
+                        Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
+
+                        // Return early to prevent from further being notified (since nothing has
+                        // changed)
+                        return;
+                    }
+                }
+
+                // Flush the cache so that we can reload new items from the service
+                synchronized (mCache) {
+                    mCache.reset();
+                }
+
+                // Re-request the new metadata (only after the notification to the factory)
+                updateMetaData();
+
+                // Propagate the notification back to the base adapter
+                mMainQueue.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        superNotifyDataSetChanged();
+                    }
+                });
+            }
+        });
+
+        // Note: we do not call super.notifyDataSetChanged() until the RemoteViewsFactory has had
+        // a chance to update itself and return new meta data associated with the new data.
+    }
+
+    private void superNotifyDataSetChanged() {
+        super.notifyDataSetChanged();
+    }
+
+    private boolean requestBindService() {
+        // try binding the service (which will start it if it's not already running)
+        if (!mServiceConnection.isConnected()) {
+            mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+        }
+
+        return mServiceConnection.isConnected();
+    }
+}
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
new file mode 100644
index 0000000..16126aa
--- /dev/null
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -0,0 +1,167 @@
+/*
+ * 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 java.util.HashMap;
+import java.util.Map;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Pair;
+
+import com.android.internal.widget.IRemoteViewsFactory;
+
+/**
+ * The service to be connected to for a remote adapter to request RemoteViews.  Users should
+ * extend the RemoteViewsService to provide the appropriate RemoteViewsFactory's used to
+ * populate the remote collection view (ListView, GridView, etc).
+ */
+public abstract class RemoteViewsService extends Service {
+
+    private static final String LOG_TAG = "RemoteViewsService";
+
+    // multimap implementation for reference counting
+    private HashMap<Intent.FilterComparison, Pair<RemoteViewsFactory, Integer>> mRemoteViewFactories;
+    private final Object mLock = new Object();
+
+    /**
+     * An interface for an adapter between a remote collection view (ListView, GridView, etc) and
+     * the underlying data for that view.  The implementor is responsible for making a RemoteView
+     * for each item in the data set.
+     * 
+     * @see android.widget.Adapter
+     * @see android.appwidget.AppWidgetManager
+     */
+    public interface RemoteViewsFactory {
+        /**
+         * Called when your factory is first constructed. The same factory may be shared across
+         * multiple RemoteViewAdapters depending on the intent passed.
+         */
+        public void onCreate();
+        /**
+         * Called when notifyDataSetChanged() is triggered on the remote adapter. This allows a
+         * RemoteViewsFactory to respond to data changes by updating any internal references.
+         *
+         * @see android.appwidget.AppWidgetManager#notifyAppWidgetViewDataChanged(int[], int)
+         */
+        public void onDataSetChanged();
+        /**
+         * Called when the last RemoteViewsAdapter that is associated with this factory is
+         * unbound.
+         */
+        public void onDestroy();
+
+        public int getCount();
+        public RemoteViews getViewAt(int position);
+        public RemoteViews getLoadingView();
+        public int getViewTypeCount();
+        public long getItemId(int position);
+        public boolean hasStableIds();
+    }
+
+    /**
+     * A private proxy class for the private IRemoteViewsFactory interface through the
+     * public RemoteViewsFactory interface.
+     */
+    private class RemoteViewsFactoryAdapter extends IRemoteViewsFactory.Stub {
+        public RemoteViewsFactoryAdapter(RemoteViewsFactory factory) {
+            mFactory = factory;
+        }
+        public synchronized void onDataSetChanged() {
+            mFactory.onDataSetChanged();
+        }
+        public synchronized int getCount() {
+            return mFactory.getCount();
+        }
+        public synchronized RemoteViews getViewAt(int position) {
+            RemoteViews rv = mFactory.getViewAt(position);
+            rv.setIsWidgetCollectionChild(true);
+            return rv;
+        }
+        public synchronized RemoteViews getLoadingView() {
+            return mFactory.getLoadingView();
+        }
+        public synchronized int getViewTypeCount() {
+            return mFactory.getViewTypeCount();
+        }
+        public synchronized long getItemId(int position) {
+            return mFactory.getItemId(position);
+        }
+        public synchronized boolean hasStableIds() {
+            return mFactory.hasStableIds();
+        }
+
+        private RemoteViewsFactory mFactory;
+    }
+
+    public RemoteViewsService() {
+        mRemoteViewFactories =
+                new HashMap<Intent.FilterComparison, Pair<RemoteViewsFactory, Integer>>();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        synchronized (mLock) {
+            // increment the reference count to the particular factory associated with this intent
+            Intent.FilterComparison fc = new Intent.FilterComparison(intent);
+            Pair<RemoteViewsFactory, Integer> factoryRef = null;
+            RemoteViewsFactory factory = null;
+            if (!mRemoteViewFactories.containsKey(fc)) {
+                factory = onGetViewFactory(intent);
+                factoryRef = new Pair<RemoteViewsFactory, Integer>(factory, 1);
+                mRemoteViewFactories.put(fc, factoryRef);
+                factory.onCreate();
+            } else {
+                Pair<RemoteViewsFactory, Integer> oldFactoryRef = mRemoteViewFactories.get(fc);
+                factory = oldFactoryRef.first;
+                int newRefCount = oldFactoryRef.second.intValue() + 1;
+                factoryRef = new Pair<RemoteViewsFactory, Integer>(oldFactoryRef.first, newRefCount);
+                mRemoteViewFactories.put(fc, factoryRef);
+            }
+            return new RemoteViewsFactoryAdapter(factory);
+        }
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        synchronized (mLock) {
+            Intent.FilterComparison fc = new Intent.FilterComparison(intent);
+            if (mRemoteViewFactories.containsKey(fc)) {
+                // this alleviates the user's responsibility of having to clear all factories
+                Pair<RemoteViewsFactory, Integer> oldFactoryRef =
+                        mRemoteViewFactories.get(fc);
+                int newRefCount = oldFactoryRef.second.intValue() - 1;
+                if (newRefCount <= 0) {
+                    oldFactoryRef.first.onDestroy();
+                    mRemoteViewFactories.remove(fc);
+                } else {
+                    Pair<RemoteViewsFactory, Integer> factoryRef =
+                            new Pair<RemoteViewsFactory, Integer>(oldFactoryRef.first, newRefCount);
+                    mRemoteViewFactories.put(fc, factoryRef);
+                }
+            }
+        }
+        return super.onUnbind(intent);
+    }
+
+    /**
+     * To be implemented by the derived service to generate appropriate factories for
+     * the data.
+     */
+    public abstract RemoteViewsFactory onGetViewFactory(Intent intent);
+}
diff --git a/core/java/android/widget/ResourceCursorAdapter.java b/core/java/android/widget/ResourceCursorAdapter.java
index c9c217a..aee411e 100644
--- a/core/java/android/widget/ResourceCursorAdapter.java
+++ b/core/java/android/widget/ResourceCursorAdapter.java
@@ -36,9 +36,8 @@
     
     /**
      * Constructor.
-     * 
-     * @param context The context where the ListView associated with this
-     *            SimpleListItemFactory is running
+     *
+     * @param context The context where the ListView associated with this adapter is running
      * @param layout resource identifier of a layout file that defines the views
      *            for this list item.  Unless you override them later, this will
      *            define both the item views and the drop down views.
@@ -51,9 +50,8 @@
     
     /**
      * Constructor.
-     * 
-     * @param context The context where the ListView associated with this
-     *            SimpleListItemFactory is running
+     *
+     * @param context The context where the ListView associated with this adapter is running
      * @param layout resource identifier of a layout file that defines the views
      *            for this list item.  Unless you override them later, this will
      *            define both the item views and the drop down views.
@@ -69,6 +67,22 @@
     }
 
     /**
+     * Constructor.
+     *
+     * @param context The context where the ListView associated with this adapter is running
+     * @param layout resource identifier of a layout file that defines the views
+     *            for this list item.  Unless you override them later, this will
+     *            define both the item views and the drop down views.
+     * @param c The cursor from which to get the data.
+     * @param flags flags used to determine the behavior of the adapter
+     */
+    public ResourceCursorAdapter(Context context, int layout, Cursor c, int flags) {
+        super(context, c, flags);
+        mLayout = mDropDownLayout = layout;
+        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    }
+
+    /**
      * Inflates view(s) from the specified XML file.
      * 
      * @see android.widget.CursorAdapter#newView(android.content.Context,
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index 3b113ae..93a1179 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -111,8 +111,7 @@
         }
 
         Rect r = getBounds();
-        if (canvas.quickReject(r.left, r.top, r.right, r.bottom, 
-                Canvas.EdgeType.AA)) {
+        if (canvas.quickReject(r.left, r.top, r.right, r.bottom, Canvas.EdgeType.AA)) {
             return;
         }
         if (drawTrack) {
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 2ad67ba..eb527eb 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -19,11 +19,8 @@
 import com.android.internal.R;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.FocusFinder;
 import android.view.KeyEvent;
@@ -52,6 +49,8 @@
  * within a larger container.
  * 
  * <p>ScrollView only supports vertical scrolling.
+ *
+ * @attr ref android.R.styleable#ScrollView_fillViewport
  */
 public class ScrollView extends FrameLayout {
     static final int ANIMATED_SCROLL_GAP = 250;
@@ -62,9 +61,7 @@
     private long mLastScroll;
 
     private final Rect mTempRect = new Rect();
-    private OverScroller mScroller;
-    private EdgeGlow mEdgeGlowTop;
-    private EdgeGlow mEdgeGlowBottom;
+    private Scroller mScroller;
 
     /**
      * Flag to indicate that we are moving focus ourselves. This is so the
@@ -118,9 +115,6 @@
     private int mMinimumVelocity;
     private int mMaximumVelocity;
     
-    private int mOverscrollDistance;
-    private int mOverflingDistance;
-
     /**
      * ID of the active pointer. This is used to retain consistency during
      * drags/flings if multiple pointers are used.
@@ -193,7 +187,7 @@
 
 
     private void initScrollView() {
-        mScroller = new OverScroller(getContext());
+        mScroller = new Scroller(getContext());
         setFocusable(true);
         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
         setWillNotDraw(false);
@@ -201,8 +195,6 @@
         mTouchSlop = configuration.getScaledTouchSlop();
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-        mOverscrollDistance = configuration.getScaledOverscrollDistance();
-        mOverflingDistance = configuration.getScaledOverflingDistance();
     }
 
     @Override
@@ -257,6 +249,8 @@
      * Indicates whether this ScrollView's content is stretched to fill the viewport.
      *
      * @return True if the content fills the viewport, false otherwise.
+     * 
+     * @attr ref android.R.styleable#ScrollView_fillViewport
      */
     public boolean isFillViewport() {
         return mFillViewport;
@@ -268,6 +262,8 @@
      *
      * @param fillViewport True to stretch the content's height to the viewport's
      *        boundaries, false otherwise.
+     * 
+     * @attr ref android.R.styleable#ScrollView_fillViewport
      */
     public void setFillViewport(boolean fillViewport) {
         if (fillViewport != mFillViewport) {
@@ -463,9 +459,6 @@
                 /* Release the drag */
                 mIsBeingDragged = false;
                 mActivePointerId = INVALID_POINTER;
-                if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
-                    invalidate();
-                }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
                 onSecondaryPointerUp(ev);
@@ -498,7 +491,9 @@
         switch (action & MotionEvent.ACTION_MASK) {
             case MotionEvent.ACTION_DOWN: {
                 final float y = ev.getY();
-                mIsBeingDragged = true;
+                if (!(mIsBeingDragged = inChild((int) ev.getX(), (int) y))) {
+                    return false;
+                }
                 
                 /*
                  * If being flinged and user touches, stop the fling. isFinished
@@ -521,36 +516,7 @@
                     final int deltaY = (int) (mLastMotionY - y);
                     mLastMotionY = y;
 
-                    final int oldX = mScrollX;
-                    final int oldY = mScrollY;
-                    final int range = getScrollRange();
-                    if (overScrollBy(0, deltaY, 0, mScrollY, 0, range,
-                            0, mOverscrollDistance, true)) {
-                        // Break our velocity if we hit a scroll barrier.
-                        mVelocityTracker.clear();
-                    }
-                    onScrollChanged(mScrollX, mScrollY, oldX, oldY);
-
-                    final int overscrollMode = getOverScrollMode();
-                    if (overscrollMode == OVER_SCROLL_ALWAYS ||
-                            (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
-                        final int pulledToY = oldY + deltaY;
-                        if (pulledToY < 0) {
-                            mEdgeGlowTop.onPull((float) deltaY / getHeight());
-                            if (!mEdgeGlowBottom.isFinished()) {
-                                mEdgeGlowBottom.onRelease();
-                            }
-                        } else if (pulledToY > range) {
-                            mEdgeGlowBottom.onPull((float) deltaY / getHeight());
-                            if (!mEdgeGlowTop.isFinished()) {
-                                mEdgeGlowTop.onRelease();
-                            }
-                        }
-                        if (mEdgeGlowTop != null
-                                && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
-                            invalidate();
-                        }
-                    }
+                    scrollBy(0, deltaY);
                 }
                 break;
             case MotionEvent.ACTION_UP: 
@@ -559,15 +525,8 @@
                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                     int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
 
-                    if (getChildCount() > 0) {
-                        if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
-                            fling(-initialVelocity);
-                        } else {
-                            final int bottom = getScrollRange();
-                            if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, bottom)) {
-                                invalidate();
-                            }
-                        }
+                    if (getChildCount() > 0 && Math.abs(initialVelocity) > mMinimumVelocity) {
+                        fling(-initialVelocity);
                     }
 
                     mActivePointerId = INVALID_POINTER;
@@ -577,27 +536,16 @@
                         mVelocityTracker.recycle();
                         mVelocityTracker = null;
                     }
-                    if (mEdgeGlowTop != null) {
-                        mEdgeGlowTop.onRelease();
-                        mEdgeGlowBottom.onRelease();
-                    }
                 }
                 break;
             case MotionEvent.ACTION_CANCEL:
                 if (mIsBeingDragged && getChildCount() > 0) {
-                    if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
-                        invalidate();
-                    }
                     mActivePointerId = INVALID_POINTER;
                     mIsBeingDragged = false;
                     if (mVelocityTracker != null) {
                         mVelocityTracker.recycle();
                         mVelocityTracker = null;
                     }
-                    if (mEdgeGlowTop != null) {
-                        mEdgeGlowTop.onRelease();
-                        mEdgeGlowBottom.onRelease();
-                    }
                 }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
@@ -624,32 +572,6 @@
         }
     }
     
-    @Override
-    protected void onOverScrolled(int scrollX, int scrollY,
-            boolean clampedX, boolean clampedY) {
-        // Treat animating scrolls differently; see #computeScroll() for why.
-        if (!mScroller.isFinished()) {
-            mScrollX = scrollX;
-            mScrollY = scrollY;
-            if (clampedY) {
-                mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange());
-            }
-        } else {
-            super.scrollTo(scrollX, scrollY);
-        }
-        awakenScrollBars();
-    }
-
-    private int getScrollRange() {
-        int scrollRange = 0;
-        if (getChildCount() > 0) {
-            View child = getChildAt(0);
-            scrollRange = Math.max(0,
-                    child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop));
-        }
-        return scrollRange;
-    }
-
     /**
      * <p>
      * Finds the next focusable component that fits in this View's bounds
@@ -1026,16 +948,7 @@
             return contentHeight;
         }
         
-        int scrollRange = getChildAt(0).getBottom();
-        final int scrollY = mScrollY;
-        final int overscrollBottom = Math.max(0, scrollRange - contentHeight);
-        if (scrollY < 0) {
-            scrollRange -= scrollY;
-        } else if (scrollY > overscrollBottom) {
-            scrollRange += scrollY - overscrollBottom;
-        }
-
-        return scrollRange;
+        return getChildAt(0).getBottom();
     }
 
     @Override
@@ -1096,20 +1009,14 @@
             int x = mScroller.getCurrX();
             int y = mScroller.getCurrY();
 
-            if (oldX != x || oldY != y) {
-                overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, getScrollRange(),
-                        0, mOverflingDistance, false);
-                onScrollChanged(mScrollX, mScrollY, oldX, oldY);
-
-                final int range = getScrollRange();
-                final int overscrollMode = getOverScrollMode();
-                if (overscrollMode == OVER_SCROLL_ALWAYS ||
-                        (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0)) {
-                    if (y < 0 && oldY >= 0) {
-                        mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
-                    } else if (y > range && oldY <= range) {
-                        mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
-                    }
+            if (getChildCount() > 0) {
+                View child = getChildAt(0);
+                x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth());
+                y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight());
+                if (x != oldX || y != oldY) {
+                    mScrollX = x;
+                    mScrollY = y;
+                    onScrollChanged(x, y, oldX, oldY);
                 }
             }
             awakenScrollBars();
@@ -1347,7 +1254,7 @@
             int bottom = getChildAt(0).getHeight();
     
             mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, 
-                    Math.max(0, bottom - height), 0, height/2);
+                    Math.max(0, bottom - height));
     
             final boolean movingDown = velocityY > 0;
     
@@ -1385,55 +1292,6 @@
         }
     }
 
-    @Override
-    public void setOverScrollMode(int mode) {
-        if (mode != OVER_SCROLL_NEVER) {
-            if (mEdgeGlowTop == null) {
-                final Resources res = getContext().getResources();
-                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
-                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
-                mEdgeGlowTop = new EdgeGlow(edge, glow);
-                mEdgeGlowBottom = new EdgeGlow(edge, glow);
-            }
-        } else {
-            mEdgeGlowTop = null;
-            mEdgeGlowBottom = null;
-        }
-        super.setOverScrollMode(mode);
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        if (mEdgeGlowTop != null) {
-            final int scrollY = mScrollY;
-            if (!mEdgeGlowTop.isFinished()) {
-                final int restoreCount = canvas.save();
-                final int width = getWidth();
-
-                canvas.translate(-width / 2, Math.min(0, scrollY));
-                mEdgeGlowTop.setSize(width * 2, getHeight());
-                if (mEdgeGlowTop.draw(canvas)) {
-                    invalidate();
-                }
-                canvas.restoreToCount(restoreCount);
-            }
-            if (!mEdgeGlowBottom.isFinished()) {
-                final int restoreCount = canvas.save();
-                final int width = getWidth();
-                final int height = getHeight();
-
-                canvas.translate(-width / 2, Math.max(getScrollRange(), scrollY) + height);
-                canvas.rotate(180, width, 0);
-                mEdgeGlowBottom.setSize(width * 2, height);
-                if (mEdgeGlowBottom.draw(canvas)) {
-                    invalidate();
-                }
-                canvas.restoreToCount(restoreCount);
-            }
-        }
-    }
-
     private int clamp(int n, int my, int child) {
         if (my >= child || n < 0) {
             /* my >= child is this case:
diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java
index 23f72b6..79ab448 100644
--- a/core/java/android/widget/Scroller.java
+++ b/core/java/android/widget/Scroller.java
@@ -50,6 +50,8 @@
     private float mDurationReciprocal;
     private float mDeltaX;
     private float mDeltaY;
+    private float mViscousFluidScale;
+    private float mViscousFluidNormalize;
     private boolean mFinished;
     private Interpolator mInterpolator;
 
@@ -61,18 +63,8 @@
     private static final int SCROLL_MODE = 0;
     private static final int FLING_MODE = 1;
 
-    private final float mDeceleration;
-
-    private static float sViscousFluidScale;
-    private static float sViscousFluidNormalize;
-
-    static {
-        // This controls the viscous fluid effect (how much of it)
-        sViscousFluidScale = 8.0f;
-        // must be set to 1.0 (used in viscousFluid())
-        sViscousFluidNormalize = 1.0f;
-        sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
-    }
+    private float mDeceleration;
+    private final float mPpi;
 
     /**
      * Create a Scroller with the default duration and interpolator.
@@ -88,13 +80,28 @@
     public Scroller(Context context, Interpolator interpolator) {
         mFinished = true;
         mInterpolator = interpolator;
-        float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
-        mDeceleration = SensorManager.GRAVITY_EARTH   // g (m/s^2)
-                      * 39.37f                        // inch/meter
-                      * ppi                           // pixels per inch
-                      * ViewConfiguration.getScrollFriction();
+        mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
+        mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
+    }
+
+    /**
+     * The amount of friction applied to flings. The default value
+     * is {@link ViewConfiguration#getScrollFriction}.
+     * 
+     * @return A scalar dimensionless value representing the coefficient of
+     *         friction.
+     */
+    public final void setFriction(float friction) {
+        mDeceleration = computeDeceleration(friction);
     }
     
+    private float computeDeceleration(float friction) {
+        return SensorManager.GRAVITY_EARTH   // g (m/s^2)
+                      * 39.37f               // inch/meter
+                      * mPpi                 // pixels per inch
+                      * friction;
+    }
+
     /**
      * 
      * Returns whether the scroller has finished scrolling.
@@ -286,6 +293,11 @@
         mDeltaX = dx;
         mDeltaY = dy;
         mDurationReciprocal = 1.0f / (float) mDuration;
+        // This controls the viscous fluid effect (how much of it)
+        mViscousFluidScale = 8.0f;
+        // must be set to 1.0 (used in viscousFluid())
+        mViscousFluidNormalize = 1.0f;
+        mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
     }
 
     /**
@@ -343,9 +355,11 @@
         mFinalY = Math.max(mFinalY, mMinY);
     }
     
-    static float viscousFluid(float x)
+    
+    
+    private float viscousFluid(float x)
     {
-        x *= sViscousFluidScale;
+        x *= mViscousFluidScale;
         if (x < 1.0f) {
             x -= (1.0f - (float)Math.exp(-x));
         } else {
@@ -353,7 +367,7 @@
             x = 1.0f - (float)Math.exp(1.0f - x);
             x = start + x * (1.0f - start);
         }
-        x *= sViscousFluidNormalize;
+        x *= mViscousFluidNormalize;
         return x;
     }
     
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
new file mode 100644
index 0000000..029aebf
--- /dev/null
+++ b/core/java/android/widget/SearchView.java
@@ -0,0 +1,1021 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import static android.widget.SuggestionsAdapter.getColumnString;
+
+import com.android.internal.R;
+
+import android.app.PendingIntent;
+import android.app.SearchManager;
+import android.app.SearchableInfo;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.speech.RecognizerIntent;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.TextView.OnEditorActionListener;
+
+import java.util.WeakHashMap;
+
+/**
+ * Provides the user interface elements for the user to enter a search query and submit a
+ * request to a search provider. Shows a list of query suggestions or results, if
+ * available and allows the user to pick a suggestion or result to launch into.
+ *
+ * <p>
+ * <b>XML attributes</b>
+ * <p>
+ * See {@link android.R.styleable#SearchView SearchView Attributes},
+ * {@link android.R.styleable#View View Attributes}
+ *
+ * @attr ref android.R.styleable#SearchView_iconifiedByDefault
+ * @attr ref android.R.styleable#SearchView_maxWidth
+ */
+public class SearchView extends LinearLayout {
+
+    private static final boolean DBG = false;
+    private static final String LOG_TAG = "SearchView";
+
+    private OnQueryChangeListener mOnQueryChangeListener;
+    private OnCloseListener mOnCloseListener;
+    private OnFocusChangeListener mOnQueryTextFocusChangeListener;
+    private OnSuggestionSelectionListener mOnSuggestionListener;
+
+    private boolean mIconifiedByDefault;
+    private boolean mIconified;
+    private CursorAdapter mSuggestionsAdapter;
+    private View mSearchButton;
+    private View mSubmitButton;
+    private View mCloseButton;
+    private View mSearchEditFrame;
+    private View mVoiceButton;
+    private AutoCompleteTextView mQueryTextView;
+    private boolean mSubmitButtonEnabled;
+    private CharSequence mQueryHint;
+    private boolean mQueryRefinement;
+    private boolean mClearingFocus;
+    private int mMaxWidth;
+
+    private SearchableInfo mSearchable;
+
+    // For voice searching
+    private final Intent mVoiceWebSearchIntent;
+    private final Intent mVoiceAppSearchIntent;
+
+    // A weak map of drawables we've gotten from other packages, so we don't load them
+    // more than once.
+    private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache =
+            new WeakHashMap<String, Drawable.ConstantState>();
+
+    /**
+     * Callbacks for changes to the query text.
+     */
+    public interface OnQueryChangeListener {
+
+        /**
+         * Called when the user submits the query. This could be due to a key press on the
+         * keyboard or due to pressing a submit button.
+         * The listener can override the standard behavior by returning true
+         * to indicate that it has handled the submit request. Otherwise return false to
+         * let the SearchView handle the submission by launching any associated intent.
+         *
+         * @param query the query text that is to be submitted
+         *
+         * @return true if the query has been handled by the listener, false to let the
+         * SearchView perform the default action.
+         */
+        boolean onSubmitQuery(String query);
+
+        /**
+         * Called when the query text is changed by the user.
+         *
+         * @param newText the new content of the query text field.
+         *
+         * @return false if the SearchView should perform the default action of showing any
+         * suggestions if available, true if the action was handled by the listener.
+         */
+        boolean onQueryTextChanged(String newText);
+    }
+
+    public interface OnCloseListener {
+
+        /**
+         * The user is attempting to close the SearchView.
+         *
+         * @return true if the listener wants to override the default behavior of clearing the
+         * text field and dismissing it, false otherwise.
+         */
+        boolean onClose();
+    }
+
+    /**
+     * Callback interface for selection events on suggestions. These callbacks
+     * are only relevant when a SearchableInfo has been specified by {@link #setSearchableInfo}.
+     */
+    public interface OnSuggestionSelectionListener {
+
+        /**
+         * Called when a suggestion was selected by navigating to it.
+         * @param position the absolute position in the list of suggestions.
+         *
+         * @return true if the listener handles the event and wants to override the default
+         * behavior of possibly rewriting the query based on the selected item, false otherwise.
+         */
+        boolean onSuggestionSelected(int position);
+
+        /**
+         * Called when a suggestion was clicked.
+         * @param position the absolute position of the clicked item in the list of suggestions.
+         *
+         * @return true if the listener handles the event and wants to override the default
+         * behavior of launching any intent or submitting a search query specified on that item.
+         * Return false otherwise.
+         */
+        boolean onSuggestionClicked(int position);
+    }
+
+    public SearchView(Context context) {
+        this(context, null);
+    }
+
+    public SearchView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        LayoutInflater inflater = (LayoutInflater) context
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        inflater.inflate(R.layout.search_view, this, true);
+
+        mSearchButton = findViewById(R.id.search_button);
+        mQueryTextView = (AutoCompleteTextView) findViewById(R.id.search_src_text);
+        mSearchEditFrame = findViewById(R.id.search_edit_frame);
+        mSubmitButton = findViewById(R.id.search_go_btn);
+        mCloseButton = findViewById(R.id.search_close_btn);
+        mVoiceButton = findViewById(R.id.search_voice_btn);
+
+        mSearchButton.setOnClickListener(mOnClickListener);
+        mCloseButton.setOnClickListener(mOnClickListener);
+        mSubmitButton.setOnClickListener(mOnClickListener);
+        mVoiceButton.setOnClickListener(mOnClickListener);
+
+        mQueryTextView.addTextChangedListener(mTextWatcher);
+        mQueryTextView.setOnEditorActionListener(mOnEditorActionListener);
+        mQueryTextView.setOnItemClickListener(mOnItemClickListener);
+        mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener);
+        // Inform any listener of focus changes 
+        mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() {
+
+            public void onFocusChange(View v, boolean hasFocus) {
+                if (mOnQueryTextFocusChangeListener != null) {
+                    mOnQueryTextFocusChangeListener.onFocusChange(SearchView.this, hasFocus);
+                }
+            }
+        });
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SearchView, 0, 0);
+        setIconifiedByDefault(a.getBoolean(R.styleable.SearchView_iconifiedByDefault, true));
+        int maxWidth = a.getDimensionPixelSize(R.styleable.SearchView_maxWidth, -1);
+        if (maxWidth != -1) {
+            setMaxWidth(maxWidth);
+        }
+        a.recycle();
+
+        // Save voice intent for later queries/launching
+        mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
+        mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+                RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
+
+        mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+        mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        updateViewsVisibility(mIconifiedByDefault);
+    }
+
+    /**
+     * Sets the SearchableInfo for this SearchView. Properties in the SearchableInfo are used
+     * to display labels, hints, suggestions, create intents for launching search results screens
+     * and controlling other affordances such as a voice button.
+     *
+     * @param searchable a SearchableInfo can be retrieved from the SearchManager, for a specific
+     * activity or a global search provider.
+     */
+    public void setSearchableInfo(SearchableInfo searchable) {
+        mSearchable = searchable;
+        if (mSearchable != null) {
+            updateSearchAutoComplete();
+        }
+        updateViewsVisibility(mIconifiedByDefault);
+    }
+
+    /** @hide */
+    @Override
+    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+        if (mClearingFocus || isIconified()) return false;
+        return mQueryTextView.requestFocus(direction, previouslyFocusedRect);
+    }
+
+    /** @hide */
+    @Override
+    public void clearFocus() {
+        mClearingFocus = true;
+        super.clearFocus();
+        mQueryTextView.clearFocus();
+        setImeVisibility(false);
+        mClearingFocus = false;
+    }
+
+    /**
+     * Sets a listener for user actions within the SearchView.
+     *
+     * @param listener the listener object that receives callbacks when the user performs
+     * actions in the SearchView such as clicking on buttons or typing a query.
+     */
+    public void setOnQueryChangeListener(OnQueryChangeListener listener) {
+        mOnQueryChangeListener = listener;
+    }
+
+    /**
+     * Sets a listener to inform when the user closes the SearchView.
+     *
+     * @param listener the listener to call when the user closes the SearchView.
+     */
+    public void setOnCloseListener(OnCloseListener listener) {
+        mOnCloseListener = listener;
+    }
+
+    /**
+     * Sets a listener to inform when the focus of the query text field changes.
+     *
+     * @param listener the listener to inform of focus changes.
+     */
+    public void setOnQueryTextFocusChangeListener(OnFocusChangeListener listener) {
+        mOnQueryTextFocusChangeListener = listener;
+    }
+
+    /**
+     * Sets a listener to inform when a suggestion is focused or clicked.
+     *
+     * @param listener the listener to inform of suggestion selection events.
+     */
+    public void setOnSuggestionSelectionListener(OnSuggestionSelectionListener listener) {
+        mOnSuggestionListener = listener;
+    }
+
+    /**
+     * Sets a query string in the text field and optionally submits the query as well.
+     *
+     * @param query the query string. This replaces any query text already present in the
+     * text field.
+     * @param submit whether to submit the query right now or only update the contents of
+     * text field.
+     */
+    public void setQuery(CharSequence query, boolean submit) {
+        mQueryTextView.setText(query);
+        if (query != null) {
+            mQueryTextView.setSelection(query.length());
+        }
+
+        // If the query is not empty and submit is requested, submit the query
+        if (submit && !TextUtils.isEmpty(query)) {
+            onSubmitQuery();
+        }
+    }
+
+    /**
+     * Sets the hint text to display in the query text field. This overrides any hint specified
+     * in the SearchableInfo.
+     *
+     * @param hint the hint text to display
+     */
+    public void setQueryHint(CharSequence hint) {
+        mQueryHint = hint;
+        updateQueryHint();
+    }
+
+    /**
+     * Sets the default or resting state of the search field. If true, a single search icon is
+     * shown by default and expands to show the text field and other buttons when pressed. Also,
+     * if the default state is iconified, then it collapses to that state when the close button
+     * is pressed. Changes to this property will take effect immediately.
+     *
+     * <p>The default value is false.</p>
+     *
+     * @param iconified whether the search field should be iconified by default
+     */
+    public void setIconifiedByDefault(boolean iconified) {
+        if (mIconifiedByDefault == iconified) return;
+        mIconifiedByDefault = iconified;
+        updateViewsVisibility(iconified);
+        setImeVisibility(!iconified);
+    }
+
+    /**
+     * Returns the default iconified state of the search field.
+     * @return
+     */
+    public boolean isIconfiedByDefault() {
+        return mIconifiedByDefault;
+    }
+
+    /**
+     * Iconifies or expands the SearchView. Any query text is cleared when iconified. This is
+     * a temporary state and does not override the default iconified state set by
+     * {@link #setIconifiedByDefault(boolean)}. If the default state is iconified, then
+     * a false here will only be valid until the user closes the field. And if the default
+     * state is expanded, then a true here will only clear the text field and not close it.
+     *
+     * @param iconify a true value will collapse the SearchView to an icon, while a false will
+     * expand it.
+     */
+    public void setIconified(boolean iconify) {
+        if (iconify) {
+            onCloseClicked();
+        } else {
+            onSearchClicked();
+        }
+    }
+
+    /**
+     * Returns the current iconified state of the SearchView.
+     *
+     * @return true if the SearchView is currently iconified, false if the search field is
+     * fully visible.
+     */
+    public boolean isIconified() {
+        return mIconified;
+    }
+
+    /**
+     * Enables showing a submit button when the query is non-empty. In cases where the SearchView
+     * is being used to filter the contents of the current activity and doesn't launch a separate
+     * results activity, then the submit button should be disabled.
+     *
+     * @param enabled true to show a submit button for submitting queries, false if a submit
+     * button is not required.
+     */
+    public void setSubmitButtonEnabled(boolean enabled) {
+        mSubmitButtonEnabled = enabled;
+        updateViewsVisibility(isIconified());
+    }
+
+    /**
+     * Returns whether the submit button is enabled when necessary or never displayed.
+     *
+     * @return whether the submit button is enabled automatically when necessary
+     */
+    public boolean isSubmitButtonEnabled() {
+        return mSubmitButtonEnabled;
+    }
+
+    /**
+     * Specifies if a query refinement button should be displayed alongside each suggestion
+     * or if it should depend on the flags set in the individual items retrieved from the
+     * suggestions provider. Clicking on the query refinement button will replace the text
+     * in the query text field with the text from the suggestion. This flag only takes effect
+     * if a SearchableInfo has been specified with {@link #setSearchableInfo(SearchableInfo)}
+     * and not when using a custom adapter.
+     *
+     * @param enable true if all items should have a query refinement button, false if only
+     * those items that have a query refinement flag set should have the button.
+     *
+     * @see SearchManager#SUGGEST_COLUMN_FLAGS
+     * @see SearchManager#FLAG_QUERY_REFINEMENT
+     */
+    public void setQueryRefinementEnabled(boolean enable) {
+        mQueryRefinement = enable;
+        if (mSuggestionsAdapter instanceof SuggestionsAdapter) {
+            ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
+                    enable ? SuggestionsAdapter.REFINE_ALL : SuggestionsAdapter.REFINE_BY_ENTRY);
+        }
+    }
+
+    /**
+     * Returns whether query refinement is enabled for all items or only specific ones.
+     * @return true if enabled for all items, false otherwise.
+     */
+    public boolean isQueryRefinementEnabled() {
+        return mQueryRefinement;
+    }
+
+    /**
+     * You can set a custom adapter if you wish. Otherwise the default adapter is used to
+     * display the suggestions from the suggestions provider associated with the SearchableInfo.
+     *
+     * @see #setSearchableInfo(SearchableInfo)
+     */
+    public void setSuggestionsAdapter(CursorAdapter adapter) {
+        mSuggestionsAdapter = adapter;
+
+        mQueryTextView.setAdapter(mSuggestionsAdapter);
+    }
+
+    /**
+     * Returns the adapter used for suggestions, if any.
+     * @return the suggestions adapter
+     */
+    public CursorAdapter getSuggestionsAdapter() {
+        return mSuggestionsAdapter;
+    }
+
+    /**
+     * Makes the view at most this many pixels wide
+     *
+     * @attr ref android.R.styleable#SearchView_maxWidth
+     */
+    public void setMaxWidth(int maxpixels) {
+        mMaxWidth = maxpixels;
+
+        requestLayout();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+
+        if ((widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.EXACTLY) && mMaxWidth > 0
+                && width > mMaxWidth) {
+            super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxWidth, widthMode), heightMeasureSpec);
+        } else {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    private void updateViewsVisibility(final boolean collapsed) {
+        mIconified = collapsed;
+        // Visibility of views that are visible when collapsed
+        final int visCollapsed = collapsed ? VISIBLE : GONE;
+        // Visibility of views that are visible when expanded
+        final int visExpanded = collapsed ? GONE : VISIBLE;
+        // Is there text in the query
+        final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
+
+        mSearchButton.setVisibility(visCollapsed);
+        mSubmitButton.setVisibility(mSubmitButtonEnabled && hasText ? visExpanded : GONE);
+        mSearchEditFrame.setVisibility(visExpanded);
+        updateVoiceButton(!hasText);
+        requestLayout();
+        invalidate();
+    }
+
+    private void setImeVisibility(boolean visible) {
+        InputMethodManager imm = (InputMethodManager)
+        getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+        // We made sure the IME was displayed, so also make sure it is closed
+        // when we go away.
+        if (imm != null) {
+            if (visible) {
+                imm.showSoftInputUnchecked(0, null);
+            } else {
+                imm.hideSoftInputFromWindow(getWindowToken(), 0);
+            }
+        }
+    }
+
+    /**
+     * Called by the SuggestionsAdapter
+     * @hide
+     */
+    /* package */void onQueryRefine(CharSequence queryText) {
+        setQuery(queryText);
+    }
+
+    private final OnClickListener mOnClickListener = new OnClickListener() {
+
+        public void onClick(View v) {
+            if (v == mSearchButton) {
+                onSearchClicked();
+            } else if (v == mCloseButton) {
+                onCloseClicked();
+            } else if (v == mSubmitButton) {
+                onSubmitQuery();
+            } else if (v == mVoiceButton) {
+                onVoiceClicked();
+            }
+        }
+    };
+
+    /**
+     * Handles the key down event for dealing with action keys.
+     *
+     * @param keyCode This is the keycode of the typed key, and is the same value as
+     *        found in the KeyEvent parameter.
+     * @param event The complete event record for the typed key
+     *
+     * @return true if the event was handled here, or false if not.
+     */
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (mSearchable == null) {
+            return false;
+        }
+
+        // if it's an action specified by the searchable activity, launch the
+        // entered query with the action key
+        SearchableInfo.ActionKeyInfo actionKey = mSearchable.findActionKey(keyCode);
+        if ((actionKey != null) && (actionKey.getQueryActionMsg() != null)) {
+            launchQuerySearch(keyCode, actionKey.getQueryActionMsg(), mQueryTextView.getText()
+                    .toString());
+            return true;
+        }
+
+        return super.onKeyDown(keyCode, event);
+    }
+
+    private void updateQueryHint() {
+        if (mQueryHint != null) {
+            mQueryTextView.setHint(mQueryHint);
+        } else if (mSearchable != null) {
+            CharSequence hint = null;
+            int hintId = mSearchable.getHintId();
+            if (hintId != 0) {
+                hint = getContext().getString(hintId);
+            }
+            if (hint != null) {
+                mQueryTextView.setHint(hint);
+            }
+        }
+    }
+
+    /**
+     * Updates the auto-complete text view.
+     */
+    private void updateSearchAutoComplete() {
+        // close any existing suggestions adapter
+        //closeSuggestionsAdapter();
+
+        mQueryTextView.setDropDownAnimationStyle(0); // no animation
+        mQueryTextView.setThreshold(mSearchable.getSuggestThreshold());
+
+        // attach the suggestions adapter, if suggestions are available
+        // The existence of a suggestions authority is the proxy for "suggestions available here"
+        if (mSearchable.getSuggestAuthority() != null) {
+            mSuggestionsAdapter = new SuggestionsAdapter(getContext(),
+                    this, mSearchable, mOutsideDrawablesCache);
+            mQueryTextView.setAdapter(mSuggestionsAdapter);
+            ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
+                    mQueryRefinement ? SuggestionsAdapter.REFINE_ALL
+                    : SuggestionsAdapter.REFINE_BY_ENTRY);
+        }
+    }
+
+    /**
+     * Update the visibility of the voice button.  There are actually two voice search modes,
+     * either of which will activate the button.
+     * @param empty whether the search query text field is empty. If it is, then the other
+     * criteria apply to make the voice button visible. Otherwise the voice button will not
+     * be visible - i.e., if the user has typed a query, remove the voice button.
+     */
+    private void updateVoiceButton(boolean empty) {
+        int visibility = View.GONE;
+        if (mSearchable != null && mSearchable.getVoiceSearchEnabled() && empty
+                && !isIconified()) {
+            Intent testIntent = null;
+            if (mSearchable.getVoiceSearchLaunchWebSearch()) {
+                testIntent = mVoiceWebSearchIntent;
+            } else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
+                testIntent = mVoiceAppSearchIntent;
+            }
+            if (testIntent != null) {
+                ResolveInfo ri = getContext().getPackageManager().resolveActivity(testIntent,
+                        PackageManager.MATCH_DEFAULT_ONLY);
+                if (ri != null) {
+                    visibility = View.VISIBLE;
+                }
+            }
+        }
+        mVoiceButton.setVisibility(visibility);
+    }
+
+    private final OnEditorActionListener mOnEditorActionListener = new OnEditorActionListener() {
+
+        /**
+         * Called when the input method default action key is pressed.
+         */
+        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+            onSubmitQuery();
+            return true;
+        }
+    };
+
+    private void onTextChanged(CharSequence newText) {
+        CharSequence text = mQueryTextView.getText();
+        boolean hasText = !TextUtils.isEmpty(text);
+        if (isSubmitButtonEnabled()) {
+            mSubmitButton.setVisibility(hasText ? VISIBLE : GONE);
+            requestLayout();
+            invalidate();
+        }
+        updateVoiceButton(!hasText);
+        if (mOnQueryChangeListener != null) {
+            mOnQueryChangeListener.onQueryTextChanged(newText.toString());
+        }
+    }
+
+    private void onSubmitQuery() {
+        CharSequence query = mQueryTextView.getText();
+        if (!TextUtils.isEmpty(query)) {
+            if (mOnQueryChangeListener == null
+                    || !mOnQueryChangeListener.onSubmitQuery(query.toString())) {
+                if (mSearchable != null) {
+                    launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString());
+                    setImeVisibility(false);
+                }
+                dismissSuggestions();
+            }
+        }
+    }
+
+    private void dismissSuggestions() {
+        mQueryTextView.dismissDropDown();
+    }
+
+    private void onCloseClicked() {
+        if (mOnCloseListener == null || !mOnCloseListener.onClose()) {
+            CharSequence text = mQueryTextView.getText();
+            if (TextUtils.isEmpty(text)) {
+                // query field already empty, hide the keyboard and remove focus
+                clearFocus();
+                setImeVisibility(false);
+            } else {
+                mQueryTextView.setText("");
+            }
+            updateViewsVisibility(mIconifiedByDefault);
+            if (mIconifiedByDefault) setImeVisibility(false);
+        }
+    }
+
+    private void onSearchClicked() {
+        mQueryTextView.requestFocus();
+        updateViewsVisibility(false);
+        setImeVisibility(true);
+    }
+
+    private void onVoiceClicked() {
+        // guard against possible race conditions
+        if (mSearchable == null) {
+            return;
+        }
+        SearchableInfo searchable = mSearchable;
+        try {
+            if (searchable.getVoiceSearchLaunchWebSearch()) {
+                Intent webSearchIntent = createVoiceWebSearchIntent(mVoiceWebSearchIntent,
+                        searchable);
+                getContext().startActivity(webSearchIntent);
+            } else if (searchable.getVoiceSearchLaunchRecognizer()) {
+                Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent,
+                        searchable);
+                getContext().startActivity(appSearchIntent);
+            }
+        } catch (ActivityNotFoundException e) {
+            // Should not happen, since we check the availability of
+            // voice search before showing the button. But just in case...
+            Log.w(LOG_TAG, "Could not find voice search activity");
+        }
+    }
+
+    private final OnItemClickListener mOnItemClickListener = new OnItemClickListener() {
+
+        /**
+         * Implements OnItemClickListener
+         */
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            if (DBG)
+                Log.d(LOG_TAG, "onItemClick() position " + position);
+            if (mOnSuggestionListener == null
+                    || !mOnSuggestionListener.onSuggestionClicked(position)) {
+                launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
+                setImeVisibility(false);
+                dismissSuggestions();
+            }
+        }
+    };
+
+    private final OnItemSelectedListener mOnItemSelectedListener = new OnItemSelectedListener() {
+
+        /**
+         * Implements OnItemSelectedListener
+         */
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            if (DBG)
+                Log.d(LOG_TAG, "onItemSelected() position " + position);
+            // A suggestion has been selected, rewrite the query if possible,
+            // otherwise the restore the original query.
+            if (mOnSuggestionListener == null
+                    || !mOnSuggestionListener.onSuggestionSelected(position)) {
+                rewriteQueryFromSuggestion(position);
+            }
+        }
+
+        /**
+         * Implements OnItemSelectedListener
+         */
+        public void onNothingSelected(AdapterView<?> parent) {
+            if (DBG)
+                Log.d(LOG_TAG, "onNothingSelected()");
+        }
+    };
+
+    /**
+     * Query rewriting.
+     */
+    private void rewriteQueryFromSuggestion(int position) {
+        CharSequence oldQuery = mQueryTextView.getText();
+        Cursor c = mSuggestionsAdapter.getCursor();
+        if (c == null) {
+            return;
+        }
+        if (c.moveToPosition(position)) {
+            // Get the new query from the suggestion.
+            CharSequence newQuery = mSuggestionsAdapter.convertToString(c);
+            if (newQuery != null) {
+                // The suggestion rewrites the query.
+                // Update the text field, without getting new suggestions.
+                setQuery(newQuery);
+            } else {
+                // The suggestion does not rewrite the query, restore the user's query.
+                setQuery(oldQuery);
+            }
+        } else {
+            // We got a bad position, restore the user's query.
+            setQuery(oldQuery);
+        }
+    }
+
+    /**
+     * Launches an intent based on a suggestion.
+     *
+     * @param position The index of the suggestion to create the intent from.
+     * @param actionKey The key code of the action key that was pressed,
+     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
+     * @param actionMsg The message for the action key that was pressed,
+     *        or <code>null</code> if none.
+     * @return true if a successful launch, false if could not (e.g. bad position).
+     */
+    private boolean launchSuggestion(int position, int actionKey, String actionMsg) {
+        Cursor c = mSuggestionsAdapter.getCursor();
+        if ((c != null) && c.moveToPosition(position)) {
+
+            Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg);
+
+            // launch the intent
+            launchIntent(intent);
+
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Launches an intent, including any special intent handling.
+     */
+    private void launchIntent(Intent intent) {
+        if (intent == null) {
+            return;
+        }
+        try {
+            // If the intent was created from a suggestion, it will always have an explicit
+            // component here.
+            getContext().startActivity(intent);
+        } catch (RuntimeException ex) {
+            Log.e(LOG_TAG, "Failed launch activity: " + intent, ex);
+        }
+    }
+
+    /**
+     * Sets the text in the query box, without updating the suggestions.
+     */
+    private void setQuery(CharSequence query) {
+        mQueryTextView.setText(query, true);
+        // Move the cursor to the end
+        mQueryTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
+    }
+
+    private void launchQuerySearch(int actionKey, String actionMsg, String query) {
+        String action = Intent.ACTION_SEARCH;
+        Intent intent = createIntent(action, null, null, query, actionKey, actionMsg);
+        getContext().startActivity(intent);
+    }
+
+    /**
+     * Constructs an intent from the given information and the search dialog state.
+     *
+     * @param action Intent action.
+     * @param data Intent data, or <code>null</code>.
+     * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>.
+     * @param query Intent query, or <code>null</code>.
+     * @param actionKey The key code of the action key that was pressed,
+     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
+     * @param actionMsg The message for the action key that was pressed,
+     *        or <code>null</code> if none.
+     * @param mode The search mode, one of the acceptable values for
+     *             {@link SearchManager#SEARCH_MODE}, or {@code null}.
+     * @return The intent.
+     */
+    private Intent createIntent(String action, Uri data, String extraData, String query,
+            int actionKey, String actionMsg) {
+        // Now build the Intent
+        Intent intent = new Intent(action);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        // We need CLEAR_TOP to avoid reusing an old task that has other activities
+        // on top of the one we want. We don't want to do this in in-app search though,
+        // as it can be destructive to the activity stack.
+        if (data != null) {
+            intent.setData(data);
+        }
+        intent.putExtra(SearchManager.USER_QUERY, query);
+        if (query != null) {
+            intent.putExtra(SearchManager.QUERY, query);
+        }
+        if (extraData != null) {
+            intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
+        }
+        if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
+            intent.putExtra(SearchManager.ACTION_KEY, actionKey);
+            intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
+        }
+        intent.setComponent(mSearchable.getSearchActivity());
+        return intent;
+    }
+
+    /**
+     * Create and return an Intent that can launch the voice search activity for web search.
+     */
+    private Intent createVoiceWebSearchIntent(Intent baseIntent, SearchableInfo searchable) {
+        Intent voiceIntent = new Intent(baseIntent);
+        ComponentName searchActivity = searchable.getSearchActivity();
+        voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null
+                : searchActivity.flattenToShortString());
+        return voiceIntent;
+    }
+
+    /**
+     * Create and return an Intent that can launch the voice search activity, perform a specific
+     * voice transcription, and forward the results to the searchable activity.
+     *
+     * @param baseIntent The voice app search intent to start from
+     * @return A completely-configured intent ready to send to the voice search activity
+     */
+    private Intent createVoiceAppSearchIntent(Intent baseIntent, SearchableInfo searchable) {
+        ComponentName searchActivity = searchable.getSearchActivity();
+
+        // create the necessary intent to set up a search-and-forward operation
+        // in the voice search system.   We have to keep the bundle separate,
+        // because it becomes immutable once it enters the PendingIntent
+        Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
+        queryIntent.setComponent(searchActivity);
+        PendingIntent pending = PendingIntent.getActivity(getContext(), 0, queryIntent,
+                PendingIntent.FLAG_ONE_SHOT);
+
+        // Now set up the bundle that will be inserted into the pending intent
+        // when it's time to do the search.  We always build it here (even if empty)
+        // because the voice search activity will always need to insert "QUERY" into
+        // it anyway.
+        Bundle queryExtras = new Bundle();
+
+        // Now build the intent to launch the voice search.  Add all necessary
+        // extras to launch the voice recognizer, and then all the necessary extras
+        // to forward the results to the searchable activity
+        Intent voiceIntent = new Intent(baseIntent);
+
+        // Add all of the configuration options supplied by the searchable's metadata
+        String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM;
+        String prompt = null;
+        String language = null;
+        int maxResults = 1;
+
+        Resources resources = getResources();
+        if (searchable.getVoiceLanguageModeId() != 0) {
+            languageModel = resources.getString(searchable.getVoiceLanguageModeId());
+        }
+        if (searchable.getVoicePromptTextId() != 0) {
+            prompt = resources.getString(searchable.getVoicePromptTextId());
+        }
+        if (searchable.getVoiceLanguageId() != 0) {
+            language = resources.getString(searchable.getVoiceLanguageId());
+        }
+        if (searchable.getVoiceMaxResults() != 0) {
+            maxResults = searchable.getVoiceMaxResults();
+        }
+        voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
+        voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
+        voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
+        voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
+        voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null
+                : searchActivity.flattenToShortString());
+
+        // Add the values that configure forwarding the results
+        voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending);
+        voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras);
+
+        return voiceIntent;
+    }
+
+    /**
+     * When a particular suggestion has been selected, perform the various lookups required
+     * to use the suggestion.  This includes checking the cursor for suggestion-specific data,
+     * and/or falling back to the XML for defaults;  It also creates REST style Uri data when
+     * the suggestion includes a data id.
+     *
+     * @param c The suggestions cursor, moved to the row of the user's selection
+     * @param actionKey The key code of the action key that was pressed,
+     *        or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
+     * @param actionMsg The message for the action key that was pressed,
+     *        or <code>null</code> if none.
+     * @return An intent for the suggestion at the cursor's position.
+     */
+    private Intent createIntentFromSuggestion(Cursor c, int actionKey, String actionMsg) {
+        try {
+            // use specific action if supplied, or default action if supplied, or fixed default
+            String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
+
+            if (action == null) {
+                action = mSearchable.getSuggestIntentAction();
+            }
+            if (action == null) {
+                action = Intent.ACTION_SEARCH;
+            }
+
+            // use specific data if supplied, or default data if supplied
+            String data = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
+            if (data == null) {
+                data = mSearchable.getSuggestIntentData();
+            }
+            // then, if an ID was provided, append it.
+            if (data != null) {
+                String id = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
+                if (id != null) {
+                    data = data + "/" + Uri.encode(id);
+                }
+            }
+            Uri dataUri = (data == null) ? null : Uri.parse(data);
+
+            String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
+            String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
+
+            return createIntent(action, dataUri, extraData, query, actionKey, actionMsg);
+        } catch (RuntimeException e ) {
+            int rowNum;
+            try {                       // be really paranoid now
+                rowNum = c.getPosition();
+            } catch (RuntimeException e2 ) {
+                rowNum = -1;
+            }
+            Log.w(LOG_TAG, "Search Suggestions cursor at row " + rowNum +
+                            " returned exception" + e.toString());
+            return null;
+        }
+    }
+
+    /**
+     * Callback to watch the text field for empty/non-empty
+     */
+    private TextWatcher mTextWatcher = new TextWatcher() {
+
+        public void beforeTextChanged(CharSequence s, int start, int before, int after) { }
+
+        public void onTextChanged(CharSequence s, int start,
+                int before, int after) {
+            SearchView.this.onTextChanged(s);
+        }
+
+        public void afterTextChanged(Editable s) {
+        }
+    };
+}
diff --git a/core/java/android/widget/SimpleCursorAdapter.java b/core/java/android/widget/SimpleCursorAdapter.java
index 7d3459e..d1c2270 100644
--- a/core/java/android/widget/SimpleCursorAdapter.java
+++ b/core/java/android/widget/SimpleCursorAdapter.java
@@ -62,7 +62,8 @@
     private int mStringConversionColumn = -1;
     private CursorToStringConverter mCursorToStringConverter;
     private ViewBinder mViewBinder;
-    private String[] mOriginalFrom;
+
+    String[] mOriginalFrom;
 
     /**
      * Constructor.
diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java
index 11d72de..bdeb5c2 100644
--- a/core/java/android/widget/SlidingDrawer.java
+++ b/core/java/android/widget/SlidingDrawer.java
@@ -652,7 +652,7 @@
         // Try only once... we should really loop but it's not a big deal
         // if the draw was cancelled, it will only be temporary anyway
         content.getViewTreeObserver().dispatchOnPreDraw();
-        content.buildDrawingCache();
+        if (!content.isHardwareAccelerated()) content.buildDrawingCache();
 
         content.setVisibility(View.GONE);        
     }
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 8ddb06c..60422ae 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -24,6 +24,7 @@
 import android.content.res.TypedArray;
 import android.database.DataSetObserver;
 import android.util.AttributeSet;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -40,27 +41,154 @@
  */
 @Widget
 public class Spinner extends AbsSpinner implements OnClickListener {
+    private static final String TAG = "Spinner";
     
-    private CharSequence mPrompt;
-    private AlertDialog mPopup;
+    /**
+     * Use a dialog window for selecting spinner options.
+     */
+    public static final int MODE_DIALOG = 0;
+    
+    /**
+     * Use a dropdown anchored to the Spinner for selecting spinner options.
+     */
+    public static final int MODE_DROPDOWN = 1;
+    
+    /**
+     * Use the theme-supplied value to select the dropdown mode.
+     */
+    private static final int MODE_THEME = -1;
+    
+    private SpinnerPopup mPopup;
+    private DropDownAdapter mTempAdapter;
 
+    /**
+     * Construct a new spinner with the given context's theme.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     */
     public Spinner(Context context) {
         this(context, null);
     }
 
+    /**
+     * Construct a new spinner with the given context's theme and the supplied
+     * mode of displaying choices. <code>mode</code> may be one of
+     * {@link #MODE_DIALOG} or {@link #MODE_DROPDOWN}.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     * @param mode Constant describing how the user will select choices from the spinner.
+     * 
+     * @see #MODE_DIALOG
+     * @see #MODE_DROPDOWN
+     */
+    public Spinner(Context context, int mode) {
+        this(context, null, com.android.internal.R.attr.spinnerStyle, mode);
+    }
+
+    /**
+     * Construct a new spinner with the given context's theme and the supplied attribute set.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     */
     public Spinner(Context context, AttributeSet attrs) {
         this(context, attrs, com.android.internal.R.attr.spinnerStyle);
     }
 
+    /**
+     * Construct a new spinner with the given context's theme, the supplied attribute set,
+     * and default style.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @param defStyle The default style to apply to this view. If 0, no style
+     *        will be applied (beyond what is included in the theme). This may
+     *        either be an attribute resource, whose value will be retrieved
+     *        from the current theme, or an explicit style resource.
+     */
     public Spinner(Context context, AttributeSet attrs, int defStyle) {
+        this(context, attrs, defStyle, MODE_THEME);
+    }
+
+    /**
+     * Construct a new spinner with the given context's theme, the supplied attribute set,
+     * and default style. <code>mode</code> may be one of {@link #MODE_DIALOG} or
+     * {@link #MODE_DROPDOWN} and determines how the user will select choices from the spinner.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @param defStyle The default style to apply to this view. If 0, no style
+     *        will be applied (beyond what is included in the theme). This may
+     *        either be an attribute resource, whose value will be retrieved
+     *        from the current theme, or an explicit style resource.
+     * @param mode Constant describing how the user will select choices from the spinner.
+     * 
+     * @see #MODE_DIALOG
+     * @see #MODE_DROPDOWN
+     */
+    public Spinner(Context context, AttributeSet attrs, int defStyle, int mode) {
         super(context, attrs, defStyle);
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 com.android.internal.R.styleable.Spinner, defStyle, 0);
+
+        if (mode == MODE_THEME) {
+            mode = a.getInt(com.android.internal.R.styleable.Spinner_spinnerMode, MODE_DIALOG);
+        }
         
-        mPrompt = a.getString(com.android.internal.R.styleable.Spinner_prompt);
+        switch (mode) {
+        case MODE_DIALOG: {
+            mPopup = new DialogPopup();
+            break;
+        }
+
+        case MODE_DROPDOWN: {
+            final int hintResource = a.getResourceId(
+                    com.android.internal.R.styleable.Spinner_popupPromptView, 0);
+
+            DropdownPopup popup = new DropdownPopup(context, attrs, defStyle, hintResource);
+
+            popup.setWidth(a.getLayoutDimension(
+                    com.android.internal.R.styleable.Spinner_dropDownWidth,
+                    ViewGroup.LayoutParams.WRAP_CONTENT));
+            popup.setBackgroundDrawable(a.getDrawable(
+                    com.android.internal.R.styleable.Spinner_popupBackground));
+            popup.setVerticalOffset(a.getDimensionPixelOffset(
+                    com.android.internal.R.styleable.Spinner_dropDownVerticalOffset, 0));
+            popup.setHorizontalOffset(a.getDimensionPixelOffset(
+                    com.android.internal.R.styleable.Spinner_dropDownHorizontalOffset, 0));
+
+            mPopup = popup;
+            break;
+        }
+        }
+        
+        mPopup.setPromptText(a.getString(com.android.internal.R.styleable.Spinner_prompt));
 
         a.recycle();
+
+        // Base constructor can call setAdapter before we initialize mPopup.
+        // Finish setting things up if this happened.
+        if (mTempAdapter != null) {
+            mPopup.setAdapter(mTempAdapter);
+            mTempAdapter = null;
+        }
+    }
+    
+    @Override
+    public void setAdapter(SpinnerAdapter adapter) {
+        super.setAdapter(adapter);
+
+        if (mPopup != null) {
+            mPopup.setAdapter(new DropDownAdapter(adapter));
+        } else {
+            mTempAdapter = new DropDownAdapter(adapter);
+        }
     }
 
     @Override
@@ -87,7 +215,6 @@
         
         if (mPopup != null && mPopup.isShowing()) {
             mPopup.dismiss();
-            mPopup = null;
         }
     }
 
@@ -197,8 +324,6 @@
         return child;
     }
 
-
-
     /**
      * Helper for makeAndAddView to set the position of a view
      * and fill out its layout paramters.
@@ -249,15 +374,10 @@
         
         if (!handled) {
             handled = true;
-            Context context = getContext();
-            
-            final DropDownAdapter adapter = new DropDownAdapter(getAdapter());
 
-            AlertDialog.Builder builder = new AlertDialog.Builder(context);
-            if (mPrompt != null) {
-                builder.setTitle(mPrompt);
+            if (!mPopup.isShowing()) {
+                mPopup.show();
             }
-            mPopup = builder.setSingleChoiceItems(adapter, getSelectedItemPosition(), this).show();
         }
 
         return handled;
@@ -266,7 +386,6 @@
     public void onClick(DialogInterface dialog, int which) {
         setSelection(which);
         dialog.dismiss();
-        mPopup = null;
     }
 
     /**
@@ -274,7 +393,7 @@
      * @param prompt the prompt to set
      */
     public void setPrompt(CharSequence prompt) {
-        mPrompt = prompt;
+        mPopup.setPromptText(prompt);
     }
 
     /**
@@ -282,14 +401,14 @@
      * @param promptId the resource ID of the prompt to display when the dialog is shown
      */
     public void setPromptId(int promptId) {
-        mPrompt = getContext().getText(promptId);
+        setPrompt(getContext().getText(promptId));
     }
 
     /**
      * @return The prompt to display when the dialog is shown
      */
     public CharSequence getPrompt() {
-        return mPrompt;
+        return mPopup.getHintText();
     }
     
     /**
@@ -387,4 +506,124 @@
             return getCount() == 0;
         }
     }
+    
+    /**
+     * Implements some sort of popup selection interface for selecting a spinner option.
+     * Allows for different spinner modes.
+     */
+    private interface SpinnerPopup {
+        public void setAdapter(ListAdapter adapter);
+        
+        /**
+         * Show the popup
+         */
+        public void show();
+        
+        /**
+         * Dismiss the popup
+         */
+        public void dismiss();
+        
+        /**
+         * @return true if the popup is showing, false otherwise.
+         */
+        public boolean isShowing();
+        
+        /**
+         * Set hint text to be displayed to the user. This should provide
+         * a description of the choice being made.
+         * @param hintText Hint text to set.
+         */
+        public void setPromptText(CharSequence hintText);
+        public CharSequence getHintText();
+    }
+    
+    private class DialogPopup implements SpinnerPopup, DialogInterface.OnClickListener {
+        private AlertDialog mPopup;
+        private ListAdapter mListAdapter;
+        private CharSequence mPrompt;
+
+        public void dismiss() {
+            mPopup.dismiss();
+            mPopup = null;
+        }
+
+        public boolean isShowing() {
+            return mPopup != null ? mPopup.isShowing() : false;
+        }
+
+        public void setAdapter(ListAdapter adapter) {
+            mListAdapter = adapter;
+        }
+
+        public void setPromptText(CharSequence hintText) {
+            mPrompt = hintText;
+        }
+        
+        public CharSequence getHintText() {
+            return mPrompt;
+        }
+
+        public void show() {
+            AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+            if (mPrompt != null) {
+                builder.setTitle(mPrompt);
+            }
+            mPopup = builder.setSingleChoiceItems(mListAdapter,
+                    getSelectedItemPosition(), this).show();
+        }
+        
+        public void onClick(DialogInterface dialog, int which) {
+            setSelection(which);
+            dismiss();
+        }
+    }
+    
+    private class DropdownPopup extends ListPopupWindow implements SpinnerPopup {
+        private CharSequence mHintText;
+        private TextView mHintView;
+        private int mHintResource;
+        
+        public DropdownPopup(Context context, AttributeSet attrs,
+                int defStyleRes, int hintResource) {
+            super(context, attrs, 0, defStyleRes);
+            
+            mHintResource = hintResource;
+            
+            setAnchorView(Spinner.this);
+            setModal(true);
+            setPromptPosition(POSITION_PROMPT_ABOVE);
+            setOnItemClickListener(new OnItemClickListener() {
+                public void onItemClick(AdapterView parent, View v, int position, long id) {
+                    Spinner.this.setSelection(position);
+                    dismiss();
+                }
+            });
+        }
+        
+        public CharSequence getHintText() {
+            return mHintText;
+        }
+        
+        public void setPromptText(CharSequence hintText) {
+            mHintText = hintText;
+            if (mHintView != null) {
+                mHintView.setText(hintText);
+            }
+        }
+
+        @Override
+        public void show() {
+            if (mHintView == null) {
+                final TextView textView = (TextView) LayoutInflater.from(getContext()).inflate(
+                        mHintResource, null).findViewById(com.android.internal.R.id.text1);
+                textView.setText(mHintText);
+                setPromptView(textView);
+                mHintView = textView;
+            }
+            super.show();
+            getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+            setSelection(Spinner.this.getSelectedItemPosition());
+        }
+    }
 }
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
new file mode 100644
index 0000000..a27e1cc
--- /dev/null
+++ b/core/java/android/widget/StackView.java
@@ -0,0 +1,1063 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.graphics.TableMaskFilter;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.animation.LinearInterpolator;
+import android.widget.RemoteViews.RemoteView;
+
+@RemoteView
+/**
+ * A view that displays its children in a stack and allows users to discretely swipe
+ * through the children.
+ */
+public class StackView extends AdapterViewAnimator {
+    private final String TAG = "StackView";
+
+    /**
+     * Default animation parameters
+     */
+    private static final int DEFAULT_ANIMATION_DURATION = 400;
+    private static final int MINIMUM_ANIMATION_DURATION = 50;
+
+    /**
+     * Parameters effecting the perspective visuals
+     */
+    private static float PERSPECTIVE_SHIFT_FACTOR = 0.12f;
+    @SuppressWarnings({"FieldCanBeLocal"})
+    private static float PERSPECTIVE_SCALE_FACTOR = 0.35f;
+
+    /**
+     * Represent the two possible stack modes, one where items slide up, and the other
+     * where items slide down. The perspective is also inverted between these two modes.
+     */
+    private static final int ITEMS_SLIDE_UP = 0;
+    private static final int ITEMS_SLIDE_DOWN = 1;
+
+    /**
+     * These specify the different gesture states
+     */
+    private static final int GESTURE_NONE = 0;
+    private static final int GESTURE_SLIDE_UP = 1;
+    private static final int GESTURE_SLIDE_DOWN = 2;
+
+    /**
+     * Specifies how far you need to swipe (up or down) before it
+     * will be consider a completed gesture when you lift your finger
+     */
+    private static final float SWIPE_THRESHOLD_RATIO = 0.2f;
+
+    /**
+     * Specifies the total distance, relative to the size of the stack,
+     * that views will be slid, either up or down
+     */
+    private static final float SLIDE_UP_RATIO = 0.7f;
+
+    /**
+     * Sentinel value for no current active pointer.
+     * Used by {@link #mActivePointerId}.
+     */
+    private static final int INVALID_POINTER = -1;
+
+    /**
+     * Number of active views in the stack. One fewer view is actually visible, as one is hidden.
+     */
+    private static final int NUM_ACTIVE_VIEWS = 5;
+
+    private static final int FRAME_PADDING = 4;
+
+    /**
+     * These variables are all related to the current state of touch interaction
+     * with the stack
+     */
+    private float mInitialY;
+    private float mInitialX;
+    private int mActivePointerId;
+    private int mYVelocity = 0;
+    private int mSwipeGestureType = GESTURE_NONE;
+    private int mSlideAmount;
+    private int mSwipeThreshold;
+    private int mTouchSlop;
+    private int mMaximumVelocity;
+    private VelocityTracker mVelocityTracker;
+    private boolean mTransitionIsSetup = false;
+
+    private static HolographicHelper sHolographicHelper;
+    private ImageView mHighlight;
+    private StackSlider mStackSlider;
+    private boolean mFirstLayoutHappened = false;
+    private int mStackMode;
+    private int mFramePadding;
+    private final Rect invalidateRect = new Rect();
+
+    public StackView(Context context) {
+        super(context);
+        initStackView();
+    }
+
+    public StackView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initStackView();
+    }
+
+    private void initStackView() {
+        configureViewAnimator(NUM_ACTIVE_VIEWS, 1);
+        setStaticTransformationsEnabled(true);
+        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
+        mTouchSlop = configuration.getScaledTouchSlop();
+        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+        mActivePointerId = INVALID_POINTER;
+
+        mHighlight = new ImageView(getContext());
+        mHighlight.setLayoutParams(new LayoutParams(mHighlight));
+        addViewInLayout(mHighlight, -1, new LayoutParams(mHighlight));
+        mStackSlider = new StackSlider();
+
+        if (sHolographicHelper == null) {
+            sHolographicHelper = new HolographicHelper(mContext);
+        }
+        setClipChildren(false);
+        setClipToPadding(false);
+
+        // This sets the form of the StackView, which is currently to have the perspective-shifted
+        // views above the active view, and have items slide down when sliding out. The opposite is
+        // available by using ITEMS_SLIDE_UP.
+        mStackMode = ITEMS_SLIDE_DOWN;
+
+        // This is a flag to indicate the the stack is loading for the first time
+        mWhichChild = -1;
+
+        // Adjust the frame padding based on the density, since the highlight changes based
+        // on the density
+        final float density = mContext.getResources().getDisplayMetrics().density;
+        mFramePadding = (int) Math.ceil(density * FRAME_PADDING);
+    }
+
+    /**
+     * Animate the views between different relative indexes within the {@link AdapterViewAnimator}
+     */
+    void animateViewForTransition(int fromIndex, int toIndex, View view) {
+        if (fromIndex == -1 && toIndex != 0) {
+            // Fade item in
+            if (view.getAlpha() == 1) {
+                view.setAlpha(0);
+            }
+            view.setVisibility(VISIBLE);
+
+            ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, "alpha", view.getAlpha(), 1.0f);
+            fadeIn.setDuration(DEFAULT_ANIMATION_DURATION);
+            fadeIn.start();
+        } else if (fromIndex == 0 && toIndex == 1) {
+            // Slide item in
+            view.setVisibility(VISIBLE);
+
+            int duration = Math.round(mStackSlider.getDurationForNeutralPosition(mYVelocity));
+
+            StackSlider animationSlider = new StackSlider(mStackSlider);
+            PropertyValuesHolder slideInY = PropertyValuesHolder.ofFloat("YProgress", 0.0f);
+            PropertyValuesHolder slideInX = PropertyValuesHolder.ofFloat("XProgress", 0.0f);
+            ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(animationSlider,
+                    slideInX, slideInY);
+            pa.setDuration(duration);
+            pa.setInterpolator(new LinearInterpolator());
+            pa.start();
+        } else if (fromIndex == 1 && toIndex == 0) {
+            // Slide item out
+            int duration = Math.round(mStackSlider.getDurationForOffscreenPosition(mYVelocity));
+
+            StackSlider animationSlider = new StackSlider(mStackSlider);
+            PropertyValuesHolder slideOutY = PropertyValuesHolder.ofFloat("YProgress", 1.0f);
+            PropertyValuesHolder slideOutX = PropertyValuesHolder.ofFloat("XProgress", 0.0f);
+            ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(animationSlider,
+                    slideOutX, slideOutY);
+            pa.setDuration(duration);
+            pa.setInterpolator(new LinearInterpolator());
+            pa.start();
+        } else if (fromIndex == -1 && toIndex == 0) {
+            // Make sure this view that is "waiting in the wings" is invisible
+            view.setAlpha(0.0f);
+            view.setVisibility(INVISIBLE);
+            LayoutParams lp = (LayoutParams) view.getLayoutParams();
+            lp.setVerticalOffset(-mSlideAmount);
+        } else if (toIndex == -1) {
+            // Fade item out
+            ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, "alpha", view.getAlpha(), 0.0f);
+            fadeOut.setDuration(DEFAULT_ANIMATION_DURATION);
+            fadeOut.start();
+        }
+
+        // Implement the faked perspective
+        if (toIndex != -1) {
+            transformViewAtIndex(toIndex, view);
+        }
+    }
+
+    private void setupStackSlider(View v, int mode) {
+        mStackSlider.setMode(mode);
+        if (v != null) {
+            mHighlight.setImageBitmap(sHolographicHelper.createOutline(v));
+            mHighlight.setRotation(v.getRotation());
+            mHighlight.setTranslationY(v.getTranslationY());
+            mHighlight.bringToFront();
+            v.bringToFront();
+            mStackSlider.setView(v);
+
+            v.setVisibility(VISIBLE);
+        }
+    }
+
+    @Override
+    @android.view.RemotableViewMethod
+    public void showNext() {
+        if (!mTransitionIsSetup) {
+            View v = getViewAtRelativeIndex(1);
+            if (v != null) {
+                setupStackSlider(v, StackSlider.NORMAL_MODE);
+                mStackSlider.setYProgress(0);
+                mStackSlider.setXProgress(0);
+            }
+        }
+        super.showNext();
+    }
+
+    @Override
+    @android.view.RemotableViewMethod
+    public void showPrevious() {
+        if (!mTransitionIsSetup) {
+            View v = getViewAtRelativeIndex(0);
+            if (v != null) {
+                setupStackSlider(v, StackSlider.NORMAL_MODE);
+                mStackSlider.setYProgress(1);
+                mStackSlider.setXProgress(0);
+            }
+        }
+        super.showPrevious();
+    }
+
+    private void transformViewAtIndex(int index, View view) {
+        float maxPerpectiveShift = mMeasuredHeight * PERSPECTIVE_SHIFT_FACTOR;
+
+        index = mMaxNumActiveViews - index - 1;
+        if (index == mMaxNumActiveViews - 1) index--;
+
+        float r = (index * 1.0f) / (mMaxNumActiveViews - 2);
+
+        float scale = 1 - PERSPECTIVE_SCALE_FACTOR * (1 - r);
+        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", scale);
+        PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", scale);
+
+        r = (float) Math.pow(r, 2);
+
+        int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1;
+        float perspectiveTranslation = -stackDirection * r * maxPerpectiveShift;
+        float scaleShiftCorrection = stackDirection * (1 - scale) *
+                (mMeasuredHeight * (1 - PERSPECTIVE_SHIFT_FACTOR) / 2.0f);
+        float transY = perspectiveTranslation + scaleShiftCorrection;
+
+        PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", transY);
+        ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(view, scaleX, scaleY, translationY);
+        pa.setDuration(100);
+        pa.start();
+    }
+
+    @Override
+    void showOnly(int childIndex, boolean animate, boolean onLayout) {
+        super.showOnly(childIndex, animate, onLayout);
+
+        // Here we need to make sure that the z-order of the children is correct
+        for (int i = mCurrentWindowEnd; i >= mCurrentWindowStart; i--) {
+            int index = modulo(i, getWindowSize());
+            View v = mViewsMap.get(index).view;
+            if (v != null) v.bringToFront();
+        }
+        mTransitionIsSetup = false;
+    }
+
+    private void updateChildTransforms() {
+        for (int i = 0; i < getNumActiveViews(); i++) {
+            View v = getViewAtRelativeIndex(i);
+            if (v != null) {
+                transformViewAtIndex(i, v);
+            }
+        }
+    }
+
+    @Override
+    FrameLayout getFrameForChild() {
+        FrameLayout fl = new FrameLayout(mContext);
+        fl.setPadding(mFramePadding, mFramePadding, mFramePadding, mFramePadding);
+        return fl;
+    }
+
+    /**
+     * Apply any necessary tranforms for the child that is being added.
+     */
+    void applyTransformForChildAtIndex(View child, int relativeIndex) {
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        canvas.getClipBounds(invalidateRect);
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
+            invalidateRect.union(lp.getInvalidateRect());
+            lp.resetInvalidateRect();
+        }
+
+        canvas.save(Canvas.CLIP_SAVE_FLAG);
+        canvas.clipRect(invalidateRect, Region.Op.UNION);
+        super.dispatchDraw(canvas);
+        canvas.restore();
+    }
+
+    private void onLayout() {
+        if (!mFirstLayoutHappened) {
+            mSlideAmount = Math.round(SLIDE_UP_RATIO * getMeasuredHeight());
+            updateChildTransforms();
+            mSwipeThreshold = Math.round(SWIPE_THRESHOLD_RATIO * mSlideAmount);
+            mFirstLayoutHappened = true;
+        }
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        int action = ev.getAction();
+        switch(action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN: {
+                if (mActivePointerId == INVALID_POINTER) {
+                    mInitialX = ev.getX();
+                    mInitialY = ev.getY();
+                    mActivePointerId = ev.getPointerId(0);
+                }
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: {
+                int pointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (pointerIndex == INVALID_POINTER) {
+                    // no data for our primary pointer, this shouldn't happen, log it
+                    Log.d(TAG, "Error: No data for our primary pointer.");
+                    return false;
+                }
+                float newY = ev.getY(pointerIndex);
+                float deltaY = newY - mInitialY;
+
+                beginGestureIfNeeded(deltaY);
+                break;
+            }
+            case MotionEvent.ACTION_POINTER_UP: {
+                onSecondaryPointerUp(ev);
+                break;
+            }
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL: {
+                mActivePointerId = INVALID_POINTER;
+                mSwipeGestureType = GESTURE_NONE;
+            }
+        }
+
+        return mSwipeGestureType != GESTURE_NONE;
+    }
+
+    private void beginGestureIfNeeded(float deltaY) {
+        if ((int) Math.abs(deltaY) > mTouchSlop && mSwipeGestureType == GESTURE_NONE) {
+            int swipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN;
+            cancelLongPress();
+            requestDisallowInterceptTouchEvent(true);
+
+            if (mAdapter == null) return;
+
+            int activeIndex;
+            if (mStackMode == ITEMS_SLIDE_UP) {
+                activeIndex = (swipeGestureType == GESTURE_SLIDE_DOWN) ? 0 : 1;
+            } else {
+                activeIndex = (swipeGestureType == GESTURE_SLIDE_DOWN) ? 1 : 0;
+            }
+
+            int stackMode;
+            if (mLoopViews) {
+                stackMode = StackSlider.NORMAL_MODE;
+            } else if (mCurrentWindowStartUnbounded + activeIndex == -1) {
+                activeIndex++;
+                stackMode = StackSlider.BEGINNING_OF_STACK_MODE;
+            } else if (mCurrentWindowStartUnbounded + activeIndex == mAdapter.getCount() - 1) {
+                stackMode = StackSlider.END_OF_STACK_MODE;
+            } else {
+                stackMode = StackSlider.NORMAL_MODE;
+            }
+
+            mTransitionIsSetup = stackMode == StackSlider.NORMAL_MODE;
+
+            View v = getViewAtRelativeIndex(activeIndex);
+            if (v == null) return;
+
+            setupStackSlider(v, stackMode);
+
+            // We only register this gesture if we've made it this far without a problem
+            mSwipeGestureType = swipeGestureType;
+            cancelHandleClick();
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        super.onTouchEvent(ev);
+
+        int action = ev.getAction();
+        int pointerIndex = ev.findPointerIndex(mActivePointerId);
+        if (pointerIndex == INVALID_POINTER) {
+            // no data for our primary pointer, this shouldn't happen, log it
+            Log.d(TAG, "Error: No data for our primary pointer.");
+            return false;
+        }
+
+        float newY = ev.getY(pointerIndex);
+        float newX = ev.getX(pointerIndex);
+        float deltaY = newY - mInitialY;
+        float deltaX = newX - mInitialX;
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(ev);
+
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_MOVE: {
+                beginGestureIfNeeded(deltaY);
+
+                float rx = deltaX / (mSlideAmount * 1.0f);
+                if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
+                    float r = (deltaY - mTouchSlop * 1.0f) / mSlideAmount * 1.0f;
+                    if (mStackMode == ITEMS_SLIDE_DOWN) r = 1 - r;
+                    mStackSlider.setYProgress(1 - r);
+                    mStackSlider.setXProgress(rx);
+                    return true;
+                } else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
+                    float r = -(deltaY + mTouchSlop * 1.0f) / mSlideAmount * 1.0f;
+                    if (mStackMode == ITEMS_SLIDE_DOWN) r = 1 - r;
+                    mStackSlider.setYProgress(r);
+                    mStackSlider.setXProgress(rx);
+                    return true;
+                }
+                break;
+            }
+            case MotionEvent.ACTION_UP: {
+                handlePointerUp(ev);
+                break;
+            }
+            case MotionEvent.ACTION_POINTER_UP: {
+                onSecondaryPointerUp(ev);
+                break;
+            }
+            case MotionEvent.ACTION_CANCEL: {
+                mActivePointerId = INVALID_POINTER;
+                mSwipeGestureType = GESTURE_NONE;
+                break;
+            }
+        }
+        return true;
+    }
+
+    private final Rect touchRect = new Rect();
+    private void onSecondaryPointerUp(MotionEvent ev) {
+        final int activePointerIndex = ev.getActionIndex();
+        final int pointerId = ev.getPointerId(activePointerIndex);
+        if (pointerId == mActivePointerId) {
+
+            int activeViewIndex = (mSwipeGestureType == GESTURE_SLIDE_DOWN) ? 0 : 1;
+
+            View v = getViewAtRelativeIndex(activeViewIndex);
+            if (v == null) return;
+
+            // Our primary pointer has gone up -- let's see if we can find
+            // another pointer on the view. If so, then we should replace
+            // our primary pointer with this new pointer and adjust things
+            // so that the view doesn't jump
+            for (int index = 0; index < ev.getPointerCount(); index++) {
+                if (index != activePointerIndex) {
+
+                    float x = ev.getX(index);
+                    float y = ev.getY(index);
+
+                    touchRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+                    if (touchRect.contains(Math.round(x), Math.round(y))) {
+                        float oldX = ev.getX(activePointerIndex);
+                        float oldY = ev.getY(activePointerIndex);
+
+                        // adjust our frame of reference to avoid a jump
+                        mInitialY += (y - oldY);
+                        mInitialX += (x - oldX);
+
+                        mActivePointerId = ev.getPointerId(index);
+                        if (mVelocityTracker != null) {
+                            mVelocityTracker.clear();
+                        }
+                        // ok, we're good, we found a new pointer which is touching the active view
+                        return;
+                    }
+                }
+            }
+            // if we made it this far, it means we didn't find a satisfactory new pointer :(,
+            // so end the gesture
+            handlePointerUp(ev);
+        }
+    }
+
+    private void handlePointerUp(MotionEvent ev) {
+        int pointerIndex = ev.findPointerIndex(mActivePointerId);
+        float newY = ev.getY(pointerIndex);
+        int deltaY = (int) (newY - mInitialY);
+
+        if (mVelocityTracker != null) {
+            mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+            mYVelocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
+        }
+
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+
+        if (deltaY > mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_DOWN
+                && mStackSlider.mMode == StackSlider.NORMAL_MODE) {
+            // Swipe threshold exceeded, swipe down
+            if (mStackMode == ITEMS_SLIDE_UP) {
+                showPrevious();
+            } else {
+                showNext();
+            }
+            mHighlight.bringToFront();
+        } else if (deltaY < -mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_UP
+                && mStackSlider.mMode == StackSlider.NORMAL_MODE) {
+            // Swipe threshold exceeded, swipe up
+            if (mStackMode == ITEMS_SLIDE_UP) {
+                showNext();
+            } else {
+                showPrevious();
+            }
+
+            mHighlight.bringToFront();
+        } else if (mSwipeGestureType == GESTURE_SLIDE_UP ) {
+            // Didn't swipe up far enough, snap back down
+            int duration;
+            float finalYProgress = (mStackMode == ITEMS_SLIDE_DOWN) ? 1 : 0;
+            if (mStackMode == ITEMS_SLIDE_UP || mStackSlider.mMode != StackSlider.NORMAL_MODE) {
+                duration = Math.round(mStackSlider.getDurationForNeutralPosition());
+            } else {
+                duration = Math.round(mStackSlider.getDurationForOffscreenPosition());
+            }
+
+            StackSlider animationSlider = new StackSlider(mStackSlider);
+            PropertyValuesHolder snapBackY = PropertyValuesHolder.ofFloat("YProgress", finalYProgress);
+            PropertyValuesHolder snapBackX = PropertyValuesHolder.ofFloat("XProgress", 0.0f);
+            ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(animationSlider,
+                    snapBackX, snapBackY);
+            pa.setDuration(duration);
+            pa.setInterpolator(new LinearInterpolator());
+            pa.start();
+        } else if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
+            // Didn't swipe down far enough, snap back up
+            float finalYProgress = (mStackMode == ITEMS_SLIDE_DOWN) ? 0 : 1;
+            int duration;
+            if (mStackMode == ITEMS_SLIDE_DOWN || mStackSlider.mMode != StackSlider.NORMAL_MODE) {
+                duration = Math.round(mStackSlider.getDurationForNeutralPosition());
+            } else {
+                duration = Math.round(mStackSlider.getDurationForOffscreenPosition());
+            }
+
+            StackSlider animationSlider = new StackSlider(mStackSlider);
+            PropertyValuesHolder snapBackY =
+                    PropertyValuesHolder.ofFloat("YProgress",finalYProgress);
+            PropertyValuesHolder snapBackX = PropertyValuesHolder.ofFloat("XProgress", 0.0f);
+            ObjectAnimator pa = ObjectAnimator.ofPropertyValuesHolder(animationSlider,
+                    snapBackX, snapBackY);
+            pa.setDuration(duration);
+            pa.start();
+        }
+
+        mActivePointerId = INVALID_POINTER;
+        mSwipeGestureType = GESTURE_NONE;
+    }
+
+    private class StackSlider {
+        View mView;
+        float mYProgress;
+        float mXProgress;
+
+        static final int NORMAL_MODE = 0;
+        static final int BEGINNING_OF_STACK_MODE = 1;
+        static final int END_OF_STACK_MODE = 2;
+
+        int mMode = NORMAL_MODE;
+
+        public StackSlider() {
+        }
+
+        public StackSlider(StackSlider copy) {
+            mView = copy.mView;
+            mYProgress = copy.mYProgress;
+            mXProgress = copy.mXProgress;
+            mMode = copy.mMode;
+        }
+
+        private float cubic(float r) {
+            return (float) (Math.pow(2 * r - 1, 3) + 1) / 2.0f;
+        }
+
+        private float highlightAlphaInterpolator(float r) {
+            float pivot = 0.4f;
+            if (r < pivot) {
+                return 0.85f * cubic(r / pivot);
+            } else {
+                return 0.85f * cubic(1 - (r - pivot) / (1 - pivot));
+            }
+        }
+
+        private float viewAlphaInterpolator(float r) {
+            float pivot = 0.3f;
+            if (r > pivot) {
+                return (r - pivot) / (1 - pivot);
+            } else {
+                return 0;
+            }
+        }
+
+        private float rotationInterpolator(float r) {
+            float pivot = 0.2f;
+            if (r < pivot) {
+                return 0;
+            } else {
+                return (r - pivot) / (1 - pivot);
+            }
+        }
+
+        void setView(View v) {
+            mView = v;
+        }
+
+        public void setYProgress(float r) {
+            // enforce r between 0 and 1
+            r = Math.min(1.0f, r);
+            r = Math.max(0, r);
+
+            mYProgress = r;
+            final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
+            final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();
+
+            int stackDirection = (mStackMode == ITEMS_SLIDE_UP) ? 1 : -1;
+
+            switch (mMode) {
+                case NORMAL_MODE:
+                    viewLp.setVerticalOffset(Math.round(-r * stackDirection * mSlideAmount));
+                    highlightLp.setVerticalOffset(Math.round(-r * stackDirection * mSlideAmount));
+                    mHighlight.setAlpha(highlightAlphaInterpolator(r));
+
+                    float alpha = viewAlphaInterpolator(1 - r);
+
+                    // We make sure that views which can't be seen (have 0 alpha) are also invisible
+                    // so that they don't interfere with click events.
+                    if (mView.getAlpha() == 0 && alpha != 0 && mView.getVisibility() != VISIBLE) {
+                        mView.setVisibility(VISIBLE);
+                    } else if (alpha == 0 && mView.getAlpha() != 0
+                            && mView.getVisibility() == VISIBLE) {
+                        mView.setVisibility(INVISIBLE);
+                    }
+
+                    mView.setAlpha(alpha);
+                    mView.setRotationX(stackDirection * 90.0f * rotationInterpolator(r));
+                    mHighlight.setRotationX(stackDirection * 90.0f * rotationInterpolator(r));
+                    break;
+                case END_OF_STACK_MODE:
+                    r = r * 0.2f;
+                    viewLp.setVerticalOffset(Math.round(-stackDirection * r * mSlideAmount));
+                    highlightLp.setVerticalOffset(Math.round(-stackDirection * r * mSlideAmount));
+                    mHighlight.setAlpha(highlightAlphaInterpolator(r));
+                    break;
+                case BEGINNING_OF_STACK_MODE:
+                    r = (1-r) * 0.2f;
+                    viewLp.setVerticalOffset(Math.round(stackDirection * r * mSlideAmount));
+                    highlightLp.setVerticalOffset(Math.round(stackDirection * r * mSlideAmount));
+                    mHighlight.setAlpha(highlightAlphaInterpolator(r));
+                    break;
+            }
+        }
+
+        public void setXProgress(float r) {
+            // enforce r between 0 and 1
+            r = Math.min(2.0f, r);
+            r = Math.max(-2.0f, r);
+
+            mXProgress = r;
+
+            final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
+            final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();
+
+            r *= 0.2f;
+            viewLp.setHorizontalOffset(Math.round(r * mSlideAmount));
+            highlightLp.setHorizontalOffset(Math.round(r * mSlideAmount));
+        }
+
+        void setMode(int mode) {
+            mMode = mode;
+        }
+
+        float getDurationForNeutralPosition() {
+            return getDuration(false, 0);
+        }
+
+        float getDurationForOffscreenPosition() {
+            return getDuration(true, 0);
+        }
+
+        float getDurationForNeutralPosition(float velocity) {
+            return getDuration(false, velocity);
+        }
+
+        float getDurationForOffscreenPosition(float velocity) {
+            return getDuration(true, velocity);
+        }
+
+        private float getDuration(boolean invert, float velocity) {
+            if (mView != null) {
+                final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
+
+                float d = (float) Math.sqrt(Math.pow(viewLp.horizontalOffset, 2) +
+                        Math.pow(viewLp.verticalOffset, 2));
+                float maxd = (float) Math.sqrt(Math.pow(mSlideAmount, 2) +
+                        Math.pow(0.4f * mSlideAmount, 2));
+
+                if (velocity == 0) {
+                    return (invert ? (1 - d / maxd) : d / maxd) * DEFAULT_ANIMATION_DURATION;
+                } else {
+                    float duration = invert ? d / Math.abs(velocity) :
+                            (maxd - d) / Math.abs(velocity);
+                    if (duration < MINIMUM_ANIMATION_DURATION ||
+                            duration > DEFAULT_ANIMATION_DURATION) {
+                        return getDuration(invert, 0);
+                    } else {
+                        return duration;
+                    }
+                }
+            }
+            return 0;
+        }
+
+        // Used for animations
+        @SuppressWarnings({"UnusedDeclaration"})
+        public float getYProgress() {
+            return mYProgress;
+        }
+
+        // Used for animations
+        @SuppressWarnings({"UnusedDeclaration"})
+        public float getXProgress() {
+            return mXProgress;
+        }
+    }
+
+    @Override
+    public void onRemoteAdapterConnected() {
+        super.onRemoteAdapterConnected();
+        // On first run, we want to set the stack to the end.
+        if (mWhichChild == -1) {
+            mWhichChild = 0;
+        }
+        setDisplayedChild(mWhichChild);
+    }
+
+    LayoutParams createOrReuseLayoutParams(View v) {
+        final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
+        if (currentLp instanceof LayoutParams) {
+            LayoutParams lp = (LayoutParams) currentLp;
+            lp.setHorizontalOffset(0);
+            lp.setVerticalOffset(0);
+            lp.width = 0;
+            lp.width = 0;
+            return lp;
+        }
+        return new LayoutParams(v);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        boolean dataChanged = mDataChanged;
+        if (dataChanged) {
+            handleDataChanged();
+
+            // if the data changes, mWhichChild might be out of the bounds of the adapter
+            // in this case, we reset mWhichChild to the beginning
+            if (mWhichChild >= mAdapter.getCount())
+                mWhichChild = 0;
+
+            showOnly(mWhichChild, true, true);
+            refreshChildren();
+        }
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+
+            int childRight = mPaddingLeft + child.getMeasuredWidth();
+            int childBottom = mPaddingTop + child.getMeasuredHeight();
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+            child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset,
+                    childRight + lp.horizontalOffset, childBottom + lp.verticalOffset);
+
+        }
+
+        mDataChanged = false;
+        onLayout();
+    }
+
+    private void measureChildren() {
+        final int count = getChildCount();
+        final int childWidth = mMeasuredWidth - mPaddingLeft - mPaddingRight;
+        final int childHeight = Math.round(mMeasuredHeight*(1-PERSPECTIVE_SHIFT_FACTOR))
+                - mPaddingTop - mPaddingBottom;
+
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+        final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+
+        boolean haveChildRefSize = (mReferenceChildWidth != -1 && mReferenceChildHeight != -1);
+
+        // We need to deal with the case where our parent hasn't told us how
+        // big we should be. In this case we should
+        float factor = 1/(1 - PERSPECTIVE_SHIFT_FACTOR);
+        if (heightSpecMode == MeasureSpec.UNSPECIFIED) {
+            heightSpecSize = haveChildRefSize ?
+                    Math.round(mReferenceChildHeight * (1 + factor)) +
+                    mPaddingTop + mPaddingBottom : 0;
+        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+            heightSpecSize = haveChildRefSize ? Math.min(
+                    Math.round(mReferenceChildHeight * (1 + factor)) + mPaddingTop +
+                    mPaddingBottom, heightSpecSize) : 0;
+        }
+
+        if (widthSpecMode == MeasureSpec.UNSPECIFIED) {
+            widthSpecSize = haveChildRefSize ? mReferenceChildWidth + mPaddingLeft +
+                    mPaddingRight : 0;
+        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+            widthSpecSize = haveChildRefSize ? Math.min(mReferenceChildWidth + mPaddingLeft +
+                    mPaddingRight, widthSpecSize) : 0;
+        }
+
+        setMeasuredDimension(widthSpecSize, heightSpecSize);
+        measureChildren();
+    }
+
+    class LayoutParams extends ViewGroup.LayoutParams {
+        int horizontalOffset;
+        int verticalOffset;
+        View mView;
+        int left, top, right, bottom;
+        private final Rect parentRect = new Rect();
+        private final Rect invalidateRect = new Rect();
+        private final RectF invalidateRectf = new RectF();
+        private final Rect globalInvalidateRect = new Rect();
+
+        LayoutParams(View view) {
+            super(0, 0);
+            width = 0;
+            height = 0;
+            horizontalOffset = 0;
+            verticalOffset = 0;
+            mView = view;
+        }
+
+        LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            horizontalOffset = 0;
+            verticalOffset = 0;
+            width = 0;
+            height = 0;
+        }
+
+        void invalidateGlobalRegion(View v, Rect r) {
+            // We need to make a new rect here, so as not to modify the one passed
+            globalInvalidateRect.set(r);
+            View p = v;
+            if (!(v.getParent() != null && v.getParent() instanceof View)) return;
+
+            boolean firstPass = true;
+            parentRect.set(0, 0, 0, 0);
+            int depth = 0;
+            while (p.getParent() != null && p.getParent() instanceof View
+                    && !parentRect.contains(globalInvalidateRect)) {
+                if (!firstPass) {
+                    globalInvalidateRect.offset(p.getLeft() - p.getScrollX(), p.getTop()
+                            - p.getScrollY());
+                    depth++;
+                }
+                firstPass = false;
+                p = (View) p.getParent();
+                parentRect.set(p.getScrollX(), p.getScrollY(),
+                               p.getWidth() + p.getScrollX(), p.getHeight() + p.getScrollY());
+
+            }
+
+            p.invalidate(globalInvalidateRect.left, globalInvalidateRect.top,
+                    globalInvalidateRect.right, globalInvalidateRect.bottom);
+        }
+
+        Rect getInvalidateRect() {
+            return invalidateRect;
+        }
+
+        void resetInvalidateRect() {
+           invalidateRect.set(0, 0, 0, 0);
+        }
+
+        // This is public so that ObjectAnimator can access it
+        public void setVerticalOffset(int newVerticalOffset) {
+            int offsetDelta = newVerticalOffset - verticalOffset;
+            verticalOffset = newVerticalOffset;
+
+            if (mView != null) {
+                mView.requestLayout();
+                int top = Math.min(mView.getTop() + offsetDelta, mView.getTop());
+                int bottom = Math.max(mView.getBottom() + offsetDelta, mView.getBottom());
+
+                invalidateRectf.set(mView.getLeft(), top, mView.getRight(), bottom);
+
+                float xoffset = -invalidateRectf.left;
+                float yoffset = -invalidateRectf.top;
+                invalidateRectf.offset(xoffset, yoffset);
+                mView.getMatrix().mapRect(invalidateRectf);
+                invalidateRectf.offset(-xoffset, -yoffset);
+                invalidateRect.union((int) Math.floor(invalidateRectf.left),
+                        (int) Math.floor(invalidateRectf.top),
+                        (int) Math.ceil(invalidateRectf.right),
+                        (int) Math.ceil(invalidateRectf.bottom));
+
+                invalidateGlobalRegion(mView, invalidateRect);
+            }
+        }
+
+        public void setHorizontalOffset(int newHorizontalOffset) {
+            int offsetDelta = newHorizontalOffset - horizontalOffset;
+            horizontalOffset = newHorizontalOffset;
+
+            if (mView != null) {
+                mView.requestLayout();
+                int left = Math.min(mView.getLeft() + offsetDelta, mView.getLeft());
+                int right = Math.max(mView.getRight() + offsetDelta, mView.getRight());
+                invalidateRectf.set(left,  mView.getTop(), right, mView.getBottom());
+
+                float xoffset = -invalidateRectf.left;
+                float yoffset = -invalidateRectf.top;
+                invalidateRectf.offset(xoffset, yoffset);
+                mView.getMatrix().mapRect(invalidateRectf);
+                invalidateRectf.offset(-xoffset, -yoffset);
+
+                invalidateRect.union((int) Math.floor(invalidateRectf.left),
+                        (int) Math.floor(invalidateRectf.top),
+                        (int) Math.ceil(invalidateRectf.right),
+                        (int) Math.ceil(invalidateRectf.bottom));
+
+                invalidateGlobalRegion(mView, invalidateRect);
+            }
+        }
+    }
+
+    private static class HolographicHelper {
+        private final Paint mHolographicPaint = new Paint();
+        private final Paint mErasePaint = new Paint();
+        private final Paint mBlurPaint = new Paint();
+
+        HolographicHelper(Context context) {
+            initializePaints(context);
+        }
+
+        void initializePaints(Context context) {
+            final float density = context.getResources().getDisplayMetrics().density;
+
+            mHolographicPaint.setColor(0xff6699ff);
+            mHolographicPaint.setFilterBitmap(true);
+            mHolographicPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
+            mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+            mErasePaint.setFilterBitmap(true);
+            mBlurPaint.setMaskFilter(new BlurMaskFilter(2*density, BlurMaskFilter.Blur.NORMAL));
+        }
+
+        Bitmap createOutline(View v) {
+            if (v.getMeasuredWidth() == 0 || v.getMeasuredHeight() == 0) {
+                return null;
+            }
+
+            Bitmap bitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
+                    Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+
+            float rotationX = v.getRotationX();
+            float rotation = v.getRotation();
+            float translationY = v.getTranslationY();
+            v.setRotationX(0);
+            v.setRotation(0);
+            v.setTranslationY(0);
+            v.draw(canvas);
+            v.setRotationX(rotationX);
+            v.setRotation(rotation);
+            v.setTranslationY(translationY);
+
+            drawOutline(canvas, bitmap);
+            return bitmap;
+        }
+
+        final Matrix id = new Matrix();
+        void drawOutline(Canvas dest, Bitmap src) {
+            int[] xy = new int[2];
+            Bitmap mask = src.extractAlpha(mBlurPaint, xy);
+            Canvas maskCanvas = new Canvas(mask);
+            maskCanvas.drawBitmap(src, -xy[0], -xy[1], mErasePaint);
+            dest.drawColor(0, PorterDuff.Mode.CLEAR);
+            dest.setMatrix(id);
+            dest.drawBitmap(mask, xy[0], xy[1], mHolographicPaint);
+            mask.recycle();
+        }
+    }
+}
diff --git a/core/java/android/widget/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java
new file mode 100644
index 0000000..1ebe622
--- /dev/null
+++ b/core/java/android/widget/SuggestionsAdapter.java
@@ -0,0 +1,702 @@
+/*
+ * 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.widget;
+
+import com.android.internal.R;
+
+import android.app.SearchDialog;
+import android.app.SearchManager;
+import android.app.SearchableInfo;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContentResolver.OpenResourceIdResult;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.StateListDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextUtils;
+import android.text.style.TextAppearanceSpan;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.WeakHashMap;
+
+/**
+ * Provides the contents for the suggestion drop-down list.in {@link SearchDialog}.
+ *
+ * @hide
+ */
+class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListener {
+
+    private static final boolean DBG = false;
+    private static final String LOG_TAG = "SuggestionsAdapter";
+    private static final int QUERY_LIMIT = 50;
+
+    static final int REFINE_NONE = 0;
+    static final int REFINE_BY_ENTRY = 1;
+    static final int REFINE_ALL = 2;
+
+    private SearchManager mSearchManager;
+    private SearchView mSearchView;
+    private SearchableInfo mSearchable;
+    private Context mProviderContext;
+    private WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache;
+    private boolean mClosed = false;
+    private int mQueryRefinement = REFINE_BY_ENTRY;
+
+    // URL color
+    private ColorStateList mUrlColor;
+
+    // Cached column indexes, updated when the cursor changes.
+    private int mText1Col;
+    private int mText2Col;
+    private int mText2UrlCol;
+    private int mIconName1Col;
+    private int mIconName2Col;
+    private int mFlagsCol;
+
+    static final int NONE = -1;
+
+    private final Runnable mStartSpinnerRunnable;
+    private final Runnable mStopSpinnerRunnable;
+
+    /**
+     * The amount of time we delay in the filter when the user presses the delete key.
+     * @see Filter#setDelayer(android.widget.Filter.Delayer).
+     */
+    private static final long DELETE_KEY_POST_DELAY = 500L;
+
+    public SuggestionsAdapter(Context context, SearchView searchView,
+            SearchableInfo searchable,
+            WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache) {
+        super(context,
+                com.android.internal.R.layout.search_dropdown_item_icons_2line,
+                null,   // no initial cursor
+                true);  // auto-requery
+        mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
+        mSearchView = searchView;
+        mSearchable = searchable;
+        // set up provider resources (gives us icons, etc.)
+        Context activityContext = mSearchable.getActivityContext(mContext);
+        mProviderContext = mSearchable.getProviderContext(mContext, activityContext);
+
+        mOutsideDrawablesCache = outsideDrawablesCache;
+        
+        mStartSpinnerRunnable = new Runnable() {
+                public void run() {
+                // mSearchView.setWorking(true); // TODO:
+                }
+            };
+
+        mStopSpinnerRunnable = new Runnable() {
+            public void run() {
+                // mSearchView.setWorking(false); // TODO:
+            }
+        };
+
+        // delay 500ms when deleting
+        getFilter().setDelayer(new Filter.Delayer() {
+
+            private int mPreviousLength = 0;
+
+            public long getPostingDelay(CharSequence constraint) {
+                if (constraint == null) return 0;
+
+                long delay = constraint.length() < mPreviousLength ? DELETE_KEY_POST_DELAY : 0;
+                mPreviousLength = constraint.length();
+                return delay;
+            }
+        });
+    }
+
+    /**
+     * Enables query refinement for all suggestions. This means that an additional icon
+     * will be shown for each entry. When clicked, the suggested text on that line will be
+     * copied to the query text field.
+     * <p>
+     *
+     * @param refine which queries to refine. Possible values are {@link #REFINE_NONE},
+     * {@link #REFINE_BY_ENTRY}, and {@link #REFINE_ALL}.
+     */
+    public void setQueryRefinement(int refineWhat) {
+        mQueryRefinement = refineWhat;
+    }
+
+    /**
+     * Returns the current query refinement preference.
+     * @return value of query refinement preference
+     */
+    public int getQueryRefinement() {
+        return mQueryRefinement;
+    }
+
+    /**
+     * Overridden to always return <code>false</code>, since we cannot be sure that
+     * suggestion sources return stable IDs.
+     */
+    @Override
+    public boolean hasStableIds() {
+        return false;
+    }
+
+    /**
+     * Use the search suggestions provider to obtain a live cursor.  This will be called
+     * in a worker thread, so it's OK if the query is slow (e.g. round trip for suggestions).
+     * The results will be processed in the UI thread and changeCursor() will be called.
+     */
+    @Override
+    public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
+        if (DBG) Log.d(LOG_TAG, "runQueryOnBackgroundThread(" + constraint + ")");
+        String query = (constraint == null) ? "" : constraint.toString();
+        /**
+         * for in app search we show the progress spinner until the cursor is returned with
+         * the results.
+         */
+        Cursor cursor = null;
+        //mSearchView.getWindow().getDecorView().post(mStartSpinnerRunnable); // TODO:
+        try {
+            cursor = mSearchManager.getSuggestions(mSearchable, query, QUERY_LIMIT);
+            // trigger fill window so the spinner stays up until the results are copied over and
+            // closer to being ready
+            if (cursor != null) {
+                cursor.getCount();
+                return cursor;
+            }
+        } catch (RuntimeException e) {
+            Log.w(LOG_TAG, "Search suggestions query threw an exception.", e);
+        }
+        // If cursor is null or an exception was thrown, stop the spinner and return null.
+        // changeCursor doesn't get called if cursor is null
+        // mSearchView.getWindow().getDecorView().post(mStopSpinnerRunnable); // TODO:
+        return null;
+    }
+
+    public void close() {
+        if (DBG) Log.d(LOG_TAG, "close()");
+        changeCursor(null);
+        mClosed = true;
+    }
+
+    @Override
+    public void notifyDataSetChanged() {
+        if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged");
+        super.notifyDataSetChanged();
+
+        // mSearchView.onDataSetChanged(); // TODO:
+
+        updateSpinnerState(getCursor());
+    }
+
+    @Override
+    public void notifyDataSetInvalidated() {
+        if (DBG) Log.d(LOG_TAG, "notifyDataSetInvalidated");
+        super.notifyDataSetInvalidated();
+
+        updateSpinnerState(getCursor());
+    }
+
+    private void updateSpinnerState(Cursor cursor) {
+        Bundle extras = cursor != null ? cursor.getExtras() : null;
+        if (DBG) {
+            Log.d(LOG_TAG, "updateSpinnerState - extra = "
+                + (extras != null
+                        ? extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)
+                        : null));
+        }
+        // Check if the Cursor indicates that the query is not complete and show the spinner
+        if (extras != null
+                && extras.getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)) {
+            // mSearchView.getWindow().getDecorView().post(mStartSpinnerRunnable); // TODO:
+            return;
+        }
+        // If cursor is null or is done, stop the spinner
+        // mSearchView.getWindow().getDecorView().post(mStopSpinnerRunnable); // TODO:
+    }
+
+    /**
+     * Cache columns.
+     */
+    @Override
+    public void changeCursor(Cursor c) {
+        if (DBG) Log.d(LOG_TAG, "changeCursor(" + c + ")");
+
+        if (mClosed) {
+            Log.w(LOG_TAG, "Tried to change cursor after adapter was closed.");
+            if (c != null) c.close();
+            return;
+        }
+
+        try {
+            super.changeCursor(c);
+
+            if (c != null) {
+                mText1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
+                mText2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2);
+                mText2UrlCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
+                mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
+                mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
+                mFlagsCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_FLAGS);
+            }
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "error changing cursor and caching columns", e);
+        }
+    }
+
+    /**
+     * Tags the view with cached child view look-ups.
+     */
+    @Override
+    public View newView(Context context, Cursor cursor, ViewGroup parent) {
+        View v = super.newView(context, cursor, parent);
+        v.setTag(new ChildViewCache(v));
+        return v;
+    }
+
+    /**
+     * Cache of the child views of drop-drown list items, to avoid looking up the children
+     * each time the contents of a list item are changed.
+     */
+    private final static class ChildViewCache {
+        public final TextView mText1;
+        public final TextView mText2;
+        public final ImageView mIcon1;
+        public final ImageView mIcon2;
+        public final ImageView mIconRefine;
+
+        public ChildViewCache(View v) {
+            mText1 = (TextView) v.findViewById(com.android.internal.R.id.text1);
+            mText2 = (TextView) v.findViewById(com.android.internal.R.id.text2);
+            mIcon1 = (ImageView) v.findViewById(com.android.internal.R.id.icon1);
+            mIcon2 = (ImageView) v.findViewById(com.android.internal.R.id.icon2);
+            mIconRefine = (ImageView) v.findViewById(com.android.internal.R.id.edit_query);
+        }
+    }
+
+    @Override
+    public void bindView(View view, Context context, Cursor cursor) {
+        ChildViewCache views = (ChildViewCache) view.getTag();
+
+        int flags = 0;
+        if (mFlagsCol != -1) {
+            flags = cursor.getInt(mFlagsCol);
+        }
+        if (views.mText1 != null) {
+            String text1 = getStringOrNull(cursor, mText1Col);
+            setViewText(views.mText1, text1);
+        }
+        if (views.mText2 != null) {
+            // First check TEXT_2_URL
+            CharSequence text2 = getStringOrNull(cursor, mText2UrlCol);
+            if (text2 != null) {
+                text2 = formatUrl(text2);
+            } else {
+                text2 = getStringOrNull(cursor, mText2Col);
+            }
+
+            // If no second line of text is indicated, allow the first line of text
+            // to be up to two lines if it wants to be.
+            if (TextUtils.isEmpty(text2)) {
+                if (views.mText1 != null) {
+                    views.mText1.setSingleLine(false);
+                    views.mText1.setMaxLines(2);
+                }
+            } else {
+                if (views.mText1 != null) {
+                    views.mText1.setSingleLine(true);
+                    views.mText1.setMaxLines(1);
+                }
+            }
+            setViewText(views.mText2, text2);
+        }
+
+        if (views.mIcon1 != null) {
+            setViewDrawable(views.mIcon1, getIcon1(cursor));
+        }
+        if (views.mIcon2 != null) {
+            setViewDrawable(views.mIcon2, getIcon2(cursor));
+        }
+        if (mQueryRefinement == REFINE_ALL
+                || (mQueryRefinement == REFINE_BY_ENTRY
+                        && (flags & SearchManager.FLAG_QUERY_REFINEMENT) != 0)) {
+            views.mIconRefine.setVisibility(View.VISIBLE);
+            views.mIconRefine.setTag(views.mText1.getText());
+            views.mIconRefine.setOnClickListener(this);
+        } else {
+            views.mIconRefine.setVisibility(View.GONE);
+        }
+    }
+
+    public void onClick(View v) {
+        Object tag = v.getTag();
+        if (tag instanceof CharSequence) {
+            mSearchView.onQueryRefine((CharSequence) tag);
+        }
+    }
+
+    private CharSequence formatUrl(CharSequence url) {
+        if (mUrlColor == null) {
+            // Lazily get the URL color from the current theme.
+            TypedValue colorValue = new TypedValue();
+            mContext.getTheme().resolveAttribute(R.attr.textColorSearchUrl, colorValue, true);
+            mUrlColor = mContext.getResources().getColorStateList(colorValue.resourceId);
+        }
+
+        SpannableString text = new SpannableString(url);
+        text.setSpan(new TextAppearanceSpan(null, 0, 0, mUrlColor, null),
+                0, url.length(),
+                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        return text;
+    }
+
+    private void setViewText(TextView v, CharSequence text) {
+        // Set the text even if it's null, since we need to clear any previous text.
+        v.setText(text);
+
+        if (TextUtils.isEmpty(text)) {
+            v.setVisibility(View.GONE);
+        } else {
+            v.setVisibility(View.VISIBLE);
+        }
+    }
+
+    private Drawable getIcon1(Cursor cursor) {
+        if (mIconName1Col < 0) {
+            return null;
+        }
+        String value = cursor.getString(mIconName1Col);
+        Drawable drawable = getDrawableFromResourceValue(value);
+        if (drawable != null) {
+            return drawable;
+        }
+        return getDefaultIcon1(cursor);
+    }
+
+    private Drawable getIcon2(Cursor cursor) {
+        if (mIconName2Col < 0) {
+            return null;
+        }
+        String value = cursor.getString(mIconName2Col);
+        return getDrawableFromResourceValue(value);
+    }
+
+    /**
+     * Sets the drawable in an image view, makes sure the view is only visible if there
+     * is a drawable.
+     */
+    private void setViewDrawable(ImageView v, Drawable drawable) {
+        // Set the icon even if the drawable is null, since we need to clear any
+        // previous icon.
+        v.setImageDrawable(drawable);
+
+        if (drawable == null) {
+            v.setVisibility(View.GONE);
+        } else {
+            v.setVisibility(View.VISIBLE);
+
+            // This is a hack to get any animated drawables (like a 'working' spinner)
+            // to animate. You have to setVisible true on an AnimationDrawable to get
+            // it to start animating, but it must first have been false or else the
+            // call to setVisible will be ineffective. We need to clear up the story
+            // about animated drawables in the future, see http://b/1878430.
+            drawable.setVisible(false, false);
+            drawable.setVisible(true, false);
+        }
+    }
+
+    /**
+     * Gets the text to show in the query field when a suggestion is selected.
+     *
+     * @param cursor The Cursor to read the suggestion data from. The Cursor should already
+     *        be moved to the suggestion that is to be read from.
+     * @return The text to show, or <code>null</code> if the query should not be
+     *         changed when selecting this suggestion.
+     */
+    @Override
+    public CharSequence convertToString(Cursor cursor) {
+        if (cursor == null) {
+            return null;
+        }
+
+        String query = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_QUERY);
+        if (query != null) {
+            return query;
+        }
+
+        if (mSearchable.shouldRewriteQueryFromData()) {
+            String data = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
+            if (data != null) {
+                return data;
+            }
+        }
+
+        if (mSearchable.shouldRewriteQueryFromText()) {
+            String text1 = getColumnString(cursor, SearchManager.SUGGEST_COLUMN_TEXT_1);
+            if (text1 != null) {
+                return text1;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * This method is overridden purely to provide a bit of protection against
+     * flaky content providers.
+     *
+     * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
+     */
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        try {
+            return super.getView(position, convertView, parent);
+        } catch (RuntimeException e) {
+            Log.w(LOG_TAG, "Search suggestions cursor threw exception.", e);
+            // Put exception string in item title
+            View v = newView(mContext, mCursor, parent);
+            if (v != null) {
+                ChildViewCache views = (ChildViewCache) v.getTag();
+                TextView tv = views.mText1;
+                tv.setText(e.toString());
+            }
+            return v;
+        }
+    }
+
+    /**
+     * Gets a drawable given a value provided by a suggestion provider.
+     *
+     * This value could be just the string value of a resource id
+     * (e.g., "2130837524"), in which case we will try to retrieve a drawable from
+     * the provider's resources. If the value is not an integer, it is
+     * treated as a Uri and opened with
+     * {@link ContentResolver#openOutputStream(android.net.Uri, String)}.
+     *
+     * All resources and URIs are read using the suggestion provider's context.
+     *
+     * If the string is not formatted as expected, or no drawable can be found for
+     * the provided value, this method returns null.
+     *
+     * @param drawableId a string like "2130837524",
+     *        "android.resource://com.android.alarmclock/2130837524",
+     *        or "content://contacts/photos/253".
+     * @return a Drawable, or null if none found
+     */
+    private Drawable getDrawableFromResourceValue(String drawableId) {
+        if (drawableId == null || drawableId.length() == 0 || "0".equals(drawableId)) {
+            return null;
+        }
+        try {
+            // First, see if it's just an integer
+            int resourceId = Integer.parseInt(drawableId);
+            // It's an int, look for it in the cache
+            String drawableUri = ContentResolver.SCHEME_ANDROID_RESOURCE
+                    + "://" + mProviderContext.getPackageName() + "/" + resourceId;
+            // Must use URI as cache key, since ints are app-specific
+            Drawable drawable = checkIconCache(drawableUri);
+            if (drawable != null) {
+                return drawable;
+            }
+            // Not cached, find it by resource ID
+            drawable = mProviderContext.getResources().getDrawable(resourceId);
+            // Stick it in the cache, using the URI as key
+            storeInIconCache(drawableUri, drawable);
+            return drawable;
+        } catch (NumberFormatException nfe) {
+            // It's not an integer, use it as a URI
+            Drawable drawable = checkIconCache(drawableId);
+            if (drawable != null) {
+                return drawable;
+            }
+            Uri uri = Uri.parse(drawableId);
+            drawable = getDrawable(uri);
+            storeInIconCache(drawableId, drawable);
+            return drawable;
+        } catch (Resources.NotFoundException nfe) {
+            // It was an integer, but it couldn't be found, bail out
+            Log.w(LOG_TAG, "Icon resource not found: " + drawableId);
+            return null;
+        }
+    }
+
+    /**
+     * Gets a drawable by URI, without using the cache.
+     *
+     * @return A drawable, or {@code null} if the drawable could not be loaded.
+     */
+    private Drawable getDrawable(Uri uri) {
+        try {
+            String scheme = uri.getScheme();
+            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
+                // Load drawables through Resources, to get the source density information
+                OpenResourceIdResult r =
+                    mProviderContext.getContentResolver().getResourceId(uri);
+                try {
+                    return r.r.getDrawable(r.id);
+                } catch (Resources.NotFoundException ex) {
+                    throw new FileNotFoundException("Resource does not exist: " + uri);
+                }
+            } else {
+                // Let the ContentResolver handle content and file URIs.
+                InputStream stream = mProviderContext.getContentResolver().openInputStream(uri);
+                if (stream == null) {
+                    throw new FileNotFoundException("Failed to open " + uri);
+                }
+                try {
+                    return Drawable.createFromStream(stream, null);
+                } finally {
+                    try {
+                        stream.close();
+                    } catch (IOException ex) {
+                        Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex);
+                    }
+                }
+            }
+        } catch (FileNotFoundException fnfe) {
+            Log.w(LOG_TAG, "Icon not found: " + uri + ", " + fnfe.getMessage());
+            return null;
+        }
+    }
+
+    private Drawable checkIconCache(String resourceUri) {
+        Drawable.ConstantState cached = mOutsideDrawablesCache.get(resourceUri);
+        if (cached == null) {
+            return null;
+        }
+        if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + resourceUri);
+        return cached.newDrawable();
+    }
+
+    private void storeInIconCache(String resourceUri, Drawable drawable) {
+        if (drawable != null) {
+            mOutsideDrawablesCache.put(resourceUri, drawable.getConstantState());
+        }
+    }
+
+    /**
+     * Gets the left-hand side icon that will be used for the current suggestion
+     * if the suggestion contains an icon column but no icon or a broken icon.
+     *
+     * @param cursor A cursor positioned at the current suggestion.
+     * @return A non-null drawable.
+     */
+    private Drawable getDefaultIcon1(Cursor cursor) {
+        // Check the component that gave us the suggestion
+        Drawable drawable = getActivityIconWithCache(mSearchable.getSearchActivity());
+        if (drawable != null) {
+            return drawable;
+        }
+
+        // Fall back to a default icon
+        return mContext.getPackageManager().getDefaultActivityIcon();
+    }
+
+    /**
+     * Gets the activity or application icon for an activity.
+     * Uses the local icon cache for fast repeated lookups.
+     *
+     * @param component Name of an activity.
+     * @return A drawable, or {@code null} if neither the activity nor the application
+     *         has an icon set.
+     */
+    private Drawable getActivityIconWithCache(ComponentName component) {
+        // First check the icon cache
+        String componentIconKey = component.flattenToShortString();
+        // Using containsKey() since we also store null values.
+        if (mOutsideDrawablesCache.containsKey(componentIconKey)) {
+            Drawable.ConstantState cached = mOutsideDrawablesCache.get(componentIconKey);
+            return cached == null ? null : cached.newDrawable(mProviderContext.getResources());
+        }
+        // Then try the activity or application icon
+        Drawable drawable = getActivityIcon(component);
+        // Stick it in the cache so we don't do this lookup again.
+        Drawable.ConstantState toCache = drawable == null ? null : drawable.getConstantState();
+        mOutsideDrawablesCache.put(componentIconKey, toCache);
+        return drawable;
+    }
+
+    /**
+     * Gets the activity or application icon for an activity.
+     *
+     * @param component Name of an activity.
+     * @return A drawable, or {@code null} if neither the acitivy or the application
+     *         have an icon set.
+     */
+    private Drawable getActivityIcon(ComponentName component) {
+        PackageManager pm = mContext.getPackageManager();
+        final ActivityInfo activityInfo;
+        try {
+            activityInfo = pm.getActivityInfo(component, PackageManager.GET_META_DATA);
+        } catch (NameNotFoundException ex) {
+            Log.w(LOG_TAG, ex.toString());
+            return null;
+        }
+        int iconId = activityInfo.getIconResource();
+        if (iconId == 0) return null;
+        String pkg = component.getPackageName();
+        Drawable drawable = pm.getDrawable(pkg, iconId, activityInfo.applicationInfo);
+        if (drawable == null) {
+            Log.w(LOG_TAG, "Invalid icon resource " + iconId + " for "
+                    + component.flattenToShortString());
+            return null;
+        }
+        return drawable;
+    }
+
+    /**
+     * Gets the value of a string column by name.
+     *
+     * @param cursor Cursor to read the value from.
+     * @param columnName The name of the column to read.
+     * @return The value of the given column, or <code>null</null>
+     *         if the cursor does not contain the given column.
+     */
+    public static String getColumnString(Cursor cursor, String columnName) {
+        int col = cursor.getColumnIndex(columnName);
+        return getStringOrNull(cursor, col);
+    }
+
+    private static String getStringOrNull(Cursor cursor, int col) {
+        if (col == NONE) {
+            return null;
+        }
+        try {
+            return cursor.getString(col);
+        } catch (Exception e) {
+            Log.e(LOG_TAG,
+                    "unexpected error retrieving valid column from cursor, "
+                            + "did the remote process die?", e);
+            return null;
+        }
+    }
+}
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index f720cee..2949208 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -21,6 +21,7 @@
 import android.app.LocalActivityManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.util.AttributeSet;
@@ -63,6 +64,8 @@
     private OnTabChangeListener mOnTabChangeListener;
     private OnKeyListener mTabKeyListener;
 
+    private int mTabLayoutId;
+
     public TabHost(Context context) {
         super(context);
         initTabHost();
@@ -70,6 +73,18 @@
 
     public TabHost(Context context, AttributeSet attrs) {
         super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.TabWidget,
+                com.android.internal.R.attr.tabWidgetStyle, 0);
+
+        mTabLayoutId = a.getResourceId(R.styleable.TabWidget_tabLayout, 0);
+        if (mTabLayoutId == 0) {
+            throw new IllegalArgumentException("Invalid TabWidget tabLayout id");
+        }
+
+        a.recycle();
+
         initTabHost();
     }
 
@@ -144,6 +159,11 @@
         }
     }
 
+    @Override
+    public void sendAccessibilityEvent(int eventType) {
+        /* avoid super class behavior - TabWidget sends the right events */
+    }
+
     /**
      * If you are using {@link TabSpec#setContent(android.content.Intent)}, this
      * must be called since the activityGroup is needed to launch the local activity.
@@ -209,6 +229,7 @@
         if (tabSpec.mIndicatorStrategy instanceof ViewIndicatorStrategy) {
             mTabWidget.setStripEnabled(false);
         }
+
         mTabWidget.addView(tabIndicator);
         mTabSpecs.add(tabSpec);
 
@@ -508,7 +529,7 @@
             final Context context = getContext();
             LayoutInflater inflater =
                     (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-            View tabIndicator = inflater.inflate(R.layout.tab_indicator,
+            View tabIndicator = inflater.inflate(mTabLayoutId,
                     mTabWidget, // tab widget is the parent
                     false); // no inflate params
 
@@ -520,7 +541,7 @@
                 tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
                 tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
             }
-            
+
             return tabIndicator;
         }
     }
@@ -542,7 +563,7 @@
             final Context context = getContext();
             LayoutInflater inflater =
                     (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-            View tabIndicator = inflater.inflate(R.layout.tab_indicator,
+            View tabIndicator = inflater.inflate(mTabLayoutId,
                     mTabWidget, // tab widget is the parent
                     false); // no inflate params
 
@@ -550,14 +571,17 @@
             tv.setText(mLabel);
 
             final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon);
-            iconView.setImageDrawable(mIcon);
+            if (mIcon != null) {
+                iconView.setImageDrawable(mIcon);
+                iconView.setVisibility(VISIBLE);
+            }
 
             if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
                 // Donut apps get old color scheme
                 tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
                 tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
             }
-            
+
             return tabIndicator;
         }
     }
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index b4b355a..ce4e8e5 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -26,8 +26,9 @@
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 
 /**
  *
@@ -52,7 +53,8 @@
 public class TabWidget extends LinearLayout implements OnFocusChangeListener {
     private OnTabSelectionChanged mSelectionChangedListener;
 
-    private int mSelectedTab = 0;
+    // This value will be set to 0 as soon as the first tab is added to TabHost.
+    private int mSelectedTab = -1;
 
     private Drawable mLeftStrip;
     private Drawable mRightStrip;
@@ -97,14 +99,18 @@
 
     @Override
     protected int getChildDrawingOrder(int childCount, int i) {
-        // Always draw the selected tab last, so that drop shadows are drawn
-        // in the correct z-order.
-        if (i == childCount - 1) {
-            return mSelectedTab;
-        } else if (i >= mSelectedTab) {
-            return i + 1;
-        } else {
+        if (mSelectedTab == -1) {
             return i;
+        } else {
+            // Always draw the selected tab last, so that drop shadows are drawn
+            // in the correct z-order.
+            if (i == childCount - 1) {
+                return mSelectedTab;
+            } else if (i >= mSelectedTab) {
+                return i + 1;
+            } else {
+                return i;
+            }
         }
     }
 
@@ -113,7 +119,9 @@
 
         final Context context = mContext;
         final Resources resources = context.getResources();
-        
+
+        // Tests the target Sdk version, as set in the Manifest. Could not be set using styles.xml
+        // in a values-v? directory which targets the current platform Sdk version instead.
         if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
             // Donut apps get old color scheme
             if (mLeftStrip == null) {
@@ -124,16 +132,6 @@
                 mRightStrip = resources.getDrawable(
                         com.android.internal.R.drawable.tab_bottom_right_v4);
             }
-        } else {
-            // Use modern color scheme for Eclair and beyond
-            if (mLeftStrip == null) {
-                mLeftStrip = resources.getDrawable(
-                        com.android.internal.R.drawable.tab_bottom_left);
-            }
-            if (mRightStrip == null) {
-                mRightStrip = resources.getDrawable(
-                        com.android.internal.R.drawable.tab_bottom_right);
-            }
         }
 
         // Deal with focus, as we don't want the focus to go by default
@@ -334,14 +332,30 @@
      *  @see #focusCurrentTab
      */
     public void setCurrentTab(int index) {
-        if (index < 0 || index >= getTabCount()) {
+        if (index < 0 || index >= getTabCount() || index == mSelectedTab) {
             return;
         }
 
-        getChildTabViewAt(mSelectedTab).setSelected(false);
+        if (mSelectedTab != -1) {
+            getChildTabViewAt(mSelectedTab).setSelected(false);
+        }
         mSelectedTab = index;
         getChildTabViewAt(mSelectedTab).setSelected(true);
         mStripMoved = true;
+
+        if (isShown()) {
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+        }
+    }
+
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        event.setItemCount(getTabCount());
+        event.setCurrentItemIndex(mSelectedTab);
+        if (mSelectedTab != -1) {
+            getChildTabViewAt(mSelectedTab).dispatchPopulateAccessibilityEvent(event);
+        }
+        return true;
     }
 
     /**
@@ -415,6 +429,15 @@
         child.setOnFocusChangeListener(this);
     }
 
+    @Override
+    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
+        // this class fires events only when tabs are focused or selected
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && isFocused()) {
+            return;
+        }
+        super.sendAccessibilityEventUnchecked(event);
+    }
+
     /**
      * Provides a way for {@link TabHost} to be notified that the user clicked on a tab indicator.
      */
@@ -435,6 +458,10 @@
                 if (getChildTabViewAt(i) == v) {
                     setCurrentTab(i);
                     mSelectionChangedListener.onTabSelectionChanged(i, false);
+                    if (isShown()) {
+                        // a tab is focused so send an event to announce the tab widget state
+                        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+                    }
                     break;
                 }
                 i++;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 4540d5d..2ebed5f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -21,8 +21,10 @@
 
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.R;
+import android.content.ClipData;
+import android.content.ClipboardManager;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -36,6 +38,7 @@
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.ExtractEditText;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -44,7 +47,6 @@
 import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.text.BoringLayout;
-import android.text.ClipboardManager;
 import android.text.DynamicLayout;
 import android.text.Editable;
 import android.text.GetChars;
@@ -84,13 +86,17 @@
 import android.util.FloatMath;
 import android.util.Log;
 import android.util.TypedValue;
+import android.view.ActionMode;
 import android.view.ContextMenu;
 import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
+import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
@@ -193,7 +199,9 @@
     static final String LOG_TAG = "TextView";
     static final boolean DEBUG_EXTRACT = false;
     
-    private static int PRIORITY = 100;
+    private static final int PRIORITY = 100;
+
+    private int mCurrentAlpha = 255;    
 
     final int[] mTempCoords = new int[2];
     Rect mTempRect;
@@ -292,6 +300,8 @@
     int mTextSelectHandleLeftRes;
     int mTextSelectHandleRightRes;
     int mTextSelectHandleRes;
+    int mTextEditPasteWindowLayout;
+    int mTextEditNoPasteWindowLayout;
 
     Drawable mSelectHandleLeft;
     Drawable mSelectHandleRight;
@@ -729,17 +739,26 @@
             case com.android.internal.R.styleable.TextView_textSelectHandle:
                 mTextSelectHandleRes = a.getResourceId(attr, 0);
                 break;
+
+            case com.android.internal.R.styleable.TextView_textEditPasteWindowLayout:
+                mTextEditPasteWindowLayout = a.getResourceId(attr, 0);
+                break;
+
+            case com.android.internal.R.styleable.TextView_textEditNoPasteWindowLayout:
+                mTextEditNoPasteWindowLayout = a.getResourceId(attr, 0);
+                break;
+
+            case com.android.internal.R.styleable.TextView_textLineHeight:
+                int lineHeight = a.getDimensionPixelSize(attr, 0);
+                if (lineHeight != 0) {
+                    setLineHeight(lineHeight);
+                }
             }
         }
         a.recycle();
 
         BufferType bufferType = BufferType.EDITABLE;
 
-        if ((inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION))
-                == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)) {
-            password = true;
-        }
-
         if (inputMethod != null) {
             Class<?> c;
 
@@ -843,10 +862,19 @@
             }
         }
 
-        if (password && (mInputType&EditorInfo.TYPE_MASK_CLASS)
-                == EditorInfo.TYPE_CLASS_TEXT) {
-            mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION))
-                | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
+        if (password) {
+            // Caller used the deprecated xml attribute "password".  Ensure that
+            // the inputType is correct.
+            boolean normalText = (mInputType & EditorInfo.TYPE_MASK_CLASS)
+                    == EditorInfo.TYPE_CLASS_TEXT;
+            if (normalText && !isPasswordInputType(mInputType)) {
+                mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION))
+                    | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
+            }
+        } else if (isPasswordInputType(mInputType)) {
+            // Caller did not use the deprecated xml attribute "password", but
+            // did set the input properly.  Set password to true.
+            password = true;
         }
 
         if (selectallonfocus) {
@@ -1074,6 +1102,9 @@
      * or last-line padding.
      */
     public int getLineHeight() {
+        if (mLineHeight != 0) {
+            return mLineHeight;
+        }
         return FastMath.round(mTextPaint.getFontMetricsInt(null) * mSpacingMult
                           + mSpacingAdd);
     }
@@ -1666,10 +1697,27 @@
                                        TextAppearance_textStyle, -1);
 
         setTypefaceByIndex(typefaceIndex, styleIndex);
+        
+        int lineHeight = appearance.getDimensionPixelSize(
+                com.android.internal.R.styleable.TextAppearance_textLineHeight, 0);
+        if (lineHeight != 0) {
+            setLineHeight(lineHeight);
+        }
+
         appearance.recycle();
     }
 
     /**
+     * Set the height of a line of text in pixels. This value will override line height
+     * values stored in the font modified by lineSpacingExtra and lineSpacingMultiplier.
+     *
+     * @param lineHeight Desired height of a single line of text in pixels
+     */
+    public void setLineHeight(int lineHeight) {
+        mLineHeight = lineHeight;
+    }
+
+    /**
      * @return the size (in pixels) of the default text size in this TextView.
      */
     public float getTextSize() {
@@ -2837,6 +2885,14 @@
             c.drawText(mChars, start + mStart, end - start, x, y, p);
         }
 
+        public void drawTextRun(Canvas c, int start, int end,
+                int contextStart, int contextEnd, float x, float y, int flags, Paint p) {
+            int count = end - start;
+            int contextCount = contextEnd - contextStart;
+            c.drawTextRun(mChars, start + mStart, count, contextStart + mStart,
+                    contextCount, x, y, flags, p);
+        }
+
         public float measureText(int start, int end, Paint p) {
             return p.measureText(mChars, start + mStart, end - start);
         }
@@ -2844,6 +2900,23 @@
         public int getTextWidths(int start, int end, float[] widths, Paint p) {
             return p.getTextWidths(mChars, start + mStart, end - start, widths);
         }
+
+        public float getTextRunAdvances(int start, int end, int contextStart,
+                int contextEnd, int flags, float[] advances, int advancesIndex,
+                Paint p) {
+            int count = end - start;
+            int contextCount = contextEnd - contextStart;
+            return p.getTextRunAdvances(mChars, start + mStart, count,
+                    contextStart + mStart, contextCount, flags, advances,
+                    advancesIndex);
+        }
+
+        public int getTextRunCursor(int contextStart, int contextEnd, int flags,
+                int offset, int cursorOpt, Paint p) {
+            int contextCount = contextEnd - contextStart;
+            return p.getTextRunCursor(mChars, contextStart + mStart,
+                    contextCount, flags, offset + mStart, cursorOpt);
+        }
     }
 
     /**
@@ -2996,7 +3069,9 @@
                 | EditorInfo.TYPE_MASK_VARIATION);
         return variation
                 == (EditorInfo.TYPE_CLASS_TEXT
-                        | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
+                        | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)
+                        || variation == (EditorInfo.TYPE_CLASS_TEXT
+                        | EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD);
     }
 
     private boolean isVisiblePasswordInputType(int inputType) {
@@ -3160,7 +3235,8 @@
      * for this text view.  The default implementation will call your action
      * listener supplied to {@link #setOnEditorActionListener}, or perform
      * a standard operation for {@link EditorInfo#IME_ACTION_NEXT
-     * EditorInfo.IME_ACTION_NEXT} or {@link EditorInfo#IME_ACTION_DONE
+     * EditorInfo.IME_ACTION_NEXT}, {@link EditorInfo#IME_ACTION_PREVIOUS
+     * EditorInfo.IME_ACTION_PREVIOUS}, or {@link EditorInfo#IME_ACTION_DONE
      * EditorInfo.IME_ACTION_DONE}.
      * 
      * <p>For backwards compatibility, if no IME options have been set and the
@@ -3197,6 +3273,16 @@
                 }
                 return;
                 
+            } else if (actionCode == EditorInfo.IME_ACTION_PREVIOUS) {
+                View v = focusSearch(FOCUS_UP);
+                if (v != null) {
+                    if (!v.requestFocus(FOCUS_UP)) {
+                        throw new IllegalStateException("focus search returned a view " +
+                                "that wasn't able to take focus!");
+                    }
+                }
+                return;
+
             } else if (actionCode == EditorInfo.IME_ACTION_DONE) {
                 InputMethodManager imm = InputMethodManager.peekInstance();
                 if (imm != null) {
@@ -3775,7 +3861,7 @@
         //   allow to test for hasSelection in onFocusChanged, which would trigger a
         //   startTextSelectionMode here. TODO
         if (selectionController != null && hasSelection()) {
-            startTextSelectionMode();
+            startSelectionActionMode();
         }
 
         mPreDrawState = PREDRAW_DONE;
@@ -3867,6 +3953,25 @@
     }
 
     @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+        if (mDrawables != null) {
+            if (mDrawables.mDrawableLeft != null) {
+                mDrawables.mDrawableLeft.jumpToCurrentState();
+            }
+            if (mDrawables.mDrawableTop != null) {
+                mDrawables.mDrawableTop.jumpToCurrentState();
+            }
+            if (mDrawables.mDrawableRight != null) {
+                mDrawables.mDrawableRight.jumpToCurrentState();
+            }
+            if (mDrawables.mDrawableBottom != null) {
+                mDrawables.mDrawableBottom.jumpToCurrentState();
+            }
+        }
+    }
+
+    @Override
     public void invalidateDrawable(Drawable drawable) {
         if (verifyDrawable(drawable)) {
             final Rect dirty = drawable.getBounds();
@@ -3915,7 +4020,25 @@
     }
 
     @Override
+    protected boolean onSetAlpha(int alpha) {
+        if (mMovement == null && getBackground() == null) {
+            mCurrentAlpha = alpha;
+            final Drawables dr = mDrawables;
+            if (dr != null) {
+                if (dr.mDrawableLeft != null) dr.mDrawableLeft.mutate().setAlpha(alpha);
+                if (dr.mDrawableTop != null) dr.mDrawableTop.mutate().setAlpha(alpha);
+                if (dr.mDrawableRight != null) dr.mDrawableRight.mutate().setAlpha(alpha);
+                if (dr.mDrawableBottom != null) dr.mDrawableBottom.mutate().setAlpha(alpha);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
     protected void onDraw(Canvas canvas) {
+        if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return;
+
         restartMarqueeIfNeeded();
 
         // Draw the background for this view
@@ -4011,6 +4134,7 @@
         }
 
         mTextPaint.setColor(color);
+        mTextPaint.setAlpha(mCurrentAlpha);
         mTextPaint.drawableState = getDrawableState();
 
         canvas.save();
@@ -4407,8 +4531,8 @@
 
                 // Has to be done on key down (and not on key up) to correctly be intercepted.
             case KeyEvent.KEYCODE_BACK:
-                if (mIsInTextSelectionMode) {
-                    stopTextSelectionMode();
+                if (mSelectionActionMode != null) {
+                    stopSelectionActionMode();
                     return -1;
                 }
                 break;
@@ -4491,7 +4615,7 @@
         }
 
         hideControllers();
-        stopTextSelectionMode();
+        stopSelectionActionMode();
 
         switch (keyCode) {
             case KeyEvent.KEYCODE_DPAD_CENTER:
@@ -4601,9 +4725,15 @@
             } else {
                 outAttrs.imeOptions = EditorInfo.IME_NULL;
             }
+            if (focusSearch(FOCUS_DOWN) != null) {
+                outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
+            }
+            if (focusSearch(FOCUS_UP) != null) {
+                outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS;
+            }
             if ((outAttrs.imeOptions&EditorInfo.IME_MASK_ACTION)
                     == EditorInfo.IME_ACTION_UNSPECIFIED) {
-                if (focusSearch(FOCUS_DOWN) != null) {
+                if ((outAttrs.imeOptions&EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0) {
                     // An action has not been set, but the enter key will move to
                     // the next focus, so set the action to that.
                     outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
@@ -4908,7 +5038,7 @@
                 makeBlink();
             }
         }
-        
+
         checkForResize();
     }
     
@@ -4949,6 +5079,9 @@
         }
 
         mLayout = mHintLayout = null;
+
+        // Since it depends on the value of mLayout
+        prepareCursorControllers();
     }
 
     /**
@@ -4999,6 +5132,8 @@
                 break;
 
             case Gravity.RIGHT:
+                // Note, Layout resolves ALIGN_OPPOSITE to left or
+                // right based on the paragraph direction.
                 alignment = Layout.Alignment.ALIGN_OPPOSITE;
                 break;
 
@@ -5163,6 +5298,8 @@
     }
 
     private boolean compressText(float width) {
+        if (isHardwareAccelerated()) return false;
+        
         // Only compress the text if it hasn't been compressed by the previous pass
         if (width > 0.0f && mLayout != null && getLineCount() == 1 && !mUserSetTextScaleX &&
                 mTextPaint.getTextScaleX() == 1.0f) {
@@ -5823,11 +5960,15 @@
         final int leftChar = mLayout.getOffsetForHorizontal(line, hs);
         final int rightChar = mLayout.getOffsetForHorizontal(line, hspace+hs);
         
+        // line might contain bidirectional text
+        final int lowChar = leftChar < rightChar ? leftChar : rightChar;
+        final int highChar = leftChar > rightChar ? leftChar : rightChar;
+
         int newStart = start;
-        if (newStart < leftChar) {
-            newStart = leftChar;
-        } else if (newStart > rightChar) {
-            newStart = rightChar;
+        if (newStart < lowChar) {
+            newStart = lowChar;
+        } else if (newStart > highChar) {
+            newStart = highChar;
         }
         
         if (newStart != start) {
@@ -6658,11 +6799,14 @@
 
             hideInsertionPointCursorController();
             if (this instanceof ExtractEditText) {
-                // terminateTextSelectionMode would remove selection, which we want to keep when
+                // terminateTextSelectionMode removes selection, which we want to keep when
                 // ExtractEditText goes out of focus.
-                mIsInTextSelectionMode = false;
+                final int selStart = getSelectionStart();
+                final int selEnd = getSelectionEnd();
+                terminateSelectionActionMode();
+                Selection.setSelection((Spannable) mText, selStart, selEnd);
             } else {
-                terminateTextSelectionMode();
+                terminateSelectionActionMode();
             }
 
             if (mSelectionModifierCursorController != null) {
@@ -6780,9 +6924,9 @@
                 return;
             } else {
                 // Tapping outside stops selection mode, if any
-                stopTextSelectionMode();
+                stopSelectionActionMode();
 
-                if (mInsertionPointCursorController != null && mText.length() > 0) {
+                if (mInsertionPointCursorController != null) {
                     mInsertionPointCursorController.show();
                 }
             }
@@ -6793,13 +6937,13 @@
 
     class CommitSelectionReceiver extends ResultReceiver {
         private final int mPrevStart, mPrevEnd;
-        
+
         public CommitSelectionReceiver(int prevStart, int prevEnd) {
             super(getHandler());
             mPrevStart = prevStart;
             mPrevEnd = prevEnd;
         }
-        
+
         @Override
         protected void onReceiveResult(int resultCode, Bundle resultData) {
             // If this tap was actually used to show the IMM, leave cursor or selection unchanged
@@ -6811,23 +6955,23 @@
                 Selection.setSelection((Spannable)mText, start, end);
 
                 if (hasSelection()) {
-                    startTextSelectionMode();
+                    startSelectionActionMode();
                 }
             }
         }
     }
-    
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         final int action = event.getActionMasked();
-        if (action == MotionEvent.ACTION_DOWN) {
-            if (mInsertionPointCursorController != null) {
-                mInsertionPointCursorController.onTouchEvent(event);
-            }
-            if (mSelectionModifierCursorController != null) {
-                mSelectionModifierCursorController.onTouchEvent(event);
-            }
+        if (mInsertionPointCursorController != null) {
+            mInsertionPointCursorController.onTouchEvent(event);
+        }
+        if (mSelectionModifierCursorController != null) {
+            mSelectionModifierCursorController.onTouchEvent(event);
+        }
 
+        if (action == MotionEvent.ACTION_DOWN) {
             // Reset this state; it will be re-set if super.onTouchEvent
             // causes focus to move to the view.
             mTouchFocusSelected = false;
@@ -6848,13 +6992,6 @@
 
         if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
                 && mText instanceof Spannable && mLayout != null) {
-            if (mInsertionPointCursorController != null) {
-                mInsertionPointCursorController.onTouchEvent(event);
-            }
-            if (mSelectionModifierCursorController != null) {
-                mSelectionModifierCursorController.onTouchEvent(event);
-            }
-
             boolean handled = false;
 
             // Save previous selection, in case this event is used to show the IME.
@@ -6863,7 +7000,7 @@
 
             final int oldScrollX = mScrollX;
             final int oldScrollY = mScrollY;
-            
+
             if (mMovement != null) {
                 handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
             }
@@ -6928,7 +7065,7 @@
             }
         } else {
             // Stop selection mode if the controller becomes unavailable.
-            stopTextSelectionMode();
+            stopSelectionActionMode();
             mSelectionModifierCursorController = null;
         }
     }
@@ -7017,6 +7154,7 @@
 
     @Override
     protected float getLeftFadingEdgeStrength() {
+        if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return 0.0f;
         if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
             if (mMarquee != null && !mMarquee.isStopped()) {
                 final Marquee marquee = mMarquee;
@@ -7043,6 +7181,7 @@
 
     @Override
     protected float getRightFadingEdgeStrength() {
+        if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return 0.0f;
         if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
             if (mMarquee != null && !mMarquee.isStopped()) {
                 final Marquee marquee = mMarquee;
@@ -7057,6 +7196,7 @@
                     case Gravity.RIGHT:
                         return 0.0f;
                     case Gravity.CENTER_HORIZONTAL:
+                    case Gravity.FILL_HORIZONTAL:
                         return (mLayout.getLineWidth(0) - ((mRight - mLeft) -
                                 getCompoundPaddingLeft() - getCompoundPaddingRight())) /
                                 getHorizontalFadingEdgeLength();
@@ -7068,8 +7208,10 @@
 
     @Override
     protected int computeHorizontalScrollRange() {
-        if (mLayout != null)
-            return mLayout.getWidth();
+        if (mLayout != null) {
+            return mSingleLine && (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT ?
+                    (int) mLayout.getLineWidth(0) : mLayout.getWidth();
+        }
 
         return super.computeHorizontalScrollRange();
     }
@@ -7216,7 +7358,7 @@
                 getSelectionStart() >= 0 &&
                 getSelectionEnd() >= 0 &&
                 ((ClipboardManager)getContext().getSystemService(Context.CLIPBOARD_SERVICE)).
-                hasText());
+                hasPrimaryClip());
     }
 
     /**
@@ -7242,8 +7384,10 @@
         int variation = mInputType & InputType.TYPE_MASK_VARIATION;
         if (variation == InputType.TYPE_TEXT_VARIATION_URI ||
             variation == InputType.TYPE_TEXT_VARIATION_PASSWORD ||
+            variation == InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD ||
             variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ||
             variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS ||
+            variation == InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS ||
             variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
             return -1;
         }
@@ -7261,6 +7405,10 @@
             char c = mTransformed.charAt(start - 1);
             int type = Character.getType(c);
 
+            // Cases where the text ends with a '.' and we select from the end of the line (right
+            // after the dot), or when we select from the space character in "aaaa, bbbb".
+            if (start == end && type == Character.OTHER_PUNCTUATION) continue;
+
             if (c != '\'' &&
                 type != Character.UPPERCASE_LETTER &&
                 type != Character.LOWERCASE_LETTER &&
@@ -7308,7 +7456,7 @@
         // Two ints packed in a long
         return packRangeInLong(start, end);
     }
-    
+
     private static long packRangeInLong(int start, int end) {
         return (((long) start) << 32) | end;
     }
@@ -7342,7 +7490,7 @@
         }
 
         int selectionStart, selectionEnd;
-        
+
         long wordLimits = getWordLimitsAt(minOffset);
         if (wordLimits >= 0) {
             selectionStart = extractRangeStartFromLong(wordLimits);
@@ -7359,19 +7507,7 @@
 
         Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
     }
-    
-    private String getWordForDictionary() {
-        int seedPosition = mContextMenuTriggeredByKey ? getSelectionStart() : getLastTapPosition();
-        long wordLimits = getWordLimitsAt(seedPosition);
-        if (wordLimits >= 0) {
-            int start = extractRangeStartFromLong(wordLimits);
-            int end = extractRangeEndFromLong(wordLimits);
-            return mTransformed.subSequence(start, end).toString();
-        } else {
-            return null;
-        }
-    }
-    
+
     @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         if (!isShown()) {
@@ -7421,83 +7557,34 @@
         // it may not be called (if the user/ discards the context menu with the back key).
         mDPadCenterIsDown = mEnterKeyIsDown = false;
 
-        if (mIsInTextSelectionMode) {
-            MenuHandler handler = new MenuHandler();
-            
-            if (canCut()) {
-                menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut).
-                     setOnMenuItemClickListener(handler).
-                     setAlphabeticShortcut('x');
+        MenuHandler handler = new MenuHandler();
+
+        if (mText instanceof Spanned) {
+            int selStart = getSelectionStart();
+            int selEnd = getSelectionEnd();
+
+            int min = Math.min(selStart, selEnd);
+            int max = Math.max(selStart, selEnd);
+
+            URLSpan[] urls = ((Spanned) mText).getSpans(min, max,
+                                                        URLSpan.class);
+            if (urls.length == 1) {
+                menu.add(0, ID_COPY_URL, 0,
+                         com.android.internal.R.string.copyUrl).
+                            setOnMenuItemClickListener(handler);
+
                 added = true;
             }
-
-            if (canCopy()) {
-                menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
-                     setOnMenuItemClickListener(handler).
-                     setAlphabeticShortcut('c');
-                added = true;
-            }
-
-            if (canPaste()) {
-                menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
-                     setOnMenuItemClickListener(handler).
-                     setAlphabeticShortcut('v');
-                added = true;
-            }
-        } else {
-            MenuHandler handler = new MenuHandler();
-
-            if (canSelectText()) {
-                if (!hasPasswordTransformationMethod()) {
-                    // selectCurrentWord is not available on a password field and would return an
-                    // arbitrary 10-charater selection around pressed position. Discard it.
-                    // SelectAll is still useful to be able to clear the field using the delete key.
-                    menu.add(0, ID_START_SELECTING_TEXT, 0, com.android.internal.R.string.selectText).
-                    setOnMenuItemClickListener(handler);
-                }
-                menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
-                     setOnMenuItemClickListener(handler).
-                     setAlphabeticShortcut('a');
-                added = true;
-            }
-
-            if (mText instanceof Spanned) {
-                int selStart = getSelectionStart();
-                int selEnd = getSelectionEnd();
-
-                int min = Math.min(selStart, selEnd);
-                int max = Math.max(selStart, selEnd);
-
-                URLSpan[] urls = ((Spanned) mText).getSpans(min, max,
-                        URLSpan.class);
-                if (urls.length == 1) {
-                    menu.add(0, ID_COPY_URL, 0, com.android.internal.R.string.copyUrl).
-                         setOnMenuItemClickListener(handler);
-                    added = true;
-                }
-            }
-            
-            if (canPaste()) {
-                menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
-                     setOnMenuItemClickListener(handler).
-                     setAlphabeticShortcut('v');
-                added = true;
-            }
-
-            if (isInputMethodTarget()) {
-                menu.add(1, ID_SWITCH_INPUT_METHOD, 0, com.android.internal.R.string.inputMethod).
-                     setOnMenuItemClickListener(handler);
-                added = true;
-            }
-
-            String word = getWordForDictionary();
-            if (word != null) {
-                menu.add(1, ID_ADD_TO_DICTIONARY, 0,
-                     getContext().getString(com.android.internal.R.string.addToDictionary, word)).
-                     setOnMenuItemClickListener(handler);
-                added = true;
-
-            }
+        }
+        
+        // The context menu is not empty, which will prevent the selection mode from starting.
+        // Add a entry to start it in the context menu.
+        // TODO Does not handle the case where a subclass does not call super.thisMethod or
+        // populates the menu AFTER this call.
+        if (menu.size() > 0) {
+            menu.add(0, ID_SELECTION_MODE, 0, com.android.internal.R.string.selectTextMode).
+            setOnMenuItemClickListener(handler);
+            added = true;
         }
 
         if (added) {
@@ -7514,15 +7601,14 @@
         return imm != null && imm.isActive(this);
     }
     
-    // Context menu entries
+    // Selection context mode
     private static final int ID_SELECT_ALL = android.R.id.selectAll;
-    private static final int ID_START_SELECTING_TEXT = android.R.id.startSelectingText;
     private static final int ID_CUT = android.R.id.cut;
     private static final int ID_COPY = android.R.id.copy;
     private static final int ID_PASTE = android.R.id.paste;
+    // Context menu entries
     private static final int ID_COPY_URL = android.R.id.copyUrl;
-    private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod;
-    private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary;
+    private static final int ID_SELECTION_MODE = android.R.id.selectTextMode;
 
     private class MenuHandler implements MenuItem.OnMenuItemClickListener {
         public boolean onMenuItemClick(MenuItem item) {
@@ -7532,11 +7618,7 @@
 
     /**
      * Called when a context menu option for the text view is selected.  Currently
-     * this will be one of: {@link android.R.id#selectAll},
-     * {@link android.R.id#startSelectingText},
-     * {@link android.R.id#cut}, {@link android.R.id#copy},
-     * {@link android.R.id#paste}, {@link android.R.id#copyUrl},
-     * or {@link android.R.id#switchInputMethod}.
+     * this will be {@link android.R.id#copyUrl} or {@link android.R.id#selectTextMode}.
      */
     public boolean onTextContextMenuItem(int id) {
         int min = 0;
@@ -7550,65 +7632,30 @@
             max = Math.max(0, Math.max(selStart, selEnd));
         }
 
-        ClipboardManager clip = (ClipboardManager)getContext()
+        ClipboardManager clipboard = (ClipboardManager)getContext()
                 .getSystemService(Context.CLIPBOARD_SERVICE);
 
         switch (id) {
-            case ID_SELECT_ALL:
-                Selection.setSelection((Spannable) mText, 0, mText.length());
-                startTextSelectionMode();
-                return true;
-
-            case ID_START_SELECTING_TEXT:
-                startTextSelectionMode();
-                return true;
-
-            case ID_CUT:                
-                clip.setText(mTransformed.subSequence(min, max));
-                ((Editable) mText).delete(min, max);
-                stopTextSelectionMode();
-                return true;
-
-            case ID_COPY:
-                clip.setText(mTransformed.subSequence(min, max));
-                stopTextSelectionMode();
-                return true;
-
-            case ID_PASTE:
-                CharSequence paste = clip.getText();
-
-                if (paste != null && paste.length() > 0) {
-                    long minMax = prepareSpacesAroundPaste(min, max, paste);
-                    min = extractRangeStartFromLong(minMax);
-                    max = extractRangeEndFromLong(minMax);
-                    Selection.setSelection((Spannable) mText, max);
-                    ((Editable) mText).replace(min, max, paste);
-                    stopTextSelectionMode();
-                }
-                return true;
-
             case ID_COPY_URL:
                 URLSpan[] urls = ((Spanned) mText).getSpans(min, max, URLSpan.class);
-                if (urls.length == 1) {
-                    clip.setText(urls[0].getURL());
+                if (urls.length >= 1) {
+                    ClipData clip = null;
+                    for (int i=0; i<urls.length; i++) {
+                        Uri uri = Uri.parse(urls[0].getURL());
+                        if (clip == null) {
+                            clip = ClipData.newRawUri(null, null, uri);
+                        } else {
+                            clip.addItem(new ClipData.Item(uri));
+                        }
+                    }
+                    if (clip != null) {
+                        clipboard.setPrimaryClip(clip);
+                    }
                 }
                 return true;
 
-            case ID_SWITCH_INPUT_METHOD:
-                InputMethodManager imm = InputMethodManager.peekInstance();
-                if (imm != null) {
-                    imm.showInputMethodPicker();
-                }
-                return true;
-
-            case ID_ADD_TO_DICTIONARY:
-                String word = getWordForDictionary();
-                if (word != null) {
-                    Intent i = new Intent("com.android.settings.USER_DICTIONARY_INSERT");
-                    i.putExtra("word", word);
-                    i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
-                    getContext().startActivity(i);
-                }
+            case ID_SELECTION_MODE:
+                startSelectionActionMode();
                 return true;
             }
 
@@ -7664,35 +7711,81 @@
             mEatTouchRelease = true;
             return true;
         }
+        
+        if (startSelectionActionMode()) {
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+            mEatTouchRelease = true;
+            return true;
+        }
 
         return false;
     }
 
-    private void startTextSelectionMode() {
-        if (!mIsInTextSelectionMode) {
-            if (mSelectionModifierCursorController == null) {
-                Log.w(LOG_TAG, "TextView has no selection controller. Action mode cancelled.");
-                return;
-            }
+    private boolean touchPositionIsInSelection() {
+        int selectionStart = getSelectionStart();
+        int selectionEnd = getSelectionEnd();
 
-            if (!requestFocus()) {
-                return;
-            }
-
-            selectCurrentWord();
-            mSelectionModifierCursorController.show();
-            mIsInTextSelectionMode = true;
+        if (selectionStart == selectionEnd) {
+            return false;
         }
+
+        if (selectionStart > selectionEnd) {
+            int tmp = selectionStart;
+            selectionStart = selectionEnd;
+            selectionEnd = tmp;
+            Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
+        }
+
+        SelectionModifierCursorController selectionModifierCursorController =
+            ((SelectionModifierCursorController) mSelectionModifierCursorController);
+        int minOffset = selectionModifierCursorController.getMinTouchOffset();
+        int maxOffset = selectionModifierCursorController.getMaxTouchOffset();
+
+        return ((minOffset >= selectionStart) && (maxOffset < selectionEnd));
     }
-    
+
     /**
-     * Same as {@link #stopTextSelectionMode()}, except that there is no cursor controller
+     * Provides the callback used to start a selection action mode.
+     *
+     * @return A callback instance that will be used to start selection mode, or null if selection
+     * mode is not available.
+     */
+    private ActionMode.Callback getActionModeCallback() {
+        // Long press in the current selection.
+        // Should initiate a drag. Return false, to rely on context menu for now.
+        if (canSelectText() && !touchPositionIsInSelection()) {
+            return new SelectionActionModeCallback();
+        }
+        return null;
+    }
+
+    /**
+     *
+     * @return true if the selection mode was actually started.
+     */
+    private boolean startSelectionActionMode() {
+        if (mSelectionActionMode != null) {
+            // Selection action mode is already started
+            return false;
+        }
+
+        ActionMode.Callback actionModeCallback = getActionModeCallback();
+        if (actionModeCallback != null) {
+            mSelectionActionMode = startActionMode(actionModeCallback);
+            return mSelectionActionMode != null;
+        }
+
+        return false;
+    }
+
+    /**
+     * Same as {@link #stopSelectionActionMode()}, except that there is no cursor controller
      * fade out animation. Needed since the drawable and their alpha values are shared by all
      * TextViews. Switching from one TextView to another would fade the cursor controllers in the
      * new one otherwise.
      */
-    private void terminateTextSelectionMode() {
-        stopTextSelectionMode();
+    private void terminateSelectionActionMode() {
+        stopSelectionActionMode();
         if (mSelectionModifierCursorController != null) {
             SelectionModifierCursorController selectionModifierCursorController =
                 (SelectionModifierCursorController) mSelectionModifierCursorController;
@@ -7700,11 +7793,174 @@
         }
     }
 
-    private void stopTextSelectionMode() {
-        if (mIsInTextSelectionMode) {
-            Selection.setSelection((Spannable) mText, getSelectionEnd());
-            hideSelectionModifierCursorController();
-            mIsInTextSelectionMode = false;
+    private void stopSelectionActionMode() {
+        if (mSelectionActionMode != null) {
+            mSelectionActionMode.finish();
+        }
+    }
+
+    /**
+     * Paste clipboard content between min and max positions.
+     *
+     * @param clipboard getSystemService(Context.CLIPBOARD_SERVICE)
+     */
+    private void paste(ClipboardManager clipboard, int min, int max) {
+        ClipData clip = clipboard.getPrimaryClip();
+        if (clip != null) {
+            boolean didfirst = false;
+            for (int i=0; i<clip.getItemCount(); i++) {
+                CharSequence paste = clip.getItem(i).coerceToText(getContext());
+                if (paste != null) {
+                    if (!didfirst) {
+                        long minMax = prepareSpacesAroundPaste(min, max, paste);
+                        min = extractRangeStartFromLong(minMax);
+                        max = extractRangeEndFromLong(minMax);
+                        Selection.setSelection((Spannable) mText, max);
+                        ((Editable) mText).replace(min, max, paste);
+                    } else {
+                        ((Editable) mText).insert(getSelectionEnd(), "\n");
+                        ((Editable) mText).insert(getSelectionEnd(), paste);
+                    }
+                }
+            }
+            stopSelectionActionMode();
+        }
+    }
+
+    private class SelectionActionModeCallback implements ActionMode.Callback {
+
+        @Override
+        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+            if (mSelectionModifierCursorController == null) {
+                Log.w(LOG_TAG, "TextView has no selection controller. Action mode cancelled.");
+                return false;
+            }
+
+            if (!requestFocus()) {
+                return false;
+            }
+
+            TypedArray styledAttributes = mContext.obtainStyledAttributes(R.styleable.Theme);
+
+            mode.setTitle(mContext.getString(com.android.internal.R.string.textSelectionCABTitle));
+            mode.setSubtitle(null);
+
+            boolean atLeastOne = false;
+
+            if (canSelectText()) {
+                if (hasPasswordTransformationMethod()) {
+                    // selectCurrentWord is not available on a password field and would return an
+                    // arbitrary 10-charater selection around pressed position. Select all instead.
+                    Selection.setSelection((Spannable) mText, 0, mText.length());
+                } else {
+                    selectCurrentWord();
+                }
+
+                menu.add(0, ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
+                    setAlphabeticShortcut('a').
+                    setShowAsAction(
+                            MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+                atLeastOne = true;
+            }
+
+            if (canCut()) {
+                menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut).
+                    setIcon(styledAttributes.getResourceId(R.styleable.Theme_actionModeCutDrawable, 0)).
+                    setAlphabeticShortcut('x').
+                    setShowAsAction(
+                            MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+                atLeastOne = true;
+            }
+
+            if (canCopy()) {
+                menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
+                    setIcon(styledAttributes.getResourceId(R.styleable.Theme_actionModeCopyDrawable, 0)).
+                    setAlphabeticShortcut('c').
+                    setShowAsAction(
+                            MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+                atLeastOne = true;
+            }
+
+            if (canPaste()) {
+                menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
+                        setIcon(styledAttributes.getResourceId(R.styleable.Theme_actionModePasteDrawable, 0)).
+                        setAlphabeticShortcut('v').
+                        setShowAsAction(
+                                MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+                atLeastOne = true;
+            }
+
+            styledAttributes.recycle();
+
+            if (atLeastOne) {
+                mSelectionModifierCursorController.show();
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+            return true;
+        }
+
+        @Override
+        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            final int itemId = item.getItemId();
+
+            if (itemId == ID_SELECT_ALL) {
+                Selection.setSelection((Spannable) mText, 0, mText.length());
+                // Update controller positions after selection change.
+                if (mSelectionModifierCursorController != null) {
+                    mSelectionModifierCursorController.show();
+                }
+                return true;
+            }
+
+            ClipboardManager clipboard = (ClipboardManager) getContext().
+                    getSystemService(Context.CLIPBOARD_SERVICE);
+
+            int min = 0;
+            int max = mText.length();
+
+            if (isFocused()) {
+                final int selStart = getSelectionStart();
+                final int selEnd = getSelectionEnd();
+
+                min = Math.max(0, Math.min(selStart, selEnd));
+                max = Math.max(0, Math.max(selStart, selEnd));
+            }
+
+            switch (item.getItemId()) {
+                case ID_PASTE:
+                    paste(clipboard, min, max);
+                    return true;
+
+                case ID_CUT:
+                    clipboard.setPrimaryClip(ClipData.newPlainText(null, null,
+                            mTransformed.subSequence(min, max)));
+                    ((Editable) mText).delete(min, max);
+                    stopSelectionActionMode();
+                    return true;
+
+                case ID_COPY:
+                    clipboard.setPrimaryClip(ClipData.newPlainText(null, null,
+                            mTransformed.subSequence(min, max)));
+                    stopSelectionActionMode();
+                    return true;
+            }
+
+            return false;
+        }
+
+        @Override
+        public void onDestroyActionMode(ActionMode mode) {
+            Selection.setSelection((Spannable) mText, getSelectionStart());
+            if (mSelectionModifierCursorController != null) {
+                mSelectionModifierCursorController.hide();
+            }
+            mSelectionActionMode = null;
         }
     }
 
@@ -7746,6 +8002,103 @@
         public boolean onTouchEvent(MotionEvent event);
     }
 
+    private class PastePopupMenu implements OnClickListener {
+        private PopupWindow mContainer;
+        private int mPositionX;
+        private int mPositionY;
+        private View mPasteView, mNoPasteView;
+
+        public PastePopupMenu() {
+            mContainer = new PopupWindow(TextView.this.mContext, null,
+                    com.android.internal.R.attr.textSelectHandleWindowStyle);
+            mContainer.setSplitTouchEnabled(true);
+            mContainer.setClippingEnabled(false);
+            mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
+
+            mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
+            mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
+        }
+
+        private void updateContent() {
+            View view = canPaste() ? mPasteView : mNoPasteView;
+
+            if (view == null) {
+                final int layout = canPaste() ? mTextEditPasteWindowLayout :
+                    mTextEditNoPasteWindowLayout;
+                LayoutInflater inflater = (LayoutInflater)TextView.this.mContext.
+                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+                if (inflater != null) {
+                    view = inflater.inflate(layout, null);
+                }
+
+                if (view == null) {
+                    throw new IllegalArgumentException("Unable to inflate TextEdit paste window");
+                }
+
+                final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+                view.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT));
+                view.measure(size, size);
+
+                view.setOnClickListener(this);
+
+                if (canPaste()) mPasteView = view;
+                else mNoPasteView = view;
+            }
+
+            mContainer.setContentView(view);
+        }
+
+        public void show() {
+            updateContent();
+            final int[] coords = mTempCoords;
+            TextView.this.getLocationInWindow(coords);
+            positionAtCursor();
+            coords[0] += mPositionX;
+            coords[1] += mPositionY;
+            mContainer.showAtLocation(TextView.this, Gravity.NO_GRAVITY, coords[0], coords[1]);
+        }
+
+        public void hide() {
+            mContainer.dismiss();
+        }
+
+        public boolean isShowing() {
+            return mContainer.isShowing();
+        }
+
+        @Override
+        public void onClick(View v) {
+            if (canPaste()) {
+                ClipboardManager clipboard =
+                    (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+                paste(clipboard, getSelectionStart(), getSelectionEnd());
+            }
+            hide();
+        }
+
+        void positionAtCursor() {
+            final int offset = TextView.this.getSelectionStart();
+            View contentView = mContainer.getContentView();
+            final int width = contentView.getMeasuredWidth();
+            final int height = contentView.getMeasuredHeight();
+            final int line = mLayout.getLineForOffset(offset);
+            final int lineTop = mLayout.getLineTop(line);
+
+            final Rect bounds = sCursorControllerTempRect;
+            bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - width / 2.0f);
+            bounds.top = lineTop - height;
+
+            bounds.right = bounds.left + width;
+            bounds.bottom = bounds.top + height;
+
+            convertFromViewportToContentCoordinates(bounds);
+
+            mPositionX = bounds.left;
+            mPositionY = bounds.top;
+        }
+    }
+
     private class HandleView extends View {
         private boolean mPositionOnTop = false;
         private Drawable mDrawable;
@@ -7762,6 +8115,9 @@
         private float mTouchOffsetY;
         private int mLastParentX;
         private int mLastParentY;
+        private int mContainerPositionX, mContainerPositionY;
+        private long mTouchTimer;
+        private PastePopupMenu mPastePopupWindow;
 
         public static final int LEFT = 0;
         public static final int CENTER = 1;
@@ -7789,7 +8145,7 @@
                 }
                 mDrawable = mSelectHandleLeft;
                 handleWidth = mDrawable.getIntrinsicWidth();
-                mHotspotX = handleWidth / 4 * 3;
+                mHotspotX = (handleWidth * 3) / 4;
                 break;
             }
 
@@ -7813,6 +8169,7 @@
                 mDrawable = mSelectHandleCenter;
                 handleWidth = mDrawable.getIntrinsicWidth();
                 mHotspotX = handleWidth / 2;
+                mPastePopupWindow = new PastePopupMenu();
                 break;
             }
             }
@@ -7826,7 +8183,7 @@
         }
 
         @Override
-        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
             setMeasuredDimension(mDrawable.getIntrinsicWidth(),
                     mDrawable.getIntrinsicHeight());
         }
@@ -7839,14 +8196,22 @@
             mContainer.setContentView(this);
             final int[] coords = mTempCoords;
             TextView.this.getLocationInWindow(coords);
-            coords[0] += mPositionX;
-            coords[1] += mPositionY;
-            mContainer.showAtLocation(TextView.this, 0, coords[0], coords[1]);
+            mContainerPositionX = coords[0] + mPositionX;
+            mContainerPositionY = coords[1] + mPositionY;
+            mContainer.showAtLocation(TextView.this, 0, mContainerPositionX, mContainerPositionY);
+
+            // Hide paste view when handle is moved.
+            if (mPastePopupWindow != null) {
+                mPastePopupWindow.hide();
+            }
         }
 
         public void hide() {
             mIsDragging = false;
             mContainer.dismiss();
+            if (mPastePopupWindow != null) {
+                mPastePopupWindow.hide();
+            }
         }
 
         public boolean isShowing() {
@@ -7901,8 +8266,22 @@
                 if (mContainer.isShowing()){
                     coords = mTempCoords;
                     TextView.this.getLocationInWindow(coords);
-                    mContainer.update(coords[0] + mPositionX, coords[1] + mPositionY,
-                            mRight - mLeft, mBottom - mTop);
+                    final int containerPositionX = coords[0] + mPositionX;
+                    final int containerPositionY = coords[1] + mPositionY;
+
+                    if (containerPositionX != mContainerPositionX || 
+                        containerPositionY != mContainerPositionY) {
+                        mContainerPositionX = containerPositionX;
+                        mContainerPositionY = containerPositionY;
+
+                        mContainer.update(mContainerPositionX, mContainerPositionY,
+                                mRight - mLeft, mBottom - mTop);
+
+                        // Hide paste popup window as soon as a scroll occurs.
+                        if (mPastePopupWindow != null) {
+                            mPastePopupWindow.hide();
+                        }
+                    }
                 } else {
                     show();
                 }
@@ -7918,6 +8297,10 @@
                         mLastParentX = coords[0];
                         mLastParentY = coords[1];
                     }
+                    // Hide paste popup window as soon as the handle is dragged.
+                    if (mPastePopupWindow != null) {
+                        mPastePopupWindow.hide();
+                    }
                 }
             } else {
                 hide();
@@ -7925,7 +8308,7 @@
         }
 
         @Override
-        public void onDraw(Canvas c) {
+        protected void onDraw(Canvas c) {
             mDrawable.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
             if (mPositionOnTop) {
                 c.save();
@@ -7950,6 +8333,15 @@
                 mLastParentX = coords[0];
                 mLastParentY = coords[1];
                 mIsDragging = true;
+                if (mPastePopupWindow != null) {
+                    mTouchTimer = SystemClock.uptimeMillis();
+                    if (mPastePopupWindow.isShowing()) {
+                        // Tapping on the handle again dismisses the displayed paste view,
+                        mPastePopupWindow.hide();
+                        // and makes sure the action up does not display the paste view.
+                        mTouchTimer = 0;
+                    }
+                }
                 break;
             }
             case MotionEvent.ACTION_MOVE: {
@@ -7963,6 +8355,24 @@
                 break;
             }
             case MotionEvent.ACTION_UP:
+                if (mPastePopupWindow != null) {
+                    long delay = SystemClock.uptimeMillis() - mTouchTimer;
+                    if (delay < ViewConfiguration.getTapTimeout()) {
+                        final float touchOffsetX = ev.getRawX() - mPositionX;
+                        final float touchOffsetY = ev.getRawY() - mPositionY;
+                        final float dx = touchOffsetX - mTouchToWindowOffsetX;
+                        final float dy = touchOffsetY - mTouchToWindowOffsetY;
+                        final float distanceSquared = dx * dx + dy * dy;
+                        final ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
+                        final int doubleTapSlop = viewConfiguration.getScaledDoubleTapSlop();
+                        final int slopSquared = doubleTapSlop * doubleTapSlop;
+                        if (distanceSquared < slopSquared) {
+                            mPastePopupWindow.show();
+                        }
+                    }
+                }
+                mIsDragging = false;
+                break;
             case MotionEvent.ACTION_CANCEL:
                 mIsDragging = false;
             }
@@ -8072,6 +8482,11 @@
         // Whether selection anchors are active
         private boolean mIsShowing;
 
+        // Double tap detection
+        private long mPreviousTapUpTime = 0;
+        private int mPreviousTapPositionX;
+        private int mPreviousTapPositionY;
+
         private static final int DELAY_BEFORE_FADE_OUT = 4100;
 
         private final Runnable mHider = new Runnable() {
@@ -8177,6 +8592,26 @@
                         // Remember finger down position, to be able to start selection from there
                         mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
 
+                        // Double tap detection
+                        long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime;
+                        if (duration <= ViewConfiguration.getDoubleTapTimeout()) {
+                            final int deltaX = x - mPreviousTapPositionX;
+                            final int deltaY = y - mPreviousTapPositionY;
+                            final int distanceSquared = deltaX * deltaX + deltaY * deltaY;
+                            final int doubleTapSlop =
+                                ViewConfiguration.get(getContext()).getScaledDoubleTapSlop();
+                            final int slopSquared = doubleTapSlop * doubleTapSlop;
+                            if (distanceSquared < slopSquared) {
+                                startSelectionActionMode();
+                                // Hacky: onTapUpEvent will open a context menu with cut/copy
+                                // Prevent this by hiding handles which will be revived instead.
+                                hide();
+                            }
+                        }
+
+                        mPreviousTapPositionX = x;
+                        mPreviousTapPositionY = y;
+
                         break;
 
                     case MotionEvent.ACTION_POINTER_DOWN:
@@ -8188,6 +8623,10 @@
                             updateMinAndMaxOffsets(event);
                         }
                         break;
+
+                    case MotionEvent.ACTION_UP:
+                        mPreviousTapUpTime = SystemClock.uptimeMillis();
+                        break;
                 }
             }
             return false;
@@ -8310,7 +8749,8 @@
         return getOffsetForHorizontal(line, x);
     }
 
-    @ViewDebug.ExportedProperty
+
+    @ViewDebug.ExportedProperty(category = "text")
     private CharSequence            mText;
     private CharSequence            mTransformed;
     private BufferType              mBufferType = BufferType.NORMAL;
@@ -8341,7 +8781,7 @@
     // Cursor Controllers. Null when disabled.
     private CursorController        mInsertionPointCursorController;
     private CursorController        mSelectionModifierCursorController;
-    private boolean                 mIsInTextSelectionMode = false;
+    private ActionMode              mSelectionActionMode;
     // These are needed to desambiguate a long click. If the long click comes from ones of these, we
     // select from the current cursor position. Otherwise, select from long pressed position.
     private boolean                 mDPadCenterIsDown = false;
@@ -8361,6 +8801,7 @@
 
     private float                   mSpacingMult = 1;
     private float                   mSpacingAdd = 0;
+    private int                     mLineHeight = 0;
 
     private static final int        LINES = 1;
     private static final int        EMS = LINES;
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index df957ac..29ca49a 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -30,6 +30,8 @@
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 
 /**
  * A toast is a view containing a quick little message for the user.  The toast class
@@ -281,6 +283,21 @@
         tv.setText(s);
     }
 
+    private void trySendAccessibilityEvent() {
+        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
+        if (!accessibilityManager.isEnabled()) {
+            return;
+        }
+        // treat toasts as notifications since they are used to
+        // announce a transient piece of information to the user
+        AccessibilityEvent event = AccessibilityEvent.obtain(
+                AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
+        event.setClassName(getClass().getName());
+        event.setPackageName(mContext.getPackageName());
+        mView.dispatchPopulateAccessibilityEvent(event);
+        accessibilityManager.sendAccessibilityEvent(event);
+    }
+
     // =======================================================================================
     // All the gunk below is the interaction with the Notification Service, which handles
     // the proper ordering of these system-wide.
@@ -371,6 +388,7 @@
                 }
                 if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
                 mWM.addView(mView, mParams);
+                trySendAccessibilityEvent();
             }
         }
 
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 531d9fe..ec31dd4 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -62,9 +62,6 @@
     private static final int STATE_PLAYING            = 3;
     private static final int STATE_PAUSED             = 4;
     private static final int STATE_PLAYBACK_COMPLETED = 5;
-    private static final int STATE_SUSPEND            = 6;
-    private static final int STATE_RESUME             = 7;
-    private static final int STATE_SUSPEND_UNSUPPORTED = 8;
 
     // mCurrentState is a VideoView object's current state.
     // mTargetState is the state that a method caller intends to reach.
@@ -90,7 +87,6 @@
     private boolean     mCanPause;
     private boolean     mCanSeekBack;
     private boolean     mCanSeekForward;
-    private int         mStateWhenSuspended;  //state before calling suspend()
 
     public VideoView(Context context) {
         super(context);
@@ -470,14 +466,7 @@
         public void surfaceCreated(SurfaceHolder holder)
         {
             mSurfaceHolder = holder;
-            //resume() was called before surfaceCreated()
-            if (mMediaPlayer != null && mCurrentState == STATE_SUSPEND
-                   && mTargetState == STATE_RESUME) {
-                mMediaPlayer.setDisplay(mSurfaceHolder);
-                resume();
-            } else {
-                openVideo();
-            }
+            openVideo();
         }
 
         public void surfaceDestroyed(SurfaceHolder holder)
@@ -485,9 +474,7 @@
             // after we return from this we can't use the surface any more
             mSurfaceHolder = null;
             if (mMediaController != null) mMediaController.hide();
-            if (mCurrentState != STATE_SUSPEND) {
-                release(true);
-            }
+            release(true);
         }
     };
 
@@ -528,6 +515,7 @@
         boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&
                                      keyCode != KeyEvent.KEYCODE_VOLUME_UP &&
                                      keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&
+                                     keyCode != KeyEvent.KEYCODE_VOLUME_MUTE &&
                                      keyCode != KeyEvent.KEYCODE_MENU &&
                                      keyCode != KeyEvent.KEYCODE_CALL &&
                                      keyCode != KeyEvent.KEYCODE_ENDCALL;
@@ -542,10 +530,19 @@
                     mMediaController.hide();
                 }
                 return true;
+            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
+                if (mMediaPlayer.isPlaying()) {
+                    start();
+                    mMediaController.hide();
+                }
+                return true;
             } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
-                    && mMediaPlayer.isPlaying()) {
-                pause();
-                mMediaController.show();
+                    || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
+                if (!mMediaPlayer.isPlaying()) {
+                    pause();
+                    mMediaController.show();
+                }
+                return true;
             } else {
                 toggleMediaControlsVisiblity();
             }
@@ -581,39 +578,14 @@
     }
 
     public void suspend() {
-        if (isInPlaybackState()) {
-            if (mMediaPlayer.suspend()) {
-                mStateWhenSuspended = mCurrentState;
-                mCurrentState = STATE_SUSPEND;
-                mTargetState = STATE_SUSPEND;
-            } else {
-                release(false);
-                mCurrentState = STATE_SUSPEND_UNSUPPORTED;
-                Log.w(TAG, "Unable to suspend video. Release MediaPlayer.");
-            }
-        }
+        release(false);
     }
 
     public void resume() {
-        if (mSurfaceHolder == null && mCurrentState == STATE_SUSPEND){
-            mTargetState = STATE_RESUME;
-            return;
-        }
-        if (mMediaPlayer != null && mCurrentState == STATE_SUSPEND) {
-            if (mMediaPlayer.resume()) {
-                mCurrentState = mStateWhenSuspended;
-                mTargetState = mStateWhenSuspended;
-            } else {
-                Log.w(TAG, "Unable to resume video");
-            }
-            return;
-        }
-        if (mCurrentState == STATE_SUSPEND_UNSUPPORTED) {
-            openVideo();
-        }
+        openVideo();
     }
 
-   // cache duration as mDuration for faster access
+    // cache duration as mDuration for faster access
     public int getDuration() {
         if (isInPlaybackState()) {
             if (mDuration > 0) {
diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java
index 907cfb3..7b66893 100644
--- a/core/java/android/widget/ViewAnimator.java
+++ b/core/java/android/widget/ViewAnimator.java
@@ -31,11 +31,13 @@
  *
  * @attr ref android.R.styleable#ViewAnimator_inAnimation
  * @attr ref android.R.styleable#ViewAnimator_outAnimation
+ * @attr ref android.R.styleable#ViewAnimator_animateFirstView
  */
 public class ViewAnimator extends FrameLayout {
 
     int mWhichChild = 0;
     boolean mFirstTime = true;
+
     boolean mAnimateFirstTime = true;
 
     Animation mInAnimation;
@@ -59,6 +61,10 @@
         if (resource > 0) {
             setOutAnimation(context, resource);
         }
+
+        boolean flag = a.getBoolean(com.android.internal.R.styleable.ViewAnimator_animateFirstView, true);
+        setAnimateFirstView(flag);
+
         a.recycle();
 
         initViewAnimator(context, attrs);
@@ -84,10 +90,10 @@
         setMeasureAllChildren(measureAllChildren);
         a.recycle();
     }
-    
+
     /**
      * Sets which child view will be displayed.
-     * 
+     *
      * @param whichChild the index of the child view to display
      */
     public void setDisplayedChild(int whichChild) {
@@ -105,14 +111,14 @@
             requestFocus(FOCUS_FORWARD);
         }
     }
-    
+
     /**
      * Returns the index of the currently displayed child view.
      */
     public int getDisplayedChild() {
         return mWhichChild;
     }
-    
+
     /**
      * Manually shows the next child.
      */
@@ -128,6 +134,35 @@
     }
 
     /**
+     * Shows only the specified child. The other displays Views exit the screen,
+     * optionally with the with the {@link #getOutAnimation() out animation} and
+     * the specified child enters the screen, optionally with the
+     * {@link #getInAnimation() in animation}.
+     *
+     * @param childIndex The index of the child to be shown.
+     * @param animate Whether or not to use the in and out animations, defaults
+     *            to true.
+     */
+    void showOnly(int childIndex, boolean animate) {
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            if (i == childIndex) {
+                if (animate && mInAnimation != null) {
+                    child.startAnimation(mInAnimation);
+                }
+                child.setVisibility(View.VISIBLE);
+                mFirstTime = false;
+            } else {
+                if (animate && mOutAnimation != null && child.getVisibility() == View.VISIBLE) {
+                    child.startAnimation(mOutAnimation);
+                } else if (child.getAnimation() == mInAnimation)
+                    child.clearAnimation();
+                child.setVisibility(View.GONE);
+            }
+        }
+    }
+    /**
      * Shows only the specified child. The other displays Views exit the screen
      * with the {@link #getOutAnimation() out animation} and the specified child
      * enters the screen with the {@link #getInAnimation() in animation}.
@@ -135,24 +170,8 @@
      * @param childIndex The index of the child to be shown.
      */
     void showOnly(int childIndex) {
-        final int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            final View child = getChildAt(i);
-            final boolean checkForFirst = (!mFirstTime || mAnimateFirstTime);
-            if (i == childIndex) {
-                if (checkForFirst && mInAnimation != null) {
-                    child.startAnimation(mInAnimation);
-                }
-                child.setVisibility(View.VISIBLE);
-                mFirstTime = false;
-            } else {
-                if (checkForFirst && mOutAnimation != null && child.getVisibility() == View.VISIBLE) {
-                    child.startAnimation(mOutAnimation);
-                } else if (child.getAnimation() == mInAnimation)
-                    child.clearAnimation();
-                child.setVisibility(View.GONE);
-            }
-        }
+        final boolean animate = (!mFirstTime || mAnimateFirstTime);
+        showOnly(childIndex, animate);
     }
 
     @Override
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index 8034961..c6f6e81 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -75,7 +75,7 @@
                 updateRunning();
             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                 mUserPresent = true;
-                updateRunning();
+                updateRunning(false);
             }
         }
     };
@@ -109,7 +109,7 @@
     protected void onWindowVisibilityChanged(int visibility) {
         super.onWindowVisibilityChanged(visibility);
         mVisible = visibility == VISIBLE;
-        updateRunning();
+        updateRunning(false);
     }
 
     /**
@@ -144,10 +144,22 @@
      * on {@link #mRunning} and {@link #mVisible} state.
      */
     private void updateRunning() {
+        updateRunning(true);
+    }
+
+    /**
+     * Internal method to start or stop dispatching flip {@link Message} based
+     * on {@link #mRunning} and {@link #mVisible} state.
+     *
+     * @param flipNow Determines whether or not to execute the animation now, in
+     *            addition to queuing future flips. If omitted, defaults to
+     *            true.
+     */
+    private void updateRunning(boolean flipNow) {
         boolean running = mVisible && mStarted && mUserPresent;
         if (running != mRunning) {
             if (running) {
-                showOnly(mWhichChild);
+                showOnly(mWhichChild, flipNow);
                 Message msg = mHandler.obtainMessage(FLIP_MSG);
                 mHandler.sendMessageDelayed(msg, mFlipInterval);
             } else {
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index 3df419a..450c966 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -66,8 +66,9 @@
  * {@link #setZoomInEnabled(boolean)} and {@link #setZoomOutEnabled(boolean)}.
  * <p>
  * If you are using this with a custom View, please call
- * {@link #setVisible(boolean) setVisible(false)} from the
- * {@link View#onDetachedFromWindow}.
+ * {@link #setVisible(boolean) setVisible(false)} from
+ * {@link View#onDetachedFromWindow} and from {@link View#onVisibilityChanged}
+ * when <code>visibility != View.VISIBLE</code>.
  *
  */
 public class ZoomButtonsController implements View.OnTouchListener {
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
new file mode 100644
index 0000000..7cf369f
--- /dev/null
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -0,0 +1,689 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuPopupHelper;
+import com.android.internal.view.menu.SubMenuBuilder;
+import com.android.internal.widget.ActionBarContextView;
+import com.android.internal.widget.ActionBarView;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.SpinnerAdapter;
+import android.widget.ViewAnimator;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * ActionBarImpl is the ActionBar implementation used
+ * by devices of all screen sizes. If it detects a compatible decor,
+ * it will split contextual modes across both the ActionBarView at
+ * the top of the screen and a horizontal LinearLayout at the bottom
+ * which is normally hidden.
+ */
+public class ActionBarImpl extends ActionBar {
+    private static final int NORMAL_VIEW = 0;
+    private static final int CONTEXT_VIEW = 1;
+
+    private Context mContext;
+    private Activity mActivity;
+    private Dialog mDialog;
+
+    private ViewAnimator mAnimatorView;
+    private ActionBarView mActionView;
+    private ActionBarContextView mUpperContextView;
+    private LinearLayout mLowerContextView;
+
+    private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
+
+    private TabImpl mSelectedTab;
+    private int mSavedTabPosition = INVALID_POSITION;
+    
+    private ActionMode mActionMode;
+    
+    private static final int CONTEXT_DISPLAY_NORMAL = 0;
+    private static final int CONTEXT_DISPLAY_SPLIT = 1;
+    
+    private static final int INVALID_POSITION = -1;
+
+    private int mContextDisplayMode;
+
+    private boolean mClosingContext;
+
+    final Handler mHandler = new Handler();
+    final Runnable mCloseContext = new Runnable() {
+        public void run() {
+            mUpperContextView.closeMode();
+            if (mLowerContextView != null) {
+                mLowerContextView.removeAllViews();
+            }
+            mClosingContext = false;
+        }
+    };
+
+    public ActionBarImpl(Activity activity) {
+        mActivity = activity;
+        init(activity.getWindow().getDecorView());
+    }
+
+    public ActionBarImpl(Dialog dialog) {
+        mDialog = dialog;
+        init(dialog.getWindow().getDecorView());
+    }
+
+    private void init(View decor) {
+        mContext = decor.getContext();
+        mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
+        mUpperContextView = (ActionBarContextView) decor.findViewById(
+                com.android.internal.R.id.action_context_bar);
+        mLowerContextView = (LinearLayout) decor.findViewById(
+                com.android.internal.R.id.lower_action_context_bar);
+        mAnimatorView = (ViewAnimator) decor.findViewById(
+                com.android.internal.R.id.action_bar_animator);
+
+        if (mActionView == null || mUpperContextView == null || mAnimatorView == null) {
+            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
+                    "with a compatible window decor layout");
+        }
+
+        mActionView.setContextView(mUpperContextView);
+        mContextDisplayMode = mLowerContextView == null ?
+                CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
+    }
+
+    @Override
+    public void setTitle(int resId) {
+        setTitle(mContext.getString(resId));
+    }
+
+    @Override
+    public void setSubtitle(int resId) {
+        setSubtitle(mContext.getString(resId));
+    }
+
+    public void setCustomNavigationMode(View view) {
+        cleanupTabs();
+        setCustomView(view);
+        setDisplayOptions(DISPLAY_SHOW_CUSTOM, DISPLAY_SHOW_CUSTOM | DISPLAY_SHOW_TITLE);
+        mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
+        mActionView.setCallback(null);
+    }
+
+    public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
+        setDropdownNavigationMode(adapter, callback, -1);
+    }
+
+    public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback,
+            int defaultSelectedPosition) {
+        cleanupTabs();
+        setDisplayOptions(0, DISPLAY_SHOW_CUSTOM | DISPLAY_SHOW_TITLE);
+        mActionView.setNavigationMode(NAVIGATION_MODE_LIST);
+        setListNavigationCallbacks(adapter, callback);
+        if (defaultSelectedPosition >= 0) {
+            mActionView.setDropdownSelectedPosition(defaultSelectedPosition);
+        }
+    }
+
+    public void setStandardNavigationMode() {
+        cleanupTabs();
+        setDisplayOptions(DISPLAY_SHOW_TITLE, DISPLAY_SHOW_TITLE | DISPLAY_SHOW_CUSTOM);
+        mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
+        mActionView.setCallback(null);
+    }
+
+    public void setSelectedNavigationItem(int position) {
+        switch (mActionView.getNavigationMode()) {
+        case NAVIGATION_MODE_TABS:
+            selectTab(mTabs.get(position));
+            break;
+        case NAVIGATION_MODE_LIST:
+            mActionView.setDropdownSelectedPosition(position);
+            break;
+        default:
+            throw new IllegalStateException(
+                    "setSelectedNavigationIndex not valid for current navigation mode");
+        }
+    }
+
+    public int getSelectedNavigationItem() {
+        return getSelectedNavigationIndex();
+    }
+
+    public void removeAllTabs() {
+        cleanupTabs();
+    }
+
+    private void cleanupTabs() {
+        if (mSelectedTab != null) {
+            selectTab(null);
+        }
+        mTabs.clear();
+        mActionView.removeAllTabs();
+        mSavedTabPosition = INVALID_POSITION;
+    }
+
+    public void setTitle(CharSequence title) {
+        mActionView.setTitle(title);
+    }
+
+    public void setSubtitle(CharSequence subtitle) {
+        mActionView.setSubtitle(subtitle);
+    }
+
+    public void setDisplayOptions(int options) {
+        mActionView.setDisplayOptions(options);
+    }
+
+    public void setDisplayOptions(int options, int mask) {
+        final int current = mActionView.getDisplayOptions(); 
+        mActionView.setDisplayOptions((options & mask) | (current & ~mask));
+    }
+
+    public void setBackgroundDrawable(Drawable d) {
+        mActionView.setBackgroundDrawable(d);
+    }
+
+    public View getCustomNavigationView() {
+        return mActionView.getCustomNavigationView();
+    }
+
+    public CharSequence getTitle() {
+        return mActionView.getTitle();
+    }
+
+    public CharSequence getSubtitle() {
+        return mActionView.getSubtitle();
+    }
+
+    public int getNavigationMode() {
+        return mActionView.getNavigationMode();
+    }
+
+    public int getDisplayOptions() {
+        return mActionView.getDisplayOptions();
+    }
+
+    public ActionMode startActionMode(ActionMode.Callback callback) {
+        if (mActionMode != null) {
+            mActionMode.finish();
+        }
+
+        // Don't wait for the close context mode animation to finish.
+        if (mClosingContext) {
+            mAnimatorView.clearAnimation();
+            mHandler.removeCallbacks(mCloseContext);
+            mCloseContext.run();
+        }
+
+        ActionMode mode = new ActionModeImpl(callback);
+        if (callback.onCreateActionMode(mode, mode.getMenu())) {
+            mode.invalidate();
+            mUpperContextView.initForMode(mode);
+            mAnimatorView.setDisplayedChild(CONTEXT_VIEW);
+            if (mLowerContextView != null) {
+                // TODO animate this
+                mLowerContextView.setVisibility(View.VISIBLE);
+            }
+            mActionMode = mode;
+            return mode;
+        }
+        return null;
+    }
+
+    private void configureTab(Tab tab, int position) {
+        final TabImpl tabi = (TabImpl) tab;
+        final ActionBar.TabListener callback = tabi.getCallback();
+
+        if (callback == null) {
+            throw new IllegalStateException("Action Bar Tab must have a Callback");
+        }
+
+        tabi.setPosition(position);
+        mTabs.add(position, tabi);
+
+        final int count = mTabs.size();
+        for (int i = position + 1; i < count; i++) {
+            mTabs.get(i).setPosition(i);
+        }
+    }
+
+    @Override
+    public void addTab(Tab tab) {
+        addTab(tab, mTabs.isEmpty());
+    }
+
+    @Override
+    public void addTab(Tab tab, int position) {
+        addTab(tab, position, mTabs.isEmpty());
+    }
+
+    @Override
+    public void addTab(Tab tab, boolean setSelected) {
+        mActionView.addTab(tab, setSelected);
+        configureTab(tab, mTabs.size());
+        if (setSelected) {
+            selectTab(tab);
+        }
+    }
+
+    @Override
+    public void addTab(Tab tab, int position, boolean setSelected) {
+        mActionView.addTab(tab, position, setSelected);
+        configureTab(tab, position);
+        if (setSelected) {
+            selectTab(tab);
+        }
+    }
+
+    @Override
+    public Tab newTab() {
+        return new TabImpl();
+    }
+
+    @Override
+    public void removeTab(Tab tab) {
+        removeTabAt(tab.getPosition());
+    }
+
+    @Override
+    public void removeTabAt(int position) {
+        int selectedTabPosition = mSelectedTab != null
+                ? mSelectedTab.getPosition() : mSavedTabPosition;
+        mActionView.removeTabAt(position);
+        mTabs.remove(position);
+
+        final int newTabCount = mTabs.size();
+        for (int i = position; i < newTabCount; i++) {
+            mTabs.get(i).setPosition(i);
+        }
+
+        if (selectedTabPosition == position) {
+            selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
+        }
+    }
+
+    @Override
+    public void setTabNavigationMode() {
+        if (mActivity == null) {
+            throw new IllegalStateException(
+                    "Tab navigation mode cannot be used outside of an Activity");
+        }
+        setDisplayOptions(0, DISPLAY_SHOW_TITLE | DISPLAY_SHOW_CUSTOM);
+        mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
+    }
+
+    @Override
+    public void selectTab(Tab tab) {
+        if (getNavigationMode() != NAVIGATION_MODE_TABS) {
+            mSavedTabPosition = tab != null ? tab.getPosition() : INVALID_POSITION;
+            return;
+        }
+
+        final FragmentTransaction trans = mActivity.getFragmentManager().openTransaction()
+                .disallowAddToBackStack();
+
+        if (mSelectedTab == tab) {
+            if (mSelectedTab != null) {
+                mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans);
+            }
+        } else {
+            mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
+            if (mSelectedTab != null) {
+                mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans);
+            }
+            mSelectedTab = (TabImpl) tab;
+            if (mSelectedTab != null) {
+                mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans);
+            }
+        }
+
+        if (!trans.isEmpty()) {
+            trans.commit();
+        }
+    }
+
+    @Override
+    public Tab getSelectedTab() {
+        return mSelectedTab;
+    }
+
+    @Override
+    public int getHeight() {
+        return mActionView.getHeight();
+    }
+
+    @Override
+    public void show() {
+        // TODO animate!
+        mAnimatorView.setVisibility(View.VISIBLE);
+    }
+
+    @Override
+    public void hide() {
+        // TODO animate!
+        mAnimatorView.setVisibility(View.GONE);
+    }
+
+    public boolean isShowing() {
+        return mAnimatorView.getVisibility() == View.VISIBLE;
+    }
+
+    /**
+     * @hide 
+     */
+    public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback {
+        private ActionMode.Callback mCallback;
+        private MenuBuilder mMenu;
+        private WeakReference<View> mCustomView;
+        
+        public ActionModeImpl(ActionMode.Callback callback) {
+            mCallback = callback;
+            mMenu = new MenuBuilder(mActionView.getContext())
+                    .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            mMenu.setCallback(this);
+        }
+
+        @Override
+        public MenuInflater getMenuInflater() {
+            return new MenuInflater(mContext);
+        }
+
+        @Override
+        public Menu getMenu() {
+            return mMenu;
+        }
+
+        @Override
+        public void finish() {
+            if (mActionMode != this) {
+                // Not the active action mode - no-op
+                return;
+            }
+
+            mCallback.onDestroyActionMode(this);
+            mAnimatorView.setDisplayedChild(NORMAL_VIEW);
+
+            // Clear out the context mode views after the animation finishes
+            mClosingContext = true;
+            mHandler.postDelayed(mCloseContext, mAnimatorView.getOutAnimation().getDuration());
+
+            if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) {
+                // TODO Animate this
+                mLowerContextView.setVisibility(View.GONE);
+            }
+            mActionMode = null;
+        }
+
+        @Override
+        public void invalidate() {
+            if (mCallback.onPrepareActionMode(this, mMenu)) {
+                // Refresh content in both context views
+            }
+        }
+
+        @Override
+        public void setCustomView(View view) {
+            mUpperContextView.setCustomView(view);
+            mCustomView = new WeakReference<View>(view);
+        }
+
+        @Override
+        public void setSubtitle(CharSequence subtitle) {
+            mUpperContextView.setSubtitle(subtitle);
+        }
+
+        @Override
+        public void setTitle(CharSequence title) {
+            mUpperContextView.setTitle(title);
+        }
+
+        @Override
+        public void setTitle(int resId) {
+            setTitle(mActivity.getString(resId));
+        }
+
+        @Override
+        public void setSubtitle(int resId) {
+            setSubtitle(mActivity.getString(resId));
+        }
+
+        @Override
+        public CharSequence getTitle() {
+            return mUpperContextView.getTitle();
+        }
+
+        @Override
+        public CharSequence getSubtitle() {
+            return mUpperContextView.getSubtitle();
+        }
+        
+        @Override
+        public View getCustomView() {
+            return mCustomView != null ? mCustomView.get() : null;
+        }
+
+        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+            return mCallback.onActionItemClicked(this, item);
+        }
+
+        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+        }
+
+        public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+            if (!subMenu.hasVisibleItems()) {
+                return true;
+            }
+
+            new MenuPopupHelper(mContext, subMenu).show();
+            return true;
+        }
+
+        public void onCloseSubMenu(SubMenuBuilder menu) {
+        }
+
+        public void onMenuModeChange(MenuBuilder menu) {
+            invalidate();
+            mUpperContextView.showOverflowMenu();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public class TabImpl extends ActionBar.Tab {
+        private ActionBar.TabListener mCallback;
+        private Object mTag;
+        private Drawable mIcon;
+        private CharSequence mText;
+        private int mPosition;
+        private View mCustomView;
+
+        @Override
+        public Object getTag() {
+            return mTag;
+        }
+
+        @Override
+        public Tab setTag(Object tag) {
+            mTag = tag;
+            return this;
+        }
+
+        public ActionBar.TabListener getCallback() {
+            return mCallback;
+        }
+
+        @Override
+        public Tab setTabListener(ActionBar.TabListener callback) {
+            mCallback = callback;
+            return this;
+        }
+
+        @Override
+        public View getCustomView() {
+            return mCustomView;
+        }
+
+        @Override
+        public Tab setCustomView(View view) {
+            mCustomView = view;
+            return this;
+        }
+
+        @Override
+        public Drawable getIcon() {
+            return mIcon;
+        }
+
+        @Override
+        public int getPosition() {
+            return mPosition;
+        }
+
+        public void setPosition(int position) {
+            mPosition = position;
+        }
+
+        @Override
+        public CharSequence getText() {
+            return mText;
+        }
+
+        @Override
+        public Tab setIcon(Drawable icon) {
+            mIcon = icon;
+            return this;
+        }
+
+        @Override
+        public Tab setText(CharSequence text) {
+            mText = text;
+            return this;
+        }
+
+        @Override
+        public void select() {
+            selectTab(this);
+        }
+    }
+
+    @Override
+    public void setCustomView(View view) {
+        mActionView.setCustomNavigationView(view);
+    }
+
+    @Override
+    public void setCustomView(View view, LayoutParams layoutParams) {
+        view.setLayoutParams(layoutParams);
+        mActionView.setCustomNavigationView(view);
+    }
+
+    @Override
+    public void setListNavigationCallbacks(SpinnerAdapter adapter, NavigationCallback callback) {
+        mActionView.setDropdownAdapter(adapter);
+        mActionView.setCallback(callback);
+    }
+
+    @Override
+    public int getSelectedNavigationIndex() {
+        switch (mActionView.getNavigationMode()) {
+            case NAVIGATION_MODE_TABS:
+                return mSelectedTab.getPosition();
+            case NAVIGATION_MODE_LIST:
+                return mActionView.getDropdownSelectedPosition();
+            default:
+                return -1;
+        }
+    }
+
+    @Override
+    public int getNavigationItemCount() {
+        switch (mActionView.getNavigationMode()) {
+            case NAVIGATION_MODE_TABS:
+                return mTabs.size();
+            case NAVIGATION_MODE_LIST:
+                SpinnerAdapter adapter = mActionView.getDropdownAdapter();
+                return adapter != null ? adapter.getCount() : 0;
+            default:
+                return 0;
+        }
+    }
+
+    @Override
+    public int getTabCount() {
+        return mTabs.size();
+    }
+
+    @Override
+    public void setNavigationMode(int mode) {
+        final int oldMode = mActionView.getNavigationMode();
+        switch (oldMode) {
+            case NAVIGATION_MODE_TABS:
+                mSavedTabPosition = getSelectedNavigationIndex();
+                selectTab(null);
+                break;
+        }
+        mActionView.setNavigationMode(mode);
+        switch (mode) {
+            case NAVIGATION_MODE_TABS:
+                if (mSavedTabPosition != INVALID_POSITION) {
+                    setSelectedNavigationItem(mSavedTabPosition);
+                    mSavedTabPosition = INVALID_POSITION;
+                }
+                break;
+        }
+    }
+
+    @Override
+    public Tab getTabAt(int index) {
+        return mTabs.get(index);
+    }
+
+    /**
+     * This fragment is added when we're keeping a back stack in a tab switch
+     * transaction. We use it to change the selected tab in the action bar view
+     * when we back out.
+     */
+    private class SwitchSelectedTabViewFragment extends Fragment {
+        private int mSelectedTabIndex;
+
+        public SwitchSelectedTabViewFragment(int oldSelectedTab) {
+            mSelectedTabIndex = oldSelectedTab;
+        }
+
+        @Override
+        public void onDetach() {
+            if (mSelectedTabIndex >= 0 && mSelectedTabIndex < getTabCount()) {
+                mActionView.setTabSelected(mSelectedTabIndex);
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index fefdcea..b2810b1 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -30,6 +30,7 @@
 import android.os.Message;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -165,6 +166,13 @@
         }
     }
 
+    private static boolean shouldCenterSingleButton(Context context) {
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogCenterButtons,
+                outValue, true);
+        return outValue.data != 0;
+    }
+
     public AlertController(Context context, DialogInterface di, Window window) {
         mContext = context;
         mDialogInterface = di;
@@ -508,16 +516,18 @@
             whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
         }
 
-        /*
-         * If we only have 1 button it should be centered on the layout and
-         * expand to fill 50% of the available space.
-         */
-        if (whichButtons == BIT_BUTTON_POSITIVE) {
-            centerButton(mButtonPositive);
-        } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
-            centerButton(mButtonNeutral);
-        } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
-            centerButton(mButtonNeutral);
+        if (shouldCenterSingleButton(mContext)) {
+            /*
+             * If we only have 1 button it should be centered on the layout and
+             * expand to fill 50% of the available space.
+             */
+            if (whichButtons == BIT_BUTTON_POSITIVE) {
+                centerButton(mButtonPositive);
+            } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
+                centerButton(mButtonNeutral);
+            } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
+                centerButton(mButtonNeutral);
+            }
         }
         
         return whichButtons != 0;
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
new file mode 100644
index 0000000..6f6b40b
--- /dev/null
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import com.android.internal.R;
+
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.ListFragment;
+import android.app.backup.BackupManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import java.text.Collator;
+import java.util.Arrays;
+import java.util.Locale;
+
+public class LocalePicker extends ListFragment {
+    private static final String TAG = "LocalePicker";
+    private static final boolean DEBUG = false;
+
+    public static interface LocaleSelectionListener {
+        // You can add any argument if you really need it...
+        public void onLocaleSelected(Locale locale);
+    }
+
+    LocaleSelectionListener mListener;  // default to null
+
+    public static class LocaleInfo implements Comparable<LocaleInfo> {
+        static final Collator sCollator = Collator.getInstance();
+
+        String label;
+        Locale locale;
+
+        public LocaleInfo(String label, Locale locale) {
+            this.label = label;
+            this.locale = locale;
+        }
+
+        public String getLabel() {
+            return label;
+        }
+
+        public Locale getLocale() {
+            return locale;
+        }
+
+        @Override
+        public String toString() {
+            return this.label;
+        }
+
+        @Override
+        public int compareTo(LocaleInfo another) {
+            return sCollator.compare(this.label, another.label);
+        }
+    }
+
+    /**
+     * Constructs an Adapter object containing Locale information. Content is sorted by
+     * {@link LocaleInfo#label}.
+     */
+    public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) {
+        final Resources resources = context.getResources();
+        final String[] locales = context.getAssets().getLocales();
+        final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
+        final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
+        Arrays.sort(locales);
+        final int origSize = locales.length;
+        final LocaleInfo[] preprocess = new LocaleInfo[origSize];
+        int finalSize = 0;
+        for (int i = 0 ; i < origSize; i++ ) {
+            final String s = locales[i];
+            final int len = s.length();
+            if (len == 5) {
+                String language = s.substring(0, 2);
+                String country = s.substring(3, 5);
+                final Locale l = new Locale(language, country);
+
+                if (finalSize == 0) {
+                    if (DEBUG) {
+                        Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
+                    }
+                    preprocess[finalSize++] =
+                            new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l);
+                } else {
+                    // check previous entry:
+                    //  same lang and a country -> upgrade to full name and
+                    //    insert ours with full name
+                    //  diff lang -> insert ours with lang-only name
+                    if (preprocess[finalSize-1].locale.getLanguage().equals(
+                            language)) {
+                        if (DEBUG) {
+                            Log.v(TAG, "backing up and fixing "+
+                                    preprocess[finalSize-1].label+" to "+
+                                    getDisplayName(preprocess[finalSize-1].locale,
+                                            specialLocaleCodes, specialLocaleNames));
+                        }
+                        preprocess[finalSize-1].label = toTitleCase(
+                                getDisplayName(preprocess[finalSize-1].locale,
+                                        specialLocaleCodes, specialLocaleNames));
+                        if (DEBUG) {
+                            Log.v(TAG, "  and adding "+ toTitleCase(
+                                    getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
+                        }
+                        preprocess[finalSize++] =
+                                new LocaleInfo(toTitleCase(
+                                        getDisplayName(
+                                                l, specialLocaleCodes, specialLocaleNames)), l);
+                    } else {
+                        String displayName;
+                        if (s.equals("zz_ZZ")) {
+                            displayName = "Pseudo...";
+                        } else {
+                            displayName = toTitleCase(l.getDisplayLanguage(l));
+                        }
+                        if (DEBUG) {
+                            Log.v(TAG, "adding "+displayName);
+                        }
+                        preprocess[finalSize++] = new LocaleInfo(displayName, l);
+                    }
+                }
+            }
+        }
+
+        final LocaleInfo[] localeInfos = new LocaleInfo[finalSize];
+        for (int i = 0; i < finalSize; i++) {
+            localeInfos[i] = preprocess[i];
+        }
+        Arrays.sort(localeInfos);
+        final int layoutId = R.layout.locale_picker_item;
+        final int fieldId = R.id.locale;
+        return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos);
+    }
+
+    private static String toTitleCase(String s) {
+        if (s.length() == 0) {
+            return s;
+        }
+
+        return Character.toUpperCase(s.charAt(0)) + s.substring(1);
+    }
+
+    private static String getDisplayName(
+            Locale l, String[] specialLocaleCodes, String[] specialLocaleNames) {
+        String code = l.toString();
+
+        for (int i = 0; i < specialLocaleCodes.length; i++) {
+            if (specialLocaleCodes[i].equals(code)) {
+                return specialLocaleNames[i];
+            }
+        }
+
+        return l.getDisplayName(l);
+    }
+
+    @Override
+    public void onActivityCreated(final Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        final ArrayAdapter<LocaleInfo> adapter = constructAdapter(getActivity());
+        setListAdapter(adapter);
+    }
+
+    public void setLocaleSelectionListener(LocaleSelectionListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        getListView().requestFocus();
+    }
+
+    /**
+     * Each listener needs to call {@link #updateLocale(Locale)} to actually change the locale.
+     *
+     * We don't call {@link #updateLocale(Locale)} automatically, as it halt the system for
+     * a moment and some callers won't want it.
+     */
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        if (mListener != null) {
+            final Locale locale = ((LocaleInfo)getListAdapter().getItem(position)).locale;
+            mListener.onLocaleSelected(locale);
+        }
+    }
+
+    /**
+     * Requests the system to update the system locale. Note that the system looks halted
+     * for a while during the Locale migration, so the caller need to take care of it.
+     */
+    public static void updateLocale(Locale locale) {
+        try {
+            IActivityManager am = ActivityManagerNative.getDefault();
+            Configuration config = am.getConfiguration();
+
+            config.locale = locale;
+
+            // indicate this isn't some passing default - the user wants this remembered
+            config.userSetLocale = true;
+
+            am.updateConfiguration(config);
+            // Trigger the dirty bit for the Settings Provider.
+            BackupManager.dataChanged("com.android.providers.settings");
+        } catch (RemoteException e) {
+            // Intentionally left blank
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index cd1b569..cbdb947 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -18,15 +18,17 @@
 package com.android.internal.app;
 
 import android.app.ActivityManagerNative;
+import android.app.AlertDialog;
+import android.app.Dialog;
 import android.app.IActivityManager;
 import android.app.ProgressDialog;
-import android.app.AlertDialog;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.IBluetooth;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.Handler;
 import android.os.Power;
 import android.os.PowerManager;
@@ -96,13 +98,20 @@
             }
         }
 
-        Log.d(TAG, "Notifying thread to start radio shutdown");
+        final int longPressBehavior = context.getResources().getInteger(
+                        com.android.internal.R.integer.config_longPressOnPowerBehavior);
+        final int resourceId = longPressBehavior == 2
+                ? com.android.internal.R.string.shutdown_confirm_question
+                : com.android.internal.R.string.shutdown_confirm;
+
+        Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
 
         if (confirm) {
+            final CloseDialogReceiver closer = new CloseDialogReceiver(context);
             final AlertDialog dialog = new AlertDialog.Builder(context)
                     .setIcon(android.R.drawable.ic_dialog_alert)
                     .setTitle(com.android.internal.R.string.power_off)
-                    .setMessage(com.android.internal.R.string.shutdown_confirm)
+                    .setMessage(resourceId)
                     .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                         public void onClick(DialogInterface dialog, int which) {
                             beginShutdownSequence(context);
@@ -110,6 +119,8 @@
                     })
                     .setNegativeButton(com.android.internal.R.string.no, null)
                     .create();
+            closer.dialog = dialog;
+            dialog.setOnDismissListener(closer);
             dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
             if (!context.getResources().getBoolean(
                     com.android.internal.R.bool.config_sf_slowBlur)) {
@@ -121,6 +132,27 @@
         }
     }
 
+    private static class CloseDialogReceiver extends BroadcastReceiver
+            implements DialogInterface.OnDismissListener {
+        private Context mContext;
+        public Dialog dialog;
+
+        CloseDialogReceiver(Context context) {
+            mContext = context;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+            context.registerReceiver(this, filter);
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            dialog.cancel();
+        }
+
+        public void onDismiss(DialogInterface unused) {
+            mContext.unregisterReceiver(this);
+        }
+    }
+
     /**
      * Request a clean shutdown, waiting for subsystems to clean up their
      * state etc.  Must be called from a Looper thread in which its UI
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
index 2ed4773..216d985 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
@@ -24,5 +24,6 @@
 oneway interface IAppWidgetHost {
     void updateAppWidget(int appWidgetId, in RemoteViews views);
     void providerChanged(int appWidgetId, in AppWidgetProviderInfo info);
+    void viewDataChanged(int appWidgetId, int viewId);
 }
 
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 496aa1a..4d56745 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -40,7 +40,9 @@
     // for AppWidgetManager
     //
     void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views);
+    void partiallyUpdateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views);
     void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views);
+    void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, int viewId);
     List<AppWidgetProviderInfo> getInstalledProviders();
     AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId);
     void bindAppWidgetId(int appWidgetId, in ComponentName provider);
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 00c4dbe..4ae55fc 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -259,6 +259,7 @@
                 File destFile = new File(sharedLibraryDir, entry.second);
                 copyNativeBinaryLI(zipFile, entry.first, sharedLibraryDir, destFile);
             }
+            zipFile.close();
         } catch (ZipException e) {
             Slog.w(TAG, "Failed to extract data from package file", e);
             return PackageManager.INSTALL_FAILED_INVALID_APK;
diff --git a/core/java/com/android/internal/content/SyncStateContentProviderHelper.java b/core/java/com/android/internal/content/SyncStateContentProviderHelper.java
deleted file mode 100644
index 274082c..0000000
--- a/core/java/com/android/internal/content/SyncStateContentProviderHelper.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.content;
-
-import com.android.internal.util.ArrayUtils;
-
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.accounts.Account;
-import android.content.ContentValues;
-import android.provider.SyncStateContract;
-
-/**
- * Extends the schema of a ContentProvider to include the _sync_state table
- * and implements query/insert/update/delete to access that table using the
- * authority "syncstate". This can be used to store the sync state for a
- * set of accounts.
- *
- * @hide
- */
-public class SyncStateContentProviderHelper {
-    private static final String SELECT_BY_ACCOUNT =
-            SyncStateContract.Columns.ACCOUNT_NAME + "=? AND "
-                    + SyncStateContract.Columns.ACCOUNT_TYPE + "=?";
-
-    private static final String SYNC_STATE_TABLE = "_sync_state";
-    private static final String SYNC_STATE_META_TABLE = "_sync_state_metadata";
-    private static final String SYNC_STATE_META_VERSION_COLUMN = "version";
-
-    private static long DB_VERSION = 1;
-
-    private static final String[] ACCOUNT_PROJECTION =
-            new String[]{SyncStateContract.Columns.ACCOUNT_NAME,
-                    SyncStateContract.Columns.ACCOUNT_TYPE};
-
-    public static final String PATH = "syncstate";
-
-    private static final String QUERY_COUNT_SYNC_STATE_ROWS =
-            "SELECT count(*)"
-                    + " FROM " + SYNC_STATE_TABLE
-                    + " WHERE " + SyncStateContract.Columns._ID + "=?";
-
-    public void createDatabase(SQLiteDatabase db) {
-        db.execSQL("DROP TABLE IF EXISTS " + SYNC_STATE_TABLE);
-        db.execSQL("CREATE TABLE " + SYNC_STATE_TABLE + " ("
-                + SyncStateContract.Columns._ID + " INTEGER PRIMARY KEY,"
-                + SyncStateContract.Columns.ACCOUNT_NAME + " TEXT NOT NULL,"
-                + SyncStateContract.Columns.ACCOUNT_TYPE + " TEXT NOT NULL,"
-                + SyncStateContract.Columns.DATA + " TEXT,"
-                + "UNIQUE(" + SyncStateContract.Columns.ACCOUNT_NAME + ", "
-                + SyncStateContract.Columns.ACCOUNT_TYPE + "));");
-
-        db.execSQL("DROP TABLE IF EXISTS " + SYNC_STATE_META_TABLE);
-        db.execSQL("CREATE TABLE " + SYNC_STATE_META_TABLE + " ("
-                + SYNC_STATE_META_VERSION_COLUMN + " INTEGER);");
-        ContentValues values = new ContentValues();
-        values.put(SYNC_STATE_META_VERSION_COLUMN, DB_VERSION);
-        db.insert(SYNC_STATE_META_TABLE, SYNC_STATE_META_VERSION_COLUMN, values);
-    }
-
-    public void onDatabaseOpened(SQLiteDatabase db) {
-        long version = DatabaseUtils.longForQuery(db,
-                "SELECT " + SYNC_STATE_META_VERSION_COLUMN + " FROM " + SYNC_STATE_META_TABLE,
-                null);
-        if (version != DB_VERSION) {
-            createDatabase(db);
-        }
-    }
-
-    public Cursor query(SQLiteDatabase db, String[] projection,
-            String selection, String[] selectionArgs, String sortOrder) {
-        return db.query(SYNC_STATE_TABLE, projection, selection, selectionArgs,
-                null, null, sortOrder);
-    }
-
-    public long insert(SQLiteDatabase db, ContentValues values) {
-        return db.replace(SYNC_STATE_TABLE, SyncStateContract.Columns.ACCOUNT_NAME, values);
-    }
-
-    public int delete(SQLiteDatabase db, String userWhere, String[] whereArgs) {
-        return db.delete(SYNC_STATE_TABLE, userWhere, whereArgs);
-    }
-
-    public int update(SQLiteDatabase db, ContentValues values,
-            String selection, String[] selectionArgs) {
-        return db.update(SYNC_STATE_TABLE, values, selection, selectionArgs);
-    }
-
-    public int update(SQLiteDatabase db, long rowId, Object data) {
-        if (DatabaseUtils.longForQuery(db, QUERY_COUNT_SYNC_STATE_ROWS,
-                new String[]{Long.toString(rowId)}) < 1) {
-            return 0;
-        }
-        db.execSQL("UPDATE " + SYNC_STATE_TABLE
-                + " SET " + SyncStateContract.Columns.DATA + "=?"
-                + " WHERE " + SyncStateContract.Columns._ID + "=" + rowId,
-                new Object[]{data});
-        // assume a row was modified since we know it exists
-        return 1;
-    }
-
-    public void onAccountsChanged(SQLiteDatabase db, Account[] accounts) {
-        Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null);
-        try {
-            while (c.moveToNext()) {
-                final String accountName = c.getString(0);
-                final String accountType = c.getString(1);
-                Account account = new Account(accountName, accountType);
-                if (!ArrayUtils.contains(accounts, account)) {
-                    db.delete(SYNC_STATE_TABLE, SELECT_BY_ACCOUNT,
-                            new String[]{accountName, accountType});
-                }
-            }
-        } finally {
-            c.close();
-        }
-    }
-}
\ No newline at end of file
diff --git a/core/java/com/android/internal/database/SortCursor.java b/core/java/com/android/internal/database/SortCursor.java
index 99410bc..0025512 100644
--- a/core/java/com/android/internal/database/SortCursor.java
+++ b/core/java/com/android/internal/database/SortCursor.java
@@ -182,24 +182,6 @@
     }
 
     @Override
-    public boolean deleteRow()
-    {
-        return mCursor.deleteRow();
-    }
-
-    @Override
-    public boolean commitUpdates() {
-        int length = mCursors.length;
-        for (int i = 0 ; i < length ; i++) {
-            if (mCursors[i] != null) {
-                mCursors[i].commitUpdates();
-            }
-        }
-        onChange(true);
-        return true;
-    }
-
-    @Override
     public String getString(int column)
     {
         return mCursor.getString(column);
@@ -236,6 +218,11 @@
     }
 
     @Override
+    public int getType(int column) {
+        return mCursor.getType(column);
+    }
+
+    @Override
     public boolean isNull(int column)
     {
         return mCursor.isNull(column);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 1f42fbd..630830d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.util.JournaledFile;
 
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.net.TrafficStats;
 import android.os.BatteryManager;
@@ -50,6 +51,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
@@ -63,16 +65,16 @@
     private static final String TAG = "BatteryStatsImpl";
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_HISTORY = false;
-    
+
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
-    private static final int MAGIC = 0xBA757475; // 'BATSTATS' 
+    private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
     private static final int VERSION = 52;
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
-    
+
     // No, really, THIS is the maximum number of items we will record in the history.
     private static final int MAX_MAX_HISTORY_ITEMS = 3000;
 
@@ -80,9 +82,9 @@
     // per uid; once the limit is reached, we batch the remaining wakelocks
     // in to one common name.
     private static final int MAX_WAKELOCKS_PER_UID = 30;
-    
+
     private static final String BATCHED_WAKELOCK_NAME = "*overflow*";
-    
+
     private static int sNumSpeedSteps;
 
     private final JournaledFile mFile;
@@ -145,9 +147,9 @@
     // These are the objects that will want to do something when the device
     // is unplugged from power.
     final ArrayList<Unpluggable> mUnpluggables = new ArrayList<Unpluggable>();
-    
+
     boolean mShuttingDown;
-    
+
     long mHistoryBaseTime;
     boolean mHaveBatteryLevel = false;
     boolean mRecordingHistory = true;
@@ -157,7 +159,7 @@
     HistoryItem mHistoryLastEnd;
     HistoryItem mHistoryCache;
     final HistoryItem mHistoryCur = new HistoryItem();
-    
+
     int mStartCount;
 
     long mBatteryUptime;
@@ -171,41 +173,41 @@
     long mRealtime;
     long mRealtimeStart;
     long mLastRealtime;
-    
+
     boolean mScreenOn;
     StopwatchTimer mScreenOnTimer;
 
     int mScreenBrightnessBin = -1;
     final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
-    
+
     Counter mInputEventCounter;
-    
+
     boolean mPhoneOn;
     StopwatchTimer mPhoneOnTimer;
-    
+
     boolean mAudioOn;
     StopwatchTimer mAudioOnTimer;
-    
+
     boolean mVideoOn;
     StopwatchTimer mVideoOnTimer;
-    
+
     int mPhoneSignalStrengthBin = -1;
-    final StopwatchTimer[] mPhoneSignalStrengthsTimer = 
+    final StopwatchTimer[] mPhoneSignalStrengthsTimer =
             new StopwatchTimer[NUM_SIGNAL_STRENGTH_BINS];
 
     StopwatchTimer mPhoneSignalScanningTimer;
 
     int mPhoneDataConnectionType = -1;
-    final StopwatchTimer[] mPhoneDataConnectionsTimer = 
+    final StopwatchTimer[] mPhoneDataConnectionsTimer =
             new StopwatchTimer[NUM_DATA_CONNECTION_TYPES];
-    
+
     boolean mWifiOn;
     StopwatchTimer mWifiOnTimer;
     int mWifiOnUid = -1;
 
     boolean mGlobalWifiRunning;
     StopwatchTimer mGlobalWifiRunningTimer;
-    
+
     boolean mBluetoothOn;
     StopwatchTimer mBluetoothOnTimer;
 
@@ -254,15 +256,15 @@
     /*
      * Holds a SamplingTimer associated with each kernel wakelock name being tracked.
      */
-    private final HashMap<String, SamplingTimer> mKernelWakelockStats = 
+    private final HashMap<String, SamplingTimer> mKernelWakelockStats =
             new HashMap<String, SamplingTimer>();
-    
+
     public Map<String, ? extends SamplingTimer> getKernelWakelockStats() {
         return mKernelWakelockStats;
     }
-    
+
     private static int sKernelWakelockUpdateVersion = 0;
-    
+
     private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
         Process.PROC_TAB_TERM|Process.PROC_OUT_STRING,                // 0: name
         Process.PROC_TAB_TERM|Process.PROC_OUT_LONG,                  // 1: count
@@ -271,19 +273,19 @@
         Process.PROC_TAB_TERM,
         Process.PROC_TAB_TERM|Process.PROC_OUT_LONG,                  // 5: totalTime
     };
-    
+
     private final String[] mProcWakelocksName = new String[3];
     private final long[] mProcWakelocksData = new long[3];
-    
+
     /*
      * Used as a buffer for reading in data from /proc/wakelocks before it is processed and added
      * to mKernelWakelockStats.
      */
-    private final Map<String, KernelWakelockStats> mProcWakelockFileStats = 
+    private final Map<String, KernelWakelockStats> mProcWakelockFileStats =
             new HashMap<String, KernelWakelockStats>();
 
     private HashMap<String, Integer> mUidCache = new HashMap<String, Integer>();
-    
+
     // For debugging
     public BatteryStatsImpl() {
         mFile = null;
@@ -294,7 +296,7 @@
         void unplug(long batteryUptime, long batteryRealtime);
         void plug(long batteryUptime, long batteryRealtime);
     }
-    
+
     /**
      * State for keeping track of counting information.
      */
@@ -305,7 +307,7 @@
         int mLastCount;
         int mUnpluggedCount;
         int mPluggedCount;
-        
+
         Counter(ArrayList<Unpluggable> unpluggables, Parcel in) {
             mUnpluggables = unpluggables;
             mPluggedCount = in.readInt();
@@ -320,7 +322,7 @@
             mUnpluggables = unpluggables;
             unpluggables.add(this);
         }
-        
+
         public void writeToParcel(Parcel out) {
             out.writeInt(mCount.get());
             out.writeInt(mLoadedCount);
@@ -335,7 +337,7 @@
         public void plug(long batteryUptime, long batteryRealtime) {
             mPluggedCount = mCount.get();
         }
-        
+
         /**
          * Writes a possibly null Counter to a Parcel.
          *
@@ -375,7 +377,7 @@
                     + " mUnpluggedCount=" + mUnpluggedCount
                     + " mPluggedCount=" + mPluggedCount);
         }
-        
+
         void stepAtomic() {
             mCount.incrementAndGet();
         }
@@ -390,11 +392,11 @@
                 detach();
             }
         }
-        
+
         void detach() {
             mUnpluggables.remove(this);
         }
-        
+
         void writeSummaryFromParcelLocked(Parcel out) {
             int count = mCount.get();
             out.writeInt(count);
@@ -429,41 +431,41 @@
     public static abstract class Timer extends BatteryStats.Timer implements Unpluggable {
         final int mType;
         final ArrayList<Unpluggable> mUnpluggables;
-        
+
         int mCount;
         int mLoadedCount;
         int mLastCount;
         int mUnpluggedCount;
-        
+
         // Times are in microseconds for better accuracy when dividing by the
         // lock count, and are in "battery realtime" units.
-        
+
         /**
          * The total time we have accumulated since the start of the original
          * boot, to the last time something interesting happened in the
          * current run.
          */
         long mTotalTime;
-        
+
         /**
          * The total time we loaded for the previous runs.  Subtract this from
          * mTotalTime to find the time for the current run of the system.
          */
         long mLoadedTime;
-        
+
         /**
          * The run time of the last run of the system, as loaded from the
          * saved data.
          */
         long mLastTime;
-        
+
         /**
          * The value of mTotalTime when unplug() was last called.  Subtract
          * this from mTotalTime to find the time since the last unplug from
          * power.
          */
         long mUnpluggedTime;
-        
+
         /**
          * Constructs from a parcel.
          * @param type
@@ -474,7 +476,7 @@
         Timer(int type, ArrayList<Unpluggable> unpluggables, Parcel in) {
             mType = type;
             mUnpluggables = unpluggables;
-            
+
             mCount = in.readInt();
             mLoadedCount = in.readInt();
             mLastCount = 0;
@@ -493,9 +495,9 @@
         }
 
         protected abstract long computeRunTimeLocked(long curBatteryRealtime);
-        
+
         protected abstract int computeCurrentCountLocked();
-        
+
         /**
          * Clear state of this timer.  Returns true if the timer is inactive
          * so can be completely dropped.
@@ -508,11 +510,11 @@
             }
             return true;
         }
-        
+
         void detach() {
             mUnpluggables.remove(this);
         }
-        
+
         public void writeToParcel(Parcel out, long batteryRealtime) {
             out.writeInt(mCount);
             out.writeInt(mLoadedCount);
@@ -549,7 +551,7 @@
                         + ": new mTotalTime=" + mTotalTime);
             }
         }
-        
+
         /**
          * Writes a possibly null Timer to a Parcel.
          *
@@ -610,8 +612,8 @@
             pw.println(prefix + "mLastTime=" + mLastTime
                     + " mUnpluggedTime=" + mUnpluggedTime);
         }
-        
-        
+
+
         void writeSummaryFromParcelLocked(Parcel out, long batteryRealtime) {
             long runTime = computeRunTimeLocked(batteryRealtime);
             // Divide by 1000 for backwards compatibility
@@ -629,9 +631,9 @@
             mUnpluggedCount = mCount;
         }
     }
-    
+
     public static final class SamplingTimer extends Timer {
-        
+
         /**
          * The most recent reported count from /proc/wakelocks.
          */
@@ -645,7 +647,7 @@
 
         /**
          * The most recent reported total_time from /proc/wakelocks.
-         */ 
+         */
         long mCurrentReportedTotalTime;
 
 
@@ -664,12 +666,12 @@
          * Whether we are currently recording reported values.
          */
         boolean mTrackingReportedValues;
-        
+
         /*
          * A sequnce counter, incremented once for each update of the stats.
          */
         int mUpdateVersion;
-        
+
         SamplingTimer(ArrayList<Unpluggable> unpluggables, boolean inDischarge, Parcel in) {
             super(0, unpluggables, in);
             mCurrentReportedCount = in.readInt();
@@ -679,28 +681,28 @@
             mTrackingReportedValues = in.readInt() == 1;
             mInDischarge = inDischarge;
         }
-        
-        SamplingTimer(ArrayList<Unpluggable> unpluggables, boolean inDischarge, 
+
+        SamplingTimer(ArrayList<Unpluggable> unpluggables, boolean inDischarge,
                 boolean trackReportedValues) {
             super(0, unpluggables);
             mTrackingReportedValues = trackReportedValues;
             mInDischarge = inDischarge;
         }
-        
+
         public void setStale() {
             mTrackingReportedValues = false;
             mUnpluggedReportedTotalTime = 0;
             mUnpluggedReportedCount = 0;
         }
-        
+
         public void setUpdateVersion(int version) {
             mUpdateVersion = version;
         }
-        
+
         public int getUpdateVersion() {
             return mUpdateVersion;
         }
-        
+
         public void updateCurrentReportedCount(int count) {
             if (mInDischarge && mUnpluggedReportedCount == 0) {
                 // Updating the reported value for the first time.
@@ -710,7 +712,7 @@
             }
             mCurrentReportedCount = count;
         }
-        
+
         public void updateCurrentReportedTotalTime(long totalTime) {
             if (mInDischarge && mUnpluggedReportedTotalTime == 0) {
                 // Updating the reported value for the first time.
@@ -720,7 +722,7 @@
             }
             mCurrentReportedTotalTime = totalTime;
         }
-        
+
         public void unplug(long batteryUptime, long batteryRealtime) {
             super.unplug(batteryUptime, batteryRealtime);
             if (mTrackingReportedValues) {
@@ -734,25 +736,25 @@
             super.plug(batteryUptime, batteryRealtime);
             mInDischarge = false;
         }
-        
+
         public void logState(Printer pw, String prefix) {
             super.logState(pw, prefix);
-            pw.println(prefix + "mCurrentReportedCount=" + mCurrentReportedCount 
+            pw.println(prefix + "mCurrentReportedCount=" + mCurrentReportedCount
                     + " mUnpluggedReportedCount=" + mUnpluggedReportedCount
                     + " mCurrentReportedTotalTime=" + mCurrentReportedTotalTime
                     + " mUnpluggedReportedTotalTime=" + mUnpluggedReportedTotalTime);
         }
-        
+
         protected long computeRunTimeLocked(long curBatteryRealtime) {
-            return mTotalTime + (mInDischarge && mTrackingReportedValues 
+            return mTotalTime + (mInDischarge && mTrackingReportedValues
                     ? mCurrentReportedTotalTime - mUnpluggedReportedTotalTime : 0);
         }
-        
+
         protected int computeCurrentCountLocked() {
             return mCount + (mInDischarge && mTrackingReportedValues
                     ? mCurrentReportedCount - mUnpluggedReportedCount : 0);
         }
-        
+
         public void writeToParcel(Parcel out, long batteryRealtime) {
             super.writeToParcel(out, batteryRealtime);
             out.writeInt(mCurrentReportedCount);
@@ -761,13 +763,13 @@
             out.writeLong(mUnpluggedReportedTotalTime);
             out.writeInt(mTrackingReportedValues ? 1 : 0);
         }
-        
+
         boolean reset(BatteryStatsImpl stats, boolean detachIfReset) {
             super.reset(stats, detachIfReset);
             setStale();
             return true;
         }
-        
+
         void writeSummaryFromParcelLocked(Parcel out, long batteryRealtime) {
             super.writeSummaryFromParcelLocked(out, batteryRealtime);
             out.writeLong(mCurrentReportedTotalTime);
@@ -782,7 +784,7 @@
             mTrackingReportedValues = in.readInt() == 1;
         }
     }
-    
+
     /**
      * State for keeping track of timing information.
      */
@@ -798,7 +800,7 @@
          * time we have been running since we last computed an update.
          */
         long mUpdateTime;
-        
+
         /**
          * The total time at which the timer was acquired, to determine if it
          * was actually held for an interesting duration.
@@ -827,7 +829,7 @@
             mUid = uid;
             mTimerPool = timerPool;
         }
-        
+
         void setTimeout(long timeout) {
             mTimeout = timeout;
         }
@@ -855,7 +857,7 @@
             pw.println(prefix + "mNesting=" + mNesting + "mUpdateTime=" + mUpdateTime
                     + " mAcquireTime=" + mAcquireTime);
         }
-        
+
         void startRunningLocked(BatteryStatsImpl stats) {
             if (mNesting++ == 0) {
                 mUpdateTime = stats.getBatteryRealtimeLocked(
@@ -895,19 +897,19 @@
                     // Remove this timer from the active pool
                     mTimerPool.remove(this);
                 } else {
-                    final long realtime = SystemClock.elapsedRealtime() * 1000; 
+                    final long realtime = SystemClock.elapsedRealtime() * 1000;
                     final long batteryRealtime = stats.getBatteryRealtimeLocked(realtime);
                     mNesting = 1;
                     mTotalTime = computeRunTimeLocked(batteryRealtime);
                     mNesting = 0;
                 }
-                
+
                 if (DEBUG && mType < 0) {
                     Log.v(TAG, "stop #" + mType + ": mUpdateTime=" + mUpdateTime
                             + " mTotalTime=" + mTotalTime + " mCount=" + mCount
                             + " mAcquireTime=" + mAcquireTime);
                 }
-                
+
                 if (mTotalTime == mAcquireTime) {
                     // If there was no change in the time, then discard this
                     // count.  A somewhat cheezy strategy, but hey.
@@ -920,7 +922,7 @@
         // due to a change in timer count
         private static void refreshTimersLocked(final BatteryStatsImpl stats,
                 final ArrayList<StopwatchTimer> pool) {
-            final long realtime = SystemClock.elapsedRealtime() * 1000; 
+            final long realtime = SystemClock.elapsedRealtime() * 1000;
             final long batteryRealtime = stats.getBatteryRealtimeLocked(realtime);
             final int N = pool.size();
             for (int i=N-1; i>= 0; i--) {
@@ -959,25 +961,25 @@
             mAcquireTime = mTotalTime;
             return canDetach;
         }
-        
+
         void detach() {
             super.detach();
             if (mTimerPool != null) {
                 mTimerPool.remove(this);
             }
         }
-        
+
         void readSummaryFromParcelLocked(Parcel in) {
             super.readSummaryFromParcelLocked(in);
             mNesting = 0;
         }
     }
-    
+
     private final Map<String, KernelWakelockStats> readKernelWakelockStats() {
-        
+
         byte[] buffer = new byte[8192];
         int len;
-        
+
         try {
             FileInputStream is = new FileInputStream("/proc/wakelocks");
             len = is.read(buffer);
@@ -997,10 +999,10 @@
         } catch (java.io.IOException e) {
             return null;
         }
-        
+
         return parseProcWakelocks(buffer, len);
     }
-    
+
     private final Map<String, KernelWakelockStats> parseProcWakelocks(
             byte[] wlBuffer, int len) {
         String name;
@@ -1016,11 +1018,11 @@
 
         synchronized(this) {
             Map<String, KernelWakelockStats> m = mProcWakelockFileStats;
-            
+
             sKernelWakelockUpdateVersion++;
             while (endIndex < len) {
-                for (endIndex=startIndex; 
-                        endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0'; 
+                for (endIndex=startIndex;
+                        endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0';
                         endIndex++);
                 endIndex++; // endIndex is an exclusive upper bound.
                 // Don't go over the end of the buffer, Process.parseProcLine might
@@ -1047,7 +1049,7 @@
 
                 if (parsed && name.length() > 0) {
                     if (!m.containsKey(name)) {
-                        m.put(name, new KernelWakelockStats(count, totalTime, 
+                        m.put(name, new KernelWakelockStats(count, totalTime,
                                 sKernelWakelockUpdateVersion));
                         numUpdatedWlNames++;
                     } else {
@@ -1078,27 +1080,27 @@
             return m;
         }
     }
-    
+
     private class KernelWakelockStats {
         public int mCount;
         public long mTotalTime;
         public int mVersion;
-        
+
         KernelWakelockStats(int count, long totalTime, int version) {
             mCount = count;
             mTotalTime = totalTime;
             mVersion = version;
         }
     }
-    
+
     /*
-     * Get the KernelWakelockTimer associated with name, and create a new one if one 
+     * Get the KernelWakelockTimer associated with name, and create a new one if one
      * doesn't already exist.
      */
     public SamplingTimer getKernelWakelockTimerLocked(String name) {
         SamplingTimer kwlt = mKernelWakelockStats.get(name);
         if (kwlt == null) {
-            kwlt = new SamplingTimer(mUnpluggables, mOnBatteryInternal, 
+            kwlt = new SamplingTimer(mUnpluggables, mOnBatteryInternal,
                     true /* track reported values */);
             mKernelWakelockStats.put(name, kwlt);
         }
@@ -1142,7 +1144,7 @@
     }
 
     /**
-     * Returns the duration that the cell radio was up for data transfers. 
+     * Returns the duration that the cell radio was up for data transfers.
      */
     public long getRadioDataUptime() {
         if (mRadioDataStart == -1) {
@@ -1154,7 +1156,10 @@
 
     private int getCurrentBluetoothPingCount() {
         if (mBtHeadset != null) {
-            return mBtHeadset.getBatteryUsageHint();
+            List<BluetoothDevice> deviceList = mBtHeadset.getConnectedDevices();
+            if (deviceList.size() > 0) {
+                return mBtHeadset.getBatteryUsageHint(deviceList.get(0));
+            }
         }
         return -1;
     }
@@ -1228,7 +1233,7 @@
 
         addHistoryRecordLocked(curTime, HistoryItem.CMD_UPDATE);
     }
-    
+
     void addHistoryRecordLocked(long curTime, byte cmd) {
         HistoryItem rec = mHistoryCache;
         if (rec != null) {
@@ -1237,10 +1242,10 @@
             rec = new HistoryItem();
         }
         rec.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
-        
+
         addHistoryRecordLocked(rec);
     }
-    
+
     void addHistoryRecordLocked(HistoryItem rec) {
         mNumHistoryItems++;
         rec.next = null;
@@ -1252,7 +1257,7 @@
             mHistory = mHistoryEnd = rec;
         }
     }
-    
+
     void clearHistoryLocked() {
         if (mHistory != null) {
             mHistoryEnd.next = mHistoryCache;
@@ -1262,7 +1267,7 @@
         mNumHistoryItems = 0;
         mHistoryBaseTime = 0;
     }
-    
+
     public void doUnplugLocked(long batteryUptime, long batteryRealtime) {
         for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
             Uid u = mUidStats.valueAt(iu);
@@ -1526,7 +1531,7 @@
     }
 
     int mGpsNesting;
-    
+
     public void noteStartGpsLocked(int uid) {
         if (mGpsNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG;
@@ -1537,7 +1542,7 @@
         mGpsNesting++;
         getUidStatsLocked(uid).noteStartGps();
     }
-    
+
     public void noteStopGpsLocked(int uid) {
         mGpsNesting--;
         if (mGpsNesting == 0) {
@@ -1566,7 +1571,7 @@
             noteStartWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL);
         }
     }
-    
+
     public void noteScreenOffLocked() {
         if (mScreenOn) {
             mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
@@ -1582,7 +1587,7 @@
             noteStopWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL);
         }
     }
-    
+
     public void noteScreenBrightnessLocked(int brightness) {
         // Bin the brightness.
         int bin = brightness / (256/NUM_SCREEN_BRIGHTNESS_BINS);
@@ -1603,15 +1608,15 @@
             mScreenBrightnessBin = bin;
         }
     }
-    
+
     public void noteInputEventAtomic() {
         mInputEventCounter.stepAtomic();
     }
-    
+
     public void noteUserActivityLocked(int uid, int event) {
         getUidStatsLocked(uid).noteUserActivityLocked(event);
     }
-    
+
     public void notePhoneOnLocked() {
         if (!mPhoneOn) {
             mHistoryCur.states |= HistoryItem.STATE_PHONE_IN_CALL_FLAG;
@@ -1622,7 +1627,7 @@
             mPhoneOnTimer.startRunningLocked(this);
         }
     }
-    
+
     public void notePhoneOffLocked() {
         if (mPhoneOn) {
             mHistoryCur.states &= ~HistoryItem.STATE_PHONE_IN_CALL_FLAG;
@@ -1682,7 +1687,7 @@
                 mPhoneSignalScanningTimer.startRunningLocked(this);
             }
         }
-        
+
         if (!scanning) {
             // If we are no longer scanning, then stop the scanning timer.
             if (mPhoneSignalScanningTimer.isRunningLocked()) {
@@ -1740,7 +1745,7 @@
             mPhoneSignalStrengthsTimer[bin].startRunningLocked(this);
         }
     }
-    
+
     public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData) {
         int bin = DATA_CONNECTION_NONE;
         if (hasData) {
@@ -1781,6 +1786,12 @@
                 case TelephonyManager.NETWORK_TYPE_EVDO_B:
                     bin = DATA_CONNECTION_EVDO_B;
                     break;
+                case TelephonyManager.NETWORK_TYPE_LTE:
+                    bin = DATA_CONNECTION_LTE;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_EHRPD:
+                    bin = DATA_CONNECTION_EHRPD;
+                    break;
                 default:
                     bin = DATA_CONNECTION_OTHER;
                     break;
@@ -1800,7 +1811,7 @@
             mPhoneDataConnectionsTimer[bin].startRunningLocked(this);
         }
     }
-    
+
     public void noteWifiOnLocked() {
         if (!mWifiOn) {
             mHistoryCur.states |= HistoryItem.STATE_WIFI_ON_FLAG;
@@ -1811,7 +1822,7 @@
             mWifiOnTimer.startRunningLocked(this);
         }
     }
-    
+
     public void noteWifiOffLocked() {
         if (mWifiOn) {
             mHistoryCur.states &= ~HistoryItem.STATE_WIFI_ON_FLAG;
@@ -1838,7 +1849,7 @@
         }
         getUidStatsLocked(uid).noteAudioTurnedOnLocked();
     }
-    
+
     public void noteAudioOffLocked(int uid) {
         if (mAudioOn) {
             mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
@@ -1862,7 +1873,7 @@
         }
         getUidStatsLocked(uid).noteVideoTurnedOnLocked();
     }
-    
+
     public void noteVideoOffLocked(int uid) {
         if (mVideoOn) {
             mHistoryCur.states &= ~HistoryItem.STATE_VIDEO_ON_FLAG;
@@ -1934,7 +1945,7 @@
             mBluetoothOnTimer.startRunningLocked(this);
         }
     }
-    
+
     public void noteBluetoothOffLocked() {
         if (mBluetoothOn) {
             mHistoryCur.states &= ~HistoryItem.STATE_BLUETOOTH_ON_FLAG;
@@ -1945,9 +1956,9 @@
             mBluetoothOnTimer.stopRunningLocked(this);
         }
     }
-    
+
     int mWifiFullLockNesting = 0;
-    
+
     public void noteFullWifiLockAcquiredLocked(int uid) {
         if (mWifiFullLockNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
@@ -1971,7 +1982,7 @@
     }
 
     int mWifiScanLockNesting = 0;
-    
+
     public void noteScanWifiLockAcquiredLocked(int uid) {
         if (mWifiScanLockNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_LOCK_FLAG;
@@ -1995,7 +2006,7 @@
     }
 
     int mWifiMulticastNesting = 0;
-    
+
     public void noteWifiMulticastEnabledLocked(int uid) {
         if (mWifiMulticastNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
@@ -2063,7 +2074,7 @@
     @Override public long getScreenOnTime(long batteryRealtime, int which) {
         return mScreenOnTimer.getTotalTimeLocked(batteryRealtime, which);
     }
-    
+
     @Override public long getScreenBrightnessTime(int brightnessBin,
             long batteryRealtime, int which) {
         return mScreenBrightnessTimer[brightnessBin].getTotalTimeLocked(
@@ -2073,7 +2084,7 @@
     @Override public int getInputEventCount(int which) {
         return mInputEventCounter.getCountLocked(which);
     }
-    
+
     @Override public long getPhoneOnTime(long batteryRealtime, int which) {
         return mPhoneOnTimer.getTotalTimeLocked(batteryRealtime, which);
     }
@@ -2093,21 +2104,21 @@
     @Override public int getPhoneSignalStrengthCount(int dataType, int which) {
         return mPhoneDataConnectionsTimer[dataType].getCountLocked(which);
     }
-    
+
     @Override public long getPhoneDataConnectionTime(int dataType,
             long batteryRealtime, int which) {
         return mPhoneDataConnectionsTimer[dataType].getTotalTimeLocked(
                 batteryRealtime, which);
     }
-    
+
     @Override public int getPhoneDataConnectionCount(int dataType, int which) {
         return mPhoneDataConnectionsTimer[dataType].getCountLocked(which);
     }
-    
+
     @Override public long getWifiOnTime(long batteryRealtime, int which) {
         return mWifiOnTimer.getTotalTimeLocked(batteryRealtime, which);
     }
-    
+
     @Override public long getGlobalWifiRunningTime(long batteryRealtime, int which) {
         return mGlobalWifiRunningTimer.getTotalTimeLocked(batteryRealtime, which);
     }
@@ -2115,11 +2126,11 @@
     @Override public long getBluetoothOnTime(long batteryRealtime, int which) {
         return mBluetoothOnTimer.getTotalTimeLocked(batteryRealtime, which);
     }
-    
+
     @Override public boolean getIsOnBattery() {
         return mOnBattery;
     }
-    
+
     @Override public SparseArray<? extends BatteryStats.Uid> getUidStats() {
         return mUidStats;
     }
@@ -2128,7 +2139,7 @@
      * The statistics associated with a particular uid.
      */
     public final class Uid extends BatteryStats.Uid {
-        
+
         final int mUid;
         long mLoadedTcpBytesReceived;
         long mLoadedTcpBytesSent;
@@ -2136,32 +2147,32 @@
         long mCurrentTcpBytesSent;
         long mTcpBytesReceivedAtLastUnplug;
         long mTcpBytesSentAtLastUnplug;
-        
+
         // These are not saved/restored when parcelling, since we want
         // to return from the parcel with a snapshot of the state.
         long mStartedTcpBytesReceived = -1;
         long mStartedTcpBytesSent = -1;
-        
+
         boolean mWifiRunning;
         StopwatchTimer mWifiRunningTimer;
-        
+
         boolean mFullWifiLockOut;
         StopwatchTimer mFullWifiLockTimer;
-        
+
         boolean mScanWifiLockOut;
         StopwatchTimer mScanWifiLockTimer;
-        
+
         boolean mWifiMulticastEnabled;
         StopwatchTimer mWifiMulticastTimer;
-        
+
         boolean mAudioTurnedOn;
         StopwatchTimer mAudioTurnedOnTimer;
-        
+
         boolean mVideoTurnedOn;
         StopwatchTimer mVideoTurnedOnTimer;
 
         Counter[] mUserActivityCounters;
-        
+
         /**
          * The statistics we have collected for this uid's wake locks.
          */
@@ -2181,7 +2192,7 @@
          * The statistics we have collected for this uid's processes.
          */
         final HashMap<String, Pkg> mPackageStats = new HashMap<String, Pkg>();
-        
+
         /**
          * The transient wake stats we have collected for this uid's pids.
          */
@@ -2242,7 +2253,7 @@
                 return current;
             }
         }
-        
+
         public long computeCurrentTcpBytesReceived() {
             return mCurrentTcpBytesReceived + (mStartedTcpBytesReceived >= 0
                     ? (TrafficStats.getUidRxBytes(mUid) - mStartedTcpBytesReceived) : 0);
@@ -2262,7 +2273,7 @@
                 return current;
             }
         }
-        
+
         @Override
         public void noteWifiRunningLocked() {
             if (!mWifiRunning) {
@@ -2274,7 +2285,7 @@
                 mWifiRunningTimer.startRunningLocked(BatteryStatsImpl.this);
             }
         }
-        
+
         @Override
         public void noteWifiStoppedLocked() {
             if (mWifiRunning) {
@@ -2282,7 +2293,7 @@
                 mWifiRunningTimer.stopRunningLocked(BatteryStatsImpl.this);
             }
         }
-        
+
         @Override
         public void noteFullWifiLockAcquiredLocked() {
             if (!mFullWifiLockOut) {
@@ -2294,7 +2305,7 @@
                 mFullWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
             }
         }
-        
+
         @Override
         public void noteFullWifiLockReleasedLocked() {
             if (mFullWifiLockOut) {
@@ -2302,7 +2313,7 @@
                 mFullWifiLockTimer.stopRunningLocked(BatteryStatsImpl.this);
             }
         }
-        
+
         @Override
         public void noteScanWifiLockAcquiredLocked() {
             if (!mScanWifiLockOut) {
@@ -2314,7 +2325,7 @@
                 mScanWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
             }
         }
-        
+
         @Override
         public void noteScanWifiLockReleasedLocked() {
             if (mScanWifiLockOut) {
@@ -2383,7 +2394,7 @@
             }
         }
 
-        @Override 
+        @Override
         public long getWifiRunningTime(long batteryRealtime, int which) {
             if (mWifiRunningTimer == null) {
                 return 0;
@@ -2391,15 +2402,15 @@
             return mWifiRunningTimer.getTotalTimeLocked(batteryRealtime, which);
         }
 
-        @Override 
+        @Override
         public long getFullWifiLockTime(long batteryRealtime, int which) {
             if (mFullWifiLockTimer == null) {
                 return 0;
             }
             return mFullWifiLockTimer.getTotalTimeLocked(batteryRealtime, which);
         }
-        
-        @Override 
+
+        @Override
         public long getScanWifiLockTime(long batteryRealtime, int which) {
             if (mScanWifiLockTimer == null) {
                 return 0;
@@ -2416,7 +2427,7 @@
                                                           which);
         }
 
-        @Override 
+        @Override
         public long getAudioTurnedOnTime(long batteryRealtime, int which) {
             if (mAudioTurnedOnTimer == null) {
                 return 0;
@@ -2424,7 +2435,7 @@
             return mAudioTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
         }
 
-        @Override 
+        @Override
         public long getVideoTurnedOnTime(long batteryRealtime, int which) {
             if (mVideoTurnedOnTimer == null) {
                 return 0;
@@ -2441,12 +2452,12 @@
             else if (type >= NUM_USER_ACTIVITY_TYPES) type = NUM_USER_ACTIVITY_TYPES-1;
             mUserActivityCounters[type].stepAtomic();
         }
-        
+
         @Override
         public boolean hasUserActivity() {
             return mUserActivityCounters != null;
         }
-        
+
         @Override
         public int getUserActivityCount(int type, int which) {
             if (mUserActivityCounters == null) {
@@ -2454,14 +2465,14 @@
             }
             return mUserActivityCounters[type].getCountLocked(which);
         }
-        
+
         void initUserActivityLocked() {
             mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES];
             for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
                 mUserActivityCounters[i] = new Counter(mUnpluggables);
             }
         }
-        
+
         public long computeCurrentTcpBytesSent() {
             return mCurrentTcpBytesSent + (mStartedTcpBytesSent >= 0
                     ? (TrafficStats.getUidTxBytes(mUid) - mStartedTcpBytesSent) : 0);
@@ -2473,7 +2484,7 @@
          */
         boolean reset() {
             boolean active = false;
-            
+
             if (mWifiRunningTimer != null) {
                 active |= !mWifiRunningTimer.reset(BatteryStatsImpl.this, false);
                 active |= mWifiRunning;
@@ -2498,10 +2509,10 @@
                 active |= !mVideoTurnedOnTimer.reset(BatteryStatsImpl.this, false);
                 active |= mVideoTurnedOn;
             }
-            
+
             mLoadedTcpBytesReceived = mLoadedTcpBytesSent = 0;
             mCurrentTcpBytesReceived = mCurrentTcpBytesSent = 0;
-            
+
             if (mUserActivityCounters != null) {
                 for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
                     mUserActivityCounters[i].reset(false);
@@ -2565,7 +2576,7 @@
                 }
                 mPackageStats.clear();
             }
-            
+
             mPids.clear();
 
             if (!active) {
@@ -2593,10 +2604,10 @@
                     }
                 }
             }
-            
+
             return !active;
         }
-        
+
         void writeToParcelLocked(Parcel out, long batteryRealtime) {
             out.writeInt(mWakelockStats.size());
             for (Map.Entry<String, Uid.Wakelock> wakelockEntry : mWakelockStats.entrySet()) {
@@ -2625,7 +2636,7 @@
                 Uid.Pkg pkg = pkgEntry.getValue();
                 pkg.writeToParcelLocked(out);
             }
-            
+
             out.writeLong(mLoadedTcpBytesReceived);
             out.writeLong(mLoadedTcpBytesSent);
             out.writeLong(computeCurrentTcpBytesReceived());
@@ -2719,7 +2730,7 @@
                 pkg.readFromParcelLocked(in);
                 mPackageStats.put(packageName, pkg);
             }
-            
+
             mLoadedTcpBytesReceived = in.readLong();
             mLoadedTcpBytesSent = in.readLong();
             mCurrentTcpBytesReceived = in.readLong();
@@ -2840,7 +2851,7 @@
                 }
                 return !wlactive;
             }
-            
+
             void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
                 mTimerPartial = readTimerFromParcel(WAKE_TYPE_PARTIAL,
                         mPartialTimers, unpluggables, in);
@@ -2870,7 +2881,7 @@
         public final class Sensor extends BatteryStats.Uid.Sensor {
             final int mHandle;
             StopwatchTimer mTimer;
-            
+
             public Sensor(int handle) {
                 mHandle = handle;
             }
@@ -2896,7 +2907,7 @@
                 }
                 return false;
             }
-            
+
             void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
                 mTimer = readTimerFromParcel(unpluggables, in);
             }
@@ -3018,7 +3029,7 @@
 
             public void plug(long batteryUptime, long batteryRealtime) {
             }
-            
+
             void detach() {
                 mUnpluggables.remove(this);
                 for (int i = 0; i < mSpeedBins.length; i++) {
@@ -3029,7 +3040,7 @@
                     }
                 }
             }
-            
+
             public int countExcessivePowers() {
                 return mExcessivePower != null ? mExcessivePower.size() : 0;
             }
@@ -3296,18 +3307,18 @@
             Pkg() {
                 mUnpluggables.add(this);
             }
-            
+
             public void unplug(long batteryUptime, long batteryRealtime) {
                 mUnpluggedWakeups = mWakeups;
             }
 
             public void plug(long batteryUptime, long batteryRealtime) {
             }
-            
+
             void detach() {
                 mUnpluggables.remove(this);
             }
-            
+
             void readFromParcelLocked(Parcel in) {
                 mWakeups = in.readInt();
                 mLoadedWakeups = in.readInt();
@@ -3458,7 +3469,7 @@
                 Serv() {
                     mUnpluggables.add(this);
                 }
-                
+
                 public void unplug(long batteryUptime, long batteryRealtime) {
                     mUnpluggedStartTime = getStartTimeToNowLocked(batteryUptime);
                     mUnpluggedStarts = mStarts;
@@ -3467,11 +3478,11 @@
 
                 public void plug(long batteryUptime, long batteryRealtime) {
                 }
-                
+
                 void detach() {
                     mUnpluggables.remove(this);
                 }
-                
+
                 void readFromParcelLocked(Parcel in) {
                     mStartTime = in.readLong();
                     mRunningSince = in.readLong();
@@ -3646,7 +3657,7 @@
         public SparseArray<? extends Pid> getPidStats() {
             return mPids;
         }
-        
+
         public Pid getPidStatsLocked(int pid) {
             Pid p = mPids.get(pid);
             if (p == null) {
@@ -3784,7 +3795,7 @@
                 p.addExcessiveWake(overTime, usedTime);
             }
         }
-        
+
         public void reportExcessiveCpuLocked(String proc, long overTime, long usedTime) {
             Proc p = getProcessStatsLocked(proc);
             if (p != null) {
@@ -3796,7 +3807,7 @@
             StopwatchTimer t = getSensorTimerLocked(sensor, true);
             if (t != null) {
                 t.startRunningLocked(BatteryStatsImpl.this);
-            }            
+            }
         }
 
         public void noteStopSensor(int sensor) {
@@ -3804,16 +3815,16 @@
             StopwatchTimer t = getSensorTimerLocked(sensor, false);
             if (t != null) {
                 t.stopRunningLocked(BatteryStatsImpl.this);
-            }            
+            }
         }
-        
+
         public void noteStartGps() {
             StopwatchTimer t = getSensorTimerLocked(Sensor.GPS, true);
             if (t != null) {
                 t.startRunningLocked(BatteryStatsImpl.this);
-            }  
+            }
         }
-        
+
         public void noteStopGps() {
             StopwatchTimer t = getSensorTimerLocked(Sensor.GPS, false);
             if (t != null) {
@@ -3903,12 +3914,12 @@
     public HistoryItem getHistory() {
         return mHistory;
     }
-    
+
     @Override
     public long getHistoryBaseTime() {
         return mHistoryBaseTime;
     }
-    
+
     @Override
     public int getStartCount() {
         return mStartCount;
@@ -3930,7 +3941,7 @@
         mUnpluggedBatteryUptime = getBatteryUptimeLocked(mUptimeStart);
         mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(mRealtimeStart);
     }
-    
+
     public void resetAllStatsLocked() {
         mStartCount = 0;
         initTimes();
@@ -3952,24 +3963,24 @@
         mWifiOnTimer.reset(this, false);
         mGlobalWifiRunningTimer.reset(this, false);
         mBluetoothOnTimer.reset(this, false);
-        
+
         for (int i=0; i<mUidStats.size(); i++) {
             if (mUidStats.valueAt(i).reset()) {
                 mUidStats.remove(mUidStats.keyAt(i));
                 i--;
             }
         }
-        
+
         if (mKernelWakelockStats.size() > 0) {
             for (SamplingTimer timer : mKernelWakelockStats.values()) {
                 mUnpluggables.remove(timer);
             }
             mKernelWakelockStats.clear();
         }
-        
+
         clearHistoryLocked();
     }
-    
+
     void setOnBattery(boolean onBattery, int oldStatus, int level) {
         synchronized(this) {
             boolean doWrite = false;
@@ -3977,7 +3988,7 @@
             m.arg1 = onBattery ? 1 : 0;
             mHandler.sendMessage(m);
             mOnBattery = mOnBatteryInternal = onBattery;
-            
+
             long uptime = SystemClock.uptimeMillis() * 1000;
             long mSecRealtime = SystemClock.elapsedRealtime();
             long realtime = mSecRealtime * 1000;
@@ -4030,10 +4041,10 @@
             }
         }
     }
-    
+
     // This should probably be exposed in the API, though it's not critical
     private static final int BATTERY_PLUGGED_NONE = 0;
-    
+
     public void setBatteryState(int status, int health, int plugType, int level,
             int temp, int volt) {
         boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
@@ -4101,10 +4112,10 @@
             mRecordingHistory = false;
         }
     }
-    
+
     public void updateKernelWakelocksLocked() {
         Map<String, KernelWakelockStats> m = readKernelWakelockStats();
-        
+
         if (m == null) {
             // Not crashing might make board bringup easier.
             Slog.w(TAG, "Couldn't get kernel wake lock stats");
@@ -4114,10 +4125,10 @@
         for (Map.Entry<String, KernelWakelockStats> ent : m.entrySet()) {
             String name = ent.getKey();
             KernelWakelockStats kws = ent.getValue();
-        
+
             SamplingTimer kwlt = mKernelWakelockStats.get(name);
             if (kwlt == null) {
-                kwlt = new SamplingTimer(mUnpluggables, mOnBatteryInternal, 
+                kwlt = new SamplingTimer(mUnpluggables, mOnBatteryInternal,
                         true /* track reported values */);
                 mKernelWakelockStats.put(name, kwlt);
             }
@@ -4125,7 +4136,7 @@
             kwlt.updateCurrentReportedTotalTime(kws.mTotalTime);
             kwlt.setUpdateVersion(sKernelWakelockUpdateVersion);
         }
-        
+
         if (m.size() != mKernelWakelockStats.size()) {
             // Set timers to stale if they didn't appear in /proc/wakelocks this time.
             for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
@@ -4270,18 +4281,18 @@
             return getDischargeStartLevelLocked();
         }
     }
-    
+
     public int getDischargeStartLevelLocked() {
             return mDischargeUnplugLevel;
     }
-    
+
     @Override
     public int getDischargeCurrentLevel() {
         synchronized(this) {
             return getDischargeCurrentLevelLocked();
         }
     }
-    
+
     public int getDischargeCurrentLevelLocked() {
             return mDischargeCurrentLevel;
     }
@@ -4424,7 +4435,7 @@
         writeSyncLocked();
         mShuttingDown = true;
     }
-    
+
     Parcel mPendingWrite = null;
     final ReentrantLock mWriteLock = new ReentrantLock();
 
@@ -4445,7 +4456,7 @@
         if (mShuttingDown) {
             return;
         }
-        
+
         Parcel out = Parcel.obtain();
         writeSummaryToParcel(out);
         mLastWriteTime = SystemClock.elapsedRealtime();
@@ -4545,7 +4556,7 @@
         } catch(java.io.IOException e) {
             Slog.e("BatteryStats", "Error reading battery statistics", e);
         }
-        
+
         addHistoryRecordLocked(SystemClock.elapsedRealtime(), HistoryItem.CMD_START);
     }
 
@@ -4564,7 +4575,7 @@
                 mHistoryBaseTime = rec.time;
             }
         }
-        
+
         long oldnow = SystemClock.elapsedRealtime() - (5*60*100);
         if (oldnow > 0) {
             // If the system process has restarted, but not the entire
@@ -4576,7 +4587,7 @@
             mHistoryBaseTime -= oldnow;
         }
     }
-    
+
     void writeHistory(Parcel out) {
         HistoryItem rec = mHistory;
         while (rec != null) {
@@ -4585,7 +4596,7 @@
         }
         out.writeLong(-1);
     }
-    
+
     private void readSummaryFromParcel(Parcel in) {
         final int version = in.readInt();
         if (version != VERSION) {
@@ -4595,7 +4606,7 @@
         }
 
         readHistory(in);
-        
+
         mStartCount = in.readInt();
         mBatteryUptime = in.readLong();
         mBatteryRealtime = in.readLong();
@@ -4605,9 +4616,9 @@
         mDischargeCurrentLevel = in.readInt();
         mLowDischargeAmountSinceCharge = in.readInt();
         mHighDischargeAmountSinceCharge = in.readInt();
-        
+
         mStartCount++;
-        
+
         mScreenOn = false;
         mScreenOnTimer.readSummaryFromParcelLocked(in);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
@@ -4687,7 +4698,7 @@
                     u.mUserActivityCounters[i].readSummaryFromParcelLocked(in);
                 }
             }
-            
+
             int NW = in.readInt();
             if (NW > 10000) {
                 Slog.w(TAG, "File corrupt: too many wake locks " + NW);
@@ -4780,7 +4791,7 @@
         out.writeInt(VERSION);
 
         writeHistory(out);
-        
+
         out.writeInt(mStartCount);
         out.writeLong(computeBatteryUptime(NOW_SYS, STATS_SINCE_CHARGED));
         out.writeLong(computeBatteryRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
@@ -4790,7 +4801,7 @@
         out.writeInt(mDischargeCurrentLevel);
         out.writeInt(mLowDischargeAmountSinceCharge);
         out.writeInt(mHighDischargeAmountSinceCharge);
-        
+
         mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
             mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL);
@@ -4819,14 +4830,14 @@
                 out.writeInt(0);
             }
         }
-        
+
         out.writeInt(sNumSpeedSteps);
         final int NU = mUidStats.size();
         out.writeInt(NU);
         for (int iu = 0; iu < NU; iu++) {
             out.writeInt(mUidStats.keyAt(iu));
             Uid u = mUidStats.valueAt(iu);
-            
+
             if (u.mWifiRunningTimer != null) {
                 out.writeInt(1);
                 u.mWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL);
@@ -4872,7 +4883,7 @@
                     u.mUserActivityCounters[i].writeSummaryFromParcelLocked(out);
                 }
             }
-            
+
             int NW = u.mWakelockStats.size();
             out.writeInt(NW);
             if (NW > 0) {
@@ -4964,7 +4975,7 @@
                     }
                 }
             }
-            
+
             out.writeLong(u.getTcpBytesReceived(STATS_SINCE_CHARGED));
             out.writeLong(u.getTcpBytesSent(STATS_SINCE_CHARGED));
         }
@@ -4973,7 +4984,7 @@
     public void readFromParcel(Parcel in) {
         readFromParcelLocked(in);
     }
-    
+
     void readFromParcelLocked(Parcel in) {
         int magic = in.readInt();
         if (magic != MAGIC) {
@@ -4981,7 +4992,7 @@
         }
 
         readHistory(in);
-        
+
         mStartCount = in.readInt();
         mBatteryUptime = in.readLong();
         mBatteryLastUptime = 0;
@@ -5056,7 +5067,7 @@
                 mKernelWakelockStats.put(wakelockName, kwlt);
             }
         }
-        
+
         mPartialTimers.clear();
         mFullTimers.clear();
         mWindowTimers.clear();
@@ -5084,18 +5095,18 @@
     public void writeToParcelWithoutUids(Parcel out, int flags) {
         writeToParcelLocked(out, false, flags);
     }
-    
-    @SuppressWarnings("unused") 
+
+    @SuppressWarnings("unused")
     void writeToParcelLocked(Parcel out, boolean inclUids, int flags) {
         final long uSecUptime = SystemClock.uptimeMillis() * 1000;
         final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
         final long batteryUptime = getBatteryUptimeLocked(uSecUptime);
         final long batteryRealtime = getBatteryRealtimeLocked(uSecRealtime);
-        
+
         out.writeInt(MAGIC);
-        
+
         writeHistory(out);
-        
+
         out.writeInt(mStartCount);
         out.writeLong(mBatteryUptime);
         out.writeLong(mBatteryRealtime);
@@ -5184,7 +5195,7 @@
             return new BatteryStatsImpl[size];
         }
     };
-    
+
     public void dumpLocked(PrintWriter pw) {
         if (DEBUG) {
             Printer pr = new PrintWriterPrinter(pw);
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 59600dc..18e1b45 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -24,6 +24,7 @@
 import android.os.Process;
 import android.os.SystemProperties;
 import android.util.Config;
+import android.util.Finalizers;
 import android.util.Log;
 import android.util.Slog;
 
@@ -141,6 +142,12 @@
             Debug.enableEmulatorTraceOutput();
         }
 
+        /**
+         * Initialize the thread used to reclaim resources without
+         * going through finalizers.
+         */
+        Finalizers.init();
+
         initialized = true;
     }
 
@@ -257,7 +264,9 @@
         // TODO: Doing this here works, but it seems kind of arbitrary. Find
         // a better place. The goal is to set it up for applications, but not
         // tools like am.
+        System.out.close();
         System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
+        System.err.close();
         System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
 
         commonInit();
@@ -331,9 +340,6 @@
         }
     }
 
-    /** Counter used to prevent reentrancy in {@link #reportException}. */
-    private static final AtomicInteger sInReportException = new AtomicInteger();
-
     /**
      * Set the object identifying this application/process, for reporting VM
      * errors.
diff --git a/core/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
index ea278a6..6d2bcae 100644
--- a/core/java/com/android/internal/os/SamplingProfilerIntegration.java
+++ b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
@@ -26,7 +26,9 @@
 import java.io.PrintStream;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
 
+import android.content.pm.PackageInfo;
 import android.util.Log;
 import android.os.*;
 
@@ -37,15 +39,27 @@
 
     private static final String TAG = "SamplingProfilerIntegration";
 
+    public static final String SNAPSHOT_DIR = "/data/snapshots";
+
     private static final boolean enabled;
     private static final Executor snapshotWriter;
+    private static final int samplingProfilerHz;
+
+    /** Whether or not we've created the snapshots dir. */
+    private static boolean dirMade = false;
+
+    /** Whether or not a snapshot is being persisted. */
+    private static final AtomicBoolean pending = new AtomicBoolean(false);
+
     static {
-        enabled = "1".equals(SystemProperties.get("persist.sampling_profiler"));
-        if (enabled) {
+        samplingProfilerHz = SystemProperties.getInt("persist.sys.profiler_hz", 0);
+        if (samplingProfilerHz > 0) {
             snapshotWriter = Executors.newSingleThreadExecutor();
-            Log.i(TAG, "Profiler is enabled.");
+            enabled = true;
+            Log.i(TAG, "Profiler is enabled. Sampling Profiler Hz: " + samplingProfilerHz);
         } else {
             snapshotWriter = null;
+            enabled = false;
             Log.i(TAG, "Profiler is disabled.");
         }
     }
@@ -68,49 +82,48 @@
         }
         ThreadGroup group = Thread.currentThread().getThreadGroup();
         SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupTheadSet(group);
-        INSTANCE = new SamplingProfiler(4, threadSet);
-        INSTANCE.start(10);
+        INSTANCE = new SamplingProfiler(4, threadSet); // TODO parameter for depth
+        INSTANCE.start(1000/samplingProfilerHz);
     }
 
-    /** Whether or not we've created the snapshots dir. */
-    static boolean dirMade = false;
-
-    /** Whether or not a snapshot is being persisted. */
-    static volatile boolean pending;
-
     /**
-     * Writes a snapshot to the SD card if profiling is enabled.
+     * Writes a snapshot if profiling is enabled.
      */
-    public static void writeSnapshot(final String name) {
+    public static void writeSnapshot(final String processName, final PackageInfo packageInfo) {
         if (!enabled) {
             return;
         }
 
         /*
-         * If we're already writing a snapshot, don't bother enqueing another
+         * If we're already writing a snapshot, don't bother enqueueing another
          * request right now. This will reduce the number of individual
          * snapshots and in turn the total amount of memory consumed (one big
          * snapshot is smaller than N subset snapshots).
          */
-        if (!pending) {
-            pending = true;
+        if (pending.compareAndSet(false, true)) {
             snapshotWriter.execute(new Runnable() {
                 public void run() {
-                    String dir =
-                        Environment.getExternalStorageDirectory().getPath() + "/snapshots";
                     if (!dirMade) {
-                        new File(dir).mkdirs();
-                        if (new File(dir).isDirectory()) {
+                        File dir = new File(SNAPSHOT_DIR);
+                        dir.mkdirs();
+                        // the directory needs to be writable to anybody
+                        dir.setWritable(true, false);
+                        // the directory needs to be executable to anybody
+                        // don't know why yet, but mode 723 would work, while
+                        // mode 722 throws FileNotFoundExecption at line 151
+                        dir.setExecutable(true, false);
+                        if (new File(SNAPSHOT_DIR).isDirectory()) {
                             dirMade = true;
                         } else {
-                            Log.w(TAG, "Creation of " + dir + " failed.");
+                            Log.w(TAG, "Creation of " + SNAPSHOT_DIR + " failed.");
+                            pending.set(false);
                             return;
                         }
                     }
                     try {
-                        writeSnapshot(dir, name);
+                        writeSnapshot(SNAPSHOT_DIR, processName, packageInfo);
                     } finally {
-                        pending = false;
+                        pending.set(false);
                     }
                 }
             });
@@ -124,15 +137,15 @@
         if (!enabled) {
             return;
         }
-
-        String dir = "/data/zygote/snapshots";
-        new File(dir).mkdirs();
-        writeSnapshot(dir, "zygote");
+        writeSnapshot("zygote", null);
         INSTANCE.shutdown();
         INSTANCE = null;
     }
 
-    private static void writeSnapshot(String dir, String name) {
+    /**
+     * pass in PackageInfo to retrieve various values for snapshot header
+     */
+    private static void writeSnapshot(String dir, String processName, PackageInfo packageInfo) {
         if (!enabled) {
             return;
         }
@@ -144,40 +157,55 @@
          * we capture two snapshots in rapid succession.
          */
         long start = System.currentTimeMillis();
-        String path = dir + "/" + name.replace(':', '.') + "-"
-                + System.currentTimeMillis() + ".snapshot";
-
-        // Try to open the file a few times. The SD card may not be mounted.
-        PrintStream out;
-        int count = 0;
-        while (true) {
-            try {
-                out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path)));
-                break;
-            } catch (FileNotFoundException e) {
-                if (++count > 3) {
-                    Log.e(TAG, "Could not open " + path + ".");
-                    return;
-                }
-
-                // Sleep for a bit and then try again.
-                try {
-                    Thread.sleep(2500);
-                } catch (InterruptedException e1) { /* ignore */ }
-            }
-        }
-
+        String name = processName.replaceAll(":", ".");
+        String path = dir + "/" + name + "-" +System.currentTimeMillis() + ".snapshot";
+        PrintStream out = null;
         try {
+            out = new PrintStream(new BufferedOutputStream(new FileOutputStream(path)));
+        } catch (IOException e) {
+            Log.e(TAG, "Could not open " + path + ":" + e);
+            return;
+        }
+        try {
+            generateSnapshotHeader(name, packageInfo, out);
             INSTANCE.writeHprofData(out);
         } finally {
             out.close();
         }
         if (out.checkError()) {
             Log.e(TAG, "Error writing snapshot.");
-        } else {
-            long elapsed = System.currentTimeMillis() - start;
-            Log.i(TAG, "Wrote snapshot for " + name
-                  + " in " + elapsed + "ms.");
+            return;
         }
+        // set file readable to the world so that SamplingProfilerService
+        // can put it to dropbox
+        new File(path).setReadable(true, false);
+
+        long elapsed = System.currentTimeMillis() - start;
+        Log.i(TAG, "Wrote snapshot for " + name + " in " + elapsed + "ms.");
+    }
+
+    /**
+     * generate header for snapshots, with the following format (like http header):
+     *
+     * Version: <version number of profiler>\n
+     * Process: <process name>\n
+     * Package: <package name, if exists>\n
+     * Package-Version: <version number of the package, if exists>\n
+     * Build: <fingerprint>\n
+     * \n
+     * <the actual snapshot content begins here...>
+     */
+    private static void generateSnapshotHeader(String processName, PackageInfo packageInfo,
+            PrintStream out) {
+        // profiler version
+        out.println("Version: 2");
+        out.println("Process: " + processName);
+        if (packageInfo != null) {
+            out.println("Package: " + packageInfo.packageName);
+            out.println("Package-Version: " + packageInfo.versionCode);
+        }
+        out.println("Build: " + Build.FINGERPRINT);
+        // single blank line means the end of snapshot header.
+        out.println();
     }
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index f0e5517..848bf9e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -22,6 +22,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.LocalServerSocket;
 import android.os.Debug;
+import android.os.FileUtils;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.util.Config;
@@ -494,6 +495,9 @@
 
         closeServerSocket();
 
+        // set umask to 0077 so new files and directories will default to owner-only permissions.
+        FileUtils.setUMask(FileUtils.S_IRWXG | FileUtils.S_IRWXO);
+
         /*
          * Pass the remaining arguments to SystemServer.
          * "--nice-name=system_server com.android.server.SystemServer"
@@ -511,7 +515,7 @@
         String args[] = {
             "--setuid=1000",
             "--setgid=1000",
-            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
+            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003",
             "--capabilities=130104352,130104352",
             "--runtime-init",
             "--nice-name=system_server",
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 4501bd7..f87ca3e 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -30,5 +30,8 @@
     void disable(int state);
     void animateExpand();
     void animateCollapse();
+    void setLightsOn(boolean on);
+    void setMenuKeyVisible(boolean visible);
+    void setIMEButtonVisible(boolean visible);
 }
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 852630d..19666fd 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -30,14 +30,20 @@
     void setIcon(String slot, String iconPackage, int iconId, int iconLevel);
     void setIconVisibility(String slot, boolean visible);
     void removeIcon(String slot);
+    void setActiveWindowIsFullscreen(boolean fullscreen);
+    void setMenuKeyVisible(boolean visible);
+    void setIMEButtonVisible(boolean visible);
 
     // ---- Methods below are for use by the status bar policy services ----
     // You need the STATUS_BAR_SERVICE permission
     void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList,
-            out List<IBinder> notificationKeys, out List<StatusBarNotification> notifications);
+            out List<IBinder> notificationKeys, out List<StatusBarNotification> notifications,
+            out int[] switches);
     void onPanelRevealed();
     void onNotificationClick(String pkg, String tag, int id);
     void onNotificationError(String pkg, String tag, int id,
             int uid, int initialPid, String message);
     void onClearAllNotifications();
+    void onNotificationClear(String pkg, String tag, int id);
+    void setLightsOn(boolean on);
 }
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/com/android/internal/statusbar/StatusBarNotification.java
index aa340fb..2b96bf6 100644
--- a/core/java/com/android/internal/statusbar/StatusBarNotification.java
+++ b/core/java/com/android/internal/statusbar/StatusBarNotification.java
@@ -121,6 +121,10 @@
         return (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0;
     }
 
+    public boolean isClearable() {
+        return ((notification.flags & Notification.FLAG_ONGOING_EVENT) == 0)
+                && ((notification.flags & Notification.FLAG_NO_CLEAR) == 0);
+    }
 }
 
 
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
new file mode 100644
index 0000000..b0778dd
--- /dev/null
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -0,0 +1,777 @@
+/**
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Stack;
+
+/**
+ * An asynchronous channel between two handlers.
+ *
+ * The handlers maybe in the same process or in another process. There
+ * are two protocol styles that can be used with an AysncChannel. The
+ * first is a simple request/reply protocol where the server does
+ * not need to know which client is issuing the request.
+ *
+ * In a simple request/reply protocol the client/source sends requests to the
+ * server/destination. And the server uses the replyToMessage methods.
+ * In this usage model there is no need for the destination to
+ * use the connect methods. The typical sequence of operations is:
+ *
+ *   1) Client calls AsyncChannel#connect
+ *   2) Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel
+
+ *   3) Client calls AsyncChannel#sendMessage(msgX)
+ *   4) Server receives and processes msgX
+ *   5) Server optionally calls AsyncChannel#replyToMessage(msgY)
+ *      and if sent Client receives and processes msgY
+ *   6) Loop to step 3 until done
+ *
+ *   7) When done Client calls {@link AsyncChannel#disconnect(int)}
+ *   8) Client receives CMD_CHANNEL_DISCONNECTED from AsyncChannel
+ *
+ * A second usage model is where the server/destination needs to know
+ * which client it's connected too. For example the server needs to
+ * send unsolicited messages back to the client. Or the server keeps
+ * different state for each client. In this model the server will also
+ * use the connect methods. The typical sequence of operation is:
+ *
+ *   1)  Client calls AsyncChannel#connect
+ *   2)  Client receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel
+ *   3)  Client calls AsyncChannel#sendMessage(CMD_CHANNEL_FULL_CONNECTION)
+ *   4)  Server receives CMD_CHANNEL_FULL_CONNECTION
+ *   5)  Server calls AsyncChannel#connect
+ *   6)  Server receives CMD_CHANNEL_HALF_CONNECTED from AsyncChannel
+ *   7)  Server sends AsyncChannel#sendMessage(CMD_CHANNEL_FULLY_CONNECTED)
+ *   8)  Client receives CMD_CHANNEL_FULLY_CONNECTED
+ *
+ *   9)  Client/Server uses AsyncChannel#sendMessage/replyToMessage
+ *       to communicate and perform work
+ *   10) Loop to step 9 until done
+ *
+ *   11) When done Client/Server calls {@link AsyncChannel#disconnect(int)}
+ *   12) Client/Server receives CMD_CHANNEL_DISCONNECTED from AsyncChannel
+ */
+public class AsyncChannel {
+    /** Log tag */
+    private static final String TAG = "AsyncChannel";
+
+    /** Enable to turn on debugging */
+    private static final boolean DBG = false;
+
+    /**
+     * Command sent when the channel is half connected. Half connected
+     * means that the channel can be used to send commends to the destination
+     * but the destination is unaware that the channel exists. The first
+     * command sent to the destination is typically CMD_CHANNEL_FULL_CONNECTION if
+     * it is desired to establish a long term connection, but any command maybe
+     * sent.
+     *
+     * msg.arg1 == 0 : STATUS_SUCCESSFUL
+     *             1 : STATUS_BINDING_UNSUCCESSFUL
+     * msg.arg2 == token parameter
+     * msg.replyTo == dstMessenger if successful
+     */
+    public static final int CMD_CHANNEL_HALF_CONNECTED = -1;
+
+    /**
+     * Command typically sent when after receiving the CMD_CHANNEL_HALF_CONNECTED.
+     * This is used to initiate a long term connection with the destination and
+     * typically the destination will reply with CMD_CHANNEL_FULLY_CONNECTED.
+     *
+     * msg.replyTo = srcMessenger.
+     */
+    public static final int CMD_CHANNEL_FULL_CONNECTION = -2;
+
+    /**
+     * Command typically sent after the destination receives a CMD_CHANNEL_FULL_CONNECTION.
+     * This signifies the acceptance or rejection of the channel by the sender.
+     *
+     * msg.arg1 == 0 : Accept connection
+     *               : All other values signify the destination rejected the connection
+     *                 and {@link AsyncChannel#disconnect(int)} would typically be called.
+     */
+    public static final int CMD_CHANNEL_FULLY_CONNECTED = -3;
+
+    /**
+     * Command sent when one side or the other wishes to disconnect. The sender
+     * may or may not be able to receive a reply depending upon the protocol and
+     * the state of the connection. The receiver should call {@link AsyncChannel#disconnect(int)}
+     * to close its side of the channel and it will receive a CMD_CHANNEL_DISCONNECTED
+     * when the channel is closed.
+     *
+     * msg.replyTo = messenger that is disconnecting
+     */
+    public static final int CMD_CHANNEL_DISCONNECT = -4;
+
+    /**
+     * Command sent when the channel becomes disconnected. This is sent when the
+     * channel is forcibly disconnected by the system or as a reply to CMD_CHANNEL_DISCONNECT.
+     *
+     * msg.arg1 == 0 : STATUS_SUCCESSFUL
+     *               : All other values signify failure and the channel state is indeterminate
+     * msg.arg2 == token parameter
+     * msg.replyTo = messenger disconnecting or null if it was never connected.
+     */
+    public static final int CMD_CHANNEL_DISCONNECTED = -5;
+
+    /** Successful status always 0, !0 is an unsuccessful status */
+    public static final int STATUS_SUCCESSFUL = 0;
+
+    /** Error attempting to bind on a connect */
+    public static final int STATUS_BINDING_UNSUCCESSFUL = 1;
+
+    /** Service connection */
+    private AsyncChannelConnection mConnection;
+
+    /** Context for source */
+    private Context mSrcContext;
+
+    /** Handler for source */
+    private Handler mSrcHandler;
+
+    /** Messenger for source */
+    private Messenger mSrcMessenger;
+
+    /** Messenger for destination */
+    private Messenger mDstMessenger;
+
+    /**
+     * AsyncChannel constructor
+     */
+    public AsyncChannel() {
+    }
+
+    /**
+     * Connect handler to named package/class.
+     *
+     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstPackageName is the destination package name
+     * @param dstClassName is the fully qualified class name (i.e. contains
+     *            package name)
+     * @param token unique id for this connection
+     */
+    private void connectSrcHandlerToPackage(
+            Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName,
+            int token) {
+        if (DBG) log("connect srcHandler to dst Package & class E");
+
+        mConnection = new AsyncChannelConnection(token);
+
+        /* Initialize the source information */
+        mSrcContext = srcContext;
+        mSrcHandler = srcHandler;
+        mSrcMessenger = new Messenger(srcHandler);
+
+        /*
+         * Initialize destination information to null they will
+         * be initialized when the AsyncChannelConnection#onServiceConnected
+         * is called
+         */
+        mDstMessenger = null;
+
+        /* Send intent to create the connection */
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setClassName(dstPackageName, dstClassName);
+        boolean result = srcContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+        if (!result) {
+            replyHalfConnected(STATUS_BINDING_UNSUCCESSFUL, token);
+        }
+
+        if (DBG) log("connect srcHandler to dst Package & class X result=" + result);
+    }
+
+    /**
+     * Connect handler to named package/class.
+     *
+     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     *      msg.arg1 = status
+     *      msg.arg2 = token
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstPackageName is the destination package name
+     * @param dstClassName is the fully qualified class name (i.e. contains
+     *            package name)
+     * @param token returned in msg.arg2
+     */
+    public void connect(Context srcContext, Handler srcHandler, String dstPackageName,
+            String dstClassName, int token) {
+        if (DBG) log("connect srcHandler to dst Package & class E");
+
+        final class ConnectAsync implements Runnable {
+            Context mSrcCtx;
+            Handler mSrcHdlr;
+            String mDstPackageName;
+            String mDstClassName;
+            int mToken;
+
+            ConnectAsync(Context srcContext, Handler srcHandler, String dstPackageName,
+                    String dstClassName, int token) {
+                mSrcCtx = srcContext;
+                mSrcHdlr = srcHandler;
+                mDstPackageName = dstPackageName;
+                mDstClassName = dstClassName;
+                mToken = token;
+            }
+
+            public void run() {
+                connectSrcHandlerToPackage(mSrcCtx, mSrcHdlr, mDstPackageName, mDstClassName,
+                        mToken);
+            }
+        }
+
+        ConnectAsync ca = new ConnectAsync(srcContext, srcHandler, dstPackageName, dstClassName,
+                token);
+        new Thread(ca).start();
+
+        if (DBG) log("connect srcHandler to dst Package & class X");
+    }
+
+    /**
+     * Connect handler to a class
+     *
+     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     *      msg.arg1 = status
+     *      msg.arg2 = token
+     *
+     * @param srcContext
+     * @param srcHandler
+     * @param klass is the class to send messages to.
+     * @param token returned in msg.arg2
+     */
+    public void connect(Context srcContext, Handler srcHandler, Class<?> klass, int token) {
+        connect(srcContext, srcHandler, klass.getPackage().getName(), klass.getName(), token);
+    }
+
+    /**
+     * Connect handler and messenger.
+     *
+     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     *      msg.arg1 = status
+     *      msg.arg2 = token
+     *
+     * @param srcContext
+     * @param srcHandler
+     * @param dstMessenger
+     * @param token returned in msg.arg2
+     */
+    public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger, int token) {
+        if (DBG) log("connect srcHandler to the dstMessenger  E");
+
+        // Initialize source fields
+        mSrcContext = srcContext;
+        mSrcHandler = srcHandler;
+        mSrcMessenger = new Messenger(mSrcHandler);
+
+        // Initialize destination fields
+        mDstMessenger = dstMessenger;
+
+        if (DBG) log("tell source we are half connected");
+
+        // Tell source we are half connected
+        replyHalfConnected(STATUS_SUCCESSFUL, token);
+
+        if (DBG) log("connect srcHandler to the dstMessenger X");
+    }
+
+    /**
+     * Connect two local Handlers.
+     *
+     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcHandler when complete.
+     *      msg.arg1 = status
+     *      msg.arg2 = token
+     *
+     * @param srcContext is the context of the source
+     * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED
+     *            messages
+     * @param dstHandler is the hander to send messages to.
+     * @param token returned in msg.arg2
+     */
+    public void connect(Context srcContext, Handler srcHandler, Handler dstHandler, int token) {
+        connect(srcContext, srcHandler, new Messenger(dstHandler), token);
+    }
+
+    /**
+     * Connect service and messenger.
+     *
+     * Sends a CMD_CHANNEL_HALF_CONNECTED message to srcAsyncService when complete.
+     *      msg.arg1 = status
+     *      msg.arg2 = token
+     *
+     * @param srcAsyncService
+     * @param dstMessenger
+     * @param token returned in msg.arg2
+     */
+    public void connect(AsyncService srcAsyncService, Messenger dstMessenger, int token) {
+        connect(srcAsyncService, srcAsyncService.getHandler(), dstMessenger, token);
+    }
+
+    /**
+     * To close the connection call when handler receives CMD_CHANNEL_DISCONNECTED
+     */
+    public void disconnected() {
+        mSrcHandler = null;
+        mSrcMessenger = null;
+        mDstMessenger = null;
+        mConnection = null;
+    }
+
+    /**
+     * Disconnect
+     */
+    public void disconnect(int token) {
+        if (mConnection != null) {
+            mConnection.setToken(token);
+            mSrcContext.unbindService(mConnection);
+        }
+        if (mSrcHandler != null) {
+            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
+            msg.arg1 = STATUS_SUCCESSFUL;
+            msg.arg2 = token;
+            msg.replyTo = mDstMessenger;
+            mSrcHandler.sendMessage(msg);
+        }
+    }
+
+    /**
+     * Send a message to the destination handler.
+     *
+     * @param msg
+     */
+    public void sendMessage(Message msg) {
+        msg.replyTo = mSrcMessenger;
+        try {
+            mDstMessenger.send(msg);
+        } catch (RemoteException e) {
+            log("TODO: handle sendMessage RemoteException" + e);
+        }
+    }
+
+    /**
+     * Send a message to the destination handler
+     *
+     * @param what
+     */
+    public void sendMessage(int what) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        sendMessage(msg);
+    }
+
+    /**
+     * Send a message to the destination handler
+     *
+     * @param what
+     * @param arg1
+     */
+    public void sendMessage(int what, int arg1) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        sendMessage(msg);
+    }
+
+    /**
+     * Send a message to the destination handler
+     *
+     * @param what
+     * @param arg1
+     * @param arg2
+     */
+    public void sendMessage(int what, int arg1, int arg2) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        sendMessage(msg);
+    }
+
+    /**
+     * Send a message to the destination handler
+     *
+     * @param what
+     * @param arg1
+     * @param arg2
+     * @param obj
+     */
+    public void sendMessage(int what, int arg1, int arg2, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        msg.obj = obj;
+        sendMessage(msg);
+    }
+
+    /**
+     * Send a message to the destination handler
+     *
+     * @param what
+     * @param obj
+     */
+    public void sendMessage(int what, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.obj = obj;
+        sendMessage(msg);
+    }
+
+    /**
+     * Reply to srcMsg sending dstMsg
+     *
+     * @param srcMsg
+     * @param dstMsg
+     */
+    public void replyToMessage(Message srcMsg, Message dstMsg) {
+        try {
+            srcMsg.replyTo.send(dstMsg);
+        } catch (RemoteException e) {
+            log("TODO: handle replyToMessage RemoteException" + e);
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Reply to srcMsg
+     *
+     * @param srcMsg
+     * @param what
+     */
+    public void replyToMessage(Message srcMsg, int what) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        replyToMessage(srcMsg, msg);
+    }
+
+    /**
+     * Reply to srcMsg
+     *
+     * @param srcMsg
+     * @param what
+     * @param arg1
+     */
+    public void replyToMessage(Message srcMsg, int what, int arg1) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        replyToMessage(srcMsg, msg);
+    }
+
+    /**
+     * Reply to srcMsg
+     *
+     * @param srcMsg
+     * @param what
+     * @param arg1
+     * @param arg2
+     */
+    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        replyToMessage(srcMsg, msg);
+    }
+
+    /**
+     * Reply to srcMsg
+     *
+     * @param srcMsg
+     * @param what
+     * @param arg1
+     * @param arg2
+     * @param obj
+     */
+    public void replyToMessage(Message srcMsg, int what, int arg1, int arg2, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        msg.obj = obj;
+        replyToMessage(srcMsg, msg);
+    }
+
+    /**
+     * Reply to srcMsg
+     *
+     * @param srcMsg
+     * @param what
+     * @param obj
+     */
+    public void replyToMessage(Message srcMsg, int what, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.obj = obj;
+        replyToMessage(srcMsg, msg);
+    }
+
+    /**
+     * Send the Message synchronously.
+     *
+     * @param msg to send
+     * @return reply message or null if an error.
+     */
+    public Message sendMessageSynchronously(Message msg) {
+        Message resultMsg = SyncMessenger.sendMessageSynchronously(mDstMessenger, msg);
+        return resultMsg;
+    }
+
+    /**
+     * Send the Message synchronously.
+     *
+     * @param what
+     * @return reply message or null if an error.
+     */
+    public Message sendMessageSynchronously(int what) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        Message resultMsg = sendMessageSynchronously(msg);
+        return resultMsg;
+    }
+
+    /**
+     * Send the Message synchronously.
+     *
+     * @param what
+     * @param arg1
+     * @return reply message or null if an error.
+     */
+    public Message sendMessageSynchronously(int what, int arg1) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        Message resultMsg = sendMessageSynchronously(msg);
+        return resultMsg;
+    }
+
+    /**
+     * Send the Message synchronously.
+     *
+     * @param what
+     * @param arg1
+     * @param arg2
+     * @return reply message or null if an error.
+     */
+    public Message sendMessageSynchronously(int what, int arg1, int arg2) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        Message resultMsg = sendMessageSynchronously(msg);
+        return resultMsg;
+    }
+
+    /**
+     * Send the Message synchronously.
+     *
+     * @param what
+     * @param arg1
+     * @param arg2
+     * @param obj
+     * @return reply message or null if an error.
+     */
+    public Message sendMessageSynchronously(int what, int arg1, int arg2, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = arg1;
+        msg.arg2 = arg2;
+        msg.obj = obj;
+        Message resultMsg = sendMessageSynchronously(msg);
+        return resultMsg;
+    }
+
+    /**
+     * Send the Message synchronously.
+     *
+     * @param what
+     * @param obj
+     * @return reply message or null if an error.
+     */
+    public Message sendMessageSynchronously(int what, Object obj) {
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.obj = obj;
+        Message resultMsg = sendMessageSynchronously(msg);
+        return resultMsg;
+    }
+
+    /**
+     * Helper class to send messages synchronously
+     */
+    private static class SyncMessenger {
+        /** A stack of SyncMessengers */
+        private static Stack<SyncMessenger> sStack = new Stack<SyncMessenger>();
+        /** A number of SyncMessengers created */
+        private static int sCount = 0;
+        /** The handler thread */
+        private HandlerThread mHandlerThread;
+        /** The handler that will receive the result */
+        private SyncHandler mHandler;
+        /** The messenger used to send the message */
+        private Messenger mMessenger;
+
+        /** private constructor */
+        private SyncMessenger() {
+        }
+
+        /** Synchronous Handler class */
+        private class SyncHandler extends Handler {
+            /** The object used to wait/notify */
+            private Object mLockObject = new Object();
+            /** The resulting message */
+            private Message mResultMsg;
+
+            /** Constructor */
+            private SyncHandler(Looper looper) {
+                super(looper);
+            }
+
+            /** Handle of the reply message */
+            @Override
+            public void handleMessage(Message msg) {
+                mResultMsg = Message.obtain();
+                mResultMsg.copyFrom(msg);
+                synchronized(mLockObject) {
+                    mLockObject.notify();
+                }
+            }
+        }
+
+        /**
+         * @return the SyncMessenger
+         */
+        private static SyncMessenger obtain() {
+            SyncMessenger sm;
+            synchronized (sStack) {
+                if (sStack.isEmpty()) {
+                    sm = new SyncMessenger();
+                    sm.mHandlerThread = new HandlerThread("SyncHandler-" + sCount++);
+                    sm.mHandlerThread.start();
+                    sm.mHandler = sm.new SyncHandler(sm.mHandlerThread.getLooper());
+                    sm.mMessenger = new Messenger(sm.mHandler);
+                } else {
+                    sm = sStack.pop();
+                }
+            }
+            return sm;
+        }
+
+        /**
+         * Recycle this object
+         */
+        private void recycle() {
+            synchronized (sStack) {
+                sStack.push(this);
+            }
+        }
+
+        /**
+         * Send a message synchronously.
+         *
+         * @param msg to send
+         * @return result message or null if an error occurs
+         */
+        private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
+            SyncMessenger sm = SyncMessenger.obtain();
+            try {
+                msg.replyTo = sm.mMessenger;
+                dstMessenger.send(msg);
+                synchronized (sm.mHandler.mLockObject) {
+                    sm.mHandler.mLockObject.wait();
+                }
+            } catch (InterruptedException e) {
+                sm.mHandler.mResultMsg = null;
+            } catch (RemoteException e) {
+                sm.mHandler.mResultMsg = null;
+            }
+            Message resultMsg = sm.mHandler.mResultMsg;
+            sm.recycle();
+            return resultMsg;
+        }
+    }
+
+    /**
+     * Reply to the src handler that we're half connected.
+     *
+     * @param status to be stored in msg.arg1
+     */
+    private void replyHalfConnected(int status, int token) {
+        Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
+        msg.arg1 = status;
+        msg.arg2 = token;
+        msg.replyTo = mDstMessenger;
+        mSrcHandler.sendMessage(msg);
+    }
+
+    /**
+     * ServiceConnection to receive call backs.
+     */
+    class AsyncChannelConnection implements ServiceConnection {
+        private int mToken;
+
+        AsyncChannelConnection(int token) {
+            mToken = token;
+        }
+
+        /**
+         * @param token
+         */
+        public void setToken(int token) {
+            mToken = token;
+        }
+
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            mDstMessenger = new Messenger(service);
+            replyHalfConnected(STATUS_SUCCESSFUL, mToken);
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
+            msg.arg1 = STATUS_SUCCESSFUL;
+            msg.arg2 = mToken;
+            msg.replyTo = mDstMessenger;
+            mSrcHandler.sendMessage(msg);
+        }
+    }
+
+    /**
+     * Log the string.
+     *
+     * @param s
+     */
+    private static void log(String s) {
+        Slog.d(TAG, s);
+    }
+}
diff --git a/core/java/com/android/internal/util/AsyncService.java b/core/java/com/android/internal/util/AsyncService.java
new file mode 100644
index 0000000..54d3c42
--- /dev/null
+++ b/core/java/com/android/internal/util/AsyncService.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.util.Slog;
+
+/**
+ * A service that receives Intents and IBinder transactions
+ * as messages via an AsyncChannel.
+ * <p>
+ * The Start Intent arrives as CMD_ASYNC_SERVICE_ON_START_INTENT with msg.arg1 = flags,
+ * msg.arg2 = startId, and msg.obj = intent.
+ * <p>
+ */
+abstract public class AsyncService extends Service {
+    private static final String TAG = "AsyncService";
+
+    protected static final boolean DBG = true;
+
+    /** The command sent when a onStartCommand is invoked */
+    public static final int CMD_ASYNC_SERVICE_ON_START_INTENT = IBinder.LAST_CALL_TRANSACTION;
+
+    /** The command sent when a onDestroy is invoked */
+    public static final int CMD_ASYNC_SERVICE_DESTROY = IBinder.LAST_CALL_TRANSACTION + 1;
+
+    /** Messenger transport */
+    protected Messenger mMessenger;
+
+    /** Message Handler that will receive messages */
+    Handler mHandler;
+
+    public static final class AsyncServiceInfo {
+        /** Message Handler that will receive messages */
+        public Handler mHandler;
+
+        /**
+         * The flags returned by onStartCommand on how to restart.
+         * For instance @see android.app.Service#START_STICKY
+         */
+        public int mRestartFlags;
+    }
+
+    AsyncServiceInfo mAsyncServiceInfo;
+
+    /**
+     * Create the service's handler returning AsyncServiceInfo.
+     *
+     * @return AsyncServiceInfo
+     */
+    abstract public AsyncServiceInfo createHandler();
+
+    /**
+     * Get the handler
+     */
+    public Handler getHandler() {
+        return mHandler;
+    }
+
+    /**
+     * onCreate
+     */
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mAsyncServiceInfo = createHandler();
+        mHandler = mAsyncServiceInfo.mHandler;
+        mMessenger = new Messenger(mHandler);
+    }
+
+    /**
+     * Sends the CMD_ASYNC_SERVICE_ON_START_INTENT message.
+     */
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (DBG) Slog.d(TAG, "onStartCommand");
+
+        Message msg = mHandler.obtainMessage();
+        msg.what = CMD_ASYNC_SERVICE_ON_START_INTENT;
+        msg.arg1 = flags;
+        msg.arg2 = startId;
+        msg.obj = intent;
+        mHandler.sendMessage(msg);
+
+        return mAsyncServiceInfo.mRestartFlags;
+    }
+
+    /**
+     * Called when service is destroyed. After returning the
+     * service is dead an no more processing should be expected
+     * to occur.
+     */
+    @Override
+    public void onDestroy() {
+        if (DBG) Slog.d(TAG, "onDestroy");
+
+        Message msg = mHandler.obtainMessage();
+        msg.what = CMD_ASYNC_SERVICE_DESTROY;
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Returns the Messenger's binder.
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+}
diff --git a/core/java/com/android/internal/util/HierarchicalStateMachine.java b/core/java/com/android/internal/util/HierarchicalStateMachine.java
index fc1e866..7920b72 100644
--- a/core/java/com/android/internal/util/HierarchicalStateMachine.java
+++ b/core/java/com/android/internal/util/HierarchicalStateMachine.java
@@ -1199,6 +1199,35 @@
     }
 
     /**
+     * Get a message and set Message.target = this,
+     * what, arg1 and arg2
+     *
+     * @param what  is assigned to Message.what
+     * @param arg1  is assigned to Message.arg1
+     * @param arg2  is assigned to Message.arg2
+     * @return  A Message object from the global pool.
+     */
+    public final Message obtainMessage(int what, int arg1, int arg2)
+    {
+        return Message.obtain(mHsmHandler, what, arg1, arg2);
+    }
+
+    /**
+     * Get a message and set Message.target = this,
+     * what, arg1, arg2 and obj
+     *
+     * @param what  is assigned to Message.what
+     * @param arg1  is assigned to Message.arg1
+     * @param arg2  is assigned to Message.arg2
+     * @param obj is assigned to Message.obj
+     * @return  A Message object from the global pool.
+     */
+    public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
+    {
+        return Message.obtain(mHsmHandler, what, arg1, arg2, obj);
+    }
+
+    /**
      * Enqueue a message to this state machine.
      */
     public final void sendMessage(int what) {
@@ -1265,6 +1294,14 @@
     }
 
     /**
+     * Removes a message from the message queue.
+     * Protected, may only be called by instances of HierarchicalStateMachine.
+     */
+    protected final void removeMessages(int what) {
+        mHsmHandler.removeMessages(what);
+    }
+
+    /**
      * Conditionally quit the looper and stop execution.
      *
      * This sends the HSM_QUIT_MSG to the state machine and
diff --git a/core/java/com/android/internal/util/TypedProperties.java b/core/java/com/android/internal/util/TypedProperties.java
index c2ce210..5613999 100644
--- a/core/java/com/android/internal/util/TypedProperties.java
+++ b/core/java/com/android/internal/util/TypedProperties.java
@@ -412,7 +412,7 @@
      */
 
     /**
-     * An unchecked exception that is thrown if a {@code get&lt;TYPE&gt;()} method
+     * An unchecked exception that is thrown if a {@code get<TYPE>()} method
      * is used to retrieve a parameter whose type does not match the method name.
      */
     public static class TypeException extends IllegalArgumentException {
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 8d8df16..e00a853 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -26,6 +26,7 @@
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -284,6 +285,26 @@
 
         out.endTag(null, "list");
     }
+    
+    public static final void writeSetXml(Set val, String name, XmlSerializer out)
+            throws XmlPullParserException, java.io.IOException {
+        if (val == null) {
+            out.startTag(null, "null");
+            out.endTag(null, "null");
+            return;
+        }
+        
+        out.startTag(null, "set");
+        if (name != null) {
+            out.attribute(null, "name", name);
+        }
+        
+        for (Object v : val) {
+            writeValueXml(v, null, out);
+        }
+        
+        out.endTag(null, "set");
+    }
 
     /**
      * Flatten a byte[] into an XmlSerializer.  The list can later be read back
@@ -426,6 +447,9 @@
         } else if (v instanceof List) {
             writeListXml((List)v, name, out);
             return;
+        } else if (v instanceof Set) {
+            writeSetXml((Set)v, name, out);
+            return;
         } else if (v instanceof CharSequence) {
             // XXX This is to allow us to at least write something if
             // we encounter styled text...  but it means we will drop all
@@ -476,7 +500,7 @@
      *
      * @param in The InputStream from which to read.
      *
-     * @return HashMap The resulting list.
+     * @return ArrayList The resulting list.
      *
      * @see #readMapXml
      * @see #readValueXml
@@ -490,6 +514,29 @@
         parser.setInput(in, null);
         return (ArrayList)readValueXml(parser, new String[1]);
     }
+    
+    
+    /**
+     * Read a HashSet from an InputStream containing XML. The stream can
+     * previously have been written by writeSetXml().
+     * 
+     * @param in The InputStream from which to read.
+     * 
+     * @return HashSet The resulting set.
+     * 
+     * @throws XmlPullParserException
+     * @throws java.io.IOException
+     * 
+     * @see #readValueXml
+     * @see #readThisSetXml
+     * @see #writeSetXml
+     */
+    public static final HashSet readSetXml(InputStream in)
+            throws XmlPullParserException, java.io.IOException {
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(in, null);
+        return (HashSet) readValueXml(parser, new String[1]);
+    }
 
     /**
      * Read a HashMap object from an XmlPullParser.  The XML data could
@@ -573,6 +620,47 @@
         throw new XmlPullParserException(
             "Document ended before " + endTag + " end tag");
     }
+    
+    /**
+     * Read a HashSet object from an XmlPullParser. The XML data could previously
+     * have been generated by writeSetXml(). The XmlPullParser must be positioned
+     * <em>after</em> the tag that begins the set.
+     * 
+     * @param parser The XmlPullParser from which to read the set data.
+     * @param endTag Name of the tag that will end the set, usually "set".
+     * @param name An array of one string, used to return the name attribute
+     *             of the set's tag.
+     *
+     * @return HashSet The newly generated set.
+     * 
+     * @throws XmlPullParserException
+     * @throws java.io.IOException
+     * 
+     * @see #readSetXml
+     */
+    public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
+            throws XmlPullParserException, java.io.IOException {
+        HashSet set = new HashSet();
+        
+        int eventType = parser.getEventType();
+        do {
+            if (eventType == parser.START_TAG) {
+                Object val = readThisValueXml(parser, name);
+                set.add(val);
+                //System.out.println("Adding to set: " + val);
+            } else if (eventType == parser.END_TAG) {
+                if (parser.getName().equals(endTag)) {
+                    return set;
+                }
+                throw new XmlPullParserException(
+                        "Expected " + endTag + " end tag at: " + parser.getName());
+            }
+            eventType = parser.next();
+        } while (eventType != parser.END_DOCUMENT);
+        
+        throw new XmlPullParserException(
+                "Document ended before " + endTag + " end tag");
+    }
 
     /**
      * Read an int[] object from an XmlPullParser.  The XML data could
@@ -740,6 +828,12 @@
             name[0] = valueName;
             //System.out.println("Returning value for " + valueName + ": " + res);
             return res;
+        } else if (tagName.equals("set")) {
+            parser.next();
+            res = readThisSetXml(parser, "set", name);
+            name[0] = valueName;
+            //System.out.println("Returning value for " + valueName + ": " + res);
+            return res;
         } else {
             throw new XmlPullParserException(
                 "Unknown tag: " + tagName);
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 4da74e6..d5213db 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -16,11 +16,14 @@
 
 package com.android.internal.view;
 
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.view.DragEvent;
 import android.view.IWindow;
 import android.view.IWindowSession;
 import android.view.KeyEvent;
@@ -66,7 +69,10 @@
             }
         }
     }
-    
+
+    public void dispatchDragEvent(DragEvent event) {
+    }
+
     public void dispatchWallpaperCommand(String action, int x, int y,
             int z, Bundle extras, boolean sync) {
         if (sync) {
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 8ff18ed..c7fcab8 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -23,6 +23,7 @@
 import android.view.MotionEvent;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
+import android.view.inputmethod.InputMethodSubtype;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodCallback;
 import com.android.internal.view.IInputMethodSession;
@@ -52,4 +53,6 @@
     void showSoftInput(int flags, in ResultReceiver resultReceiver);
     
     void hideSoftInput(int flags, in ResultReceiver resultReceiver);
+
+    void changeInputMethodSubtype(in InputMethodSubtype subtype);
 }
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index adec0a7..ca1cd59 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -18,6 +18,7 @@
 
 import android.os.ResultReceiver;
 import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.EditorInfo;
 import com.android.internal.view.InputBindResult;
 import com.android.internal.view.IInputContext;
@@ -48,10 +49,14 @@
             int softInputMode, boolean first, int windowFlags);
             
     void showInputMethodPickerFromClient(in IInputMethodClient client);
+    void showInputMethodSubtypePickerFromClient(in IInputMethodClient client);
+    void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
     void setInputMethod(in IBinder token, String id);
     void hideMySoftInput(in IBinder token, int flags);
     void showMySoftInput(in IBinder token, int flags);
     void updateStatusIcon(in IBinder token, String packageName, int iconId);
+    void setIMEButtonVisible(in IBinder token, boolean visible);
+    InputMethodSubtype getCurrentInputMethodSubtype();
     
     boolean setInputMethodEnabled(String id, boolean enabled);
 }
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
new file mode 100644
index 0000000..b54daba
--- /dev/null
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.view;
+
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuPopupHelper;
+import com.android.internal.view.menu.SubMenuBuilder;
+import com.android.internal.widget.ActionBarContextView;
+
+import android.content.Context;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import java.lang.ref.WeakReference;
+
+public class StandaloneActionMode extends ActionMode implements MenuBuilder.Callback {
+    private Context mContext;
+    private ActionBarContextView mContextView;
+    private ActionMode.Callback mCallback;
+    private WeakReference<View> mCustomView;
+    private boolean mFinished;
+
+    private MenuBuilder mMenu;
+
+    public StandaloneActionMode(Context context, ActionBarContextView view,
+            ActionMode.Callback callback) {
+        mContext = context;
+        mContextView = view;
+        mCallback = callback;
+
+        mMenu = new MenuBuilder(context).setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+        mMenu.setCallback(this);
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        mContextView.setTitle(title);
+    }
+
+    @Override
+    public void setSubtitle(CharSequence subtitle) {
+        mContextView.setSubtitle(subtitle);
+    }
+
+    @Override
+    public void setTitle(int resId) {
+        setTitle(mContext.getString(resId));
+    }
+
+    @Override
+    public void setSubtitle(int resId) {
+        setSubtitle(mContext.getString(resId));
+    }
+
+    @Override
+    public void setCustomView(View view) {
+        mContextView.setCustomView(view);
+        mCustomView = view != null ? new WeakReference<View>(view) : null;
+    }
+
+    @Override
+    public void invalidate() {
+        mCallback.onPrepareActionMode(this, mMenu);
+    }
+
+    @Override
+    public void finish() {
+        if (mFinished) {
+            return;
+        }
+        mFinished = true;
+
+        mCallback.onDestroyActionMode(this);
+    }
+
+    @Override
+    public Menu getMenu() {
+        return mMenu;
+    }
+
+    @Override
+    public CharSequence getTitle() {
+        return mContextView.getTitle();
+    }
+
+    @Override
+    public CharSequence getSubtitle() {
+        return mContextView.getSubtitle();
+    }
+
+    @Override
+    public View getCustomView() {
+        return mCustomView != null ? mCustomView.get() : null;
+    }
+
+    @Override
+    public MenuInflater getMenuInflater() {
+        return new MenuInflater(mContext);
+    }
+
+    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+        return mCallback.onActionItemClicked(this, item);
+    }
+
+    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+    }
+
+    public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+        if (!subMenu.hasVisibleItems()) {
+            return true;
+        }
+
+        new MenuPopupHelper(mContext, subMenu).show();
+        return true;
+    }
+
+    public void onCloseSubMenu(SubMenuBuilder menu) {
+    }
+
+    public void onMenuModeChange(MenuBuilder menu) {
+        invalidate();
+        mContextView.showOverflowMenu();
+    }
+}
diff --git a/core/java/com/android/internal/view/menu/ActionMenu.java b/core/java/com/android/internal/view/menu/ActionMenu.java
new file mode 100644
index 0000000..3d44ebc
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/ActionMenu.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view.menu;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+
+/**
+ * @hide
+ */
+public class ActionMenu implements Menu {
+    private Context mContext;
+    
+    private boolean mIsQwerty;
+    
+    private ArrayList<ActionMenuItem> mItems;
+    
+    public ActionMenu(Context context) {
+        mContext = context;
+        mItems = new ArrayList<ActionMenuItem>();
+    }
+    
+    public Context getContext() {
+        return mContext;
+    }
+
+    public MenuItem add(CharSequence title) {
+        return add(0, 0, 0, title);
+    }
+
+    public MenuItem add(int titleRes) {
+        return add(0, 0, 0, titleRes);
+    }
+
+    public MenuItem add(int groupId, int itemId, int order, int titleRes) {
+        return add(groupId, itemId, order, mContext.getResources().getString(titleRes));
+    }
+    
+    public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
+        ActionMenuItem item = new ActionMenuItem(getContext(),
+                groupId, itemId, 0, order, title);
+        mItems.add(order, item);
+        return item;
+    }
+
+    public int addIntentOptions(int groupId, int itemId, int order,
+            ComponentName caller, Intent[] specifics, Intent intent, int flags,
+            MenuItem[] outSpecificItems) {
+        PackageManager pm = mContext.getPackageManager();
+        final List<ResolveInfo> lri =
+                pm.queryIntentActivityOptions(caller, specifics, intent, 0);
+        final int N = lri != null ? lri.size() : 0;
+
+        if ((flags & FLAG_APPEND_TO_GROUP) == 0) {
+            removeGroup(groupId);
+        }
+
+        for (int i=0; i<N; i++) {
+            final ResolveInfo ri = lri.get(i);
+            Intent rintent = new Intent(
+                ri.specificIndex < 0 ? intent : specifics[ri.specificIndex]);
+            rintent.setComponent(new ComponentName(
+                    ri.activityInfo.applicationInfo.packageName,
+                    ri.activityInfo.name));
+            final MenuItem item = add(groupId, itemId, order, ri.loadLabel(pm))
+                    .setIcon(ri.loadIcon(pm))
+                    .setIntent(rintent);
+            if (outSpecificItems != null && ri.specificIndex >= 0) {
+                outSpecificItems[ri.specificIndex] = item;
+            }
+        }
+
+        return N;
+    }
+
+    public SubMenu addSubMenu(CharSequence title) {
+        // TODO Implement submenus
+        return null;
+    }
+
+    public SubMenu addSubMenu(int titleRes) {
+        // TODO Implement submenus
+        return null;
+    }
+
+    public SubMenu addSubMenu(int groupId, int itemId, int order,
+            CharSequence title) {
+        // TODO Implement submenus
+        return null;
+    }
+
+    public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
+        // TODO Implement submenus
+        return null;
+    }
+
+    public void clear() {
+        mItems.clear();
+    }
+
+    public void close() {
+    }
+    
+    private int findItemIndex(int id) {
+        final ArrayList<ActionMenuItem> items = mItems;
+        final int itemCount = items.size();
+        for (int i = 0; i < itemCount; i++) {
+            if (items.get(i).getItemId() == id) {
+                return i;
+            }
+        }
+        
+        return -1;
+    }
+
+    public MenuItem findItem(int id) {
+        return mItems.get(findItemIndex(id));
+    }
+
+    public MenuItem getItem(int index) {
+        return mItems.get(index);
+    }
+
+    public boolean hasVisibleItems() {
+        final ArrayList<ActionMenuItem> items = mItems;
+        final int itemCount = items.size();
+        
+        for (int i = 0; i < itemCount; i++) {
+            if (items.get(i).isVisible()) {
+                return true;
+            }
+        }
+        
+        return false;
+    }
+    
+    private ActionMenuItem findItemWithShortcut(int keyCode, KeyEvent event) {
+        // TODO Make this smarter.
+        final boolean qwerty = mIsQwerty;
+        final ArrayList<ActionMenuItem> items = mItems;
+        final int itemCount = items.size();
+        
+        for (int i = 0; i < itemCount; i++) {
+            ActionMenuItem item = items.get(i);
+            final char shortcut = qwerty ? item.getAlphabeticShortcut() :
+                    item.getNumericShortcut();
+            if (keyCode == shortcut) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    public boolean isShortcutKey(int keyCode, KeyEvent event) {
+        return findItemWithShortcut(keyCode, event) != null;
+    }
+
+    public boolean performIdentifierAction(int id, int flags) {
+        final int index = findItemIndex(id);
+        if (index < 0) {
+            return false;
+        }
+
+        return mItems.get(index).invoke();
+    }
+
+    public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
+        ActionMenuItem item = findItemWithShortcut(keyCode, event);
+        if (item == null) {
+            return false;
+        }
+        
+        return item.invoke();
+    }
+
+    public void removeGroup(int groupId) {
+        final ArrayList<ActionMenuItem> items = mItems;
+        int itemCount = items.size();
+        int i = 0;
+        while (i < itemCount) {
+            if (items.get(i).getGroupId() == groupId) {
+                items.remove(i);
+                itemCount--;
+            } else {
+                i++;
+            }
+        }
+    }
+
+    public void removeItem(int id) {
+        mItems.remove(findItemIndex(id));
+    }
+
+    public void setGroupCheckable(int group, boolean checkable,
+            boolean exclusive) {
+        final ArrayList<ActionMenuItem> items = mItems;
+        final int itemCount = items.size();
+        
+        for (int i = 0; i < itemCount; i++) {
+            ActionMenuItem item = items.get(i);
+            if (item.getGroupId() == group) {
+                item.setCheckable(checkable);
+                item.setExclusiveCheckable(exclusive);
+            }
+        }
+    }
+
+    public void setGroupEnabled(int group, boolean enabled) {
+        final ArrayList<ActionMenuItem> items = mItems;
+        final int itemCount = items.size();
+        
+        for (int i = 0; i < itemCount; i++) {
+            ActionMenuItem item = items.get(i);
+            if (item.getGroupId() == group) {
+                item.setEnabled(enabled);
+            }
+        }
+    }
+
+    public void setGroupVisible(int group, boolean visible) {
+        final ArrayList<ActionMenuItem> items = mItems;
+        final int itemCount = items.size();
+        
+        for (int i = 0; i < itemCount; i++) {
+            ActionMenuItem item = items.get(i);
+            if (item.getGroupId() == group) {
+                item.setVisible(visible);
+            }
+        }
+    }
+
+    public void setQwertyMode(boolean isQwerty) {
+        mIsQwerty = isQwerty;
+    }
+
+    public int size() {
+        return mItems.size();
+    }
+}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java
new file mode 100644
index 0000000..d2851a9
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view.menu;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.View;
+
+/**
+ * @hide
+ */
+public class ActionMenuItem implements MenuItem {
+    private final int mId;
+    private final int mGroup;
+    private final int mCategoryOrder;
+    private final int mOrdering;
+    
+    private CharSequence mTitle;
+    private CharSequence mTitleCondensed;
+    private Intent mIntent;
+    private char mShortcutNumericChar;
+    private char mShortcutAlphabeticChar;
+    
+    private Drawable mIconDrawable;
+    private int mIconResId = NO_ICON;
+    
+    private Context mContext;
+    
+    private MenuItem.OnMenuItemClickListener mClickListener;
+    
+    private static final int NO_ICON = 0;
+    
+    private int mFlags = ENABLED;
+    private static final int CHECKABLE      = 0x00000001;
+    private static final int CHECKED        = 0x00000002;
+    private static final int EXCLUSIVE      = 0x00000004;
+    private static final int HIDDEN         = 0x00000008;
+    private static final int ENABLED        = 0x00000010;
+    
+    public ActionMenuItem(Context context, int group, int id, int categoryOrder, int ordering,
+            CharSequence title) {
+        mContext = context;
+        mId = id;
+        mGroup = group;
+        mCategoryOrder = categoryOrder;
+        mOrdering = ordering;
+        mTitle = title;
+    }
+    
+    public char getAlphabeticShortcut() {
+        return mShortcutAlphabeticChar;
+    }
+
+    public int getGroupId() {
+        return mGroup;
+    }
+
+    public Drawable getIcon() {
+        return mIconDrawable;
+    }
+
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    public int getItemId() {
+        return mId;
+    }
+
+    public ContextMenuInfo getMenuInfo() {
+        return null;
+    }
+
+    public char getNumericShortcut() {
+        return mShortcutNumericChar;
+    }
+
+    public int getOrder() {
+        return mOrdering;
+    }
+
+    public SubMenu getSubMenu() {
+        return null;
+    }
+
+    public CharSequence getTitle() {
+        return mTitle;
+    }
+
+    public CharSequence getTitleCondensed() {
+        return mTitleCondensed;
+    }
+
+    public boolean hasSubMenu() {
+        return false;
+    }
+
+    public boolean isCheckable() {
+        return (mFlags & CHECKABLE) != 0; 
+    }
+
+    public boolean isChecked() {
+        return (mFlags & CHECKED) != 0;
+    }
+
+    public boolean isEnabled() {
+        return (mFlags & ENABLED) != 0;
+    }
+
+    public boolean isVisible() {
+        return (mFlags & HIDDEN) == 0;
+    }
+
+    public MenuItem setAlphabeticShortcut(char alphaChar) {
+        mShortcutAlphabeticChar = alphaChar;
+        return this;
+    }
+
+    public MenuItem setCheckable(boolean checkable) {
+        mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
+        return this;
+    }
+    
+    public ActionMenuItem setExclusiveCheckable(boolean exclusive) {
+        mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
+        return this;
+    }
+
+    public MenuItem setChecked(boolean checked) {
+        mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
+        return this;
+    }
+
+    public MenuItem setEnabled(boolean enabled) {
+        mFlags = (mFlags & ~ENABLED) | (enabled ? ENABLED : 0);
+        return this;
+    }
+
+    public MenuItem setIcon(Drawable icon) {
+        mIconDrawable = icon;
+        mIconResId = NO_ICON;
+        return this;
+    }
+
+    public MenuItem setIcon(int iconRes) {
+        mIconResId = iconRes;
+        mIconDrawable = mContext.getResources().getDrawable(iconRes);
+        return this;
+    }
+
+    public MenuItem setIntent(Intent intent) {
+        mIntent = intent;
+        return this;
+    }
+
+    public MenuItem setNumericShortcut(char numericChar) {
+        mShortcutNumericChar = numericChar;
+        return this;
+    }
+
+    public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
+        mClickListener = menuItemClickListener;
+        return this;
+    }
+
+    public MenuItem setShortcut(char numericChar, char alphaChar) {
+        mShortcutNumericChar = numericChar;
+        mShortcutAlphabeticChar = alphaChar;
+        return this;
+    }
+
+    public MenuItem setTitle(CharSequence title) {
+        mTitle = title;
+        return this;
+    }
+
+    public MenuItem setTitle(int title) {
+        mTitle = mContext.getResources().getString(title);
+        return this;
+    }
+
+    public MenuItem setTitleCondensed(CharSequence title) {
+        mTitleCondensed = title;
+        return this;
+    }
+
+    public MenuItem setVisible(boolean visible) {
+        mFlags = (mFlags & HIDDEN) | (visible ? 0 : HIDDEN);
+        return this;
+    }
+
+    public boolean invoke() {
+        if (mClickListener != null && mClickListener.onMenuItemClick(this)) {
+            return true;
+        }
+        
+        if (mIntent != null) {
+            mContext.startActivity(mIntent);
+            return true;
+        }
+        
+        return false;
+    }
+    
+    public void setShowAsAction(int show) {
+        // Do nothing. ActionMenuItems always show as action buttons.
+    }
+
+    public MenuItem setActionView(View actionView) {
+        throw new UnsupportedOperationException();
+    }
+
+    public View getActionView() {
+        return null;
+    }
+}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
new file mode 100644
index 0000000..d5f69ba
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view.menu;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+/**
+ * @hide
+ */
+public class ActionMenuItemView extends LinearLayout
+        implements MenuView.ItemView, View.OnClickListener {
+    private static final String TAG = "ActionMenuItemView";
+
+    private MenuItemImpl mItemData;
+    private CharSequence mTitle;
+    private MenuBuilder.ItemInvoker mItemInvoker;
+
+    private ImageButton mImageButton;
+    private Button mTextButton;
+
+    public ActionMenuItemView(Context context) {
+        this(context, null);
+    }
+
+    public ActionMenuItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public void onFinishInflate() {
+        mImageButton = (ImageButton) findViewById(com.android.internal.R.id.imageButton);
+        mTextButton = (Button) findViewById(com.android.internal.R.id.textButton);
+        mImageButton.setOnClickListener(this);
+        mTextButton.setOnClickListener(this);
+    }
+
+    public MenuItemImpl getItemData() {
+        return mItemData;
+    }
+
+    public void initialize(MenuItemImpl itemData, int menuType) {
+        mItemData = itemData;
+
+        setIcon(itemData.getIcon());
+        setTitle(itemData.getTitle()); // Title only takes effect if there is no icon
+        setId(itemData.getItemId());
+
+        setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
+        setEnabled(itemData.isEnabled());
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        mImageButton.setEnabled(enabled);
+        mTextButton.setEnabled(enabled);
+    }
+
+    public void onClick(View v) {
+        if (mItemInvoker != null) {
+            mItemInvoker.invokeItem(mItemData);
+        }
+    }
+
+    public void setItemInvoker(MenuBuilder.ItemInvoker invoker) {
+        mItemInvoker = invoker;
+    }
+
+    public boolean prefersCondensedTitle() {
+        return false;
+    }
+
+    public void setCheckable(boolean checkable) {
+        // TODO Support checkable action items
+    }
+
+    public void setChecked(boolean checked) {
+        // TODO Support checkable action items
+    }
+
+    public void setIcon(Drawable icon) {
+        mImageButton.setImageDrawable(icon);
+        if (icon != null) {
+            mImageButton.setVisibility(VISIBLE);
+            mTextButton.setVisibility(GONE);
+        } else {
+            mImageButton.setVisibility(GONE);
+        }
+    }
+    
+    public boolean hasText() {
+        return mTextButton.getVisibility() != GONE;
+    }
+
+    public void setShortcut(boolean showShortcut, char shortcutKey) {
+        // Action buttons don't show text for shortcut keys.
+    }
+
+    public void setTitle(CharSequence title) {
+        mTitle = title;
+
+        // populate accessibility description with title
+        setContentDescription(title);
+
+        if (mImageButton.getDrawable() == null || mItemData.showsTextAsAction()) {
+            mTextButton.setText(mTitle);
+            mTextButton.setVisibility(VISIBLE);
+        }
+    }
+
+    public boolean showsIcon() {
+        return true;
+    }
+}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
new file mode 100644
index 0000000..621defe
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.view.menu;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import java.util.ArrayList;
+
+/**
+ * @hide
+ */
+public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvoker, MenuView {
+    private static final String TAG = "ActionMenuView";
+
+    // TODO Theme/style this.
+    private static final int DIVIDER_PADDING = 12; // dips
+    
+    private MenuBuilder mMenu;
+
+    private int mMaxItems;
+    private boolean mReserveOverflow;
+    private OverflowMenuButton mOverflowButton;
+    private MenuPopupHelper mOverflowPopup;
+    
+    private float mButtonPaddingLeft;
+    private float mButtonPaddingRight;
+    private float mDividerPadding;
+    
+    private Drawable mDivider;
+
+    private Runnable mShowOverflow = new Runnable() {
+        public void run() {
+            showOverflowMenu();
+        }
+    };
+    
+    public ActionMenuView(Context context) {
+        this(context, null);
+    }
+    
+    public ActionMenuView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        
+        final Resources res = getResources();
+
+        // Measure for initial configuration
+        mMaxItems = getMaxActionButtons();
+
+        // TODO There has to be a better way to indicate that we don't have a hard menu key.
+        final int screen = res.getConfiguration().screenLayout;
+        mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
+                Configuration.SCREENLAYOUT_SIZE_XLARGE;
+        
+        TypedArray a = context.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+        final int buttonStyle = a.getResourceId(
+                com.android.internal.R.styleable.Theme_actionButtonStyle, 0);
+        final int groupStyle = a.getResourceId(
+                com.android.internal.R.styleable.Theme_buttonGroupStyle, 0);
+        a.recycle();
+        
+        a = context.obtainStyledAttributes(buttonStyle, com.android.internal.R.styleable.View);
+        mButtonPaddingLeft = a.getDimension(com.android.internal.R.styleable.View_paddingLeft, 0);
+        mButtonPaddingRight = a.getDimension(com.android.internal.R.styleable.View_paddingRight, 0);
+        a.recycle();
+        
+        a = context.obtainStyledAttributes(groupStyle,
+                com.android.internal.R.styleable.ButtonGroup);
+        mDivider = a.getDrawable(com.android.internal.R.styleable.ButtonGroup_divider);
+        a.recycle();
+        
+        mDividerPadding = DIVIDER_PADDING * res.getDisplayMetrics().density;
+
+        setBaselineAligned(false);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        final int screen = newConfig.screenLayout;
+        mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
+                Configuration.SCREENLAYOUT_SIZE_XLARGE;
+        mMaxItems = getMaxActionButtons();
+        if (mMenu != null) {
+            mMenu.setMaxActionItems(mMaxItems);
+            updateChildren(false);
+        }
+
+        if (mOverflowPopup != null && mOverflowPopup.isShowing()) {
+            mOverflowPopup.dismiss();
+            post(mShowOverflow);
+        }
+    }
+
+    private int getMaxActionButtons() {
+        return getResources().getInteger(com.android.internal.R.integer.max_action_buttons);
+    }
+
+    public boolean isOverflowReserved() {
+        return mReserveOverflow;
+    }
+    
+    public void setOverflowReserved(boolean reserveOverflow) {
+        mReserveOverflow = reserveOverflow;
+    }
+    
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                LayoutParams.WRAP_CONTENT);
+        params.gravity = Gravity.CENTER_VERTICAL;
+        return params;
+    }
+    
+    @Override
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        return generateDefaultLayoutParams();
+    }
+
+    public boolean invokeItem(MenuItemImpl item) {
+        return mMenu.performItemAction(item, 0);
+    }
+
+    public int getWindowAnimations() {
+        return 0;
+    }
+
+    public void initialize(MenuBuilder menu, int menuType) {
+        menu.setMaxActionItems(mMaxItems);
+        mMenu = menu;
+        updateChildren(true);
+    }
+
+    public void updateChildren(boolean cleared) {
+        final boolean reserveOverflow = mReserveOverflow;
+        removeAllViews();
+        
+        final ArrayList<MenuItemImpl> itemsToShow = mMenu.getActionItems(reserveOverflow);
+        final int itemCount = itemsToShow.size();
+        
+        boolean needsDivider = false;
+        for (int i = 0; i < itemCount; i++) {
+            if (needsDivider) {
+                addView(makeDividerView(), makeDividerLayoutParams());
+            }
+            final MenuItemImpl itemData = itemsToShow.get(i);
+            final View actionView = itemData.getActionView();
+            if (actionView != null) {
+                final ViewParent parent = actionView.getParent();
+                if (parent instanceof ViewGroup) {
+                    ((ViewGroup) parent).removeView(actionView);
+                }
+                addView(actionView, makeActionViewLayoutParams());
+            } else {
+                needsDivider = addItemView(i == 0 || !needsDivider,
+                        (ActionMenuItemView) itemData.getItemView(
+                                MenuBuilder.TYPE_ACTION_BUTTON, this));
+            }
+        }
+
+        if (reserveOverflow) {
+            if (mMenu.getNonActionItems(true).size() > 0) {
+                OverflowMenuButton button = new OverflowMenuButton(mContext);
+                addView(button);
+                mOverflowButton = button;
+            } else {
+                mOverflowButton = null;
+            }
+        }
+    }
+
+    public boolean showOverflowMenu() {
+        if (mOverflowButton != null) {
+            final MenuPopupHelper popup =
+                    new MenuPopupHelper(getContext(), mMenu, mOverflowButton, true);
+            // Post this for later; we might still need a layout for the anchor to be right.
+            post(new Runnable() {
+                public void run() {
+                    popup.show();
+                }
+            });
+            mOverflowPopup = popup;
+            return true;
+        }
+        return false;
+    }
+
+    public boolean isOverflowMenuShowing() {
+        MenuPopupHelper popup = mOverflowPopup;
+        if (popup != null) {
+            return popup.isShowing();
+        }
+        return false;
+    }
+
+    public boolean hideOverflowMenu() {
+        MenuPopupHelper popup = mOverflowPopup;
+        if (popup != null) {
+            popup.dismiss();
+            return true;
+        }
+        return false;
+    }
+
+    private boolean addItemView(boolean needsDivider, ActionMenuItemView view) {
+        view.setItemInvoker(this);
+        boolean hasText = view.hasText();
+        
+        if (hasText && needsDivider) {
+            addView(makeDividerView(), makeDividerLayoutParams());
+        }
+        addView(view);
+        return hasText;
+    }
+
+    private ImageView makeDividerView() {
+        ImageView result = new ImageView(mContext);
+        result.setImageDrawable(mDivider);
+        result.setScaleType(ImageView.ScaleType.FIT_XY);
+        return result;
+    }
+
+    private LayoutParams makeDividerLayoutParams() {
+        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                LayoutParams.MATCH_PARENT);
+        params.topMargin = (int) mDividerPadding;
+        params.bottomMargin = (int) mDividerPadding;
+        return params;
+    }
+
+    private LayoutParams makeActionViewLayoutParams() {
+        LayoutParams params = generateDefaultLayoutParams();
+        params.leftMargin = (int) mButtonPaddingLeft;
+        params.rightMargin = (int) mButtonPaddingRight;
+        return params;
+    }
+
+    private class OverflowMenuButton extends ImageButton {
+        public OverflowMenuButton(Context context) {
+            super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
+
+            setClickable(true);
+            setFocusable(true);
+            setVisibility(VISIBLE);
+            setEnabled(true);
+        }
+
+        @Override
+        public boolean performClick() {
+            if (super.performClick()) {
+                return true;
+            }
+
+            // Change to overflow mode
+            mMenu.getCallback().onMenuModeChange(mMenu);
+            return true;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/view/menu/IconMenuView.java b/core/java/com/android/internal/view/menu/IconMenuView.java
index beb57ba..178dcde 100644
--- a/core/java/com/android/internal/view/menu/IconMenuView.java
+++ b/core/java/com/android/internal/view/menu/IconMenuView.java
@@ -337,7 +337,9 @@
         // This method does a clear refresh of children
         removeAllViews();
         
-        final ArrayList<MenuItemImpl> itemsToShow = mMenu.getVisibleItems();
+        // IconMenuView never wants content sorted for an overflow action button, since
+        // it is never used in the presence of an overflow button.
+        final ArrayList<MenuItemImpl> itemsToShow = mMenu.getNonActionItems(false);
         final int numItems = itemsToShow.size();
         final int numItemsThatCanFit = mMaxItems;
         // Minimum of the num that can fit and the num that we have
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 228d5d0..d0faea6 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -27,16 +27,18 @@
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.util.Log;
 import android.util.SparseArray;
+import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.SubMenu;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.LayoutInflater;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
@@ -54,7 +56,7 @@
     private static final String LOGTAG = "MenuBuilder";
     
     /** The number of different menu types */
-    public static final int NUM_TYPES = 3;
+    public static final int NUM_TYPES = 5;
     /** The menu type that represents the icon menu view */
     public static final int TYPE_ICON = 0;
     /** The menu type that represents the expanded menu view */
@@ -65,14 +67,28 @@
      * have an ItemView.
      */
     public static final int TYPE_DIALOG = 2;
+    /**
+     * The menu type that represents a button in the application's action bar.
+     */
+    public static final int TYPE_ACTION_BUTTON = 3;
+    /**
+     * The menu type that represents a menu popup.
+     */
+    public static final int TYPE_POPUP = 4;
 
     private static final String VIEWS_TAG = "android:views";
-    
+
+    private static final int THEME_SYSTEM_DEFAULT = 0;
+    private static final int THEME_APPLICATION = -1;
+    private static final int THEME_ALERT_DIALOG = -2;
+
     // Order must be the same order as the TYPE_*
     static final int THEME_RES_FOR_TYPE[] = new int[] {
         com.android.internal.R.style.Theme_IconMenu,
         com.android.internal.R.style.Theme_ExpandedMenu,
-        0,
+        THEME_ALERT_DIALOG,
+        THEME_APPLICATION,
+        THEME_APPLICATION,
     };
     
     // Order must be the same order as the TYPE_*
@@ -80,6 +96,8 @@
         com.android.internal.R.layout.icon_menu_layout,
         com.android.internal.R.layout.expanded_menu_layout,
         0,
+        com.android.internal.R.layout.action_menu_layout,
+        0,
     };
 
     // Order must be the same order as the TYPE_*
@@ -87,6 +105,8 @@
         com.android.internal.R.layout.icon_menu_item_layout,
         com.android.internal.R.layout.list_menu_item_layout,
         com.android.internal.R.layout.list_menu_item_layout,
+        com.android.internal.R.layout.action_menu_item_layout,
+        com.android.internal.R.layout.popup_menu_item_layout,
     };
 
     private static final int[]  sCategoryToOrder = new int[] {
@@ -130,6 +150,35 @@
      * fetched from {@link #getVisibleItems()}
      */ 
     private boolean mIsVisibleItemsStale;
+    
+    /**
+     * Contains only the items that should appear in the Action Bar, if present.
+     */
+    private ArrayList<MenuItemImpl> mActionItems;
+    /**
+     * Contains items that should NOT appear in the Action Bar, if present.
+     */
+    private ArrayList<MenuItemImpl> mNonActionItems;
+    /**
+     * The number of visible action buttons permitted in this menu
+     */
+    private int mMaxActionItems;
+    /**
+     * Whether or not the items (or any one item's action state) has changed since it was
+     * last fetched.
+     */
+    private boolean mIsActionItemsStale;
+
+    /**
+     * Whether the process of granting space as action items should reserve a space for
+     * an overflow option in the action list.
+     */
+    private boolean mReserveActionOverflow;
+
+    /**
+     * Default value for how added items should show in the action list.
+     */
+    private int mDefaultShowAsAction = MenuItem.SHOW_AS_ACTION_NEVER;
 
     /**
      * Current use case is Context Menus: As Views populate the context menu, each one has
@@ -158,7 +207,14 @@
     private boolean mPreventDispatchingItemsChanged = false;
     
     private boolean mOptionalIconsVisible = false;
-    
+
+    private static int getAlertDialogTheme(Context context) {
+        TypedValue outValue = new TypedValue();
+        context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme,
+                outValue, true);
+        return outValue.resourceId;
+    }
+
     private MenuType[] mMenuTypes;
     class MenuType {
         private int mMenuType;
@@ -176,8 +232,20 @@
         LayoutInflater getInflater() {
             // Create an inflater that uses the given theme for the Views it inflates
             if (mInflater == null) {
-                Context wrappedContext = new ContextThemeWrapper(mContext,
-                        THEME_RES_FOR_TYPE[mMenuType]); 
+                Context wrappedContext;
+                int themeResForType = THEME_RES_FOR_TYPE[mMenuType];
+                switch (themeResForType) {
+                    case THEME_APPLICATION:
+                        wrappedContext = mContext;
+                        break;
+                    case THEME_ALERT_DIALOG:
+                        wrappedContext = new ContextThemeWrapper(mContext,
+                                getAlertDialogTheme(mContext));
+                        break;
+                    default:
+                        wrappedContext = new ContextThemeWrapper(mContext, themeResForType);
+                        break;
+                }
                 mInflater = (LayoutInflater) wrappedContext
                         .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
             }
@@ -281,10 +349,19 @@
         mVisibleItems = new ArrayList<MenuItemImpl>();
         mIsVisibleItemsStale = true;
         
+        mActionItems = new ArrayList<MenuItemImpl>();
+        mNonActionItems = new ArrayList<MenuItemImpl>();
+        mIsActionItemsStale = true;
+        
         mShortcutsVisible =
                 (mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS);
     }
     
+    public MenuBuilder setDefaultShowAsAction(int defaultShowAsAction) {
+        mDefaultShowAsAction = defaultShowAsAction;
+        return this;
+    }
+    
     public void setCallback(Callback callback) {
         mCallback = callback;
     }
@@ -368,7 +445,8 @@
     private MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
         final int ordering = getOrdering(categoryOrder);
         
-        final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder, ordering, title);
+        final MenuItemImpl item = new MenuItemImpl(this, group, id, categoryOrder,
+                ordering, title, mDefaultShowAsAction);
 
         if (mCurrentMenuInfo != null) {
             // Pass along the current menu info
@@ -633,6 +711,11 @@
         return mItems.get(index);
     }
 
+    public MenuItem getOverflowItem(int index) {
+        flagActionItems(true);
+        return mNonActionItems.get(index);
+    }
+
     public boolean isShortcutKey(int keyCode, KeyEvent event) {
         return findItemWithShortcutForKey(keyCode, event) != null;
     }
@@ -900,6 +983,7 @@
     private void onItemsChanged(boolean cleared) {
         if (!mPreventDispatchingItemsChanged) {
             if (mIsVisibleItemsStale == false) mIsVisibleItemsStale = true;
+            if (mIsActionItemsStale == false) mIsActionItemsStale = true;
             
             MenuType[] menuTypes = mMenuTypes;
             for (int i = 0; i < NUM_TYPES; i++) {
@@ -920,6 +1004,15 @@
         onItemsChanged(false);
     }
     
+    /**
+     * Called by {@link MenuItemImpl} when its action request status is changed.
+     * @param item The item that has gone through a change in action request status.
+     */
+    void onItemActionRequestChanged(MenuItemImpl item) {
+        // Notify of items being changed
+        onItemsChanged(false);
+    }
+    
     ArrayList<MenuItemImpl> getVisibleItems() {
         if (!mIsVisibleItemsStale) return mVisibleItems;
         
@@ -934,9 +1027,83 @@
         }
         
         mIsVisibleItemsStale = false;
+        mIsActionItemsStale = true;
         
         return mVisibleItems;
     }
+    
+    private void flagActionItems(boolean reserveActionOverflow) {
+        if (reserveActionOverflow != mReserveActionOverflow) {
+            mReserveActionOverflow = reserveActionOverflow;
+            mIsActionItemsStale = true;
+        }
+
+        if (!mIsActionItemsStale) {
+            return;
+        }
+
+        final ArrayList<MenuItemImpl> visibleItems = getVisibleItems();
+        final int itemsSize = visibleItems.size();
+        int maxActions = mMaxActionItems;
+
+        int requiredItems = 0;
+        int requestedItems = 0;
+        boolean hasOverflow = false;
+        for (int i = 0; i < itemsSize; i++) {
+            MenuItemImpl item = visibleItems.get(i);
+            if (item.requiresActionButton()) {
+                requiredItems++;
+            } else if (item.requestsActionButton()) {
+                requestedItems++;
+            } else {
+                hasOverflow = true;
+            }
+        }
+
+        // Reserve a spot for the overflow item if needed.
+        if (reserveActionOverflow &&
+                (hasOverflow || requiredItems + requestedItems > maxActions)) {
+            maxActions--;
+        }
+        maxActions -= requiredItems;
+
+        // Flag as many more requested items as will fit.
+        for (int i = 0; i < itemsSize; i++) {
+            MenuItemImpl item = visibleItems.get(i);
+            if (item.requestsActionButton()) {
+                item.setIsActionButton(maxActions > 0);
+                maxActions--;
+            }
+        }
+
+        mActionItems.clear();
+        mNonActionItems.clear();
+        for (int i = 0; i < itemsSize; i++) {
+            MenuItemImpl item = visibleItems.get(i);
+            if (item.isActionButton()) {
+                mActionItems.add(item);
+            } else {
+                mNonActionItems.add(item);
+            }
+        }
+
+        mIsActionItemsStale = false;
+    }
+    
+    ArrayList<MenuItemImpl> getActionItems(boolean reserveActionOverflow) {
+        flagActionItems(reserveActionOverflow);
+        return mActionItems;
+    }
+    
+    ArrayList<MenuItemImpl> getNonActionItems(boolean reserveActionOverflow) {
+        flagActionItems(reserveActionOverflow);
+        return mNonActionItems;
+    }
+    
+    void setMaxActionItems(int maxActionItems) {
+        mMaxActionItems = maxActionItems;
+        mIsActionItemsStale = true;
+    }
 
     public void clearHeader() {
         mHeaderIcon = null;
@@ -1078,6 +1245,16 @@
         return new MenuAdapter(menuType);
     }
 
+    /**
+     * Gets an adapter for providing overflow (non-action) items and their views.
+     *
+     * @param menuType The type of menu to get an adapter for.
+     * @return A {@link MenuAdapter} for this menu with the given menu type.
+     */
+    public MenuAdapter getOverflowMenuAdapter(int menuType) {
+        return new OverflowMenuAdapter(menuType);
+    }
+
     void setOptionalIconsVisible(boolean visible) {
         mOptionalIconsVisible = visible;
     }
@@ -1155,8 +1332,42 @@
         }
 
         public View getView(int position, View convertView, ViewGroup parent) {
-            return ((MenuItemImpl) getItem(position)).getItemView(mMenuType, parent);
+            if (convertView != null) {
+                MenuView.ItemView itemView = (MenuView.ItemView) convertView;
+                itemView.getItemData().setItemView(mMenuType, null);
+
+                MenuItemImpl item = (MenuItemImpl) getItem(position);
+                itemView.initialize(item, mMenuType);
+                item.setItemView(mMenuType, itemView);
+                return convertView;
+            } else {
+                MenuItemImpl item = (MenuItemImpl) getItem(position);
+                item.setItemView(mMenuType, null);
+                return item.getItemView(mMenuType, parent);
+            }
+        }
+    }
+
+    /**
+     * An adapter that allows an {@link AdapterView} to use this {@link MenuBuilder} as a data
+     * source for overflow menu items that do not fit in the list of action items.
+     */
+    private class OverflowMenuAdapter extends MenuAdapter {
+        private ArrayList<MenuItemImpl> mOverflowItems;
+
+        public OverflowMenuAdapter(int menuType) {
+            super(menuType);
+            mOverflowItems = getNonActionItems(true);
         }
 
+        @Override
+        public MenuItemImpl getItem(int position) {
+            return mOverflowItems.get(position);
+        }
+
+        @Override
+        public int getCount() {
+            return mOverflowItems.size();
+        }
     }
 }
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 9b58205..8eee360 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -38,6 +38,10 @@
 public final class MenuItemImpl implements MenuItem {
     private static final String TAG = "MenuItemImpl";
     
+    private static final int SHOW_AS_ACTION_MASK = SHOW_AS_ACTION_NEVER |
+            SHOW_AS_ACTION_IF_ROOM |
+            SHOW_AS_ACTION_ALWAYS;
+
     private final int mId;
     private final int mGroup;
     private final int mCategoryOrder;
@@ -74,6 +78,11 @@
     private static final int EXCLUSIVE      = 0x00000004;
     private static final int HIDDEN         = 0x00000008;
     private static final int ENABLED        = 0x00000010;
+    private static final int IS_ACTION      = 0x00000020;
+
+    private int mShowAsAction = SHOW_AS_ACTION_NEVER;
+
+    private View mActionView;
 
     /** Used for the icon resource ID if this item does not have an icon */
     static final int NO_ICON = 0;
@@ -105,7 +114,7 @@
      * @param title The text to display for the item.
      */
     MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
-            CharSequence title) {
+            CharSequence title, int showAsAction) {
 
         if (sPrependShortcutLabel == null) {
             // This is instantiated from the UI thread, so no chance of sync issues 
@@ -126,6 +135,7 @@
         mCategoryOrder = categoryOrder;
         mOrdering = ordering;
         mTitle = title;
+        mShowAsAction = showAsAction;
     }
     
     /**
@@ -580,6 +590,10 @@
         return (View) mItemViews[menuType].get();
     }
 
+    void setItemView(int menuType, ItemView view) {
+        mItemViews[menuType] = new WeakReference<ItemView>(view);
+    }
+
     /**
      * Create and initializes a menu item view that implements {@link MenuView.ItemView}.
      * @param menuType The type of menu to get a View for (must be one of
@@ -628,6 +642,59 @@
      * @return Whether the given menu type should show icons for menu items.
      */
     public boolean shouldShowIcon(int menuType) {
-        return menuType == MenuBuilder.TYPE_ICON || mMenu.getOptionalIconsVisible();
+        return menuType == MenuBuilder.TYPE_ICON ||
+                menuType == MenuBuilder.TYPE_ACTION_BUTTON ||
+                menuType == MenuBuilder.TYPE_POPUP ||
+                mMenu.getOptionalIconsVisible();
+    }
+    
+    public boolean isActionButton() {
+        return (mFlags & IS_ACTION) == IS_ACTION || requiresActionButton();
+    }
+    
+    public boolean requestsActionButton() {
+        return (mShowAsAction & SHOW_AS_ACTION_IF_ROOM) == SHOW_AS_ACTION_IF_ROOM;
+    }
+    
+    public boolean requiresActionButton() {
+        return (mShowAsAction & SHOW_AS_ACTION_ALWAYS) == SHOW_AS_ACTION_ALWAYS;
+    }
+
+    public void setIsActionButton(boolean isActionButton) {
+        if (isActionButton) {
+            mFlags |= IS_ACTION;
+        } else {
+            mFlags &= ~IS_ACTION;
+        }
+    }
+
+    public boolean showsTextAsAction() {
+        return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT;
+    }
+
+    public void setShowAsAction(int actionEnum) {
+        switch (actionEnum & SHOW_AS_ACTION_MASK) {
+            case SHOW_AS_ACTION_ALWAYS:
+            case SHOW_AS_ACTION_IF_ROOM:
+            case SHOW_AS_ACTION_NEVER:
+                // Looks good!
+                break;
+
+            default:
+                // Mutually exclusive options selected!
+                throw new IllegalArgumentException("SHOW_AS_ACTION_ALWAYS, SHOW_AS_ACTION_IF_ROOM,"
+                        + " and SHOW_AS_ACTION_NEVER are mutually exclusive.");
+        }
+        mShowAsAction = actionEnum;
+        mMenu.onItemActionRequestChanged(this);
+    }
+
+    public MenuItem setActionView(View view) {
+        mActionView = view;
+        return this;
+    }
+
+    public View getActionView() {
+        return mActionView;
     }
 }
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
new file mode 100644
index 0000000..c947824
--- /dev/null
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view.menu;
+
+import com.android.internal.view.menu.MenuBuilder.MenuAdapter;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.view.KeyEvent;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.widget.AdapterView;
+import android.widget.ListPopupWindow;
+import android.widget.PopupWindow;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * @hide
+ */
+public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener {
+    private static final String TAG = "MenuPopupHelper";
+
+    private Context mContext;
+    private ListPopupWindow mPopup;
+    private MenuBuilder mMenu;
+    private int mPopupMaxWidth;
+    private WeakReference<View> mAnchorView;
+    private boolean mOverflowOnly;
+
+    private PopupWindow.OnDismissListener mDismissListener = new PopupWindow.OnDismissListener() {
+        public void onDismiss() {
+            mPopup = null;
+        }
+    };
+
+    public MenuPopupHelper(Context context, MenuBuilder menu) {
+        this(context, menu, null, false);
+    }
+
+    public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) {
+        this(context, menu, anchorView, false);
+    }
+
+    public MenuPopupHelper(Context context, MenuBuilder menu,
+            View anchorView, boolean overflowOnly) {
+        mContext = context;
+        mMenu = menu;
+        mOverflowOnly = overflowOnly;
+
+        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        mPopupMaxWidth = metrics.widthPixels / 2;
+
+        if (anchorView != null) {
+            mAnchorView = new WeakReference<View>(anchorView);
+        }
+    }
+
+    public void show() {
+        mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle);
+        mPopup.setOnItemClickListener(this);
+        mPopup.setOnDismissListener(mDismissListener);
+
+        final MenuAdapter adapter = mOverflowOnly ?
+                mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) :
+                mMenu.getMenuAdapter(MenuBuilder.TYPE_POPUP);
+        mPopup.setAdapter(adapter);
+        mPopup.setModal(true);
+
+        if (mAnchorView != null) {
+            mPopup.setAnchorView(mAnchorView.get());
+        } else if (mMenu instanceof SubMenuBuilder) {
+            SubMenuBuilder subMenu = (SubMenuBuilder) mMenu;
+            final MenuItemImpl itemImpl = (MenuItemImpl) subMenu.getItem();
+            mPopup.setAnchorView(itemImpl.getItemView(MenuBuilder.TYPE_ACTION_BUTTON, null));
+        } else {
+            throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
+        }
+
+        mPopup.setContentWidth(Math.min(measureContentWidth(adapter), mPopupMaxWidth));
+        mPopup.show();
+        mPopup.getListView().setOnKeyListener(this);
+    }
+
+    public void dismiss() {
+        if (isShowing()) {
+            mPopup.dismiss();
+        }
+    }
+
+    public boolean isShowing() {
+        return mPopup != null && mPopup.isShowing();
+    }
+
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        MenuItem item = null;
+        if (mOverflowOnly) {
+            item = mMenu.getOverflowItem(position);
+        } else {
+            item = mMenu.getItem(position);
+        }
+        mMenu.performItemAction(item, 0);
+        mPopup.dismiss();
+    }
+
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) {
+            dismiss();
+            return true;
+        }
+        return false;
+    }
+
+    private int measureContentWidth(MenuAdapter adapter) {
+        // Menus don't tend to be long, so this is more sane than it looks.
+        int width = 0;
+        View itemView = null;
+        final int widthMeasureSpec =
+            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        final int heightMeasureSpec =
+            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+        final int count = adapter.getCount();
+        for (int i = 0; i < count; i++) {
+            itemView = adapter.getView(i, itemView, null);
+            itemView.measure(widthMeasureSpec, heightMeasureSpec);
+            width = Math.max(width, itemView.getMeasuredWidth());
+        }
+        return width;
+    }
+}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
new file mode 100644
index 0000000..e93c414
--- /dev/null
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget;
+
+import com.android.internal.R;
+import com.android.internal.view.menu.ActionMenuView;
+import com.android.internal.view.menu.MenuBuilder;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.ActionMode;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * @hide
+ */
+public class ActionBarContextView extends ViewGroup {
+    private int mContentHeight;
+    
+    private CharSequence mTitle;
+    private CharSequence mSubtitle;
+
+    private View mClose;
+    private View mCustomView;
+    private LinearLayout mTitleLayout;
+    private TextView mTitleView;
+    private TextView mSubtitleView;
+    private int mTitleStyleRes;
+    private int mSubtitleStyleRes;
+    private ActionMenuView mMenuView;
+    
+    public ActionBarContextView(Context context) {
+        this(context, null);
+    }
+    
+    public ActionBarContextView(Context context, AttributeSet attrs) {
+        this(context, attrs, com.android.internal.R.attr.actionModeStyle);
+    }
+    
+    public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionMode, defStyle, 0);
+        setBackgroundDrawable(a.getDrawable(
+                com.android.internal.R.styleable.ActionMode_background));
+        mTitleStyleRes = a.getResourceId(
+                com.android.internal.R.styleable.ActionMode_titleTextStyle, 0);
+        mSubtitleStyleRes = a.getResourceId(
+                com.android.internal.R.styleable.ActionMode_subtitleTextStyle, 0);
+
+        mContentHeight = a.getLayoutDimension(
+                com.android.internal.R.styleable.ActionMode_height, 0);
+        a.recycle();
+    }
+    
+    @Override
+    public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
+        // No starting an action mode for an existing action mode UI child! (Where would it go?)
+        return null;
+    }
+
+    public void setHeight(int height) {
+        mContentHeight = height;
+    }
+
+    public void setCustomView(View view) {
+        if (mCustomView != null) {
+            removeView(mCustomView);
+        }
+        mCustomView = view;
+        if (mTitleLayout != null) {
+            removeView(mTitleLayout);
+            mTitleLayout = null;
+        }
+        if (view != null) {
+            addView(view);
+        }
+        requestLayout();
+    }
+
+    public void setTitle(CharSequence title) {
+        mTitle = title;
+        initTitle();
+    }
+
+    public void setSubtitle(CharSequence subtitle) {
+        mSubtitle = subtitle;
+        initTitle();
+    }
+
+    public CharSequence getTitle() {
+        return mTitle;
+    }
+
+    public CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    private void initTitle() {
+        if (mTitleLayout == null) {
+            LayoutInflater inflater = LayoutInflater.from(getContext());
+            inflater.inflate(R.layout.action_bar_title_item, this);
+            mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1);
+            mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
+            mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
+            if (mTitle != null) {
+                mTitleView.setText(mTitle);
+                if (mTitleStyleRes != 0) {
+                    mTitleView.setTextAppearance(mContext, mTitleStyleRes);
+                }
+            }
+            if (mSubtitle != null) {
+                mSubtitleView.setText(mSubtitle);
+                if (mSubtitleStyleRes != 0) {
+                    mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
+                }
+                mSubtitleView.setVisibility(VISIBLE);
+            }
+        } else {
+            mTitleView.setText(mTitle);
+            mSubtitleView.setText(mSubtitle);
+            mSubtitleView.setVisibility(mSubtitle != null ? VISIBLE : GONE);
+            if (mTitleLayout.getParent() == null) {
+                addView(mTitleLayout);
+            }
+        }
+    }
+
+    public void initForMode(final ActionMode mode) {
+        if (mClose == null) {
+            LayoutInflater inflater = LayoutInflater.from(mContext);
+            inflater.inflate(R.layout.action_mode_close_item, this);
+            mClose = getChildAt(getChildCount() - 1);
+        } else {
+            addView(mClose);
+        }
+
+        View closeButton = mClose.findViewById(R.id.action_mode_close_button);
+        closeButton.setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                mode.finish();
+            }
+        });
+
+        final MenuBuilder menu = (MenuBuilder) mode.getMenu();
+        mMenuView = (ActionMenuView) menu.getMenuView(MenuBuilder.TYPE_ACTION_BUTTON, this);
+        mMenuView.setOverflowReserved(true);
+        mMenuView.updateChildren(false);
+        addView(mMenuView);
+    }
+
+    public void closeMode() {
+        removeAllViews();
+        mCustomView = null;
+        mMenuView = null;
+    }
+
+    public boolean showOverflowMenu() {
+        if (mMenuView != null) {
+            return mMenuView.showOverflowMenu();
+        }
+        return false;
+    }
+
+    public boolean hideOverflowMenu() {
+        if (mMenuView != null) {
+            return mMenuView.hideOverflowMenu();
+        }
+        return false;
+    }
+
+    public boolean isOverflowMenuShowing() {
+        if (mMenuView != null) {
+            return mMenuView.isOverflowMenuShowing();
+        }
+        return false;
+    }
+
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        // Used by custom views if they don't supply layout params. Everything else
+        // added to an ActionBarContextView should have them already.
+        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        if (widthMode != MeasureSpec.EXACTLY) {
+            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
+                    "with android:layout_width=\"match_parent\" (or fill_parent)");
+        }
+
+        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        if (heightMode == MeasureSpec.UNSPECIFIED) {
+            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
+                    "with android:layout_height=\"wrap_content\"");
+        }
+        
+        final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
+
+        int maxHeight = mContentHeight > 0 ?
+                mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
+
+        final int verticalPadding = getPaddingTop() + getPaddingBottom();
+        int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
+        final int height = maxHeight - verticalPadding;
+        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+        
+        if (mClose != null) {
+            availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
+        }
+
+        if (mTitleLayout != null && mCustomView == null) {
+            availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
+        }
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            if (child == mClose || child == mTitleLayout || child == mCustomView) {
+                continue;
+            }
+            
+            availableWidth = measureChildView(child, availableWidth, childSpecHeight, 0);
+        }
+
+        if (mCustomView != null) {
+            LayoutParams lp = mCustomView.getLayoutParams();
+            final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
+                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+            final int customWidth = lp.width >= 0 ?
+                    Math.min(lp.width, availableWidth) : availableWidth;
+            final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
+                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+            final int customHeight = lp.height >= 0 ?
+                    Math.min(lp.height, height) : height;
+            mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode),
+                    MeasureSpec.makeMeasureSpec(customHeight, customHeightMode));
+        }
+
+        if (mContentHeight <= 0) {
+            int measuredHeight = 0;
+            final int count = getChildCount();
+            for (int i = 0; i < count; i++) {
+                View v = getChildAt(i);
+                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
+                if (paddedViewHeight > measuredHeight) {
+                    measuredHeight = paddedViewHeight;
+                }
+            }
+            setMeasuredDimension(contentWidth, measuredHeight);
+        } else {
+            setMeasuredDimension(contentWidth, maxHeight);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int x = getPaddingLeft();
+        final int y = getPaddingTop();
+        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
+        
+        if (mClose != null && mClose.getVisibility() != GONE) {
+            x += positionChild(mClose, x, y, contentHeight);
+        }
+        
+        if (mTitleLayout != null && mCustomView == null) {
+            x += positionChild(mTitleLayout, x, y, contentHeight);
+        }
+        
+        if (mCustomView != null) {
+            x += positionChild(mCustomView, x, y, contentHeight);
+        }
+        
+        x = r - l - getPaddingRight();
+
+        if (mMenuView != null) {
+            x -= positionChildInverse(mMenuView, x, y, contentHeight);
+        }
+    }
+
+    private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) {
+        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+                childSpecHeight);
+
+        availableWidth -= child.getMeasuredWidth();
+        availableWidth -= spacing;
+
+        return availableWidth;
+    }
+    
+    private int positionChild(View child, int x, int y, int contentHeight) {
+        int childWidth = child.getMeasuredWidth();
+        int childHeight = child.getMeasuredHeight();
+        int childTop = y + (contentHeight - childHeight) / 2;
+
+        child.layout(x, childTop, x + childWidth, childTop + childHeight);
+
+        return childWidth;
+    }
+    
+    private int positionChildInverse(View child, int x, int y, int contentHeight) {
+        int childWidth = child.getMeasuredWidth();
+        int childHeight = child.getMeasuredHeight();
+        int childTop = y + (contentHeight - childHeight) / 2;
+
+        child.layout(x - childWidth, childTop, x, childTop + childHeight);
+
+        return childWidth;
+    }
+}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
new file mode 100644
index 0000000..be6a57a
--- /dev/null
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -0,0 +1,914 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import com.android.internal.R;
+import com.android.internal.view.menu.ActionMenuItem;
+import com.android.internal.view.menu.ActionMenuView;
+import com.android.internal.view.menu.MenuBuilder;
+
+import android.app.ActionBar;
+import android.app.ActionBar.NavigationCallback;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils.TruncateAt;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.ActionMode;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.AdapterView;
+import android.widget.HorizontalScrollView;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+/**
+ * @hide
+ */
+public class ActionBarView extends ViewGroup {
+    private static final String TAG = "ActionBarView";
+
+    /**
+     * Display options applied by default
+     */
+    public static final int DISPLAY_DEFAULT = 0;
+
+    /**
+     * Display options that require re-layout as opposed to a simple invalidate
+     */
+    private static final int DISPLAY_RELAYOUT_MASK =
+            ActionBar.DISPLAY_SHOW_HOME |
+            ActionBar.DISPLAY_USE_LOGO |
+            ActionBar.DISPLAY_HOME_AS_UP |
+            ActionBar.DISPLAY_SHOW_CUSTOM |
+            ActionBar.DISPLAY_SHOW_TITLE;
+
+    private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.LEFT | Gravity.CENTER_VERTICAL;
+    
+    private final int mContentHeight;
+
+    private int mNavigationMode;
+    private int mDisplayOptions = ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP;
+    private CharSequence mTitle;
+    private CharSequence mSubtitle;
+    private Drawable mIcon;
+    private Drawable mLogo;
+    private Drawable mDivider;
+    private Drawable mHomeAsUpIndicator;
+
+    private LinearLayout mHomeLayout;
+    private ImageView mHomeAsUpView;
+    private ImageView mIconView;
+    private ImageView mLogoView;
+    private LinearLayout mTitleLayout;
+    private TextView mTitleView;
+    private TextView mSubtitleView;
+    private Spinner mSpinner;
+    private HorizontalScrollView mTabScrollView;
+    private LinearLayout mTabLayout;
+    private View mCustomNavView;
+    
+    private int mTitleStyleRes;
+    private int mSubtitleStyleRes;
+
+    private boolean mShowMenu;
+    private boolean mUserTitle;
+
+    private MenuBuilder mOptionsMenu;
+    private ActionMenuView mMenuView;
+    
+    private ActionBarContextView mContextView;
+
+    private ActionMenuItem mLogoNavItem;
+
+    private SpinnerAdapter mSpinnerAdapter;
+    private NavigationCallback mCallback;
+
+    private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
+            new AdapterView.OnItemSelectedListener() {
+        public void onItemSelected(AdapterView parent, View view, int position, long id) {
+            if (mCallback != null) {
+                mCallback.onNavigationItemSelected(position, id);
+            }
+        }
+        public void onNothingSelected(AdapterView parent) {
+            // Do nothing
+        }
+    };
+
+    private OnClickListener mTabClickListener = null;
+
+    public ActionBarView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar);
+
+        ApplicationInfo appInfo = context.getApplicationInfo();
+        PackageManager pm = context.getPackageManager();
+        mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode,
+                ActionBar.NAVIGATION_MODE_STANDARD);
+        mTitle = a.getText(R.styleable.ActionBar_title);
+        mSubtitle = a.getText(R.styleable.ActionBar_subtitle);
+        
+        mLogo = a.getDrawable(R.styleable.ActionBar_logo);
+        if (mLogo == null) {
+            if (context instanceof Activity) {
+                try {
+                    mLogo = pm.getActivityLogo(((Activity) context).getComponentName());
+                } catch (NameNotFoundException e) {
+                    Log.e(TAG, "Activity component name not found!", e);
+                }
+            }
+            if (mLogo == null) {
+                mLogo = appInfo.loadLogo(pm);
+            }
+        }
+
+        mIcon = a.getDrawable(R.styleable.ActionBar_icon);
+        if (mIcon == null) {
+            if (context instanceof Activity) {
+                try {
+                    mIcon = pm.getActivityIcon(((Activity) context).getComponentName());
+                } catch (NameNotFoundException e) {
+                    Log.e(TAG, "Activity component name not found!", e);
+                }
+            }
+            if (mIcon == null) {
+                mIcon = appInfo.loadIcon(pm);
+            }
+        }
+
+        mHomeLayout = new LinearLayout(context, null,
+                com.android.internal.R.attr.actionButtonStyle);
+        mHomeLayout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+                LayoutParams.MATCH_PARENT));
+
+        mHomeAsUpIndicator = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator);
+
+        mHomeAsUpView = new ImageView(context);
+        mHomeAsUpView.setImageDrawable(mHomeAsUpIndicator);
+        mHomeAsUpView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+                LayoutParams.MATCH_PARENT));
+        mHomeLayout.addView(mHomeAsUpView);
+        
+        Drawable background = a.getDrawable(R.styleable.ActionBar_background);
+        if (background != null) {
+            setBackgroundDrawable(background);
+        }
+        
+        mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
+        mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0);
+
+        setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT));
+
+        final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
+        if (customNavId != 0) {
+            LayoutInflater inflater = LayoutInflater.from(context);
+            mCustomNavView = (View) inflater.inflate(customNavId, null);
+            mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD;
+            setDisplayOptions(mDisplayOptions | ActionBar.DISPLAY_SHOW_CUSTOM);
+        }
+
+        mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
+        
+        mDivider = a.getDrawable(R.styleable.ActionBar_divider);
+
+        a.recycle();
+        
+        mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle);
+        mHomeLayout.setOnClickListener(new OnClickListener() {
+          public void onClick(View v) {
+            Context context = getContext();
+            if (context instanceof Activity) {
+              Activity activity = (Activity) context;
+              activity.onOptionsItemSelected(mLogoNavItem);
+            }
+          }
+        });
+        mHomeLayout.setClickable(true);
+        mHomeLayout.setFocusable(true);
+    }
+
+    @Override
+    public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
+        // No starting an action mode for an action bar child! (Where would it go?)
+        return null;
+    }
+
+    public void setCallback(NavigationCallback callback) {
+        mCallback = callback;
+    }
+
+    public void setMenu(Menu menu) {
+        MenuBuilder builder = (MenuBuilder) menu;
+        mOptionsMenu = builder;
+        if (mMenuView != null) {
+            removeView(mMenuView);
+        }
+        final ActionMenuView menuView = (ActionMenuView) builder.getMenuView(
+                MenuBuilder.TYPE_ACTION_BUTTON, null);
+        final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                LayoutParams.MATCH_PARENT);
+        menuView.setLayoutParams(layoutParams);
+        addView(menuView);
+        mMenuView = menuView;
+    }
+
+    public boolean showOverflowMenu() {
+        if (mMenuView != null) {
+            return mMenuView.showOverflowMenu();
+        }
+        return false;
+    }
+
+    public void postShowOverflowMenu() {
+        post(new Runnable() {
+            public void run() {
+                showOverflowMenu();
+            }
+        });
+    }
+
+    public boolean hideOverflowMenu() {
+        if (mMenuView != null) {
+            return mMenuView.hideOverflowMenu();
+        }
+        return false;
+    }
+
+    public boolean isOverflowMenuShowing() {
+        if (mMenuView != null) {
+            return mMenuView.isOverflowMenuShowing();
+        }
+        return false;
+    }
+
+    public boolean isOverflowReserved() {
+        return mMenuView != null && mMenuView.isOverflowReserved();
+    }
+
+    public void setCustomNavigationView(View view) {
+        final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0;
+        if (mCustomNavView != null && showCustom) {
+            removeView(mCustomNavView);
+        }
+        mCustomNavView = view;
+        if (mCustomNavView != null && showCustom) {
+            addView(mCustomNavView);
+        }
+    }
+
+    public CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Set the action bar title. This will always replace or override window titles.
+     * @param title Title to set
+     *
+     * @see #setWindowTitle(CharSequence)
+     */
+    public void setTitle(CharSequence title) {
+        mUserTitle = true;
+        setTitleImpl(title);
+    }
+
+    /**
+     * Set the window title. A window title will always be replaced or overridden by a user title.
+     * @param title Title to set
+     *
+     * @see #setTitle(CharSequence)
+     */
+    public void setWindowTitle(CharSequence title) {
+        if (!mUserTitle) {
+            setTitleImpl(title);
+        }
+    }
+
+    private void setTitleImpl(CharSequence title) {
+        mTitle = title;
+        if (mTitleView != null) {
+            mTitleView.setText(title);
+        }
+        if (mLogoNavItem != null) {
+            mLogoNavItem.setTitle(title);
+        }
+    }
+
+    public CharSequence getSubtitle() {
+        return mSubtitle;
+    }
+
+    public void setSubtitle(CharSequence subtitle) {
+        mSubtitle = subtitle;
+        if (mSubtitleView != null) {
+            mSubtitleView.setText(subtitle);
+        }
+    }
+
+    public void setDisplayOptions(int options) {
+        // TODO Remove this once DISPLAY_HIDE_HOME is removed
+        if ((options & ActionBar.DISPLAY_HIDE_HOME) != 0) {
+            options &= ~(ActionBar.DISPLAY_HIDE_HOME | ActionBar.DISPLAY_SHOW_HOME);
+        }
+        // End TODO
+
+        final int flagsChanged = options ^ mDisplayOptions;
+        mDisplayOptions = options;
+        if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) {
+            final int vis = (options & ActionBar.DISPLAY_SHOW_HOME) != 0 ? VISIBLE : GONE;
+            mHomeLayout.setVisibility(vis);
+
+            if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
+                mHomeAsUpView.setVisibility((options & ActionBar.DISPLAY_HOME_AS_UP) != 0
+                        ? VISIBLE : GONE);
+            }
+
+            if (mLogoView != null && (flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) {
+                final boolean logoVis = (options & ActionBar.DISPLAY_USE_LOGO) != 0;
+                mLogoView.setVisibility(logoVis ? VISIBLE : GONE);
+                mIconView.setVisibility(logoVis ? GONE : VISIBLE);
+            }
+
+            if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+                if ((options & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+                    initTitle();
+                } else {
+                    removeView(mTitleLayout);
+                }
+            }
+
+            if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) {
+                if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+                    addView(mCustomNavView);
+                } else {
+                    removeView(mCustomNavView);
+                }
+            }
+            
+            requestLayout();
+        } else {
+            invalidate();
+        }
+    }
+
+    public void setNavigationMode(int mode) {
+        final int oldMode = mNavigationMode;
+        if (mode != oldMode) {
+            switch (oldMode) {
+            case ActionBar.NAVIGATION_MODE_LIST:
+                if (mSpinner != null) {
+                    removeView(mSpinner);
+                }
+                break;
+            case ActionBar.NAVIGATION_MODE_TABS:
+                if (mTabLayout != null) {
+                    removeView(mTabScrollView);
+                }
+            }
+            
+            switch (mode) {
+            case ActionBar.NAVIGATION_MODE_LIST:
+                mSpinner = new Spinner(mContext, null,
+                        com.android.internal.R.attr.actionDropDownStyle);
+                mSpinner.setAdapter(mSpinnerAdapter);
+                mSpinner.setOnItemSelectedListener(mNavItemSelectedListener);
+                addView(mSpinner);
+                break;
+            case ActionBar.NAVIGATION_MODE_TABS:
+                ensureTabsExist();
+                addView(mTabScrollView);
+                break;
+            }
+            mNavigationMode = mode;
+            requestLayout();
+        }
+    }
+    
+    private void ensureTabsExist() {
+        if (mTabScrollView == null) {
+            mTabScrollView = new HorizontalScrollView(getContext());
+            mTabLayout = new LinearLayout(getContext(), null,
+                    com.android.internal.R.attr.actionBarTabBarStyle);
+            mTabScrollView.addView(mTabLayout);
+        }
+    }
+
+    public void setDropdownAdapter(SpinnerAdapter adapter) {
+        mSpinnerAdapter = adapter;
+        if (mSpinner != null) {
+            mSpinner.setAdapter(adapter);
+        }
+    }
+
+    public SpinnerAdapter getDropdownAdapter() {
+        return mSpinnerAdapter;
+    }
+
+    public void setDropdownSelectedPosition(int position) {
+        mSpinner.setSelection(position);
+    }
+
+    public int getDropdownSelectedPosition() {
+        return mSpinner.getSelectedItemPosition();
+    }
+
+    public View getCustomNavigationView() {
+        return mCustomNavView;
+    }
+    
+    public int getNavigationMode() {
+        return mNavigationMode;
+    }
+    
+    public int getDisplayOptions() {
+        return mDisplayOptions;
+    }
+
+    private TabView createTabView(ActionBar.Tab tab) {
+        final TabView tabView = new TabView(getContext(), tab);
+        tabView.setFocusable(true);
+
+        if (mTabClickListener == null) {
+            mTabClickListener = new TabClickListener();
+        }
+        tabView.setOnClickListener(mTabClickListener);
+        return tabView;
+    }
+
+    public void addTab(ActionBar.Tab tab, boolean setSelected) {
+        ensureTabsExist();
+        View tabView = createTabView(tab);
+        mTabLayout.addView(tabView);
+        if (setSelected) {
+            tabView.setSelected(true);
+        }
+    }
+
+    public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
+        ensureTabsExist();
+        final TabView tabView = createTabView(tab);
+        mTabLayout.addView(tabView, position);
+        if (setSelected) {
+            tabView.setSelected(true);
+        }
+    }
+
+    public void removeTabAt(int position) {
+        if (mTabLayout != null) {
+            mTabLayout.removeViewAt(position);
+        }
+    }
+
+    public void removeAllTabs() {
+        if (mTabLayout != null) {
+            mTabLayout.removeAllViews();
+        }
+    }
+
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        // Used by custom nav views if they don't supply layout params. Everything else
+        // added to an ActionBarView should have them already.
+        return new ActionBar.LayoutParams(DEFAULT_CUSTOM_GRAVITY);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        final Context context = getContext();
+
+        if (mLogo != null) {
+            mLogoView = new ImageView(context);
+            mLogoView.setScaleType(ImageView.ScaleType.CENTER);
+            mLogoView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+                    LayoutParams.MATCH_PARENT));
+            mLogoView.setImageDrawable(mLogo);
+            mLogoView.setVisibility((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0
+                    ? VISIBLE : GONE);
+            mHomeLayout.addView(mLogoView);
+        }
+
+        if (mIcon != null) {
+            mIconView = new ImageView(context, null,
+                    com.android.internal.R.attr.actionButtonStyle);
+            mIconView.setScaleType(ImageView.ScaleType.CENTER);
+            mIconView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+                    LayoutParams.MATCH_PARENT));
+            mIconView.setImageDrawable(mIcon);
+            mIconView.setVisibility(
+                    (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null
+                    ? VISIBLE : GONE);
+            mHomeLayout.addView(mIconView);
+        }
+
+        addView(mHomeLayout);
+
+        switch (mNavigationMode) {
+        case ActionBar.NAVIGATION_MODE_STANDARD:
+            if (mLogoView == null) {
+                initTitle();
+            }
+            break;
+            
+        case ActionBar.NAVIGATION_MODE_LIST:
+            throw new UnsupportedOperationException(
+                    "Inflating list navigation isn't supported yet!");
+            
+        case ActionBar.NAVIGATION_MODE_TABS:
+            throw new UnsupportedOperationException(
+                    "Inflating tab navigation isn't supported yet!");
+        }
+
+        if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+            final ViewParent parent = mCustomNavView.getParent();
+            if (parent != this) {
+                if (parent instanceof ViewGroup) {
+                    ((ViewGroup) parent).removeView(mCustomNavView);
+                }
+                addView(mCustomNavView);
+            }
+        }
+    }
+    
+    private void initTitle() {
+        LayoutInflater inflater = LayoutInflater.from(getContext());
+        mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null);
+        mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
+        mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
+
+        if (mTitleStyleRes != 0) {
+            mTitleView.setTextAppearance(mContext, mTitleStyleRes);
+        }
+        if (mTitle != null) {
+            mTitleView.setText(mTitle);
+        }
+
+        if (mSubtitleStyleRes != 0) {
+            mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
+        }
+        if (mSubtitle != null) {
+            mSubtitleView.setText(mSubtitle);
+            mSubtitleView.setVisibility(VISIBLE);
+        }
+
+        addView(mTitleLayout);
+    }
+
+    public void setTabSelected(int position) {
+        ensureTabsExist();
+        final int tabCount = mTabLayout.getChildCount();
+        for (int i = 0; i < tabCount; i++) {
+            final View child = mTabLayout.getChildAt(i);
+            child.setSelected(i == position);
+        }
+    }
+
+    public void setContextView(ActionBarContextView view) {
+        mContextView = view;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        if (widthMode != MeasureSpec.EXACTLY) {
+            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
+                    "with android:layout_width=\"match_parent\" (or fill_parent)");
+        }
+        
+        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        if (heightMode != MeasureSpec.AT_MOST) {
+            throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
+                    "with android:layout_height=\"wrap_content\"");
+        }
+
+        int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
+
+        int maxHeight = mContentHeight > 0 ?
+                mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
+        
+        final int verticalPadding = getPaddingTop() + getPaddingBottom();
+        final int paddingLeft = getPaddingLeft();
+        final int paddingRight = getPaddingRight();
+        final int height = maxHeight - verticalPadding;
+        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+
+        int availableWidth = contentWidth - paddingLeft - paddingRight;
+        int leftOfCenter = availableWidth / 2;
+        int rightOfCenter = leftOfCenter;
+
+        if (mHomeLayout.getVisibility() != GONE) {
+            availableWidth = measureChildView(mHomeLayout, availableWidth, childSpecHeight, 0);
+            leftOfCenter -= mHomeLayout.getMeasuredWidth();
+        }
+        
+        if (mMenuView != null) {
+            availableWidth = measureChildView(mMenuView, availableWidth,
+                    childSpecHeight, 0);
+            rightOfCenter -= mMenuView.getMeasuredWidth();
+        }
+
+        if (mTitleLayout != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+            availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
+            leftOfCenter -= mTitleLayout.getMeasuredWidth();
+        }
+
+        switch (mNavigationMode) {
+        case ActionBar.NAVIGATION_MODE_LIST:
+            if (mSpinner != null) {
+                mSpinner.measure(
+                        MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
+                final int spinnerWidth = mSpinner.getMeasuredWidth();
+                availableWidth -= spinnerWidth;
+                leftOfCenter -= spinnerWidth;
+            }
+            break;
+        case ActionBar.NAVIGATION_MODE_TABS:
+            if (mTabScrollView != null) {
+                mTabScrollView.measure(
+                        MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+                final int tabWidth = mTabScrollView.getMeasuredWidth();
+                availableWidth -= tabWidth;
+                leftOfCenter -= tabWidth;
+            }
+            break;
+        }
+
+        if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) {
+            final LayoutParams lp = generateLayoutParams(mCustomNavView.getLayoutParams());
+            final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
+                    (ActionBar.LayoutParams) lp : null;
+
+            int horizontalMargin = 0;
+            int verticalMargin = 0;
+            if (ablp != null) {
+                horizontalMargin = ablp.leftMargin + ablp.rightMargin;
+                verticalMargin = ablp.topMargin + ablp.bottomMargin;
+            }
+
+            // If the action bar is wrapping to its content height, don't allow a custom
+            // view to MATCH_PARENT.
+            int customNavHeightMode;
+            if (mContentHeight <= 0) {
+                customNavHeightMode = MeasureSpec.AT_MOST;
+            } else {
+                customNavHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
+                        MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+            }
+            final int customNavHeight = Math.max(0,
+                    (lp.height >= 0 ? Math.min(lp.height, height) : height) - verticalMargin);
+
+            final int customNavWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
+                    MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+            int customNavWidth = Math.max(0,
+                    (lp.width >= 0 ? Math.min(lp.width, availableWidth) : availableWidth)
+                    - horizontalMargin);
+            final int hgrav = (ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY) &
+                    Gravity.HORIZONTAL_GRAVITY_MASK;
+
+            // Centering a custom view is treated specially; we try to center within the whole
+            // action bar rather than in the available space.
+            if (hgrav == Gravity.CENTER_HORIZONTAL && lp.width == LayoutParams.MATCH_PARENT) {
+                customNavWidth = Math.min(leftOfCenter, rightOfCenter) * 2;
+            }
+
+            mCustomNavView.measure(
+                    MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode),
+                    MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode));
+        }
+
+        if (mContentHeight <= 0) {
+            int measuredHeight = 0;
+            final int count = getChildCount();
+            for (int i = 0; i < count; i++) {
+                View v = getChildAt(i);
+                int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
+                if (paddedViewHeight > measuredHeight) {
+                    measuredHeight = paddedViewHeight;
+                }
+            }
+            setMeasuredDimension(contentWidth, measuredHeight);
+        } else {
+            setMeasuredDimension(contentWidth, maxHeight);
+        }
+
+        if (mContextView != null) {
+            mContextView.setHeight(getMeasuredHeight());
+        }
+    }
+
+    private int measureChildView(View child, int availableWidth, int childSpecHeight, int spacing) {
+        child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
+                childSpecHeight);
+
+        availableWidth -= child.getMeasuredWidth();
+        availableWidth -= spacing;
+
+        return availableWidth;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int x = getPaddingLeft();
+        final int y = getPaddingTop();
+        final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
+
+        if (mHomeLayout.getVisibility() != GONE) {
+            x += positionChild(mHomeLayout, x, y, contentHeight);
+        }
+        
+        if (mTitleLayout != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
+            x += positionChild(mTitleLayout, x, y, contentHeight);
+        }
+
+        switch (mNavigationMode) {
+        case ActionBar.NAVIGATION_MODE_STANDARD:
+            break;
+        case ActionBar.NAVIGATION_MODE_LIST:
+            if (mSpinner != null) {
+                x += positionChild(mSpinner, x, y, contentHeight);
+            }
+            break;
+        case ActionBar.NAVIGATION_MODE_TABS:
+            if (mTabScrollView != null) {
+                x += positionChild(mTabScrollView, x, y, contentHeight);
+            }
+        }
+
+        int menuLeft = r - l - getPaddingRight();
+        if (mMenuView != null) {
+            positionChildInverse(mMenuView, menuLeft, y, contentHeight);
+            menuLeft -= mMenuView.getMeasuredWidth();
+        }
+
+        if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
+            LayoutParams lp = mCustomNavView.getLayoutParams();
+            final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ?
+                    (ActionBar.LayoutParams) lp : null;
+
+            final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY;
+            final int navWidth = mCustomNavView.getMeasuredWidth();
+
+            int topMargin = 0;
+            int bottomMargin = 0;
+            if (ablp != null) {
+                x += ablp.leftMargin;
+                menuLeft -= ablp.rightMargin;
+                topMargin = ablp.topMargin;
+                bottomMargin = ablp.bottomMargin;
+            }
+
+            int hgravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+            // See if we actually have room to truly center; if not push against left or right.
+            if (hgravity == Gravity.CENTER_HORIZONTAL) {
+                final int centeredLeft = ((mRight - mLeft) - navWidth) / 2;
+                if (centeredLeft < x) {
+                    hgravity = Gravity.LEFT;
+                } else if (centeredLeft + navWidth > menuLeft) {
+                    hgravity = Gravity.RIGHT;
+                }
+            }
+
+            int xpos = 0;
+            switch (hgravity) {
+                case Gravity.CENTER_HORIZONTAL:
+                    xpos = ((mRight - mLeft) - navWidth) / 2;
+                    break;
+                case Gravity.LEFT:
+                    xpos = x;
+                    break;
+                case Gravity.RIGHT:
+                    xpos = menuLeft - navWidth;
+                    break;
+            }
+
+            int ypos = 0;
+            switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
+                case Gravity.CENTER_VERTICAL:
+                    final int paddedTop = mTop + getPaddingTop();
+                    final int paddedBottom = mBottom - getPaddingBottom();
+                    ypos = ((paddedBottom - paddedTop) - mCustomNavView.getMeasuredHeight()) / 2;
+                    break;
+                case Gravity.TOP:
+                    ypos = getPaddingTop() + topMargin;
+                    break;
+                case Gravity.BOTTOM:
+                    ypos = getHeight() - getPaddingBottom() - mCustomNavView.getMeasuredHeight()
+                            - bottomMargin;
+                    break;
+            }
+            x += positionChild(mCustomNavView, xpos, ypos, contentHeight);
+        }
+    }
+
+    private int positionChild(View child, int x, int y, int contentHeight) {
+        int childWidth = child.getMeasuredWidth();
+        int childHeight = child.getMeasuredHeight();
+        int childTop = y + (contentHeight - childHeight) / 2;
+
+        child.layout(x, childTop, x + childWidth, childTop + childHeight);
+
+        return childWidth;
+    }
+    
+    private int positionChildInverse(View child, int x, int y, int contentHeight) {
+        int childWidth = child.getMeasuredWidth();
+        int childHeight = child.getMeasuredHeight();
+        int childTop = y + (contentHeight - childHeight) / 2;
+
+        child.layout(x - childWidth, childTop, x, childTop + childHeight);
+
+        return childWidth;
+    }
+
+    private static class TabView extends LinearLayout {
+        private ActionBar.Tab mTab;
+
+        public TabView(Context context, ActionBar.Tab tab) {
+            super(context, null, com.android.internal.R.attr.actionBarTabStyle);
+            mTab = tab;
+
+            final View custom = tab.getCustomView();
+            if (custom != null) {
+                addView(custom);
+            } else {
+                // TODO Style tabs based on the theme
+
+                final Drawable icon = tab.getIcon();
+                final CharSequence text = tab.getText();
+
+                if (icon != null) {
+                    ImageView iconView = new ImageView(context);
+                    iconView.setImageDrawable(icon);
+                    LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                            LayoutParams.WRAP_CONTENT);
+                    lp.gravity = Gravity.CENTER_VERTICAL;
+                    iconView.setLayoutParams(lp);
+                    addView(iconView);
+                }
+
+                if (text != null) {
+                    TextView textView = new TextView(context, null,
+                            com.android.internal.R.attr.actionBarTabTextStyle);
+                    textView.setText(text);
+                    textView.setSingleLine();
+                    textView.setEllipsize(TruncateAt.END);
+                    LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                            LayoutParams.WRAP_CONTENT);
+                    lp.gravity = Gravity.CENTER_VERTICAL;
+                    textView.setLayoutParams(lp);
+                    addView(textView);
+                }
+            }
+
+            setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+                    LayoutParams.MATCH_PARENT, 1));
+        }
+
+        public ActionBar.Tab getTab() {
+            return mTab;
+        }
+    }
+
+    private class TabClickListener implements OnClickListener {
+        public void onClick(View view) {
+            TabView tabView = (TabView) view;
+            tabView.getTab().select();
+            final int tabCount = mTabLayout.getChildCount();
+            for (int i = 0; i < tabCount; i++) {
+                final View child = mTabLayout.getChildAt(i);
+                child.setSelected(child == view);
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/ContactHeaderWidget.java b/core/java/com/android/internal/widget/ContactHeaderWidget.java
deleted file mode 100644
index 4392113..0000000
--- a/core/java/com/android/internal/widget/ContactHeaderWidget.java
+++ /dev/null
@@ -1,668 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import com.android.internal.R;
-
-import android.Manifest;
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.os.SystemClock;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.PhoneLookup;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.StatusUpdates;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.QuickContactBadge;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-/**
- * Header used across system for displaying a title bar with contact info. You
- * can bind specific values on the header, or use helper methods like
- * {@link #bindFromContactId(long)} to populate asynchronously.
- * <p>
- * The parent must request the {@link Manifest.permission#READ_CONTACTS}
- * permission to access contact data.
- */
-public class ContactHeaderWidget extends FrameLayout implements View.OnClickListener {
-
-    private static final String TAG = "ContactHeaderWidget";
-
-    private TextView mDisplayNameView;
-    private View mAggregateBadge;
-    private TextView mPhoneticNameView;
-    private CheckBox mStarredView;
-    private QuickContactBadge mPhotoView;
-    private ImageView mPresenceView;
-    private TextView mStatusView;
-    private TextView mStatusAttributionView;
-    private int mNoPhotoResource;
-    private QueryHandler mQueryHandler;
-
-    protected Uri mContactUri;
-
-    protected String[] mExcludeMimes = null;
-
-    protected ContentResolver mContentResolver;
-
-    /**
-     * Interface for callbacks invoked when the user interacts with a header.
-     */
-    public interface ContactHeaderListener {
-        public void onPhotoClick(View view);
-        public void onDisplayNameClick(View view);
-    }
-
-    private ContactHeaderListener mListener;
-
-
-    private interface ContactQuery {
-        //Projection used for the summary info in the header.
-        String[] COLUMNS = new String[] {
-            Contacts._ID,
-            Contacts.LOOKUP_KEY,
-            Contacts.PHOTO_ID,
-            Contacts.DISPLAY_NAME,
-            Contacts.PHONETIC_NAME,
-            Contacts.STARRED,
-            Contacts.CONTACT_PRESENCE,
-            Contacts.CONTACT_STATUS,
-            Contacts.CONTACT_STATUS_TIMESTAMP,
-            Contacts.CONTACT_STATUS_RES_PACKAGE,
-            Contacts.CONTACT_STATUS_LABEL,
-        };
-        int _ID = 0;
-        int LOOKUP_KEY = 1;
-        int PHOTO_ID = 2;
-        int DISPLAY_NAME = 3;
-        int PHONETIC_NAME = 4;
-        //TODO: We need to figure out how we're going to get the phonetic name.
-        //static final int HEADER_PHONETIC_NAME_COLUMN_INDEX
-        int STARRED = 5;
-        int CONTACT_PRESENCE_STATUS = 6;
-        int CONTACT_STATUS = 7;
-        int CONTACT_STATUS_TIMESTAMP = 8;
-        int CONTACT_STATUS_RES_PACKAGE = 9;
-        int CONTACT_STATUS_LABEL = 10;
-    }
-
-    private interface PhotoQuery {
-        String[] COLUMNS = new String[] {
-            Photo.PHOTO
-        };
-
-        int PHOTO = 0;
-    }
-
-    //Projection used for looking up contact id from phone number
-    protected static final String[] PHONE_LOOKUP_PROJECTION = new String[] {
-        PhoneLookup._ID,
-        PhoneLookup.LOOKUP_KEY,
-    };
-    protected static final int PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0;
-    protected static final int PHONE_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX = 1;
-
-    //Projection used for looking up contact id from email address
-    protected static final String[] EMAIL_LOOKUP_PROJECTION = new String[] {
-        RawContacts.CONTACT_ID,
-        Contacts.LOOKUP_KEY,
-    };
-    protected static final int EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0;
-    protected static final int EMAIL_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX = 1;
-
-    protected static final String[] CONTACT_LOOKUP_PROJECTION = new String[] {
-        Contacts._ID,
-    };
-    protected static final int CONTACT_LOOKUP_ID_COLUMN_INDEX = 0;
-
-    private static final int TOKEN_CONTACT_INFO = 0;
-    private static final int TOKEN_PHONE_LOOKUP = 1;
-    private static final int TOKEN_EMAIL_LOOKUP = 2;
-    private static final int TOKEN_PHOTO_QUERY = 3;
-
-    public ContactHeaderWidget(Context context) {
-        this(context, null);
-    }
-
-    public ContactHeaderWidget(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public ContactHeaderWidget(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        mContentResolver = mContext.getContentResolver();
-
-        LayoutInflater inflater =
-            (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        inflater.inflate(R.layout.contact_header, this);
-
-        mDisplayNameView = (TextView) findViewById(R.id.name);
-        mAggregateBadge = findViewById(R.id.aggregate_badge);
-
-        mPhoneticNameView = (TextView) findViewById(R.id.phonetic_name);
-
-        mStarredView = (CheckBox)findViewById(R.id.star);
-        mStarredView.setOnClickListener(this);
-
-        mPhotoView = (QuickContactBadge) findViewById(R.id.photo);
-
-        mPresenceView = (ImageView) findViewById(R.id.presence);
-
-        mStatusView = (TextView)findViewById(R.id.status);
-        mStatusAttributionView = (TextView)findViewById(R.id.status_date);
-
-        // Set the photo with a random "no contact" image
-        long now = SystemClock.elapsedRealtime();
-        int num = (int) now & 0xf;
-        if (num < 9) {
-            // Leaning in from right, common
-            mNoPhotoResource = R.drawable.ic_contact_picture;
-        } else if (num < 14) {
-            // Leaning in from left uncommon
-            mNoPhotoResource = R.drawable.ic_contact_picture_2;
-        } else {
-            // Coming in from the top, rare
-            mNoPhotoResource = R.drawable.ic_contact_picture_3;
-        }
-
-        resetAsyncQueryHandler();
-    }
-
-    public void enableClickListeners() {
-        mDisplayNameView.setOnClickListener(this);
-        mPhotoView.setOnClickListener(this);
-    }
-
-    /**
-     * Set the given {@link ContactHeaderListener} to handle header events.
-     */
-    public void setContactHeaderListener(ContactHeaderListener listener) {
-        mListener = listener;
-    }
-
-    private void performPhotoClick() {
-        if (mListener != null) {
-            mListener.onPhotoClick(mPhotoView);
-        }
-    }
-
-    private void performDisplayNameClick() {
-        if (mListener != null) {
-            mListener.onDisplayNameClick(mDisplayNameView);
-        }
-    }
-
-    private class QueryHandler extends AsyncQueryHandler {
-
-        public QueryHandler(ContentResolver cr) {
-            super(cr);
-        }
-
-        @Override
-        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
-            try{
-                if (this != mQueryHandler) {
-                    Log.d(TAG, "onQueryComplete: discard result, the query handler is reset!");
-                    return;
-                }
-
-                switch (token) {
-                    case TOKEN_PHOTO_QUERY: {
-                        //Set the photo
-                        Bitmap photoBitmap = null;
-                        if (cursor != null && cursor.moveToFirst()
-                                && !cursor.isNull(PhotoQuery.PHOTO)) {
-                            byte[] photoData = cursor.getBlob(PhotoQuery.PHOTO);
-                            photoBitmap = BitmapFactory.decodeByteArray(photoData, 0,
-                                    photoData.length, null);
-                        }
-
-                        if (photoBitmap == null) {
-                            photoBitmap = loadPlaceholderPhoto(null);
-                        }
-                        mPhotoView.setImageBitmap(photoBitmap);
-                        if (cookie != null && cookie instanceof Uri) {
-                            mPhotoView.assignContactUri((Uri) cookie);
-                        }
-                        invalidate();
-                        break;
-                    }
-                    case TOKEN_CONTACT_INFO: {
-                        if (cursor != null && cursor.moveToFirst()) {
-                            bindContactInfo(cursor);
-                            Uri lookupUri = Contacts.getLookupUri(cursor.getLong(ContactQuery._ID),
-                                    cursor.getString(ContactQuery.LOOKUP_KEY));
-
-                            final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
-
-                            if (photoId == 0) {
-                                mPhotoView.setImageBitmap(loadPlaceholderPhoto(null));
-                                if (cookie != null && cookie instanceof Uri) {
-                                    mPhotoView.assignContactUri((Uri) cookie);
-                                }
-                                invalidate();
-                            } else {
-                                startPhotoQuery(photoId, lookupUri,
-                                        false /* don't reset query handler */);
-                            }
-                        } else {
-                            // shouldn't really happen
-                            setDisplayName(null, null);
-                            setSocialSnippet(null);
-                            setPhoto(loadPlaceholderPhoto(null));
-                        }
-                        break;
-                    }
-                    case TOKEN_PHONE_LOOKUP: {
-                        if (cursor != null && cursor.moveToFirst()) {
-                            long contactId = cursor.getLong(PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX);
-                            String lookupKey = cursor.getString(
-                                    PHONE_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX);
-                            bindFromContactUriInternal(Contacts.getLookupUri(contactId, lookupKey),
-                                    false /* don't reset query handler */);
-                        } else {
-                            String phoneNumber = (String) cookie;
-                            setDisplayName(phoneNumber, null);
-                            setSocialSnippet(null);
-                            setPhoto(loadPlaceholderPhoto(null));
-                            mPhotoView.assignContactFromPhone(phoneNumber, true);
-                        }
-                        break;
-                    }
-                    case TOKEN_EMAIL_LOOKUP: {
-                        if (cursor != null && cursor.moveToFirst()) {
-                            long contactId = cursor.getLong(EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX);
-                            String lookupKey = cursor.getString(
-                                    EMAIL_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX);
-                            bindFromContactUriInternal(Contacts.getLookupUri(contactId, lookupKey),
-                                    false /* don't reset query handler */);
-                        } else {
-                            String emailAddress = (String) cookie;
-                            setDisplayName(emailAddress, null);
-                            setSocialSnippet(null);
-                            setPhoto(loadPlaceholderPhoto(null));
-                            mPhotoView.assignContactFromEmail(emailAddress, true);
-                        }
-                        break;
-                    }
-                }
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public void setSelectedContactsAppTabIndex(int value) {
-        mPhotoView.setSelectedContactsAppTabIndex(value);
-    }
-
-    /**
-     * Turn on/off showing of the aggregate badge element.
-     */
-    public void showAggregateBadge(boolean showBagde) {
-        mAggregateBadge.setVisibility(showBagde ? View.VISIBLE : View.GONE);
-    }
-
-    /**
-     * Turn on/off showing of the star element.
-     */
-    public void showStar(boolean showStar) {
-        mStarredView.setVisibility(showStar ? View.VISIBLE : View.GONE);
-    }
-
-    /**
-     * Manually set the starred state of this header widget. This doesn't change
-     * the underlying {@link Contacts} value, only the UI state.
-     */
-    public void setStared(boolean starred) {
-        mStarredView.setChecked(starred);
-    }
-
-    /**
-     * Manually set the presence.
-     */
-    public void setPresence(int presence) {
-        mPresenceView.setImageResource(StatusUpdates.getPresenceIconResourceId(presence));
-    }
-
-    /**
-     * Manually set the contact uri
-     */
-    public void setContactUri(Uri uri) {
-        setContactUri(uri, true);
-    }
-
-    /**
-     * Manually set the contact uri
-     */
-    public void setContactUri(Uri uri, boolean sendToFastrack) {
-        mContactUri = uri;
-        if (sendToFastrack) {
-            mPhotoView.assignContactUri(uri);
-        }
-    }
-
-    /**
-     * Manually set the photo to display in the header. This doesn't change the
-     * underlying {@link Contacts}, only the UI state.
-     */
-    public void setPhoto(Bitmap bitmap) {
-        mPhotoView.setImageBitmap(bitmap);
-    }
-
-    /**
-     * Manually set the display name and phonetic name to show in the header.
-     * This doesn't change the underlying {@link Contacts}, only the UI state.
-     */
-    public void setDisplayName(CharSequence displayName, CharSequence phoneticName) {
-        mDisplayNameView.setText(displayName);
-        if (!TextUtils.isEmpty(phoneticName)) {
-            mPhoneticNameView.setText(phoneticName);
-            mPhoneticNameView.setVisibility(View.VISIBLE);
-        } else {
-            mPhoneticNameView.setVisibility(View.GONE);
-        }
-    }
-
-    /**
-     * Manually set the social snippet text to display in the header.
-     */
-    public void setSocialSnippet(CharSequence snippet) {
-        if (snippet == null) {
-            mStatusView.setVisibility(View.GONE);
-            mStatusAttributionView.setVisibility(View.GONE);
-        } else {
-            mStatusView.setText(snippet);
-            mStatusView.setVisibility(View.VISIBLE);
-        }
-    }
-
-    /**
-     * Set a list of specific MIME-types to exclude and not display. For
-     * example, this can be used to hide the {@link Contacts#CONTENT_ITEM_TYPE}
-     * profile icon.
-     */
-    public void setExcludeMimes(String[] excludeMimes) {
-        mExcludeMimes = excludeMimes;
-        mPhotoView.setExcludeMimes(excludeMimes);
-    }
-
-    /**
-     * Convenience method for binding all available data from an existing
-     * contact.
-     *
-     * @param contactLookupUri a {Contacts.CONTENT_LOOKUP_URI} style URI.
-     */
-    public void bindFromContactLookupUri(Uri contactLookupUri) {
-        bindFromContactUriInternal(contactLookupUri, true /* reset query handler */);
-    }
-
-    /**
-     * Convenience method for binding all available data from an existing
-     * contact.
-     *
-     * @param contactUri a {Contacts.CONTENT_URI} style URI.
-     * @param resetQueryHandler whether to use a new AsyncQueryHandler or not.
-     */
-    private void bindFromContactUriInternal(Uri contactUri, boolean resetQueryHandler) {
-        mContactUri = contactUri;
-        startContactQuery(contactUri, resetQueryHandler);
-    }
-
-    /**
-     * Convenience method for binding all available data from an existing
-     * contact.
-     *
-     * @param emailAddress The email address used to do a reverse lookup in
-     * the contacts database. If more than one contact contains this email
-     * address, one of them will be chosen to bind to.
-     */
-    public void bindFromEmail(String emailAddress) {
-        resetAsyncQueryHandler();
-
-        mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, emailAddress,
-                Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(emailAddress)),
-                EMAIL_LOOKUP_PROJECTION, null, null, null);
-    }
-
-    /**
-     * Convenience method for binding all available data from an existing
-     * contact.
-     *
-     * @param number The phone number used to do a reverse lookup in
-     * the contacts database. If more than one contact contains this phone
-     * number, one of them will be chosen to bind to.
-     */
-    public void bindFromPhoneNumber(String number) {
-        resetAsyncQueryHandler();
-
-        mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, number,
-                Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)),
-                PHONE_LOOKUP_PROJECTION, null, null, null);
-    }
-
-    /**
-     * startContactQuery
-     *
-     * internal method to query contact by Uri.
-     *
-     * @param contactUri the contact uri
-     * @param resetQueryHandler whether to use a new AsyncQueryHandler or not
-     */
-    private void startContactQuery(Uri contactUri, boolean resetQueryHandler) {
-        if (resetQueryHandler) {
-            resetAsyncQueryHandler();
-        }
-
-        mQueryHandler.startQuery(TOKEN_CONTACT_INFO, contactUri, contactUri, ContactQuery.COLUMNS,
-                null, null, null);
-    }
-
-    /**
-     * startPhotoQuery
-     *
-     * internal method to query contact photo by photo id and uri.
-     *
-     * @param photoId the photo id.
-     * @param lookupKey the lookup uri.
-     * @param resetQueryHandler whether to use a new AsyncQueryHandler or not.
-     */
-    protected void startPhotoQuery(long photoId, Uri lookupKey, boolean resetQueryHandler) {
-        if (resetQueryHandler) {
-            resetAsyncQueryHandler();
-        }
-
-        mQueryHandler.startQuery(TOKEN_PHOTO_QUERY, lookupKey,
-                ContentUris.withAppendedId(Data.CONTENT_URI, photoId), PhotoQuery.COLUMNS,
-                null, null, null);
-    }
-
-    /**
-     * Method to force this widget to forget everything it knows about the contact.
-     * We need to stop any existing async queries for phone, email, contact, and photos.
-     */
-    public void wipeClean() {
-        resetAsyncQueryHandler();
-
-        setDisplayName(null, null);
-        setPhoto(loadPlaceholderPhoto(null));
-        setSocialSnippet(null);
-        setPresence(0);
-        mContactUri = null;
-        mExcludeMimes = null;
-    }
-
-
-    private void resetAsyncQueryHandler() {
-        // the api AsyncQueryHandler.cancelOperation() doesn't really work. Since we really
-        // need the old async queries to be cancelled, let's do it the hard way.
-        mQueryHandler = new QueryHandler(mContentResolver);
-    }
-
-    /**
-     * Bind the contact details provided by the given {@link Cursor}.
-     */
-    protected void bindContactInfo(Cursor c) {
-        final String displayName = c.getString(ContactQuery.DISPLAY_NAME);
-        final String phoneticName = c.getString(ContactQuery.PHONETIC_NAME);
-        this.setDisplayName(displayName, phoneticName);
-
-        final boolean starred = c.getInt(ContactQuery.STARRED) != 0;
-        mStarredView.setChecked(starred);
-
-        //Set the presence status
-        if (!c.isNull(ContactQuery.CONTACT_PRESENCE_STATUS)) {
-            int presence = c.getInt(ContactQuery.CONTACT_PRESENCE_STATUS);
-            mPresenceView.setImageResource(StatusUpdates.getPresenceIconResourceId(presence));
-            mPresenceView.setVisibility(View.VISIBLE);
-        } else {
-            mPresenceView.setVisibility(View.GONE);
-        }
-
-        //Set the status update
-        String status = c.getString(ContactQuery.CONTACT_STATUS);
-        if (!TextUtils.isEmpty(status)) {
-            mStatusView.setText(status);
-            mStatusView.setVisibility(View.VISIBLE);
-
-            CharSequence timestamp = null;
-
-            if (!c.isNull(ContactQuery.CONTACT_STATUS_TIMESTAMP)) {
-                long date = c.getLong(ContactQuery.CONTACT_STATUS_TIMESTAMP);
-
-                // Set the date/time field by mixing relative and absolute
-                // times.
-                int flags = DateUtils.FORMAT_ABBREV_RELATIVE;
-
-                timestamp = DateUtils.getRelativeTimeSpanString(date,
-                        System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, flags);
-            }
-
-            String label = null;
-
-            if (!c.isNull(ContactQuery.CONTACT_STATUS_LABEL)) {
-                String resPackage = c.getString(ContactQuery.CONTACT_STATUS_RES_PACKAGE);
-                int labelResource = c.getInt(ContactQuery.CONTACT_STATUS_LABEL);
-                Resources resources;
-                if (TextUtils.isEmpty(resPackage)) {
-                    resources = getResources();
-                } else {
-                    PackageManager pm = getContext().getPackageManager();
-                    try {
-                        resources = pm.getResourcesForApplication(resPackage);
-                    } catch (NameNotFoundException e) {
-                        Log.w(TAG, "Contact status update resource package not found: "
-                                + resPackage);
-                        resources = null;
-                    }
-                }
-
-                if (resources != null) {
-                    try {
-                        label = resources.getString(labelResource);
-                    } catch (NotFoundException e) {
-                        Log.w(TAG, "Contact status update resource not found: " + resPackage + "@"
-                                + labelResource);
-                    }
-                }
-            }
-
-            CharSequence attribution;
-            if (timestamp != null && label != null) {
-                attribution = getContext().getString(
-                        R.string.contact_status_update_attribution_with_date,
-                        timestamp, label);
-            } else if (timestamp == null && label != null) {
-                attribution = getContext().getString(
-                        R.string.contact_status_update_attribution,
-                        label);
-            } else if (timestamp != null) {
-                attribution = timestamp;
-            } else {
-                attribution = null;
-            }
-            if (attribution != null) {
-                mStatusAttributionView.setText(attribution);
-                mStatusAttributionView.setVisibility(View.VISIBLE);
-            } else {
-                mStatusAttributionView.setVisibility(View.GONE);
-            }
-        } else {
-            mStatusView.setVisibility(View.GONE);
-            mStatusAttributionView.setVisibility(View.GONE);
-        }
-    }
-
-    public void onClick(View view) {
-        switch (view.getId()) {
-            case R.id.star: {
-                // Toggle "starred" state
-                // Make sure there is a contact
-                if (mContactUri != null) {
-                    final ContentValues values = new ContentValues(1);
-                    values.put(Contacts.STARRED, mStarredView.isChecked());
-                    mContentResolver.update(mContactUri, values, null, null);
-                }
-                break;
-            }
-            case R.id.photo: {
-                performPhotoClick();
-                break;
-            }
-            case R.id.name: {
-                performDisplayNameClick();
-                break;
-            }
-        }
-    }
-
-    private Bitmap loadPlaceholderPhoto(BitmapFactory.Options options) {
-        if (mNoPhotoResource == 0) {
-            return null;
-        }
-        return BitmapFactory.decodeResource(mContext.getResources(),
-                mNoPhotoResource, options);
-    }
-}
diff --git a/core/java/com/android/internal/widget/DigitalClock.java b/core/java/com/android/internal/widget/DigitalClock.java
index fa47ff6..23e2277 100644
--- a/core/java/com/android/internal/widget/DigitalClock.java
+++ b/core/java/com/android/internal/widget/DigitalClock.java
@@ -30,7 +30,7 @@
 import android.text.format.DateFormat;
 import android.util.AttributeSet;
 import android.view.View;
-import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import java.text.DateFormatSymbols;
@@ -39,7 +39,7 @@
 /**
  * Displays the time
  */
-public class DigitalClock extends LinearLayout {
+public class DigitalClock extends RelativeLayout {
 
     private final static String M12 = "h:mm";
     private final static String M24 = "kk:mm";
diff --git a/core/java/com/android/internal/widget/DrawableHolder.java b/core/java/com/android/internal/widget/DrawableHolder.java
new file mode 100644
index 0000000..958cabb
--- /dev/null
+++ b/core/java/com/android/internal/widget/DrawableHolder.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import java.util.ArrayList;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.Animator.AnimatorListener;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.util.Log;
+import android.view.animation.DecelerateInterpolator;
+
+/**
+ * This class is a container for a Drawable with multiple animated properties.
+ *
+ */
+public class DrawableHolder implements AnimatorListener {
+    public static final DecelerateInterpolator EASE_OUT_INTERPOLATOR = new DecelerateInterpolator();
+    private static final String TAG = "DrawableHolder";
+    private static final boolean DBG = false;
+    private float mX = 0.0f;
+    private float mY = 0.0f;
+    private float mScaleX = 1.0f;
+    private float mScaleY = 1.0f;
+    private BitmapDrawable mDrawable;
+    private float mAlpha = 1f;
+    private ArrayList<ObjectAnimator> mAnimators = new ArrayList<ObjectAnimator>();
+    private ArrayList<ObjectAnimator> mNeedToStart = new ArrayList<ObjectAnimator>();
+
+    public DrawableHolder(BitmapDrawable drawable) {
+        this(drawable, 0.0f, 0.0f);
+    }
+
+    public DrawableHolder(BitmapDrawable drawable, float x, float y) {
+        mDrawable = drawable;
+        mX = x;
+        mY = y;
+        mDrawable.getPaint().setAntiAlias(true); // Force AA
+        mDrawable.setBounds(0, 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight());
+    }
+
+    /**
+     *
+     * Adds an animation that interpolates given property from its current value
+     * to the given value.
+     *
+     * @param duration the duration, in ms.
+     * @param delay the delay to start the animation, in ms.
+     * @param property the property to animate
+     * @param toValue the target value
+     * @param replace if true, replace the current animation with this one.
+     */
+    public ObjectAnimator addAnimTo(long duration, long delay,
+            String property, float toValue, boolean replace) {
+
+        if (replace) removeAnimationFor(property);
+
+        ObjectAnimator anim = ObjectAnimator.ofFloat(this, property, toValue);
+        anim.setDuration(duration);
+        anim.setStartDelay(delay);
+        anim.setInterpolator(EASE_OUT_INTERPOLATOR);
+        this.addAnimation(anim, replace);
+        if (DBG) Log.v(TAG, "animationCount = " + mAnimators.size());
+        return anim;
+    }
+
+    /**
+     * Stops all animations for the given property and removes it from the list.
+     *
+     * @param property
+     */
+    public void removeAnimationFor(String property) {
+        ArrayList<ObjectAnimator> removalList = new ArrayList<ObjectAnimator>();
+        for (ObjectAnimator currentAnim : mAnimators) {
+            if (property.equals(currentAnim.getPropertyName())) {
+                currentAnim.cancel();
+                removalList.add(currentAnim);
+            }
+        }
+        if (DBG) Log.v(TAG, "Remove list size: " + removalList.size());
+        mAnimators.removeAll(removalList);
+    }
+
+    /**
+     * Stops all animations and removes them from the list.
+     */
+    public void clearAnimations() {
+        for (ObjectAnimator currentAnim : mAnimators) {
+            currentAnim.cancel();
+        }
+        mAnimators.clear();
+    }
+
+    /**
+     * Adds the given animation to the list of animations for this object.
+     *
+     * @param anim
+     * @param overwrite
+     * @return
+     */
+    private DrawableHolder addAnimation(ObjectAnimator anim, boolean overwrite) {
+        if (anim != null)
+            mAnimators.add(anim);
+        mNeedToStart.add(anim);
+        return this;
+    }
+
+    /**
+     * Draw this object to the canvas using the properties defined in this class.
+     *
+     * @param canvas canvas to draw into
+     */
+    public void draw(Canvas canvas) {
+        final float threshold = 1.0f / 256.0f; // contribution less than 1 LSB of RGB byte
+        if (mAlpha <= threshold) // don't bother if it won't show up
+            return;
+        canvas.save(Canvas.MATRIX_SAVE_FLAG);
+        canvas.translate(mX, mY);
+        canvas.scale(mScaleX, mScaleY);
+        canvas.translate(-0.5f*getWidth(), -0.5f*getHeight());
+        mDrawable.setAlpha((int) Math.round(mAlpha * 255f));
+        mDrawable.draw(canvas);
+        canvas.restore();
+    }
+
+    /**
+     * Starts all animations added since the last call to this function.  Used to synchronize
+     * animations.
+     *
+     * @param listener an optional listener to add to the animations. Typically used to know when
+     * to invalidate the surface these are being drawn to.
+     */
+    public void startAnimations(ValueAnimator.AnimatorUpdateListener listener) {
+        for (int i = 0; i < mNeedToStart.size(); i++) {
+            ObjectAnimator anim = mNeedToStart.get(i);
+            anim.addUpdateListener(listener);
+            anim.addListener(this);
+            anim.start();
+        }
+        mNeedToStart.clear();
+    }
+
+
+    public DrawableHolder setX(float value) {
+        mX = value;
+        return this;
+    }
+
+    public DrawableHolder setY(float value) {
+        mY = value;
+        return this;
+    }
+
+    public DrawableHolder setScaleX(float value) {
+        mScaleX = value;
+        return this;
+    }
+
+    public DrawableHolder setScaleY(float value) {
+        mScaleY = value;
+        return this;
+    }
+
+    public DrawableHolder setAlpha(float alpha) {
+        mAlpha = alpha;
+        return this;
+    }
+
+    public float getX() {
+        return mX;
+    }
+
+    public float getY() {
+        return mY;
+    }
+
+    public float getScaleX() {
+        return mScaleX;
+    }
+
+    public float getScaleY() {
+        return mScaleY;
+    }
+
+    public float getAlpha() {
+        return mAlpha;
+    }
+
+    public BitmapDrawable getDrawable() {
+        return mDrawable;
+    }
+
+    public int getWidth() {
+        return mDrawable.getIntrinsicWidth();
+    }
+
+    public int getHeight() {
+        return mDrawable.getIntrinsicHeight();
+    }
+
+    public void onAnimationCancel(Animator animation) {
+
+    }
+
+    public void onAnimationEnd(Animator animation) {
+        mAnimators.remove(animation);
+    }
+
+    public void onAnimationRepeat(Animator animation) {
+
+    }
+
+    public void onAnimationStart(Animator animation) {
+
+    }
+}
diff --git a/core/java/com/android/internal/widget/EditStyledText.java b/core/java/com/android/internal/widget/EditStyledText.java
deleted file mode 100644
index 82197c0..0000000
--- a/core/java/com/android/internal/widget/EditStyledText.java
+++ /dev/null
@@ -1,1663 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import java.io.InputStream;
-import java.util.ArrayList;
-
-import android.app.AlertDialog.Builder;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.RectShape;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.Html;
-import android.text.Layout;
-import android.text.Spannable;
-import android.text.Spanned;
-import android.text.method.ArrowKeyMovementMethod;
-import android.text.style.AbsoluteSizeSpan;
-import android.text.style.AlignmentSpan;
-import android.text.style.CharacterStyle;
-import android.text.style.ForegroundColorSpan;
-import android.text.style.ImageSpan;
-import android.text.style.ParagraphStyle;
-import android.text.style.QuoteSpan;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-import android.widget.TextView;
-
-/**
- * EditStyledText extends EditText for managing the flow and status to edit
- * the styled text. This manages the states and flows of editing, supports
- * inserting image, import/export HTML.
- */
-public class EditStyledText extends EditText {
-
-    private static final String LOG_TAG = "EditStyledText";
-    private static final boolean DBG = false;
-
-    /**
-     * The modes of editing actions.
-     */
-    /** The mode that no editing action is done. */
-    public static final int MODE_NOTHING = 0;
-    /** The mode of copy. */
-    public static final int MODE_COPY = 1;
-    /** The mode of paste. */
-    public static final int MODE_PASTE = 2;
-    /** The mode of changing size. */
-    public static final int MODE_SIZE = 3;
-    /** The mode of changing color. */
-    public static final int MODE_COLOR = 4;
-    /** The mode of selection. */
-    public static final int MODE_SELECT = 5;
-    /** The mode of changing alignment. */
-    public static final int MODE_ALIGN = 6;
-    /** The mode of changing cut. */
-    public static final int MODE_CUT = 7;
-
-    /**
-     * The state of selection.
-     */
-    /** The state that selection isn't started. */
-    public static final int STATE_SELECT_OFF = 0;
-    /** The state that selection is started. */
-    public static final int STATE_SELECT_ON = 1;
-    /** The state that selection is done, but not fixed. */
-    public static final int STATE_SELECTED = 2;
-    /** The state that selection is done and not fixed. */
-    public static final int STATE_SELECT_FIX = 3;
-
-    /**
-     * The help message strings.
-     */
-    public static final int HINT_MSG_NULL = 0;
-    public static final int HINT_MSG_COPY_BUF_BLANK = 1;
-    public static final int HINT_MSG_SELECT_START = 2;
-    public static final int HINT_MSG_SELECT_END = 3;
-    public static final int HINT_MSG_PUSH_COMPETE = 4;
-
-    
-    /**
-     * The help message strings.
-     */
-    public static final int DEFAULT_BACKGROUND_COLOR = 0x00FFFFFF;
-
-    /**
-     * EditStyledTextInterface provides functions for notifying messages to
-     * calling class.
-     */
-    public interface EditStyledTextNotifier {
-        public void notifyHintMsg(int msgId);
-        public void notifyStateChanged(int mode, int state);
-    }
-
-    private EditStyledTextNotifier mESTInterface;
-
-    /**
-     * EditStyledTextEditorManager manages the flow and status of each
-     * function for editing styled text.
-     */
-    private EditorManager mManager;
-    private StyledTextConverter mConverter;
-    private StyledTextDialog mDialog;
-    private Drawable mDefaultBackground;
-    private int mBackgroundColor;
-
-    /**
-     * EditStyledText extends EditText for managing flow of each editing
-     * action.
-     */
-    public EditStyledText(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        init();
-    }
-
-    public EditStyledText(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init();
-    }
-
-    public EditStyledText(Context context) {
-        super(context);
-        init();
-    }
-
-    /**
-     * Set Notifier.
-     */
-    public void setNotifier(EditStyledTextNotifier estInterface) {
-        mESTInterface = estInterface;
-    }
-
-    /**
-     * Set Builder for AlertDialog.
-     * 
-     * @param builder
-     *            Builder for opening Alert Dialog.
-     */
-    public void setBuilder(Builder builder) {
-        mDialog.setBuilder(builder);
-    }
-
-    /**
-     * Set Parameters for ColorAlertDialog.
-     * 
-     * @param colortitle
-     *            Title for Alert Dialog.
-     * @param colornames
-     *            List of name of selecting color.
-     * @param colorints
-     *            List of int of color.
-     */
-    public void setColorAlertParams(CharSequence colortitle,
-            CharSequence[] colornames, CharSequence[] colorints) {
-        mDialog.setColorAlertParams(colortitle, colornames, colorints);
-    }
-
-    /**
-     * Set Parameters for SizeAlertDialog.
-     * 
-     * @param sizetitle
-     *            Title for Alert Dialog.
-     * @param sizenames
-     *            List of name of selecting size.
-     * @param sizedisplayints
-     *            List of int of size displayed in TextView.
-     * @param sizesendints
-     *            List of int of size exported to HTML.
-     */
-    public void setSizeAlertParams(CharSequence sizetitle,
-            CharSequence[] sizenames, CharSequence[] sizedisplayints,
-            CharSequence[] sizesendints) {
-        mDialog.setSizeAlertParams(sizetitle, sizenames, sizedisplayints,
-                sizesendints);
-    }
-
-    public void setAlignAlertParams(CharSequence aligntitle,
-            CharSequence[] alignnames) {
-        mDialog.setAlignAlertParams(aligntitle, alignnames);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (mManager.isSoftKeyBlocked() &&
-                event.getAction() == MotionEvent.ACTION_UP) {
-            cancelLongPress();
-        }
-        final boolean superResult = super.onTouchEvent(event);
-        if (event.getAction() == MotionEvent.ACTION_UP) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onTouchEvent");
-            }
-            mManager.onCursorMoved();
-        }
-        return superResult;
-    }
-
-    /**
-     * Start editing. This function have to be called before other editing
-     * actions.
-     */
-    public void onStartEdit() {
-        mManager.onStartEdit();
-    }
-
-    /**
-     * End editing.
-     */
-    public void onEndEdit() {
-        mManager.onEndEdit();
-    }
-
-    /**
-     * Start "Copy" action.
-     */
-    public void onStartCopy() {
-        mManager.onStartCopy();
-    }
-
-    /**
-     * Start "Cut" action.
-     */
-    public void onStartCut() {
-        mManager.onStartCut();
-    }
-
-    /**
-     * Start "Paste" action.
-     */
-    public void onStartPaste() {
-        mManager.onStartPaste();
-    }
-
-    /**
-     * Start changing "Size" action.
-     */
-    public void onStartSize() {
-        mManager.onStartSize();
-    }
-
-    /**
-     * Start changing "Color" action.
-     */
-    public void onStartColor() {
-        mManager.onStartColor();
-    }
-
-    /**
-     * Start changing "BackgroundColor" action.
-     */
-    public void onStartBackgroundColor() {
-        mManager.onStartBackgroundColor();
-    }
-
-    /**
-     * Start changing "Alignment" action.
-     */
-    public void onStartAlign() {
-        mManager.onStartAlign();
-    }
-
-    /**
-     * Start "Select" action.
-     */
-    public void onStartSelect() {
-        mManager.onStartSelect();
-    }
-
-    /**
-     * Start "SelectAll" action.
-     */
-    public void onStartSelectAll() {
-        mManager.onStartSelectAll();
-    }
-
-    /**
-     * Fix Selected Item.
-     */
-    public void onFixSelectedItem() {
-        mManager.onFixSelectedItem();
-    }
-
-    /**
-     * InsertImage to TextView by using URI
-     * 
-     * @param uri
-     *            URI of the iamge inserted to TextView.
-     */
-    public void onInsertImage(Uri uri) {
-        mManager.onInsertImage(uri);
-    }
-
-    /**
-     * InsertImage to TextView by using resource ID
-     * 
-     * @param resId
-     *            Resource ID of the iamge inserted to TextView.
-     */
-    public void onInsertImage(int resId) {
-        mManager.onInsertImage(resId);
-    }
-
-    public void onInsertHorizontalLine() {
-        mManager.onInsertHorizontalLine();
-    }
-
-    public void onClearStyles() {
-        mManager.onClearStyles();
-    }
-    /**
-     * Set Size of the Item.
-     * 
-     * @param size
-     *            The size of the Item.
-     */
-    public void setItemSize(int size) {
-        mManager.setItemSize(size);
-    }
-
-    /**
-     * Set Color of the Item.
-     * 
-     * @param color
-     *            The color of the Item.
-     */
-    public void setItemColor(int color) {
-        mManager.setItemColor(color);
-    }
-
-    /**
-     * Set Alignment of the Item.
-     * 
-     * @param color
-     *            The color of the Item.
-     */
-    public void setAlignment(Layout.Alignment align) {
-        mManager.setAlignment(align);
-    }
-
-    /**
-     * Set Background color of View.
-     * 
-     * @param color
-     *            The background color of view.
-     */
-    @Override
-    public void setBackgroundColor(int color) {
-        super.setBackgroundColor(color);
-        mBackgroundColor = color;
-    }
-
-    /**
-     * Set html to EditStyledText.
-     * 
-     * @param html
-     *            The html to be set.
-     */
-    public void setHtml(String html) {
-        mConverter.SetHtml(html);
-    }
-    /**
-     * Check whether editing is started or not.
-     * 
-     * @return Whether editing is started or not.
-     */
-    public boolean isEditting() {
-        return mManager.isEditting();
-    }
-
-    /**
-     * Check whether styled text or not.
-     * 
-     * @return Whether styled text or not.
-     */
-    public boolean isStyledText() {
-        return mManager.isStyledText();
-    }
-    /**
-     * Check whether SoftKey is Blocked or not.
-     * 
-     * @return whether SoftKey is Blocked or not.
-     */
-    public boolean isSoftKeyBlocked() {
-        return mManager.isSoftKeyBlocked();
-    }
-
-    /**
-     * Get the mode of the action.
-     * 
-     * @return The mode of the action.
-     */
-    public int getEditMode() {
-        return mManager.getEditMode();
-    }
-
-    /**
-     * Get the state of the selection.
-     * 
-     * @return The state of the selection.
-     */
-    public int getSelectState() {
-        return mManager.getSelectState();
-    }
-
-    @Override
-    public Bundle getInputExtras(boolean create) {
-        if (DBG) {
-            Log.d(LOG_TAG, "---getInputExtras");
-        }
-        Bundle bundle = super.getInputExtras(create);
-        if (bundle != null) {
-            bundle = new Bundle();
-        }
-        bundle.putBoolean("allowEmoji", true);
-        return bundle;
-    }
-
-    /**
-     * Get the state of the selection.
-     * 
-     * @return The state of the selection.
-     */
-    public String getHtml() {
-        return mConverter.getHtml();
-    }
-
-    /**
-     * Get the state of the selection.
-     * 
-     * @param uris
-     *            The array of used uris.
-     * @return The state of the selection.
-     */
-    public String getHtml(ArrayList<Uri> uris) {
-        mConverter.getUriArray(uris, getText());
-        return mConverter.getHtml();
-    }
-
-    /**
-     * Get Background color of View.
-     * 
-     * @return The background color of View.
-     */
-    public int getBackgroundColor() {
-        return mBackgroundColor;
-    }
-
-    /**
-     * Get Foreground color of View.
-     * 
-     * @return The background color of View.
-     */
-    public int getForeGroundColor(int pos) {
-        if (DBG) {
-            Log.d(LOG_TAG, "---getForeGroundColor: " + pos);
-        }
-        if (pos < 0 || pos > getText().length()) {
-            Log.e(LOG_TAG, "---getForeGroundColor: Illigal position.");
-            return DEFAULT_BACKGROUND_COLOR;
-        } else {
-            ForegroundColorSpan[] spans =
-                getText().getSpans(pos, pos, ForegroundColorSpan.class);
-            if (spans.length > 0) {
-                return spans[0].getForegroundColor();
-            } else {
-                return DEFAULT_BACKGROUND_COLOR;
-            }
-        }
-    }
-
-    /**
-     * Initialize members.
-     */
-    private void init() {
-        if (DBG) {
-            Log.d(LOG_TAG, "--- init");
-        }
-        requestFocus();
-        mDefaultBackground = getBackground();
-        mBackgroundColor = DEFAULT_BACKGROUND_COLOR;
-        mManager = new EditorManager(this);
-        mConverter = new StyledTextConverter(this);
-        mDialog = new StyledTextDialog(this);
-        setMovementMethod(new StyledTextArrowKeyMethod(mManager));
-        mManager.blockSoftKey();
-        mManager.unblockSoftKey();
-    }
-
-    /**
-     * Show Foreground Color Selecting Dialog.
-     */
-    private void onShowForegroundColorAlert() {
-        mDialog.onShowForegroundColorAlertDialog();
-    }
-
-    /**
-     * Show Background Color Selecting Dialog.
-     */
-    private void onShowBackgroundColorAlert() {
-        mDialog.onShowBackgroundColorAlertDialog();
-    }
-
-    /**
-     * Show Size Selecting Dialog.
-     */
-    private void onShowSizeAlert() {
-        mDialog.onShowSizeAlertDialog();
-    }
-
-    /**
-     * Show Alignment Selecting Dialog.
-     */
-    private void onShowAlignAlert() {
-        mDialog.onShowAlignAlertDialog();
-    }
-
-    /**
-     * Notify hint messages what action is expected to calling class.
-     * 
-     * @param msgId
-     *            Id of the hint message.
-     */
-    private void setHintMessage(int msgId) {
-        if (mESTInterface != null) {
-            mESTInterface.notifyHintMsg(msgId);
-        }
-    }
-
-    /**
-     * Notify the event that the mode and state are changed.
-     * 
-     * @param mode
-     *            Mode of the editing action.
-     * @param state
-     *            Mode of the selection state.
-     */
-    private void notifyStateChanged(int mode, int state) {
-        if (mESTInterface != null) {
-            mESTInterface.notifyStateChanged(mode, state);
-        }
-    }
-
-    /**
-     * EditorManager manages the flow and status of editing actions.
-     */
-    private class EditorManager {
-        private boolean mEditFlag = false;
-        private boolean mSoftKeyBlockFlag = false;
-        private int mMode = 0;
-        private int mState = 0;
-        private int mCurStart = 0;
-        private int mCurEnd = 0;
-        private EditStyledText mEST;
-
-        EditorManager(EditStyledText est) {
-            mEST = est;
-        }
-
-        public void onStartEdit() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onStartEdit");
-            }
-            Log.d(LOG_TAG, "--- onstartedit:");
-            handleResetEdit();
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onEndEdit() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onEndEdit");
-            }
-            handleCancel();
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onStartCopy() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onStartCopy");
-            }
-            handleCopy();
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onStartCut() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onStartCut");
-            }
-            handleCut();
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onStartPaste() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onStartPaste");
-            }
-            handlePaste();
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onStartSize() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onStartSize");
-            }
-            handleSize();
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onStartAlign() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onStartAlignRight");
-            }
-            handleAlign();
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onStartColor() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onClickColor");
-            }
-            handleColor();
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onStartBackgroundColor() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onClickColor");
-            }
-            mEST.onShowBackgroundColorAlert();
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onStartSelect() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onClickSelect");
-            }
-            mMode = MODE_SELECT;
-            if (mState == STATE_SELECT_OFF) {
-                handleSelect();
-            } else {
-                unsetSelect();
-                handleSelect();
-            }
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onCursorMoved() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onClickView");
-            }
-            if (mState == STATE_SELECT_ON || mState == STATE_SELECTED) {
-                handleSelect();
-                mEST.notifyStateChanged(mMode, mState);
-            }
-        }
-
-        public void onStartSelectAll() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onClickSelectAll");
-            }
-            handleSelectAll();
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onFixSelectedItem() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onClickComplete");
-            }
-            handleComplete();
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onInsertImage(Uri uri) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onInsertImage by URI: " + uri.getPath()
-                        + "," + uri.toString());
-            }
-            insertImageSpan(new ImageSpan(mEST.getContext(), uri));
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onInsertImage(int resID) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onInsertImage by resID");
-            }
-            insertImageSpan(new ImageSpan(mEST.getContext(), resID));
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onInsertHorizontalLine() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onInsertHorizontalLine:");
-            }
-            insertImageSpan(new HorizontalLineSpan(0xFF000000, mEST));
-            mEST.notifyStateChanged(mMode, mState);
-        }
-
-        public void onClearStyles() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onClearStyles");
-            }
-            Editable txt = mEST.getText();
-            int len = txt.length();
-            Object[] styles = txt.getSpans(0, len, Object.class);
-            for (Object style : styles) {
-                if (style instanceof ParagraphStyle ||
-                        style instanceof QuoteSpan ||
-                        style instanceof CharacterStyle) {
-                    if (style instanceof ImageSpan) {
-                        int start = txt.getSpanStart(style);
-                        int end = txt.getSpanEnd(style);
-                        txt.replace(start, end, "");
-                    }
-                    txt.removeSpan(style);
-                }
-            }
-            mEST.setBackgroundDrawable(mEST.mDefaultBackground);
-            mEST.mBackgroundColor = DEFAULT_BACKGROUND_COLOR;
-        }
-
-        public void setItemSize(int size) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onClickSizeItem");
-            }
-            if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
-                changeSizeSelectedText(size);
-                handleResetEdit();
-            }
-        }
-
-        public void setItemColor(int color) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onClickColorItem");
-            }
-            if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
-                changeColorSelectedText(color);
-                handleResetEdit();
-            }
-        }
-
-        public void setAlignment(Layout.Alignment align) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onClickColorItem");
-            }
-            if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
-                changeAlign(align);
-                handleResetEdit();
-            }
-        }
-
-        public boolean isEditting() {
-            return mEditFlag;
-        }
-
-        /* If the style of the span is added, add check case for that style */
-        public boolean isStyledText() {
-            Editable txt = mEST.getText();
-            int len = txt.length();
-            if (txt.getSpans(0, len -1, ParagraphStyle.class).length > 0 ||
-                    txt.getSpans(0, len -1, QuoteSpan.class).length > 0 ||
-                    txt.getSpans(0, len -1, CharacterStyle.class).length > 0 ||
-                    mEST.mBackgroundColor != DEFAULT_BACKGROUND_COLOR) {
-                return true;
-            }
-            return false;
-        }
-
-        public boolean isSoftKeyBlocked() {
-            return mSoftKeyBlockFlag;
-        }
-
-        public int getEditMode() {
-            return mMode;
-        }
-
-        public int getSelectState() {
-            return mState;
-        }
-
-        public int getSelectionStart() {
-            return mCurStart;
-        }
-
-        public int getSelectionEnd() {
-            return mCurEnd;
-        }
-
-        private void doNextHandle() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- doNextHandle: " + mMode + "," + mState);
-            }
-            switch (mMode) {
-            case MODE_COPY:
-                handleCopy();
-                break;
-            case MODE_CUT:
-                handleCut();
-                break;
-            case MODE_PASTE:
-                handlePaste();
-                break;
-            case MODE_SIZE:
-                handleSize();
-                break;
-            case MODE_COLOR:
-                handleColor();
-                break;
-            case MODE_ALIGN:
-                handleAlign();
-                break;
-            default:
-                break;
-            }
-        }
-
-        private void handleCancel() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- handleCancel");
-            }
-            mMode = MODE_NOTHING;
-            mState = STATE_SELECT_OFF;
-            mEditFlag = false;
-            Log.d(LOG_TAG, "--- handleCancel:" + mEST.getInputType());
-            unblockSoftKey();
-            unsetSelect();
-        }
-
-        private void handleComplete() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- handleComplete");
-            }
-            if (!mEditFlag) {
-                return;
-            }
-            if (mState == STATE_SELECTED) {
-                mState = STATE_SELECT_FIX;
-            }
-            doNextHandle();
-        }
-
-        private void handleTextViewFunc(int mode, int id) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- handleTextView: " + mMode + "," + mState +
-                        "," + id);
-            }
-            if (!mEditFlag) {
-                return;
-            }
-            if (mMode == MODE_NOTHING || mMode == MODE_SELECT) {
-                mMode = mode;
-                if (mState == STATE_SELECTED) {
-                    mState = STATE_SELECT_FIX;
-                    handleTextViewFunc(mode, id);
-                } else {
-                    handleSelect();
-                }
-            } else if (mMode != mode) {
-                handleCancel();
-                mMode = mode;
-                handleTextViewFunc(mode, id);
-            } else if (mState == STATE_SELECT_FIX) {
-                mEST.onTextContextMenuItem(id);
-                handleResetEdit();
-            }
-        }
-
-        private void handleCopy() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- handleCopy: " + mMode + "," + mState);
-            }
-            handleTextViewFunc(MODE_COPY, android.R.id.copy);
-        }
-
-        private void handleCut() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- handleCopy: " + mMode + "," + mState);
-            }
-            handleTextViewFunc(MODE_CUT, android.R.id.cut);
-        }
-
-        private void handlePaste() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- handlePaste");
-            }
-            if (!mEditFlag) {
-                return;
-            }
-            mEST.onTextContextMenuItem(android.R.id.paste);
-        }
-
-        private void handleSetSpan(int mode) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- handleSetSpan:" + mEditFlag + ","
-                        + mState + ',' + mMode);
-            }
-            if (!mEditFlag) {
-                Log.e(LOG_TAG, "--- handleSetSpan: Editing is not started.");
-                return;
-            }
-            if (mMode == MODE_NOTHING || mMode == MODE_SELECT) {
-                mMode = mode;
-                if (mState == STATE_SELECTED) {
-                    mState = STATE_SELECT_FIX;
-                    handleSetSpan(mode);
-                } else {
-                    handleSelect();
-                }
-            } else if (mMode != mode) {
-                handleCancel();
-                mMode = mode;
-                handleSetSpan(mode);
-            } else {
-                if (mState == STATE_SELECT_FIX) {
-                    mEST.setHintMessage(HINT_MSG_NULL);
-                    switch (mode) {
-                    case MODE_COLOR:
-                        mEST.onShowForegroundColorAlert();
-                        break;
-                    case MODE_SIZE:
-                        mEST.onShowSizeAlert();
-                        break;
-                    case MODE_ALIGN:
-                        mEST.onShowAlignAlert();
-                        break;
-                    default:
-                        Log.e(LOG_TAG, "--- handleSetSpan: invalid mode.");
-                        break;
-                    }
-                } else {
-                    Log.d(LOG_TAG, "--- handleSetSpan: do nothing.");
-                }
-            }
-        }
-
-        private void handleSize() {
-            handleSetSpan(MODE_SIZE);
-        }
-
-        private void handleColor() {
-            handleSetSpan(MODE_COLOR);
-        }
-
-        private void handleAlign() {
-            handleSetSpan(MODE_ALIGN);
-        }
-
-        private void handleSelect() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- handleSelect:" + mEditFlag + "," + mState);
-            }
-            if (!mEditFlag) {
-                return;
-            }
-            if (mState == STATE_SELECT_OFF) {
-                if (isTextSelected()) {
-                    Log.e(LOG_TAG, "Selection is off, but selected");
-                }
-                setSelectStartPos();
-                blockSoftKey();
-                mEST.setHintMessage(HINT_MSG_SELECT_END);
-            } else if (mState == STATE_SELECT_ON) {
-                if (isTextSelected()) {
-                    Log.e(LOG_TAG, "Selection now start, but selected");
-                }
-                setSelectedEndPos();
-                mEST.setHintMessage(HINT_MSG_PUSH_COMPETE);
-                doNextHandle();
-            } else if (mState == STATE_SELECTED) {
-                if (!isTextSelected()) {
-                    Log.e(LOG_TAG, "Selection is done, but not selected");
-                }
-                setSelectedEndPos();
-                doNextHandle();
-            }
-        }
-
-        private void handleSelectAll() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- handleSelectAll");
-            }
-            if (!mEditFlag) {
-                return;
-            }
-            mEST.selectAll();
-            mState = STATE_SELECTED;
-        }
-
-        private void handleResetEdit() {
-            if (DBG) {
-                Log.d(LOG_TAG, "Reset Editor");
-            }
-            blockSoftKey();
-            handleCancel();
-            mEditFlag = true;
-            mEST.setHintMessage(HINT_MSG_SELECT_START);
-        }
-
-        private void setSelection() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onSelect:" + mCurStart + "," + mCurEnd);
-            }
-            if (mCurStart >= 0 && mCurStart <= mEST.getText().length()
-                    && mCurEnd >= 0 && mCurEnd <= mEST.getText().length()) {
-                if (mCurStart < mCurEnd) {
-                    mEST.setSelection(mCurStart, mCurEnd);
-                } else {
-                    mEST.setSelection(mCurEnd, mCurStart);
-                }
-                mState = STATE_SELECTED;
-            } else {
-                Log.e(LOG_TAG,
-                        "Select is on, but cursor positions are illigal.:"
-                                + mEST.getText().length() + "," + mCurStart
-                                + "," + mCurEnd);
-            }
-        }
-
-        private void unsetSelect() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- offSelect");
-            }
-            int currpos = mEST.getSelectionStart();
-            mEST.setSelection(currpos, currpos);
-            mState = STATE_SELECT_OFF;
-        }
-
-        private void setSelectStartPos() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- setSelectStartPos");
-            }
-            mCurStart = mEST.getSelectionStart();
-            mState = STATE_SELECT_ON;
-        }
-
-        private void setSelectedEndPos() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- setSelectEndPos:");
-            }
-            if (mEST.getSelectionStart() == mCurStart) {
-                setSelectedEndPos(mEST.getSelectionEnd());
-            } else {
-                setSelectedEndPos(mEST.getSelectionStart());
-            }
-        }
-
-        public void setSelectedEndPos(int pos) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- setSelectedEndPos:");
-            }
-            mCurEnd = pos;
-            setSelection();
-        }
-
-        private boolean isTextSelected() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- isTextSelected:" + mCurStart + ","
-                        + mCurEnd);
-            }
-            return (mCurStart != mCurEnd)
-                    && (mState == STATE_SELECTED ||
-                            mState == STATE_SELECT_FIX);
-        }
-
-        private void setStyledTextSpan(Object span, int start, int end) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- setStyledTextSpan:" + mMode + ","
-                        + start + "," + end);
-            }
-            if (start < end) {
-                mEST.getText().setSpan(span, start, end,
-                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-            } else {
-                mEST.getText().setSpan(span, end, start,
-                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-            }
-        }
-
-        private void changeSizeSelectedText(int size) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- changeSize:" + size);
-            }
-            setStyledTextSpan(new AbsoluteSizeSpan(size),
-                mCurStart, mCurEnd);
-        }
-
-        private void changeColorSelectedText(int color) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- changeColor:" + color);
-            }
-            setStyledTextSpan(new ForegroundColorSpan(color),
-                mCurStart, mCurEnd);
-        }
-
-        private void changeAlign(Layout.Alignment align) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- changeAlign:" + align);
-            }
-            setStyledTextSpan(new AlignmentSpan.Standard(align),
-                    findLineStart(mEST.getText(), mCurStart),
-                    findLineEnd(mEST.getText(), mCurEnd));
-        }
-
-        private int findLineStart(Editable text, int current) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- findLineStart: curr:" + current +
-                        ", length:" + text.length());
-            }
-            int pos = current;
-            for (; pos > 0; pos--) {
-                if (text.charAt(pos - 1) == '\n') {
-                    break;
-                }
-            }
-            return pos;
-        }
-
-        private void insertImageSpan(ImageSpan span) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- insertImageSpan");
-            }
-            if (span != null) {
-                Log.d(LOG_TAG, "--- insertimagespan:" + span.getDrawable().getIntrinsicHeight() + "," + span.getDrawable().getIntrinsicWidth());
-                Log.d(LOG_TAG, "--- insertimagespan:" + span.getDrawable().getClass());
-                int curpos = mEST.getSelectionStart();
-                mEST.getText().insert(curpos, "\uFFFC");
-                mEST.getText().setSpan(span, curpos, curpos + 1,
-                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-                mEST.notifyStateChanged(mMode, mState);
-            } else {
-                Log.e(LOG_TAG, "--- insertImageSpan: null span was inserted");
-            }
-        }
-
-        private int findLineEnd(Editable text, int current) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- findLineEnd: curr:" + current +
-                        ", length:" + text.length());
-            }
-            int pos = current;
-            for (; pos < text.length(); pos++) {
-                if (pos > 0 && text.charAt(pos - 1) == '\n') {
-                    break;
-                }
-            }
-            return pos;
-        }
-
-        private void blockSoftKey() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- blockSoftKey:");
-            }
-            InputMethodManager imm = (InputMethodManager) mEST.getContext().
-            getSystemService(Context.INPUT_METHOD_SERVICE);
-            imm.hideSoftInputFromWindow(mEST.getWindowToken(), 0);
-            mEST.setOnClickListener(
-                    new OnClickListener() {
-                        public void onClick(View v) {
-                            Log.d(LOG_TAG, "--- ontrackballclick:");
-                            onFixSelectedItem();
-                        }
-            });
-            mSoftKeyBlockFlag = true;
-        }
-
-        private void unblockSoftKey() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- unblockSoftKey:");
-            }
-            mEST.setOnClickListener(null);
-            mSoftKeyBlockFlag = false;
-        }
-    }
-
-    private class StyledTextConverter {
-        private EditStyledText mEST;
-
-        public StyledTextConverter(EditStyledText est) {
-            mEST = est;
-        }
-
-        public String getHtml() {
-            String htmlBody = Html.toHtml(mEST.getText());
-            if (DBG) {
-                Log.d(LOG_TAG, "--- getConvertedBody:" + htmlBody);
-            }
-            return htmlBody;
-        }
-
-        public void getUriArray(ArrayList<Uri> uris, Editable text) {
-            uris.clear();
-            if (DBG) {
-                Log.d(LOG_TAG, "--- getUriArray:");
-            }
-            int len = text.length();
-            int next;
-            for (int i = 0; i < text.length(); i = next) {
-                next = text.nextSpanTransition(i, len, ImageSpan.class);
-                ImageSpan[] images = text.getSpans(i, next, ImageSpan.class);
-                for (int j = 0; j < images.length; j++) {
-                    if (DBG) {
-                        Log.d(LOG_TAG, "--- getUriArray: foundArray" +
-                                ((ImageSpan) images[j]).getSource());
-                    }
-                    uris.add(Uri.parse(
-                            ((ImageSpan) images[j]).getSource()));
-                }
-            }
-        }
-
-        public void SetHtml (String html) {
-            final Spanned spanned = Html.fromHtml(html, new Html.ImageGetter() {
-                public Drawable getDrawable(String src) {
-                    Log.d(LOG_TAG, "--- sethtml: src="+src);
-                    if (src.startsWith("content://")) {
-                        Uri uri = Uri.parse(src);
-                        try {
-                            InputStream is = mEST.getContext().getContentResolver().openInputStream(uri);
-                            Bitmap bitmap = BitmapFactory.decodeStream(is);
-                            Drawable drawable = new BitmapDrawable(
-                                    getContext().getResources(), bitmap);
-                            drawable.setBounds(0, 0,
-                                    drawable.getIntrinsicWidth(),
-                                    drawable.getIntrinsicHeight());
-                            is.close();
-                            return drawable;
-                        } catch (Exception e) {
-                            Log.e(LOG_TAG, "--- set html: Failed to loaded content " + uri, e);
-                            return null;
-                        }
-                    }
-                    Log.d(LOG_TAG, "  unknown src="+src);
-                    return null;
-                }
-            }, null);
-            mEST.setText(spanned);
-        }
-    }
-
-    private class StyledTextDialog {
-        Builder mBuilder;
-        CharSequence mColorTitle;
-        CharSequence mSizeTitle;
-        CharSequence mAlignTitle;
-        CharSequence[] mColorNames;
-        CharSequence[] mColorInts;
-        CharSequence[] mSizeNames;
-        CharSequence[] mSizeDisplayInts;
-        CharSequence[] mSizeSendInts;
-        CharSequence[] mAlignNames;
-        EditStyledText mEST;
-
-        public StyledTextDialog(EditStyledText est) {
-            mEST = est;
-        }
-
-        public void setBuilder(Builder builder) {
-            mBuilder = builder;
-        }
-
-        public void setColorAlertParams(CharSequence colortitle,
-                CharSequence[] colornames, CharSequence[] colorints) {
-            mColorTitle = colortitle;
-            mColorNames = colornames;
-            mColorInts = colorints;
-        }
-
-        public void setSizeAlertParams(CharSequence sizetitle,
-                CharSequence[] sizenames, CharSequence[] sizedisplayints,
-                CharSequence[] sizesendints) {
-            mSizeTitle = sizetitle;
-            mSizeNames = sizenames;
-            mSizeDisplayInts = sizedisplayints;
-            mSizeSendInts = sizesendints;
-        }
-
-        public void setAlignAlertParams(CharSequence aligntitle,
-                CharSequence[] alignnames) {
-            mAlignTitle = aligntitle;
-            mAlignNames = alignnames;
-        }
-
-        private boolean checkColorAlertParams() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- checkParams");
-            }
-            if (mBuilder == null) {
-                Log.e(LOG_TAG, "--- builder is null.");
-                return false;
-            } else if (mColorTitle == null || mColorNames == null
-                    || mColorInts == null) {
-                Log.e(LOG_TAG, "--- color alert params are null.");
-                return false;
-            } else if (mColorNames.length != mColorInts.length) {
-                Log.e(LOG_TAG, "--- the length of color alert params are "
-                        + "different.");
-                return false;
-            }
-            return true;
-        }
-
-        private boolean checkSizeAlertParams() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- checkParams");
-            }
-            if (mBuilder == null) {
-                Log.e(LOG_TAG, "--- builder is null.");
-                return false;
-            } else if (mSizeTitle == null || mSizeNames == null
-                    || mSizeDisplayInts == null || mSizeSendInts == null) {
-                Log.e(LOG_TAG, "--- size alert params are null.");
-                return false;
-            } else if (mSizeNames.length != mSizeDisplayInts.length
-                    && mSizeSendInts.length != mSizeDisplayInts.length) {
-                Log.e(LOG_TAG, "--- the length of size alert params are "
-                        + "different.");
-                return false;
-            }
-            return true;
-        }
-
-        private boolean checkAlignAlertParams() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- checkAlignAlertParams");
-            }
-            if (mBuilder == null) {
-                Log.e(LOG_TAG, "--- builder is null.");
-                return false;
-            } else if (mAlignTitle == null) {
-                Log.e(LOG_TAG, "--- align alert params are null.");
-                return false;
-            }
-            return true;
-        }
-
-        private void onShowForegroundColorAlertDialog() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onShowForegroundColorAlertDialog");
-            }
-            if (!checkColorAlertParams()) {
-                return;
-            }
-            mBuilder.setTitle(mColorTitle);
-            mBuilder.setIcon(0);
-            mBuilder.
-            setItems(mColorNames,
-                    new DialogInterface.OnClickListener() {
-                public void onClick(DialogInterface dialog, int which) {
-                    Log.d("EETVM", "mBuilder.onclick:" + which);
-                    int color = Integer.parseInt(
-                            (String) mColorInts[which], 16) - 0x01000000;
-                    mEST.setItemColor(color);
-                }
-            });
-            mBuilder.show();
-        }
-
-        private void onShowBackgroundColorAlertDialog() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onShowBackgroundColorAlertDialog");
-            }
-            if (!checkColorAlertParams()) {
-                return;
-            }
-            mBuilder.setTitle(mColorTitle);
-            mBuilder.setIcon(0);
-            mBuilder.
-            setItems(mColorNames,
-                    new DialogInterface.OnClickListener() {
-                public void onClick(DialogInterface dialog, int which) {
-                    Log.d("EETVM", "mBuilder.onclick:" + which);
-                    int color = Integer.parseInt(
-                            (String) mColorInts[which], 16) - 0x01000000;
-                    mEST.setBackgroundColor(color);
-                }
-            });
-            mBuilder.show();
-        }
-
-        private void onShowSizeAlertDialog() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onShowSizeAlertDialog");
-            }
-            if (!checkSizeAlertParams()) {
-                return;
-            }
-            mBuilder.setTitle(mSizeTitle);
-            mBuilder.setIcon(0);
-            mBuilder.
-            setItems(mSizeNames,
-                    new DialogInterface.OnClickListener() {
-                public void onClick(DialogInterface dialog, int which) {
-                    Log.d(LOG_TAG, "mBuilder.onclick:" + which);
-                    int size = Integer
-                    .parseInt((String) mSizeDisplayInts[which]);
-                    mEST.setItemSize(size);
-                }
-            });
-            mBuilder.show();
-        }
-
-        private void onShowAlignAlertDialog() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- onShowAlignAlertDialog");
-            }
-            if (!checkAlignAlertParams()) {
-                return;
-            }
-            mBuilder.setTitle(mAlignTitle);
-            mBuilder.setIcon(0);
-            mBuilder.
-            setItems(mAlignNames,
-                    new DialogInterface.OnClickListener() {
-                public void onClick(DialogInterface dialog, int which) {
-                    Log.d(LOG_TAG, "mBuilder.onclick:" + which);
-                    Layout.Alignment align = Layout.Alignment.ALIGN_NORMAL;
-                    switch (which) {
-                    case 0:
-                        align = Layout.Alignment.ALIGN_NORMAL;
-                        break;
-                    case 1:
-                        align = Layout.Alignment.ALIGN_CENTER;
-                        break;
-                    case 2:
-                        align = Layout.Alignment.ALIGN_OPPOSITE;
-                        break;
-                    default:
-                        break;
-                    }
-                    mEST.setAlignment(align);
-                }
-            });
-            mBuilder.show();
-        }
-    }
-
-    private class StyledTextArrowKeyMethod extends ArrowKeyMovementMethod {
-        EditorManager mManager;
-        StyledTextArrowKeyMethod(EditorManager manager) {
-            super();
-            mManager = manager;
-        }
-
-        @Override
-        public boolean onKeyDown(TextView widget, Spannable buffer,
-                int keyCode, KeyEvent event) {
-            if (!mManager.isSoftKeyBlocked()) {
-                return super.onKeyDown(widget, buffer, keyCode, event);
-            }
-            if (executeDown(widget, buffer, keyCode)) {
-                return true;
-            }
-            return false;
-        }
-
-        private int getEndPos(TextView widget) {
-            int end;
-            if (widget.getSelectionStart() == mManager.getSelectionStart()) {
-                end = widget.getSelectionEnd();
-            } else {
-                end = widget.getSelectionStart();
-            }
-            return end;
-        }
-
-        private boolean up(TextView widget, Spannable buffer) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- up:");
-            }
-            Layout layout = widget.getLayout();
-            int end = getEndPos(widget);
-            int line = layout.getLineForOffset(end);
-            if (line > 0) {
-                int to;
-                if (layout.getParagraphDirection(line) ==
-                    layout.getParagraphDirection(line - 1)) {
-                    float h = layout.getPrimaryHorizontal(end);
-                    to = layout.getOffsetForHorizontal(line - 1, h);
-                } else {
-                    to = layout.getLineStart(line - 1);
-                }
-                mManager.setSelectedEndPos(to);
-                mManager.onCursorMoved();
-                return true;
-            }
-            return false;
-        }
-
-        private boolean down(TextView widget, Spannable buffer) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- down:");
-            }
-            Layout layout = widget.getLayout();
-            int end = getEndPos(widget);
-            int line = layout.getLineForOffset(end);
-            if (line < layout.getLineCount() - 1) {
-                int to;
-                if (layout.getParagraphDirection(line) ==
-                    layout.getParagraphDirection(line + 1)) {
-                    float h = layout.getPrimaryHorizontal(end);
-                    to = layout.getOffsetForHorizontal(line + 1, h);
-                } else {
-                    to = layout.getLineStart(line + 1);
-                }
-                mManager.setSelectedEndPos(to);
-                mManager.onCursorMoved();
-                return true;
-            }
-            return false;
-        }
-
-        private boolean left(TextView widget, Spannable buffer) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- left:");
-            }
-            Layout layout = widget.getLayout();
-            int to = layout.getOffsetToLeftOf(getEndPos(widget));
-            mManager.setSelectedEndPos(to);
-            mManager.onCursorMoved();
-            return true;
-        }
-
-        private boolean right(TextView widget, Spannable buffer) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- right:");
-            }
-            Layout layout = widget.getLayout();
-            int to = layout.getOffsetToRightOf(getEndPos(widget));
-            mManager.setSelectedEndPos(to);
-            mManager.onCursorMoved();
-            return true;
-        }
-
-        private boolean executeDown(TextView widget, Spannable buffer,
-                int keyCode) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- executeDown: " + keyCode);
-            }
-            boolean handled = false;
-
-            switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_UP:
-                handled |= up(widget, buffer);
-                break;
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-                handled |= down(widget, buffer);
-                break;
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-                handled |= left(widget, buffer);
-                break;
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-                handled |= right(widget, buffer);
-                break;
-                case KeyEvent.KEYCODE_DPAD_CENTER:
-                    mManager.onFixSelectedItem();
-                    handled = true;
-                    break;
-            }
-            return handled;
-        }
-    }
-
-    public class HorizontalLineSpan extends ImageSpan {
-        public HorizontalLineSpan(int color, View view) {
-            super(new HorizontalLineDrawable(color, view));
-        }
-    }
-    public class HorizontalLineDrawable extends ShapeDrawable {
-        private View mView;
-        public HorizontalLineDrawable(int color, View view) {
-            super(new RectShape());
-            mView = view;
-            renewColor(color);
-            renewBounds(view);
-        }
-        @Override
-        public void draw(Canvas canvas) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- draw:");
-            }
-            renewColor();
-            renewBounds(mView);
-            super.draw(canvas);
-        }
-
-        private void renewBounds(View view) {
-            if (DBG) {
-                int width = mView.getBackground().getBounds().width();
-                int height = mView.getBackground().getBounds().height();
-                Log.d(LOG_TAG, "--- renewBounds:" + width + "," + height);
-                Log.d(LOG_TAG, "--- renewBounds:" + mView.getClass());
-            }
-            int width = mView.getWidth();
-            if (width > 20) {
-                width -= 20;
-            }
-            setBounds(0, 0, width, 2);
-        }
-        private void renewColor(int color) {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- renewColor:" + color);
-            }
-            getPaint().setColor(color);
-        }
-        private void renewColor() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- renewColor:");
-            }
-            if (mView instanceof View) {
-                ImageSpan parent = getParentSpan();
-                Editable text = ((EditStyledText)mView).getText();
-                int start = text.getSpanStart(parent);
-                ForegroundColorSpan[] spans = text.getSpans(start, start, ForegroundColorSpan.class);
-                if (spans.length > 0) {
-                    renewColor(spans[spans.length - 1].getForegroundColor());
-                }
-            }
-        }
-        private ImageSpan getParentSpan() {
-            if (DBG) {
-                Log.d(LOG_TAG, "--- getParentSpan:");
-            }
-            if (mView instanceof EditStyledText) {
-                Editable text = ((EditStyledText)mView).getText();
-                ImageSpan[] images = text.getSpans(0, text.length(), ImageSpan.class);
-                if (images.length > 0) {
-                    for (ImageSpan image: images) {
-                        if (image.getDrawable() == this) {
-                            return image;
-                        }
-                    }
-                }
-            }
-            Log.e(LOG_TAG, "---renewBounds: Couldn't find");
-            return null;
-        }
-    }
-}
diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
new file mode 100644
index 0000000..ae9900c
--- /dev/null
+++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.android.internal.widget;
+
+import android.widget.RemoteViews;
+
+/** {@hide} */
+interface IRemoteViewsFactory {
+    void onDataSetChanged();
+    int getCount();
+    RemoteViews getViewAt(int position);
+    RemoteViews getLoadingView();
+    int getViewTypeCount();
+    long getItemId(int position);
+    boolean hasStableIds();
+}
+
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index c788605..90423be 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -96,6 +96,8 @@
     public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
     private final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt";
 
+    private final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
+
     private final Context mContext;
     private final ContentResolver mContentResolver;
     private DevicePolicyManager mDevicePolicyManager;
@@ -163,6 +165,33 @@
         return getDevicePolicyManager().getPasswordQuality(null);
     }
 
+    public int getRequestedPasswordHistoryLength() {
+        return getDevicePolicyManager().getPasswordHistoryLength(null);
+    }
+
+    public int getRequestedPasswordMinimumLetters() {
+        return getDevicePolicyManager().getPasswordMinimumLetters(null);
+    }
+
+    public int getRequestedPasswordMinimumUpperCase() {
+        return getDevicePolicyManager().getPasswordMinimumUpperCase(null);
+    }
+
+    public int getRequestedPasswordMinimumLowerCase() {
+        return getDevicePolicyManager().getPasswordMinimumLowerCase(null);
+    }
+
+    public int getRequestedPasswordMinimumNumeric() {
+        return getDevicePolicyManager().getPasswordMinimumNumeric(null);
+    }
+
+    public int getRequestedPasswordMinimumSymbols() {
+        return getDevicePolicyManager().getPasswordMinimumSymbols(null);
+    }
+
+    public int getRequestedPasswordMinimumNonLetter() {
+        return getDevicePolicyManager().getPasswordMinimumNonLetter(null);
+    }
     /**
      * Returns the actual password mode, as set by keyguard after updating the password.
      *
@@ -227,6 +256,33 @@
     }
 
     /**
+     * Check to see if a password matches any of the passwords stored in the
+     * password history.
+     *
+     * @param password The password to check.
+     * @return Whether the password matches any in the history.
+     */
+    public boolean checkPasswordHistory(String password) {
+        String passwordHashString = new String(passwordToHash(password));
+        String passwordHistory = getString(PASSWORD_HISTORY_KEY);
+        if (passwordHistory == null) {
+            return false;
+        }
+        // Password History may be too long...
+        int passwordHashLength = passwordHashString.length();
+        int passwordHistoryLength = getRequestedPasswordHistoryLength();
+        if(passwordHistoryLength == 0) {
+            return false;
+        }
+        int neededPasswordHistoryLength = passwordHashLength * passwordHistoryLength
+                + passwordHistoryLength - 1;
+        if (passwordHistory.length() > neededPasswordHistoryLength) {
+            passwordHistory = passwordHistory.substring(0, neededPasswordHistoryLength);
+        }
+        return passwordHistory.contains(passwordHashString);
+    }
+
+    /**
      * Check to see if the user has stored a lock pattern.
      * @return Whether a saved pattern exists.
      */
@@ -279,6 +335,11 @@
                     activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
                 }
                 break;
+            case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
+                if (isLockPasswordEnabled()) {
+                    activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+                }
+                break;
         }
         return activePasswordQuality;
     }
@@ -287,8 +348,6 @@
      * Clear any lock pattern or password.
      */
     public void clearLock() {
-        getDevicePolicyManager().setActivePasswordState(
-                DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
         saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
         setLockPatternEnabled(false);
         saveLockPattern(null);
@@ -301,7 +360,7 @@
      */
     public void saveLockPattern(List<LockPatternView.Cell> pattern) {
         // Compute the hash
-        final byte[] hash  = LockPatternUtils.patternToHash(pattern);
+        final byte[] hash = LockPatternUtils.patternToHash(pattern);
         try {
             // Write the hash to file
             RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "rw");
@@ -316,14 +375,15 @@
             if (pattern != null) {
                 setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
                 setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
-                dpm.setActivePasswordState(
-                        DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern.size());
+                dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern
+                        .size(), 0, 0, 0, 0, 0, 0);
             } else {
-                dpm.setActivePasswordState(
-                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
+                dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
+                        0, 0, 0, 0, 0);
             }
         } catch (FileNotFoundException fnfe) {
-            // Cant do much, unless we want to fail over to using the settings provider
+            // Cant do much, unless we want to fail over to using the settings
+            // provider
             Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
         } catch (IOException ioe) {
             // Cant do much
@@ -381,17 +441,59 @@
             DevicePolicyManager dpm = getDevicePolicyManager();
             if (password != null) {
                 int computedQuality = computePasswordQuality(password);
-                setLong(PASSWORD_TYPE_KEY, computedQuality);
+                setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality));
                 if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
-                    dpm.setActivePasswordState(computedQuality, password.length());
+                    int letters = 0;
+                    int uppercase = 0;
+                    int lowercase = 0;
+                    int numbers = 0;
+                    int symbols = 0;
+                    int nonletter = 0;
+                    for (int i = 0; i < password.length(); i++) {
+                        char c = password.charAt(i);
+                        if (c >= 'A' && c <= 'Z') {
+                            letters++;
+                            uppercase++;
+                        } else if (c >= 'a' && c <= 'z') {
+                            letters++;
+                            lowercase++;
+                        } else if (c >= '0' && c <= '9') {
+                            numbers++;
+                            nonletter++;
+                        } else {
+                            symbols++;
+                            nonletter++;
+                        }
+                    }
+                    dpm.setActivePasswordState(Math.max(quality, computedQuality), password
+                            .length(), letters, uppercase, lowercase, numbers, symbols, nonletter);
                 } else {
                     // The password is not anything.
                     dpm.setActivePasswordState(
-                            DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
+                            DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0);
                 }
+                // Add the password to the password history. We assume all
+                // password
+                // hashes have the same length for simplicity of implementation.
+                String passwordHistory = getString(PASSWORD_HISTORY_KEY);
+                if (passwordHistory == null) {
+                    passwordHistory = new String();
+                }
+                int passwordHistoryLength = getRequestedPasswordHistoryLength();
+                if (passwordHistoryLength == 0) {
+                    passwordHistory = "";
+                } else {
+                    passwordHistory = new String(hash) + "," + passwordHistory;
+                    // Cut it to contain passwordHistoryLength hashes
+                    // and passwordHistoryLength -1 commas.
+                    passwordHistory = passwordHistory.substring(0, Math.min(hash.length
+                            * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory
+                            .length()));
+                }
+                setString(PASSWORD_HISTORY_KEY, passwordHistory);
             } else {
                 dpm.setActivePasswordState(
-                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0);
+                        DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0);
             }
         } catch (FileNotFoundException fnfe) {
             // Cant do much, unless we want to fail over to using the settings provider
@@ -531,7 +633,8 @@
         return savedPasswordExists() &&
                 (mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
                         || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
-                        || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
+                        || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
+                        || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
     }
 
     /**
@@ -655,12 +758,21 @@
         android.provider.Settings.Secure.putLong(mContentResolver, secureSettingKey, value);
     }
 
+    private String getString(String secureSettingKey) {
+        return android.provider.Settings.Secure.getString(mContentResolver, secureSettingKey);
+    }
+
+    private void setString(String secureSettingKey, String value) {
+        android.provider.Settings.Secure.putString(mContentResolver, secureSettingKey, value);
+    }
+
     public boolean isSecure() {
         long mode = getKeyguardStoredPasswordQuality();
         final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
         final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
                 || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
-                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
+                || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
         final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
                 || isPassword && savedPasswordExists();
         return secure;
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 007e7b9..f80fbd8 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -129,6 +129,7 @@
     private long[] mVibePattern;
 
     private int mAspect;
+    private final Matrix mArrowMatrix = new Matrix();
 
     /**
      * Represents a cell in the 3 X 3 matrix of the unlock pattern view.
@@ -923,7 +924,6 @@
         // This assumes that the arrow image is drawn at 12:00 with it's top edge
         // coincident with the circle bitmap's top edge.
         Bitmap arrow = green ? mBitmapArrowGreenUp : mBitmapArrowRedUp;
-        Matrix matrix = new Matrix();
         final int cellWidth = mBitmapCircleDefault.getWidth();
         final int cellHeight = mBitmapCircleDefault.getHeight();
 
@@ -933,10 +933,10 @@
         final float angle = (float) Math.toDegrees(theta) + 90.0f;
 
         // compose matrix
-        matrix.setTranslate(leftX + offsetX, topY + offsetY); // transform to cell position
-        matrix.preRotate(angle, cellWidth / 2.0f, cellHeight / 2.0f);  // rotate about cell center
-        matrix.preTranslate((cellWidth - arrow.getWidth()) / 2.0f, 0.0f); // translate to 12:00 pos
-        canvas.drawBitmap(arrow, matrix, mPaint);
+        mArrowMatrix.setTranslate(leftX + offsetX, topY + offsetY); // transform to cell position
+        mArrowMatrix.preRotate(angle, cellWidth / 2.0f, cellHeight / 2.0f);  // rotate about cell center
+        mArrowMatrix.preTranslate((cellWidth - arrow.getWidth()) / 2.0f, 0.0f); // translate to 12:00 pos
+        canvas.drawBitmap(arrow, mArrowMatrix, mPaint);
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboard.java b/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
index e1a6737..facda36 100644
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
@@ -67,8 +67,22 @@
         this(context, xmlLayoutResId, 0);
     }
 
+    public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int width, int height) {
+        this(context, xmlLayoutResId, 0, width, height);
+    }
+
     public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int mode) {
         super(context, xmlLayoutResId, mode);
+        init(context);
+    }
+
+    public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int mode,
+            int width, int height) {
+        super(context, xmlLayoutResId, mode, width, height);
+        init(context);
+    }
+
+    private void init(Context context) {
         final Resources res = context.getResources();
         mRes = res;
         mShiftIcon = res.getDrawable(R.drawable.sym_keyboard_shift);
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
index 53720e4..384f7bc 100644
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
+++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
@@ -54,10 +54,20 @@
     private Vibrator mVibrator;
 
     public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView) {
+        this(context, keyboardView, targetView, true);
+    }
+
+    public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView,
+            boolean useFullScreenWidth) {
         mContext = context;
         mTargetView = targetView;
         mKeyboardView = keyboardView;
-        createKeyboards();
+        if (useFullScreenWidth || mKeyboardView.getLayoutParams().width == -1) {
+            createKeyboards();
+        } else {
+            createKeyboardsWithSpecificSize(mKeyboardView.getLayoutParams().width,
+                    mKeyboardView.getLayoutParams().height);
+        }
         mKeyboardView.setOnKeyboardActionListener(this);
         mVibrator = new Vibrator();
     }
@@ -66,6 +76,29 @@
         return mKeyboardMode == KEYBOARD_MODE_ALPHA;
     }
 
+    private void createKeyboardsWithSpecificSize(int viewWidth, int viewHeight) {
+        mNumericKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_numeric,
+                viewWidth, viewHeight);
+        mQwertyKeyboard = new PasswordEntryKeyboard(mContext,
+                R.xml.password_kbd_qwerty, R.id.mode_normal, viewWidth, viewHeight);
+        mQwertyKeyboard.enableShiftLock();
+
+        mQwertyKeyboardShifted = new PasswordEntryKeyboard(mContext,
+                R.xml.password_kbd_qwerty_shifted,
+                R.id.mode_normal, viewWidth, viewHeight);
+        mQwertyKeyboardShifted.enableShiftLock();
+        mQwertyKeyboardShifted.setShifted(true); // always shifted.
+
+        mSymbolsKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_symbols,
+                viewWidth, viewHeight);
+        mSymbolsKeyboard.enableShiftLock();
+
+        mSymbolsKeyboardShifted = new PasswordEntryKeyboard(mContext,
+                R.xml.password_kbd_symbols_shift, viewWidth, viewHeight);
+        mSymbolsKeyboardShifted.enableShiftLock();
+        mSymbolsKeyboardShifted.setShifted(true); // always shifted
+    }
+
     private void createKeyboards() {
         mNumericKeyboard = new PasswordEntryKeyboard(mContext, R.xml.password_kbd_numeric);
         mQwertyKeyboard = new PasswordEntryKeyboard(mContext,
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index 9152729..3865510 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -17,10 +17,8 @@
 package com.android.internal.widget;
 
 import android.content.Context;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Vibrator;
@@ -38,6 +36,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 import android.widget.ImageView.ScaleType;
+
 import com.android.internal.R;
 
 /**
@@ -69,21 +68,21 @@
     private int mGrabbedState = OnTriggerListener.NO_HANDLE;
     private boolean mTriggered = false;
     private Vibrator mVibrator;
-    private float mDensity; // used to scale dimensions for bitmaps.
+    private final float mDensity; // used to scale dimensions for bitmaps.
 
     /**
      * Either {@link #HORIZONTAL} or {@link #VERTICAL}.
      */
-    private int mOrientation;
+    private final int mOrientation;
 
-    private Slider mLeftSlider;
-    private Slider mRightSlider;
+    private final Slider mLeftSlider;
+    private final Slider mRightSlider;
     private Slider mCurrentSlider;
     private boolean mTracking;
     private float mThreshold;
     private Slider mOtherSlider;
     private boolean mAnimating;
-    private Rect mTmpRect;
+    private final Rect mTmpRect;
 
     /**
      * Listener used to reset the view when the current animation completes.
@@ -417,7 +416,7 @@
         }
 
         /**
-         * Start animating the slider. Note we need two animations since an Animator
+         * Start animating the slider. Note we need two animations since an ValueAnimator
          * keeps internal state of the invalidation region which is just the view being animated.
          *
          * @param anim1
@@ -475,10 +474,13 @@
         int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
         int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
 
-        if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
-            Log.e("SlidingTab", "SlidingTab cannot have UNSPECIFIED MeasureSpec"
-                    +"(wspec=" + widthSpecMode + ", hspec=" + heightSpecMode + ")",
-                    new RuntimeException(LOG_TAG + "stack:"));
+        if (DBG) {
+            if (widthSpecMode == MeasureSpec.UNSPECIFIED 
+                    || heightSpecMode == MeasureSpec.UNSPECIFIED) {
+                Log.e("SlidingTab", "SlidingTab cannot have UNSPECIFIED MeasureSpec"
+                        +"(wspec=" + widthSpecMode + ", hspec=" + heightSpecMode + ")",
+                        new RuntimeException(LOG_TAG + "stack:"));
+            }
         }
 
         mLeftSlider.measure();
@@ -608,14 +610,7 @@
 
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
-                    mTracking = false;
-                    mTriggered = false;
-                    mOtherSlider.show(true);
-                    mCurrentSlider.reset(false);
-                    mCurrentSlider.hideTarget();
-                    mCurrentSlider = null;
-                    mOtherSlider = null;
-                    setGrabbedState(OnTriggerListener.NO_HANDLE);
+                    cancelGrab();
                     break;
             }
         }
@@ -623,6 +618,17 @@
         return mTracking || super.onTouchEvent(event);
     }
 
+    private void cancelGrab() {
+        mTracking = false;
+        mTriggered = false;
+        mOtherSlider.show(true);
+        mCurrentSlider.reset(false);
+        mCurrentSlider.hideTarget();
+        mCurrentSlider = null;
+        mOtherSlider = null;
+        setGrabbedState(OnTriggerListener.NO_HANDLE);
+    }
+
     void startAnimating(final boolean holdAfter) {
         mAnimating = true;
         final Animation trans1;
@@ -832,6 +838,17 @@
         }
     }
 
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        // When visibility changes and the user has a tab selected, unselect it and
+        // make sure their callback gets called.
+        if (changedView == this && visibility != VISIBLE
+                && mGrabbedState != OnTriggerListener.NO_HANDLE) {
+            cancelGrab();
+        }
+    }
+
     /**
      * Sets the current grabbed state, and dispatches a grabbed state change
      * event to our listener.
diff --git a/core/java/com/android/internal/widget/WaveView.java b/core/java/com/android/internal/widget/WaveView.java
new file mode 100644
index 0000000..81633b1
--- /dev/null
+++ b/core/java/com/android/internal/widget/WaveView.java
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import java.util.ArrayList;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Vibrator;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.internal.R;
+
+/**
+ * A special widget containing a center and outer ring. Moving the center ring to the outer ring
+ * causes an event that can be caught by implementing OnTriggerListener.
+ */
+public class WaveView extends View implements ValueAnimator.AnimatorUpdateListener {
+    private static final String TAG = "WaveView";
+    private static final boolean DBG = false;
+    private static final int WAVE_COUNT = 5; // default wave count
+    private static final long VIBRATE_SHORT = 20;  // msec
+    private static final long VIBRATE_LONG = 20;  // msec
+
+    // Lock state machine states
+    private static final int STATE_RESET_LOCK = 0;
+    private static final int STATE_READY = 1;
+    private static final int STATE_START_ATTEMPT = 2;
+    private static final int STATE_ATTEMPTING = 3;
+    private static final int STATE_UNLOCK_ATTEMPT = 4;
+    private static final int STATE_UNLOCK_SUCCESS = 5;
+
+    // Animation properties.
+    private static final long DURATION = 300; // duration of transitional animations
+    private static final long FINAL_DURATION = 200; // duration of final animations when unlocking
+    private static final long RING_DELAY = 1300; // when to start fading animated rings
+    private static final long FINAL_DELAY = 200; // delay for unlock success animation
+    private static final long SHORT_DELAY = 100; // for starting one animation after another.
+    private static final long WAVE_DURATION = 2000; // amount of time for way to expand/decay
+    private static final long RESET_TIMEOUT = 3000; // elapsed time of inactivity before we reset
+    private static final long DELAY_INCREMENT = 15; // increment per wave while tracking motion
+    private static final long DELAY_INCREMENT2 = 12; // increment per wave while not tracking
+
+    private Vibrator mVibrator;
+    private OnTriggerListener mOnTriggerListener;
+    private ArrayList<DrawableHolder> mDrawables = new ArrayList<DrawableHolder>(3);
+    private ArrayList<DrawableHolder> mLightWaves = new ArrayList<DrawableHolder>(WAVE_COUNT);
+    private boolean mFingerDown = false;
+    private float mRingRadius = 182.0f; // Radius of bitmap ring. Used to snap halo to it
+    private int mSnapRadius = 136; // minimum threshold for drag unlock
+    private int mWaveDelay = 240; // time to delay
+    private int mWaveCount = WAVE_COUNT;  // number of waves
+    private long mWaveTimerDelay = mWaveDelay;
+    private int mCurrentWave = 0;
+    private float mLockCenterX; // center of widget as dictated by widget size
+    private float mLockCenterY;
+    private float mMouseX; // current mouse position as of last touch event
+    private float mMouseY;
+    private DrawableHolder mUnlockRing;
+    private DrawableHolder mUnlockDefault;
+    private DrawableHolder mUnlockHalo;
+    private int mLockState = STATE_RESET_LOCK;
+    private int mGrabbedState = OnTriggerListener.NO_HANDLE;
+
+    public WaveView(Context context) {
+        this(context, null);
+    }
+
+    public WaveView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        // TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
+        // mOrientation = a.getInt(R.styleable.WaveView_orientation, HORIZONTAL);
+        // a.recycle();
+
+        initDrawables();
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        mLockCenterX = 0.5f * w;
+        mLockCenterY = 0.5f * h;
+        super.onSizeChanged(w, h, oldw, oldh);
+    }
+
+    @Override
+    protected int getSuggestedMinimumWidth() {
+        // View should be large enough to contain the unlock ring + halo
+        return mUnlockRing.getWidth() + mUnlockHalo.getWidth();
+    }
+
+    @Override
+    protected int getSuggestedMinimumHeight() {
+        // View should be large enough to contain the unlock ring + halo
+        return mUnlockRing.getHeight() + mUnlockHalo.getHeight();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+        int widthSpecSize =  MeasureSpec.getSize(widthMeasureSpec);
+        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
+        int width;
+        int height;
+
+        if (widthSpecMode == MeasureSpec.AT_MOST) {
+            width = Math.min(widthSpecSize, getSuggestedMinimumWidth());
+        } else if (widthSpecMode == MeasureSpec.EXACTLY) {
+            width = widthSpecSize;
+        } else {
+            width = getSuggestedMinimumWidth();
+        }
+
+        if (heightSpecMode == MeasureSpec.AT_MOST) {
+            height = Math.min(heightSpecSize, getSuggestedMinimumWidth());
+        } else if (heightSpecMode == MeasureSpec.EXACTLY) {
+            height = heightSpecSize;
+        } else {
+            height = getSuggestedMinimumHeight();
+        }
+
+        setMeasuredDimension(width, height);
+    }
+
+    private void initDrawables() {
+        mUnlockRing = new DrawableHolder(createDrawable(R.drawable.unlock_ring))
+            .setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f).setAlpha(0.0f);
+        mDrawables.add(mUnlockRing);
+
+        mUnlockDefault = new DrawableHolder(createDrawable(R.drawable.unlock_default))
+            .setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f).setAlpha(0.0f);
+        mDrawables.add(mUnlockDefault);
+
+        mUnlockHalo = new DrawableHolder(createDrawable(R.drawable.unlock_halo))
+            .setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f).setAlpha(0.0f);
+        mDrawables.add(mUnlockHalo);
+
+        BitmapDrawable wave = createDrawable(R.drawable.unlock_wave);
+        for (int i = 0; i < mWaveCount; i++) {
+            DrawableHolder holder = new DrawableHolder(wave);
+            mLightWaves.add(holder);
+            holder.setAlpha(0.0f);
+        }
+    }
+
+    private void waveUpdateFrame(float mouseX, float mouseY, boolean fingerDown) {
+        double distX = mouseX - mLockCenterX;
+        double distY = mouseY - mLockCenterY;
+        int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
+        double touchA = Math.atan2(distX, distY);
+        float ringX = (float) (mLockCenterX + mRingRadius * Math.sin(touchA));
+        float ringY = (float) (mLockCenterY + mRingRadius * Math.cos(touchA));
+
+        switch (mLockState) {
+            case STATE_RESET_LOCK:
+                if (DBG) Log.v(TAG, "State RESET_LOCK");
+                mWaveTimerDelay = mWaveDelay;
+                for (int i = 0; i < mLightWaves.size(); i++) {
+                    //TweenMax.to(mLightWave.get(i), .3, {alpha:0, ease:Quint.easeOut});
+                    DrawableHolder holder = mLightWaves.get(i);
+                    holder.addAnimTo(300, 0, "alpha", 0.0f, false);
+                }
+                for (int i = 0; i < mLightWaves.size(); i++) {
+                    mLightWaves.get(i).startAnimations(this);
+                }
+
+                //TweenMax.to(unlockRing, .5, { x: lockX, y: lockY, scaleX: .1, scaleY: .1,
+                // alpha: 0, overwrite: true, ease:Quint.easeOut   });
+                mUnlockRing.addAnimTo(DURATION, 0, "x", mLockCenterX, true);
+                mUnlockRing.addAnimTo(DURATION, 0, "y", mLockCenterY, true);
+                mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 0.1f, true);
+                mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 0.1f, true);
+                mUnlockRing.addAnimTo(DURATION, 0, "alpha", 0.0f, true);
+
+                //TweenMax.to(unlockDefault, 0, { x: lockX, y: lockY, scaleX: .1, scaleY: .1,
+                // alpha: 0  , overwrite: true                       });
+                mUnlockDefault.removeAnimationFor("x");
+                mUnlockDefault.removeAnimationFor("y");
+                mUnlockDefault.removeAnimationFor("scaleX");
+                mUnlockDefault.removeAnimationFor("scaleY");
+                mUnlockDefault.removeAnimationFor("alpha");
+                mUnlockDefault.setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f)
+                        .setAlpha(0.0f);
+
+                //TweenMax.to(unlockDefault, .5, { delay: .1, scaleX: 1, scaleY: 1,
+                // alpha: 1, overwrite: true, ease:Quint.easeOut   });
+                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true);
+                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true);
+                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true);
+
+                //TweenMax.to(unlockHalo, 0, { x: lockX, y: lockY, scaleX:.1, scaleY: .1,
+                // alpha: 0  , overwrite: true                       });
+                mUnlockHalo.removeAnimationFor("x");
+                mUnlockHalo.removeAnimationFor("y");
+                mUnlockHalo.removeAnimationFor("scaleX");
+                mUnlockHalo.removeAnimationFor("scaleY");
+                mUnlockHalo.removeAnimationFor("alpha");
+                mUnlockHalo.setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f)
+                        .setAlpha(0.0f);
+
+                //TweenMax.to(unlockHalo, .5, { x: lockX, y: lockY, scaleX: 1, scaleY: 1,
+                // alpha: 1  , overwrite: true, ease:Quint.easeOut   });
+                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "x", mLockCenterX, true);
+                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "y", mLockCenterY, true);
+                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true);
+                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true);
+                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true);
+
+                //lockTimer.stop();
+                removeCallbacks(mLockTimerActions);
+
+                mLockState = STATE_READY;
+                break;
+
+            case STATE_READY:
+                if (DBG) Log.v(TAG, "State READY");
+                break;
+
+            case STATE_START_ATTEMPT:
+                if (DBG) Log.v(TAG, "State START_ATTEMPT");
+                //TweenMax.to(unlockDefault, 0, {scaleX: .1, scaleY:.1, alpha: 0,
+                // x:lockX +182, y: lockY  , overwrite: true   });
+                mUnlockDefault.removeAnimationFor("x");
+                mUnlockDefault.removeAnimationFor("y");
+                mUnlockDefault.removeAnimationFor("scaleX");
+                mUnlockDefault.removeAnimationFor("scaleY");
+                mUnlockDefault.removeAnimationFor("alpha");
+                mUnlockDefault.setX(mLockCenterX + 182).setY(mLockCenterY).setScaleX(0.1f)
+                        .setScaleY(0.1f).setAlpha(0.0f);
+
+                //TweenMax.to(unlockDefault, 0.5, { delay: .1   , scaleX: 1, scaleY: 1,
+                // alpha: 1, ease:Quint.easeOut                        });
+                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, false);
+                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, false);
+                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, false);
+
+                //TweenMax.to(unlockRing, 0.5, {scaleX: 1, scaleY: 1,
+                // alpha: 1, ease:Quint.easeOut, overwrite: true   });
+                mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 1.0f, true);
+                mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 1.0f, true);
+                mUnlockRing.addAnimTo(DURATION, 0, "alpha", 1.0f, true);
+
+                postDelayed(mAddWaveAction, mWaveTimerDelay);
+
+                mLockState = STATE_ATTEMPTING;
+                break;
+
+            case STATE_ATTEMPTING:
+                if (DBG) Log.v(TAG, "State ATTEMPTING (fingerDown = " + fingerDown + ")");
+                //TweenMax.to(unlockHalo, 0.4, { x:mouseX, y:mouseY, scaleX:1, scaleY:1,
+                // alpha: 1, ease:Quint.easeOut });
+                if (dragDistance > mSnapRadius) {
+                    if (fingerDown) {
+                        //TweenMax.to(unlockHalo, 0.4, {x:ringX, y:ringY, scaleX:1, scaleY:1,
+                        // alpha: 1 , ease:Quint.easeOut    , overwrite: true });
+                        mUnlockHalo.addAnimTo(0, 0, "x", ringX, true);
+                        mUnlockHalo.addAnimTo(0, 0, "y", ringY, true);
+                        mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true);
+                        mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true);
+                        mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true);
+                    }  else {
+                        if (DBG) Log.v(TAG, "up detected, moving to STATE_UNLOCK_ATTEMPT");
+                        mLockState = STATE_UNLOCK_ATTEMPT;
+                    }
+                } else {
+                    mUnlockHalo.addAnimTo(0, 0, "x", mouseX, true);
+                    mUnlockHalo.addAnimTo(0, 0, "y", mouseY, true);
+                    mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true);
+                    mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true);
+                    mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true);
+                }
+                break;
+
+            case STATE_UNLOCK_ATTEMPT:
+                if (DBG) Log.v(TAG, "State UNLOCK_ATTEMPT");
+                if (dragDistance > mSnapRadius) {
+                    for (int n = 0; n < mLightWaves.size(); n++) {
+                        //TweenMax.to(this["lightWave"+n], .5,{alpha:0, delay: (6+n-currentWave)*.1,
+                        // x:ringX, y:ringY, scaleX: .1, scaleY: .1, ease:Quint.easeOut});
+                        DrawableHolder wave = mLightWaves.get(n);
+                        long delay = 1000L*(6 + n - mCurrentWave)/10L;
+                        wave.addAnimTo(FINAL_DURATION, delay, "x", ringX, true);
+                        wave.addAnimTo(FINAL_DURATION, delay, "y", ringY, true);
+                        wave.addAnimTo(FINAL_DURATION, delay, "scaleX", 0.1f, true);
+                        wave.addAnimTo(FINAL_DURATION, delay, "scaleY", 0.1f, true);
+                        wave.addAnimTo(FINAL_DURATION, delay, "alpha", 0.0f, true);
+                    }
+                    for (int i = 0; i < mLightWaves.size(); i++) {
+                        mLightWaves.get(i).startAnimations(this);
+                    }
+
+                    //TweenMax.to(unlockRing, .5, {x:ringX, y: ringY, scaleX: .1, scaleY: .1,
+                    // alpha: 0, ease: Quint.easeOut   });
+                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "x", ringX, false);
+                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "y", ringY, false);
+                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "scaleX", 0.1f, false);
+                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "scaleY", 0.1f, false);
+                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "alpha", 0.0f, false);
+
+                    //TweenMax.to(unlockRing, .5, { delay: 1.3, alpha: 0  , ease: Quint.easeOut });
+                    mUnlockRing.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
+
+                    //TweenMax.to(unlockDefault, 0, { x:ringX, y: ringY, scaleX: .1, scaleY: .1,
+                    // alpha: 0  , overwrite: true });
+                    mUnlockDefault.removeAnimationFor("x");
+                    mUnlockDefault.removeAnimationFor("y");
+                    mUnlockDefault.removeAnimationFor("scaleX");
+                    mUnlockDefault.removeAnimationFor("scaleY");
+                    mUnlockDefault.removeAnimationFor("alpha");
+                    mUnlockDefault.setX(ringX).setY(ringY).setScaleX(0.1f).setScaleY(0.1f)
+                            .setAlpha(0.0f);
+
+                    //TweenMax.to(unlockDefault, .5, { x:ringX, y: ringY, scaleX: 1, scaleY: 1,
+                    // alpha: 1  , ease: Quint.easeOut  , overwrite: true });
+                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "x", ringX, true);
+                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "y", ringY, true);
+                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "scaleX", 1.0f, true);
+                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "scaleY", 1.0f, true);
+                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "alpha", 1.0f, true);
+
+                    //TweenMax.to(unlockDefault, .5, { delay: 1.3, scaleX: 3, scaleY: 3,
+                    // alpha: 1, ease: Quint.easeOut });
+                    mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleX", 3.0f, false);
+                    mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleY", 3.0f, false);
+                    mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
+
+                    //TweenMax.to(unlockHalo, .5, { x:ringX, y: ringY , ease: Back.easeOut    });
+                    mUnlockHalo.addAnimTo(FINAL_DURATION, 0, "x", ringX, false);
+                    mUnlockHalo.addAnimTo(FINAL_DURATION, 0, "y", ringY, false);
+
+                    //TweenMax.to(unlockHalo, .5, { delay: 1.3, scaleX: 3, scaleY: 3,
+                    // alpha: 1, ease: Quint.easeOut   });
+                    mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleX", 3.0f, false);
+                    mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleY", 3.0f, false);
+                    mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
+
+                    removeCallbacks(mLockTimerActions);
+
+                    postDelayed(mLockTimerActions, RESET_TIMEOUT);
+
+                    dispatchTriggerEvent(OnTriggerListener.CENTER_HANDLE);
+                    mLockState = STATE_UNLOCK_SUCCESS;
+                } else {
+                    mLockState = STATE_RESET_LOCK;
+                }
+                break;
+
+            case STATE_UNLOCK_SUCCESS:
+                if (DBG) Log.v(TAG, "State UNLOCK_SUCCESS");
+                removeCallbacks(mAddWaveAction);
+                break;
+
+            default:
+                if (DBG) Log.v(TAG, "Unknown state " + mLockState);
+                break;
+        }
+        mUnlockDefault.startAnimations(this);
+        mUnlockHalo.startAnimations(this);
+        mUnlockRing.startAnimations(this);
+    }
+
+    BitmapDrawable createDrawable(int resId) {
+        Resources res = getResources();
+        Bitmap bitmap = BitmapFactory.decodeResource(res, resId);
+        return new BitmapDrawable(res, bitmap);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        waveUpdateFrame(mMouseX, mMouseY, mFingerDown);
+        for (int i = 0; i < mDrawables.size(); ++i) {
+            mDrawables.get(i).draw(canvas);
+        }
+        for (int i = 0; i < mLightWaves.size(); ++i) {
+            mLightWaves.get(i).draw(canvas);
+        }
+    }
+
+    private final Runnable mLockTimerActions = new Runnable() {
+        public void run() {
+            if (DBG) Log.v(TAG, "LockTimerActions");
+            // reset lock after inactivity
+            if (mLockState == STATE_ATTEMPTING) {
+                mLockState = STATE_RESET_LOCK;
+            }
+            // for prototype, reset after successful unlock
+            if (mLockState == STATE_UNLOCK_SUCCESS) {
+                mLockState = STATE_RESET_LOCK;
+            }
+            invalidate();
+        }
+    };
+
+    private final Runnable mAddWaveAction = new Runnable() {
+        public void run() {
+            double distX = mMouseX - mLockCenterX;
+            double distY = mMouseY - mLockCenterY;
+            int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
+            if (mLockState == STATE_ATTEMPTING && dragDistance < mSnapRadius
+                    && mWaveTimerDelay >= mWaveDelay) {
+                mWaveTimerDelay = Math.min(WAVE_DURATION, mWaveTimerDelay + DELAY_INCREMENT);
+
+                DrawableHolder wave = mLightWaves.get(mCurrentWave);
+                wave.setAlpha(0.0f);
+                wave.setScaleX(0.2f);
+                wave.setScaleY(0.2f);
+                wave.setX(mMouseX);
+                wave.setY(mMouseY);
+
+                //TweenMax.to(this["lightWave"+currentWave], 2, { x:lockX , y:lockY, alpha: 1.5,
+                // scaleX: 1, scaleY:1, ease:Cubic.easeOut});
+                wave.addAnimTo(WAVE_DURATION, 0, "x", mLockCenterX, true);
+                wave.addAnimTo(WAVE_DURATION, 0, "y", mLockCenterY, true);
+                wave.addAnimTo(WAVE_DURATION*2/3, 0, "alpha", 1.0f, true);
+                wave.addAnimTo(WAVE_DURATION, 0, "scaleX", 1.0f, true);
+                wave.addAnimTo(WAVE_DURATION, 0, "scaleY", 1.0f, true);
+
+                //TweenMax.to(this["lightWave"+currentWave], 1, { delay: 1.3
+                // , alpha: 0  , ease:Quint.easeOut});
+                wave.addAnimTo(1000, RING_DELAY, "alpha", 0.0f, false);
+                wave.startAnimations(WaveView.this);
+
+                mCurrentWave = (mCurrentWave+1) % mWaveCount;
+                if (DBG) Log.v(TAG, "WaveTimerDelay: start new wave in " + mWaveTimerDelay);
+                postDelayed(mAddWaveAction, mWaveTimerDelay);
+            } else {
+                mWaveTimerDelay += DELAY_INCREMENT2;
+            }
+        }
+    };
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        final int action = event.getAction();
+        mMouseX = event.getX();
+        mMouseY = event.getY();
+        boolean handled = false;
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                removeCallbacks(mLockTimerActions);
+                mFingerDown = true;
+                setGrabbedState(OnTriggerListener.CENTER_HANDLE);
+                {
+                    float x = mMouseX - mUnlockHalo.getX();
+                    float y = mMouseY - mUnlockHalo.getY();
+                    float dist = (float) Math.hypot(x, y);
+                    if (dist < mUnlockHalo.getWidth()*0.5f) {
+                        if (mLockState == STATE_READY) {
+                            mLockState = STATE_START_ATTEMPT;
+                        }
+                    }
+                }
+                handled = true;
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                handled = true;
+                break;
+
+            case MotionEvent.ACTION_UP:
+                if (DBG) Log.v(TAG, "ACTION_UP");
+                mFingerDown = false;
+                postDelayed(mLockTimerActions, RESET_TIMEOUT);
+                setGrabbedState(OnTriggerListener.NO_HANDLE);
+                // Normally the state machine is driven by user interaction causing redraws.
+                // However, when there's no more user interaction and no running animations,
+                // the state machine stops advancing because onDraw() never gets called.
+                // The following ensures we advance to the next state in this case,
+                // either STATE_UNLOCK_ATTEMPT or STATE_RESET_LOCK.
+                waveUpdateFrame(mMouseX, mMouseY, mFingerDown);
+                handled = true;
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+                mFingerDown = false;
+                handled = true;
+                break;
+        }
+        invalidate();
+        return handled ? true : super.onTouchEvent(event);
+    }
+
+    /**
+     * Triggers haptic feedback.
+     */
+    private synchronized void vibrate(long duration) {
+        if (mVibrator == null) {
+            mVibrator = (android.os.Vibrator)
+                    getContext().getSystemService(Context.VIBRATOR_SERVICE);
+        }
+        mVibrator.vibrate(duration);
+    }
+
+    /**
+     * Registers a callback to be invoked when the user triggers an event.
+     *
+     * @param listener the OnDialTriggerListener to attach to this view
+     */
+    public void setOnTriggerListener(OnTriggerListener listener) {
+        mOnTriggerListener = listener;
+    }
+
+    /**
+     * Dispatches a trigger event to listener. Ignored if a listener is not set.
+     * @param whichHandle the handle that triggered the event.
+     */
+    private void dispatchTriggerEvent(int whichHandle) {
+        vibrate(VIBRATE_LONG);
+        if (mOnTriggerListener != null) {
+            mOnTriggerListener.onTrigger(this, whichHandle);
+        }
+    }
+
+    /**
+     * Sets the current grabbed state, and dispatches a grabbed state change
+     * event to our listener.
+     */
+    private void setGrabbedState(int newState) {
+        if (newState != mGrabbedState) {
+            mGrabbedState = newState;
+            if (mOnTriggerListener != null) {
+                mOnTriggerListener.onGrabbedStateChange(this, mGrabbedState);
+            }
+        }
+    }
+
+    public interface OnTriggerListener {
+        /**
+         * Sent when the user releases the handle.
+         */
+        public static final int NO_HANDLE = 0;
+
+        /**
+         * Sent when the user grabs the center handle
+         */
+        public static final int CENTER_HANDLE = 10;
+
+        /**
+         * Called when the user drags the center ring beyond a threshold.
+         */
+        void onTrigger(View v, int whichHandle);
+
+        /**
+         * Called when the "grabbed state" changes (i.e. when the user either grabs or releases
+         * one of the handles.)
+         *
+         * @param v the view that was triggered
+         * @param grabbedState the new state: {@link #NO_HANDLE}, {@link #CENTER_HANDLE},
+         */
+        void onGrabbedStateChange(View v, int grabbedState);
+    }
+
+    public void onAnimationUpdate(ValueAnimator animation) {
+        invalidate();
+    }
+
+    public void reset() {
+        mLockState = STATE_RESET_LOCK;
+    }
+}
diff --git a/core/java/com/android/internal/widget/WeightedLinearLayout.java b/core/java/com/android/internal/widget/WeightedLinearLayout.java
index 3d09f08..385a7c3 100644
--- a/core/java/com/android/internal/widget/WeightedLinearLayout.java
+++ b/core/java/com/android/internal/widget/WeightedLinearLayout.java
@@ -30,8 +30,10 @@
  * the available space.
  */
 public class WeightedLinearLayout extends LinearLayout {
-    private float mMajorWeight;
-    private float mMinorWeight;
+    private float mMajorWeightMin;
+    private float mMinorWeightMin;
+    private float mMajorWeightMax;
+    private float mMinorWeightMax;
 
     public WeightedLinearLayout(Context context) {
         super(context);
@@ -43,8 +45,10 @@
         TypedArray a = 
             context.obtainStyledAttributes(attrs, styleable.WeightedLinearLayout);
 
-        mMajorWeight = a.getFloat(styleable.WeightedLinearLayout_majorWeight, 0.0f);
-        mMinorWeight = a.getFloat(styleable.WeightedLinearLayout_minorWeight, 0.0f);
+        mMajorWeightMin = a.getFloat(styleable.WeightedLinearLayout_majorWeightMin, 0.0f);
+        mMinorWeightMin = a.getFloat(styleable.WeightedLinearLayout_minorWeightMin, 0.0f);
+        mMajorWeightMax = a.getFloat(styleable.WeightedLinearLayout_majorWeightMax, 0.0f);
+        mMinorWeightMax = a.getFloat(styleable.WeightedLinearLayout_minorWeightMax, 0.0f);
         
         a.recycle();
     }
@@ -60,17 +64,20 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
         int width = getMeasuredWidth();
-        int height = getMeasuredHeight();
         boolean measure = false;
 
         widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
-        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, EXACTLY);
 
-        final float widthWeight = isPortrait ? mMinorWeight : mMajorWeight;
-        if (widthMode == AT_MOST && widthWeight > 0.0f) {
-            if (width < (screenWidth * widthWeight)) {
-                widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (screenWidth * widthWeight),
-                        EXACTLY);
+        final float widthWeightMin = isPortrait ? mMinorWeightMin : mMajorWeightMin;
+        final float widthWeightMax = isPortrait ? mMinorWeightMax : mMajorWeightMax;
+        if (widthMode == AT_MOST) {
+            final int weightedMin = (int) (screenWidth * widthWeightMin);
+            final int weightedMax = (int) (screenWidth * widthWeightMin);
+            if (widthWeightMin > 0.0f && width < weightedMin) {
+                widthMeasureSpec = MeasureSpec.makeMeasureSpec(weightedMin, EXACTLY);
+                measure = true;
+            } else if (widthWeightMax > 0.0f && width > weightedMax) {
+                widthMeasureSpec = MeasureSpec.makeMeasureSpec(weightedMax, EXACTLY);
                 measure = true;
             }
         }
@@ -78,7 +85,7 @@
         // TODO: Support height?
 
         if (measure) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);            
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
     }
 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index c3f393d..bcd7bae 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -19,12 +19,15 @@
   LOCAL_CFLAGS += -DCUSTOM_RUNTIME_HEAP_MAX=$(USE_CUSTOM_RUNTIME_HEAP_MAX)
 endif
 
+ifeq ($(USE_OPENGL_RENDERER),true)
+	LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER
+endif
+
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 
 LOCAL_SRC_FILES:= \
 	ActivityManager.cpp \
 	AndroidRuntime.cpp \
-	CursorWindow.cpp \
 	Time.cpp \
 	com_google_android_gles_jni_EGLImpl.cpp \
 	com_google_android_gles_jni_GLImpl.cpp.arm \
@@ -48,6 +51,7 @@
 	android_view_InputChannel.cpp \
 	android_view_InputQueue.cpp \
 	android_view_KeyEvent.cpp \
+	android_view_GLES20Canvas.cpp \
 	android_view_MotionEvent.cpp \
 	android_text_AndroidCharacter.cpp \
 	android_text_AndroidBidi.cpp \
@@ -103,9 +107,11 @@
 	android_graphics_PixelFormat.cpp \
 	android/graphics/Picture.cpp \
 	android/graphics/PorterDuff.cpp \
+	android/graphics/BitmapRegionDecoder.cpp \
 	android/graphics/Rasterizer.cpp \
 	android/graphics/Region.cpp \
 	android/graphics/Shader.cpp \
+	android/graphics/TextLayout.cpp \
 	android/graphics/Typeface.cpp \
 	android/graphics/Utils.cpp \
 	android/graphics/Xfermode.cpp \
@@ -125,7 +131,6 @@
 	android_bluetooth_common.cpp \
 	android_bluetooth_BluetoothAudioGateway.cpp \
 	android_bluetooth_BluetoothSocket.cpp \
-	android_bluetooth_ScoSocket.cpp \
 	android_server_BluetoothService.cpp \
 	android_server_BluetoothEventLoop.cpp \
 	android_server_BluetoothA2dpService.cpp \
@@ -139,11 +144,13 @@
 	android_backup_FileBackupHelperBase.cpp \
 	android_backup_BackupHelperDispatcher.cpp \
 	android_content_res_ObbScanner.cpp \
-    android_content_res_Configuration.cpp
+	android_content_res_Configuration.cpp \
+    android_animation_PropertyValuesHolder.cpp
 
 LOCAL_C_INCLUDES += \
 	$(JNI_H_INCLUDE) \
 	$(LOCAL_PATH)/android/graphics \
+	$(LOCAL_PATH)/../../libs/hwui \
 	$(call include-path-for, bluedroid) \
 	$(call include-path-for, libhardware)/hardware \
 	$(call include-path-for, libhardware_legacy)/hardware_legacy \
@@ -195,6 +202,10 @@
 	libjpeg \
 	libnfc_ndef
 
+ifeq ($(USE_OPENGL_RENDERER),true)
+	LOCAL_SHARED_LIBRARIES += libhwui
+endif
+
 ifeq ($(BOARD_HAVE_BLUETOOTH),true)
 LOCAL_C_INCLUDES += \
 	external/dbus \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 648d93f..1018ddb 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -53,6 +53,7 @@
 extern int register_android_os_Process(JNIEnv* env);
 extern int register_android_graphics_Bitmap(JNIEnv*);
 extern int register_android_graphics_BitmapFactory(JNIEnv*);
+extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
 extern int register_android_graphics_Camera(JNIEnv* env);
 extern int register_android_graphics_Graphics(JNIEnv* env);
 extern int register_android_graphics_Interpolator(JNIEnv* env);
@@ -114,6 +115,7 @@
 extern int register_android_graphics_PixelFormat(JNIEnv* env);
 extern int register_com_android_internal_graphics_NativeUtils(JNIEnv *env);
 extern int register_android_view_Display(JNIEnv* env);
+extern int register_android_view_GLES20Canvas(JNIEnv* env);
 extern int register_android_view_Surface(JNIEnv* env);
 extern int register_android_view_ViewRoot(JNIEnv* env);
 extern int register_android_database_CursorWindow(JNIEnv* env);
@@ -152,7 +154,6 @@
 extern int register_android_bluetooth_HeadsetBase(JNIEnv* env);
 extern int register_android_bluetooth_BluetoothAudioGateway(JNIEnv* env);
 extern int register_android_bluetooth_BluetoothSocket(JNIEnv *env);
-extern int register_android_bluetooth_ScoSocket(JNIEnv *env);
 extern int register_android_server_BluetoothService(JNIEnv* env);
 extern int register_android_server_BluetoothEventLoop(JNIEnv *env);
 extern int register_android_server_BluetoothA2dpService(JNIEnv* env);
@@ -170,6 +171,7 @@
 extern int register_android_view_MotionEvent(JNIEnv* env);
 extern int register_android_content_res_ObbScanner(JNIEnv* env);
 extern int register_android_content_res_Configuration(JNIEnv* env);
+extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
 
 static AndroidRuntime* gCurRuntime = NULL;
 
@@ -1197,6 +1199,7 @@
     REG_JNI(register_android_nio_utils),
     REG_JNI(register_android_graphics_PixelFormat),
     REG_JNI(register_android_graphics_Graphics),
+    REG_JNI(register_android_view_GLES20Canvas),
     REG_JNI(register_android_view_Surface),
     REG_JNI(register_android_view_ViewRoot),
     REG_JNI(register_com_google_android_gles_jni_EGLImpl),
@@ -1209,6 +1212,7 @@
 
     REG_JNI(register_android_graphics_Bitmap),
     REG_JNI(register_android_graphics_BitmapFactory),
+    REG_JNI(register_android_graphics_BitmapRegionDecoder),
     REG_JNI(register_android_graphics_Camera),
     REG_JNI(register_android_graphics_Canvas),
     REG_JNI(register_android_graphics_ColorFilter),
@@ -1269,7 +1273,6 @@
     REG_JNI(register_android_bluetooth_HeadsetBase),
     REG_JNI(register_android_bluetooth_BluetoothAudioGateway),
     REG_JNI(register_android_bluetooth_BluetoothSocket),
-    REG_JNI(register_android_bluetooth_ScoSocket),
     REG_JNI(register_android_server_BluetoothService),
     REG_JNI(register_android_server_BluetoothEventLoop),
     REG_JNI(register_android_server_BluetoothA2dpService),
@@ -1289,6 +1292,8 @@
 
     REG_JNI(register_android_content_res_ObbScanner),
     REG_JNI(register_android_content_res_Configuration),
+
+    REG_JNI(register_android_animation_PropertyValuesHolder),
 };
 
 /*
diff --git a/core/jni/CursorWindow.cpp b/core/jni/CursorWindow.cpp
deleted file mode 100644
index 7877921..0000000
--- a/core/jni/CursorWindow.cpp
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * Copyright (C) 2006-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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "CursorWindow"
-
-#include <utils/Log.h>
-#include <binder/MemoryHeapBase.h>
-#include <binder/MemoryBase.h>
-
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include <jni.h>
-#include <JNIHelp.h>
-
-#include "CursorWindow.h"
-
-
-namespace android {
-
-CursorWindow::CursorWindow(size_t maxSize) :
-    mMaxSize(maxSize)
-{
-}
-
-bool CursorWindow::setMemory(const sp<IMemory>& memory)
-{
-    mMemory = memory;
-    mData = (uint8_t *) memory->pointer();
-    if (mData == NULL) {
-        return false;
-    }
-    mHeader = (window_header_t *) mData;
-
-    // Make the window read-only
-    ssize_t size = memory->size();
-    mSize = size;
-    mMaxSize = size;
-    mFreeOffset = size;
-LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData);
-    return true;
-}
-
-bool CursorWindow::initBuffer(bool localOnly)
-{
-    //TODO Use a non-memory dealer mmap region for localOnly
-
-    sp<MemoryHeapBase> heap;
-    heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow");
-    if (heap != NULL) {
-        mMemory = new MemoryBase(heap, 0, mMaxSize);
-        if (mMemory != NULL) {
-            mData = (uint8_t *) mMemory->pointer();
-            if (mData) {
-                mHeader = (window_header_t *) mData;
-                mSize = mMaxSize;
-
-                // Put the window into a clean state
-                clear();
-            LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData);
-                return true;                
-            }
-        } 
-        LOGE("CursorWindow heap allocation failed");
-        return false;
-    } else {
-        LOGE("failed to create the CursorWindow heap");
-        return false;
-    }
-}
-
-CursorWindow::~CursorWindow()
-{
-    // Everything that matters is a smart pointer
-}
-
-void CursorWindow::clear()
-{
-    mHeader->numRows = 0;
-    mHeader->numColumns = 0;
-    mFreeOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE;
-    // Mark the first chunk's next 'pointer' as null
-    *((uint32_t *)(mData + mFreeOffset - sizeof(uint32_t))) = 0;
-}
-
-int32_t CursorWindow::freeSpace()
-{
-    int32_t freeSpace = mSize - mFreeOffset;
-    if (freeSpace < 0) {
-        freeSpace = 0;
-    }
-    return freeSpace;
-}
-
-field_slot_t * CursorWindow::allocRow()
-{
-    // Fill in the row slot
-    row_slot_t * rowSlot = allocRowSlot();
-    if (rowSlot == NULL) {
-        return NULL;
-    }
-
-    // Allocate the slots for the field directory
-    size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t);
-    uint32_t fieldDirOffset = alloc(fieldDirSize);
-    if (!fieldDirOffset) {
-        mHeader->numRows--;
-        LOGE("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows);
-        return NULL;
-    }
-    field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset);
-    memset(fieldDir, 0x0, fieldDirSize);
-
-LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", (mHeader->numRows - 1), ((uint8_t *)rowSlot) - mData, fieldDirSize, fieldDirOffset);
-    rowSlot->offset = fieldDirOffset;
-
-    return fieldDir;
-}
-
-uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned)
-{
-    int32_t size;
-    uint32_t padding;
-    if (aligned) {
-        // 4 byte alignment
-        padding = 4 - (mFreeOffset & 0x3);
-    } else {
-        padding = 0;
-    }
-
-    size = requestedSize + padding;
-
-    if (size > freeSpace()) {
-        LOGE("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d", mSize, size, freeSpace(), mHeader->numRows);
-        // Only grow the window if the first row doesn't fit
-        if (mHeader->numRows > 1) {
-LOGE("not growing since there are already %d row(s), max size %d", mHeader->numRows, mMaxSize);
-            return 0;
-        }
-
-        // Find a new size that will fit the allocation
-        int allocated = mSize - freeSpace();
-        int newSize = mSize + WINDOW_ALLOCATION_SIZE;
-        while (size > (newSize - allocated)) {
-            newSize += WINDOW_ALLOCATION_SIZE;
-            if (newSize > mMaxSize) {
-                LOGE("Attempting to grow window beyond max size (%d)", mMaxSize);
-                return 0;
-            }
-        }
-LOG_WINDOW("found size %d", newSize);
-        mSize = newSize;
-    }
-
-    uint32_t offset = mFreeOffset + padding;
-    mFreeOffset += size;
-    return offset;
-}
-
-row_slot_t * CursorWindow::getRowSlot(int row)
-{
-    LOG_WINDOW("enter getRowSlot current row num %d, this row %d", mHeader->numRows, row);
-    int chunkNum = row / ROW_SLOT_CHUNK_NUM_ROWS;
-    int chunkPos = row % ROW_SLOT_CHUNK_NUM_ROWS;
-    int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
-    uint8_t * rowChunk = mData + sizeof(window_header_t);
-    for (int i = 0; i < chunkNum; i++) {
-        rowChunk = offsetToPtr(*((uint32_t *)(mData + chunkPtrOffset)));
-        chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
-    }
-    return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
-    LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row);    
-}
-
-row_slot_t * CursorWindow::allocRowSlot()
-{
-    int chunkNum = mHeader->numRows / ROW_SLOT_CHUNK_NUM_ROWS;
-    int chunkPos = mHeader->numRows % ROW_SLOT_CHUNK_NUM_ROWS;
-    int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
-    uint8_t * rowChunk = mData + sizeof(window_header_t);
-LOG_WINDOW("Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d", mHeader->numRows, chunkNum, chunkPos);
-    for (int i = 0; i < chunkNum; i++) {
-        uint32_t nextChunkOffset = *((uint32_t *)(mData + chunkPtrOffset));
-LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset);
-        if (nextChunkOffset == 0) {
-            // Allocate a new row chunk
-            nextChunkOffset = alloc(ROW_SLOT_CHUNK_SIZE, true);
-            if (nextChunkOffset == 0) {
-                return NULL;
-            }
-            rowChunk = offsetToPtr(nextChunkOffset);
-LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk);
-            *((uint32_t *)(mData + chunkPtrOffset)) = rowChunk - mData;
-            // Mark the new chunk's next 'pointer' as null
-            *((uint32_t *)(rowChunk + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t))) = 0;
-        } else {
-LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset);
-            rowChunk = offsetToPtr(nextChunkOffset);
-            chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
-        }
-    }
-    mHeader->numRows++;
-
-    return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
-}
-
-field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column)
-{
-  if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
-      LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
-      return NULL;
-  }        
-  row_slot_t * rowSlot = getRowSlot(row);
-  if (!rowSlot) {
-      LOGE("Failed to find rowSlot for row %d", row);
-      return NULL;
-  }
-  if (rowSlot->offset == 0 || rowSlot->offset >= mSize) {
-      LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
-      return NULL;
-  }  
-  int fieldDirOffset = rowSlot->offset;
-  return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;  
-}
-
-uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotOut)
-{
-    if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
-        LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
-        return -1;
-    }        
-    row_slot_t * rowSlot = getRowSlot(row);
-    if (!rowSlot) {
-        LOGE("Failed to find rowSlot for row %d", row);
-        return -1;
-    }
-    if (rowSlot->offset == 0 || rowSlot->offset >= mSize) {
-        LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
-        return -1;
-    }
-LOG_WINDOW("Found field directory for %d,%d at rowSlot %d, offset %d", row, column, (uint8_t *)rowSlot - mData, rowSlot->offset);
-    field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(rowSlot->offset);
-LOG_WINDOW("Read field_slot_t %d,%d: offset = %d, size = %d, type = %d", row, column, fieldDir[column].data.buffer.offset, fieldDir[column].data.buffer.size, fieldDir[column].type);
-
-    // Copy the data to the out param
-    slotOut->data.buffer.offset = fieldDir[column].data.buffer.offset;
-    slotOut->data.buffer.size = fieldDir[column].data.buffer.size;
-    slotOut->type = fieldDir[column].type;
-    return 0;
-}
-
-void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size)
-{
-    assert(offset + size <= mSize);    
-    memcpy(mData + offset, data, size);
-}
-
-void CursorWindow::copyIn(uint32_t offset, int64_t data)
-{
-    assert(offset + sizeof(int64_t) <= mSize);
-    memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t));
-}
-
-void CursorWindow::copyIn(uint32_t offset, double data)
-{
-    assert(offset + sizeof(double) <= mSize);
-    memcpy(mData + offset, (uint8_t *)&data, sizeof(double));
-}
-
-void CursorWindow::copyOut(uint32_t offset, uint8_t * data, size_t size)
-{
-    assert(offset + size <= mSize);
-    memcpy(data, mData + offset, size);
-}
-
-int64_t CursorWindow::copyOutLong(uint32_t offset)
-{
-    int64_t value;
-    assert(offset + sizeof(int64_t) <= mSize);
-    memcpy(&value, mData + offset, sizeof(int64_t));
-    return value;
-}
-
-double CursorWindow::copyOutDouble(uint32_t offset)
-{
-    double value;
-    assert(offset + sizeof(double) <= mSize);
-    memcpy(&value, mData + offset, sizeof(double));
-    return value;
-}
-
-bool CursorWindow::putLong(unsigned int row, unsigned int col, int64_t value)
-{
-    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
-    if (!fieldSlot) {
-        return false;
-    }
-
-#if WINDOW_STORAGE_INLINE_NUMERICS
-    fieldSlot->data.l = value;
-#else
-    int offset = alloc(sizeof(int64_t));
-    if (!offset) {
-        return false;
-    }
-
-    copyIn(offset, value);
-
-    fieldSlot->data.buffer.offset = offset;
-    fieldSlot->data.buffer.size = sizeof(int64_t);
-#endif
-    fieldSlot->type = FIELD_TYPE_INTEGER;
-    return true;
-}
-
-bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value)
-{
-    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
-    if (!fieldSlot) {
-        return false;
-    }
-
-#if WINDOW_STORAGE_INLINE_NUMERICS
-    fieldSlot->data.d = value;
-#else
-    int offset = alloc(sizeof(int64_t));
-    if (!offset) {
-        return false;
-    }
-
-    copyIn(offset, value);
-
-    fieldSlot->data.buffer.offset = offset;
-    fieldSlot->data.buffer.size = sizeof(double);
-#endif
-    fieldSlot->type = FIELD_TYPE_FLOAT;
-    return true;
-}
-
-bool CursorWindow::putNull(unsigned int row, unsigned int col)
-{
-    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
-    if (!fieldSlot) {
-        return false;
-    }
-
-    fieldSlot->type = FIELD_TYPE_NULL;
-    fieldSlot->data.buffer.offset = 0;
-    fieldSlot->data.buffer.size = 0;
-    return true;
-}
-
-bool CursorWindow::getLong(unsigned int row, unsigned int col, int64_t * valueOut)
-{
-    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
-    if (!fieldSlot || fieldSlot->type != FIELD_TYPE_INTEGER) {
-        return false;
-    }
-    
-#if WINDOW_STORAGE_INLINE_NUMERICS
-    *valueOut = fieldSlot->data.l;
-#else
-    *valueOut = copyOutLong(fieldSlot->data.buffer.offset);
-#endif
-    return true;
-}
-
-bool CursorWindow::getDouble(unsigned int row, unsigned int col, double * valueOut)
-{
-    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
-    if (!fieldSlot || fieldSlot->type != FIELD_TYPE_FLOAT) {
-        return false;
-    }
-
-#if WINDOW_STORAGE_INLINE_NUMERICS
-    *valueOut = fieldSlot->data.d;
-#else
-    *valueOut = copyOutDouble(fieldSlot->data.buffer.offset);
-#endif
-    return true;
-}
-
-bool CursorWindow::getNull(unsigned int row, unsigned int col, bool * valueOut)
-{
-    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
-    if (!fieldSlot) {
-        return false;
-    }
-    
-    if (fieldSlot->type != FIELD_TYPE_NULL) {
-        *valueOut = false;
-    } else {
-        *valueOut = true;
-    }
-    return true;
-}
-
-}; // namespace android
diff --git a/core/jni/CursorWindow.h b/core/jni/CursorWindow.h
deleted file mode 100644
index 3fcb560..0000000
--- a/core/jni/CursorWindow.h
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _ANDROID__DATABASE_WINDOW_H
-#define _ANDROID__DATABASE_WINDOW_H
-
-#include <cutils/log.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include <binder/IMemory.h>
-#include <utils/RefBase.h>
-
-#include <jni.h>
-
-#define DEFAULT_WINDOW_SIZE 4096
-#define MAX_WINDOW_SIZE (1024 * 1024)
-#define WINDOW_ALLOCATION_SIZE 4096
-
-#define ROW_SLOT_CHUNK_NUM_ROWS 16
-
-// Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS,
-// with an offset after the rows that points to the next chunk
-#define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t))
-
-
-#if LOG_NDEBUG
-
-#define IF_LOG_WINDOW() if (false)
-#define LOG_WINDOW(...)
-
-#else
-
-#define IF_LOG_WINDOW() IF_LOG(LOG_DEBUG, "CursorWindow")
-#define LOG_WINDOW(...) LOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)
-
-#endif
-
-
-// When defined to true strings are stored as UTF8, otherwise they're UTF16
-#define WINDOW_STORAGE_UTF8 1
-
-// When defined to true numberic values are stored inline in the field_slot_t, otherwise they're allocated in the window
-#define WINDOW_STORAGE_INLINE_NUMERICS 1
-
-namespace android {
-
-typedef struct
-{
-    uint32_t numRows;
-    uint32_t numColumns;
-} window_header_t;
-
-typedef struct
-{
-    uint32_t offset;
-} row_slot_t;
-
-typedef struct
-{
-    uint8_t type;
-    union {
-        double d;
-        int64_t l;
-        struct {
-            uint32_t offset;
-            uint32_t size;
-        } buffer;
-    } data;
-} __attribute__((packed)) field_slot_t;
-
-#define FIELD_TYPE_INTEGER 1
-#define FIELD_TYPE_FLOAT 2
-#define FIELD_TYPE_STRING 3
-#define FIELD_TYPE_BLOB 4
-#define FIELD_TYPE_NULL 5
-
-/**
- * This class stores a set of rows from a database in a buffer. The begining of the
- * window has first chunk of row_slot_ts, which are offsets to the row directory, followed by
- * an offset to the next chunk in a linked-list of additional chunk of row_slot_ts in case
- * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a
- * field_slot_t per column, which has the size, offset, and type of the data for that field.
- * Note that the data types come from sqlite3.h.
- */
-class CursorWindow
-{
-public:
-                        CursorWindow(size_t maxSize);
-                        CursorWindow(){}
-    bool                setMemory(const sp<IMemory>&);
-                        ~CursorWindow();
-
-    bool                initBuffer(bool localOnly);
-    sp<IMemory>         getMemory() {return mMemory;}
-
-    size_t              size() {return mSize;}
-    uint8_t *           data() {return mData;}
-    uint32_t            getNumRows() {return mHeader->numRows;}
-    uint32_t            getNumColumns() {return mHeader->numColumns;}
-    void                freeLastRow() {
-                            if (mHeader->numRows > 0) {
-                                mHeader->numRows--;
-                            }
-                        }
-    bool                setNumColumns(uint32_t numColumns)
-                            {
-                                uint32_t cur = mHeader->numColumns;
-                                if (cur > 0 && cur != numColumns) {
-                                    LOGE("Trying to go from %d columns to %d", cur, numColumns);
-                                    return false;
-                                }
-                                mHeader->numColumns = numColumns;
-                                return true;
-                            }
-
-    int32_t             freeSpace();
-
-    void                clear();
-
-                        /**
-                         * Allocate a row slot and its directory. The returned
-                         * pointer points to the begining of the row's directory
-                         * or NULL if there wasn't room. The directory is
-                         * initialied with NULL entries for each field.
-                         */
-    field_slot_t *      allocRow();
-
-                        /**
-                         * Allocate a portion of the window. Returns the offset
-                         * of the allocation, or 0 if there isn't enough space.
-                         * If aligned is true, the allocation gets 4 byte alignment.
-                         */
-    uint32_t            alloc(size_t size, bool aligned = false);
-
-    uint32_t            read_field_slot(int row, int column, field_slot_t * slot);
-
-                        /**
-                         * Copy data into the window at the given offset.
-                         */
-    void                copyIn(uint32_t offset, uint8_t const * data, size_t size);
-    void                copyIn(uint32_t offset, int64_t data);
-    void                copyIn(uint32_t offset, double data);
-
-    void                copyOut(uint32_t offset, uint8_t * data, size_t size);
-    int64_t             copyOutLong(uint32_t offset);
-    double              copyOutDouble(uint32_t offset);
-
-    bool                putLong(unsigned int row, unsigned int col, int64_t value);
-    bool                putDouble(unsigned int row, unsigned int col, double value);
-    bool                putNull(unsigned int row, unsigned int col);
-
-    bool                getLong(unsigned int row, unsigned int col, int64_t * valueOut);
-    bool                getDouble(unsigned int row, unsigned int col, double * valueOut);
-    bool                getNull(unsigned int row, unsigned int col, bool * valueOut);
-
-    uint8_t *           offsetToPtr(uint32_t offset) {return mData + offset;}
-
-    row_slot_t *        allocRowSlot();
-
-    row_slot_t *        getRowSlot(int row);
-    
-                        /**
-                         * return NULL if Failed to find rowSlot or
-                         * Invalid rowSlot
-                         */
-    field_slot_t *      getFieldSlotWithCheck(int row, int column);
-    field_slot_t *      getFieldSlot(int row, int column)
-                            {
-                                int fieldDirOffset = getRowSlot(row)->offset;
-                                return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;
-                            }
-
-private:
-    uint8_t * mData;
-    size_t mSize;
-    size_t mMaxSize;
-    window_header_t * mHeader;
-    sp<IMemory> mMemory;
-
-    /**
-     * Offset of the lowest unused data byte in the array.
-     */
-    uint32_t mFreeOffset;
-};
-
-}; // namespace android
-
-#endif
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 88bbafd..8956e39 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -12,6 +12,8 @@
 

 #include <jni.h>

 

+#include <Caches.h>

+

 #if 0

     #define TRACE_BITMAP(code)  code

 #else

@@ -251,10 +253,22 @@
 }

 

 static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {

+#ifdef USE_OPENGL_RENDERER

+    if (android::uirenderer::Caches::hasInstance()) {

+        android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);

+        return;

+    }

+#endif // USE_OPENGL_RENDERER

     delete bitmap;

 }

 

 static void Bitmap_recycle(JNIEnv* env, jobject, SkBitmap* bitmap) {

+#ifdef USE_OPENGL_RENDERER

+    if (android::uirenderer::Caches::hasInstance()) {

+        android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);

+        return;

+    }

+#endif // USE_OPENGL_RENDERER

     bitmap->setPixels(NULL, NULL);

 }

 

@@ -313,6 +327,10 @@
     return bitmap->config();

 }

 

+static int Bitmap_getGenerationId(JNIEnv* env, jobject, SkBitmap* bitmap) {

+    return bitmap->getGenerationID();

+}

+

 static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) {

     return !bitmap->isOpaque();

 }

@@ -606,6 +624,7 @@
         (void*)Bitmap_writeToParcel },

     {   "nativeExtractAlpha",       "(II[I)Landroid/graphics/Bitmap;",

         (void*)Bitmap_extractAlpha },

+    {   "nativeGenerationId",       "(I)I", (void*)Bitmap_getGenerationId },

     {   "nativeGetPixel",           "(III)I", (void*)Bitmap_getPixel },

     {   "nativeGetPixels",          "(I[IIIIIII)V", (void*)Bitmap_getPixels },

     {   "nativeSetPixel",           "(IIII)V", (void*)Bitmap_setPixel },

diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 90a0243..3b2406c 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -146,6 +146,11 @@
             env->GetBooleanField(options, gOptions_shareableFieldID);
 }
 
+static bool optionsJustBounds(JNIEnv* env, jobject options) {
+    return options != NULL &&
+            env->GetBooleanField(options, gOptions_justBoundsFieldID);
+}
+
 static bool optionsReportSizeToVM(JNIEnv* env, jobject options) {
     return NULL == options ||
             !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
@@ -183,7 +188,7 @@
     
     if (NULL != options) {
         sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
-        if (env->GetBooleanField(options, gOptions_justBoundsFieldID)) {
+        if (optionsJustBounds(env, options)) {
             mode = SkImageDecoder::kDecodeBounds_Mode;
         }
         // initialize these, in case we fail later on
@@ -415,13 +420,14 @@
     /*  If optionsShareable() we could decide to just wrap the java array and
         share it, but that means adding a globalref to the java array object
         and managing its lifetime. For now we just always copy the array's data
-        if optionsPurgeable().
+        if optionsPurgeable(), unless we're just decoding bounds.
      */
+    bool purgeable = optionsPurgeable(env, options)
+            && !optionsJustBounds(env, options);
     AutoJavaByteArray ar(env, byteArray);
-    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length,
-                                          optionsPurgeable(env, options));
+    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
     SkAutoUnref aur(stream);
-    return doDecode(env, stream, NULL, options, true);
+    return doDecode(env, stream, NULL, options, purgeable);
 }
 
 static void nativeRequestCancel(JNIEnv*, jobject joptions) {
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
new file mode 100644
index 0000000..bf18d55
--- /dev/null
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BitmapRegionDecoder"
+
+#include "SkBitmap.h"
+#include "SkImageEncoder.h"
+#include "GraphicsJNI.h"
+#include "SkUtils.h"
+#include "SkTemplates.h"
+#include "SkPixelRef.h"
+#include "SkStream.h"
+#include "BitmapFactory.h"
+#include "AutoDecodeCancel.h"
+#include "SkBitmapRegionDecoder.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+#include "Utils.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include "android_util_Binder.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include <binder/Parcel.h>
+#include <jni.h>
+#include <utils/Asset.h>
+#include <sys/stat.h>
+
+static jclass gFileDescriptor_class;
+static jfieldID gFileDescriptor_descriptor;
+
+#if 0
+    #define TRACE_BITMAP(code)  code
+#else
+    #define TRACE_BITMAP(code)
+#endif
+
+using namespace android;
+
+static SkMemoryStream* buildSkMemoryStream(SkStream *stream) {
+    size_t bufferSize = 4096;
+    size_t streamLen = 0;
+    size_t len;
+    char* data = (char*)sk_malloc_throw(bufferSize);
+
+    while ((len = stream->read(data + streamLen,
+                    bufferSize - streamLen)) != 0) {
+        streamLen += len;
+        if (streamLen == bufferSize) {
+            bufferSize *= 2;
+            data = (char*)sk_realloc_throw(data, bufferSize);
+        }
+    }
+    data = (char*)sk_realloc_throw(data, streamLen);
+
+    SkMemoryStream* streamMem = new SkMemoryStream();
+    streamMem->setMemoryOwned(data, streamLen);
+    return streamMem;
+}
+
+static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) {
+    SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
+    int width, height;
+    if (NULL == decoder) {
+        doThrowIOE(env, "Image format not supported");
+        return nullObjectReturn("SkImageDecoder::Factory returned null");
+    }
+
+    JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
+    decoder->setAllocator(javaAllocator);
+    JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
+    decoder->setReporter(javaMemoryReporter);
+    javaAllocator->unref();
+    javaMemoryReporter->unref();
+
+    if (!decoder->buildTileIndex(stream, &width, &height)) {
+        char msg[100];
+        snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
+                decoder->getFormatName());
+        doThrowIOE(env, msg);
+        return nullObjectReturn("decoder->buildTileIndex returned false");
+    }
+
+    SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height);
+
+    return GraphicsJNI::createBitmapRegionDecoder(env, bm);
+}
+
+static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
+                                     int offset, int length, jboolean isShareable) {
+    /*  If isShareable we could decide to just wrap the java array and
+        share it, but that means adding a globalref to the java array object
+        For now we just always copy the array's data if isShareable.
+     */
+    AutoJavaByteArray ar(env, byteArray);
+    SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
+    return doBuildTileIndex(env, stream);
+}
+
+static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
+                                          jobject fileDescriptor, jboolean isShareable) {
+    NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
+
+    jint descriptor = env->GetIntField(fileDescriptor,
+                                       gFileDescriptor_descriptor);
+    SkStream *stream = NULL;
+    struct stat fdStat;
+    int newFD;
+    if (fstat(descriptor, &fdStat) == -1) {
+        doThrowIOE(env, "broken file descriptor");
+        return nullObjectReturn("fstat return -1");
+    }
+
+    if (isShareable &&
+            S_ISREG(fdStat.st_mode) &&
+            (newFD = ::dup(descriptor)) != -1) {
+        SkFDStream* fdStream = new SkFDStream(newFD, true);
+        if (!fdStream->isValid()) {
+            fdStream->unref();
+            return NULL;
+        }
+        stream = fdStream;
+    } else {
+        /* Restore our offset when we leave, so we can be called more than once
+           with the same descriptor. This is only required if we didn't dup the
+           file descriptor, but it is OK to do it all the time.
+        */
+        AutoFDSeek as(descriptor);
+
+        SkFDStream* fdStream = new SkFDStream(descriptor, false);
+        if (!fdStream->isValid()) {
+            fdStream->unref();
+            return NULL;
+        }
+        stream = buildSkMemoryStream(fdStream);
+        fdStream->unref();
+    }
+
+    return doBuildTileIndex(env, stream);
+}
+
+static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
+                                  jobject is,       // InputStream
+                                  jbyteArray storage, // byte[]
+                                  jboolean isShareable) {
+    jobject largeBitmap = NULL;
+    SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
+
+    if (stream) {
+        // for now we don't allow shareable with java inputstreams
+        SkMemoryStream *mStream = buildSkMemoryStream(stream);
+        largeBitmap = doBuildTileIndex(env, mStream);
+        stream->unref();
+    }
+    return largeBitmap;
+}
+
+static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
+                                 jint native_asset, // Asset
+                                 jboolean isShareable) {
+    SkStream* stream, *assStream;
+    Asset* asset = reinterpret_cast<Asset*>(native_asset);
+    assStream = new AssetStreamAdaptor(asset);
+    stream = buildSkMemoryStream(assStream);
+    assStream->unref();
+    return doBuildTileIndex(env, stream);
+}
+
+/*
+ * nine patch not supported
+ *
+ * purgeable not supported
+ * reportSizeToVM not supported
+ */
+static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd,
+        int start_x, int start_y, int width, int height, jobject options) {
+    SkImageDecoder *decoder = brd->getDecoder();
+    int sampleSize = 1;
+    SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
+    bool doDither = true;
+
+    if (NULL != options) {
+        sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
+        // initialize these, in case we fail later on
+        env->SetIntField(options, gOptions_widthFieldID, -1);
+        env->SetIntField(options, gOptions_heightFieldID, -1);
+        env->SetObjectField(options, gOptions_mimeFieldID, 0);
+
+        jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
+        prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
+        doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
+    }
+
+    decoder->setDitherImage(doDither);
+    SkBitmap*           bitmap = new SkBitmap;
+    SkAutoTDelete<SkBitmap>       adb(bitmap);
+    AutoDecoderCancel   adc(options, decoder);
+
+    // To fix the race condition in case "requestCancelDecode"
+    // happens earlier than AutoDecoderCancel object is added
+    // to the gAutoDecoderCancelMutex linked list.
+    if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
+        return nullObjectReturn("gOptions_mCancelID");;
+    }
+
+    SkIRect region;
+    region.fLeft = start_x;
+    region.fTop = start_y;
+    region.fRight = start_x + width;
+    region.fBottom = start_y + height;
+
+    if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
+        return nullObjectReturn("decoder->decodeRegion returned false");
+    }
+
+    // update options (if any)
+    if (NULL != options) {
+        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
+        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
+        // TODO: set the mimeType field with the data from the codec.
+        // but how to reuse a set of strings, rather than allocating new one
+        // each time?
+        env->SetObjectField(options, gOptions_mimeFieldID,
+                            getMimeTypeString(env, decoder->getFormat()));
+    }
+
+    // detach bitmap from its autotdeleter, since we want to own it now
+    adb.detach();
+
+    SkPixelRef* pr;
+    pr = bitmap->pixelRef();
+    // promise we will never change our pixels (great for sharing and pictures)
+    pr->setImmutable();
+    // now create the java bitmap
+    return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
+}
+
+static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
+    return brd->getHeight();
+}
+
+static int nativeGetWidth(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
+    return brd->getWidth();
+}
+
+static void nativeClean(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
+    delete brd;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gBitmapRegionDecoderMethods[] = {
+    {   "nativeDecodeRegion",
+        "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+        (void*)nativeDecodeRegion},
+
+    {   "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
+
+    {   "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
+
+    {   "nativeClean", "(I)V", (void*)nativeClean},
+
+    {   "nativeNewInstance",
+        "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromByteArray
+    },
+
+    {   "nativeNewInstance",
+        "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromStream
+    },
+
+    {   "nativeNewInstance",
+        "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromFileDescriptor
+    },
+
+    {   "nativeNewInstance",
+        "(IZ)Landroid/graphics/BitmapRegionDecoder;",
+        (void*)nativeNewInstanceFromAsset
+    },
+};
+
+#define kClassPathName  "android/graphics/BitmapRegionDecoder"
+
+static jclass make_globalref(JNIEnv* env, const char classname[]) {
+    jclass c = env->FindClass(classname);
+    SkASSERT(c);
+    return (jclass)env->NewGlobalRef(c);
+}
+
+static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
+                                const char fieldname[], const char type[]) {
+    jfieldID id = env->GetFieldID(clazz, fieldname, type);
+    SkASSERT(id);
+    return id;
+}
+
+int register_android_graphics_BitmapRegionDecoder(JNIEnv* env);
+int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
+{
+
+    gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor");
+    gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I");
+    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+            gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods));
+}
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index e1e9536..8944d47 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -20,15 +20,18 @@
 
 #include "SkCanvas.h"
 #include "SkDevice.h"
-#include "SkGLCanvas.h"
 #include "SkGraphics.h"
 #include "SkImageRef_GlobalPool.h"
 #include "SkPorterDuff.h"
 #include "SkShader.h"
 #include "SkTemplates.h"
 
-#include "SkBoundaryPatch.h"
-#include "SkMeshUtils.h"
+#include "TextLayout.h"
+
+#include "unicode/ubidi.h"
+#include "unicode/ushape.h"
+
+#include <utils/Log.h>
 
 #define TIME_DRAWx
 
@@ -60,13 +63,8 @@
         return bitmap ? new SkCanvas(*bitmap) : new SkCanvas;
     }
     
-    static SkCanvas* initGL(JNIEnv* env, jobject) {
-        return new SkGLCanvas;
-    }
-    
     static void freeCaches(JNIEnv* env, jobject) {
         // these are called in no particular order
-        SkGLCanvas::DeleteAllTextures();
         SkImageRef_GlobalPool::SetRAMUsed(0);
         SkGraphics::SetFontCacheUsed(0);
     }
@@ -103,11 +101,6 @@
         return canvas->getDevice()->accessBitmap(false).height();
     }
 
-    static void setViewport(JNIEnv* env, jobject, SkCanvas* canvas,
-                            int width, int height) {
-        canvas->setViewport(width, height);
-    }
-    
     static void setBitmap(JNIEnv* env, jobject, SkCanvas* canvas,
                           SkBitmap* bitmap) {
         canvas->setBitmapDevice(*bitmap);
@@ -743,45 +736,49 @@
         canvas->drawVertices(mode, ptCount, verts, texs, colors, NULL,
                              indices, indexCount, *paint);
     }
-    
-    static void drawText___CIIFFPaint(JNIEnv* env, jobject, SkCanvas* canvas,
+
+
+    static void drawText___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
                                       jcharArray text, int index, int count,
-                                      jfloat x, jfloat y, SkPaint* paint) {
+                                      jfloat x, jfloat y, int flags, SkPaint* paint) {
         jchar* textArray = env->GetCharArrayElements(text, NULL);
-        jsize textCount = env->GetArrayLength(text);
-        SkScalar x_ = SkFloatToScalar(x);
-        SkScalar y_ = SkFloatToScalar(y);
-        canvas->drawText(textArray + index, count << 1, x_, y_, *paint);
-        env->ReleaseCharArrayElements(text, textArray, 0);
+        TextLayout::drawText(paint, textArray + index, count, flags, x, y, canvas);
+        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
     }
- 
-    static void drawText__StringIIFFPaint(JNIEnv* env, jobject,
-                            SkCanvas* canvas, jstring text, int start, int end,
-                                          jfloat x, jfloat y, SkPaint* paint) {
-        const void* text_ = env->GetStringChars(text, NULL);
-        SkScalar x_ = SkFloatToScalar(x);
-        SkScalar y_ = SkFloatToScalar(y);
-        canvas->drawText((const uint16_t*)text_ + start, (end - start) << 1,
-                         x_, y_, *paint);
-        env->ReleaseStringChars(text, (const jchar*) text_);
+
+    static void drawText__StringIIFFIPaint(JNIEnv* env, jobject,
+                                          SkCanvas* canvas, jstring text,
+                                          int start, int end,
+                                          jfloat x, jfloat y, int flags, SkPaint* paint) {
+        const jchar* textArray = env->GetStringChars(text, NULL);
+        TextLayout::drawText(paint, textArray + start, end - start, flags, x, y, canvas);
+        env->ReleaseStringChars(text, textArray);
     }
-    
-    static void drawString(JNIEnv* env, jobject canvas, jstring text,
-                           jfloat x, jfloat y, jobject paint) {
-        NPE_CHECK_RETURN_VOID(env, canvas);
-        NPE_CHECK_RETURN_VOID(env, paint);
-        NPE_CHECK_RETURN_VOID(env, text);
-        size_t count = env->GetStringLength(text);
-        if (0 == count) {
-            return;
-        }
-        const jchar* text_ = env->GetStringChars(text, NULL);
-        SkCanvas* c = GraphicsJNI::getNativeCanvas(env, canvas);
-        c->drawText(text_, count << 1, SkFloatToScalar(x), SkFloatToScalar(y),
-                    *GraphicsJNI::getNativePaint(env, paint));
-        env->ReleaseStringChars(text, text_);
+
+    static void drawTextRun___CIIIIFFIPaint(
+        JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index,
+        int count, int contextIndex, int contextCount,
+        jfloat x, jfloat y, int dirFlags, SkPaint* paint) {
+
+        jchar* chars = env->GetCharArrayElements(text, NULL);
+        TextLayout::drawTextRun(paint, chars + contextIndex, index - contextIndex,
+                                count, contextCount, dirFlags, x, y, canvas);
+        env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
     }
-    
+
+    static void drawTextRun__StringIIIIFFIPaint(
+        JNIEnv* env, jobject obj, SkCanvas* canvas, jstring text, jint start,
+        jint end, jint contextStart, jint contextEnd,
+        jfloat x, jfloat y, jint dirFlags, SkPaint* paint) {
+
+        jint count = end - start;
+        jint contextCount = contextEnd - contextStart;
+        const jchar* chars = env->GetStringChars(text, NULL);
+        TextLayout::drawTextRun(paint, chars + contextStart, start - contextStart,
+                                count, contextCount, dirFlags, x, y, canvas);
+        env->ReleaseStringChars(text, chars);
+    }
+
     static void drawPosText___CII_FPaint(JNIEnv* env, jobject, SkCanvas* canvas,
                                          jcharArray text, int index, int count,
                                          jfloatArray pos, SkPaint* paint) {
@@ -804,10 +801,11 @@
         }
         delete[] posPtr;
     }
- 
+
     static void drawPosText__String_FPaint(JNIEnv* env, jobject,
                                            SkCanvas* canvas, jstring text,
-                                           jfloatArray pos, SkPaint* paint) {
+                                           jfloatArray pos,
+                                           SkPaint* paint) {
         const void* text_ = text ? env->GetStringChars(text, NULL) : NULL;
         int byteLength = text ? env->GetStringLength(text) : 0;
         float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
@@ -827,27 +825,27 @@
         }
         delete[] posPtr;
     }
- 
+
     static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
-                        SkCanvas* canvas, jcharArray text, int index, int count,
-                SkPath* path, jfloat hOffset, jfloat vOffset, SkPaint* paint) {
+            SkCanvas* canvas, jcharArray text, int index, int count,
+            SkPath* path, jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) {
 
         jchar* textArray = env->GetCharArrayElements(text, NULL);
-        canvas->drawTextOnPathHV(textArray + index, count << 1, *path,
-                    SkFloatToScalar(hOffset), SkFloatToScalar(vOffset), *paint);
+        TextLayout::drawTextOnPath(paint, textArray, count, bidiFlags, hOffset, vOffset,
+                                   path, canvas);
         env->ReleaseCharArrayElements(text, textArray, 0);
     }
- 
+
     static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
-                            SkCanvas* canvas, jstring text, SkPath* path,
-                            jfloat hOffset, jfloat vOffset, SkPaint* paint) {
+            SkCanvas* canvas, jstring text, SkPath* path,
+            jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) {
         const jchar* text_ = env->GetStringChars(text, NULL);
-        int byteLength = env->GetStringLength(text) << 1;
-        canvas->drawTextOnPathHV(text_, byteLength, *path,
-                    SkFloatToScalar(hOffset), SkFloatToScalar(vOffset), *paint);
+        int count = env->GetStringLength(text);
+        TextLayout::drawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
+                                   path, canvas);
         env->ReleaseStringChars(text, text_);
     }
- 
+
     static bool getClipBounds(JNIEnv* env, jobject, SkCanvas* canvas,
                               jobject bounds) {
         SkRect   r;
@@ -868,12 +866,10 @@
 static JNINativeMethod gCanvasMethods[] = {
     {"finalizer", "(I)V", (void*) SkCanvasGlue::finalizer},
     {"initRaster","(I)I", (void*) SkCanvasGlue::initRaster},
-    {"initGL","()I", (void*) SkCanvasGlue::initGL},
     {"isOpaque","()Z", (void*) SkCanvasGlue::isOpaque},
     {"getWidth","()I", (void*) SkCanvasGlue::getWidth},
     {"getHeight","()I", (void*) SkCanvasGlue::getHeight},
     {"native_setBitmap","(II)V", (void*) SkCanvasGlue::setBitmap},
-    {"nativeSetViewport", "(III)V", (void*) SkCanvasGlue::setViewport},
     {"save","()I", (void*) SkCanvasGlue::saveAll},
     {"save","(I)I", (void*) SkCanvasGlue::save},
     {"native_saveLayer","(ILandroid/graphics/RectF;II)I",
@@ -940,26 +936,27 @@
         (void*) SkCanvasGlue::drawBitmapRR},
     {"native_drawBitmap", "(I[IIIFFIIZI)V",
     (void*)SkCanvasGlue::drawBitmapArray},
-    
     {"nativeDrawBitmapMatrix", "(IIII)V",
         (void*)SkCanvasGlue::drawBitmapMatrix},
     {"nativeDrawBitmapMesh", "(IIII[FI[III)V",
         (void*)SkCanvasGlue::drawBitmapMesh},
     {"nativeDrawVertices", "(III[FI[FI[II[SIII)V",
         (void*)SkCanvasGlue::drawVertices},
-    {"native_drawText","(I[CIIFFI)V",
-        (void*) SkCanvasGlue::drawText___CIIFFPaint},
-    {"native_drawText","(ILjava/lang/String;IIFFI)V",
-        (void*) SkCanvasGlue::drawText__StringIIFFPaint},
-    {"drawText","(Ljava/lang/String;FFLandroid/graphics/Paint;)V",
-        (void*) SkCanvasGlue::drawString},
+    {"native_drawText","(I[CIIFFII)V",
+        (void*) SkCanvasGlue::drawText___CIIFFIPaint},
+    {"native_drawText","(ILjava/lang/String;IIFFII)V",
+        (void*) SkCanvasGlue::drawText__StringIIFFIPaint},
+    {"native_drawTextRun","(I[CIIIIFFII)V",
+        (void*) SkCanvasGlue::drawTextRun___CIIIIFFIPaint},
+    {"native_drawTextRun","(ILjava/lang/String;IIIIFFII)V",
+        (void*) SkCanvasGlue::drawTextRun__StringIIIIFFIPaint},
     {"native_drawPosText","(I[CII[FI)V",
         (void*) SkCanvasGlue::drawPosText___CII_FPaint},
     {"native_drawPosText","(ILjava/lang/String;[FI)V",
         (void*) SkCanvasGlue::drawPosText__String_FPaint},
-    {"native_drawTextOnPath","(I[CIIIFFI)V",
+    {"native_drawTextOnPath","(I[CIIIFFII)V",
         (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint},
-    {"native_drawTextOnPath","(ILjava/lang/String;IFFI)V",
+    {"native_drawTextOnPath","(ILjava/lang/String;IFFII)V",
         (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint},
     {"native_drawPicture", "(II)V", (void*) SkCanvasGlue::drawPicture},
 
@@ -968,40 +965,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static void BoundaryPatch_computeCubic(JNIEnv* env, jobject, jfloatArray jpts,
-                                   int texW, int texH, int rows, int cols,
-                                   jfloatArray jverts, jshortArray jidx) {
-    AutoJavaFloatArray ptsArray(env, jpts, 24, kRO_JNIAccess);
-
-    int vertCount = rows * cols;
-    AutoJavaFloatArray vertsArray(env, jverts, vertCount * 4, kRW_JNIAccess);
-    SkPoint* verts = (SkPoint*)vertsArray.ptr();
-    SkPoint* texs = verts + vertCount;
-
-    int idxCount = (rows - 1) * (cols - 1) * 6;
-    AutoJavaShortArray idxArray(env, jidx, idxCount, kRW_JNIAccess);
-    uint16_t* idx = (uint16_t*)idxArray.ptr();  // cast from int16_t*
-
-    SkCubicBoundary cubic;
-    memcpy(cubic.fPts, ptsArray.ptr(), 12 * sizeof(SkPoint));
-
-    SkBoundaryPatch patch;
-    patch.setBoundary(&cubic);
-    // generate our verts
-    patch.evalPatch(verts, rows, cols);
-
-    SkMeshIndices mesh;
-    // generate our texs and idx
-    mesh.init(texs, idx, texW, texH, rows, cols);
-}
-
-static JNINativeMethod gBoundaryPatchMethods[] = {
-    {"nativeComputeCubicPatch", "([FIIII[F[S)V",
-    (void*)BoundaryPatch_computeCubic },
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
 #include <android_runtime/AndroidRuntime.h>
 
 #define REG(env, name, array) \
@@ -1013,7 +976,6 @@
     int result;
 
     REG(env, "android/graphics/Canvas", gCanvasMethods);
-    REG(env, "android/graphics/utils/BoundaryPatch", gBoundaryPatchMethods);
     
     return result;
 }
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index ebfb209..e2a959d 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -23,28 +23,79 @@
 #include "SkColorMatrixFilter.h"
 #include "SkPorterDuff.h"
 
+#include <SkiaColorFilter.h>
+#include <Caches.h>
+
 namespace android {
 
+using namespace uirenderer;
+
 class SkColorFilterGlue {
 public:
-
-    static void finalizer(JNIEnv* env, jobject clazz, SkColorFilter* obj) {
+    static void finalizer(JNIEnv* env, jobject clazz, SkColorFilter* obj, SkiaColorFilter* f) {
         obj->safeUnref();
+        // f == NULL when not !USE_OPENGL_RENDERER, so no need to delete outside the ifdef
+#ifdef USE_OPENGL_RENDERER
+        if (android::uirenderer::Caches::hasInstance()) {
+            android::uirenderer::Caches::getInstance().resourceCache.destructor(f);
+        } else {
+            delete f;
+        }
+#endif
     }
 
-    static SkColorFilter* CreatePorterDuffFilter(JNIEnv* env, jobject,
-                            jint srcColor, SkPorterDuff::Mode mode) {
-        return SkColorFilter::CreateModeFilter(srcColor,
-                                           SkPorterDuff::ToXfermodeMode(mode));
+    static SkiaColorFilter* glCreatePorterDuffFilter(JNIEnv* env, jobject, SkColorFilter *skFilter,
+            jint srcColor, SkPorterDuff::Mode mode) {
+#ifdef USE_OPENGL_RENDERER
+        return new SkiaBlendFilter(skFilter, srcColor, SkPorterDuff::ToXfermodeMode(mode));
+#else
+        return NULL;
+#endif
     }
- 
-    static SkColorFilter* CreateLightingFilter(JNIEnv* env, jobject,
-                                               jint mul, jint add) {
+
+    static SkiaColorFilter* glCreateLightingFilter(JNIEnv* env, jobject, SkColorFilter *skFilter,
+            jint mul, jint add) {
+#ifdef USE_OPENGL_RENDERER
+        return new SkiaLightingFilter(skFilter, mul, add);
+#else
+        return NULL;
+#endif
+    }
+
+    static SkiaColorFilter* glCreateColorMatrixFilter(JNIEnv* env, jobject, SkColorFilter *skFilter,
+            jfloatArray jarray) {
+#ifdef USE_OPENGL_RENDERER
+        AutoJavaFloatArray autoArray(env, jarray, 20);
+        const float* src = autoArray.ptr();
+
+        float* colorMatrix = new float[16];
+        memcpy(colorMatrix, src, 4 * sizeof(float));
+        memcpy(&colorMatrix[4], &src[5], 4 * sizeof(float));
+        memcpy(&colorMatrix[8], &src[10], 4 * sizeof(float));
+        memcpy(&colorMatrix[12], &src[15], 4 * sizeof(float));
+
+        float* colorVector = new float[4];
+        colorVector[0] = src[4];
+        colorVector[1] = src[9];
+        colorVector[2] = src[14];
+        colorVector[3] = src[19];
+
+        return new SkiaColorMatrixFilter(skFilter, colorMatrix, colorVector);
+#else
+        return NULL;
+#endif
+    }
+
+    static SkColorFilter* CreatePorterDuffFilter(JNIEnv* env, jobject, jint srcColor,
+            SkPorterDuff::Mode mode) {
+        return SkColorFilter::CreateModeFilter(srcColor, SkPorterDuff::ToXfermodeMode(mode));
+    }
+
+    static SkColorFilter* CreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) {
         return SkColorFilter::CreateLightingFilter(mul, add);
     }
-    
-    static SkColorFilter* CreateColorMatrixFilter(JNIEnv* env, jobject,
-                                                  jfloatArray jarray) {
+
+    static SkColorFilter* CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) {
         AutoJavaFloatArray autoArray(env, jarray, 20);
         const float* src = autoArray.ptr();
 
@@ -58,26 +109,25 @@
         return new SkColorMatrixFilter(src);
 #endif
     }
- 
 };
 
 static JNINativeMethod colorfilter_methods[] = {
-    {"finalizer", "(I)V", (void*) SkColorFilterGlue::finalizer}
+    {"finalizer", "(II)V", (void*) SkColorFilterGlue::finalizer}
 };
 
 static JNINativeMethod porterduff_methods[] = {
-    {"native_CreatePorterDuffFilter","(II)I",
-        (void*) SkColorFilterGlue::CreatePorterDuffFilter}
+    { "native_CreatePorterDuffFilter", "(II)I", (void*) SkColorFilterGlue::CreatePorterDuffFilter   },
+    { "nCreatePorterDuffFilter",       "(III)I", (void*) SkColorFilterGlue::glCreatePorterDuffFilter }
 };
 
 static JNINativeMethod lighting_methods[] = {
-    {"native_CreateLightingFilter","(II)I",
-        (void*) SkColorFilterGlue::CreateLightingFilter}
+    { "native_CreateLightingFilter", "(II)I", (void*) SkColorFilterGlue::CreateLightingFilter   },
+    { "nCreateLightingFilter",       "(III)I", (void*) SkColorFilterGlue::glCreateLightingFilter },
 };
 
 static JNINativeMethod colormatrix_methods[] = {
-    {"nativeColorMatrixFilter","([F)I",
-        (void*) SkColorFilterGlue::CreateColorMatrixFilter}
+    { "nativeColorMatrixFilter", "([F)I", (void*) SkColorFilterGlue::CreateColorMatrixFilter   },
+    { "nColorMatrixFilter",      "(I[F)I", (void*) SkColorFilterGlue::glCreateColorMatrixFilter }
 };
 
 #define REG(env, name, array) \
@@ -85,7 +135,6 @@
                                                     SK_ARRAY_COUNT(array));  \
     if (result < 0) return result
 
-
 int register_android_graphics_ColorFilter(JNIEnv* env) {
     int result;
     
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 195f4d2..9467be8 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -46,7 +46,7 @@
 }
 
 void doThrowIOE(JNIEnv* env, const char* msg) {
-    doThrow(env, "java/lang/IOException", msg);
+    doThrow(env, "java/io/IOException", msg);
 }
 
 bool GraphicsJNI::hasException(JNIEnv *env) {
@@ -168,6 +168,9 @@
 static jclass   gBitmapConfig_class;
 static jfieldID gBitmapConfig_nativeInstanceID;
 
+static jclass   gBitmapRegionDecoder_class;
+static jmethodID gBitmapRegionDecoder_constructorMethodID;
+
 static jclass   gCanvas_class;
 static jfieldID gCanvas_nativeInstanceID;
 
@@ -374,6 +377,24 @@
     return obj;
 }
 
+jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
+{
+    SkASSERT(bitmap != NULL);
+
+    jobject obj = env->AllocObject(gBitmapRegionDecoder_class);
+    if (hasException(env)) {
+        obj = NULL;
+        return obj;
+    }
+    if (obj) {
+        env->CallVoidMethod(obj, gBitmapRegionDecoder_constructorMethodID, (jint)bitmap);
+        if (hasException(env)) {
+            obj = NULL;
+        }
+    }
+    return obj;
+}
+
 jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
 {
     SkASSERT(region != NULL);
@@ -592,6 +613,9 @@
     gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
                                             "(IZ[BI)V");
 
+    gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
+    gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
+
     gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
     gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
                                                      "nativeInt", "I");    
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 1f94418..d0f9125 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -4,6 +4,7 @@
 #include "SkPoint.h"
 #include "SkRect.h"
 #include "SkBitmap.h"
+#include "../images/SkBitmapRegionDecoder.h"
 #include "../images/SkImageDecoder.h"
 #include <jni.h>
 
@@ -55,6 +56,8 @@
     
     static jobject createRegion(JNIEnv* env, SkRegion* region);
 
+    static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
+
     /** Set a pixelref for the bitmap (needs setConfig to already be called)
         Returns true on success. If it returns false, then it failed, and the
         appropriate exception will have been raised.
diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp
index b782766..d0871ac5 100644
--- a/core/jni/android/graphics/Matrix.cpp
+++ b/core/jni/android/graphics/Matrix.cpp
@@ -15,11 +15,6 @@
 ** limitations under the License.
 */
 
-// This file was generated from the C++ include file: SkMatrix.h
-// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, 
-// or one of the auxilary file specifications in device/tools/gluemaker.
-
 #include "jni.h"
 #include "GraphicsJNI.h"
 #include <android_runtime/AndroidRuntime.h>
@@ -27,6 +22,10 @@
 #include "SkMatrix.h"
 #include "SkTemplates.h"
 
+#include "Matrix.h"
+
+#include <Caches.h>
+
 namespace android {
 
 class SkMatrixGlue {
@@ -403,10 +402,20 @@
     {"native_equals", "(II)Z", (void*) SkMatrixGlue::equals}
 };
 
+static jfieldID sNativeInstanceField;
+
 int register_android_graphics_Matrix(JNIEnv* env) {
     int result = AndroidRuntime::registerNativeMethods(env, "android/graphics/Matrix", methods,
         sizeof(methods) / sizeof(methods[0]));
+
+    jclass clazz = env->FindClass("android/graphics/Matrix");
+    sNativeInstanceField = env->GetFieldID(clazz, "native_instance", "I");
+
     return result;
 }
 
+SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj) {
+    return reinterpret_cast<SkMatrix*>(env->GetIntField(matrixObj, sNativeInstanceField));
+}
+
 }
diff --git a/core/jni/android/graphics/Matrix.h b/core/jni/android/graphics/Matrix.h
new file mode 100644
index 0000000..31edf88
--- /dev/null
+++ b/core/jni/android/graphics/Matrix.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_GRAPHICS_MATRIX_H
+#define _ANDROID_GRAPHICS_MATRIX_H
+
+#include "jni.h"
+#include "SkMatrix.h"
+
+namespace android {
+
+/* Gets the underlying SkMatrix from a Matrix object. */
+extern SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj);
+
+} // namespace android
+
+#endif // _ANDROID_GRAPHICS_MATRIX_H
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 780badc..e62b034 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -15,11 +15,6 @@
 ** limitations under the License.
 */
 
-// This file was generated from the C++ include file: SkPaint.h
-// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp, 
-// or one of the auxilary file specifications in device/tools/gluemaker.
-
 #include "jni.h"
 #include "GraphicsJNI.h"
 #include <android_runtime/AndroidRuntime.h>
@@ -31,6 +26,12 @@
 #include "SkShader.h"
 #include "SkTypeface.h"
 #include "SkXfermode.h"
+#include "unicode/ushape.h"
+#include "TextLayout.h"
+
+// temporary for debugging
+#include <Caches.h>
+#include <utils/Log.h>
 
 namespace android {
 
@@ -57,6 +58,9 @@
 
 class SkPaintGlue {
 public:
+    enum MoveOpt {
+        AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
+    };
 
     static void finalizer(JNIEnv* env, jobject clazz, SkPaint* obj) {
         delete obj;
@@ -395,20 +399,161 @@
         env->ReleaseStringChars(text, textArray);
         return count;
     }
-    
-    static void getTextPath___CIIFFPath(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, int index, int count, jfloat x, jfloat y, SkPath* path) {
-        const jchar* textArray = env->GetCharArrayElements(text, NULL);
-        paint->getTextPath(textArray + index, count << 1, SkFloatToScalar(x), SkFloatToScalar(y), path);
-        env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
-                                      JNI_ABORT);
+
+    static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, const jchar *text,
+                                    jint start, jint count, jint contextCount, jint flags,
+                                    jfloatArray advances, jint advancesIndex) {
+        jfloat advancesArray[count];
+        jfloat totalAdvance;
+
+        TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, flags,
+                                       advancesArray, totalAdvance);
+
+        if (advances != NULL) {
+            env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray);
+        }
+        return totalAdvance;
     }
- 
-    static void getTextPath__StringIIFFPath(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, int start, int end, jfloat x, jfloat y, SkPath* path) {
+
+    static float getTextRunAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
+            jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
+            jint flags, jfloatArray advances, jint advancesIndex) {
+        jchar* textArray = env->GetCharArrayElements(text, NULL);
+        jfloat result = doTextRunAdvances(env, paint, textArray + contextIndex,
+            index - contextIndex, count, contextCount, flags, advances, advancesIndex);
+        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+        return result;
+    }
+
+    static float getTextRunAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
+            jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags,
+            jfloatArray advances, jint advancesIndex) {
         const jchar* textArray = env->GetStringChars(text, NULL);
-        paint->getTextPath(textArray + start, (end - start) << 1, SkFloatToScalar(x), SkFloatToScalar(y), path);
+        jfloat result = doTextRunAdvances(env, paint, textArray + contextStart,
+            start - contextStart, end - start, contextEnd - contextStart, flags, advances,
+            advancesIndex);
+        env->ReleaseStringChars(text, textArray);
+        return result;
+    }
+
+    static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start,
+            jint count, jint flags, jint offset, jint opt) {
+        SkScalar scalarArray[count];
+        jchar buffer[count];
+
+        // this is where we'd call harfbuzz
+        // for now we just use ushape.c and widths returned from skia
+
+        int widths;
+        if (flags & 0x1) { // rtl, call arabic shaping in case
+            UErrorCode status = U_ZERO_ERROR;
+            // Use fixed length since we need to keep start and count valid
+            u_shapeArabic(text + start, count, buffer, count,
+                    U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_LOGICAL |
+                    U_SHAPE_LETTERS_SHAPE | U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
+            // we shouldn't fail unless there's an out of memory condition,
+            // in which case we're hosed anyway
+            for (int i = 0; i < count; ++i) {
+              if (buffer[i] == 0xffff) {
+                buffer[i] = 0x200b; // zero-width-space for skia
+              }
+            }
+            widths = paint->getTextWidths(buffer, count << 1, scalarArray);
+        } else {
+            widths = paint->getTextWidths(text + start, count << 1, scalarArray);
+        }
+
+        if (widths < count) {
+            // Skia operates on code points, not code units, so surrogate pairs return only one
+            // value. Expand the result so we have one value per UTF-16 code unit.
+
+            // Note, skia's getTextWidth gets confused if it encounters a surrogate pair,
+            // leaving the remaining widths zero.  Not nice.
+            const jchar *chars = text + start;
+            for (int i = count, p = widths - 1; --i > p;) {
+                if (chars[i] >= 0xdc00 && chars[i] < 0xe000 &&
+                        chars[i-1] >= 0xd800 && chars[i-1] < 0xdc00) {
+                    scalarArray[i] = 0;
+                } else {
+                  scalarArray[i] = scalarArray[--p];
+                }
+            }
+        }
+
+        jint pos = offset - start;
+        switch (opt) {
+        case AFTER:
+          if (pos < count) {
+            pos += 1;
+          }
+          // fall through
+        case AT_OR_AFTER:
+          while (pos < count && scalarArray[pos] == 0) {
+            ++pos;
+          }
+          break;
+        case BEFORE:
+          if (pos > 0) {
+            --pos;
+          }
+          // fall through
+        case AT_OR_BEFORE:
+          while (pos > 0 && scalarArray[pos] == 0) {
+            --pos;
+          }
+          break;
+        case AT:
+        default:
+          if (scalarArray[pos] == 0) {
+            pos = -1;
+          }
+          break;
+        }
+
+        if (pos != -1) {
+          pos += start;
+        }
+
+        return pos;
+    }
+
+    static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text,
+            jint contextStart, jint contextCount, jint flags, jint offset, jint cursorOpt) {
+        jchar* textArray = env->GetCharArrayElements(text, NULL);
+        jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, flags,
+                offset, cursorOpt);
+        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+        return result;
+    }
+
+    static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text,
+            jint contextStart, jint contextEnd, jint flags, jint offset, jint cursorOpt) {
+        const jchar* textArray = env->GetStringChars(text, NULL);
+        jint result = doTextRunCursor(env, paint, textArray, contextStart,
+                contextEnd - contextStart, flags, offset, cursorOpt);
+        env->ReleaseStringChars(text, textArray);
+        return result;
+    }
+
+    static void getTextPath(JNIEnv* env, SkPaint* paint, const jchar* text, jint count,
+                            jint bidiFlags, jfloat x, jfloat y, SkPath *path) {
+        TextLayout::getTextPath(paint, text, count, bidiFlags, x, y, path);
+    }
+
+    static void getTextPath___C(JNIEnv* env, jobject clazz, SkPaint* paint, jint bidiFlags,
+            jcharArray text, int index, int count, jfloat x, jfloat y, SkPath* path) {
+        const jchar* textArray = env->GetCharArrayElements(text, NULL);
+        getTextPath(env, paint, textArray + index, count, bidiFlags, x, y, path);
+        env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
+    }
+
+    static void getTextPath__String(JNIEnv* env, jobject clazz, SkPaint* paint, jint bidiFlags,
+            jstring text, int start, int end, jfloat x, jfloat y, SkPath* path) {
+        const jchar* textArray = env->GetStringChars(text, NULL);
+        getTextPath(env, paint, textArray + start, end - start, bidiFlags, x, y, path);
         env->ReleaseStringChars(text, textArray);
     }
- 
+
     static void setShadowLayer(JNIEnv* env, jobject jpaint, jfloat radius,
                                jfloat dx, jfloat dy, int color) {
         NPE_CHECK_RETURN_VOID(env, jpaint);
@@ -576,13 +721,20 @@
     {"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS},
     {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F},
     {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F},
-    {"native_getTextPath","(I[CIIFFI)V", (void*) SkPaintGlue::getTextPath___CIIFFPath},
-    {"native_getTextPath","(ILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__StringIIFFPath},
+    {"native_getTextRunAdvances","(I[CIIIII[FI)F", (void*)
+        SkPaintGlue::getTextRunAdvances___CIIIII_FI},
+    {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FI)F",
+        (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FI},
+    {"native_getTextRunCursor", "(I[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C},
+    {"native_getTextRunCursor", "(ILjava/lang/String;IIIII)I",
+        (void*) SkPaintGlue::getTextRunCursor__String},
+    {"native_getTextPath","(II[CIIFFI)V", (void*) SkPaintGlue::getTextPath___C},
+    {"native_getTextPath","(IILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__String},
     {"nativeGetStringBounds", "(ILjava/lang/String;IILandroid/graphics/Rect;)V",
                                         (void*) SkPaintGlue::getStringBounds },
     {"nativeGetCharArrayBounds", "(I[CIILandroid/graphics/Rect;)V",
                                     (void*) SkPaintGlue::getCharArrayBounds },
-    {"setShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer}
+    {"nSetShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer}
 };
 
 static jfieldID req_fieldID(jfieldID id) {
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index b8b2a10..abe33f4 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -26,12 +26,19 @@
 
 #include "SkPath.h"
 
+#include <Caches.h>
+
 namespace android {
 
 class SkPathGlue {
 public:
 
     static void finalizer(JNIEnv* env, jobject clazz, SkPath* obj) {
+#ifdef USE_OPENGL_RENDERER
+        if (android::uirenderer::Caches::hasInstance()) {
+            android::uirenderer::Caches::getInstance().pathCache.remove(obj);
+        }
+#endif
         delete obj;
     }
 
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index b09c62b..4be1321 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -8,6 +8,16 @@
 #include "SkTemplates.h"
 #include "SkXfermode.h"
 
+#include <SkiaShader.h>
+#include <Caches.h>
+
+using namespace android::uirenderer;
+
+static struct {
+    jclass clazz;
+    jfieldID shader;
+} gShaderClassInfo;
+
 static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
     if (NULL == ptr) {
         doThrowIAE(env);
@@ -41,9 +51,17 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static void Shader_destructor(JNIEnv* env, jobject, SkShader* shader)
+static void Shader_destructor(JNIEnv* env, jobject o, SkShader* shader, SkiaShader* skiaShader)
 {
     shader->safeUnref();
+    // skiaShader == NULL when not !USE_OPENGL_RENDERER, so no need to delete it outside the ifdef
+#ifdef USE_OPENGL_RENDERER
+    if (android::uirenderer::Caches::hasInstance()) {
+        android::uirenderer::Caches::getInstance().resourceCache.destructor(skiaShader);
+    } else {
+        delete skiaShader;
+    }
+#endif
 }
 
 static bool Shader_getLocalMatrix(JNIEnv* env, jobject, const SkShader* shader, SkMatrix* matrix)
@@ -51,7 +69,8 @@
     return shader ? shader->getLocalMatrix(matrix) : false;
 }
  
-static void Shader_setLocalMatrix(JNIEnv* env, jobject, SkShader* shader, const SkMatrix* matrix)
+static void Shader_setLocalMatrix(JNIEnv* env, jobject o, SkShader* shader, SkiaShader* skiaShader,
+        const SkMatrix* matrix)
 {
     if (shader) {
         if (NULL == matrix) {
@@ -60,24 +79,40 @@
         else {
             shader->setLocalMatrix(*matrix);
         }
+#ifdef USE_OPENGL_RENDERER
+        skiaShader->setMatrix(const_cast<SkMatrix*>(matrix));
+#endif
     }
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static SkShader* BitmapShader_constructor(JNIEnv* env, jobject, const SkBitmap* bitmap,
+static SkShader* BitmapShader_constructor(JNIEnv* env, jobject o, const SkBitmap* bitmap,
                                           int tileModeX, int tileModeY)
 {
     SkShader* s = SkShader::CreateBitmapShader(*bitmap,
                                         (SkShader::TileMode)tileModeX,
                                         (SkShader::TileMode)tileModeY);
+
     ThrowIAE_IfNull(env, s);
     return s;
 }
+
+static SkiaShader* BitmapShader_postConstructor(JNIEnv* env, jobject o, SkShader* shader,
+        SkBitmap* bitmap, int tileModeX, int tileModeY) {
+#ifdef USE_OPENGL_RENDERER
+    SkiaShader* skiaShader = new SkiaBitmapShader(bitmap, shader,
+            static_cast<SkShader::TileMode>(tileModeX), static_cast<SkShader::TileMode>(tileModeY),
+            NULL, (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
+    return skiaShader;
+#else
+    return NULL;
+#endif
+}
     
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static SkShader* LinearGradient_create1(JNIEnv* env, jobject,
+static SkShader* LinearGradient_create1(JNIEnv* env, jobject o,
                                         float x0, float y0, float x1, float y1,
                                         jintArray colorArray, jfloatArray posArray, int tileMode)
 {
@@ -85,31 +120,95 @@
     pts[0].set(SkFloatToScalar(x0), SkFloatToScalar(y0));
     pts[1].set(SkFloatToScalar(x1), SkFloatToScalar(y1));
 
-    size_t      count = env->GetArrayLength(colorArray);
+    size_t count = env->GetArrayLength(colorArray);
     const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
 
     SkAutoSTMalloc<8, SkScalar> storage(posArray ? count : 0);
     SkScalar*                   pos = NULL;
-    
+
     if (posArray) {
         AutoJavaFloatArray autoPos(env, posArray, count);
         const float* posValues = autoPos.ptr();
         pos = (SkScalar*)storage.get();
-        for (size_t i = 0; i < count; i++)
+        for (size_t i = 0; i < count; i++) {
             pos[i] = SkFloatToScalar(posValues[i]);
+        }
     }
-
+    
     SkShader* shader = SkGradientShader::CreateLinear(pts,
                                 reinterpret_cast<const SkColor*>(colorValues),
                                 pos, count,
                                 static_cast<SkShader::TileMode>(tileMode));
-    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues),
-                                 JNI_ABORT);
+
+    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
     ThrowIAE_IfNull(env, shader);
     return shader;
 }
 
-static SkShader* LinearGradient_create2(JNIEnv* env, jobject,
+static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader* shader,
+        float x0, float y0, float x1, float y1, jintArray colorArray,
+        jfloatArray posArray, int tileMode) {
+#ifdef USE_OPENGL_RENDERER
+    size_t count = env->GetArrayLength(colorArray);
+    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
+
+    jfloat* storedBounds = new jfloat[4];
+    storedBounds[0] = x0; storedBounds[1] = y0;
+    storedBounds[2] = x1; storedBounds[3] = y1;
+    jfloat* storedPositions = new jfloat[count];
+    uint32_t* storedColors = new uint32_t[count];
+    for (size_t i = 0; i < count; i++) {
+        storedColors[i] = static_cast<uint32_t>(colorValues[i]);
+    }
+
+    if (posArray) {
+        AutoJavaFloatArray autoPos(env, posArray, count);
+        const float* posValues = autoPos.ptr();
+        for (size_t i = 0; i < count; i++) {
+            storedPositions[i] = posValues[i];
+        }
+    } else {
+        storedPositions[0] = 0.0f;
+        storedPositions[1] = 1.0f;
+    }
+
+    SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
+            storedPositions, count, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
+            (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
+
+    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
+    return skiaShader;
+#else
+    return NULL;
+#endif
+}
+
+static SkiaShader* LinearGradient_postCreate2(JNIEnv* env, jobject o, SkShader* shader,
+        float x0, float y0, float x1, float y1, int color0, int color1, int tileMode) {
+#ifdef USE_OPENGL_RENDERER
+    float* storedBounds = new float[4];
+    storedBounds[0] = x0; storedBounds[1] = y0;
+    storedBounds[2] = x1; storedBounds[3] = y1;
+
+    float* storedPositions = new float[2];
+    storedPositions[0] = 0.0f;
+    storedPositions[1] = 1.0f;
+
+    uint32_t* storedColors = new uint32_t[2];
+    storedColors[0] = static_cast<uint32_t>(color0);
+    storedColors[1] = static_cast<uint32_t>(color1);
+
+    SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
+            storedPositions, 2, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
+            (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
+
+    return skiaShader;
+#else
+    return NULL;
+#endif
+}
+
+static SkShader* LinearGradient_create2(JNIEnv* env, jobject o,
                                         float x0, float y0, float x1, float y1,
                                         int color0, int color1, int tileMode)
 {
@@ -120,18 +219,17 @@
     SkColor colors[2];
     colors[0] = color0;
     colors[1] = color1;
-
+    
     SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode);
+
     ThrowIAE_IfNull(env, s);
     return s;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static SkShader* RadialGradient_create1(JNIEnv* env, jobject,
-                                        float x, float y, float radius,
-                                        jintArray colorArray, jfloatArray posArray, int tileMode)
-{
+static SkShader* RadialGradient_create1(JNIEnv* env, jobject, float x, float y, float radius,
+        jintArray colorArray, jfloatArray posArray, int tileMode) {
     SkPoint center;
     center.set(SkFloatToScalar(x), SkFloatToScalar(y));
 
@@ -161,10 +259,8 @@
     return shader;
 }
 
-static SkShader* RadialGradient_create2(JNIEnv* env, jobject,
-                                        float x, float y, float radius,
-                                        int color0, int color1, int tileMode)
-{
+static SkShader* RadialGradient_create2(JNIEnv* env, jobject, float x, float y, float radius,
+        int color0, int color1, int tileMode) {
     SkPoint center;
     center.set(SkFloatToScalar(x), SkFloatToScalar(y));
 
@@ -178,11 +274,65 @@
     return s;
 }
 
+static SkiaShader* RadialGradient_postCreate1(JNIEnv* env, jobject o, SkShader* shader,
+        float x, float y, float radius, jintArray colorArray, jfloatArray posArray, int tileMode) {
+#ifdef USE_OPENGL_RENDERER
+    size_t count = env->GetArrayLength(colorArray);
+    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
+
+    jfloat* storedPositions = new jfloat[count];
+    uint32_t* storedColors = new uint32_t[count];
+    for (size_t i = 0; i < count; i++) {
+        storedColors[i] = static_cast<uint32_t>(colorValues[i]);
+    }
+
+    if (posArray) {
+        AutoJavaFloatArray autoPos(env, posArray, count);
+        const float* posValues = autoPos.ptr();
+        for (size_t i = 0; i < count; i++) {
+            storedPositions[i] = posValues[i];
+        }
+    } else {
+        storedPositions[0] = 0.0f;
+        storedPositions[1] = 1.0f;
+    }
+
+    SkiaShader* skiaShader = new SkiaCircularGradientShader(x, y, radius, storedColors,
+            storedPositions, count, shader, (SkShader::TileMode) tileMode, NULL,
+            (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
+
+    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
+    return skiaShader;
+#else
+    return NULL;
+#endif
+}
+
+static SkiaShader* RadialGradient_postCreate2(JNIEnv* env, jobject o, SkShader* shader,
+        float x, float y, float radius, int color0, int color1, int tileMode) {
+#ifdef USE_OPENGL_RENDERER
+    float* storedPositions = new float[2];
+    storedPositions[0] = 0.0f;
+    storedPositions[1] = 1.0f;
+
+    uint32_t* storedColors = new uint32_t[2];
+    storedColors[0] = static_cast<uint32_t>(color0);
+    storedColors[1] = static_cast<uint32_t>(color1);
+
+    SkiaShader* skiaShader = new SkiaCircularGradientShader(x, y, radius, storedColors,
+            storedPositions, 2, shader, (SkShader::TileMode) tileMode, NULL,
+            (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
+
+    return skiaShader;
+#else
+    return NULL;
+#endif
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static SkShader* SweepGradient_create1(JNIEnv* env, jobject, float x, float y,
-                                    jintArray jcolors, jfloatArray jpositions)
-{
+        jintArray jcolors, jfloatArray jpositions) {
     size_t      count = env->GetArrayLength(jcolors);
     const jint* colors = env->GetIntArrayElements(jcolors, NULL);
     
@@ -209,8 +359,7 @@
 }
 
 static SkShader* SweepGradient_create2(JNIEnv* env, jobject, float x, float y,
-                                        int color0, int color1)
-{
+        int color0, int color1) {
     SkColor colors[2];
     colors[0] = color0;
     colors[1] = color1;
@@ -220,20 +369,97 @@
     return s;
 }
 
+static SkiaShader* SweepGradient_postCreate1(JNIEnv* env, jobject o, SkShader* shader,
+        float x, float y, jintArray colorArray, jfloatArray posArray) {
+#ifdef USE_OPENGL_RENDERER
+    size_t count = env->GetArrayLength(colorArray);
+    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
+
+    jfloat* storedPositions = new jfloat[count];
+    uint32_t* storedColors = new uint32_t[count];
+    for (size_t i = 0; i < count; i++) {
+        storedColors[i] = static_cast<uint32_t>(colorValues[i]);
+    }
+
+    if (posArray) {
+        AutoJavaFloatArray autoPos(env, posArray, count);
+        const float* posValues = autoPos.ptr();
+        for (size_t i = 0; i < count; i++) {
+            storedPositions[i] = posValues[i];
+        }
+    } else {
+        storedPositions[0] = 0.0f;
+        storedPositions[1] = 1.0f;
+    }
+
+    SkiaShader* skiaShader = new SkiaSweepGradientShader(x, y, storedColors, storedPositions, count,
+            shader, NULL, (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
+
+    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
+    return skiaShader;
+#else
+    return NULL;
+#endif
+}
+
+static SkiaShader* SweepGradient_postCreate2(JNIEnv* env, jobject o, SkShader* shader,
+        float x, float y, int color0, int color1) {
+#ifdef USE_OPENGL_RENDERER
+    float* storedPositions = new float[2];
+    storedPositions[0] = 0.0f;
+    storedPositions[1] = 1.0f;
+
+    uint32_t* storedColors = new uint32_t[2];
+    storedColors[0] = static_cast<uint32_t>(color0);
+    storedColors[1] = static_cast<uint32_t>(color1);
+
+    SkiaShader* skiaShader = new SkiaSweepGradientShader(x, y, storedColors, storedPositions, 2,
+            shader, NULL, (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
+
+    return skiaShader;
+#else
+    return NULL;
+#endif
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static SkShader* ComposeShader_create1(JNIEnv* env, jobject,
-                                       SkShader* shaderA, SkShader* shaderB, SkXfermode* mode)
+static SkShader* ComposeShader_create1(JNIEnv* env, jobject o,
+        SkShader* shaderA, SkShader* shaderB, SkXfermode* mode)
 {
     return new SkComposeShader(shaderA, shaderB, mode);
 }
 
-static SkShader* ComposeShader_create2(JNIEnv* env, jobject,
-                                       SkShader* shaderA, SkShader* shaderB, SkPorterDuff::Mode mode)
+static SkShader* ComposeShader_create2(JNIEnv* env, jobject o,
+        SkShader* shaderA, SkShader* shaderB, SkPorterDuff::Mode porterDuffMode)
 {
-    SkAutoUnref au(SkPorterDuff::CreateXfermode(mode));
+    SkAutoUnref au(SkPorterDuff::CreateXfermode(porterDuffMode));
+    SkXfermode* mode = (SkXfermode*) au.get();
+    return new SkComposeShader(shaderA, shaderB, mode);
+}
 
-    return new SkComposeShader(shaderA, shaderB, (SkXfermode*)au.get());
+static SkiaShader* ComposeShader_postCreate2(JNIEnv* env, jobject o, SkShader* shader,
+        SkiaShader* shaderA, SkiaShader* shaderB, SkPorterDuff::Mode porterDuffMode) {
+#ifdef USE_OPENGL_RENDERER
+    SkXfermode::Mode mode = SkPorterDuff::ToXfermodeMode(porterDuffMode);
+    return new SkiaComposeShader(shaderA, shaderB, mode, shader);
+#else
+    return NULL;
+#endif
+}
+
+static SkiaShader* ComposeShader_postCreate1(JNIEnv* env, jobject o, SkShader* shader,
+        SkiaShader* shaderA, SkiaShader* shaderB, SkXfermode* mode) {
+#ifdef USE_OPENGL_RENDERER
+    SkXfermode::Mode skiaMode;
+    if (!SkXfermode::IsMode(mode, &skiaMode)) {
+        // TODO: Support other modes
+        skiaMode = SkXfermode::kSrcOver_Mode;
+    }
+    return new SkiaComposeShader(shaderA, shaderB, skiaMode, shader);
+#else
+    return NULL;
+#endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -244,33 +470,42 @@
 };
 
 static JNINativeMethod gShaderMethods[] = {
-    { "nativeDestructor",        "(I)V",     (void*)Shader_destructor        },
+    { "nativeDestructor",        "(II)V",    (void*)Shader_destructor        },
     { "nativeGetLocalMatrix",    "(II)Z",    (void*)Shader_getLocalMatrix    },
-    { "nativeSetLocalMatrix",    "(II)V",    (void*)Shader_setLocalMatrix    }
+    { "nativeSetLocalMatrix",    "(III)V",   (void*)Shader_setLocalMatrix    }
 };
 
 static JNINativeMethod gBitmapShaderMethods[] = {
-    { "nativeCreate",   "(III)I",  (void*)BitmapShader_constructor }
+    { "nativeCreate",     "(III)I",  (void*)BitmapShader_constructor },
+    { "nativePostCreate", "(IIII)I", (void*)BitmapShader_postConstructor }
 };
 
 static JNINativeMethod gLinearGradientMethods[] = {
-    { "nativeCreate1",  "(FFFF[I[FI)I", (void*)LinearGradient_create1   },
-    { "nativeCreate2",  "(FFFFIII)I",   (void*)LinearGradient_create2   }
+    { "nativeCreate1",     "(FFFF[I[FI)I",  (void*)LinearGradient_create1     },
+    { "nativeCreate2",     "(FFFFIII)I",    (void*)LinearGradient_create2     },
+    { "nativePostCreate1", "(IFFFF[I[FI)I", (void*)LinearGradient_postCreate1 },
+    { "nativePostCreate2", "(IFFFFIII)I",   (void*)LinearGradient_postCreate2 }
 };
 
 static JNINativeMethod gRadialGradientMethods[] = {
-    {"nativeCreate1",   "(FFF[I[FI)I",  (void*)RadialGradient_create1   },
-    {"nativeCreate2",   "(FFFIII)I",    (void*)RadialGradient_create2   }
+    { "nativeCreate1",     "(FFF[I[FI)I",  (void*)RadialGradient_create1     },
+    { "nativeCreate2",     "(FFFIII)I",    (void*)RadialGradient_create2     },
+    { "nativePostCreate1", "(IFFF[I[FI)I", (void*)RadialGradient_postCreate1 },
+    { "nativePostCreate2", "(IFFFIII)I",   (void*)RadialGradient_postCreate2 }
 };
 
 static JNINativeMethod gSweepGradientMethods[] = {
-    {"nativeCreate1",   "(FF[I[F)I",  (void*)SweepGradient_create1   },
-    {"nativeCreate2",   "(FFII)I",    (void*)SweepGradient_create2   }
+    { "nativeCreate1",     "(FF[I[F)I",  (void*)SweepGradient_create1     },
+    { "nativeCreate2",     "(FFII)I",    (void*)SweepGradient_create2     },
+    { "nativePostCreate1", "(IFF[I[F)I", (void*)SweepGradient_postCreate1 },
+    { "nativePostCreate2", "(IFFII)I",   (void*)SweepGradient_postCreate2 }
 };
 
 static JNINativeMethod gComposeShaderMethods[] = {
-    {"nativeCreate1",  "(III)I",    (void*)ComposeShader_create1 },
-    {"nativeCreate2",  "(III)I",    (void*)ComposeShader_create2 }
+    { "nativeCreate1",      "(III)I",   (void*)ComposeShader_create1     },
+    { "nativeCreate2",      "(III)I",   (void*)ComposeShader_create2     },
+    { "nativePostCreate1",  "(IIII)I",  (void*)ComposeShader_postCreate1 },
+    { "nativePostCreate2",  "(IIII)I",  (void*)ComposeShader_postCreate2 }
 };
 
 #include <android_runtime/AndroidRuntime.h>
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
new file mode 100644
index 0000000..147e1fa
--- /dev/null
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TextLayout.h"
+
+#include <android_runtime/AndroidRuntime.h>
+
+#include "SkTemplates.h"
+#include "unicode/ubidi.h"
+#include "unicode/ushape.h"
+#include <utils/Log.h>
+
+
+namespace android {
+// Returns true if we might need layout.  If bidiFlags force LTR, assume no layout, if
+// bidiFlags indicate there probably is RTL, assume we do, otherwise scan the text
+// looking for a character >= the first RTL character in unicode and assume we do if
+// we find one.
+bool TextLayout::needsLayout(const jchar* text, jint len, jint bidiFlags) {
+    if (bidiFlags == kBidi_Force_LTR) {
+        return false;
+    }
+    if ((bidiFlags == kBidi_RTL) || (bidiFlags == kBidi_Default_RTL) ||
+            bidiFlags == kBidi_Force_RTL) {
+        return true;
+    }
+    for (int i = 0; i < len; ++i) {
+        if (text[i] >= 0x0590) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * Character-based Arabic shaping.
+ *
+ * We'll use harfbuzz and glyph-based shaping instead once we're set up for it.
+ *
+ * @context the text context
+ * @start the start of the text to render
+ * @count the length of the text to render, start + count  must be <= contextCount
+ * @contextCount the length of the context
+ * @shaped where to put the shaped text, must have capacity for count uchars
+ * @return the length of the shaped text, or -1 if error
+ */
+int TextLayout::shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount,
+                        jchar* shaped, UErrorCode &status) {
+    jchar buffer[contextCount];
+
+    // Use fixed length since we need to keep start and count valid
+    u_shapeArabic(context, contextCount, buffer, contextCount,
+                   U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
+                   U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
+                   U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
+
+    if (U_SUCCESS(status)) {
+        // trim out 0xffff following ligatures, if any
+        int end = 0;
+        for (int i = start, e = start + count; i < e; ++i) {
+            if (buffer[i] != 0xffff) {
+                buffer[end++] = buffer[i];
+            }
+        }
+        count = end;
+        // LOG(LOG_INFO, "CSRTL", "start %d count %d ccount %d\n", start, count, contextCount);
+        ubidi_writeReverse(buffer, count, shaped, count, UBIDI_DO_MIRRORING | UBIDI_OUTPUT_REVERSE
+                           | UBIDI_KEEP_BASE_COMBINING, &status);
+        if (U_SUCCESS(status)) {
+            return count;
+        }
+    }
+
+    return -1;
+}
+
+/**
+ * Basic character-based layout supporting rtl and arabic shaping.
+ * Runs bidi on the text and generates a reordered, shaped line in buffer, returning
+ * the length.
+ * @text the text
+ * @len the length of the text in uchars
+ * @dir receives the resolved paragraph direction
+ * @buffer the buffer to receive the reordered, shaped line.  Must have capacity of
+ * at least len jchars.
+ * @flags line bidi flags
+ * @return the length of the reordered, shaped line, or -1 if error
+ */
+jint TextLayout::layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer,
+        UErrorCode &status) {
+    static const int RTL_OPTS = UBIDI_DO_MIRRORING | UBIDI_KEEP_BASE_COMBINING |
+            UBIDI_REMOVE_BIDI_CONTROLS | UBIDI_OUTPUT_REVERSE;
+
+    UBiDiLevel bidiReq = 0;
+    switch (flags) {
+    case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
+    case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
+    case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
+    case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
+    case kBidi_Force_LTR: memcpy(buffer, text, len * sizeof(jchar)); return len;
+    case kBidi_Force_RTL: return shapeRtlText(text, 0, len, len, buffer, status);
+    }
+
+    int32_t result = -1;
+
+    UBiDi* bidi = ubidi_open();
+    if (bidi) {
+        ubidi_setPara(bidi, text, len, bidiReq, NULL, &status);
+        if (U_SUCCESS(status)) {
+            dir = ubidi_getParaLevel(bidi) & 0x1; // 0 if ltr, 1 if rtl
+
+            int rc = ubidi_countRuns(bidi, &status);
+            if (U_SUCCESS(status)) {
+                // LOG(LOG_INFO, "LAYOUT", "para bidiReq=%d dir=%d rc=%d\n", bidiReq, dir, rc);
+
+                int32_t slen = 0;
+                for (int i = 0; i < rc; ++i) {
+                    int32_t start;
+                    int32_t length;
+                    UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &start, &length);
+
+                    if (runDir == UBIDI_RTL) {
+                        slen += shapeRtlText(text + start, 0, length, length, buffer + slen, status);
+                    } else {
+                        memcpy(buffer + slen, text + start, length * sizeof(jchar));
+                        slen += length;
+                    }
+                }
+                if (U_SUCCESS(status)) {
+                    result = slen;
+                }
+            }
+        }
+        ubidi_close(bidi);
+    }
+
+    return result;
+}
+
+bool TextLayout::prepareText(SkPaint *paint, const jchar* text, jsize len, jint bidiFlags,
+        const jchar** outText, int32_t* outBytes, jchar** outBuffer) {
+    const jchar *workText = text;
+    jchar *buffer = NULL;
+    int dir = kDirection_LTR;
+    if (needsLayout(text, len, bidiFlags)) {
+        buffer =(jchar *) malloc(len * sizeof(jchar));
+        if (!buffer) {
+            return false;
+        }
+        UErrorCode status = U_ZERO_ERROR;
+        len = layoutLine(text, len, bidiFlags, dir, buffer, status); // might change len, dir
+        if (!U_SUCCESS(status)) {
+            LOG(LOG_WARN, "LAYOUT", "drawText error %d\n", status);
+            free(buffer);
+            return false; // can't render
+        }
+
+        workText = buffer; // use the shaped text
+    }
+
+    bool trimLeft = false;
+    bool trimRight = false;
+
+    SkPaint::Align horiz = paint->getTextAlign();
+    switch (horiz) {
+        case SkPaint::kLeft_Align: trimLeft = dir & kDirection_Mask; break;
+        case SkPaint::kCenter_Align: trimLeft = trimRight = true; break;
+        case SkPaint::kRight_Align: trimRight = !(dir & kDirection_Mask);
+        default: break;
+    }
+    const jchar* workLimit = workText + len;
+
+    if (trimLeft) {
+        while (workText < workLimit && *workText == ' ') {
+            ++workText;
+        }
+    }
+    if (trimRight) {
+        while (workLimit > workText && *(workLimit - 1) == ' ') {
+            --workLimit;
+        }
+    }
+
+    *outBytes = (workLimit - workText) << 1;
+    *outText = workText;
+    *outBuffer = buffer;
+
+    return true;
+}
+
+// Draws or gets the path of a paragraph of text on a single line, running bidi and shaping.
+// This will draw if canvas is not null, otherwise path must be non-null and it will create
+// a path representing the text that would have been drawn.
+void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len,
+                            jint bidiFlags, jfloat x, jfloat y,SkCanvas *canvas, SkPath *path) {
+    const jchar *workText;
+    jchar *buffer = NULL;
+    int32_t workBytes;
+    if (prepareText(paint, text, len, bidiFlags, &workText, &workBytes, &buffer)) {
+        SkScalar x_ = SkFloatToScalar(x);
+        SkScalar y_ = SkFloatToScalar(y);
+        if (canvas) {
+            canvas->drawText(workText, workBytes, x_, y_, *paint);
+        } else {
+            paint->getTextPath(workText, workBytes, x_, y_, path);
+        }
+        free(buffer);
+    }
+}
+
+bool TextLayout::prepareRtlTextRun(const jchar* context, jsize start, jsize& count,
+        jsize contextCount, jchar* shaped) {
+    UErrorCode status = U_ZERO_ERROR;
+    count = shapeRtlText(context, start, count, contextCount, shaped, status);
+    if (U_SUCCESS(status)) {
+        return true;
+    } else {
+        LOG(LOG_WARN, "LAYOUT", "drawTextRun error %d\n", status);
+    }
+    return false;
+}
+
+void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars,
+                             jint start, jint count, jint contextCount,
+                             int dirFlags, jfloat x, jfloat y, SkCanvas* canvas) {
+
+     SkScalar x_ = SkFloatToScalar(x);
+     SkScalar y_ = SkFloatToScalar(y);
+
+     uint8_t rtl = dirFlags & 0x1;
+     if (rtl) {
+         SkAutoSTMalloc<80, jchar> buffer(contextCount);
+         if (prepareRtlTextRun(chars, start, count, contextCount, buffer.get())) {
+             canvas->drawText(buffer.get(), count << 1, x_, y_, *paint);
+         }
+     } else {
+         canvas->drawText(chars + start, count << 1, x_, y_, *paint);
+     }
+ }
+
+void TextLayout::getTextRunAdvances(SkPaint *paint, const jchar *chars, jint start,
+                                    jint count, jint contextCount, jint dirFlags,
+                                    jfloat *resultAdvances, jfloat &resultTotalAdvance) {
+    jchar buffer[contextCount];
+
+    SkScalar* scalarArray = (SkScalar *)resultAdvances;
+    resultTotalAdvance = 0;
+
+    // this is where we'd call harfbuzz
+    // for now we just use ushape.c
+
+    int widths;
+    const jchar* text;
+    if (dirFlags & 0x1) { // rtl, call arabic shaping in case
+        UErrorCode status = U_ZERO_ERROR;
+        // Use fixed length since we need to keep start and count valid
+        u_shapeArabic(chars, contextCount, buffer, contextCount,
+                      U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
+                      U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
+                      U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
+        // we shouldn't fail unless there's an out of memory condition,
+        // in which case we're hosed anyway
+        for (int i = start, e = i + count; i < e; ++i) {
+          if (buffer[i] == 0xffff) {
+            buffer[i] = 0x200b; // zero-width-space for skia
+          }
+        }
+        text = buffer + start;
+        widths = paint->getTextWidths(text, count << 1, scalarArray);
+    } else {
+        text = chars + start;
+        widths = paint->getTextWidths(text, count << 1, scalarArray);
+    }
+
+    if (widths < count) {
+        // Skia operates on code points, not code units, so surrogate pairs return only
+        // one value. Expand the result so we have one value per UTF-16 code unit.
+
+        // Note, skia's getTextWidth gets confused if it encounters a surrogate pair,
+        // leaving the remaining widths zero.  Not nice.
+        for (int i = 0, p = 0; i < widths; ++i) {
+            resultTotalAdvance += resultAdvances[p++] = SkScalarToFloat(scalarArray[i]);
+            if (p < count && text[p] >= 0xdc00 && text[p] < 0xe000 &&
+                    text[p-1] >= 0xd800 && text[p-1] < 0xdc00) {
+                resultAdvances[p++] = 0;
+            }
+        }
+    } else {
+        for (int i = 0; i < count; i++) {
+            resultTotalAdvance += resultAdvances[i] = SkScalarToFloat(scalarArray[i]);
+        }
+    }
+}
+
+
+// Draws a paragraph of text on a single line, running bidi and shaping
+void TextLayout::drawText(SkPaint* paint, const jchar* text, jsize len,
+                          int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas) {
+
+    handleText(paint, text, len, bidiFlags, x, y, canvas, NULL);
+}
+
+void TextLayout::getTextPath(SkPaint *paint, const jchar *text, jsize len,
+                             jint bidiFlags, jfloat x, jfloat y, SkPath *path) {
+    handleText(paint, text, len, bidiFlags, x, y, NULL, path);
+}
+
+
+void TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count,
+                                int bidiFlags, jfloat hOffset, jfloat vOffset,
+                                SkPath* path, SkCanvas* canvas) {
+
+    SkScalar h_ = SkFloatToScalar(hOffset);
+    SkScalar v_ = SkFloatToScalar(vOffset);
+
+    if (!needsLayout(text, count, bidiFlags)) {
+        canvas->drawTextOnPathHV(text, count << 1, *path, h_, v_, *paint);
+        return;
+    }
+
+    SkAutoSTMalloc<80, jchar> buffer(count);
+    int dir = kDirection_LTR;
+    UErrorCode status = U_ZERO_ERROR;
+    count = layoutLine(text, count, bidiFlags, dir, buffer.get(), status);
+    if (U_SUCCESS(status)) {
+        canvas->drawTextOnPathHV(buffer.get(), count << 1, *path, h_, v_, *paint);
+    }
+}
+
+}
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
new file mode 100644
index 0000000..8f666c0
--- /dev/null
+++ b/core/jni/android/graphics/TextLayout.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "unicode/utypes.h"
+
+namespace android {
+
+class TextLayout {
+public:
+
+    enum {
+        kDirection_LTR = 0,
+        kDirection_RTL = 1,
+
+        kDirection_Mask = 0x1
+    };
+
+    enum {
+        kBidi_LTR = 0,
+        kBidi_RTL = 1,
+        kBidi_Default_LTR = 2,
+        kBidi_Default_RTL = 3,
+        kBidi_Force_LTR = 4,
+        kBidi_Force_RTL = 5,
+
+        kBidi_Mask = 0x7
+    };
+
+    /*
+     * Draws a unidirectional run of text.
+     */
+    static void drawTextRun(SkPaint* paint, const jchar* chars,
+                            jint start, jint count, jint contextCount,
+                            int dirFlags, jfloat x, jfloat y, SkCanvas* canvas);
+
+    static void getTextRunAdvances(SkPaint *paint, const jchar *chars, jint start,
+                                   jint count, jint contextCount, jint dirFlags,
+                                   jfloat *resultAdvances, jfloat &resultTotalAdvance);
+
+    static void drawText(SkPaint* paint, const jchar* text, jsize len,
+                         jint bidiFlags, jfloat x, jfloat y, SkCanvas* canvas);
+
+    static void getTextPath(SkPaint *paint, const jchar *text, jsize len,
+                            jint bidiFlags, jfloat x, jfloat y, SkPath *path);
+
+    static void drawTextOnPath(SkPaint* paint, const jchar* text, jsize len,
+                               int bidiFlags, jfloat hOffset, jfloat vOffset,
+                               SkPath* path, SkCanvas* canvas);
+                               
+   static bool prepareText(SkPaint *paint, const jchar* text, jsize len, jint bidiFlags,
+        const jchar** outText, int32_t* outBytes, jchar** outBuffer);
+    static bool prepareRtlTextRun(const jchar* context, jsize start, jsize& count,
+        jsize contextCount, jchar* shaped);
+        
+
+private:
+    static bool needsLayout(const jchar* text, jint len, jint bidiFlags);
+    static int shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount,
+                            jchar* shaped, UErrorCode &status);
+    static jint layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer,
+                           UErrorCode &status);
+    static void handleText(SkPaint *paint, const jchar* text, jsize len,
+                           int bidiFlags, jfloat x, jfloat y,SkCanvas *canvas, SkPath *path);
+};
+
+}
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 7c7bfeb..1fe72e6 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -130,7 +130,13 @@
         return NULL;
     }
     
-    return SkTypeface::CreateFromStream(new AssetStream(asset, true));
+    SkStream* stream = new AssetStream(asset, true);
+    SkTypeface* face = SkTypeface::CreateFromStream(stream);
+    // SkTypeFace::CreateFromStream calls ref() on the stream, so we
+    // need to unref it here or it won't be freed later on
+    stream->unref();
+
+    return face;
 }
 
 static SkTypeface* Typeface_createFromFile(JNIEnv* env, jobject, jstring jpath) {
diff --git a/core/jni/android_animation_PropertyValuesHolder.cpp b/core/jni/android_animation_PropertyValuesHolder.cpp
new file mode 100644
index 0000000..5991805
--- /dev/null
+++ b/core/jni/android_animation_PropertyValuesHolder.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "jni.h"
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/misc.h>
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/animation/PropertyValuesHolder";
+
+static jmethodID android_animation_PropertyValuesHolder_getIntMethod(
+        JNIEnv* env, jclass pvhClass, jclass targetClass, jstring methodName)
+{
+    const char *nativeString = env->GetStringUTFChars(methodName, 0);
+    jmethodID mid = env->GetMethodID(targetClass, nativeString, "(I)V");
+    env->ReleaseStringUTFChars(methodName, nativeString);
+    return mid;
+}
+
+static jmethodID android_animation_PropertyValuesHolder_getFloatMethod(
+        JNIEnv* env, jclass pvhClass, jclass targetClass, jstring methodName)
+{
+    const char *nativeString = env->GetStringUTFChars(methodName, 0);
+    jmethodID mid = env->GetMethodID(targetClass, nativeString, "(F)V");
+    env->ReleaseStringUTFChars(methodName, nativeString);
+    return mid;
+}
+
+static void android_animation_PropertyValuesHolder_callIntMethod(
+        JNIEnv* env, jclass pvhObject, jobject target, jmethodID methodID, int arg)
+{
+    env->CallVoidMethod(target, methodID, arg);
+}
+
+static void android_animation_PropertyValuesHolder_callFloatMethod(
+        JNIEnv* env, jclass pvhObject, jobject target, jmethodID methodID, float arg)
+{
+    env->CallVoidMethod(target, methodID, arg);
+}
+
+static JNINativeMethod gMethods[] = {
+    {   "nGetIntMethod", "(Ljava/lang/Class;Ljava/lang/String;)I",
+            (void*)android_animation_PropertyValuesHolder_getIntMethod },
+    {   "nGetFloatMethod", "(Ljava/lang/Class;Ljava/lang/String;)I",
+            (void*)android_animation_PropertyValuesHolder_getFloatMethod },
+    {   "nCallIntMethod", "(Ljava/lang/Object;II)V",
+            (void*)android_animation_PropertyValuesHolder_callIntMethod },
+    {   "nCallFloatMethod", "(Ljava/lang/Object;IF)V",
+            (void*)android_animation_PropertyValuesHolder_callFloatMethod }
+};
+
+int register_android_animation_PropertyValuesHolder(JNIEnv* env)
+{
+    return AndroidRuntime::registerNativeMethods(env,
+            kClassPathName, gMethods, NELEM(gMethods));
+}
+
+};
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 45fd5a0..a586f49 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -1029,7 +1029,7 @@
 
 #define FIND_CLASS(var, className) \
         var = env->FindClass(className); \
-        LOG_FATAL_IF(! var, "Unable to find class " className); \
+        LOG_FATAL_IF(! var, "Unable to find class %s", className); \
         var = jclass(env->NewGlobalRef(var));
 
 #define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
diff --git a/core/jni/android_bluetooth_ScoSocket.cpp b/core/jni/android_bluetooth_ScoSocket.cpp
deleted file mode 100644
index 94e4409..0000000
--- a/core/jni/android_bluetooth_ScoSocket.cpp
+++ /dev/null
@@ -1,689 +0,0 @@
-/*
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "bluetooth_ScoSocket.cpp"
-
-#include "android_bluetooth_common.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "JNIHelp.h"
-#include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/poll.h>
-
-#ifdef HAVE_BLUETOOTH
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sco.h>
-#include <bluetooth/hci.h>
-
-#define MAX_LINE 255
-
-/*
- * Defines the module strings used in the blacklist file.
- * These are used by consumers of the blacklist file to see if the line is
- * used by that module.
- */
-#define SCO_BLACKLIST_MODULE_NAME "scoSocket"
-
-
-/* Define the type strings used in the blacklist file. */
-#define BLACKLIST_BY_NAME "name"
-#define BLACKLIST_BY_PARTIAL_NAME "partial_name"
-#define BLACKLIST_BY_OUI "vendor_oui"
-
-#endif
-
-/* Ideally, blocking I/O on a SCO socket would return when another thread
- * calls close(). However it does not right now, in fact close() on a SCO
- * socket has strange behavior (returns a bogus value) when other threads
- * are performing blocking I/O on that socket. So, to workaround, we always
- * call close() from the same thread that does blocking I/O. This requires the
- * use of a socketpair to signal the blocking I/O to abort.
- *
- * Unfortunately I don't know a way to abort connect() yet, but at least this
- * times out after the BT page timeout (10 seconds currently), so the thread
- * will die eventually. The fact that the thread can outlive
- * the Java object forces us to use a mutex in destoryNative().
- *
- * The JNI API is entirely async.
- *
- * Also note this class deals only with SCO connections, not with data
- * transmission.
- */
-namespace android {
-#ifdef HAVE_BLUETOOTH
-
-static JavaVM *jvm;
-static jfieldID field_mNativeData;
-static jmethodID method_onAccepted;
-static jmethodID method_onConnected;
-static jmethodID method_onClosed;
-
-struct thread_data_t;
-static void *work_thread(void *arg);
-static int connect_work(const char *address, uint16_t sco_pkt_type);
-static int accept_work(int signal_sk);
-static void wait_for_close(int sk, int signal_sk);
-static void closeNative(JNIEnv *env, jobject object);
-
-static void parseBlacklist(void);
-static uint16_t getScoType(char *address, const char *name);
-
-#define COMPARE_STRING(key, s) (!strncmp(key, s, strlen(s)))
-
-/* Blacklist data */
-typedef struct scoBlacklist {
-    int fieldType;
-    char *value;
-    uint16_t scoType;
-    struct scoBlacklist *next;
-} scoBlacklist_t;
-
-#define BL_TYPE_NAME 1   // Field type is name string
-
-static scoBlacklist_t *blacklist = NULL;
-
-/* shared native data - protected by mutex */
-typedef struct {
-    pthread_mutex_t mutex;
-    int signal_sk;        // socket to signal blocked I/O to unblock
-    jobject object;       // JNI global ref to the Java object
-    thread_data_t *thread_data;  // pointer to thread local data
-                                 // max 1 thread per sco socket
-} native_data_t;
-
-/* thread local data */
-struct thread_data_t {
-    native_data_t *nat;
-    bool is_accept;        // accept (listening) or connect (outgoing) thread
-    int signal_sk;         // socket for thread to listen for unblock signal
-    char address[BTADDR_SIZE];  // BT addres as string
-    uint16_t sco_pkt_type;   // SCO packet types supported
-};
-
-static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
-    return (native_data_t *)(env->GetIntField(object, field_mNativeData));
-}
-
-static uint16_t str2scoType (char *key) {
-    LOGV("%s: key = %s", __FUNCTION__, key);
-    if (COMPARE_STRING(key, "ESCO_HV1"))
-        return ESCO_HV1;
-    if (COMPARE_STRING(key, "ESCO_HV2"))
-        return ESCO_HV2;
-    if (COMPARE_STRING(key, "ESCO_HV3"))
-        return ESCO_HV3;
-    if (COMPARE_STRING(key, "ESCO_EV3"))
-        return ESCO_EV3;
-    if (COMPARE_STRING(key, "ESCO_EV4"))
-        return ESCO_EV4;
-    if (COMPARE_STRING(key, "ESCO_EV5"))
-        return ESCO_EV5;
-    if (COMPARE_STRING(key, "ESCO_2EV3"))
-        return ESCO_2EV3;
-    if (COMPARE_STRING(key, "ESCO_3EV3"))
-        return ESCO_3EV3;
-    if (COMPARE_STRING(key, "ESCO_2EV5"))
-        return ESCO_2EV5;
-    if (COMPARE_STRING(key, "ESCO_3EV5"))
-        return ESCO_3EV5;
-    if (COMPARE_STRING(key, "SCO_ESCO_MASK"))
-        return SCO_ESCO_MASK;
-    if (COMPARE_STRING(key, "EDR_ESCO_MASK"))
-        return EDR_ESCO_MASK;
-    if (COMPARE_STRING(key, "ALL_ESCO_MASK"))
-        return ALL_ESCO_MASK;
-    LOGE("Unknown SCO Type (%s) skipping",key);
-    return 0;
-}
-
-static void parseBlacklist(void) {
-    const char *filename = "/etc/bluetooth/blacklist.conf";
-    char line[MAX_LINE];
-    scoBlacklist_t *list = NULL;
-    scoBlacklist_t *newelem;
-
-    LOGV(__FUNCTION__);
-
-    /* Open file */
-    FILE *fp = fopen(filename, "r");
-    if(!fp) {
-        LOGE("Error(%s)opening blacklist file", strerror(errno));
-        return;
-    }
-
-    while (fgets(line, MAX_LINE, fp) != NULL) {
-        if ((COMPARE_STRING(line, "//")) || (!strcmp(line, "")))
-            continue;
-        char *module = strtok(line,":");
-        if (COMPARE_STRING(module, SCO_BLACKLIST_MODULE_NAME)) {
-            newelem = (scoBlacklist_t *)calloc(1, sizeof(scoBlacklist_t));
-            if (newelem == NULL) {
-                LOGE("%s: out of memory!", __FUNCTION__);
-                return;
-            }
-            // parse line
-            char *type = strtok(NULL, ",");
-            char *valueList = strtok(NULL, ",");
-            char *paramList = strtok(NULL, ",");
-            if (COMPARE_STRING(type, BLACKLIST_BY_NAME)) {
-                // Extract Name from Value list
-                newelem->fieldType = BL_TYPE_NAME;
-                newelem->value = (char *)calloc(1, strlen(valueList));
-                if (newelem->value == NULL) {
-                    LOGE("%s: out of memory!", __FUNCTION__);
-                    continue;
-                }
-                valueList++;  // Skip open quote
-                strncpy(newelem->value, valueList, strlen(valueList) - 1);
-
-                // Get Sco Settings from Parameters
-                char *param = strtok(paramList, ";");
-                uint16_t scoTypes = 0;
-                while (param != NULL) {
-                    uint16_t sco;
-                    if (param[0] == '-') {
-                        param++;
-                        sco = str2scoType(param);
-                        if (sco != 0)
-                            scoTypes &= ~sco;
-                    } else if (param[0] == '+') {
-                        param++;
-                        sco = str2scoType(param);
-                        if (sco != 0)
-                            scoTypes |= sco;
-                    } else if (param[0] == '=') {
-                        param++;
-                        sco = str2scoType(param);
-                        if (sco != 0)
-                            scoTypes = sco;
-                    } else {
-                        LOGE("Invalid SCO type must be =, + or -");
-                    }
-                    param = strtok(NULL, ";");
-                }
-                newelem->scoType = scoTypes;
-            } else {
-                LOGE("Unknown SCO type entry in Blacklist file");
-                continue;
-            }
-            if (list) {
-                list->next = newelem;
-                list = newelem;
-            } else {
-                blacklist = list = newelem;
-            }
-            LOGI("Entry name = %s ScoTypes = 0x%x", newelem->value,
-                 newelem->scoType);
-        }
-    }
-    fclose(fp);
-    return;
-}
-static uint16_t getScoType(char *address, const char *name) {
-    uint16_t ret = 0;
-    scoBlacklist_t *list = blacklist;
-
-    while (list != NULL) {
-        if (list->fieldType == BL_TYPE_NAME) {
-            if (COMPARE_STRING(name, list->value)) {
-                ret = list->scoType;
-                break;
-            }
-        }
-        list = list->next;
-    }
-    LOGI("%s %s - 0x%x",  __FUNCTION__, name, ret);
-    return ret;
-}
-#endif
-
-static void classInitNative(JNIEnv* env, jclass clazz) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    if (env->GetJavaVM(&jvm) < 0) {
-        LOGE("Could not get handle to the VM");
-    }
-    field_mNativeData = get_field(env, clazz, "mNativeData", "I");
-    method_onAccepted = env->GetMethodID(clazz, "onAccepted", "(I)V");
-    method_onConnected = env->GetMethodID(clazz, "onConnected", "(I)V");
-    method_onClosed = env->GetMethodID(clazz, "onClosed", "()V");
-
-    /* Read the blacklist file in here */
-    parseBlacklist();
-#endif
-}
-
-/* Returns false if a serious error occured */
-static jboolean initNative(JNIEnv* env, jobject object) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-
-    native_data_t *nat = (native_data_t *) calloc(1, sizeof(native_data_t));
-    if (nat == NULL) {
-        LOGE("%s: out of memory!", __FUNCTION__);
-        return JNI_FALSE;
-    }
-
-    pthread_mutex_init(&nat->mutex, NULL);
-    env->SetIntField(object, field_mNativeData, (jint)nat);
-    nat->signal_sk = -1;
-    nat->object = NULL;
-    nat->thread_data = NULL;
-
-#endif
-    return JNI_TRUE;
-}
-
-static void destroyNative(JNIEnv* env, jobject object) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    
-    closeNative(env, object);
-    
-    pthread_mutex_lock(&nat->mutex);
-    if (nat->thread_data != NULL) {
-        nat->thread_data->nat = NULL;
-    }
-    pthread_mutex_unlock(&nat->mutex);
-    pthread_mutex_destroy(&nat->mutex);
-
-    free(nat);
-#endif
-}
-
-static jboolean acceptNative(JNIEnv *env, jobject object) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    int signal_sks[2];
-    pthread_t thread;
-    struct thread_data_t *data = NULL;
-
-    pthread_mutex_lock(&nat->mutex);
-    if (nat->signal_sk != -1) {
-        pthread_mutex_unlock(&nat->mutex);
-        return JNI_FALSE;
-    }
-
-    // setup socketpair to pass messages between threads
-    if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_sks) < 0) {
-        LOGE("%s: socketpair() failed: %s", __FUNCTION__, strerror(errno));
-        pthread_mutex_unlock(&nat->mutex);
-        return JNI_FALSE;
-    }
-    nat->signal_sk = signal_sks[0];
-    nat->object = env->NewGlobalRef(object);
-
-    data = (thread_data_t *)calloc(1, sizeof(thread_data_t));
-    if (data == NULL) {
-        LOGE("%s: out of memory", __FUNCTION__);
-        pthread_mutex_unlock(&nat->mutex);
-        return JNI_FALSE;
-    }
-    nat->thread_data = data;
-    pthread_mutex_unlock(&nat->mutex);
-
-    data->signal_sk = signal_sks[1];
-    data->nat = nat;
-    data->is_accept = true;
-
-    if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) {
-        LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno));
-        return JNI_FALSE;
-    }
-    return JNI_TRUE;
-
-#endif
-    return JNI_FALSE;
-}
-
-static jboolean connectNative(JNIEnv *env, jobject object, jstring address,
-        jstring name) {
-
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    int signal_sks[2];
-    pthread_t thread;
-    struct thread_data_t *data;
-    const char *c_address;
-    const char *c_name;
-
-    pthread_mutex_lock(&nat->mutex);
-    if (nat->signal_sk != -1) {
-        pthread_mutex_unlock(&nat->mutex);
-        return JNI_FALSE;
-    }
-
-    // setup socketpair to pass messages between threads
-    if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_sks) < 0) {
-        LOGE("%s: socketpair() failed: %s\n", __FUNCTION__, strerror(errno));
-        pthread_mutex_unlock(&nat->mutex);
-        return JNI_FALSE;
-    }
-    nat->signal_sk = signal_sks[0];
-    nat->object = env->NewGlobalRef(object);
-
-    data = (thread_data_t *)calloc(1, sizeof(thread_data_t));
-    if (data == NULL) {
-        LOGE("%s: out of memory", __FUNCTION__);
-        pthread_mutex_unlock(&nat->mutex);
-        return JNI_FALSE;
-    }
-    pthread_mutex_unlock(&nat->mutex);
-
-    data->signal_sk = signal_sks[1];
-    data->nat = nat;
-    c_address = env->GetStringUTFChars(address, NULL);
-    strlcpy(data->address, c_address, BTADDR_SIZE);
-    env->ReleaseStringUTFChars(address, c_address);
-    data->is_accept = false;
-
-    if (name == NULL) {
-        LOGE("%s: Null pointer passed in for device name", __FUNCTION__);
-        data->sco_pkt_type = 0;
-    } else {
-        c_name = env->GetStringUTFChars(name, NULL);
-        /* See if this device is in the black list */
-        data->sco_pkt_type = getScoType(data->address, c_name);
-        env->ReleaseStringUTFChars(name, c_name);
-    }
-    if (pthread_create(&thread, NULL, &work_thread, (void *)data) < 0) {
-        LOGE("%s: pthread_create() failed: %s", __FUNCTION__, strerror(errno));
-        return JNI_FALSE;
-    }
-    return JNI_TRUE;
-
-#endif
-    return JNI_FALSE;
-}
-
-static void closeNative(JNIEnv *env, jobject object) {
-    LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
-    int signal_sk;
-
-    pthread_mutex_lock(&nat->mutex);
-    signal_sk = nat->signal_sk;
-    nat->signal_sk = -1;
-    env->DeleteGlobalRef(nat->object);
-    nat->object = NULL;
-    pthread_mutex_unlock(&nat->mutex);
-
-    if (signal_sk >= 0) {
-        LOGV("%s: signal_sk = %d", __FUNCTION__, signal_sk);
-        unsigned char dummy;
-        write(signal_sk, &dummy, sizeof(dummy));
-        close(signal_sk);
-    }
-#endif
-}
-
-#ifdef HAVE_BLUETOOTH
-/* thread entry point */
-static void *work_thread(void *arg) {
-    JNIEnv* env;
-    thread_data_t *data = (thread_data_t *)arg;
-    int sk;
-
-    LOGV(__FUNCTION__);
-    if (jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
-        LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
-        return NULL;
-    }
-
-    /* connect the SCO socket */
-    if (data->is_accept) {
-        LOGV("SCO OBJECT %p ACCEPT #####", data->nat->object);
-        sk = accept_work(data->signal_sk);
-        LOGV("SCO OBJECT %p END ACCEPT *****", data->nat->object);
-    } else {
-        sk = connect_work(data->address, data->sco_pkt_type);
-    }
-
-    /* callback with connection result */
-    if (data->nat == NULL) {
-        LOGV("%s: object destroyed!", __FUNCTION__);
-        goto done;
-    }
-    pthread_mutex_lock(&data->nat->mutex);
-    if (data->nat->object == NULL) {
-        pthread_mutex_unlock(&data->nat->mutex);
-        LOGV("%s: callback cancelled", __FUNCTION__);
-        goto done;
-    }
-    if (data->is_accept) {
-        env->CallVoidMethod(data->nat->object, method_onAccepted, sk);
-    } else {
-        env->CallVoidMethod(data->nat->object, method_onConnected, sk);
-    }
-    pthread_mutex_unlock(&data->nat->mutex);
-
-    if (sk < 0) {
-        goto done;
-    }
-
-    LOGV("SCO OBJECT %p %d CONNECTED +++ (%s)", data->nat->object, sk,
-         data->is_accept ? "in" : "out");
-
-    /* wait for the socket to close */
-    LOGV("wait_for_close()...");
-    wait_for_close(sk, data->signal_sk);
-    LOGV("wait_for_close() returned");
-
-    /* callback with close result */
-    if (data->nat == NULL) {
-        LOGV("%s: object destroyed!", __FUNCTION__);
-        goto done;
-    }
-    pthread_mutex_lock(&data->nat->mutex);
-    if (data->nat->object == NULL) {
-        LOGV("%s: callback cancelled", __FUNCTION__);
-    } else {
-        env->CallVoidMethod(data->nat->object, method_onClosed);
-    }
-    pthread_mutex_unlock(&data->nat->mutex);
-
-done:
-    if (sk >= 0) {
-        close(sk);
-        LOGV("SCO OBJECT %p %d CLOSED --- (%s)", data->nat->object, sk, data->is_accept ? "in" : "out");
-    }
-    if (data->signal_sk >= 0) {
-        close(data->signal_sk);
-    }
-    LOGV("SCO socket closed");
-
-    if (data->nat != NULL) {
-        pthread_mutex_lock(&data->nat->mutex);
-        env->DeleteGlobalRef(data->nat->object);
-        data->nat->object = NULL;
-        data->nat->thread_data = NULL;
-        pthread_mutex_unlock(&data->nat->mutex);
-    }
-
-    free(data);
-    if (jvm->DetachCurrentThread() != JNI_OK) {
-        LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
-    }
-
-    LOGV("work_thread() done");
-    return NULL;
-}
-
-static int accept_work(int signal_sk) {
-    LOGV(__FUNCTION__);
-    int sk;
-    int nsk;
-    int addr_sz;
-    int max_fd;
-    fd_set fds;
-    struct sockaddr_sco addr;
-
-    sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
-    if (sk < 0) {
-        LOGE("%s socket() failed: %s", __FUNCTION__, strerror(errno));
-        return -1;
-    }
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sco_family = AF_BLUETOOTH;
-    memcpy(&addr.sco_bdaddr, BDADDR_ANY, sizeof(bdaddr_t));
-    if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        LOGE("%s bind() failed: %s", __FUNCTION__, strerror(errno));
-        goto error;
-    }
-
-    if (listen(sk, 1)) {
-        LOGE("%s: listen() failed: %s", __FUNCTION__, strerror(errno));
-        goto error;
-    }
-
-    memset(&addr, 0, sizeof(addr));
-    addr_sz = sizeof(addr);
-
-    FD_ZERO(&fds);
-    FD_SET(sk, &fds);
-    FD_SET(signal_sk, &fds);
-
-    max_fd = (sk > signal_sk) ? sk : signal_sk;
-    LOGI("Listening SCO socket...");
-    while (select(max_fd + 1, &fds, NULL, NULL, NULL) < 0) {
-        if (errno != EINTR) {
-            LOGE("%s: select() failed: %s", __FUNCTION__, strerror(errno));
-            goto error;
-        }
-        LOGV("%s: select() EINTR, retrying", __FUNCTION__);
-    }
-    LOGV("select() returned");
-    if (FD_ISSET(signal_sk, &fds)) {
-        // signal to cancel listening
-        LOGV("cancelled listening socket, closing");
-        goto error;
-    }
-    if (!FD_ISSET(sk, &fds)) {
-        LOGE("error: select() returned >= 0 with no fds set");
-        goto error;
-    }
-
-    nsk = accept(sk, (struct sockaddr *)&addr, &addr_sz);
-    if (nsk < 0) {
-        LOGE("%s: accept() failed: %s", __FUNCTION__, strerror(errno));
-        goto error;
-    }
-    LOGI("Connected SCO socket (incoming)");
-    close(sk);  // The listening socket
-
-    return nsk;
-
-error:
-    close(sk);
-
-    return -1;
-}
-
-static int connect_work(const char *address, uint16_t sco_pkt_type) {
-    LOGV(__FUNCTION__);
-    struct sockaddr_sco addr;
-    int sk = -1;
-
-    sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
-    if (sk < 0) {
-        LOGE("%s: socket() failed: %s", __FUNCTION__, strerror(errno));
-        return -1;
-    }
-
-    /* Bind to local address */
-    memset(&addr, 0, sizeof(addr));
-    addr.sco_family = AF_BLUETOOTH;
-    memcpy(&addr.sco_bdaddr, BDADDR_ANY, sizeof(bdaddr_t));
-    if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        LOGE("%s: bind() failed: %s", __FUNCTION__, strerror(errno));
-        goto error;
-    }
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sco_family = AF_BLUETOOTH;
-    get_bdaddr(address, &addr.sco_bdaddr);
-    addr.sco_pkt_type = sco_pkt_type;
-    LOGI("Connecting to socket");
-    while (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
-        if (errno != EINTR) {
-            LOGE("%s: connect() failed: %s", __FUNCTION__, strerror(errno));
-            goto error;
-        }
-        LOGV("%s: connect() EINTR, retrying", __FUNCTION__);
-    }
-    LOGI("SCO socket connected (outgoing)");
-
-    return sk;
-
-error:
-    if (sk >= 0) close(sk);
-    return -1;
-}
-
-static void wait_for_close(int sk, int signal_sk) {
-    LOGV(__FUNCTION__);
-    pollfd p[2];
-
-    memset(p, 0, 2 * sizeof(pollfd));
-    p[0].fd = sk;
-    p[1].fd = signal_sk;
-    p[1].events = POLLIN | POLLPRI;
-
-    LOGV("poll...");
-
-    while (poll(p, 2, -1) < 0) {  // blocks
-        if (errno != EINTR) {
-            LOGE("%s: poll() failed: %s", __FUNCTION__, strerror(errno));
-            break;
-        }
-        LOGV("%s: poll() EINTR, retrying", __FUNCTION__);
-    }
-
-    LOGV("poll() returned");
-}
-#endif
-
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "()V", (void *)initNative},
-    {"destroyNative", "()V", (void *)destroyNative},
-    {"connectNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)connectNative},
-    {"acceptNative", "()Z", (void *)acceptNative},
-    {"closeNative", "()V", (void *)closeNative},
-};
-
-int register_android_bluetooth_ScoSocket(JNIEnv *env) {
-    return AndroidRuntime::registerNativeMethods(env,
-            "android/bluetooth/ScoSocket", sMethods, NELEM(sMethods));
-}
-
-} /* namespace android */
diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp
index 43c3a95..aae0f21 100644
--- a/core/jni/android_bluetooth_common.cpp
+++ b/core/jni/android_bluetooth_common.cpp
@@ -69,6 +69,16 @@
     {"UUIDs", DBUS_TYPE_ARRAY},
 };
 
+static Properties input_properties[] = {
+    {"Connected", DBUS_TYPE_BOOLEAN},
+};
+
+static Properties pan_properties[] = {
+    {"Connected", DBUS_TYPE_BOOLEAN},
+    {"Interface", DBUS_TYPE_STRING},
+    {"UUID", DBUS_TYPE_STRING},
+};
+
 typedef union {
     char *str_val;
     int int_val;
@@ -187,6 +197,7 @@
     dbus_bool_t ret;
     va_list lst;
     va_start(lst, first_arg_type);
+
     ret = dbus_func_args_async_valist(env, conn,
                                       timeout_ms,
                                       reply, user, nat,
@@ -699,6 +710,16 @@
                     sizeof(remote_device_properties) / sizeof(Properties));
 }
 
+jobjectArray parse_input_property_change(JNIEnv *env, DBusMessage *msg) {
+    return parse_property_change(env, msg, (Properties *) &input_properties,
+                    sizeof(input_properties) / sizeof(Properties));
+}
+
+jobjectArray parse_pan_property_change(JNIEnv *env, DBusMessage *msg) {
+    return parse_property_change(env, msg, (Properties *) &pan_properties,
+                    sizeof(pan_properties) / sizeof(Properties));
+}
+
 jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter) {
     return parse_properties(env, iter, (Properties *) &adapter_properties,
                             sizeof(adapter_properties) / sizeof(Properties));
@@ -709,6 +730,11 @@
                           sizeof(remote_device_properties) / sizeof(Properties));
 }
 
+jobjectArray parse_input_properties(JNIEnv *env, DBusMessageIter *iter) {
+    return parse_properties(env, iter, (Properties *) &input_properties,
+                          sizeof(input_properties) / sizeof(Properties));
+}
+
 int get_bdaddr(const char *str, bdaddr_t *ba) {
     char *d = ((char *)ba) + 5, *endp;
     int i;
diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h
index 378bb6f..9222e1a 100644
--- a/core/jni/android_bluetooth_common.h
+++ b/core/jni/android_bluetooth_common.h
@@ -162,6 +162,9 @@
 jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter);
 jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg);
 jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg);
+jobjectArray parse_input_properties(JNIEnv *env, DBusMessageIter *iter);
+jobjectArray parse_input_property_change(JNIEnv *env, DBusMessage *msg);
+jobjectArray parse_pan_property_change(JNIEnv *env, DBusMessage *msg);
 void append_variant(DBusMessageIter *iter, int type, void *val);
 int get_bdaddr(const char *str, bdaddr_t *ba);
 void get_bdaddr_as_string(const bdaddr_t *ba, char *str);
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index 91449bc..040dac3 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -29,7 +29,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "CursorWindow.h"
+#include "binder/CursorWindow.h"
 #include "sqlite3_exception.h"
 #include "android_util_Binder.h"
 
@@ -225,70 +225,6 @@
     return NULL;
 }
 
-static jboolean isBlob_native(JNIEnv* env, jobject object, jint row, jint column)
-{
-    int32_t err;
-    CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking if column is a blob or null for %d,%d from %p", row, column, window);
-
-    field_slot_t field;
-    err = window->read_field_slot(row, column, &field);
-    if (err != 0) {
-        throwExceptionWithRowCol(env, row, column);
-        return NULL;
-    }
-
-    return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL;
-}
-
-static jboolean isString_native(JNIEnv* env, jobject object, jint row, jint column)
-{
-    int32_t err;
-    CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking if column is a string or null for %d,%d from %p", row, column, window);
-
-    field_slot_t field;
-    err = window->read_field_slot(row, column, &field);
-    if (err != 0) {
-        throwExceptionWithRowCol(env, row, column);
-        return NULL;
-    }
-
-    return field.type == FIELD_TYPE_STRING || field.type == FIELD_TYPE_NULL;
-}
-
-static jboolean isInteger_native(JNIEnv* env, jobject object, jint row, jint column)
-{
-    int32_t err;
-    CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking if column is an integer for %d,%d from %p", row, column, window);
-
-    field_slot_t field;
-    err = window->read_field_slot(row, column, &field);
-    if (err != 0) {
-        throwExceptionWithRowCol(env, row, column);
-        return NULL;
-    }
-
-    return field.type == FIELD_TYPE_INTEGER;
-}
-
-static jboolean isFloat_native(JNIEnv* env, jobject object, jint row, jint column)
-{
-    int32_t err;
-    CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking if column is a float for %d,%d from %p", row, column, window);
-
-    field_slot_t field;
-    err = window->read_field_slot(row, column, &field);
-    if (err != 0) {
-        throwExceptionWithRowCol(env, row, column);
-        return NULL;
-    }
-
-    return field.type == FIELD_TYPE_FLOAT;
-}
-
 static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column)
 {
     int32_t err;
@@ -487,10 +423,9 @@
     }
 }
 
-static jboolean isNull_native(JNIEnv* env, jobject object, jint row, jint column)
+bool isNull_native(CursorWindow *window, jint row, jint column)
 {
-    CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window);
+    LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window);
 
     bool isNull;
     if (window->getNull(row, column, &isNull)) {
@@ -652,6 +587,26 @@
     window->freeLastRow();
 }
 
+static jint getType_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+    int32_t err;
+    CursorWindow * window = GET_WINDOW(env, object);
+    LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window);
+
+    if (isNull_native(window, row, column)) {
+      return FIELD_TYPE_NULL;
+    }
+
+    field_slot_t field;
+    err = window->read_field_slot(row, column, &field);
+    if (err != 0) {
+        throwExceptionWithRowCol(env, row, column);
+        return NULL;
+    }
+
+    return field.type;
+}
+
 static JNINativeMethod sMethods[] =
 {
      /* name, signature, funcPtr */
@@ -662,11 +617,9 @@
     {"close_native", "()V", (void *)native_close},
     {"getLong_native", "(II)J", (void *)getLong_native},
     {"getBlob_native", "(II)[B", (void *)getBlob_native},
-    {"isBlob_native", "(II)Z", (void *)isBlob_native},
     {"getString_native", "(II)Ljava/lang/String;", (void *)getString_native},
     {"copyStringToBuffer_native", "(IIILandroid/database/CharArrayBuffer;)[C", (void *)copyStringToBuffer_native},
     {"getDouble_native", "(II)D", (void *)getDouble_native},
-    {"isNull_native", "(II)Z", (void *)isNull_native},
     {"getNumRows_native", "()I", (void *)getNumRows},
     {"setNumColumns_native", "(I)Z", (void *)setNumColumns},
     {"allocRow_native", "()Z", (void *)allocRow},
@@ -676,9 +629,7 @@
     {"putDouble_native", "(DII)Z", (void *)putDouble_native},
     {"freeLastRow_native", "()V", (void *)freeLastRow},
     {"putNull_native", "(II)Z", (void *)putNull_native},
-    {"isString_native", "(II)Z", (void *)isString_native},
-    {"isFloat_native", "(II)Z", (void *)isFloat_native},
-    {"isInteger_native", "(II)Z", (void *)isInteger_native},
+    {"getType_native", "(II)I", (void *)getType_native},
 };
 
 int register_android_database_CursorWindow(JNIEnv * env)
diff --git a/core/jni/android_database_SQLiteCompiledSql.cpp b/core/jni/android_database_SQLiteCompiledSql.cpp
index 8d1c39e..de4c5c8 100644
--- a/core/jni/android_database_SQLiteCompiledSql.cpp
+++ b/core/jni/android_database_SQLiteCompiledSql.cpp
@@ -91,22 +91,11 @@
     compile(env, object, GET_HANDLE(env, object), sqlString);
 }
 
-static void native_finalize(JNIEnv* env, jobject object)
-{
-    int err;
-    sqlite3_stmt * statement = GET_STATEMENT(env, object);
-
-    if (statement != NULL) {
-        sqlite3_finalize(statement);
-        env->SetIntField(object, gStatementField, 0);
-    }
-}
 
 static JNINativeMethod sMethods[] =
 {
      /* name, signature, funcPtr */
     {"native_compile", "(Ljava/lang/String;)V", (void *)native_compile},
-    {"native_finalize", "()V", (void *)native_finalize},
 };
 
 int register_android_database_SQLiteCompiledSql(JNIEnv * env)
diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
index 36234a9..7aeed98 100644
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ b/core/jni/android_database_SQLiteDatabase.cpp
@@ -15,7 +15,7 @@
  */
 
 #undef LOG_TAG
-#define LOG_TAG "Database"
+#define LOG_TAG "SqliteDatabaseCpp"
 
 #include <utils/Log.h>
 #include <utils/String8.h>
@@ -51,6 +51,8 @@
 /* uncomment the next line to force-enable logging of all statements */
 // #define DB_LOG_STATEMENTS
 
+#define DEBUG_JNI 0
+
 namespace android {
 
 enum {
@@ -62,9 +64,11 @@
 };
 
 static jfieldID offset_db_handle;
+static jmethodID method_custom_function_callback;
+static jclass string_class = NULL;
 
-static char *createStr(const char *path) {
-    int len = strlen(path);
+static char *createStr(const char *path, short extra) {
+    int len = strlen(path) + extra;
     char *str = (char *)malloc(len + 1);
     strncpy(str, path, len);
     str[len] = NULL;
@@ -73,8 +77,9 @@
 
 static void sqlLogger(void *databaseName, int iErrCode, const char *zMsg) {
     // skip printing this message if it is due to certain types of errors
-    if (iErrCode == SQLITE_CONSTRAINT) return;
-    LOGI("sqlite returned: error code = %d, msg = %s\n", iErrCode, zMsg);
+    if (iErrCode == 0 || iErrCode == SQLITE_CONSTRAINT) return;
+    // print databasename, errorcode and msg
+    LOGI("sqlite returned: error code = %d, msg = %s, db=%s\n", iErrCode, zMsg, databaseName);
 }
 
 // register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
@@ -85,9 +90,9 @@
     }
 
     LOGV("Registering sqlite logging func \n");
-    int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path));
+    int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path, 0));
     if (err != SQLITE_OK) {
-        LOGE("sqlite_config failed error_code = %d. THIS SHOULD NEVER occur.\n", err);
+        LOGW("sqlite returned error = %d when trying to register logging func.\n", err);
         return;
     }
     loggingFuncSet = true;
@@ -176,13 +181,17 @@
     if (handle != NULL) sqlite3_close(handle);
 }
 
-static char *getDatabaseName(JNIEnv* env, sqlite3 * handle, jstring databaseName) {
+static char *getDatabaseName(JNIEnv* env, sqlite3 * handle, jstring databaseName, short connNum) {
     char const *path = env->GetStringUTFChars(databaseName, NULL);
     if (path == NULL) {
         LOGE("Failure in getDatabaseName(). VM ran out of memory?\n");
         return NULL; // VM would have thrown OutOfMemoryError
     }
-    char *dbNameStr = createStr(path);
+    char *dbNameStr = createStr(path, 4);
+    if (connNum > 999) { // TODO: if number of pooled connections > 999, fix this line.
+      connNum = -1;
+    }
+    sprintf(dbNameStr + strlen(path), "|%03d", connNum);
     env->ReleaseStringUTFChars(databaseName, path);
     return dbNameStr;
 }
@@ -192,10 +201,10 @@
 }
 
 /* public native void enableSqlTracing(); */
-static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName)
+static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName, jshort connType)
 {
     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-    sqlite3_trace(handle, &sqlTrace, (void *)getDatabaseName(env, handle, databaseName));
+    sqlite3_trace(handle, &sqlTrace, (void *)getDatabaseName(env, handle, databaseName, connType));
 }
 
 static void sqlProfile(void *databaseName, const char *sql, sqlite3_uint64 tm) {
@@ -204,13 +213,13 @@
 }
 
 /* public native void enableSqlProfiling(); */
-static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName)
+static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName, jshort connType)
 {
     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-    sqlite3_profile(handle, &sqlProfile, (void *)getDatabaseName(env, handle, databaseName));
+    sqlite3_profile(handle, &sqlProfile, (void *)getDatabaseName(env, handle, databaseName,
+            connType));
 }
 
-
 /* public native void close(); */
 static void dbclose(JNIEnv* env, jobject object)
 {
@@ -240,73 +249,6 @@
     }
 }
 
-/* public native void native_execSQL(String sql); */
-static void native_execSQL(JNIEnv* env, jobject object, jstring sqlString)
-{
-    int err;
-    int stepErr;
-    sqlite3_stmt * statement = NULL;
-    sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-    jchar const * sql = env->GetStringChars(sqlString, NULL);
-    jsize sqlLen = env->GetStringLength(sqlString);
-
-    if (sql == NULL || sqlLen == 0) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", "You must supply an SQL string");
-        return;
-    }
-
-    err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL);
-
-    env->ReleaseStringChars(sqlString, sql);
-
-    if (err != SQLITE_OK) {
-        char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
-        LOGE("Failure %d (%s) on %p when preparing '%s'.\n", err, sqlite3_errmsg(handle), handle, sql8);
-        throw_sqlite3_exception(env, handle, sql8);
-        env->ReleaseStringUTFChars(sqlString, sql8);
-        return;
-    }
-
-    stepErr = sqlite3_step(statement);
-    err = sqlite3_finalize(statement);
-
-    if (stepErr != SQLITE_DONE) {
-        if (stepErr == SQLITE_ROW) {
-            throw_sqlite3_exception(env, "Queries cannot be performed using execSQL(), use query() instead.");
-        } else {
-            char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
-            LOGE("Failure %d (%s) on %p when executing '%s'\n", err, sqlite3_errmsg(handle), handle, sql8);
-            throw_sqlite3_exception(env, handle, sql8);
-            env->ReleaseStringUTFChars(sqlString, sql8);
-
-        }
-    } else
-#ifndef DB_LOG_STATEMENTS
-    IF_LOGV()
-#endif
-    {
-        char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
-        LOGV("Success on %p when executing '%s'\n", handle, sql8);
-        env->ReleaseStringUTFChars(sqlString, sql8);
-    }
-}
-
-/* native long lastInsertRow(); */
-static jlong lastInsertRow(JNIEnv* env, jobject object)
-{
-    sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-
-    return sqlite3_last_insert_rowid(handle);
-}
-
-/* native int lastChangeCount(); */
-static jint lastChangeCount(JNIEnv* env, jobject object)
-{
-    sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-
-    return sqlite3_changes(handle);
-}
-
 /* native int native_getDbLookaside(); */
 static jint native_getDbLookaside(JNIEnv* env, jobject object)
 {
@@ -443,19 +385,93 @@
     return sqlite3_release_memory(SQLITE_SOFT_HEAP_LIMIT);
 }
 
+static void native_finalize(JNIEnv* env, jobject object, jint statementId)
+{
+    if (statementId > 0) {
+        sqlite3_finalize((sqlite3_stmt *)statementId);
+    }
+}
+
+static void custom_function_callback(sqlite3_context * context, int argc, sqlite3_value ** argv) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    if (!env) {
+        LOGE("custom_function_callback cannot call into Java on this thread");
+        return;
+    }
+    // get global ref to CustomFunction object from our user data
+    jobject function = (jobject)sqlite3_user_data(context);
+
+    // pack up the arguments into a string array
+    if (!string_class)
+        string_class = (jclass)env->NewGlobalRef(env->FindClass("java/lang/String"));
+    jobjectArray strArray = env->NewObjectArray(argc, string_class, NULL);
+    if (!strArray)
+        goto done;
+    for (int i = 0; i < argc; i++) {
+        char* arg = (char *)sqlite3_value_text(argv[i]);
+        if (!arg) {
+            LOGE("NULL argument in custom_function_callback.  This should not happen.");
+            return;
+        }
+        jobject obj = env->NewStringUTF(arg);
+        if (!obj)
+            goto done;
+        env->SetObjectArrayElement(strArray, i, obj);
+        env->DeleteLocalRef(obj);
+    }
+
+    env->CallVoidMethod(function, method_custom_function_callback, strArray);
+
+done:
+    if (env->ExceptionCheck()) {
+        LOGE("An exception was thrown by custom sqlite3 function.");
+        LOGE_EX(env);
+        env->ExceptionClear();
+    }
+}
+
+static jint native_addCustomFunction(JNIEnv* env, jobject object,
+        jstring name, jint numArgs, jobject function)
+{
+    sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
+    char const *nameStr = env->GetStringUTFChars(name, NULL);
+    jobject ref = env->NewGlobalRef(function);
+    LOGD_IF(DEBUG_JNI, "native_addCustomFunction %s ref: %p", nameStr, ref);
+    int err = sqlite3_create_function(handle, nameStr, numArgs, SQLITE_UTF8,
+            (void *)ref, custom_function_callback, NULL, NULL);
+    env->ReleaseStringUTFChars(name, nameStr);
+
+    if (err == SQLITE_OK)
+        return (int)ref;
+    else {
+        LOGE("sqlite3_create_function returned %d", err);
+        env->DeleteGlobalRef(ref);
+        throw_sqlite3_exception(env, handle);
+        return 0;
+     }
+}
+
+static void native_releaseCustomFunction(JNIEnv* env, jobject object, jint ref)
+{
+    LOGD_IF(DEBUG_JNI, "native_releaseCustomFunction %d", ref);
+    env->DeleteGlobalRef((jobject)ref);
+}
+
 static JNINativeMethod sMethods[] =
 {
     /* name, signature, funcPtr */
     {"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen},
     {"dbclose", "()V", (void *)dbclose},
-    {"enableSqlTracing", "(Ljava/lang/String;)V", (void *)enableSqlTracing},
-    {"enableSqlProfiling", "(Ljava/lang/String;)V", (void *)enableSqlProfiling},
-    {"native_execSQL", "(Ljava/lang/String;)V", (void *)native_execSQL},
-    {"lastInsertRow", "()J", (void *)lastInsertRow},
-    {"lastChangeCount", "()I", (void *)lastChangeCount},
+    {"enableSqlTracing", "(Ljava/lang/String;S)V", (void *)enableSqlTracing},
+    {"enableSqlProfiling", "(Ljava/lang/String;S)V", (void *)enableSqlProfiling},
     {"native_setLocale", "(Ljava/lang/String;I)V", (void *)native_setLocale},
     {"native_getDbLookaside", "()I", (void *)native_getDbLookaside},
     {"releaseMemory", "()I", (void *)native_releaseMemory},
+    {"native_finalize", "(I)V", (void *)native_finalize},
+    {"native_addCustomFunction",
+                    "(Ljava/lang/String;ILandroid/database/sqlite/SQLiteDatabase$CustomFunction;)I",
+                    (void *)native_addCustomFunction},
+    {"native_releaseCustomFunction", "(I)V", (void *)native_releaseCustomFunction},
 };
 
 int register_android_database_SQLiteDatabase(JNIEnv *env)
@@ -474,7 +490,19 @@
         return -1;
     }
 
-    return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase", sMethods, NELEM(sMethods));
+    clazz = env->FindClass("android/database/sqlite/SQLiteDatabase$CustomFunction");
+    if (clazz == NULL) {
+        LOGE("Can't find android/database/sqlite/SQLiteDatabase$CustomFunction\n");
+        return -1;
+    }
+    method_custom_function_callback = env->GetMethodID(clazz, "callback", "([Ljava/lang/String;)V");
+    if (method_custom_function_callback == NULL) {
+        LOGE("Can't find method SQLiteDatabase.CustomFunction.callback\n");
+        return -1;
+    }
+
+    return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase",
+            sMethods, NELEM(sMethods));
 }
 
 /* throw a SQLiteException with a message appropriate for the error in handle */
@@ -523,6 +551,7 @@
             exceptionClass = "android/database/sqlite/SQLiteDiskIOException";
             break;
         case SQLITE_CORRUPT:
+        case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also
             exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException";
             break;
         case SQLITE_CONSTRAINT:
@@ -540,6 +569,36 @@
         case SQLITE_MISUSE:
            exceptionClass = "android/database/sqlite/SQLiteMisuseException";
            break;
+        case SQLITE_PERM:
+           exceptionClass = "android/database/sqlite/SQLiteAccessPermException";
+           break;
+        case SQLITE_BUSY:
+           exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException";
+           break;
+        case SQLITE_LOCKED:
+           exceptionClass = "android/database/sqlite/SQLiteTableLockedException";
+           break;
+        case SQLITE_READONLY:
+           exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException";
+           break;
+        case SQLITE_CANTOPEN:
+           exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException";
+           break;
+        case SQLITE_TOOBIG:
+           exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException";
+           break;
+        case SQLITE_RANGE:
+           exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException";
+           break;
+        case SQLITE_NOMEM:
+           exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException";
+           break;
+        case SQLITE_MISMATCH:
+           exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException";
+           break;
+        case SQLITE_UNCLOSED:
+           exceptionClass = "android/database/sqlite/SQLiteUnfinalizedObjectsException";
+           break;
         default:
            exceptionClass = "android/database/sqlite/SQLiteException";
            break;
diff --git a/core/jni/android_database_SQLiteQuery.cpp b/core/jni/android_database_SQLiteQuery.cpp
index 44271683..9e42189 100644
--- a/core/jni/android_database_SQLiteQuery.cpp
+++ b/core/jni/android_database_SQLiteQuery.cpp
@@ -15,7 +15,7 @@
  */
 
 #undef LOG_TAG
-#define LOG_TAG "Cursor"
+#define LOG_TAG "SqliteCursor.cpp"
 
 #include <jni.h>
 #include <JNIHelp.h>
@@ -29,7 +29,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "CursorWindow.h"
+#include "binder/CursorWindow.h"
 #include "sqlite3_exception.h"
 
 
@@ -73,7 +73,7 @@
             return -1;
         }
     }
-    LOGD("skip_rows row %d", maxRows);
+    LOG_WINDOW("skip_rows row %d", maxRows);
     return maxRows;
 }
 
@@ -101,7 +101,7 @@
         }
     }
     sqlite3_reset(statement);
-    LOGD("finish_program_and_get_row_count row %d", numRows);
+    LOG_WINDOW("finish_program_and_get_row_count row %d", numRows);
     return numRows;
 }
 
@@ -116,6 +116,8 @@
     int retryCount;
     int boundParams;
     CursorWindow * window;
+    bool gotAllRows = true;
+    bool gotException = false;
     
     if (statement == NULL) {
         LOGE("Invalid statement in fillWindow()");
@@ -131,8 +133,7 @@
         err = sqlite3_bind_int(statement, offsetParam, startPos);
         if (err != SQLITE_OK) {
             LOGE("Unable to bind offset position, offsetParam = %d", offsetParam);
-            jniThrowException(env, "java/lang/IllegalArgumentException",
-                              sqlite3_errmsg(GET_HANDLE(env, object)));
+            throw_sqlite3_exception(env, GET_HANDLE(env, object));
             return 0;
         }
         LOG_WINDOW("Bound to startPos %d", startPos);
@@ -167,7 +168,7 @@
             LOGE("startPos %d > actual rows %d", startPos, num);
             return num;
         }
-    } 
+    }
     
     while(startPos != 0 || numRows < maxRead) {
         err = sqlite3_step(statement);
@@ -181,8 +182,9 @@
             {
                 field_slot_t * fieldDir = window->allocRow();
                 if (!fieldDir) {
-                    LOGE("Failed allocating fieldDir at startPos %d row %d", startPos, numRows);
-                    return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
+                    LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d", startPos, numRows);
+                    gotAllRows = false;
+                    goto return_count;
                 }
             }
 
@@ -205,9 +207,10 @@
                     int offset = window->alloc(size);
                     if (!offset) {
                         window->freeLastRow();
-                        LOGE("Failed allocating %u bytes for text/blob at %d,%d", size,
+                        LOG_WINDOW("Failed allocating %u bytes for text/blob at %d,%d", size,
                                    startPos + numRows, i);
-                        return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
+                        gotAllRows = false;
+                        goto return_count;
                     }
 
                     window->copyIn(offset, text, size);
@@ -225,8 +228,9 @@
                     int64_t value = sqlite3_column_int64(statement, i);
                     if (!window->putLong(numRows, i, value)) {
                         window->freeLastRow();
-                        LOGE("Failed allocating space for a long in column %d", i);
-                        return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
+                        LOG_WINDOW("Failed allocating space for a long in column %d", i);
+                        gotAllRows = false;
+                        goto return_count;
                     }
                     LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + numRows, i, value);
                 } else if (type == SQLITE_FLOAT) {
@@ -234,8 +238,9 @@
                     double value = sqlite3_column_double(statement, i);
                     if (!window->putDouble(numRows, i, value)) {
                         window->freeLastRow();
-                        LOGE("Failed allocating space for a double in column %d", i);
-                        return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
+                        LOG_WINDOW("Failed allocating space for a double in column %d", i);
+                        gotAllRows = false;
+                        goto return_count;
                     }
                     LOG_WINDOW("%d,%d is FLOAT %lf", startPos + numRows, i, value);
                 } else if (type == SQLITE_BLOB) {
@@ -245,9 +250,10 @@
                     int offset = window->alloc(size);
                     if (!offset) {
                         window->freeLastRow();
-                        LOGE("Failed allocating %u bytes for blob at %d,%d", size,
+                        LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d", size,
                                    startPos + numRows, i);
-                        return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
+                        gotAllRows = false;
+                        goto return_count;
                     }
 
                     window->copyIn(offset, blob, size);
@@ -269,6 +275,7 @@
                     // Unknown data
                     LOGE("Unknown column type when filling database window");
                     throw_sqlite3_exception(env, "Unknown column type when filling window");
+                    gotException = true;
                     break;
                 }
             }
@@ -290,6 +297,8 @@
             LOG_WINDOW("Database locked, retrying");
             if (retryCount > 50) {
                 LOGE("Bailing on database busy rety");
+                throw_sqlite3_exception(env, GET_HANDLE(env, object), "retrycount exceeded");
+                gotException = true;
                 break;
             }
 
@@ -300,18 +309,33 @@
             continue;
         } else {
             throw_sqlite3_exception(env, GET_HANDLE(env, object));
+            gotException = true;
             break;
         }
     }
 
     LOG_WINDOW("Resetting statement %p after fetching %d rows in %d bytes\n\n\n\n", statement,
             numRows, window->size() - window->freeSpace());
-//    LOGI("Filled window with %d rows in %d bytes", numRows, window->size() - window->freeSpace());
+    LOG_WINDOW("Filled window with %d rows in %d bytes", numRows,
+            window->size() - window->freeSpace());
     if (err == SQLITE_ROW) {
+        // there is more data to be returned. let the caller know by returning -1
         return -1;
-    } else {
+    }
+  return_count:
+    if (startPos) {
         sqlite3_reset(statement);
-        return startPos + numRows;
+        LOG_WINDOW("Not doing count(*) because startPos %d is non-zero", startPos);
+        return 0;
+    } else if (gotAllRows) {
+        sqlite3_reset(statement);
+        LOG_WINDOW("Not doing count(*) because we already know the count(*)");
+        return numRows;
+    } else if (gotException) {
+        return 0;
+    } else {
+        // since startPos == 0, we need to get the count(*) of the result set
+        return numRows + 1 + finish_program_and_get_row_count(statement);
     }
 }
 
@@ -336,7 +360,8 @@
 static JNINativeMethod sMethods[] =
 {
      /* name, signature, funcPtr */
-    {"native_fill_window", "(Landroid/database/CursorWindow;IIII)I", (void *)native_fill_window},
+    {"native_fill_window", "(Landroid/database/CursorWindow;IIII)I",
+            (void *)native_fill_window},
     {"native_column_count", "()I", (void*)native_column_count},
     {"native_column_name", "(I)Ljava/lang/String;", (void *)native_column_name},
 };
diff --git a/core/jni/android_database_SQLiteStatement.cpp b/core/jni/android_database_SQLiteStatement.cpp
index ff2ed5d..97e0483 100644
--- a/core/jni/android_database_SQLiteStatement.cpp
+++ b/core/jni/android_database_SQLiteStatement.cpp
@@ -16,7 +16,9 @@
 */
 
 #undef LOG_TAG
-#define LOG_TAG "Cursor"
+#define LOG_TAG "SQLiteStatementCpp"
+
+#include "android_util_Binder.h"
 
 #include <jni.h>
 #include <JNIHelp.h>
@@ -24,11 +26,16 @@
 
 #include <sqlite3.h>
 
+#include <cutils/ashmem.h>
 #include <utils/Log.h>
 
+#include <fcntl.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include "sqlite3_exception.h"
 
@@ -48,22 +55,40 @@
         (sqlite3 *)env->GetIntField(object, gHandleField)
 
 
-static void native_execute(JNIEnv* env, jobject object)
+static jint native_execute(JNIEnv* env, jobject object)
 {
     int err;
     sqlite3 * handle = GET_HANDLE(env, object);
     sqlite3_stmt * statement = GET_STATEMENT(env, object);
+    int numChanges = -1;
 
     // Execute the statement
     err = sqlite3_step(statement);
 
-    // Throw an exception if an error occured
-    if (err != SQLITE_DONE) {
+    // Throw an exception if an error occurred
+    if (err == SQLITE_ROW) {
+        throw_sqlite3_exception(env,
+                "Queries can be performed using SQLiteDatabase query or rawQuery methods only.");
+    } else if (err != SQLITE_DONE) {
         throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
+    } else {
+        numChanges = sqlite3_changes(handle);
     }
 
-    // Reset the statment so it's ready to use again
+    // Reset the statement so it's ready to use again
     sqlite3_reset(statement);
+    return numChanges;
+}
+
+static jlong native_executeInsert(JNIEnv* env, jobject object)
+{
+    sqlite3 * handle = GET_HANDLE(env, object);
+    jint numChanges = native_execute(env, object);
+    if (numChanges > 0) {
+        return sqlite3_last_insert_rowid(handle);
+    } else {
+        return -1;
+    }
 }
 
 static jlong native_1x1_long(JNIEnv* env, jobject object)
@@ -115,13 +140,125 @@
     return value;
 }
 
+static jobject createParcelFileDescriptor(JNIEnv * env, int fd)
+{
+    // Create FileDescriptor object
+    jobject fileDesc = newFileDescriptor(env, fd);
+    if (fileDesc == NULL) {
+        // FileDescriptor constructor has thrown an exception
+        close(fd);
+        return NULL;
+    }
+
+    // Wrap it in a ParcelFileDescriptor
+    jobject parcelFileDesc = newParcelFileDescriptor(env, fileDesc);
+    if (parcelFileDesc == NULL) {
+        // ParcelFileDescriptor constructor has thrown an exception
+        close(fd);
+        return NULL;
+    }
+
+    return parcelFileDesc;
+}
+
+// Creates an ashmem area, copies some data into it, and returns
+// a ParcelFileDescriptor for the ashmem area.
+static jobject create_ashmem_region_with_data(JNIEnv * env,
+                                              const void * data, int length)
+{
+    // Create ashmem area
+    int fd = ashmem_create_region(NULL, length);
+    if (fd < 0) {
+        LOGE("ashmem_create_region failed: %s", strerror(errno));
+        jniThrowIOException(env, errno);
+        return NULL;
+    }
+
+    if (length > 0) {
+        // mmap the ashmem area
+        void * ashmem_ptr =
+                mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+        if (ashmem_ptr == MAP_FAILED) {
+            LOGE("mmap failed: %s", strerror(errno));
+            jniThrowIOException(env, errno);
+            close(fd);
+            return NULL;
+        }
+
+        // Copy data to ashmem area
+        memcpy(ashmem_ptr, data, length);
+
+        // munmap ashmem area
+        if (munmap(ashmem_ptr, length) < 0) {
+            LOGE("munmap failed: %s", strerror(errno));
+            jniThrowIOException(env, errno);
+            close(fd);
+            return NULL;
+        }
+    }
+
+    // Make ashmem area read-only
+    if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
+        LOGE("ashmem_set_prot_region failed: %s", strerror(errno));
+        jniThrowIOException(env, errno);
+        close(fd);
+        return NULL;
+    }
+
+    // Wrap it in a ParcelFileDescriptor
+    return createParcelFileDescriptor(env, fd);
+}
+
+static jobject native_1x1_blob_ashmem(JNIEnv* env, jobject object)
+{
+    int err;
+    sqlite3 * handle = GET_HANDLE(env, object);
+    sqlite3_stmt * statement = GET_STATEMENT(env, object);
+    jobject value = NULL;
+
+    // Execute the statement
+    err = sqlite3_step(statement);
+
+    // Handle the result
+    if (err == SQLITE_ROW) {
+        // No errors, read the data and return it
+        const void * blob = sqlite3_column_blob(statement, 0);
+        if (blob != NULL) {
+            int len = sqlite3_column_bytes(statement, 0);
+            if (len >= 0) {
+                value = create_ashmem_region_with_data(env, blob, len);
+            }
+        }
+    } else {
+        throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
+    }
+
+    // Reset the statment so it's ready to use again
+    sqlite3_reset(statement);
+
+    return value;
+}
+
+static void native_executeSql(JNIEnv* env, jobject object, jstring sql)
+{
+    char const* sqlString = env->GetStringUTFChars(sql, NULL);
+    sqlite3 * handle = GET_HANDLE(env, object);
+    int err = sqlite3_exec(handle, sqlString, NULL, NULL, NULL);
+    if (err != SQLITE_OK) {
+        throw_sqlite3_exception(env, handle);
+    }
+    env->ReleaseStringUTFChars(sql, sqlString);
+}
 
 static JNINativeMethod sMethods[] =
 {
      /* name, signature, funcPtr */
-    {"native_execute", "()V", (void *)native_execute},
+    {"native_execute", "()I", (void *)native_execute},
+    {"native_executeInsert", "()J", (void *)native_executeInsert},
     {"native_1x1_long", "()J", (void *)native_1x1_long},
     {"native_1x1_string", "()Ljava/lang/String;", (void *)native_1x1_string},
+    {"native_1x1_blob_ashmem", "()Landroid/os/ParcelFileDescriptor;", (void *)native_1x1_blob_ashmem},
+    {"native_executeSql", "(Ljava/lang/String;)V", (void *)native_executeSql},
 };
 
 int register_android_database_SQLiteStatement(JNIEnv * env)
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 6575b9a..351f264 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -224,7 +224,8 @@
     { "addRoute", "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)I",
        (void *)android_net_utils_addRoute },
     { "removeHostRoutes", "(Ljava/lang/String;)I",  (void *)android_net_utils_removeHostRoutes },
-    { "getDefaultRoute", "(Ljava/lang/String;)I",  (void *)android_net_utils_getDefaultRoute },
+    { "getDefaultRouteNative", "(Ljava/lang/String;)I",
+       (void *)android_net_utils_getDefaultRoute },
     { "removeDefaultRoute", "(Ljava/lang/String;)I",  (void *)android_net_utils_removeDefaultRoute },
     { "resetConnections", "(Ljava/lang/String;)I",  (void *)android_net_utils_resetConnections },
     { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z",  (void *)android_net_utils_runDhcp },
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index fb029e6..0663e98 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -25,11 +25,14 @@
 #include "wifi.h"
 
 #define WIFI_PKG_NAME "android/net/wifi/WifiNative"
+#define BUF_SIZE 256
 
 namespace android {
 
 static jboolean sScanModeActive = false;
 
+//TODO: check general errors in addition to overflow on snprintf
+
 /*
  * The following remembers the jfieldID's of the fields
  * of the DhcpInfo Java object, so that we don't have
@@ -65,7 +68,7 @@
 
 static jint doIntCommand(const char *cmd)
 {
-    char reply[256];
+    char reply[BUF_SIZE];
 
     if (doCommand(cmd, reply, sizeof(reply)) != 0) {
         return (jint)-1;
@@ -76,7 +79,7 @@
 
 static jboolean doBooleanCommand(const char *cmd, const char *expect)
 {
-    char reply[256];
+    char reply[BUF_SIZE];
 
     if (doCommand(cmd, reply, sizeof(reply)) != 0) {
         return (jboolean)JNI_FALSE;
@@ -98,6 +101,11 @@
     }
 }
 
+static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jobject clazz)
+{
+    return (jboolean)(::is_wifi_driver_loaded() == 1);
+}
+
 static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject clazz)
 {
     return (jboolean)(::wifi_load_driver() == 0);
@@ -130,7 +138,7 @@
 
 static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject clazz)
 {
-    char buf[256];
+    char buf[BUF_SIZE];
 
     int nread = ::wifi_wait_for_event(buf, sizeof buf);
     if (nread > 0) {
@@ -150,13 +158,73 @@
     return doIntCommand("ADD_NETWORK");
 }
 
+static jboolean android_net_wifi_wpsPbcCommand(JNIEnv* env, jobject clazz, jstring bssid)
+{
+    char cmdstr[BUF_SIZE];
+    jboolean isCopy;
+
+    const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
+    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "WPS_PBC %s", bssidStr);
+    env->ReleaseStringUTFChars(bssid, bssidStr);
+
+    if ((numWritten == -1) || (numWritten >= sizeof(cmdstr))) {
+        return false;
+    }
+    return doBooleanCommand(cmdstr, "OK");
+}
+
+static jboolean android_net_wifi_wpsPinFromAccessPointCommand(JNIEnv* env, jobject clazz, jstring bssid, int apPin)
+{
+    char cmdstr[BUF_SIZE];
+    jboolean isCopy;
+
+    const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
+    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "WPS_REG %s %d", bssidStr, apPin);
+    env->ReleaseStringUTFChars(bssid, bssidStr);
+
+    if ((numWritten == -1) || (numWritten >= (int)sizeof(cmdstr))) {
+        return false;
+    }
+    return doBooleanCommand(cmdstr, "OK");
+}
+
+static jint android_net_wifi_wpsPinFromDeviceCommand(JNIEnv* env, jobject clazz, jstring bssid)
+{
+    char cmdstr[BUF_SIZE];
+    jboolean isCopy;
+
+    const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
+    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "WPS_PIN %s", bssidStr);
+    env->ReleaseStringUTFChars(bssid, bssidStr);
+
+    if ((numWritten == -1) || (numWritten >= (int)sizeof(cmdstr))) {
+        return -1;
+    }
+    return doIntCommand(cmdstr);
+}
+
+static jboolean android_net_wifi_setCountryCodeCommand(JNIEnv* env, jobject clazz, jstring country)
+{
+    char cmdstr[BUF_SIZE];
+    jboolean isCopy;
+
+    const char *countryStr = env->GetStringUTFChars(country, &isCopy);
+    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER COUNTRY %s", countryStr);
+    env->ReleaseStringUTFChars(country, countryStr);
+
+    if ((numWritten == -1) || (numWritten >= (int)sizeof(cmdstr))) {
+        return false;
+    }
+    return doBooleanCommand(cmdstr, "OK");
+}
+
 static jboolean android_net_wifi_setNetworkVariableCommand(JNIEnv* env,
                                                            jobject clazz,
                                                            jint netId,
                                                            jstring name,
                                                            jstring value)
 {
-    char cmdstr[256];
+    char cmdstr[BUF_SIZE];
     jboolean isCopy;
 
     const char *nameStr = env->GetStringUTFChars(name, &isCopy);
@@ -179,7 +247,7 @@
                                                           jint netId,
                                                           jstring name)
 {
-    char cmdstr[256];
+    char cmdstr[BUF_SIZE];
     jboolean isCopy;
 
     const char *nameStr = env->GetStringUTFChars(name, &isCopy);
@@ -197,7 +265,7 @@
 
 static jboolean android_net_wifi_removeNetworkCommand(JNIEnv* env, jobject clazz, jint netId)
 {
-    char cmdstr[256];
+    char cmdstr[BUF_SIZE];
 
     int numWritten = snprintf(cmdstr, sizeof(cmdstr), "REMOVE_NETWORK %d", netId);
     int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
@@ -210,7 +278,7 @@
                                                   jint netId,
                                                   jboolean disableOthers)
 {
-    char cmdstr[256];
+    char cmdstr[BUF_SIZE];
     const char *cmd = disableOthers ? "SELECT_NETWORK" : "ENABLE_NETWORK";
 
     int numWritten = snprintf(cmdstr, sizeof(cmdstr), "%s %d", cmd, netId);
@@ -221,7 +289,7 @@
 
 static jboolean android_net_wifi_disableNetworkCommand(JNIEnv* env, jobject clazz, jint netId)
 {
-    char cmdstr[256];
+    char cmdstr[BUF_SIZE];
 
     int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DISABLE_NETWORK %d", netId);
     int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
@@ -315,7 +383,7 @@
 
 static jint android_net_wifi_getRssiHelper(const char *cmd)
 {
-    char reply[256];
+    char reply[BUF_SIZE];
     int rssi = -200;
 
     if (doCommand(cmd, reply, sizeof(reply)) != 0) {
@@ -354,7 +422,7 @@
 
 static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject clazz)
 {
-    char reply[256];
+    char reply[BUF_SIZE];
     int linkspeed;
 
     if (doCommand("DRIVER LINKSPEED", reply, sizeof(reply)) != 0) {
@@ -368,8 +436,8 @@
 
 static jstring android_net_wifi_getMacAddressCommand(JNIEnv* env, jobject clazz)
 {
-    char reply[256];
-    char buf[256];
+    char reply[BUF_SIZE];
+    char buf[BUF_SIZE];
 
     if (doCommand("DRIVER MACADDR", reply, sizeof(reply)) != 0) {
         return env->NewStringUTF(NULL);
@@ -384,7 +452,7 @@
 
 static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject clazz, jint mode)
 {
-    char cmdstr[256];
+    char cmdstr[BUF_SIZE];
 
     int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER POWERMODE %d", mode);
     int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
@@ -394,7 +462,7 @@
 
 static jint android_net_wifi_getPowerModeCommand(JNIEnv* env, jobject clazz)
 {
-    char reply[256];
+    char reply[BUF_SIZE];
     int power;
 
     if (doCommand("DRIVER GETPOWER", reply, sizeof(reply)) != 0) {
@@ -406,35 +474,33 @@
     return (jint)power;
 }
 
-static jboolean android_net_wifi_setNumAllowedChannelsCommand(JNIEnv* env, jobject clazz, jint numChannels)
+static jboolean android_net_wifi_setBandCommand(JNIEnv* env, jobject clazz, jint band)
 {
-    char cmdstr[256];
+    char cmdstr[25];
 
-    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER SCAN-CHANNELS %u", numChannels);
+    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETBAND %d", band);
     int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
 
     return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
 }
 
-static jint android_net_wifi_getNumAllowedChannelsCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getBandCommand(JNIEnv* env, jobject clazz)
 {
-    char reply[256];
-    int numChannels;
+    char reply[25];
+    int band;
 
-    if (doCommand("DRIVER SCAN-CHANNELS", reply, sizeof(reply)) != 0) {
-        return -1;
+    if (doCommand("DRIVER GETBAND", reply, sizeof(reply)) != 0) {
+        return (jint)-1;
     }
-    // reply comes back in the form "Scan-Channels = X" where X is the
-    // number of channels
-    if (sscanf(reply, "%*s = %u", &numChannels) == 1)
-        return numChannels;
-    else
-        return -1;
+    // reply comes back in the form "Band X" where X is the
+    // number we're interested in.
+    sscanf(reply, "%*s %u", &band);
+    return (jint)band;
 }
 
 static jboolean android_net_wifi_setBluetoothCoexistenceModeCommand(JNIEnv* env, jobject clazz, jint mode)
 {
-    char cmdstr[256];
+    char cmdstr[BUF_SIZE];
 
     int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER BTCOEXMODE %d", mode);
     int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
@@ -444,7 +510,7 @@
 
 static jboolean android_net_wifi_setBluetoothCoexistenceScanModeCommand(JNIEnv* env, jobject clazz, jboolean setCoexScanMode)
 {
-    char cmdstr[256];
+    char cmdstr[BUF_SIZE];
 
     int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER BTCOEXSCAN-%s", setCoexScanMode ? "START" : "STOP");
     int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
@@ -466,7 +532,7 @@
 
 static jboolean android_net_wifi_setScanResultHandlingCommand(JNIEnv* env, jobject clazz, jint mode)
 {
-    char cmdstr[256];
+    char cmdstr[BUF_SIZE];
 
     int numWritten = snprintf(cmdstr, sizeof(cmdstr), "AP_SCAN %d", mode);
     int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
@@ -476,7 +542,7 @@
 
 static jboolean android_net_wifi_addToBlacklistCommand(JNIEnv* env, jobject clazz, jstring bssid)
 {
-    char cmdstr[256];
+    char cmdstr[BUF_SIZE];
     jboolean isCopy;
 
     const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
@@ -495,7 +561,7 @@
 
 static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, jobject clazz, jboolean enabled)
 {
-    char cmdstr[25];
+    char cmdstr[BUF_SIZE];
 
     snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETSUSPENDOPT %d", enabled ? 0 : 1);
     return doBooleanCommand(cmdstr, "OK");
@@ -533,6 +599,7 @@
     /* name, signature, funcPtr */
 
     { "loadDriver", "()Z",  (void *)android_net_wifi_loadDriver },
+    { "isDriverLoaded", "()Z",  (void *)android_net_wifi_isDriverLoaded},
     { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
     { "startSupplicant", "()Z",  (void *)android_net_wifi_startSupplicant },
     { "stopSupplicant", "()Z",  (void *)android_net_wifi_stopSupplicant },
@@ -564,8 +631,8 @@
     { "stopPacketFiltering", "()Z", (void*) android_net_wifi_stopPacketFiltering },
     { "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand },
     { "getPowerModeCommand", "()I", (void*) android_net_wifi_getPowerModeCommand },
-    { "setNumAllowedChannelsCommand", "(I)Z", (void*) android_net_wifi_setNumAllowedChannelsCommand },
-    { "getNumAllowedChannelsCommand", "()I", (void*) android_net_wifi_getNumAllowedChannelsCommand },
+    { "setBandCommand", "(I)Z", (void*) android_net_wifi_setBandCommand},
+    { "getBandCommand", "()I", (void*) android_net_wifi_getBandCommand},
     { "setBluetoothCoexistenceModeCommand", "(I)Z",
     		(void*) android_net_wifi_setBluetoothCoexistenceModeCommand },
     { "setBluetoothCoexistenceScanModeCommand", "(Z)Z",
@@ -580,8 +647,15 @@
     { "setScanResultHandlingCommand", "(I)Z", (void*) android_net_wifi_setScanResultHandlingCommand },
     { "addToBlacklistCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_addToBlacklistCommand },
     { "clearBlacklistCommand", "()Z", (void*) android_net_wifi_clearBlacklistCommand },
-    { "setSuspendOptimizationsCommand", "(Z)Z", (void*) android_net_wifi_setSuspendOptimizationsCommand},
-
+    { "startWpsPbcCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_wpsPbcCommand },
+    { "startWpsWithPinFromAccessPointCommand", "(Ljava/lang/String;I)Z",
+        (void*) android_net_wifi_wpsPinFromAccessPointCommand },
+    { "startWpsWithPinFromDeviceCommand", "(Ljava/lang/String;)I",
+        (void*) android_net_wifi_wpsPinFromDeviceCommand },
+    { "setSuspendOptimizationsCommand", "(Z)Z",
+        (void*) android_net_wifi_setSuspendOptimizationsCommand},
+    { "setCountryCodeCommand", "(Ljava/lang/String;)Z",
+        (void*) android_net_wifi_setCountryCodeCommand},
     { "doDhcpRequest", "(Landroid/net/DhcpInfo;)Z", (void*) android_net_wifi_doDhcpRequest },
     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_wifi_getDhcpError },
 };
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 3ee404a..2297834 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "android.os.Debug"
 #include "JNIHelp.h"
 #include "jni.h"
 #include "utils/misc.h"
@@ -24,6 +25,8 @@
 #include <unistd.h>
 #include <time.h>
 #include <sys/time.h>
+#include <errno.h>
+#include <assert.h>
 
 #ifdef HAVE_MALLOC_H
 #include <malloc.h>
@@ -274,6 +277,190 @@
 jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz);
 jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz);
 
+
+#ifdef HAVE_ANDROID_OS
+/* pulled out of bionic */
+extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
+    size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
+extern "C" void free_malloc_leak_info(uint8_t* info);
+#define SIZE_FLAG_ZYGOTE_CHILD  (1<<31)
+#define BACKTRACE_SIZE          32
+
+/*
+ * This is a qsort() callback.
+ *
+ * See dumpNativeHeap() for comments about the data format and sort order.
+ */
+static int compareHeapRecords(const void* vrec1, const void* vrec2)
+{
+    const size_t* rec1 = (const size_t*) vrec1;
+    const size_t* rec2 = (const size_t*) vrec2;
+    size_t size1 = *rec1;
+    size_t size2 = *rec2;
+
+    if (size1 < size2) {
+        return 1;
+    } else if (size1 > size2) {
+        return -1;
+    }
+
+    intptr_t* bt1 = (intptr_t*)(rec1 + 2);
+    intptr_t* bt2 = (intptr_t*)(rec2 + 2);
+    for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) {
+        intptr_t addr1 = bt1[idx];
+        intptr_t addr2 = bt2[idx];
+        if (addr1 == addr2) {
+            if (addr1 == 0)
+                break;
+            continue;
+        }
+        if (addr1 < addr2) {
+            return -1;
+        } else if (addr1 > addr2) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * The get_malloc_leak_info() call returns an array of structs that
+ * look like this:
+ *
+ *   size_t size
+ *   size_t allocations
+ *   intptr_t backtrace[32]
+ *
+ * "size" is the size of the allocation, "backtrace" is a fixed-size
+ * array of function pointers, and "allocations" is the number of
+ * allocations with the exact same size and backtrace.
+ *
+ * The entries are sorted by descending total size (i.e. size*allocations)
+ * then allocation count.  For best results with "diff" we'd like to sort
+ * primarily by individual size then stack trace.  Since the entries are
+ * fixed-size, and we're allowed (by the current implementation) to mangle
+ * them, we can do this in place.
+ */
+static void dumpNativeHeap(FILE* fp)
+{
+    uint8_t* info = NULL;
+    size_t overallSize, infoSize, totalMemory, backtraceSize;
+
+    get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
+        &backtraceSize);
+    if (info == NULL) {
+        fprintf(fp, "Native heap dump not available. To enable, run these"
+                    " commands (requires root):\n");
+        fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n");
+        fprintf(fp, "$ adb shell stop\n");
+        fprintf(fp, "$ adb shell start\n");
+        return;
+    }
+    assert(infoSize != 0);
+    assert(overallSize % infoSize == 0);
+
+    fprintf(fp, "Android Native Heap Dump v1.0\n\n");
+
+    size_t recordCount = overallSize / infoSize;
+    fprintf(fp, "Total memory: %zu\n", totalMemory);
+    fprintf(fp, "Allocation records: %zd\n", recordCount);
+    if (backtraceSize != BACKTRACE_SIZE) {
+        fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n",
+            backtraceSize, BACKTRACE_SIZE);
+    }
+    fprintf(fp, "\n");
+
+    /* re-sort the entries */
+    qsort(info, recordCount, infoSize, compareHeapRecords);
+
+    /* dump the entries to the file */
+    const uint8_t* ptr = info;
+    for (size_t idx = 0; idx < recordCount; idx++) {
+        size_t size = *(size_t*) ptr;
+        size_t allocations = *(size_t*) (ptr + sizeof(size_t));
+        intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2);
+
+        fprintf(fp, "z %d  sz %8zu  num %4zu  bt",
+                (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
+                size & ~SIZE_FLAG_ZYGOTE_CHILD,
+                allocations);
+        for (size_t bt = 0; bt < backtraceSize; bt++) {
+            if (backtrace[bt] == 0) {
+                break;
+            } else {
+                fprintf(fp, " %08x", backtrace[bt]);
+            }
+        }
+        fprintf(fp, "\n");
+
+        ptr += infoSize;
+    }
+
+    free_malloc_leak_info(info);
+
+    fprintf(fp, "MAPS\n");
+    const char* maps = "/proc/self/maps";
+    FILE* in = fopen(maps, "r");
+    if (in == NULL) {
+        fprintf(fp, "Could not open %s\n", maps);
+        return;
+    }
+    char buf[BUFSIZ];
+    while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) {
+        fwrite(buf, sizeof(char), n, fp);
+    }
+    fclose(in);
+
+    fprintf(fp, "END\n");
+}
+#endif /*HAVE_ANDROID_OS*/
+
+/*
+ * Dump the native heap, writing human-readable output to the specified
+ * file descriptor.
+ */
+static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz,
+    jobject fileDescriptor)
+{
+    if (fileDescriptor == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+    int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+    if (origFd < 0) {
+        jniThrowRuntimeException(env, "Invalid file descriptor");
+        return;
+    }
+
+    /* dup() the descriptor so we don't close the original with fclose() */
+    int fd = dup(origFd);
+    if (fd < 0) {
+        LOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
+        jniThrowRuntimeException(env, "dup() failed");
+        return;
+    }
+
+    FILE* fp = fdopen(fd, "w");
+    if (fp == NULL) {
+        LOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
+        close(fd);
+        jniThrowRuntimeException(env, "fdopen() failed");
+        return;
+    }
+
+#ifdef HAVE_ANDROID_OS
+    LOGD("Native heap dump starting...\n");
+    dumpNativeHeap(fp);
+    LOGD("Native heap dump complete.\n");
+#else
+    fprintf(fp, "Native heap dump not available on this platform\n");
+#endif
+
+    fclose(fp);
+}
+
+
 /*
  * JNI registration.
  */
@@ -289,6 +476,8 @@
             (void*) android_os_Debug_getDirtyPages },
     { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)V",
             (void*) android_os_Debug_getDirtyPagesPid },
+    { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
+            (void*) android_os_Debug_dumpNativeHeap },
     { "getBinderSentTransactions", "()I",
             (void*) android_os_Debug_getBinderSentTransactions },
     { "getBinderReceivedTransactions", "()I",
@@ -320,4 +509,4 @@
     return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
 }
 
-};
+}; // namespace android
diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp
index 21cb919..d3faa2f 100644
--- a/core/jni/android_os_FileUtils.cpp
+++ b/core/jni/android_os_FileUtils.cpp
@@ -113,6 +113,11 @@
     #endif
 }
 
+jint android_os_FileUtils_setUMask(JNIEnv* env, jobject clazz, jint mask)
+{
+    return umask(mask);
+}
+
 jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring path)
 {
     #if HAVE_ANDROID_OS
@@ -170,6 +175,7 @@
 static const JNINativeMethod methods[] = {
     {"setPermissions",  "(Ljava/lang/String;III)I", (void*)android_os_FileUtils_setPermissions},
     {"getPermissions",  "(Ljava/lang/String;[I)I", (void*)android_os_FileUtils_getPermissions},
+    {"setUMask",        "(I)I",                    (void*)android_os_FileUtils_setUMask},
     {"getFatVolumeId",  "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId},
     {"getFileStatus",  "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z", (void*)android_os_FileUtils_getFileStatus},
 };
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index d8e049d..b0ba695 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -50,6 +50,8 @@
 static jmethodID method_onDeviceCreated;
 static jmethodID method_onDeviceRemoved;
 static jmethodID method_onDeviceDisconnectRequested;
+static jmethodID method_onNetworkDeviceDisconnected;
+static jmethodID method_onNetworkDeviceConnected;
 
 static jmethodID method_onCreatePairedDeviceResult;
 static jmethodID method_onCreateDeviceResult;
@@ -66,6 +68,11 @@
 static jmethodID method_onAgentAuthorize;
 static jmethodID method_onAgentCancel;
 
+static jmethodID method_onInputDevicePropertyChanged;
+static jmethodID method_onInputDeviceConnectionResult;
+static jmethodID method_onPanDevicePropertyChanged;
+static jmethodID method_onPanDeviceConnectionResult;
+
 typedef event_loop_native_data_t native_data_t;
 
 #define EVENT_LOOP_REFS 10
@@ -97,6 +104,10 @@
     method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V");
     method_onDeviceDisconnectRequested = env->GetMethodID(clazz, "onDeviceDisconnectRequested",
                                                         "(Ljava/lang/String;)V");
+    method_onNetworkDeviceConnected = env->GetMethodID(clazz, "onNetworkDeviceConnected",
+                                                     "(Ljava/lang/String;Ljava/lang/String;I)V");
+    method_onNetworkDeviceDisconnected = env->GetMethodID(clazz, "onNetworkDeviceDisconnected",
+                                                              "(Ljava/lang/String;)V");
 
     method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
                                                          "(Ljava/lang/String;I)V");
@@ -120,6 +131,14 @@
                                                "(Ljava/lang/String;I)V");
     method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey",
                                                "(Ljava/lang/String;II)V");
+    method_onInputDevicePropertyChanged = env->GetMethodID(clazz, "onInputDevicePropertyChanged",
+                                               "(Ljava/lang/String;[Ljava/lang/String;)V");
+    method_onInputDeviceConnectionResult = env->GetMethodID(clazz, "onInputDeviceConnectionResult",
+                                               "(Ljava/lang/String;Z)V");
+    method_onPanDevicePropertyChanged = env->GetMethodID(clazz, "onPanDevicePropertyChanged",
+                                               "(Ljava/lang/String;[Ljava/lang/String;)V");
+    method_onPanDeviceConnectionResult = env->GetMethodID(clazz, "onPanDeviceConnectionResult",
+                                               "(Ljava/lang/String;Z)V");
     method_onRequestOobData = env->GetMethodID(clazz, "onRequestOobData",
                                                "(Ljava/lang/String;I)V");
 
@@ -232,6 +251,27 @@
             return JNI_FALSE;
         }
         dbus_bus_add_match(nat->conn,
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Input'",
+                &err);
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+            return JNI_FALSE;
+        }
+        dbus_bus_add_match(nat->conn,
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Network'",
+                &err);
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+            return JNI_FALSE;
+        }
+        dbus_bus_add_match(nat->conn,
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".NetworkServer'",
+                &err);
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+            return JNI_FALSE;
+        }
+        dbus_bus_add_match(nat->conn,
                 "type='signal',interface='org.bluez.AudioSink'",
                 &err);
         if (dbus_error_is_set(&err)) {
@@ -390,13 +430,31 @@
         dbus_connection_unregister_object_path(nat->conn, agent_path);
 
         dbus_bus_remove_match(nat->conn,
-                "type='signal',interface='org.bluez.AudioSink'",
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".AudioSink'",
                 &err);
         if (dbus_error_is_set(&err)) {
             LOG_AND_FREE_DBUS_ERROR(&err);
         }
         dbus_bus_remove_match(nat->conn,
-                "type='signal',interface='org.bluez.Device'",
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'",
+                &err);
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+        }
+        dbus_bus_remove_match(nat->conn,
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Input'",
+                &err);
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+        }
+        dbus_bus_remove_match(nat->conn,
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Network'",
+                &err);
+        if (dbus_error_is_set(&err)) {
+            LOG_AND_FREE_DBUS_ERROR(&err);
+        }
+        dbus_bus_remove_match(nat->conn,
+                "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".NetworkServer'",
                 &err);
         if (dbus_error_is_set(&err)) {
             LOG_AND_FREE_DBUS_ERROR(&err);
@@ -755,7 +813,7 @@
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     }
 
-    LOGE("%s: Received signal %s:%s from %s", __FUNCTION__,
+    LOGV("%s: Received signal %s:%s from %s", __FUNCTION__,
         dbus_message_get_interface(msg), dbus_message_get_member(msg),
         dbus_message_get_path(msg));
 
@@ -861,6 +919,73 @@
                             method_onDeviceDisconnectRequested,
                             env->NewStringUTF(remote_device_path));
         goto success;
+    } else if (dbus_message_is_signal(msg,
+                                      "org.bluez.Input",
+                                      "PropertyChanged")) {
+
+        jobjectArray str_array =
+                    parse_input_property_change(env, msg);
+        if (str_array != NULL) {
+            const char *c_path = dbus_message_get_path(msg);
+            env->CallVoidMethod(nat->me,
+                                method_onInputDevicePropertyChanged,
+                                env->NewStringUTF(c_path),
+                                str_array);
+        } else {
+            LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+        }
+        goto success;
+    } else if (dbus_message_is_signal(msg,
+                                     "org.bluez.Network",
+                                     "PropertyChanged")) {
+
+       jobjectArray str_array =
+                   parse_pan_property_change(env, msg);
+       if (str_array != NULL) {
+           const char *c_path = dbus_message_get_path(msg);
+           env->CallVoidMethod(nat->me,
+                               method_onPanDevicePropertyChanged,
+                               env->NewStringUTF(c_path),
+                               str_array);
+       } else {
+           LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+       }
+       goto success;
+    } else if (dbus_message_is_signal(msg,
+                                     "org.bluez.NetworkServer",
+                                     "DeviceDisconnected")) {
+       char *c_address;
+       if (dbus_message_get_args(msg, &err,
+                                  DBUS_TYPE_STRING, &c_address,
+                                  DBUS_TYPE_INVALID)) {
+           env->CallVoidMethod(nat->me,
+                               method_onNetworkDeviceDisconnected,
+                               env->NewStringUTF(c_address));
+       } else {
+           LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+       }
+       goto success;
+    } else if (dbus_message_is_signal(msg,
+                                     "org.bluez.NetworkServer",
+                                     "DeviceConnected")) {
+       char *c_address;
+       char *c_iface;
+       uint16_t uuid;
+
+       if (dbus_message_get_args(msg, &err,
+                                  DBUS_TYPE_STRING, &c_address,
+                                  DBUS_TYPE_STRING, &c_iface,
+                                  DBUS_TYPE_UINT16, &uuid,
+                                  DBUS_TYPE_INVALID)) {
+           env->CallVoidMethod(nat->me,
+                               method_onNetworkDeviceConnected,
+                               env->NewStringUTF(c_address),
+                               env->NewStringUTF(c_iface),
+                               uuid);
+       } else {
+           LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+       }
+       goto success;
     }
 
     ret = a2dp_event_filter(msg, env);
@@ -1270,6 +1395,57 @@
     env->DeleteLocalRef(addr);
     free(user);
 }
+
+void onInputDeviceConnectionResult(DBusMessage *msg, void *user, void *n) {
+    LOGV(__FUNCTION__);
+
+    native_data_t *nat = (native_data_t *)n;
+    const char *path = (const char *)user;
+    DBusError err;
+    dbus_error_init(&err);
+    JNIEnv *env;
+    nat->vm->GetEnv((void**)&env, nat->envVer);
+
+    bool result = JNI_TRUE;
+    if (dbus_set_error_from_message(&err, msg)) {
+        LOG_AND_FREE_DBUS_ERROR(&err);
+        result = JNI_FALSE;
+    }
+    LOGV("... Device Path = %s, result = %d", path, result);
+    jstring jPath = env->NewStringUTF(path);
+    env->CallVoidMethod(nat->me,
+                        method_onInputDeviceConnectionResult,
+                        jPath,
+                        result);
+    env->DeleteLocalRef(jPath);
+    free(user);
+}
+
+void onPanDeviceConnectionResult(DBusMessage *msg, void *user, void *n) {
+    LOGV(__FUNCTION__);
+
+    native_data_t *nat = (native_data_t *)n;
+    const char *path = (const char *)user;
+    DBusError err;
+    dbus_error_init(&err);
+    JNIEnv *env;
+    nat->vm->GetEnv((void**)&env, nat->envVer);
+
+    bool result = JNI_TRUE;
+    if (dbus_set_error_from_message(&err, msg)) {
+        LOG_AND_FREE_DBUS_ERROR(&err);
+        result = JNI_FALSE;
+    }
+    LOGV("... Pan Device Path = %s, result = %d", path, result);
+    jstring jPath = env->NewStringUTF(path);
+    env->CallVoidMethod(nat->me,
+                        method_onPanDeviceConnectionResult,
+                        jPath,
+                        result);
+    env->DeleteLocalRef(jPath);
+    free(user);
+}
+
 #endif
 
 static JNINativeMethod sMethods[] = {
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index daa59a6..848b8f5 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -16,6 +16,11 @@
 
 #define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"
 #define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
+#define DBUS_INPUT_IFACE BLUEZ_DBUS_BASE_IFC ".Input"
+#define DBUS_NETWORK_IFACE BLUEZ_DBUS_BASE_IFC ".Network"
+#define DBUS_NETWORKSERVER_IFACE BLUEZ_DBUS_BASE_IFC ".NetworkServer"
+
+
 #define LOG_TAG "BluetoothService.cpp"
 
 #include "android_bluetooth_common.h"
@@ -68,6 +73,9 @@
 void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat);
 void onDiscoverServicesResult(DBusMessage *msg, void *user, void *nat);
 void onCreateDeviceResult(DBusMessage *msg, void *user, void *nat);
+void onInputDeviceConnectionResult(DBusMessage *msg, void *user, void *nat);
+void onConnectPanResult(DBusMessage *msg, void *user, void *n);
+void onPanDeviceConnectionResult(DBusMessage *msg, void *user, void *nat);
 
 
 /** Get native data stored in the opaque (Java code maintained) pointer mNativeData
@@ -736,11 +744,10 @@
     LOGV(__FUNCTION__);
     native_data_t *nat = get_native_data(env, object);
     if (nat) {
-        DBusMessage *reply, *msg;
+        DBusMessage *msg;
         DBusMessageIter iter;
-        DBusError err;
+        dbus_bool_t reply = JNI_FALSE;
         const char *c_key = env->GetStringUTFChars(key, NULL);
-        dbus_error_init(&err);
 
         msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
                                            get_adapter_path(env, object),
@@ -756,19 +763,13 @@
         dbus_message_iter_init_append(msg, &iter);
         append_variant(&iter, type, value);
 
-        reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
+        // Asynchronous call - the callbacks come via propertyChange
+        reply = dbus_connection_send_with_reply(nat->conn, msg, NULL, -1);
         dbus_message_unref(msg);
 
         env->ReleaseStringUTFChars(key, c_key);
+        return reply ? JNI_TRUE : JNI_FALSE;
 
-        if (!reply) {
-            if (dbus_error_is_set(&err)) {
-                LOG_AND_FREE_DBUS_ERROR(&err);
-            } else
-                LOGE("DBus reply is NULL in function %s", __FUNCTION__);
-            return JNI_FALSE;
-        }
-        return JNI_TRUE;
     }
 #endif
     return JNI_FALSE;
@@ -990,6 +991,160 @@
     return JNI_FALSE;
 }
 
+static jboolean connectInputDeviceNative(JNIEnv *env, jobject object, jstring path) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat = get_native_data(env, object);
+    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+    struct event_loop_native_data_t *eventLoopNat =
+            get_EventLoop_native_data(env, eventLoop);
+
+    if (nat && eventLoopNat) {
+        const char *c_path = env->GetStringUTFChars(path, NULL);
+
+        int len = env->GetStringLength(path) + 1;
+        char *context_path = (char *)calloc(len, sizeof(char));
+        strlcpy(context_path, c_path, len);  // for callback
+
+        bool ret = dbus_func_args_async(env, nat->conn, -1, onInputDeviceConnectionResult,
+                                        context_path, eventLoopNat, c_path, DBUS_INPUT_IFACE,
+                                        "Connect",
+                                        DBUS_TYPE_INVALID);
+
+        env->ReleaseStringUTFChars(path, c_path);
+        return ret ? JNI_TRUE : JNI_FALSE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
+static jboolean disconnectInputDeviceNative(JNIEnv *env, jobject object,
+                                     jstring path) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat = get_native_data(env, object);
+    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+    struct event_loop_native_data_t *eventLoopNat =
+            get_EventLoop_native_data(env, eventLoop);
+
+    if (nat && eventLoopNat) {
+        const char *c_path = env->GetStringUTFChars(path, NULL);
+
+        int len = env->GetStringLength(path) + 1;
+        char *context_path = (char *)calloc(len, sizeof(char));
+        strlcpy(context_path, c_path, len);  // for callback
+
+        bool ret = dbus_func_args_async(env, nat->conn, -1, onInputDeviceConnectionResult,
+                                        context_path, eventLoopNat, c_path, DBUS_INPUT_IFACE,
+                                        "Disconnect",
+                                        DBUS_TYPE_INVALID);
+
+        env->ReleaseStringUTFChars(path, c_path);
+        return ret ? JNI_TRUE : JNI_FALSE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
+static jboolean setBluetoothTetheringNative(JNIEnv *env, jobject object, jboolean value,
+                                            jstring src_role, jstring bridge) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat = get_native_data(env, object);
+    if (nat) {
+        DBusMessage *reply;
+        const char *c_role = env->GetStringUTFChars(src_role, NULL);
+        const char *c_bridge = env->GetStringUTFChars(bridge, NULL);
+        if (value) {
+            LOGE("setBluetoothTetheringNative true");
+            reply = dbus_func_args(env, nat->conn,
+                                  get_adapter_path(env, object),
+                                  DBUS_NETWORKSERVER_IFACE,
+                                  "Register",
+                                  DBUS_TYPE_STRING, &c_role,
+                                  DBUS_TYPE_STRING, &c_bridge,
+                                  DBUS_TYPE_INVALID);
+        } else {
+            LOGE("setBluetoothTetheringNative false");
+            reply = dbus_func_args(env, nat->conn,
+                                  get_adapter_path(env, object),
+                                  DBUS_NETWORKSERVER_IFACE,
+                                  "Unregister",
+                                  DBUS_TYPE_STRING, &c_role,
+                                  DBUS_TYPE_INVALID);
+        }
+        env->ReleaseStringUTFChars(src_role, c_role);
+        env->ReleaseStringUTFChars(bridge, c_bridge);
+        return reply ? JNI_TRUE : JNI_FALSE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
+static jboolean connectPanDeviceNative(JNIEnv *env, jobject object, jstring path,
+                                       jstring srcRole, jstring dstRole) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    LOGE("connectPanDeviceNative");
+    native_data_t *nat = get_native_data(env, object);
+    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+    struct event_loop_native_data_t *eventLoopNat =
+            get_EventLoop_native_data(env, eventLoop);
+
+    if (nat && eventLoopNat) {
+        const char *c_path = env->GetStringUTFChars(path, NULL);
+        const char *src = env->GetStringUTFChars(srcRole, NULL);
+        const char *dst = env->GetStringUTFChars(dstRole, NULL);
+
+        int len = env->GetStringLength(path) + 1;
+        char *context_path = (char *)calloc(len, sizeof(char));
+        strlcpy(context_path, c_path, len);  // for callback
+
+        bool ret = dbus_func_args_async(env, nat->conn, -1,onPanDeviceConnectionResult,
+                                    context_path, eventLoopNat, c_path,
+                                    DBUS_NETWORK_IFACE, "Connect",
+                                    DBUS_TYPE_STRING, &src,
+                                    DBUS_TYPE_STRING, &dst,
+                                    DBUS_TYPE_INVALID);
+
+        env->ReleaseStringUTFChars(path, c_path);
+        env->ReleaseStringUTFChars(srcRole, src);
+        env->ReleaseStringUTFChars(dstRole, dst);
+        return ret ? JNI_TRUE : JNI_FALSE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
+static jboolean disconnectPanDeviceNative(JNIEnv *env, jobject object,
+                                     jstring path) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    LOGE("disconnectPanDeviceNative");
+    native_data_t *nat = get_native_data(env, object);
+    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+    struct event_loop_native_data_t *eventLoopNat =
+            get_EventLoop_native_data(env, eventLoop);
+
+    if (nat && eventLoopNat) {
+        const char *c_path = env->GetStringUTFChars(path, NULL);
+
+        int len = env->GetStringLength(path) + 1;
+        char *context_path = (char *)calloc(len, sizeof(char));
+        strlcpy(context_path, c_path, len);  // for callback
+
+        bool ret = dbus_func_args_async(env, nat->conn, -1,onPanDeviceConnectionResult,
+                                        context_path, eventLoopNat, c_path,
+                                        DBUS_NETWORK_IFACE, "Disconnect",
+                                        DBUS_TYPE_INVALID);
+
+        env->ReleaseStringUTFChars(path, c_path);
+        return ret ? JNI_TRUE : JNI_FALSE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
 static JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     {"classInitNative", "()V", (void*)classInitNative},
@@ -1039,8 +1194,18 @@
     {"addRfcommServiceRecordNative", "(Ljava/lang/String;JJS)I", (void *)addRfcommServiceRecordNative},
     {"removeServiceRecordNative", "(I)Z", (void *)removeServiceRecordNative},
     {"setLinkTimeoutNative", "(Ljava/lang/String;I)Z", (void *)setLinkTimeoutNative},
+    // HID functions
+    {"connectInputDeviceNative", "(Ljava/lang/String;)Z", (void *)connectInputDeviceNative},
+    {"disconnectInputDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectInputDeviceNative},
+
+    {"setBluetoothTetheringNative", "(ZLjava/lang/String;Ljava/lang/String;)Z",
+              (void *)setBluetoothTetheringNative},
+    {"connectPanDeviceNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+              (void *)connectPanDeviceNative},
+    {"disconnectPanDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectPanDeviceNative},
 };
 
+
 int register_android_server_BluetoothService(JNIEnv *env) {
     return AndroidRuntime::registerNativeMethods(env,
                 "android/server/BluetoothService", sMethods, NELEM(sMethods));
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index f3b9357..c4056a4 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -701,6 +701,7 @@
 
 static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz,
                                                            jint ident,
+                                                           jshort density,
                                                            jobject outValue,
                                                            jboolean resolve)
 {
@@ -713,7 +714,7 @@
     Res_value value;
     ResTable_config config;
     uint32_t typeSpecFlags;
-    ssize_t block = res.getResource(ident, &value, false, &typeSpecFlags, &config);
+    ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config);
 #if THROW_ON_BAD_ID
     if (block == BAD_INDEX) {
         jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
@@ -1703,7 +1704,7 @@
         (void*) android_content_AssetManager_getResourceTypeName },
     { "getResourceEntryName","(I)Ljava/lang/String;",
         (void*) android_content_AssetManager_getResourceEntryName },
-    { "loadResourceValue","(ILandroid/util/TypedValue;Z)I",
+    { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I",
         (void*) android_content_AssetManager_loadResourceValue },
     { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I",
         (void*) android_content_AssetManager_loadResourceBagValue },
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
new file mode 100644
index 0000000..6d848a4
--- /dev/null
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/ResourceTypes.h>
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkRegion.h>
+#include <SkScalerContext.h>
+#include <SkTemplates.h>
+#include <SkXfermode.h>
+
+#include <DisplayListRenderer.h>
+#include <OpenGLDebugRenderer.h>
+#include <OpenGLRenderer.h>
+#include <SkiaShader.h>
+#include <SkiaColorFilter.h>
+#include <Rect.h>
+
+#include "TextLayout.h"
+
+namespace android {
+
+using namespace uirenderer;
+
+/**
+ * Note: OpenGLRenderer JNI layer is generated and compiled only on supported
+ *       devices. This means all the logic must be compiled only when the
+ *       preprocessor variable USE_OPENGL_RENDERER is defined.
+ */
+#ifdef USE_OPENGL_RENDERER
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_RENDERER 0
+#define PROFILE_RENDERER 0
+
+// Debug
+#if DEBUG_RENDERER
+    #define RENDERER_LOGD(...) LOGD(__VA_ARGS__)
+#else
+    #define RENDERER_LOGD(...)
+#endif
+
+// ----------------------------------------------------------------------------
+// Java APIs
+// ----------------------------------------------------------------------------
+
+static struct {
+    jclass clazz;
+    jmethodID set;
+} gRectClassInfo;
+
+// ----------------------------------------------------------------------------
+// Constructors
+// ----------------------------------------------------------------------------
+
+static OpenGLRenderer* android_view_GLES20Canvas_createRenderer(JNIEnv* env, jobject canvas) {
+    RENDERER_LOGD("Create OpenGLRenderer");
+#if PROFILE_RENDERER
+    return new OpenGLDebugRenderer;
+#else
+    return new OpenGLRenderer;
+#endif
+}
+
+static void android_view_GLES20Canvas_destroyRenderer(JNIEnv* env, jobject clazz,
+        OpenGLRenderer* renderer) {
+    RENDERER_LOGD("Destroy OpenGLRenderer");
+    delete renderer;
+}
+
+// ----------------------------------------------------------------------------
+// Setup
+// ----------------------------------------------------------------------------
+
+static void android_view_GLES20Canvas_setViewport(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jint width, jint height) {
+    renderer->setViewport(width, height);
+}
+
+static void android_view_GLES20Canvas_prepare(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jboolean opaque) {
+    renderer->prepare(opaque);
+}
+
+static void android_view_GLES20Canvas_finish(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer) {
+    renderer->finish();
+}
+
+static void android_view_GLES20Canvas_acquireContext(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer) {
+    renderer->acquireContext();
+}
+
+static void android_view_GLES20Canvas_releaseContext(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer) {
+    renderer->releaseContext();
+}
+
+// ----------------------------------------------------------------------------
+// State
+// ----------------------------------------------------------------------------
+
+static jint android_view_GLES20Canvas_save(JNIEnv* env, jobject canvas, OpenGLRenderer* renderer,
+        jint flags) {
+    return renderer->save(flags);
+}
+
+static jint android_view_GLES20Canvas_getSaveCount(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer) {
+    return renderer->getSaveCount();
+}
+
+static void android_view_GLES20Canvas_restore(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer) {
+    renderer->restore();
+}
+
+static void android_view_GLES20Canvas_restoreToCount(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jint saveCount) {
+    renderer->restoreToCount(saveCount);
+}
+
+// ----------------------------------------------------------------------------
+// Layers
+// ----------------------------------------------------------------------------
+
+static jint android_view_GLES20Canvas_saveLayer(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
+        SkPaint* paint, jint saveFlags) {
+    return renderer->saveLayer(left, top, right, bottom, paint, saveFlags);
+}
+
+static jint android_view_GLES20Canvas_saveLayerAlpha(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
+        jint alpha, jint saveFlags) {
+    return renderer->saveLayerAlpha(left, top, right, bottom, alpha, saveFlags);
+}
+
+// ----------------------------------------------------------------------------
+// Clipping
+// ----------------------------------------------------------------------------
+
+static bool android_view_GLES20Canvas_quickReject(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
+        SkCanvas::EdgeType edge) {
+    return renderer->quickReject(left, top, right, bottom);
+}
+
+static bool android_view_GLES20Canvas_clipRectF(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
+        SkRegion::Op op) {
+    return renderer->clipRect(left, top, right, bottom, op);
+}
+
+static bool android_view_GLES20Canvas_clipRect(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jint left, jint top, jint right, jint bottom,
+        SkRegion::Op op) {
+    return renderer->clipRect(float(left), float(top), float(right), float(bottom), op);
+}
+
+static bool android_view_GLES20Canvas_getClipBounds(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jobject rect) {
+    const android::uirenderer::Rect& bounds(renderer->getClipBounds());
+
+    env->CallVoidMethod(rect, gRectClassInfo.set,
+            int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom));
+
+    return !bounds.isEmpty();
+}
+
+// ----------------------------------------------------------------------------
+// Transforms
+// ----------------------------------------------------------------------------
+
+static void android_view_GLES20Canvas_translate(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jfloat dx, jfloat dy) {
+    renderer->translate(dx, dy);
+}
+
+static void android_view_GLES20Canvas_rotate(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jfloat degrees) {
+    renderer->rotate(degrees);
+}
+
+static void android_view_GLES20Canvas_scale(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jfloat sx, jfloat sy) {
+    renderer->scale(sx, sy);
+}
+
+static void android_view_GLES20Canvas_setMatrix(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkMatrix* matrix) {
+    renderer->setMatrix(matrix);
+}
+
+static const float* android_view_GLES20Canvas_getNativeMatrix(JNIEnv* env,
+        jobject canvas, OpenGLRenderer* renderer) {
+    return renderer->getMatrix();
+}
+
+static void android_view_GLES20Canvas_getMatrix(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkMatrix* matrix) {
+    renderer->getMatrix(matrix);
+}
+
+static void android_view_GLES20Canvas_concatMatrix(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkMatrix* matrix) {
+    renderer->concatMatrix(matrix);
+}
+
+// ----------------------------------------------------------------------------
+// Drawing
+// ----------------------------------------------------------------------------
+
+static void android_view_GLES20Canvas_drawBitmap(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkBitmap* bitmap, float left, float top, SkPaint* paint) {
+    renderer->drawBitmap(bitmap, left, top, paint);
+}
+
+static void android_view_GLES20Canvas_drawBitmapRect(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkBitmap* bitmap,
+        float srcLeft, float srcTop, float srcRight, float srcBottom,
+        float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint) {
+    renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
+            dstLeft, dstTop, dstRight, dstBottom, paint);
+}
+
+static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
+    renderer->drawBitmap(bitmap, matrix, paint);
+}
+
+static void android_view_GLES20Canvas_drawPatch(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkBitmap* bitmap, jbyteArray chunks,
+        float left, float top, float right, float bottom, SkPaint* paint) {
+    jbyte* storage = env->GetByteArrayElements(chunks, NULL);
+    Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(storage);
+    Res_png_9patch::deserialize(patch);
+
+    renderer->drawPatch(bitmap, &patch->xDivs[0], &patch->yDivs[0], &patch->colors[0],
+            patch->numXDivs, patch->numYDivs, patch->numColors,
+            left, top, right, bottom, paint);
+
+    env->ReleaseByteArrayElements(chunks, storage, 0);
+}
+
+static void android_view_GLES20Canvas_drawColor(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jint color, SkXfermode::Mode mode) {
+    renderer->drawColor(color, mode);
+}
+
+static void android_view_GLES20Canvas_drawRect(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
+        SkPaint* paint) {
+    renderer->drawRect(left, top, right, bottom, paint);
+}
+
+static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkRegion* region, SkPaint* paint) {
+    SkRegion::Iterator it(*region);
+    while (!it.done()) {
+        const SkIRect& r = it.rect();
+        renderer->drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
+        it.next();
+    }
+}
+
+static void android_view_GLES20Canvas_drawPath(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkPath* path, SkPaint* paint) {
+    renderer->drawPath(path, paint);
+}
+
+static void android_view_GLES20Canvas_drawLines(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jfloatArray points, jint offset, jint count, SkPaint* paint) {
+    jfloat* storage = env->GetFloatArrayElements(points, NULL);
+
+    renderer->drawLines(storage + offset, count, paint);
+
+    env->ReleaseFloatArrayElements(points, storage, 0);
+}
+
+// ----------------------------------------------------------------------------
+// Shaders and color filters
+// ----------------------------------------------------------------------------
+
+static void android_view_GLES20Canvas_resetModifiers(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer) {
+    renderer->resetShader();
+    renderer->resetColorFilter();
+    renderer->resetShadow();
+}
+
+static void android_view_GLES20Canvas_setupShader(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkiaShader* shader) {
+    renderer->setupShader(shader);
+}
+
+static void android_view_GLES20Canvas_setupColorFilter(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkiaColorFilter* filter) {
+    renderer->setupColorFilter(filter);
+}
+
+static void android_view_GLES20Canvas_setupShadow(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jfloat radius, jfloat dx, jfloat dy, jint color) {
+    renderer->setupShadow(radius, dx, dy, color);
+}
+
+// ----------------------------------------------------------------------------
+// Text
+// ----------------------------------------------------------------------------
+
+static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
+        jfloat x, jfloat y, int flags, SkPaint* paint) {
+    const jchar *workText;
+    jchar* buffer = NULL;
+    int32_t workBytes;
+    if (TextLayout::prepareText(paint, text, count, flags, &workText, &workBytes, &buffer)) {
+        renderer->drawText((const char*) workText, workBytes, count, x, y, paint);
+        free(buffer);
+    }
+}
+
+static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
+        jint start, jint count, jint contextCount, jfloat x, jfloat y,
+        int flags, SkPaint* paint) {
+    uint8_t rtl = flags & 0x1;
+    if (rtl) {
+        SkAutoSTMalloc<80, jchar> buffer(contextCount);
+        jchar* shaped = buffer.get();
+        if (TextLayout::prepareRtlTextRun(text, start, count, contextCount, shaped)) {
+            renderer->drawText((const char*) shaped, count << 1, count, x, y, paint);
+        } else {
+            LOGW("drawTextRun error");
+        }
+    } else {
+        renderer->drawText((const char*) (text + start), count << 1, count, x, y, paint);
+    }
+}
+
+static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jcharArray text, int index, int count,
+        jfloat x, jfloat y, int flags, SkPaint* paint) {
+    jchar* textArray = env->GetCharArrayElements(text, NULL);
+    renderText(renderer, textArray + index, count, x, y, flags, paint);
+    env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+}
+
+static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jstring text, int start, int end,
+        jfloat x, jfloat y, int flags, SkPaint* paint) {
+    const jchar* textArray = env->GetStringChars(text, NULL);
+    renderText(renderer, textArray + start, end - start, x, y, flags, paint);
+    env->ReleaseStringChars(text, textArray);
+}
+
+static void android_view_GLES20Canvas_drawTextRunArray(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jcharArray text, int index, int count,
+        int contextIndex, int contextCount, jfloat x, jfloat y, int dirFlags,
+        SkPaint* paint) {
+    jchar* textArray = env->GetCharArrayElements(text, NULL);
+    renderTextRun(renderer, textArray + contextIndex, index - contextIndex,
+            count, contextCount, x, y, dirFlags, paint);
+    env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+ }
+
+static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jstring text, int start, int end,
+        int contextStart, int contextEnd, jfloat x, jfloat y, int dirFlags,
+        SkPaint* paint) {
+    const jchar* textArray = env->GetStringChars(text, NULL);
+    jint count = end - start;
+    jint contextCount = contextEnd - contextStart;
+    renderTextRun(renderer, textArray + contextStart, start - contextStart,
+            count, contextCount, x, y, dirFlags, paint);
+    env->ReleaseStringChars(text, textArray);
+}
+
+// ----------------------------------------------------------------------------
+// Display lists
+// ----------------------------------------------------------------------------
+
+static OpenGLRenderer* android_view_GLES20Canvas_createDisplayListRenderer(
+        JNIEnv* env, jobject canvas) {
+    return new DisplayListRenderer;
+}
+
+static DisplayList* android_view_GLES20Canvas_createDisplayList(JNIEnv* env,
+        jobject canvas, DisplayListRenderer* renderer) {
+    return renderer->getDisplayList();
+}
+
+static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env,
+        jobject clazz, DisplayList* displayList) {
+    delete displayList;
+}
+
+static void android_view_GLES20Canvas_drawDisplayList(JNIEnv* env,
+        jobject canvas, OpenGLRenderer* renderer, DisplayList* displayList) {
+    displayList->replay(*renderer);
+}
+
+#endif // USE_OPENGL_RENDERER
+
+// ----------------------------------------------------------------------------
+// Common
+// ----------------------------------------------------------------------------
+
+static jboolean android_view_GLES20Canvas_isAvailable(JNIEnv* env, jobject clazz) {
+#ifdef USE_OPENGL_RENDERER
+    return JNI_TRUE;
+#else
+    return JNI_FALSE;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/view/GLES20Canvas";
+
+static JNINativeMethod gMethods[] = {
+    { "nIsAvailable",       "()Z",             (void*) android_view_GLES20Canvas_isAvailable },
+
+#ifdef USE_OPENGL_RENDERER
+    { "nCreateRenderer",    "()I",             (void*) android_view_GLES20Canvas_createRenderer },
+    { "nDestroyRenderer",   "(I)V",            (void*) android_view_GLES20Canvas_destroyRenderer },
+    { "nSetViewport",       "(III)V",          (void*) android_view_GLES20Canvas_setViewport },
+    { "nPrepare",           "(IZ)V",           (void*) android_view_GLES20Canvas_prepare },
+    { "nFinish",            "(I)V",            (void*) android_view_GLES20Canvas_finish },
+    { "nAcquireContext",    "(I)V",            (void*) android_view_GLES20Canvas_acquireContext },
+    { "nReleaseContext",    "(I)V",            (void*) android_view_GLES20Canvas_releaseContext },
+
+    { "nSave",              "(II)I",           (void*) android_view_GLES20Canvas_save },
+    { "nRestore",           "(I)V",            (void*) android_view_GLES20Canvas_restore },
+    { "nRestoreToCount",    "(II)V",           (void*) android_view_GLES20Canvas_restoreToCount },
+    { "nGetSaveCount",      "(I)I",            (void*) android_view_GLES20Canvas_getSaveCount },
+
+    { "nSaveLayer",         "(IFFFFII)I",      (void*) android_view_GLES20Canvas_saveLayer },
+    { "nSaveLayerAlpha",    "(IFFFFII)I",      (void*) android_view_GLES20Canvas_saveLayerAlpha },
+
+    { "nQuickReject",       "(IFFFFI)Z",       (void*) android_view_GLES20Canvas_quickReject },
+    { "nClipRect",          "(IFFFFI)Z",       (void*) android_view_GLES20Canvas_clipRectF },
+    { "nClipRect",          "(IIIIII)Z",       (void*) android_view_GLES20Canvas_clipRect },
+
+    { "nTranslate",         "(IFF)V",          (void*) android_view_GLES20Canvas_translate },
+    { "nRotate",            "(IF)V",           (void*) android_view_GLES20Canvas_rotate },
+    { "nScale",             "(IFF)V",          (void*) android_view_GLES20Canvas_scale },
+
+    { "nSetMatrix",         "(II)V",           (void*) android_view_GLES20Canvas_setMatrix },
+    { "nGetMatrix",         "(I)I",            (void*) android_view_GLES20Canvas_getNativeMatrix },
+    { "nGetMatrix",         "(II)V",           (void*) android_view_GLES20Canvas_getMatrix },
+    { "nConcatMatrix",      "(II)V",           (void*) android_view_GLES20Canvas_concatMatrix },
+
+    { "nDrawBitmap",        "(IIFFI)V",        (void*) android_view_GLES20Canvas_drawBitmap },
+    { "nDrawBitmap",        "(IIFFFFFFFFI)V",  (void*) android_view_GLES20Canvas_drawBitmapRect },
+    { "nDrawBitmap",        "(IIII)V",         (void*) android_view_GLES20Canvas_drawBitmapMatrix },
+    { "nDrawPatch",         "(II[BFFFFI)V",    (void*) android_view_GLES20Canvas_drawPatch },
+    { "nDrawColor",         "(III)V",          (void*) android_view_GLES20Canvas_drawColor },
+    { "nDrawRect",          "(IFFFFI)V",       (void*) android_view_GLES20Canvas_drawRect },
+    { "nDrawRects",         "(III)V",          (void*) android_view_GLES20Canvas_drawRects },
+    { "nDrawPath",          "(III)V",          (void*) android_view_GLES20Canvas_drawPath },
+    { "nDrawLines",         "(I[FIII)V",       (void*) android_view_GLES20Canvas_drawLines },
+
+    { "nResetModifiers",    "(I)V",            (void*) android_view_GLES20Canvas_resetModifiers },
+    { "nSetupShader",       "(II)V",           (void*) android_view_GLES20Canvas_setupShader },
+    { "nSetupColorFilter",  "(II)V",           (void*) android_view_GLES20Canvas_setupColorFilter },
+    { "nSetupShadow",       "(IFFFI)V",        (void*) android_view_GLES20Canvas_setupShadow },
+
+    { "nDrawText",          "(I[CIIFFII)V",    (void*) android_view_GLES20Canvas_drawTextArray },
+    { "nDrawText",          "(ILjava/lang/String;IIFFII)V",
+            (void*) android_view_GLES20Canvas_drawText },
+
+    { "nDrawTextRun",       "(I[CIIIIFFII)V",  (void*) android_view_GLES20Canvas_drawTextRunArray },
+    { "nDrawTextRun",       "(ILjava/lang/String;IIIIFFII)V",
+            (void*) android_view_GLES20Canvas_drawTextRun },
+
+    { "nGetClipBounds",     "(ILandroid/graphics/Rect;)Z",
+            (void*) android_view_GLES20Canvas_getClipBounds },
+
+    { "nCreateDisplayListRenderer", "()I",     (void*) android_view_GLES20Canvas_createDisplayListRenderer },
+    { "nCreateDisplayList",  "(I)I",           (void*) android_view_GLES20Canvas_createDisplayList },
+    { "nDestroyDisplayList", "(I)V",           (void*) android_view_GLES20Canvas_destroyDisplayList },
+    { "nDrawDisplayList",    "(II)V",          (void*) android_view_GLES20Canvas_drawDisplayList },
+
+#endif
+};
+
+#ifdef USE_OPENGL_RENDERER
+    #define FIND_CLASS(var, className) \
+            var = env->FindClass(className); \
+            LOG_FATAL_IF(! var, "Unable to find class " className); \
+            var = jclass(env->NewGlobalRef(var));
+    
+    #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+            var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+            LOG_FATAL_IF(! var, "Unable to find method " methodName);
+#else
+    #define FIND_CLASS(var, className)
+    #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor)
+#endif
+
+int register_android_view_GLES20Canvas(JNIEnv* env) {
+    FIND_CLASS(gRectClassInfo.clazz, "android/graphics/Rect");
+    GET_METHOD_ID(gRectClassInfo.set, gRectClassInfo.clazz, "set", "(IIII)V");
+
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+};
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 93fd54f..537ac72 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -22,10 +22,26 @@
 #include <utils/Log.h>
 #include <ui/Input.h>
 #include "android_view_MotionEvent.h"
+#include "android/graphics/Matrix.h"
+
+#include <math.h>
+#include "SkMatrix.h"
+#include "SkScalar.h"
 
 // Number of float items per entry in a DVM sample data array
 #define NUM_SAMPLE_DATA 9
 
+#define SAMPLE_X 0
+#define SAMPLE_Y 1
+#define SAMPLE_PRESSURE 2
+#define SAMPLE_SIZE 3
+#define SAMPLE_TOUCH_MAJOR 4
+#define SAMPLE_TOUCH_MINOR 5
+#define SAMPLE_TOOL_MAJOR 6
+#define SAMPLE_TOOL_MINOR 7
+#define SAMPLE_ORIENTATION 8
+
+
 namespace android {
 
 // ----------------------------------------------------------------------------
@@ -238,8 +254,87 @@
     }
 }
 
+static inline float transformAngle(const SkMatrix* matrix, float angleRadians) {
+    // Construct and transform a vector oriented at the specified clockwise angle from vertical.
+    // Coordinate system: down is increasing Y, right is increasing X.
+    SkPoint vector;
+    vector.fX = SkFloatToScalar(sinf(angleRadians));
+    vector.fY = SkFloatToScalar(- cosf(angleRadians));
+    matrix->mapVectors(& vector, 1);
+
+    // Derive the transformed vector's clockwise angle from vertical.
+    float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(- vector.fY));
+    if (result < - M_PI_2) {
+        result += M_PI;
+    } else if (result > M_PI_2) {
+        result -= M_PI;
+    }
+    return result;
+}
+
+static void android_view_MotionEvent_nativeTransform(JNIEnv* env,
+        jobject eventObj, jobject matrixObj) {
+    SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj);
+
+    jfloat oldXOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mXOffset);
+    jfloat oldYOffset = env->GetFloatField(eventObj, gMotionEventClassInfo.mYOffset);
+    jint numPointers = env->GetIntField(eventObj, gMotionEventClassInfo.mNumPointers);
+    jint numSamples = env->GetIntField(eventObj, gMotionEventClassInfo.mNumSamples);
+    jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj,
+            gMotionEventClassInfo.mDataSamples));
+    jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL);
+
+    // The tricky part of this implementation is to preserve the value of
+    // rawX and rawY.  So we apply the transformation to the first point
+    // then derive an appropriate new X/Y offset that will preserve rawX and rawY.
+    SkPoint point;
+    jfloat rawX = dataSamples[SAMPLE_X];
+    jfloat rawY = dataSamples[SAMPLE_Y];
+    matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset),
+            & point);
+    jfloat newX = SkScalarToFloat(point.fX);
+    jfloat newY = SkScalarToFloat(point.fY);
+    jfloat newXOffset = newX - rawX;
+    jfloat newYOffset = newY - rawY;
+
+    dataSamples[SAMPLE_ORIENTATION] = transformAngle(matrix, dataSamples[SAMPLE_ORIENTATION]);
+
+    // Apply the transformation to all samples.
+    jfloat* currentDataSample = dataSamples;
+    jfloat* endDataSample = dataSamples + numPointers * numSamples * NUM_SAMPLE_DATA;
+    for (;;) {
+        currentDataSample += NUM_SAMPLE_DATA;
+        if (currentDataSample == endDataSample) {
+            break;
+        }
+
+        jfloat x = currentDataSample[SAMPLE_X] + oldXOffset;
+        jfloat y = currentDataSample[SAMPLE_Y] + oldYOffset;
+        matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), & point);
+        currentDataSample[SAMPLE_X] = SkScalarToFloat(point.fX) - newXOffset;
+        currentDataSample[SAMPLE_Y] = SkScalarToFloat(point.fY) - newYOffset;
+
+        currentDataSample[SAMPLE_ORIENTATION] = transformAngle(matrix,
+                currentDataSample[SAMPLE_ORIENTATION]);
+    }
+
+    env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, 0);
+
+    env->SetFloatField(eventObj, gMotionEventClassInfo.mXOffset, newXOffset);
+    env->SetFloatField(eventObj, gMotionEventClassInfo.mYOffset, newYOffset);
+
+    env->DeleteLocalRef(dataSampleArray);
+}
+
 // ----------------------------------------------------------------------------
 
+static JNINativeMethod gMotionEventMethods[] = {
+    /* name, signature, funcPtr */
+    { "nativeTransform",
+            "(Landroid/graphics/Matrix;)V",
+            (void*)android_view_MotionEvent_nativeTransform },
+};
+
 #define FIND_CLASS(var, className) \
         var = env->FindClass(className); \
         LOG_FATAL_IF(! var, "Unable to find class " className); \
@@ -258,6 +353,10 @@
         LOG_FATAL_IF(! var, "Unable to find field " fieldName);
 
 int register_android_view_MotionEvent(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, "android/view/MotionEvent",
+            gMotionEventMethods, NELEM(gMotionEventMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
     FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent");
 
     GET_STATIC_METHOD_ID(gMotionEventClassInfo.obtain, gMotionEventClassInfo.clazz,
diff --git a/core/jni/android_view_ViewRoot.cpp b/core/jni/android_view_ViewRoot.cpp
index 5173bb8..2988ae8 100644
--- a/core/jni/android_view_ViewRoot.cpp
+++ b/core/jni/android_view_ViewRoot.cpp
@@ -76,10 +76,6 @@
     canvas->restore();
 }
 
-static void android_view_ViewRoot_abandonGlCaches(JNIEnv* env, jobject) {
-    SkGLCanvas::AbandonAllTextures();
-}
-
 
 // ----------------------------------------------------------------------------
 
@@ -87,9 +83,7 @@
 
 static JNINativeMethod gMethods[] = {
     {   "nativeShowFPS", "(Landroid/graphics/Canvas;I)V",
-                                        (void*)android_view_ViewRoot_showFPS },
-    {   "nativeAbandonGlCaches", "()V", 
-                                (void*)android_view_ViewRoot_abandonGlCaches }
+                                        (void*)android_view_ViewRoot_showFPS }
 };
 
 int register_android_view_ViewRoot(JNIEnv* env) {
diff --git a/core/res/Android.mk b/core/res/Android.mk
index 7d11148..22f4fdc 100644
--- a/core/res/Android.mk
+++ b/core/res/Android.mk
@@ -33,8 +33,16 @@
 # PRODUCT-agnostic resource data like IDs and type definitions.
 LOCAL_EXPORT_PACKAGE_RESOURCES := true
 
+# Include resources generated by system RenderScript files.
+framework_GENERATED_SOURCE_DIR := $(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)/src
+framework_RenderScript_STAMP_FILE := $(framework_GENERATED_SOURCE_DIR)/RenderScript.stamp
+#LOCAL_RESOURCE_DIR := $(framework_GENERATED_SOURCE_DIR)/renderscript/res $(LOCAL_PATH)/res
+
 include $(BUILD_PACKAGE)
 
+# Make sure the system .rs files get compiled before building the package-export.apk.
+# $(resource_export_package): $(framework_RenderScript_STAMP_FILE)
+
 # define a global intermediate target that other module may depend on.
 .PHONY: framework-res-package-target
 framework-res-package-target: $(LOCAL_BUILT_MODULE)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 07a48e7..43f175f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -85,10 +85,13 @@
     <protected-broadcast android:name="android.hardware.action.USB_CONNECTED" />
     <protected-broadcast android:name="android.hardware.action.USB_DISCONNECTED" />
     <protected-broadcast android:name="android.hardware.action.USB_STATE" />
+    <protected-broadcast android:name="android.hardware.action.USB_CAMERA_ATTACHED" />
+    <protected-broadcast android:name="android.hardware.action.USB_CAMERA_DETACHED" />
 
     <protected-broadcast android:name="android.nfc.action.TAG_DISCOVERED" />
     <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
+    <protected-broadcast android:name="android.intent.action.CLEAR_DNS_CACHE" />
 
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
@@ -362,6 +365,13 @@
         android:description="@string/permdesc_accountManagerService"
         android:label="@string/permlab_accountManagerService" />
 
+    <!-- Allows an internal user to use privaledged ConnectivityManager
+    APIs.
+        @hide -->
+    <permission android:name="android.permission.CONNECTIVITY_INTERNAL"
+        android:permissionGroup="android.permission-group.NETWORK"
+        android:protectionLevel="signatureOrSystem" />
+
     <!-- ================================== -->
     <!-- Permissions for accessing accounts -->
     <!-- ================================== -->
@@ -523,6 +533,13 @@
         android:description="@string/permdesc_sdcardWrite"
         android:protectionLevel="dangerous" />
 
+    <!-- Allows an application to write to internal media storage -->
+    <permission android:name="android.permission.WRITE_MEDIA_STORAGE"
+        android:permissionGroup="android.permission-group.STORAGE"
+        android:label="@string/permlab_mediaStorageWrite"
+        android:description="@string/permdesc_mediaStorageWrite"
+        android:protectionLevel="signatureOrSystem" />
+
     <!-- ============================================ -->
     <!-- Permissions for low-level system interaction -->
     <!-- ============================================ -->
@@ -1292,14 +1309,14 @@
                 android:excludeFromRecents="true">
         </activity>
         <activity android:name="com.android.internal.app.PlatLogoActivity"
-                android:theme="@style/Theme.NoTitleBar.Fullscreen">
+                android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen">
         </activity>
         <activity android:name="com.android.internal.app.DisableCarModeActivity"
                 android:theme="@style/Theme.NoDisplay"
                 android:excludeFromRecents="true">
         </activity>
         <activity android:name="com.android.internal.app.RingtonePickerActivity"
-                android:theme="@style/Theme.Dialog.Alert"
+                android:theme="@style/Theme.Holo.Dialog.Alert"
                 android:excludeFromRecents="true"
                 android:multiprocess="true">
             <intent-filter>
@@ -1325,6 +1342,10 @@
                 <action android:name="android.intent.action.ACTION_REQUEST_SHUTDOWN" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.REBOOT" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
         </activity>
         <activity android:name="com.android.internal.app.NetInitiatedActivity"
                 android:theme="@style/Theme.Dialog.Alert"
diff --git a/core/res/assets/webkit/hyph_en_US.dic b/core/res/assets/webkit/hyph_en_US.dic
new file mode 100644
index 0000000..d91204b
--- /dev/null
+++ b/core/res/assets/webkit/hyph_en_US.dic
@@ -0,0 +1,9784 @@
+ISO8859-1
+LEFTHYPHENMIN 2
+RIGHTHYPHENMIN 3
+.a2ch4
+.ad4der
+.a2d
+.ad1d4
+.a2f1t
+.a2f
+.a4l3t
+.am5at
+.4a1ma
+.an5c
+.a2n
+.2ang4
+.an1i5m
+.an1t4
+.an3te
+.anti5s
+.ant2i
+.a4r5s2
+.2a2r
+.ar4t2ie4
+.ar1ti
+.ar4ty
+.as3c
+.as1p
+.a2s1s
+.aster5
+.a2tom5
+.a1to
+.au1d
+.av4i
+.awn4
+.ba4g
+.ba5na
+.ba2n
+.bas4e
+.ber4
+.be5r1a
+.be3s1m
+.4bes4
+.b4e5s2to
+.bri2
+.but4ti
+.bu4t3t2
+.cam4pe
+.1ca
+.ca4m1p
+.can5c
+.ca2n
+.capa5b
+.ca1pa
+.car5ol
+.c2a2r
+.ca4t
+.ce4la
+.2ch4
+.chill5i
+.ch4il2
+.chil1l
+.1ci2
+.cit5r
+.2c1it
+.co3e2
+.1co
+.co4r
+.cor5n1er
+.corn2e
+.de4moi2
+.d4em
+.de1mo
+.de3o
+.de3r1a
+.de3r1i
+.de1s4c
+.des2
+.dic1t2io5
+.3di2c1t
+.do4t
+.1do
+.du4c
+.1du
+.du4m1b5
+.earth5
+.ear2t
+.e2a2r
+.eas3i
+.2e1b4
+.eer4
+.eg2
+.e2l5d
+.el3em
+.enam3
+.e1na
+.en3g
+.e2n3s2
+.eq5ui5t
+.e1q
+.equ2
+.eq2ui2
+.er4ri
+.er1r4
+.es3
+.4eu3
+.eye5
+.fes3
+.for5mer
+.1fo
+.fo2r
+.for1m
+.for2me
+.1ga2
+.ge2
+.gen3t4
+.1gen
+.ge5o2g
+.1geo
+.1g2i5a
+.gi4b
+.go4r
+.1go
+.hand5i
+.ha2n
+.h4and
+.ha4n5k2
+.he2
+.hero5i2
+.h2ero
+.h1es3
+.he4t3
+.hi3b
+.hi3er
+.h2ie4
+.hon5ey
+.ho2n
+.hon3o
+.hov5
+.id4l
+.2id
+.idol3
+.i1do
+.im3m
+.im5p1i2n
+.i4m1p
+.im2pi
+.in1
+.in3ci
+.2ine2
+.4i4n2k2
+.2i2n3s2
+.ir5r4
+.4ir
+.is4i
+.ju3r
+.la4cy
+.la4m
+.lat5er
+.l4ath5
+.le2
+.leg5e
+.len4
+.lep5
+.lev1
+.l2i4g
+.li1g5a
+.li2n
+.l2i3o
+.l1i4t
+.ma1g5a5
+.1ma
+.mal5o
+.ma1n5a
+.ma2n
+.mar5ti
+.m2a2r
+.me2
+.mer3c
+.me5ter
+.me1te
+.m2is1
+.mis4t5i
+.mon3e
+.1mo
+.mo2n
+.mo3ro
+.mo2r
+.mu5ta
+.1mu
+.mu2ta5b
+.ni4c
+.od2
+.od1d5
+.of5te
+.o2ft
+.or5a1to
+.o1ra
+.or3c
+.or1d
+.or3t
+.os3
+.os4tl
+.4oth3
+.out3
+.ou2
+.ped5al
+.2p2ed
+.p2e2d2a
+.pe5te
+.pe2t
+.pe5tit
+.p2i4e4
+.pio5n4
+.3p2i1o
+.pi2t
+.pre3m
+.pr2
+.ra4c
+.ran4t
+.ra2n
+.ratio5n1a
+.ratio2n4
+.ra1t2io
+.ree2
+.re5mit
+.res2
+.re5stat
+.res2t
+.res1ta
+.r2i4g
+.ri2t5u
+.ro4q
+.ros5t
+.row5d
+.ru4d
+.3s4c2i3e4
+.s1ci
+.5se2l2f5
+.sel1l5
+.se2n
+.se5r2ie4
+.ser1i
+.s2h2
+.si2
+.s3ing4
+.2s1in
+.st4
+.sta5b2l2
+.s1ta
+.s2tab
+.s4y2
+.1ta4
+.te4
+.3ten5a2n
+.te1na
+.th2
+.ti2
+.til4
+.ti1m5o5
+.1tim
+.ting4
+.2t1in
+.t4i4n5k2
+.to1n4a
+.1to
+.to2n
+.to4p
+.top5i
+.to2u5s
+.tou2
+.trib5ut
+.tr4ib
+.u1n1a
+.un3ce
+.under5
+.un1de
+.u2n1e
+.u4n5k2
+.un5o
+.un3u4
+.up3
+.ure3
+.us5a2
+.2us
+.ven4de
+.ve5r1a
+.wil5i
+.wi2
+.wil2
+.ye4
+4ab.
+a5bal
+a5ba2n
+abe2
+ab5erd
+ab2i5a
+ab5i2t5ab
+abi2t
+abi1ta
+ab5lat
+ab2l2
+ab5o5l1iz
+abol2i
+4abr
+ab5rog
+ab3ul
+a4c2a2r
+a1ca
+ac5ard
+ac5aro
+a5ceou2
+ac1er
+a5che4t
+a2ch
+ache2
+4a2ci
+a3c2ie4
+a2c1in
+a3c2io
+ac5rob
+act5if2
+a2c1t
+ac3ul
+ac4um
+a2d
+ad4d1in
+ad1d4
+ad5er.
+2adi
+a3d4i3a
+ad3i1ca
+adi4er
+ad2ie4
+a3d2io
+a3dit
+a5di1u
+ad4le
+ad3ow
+a1do
+ad5ra2n
+a1dr
+ad4su
+a2d1s2
+4a1du
+a3du2c
+ad5um
+ae4r
+aer2i4e4
+aer1i
+a2f
+a4f1f4
+a4gab
+a1ga
+aga4n
+ag5el1l
+a1ge4o
+4ag4eu
+ag1i
+4ag4l2
+ag1n
+a2go
+3a3g4o4g
+ag3o3ni
+ago2n2
+a5guer
+a2gue
+ag5ul
+a4gy
+a3ha
+a3he
+a4h4l4
+a3ho
+ai2
+a5i1a
+a3ic.
+ai5ly
+a4i4n
+ain5in
+a2ini
+a2i1n5o
+ait5en
+a2ite
+a1j
+ak1en
+al5ab
+al3a2d
+a4l2a2r
+4aldi4
+a2ld
+2ale
+al3end
+a4lent2i
+a1len1t
+a5le5o
+al1i
+al4ia.
+al2i1a
+al2i4e4
+al5lev
+al1l
+al2le
+4allic
+all2i
+4a2lm
+a5log.
+a4ly.
+a1ly
+4a2lys4
+5a5lys1t
+5alyt
+3alyz
+4a1ma
+a2m5ab
+am3ag
+ama5ra
+am2a2r
+am5asc
+a4ma3tis
+a4m5a1to
+am5er1a
+am3ic
+am5if
+am5i1ly
+am1in
+am2i4no
+a2mo
+a5mo2n
+amor5i
+amo2r
+amp5en
+a4m1p
+a2n
+an3age
+a1na
+3ana1ly
+a3n2a2r
+an3ar3c
+anar4i
+a3nati
+an2at
+4and
+ande4s2
+an1de
+an3dis1
+an1dl
+an4dow
+an1do
+a5nee
+a3nen
+an5e2st.
+a1nes
+a2nest
+a3n4eu
+2ang
+ang5ie4
+an1gl2
+a4n1ic
+a3nies
+an2ie4
+an3i3f
+an4ime
+an1im
+a5nim1i
+a5n2ine
+an1in
+an3i4o
+a3n2ip
+an3is2h
+an3it
+a3ni1u
+an4kli
+a4nk2
+an1k1l
+5anniz
+a4n1n2
+ano4
+an5ot
+an4oth5
+an2sa2
+a2n1s2
+an4s1co
+ans4c
+an4s1n4
+an2sp
+ans3po
+an4st
+an4su2r
+an1su
+anta2l4
+an1t
+an1ta
+an4t2ie4
+ant2i
+4an1to
+an2tr
+an4tw4
+an3u1a
+an3ul
+a5nur
+4ao
+ap2a2r4
+a1pa
+ap5at
+ap5er3o
+a3ph4er
+4aphi
+a4pilla
+apil1l
+ap5ill2a2r
+ap3i2n
+ap3i1ta
+a3pi2tu
+a2p2l2
+apo4c5
+ap5o1la
+apor5i
+a1p4or
+apos3t
+a1pos
+aps5e4s
+a2p1s2
+ap2se
+a3pu
+aque5
+aqu2
+2a2r
+ar3a2c1t
+a5rade
+ara2d
+ar5adis1
+ar2adi
+ar3al
+a5rame1te
+aram3et
+ar2an4g
+ara2n
+ara3p
+ar4at
+a5ra1t2io
+ar5a1t2iv
+a5rau
+ar5av4
+araw4
+arbal4
+ar1b
+ar4cha2n
+ar1c
+ar3cha
+ar2ch
+ar5d2ine
+ard2i
+ard1in4
+ar4dr
+ar5eas
+a3ree
+ar3en1t
+a5r2e2ss
+ar4fi
+ar1f
+ar4f4l2
+ar1i
+ar5i2al
+ar2i3a
+ar3i2a2n
+a3ri5et
+ar2ie4
+ar4im
+ar5in2at
+ar2i1na
+ar3i1o
+ar2iz
+ar2mi
+ar1m
+ar5o5d
+a5roni
+aro2n
+a3roo2
+ar2p
+ar3q
+arre4
+ar1r4
+ar4sa2
+a4rs2
+ar2s2h
+4as.
+a2s4ab
+asa2
+as3an1t
+asa2n
+ashi4
+as2h
+a5sia.
+as2i1a
+a3si1b
+a3sic
+5a5si4t
+ask3i
+ask2
+as4l2
+a4soc
+a1so
+as5ph
+as4s2h
+a2ss
+as3ten
+as1t4r
+asu1r5a
+a1su
+asu2r
+a2ta
+at3ab2l2
+a2tab
+at5ac
+at3alo
+ata2l
+at5ap
+ate5c
+at5e2ch
+at3e1go
+ateg4
+at3en.
+at3er1a
+ater5n
+a5ter1na
+at3est
+at5ev
+4ath
+ath5em
+ath2e
+a5the2n
+at4ho
+ath5om
+4ati.
+a5t2i1a
+a2t5i5b
+at1ic
+at3if2
+ation5a2r
+a1t2io
+atio2n
+atio1n1a
+at3i1tu
+a4tog
+a1to
+a2tom
+at5om2iz
+a4top
+a4tos2
+a1tr
+at5rop
+at4sk2
+a4t1s2
+at4tag
+a4t3t2
+at1ta
+at5te
+at4th
+a2tu
+at5u1a
+a4t5ue
+at3ul
+at3u1ra
+a2ty
+au4b
+augh3
+au3gu
+au4l2
+aun5d
+au3r
+au5si1b
+a2us
+a4ut5en
+au1th
+a2va
+av3ag4
+a5va2n
+av4e4no
+av3er1a
+av5ern
+av5ery
+av1i
+avi4er
+av2ie4
+av3ig
+av5oc
+a1vor
+3away
+aw3i2
+aw4ly
+aws4
+ax4i5c
+ax3i
+ax4id
+ay5al
+aye4
+ays4
+azi4er
+a2z1i
+az2ie4
+az2z5i
+a4z1z2
+5ba.
+bad5ger
+ba2d
+ba4ge
+bal1a
+ban5dag
+ba2n
+b4and
+ban1d2a
+ban4e
+ban3i
+barbi5
+b2a2r
+bar1b
+bar2i4a
+bar1i
+bas4si
+ba2ss
+1bat
+ba4z
+2b1b
+b2be
+b3ber
+bbi4na
+4b1d
+4be.
+beak4
+bea2t3
+4be2d
+b2e3d2a
+be3de
+b4e3di
+be3gi
+be5gu
+1bel
+be1l2i
+be3lo
+4be5m
+be5n2ig
+be5nu
+4bes4
+be3sp
+b2e5st4r
+3bet
+be1t5iz
+be5tr
+be3tw4
+be3w
+be5y1o4
+2bf
+4b3h
+bi2b
+b2i4d
+3b2ie4
+bi5en
+bi4er
+2b3if
+1bil
+bi3l2iz
+bil1i
+bin2a5r4
+bi1na
+b4in4d
+bi5net
+b2ine
+bi3o2gr
+b2io
+bi5ou2
+bi2t
+3b2i3t2io
+bi1ti
+bi3tr
+3bit5u1a
+bi1tu
+b5i4tz
+b1j
+bk4
+b2l2
+bl4ath5
+b4le.
+blen4
+5ble1sp
+bles2
+b3lis
+b4lo
+blun4t
+4b1m
+4b3n
+bne5g
+3bod
+bod3i
+bo4e
+bol3ic
+bol2i
+bom4bi
+bo4m1b
+bo1n4a
+bo2n
+bon5at
+3boo2
+5bor.
+4b1o1ra
+bor5d
+5bore
+5bori
+5bos4
+b5o1ta
+b4oth5
+bo4to
+boun2d3
+bou2
+4bp
+4brit
+br4oth3
+2b5s2
+bsor4
+b1so
+2bt
+b2t4l
+b4to
+b3tr
+buf4fer1
+bu4f1f
+bu4ga
+bu3l2i
+bu1mi4
+bu4n
+bunt4i
+bun1t
+bu3re
+bus5ie4
+b2us
+buss4e
+bu2ss
+5bust
+4bu1ta
+3bu1t2io
+b4u1t2i
+b5u1to
+b1v
+4b5w
+5by.
+bys4
+1ca
+cab3in
+ca1b2l2
+ca2ch4
+ca5den
+ca2d
+4cag4
+2c5ah
+ca3lat
+cal4la
+cal1l
+cal2l5in4
+call2i
+4calo
+c4an5d
+ca2n
+can4e
+ca4n4ic
+can5is
+can3iz
+can4ty
+can1t
+cany4
+ca5per
+car5om
+c2a2r
+cast5er
+cas5t2ig
+cast2i
+4cas4y
+c4a4th
+4ca1t2iv
+cav5al
+ca2va
+c3c
+ccha5
+c2ch
+c3c2i4a
+c1ci
+ccom1pa5
+c1co
+cco4m1p
+cco2n4
+ccou3t
+ccou2
+2ce.
+4ced.
+4ce1den
+3cei2
+5cel.
+3cel1l
+1cen
+3cenc
+2cen4e
+4ceni
+3cen1t
+3cep
+ce5ram
+cer1a
+4ce1s4a2
+3ces1si
+c2e2ss
+ces5si5b
+ces5t
+cet4
+c5e4ta
+cew4
+2ch
+4ch.
+4ch3ab
+5cha4n1ic
+cha2n
+ch5a5nis
+che2
+cheap3
+4ch4ed
+ch5e5lo
+3chemi
+ch5ene
+che2n
+ch3er.
+ch3e4r1s2
+4ch1in
+5chi2ne.
+ch2ine
+ch5i5n2e2ss
+chi1nes
+5ch2ini
+5ch2io
+3chit
+chi2z
+3cho2
+ch4ti
+1ci
+3c2i1a
+ci2a5b
+ci2a5r
+ci5c
+4cier
+c2ie4
+5c4i2f3ic.
+ci1fi
+4c4i5i4
+ci4la
+3cil1i
+2cim
+2cin
+c4i1na
+3cin2at
+cin3em
+c2ine
+c1ing
+c5ing.
+5c2i1no
+cio2n4
+c2io
+4cipe4
+c2ip
+ci3ph
+4cip4ic
+cip3i
+4cis1ta
+4cis1t2i
+2c1it
+ci1t3iz
+ci1ti
+5ciz
+ck1
+ck3i
+1c4l4
+4cl2a2r
+c5la5ra1t2io
+clar4at
+5clare
+cle4m
+4clic
+clim4
+c1ly4
+c5n
+1co
+co5ag
+c4oa
+coe2
+2cog
+co4gr
+coi4
+co3inc
+col5i
+5colo
+col3o4r
+com5er
+co2me
+co1n4a
+co2n
+c4one
+con3g
+con5t
+co3pa
+cop3ic
+co4p2l2
+4cor1b
+coro3n
+cos4e
+cov1
+cove4
+cow5a
+co2z5e
+co5z1i
+c1q
+cras5t
+cr2as
+5crat.
+5crat1ic
+cre3a2t
+5c2r2ed
+4c3re1ta
+cre4v2
+cri2
+cri5f
+c4rin
+cr2is4
+5cri1ti
+cro4p2l2
+crop5o
+cros4e
+cru4d
+4c3s2
+2c1t
+c2ta4b
+c1ta
+ct5ang
+cta2n
+c5tan1t
+c2te
+c3ter
+c4t4ic1u
+ctim3i
+c1tim
+ctu4r
+c1tu
+c4tw4
+cud5
+c4uf
+c4ui2
+cu5i1ty
+5cul2i
+cul4tis4
+cul1ti
+cu4lt
+3c4ul1tu2
+cu2ma
+c3ume
+cu4mi
+3cun
+cu3pi
+cu5py
+cu2r5a4b
+cu1ra
+cu5r2i3a
+1c2us
+cus1s4i
+cu2ss
+3c4ut
+cu4t2ie4
+c4u1t2i
+4c5u1t2iv
+4cutr
+1cy
+c2ze4
+1d2a
+5da.
+2d3a4b
+da2ch4
+4da2f
+2dag
+da2m2
+d2an3g
+da2n
+dard5
+d2a2r
+dark5
+4dary
+3dat
+4da1t2iv
+4da1to
+5dav4
+dav5e
+5day
+d1b
+d5c
+d1d4
+2de.
+dea2f5
+de4b5i2t
+d2e1b
+de4bo2n
+deca2n4
+de1ca
+de4cil
+de1c2i
+de5com
+de1co
+2d1ed
+4dee.
+de5if
+dei2
+del2i4e4
+del2i
+de4l5i5q
+de5lo
+d4em
+5dem.
+3demic
+dem5ic.
+de5mil
+de4mo2n3s2
+de1mo
+demo2n
+demo2r5
+1den
+de4n2a2r
+de1na
+d4e3no
+denti5f2
+den1t
+dent2i
+de3nu
+de1p
+de3pa
+depi4
+de2pu
+d3e1q
+d4er1h4
+5der3m4
+d5ern5iz
+de4r5s2
+des2
+d2es.
+de1s2c
+de2s5o
+des3t2i
+d2e3st4r
+de4su
+de1t
+de2to
+de1v
+de2v3i4l
+de1vi
+4dey
+4d1f
+d4ga
+d3ge4t
+dg1i
+d2gy
+d1h2
+5di.
+1d4i3a
+dia5b
+d4i4cam
+di1ca
+d4ice
+3di2c1t
+3d2id
+5di3en
+d2ie4
+d1if
+di3ge
+d2ig
+di4la1to
+di1la
+d1in
+1di1na
+3di2ne.
+d2ine
+5d2ini
+di5niz
+1d2io
+dio5g
+di4p2l2
+d2ip
+d4ir2
+di1re
+dir1t5i
+dis1
+5disi
+d4is3t
+d2i1ti
+1d2i1v
+d1j
+d5k2
+4d5la
+3dle.
+3dled
+3dles.
+dles2
+4d3l2e2ss
+2d3lo
+4d5lu
+2d1ly
+d1m
+4d1n4
+1do
+3do.
+do5de
+5doe
+2d5of
+d4og
+do4la
+dol2i4
+do5lo4r
+dom5iz
+do3n2at
+do2n
+do1n1a
+doni4
+doo3d
+doo2
+do4p4p
+d4or
+3dos
+4d5out
+dou2
+do4v
+3dox
+d1p
+1dr
+drag5o2n2
+dra2go
+4dr2ai2
+dre4
+dre2a5r
+5dren
+dr4i4b
+dril4
+dro4p
+4drow
+5drupli
+dru3p2l2
+4dry
+2d1s2
+ds4p
+d4sw2
+d4s4y
+d2th
+1du
+d1u1a
+du2c
+d1u3ca
+duc5er
+4duct.
+du2c1t
+4duc4t1s2
+du5el
+du4g
+d3ul4e
+dum4be
+du4m1b
+du4n
+4dup
+du4pe
+d1v
+d1w
+d2y
+5dyn
+dy4s2e
+dys5p
+e1a4b
+e3a2c1t
+ea2d1
+ead5ie4
+e2adi
+ea4ge
+ea5ger
+ea4l
+eal5er
+e2ale
+eal3ou2
+eam3er
+e5and
+ea2n
+ear3a
+e2a2r
+ear4c
+ear5es
+ear4ic
+ear1i
+ear4il
+ear5k
+ear2t
+eart3e
+ea5sp
+e3a2ss
+east3
+ea2t
+eat5en
+eath3i
+e4ath
+e5at3if2
+e4a3tu
+ea2v
+eav3en
+eav5i
+eav5o
+2e1b
+e4bel.
+e1bel
+e4be2l1s2
+e4ben
+e4bi2t
+e3br
+e4ca2d
+e1ca
+ecan5c
+eca2n
+ec1ca5
+ec3c
+e1ce
+ec5es1sa2
+ec2e2ss
+e1c2i
+e4cib
+ec5ificat
+eci1fi
+ecifi1ca
+ec5i3f2ie4
+ec5i1fy
+e2c3im
+e2c1i4t
+e5c2ite
+e4clam
+e1c4l4
+e4cl2us
+e2col
+e1co
+e4com1m
+e4compe
+eco4m1p
+e4con1c
+eco2n
+e2cor
+ec3o1ra
+eco5ro
+e1cr
+e4crem
+ec4ta2n
+e2c1t
+ec1ta
+ec4te
+e1cu
+e4cul
+ec3u1la
+2e2d2a
+4ed3d4
+e4d1er
+ede4s2
+4edi
+e3d4i3a
+ed3ib
+ed3i1ca
+ed3im
+ed1it
+edi5z
+4e1do
+e4dol
+edo2n2
+e4dri
+e1dr
+e4dul
+e1du
+ed5u1l4o
+ee2c
+e4ed3i
+ee2f
+eel3i
+ee4ly
+ee2m
+ee4na
+ee4p1
+ee2s4
+eest4
+ee4ty
+e5ex
+e1f
+e4f3ere
+efer1
+1e4f1f
+e4fic
+e1fi
+5ef2i1c4i
+efil4
+e3f2i2ne
+e2fin
+ef5i5n2ite
+ef2ini
+efin2it
+3efit
+efor5es
+e1fo
+efo2r
+e4fu4se.
+e3fu
+ef2us
+4egal
+e1ga
+eger4
+eg5ib
+eg4ic
+eg5ing
+e5git5
+eg5n
+e4go.
+e1go
+e4gos
+eg1ul
+e5gur
+5e1gy
+e1h4
+eher4
+ei2
+e5ic
+e2i5d
+e2ig2
+ei5g4l2
+e3i4m1b
+e3in3f
+e1ing
+e5inst
+e2i2n1s2
+eir4d
+e4ir
+e2it3e
+e2i3th
+e5i1ty
+e1j
+e4jud
+ej5udi
+eki4n
+ek1i
+ek4la
+ek1l
+e1la
+e4la.
+e4lac
+e3l4an4d
+ela2n
+e4l5a1t2iv
+e4law
+elax1a4
+e3le2a
+el5ebra
+el2e1b
+ele3br
+5elec
+e4led
+el3e1ga
+e5len
+e4l1er
+e1les2
+e2l2f
+el2i
+e3libe4
+e4l5ic.
+el3i1ca
+e3lier
+el2ie4
+el5i3gib
+el2ig
+el4igi
+e5lim
+e4l3ing
+e3l2io
+e2lis
+el5is2h
+e3l2iv3
+4ella
+el1l
+el4lab
+ell4o4
+e5loc
+el5og
+el3op.
+el2s2h
+e2l1s2
+el4ta
+e4lt
+e5lud
+el5ug
+e4mac
+e1ma
+e4mag
+e5ma2n
+em5a1na
+e4m5b
+e1me
+e2mel
+e4met
+em3i1ca
+em2i4e4
+em5igra
+em2ig4
+emi1gr
+em1in2
+em5ine
+em3i3ni
+e4m2is
+em5is2h
+e5m4i2s1s
+em3iz
+5emniz
+e4m1n
+emo4g
+e1mo
+emo3n2i5o
+emo2n
+em3pi
+e4m1p
+e4mul
+e1mu
+em5u1la
+emu3n2
+e3my
+en5a2mo
+e1na
+e4nan1t
+en2a2n
+ench4er
+en2ch
+enche2
+en3dic
+e5nea
+e5nee
+en3em
+en5ero
+en1er
+en5e1si
+e1nes
+e2n5est
+en3etr
+e3ne4w
+en5i4c3s2
+e5n2ie4
+e5nil
+e3n2i4o
+en3is2h
+en3it
+e5ni1u
+5eniz
+4e4n1n2
+4eno
+e4no4g
+e4nos
+en3ov
+en4sw2
+e2n1s2
+ent5age
+en1t
+en1ta
+4enth1es
+enth2e
+en3u1a
+en5uf
+e3ny.
+4e4n3z
+e5of
+eo2g
+e4oi4
+e3ol
+eop3a2r
+eo2pa
+e1or
+eo3re
+eo5rol
+eos4
+e4ot
+eo4to
+e5out
+eou2
+e5ow
+e2pa
+e3p4ai2
+ep5anc
+epa2n
+e5pel
+e3pen1t
+ep5e5t2i1t2io
+epe2t
+epeti1ti
+ephe4
+e4pli
+e1p2l2
+e1po
+e4prec
+epr2
+ep5re1ca
+e4p2r2ed
+ep3re1h4
+e3pro
+e4prob
+ep4s4h
+e2p1s2
+ep5ti5b
+e2p1t
+e4pu2t
+ep5u1ta
+e1q
+equi3l
+equ2
+eq2ui2
+e4q3ui3s
+er1a
+e2ra4b
+4er4and
+era2n
+er3a2r
+4er4ati.
+2er1b
+er4b2l2
+er3ch
+er1c
+er4che2
+2e2re.
+e3re1a4l
+ere5co
+ere3in
+erei2
+er5el.
+er3e1mo
+er5e1na
+er5ence
+4erene
+er3en1t
+ere4q
+er5e2ss
+er3es2t
+eret4
+er1h4
+er1i
+e1r2i3a4
+5erick1
+e3rien
+er2ie4
+eri4er
+er3in4e
+e1r2i1o
+4erit
+er4i1u
+er2i4v
+e4ri1va
+er3m4
+er4nis4
+4er3n2it
+5erniz
+er3no4
+2ero
+er5ob
+e5r2oc
+ero4r
+er1ou2
+e4r1s2
+er3set
+er2se
+ert3er
+4er2tl
+er3tw4
+4eru
+eru4t
+5erwau
+er1w
+e1s4a2
+e4sa2ge.
+e4sages
+es2c
+e2s1ca
+es5ca2n
+e3scr
+es5cu
+e1s2e
+e2sec
+es5e1cr
+e4s5enc
+e4sert.
+e4ser4t1s2
+e4ser1va
+4es2h
+e3sha
+esh5e2n
+e1si
+e2sic
+e2s2id
+es5i1den
+e4s5ig1n4a
+es2ig
+e2s5im
+e2s4i4n
+esis4te
+e1sis
+e5si4u
+e5skin
+esk2
+esk1i
+es4mi
+e2s1m
+e2sol
+e1so
+es3olu
+e2so2n
+es5o1n1a4
+e1sp
+e2s3per
+es5pi1ra
+esp4ir
+es4pre
+espr2
+2e2ss
+es4si4b
+es1si
+esta2n4
+es1ta
+es3t2ig
+est2i
+es5tim
+4es2to
+e3sto2n
+2est4r
+e5stro
+estruc5
+e2su2r
+e1su
+es5ur1r4
+es4w2
+e2ta4b
+e1ta
+e3ten4d
+e3teo
+ethod3
+et1ic
+e5tide
+et2id
+e2t1in4
+et2i4no
+e5t4ir
+e5t2i1t2io
+eti1ti
+et5i1t2iv
+4e2t1n2
+et5o1n1a
+e1to
+eto2n
+e3tra
+e3tre
+et3ric
+et5rif
+et3rog
+et5ros
+et3u1a
+e1tu
+et5ym
+e1ty
+e4t5z
+4eu
+e5un
+e3up
+eu3ro
+e2us4
+eute4
+euti5l
+e4u1t2i
+eu5tr
+eva2p5
+e1va
+e2vas
+ev5ast
+e5vea
+ev3el1l
+eve4l3o
+e5veng
+even4i
+ev1er
+e5v2er1b
+e1vi
+ev3id
+e2vi4l
+e4v1in
+e3v2i4v
+e5voc
+e5vu
+e1wa
+e4wag
+e5wee
+e3wh
+ewil5
+ewi2
+ew3in4g
+e3wit
+1ex3p
+5ey1c
+5eye.
+eys4
+1fa
+fa3b2l2
+f4ab3r
+fa4ce
+4fag
+fa4i4n4
+fai2
+fal2l5e
+fal1l
+4f4a4ma
+fam5is
+5f2a2r
+far5th
+fa3ta
+fa3th2e
+f4ath
+4fa1to
+fau4lt5
+fau4l2
+4f5b
+4fd
+4fe.
+feas4
+fe4ath3
+fea2t
+f2e4b
+4fe1ca
+5fe2c1t
+2fed
+fe3l2i
+fe4mo
+fen2d
+fen1d5e
+fer1
+5fer1r4
+fev4
+4f1f
+f4fes
+f4f2ie4
+f1fi
+f5f2in.
+f2fin
+f2f5is
+f4f2ly5
+ff4l2
+f2fy
+4fh
+1fi
+f2i3a
+2f3ic.
+4f3ical
+fi1ca
+f3ica2n
+4ficate
+f3i1cen
+fi3cer
+f2i1c4i
+5fi3c2i1a
+5fic2ie4
+4fi4c3s2
+fi3cu
+fi5del
+f2id
+fight5
+f2ig
+fil5i
+fil2l5in4
+fil1l
+fill2i
+4fi1ly
+2fin
+5fi1na
+f4in2d5
+f2i2ne
+f1in3g
+f2i4n4n2
+fis4t2i
+f4l2
+f5l2e2ss
+fles2
+flin4
+flo3re
+f2ly5
+4fm
+4fn
+1fo
+5fo2n
+fon4de
+f2ond
+fon4t
+fo2r
+fo5rat
+fo1ra
+for5ay
+fore5t
+for4i
+for1t5a
+fos5
+4f5p
+fra4t
+f5rea
+fres5c
+fri2
+fril4
+frol5
+2f3s
+2ft
+f4to
+f2ty
+3fu
+fu5el
+4fug
+fu4min
+fu1mi
+fu5ne
+fu3ri
+fusi4
+f2us
+fu2s4s
+4fu1ta
+1fy
+1ga
+ga2f4
+5gal.
+3gal1i
+ga3lo
+2gam
+ga5met
+g5a2mo
+gan5is
+ga2n
+ga3niz
+gani5za1
+4gano4
+gar5n4
+g2a2r
+ga2ss4
+g4ath3
+4ga1t2iv
+4gaz
+g3b
+gd4
+2ge.
+2ged
+geez4
+gel4in
+gel2i
+ge5lis
+ge5l1iz
+4ge1ly
+1gen
+ge4n2at
+ge1na
+g5e5niz
+4g4eno
+4geny
+1geo
+ge3om
+g4ery
+5ge1si
+geth5
+4ge1to
+ge4ty
+ge4v
+4g1g2
+g2ge
+g3ger
+gglu5
+ggl2
+g1go4
+gh3in
+gh5out
+ghou2
+gh4to
+5gi.
+1g2i4a
+gi2a5r
+g1ic
+5gi3c2i1a
+g2i1ci
+g4i1co
+gien5
+g2ie4
+5gies.
+gil4
+g3i1men
+3g4in.
+g4in5ge
+5g4i2n1s2
+5g2io
+3g4ir
+gir4l
+g3is1l2
+gi4u
+5g2iv
+3giz
+gl2
+gla4
+gl2ad5i
+gla2d
+5glas
+1gle
+gli4b
+g3l2ig
+3glo
+glo3r
+g1m
+g4my
+g1n4a
+g4na.
+gne4t4t2
+g1ni
+g2n1in
+g4n2i4o
+g1no
+g4no4n
+1go
+3go.
+gob5
+5goe
+3g4o4g
+go3is
+goi2
+go2n2
+4g3o3n1a
+gon5do5
+g2ond
+go3ni
+5goo2
+go5riz
+gor5ou2
+5gos.
+gov1
+g3p
+1gr
+4gra1d2a
+gra2d
+g4r2ai2
+gra2n2
+5gra4ph.
+g5ra3ph4er
+5graph1ic
+gr4aphi
+4g3ra1phy
+4gray
+gre4n
+4gress.
+gr2e2ss
+4grit
+g4ro
+gruf4
+gs2
+g5ste
+gth3
+gu4a
+3guar2d
+gu2a2r
+2gue
+5gui5t
+g2ui2
+3gun
+3g2us
+4gu4t
+g3w
+1gy
+2g5y3n
+gy5ra
+h3ab4l2
+ha2ch4
+hae4m
+hae4t
+h5agu
+ha3la
+hala3m
+ha4m
+han4ci
+ha2n
+han4cy
+5hand.
+h4and
+h2an4g
+hang5er
+han1g5o
+h5a5niz
+ha4n4k2
+han4te
+han1t
+ha2p3l2
+ha2p5t
+ha3ra2n
+h2a2r
+ha5r2as
+har2d
+hard3e
+har4le4
+har1l
+harp5en
+har2p
+har5ter
+ha2s5s
+haun4
+5haz
+haz3a1
+h1b
+1hea2d1
+3he2a2r
+he4ca2n
+he1ca
+h5ecat
+h4ed
+h4e5do5
+he3l4i
+hel4lis
+hel1l
+hell2i
+hel4ly
+h5elo
+he4m4p
+he2n
+he1na4
+hen5at
+he1o5r
+hep5
+h4er1a
+hera3p
+her4ba
+h2er1b
+here5a
+h3ern
+h5er1ou2
+h2ero
+h3ery
+h1es
+he2s5p
+he4t
+he2t4ed
+h4eu4
+h1f
+h1h
+hi5a2n
+h2i1a
+hi4co
+high5
+h2ig
+h4il2
+himer4
+h4i1na
+hion4e
+h2io
+hio2n
+h2i4p
+hir4l
+h4ir
+hi3ro
+hir4p
+hir4r4
+his3el
+h4ise
+h4i2s4s
+hith5er
+h2ith
+hith2e
+h2i2v
+4hk
+4h1l4
+hla2n4
+h2lo
+hlo3ri
+4h1m
+hmet4
+2h1n
+h5odiz
+h5o2d1s2
+ho4g
+ho1ge4
+hol5a2r
+ho1la
+3hol4e
+ho4ma
+ho2me3
+ho1n4a
+ho2n
+ho5ny
+3hood
+hoo2
+hoo2n4
+hor5at
+ho1ra
+ho5r2is
+hort3e
+ho5ru
+hos4e
+ho5sen
+hos1p
+1ho2us
+hou2
+house3
+hov5el
+4h5p
+4hr4
+hree5
+hro5niz
+hro2n
+hro3po
+4h1s2
+h4s2h
+h4t2a2r
+h1ta
+ht1en
+ht5es
+h4ty
+hu4g
+hu4min
+hu1mi
+hun5ke
+hu4nk2
+hun4t
+hus3t4
+h2us
+hu4t
+h1w
+h4war4t
+hw2a2r
+hy3pe
+hy3ph
+hy2s
+2i1a
+i2al
+iam4
+iam5e1te
+i2a2n
+4ianc
+ian3i
+4ian4t
+ia5pe
+ia2ss4
+i4a1t2iv
+ia4tric
+ia1tr
+i4a2tu
+ibe4
+ib3er1a
+ib5ert
+ib5i1a
+ib3in
+ib5it.
+ibi2t
+ib5ite
+i1b2l2
+ib3li
+i5bo
+i1br
+i2b5ri
+i5bu4n
+4icam
+i1ca
+5icap
+4ic2a2r
+i4car.
+i4cara
+icas5
+i4cay
+iccu4
+ic3c
+4iceo
+4i2ch
+2i1ci
+i5c2id
+ic5i1na
+i2cin
+i2c2ip
+ic3i1pa
+i4c1ly4
+i1c4l4
+i2c5oc
+i1co
+4i1cr
+5icra
+i4cry
+ic4te
+i2c1t
+ic1tu2
+ic4t3u1a
+ic3u1la
+ic4um
+ic5uo
+i3cur
+2id
+i4dai2
+i1d2a
+id5anc
+ida2n
+id5d4
+ide3a4l
+ide4s2
+i2di
+id5i2a2n
+i1d4i3a
+idi4a2r
+i5d2ie4
+i1d3io
+idi5ou2
+id1it
+id5i1u
+i3dle
+i4dom
+i1do
+id3ow
+i4dr
+i2du
+id5uo
+2ie4
+ied4e
+5ie5ga
+ie2ld3
+ie1n5a4
+ien4e
+i5e4n1n2
+i3ent2i
+ien1t
+i1er.
+i3es2c
+i1est
+i3et
+4if.
+if5ero
+ifer1
+iff5en
+i4f1f
+if4fr
+4i2f3ic.
+i1fi
+i3f2ie4
+i3f4l2
+4i2ft
+2ig
+iga5b
+i1ga
+ig3er1a
+ight3i
+4igi
+i3gib
+ig3il4
+ig3in
+ig3it
+i4g4l2
+i2go
+ig3or
+ig5ot
+i5gre
+i1gr
+ig2u5i2
+ig1ur
+i3h
+4i5i4
+i3j
+4ik
+i1la
+il3a4b
+i4l4ade
+ila2d
+i2l5am
+ila5ra
+il2a2r
+i3leg
+il1er
+ilev4
+i2l5f
+il1i
+il3i1a
+il2ib
+il3io
+il4ist
+2il1it
+il2iz
+ill5ab
+il1l
+4i2l1n2
+il3o1q
+il4ty
+i4lt
+il5ur
+il3v
+i4mag
+i1ma
+im3age
+ima5ry
+im2a2r
+iment2a5r
+i1men
+i3men1t
+imen1ta
+4imet
+im1i
+im5i1d4a
+im2id
+imi5le
+i5m2ini
+4imit
+im4ni
+i4m1n
+i3mo2n
+i1mo
+i2mu
+im3u1la
+2in.
+i4n3au
+i1na
+4inav
+incel4
+in3cer
+4ind
+in5dling
+2ine
+i3nee
+in4er4a2r
+in1er
+iner1a
+i5n2e2ss
+i1nes
+4in1ga
+4inge
+in5gen
+4ingi
+in5gling
+ingl2
+4in1go
+4in1gu
+2ini
+i5ni.
+i4n4i1a
+in3i4o
+in1is
+i5ni4te.
+in2it
+in2ite
+5i3n2i1t2io
+ini1ti
+in3i1ty
+4i4nk2
+4i4n1l
+2i4n1n2
+2i1no
+i4no4c
+ino4s
+i4not
+2i2n1s2
+in3se
+insu1r5a
+in1su
+insu2r
+2int.
+in1t
+2in4th
+in1u
+i5n2us
+4iny
+2io
+4io.
+io1ge4
+io2gr
+i1ol
+io4m
+ion3at
+io2n
+io1n1a
+ion4ery
+ion1er
+ion3i
+i2o5ph
+ior3i
+i4os
+i4o5th
+i5oti
+io4to
+i4our
+iou2
+2ip
+ipe4
+iphr2as4
+ip4hr4
+ip3i
+ip4ic
+ip4re4
+ipr2
+ip3ul
+i3qua
+iqu2
+iq5ue1f
+iq3u2id
+iq2ui2
+iq3ui3t
+4ir
+i1ra
+i2ra4b
+i4rac
+ird5e
+ire4de
+i2r2ed
+i4re1f
+i4rel4
+i4res
+ir5gi
+irg2
+ir1i
+iri5de
+ir2id
+ir4is
+iri3tu
+5i5r2iz
+ir4min
+ir1m
+iro4g
+5iron.
+iro2n
+ir5ul
+2is.
+is5ag
+isa2
+is3a2r
+isas5
+2is1c
+is3ch2
+4ise
+is3er
+3i4s3f
+is5ha2n
+is2h
+is3ho2n3
+isho4
+ish5op
+is3i1b
+is2i4d
+i5sis
+is5i1t2iv
+isi1ti
+4is4k2
+isla2n4
+is1l2
+4is4m1s2
+i2s1m
+i2so
+iso5mer
+i3som
+iso2me
+is1p
+is2pi
+is4py
+4i2s1s
+is4sal
+is1sa2
+issen4
+is4s1e4s
+is4ta.
+is1ta
+is1te
+is1t2i
+ist4ly
+is2tl
+4istral
+ist4r
+is1tra
+i2su
+is5us
+4i3ta.
+i1ta
+ita4bi
+i2tab
+i4tag
+4ita5m
+i3ta2n
+i3tat
+2ite
+it3er1a
+i5ter1i
+it4es
+2ith
+i1ti
+4i1t2i1a
+4i2tic
+it3i1ca
+5i5tick1
+i2t3ig
+it5il1l
+i2tim
+2i1t2io
+4itis
+i4ti2s4m
+i2t5o5m
+i1to
+4ito2n
+i4tram
+i1tra
+it5ry
+4i4t3t2
+it3u1at
+i1tu
+itu1a
+i5tud2
+it3ul
+4itz.
+i4tz
+i1u
+2iv
+iv3el1l
+iv3en.
+i4v3er.
+i4vers.
+ive4r1s2
+iv5il.
+i2vil
+iv5io
+iv1it
+i5vore
+iv3o3ro
+i4v3ot
+4i5w
+ix4o
+4iy
+4iz2a2r2
+iza1
+i2z1i4
+5izon1t
+i1zo
+izo2n
+5ja
+jac4q
+ja4p
+1je
+je4r5s2
+4jes4t2ie4
+jest2i
+4jes2ty
+jew3
+jo4p
+5judg
+3ka.
+k3ab
+k5ag
+kais4
+kai2
+kal4
+k1b
+k2ed
+1kee
+ke4g
+ke5l2i
+k3en4d
+k1er
+kes4
+k3e2st.
+ke4ty
+k3f
+kh4
+k1i
+5ki.
+5k2ic
+k4il1l
+kilo5
+k4im
+k4in.
+kin4de
+k4ind
+k5i5n2e2ss
+k2ine
+ki1nes
+kin4g
+k2i4p
+kis4
+k5is2h
+kk4
+k1l
+4k3ley
+4k1ly
+k1m
+k5nes
+1k2no
+ko5r
+kos2h4
+k3ou2
+kro5n
+4k1s2
+k4sc
+ks4l2
+k4s4y
+k5t
+k1w
+lab3ic
+l4abo
+l4a2ci4
+l4ade
+la2d
+la3d2y
+lag4n
+la2m3o
+3l4and
+la2n
+lan4dl
+lan5et
+lan4te
+lan1t
+lar4g2
+l2a2r
+lar3i
+las4e
+la5ta2n
+la2ta
+4latel2i4
+4la1t2iv
+4lav
+la4v4a
+2l1b
+lbin4
+4l1c2
+lce4
+l3ci
+2ld
+l2de
+ld4ere
+ld4er1i
+ldi4
+ld5is1
+l3dr
+l4dri
+le2a
+le4bi
+l2e1b
+le2ft5
+le1f
+5leg.
+5le4g1g2
+le4mat
+le1ma
+lem5at1ic
+4len.
+3lenc
+5le2ne.
+1len1t
+le3ph
+le4pr2
+le2ra5b
+ler1a
+ler4e
+3lerg2
+3l4er1i
+l4ero
+les2
+le5s1co
+les2c
+5lesq
+3l2e2ss
+5less.
+l3e1va
+lev4er.
+lev1er
+lev4er1a
+lev4e4r1s2
+3ley
+4leye
+2lf
+l5fr
+4l1g4
+l5ga
+lg2a2r3
+l4ges
+l1go3
+2l3h
+li4ag
+l2i1a
+li2am4
+liar5iz
+li2a2r
+liar1i
+li4as
+li4a1to
+li5bi
+5lic2io
+l2i1ci
+li4cor
+li1co
+4li4c3s2
+4lict.
+li2c1t
+l4icu
+l3i1cy
+l3i1d2a
+l2id
+lid5er
+3li2di
+lif3er1
+l4i4f1f
+li4f4l2
+5ligate
+l2ig
+li1ga
+3ligh
+li4gra
+li1gr
+3l4ik
+4l4i4l
+lim4b2l2
+li4m1b
+lim3i
+li4mo
+l4i4m4p
+l4i1na
+1l4ine
+lin3ea
+l2in3i
+link5er
+l4i4nk2
+li5og
+l2io
+4l4iq
+lis4p
+l1it
+l2it.
+5lit3i1ca
+li1ti
+l4i2tic
+l5i5ti4c3s2
+liv3er
+l2iv
+l1iz
+4lj
+lka3
+l3kal4
+lka4t
+l1l
+l4law
+l2le
+l5le2a
+l3lec
+l3leg
+l3lel
+l3le4n
+l3le4t
+ll2i
+l2lin4
+l5l4i1na
+ll4o
+lloq2ui5
+llo1q
+lloqu2
+l2l5out
+llou2
+l5low
+2lm
+l5met
+lm3ing
+l4mo2d1
+l1mo
+lmo2n4
+2l1n2
+3lo.
+lob5al
+lo4ci
+4lof
+3log1ic
+l5o1go
+3logu
+lom3er
+lo2me
+5long
+lo2n
+lon4i
+l3o3niz
+lood5
+loo2
+5lo4pe.
+lop3i
+l3o4p1m
+lo1ra4
+lo4ra1to
+lo5r2ie4
+lor5ou2
+5los.
+los5et
+5los5o3phiz
+lo2so
+los4op
+los2oph
+5los5o1phy
+los4t
+lo4ta
+loun5d
+lou2
+2lout
+4lov
+2lp
+lpa5b
+l1pa
+l3pha
+l5phi
+lp5ing
+lpi2n
+l3pit
+l4p2l2
+l5pr2
+4l1r
+2l1s2
+l4sc
+l2se
+l4s2ie4
+4lt
+lt5ag
+l1ta
+ltane5
+lta2n
+l1te
+lten4
+lter1a4
+lth3i
+l5ties.
+lt2ie4
+ltis4
+l1tr
+l1tu2
+ltu1r3a
+lu5a
+lu3br
+lu2ch4
+lu3ci
+lu3en
+luf4
+lu5id
+l2ui2
+lu4ma
+5lu1mi
+l5umn.
+lu4m1n
+5lum3n4i1a
+lu3o
+luo3r
+4lup
+lu2ss4
+l2us
+lus3te
+1lut
+l5ven
+l5vet4
+2l1w
+1ly
+4lya
+4ly1b
+ly5me4
+ly3no
+2lys4
+l5y3s2e
+1ma
+2mab
+ma2ca
+ma5ch2ine
+ma2ch
+ma4ch1in
+ma4c4l4
+mag5in
+mag1i
+5mag1n
+2mah
+ma2id5
+mai2
+4ma2ld
+ma3l2ig
+mal1i
+ma5lin
+mal4l2i
+mal1l
+mal4ty
+ma4lt
+5ma3n4i1a
+ma2n
+man5is
+man3iz
+4map
+ma5ri2ne.
+m2a2r
+mar1i
+mar2in4e
+ma5r2iz
+mar4ly
+mar1l
+mar3v
+ma5sce
+mas4e
+mas1t
+5mate
+m4ath3
+ma3tis
+4mati3za1
+ma1tiz
+4m1b
+m1ba4t5
+m5bil
+m4b3ing
+mb2i4v
+4m5c
+4me.
+2med
+4med.
+5me3d4i3a
+m4edi
+me3d2ie4
+m5e5d2y
+me2g
+mel5o2n
+me4l4t
+me2m
+me1m1o3
+1men
+me1n4a
+men5ac
+men4de
+4mene
+men4i
+me2n1s4
+men1su5
+3men1t
+men4te
+me5o2n
+m5er1sa2
+me4r1s2
+2mes
+3mest2i
+me4ta
+met3a2l
+me1te
+me5thi
+m4etr
+5met3ric
+me5tr2ie4
+me3try
+me4v
+4m1f
+2mh
+5mi.
+m2i3a
+mi1d4a
+m2id
+mid4g
+m2ig4
+3mil3i1a
+mil1i
+m5i5l2ie4
+m4il1l
+mi1n4a
+3m4ind
+m5i3nee
+m2ine
+m4ingl2
+min5gli
+m5ing1ly
+min4t
+m4in1u
+miot4
+m2io
+m2is
+mi4s4er.
+m4ise
+mis3er
+mis5l2
+mis4t2i
+m5i4stry
+mist4r
+4m2ith
+m2iz
+4mk
+4m1l
+m1m
+mma5ry
+m1ma
+mm2a2r
+4m1n
+m1n4a
+m4n1in
+mn4o
+1mo
+4mocr
+5moc5ra1tiz
+mo2d1
+mo4go
+mois2
+moi2
+mo4i5se
+4m2ok
+mo5lest
+moles2
+mo3me
+mon5et
+mo2n
+mon5ge
+mo3n4i3a
+mon4i2s1m
+mon1is
+mon4ist
+mo3niz
+monol4
+mo3ny.
+mo2r
+4mo5ra.
+mo1ra
+mos2
+mo5sey
+mo3sp
+m4oth3
+m5ouf
+mou2
+3mo2us
+mo2v
+4m1p
+mpara5
+m1pa
+mp2a2r
+mpa5rab
+mp4a4r5i
+m3pe2t
+mphas4
+m2pi
+mp2i4a
+mp5ies
+mp2ie4
+m4p1i2n
+m5p4ir
+mp5is
+mpo3ri
+m1p4or
+mpos5ite
+m1pos
+m4po2us
+mpou2
+mpov5
+mp4tr
+m2p1t
+m2py
+4m3r
+4m1s2
+m4s2h
+m5si
+4mt
+1mu
+mul2a5r4
+mu1la
+5mu4lt
+mul1ti3
+3mum
+mun2
+4mup
+mu4u
+4mw
+1na
+2n1a2b
+n4abu
+4nac.
+na4ca
+n5a2c1t
+nag5er.
+nak4
+na4l1i
+na5l2i1a
+4na4lt
+na5mit
+n2a2n
+nan1ci4
+nan4it
+na4nk4
+nar3c
+n2a2r
+4nare
+nar3i
+nar4l
+n5ar1m
+n4as
+nas4c
+nas5t2i
+n2at
+na3ta2l
+na2ta
+nat5o5m2iz
+na2tom
+na1to
+n2au
+nau3se
+na2us
+3naut
+nav4e
+4n1b4
+nc2a2r5
+n1ca
+n4ces.
+n3cha
+n2ch
+n5cheo
+nche2
+n5ch4il2
+n3chis
+n2c1in
+n1ci
+n2c4it
+ncou1r5a
+n1co
+ncou2
+n1cr
+n1cu
+n4dai2
+n1d2a
+n5da2n
+n1de
+nd5e2st.
+ndes2
+ndi4b
+n5d2if
+n1dit
+n3diz
+n5du2c
+n1du
+ndu4r
+nd2we
+nd1w
+2ne.
+n3e2a2r
+n2e2b
+neb3u
+ne2c
+5neck1
+2ned
+ne4gat
+ne1ga
+ne4g5a1t2iv
+5nege
+ne4la
+nel5iz
+nel2i
+ne5mi
+ne4mo
+1nen
+4nene
+3neo
+ne4po
+ne2q
+n1er
+ne2ra5b
+ner1a
+n4er3a2r
+n2ere
+n4er5i
+ner4r4
+1nes
+2nes.
+4ne1sp
+2nest
+4nes4w2
+3net1ic
+ne4v
+n5eve
+ne4w
+n3f
+n4gab
+n1ga
+n3gel
+nge4n4e
+n1gen
+n5gere
+n3ger1i
+ng5ha
+n3gib
+ng1in
+n5git
+n4gla4
+ngl2
+ngov4
+n1go
+ng5s2h
+ngs2
+n1gu
+n4gum
+n2gy
+4n1h4
+nha4
+nhab3
+nhe4
+3n4i1a
+ni3a2n
+ni4ap
+ni3ba
+ni4b2l2
+n2i4d
+ni5di
+ni4er
+n2ie4
+ni2fi
+ni5ficat
+nifi1ca
+n5i1gr
+n2ig
+n4ik4
+n1im
+ni3m2iz
+nim1i
+n1in
+5ni2ne.
+n2ine
+nin4g
+n2i4o
+5n2is.
+nis4ta
+n2it
+n4ith
+3n2i1t2io
+ni1ti
+n3itor
+ni1to
+ni3tr
+n1j
+4nk2
+n5k2ero
+nk1er
+n3ket
+nk3in
+nk1i
+n1k1l
+4n1l
+n5m
+nme4
+nmet4
+4n1n2
+nne4
+nni3al
+n3n4i1a
+nn2i4v
+nob4l2
+no3ble
+n5o1c4l4
+4n3o2d
+3noe
+4nog
+no1ge4
+nois5i
+noi2
+no5l4i
+5nol1o1gis
+3nomic
+n5o5m2iz
+no4mo
+no3my
+no4n
+non4ag
+no1n1a
+non5i
+n5oniz
+4nop
+5nop5o5l2i
+no2r5ab
+no1ra
+no4rary
+nor2a2r
+4nos2c
+nos4e
+nos5t
+no5ta
+1nou2
+3noun
+nov3el3
+nowl3
+n1p4
+npi4
+npre4c
+npr2
+n1q
+n1r
+nru4
+2n1s2
+n2s5ab
+nsa2
+nsati4
+ns4c
+n2se
+n4s3e4s
+ns2id1
+ns2ig4
+n2s1l2
+n2s3m
+n4soc
+n1so
+ns4pe
+n5spi
+nsta5b2l2
+ns1ta
+ns2tab
+n1t
+n2ta4b
+n1ta
+nte4r3s2
+nt2i
+n5ti2b
+nti4er
+nt2ie4
+nti2f2
+n3t2ine
+n2t1in
+n4t3ing
+nt2i4p
+ntrol5l2i
+ntrol1l
+n4t4s2
+ntu3me
+n1tu
+n3tum
+nu1a
+nu4d
+nu5en
+nuf4fe
+nu4f1f
+n3ui4n
+n2ui2
+3nu3it
+n4um
+nu1me
+n5u1mi
+3nu4n
+n3uo
+nu3tr
+n1v2
+n1w4
+nym4
+nyp4
+4nz
+n3za1
+4oa
+oa2d3
+o5a5les2
+o2ale
+oard3
+o2a2r
+oas4e
+oast5e
+oat5i
+ob3a3b
+o5b2a2r
+o1be4l
+o1bi
+o2bin
+ob5ing
+o3br
+ob3ul
+o1ce
+o2ch4
+o3che4t
+oche2
+ocif3
+o1ci
+o4cil
+o4clam
+o1c4l4
+o4cod
+o1co
+oc3rac
+oc5ra1tiz
+ocre3
+5ocrit
+ocri2
+octo2r5a
+o2c1t
+oc1to
+oc3u1la
+o5cure
+od5d1ed
+od1d4
+od3ic
+o1d2i3o
+o2do4
+od4or3
+o4d5uct.
+o1du
+odu2c
+odu2c1t
+o4d5uc4t1s2
+o4el
+o5eng
+o3er
+oe4ta
+o3ev
+o2fi
+of5ite
+of4i4t4t2
+o2g5a5r
+o1ga
+o4g5a1t2iv
+o4ga1to
+o1ge
+o5gene
+o1gen
+o5geo
+o4ger
+o3g2ie4
+1o1gis
+og3it
+o4gl2
+o5g2ly
+3ogniz
+og1ni
+o4g4ro
+o1gr
+og2u5i2
+1o1gy
+2o2g5y3n
+o1h2
+ohab5
+oi2
+oic3es
+oi3der
+o2id
+oi4f1f4
+o2ig4
+oi5let
+o3ing
+oint5er
+oin1t
+o5i2s1m
+oi5so2n
+oi2so
+oist5en
+ois1te
+oi3ter
+o2ite
+o5j
+2ok
+o3ken
+ok5ie4
+ok1i
+o1la
+o4la2n
+ola2ss4
+o2l2d
+ol2d1e
+ol3er
+o3les2c
+oles2
+o3let
+ol4fi
+o2lf
+ol2i
+o3l2i1a
+o3lice
+ol5id.
+ol2id
+o3li4f
+o5l4i4l
+ol3ing
+o5l2io
+o5l2is.
+ol3is2h
+o5l2ite
+ol1it
+o5l2i1t2io
+oli1ti
+o5l2iv
+oll2i4e4
+ol1l
+oll2i
+ol5o3giz
+olo4r
+ol5p2l2
+o2lp
+o4l2t
+ol3ub
+ol3ume
+ol3un
+o5l2us
+ol2v
+o2ly
+o2m5ah
+o1ma
+oma5l
+om5a1tiz
+om2be
+o4m1b
+om4b2l2
+o2me
+om3e1n4a
+o1men
+om5er2se
+ome4r1s2
+o4met
+om5e3try
+om4etr
+o3m2i3a
+om3ic.
+om3i1ca
+o5m2id
+om1in
+o5m2ini
+5ommend
+om1m
+om1men
+omo4ge
+o1mo
+o4mo2n
+om3pi
+o4m1p
+ompro5
+ompr2
+o2n
+o1n1a
+on4ac
+o3n2a2n
+on1c
+3oncil
+on1ci
+2ond
+on5do
+o3nen
+o2n5est
+o1nes
+on4gu
+on1ic
+o3n2i4o
+on1is
+o5ni1u
+on3key
+o4nk2
+on4odi
+o4n3o2d
+on3o3my
+o2n3s2
+on5spi4
+onspi1r5a
+onsp4ir
+on1su4
+onten4
+on1t
+on3t4i
+onti2f5
+on5um
+on1va5
+on1v2
+oo2
+ood5e
+ood5i
+o2o4k
+oop3i
+o3ord
+oost5
+o2pa
+o2p2e5d
+op1er
+3oper1a
+4op4erag
+2oph
+o5pha2n
+o5ph4er
+op3ing
+opi2n
+o3pit
+o5po2n
+o4posi
+o1pos
+o1pr2
+op1u
+opy5
+o1q
+o1ra
+o5ra.
+o4r3ag
+or5al1iz
+oral1i
+or5an4ge
+ora2n
+or2ang
+ore5a
+o5re1a4l
+or3ei2
+or4e5s2h
+or5e2st.
+ores2t
+orew4
+or4gu
+org2
+4o5r2i3a
+or3i1ca
+o5ril
+or1in
+o1r2i1o
+or3i1ty
+o3ri1u
+or2mi
+or1m
+orn2e
+o5rof
+or3oug
+orou2
+or5pe
+or1p
+3orrh4
+or1r4
+or4se
+o4rs2
+ors5en
+orst4
+or3thi
+or3thy
+or4ty
+o5rum
+o1ry
+os3al
+osa2
+os2c
+os4ce
+o3scop
+os1co
+4oscopi
+o5scr
+os4i4e4
+os5i1t2iv
+osi1ti
+os3i1to
+os3i1ty
+o5si4u
+os4l2
+o2so
+o2s4pa
+os4po
+os2ta
+o5stati
+os5til
+ost2i
+os5tit
+o4ta2n
+o1ta
+otele4g
+ot3er.
+ot5e4r1s2
+o4tes
+4oth
+oth5e1si
+oth2e
+oth1es
+oth3i4
+ot3ic.
+ot5i1ca
+o3tice
+o3tif2
+o3tis
+oto5s2
+o1to
+ou2
+ou3b2l2
+ouch5i
+ou2ch
+ou5et
+ou4l
+ounc5er
+oun2d
+ou5v2
+ov4en
+over4ne
+ove4r3s2
+ov4ert
+o3vis
+o4vi1ti4
+o5v4ol
+ow3der
+ow3el
+ow5est3
+ow1i2
+own5i
+o4wo2
+oy1a
+1pa
+pa4ca
+pa4ce
+pa2c4t
+p4a2d
+5paga4n
+pa1ga
+p3agat
+p4ai2
+pa4i4n4
+p4al
+pa1n4a
+pa2n
+pan3el
+pan4ty
+pan1t
+pa3ny
+pa1p
+pa4pu
+para5b2l2
+p2a2r
+pa2rab
+par5age
+par5d2i
+3pare
+par5el
+p4a4r1i
+par4is
+pa2te
+pa5ter
+5pathic
+p4ath
+pa5thy
+pa4tric
+pa1tr
+pav4
+3pay
+4p1b
+pd4
+4pe.
+3pe4a
+pear4l
+pe2a2r
+pe2c
+2p2ed
+3pede
+3p4edi
+pe3d4i3a4
+ped4ic
+p4ee
+pee4d
+pek4
+pe4la
+pel2i4e4
+pel2i
+pe4n2a2n
+pe1na
+p4enc
+pen4th
+pen1t
+pe5o2n
+p4era.
+per1a
+pera5b2l2
+pe2ra4b
+p4erag
+p4er1i
+peri5st
+per2is
+per4mal
+per3m4
+per1ma
+per2me5
+p4ern
+p2er3o
+per3ti
+p4e5ru
+per1v
+pe2t
+pe5ten
+pe5tiz
+4pf
+4pg
+4ph.
+phar5i
+ph2a2r
+ph4e3no
+phe2n
+ph4er
+ph4es.
+ph1es
+ph1ic
+5ph2ie4
+ph5ing
+5phis1t2i
+3phiz
+p4h2l4
+3phob
+3phone
+pho2n
+5phoni
+pho4r
+4p4h1s2
+ph3t
+5phu
+1phy
+p2i3a
+pi2a2n4
+pi4c2ie4
+p2i1ci
+pi4cy
+p4id
+p5i1d2a
+pi3de
+5pi2di
+3piec
+p2ie4
+pi3en
+pi4grap
+p2ig
+pi1gr
+pi3lo
+pi2n
+p4in.
+p4ind4
+p4i1no
+3p2i1o
+pio2n4
+p3ith
+pi5tha
+pi2tu
+2p3k2
+1p2l2
+3pla2n
+plas5t
+pl2i3a
+pli5er
+pl2ie4
+4pl2ig
+pli4n
+ploi4
+plu4m
+plu4m4b
+4p1m
+2p3n
+po4c
+5pod.
+po5em
+po3et5
+5po4g
+poin2
+poi2
+5poin1t
+poly5t
+po2ly
+po4ni
+po2n
+po4p
+1p4or
+po4ry
+1pos
+po2s1s
+p4ot
+po4ta
+5poun
+pou2
+4p1p
+ppa5ra
+p1pa
+pp2a2r
+p2pe
+p4p2ed
+p5pel
+p3pen
+p3per
+p3pe2t
+ppo5s2ite
+p1pos
+pr2
+pray4e4
+5pre1c2i
+pre5co
+pre3e2m
+pre4f5ac
+pre1f
+pre1fa
+pre4la
+pr1e3r4
+p3re1s2e
+3pr2e2ss
+pre5ten
+pre3v2
+5pr2i4e4
+prin4t3
+pr2i4s
+pri2s3o
+p3ro1ca
+pr2oc
+prof5it
+pro2fi
+pro3l
+pros3e
+pro1t
+2p1s2
+p2se
+ps4h
+p4si1b
+2p1t
+p2t5a4b
+p1ta
+p2te
+p2th
+p1ti3m
+ptu4r
+p1tu
+p4tw4
+pub3
+pue4
+puf4
+pu4l3c2
+pu4m
+pu2n
+pur4r4
+5p2us
+pu2t
+5pute
+put3er
+pu3tr
+put4t1ed
+pu4t3t2
+put4t1in
+p3w
+qu2
+qua5v4
+2que.
+3quer
+3quet
+2rab
+ra3bi
+rach4e2
+ra2ch
+r5a1c4l4
+raf5fi
+ra2f
+ra4f1f4
+ra2f4t
+r2ai2
+ra4lo
+ram3et
+r2ami
+ra3ne5o
+ra2n
+ran4ge
+r2ang
+r4ani
+ra5no4
+rap3er
+3ra1phy
+rar5c
+r2a2r
+rare4
+rar5e1f
+4raril
+rar1i
+r2as
+ratio2n4
+ra1t2io
+rau4t
+ra5vai2
+ra2va
+rav3el
+ra5z2ie4
+ra2z1i
+r1b
+r4bab
+r4bag
+rbi2
+r2b3i4f
+r2bin
+r5b2ine
+rb5ing.
+rb4o
+r1c
+r2ce
+r1cen4
+r3cha
+r2ch
+rch4er
+rche2
+r4ci4b
+r1ci
+r2c4it
+rcum3
+r4dal
+r1d2a
+rd2i
+r1d4i4a
+rdi4er
+rd2ie4
+rd1in4
+rd3ing
+2re.
+re1a4l
+re3a2n
+re5ar1r4
+re2a2r
+5rea2v
+re4aw
+r5ebrat
+r2e1b
+re3br
+rec5ol1l
+re2col
+re1co
+re4c5ompe
+reco4m1p
+re4cre
+re1cr
+2r2ed
+re1de
+re3dis1
+r4edi
+red5it
+re4fac
+re1f
+re1fa
+re2fe
+re5fer.
+refer1
+re3fi
+re4fy
+reg3is
+re5it
+rei2
+re1l2i
+re5lu
+r4en4ta
+ren1t
+ren4te
+re1o
+re5pi2n
+re4posi
+re1po
+re1pos
+re1pu
+r1er4
+r4er1i
+r2ero4
+r4e5ru
+r4es.
+re4spi
+re1sp
+res4s5i4b
+r2e2ss
+res1si
+res2t
+re5s2ta2l
+res1ta
+r2e3st4r
+re4ter
+re4ti4z
+re3tri
+r4eu2
+re5u1t2i
+rev2
+re4val
+re1va
+rev3el
+r5ev5er.
+rev1er
+re5ve4r1s2
+re5vert
+re5vi4l
+re1vi
+rev5olu
+re4wh
+r1f
+r3fu4
+r4fy
+rg2
+rg3er
+r3get
+r3g1ic
+rgi4n
+rg3ing
+r5gis
+r5git
+r1gl2
+rgo4n2
+r1go
+r3gu
+rh4
+4rh.
+4rhal
+r2i3a
+ria4b
+ri4ag
+r4ib
+rib3a
+ric5as5
+ri1ca
+r4ice
+4r2i1ci
+5ri5c2id
+ri4c2ie4
+r4i1co
+rid5er
+r2id
+ri3enc
+r2ie4
+ri3en1t
+ri1er
+ri5et
+rig5a2n
+r2ig
+ri1ga
+5r4igi
+ril3iz
+ril1i
+5rima2n
+ri1ma
+rim5i
+3ri1mo
+rim4pe
+ri4m1p
+r2i1na
+5rina.
+r4in4d
+r2in4e
+rin4g
+r2i1o
+5riph
+r2ip
+riph5e
+ri2p2l2
+rip5lic
+r4iq
+r2is
+r4is.
+r2is4c
+r3is2h
+ris4p
+ri3ta3b
+ri1ta
+r5ited.
+r2ite
+ri2t1ed
+rit5er.
+rit5e4r1s2
+r4i2t3ic
+ri1ti
+ri2tu
+rit5ur
+riv5el
+r2iv
+riv3et
+riv3i
+r3j
+r3ket
+rk4le
+rk1l
+rk4lin
+r1l
+rle4
+r2led
+r4l2ig
+r4lis
+rl5is2h
+r3lo4
+r1m
+rma5c
+r1ma
+r2me
+r3men
+rm5e4r1s2
+rm3ing
+r4ming.
+r4m2io
+r3mit
+r4my
+r4n2a2r
+r1na
+r3nel
+r4n1er
+r5net
+r3ney
+r5nic
+r1nis4
+r3n2it
+r3n2iv
+rno4
+r4nou2
+r3nu
+rob3l2
+r2oc
+ro3cr
+ro4e
+ro1fe
+ro5fil
+ro2fi
+r2ok2
+ro5k1er
+5role.
+rom5e1te
+ro2me
+ro4met
+rom4i
+ro4m4p
+ron4al
+ro2n
+ro1n1a
+ron4e
+ro5n4is
+ron4ta
+ron1t
+1room
+roo2
+5root
+ro3pel
+rop3ic
+ror3i
+ro5ro
+ro2s5per
+ro2s4s
+ro4th2e
+r4oth
+ro4ty
+ro4va
+rov5el
+rox5
+r1p
+r4pe4a
+r5pen1t
+rp5er.
+r3pe2t
+rp4h4
+rp3ing
+rpi2n
+r3po
+r1r4
+rre4c
+rre4f
+r4re1o
+rre4s2t
+rr2i4o
+rr2i4v
+rro2n4
+rros4
+rrys4
+4rs2
+r1sa2
+rsa5ti
+rs4c
+r2se
+r3sec
+rse4cr
+r4s5er.
+rs3e4s
+r5se5v2
+r1s2h
+r5sha
+r1si
+r4si4b
+rso2n3
+r1so
+r1sp
+r5sw2
+rta2ch4
+r1ta
+r4tag
+r3t2e1b
+r3ten4d
+r1te5o
+r1ti
+r2t5i2b
+rt2i4d
+r4tier
+rt2ie4
+r3t2ig
+rtil3i
+rtil4l
+r4ti1ly
+r4tist
+r4t2iv
+r3tri
+rtr2oph4
+rt4s2h4
+r4t1s2
+ru3a
+ru3e4l
+ru3en
+ru4gl2
+ru3i4n
+r2ui2
+rum3p2l2
+ru4m2p
+ru2n
+ru4nk5
+run4ty
+run1t
+r5usc2
+r2us
+ru2t1i5n
+r4u1t2i
+rv4e
+rvel4i
+r3ven
+rv5er.
+r5vest
+rv4e2s
+r3vey
+r3vic
+r3v2i4v
+r3vo
+r1w
+ry4c
+5rynge
+ryn5g
+ry3t
+sa2
+2s1ab
+5sack1
+sac3ri2
+s3a2c1t
+5sai2
+sa4l2a2r4
+s4a2l4m
+sa5lo
+sa4l4t
+3sanc
+sa2n
+san4de
+s4and
+s1ap
+sa5ta
+5sa3t2io
+sa2t3u
+sau4
+sa5vor
+5saw
+4s5b
+scan4t5
+s1ca
+sca2n
+sca4p
+scav5
+s4ced
+4s3cei2
+s4ces
+s2ch2
+s4cho2
+3s4c2ie4
+s1ci
+5sc4in4d
+s2cin
+scle5
+s1c4l4
+s4cli
+scof4
+s1co
+4scopy5
+scou1r5a
+scou2
+s1cu
+4s5d
+4se.
+se4a
+seas4
+sea5w
+se2c3o
+3se2c1t
+4s4ed
+se4d4e
+s5edl
+se2g
+se1g3r
+5sei2
+se1le
+5se2l2f
+5selv
+4se1me
+se4mol
+se1mo
+sen5at
+se1na
+4senc
+sen4d
+s5e2ned
+sen5g
+s5en1in
+4sen4t1d
+sen1t
+4sen2tl
+se2p3a3
+4s1er.
+s4er1l
+s2er4o
+4ser3vo
+s1e4s
+s4e5s2h
+ses5t
+5se5um
+s4eu
+5sev
+sev3en
+sew4i2
+5sex
+4s3f
+2s3g
+s2h
+2sh.
+sh1er
+5shev
+sh1in
+sh3io
+3sh2i4p
+sh2i2v5
+sho4
+sh5o2l2d
+sho2n3
+shor4
+short5
+4sh1w
+si1b
+s5ic3c
+3si2de.
+s2id
+5side4s2
+5si2di
+si5diz
+4sig1n4a
+s2ig
+sil4e
+4si1ly
+2s1in
+s2i1na
+5si2ne.
+s2ine
+s3ing
+1s2io
+5sio2n
+sio1n5a
+s4i2r
+si1r5a
+1sis
+3s2i1t2io
+si1ti
+5si1u
+1s2iv
+5siz
+sk2
+4ske
+s3ket
+sk5ine
+sk1i
+sk5in4g
+s1l2
+s3lat
+s2le
+sl2ith5
+sl1it
+2s1m
+s3ma
+smal1l3
+sma2n3
+smel4
+s5men
+5s4m2ith
+smo2l5d4
+s1mo
+s1n4
+1so
+so4ce
+so2ft3
+so4lab
+so1la
+so2l3d2
+so3lic
+sol2i
+5sol2v
+3som
+3s4on.
+so2n
+so1n1a4
+son4g
+s4op
+5soph1ic
+s2oph
+s5o3phiz
+s5o1phy
+sor5c
+sor5d
+4sov
+so5vi
+2s1pa
+5sp4ai2
+spa4n
+spen4d
+2s5peo
+2sper
+s2phe
+3sph4er
+spho5
+spil4
+sp5ing
+spi2n
+4s3p2i1o
+s4p1ly
+s1p2l2
+s4po2n
+s1p4or4
+4sp4ot
+squal4l
+squ2
+s1r
+2ss
+s1sa2
+ssas3
+s2s5c
+s3sel
+s5sen5g
+s4ses.
+ss1e4s
+s5set
+s1si
+s4s2ie4
+ssi4er
+s4s5i1ly
+s4s1l2
+ss4li
+s4s1n4
+sspen4d4
+ss2t
+ssu1r5a
+s1su
+ssu2r
+ss5w2
+2st.
+s2tag
+s1ta
+s2ta2l
+stam4i
+5st4and
+sta2n
+s4ta4p
+5stat.
+s4t1ed
+stern5i
+s5t2ero
+ste2w
+ste1w5a
+s3th2e
+st2i
+s4ti.
+s5t2i1a
+s1tic
+5s4tick1
+s4t2ie4
+s3tif2
+st3ing
+s2t1in
+5st4ir
+s1tle
+s2tl
+5stock1
+s1to
+sto2m3a
+5stone
+sto2n
+s4top
+3store
+st4r
+s4tra2d
+s1tra
+5stra2tu
+s4tray
+s4tr2id
+4stry
+4st3w4
+s2ty
+1su
+su1al
+su4b3
+su2g3
+su5is
+s2ui2
+suit3
+s4ul
+su2m
+su1m3i
+su2n
+su2r
+4sv
+sw2
+4s1wo2
+s4y
+4sy1c
+3syl
+syn5o
+sy5rin
+1ta
+3ta.
+2tab
+ta5bles2
+tab2l2
+5tab5o5l1iz
+tabol2i
+4t4a2ci
+ta5do
+ta2d
+4ta2f4
+tai5lo
+tai2
+ta2l
+ta5la
+tal5en
+t2ale
+tal3i
+4talk
+tal4lis
+tal1l
+tall2i
+ta5log
+ta5mo
+tan4de
+ta2n
+t4and
+1tan1ta3
+tan1t
+ta5per
+ta5p2l2
+tar4a
+t2a2r
+4tar1c
+4tare
+ta3r2iz
+tar1i
+tas4e
+ta5s4y
+4tat1ic
+ta4tur
+ta2tu
+taun4
+tav4
+2taw
+tax4is
+tax3i
+2t1b
+4tc
+t4ch
+tch5e4t
+tche2
+4t1d
+4te.
+te2ad4i
+tea2d1
+4tea2t
+te1ce4
+5te2c1t
+2t1ed
+t4e5di
+1tee
+teg4
+te5ger4
+te5gi
+3tel.
+tel2i4
+5te2l1s2
+te2ma2
+tem3at
+3ten2a2n
+te1na
+3tenc
+3tend
+4te1nes
+1ten1t
+ten4tag
+ten1ta
+1teo
+te4p
+te5pe
+ter3c
+5ter3d
+1ter1i
+ter5ies
+ter2ie4
+ter3is
+teri5za1
+5t4er3n2it
+ter5v
+4tes.
+4t2e2ss
+t3ess.
+teth5e
+3t4eu
+3tex
+4tey
+2t1f
+4t1g
+2th.
+tha2n4
+th2e
+4thea
+th3eas
+the5a2t
+the3is
+thei2
+3the4t
+th5ic.
+th5i1ca
+4th4il2
+5th4i4nk2
+4t4h1l4
+th5ode
+5thod3ic
+4thoo2
+thor5it
+tho5riz
+2t4h1s2
+1t2i1a
+ti4ab
+ti4a1to
+2ti2b
+4tick1
+t4i1co
+t4ic1u
+5ti2di
+t2id
+3tien
+t2ie4
+tif2
+ti5fy
+2t2ig
+5tigu
+til2l5in4
+til1l
+till2i
+1tim
+4ti4m1p
+tim5ul
+ti2mu
+2t1in
+t2i1na
+3ti2ne.
+t2ine
+3t2ini
+1t2io
+ti5oc
+tion5ee
+tio2n
+5tiq
+ti3sa2
+3t4ise
+ti2s4m
+ti5so
+tis4p
+5tisti1ca
+tis1t2i
+tis1tic
+ti3tl
+ti4u
+1t2iv
+ti1v4a
+1tiz
+ti3za1
+ti3ze4n
+ti2ze
+2tl
+t5la
+tla2n4
+3tle.
+3tled
+3tles.
+tles2
+t5let.
+t5lo
+4t1m
+tme4
+2t1n2
+1to
+to3b
+to5crat
+4to2do4
+2tof
+to2gr
+to5ic
+toi2
+to2ma
+to4m4b
+to3my
+ton4a4l1i
+to2n
+to1n1a
+to3n2at
+4tono
+4tony
+to2ra
+to3r2ie4
+tor5iz
+tos2
+5tour
+tou2
+4tout
+to3w2a2r
+4t1p
+1tra
+t2ra3b
+tra5ch
+tr4a2ci4
+tra2c4it
+trac4te
+tra2c1t
+tr2as4
+tra5ven
+trav5e2s5
+tre5f
+tre4m
+trem5i
+5tr2i3a
+tri5ces
+tr4ice
+5tri3c2i1a
+t4r2i1ci
+4tri4c3s2
+2trim
+tr2i4v
+tro5m4i
+tron5i
+tro2n
+4trony
+tro5phe
+tr2oph
+tro3sp
+tro3v
+tr2u5i2
+tr2us4
+4t1s2
+t4sc
+ts2h4
+t4sw2
+4t3t2
+t4tes
+t5to
+t1tu4
+1tu
+tu1a
+tu3a2r
+tu4b4i
+tud2
+4tue
+4tuf4
+5t2u3i2
+3tum
+tu4nis
+tu1ni
+2t3up.
+3ture
+5turi
+tur3is
+tur5o
+tu5ry
+3t2us
+4tv
+tw4
+4t1wa
+twis4
+twi2
+4t1wo2
+1ty
+4tya
+2tyl
+type3
+ty5ph
+4tz
+t2z4e
+4uab
+uac4
+ua5na
+ua2n
+uan4i
+uar5an1t
+u2a2r
+uara2n
+uar2d
+uar3i
+uar3t
+u1at
+uav4
+ub4e
+u4bel
+u3ber
+u4b2ero
+u1b4i
+u4b5ing
+u3b4le.
+ub2l2
+u3ca
+uci4b
+u1ci
+u2c4it
+ucle3
+u1c4l4
+u3cr
+u3cu
+u4cy
+ud5d4
+ud3er
+ud5est
+udes2
+ude1v4
+u1dic
+ud3ied
+ud2ie4
+ud3ies
+ud5is1
+u5dit
+u4do2n
+u1do
+ud4si
+u2d1s2
+u4du
+u4ene
+ue2n1s4
+uen4te
+uen1t
+uer4il
+uer1i
+3u1fa
+u3f4l2
+ugh3e2n
+ug5in
+2ui2
+uil5iz
+uil1i
+ui4n
+u1ing
+uir4m
+u4ir
+ui1ta4
+u2iv3
+ui4v4er.
+u5j
+4uk
+u1la
+ula5b
+u5lati
+ul2ch4
+u4l1c2
+5ulche2
+ul3der
+u2ld
+ul2de
+ul4e
+u1len
+ul4gi
+u4l1g4
+ul2i
+u5l2i1a
+ul3ing
+ul5is2h
+ul4l2a2r
+ul1l
+ul4li4b
+ull2i
+ul4lis
+4u2l3m
+u1l4o
+4u2l1s2
+uls5e4s
+ul2se
+ul1ti
+u4lt
+ul1tra3
+ul1tr
+4ul1tu2
+u3lu
+ul5ul
+ul5v
+u2m5ab
+u1ma
+um4bi
+u4m1b
+um4b1ly
+umb2l2
+u1mi
+u4m3ing
+umor5o
+u1mo
+umo2r
+u4m2p
+un2at4
+u1na
+u2ne
+un4er
+u1ni
+un4im
+u2n1in
+un5is2h
+un2i3v
+u2n3s4
+un4sw2
+un2t3a4b
+un1t
+un1ta
+un4ter.
+un4tes
+unu4
+un5y
+u4n5z
+u4o4rs2
+u5os
+u1ou2
+u1pe
+upe4r5s2
+u5p2i3a
+up3ing
+upi2n
+u3p2l2
+u4p3p
+upport5
+up1p4or
+up2t5i2b
+u2p1t
+up1tu4
+u1ra
+4ura.
+u4rag
+u4r2as
+ur4be
+ur1b
+ur1c4
+ur1d
+ure5a2t
+ur4fer1
+ur1f
+ur4fr
+u3rif
+uri4fic
+uri1fi
+ur1in
+u3r2i1o
+u1rit
+ur3iz
+ur2l
+url5ing.
+ur4no4
+uros4
+ur4pe
+ur1p
+ur4pi
+urs5er
+u4rs2
+ur2se
+ur5tes
+ur3th2e
+ur1ti4
+ur4t2ie4
+u3ru
+2us
+u5sa2d
+usa2
+u5sa2n
+us4ap
+usc2
+us3ci
+use5a
+u5s2i1a
+u3sic
+us4lin
+us1l2
+us1p
+us5s1l2
+u2ss
+us5tere
+us1t4r
+u2su
+usu2r4
+u2ta4b
+u1ta
+u3tat
+4u4te.
+4utel
+4uten
+uten4i
+4u1t2i
+uti5l2iz
+util1i
+u3t2ine
+u2t1in
+ut3ing
+utio1n5a
+u1t2io
+utio2n
+u4tis
+5u5tiz
+u4t1l
+u2t5of
+u1to
+uto5g
+uto5mat1ic
+uto2ma
+u5to2n
+u4tou2
+u4t1s4
+u3u
+uu4m
+u1v2
+ux1u3
+u2z4e
+1va
+5va.
+2v1a4b
+vac5il
+v4a2ci
+vac3u
+vag4
+va4ge
+va5l2i4e4
+val1i
+val5o
+val1u
+va5mo
+va5niz
+va2n
+va5pi
+var5ied
+v2a2r
+var1i
+var2ie4
+3vat
+4ve.
+4ved
+veg3
+v3el.
+vel3l2i
+vel1l
+ve4lo
+v4e1ly
+ven3om
+v4eno
+v5enue
+v4erd
+5v2e2re.
+v4erel
+v3eren
+ver5enc
+v4eres
+ver3ie4
+ver1i
+vermi4n
+ver3m4
+3ver2se
+ve4r1s2
+ver3th
+v4e2s
+4ves.
+ves4te
+ve4te
+vet3er
+ve4ty
+vi5al1i
+v2i1a
+vi2al
+5vi2a2n
+5vi2de.
+v2id
+5vi2d1ed
+4v3i1den
+5vide4s2
+5vi2di
+v3if
+vi5gn
+v2ig
+v4ik4
+2vil
+5v2il1it
+vil1i
+v3i3l2iz
+v1in
+4vi4na
+v2inc
+v4in5d
+4ving
+vi1o3l
+v2io
+v3io4r
+vi1ou2
+v2i4p
+vi5ro
+v4ir
+vis3it
+vi3so
+vi3su
+4vi1ti
+vit3r
+4vi1ty
+3v2iv
+5vo.
+voi4
+3v2ok
+vo4la
+v5ole
+5vo4l2t
+3vol2v
+vom5i
+vo2r5ab
+vo1ra
+vori4
+vo4ry
+vo4ta
+4vo1tee
+4vv4
+v4y
+w5ab2l2
+2wac
+wa5ger
+wa2g5o
+wait5
+wai2
+w5al.
+wam4
+war4t
+w2a2r
+was4t
+wa1te
+wa5ver
+w1b
+wea5r2ie4
+we2a2r
+wear1i
+we4ath3
+wea2t
+we4d4n4
+weet3
+wee5v
+wel4l
+w1er
+west3
+w3ev
+whi4
+wi2
+wil2
+wil2l5in4
+wil1l
+will2i
+win4de
+w4ind
+win4g
+w4ir4
+3w4ise
+w2ith3
+wiz5
+w4k
+wl4es2
+wl3in
+w4no
+1wo2
+wom1
+wo5v4en
+w5p
+wra4
+wri4
+wri1ta4
+w3s2h
+ws4l2
+ws4pe
+w5s4t
+4wt
+wy4
+x1a
+xac5e
+x4a2go
+xam3
+x4ap
+xas5
+x3c2
+x1e
+xe4cu1to
+xe1cu
+xe3c4ut
+x2ed
+xer4i
+x2e5ro
+x1h
+xhi2
+xh4il5
+xhu4
+x3i
+x2i5a
+xi5c
+xi5di
+x2id
+x4ime
+xi5m2iz
+xim1i
+x3o
+x4ob
+x3p
+xp4an4d
+x1pa
+xpa2n
+xpec1to5
+xpe2c
+xpe2c1t
+x2p2e3d
+x1t2
+x3ti
+x1u
+xu3a
+xx4
+y5ac
+3y2a2r4
+y5at
+y1b
+y1c
+y2ce
+yc5er
+y3ch
+ych4e2
+ycom4
+y1co
+ycot4
+y1d
+y5ee
+y1er
+y4er1f
+yes4
+ye4t
+y5gi
+4y3h
+y1i
+y3la
+ylla5b2l2
+yl1l
+y3lo
+y5lu
+ymbol5
+y4m1b
+yme4
+ym1pa3
+y4m1p
+yn3c4hr4
+yn2ch
+yn5d
+yn5g
+yn5ic
+5ynx
+y1o4
+yo5d
+y4o5g
+yom4
+yo5net
+yo2n
+y4o2n3s2
+y4os
+y4p2ed
+yper5
+yp3i
+y3po
+y4po4c
+yp2ta
+y2p1t
+y5pu
+yra5m
+yr5i3a
+y3ro
+yr4r4
+ys4c
+y3s2e
+ys3i1ca
+y1s3io
+3y1sis
+y4so
+y2ss4
+ys1t
+ys3ta
+ysu2r4
+y1su
+y3thin
+yt3ic
+y1w
+za1
+z5a2b
+z2a2r2
+4zb
+2ze
+ze4n
+ze4p
+z1er
+z2e3ro
+zet4
+2z1i
+z4il
+z4is
+5zl
+4zm
+1zo
+zo4m
+zo5ol
+zoo2
+zte4
+4z1z2
+z4zy
+.as9s8o9c8i8a8te.
+.as1so
+.asso1ci
+.asso3c2i1a
+.as9s8o9c8i8a8t8es.
+.de8c9l8i9n8a9t8i8on.
+.de1c4l4
+.decl4i1na
+.declin2at
+.declina1t2io
+.declinatio2n
+.ob8l8i8g9a9t8o8ry.
+.ob2l2
+.obl2ig
+.obli1ga
+.obliga1to
+.obligato1ry
+.ph8i8l9a8n9t8h8r8o8p8ic.
+.ph4il2
+.phi1la
+.phila2n
+.philan1t
+.philant4hr4
+.philanthrop3ic
+.pr8e8s8e8nt.
+.p3re1s2e
+.presen1t
+.pr8e8s8e8n8ts.
+.presen4t4s2
+.pr8o8j8e8ct.
+.pro5j
+.pro1je
+.proje2c1t
+.pr8o8j8e8c8ts.
+.projec4t1s2
+.re8c9i9p8r8o8c9i9t8y.
+.re1c2i
+.rec2ip
+.recipr2
+.recipr2oc
+.re1cipro1ci
+.recipro2c1it
+.reciproci1ty
+.re9c8o8g9n8i9z8a8n8ce.
+.re1co
+.re2cog
+.rec3ogniz
+.recog1ni
+.recogniza1
+.recogniza2n
+.re8f9o8r9m8a9t8i8on.
+.re1f
+.re1fo
+.refo2r
+.refor1m
+.refor1ma
+.reforma1t2io
+.reformatio2n
+.re8t9r8i9b8u9t8i8on.
+.re3tri
+.retr4ib
+.retri3bu1t2io
+.retrib4u1t2i
+.retributio2n
+.ta9b8le.
+.2tab
+.tab2l2
+.ac8a8d9e9m8y.
+.a1ca
+.aca2d
+.acad4em
+.acade3my
+.ac8a8d9e9m8i8e8s.
+.academ2i4e4
+.ac9c8u9s8a9t8i8v8e.
+.ac3c
+.ac1c2us
+.accusa2
+.accusa1t2iv
+.ac8r8o9n8y8m.
+.acro2n
+.acronym4
+.ac8r8y8l9a8m8i8d8e.
+.acry3la
+.acrylam2id
+.ac8r8y8l9a8m8i8d8e8s.
+.acrylamide4s2
+.ac8r8y8l9a8l8d8e9h8y8d8e.
+.acryla2ld
+.acrylal2de
+.acrylalde1h4
+.acrylaldehy1d
+.ad8d9a9b8l8e.
+.ad1d2a
+.ad2d3a4b
+.addab2l2
+.ad8d9i9b8l8e.
+.addi1b2l2
+.ad8r8e8n9a9l8i8n8e.
+.a1dr
+.adre4
+.a5dren
+.adre1na
+.adrena4l1i
+.adrena1l4ine
+.ae8r8o9s8p8a8c8e.
+.ae4r
+.a2ero
+.aero2s4pa
+.aerospa4ce
+.af9t8e8r9t8h8o8u8g8h8t.
+.afterthou2
+.af9t8e8r9t8h8o8u8g8h8t8s.
+.afterthough4t1s2
+.ag8r8o8n9o9m8i8s8t.
+.a1gr
+.ag4ro
+.agro2n
+.agronom2is
+.ag8r8o8n9o9m8i8s8t8s.
+.agronomis4t1s2
+.al9g8e9b8r8a9i9c8a8l9l8y.
+.a4l1g4
+.alg2e1b
+.alge3br
+.algebr2ai2
+.algebrai1ca
+.algebraical1l
+.algebraical1ly
+.am9p8h8e8t9a9m8i8n8e.
+.a4m1p
+.amphe4t
+.amphe1ta
+.amphetam1in
+.amphetam2ine
+.am9p8h8e8t9a9m8i8n8e8s.
+.amphetami1nes
+.an9a9l8y8s8e.
+.3ana1ly
+.a1na
+.an4a2lys4
+.anal5y3s2e
+.an9a9l8y8s8e8d.
+.analy4s4ed
+.an8a8l8y9s8e8s.
+.analys1e4s
+.an9i8s8o9t8r8o8p9i8c.
+.ani2so
+.anisotrop3ic
+.an9i8s8o9t8r8o8p9i9c8a8l9l8y.
+.anisotropi1ca
+.anisotropical1l
+.anisotropical1ly
+.an9i8s8o8t9r8o9p8i8s8m.
+.anisotropi2s1m
+.an9i8s8o8t9r8o8p8y.
+.anisotropy5
+.an8o8m9a8l8y.
+.ano4
+.anoma5l
+.ano1ma
+.anoma1ly
+.an8o8m9a8l8i8e8s.
+.anomal1i
+.anomal2i4e4
+.an8t8i9d8e8r8i8v9a9t8i8v8e.
+.ant2id
+.antider1i
+.antider2i4v
+.antide4ri1va
+.antideri3vat
+.antider2iva1t2iv
+.an8t8i9d8e8r8i8v9a9t8i8v8e8s.
+.antiderivativ4e2s
+.an8t8i9h8o8l8o9m8o8r9p8h8i8c.
+.anti3h
+.antiholo1mo
+.antiholomo2r
+.antiholomor1p
+.antiholomorp4h4
+.antiholomorph1ic
+.an9t8i8n9o9m8y.
+.an2t1in
+.ant2i1no
+.antino3my
+.an9t8i8n9o9m8i8e8s.
+.antinom2ie4
+.an9t8i9n8u9c8l8e8a8r.
+.antin1u
+.antinucle3
+.antinu1c4l4
+.antinucle2a
+.antinucle2a2r
+.an9t8i9n8u9c8l8e9o8n.
+.antinucleo2n
+.an9t8i9r8e8v9o9l8u9t8i8o8n9a8r8y.
+.ant4ir
+.antirev2
+.antirev5olu
+.antirevo1lut
+.antirevol4u1t2i
+.antirevolutio1n5a
+.antirevolu1t2io
+.antirevolutio2n
+.antirevolution2a2r
+.ap8o8t8h9e9o9s8e8s.
+.ap4ot
+.ap4oth
+.apoth2e
+.apotheos4
+.apotheos1e4s
+.ap8o8t8h9e9o9s8i8s.
+.apotheo1sis
+.ap9p8e8n9d8i8x.
+.a4p1p
+.ap2pe
+.ap3pen
+.ar9c8h8i9m8e9d8e8a8n.
+.ar1c
+.ar2ch
+.archi2med
+.archimedea2n
+.ar9c8h8i9p8e8l9a8g8o.
+.arch2i4p
+.archipe4
+.archipe4la
+.archipela2go
+.ar9c8h8i9p8e8l9a9g8o8s.
+.ar9c8h8i8v8e.
+.arch2i2v
+.ar9c8h8i8v8e8s.
+.archiv4e2s
+.ar9c8h8i8v9i8n8g.
+.archiv1in
+.archi4ving
+.ar9c8h8i8v9i8s8t.
+.ar9c8h8i8v9i8s8t8s.
+.archivis4t1s2
+.ar9c8h8e9t8y8p9a8l.
+.arche2
+.arche4t
+.arche1ty
+.archety1pa
+.archetyp4al
+.ar9c8h8e9t8y8p9i9c8a8l.
+.archetyp3i
+.archetypi1ca
+.ar8c9t8a8n9g8e8n8t.
+.ar2c1t
+.arct5ang
+.arc1ta
+.arcta2n
+.arctan1gen
+.arctangen1t
+.ar8c9t8a8n9g8e8n8t8s.
+.arctangen4t4s2
+.as9s8i8g8n9a9b8l8e.
+.as1si
+.as4sig1n4a
+.ass2ig
+.assig2n1a2b
+.assignab2l2
+.as9s8i8g8n9o8r.
+.assig1no
+.as9s8i8g8n9o8r8s.
+.assigno4rs2
+.as9s8i8s8t9a8n8t9s8h8i8p.
+.as1sis
+.assis1ta
+.assista2n
+.assistan1t
+.assistan4t4s2
+.assistants2h4
+.assistant3sh2i4p
+.as9s8i8s8t9a8n8t9s8h8i8p8s.
+.assistantshi2p1s2
+.as8y8m8p9t8o9m8a8t8i8c.
+.as4y
+.asy4m1p
+.asym2p1t
+.asymp1to
+.asympto2ma
+.asymptomat1ic
+.as9y8m8p9t8o8t9i8c.
+.as8y8n9c8h8r8o9n8o8u8s.
+.asyn3c4hr4
+.asyn2ch
+.asynchro2n
+.asynchro1nou2
+.asynchrono2us
+.at8h9e8r9o9s8c8l8e9r8o9s8i8s.
+.4ath
+.ath2e
+.ath2ero
+.atheros2c
+.atheroscle5
+.atheros1c4l4
+.ath2eroscl4ero
+.atherosclero1sis
+.at9m8o8s9p8h8e8r8e.
+.a4t1m
+.at1mo
+.atmos2
+.atmo3sp
+.atmos2phe
+.atmo3sph4er
+.at9m8o8s9p8h8e8r8e8s.
+.at9t8r8i8b9u8t8e8d.
+.a4t3t2
+.attr4ib
+.attribu2t1ed
+.at9t8r8i8b9u8t9a8b8l8e.
+.attri4bu1ta
+.attribu2ta4b
+.attributab2l2
+.au9t8o9m8a9t8i8o8n.
+.au1to
+.auto2ma
+.automa1t2io
+.automatio2n
+.au9t8o8m9a9t8o8n.
+.automa1to
+.automato2n
+.au9t8o8m9a9t8a.
+.automa2ta
+.au9t8o9n8u8m9b8e8r9i8n8g.
+.au5to2n
+.auton5um
+.autonu4m1b
+.autonumber1i
+.autonumberin4g
+.au9t8o8n9o9m8o8u8s.
+.au4tono
+.autono4mo
+.autono3mo2us
+.autonomou2
+.au8t8o9r8o8u8n8d9i8n8g.
+.autorou2
+.autoroun2d
+.autoround1in
+.av9o8i8r9d8u9p8o8i8s.
+.avoi4
+.avo4ir
+.avoir1du
+.avoir4dup
+.avoirdupoi2
+.ba8n8d9l8e8a8d8e8r.
+.b4and
+.ban1dl
+.bandle2a
+.bandlea2d1
+.ba8n8d9l8e8a8d8e8r8s.
+.bandleade4r5s2
+.ba8n8k9r8u8p8t.
+.ba4nk2
+.bankru2p1t
+.ba8n8k9r8u8p8t9c8y.
+.bankrup4tc
+.bankrupt1cy
+.ba8n8k9r8u8p8t9c8i8e8s.
+.bankrupt1ci
+.bankruptc2ie4
+.ba8r9o8n8i8e8s.
+.b2a2r
+.ba5roni
+.baro2n
+.baron2ie4
+.ba8s8e9l8i8n8e9s8k8i8p.
+.basel2i
+.base1l4ine
+.baseli1nes
+.baselinesk2
+.baselinesk1i
+.baselinesk2i4p
+.ba9t8h8y8m9e9t8r8y.
+.1bat
+.b4ath
+.bathyme4
+.bathym4etr
+.bathyme3try
+.ba8t8h8y9s8c8a8p8h8e.
+.bathy2s
+.bathys4c
+.bathysca4p
+.bathys1ca
+.be8a8n9i8e8s.
+.bea2n
+.bea3nies
+.bean2ie4
+.be9h8a8v9i8o8u8r.
+.be1h4
+.behav1i
+.behavi1ou2
+.behav2io
+.behavi4our
+.be9h8a8v9i8o8u8r8s.
+.behaviou4rs2
+.be8v8i8e8s.
+.be1vi
+.bev2ie4
+.bi8b9l8i9o8g9r8a9p8h8y9s8t8y8l8e.
+.bi2b
+.bi1b2l2
+.bib3li
+.bibli5og
+.bibl2io
+.biblio2gr
+.biblio4g3ra1phy
+.bibliography2s
+.bibliographys1t
+.bibliographys2ty
+.bibliographys2tyl
+.bi9d8i8f9f8e8r9e8n9t8i8a8l.
+.b2i4d
+.bi2di
+.bid1if
+.bidi4f1f
+.bidiffer1
+.bidiffer3en1t
+.bidifferent2i
+.bidifferen1t2i1a
+.bidifferenti2al
+.bi8g9g8e8s8t.
+.b2ig
+.bi4g1g2
+.big2ge
+.bi8l8l9a8b8l8e.
+.1bil
+.bill5ab
+.bil1l
+.billab2l2
+.bi8o9m8a8t8h9e9m8a8t9i8c8s.
+.b2io
+.bio4m
+.bio1ma
+.biom4ath3
+.biomath5em
+.biomath2e
+.biomathe1ma
+.biomathemat1ic
+.biomathemati4c3s2
+.bi8o9m8e8d9i9c8a8l.
+.bio2me
+.bio2med
+.biom4edi
+.biomed3i1ca
+.bi8o9m8e8d9i9c8i8n8e.
+.biomed2i1ci
+.biomedi2cin
+.biomedic2ine
+.bi8o9r8h8y8t8h8m8s.
+.biorh4
+.biorhyt4h1m
+.biorhyth4m1s2
+.bi8t9m8a8p.
+.bi2t
+.bi4t1m
+.bit1ma
+.bit4map
+.bi8t9m8a8p8s.
+.bitma2p1s2
+.bl8a8n8d9e8r.
+.b2l2
+.b3l4and
+.bla2n
+.blan1de
+.bl8a8n8d9e8s8t.
+.blande4s2
+.bl8i8n8d9e8r.
+.bl4ind
+.blin1de
+.bl8o8n8d8e8s.
+.b4lo
+.blo2n
+.bl2ond
+.blon1de
+.blondes2
+.bl8u8e9p8r8i8n8t.
+.bluepr2
+.blueprin4t3
+.bl8u8e9p8r8i8n8t8s.
+.blueprin4t4s2
+.bo9l8o8m9e9t8e8r.
+.bolo2me
+.bolo4met
+.bolome1te
+.bo8o8k9s8e8l8l9e8r.
+.3boo2
+.bo2o4k
+.boo4k1s2
+.booksel1l
+.booksel2le
+.bo8o8k9s8e8l8l9e8r8s.
+.bookselle4r1s2
+.bo8o8l9e8a8n.
+.boole2a
+.boolea2n
+.bo8o8l9e8a8n8s.
+.boolea2n1s2
+.bo8r9n8o9l8o8g9i9c8a8l.
+.borno4
+.borno3log1ic
+.bornologi1ca
+.bo8t9u9l8i8s8m.
+.bo1tu
+.botul2i
+.botuli2s1m
+.br8u8s8q8u8e8r.
+.br2us
+.brusqu2
+.brus3quer
+.bu8f9f8e8r.
+.buf4fer1
+.bu4f1f
+.bu8f9f8e8r8s.
+.buffe4r1s2
+.bu8s8i8e8r.
+.bus5ie4
+.b2us
+.bu8s8i8e8s8t.
+.busi1est
+.bu8s8s8i8n8g.
+.bu2ss
+.bus1si
+.bus2s1in
+.buss3ing
+.bu8t8t8e8d.
+.but2t1ed
+.bu8z8z9w8o8r8d.
+.bu4z1z2
+.buzz1wo2
+.bu8z8z9w8o8r8d8s.
+.buzzwor2d1s2
+.ca9c8o8p8h9o9n8y.
+.ca1co
+.cac2oph
+.cacopho5ny
+.cacopho2n
+.ca9c8o8p8h9o9n8i8e8s.
+.caco5phoni
+.cacophon2ie4
+.ca8l8l9e8r.
+.cal1l
+.cal2le
+.ca8l8l9e8r8s.
+.calle4r1s2
+.ca8m9e8r8a9m8e8n.
+.cam5er1a
+.camera1men
+.ca8r8t9w8h8e8e8l.
+.cartw4
+.ca8r8t9w8h8e8e8l8s.
+.cartwhee2l1s2
+.ca9t8a8r8r8h8s.
+.ca2ta
+.cat2a2r
+.catar1r4
+.catarrh4
+.catarr4h1s2
+.ca8t9a9s8t8r8o8p8h9i8c.
+.catas1t4r
+.catastr2oph
+.catastroph1ic
+.ca8t9a9s8t8r8o8p8h9i9c8a8l8l8y.
+.catastrophi1ca
+.catastrophical1l
+.catastrophical1ly
+.ca8t9e9n8o8i8d.
+.cat4eno
+.catenoi2
+.cateno2id
+.ca8t9e9n8o8i8d8s.
+.catenoi2d1s2
+.ca8u9l8i9f8l8o8w9e8r.
+.cau4l2
+.caul2i
+.cauli4f4l2
+.cauliflow1er
+.ch8a8p9a8r9r8a8l.
+.chap2a2r4
+.cha1pa
+.chapar1r4
+.ch8a8r9t8r8e8u8s8e.
+.ch2a2r
+.chartr4eu2
+.chartre2us4
+.ch8e8m8o9t8h8e8r9a8p8y.
+.che2
+.che1mo
+.chem4oth3
+.chemoth2e
+.chemoth4er1a
+.chemothera3p
+.ch8e8m8o9t8h8e8r9a9p8i8e8s.
+.chemotherap2ie4
+.ch8l8o8r8o9m8e8t8h9a8n8e.
+.c4h1l4
+.ch2lo
+.chloro2me
+.chloro4met
+.chlorometha2n4
+.ch8l8o8r8o9m8e8t8h9a8n8e8s.
+.chlorometha1nes
+.ch8o9l8e8s9t8e8r8i8c.
+.3cho2
+.c3hol4e
+.choles2
+.choles1ter1i
+.ci8g9a9r8e8t8t8e.
+.c2ig
+.ci1ga
+.cig2a2r
+.cigare4t3t2
+.ci8g9a9r8e8t8t8e8s.
+.cigaret4tes
+.ci8n8q8u8e9f8o8i8l.
+.2cin
+.cin1q
+.cinqu2
+.cinque1f
+.cinque1fo
+.cinquefoi2
+.co9a8s8s8o9c8i8a9t8i8v8e.
+.c4oa
+.coa2ss
+.coas1so
+.coasso1ci
+.coasso3c2i1a
+.coassoci4a1t2iv
+.co9g8n8a8c.
+.2cog
+.cog1n4a
+.co9g8n8a8c8s.
+.cogna4c3s2
+.co9k8e8r9n8e8l.
+.c2ok
+.cok1er
+.coker3nel
+.co9k8e8r9n8e8l8s.
+.cokerne2l1s2
+.co8l9l8i8n9e8a9t8i8o8n.
+.col1l
+.coll2i
+.col2lin4
+.col1l4ine
+.collin3ea
+.collinea2t
+.collinea1t2io
+.collineatio2n
+.co8l9u8m8n8s.
+.colu4m1n
+.colum2n1s2
+.co8m9p8a8r9a8n8d.
+.co4m1p
+.compara5
+.com1pa
+.comp2a2r
+.compara2n
+.compar4and
+.co8m9p8a8r9a8n8d8s.
+.comparan2d1s2
+.co8m9p8e8n9d8i8u8m.
+.compendi1u
+.co8m9p8o9n8e8n8t9w8i8s8e.
+.compo2n
+.compo3nen
+.componen1t
+.componentw4
+.componentwis4
+.componentwi2
+.component3w4ise
+.co8m8p9t8r8o8l9l8e8r.
+.comp4tr
+.com2p1t
+.comptrol1l
+.comptrol2le
+.co8m8p9t8r8o8l9l8e8r8s.
+.comptrolle4r1s2
+.co8n9f8o8r8m9a8b8l8e.
+.co2n
+.con3f
+.con1fo
+.confo2r
+.confor1m
+.confor1ma
+.confor2mab
+.conformab2l2
+.co8n9f8o8r8m9i8s8t.
+.confor2mi
+.conform2is
+.co8n9f8o8r8m9i8s8t8s.
+.conformis4t1s2
+.co8n9f8o8r8m9i8t8y.
+.confor3mit
+.conformi1ty
+.co8n9g8r8e8s8s.
+.con3g
+.con1gr
+.congr2e2ss
+.co8n9g8r8e8s8s8e8s.
+.congress1e4s
+.co8n9t8r8i8b9u8t8e.
+.con5t
+.contr4ib
+.co8n9t8r8i8b9u8t8e8s.
+.co8n9t8r8i8b9u8t8e8d.
+.contribu2t1ed
+.co9r8e9l8a9t8i8o8n.
+.core1la
+.corela1t2io
+.corelatio2n
+.co9r8e9l8a9t8i8o8n8s.
+.corelatio2n3s2
+.co9r8e9l8i9g8i8o8n9i8s8t.
+.core1l2i
+.corel2ig
+.corel4igi
+.coreli5g2io
+.coreligion3i
+.coreligio2n
+.coreligion1is
+.co9r8e9l8i9g8i8o8n9i8s8t8s.
+.coreligionis4t1s2
+.co9r8e9o8p9s8i8s.
+.core1o
+.coreo2p1s2
+.coreop1sis
+.co9r8e9s8p8o8n9d8e8n8t.
+.core1sp
+.cores4po2n
+.coresp2ond
+.corespon1de
+.corespon1den
+.coresponden1t
+.co9r8e9s8p8o8n9d8e8n8t8s.
+.coresponden4t4s2
+.co9s8e9c8a8n8t.
+.cos4e
+.cose1ca
+.coseca2n
+.cosecan1t
+.co9t8a8n9g8e8n8t.
+.co4ta2n
+.co1ta
+.cot2ang
+.cotan1gen
+.cotangen1t
+.co8u8r9s8e8s.
+.cou2
+.cou4rs2
+.cour2se
+.cours3e4s
+.co9w8o8r8k9e8r.
+.co4wo2
+.cowork1er
+.co9w8o8r8k9e8r8s.
+.coworke4r1s2
+.cr8a8n8k9c8a8s8e.
+.cra2n
+.cra4nk2
+.crank1ca
+.cr8a8n8k9s8h8a8f8t.
+.cran4k1s2
+.cranks2h
+.cranksha2f
+.cranksha2ft
+.cr8o8c9o9d8i8l8e.
+.cr2oc
+.cro4cod
+.cro1co
+.cr8o8c9o9d8i8l8e8s.
+.crocodiles2
+.cr8o8s8s9h8a8t8c8h.
+.cro2s4s
+.cross2h
+.crossha4tc
+.crosshat4ch
+.cr8o8s8s9h8a8t8c8h8e8d.
+.crosshatche2
+.crosshat4ch4ed
+.cr8o8s8s9o8v8e8r.
+.cros1so
+.cros4sov
+.cr8y8p9t8o9g8r8a8m.
+.cry2p1t
+.cryp1to
+.crypto2gr
+.cr8y8p9t8o9g8r8a8m8s.
+.cryptogra4m1s2
+.cu8f8f9l8i8n8k.
+.c4uf
+.cu4f1f
+.cuff4l2
+.cufflin4
+.cuffl4i4nk2
+.cu8f8f9l8i8n8k8s.
+.cufflin4k1s2
+.cu9n8e8i9f8o8r8m.
+.3cun
+.cu2ne
+.cunei2
+.cunei1fo
+.cuneifo2r
+.cuneifor1m
+.cu8s9t8o8m9i8z9a9b8l8e.
+.1c2us
+.cus1to
+.custom2iz
+.customiza1
+.customiz5a2b
+.customizab2l2
+.cu8s9t8o8m9i8z8e.
+.customi2ze
+.cu8s9t8o8m9i8z8e8s.
+.cu8s9t8o8m9i8z8e8d.
+.da8c8h8s9h8u8n8d.
+.1d2a
+.da2ch4
+.dac4h1s2
+.dach4s2h
+.da8m9s8e8l9f8l8y.
+.da2m2
+.da4m1s2
+.dam5se2l2f
+.damself4l2
+.damself2ly5
+.da8m9s8e8l9f8l8i8e8s.
+.damselfl2ie4
+.da8c8t8y8l9o9g8r8a8m.
+.da2c1t
+.dac1ty
+.dac2tyl
+.dacty3lo
+.dactylo1gr
+.da8c8t8y8l9o9g8r8a8p8h.
+.da8t8a9b8a8s8e.
+.3dat
+.da2ta
+.da2tab
+.da8t8a9b8a8s8e8s.
+.databas1e4s
+.da8t8a9p8a8t8h.
+.dat5ap
+.datap5at
+.data1pa
+.datap4ath
+.da8t8a9p8a8t8h8s.
+.datapa2t4h1s2
+.da8t8e9s8t8a8m8p.
+.dat3est
+.dates1ta
+.datesta4m1p
+.da8t8e9s8t8a8m8p8s.
+.datestam2p1s2
+.de9c8l8a8r9a8b8l8e.
+.de4cl2a2r
+.decla2rab
+.declarab2l2
+.de9f8i8n9i9t8i8v8e.
+.de1f
+.de1fi
+.de2fin
+.def2ini
+.defin2it
+.defini1ti
+.defini1t2iv
+.de9l8e8c9t8a9b8l8e.
+.d5elec
+.dele2c1t
+.delec2ta4b
+.delec1ta
+.delectab2l2
+.de8m8i9s8e8m8i9q8u8a9v8e8r.
+.de4m2is
+.dem4ise
+.demisemi3qua
+.demisemiqu2
+.demisemiqua5v4
+.de8m8i9s8e8m8i9q8u8a9v8e8r8s.
+.demisemiquave4r1s2
+.de9m8o8c9r8a9t8i8s8m.
+.de4mocr
+.democrati2s4m
+.de8m8o8s.
+.demos2
+.de9r8i8v9a9t8i8v8e.
+.der2i4v
+.de4ri1va
+.deri3vat
+.der2iva1t2iv
+.de9r8i8v9a9t8i8v8e8s.
+.derivativ4e2s
+.di8a9l8e8c9t8i8c.
+.1d4i3a
+.di2al
+.di2ale
+.diale2c1t
+.di8a9l8e8c9t8i8c8s.
+.dialecti4c3s2
+.di8a9l8e8c9t8i9c8i8a8n.
+.dialect2i1ci
+.d2i1alecti3c2i1a
+.dialectici2a2n
+.di8a9l8e8c9t8i9c8i8a8n8s.
+.dialecticia2n1s2
+.di9c8h8l8o8r8o9m8e8t8h9a8n8e.
+.d4i2ch
+.dic4h1l4
+.dich2lo
+.dichloro2me
+.dichloro4met
+.dichlorometha2n4
+.di8f9f8r8a8c8t.
+.d1if
+.dif4fr
+.di4f1f
+.diffra2c1t
+.di8f9f8r8a8c8t8s.
+.diffrac4t1s2
+.di8f9f8r8a8c9t8i8o8n.
+.diffrac1t2io
+.diffractio2n
+.di8f9f8r8a8c9t8i8o8n8s.
+.diffractio2n3s2
+.di8r8e8r.
+.d4ir2
+.di1re
+.dir1er4
+.di8r8e9n8e8s8s.
+.dire1nes
+.diren2e2ss
+.di8s9p8a8r9a8n8d.
+.dis1
+.dis1p
+.di2s1pa
+.disp2a2r
+.dispara2n
+.dispar4and
+.di8s9p8a8r9a8n8d8s.
+.disparan2d1s2
+.di8s9t8r8a8u8g8h8t9l8y.
+.d4is3t
+.dist4r
+.dis1tra
+.distraugh3
+.distraugh2tl
+.distraught1ly
+.di8s9t8r8i8b9u8t8e.
+.distr4ib
+.di8s9t8r8i8b9u8t8e8s.
+.di8s9t8r8i8b9u8t8e8d.
+.distribu2t1ed
+.do8u9b8l8e9s8p8a8c8e.
+.dou2
+.dou3b2l2
+.dou5ble1sp
+.doubles2
+.double2s1pa
+.doublespa4ce
+.do8u9b8l8e9s8p8a8c9i8n8g.
+.doublesp4a2ci
+.doublespa2c1in
+.doublespac1ing
+.do8l8l9i8s8h.
+.dol1l
+.doll2i
+.dollis2h
+.dr8i8f8t9a8g8e.
+.1dr
+.dr4i2ft
+.drif1ta
+.dr8i8v9e8r8s.
+.dr2iv
+.drive4r1s2
+.dr8o8m9e9d8a8r8y.
+.dro2me
+.dro2med
+.drom2e2d2a
+.drome4dary
+.dromed2a2r
+.dr8o8m9e9d8a8r8i8e8s.
+.dromedar1i
+.dromedar2ie4
+.du9o8p9o9l8i8s8t.
+.duopol2i
+.du9o8p9o9l8i8s8t8s.
+.duopolis4t1s2
+.du9o8p9o8l8y.
+.duopo2ly
+.dy8s9l8e8x8i8a.
+.d2y
+.dys1l2
+.dys2le
+.dyslex3i
+.dyslex2i5a
+.dy8s9l8e8c9t8i8c.
+.dysle2c1t
+.ea8s8t9e8n8d9e8r8s.
+.east3
+.eas3ten
+.eas3tend
+.easten1de
+.eastende4r5s2
+.ec8o9n8o8m9i8c8s.
+.e1co
+.eco2n
+.eco3nomic
+.economi4c3s2
+.ec8o8n9o9m8i8s8t.
+.econom2is
+.ec8o8n9o9m8i8s8t8s.
+.economis4t1s2
+.ei9g8e8n9c8l8a8s8s.
+.ei2
+.e2ig2
+.ei1gen
+.eigen1c4l4
+.eigencla2ss
+.ei9g8e8n9c8l8a8s8s8e8s.
+.eigenclass1e4s
+.ei9g8e8n9v8a8l9u8e.
+.eigen1v2
+.eigen1va
+.eigenval1u
+.ei9g8e8n9v8a8l9u8e8s.
+.el8e8c8t8r8o9m8e8c8h8a8n9i9c8a8l.
+.5elec
+.ele2c1t
+.electro2me
+.electrome2ch
+.electrome5cha4n1ic
+.electromecha2n
+.electromechani1ca
+.el8e8c8t8r8o9m8e8c8h8a8n8o9a8c8o8u8s8t8i8c.
+.electromechano4
+.electromechan4oa
+.electromechanoa1co
+.electromechanoacou2
+.electromechanoaco2us
+.electromechanoacoust2i
+.electromechanoacous1tic
+.el8i8t9i8s8t.
+.el2i
+.el1it
+.eli1ti
+.el4itis
+.el8i8t9i8s8t8s.
+.elitis4t1s2
+.en9t8r8e9p8r8e9n8e8u8r.
+.en1t
+.entrepr2
+.entrepren4eu
+.en9t8r8e9p8r8e9n8e8u8r9i8a8l.
+.entrepreneur2i3a
+.entrepreneuri2al
+.ep9i9n8e8p8h9r8i8n8e.
+.epi2n
+.ep2ine
+.epinep4hr4
+.ep2inephr2in4e
+.eq8u8i9v8a8r8i9a8n8t.
+.equ2iv3
+.equi1va
+.equiv2a2r
+.equivar1i
+.equivar3i2a2n
+.equivar2i3a
+.equivar4ian4t
+.eq8u8i9v8a8r8i9a8n8c8e.
+.equivar4ianc
+.et8h9a8n8e.
+.etha2n4
+.et8h9y8l9e8n8e.
+.ev8e8r9s8i9b8l8e.
+.ev1er
+.eve4r1s2
+.ever1si
+.ever4si4b
+.eversi1b2l2
+.ev8e8r8t.
+.ev8e8r8t8s.
+.ever4t1s2
+.ev8e8r8t9e8d.
+.ever2t1ed
+.ev8e8r8t9i8n8g.
+.ever1ti
+.ever2t1in
+.ex9q8u8i8s9i8t8e.
+.exqu2
+.exq2ui2
+.exquis2ite
+.ex9t8r8a9o8r9d8i9n8a8r8y.
+.ex1t2
+.ex1tra
+.extr4ao
+.extraord2i
+.extraord1in4
+.extraor1di1na
+.extraordin2a2r
+.fa8l8l9i8n8g.
+.1fa
+.fal1l
+.fall2i
+.fal2lin4
+.fe8r8m8i9o8n8s.
+.fer1
+.fer3m4
+.fer4m2io
+.fermio2n
+.fermio2n3s2
+.fi9n8i8t8e9l8y.
+.1fi
+.2fin
+.f2ini
+.fin2it
+.fin2ite
+.finite1ly
+.fl8a9g8e8l9l8u8m.
+.f4l2
+.flag5el1l
+.fl8a9g8e8l9l8a.
+.flag4ella
+.fl8a8m9m8a9b8l8e8s.
+.flam1m
+.flam1ma
+.flam2mab
+.flammab2l2
+.flammables2
+.fl8e8d8g9l8i8n8g.
+.fledgl2
+.fl8o8w9c8h8a8r8t.
+.flow2ch
+.flowch2a2r
+.fl8o8w9c8h8a8r8t8s.
+.flowchar4t1s2
+.fl8u8o8r8o9c8a8r9b8o8n.
+.flu3o
+.fluo3r
+.fluor2oc
+.fluoro1ca
+.fluoroc2a2r
+.fluorocar1b
+.fluorocarb4o
+.fluorocarbo2n
+.fo8r9m8i9d8a9b8l8e.
+.for2mi
+.formi1d4a
+.form2id
+.formi2d3a4b
+.formidab2l2
+.fo8r9m8i9d8a9b8l8y.
+.formidab1ly
+.fo8r9s8y8t8h9i8a.
+.fo4rs2
+.fors4y
+.forsyth2i1a
+.fo8r8t8h9r8i8g8h8t.
+.fort4hr4
+.forthr2ig
+.fr8e8e9l8o8a8d8e8r.
+.freel4oa
+.freeloa2d3
+.fr8e8e9l8o8a8d8e8r8s.
+.freeloade4r5s2
+.fr8i8e8n8d9l8i8e8r.
+.fri2
+.fr2ie4
+.friendl2ie4
+.fr8i9v8o8l9i8t8y.
+.fr2iv
+.frivol2i
+.frivol1it
+.frivoli1ty
+.fr8i9v8o8l9i9t8i8e8s.
+.frivoli1ti
+.frivolit2ie4
+.fr8i8v9o9l8o8u8s.
+.frivolou2
+.frivolo2us
+.ga9l8a8c9t8i8c.
+.gala2c1t
+.ga8l9a8x8y.
+.ga8l9a8x9i8e8s.
+.galax3i
+.galax2ie4
+.ga8s9o8m9e9t8e8r.
+.ga1so
+.ga3som
+.gaso2me
+.gaso4met
+.gasome1te
+.ge9o9d8e8s9i8c.
+.geodes2
+.geode1si
+.geode2sic
+.ge9o9d8e8t9i8c.
+.geode1t
+.geodet1ic
+.ge8o9m8e8t9r8i8c.
+.ge3om
+.geo2me
+.geo4met
+.geom4etr
+.geo5met3ric
+.ge8o9m8e8t9r8i8c8s.
+.geome4tri4c3s2
+.ge9o9s8t8r8o8p8h8i8c.
+.geos4
+.geost4r
+.geostr2oph
+.geostroph1ic
+.ge8o9t8h8e8r9m8a8l.
+.ge4ot
+.ge4oth
+.geoth2e
+.geother3m4
+.geother1ma
+.ge9o8t9r8o9p8i8s8m.
+.geotropi2s1m
+.gn8o9m8o8n.
+.g1no
+.gno4mo
+.gno4mo2n
+.gn8o9m8o8n8s.
+.gnomo2n3s2
+.gr8a8n8d9u8n8c8l8e.
+.1gr
+.gra2n2
+.gr4and
+.gran1du
+.grandu4n
+.grandun1c4l4
+.gr8a8n8d9u8n8c8l8e8s.
+.granduncles2
+.gr8i8e8v9a8n8c8e.
+.gr2ie4
+.grie1va
+.grieva2n
+.gr8i8e8v9a8n8c8e8s.
+.gr8i8e8v9o8u8s.
+.grievou2
+.grievo2us
+.gr8i8e8v9o8u8s9l8y.
+.grievous1l2
+.grievous1ly
+.ha8i8r9s8t8y8l8e.
+.hai2
+.ha4ir
+.hai4rs2
+.hairs2ty
+.hairs2tyl
+.ha8i8r9s8t8y8l8e8s.
+.hairstyles2
+.ha8i8r9s8t8y8l9i8s8t.
+.ha8i8r9s8t8y8l9i8s8t8s.
+.hairstylis4t1s2
+.ha8l8f9s8p8a8c8e.
+.ha2lf
+.hal2f3s
+.half2s1pa
+.halfspa4ce
+.ha8l8f9s8p8a8c8e8s.
+.ha8l8f9w8a8y.
+.ha8r9b8i8n9g8e8r.
+.h2a2r
+.har1b
+.harbi2
+.har2bin
+.harb4inge
+.ha8r9b8i8n9g8e8r8s.
+.harbinge4r1s2
+.ha8r9l8e9q8u8i8n.
+.har4le4
+.har1l
+.harle1q
+.harlequ2
+.harleq2ui2
+.harlequi4n
+.ha8r9l8e9q8u8i8n8s.
+.harlequ2i2n1s2
+.ha8t8c8h9e8r8i8e8s.
+.ha4tc
+.hat4ch
+.hatche2
+.hatcher1i
+.hatcher2ie4
+.he8m8i9d8e8m8i9s8e8m8i9q8u8a9v8e8r.
+.hem2id
+.hemid4em
+.hemide4m2is
+.hemidem4ise
+.hemidemisemi3qua
+.hemidemisemiqu2
+.hemidemisemiqua5v4
+.he8m8i9d8e8m8i9s8e8m8i9q8u8a9v8e8r8s.
+.hemidemisemiquave4r1s2
+.he9m8o9g8l8o9b8i8n.
+.hemo4g
+.he1mo
+.hemo4gl2
+.hemo3glo
+.hemoglo1bi
+.hemoglo2bin
+.he9m8o9p8h8i8l9i8a.
+.hem2oph
+.hemoph4il2
+.hemophil1i
+.hemophil3i1a
+.he9m8o9p8h8i8l9i8a8c.
+.he9m8o9p8h8i8l9i8a8c8s.
+.hemophilia4c3s2
+.he8m8o9r8h8e9o8l9o8g8y.
+.hemo2r
+.hemorh4
+.hemorhe3ol
+.hemorheol1o1gy
+.he9p8a8t9i8c.
+.hep5
+.he2pa
+.hepat1ic
+.he8r9m8a8p8h9r8o9d8i8t8e.
+.her3m4
+.her1ma
+.her4map
+.hermap4hr4
+.hermaphrod2ite
+.he8r9m8a8p8h9r8o9d8i8t9i8c.
+.hermaphrod2i1ti
+.hermaphrod4i2tic
+.he9r8o8e8s.
+.hero4e
+.he8x8a9d8e8c9i9m8a8l.
+.hex1a
+.hexa2d
+.hexade1c2i
+.hexade2c3im
+.hexadeci1ma
+.ho9l8o9n8o9m8y.
+.holo2n
+.holon3o3my
+.ho9m8e8o9m8o8r9p8h8i8c.
+.ho2me3
+.homeo1mo
+.homeomo2r
+.homeomor1p
+.homeomorp4h4
+.homeomorph1ic
+.ho9m8e8o9m8o8r9p8h8i8s8m.
+.homeomorphi2s1m
+.ho9m8o9t8h8e8t8i8c.
+.ho1mo
+.hom4oth3
+.homoth2e
+.homo3the4t
+.homothet1ic
+.ho8r8s8e9r8a8d9i8s8h.
+.hor4se
+.ho4rs2
+.horser1a
+.horsera2d
+.horser2adi
+.horseradis1
+.horseradis2h
+.ho8t9b8e8d.
+.ho2t1b
+.hot4be2d
+.ho8t9b8e8d8s.
+.hotbe2d1s2
+.hy9d8r8o9t8h8e8r9m8a8l.
+.hy1d
+.hy1dr
+.hydro4th2e
+.hydr4oth
+.hydrother3m4
+.hydrother1ma
+.hy9p8o9t8h8a8l9a9m8u8s.
+.hy3po
+.hyp4ot
+.hyp4oth
+.hypotha3la
+.hypothala3m
+.hypothala1mu
+.hypothalam2us
+.id8e8a8l8s.
+.ide3a4l
+.idea2l1s2
+.id8e8o9g8r8a8p8h8s.
+.ideo2g
+.ideo1gr
+.ideogra4p4h1s2
+.id8i8o9s8y8n9c8r8a8s8y.
+.i2di
+.i1d3io
+.idi4os
+.idios4y
+.idiosyn1cr
+.idiosyncr2as
+.idiosyncras4y
+.id8i8o9s8y8n9c8r8a9s8i8e8s.
+.idiosyncras2ie4
+.id8i8o9s8y8n9c8r8a8t8i8c.
+.idiosyn5crat1ic
+.id8i8o9s8y8n9c8r8a8t9i9c8a8l9l8y.
+.idiosyncrati1ca
+.idiosyncratical1l
+.idiosyncratical1ly
+.ig9n8i8t9e8r.
+.2ig
+.ig1ni
+.ign2it
+.ign2ite
+.ig9n8i8t9e8r8s.
+.ignite4r1s2
+.ig9n8i9t8o8r.
+.ign3itor
+.igni1to
+.ig8n8o8r8e9s8p8a8c8e8s.
+.ig1no
+.ignore1sp
+.ignore2s1pa
+.ignorespa4ce
+.im9p8e8d9a8n8c8e.
+.im2p2ed
+.imp2e2d2a
+.impeda2n
+.im9p8e8d9a8n8c8e8s.
+.in9d8u9b8i9t8a9b8l8e.
+.4ind
+.in1du
+.indu1b4i
+.indubi2t
+.indubi1ta
+.indubi2tab
+.indubitab2l2
+.in9f8i8n9i8t8e9l8y.
+.in3f
+.in1fi
+.in2fin
+.inf2ini
+.infin2it
+.infin2ite
+.infinite1ly
+.in9f8i8n9i9t8e8s9i9m8a8l.
+.infinit4es
+.infinite1si
+.infinite2s5im
+.infinitesi1ma
+.in9f8r8a9s8t8r8u8c9t8u8r8e.
+.infr2as
+.infras1t4r
+.infrastru2c1t
+.infrastructu4r
+.infrastruc1tu
+.infrastruc3ture
+.in9f8r8a9s8t8r8u8c9t8u8r8e8s.
+.in9s8t8a8l8l9e8r.
+.ins2ta2l
+.ins1ta
+.instal1l
+.instal2le
+.in9s8t8a8l8l9e8r8s.
+.installe4r1s2
+.in9t8e8r9d8i8s9c8i9p8l8i9n8a8r8y.
+.in1t
+.in5ter3d
+.interd2i
+.interdis1
+.interd2is1c
+.interdis1ci
+.interdisc2ip
+.interdisci1p2l2
+.interdiscipli4n
+.interdiscipl4i1na
+.interdisciplin2a2r
+.in9t8e8r9g8a9l8a8c9t8i8c.
+.interg2
+.inter1ga
+.intergala2c1t
+.in9u8t8i8l8e.
+.in1u
+.in4u1t2i
+.in9u8t8i8l9i9t8y.
+.inutil1i
+.inut2il1it
+.inutili1ty
+.ir9r8e9d8u8c9i8b8l8e.
+.ir2r2ed
+.irre1du
+.irredu2c
+.irreduci4b
+.irredu1ci
+.irreduci1b2l2
+.ir9r8e9d8u8c9i8b8l8y.
+.irreducib1ly
+.ir9r8e8v9o9c8a9b8l8e.
+.irrev2
+.irre5voc
+.irrevo1ca
+.irrevoca1b2l2
+.is8o8t9r8o8p8y.
+.i2so
+.isotropy5
+.is8o9t8r8o8p9i8c.
+.isotrop3ic
+.it8i8n9e8r9a8r8y.
+.i1ti
+.i2t1in
+.it2ine
+.itin4er4a2r
+.itin1er
+.itiner1a
+.it8i8n9e8r9a8r9i8e8s.
+.itinerar1i
+.itinerar2ie4
+.je9r8e9m8i9a8d8s.
+.1je
+.jerem2i3a
+.jeremia2d
+.jeremia2d1s2
+.ke8y9n8o8t8e.
+.ke8y9n8o8t8e8s.
+.keyno4tes
+.ke8y9s8t8r8o8k8e.
+.keys4
+.keys1t
+.keyst4r
+.keystr2ok2
+.ke8y9s8t8r8o8k8e8s.
+.keystrokes4
+.ki8l8n9i8n8g.
+.k1i
+.k4i2l1n2
+.kiln1in
+.kilnin4g
+.la8c9i9e8s8t.
+.l4a2ci4
+.la3c2ie4
+.laci1est
+.la8m9e8n9t8a9b8l8e.
+.la1men
+.la3men1t
+.lamen2ta4b
+.lamen1ta
+.lamentab2l2
+.la8n8d9s8c8a8p9e8r.
+.3l4and
+.la2n
+.lan2d1s2
+.landsca4p
+.lands1ca
+.landsca5per
+.la8n8d9s8c8a8p9e8r8s.
+.landscape4r1s2
+.la8r9c8e9n8y.
+.l2a2r
+.lar1c
+.lar2ce
+.lar1cen4
+.la8r9c8e9n9i8s8t.
+.lar4ceni
+.le8a8f9h8o8p9p8e8r.
+.le2a
+.lea2f
+.lea4fh
+.leafho4p1p
+.leafhop2pe
+.leafhop3per
+.le8a8f9h8o8p9p8e8r8s.
+.leafhoppe4r1s2
+.le8t9t8e8r9s8p8a8c9i8n8g.
+.le4t3t2
+.lette4r1s2
+.letter1sp
+.letter2s1pa
+.lettersp4a2ci
+.letterspa2c1in
+.letterspac1ing
+.li8f8e9s8p8a8n.
+.life1sp
+.life2s1pa
+.lifespa4n
+.li8f8e9s8p8a8n8s.
+.lifespa2n1s2
+.li8f8e9s8t8y8l8e.
+.lifes2ty
+.lifes2tyl
+.li8f8e9s8t8y8l8e8s.
+.lifestyles2
+.li8g8h8t9w8e8i8g8h8t.
+.3ligh
+.lightw4
+.lightwei2
+.l2ightwe2ig2
+.li8m9o8u9s8i8n8e8s.
+.li4mo
+.li3mo2us
+.limou2
+.limou2s1in
+.limous2ine
+.limousi1nes
+.li8n8e9b8a8c8k8e8r.
+.1l4ine
+.lin2e2b
+.lineback1
+.lineback1er
+.li8n8e9s8p8a8c8i8n8g.
+.li1nes
+.li4ne1sp
+.line2s1pa
+.linesp4a2ci
+.linespa2c1in
+.linespac1ing
+.li9o8n9e8s8s.
+.lio2n
+.lio1nes
+.lion2e2ss
+.li8t8h9o9g8r8a8p8h8e8d.
+.l2ith
+.litho4g
+.litho1gr
+.lithograph4ed
+.li8t8h9o9g8r8a8p8h8s.
+.lithogra4p4h1s2
+.lo9b8o8t9o8m8y.
+.lobo4to
+.loboto3my
+.lo9b8o8t9o8m9i8z8e.
+.lobotom2iz
+.lobotomi2ze
+.lo8g8e8s.
+.lo1ge
+.lo8n8g9e8s8t.
+.5long
+.lo2n
+.lo9q8u8a8c9i8t8y.
+.lo1q
+.loqu2
+.loquac4
+.loqu4a2ci
+.loqua2c1it
+.loquaci1ty
+.lo8v8e9s8t8r8u8c8k.
+.4lov
+.lov4e2s
+.lov2est4r
+.lovestruc5
+.lovestruck1
+.ma8c8r8o9e8c8o9n8o8m8i8c8s.
+.macro4e
+.macroe1co
+.macroeco2n
+.macroeco3nomic
+.macroeconomi4c3s2
+.ma8l9a9p8r8o8p9i8s8m.
+.malapr2
+.malapropi2s1m
+.ma8l9a9p8r8o8p9i8s8m8s.
+.malaprop4is4m1s2
+.ma8n9s8l8a8u8g8h9t8e8r.
+.ma2n1s2
+.man2s1l2
+.manslaugh3
+.ma8n9u9s8c8r8i8p8t.
+.man2us
+.manusc2
+.manuscri2
+.manuscr2ip
+.manuscri2p1t
+.ma8r9g8i8n9a8l.
+.marg2
+.margi4n
+.margi1na
+.ma8t8h9e9m8a9t8i9c8i8a8n.
+.m4ath3
+.math5em
+.math2e
+.1mathe1ma
+.mathemat1ic
+.mathemat2i1ci
+.mathemati3c2i1a
+.mathematici2a2n
+.ma8t8h9e9m8a9t8i9c8i8a8n8s.
+.mathematicia2n1s2
+.ma8t8t8e8s.
+.mat5te
+.ma4t3t2
+.mat4tes
+.me8d9i8c9a8i8d.
+.2med
+.m4edi
+.med3i1ca
+.medicai2
+.medica2id
+.me8d8i9o8c8r8e.
+.me1d2io
+.mediocre3
+.me8d8i9o8c9r8i9t8i8e8s.
+.medi5ocrit
+.mediocri2
+.medio5cri1ti
+.mediocrit2ie4
+.me8g8a9l8i8t8h.
+.me2g
+.m4egal
+.me1ga
+.me3gal1i
+.megal1it
+.megal2ith
+.me8g8a9l8i8t8h8s.
+.megali2t4h1s2
+.me8t8a9b8o8l9i8c.
+.me4ta
+.me2ta4b
+.metabol3ic
+.metabol2i
+.me9t8a8b9o9l8i8s8m.
+.metaboli2s1m
+.me9t8a8b9o9l8i8s8m8s.
+.metabol4is4m1s2
+.me9t8a8b9o9l8i8t8e.
+.metabo5l2ite
+.metabol1it
+.me9t8a8b9o9l8i8t8e8s.
+.metabolit4es
+.me8t8a9l8a8n9g8u8a8g8e.
+.met3a2l
+.meta5la
+.metala2n
+.metal2ang
+.metalan1gu
+.metalangu4a
+.me8t8a9l8a8n9g8u8a8g8e8s.
+.me8t8a9p8h8o8r9i8c.
+.metapho4r
+.me8t8h9a8n8e.
+.metha2n4
+.me9t8r8o8p9o9l8i8s.
+.m4etr
+.metropol2i
+.me9t8r8o8p9o9l8i8s8e8s.
+.metropol4ise
+.metropolis1e4s
+.me8t9r8o9p8o8l9i9t8a8n.
+.metropol1it
+.metropoli3ta2n
+.metropoli1ta
+.me8t9r8o9p8o8l9i9t8a8n8s.
+.metropolita2n1s2
+.mi8c8r8o9e8c8o9n8o8m8i8c8s.
+.m4i1cr
+.micro4e
+.microe1co
+.microeco2n
+.microeco3nomic
+.microeconomi4c3s2
+.mi9c8r8o9f8i8c8h8e.
+.micro2fi
+.microf4i2ch
+.microfiche2
+.mi9c8r8o9f8i8c8h8e8s.
+.microfich1es
+.mi8c8r8o9o8r8g8a8n9i8s8m.
+.microo2
+.microorg2
+.microor1ga
+.microorgan5is
+.microorga2n
+.microorgani2s1m
+.mi8c8r8o9o8r8g8a8n9i8s8m8s.
+.microorgan4is4m1s2
+.mi8l8l9a8g8e.
+.m4il1l
+.mi8l9l8i9l8i8t8e8r.
+.mill2i
+.mil4l4i4l
+.millil1i
+.mill2il1it
+.millil2ite
+.mi8m8e8o9g8r8a8p8h8e8d.
+.mimeo2g
+.mimeo1gr
+.mimeograph4ed
+.mi8m8e8o9g8r8a8p8h8s.
+.mimeogra4p4h1s2
+.mi8m9i8c9r8i8e8s.
+.mim1i
+.mim4i1cr
+.mimicri2
+.mimicr2ie4
+.mi8n9i8s.
+.m2ini
+.min1is
+.mi8n8i9s8y8m9p8o9s8i8u8m.
+.minis4y
+.minisy4m1p
+.minisym1pos
+.minisympo5si4u
+.mi8n8i9s8y8m9p8o9s8i8a.
+.minisympos2i1a
+.mi9n8u8t9e8r.
+.m4in1u
+.mi9n8u8t9e8s8t.
+.mi8s9c8h8i8e9v8o8u8s9l8y.
+.m2is1c
+.mis3ch2
+.misch2ie4
+.mischievou2
+.mischievo2us
+.mischievous1l2
+.mischievous1ly
+.mi9s8e8r8s.
+.m4ise
+.mis3er
+.mise4r1s2
+.mi9s8o8g9a9m8y.
+.mi2so
+.miso1ga
+.miso2gam
+.mo8d9e8l9l8i8n8g.
+.mo2d1
+.model1l
+.modell2i
+.model2lin4
+.mo8l9e9c8u8l8e.
+.mole1cu
+.mole4cul
+.molecul4e
+.mo8l9e9c8u8l8e8s.
+.molecules2
+.mo8n9a8r8c8h8s.
+.mo1n1a
+.monar3c
+.mon2a2r
+.monar2ch
+.monarc4h1s2
+.mo8n8e8y9l8e8n9d8e8r.
+.moneylen1de
+.mo8n8e8y9l8e8n9d8e8r8s.
+.moneylende4r5s2
+.mo8n8o9c8h8r8o8m8e.
+.mono2ch4
+.monoc4hr4
+.monochro2me
+.mo8n8o9e8n9e8r9g8e8t8i8c.
+.mo3noe
+.monoen1er
+.monoenerg2
+.monoener3get
+.monoenerget1ic
+.mo8n9o8i8d.
+.monoi2
+.mono2id
+.mo8n8o9p8o8l8e.
+.mo4nop
+.mo8n8o9p8o8l8e8s.
+.monopoles2
+.mo9n8o8p9o8l8y.
+.monopo2ly
+.mo8n8o9s8p8l8i8n8e.
+.monos1p2l2
+.monospli4n
+.monosp1l4ine
+.mo8n8o9s8p8l8i8n8e8s.
+.monospli1nes
+.mo8n8o9s8t8r8o8f8i8c.
+.monos5t
+.monost4r
+.monostro2fi
+.mo9n8o8t9o9n8i8e8s.
+.mono1to
+.mo2noto2n
+.monoton2ie4
+.mo9n8o8t9o9n8o8u8s.
+.mono4tono
+.monoto1nou2
+.monotono2us
+.mo9r8o8n9i8s8m.
+.moro5n4is
+.moro2n
+.moroni2s1m
+.mo8s9q8u8i9t8o.
+.mos2
+.mosqu2
+.mosq2ui2
+.mosqui1to
+.mo8s9q8u8i9t8o8s.
+.mosquitos2
+.mo8s9q8u8i9t8o8e8s.
+.mu8d9r8o8o8m.
+.mu1dr
+.mud1room
+.mudroo2
+.mu8d9r8o8o8m8s.
+.mudroo4m1s2
+.mu8l9t8i9f8a8c9e8t8e8d.
+.5mu4lt
+.mul1ti3
+.multif2
+.multi1fa
+.multifa4ce
+.multifacet4
+.multiface2t1ed
+.mu8l9t8i9p8l8i8c9a8b8l8e.
+.mult2ip
+.multi1p2l2
+.multipli1ca
+.multiplica1b2l2
+.mu8l8t8i9u8s8e8r.
+.multi4u
+.multi2us
+.ne8o9f8i8e8l8d8s.
+.3neo
+.ne5of
+.neo2fi
+.neof2ie4
+.neofie2ld3
+.neofiel2d1s2
+.ne8o9n8a8z8i.
+.neo2n
+.neo1n1a
+.neona2z1i
+.ne8o9n8a8z8i8s.
+.neonaz4is
+.ne8p8h9e8w8s.
+.nephe4
+.ne8p8h9r8i8t8e.
+.nep4hr4
+.nephr2ite
+.ne8p8h9r8i8t8i8c.
+.nephr4i2t3ic
+.nephri1ti
+.ne8w9e8s8t.
+.ne4w
+.newest3
+.ne8w8s9l8e8t9t8e8r.
+.news4l2
+.news2le
+.newsle4t3t2
+.ne8w8s9l8e8t9t8e8r8s.
+.newslette4r1s2
+.ni8t8r8o9m8e8t8h9a8n8e.
+.n2it
+.ni3tr
+.nitro2me
+.nitro4met
+.nitrometha2n4
+.no9n8a8m8e.
+.no4n
+.no1n1a
+.no8n9a8r9i8t8h9m8e8t9i8c.
+.nonar3i
+.non2a2r
+.nonar2ith
+.nonarit4h1m
+.nonarithmet4
+.nonarithmet1ic
+.no8n9e8m8e8r9g8e8n8c8y.
+.none1me
+.nonemerg2
+.nonemer1gen
+.nonemergen1cy
+.no8n9e8q8u8i9v8a8r8i9a8n8c8e.
+.none2q
+.nonequ2
+.noneq2ui2
+.nonequ2iv3
+.nonequi1va
+.nonequiv2a2r
+.nonequivar1i
+.nonequivar3i2a2n
+.nonequivar2i3a
+.nonequivar4ianc
+.no8n8e9t8h8e9l8e8s8s.
+.noneth2e
+.nonethe1les2
+.nonethe3l2e2ss
+.no8n9e8u8c8l8i8d9e8a8n.
+.non4eu
+.noneu1c4l4
+.noneucl2id
+.noneuclidea2n
+.no8n9i8s8o9m8o8r9p8h8i8c.
+.non5i
+.non1is
+.noni2so
+.noni3som
+.noniso1mo
+.nonisomo2r
+.nonisomor1p
+.nonisomorp4h4
+.nonisomorph1ic
+.no8n9p8s8e8u8d8o9c8o8m9p8a8c8t.
+.non1p4
+.non2p1s2
+.nonp2se
+.nonps4eu
+.nonpseu1do
+.nonpseudo1co
+.nonpseudoco4m1p
+.nonpseudocom1pa
+.nonpseudocompa2c4t
+.no8n9s8m8o8o8t8h.
+.no2n3s2
+.non2s3m
+.nons1mo
+.nonsmoo2
+.nonsmo4oth
+.no8n9u8n8i9f8o8r8m.
+.no3nu4n
+.nonu1ni
+.nonuni1fo
+.nonunifo2r
+.nonunifor1m
+.no8n9u8n8i9f8o8r8m9l8y.
+.nonunifor4m1l
+.nonuniform1ly
+.no8r9e8p9i9n8e8p8h9r8i8n8e.
+.nore5pi2n
+.norep2ine
+.norepinep4hr4
+.norep2inephr2in4e
+.no8t9w8i8t8h9s8t8a8n8d9i8n8g.
+.notw4
+.notwi2
+.notw2ith3
+.notwi2t4h1s2
+.notwith5st4and
+.notwiths1ta
+.notwithsta2n
+.notwithstand1in
+.nu9c8l8e8o9t8i8d8e.
+.nucle3
+.nu1c4l4
+.nucle4ot
+.nucleot2id
+.nu9c8l8e8o9t8i8d8e8s.
+.nucleotide4s2
+.nu8t9c8r8a8c8k9e8r.
+.nu4tc
+.nutcrack1
+.nutcrack1er
+.nu8t9c8r8a8c8k9e8r8s.
+.nutcracke4r1s2
+.oe8r9s8t8e8d8s.
+.o3er
+.oe4r1s2
+.oers4t1ed
+.oerste2d1s2
+.of8f9l8i8n8e.
+.o4f1f
+.off4l2
+.offlin4
+.off1l4ine
+.of8f9l8o8a8d.
+.offl4oa
+.offloa2d3
+.of8f9l8o8a8d8s.
+.offloa2d1s2
+.of8f9l8o8a8d8e8d.
+.offloa2d1ed
+.ol8i9g8o8p9o9l8i8s8t.
+.ol2i
+.ol2ig
+.oli2go
+.ol2igopol2i
+.ol8i9g8o8p9o9l8i8s8t8s.
+.oligopolis4t1s2
+.ol8i9g8o8p9o8l8y.
+.oligopo2ly
+.ol8i9g8o8p9o8l9i8e8s.
+.oligopol2ie4
+.op9e8r9a8n8d.
+.op1er
+.3oper1a
+.op4er4and
+.opera2n
+.op9e8r9a8n8d8s.
+.operan2d1s2
+.or8a8n8g9u8t8a8n.
+.ora2n
+.or2ang
+.oran1gu
+.oran4gu4t
+.orangu1ta
+.ora2nguta2n
+.or8a8n8g9u8t8a8n8s.
+.oranguta2n1s2
+.or9t8h8o9d8o8n9t8i8s8t.
+.ortho2do4
+.orthodo2n
+.orthodon3t4i
+.orthodon1t
+.or9t8h8o9d8o8n9t8i8s8t8s.
+.orthodontis4t1s2
+.or9t8h8o9k8e8r9a9t8o8l9o8g8y.
+.orth2ok
+.orthok1er
+.orthoker1a
+.orthokera1to
+.orthokeratol1o1gy
+.or8t8h8o9n8i8t8r8o9t8o8l8u8e8n8e.
+.ortho2n
+.orthon2it
+.orthoni3tr
+.orthonitro1to
+.orthonitrotolu3en
+.orthonitrotolu4ene
+.ov8e8r9v8i8e8w.
+.overv2ie4
+.ov8e8r9v8i8e8w8s.
+.ox9i8d9i8c.
+.ox3i
+.oxi5di
+.ox2id
+.pa8d9d8i8n8g.
+.1pa
+.p4a2d
+.pad4d1in
+.pad1d4
+.pa8i8n9l8e8s8s9l8y.
+.p4ai2
+.pa4i4n4
+.pa4i4n1l
+.painles2
+.pain3l2e2ss
+.painles4s1l2
+.painless1ly
+.pa8l9e8t8t8e.
+.p4al
+.p2ale
+.pale4t3t2
+.pa8l9e8t8t8e8s.
+.palet4tes
+.pa8r9a9b8o8l8a.
+.p2a2r
+.pa2rab
+.parabo1la
+.pa8r9a9b8o8l9i8c.
+.parabol3ic
+.parabol2i
+.pa9r8a8b9o9l8o8i8d.
+.paraboloi2
+.parabolo2id
+.pa8r9a9d8i8g8m.
+.para2d
+.par2adi
+.parad2ig
+.paradig1m
+.pa8r9a9d8i8g8m8s.
+.paradig4m1s2
+.pa8r8a9c8h8u8t8e.
+.para2ch
+.parachu4t
+.pa8r8a9c8h8u8t8e8s.
+.pa8r8a9d8i9m8e8t8h8y8l9b8e8n8z8e8n8e.
+.parad4imet
+.paradimethy2l1b
+.paradimethylb4e4n3z
+.paradimethylben2ze
+.paradimethylbenze4n
+.pa8r8a9f8l8u8o8r8o9t8o8l8u8e8n8e.
+.para2f
+.paraf4l2
+.paraflu3o
+.parafluo3r
+.parafluoro1to
+.parafluorotolu3en
+.parafluorotolu4ene
+.pa8r8a9g8r8a8p8h9e8r.
+.para1gr
+.parag5ra3ph4er
+.pa8r8a9l8e9g8a8l.
+.par3al
+.par2ale
+.paral4egal
+.parale1ga
+.pa8r9a8l9l8e8l9i8s8m.
+.paral1l
+.paral2le
+.paral3lel
+.parallel2i
+.paralle2lis
+.paralleli2s1m
+.pa8r8a9m8a8g9n8e8t9i8s8m.
+.par4a1ma
+.param3ag
+.para5mag1n
+.paramagneti2s4m
+.pa8r8a9m8e8d8i8c.
+.para2med
+.param4edi
+.pa8r8a9m8e8t8h8y8l9a8n8i8s8o8l8e.
+.param3et
+.paramethy3la
+.paramethyla2n
+.paramethylani2so
+.pa9r8a8m9e9t8r8i8z8e.
+.param4etr
+.parametri2ze
+.pa8r8a9m8i8l9i9t8a8r8y.
+.par2ami
+.paramil1i
+.param2il1it
+.paramili1ta
+.paramilit2a2r
+.pa8r8a9m8o8u8n8t.
+.para2mo
+.paramou2
+.paramoun1t
+.pa8t8h9o9g8e8n9i8c.
+.p4ath
+.pat4ho
+.patho4g
+.patho1ge4
+.patho1gen
+.pe8e8v9i8s8h.
+.p4ee
+.pee1vi
+.peevis2h
+.pe8e8v9i8s8h9n8e8s8s.
+.peevis2h1n
+.peevish1nes
+.peevishn2e2ss
+.pe8n9t8a9g8o8n.
+.pen1t
+.pen1ta
+.penta2go
+.pentago2n2
+.pe8n9t8a9g8o8n8s.
+.pentago2n3s2
+.pe9t8r8o9l8e9u8m.
+.petrol4eu
+.ph8e9n8o8m9e9n8o8n.
+.ph4e3no
+.phe2n
+.pheno2me
+.pheno1men
+.phenom4eno
+.phenomeno4n
+.ph8e8n8y8l9a8l8a9n8i8n8e.
+.pheny3la
+.phenylala2n
+.phenylala5n2ine
+.phenylalan1in
+.ph8i9l8a8t9e9l8i8s8t.
+.phi4latel2i4
+.philate2lis
+.ph8i9l8a8t9e9l8i8s8t8s.
+.philatelis4t1s2
+.ph8o9n8e8m8e.
+.3phone
+.pho2n
+.phone1me
+.ph8o9n8e8m8e8s.
+.phone2mes
+.ph8o9n8e9m8i8c.
+.phone5mi
+.ph8o8s9p8h8o8r9i8c.
+.phos1p
+.phospho5
+.phospho4r
+.ph8o9t8o9g8r8a8p8h8s.
+.pho1to
+.photo2gr
+.photogra4p4h1s2
+.ph8o9t8o9o8f8f9s8e8t.
+.photoo2
+.photoo4f1f
+.photoof2f3s
+.pi8c9a9d8o8r.
+.pi1ca
+.pica2d
+.pica1do
+.picad4or
+.pi8c9a9d8o8r8s.
+.picado4rs2
+.pi8p8e9l8i8n8e.
+.p2ip
+.pipe4
+.pipel2i
+.pipe1l4ine
+.pi8p8e9l8i8n8e8s.
+.pipeli1nes
+.pi8p8e9l8i8n9i8n8g.
+.pipel2in3i
+.pipelin1in
+.pipelinin4g
+.pi9r8a9n8h8a8s.
+.p4ir
+.pi1ra
+.pira2n
+.pira4n1h4
+.piranha4
+.pl8a8c8a9b8l8e.
+.1p2l2
+.pla1ca
+.placa1b2l2
+.pl8a8n8t9h8o8p9p8e8r.
+.3pla2n
+.plan1t
+.plantho4p1p
+.planthop2pe
+.planthop3per
+.pl8a8n8t9h8o8p9p8e8r8s.
+.planthoppe4r1s2
+.pl8e8a8s9a8n8c8e.
+.ple2a
+.pleasa2
+.plea3sanc
+.pleasa2n
+.pl8u8g9i8n.
+.plug5in
+.pl8u8g9i8n8s.
+.plu5g4i2n1s2
+.po8l9t8e8r9g8e8i8s8t.
+.po4l2t
+.pol1te
+.polterg2
+.poltergei2
+.po8l8y9e8n8e.
+.po2ly
+.po8l8y9e8t8h9y8l9e8n8e.
+.polye4t
+.po9l8y8g9a9m8i8s8t.
+.poly1ga
+.poly2gam
+.polygam2is
+.po9l8y8g9a9m8i8s8t8s.
+.polygamis4t1s2
+.po8l8y8g9o8n9i9z8a9t8i8o8n.
+.poly1go
+.polygo2n2
+.polygo3ni
+.polygoniza1
+.polygoniza1t2io
+.polygonizatio2n
+.po9l8y8p8h9o9n8o8u8s.
+.polypho2n
+.polypho1nou2
+.polyphono2us
+.po8l8y9s8t8y8r8e8n8e.
+.po2lys4
+.polys1t
+.polys2ty
+.po8m8e9g8r8a8n9a8t8e.
+.po2me
+.pome2g
+.pome1gr
+.pomegra2n2
+.pomegra1na
+.pomegran2at
+.po8r8o9e8l8a8s9t8i8c.
+.1p4or
+.poro4e
+.poro4el
+.poroe1la
+.poroelast2i
+.poroelas1tic
+.po8r9o8u8s.
+.porou2
+.poro2us
+.po8r9t8a9b8l8e.
+.por1ta
+.por2tab
+.portab2l2
+.po8s8t9a8m9b8l8e.
+.1pos
+.pos2ta
+.posta4m1b
+.postamb2l2
+.po8s8t9a8m9b8l8e8s.
+.postambles2
+.po8s8t9h8u9m8o8u8s.
+.posthu1mo
+.posthu3mo2us
+.posthumou2
+.po8s8t9s8c8r8i8p8t.
+.pos4t1s2
+.post4sc
+.postscri2
+.postscr2ip
+.postscri2p1t
+.po8s8t9s8c8r8i8p8t8s.
+.postscrip4t1s2
+.po8s9t8u8r9a8l.
+.pos1tu
+.postu1ra
+.pr8e9a8m9b8l8e.
+.prea4m1b
+.preamb2l2
+.pr8e9a8m9b8l8e8s.
+.preambles2
+.pr8e9l8o8a8d8e8d.
+.prel4oa
+.preloa2d3
+.preloa2d1ed
+.pr8e9p8a8r9i8n8g.
+.pre2pa
+.prep4a4r1i
+.prep2a2r
+.preparin4g
+.pr8e9p8r8i8n8t.
+.pr2epr2
+.preprin4t3
+.pr8e9p8r8i8n8t8s.
+.preprin4t4s2
+.pr8e9p8r8o8c8e8s9s8o8r.
+.pre3pro
+.prepr2oc
+.prepro1ce
+.preproc2e2ss
+.preproces1so
+.pr8e9p8r8o8c8e8s9s8o8r8s.
+.preprocesso4rs2
+.pr8e9s8p8l8i8t9t8i8n8g.
+.pre1sp
+.pres1p2l2
+.prespl1it
+.prespl4i4t3t2
+.presplit2t1in
+.pr8e9w8r8a8p.
+.prewra4
+.pr8e9w8r8a8p8p8e8d.
+.prewra4p1p
+.prewrap2pe
+.prewrap4p2ed
+.pr8i8e8s8t9e8s8s8e8s.
+.5pr2i4e4
+.pri1est
+.pries4t2e2ss
+.priestess1e4s
+.pr8e8t9t8y9p8r8i8n9t8e8r.
+.pre4t3t2
+.pret1ty
+.pr2ettypr2
+.prettyprin4t3
+.pr8e8t9t8y9p8r8i8n9t8i8n8g.
+.prettyprint2i
+.prettyprin4t3ing
+.prettyprin2t1in
+.pr8o9c8e9d8u8r9a8l.
+.pr2oc
+.pro1ce
+.proce1du
+.procedu1ra
+.pr8o8c8e8s8s.
+.proc2e2ss
+.pr8o9c8u8r9a8n8c8e.
+.procu1ra
+.procura2n
+.pr8o8g9e9n8i8e8s.
+.pro1ge
+.pro1gen
+.proge5n2ie4
+.pr8o8g9e9n8y.
+.pro4geny
+.pr8o9g8r8a8m9m8a8b8l8e.
+.pro1gr
+.program1m
+.program1ma
+.program2mab
+.programmab2l2
+.pr8o8m9i9n8e8n8t.
+.prom4i
+.prom1in
+.prom2ine
+.promi1nen
+.prominen1t
+.pr8o9m8i8s9c8u9o8u8s.
+.prom2is
+.prom2is1c
+.promis1cu
+.promiscu1ou2
+.promiscuo2us
+.pr8o8m9i8s9s8o8r8y.
+.prom4i2s1s
+.promis1so
+.promisso1ry
+.pr8o8m9i8s8e.
+.prom4ise
+.pr8o8m9i8s8e8s.
+.promis1e4s
+.pr8o9p8e8l9l8e8r.
+.pro3pel
+.propel1l
+.propel2le
+.pr8o9p8e8l9l8e8r8s.
+.propelle4r1s2
+.pr8o9p8e8l9l8i8n8g.
+.propell2i
+.propel2lin4
+.pr8o9h8i8b9i9t8i8v8e.
+.pro1h2
+.prohibi2t
+.prohibi1ti
+.prohibi1t2iv
+.pr8o9h8i8b9i9t8i8v8e9l8y.
+.prohibitiv4e1ly
+.pr8o9s8c8i8u8t9t8o.
+.pros2c
+.pros1ci
+.prosci1u
+.prosciu4t3t2
+.prosciut5to
+.pr8o9t8e8s8t9e8r.
+.pro1t
+.pro4tes
+.pr8o9t8e8s8t9e8r8s.
+.proteste4r1s2
+.pr8o9t8e8s9t8o8r.
+.prot4es2to
+.pr8o9t8e8s9t8o8r8s.
+.protesto4rs2
+.pr8o9t8o9l8a8n9g8u8a8g8e.
+.pro1to
+.proto1la
+.proto4la2n
+.protol2ang
+.protolan1gu
+.protolangu4a
+.pr8o9t8o9t8y8p9a8l.
+.proto1ty
+.prototy1pa
+.prototyp4al
+.pr8o8v9i8n8c8e.
+.prov1in
+.prov2inc
+.pr8o8v9i8n8c8e8s.
+.pr8o9v8i8n9c8i8a8l.
+.provin1ci
+.provin3c2i1a
+.provinci2al
+.pr8o8w9e8s8s.
+.prow2e2ss
+.ps8e8u9d8o9d8i8f9f8e8r9e8n9t8i8a8l.
+.2p1s2
+.p2se
+.ps4eu
+.pseu1do
+.pseudod1if
+.pseudodi4f1f
+.pseudodiffer1
+.pseudodiffer3en1t
+.pseudodifferent2i
+.pseudodifferen1t2i1a
+.pseudodifferenti2al
+.ps8e8u9d8o9f8i9n8i8t8e.
+.pseu2d5of
+.pseudo2fi
+.pseudo2fin
+.pseudof2ini
+.pseudofin2it
+.pseudofin2ite
+.ps8e8u9d8o9f8i9n8i8t8e9l8y.
+.pseudofinite1ly
+.ps8e8u9d8o9f8o8r8c8e8s.
+.pseudo1fo
+.pseudofo2r
+.pseudofor1c
+.pseudofor2ce
+.ps8e8u9d8o8g9r8a9p8h8e8r.
+.pseud4og
+.pseudo1gr
+.pseudog5ra3ph4er
+.ps8e8u9d8o9g8r8o8u8p.
+.pseudo4g4ro
+.pseudogrou2
+.ps8e8u9d8o9g8r8o8u8p8s.
+.pseudogrou2p1s2
+.ps8e8u9d8o9n8y8m.
+.pseu4do2n
+.pseudonym4
+.ps8e8u9d8o9n8y8m8s.
+.pseudony4m1s2
+.ps8e8u9d8o9w8o8r8d.
+.pseudo4wo2
+.ps8e8u9d8o9w8o8r8d8s.
+.pseudowor2d1s2
+.ps8y9c8h8e9d8e8l9i8c.
+.ps4y
+.p4sy1c
+.psy3ch
+.psych4e2
+.psy4ch4ed
+.psychedel2i
+.ps8y8c8h8s.
+.psyc4h1s2
+.pu9b8e8s9c8e8n8c8e.
+.pub3
+.pub4e
+.pu4bes4
+.pubes2c
+.pubes1cen
+.pubes3cenc
+.qu8a8d9d8i8n8g.
+.qu2
+.qua2d
+.quad4d1in
+.quad1d4
+.qu8a9d8r8a8t9i8c.
+.qua1dr
+.quadrat1ic
+.qu8a9d8r8a8t9i8c8s.
+.quadrati4c3s2
+.qu8a8d9r8a9t8u8r8e.
+.quadra2tu
+.quadra3ture
+.qu8a8d9r8i9p8l8e8g9i8c.
+.quadri2p2l2
+.quadr2ip
+.quadripleg4ic
+.qu8a8i8n8t9e8r.
+.quai2
+.qua4i4n
+.quain1t
+.qu8a8i8n8t9e8s8t.
+.qu8a9s8i9e8q8u8i8v9a9l8e8n8c8e.
+.quas2ie4
+.quasie1q
+.qu2asiequ2
+.quasieq2ui2
+.quasiequ2iv3
+.quasiequi1va
+.quasiequiv2ale
+.quasiequiva3lenc
+.qu8a9s8i9e8q8u8i8v9a9l8e8n8c8e8s.
+.qu8a9s8i9e8q8u8i8v9a9l8e8n8t.
+.quasiequiva1len1t
+.qu8a9s8i9h8y9p8o9n8o8r9m8a8l.
+.quasi3h
+.quasihy3po
+.quasihypo2n
+.quasihyponor1m
+.quasihyponor1ma
+.qu8a9s8i9r8a8d9i9c8a8l.
+.quas4i2r
+.quasi1r5a
+.quasira2d
+.quasir2adi
+.quasirad3i1ca
+.qu8a9s8i9r8e8s8i8d9u8a8l.
+.quasi4res
+.quasire1si
+.quasire2s2id
+.quasiresi2du
+.quasiresid1u1a
+.qu8a9s8i9s8m8o8o8t8h.
+.qua1sis
+.quasi2s1m
+.quasis1mo
+.quasismoo2
+.quasismo4oth
+.qu8a9s8i9s8t8a9t8i8o8n9a8r8y.
+.quasis1ta
+.quasistation5a2r
+.quasista1t2io
+.quasistatio2n
+.quasistatio1n1a
+.qu8a9s8i9t8o8p8o8s.
+.qu5a5si4t
+.quasi1to
+.quasito1pos
+.qu8a9s8i9t8r8i9a8n9g8u9l8a8r.
+.quasi5tr2i3a
+.quasitri2a2n
+.quasitri2ang
+.quasitrian1gu
+.quasitriangu1la
+.quasitriangul2a2r
+.qu8a9s8i9t8r8i8v9i8a8l.
+.quasitr2i4v
+.quasitriv3i
+.quasitriv2i1a
+.quasitrivi2al
+.qu8i8n9t8e8s9s8e8n8c8e.
+.q2ui2
+.qui4n
+.quin1t
+.quin4t2e2ss
+.quintes4senc
+.qu8i8n9t8e8s9s8e8n8c8e8s.
+.qu8i8n9t8e8s9s8e8n9t8i8a8l.
+.quintessen1t
+.quintessent2i
+.quintessen1t2i1a
+.quintessenti2al
+.ra8b9b8i8t9r8y.
+.2rab
+.ra2b1b
+.rabbi2t
+.rabbi3tr
+.rabbit5ry
+.ra9d8i9o8g9r8a9p8h8y.
+.ra2d
+.r2adi
+.ra3d2io
+.radio5g
+.radio2gr
+.radio4g3ra1phy
+.ra8f8f9i8s8h.
+.raf5fi
+.ra2f
+.ra4f1f4
+.raf2f5is
+.raffis2h
+.ra8f8f9i8s8h9l8y.
+.raffis4h1l4
+.raffish1ly
+.ra8m9s8h8a8c8k8l8e.
+.ra4m1s2
+.ram4s2h
+.ramshack1
+.ramshack1l
+.ra8v9e8n9o8u8s.
+.rav4e4no
+.rave1nou2
+.raveno2us
+.re9a8r8r8a8n8g8e9m8e8n8t.
+.re5ar1r4
+.re2a2r
+.rearran4ge
+.rearra2n
+.rearr2ang
+.rearrange1me
+.rearrange1men
+.rearrange3men1t
+.re9a8r8r8a8n8g8e9m8e8n8t8s.
+.rearrangemen4t4s2
+.re8c9i9p8r8o8c9i9t8i8e8s.
+.reciproci1ti
+.reciprocit2ie4
+.re8c9t8a8n9g8l8e.
+.rec4ta2n
+.re2c1t
+.rect5ang
+.rec1ta
+.rectan1gl2
+.rectan1gle
+.re8c9t8a8n9g8l8e8s.
+.rectangles2
+.re8c9t8a8n9g8u9l8a8r.
+.rectan1gu
+.rectangu1la
+.rectangul2a2r
+.re9d8i9r8e8c8t.
+.2r2ed
+.r4edi
+.red4ir2
+.redi1re
+.redire2c1t
+.re9d8i9r8e8c8t9i8o8n.
+.redirec1t2io
+.redirectio2n
+.re9d8u8c9i8b8l8e.
+.re1du
+.redu2c
+.reduci4b
+.redu1ci
+.reduci1b2l2
+.re9e8c8h8o.
+.ree2c
+.ree2ch
+.ree3cho2
+.re9p8h8r8a8s8e.
+.rep4hr4
+.rephr2as
+.re9p8h8r8a8s8e8s.
+.rephras1e4s
+.re9p8h8r8a8s8e8d.
+.rephra4s4ed
+.re9p8o9s8i9t8i8o8n.
+.re4posi
+.re1po
+.re1pos
+.repo3s2i1t2io
+.reposi1ti
+.repositio2n
+.re9p8o9s8i9t8i8o8n8s.
+.repositio2n3s2
+.re9p8r8i8n8t.
+.repr2
+.reprin4t3
+.re9p8r8i8n8t8s.
+.reprin4t4s2
+.re9s8t8o8r9a8b8l8e.
+.r4es2to
+.resto2ra
+.resto2rab
+.restorab2l2
+.re8t8r8o9f8i8t.
+.retro2fi
+.re8t8r8o9f8i8t9t8e8d.
+.retrof4i4t4t2
+.retrofit2t1ed
+.re9u8s9a8b8l8e.
+.r4eu2
+.re2us4
+.reusa2
+.reu2s1ab
+.reusab2l2
+.re9u8s8e.
+.re9w8i8r8e.
+.rewi2
+.rew4ir4
+.re9w8r8a8p.
+.rewra4
+.re9w8r8a8p8p8e8d.
+.rewra4p1p
+.rewrap2pe
+.rewrap4p2ed
+.re9w8r8i8t8e.
+.rewri4
+.rewr2ite
+.rh8i9n8o8c9e8r9o8s.
+.rh4
+.rh2i1no
+.rhi4no4c
+.rhino1ce
+.rhinoc2ero
+.ri8g8h8t9e8o8u8s.
+.righ1teo
+.righteou2
+.righteo2us
+.ri8g8h8t9e8o8u8s9n8e8s8s.
+.righteous1n4
+.righteous1nes
+.righteousn2e2ss
+.ri8n8g9l8e8a8d8e8r.
+.rin4g
+.ringl2
+.rin1gle
+.ringle2a
+.ringlea2d1
+.ri8n8g9l8e8a8d8e8r8s.
+.ringleade4r5s2
+.ro9b8o8t.
+.ro9b8o8t8s.
+.robo4t1s2
+.ro9b8o8t8i8c.
+.ro9b8o8t9i8c8s.
+.roboti4c3s2
+.ro8u8n8d9t8a8b8l8e.
+.rou2
+.roun2d
+.round1ta
+.round2tab
+.roundtab2l2
+.ro8u8n8d9t8a8b8l8e8s.
+.roundta5bles2
+.sa8l8e8s9c8l8e8r8k.
+.sa2
+.s2ale
+.sales2
+.sales2c
+.salescle5
+.sales1c4l4
+.sa8l8e8s9c8l8e8r8k8s.
+.salescler4k1s2
+.sa8l8e8s9w8o8m8a8n.
+.sales4w2
+.sale4s1wo2
+.saleswom1
+.saleswo1ma
+.saleswoma2n
+.sa8l8e8s9w8o8m8e8n.
+.saleswo2me
+.saleswo1men
+.sa8l9m8o9n8e8l9l8a.
+.s4a2l4m
+.salmo2n4
+.sal1mo
+.salmon4ella
+.salmonel1l
+.sa8l9t8a9t8i8o8n.
+.sa4l4t
+.sal1ta
+.salta1t2io
+.saltatio2n
+.sa8r9s8a9p8a8r9i8l9l8a.
+.s2a2r
+.sa2r4sa2
+.sa4rs2
+.sars1ap
+.s2a2rsap2a2r4
+.sarsa1pa
+.sarsap4a4r1i
+.sarsaparil1l
+.sa8u8e8r9k8r8a8u8t.
+.sau4
+.sauerkrau4t
+.sc8a8t9o9l8o8g9i9c8a8l.
+.s1ca
+.sca1to
+.scato3log1ic
+.scatologi1ca
+.sc8h8e8d9u8l9i8n8g.
+.s2ch2
+.sche2
+.s4ch4ed
+.sche4dul
+.sche1du
+.schedul2i
+.schedul3ing
+.sc8h8i8z9o9p8h8r8e8n8i8c.
+.schi2z
+.schi1zo
+.schiz2oph
+.schizop4hr4
+.sc8h8n8a8u9z8e8r.
+.sc2h1n
+.sch1na
+.schn2au
+.schnau2z4e
+.schnauz1er
+.sc8h8o8o8l9c8h8i8l8d.
+.s4cho2
+.schoo2
+.schoo4l1c2
+.s2chool2ch
+.schoolch4il2
+.schoolchi2ld
+.sc8h8o8o8l9c8h8i8l8d9r8e8n.
+.schoolchil3dr
+.schoolchildre4
+.schoolchil5dren
+.sc8h8o8o8l9t8e8a8c8h8e8r.
+.schoo4l2t
+.school1te
+.s2chooltea2ch
+.schoolteache2
+.sc8h8o8o8l9t8e8a8c8h9e8r8s.
+.schoolteach3e4r1s2
+.sc8r8u9t8i9n8y.
+.scru2t1i5n
+.scr4u1t2i
+.scrut4iny
+.sc8y8t8h9i8n8g.
+.s1cy
+.scy3thin
+.se8l8l9e8r.
+.sel2le
+.se8l8l9e8r8s.
+.selle4r1s2
+.se8c9r8e9t8a8r9i8a8t.
+.se1cr
+.se4c3re1ta
+.secret2a2r
+.secretar1i
+.secretar2i3a
+.se8c9r8e9t8a8r9i8a8t8s.
+.secretaria4t1s2
+.se8m9a9p8h8o8r8e.
+.se1ma
+.se4map
+.semapho4r
+.se8m9a9p8h8o8r8e8s.
+.se9m8e8s9t8e8r.
+.4se1me
+.se2mes
+.se8m8i9d8e8f9i9n8i8t8e.
+.sem2id
+.semide1f
+.semidef5i5n2ite
+.semide1fi
+.semide2fin
+.semidef2ini
+.semidefin2it
+.se8m8i9d8i9r8e8c8t.
+.semi2di
+.semid4ir2
+.semidi1re
+.semidire2c1t
+.se8m8i9h8o9m8o9t8h8e8t9i8c.
+.semi3h
+.semiho1mo
+.semihom4oth3
+.semihomoth2e
+.semihomo3the4t
+.semihomothet1ic
+.se8m8i9r8i8n8g.
+.sem4ir
+.semir1i
+.semirin4g
+.se8m8i9r8i8n8g8s.
+.semirings2
+.se8m8i9s8i8m9p8l8e.
+.se4m2is
+.semisi4m1p
+.semisim1p2l2
+.se8m8i9s8k8i8l8l8e8d.
+.sem4is4k2
+.semisk1i
+.semisk4il1l
+.semiskil2le
+.se8r8o9e8p8i9d8e9m8i9o9l8o8g9i9c8a8l.
+.s2er4o
+.sero4e
+.seroep4id
+.seroepi3de
+.seroepid4em
+.seroepidem2io
+.seroepidemi1ol
+.seroepidemio3log1ic
+.seroepidemiologi1ca
+.se8r9v8o9m8e8c8h9a8n8i8s8m.
+.4ser3vo
+.servo2me
+.servome2ch
+.servomech5a5nis
+.servomecha2n
+.servomechani2s1m
+.se8r9v8o9m8e8c8h9a8n8i8s8m8s.
+.servomechan4is4m1s2
+.se8s9q8u8i9p8e9d8a9l8i8a8n.
+.s1e4s
+.sesqu2
+.sesq2ui2
+.sesqu2ip
+.sesquipe4
+.sesqui2p2ed
+.sesquip2e2d2a
+.sesquipedal1i
+.sesquipedal2i1a
+.sesquipedali2a2n
+.se8t9u8p.
+.se1tu
+.se8t9u8p8s.
+.setu2p1s2
+.se9v8e8r8e9l8y.
+.5sev
+.sev1er
+.sev4erel
+.severe1ly
+.sh8a8p8e9a8b8l8e.
+.sha3pe4a
+.shape1a4b
+.shapeab2l2
+.sh8o8e9s8t8r8i8n8g.
+.sho4
+.sho2est4r
+.shoestrin4g
+.sh8o8e9s8t8r8i8n8g8s.
+.shoestrings2
+.si8d8e9s8t8e8p.
+.5side4s2
+.s2id
+.sideste4p
+.si8d8e9s8t8e8p8s.
+.sideste2p1s2
+.si8d8e9s8w8i8p8e.
+.sides4w2
+.sideswi2
+.sidesw2ip
+.sideswipe4
+.sk8y9s8c8r8a8p8e8r.
+.sk2
+.skys4c
+.skyscrap3er
+.sk8y9s8c8r8a8p8e8r8s.
+.skyscrape4r1s2
+.sm8o8k8e9s8t8a8c8k.
+.2s1m
+.s1mo
+.s4m2ok
+.smokes4
+.smokes1ta
+.smokestack1
+.sm8o8k8e9s8t8a8c8k8s.
+.smokestac4k1s2
+.sn8o8r9k8e8l9i8n8g.
+.s1n4
+.snorke5l2i
+.snorke4l3ing
+.so9l8e9n8o8i8d.
+.1so
+.sol4eno
+.solenoi2
+.soleno2id
+.so9l8e9n8o8i8d8s.
+.solenoi2d1s2
+.so8l8u8t8e.
+.so1lut
+.so8l8u8t8e8s.
+.so8v9e8r9e8i8g8n.
+.4sov
+.soverei2
+.sovere2ig2
+.so8v9e8r9e8i8g8n8s.
+.sovereig2n1s2
+.sp8a9c8e8s.
+.2s1pa
+.spa4ce
+.sp8e9c8i8o8u8s.
+.spe2c
+.spe1c2i
+.spec2io
+.speciou2
+.specio2us
+.sp8e8l8l9e8r.
+.spel1l
+.spel2le
+.sp8e8l8l9e8r8s.
+.spelle4r1s2
+.sp8e8l8l9i8n8g.
+.spell2i
+.spel2lin4
+.sp8e9l8u8n8k9e8r.
+.spelu4nk2
+.spelunk1er
+.sp8e8n8d9t8h8r8i8f8t.
+.spen4d
+.spend2th
+.spendt4hr4
+.spendthr4i2ft
+.sp8h8e8r9o8i8d.
+.s2phe
+.3sph4er
+.sph2ero
+.spheroi2
+.sphero2id
+.sp8h8e8r9o8i8d9a8l.
+.spheroi1d2a
+.sp8h8i8n9g8e8s.
+.sph5ing
+.sph4inge
+.sp8i8c9i9l8y.
+.sp2i1ci
+.spici1ly
+.sp8i8n9o8r8s.
+.spi2n
+.sp4i1no
+.spino4rs2
+.sp8o8k8e8s9w8o8m8a8n.
+.sp2ok
+.spokes4
+.spokes4w2
+.spoke4s1wo2
+.spokeswom1
+.spokeswo1ma
+.spokeswoma2n
+.sp8o8k8e8s9w8o8m8e8n.
+.spokeswo2me
+.spokeswo1men
+.sp8o8r8t8s9c8a8s8t.
+.s1p4or4
+.spor4t1s2
+.sport4sc
+.sports1ca
+.sp8o8r8t8s9c8a8s8t9e8r.
+.sportscast5er
+.sp8o8r9t8i8v8e9l8y.
+.spor1ti
+.spor4t2iv
+.sportiv4e1ly
+.sp8o8r8t8s9w8e8a8r.
+.sport4sw2
+.sportswe2a2r
+.sp8o8r8t8s9w8r8i8t8e8r.
+.sportswri4
+.sportswr2ite
+.sp8o8r8t8s9w8r8i8t8e8r8s.
+.sportswrit5e4r1s2
+.sp8r8i8g8h8t9l8i8e8r.
+.spr2
+.spr2ig
+.sprigh2tl
+.sprightl2ie4
+.sq8u8e8a9m8i8s8h.
+.squ2
+.squeam2is
+.squeamis2h
+.st8a8n8d9a8l8o8n8e.
+.5st4and
+.sta2n
+.stan1d2a
+.standalo2n
+.st8a8r9t8l8i8n8g.
+.st2a2r
+.star2tl
+.st8a8r9t8l8i8n8g9l8y.
+.startlingl2
+.startling1ly
+.st8a9t8i8s9t8i8c8s.
+.statis1t2i
+.statis1tic
+.statisti4c3s2
+.st8e8a8l8t8h9i8l8y.
+.stea4l
+.stea4lt
+.stealth3i
+.steal4th4il2
+.stealthi1ly
+.st8e8e8p8l8e9c8h8a8s8e.
+.s1tee
+.stee4p1
+.stee1p2l2
+.steeple2ch
+.st8e8r8e8o9g8r8a8p8h9i8c.
+.stere1o
+.stereo2g
+.stereo1gr
+.stereo5graph1ic
+.stereogr4aphi
+.st8o9c8h8a8s9t8i8c.
+.s1to
+.sto2ch4
+.stochast2i
+.stochas1tic
+.st8r8a8n8g8e9n8e8s8s.
+.st4r
+.s1tra
+.stran4ge
+.stra2n
+.str2ang
+.strange4n4e
+.stran1gen
+.strange1nes
+.strangen2e2ss
+.st8r8a8p9h8a8n8g8e8r.
+.straph2an4g
+.straphang5er
+.strapha2n
+.st8r8a8t9a9g8e8m.
+.stra2ta
+.st8r8a8t9a9g8e8m8s.
+.stratage4m1s2
+.st8r8e8t8c8h9i9e8r.
+.stre4tc
+.stret4ch
+.stretch2ie4
+.st8r8i8p9t8e8a8s8e.
+.str2ip
+.stri2p1t
+.strip2te
+.st8r8o8n8g9h8o8l8d.
+.stro2n
+.strongho2l2d
+.st8r8o8n8g9e8s8t.
+.st8u9p8i8d9e8r.
+.s1tu
+.stup4id
+.stupi3de
+.st8u9p8i8d9e8s8t.
+.stupide4s2
+.su8b9d8i8f9f8e8r9e8n9t8i8a8l.
+.1su
+.su4b3
+.su4b1d
+.subd1if
+.subdi4f1f
+.subdiffer1
+.subdiffer3en1t
+.subdifferent2i
+.subdifferen1t2i1a
+.subdifferenti2al
+.su8b9e8x9p8r8e8s9s8i8o8n.
+.sub4e
+.sub1ex3p
+.subexpr2
+.subex3pr2e2ss
+.subexpres1si
+.subexpres1s2io
+.subexpres5sio2n
+.su8b9e8x9p8r8e8s9s8i8o8n8s.
+.subexpressio2n3s2
+.su8m9m8a9b8l8e.
+.su2m
+.sum1m
+.sum1ma
+.sum2mab
+.summab2l2
+.su8p8e8r9e8g8o.
+.su1pe
+.supere1go
+.su8p8e8r9e8g8o8s.
+.supere4gos
+.su9p8r8e8m9a9c8i8s8t.
+.supr2
+.supre4mac
+.supre1ma
+.suprem4a2ci
+.su9p8r8e8m9a9c8i8s8t8s.
+.supremacis4t1s2
+.su8r9v8e8i8l9l8a8n8c8e.
+.su2r
+.surv4e
+.survei2
+.surveil1l
+.surveilla2n
+.sw8i8m9m8i8n8g9l8y.
+.sw2
+.swi2
+.swim1m
+.swimm4ingl2
+.swimm5ing1ly
+.sy8m8p9t8o9m8a8t8i8c.
+.sy4m1p
+.sym2p1t
+.symp1to
+.sympto2ma
+.symptomat1ic
+.sy8n9c8h8r8o9m8e8s8h.
+.syn3c4hr4
+.syn2ch
+.synchro2me
+.synchro2mes
+.synchrom4es2h
+.sy8n9c8h8r8o9n8o8u8s.
+.synchro2n
+.synchro1nou2
+.synchrono2us
+.sy8n9c8h8r8o9t8r8o8n.
+.synchrotro2n
+.ta8f8f9r8a8i8l.
+.4ta2f4
+.ta4f1f4
+.taffr2ai2
+.ta8l8k9a9t8i8v8e.
+.ta2l
+.4talk
+.talka3
+.talka4t
+.talka1t2iv
+.ta9p8e8s9t8r8y.
+.tap2est4r
+.tape4stry
+.ta9p8e8s9t8r8i8e8s.
+.tapestr2ie4
+.ta8r9p8a8u9l8i8n.
+.t2a2r
+.tar2p
+.tar1pa
+.tarpau4l2
+.tarpaul2i
+.ta8r9p8a8u9l8i8n8s.
+.tarpaul2i2n1s2
+.te9l8e8g9r8a9p8h8e8r.
+.tele1gr
+.teleg5ra3ph4er
+.te9l8e8g9r8a9p8h8e8r8s.
+.telegraphe4r1s2
+.te8l8e9k8i9n8e8t9i8c.
+.teleki4n
+.telek1i
+.telek2ine
+.teleki3net1ic
+.te8l8e9k8i9n8e8t9i8c8s.
+.telekineti4c3s2
+.te8l8e9r8o9b8o8t9i8c8s.
+.te4l1er
+.tel4ero
+.teler5ob
+.teleroboti4c3s2
+.te8l8l9e8r.
+.tel1l
+.tel2le
+.te8l8l9e8r8s.
+.telle4r1s2
+.te8m9p8o9r8a8r9i8l8y.
+.te4m1p
+.tem1p4or
+.tempo1ra
+.tempo4raril
+.tempor2a2r
+.temporar1i
+.temporari1ly
+.te8n9u8r8e.
+.te8s8t9b8e8d.
+.tes2t1b
+.test4be2d
+.te8x8t9w8i8d8t8h.
+.3tex
+.tex1t2
+.textw4
+.textwi2
+.textw2id
+.textwid2th
+.th8a8l9a9m8u8s.
+.tha3la
+.thala3m
+.thala1mu
+.thalam2us
+.th8e8r9m8o9e8l8a8s9t8i8c.
+.th2e
+.ther3m4
+.ther1mo
+.thermo4el
+.thermoe1la
+.thermoelast2i
+.thermoelas1tic
+.ti8m8e9s8t8a8m8p.
+.ti2mes
+.times1ta
+.timesta4m1p
+.ti8m8e9s8t8a8m8p8s.
+.timestam2p1s2
+.to8o8l9k8i8t.
+.too2
+.toolk1i
+.to8o8l9k8i8t8s.
+.toolki4t1s2
+.to8p8o9g8r8a8p8h9i9c8a8l.
+.to5po4g
+.topo1gr
+.topo5graph1ic
+.topogr4aphi
+.topographi1ca
+.to8q8u8e8s.
+.to1q
+.toqu2
+.tr8a8i9t8o8r9o8u8s.
+.1tra
+.tr2ai2
+.trai1to
+.traitorou2
+.traitoro2us
+.tr8a8n8s9c8e8i8v8e8r.
+.tra2n
+.tra2n1s2
+.trans4c
+.tran4s3cei2
+.transce2iv
+.tr8a8n8s9c8e8i8v8e8r8s.
+.transceive4r1s2
+.tr8a8n8s9g8r8e8s8s.
+.tran2s3g
+.trans1gr
+.transgr2e2ss
+.tr8a8n8s9v8e8r9s8a8l.
+.tran4sv
+.transve4r1s2
+.transver1sa2
+.tr8a8n8s9v8e8r9s8a8l8s.
+.transversa2l1s2
+.tr8a8n8s9v8e8s9t8i8t8e.
+.transv4e2s
+.transvest2i
+.transvest2ite
+.tr8a8n8s9v8e8s9t8i8t8e8s.
+.transvestit4es
+.tr8a9v8e8r8s9a9b8l8e.
+.trave4r1s2
+.traver1sa2
+.traver2s1ab
+.traversab2l2
+.tr8a9v8e8r9s8a8l.
+.tr8a9v8e8r9s8a8l8s.
+.traversa2l1s2
+.tr8i9e8t8h8y8l9a8m8i8n8e.
+.tri5et
+.tr2ie4
+.triethy3la
+.triethylam1in
+.triethylam2ine
+.tr8e8a8c8h9e8r8i8e8s.
+.trea2ch
+.treache2
+.treacher1i
+.treacher2ie4
+.tr8o8u9b8a9d8o8u8r.
+.trou2
+.trouba2d
+.trouba1do
+.troubadou2
+.tu8r9k8e8y.
+.1tu
+.tu8r9k8e8y8s.
+.turkeys4
+.tu8r8n9a8r8o8u8n8d.
+.tur4n2a2r
+.tur1na
+.turnarou2
+.turnaroun2d
+.tu8r8n9a8r8o8u8n8d8s.
+.turnaroun2d1s2
+.ty8p9a8l.
+.1ty
+.ty1pa
+.typ4al
+.un9a8t9t8a8c8h8e8d.
+.un2at4
+.una4t3t2
+.unat1ta
+.unatta2ch
+.unattache2
+.unatta4ch4ed
+.un9e8r8r9i8n8g9l8y.
+.un4er
+.uner4r4
+.unerrin4g
+.unerringl2
+.unerring1ly
+.un9f8r8i8e8n8d9l8y.
+.un3f
+.unfri2
+.unfr2ie4
+.unfrien2d1ly
+.un9f8r8i8e8n8d9l8i9e8r.
+.unfriendl2ie4
+.va8g8u8e8r.
+.1va
+.vag4
+.va5guer
+.va2gue
+.va8u8d8e9v8i8l8l8e.
+.vaude1v4
+.vaude2v3i4l
+.vaude1vi
+.vaudevil1l
+.vaudevil2le
+.vi8c9a8r8s.
+.v4ic2a2r
+.vi1ca
+.vica4rs2
+.vi8l9l8a8i8n9e8s8s.
+.2vil
+.vil1l
+.villai2
+.villa4i4n
+.villa2ine
+.villai5n2e2ss
+.villai1nes
+.vi8s9u8a8l.
+.vi3su
+.visu1al
+.vi8s9u8a8l9l8y.
+.visual1l
+.visual1ly
+.vi9v8i8p9a9r8o8u8s.
+.3v2iv
+.viv2i4p
+.vivi1pa
+.vivip2a2r
+.viviparou2
+.viviparo2us
+.vo8i8c8e9p8r8i8n8t.
+.voi4
+.voi3cep
+.voicepr2
+.voiceprin4t3
+.vs8p8a8c8e.
+.v2s1pa
+.vspa4ce
+.wa8d9d8i8n8g.
+.wa2d
+.wad4d1in
+.wad1d4
+.wa8l8l9f8l8o8w8e8r.
+.wal1l
+.wal2lf
+.wallf4l2
+.wallflow1er
+.wa8l8l9f8l8o8w9e8r8s.
+.wallflowe4r1s2
+.wa8r8m9e8s8t.
+.w2a2r
+.war1m
+.war2me
+.war2mes
+.wa8s8t8e9w8a8t8e8r.
+.was4t
+.waste2w
+.waste1w5a
+.wastewa1te
+.wa8v8e9g8u8i8d8e.
+.waveg3
+.waveg2ui2
+.wavegu2id
+.wa8v8e9g8u8i8d8e8s.
+.waveguide4s2
+.wa8v8e9l8e8t.
+.wa8v8e9l8e8t8s.
+.wavele4t1s2
+.we8b9l8i8k8e.
+.w2e1b
+.web2l2
+.web3l4ik
+.we8e8k9n8i8g8h8t.
+.weekn2ig
+.we8e8k9n8i8g8h8t8s.
+.weeknigh4t1s2
+.wh8e8e8l9c8h8a8i8r.
+.whee4l1c2
+.wheel2ch
+.wheelchai2
+.wheelcha4ir
+.wh8e8e8l9c8h8a8i8r8s.
+.wheelchai4rs2
+.wh8i8c8h9e8v8e8r.
+.whi4
+.wh4i2ch
+.whiche2
+.whichev1er
+.wh8i8t8e9s8i8d8e8d.
+.wh2ite
+.whit4es
+.white1si
+.white2s2id
+.whitesi2d1ed
+.wh8i8t8e9s8p8a8c8e.
+.white1sp
+.white2s1pa
+.whitespa4ce
+.wh8i8t8e9s8p8a8c8e8s.
+.wi8d8e9s8p8r8e8a8d.
+.w2id
+.wide4s2
+.wide1sp
+.wides4pre
+.widespr2
+.widesprea2d1
+.wi8n8g9s8p8a8n.
+.win4g
+.wings2
+.wing2s1pa
+.wingspa4n
+.wi8n8g9s8p8a8n8s.
+.wingspa2n1s2
+.wi8n8g9s8p8r8e8a8d.
+.wingspr2
+.wingsprea2d1
+.wi8t8c8h9c8r8a8f8t.
+.wi4tc
+.wit4ch
+.witchcra2f4t
+.witchcra2f
+.wo8r8d9s8p8a8c9i8n8g.
+.1wo2
+.wor2d1s2
+.words4p
+.word2s1pa
+.wordsp4a2ci
+.wordspa2c1in
+.wordspac1ing
+.wo8r8k9a8r8o8u8n8d.
+.work2a2r
+.workarou2
+.workaroun2d
+.wo8r8k9a8r8o8u8n8d8s.
+.workaroun2d1s2
+.wo8r8k9h8o8r8s8e.
+.workh4
+.workhor4se
+.workho4rs2
+.wo8r8k9h8o8r8s8e8s.
+.workhors3e4s
+.wr8a8p9a8r8o8u8n8d.
+.wra4
+.wrap2a2r4
+.wra1pa
+.wraparou2
+.wraparoun2d
+.wr8e8t8c8h9e8d.
+.wre4tc
+.wret4ch
+.wretche2
+.wret4ch4ed
+.wr8e8t8c8h9e8d9l8y.
+.wretche2d1ly
+.ye8s9t8e8r9y8e8a8r.
+.yes4
+.yesterye2a2r
+.al9g8e9b8r8a8i9s8c8h8e.
+.algebra2is1c
+.algebrais3ch2
+.algebraische2
+.al9l8e9g8h8e9n8y.
+.al1l
+.al2le
+.al3leg
+.alleghe2n
+.ar9k8a8n9s8a8s.
+.arka2n
+.arkan2sa2
+.arka2n1s2
+.at8p9a8s8e.
+.a4t1p
+.at1pa
+.at8p9a8s8e8s.
+.atpas1e4s
+.au8s9t8r8a8l9a8s8i8a8n.
+.a2us
+.aus1t4r
+.aus1tra
+.australas2i1a
+.australasi2a2n
+.au8t8o9m8a8t8i9s8i8e8r9t8e8r.
+.automa3tis
+.automatis2ie4
+.automatisiert3er
+.be9d8i8e9n8u8n8g.
+.4be2d
+.b4e3di
+.be5di3en
+.bed2ie4
+.bedie3nu4n
+.be8m8b8o.
+.4be5m
+.be4m5b
+.bi8b9l8i9o9g8r8a9p8h8i9s8c8h8e.
+.bibliogr4aphi
+.bibliograph2is1c
+.bibliographis3ch2
+.bibliographische2
+.bo8s9t8o8n.
+.5bos4
+.bos1to
+.bosto2n
+.br8o8w8n9i8a8n.
+.brown5i
+.brow3n4i1a
+.browni3a2n
+.br8u8n8s9w8i8c8k.
+.bru2n
+.bru2n3s4
+.brun4sw2
+.brunswi2
+.brunswick1
+.bu9d8a9p8e8s8t.
+.bu1d2a
+.ca8r9i8b9b8e8a8n.
+.car1i
+.car4ib
+.cari2b1b
+.carib2be
+.caribbea2n
+.ch8a8r8l8e8s9t8o8n.
+.char4le4
+.char1l
+.charles2
+.charl4es2to
+.charle3sto2n
+.ch8a8r9l8o8t8t8e8s9v8i8l8l8e.
+.char3lo4
+.charlo4t3t2
+.charlot4tes
+.charlotte4sv
+.charlottes2vil
+.charlottesvil1l
+.charlottesvil2le
+.co9l8u8m9b8i8a.
+.colum4bi
+.colu4m1b
+.columb2i1a
+.cz8e8c8h8o9s8l8o9v8a9k8i8a.
+.c2ze4
+.cze2ch
+.cze3cho2
+.czechos4l2
+.czechos4lov
+.czechoslo1va
+.czechoslovak1i
+.czechoslovak2i1a
+.de8l9a9w8a8r8e.
+.de1la
+.de4law
+.delaw2a2r
+.di8j8k9s8t8r8a.
+.di3j
+.dij4k1s2
+.dijkst4r
+.dijks1tra
+.du8a8n8e.
+.d1u1a
+.dua2n
+.dy9n8a9m8i9s8c8h8e.
+.5dyn
+.dy1na
+.dynam2is
+.dynam2is1c
+.dynamis3ch2
+.dynamische2
+.en8g9l8i8s8h.
+.engl2
+.englis2h
+.eu8l8e8r9i8a8n.
+.eul4e
+.eu3l4er1i
+.eule1r2i3a4
+.euleri2a2n
+.ev8a8n9s8t8o8n.
+.e1va
+.eva2n
+.evan4st
+.eva2n1s2
+.evans1to
+.evansto2n
+.fe8b9r8u9a8r8y.
+.f2e4b
+.fe3br
+.febru3a
+.febru2a2r
+.fe8s8t9s8c8h8r8i8f8t.
+.fes4t1s2
+.fest4sc
+.fests2ch2
+.festsc4hr4
+.festschr4i2ft
+.fl8o8r9i9d8a.
+.flor2id
+.flori1d2a
+.fl8o8r9i9d9i8a8n.
+.flori2di
+.florid5i2a2n
+.flori1d4i3a
+.fo8r9s8c8h8u8n8g8s9i8n9s8t8i9t8u8t.
+.fors4c
+.fors2ch2
+.forschungs2
+.forschung2s1in
+.forschungs2i2n1s2
+.forschungsinst2i
+.forschungsinsti1tu
+.fr8e8e9b8s8d.
+.fre2e1b
+.free2b5s2
+.freeb4s5d
+.fu8n8k9t8s8i8o8n8a8l.
+.3fu
+.fu4nk2
+.funk5t
+.funk4t1s2
+.funkt1s2io
+.funkt5sio2n
+.funktsio1n5a
+.ga8u8s8s9i8a8n.
+.ga2us
+.gau2ss
+.gaus1si
+.gauss2i1a
+.gaussi2a2n
+.gh8o8s8t9s8c8r8i8p8t.
+.ghos4t1s2
+.ghost4sc
+.ghostscri2
+.ghostscr2ip
+.ghostscri2p1t
+.gh8o8s8t9v8i8e8w.
+.ghos4tv
+.ghostv2ie4
+.gr8a8s8s9m8a8n8n9i8a8n.
+.gr2as
+.gra2ss
+.gras2s1m
+.grass3ma
+.grassma2n3
+.grassma4n1n2
+.grassman3n4i1a
+.grassma2nni3a2n
+.gr8e8i8f8s9w8a8l8d.
+.grei2
+.grei2f3s
+.greifsw2
+.greifswa2ld
+.gr8o8t8h8e8n9d8i8e8c8k.
+.g4ro
+.gro4th2e
+.gr4oth
+.grothe2n
+.grothend2ie4
+.grothendieck1
+.gr8u8n8d9l8e8h9r8e8n.
+.gru2n
+.grundle1h4
+.grundle4hr4
+.ha9d8a9m8a8r8d.
+.ha2d
+.ha1d2a
+.hada2m2
+.had4a1ma
+.hadam2a2r
+.ha8i9f8a.
+.hai1fa
+.ha8m8i8l9t8o8n9i8a8n.
+.ha4m
+.hami4lt
+.hamil1to
+.hamilto2n
+.hamilto3n4i1a
+.hamiltoni3a2n
+.he8l9s8i8n8k8i.
+.he2l1s2
+.hel2s1in
+.hels4i4nk2
+.helsink1i
+.he8r9m8i8t9i8a8n.
+.her3mit
+.hermi1ti
+.herm4i1t2i1a
+.hermiti2a2n
+.hi8b8b8s.
+.hi2b1b
+.hib2b5s2
+.ho8k9k8a8i9d8o.
+.h2ok
+.hokk4
+.hokkai2
+.hokka2id
+.hokkai1do
+.ja8c9k8o8w9s8k8i.
+.5ja
+.jack1
+.jackowsk2
+.jackowsk1i
+.ja8n9u9a8r8y.
+.ja2n
+.jan3u1a
+.janu2a2r
+.ja9p8a9n8e8s8e.
+.ja4p
+.ja1pa
+.japa2n
+.japa1nes
+.japane1s2e
+.ka8d9o8m9t8s8e8v.
+.ka2d
+.ka1do
+.kado4mt
+.kadom4t1s2
+.kadomt5sev
+.ka8n9s8a8s.
+.ka2n
+.kan2sa2
+.ka2n1s2
+.ka8r8l8s9r8u8h8e.
+.k2a2r
+.kar1l
+.kar2l1s2
+.karls1r
+.ko8r9t8e9w8e8g.
+.ko5r
+.kr8i8s8h8n8a.
+.kr2is
+.kr3is2h
+.kris2h1n
+.krish1na
+.kr8i8s8h9n8a9i8s8m.
+.krishnai2
+.krishnai2s1m
+.kr8i8s8h9n8a8n.
+.krishn2a2n
+.la8n9c8a8s9t8e8r.
+.lan1ca
+.lancast5er
+.le9g8e8n8d8r8e.
+.le1gen
+.legen1dr
+.legendre4
+.le8i8c8e8s9t8e8r.
+.lei2
+.le5ic
+.leices5t
+.li8p9s8c8h8i8t8z.
+.l2ip
+.li2p1s2
+.lips2ch2
+.lips3chit
+.lipschi4tz
+.li8p9s8c8h8i8t8z9i8a8n.
+.lipschit2z1i
+.lipschitz2i1a
+.lipschitzi2a2n
+.lo8j9b8a8n.
+.lo5j
+.lojba2n
+.lo8u9i9s8i9a8n8a.
+.lou2
+.lo2ui2
+.louis2i1a
+.louisi2a2n
+.louisia1na
+.ma8c9o8s.
+.ma1co
+.ma8n9c8h8e8s9t8e8r.
+.man2ch
+.manche2
+.manch1es
+.ma8r9k8o8v9i8a8n.
+.marko5vi2a2n
+.markov2i1a
+.ma8r8k8t9o8b8e8r9d8o8r8f.
+.mark5t
+.mark1to
+.markto3b
+.marktober1do
+.marktoberd4or
+.marktoberdor1f
+.ma8s8s9a9c8h8u9s8e8t8t8s.
+.ma2ss
+.mas1sa2
+.massa2ch
+.massach2us
+.massachuse4t3t2
+.massachuset4t1s2
+.ma8x9w8e8l8l.
+.maxwel4l
+.mi9c8r8o9s8o8f8t.
+.micro2so
+.microso2ft3
+.mi8n9n8e9a8p9o9l8i8s.
+.m2i4n1n2
+.minne4
+.minneapol2i
+.mi8n9n8e9s8o8t8a.
+.min1nes
+.minne1so
+.minneso1ta
+.mo8s9c8o8w.
+.mos2c
+.mos1co
+.na8c8h9r8i8c8h8t8e8n.
+.1na
+.na2ch
+.nac4hr4
+.na2chr4i2ch
+.nachricht1en
+.na8s8h9v8i8l8l8e.
+.n4as
+.nas2h
+.nash2vil
+.nashvil1l
+.nashvil2le
+.ne8t9b8s8d.
+.ne2t1b
+.net2b5s2
+.netb4s5d
+.ne8t9s8c8a8p8e.
+.ne4t1s2
+.net4sc
+.netsca4p
+.nets1ca
+.ni8j9m8e9g8e8n.
+.ni3j
+.nijme2g
+.nijme1gen
+.no8e9t8h8e8r9i8a8n.
+.3noe
+.noeth2e
+.noether1i
+.noethe1r2i3a4
+.noetheri2a2n
+.no8o8r8d9w8i8j8k8e8r9h8o8u8t.
+.noo2
+.no3ord
+.noord1w
+.noordwi2
+.noordwi3j
+.noordwijk1er
+.noordwijker1h4
+.noordwijkerhou2
+.no9v8e8m9b8e8r.
+.nove4m5b
+.op8e8n9b8s8d.
+.ope4n1b4
+.open2b5s2
+.openb4s5d
+.op8e8n9o8f8f8i8c8e.
+.op4eno
+.openo4f1f
+.openof1fi
+.pa8l8a9t8i8n8o.
+.pala2t1in
+.palat2i1no
+.pa9l8e8r9m8o.
+.paler3m4
+.paler1mo
+.pe9t8r8o8v9s8k8i.
+.petro3v
+.petrovsk2
+.petrovsk1i
+.pf8a8f8f9i8a8n.
+.4pf
+.p1fa
+.pfa2f
+.pfa4f1f4
+.pfaf1fi
+.pfaff2i3a
+.pfaffi2a2n
+.ph8i8l9a9d8e8l9p8h8i8a.
+.phi4l4ade
+.phila2d
+.philade2lp
+.philadel5phi
+.philadelph2i1a
+.ph8i8l9o9s8o8p8h9i9s8c8h8e.
+.philo2so
+.philos4op
+.philos2oph
+.philosoph2is1c
+.philosophis3ch2
+.philosophische2
+.po8i8n9c8a8r8e.
+.poin2
+.poi2
+.poinc2a2r5
+.poin1ca
+.po9t8e8n9t8i8a8l9g8l8e8i9c8h8u8n8g.
+.p4ot
+.po1ten1t
+.potent2i
+.poten1t2i1a
+.potenti2al
+.potentia4l1g4
+.potentialgl2
+.potential1gle
+.potentialglei2
+.potentialgle5ic
+.potentialgle4i2ch
+.ra9d8h8a9k8r8i8s8h9n8a8n.
+.rad1h2
+.radhakr2is
+.radhakr3is2h
+.radhakris2h1n
+.radhakrish1na
+.radhakrishn2a2n
+.ra8t8h8s9k8e8l9l8e8r.
+.r4ath
+.ra2t4h1s2
+.rathsk2
+.rath4ske
+.rathskel1l
+.rathskel2le
+.ri8e9m8a8n8n9i8a8n.
+.r2ie4
+.rie5ma2n
+.rie1ma
+.riema4n1n2
+.rieman3n4i1a
+.riema2nni3a2n
+.ry8d9b8e8r8g.
+.ry1d
+.ryd1b
+.rydberg2
+.sc8h8o8t9t8i8s8c8h8e.
+.scho4t3t2
+.schott2is1c
+.s2ch2ottis3ch2
+.schottische2
+.sc8h8r8o9d8i8n8g9e8r.
+.sc4hr4
+.schrod1in
+.schrod4inge
+.sc8h8w8a9b8a9c8h8e8r.
+.sch1w
+.schwaba2ch
+.schwabache2
+.sc8h8w8a8r8z9s8c8h8i8l8d.
+.schw2a2r
+.s2chwarzs2ch2
+.schwarzsch4il2
+.schwarzschi2ld
+.se8p9t8e8m9b8e8r.
+.se2p1t
+.sep2te
+.septe4m5b
+.st8o8k8e8s9s8c8h8e.
+.st2ok
+.stokes4
+.stok2e2ss
+.stokes2s5c
+.stokess2ch2
+.stokessche2
+.st8u8t8t9g8a8r8t.
+.stu4t3t2
+.stut4t1g
+.stutt1ga
+.stuttg2a2r
+.su8s9q8u8e9h8a8n9n8a.
+.s2us
+.susqu2
+.susque1h4
+.susqueha2n
+.susqueha4n1n2
+.susquehan1na
+.ta8u9b8e8r9i8a8n.
+.tau4b
+.taub4e
+.tau3ber
+.tauber1i
+.taube1r2i3a4
+.tauberi2a2n
+.te8c8h9n8i9s8c8h8e.
+.te2ch
+.tec2h1n
+.techn2is1c
+.te2chnis3ch2
+.technische2
+.te8n9n8e8s9s8e8e.
+.t4e4n1n2
+.tenne4
+.ten1nes
+.tenn2e2ss
+.to9m8a9s8z8e8w9s8k8i.
+.to2ma
+.tomas2ze
+.tomaszewsk2
+.tomaszewsk1i
+.ty9p8o9g8r8a8p8h8i8q8u8e.
+.ty3po
+.ty5po4g
+.typo1gr
+.typogr4aphi
+.typographiqu2
+.uk8r8a8i8n9i8a8n.
+.4uk
+.ukr2ai2
+.ukra4i4n
+.ukra2ini
+.ukrai4n4i1a
+.ukraini3a2n
+.ve8r9a8l8l9g8e9m8e8i8n9e8r8t8e.
+.veral1l
+.veral4l1g4
+.verallge1me
+.verallgemei2
+.verallgeme2ine
+.verallgemein1er
+.ve8r9e8i8n9i9g8u8n8g.
+.vere3in
+.verei2
+.vere2ini
+.verein2ig
+.vereini3gun
+.ve8r9t8e8i9l8u8n9g8e8n.
+.vertei2
+.verteilun1gen
+.vi8i8i8t8h.
+.v4i5i4
+.vi4i5i4
+.vii2ith
+.vi8i8t8h.
+.vi2ith
+.wa8h8r9s8c8h8e8i8n9l8i8c8h9k8e8i8t8s9t8h8e8o9r8i8e.
+.wa4hr4
+.wah4rs2
+.wahrs4c
+.wahrs2ch2
+.wahrsche2
+.wahrschei2
+.wahrsche4i4n1l
+.wahrs2cheinl4i2ch
+.wahrscheinlic4hk
+.wahrscheinlichkei2
+.wahrscheinlichkei4t1s2
+.wahrscheinlichkeits3th2e
+.wahrscheinlichkeitsthe1o5r
+.wahrscheinlichkeitstheor2ie4
+.we8r9n8e8r.
+.w1er
+.wer4n1er
+.we8r9t8h8e8r9i8a8n.
+.werth2e
+.werther1i
+.werthe1r2i3a4
+.wertheri2a2n
+.wi8n9c8h8e8s9t8e8r.
+.win2ch
+.winche2
+.winch1es
+.wi8r8t9s8c8h8a8f8t.
+.w4ir4
+.wir4t1s2
+.wirt4sc
+.wirts2ch2
+.wirtscha2f
+.wirtscha2ft
+.wi8s9s8e8n9s8c8h8a8f8t9l8i8c8h.
+.w4i2s1s
+.wissen4
+.wisse2n1s2
+.wissens4c
+.wissens2ch2
+.wissenscha2f
+.wissenscha2ft
+.wissenschaf2tl
+.wissens2chaftl4i2ch
+.xv8i8i8i8t8h.
+.xv4i5i4
+.xvi4i5i4
+.xvii2ith
+.xv8i8i8t8h.
+.xvi2ith
+.xx8i8i8i8r8d.
+.xx4
+.xx3i
+.xx4i5i4
+.xxi4i5i4
+.xxii4ir
+.xx8i8i8n8d.
+.xxi4ind
+.yi8n8g9y8o8n8g.
+.y1i
+.yin2gy
+.yingy1o4
+.yingyo2n
+.sh8u9x8u8e.
+.shux1u3
+.ji9s8u8a8n.
+.ji2su
+.jisua2n
+.ze8a9l8a8n8d.
+.2ze
+.zea4l
+.zea3l4and
+.zeala2n
+.ze8i8t9s8c8h8r8i8f8t.
+.zei2
+.zei4t1s2
+.zeit4sc
+.zeits2ch2
+.zeitsc4hr4
+.zeitschr4i2ft
diff --git a/core/res/assets/webkit/incognito_mode_start_page.html b/core/res/assets/webkit/incognito_mode_start_page.html
new file mode 100644
index 0000000..b070c6d
--- /dev/null
+++ b/core/res/assets/webkit/incognito_mode_start_page.html
@@ -0,0 +1,24 @@
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
+    <title>New incognito window</title>
+  </head>
+  <body>
+    <p><strong>You've gone incognito</strong>. Pages you view in this window
+      won't appear in your browser history or search history, and they won't
+      leave other traces, like cookies, on your computer after you close the
+      incognito window. Any files you download or bookmarks you create will be
+      preserved, however.</p>
+
+    <p><strong>Going incognito doesn't affect the behavior of other people,
+	servers, or software. Be wary of:</strong></p>
+
+    <ul>
+      <li>Websites that collect or share information about you</li>
+      <li>Internet service providers or employers that track the pages you visit</li>
+      <li>Malicious software that tracks your keystrokes in exchange for free smileys</li>
+      <li>Surveillance by secret agents</li>
+      <li>People standing behind you</li>
+    </ul>
+  </body>
+</html>
diff --git a/core/res/res/anim/accelerate_cubic_interpolator.xml b/core/res/res/anim/accelerate_cubic_interpolator.xml
new file mode 100644
index 0000000..c736766
--- /dev/null
+++ b/core/res/res/anim/accelerate_cubic_interpolator.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/ease_in_interpolator.xml
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+        android:factor="1.5" />
diff --git a/core/res/res/anim/accelerate_quint_interpolator.xml b/core/res/res/anim/accelerate_quint_interpolator.xml
new file mode 100644
index 0000000..5ab4909
--- /dev/null
+++ b/core/res/res/anim/accelerate_quint_interpolator.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/ease_in_interpolator.xml
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+        android:factor="2.5" />
diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml
index f1258e8..0674284 100644
--- a/core/res/res/anim/activity_close_enter.xml
+++ b/core/res/res/anim/activity_close_enter.xml
@@ -17,9 +17,8 @@
 */
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:zAdjustment="top">
-	<translate android:fromXDelta="-100%" android:toXDelta="0"
-        android:duration="@android:integer/config_shortAnimTime"/>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Do nothing. -->
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+            android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml
index bf3d8cd3..5e29df4 100644
--- a/core/res/res/anim/activity_close_exit.xml
+++ b/core/res/res/anim/activity_close_exit.xml
@@ -17,8 +17,13 @@
 */
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator">
-	<translate android:fromXDelta="0%" android:toXDelta="33%"
-        android:duration="@android:integer/config_shortAnimTime"/>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <scale android:fromXScale="1.0" android:toXScale="1.0"
+            android:fromYScale="1.0" android:toYScale="0.9"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0"
+            android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml
index a9ea381..63cc9a5 100644
--- a/core/res/res/anim/activity_open_enter.xml
+++ b/core/res/res/anim/activity_open_enter.xml
@@ -17,8 +17,13 @@
 */
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator">
-	<translate android:fromXDelta="33%" android:toXDelta="0"
-        android:duration="@android:integer/config_shortAnimTime"/>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <scale android:fromXScale="1.0" android:toXScale="1.0"
+            android:fromYScale="0.9" android:toYScale="1.0"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="0" android:toAlpha="1.0"
+            android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml
index b04b79e..0674284 100644
--- a/core/res/res/anim/activity_open_exit.xml
+++ b/core/res/res/anim/activity_open_exit.xml
@@ -17,9 +17,8 @@
 */
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:zAdjustment="top">
-	<translate android:fromXDelta="0%" android:toXDelta="-100%"
-        android:duration="@android:integer/config_shortAnimTime"/>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Do nothing. -->
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+            android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
diff --git a/core/res/res/anim/animator_fade_in.xml b/core/res/res/anim/animator_fade_in.xml
new file mode 100644
index 0000000..2a28b4d
--- /dev/null
+++ b/core/res/res/anim/animator_fade_in.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@anim/accelerate_interpolator"
+    android:valueFrom="0"
+    android:valueTo="1"
+    android:propertyName="alpha"
+    android:duration="@android:integer/config_mediumAnimTime"
+/>
diff --git a/core/res/res/anim/animator_fade_out.xml b/core/res/res/anim/animator_fade_out.xml
new file mode 100644
index 0000000..4db6591
--- /dev/null
+++ b/core/res/res/anim/animator_fade_out.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@anim/accelerate_interpolator"
+    android:valueFrom="1.0"
+    android:valueTo="0.0"
+    android:propertyName="alpha"
+    android:duration="@android:integer/config_mediumAnimTime"
+/>
diff --git a/core/res/res/anim/decelerate_cubic_interpolator.xml b/core/res/res/anim/decelerate_cubic_interpolator.xml
new file mode 100644
index 0000000..78bdb40
--- /dev/null
+++ b/core/res/res/anim/decelerate_cubic_interpolator.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/ease_in_interpolator.xml
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+        android:factor="1.5" />
diff --git a/core/res/res/anim/decelerate_quint_interpolator.xml b/core/res/res/anim/decelerate_quint_interpolator.xml
new file mode 100644
index 0000000..0e543e6
--- /dev/null
+++ b/core/res/res/anim/decelerate_quint_interpolator.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/ease_in_interpolator.xml
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+        android:factor="2.5" />
diff --git a/core/res/res/anim/fragment_close_enter.xml b/core/res/res/anim/fragment_close_enter.xml
new file mode 100644
index 0000000..edf1948
--- /dev/null
+++ b/core/res/res/anim/fragment_close_enter.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:interpolator="@anim/decelerate_interpolator"
+        android:valueFrom="0"
+        android:valueTo="1"
+        android:valueType="floatType"
+        android:propertyName="alpha"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+    <objectAnimator
+        android:interpolator="@anim/decelerate_interpolator"
+        android:valueFrom="-100"
+        android:valueTo="0"
+        android:valueType="floatType"
+        android:propertyName="translationX"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_close_exit.xml b/core/res/res/anim/fragment_close_exit.xml
new file mode 100644
index 0000000..fbba476
--- /dev/null
+++ b/core/res/res/anim/fragment_close_exit.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:interpolator="@anim/accelerate_interpolator"
+        android:valueFrom="1"
+        android:valueTo="0"
+        android:valueType="floatType"
+        android:propertyName="alpha"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+    <objectAnimator
+        android:interpolator="@anim/accelerate_interpolator"
+        android:valueFrom="0"
+        android:valueTo="100"
+        android:valueType="floatType"
+        android:propertyName="translationX"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_next_enter.xml b/core/res/res/anim/fragment_next_enter.xml
new file mode 100644
index 0000000..d2d6ec9
--- /dev/null
+++ b/core/res/res/anim/fragment_next_enter.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:valueFrom="0"
+        android:valueTo="1"
+        android:valueType="floatType"
+        android:propertyName="alpha"
+        android:duration="@android:integer/config_longAnimTime"/>
+    <objectAnimator
+        android:valueFrom="50"
+        android:valueTo="0"
+        android:valueType="floatType"
+        android:propertyName="translationY"
+        android:duration="@android:integer/config_longAnimTime"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_next_exit.xml b/core/res/res/anim/fragment_next_exit.xml
new file mode 100644
index 0000000..fbb82d9
--- /dev/null
+++ b/core/res/res/anim/fragment_next_exit.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:valueFrom="1"
+        android:valueTo="0"
+        android:valueType="floatType"
+        android:propertyName="alpha"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+    <objectAnimator
+        android:valueFrom="0"
+        android:valueTo="-50"
+        android:valueType="floatType"
+        android:propertyName="translationY"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_open_enter.xml b/core/res/res/anim/fragment_open_enter.xml
new file mode 100644
index 0000000..334f4ef
--- /dev/null
+++ b/core/res/res/anim/fragment_open_enter.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:valueFrom="0"
+        android:valueTo="1"
+        android:valueType="floatType"
+        android:propertyName="alpha"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+    <objectAnimator
+        android:valueFrom="100"
+        android:valueTo="0"
+        android:valueType="floatType"
+        android:propertyName="translationX"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_open_exit.xml b/core/res/res/anim/fragment_open_exit.xml
new file mode 100644
index 0000000..764fc39
--- /dev/null
+++ b/core/res/res/anim/fragment_open_exit.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:valueFrom="1"
+        android:valueTo="0"
+        android:valueType="floatType"
+        android:propertyName="alpha"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+    <objectAnimator
+        android:valueFrom="0"
+        android:valueTo="-100"
+        android:valueType="floatType"
+        android:propertyName="translationX"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_prev_enter.xml b/core/res/res/anim/fragment_prev_enter.xml
new file mode 100644
index 0000000..d37afd0
--- /dev/null
+++ b/core/res/res/anim/fragment_prev_enter.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:valueFrom="0"
+        android:valueTo="1"
+        android:valueType="floatType"
+        android:propertyName="alpha"
+        android:duration="@android:integer/config_longAnimTime"/>
+    <objectAnimator
+        android:valueFrom="-50"
+        android:valueTo="0"
+        android:valueType="floatType"
+        android:propertyName="translationY"
+        android:duration="@android:integer/config_longAnimTime"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/fragment_prev_exit.xml b/core/res/res/anim/fragment_prev_exit.xml
new file mode 100644
index 0000000..a445a4d
--- /dev/null
+++ b/core/res/res/anim/fragment_prev_exit.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:valueFrom="1"
+        android:valueTo="0"
+        android:valueType="floatType"
+        android:propertyName="alpha"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+    <objectAnimator
+        android:valueFrom="0"
+        android:valueTo="50"
+        android:valueType="floatType"
+        android:propertyName="translationY"
+        android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/push_down_in_no_alpha.xml b/core/res/res/anim/push_down_in_no_alpha.xml
new file mode 100644
index 0000000..045d691
--- /dev/null
+++ b/core/res/res/anim/push_down_in_no_alpha.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<translate android:fromYDelta="-100%p" android:toYDelta="0"
+            android:duration="@android:integer/config_longAnimTime"/>
+</set>
diff --git a/core/res/res/anim/push_down_out_no_alpha.xml b/core/res/res/anim/push_down_out_no_alpha.xml
new file mode 100644
index 0000000..3c2474a
--- /dev/null
+++ b/core/res/res/anim/push_down_out_no_alpha.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<translate android:fromYDelta="0" android:toYDelta="100%p"
+            android:duration="@android:integer/config_longAnimTime"/>
+</set>
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index c42ad83..155ad00 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -18,14 +18,9 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:zAdjustment="top">
-    <!-- For now stay like the normal activity transition.
-    <scale android:fromXScale="2.0" android:toXScale="1.0"
-           android:fromYScale="2.0" android:toYScale="1.0"
-           android:pivotX="100%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_shortAnimTime" />
-    -->
-    <translate android:fromXDelta="-100%" android:toXDelta="0"
-        android:duration="@android:integer/config_shortAnimTime"/>
+        android:interpolator="@anim/decelerate_interpolator">
+    <!-- Do nothing. -->
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+            android:interpolator="@anim/decelerate_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index 66d3480..77684d9 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -19,6 +19,11 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:interpolator="@anim/decelerate_interpolator">
-    <translate android:fromXDelta="0%" android:toXDelta="33%"
-        android:duration="@android:integer/config_shortAnimTime"/>
+    <scale android:fromXScale="1.0" android:toXScale="1.0"
+            android:fromYScale="1.0" android:toYScale="0.9"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0"
+            android:interpolator="@anim/decelerate_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index 66adf9f..d4140ef 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -19,6 +19,11 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:interpolator="@anim/decelerate_interpolator">
-    <translate android:fromXDelta="33%" android:toXDelta="0"
-        android:duration="@android:integer/config_shortAnimTime"/>
+    <scale android:fromXScale="1.0" android:toXScale="1.0"
+            android:fromYScale="0.9" android:toYScale="1.0"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="0" android:toAlpha="1.0"
+            android:interpolator="@anim/accelerate_decelerate_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index 4a2cef4..155ad00 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -18,14 +18,9 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/decelerate_interpolator"
-        android:zAdjustment="top">
-    <!-- For now stay like the normal activity transition.
-    <scale android:fromXScale="1.0" android:toXScale="2.0"
-           android:fromYScale="1.0" android:toYScale="2.0"
-           android:pivotX="100%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_shortAnimTime" />
-    -->
-    <translate android:fromXDelta="0%" android:toXDelta="-100%"
-        android:duration="@android:integer/config_shortAnimTime"/>
+        android:interpolator="@anim/decelerate_interpolator">
+    <!-- Do nothing. -->
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+            android:interpolator="@anim/decelerate_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
diff --git a/core/res/res/anim/wallpaper_close_enter.xml b/core/res/res/anim/wallpaper_close_enter.xml
index 6eb2a89..38eac97 100644
--- a/core/res/res/anim/wallpaper_close_enter.xml
+++ b/core/res/res/anim/wallpaper_close_enter.xml
@@ -17,8 +17,26 @@
 */
 -->
 
+<!-- New holo animation, zooming contents on top of wallpaper down. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:zAdjustment="top">
+    <scale android:fromXScale="1.0" android:toXScale="1.0"
+            android:fromYScale=".9" android:toYScale="1.0"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:fillEnabled="true" android:fillBefore="true"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:startOffset="@android:integer/config_mediumAnimTime"
+            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="0" android:toAlpha="1.0"
+            android:fillEnabled="true" android:fillBefore="true"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:startOffset="@android:integer/config_mediumAnimTime"
+            android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
+
 <!-- This version zooms the new non-wallpaper up out of the
      wallpaper, without zooming the wallpaper itself. -->
+<!--
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:zAdjustment="top">
     <scale android:fromXScale=".5" android:toXScale="1.0"
@@ -30,6 +48,7 @@
            android:interpolator="@anim/accelerate_decelerate_interpolator"
            android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
+-->
 
 <!-- This version zooms the new non-wallpaper down on top of the
      wallpaper, without zooming the wallpaper itself. -->
diff --git a/core/res/res/anim/wallpaper_close_exit.xml b/core/res/res/anim/wallpaper_close_exit.xml
index eccce81..f8805a2 100644
--- a/core/res/res/anim/wallpaper_close_exit.xml
+++ b/core/res/res/anim/wallpaper_close_exit.xml
@@ -17,8 +17,28 @@
 */
 -->
 
+<!-- New holo animation, zooming contents on top of wallpaper down. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:detachWallpaper="true">
+    <scale android:fromXScale="1.0" android:toXScale="0.8"
+            android:fromYScale="1.0" android:toYScale="0.8"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:fillEnabled="true" android:fillAfter="true"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0"
+            android:fillEnabled="true" android:fillAfter="true"
+            android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime"/>
+    <!-- This is just to keep the animation running for the entire duration. -->
+    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+            android:startOffset="@android:integer/config_mediumAnimTime"
+            android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
+
 <!-- This version zooms the new non-wallpaper up out of the
      wallpaper, without zooming the wallpaper itself. -->
+<!--
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:interpolator="@anim/decelerate_interpolator"
         android:detachWallpaper="true">
@@ -27,6 +47,7 @@
            android:pivotX="50%p" android:pivotY="50%p"
            android:duration="@android:integer/config_mediumAnimTime" />
 </set>
+-->
 
 <!-- This version zooms the new non-wallpaper down on top of the
      wallpaper, without zooming the wallpaper itself. -->
diff --git a/core/res/res/anim/wallpaper_intra_close_enter.xml b/core/res/res/anim/wallpaper_intra_close_enter.xml
index 5c4f5c9..73bf9a3 100644
--- a/core/res/res/anim/wallpaper_intra_close_enter.xml
+++ b/core/res/res/anim/wallpaper_intra_close_enter.xml
@@ -20,12 +20,6 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:interpolator="@anim/decelerate_interpolator"
         android:zAdjustment="top">
-    <scale android:fromXScale="2.0" android:toXScale="1.0"
-           android:fromYScale="2.0" android:toYScale="1.0"
-           android:pivotX="100%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-	<translate android:fromXDelta="-150%p" android:toXDelta="0%p"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+            android:duration="@android:integer/config_shortAnimTime" />
 </set>
diff --git a/core/res/res/anim/wallpaper_intra_close_exit.xml b/core/res/res/anim/wallpaper_intra_close_exit.xml
index acaf773..d43660e 100644
--- a/core/res/res/anim/wallpaper_intra_close_exit.xml
+++ b/core/res/res/anim/wallpaper_intra_close_exit.xml
@@ -18,13 +18,7 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/accelerate_interpolator">
-    <scale android:fromXScale="1.0" android:toXScale=".5"
-           android:fromYScale="1.0" android:toYScale=".5"
-           android:pivotX="100%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-	<translate android:fromXDelta="0%p" android:toXDelta="100%p"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+        android:interpolator="@anim/decelerate_interpolator">
+    <alpha android:fromAlpha="0.5" android:toAlpha="0.0"
+            android:duration="@android:integer/config_shortAnimTime"/>
 </set>
diff --git a/core/res/res/anim/wallpaper_intra_open_enter.xml b/core/res/res/anim/wallpaper_intra_open_enter.xml
index 81c9991..c85ca4c 100644
--- a/core/res/res/anim/wallpaper_intra_open_enter.xml
+++ b/core/res/res/anim/wallpaper_intra_open_enter.xml
@@ -19,12 +19,6 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:interpolator="@anim/decelerate_interpolator">
-    <scale android:fromXScale=".5" android:toXScale="1.0"
-           android:fromYScale=".5" android:toYScale="1.0"
-           android:pivotX="100%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-    <translate android:fromXDelta="100%p" android:toXDelta="0"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+            android:duration="@android:integer/config_shortAnimTime" />
 </set>
diff --git a/core/res/res/anim/wallpaper_intra_open_exit.xml b/core/res/res/anim/wallpaper_intra_open_exit.xml
index 28c4287..eaeac22 100644
--- a/core/res/res/anim/wallpaper_intra_open_exit.xml
+++ b/core/res/res/anim/wallpaper_intra_open_exit.xml
@@ -18,14 +18,8 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/accelerate_interpolator"
+        android:interpolator="@anim/decelerate_interpolator"
         android:zAdjustment="top">
-    <scale android:fromXScale="1.0" android:toXScale="2.0"
-           android:fromYScale="1.0" android:toYScale="2.0"
-           android:pivotX="100%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-    <translate android:fromXDelta="0" android:toXDelta="-150%p"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+    <alpha android:fromAlpha="0.5" android:toAlpha="0.0"
+            android:duration="@android:integer/config_shortAnimTime"/>
 </set>
diff --git a/core/res/res/anim/wallpaper_open_enter.xml b/core/res/res/anim/wallpaper_open_enter.xml
index a32c39e..f150900 100644
--- a/core/res/res/anim/wallpaper_open_enter.xml
+++ b/core/res/res/anim/wallpaper_open_enter.xml
@@ -17,8 +17,25 @@
 */
 -->
 
+<!-- New holo animation, zooming contents on top of wallpaper back up. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:detachWallpaper="true">
+    <scale android:fromXScale="0.8" android:toXScale="1.0"
+            android:fromYScale="0.8" android:toYScale="1.0"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:startOffset="@android:integer/config_mediumAnimTime"
+            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="0" android:toAlpha="1.0"
+            android:interpolator="@anim/decelerate_cubic_interpolator"
+            android:startOffset="@android:integer/config_mediumAnimTime"
+            android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
+
+
 <!-- This version zooms the exiting non-wallpaper down in to the
      wallpaper, without zooming the wallpaper itself. -->
+<!--
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:interpolator="@anim/decelerate_interpolator"
         android:detachWallpaper="true">
@@ -27,6 +44,7 @@
            android:pivotX="50%p" android:pivotY="50%p"
            android:duration="@android:integer/config_mediumAnimTime" />
 </set>
+-->
 
 <!-- This version zooms the new non-wallpaper down on top of the
      wallpaper, without zooming the wallpaper itself. -->
diff --git a/core/res/res/anim/wallpaper_open_exit.xml b/core/res/res/anim/wallpaper_open_exit.xml
index 8f0e012..c9d5310 100644
--- a/core/res/res/anim/wallpaper_open_exit.xml
+++ b/core/res/res/anim/wallpaper_open_exit.xml
@@ -16,8 +16,23 @@
 ** limitations under the License.
 */
 -->
+
+<!-- New holo animation, zooming contents on top of wallpaper back up. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:zAdjustment="top">
+    <scale android:fromXScale="1.0" android:toXScale="1.0"
+            android:fromYScale="1.0" android:toYScale=".9"
+            android:pivotX="50%p" android:pivotY="50%p"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0"
+            android:interpolator="@anim/decelerate_quint_interpolator"
+            android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
+
 <!-- This version zooms the exiting non-wallpaper down in to the
      wallpaper, without zooming the wallpaper itself. -->
+<!--
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:zAdjustment="top">
     <scale android:fromXScale="1.0" android:toXScale=".5"
@@ -29,6 +44,7 @@
            android:interpolator="@anim/accelerate_decelerate_interpolator"
             android:duration="@android:integer/config_mediumAnimTime"/>
 </set>
+-->
 
 <!-- This version zooms the new non-wallpaper down on top of the
      wallpaper, without zooming the wallpaper itself. -->
diff --git a/core/res/res/color/primary_text_dark.xml b/core/res/res/color/primary_text_dark.xml
index f0812ed..3dd73e7 100644
--- a/core/res/res/color/primary_text_dark.xml
+++ b/core/res/res/color/primary_text_dark.xml
@@ -19,8 +19,6 @@
     <item android:state_window_focused="false" android:color="@android:color/bright_foreground_dark"/>
     <item android:state_pressed="true" android:color="@android:color/bright_foreground_dark_inverse"/>
     <item android:state_selected="true" android:color="@android:color/bright_foreground_dark_inverse"/>
-    <item android:state_focused="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
     <item android:color="@android:color/bright_foreground_dark"/> <!-- not selected -->
-
 </selector>
-
diff --git a/core/res/res/color/primary_text_dark_focused.xml b/core/res/res/color/primary_text_dark_focused.xml
index 7f3906a..c97c0bd 100644
--- a/core/res/res/color/primary_text_dark_focused.xml
+++ b/core/res/res/color/primary_text_dark_focused.xml
@@ -16,6 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_selected="true" android:color="@android:color/bright_foreground_dark_inverse" />
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse" />
     <item android:state_focused="true" android:color="@android:color/bright_foreground_dark_inverse" />
     <item android:state_pressed="true" android:color="@android:color/bright_foreground_dark_inverse" />
     <item android:color="@android:color/bright_foreground_dark" />
diff --git a/core/res/res/color/primary_text_dark_nodisable.xml b/core/res/res/color/primary_text_dark_nodisable.xml
index be1b9f9..443f7f4 100644
--- a/core/res/res/color/primary_text_dark_nodisable.xml
+++ b/core/res/res/color/primary_text_dark_nodisable.xml
@@ -16,6 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_selected="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
     <item android:color="@android:color/bright_foreground_dark"/> <!-- not selected -->
 </selector>
 
diff --git a/core/res/res/color/primary_text_disable_only_holo_dark.xml b/core/res/res/color/primary_text_disable_only_holo_dark.xml
new file mode 100644
index 0000000..6de4583
--- /dev/null
+++ b/core/res/res/color/primary_text_disable_only_holo_dark.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@android:color/bright_foreground_dark_disabled"/>
+    <item android:color="@android:color/bright_foreground_dark"/>
+</selector>
+
diff --git a/core/res/res/color/primary_text_disable_only_holo_light.xml b/core/res/res/color/primary_text_disable_only_holo_light.xml
new file mode 100644
index 0000000..87a92c8
--- /dev/null
+++ b/core/res/res/color/primary_text_disable_only_holo_light.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@android:color/bright_foreground_light_disabled"/>
+    <item android:color="@android:color/bright_foreground_light"/>
+</selector>
+
diff --git a/core/res/res/color/primary_text_focused_holo_dark.xml b/core/res/res/color/primary_text_focused_holo_dark.xml
new file mode 100644
index 0000000..80c68a4
--- /dev/null
+++ b/core/res/res/color/primary_text_focused_holo_dark.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="@android:color/bright_foreground_dark_inverse" />
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse" />
+    <item android:state_focused="true" android:color="@android:color/bright_foreground_dark_inverse" />
+    <item android:state_pressed="true" android:color="@android:color/bright_foreground_dark_inverse" />
+    <item android:color="@android:color/bright_foreground_dark" />
+</selector>
+
diff --git a/core/res/res/color/primary_text_holo_dark.xml b/core/res/res/color/primary_text_holo_dark.xml
new file mode 100644
index 0000000..6418664
--- /dev/null
+++ b/core/res/res/color/primary_text_holo_dark.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@android:color/bright_foreground_disabled_holo_dark"/>
+    <item android:state_window_focused="false" android:color="@android:color/bright_foreground_holo_dark"/>
+    <item android:state_pressed="true" android:color="@android:color/bright_foreground_holo_dark"/>
+    <item android:state_selected="true" android:color="@android:color/bright_foreground_holo_dark"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_holo_dark"/>
+    <item android:color="@android:color/bright_foreground_holo_dark"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/color/primary_text_holo_light.xml b/core/res/res/color/primary_text_holo_light.xml
new file mode 100644
index 0000000..86f8f86
--- /dev/null
+++ b/core/res/res/color/primary_text_holo_light.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@android:color/bright_foreground_disabled_holo_light"/>
+    <item android:state_window_focused="false" android:color="@android:color/bright_foreground_holo_light"/>
+    <item android:state_pressed="true" android:color="@android:color/bright_foreground_holo_light"/>
+    <item android:state_selected="true" android:color="@android:color/bright_foreground_holo_light"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_holo_light"/>
+    <item android:color="@android:color/bright_foreground_holo_light"/> <!-- not selected -->
+    
+</selector>
+
diff --git a/core/res/res/color/primary_text_light.xml b/core/res/res/color/primary_text_light.xml
index e112034..a12c6b4 100644
--- a/core/res/res/color/primary_text_light.xml
+++ b/core/res/res/color/primary_text_light.xml
@@ -19,6 +19,7 @@
     <item android:state_window_focused="false" android:color="@android:color/bright_foreground_light"/>
     <item android:state_pressed="true" android:color="@android:color/bright_foreground_light"/>
     <item android:state_selected="true" android:color="@android:color/bright_foreground_light"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_light"/>
     <item android:color="@android:color/bright_foreground_light"/> <!-- not selected -->
     
 </selector>
diff --git a/core/res/res/color/primary_text_light_nodisable.xml b/core/res/res/color/primary_text_light_nodisable.xml
index 2d35470..051cccf 100644
--- a/core/res/res/color/primary_text_light_nodisable.xml
+++ b/core/res/res/color/primary_text_light_nodisable.xml
@@ -16,6 +16,7 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_selected="true" android:color="@android:color/bright_foreground_light"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_light"/>
     <item android:color="@android:color/bright_foreground_light"/> <!-- not selected -->
 </selector>
 
diff --git a/core/res/res/color/primary_text_nodisable_holo_dark.xml b/core/res/res/color/primary_text_nodisable_holo_dark.xml
new file mode 100644
index 0000000..f45088f
--- /dev/null
+++ b/core/res/res/color/primary_text_nodisable_holo_dark.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+    <item android:color="@android:color/bright_foreground_dark"/> <!-- not selected -->
+</selector>
+
diff --git a/core/res/res/color/primary_text_nodisable_holo_light.xml b/core/res/res/color/primary_text_nodisable_holo_light.xml
new file mode 100644
index 0000000..331e8c3
--- /dev/null
+++ b/core/res/res/color/primary_text_nodisable_holo_light.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="@android:color/bright_foreground_light"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_light"/>
+    <item android:color="@android:color/bright_foreground_light"/> <!-- not selected -->
+</selector>
+
diff --git a/core/res/res/color/search_url_text_holo.xml b/core/res/res/color/search_url_text_holo.xml
new file mode 100644
index 0000000..78093ba
--- /dev/null
+++ b/core/res/res/color/search_url_text_holo.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:color="@android:color/search_url_text_pressed"/>
+    <item android:state_selected="true" android:color="@android:color/search_url_text_selected"/>
+    <item android:color="@android:color/search_url_text_normal"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/color/secondary_text_dark.xml b/core/res/res/color/secondary_text_dark.xml
index c195ef0..1a38fa9 100644
--- a/core/res/res/color/secondary_text_dark.xml
+++ b/core/res/res/color/secondary_text_dark.xml
@@ -20,6 +20,7 @@
     <item android:state_selected="true" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_inverse_disabled"/>
     <item android:state_pressed="true" android:state_enabled="false" android:color="@android:color/dim_foreground_dark_inverse_disabled"/>
     <item android:state_selected="true" android:color="@android:color/dim_foreground_dark_inverse"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
     <item android:state_pressed="true" android:color="@android:color/dim_foreground_dark_inverse"/>
     <item android:state_enabled="false" android:color="@android:color/dim_foreground_dark_disabled"/>
     <item android:color="@android:color/dim_foreground_dark"/> <!-- not selected -->
diff --git a/core/res/res/color/secondary_text_dark_nodisable.xml b/core/res/res/color/secondary_text_dark_nodisable.xml
index 2c87a25..cbc7b02 100644
--- a/core/res/res/color/secondary_text_dark_nodisable.xml
+++ b/core/res/res/color/secondary_text_dark_nodisable.xml
@@ -16,5 +16,6 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_selected="true" android:color="@android:color/dim_foreground_dark_inverse"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
     <item android:color="@android:color/dim_foreground_dark"/> <!-- not selected -->
 </selector>
diff --git a/core/res/res/color/secondary_text_holo_dark.xml b/core/res/res/color/secondary_text_holo_dark.xml
new file mode 100644
index 0000000..881a1de3
--- /dev/null
+++ b/core/res/res/color/secondary_text_holo_dark.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:state_enabled="false" android:color="@android:color/dim_foreground_disabled_holo_dark"/>
+    <item android:state_window_focused="false" android:color="@android:color/dim_foreground_holo_dark"/>
+    <item android:state_selected="true" android:state_enabled="false" android:color="@android:color/dim_foreground_inverse_disabled_holo_dark"/>
+    <item android:state_pressed="true" android:state_enabled="false" android:color="@android:color/dim_foreground_inverse_disabled_holo_dark"/>
+    <item android:state_selected="true" android:color="@android:color/dim_foreground_inverse_holo_dark"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_inverse_holo_dark"/>
+    <item android:state_pressed="true" android:color="@android:color/dim_foreground_inverse_holo_dark"/>
+    <item android:state_enabled="false" android:color="@android:color/dim_foreground_disabled_holo_dark"/>
+    <item android:color="@android:color/dim_foreground_holo_dark"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/color/secondary_text_holo_light.xml b/core/res/res/color/secondary_text_holo_light.xml
new file mode 100644
index 0000000..05721b2
--- /dev/null
+++ b/core/res/res/color/secondary_text_holo_light.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:state_enabled="false" android:color="@android:color/dim_foreground_disabled_holo_light"/>
+    <item android:state_window_focused="false" android:color="@android:color/dim_foreground_holo_light"/>
+    <!-- Since there is only one selector (for both light and dark), the light's selected state shouldn't be inversed like the dark's. -->
+    <item android:state_pressed="true" android:state_enabled="false" android:color="@android:color/dim_foreground_disabled_holo_light"/>
+    <item android:state_selected="true" android:state_enabled="false" android:color="@android:color/dim_foreground_disabled_holo_light"/>
+    <item android:state_pressed="true" android:color="@android:color/dim_foreground_holo_light"/>
+    <item android:state_selected="true" android:color="@android:color/dim_foreground_holo_light"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_holo_light"/>
+    <item android:state_enabled="false" android:color="@android:color/dim_foreground_disabled_holo_light"/>
+    <item android:color="@android:color/dim_foreground_holo_light"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/color/secondary_text_light.xml b/core/res/res/color/secondary_text_light.xml
index 99249a5..293f4aa 100644
--- a/core/res/res/color/secondary_text_light.xml
+++ b/core/res/res/color/secondary_text_light.xml
@@ -22,6 +22,7 @@
     <item android:state_selected="true" android:state_enabled="false" android:color="@android:color/dim_foreground_light_disabled"/>
     <item android:state_pressed="true" android:color="@android:color/dim_foreground_light"/>
     <item android:state_selected="true" android:color="@android:color/dim_foreground_light"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_light"/>
     <item android:state_enabled="false" android:color="@android:color/dim_foreground_light_disabled"/>
     <item android:color="@android:color/dim_foreground_light"/> <!-- not selected -->
 </selector>
diff --git a/core/res/res/color/secondary_text_light_nodisable.xml b/core/res/res/color/secondary_text_light_nodisable.xml
index 2c87a25..cbc7b02 100644
--- a/core/res/res/color/secondary_text_light_nodisable.xml
+++ b/core/res/res/color/secondary_text_light_nodisable.xml
@@ -16,5 +16,6 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_selected="true" android:color="@android:color/dim_foreground_dark_inverse"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
     <item android:color="@android:color/dim_foreground_dark"/> <!-- not selected -->
 </selector>
diff --git a/core/res/res/color/secondary_text_nodisable_holo_dark.xml b/core/res/res/color/secondary_text_nodisable_holo_dark.xml
new file mode 100644
index 0000000..8c22241
--- /dev/null
+++ b/core/res/res/color/secondary_text_nodisable_holo_dark.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="@android:color/dim_foreground_dark_inverse"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+    <item android:color="@android:color/dim_foreground_dark"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/color/secondary_text_nodisable_holo_light.xml b/core/res/res/color/secondary_text_nodisable_holo_light.xml
new file mode 100644
index 0000000..8c22241
--- /dev/null
+++ b/core/res/res/color/secondary_text_nodisable_holo_light.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="@android:color/dim_foreground_dark_inverse"/>
+    <item android:state_activated="true" android:color="@android:color/bright_foreground_dark_inverse"/>
+    <item android:color="@android:color/dim_foreground_dark"/> <!-- not selected -->
+</selector>
diff --git a/core/res/res/color/tertiary_text_holo_dark.xml b/core/res/res/color/tertiary_text_holo_dark.xml
new file mode 100644
index 0000000..0718d7d
--- /dev/null
+++ b/core/res/res/color/tertiary_text_holo_dark.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="#808080"/>
+    <item android:state_window_focused="false" android:color="#808080"/>
+    <item android:state_pressed="true" android:color="#808080"/>
+    <item android:state_selected="true" android:color="@android:color/dim_foreground_holo_light"/>
+    <item android:color="#808080"/> <!-- not selected -->
+</selector>
+
diff --git a/core/res/res/color/tertiary_text_holo_light.xml b/core/res/res/color/tertiary_text_holo_light.xml
new file mode 100644
index 0000000..9e5c2d9
--- /dev/null
+++ b/core/res/res/color/tertiary_text_holo_light.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="#808080"/>
+    <item android:state_window_focused="false" android:color="#808080"/>
+    <item android:state_pressed="true" android:color="#808080"/>
+    <item android:state_selected="true" android:color="#808080"/>
+    <item android:color="#808080"/> <!-- not selected -->
+</selector>
+
diff --git a/core/res/res/color/widget_edittext_holo_dark.xml b/core/res/res/color/widget_edittext_holo_dark.xml
new file mode 100644
index 0000000..4f3eb62
--- /dev/null
+++ b/core/res/res/color/widget_edittext_holo_dark.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="false" android:color="@android:color/bright_foreground_light"/> <!-- unfocused -->
+    <item android:color="@android:color/bright_foreground_light"/>
+</selector>
diff --git a/core/res/res/color/widget_edittext_holo_light.xml b/core/res/res/color/widget_edittext_holo_light.xml
new file mode 100644
index 0000000..7b950d4
--- /dev/null
+++ b/core/res/res/color/widget_edittext_holo_light.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="false" android:color="@android:color/bright_foreground_dark"/> <!-- unfocused -->
+    <item android:color="@android:color/bright_foreground_dark"/>
+</selector>
diff --git a/core/res/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png b/core/res/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png
deleted file mode 100755
index ca76375..0000000
--- a/core/res/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off.png b/core/res/res/drawable-hdpi/btn_check_off.png
old mode 100755
new mode 100644
index bb62e6f..aad9ef7
--- a/core/res/res/drawable-hdpi/btn_check_off.png
+++ b/core/res/res/drawable-hdpi/btn_check_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable.png b/core/res/res/drawable-hdpi/btn_check_off_disable.png
old mode 100755
new mode 100644
index b346381..eaee9e0
--- a/core/res/res/drawable-hdpi/btn_check_off_disable.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
old mode 100755
new mode 100644
index 8663369..6d2c293
--- a/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_dark.png
new file mode 100644
index 0000000..d93e580
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_light.png
new file mode 100644
index 0000000..ffbe776
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_disable_holo_dark.png
new file mode 100644
index 0000000..d93e580
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_disable_holo_light.png
new file mode 100644
index 0000000..ffbe776
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disabled_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_disabled_focused_holo_dark.png
new file mode 100644
index 0000000..c0f0d167
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disabled_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disabled_focused_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_disabled_focused_holo_light.png
new file mode 100644
index 0000000..ba03d28
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disabled_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disabled_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_disabled_holo_dark.png
new file mode 100644
index 0000000..a320240
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disabled_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_disabled_holo_light.png
new file mode 100644
index 0000000..7f48c54
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_focused_holo_dark.png
new file mode 100644
index 0000000..82b7ba7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_focused_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_focused_holo_light.png
new file mode 100644
index 0000000..69ecf05
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_holo_dark.png
new file mode 100644
index 0000000..4148ac4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_holo_light.png
new file mode 100644
index 0000000..2cc946f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_normal_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_normal_holo_dark.png
new file mode 100644
index 0000000..b81d4f9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_normal_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_normal_holo_light.png
new file mode 100644
index 0000000..b74055e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed.png b/core/res/res/drawable-hdpi/btn_check_off_pressed.png
old mode 100755
new mode 100644
index 67e49df..1c442e9
--- a/core/res/res/drawable-hdpi/btn_check_off_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_dark.png
new file mode 100644
index 0000000..c18e18b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_light.png
new file mode 100644
index 0000000..258c3d3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected.png b/core/res/res/drawable-hdpi/btn_check_off_selected.png
old mode 100755
new mode 100644
index 1791d1f..b852b2c
--- a/core/res/res/drawable-hdpi/btn_check_off_selected.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_off_selected_holo_dark.png
new file mode 100644
index 0000000..f819928
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected_holo_light.png b/core/res/res/drawable-hdpi/btn_check_off_selected_holo_light.png
new file mode 100644
index 0000000..57cb512
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on.png b/core/res/res/drawable-hdpi/btn_check_on.png
old mode 100755
new mode 100644
index 15cd25e..cd5c181
--- a/core/res/res/drawable-hdpi/btn_check_on.png
+++ b/core/res/res/drawable-hdpi/btn_check_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable.png b/core/res/res/drawable-hdpi/btn_check_on_disable.png
old mode 100755
new mode 100644
index e3fe323..b4fc51a
--- a/core/res/res/drawable-hdpi/btn_check_on_disable.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
old mode 100755
new mode 100644
index fa41bb7..bf34647
--- a/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png
new file mode 100644
index 0000000..1f7aeee
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_light.png
new file mode 100644
index 0000000..1f740ad
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_disable_holo_dark.png
new file mode 100644
index 0000000..1f7aeee
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_disable_holo_light.png
new file mode 100644
index 0000000..1f740ad
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disabled_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_disabled_focused_holo_dark.png
new file mode 100644
index 0000000..e63e5a5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disabled_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disabled_focused_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_disabled_focused_holo_light.png
new file mode 100644
index 0000000..37415e0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disabled_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disabled_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_disabled_holo_dark.png
new file mode 100644
index 0000000..77a3115
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disabled_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_disabled_holo_light.png
new file mode 100644
index 0000000..51993e3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_focused_holo_dark.png
new file mode 100644
index 0000000..817adf7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_focused_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_focused_holo_light.png
new file mode 100644
index 0000000..6459240
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_holo_dark.png
new file mode 100644
index 0000000..d0c4415
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_holo_light.png
new file mode 100644
index 0000000..af84d4b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_normal_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_normal_holo_dark.png
new file mode 100644
index 0000000..bf4f3bb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_normal_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_normal_holo_light.png
new file mode 100644
index 0000000..5a2a3c1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed.png b/core/res/res/drawable-hdpi/btn_check_on_pressed.png
old mode 100755
new mode 100644
index 906e283..fa5c7a2
--- a/core/res/res/drawable-hdpi/btn_check_on_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png
new file mode 100644
index 0000000..1333cb1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png
new file mode 100644
index 0000000..4413328
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected.png b/core/res/res/drawable-hdpi/btn_check_on_selected.png
old mode 100755
new mode 100644
index eb496a8..a6a21ad
--- a/core/res/res/drawable-hdpi/btn_check_on_selected.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_selected_holo_dark.png
new file mode 100644
index 0000000..e758aab
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected_holo_light.png b/core/res/res/drawable-hdpi/btn_check_on_selected_holo_light.png
new file mode 100644
index 0000000..2edf656
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_circle_disable.png b/core/res/res/drawable-hdpi/btn_circle_disable.png
index 39652a8..d829716 100644
--- a/core/res/res/drawable-hdpi/btn_circle_disable.png
+++ b/core/res/res/drawable-hdpi/btn_circle_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_circle_disable_focused.png b/core/res/res/drawable-hdpi/btn_circle_disable_focused.png
index 1aa7ffe..c1b5b6e 100644
--- a/core/res/res/drawable-hdpi/btn_circle_disable_focused.png
+++ b/core/res/res/drawable-hdpi/btn_circle_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_circle_normal.png b/core/res/res/drawable-hdpi/btn_circle_normal.png
old mode 100755
new mode 100644
index 6011219..bf3fb5a
--- a/core/res/res/drawable-hdpi/btn_circle_normal.png
+++ b/core/res/res/drawable-hdpi/btn_circle_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_circle_pressed.png b/core/res/res/drawable-hdpi/btn_circle_pressed.png
old mode 100755
new mode 100644
index 4942e50..50e22e6
--- a/core/res/res/drawable-hdpi/btn_circle_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_circle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_circle_selected.png b/core/res/res/drawable-hdpi/btn_circle_selected.png
old mode 100755
new mode 100644
index fe49a40..cfc68fb
--- a/core/res/res/drawable-hdpi/btn_circle_selected.png
+++ b/core/res/res/drawable-hdpi/btn_circle_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_close_normal.png b/core/res/res/drawable-hdpi/btn_close_normal.png
old mode 100755
new mode 100644
index 47f11e5..38b49f1
--- a/core/res/res/drawable-hdpi/btn_close_normal.png
+++ b/core/res/res/drawable-hdpi/btn_close_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_close_pressed.png b/core/res/res/drawable-hdpi/btn_close_pressed.png
old mode 100755
new mode 100644
index 5b96b4e..aa9ea49f0
--- a/core/res/res/drawable-hdpi/btn_close_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_close_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_close_selected.png b/core/res/res/drawable-hdpi/btn_close_selected.png
old mode 100755
new mode 100644
index e27d684..870c670
--- a/core/res/res/drawable-hdpi/btn_close_selected.png
+++ b/core/res/res/drawable-hdpi/btn_close_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_code_lock_default.png b/core/res/res/drawable-hdpi/btn_code_lock_default.png
index 4469ce0..df3137f 100644
--- a/core/res/res/drawable-hdpi/btn_code_lock_default.png
+++ b/core/res/res/drawable-hdpi/btn_code_lock_default.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_code_lock_touched.png b/core/res/res/drawable-hdpi/btn_code_lock_touched.png
index 0410dd3..bf9e46a 100644
--- a/core/res/res/drawable-hdpi/btn_code_lock_touched.png
+++ b/core/res/res/drawable-hdpi/btn_code_lock_touched.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png
new file mode 100644
index 0000000..19ad5c9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png
new file mode 100644
index 0000000..180cdf5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png
new file mode 100644
index 0000000..c66bb07
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png
new file mode 100644
index 0000000..c0d3dd7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png
new file mode 100644
index 0000000..b440e15
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png
new file mode 100644
index 0000000..8769d0a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal.9.png b/core/res/res/drawable-hdpi/btn_default_normal.9.png
old mode 100755
new mode 100644
index 803651b..329ce6e
--- a/core/res/res/drawable-hdpi/btn_default_normal.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_disable.9.png b/core/res/res/drawable-hdpi/btn_default_normal_disable.9.png
old mode 100755
new mode 100644
index f4f01c7..a518c6b
--- a/core/res/res/drawable-hdpi/btn_default_normal_disable.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_normal_disable.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_disable_focused.9.png b/core/res/res/drawable-hdpi/btn_default_normal_disable_focused.9.png
old mode 100755
new mode 100644
index 5376db2..71a05b7
--- a/core/res/res/drawable-hdpi/btn_default_normal_disable_focused.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_normal_disable_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png
new file mode 100644
index 0000000..7b8e9e8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png
new file mode 100644
index 0000000..37c8028
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed.9.png b/core/res/res/drawable-hdpi/btn_default_pressed.9.png
old mode 100755
new mode 100644
index 4312c27..d9d02bf
--- a/core/res/res/drawable-hdpi/btn_default_pressed.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png
new file mode 100644
index 0000000..aa08f45
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png
new file mode 100644
index 0000000..3e99cb6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_selected.9.png b/core/res/res/drawable-hdpi/btn_default_selected.9.png
old mode 100755
new mode 100644
index 06b7790..ab7c612
--- a/core/res/res/drawable-hdpi/btn_default_selected.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_normal.9.png b/core/res/res/drawable-hdpi/btn_default_small_normal.9.png
old mode 100755
new mode 100644
index 6d3ea9a..baafed6
--- a/core/res/res/drawable-hdpi/btn_default_small_normal.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_small_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_normal_disable.9.png b/core/res/res/drawable-hdpi/btn_default_small_normal_disable.9.png
old mode 100755
new mode 100644
index 2646ba0..175197b
--- a/core/res/res/drawable-hdpi/btn_default_small_normal_disable.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_small_normal_disable.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png b/core/res/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png
old mode 100755
new mode 100644
index 013210c..ec1feff
--- a/core/res/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_pressed.9.png b/core/res/res/drawable-hdpi/btn_default_small_pressed.9.png
old mode 100755
new mode 100644
index 24cefd4..c1f9a0f
--- a/core/res/res/drawable-hdpi/btn_default_small_pressed.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_small_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_selected.9.png b/core/res/res/drawable-hdpi/btn_default_small_selected.9.png
old mode 100755
new mode 100644
index bedbceb..0ea3f40
--- a/core/res/res/drawable-hdpi/btn_default_small_selected.9.png
+++ b/core/res/res/drawable-hdpi/btn_default_small_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dialog_disable.png b/core/res/res/drawable-hdpi/btn_dialog_disable.png
old mode 100755
new mode 100644
index 4ff634b..2fc5d1a
--- a/core/res/res/drawable-hdpi/btn_dialog_disable.png
+++ b/core/res/res/drawable-hdpi/btn_dialog_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dialog_normal.png b/core/res/res/drawable-hdpi/btn_dialog_normal.png
old mode 100755
new mode 100644
index e0cc339..c4a1026
--- a/core/res/res/drawable-hdpi/btn_dialog_normal.png
+++ b/core/res/res/drawable-hdpi/btn_dialog_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dialog_pressed.png b/core/res/res/drawable-hdpi/btn_dialog_pressed.png
old mode 100755
new mode 100644
index ed8e008..846f8bf
--- a/core/res/res/drawable-hdpi/btn_dialog_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_dialog_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dialog_selected.png b/core/res/res/drawable-hdpi/btn_dialog_selected.png
old mode 100755
new mode 100644
index 9b1a100..659c289
--- a/core/res/res/drawable-hdpi/btn_dialog_selected.png
+++ b/core/res/res/drawable-hdpi/btn_dialog_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png b/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png
index 0d25b6e..c6503c7 100644
--- a/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png
+++ b/core/res/res/drawable-hdpi/btn_dropdown_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png b/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png
index e21fd75..152de8b 100644
--- a/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png
+++ b/core/res/res/drawable-hdpi/btn_dropdown_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_normal.9.png b/core/res/res/drawable-hdpi/btn_dropdown_normal.9.png
old mode 100755
new mode 100644
index f10402f..9392495
--- a/core/res/res/drawable-hdpi/btn_dropdown_normal.9.png
+++ b/core/res/res/drawable-hdpi/btn_dropdown_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_pressed.9.png b/core/res/res/drawable-hdpi/btn_dropdown_pressed.9.png
old mode 100755
new mode 100644
index 366c6e0..beaba45
--- a/core/res/res/drawable-hdpi/btn_dropdown_pressed.9.png
+++ b/core/res/res/drawable-hdpi/btn_dropdown_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_selected.9.png b/core/res/res/drawable-hdpi/btn_dropdown_selected.9.png
old mode 100755
new mode 100644
index f063c8d..ec51fc9
--- a/core/res/res/drawable-hdpi/btn_dropdown_selected.9.png
+++ b/core/res/res/drawable-hdpi/btn_dropdown_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_group_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_group_disabled_holo_dark.9.png
new file mode 100644
index 0000000..bd353ae
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_group_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_group_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_group_disabled_holo_light.9.png
new file mode 100644
index 0000000..d127b3c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_group_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_group_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_group_focused_holo_dark.9.png
new file mode 100644
index 0000000..1d1a589
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_group_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_group_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_group_focused_holo_light.9.png
new file mode 100644
index 0000000..1d1a589
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_group_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_group_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_group_pressed_holo_dark.9.png
new file mode 100644
index 0000000..8922fa4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_group_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_group_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_group_pressed_holo_light.9.png
new file mode 100644
index 0000000..53cf2f3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_group_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_media_player.9.png b/core/res/res/drawable-hdpi/btn_media_player.9.png
index c23cfb6e..bf16315 100644
--- a/core/res/res/drawable-hdpi/btn_media_player.9.png
+++ b/core/res/res/drawable-hdpi/btn_media_player.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_media_player_disabled.9.png b/core/res/res/drawable-hdpi/btn_media_player_disabled.9.png
index 45228f1..d7b8ed5 100644
--- a/core/res/res/drawable-hdpi/btn_media_player_disabled.9.png
+++ b/core/res/res/drawable-hdpi/btn_media_player_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_media_player_disabled_selected.9.png b/core/res/res/drawable-hdpi/btn_media_player_disabled_selected.9.png
index 27e4e1d..1a35c31 100644
--- a/core/res/res/drawable-hdpi/btn_media_player_disabled_selected.9.png
+++ b/core/res/res/drawable-hdpi/btn_media_player_disabled_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_media_player_pressed.9.png b/core/res/res/drawable-hdpi/btn_media_player_pressed.9.png
index 0b98507..17dd3fc 100644
--- a/core/res/res/drawable-hdpi/btn_media_player_pressed.9.png
+++ b/core/res/res/drawable-hdpi/btn_media_player_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_media_player_selected.9.png b/core/res/res/drawable-hdpi/btn_media_player_selected.9.png
index e43fa8a..a146d8f 100644
--- a/core/res/res/drawable-hdpi/btn_media_player_selected.9.png
+++ b/core/res/res/drawable-hdpi/btn_media_player_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_disabled_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_disabled_off_holo_dark.png
new file mode 100644
index 0000000..4fdf5ce
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_disabled_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_disabled_off_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_disabled_off_holo_light.png
new file mode 100644
index 0000000..e70f8c3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_disabled_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_disabled_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_disabled_on_holo_dark.png
new file mode 100644
index 0000000..c28d6cd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_disabled_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_disabled_on_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_disabled_on_holo_light.png
new file mode 100644
index 0000000..ebf4da6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_disabled_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_focused_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_focused_off_holo_dark.png
new file mode 100644
index 0000000..9a5455c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_focused_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_focused_off_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_focused_off_holo_light.png
new file mode 100644
index 0000000..9648df2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_focused_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_focused_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_focused_on_holo_dark.png
new file mode 100644
index 0000000..cd59fab
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_focused_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_focused_on_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_focused_on_holo_light.png
new file mode 100644
index 0000000..4da6192
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_focused_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_normal_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_normal_off_holo_dark.png
new file mode 100644
index 0000000..f18c6da
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_normal_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_normal_off_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_normal_off_holo_light.png
new file mode 100644
index 0000000..70afefc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_normal_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_normal_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_normal_on_holo_dark.png
new file mode 100644
index 0000000..fdf1586
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_normal_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_normal_on_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_normal_on_holo_light.png
new file mode 100644
index 0000000..457038a6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_normal_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off.png b/core/res/res/drawable-hdpi/btn_radio_off.png
old mode 100755
new mode 100644
index 48ee2ba..c0b14aa
--- a/core/res/res/drawable-hdpi/btn_radio_off.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_off_holo_dark.png
new file mode 100644
index 0000000..dd18b7a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_off_holo_light.png
new file mode 100644
index 0000000..66d538f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
old mode 100755
new mode 100644
index 5a4ad89..3189581
--- a/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_dark.png
new file mode 100644
index 0000000..4e777f8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_light.png
new file mode 100644
index 0000000..6062033
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected.png b/core/res/res/drawable-hdpi/btn_radio_off_selected.png
old mode 100755
new mode 100644
index 7d5c676..f337703
--- a/core/res/res/drawable-hdpi/btn_radio_off_selected.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_dark.png
new file mode 100644
index 0000000..683a883
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_light.png
new file mode 100644
index 0000000..19524ff
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on.png b/core/res/res/drawable-hdpi/btn_radio_on.png
old mode 100755
new mode 100644
index 2472c20..c90d2eb
--- a/core/res/res/drawable-hdpi/btn_radio_on.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_on_holo_dark.png
new file mode 100644
index 0000000..2e1111b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_on_holo_light.png
new file mode 100644
index 0000000..90639ec
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
old mode 100755
new mode 100644
index 98d74ce..d79450b8
--- a/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_dark.png
new file mode 100644
index 0000000..1907215
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_light.png
new file mode 100644
index 0000000..b51c7ad
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected.png b/core/res/res/drawable-hdpi/btn_radio_on_selected.png
old mode 100755
new mode 100644
index b6ab46c..db50c43
--- a/core/res/res/drawable-hdpi/btn_radio_on_selected.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_dark.png
new file mode 100644
index 0000000..06d39cc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_light.png
new file mode 100644
index 0000000..06a4314
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_pressed_off_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_pressed_off_holo_dark.png
new file mode 100644
index 0000000..aa10966
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_pressed_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_pressed_off_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_pressed_off_holo_light.png
new file mode 100644
index 0000000..8eaf738
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_pressed_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_pressed_on_holo_dark.png b/core/res/res/drawable-hdpi/btn_radio_pressed_on_holo_dark.png
new file mode 100644
index 0000000..674cc55
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_pressed_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_pressed_on_holo_light.png b/core/res/res/drawable-hdpi/btn_radio_pressed_on_holo_light.png
new file mode 100644
index 0000000..b4f8800
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_pressed_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_square_overlay_disabled.png b/core/res/res/drawable-hdpi/btn_square_overlay_disabled.png
old mode 100755
new mode 100644
index ff06697..71a037e
--- a/core/res/res/drawable-hdpi/btn_square_overlay_disabled.png
+++ b/core/res/res/drawable-hdpi/btn_square_overlay_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_square_overlay_normal.png b/core/res/res/drawable-hdpi/btn_square_overlay_normal.png
old mode 100755
new mode 100644
index c311566..bf5da22
--- a/core/res/res/drawable-hdpi/btn_square_overlay_normal.png
+++ b/core/res/res/drawable-hdpi/btn_square_overlay_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_square_overlay_pressed.png b/core/res/res/drawable-hdpi/btn_square_overlay_pressed.png
old mode 100755
new mode 100644
index ac7fdef..52a302d
--- a/core/res/res/drawable-hdpi/btn_square_overlay_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_square_overlay_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_square_overlay_selected.png b/core/res/res/drawable-hdpi/btn_square_overlay_selected.png
old mode 100755
new mode 100644
index ee54149..e065682
--- a/core/res/res/drawable-hdpi/btn_square_overlay_selected.png
+++ b/core/res/res/drawable-hdpi/btn_square_overlay_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_off_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_star_off_focused_holo_dark.png
new file mode 100644
index 0000000..15feef1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_off_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_off_focused_holo_light.png b/core/res/res/drawable-hdpi/btn_star_off_focused_holo_light.png
new file mode 100644
index 0000000..0cff364
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_off_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_off_normal_holo_dark.png b/core/res/res/drawable-hdpi/btn_star_off_normal_holo_dark.png
new file mode 100644
index 0000000..f480729
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_off_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_off_normal_holo_light.png b/core/res/res/drawable-hdpi/btn_star_off_normal_holo_light.png
new file mode 100644
index 0000000..1c632e8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_off_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_on_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_star_on_focused_holo_dark.png
new file mode 100644
index 0000000..2dd3d77
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_on_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_on_focused_holo_light.png b/core/res/res/drawable-hdpi/btn_star_on_focused_holo_light.png
new file mode 100644
index 0000000..7b10118
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_on_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_on_normal_holo_dark.png b/core/res/res/drawable-hdpi/btn_star_on_normal_holo_dark.png
new file mode 100644
index 0000000..2f863bb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_on_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_on_normal_holo_light.png b/core/res/res/drawable-hdpi/btn_star_on_normal_holo_light.png
new file mode 100644
index 0000000..1220c20
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_on_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
new file mode 100755
index 0000000..5a8c8e5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
new file mode 100755
index 0000000..2263153
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
new file mode 100755
index 0000000..dbfa16a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
new file mode 100755
index 0000000..47a43e9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
new file mode 100755
index 0000000..2787a4b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
new file mode 100755
index 0000000..c8bf8b1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_holo.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_holo.9.png
new file mode 100755
index 0000000..f903bdb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
new file mode 100755
index 0000000..eb4f17a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
new file mode 100755
index 0000000..42b434c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png
new file mode 100755
index 0000000..e362aa1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png
new file mode 100755
index 0000000..cc61414
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
new file mode 100755
index 0000000..8b813b9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
new file mode 100755
index 0000000..54686cd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
new file mode 100755
index 0000000..056f451
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
new file mode 100755
index 0000000..2c4a6dd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
new file mode 100755
index 0000000..127a9be
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
new file mode 100755
index 0000000..4853ef4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png
new file mode 100755
index 0000000..4c1d89d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
new file mode 100755
index 0000000..d767185
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
new file mode 100755
index 0000000..00e6105
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png
new file mode 100755
index 0000000..293cad3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png
new file mode 100755
index 0000000..ead4ccf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_pressed_holo_light.9.png
new file mode 100644
index 0000000..6963a0e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_divider_holo_dark.png b/core/res/res/drawable-hdpi/cab_divider_holo_dark.png
new file mode 100755
index 0000000..e2c2119
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_divider_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_divider_holo_light.png b/core/res/res/drawable-hdpi/cab_divider_holo_light.png
new file mode 100755
index 0000000..51e2295
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_divider_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_divider_vertical_dark.png b/core/res/res/drawable-hdpi/cab_divider_vertical_dark.png
new file mode 100755
index 0000000..b1f035c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_divider_vertical_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_divider_vertical_light.png b/core/res/res/drawable-hdpi/cab_divider_vertical_light.png
new file mode 100755
index 0000000..2183b12
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_divider_vertical_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_holo_dark.9.png b/core/res/res/drawable-hdpi/cab_holo_dark.9.png
new file mode 100755
index 0000000..64e2052
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_holo_light.9.png b/core/res/res/drawable-hdpi/cab_holo_light.9.png
new file mode 100755
index 0000000..4f2afcf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_ic_close_focused_holo.png b/core/res/res/drawable-hdpi/cab_ic_close_focused_holo.png
new file mode 100755
index 0000000..9d7f932
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_ic_close_focused_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_ic_close_normal_holo.png b/core/res/res/drawable-hdpi/cab_ic_close_normal_holo.png
new file mode 100755
index 0000000..641507d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_ic_close_normal_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_ic_close_pressed_holo.png b/core/res/res/drawable-hdpi/cab_ic_close_pressed_holo.png
new file mode 100755
index 0000000..ebb0fc8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_ic_close_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_disabled_off_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_disabled_off_holo_dark.png
new file mode 100644
index 0000000..6de74a7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_disabled_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_disabled_off_holo_light.png b/core/res/res/drawable-hdpi/checkbox_disabled_off_holo_light.png
new file mode 100644
index 0000000..a0e201d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_disabled_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_disabled_on_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_disabled_on_holo_dark.png
new file mode 100644
index 0000000..9bb69c4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_disabled_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_disabled_on_holo_light.png b/core/res/res/drawable-hdpi/checkbox_disabled_on_holo_light.png
new file mode 100644
index 0000000..bffc5aa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_disabled_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_focused_off_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_focused_off_holo_dark.png
new file mode 100644
index 0000000..bbe04b6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_focused_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_focused_off_holo_light.png b/core/res/res/drawable-hdpi/checkbox_focused_off_holo_light.png
new file mode 100644
index 0000000..62f1efa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_focused_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_focused_on_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_focused_on_holo_dark.png
new file mode 100644
index 0000000..c4026a8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_focused_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_focused_on_holo_light.png b/core/res/res/drawable-hdpi/checkbox_focused_on_holo_light.png
new file mode 100644
index 0000000..409aa3e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_focused_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_normal_off_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_normal_off_holo_dark.png
new file mode 100644
index 0000000..10f1bc4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_normal_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_normal_off_holo_light.png b/core/res/res/drawable-hdpi/checkbox_normal_off_holo_light.png
new file mode 100644
index 0000000..20a1efa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_normal_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_normal_on_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_normal_on_holo_dark.png
new file mode 100644
index 0000000..0a10ec8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_normal_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_normal_on_holo_light.png b/core/res/res/drawable-hdpi/checkbox_normal_on_holo_light.png
new file mode 100644
index 0000000..34b53ee
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_normal_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_off_background.png b/core/res/res/drawable-hdpi/checkbox_off_background.png
index 275cba6..a8e4785 100644
--- a/core/res/res/drawable-hdpi/checkbox_off_background.png
+++ b/core/res/res/drawable-hdpi/checkbox_off_background.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_on_background.png b/core/res/res/drawable-hdpi/checkbox_on_background.png
index a390814..800d3d5 100644
--- a/core/res/res/drawable-hdpi/checkbox_on_background.png
+++ b/core/res/res/drawable-hdpi/checkbox_on_background.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_pressed_off_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_pressed_off_holo_dark.png
new file mode 100644
index 0000000..7f14620
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_pressed_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_pressed_off_holo_light.png b/core/res/res/drawable-hdpi/checkbox_pressed_off_holo_light.png
new file mode 100644
index 0000000..cabf936
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_pressed_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_pressed_on_holo_dark.png b/core/res/res/drawable-hdpi/checkbox_pressed_on_holo_dark.png
new file mode 100644
index 0000000..bcddb31
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_pressed_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_pressed_on_holo_light.png b/core/res/res/drawable-hdpi/checkbox_pressed_on_holo_light.png
new file mode 100644
index 0000000..84160e5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_pressed_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png
new file mode 100644
index 0000000..3a84de9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png
new file mode 100644
index 0000000..bbfb9f7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png
new file mode 100644
index 0000000..af69ee5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_divider_horizontal_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_divider_horizontal_holo_dark.9.png
new file mode 100644
index 0000000..77b0999
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_divider_horizontal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_divider_horizontal_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_divider_horizontal_holo_light.9.png
new file mode 100644
index 0000000..3fde76e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_divider_horizontal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_full_holo.9.png b/core/res/res/drawable-hdpi/dialog_full_holo.9.png
new file mode 100644
index 0000000..5d2e4e1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_full_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png
new file mode 100644
index 0000000..59b9b13
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png
new file mode 100644
index 0000000..e1ec9dc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_ic_close_focused_holo_dark.png b/core/res/res/drawable-hdpi/dialog_ic_close_focused_holo_dark.png
new file mode 100644
index 0000000..0b67f24
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_ic_close_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_ic_close_focused_holo_light.png b/core/res/res/drawable-hdpi/dialog_ic_close_focused_holo_light.png
new file mode 100644
index 0000000..107ffe4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_ic_close_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_ic_close_normal_holo_dark.png b/core/res/res/drawable-hdpi/dialog_ic_close_normal_holo_dark.png
new file mode 100644
index 0000000..2711284
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_ic_close_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_ic_close_normal_holo_light.png b/core/res/res/drawable-hdpi/dialog_ic_close_normal_holo_light.png
new file mode 100644
index 0000000..1aca4b7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_ic_close_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_ic_close_pressed_holo_dark.png b/core/res/res/drawable-hdpi/dialog_ic_close_pressed_holo_dark.png
new file mode 100644
index 0000000..9e06c00
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_ic_close_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_ic_close_pressed_holo_light.png b/core/res/res/drawable-hdpi/dialog_ic_close_pressed_holo_light.png
new file mode 100644
index 0000000..f032510
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_ic_close_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo.9.png
new file mode 100644
index 0000000..dc5e79d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_middle_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png
new file mode 100644
index 0000000..a9f85ea
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png
new file mode 100644
index 0000000..c4c4652
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_top_holo.9.png b/core/res/res/drawable-hdpi/dialog_top_holo.9.png
new file mode 100644
index 0000000..0275c18
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_top_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png
new file mode 100644
index 0000000..32c2cb4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png
new file mode 100644
index 0000000..7a8fb0e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png b/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png
old mode 100755
new mode 100644
index bd1cc0e..41b776b
--- a/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png
+++ b/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png b/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png
old mode 100755
new mode 100644
index bd1cc0e..eb75a22
--- a/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png
+++ b/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png b/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png
index cb62721..55a5e53 100644
--- a/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png
+++ b/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png b/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png
old mode 100755
new mode 100644
index bd1cc0e..60e2cb2
--- a/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png
+++ b/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_dim_dark.9.png b/core/res/res/drawable-hdpi/divider_horizontal_dim_dark.9.png
index 63859f7..cf34613 100644
--- a/core/res/res/drawable-hdpi/divider_horizontal_dim_dark.9.png
+++ b/core/res/res/drawable-hdpi/divider_horizontal_dim_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_holo_dark.9.png b/core/res/res/drawable-hdpi/divider_horizontal_holo_dark.9.png
new file mode 100644
index 0000000..e8e1deb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_horizontal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_holo_light.9.png b/core/res/res/drawable-hdpi/divider_horizontal_holo_light.9.png
new file mode 100644
index 0000000..9e6cbbe
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_horizontal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_bright.9.png b/core/res/res/drawable-hdpi/divider_vertical_bright.9.png
index 99a67b9..41b776b 100644
--- a/core/res/res/drawable-hdpi/divider_vertical_bright.9.png
+++ b/core/res/res/drawable-hdpi/divider_vertical_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_bright_opaque.9.png b/core/res/res/drawable-hdpi/divider_vertical_bright_opaque.9.png
index 5c537ee..eb75a22 100644
--- a/core/res/res/drawable-hdpi/divider_vertical_bright_opaque.9.png
+++ b/core/res/res/drawable-hdpi/divider_vertical_bright_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_dark.9.png b/core/res/res/drawable-hdpi/divider_vertical_dark.9.png
index a6c9295..55a5e53 100644
--- a/core/res/res/drawable-hdpi/divider_vertical_dark.9.png
+++ b/core/res/res/drawable-hdpi/divider_vertical_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_dark_opaque.9.png b/core/res/res/drawable-hdpi/divider_vertical_dark_opaque.9.png
index 8f35315..60e2cb2 100644
--- a/core/res/res/drawable-hdpi/divider_vertical_dark_opaque.9.png
+++ b/core/res/res/drawable-hdpi/divider_vertical_dark_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_holo_dark.9.png b/core/res/res/drawable-hdpi/divider_vertical_holo_dark.9.png
new file mode 100644
index 0000000..c039428
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_vertical_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_holo_light.9.png b/core/res/res/drawable-hdpi/divider_vertical_holo_light.9.png
new file mode 100644
index 0000000..7c4a29f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_vertical_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_disabled_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/dropdown_disabled_focused_holo_dark.9.png
new file mode 100644
index 0000000..519f522
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_disabled_focused_holo_light.9.png b/core/res/res/drawable-hdpi/dropdown_disabled_focused_holo_light.9.png
new file mode 100644
index 0000000..9c181d0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/dropdown_disabled_holo_dark.9.png
new file mode 100644
index 0000000..6ca975f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/dropdown_disabled_holo_light.9.png
new file mode 100644
index 0000000..7a20af7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/dropdown_focused_holo_dark.9.png
new file mode 100644
index 0000000..a3dfb98
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_focused_holo_light.9.png b/core/res/res/drawable-hdpi/dropdown_focused_holo_light.9.png
new file mode 100644
index 0000000..766543c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_ic_arrow_disabled_focused_holo_dark.png b/core/res/res/drawable-hdpi/dropdown_ic_arrow_disabled_focused_holo_dark.png
new file mode 100644
index 0000000..53f02576
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_ic_arrow_disabled_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_ic_arrow_disabled_focused_holo_light.png b/core/res/res/drawable-hdpi/dropdown_ic_arrow_disabled_focused_holo_light.png
new file mode 100644
index 0000000..0daee9b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_ic_arrow_disabled_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_ic_arrow_disabled_holo_dark.png b/core/res/res/drawable-hdpi/dropdown_ic_arrow_disabled_holo_dark.png
new file mode 100644
index 0000000..b659926
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_ic_arrow_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_ic_arrow_disabled_holo_light.png b/core/res/res/drawable-hdpi/dropdown_ic_arrow_disabled_holo_light.png
new file mode 100644
index 0000000..e22dbfd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_ic_arrow_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_ic_arrow_focused_holo_dark.png b/core/res/res/drawable-hdpi/dropdown_ic_arrow_focused_holo_dark.png
new file mode 100644
index 0000000..0f65227
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_ic_arrow_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_ic_arrow_focused_holo_light.png b/core/res/res/drawable-hdpi/dropdown_ic_arrow_focused_holo_light.png
new file mode 100644
index 0000000..9c47d7e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_ic_arrow_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_ic_arrow_normal_holo_dark.png b/core/res/res/drawable-hdpi/dropdown_ic_arrow_normal_holo_dark.png
new file mode 100644
index 0000000..06e5b47
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_ic_arrow_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_ic_arrow_normal_holo_light.png b/core/res/res/drawable-hdpi/dropdown_ic_arrow_normal_holo_light.png
new file mode 100644
index 0000000..d362ec1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_ic_arrow_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_ic_arrow_pressed_holo_dark.png b/core/res/res/drawable-hdpi/dropdown_ic_arrow_pressed_holo_dark.png
new file mode 100644
index 0000000..d010995
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_ic_arrow_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_ic_arrow_pressed_holo_light.png b/core/res/res/drawable-hdpi/dropdown_ic_arrow_pressed_holo_light.png
new file mode 100644
index 0000000..b95f94b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_ic_arrow_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_normal_holo_dark.9.png b/core/res/res/drawable-hdpi/dropdown_normal_holo_dark.9.png
new file mode 100644
index 0000000..a4ac317
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_normal_holo_light.9.png b/core/res/res/drawable-hdpi/dropdown_normal_holo_light.9.png
new file mode 100644
index 0000000..b4ab9ad
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/dropdown_pressed_holo_dark.9.png
new file mode 100644
index 0000000..f6382c8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dropdown_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/dropdown_pressed_holo_light.9.png
new file mode 100644
index 0000000..c849e2f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dropdown_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/edit_query.png b/core/res/res/drawable-hdpi/edit_query.png
new file mode 100644
index 0000000..d3e64b2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/edit_query.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/edit_query_background_normal.9.png b/core/res/res/drawable-hdpi/edit_query_background_normal.9.png
new file mode 100644
index 0000000..c083129
--- /dev/null
+++ b/core/res/res/drawable-hdpi/edit_query_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/edit_query_background_pressed.9.png b/core/res/res/drawable-hdpi/edit_query_background_pressed.9.png
new file mode 100644
index 0000000..41f3970
--- /dev/null
+++ b/core/res/res/drawable-hdpi/edit_query_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/edit_query_background_selected.9.png b/core/res/res/drawable-hdpi/edit_query_background_selected.9.png
new file mode 100644
index 0000000..04a8d12
--- /dev/null
+++ b/core/res/res/drawable-hdpi/edit_query_background_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_embarrassed.png b/core/res/res/drawable-hdpi/emo_im_embarrassed.png
new file mode 100644
index 0000000..cf7daf7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_embarrassed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/expander_ic_maximized.9.png b/core/res/res/drawable-hdpi/expander_ic_maximized.9.png
index 2ec27af..04943aa 100644
--- a/core/res/res/drawable-hdpi/expander_ic_maximized.9.png
+++ b/core/res/res/drawable-hdpi/expander_ic_maximized.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/expander_ic_minimized.9.png b/core/res/res/drawable-hdpi/expander_ic_minimized.9.png
index 0c19bb7..7bddbce 100644
--- a/core/res/res/drawable-hdpi/expander_ic_minimized.9.png
+++ b/core/res/res/drawable-hdpi/expander_ic_minimized.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_ab_back_holo_dark.png b/core/res/res/drawable-hdpi/ic_ab_back_holo_dark.png
new file mode 100644
index 0000000..a8da981
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_ab_back_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_ab_back_holo_light.png b/core/res/res/drawable-hdpi/ic_ab_back_holo_light.png
new file mode 100644
index 0000000..af0f308
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_ab_back_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_aggregated.png b/core/res/res/drawable-hdpi/ic_aggregated.png
index 7ca15b1..b9cfc7e 100644
--- a/core/res/res/drawable-hdpi/ic_aggregated.png
+++ b/core/res/res/drawable-hdpi/ic_aggregated.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_back.png b/core/res/res/drawable-hdpi/ic_btn_back.png
new file mode 100644
index 0000000..f8b3285
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_back.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_find_next.png b/core/res/res/drawable-hdpi/ic_btn_find_next.png
new file mode 100644
index 0000000..b696a6b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_find_next.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_find_prev.png b/core/res/res/drawable-hdpi/ic_btn_find_prev.png
new file mode 100644
index 0000000..5550c5a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_find_prev.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_next.png b/core/res/res/drawable-hdpi/ic_btn_next.png
new file mode 100644
index 0000000..b2c6e1b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_next.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_round_more_disabled.png b/core/res/res/drawable-hdpi/ic_btn_round_more_disabled.png
old mode 100755
new mode 100644
index 3f1176f..0125944
--- a/core/res/res/drawable-hdpi/ic_btn_round_more_disabled.png
+++ b/core/res/res/drawable-hdpi/ic_btn_round_more_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_round_more_normal.png b/core/res/res/drawable-hdpi/ic_btn_round_more_normal.png
old mode 100755
new mode 100644
index 8abda4d..33d7f89
--- a/core/res/res/drawable-hdpi/ic_btn_round_more_normal.png
+++ b/core/res/res/drawable-hdpi/ic_btn_round_more_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_close_normal_holo.png b/core/res/res/drawable-hdpi/ic_dialog_close_normal_holo.png
new file mode 100644
index 0000000..5ee5bb8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_close_normal_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_close_pressed_holo.png b/core/res/res/drawable-hdpi/ic_dialog_close_pressed_holo.png
new file mode 100644
index 0000000..792db06
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_close_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_focused_holo.png b/core/res/res/drawable-hdpi/ic_dialog_focused_holo.png
new file mode 100644
index 0000000..e208575
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_focused_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_emergency.png b/core/res/res/drawable-hdpi/ic_emergency.png
index 89c05e3..b4465ff 100644
--- a/core/res/res/drawable-hdpi/ic_emergency.png
+++ b/core/res/res/drawable-hdpi/ic_emergency.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_input_add.png b/core/res/res/drawable-hdpi/ic_input_add.png
old mode 100755
new mode 100644
index f9ce574..d26ebac
--- a/core/res/res/drawable-hdpi/ic_input_add.png
+++ b/core/res/res/drawable-hdpi/ic_input_add.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_ff.png b/core/res/res/drawable-hdpi/ic_media_ff.png
index 0d9ecd0..b0dc05b 100644
--- a/core/res/res/drawable-hdpi/ic_media_ff.png
+++ b/core/res/res/drawable-hdpi/ic_media_ff.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_next.png b/core/res/res/drawable-hdpi/ic_media_next.png
index 5ee97b6..2552f4e 100644
--- a/core/res/res/drawable-hdpi/ic_media_next.png
+++ b/core/res/res/drawable-hdpi/ic_media_next.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_pause.png b/core/res/res/drawable-hdpi/ic_media_pause.png
index aa25ee4..d4670c2 100644
--- a/core/res/res/drawable-hdpi/ic_media_pause.png
+++ b/core/res/res/drawable-hdpi/ic_media_pause.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_play.png b/core/res/res/drawable-hdpi/ic_media_play.png
index a7cdd41..e67ec80 100644
--- a/core/res/res/drawable-hdpi/ic_media_play.png
+++ b/core/res/res/drawable-hdpi/ic_media_play.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_previous.png b/core/res/res/drawable-hdpi/ic_media_previous.png
index b581a11..05eba71 100644
--- a/core/res/res/drawable-hdpi/ic_media_previous.png
+++ b/core/res/res/drawable-hdpi/ic_media_previous.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_rew.png b/core/res/res/drawable-hdpi/ic_media_rew.png
index 4dd8739..88eed2e 100644
--- a/core/res/res/drawable-hdpi/ic_media_rew.png
+++ b/core/res/res/drawable-hdpi/ic_media_rew.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_account_list.png b/core/res/res/drawable-hdpi/ic_menu_account_list.png
index c62261f..f858d2c 100644
--- a/core/res/res/drawable-hdpi/ic_menu_account_list.png
+++ b/core/res/res/drawable-hdpi/ic_menu_account_list.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_add.png b/core/res/res/drawable-hdpi/ic_menu_add.png
index 7b0dfc5..65cc01e 100644
--- a/core/res/res/drawable-hdpi/ic_menu_add.png
+++ b/core/res/res/drawable-hdpi/ic_menu_add.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_agenda.png b/core/res/res/drawable-hdpi/ic_menu_agenda.png
index 88659ae..6bb5cc8 100644
--- a/core/res/res/drawable-hdpi/ic_menu_agenda.png
+++ b/core/res/res/drawable-hdpi/ic_menu_agenda.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_allfriends.png b/core/res/res/drawable-hdpi/ic_menu_allfriends.png
index 61c5f6c..8d11ca1 100644
--- a/core/res/res/drawable-hdpi/ic_menu_allfriends.png
+++ b/core/res/res/drawable-hdpi/ic_menu_allfriends.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_always_landscape_portrait.png b/core/res/res/drawable-hdpi/ic_menu_always_landscape_portrait.png
index 3acae27..7ae1760 100644
--- a/core/res/res/drawable-hdpi/ic_menu_always_landscape_portrait.png
+++ b/core/res/res/drawable-hdpi/ic_menu_always_landscape_portrait.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_archive.png b/core/res/res/drawable-hdpi/ic_menu_archive.png
index 48fbcc4..9ca5c62 100644
--- a/core/res/res/drawable-hdpi/ic_menu_archive.png
+++ b/core/res/res/drawable-hdpi/ic_menu_archive.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_attachment.png b/core/res/res/drawable-hdpi/ic_menu_attachment.png
index 876377f..8f11153 100644
--- a/core/res/res/drawable-hdpi/ic_menu_attachment.png
+++ b/core/res/res/drawable-hdpi/ic_menu_attachment.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_back.png b/core/res/res/drawable-hdpi/ic_menu_back.png
index 7abf819..a6cd712 100644
--- a/core/res/res/drawable-hdpi/ic_menu_back.png
+++ b/core/res/res/drawable-hdpi/ic_menu_back.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_block.png b/core/res/res/drawable-hdpi/ic_menu_block.png
index 7e716c4..e1f9c2c 100644
--- a/core/res/res/drawable-hdpi/ic_menu_block.png
+++ b/core/res/res/drawable-hdpi/ic_menu_block.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_blocked_user.png b/core/res/res/drawable-hdpi/ic_menu_blocked_user.png
index b679bf31..3dd9a4a 100644
--- a/core/res/res/drawable-hdpi/ic_menu_blocked_user.png
+++ b/core/res/res/drawable-hdpi/ic_menu_blocked_user.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_call.png b/core/res/res/drawable-hdpi/ic_menu_call.png
index 93f97ce..2ccc087 100644
--- a/core/res/res/drawable-hdpi/ic_menu_call.png
+++ b/core/res/res/drawable-hdpi/ic_menu_call.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_camera.png b/core/res/res/drawable-hdpi/ic_menu_camera.png
index 4e10e3e..5a3850f 100644
--- a/core/res/res/drawable-hdpi/ic_menu_camera.png
+++ b/core/res/res/drawable-hdpi/ic_menu_camera.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_cc.png b/core/res/res/drawable-hdpi/ic_menu_cc.png
index 62510d2..47905a5 100644
--- a/core/res/res/drawable-hdpi/ic_menu_cc.png
+++ b/core/res/res/drawable-hdpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_chat_dashboard.png b/core/res/res/drawable-hdpi/ic_menu_chat_dashboard.png
index dc22e6a..dde6741 100644
--- a/core/res/res/drawable-hdpi/ic_menu_chat_dashboard.png
+++ b/core/res/res/drawable-hdpi/ic_menu_chat_dashboard.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_clear_playlist.png b/core/res/res/drawable-hdpi/ic_menu_clear_playlist.png
index 45ce7e8..e6be48b 100644
--- a/core/res/res/drawable-hdpi/ic_menu_clear_playlist.png
+++ b/core/res/res/drawable-hdpi/ic_menu_clear_playlist.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_close_clear_cancel.png b/core/res/res/drawable-hdpi/ic_menu_close_clear_cancel.png
index 4683f61..a54ea9d 100644
--- a/core/res/res/drawable-hdpi/ic_menu_close_clear_cancel.png
+++ b/core/res/res/drawable-hdpi/ic_menu_close_clear_cancel.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_compass.png b/core/res/res/drawable-hdpi/ic_menu_compass.png
index 756bd22..104270f 100644
--- a/core/res/res/drawable-hdpi/ic_menu_compass.png
+++ b/core/res/res/drawable-hdpi/ic_menu_compass.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_compose.png b/core/res/res/drawable-hdpi/ic_menu_compose.png
index bc153fa..6ad379e 100644
--- a/core/res/res/drawable-hdpi/ic_menu_compose.png
+++ b/core/res/res/drawable-hdpi/ic_menu_compose.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_copy_dark.png b/core/res/res/drawable-hdpi/ic_menu_copy_dark.png
new file mode 100644
index 0000000..852f146
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_copy_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_copy_light.png b/core/res/res/drawable-hdpi/ic_menu_copy_light.png
new file mode 100644
index 0000000..ad09b37
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_copy_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_crop.png b/core/res/res/drawable-hdpi/ic_menu_crop.png
index 82970b8..0d4c9fe 100644
--- a/core/res/res/drawable-hdpi/ic_menu_crop.png
+++ b/core/res/res/drawable-hdpi/ic_menu_crop.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_cut_dark.png b/core/res/res/drawable-hdpi/ic_menu_cut_dark.png
new file mode 100644
index 0000000..7716a94
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_cut_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_cut_light.png b/core/res/res/drawable-hdpi/ic_menu_cut_light.png
new file mode 100644
index 0000000..bea6db1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_cut_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_day.png b/core/res/res/drawable-hdpi/ic_menu_day.png
index 39612e8..84429aa 100644
--- a/core/res/res/drawable-hdpi/ic_menu_day.png
+++ b/core/res/res/drawable-hdpi/ic_menu_day.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_delete.png b/core/res/res/drawable-hdpi/ic_menu_delete.png
index ce5ecc4..2aed26a 100644
--- a/core/res/res/drawable-hdpi/ic_menu_delete.png
+++ b/core/res/res/drawable-hdpi/ic_menu_delete.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_directions.png b/core/res/res/drawable-hdpi/ic_menu_directions.png
index 42423b3..23f6eb3 100644
--- a/core/res/res/drawable-hdpi/ic_menu_directions.png
+++ b/core/res/res/drawable-hdpi/ic_menu_directions.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_edit.png b/core/res/res/drawable-hdpi/ic_menu_edit.png
index 4748cda..602dd10 100644
--- a/core/res/res/drawable-hdpi/ic_menu_edit.png
+++ b/core/res/res/drawable-hdpi/ic_menu_edit.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_emoticons.png b/core/res/res/drawable-hdpi/ic_menu_emoticons.png
index cb520ae..2fab515 100644
--- a/core/res/res/drawable-hdpi/ic_menu_emoticons.png
+++ b/core/res/res/drawable-hdpi/ic_menu_emoticons.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_end_conversation.png b/core/res/res/drawable-hdpi/ic_menu_end_conversation.png
index e71bb36..c05a207 100644
--- a/core/res/res/drawable-hdpi/ic_menu_end_conversation.png
+++ b/core/res/res/drawable-hdpi/ic_menu_end_conversation.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_find.png b/core/res/res/drawable-hdpi/ic_menu_find.png
new file mode 100644
index 0000000..17ac694
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_find.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_forward.png b/core/res/res/drawable-hdpi/ic_menu_forward.png
index 6b1804f..606e6aa 100644
--- a/core/res/res/drawable-hdpi/ic_menu_forward.png
+++ b/core/res/res/drawable-hdpi/ic_menu_forward.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_friendslist.png b/core/res/res/drawable-hdpi/ic_menu_friendslist.png
index ab9c552..a90e573 100644
--- a/core/res/res/drawable-hdpi/ic_menu_friendslist.png
+++ b/core/res/res/drawable-hdpi/ic_menu_friendslist.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_gallery.png b/core/res/res/drawable-hdpi/ic_menu_gallery.png
index ba07941..7c528fa 100644
--- a/core/res/res/drawable-hdpi/ic_menu_gallery.png
+++ b/core/res/res/drawable-hdpi/ic_menu_gallery.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_goto.png b/core/res/res/drawable-hdpi/ic_menu_goto.png
index 49e68ce..a1acecb 100644
--- a/core/res/res/drawable-hdpi/ic_menu_goto.png
+++ b/core/res/res/drawable-hdpi/ic_menu_goto.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_help.png b/core/res/res/drawable-hdpi/ic_menu_help.png
index a5b4a2f..4300e86 100644
--- a/core/res/res/drawable-hdpi/ic_menu_help.png
+++ b/core/res/res/drawable-hdpi/ic_menu_help.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_home.png b/core/res/res/drawable-hdpi/ic_menu_home.png
index 949fd04..35cb52a 100644
--- a/core/res/res/drawable-hdpi/ic_menu_home.png
+++ b/core/res/res/drawable-hdpi/ic_menu_home.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_info_details.png b/core/res/res/drawable-hdpi/ic_menu_info_details.png
index 6b3f7fd..7696ceb 100644
--- a/core/res/res/drawable-hdpi/ic_menu_info_details.png
+++ b/core/res/res/drawable-hdpi/ic_menu_info_details.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_invite.png b/core/res/res/drawable-hdpi/ic_menu_invite.png
index f578523..3cb129f 100644
--- a/core/res/res/drawable-hdpi/ic_menu_invite.png
+++ b/core/res/res/drawable-hdpi/ic_menu_invite.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_login.png b/core/res/res/drawable-hdpi/ic_menu_login.png
index 29e2db6..d23ebf0 100644
--- a/core/res/res/drawable-hdpi/ic_menu_login.png
+++ b/core/res/res/drawable-hdpi/ic_menu_login.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_manage.png b/core/res/res/drawable-hdpi/ic_menu_manage.png
index c08e64f..c7c4cbce 100644
--- a/core/res/res/drawable-hdpi/ic_menu_manage.png
+++ b/core/res/res/drawable-hdpi/ic_menu_manage.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_mapmode.png b/core/res/res/drawable-hdpi/ic_menu_mapmode.png
index 80c2aa9..c895ccb 100644
--- a/core/res/res/drawable-hdpi/ic_menu_mapmode.png
+++ b/core/res/res/drawable-hdpi/ic_menu_mapmode.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_mark.png b/core/res/res/drawable-hdpi/ic_menu_mark.png
index e6126fb..724d787 100644
--- a/core/res/res/drawable-hdpi/ic_menu_mark.png
+++ b/core/res/res/drawable-hdpi/ic_menu_mark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_month.png b/core/res/res/drawable-hdpi/ic_menu_month.png
index 3bf3738..3e55ae6 100644
--- a/core/res/res/drawable-hdpi/ic_menu_month.png
+++ b/core/res/res/drawable-hdpi/ic_menu_month.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_more.png b/core/res/res/drawable-hdpi/ic_menu_more.png
index 052f8f2..ccecc1d 100644
--- a/core/res/res/drawable-hdpi/ic_menu_more.png
+++ b/core/res/res/drawable-hdpi/ic_menu_more.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_focused_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_focused_holo_dark.png
new file mode 100644
index 0000000..061f80a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_focused_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_focused_holo_light.png
new file mode 100644
index 0000000..d818806
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png
new file mode 100644
index 0000000..8563c1a8d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png
new file mode 100644
index 0000000..1cd2384
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_my_calendar.png b/core/res/res/drawable-hdpi/ic_menu_my_calendar.png
index afd696d..3d6ea1f3 100644
--- a/core/res/res/drawable-hdpi/ic_menu_my_calendar.png
+++ b/core/res/res/drawable-hdpi/ic_menu_my_calendar.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_mylocation.png b/core/res/res/drawable-hdpi/ic_menu_mylocation.png
index 379f15c..1bcb0cd 100644
--- a/core/res/res/drawable-hdpi/ic_menu_mylocation.png
+++ b/core/res/res/drawable-hdpi/ic_menu_mylocation.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_myplaces.png b/core/res/res/drawable-hdpi/ic_menu_myplaces.png
index 6cb7c8b..5f726d8 100644
--- a/core/res/res/drawable-hdpi/ic_menu_myplaces.png
+++ b/core/res/res/drawable-hdpi/ic_menu_myplaces.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_notifications.png b/core/res/res/drawable-hdpi/ic_menu_notifications.png
index 1b1c5ed..fb63937 100644
--- a/core/res/res/drawable-hdpi/ic_menu_notifications.png
+++ b/core/res/res/drawable-hdpi/ic_menu_notifications.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_paste_dark.png b/core/res/res/drawable-hdpi/ic_menu_paste_dark.png
new file mode 100644
index 0000000..5579443
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_paste_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_paste_light.png b/core/res/res/drawable-hdpi/ic_menu_paste_light.png
new file mode 100644
index 0000000..6674914
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_paste_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_play_clip.png b/core/res/res/drawable-hdpi/ic_menu_play_clip.png
index 439890c..ddde03a 100644
--- a/core/res/res/drawable-hdpi/ic_menu_play_clip.png
+++ b/core/res/res/drawable-hdpi/ic_menu_play_clip.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_preferences.png b/core/res/res/drawable-hdpi/ic_menu_preferences.png
index 039c721..81bca4a 100644
--- a/core/res/res/drawable-hdpi/ic_menu_preferences.png
+++ b/core/res/res/drawable-hdpi/ic_menu_preferences.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_recent_history.png b/core/res/res/drawable-hdpi/ic_menu_recent_history.png
index bbd5de3..0dd1627 100644
--- a/core/res/res/drawable-hdpi/ic_menu_recent_history.png
+++ b/core/res/res/drawable-hdpi/ic_menu_recent_history.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_refresh.png b/core/res/res/drawable-hdpi/ic_menu_refresh.png
index bbb08f1..53cacca 100644
--- a/core/res/res/drawable-hdpi/ic_menu_refresh.png
+++ b/core/res/res/drawable-hdpi/ic_menu_refresh.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_report_image.png b/core/res/res/drawable-hdpi/ic_menu_report_image.png
index 4db2187..b6aa5d6 100644
--- a/core/res/res/drawable-hdpi/ic_menu_report_image.png
+++ b/core/res/res/drawable-hdpi/ic_menu_report_image.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_revert.png b/core/res/res/drawable-hdpi/ic_menu_revert.png
index ffc67d9..11860a4 100644
--- a/core/res/res/drawable-hdpi/ic_menu_revert.png
+++ b/core/res/res/drawable-hdpi/ic_menu_revert.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_rotate.png b/core/res/res/drawable-hdpi/ic_menu_rotate.png
index 835e393..85115af 100644
--- a/core/res/res/drawable-hdpi/ic_menu_rotate.png
+++ b/core/res/res/drawable-hdpi/ic_menu_rotate.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_save.png b/core/res/res/drawable-hdpi/ic_menu_save.png
index 62d0b9a..fc26c5d 100644
--- a/core/res/res/drawable-hdpi/ic_menu_save.png
+++ b/core/res/res/drawable-hdpi/ic_menu_save.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_search.png b/core/res/res/drawable-hdpi/ic_menu_search.png
index 9154d6e..f78234e 100644
--- a/core/res/res/drawable-hdpi/ic_menu_search.png
+++ b/core/res/res/drawable-hdpi/ic_menu_search.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_send.png b/core/res/res/drawable-hdpi/ic_menu_send.png
index 9597731..2567b58 100644
--- a/core/res/res/drawable-hdpi/ic_menu_send.png
+++ b/core/res/res/drawable-hdpi/ic_menu_send.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_set_as.png b/core/res/res/drawable-hdpi/ic_menu_set_as.png
index 83a87b9..7e79c15 100644
--- a/core/res/res/drawable-hdpi/ic_menu_set_as.png
+++ b/core/res/res/drawable-hdpi/ic_menu_set_as.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_share.png b/core/res/res/drawable-hdpi/ic_menu_share.png
index b49d9ad7..b41b348 100644
--- a/core/res/res/drawable-hdpi/ic_menu_share.png
+++ b/core/res/res/drawable-hdpi/ic_menu_share.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_slideshow.png b/core/res/res/drawable-hdpi/ic_menu_slideshow.png
index 6208585..925f4eb1 100644
--- a/core/res/res/drawable-hdpi/ic_menu_slideshow.png
+++ b/core/res/res/drawable-hdpi/ic_menu_slideshow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_sort_alphabetically.png b/core/res/res/drawable-hdpi/ic_menu_sort_alphabetically.png
index 94ee3be..5d68998 100644
--- a/core/res/res/drawable-hdpi/ic_menu_sort_alphabetically.png
+++ b/core/res/res/drawable-hdpi/ic_menu_sort_alphabetically.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_sort_by_size.png b/core/res/res/drawable-hdpi/ic_menu_sort_by_size.png
index b43bc57..c9388fd 100644
--- a/core/res/res/drawable-hdpi/ic_menu_sort_by_size.png
+++ b/core/res/res/drawable-hdpi/ic_menu_sort_by_size.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_star.png b/core/res/res/drawable-hdpi/ic_menu_star.png
index 30f6314..21a2c4b 100644
--- a/core/res/res/drawable-hdpi/ic_menu_star.png
+++ b/core/res/res/drawable-hdpi/ic_menu_star.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_start_conversation.png b/core/res/res/drawable-hdpi/ic_menu_start_conversation.png
index 0dfc024..d63d3a7 100644
--- a/core/res/res/drawable-hdpi/ic_menu_start_conversation.png
+++ b/core/res/res/drawable-hdpi/ic_menu_start_conversation.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_stop.png b/core/res/res/drawable-hdpi/ic_menu_stop.png
index 310b6ca..7c99ed4 100644
--- a/core/res/res/drawable-hdpi/ic_menu_stop.png
+++ b/core/res/res/drawable-hdpi/ic_menu_stop.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_today.png b/core/res/res/drawable-hdpi/ic_menu_today.png
index 653f70cd..4a9352d 100644
--- a/core/res/res/drawable-hdpi/ic_menu_today.png
+++ b/core/res/res/drawable-hdpi/ic_menu_today.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_upload.png b/core/res/res/drawable-hdpi/ic_menu_upload.png
index 7454ba3..d845112 100644
--- a/core/res/res/drawable-hdpi/ic_menu_upload.png
+++ b/core/res/res/drawable-hdpi/ic_menu_upload.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_upload_you_tube.png b/core/res/res/drawable-hdpi/ic_menu_upload_you_tube.png
index 448f6c4..df5fa7f 100644
--- a/core/res/res/drawable-hdpi/ic_menu_upload_you_tube.png
+++ b/core/res/res/drawable-hdpi/ic_menu_upload_you_tube.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_view.png b/core/res/res/drawable-hdpi/ic_menu_view.png
index 5b9f2b9..75155d4 100644
--- a/core/res/res/drawable-hdpi/ic_menu_view.png
+++ b/core/res/res/drawable-hdpi/ic_menu_view.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_week.png b/core/res/res/drawable-hdpi/ic_menu_week.png
index c184f2e..c216eca 100644
--- a/core/res/res/drawable-hdpi/ic_menu_week.png
+++ b/core/res/res/drawable-hdpi/ic_menu_week.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_zoom.png b/core/res/res/drawable-hdpi/ic_menu_zoom.png
index c1d18d5..9fa4d7e 100644
--- a/core/res/res/drawable-hdpi/ic_menu_zoom.png
+++ b/core/res/res/drawable-hdpi/ic_menu_zoom.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_search_category_default.png b/core/res/res/drawable-hdpi/ic_search_category_default.png
index d368c54..f78234e 100644
--- a/core/res/res/drawable-hdpi/ic_search_category_default.png
+++ b/core/res/res/drawable-hdpi/ic_search_category_default.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_green_up.png
index 6f85f38..6560696 100644
--- a/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_green_up.png
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_green_up.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_red_up.png
index bec07f1..698c3ec 100644
--- a/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_red_up.png
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_red_up.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png
index c45b956..0102a61 100644
--- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png
index b9fd0a4..82ad8f7 100644
--- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png
index 94e947d..f9d0d33 100644
--- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_normal.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_normal.9.png
index b9ec237..109be42 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_left_end_normal.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_left_end_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_normal.9.png b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_normal.9.png
index 30fcda5..030c9e9 100644
--- a/core/res/res/drawable-hdpi/jog_tab_bar_right_end_normal.9.png
+++ b/core/res/res/drawable-hdpi/jog_tab_bar_right_end_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_left_confirm_gray.png b/core/res/res/drawable-hdpi/jog_tab_left_confirm_gray.png
old mode 100755
new mode 100644
index 9599fb5..3499208
--- a/core/res/res/drawable-hdpi/jog_tab_left_confirm_gray.png
+++ b/core/res/res/drawable-hdpi/jog_tab_left_confirm_gray.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_left_confirm_green.png b/core/res/res/drawable-hdpi/jog_tab_left_confirm_green.png
old mode 100755
new mode 100644
index 46d9ab3..91eaec8
--- a/core/res/res/drawable-hdpi/jog_tab_left_confirm_green.png
+++ b/core/res/res/drawable-hdpi/jog_tab_left_confirm_green.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_left_confirm_red.png b/core/res/res/drawable-hdpi/jog_tab_left_confirm_red.png
index 6c0dc0a..8818b9e 100644
--- a/core/res/res/drawable-hdpi/jog_tab_left_confirm_red.png
+++ b/core/res/res/drawable-hdpi/jog_tab_left_confirm_red.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_left_confirm_yellow.png b/core/res/res/drawable-hdpi/jog_tab_left_confirm_yellow.png
old mode 100755
new mode 100644
index 3f9fb8f..e5bc5f6
--- a/core/res/res/drawable-hdpi/jog_tab_left_confirm_yellow.png
+++ b/core/res/res/drawable-hdpi/jog_tab_left_confirm_yellow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_left_normal.png b/core/res/res/drawable-hdpi/jog_tab_left_normal.png
index d43c5e2..5326c7c 100644
--- a/core/res/res/drawable-hdpi/jog_tab_left_normal.png
+++ b/core/res/res/drawable-hdpi/jog_tab_left_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_left_pressed.png b/core/res/res/drawable-hdpi/jog_tab_left_pressed.png
old mode 100755
new mode 100644
index ec98790..7b906df
--- a/core/res/res/drawable-hdpi/jog_tab_left_pressed.png
+++ b/core/res/res/drawable-hdpi/jog_tab_left_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_right_confirm_gray.png b/core/res/res/drawable-hdpi/jog_tab_right_confirm_gray.png
old mode 100755
new mode 100644
index 2861e8d..ea8c315
--- a/core/res/res/drawable-hdpi/jog_tab_right_confirm_gray.png
+++ b/core/res/res/drawable-hdpi/jog_tab_right_confirm_gray.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_right_confirm_green.png b/core/res/res/drawable-hdpi/jog_tab_right_confirm_green.png
old mode 100755
new mode 100644
index e974bbc..aa0ceb9
--- a/core/res/res/drawable-hdpi/jog_tab_right_confirm_green.png
+++ b/core/res/res/drawable-hdpi/jog_tab_right_confirm_green.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_right_confirm_red.png b/core/res/res/drawable-hdpi/jog_tab_right_confirm_red.png
index 9647fa6..d772fb6 100644
--- a/core/res/res/drawable-hdpi/jog_tab_right_confirm_red.png
+++ b/core/res/res/drawable-hdpi/jog_tab_right_confirm_red.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_right_confirm_yellow.png b/core/res/res/drawable-hdpi/jog_tab_right_confirm_yellow.png
old mode 100755
new mode 100644
index ad878e1..3cfeb67
--- a/core/res/res/drawable-hdpi/jog_tab_right_confirm_yellow.png
+++ b/core/res/res/drawable-hdpi/jog_tab_right_confirm_yellow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_right_normal.png b/core/res/res/drawable-hdpi/jog_tab_right_normal.png
index 1eb4234..da7726b 100644
--- a/core/res/res/drawable-hdpi/jog_tab_right_normal.png
+++ b/core/res/res/drawable-hdpi/jog_tab_right_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/jog_tab_right_pressed.png b/core/res/res/drawable-hdpi/jog_tab_right_pressed.png
old mode 100755
new mode 100644
index 647e802..450a325
--- a/core/res/res/drawable-hdpi/jog_tab_right_pressed.png
+++ b/core/res/res/drawable-hdpi/jog_tab_right_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_activated_holo.9.png b/core/res/res/drawable-hdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..cd9a08e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_activated_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_divider_holo_dark.9.png b/core/res/res/drawable-hdpi/list_divider_holo_dark.9.png
new file mode 100644
index 0000000..7b18202
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_divider_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_divider_holo_light.9.png b/core/res/res/drawable-hdpi/list_divider_holo_light.9.png
new file mode 100644
index 0000000..17bcbb4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_divider_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_divider_horizontal_holo_dark.9.png b/core/res/res/drawable-hdpi/list_divider_horizontal_holo_dark.9.png
new file mode 100644
index 0000000..0a4347f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_divider_horizontal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_focused_holo.9.png b/core/res/res/drawable-hdpi/list_focused_holo.9.png
new file mode 100644
index 0000000..b91e9f2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_longpressed_holo.9.png b/core/res/res/drawable-hdpi/list_longpressed_holo.9.png
new file mode 100644
index 0000000..db4831d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_longpressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/list_pressed_holo_dark.9.png
new file mode 100644
index 0000000..1ce1601
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..b36aa2c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_section_divider_holo_dark.9.png b/core/res/res/drawable-hdpi/list_section_divider_holo_dark.9.png
new file mode 100644
index 0000000..2dabb5f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_section_divider_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_section_divider_holo_light.9.png b/core/res/res/drawable-hdpi/list_section_divider_holo_light.9.png
new file mode 100644
index 0000000..763f4f6f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_section_divider_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_section_header_holo_dark.9.png b/core/res/res/drawable-hdpi/list_section_header_holo_dark.9.png
new file mode 100644
index 0000000..2030d3b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_section_header_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_section_header_holo_light.9.png b/core/res/res/drawable-hdpi/list_section_header_holo_light.9.png
new file mode 100644
index 0000000..4ca2773
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_section_header_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/list_selected_holo_dark.9.png
new file mode 100644
index 0000000..c054e14
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selected_holo_light.9.png b/core/res/res/drawable-hdpi/list_selected_holo_light.9.png
new file mode 100644
index 0000000..77c4032
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_activated_holo_dark.9.png b/core/res/res/drawable-hdpi/list_selector_activated_holo_dark.9.png
new file mode 100644
index 0000000..1a516c1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_activated_holo_light.9.png b/core/res/res/drawable-hdpi/list_selector_activated_holo_light.9.png
new file mode 100644
index 0000000..f22217b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_default.9.png b/core/res/res/drawable-hdpi/list_selector_background_default.9.png
new file mode 100644
index 0000000..25c6241
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_default_light.9.png b/core/res/res/drawable-hdpi/list_selector_background_default_light.9.png
new file mode 100644
index 0000000..c3e69f0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_default_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png b/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png
old mode 100755
new mode 100644
index 9e1c42a..ab377d8
--- a/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png
+++ b/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_disabled_light.9.png b/core/res/res/drawable-hdpi/list_selector_background_disabled_light.9.png
new file mode 100644
index 0000000..8edc9cd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_disabled_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_focus.9.png b/core/res/res/drawable-hdpi/list_selector_background_focus.9.png
old mode 100755
new mode 100644
index 5563c80..94a9fba
--- a/core/res/res/drawable-hdpi/list_selector_background_focus.9.png
+++ b/core/res/res/drawable-hdpi/list_selector_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_focused.9.png b/core/res/res/drawable-hdpi/list_selector_background_focused.9.png
new file mode 100644
index 0000000..60bb454
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_focused_light.9.png b/core/res/res/drawable-hdpi/list_selector_background_focused_light.9.png
new file mode 100644
index 0000000..60bb454
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_focused_selected.9.png b/core/res/res/drawable-hdpi/list_selector_background_focused_selected.9.png
new file mode 100644
index 0000000..b527da1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_focused_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png b/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png
old mode 100755
new mode 100644
index 72d3a08..bd74426
--- a/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png
+++ b/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_longpress_light.9.png b/core/res/res/drawable-hdpi/list_selector_background_longpress_light.9.png
new file mode 100644
index 0000000..fc2ba2e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_longpress_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png b/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png
old mode 100755
new mode 100644
index 7568b30..d18d6f7
--- a/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png
+++ b/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_pressed_light.9.png b/core/res/res/drawable-hdpi/list_selector_background_pressed_light.9.png
new file mode 100644
index 0000000..00786e5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_pressed_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_selected.9.png b/core/res/res/drawable-hdpi/list_selector_background_selected.9.png
new file mode 100644
index 0000000..cbf50b3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_selected_light.9.png b/core/res/res/drawable-hdpi/list_selector_background_selected_light.9.png
new file mode 100644
index 0000000..34ea86e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_selected_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/list_selector_disabled_holo_dark.9.png
new file mode 100644
index 0000000..f6fd30d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/list_selector_disabled_holo_light.9.png
new file mode 100644
index 0000000..ca8e9a2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/list_selector_focused_holo_dark.9.png
new file mode 100644
index 0000000..c1f3d7d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_focused_holo_light.9.png b/core/res/res/drawable-hdpi/list_selector_focused_holo_light.9.png
new file mode 100644
index 0000000..99bb246
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_multiselect_holo_dark.9.png b/core/res/res/drawable-hdpi/list_selector_multiselect_holo_dark.9.png
new file mode 100644
index 0000000..f702fc8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_multiselect_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_multiselect_holo_light.9.png b/core/res/res/drawable-hdpi/list_selector_multiselect_holo_light.9.png
new file mode 100644
index 0000000..e8f277d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_multiselect_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/list_selector_pressed_holo_dark.9.png
new file mode 100644
index 0000000..0ed5ba3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/list_selector_pressed_holo_light.9.png
new file mode 100644
index 0000000..1471c17
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_background.9.png b/core/res/res/drawable-hdpi/menu_background.9.png
index f4c9e08..60f0731 100644
--- a/core/res/res/drawable-hdpi/menu_background.9.png
+++ b/core/res/res/drawable-hdpi/menu_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_background_fill_parent_width.9.png b/core/res/res/drawable-hdpi/menu_background_fill_parent_width.9.png
index a3cec11..09eac9b 100644
--- a/core/res/res/drawable-hdpi/menu_background_fill_parent_width.9.png
+++ b/core/res/res/drawable-hdpi/menu_background_fill_parent_width.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png
new file mode 100644
index 0000000..7ec3a33f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png
new file mode 100644
index 0000000..da1fe94
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menu_dropdown_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_submenu_background.9.png b/core/res/res/drawable-hdpi/menu_submenu_background.9.png
index c7056e0..cbd4400 100644
--- a/core/res/res/drawable-hdpi/menu_submenu_background.9.png
+++ b/core/res/res/drawable-hdpi/menu_submenu_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/minitab_lt_focus.9.png b/core/res/res/drawable-hdpi/minitab_lt_focus.9.png
new file mode 100644
index 0000000..3ba8376
--- /dev/null
+++ b/core/res/res/drawable-hdpi/minitab_lt_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/minitab_lt_press.9.png b/core/res/res/drawable-hdpi/minitab_lt_press.9.png
new file mode 100644
index 0000000..df226c7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/minitab_lt_press.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/minitab_lt_selected.9.png b/core/res/res/drawable-hdpi/minitab_lt_selected.9.png
new file mode 100644
index 0000000..bb417e6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/minitab_lt_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/minitab_lt_unselected.9.png b/core/res/res/drawable-hdpi/minitab_lt_unselected.9.png
new file mode 100644
index 0000000..d9ef49e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/minitab_lt_unselected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/minitab_lt_unselected_press.9.png b/core/res/res/drawable-hdpi/minitab_lt_unselected_press.9.png
new file mode 100644
index 0000000..383c4fc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/minitab_lt_unselected_press.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/nav_divider.png b/core/res/res/drawable-hdpi/nav_divider.png
new file mode 100644
index 0000000..7ca3e61
--- /dev/null
+++ b/core/res/res/drawable-hdpi/nav_divider.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/overscroll_edge.png b/core/res/res/drawable-hdpi/overscroll_edge.png
index e8c1aa3..58b4f19 100644
--- a/core/res/res/drawable-hdpi/overscroll_edge.png
+++ b/core/res/res/drawable-hdpi/overscroll_edge.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/overscroll_glow.png b/core/res/res/drawable-hdpi/overscroll_glow.png
index 3418384..fa58f1e 100644
--- a/core/res/res/drawable-hdpi/overscroll_glow.png
+++ b/core/res/res/drawable-hdpi/overscroll_glow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/panel_background.9.png b/core/res/res/drawable-hdpi/panel_background.9.png
index 03175d4..bfe5713 100644
--- a/core/res/res/drawable-hdpi/panel_background.9.png
+++ b/core/res/res/drawable-hdpi/panel_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/picture_emergency.png b/core/res/res/drawable-hdpi/picture_emergency.png
index 64972c2..b0f10f9 100644
--- a/core/res/res/drawable-hdpi/picture_emergency.png
+++ b/core/res/res/drawable-hdpi/picture_emergency.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_bottom_bright.9.png b/core/res/res/drawable-hdpi/popup_bottom_bright.9.png
index 6e5fbb5..cca47d3 100644
--- a/core/res/res/drawable-hdpi/popup_bottom_bright.9.png
+++ b/core/res/res/drawable-hdpi/popup_bottom_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_bottom_dark.9.png b/core/res/res/drawable-hdpi/popup_bottom_dark.9.png
index 3434b2d..62a0bd0 100644
--- a/core/res/res/drawable-hdpi/popup_bottom_dark.9.png
+++ b/core/res/res/drawable-hdpi/popup_bottom_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_bottom_medium.9.png b/core/res/res/drawable-hdpi/popup_bottom_medium.9.png
index 673a509..6ebb4f7 100644
--- a/core/res/res/drawable-hdpi/popup_bottom_medium.9.png
+++ b/core/res/res/drawable-hdpi/popup_bottom_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_center_bright.9.png b/core/res/res/drawable-hdpi/popup_center_bright.9.png
index c2a739c..756e9ed 100644
--- a/core/res/res/drawable-hdpi/popup_center_bright.9.png
+++ b/core/res/res/drawable-hdpi/popup_center_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_center_dark.9.png b/core/res/res/drawable-hdpi/popup_center_dark.9.png
index 9d2bfb1..77b4524 100644
--- a/core/res/res/drawable-hdpi/popup_center_dark.9.png
+++ b/core/res/res/drawable-hdpi/popup_center_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_center_medium.9.png b/core/res/res/drawable-hdpi/popup_center_medium.9.png
index 4375bf2d..de4be2a 100644
--- a/core/res/res/drawable-hdpi/popup_center_medium.9.png
+++ b/core/res/res/drawable-hdpi/popup_center_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_full_bright.9.png b/core/res/res/drawable-hdpi/popup_full_bright.9.png
index 6b8aa9d..6c30bec 100644
--- a/core/res/res/drawable-hdpi/popup_full_bright.9.png
+++ b/core/res/res/drawable-hdpi/popup_full_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_full_dark.9.png b/core/res/res/drawable-hdpi/popup_full_dark.9.png
index 2884abe..fc8c00e 100644
--- a/core/res/res/drawable-hdpi/popup_full_dark.9.png
+++ b/core/res/res/drawable-hdpi/popup_full_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_top_bright.9.png b/core/res/res/drawable-hdpi/popup_top_bright.9.png
index 76c35ec..ddd30ab 100644
--- a/core/res/res/drawable-hdpi/popup_top_bright.9.png
+++ b/core/res/res/drawable-hdpi/popup_top_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_top_dark.9.png b/core/res/res/drawable-hdpi/popup_top_dark.9.png
index f317330..144d0fc 100644
--- a/core/res/res/drawable-hdpi/popup_top_dark.9.png
+++ b/core/res/res/drawable-hdpi/popup_top_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/quickactions_arrowdown_left_holo_dark.9.png b/core/res/res/drawable-hdpi/quickactions_arrowdown_left_holo_dark.9.png
new file mode 100644
index 0000000..93a8417
--- /dev/null
+++ b/core/res/res/drawable-hdpi/quickactions_arrowdown_left_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/quickactions_arrowdown_left_holo_light.9.png b/core/res/res/drawable-hdpi/quickactions_arrowdown_left_holo_light.9.png
new file mode 100644
index 0000000..61e856a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/quickactions_arrowdown_left_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/quickactions_arrowdown_right_holo_dark.9.png b/core/res/res/drawable-hdpi/quickactions_arrowdown_right_holo_dark.9.png
new file mode 100644
index 0000000..7632a16
--- /dev/null
+++ b/core/res/res/drawable-hdpi/quickactions_arrowdown_right_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/quickactions_arrowdown_right_holo_light.9.png b/core/res/res/drawable-hdpi/quickactions_arrowdown_right_holo_light.9.png
new file mode 100644
index 0000000..8e66ad1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/quickactions_arrowdown_right_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/quickactions_arrowup_left_holo_dark.9.png b/core/res/res/drawable-hdpi/quickactions_arrowup_left_holo_dark.9.png
new file mode 100644
index 0000000..02618ca
--- /dev/null
+++ b/core/res/res/drawable-hdpi/quickactions_arrowup_left_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/quickactions_arrowup_left_holo_light.9.png b/core/res/res/drawable-hdpi/quickactions_arrowup_left_holo_light.9.png
new file mode 100644
index 0000000..939050d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/quickactions_arrowup_left_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/quickactions_arrowup_left_right_holo_dark.9.png b/core/res/res/drawable-hdpi/quickactions_arrowup_left_right_holo_dark.9.png
new file mode 100644
index 0000000..f5cf487
--- /dev/null
+++ b/core/res/res/drawable-hdpi/quickactions_arrowup_left_right_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/quickactions_arrowup_right_holo_light.9.png b/core/res/res/drawable-hdpi/quickactions_arrowup_right_holo_light.9.png
new file mode 100644
index 0000000..1237f26
--- /dev/null
+++ b/core/res/res/drawable-hdpi/quickactions_arrowup_right_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/quickcontact_nobadge.9.png b/core/res/res/drawable-hdpi/quickcontact_nobadge.9.png
new file mode 100644
index 0000000..68d43c4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/quickcontact_nobadge.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrollbar_handle_accelerated_anim2.9.png b/core/res/res/drawable-hdpi/scrollbar_handle_accelerated_anim2.9.png
index 0a28223..c916780 100644
--- a/core/res/res/drawable-hdpi/scrollbar_handle_accelerated_anim2.9.png
+++ b/core/res/res/drawable-hdpi/scrollbar_handle_accelerated_anim2.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_holo.png b/core/res/res/drawable-hdpi/scrubber_control_holo.png
new file mode 100644
index 0000000..3a72307
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_control_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_track_holo_dark.9.png b/core/res/res/drawable-hdpi/scrubber_track_holo_dark.9.png
new file mode 100644
index 0000000..e6d7123
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_track_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_track_holo_light.9.png b/core/res/res/drawable-hdpi/scrubber_track_holo_light.9.png
new file mode 100644
index 0000000..f75bfa0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrubber_track_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_active_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_active_holo_dark.9.png
new file mode 100644
index 0000000..bbf2861
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_active_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_active_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_active_holo_light.9.png
new file mode 100644
index 0000000..a10098d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_active_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_black_16.png b/core/res/res/drawable-hdpi/spinner_black_16.png
old mode 100755
new mode 100644
index 42eb734f..eb34867
--- a/core/res/res/drawable-hdpi/spinner_black_16.png
+++ b/core/res/res/drawable-hdpi/spinner_black_16.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_black_20.png b/core/res/res/drawable-hdpi/spinner_black_20.png
old mode 100755
new mode 100644
index c88fbc9..dac06d7
--- a/core/res/res/drawable-hdpi/spinner_black_20.png
+++ b/core/res/res/drawable-hdpi/spinner_black_20.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_black_48.png b/core/res/res/drawable-hdpi/spinner_black_48.png
old mode 100755
new mode 100644
index 7f9c4f3..337f72a
--- a/core/res/res/drawable-hdpi/spinner_black_48.png
+++ b/core/res/res/drawable-hdpi/spinner_black_48.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_black_76.png b/core/res/res/drawable-hdpi/spinner_black_76.png
old mode 100755
new mode 100644
index c1c177e..2edc3e7
--- a/core/res/res/drawable-hdpi/spinner_black_76.png
+++ b/core/res/res/drawable-hdpi/spinner_black_76.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_active_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_cab_active_holo_dark.9.png
new file mode 100644
index 0000000..ffae7e7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_cab_active_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_active_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_cab_active_holo_light.9.png
new file mode 100644
index 0000000..eaa38e2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_cab_active_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_default_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_cab_default_holo_dark.9.png
new file mode 100644
index 0000000..8693710
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_cab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_default_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_cab_default_holo_light.9.png
new file mode 100644
index 0000000..941739e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_cab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_cab_disabled_holo_dark.9.png
new file mode 100644
index 0000000..e672c95
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_cab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_cab_disabled_holo_light.9.png
new file mode 100644
index 0000000..f499540
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_cab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_cab_focused_holo_dark.9.png
new file mode 100644
index 0000000..f4900a5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_cab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_focused_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_cab_focused_holo_light.9.png
new file mode 100644
index 0000000..1286542
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_cab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_cab_pressed_holo_dark.9.png
new file mode 100644
index 0000000..dc236ec
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_cab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_cab_pressed_holo_light.9.png
new file mode 100644
index 0000000..17e3a27
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_cab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_default_holo_dark.9.png
new file mode 100644
index 0000000..7189c6d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_default_holo_light.9.png
new file mode 100644
index 0000000..996ef15
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_disabled_holo_dark.9.png
new file mode 100644
index 0000000..adde694
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_disabled_holo_light.9.png
new file mode 100644
index 0000000..fdb4bdf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_focused_holo_dark.9.png
new file mode 100644
index 0000000..d40d165
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_focused_holo_light.9.png
new file mode 100644
index 0000000..096b977
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_pressed_holo_dark.9.png
new file mode 100644
index 0000000..5428b0a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_pressed_holo_light.9.png
new file mode 100644
index 0000000..33b661b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_white_16.png b/core/res/res/drawable-hdpi/spinner_white_16.png
old mode 100755
new mode 100644
index 4e037ae..7914a68
--- a/core/res/res/drawable-hdpi/spinner_white_16.png
+++ b/core/res/res/drawable-hdpi/spinner_white_16.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_white_48.png b/core/res/res/drawable-hdpi/spinner_white_48.png
old mode 100755
new mode 100644
index d8519f2..faee8ca
--- a/core/res/res/drawable-hdpi/spinner_white_48.png
+++ b/core/res/res/drawable-hdpi/spinner_white_48.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_white_76.png b/core/res/res/drawable-hdpi/spinner_white_76.png
old mode 100755
new mode 100644
index 6d6aa94..cd26379
--- a/core/res/res/drawable-hdpi/spinner_white_76.png
+++ b/core/res/res/drawable-hdpi/spinner_white_76.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_call_mute.png b/core/res/res/drawable-hdpi/stat_notify_call_mute.png
index b0f7990..b86d2ca 100755
--- a/core/res/res/drawable-hdpi/stat_notify_call_mute.png
+++ b/core/res/res/drawable-hdpi/stat_notify_call_mute.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_car_mode.png b/core/res/res/drawable-hdpi/stat_notify_car_mode.png
index e700d79..03499a4 100644
--- a/core/res/res/drawable-hdpi/stat_notify_car_mode.png
+++ b/core/res/res/drawable-hdpi/stat_notify_car_mode.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_chat.png b/core/res/res/drawable-hdpi/stat_notify_chat.png
old mode 100644
new mode 100755
index 71ea8de..097a979
--- a/core/res/res/drawable-hdpi/stat_notify_chat.png
+++ b/core/res/res/drawable-hdpi/stat_notify_chat.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_disk_full.png b/core/res/res/drawable-hdpi/stat_notify_disk_full.png
index 66e7380..6ceeda8 100755
--- a/core/res/res/drawable-hdpi/stat_notify_disk_full.png
+++ b/core/res/res/drawable-hdpi/stat_notify_disk_full.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_email_generic.png b/core/res/res/drawable-hdpi/stat_notify_email_generic.png
index bc5fcab..07de4cb 100644
--- a/core/res/res/drawable-hdpi/stat_notify_email_generic.png
+++ b/core/res/res/drawable-hdpi/stat_notify_email_generic.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_error.png b/core/res/res/drawable-hdpi/stat_notify_error.png
old mode 100755
new mode 100644
index b3a18b3..37c8853
--- a/core/res/res/drawable-hdpi/stat_notify_error.png
+++ b/core/res/res/drawable-hdpi/stat_notify_error.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_gmail.png b/core/res/res/drawable-hdpi/stat_notify_gmail.png
index ea8beae..1aadf68 100644
--- a/core/res/res/drawable-hdpi/stat_notify_gmail.png
+++ b/core/res/res/drawable-hdpi/stat_notify_gmail.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_missed_call.png b/core/res/res/drawable-hdpi/stat_notify_missed_call.png
old mode 100644
new mode 100755
index 3c19c93..192574d4e
--- a/core/res/res/drawable-hdpi/stat_notify_missed_call.png
+++ b/core/res/res/drawable-hdpi/stat_notify_missed_call.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_more.png b/core/res/res/drawable-hdpi/stat_notify_more.png
index f54b3d4..1c7f9db 100755
--- a/core/res/res/drawable-hdpi/stat_notify_more.png
+++ b/core/res/res/drawable-hdpi/stat_notify_more.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sdcard.png b/core/res/res/drawable-hdpi/stat_notify_sdcard.png
old mode 100755
new mode 100644
index dd947a5..d3b624b
--- a/core/res/res/drawable-hdpi/stat_notify_sdcard.png
+++ b/core/res/res/drawable-hdpi/stat_notify_sdcard.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sdcard_prepare.png b/core/res/res/drawable-hdpi/stat_notify_sdcard_prepare.png
old mode 100755
new mode 100644
index 4b9b9ca..a483ba2
--- a/core/res/res/drawable-hdpi/stat_notify_sdcard_prepare.png
+++ b/core/res/res/drawable-hdpi/stat_notify_sdcard_prepare.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sdcard_usb.png b/core/res/res/drawable-hdpi/stat_notify_sdcard_usb.png
old mode 100755
new mode 100644
index fb2b26a..a5e369e2
--- a/core/res/res/drawable-hdpi/stat_notify_sdcard_usb.png
+++ b/core/res/res/drawable-hdpi/stat_notify_sdcard_usb.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sim_toolkit.png b/core/res/res/drawable-hdpi/stat_notify_sim_toolkit.png
index 8865bda..3b14c26 100755
--- a/core/res/res/drawable-hdpi/stat_notify_sim_toolkit.png
+++ b/core/res/res/drawable-hdpi/stat_notify_sim_toolkit.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sync.png b/core/res/res/drawable-hdpi/stat_notify_sync.png
index 004cfab..6f9cf84 100755
--- a/core/res/res/drawable-hdpi/stat_notify_sync.png
+++ b/core/res/res/drawable-hdpi/stat_notify_sync.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sync_anim0.png b/core/res/res/drawable-hdpi/stat_notify_sync_anim0.png
index 6973fc5..6f9cf84 100755
--- a/core/res/res/drawable-hdpi/stat_notify_sync_anim0.png
+++ b/core/res/res/drawable-hdpi/stat_notify_sync_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sync_error.png b/core/res/res/drawable-hdpi/stat_notify_sync_error.png
index 26b2446..6e3b545 100755
--- a/core/res/res/drawable-hdpi/stat_notify_sync_error.png
+++ b/core/res/res/drawable-hdpi/stat_notify_sync_error.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_voicemail.png b/core/res/res/drawable-hdpi/stat_notify_voicemail.png
index 5b77846..70b2411 100755
--- a/core/res/res/drawable-hdpi/stat_notify_voicemail.png
+++ b/core/res/res/drawable-hdpi/stat_notify_voicemail.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png b/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png
old mode 100644
new mode 100755
index 76034e5..e9405dd
--- a/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png
+++ b/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_adb.png b/core/res/res/drawable-hdpi/stat_sys_adb.png
old mode 100755
new mode 100644
index f99b780..aef8650
--- a/core/res/res/drawable-hdpi/stat_sys_adb.png
+++ b/core/res/res/drawable-hdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_0.png b/core/res/res/drawable-hdpi/stat_sys_battery_0.png
index 82f2509..3ed0105 100644
--- a/core/res/res/drawable-hdpi/stat_sys_battery_0.png
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_10.png b/core/res/res/drawable-hdpi/stat_sys_battery_10.png
old mode 100755
new mode 100644
index 4486553..c81616b
--- a/core/res/res/drawable-hdpi/stat_sys_battery_10.png
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_10.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_100.png b/core/res/res/drawable-hdpi/stat_sys_battery_100.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_20.png b/core/res/res/drawable-hdpi/stat_sys_battery_20.png
old mode 100755
new mode 100644
index c8f9c92..eb5ef09
--- a/core/res/res/drawable-hdpi/stat_sys_battery_20.png
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_20.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_40.png b/core/res/res/drawable-hdpi/stat_sys_battery_40.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_60.png b/core/res/res/drawable-hdpi/stat_sys_battery_60.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_80.png b/core/res/res/drawable-hdpi/stat_sys_battery_80.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim0.png b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim0.png
old mode 100755
new mode 100644
index c7464f7..9a6c683
--- a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim0.png
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim1.png b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim1.png
old mode 100755
new mode 100644
index 997feb3..c40c622
--- a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim1.png
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim2.png b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim2.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim3.png b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim3.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim4.png b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim4.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim5.png b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim5.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_unknown.png b/core/res/res/drawable-hdpi/stat_sys_battery_unknown.png
old mode 100755
new mode 100644
index dadfe8d..1a9abaf
--- a/core/res/res/drawable-hdpi/stat_sys_battery_unknown.png
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_unknown.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png
old mode 100755
new mode 100644
index e8fbc9e..7a8b78f
--- a/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png
+++ b/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_usb.png b/core/res/res/drawable-hdpi/stat_sys_data_usb.png
index e916fbb..4c14c07 100755
--- a/core/res/res/drawable-hdpi/stat_sys_data_usb.png
+++ b/core/res/res/drawable-hdpi/stat_sys_data_usb.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim0.png b/core/res/res/drawable-hdpi/stat_sys_download_anim0.png
index c127e6e..d9c9e4c 100755
--- a/core/res/res/drawable-hdpi/stat_sys_download_anim0.png
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_gps_on.png b/core/res/res/drawable-hdpi/stat_sys_gps_on.png
index 2ed6082..99a8c6c 100644
--- a/core/res/res/drawable-hdpi/stat_sys_gps_on.png
+++ b/core/res/res/drawable-hdpi/stat_sys_gps_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call.png b/core/res/res/drawable-hdpi/stat_sys_phone_call.png
old mode 100755
new mode 100644
index 9b5f075..950713b
--- a/core/res/res/drawable-hdpi/stat_sys_phone_call.png
+++ b/core/res/res/drawable-hdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_forward.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_forward.png
old mode 100755
new mode 100644
index 032f8f1..07a2e9d
--- a/core/res/res/drawable-hdpi/stat_sys_phone_call_forward.png
+++ b/core/res/res/drawable-hdpi/stat_sys_phone_call_forward.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_on_hold.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_on_hold.png
old mode 100755
new mode 100644
index 5b0a68d..033a558
--- a/core/res/res/drawable-hdpi/stat_sys_phone_call_on_hold.png
+++ b/core/res/res/drawable-hdpi/stat_sys_phone_call_on_hold.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_0_cdma.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_0_cdma.png
index 14a7e94..28b2fc2 100644
--- a/core/res/res/drawable-hdpi/stat_sys_r_signal_0_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_0_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_1_cdma.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_1_cdma.png
index 9cf04b5..a7449cd 100644
--- a/core/res/res/drawable-hdpi/stat_sys_r_signal_1_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_1_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_2_cdma.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_2_cdma.png
index dbd3308..c4c1fb8 100644
--- a/core/res/res/drawable-hdpi/stat_sys_r_signal_2_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_2_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_3_cdma.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_3_cdma.png
old mode 100755
new mode 100644
index a3a6b6c..0a32d2e
--- a/core/res/res/drawable-hdpi/stat_sys_r_signal_3_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_3_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_4_cdma.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_4_cdma.png
index 0f95041..beefdd2 100644
--- a/core/res/res/drawable-hdpi/stat_sys_r_signal_4_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_4_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ra_signal_0_cdma.png b/core/res/res/drawable-hdpi/stat_sys_ra_signal_0_cdma.png
old mode 100755
new mode 100644
index a2fa547..3333933
--- a/core/res/res/drawable-hdpi/stat_sys_ra_signal_0_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_ra_signal_0_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ra_signal_1_cdma.png b/core/res/res/drawable-hdpi/stat_sys_ra_signal_1_cdma.png
old mode 100755
new mode 100644
index 17c8681..6d69ed6
--- a/core/res/res/drawable-hdpi/stat_sys_ra_signal_1_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_ra_signal_1_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ra_signal_2_cdma.png b/core/res/res/drawable-hdpi/stat_sys_ra_signal_2_cdma.png
old mode 100755
new mode 100644
index 4a21fb6..a78957d
--- a/core/res/res/drawable-hdpi/stat_sys_ra_signal_2_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_ra_signal_2_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ra_signal_3_cdma.png b/core/res/res/drawable-hdpi/stat_sys_ra_signal_3_cdma.png
index 188b1f9..91037da 100644
--- a/core/res/res/drawable-hdpi/stat_sys_ra_signal_3_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_ra_signal_3_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ra_signal_4_cdma.png b/core/res/res/drawable-hdpi/stat_sys_ra_signal_4_cdma.png
old mode 100755
new mode 100644
index 5729f7d..44c1dca
--- a/core/res/res/drawable-hdpi/stat_sys_ra_signal_4_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_ra_signal_4_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_secure.png b/core/res/res/drawable-hdpi/stat_sys_secure.png
old mode 100755
new mode 100644
index 0889e49..4bae258
--- a/core/res/res/drawable-hdpi/stat_sys_secure.png
+++ b/core/res/res/drawable-hdpi/stat_sys_secure.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_0_cdma.png b/core/res/res/drawable-hdpi/stat_sys_signal_0_cdma.png
index af43e00..3c7db08 100755
--- a/core/res/res/drawable-hdpi/stat_sys_signal_0_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_0_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_1_cdma.png b/core/res/res/drawable-hdpi/stat_sys_signal_1_cdma.png
index 4ffe421..f8f40a8 100755
--- a/core/res/res/drawable-hdpi/stat_sys_signal_1_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_1_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_2_cdma.png b/core/res/res/drawable-hdpi/stat_sys_signal_2_cdma.png
index 6f27b96..a243195 100755
--- a/core/res/res/drawable-hdpi/stat_sys_signal_2_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_2_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_3_cdma.png b/core/res/res/drawable-hdpi/stat_sys_signal_3_cdma.png
index ddc46b0..9c369e7 100755
--- a/core/res/res/drawable-hdpi/stat_sys_signal_3_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_3_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_4_cdma.png b/core/res/res/drawable-hdpi/stat_sys_signal_4_cdma.png
index fb3cfe9..219bbbd 100755
--- a/core/res/res/drawable-hdpi/stat_sys_signal_4_cdma.png
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_4_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_evdo_0.png b/core/res/res/drawable-hdpi/stat_sys_signal_evdo_0.png
index b697ca4..ae18ecd 100755
--- a/core/res/res/drawable-hdpi/stat_sys_signal_evdo_0.png
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_evdo_0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_evdo_1.png b/core/res/res/drawable-hdpi/stat_sys_signal_evdo_1.png
index a61de4d..4fed92c 100755
--- a/core/res/res/drawable-hdpi/stat_sys_signal_evdo_1.png
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_evdo_1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_evdo_2.png b/core/res/res/drawable-hdpi/stat_sys_signal_evdo_2.png
index 62e0393..96f1248 100755
--- a/core/res/res/drawable-hdpi/stat_sys_signal_evdo_2.png
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_evdo_2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_evdo_3.png b/core/res/res/drawable-hdpi/stat_sys_signal_evdo_3.png
index 09eae9d..b0e1328 100755
--- a/core/res/res/drawable-hdpi/stat_sys_signal_evdo_3.png
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_evdo_3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_evdo_4.png b/core/res/res/drawable-hdpi/stat_sys_signal_evdo_4.png
index 4012ac5..da15645 100755
--- a/core/res/res/drawable-hdpi/stat_sys_signal_evdo_4.png
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_evdo_4.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_speakerphone.png b/core/res/res/drawable-hdpi/stat_sys_speakerphone.png
old mode 100755
new mode 100644
index 21f96c4..51dea58
--- a/core/res/res/drawable-hdpi/stat_sys_speakerphone.png
+++ b/core/res/res/drawable-hdpi/stat_sys_speakerphone.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_tether_bluetooth.png
index 4ebee58..e43fbae 100644
--- a/core/res/res/drawable-hdpi/stat_sys_tether_bluetooth.png
+++ b/core/res/res/drawable-hdpi/stat_sys_tether_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_general.png b/core/res/res/drawable-hdpi/stat_sys_tether_general.png
index e23f7f9..c42b00c 100644
--- a/core/res/res/drawable-hdpi/stat_sys_tether_general.png
+++ b/core/res/res/drawable-hdpi/stat_sys_tether_general.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_usb.png b/core/res/res/drawable-hdpi/stat_sys_tether_usb.png
index d432f9a..c6c533d 100644
--- a/core/res/res/drawable-hdpi/stat_sys_tether_usb.png
+++ b/core/res/res/drawable-hdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_wifi.png b/core/res/res/drawable-hdpi/stat_sys_tether_wifi.png
index 74dfbba..9fcadef 100644
--- a/core/res/res/drawable-hdpi/stat_sys_tether_wifi.png
+++ b/core/res/res/drawable-hdpi/stat_sys_tether_wifi.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_throttled.png b/core/res/res/drawable-hdpi/stat_sys_throttled.png
old mode 100755
new mode 100644
index 58eafc0..33c0521
--- a/core/res/res/drawable-hdpi/stat_sys_throttled.png
+++ b/core/res/res/drawable-hdpi/stat_sys_throttled.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call.png b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call.png
old mode 100755
new mode 100644
index 83e8ead..dfb3424
--- a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call.png
+++ b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_on_hold.png b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_on_hold.png
old mode 100755
new mode 100644
index 9731c46..402295b
--- a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_on_hold.png
+++ b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_on_hold.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_warning.png b/core/res/res/drawable-hdpi/stat_sys_warning.png
old mode 100755
new mode 100644
index cb8a3d4..37c8853
--- a/core/res/res/drawable-hdpi/stat_sys_warning.png
+++ b/core/res/res/drawable-hdpi/stat_sys_warning.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_header_background.9.png b/core/res/res/drawable-hdpi/status_bar_header_background.9.png
old mode 100755
new mode 100644
index 79d77aa..37b5fef
--- a/core/res/res/drawable-hdpi/status_bar_header_background.9.png
+++ b/core/res/res/drawable-hdpi/status_bar_header_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png b/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png
index b07c7bc..0876bc6 100644
--- a/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png
+++ b/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png b/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png
index de2f3c3..810c950 100644
--- a/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png
+++ b/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png b/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png
index b5eab83..343e4ca 100644
--- a/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png
+++ b/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/statusbar_background.9.png b/core/res/res/drawable-hdpi/statusbar_background.9.png
new file mode 100644
index 0000000..a4be298
--- /dev/null
+++ b/core/res/res/drawable-hdpi/statusbar_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_action_call.png b/core/res/res/drawable-hdpi/sym_action_call.png
index 105f7d0..da8afee 100644
--- a/core/res/res/drawable-hdpi/sym_action_call.png
+++ b/core/res/res/drawable-hdpi/sym_action_call.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_bottom_holo.9.png b/core/res/res/drawable-hdpi/tab_bottom_holo.9.png
new file mode 100644
index 0000000..ec9fa8d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_bottom_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_focus.9.png b/core/res/res/drawable-hdpi/tab_focus.9.png
index 7591c07..6e8a71f 100644
--- a/core/res/res/drawable-hdpi/tab_focus.9.png
+++ b/core/res/res/drawable-hdpi/tab_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_focus_bar_left.9.png b/core/res/res/drawable-hdpi/tab_focus_bar_left.9.png
old mode 100755
new mode 100644
index 0ee8347..51194a4
--- a/core/res/res/drawable-hdpi/tab_focus_bar_left.9.png
+++ b/core/res/res/drawable-hdpi/tab_focus_bar_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_focus_bar_right.9.png b/core/res/res/drawable-hdpi/tab_focus_bar_right.9.png
old mode 100755
new mode 100644
index 0ee8347..51194a4
--- a/core/res/res/drawable-hdpi/tab_focus_bar_right.9.png
+++ b/core/res/res/drawable-hdpi/tab_focus_bar_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_focused_holo.9.png b/core/res/res/drawable-hdpi/tab_focused_holo.9.png
new file mode 100644
index 0000000..39a8f7a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_press.9.png b/core/res/res/drawable-hdpi/tab_press.9.png
old mode 100755
new mode 100644
index 01798a3..119b2c6
--- a/core/res/res/drawable-hdpi/tab_press.9.png
+++ b/core/res/res/drawable-hdpi/tab_press.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_press_bar_left.9.png b/core/res/res/drawable-hdpi/tab_press_bar_left.9.png
old mode 100755
new mode 100644
index ee129ba..dc2fbce
--- a/core/res/res/drawable-hdpi/tab_press_bar_left.9.png
+++ b/core/res/res/drawable-hdpi/tab_press_bar_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_press_bar_right.9.png b/core/res/res/drawable-hdpi/tab_press_bar_right.9.png
old mode 100755
new mode 100644
index ee129ba..dc2fbce
--- a/core/res/res/drawable-hdpi/tab_press_bar_right.9.png
+++ b/core/res/res/drawable-hdpi/tab_press_bar_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_pressed_holo.9.png b/core/res/res/drawable-hdpi/tab_pressed_holo.9.png
new file mode 100644
index 0000000..b8a569f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_selected.9.png b/core/res/res/drawable-hdpi/tab_selected.9.png
old mode 100755
new mode 100644
index 6ee775f..f036b9a
--- a/core/res/res/drawable-hdpi/tab_selected.9.png
+++ b/core/res/res/drawable-hdpi/tab_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_selected_bar_left.9.png b/core/res/res/drawable-hdpi/tab_selected_bar_left.9.png
old mode 100755
new mode 100644
index 03bcc13..aa935fe
--- a/core/res/res/drawable-hdpi/tab_selected_bar_left.9.png
+++ b/core/res/res/drawable-hdpi/tab_selected_bar_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_selected_bar_right.9.png b/core/res/res/drawable-hdpi/tab_selected_bar_right.9.png
old mode 100755
new mode 100644
index f228445..aa935fe
--- a/core/res/res/drawable-hdpi/tab_selected_bar_right.9.png
+++ b/core/res/res/drawable-hdpi/tab_selected_bar_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_selected_focused_holo.9.png b/core/res/res/drawable-hdpi/tab_selected_focused_holo.9.png
new file mode 100644
index 0000000..1ba35d5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_selected_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_selected_holo.9.png b/core/res/res/drawable-hdpi/tab_selected_holo.9.png
new file mode 100644
index 0000000..ef913cc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_selected_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_selected_pressed_focused_holo.9.png b/core/res/res/drawable-hdpi/tab_selected_pressed_focused_holo.9.png
new file mode 100644
index 0000000..d7e9688
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_selected_pressed_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_selected_pressed_holo.9.png b/core/res/res/drawable-hdpi/tab_selected_pressed_holo.9.png
new file mode 100644
index 0000000..b8b1fcf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_selected_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_unselected.9.png b/core/res/res/drawable-hdpi/tab_unselected.9.png
old mode 100755
new mode 100644
index 67561e2..c3a1f30
--- a/core/res/res/drawable-hdpi/tab_unselected.9.png
+++ b/core/res/res/drawable-hdpi/tab_unselected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_unselected_focused_holo.9.png b/core/res/res/drawable-hdpi/tab_unselected_focused_holo.9.png
new file mode 100644
index 0000000..256e8e7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_unselected_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_unselected_holo.9.png b/core/res/res/drawable-hdpi/tab_unselected_holo.9.png
new file mode 100644
index 0000000..eaa306a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_unselected_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_unselected_pressed_focused_holo.9.png b/core/res/res/drawable-hdpi/tab_unselected_pressed_focused_holo.9.png
new file mode 100644
index 0000000..d17b820
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_unselected_pressed_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_unselected_pressed_holo.9.png b/core/res/res/drawable-hdpi/tab_unselected_pressed_holo.9.png
new file mode 100644
index 0000000..a344994
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_unselected_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_edit_paste_window.9.png b/core/res/res/drawable-hdpi/text_edit_paste_window.9.png
new file mode 100644
index 0000000..7cd000b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/text_edit_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle.png b/core/res/res/drawable-hdpi/text_select_handle.png
new file mode 100644
index 0000000..93a5a15
--- /dev/null
+++ b/core/res/res/drawable-hdpi/text_select_handle.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle_middle.png b/core/res/res/drawable-hdpi/text_select_handle_middle.png
index daf8e68..5a83c6c 100755
--- a/core/res/res/drawable-hdpi/text_select_handle_middle.png
+++ b/core/res/res/drawable-hdpi/text_select_handle_middle.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default.9.png b/core/res/res/drawable-hdpi/textfield_default.9.png
old mode 100755
new mode 100644
index f7b6e99..4c20179
--- a/core/res/res/drawable-hdpi/textfield_default.9.png
+++ b/core/res/res/drawable-hdpi/textfield_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
new file mode 100644
index 0000000..7ec2192
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
new file mode 100644
index 0000000..c03e4f6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled.9.png b/core/res/res/drawable-hdpi/textfield_disabled.9.png
old mode 100755
new mode 100644
index 3011502..81569d1
--- a/core/res/res/drawable-hdpi/textfield_disabled.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
new file mode 100644
index 0000000..6642717
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
new file mode 100644
index 0000000..9572752
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_selected.9.png b/core/res/res/drawable-hdpi/textfield_disabled_selected.9.png
old mode 100755
new mode 100644
index e0f82eb..2591490
--- a/core/res/res/drawable-hdpi/textfield_disabled_selected.9.png
+++ b/core/res/res/drawable-hdpi/textfield_disabled_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png
new file mode 100644
index 0000000..0ad248c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png
new file mode 100644
index 0000000..b7a07c4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_pressed.9.png b/core/res/res/drawable-hdpi/textfield_pressed.9.png
old mode 100755
new mode 100644
index 296d3da..a42d87f
--- a/core/res/res/drawable-hdpi/textfield_pressed.9.png
+++ b/core/res/res/drawable-hdpi/textfield_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png
new file mode 100644
index 0000000..a271ac9b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png
new file mode 100644
index 0000000..521722d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_search_default_holo_dark.9.png
new file mode 100644
index 0000000..5b62564
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_search_default_holo_light.9.png
new file mode 100644
index 0000000..881edeb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_search_selected_holo_dark.9.png
new file mode 100644
index 0000000..cb3f35b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_selected_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_search_selected_holo_light.9.png
new file mode 100644
index 0000000..742b137
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_selected.9.png b/core/res/res/drawable-hdpi/textfield_selected.9.png
old mode 100755
new mode 100644
index cf2cae3..a36ed72
--- a/core/res/res/drawable-hdpi/textfield_selected.9.png
+++ b/core/res/res/drawable-hdpi/textfield_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png
new file mode 100644
index 0000000..a271ac9b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png
new file mode 100644
index 0000000..521722d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/title_bar_shadow.9.png b/core/res/res/drawable-hdpi/title_bar_shadow.9.png
old mode 100755
new mode 100644
index c3a0a23..e6dab63
--- a/core/res/res/drawable-hdpi/title_bar_shadow.9.png
+++ b/core/res/res/drawable-hdpi/title_bar_shadow.9.png
Binary files differ
diff --git a/core/res/res/drawable-land-hdpi/ic_jog_dial_sound_off.png b/core/res/res/drawable-land-hdpi/ic_jog_dial_sound_off.png
deleted file mode 100755
index d73db48..0000000
--- a/core/res/res/drawable-land-hdpi/ic_jog_dial_sound_off.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-land-hdpi/ic_jog_dial_sound_on.png b/core/res/res/drawable-land-hdpi/ic_jog_dial_sound_on.png
deleted file mode 100755
index 90da6e3..0000000
--- a/core/res/res/drawable-land-hdpi/ic_jog_dial_sound_on.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-land-hdpi/ic_jog_dial_unlock.png b/core/res/res/drawable-land-hdpi/ic_jog_dial_unlock.png
deleted file mode 100755
index a9af1af..0000000
--- a/core/res/res/drawable-land-hdpi/ic_jog_dial_unlock.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-land-mdpi/ic_jog_dial_sound_off.png b/core/res/res/drawable-land-mdpi/ic_jog_dial_sound_off.png
deleted file mode 100644
index ed54da6..0000000
--- a/core/res/res/drawable-land-mdpi/ic_jog_dial_sound_off.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-land-mdpi/ic_jog_dial_sound_on.png b/core/res/res/drawable-land-mdpi/ic_jog_dial_sound_on.png
deleted file mode 100644
index 9994b9f..0000000
--- a/core/res/res/drawable-land-mdpi/ic_jog_dial_sound_on.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-land-mdpi/ic_jog_dial_unlock.png b/core/res/res/drawable-land-mdpi/ic_jog_dial_unlock.png
deleted file mode 100644
index ae8188b..0000000
--- a/core/res/res/drawable-land-mdpi/ic_jog_dial_unlock.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-land-mdpi/jog_tab_target_gray.png b/core/res/res/drawable-land-mdpi/jog_tab_target_gray.png
deleted file mode 100644
index 7fd415d..0000000
--- a/core/res/res/drawable-land-mdpi/jog_tab_target_gray.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-land-mdpi/jog_tab_target_green.png b/core/res/res/drawable-land-mdpi/jog_tab_target_green.png
deleted file mode 100644
index 6f512fa..0000000
--- a/core/res/res/drawable-land-mdpi/jog_tab_target_green.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-land-mdpi/jog_tab_target_red.png b/core/res/res/drawable-land-mdpi/jog_tab_target_red.png
deleted file mode 100644
index 3e2293a..0000000
--- a/core/res/res/drawable-land-mdpi/jog_tab_target_red.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-land-mdpi/jog_tab_target_yellow.png b/core/res/res/drawable-land-mdpi/jog_tab_target_yellow.png
deleted file mode 100644
index 67df9d9..0000000
--- a/core/res/res/drawable-land-mdpi/jog_tab_target_yellow.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off.png b/core/res/res/drawable-mdpi/btn_check_off.png
index 251ddff..56d3861 100644
--- a/core/res/res/drawable-mdpi/btn_check_off.png
+++ b/core/res/res/drawable-mdpi/btn_check_off.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable.png b/core/res/res/drawable-mdpi/btn_check_off_disable.png
index 45e6804..e012afd 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_disable.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
index 193acd2..0837bbd 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_dark.png
new file mode 100644
index 0000000..44b987b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_light.png
new file mode 100644
index 0000000..2a3824b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_disable_holo_dark.png
new file mode 100644
index 0000000..44b987b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_disable_holo_light.png
new file mode 100644
index 0000000..2a3824b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disabled_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_disabled_focused_holo_dark.png
new file mode 100644
index 0000000..7e58392
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disabled_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disabled_focused_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_disabled_focused_holo_light.png
new file mode 100644
index 0000000..d5c1f7c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disabled_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disabled_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_disabled_holo_dark.png
new file mode 100644
index 0000000..b6eec97
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disabled_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_disabled_holo_light.png
new file mode 100644
index 0000000..1bc34b6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_focused_holo_dark.png
new file mode 100644
index 0000000..894836f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_focused_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_focused_holo_light.png
new file mode 100644
index 0000000..ae81a5e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_holo_dark.png
new file mode 100644
index 0000000..b03f356
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_holo_light.png
new file mode 100644
index 0000000..9dbbd49
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_normal_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_normal_holo_dark.png
new file mode 100644
index 0000000..5434614
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_normal_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_normal_holo_light.png
new file mode 100644
index 0000000..0d43dc4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed.png b/core/res/res/drawable-mdpi/btn_check_off_pressed.png
index 807901c..984dfd7 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_dark.png
new file mode 100644
index 0000000..0f9f4f0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_light.png
new file mode 100644
index 0000000..77ad452
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected.png b/core/res/res/drawable-mdpi/btn_check_off_selected.png
index dbc3beb..20842d4 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_selected.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_off_selected_holo_dark.png
new file mode 100644
index 0000000..89ea3a8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected_holo_light.png b/core/res/res/drawable-mdpi/btn_check_off_selected_holo_light.png
new file mode 100644
index 0000000..3fa45d9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on.png b/core/res/res/drawable-mdpi/btn_check_on.png
index 4c83e2e..791ac1d 100644
--- a/core/res/res/drawable-mdpi/btn_check_on.png
+++ b/core/res/res/drawable-mdpi/btn_check_on.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable.png b/core/res/res/drawable-mdpi/btn_check_on_disable.png
index f1bf178..6cb02f3 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_disable.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
index ea232ee..8a73b33 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png
new file mode 100644
index 0000000..48cc017
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_light.png
new file mode 100644
index 0000000..c9ebbca
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_disable_holo_dark.png
new file mode 100644
index 0000000..48cc017
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_disable_holo_light.png
new file mode 100644
index 0000000..c9ebbca
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disabled_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_disabled_focused_holo_dark.png
new file mode 100644
index 0000000..28bf7f6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disabled_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disabled_focused_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_disabled_focused_holo_light.png
new file mode 100644
index 0000000..bb2d314
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disabled_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disabled_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_disabled_holo_dark.png
new file mode 100644
index 0000000..8cf4554
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disabled_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_disabled_holo_light.png
new file mode 100644
index 0000000..ba37f93
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_focused_holo_dark.png
new file mode 100644
index 0000000..1fca094
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_focused_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_focused_holo_light.png
new file mode 100644
index 0000000..21c33f0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_holo_dark.png
new file mode 100644
index 0000000..ca4d509
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_holo_light.png
new file mode 100644
index 0000000..158f3a1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_normal_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_normal_holo_dark.png
new file mode 100644
index 0000000..e65c0b4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_normal_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_normal_holo_light.png
new file mode 100644
index 0000000..9576bea
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed.png b/core/res/res/drawable-mdpi/btn_check_on_pressed.png
index 0de8a4c..300d64a 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png
new file mode 100644
index 0000000..84733fb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png
new file mode 100644
index 0000000..67a15d2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected.png b/core/res/res/drawable-mdpi/btn_check_on_selected.png
index 20294f3..0b36adb 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_selected.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_selected_holo_dark.png
new file mode 100644
index 0000000..5b93f8b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected_holo_light.png b/core/res/res/drawable-mdpi/btn_check_on_selected_holo_light.png
new file mode 100644
index 0000000..74ab250
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_circle_disable.png b/core/res/res/drawable-mdpi/btn_circle_disable.png
index 29e227c..33b74a6 100644
--- a/core/res/res/drawable-mdpi/btn_circle_disable.png
+++ b/core/res/res/drawable-mdpi/btn_circle_disable.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_circle_disable_focused.png b/core/res/res/drawable-mdpi/btn_circle_disable_focused.png
index c5aa3c5..005ad8d 100644
--- a/core/res/res/drawable-mdpi/btn_circle_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_circle_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_circle_normal.png b/core/res/res/drawable-mdpi/btn_circle_normal.png
index 6358351..fc5af1c 100644
--- a/core/res/res/drawable-mdpi/btn_circle_normal.png
+++ b/core/res/res/drawable-mdpi/btn_circle_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_circle_pressed.png b/core/res/res/drawable-mdpi/btn_circle_pressed.png
index dc07a61..8f40afd 100644
--- a/core/res/res/drawable-mdpi/btn_circle_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_circle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_circle_selected.png b/core/res/res/drawable-mdpi/btn_circle_selected.png
index 6eb2ff5..c74fac2 100644
--- a/core/res/res/drawable-mdpi/btn_circle_selected.png
+++ b/core/res/res/drawable-mdpi/btn_circle_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_close_normal.png b/core/res/res/drawable-mdpi/btn_close_normal.png
index eca5828..4c6e79d 100644
--- a/core/res/res/drawable-mdpi/btn_close_normal.png
+++ b/core/res/res/drawable-mdpi/btn_close_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_close_pressed.png b/core/res/res/drawable-mdpi/btn_close_pressed.png
index 3c745bb..fc983af 100644
--- a/core/res/res/drawable-mdpi/btn_close_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_close_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_close_selected.png b/core/res/res/drawable-mdpi/btn_close_selected.png
index c41f039..f2bf91a 100644
--- a/core/res/res/drawable-mdpi/btn_close_selected.png
+++ b/core/res/res/drawable-mdpi/btn_close_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_code_lock_default.png b/core/res/res/drawable-mdpi/btn_code_lock_default.png
index f524317..c2e0b05 100755
--- a/core/res/res/drawable-mdpi/btn_code_lock_default.png
+++ b/core/res/res/drawable-mdpi/btn_code_lock_default.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_code_lock_touched.png b/core/res/res/drawable-mdpi/btn_code_lock_touched.png
index 5cd436c..70e95a2 100755
--- a/core/res/res/drawable-mdpi/btn_code_lock_touched.png
+++ b/core/res/res/drawable-mdpi/btn_code_lock_touched.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png
new file mode 100644
index 0000000..ca2271a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png
new file mode 100644
index 0000000..0d498e3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png
new file mode 100644
index 0000000..ae922ce
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png
new file mode 100644
index 0000000..89aef31
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png
new file mode 100644
index 0000000..1f4be44
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png
new file mode 100644
index 0000000..e4d0ba0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal.9.png b/core/res/res/drawable-mdpi/btn_default_normal.9.png
index 7ff74b2..a2d5ccd 100644
--- a/core/res/res/drawable-mdpi/btn_default_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_disable.9.png b/core/res/res/drawable-mdpi/btn_default_normal_disable.9.png
index d3e11b5..edd3a3e 100644
--- a/core/res/res/drawable-mdpi/btn_default_normal_disable.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_normal_disable.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_disable_focused.9.png b/core/res/res/drawable-mdpi/btn_default_normal_disable_focused.9.png
index 843ca7a..f506179 100644
--- a/core/res/res/drawable-mdpi/btn_default_normal_disable_focused.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_normal_disable_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png
new file mode 100644
index 0000000..050fb19
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png
new file mode 100644
index 0000000..75651fc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_pressed.9.png b/core/res/res/drawable-mdpi/btn_default_pressed.9.png
index 74fd58b..033bf89 100644
--- a/core/res/res/drawable-mdpi/btn_default_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png
new file mode 100644
index 0000000..c7d02cf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png
new file mode 100644
index 0000000..40c9f64
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_selected.9.png b/core/res/res/drawable-mdpi/btn_default_selected.9.png
index 415b145..1e900bf 100644
--- a/core/res/res/drawable-mdpi/btn_default_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_normal.9.png b/core/res/res/drawable-mdpi/btn_default_small_normal.9.png
index 5dddd46..bcedd5f 100644
--- a/core/res/res/drawable-mdpi/btn_default_small_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_small_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_normal_disable.9.png b/core/res/res/drawable-mdpi/btn_default_small_normal_disable.9.png
index 6ab5c4a..ac6260f 100644
--- a/core/res/res/drawable-mdpi/btn_default_small_normal_disable.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_small_normal_disable.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png b/core/res/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png
index c65bace..4ee1b3f 100644
--- a/core/res/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_pressed.9.png b/core/res/res/drawable-mdpi/btn_default_small_pressed.9.png
index 43e82f9..25e38f4 100644
--- a/core/res/res/drawable-mdpi/btn_default_small_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_small_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_small_selected.9.png b/core/res/res/drawable-mdpi/btn_default_small_selected.9.png
index 7a376a9..cc209c6 100644
--- a/core/res/res/drawable-mdpi/btn_default_small_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_small_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dialog_disable.png b/core/res/res/drawable-mdpi/btn_dialog_disable.png
index 3de9895..f041cab 100755
--- a/core/res/res/drawable-mdpi/btn_dialog_disable.png
+++ b/core/res/res/drawable-mdpi/btn_dialog_disable.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dialog_normal.png b/core/res/res/drawable-mdpi/btn_dialog_normal.png
index eca5828..a2d27fa 100755
--- a/core/res/res/drawable-mdpi/btn_dialog_normal.png
+++ b/core/res/res/drawable-mdpi/btn_dialog_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dialog_pressed.png b/core/res/res/drawable-mdpi/btn_dialog_pressed.png
index f9c4551..9c9922a 100755
--- a/core/res/res/drawable-mdpi/btn_dialog_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_dialog_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dialog_selected.png b/core/res/res/drawable-mdpi/btn_dialog_selected.png
index b0afd7f..7656de5 100755
--- a/core/res/res/drawable-mdpi/btn_dialog_selected.png
+++ b/core/res/res/drawable-mdpi/btn_dialog_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dropdown_disabled.9.png b/core/res/res/drawable-mdpi/btn_dropdown_disabled.9.png
index 72915b5..f7464c7 100644
--- a/core/res/res/drawable-mdpi/btn_dropdown_disabled.9.png
+++ b/core/res/res/drawable-mdpi/btn_dropdown_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dropdown_disabled_focused.9.png b/core/res/res/drawable-mdpi/btn_dropdown_disabled_focused.9.png
index 438c06a..ffe219f 100644
--- a/core/res/res/drawable-mdpi/btn_dropdown_disabled_focused.9.png
+++ b/core/res/res/drawable-mdpi/btn_dropdown_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dropdown_normal.9.png b/core/res/res/drawable-mdpi/btn_dropdown_normal.9.png
index 8540501..f6e9a19 100644
--- a/core/res/res/drawable-mdpi/btn_dropdown_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_dropdown_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dropdown_pressed.9.png b/core/res/res/drawable-mdpi/btn_dropdown_pressed.9.png
index 9a50396..3bdf52d 100644
--- a/core/res/res/drawable-mdpi/btn_dropdown_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_dropdown_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_dropdown_selected.9.png b/core/res/res/drawable-mdpi/btn_dropdown_selected.9.png
index a0a3fef..087956e 100644
--- a/core/res/res/drawable-mdpi/btn_dropdown_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_dropdown_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_group_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_group_disabled_holo_dark.9.png
new file mode 100644
index 0000000..aa04cc9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_group_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_group_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_group_disabled_holo_light.9.png
new file mode 100644
index 0000000..25aefd2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_group_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_group_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_group_focused_holo_dark.9.png
new file mode 100644
index 0000000..b77cc78
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_group_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_group_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_group_focused_holo_light.9.png
new file mode 100644
index 0000000..aa00c75
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_group_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_group_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_group_pressed_holo_dark.9.png
new file mode 100644
index 0000000..e35333b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_group_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_group_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_group_pressed_holo_light.9.png
new file mode 100644
index 0000000..878fdda
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_group_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_media_player.9.png b/core/res/res/drawable-mdpi/btn_media_player.9.png
index c479e33..3ec3f683 100755
--- a/core/res/res/drawable-mdpi/btn_media_player.9.png
+++ b/core/res/res/drawable-mdpi/btn_media_player.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_media_player_disabled.9.png b/core/res/res/drawable-mdpi/btn_media_player_disabled.9.png
index 8f9d8b4..e74335b 100755
--- a/core/res/res/drawable-mdpi/btn_media_player_disabled.9.png
+++ b/core/res/res/drawable-mdpi/btn_media_player_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_media_player_disabled_selected.9.png b/core/res/res/drawable-mdpi/btn_media_player_disabled_selected.9.png
index 0ba6794..2c6517f 100755
--- a/core/res/res/drawable-mdpi/btn_media_player_disabled_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_media_player_disabled_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_media_player_pressed.9.png b/core/res/res/drawable-mdpi/btn_media_player_pressed.9.png
index f341470..40bee47 100755
--- a/core/res/res/drawable-mdpi/btn_media_player_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_media_player_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_media_player_selected.9.png b/core/res/res/drawable-mdpi/btn_media_player_selected.9.png
index 09f4c992..28d809f 100755
--- a/core/res/res/drawable-mdpi/btn_media_player_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_media_player_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_disabled_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_disabled_off_holo_dark.png
new file mode 100644
index 0000000..f937182
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_disabled_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_disabled_off_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_disabled_off_holo_light.png
new file mode 100644
index 0000000..4c35fb3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_disabled_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_disabled_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_disabled_on_holo_dark.png
new file mode 100644
index 0000000..af38a4f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_disabled_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_disabled_on_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_disabled_on_holo_light.png
new file mode 100644
index 0000000..a368602
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_disabled_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_focused_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_focused_off_holo_dark.png
new file mode 100644
index 0000000..695d0b9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_focused_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_focused_off_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_focused_off_holo_light.png
new file mode 100644
index 0000000..6467bea
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_focused_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_focused_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_focused_on_holo_dark.png
new file mode 100644
index 0000000..813a069
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_focused_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_focused_on_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_focused_on_holo_light.png
new file mode 100644
index 0000000..fe80d69
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_focused_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_normal_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_normal_off_holo_dark.png
new file mode 100644
index 0000000..5b6d906
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_normal_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_normal_off_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_normal_off_holo_light.png
new file mode 100644
index 0000000..e5132ef
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_normal_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_normal_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_normal_on_holo_dark.png
new file mode 100644
index 0000000..e216d35
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_normal_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_normal_on_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_normal_on_holo_light.png
new file mode 100644
index 0000000..ed3946a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_normal_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off.png b/core/res/res/drawable-mdpi/btn_radio_off.png
index d2e42a5..407632b 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_off_holo_dark.png
new file mode 100644
index 0000000..a3cef04
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_off_holo_light.png
new file mode 100644
index 0000000..e8def55
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
index f9cb91c..d6d8a9d 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_dark.png
new file mode 100644
index 0000000..1a9310b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_light.png
new file mode 100644
index 0000000..bc28b5b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected.png b/core/res/res/drawable-mdpi/btn_radio_off_selected.png
index b3071b7..53f3e87 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_selected.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_dark.png
new file mode 100644
index 0000000..2b0ddd1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_light.png
new file mode 100644
index 0000000..745cf19
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on.png b/core/res/res/drawable-mdpi/btn_radio_on.png
index ee14924..25a3ccc 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_on_holo_dark.png
new file mode 100644
index 0000000..9954500
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_on_holo_light.png
new file mode 100644
index 0000000..fa67a43
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
index 2917ba4..c904a35 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_dark.png
new file mode 100644
index 0000000..c15c310
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_light.png
new file mode 100644
index 0000000..aa07c5a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected.png b/core/res/res/drawable-mdpi/btn_radio_on_selected.png
index e1d542a..78e1fc0 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_selected.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_dark.png
new file mode 100644
index 0000000..e96a74f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_light.png
new file mode 100644
index 0000000..c51c96c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_selected_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_pressed_off_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_pressed_off_holo_dark.png
new file mode 100644
index 0000000..9ab32d1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_pressed_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_pressed_off_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_pressed_off_holo_light.png
new file mode 100644
index 0000000..7175853
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_pressed_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_pressed_on_holo_dark.png b/core/res/res/drawable-mdpi/btn_radio_pressed_on_holo_dark.png
new file mode 100644
index 0000000..a6fa7b6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_pressed_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_pressed_on_holo_light.png b/core/res/res/drawable-mdpi/btn_radio_pressed_on_holo_light.png
new file mode 100644
index 0000000..e7a634d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_pressed_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_square_overlay_disabled.png b/core/res/res/drawable-mdpi/btn_square_overlay_disabled.png
index 285fde3..cf7e4ea 100644
--- a/core/res/res/drawable-mdpi/btn_square_overlay_disabled.png
+++ b/core/res/res/drawable-mdpi/btn_square_overlay_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_square_overlay_normal.png b/core/res/res/drawable-mdpi/btn_square_overlay_normal.png
index cc951d6..45fa4fe 100644
--- a/core/res/res/drawable-mdpi/btn_square_overlay_normal.png
+++ b/core/res/res/drawable-mdpi/btn_square_overlay_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_square_overlay_pressed.png b/core/res/res/drawable-mdpi/btn_square_overlay_pressed.png
index a583060..952571b 100644
--- a/core/res/res/drawable-mdpi/btn_square_overlay_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_square_overlay_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_square_overlay_selected.png b/core/res/res/drawable-mdpi/btn_square_overlay_selected.png
index ff9e13e..ce4515e 100644
--- a/core/res/res/drawable-mdpi/btn_square_overlay_selected.png
+++ b/core/res/res/drawable-mdpi/btn_square_overlay_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_star_off_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_star_off_focused_holo_dark.png
new file mode 100644
index 0000000..153e50d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_star_off_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_star_off_focused_holo_light.png b/core/res/res/drawable-mdpi/btn_star_off_focused_holo_light.png
new file mode 100644
index 0000000..379fd24
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_star_off_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_star_off_normal_holo_dark.png b/core/res/res/drawable-mdpi/btn_star_off_normal_holo_dark.png
new file mode 100644
index 0000000..fe35f1c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_star_off_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_star_off_normal_holo_light.png b/core/res/res/drawable-mdpi/btn_star_off_normal_holo_light.png
new file mode 100644
index 0000000..002237f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_star_off_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_star_on_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_star_on_focused_holo_dark.png
new file mode 100644
index 0000000..0ad2583
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_star_on_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_star_on_focused_holo_light.png b/core/res/res/drawable-mdpi/btn_star_on_focused_holo_light.png
new file mode 100644
index 0000000..ce7c3b4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_star_on_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_star_on_normal_holo_dark.png b/core/res/res/drawable-mdpi/btn_star_on_normal_holo_dark.png
new file mode 100644
index 0000000..ee93e88
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_star_on_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_star_on_normal_holo_light.png b/core/res/res/drawable-mdpi/btn_star_on_normal_holo_light.png
new file mode 100644
index 0000000..597fd9a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_star_on_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
new file mode 100755
index 0000000..deecc51
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
new file mode 100755
index 0000000..76d4f05
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
new file mode 100755
index 0000000..0ecd1ae
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
new file mode 100755
index 0000000..a7015ee
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
new file mode 100755
index 0000000..71949b2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
new file mode 100755
index 0000000..351f76b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_holo.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_holo.9.png
new file mode 100755
index 0000000..0ca659e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
new file mode 100755
index 0000000..44b9503
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
new file mode 100755
index 0000000..d70a1fc5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png
new file mode 100755
index 0000000..5f9d2b1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png
new file mode 100755
index 0000000..6723757
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_off_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
new file mode 100755
index 0000000..62b67a5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
new file mode 100755
index 0000000..b8418bc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
new file mode 100755
index 0000000..f9a11a5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
new file mode 100755
index 0000000..e983c8a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
new file mode 100755
index 0000000..ed8407b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
new file mode 100755
index 0000000..31f36b7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png
new file mode 100755
index 0000000..66cbe48
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
new file mode 100755
index 0000000..e023c44
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
new file mode 100755
index 0000000..e4008cf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png
new file mode 100755
index 0000000..56e1157
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png
new file mode 100755
index 0000000..913aed5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_on_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_pressed_holo_light.9.png
new file mode 100644
index 0000000..b6508fc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_toggle_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_divider_holo_dark.png b/core/res/res/drawable-mdpi/cab_divider_holo_dark.png
new file mode 100755
index 0000000..317263a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_divider_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_divider_holo_light.png b/core/res/res/drawable-mdpi/cab_divider_holo_light.png
new file mode 100755
index 0000000..824ad27
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_divider_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_divider_vertical_dark.png b/core/res/res/drawable-mdpi/cab_divider_vertical_dark.png
new file mode 100755
index 0000000..f7ed6df
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_divider_vertical_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_divider_vertical_light.png b/core/res/res/drawable-mdpi/cab_divider_vertical_light.png
new file mode 100755
index 0000000..73ac0d9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_divider_vertical_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_holo_dark.9.png b/core/res/res/drawable-mdpi/cab_holo_dark.9.png
new file mode 100755
index 0000000..7daae1f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_holo_light.9.png b/core/res/res/drawable-mdpi/cab_holo_light.9.png
new file mode 100755
index 0000000..0e64c5c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_ic_close_focused_holo.png b/core/res/res/drawable-mdpi/cab_ic_close_focused_holo.png
new file mode 100755
index 0000000..59149e4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_ic_close_focused_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_ic_close_normal_holo.png b/core/res/res/drawable-mdpi/cab_ic_close_normal_holo.png
new file mode 100755
index 0000000..b378885
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_ic_close_normal_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_ic_close_pressed_holo.png b/core/res/res/drawable-mdpi/cab_ic_close_pressed_holo.png
new file mode 100755
index 0000000..29b0407
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_ic_close_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_disabled_off_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_disabled_off_holo_dark.png
new file mode 100644
index 0000000..e1094be
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_disabled_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_disabled_off_holo_light.png b/core/res/res/drawable-mdpi/checkbox_disabled_off_holo_light.png
new file mode 100644
index 0000000..c17377c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_disabled_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_disabled_on_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_disabled_on_holo_dark.png
new file mode 100644
index 0000000..f2c5290
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_disabled_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_disabled_on_holo_light.png b/core/res/res/drawable-mdpi/checkbox_disabled_on_holo_light.png
new file mode 100644
index 0000000..06bd903
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_disabled_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_focused_off_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_focused_off_holo_dark.png
new file mode 100644
index 0000000..be624c2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_focused_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_focused_off_holo_light.png b/core/res/res/drawable-mdpi/checkbox_focused_off_holo_light.png
new file mode 100644
index 0000000..2493ce2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_focused_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_focused_on_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_focused_on_holo_dark.png
new file mode 100644
index 0000000..7cdc1df
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_focused_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_focused_on_holo_light.png b/core/res/res/drawable-mdpi/checkbox_focused_on_holo_light.png
new file mode 100644
index 0000000..f977e72
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_focused_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_normal_off_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_normal_off_holo_dark.png
new file mode 100644
index 0000000..f824f76
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_normal_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_normal_off_holo_light.png b/core/res/res/drawable-mdpi/checkbox_normal_off_holo_light.png
new file mode 100644
index 0000000..a76f68c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_normal_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_normal_on_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_normal_on_holo_dark.png
new file mode 100644
index 0000000..e4fd418
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_normal_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_normal_on_holo_light.png b/core/res/res/drawable-mdpi/checkbox_normal_on_holo_light.png
new file mode 100644
index 0000000..d572ef5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_normal_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_off_background.png b/core/res/res/drawable-mdpi/checkbox_off_background.png
index e9471e5..6b2124f 100644
--- a/core/res/res/drawable-mdpi/checkbox_off_background.png
+++ b/core/res/res/drawable-mdpi/checkbox_off_background.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_on_background.png b/core/res/res/drawable-mdpi/checkbox_on_background.png
index c247069..56495fc 100644
--- a/core/res/res/drawable-mdpi/checkbox_on_background.png
+++ b/core/res/res/drawable-mdpi/checkbox_on_background.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_pressed_off_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_pressed_off_holo_dark.png
new file mode 100644
index 0000000..686707e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_pressed_off_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_pressed_off_holo_light.png b/core/res/res/drawable-mdpi/checkbox_pressed_off_holo_light.png
new file mode 100644
index 0000000..17dd1da
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_pressed_off_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_pressed_on_holo_dark.png b/core/res/res/drawable-mdpi/checkbox_pressed_on_holo_dark.png
new file mode 100644
index 0000000..8cf2b1b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_pressed_on_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/checkbox_pressed_on_holo_light.png b/core/res/res/drawable-mdpi/checkbox_pressed_on_holo_light.png
new file mode 100644
index 0000000..c2df6da
--- /dev/null
+++ b/core/res/res/drawable-mdpi/checkbox_pressed_on_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png
new file mode 100644
index 0000000..cb3d0f2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png
new file mode 100644
index 0000000..1e2d327
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png
new file mode 100644
index 0000000..ac2d7a3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_divider_horizontal_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_divider_horizontal_holo_dark.9.png
new file mode 100644
index 0000000..77b0999
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_divider_horizontal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_divider_horizontal_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_divider_horizontal_holo_light.9.png
new file mode 100644
index 0000000..3fde76e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_divider_horizontal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_full_holo.9.png b/core/res/res/drawable-mdpi/dialog_full_holo.9.png
new file mode 100644
index 0000000..0ec9421
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_full_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png
new file mode 100644
index 0000000..9d93403
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png
new file mode 100644
index 0000000..1c08e6a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_ic_close_focused_holo_dark.png b/core/res/res/drawable-mdpi/dialog_ic_close_focused_holo_dark.png
new file mode 100644
index 0000000..cc5f4f4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_ic_close_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_ic_close_focused_holo_light.png b/core/res/res/drawable-mdpi/dialog_ic_close_focused_holo_light.png
new file mode 100644
index 0000000..51b4e4d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_ic_close_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_ic_close_normal_holo_dark.png b/core/res/res/drawable-mdpi/dialog_ic_close_normal_holo_dark.png
new file mode 100644
index 0000000..503db7c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_ic_close_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_ic_close_normal_holo_light.png b/core/res/res/drawable-mdpi/dialog_ic_close_normal_holo_light.png
new file mode 100644
index 0000000..6a2fb5e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_ic_close_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_ic_close_pressed_holo_dark.png b/core/res/res/drawable-mdpi/dialog_ic_close_pressed_holo_dark.png
new file mode 100644
index 0000000..3463b668
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_ic_close_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_ic_close_pressed_holo_light.png b/core/res/res/drawable-mdpi/dialog_ic_close_pressed_holo_light.png
new file mode 100644
index 0000000..55dedc6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_ic_close_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo.9.png
new file mode 100644
index 0000000..36da5ca
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_middle_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png
new file mode 100644
index 0000000..741b8dc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png
new file mode 100644
index 0000000..0854fe5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_top_holo.9.png b/core/res/res/drawable-mdpi/dialog_top_holo.9.png
new file mode 100644
index 0000000..1ed519b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_top_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png
new file mode 100644
index 0000000..43c0050
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png
new file mode 100644
index 0000000..bc5fc92
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_bright.9.png b/core/res/res/drawable-mdpi/divider_horizontal_bright.9.png
index 24f2a3f..41b776b 100644
--- a/core/res/res/drawable-mdpi/divider_horizontal_bright.9.png
+++ b/core/res/res/drawable-mdpi/divider_horizontal_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_bright_opaque.9.png b/core/res/res/drawable-mdpi/divider_horizontal_bright_opaque.9.png
index 24f2a3f..eb75a22 100644
--- a/core/res/res/drawable-mdpi/divider_horizontal_bright_opaque.9.png
+++ b/core/res/res/drawable-mdpi/divider_horizontal_bright_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_dark.9.png b/core/res/res/drawable-mdpi/divider_horizontal_dark.9.png
index 470be26..55a5e53 100644
--- a/core/res/res/drawable-mdpi/divider_horizontal_dark.9.png
+++ b/core/res/res/drawable-mdpi/divider_horizontal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png b/core/res/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png
index 24f2a3f..60e2cb2 100644
--- a/core/res/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png
+++ b/core/res/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_dim_dark.9.png b/core/res/res/drawable-mdpi/divider_horizontal_dim_dark.9.png
index 08838ca..cf34613 100644
--- a/core/res/res/drawable-mdpi/divider_horizontal_dim_dark.9.png
+++ b/core/res/res/drawable-mdpi/divider_horizontal_dim_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_holo_dark.9.png b/core/res/res/drawable-mdpi/divider_horizontal_holo_dark.9.png
new file mode 100644
index 0000000..d6548c6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_horizontal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_holo_light.9.png b/core/res/res/drawable-mdpi/divider_horizontal_holo_light.9.png
new file mode 100644
index 0000000..9a42dd2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_horizontal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_bright.9.png b/core/res/res/drawable-mdpi/divider_vertical_bright.9.png
index 395227a..41b776b 100644
--- a/core/res/res/drawable-mdpi/divider_vertical_bright.9.png
+++ b/core/res/res/drawable-mdpi/divider_vertical_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_bright_opaque.9.png b/core/res/res/drawable-mdpi/divider_vertical_bright_opaque.9.png
index 5c537ee..eb75a22 100644
--- a/core/res/res/drawable-mdpi/divider_vertical_bright_opaque.9.png
+++ b/core/res/res/drawable-mdpi/divider_vertical_bright_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_dark.9.png b/core/res/res/drawable-mdpi/divider_vertical_dark.9.png
index 470be26..55a5e53 100644
--- a/core/res/res/drawable-mdpi/divider_vertical_dark.9.png
+++ b/core/res/res/drawable-mdpi/divider_vertical_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_dark_opaque.9.png b/core/res/res/drawable-mdpi/divider_vertical_dark_opaque.9.png
index 8f35315..60e2cb2 100644
--- a/core/res/res/drawable-mdpi/divider_vertical_dark_opaque.9.png
+++ b/core/res/res/drawable-mdpi/divider_vertical_dark_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_holo_dark.9.png b/core/res/res/drawable-mdpi/divider_vertical_holo_dark.9.png
new file mode 100644
index 0000000..c039428
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_vertical_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_holo_light.9.png b/core/res/res/drawable-mdpi/divider_vertical_holo_light.9.png
new file mode 100644
index 0000000..7c4a29f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_vertical_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/dropdown_disabled_focused_holo_dark.9.png
new file mode 100644
index 0000000..d9ad9d3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_disabled_focused_holo_light.9.png b/core/res/res/drawable-mdpi/dropdown_disabled_focused_holo_light.9.png
new file mode 100644
index 0000000..3d82dc6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_disabled_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/dropdown_disabled_holo_dark.9.png
new file mode 100644
index 0000000..15e2993
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/dropdown_disabled_holo_light.9.png
new file mode 100644
index 0000000..4831556
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/dropdown_focused_holo_dark.9.png
new file mode 100644
index 0000000..4021da8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_focused_holo_light.9.png b/core/res/res/drawable-mdpi/dropdown_focused_holo_light.9.png
new file mode 100644
index 0000000..120fe9a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_ic_arrow_disabled_focused_holo_dark.png b/core/res/res/drawable-mdpi/dropdown_ic_arrow_disabled_focused_holo_dark.png
new file mode 100644
index 0000000..d2b3557
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_ic_arrow_disabled_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_ic_arrow_disabled_focused_holo_light.png b/core/res/res/drawable-mdpi/dropdown_ic_arrow_disabled_focused_holo_light.png
new file mode 100644
index 0000000..cf50169
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_ic_arrow_disabled_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_ic_arrow_disabled_holo_dark.png b/core/res/res/drawable-mdpi/dropdown_ic_arrow_disabled_holo_dark.png
new file mode 100644
index 0000000..2663411
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_ic_arrow_disabled_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_ic_arrow_disabled_holo_light.png b/core/res/res/drawable-mdpi/dropdown_ic_arrow_disabled_holo_light.png
new file mode 100644
index 0000000..def24e4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_ic_arrow_disabled_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_ic_arrow_focused_holo_dark.png b/core/res/res/drawable-mdpi/dropdown_ic_arrow_focused_holo_dark.png
new file mode 100644
index 0000000..9196b72
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_ic_arrow_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_ic_arrow_focused_holo_light.png b/core/res/res/drawable-mdpi/dropdown_ic_arrow_focused_holo_light.png
new file mode 100644
index 0000000..85372b9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_ic_arrow_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_ic_arrow_normal_holo_dark.png b/core/res/res/drawable-mdpi/dropdown_ic_arrow_normal_holo_dark.png
new file mode 100644
index 0000000..566be42
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_ic_arrow_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_ic_arrow_normal_holo_light.png b/core/res/res/drawable-mdpi/dropdown_ic_arrow_normal_holo_light.png
new file mode 100644
index 0000000..e600500
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_ic_arrow_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_ic_arrow_pressed_holo_dark.png b/core/res/res/drawable-mdpi/dropdown_ic_arrow_pressed_holo_dark.png
new file mode 100644
index 0000000..ef21dc2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_ic_arrow_pressed_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_ic_arrow_pressed_holo_light.png b/core/res/res/drawable-mdpi/dropdown_ic_arrow_pressed_holo_light.png
new file mode 100644
index 0000000..52fc112
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_ic_arrow_pressed_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_normal_holo_dark.9.png b/core/res/res/drawable-mdpi/dropdown_normal_holo_dark.9.png
new file mode 100644
index 0000000..a5da0cd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_normal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_normal_holo_light.9.png b/core/res/res/drawable-mdpi/dropdown_normal_holo_light.9.png
new file mode 100644
index 0000000..6695af9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_normal_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/dropdown_pressed_holo_dark.9.png
new file mode 100644
index 0000000..e7e70fb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dropdown_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/dropdown_pressed_holo_light.9.png
new file mode 100644
index 0000000..f760c88
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dropdown_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/edit_query.png b/core/res/res/drawable-mdpi/edit_query.png
new file mode 100644
index 0000000..22322cb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/edit_query.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/edit_query_background_normal.9.png b/core/res/res/drawable-mdpi/edit_query_background_normal.9.png
new file mode 100644
index 0000000..8f957b8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/edit_query_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/edit_query_background_pressed.9.png b/core/res/res/drawable-mdpi/edit_query_background_pressed.9.png
new file mode 100644
index 0000000..c88d4d5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/edit_query_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/edit_query_background_selected.9.png b/core/res/res/drawable-mdpi/edit_query_background_selected.9.png
new file mode 100644
index 0000000..ffe5791
--- /dev/null
+++ b/core/res/res/drawable-mdpi/edit_query_background_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_embarrassed.png b/core/res/res/drawable-mdpi/emo_im_embarrassed.png
similarity index 100%
rename from core/res/res/drawable/emo_im_embarrassed.png
rename to core/res/res/drawable-mdpi/emo_im_embarrassed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/expander_ic_maximized.9.png b/core/res/res/drawable-mdpi/expander_ic_maximized.9.png
index d5c3276..465cabd 100644
--- a/core/res/res/drawable-mdpi/expander_ic_maximized.9.png
+++ b/core/res/res/drawable-mdpi/expander_ic_maximized.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/expander_ic_minimized.9.png b/core/res/res/drawable-mdpi/expander_ic_minimized.9.png
index 4515b42..9967ecb 100644
--- a/core/res/res/drawable-mdpi/expander_ic_minimized.9.png
+++ b/core/res/res/drawable-mdpi/expander_ic_minimized.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_ab_back_holo_dark.png b/core/res/res/drawable-mdpi/ic_ab_back_holo_dark.png
new file mode 100644
index 0000000..7aae741
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_ab_back_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_ab_back_holo_light.png b/core/res/res/drawable-mdpi/ic_ab_back_holo_light.png
new file mode 100644
index 0000000..66ef51c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_ab_back_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_aggregated.png b/core/res/res/drawable-mdpi/ic_aggregated.png
index 7c2e2b0..ad42071 100644
--- a/core/res/res/drawable-mdpi/ic_aggregated.png
+++ b/core/res/res/drawable-mdpi/ic_aggregated.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_back.png b/core/res/res/drawable-mdpi/ic_btn_back.png
new file mode 100644
index 0000000..c9bff4c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_btn_back.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_find_next.png b/core/res/res/drawable-mdpi/ic_btn_find_next.png
new file mode 100644
index 0000000..abdc247
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_btn_find_next.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_find_prev.png b/core/res/res/drawable-mdpi/ic_btn_find_prev.png
new file mode 100644
index 0000000..4e3da1d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_btn_find_prev.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_next.png b/core/res/res/drawable-mdpi/ic_btn_next.png
new file mode 100755
index 0000000..c6cf436
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_btn_next.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_round_more_disabled.png b/core/res/res/drawable-mdpi/ic_btn_round_more_disabled.png
index 428edf2..1ab98c9 100644
--- a/core/res/res/drawable-mdpi/ic_btn_round_more_disabled.png
+++ b/core/res/res/drawable-mdpi/ic_btn_round_more_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_round_more_normal.png b/core/res/res/drawable-mdpi/ic_btn_round_more_normal.png
index c2ecb01..ebdc55c 100644
--- a/core/res/res/drawable-mdpi/ic_btn_round_more_normal.png
+++ b/core/res/res/drawable-mdpi/ic_btn_round_more_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_bullet_key_permission.png b/core/res/res/drawable-mdpi/ic_bullet_key_permission.png
index 68ad039..ccb010f 100644
--- a/core/res/res/drawable-mdpi/ic_bullet_key_permission.png
+++ b/core/res/res/drawable-mdpi/ic_bullet_key_permission.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_dialog_close_normal_holo.png b/core/res/res/drawable-mdpi/ic_dialog_close_normal_holo.png
new file mode 100644
index 0000000..7f2a029
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_dialog_close_normal_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_dialog_close_pressed_holo.png b/core/res/res/drawable-mdpi/ic_dialog_close_pressed_holo.png
new file mode 100644
index 0000000..daa8e18
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_dialog_close_pressed_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_dialog_focused_holo.png b/core/res/res/drawable-mdpi/ic_dialog_focused_holo.png
new file mode 100644
index 0000000..179f2de
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_dialog_focused_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_input_add.png b/core/res/res/drawable-mdpi/ic_input_add.png
index 10d9599..00770f8 100644
--- a/core/res/res/drawable-mdpi/ic_input_add.png
+++ b/core/res/res/drawable-mdpi/ic_input_add.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_ff.png b/core/res/res/drawable-mdpi/ic_media_ff.png
old mode 100644
new mode 100755
index d99779d..ce7e195
--- a/core/res/res/drawable-mdpi/ic_media_ff.png
+++ b/core/res/res/drawable-mdpi/ic_media_ff.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_next.png b/core/res/res/drawable-mdpi/ic_media_next.png
old mode 100644
new mode 100755
index cee4930..84f38e8
--- a/core/res/res/drawable-mdpi/ic_media_next.png
+++ b/core/res/res/drawable-mdpi/ic_media_next.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_pause.png b/core/res/res/drawable-mdpi/ic_media_pause.png
old mode 100644
new mode 100755
index 9e8b675..688118e
--- a/core/res/res/drawable-mdpi/ic_media_pause.png
+++ b/core/res/res/drawable-mdpi/ic_media_pause.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_play.png b/core/res/res/drawable-mdpi/ic_media_play.png
old mode 100644
new mode 100755
index 41cd65f..7aa7af8
--- a/core/res/res/drawable-mdpi/ic_media_play.png
+++ b/core/res/res/drawable-mdpi/ic_media_play.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_previous.png b/core/res/res/drawable-mdpi/ic_media_previous.png
old mode 100644
new mode 100755
index 1be95b4..1bba544
--- a/core/res/res/drawable-mdpi/ic_media_previous.png
+++ b/core/res/res/drawable-mdpi/ic_media_previous.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_rew.png b/core/res/res/drawable-mdpi/ic_media_rew.png
old mode 100644
new mode 100755
index 8311508..132df7f
--- a/core/res/res/drawable-mdpi/ic_media_rew.png
+++ b/core/res/res/drawable-mdpi/ic_media_rew.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_account_list.png b/core/res/res/drawable-mdpi/ic_menu_account_list.png
index d3af01b..f0945b2 100644
--- a/core/res/res/drawable-mdpi/ic_menu_account_list.png
+++ b/core/res/res/drawable-mdpi/ic_menu_account_list.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_add.png b/core/res/res/drawable-mdpi/ic_menu_add.png
old mode 100644
new mode 100755
index 57a4099..6752bfd
--- a/core/res/res/drawable-mdpi/ic_menu_add.png
+++ b/core/res/res/drawable-mdpi/ic_menu_add.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_agenda.png b/core/res/res/drawable-mdpi/ic_menu_agenda.png
old mode 100644
new mode 100755
index 3fb0031..9f2c1dc
--- a/core/res/res/drawable-mdpi/ic_menu_agenda.png
+++ b/core/res/res/drawable-mdpi/ic_menu_agenda.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_allfriends.png b/core/res/res/drawable-mdpi/ic_menu_allfriends.png
old mode 100644
new mode 100755
index e4e2727..a5bd331
--- a/core/res/res/drawable-mdpi/ic_menu_allfriends.png
+++ b/core/res/res/drawable-mdpi/ic_menu_allfriends.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_always_landscape_portrait.png b/core/res/res/drawable-mdpi/ic_menu_always_landscape_portrait.png
index 451ccc9..68911c4 100644
--- a/core/res/res/drawable-mdpi/ic_menu_always_landscape_portrait.png
+++ b/core/res/res/drawable-mdpi/ic_menu_always_landscape_portrait.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_archive.png b/core/res/res/drawable-mdpi/ic_menu_archive.png
index 7231fbb5..a4599e3 100644
--- a/core/res/res/drawable-mdpi/ic_menu_archive.png
+++ b/core/res/res/drawable-mdpi/ic_menu_archive.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_attachment.png b/core/res/res/drawable-mdpi/ic_menu_attachment.png
index 83e0687..89d626f 100644
--- a/core/res/res/drawable-mdpi/ic_menu_attachment.png
+++ b/core/res/res/drawable-mdpi/ic_menu_attachment.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_back.png b/core/res/res/drawable-mdpi/ic_menu_back.png
index 91fa3e7..5ce50eb 100644
--- a/core/res/res/drawable-mdpi/ic_menu_back.png
+++ b/core/res/res/drawable-mdpi/ic_menu_back.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_block.png b/core/res/res/drawable-mdpi/ic_menu_block.png
index fce9297..422eeb1 100644
--- a/core/res/res/drawable-mdpi/ic_menu_block.png
+++ b/core/res/res/drawable-mdpi/ic_menu_block.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_blocked_user.png b/core/res/res/drawable-mdpi/ic_menu_blocked_user.png
index 55322459..5a5619b 100644
--- a/core/res/res/drawable-mdpi/ic_menu_blocked_user.png
+++ b/core/res/res/drawable-mdpi/ic_menu_blocked_user.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_call.png b/core/res/res/drawable-mdpi/ic_menu_call.png
index eb05949..a63f86b 100644
--- a/core/res/res/drawable-mdpi/ic_menu_call.png
+++ b/core/res/res/drawable-mdpi/ic_menu_call.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_camera.png b/core/res/res/drawable-mdpi/ic_menu_camera.png
old mode 100644
new mode 100755
index 7fddf7c..cdf7ca3
--- a/core/res/res/drawable-mdpi/ic_menu_camera.png
+++ b/core/res/res/drawable-mdpi/ic_menu_camera.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_cc.png b/core/res/res/drawable-mdpi/ic_menu_cc.png
index 97fb663..4876021 100644
--- a/core/res/res/drawable-mdpi/ic_menu_cc.png
+++ b/core/res/res/drawable-mdpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_chat_dashboard.png b/core/res/res/drawable-mdpi/ic_menu_chat_dashboard.png
index 54e9023..37fd3cbd 100644
--- a/core/res/res/drawable-mdpi/ic_menu_chat_dashboard.png
+++ b/core/res/res/drawable-mdpi/ic_menu_chat_dashboard.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_clear_playlist.png b/core/res/res/drawable-mdpi/ic_menu_clear_playlist.png
index 52791a0..750db62 100644
--- a/core/res/res/drawable-mdpi/ic_menu_clear_playlist.png
+++ b/core/res/res/drawable-mdpi/ic_menu_clear_playlist.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_close_clear_cancel.png b/core/res/res/drawable-mdpi/ic_menu_close_clear_cancel.png
index 04a76b5..78222ea 100644
--- a/core/res/res/drawable-mdpi/ic_menu_close_clear_cancel.png
+++ b/core/res/res/drawable-mdpi/ic_menu_close_clear_cancel.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_compass.png b/core/res/res/drawable-mdpi/ic_menu_compass.png
index b5f885e..7717dde 100644
--- a/core/res/res/drawable-mdpi/ic_menu_compass.png
+++ b/core/res/res/drawable-mdpi/ic_menu_compass.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_compose.png b/core/res/res/drawable-mdpi/ic_menu_compose.png
index 121228c..1b4733e 100644
--- a/core/res/res/drawable-mdpi/ic_menu_compose.png
+++ b/core/res/res/drawable-mdpi/ic_menu_compose.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_copy_dark.png b/core/res/res/drawable-mdpi/ic_menu_copy_dark.png
new file mode 100644
index 0000000..35c3318
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_copy_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_copy_light.png b/core/res/res/drawable-mdpi/ic_menu_copy_light.png
new file mode 100644
index 0000000..3b179d8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_copy_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_crop.png b/core/res/res/drawable-mdpi/ic_menu_crop.png
old mode 100644
new mode 100755
index 4892ca0..c0df996
--- a/core/res/res/drawable-mdpi/ic_menu_crop.png
+++ b/core/res/res/drawable-mdpi/ic_menu_crop.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_cut_dark.png b/core/res/res/drawable-mdpi/ic_menu_cut_dark.png
new file mode 100644
index 0000000..dfe8b4a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_cut_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_cut_light.png b/core/res/res/drawable-mdpi/ic_menu_cut_light.png
new file mode 100644
index 0000000..748dc9b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_cut_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_day.png b/core/res/res/drawable-mdpi/ic_menu_day.png
old mode 100644
new mode 100755
index f366c1b..db5d3a4
--- a/core/res/res/drawable-mdpi/ic_menu_day.png
+++ b/core/res/res/drawable-mdpi/ic_menu_day.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_delete.png b/core/res/res/drawable-mdpi/ic_menu_delete.png
old mode 100644
new mode 100755
index bb533f7..7d95494
--- a/core/res/res/drawable-mdpi/ic_menu_delete.png
+++ b/core/res/res/drawable-mdpi/ic_menu_delete.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_directions.png b/core/res/res/drawable-mdpi/ic_menu_directions.png
old mode 100644
new mode 100755
index fbac73d..00a288f
--- a/core/res/res/drawable-mdpi/ic_menu_directions.png
+++ b/core/res/res/drawable-mdpi/ic_menu_directions.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_edit.png b/core/res/res/drawable-mdpi/ic_menu_edit.png
old mode 100644
new mode 100755
index 1de85ca..41a9c2e
--- a/core/res/res/drawable-mdpi/ic_menu_edit.png
+++ b/core/res/res/drawable-mdpi/ic_menu_edit.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_emoticons.png b/core/res/res/drawable-mdpi/ic_menu_emoticons.png
index 95fd566..e8c4e47 100644
--- a/core/res/res/drawable-mdpi/ic_menu_emoticons.png
+++ b/core/res/res/drawable-mdpi/ic_menu_emoticons.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_end_conversation.png b/core/res/res/drawable-mdpi/ic_menu_end_conversation.png
index 9ddb03e..0ea0fcb 100644
--- a/core/res/res/drawable-mdpi/ic_menu_end_conversation.png
+++ b/core/res/res/drawable-mdpi/ic_menu_end_conversation.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_find.png b/core/res/res/drawable-mdpi/ic_menu_find.png
new file mode 100644
index 0000000..4d96348
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_find.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_forward.png b/core/res/res/drawable-mdpi/ic_menu_forward.png
index 632e4f6..0936fac 100644
--- a/core/res/res/drawable-mdpi/ic_menu_forward.png
+++ b/core/res/res/drawable-mdpi/ic_menu_forward.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_friendslist.png b/core/res/res/drawable-mdpi/ic_menu_friendslist.png
index 45baa48..8ec6b1a 100644
--- a/core/res/res/drawable-mdpi/ic_menu_friendslist.png
+++ b/core/res/res/drawable-mdpi/ic_menu_friendslist.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_gallery.png b/core/res/res/drawable-mdpi/ic_menu_gallery.png
old mode 100644
new mode 100755
index 2d74993..a3deef1
--- a/core/res/res/drawable-mdpi/ic_menu_gallery.png
+++ b/core/res/res/drawable-mdpi/ic_menu_gallery.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_goto.png b/core/res/res/drawable-mdpi/ic_menu_goto.png
index 3810d54..40183eb 100644
--- a/core/res/res/drawable-mdpi/ic_menu_goto.png
+++ b/core/res/res/drawable-mdpi/ic_menu_goto.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_help.png b/core/res/res/drawable-mdpi/ic_menu_help.png
index 4f65a61..7c55dfd 100644
--- a/core/res/res/drawable-mdpi/ic_menu_help.png
+++ b/core/res/res/drawable-mdpi/ic_menu_help.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_home.png b/core/res/res/drawable-mdpi/ic_menu_home.png
index 23dcecd..34943f6 100644
--- a/core/res/res/drawable-mdpi/ic_menu_home.png
+++ b/core/res/res/drawable-mdpi/ic_menu_home.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_info_details.png b/core/res/res/drawable-mdpi/ic_menu_info_details.png
old mode 100644
new mode 100755
index 1f8a7cd..1786d1e
--- a/core/res/res/drawable-mdpi/ic_menu_info_details.png
+++ b/core/res/res/drawable-mdpi/ic_menu_info_details.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_invite.png b/core/res/res/drawable-mdpi/ic_menu_invite.png
index ccbf317..7577e6d 100644
--- a/core/res/res/drawable-mdpi/ic_menu_invite.png
+++ b/core/res/res/drawable-mdpi/ic_menu_invite.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_login.png b/core/res/res/drawable-mdpi/ic_menu_login.png
index d489787..2b856bc 100644
--- a/core/res/res/drawable-mdpi/ic_menu_login.png
+++ b/core/res/res/drawable-mdpi/ic_menu_login.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_manage.png b/core/res/res/drawable-mdpi/ic_menu_manage.png
old mode 100644
new mode 100755
index a96bb0c..f155bbc
--- a/core/res/res/drawable-mdpi/ic_menu_manage.png
+++ b/core/res/res/drawable-mdpi/ic_menu_manage.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_mapmode.png b/core/res/res/drawable-mdpi/ic_menu_mapmode.png
index ef453e1..d85cab5 100644
--- a/core/res/res/drawable-mdpi/ic_menu_mapmode.png
+++ b/core/res/res/drawable-mdpi/ic_menu_mapmode.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_mark.png b/core/res/res/drawable-mdpi/ic_menu_mark.png
index d2a0116..5e95da7 100644
--- a/core/res/res/drawable-mdpi/ic_menu_mark.png
+++ b/core/res/res/drawable-mdpi/ic_menu_mark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_month.png b/core/res/res/drawable-mdpi/ic_menu_month.png
old mode 100644
new mode 100755
index d5285ab..bf6cb89
--- a/core/res/res/drawable-mdpi/ic_menu_month.png
+++ b/core/res/res/drawable-mdpi/ic_menu_month.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_more.png b/core/res/res/drawable-mdpi/ic_menu_more.png
index 168dcd3..b9fc5fa 100644
--- a/core/res/res/drawable-mdpi/ic_menu_more.png
+++ b/core/res/res/drawable-mdpi/ic_menu_more.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_dark.png
new file mode 100644
index 0000000..6f87b11
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_light.png
new file mode 100644
index 0000000..04dac38
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png
new file mode 100644
index 0000000..5580af6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png
new file mode 100644
index 0000000..fc2081a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_my_calendar.png b/core/res/res/drawable-mdpi/ic_menu_my_calendar.png
old mode 100644
new mode 100755
index c1c9c20..0c88fd3
--- a/core/res/res/drawable-mdpi/ic_menu_my_calendar.png
+++ b/core/res/res/drawable-mdpi/ic_menu_my_calendar.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_mylocation.png b/core/res/res/drawable-mdpi/ic_menu_mylocation.png
old mode 100644
new mode 100755
index 9ee5024..fdbd5ca
--- a/core/res/res/drawable-mdpi/ic_menu_mylocation.png
+++ b/core/res/res/drawable-mdpi/ic_menu_mylocation.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_myplaces.png b/core/res/res/drawable-mdpi/ic_menu_myplaces.png
index 040f9b1..06f11ba 100644
--- a/core/res/res/drawable-mdpi/ic_menu_myplaces.png
+++ b/core/res/res/drawable-mdpi/ic_menu_myplaces.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_notifications.png b/core/res/res/drawable-mdpi/ic_menu_notifications.png
index f361e82..866d4e0 100644
--- a/core/res/res/drawable-mdpi/ic_menu_notifications.png
+++ b/core/res/res/drawable-mdpi/ic_menu_notifications.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_paste_dark.png b/core/res/res/drawable-mdpi/ic_menu_paste_dark.png
new file mode 100644
index 0000000..caec299
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_paste_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_paste_light.png b/core/res/res/drawable-mdpi/ic_menu_paste_light.png
new file mode 100644
index 0000000..434f5d1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_paste_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_play_clip.png b/core/res/res/drawable-mdpi/ic_menu_play_clip.png
index 8a01f70..4669947 100644
--- a/core/res/res/drawable-mdpi/ic_menu_play_clip.png
+++ b/core/res/res/drawable-mdpi/ic_menu_play_clip.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_preferences.png b/core/res/res/drawable-mdpi/ic_menu_preferences.png
index 2f34c76..60dbff6 100644
--- a/core/res/res/drawable-mdpi/ic_menu_preferences.png
+++ b/core/res/res/drawable-mdpi/ic_menu_preferences.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_recent_history.png b/core/res/res/drawable-mdpi/ic_menu_recent_history.png
index 4722cbb..4ccae5d 100644
--- a/core/res/res/drawable-mdpi/ic_menu_recent_history.png
+++ b/core/res/res/drawable-mdpi/ic_menu_recent_history.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_refresh.png b/core/res/res/drawable-mdpi/ic_menu_refresh.png
index c3551bec..77d70dd 100644
--- a/core/res/res/drawable-mdpi/ic_menu_refresh.png
+++ b/core/res/res/drawable-mdpi/ic_menu_refresh.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_report_image.png b/core/res/res/drawable-mdpi/ic_menu_report_image.png
index 4f128d7..393d727 100644
--- a/core/res/res/drawable-mdpi/ic_menu_report_image.png
+++ b/core/res/res/drawable-mdpi/ic_menu_report_image.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_revert.png b/core/res/res/drawable-mdpi/ic_menu_revert.png
index 2afc8e8..e7e04f5 100644
--- a/core/res/res/drawable-mdpi/ic_menu_revert.png
+++ b/core/res/res/drawable-mdpi/ic_menu_revert.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_rotate.png b/core/res/res/drawable-mdpi/ic_menu_rotate.png
old mode 100644
new mode 100755
index 547d4ca..27368b2
--- a/core/res/res/drawable-mdpi/ic_menu_rotate.png
+++ b/core/res/res/drawable-mdpi/ic_menu_rotate.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_save.png b/core/res/res/drawable-mdpi/ic_menu_save.png
index ffd7fb7..36d50b3 100644
--- a/core/res/res/drawable-mdpi/ic_menu_save.png
+++ b/core/res/res/drawable-mdpi/ic_menu_save.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_search.png b/core/res/res/drawable-mdpi/ic_menu_search.png
old mode 100644
new mode 100755
index ef949d5..94446db
--- a/core/res/res/drawable-mdpi/ic_menu_search.png
+++ b/core/res/res/drawable-mdpi/ic_menu_search.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_send.png b/core/res/res/drawable-mdpi/ic_menu_send.png
old mode 100644
new mode 100755
index 672ab71..74c096d
--- a/core/res/res/drawable-mdpi/ic_menu_send.png
+++ b/core/res/res/drawable-mdpi/ic_menu_send.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_set_as.png b/core/res/res/drawable-mdpi/ic_menu_set_as.png
old mode 100644
new mode 100755
index e2c92ef..cb9dc49
--- a/core/res/res/drawable-mdpi/ic_menu_set_as.png
+++ b/core/res/res/drawable-mdpi/ic_menu_set_as.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_share.png b/core/res/res/drawable-mdpi/ic_menu_share.png
old mode 100644
new mode 100755
index b7c1c25..44db9b1
--- a/core/res/res/drawable-mdpi/ic_menu_share.png
+++ b/core/res/res/drawable-mdpi/ic_menu_share.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_slideshow.png b/core/res/res/drawable-mdpi/ic_menu_slideshow.png
index 08a40f2..04fda7f 100644
--- a/core/res/res/drawable-mdpi/ic_menu_slideshow.png
+++ b/core/res/res/drawable-mdpi/ic_menu_slideshow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_sort_alphabetically.png b/core/res/res/drawable-mdpi/ic_menu_sort_alphabetically.png
old mode 100644
new mode 100755
index 06d5c74..2583eb8
--- a/core/res/res/drawable-mdpi/ic_menu_sort_alphabetically.png
+++ b/core/res/res/drawable-mdpi/ic_menu_sort_alphabetically.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_sort_by_size.png b/core/res/res/drawable-mdpi/ic_menu_sort_by_size.png
old mode 100644
new mode 100755
index 3c39953..65e2786
--- a/core/res/res/drawable-mdpi/ic_menu_sort_by_size.png
+++ b/core/res/res/drawable-mdpi/ic_menu_sort_by_size.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_star.png b/core/res/res/drawable-mdpi/ic_menu_star.png
old mode 100644
new mode 100755
index cf50cf9..527d74a
--- a/core/res/res/drawable-mdpi/ic_menu_star.png
+++ b/core/res/res/drawable-mdpi/ic_menu_star.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_start_conversation.png b/core/res/res/drawable-mdpi/ic_menu_start_conversation.png
index 10ef649..aadcc2f 100644
--- a/core/res/res/drawable-mdpi/ic_menu_start_conversation.png
+++ b/core/res/res/drawable-mdpi/ic_menu_start_conversation.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_stop.png b/core/res/res/drawable-mdpi/ic_menu_stop.png
index 51efa91..4fc825c 100644
--- a/core/res/res/drawable-mdpi/ic_menu_stop.png
+++ b/core/res/res/drawable-mdpi/ic_menu_stop.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_today.png b/core/res/res/drawable-mdpi/ic_menu_today.png
old mode 100644
new mode 100755
index 2b15f29..c63b6af
--- a/core/res/res/drawable-mdpi/ic_menu_today.png
+++ b/core/res/res/drawable-mdpi/ic_menu_today.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_upload.png b/core/res/res/drawable-mdpi/ic_menu_upload.png
old mode 100644
new mode 100755
index 04f3a2c8..1c0dd3f
--- a/core/res/res/drawable-mdpi/ic_menu_upload.png
+++ b/core/res/res/drawable-mdpi/ic_menu_upload.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_upload_you_tube.png b/core/res/res/drawable-mdpi/ic_menu_upload_you_tube.png
old mode 100644
new mode 100755
index add453b..0095564
--- a/core/res/res/drawable-mdpi/ic_menu_upload_you_tube.png
+++ b/core/res/res/drawable-mdpi/ic_menu_upload_you_tube.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_view.png b/core/res/res/drawable-mdpi/ic_menu_view.png
old mode 100644
new mode 100755
index 6aeac7f..69828a9
--- a/core/res/res/drawable-mdpi/ic_menu_view.png
+++ b/core/res/res/drawable-mdpi/ic_menu_view.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_week.png b/core/res/res/drawable-mdpi/ic_menu_week.png
old mode 100644
new mode 100755
index cba437c..62cd65e
--- a/core/res/res/drawable-mdpi/ic_menu_week.png
+++ b/core/res/res/drawable-mdpi/ic_menu_week.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_zoom.png b/core/res/res/drawable-mdpi/ic_menu_zoom.png
index 9695c69..0b8c4e8 100644
--- a/core/res/res/drawable-mdpi/ic_menu_zoom.png
+++ b/core/res/res/drawable-mdpi/ic_menu_zoom.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_search_category_default.png b/core/res/res/drawable-mdpi/ic_search_category_default.png
old mode 100644
new mode 100755
index 9b55383..94446db
--- a/core/res/res/drawable-mdpi/ic_search_category_default.png
+++ b/core/res/res/drawable-mdpi/ic_search_category_default.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
index 7ddeba5..ef91dc4 100644
--- a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
index 7201e58..f3d4204b 100644
--- a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
index 8546c5f..4e88b37 100755
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
index a98a29a..8020846 100755
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png
index 6d579cb..b7aee1ba 100755
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_gray.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_gray.9.png
index 4440abc..adbb146 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_gray.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_gray.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_green.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_green.9.png
index 26c07e6..e8be7bf 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_green.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_green.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_red.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_red.9.png
index 684f8ac..120a9d8 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_red.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_red.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_yellow.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_yellow.9.png
index 56cfce33a..60ec146 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_yellow.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_confirm_yellow.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_normal.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_normal.9.png
index d4e1929..7477453 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_normal.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_pressed.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_pressed.9.png
index 39cd1e5..c79a35c 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_left_end_pressed.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_left_end_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_gray.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_gray.9.png
index a6ee329..4ce09fa 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_gray.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_gray.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_green.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_green.9.png
index 386ed9d..9d7565f 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_green.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_green.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_red.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_red.9.png
index 0242a42..d5f9bd8 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_red.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_red.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_yellow.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_yellow.9.png
index b8c2e18..5b9c5b4 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_yellow.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_confirm_yellow.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_normal.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_normal.9.png
index 4c608ee..2e6ca2e 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_normal.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_pressed.9.png b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_pressed.9.png
index 8bdfd84..f41750d 100644
--- a/core/res/res/drawable-mdpi/jog_tab_bar_right_end_pressed.9.png
+++ b/core/res/res/drawable-mdpi/jog_tab_bar_right_end_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_left_confirm_gray.png b/core/res/res/drawable-mdpi/jog_tab_left_confirm_gray.png
old mode 100755
new mode 100644
index 3dce451..e8544ff
--- a/core/res/res/drawable-mdpi/jog_tab_left_confirm_gray.png
+++ b/core/res/res/drawable-mdpi/jog_tab_left_confirm_gray.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_left_confirm_green.png b/core/res/res/drawable-mdpi/jog_tab_left_confirm_green.png
old mode 100755
new mode 100644
index 829b146..d0ba8f8
--- a/core/res/res/drawable-mdpi/jog_tab_left_confirm_green.png
+++ b/core/res/res/drawable-mdpi/jog_tab_left_confirm_green.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_left_confirm_red.png b/core/res/res/drawable-mdpi/jog_tab_left_confirm_red.png
index f2ceb2e..5188c86 100644
--- a/core/res/res/drawable-mdpi/jog_tab_left_confirm_red.png
+++ b/core/res/res/drawable-mdpi/jog_tab_left_confirm_red.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_left_confirm_yellow.png b/core/res/res/drawable-mdpi/jog_tab_left_confirm_yellow.png
old mode 100755
new mode 100644
index 5a29262..861e17a
--- a/core/res/res/drawable-mdpi/jog_tab_left_confirm_yellow.png
+++ b/core/res/res/drawable-mdpi/jog_tab_left_confirm_yellow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_left_normal.png b/core/res/res/drawable-mdpi/jog_tab_left_normal.png
old mode 100755
new mode 100644
index eb91e97..7af1b85
--- a/core/res/res/drawable-mdpi/jog_tab_left_normal.png
+++ b/core/res/res/drawable-mdpi/jog_tab_left_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_left_pressed.png b/core/res/res/drawable-mdpi/jog_tab_left_pressed.png
old mode 100755
new mode 100644
index 9951992..b76e83e
--- a/core/res/res/drawable-mdpi/jog_tab_left_pressed.png
+++ b/core/res/res/drawable-mdpi/jog_tab_left_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_right_confirm_gray.png b/core/res/res/drawable-mdpi/jog_tab_right_confirm_gray.png
old mode 100755
new mode 100644
index d446480..814a50d
--- a/core/res/res/drawable-mdpi/jog_tab_right_confirm_gray.png
+++ b/core/res/res/drawable-mdpi/jog_tab_right_confirm_gray.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_right_confirm_green.png b/core/res/res/drawable-mdpi/jog_tab_right_confirm_green.png
old mode 100755
new mode 100644
index 96d7acb..cf157fc
--- a/core/res/res/drawable-mdpi/jog_tab_right_confirm_green.png
+++ b/core/res/res/drawable-mdpi/jog_tab_right_confirm_green.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_right_confirm_red.png b/core/res/res/drawable-mdpi/jog_tab_right_confirm_red.png
index 2e1e105..74f2935 100644
--- a/core/res/res/drawable-mdpi/jog_tab_right_confirm_red.png
+++ b/core/res/res/drawable-mdpi/jog_tab_right_confirm_red.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_right_confirm_yellow.png b/core/res/res/drawable-mdpi/jog_tab_right_confirm_yellow.png
old mode 100755
new mode 100644
index 8224c38..6655731
--- a/core/res/res/drawable-mdpi/jog_tab_right_confirm_yellow.png
+++ b/core/res/res/drawable-mdpi/jog_tab_right_confirm_yellow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_right_normal.png b/core/res/res/drawable-mdpi/jog_tab_right_normal.png
old mode 100755
new mode 100644
index f2113f2..479c9a5
--- a/core/res/res/drawable-mdpi/jog_tab_right_normal.png
+++ b/core/res/res/drawable-mdpi/jog_tab_right_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/jog_tab_right_pressed.png b/core/res/res/drawable-mdpi/jog_tab_right_pressed.png
old mode 100755
new mode 100644
index 65cd51e..454aaf2
--- a/core/res/res/drawable-mdpi/jog_tab_right_pressed.png
+++ b/core/res/res/drawable-mdpi/jog_tab_right_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_activated_holo.9.png b/core/res/res/drawable-mdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..01ec674
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_activated_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_divider_holo_dark.9.png b/core/res/res/drawable-mdpi/list_divider_holo_dark.9.png
new file mode 100644
index 0000000..7b18202
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_divider_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_divider_holo_light.9.png b/core/res/res/drawable-mdpi/list_divider_holo_light.9.png
new file mode 100644
index 0000000..17bcbb4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_divider_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_divider_horizontal_holo_dark.9.png b/core/res/res/drawable-mdpi/list_divider_horizontal_holo_dark.9.png
new file mode 100644
index 0000000..0a4347f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_divider_horizontal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_focused_holo.9.png b/core/res/res/drawable-mdpi/list_focused_holo.9.png
new file mode 100644
index 0000000..23385df
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_longpressed_holo.9.png b/core/res/res/drawable-mdpi/list_longpressed_holo.9.png
new file mode 100644
index 0000000..3312a51
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_longpressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/list_pressed_holo_dark.9.png
new file mode 100644
index 0000000..bbb14d8
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..72380d4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_section_divider_holo_dark.9.png b/core/res/res/drawable-mdpi/list_section_divider_holo_dark.9.png
new file mode 100644
index 0000000..953d7bf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_section_divider_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_section_divider_holo_light.9.png b/core/res/res/drawable-mdpi/list_section_divider_holo_light.9.png
new file mode 100644
index 0000000..e5fe664
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_section_divider_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_section_header_holo_dark.9.png b/core/res/res/drawable-mdpi/list_section_header_holo_dark.9.png
new file mode 100644
index 0000000..48dfea0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_section_header_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_section_header_holo_light.9.png b/core/res/res/drawable-mdpi/list_section_header_holo_light.9.png
new file mode 100644
index 0000000..36a046b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_section_header_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/list_selected_holo_dark.9.png
new file mode 100644
index 0000000..c453506
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selected_holo_light.9.png b/core/res/res/drawable-mdpi/list_selected_holo_light.9.png
new file mode 100644
index 0000000..58b6b3c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/list_selector_activated_holo_dark.9.png
new file mode 100644
index 0000000..66bc259
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_activated_holo_light.9.png b/core/res/res/drawable-mdpi/list_selector_activated_holo_light.9.png
new file mode 100644
index 0000000..c5822b1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_default.9.png b/core/res/res/drawable-mdpi/list_selector_background_default.9.png
new file mode 100644
index 0000000..cac71b0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_default_light.9.png b/core/res/res/drawable-mdpi/list_selector_background_default_light.9.png
new file mode 100644
index 0000000..bdedffd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_default_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png b/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png
index 43c36cb..60f19fe 100644
--- a/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_disabled_light.9.png b/core/res/res/drawable-mdpi/list_selector_background_disabled_light.9.png
new file mode 100644
index 0000000..bc992612
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_disabled_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_focus.9.png b/core/res/res/drawable-mdpi/list_selector_background_focus.9.png
index 53a7eac..421e734 100644
--- a/core/res/res/drawable-mdpi/list_selector_background_focus.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_focused.9.png b/core/res/res/drawable-mdpi/list_selector_background_focused.9.png
new file mode 100644
index 0000000..5b1f195
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_focused_light.9.png b/core/res/res/drawable-mdpi/list_selector_background_focused_light.9.png
new file mode 100644
index 0000000..5b1f195
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_focused_selected.9.png b/core/res/res/drawable-mdpi/list_selector_background_focused_selected.9.png
new file mode 100644
index 0000000..e5b0c1d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_focused_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png b/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png
index 0818761..7817667 100644
--- a/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_longpress_light.9.png b/core/res/res/drawable-mdpi/list_selector_background_longpress_light.9.png
new file mode 100644
index 0000000..646fc69
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_longpress_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png b/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png
index 8bd86b2..1531d9d 100644
--- a/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_pressed_light.9.png b/core/res/res/drawable-mdpi/list_selector_background_pressed_light.9.png
new file mode 100644
index 0000000..fdf6f49
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_pressed_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_selected.9.png b/core/res/res/drawable-mdpi/list_selector_background_selected.9.png
new file mode 100644
index 0000000..a4ac1e3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_background_selected_light.9.png b/core/res/res/drawable-mdpi/list_selector_background_selected_light.9.png
new file mode 100644
index 0000000..d086194
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_background_selected_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/list_selector_disabled_holo_dark.9.png
new file mode 100644
index 0000000..92da2f0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/list_selector_disabled_holo_light.9.png
new file mode 100644
index 0000000..42cb646
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/list_selector_focused_holo_dark.9.png
new file mode 100644
index 0000000..eb4555d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_focused_holo_light.9.png b/core/res/res/drawable-mdpi/list_selector_focused_holo_light.9.png
new file mode 100644
index 0000000..d799fbf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_multiselect_holo_dark.9.png b/core/res/res/drawable-mdpi/list_selector_multiselect_holo_dark.9.png
new file mode 100644
index 0000000..2c3647e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_multiselect_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_multiselect_holo_light.9.png b/core/res/res/drawable-mdpi/list_selector_multiselect_holo_light.9.png
new file mode 100644
index 0000000..860c58e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_multiselect_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/list_selector_pressed_holo_dark.9.png
new file mode 100644
index 0000000..bf36a43
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selector_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/list_selector_pressed_holo_light.9.png
new file mode 100644
index 0000000..0f0be2a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/list_selector_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_background.9.png b/core/res/res/drawable-mdpi/menu_background.9.png
index 41a3d34..9f16df9 100644
--- a/core/res/res/drawable-mdpi/menu_background.9.png
+++ b/core/res/res/drawable-mdpi/menu_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_background_fill_parent_width.9.png b/core/res/res/drawable-mdpi/menu_background_fill_parent_width.9.png
index 1ddf091..da3011b 100644
--- a/core/res/res/drawable-mdpi/menu_background_fill_parent_width.9.png
+++ b/core/res/res/drawable-mdpi/menu_background_fill_parent_width.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png
new file mode 100644
index 0000000..7d5c10c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png
new file mode 100644
index 0000000..a0d3094d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/menu_dropdown_panel_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menu_submenu_background.9.png b/core/res/res/drawable-mdpi/menu_submenu_background.9.png
index 26f24bc..a153532 100644
--- a/core/res/res/drawable-mdpi/menu_submenu_background.9.png
+++ b/core/res/res/drawable-mdpi/menu_submenu_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/minitab_lt_focus.9.png b/core/res/res/drawable-mdpi/minitab_lt_focus.9.png
new file mode 100644
index 0000000..415c571
--- /dev/null
+++ b/core/res/res/drawable-mdpi/minitab_lt_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/minitab_lt_press.9.png b/core/res/res/drawable-mdpi/minitab_lt_press.9.png
new file mode 100644
index 0000000..4166543
--- /dev/null
+++ b/core/res/res/drawable-mdpi/minitab_lt_press.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/minitab_lt_selected.9.png b/core/res/res/drawable-mdpi/minitab_lt_selected.9.png
new file mode 100644
index 0000000..fefa27e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/minitab_lt_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/minitab_lt_unselected.9.png b/core/res/res/drawable-mdpi/minitab_lt_unselected.9.png
new file mode 100644
index 0000000..0051cd5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/minitab_lt_unselected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/minitab_lt_unselected_press.9.png b/core/res/res/drawable-mdpi/minitab_lt_unselected_press.9.png
new file mode 100644
index 0000000..69444dd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/minitab_lt_unselected_press.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/nav_divider.png b/core/res/res/drawable-mdpi/nav_divider.png
new file mode 100644
index 0000000..c9413d7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/nav_divider.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/overscroll_edge.png b/core/res/res/drawable-mdpi/overscroll_edge.png
index a63a59d..22f4ef8 100644
--- a/core/res/res/drawable-mdpi/overscroll_edge.png
+++ b/core/res/res/drawable-mdpi/overscroll_edge.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/overscroll_glow.png b/core/res/res/drawable-mdpi/overscroll_glow.png
index ed5cd78..761fb74 100644
--- a/core/res/res/drawable-mdpi/overscroll_glow.png
+++ b/core/res/res/drawable-mdpi/overscroll_glow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/panel_background.9.png b/core/res/res/drawable-mdpi/panel_background.9.png
index 822b6c6..2305be4 100644
--- a/core/res/res/drawable-mdpi/panel_background.9.png
+++ b/core/res/res/drawable-mdpi/panel_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/picture_emergency.png b/core/res/res/drawable-mdpi/picture_emergency.png
index a224b80..3690b07 100644
--- a/core/res/res/drawable-mdpi/picture_emergency.png
+++ b/core/res/res/drawable-mdpi/picture_emergency.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_bottom_bright.9.png b/core/res/res/drawable-mdpi/popup_bottom_bright.9.png
index e7b713d..e8e203b 100644
--- a/core/res/res/drawable-mdpi/popup_bottom_bright.9.png
+++ b/core/res/res/drawable-mdpi/popup_bottom_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_bottom_dark.9.png b/core/res/res/drawable-mdpi/popup_bottom_dark.9.png
index 88ce336..76a2a7f 100644
--- a/core/res/res/drawable-mdpi/popup_bottom_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_bottom_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_bottom_medium.9.png b/core/res/res/drawable-mdpi/popup_bottom_medium.9.png
old mode 100644
new mode 100755
index e5aaad0..dee6d6b
--- a/core/res/res/drawable-mdpi/popup_bottom_medium.9.png
+++ b/core/res/res/drawable-mdpi/popup_bottom_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_center_bright.9.png b/core/res/res/drawable-mdpi/popup_center_bright.9.png
index a259356..c817338db 100644
--- a/core/res/res/drawable-mdpi/popup_center_bright.9.png
+++ b/core/res/res/drawable-mdpi/popup_center_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_center_dark.9.png b/core/res/res/drawable-mdpi/popup_center_dark.9.png
index 9378dbf..79ffdaa 100644
--- a/core/res/res/drawable-mdpi/popup_center_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_center_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_center_medium.9.png b/core/res/res/drawable-mdpi/popup_center_medium.9.png
old mode 100644
new mode 100755
index 885403c..ba2e9bf
--- a/core/res/res/drawable-mdpi/popup_center_medium.9.png
+++ b/core/res/res/drawable-mdpi/popup_center_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_full_bright.9.png b/core/res/res/drawable-mdpi/popup_full_bright.9.png
index d7fb3db..d33ff2b 100644
--- a/core/res/res/drawable-mdpi/popup_full_bright.9.png
+++ b/core/res/res/drawable-mdpi/popup_full_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_full_dark.9.png b/core/res/res/drawable-mdpi/popup_full_dark.9.png
index 7b9f291..2305be4 100644
--- a/core/res/res/drawable-mdpi/popup_full_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_full_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_top_bright.9.png b/core/res/res/drawable-mdpi/popup_top_bright.9.png
index 72d82f0..727a948 100644
--- a/core/res/res/drawable-mdpi/popup_top_bright.9.png
+++ b/core/res/res/drawable-mdpi/popup_top_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_top_dark.9.png b/core/res/res/drawable-mdpi/popup_top_dark.9.png
index 616d80f..af511f2 100644
--- a/core/res/res/drawable-mdpi/popup_top_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_top_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickactions_arrowdown_left_holo_dark.9.png b/core/res/res/drawable-mdpi/quickactions_arrowdown_left_holo_dark.9.png
new file mode 100644
index 0000000..ece6551
--- /dev/null
+++ b/core/res/res/drawable-mdpi/quickactions_arrowdown_left_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickactions_arrowdown_left_holo_light.9.png b/core/res/res/drawable-mdpi/quickactions_arrowdown_left_holo_light.9.png
new file mode 100644
index 0000000..819656f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/quickactions_arrowdown_left_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickactions_arrowdown_right_holo_dark.9.png b/core/res/res/drawable-mdpi/quickactions_arrowdown_right_holo_dark.9.png
new file mode 100644
index 0000000..8e95970
--- /dev/null
+++ b/core/res/res/drawable-mdpi/quickactions_arrowdown_right_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickactions_arrowdown_right_holo_light.9.png b/core/res/res/drawable-mdpi/quickactions_arrowdown_right_holo_light.9.png
new file mode 100644
index 0000000..d5bef51
--- /dev/null
+++ b/core/res/res/drawable-mdpi/quickactions_arrowdown_right_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickactions_arrowup_left_holo_dark.9.png b/core/res/res/drawable-mdpi/quickactions_arrowup_left_holo_dark.9.png
new file mode 100644
index 0000000..543e341
--- /dev/null
+++ b/core/res/res/drawable-mdpi/quickactions_arrowup_left_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickactions_arrowup_left_holo_light.9.png b/core/res/res/drawable-mdpi/quickactions_arrowup_left_holo_light.9.png
new file mode 100644
index 0000000..e40e91d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/quickactions_arrowup_left_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickactions_arrowup_left_right_holo_dark.9.png b/core/res/res/drawable-mdpi/quickactions_arrowup_left_right_holo_dark.9.png
new file mode 100644
index 0000000..a4617e7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/quickactions_arrowup_left_right_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickactions_arrowup_right_holo_light.9.png b/core/res/res/drawable-mdpi/quickactions_arrowup_right_holo_light.9.png
new file mode 100644
index 0000000..1e8e7a06
--- /dev/null
+++ b/core/res/res/drawable-mdpi/quickactions_arrowup_right_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickcontact_badge_pressed.9.png b/core/res/res/drawable-mdpi/quickcontact_badge_pressed.9.png
index f970ad6..c8ca33a 100644
--- a/core/res/res/drawable-mdpi/quickcontact_badge_pressed.9.png
+++ b/core/res/res/drawable-mdpi/quickcontact_badge_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickcontact_badge_small_pressed.9.png b/core/res/res/drawable-mdpi/quickcontact_badge_small_pressed.9.png
index 437d25b..b23e921 100644
--- a/core/res/res/drawable-mdpi/quickcontact_badge_small_pressed.9.png
+++ b/core/res/res/drawable-mdpi/quickcontact_badge_small_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickcontact_badge_small_unpressed.9.png b/core/res/res/drawable-mdpi/quickcontact_badge_small_unpressed.9.png
index a58fe55..38f14f7 100644
--- a/core/res/res/drawable-mdpi/quickcontact_badge_small_unpressed.9.png
+++ b/core/res/res/drawable-mdpi/quickcontact_badge_small_unpressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickcontact_badge_unpressed.9.png b/core/res/res/drawable-mdpi/quickcontact_badge_unpressed.9.png
index 68a84610..d8dff34 100644
--- a/core/res/res/drawable-mdpi/quickcontact_badge_unpressed.9.png
+++ b/core/res/res/drawable-mdpi/quickcontact_badge_unpressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/quickcontact_nobadge.9.png b/core/res/res/drawable-mdpi/quickcontact_nobadge.9.png
new file mode 100644
index 0000000..ebe747b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/quickcontact_nobadge.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/rate_star_med_half.png b/core/res/res/drawable-mdpi/rate_star_med_half.png
index 502e611..65a8671 100644
--- a/core/res/res/drawable-mdpi/rate_star_med_half.png
+++ b/core/res/res/drawable-mdpi/rate_star_med_half.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/rate_star_med_off.png b/core/res/res/drawable-mdpi/rate_star_med_off.png
index c99d1db..fba0ade 100644
--- a/core/res/res/drawable-mdpi/rate_star_med_off.png
+++ b/core/res/res/drawable-mdpi/rate_star_med_off.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/rate_star_med_on.png b/core/res/res/drawable-mdpi/rate_star_med_on.png
index 57296b1..a1941b6 100644
--- a/core/res/res/drawable-mdpi/rate_star_med_on.png
+++ b/core/res/res/drawable-mdpi/rate_star_med_on.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrollbar_handle_accelerated_anim2.9.png b/core/res/res/drawable-mdpi/scrollbar_handle_accelerated_anim2.9.png
index da4c275..85caddd 100755
--- a/core/res/res/drawable-mdpi/scrollbar_handle_accelerated_anim2.9.png
+++ b/core/res/res/drawable-mdpi/scrollbar_handle_accelerated_anim2.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_holo.png b/core/res/res/drawable-mdpi/scrubber_control_holo.png
new file mode 100644
index 0000000..135b2aa
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_control_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png b/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png
new file mode 100644
index 0000000..7b48cf9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_track_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png b/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png
new file mode 100644
index 0000000..7c84ac9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/scrubber_track_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_active_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_active_holo_dark.9.png
new file mode 100644
index 0000000..c235501
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_active_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_active_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_active_holo_light.9.png
new file mode 100644
index 0000000..06e2bde
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_active_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_black_16.png b/core/res/res/drawable-mdpi/spinner_black_16.png
index 4b7fdfe..5ee33ce 100644
--- a/core/res/res/drawable-mdpi/spinner_black_16.png
+++ b/core/res/res/drawable-mdpi/spinner_black_16.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_black_20.png b/core/res/res/drawable-mdpi/spinner_black_20.png
index 86d7a20..e55b60d 100755
--- a/core/res/res/drawable-mdpi/spinner_black_20.png
+++ b/core/res/res/drawable-mdpi/spinner_black_20.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_black_48.png b/core/res/res/drawable-mdpi/spinner_black_48.png
index f1571f9..3a68192 100644
--- a/core/res/res/drawable-mdpi/spinner_black_48.png
+++ b/core/res/res/drawable-mdpi/spinner_black_48.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_black_76.png b/core/res/res/drawable-mdpi/spinner_black_76.png
index e9f6e8f..ec57460 100644
--- a/core/res/res/drawable-mdpi/spinner_black_76.png
+++ b/core/res/res/drawable-mdpi/spinner_black_76.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_active_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_cab_active_holo_dark.9.png
new file mode 100644
index 0000000..bb57d8d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_cab_active_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_active_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_cab_active_holo_light.9.png
new file mode 100644
index 0000000..4e3edff
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_cab_active_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_default_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_cab_default_holo_dark.9.png
new file mode 100644
index 0000000..62d2f6e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_cab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_default_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_cab_default_holo_light.9.png
new file mode 100644
index 0000000..df80aa6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_cab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_cab_disabled_holo_dark.9.png
new file mode 100644
index 0000000..fc9c109
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_cab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_cab_disabled_holo_light.9.png
new file mode 100644
index 0000000..7fc7cc1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_cab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_cab_focused_holo_dark.9.png
new file mode 100644
index 0000000..0f49fe9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_cab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_focused_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_cab_focused_holo_light.9.png
new file mode 100644
index 0000000..032d5d3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_cab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_cab_pressed_holo_dark.9.png
new file mode 100644
index 0000000..9b8a54b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_cab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_cab_pressed_holo_light.9.png
new file mode 100644
index 0000000..6d4fa54
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_cab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_default_holo_dark.9.png
new file mode 100644
index 0000000..3dba2352
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_default_holo_light.9.png
new file mode 100644
index 0000000..4614cfa
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_disabled_holo_dark.9.png
new file mode 100644
index 0000000..95d7c86
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_disabled_holo_light.9.png
new file mode 100644
index 0000000..2dba270
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_focused_holo_dark.9.png
new file mode 100644
index 0000000..2e2fadd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_focused_holo_light.9.png
new file mode 100644
index 0000000..5bbbf63
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_pressed_holo_dark.9.png
new file mode 100644
index 0000000..25aaed2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_pressed_holo_light.9.png
new file mode 100644
index 0000000..29cbc46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_white_16.png b/core/res/res/drawable-mdpi/spinner_white_16.png
index 650e315..dd2e1fd 100644
--- a/core/res/res/drawable-mdpi/spinner_white_16.png
+++ b/core/res/res/drawable-mdpi/spinner_white_16.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_white_48.png b/core/res/res/drawable-mdpi/spinner_white_48.png
index 11eacf8..d25a33e 100644
--- a/core/res/res/drawable-mdpi/spinner_white_48.png
+++ b/core/res/res/drawable-mdpi/spinner_white_48.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_white_76.png b/core/res/res/drawable-mdpi/spinner_white_76.png
index 6c31bc3..f53e8ff 100644
--- a/core/res/res/drawable-mdpi/spinner_white_76.png
+++ b/core/res/res/drawable-mdpi/spinner_white_76.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_call_mute.png b/core/res/res/drawable-mdpi/stat_notify_call_mute.png
index 4a3b057..6da8313 100644
--- a/core/res/res/drawable-mdpi/stat_notify_call_mute.png
+++ b/core/res/res/drawable-mdpi/stat_notify_call_mute.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_car_mode.png b/core/res/res/drawable-mdpi/stat_notify_car_mode.png
index 3b644d3..0272e6b 100644
--- a/core/res/res/drawable-mdpi/stat_notify_car_mode.png
+++ b/core/res/res/drawable-mdpi/stat_notify_car_mode.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_chat.png b/core/res/res/drawable-mdpi/stat_notify_chat.png
index 306d9c5..068b7ec 100644
--- a/core/res/res/drawable-mdpi/stat_notify_chat.png
+++ b/core/res/res/drawable-mdpi/stat_notify_chat.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_disk_full.png b/core/res/res/drawable-mdpi/stat_notify_disk_full.png
index 3eebeb8..9120f00 100755
--- a/core/res/res/drawable-mdpi/stat_notify_disk_full.png
+++ b/core/res/res/drawable-mdpi/stat_notify_disk_full.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_email_generic.png b/core/res/res/drawable-mdpi/stat_notify_email_generic.png
index 1620ad5..5b866bf 100644
--- a/core/res/res/drawable-mdpi/stat_notify_email_generic.png
+++ b/core/res/res/drawable-mdpi/stat_notify_email_generic.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_error.png b/core/res/res/drawable-mdpi/stat_notify_error.png
index 1275738..6ced2b7 100644
--- a/core/res/res/drawable-mdpi/stat_notify_error.png
+++ b/core/res/res/drawable-mdpi/stat_notify_error.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_gmail.png b/core/res/res/drawable-mdpi/stat_notify_gmail.png
index 4860c34..907306d 100644
--- a/core/res/res/drawable-mdpi/stat_notify_gmail.png
+++ b/core/res/res/drawable-mdpi/stat_notify_gmail.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_missed_call.png b/core/res/res/drawable-mdpi/stat_notify_missed_call.png
index 7bd5fcd..fe746b3 100644
--- a/core/res/res/drawable-mdpi/stat_notify_missed_call.png
+++ b/core/res/res/drawable-mdpi/stat_notify_missed_call.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_more.png b/core/res/res/drawable-mdpi/stat_notify_more.png
index a85a16e..e129ba9 100644
--- a/core/res/res/drawable-mdpi/stat_notify_more.png
+++ b/core/res/res/drawable-mdpi/stat_notify_more.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sdcard.png b/core/res/res/drawable-mdpi/stat_notify_sdcard.png
index fc0784d..23093ac 100644
--- a/core/res/res/drawable-mdpi/stat_notify_sdcard.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sdcard.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.png b/core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.png
index 93fad38..9abb1c9 100644
--- a/core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sdcard_prepare.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png b/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png
index cda4546..9880694 100644
--- a/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sim_toolkit.png b/core/res/res/drawable-mdpi/stat_notify_sim_toolkit.png
index 8b33a0f..c1ce8f2 100755
--- a/core/res/res/drawable-mdpi/stat_notify_sim_toolkit.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sim_toolkit.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sync.png b/core/res/res/drawable-mdpi/stat_notify_sync.png
index 03ce57a..0edf692 100644
--- a/core/res/res/drawable-mdpi/stat_notify_sync.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sync.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sync_anim0.png b/core/res/res/drawable-mdpi/stat_notify_sync_anim0.png
index 5481461..0edf692 100644
--- a/core/res/res/drawable-mdpi/stat_notify_sync_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sync_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_sync_error.png b/core/res/res/drawable-mdpi/stat_notify_sync_error.png
index f849b50..3078b8c 100644
--- a/core/res/res/drawable-mdpi/stat_notify_sync_error.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sync_error.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_voicemail.png b/core/res/res/drawable-mdpi/stat_notify_voicemail.png
index 636d1cc..658fa05 100644
--- a/core/res/res/drawable-mdpi/stat_notify_voicemail.png
+++ b/core/res/res/drawable-mdpi/stat_notify_voicemail.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png b/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
index 517c515..e9c74b4 100644
--- a/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
+++ b/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_adb.png b/core/res/res/drawable-mdpi/stat_sys_adb.png
old mode 100755
new mode 100644
index f0fad76..12abeda
--- a/core/res/res/drawable-mdpi/stat_sys_adb.png
+++ b/core/res/res/drawable-mdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_0.png b/core/res/res/drawable-mdpi/stat_sys_battery_0.png
index a7068b2..4a5e99e 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_10.png b/core/res/res/drawable-mdpi/stat_sys_battery_10.png
old mode 100644
new mode 100755
index 8fd88f3..b789f23
--- a/core/res/res/drawable-mdpi/stat_sys_battery_10.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_10.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_100.png b/core/res/res/drawable-mdpi/stat_sys_battery_100.png
index 5267517..d280aeb 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_100.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_20.png b/core/res/res/drawable-mdpi/stat_sys_battery_20.png
index 8bc41f0..009a9fd 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_20.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_20.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_40.png b/core/res/res/drawable-mdpi/stat_sys_battery_40.png
index d827ce9..15b57f4 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_40.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_40.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_60.png b/core/res/res/drawable-mdpi/stat_sys_battery_60.png
index 53399dc..21078fd 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_60.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_60.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_80.png b/core/res/res/drawable-mdpi/stat_sys_battery_80.png
index f38af5d..9268f7b 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_80.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_80.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png
index 9f3da11..ff3cabd 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim1.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim1.png
index 8f8c81e..b563701 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim2.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim2.png
index 12fd03f..904989e 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim3.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim3.png
index 641896c..ba011c9 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim4.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim4.png
index 6bd19b5..4f1c485 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim5.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim5.png
index f9353c0..4d3396d 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim5.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim5.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png b/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png
index 506cb45..ed72ebf 100644
--- a/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_bluetooth.png b/core/res/res/drawable-mdpi/stat_sys_data_bluetooth.png
index 85250d6..7a8b78f 100644
--- a/core/res/res/drawable-mdpi/stat_sys_data_bluetooth.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_usb.png b/core/res/res/drawable-mdpi/stat_sys_data_usb.png
index cbf1da7..2d0da4c 100644
--- a/core/res/res/drawable-mdpi/stat_sys_data_usb.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_usb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim0.png b/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
index 01b65e0..69b95cd 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim1.png b/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
index 2b0633f..1e18eb5 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim2.png b/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
index 2295a6d..d7f2312 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim3.png b/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
index 99db712..83f8d0f 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim4.png b/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
index 738dc87..9c1bd47 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_download_anim5.png b/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
index 00ff542..3a81164 100755
--- a/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_gps_on.png b/core/res/res/drawable-mdpi/stat_sys_gps_on.png
old mode 100644
new mode 100755
index df737f29..a2c677d
--- a/core/res/res/drawable-mdpi/stat_sys_gps_on.png
+++ b/core/res/res/drawable-mdpi/stat_sys_gps_on.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_phone_call.png b/core/res/res/drawable-mdpi/stat_sys_phone_call.png
index 0cfbd1b..c44d062 100644
--- a/core/res/res/drawable-mdpi/stat_sys_phone_call.png
+++ b/core/res/res/drawable-mdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_phone_call_forward.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_forward.png
index 6dcf1ba..ed4b6ec 100755
--- a/core/res/res/drawable-mdpi/stat_sys_phone_call_forward.png
+++ b/core/res/res/drawable-mdpi/stat_sys_phone_call_forward.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_phone_call_on_hold.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_on_hold.png
index c4bfc2f..9216447 100644
--- a/core/res/res/drawable-mdpi/stat_sys_phone_call_on_hold.png
+++ b/core/res/res/drawable-mdpi/stat_sys_phone_call_on_hold.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_0_cdma.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_0_cdma.png
index f39f5ba..f615681 100644
--- a/core/res/res/drawable-mdpi/stat_sys_r_signal_0_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_0_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_1_cdma.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_1_cdma.png
index 86bb2de..c3962a2 100644
--- a/core/res/res/drawable-mdpi/stat_sys_r_signal_1_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_1_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_2_cdma.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_2_cdma.png
index b6eda07..68bfe94 100644
--- a/core/res/res/drawable-mdpi/stat_sys_r_signal_2_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_2_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_3_cdma.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_3_cdma.png
index b7ca7f9..4ccd416 100644
--- a/core/res/res/drawable-mdpi/stat_sys_r_signal_3_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_3_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_r_signal_4_cdma.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_4_cdma.png
index 61a9575..0b25570 100644
--- a/core/res/res/drawable-mdpi/stat_sys_r_signal_4_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_4_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_ra_signal_0_cdma.png b/core/res/res/drawable-mdpi/stat_sys_ra_signal_0_cdma.png
index feb4f2c..9a38733 100644
--- a/core/res/res/drawable-mdpi/stat_sys_ra_signal_0_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_ra_signal_0_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_ra_signal_1_cdma.png b/core/res/res/drawable-mdpi/stat_sys_ra_signal_1_cdma.png
index a42ff0c..c70e283 100644
--- a/core/res/res/drawable-mdpi/stat_sys_ra_signal_1_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_ra_signal_1_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_ra_signal_2_cdma.png b/core/res/res/drawable-mdpi/stat_sys_ra_signal_2_cdma.png
index e991c76..a09564c 100644
--- a/core/res/res/drawable-mdpi/stat_sys_ra_signal_2_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_ra_signal_2_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_ra_signal_3_cdma.png b/core/res/res/drawable-mdpi/stat_sys_ra_signal_3_cdma.png
index 4b743fb..2637dec 100644
--- a/core/res/res/drawable-mdpi/stat_sys_ra_signal_3_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_ra_signal_3_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_ra_signal_4_cdma.png b/core/res/res/drawable-mdpi/stat_sys_ra_signal_4_cdma.png
index 65172b7..aba13e7 100644
--- a/core/res/res/drawable-mdpi/stat_sys_ra_signal_4_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_ra_signal_4_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_secure.png b/core/res/res/drawable-mdpi/stat_sys_secure.png
index a632fb2..5f9ae69 100644
--- a/core/res/res/drawable-mdpi/stat_sys_secure.png
+++ b/core/res/res/drawable-mdpi/stat_sys_secure.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_0_cdma.png b/core/res/res/drawable-mdpi/stat_sys_signal_0_cdma.png
index 03c51ce..11f9dfe 100644
--- a/core/res/res/drawable-mdpi/stat_sys_signal_0_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_0_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_1_cdma.png b/core/res/res/drawable-mdpi/stat_sys_signal_1_cdma.png
index dced6df..4ef3dba 100644
--- a/core/res/res/drawable-mdpi/stat_sys_signal_1_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_1_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_2_cdma.png b/core/res/res/drawable-mdpi/stat_sys_signal_2_cdma.png
index 9eac4c6..530ed45 100644
--- a/core/res/res/drawable-mdpi/stat_sys_signal_2_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_2_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_3_cdma.png b/core/res/res/drawable-mdpi/stat_sys_signal_3_cdma.png
index 74d983c..60a868a 100644
--- a/core/res/res/drawable-mdpi/stat_sys_signal_3_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_3_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_4_cdma.png b/core/res/res/drawable-mdpi/stat_sys_signal_4_cdma.png
index 8cc40b5..5f2950e 100644
--- a/core/res/res/drawable-mdpi/stat_sys_signal_4_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_4_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_evdo_0.png b/core/res/res/drawable-mdpi/stat_sys_signal_evdo_0.png
index 177e0e9..defc129 100644
--- a/core/res/res/drawable-mdpi/stat_sys_signal_evdo_0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_evdo_0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_evdo_1.png b/core/res/res/drawable-mdpi/stat_sys_signal_evdo_1.png
index 5f66319..e3a81ed 100644
--- a/core/res/res/drawable-mdpi/stat_sys_signal_evdo_1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_evdo_1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_evdo_2.png b/core/res/res/drawable-mdpi/stat_sys_signal_evdo_2.png
index c365912..68c99a9 100644
--- a/core/res/res/drawable-mdpi/stat_sys_signal_evdo_2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_evdo_2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_evdo_3.png b/core/res/res/drawable-mdpi/stat_sys_signal_evdo_3.png
index 58d631b..2f6c435 100644
--- a/core/res/res/drawable-mdpi/stat_sys_signal_evdo_3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_evdo_3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_signal_evdo_4.png b/core/res/res/drawable-mdpi/stat_sys_signal_evdo_4.png
index e63af68..4c9d4c6 100644
--- a/core/res/res/drawable-mdpi/stat_sys_signal_evdo_4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_evdo_4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_speakerphone.png b/core/res/res/drawable-mdpi/stat_sys_speakerphone.png
index d0411cf..642dfd4 100644
--- a/core/res/res/drawable-mdpi/stat_sys_speakerphone.png
+++ b/core/res/res/drawable-mdpi/stat_sys_speakerphone.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_bluetooth.png b/core/res/res/drawable-mdpi/stat_sys_tether_bluetooth.png
index 2db135d..efb64ad 100644
--- a/core/res/res/drawable-mdpi/stat_sys_tether_bluetooth.png
+++ b/core/res/res/drawable-mdpi/stat_sys_tether_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_general.png b/core/res/res/drawable-mdpi/stat_sys_tether_general.png
index 11e638f..3688803 100644
--- a/core/res/res/drawable-mdpi/stat_sys_tether_general.png
+++ b/core/res/res/drawable-mdpi/stat_sys_tether_general.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_usb.png b/core/res/res/drawable-mdpi/stat_sys_tether_usb.png
index 8971f93..73f1a81 100644
--- a/core/res/res/drawable-mdpi/stat_sys_tether_usb.png
+++ b/core/res/res/drawable-mdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_wifi.png b/core/res/res/drawable-mdpi/stat_sys_tether_wifi.png
index 32b9431..d448491 100644
--- a/core/res/res/drawable-mdpi/stat_sys_tether_wifi.png
+++ b/core/res/res/drawable-mdpi/stat_sys_tether_wifi.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_throttled.png b/core/res/res/drawable-mdpi/stat_sys_throttled.png
index bc9b223..97ac427 100644
--- a/core/res/res/drawable-mdpi/stat_sys_throttled.png
+++ b/core/res/res/drawable-mdpi/stat_sys_throttled.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png
index f6fcc7a..b7a5978 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
index 5d05381..a203e15 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
index a8699bc..4af7630 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png
index f5ad44a..1dd76b1 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png
index b5884f6..36c18bf 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png
index ae6b8a9..748331f 100755
--- a/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_vp_phone_call.png b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call.png
index f42c7e4..aa03b4f 100644
--- a/core/res/res/drawable-mdpi/stat_sys_vp_phone_call.png
+++ b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_on_hold.png b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_on_hold.png
index 795f079..5f45440 100644
--- a/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_on_hold.png
+++ b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_on_hold.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_warning.png b/core/res/res/drawable-mdpi/stat_sys_warning.png
index c0823da..be00f47 100644
--- a/core/res/res/drawable-mdpi/stat_sys_warning.png
+++ b/core/res/res/drawable-mdpi/stat_sys_warning.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/status_bar_header_background.9.png b/core/res/res/drawable-mdpi/status_bar_header_background.9.png
index 3787ad3..fa9a90c 100644
--- a/core/res/res/drawable-mdpi/status_bar_header_background.9.png
+++ b/core/res/res/drawable-mdpi/status_bar_header_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/status_bar_item_app_background_normal.9.png b/core/res/res/drawable-mdpi/status_bar_item_app_background_normal.9.png
index 873c556..c079615 100644
--- a/core/res/res/drawable-mdpi/status_bar_item_app_background_normal.9.png
+++ b/core/res/res/drawable-mdpi/status_bar_item_app_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/status_bar_item_background_focus.9.png b/core/res/res/drawable-mdpi/status_bar_item_background_focus.9.png
index cec060e..c3e24158 100644
--- a/core/res/res/drawable-mdpi/status_bar_item_background_focus.9.png
+++ b/core/res/res/drawable-mdpi/status_bar_item_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/status_bar_item_background_normal.9.png b/core/res/res/drawable-mdpi/status_bar_item_background_normal.9.png
index 38a96e3..f0e4d06 100644
--- a/core/res/res/drawable-mdpi/status_bar_item_background_normal.9.png
+++ b/core/res/res/drawable-mdpi/status_bar_item_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/status_bar_item_background_pressed.9.png b/core/res/res/drawable-mdpi/status_bar_item_background_pressed.9.png
index ae1263d..02b4e9a 100644
--- a/core/res/res/drawable-mdpi/status_bar_item_background_pressed.9.png
+++ b/core/res/res/drawable-mdpi/status_bar_item_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/statusbar_background.9.png b/core/res/res/drawable-mdpi/statusbar_background.9.png
new file mode 100644
index 0000000..eb7c1a4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/statusbar_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_bottom_holo.9.png b/core/res/res/drawable-mdpi/tab_bottom_holo.9.png
new file mode 100644
index 0000000..1e40b9c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_bottom_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_focus.9.png b/core/res/res/drawable-mdpi/tab_focus.9.png
index 4ca207b..d9bcc57a 100755
--- a/core/res/res/drawable-mdpi/tab_focus.9.png
+++ b/core/res/res/drawable-mdpi/tab_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_focus_bar_left.9.png b/core/res/res/drawable-mdpi/tab_focus_bar_left.9.png
index 7150301..2536d94 100755
--- a/core/res/res/drawable-mdpi/tab_focus_bar_left.9.png
+++ b/core/res/res/drawable-mdpi/tab_focus_bar_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_focus_bar_right.9.png b/core/res/res/drawable-mdpi/tab_focus_bar_right.9.png
index 7150301..2536d94 100755
--- a/core/res/res/drawable-mdpi/tab_focus_bar_right.9.png
+++ b/core/res/res/drawable-mdpi/tab_focus_bar_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_focused_holo.9.png b/core/res/res/drawable-mdpi/tab_focused_holo.9.png
new file mode 100644
index 0000000..187d8c5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_press.9.png b/core/res/res/drawable-mdpi/tab_press.9.png
index 18ff645..3332660 100755
--- a/core/res/res/drawable-mdpi/tab_press.9.png
+++ b/core/res/res/drawable-mdpi/tab_press.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_press_bar_left.9.png b/core/res/res/drawable-mdpi/tab_press_bar_left.9.png
index 735e46b..d2c75e3 100755
--- a/core/res/res/drawable-mdpi/tab_press_bar_left.9.png
+++ b/core/res/res/drawable-mdpi/tab_press_bar_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_press_bar_right.9.png b/core/res/res/drawable-mdpi/tab_press_bar_right.9.png
index 735e46b..d2c75e3 100755
--- a/core/res/res/drawable-mdpi/tab_press_bar_right.9.png
+++ b/core/res/res/drawable-mdpi/tab_press_bar_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_pressed_holo.9.png b/core/res/res/drawable-mdpi/tab_pressed_holo.9.png
new file mode 100644
index 0000000..a76fbae
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_selected.9.png b/core/res/res/drawable-mdpi/tab_selected.9.png
index 848e891..54190ea 100644
--- a/core/res/res/drawable-mdpi/tab_selected.9.png
+++ b/core/res/res/drawable-mdpi/tab_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_selected_bar_left.9.png b/core/res/res/drawable-mdpi/tab_selected_bar_left.9.png
index e807cf7..d20f3a2 100755
--- a/core/res/res/drawable-mdpi/tab_selected_bar_left.9.png
+++ b/core/res/res/drawable-mdpi/tab_selected_bar_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_selected_bar_right.9.png b/core/res/res/drawable-mdpi/tab_selected_bar_right.9.png
index e807cf7..d20f3a2 100755
--- a/core/res/res/drawable-mdpi/tab_selected_bar_right.9.png
+++ b/core/res/res/drawable-mdpi/tab_selected_bar_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_selected_focused_holo.9.png b/core/res/res/drawable-mdpi/tab_selected_focused_holo.9.png
new file mode 100644
index 0000000..9a33cd2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_selected_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_selected_holo.9.png b/core/res/res/drawable-mdpi/tab_selected_holo.9.png
new file mode 100644
index 0000000..e029e57
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_selected_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_selected_pressed_focused_holo.9.png b/core/res/res/drawable-mdpi/tab_selected_pressed_focused_holo.9.png
new file mode 100644
index 0000000..285116e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_selected_pressed_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_selected_pressed_holo.9.png b/core/res/res/drawable-mdpi/tab_selected_pressed_holo.9.png
new file mode 100644
index 0000000..dadefa7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_selected_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_unselected.9.png b/core/res/res/drawable-mdpi/tab_unselected.9.png
index 6e1c6e70..1b8a69c9 100644
--- a/core/res/res/drawable-mdpi/tab_unselected.9.png
+++ b/core/res/res/drawable-mdpi/tab_unselected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_unselected_focused_holo.9.png b/core/res/res/drawable-mdpi/tab_unselected_focused_holo.9.png
new file mode 100644
index 0000000..032a992
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_unselected_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_unselected_holo.9.png b/core/res/res/drawable-mdpi/tab_unselected_holo.9.png
new file mode 100644
index 0000000..848f3f1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_unselected_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_unselected_pressed_focused_holo.9.png b/core/res/res/drawable-mdpi/tab_unselected_pressed_focused_holo.9.png
new file mode 100644
index 0000000..3845135
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_unselected_pressed_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_unselected_pressed_holo.9.png b/core/res/res/drawable-mdpi/tab_unselected_pressed_holo.9.png
new file mode 100644
index 0000000..23fd8c9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_unselected_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_edit_paste_window.9.png b/core/res/res/drawable-mdpi/text_edit_paste_window.9.png
new file mode 100644
index 0000000..6b98c13
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_edit_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle.png b/core/res/res/drawable-mdpi/text_select_handle.png
new file mode 100644
index 0000000..93a5a15
--- /dev/null
+++ b/core/res/res/drawable-mdpi/text_select_handle.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_left.png b/core/res/res/drawable-mdpi/text_select_handle_left.png
index 81e6c20..4ee2f42 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_left.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_left.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_middle.png b/core/res/res/drawable-mdpi/text_select_handle_middle.png
index 9421759d..3d16052 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_middle.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_middle.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_right.png b/core/res/res/drawable-mdpi/text_select_handle_right.png
index e112560..3d38928 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_right.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_right.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default.9.png b/core/res/res/drawable-mdpi/textfield_default.9.png
index 1a59bb2..cc78e6c 100644
--- a/core/res/res/drawable-mdpi/textfield_default.9.png
+++ b/core/res/res/drawable-mdpi/textfield_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
new file mode 100644
index 0000000..3a5f36d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
new file mode 100644
index 0000000..b8cc76f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled.9.png b/core/res/res/drawable-mdpi/textfield_disabled.9.png
index 800205a..9c77149 100644
--- a/core/res/res/drawable-mdpi/textfield_disabled.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
new file mode 100644
index 0000000..a1f0c71
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
new file mode 100644
index 0000000..71e3103
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_selected.9.png b/core/res/res/drawable-mdpi/textfield_disabled_selected.9.png
index 59e1536..6d47708 100644
--- a/core/res/res/drawable-mdpi/textfield_disabled_selected.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png
new file mode 100644
index 0000000..ac6d406
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png
new file mode 100644
index 0000000..bb6e953
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_pressed.9.png b/core/res/res/drawable-mdpi/textfield_pressed.9.png
index 6d03e1e..c909ad2 100644
--- a/core/res/res/drawable-mdpi/textfield_pressed.9.png
+++ b/core/res/res/drawable-mdpi/textfield_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png
new file mode 100644
index 0000000..7667d95
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png
new file mode 100644
index 0000000..269affd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_search_default_holo_dark.9.png
new file mode 100644
index 0000000..3549948
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_search_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_search_default_holo_light.9.png
new file mode 100644
index 0000000..6db03cb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_search_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_search_selected_holo_dark.9.png
new file mode 100644
index 0000000..670439a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_search_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_selected_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_search_selected_holo_light.9.png
new file mode 100644
index 0000000..90e26e2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_search_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_selected.9.png b/core/res/res/drawable-mdpi/textfield_selected.9.png
index faadace..0c1b446 100644
--- a/core/res/res/drawable-mdpi/textfield_selected.9.png
+++ b/core/res/res/drawable-mdpi/textfield_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png
new file mode 100644
index 0000000..7667d95
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png
new file mode 100644
index 0000000..269affd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/title_bar_shadow.9.png b/core/res/res/drawable-mdpi/title_bar_shadow.9.png
index f2988a3..dbcefee 100644
--- a/core/res/res/drawable-mdpi/title_bar_shadow.9.png
+++ b/core/res/res/drawable-mdpi/title_bar_shadow.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/background_holo_dark.png b/core/res/res/drawable-nodpi/background_holo_dark.png
new file mode 100644
index 0000000..f2ff75a
--- /dev/null
+++ b/core/res/res/drawable-nodpi/background_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/background_holo_light.png b/core/res/res/drawable-nodpi/background_holo_light.png
new file mode 100644
index 0000000..04f52ae
--- /dev/null
+++ b/core/res/res/drawable-nodpi/background_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.jpg b/core/res/res/drawable-nodpi/platlogo.jpg
deleted file mode 100644
index 0e7780c..0000000
--- a/core/res/res/drawable-nodpi/platlogo.jpg
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.png b/core/res/res/drawable-nodpi/platlogo.png
new file mode 100644
index 0000000..e5af356
--- /dev/null
+++ b/core/res/res/drawable-nodpi/platlogo.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-hdpi/unlock_default.png b/core/res/res/drawable-xlarge-hdpi/unlock_default.png
new file mode 100644
index 0000000..67311b9
--- /dev/null
+++ b/core/res/res/drawable-xlarge-hdpi/unlock_default.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-hdpi/unlock_halo.png b/core/res/res/drawable-xlarge-hdpi/unlock_halo.png
new file mode 100644
index 0000000..d841dea
--- /dev/null
+++ b/core/res/res/drawable-xlarge-hdpi/unlock_halo.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-hdpi/unlock_ring.png b/core/res/res/drawable-xlarge-hdpi/unlock_ring.png
new file mode 100644
index 0000000..172efb8
--- /dev/null
+++ b/core/res/res/drawable-xlarge-hdpi/unlock_ring.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-hdpi/unlock_wave.png b/core/res/res/drawable-xlarge-hdpi/unlock_wave.png
new file mode 100644
index 0000000..1ad26c3
--- /dev/null
+++ b/core/res/res/drawable-xlarge-hdpi/unlock_wave.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/default_wallpaper.jpg b/core/res/res/drawable-xlarge-mdpi/default_wallpaper.jpg
new file mode 100644
index 0000000..5acad94
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png
new file mode 100644
index 0000000..336a820
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png
new file mode 100644
index 0000000..ebef531
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png
new file mode 100644
index 0000000..405e218
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png
new file mode 100644
index 0000000..f349b63
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_default.png b/core/res/res/drawable-xlarge-mdpi/unlock_default.png
new file mode 100644
index 0000000..0a441c0
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/unlock_default.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_halo.png b/core/res/res/drawable-xlarge-mdpi/unlock_halo.png
new file mode 100644
index 0000000..09b0526
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/unlock_halo.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_ring.png b/core/res/res/drawable-xlarge-mdpi/unlock_ring.png
new file mode 100644
index 0000000..1ac6d54
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/unlock_ring.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_wave.png b/core/res/res/drawable-xlarge-mdpi/unlock_wave.png
new file mode 100644
index 0000000..21bfa24
--- /dev/null
+++ b/core/res/res/drawable-xlarge-mdpi/unlock_wave.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-nodpi/default_wallpaper.jpg b/core/res/res/drawable-xlarge-nodpi/default_wallpaper.jpg
new file mode 100644
index 0000000..5acad94
--- /dev/null
+++ b/core/res/res/drawable-xlarge-nodpi/default_wallpaper.jpg
Binary files differ
diff --git a/core/res/res/drawable/action_bar_background.xml b/core/res/res/drawable/action_bar_background.xml
new file mode 100644
index 0000000..3929d7f
--- /dev/null
+++ b/core/res/res/drawable/action_bar_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient
+        android:startColor="#ffd1d2d4"
+        android:endColor="#ff85878a"
+        android:angle="270" />
+</shape>
diff --git a/core/res/res/drawable/action_bar_context_background.xml b/core/res/res/drawable/action_bar_context_background.xml
new file mode 100644
index 0000000..8789898
--- /dev/null
+++ b/core/res/res/drawable/action_bar_context_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient
+        android:startColor="#ff000000"
+        android:centerColor="#ffd1d2d4"
+        android:endColor="#ff85878a"
+        android:angle="270" />
+</shape>
diff --git a/core/res/res/drawable/action_bar_divider.xml b/core/res/res/drawable/action_bar_divider.xml
new file mode 100644
index 0000000..414309f
--- /dev/null
+++ b/core/res/res/drawable/action_bar_divider.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient
+        android:startColor="#ffe1e2e4"
+        android:endColor="#ff95979a"
+        android:angle="270" />
+</shape>
diff --git a/core/res/res/drawable/activated_background.xml b/core/res/res/drawable/activated_background.xml
new file mode 100644
index 0000000..1047e5b
--- /dev/null
+++ b/core/res/res/drawable/activated_background.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+    <item android:state_activated="true" android:drawable="@android:drawable/list_selector_background_selected" />
+    <item android:drawable="@color/transparent" />
+</selector>
diff --git a/core/res/res/drawable/activated_background_holo_dark.xml b/core/res/res/drawable/activated_background_holo_dark.xml
new file mode 100644
index 0000000..a29bcb9
--- /dev/null
+++ b/core/res/res/drawable/activated_background_holo_dark.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+    <item android:state_activated="true" android:drawable="@android:drawable/list_activated_holo" />
+    <item android:drawable="@color/transparent" />
+</selector>
diff --git a/core/res/res/drawable/activated_background_holo_light.xml b/core/res/res/drawable/activated_background_holo_light.xml
new file mode 100644
index 0000000..a29bcb9
--- /dev/null
+++ b/core/res/res/drawable/activated_background_holo_light.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+    <item android:state_activated="true" android:drawable="@android:drawable/list_activated_holo" />
+    <item android:drawable="@color/transparent" />
+</selector>
diff --git a/core/res/res/drawable/activated_background_light.xml b/core/res/res/drawable/activated_background_light.xml
new file mode 100644
index 0000000..7d737db
--- /dev/null
+++ b/core/res/res/drawable/activated_background_light.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+    <item android:state_activated="true" android:drawable="@drawable/list_selector_background_selected_light" />
+    <item android:drawable="@color/transparent" />
+</selector>
diff --git a/core/res/res/drawable/btn_check_holo_dark.xml b/core/res/res/drawable/btn_check_holo_dark.xml
new file mode 100644
index 0000000..a163c2e
--- /dev/null
+++ b/core/res/res/drawable/btn_check_holo_dark.xml
@@ -0,0 +1,65 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- Enabled states -->
+        
+    <item android:state_checked="true" android:state_window_focused="false"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_on_holo_dark" />
+    <item android:state_checked="false" android:state_window_focused="false"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_off_holo_dark" />
+
+    <item android:state_checked="true" android:state_pressed="true"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_on_pressed_holo_dark" />
+    <item android:state_checked="false" android:state_pressed="true"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_off_pressed_holo_dark" />
+
+    <item android:state_checked="true" android:state_focused="true"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_on_selected_holo_dark" />
+    <item android:state_checked="false" android:state_focused="true"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_off_selected_holo_dark" />
+
+    <item android:state_checked="false"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_off_holo_dark" />
+    <item android:state_checked="true"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_on_holo_dark" />
+
+
+    <!-- Disabled states -->
+
+    <item android:state_checked="true" android:state_window_focused="false"
+          android:drawable="@drawable/btn_check_on_disabled_holo_dark" />
+    <item android:state_checked="false" android:state_window_focused="false"
+          android:drawable="@drawable/btn_check_off_disabled_holo_dark" />
+
+    <item android:state_checked="true" android:state_focused="true"
+          android:drawable="@drawable/btn_check_on_disabled_focused_holo_dark" />
+    <item android:state_checked="false" android:state_focused="true"
+          android:drawable="@drawable/btn_check_off_disabled_focused_holo_dark" />
+
+    <item android:state_checked="false" android:drawable="@drawable/btn_check_off_disabled_holo_dark" />
+    <item android:state_checked="true" android:drawable="@drawable/btn_check_on_disabled_holo_dark" />
+
+</selector>
diff --git a/core/res/res/drawable/btn_check_holo_light.xml b/core/res/res/drawable/btn_check_holo_light.xml
new file mode 100644
index 0000000..5c49456
--- /dev/null
+++ b/core/res/res/drawable/btn_check_holo_light.xml
@@ -0,0 +1,65 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- Enabled states -->
+        
+    <item android:state_checked="true" android:state_window_focused="false"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_on_holo_light" />
+    <item android:state_checked="false" android:state_window_focused="false"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_off_holo_light" />
+
+    <item android:state_checked="true" android:state_pressed="true"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_on_pressed_holo_light" />
+    <item android:state_checked="false" android:state_pressed="true"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_off_pressed_holo_light" />
+
+    <item android:state_checked="true" android:state_focused="true"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_on_selected_holo_light" />
+    <item android:state_checked="false" android:state_focused="true"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_off_selected_holo_light" />
+
+    <item android:state_checked="false"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_off_holo_light" />
+    <item android:state_checked="true"
+          android:state_enabled="true"
+          android:drawable="@drawable/btn_check_on_holo_light" />
+
+
+    <!-- Disabled states -->
+
+    <item android:state_checked="true" android:state_window_focused="false"
+          android:drawable="@drawable/btn_check_on_disabled_holo_light" />
+    <item android:state_checked="false" android:state_window_focused="false"
+          android:drawable="@drawable/btn_check_off_disabled_holo_light" />
+
+    <item android:state_checked="true" android:state_focused="true"
+          android:drawable="@drawable/btn_check_on_disabled_focused_holo_light" />
+    <item android:state_checked="false" android:state_focused="true"
+          android:drawable="@drawable/btn_check_off_disabled_focused_holo_light" />
+
+    <item android:state_checked="false" android:drawable="@drawable/btn_check_off_disabled_holo_light" />
+    <item android:state_checked="true" android:drawable="@drawable/btn_check_on_disabled_holo_light" />
+
+</selector>
diff --git a/core/res/res/drawable/btn_default_holo_dark.xml b/core/res/res/drawable/btn_default_holo_dark.xml
new file mode 100644
index 0000000..c250070
--- /dev/null
+++ b/core/res/res/drawable/btn_default_holo_dark.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_default_normal_holo_dark" />
+    <item android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_default_disabled_holo_dark" />
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/btn_default_pressed_holo_dark" />
+    <item android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_default_focused_holo_dark" />
+    <item android:state_enabled="true"
+        android:drawable="@drawable/btn_default_normal_holo_dark" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_default_disabled_focused_holo_dark" />
+    <item
+         android:drawable="@drawable/btn_default_disabled_holo_dark" />
+</selector>
diff --git a/core/res/res/drawable/btn_default_holo_light.xml b/core/res/res/drawable/btn_default_holo_light.xml
new file mode 100644
index 0000000..61bb1bd
--- /dev/null
+++ b/core/res/res/drawable/btn_default_holo_light.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_default_normal_holo_light" />
+    <item android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_default_disabled_holo_light" />
+    <item android:state_pressed="true"
+        android:drawable="@drawable/btn_default_pressed_holo_light" />
+    <item android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_default_focused_holo_light" />
+    <item android:state_enabled="true"
+        android:drawable="@drawable/btn_default_normal_holo_light" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_default_disabled_focused_holo_light" />
+    <item
+         android:drawable="@drawable/btn_default_disabled_holo_light" />
+</selector>
diff --git a/core/res/res/drawable/btn_radio_holo_dark.xml b/core/res/res/drawable/btn_radio_holo_dark.xml
new file mode 100644
index 0000000..8984f6d
--- /dev/null
+++ b/core/res/res/drawable/btn_radio_holo_dark.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true" android:state_window_focused="false"
+          android:drawable="@drawable/btn_radio_on_holo_dark" />
+    <item android:state_checked="false" android:state_window_focused="false"
+          android:drawable="@drawable/btn_radio_off_holo_dark" />
+          
+    <item android:state_checked="true" android:state_pressed="true"
+          android:drawable="@drawable/btn_radio_on_pressed_holo_dark" />
+    <item android:state_checked="false" android:state_pressed="true"
+          android:drawable="@drawable/btn_radio_off_pressed_holo_dark" />
+
+    <item android:state_checked="true" android:state_focused="true"
+          android:drawable="@drawable/btn_radio_on_selected_holo_dark" />
+    <item android:state_checked="false" android:state_focused="true"
+          android:drawable="@drawable/btn_radio_off_selected_holo_dark" />
+
+    <item android:state_checked="false" android:drawable="@drawable/btn_radio_off_holo_dark" />
+    <item android:state_checked="true" android:drawable="@drawable/btn_radio_on_holo_dark" />
+</selector>
diff --git a/core/res/res/drawable/btn_radio_holo_light.xml b/core/res/res/drawable/btn_radio_holo_light.xml
new file mode 100644
index 0000000..001508c
--- /dev/null
+++ b/core/res/res/drawable/btn_radio_holo_light.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true" android:state_window_focused="false"
+          android:drawable="@drawable/btn_radio_on_holo_light" />
+    <item android:state_checked="false" android:state_window_focused="false"
+          android:drawable="@drawable/btn_radio_off_holo_light" />
+          
+    <item android:state_checked="true" android:state_pressed="true"
+          android:drawable="@drawable/btn_radio_on_pressed_holo_light" />
+    <item android:state_checked="false" android:state_pressed="true"
+          android:drawable="@drawable/btn_radio_off_pressed_holo_light" />
+
+    <item android:state_checked="true" android:state_focused="true"
+          android:drawable="@drawable/btn_radio_on_selected_holo_light" />
+    <item android:state_checked="false" android:state_focused="true"
+          android:drawable="@drawable/btn_radio_off_selected_holo_light" />
+
+    <item android:state_checked="false" android:drawable="@drawable/btn_radio_off_holo_light" />
+    <item android:state_checked="true" android:drawable="@drawable/btn_radio_on_holo_light" />
+</selector>
diff --git a/core/res/res/drawable/btn_toggle_holo_dark.xml b/core/res/res/drawable/btn_toggle_holo_dark.xml
new file mode 100644
index 0000000..658ca9e
--- /dev/null
+++ b/core/res/res/drawable/btn_toggle_holo_dark.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true"
+        android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_on_normal_holo_dark" />
+    <item android:state_checked="true"
+        android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_toggle_on_disabled_holo_dark" />
+    <item android:state_checked="true" android:state_pressed="true"
+        android:drawable="@drawable/btn_toggle_on_pressed_holo_dark" />
+    <item android:state_checked="true"
+        android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_on_focused_holo_dark" />
+    <item android:state_checked="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_on_normal_holo_dark" />
+    <item android:state_checked="true"  android:state_focused="true"
+        android:drawable="@drawable/btn_toggle_on_disabled_focused_holo_dark" />
+    <item android:state_checked="true"
+         android:drawable="@drawable/btn_toggle_on_disabled_holo_dark" />
+
+    <item android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_off_normal_holo_dark" />
+    <item android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_toggle_off_disabled_holo_dark" />
+    <item android:state_pressed="true"
+        android:drawable="@drawable/btn_toggle_off_pressed_holo_dark" />
+    <item android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_off_focused_holo_dark" />
+    <item android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_off_normal_holo_dark" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_toggle_off_disabled_focused_holo_dark" />
+    <item
+         android:drawable="@drawable/btn_toggle_off_disabled_holo_dark" />
+</selector>
diff --git a/core/res/res/drawable/btn_toggle_holo_light.xml b/core/res/res/drawable/btn_toggle_holo_light.xml
new file mode 100644
index 0000000..d3f1012
--- /dev/null
+++ b/core/res/res/drawable/btn_toggle_holo_light.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true"
+        android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_on_normal_holo_light" />
+    <item android:state_checked="true"
+        android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_toggle_on_disabled_holo_light" />
+    <item android:state_checked="true" android:state_pressed="true"
+        android:drawable="@drawable/btn_toggle_on_pressed_holo_light" />
+    <item android:state_checked="true"
+        android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_on_focused_holo_light" />
+    <item android:state_checked="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_on_normal_holo_light" />
+    <item android:state_checked="true"  android:state_focused="true"
+        android:drawable="@drawable/btn_toggle_on_disabled_focused_holo_light" />
+    <item android:state_checked="true"
+         android:drawable="@drawable/btn_toggle_on_disabled_holo_light" />
+
+    <item android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_off_normal_holo_light" />
+    <item android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/btn_toggle_off_disabled_holo_light" />
+    <item android:state_pressed="true"
+        android:drawable="@drawable/btn_toggle_off_pressed_holo_light" />
+    <item android:state_focused="true" android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_off_focused_holo_light" />
+    <item android:state_enabled="true"
+        android:drawable="@drawable/btn_toggle_off_normal_holo_light" />
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_toggle_off_disabled_focused_holo_light" />
+    <item
+         android:drawable="@drawable/btn_toggle_off_disabled_holo_light" />
+</selector>
diff --git a/core/res/res/drawable/cab_ic_close_holo.xml b/core/res/res/drawable/cab_ic_close_holo.xml
new file mode 100644
index 0000000..1baf7cc
--- /dev/null
+++ b/core/res/res/drawable/cab_ic_close_holo.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true"
+          android:drawable="@drawable/cab_ic_close_pressed_holo" />
+    <item android:state_focused="true"
+          android:drawable="@drawable/cab_ic_close_focused_holo" />
+    <item android:drawable="@drawable/cab_ic_close_normal_holo" />
+</selector>
diff --git a/core/res/res/drawable/edit_query_background.xml b/core/res/res/drawable/edit_query_background.xml
new file mode 100644
index 0000000..1446f8c
--- /dev/null
+++ b/core/res/res/drawable/edit_query_background.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_window_focused="false"
+        android:drawable="@drawable/edit_query_background_normal" />
+
+    <item android:state_focused="true" android:state_pressed="true"
+        android:drawable="@drawable/edit_query_background_pressed" />
+
+    <item android:state_focused="false" android:state_pressed="true"
+          android:drawable="@drawable/edit_query_background_pressed" />
+
+    <item android:state_focused="true"
+        android:drawable="@drawable/edit_query_background_selected" />
+
+    <item android:drawable="@drawable/edit_query_background_normal" />
+
+</selector>
diff --git a/core/res/res/drawable/edit_text_holo_dark.xml b/core/res/res/drawable/edit_text_holo_dark.xml
new file mode 100644
index 0000000..b7d24ff
--- /dev/null
+++ b/core/res/res/drawable/edit_text_holo_dark.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/textfield_default_holo_dark" />
+    <item android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/textfield_disabled_holo_dark" />
+    <item android:state_pressed="true" android:drawable="@drawable/textfield_pressed_holo_dark" />
+    <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_selected_holo_dark" />
+    <item android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_dark" />
+    <item android:state_focused="true" android:drawable="@drawable/textfield_disabled_selected_holo_dark" />
+    <item android:drawable="@drawable/textfield_disabled_holo_dark" />
+</selector>
+
diff --git a/core/res/res/drawable/edit_text_holo_light.xml b/core/res/res/drawable/edit_text_holo_light.xml
new file mode 100644
index 0000000..dae39e3
--- /dev/null
+++ b/core/res/res/drawable/edit_text_holo_light.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:state_enabled="true"
+        android:drawable="@drawable/textfield_default_holo_light" />
+    <item android:state_window_focused="false" android:state_enabled="false"
+        android:drawable="@drawable/textfield_disabled_holo_light" />
+    <item android:state_pressed="true" android:drawable="@drawable/textfield_pressed_holo_light" />
+    <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_selected_holo_light" />
+    <item android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_light" />
+    <item android:state_focused="true" android:drawable="@drawable/textfield_disabled_selected_holo_light" />
+    <item android:drawable="@drawable/textfield_disabled_holo_light" />
+</selector>
+
diff --git a/core/res/res/drawable/group_button_background_holo_dark.xml b/core/res/res/drawable/group_button_background_holo_dark.xml
new file mode 100644
index 0000000..fa00785
--- /dev/null
+++ b/core/res/res/drawable/group_button_background_holo_dark.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_window_focused="false" android:drawable="@color/transparent" />
+
+    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/btn_group_disabled_holo_dark" />
+    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/btn_group_disabled_holo_dark" />
+    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/btn_group_pressed_holo_dark" />
+    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/btn_group_pressed_holo_dark" />
+    <item android:state_focused="true"                                                             android:drawable="@drawable/btn_group_focused_holo_dark" />
+    <item                                                                                          android:drawable="@color/transparent" />
+</selector>
diff --git a/core/res/res/drawable/group_button_background_holo_light.xml b/core/res/res/drawable/group_button_background_holo_light.xml
new file mode 100644
index 0000000..1e74ec7
--- /dev/null
+++ b/core/res/res/drawable/group_button_background_holo_light.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_window_focused="false" android:drawable="@color/transparent" />
+
+    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/btn_group_disabled_holo_light" />
+    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/btn_group_disabled_holo_light" />
+    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/btn_group_pressed_holo_light" />
+    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/btn_group_pressed_holo_light" />
+    <item android:state_focused="true"                                                             android:drawable="@drawable/btn_group_focused_holo_light" />
+    <item                                                                                          android:drawable="@color/transparent" />
+</selector>
diff --git a/core/res/res/drawable/ic_menu_moreoverflow_holo_dark.xml b/core/res/res/drawable/ic_menu_moreoverflow_holo_dark.xml
new file mode 100644
index 0000000..4691edf
--- /dev/null
+++ b/core/res/res/drawable/ic_menu_moreoverflow_holo_dark.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" android:drawable="@drawable/ic_menu_moreoverflow_focused_holo_dark" />
+    <item android:drawable="@drawable/ic_menu_moreoverflow_normal_holo_dark" />
+</selector>
diff --git a/core/res/res/drawable/ic_menu_moreoverflow_holo_light.xml b/core/res/res/drawable/ic_menu_moreoverflow_holo_light.xml
new file mode 100644
index 0000000..5c52ff4
--- /dev/null
+++ b/core/res/res/drawable/ic_menu_moreoverflow_holo_light.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true" android:drawable="@drawable/ic_menu_moreoverflow_focused_holo_light" />
+    <item android:drawable="@drawable/ic_menu_moreoverflow_normal_holo_light" />
+</selector>
diff --git a/core/res/res/drawable/item_background.xml b/core/res/res/drawable/item_background.xml
new file mode 100644
index 0000000..a1c9ff8
--- /dev/null
+++ b/core/res/res/drawable/item_background.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_window_focused="false" android:drawable="@color/transparent" />
+
+    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_background_disabled" />
+    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_background_disabled" />
+    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition" />
+    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition" />
+    <item android:state_focused="true"                                                             android:drawable="@drawable/list_selector_background_focused" />
+    <item android:drawable="@color/transparent" />
+
+</selector>
diff --git a/core/res/res/drawable/item_background_holo_dark.xml b/core/res/res/drawable/item_background_holo_dark.xml
new file mode 100644
index 0000000..f119ec0
--- /dev/null
+++ b/core/res/res/drawable/item_background_holo_dark.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_window_focused="false" android:drawable="@color/transparent" />
+
+    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_disabled_holo_dark" />
+    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_disabled_holo_dark" />
+    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_dark" />
+    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_dark" />
+    <item android:state_focused="true"                                                             android:drawable="@drawable/list_focused_holo" />
+    <item                                                                                          android:drawable="@color/transparent" />
+</selector>
diff --git a/core/res/res/drawable/item_background_holo_light.xml b/core/res/res/drawable/item_background_holo_light.xml
new file mode 100644
index 0000000..d3d278a
--- /dev/null
+++ b/core/res/res/drawable/item_background_holo_light.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_window_focused="false" android:drawable="@color/transparent" />
+
+    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_disabled_holo_light" />
+    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_disabled_holo_light" />
+    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_light" />
+    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_light" />
+    <item android:state_focused="true"                                                             android:drawable="@drawable/list_focused_holo" />
+    <item                                                                                          android:drawable="@color/transparent" />
+</selector>
diff --git a/core/res/res/drawable/list_selected_background.xml b/core/res/res/drawable/list_selected_background.xml
new file mode 100644
index 0000000..2bb6594
--- /dev/null
+++ b/core/res/res/drawable/list_selected_background.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_focused="true"   android:state_checked="true" android:drawable="@drawable/list_selector_background_focused_selected" />
+    <item android:state_focused="false"  android:state_checked="true" android:drawable="@drawable/list_selector_background_selected" />
+
+</selector>
diff --git a/core/res/res/drawable/list_selected_background_light.xml b/core/res/res/drawable/list_selected_background_light.xml
new file mode 100644
index 0000000..15fc1cb
--- /dev/null
+++ b/core/res/res/drawable/list_selected_background_light.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_focused="true"   android:state_checked="true" android:drawable="@drawable/list_selector_background_focused_selected" />
+    <item android:state_focused="false"  android:state_checked="true" android:drawable="@drawable/list_selector_background_selected_light" />
+
+</selector>
diff --git a/core/res/res/drawable/list_selector_background.xml b/core/res/res/drawable/list_selector_background.xml
index bca996c..f5eb12d 100644
--- a/core/res/res/drawable/list_selector_background.xml
+++ b/core/res/res/drawable/list_selector_background.xml
@@ -14,24 +14,16 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
 
-    <item android:state_window_focused="false"
-        android:drawable="@color/transparent" />
+    <item android:state_window_focused="false" android:drawable="@color/transparent" />
 
     <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
-    <item android:state_focused="true" android:state_enabled="false"
-        android:state_pressed="true"
-        android:drawable="@drawable/list_selector_background_disabled" />
-    <item android:state_focused="true" android:state_enabled="false"
-        android:drawable="@drawable/list_selector_background_disabled" />
-
-    <item android:state_focused="true" android:state_pressed="true"
-        android:drawable="@drawable/list_selector_background_transition" />
-    <item android:state_focused="false" android:state_pressed="true"
-        android:drawable="@drawable/list_selector_background_transition" />
-
-    <item android:state_focused="true"
-        android:drawable="@drawable/list_selector_background_focus" />
-
+    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_background_disabled" />
+    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_background_disabled" />
+    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition" />
+    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition" />
+    <item android:state_focused="true"                                                             android:drawable="@drawable/list_selector_background_focused" />
+    
 </selector>
diff --git a/core/res/res/drawable/list_selector_background_light.xml b/core/res/res/drawable/list_selector_background_light.xml
new file mode 100644
index 0000000..50a821b
--- /dev/null
+++ b/core/res/res/drawable/list_selector_background_light.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+
+    <item android:state_window_focused="false" android:drawable="@color/transparent" />
+
+    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_background_disabled_light" />
+    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_background_disabled_light" />
+    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_light" />
+    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_light" />
+    <item android:state_focused="true"                                                             android:drawable="@drawable/list_selector_background_focused_light" />
+
+</selector>
diff --git a/core/res/res/drawable/list_selector_background_transition_holo_dark.xml b/core/res/res/drawable/list_selector_background_transition_holo_dark.xml
new file mode 100644
index 0000000..7c68426
--- /dev/null
+++ b/core/res/res/drawable/list_selector_background_transition_holo_dark.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@android:drawable/list_pressed_holo_dark"  />
+    <item android:drawable="@android:drawable/list_longpressed_holo"  />
+</transition>
diff --git a/core/res/res/drawable/list_selector_background_transition_holo_light.xml b/core/res/res/drawable/list_selector_background_transition_holo_light.xml
new file mode 100644
index 0000000..fc08a84
--- /dev/null
+++ b/core/res/res/drawable/list_selector_background_transition_holo_light.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@android:drawable/list_pressed_holo_light"  />
+    <item android:drawable="@android:drawable/list_longpressed_holo"  />
+</transition>
diff --git a/core/res/res/drawable/list_selector_background_transition_light.xml b/core/res/res/drawable/list_selector_background_transition_light.xml
new file mode 100644
index 0000000..634f895
--- /dev/null
+++ b/core/res/res/drawable/list_selector_background_transition_light.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@android:drawable/list_selector_background_pressed_light"  />
+    <item android:drawable="@android:drawable/list_selector_background_longpress_light"  />
+</transition>
diff --git a/core/res/res/drawable/list_selector_holo_dark.xml b/core/res/res/drawable/list_selector_holo_dark.xml
new file mode 100644
index 0000000..9a6cb89
--- /dev/null
+++ b/core/res/res/drawable/list_selector_holo_dark.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+
+    <item android:state_window_focused="false" android:drawable="@color/transparent" />
+
+    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_disabled_holo_dark" />
+    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_disabled_holo_dark" />
+    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_dark" />
+    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_dark" />
+    <item android:state_focused="true"                                                             android:drawable="@drawable/list_focused_holo" />
+</selector>
diff --git a/core/res/res/drawable/list_selector_holo_light.xml b/core/res/res/drawable/list_selector_holo_light.xml
new file mode 100644
index 0000000..844259e
--- /dev/null
+++ b/core/res/res/drawable/list_selector_holo_light.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+
+    <item android:state_window_focused="false" android:drawable="@color/transparent" />
+
+    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_disabled_holo_light" />
+    <item android:state_focused="true"  android:state_enabled="false"                              android:drawable="@drawable/list_selector_disabled_holo_light" />
+    <item android:state_focused="true"                                android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_light" />
+    <item android:state_focused="false"                               android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_light" />
+    <item android:state_focused="true"                                                             android:drawable="@drawable/list_focused_holo" />
+
+</selector>
diff --git a/core/res/res/drawable/minitab_lt.xml b/core/res/res/drawable/minitab_lt.xml
new file mode 100644
index 0000000..aeea97c
--- /dev/null
+++ b/core/res/res/drawable/minitab_lt.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:state_selected="true"
+          android:drawable="@drawable/minitab_lt_press" />
+    <item android:state_selected="true"
+          android:drawable="@drawable/minitab_lt_selected" />
+    <item android:state_pressed="true"
+          android:drawable="@drawable/minitab_lt_unselected_press" />
+    <item android:drawable="@drawable/minitab_lt_unselected" />
+</selector>
diff --git a/core/res/res/drawable/quickcontact_nobadge.xml b/core/res/res/drawable/quickcontact_nobadge.xml
deleted file mode 100644
index 922fa0e..0000000
--- a/core/res/res/drawable/quickcontact_nobadge.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* bubble_with_chats.xml
-**
-** Copyright 2009, 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.
-*/
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/quickcontact_nobadge_pressed" />
-    <item android:state_selected="true" android:drawable="@drawable/quickcontact_nobadge_highlight" />
-    <item android:state_focused="true" android:drawable="@drawable/quickcontact_nobadge_highlight" />
-    <item android:state_enabled="false" android:drawable="@drawable/quickcontact_nobadge_normal" />
-    <item android:drawable="@drawable/quickcontact_nobadge_normal" />
-</selector>
diff --git a/core/res/res/drawable/quickcontact_nobadge_highlight.9.png b/core/res/res/drawable/quickcontact_nobadge_highlight.9.png
deleted file mode 100644
index f0f50b3..0000000
--- a/core/res/res/drawable/quickcontact_nobadge_highlight.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/quickcontact_nobadge_normal.9.png b/core/res/res/drawable/quickcontact_nobadge_normal.9.png
deleted file mode 100644
index 01cc9dc..0000000
--- a/core/res/res/drawable/quickcontact_nobadge_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/quickcontact_nobadge_pressed.9.png b/core/res/res/drawable/quickcontact_nobadge_pressed.9.png
deleted file mode 100644
index 6e22c87..0000000
--- a/core/res/res/drawable/quickcontact_nobadge_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml b/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml
new file mode 100644
index 0000000..90172a5
--- /dev/null
+++ b/core/res/res/drawable/scrubber_progress_horizontal_holo_dark.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:id="@android:id/background"
+          android:drawable="@android:drawable/scrubber_track_holo_dark" />
+    <item android:id="@android:id/secondaryProgress"
+          android:drawable="@android:drawable/scrubber_track_holo_dark" />
+    <item android:id="@android:id/progress"
+          android:drawable="@android:drawable/scrubber_track_holo_dark" />
+</layer-list>
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml b/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml
new file mode 100644
index 0000000..5fc9697
--- /dev/null
+++ b/core/res/res/drawable/scrubber_progress_horizontal_holo_light.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:id="@android:id/background"
+          android:drawable="@android:drawable/scrubber_track_holo_light" />
+    <item android:id="@android:id/secondaryProgress"
+          android:drawable="@android:drawable/scrubber_track_holo_light" />
+    <item android:id="@android:id/progress"
+          android:drawable="@android:drawable/scrubber_track_holo_light" />
+</layer-list>
diff --git a/core/res/res/drawable/search_dropdown_dark.xml b/core/res/res/drawable/search_dropdown_dark.xml
new file mode 100644
index 0000000..2628418
--- /dev/null
+++ b/core/res/res/drawable/search_dropdown_dark.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#F0A0A0A0"/>
+    <stroke android:width="2dp" color="#A00080FF"/>
+    <padding android:left="5dp" android:top="0dp"
+        android:right="5dp" android:bottom="1dp" />
+</shape>
diff --git a/core/res/res/drawable/search_dropdown_light.xml b/core/res/res/drawable/search_dropdown_light.xml
new file mode 100644
index 0000000..0d00c58
--- /dev/null
+++ b/core/res/res/drawable/search_dropdown_light.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#F0FFFFFF"/>
+    <stroke android:width="1dp" color="#A00080FF"/>
+    <padding android:left="5dp" android:top="0dp"
+        android:right="5dp" android:bottom="1dp" />
+</shape>
diff --git a/core/res/res/drawable/spinner_background_holo_dark.xml b/core/res/res/drawable/spinner_background_holo_dark.xml
new file mode 100644
index 0000000..eb6b18b
--- /dev/null
+++ b/core/res/res/drawable/spinner_background_holo_dark.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:drawable="@drawable/spinner_disabled_holo_dark" />
+    <item android:state_pressed="true"
+          android:drawable="@drawable/spinner_pressed_holo_dark" />
+    <item android:state_pressed="false" android:state_focused="true"
+          android:drawable="@drawable/spinner_focused_holo_dark" />
+    <item android:drawable="@drawable/spinner_default_holo_dark" />
+</selector>
diff --git a/core/res/res/drawable/spinner_background_holo_light.xml b/core/res/res/drawable/spinner_background_holo_light.xml
new file mode 100644
index 0000000..9d17ed0c
--- /dev/null
+++ b/core/res/res/drawable/spinner_background_holo_light.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:drawable="@drawable/spinner_disabled_holo_light" />
+    <item android:state_pressed="true"
+          android:drawable="@drawable/spinner_pressed_holo_light" />
+    <item android:state_pressed="false" android:state_focused="true"
+          android:drawable="@drawable/spinner_focused_holo_light" />
+    <item android:drawable="@drawable/spinner_default_holo_light" />
+</selector>
diff --git a/core/res/res/drawable/spinner_cab_background_holo_dark.xml b/core/res/res/drawable/spinner_cab_background_holo_dark.xml
new file mode 100644
index 0000000..5572450
--- /dev/null
+++ b/core/res/res/drawable/spinner_cab_background_holo_dark.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:drawable="@drawable/spinner_cab_disabled_holo_dark" />
+    <item android:state_pressed="true"
+          android:drawable="@drawable/spinner_cab_pressed_holo_dark" />
+    <item android:state_pressed="false" android:state_focused="true"
+          android:drawable="@drawable/spinner_cab_focused_holo_dark" />
+    <item android:drawable="@drawable/spinner_cab_default_holo_dark" />
+</selector>
diff --git a/core/res/res/drawable/spinner_cab_background_holo_light.xml b/core/res/res/drawable/spinner_cab_background_holo_light.xml
new file mode 100644
index 0000000..98ff48d
--- /dev/null
+++ b/core/res/res/drawable/spinner_cab_background_holo_light.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:drawable="@drawable/spinner_cab_disabled_holo_light" />
+    <item android:state_pressed="true"
+          android:drawable="@drawable/spinner_cab_pressed_holo_light" />
+    <item android:state_pressed="false" android:state_focused="true"
+          android:drawable="@drawable/spinner_cab_focused_holo_light" />
+    <item android:drawable="@drawable/spinner_cab_default_holo_light" />
+</selector>
diff --git a/core/res/res/drawable/tab_bottom_right.xml b/core/res/res/drawable/tab_bottom_right.xml
index f7f5c2f..450c461 100644
--- a/core/res/res/drawable/tab_bottom_right.xml
+++ b/core/res/res/drawable/tab_bottom_right.xml
@@ -16,6 +16,6 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_pressed="true" android:drawable="@drawable/tab_press_bar_right"/>
-    <item android:state_focused="false" android:state_pressed="false" android:drawable="@drawable/tab_selected_bar_right"/>
-    <item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/tab_focus_bar_right"/>
+    <item android:state_focused="false" android:drawable="@drawable/tab_selected_bar_right"/>
+    <item android:state_focused="true" android:drawable="@drawable/tab_focus_bar_right"/>
 </selector>
diff --git a/core/res/res/drawable/tab_indicator_holo.xml b/core/res/res/drawable/tab_indicator_holo.xml
new file mode 100644
index 0000000..0d46e0c
--- /dev/null
+++ b/core/res/res/drawable/tab_indicator_holo.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Non focused states -->
+    <item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/tab_unselected_holo" />
+    <item android:state_focused="false" android:state_selected="true"  android:state_pressed="false" android:drawable="@drawable/tab_selected_holo" />
+
+    <!-- Focused states -->
+    <item android:state_focused="true" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/tab_unselected_focused_holo" />
+    <item android:state_focused="true" android:state_selected="true"  android:state_pressed="false" android:drawable="@drawable/tab_selected_focused_holo" />
+
+    <!-- Pressed -->
+    <!--    Non focused states -->
+    <item android:state_focused="false" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/tab_unselected_pressed_holo" />
+    <item android:state_focused="false" android:state_selected="true"  android:state_pressed="true" android:drawable="@drawable/tab_selected_pressed_holo" />
+
+    <!--    Focused states -->
+    <item android:state_focused="true" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/tab_unselected_pressed_focused_holo" />
+    <item android:state_focused="true" android:state_selected="true"  android:state_pressed="true" android:drawable="@drawable/tab_selected_pressed_focused_holo" />
+</selector>
diff --git a/core/res/res/layout-xlarge/activity_list.xml b/core/res/res/layout-xlarge/activity_list.xml
new file mode 100644
index 0000000..9d6b8f5
--- /dev/null
+++ b/core/res/res/layout-xlarge/activity_list.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.internal.widget.WeightedLinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/parentPanel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingTop="9dip"
+    android:paddingBottom="3dip"
+    android:paddingLeft="3dip"
+    android:paddingRight="1dip"
+    android:majorWeightMin="0.45"
+    android:minorWeightMin="0.72"
+    android:majorWeightMax="0.45"
+    android:minorWeightMax="0.72">
+
+    <LinearLayout android:id="@+id/topPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="48dip"
+        android:orientation="vertical">
+        <LinearLayout android:id="@+id/title_template"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:gravity="center_vertical"
+            android:layout_marginTop="8dip"
+            android:layout_marginBottom="8dip"
+            android:layout_marginRight="32dip">
+            <ImageView android:id="@+id/icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingRight="16dip"
+                android:src="@null"
+                android:visibility="gone" />
+            <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle"
+                style="?android:attr/textAppearanceLarge"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+        <ImageView android:id="@+id/titleDivider"
+            android:layout_width="match_parent"
+            android:layout_height="1dip"
+            android:scaleType="fitXY"
+            android:gravity="fill_horizontal"
+            android:src="@android:drawable/divider_horizontal_dark" />
+        <!-- If the client uses a customTitle, it will be added here. -->
+    </LinearLayout>
+
+    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1">
+
+        <ListView
+            android:id="@android:id/list"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            />
+
+        <TextView
+            android:id="@android:id/empty"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:text="@string/activity_list_empty"
+            android:visibility="gone"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            />
+    </FrameLayout>
+
+    <ImageView
+        android:layout_width="match_parent"
+        android:layout_height="1dip"
+        android:scaleType="fitXY"
+        android:gravity="fill_horizontal"
+        android:src="@android:drawable/divider_horizontal_dark" />
+
+    <LinearLayout android:id="@+id/buttonPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="54dip"
+        android:orientation="vertical" >
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:paddingTop="4dip"
+            android:paddingLeft="2dip"
+            android:paddingRight="2dip"
+            android:measureWithLargestChild="true">
+            <LinearLayout android:id="@+id/leftSpacer"
+                android:layout_weight="0.25"
+                android:layout_width="0dip"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                />
+            <Button android:id="@+id/button1"
+                android:layout_width="0dip"
+                android:layout_gravity="center"
+                android:layout_weight="1"
+                android:maxLines="2"
+                android:text="@string/cancel"
+                android:layout_height="wrap_content" />
+            <LinearLayout android:id="@+id/rightSpacer"
+                android:layout_width="0dip"
+                android:layout_weight="0.25"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                />
+        </LinearLayout>
+     </LinearLayout>
+</com.android.internal.widget.WeightedLinearLayout>
diff --git a/core/res/res/layout-xlarge/alert_dialog.xml b/core/res/res/layout-xlarge/alert_dialog.xml
new file mode 100644
index 0000000..82b4509
--- /dev/null
+++ b/core/res/res/layout-xlarge/alert_dialog.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/layout/alert_dialog.xml
+**
+** 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.
+*/
+-->
+
+<com.android.internal.widget.WeightedLinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/parentPanel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingTop="9dip"
+    android:paddingBottom="3dip"
+    android:paddingLeft="3dip"
+    android:paddingRight="1dip"
+    android:majorWeightMin="0.45"
+    android:minorWeightMin="0.72"
+    android:majorWeightMax="0.45"
+    android:minorWeightMax="0.72">
+
+    <LinearLayout android:id="@+id/topPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="54dip"
+        android:orientation="vertical">
+        <LinearLayout android:id="@+id/title_template"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:gravity="center_vertical"
+            android:layout_marginTop="6dip"
+            android:layout_marginBottom="9dip"
+            android:layout_marginLeft="10dip"
+            android:layout_marginRight="10dip">
+            <ImageView android:id="@+id/icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="top"
+                android:paddingTop="6dip"
+                android:paddingRight="10dip"
+                android:src="@drawable/ic_dialog_info" />
+            <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle"
+                style="?android:attr/textAppearanceLarge"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+        <ImageView android:id="@+id/titleDivider"
+            android:layout_width="match_parent"
+            android:layout_height="1dip"
+            android:visibility="gone"
+            android:scaleType="fitXY"
+            android:gravity="fill_horizontal"
+            android:src="@android:drawable/divider_horizontal_dark" />
+        <!-- If the client uses a customTitle, it will be added here. -->
+    </LinearLayout>
+
+    <LinearLayout android:id="@+id/contentPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <ScrollView android:id="@+id/scrollView"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="2dip"
+            android:paddingBottom="12dip"
+            android:paddingLeft="14dip"
+            android:paddingRight="10dip">
+            <TextView android:id="@+id/message"
+                style="?android:attr/textAppearanceMedium"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:padding="5dip" />
+        </ScrollView>
+    </LinearLayout>
+
+    <FrameLayout android:id="@+id/customPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1">
+        <FrameLayout android:id="@+android:id/custom"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="5dip"
+            android:paddingBottom="5dip" />
+    </FrameLayout>
+
+    <LinearLayout android:id="@+id/buttonPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="54dip"
+        android:orientation="vertical" >
+        <ButtonGroup
+            style="?android:attr/alertDialogButtonGroupStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:paddingTop="4dip"
+            android:paddingLeft="2dip"
+            android:paddingRight="2dip"
+            android:measureWithLargestChild="true">
+            <LinearLayout android:id="@+id/leftSpacer"
+                android:layout_weight="0.25"
+                android:layout_width="0dip"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                android:visibility="gone" />
+            <Button android:id="@+id/button1"
+                android:layout_width="0dip"
+                android:layout_gravity="left"
+                android:layout_weight="1"
+                android:maxLines="2"
+                android:layout_height="wrap_content" />
+            <Button android:id="@+id/button3"
+                android:layout_width="0dip"
+                android:layout_gravity="center_horizontal"
+                android:layout_weight="1"
+                android:maxLines="2"
+                android:layout_height="wrap_content" />
+            <Button android:id="@+id/button2"
+                android:layout_width="0dip"
+                android:layout_gravity="right"
+                android:layout_weight="1"
+                android:maxLines="2"
+                android:layout_height="wrap_content" />
+            <LinearLayout android:id="@+id/rightSpacer"
+                android:layout_width="0dip"
+                android:layout_weight="0.25"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                android:visibility="gone" />
+        </ButtonGroup>
+     </LinearLayout>
+</com.android.internal.widget.WeightedLinearLayout>
diff --git a/core/res/res/layout-xlarge/alert_dialog_holo.xml b/core/res/res/layout-xlarge/alert_dialog_holo.xml
new file mode 100644
index 0000000..a01e03a
--- /dev/null
+++ b/core/res/res/layout-xlarge/alert_dialog_holo.xml
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.internal.widget.WeightedLinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/parentPanel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingTop="9dip"
+    android:paddingBottom="3dip"
+    android:paddingLeft="3dip"
+    android:paddingRight="1dip"
+    android:majorWeightMin="0.45"
+    android:minorWeightMin="0.72"
+    android:majorWeightMax="0.45"
+    android:minorWeightMax="0.72">
+
+    <LinearLayout android:id="@+id/topPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="48dip"
+        android:orientation="vertical">
+        <LinearLayout android:id="@+id/title_template"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:gravity="center_vertical"
+            android:layout_marginTop="8dip"
+            android:layout_marginBottom="8dip"
+            android:layout_marginLeft="32dip"
+            android:layout_marginRight="32dip">
+            <ImageView android:id="@+id/icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingRight="16dip"
+                android:src="@null" />
+            <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle"
+                style="?android:attr/textAppearanceMedium"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+        <ImageView android:id="@+id/titleDivider"
+            android:layout_width="match_parent"
+            android:layout_height="1dip"
+            android:visibility="gone"
+            android:scaleType="fitXY"
+            android:gravity="fill_horizontal"
+            android:src="@android:drawable/divider_horizontal_dark" />
+        <!-- If the client uses a customTitle, it will be added here. -->
+    </LinearLayout>
+
+    <LinearLayout android:id="@+id/contentPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <ScrollView android:id="@+id/scrollView"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="32dip"
+            android:paddingBottom="32dip"
+            android:paddingLeft="32dip"
+            android:paddingRight="32dip">
+            <TextView android:id="@+id/message"
+                style="?android:attr/textAppearanceMedium"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </ScrollView>
+    </LinearLayout>
+
+    <FrameLayout android:id="@+id/customPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1">
+        <FrameLayout android:id="@+android:id/custom"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dip"
+            android:paddingBottom="8dip" />
+    </FrameLayout>
+
+    <LinearLayout android:id="@+id/buttonPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="54dip"
+        android:orientation="vertical" >
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:paddingTop="4dip"
+            android:paddingLeft="2dip"
+            android:paddingRight="2dip"
+            android:measureWithLargestChild="true">
+            <LinearLayout android:id="@+id/leftSpacer"
+                android:layout_weight="0.25"
+                android:layout_width="0dip"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                android:visibility="gone" />
+            <Button android:id="@+id/button1"
+                android:layout_width="0dip"
+                android:layout_gravity="left"
+                android:layout_weight="1"
+                android:maxLines="2"
+                android:layout_height="wrap_content" />
+            <Button android:id="@+id/button3"
+                android:layout_width="0dip"
+                android:layout_gravity="center_horizontal"
+                android:layout_weight="1"
+                android:maxLines="2"
+                android:layout_height="wrap_content" />
+            <Button android:id="@+id/button2"
+                android:layout_width="0dip"
+                android:layout_gravity="right"
+                android:layout_weight="1"
+                android:maxLines="2"
+                android:layout_height="wrap_content" />
+            <LinearLayout android:id="@+id/rightSpacer"
+                android:layout_width="0dip"
+                android:layout_weight="0.25"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                android:visibility="gone" />
+        </LinearLayout>
+     </LinearLayout>
+</com.android.internal.widget.WeightedLinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard.xml b/core/res/res/layout-xlarge/keyguard.xml
new file mode 100644
index 0000000..ca629f8
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/layout/keyguard.xml
+**
+** 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.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
+    android:paddingLeft="20dip"
+    android:paddingTop="20dip"
+    android:paddingRight="20dip"
+    android:paddingBottom="20dip"
+    android:orientation="vertical" 
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="#ff000000">
+
+    <TextView
+        android:id="@+id/label"
+        android:textSize="16sp" 
+        android:textStyle="bold" 
+        android:textColor="#FFFFFFFF" 
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" 
+        android:text="@string/keyguard_label_text" />
+</LinearLayout>
+
diff --git a/core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml b/core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml
new file mode 100644
index 0000000..8a46546
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:background="@android:color/background_dark"
+        >
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="0px"
+        android:layout_weight="1"
+        android:layout_above="@+id/emergencyCall">
+        <RelativeLayout 
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+                >
+        
+            <TextView 
+                android:id="@+id/topHeader"
+                android:layout_width="match_parent"
+                android:layout_height="64dip"
+                android:layout_alignParentTop="true"
+                android:layout_marginLeft="4dip"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:gravity="center_vertical"
+                android:drawableLeft="@drawable/ic_lock_idle_lock"
+                android:drawablePadding="5dip"
+                />
+        
+            <!-- spacer below header -->
+            <View
+                android:id="@+id/spacerTop"
+                android:layout_width="match_parent"
+                android:layout_height="1dip"
+                android:layout_below="@id/topHeader"
+                android:background="@drawable/divider_horizontal_dark"/>
+        
+            <TextView
+                android:id="@+id/instructions"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/spacerTop"
+                android:layout_marginTop="8dip"
+                android:layout_marginLeft="9dip"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:text="@android:string/lockscreen_glogin_instructions"
+                />
+        
+            <EditText
+                android:id="@+id/login"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@id/instructions"
+                android:layout_marginTop="8dip"
+                android:layout_marginLeft="7dip"
+                android:layout_marginRight="7dip"
+                android:hint="@android:string/lockscreen_glogin_username_hint"
+                android:inputType="textEmailAddress"
+                />
+        
+            <EditText
+                android:id="@+id/password"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@id/login"
+                android:layout_marginTop="15dip"
+                android:layout_marginLeft="7dip"
+                android:layout_marginRight="7dip"
+                android:inputType="textPassword"
+                android:hint="@android:string/lockscreen_glogin_password_hint"
+                android:nextFocusRight="@+id/ok"
+                android:nextFocusDown="@+id/ok"
+                />
+        
+            <!-- ok below password, aligned to right of screen -->
+            <Button
+                android:id="@+id/ok"
+                android:layout_width="85dip"
+                android:layout_height="wrap_content"
+                android:layout_below="@id/password"
+                android:layout_marginTop="7dip"
+                android:layout_marginRight="7dip"
+                android:layout_alignParentRight="true"
+                android:text="@android:string/lockscreen_glogin_submit_button"
+                />
+        
+        </RelativeLayout>
+    </ScrollView>
+    
+    <!-- spacer above emergency call -->
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dip"
+        android:layout_marginBottom="4dip"
+
+        android:background="@drawable/divider_horizontal_dark"/>
+
+    <!-- emergency call button at bottom center -->
+    <Button
+        android:id="@+id/emergencyCall"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:drawableLeft="@drawable/ic_emergency"
+        android:drawablePadding="8dip"
+        android:text="@android:string/lockscreen_emergency_call"
+        />
+
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_lock.xml b/core/res/res/layout-xlarge/keyguard_screen_lock.xml
new file mode 100644
index 0000000..733a350
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_lock.xml
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+        
+<!-- This is the general lock screen which shows information about the
+  state of the device, as well as instructions on how to get past it
+  depending on the state of the device.  It is the same for landscape
+  and portrait.-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:gravity="bottom"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#ff800000"
+        >
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginBottom="15dip"
+        android:layout_marginLeft="15dip"
+        android:layout_marginRight="15dip"
+        android:paddingTop="20dip"
+        android:paddingBottom="20dip"
+        android:background="@android:drawable/popup_full_dark"
+        >
+
+        <!-- when sim is present -->
+        <TextView android:id="@+id/headerSimOk1"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:gravity="center"
+                  android:textSize="34sp"/>
+        <TextView android:id="@+id/headerSimOk2"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:gravity="center"
+                  android:textSize="34sp"/>
+
+        <!-- when sim is missing / locked -->
+        <TextView android:id="@+id/headerSimBad1"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:gravity="center"
+                  android:text="@android:string/lockscreen_missing_sim_message"
+                  android:textAppearance="?android:attr/textAppearanceLarge"/>
+        <TextView android:id="@+id/headerSimBad2"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:layout_marginTop="7dip"
+                  android:layout_marginBottom="7dip"
+                  android:gravity="center"
+                  android:text="@android:string/lockscreen_missing_sim_instructions"
+                  android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+        <!-- spacer after carrier info / sim messages -->
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dip"
+            android:layout_marginTop="8dip"
+            android:background="@android:drawable/divider_horizontal_dark"/>
+
+        <!-- time and date -->
+        <TextView android:id="@+id/time"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:gravity="center"
+                  android:textSize="34sp"/>
+
+        <TextView android:id="@+id/date"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:gravity="center"
+                  android:textSize="18sp"/>
+
+        <!-- spacer after time and date -->
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dip"
+            android:layout_marginBottom="8dip"
+            android:background="@android:drawable/divider_horizontal_dark"
+                />
+
+        <!-- battery info -->
+        <LinearLayout android:id="@+id/batteryInfo"
+                android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+              >
+
+            <ImageView android:id="@+id/batteryInfoIcon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="6dip"
+                android:baselineAligned="true"
+                android:gravity="center"
+            />
+
+            <TextView android:id="@+id/batteryInfoText"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:textSize="18sp"
+                      android:gravity="center"
+            />
+
+        </LinearLayout>
+
+        <!-- spacer after battery info -->
+        <View android:id="@+id/batteryInfoSpacer"
+            android:layout_width="match_parent"
+            android:layout_height="1dip"
+            android:layout_marginTop="8dip"
+            android:layout_marginBottom="8dip"
+            android:background="@android:drawable/divider_horizontal_dark"
+                />
+
+        <!-- next alarm info -->
+
+        <LinearLayout android:id="@+id/nextAlarmInfo"
+                android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+              >
+
+            <ImageView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="6dip"
+                android:baselineAligned="true"
+                android:src="@android:drawable/ic_lock_idle_alarm"
+                android:gravity="center"
+            />
+
+            <TextView android:id="@+id/nextAlarmText"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content"
+                      android:textSize="18sp"
+                      android:gravity="center"
+            />
+        </LinearLayout>
+
+        <!-- spacer after alarm info -->
+        <View android:id="@+id/nextAlarmSpacer"
+            android:layout_width="match_parent"
+            android:layout_height="1dip"
+            android:layout_marginTop="8dip"
+            android:layout_marginBottom="8dip"
+            android:background="@android:drawable/divider_horizontal_dark"/>
+
+        <!-- lock icon with 'screen locked' message
+             (shown when SIM card is present) -->
+        <LinearLayout android:id="@+id/screenLockedInfo"
+            android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            >
+
+            <ImageView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="6dip"
+                android:baselineAligned="true"
+                android:src="@android:drawable/ic_lock_idle_lock"
+                android:gravity="center"
+            />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="18sp"
+                android:text="@android:string/lockscreen_screen_locked"
+                android:gravity="center"
+                    />
+        </LinearLayout>
+
+        <!-- message about how to unlock
+             (shown when SIM card is present) -->
+        <TextView android:id="@+id/lockInstructions"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:layout_marginBottom="5dip"
+                  android:gravity="center"
+                  android:textSize="14sp"/>
+
+
+        <!-- emergency call button shown when sim is missing or PUKd -->
+        <Button
+            android:id="@+id/emergencyCallButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="5dip"
+            android:layout_marginTop="5dip"
+            android:layout_gravity="center_horizontal"
+            android:drawableLeft="@drawable/ic_emergency"
+            android:drawablePadding="8dip"
+            android:text="@android:string/lockscreen_emergency_call"
+           />
+
+    </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml b/core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml
new file mode 100644
index 0000000..c2d87a2
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+        >
+    
+    <!-- left side: status and emergency call button -->
+    <LinearLayout
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:layout_width="0dip"
+            android:orientation="vertical"
+            android:gravity="center_vertical"
+                >
+        <include layout="@layout/keyguard_screen_status_land" />
+    </LinearLayout>
+    
+    <!-- right side: password -->
+    <LinearLayout
+        android:layout_width="0dip"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:layout_weight="1"
+        android:gravity="center">
+
+        <!-- Password entry field -->
+        <EditText android:id="@+id/passwordEntry"
+            android:layout_height="wrap_content"
+            android:layout_width="330dip"
+            android:singleLine="true"
+            android:textStyle="normal"
+            android:inputType="textPassword"
+            android:gravity="center"
+            android:layout_gravity="center"
+            android:textSize="24sp"
+            android:layout_marginTop="120dip"
+            android:layout_marginBottom="5dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:background="@drawable/password_field_default"
+            android:textColor="#ffffffff"
+            />
+
+        <!-- Numeric keyboard -->
+        <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+            android:layout_width="330dip"
+            android:layout_height="260dip"
+            android:background="#00000000"
+            android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+        />
+        <!-- Alphanumeric keyboard -->
+        <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboardAlpha"
+            android:layout_width="450dip"
+            android:layout_height="230dip"
+            android:background="#00000000"
+            android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+        />
+
+        <!-- emergency call button -->
+        <Button
+            android:id="@+id/emergencyCall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:drawableLeft="@drawable/ic_emergency"
+            android:drawablePadding="8dip"
+            android:text="@string/lockscreen_emergency_call"
+            android:visibility="gone"
+            style="@style/Widget.Button.Transparent"
+        />
+
+    </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml b/core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml
new file mode 100644
index 0000000..0927e59
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+        >
+    
+    <!-- left side: status and emergency call button -->
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="0dip"
+            android:layout_weight="1"
+            android:orientation="vertical"
+            android:gravity="center_vertical"
+                >
+        <include layout="@layout/keyguard_screen_status_land" />
+    </LinearLayout>
+    
+    <!-- right side: password -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:gravity="center">
+
+        <!-- Password entry field -->
+        <EditText android:id="@+id/passwordEntry"
+            android:layout_height="wrap_content"
+            android:layout_width="330dip"
+            android:singleLine="true"
+            android:textStyle="normal"
+            android:inputType="textPassword"
+            android:gravity="center"
+            android:layout_gravity="center"
+            android:textSize="24sp"
+            android:layout_marginTop="120dip"
+            android:layout_marginBottom="5dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:background="@drawable/password_field_default"
+            android:textColor="#ffffffff"
+            />
+
+        <!-- Numeric keyboard -->
+        <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard"
+            android:layout_width="330dip"
+            android:layout_height="260dip"
+            android:background="#00000000"
+            android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+        />
+        <!-- Alphanumeric keyboard -->
+        <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboardAlpha"
+            android:layout_width="450dip"
+            android:layout_height="230dip"
+            android:background="#00000000"
+            android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+        />
+
+        <!-- emergency call button -->
+        <Button
+            android:id="@+id/emergencyCall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:drawableLeft="@drawable/ic_emergency"
+            android:drawablePadding="8dip"
+            android:text="@string/lockscreen_emergency_call"
+            android:visibility="gone"
+            style="@style/Widget.Button.Transparent"
+        />
+
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml
new file mode 100644
index 0000000..b8cbe51
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/background_dark"
+        >
+
+  
+    <!-- right side -->
+    <!-- header text ('Enter Pin Code') -->
+    <TextView android:id="@+id/headerText"
+        android:layout_above="@+id/carrier"
+        android:layout_centerHorizontal="true"
+        android:layout_marginBottom="30dip"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="24sp"
+            />
+
+    <!-- Carrier info -->
+    <TextView android:id="@+id/carrier"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_above="@+id/pinDisplayGroup"
+        android:layout_marginTop="9dip"
+        android:gravity="left|bottom"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+    />
+
+    <!-- displays dots as user enters pin -->
+    <LinearLayout android:id="@+id/pinDisplayGroup"
+        android:orientation="horizontal"
+        android:layout_centerInParent="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:addStatesFromChildren="true"
+        android:gravity="center_vertical"
+        android:baselineAligned="false"
+        android:paddingRight="0dip"
+        android:layout_marginRight="30dip"
+        android:layout_marginLeft="30dip"
+        android:background="@android:drawable/edit_text"
+    >
+
+        <EditText android:id="@+id/pinDisplay"
+            android:layout_width="0dip"
+            android:layout_weight="1"
+            android:layout_height="match_parent"
+            android:maxLines="1"
+            android:background="@null"
+            android:textSize="32sp"
+            android:inputType="textPassword"
+            />
+
+        <ImageButton android:id="@+id/backspace"
+             android:src="@android:drawable/ic_input_delete"
+             android:layout_width="wrap_content"
+             android:layout_height="match_parent"
+             android:layout_marginTop="2dip"
+             android:layout_marginRight="2dip"
+             android:layout_marginBottom="2dip"
+             android:gravity="center"
+            />
+
+    </LinearLayout>
+        
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_alignParentBottom="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="8dip"
+        android:layout_marginLeft="8dip"
+        android:layout_marginRight="8dip">
+
+        <Button android:id="@+id/ok"
+            android:text="@android:string/ok"
+            android:layout_alignParentBottom="true"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:layout_marginBottom="8dip"
+            android:layout_marginRight="8dip"
+            android:textSize="18sp"
+            />
+
+        <Button android:id="@+id/emergencyCall"
+            android:text="@android:string/lockscreen_emergency_call"
+            android:layout_alignParentBottom="true"
+            android:layout_centerHorizontal="true"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:layout_marginBottom="8dip"
+            android:layout_marginLeft="8dip"
+            android:textSize="18sp"
+            android:drawableLeft="@drawable/ic_emergency"
+            android:drawablePadding="8dip"
+        />
+    </LinearLayout>
+
+</RelativeLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml
new file mode 100644
index 0000000..009148f
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:background="@android:color/background_dark"
+    android:gravity="center_horizontal">
+
+    <LinearLayout android:id="@+id/topDisplayGroup"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <!-- header text ('Enter Pin Code') -->
+        <TextView android:id="@+id/headerText"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+        <!-- Carrier info -->
+        <TextView android:id="@+id/carrier"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="9dip"
+            android:gravity="center"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+        <!-- password entry -->
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:layout_marginRight="6dip"
+            android:layout_marginLeft="6dip"
+            android:gravity="center_vertical"
+            android:background="@android:drawable/edit_text">
+
+            <!-- displays dots as user enters pin -->
+            <TextView android:id="@+id/pinDisplay"
+                android:layout_width="0dip"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:maxLines="1"
+                android:textAppearance="?android:attr/textAppearanceLargeInverse"
+                android:textStyle="bold"
+                android:inputType="textPassword"
+            />
+
+            <ImageButton android:id="@+id/backspace"
+                android:src="@android:drawable/ic_input_delete"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="-3dip"
+                android:layout_marginBottom="-3dip"
+            />
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <include
+        android:id="@+id/keyPad"
+        layout="@android:layout/twelve_key_entry"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/topDisplayGroup"
+        android:layout_marginTop="10dip"
+    />
+
+    <!-- spacer below keypad -->
+    <View
+        android:id="@+id/spacerBottom"
+        android:layout_width="match_parent"
+        android:layout_height="1dip"
+        android:layout_marginTop="6dip"
+        android:layout_above="@id/emergencyCall"
+        android:background="@android:drawable/divider_horizontal_dark"
+    />
+
+    <!-- The emergency button should take the rest of the space and be centered vertically -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:gravity="center"
+        android:orientation="vertical">
+
+        <!-- emergency call button -->
+        <Button
+            android:id="@+id/emergencyCall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:drawableLeft="@android:drawable/ic_emergency"
+            android:drawablePadding="8dip"
+            android:text="@android:string/lockscreen_emergency_call"
+        />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_status_land.xml b/core/res/res/layout-xlarge/keyguard_screen_status_land.xml
new file mode 100644
index 0000000..8589862
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_status_land.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Status to show on the left side of lock screen -->
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="140dip"
+        android:layout_marginTop="20dip"
+        android:gravity="left"
+        >
+
+    <TextView
+        android:id="@+id/carrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="17sp"
+        android:drawablePadding="4dip"
+        android:layout_marginTop="32dip"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        />
+
+    <com.android.internal.widget.DigitalClock android:id="@+id/time"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentLeft="true"
+        android:layout_marginTop="8dip"
+        android:layout_marginBottom="8dip"
+        >
+
+        <TextView android:id="@+id/timeDisplay"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:ellipsize="none"
+            android:textSize="120sp"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:shadowColor="#C0000000"
+            android:shadowDx="0"
+            android:shadowDy="0"
+            android:shadowRadius="3.0"
+            android:layout_marginBottom="6dip"
+            />
+
+
+        <TextView android:id="@+id/am_pm"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toRightOf="@id/timeDisplay"
+            android:layout_alignBaseline="@id/timeDisplay"
+            android:singleLine="true"
+            android:ellipsize="none"
+            android:textSize="30sp"
+            android:layout_marginLeft="8dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:shadowColor="#C0000000"
+            android:shadowDx="0"
+            android:shadowDy="0"
+            android:shadowRadius="3.0"
+            />
+
+    </com.android.internal.widget.DigitalClock>
+
+    <TextView
+        android:id="@+id/date"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/time"
+        android:layout_marginTop="5dip"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="32sp"
+        />
+
+    <!-- used for instructions such as "draw pattern to unlock", the next alarm, and charging
+         status.  -->
+    <TextView
+        android:id="@+id/status2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="32sp"
+        android:layout_marginTop="50dip"
+        android:drawablePadding="4dip"
+        />
+    <TextView
+        android:id="@+id/status1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="15dip"
+        android:textSize="32sp"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        />
+        
+    <TextView
+        android:id="@+id/propertyOf"
+        android:lineSpacingExtra="8dip"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="22sp"
+        android:layout_marginTop="50dip"
+        android:singleLine="false"
+        android:visibility="invisible"
+        />
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_status_port.xml b/core/res/res/layout-xlarge/keyguard_screen_status_port.xml
new file mode 100644
index 0000000..8589862
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_status_port.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Status to show on the left side of lock screen -->
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="140dip"
+        android:layout_marginTop="20dip"
+        android:gravity="left"
+        >
+
+    <TextView
+        android:id="@+id/carrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="17sp"
+        android:drawablePadding="4dip"
+        android:layout_marginTop="32dip"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        />
+
+    <com.android.internal.widget.DigitalClock android:id="@+id/time"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentLeft="true"
+        android:layout_marginTop="8dip"
+        android:layout_marginBottom="8dip"
+        >
+
+        <TextView android:id="@+id/timeDisplay"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:ellipsize="none"
+            android:textSize="120sp"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:shadowColor="#C0000000"
+            android:shadowDx="0"
+            android:shadowDy="0"
+            android:shadowRadius="3.0"
+            android:layout_marginBottom="6dip"
+            />
+
+
+        <TextView android:id="@+id/am_pm"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toRightOf="@id/timeDisplay"
+            android:layout_alignBaseline="@id/timeDisplay"
+            android:singleLine="true"
+            android:ellipsize="none"
+            android:textSize="30sp"
+            android:layout_marginLeft="8dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:shadowColor="#C0000000"
+            android:shadowDx="0"
+            android:shadowDy="0"
+            android:shadowRadius="3.0"
+            />
+
+    </com.android.internal.widget.DigitalClock>
+
+    <TextView
+        android:id="@+id/date"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/time"
+        android:layout_marginTop="5dip"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="32sp"
+        />
+
+    <!-- used for instructions such as "draw pattern to unlock", the next alarm, and charging
+         status.  -->
+    <TextView
+        android:id="@+id/status2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="32sp"
+        android:layout_marginTop="50dip"
+        android:drawablePadding="4dip"
+        />
+    <TextView
+        android:id="@+id/status1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="15dip"
+        android:textSize="32sp"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        />
+        
+    <TextView
+        android:id="@+id/propertyOf"
+        android:lineSpacingExtra="8dip"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textSize="22sp"
+        android:layout_marginTop="50dip"
+        android:singleLine="false"
+        android:visibility="invisible"
+        />
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml
new file mode 100644
index 0000000..b3645aa
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- This is the general lock screen which shows information about the
+  state of the device, as well as instructions on how to get past it
+  depending on the state of the device.  It is the same for landscape
+  and portrait.-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tabunlock="http://schemas.android.com/apk/res/com.android.tabunlock"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#70000000"
+    android:gravity="center_horizontal"
+    android:id="@+id/root">
+
+    <!-- left side: status and emergency call button -->
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="0dip"
+            android:layout_weight="1"
+            android:orientation="vertical"
+            android:gravity="center_vertical"
+                >
+        <include layout="@layout/keyguard_screen_status_land" />
+    </LinearLayout>
+
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="0dip"
+            android:layout_weight="1"
+            android:orientation="vertical"
+                >
+        <TextView
+            android:id="@+id/screenLocked"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/status2"
+            android:layout_marginLeft="24dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:layout_marginTop="12dip"
+            android:drawablePadding="4dip"
+            />
+
+        <com.android.internal.widget.WaveView
+            android:id="@+id/wave_view"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            />
+
+        <!-- "emergency calls only" shown when sim is missing or PUKd -->
+        <TextView
+            android:id="@+id/emergencyCallText"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/carrier"
+            android:layout_alignParentRight="true"
+            android:layout_marginTop="0dip"
+            android:layout_marginRight="8dip"
+            android:text="@string/emergency_calls_only"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="@color/white"
+           />
+
+        <!-- emergency call button shown when sim is PUKd and tab_selector is
+             hidden -->
+        <Button
+            android:id="@+id/emergencyCallButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:drawableLeft="@drawable/ic_emergency"
+            android:layout_centerInParent="true"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="80dip"
+            style="@style/Widget.Button.Transparent"
+            android:drawablePadding="8dip"
+            android:visibility="gone"
+            />
+    </LinearLayout>
+
+</RelativeLayout>
+
diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml
new file mode 100644
index 0000000..6c99ccac
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- This is the general lock screen which shows information about the
+  state of the device, as well as instructions on how to get past it
+  depending on the state of the device.-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tabunlock="http://schemas.android.com/apk/res/com.android.tabunlock"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:background="#70000000"
+    android:id="@+id/root">
+
+    <!-- left side: status and emergency call button -->
+    <LinearLayout
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:layout_width="0dip"
+            android:orientation="vertical"
+            android:gravity="center_vertical"
+                >
+        <include layout="@layout/keyguard_screen_status_land" />
+    </LinearLayout>
+
+    <!-- right side -->
+    <LinearLayout
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:layout_width="0dip"
+            android:orientation="horizontal"
+            android:gravity="center_horizontal"
+                >
+        <TextView
+                android:id="@+id/screenLocked"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@id/status2"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:gravity="center"
+                android:layout_marginTop="12dip"
+                android:drawablePadding="4dip"
+                />
+
+        <com.android.internal.widget.WaveView
+            android:id="@+id/wave_view"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_marginRight="0dip"
+            android:layout_weight="1.0"
+            />
+
+        <!-- "emergency calls only" shown when sim is missing or PUKd -->
+        <TextView
+            android:id="@+id/emergencyCallText"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_marginTop="20dip"
+            android:text="@string/emergency_calls_only"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="@color/white"
+               />
+
+        <!-- emergency call button shown when sim is PUKd and tab_selector is
+             hidden -->
+        <Button
+            android:id="@+id/emergencyCallButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:drawableLeft="@drawable/ic_emergency"
+            style="@style/Widget.Button.Transparent"
+            android:drawablePadding="8dip"
+            android:layout_marginRight="80dip"
+            android:visibility="gone"
+            />
+
+    </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml
new file mode 100644
index 0000000..42636ad
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the screen that shows the 9 circle unlock widget and instructs
+     the user how to unlock their device, or make an emergency call.  This
+     is the portrait layout.  -->
+
+<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+        >
+
+    <!-- left side: status and emergency call button -->
+    <LinearLayout
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:layout_width="0dip"
+            android:orientation="vertical"
+            android:gravity="center_vertical"
+                >
+        
+        <include layout="@layout/keyguard_screen_status_land" />
+
+        <!-- footer -->
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="140dip"
+            >
+
+            <!-- option 1: a single emergency call button -->
+            <RelativeLayout android:id="@+id/footerNormal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="left"
+                >
+                <Button android:id="@+id/emergencyCallAlone"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/lockscreen_emergency_call"
+                    style="@style/Widget.Button.Transparent"
+                    android:drawableLeft="@drawable/ic_emergency"
+                    android:drawablePadding="8dip"
+                    android:visibility="gone"
+                    />
+            </RelativeLayout>
+
+            <!-- option 2: an emergency call button, and a 'forgot pattern?' button -->
+            <LinearLayout android:id="@+id/footerForgotPattern"
+                android:orientation="vertical"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="left"
+                >
+                <Button android:id="@+id/forgotPattern"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    style="@style/Widget.Button.Transparent"
+                    />
+                <Button android:id="@+id/emergencyCallTogether"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/lockscreen_emergency_call"
+                    style="@style/Widget.Button.Transparent"
+                    android:drawableLeft="@drawable/ic_emergency"
+                    android:drawablePadding="8dip"
+                    android:visibility="gone"
+                    />
+            </LinearLayout>
+        </FrameLayout>
+    </LinearLayout>
+
+    <!-- right side: lock pattern -->
+    <LinearLayout
+        android:layout_weight="1"
+        android:layout_width="0dip"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        >
+        <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
+            android:layout_width="350dip"
+            android:layout_height="350dip"
+            android:layout_marginTop="90dip"
+            android:layout_marginRight="90dip"
+          />
+    </LinearLayout>
+
+</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml
new file mode 100644
index 0000000..aeed79b
--- /dev/null
+++ b/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+        >
+
+    <!-- left side: status and emergency call button -->
+    <LinearLayout
+            android:layout_height="0dip"
+            android:layout_weight="1"
+            android:layout_width="match_parent"
+            android:orientation="vertical"
+            android:gravity="center_vertical"
+                >
+        
+        <include layout="@layout/keyguard_screen_status_port" />
+
+        <!-- footer -->
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="140dip"
+            >
+
+            <!-- option 1: a single emergency call button -->
+            <RelativeLayout android:id="@+id/footerNormal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="left"
+                >
+                <Button android:id="@+id/emergencyCallAlone"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/lockscreen_emergency_call"
+                    style="@style/Widget.Button.Transparent"
+                    android:drawableLeft="@drawable/ic_emergency"
+                    android:drawablePadding="8dip"
+                    android:visibility="gone"
+                    />
+            </RelativeLayout>
+
+            <!-- option 2: an emergency call button, and a 'forgot pattern?' button -->
+            <LinearLayout android:id="@+id/footerForgotPattern"
+                android:orientation="vertical"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="left"
+                >
+                <Button android:id="@+id/forgotPattern"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    style="@style/Widget.Button.Transparent"
+                    />
+                <Button android:id="@+id/emergencyCallTogether"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/lockscreen_emergency_call"
+                    style="@style/Widget.Button.Transparent"
+                    android:drawableLeft="@drawable/ic_emergency"
+                    android:drawablePadding="8dip"
+                    android:visibility="gone"
+                    />
+            </LinearLayout>
+        </FrameLayout>
+    </LinearLayout>
+
+    <!-- right side: lock pattern -->
+    <LinearLayout
+        android:layout_weight="1"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:gravity="center"
+        >
+        <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
+            android:layout_width="350dip"
+            android:layout_height="350dip"
+            android:layout_marginTop="50dip"
+          />
+    </LinearLayout>
+
+</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
+
diff --git a/core/res/res/layout-xlarge/screen_action_bar.xml b/core/res/res/layout-xlarge/screen_action_bar.xml
new file mode 100644
index 0000000..c402556
--- /dev/null
+++ b/core/res/res/layout-xlarge/screen_action_bar.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!--
+This is an optimized layout for a screen with the Action Bar enabled.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:fitsSystemWindows="true">
+    <ViewAnimator android:id="@+id/action_bar_animator"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inAnimation="@anim/push_down_in_no_alpha"
+        android:outAnimation="@anim/push_down_out_no_alpha">
+        <com.android.internal.widget.ActionBarView
+            android:id="@+id/action_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?android:attr/actionBarStyle" />
+        <com.android.internal.widget.ActionBarContextView
+            android:id="@+id/action_context_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?android:attr/actionModeStyle" />
+    </ViewAnimator>
+    <FrameLayout android:id="@android:id/content"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:foregroundGravity="fill_horizontal|top"
+        android:foreground="?android:attr/windowContentOverlay" />
+</LinearLayout>
diff --git a/core/res/res/layout-xlarge/screen_action_bar_overlay.xml b/core/res/res/layout-xlarge/screen_action_bar_overlay.xml
new file mode 100644
index 0000000..af051ae
--- /dev/null
+++ b/core/res/res/layout-xlarge/screen_action_bar_overlay.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!--
+This is an optimized layout for a screen with
+the Action Bar enabled overlaying application content.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fitsSystemWindows="true">
+    <FrameLayout android:id="@android:id/content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+    <ViewAnimator android:id="@+id/action_bar_animator"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="top"
+        android:inAnimation="@anim/push_down_in_no_alpha"
+        android:outAnimation="@anim/push_down_out_no_alpha">
+        <com.android.internal.widget.ActionBarView
+            android:id="@+id/action_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?android:attr/actionBarStyle" />
+        <com.android.internal.widget.ActionBarContextView
+            android:id="@+id/action_context_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?android:attr/actionModeStyle" />
+    </ViewAnimator>
+    <ImageView android:src="?android:attr/windowContentOverlay"
+               android:scaleType="fitXY"
+               android:layout_width="match_parent"
+               android:layout_height="wrap_content"
+               android:layout_below="@id/action_bar_animator" />
+</RelativeLayout>
diff --git a/core/res/res/layout-xlarge/status_bar_latest_event_content.xml b/core/res/res/layout-xlarge/status_bar_latest_event_content.xml
new file mode 100644
index 0000000..c64b90e
--- /dev/null
+++ b/core/res/res/layout-xlarge/status_bar_latest_event_content.xml
@@ -0,0 +1,56 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:paddingTop="7dp"
+        android:paddingLeft="5dp"
+        >
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:paddingTop="3dp"
+        >
+        <!--com.android.server.status.AnimatedImageView android:id="@+id/icon" -->
+        <ImageView android:id="@+id/icon"
+            android:layout_width="25dp"
+            android:layout_height="25dp"
+            android:scaleType="fitCenter"
+            android:src="@drawable/arrow_down_float"/>
+        <TextView android:id="@+id/title"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:paddingLeft="4dp"
+            />
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        >
+        <TextView android:id="@+id/text"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:paddingLeft="4dp"
+            />
+        <android.widget.DateTimeView android:id="@+id/time"
+            android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+            android:layout_marginLeft="4dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:paddingRight="5dp"
+            />
+    </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/action_bar_title_item.xml b/core/res/res/layout/action_bar_title_item.xml
new file mode 100644
index 0000000..3864981
--- /dev/null
+++ b/core/res/res/layout/action_bar_title_item.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:orientation="vertical" >
+    <TextView android:id="@+id/action_bar_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="end" />
+    <TextView android:id="@+id/action_bar_subtitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="end" />
+</LinearLayout>
diff --git a/core/res/res/layout/action_menu_item_layout.xml b/core/res/res/layout/action_menu_item_layout.xml
new file mode 100644
index 0000000..94fcc2f
--- /dev/null
+++ b/core/res/res/layout/action_menu_item_layout.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.internal.view.menu.ActionMenuItemView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:addStatesFromChildren="true"
+    android:background="?attr/selectableItemBackground"
+    android:minWidth="64dip"
+    android:minHeight="?attr/actionBarSize"
+    android:paddingLeft="12dip"
+    android:paddingRight="12dip">
+    <ImageButton android:id="@+id/imageButton"
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                 android:layout_gravity="center"
+                 android:visibility="gone"
+                 style="?attr/actionButtonStyle" />
+    <Button android:id="@+id/textButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:visibility="gone"
+            android:textAppearance="?attr/textAppearanceMedium"
+            style="?attr/buttonStyleSmall"
+            android:background="@null"
+            android:paddingLeft="4dip"
+            android:paddingRight="4dip" />
+</com.android.internal.view.menu.ActionMenuItemView>
diff --git a/core/res/res/layout/action_menu_layout.xml b/core/res/res/layout/action_menu_layout.xml
new file mode 100644
index 0000000..18d5531
--- /dev/null
+++ b/core/res/res/layout/action_menu_layout.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.internal.view.menu.ActionMenuView
+     xmlns:android="http://schemas.android.com/apk/res/android"
+     android:layout_width="wrap_content"
+     android:layout_height="wrap_content" />
diff --git a/core/res/res/layout/action_mode_bar.xml b/core/res/res/layout/action_mode_bar.xml
new file mode 100644
index 0000000..dc09fdc
--- /dev/null
+++ b/core/res/res/layout/action_mode_bar.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<com.android.internal.widget.ActionBarContextView
+     xmlns:android="http://schemas.android.com/apk/res/android"
+     android:layout_width="match_parent"
+     android:layout_height="wrap_content"
+     android:visibility="gone"
+     style="?android:attr/actionModeStyle" />
diff --git a/core/res/res/layout/action_mode_close_item.xml b/core/res/res/layout/action_mode_close_item.xml
new file mode 100644
index 0000000..bc69890
--- /dev/null
+++ b/core/res/res/layout/action_mode_close_item.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<ButtonGroup xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="wrap_content"
+             android:layout_height="wrap_content"
+             android:layout_marginTop="12dip"
+             android:layout_marginBottom="12dip"
+             android:gravity="center"
+             android:showDividers="end"
+             android:background="?android:attr/selectableItemBackground">
+    <Button android:id="@+id/action_mode_close_button"
+            android:text="@string/action_mode_done"
+            android:paddingLeft="16dip"
+            android:paddingRight="16dip"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+</ButtonGroup>
diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml
index d461f40..3982ed9 100644
--- a/core/res/res/layout/alert_dialog.xml
+++ b/core/res/res/layout/alert_dialog.xml
@@ -28,8 +28,9 @@
     android:paddingBottom="3dip"
     android:paddingLeft="3dip"
     android:paddingRight="1dip"
-    android:majorWeight="0.65"
-    android:minorWeight="0.9">
+    android:majorWeightMin="0.65"
+    android:minorWeightMin="0.9"
+    android:majorWeightMax="0.65">
 
     <LinearLayout android:id="@+id/topPanel"
         android:layout_width="match_parent"
@@ -80,8 +81,7 @@
             android:paddingTop="2dip"
             android:paddingBottom="12dip"
             android:paddingLeft="14dip"
-            android:paddingRight="10dip"
-            android:overScrollMode="ifContentScrolls">
+            android:paddingRight="10dip">
             <TextView android:id="@+id/message"
                 style="?android:attr/textAppearanceMedium"
                 android:layout_width="match_parent"
@@ -106,14 +106,15 @@
         android:layout_height="wrap_content"
         android:minHeight="54dip"
         android:orientation="vertical" >
-        <LinearLayout
+        <ButtonGroup
+            style="?android:attr/alertDialogButtonGroupStyle"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal"
             android:paddingTop="4dip"
             android:paddingLeft="2dip"
             android:paddingRight="2dip"
-            android:useLargestChild="true">
+            android:measureWithLargestChild="true">
             <LinearLayout android:id="@+id/leftSpacer"
                 android:layout_weight="0.25"
                 android:layout_width="0dip"
@@ -144,6 +145,6 @@
                 android:layout_height="wrap_content"
                 android:orientation="horizontal"
                 android:visibility="gone" />
-        </LinearLayout>
+        </ButtonGroup>
      </LinearLayout>
 </com.android.internal.widget.WeightedLinearLayout>
diff --git a/core/res/res/layout/contact_header.xml b/core/res/res/layout/contact_header.xml
deleted file mode 100644
index bf467d3..0000000
--- a/core/res/res/layout/contact_header.xml
+++ /dev/null
@@ -1,116 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/banner"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="horizontal"
-    android:background="@drawable/title_bar_medium"
-    android:paddingRight="5dip">
-
-    <android.widget.QuickContactBadge android:id="@+id/photo"
-        android:layout_gravity="center_vertical"
-        android:layout_marginRight="8dip"
-        android:layout_marginLeft="-1dip"
-        style="@*android:style/Widget.QuickContactBadge.WindowSmall" />
-    />
-
-    <LinearLayout
-        android:layout_width="0dip"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:orientation="vertical"
-        android:layout_gravity="center_vertical" >
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
-
-            <ImageView
-                android:id="@+id/aggregate_badge"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:paddingRight="3dip"
-                android:paddingTop="3dip"
-                android:src="@drawable/ic_aggregated"
-                android:visibility="gone"
-            />
-
-            <TextView android:id="@+id/name"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:singleLine="true"
-                android:ellipsize="end"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-                android:textStyle="bold"
-                android:shadowColor="#BB000000"
-                android:shadowRadius="2.75"
-                />
-        </LinearLayout>
-
-        <TextView android:id="@+id/phonetic_name"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:layout_marginTop="-2dip"
-            android:visibility="gone"
-        />
-
-        <TextView android:id="@+id/status"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:layout_marginTop="-2dip"
-            android:visibility="gone"
-        />
-
-        <TextView android:id="@+id/status_date"
-            android:layout_width="match_parent"
-            android:layout_height="0dip"
-            android:layout_weight="1"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textSize="12sp"
-            android:layout_marginTop="-2dip"
-            android:visibility="gone"
-        />
-    </LinearLayout>
-
-    <ImageView
-        android:id="@+id/presence"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:paddingLeft="3dip"
-        android:paddingRight="6dip"
-        android:visibility="gone"
-    />
-
-    <CheckBox
-        android:id="@+id/star"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:visibility="gone"
-        android:contentDescription="@string/description_star"
-        style="?android:attr/starStyle" />
-
-</LinearLayout>
diff --git a/core/res/res/layout/fragment_bread_crumb_item.xml b/core/res/res/layout/fragment_bread_crumb_item.xml
new file mode 100644
index 0000000..517c570
--- /dev/null
+++ b/core/res/res/layout/fragment_bread_crumb_item.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center_vertical"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:drawableLeft="@drawable/nav_divider"
+        android:paddingLeft="12dp"
+        android:drawablePadding="12dp"
+        />
diff --git a/core/res/res/layout/fragment_bread_crumbs.xml b/core/res/res/layout/fragment_bread_crumbs.xml
new file mode 100644
index 0000000..f289e14
--- /dev/null
+++ b/core/res/res/layout/fragment_bread_crumbs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent">
+</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
index ab675c7..3d52f71 100644
--- a/core/res/res/layout/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -28,7 +28,7 @@
         android:layout_height="wrap_content"
         android:orientation="horizontal">
         <!-- "Enter PIN(Password) to unlock" -->
-        <TextView android:id="@+id/enter_password_label"
+        <TextView android:id="@+id/status1"
             android:layout_width="0dip"
             android:layout_height="wrap_content"
             android:layout_weight="1"
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
index 9ee8781..af8a3ef 100644
--- a/core/res/res/layout/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -24,7 +24,7 @@
     android:gravity="center_horizontal">
 
     <!-- "Enter PIN(Password) to unlock" -->
-    <TextView android:id="@+id/enter_password_label"
+    <TextView android:id="@+id/status1"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal"
diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
index c1b406f..83381a1 100644
--- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
@@ -58,18 +58,19 @@
             android:ellipsize="marquee"
             android:gravity="right|bottom"
             />
+
         <com.android.internal.widget.DigitalClock android:id="@+id/time"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignParentTop="true"
             android:layout_alignParentLeft="true"
             android:layout_marginTop="8dip"
+            android:layout_marginBottom="8dip"
             >
 
             <TextView android:id="@+id/timeDisplay"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:gravity="bottom"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:textSize="72sp"
@@ -84,8 +85,9 @@
 
             <TextView android:id="@+id/am_pm"
                 android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:gravity="bottom"
+                android:layout_height="wrap_content"
+                android:layout_toRightOf="@id/timeDisplay"
+                android:layout_alignBaseline="@id/timeDisplay"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:textSize="22sp"
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index 74a0eee..8dacfaf 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -55,12 +55,12 @@
             android:layout_alignParentTop="true"
             android:layout_marginTop="15dip"
             android:layout_marginLeft="20dip"
+            android:layout_marginBottom="8dip"
             >
 
             <TextView android:id="@+id/timeDisplay"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:gravity="bottom"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:textSize="56sp"
@@ -74,8 +74,9 @@
 
             <TextView android:id="@+id/am_pm"
                 android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:gravity="bottom"
+                android:layout_height="wrap_content"
+                android:layout_toRightOf="@id/timeDisplay"
+                android:layout_alignBaseline="@id/timeDisplay"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:textSize="18sp"
diff --git a/core/res/res/layout/list_content.xml b/core/res/res/layout/list_content.xml
index 6f9f1e0..1414032 100644
--- a/core/res/res/layout/list_content.xml
+++ b/core/res/res/layout/list_content.xml
@@ -1,8 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* //device/apps/common/assets/res/layout/list_content.xml
-**
-** Copyright 2006, The Android Open Source Project
+/* Copyright 2010, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License"); 
 ** you may not use this file except in compliance with the License. 
@@ -17,8 +15,42 @@
 ** limitations under the License.
 */
 -->
-<ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/list"
-    android:layout_width="match_parent" 
-    android:layout_height="match_parent"
-    android:drawSelectorOnTop="false"
-    />
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+    
+    <LinearLayout android:id="@+id/progressContainer"
+            android:orientation="vertical"
+            android:layout_width="match_parent" 
+            android:layout_height="match_parent"
+            android:visibility="gone"
+            android:gravity="center">
+        
+        <ProgressBar style="?android:attr/progressBarStyleLarge"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+        <TextView android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:text="@string/loading"
+                android:paddingTop="4dip"
+                android:singleLine="true" />
+            
+    </LinearLayout>
+        
+    <FrameLayout android:id="@+id/listContainer"
+            android:layout_width="match_parent" 
+            android:layout_height="match_parent">
+            
+        <ListView android:id="@android:id/list"
+                android:layout_width="match_parent" 
+                android:layout_height="match_parent"
+                android:drawSelectorOnTop="false" />
+        <TextView android:id="@+android:id/internalEmpty"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:gravity="center"
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+    </FrameLayout>
+        
+</FrameLayout>
diff --git a/core/res/res/layout/list_content_simple.xml b/core/res/res/layout/list_content_simple.xml
new file mode 100644
index 0000000..6f9f1e0
--- /dev/null
+++ b/core/res/res/layout/list_content_simple.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/layout/list_content.xml
+**
+** 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.
+*/
+-->
+<ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/list"
+    android:layout_width="match_parent" 
+    android:layout_height="match_parent"
+    android:drawSelectorOnTop="false"
+    />
diff --git a/core/res/res/layout/list_menu_item_layout.xml b/core/res/res/layout/list_menu_item_layout.xml
index 39c88722..57091a1 100644
--- a/core/res/res/layout/list_menu_item_layout.xml
+++ b/core/res/res/layout/list_menu_item_layout.xml
@@ -36,7 +36,7 @@
             android:layout_height="wrap_content"
             android:layout_alignParentTop="true"
             android:layout_alignParentLeft="true"
-            android:textAppearance="?android:attr/textAppearanceLargeInverse"
+            android:textAppearance="?android:attr/textAppearanceLarge"
             android:singleLine="true"
             android:duplicateParentState="true"
             android:ellipsize="marquee"
@@ -48,7 +48,7 @@
             android:layout_height="wrap_content"
             android:layout_below="@id/title"
             android:layout_alignParentLeft="true"
-            android:textAppearance="?android:attr/textAppearanceSmallInverse"
+            android:textAppearance="?android:attr/textAppearanceSmall"
             android:singleLine="true"
             android:duplicateParentState="true" />
 
diff --git a/core/res/res/layout/locale_picker_item.xml b/core/res/res/layout/locale_picker_item.xml
new file mode 100644
index 0000000..b63f5ab
--- /dev/null
+++ b/core/res/res/layout/locale_picker_item.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:padding="5dip">
+
+    <TextView android:id="@+id/locale"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+    />
+</LinearLayout >
diff --git a/core/res/res/layout/popup_menu_item_layout.xml b/core/res/res/layout/popup_menu_item_layout.xml
new file mode 100644
index 0000000..c30c4f2
--- /dev/null
+++ b/core/res/res/layout/popup_menu_item_layout.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.internal.view.menu.ListMenuItemView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="?android:attr/listPreferredItemHeight">
+    
+    <!-- Icon will be inserted here. -->
+    
+    <!-- The title and summary have some gap between them, and this 'group' should be centered vertically. -->
+    <RelativeLayout
+        android:layout_width="0dip"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginLeft="6dip"
+        android:layout_marginRight="6dip"
+        android:duplicateParentState="true">
+        
+        <TextView 
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentLeft="true"
+            android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
+            android:singleLine="true"
+            android:duplicateParentState="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal" />
+
+        <TextView
+            android:id="@+id/shortcut"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/title"
+            android:layout_alignParentLeft="true"
+            android:textAppearance="?android:attr/textAppearanceSmallPopupMenu"
+            android:singleLine="true"
+            android:duplicateParentState="true" />
+
+    </RelativeLayout>
+
+    <!-- Checkbox, and/or radio button will be inserted here. -->
+    
+</com.android.internal.view.menu.ListMenuItemView>
diff --git a/core/res/res/layout/preference_dialog_edittext.xml b/core/res/res/layout/preference_dialog_edittext.xml
index b2a4368..5be5773 100644
--- a/core/res/res/layout/preference_dialog_edittext.xml
+++ b/core/res/res/layout/preference_dialog_edittext.xml
@@ -17,8 +17,7 @@
 <!-- Layout used as the dialog's content View for EditTextPreference. -->
 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:overScrollMode="ifContentScrolls">
+    android:layout_height="match_parent">
 
     <LinearLayout
         android:id="@+android:id/edittext_container"
diff --git a/core/res/res/layout/preference_header_item.xml b/core/res/res/layout/preference_header_item.xml
new file mode 100644
index 0000000..f00e7aa
--- /dev/null
+++ b/core/res/res/layout/preference_header_item.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Layout of a header item in PreferenceActivity. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="48dp"
+    android:background="?android:attr/activatedBackgroundIndicator"
+    android:gravity="center_vertical"
+    android:paddingRight="?android:attr/scrollbarSize">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="6dip"
+        android:layout_marginRight="6dip"
+        android:layout_gravity="center" />
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="2dip"
+        android:layout_marginRight="6dip"
+        android:layout_marginTop="6dip"
+        android:layout_marginBottom="6dip"
+        android:layout_weight="1">
+
+        <TextView android:id="@+android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal" />
+
+        <TextView android:id="@+android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignLeft="@android:id/title"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:ellipsize="end"
+            android:maxLines="2" />
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index 8f86981..5b42407 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -4,22 +4,110 @@
 **
 ** 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 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 -->
-<ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/list"
-    android:layout_width="match_parent" 
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
     android:layout_height="match_parent"
-    android:drawSelectorOnTop="false"
-    android:scrollbarAlwaysDrawVerticalTrack="true"
-    />
+    android:layout_width="match_parent"
+    android:background="@android:color/transparent">
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="0px"
+        android:layout_weight="1">
+
+        <LinearLayout
+            android:id="@+id/headers"
+            android:orientation="vertical"
+            android:layout_width="0px"
+            android:layout_height="match_parent"
+            android:layout_marginLeft="32dp"
+            android:layout_marginTop="32dp"
+            android:layout_marginRight="16dp"
+            android:layout_marginBottom="32dp"
+            android:layout_weight="10">
+
+            <ListView android:id="@android:id/list"
+                android:layout_width="match_parent"
+                android:layout_height="0px"
+                android:layout_weight="1"
+                android:drawSelectorOnTop="false"
+                android:background="#40404040"
+                android:cacheColorHint="@android:color/transparent"
+                android:listPreferredItemHeight="48dp"
+                android:scrollbarAlwaysDrawVerticalTrack="true" />
+
+            <FrameLayout android:id="@+id/list_footer"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="0" />
+
+        </LinearLayout>
+
+        <FrameLayout android:id="@+id/prefs"
+                android:layout_width="0px"
+                android:layout_height="match_parent"
+                android:layout_weight="25"
+                android:layout_marginLeft="16dp"
+                android:layout_marginRight="16dp"
+                android:layout_marginTop="16dp"
+                android:layout_marginBottom="16dp"
+                android:background="#60202040"
+                android:visibility="gone" />
+    </LinearLayout>
+
+    <RelativeLayout android:id="@+id/button_bar"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_weight="0"
+        android:background="@android:drawable/bottom_bar"
+        android:visibility="gone">
+
+        <Button android:id="@+id/back_button"
+            android:layout_width="150dip"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:layout_alignParentLeft="true"
+            android:drawableLeft="@drawable/ic_btn_back"
+            android:drawablePadding="3dip"
+            android:text="@string/back_button_label"
+        />
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true">
+
+            <Button android:id="@+id/skip_button"
+                android:layout_width="150dip"
+                android:layout_height="wrap_content"
+                android:layout_margin="5dip"
+                android:text="@string/skip_button_label"
+                android:visibility="gone"
+            />
+
+            <Button android:id="@+id/next_button"
+                android:layout_width="150dip"
+                android:layout_height="wrap_content"
+                android:layout_margin="5dip"
+                android:drawableRight="@drawable/ic_btn_next"
+                android:drawablePadding="3dip"
+                android:text="@string/next_button_label"
+            />
+        </LinearLayout>
+    </RelativeLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
new file mode 100644
index 0000000..c499b8a
--- /dev/null
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* 
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:background="@android:color/transparent">
+
+    <ListView android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="0px"
+        android:layout_weight="1"
+        android:drawSelectorOnTop="false"
+        android:cacheColorHint="@android:color/transparent"
+        android:scrollbarAlwaysDrawVerticalTrack="true" />
+
+    <RelativeLayout android:id="@+id/button_bar"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_weight="0"
+        android:background="@android:drawable/bottom_bar"
+        android:visibility="gone">
+
+        <Button android:id="@+id/back_button"
+            android:layout_width="150dip"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:layout_alignParentLeft="true"
+            android:drawableLeft="@drawable/ic_btn_back"
+            android:drawablePadding="3dip"
+            android:text="@string/back_button_label"
+        />
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true">
+
+            <Button android:id="@+id/skip_button"
+                android:layout_width="150dip"
+                android:layout_height="wrap_content"
+                android:layout_margin="5dip"
+                android:text="@string/skip_button_label"
+                android:visibility="gone"
+            />
+
+            <Button android:id="@+id/next_button"
+                android:layout_width="150dip"
+                android:layout_height="wrap_content"
+                android:layout_margin="5dip"
+                android:drawableRight="@drawable/ic_btn_next"
+                android:drawablePadding="3dip"
+                android:text="@string/next_button_label"
+            />
+        </LinearLayout>
+    </RelativeLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/remote_views_adapter_default_loading_view.xml b/core/res/res/layout/remote_views_adapter_default_loading_view.xml
new file mode 100644
index 0000000..864e435
--- /dev/null
+++ b/core/res/res/layout/remote_views_adapter_default_loading_view.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+
+    android:id="@+id/default_loading_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_horizontal|center_vertical"
+
+    android:text="@string/loading"
+    android:textColor="#60FFFFFF"
+    android:textSize="18sp"
+    android:shadowColor="#FF000000"
+    android:shadowDx="0.0"
+    android:shadowDy="1.0"
+    android:shadowRadius="1.0" />
diff --git a/core/res/res/layout/screen.xml b/core/res/res/layout/screen.xml
index dfa9731..72f7392 100644
--- a/core/res/res/layout/screen.xml
+++ b/core/res/res/layout/screen.xml
@@ -25,6 +25,13 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
 >
+    <!-- Popout bar for action modes -->
+    <ViewStub android:id="@+id/action_mode_bar_stub"
+              android:inflatedId="@+id/action_mode_bar"
+              android:layout="@layout/action_mode_bar"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content" />
+
     <!-- Title bar -->
     <RelativeLayout android:id="@android:id/title_container"
         style="?android:attr/windowTitleBackgroundStyle"
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
new file mode 100644
index 0000000..b158414
--- /dev/null
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!--
+This is an optimized layout for a screen with the Action Bar enabled.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:fitsSystemWindows="true">
+    <ViewAnimator android:id="@+id/action_bar_animator"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inAnimation="@anim/push_down_in_no_alpha"
+        android:outAnimation="@anim/push_down_out_no_alpha">
+        <com.android.internal.widget.ActionBarView
+            android:id="@+id/action_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?android:attr/actionBarStyle" />
+        <com.android.internal.widget.ActionBarContextView
+            android:id="@+id/action_context_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?android:attr/actionModeStyle" />
+    </ViewAnimator>
+    <FrameLayout android:id="@android:id/content"
+        android:layout_width="match_parent" 
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:foregroundGravity="fill_horizontal|top"
+        android:foreground="?android:attr/windowContentOverlay" />
+    <LinearLayout android:id="@+id/lower_action_context_bar"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  style="?android:attr/actionBarStyle"
+                  android:visibility="gone" />
+</LinearLayout>
diff --git a/core/res/res/layout/screen_action_bar_overlay.xml b/core/res/res/layout/screen_action_bar_overlay.xml
new file mode 100644
index 0000000..92a1ee0
--- /dev/null
+++ b/core/res/res/layout/screen_action_bar_overlay.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!--
+This is an optimized layout for a screen with
+the Action Bar enabled overlaying application content.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fitsSystemWindows="true">
+    <FrameLayout android:id="@android:id/content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+    <ViewAnimator android:id="@+id/action_bar_animator"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="top"
+        android:inAnimation="@anim/push_down_in_no_alpha"
+        android:outAnimation="@anim/push_down_out_no_alpha">
+        <com.android.internal.widget.ActionBarView
+            android:id="@+id/action_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?android:attr/actionBarStyle" />
+        <com.android.internal.widget.ActionBarContextView
+            android:id="@+id/action_context_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            style="?android:attr/actionModeStyle" />
+    </ViewAnimator>
+    <ImageView android:src="?android:attr/windowContentOverlay"
+               android:scaleType="fitXY"
+               android:layout_width="match_parent"
+               android:layout_height="wrap_content"
+               android:layout_below="@id/action_bar_animator" />
+    <LinearLayout android:id="@+id/lower_action_context_bar"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:layout_gravity="bottom"
+                  style="?android:attr/actionBarStyle"
+                  android:visibility="gone" />
+</RelativeLayout>
diff --git a/core/res/res/layout/screen_custom_title.xml b/core/res/res/layout/screen_custom_title.xml
index 34c9de0..c62a06a 100644
--- a/core/res/res/layout/screen_custom_title.xml
+++ b/core/res/res/layout/screen_custom_title.xml
@@ -21,6 +21,13 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:fitsSystemWindows="true">
+    <!-- Popout bar for action modes -->
+    <ViewStub android:id="@+id/action_mode_bar_stub"
+              android:inflatedId="@+id/action_mode_bar"
+              android:layout="@layout/action_mode_bar"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content" />
+
     <FrameLayout android:id="@android:id/title_container" 
         android:layout_width="match_parent" 
         android:layout_height="?android:attr/windowTitleSize"
diff --git a/core/res/res/layout/screen_progress.xml b/core/res/res/layout/screen_progress.xml
index 6e694c1..c3f41fa 100644
--- a/core/res/res/layout/screen_progress.xml
+++ b/core/res/res/layout/screen_progress.xml
@@ -26,6 +26,13 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
 >
+    <!-- Popout bar for action modes -->
+    <ViewStub android:id="@+id/action_mode_bar_stub"
+              android:inflatedId="@+id/action_mode_bar"
+              android:layout="@layout/action_mode_bar"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content" />
+
     <RelativeLayout android:id="@android:id/title_container" 
         style="?android:attr/windowTitleBackgroundStyle"
         android:layout_width="match_parent" 
diff --git a/core/res/res/layout/screen_simple.xml b/core/res/res/layout/screen_simple.xml
index df511c6..87c29f6 100644
--- a/core/res/res/layout/screen_simple.xml
+++ b/core/res/res/layout/screen_simple.xml
@@ -21,9 +21,19 @@
 enabled.
 -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/content"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:fitsSystemWindows="true"
-    android:foregroundInsidePadding="false"
-    android:foregroundGravity="fill_horizontal|top"
-    android:foreground="?android:attr/windowContentOverlay" />
\ No newline at end of file
+    android:orientation="vertical">
+    <ViewStub android:id="@+id/action_mode_bar_stub"
+              android:inflatedId="@+id/action_mode_bar"
+              android:layout="@layout/action_mode_bar"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content" />
+    <FrameLayout
+         android:id="@android:id/content"
+         android:layout_width="match_parent"
+         android:layout_height="match_parent"
+         android:foregroundInsidePadding="false"
+         android:foregroundGravity="fill_horizontal|top"
+         android:foreground="?android:attr/windowContentOverlay" />
+</LinearLayout>
diff --git a/core/res/res/layout/screen_title.xml b/core/res/res/layout/screen_title.xml
index 39ea131..f5134f9 100644
--- a/core/res/res/layout/screen_title.xml
+++ b/core/res/res/layout/screen_title.xml
@@ -22,6 +22,12 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:fitsSystemWindows="true">
+    <!-- Popout bar for action modes -->
+    <ViewStub android:id="@+id/action_mode_bar_stub"
+              android:inflatedId="@+id/action_mode_bar"
+              android:layout="@layout/action_mode_bar"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content" />
     <FrameLayout
         android:layout_width="match_parent" 
         android:layout_height="?android:attr/windowTitleSize"
diff --git a/core/res/res/layout/screen_title_icons.xml b/core/res/res/layout/screen_title_icons.xml
index 62a0228..51d6a15 100644
--- a/core/res/res/layout/screen_title_icons.xml
+++ b/core/res/res/layout/screen_title_icons.xml
@@ -23,6 +23,12 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
+    <!-- Popout bar for action modes -->
+    <ViewStub android:id="@+id/action_mode_bar_stub"
+              android:inflatedId="@+id/action_mode_bar"
+              android:layout="@layout/action_mode_bar"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content" />
     <RelativeLayout android:id="@android:id/title_container"
         style="?android:attr/windowTitleBackgroundStyle"
         android:layout_width="match_parent"
diff --git a/core/res/res/layout/search_dropdown_item_icons_2line.xml b/core/res/res/layout/search_dropdown_item_icons_2line.xml
index d71b4f7..fcdf91b 100644
--- a/core/res/res/layout/search_dropdown_item_icons_2line.xml
+++ b/core/res/res/layout/search_dropdown_item_icons_2line.xml
@@ -1,31 +1,31 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* 
+/*
 **
 ** Copyright 2008, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 -->
 
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:paddingLeft="4dip"
     android:paddingRight="2dip"
     android:layout_width="match_parent"
     android:layout_height="?android:attr/searchResultListItemHeight" >
 
     <!-- Icons come first in the layout, since their placement doesn't depend on
-         the placement of the text views. -->    
+         the placement of the text views. -->
     <ImageView android:id="@android:id/icon1"
         android:layout_width="48dip"
         android:layout_height="48dip"
@@ -35,31 +35,44 @@
         android:layout_alignParentBottom="true"
         android:visibility="gone" />
 
-    <ImageView android:id="@android:id/icon2"
+    <ImageView android:id="@+id/edit_query"
         android:layout_width="48dip"
         android:layout_height="48dip"
         android:scaleType="centerInside"
         android:layout_alignParentRight="true"
         android:layout_alignParentTop="true"
         android:layout_alignParentBottom="true"
+        android:src="@android:drawable/edit_query"
+        android:background="@android:drawable/edit_query_background"
         android:visibility="gone" />
 
+    <ImageView android:id="@android:id/icon2"
+        android:layout_width="48dip"
+        android:layout_height="48dip"
+        android:scaleType="centerInside"
+        android:layout_alignWithParentIfMissing="true"
+        android:layout_toLeftOf="@id/edit_query"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentBottom="true"
+        android:visibility="gone" />
+
+
     <!-- The subtitle comes before the title, since the height of the title depends on whether the
-         subtitle is visible or gone. --> 
+         subtitle is visible or gone. -->
     <TextView android:id="@android:id/text2"
         style="?android:attr/dropDownItemStyle"
         android:textAppearance="?android:attr/textAppearanceSearchResultSubtitle"
         android:singleLine="true"
         android:layout_width="match_parent"
-        android:layout_height="29dip" 
+        android:layout_height="29dip"
         android:paddingBottom="4dip"
         android:gravity="top"
         android:layout_toRightOf="@android:id/icon1"
         android:layout_toLeftOf="@android:id/icon2"
-        android:layout_alignWithParentIfMissing="true" 
+        android:layout_alignWithParentIfMissing="true"
         android:layout_alignParentBottom="true"
         android:visibility="gone" />
-    
+
     <!-- The title is placed above the subtitle, if there is one. If there is no
          subtitle, it fills the parent. -->
     <TextView android:id="@android:id/text1"
@@ -72,5 +85,5 @@
         android:layout_toRightOf="@android:id/icon1"
         android:layout_toLeftOf="@android:id/icon2"
         android:layout_above="@android:id/text2" />
-    
+
 </RelativeLayout>
diff --git a/core/res/res/layout/search_view.xml b/core/res/res/layout/search_view.xml
new file mode 100644
index 0000000..0fd9a77
--- /dev/null
+++ b/core/res/res/layout/search_view.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/search_bar"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    >
+
+    <!-- This is actually used for the badge icon *or* the badge label (or neither) -->
+    <TextView
+        android:id="@+id/search_badge"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_vertical"
+        android:layout_marginBottom="2dip"
+        android:drawablePadding="0dip"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?android:attr/textColorPrimaryInverse"
+        android:visibility="gone"
+    />
+
+    <ImageView
+        android:id="@+id/search_button"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:src="@android:drawable/ic_btn_search"
+    />
+
+    <LinearLayout
+        android:id="@+id/search_edit_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:layout_gravity="center_vertical"
+        android:orientation="horizontal">
+
+        <!-- Inner layout contains the app icon, button(s) and EditText -->
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_gravity="center_vertical"
+            android:orientation="horizontal"
+            android:background="?android:attr/editTextBackground">
+
+            <ImageView
+                android:id="@+id/search_app_icon"
+                android:layout_height="24dip"
+                android:layout_width="24dip"
+                android:layout_marginRight="7dip"
+                android:layout_gravity="bottom"
+                android:src="@android:drawable/ic_btn_search"
+            />
+
+            <AutoCompleteTextView
+                android:id="@+id/search_src_text"
+                android:layout_height="wrap_content"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:minWidth="100dp"
+                android:layout_gravity="bottom"
+                android:paddingLeft="8dip"
+                android:paddingRight="6dip"
+                android:drawablePadding="2dip"
+                android:singleLine="true"
+                android:ellipsize="end"
+                android:background="@null"
+                android:inputType="text|textAutoComplete"
+                android:imeOptions="actionSearch"
+                android:dropDownHeight="wrap_content"
+                android:dropDownAnchor="@id/search_edit_frame"
+                android:dropDownVerticalOffset="0dip"
+                android:dropDownHorizontalOffset="0dip"
+                android:popupBackground="?android:attr/searchDropdownBackground"
+            />
+
+            <!-- TODO: Use the generic dialog close drawable -->
+            <ImageView
+                android:id="@+id/search_close_btn"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_gravity="bottom"
+                android:src="@android:drawable/btn_close"
+            />
+
+        </LinearLayout>
+
+        <ImageView
+            android:id="@+id/search_go_btn"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:layout_marginLeft="4dip"
+            android:layout_marginRight="4dip"
+            android:src="@android:drawable/ic_btn_find_next"
+        />
+    </LinearLayout>
+
+    <ImageButton
+        android:id="@+id/search_voice_btn"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_vertical"
+        android:layout_marginLeft="4dip"
+        android:layout_marginRight="4dip"
+        android:src="@android:drawable/ic_btn_speak_now"
+        android:background="@android:color/transparent"
+        android:visibility="gone"
+    />
+</LinearLayout>
diff --git a/core/res/res/layout/select_dialog.xml b/core/res/res/layout/select_dialog.xml
index a1a3e76..94dcb6a 100644
--- a/core/res/res/layout/select_dialog.xml
+++ b/core/res/res/layout/select_dialog.xml
@@ -30,6 +30,5 @@
     android:layout_height="match_parent"
     android:layout_marginTop="5px"
     android:cacheColorHint="@null"
-    android:divider="@android:drawable/divider_horizontal_bright"
-    android:scrollbars="vertical"
-    android:overScrollMode="ifContentScrolls" />
+    android:divider="?android:attr/listDividerAlertDialog"
+    android:scrollbars="vertical" />
diff --git a/core/res/res/layout/select_dialog_item.xml b/core/res/res/layout/select_dialog_item.xml
index 30fe02e..96fdcc6 100644
--- a/core/res/res/layout/select_dialog_item.xml
+++ b/core/res/res/layout/select_dialog_item.xml
@@ -29,7 +29,7 @@
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:textAppearance="?android:attr/textAppearanceLarge"
-    android:textColor="@android:color/primary_text_light_disable_only"
+    android:textColor="?android:attr/textColorAlertDialogListItem"
     android:gravity="center_vertical"
     android:paddingLeft="14dip"
     android:paddingRight="15dip"
diff --git a/core/res/res/layout/select_dialog_multichoice.xml b/core/res/res/layout/select_dialog_multichoice.xml
index 5785d3b..a9be014 100644
--- a/core/res/res/layout/select_dialog_multichoice.xml
+++ b/core/res/res/layout/select_dialog_multichoice.xml
@@ -20,11 +20,11 @@
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:textAppearance="?android:attr/textAppearanceLarge"
-    android:textColor="@android:color/primary_text_light_disable_only"
+    android:textColor="?android:attr/textColorAlertDialogListItem"
     android:gravity="center_vertical"
     android:paddingLeft="12dip"
     android:paddingRight="7dip"
-    android:checkMark="@android:drawable/btn_check"
+    android:checkMark="?android:attr/listChoiceIndicatorMultiple"
     android:ellipsize="marquee"
 />
 
diff --git a/core/res/res/layout/select_dialog_singlechoice.xml b/core/res/res/layout/select_dialog_singlechoice.xml
index 3560fee..1b9c973 100644
--- a/core/res/res/layout/select_dialog_singlechoice.xml
+++ b/core/res/res/layout/select_dialog_singlechoice.xml
@@ -20,10 +20,10 @@
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:textAppearance="?android:attr/textAppearanceLarge"
-    android:textColor="@android:color/primary_text_light_disable_only"
+    android:textColor="?android:attr/textColorAlertDialogListItem"
     android:gravity="center_vertical"
     android:paddingLeft="12dip"
     android:paddingRight="7dip"
-    android:checkMark="@android:drawable/btn_radio"
+    android:checkMark="?android:attr/listChoiceIndicatorSingle"
     android:ellipsize="marquee"
 />
diff --git a/core/res/res/layout/simple_list_item_2.xml b/core/res/res/layout/simple_list_item_2.xml
index 5eab54e..c87922c 100644
--- a/core/res/res/layout/simple_list_item_2.xml
+++ b/core/res/res/layout/simple_list_item_2.xml
@@ -15,8 +15,6 @@
 -->
 
 <TwoLineListItem xmlns:android="http://schemas.android.com/apk/res/android" 
-	android:paddingTop="2dip"
-	android:paddingBottom="2dip"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
diff --git a/core/res/res/layout/simple_list_item_activated_1.xml b/core/res/res/layout/simple_list_item_activated_1.xml
new file mode 100644
index 0000000..8416df2
--- /dev/null
+++ b/core/res/res/layout/simple_list_item_activated_1.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceLarge"
+    android:gravity="center_vertical"
+    android:background="?android:attr/activatedBackgroundIndicator"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+/>
diff --git a/core/res/res/layout/simple_list_item_activated_2.xml b/core/res/res/layout/simple_list_item_activated_2.xml
new file mode 100644
index 0000000..2ffbf02
--- /dev/null
+++ b/core/res/res/layout/simple_list_item_activated_2.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<TwoLineListItem xmlns:android="http://schemas.android.com/apk/res/android"
+    android:paddingTop="2dip"
+    android:paddingBottom="2dip"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/activatedBackgroundIndicator"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:mode="twoLine"
+>
+
+    <TextView android:id="@android:id/text1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="6dip"
+        android:layout_marginTop="6dip"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+    />
+
+    <TextView android:id="@android:id/text2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@android:id/text1"
+        android:layout_alignLeft="@android:id/text1"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+    />
+
+</TwoLineListItem>
diff --git a/core/res/res/layout/simple_selectable_list_item.xml b/core/res/res/layout/simple_selectable_list_item.xml
new file mode 100644
index 0000000..518bcd0
--- /dev/null
+++ b/core/res/res/layout/simple_selectable_list_item.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="match_parent"
+    android:layout_height="?android:attr/listPreferredItemHeight"
+    android:textAppearance="?android:attr/textAppearanceLarge"
+    android:gravity="center_vertical"
+    android:background="?android:attr/listChoiceBackgroundIndicator"
+    android:paddingLeft="6dip"
+    android:paddingRight="9dip"
+/>
diff --git a/core/res/res/layout/tab_indicator.xml b/core/res/res/layout/tab_indicator.xml
index 71e4001..bc657c3 100644
--- a/core/res/res/layout/tab_indicator.xml
+++ b/core/res/res/layout/tab_indicator.xml
@@ -20,7 +20,6 @@
     android:layout_weight="1"
     android:layout_marginLeft="-3dip"
     android:layout_marginRight="-3dip"
-    android:orientation="vertical"
     android:background="@android:drawable/tab_indicator">
 
     <ImageView android:id="@+id/icon"
diff --git a/core/res/res/layout/tab_indicator_holo.xml b/core/res/res/layout/tab_indicator_holo.xml
new file mode 100644
index 0000000..d37476b
--- /dev/null
+++ b/core/res/res/layout/tab_indicator_holo.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="56dip"
+    android:layout_weight="0"
+    android:layout_marginLeft="0dip"
+    android:layout_marginRight="0dip"
+    android:background="@android:drawable/tab_indicator_holo">
+
+    <View android:id="@+id/tab_indicator_left_spacer"
+        android:layout_width="16dip"
+        android:layout_height="0dip" />
+
+    <ImageView android:id="@+id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:visibility="gone"
+        android:layout_toRightOf="@id/tab_indicator_left_spacer"
+        android:paddingRight="8dip" />
+
+    <TextView android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_toRightOf="@id/icon"
+        android:paddingLeft="0dip"
+        android:paddingRight="16dip"
+        style="?android:attr/tabWidgetStyle" />
+
+</RelativeLayout>
diff --git a/core/res/res/layout/text_edit_no_paste_window.xml b/core/res/res/layout/text_edit_no_paste_window.xml
new file mode 100644
index 0000000..d409d97
--- /dev/null
+++ b/core/res/res/layout/text_edit_no_paste_window.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@android:drawable/text_edit_paste_window">
+
+    <ImageView android:id="@+id/paste_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingLeft="16dip"
+        android:paddingRight="8dip"
+        android:paddingTop="8dip"
+        android:paddingBottom="8dip"
+        android:layout_centerVertical="true"
+        android:background="@android:drawable/ic_menu_paste_dark"
+    />
+
+    <TextView android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingLeft="0dip"
+        android:paddingRight="16dip"
+        android:paddingTop="8dip"
+        android:paddingBottom="8dip"
+        android:layout_centerVertical="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="@android:string/pasteDisabled"
+        android:textColor="@android:color/dim_foreground_dark_inverse_disabled"
+        android:layout_toRightOf="@id/paste_icon"
+    />
+
+</RelativeLayout>
diff --git a/core/res/res/layout/text_edit_paste_window.xml b/core/res/res/layout/text_edit_paste_window.xml
new file mode 100644
index 0000000..d153365
--- /dev/null
+++ b/core/res/res/layout/text_edit_paste_window.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@android:drawable/text_edit_paste_window">
+
+    <ImageView android:id="@+id/paste_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingLeft="16dip"
+        android:paddingRight="8dip"
+        android:paddingTop="8dip"
+        android:paddingBottom="8dip"
+        android:layout_centerVertical="true"
+        android:background="@android:drawable/ic_menu_paste_light"
+    />
+
+    <TextView android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingLeft="0dip"
+        android:paddingRight="16dip"
+        android:paddingTop="8dip"
+        android:paddingBottom="8dip"
+        android:layout_centerVertical="true"
+        android:textAppearance="?android:attr/textAppearanceMediumInverse"
+        android:text="@android:string/paste"
+        android:textColor="@android:color/black"
+        android:layout_toRightOf="@id/paste_icon"
+    />
+
+</RelativeLayout>
diff --git a/core/res/res/layout/web_runtime.xml b/core/res/res/layout/web_runtime.xml
new file mode 100644
index 0000000..4ec0964
--- /dev/null
+++ b/core/res/res/layout/web_runtime.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+    <WebView
+        android:id="@+id/webview"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+    />
+
+    <ImageView
+        android:id="@+id/splashscreen"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:scaleType="fitStart"
+    />
+
+</FrameLayout>
+
diff --git a/core/res/res/layout/webview_find.xml b/core/res/res/layout/webview_find.xml
new file mode 100644
index 0000000..4cf2df5
--- /dev/null
+++ b/core/res/res/layout/webview_find.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    >
+    <EditText android:id="@+id/edit"
+        android:layout_width="0dip"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:scrollHorizontally="true"
+        android:inputType="text"
+        android:hint="@string/find_on_page"
+        android:imeOptions="actionDone"
+        android:layout_marginRight="10dip"
+        />
+    <TextView android:id="@+id/matches"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?android:attr/textColorPrimary"
+        />
+</LinearLayout>
diff --git a/core/res/res/menu/webview_copy.xml b/core/res/res/menu/webview_copy.xml
new file mode 100644
index 0000000..b28c1c1
--- /dev/null
+++ b/core/res/res/menu/webview_copy.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/copy"
+        android:icon="?android:attr/actionModeCopyDrawable"
+        android:title="@string/copy"
+        android:showAsAction="always|withText"
+        />
+    <item android:id="@+id/share"
+        android:icon="@drawable/ic_menu_share"
+        android:title="@string/share"
+        android:showAsAction="always|withText"
+        />
+    <item android:id="@+id/select_all"
+        android:title="@string/selectAll"
+        android:showAsAction="always|withText"
+        />
+    <item android:id="@+id/find"
+        android:icon="@drawable/ic_menu_find"
+        android:title="@string/find"
+        android:showAsAction="always|withText"
+        />
+</menu>
+
diff --git a/core/res/res/menu/webview_find.xml b/core/res/res/menu/webview_find.xml
new file mode 100644
index 0000000..74a40aa
--- /dev/null
+++ b/core/res/res/menu/webview_find.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/find_prev"
+        android:icon="@drawable/ic_btn_find_prev"
+        android:showAsAction="always"
+        />
+    <item android:id="@+id/find_next"
+        android:icon="@drawable/ic_btn_find_next"
+        android:showAsAction="always"
+        />
+</menu>
diff --git a/core/res/res/raw-rm/loaderror.html b/core/res/res/raw-rm/loaderror.html
new file mode 100644
index 0000000..8e4a3fe
--- /dev/null
+++ b/core/res/res/raw-rm/loaderror.html
@@ -0,0 +1,18 @@
+<html>
+    <head>
+        <title>Pagina d'internet betg disponibla</title>
+        <style type="text/css">
+            body { margin-top: 0px; padding-top: 0px; }
+            h2   { margin-top: 5px; padding-top: 0px; }
+        </style>
+
+        <body>
+
+            <img src="file:///android_asset/webkit/android-weberror.png" align="top" />
+            <h2>Pagina d'internet betg disponibla</h2>
+            <p>Impussibel da chargiar la pagina d'internet sut <a href="%s">%s</a>. Errur:</p>
+            <!-- The %e is replaced by a localized error string -->
+            <p>%e</p>
+        </body>
+    </head>
+</html>
diff --git a/core/res/res/raw-rm/nodomain.html b/core/res/res/raw-rm/nodomain.html
new file mode 100644
index 0000000..1e2833b
--- /dev/null
+++ b/core/res/res/raw-rm/nodomain.html
@@ -0,0 +1,23 @@
+<html>
+    <head>
+        <title>Pagina d'internet betg disponibla</title>
+        <style type="text/css">
+            body { margin-top: 0px; padding-top: 0px; }
+            h2   { margin-top: 5px; padding-top: 0px; }
+        </style>
+
+        <body>
+
+            <img src="file:///android_asset/webkit/android-weberror.png" align="top" />
+            <h2>Pagina d'internet betg disponibla</h2>
+            <p>La pagina d'internet sut <a href="%s">%s</a> &egrave; eventualmain deactivada temporarmain u spustada definitivamain ad ina nova adressa da web.</p>
+
+            <p><b>Qua in p&egrave;r propostas:</b></p>
+            <ul>
+                <li>Controllai che Voss apparat recepescha in signal e dispona d'ina connexiun da datas.</li>
+                <li>Chargiai pli tard danovamain la pagina d'internet.</li>
+                <li>Chargiai ina copia da la pagina da web memorisada en il cache da Google.</li>
+            </ul>
+        </body>
+    </head>
+</html>
diff --git a/core/res/res/raw/loaderror.html b/core/res/res/raw/loaderror.html
index fd3d766..565e8e9 100644
--- a/core/res/res/raw/loaderror.html
+++ b/core/res/res/raw/loaderror.html
@@ -1,7 +1,7 @@
 <html>
     <head>
         <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
-        <title>Web page not available</title>
+        <title>Webpage not available</title>
         <style type="text/css">
             body { margin-top: 0px; padding-top: 0px; }
             h2   { margin-top: 5px; padding-top: 0px; }
@@ -10,8 +10,8 @@
         <body>
 
             <img src="file:///android_asset/webkit/android-weberror.png" align="top" />
-            <h2>Web page not available</h2>
-            <p>The Web page at <a href="%s">%s</a> could not be loaded as:</p>
+            <h2>Webpage not available</h2>
+            <p>The webpage at <a href="%s">%s</a> could not be loaded because:</p>
             <!-- The %e is replaced by a localized error string -->
             <p>%e</p>
         </body>
diff --git a/core/res/res/raw/nodomain.html b/core/res/res/raw/nodomain.html
index a71dbcd..d757483 100644
--- a/core/res/res/raw/nodomain.html
+++ b/core/res/res/raw/nodomain.html
@@ -1,7 +1,7 @@
 <html>
     <head>
         <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
-        <title>Web page not available</title>
+        <title>Webpage not available</title>
         <style type="text/css">
             body { margin-top: 0px; padding-top: 0px; }
             h2   { margin-top: 5px; padding-top: 0px; }
@@ -10,18 +10,16 @@
         <body>
 
             <img src="file:///android_asset/webkit/android-weberror.png" align="top" />
-            <h2>Web page not available</h2>
-            <p>The Web page at <a href="%s">%s</a> might be
+            <h2>Webpage not available</h2>
+            <p>The webpage at <a href="%s">%s</a> might be
             temporarily down or it may have moved permanently to a new web
             address.</p>
 
-            <p><b>Here are some suggestions:</b></p>
+            <p><b>Suggestions:</b></p>
             <ul>
-                <li>Check to make sure your device has a signal and data
-                connection</li>
-                <li>Reload this web page later.</li>
-                <li>View a cached copy of the web page from Google</li>
-
+                <li>Make sure you have a data connection</li>
+                <li>Reload this webpage later</li>
+                <li>Check the address you entered</li>
             </ul>
         </body>
     </head>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 0e57306..8399db0 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Vypnout"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Vypínání..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Váš telefon bude vypnut."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Nejnovější"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Žádné nedávno použité aplikace."</string>
     <string name="global_actions" msgid="2406416831541615258">"Možnosti telefonu"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Řídit, jak a kdy se obrazovka uzamkne"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Vymazání všech dat"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Bez upozornění smazat všechna data telefonu obnovením továrních dat"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Nastavit globální proxy server zařízení"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Vyberte globální proxy server, který se bude používat, když jsou zásady aktivní. Aktuální globální proxy server nastavuje pouze první správce zařízení."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Domů"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Plocha"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Práce"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Jiné"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"pomocí <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> pomocí <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Zadejte kód PIN"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Zadejte heslo pro odblokování"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Zadejte kód PIN pro odblokování"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Nesprávný kód PIN"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Chcete-li telefon odemknout, stiskněte Menu a poté 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Číslo tísňové linky"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Zavolat zpět"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Správně!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Zkuste to prosím znovu"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Zkuste to prosím znovu"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Nabíjení (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Nabito."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Karta SIM je zablokována."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Odblokování karty SIM..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"<xliff:g id="NUMBER_0">%d</xliff:g>krát jste nakreslili nesprávné bezpečnostní gesto. "\n\n"Opakujte prosím akci za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Vícekrát (<xliff:g id="NUMBER_0">%d</xliff:g>) jste nesprávně zadali heslo. "\n\n"Zkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Vícekrát (<xliff:g id="NUMBER_0">%d</xliff:g>) jste nesprávně zadali kód PIN. "\n\n"Zkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"<xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně nakreslili své bezpečnostní gesto. Po dalších neúspěšných pokusech (<xliff:g id="NUMBER_1">%d</xliff:g>) budete požádáni o odemčení telefonu pomocí přihlášení do účtu Google."\n\n" Akci prosím opakujte za několik sekund (<xliff:g id="NUMBER_2">%d</xliff:g>)."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Sekundy zbývající do dalšího pokusu: <xliff:g id="NUMBER">%d</xliff:g>."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Zapomněli jste gesto?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Chcete opustit tuto stránku?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Vyberte OK, chcete-li pokračovat, nebo Zrušit, chcete-li na stránce zůstat."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Potvrdit"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Tip: Dvojitým klepnutím můžete zobrazení přiblížit nebo oddálit."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Automaticky vyplnit tento formulář"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"čtení historie a záložek Prohlížeče"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Umožňuje aplikaci číst všechny navštívené adresy URL a záložky Prohlížeče."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"zápis do historie a záložek Prohlížeče"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Vybrat vše"</string>
-    <string name="selectText" msgid="4862359311088898878">"Vybrat slovo"</string>
     <string name="cut" msgid="3092569408438626261">"Vyjmout"</string>
     <string name="copy" msgid="2681946229533511987">"Kopírovat"</string>
     <string name="paste" msgid="5629880836805036433">"Vložit"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Kopírovat adresu URL"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Vybrat text..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Výběr textu"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Metoda zadávání dat"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Přidat slovo „<xliff:g id="WORD">%s</xliff:g>“ do slovníku"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Úpravy textu"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Operace s textem"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Málo paměti"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"V telefonu zbývá málo místa pro ukládání dat."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Zrušit"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Upozornění"</string>
+    <string name="loading" msgid="1760724998928255250">"Načítání..."</string>
     <string name="capital_on" msgid="1544682755514494298">"ZAPNUTO"</string>
     <string name="capital_off" msgid="6815870386972805832">"VYPNOUT"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Dokončit akci pomocí aplikace"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"Síť VPN L2TP/IPSec s předsdíleným klíčem"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"Síť VPN L2TP/IPSec s certifikátem"</string>
     <string name="upload_file" msgid="2897957172366730416">"Zvolit soubor"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Není vybrán žádný soubor"</string>
     <string name="reset" msgid="2448168080964209908">"Resetovat"</string>
     <string name="submit" msgid="1602335572089911941">"Odeslat"</string>
-    <string name="description_star" msgid="2654319874908576133">"oblíbené"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Aktivován režim V autě"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Vyberte, chcete-li ukončit režim Na cestě."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Je aktivní tethering nebo hotspot"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Dotykem zahájíte konfiguraci"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Zpět"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Další"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Přeskočit"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Vysoké využití mobilních dat"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Dotykem zobrazíte další informace o využití mobilních dat"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Byl překročen limit mobilních dat"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Dotykem zobrazíte další informace o využití mobilních dat"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Žádné shody"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Vyhledat na stránce"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 shoda"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> z <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"Odpojování úložiště USB..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"Odpojování karty SD..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"Mazání úložiště USB..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"Úložiště USB je momentálně používáno počítačem."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"Karta SD je momentálně používána počítačem."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Neznámý stav externího média."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index a7ed720..a3efb4a 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Sluk"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Lukker ned ..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Din telefon slukkes nu."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Seneste"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Der er ingen nye programmer."</string>
     <string name="global_actions" msgid="2406416831541615258">"Indstillinger for telefon"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Bestem, hvordan og hvornår skærmen låses"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Slet alle data"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Slet telefonens data uden varsel ved at gendanne fabriksindstillinger"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Angiv enhedens globale proxy"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Angiv enhedens globale proxy, der skal bruges, mens politikken er aktiveret. Kun den første enhedsadministrator angiver den effektive globale proxy."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Hjem"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Hjem"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Arbejde"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Andre"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Indtast PIN-kode"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Indtast adgangskode for at låse op"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Indtast pinkode for at låse op"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Forkert PIN-kode!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Tryk på Menu og dernæst på 0 for at låse op."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nødnummer"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Tilbage til opkald"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Rigtigt!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Beklager! Prøv igen"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Beklager! Prøv igen"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Oplader (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Opladt."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kortet er låst."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Låser SIM-kortet op ..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. "\n\n"Prøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Du har indtastet din adgangskode forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. "\n\n"Prøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Du har indtastet din pinkode forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. "\n\n"Prøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> forsøg mere vil du blive bedt om at låse din telefon op ved hjælp af dit Google-login"\n\n" Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Prøv igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Har du glemt mønstret?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Naviger væk fra denne side?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n" Vælg OK for at fortsætte eller Annuller for at blive på den aktuelle side."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Bekræft"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Tip: Dobbeltklik for at zoome ind eller ud."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Autofuldfør denne formular"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"læs browserens oversigt og bogmærker"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Tillader, at programmet læser alle de webadresser, browseren har besøgt, og alle browserens bogmærker."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriv browserens oversigt og bogmærker"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Vælg alle"</string>
-    <string name="selectText" msgid="4862359311088898878">"Vælg ord"</string>
     <string name="cut" msgid="3092569408438626261">"Klip"</string>
     <string name="copy" msgid="2681946229533511987">"Kopier"</string>
     <string name="paste" msgid="5629880836805036433">"Indsæt"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Kopier webadresse"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Marker tekst..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Tekstmarkering"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Inputmetode"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Føj \"<xliff:g id="WORD">%s</xliff:g>\" til ordbog"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Rediger tekst"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Der er ikke så meget plads tilbage"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"Der er næsten ikke mere plads på telefonen."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Annuller"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Bemærk"</string>
+    <string name="loading" msgid="1760724998928255250">"Indlæser..."</string>
     <string name="capital_on" msgid="1544682755514494298">"TIL"</string>
     <string name="capital_off" msgid="6815870386972805832">"FRA"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Fuldfør handling ved hjælp af"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"L2TP/IPSec VPN baseret på forhåndsdelt nøglekodning"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"Certifikatbaseret L2TP/IPSec VPN"</string>
     <string name="upload_file" msgid="2897957172366730416">"Vælg fil"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Ingen fil er valgt"</string>
     <string name="reset" msgid="2448168080964209908">"Nulstil"</string>
     <string name="submit" msgid="1602335572089911941">"Send"</string>
-    <string name="description_star" msgid="2654319874908576133">"favorit"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Biltilstand er aktiveret"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Vælg for at afslutte biltilstand."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering eller hotspot er aktivt"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Tryk for at konfigurere"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Tilbage"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Næste"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Spring over"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Højt mobildataforbrug"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Tryk for oplysninger om brug af mobildata"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Grænsen for mobildata er overskredet"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Tryk for oplysninger om brug af mobildata"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Der er ingen matches"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Find på siden"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 match"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> af <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"Demonterer USB-lager..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"Demonterer SD-kort..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"Sletter USB-lager..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"USB-lager er i øjeblikket i brug af en computer."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"SD-kortet er i øjeblikket i brug af en computer."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Eksternt medie i ukendt tilstand."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 11c5e75..6aeb1cd 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Ausschalten"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Wird heruntergefahren..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Ihr Telefon wird heruntergefahren."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Zuletzt verwendet"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Keine zuletzt verwendeten Anwendungen"</string>
     <string name="global_actions" msgid="2406416831541615258">"Telefonoptionen"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Steuern Sie, wie und wann das Display gesperrt wird."</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Alle Daten löschen"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Auf Werkseinstellungen zurücksetzen und Daten auf dem Telefon ohne Warnung löschen"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Den globalen Proxy des Geräts festlegen"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Den globalen Proxy des Geräts zur Verwendung während der Aktivierung der Richtlinie festlegen. Nur der erste Geräteadministrator kann den gültigen globalen Proxy festlegen."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Privat"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Privat"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Geschäftlich"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Sonstige"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"über <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> über <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN-Code eingeben"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Passwort zum Entsperren eingeben"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"PIN zum Entsperren eingeben"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Falscher PIN-Code!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Drücken Sie zum Entsperren die Menütaste und dann auf \"0\"."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Notrufnummer"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Zurück zum Anruf"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Korrekt!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Bitte versuchen Sie es erneut."</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Bitte versuchen Sie es erneut."</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Wird geladen (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Aufgeladen"</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Bitte PIN-Code eingeben"</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM-Karte wird entsperrt..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. "\n\n"Versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%d</xliff:g> Mal falsch eingegeben. "\n\n"Versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Sie haben Ihre PIN <xliff:g id="NUMBER_0">%d</xliff:g> Mal falsch eingegeben. "\n\n"Versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Telefon mithilfe Ihrer Google-Anmeldeinformationen zu entsperren. "\n\n"Versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden erneut."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Versuchen Sie es in <xliff:g id="NUMBER">%d</xliff:g> Sekunden erneut."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Muster vergessen?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Von dieser Seite navigieren?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Wählen Sie \"OK\", um fortzufahren, oder wählen Sie \"Abbrechen\", um auf der aktuellen Seite zu bleiben."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Bestätigen"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Tipp: Zum Heranzoomen und Vergrößern zweimal tippen"</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Dieses Formular automatisch ausfüllen"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"Browserverlauf und Lesezeichen lesen"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Ermöglicht der Anwendung, alle URLs, die mit dem Browser besucht wurden, sowie alle Lesezeichen des Browsers zu lesen."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"Browserverlauf und Lesezeichen schreiben"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Alles auswählen"</string>
-    <string name="selectText" msgid="4862359311088898878">"Wort auswählen"</string>
     <string name="cut" msgid="3092569408438626261">"Ausschneiden"</string>
     <string name="copy" msgid="2681946229533511987">"Kopieren"</string>
     <string name="paste" msgid="5629880836805036433">"Einfügen"</string>
     <string name="copyUrl" msgid="2538211579596067402">"URL kopieren"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Text auswählen..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Textauswahl"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Eingabemethode"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"\"<xliff:g id="WORD">%s</xliff:g>\" zum Wörterbuch hinzufügen"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Text bearbeiten"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Textaktionen"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Geringer Speicher"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"Kaum noch Telefonspeicher frei."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Abbrechen"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Achtung"</string>
+    <string name="loading" msgid="1760724998928255250">"Wird geladen..."</string>
     <string name="capital_on" msgid="1544682755514494298">"EIN"</string>
     <string name="capital_off" msgid="6815870386972805832">"Aus"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Aktion durchführen mit"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"L2TP/IPSec-VPN mit vorinstalliertem Schlüssel"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"Zertifikat mit vorinstalliertem Schlüssel"</string>
     <string name="upload_file" msgid="2897957172366730416">"Datei auswählen"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Keine Datei ausgewählt"</string>
     <string name="reset" msgid="2448168080964209908">"Zurücksetzen"</string>
     <string name="submit" msgid="1602335572089911941">"Senden"</string>
-    <string name="description_star" msgid="2654319874908576133">"Favorit"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Automodus aktiviert"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Zum Beenden des Automodus auswählen"</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering oder Hotspot aktiv"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Zum Konfigurieren berühren"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Zurück"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Weiter"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Überspringen"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Hohe Mobildatennutzung"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Weitere Informationen über die Mobildatennutzung durch Berühren aufrufen"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Mobildatenlimit überschritten"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Weitere Informationen über die Mobildatennutzung durch Berühren aufrufen"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Keine Treffer"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Auf Seite suchen"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 Treffer"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> von <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"USB-Speicher wird getrennt..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"SD-Karte wird getrennt..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"USB-Speicher wird gelöscht..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"Der USB-Speicher wird derzeit von einem Computer verwendet."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"Die SD-Karte wird derzeit von einem Computer verwendet."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Unbekannter Status des externen Speichermediums"</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 4244aa5..c87a7cd 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Απενεργοποίηση"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Απενεργοποίηση..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Το τηλέφωνό σας θα απενεργοποιηθεί."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Πρόσφατα"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Δεν υπάρχουν πρόσφατες εφαρμογές."</string>
     <string name="global_actions" msgid="2406416831541615258">"Επιλογές τηλεφώνου"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Έλεγχος του τρόπου και του χρόνου κλειδώματος της οθόνης"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Διαγραφή όλων των δεδομένων"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Διαγραφή των δεδομένων του τηλεφώνου χωρίς προειδοποίηση με επαναφορά των εργοστασιακών δεδομένων"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ρύθμιση του γενικού διακομιστή μεσολάβησης της συσκευής"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ορίστε τη χρήση του γενικού διακομιστή μεσολάβησης της συσκευής όταν είναι ενεργοποιημένη η πολιτική. Μόνο ο διαχειριστής της πρώτης συσκευής ορίζει τον ισχύοντα γενικό διακομιστή μεσολάβησης."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Οικία"</item>
     <item msgid="869923650527136615">"Κινητό"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Αρχική σελίδα"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Εργασία"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Άλλο"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"μέσω <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> μέσω <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Πληκτρολογήστε τον κωδικό αριθμό PIN"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Εισαγάγετε τον κωδικό πρόσβασης για ξεκλείδωμα"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Εισαγάγετε το PIN για ξεκλείδωμα"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Εσφαλμένος κωδικός αριθμός PIN!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Για ξεκλείδωμα, πατήστε το πλήκτρο Menu και, στη συνέχεια, το πλήκτρο 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Αριθμός έκτακτης ανάγκης"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Επιστροφή στην κλήση"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Σωστό!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Προσπαθήστε αργότερα"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Προσπαθήστε αργότερα"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Φόρτιση (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Φορτίστηκε."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Η κάρτα SIM είναι κλειδωμένη."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Ξεκλείδωμα κάρτας SIM..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος<xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Προσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Καταχωρίσατε εσφαλμένα τον κωδικό πρόσβασης <xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Προσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Καταχωρίσατε εσφαλμένα το PIN σας<xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Προσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση της σύνδεσής σας Google."\n\n" Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> δευτερόλεπτα."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Προσπαθήστε ξανά σε <xliff:g id="NUMBER">%d</xliff:g> δευτερόλεπτα."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Ξεχάσατε το μοτίβο;"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Απομάκρυνση από αυτή τη σελίδα;"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Επιλέξτε OK για συνέχεια, ή Ακύρωση για παραμονή στην τρέχουσα σελίδα."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Επιβεβαίωση"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Συμβουλή: διπλό άγγιγμα για μεγέθυνση και σμίκρυνση."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Να γίνει αυτόματη συμπλήρωση αυτής της φόρμας"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ανάγνωση ιστορικού και σελιδοδεικτών προγράμματος περιήγησης"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Επιτρέπει στην εφαρμογή την ανάγνωση όλων των διευθύνσεων URL που το πρόγραμμα περιήγησης έχει επισκεφθεί και όλων των σελιδοδεικτών του προγράμματος περιήγησης."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"εγγραφή ιστορικού και σελιδοδεικτών προγράμματος περιήγησης"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Επιλογή όλων"</string>
-    <string name="selectText" msgid="4862359311088898878">"Επιλογή λέξης"</string>
     <string name="cut" msgid="3092569408438626261">"Αποκοπή"</string>
     <string name="copy" msgid="2681946229533511987">"Αντιγραφή"</string>
     <string name="paste" msgid="5629880836805036433">"Επικόλληση"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Αντιγραφή διεύθυνσης URL"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Επιλογή κειμένου..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Επιλογή κειμένου"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Μέθοδος εισόδου"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Προσθήκη της λέξης \"<xliff:g id="WORD">%s</xliff:g>\" στο λεξικό"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Επεξεργασία κειμένου"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Ενέργειες κειμένου"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Απομένει λίγος ελεύθερος χώρος"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"Έχει απομείνει λίγος ελεύθερος αποθηκευτικός χώρος στο τηλέφωνο."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Ακύρωση"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Προσοχή"</string>
+    <string name="loading" msgid="1760724998928255250">"Φόρτωση..."</string>
     <string name="capital_on" msgid="1544682755514494298">"Ενεργοποιημένο"</string>
     <string name="capital_off" msgid="6815870386972805832">"Απενεργοποίηση"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Ολοκλήρωση ενέργειας με τη χρήση"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"Κλειδί pre-shared βάσει L2TP/IPSec VPN"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"Πιστοποιητικό βάσει L2TP/IPSec VPN"</string>
     <string name="upload_file" msgid="2897957172366730416">"Επιλογή αρχείου"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Δεν έχει επιλεγεί αρχείο"</string>
     <string name="reset" msgid="2448168080964209908">"Επαναφορά"</string>
     <string name="submit" msgid="1602335572089911941">"Υποβολή"</string>
-    <string name="description_star" msgid="2654319874908576133">"αγαπημένο"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Η λειτουργία αυτοκινήτου είναι ενεργοποιημένη"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Επιλέξτε για έξοδο από τη λειτουργία αυτοκινήτου."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Σύνδεση μέσω κινητής συσκευής ή σημείου πρόσβασης ενεργή"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Αγγίξτε για να γίνει διαμόρφωση"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Πίσω"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Επόμενο"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Παράβλεψη"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Υψηλή χρήση δεδομένων κινητής τηλεφωνίας"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Αγγίξτε για να μάθετε περισσότερα σχετικά με τη χρήση δεδομένων κινητής τηλεφωνίας"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Ξεπεράστηκε το όριο δεδομένων κινητής τηλεφωνίας"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Αγγίξτε για να μάθετε περισσότερα σχετικά με τη χρήση δεδομένων κινητής τηλεφωνίας"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Δεν υπάρχουν αποτελέσματα"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Εύρεση στη σελίδα"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 αποτέλεσμα"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> από <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"Αποπροσάρτηση αποθηκευτικού χώρου USB..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"Αποπροσάρτηση κάρτας SD..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"Διαγραφή αποθηκευτικού χώρου USB..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"Αυτή τη στιγμή ο αποθηκευτικός χώρος USB χρησιμοποιείται από κάποιον υπολογιστή."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"Αυτή τη στιγμή η κάρτα SD χρησιμοποιείται από κάποιον υπολογιστή."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Το εξωτερικό μέσο είναι σε άγνωστη κατάσταση."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index dd3ddff..1610659 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -135,6 +135,7 @@
     <string name="power_off" msgid="4266614107412865048">"Apagar"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Apagando…"</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Tu teléfono se apagará."</string>
+    <string name="shutdown_confirm_question" msgid="6656441286856415014">" Me gusta  Para:"</string>
     <string name="recent_tasks_title" msgid="3691764623638127888">"Reciente"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"No hay aplicaciones recientes."</string>
     <string name="global_actions" msgid="2406416831541615258">"Opciones de teléfono"</string>
@@ -439,6 +440,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Controlar cómo y cuándo se bloquea la pantalla"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Borrar todos los datos"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Borrar los datos del teléfono sin advertencias al restablecer la configuración original"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Configura el proxy global de dispositivo"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Configuración del proxy global de dispositivo que se utilizará mientras se habilita la política. Sólo la primera administración de dispositivo configura el proxy global efectivo."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Casa"</item>
     <item msgid="869923650527136615">"Celular"</item>
@@ -536,10 +539,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Página principal"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Trabajo"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Otro"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"a través de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> a través de <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Ingresar el código de PIN"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Ingresar la contraseña para desbloquear"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Ingresa el PIN para desbloquear"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"¡Código de PIN incorrecto!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, presiona el menú y luego 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergencia"</string>
@@ -552,6 +554,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Regresar a llamada"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Lo sentimos, vuelve a intentarlo"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Lo sentimos, vuelve a intentarlo"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Cargada."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"Segmento <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +569,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"La tarjeta SIM está bloqueada."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Desbloqueando tarjeta SIM…"</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Has extraído incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Has ingresado tu contraseña de manera incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Has ingresado tu PIN de manera incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Has extraído incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos, se te solicitará que desbloquees tu teléfono al iniciar sesión en Google. "\n\n" Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Vuelve a intentarlo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"¿Olvidaste el patrón?"</string>
@@ -594,6 +599,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"¿Deseas salir de esta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Selecciona Aceptar para continuar o Cancelar para permanecer en la página actual."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Sugerencia: presiona dos veces para acercar y alejar"</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Autocompletar este formulario"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"leer historial y marcadores del navegador"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite a la aplicación leer todas las URL que ha visitado el navegador y todos los marcadores del navegador."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"escribir historial y marcadores del navegador"</string>
@@ -712,14 +718,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Seleccionar todos"</string>
-    <string name="selectText" msgid="4862359311088898878">"Seleccionar palabra"</string>
     <string name="cut" msgid="3092569408438626261">"Cortar"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
     <string name="paste" msgid="5629880836805036433">"Pegar"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copiar URL"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Seleccionar texto..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Selección de texto"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de entrada"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Agregar \"<xliff:g id="WORD">%s</xliff:g>\" al diccionario"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Editar texto"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Poco espacio de almacenamiento"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"Hay poco espacio de almacenamiento en el teléfono."</string>
     <string name="ok" msgid="5970060430562524910">"Aceptar"</string>
@@ -727,6 +733,7 @@
     <string name="yes" msgid="5362982303337969312">"Aceptar"</string>
     <string name="no" msgid="5141531044935541497">"Cancelar"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Atención"</string>
+    <string name="loading" msgid="1760724998928255250">"Cargando..."</string>
     <string name="capital_on" msgid="1544682755514494298">"Encendido"</string>
     <string name="capital_off" msgid="6815870386972805832">"APAGADO"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Completar la acción mediante"</string>
@@ -880,17 +887,27 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"Clave previamente compartida según L2TP/IPSec VPN"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"Certificado según L2TP/IPSec VPN"</string>
     <string name="upload_file" msgid="2897957172366730416">"Elegir archivo"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"No se seleccionó un archivo."</string>
     <string name="reset" msgid="2448168080964209908">"Restablecer"</string>
     <string name="submit" msgid="1602335572089911941">"Enviar"</string>
-    <string name="description_star" msgid="2654319874908576133">"favorito"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Modo auto habilitado"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Seleccionar para salir del modo auto"</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Anclaje a red o zona activa conectados"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Tocar para configurar"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Atrás"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Siguiente"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Omitir"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Amplia utilización de datos móviles"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Toca para obtener más información acerca de la utilización de datos móviles."</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Límite de datos móviles excedido"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Toca para obtener más información acerca de la utilización de datos móviles."</string>
+    <string name="no_matches" msgid="8129421908915840737">"Sin coincidencias"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Buscar en la página"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 coincidencia"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <string name="action_mode_done" msgid="7217581640461922289">"Finalizado"</string>
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"Desmontando almacenamiento USB..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"Desmontando la tarjeta SD..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"Borrando almacenamiento USB..."</string>
@@ -904,4 +921,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"Una computadora está utilizando el almacenamiento USB en este momento."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"Una computadora está utilizando la tarjeta SD en este momento."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Medios externos en estado desconocido."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 7485791..d4163fd 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Apagar"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Apagando..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"El teléfono se apagará."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Reciente"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"No hay aplicaciones recientes"</string>
     <string name="global_actions" msgid="2406416831541615258">"Opciones del teléfono"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Control de la forma en que se bloquea la pantalla y del momento en que se bloquea"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Borrar todos los datos"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Borrado de los datos del teléfono sin avisar restableciendo datos de fábrica"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Definir el servidor proxy global"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Define el servidor proxy global que se debe utilizar mientras la política esté habilitada. Solo el primer administrador de dispositivos define el servidor proxy global efectivo."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Casa"</item>
     <item msgid="869923650527136615">"Móvil"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Casa"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Trabajo"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Otro"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"a través de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> a través de <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Introduce el código PIN"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Introducir contraseña para desbloquear"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Introducir PIN para desbloquear"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"El código PIN es incorrecto."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear el teléfono, pulsa la tecla de menú y, a continuación, pulsa 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergencia"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Volver a llamada"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Inténtalo de nuevo"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Inténtalo de nuevo"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Cargado"</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Introduce el código PIN."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Desbloqueando tarjeta SIM..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Has realizado <xliff:g id="NUMBER_0">%d</xliff:g> intentos fallidos de creación de un patrón de desbloqueo. "\n\n"Inténtalo de nuevo dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Has introducido una contraseña incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Inténtalo de nuevo dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Has introducido un PIN incorrecto <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Inténtalo de nuevo dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Has realizado <xliff:g id="NUMBER_0">%d</xliff:g> intentos fallidos de creación del patrón de desbloqueo. Si realizas <xliff:g id="NUMBER_1">%d</xliff:g> intentos fallidos más, se te pedirá que desbloquees el teléfono con tus credenciales de acceso de Google."\n\n" Espera <xliff:g id="NUMBER_2">%d</xliff:g> segundos e inténtalo de nuevo."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Espera <xliff:g id="NUMBER">%d</xliff:g> segundos y vuelve a intentarlo."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"¿Has olvidado el patrón?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"¿Quieres salir de esta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Selecciona \"Aceptar\" para continuar o \"Cancelar\" para permanecer en la página actual."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Sugerencia: toca dos veces para ampliar o reducir."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Autocompletar este formulario"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"leer información de marcadores y del historial del navegador"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que la aplicación lea todas las URL que ha visitado el navegador y todos sus marcadores."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"escribir en marcadores y en el historial del navegador"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Seleccionar todo"</string>
-    <string name="selectText" msgid="4862359311088898878">"Seleccionar palabra"</string>
     <string name="cut" msgid="3092569408438626261">"Cortar"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
     <string name="paste" msgid="5629880836805036433">"Pegar"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copiar URL"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Seleccionar texto..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Selección de texto"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de introducción de texto"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Añadir \"<xliff:g id="WORD">%s</xliff:g>\" al diccionario"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Editar texto"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Poco espacio"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"Se está agotando el espacio de almacenamiento del teléfono."</string>
     <string name="ok" msgid="5970060430562524910">"Aceptar"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"Aceptar"</string>
     <string name="no" msgid="5141531044935541497">"Cancelar"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Atención"</string>
+    <string name="loading" msgid="1760724998928255250">"Cargando…"</string>
     <string name="capital_on" msgid="1544682755514494298">"Activado"</string>
     <string name="capital_off" msgid="6815870386972805832">"Desconectado"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Completar acción utilizando"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"Red privada virtual L2TP/IPSec basada en clave compartida previamente"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"Red privada virtual L2TP/IPSec basada en certificado"</string>
     <string name="upload_file" msgid="2897957172366730416">"Seleccionar archivo"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Archivo no seleccionado"</string>
     <string name="reset" msgid="2448168080964209908">"Restablecer"</string>
     <string name="submit" msgid="1602335572089911941">"Enviar"</string>
-    <string name="description_star" msgid="2654319874908576133">"favoritos"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Se ha habilitado el modo coche."</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Selecciona esta opción para salir del modo coche."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Anclaje a red activo o zona Wi-Fi"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Toca para iniciar la configuración."</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Atrás"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Siguiente"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Omitir"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Uso elevado datos móviles"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Más información sobre uso de datos"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Límite datos superado"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Más información sobre uso de datos"</string>
+    <string name="no_matches" msgid="8129421908915840737">"No hay coincidencias."</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Buscar en la página"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"Una coincidencia"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"Desactivando almacenamiento USB..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"Desactivando tarjeta SD..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"Borrando almacenamiento USB..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"Un equipo está utilizando actualmente el almacenamiento USB."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"Un equipo está utilizando actualmente la tarjeta SD."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Estado de medio externo desconocido"</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index e28953e..483ff5f1 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Éteindre"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Arrêt en cours..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Votre téléphone va s\'éteindre."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Récentes"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Aucune application récente"</string>
     <string name="global_actions" msgid="2406416831541615258">"Options du téléphone"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Gérer le mode et les conditions de verrouillage de l\'écran"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Effacer toutes les données"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Effacer les données du téléphone sans avertissement, en restaurant les valeurs d\'usine"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Définir le proxy global du mobile"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Indiquez le proxy global à utiliser pour ce mobile lorsque les règles sont activées. Seul l\'administrateur principal du mobile peut définir le proxy global utilisé."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Domicile"</item>
     <item msgid="869923650527136615">"Mobile"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Accueil"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Professionnelle"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Autre"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Saisissez le code PIN"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Saisissez le mot de passe pour procéder au déverrouillage."</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Saisissez le code PIN pour procéder au déverrouillage."</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Le code PIN est incorrect !"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Pour débloquer le clavier, appuyez sur \"Menu\" puis sur 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numéro d\'urgence"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Retour à l\'appel"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Combinaison correcte !"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Incorrect. Merci de réessayer."</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Incorrect. Merci de réessayer."</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Chargement (<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Chargé"</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"La carte SIM est verrouillée."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Déblocage de la carte SIM..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Vous avez mal reproduit le schéma de déverrouillage <xliff:g id="NUMBER_0">%d</xliff:g> fois. "\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Vous avez saisi un mot de passe incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. "\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Vous avez saisi un code PIN incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. "\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Vous avez mal saisi le schéma de déverrouillage <xliff:g id="NUMBER_0">%d</xliff:g> fois. Au bout de <xliff:g id="NUMBER_1">%d</xliff:g> tentatives supplémentaires, vous devrez débloquer votre téléphone à l\'aide de votre identifiant Google."\n\n"Merci de réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Réessayez dans <xliff:g id="NUMBER">%d</xliff:g> secondes."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Schéma oublié ?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Vous souhaitez quitter cette page ?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Sélectionnez OK pour continuer ou Annuler pour rester sur la page actuelle."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Confirmer"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Conseil : Appuyez deux fois pour effectuer un zoom avant ou arrière."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Permettre le remplissage automatique du formulaire"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lire l\'historique et les favoris du navigateur"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Autorise l\'application à lire toutes les URL auxquelles le navigateur a accédé et tous ses favoris."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"écrire dans l\'historique et les favoris du navigateur"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Tout sélectionner"</string>
-    <string name="selectText" msgid="4862359311088898878">"Sélectionner texte"</string>
     <string name="cut" msgid="3092569408438626261">"Couper"</string>
     <string name="copy" msgid="2681946229533511987">"Copier"</string>
     <string name="paste" msgid="5629880836805036433">"Coller"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copier l\'URL"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Sélect. le texte..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Sélection de texte"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Mode de saisie"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Ajouter \"<xliff:g id="WORD">%s</xliff:g>\" au dictionnaire"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Modifier le texte"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Actions sur le texte"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Espace disponible faible"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"La mémoire du téléphone commence à être pleine."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Annuler"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Attention"</string>
+    <string name="loading" msgid="1760724998928255250">"Chargement en cours..."</string>
     <string name="capital_on" msgid="1544682755514494298">"ON"</string>
     <string name="capital_off" msgid="6815870386972805832">"OFF"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Continuer avec"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"VPN L2TP/IPSec basé sur une clé pré-partagée"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"VPN L2TP/IPSec basé sur un certificat"</string>
     <string name="upload_file" msgid="2897957172366730416">"Sélectionner un fichier"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Aucun fichier sélectionné"</string>
     <string name="reset" msgid="2448168080964209908">"Réinitialiser"</string>
     <string name="submit" msgid="1602335572089911941">"Envoyer"</string>
-    <string name="description_star" msgid="2654319874908576133">"favori"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Mode Voiture activé"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Sélectionnez cette option pour quitter le mode Voiture."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Partage de connexion ou point d\'accès sans fil activé"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Toucher pour configurer"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Retour"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Suivant"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Ignorer"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Utilisation élevée des données mobiles"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Touchez pour en savoir plus sur l\'utilisation des données mobiles"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Quota d\'utilisation des données mobiles dépassé"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Touchez pour en savoir plus sur l\'utilisation des données mobiles"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Aucune correspondance"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Rechercher sur la page"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 correspondance"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> sur <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"Désinstallation de la mémoire de stockage USB..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"Désinstallation de la carte SD..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"Effacement de la  mémoire de stockage USB..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"La mémoire de stockage USB est en cours d\'utilisation par l\'ordinateur."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"La carte SD est en cours d\'utilisation par l\'ordinateur."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"État du support externe inconnu"</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 559e2e2..b5f725d 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Spegni"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Spegnimento..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Il telefono verrà spento."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Recenti"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Nessuna applicazione recente."</string>
     <string name="global_actions" msgid="2406416831541615258">"Opzioni telefono"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Controlla come e quando si blocca lo schermo"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Cancella tutti i dati"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Cancella i dati del telefono senza preavviso eseguendo un ripristino dati di fabbrica"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Imposta il proxy globale del dispositivo"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Imposta il proxy globale del dispositivo in modo da utilizzarlo mentre la norma è attiva. Il proxy globale effettivo è impostabile solo dal primo amministratore del dispositivo."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Casa"</item>
     <item msgid="869923650527136615">"Cellulare"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Casa"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Lavoro"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Altro"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"tramite <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> tramite <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Inserisci il PIN"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Inserisci password per sbloccare"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Inserisci PIN per sbloccare"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Codice PIN errato."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Per sbloccare, premi Menu, poi 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numero di emergenza"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Torna a chiamata"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Corretta."</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Riprova"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Riprova"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"In carica (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Carico."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"La SIM è bloccata."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Sblocco SIM..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi di inserimento della sequenza di sblocco. "\n\n"Riprova fra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della password. "\n\n"Riprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento del PIN. "\n\n"Riprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il telefono tramite i dati di accesso di Google."\n\n"Riprova fra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Riprova fra <xliff:g id="NUMBER">%d</xliff:g> secondi."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Hai dimenticato la sequenza?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Uscire da questa pagina?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Seleziona OK per continuare o Annulla per rimanere nella pagina corrente."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Conferma"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Suggerimento. Tocca due volte per aumentare/ridurre lo zoom."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Compila automaticamente il modulo"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lettura cronologia e segnalibri del browser"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Consente all\'applicazione di leggere tutti gli URL visitati e tutti i segnalibri del browser."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"creazione cronologia e segnalibri del browser"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Seleziona tutto"</string>
-    <string name="selectText" msgid="4862359311088898878">"Seleziona parola"</string>
     <string name="cut" msgid="3092569408438626261">"Taglia"</string>
     <string name="copy" msgid="2681946229533511987">"Copia"</string>
     <string name="paste" msgid="5629880836805036433">"Incolla"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copia URL"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Seleziona testo..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Selezione testo"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Metodo inserimento"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Aggiungi \"<xliff:g id="WORD">%s</xliff:g>\" al dizionario"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Modifica testo"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Azioni testo"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Spazio in esaurimento"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"Spazio di archiviazione del telefono in esaurimento."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Annulla"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Attenzione"</string>
+    <string name="loading" msgid="1760724998928255250">"Caricamento in corso..."</string>
     <string name="capital_on" msgid="1544682755514494298">"ON"</string>
     <string name="capital_off" msgid="6815870386972805832">"OFF"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Completa l\'azione con"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"VPN L2TP/IPSec basata su chiave precondivisa"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"VPN L2TP/IPSec basata su certificato"</string>
     <string name="upload_file" msgid="2897957172366730416">"Scegli file"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Nessun file è stato scelto"</string>
     <string name="reset" msgid="2448168080964209908">"Reimposta"</string>
     <string name="submit" msgid="1602335572089911941">"Invia"</string>
-    <string name="description_star" msgid="2654319874908576133">"preferiti"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Modalità automobile attivata"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Seleziona per uscire dalla modalità automobile."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering oppure hotspot attivo"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Tocca per configurare"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Indietro"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Avanti"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Salta"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Utilizzo dati cell. elevato"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Tocca per informazioni sull\'utilizzo dati cell."</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Limite dati cell. superato"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Tocca per informazioni sull\'utilizzo dati cell."</string>
+    <string name="no_matches" msgid="8129421908915840737">"Nessuna corrispondenza"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Trova nella pagina"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 corrispondenza"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> di <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"Smontaggio dell\'archivio USB in corso..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"Smontaggio scheda SD in corso..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"Cancellazione dell\'archivio USB in corso..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"L\'archivio USB è attualmente utilizzato da un computer."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"La scheda SD è attualmente utilizzata da un computer."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Stato dei supporti esterni sconosciuto."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 00ee365..ed0d357 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"電源を切る"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"シャットダウン中..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"携帯電話の電源を切ります。"</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"新着"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"最近使ったアプリケーションはありません。"</string>
     <string name="global_actions" msgid="2406416831541615258">"携帯電話オプション"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"画面をロックする方法とタイミングを制御する"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"すべてのデータを消去"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"警告せずにデータの初期化を実行して端末内のデータを消去します。"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"端末のグローバルプロキシを設定"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"ポリシーが有効になっている場合は端末のグローバルプロキシが使用されるように設定します。有効なグローバルプロキシを設定できるのは最初のデバイス管理者だけです。"</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"自宅"</item>
     <item msgid="869923650527136615">"携帯"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"自宅"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"勤務先"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"その他"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"<xliff:g id="SOURCE">%1$s</xliff:g>経由"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g>、更新元: <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PINコードを入力"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"ロックを解除するにはパスワードを入力"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"ロックを解除するにはPINを入力"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"PINコードが正しくありません。"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"MENU、0キーでロック解除"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急通報番号"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"通話に戻る"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"一致しました"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"やり直してください"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"やり直してください"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"充電中(<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"充電完了。"</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIMカードはロックされています。"</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIMカードのロック解除中..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"ロック解除のパターンは<xliff:g id="NUMBER_0">%d</xliff:g>回とも正しく指定されていません。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度指定してください。"</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"入力したパスワードは<xliff:g id="NUMBER_0">%d</xliff:g>回とも正しくありませんでした。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度入力してください。"</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"入力したPINは<xliff:g id="NUMBER_0">%d</xliff:g>回とも正しくありませんでした。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒後にもう一度入力してください。"</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"指定したパターンは<xliff:g id="NUMBER_0">%d</xliff:g>回とも正しくありません。あと<xliff:g id="NUMBER_1">%d</xliff:g>回指定に失敗すると、携帯電話のロックの解除にGoogleへのログインが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度指定してください。"</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g>秒後にやり直してください。"</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"パターンを忘れた場合"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"このページから移動しますか?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"移動する場合は[OK]、今のページに残る場合は[キャンセル]を選択してください。"</string>
     <string name="save_password_label" msgid="6860261758665825069">"確認"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"ヒント: ダブルタップで拡大/縮小できます。"</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"このフォームを自動入力"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ブラウザの履歴とブックマークを読み取る"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"ブラウザでアクセスしたすべてのURLおよびブラウザのすべてのブックマークの読み取りをアプリケーションに許可します。"</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"ブラウザの履歴とブックマークを書き込む"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"すべて選択"</string>
-    <string name="selectText" msgid="4862359311088898878">"語句を選択"</string>
     <string name="cut" msgid="3092569408438626261">"切り取り"</string>
     <string name="copy" msgid="2681946229533511987">"コピー"</string>
     <string name="paste" msgid="5629880836805036433">"貼り付け"</string>
     <string name="copyUrl" msgid="2538211579596067402">"URLをコピー"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"テキストを選択..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"テキスト選択"</string>
     <string name="inputMethod" msgid="1653630062304567879">"入力方法"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"辞書に「<xliff:g id="WORD">%s</xliff:g>」を追加"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"テキストを編集"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"テキスト操作"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"空き容量低下"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"携帯電話の空き容量が少なくなっています。"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"キャンセル"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"注意"</string>
+    <string name="loading" msgid="1760724998928255250">"読み込み中..."</string>
     <string name="capital_on" msgid="1544682755514494298">"ON"</string>
     <string name="capital_off" msgid="6815870386972805832">"OFF"</string>
     <string name="whichApplication" msgid="4533185947064773386">"アプリケーションを選択"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"L2TP/IPSec VPNベースの事前共有鍵"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"L2TP/IPSec VPNベースの証明書"</string>
     <string name="upload_file" msgid="2897957172366730416">"ファイルを選択"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"ファイルが選択されていません"</string>
     <string name="reset" msgid="2448168080964209908">"リセット"</string>
     <string name="submit" msgid="1602335572089911941">"送信"</string>
-    <string name="description_star" msgid="2654319874908576133">"お気に入り"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"運転モードを有効にする"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"運転モードを終了するには選択してください。"</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"テザリングまたはアクセスポイントが有効です"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"タップして設定する"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"戻る"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"次へ"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"スキップ"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"モバイルデータの使用量が増えています"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"タップしてモバイルデータ利用の詳細を表示します"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"モバイルデータの制限を超えました"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"タップしてモバイルデータ利用の詳細を表示します"</string>
+    <string name="no_matches" msgid="8129421908915840737">"該当なし"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"ページ内を検索"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1件一致"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g>/<xliff:g id="TOTAL">%d</xliff:g>件"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"USBストレージのマウント解除中..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"SDカードのマウント解除中..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"USBストレージ内のデータを消去中..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"USBストレージは現在パソコンで使用中です。"</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"SDカードは現在パソコンで使用中です。"</string>
     <string name="media_unknown_state" msgid="729192782197290385">"外部メディアの状態は不明です。"</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 2fa587b..ab593f9 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"종료"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"종료 중..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"휴대전화가 종료됩니다."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"최근 사용한 앱"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"최근에 사용한 앱이 없습니다."</string>
     <string name="global_actions" msgid="2406416831541615258">"휴대전화 옵션"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"화면을 잠그는 방법과 시기를 제어합니다."</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"모든 데이터 삭제"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"초기화를 수행하여 경고 없이 휴대전화 데이터를 지웁니다."</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"기기 전체 프록시 설정"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"정책이 사용 설정되어 있는 동안 사용될 기기 전체 프록시를 설정합니다. 첫 번째 기기 관리자가 설정한 전체 프록시만 유효합니다."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"집"</item>
     <item msgid="869923650527136615">"모바일"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"홈"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"직장"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"기타"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"<xliff:g id="SOURCE">%1$s</xliff:g>을(를) 통해"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g>(<xliff:g id="SOURCE">%2$s</xliff:g> 사용)"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN 코드 입력"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"잠금을 해제하려면 비밀번호 입력"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"잠금을 해제하려면 PIN 입력"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"PIN 코드가 잘못되었습니다."</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"잠금해제하려면 메뉴를 누른 다음 0을 누릅니다."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"비상 전화번호"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"통화로 돌아가기"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"맞습니다."</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"죄송합니다. 다시 시도하세요."</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"죄송합니다. 다시 시도해 주세요."</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"충전 중(<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"충전되었습니다."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM 카드가 잠겨 있습니다."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM 카드 잠금해제 중..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도하세요."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"비밀번호를 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"PIN을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 Google 로그인을 통해 휴대전화를 잠금해제하도록 요청됩니다. "\n\n" <xliff:g id="NUMBER_2">%d</xliff:g>초 후에 다시 시도하세요."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g>초 후에 다시 시도하세요."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"패턴을 잊으셨나요?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"다른 페이지를 탐색하시겠습니까?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"계속하려면 \'확인\'을 선택하고 현재 페이지에 그대로 있으려면 \'취소\'를 선택하세요."</string>
     <string name="save_password_label" msgid="6860261758665825069">"확인"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"도움말: 축소/확대하려면 두 번 누릅니다."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"양식 자동완성"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"브라우저의 기록 및 북마크 읽기"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"애플리케이션이 브라우저로 방문한 모든 URL과 브라우저의 모든 북마크를 읽도록 허용합니다."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"브라우저의 기록 및 북마크 쓰기"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"모두 선택"</string>
-    <string name="selectText" msgid="4862359311088898878">"단어 선택"</string>
     <string name="cut" msgid="3092569408438626261">"잘라내기"</string>
     <string name="copy" msgid="2681946229533511987">"복사"</string>
     <string name="paste" msgid="5629880836805036433">"붙여넣기"</string>
     <string name="copyUrl" msgid="2538211579596067402">"URL 복사"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"텍스트 선택..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"텍스트 선택"</string>
     <string name="inputMethod" msgid="1653630062304567879">"입력 방법"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"사전에 \'<xliff:g id="WORD">%s</xliff:g>\' 추가"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"텍스트 수정"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"텍스트 작업"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"저장공간 부족"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"휴대전화 저장공간이 부족합니다."</string>
     <string name="ok" msgid="5970060430562524910">"확인"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"확인"</string>
     <string name="no" msgid="5141531044935541497">"취소"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"주의"</string>
+    <string name="loading" msgid="1760724998928255250">"로드 중..."</string>
     <string name="capital_on" msgid="1544682755514494298">"사용"</string>
     <string name="capital_off" msgid="6815870386972805832">"사용 안함"</string>
     <string name="whichApplication" msgid="4533185947064773386">"작업을 수행할 때 사용하는 애플리케이션"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"사전 공유 키 기반 L2TP/IPSec VPN"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"인증서 기반 L2TP/IPSec VPN"</string>
     <string name="upload_file" msgid="2897957172366730416">"파일 선택"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"파일을 선택하지 않았습니다."</string>
     <string name="reset" msgid="2448168080964209908">"재설정"</string>
     <string name="submit" msgid="1602335572089911941">"제출"</string>
-    <string name="description_star" msgid="2654319874908576133">"즐겨찾기"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"운전모드 사용"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"운전모드를 종료하려면 선택하세요."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"테더링 또는 핫스팟 사용"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"구성하려면 터치하세요."</string>
+    <string name="back_button_label" msgid="2300470004503343439">"뒤로"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"다음"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"건너뛰기"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"높은 모바일 데이터 사용량"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"모바일 데이터 사용에 대해 자세히 알아보려면 터치하세요."</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"모바일 데이터 제한을 초과했습니다."</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"모바일 데이터 사용에 대해 자세히 알아보려면 터치하세요."</string>
+    <string name="no_matches" msgid="8129421908915840737">"검색결과 없음"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"페이지에서 찾기"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"검색결과 1개"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g>/<xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"USB 저장소 마운트 해제 중..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"SD 카드 마운트 해제 중..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"USB 저장소 지우는 중..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"현재 USB 저장소를 컴퓨터에서 사용하고 있습니다."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"현재 SD 카드를 컴퓨터에서 사용하고 있습니다."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"외부 미디어가 알 수 없는 상태입니다."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-mcc450-ko/config.xml b/core/res/res/values-mcc450-ko/config.xml
new file mode 100644
index 0000000..335b967
--- /dev/null
+++ b/core/res/res/values-mcc450-ko/config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <string name="gsm_alphabet_default_charset">euc-kr</string>
+</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 6424c68..e2dd8a0 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Slå av"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Avslutter…"</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Telefonen vil bli slått av."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Nylig"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Ingen nylig brukte applikasjoner."</string>
     <string name="global_actions" msgid="2406416831541615258">"Telefoninnstillinger"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Kontroller hvordan og når skjermen låses"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Slett alle data"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Tilbakestill telefonens data uten advarsel ved å utføre tilbakestilling til fabrikkstandard"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Angi enhetens globale mellomtjener"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Angir den globale mellomtjeneren på enheten som skal brukes når regelen er aktivert. Kun den opprinnelige administratoren av enheten kan angi den globale mellomtjeneren."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Hjemmenummer"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Startside"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Arbeid"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Annen"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Skriv inn PIN-kode:"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Skriv inn passord for å låse opp"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Skriv inn personlig kode for å låse opp"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Gal PIN-kode!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"For å låse opp, trykk på menyknappen og deretter 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nødnummer"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Tilbake til samtale"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Riktig!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Beklager, prøv igjen:"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Prøv igjen"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Lader (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Fullt ladet"</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kortet er låst."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Låser opp SIM-kort…"</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. "\n\n"Please try again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Du har oppgitt feil passord <xliff:g id="NUMBER_0">%d</xliff:g> ganger. "\n\n"Prøv igjen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Du har oppgitt feil personlig kode <xliff:g id="NUMBER_0">%d</xliff:g> ganger. "\n\n"Prøv igjen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using your Google sign-in."\n\n" Please try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Prøv igjen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Glemt mønsteret?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Naviger bort fra denne siden?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Velg OK for å fortsette, eller Avbryt for å forbli på denne siden."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Bekreft"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Dobbelttrykk for å zoome inn og ut."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Fyll ut dette skjemaet automatisk"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lese nettleserens logg og bokmerker"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Lar applikasjonen lese alle adresser nettleseren har besøkt, og alle nettleserens bokmerker."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skrive til nettleserens logg og bokmerker"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Merk alt"</string>
-    <string name="selectText" msgid="4862359311088898878">"Velg ord"</string>
     <string name="cut" msgid="3092569408438626261">"Klipp ut"</string>
     <string name="copy" msgid="2681946229533511987">"Kopier"</string>
     <string name="paste" msgid="5629880836805036433">"Lim inn"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Kopier URL"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Marker tekst"</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Merket tekst"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Inndatametode"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Legg «<xliff:g id="WORD">%s</xliff:g>» til ordlisten"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Rediger tekst"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Lite plass"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"Det begynner å bli lite lagringsplass på telefonen."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Avbryt"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Merk"</string>
+    <string name="loading" msgid="1760724998928255250">"Laster inn ..."</string>
     <string name="capital_on" msgid="1544682755514494298">"På"</string>
     <string name="capital_off" msgid="6815870386972805832">"Av"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Fullfør med"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"Passordbasert L2TP/IPSec-VPN"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"Sertifikatbasert L2TP/IPSec-VPN"</string>
     <string name="upload_file" msgid="2897957172366730416">"Velg fil"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Ingen fil er valgt"</string>
     <string name="reset" msgid="2448168080964209908">"Tilbakestill"</string>
     <string name="submit" msgid="1602335572089911941">"Send inn"</string>
-    <string name="description_star" msgid="2654319874908576133">"favoritt"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Bilmodus er aktivert"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Velg for å avslutte bilmodus"</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Tilknytning eller trådløs sone er aktiv"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Trykk for å konfigurere"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Tilbake"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Neste"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Hopp over"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Høy mobildatabruk"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Berør for å lese mer om bruk av mobildata"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Grensen for mobildatabruk er overskredet"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Berør for å lese mer om bruk av mobildata"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Ingen treff"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Finn på side"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 treff"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> av <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"Kobler fra USB-lagring ..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"Kobler fra SD-kort ..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"Sletter USB-lagring ..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"USB-lagring er for øyeblikket i bruk av en datamaskin."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"SD-kortet er for øyeblikket i bruk av en datamaskin."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Eksternt medium har ukjent tilstand."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 9c3478d..a040df5 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Uitschakelen"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Uitschakelen..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Uw telefoon wordt uitgeschakeld."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Recent"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Geen recente toepassingen."</string>
     <string name="global_actions" msgid="2406416831541615258">"Telefoonopties"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Beheren hoe en wanneer het scherm wordt vergrendeld"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Alle gegevens wissen"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"De gegevens van de telefoon zonder waarschuwing wissen door de fabrieksinstellingen te herstellen"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Algemene proxy voor het apparaat instellen"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Stel de algemene proxy voor het apparaat in die moet worden gebruikt terwijl het beleid is geactiveerd. Alleen de eerste apparaatbeheerder stelt de algemene proxy in."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Thuis"</item>
     <item msgid="869923650527136615">"Mobiel"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Startpagina"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Werk"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Overig"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN-code invoeren"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Voer het wachtwoord in om te ontgrendelen"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Voer de PIN-code in om te ontgrendelen"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Onjuiste PIN-code!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Druk op \'Menu\' en vervolgens op 0 om te ontgrendelen."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Alarmnummer"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Terug naar gesprek"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Juist!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Probeer het opnieuw"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Probeer het opnieuw"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Opladen (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Opgeladen."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kaart is vergrendeld."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM-kaart ontgrendelen..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. "\n\n"Probeer het over <xliff:g id="NUMBER_1">%d</xliff:g> seconden opnieuw."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"U heeft uw wachtwoord <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist ingevoerd. "\n\n"Probeer het over <xliff:g id="NUMBER_1">%d</xliff:g> seconden opnieuw."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"U heeft uw PIN-code <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist ingevoerd. "\n\n"Probeer het over <xliff:g id="NUMBER_1">%d</xliff:g> seconden opnieuw."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen, wordt u gevraagd om uw telefoon te ontgrendelen met uw Google aanmelding."\n\n" Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Probeer het over <xliff:g id="NUMBER">%d</xliff:g> seconden opnieuw."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Patroon vergeten?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Wilt u deze pagina verlaten?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Kies OK om door te gaan of Annuleren om op de huidige pagina te blijven."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Bevestigen"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Tip: tik tweemaal om in of uit te zoomen."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Dit formulier automatisch aanvullen"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"browsergeschiedenis en bladwijzers lezen"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Hiermee kan een toepassing de URL\'s lezen die u via de browser heeft bezocht, evenals alle bladwijzers van de browser."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"browsergeschiedenis en bladwijzers schrijven"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Alles selecteren"</string>
-    <string name="selectText" msgid="4862359311088898878">"Woord selecteren"</string>
     <string name="cut" msgid="3092569408438626261">"Knippen"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiëren"</string>
     <string name="paste" msgid="5629880836805036433">"Plakken"</string>
     <string name="copyUrl" msgid="2538211579596067402">"URL kopiëren"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Tekst selecteren..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Tekstselectie"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Invoermethode"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"\'<xliff:g id="WORD">%s</xliff:g>\' toevoegen aan woordenboek"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Tekst bewerken"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstacties"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Weinig ruimte"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"Opslagruimte van telefoon raakt op."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Annuleren"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Let op"</string>
+    <string name="loading" msgid="1760724998928255250">"Laden..."</string>
     <string name="capital_on" msgid="1544682755514494298">"AAN"</string>
     <string name="capital_off" msgid="6815870386972805832">"UIT"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Actie voltooien met"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"Vooraf gedeelde sleutel op basis van L2TP/IPSec VPN"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"Certificaat op basis van L2TP/IPSec VPN"</string>
     <string name="upload_file" msgid="2897957172366730416">"Bestand kiezen"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Geen bestand geselecteerd"</string>
     <string name="reset" msgid="2448168080964209908">"Opnieuw instellen"</string>
     <string name="submit" msgid="1602335572089911941">"Verzenden"</string>
-    <string name="description_star" msgid="2654319874908576133">"favoriet"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Automodus ingeschakeld"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Selecteer dit om de automodus af te sluiten."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering of hotspot actief"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Aanraken om te configureren"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Vorige"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Volgende"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Overslaan"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Hoog mobiel gegevensgebruik"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Raak aan voor meer informatie over mobiel gegevensgebruik"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Mobiele gegevenslimiet overschreden"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Raak aan voor meer informatie over mobiel gegevensgebruik"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Geen overeenkomsten"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Zoeken op pagina"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 overeenkomst"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> van <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"USB-opslag ontkoppelen..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"SD-kaart ontkoppelen..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"USB-opslag wissen..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"USB-opslag wordt momenteel gebruikt door een computer."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"SD-kaart wordt momenteel gebruikt door een computer."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Extern medium heeft onbekende status."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index e1f218a..215a477 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Wyłącz"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Wyłączanie..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Telefon zostanie wyłączony"</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Najnowsze"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Brak ostatnio używanych aplikacji."</string>
     <string name="global_actions" msgid="2406416831541615258">"Opcje telefonu"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Kontrolowanie sposobu i warunków blokowania ekranu"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Usuń wszystkie dane"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Wymazywanie danych z telefonu bez ostrzeżenia, przez przywrócenie danych fabrycznych"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ustaw globalny serwer proxy urządzenia"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ustaw globalny serwer proxy urządzenia do wykorzystywania przy włączonych zasadach. Tylko pierwszy administrator urządzenia ustawia obowiązujący globalny serwer proxy."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Dom"</item>
     <item msgid="869923650527136615">"Komórka"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Domowy"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Służbowy"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Inny"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"przez <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> przez <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Wprowadź kod PIN"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Wprowadź hasło, aby odblokować"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Wprowadź kod PIN, aby odblokować"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Błędny kod PIN!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Aby odblokować, naciśnij Menu, a następnie 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numer alarmowy"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Powrót do połączenia"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Poprawnie!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Niestety, spróbuj ponownie"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Spróbuj ponownie"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Ładowanie (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Naładowany."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"Karta SIM jest zablokowana."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Odblokowywanie karty SIM..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Wzór odblokowania został nieprawidłowo narysowany <xliff:g id="NUMBER_0">%d</xliff:g> razy. "\n\n"Spróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> sekund."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Hasło zostało nieprawidłowo wprowadzone <xliff:g id="NUMBER_0">%d</xliff:g> razy. "\n\n"Spróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Niepoprawnie wprowadzono kod PIN <xliff:g id="NUMBER_0">%d</xliff:g> razy. "\n\n"Spróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Wzór odblokowania został narysowany nieprawidłowo <xliff:g id="NUMBER_0">%d</xliff:g> razy. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach telefon trzeba będzie odblokować przez zalogowanie na koncie Google."\n\n" Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> sekund."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Spróbuj ponownie za <xliff:g id="NUMBER">%d</xliff:g> sekund."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Zapomniałeś wzoru?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Czy opuścić tę stronę?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Wybierz opcję OK, aby kontynuować, lub opcję Anuluj, aby pozostać na tej stronie."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Potwierdź"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Wskazówka: dotknij dwukrotnie, aby powiększyć lub pomniejszyć."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Wypełnij ten formularz automatycznie"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"odczyt historii i zakładek przeglądarki"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Umożliwia aplikacji odczyt wszystkich adresów URL odwiedzonych przez przeglądarkę, a także wszystkich zakładek przeglądarki."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"zapis historii i zakładek przeglądarki"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Zaznacz wszystko"</string>
-    <string name="selectText" msgid="4862359311088898878">"Zaznacz wyraz"</string>
     <string name="cut" msgid="3092569408438626261">"Wytnij"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiuj"</string>
     <string name="paste" msgid="5629880836805036433">"Wklej"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Kopiuj adres URL"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Zaznacz tekst"</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Zaznaczanie tekstu"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Metoda wprowadzania"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Dodaj termin „<xliff:g id="WORD">%s</xliff:g>” do słownika"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Edytuj tekst"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Działania na tekście"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Mało miejsca"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"Maleje ilość dostępnej pamięci telefonu."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Anuluj"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Uwaga"</string>
+    <string name="loading" msgid="1760724998928255250">"Wczytywanie..."</string>
     <string name="capital_on" msgid="1544682755514494298">"Włącz"</string>
     <string name="capital_off" msgid="6815870386972805832">"Wyłącz"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Zakończ czynność korzystając z"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"Sieć VPN L2TP/IPSec z kluczem PSK"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"Sieć VPN L2TP/IPSec z certyfikatem"</string>
     <string name="upload_file" msgid="2897957172366730416">"Wybierz plik"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Nie wybrano pliku"</string>
     <string name="reset" msgid="2448168080964209908">"Resetuj"</string>
     <string name="submit" msgid="1602335572089911941">"Prześlij"</string>
-    <string name="description_star" msgid="2654319874908576133">"ulubione"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Tryb samochodowy włączony"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Wybierz, aby zakończyć tryb samochodowy."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Jest aktywne powiązanie lub punkt dostępu"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Dotknij, aby skonfigurować"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Wróć"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Dalej"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Pomiń"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Wysoki poziom użycia danych"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Dotknij, aby zobaczyć statystyki przesyłu danych"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Przekroczono limit danych"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Dotknij, aby zobaczyć statystyki przesyłu danych"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Brak wyników"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Znajdź na stronie"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 wynik"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> z <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"Odłączanie nośnika USB..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"Odłączanie karty SD..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"Czyszczenie nośnika USB..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"Nośnik USB jest obecnie używany przez komputer."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"Karta SD jest obecnie używana przez komputer."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Nośnik zewnętrzny jest w nieznanym stanie."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 3a8759a..4bb4ddf 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Desligar"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"A encerrar..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"O seu telefone irá encerrar."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Recente"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Nenhuma aplicação recente."</string>
     <string name="global_actions" msgid="2406416831541615258">"Opções do telefone"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Controlar como e quando ocorre o bloqueio do ecrã"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Apagar todos os dados"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Apagar os dados do telefone sem avisar, ao efectuar uma reposição de dados de fábrica"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Definir o proxy global do aparelho"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Definir o proxy global do aparelho a ser utilizado quando a política estiver activada. Só o primeiro administrador do aparelho define o proxy global efectivo."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Residência"</item>
     <item msgid="869923650527136615">"Móvel"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Página inicial"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Emprego"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Outro"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"através do <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> através de <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Introduzir código PIN"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Introduza a palavra-passe para desbloquear"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Introduza o PIN para desbloquear"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Código PIN incorrecto!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, prima Menu e, em seguida, 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergência"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Regressar à chamada"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Lamentamos, tente novamente"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Lamentamos, tente novamente"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"A carregar (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Carregado."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"O cartão SIM está bloqueado."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"A desbloquear cartão SIM..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Efectuou incorrectamente o seu padrão de desbloqueio <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Introduziu incorrectamente a palavra-passe <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Introduziu incorrectamente o PIN <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Efectuou incorrectamente o seu padrão de desbloqueio <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após outras <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o telefone utilizando o seu início de sessão no Google."\n\n" Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Tente novamente dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Esqueceu-se do padrão?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Navegar para outra página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Seleccione OK para continuar ou Cancelar para permanecer na página actual."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Sugestão: toque duas vezes para aumentar ou diminuir o zoom."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Preenchimento automático deste formulário"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ler histórico e marcadores do browser"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que a aplicação leia todos os URLs visitados pelo browser e todos os marcadores do browser."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"gravar histórico e marcadores do browser"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Seleccionar tudo"</string>
-    <string name="selectText" msgid="4862359311088898878">"Seleccionar palavra"</string>
     <string name="cut" msgid="3092569408438626261">"Cortar"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
     <string name="paste" msgid="5629880836805036433">"Colar"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copiar URL"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Seleccionar texto..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Selecção de texto"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de entrada"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Adicionar \"<xliff:g id="WORD">%s</xliff:g>\" ao dicionário"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Editar texto"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Acções de texto"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Pouco espaço livre"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"O espaço de armazenamento do telefone está a ficar reduzido."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Cancelar"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Atenção"</string>
+    <string name="loading" msgid="1760724998928255250">"A carregar..."</string>
     <string name="capital_on" msgid="1544682755514494298">"Activado"</string>
     <string name="capital_off" msgid="6815870386972805832">"Desactivar"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Concluir acção utilizando"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"VPN L2TP/IPSec baseada em chave pré- partilhada"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"VPN L2TP/IPSec baseada em certificado"</string>
     <string name="upload_file" msgid="2897957172366730416">"Escolher ficheiro"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Não foi seleccionado nenhum ficheiro"</string>
     <string name="reset" msgid="2448168080964209908">"Repor"</string>
     <string name="submit" msgid="1602335572089911941">"Enviar"</string>
-    <string name="description_star" msgid="2654319874908576133">"favorito"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Modo automóvel activado"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Seleccionar para sair do modo automóvel."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Ligação ponto a ponto ou hotspot activos"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Tocar para configurar"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Anterior"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Seguinte"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Ignorar"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Utilização elevada de dados móveis"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Toque para saber mais sobre a utilização de dados móveis"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Limite de dados móveis excedido"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Toque para saber mais sobre a utilização de dados móveis"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Sem correspondências"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Localizar na página"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 correspondência"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"A desmontar armazenamento USB..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"A desmontar cartão SD..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"A apagar armazenamento USB..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"O armazenamento USB está actualmente a ser utilizado por um computador."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"O cartão SD está actualmente a ser utilizado por um computador."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Suporte de dados externo em estado desconhecido."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 562c80b..4af491a 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Desligar"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Encerrando…"</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"O seu telefone será desligado."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Recente"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Nenhum aplicativo recente."</string>
     <string name="global_actions" msgid="2406416831541615258">"Opções do telefone"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Controla como e quando a tela é bloqueada"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Apagar todos os dados"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Apaga os dados do telefone sem aviso, executando uma redefinição da configuração original"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Definir o proxy global do dispositivo"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Configura o proxy global do dispositivo para ser usado enquanto a política estiver ativada. Somente o primeiro administrador do dispositivo pode configurar um verdadeiro proxy global."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Residencial"</item>
     <item msgid="869923650527136615">"Celular"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Página inicial"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Comercial"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Outros"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"por meio de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Digite o código PIN"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Digite a senha para desbloquear"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Digite o PIN para desbloquear"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Código PIN incorreto!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, pressione Menu e, em seguida, 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergência"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Retornar à chamada"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correto!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Tente novamente"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Tente novamente"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Carregando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Carregado."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"O cartão SIM está bloqueado."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Desbloqueando o cartão SIM…"</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Você desenhou incorretamente o seu padrão de desbloqueio <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Você inseriu incorretamente a sua senha <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Você digitou incorretamente o seu PIN <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Você desenhou o seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas e você receberá uma solicitação para desbloquear o seu telefone usando o seu login do Google."\n\n" Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Tente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Esqueceu o padrão?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Deseja sair desta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Selecione OK para continuar ou Cancelar para permanecer na página atual."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Dica: toque duas vezes para aumentar e diminuir o zoom."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Preencher automaticamente este formulário"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ler histórico e favoritos do Navegador"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que o aplicativo leia todos os URLs visitados pelo Navegador e todos os favoritos do Navegador."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"gravar histórico e favoritos do Navegador"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Selecionar tudo"</string>
-    <string name="selectText" msgid="4862359311088898878">"Selecionar palavra"</string>
     <string name="cut" msgid="3092569408438626261">"Recortar"</string>
     <string name="copy" msgid="2681946229533511987">"Copiar"</string>
     <string name="paste" msgid="5629880836805036433">"Colar"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Copiar URL"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Selecionar texto..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Seleção de texto"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Método de entrada"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Adicionar \"<xliff:g id="WORD">%s</xliff:g>\" ao dicionário"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Editar texto"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Ações de texto"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Pouco espaço"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"O espaço de armazenamento do telefone está ficando baixo."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Cancelar"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Atenção"</string>
+    <string name="loading" msgid="1760724998928255250">"Carregando..."</string>
     <string name="capital_on" msgid="1544682755514494298">"ATIVADO"</string>
     <string name="capital_off" msgid="6815870386972805832">"DESATIVADO"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Complete a ação usando"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"VPN L2TP/IPSec com base em chave pré-compartilhada"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"VPN L2TP/IPSec com base em certificado"</string>
     <string name="upload_file" msgid="2897957172366730416">"Escolher arquivo"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Nenhum arquivo escolhido"</string>
     <string name="reset" msgid="2448168080964209908">"Redefinir"</string>
     <string name="submit" msgid="1602335572089911941">"Enviar"</string>
-    <string name="description_star" msgid="2654319874908576133">"favorito"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Modo de carro ativado"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Selecione para sair do modo de carro."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Vínculo ou ponto de acesso ativo"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Toque para configurar"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Voltar"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Avançar"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Ignorar"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Alto uso de dados do celular"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Toque para saber mais sobre uso de dados do celular"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Limite de dados do celular excedido"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Toque para saber mais sobre o uso de dados do celular"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Não encontrado"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Localizar na página"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"Uma correspondência"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"Desconectando armazenamento USB..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"Desconectando cartão SD..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"Apagando o armazenamento USB..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"O armazenamento USB está em uso por um computador no momento."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"O cartão SD está em uso por um computador no momento."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Mídia externa em estado desconhecido."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-rm-rCH/arrays.xml b/core/res/res/values-rm-rCH/arrays.xml
new file mode 100644
index 0000000..48e8613
--- /dev/null
+++ b/core/res/res/values-rm-rCH/arrays.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2010, 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.
+*/
+-->
+<resources>
+
+    <!-- Do not translate. -->
+    <integer-array name="maps_starting_lat_lng">
+        <item>46948020</item>
+        <item>7448206</item>
+    </integer-array>
+    <!-- Do not translate. -->
+    <integer-array name="maps_starting_zoom">
+        <item>4</item>
+    </integer-array>
+
+</resources>
diff --git a/core/res/res/values-rm/donottranslate-cldr.xml b/core/res/res/values-rm/donottranslate-cldr.xml
new file mode 100644
index 0000000..ccdb17c
--- /dev/null
+++ b/core/res/res/values-rm/donottranslate-cldr.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="month_long_standalone_january">schaner</string>
+    <string name="month_long_standalone_february">favrer</string>
+    <string name="month_long_standalone_march">mars</string>
+    <string name="month_long_standalone_april">avrigl</string>
+    <string name="month_long_standalone_may">matg</string>
+    <string name="month_long_standalone_june">zercladur</string>
+    <string name="month_long_standalone_july">fanadur</string>
+    <string name="month_long_standalone_august">avust</string>
+    <string name="month_long_standalone_september">settember</string>
+    <string name="month_long_standalone_october">october</string>
+    <string name="month_long_standalone_november">november</string>
+    <string name="month_long_standalone_december">december</string>
+
+    <string name="month_long_january">schaner</string>
+    <string name="month_long_february">favrer</string>
+    <string name="month_long_march">mars</string>
+    <string name="month_long_april">avrigl</string>
+    <string name="month_long_may">matg</string>
+    <string name="month_long_june">zercladur</string>
+    <string name="month_long_july">fanadur</string>
+    <string name="month_long_august">avust</string>
+    <string name="month_long_september">settember</string>
+    <string name="month_long_october">october</string>
+    <string name="month_long_november">november</string>
+    <string name="month_long_december">december</string>
+
+    <string name="month_medium_january">schan.</string>
+    <string name="month_medium_february">favr.</string>
+    <string name="month_medium_march">mars</string>
+    <string name="month_medium_april">avr.</string>
+    <string name="month_medium_may">matg</string>
+    <string name="month_medium_june">zercl.</string>
+    <string name="month_medium_july">fan.</string>
+    <string name="month_medium_august">avust</string>
+    <string name="month_medium_september">sett.</string>
+    <string name="month_medium_october">oct.</string>
+    <string name="month_medium_november">nov.</string>
+    <string name="month_medium_december">dec.</string>
+
+    <string name="month_shortest_january">S</string>
+    <string name="month_shortest_february">F</string>
+    <string name="month_shortest_march">M</string>
+    <string name="month_shortest_april">A</string>
+    <string name="month_shortest_may">M</string>
+    <string name="month_shortest_june">Z</string>
+    <string name="month_shortest_july">F</string>
+    <string name="month_shortest_august">A</string>
+    <string name="month_shortest_september">S</string>
+    <string name="month_shortest_october">O</string>
+    <string name="month_shortest_november">N</string>
+    <string name="month_shortest_december">D</string>
+
+    <string name="day_of_week_long_sunday">dumengia</string>
+    <string name="day_of_week_long_monday">glindesdi</string>
+    <string name="day_of_week_long_tuesday">mardi</string>
+    <string name="day_of_week_long_wednesday">mesemna</string>
+    <string name="day_of_week_long_thursday">gievgia</string>
+    <string name="day_of_week_long_friday">venderdi</string>
+    <string name="day_of_week_long_saturday">sonda</string>
+
+    <string name="day_of_week_medium_sunday">du</string>
+    <string name="day_of_week_medium_monday">gli</string>
+    <string name="day_of_week_medium_tuesday">ma</string>
+    <string name="day_of_week_medium_wednesday">me</string>
+    <string name="day_of_week_medium_thursday">gie</string>
+    <string name="day_of_week_medium_friday">ve</string>
+    <string name="day_of_week_medium_saturday">so</string>
+
+    <string name="day_of_week_short_sunday">du</string>
+    <string name="day_of_week_short_monday">gli</string>
+    <string name="day_of_week_short_tuesday">ma</string>
+    <string name="day_of_week_short_wednesday">me</string>
+    <string name="day_of_week_short_thursday">gie</string>
+    <string name="day_of_week_short_friday">ve</string>
+    <string name="day_of_week_short_saturday">so</string>
+
+    <string name="day_of_week_shortest_sunday">D</string>
+    <string name="day_of_week_shortest_monday">G</string>
+    <string name="day_of_week_shortest_tuesday">M</string>
+    <string name="day_of_week_shortest_wednesday">M</string>
+    <string name="day_of_week_shortest_thursday">G</string>
+    <string name="day_of_week_shortest_friday">V</string>
+    <string name="day_of_week_shortest_saturday">S</string>
+
+    <string name="yesterday">ier</string>
+    <string name="today">oz</string>
+    <string name="tomorrow">damaun</string>
+
+    <string name="hour_minute_24">%-k:%M</string>
+    <string name="hour_minute_ampm">%-l:%M %p</string>
+    <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
+    <string name="twelve_hour_time_format">h:mm a</string>
+    <string name="twenty_four_hour_time_format">H:mm</string>
+    <string name="numeric_date">%d.%m.%Y</string>
+    <string name="numeric_date_format">dd.MM.yyyy</string>
+    <string name="numeric_date_template">"%s.%s.%s"</string>
+    <string name="month_day_year">%-e. %B %Y</string>
+    <string name="time_of_day">%H:%M:%S</string>
+    <string name="date_and_time">%H:%M:%S %d.%m.%Y</string>
+    <string name="date_time">%1$s, %2$s</string>
+    <string name="time_date">%3$s, %1$s</string>
+    <string name="abbrev_month_day_year">%d.%m.%Y</string>
+    <string name="month_day">%-e. %B</string>
+    <string name="month">%-B</string>
+    <string name="month_year">%B %Y</string>
+    <string name="abbrev_month_day">%-e. %b</string>
+    <string name="abbrev_month">%-b</string>
+    <string name="abbrev_month_year">%b %Y</string>
+    <string name="time1_time2">%1$s - %2$s</string>
+    <string name="date1_date2">%2$s - %5$s</string>
+    <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string>
+    <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.</string>
+    <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s.%4$s - %6$s, %8$s.%7$s.%9$s</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s-%2$s-%3$s - %10$s %6$s, %9$s-%7$s-%8$s</string>
+    <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s. - %10$s %8$s.%7$s.</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s.%2$s. - %10$s %6$s, %8$s.%7$s.</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string>
+    <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string>
+    <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string>
+    <string name="time_wday_date">%1$s %2$s %3$s</string>
+    <string name="wday_date">%2$s %3$s</string>
+    <string name="time_wday">%1$s %2$s</string>
+    <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string>
+    <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string>
+    <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
+    <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s. %2$s - %10$s %6$s %8$s. %7$s</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string>
+    <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string>
+    <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s. %2$s - %6$s %8$s. %7$s</string>
+    <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
+    <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string>
+    <string name="short_format_month">%b</string>
+</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 9d20dff..91df5bc 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Выключение"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Выключение..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Телефон будет выключен."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Недавние"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Нет последних приложений."</string>
     <string name="global_actions" msgid="2406416831541615258">"Параметры телефона"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Контролировать способ и время блокировки экрана"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Удалить все данные"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Уничтожить все данные на телефоне без предупреждения путем сброса настроек"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Глобальный прокси-сервер"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Настройте глобальный прокси-сервер устройства, который будет использоваться при активной политике. Глобальный прокси-сервер должен настроить первый администратор устройства."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Домашний"</item>
     <item msgid="869923650527136615">"Мобильный"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Домашний"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Рабочий"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Другой"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"с помощью <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> с помощью <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Введите PIN-код"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Введите пароль для разблокировки"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Введите PIN-код для разблокировки"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Неверный PIN-код!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Для разблокировки нажмите \"Меню\", а затем 0."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Экстренная служба"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Вернуться к вызову"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Правильно!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Повторите попытку"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Повторите попытку"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Идет зарядка (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Заряжена."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-карта заблокирована."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Разблокировка SIM-карты…"</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Количество неудачных попыток ввода графического ключа разблокировки: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторите попытку через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Количество неудачных попыток ввода пароля: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторите попытку через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Количество неудачных попыток ввода PIN-кода: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторите попытку через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Количество неудачных попыток ввода графического ключа разблокировки: <xliff:g id="NUMBER_0">%d</xliff:g>. После <xliff:g id="NUMBER_1">%d</xliff:g> неудачных попыток вам будет предложено разблокировать телефон с помощью учетных данных Google.  "\n\n" Повторите попытку через <xliff:g id="NUMBER_2">%d</xliff:g> с."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Повторите попытку через <xliff:g id="NUMBER">%d</xliff:g> с."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Забыли графический ключ?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Перейти с этой страницы?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Нажмите \"ОК\", чтобы продолжить, или \"Отмена\", чтобы остаться на текущей странице."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Подтвердите"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Совет: нажмите дважды, чтобы увеличить и уменьшить масштаб."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Заполнить форму автоматически"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"считывать историю и закладки браузера"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Разрешает приложению считывать все URL, посещенные браузером, и все его закладки."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"записывать историю и закладки браузера"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Выбрать все"</string>
-    <string name="selectText" msgid="4862359311088898878">"Выберите слово"</string>
     <string name="cut" msgid="3092569408438626261">"Вырезать"</string>
     <string name="copy" msgid="2681946229533511987">"Копировать"</string>
     <string name="paste" msgid="5629880836805036433">"Вставить"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Копировать URL"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Выбрать текст..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Выбор текста"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Способ ввода"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Добавить \"<xliff:g id="WORD">%s</xliff:g>\" в словарь"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Изменить текст"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Операции с текстом"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Недостаточно места"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"Заканчивается место в памяти телефона."</string>
     <string name="ok" msgid="5970060430562524910">"ОК"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"ОК"</string>
     <string name="no" msgid="5141531044935541497">"Отмена"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Внимание"</string>
+    <string name="loading" msgid="1760724998928255250">"Загрузка..."</string>
     <string name="capital_on" msgid="1544682755514494298">"ВКЛ"</string>
     <string name="capital_off" msgid="6815870386972805832">"ВЫКЛ"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Что использовать?"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"L2TP/IPSec VPN (на основе предв. общ. ключа)"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"L2TP/IPSec VPN (на основе сертификата)"</string>
     <string name="upload_file" msgid="2897957172366730416">"Выбрать файл"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Не выбран файл"</string>
     <string name="reset" msgid="2448168080964209908">"Сбросить"</string>
     <string name="submit" msgid="1602335572089911941">"Отправить"</string>
-    <string name="description_star" msgid="2654319874908576133">"избранное"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Режим громкой связи включен"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Выберите для выхода из режима громкой связи."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"USB-модем или точка доступа Wi-Fi активны"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Нажмите для настройки"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Назад"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Далее"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Пропустить"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Активная передача данных"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Нажмите, чтобы узнать больше о мобильной передаче данных"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Превышен лимит на мобильные данные"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Нажмите, чтобы узнать больше о мобильной передаче данных"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Нет совпадений"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Найти на странице"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 совпадение"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> из <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"Отключение USB-накопителя..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"Отключение SD-карты..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"Очистка USB-накопителя..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"Сейчас USB-накопитель используется компьютером."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"Сейчас SD-карта используется компьютером."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Неизвестно состояние внешнего носителя."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index f2ea744..b8b6f64 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Stäng av"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Avslutar…"</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Din telefon stängs av."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"Senaste"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Inga nya program."</string>
     <string name="global_actions" msgid="2406416831541615258">"Telefonalternativ"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Kontrollera hur och när skärmlåset aktiveras"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Radera alla data"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Ta bort data från telefonen utan förvarning genom att återställa standardinställningarna"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ange global proxyserver"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ange vilken global proxyserver som ska användas när policyn är aktiverad. Endast den första enhetsadministratören anger den faktiska globala proxyservern."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Hem"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Startsida"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"Arbete"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Övrigt"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Ange PIN-kod"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Ange lösenord för att låsa upp"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Ange PIN-kod för att låsa upp"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Fel PIN-kod!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Tryck på Menu och sedan på 0 om du vill låsa upp."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nödsamtalsnummer"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Återgå till samtalet"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Korrekt!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Försök igen"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Försök igen"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Laddar (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Laddad."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kortet är låst."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Låser upp SIM-kort…"</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Du har angett din PIN-kod fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till kommer du att uppmanas att låsa upp telefonen med din Google-inloggning."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Glömt ditt grafiska lösenord?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Vill du lämna den här den här sidan?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Tryck på OK om du vill fortsätta eller på Avbryt om du vill vara kvar på den aktuella sidan."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Bekräfta"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"Tips! Dubbelklicka om du vill zooma in eller ut."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Autofyll formuläret"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"läsa webbläsarhistorik och bokmärken"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Tillåter att program läser alla webbadresser som webbläsaren har öppnat och alla webbläsarens bokmärken."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriva webbläsarhistorik och bokmärken"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Välj alla"</string>
-    <string name="selectText" msgid="4862359311088898878">"Välj ord"</string>
     <string name="cut" msgid="3092569408438626261">"Klipp ut"</string>
     <string name="copy" msgid="2681946229533511987">"Kopiera"</string>
     <string name="paste" msgid="5629880836805036433">"Klistra in"</string>
     <string name="copyUrl" msgid="2538211579596067402">"Kopiera webbadress"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Markera text..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Textmarkering"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Indatametod"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"Lägg till \"<xliff:g id="WORD">%s</xliff:g>\" i ordlistan"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Redigera text"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Textåtgärder"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Dåligt med utrymme"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"Telefonens lagringsutrymme håller på att ta slut."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Avbryt"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Obs!"</string>
+    <string name="loading" msgid="1760724998928255250">"Läser in..."</string>
     <string name="capital_on" msgid="1544682755514494298">"PÅ"</string>
     <string name="capital_off" msgid="6815870386972805832">"AV"</string>
     <string name="whichApplication" msgid="4533185947064773386">"Slutför åtgärd genom att använda"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"I förväg delad L2TP/IPSec VPN-nyckel"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"Certifikatsbaserad L2TP/IPSec VPN"</string>
     <string name="upload_file" msgid="2897957172366730416">"Välj fil"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Ingen fil har valts"</string>
     <string name="reset" msgid="2448168080964209908">"Återställ"</string>
     <string name="submit" msgid="1602335572089911941">"Skicka"</string>
-    <string name="description_star" msgid="2654319874908576133">"favorit"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Billäge aktiverat"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Välj om du vill avsluta billäge."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Internetdelning eller surfpunkt aktiverad"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Tryck om du vill konfigurera"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Tillbaka"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"Nästa"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Hoppa över"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Hög mobildataanvändning"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Tryck om du vill veta mer om mobildataanvändning"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Gränsen för mobildata har överskridits"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Tryck om du vill veta mer om mobildataanvändning"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Inga träffar"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Sök på sidan"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 träff"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> av <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"Demontera USB-lagringsenhet..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"Demonterar SD-kort..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"Raderar USB-lagring..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"USB-lagringsenheten används av en dator."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"SD-kortet används för närvarande av en dator."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Externa media i okänt tillstånd."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 0174024..5d3d55d 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"Kapat"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"Kapanıyor…"</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"Telefonunuz kapanacak."</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"En Son Görevler"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"Hiçbir yeni uygulama yok."</string>
     <string name="global_actions" msgid="2406416831541615258">"Telefon seçenekleri"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"Ekranın nasıl ve ne zaman kilitlendiğini kontrol edin"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"Tüm verileri sil"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"Fabrika verilerine sıfırlama işlemi gerçekleştirerek telefondaki verileri uyarıda bulunmadan silin"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Cihaz genelinde geçerli proxy\'i ayarla"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Politika etkin olduğunda kullanılacak cihaz genelinde geçerli proxy\'yi ayarlayın. Etkin genel proxy\'yi yalnızca ilk cihaz yöneticisi ayarlar."</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"Ev"</item>
     <item msgid="869923650527136615">"Mobil"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"Ev"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"İş"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"Diğer"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"<xliff:g id="SOURCE">%1$s</xliff:g> aracılığıyla"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="SOURCE">%2$s</xliff:g> ile <xliff:g id="DATE">%1$s</xliff:g> tarihinde"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN kodunu gir"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Kilidi açmak için şifreyi girin"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Kilidi açmak için PIN\'i girin"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Yanlış PIN kodu!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Kilidi açmak için önce Menü\'ye, sonra 0\'a basın."</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Acil durum numarası"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Çağrıya dön"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Doğru!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Üzgünüz, lütfen yeniden deneyin"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"Maalesef, tekrar deneyin"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"Şarj oluyor (<xliff:g id="PERCENT">%%</xliff:g><xliff:g id="NUMBER">%d</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"Şarj oldu."</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM kart kilitli."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM kart kilidi açılıyor…"</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. "\n\n"Lütfen <xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde yeniden deneyin."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Şifrenizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış girdiniz. "\n\n"Lütfen <xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde yeniden deneyin."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"PIN\'inizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış girdiniz. "\n\n"Lütfen <xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde yeniden deneyin."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız denemeden sonra telefonunuzu Google oturum açma bilgilerinizi kullanarak açmanız istenir."\n\n" Lütfen <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde yeniden deneyin."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> saniye içinde yeniden deneyin."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Deseni unuttunuz mu?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"Bu sayfadan ayrılıyor musunuz?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Devam etmek için Tamam\'ı, sayfada kalmak için İptal\'i tıklatın."</string>
     <string name="save_password_label" msgid="6860261758665825069">"Onayla"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"İpucu: Yakınlaştırmak ve uzaklaştırmak için iki kez hafifçe vurun."</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"Bu formu otomatik doldur"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"Tarayıcı geçmişini ve favorileri oku"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Uygulamaya Tarayıcının ziyaret etmiş olduğu tüm URL\'leri ve Tarayıcının tüm favorilerini okuma izni verir."</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"Tarayıcı geçmişini ve favorileri yaz"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"Tümünü seç"</string>
-    <string name="selectText" msgid="4862359311088898878">"Kelime seçin"</string>
     <string name="cut" msgid="3092569408438626261">"Kes"</string>
     <string name="copy" msgid="2681946229533511987">"Kopyala"</string>
     <string name="paste" msgid="5629880836805036433">"Yapıştır"</string>
     <string name="copyUrl" msgid="2538211579596067402">"URL\'yi kopyala"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"Metin seç..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"Metin seçimi"</string>
     <string name="inputMethod" msgid="1653630062304567879">"Giriş yöntemi"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"\"<xliff:g id="WORD">%s</xliff:g>\" kelimesini sözlüğe ekle"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"Metin düzenle"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"Metin eylemleri"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"Yer az"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"Telefonun depolama alanı azalıyor."</string>
     <string name="ok" msgid="5970060430562524910">"Tamam"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"Tamam"</string>
     <string name="no" msgid="5141531044935541497">"İptal"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Dikkat"</string>
+    <string name="loading" msgid="1760724998928255250">"Yükleniyor..."</string>
     <string name="capital_on" msgid="1544682755514494298">"AÇIK"</string>
     <string name="capital_off" msgid="6815870386972805832">"KAPALI"</string>
     <string name="whichApplication" msgid="4533185947064773386">"İşlemi şunu kullanarak tamamla"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"L2TP/IPSec VPN temelli önceden paylaşılmış anahtar"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"L2TP/IPSec VPN temelli sertifika"</string>
     <string name="upload_file" msgid="2897957172366730416">"Dosya seç"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"Seçili dosya yok"</string>
     <string name="reset" msgid="2448168080964209908">"Sıfırla"</string>
     <string name="submit" msgid="1602335572089911941">"Gönder"</string>
-    <string name="description_star" msgid="2654319874908576133">"favori"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Araba modu etkin"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"Araba modundan çıkmak için seçin."</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"Doğrudan bağlantı veya ortak erişim noktası etkin"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"Yapılandırmak için dokunun"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"Geri"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"İleri"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"Atla"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"Yüksek düzeyde mobil veri kullanımı"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Mobil veri kullanımı hakkında daha fazla bilgi edinmek için dokunun"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"Mobil veri limiti aşıldı"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"Mobil veri kullanımı hakkında daha fazla bilgi edinmek için dokunun"</string>
+    <string name="no_matches" msgid="8129421908915840737">"Eşleşme yok"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Sayfada bul"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 eşleşme"</item>
+    <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> / <xliff:g id="TOTAL">%d</xliff:g>"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"USB depolama biriminin bağlantısı kesiliyor..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"SD kartın bağlantısı kesiliyor..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"USB dep brm silinyr..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"USB depolama birimi, şu anda bir bilgisayar tarafından kullanılıyor."</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"SD kart şu anda bir bilgisayar tarafından kullanılıyor."</string>
     <string name="media_unknown_state" msgid="729192782197290385">"Harici medyanın durumu bilinmiyor."</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-xlarge/config.xml
new file mode 100644
index 0000000..9504d04
--- /dev/null
+++ b/core/res/res/values-xlarge/config.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <bool name="config_statusBarCanHide">false</bool>
+
+    <!-- see comment in values/config.xml -->
+    <integer name="config_longPressOnPowerBehavior">2</integer>
+
+    <!-- Show sliding tab before lockscreen -->
+    <bool name="config_enableSlidingTabFirst">false</bool>
+    <!-- Enable lockscreen rotation -->
+    <bool name="config_enableLockScreenRotation">true</bool>
+
+    <!-- Enables 3d task switcher on xlarge device -->
+    <bool name="config_enableRecentApps3D">true</bool>
+
+</resources>
+
diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml
new file mode 100644
index 0000000..115cb30
--- /dev/null
+++ b/core/res/res/values-xlarge/dimens.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.xml
+**
+** 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.
+*/
+-->
+<resources>
+    <dimen name="status_bar_height">48dip</dimen>
+    <!-- Height of the status bar -->
+    <dimen name="status_bar_icon_size">48dip</dimen>
+    <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
+    <!-- Margin for permanent screen decorations at the bottom. -->
+    <dimen name="screen_margin_bottom">48dip</dimen>
+    
+    <!-- Default height of a key in the password keyboard for alpha -->
+    <dimen name="password_keyboard_key_height_alpha">0.35in</dimen>
+    <!-- Default height of a key in the password keyboard for numeric -->
+    <dimen name="password_keyboard_key_height_numeric">0.47in</dimen>
+
+    <!-- The width that is used when creating thumbnails of applications. -->
+    <dimen name="thumbnail_width">256dp</dimen>
+    <!-- The height that is used when creating thumbnails of applications. -->
+    <dimen name="thumbnail_height">255dp</dimen>
+</resources>
diff --git a/core/res/res/values-xlarge/strings.xml b/core/res/res/values-xlarge/strings.xml
new file mode 100644
index 0000000..fc20be6
--- /dev/null
+++ b/core/res/res/values-xlarge/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Do not translate.  WebView User Agent targeted content -->
+    <string name="web_user_agent_target_content" translatable="false"></string>
+
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-xlarge/styles.xml b/core/res/res/values-xlarge/styles.xml
new file mode 100644
index 0000000..4692656
--- /dev/null
+++ b/core/res/res/values-xlarge/styles.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- Status Bar Styles -->
+
+    <style name="TextAppearance.StatusBar">
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+    </style>
+    <style name="TextAppearance.StatusBar.Ticker">
+    </style>
+    <style name="TextAppearance.StatusBar.Title">
+        <item name="android:textStyle">bold</item>
+    </style>
+
+    <style name="TextAppearance.StatusBar.Icon">
+    </style>
+    <style name="TextAppearance.StatusBar.EventContent">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+    <style name="TextAppearance.StatusBar.EventContent.Title">
+        <item name="android:textSize">18sp</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 6832787..00dc242 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"关机"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"正在关机..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"您的手机会关机。"</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"近期任务"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"没有最近的应用程序。"</string>
     <string name="global_actions" msgid="2406416831541615258">"手机选项"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"控制锁定屏幕的方式和时间"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"清除所有数据"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"恢复出厂设置时,将擦除手机上的数据而不发送警告"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"设置设备全局代理"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"请设置在启用政策的情况下要使用的设备全局代理。只有第一设备管理员才可设置有效的全局代理。"</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"住宅"</item>
     <item msgid="869923650527136615">"手机"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"住宅"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"单位"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"其他"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"通过 <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"时间:<xliff:g id="DATE">%1$s</xliff:g>,方式:<xliff:g id="SOURCE">%2$s</xliff:g>"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"输入 PIN 码"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"输入密码进行解锁"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"输入 PIN 进行解锁"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"PIN 码不正确!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"要解锁,请先按 MENU 再按 0。"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"急救或报警电话"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"返回通话"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"正确!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"很抱歉,请重试"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"很抱歉,请重试"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"正在充电 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"已充满。"</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM 卡被锁定"</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"正在解锁 SIM 卡..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次绘错了自己的解锁图案。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地输入了密码。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地输入了 PIN。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次绘错了自己的解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统会要求您使用自己的 Google 登录信息解锁手机。"\n\n"请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> 秒后重试。"</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"忘记了图案?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"是否从该页面导航至它处?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"选择“确定”继续,或选择“取消”留在当前页面。"</string>
     <string name="save_password_label" msgid="6860261758665825069">"确认"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"提示:点按两次可放大和缩小。"</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"自动填充此表单"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"读取浏览器的历史记录和书签"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"允许应用程序读取用浏览器访问过的所有网址,以及浏览器的所有书签。"</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"写入浏览器的历史记录和书签"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"全选"</string>
-    <string name="selectText" msgid="4862359311088898878">"选择文字"</string>
     <string name="cut" msgid="3092569408438626261">"剪切"</string>
     <string name="copy" msgid="2681946229533511987">"复制"</string>
     <string name="paste" msgid="5629880836805036433">"粘贴"</string>
     <string name="copyUrl" msgid="2538211579596067402">"复制网址"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"选择文字..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"文字选择"</string>
     <string name="inputMethod" msgid="1653630062304567879">"输入法"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"将“<xliff:g id="WORD">%s</xliff:g>”添加到词典"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"编辑文字"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"文字操作"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"存储空间不足"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"手机内存空间所剩不多了。"</string>
     <string name="ok" msgid="5970060430562524910">"确定"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"确定"</string>
     <string name="no" msgid="5141531044935541497">"取消"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"注意"</string>
+    <string name="loading" msgid="1760724998928255250">"正在载入..."</string>
     <string name="capital_on" msgid="1544682755514494298">"打开"</string>
     <string name="capital_off" msgid="6815870386972805832">"关闭"</string>
     <string name="whichApplication" msgid="4533185947064773386">"使用以下方式发送"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"基于预共享密钥的 L2TP/IPSec VPN"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"基于证书的 L2TP/IPSec VPN"</string>
     <string name="upload_file" msgid="2897957172366730416">"选择文件"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"未选定任何文件"</string>
     <string name="reset" msgid="2448168080964209908">"重置"</string>
     <string name="submit" msgid="1602335572089911941">"提交"</string>
-    <string name="description_star" msgid="2654319874908576133">"收藏"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"已启用车载模式"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"选择退出车载模式"</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"USB 绑定或热点已启用"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"触摸可进行配置"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"上一步"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"下一步"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"跳过"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"手机流量过多"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"轻触以了解有关手机流量详情"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"已超出手机数据上限"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"轻触以了解有关手机流量详情"</string>
+    <string name="no_matches" msgid="8129421908915840737">"无匹配项"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"在网页上查找"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 个匹配项"</item>
+    <item quantity="other" msgid="4641872797067609177">"第 <xliff:g id="INDEX">%d</xliff:g> 项,共 <xliff:g id="TOTAL">%d</xliff:g> 项"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"正在卸载 USB 存储设备..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"正在卸载 SD 卡..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"正在格式化 USB 存储设备"</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"某台计算机正在使用 USB 存储设备。"</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"目前有计算机正在使用 SD 卡。"</string>
     <string name="media_unknown_state" msgid="729192782197290385">"外部媒体处于未知状态。"</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index f8d2b4b..a6c77c9 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -135,6 +135,8 @@
     <string name="power_off" msgid="4266614107412865048">"關機"</string>
     <string name="shutdown_progress" msgid="2281079257329981203">"關機中..."</string>
     <string name="shutdown_confirm" msgid="649792175242821353">"手機即將關機。"</string>
+    <!-- no translation found for shutdown_confirm_question (6656441286856415014) -->
+    <skip />
     <string name="recent_tasks_title" msgid="3691764623638127888">"最新的"</string>
     <string name="no_recent_tasks" msgid="279702952298056674">"最近沒有存取應用程式。"</string>
     <string name="global_actions" msgid="2406416831541615258">"電話選項"</string>
@@ -439,6 +441,8 @@
     <string name="policydesc_forceLock" msgid="5696964126226028442">"控制鎖定螢幕的方式和時間"</string>
     <string name="policylab_wipeData" msgid="3910545446758639713">"清除所有資料"</string>
     <string name="policydesc_wipeData" msgid="7669895333814222586">"執行重設為原廠設定時,系統會直接清除手機資料而不提出警告"</string>
+    <string name="policylab_setGlobalProxy" msgid="2784828293747791446">"設定裝置全域 Proxy"</string>
+    <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"設定政策啟用時所要使用的裝置全域 Proxy,只有第一個裝置管理員所設定的全域 Proxy 具有效力。"</string>
   <string-array name="phoneTypes">
     <item msgid="8901098336658710359">"住家電話"</item>
     <item msgid="869923650527136615">"行動電話"</item>
@@ -536,10 +540,9 @@
     <string name="sipAddressTypeHome" msgid="6093598181069359295">"住家"</string>
     <string name="sipAddressTypeWork" msgid="6920725730797099047">"公司"</string>
     <string name="sipAddressTypeOther" msgid="4408436162950119849">"其他"</string>
-    <string name="contact_status_update_attribution" msgid="5112589886094402795">"透過 <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
-    <string name="contact_status_update_attribution_with_date" msgid="5945386376369979909">"<xliff:g id="DATE">%1$s</xliff:g>透過「<xliff:g id="SOURCE">%2$s</xliff:g>」"</string>
     <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"輸入 PIN 碼"</string>
     <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"輸入密碼即可解鎖"</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"輸入 PIN 進行解鎖"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"PIN 碼錯誤!"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按 Menu 鍵,然後按 0。"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急電話號碼"</string>
@@ -552,6 +555,7 @@
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"返回通話"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"正確!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"很抱歉,請再試一次"</string>
+    <string name="lockscreen_password_wrong" msgid="6237443657358168819">"很抱歉,請再試一次"</string>
     <string name="lockscreen_plugged_in" msgid="613343852842944435">"正在充電 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
     <string name="lockscreen_charged" msgid="4938930459620989972">"充電完成。"</string>
     <string name="lockscreen_battery_short" msgid="3617549178603354656">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
@@ -566,6 +570,8 @@
     <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM 卡已鎖定。"</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"解鎖 SIM 卡中..."</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"畫出解鎖圖形已錯誤 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n" 請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再嘗試。"</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次輸入不正確的密碼。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次輸入不正確的 PIN。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
     <string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"畫出解鎖圖形已錯誤 <xliff:g id="NUMBER_0">%d</xliff:g> 次。再錯誤 <xliff:g id="NUMBER_1">%d</xliff:g> 次後,系統會要求使用 Google 登入來解鎖。"\n\n" 請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> 秒後再試一次。"</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"忘記解鎖圖形?"</string>
@@ -594,6 +600,7 @@
     <string name="js_dialog_before_unload" msgid="1901675448179653089">"離開此頁?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n" 選取 [確定] 離開此頁;或 [取消] 留在此頁。"</string>
     <string name="save_password_label" msgid="6860261758665825069">"確認"</string>
     <string name="double_tap_toast" msgid="1068216937244567247">"提示:輕按兩下可放大縮小。"</string>
+    <string name="autofill_this_form" msgid="8940110866775097494">"自動填寫此表單"</string>
     <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"讀取瀏覽器的記錄與書籤"</string>
     <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"允許應用程式讀取瀏覽器曾經造訪過的所有網址,以及瀏覽器的所有書籤。"</string>
     <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"寫入瀏覽器的記錄與書籤"</string>
@@ -712,14 +719,14 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"全部選取"</string>
-    <string name="selectText" msgid="4862359311088898878">"選取字詞"</string>
     <string name="cut" msgid="3092569408438626261">"剪下"</string>
     <string name="copy" msgid="2681946229533511987">"複製"</string>
     <string name="paste" msgid="5629880836805036433">"貼上"</string>
     <string name="copyUrl" msgid="2538211579596067402">"複製網址"</string>
+    <string name="selectTextMode" msgid="6738556348861347240">"選取文字..."</string>
+    <string name="textSelectionCABTitle" msgid="5236850394370820357">"選取文字"</string>
     <string name="inputMethod" msgid="1653630062304567879">"輸入方式"</string>
-    <string name="addToDictionary" msgid="8793624991686948709">"將「<xliff:g id="WORD">%s</xliff:g>」新增至字典"</string>
-    <string name="editTextMenuTitle" msgid="1672989176958581452">"編輯文字"</string>
+    <string name="editTextMenuTitle" msgid="4909135564941815494">"文字動作"</string>
     <string name="low_internal_storage_view_title" msgid="1399732408701697546">"儲存空間即將不足"</string>
     <string name="low_internal_storage_view_text" msgid="635106544616378836">"手機儲存空間即將不足。"</string>
     <string name="ok" msgid="5970060430562524910">"確定"</string>
@@ -727,6 +734,7 @@
     <string name="yes" msgid="5362982303337969312">"確定"</string>
     <string name="no" msgid="5141531044935541497">"取消"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"注意"</string>
+    <string name="loading" msgid="1760724998928255250">"載入中..."</string>
     <string name="capital_on" msgid="1544682755514494298">"開啟"</string>
     <string name="capital_off" msgid="6815870386972805832">"關閉"</string>
     <string name="whichApplication" msgid="4533185947064773386">"完成操作需使用"</string>
@@ -880,17 +888,28 @@
     <string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"採用預先共用金鑰的 L2TP/IPSec VPN"</string>
     <string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"採用憑證的 L2TP/IPSec VPN"</string>
     <string name="upload_file" msgid="2897957172366730416">"選擇檔案"</string>
+    <string name="no_file_chosen" msgid="6363648562170759465">"未選擇任何檔案"</string>
     <string name="reset" msgid="2448168080964209908">"重設"</string>
     <string name="submit" msgid="1602335572089911941">"提交"</string>
-    <string name="description_star" msgid="2654319874908576133">"我的最愛"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"已啟用車用模式"</string>
     <string name="car_mode_disable_notification_message" msgid="668663626721675614">"選取結束車用模式。"</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"數據連線或無線基地台已啟用"</string>
     <string name="tethered_notification_message" msgid="3067108323903048927">"輕觸以設定"</string>
+    <string name="back_button_label" msgid="2300470004503343439">"返回"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"繼續"</string>
+    <string name="skip_button_label" msgid="1275362299471631819">"略過"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"高行動資料用量"</string>
     <string name="throttle_warning_notification_message" msgid="2609734763845705708">"輕觸即可瞭解更多有關行動資料用量的詳細資訊"</string>
     <string name="throttled_notification_title" msgid="6269541897729781332">"已達行動資料上限"</string>
     <string name="throttled_notification_message" msgid="4712369856601275146">"輕觸即可瞭解更多有關行動資料用量的詳細資訊"</string>
+    <string name="no_matches" msgid="8129421908915840737">"沒有相符項目"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"在頁面中尋找"</string>
+  <plurals name="matches_found">
+    <item quantity="one" msgid="8167147081136579439">"1 個相符項目"</item>
+    <item quantity="other" msgid="4641872797067609177">"第 <xliff:g id="INDEX">%d</xliff:g> 個相符項目 (共 <xliff:g id="TOTAL">%d</xliff:g> 個相符項目)"</item>
+  </plurals>
+    <!-- no translation found for action_mode_done (7217581640461922289) -->
+    <skip />
     <string name="progress_unmounting" product="nosdcard" msgid="535863554318797377">"正在卸載 USB 儲存裝置..."</string>
     <string name="progress_unmounting" product="default" msgid="5556813978958789471">"正在卸載 SD 卡..."</string>
     <string name="progress_erasing" product="nosdcard" msgid="4183664626203056915">"正在清除 USB 儲存裝置..."</string>
@@ -904,4 +923,8 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"電腦目前正在使用 USB 儲存裝置。"</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"電腦目前正在使用 SD 卡。"</string>
     <string name="media_unknown_state" msgid="729192782197290385">"外部媒體狀態不明。"</string>
+    <!-- no translation found for share (1778686618230011964) -->
+    <skip />
+    <!-- no translation found for find (4808270900322985960) -->
+    <skip />
 </resources>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index e799527..3e546a1 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -106,4 +106,16 @@
         <item>3</item>
     </integer-array>
 
+    <!-- Used in LocalePicker -->
+    <string-array translatable="false" name="special_locale_codes">
+        <item>zh_CN</item>
+        <item>zh_TW</item>
+    </string-array>
+
+    <!-- Used in LocalePicker -->
+    <string-array translatable="false" name="special_locale_names">
+        <item>中文 (简体)</item>
+        <item>中文 (繁體)</item>
+    </string-array>
+
 </resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 6370696..b22d833 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -14,8 +14,8 @@
      limitations under the License.
 -->
 
-<!-- Formatting note: terminate all comments with a period, to avoid breaking 
-     the documentation output. To suppress comment lines from the documentation 
+<!-- Formatting note: terminate all comments with a period, to avoid breaking
+     the documentation output. To suppress comment lines from the documentation
      output, insert an eat-comment element after the comment lines.
 -->
 
@@ -46,6 +46,7 @@
              theme does not set this value, meaning it is based on whether the
              window is floating. -->
         <attr name="backgroundDimEnabled" format="boolean" />
+        
 
         <!-- =========== -->
         <!-- Text styles -->
@@ -95,6 +96,15 @@
 
         <!-- Text color for urls in search suggestions, used by things like global search and the browser. @hide -->
         <attr name="textColorSearchUrl" format="reference|color" />
+
+        <!-- Color of highlighted text, when used in a light theme. @hide -->
+        <attr name="textColorHighlightInverse" format="reference|color" />
+        <!-- Color of link text (URLs), when used in a light theme. @hide -->
+        <attr name="textColorLinkInverse" format="reference|color" />
+
+        <!-- Color of list item text in alert dialogs. -->
+        <attr name="textColorAlertDialogListItem" format="reference|color" />
+        
         <!-- Search widget more corpus result item background. -->
         <attr name="searchWidgetCorpusItemBackground" format="reference|color" />
 
@@ -117,10 +127,20 @@
         <!-- Text color, typeface, size, and style for system search result subtitle. Defaults to primary inverse text color. -->
         <attr name="textAppearanceSearchResultSubtitle" format="reference" />
 
-
         <!-- Text color, typeface, size, and style for the text inside of a button. -->
         <attr name="textAppearanceButton" format="reference" />
 
+        <!-- Text color, typeface, size, and style for the text inside of a popup menu. -->
+        <attr name="textAppearanceLargePopupMenu" format="reference" />
+
+        <!-- Text color, typeface, size, and style for small text inside of a popup menu. -->
+        <attr name="textAppearanceSmallPopupMenu" format="reference" />
+
+        <!-- EditText text foreground color. -->
+        <attr name="editTextColor" format="reference|color" />
+        <!-- EditText background drawable. -->
+        <attr name="editTextBackground" format="reference" />
+
         <!-- A styled string, specifying the style to be used for showing
              inline candidate text when composing with an input method.  The
              text itself will be ignored, but the style spans will be applied
@@ -137,6 +157,12 @@
         <!-- Drawable to use for single choice indicators. -->
         <attr name="listChoiceIndicatorSingle" format="reference" />
 
+        <!-- Drawable used as a background for selected list items. -->
+        <attr name="listChoiceBackgroundIndicator" format="reference" />
+
+        <!-- Drawable used as a background for activated items. -->
+        <attr name="activatedBackgroundIndicator" format="reference" />
+
         <!-- ============= -->
         <!-- Button styles -->
         <!-- ============= -->
@@ -174,6 +200,8 @@
         <!-- The list item height for search results. @hide -->
         <attr name="searchResultListItemHeight" format="dimension" />
         <attr name="listDivider" format="reference" />
+        <!-- The list divider used in alert dialogs. -->
+        <attr name="listDividerAlertDialog" format="reference" />
         <!-- TextView style for list separators. -->
         <attr name="listSeparatorTextViewStyle" format="reference" />
         <!-- The preferred left padding for an expandable list item (for child-specific layouts,
@@ -244,6 +272,19 @@
              {@link android.R.styleable#WindowAnimation}. -->
         <attr name="windowAnimationStyle" format="reference" />
 
+        <!-- Flag indicating whether this window should have an Action Bar
+             in place of the usual title bar. -->
+        <attr name="windowActionBar" format="boolean" />
+
+        <!-- Flag indicating whether this window's Action Bar should overlay
+             application content. Does nothing if the window would not
+             have an Action Bar. -->
+        <attr name="windowActionBarOverlay" format="boolean" />
+
+        <!-- Flag indicating whether action modes should overlay window content
+             when there is not reserved space for their UI (such as an Action Bar). -->
+        <attr name="windowActionModeOverlay" format="boolean" />
+
         <!-- Defines the default soft input state that this window would
              like when it is displayed. -->
         <attr name="windowSoftInputMode">
@@ -285,6 +326,9 @@
                  need to close the input area to get at and interact with
                  parts of the window. -->
             <flag name="adjustPan" value="0x20" />
+            <!-- Don't resize <em>or</em> pan the window to make room for the
+                 soft input area; the window is never adjusted for it. -->
+            <flag name="adjustNothing" value="0x30" />
         </attr>
 
         <!-- Flag allowing you to disable the preview animation for a window.
@@ -306,6 +350,8 @@
         <!-- ============ -->
         <eat-comment />
         <attr name="alertDialogStyle" format="reference" />
+        <attr name="alertDialogButtonGroupStyle" format="reference" />
+        <attr name="alertDialogCenterButtons" format="boolean" />
 
         <!-- ============ -->
         <!-- Panel styles -->
@@ -390,6 +436,12 @@
         <attr name="horizontalScrollViewStyle" format="reference" />
         <!-- Default Spinner style. -->
         <attr name="spinnerStyle" format="reference" />
+        <!-- Default dropdown Spinner style. -->
+        <attr name="dropDownSpinnerStyle" format="reference" />
+        <!-- Default ActionBar dropdown style. -->
+        <attr name="actionDropDownStyle" format="reference" />
+        <!-- Default action button style. -->
+        <attr name="actionButtonStyle" format="reference" />
         <!-- Default Star style. -->
         <attr name="starStyle" format="reference" />
         <!-- Default TabWidget style. -->
@@ -427,8 +479,48 @@
         <!-- Reference to a style that will be used for the window containing a text
              selection anchor. -->
         <attr name="textSelectHandleWindowStyle" format="reference" />
+        <!-- Default ListPopupWindow style. -->
+        <attr name="listPopupWindowStyle" format="reference" />
+        <!-- Default PopupMenu style. -->
+        <attr name="popupMenuStyle" format="reference" />
 
         <!-- =================== -->
+        <!-- Action bar styles   -->
+        <!-- =================== -->
+        <eat-comment />
+        <!-- Default style for tabs within an action bar -->
+        <attr name="actionBarTabStyle" format="reference" />
+        <attr name="actionBarTabBarStyle" format="reference" />
+        <attr name="actionBarTabTextStyle" format="reference" />
+        <attr name="actionOverflowButtonStyle" format="reference" />
+        <!-- Reference to a style for the Action Bar -->
+        <attr name="actionBarStyle" format="reference" />
+        <!-- Size of the Action Bar, including the contextual
+             bar used to present Action Modes. -->
+        <attr name="actionBarSize" format="dimension" >
+            <enum name="wrap_content" value="0" />
+        </attr>
+
+
+        <!-- =================== -->
+        <!-- Action mode styles  -->
+        <!-- =================== -->
+        <eat-comment />
+        <attr name="actionModeStyle" format="reference" />
+        <attr name="actionModeCloseButtonStyle" format="reference" />
+        <!-- Background drawable to use for action mode UI -->
+        <attr name="actionModeBackground" format="reference" />
+        <!-- Drawable to use for the close action mode button -->
+        <attr name="actionModeCloseDrawable" format="reference" />
+
+        <!-- Drawable to use for the Cut action button in Contextual Action Bar -->
+        <attr name="actionModeCutDrawable" format="reference" />
+        <!-- Drawable to use for the Copy action button in Contextual Action Bar -->
+        <attr name="actionModeCopyDrawable" format="reference" />
+        <!-- Drawable to use for the Paste action button in Contextual Action Bar -->
+        <attr name="actionModePasteDrawable" format="reference" />
+        
+        <!-- =================== -->
         <!-- Preference styles   -->
         <!-- =================== -->
         <eat-comment />
@@ -468,7 +560,31 @@
         <!-- Reference to a drawable that will be used to display a text selection
              anchor for positioning the cursor within text. -->
         <attr name="textSelectHandle" format="reference" />
+        <!-- The layout of the view that is displayed on top of the cursor to paste inside a
+             TextEdit field. -->
+        <attr name="textEditPasteWindowLayout" format="reference" />
+        <!-- Variation of textEditPasteWindowLayout displayed when the clipboard is empty. -->
+        <attr name="textEditNoPasteWindowLayout" format="reference" />
 
+        <!-- Theme to use for dialogs spawned from this theme. -->
+        <attr name="dialogTheme" format="reference" />
+        <!-- Theme to use for alert dialogs spawned from this theme. -->
+        <attr name="alertDialogTheme" format="reference" />
+
+        <!-- Drawable to use for vertical dividers. -->
+        <attr name="dividerVertical" format="reference" />
+
+        <!-- Style for button groups -->
+        <attr name="buttonGroupStyle" format="reference" />
+
+        <!-- Drawable for group button backgrounds -->
+        <attr name="groupButtonBackground" format="reference" />
+
+        <!-- Background drawable for standalone items that need focus/pressed states. -->
+        <attr name="selectableItemBackground" format="reference" />
+
+        <!-- SearchView dropdown background -->
+        <attr name="searchDropdownBackground" format="reference" />
     </declare-styleable>
 
     <!-- **************************************************************** -->
@@ -514,6 +630,9 @@
     <!-- Color of link text (URLs). -->
     <attr name="textColorLink" format="reference|color" />
 
+    <!-- Height of a line of text. -->
+    <attr name="textLineHeight" format="dimension" />
+
     <!-- Where to ellipsize text. -->
     <attr name="ellipsize">
         <enum name="none" value="0" />
@@ -622,6 +741,14 @@
              {@link android.text.InputType#TYPE_CLASS_TEXT} |
              {@link android.text.InputType#TYPE_TEXT_VARIATION_PHONETIC}. -->
         <flag name="textPhonetic" value="0x000000c1" />
+        <!-- Text that will be used as an e-mail address on a web form.  Corresponds to
+             {@link android.text.InputType#TYPE_CLASS_TEXT} |
+             {@link android.text.InputType#TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS}. -->
+        <flag name="textWebEmailAddress" value="0x000000d1" />
+        <!-- Text that will be used as a password on a web form.  Corresponds to
+             {@link android.text.InputType#TYPE_CLASS_TEXT} |
+             {@link android.text.InputType#TYPE_TEXT_VARIATION_WEB_PASSWORD}. -->
+        <flag name="textWebPassword" value="0x000000e1" />
         <!-- A numeric only field.  Corresponds to
              {@link android.text.InputType#TYPE_CLASS_NUMBER}. -->
         <flag name="number" value="0x00000002" />
@@ -696,6 +823,40 @@
              Corresponds to
              {@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE}. -->
         <flag name="actionDone" value="0x00000006" />
+        <!-- The action key performs a "previous"
+             operation, taking the user to the previous field that will accept text.
+             Corresponds to
+             {@link android.view.inputmethod.EditorInfo#IME_ACTION_PREVIOUS}. -->
+        <flag name="actionPrevious" value="0x00000007" />
+        <!-- Used to request that the IME never go
+             into fullscreen mode.  Applications need to be aware that the flag is not
+             a guarantee, and not all IMEs will respect it.
+             <p>Corresponds to
+             {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_FULLSCREEN}. -->
+        <flag name="flagNoFullscreen" value="0x2000000" />
+        <!-- Like flagNavigateNext, but
+             specifies there is something interesting that a backward navigation
+             can focus on.  If the user selects the IME's facility to backward
+             navigate, this will show up in the application as an actionPrevious
+             at {@link android.view.inputmethod.InputConnection#performEditorAction(int)
+             InputConnection.performEditorAction(int)}.
+             <p>Corresponds to
+             {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_FULLSCREEN}. -->
+        <flag name="flagNavigatePrevious" value="0x4000000" />
+        <!-- Used to specify that there is something
+             interesting that a forward navigation can focus on. This is like using
+             actionNext, except allows the IME to be multiline (with
+             an enter key) as well as provide forward navigation.  Note that some
+             IMEs may not be able to do this, especially when running on a small
+             screen where there is little space.  In that case it does not need to
+             present a UI for this option.  Like actionNext, if the
+             user selects the IME's facility to forward navigate, this will show up
+             in the application at
+             {@link android.view.inputmethod.InputConnection#performEditorAction(int)
+             InputConnection.performEditorAction(int)}.
+             <p>Corresponds to
+             {@link android.view.inputmethod.EditorInfo#IME_FLAG_NAVIGATE_NEXT}. -->
+        <flag name="flagNavigateNext" value="0x8000000" />
         <!-- Used to specify that the IME does not need
              to show its extracted text UI.  For input methods that may be fullscreen,
              often when in landscape mode, this allows them to be smaller and let part
@@ -953,6 +1114,60 @@
         <enum name="KEYCODE_BUTTON_START" value="108" />
         <enum name="KEYCODE_BUTTON_SELECT" value="109" />
         <enum name="KEYCODE_BUTTON_MODE" value="110" />
+        <enum name="KEYCODE_ESCAPE" value="111" />
+        <enum name="KEYCODE_FORWARD_DEL" value="112" />
+        <enum name="KEYCODE_CTRL_LEFT" value="113" />
+        <enum name="KEYCODE_CTRL_RIGHT" value="114" />
+        <enum name="KEYCODE_CAPS_LOCK" value="115" />
+        <enum name="KEYCODE_SCROLL_LOCK" value="116" />
+        <enum name="KEYCODE_META_LEFT" value="117" />
+        <enum name="KEYCODE_META_RIGHT" value="118" />
+        <enum name="KEYCODE_FUNCTION" value="119" />
+        <enum name="KEYCODE_SYSRQ" value="120" />
+        <enum name="KEYCODE_BREAK" value="121" />
+        <enum name="KEYCODE_MOVE_HOME" value="122" />
+        <enum name="KEYCODE_MOVE_END" value="123" />
+        <enum name="KEYCODE_INSERT" value="124" />
+        <enum name="KEYCODE_FORWARD" value="125" />
+        <enum name="KEYCODE_MEDIA_PLAY" value="126" />
+        <enum name="KEYCODE_MEDIA_PAUSE" value="127" />
+        <enum name="KEYCODE_MEDIA_CLOSE" value="128" />
+        <enum name="KEYCODE_MEDIA_EJECT" value="129" />
+        <enum name="KEYCODE_MEDIA_RECORD" value="130" />
+        <enum name="KEYCODE_F1" value="131" />
+        <enum name="KEYCODE_F2" value="132" />
+        <enum name="KEYCODE_F3" value="133" />
+        <enum name="KEYCODE_F4" value="134" />
+        <enum name="KEYCODE_F5" value="135" />
+        <enum name="KEYCODE_F6" value="136" />
+        <enum name="KEYCODE_F7" value="137" />
+        <enum name="KEYCODE_F8" value="138" />
+        <enum name="KEYCODE_F9" value="139" />
+        <enum name="KEYCODE_F10" value="140" />
+        <enum name="KEYCODE_F11" value="141" />
+        <enum name="KEYCODE_F12" value="142" />
+        <enum name="KEYCODE_NUM_LOCK" value="143" />
+        <enum name="KEYCODE_NUMPAD_0" value="144" />
+        <enum name="KEYCODE_NUMPAD_1" value="145" />
+        <enum name="KEYCODE_NUMPAD_2" value="146" />
+        <enum name="KEYCODE_NUMPAD_3" value="147" />
+        <enum name="KEYCODE_NUMPAD_4" value="148" />
+        <enum name="KEYCODE_NUMPAD_5" value="149" />
+        <enum name="KEYCODE_NUMPAD_6" value="150" />
+        <enum name="KEYCODE_NUMPAD_7" value="151" />
+        <enum name="KEYCODE_NUMPAD_8" value="152" />
+        <enum name="KEYCODE_NUMPAD_9" value="153" />
+        <enum name="KEYCODE_NUMPAD_DIVIDE" value="154" />
+        <enum name="KEYCODE_NUMPAD_MULTIPLY" value="155" />
+        <enum name="KEYCODE_NUMPAD_SUBTRACT" value="156" />
+        <enum name="KEYCODE_NUMPAD_ADD" value="157" />
+        <enum name="KEYCODE_NUMPAD_DOT" value="158" />
+        <enum name="KEYCODE_NUMPAD_COMMA" value="159" />
+        <enum name="KEYCODE_NUMPAD_ENTER" value="160" />
+        <enum name="KEYCODE_NUMPAD_EQUALS" value="161" />
+        <enum name="KEYCODE_NUMPAD_LEFT_PAREN" value="162" />
+        <enum name="KEYCODE_NUMPAD_RIGHT_PAREN" value="163" />
+        <enum name="KEYCODE_VOLUME_MUTE" value="164" />
     </attr>
 
     <!-- ***************************************************************** -->
@@ -981,6 +1196,9 @@
         <attr name="textColor" />
         <attr name="backgroundDimEnabled" />
         <attr name="backgroundDimAmount" />
+        <attr name="windowActionBar" />
+        <attr name="windowActionModeOverlay" />
+        <attr name="windowActionBarOverlay" />
     </declare-styleable>
 
     <!-- The set of attributes that describe a AlertDialog's theme. -->
@@ -997,6 +1215,18 @@
         <attr name="centerMedium" format="reference|color" />
     </declare-styleable>
 
+    <!-- Fragment animation class attributes. -->
+    <declare-styleable name="FragmentAnimation">
+        <attr name="fragmentOpenEnterAnimation" format="reference" />
+        <attr name="fragmentOpenExitAnimation" format="reference" />
+        <attr name="fragmentCloseEnterAnimation" format="reference" />
+        <attr name="fragmentCloseExitAnimation" format="reference" />
+        <attr name="fragmentNextEnterAnimation" format="reference" />
+        <attr name="fragmentNextExitAnimation" format="reference" />
+        <attr name="fragmentPrevEnterAnimation" format="reference" />
+        <attr name="fragmentPrevExitAnimation" format="reference" />
+    </declare-styleable>
+
     <!-- Window animation class attributes. -->
     <declare-styleable name="WindowAnimation">
         <!-- The animation used when a window is being added. -->
@@ -1007,7 +1237,7 @@
         <attr name="windowShowAnimation" format="reference" />
         <!-- The animation used when a window is going from VISIBLE to INVISIBLE. -->
         <attr name="windowHideAnimation" format="reference" />
-        
+
         <!--  When opening a new activity, this is the animation that is
               run on the next activity (which is entering the screen). -->
         <attr name="activityOpenEnterAnimation" format="reference" />
@@ -1048,7 +1278,7 @@
               animation that is run on the top activity of the current task
               (which is exiting the screen). -->
         <attr name="taskToBackExitAnimation" format="reference" />
-        
+
         <!--  When opening a new activity that shows the wallpaper, while
               currently not showing the wallpaper, this is the animation that
               is run on the new wallpaper activity (which is entering the screen). -->
@@ -1065,7 +1295,7 @@
               currently showing the wallpaper, this is the animation that
               is run on the old wallpaper activity (which is exiting the screen). -->
         <attr name="wallpaperCloseExitAnimation" format="reference" />
-        
+
         <!--  When opening a new activity that is on top of the wallpaper
               when the current activity is also on top of the wallpaper,
               this is the animation that is run on the new activity
@@ -1350,26 +1580,17 @@
              <code>public void sayHello(View v)</code> method of your context
              (typically, your Activity). -->
         <attr name="onClick" format="string" />
-
-        <!-- Defines over-scrolling behavior. This property is used only if the
-             View is scrollable. Over-scrolling is the ability for the user to
-             receive feedback when attempting to scroll beyond meaningful content. -->
-        <attr name="overScrollMode">
-            <!-- Always show over-scroll effects, even if the content fits entirely
-                 within the available space. -->
-            <enum name="always" value="0" />
-            <!-- Only show over-scroll effects if the content is large
-                 enough to meaningfully scroll. -->
-            <enum name="ifContentScrolls" value="1" />
-            <!-- Never show over-scroll effects. -->
-            <enum name="never" value="2" />
-        </attr>
     </declare-styleable>
 
     <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
          of its subclasses.  Also see {@link #ViewGroup_Layout} for
          attributes that this class processes in its children. -->
     <declare-styleable name="ViewGroup">
+        <!-- Defines whether changes in layout (caused by adding and removing items) should
+             cause a LayoutTransition to run. When this flag is set to true, a default
+             LayoutTransition object will be set on the ViewGroup container and default
+             animations will run when these layout changes occur.-->
+        <attr name="animateLayoutChanges" format="boolean" />
         <!-- Defines whether a child is limited to draw inside of its bounds or not.
              This is useful with animations that scale the size of the children to more
              than 100% for instance. In such a case, this property should be set to false
@@ -1423,6 +1644,17 @@
             <enum name="blocksDescendants" value="2" />
         </attr>
 
+        <!-- Sets whether this ViewGroup should split MotionEvents
+             to separate child views during touch event dispatch.
+             If false (default), touch events will be dispatched to
+             the child view where the first pointer went down until
+             the last pointer goes up.
+             If true, touch events may be dispatched to multiple children.
+             MotionEvents for each pointer will be dispatched to the child
+             view where the initial ACTION_DOWN event happened.
+             See {@link android.view.ViewGroup#setMotionEventSplittingEnabled(boolean)}
+             for more information. -->
+        <attr name="splitMotionEvents" format="boolean" />
     </declare-styleable>
 
     <!-- A {@link android.view.ViewStub} lets you lazily include other XML layouts
@@ -1524,6 +1756,28 @@
         <attr name="isDefault" format="boolean" />
     </declare-styleable>
 
+    <!-- This is the subtype of InputMethod. Subtype can describe locales (e.g. en_US, fr_FR...)
+         and modes (e.g. voice, keyboard...), and is used for IME switch. This subtype allows
+         the system to call the specified subtype of the IME directly. -->
+    <declare-styleable name="InputMethod_Subtype">
+        <!-- The name of the subtype. -->
+        <attr name="label" />
+        <!-- The icon of the subtype. -->
+        <attr name="icon" />
+        <!-- The locale of the subtype. This string should be a locale (e.g. en_US, fr_FR...)
+             and will be passed to the IME when the framework calls the IME
+             with the subtype. This is also used by the framework to know the supported locales
+             of the IME.  -->
+        <attr name="imeSubtypeLocale" format="string" />
+        <!-- The mode of the subtype. This string can be a mode (e.g. voice, keyboard...) and this
+             string will be passed to the IME when the framework calls the IME with the
+             subtype.  -->
+        <attr name="imeSubtypeMode" format="string" />
+        <!-- The extra value of the subtype. This string can be any string and will be passed to
+             the IME when the framework calls the IME with the subtype.  -->
+        <attr name="imeSubtypeExtraValue" format="string" />
+    </declare-styleable>
+
     <!-- =============================== -->
     <!-- Widget package class attributes -->
     <!-- =============================== -->
@@ -1573,6 +1827,28 @@
              will use only the number of items in the adapter and the number of items visible
              on screen to determine the scrollbar's properties. -->
         <attr name="smoothScrollbar" format="boolean" />
+        <!-- A reference to an XML description of the adapter to attach to the list. -->
+        <attr name="adapter" format="reference" />
+        <!-- Defines the choice behavior for the view. By default, lists do not have
+             any choice behavior. By setting the choiceMode to singleChoice, the list
+             allows up to one item to be in a chosen state. By setting the choiceMode to
+             multipleChoice, the list allows any number of items to be chosen.
+             Finally, by setting the choiceMode to multipleChoiceModal the list allows
+             any number of items to be chosen in a special selection mode.
+             The application will supply a
+             {@link android.widget.AbsListView.MultiChoiceModeListener} using
+             {@link android.widget.AbsListView#setMultiChoiceModeListener} to control the
+             selection mode. This uses the {@link android.view.ActionMode} API. -->
+        <attr name="choiceMode">
+            <!-- Normal list that does not indicate choices. -->
+            <enum name="none" value="0" />
+            <!-- The list allows up to one choice. -->
+            <enum name="singleChoice" value="1" />
+            <!-- The list allows multiple choices. -->
+            <enum name="multipleChoice" value="2" />
+            <!-- The list allows multiple choices in a custom selection mode. -->
+            <enum name="multipleChoiceModal" value="3" />
+        </attr>
     </declare-styleable>
     <declare-styleable name="AbsSpinner">
         <!-- Reference to an array resource that will populate the Spinner.  For static content,
@@ -1768,7 +2044,7 @@
         <!-- When set to true, all children with a weight will be considered having
              the minimum size of the largest child. If false, all children are
              measured normally. -->
-        <attr name="useLargestChild" format="boolean" />
+        <attr name="measureWithLargestChild" format="boolean" />
     </declare-styleable>
     <declare-styleable name="ListView">
         <!-- Reference to an array resource that will populate the ListView.  For static content,
@@ -1779,28 +2055,12 @@
         <!-- Height of the divider. Will use the intrinsic height of the divider if this
              is not specified. -->
         <attr name="dividerHeight" format="dimension" />
-        <!-- Defines the choice behavior for the ListView. By default, lists do not have
-             any choice behavior. By setting the choiceMode to singleChoice, the List
-             allows up to one item to be in a chosen state. By setting the choiceMode to
-             multipleChoice, the list allows any number of items to be chosen. -->
-        <attr name="choiceMode">
-            <!-- Normal list that does not indicate choices. -->
-            <enum name="none" value="0" />
-            <!-- The list allows up to one choice. -->
-            <enum name="singleChoice" value="1" />
-            <!-- The list allows multiple choices. -->
-            <enum name="multipleChoice" value="2" />
-        </attr>
         <!-- When set to false, the ListView will not draw the divider after each header view.
              The default value is true. -->
         <attr name="headerDividersEnabled" format="boolean" />
         <!-- When set to false, the ListView will not draw the divider before each footer view.
              The default value is true. -->
         <attr name="footerDividersEnabled" format="boolean" />
-        <!-- Drawable to draw above list content. -->
-        <attr name="overScrollHeader" format="reference|color" />
-        <!-- Drawable to draw below list content. -->
-        <attr name="overScrollFooter" format="reference|color" />
     </declare-styleable>
     <declare-styleable name="MenuView">
         <!-- Default appearance of menu item text. -->
@@ -1929,6 +2189,8 @@
         <attr name="tabStripLeft" format="reference" />
         <!-- Drawable used to draw the right part of the strip underneath the tabs. -->
         <attr name="tabStripRight" format="reference" />
+        <!-- Layout used to organize each tab's content. -->
+        <attr name="tabLayout" format="reference" />
     </declare-styleable>
     <declare-styleable name="TextAppearance">
         <!-- Text color. -->
@@ -1945,6 +2207,8 @@
         <attr name="textColorHint" />
         <!-- Color of the links. -->
         <attr name="textColorLink" />
+        <!-- Height of a single line of text. -->
+        <attr name="textLineHeight" />
     </declare-styleable>
     <declare-styleable name="TextSwitcher">
     </declare-styleable>
@@ -2181,6 +2445,14 @@
         <!-- Reference to a drawable that will be used to display a text selection
              anchor for positioning the cursor within text. -->
         <attr name="textSelectHandle" />
+        <!-- The layout of the view that is displayed on top of the cursor to paste inside a
+             TextEdit field. -->
+        <attr name="textEditPasteWindowLayout" />
+        <!-- Variation of textEditPasteWindowLayout displayed when the clipboard is empty. -->
+        <attr name="textEditNoPasteWindowLayout" />
+
+        <!-- Height of a line of text. -->
+        <attr name="textLineHeight" />
     </declare-styleable>
     <!-- An <code>input-extras</code> is a container for extra data to supply to
          an input method.  Contains
@@ -2206,7 +2478,7 @@
         <attr name="dropDownAnchor" format="reference" />
         <!-- Specifies the basic width of the dropdown. Its value may
              be a dimension (such as "12dip") for a constant width,
-             fill_parent or match_parent to match the width of the 
+             fill_parent or match_parent to match the width of the
              screen, or wrap_content to match the width of
              the anchored view. -->
         <attr name="dropDownWidth" format="dimension">
@@ -2243,14 +2515,36 @@
         <attr name="popupAnimationStyle" format="reference" />
     </declare-styleable>
     <declare-styleable name="ViewAnimator">
+        <!-- Identifier for the animation to use when a view is shown. -->
         <attr name="inAnimation" format="reference" />
+        <!-- Identifier for the animation to use when a view is hidden. -->
         <attr name="outAnimation" format="reference" />
+        <!-- Defines whether to animate the current View when the ViewAnimation
+             is first displayed. -->
+        <attr name="animateFirstView" format="boolean" />
     </declare-styleable>
     <declare-styleable name="ViewFlipper">
         <attr name="flipInterval" format="integer" min="0" />
         <!-- When true, automatically start animating -->
         <attr name="autoStart" format="boolean" />
     </declare-styleable>
+    <declare-styleable name="AdapterViewAnimator">
+        <!-- Identifier for the animation to use when a view is shown. -->
+        <attr name="inAnimation" />
+        <!-- Identifier for the animation to use when a view is hidden. -->
+        <attr name="outAnimation" />
+        <!--Defines whether the animator loops to the first view once it
+        has reached the end of the list. -->
+        <attr name="loopViews" format="boolean" />
+        <!-- Defines whether to animate the current View when the ViewAnimation
+        is first displayed. -->
+        <attr name="animateFirstView" />
+    </declare-styleable>
+    <declare-styleable name="AdapterViewFlipper">
+        <attr name="flipInterval" />
+        <!-- When true, automatically start animating -->
+        <attr name="autoStart" />
+    </declare-styleable>
     <declare-styleable name="ViewSwitcher">
     </declare-styleable>
     <declare-styleable name="ScrollView">
@@ -2264,6 +2558,30 @@
     <declare-styleable name="Spinner">
         <!-- The prompt to display when the spinner's dialog is shown. -->
         <attr name="prompt" format="reference" />
+        <!-- Display mode for spinner options. -->
+        <attr name="spinnerMode" format="enum">
+            <!-- Spinner options will be presented to the user as a dialog window. -->
+            <enum name="dialog" value="0" />
+            <!-- Spinner options will be presented to the user as an inline dropdown
+                 anchored to the spinner widget itself. -->
+            <enum name="dropdown" value="1" />
+        </attr>
+        <!-- List selector to use for spinnerMode="dropdown" display. -->
+        <attr name="dropDownSelector" />
+        <!-- Background drawable to use for the dropdown in spinnerMode="dropdown". -->
+        <attr name="popupBackground" />
+        <!-- Vertical offset from the spinner widget for positioning the dropdown in
+             spinnerMode="dropdown". -->
+        <attr name="dropDownVerticalOffset" />
+        <!-- Horizontal offset from the spinner widget for positioning the dropdown
+             in spinnerMode="dropdown". -->
+        <attr name="dropDownHorizontalOffset" />
+        <!-- Width of the dropdown in spinnerMode="dropdown". -->
+        <attr name="dropDownWidth" />
+        <!-- Reference to a layout to use for displaying a prompt in the dropdown for
+             spinnerMode="dropdown". This layout must contain a TextView with the id
+             @android:id/text1 to be populated with the prompt text. -->
+        <attr name="popupPromptView" format="reference" />
     </declare-styleable>
     <declare-styleable name="DatePicker">
         <!-- The first year (inclusive), for example "1940". -->
@@ -2432,8 +2750,10 @@
 
     <!-- @hide -->
     <declare-styleable name="WeightedLinearLayout">
-        <attr name="majorWeight" format="float" />
-        <attr name="minorWeight" format="float" />
+        <attr name="majorWeightMin" format="float" />
+        <attr name="minorWeightMin" format="float" />
+        <attr name="majorWeightMax" format="float" />
+        <attr name="minorWeightMax" format="float" />
     </declare-styleable>
 
     <!-- ========================= -->
@@ -2467,6 +2787,10 @@
              same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with
              an RGB 565 screen). -->
         <attr name="dither" format="boolean" />
+        <!-- Amount of time (in milliseconds) to fade in a new state drawable. -->
+        <attr name="enterFadeDuration" format="integer" />
+        <!-- Amount of time (in milliseconds) to fade out an old state drawable. -->
+        <attr name="exitFadeDuration" format="integer" />
     </declare-styleable>
 
     <declare-styleable name="AnimationDrawable">
@@ -2574,6 +2898,10 @@
         <attr name="drawable" />
     </declare-styleable>
 
+    <declare-styleable name="MipmapDrawableItem">
+        <attr name="drawable" />
+    </declare-styleable>
+
     <declare-styleable name="RotateDrawable">
         <attr name="visible" />
         <attr name="fromDegrees" format="float" />
@@ -2897,6 +3225,69 @@
     </declare-styleable>
 
     <!-- ========================== -->
+    <!-- ValueAnimator class attributes -->
+    <!-- ========================== -->
+    <eat-comment />
+
+    <declare-styleable name="Animator">
+        <!-- Defines the interpolator used to smooth the animation movement in time. -->
+        <attr name="interpolator" />
+        <!-- When set to true, fillAfter is taken into account. -->
+        <!-- Amount of time (in milliseconds) for the animation to run. -->
+        <attr name="duration" />
+        <!-- Delay in milliseconds before the animation runs, once start time is reached. -->
+        <attr name="startOffset"/>
+        <!-- Defines how many times the animation should repeat. The default value is 0. -->
+        <attr name="repeatCount"/>
+        <!-- Defines the animation behavior when it reaches the end and the repeat count is
+             greater than 0 or infinite. The default value is restart. -->
+        <attr name="repeatMode"/>
+        <!-- Value the animation starts from. -->
+        <attr name="valueFrom" format="float|integer"/>
+        <!-- Value the animation animates to. -->
+        <attr name="valueTo" format="float|integer"/>
+        <!-- The type of valueFrom and valueTo. -->
+        <attr name="valueType">
+            <!-- valueFrom and valueTo are floats. -->
+            <enum name="floatType" value="0" />
+            <!-- valueFrom and valueTo are integers. -->
+            <enum name="intType"   value="1" />
+            <!-- valueFrom and valueTo are doubles. -->
+            <enum name="doubleType" value="2" />
+            <!-- valueFrom and valueTo are colors. -->
+            <enum name="colorType" value="3" />
+            <!-- valueFrom and valueTo are a custom type. -->
+            <enum name="customType" value="4" />
+        </attr>
+    </declare-styleable>
+
+    <!-- ========================== -->
+    <!-- ObjectAnimator class attributes -->
+    <!-- ========================== -->
+    <eat-comment />
+
+    <declare-styleable name="PropertyAnimator">
+        <!-- Name of the property being animated. -->
+        <attr name="propertyName" format="string"/>
+    </declare-styleable>
+
+
+    <!-- ========================== -->
+    <!-- AnimatorSet class attributes -->
+    <!-- ========================== -->
+    <eat-comment />
+
+    <declare-styleable name="AnimatorSet">
+        <!-- Name of the property being animated. -->
+        <attr name="ordering">
+            <!-- child animations should be played together. -->
+            <enum name="together" value="0" />
+            <!-- child animations should be played sequentially, in the same order as the xml. -->
+            <enum name="sequentially" value="1" />
+        </attr>
+    </declare-styleable>
+
+    <!-- ========================== -->
     <!-- State attributes           -->
     <!-- ========================== -->
     <eat-comment />
@@ -2918,6 +3309,7 @@
          <li>"state_last"
          <li>"state_only"
          <li>"state_pressed"
+         <li>"state_activated"
          <li>"state_error"
          <li>"state_circle"
          <li>"state_rect"
@@ -2925,23 +3317,35 @@
          <li>"state_move"
          </ul>  -->
     <declare-styleable name="DrawableStates">
-        <!--  State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.-->
+        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},
+             set when a view has input focus. -->
         <attr name="state_focused" format="boolean" />
-        <!--  State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.-->
+        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},
+             set when a view's window has input focus. -->
         <attr name="state_window_focused" format="boolean" />
-        <!--  State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.-->
+        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},
+             set when a view is enabled. -->
         <attr name="state_enabled" format="boolean" />
-        <!--  State identifier indicating that the object <var>may</var> display a check mark.
-              See {@link R.attr#state_checked} for the identifier that indicates whether it is
-              actually checked. -->
+        <!-- State identifier indicating that the object <var>may</var> display a check mark.
+             See {@link R.attr#state_checked} for the identifier that indicates whether it is
+             actually checked. -->
         <attr name="state_checkable" format="boolean"/>
-        <!--  State identifier indicating that the object is currently checked.  See
-              {@link R.attr#state_checkable} for an additional identifier that can indicate if
-              any object may ever display a check, regardless of whether state_checked is
-              currently set. -->
+        <!-- State identifier indicating that the object is currently checked.  See
+             {@link R.attr#state_checkable} for an additional identifier that can indicate if
+             any object may ever display a check, regardless of whether state_checked is
+             currently set. -->
         <attr name="state_checked" format="boolean"/>
-        <!--  State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.-->
+        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},
+             set when a view (or one of its parents) is currently selected. -->
         <attr name="state_selected" format="boolean" />
+        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},
+             set when the user is pressing down in a view. -->
+        <attr name="state_pressed" format="boolean" />
+        <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable},
+             set when a view or its parent has been "activated" meaning the user has currently
+             marked it as being of interest.  This is an alternative representation of
+             state_checked for when the state should be propagated down the view hierarchy. -->
+        <attr name="state_activated" format="boolean" />
         <!--  State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.-->
         <attr name="state_active" format="boolean" />
         <!--  State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.-->
@@ -2952,8 +3356,6 @@
         <attr name="state_middle" format="boolean" />
         <!--  State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.-->
         <attr name="state_last" format="boolean" />
-        <!--  State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.-->
-        <attr name="state_pressed" format="boolean" />
     </declare-styleable>
     <declare-styleable name="ViewDrawableStates">
         <attr name="state_pressed" />
@@ -2961,6 +3363,7 @@
         <attr name="state_selected" />
         <attr name="state_window_focused" />
         <attr name="state_enabled" />
+        <attr name="state_activated" />
     </declare-styleable>
     <!-- State array representing a menu item that is currently checked. -->
     <declare-styleable name="MenuItemCheckedState">
@@ -3305,6 +3708,39 @@
         <!-- Whether the item is enabled. -->
         <attr name="enabled" />
 
+        <!-- Name of a method on the Context used to inflate the menu that will be
+             called when the item is clicked. -->
+        <attr name="onClick" />
+
+        <!-- How this item should display in the Action Bar, if present. -->
+        <attr name="showAsAction">
+            <!-- Never show this item in an action bar, show it in the overflow menu instead.
+                 Mutually exclusive with "ifRoom" and "always". -->
+            <flag name="never" value="0" />
+            <!-- Show this item in an action bar if there is room for it as determined
+                 by the system. Favor this option over "always" where possible.
+                 Mutually exclusive with "never" and "always". -->
+            <flag name="ifRoom" value="1" />
+            <!-- Always show this item in an actionbar, even if it would override
+                 the system's limits of how much stuff to put there. This may make
+                 your action bar look bad on some screens. In most cases you should
+                 use "ifRoom" instead. Mutually exclusive with "ifRoom" and "never". -->
+            <flag name="always" value="2" />
+            <!-- When this item is shown as an action in the action bar, show a text
+                 label with it even if it has an icon representation. -->
+            <flag name="withText" value="4" />
+        </attr>
+
+        <!-- An optional layout to be used as an action view.
+             See {@link android.view.MenuItem#setActionView(android.view.View)}
+             for more info. -->
+        <attr name="actionLayout" format="reference" />
+
+        <!-- The name of an optional View class to instantiate and use as an
+             action view. See {@link android.view.MenuItem#setActionView(android.view.View)}
+             for more info. -->
+        <attr name="actionViewClass" format="string" />
+
     </declare-styleable>
 
     <!-- **************************************************************** -->
@@ -3320,6 +3756,25 @@
         <attr name="orderingFromXml" format="boolean" />
     </declare-styleable>
 
+    <!-- Attribute for a header describing the item shown in the top-level list
+         from which the selects the set of preference to dig in to. -->
+    <declare-styleable name="PreferenceHeader">
+        <!-- Identifier value for the header. -->
+        <attr name="id" />
+        <!-- The title of the item that is shown to the user. -->
+        <attr name="title" />
+        <!-- The summary for the item. -->
+        <attr name="summary" format="string" />
+        <!-- The title for the bread crumb of this item. -->
+        <attr name="breadCrumbTitle" format="string" />
+        <!-- The short title for the bread crumb of this item. -->
+        <attr name="breadCrumbShortTitle" format="string" />
+        <!-- An icon for the item. -->
+        <attr name="icon" />
+        <!-- The fragment that is displayed when the user selects this item. -->
+        <attr name="fragment" format="string" />
+    </declare-styleable>
+
     <!-- WARNING:  If adding attributes to Preference, make sure it does not conflict
                    with a View's attributes.  Some subclasses (e.g., EditTextPreference)
                    proxy all attributes to its EditText widget. -->
@@ -3332,10 +3787,13 @@
         <!-- The title for the Preference in a PreferenceActivity screen. -->
         <attr name="title" />
         <!-- The summary for the Preference in a PreferenceActivity screen. -->
-        <attr name="summary" format="string" />
+        <attr name="summary" />
         <!-- The order for the Preference (lower values are to be ordered first). If this is not
              specified, the default orderin will be alphabetic. -->
         <attr name="order" format="integer" />
+        <!-- When used inside of a modern PreferenceActivity, this declares
+             a new PreferenceFragment to be shown when the user selects this item. -->
+        <attr name="fragment" />
         <!-- The layout for the Preference in a PreferenceActivity screen. This should
              rarely need to be changed, look at widgetLayout instead. -->
         <attr name="layout" />
@@ -3407,6 +3865,16 @@
         <attr name="entryValues" format="reference" />
     </declare-styleable>
 
+    <declare-styleable name="MultiSelectListPreference">
+        <!-- The human-readable array to present as a list. Each entry must have a corresponding
+             index in entryValues. -->
+        <attr name="entries" />
+        <!-- The array to find the value to save for a preference when an entry from
+             entries is selected. If a user clicks the second item in entries, the
+             second item in this array will be saved to the preference. -->
+        <attr name="entryValues" />
+    </declare-styleable>
+
     <!-- Base attributes available to RingtonePreference. -->
     <declare-styleable name="RingtonePreference">
         <!-- Which ringtone type(s) to show in the picker. -->
@@ -3558,7 +4026,7 @@
     <!-- AppWidget package class attributes -->
     <!-- =============================== -->
     <eat-comment />
-    
+
     <!-- Use <code>appwidget-provider</code> as the root tag of the XML resource that
          describes an AppWidget provider.  See {@link android.appwidget android.appwidget}
          package for more info.
@@ -3575,13 +4043,49 @@
         <!-- A class name in the AppWidget's package to be launched to configure.
              If not supplied, then no activity will be launched. -->
         <attr name="configure" format="string" />
+        <!-- A preview of what the AppWidget will look like after it's configured.
+       	     If not supplied, the AppWidget's icon will be used. -->
+        <attr name="previewImage" format="reference" />
     </declare-styleable>
 
     <!-- =============================== -->
     <!-- App package class attributes -->
     <!-- =============================== -->
     <eat-comment />
-    
+
+    <!-- ============================= -->
+    <!-- View package class attributes -->
+    <!-- ============================= -->
+    <eat-comment />
+
+    <!-- Attributes that can be used with <code>&lt;fragment&gt;</code>
+         tags inside of the layout of an Activity.  This instantiates
+         the given {@link android.app.Fragment} and inserts its content
+         view into the current location in the layout. -->
+    <declare-styleable name="Fragment">
+        <!-- Supply the name of the fragment class to instantiate. -->
+        <attr name="name" />
+
+        <!-- Supply an identifier name for the top-level view, to later retrieve it
+             with {@link android.view.View#findViewById View.findViewById()} or
+             {@link android.app.Activity#findViewById Activity.findViewById()}.
+             This must be a
+             resource reference; typically you set this using the
+             <code>@+</code> syntax to create a new ID resources.
+             For example: <code>android:id="@+id/my_id"</code> which
+             allows you to later retrieve the view
+             with <code>findViewById(R.id.my_id)</code>. -->
+        <attr name="id" />
+
+        <!-- Supply a tag for the top-level view containing a String, to be retrieved
+             later with {@link android.view.View#getTag View.getTag()} or
+             searched for with {@link android.view.View#findViewWithTag
+             View.findViewWithTag()}.  It is generally preferable to use
+             IDs (through the android:id attribute) instead of tags because
+             they are faster and allow for compile-time type checking. -->
+        <attr name="tag" />
+    </declare-styleable>
+
     <!-- Use <code>device-admin</code> as the root tag of the XML resource that
          describes a
          {@link android.app.admin.DeviceAdminReceiver}, which is
@@ -3621,7 +4125,7 @@
     <!-- Accounts package class attributes -->
     <!-- =============================== -->
     <eat-comment />
-    
+
     <!-- Use <code>account-authenticator</code> as the root tag of the XML resource that
          describes an account authenticator.
      -->
@@ -3642,7 +4146,7 @@
     <!-- Accounts package class attributes -->
     <!-- =============================== -->
     <eat-comment />
-    
+
     <!-- Use <code>account-authenticator</code> as the root tag of the XML resource that
          describes an account authenticator.
      -->
@@ -3658,7 +4162,7 @@
     <!-- Contacts meta-data attributes -->
     <!-- =============================== -->
     <eat-comment />
-    
+
     <!-- TODO: remove this deprecated styleable. -->
     <eat-comment />
     <declare-styleable name="Icon">
@@ -3684,6 +4188,9 @@
         <attr name="detailColumn" format="string" />
         <!-- Flag indicating that detail should be built from SocialProvider. -->
         <attr name="detailSocialSummary" format="boolean" />
+        <!-- Resource representing the term "All Contacts" (e.g. "All Friends" or
+        "All connections"). Optional (Default is "All Contacts"). -->
+        <attr name="allContactsName" format="string" />
     </declare-styleable>
 
     <!-- =============================== -->
@@ -3715,4 +4222,147 @@
         <attr name="settingsActivity" />
     </declare-styleable>
 
+    <!-- =============================== -->
+    <!-- Adapters attributes             -->
+    <!-- =============================== -->
+    <eat-comment />
+
+    <!-- Adapter used to bind cursors. -->
+    <declare-styleable name="CursorAdapter">
+        <!-- URI to get the cursor from. Optional. -->
+        <attr name="uri" format="string" />
+        <!-- Selection statement for the query. Optional. -->
+        <attr name="selection" format="string" />
+        <!-- Sort order statement for the query. Optional. -->
+        <attr name="sortOrder" format="string" />
+        <!-- Layout resource used to display each row from the cursor. Mandatory. -->
+        <attr name="layout" />
+    </declare-styleable>
+
+    <!-- Attributes used in bind items for XML cursor adapters. -->
+    <declare-styleable name="CursorAdapter_BindItem">
+        <!-- The name of the column to bind from. Mandatory. -->
+        <attr name="from" format="string" />
+        <!-- The resource id of the view to bind to. Mandatory. -->
+        <attr name="to" format="reference" />
+        <!-- The type of binding. If this value is not specified, the type will be
+             inferred from the type of the "to" target view. Mandatory.
+
+             The type can be one of:
+             <ul>
+             <li>string, The content of the column is interpreted as a string.</li>
+             <li>image, The content of the column is interpreted as a blob describing an image.</li>
+             <li>image-uri, The content of the column is interpreted as a URI to an image.</li>
+             <li>drawable, The content of the column is interpreted as a resource id to a drawable.</li>
+             <li>A fully qualified class name, corresponding to an implementation of
+                 android.widget.Adapters.CursorBinder.</li>
+             </ul>
+         -->
+        <attr name="as" format="string" />
+    </declare-styleable>
+
+    <!-- Attributes used in select items for XML cursor adapters. -->
+    <declare-styleable name="CursorAdapter_SelectItem">
+        <!-- The name of the column to select. Mandatory. -->
+        <attr name="column" format="string" />
+    </declare-styleable>
+
+    <!-- Attributes used to map values to new values in XML cursor adapters' bind items. -->
+    <declare-styleable name="CursorAdapter_MapItem">
+        <!-- The original value from the column. Mandatory. -->
+        <attr name="fromValue" format="string" />
+        <!-- The new value from the column. Mandatory. -->
+        <attr name="toValue" format="string" />
+    </declare-styleable>
+
+    <!-- Attributes used to map values to new values in XML cursor adapters' bind items. -->
+    <declare-styleable name="CursorAdapter_TransformItem">
+        <!-- The transformation expression. Mandatory if "withClass" is not specified. -->
+        <attr name="withExpression" format="string" />
+        <!-- The transformation class, an implementation of
+             android.widget.Adapters.CursorTransformation. Mandatory if "withExpression"
+             is not specified. -->
+        <attr name="withClass" format="string" />
+    </declare-styleable>
+
+    <!-- Attributes used to style the Action Bar. -->
+    <declare-styleable name="ActionBar">
+        <!-- The type of navigation to use. -->
+        <attr name="navigationMode">
+            <!-- Normal static title text -->
+            <enum name="normal" value="0" />
+            <!-- The action bar will use a selection list for navigation. -->
+            <enum name="listMode" value="1" />
+            <!-- The action bar will use a series of horizontal tabs for navigation. -->
+            <enum name="tabMode" value="2" />
+        </attr>
+        <!-- Options affecting how the action bar is displayed. -->
+        <attr name="displayOptions">
+            <flag name="useLogo" value="0x1" />
+            <flag name="showHome" value="0x2" />
+            <flag name="homeAsUp" value="0x4" />
+            <flag name="showTitle" value="0x8" />
+            <flag name="showCustom" value="0x10" />
+            <!-- DEPRECATED - Remove this later!! -->
+            <flag name="hideHome" value="0x1000" />
+        </attr>
+        <!-- Specifies title text used for navigationMode="normal" -->
+        <attr name="title" />
+        <!-- Specifies subtitle text used for navigationMode="normal" -->
+        <attr name="subtitle" format="string" />
+        <!-- Specifies a style to use for title text. -->
+        <attr name="titleTextStyle" format="reference" />
+        <!-- Specifies a style to use for subtitle text. -->
+        <attr name="subtitleTextStyle" format="reference" />
+        <!-- Specifies the drawable used for the application icon. -->
+        <attr name="icon" />
+        <!-- Specifies the drawable used for the application logo. -->
+        <attr name="logo" />
+        <!-- Specifies the drawable used for item dividers. -->
+        <attr name="divider" />
+        <!-- Specifies a background drawable for the action bar. -->
+        <attr name="background" />
+        <!-- Specifies a layout for custom navigation. Overrides navigationMode. -->
+        <attr name="customNavigationLayout" format="reference" />
+        <!-- Specifies a fixed height. -->
+        <attr name="height" />
+        <!-- Specifies a drawable to use for the 'home as up' indicator. -->
+        <attr name="homeAsUpIndicator" format="reference" />
+    </declare-styleable>
+
+    <declare-styleable name="ActionMode">
+        <!-- Specifies a style to use for title text. -->
+        <attr name="titleTextStyle" />
+        <!-- Specifies a style to use for subtitle text. -->
+        <attr name="subtitleTextStyle" />
+        <!-- Specifies a background for the action mode bar. -->
+        <attr name="background" />
+        <!-- Specifies a fixed height for the action mode bar. -->
+        <attr name="height" />
+    </declare-styleable>
+
+    <declare-styleable name="SearchView">
+        <!-- The default state of the SearchView. If true, it will be iconified when not in
+             use and expanded when clicked. -->
+        <attr name="iconifiedByDefault" format="boolean"/>
+        <!-- An optional maximum width of the SearchView. -->
+        <attr name="maxWidth" />
+    </declare-styleable>
+
+    <declare-styleable name="ButtonGroup">
+        <!-- Drawable to use as a vertical divider between buttons. -->
+        <attr name="divider" />
+        <!-- Drawable to use as a background for buttons added to this group. -->
+        <attr name="buttonBackground" format="reference" />
+        <!-- Setting for which dividers to show. -->
+        <attr name="showDividers">
+            <flag name="beginning" value="1" />
+            <flag name="middle" value="2" />
+            <flag name="end" value="4" />
+        </attr>
+    </declare-styleable>
+
+    <declare-styleable name="ActionBar_LayoutParams">
+        <attr name="layout_gravity" />
+    </declare-styleable>
 </resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index f31dfec..fe6176d 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -75,8 +75,7 @@
          header in the Action Bar. The primary differences between an icon
          and a logo are that logos are often wider and more detailed, and are
          used without an accompanying text caption. This must be a reference
-         to a Drawable resource containing the image definition.
-         @hide -->
+         to a Drawable resource containing the image definition. -->
     <attr name="logo" format="reference" />
 
     <!-- Name of the activity to be launched to manage application's space on
@@ -235,6 +234,10 @@
          the safe mode.  -->
     <attr name="vmSafeMode" format="boolean" />
 
+    <!-- Flag indicating whether the application's rendering should be hardware
+         accelerated if possible.  -->
+    <attr name="hardwareAccelerated" format="boolean" />
+
     <!-- Flag indicating whether the given application component is available
          to other applications.  If false, it can only be accessed by
          applications with its same user id (which usually means only by
@@ -399,8 +402,7 @@
     <attr name="syncable" format="boolean" />
     
     <!-- Flag declaring this activity to be 'immersive'; immersive activities
-         should not be interrupted with other activities or notifications.
-         @hide -->
+         should not be interrupted with other activities or notifications. -->
     <attr name="immersive" format="boolean" />
 
     <!-- Specify the order in which content providers hosted by a process
@@ -734,6 +736,7 @@
         <attr name="theme" />
         <attr name="label" />
         <attr name="icon" />
+        <attr name="logo" />
         <attr name="description" />
         <attr name="permission" />
         <attr name="process" />
@@ -752,6 +755,7 @@
         <attr name="enabled" />
         <attr name="debuggable" />
         <attr name="vmSafeMode" />
+        <attr name="hardwareAccelerated" />
         <!-- Name of activity to be launched for managing the application's space on the device. -->
         <attr name="manageSpaceActivity" />
         <attr name="allowClearUserData" />
@@ -792,6 +796,7 @@
         <attr name="name" />
         <attr name="label" />
         <attr name="icon" />
+        <attr name="logo" />
         <attr name="permissionGroup" />
         <attr name="description" />
         <attr name="protectionLevel" />
@@ -816,6 +821,7 @@
         <attr name="name" />
         <attr name="label" />
         <attr name="icon" />
+        <attr name="logo" />
         <attr name="description" />
     </declare-styleable>
     
@@ -845,6 +851,7 @@
         <attr name="name" />
         <attr name="label" />
         <attr name="icon" />
+        <attr name="logo" />
     </declare-styleable>
     
     <!-- The <code>uses-permission</code> tag requests a
@@ -1049,6 +1056,7 @@
         <attr name="label" />
         <attr name="description" />
         <attr name="icon" />
+        <attr name="logo" />
         <attr name="process" />
         <attr name="authorities" />
         <attr name="syncable" />
@@ -1128,6 +1136,7 @@
         <attr name="label" />
         <attr name="description" />
         <attr name="icon" />
+        <attr name="logo" />
         <attr name="permission" />
         <attr name="process" />
         <!-- Specify whether the service is enabled or not (that is, can be instantiated by the system).
@@ -1160,6 +1169,7 @@
         <attr name="label" />
         <attr name="description" />
         <attr name="icon" />
+        <attr name="logo" />
         <attr name="permission" />
         <attr name="process" />
         <!-- Specify whether the receiver is enabled or not (that is, can be instantiated by the system).
@@ -1192,6 +1202,7 @@
         <attr name="label" />
         <attr name="description" />
         <attr name="icon" />
+        <attr name="logo" />
         <attr name="launchMode" />
         <attr name="screenOrientation" />
         <attr name="configChanges" />
@@ -1217,6 +1228,8 @@
              this activity.  A value besides "unspecified" here overrides
              any value in the theme. -->
         <attr name="windowSoftInputMode" />
+        <attr name="immersive" />
+        <attr name="hardwareAccelerated" />
     </declare-styleable>
     
     <!-- The <code>activity-alias</code> tag declares a new
@@ -1245,6 +1258,7 @@
         <attr name="label" />
         <attr name="description" />
         <attr name="icon" />
+        <attr name="logo" />
         <attr name="permission" />
         <!-- Specify whether the activity-alias is enabled or not (that is, can be instantiated by the system).
              It can also be specified for an application as a whole, in which case a value of "false"
@@ -1314,6 +1328,7 @@
          parent="AndroidManifestActivity AndroidManifestReceiver AndroidManifestService">
         <attr name="label" />
         <attr name="icon" />
+        <attr name="logo" />
         <attr name="priority" />
     </declare-styleable>
     
@@ -1420,6 +1435,7 @@
         <attr name="targetPackage" />
         <attr name="label" />
         <attr name="icon" />
+        <attr name="logo" />
         <attr name="handleProfiling" />
         <attr name="functionalTest" />
     </declare-styleable>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index b6af6b2..21b810e 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -18,8 +18,8 @@
 */
 -->
 <resources>
-	<drawable name="screen_background_light">#ffffffff</drawable>
-	<drawable name="screen_background_dark">#ff000000</drawable>
+	  <drawable name="screen_background_light">#fff3f3f3</drawable>
+	  <drawable name="screen_background_dark">#ff000000</drawable>
     <drawable name="status_bar_closed_default_background">#ff000000</drawable>
     <drawable name="status_bar_opened_default_background">#ff000000</drawable>
     <drawable name="search_bar_default_color">#ff000000</drawable>
@@ -37,23 +37,29 @@
     <color name="black">#ff000000</color>
     <color name="transparent">#00000000</color>
     <color name="background_dark">#ff000000</color>
-    <color name="bright_foreground_dark">#ffffffff</color>
+    <color name="background_light">#fff3f3f3</color>
+    <color name="bright_foreground_dark">@android:color/background_light</color>
+    <color name="bright_foreground_light">@android:color/background_dark</color>
     <color name="bright_foreground_dark_disabled">#80ffffff</color>
-    <color name="bright_foreground_dark_inverse">#ff000000</color>
+    <color name="bright_foreground_light_disabled">#80000000</color>
+    <color name="bright_foreground_dark_inverse">@android:color/bright_foreground_light</color>
+    <color name="bright_foreground_light_inverse">@android:color/bright_foreground_dark</color>
     <color name="dim_foreground_dark">#bebebe</color>
     <color name="dim_foreground_dark_disabled">#80bebebe</color>
     <color name="dim_foreground_dark_inverse">#323232</color>
     <color name="dim_foreground_dark_inverse_disabled">#80323232</color>
     <color name="hint_foreground_dark">#808080</color>
-    <color name="background_light">#ffffffff</color>
-    <color name="bright_foreground_light">#ff000000</color>
-    <color name="bright_foreground_light_inverse">#ffffffff</color>
-    <color name="bright_foreground_light_disabled">#80000000</color>
     <color name="dim_foreground_light">#323232</color>
     <color name="dim_foreground_light_disabled">#80323232</color>
     <color name="dim_foreground_light_inverse">#bebebe</color>
     <color name="dim_foreground_light_inverse_disabled">#80bebebe</color>
     <color name="hint_foreground_light">#808080</color>
+    <color name="highlight_background">#cc475925</color>
+    <color name="highlight_background_inverse">#ccd2e461</color>
+    <color name="highlighted_text_dark">#cc475925</color>
+    <color name="highlighted_text_light">#ccd2e461</color>
+    <color name="link_text_dark">#5c5cff</color>
+    <color name="link_text_light">#0000ee</color>
 
     <drawable name="stat_notify_sync_noanim">@drawable/stat_notify_sync_anim0</drawable>
     <drawable name="stat_sys_download_done">@drawable/stat_sys_download_anim0</drawable>
@@ -97,5 +103,39 @@
     <color name="keyguard_text_color_soundon">#e69310</color>
     <color name="keyguard_text_color_decline">#fe0a5a</color>
 
+    <!-- For holo theme -->
+	  <drawable name="screen_background_holo_light">#fff3f3f3</drawable>
+	  <drawable name="screen_background_holo_dark">#ff000000</drawable>
+    <color name="background_holo_dark">#ff000000</color>
+    <color name="background_holo_light">#fff3f3f3</color>
+    <color name="bright_foreground_holo_dark">@android:color/background_holo_light</color>
+    <color name="bright_foreground_holo_light">@android:color/background_holo_dark</color>
+    <color name="bright_foreground_disabled_holo_dark">#80ffffff</color>
+    <color name="bright_foreground_disabled_holo_light">#80000000</color>
+    <color name="bright_foreground_inverse_holo_dark">@android:color/bright_foreground_holo_light</color>
+    <color name="bright_foreground_inverse_holo_light">@android:color/bright_foreground_holo_dark</color>
+    <color name="dim_foreground_holo_dark">#bebebe</color>
+    <color name="dim_foreground_disabled_holo_dark">#80bebebe</color>
+    <color name="dim_foreground_inverse_holo_dark">#323232</color>
+    <color name="dim_foreground_inverse_disabled_holo_dark">#80323232</color>
+    <color name="hint_foreground_holo_dark">#808080</color>
+    <color name="dim_foreground_holo_light">#323232</color>
+    <color name="dim_foreground_disabled_holo_light">#80323232</color>
+    <color name="dim_foreground_inverse_holo_light">#bebebe</color>
+    <color name="dim_foreground_inverse_disabled_holo_light">#80bebebe</color>
+    <color name="hint_foreground_holo_light">#808080</color>
+    <color name="highlight_background_holo">#cc475925</color>
+    <color name="highlight_background_inverse_holo">#ccd2e461</color>
+    <color name="highlighted_text_holo_dark">#cc475925</color>
+    <color name="highlighted_text_holo_light">#ccd2e461</color>
+    <color name="link_text_holo_dark">#5c5cff</color>
+    <color name="link_text_holo_light">#0000ee</color>
+
+    <!-- Group buttons -->
+    <color name="group_button_dialog_pressed_holo_dark">#46c5c1ff</color>
+    <color name="group_button_dialog_focused_holo_dark">#2699cc00</color>
+
+    <color name="group_button_dialog_pressed_holo_light">#ffffffff</color>
+    <color name="group_button_dialog_focused_holo_light">#4699cc00</color>
 </resources>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d8c3104..eb3cafa 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -20,9 +20,7 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Component to be used as the status bar service.  Must implement the IStatusBar
-         interface.  This name is in the ComponentName flattened format (package/class)  -->
-    <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.StatusBarService</string>
+    <bool name="config_statusBarCanHide">true</bool>
 
     <!-- Do not translate. Defines the slots for the right-hand side icons.  That is to say, the
          icons in the status bar that are not notifications. -->
@@ -60,18 +58,29 @@
     <bool name="config_sf_slowBlur">false</bool>
     
     <!-- The duration (in milliseconds) of a short animation. -->
-    <integer name="config_shortAnimTime">150</integer>
+    <integer name="config_shortAnimTime">200</integer>
     
     <!-- The duration (in milliseconds) of a medium-length animation. -->
-    <integer name="config_mediumAnimTime">300</integer>
+    <integer name="config_mediumAnimTime">400</integer>
     
     <!-- The duration (in milliseconds) of a long animation. -->
-    <integer name="config_longAnimTime">400</integer>
+    <integer name="config_longAnimTime">500</integer>
 
     <!-- The duration (in milliseconds) that the radio will scan for a signal
          when there's no network connection. If the scan doesn't timeout, use zero -->
     <integer name="config_radioScanningTimeout">0</integer>
 
+    <!-- Set to true if the location returned Environment.getExternalStorageDirectory()
+         is actually a subdirectory of the internal storage.
+         If this is set then Environment.getExternalStorageState() will always return
+         MEDIA_MOUNTED and Intent.ACTION_MEDIA_MOUNTED will be broadcast at boot time
+         for backward compatibility with apps that require external storage. -->
+    <bool name="config_emulateExternalStorage">false</bool>
+
+    <!-- Set to true if external storage is case sensitive.
+         Typically external storage is FAT, which is case insensitive. -->
+    <bool name="config_caseSensitiveExternalStorage">false</bool>
+    
     <!-- A product with no SD card == not removable. -->
     <bool name="config_externalStorageRemovable" product="nosdcard">false</bool>
     <!-- Configures whether the primary external storage device is
@@ -107,6 +116,9 @@
         <item>"0,1"</item>
     </string-array>
 
+    <!-- The maximum duration (in milliseconds) we expect a network transition to take -->
+    <integer name="config_networkTransitionTimeout">60000</integer>
+
     <!-- List of regexpressions describing the interface (if any) that represent tetherable
          USB interfaces.  If the device doesn't want to support tething over USB this should
          be empty.  An example would be "usb.*" -->
@@ -119,6 +131,16 @@
     <string-array translatable="false" name="config_tether_wifi_regexs">
     </string-array>
 
+    <!-- List of regexpressions describing the interface (if any) that represent tetherable
+         bluetooth interfaces.  If the device doesn't want to support tethering over bluetooth this
+         should be empty. -->
+    <string-array translatable="false" name="config_tether_bluetooth_regexs">
+    </string-array>
+
+    <!-- Max number of Bluetooth tethering connections allowed. If this is
+         updated config_tether_dhcp_range has to be updated appropriately. -->
+    <integer translateable="false" name="config_max_pan_devices">5</integer>
+
     <!-- Dhcp range (min, max) to use for tethering purposes -->
     <string-array translatable="false" name="config_tether_dhcp_range">
     </string-array>
@@ -206,9 +228,19 @@
          closed.  The default is 0. -->
     <integer name="config_lidNavigationAccessibility">0</integer>
 
+    <!-- Control the behavior when the user long presses the power button.
+            0 - Nothing
+            1 - Global actions menu
+            2 - Power off (with confirmation)
+    -->
+    <integer name="config_longPressOnPowerBehavior">1</integer>
+
     <!-- Indicate whether the SD card is accessible without removing the battery. -->
     <bool name="config_batterySdCardAccessibility">false</bool>
 
+    <!-- Indicate whether the device has USB host support. -->
+    <bool name="config_hasUsbHostSupport">false</bool>
+
     <!-- Vibrator pattern for feedback about a long screen/key press -->
     <integer-array name="config_longPressVibePattern">
         <item>0</item>
@@ -258,6 +290,11 @@
 
     <bool name="config_use_strict_phone_number_comparation">false</bool>
 
+    <!-- Display low battery warning when battery level dips to this value.
+         Also, the battery stats are flushed to disk when we hit this level.  -->
+    <integer name="config_criticalBatteryWarningLevel">4</integer>
+
+    <!-- Display low battery warning when battery level dips to this value -->
     <!-- Display low battery warning when battery level dips to this value -->
     <integer name="config_lowBatteryWarningLevel">15</integer>
 
@@ -291,6 +328,15 @@
     <!-- Allow the menu hard key to be disabled in LockScreen on some devices -->
     <bool name="config_disableMenuKeyInLockScreen">false</bool>
 
+    <!-- Show sliding tab before lockscreen -->
+    <bool name="config_enableSlidingTabFirst">true</bool>
+
+    <!-- Diable lockscreen rotation by default -->
+    <bool name="config_enableLockScreenRotation">false</bool>
+
+    <!-- Enable 3D RecentApplications view -->
+    <bool name="config_enableRecentApps3D">false</bool>
+
     <!-- Array of light sensor LUX values to define our levels for auto backlight brightness support.
          The N entries of this array define N + 1 zones as follows:
 
@@ -375,6 +421,26 @@
     <!-- 2 means give warning -->
     <integer name="config_datause_notification_type">2</integer>
 
+    <!-- Flag indicating whether the current device is "voice capable".
+         If true, this means that the device supports circuit-switched
+         (i.e. voice) phone calls over the telephony network, and is
+         allowed to display the in-call UI while a cellular voice call is
+         active.  This can be overridden to false for "data only" devices
+         which can't make voice calls and don't support any in-call UI.
+
+         Note: this flag is subtly different from the
+         PackageManager.FEATURE_TELEPHONY system feature, which is
+         available on *any* device with a telephony radio, even if the
+         device is data-only. -->
+    <bool name="config_voice_capable">true</bool>
+
+    <!-- IP address of the dns server to use if nobody else suggests one -->
+    <string name="config_default_dns_server">8.8.8.8</string>
+
+    <!-- The default character set for GsmAlphabet -->
+    <!-- Empty string means MBCS is not considered -->
+    <string name="gsm_alphabet_default_charset"></string>
+
     <!-- Enables SIP on WIFI only -->
     <bool name="config_sip_wifi_only">true</bool>
 
@@ -382,6 +448,10 @@
     <!-- The restoring is handled by modem if it is true-->
     <bool translatable="false" name="skip_restoring_network_selection">false</bool>
 
+    <!-- Number of database connections opened and managed by framework layer
+         to handle queries on each database. -->
+    <integer name="db_connection_pool_size">1</integer>
+
     <!-- The URL that should be sent in an x-wap-profile header with an HTTP request,
          as defined in the Open Mobile Alliance User Agent Profile specification
          OMA-TS-UAProf-V2_0-20060206-A Section 8.1.1.1. If the URL contains a '%s'
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 11f3e50..0928ce0 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -25,6 +25,10 @@
     <!-- The standard size (both width and height) of an application icon that
          will be displayed in the app launcher and elsewhere. -->
     <dimen name="app_icon_size">48dip</dimen>
+    <!-- The maximum number of action buttons that should be permitted within
+         an action bar/action mode. This will be used to determine how many
+         showAsAction="ifRoom" items can fit. "always" items can override this. -->
+    <integer name="max_action_buttons">5</integer>
     <dimen name="toast_y_offset">64dip</dimen>
     <!-- Height of the status bar -->
     <dimen name="status_bar_height">25dip</dimen>
@@ -32,14 +36,20 @@
     <dimen name="status_bar_icon_size">25dip</dimen>
     <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
     <dimen name="status_bar_edge_ignore">5dp</dimen>
+    <!-- Margin for permanent screen decorations at the bottom. -->
+    <dimen name="screen_margin_bottom">0dip</dimen>
     <!-- Size of the fastscroll hint letter -->
     <dimen name="fastscroll_overlay_size">104dp</dimen>
     <!-- Width of the fastscroll thumb -->
     <dimen name="fastscroll_thumb_width">64dp</dimen>
     <!-- Height of the fastscroll thumb -->
     <dimen name="fastscroll_thumb_height">52dp</dimen>
-    <!-- Default height of a key in the password keyboard -->
-    <dimen name="password_keyboard_key_height">56dip</dimen>
+    <!-- Min width for a tablet device -->
+    <dimen name="min_xlarge_screen_width">800dp</dimen>
+    <!-- Default height of a key in the password keyboard for alpha -->
+    <dimen name="password_keyboard_key_height_alpha">56dip</dimen>
+    <!-- Default height of a key in the password keyboard for numeric -->
+    <dimen name="password_keyboard_key_height_numeric">56dip</dimen>
     <!-- Default correction for the space key in the password keyboard -->
     <dimen name="password_keyboard_spacebar_vertical_correction">4dip</dimen>
 </resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 8b6af71..b7f177f 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -59,6 +59,7 @@
   <item type="id" name="copy" />
   <item type="id" name="paste" />
   <item type="id" name="copyUrl" />
+  <item type="id" name="selectTextMode" />
   <item type="id" name="switchInputMethod" />
   <item type="id" name="keyboardView" />
   <item type="id" name="closeButton" />
@@ -68,4 +69,7 @@
   <item type="id" name="accountPreferences" />
   <item type="id" name="smallIcon" />
   <item type="id" name="custom" />
+  <item type="id" name="home" />
+  <item type="id" name="fillInIntent" />
+  <item type="id" name="rowTypeId" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4005e96..8772b3a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -925,7 +925,9 @@
   <public type="anim" name="slide_in_left" id="0x010a0002" />
   <public type="anim" name="slide_out_right" id="0x010a0003" />
   <public type="anim" name="accelerate_decelerate_interpolator" id="0x010a0004" />
+  <!-- Acceleration curve matching Flash's quadratic ease out function. -->
   <public type="anim" name="accelerate_interpolator" id="0x010a0005" />
+  <!-- Acceleration curve matching Flash's quadratic ease in function. -->
   <public type="anim" name="decelerate_interpolator" id="0x010a0006" />
 
 <!-- ===============================================================
@@ -1183,7 +1185,7 @@
   <public type="attr" name="colorBackgroundCacheHint" id="0x010102ab" />
   <public type="attr" name="dropDownHorizontalOffset" id="0x010102ac" />
   <public type="attr" name="dropDownVerticalOffset" id="0x010102ad" />
-  
+
   <public type="style" name="Theme.Wallpaper" id="0x0103005e" />
   <public type="style" name="Theme.Wallpaper.NoTitleBar" id="0x0103005f" />
   <public type="style" name="Theme.Wallpaper.NoTitleBar.Fullscreen" id="0x01030060" />
@@ -1191,7 +1193,7 @@
   <public type="style" name="Theme.Light.WallpaperSettings" id="0x01030062" />
   <public type="style" name="TextAppearance.SearchResult.Title" id="0x01030063" />
   <public type="style" name="TextAppearance.SearchResult.Subtitle" id="0x01030064" />
-  
+
   <!-- Semi-transparent background that can be used when placing a dark
        themed UI on top of some arbitrary background (such as the
        wallpaper).  This darkens the background sufficiently that the UI
@@ -1199,7 +1201,7 @@
   <public type="drawable" name="screen_background_dark_transparent" id="0x010800a9" />
   <public type="drawable" name="screen_background_light_transparent" id="0x010800aa" />
   <public type="drawable" name="stat_notify_sdcard_prepare" id="0x010800ab" />
-  
+
 <!-- ===============================================================
      Resources added in version 6 of the platform (Eclair 2.0.1).
      =============================================================== -->
@@ -1211,7 +1213,7 @@
   <public type="attr" name="quickContactBadgeStyleSmallWindowSmall" id="0x010102b1" />
   <public type="attr" name="quickContactBadgeStyleSmallWindowMedium" id="0x010102b2" />
   <public type="attr" name="quickContactBadgeStyleSmallWindowLarge" id="0x010102b3" />
-  
+
 <!-- ===============================================================
      Resources added in version 7 of the platform (Eclair MR1).
      =============================================================== -->
@@ -1220,7 +1222,7 @@
   <public type="attr" name="author" id="0x010102b4" />
   <public type="attr" name="autoStart" id="0x010102b5" />
 
-    
+
 <!-- ===============================================================
      Resources added in version 8 of the platform (Eclair MR2).
      =============================================================== -->
@@ -1241,19 +1243,16 @@
   <public type="attr" name="tabStripEnabled" id="0x010102bd" />
 
   <public type="id" name="custom" id="0x0102002b" />
-    
+
   <public type="anim" name="cycle_interpolator" id="0x010a000c" />
 
 <!-- ===============================================================
      Resources introduced in Gingerbread.
      =============================================================== -->
-     
+  <eat-comment />
   <public type="attr" name="logo" id="0x010102be" />
   <public type="attr" name="xlargeScreens" id="0x010102bf" />
   <public type="attr" name="immersive" id="0x010102c0" />
-  <public type="attr" name="overScrollMode" id="0x010102c1" />
-  <public type="attr" name="overScrollHeader" id="0x010102c2" />
-  <public type="attr" name="overScrollFooter" id="0x010102c3" />
   <public type="attr" name="filterTouchesWhenObscured" id="0x010102c4" />
   <public type="attr" name="textSelectHandleLeft" id="0x010102c5" />
   <public type="attr" name="textSelectHandleRight" id="0x010102c6" />
@@ -1275,5 +1274,165 @@
   <public type="style" name="TextAppearance.StatusBar.Icon" id="0x01030066" />
   <public type="style" name="TextAppearance.StatusBar.EventContent" id="0x01030067" />
   <public type="style" name="TextAppearance.StatusBar.EventContent.Title" id="0x01030068" />
+
+<!-- ===============================================================
+     Resources proposed for Honeycomb.
+     =============================================================== -->
+  <eat-comment />
+  <public type="attr" name="adapter" />
+  <public type="attr" name="selection" />
+  <public type="attr" name="sortOrder" />
+  <public type="attr" name="uri" />
+  <public type="attr" name="from" />
+  <public type="attr" name="to" />
+  <public type="attr" name="as" />
+  <public type="attr" name="fromValue" />
+  <public type="attr" name="toValue" />
+  <public type="attr" name="column" />
+  <public type="attr" name="withExpression" />
+  <public type="attr" name="withClass" />
+  <public type="attr" name="allContactsName" />
+  <public type="attr" name="windowActionBar" />
+  <public type="attr" name="actionBarStyle" />
+  <public type="attr" name="navigationMode" />
+  <public type="attr" name="displayOptions" />
+  <public type="attr" name="subtitle" />
+  <public type="attr" name="customNavigationLayout" />
+  <public type="attr" name="hardwareAccelerated" />
+  <public type="attr" name="measureWithLargestChild" />
+  <public type="attr" name="animateFirstView" />
+  <public type="attr" name="dropDownSpinnerStyle" />
+  <public type="attr" name="actionDropDownStyle" />
+  <public type="attr" name="actionButtonStyle" />
+  <public type="attr" name="showAsAction" />
+  <public type="attr" name="previewImage" />
+  <public type="attr" name="actionModeBackground" />
+  <public type="attr" name="actionModeCloseDrawable" />
+  <public type="attr" name="windowActionModeOverlay" />
+  <public type="attr" name="valueFrom" />
+  <public type="attr" name="valueTo" />
+  <public type="attr" name="valueType" />
+  <public type="attr" name="propertyName" />
+  <public type="attr" name="ordering" />
+  <public type="attr" name="fragment" />
+  <public type="attr" name="windowActionBarOverlay" />
+  <public type="attr" name="fragmentOpenEnterAnimation" />
+  <public type="attr" name="fragmentOpenExitAnimation" />
+  <public type="attr" name="fragmentCloseEnterAnimation" />
+  <public type="attr" name="fragmentCloseExitAnimation" />
+  <public type="attr" name="fragmentNextEnterAnimation" />
+  <public type="attr" name="fragmentNextExitAnimation" />
+  <public type="attr" name="fragmentPrevEnterAnimation" />
+  <public type="attr" name="fragmentPrevExitAnimation" />
+  <public type="attr" name="actionBarSize" />
+  <public type="attr" name="imeSubtypeLocale" />
+  <public type="attr" name="imeSubtypeMode" />
+  <public type="attr" name="imeSubtypeExtraValue" />
+  <public type="attr" name="splitMotionEvents" />
+  <public type="attr" name="listChoiceBackgroundIndicator" />
+  <public type="attr" name="spinnerMode" />
+  <public type="attr" name="animateLayoutChanges" />
+  <public type="attr" name="actionBarTabStyle" />
+  <public type="attr" name="actionBarTabBarStyle" />
+  <public type="attr" name="actionBarTabTextStyle" />
+  <public type="attr" name="actionOverflowButtonStyle" />
+  <public type="attr" name="actionModeCloseButtonStyle" />
+  <public type="attr" name="titleTextStyle" />
+  <public type="attr" name="subtitleTextStyle" />
+  <public type="attr" name="iconifiedByDefault" />
+  <public type="attr" name="actionLayout" />
+  <public type="attr" name="actionViewClass" />
+  <public type="attr" name="activatedBackgroundIndicator" />
+  <public type="attr" name="state_activated" />
+  <public type="attr" name="listPopupWindowStyle" />
+  <public type="attr" name="popupMenuStyle" />
+  <public type="attr" name="textAppearanceLargePopupMenu" />
+  <public type="attr" name="textAppearanceSmallPopupMenu" />
+  <public type="attr" name="breadCrumbTitle" />
+  <public type="attr" name="breadCrumbShortTitle" />
+  <public type="attr" name="listDividerAlertDialog" />
+  <public type="attr" name="textColorAlertDialogListItem" />
+  <public type="attr" name="loopViews" />
+  <public type="attr" name="dialogTheme" />
+  <public type="attr" name="alertDialogTheme" />
+  <public type="attr" name="textLineHeight" />
+  <public type="attr" name="dividerVertical" />
+  <public type="attr" name="buttonGroupStyle" />
+  <public type="attr" name="alertDialogButtonGroupStyle" />
+  <public type="attr" name="homeAsUpIndicator" />
+  <public type="attr" name="enterFadeDuration" />
+  <public type="attr" name="exitFadeDuration" />
+
+  <public type="anim" name="animator_fade_in" />
+  <public type="anim" name="animator_fade_out" />
+
+  <!-- Acceleration curve matching Flash's cubic ease out function. -->
+  <public type="anim" name="accelerate_cubic_interpolator" />
+  <!-- Acceleration curve matching Flash's cubic ease in function. -->
+  <public type="anim" name="decelerate_cubic_interpolator" />
+  <!-- Acceleration curve matching Flash's quint ease out function. -->
+  <public type="anim" name="accelerate_quint_interpolator" />
+  <!-- Acceleration curve matching Flash's quint ease in function. -->
+  <public type="anim" name="decelerate_quint_interpolator" />
   
+  <public type="id" name="home" />
+  <!-- Context menu ID for the "Select text..." menu item to switch to text
+       selection context mode in text views. -->
+  <public type="id" name="selectTextMode" />
+
+  <!-- Standard content view for a {@link android.app.ListFragment}.
+       If you are implementing a subclass of ListFragment with your
+       own customized content, you can include this layout in that
+       content to still retain all of the standard functionality of
+       the base class. -->
+  <public type="layout" name="list_content" />
+
+  <!-- A simple ListView item layout which can contain text and support (single or multiple) item selection. -->
+  <public type="layout" name="simple_selectable_list_item" />
+
+  <!-- A version of {@link #simple_list_item_1} that is able to change its
+       background state to indicate when it is activated (that is checked by
+       a ListView). -->
+  <public type="layout" name="simple_list_item_activated_1" />
+
+  <!-- A version of {@link #simple_list_item_2} that is able to change its
+       background state to indicate when it is activated (that is checked by
+       a ListView). -->
+  <public type="layout" name="simple_list_item_activated_2" />
+
+  <public type="style" name="Theme.WithActionBar" />
+  <public type="style" name="Widget.Spinner.DropDown" />
+  <public type="style" name="Widget.ActionButton" />
+  <public type="style" name="Theme.Dialog.NoFrame" />
+  <public type="style" name="Theme.NoTitleBar.OverlayActionModes" />
+
+  <public type="style" name="Theme.Holo" />
+  <public type="style" name="Theme.Light.Holo" />
+  <public type="style" name="Theme.Holo.NoActionBar" />
+  <public type="style" name="Theme.Holo.NoActionBar.Fullscreen" />
+  <public type="style" name="Theme.Light.Holo.NoActionBar" />
+  <public type="style" name="Theme.Light.Holo.NoActionBar.Fullscreen" />
+  <public type="style" name="Theme.Holo.Light" />
+  <public type="style" name="Theme.Holo.Dialog" />
+  <public type="style" name="Theme.Holo.Light.Dialog" />
+  <public type="style" name="Theme.Holo.Extended" />
+  <public type="style" name="Theme.Holo.Light.Extended" />
+  <public type="style" name="Theme.Holo.Dialog.Alert" />
+  <public type="style" name="Theme.Holo.Light.Dialog.Alert" />
+
+  <public type="style" name="Widget.ListPopupWindow" />
+  <public type="style" name="Widget.PopupMenu" />
+  <public type="style" name="Widget.ActionButton.Overflow" />
+  <public type="style" name="Widget.ActionButton.CloseMode" />
+  <public type="style" name="TextAppearance.Widget.PopupMenu.Large" />
+  <public type="style" name="TextAppearance.Widget.PopupMenu.Small" />
+  <public type="style" name="Widget.FragmentBreadCrumbs" />
+
+  <public type="string" name="selectTextMode" />
+
+  <public type="attr" name="actionModeCutDrawable" />
+  <public type="attr" name="actionModeCopyDrawable" />
+  <public type="attr" name="actionModePasteDrawable" />
+  <public type="attr" name="textEditPasteWindowLayout" />
+  <public type="attr" name="textEditNoPasteWindowLayout" />
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 075cc66..1fd5146 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -257,7 +257,9 @@
     <string name="contentServiceTooManyDeletesNotificationDesc">Too many <xliff:g id="content_type">%s</xliff:g> deletes.</string>
 
     <!-- If MMS discovers there isn't much space left on the device, it will show a toast with this message. -->
-    <string name="low_memory">Phone storage is full! Delete some files to free space.</string>
+    <string name="low_memory" product="tablet">Tablet storage is full! Delete some files to free space.</string>
+    <!-- If MMS discovers there isn't much space left on the device, it will show a toast with this message. -->
+    <string name="low_memory" product="default">Phone storage is full! Delete some files to free space.</string>
 
 
     <!-- Display name for any time a piece of data refers to the owner of the phone. For example, this could be used in place of the phone's phone number. -->
@@ -265,7 +267,9 @@
 
     <!-- Power Dialog --> <skip />
     <!-- Title for the Phone Options dialog to lock the screen, turn off the phone etc. -->
-    <string name="power_dialog">Phone options</string>
+    <string name="power_dialog" product="tablet">Tablet options</string>
+    <!-- Title for the Phone Options dialog to lock the screen, turn off the phone etc. -->
+    <string name="power_dialog" product="default">Phone options</string>
     <!-- Button to turn on silent mode, within the Phone Options dialog -->
     <string name="silent_mode">Silent mode</string>
     <!-- Button to turn on the radio, within the Phone Options dialog -->
@@ -280,16 +284,34 @@
     <!-- Shutdown Progress Dialog. This is shown if the user chooses to power off the phone. -->
     <string name="shutdown_progress">Shutting down\u2026</string>
 
-    <!-- Shutdown Confirmation Dialog.  When the user chooses to power off the phone, there will be a confirmation dialog.  This is the message. -->
-    <string name="shutdown_confirm">Your phone will shut down.</string>
+    <!-- Shutdown Confirmation Dialog.  When the user chooses to power off the phone, there will
+         be a confirmation dialog.  This is the message. -->
+    <string name="shutdown_confirm" product="tablet">Your tablet will shut down.</string>
+    <!-- Shutdown Confirmation Dialog.  When the user chooses to power off the phone, there will
+         be a confirmation dialog.  This is the message. -->
+    <string name="shutdown_confirm" product="default">Your phone will shut down.</string>
 
-    <!-- Recent Tasks dialog: title -->
+    <!-- Shutdown Confirmation Dialog.  When the user chooses to power off the phone, it asks
+         the user if they'd like to shut down.  This is the message.  This is used instead of
+         shutdown_confirm when the system is configured to use long press to go directly to the
+         power off dialog instead of the global actions menu. -->
+    <string name="shutdown_confirm_question">Would you like to shut down?</string>
+
+    <!-- Recent Tasks dialog: title
+     TODO: this should move to SystemUI.apk, but the code for the old 
+            recent dialog is still in the framework
+     -->
     <string name="recent_tasks_title">Recent</string>
-    <!-- Recent Tasks dialog: message when there are no recent applications -->
+    <!-- Recent Tasks dialog: message when there are no recent applications
+     TODO: this should move to SystemUI.apk, but the code for the old 
+            recent dialog is still in the framework
+     -->
     <string name="no_recent_tasks">No recent applications.</string>
 
     <!-- Title of the Global Actions Dialog -->
-    <string name="global_actions">Phone options</string>
+    <string name="global_actions" product="tablet">Tablet options</string>
+    <!-- Title of the Global Actions Dialog -->
+    <string name="global_actions" product="default">Phone options</string>
 
     <!-- label for item that locks the phone in the phone options dialog -->
     <string name="global_action_lock">Screen lock</string>
@@ -336,7 +358,10 @@
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_personalInfo">Your personal information</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permgroupdesc_personalInfo">Direct access to your contacts
+    <string name="permgroupdesc_personalInfo" product="tablet">Direct access to your contacts
+        and calendar stored on the tablet.</string>
+    <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permgroupdesc_personalInfo" product="default">Direct access to your contacts
         and calendar stored on the phone.</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
@@ -436,14 +461,22 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readSms">read SMS or MMS</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readSms">Allows application to read
+    <string name="permdesc_readSms" product="tablet">Allows application to read
+      SMS messages stored on your tablet or SIM card. Malicious applications
+      may read your confidential messages.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_readSms" product="default">Allows application to read
       SMS messages stored on your phone or SIM card. Malicious applications
       may read your confidential messages.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_writeSms">edit SMS or MMS</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_writeSms">Allows application to write
+    <string name="permdesc_writeSms" product="tablet">Allows application to write
+      to SMS messages stored on your tablet or SIM card. Malicious applications
+      may delete your messages.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_writeSms" product="default">Allows application to write
       to SMS messages stored on your phone or SIM card. Malicious applications
       may delete your messages.</string>
 
@@ -535,7 +568,7 @@
         monitor and control how the system launches activities.
         Malicious applications may completely compromise the system. This
         permission is only needed for development, never for normal
-        phone usage.</string>
+        use.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_broadcastPackageRemoved">send package removed broadcast</string>
@@ -597,7 +630,7 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_systemAlertWindow">Allows an application to
         show system alert windows. Malicious applications can take over the
-        entire screen of the phone.</string>
+        entire screen.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_setAnimationScale">modify global animation speed</string>
@@ -615,7 +648,11 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_injectEvents">press keys and control buttons</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_injectEvents">Allows an application to deliver
+    <string name="permdesc_injectEvents" product="tablet">Allows an application to deliver
+        its own input events (key presses, etc.) to other applications. Malicious
+        applications can use this to take over the tablet.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_injectEvents" product="default">Allows an application to deliver
         its own input events (key presses, etc.) to other applications. Malicious
         applications can use this to take over the phone.</string>
 
@@ -696,7 +733,11 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_clearAppCache">delete all application cache data</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_clearAppCache">Allows an application to free phone storage
+    <string name="permdesc_clearAppCache" product="tablet">Allows an application to free tablet storage
+        by deleting files in application cache directory. Access is very
+        restricted usually to system process.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_clearAppCache" product="default">Allows an application to free phone storage
         by deleting files in application cache directory. Access is very
         restricted usually to system process.</string>
 
@@ -708,7 +749,12 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readLogs">read sensitive log data</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readLogs">Allows an application to read from the
+    <string name="permdesc_readLogs" product="tablet">Allows an application to read from the
+        system\'s various log files.  This allows it to discover general
+        information about what you are doing with the tablet, potentially
+        including personal or private information.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_readLogs" product="default">Allows an application to read from the
         system\'s various log files.  This allows it to discover general
         information about what you are doing with the phone, potentially
         including personal or private information.</string>
@@ -724,9 +770,15 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_changeComponentState">enable or disable application components</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_changeComponentState">Allows an application to change whether a
+    <string name="permdesc_changeComponentState" product="tablet">Allows an application to change whether a
         component of another application is enabled or not. Malicious applications can use this
-        to disable important phone capabilities. Care must be used with permission, as it is
+        to disable important tablet capabilities. Care must be used with this permission, as it is
+        possible to get application components into an unusable, inconsistent, or unstable state.
+    </string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_changeComponentState" product="default">Allows an application to change whether a
+        component of another application is enabled or not. Malicious applications can use this
+        to disable important phone capabilities. Care must be used with this permission, as it is
         possible to get application components into an unusable, inconsistent, or unstable state.
     </string>
 
@@ -758,7 +810,12 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_receiveBootCompleted">automatically start at boot</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_receiveBootCompleted">Allows an application to
+    <string name="permdesc_receiveBootCompleted" product="tablet">Allows an application to
+        have itself started as soon as the system has finished booting.
+        This can make it take longer to start the tablet and allow the
+        application to slow down the overall tablet by always running.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_receiveBootCompleted" product="default">Allows an application to
         have itself started as soon as the system has finished booting.
         This can make it take longer to start the phone and allow the
         application to slow down the overall phone by always running.</string>
@@ -766,7 +823,12 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_broadcastSticky">send sticky broadcast</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_broadcastSticky">Allows an application to send
+    <string name="permdesc_broadcastSticky" product="tablet">Allows an application to send
+        sticky broadcasts, which remain after the broadcast ends.
+        Malicious applications can make the tablet slow or unstable by causing it
+        to use too much memory.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_broadcastSticky" product="default">Allows an application to send
         sticky broadcasts, which remain after the broadcast ends.
         Malicious applications can make the phone slow or unstable by causing it
         to use too much memory.</string>
@@ -774,29 +836,41 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readContacts">read contact data</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readContacts">Allows an application to read all
+    <string name="permdesc_readContacts" product="tablet">Allows an application to read all
+        of the contact (address) data stored on your tablet. Malicious applications
+        can use this to send your data to other people.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_readContacts" product="default">Allows an application to read all
         of the contact (address) data stored on your phone. Malicious applications
         can use this to send your data to other people.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_writeContacts">write contact data</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_writeContacts">Allows an application to modify the
+    <string name="permdesc_writeContacts" product="tablet">Allows an application to modify the
+        contact (address) data stored on your tablet. Malicious
+        applications can use this to erase or modify your contact data.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_writeContacts" product="default">Allows an application to modify the
         contact (address) data stored on your phone. Malicious
         applications can use this to erase or modify your contact data.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readCalendar">read calendar events</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readCalendar">Allows an application to read all
+    <string name="permdesc_readCalendar" product="tablet">Allows an application to read all
+        of the calendar events stored on your tablet. Malicious applications
+        can use this to send your calendar events to other people.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_readCalendar" product="default">Allows an application to read all
         of the calendar events stored on your phone. Malicious applications
         can use this to send your calendar events to other people.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_writeCalendar">add or modify calendar events and send email to guests</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_writeCalendar">Allows an application to add or change the 
-        events on your calendar, which may send email to guests. Malicious applications can use this 
+    <string name="permdesc_writeCalendar">Allows an application to add or change the
+        events on your calendar, which may send email to guests. Malicious applications can use this
         to erase or modify your calendar events or to send email to guests.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -823,7 +897,12 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessFineLocation">fine (GPS) location</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessFineLocation">Access fine location sources such as the
+    <string name="permdesc_accessFineLocation" product="tablet">Access fine location sources such as the
+        Global Positioning System on the tablet, where available.
+        Malicious applications can use this to determine where you are, and may
+        consume additional battery power.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_accessFineLocation" product="default">Access fine location sources such as the
         Global Positioning System on the phone, where available.
         Malicious applications can use this to determine where you are, and may
         consume additional battery power.</string>
@@ -831,7 +910,11 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessCoarseLocation">coarse (network-based) location</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessCoarseLocation">Access coarse location sources such as the cellular
+    <string name="permdesc_accessCoarseLocation" product="tablet">Access coarse location sources such as the cellular
+        network database to determine an approximate tablet location, where available. Malicious
+        applications can use this to determine approximately where you are.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_accessCoarseLocation" product="default">Access coarse location sources such as the cellular
         network database to determine an approximate phone location, where available. Malicious
         applications can use this to determine approximately where you are.</string>
 
@@ -867,15 +950,25 @@
         images the camera is seeing.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_brick">permanently disable phone</string>
+    <string name="permlab_brick" product="tablet">permanently disable tablet</string>
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_brick" product="default">permanently disable phone</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_brick">Allows the application to
+    <string name="permdesc_brick" product="tablet">Allows the application to
+        disable the entire tablet permanently. This is very dangerous.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_brick" product="default">Allows the application to
         disable the entire phone permanently. This is very dangerous.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_reboot">force phone reboot</string>
+    <string name="permlab_reboot" product="tablet">force tablet reboot</string>
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_reboot" product="default">force phone reboot</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_reboot">Allows the application to
+    <string name="permdesc_reboot" product="tablet">Allows the application to
+        force the tablet to reboot.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_reboot" product="default">Allows the application to
         force the phone to reboot.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -954,7 +1047,9 @@
         services.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_performCdmaProvisioning">directly start CDMA phone setup</string>
+    <string name="permlab_performCdmaProvisioning" product="tablet">directly start CDMA tablet setup</string>
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_performCdmaProvisioning" product="default">directly start CDMA phone setup</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_performCdmaProvisioning">Allows the application to start CDMA provisioning.
         Malicious applications may unnecessarily start CDMA provisioning</string>
@@ -997,21 +1092,35 @@
         is connected to and the like.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_wakeLock">prevent phone from sleeping</string>
+    <string name="permlab_wakeLock" product="tablet">prevent tablet from sleeping</string>
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_wakeLock" product="default">prevent phone from sleeping</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_wakeLock">Allows an application to prevent
+    <string name="permdesc_wakeLock" product="tablet">Allows an application to prevent
+        the tablet from going to sleep.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_wakeLock" product="default">Allows an application to prevent
         the phone from going to sleep.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_devicePower">power phone on or off</string>
+    <string name="permlab_devicePower" product="tablet">power tablet on or off</string>
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_devicePower" product="default">power phone on or off</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_devicePower">Allows the application to turn the
+    <string name="permdesc_devicePower" product="tablet">Allows the application to turn the
+        tablet on or off.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_devicePower" product="default">Allows the application to turn the
         phone on or off.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_factoryTest">run in factory test mode</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_factoryTest">Run as a low-level manufacturer test,
+    <string name="permdesc_factoryTest" product="tablet">Run as a low-level manufacturer test,
+        allowing complete access to the tablet hardware. Only available
+        when a tablet is running in manufacturer test mode.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_factoryTest" product="default">Run as a low-level manufacturer test,
         allowing complete access to the phone hardware. Only available
         when a phone is running in manufacturer test mode.</string>
 
@@ -1037,13 +1146,19 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_setTime">set time</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_setTime">Allows an application to change
+    <string name="permdesc_setTime" product="tablet">Allows an application to change
+        the tablet\'s clock time.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_setTime" product="default">Allows an application to change
         the phone\'s clock time.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_setTimeZone">set time zone</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_setTimeZone">Allows an application to change
+    <string name="permdesc_setTimeZone" product="tablet">Allows an application to change
+        the tablet\'s time zone.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_setTimeZone" product="default">Allows an application to change
         the phone\'s time zone.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -1055,7 +1170,10 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_getAccounts">discover known accounts</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_getAccounts">Allows an application to get
+    <string name="permdesc_getAccounts" product="tablet">Allows an application to get
+      the list of accounts known by the tablet.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_getAccounts" product="default">Allows an application to get
       the list of accounts known by the phone.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -1141,14 +1259,22 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_bluetoothAdmin">bluetooth administration</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_bluetoothAdmin">Allows an application to configure
+    <string name="permdesc_bluetoothAdmin" product="tablet">Allows an application to configure
+      the local Bluetooth tablet, and to discover and pair with remote
+      devices.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bluetoothAdmin" product="default">Allows an application to configure
       the local Bluetooth phone, and to discover and pair with remote
       devices.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_bluetooth">create Bluetooth connections</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_bluetooth">Allows an application to view
+    <string name="permdesc_bluetooth" product="tablet">Allows an application to view
+      configuration of the local Bluetooth tablet, and to make and accept
+      connections with paired devices.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bluetooth" product="default">Allows an application to view
       configuration of the local Bluetooth phone, and to make and accept
       connections with paired devices.</string>
 
@@ -1159,7 +1285,6 @@
       with Near Field Communication (NFC) tags, cards, and readers.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_disableKeyguard">disable keylock</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_disableKeyguard">Allows an application to disable
       the keylock and any associated password security. A legitimate example of
@@ -1218,6 +1343,11 @@
     <string name="permdesc_sdcardWrite" product="default">Allows an application to write to the SD card.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_mediaStorageWrite" product="default">modify/delete internal media storage contents</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_mediaStorageWrite" product="default">Allows an application to modify the contents of the internal media storage.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_cache_filesystem">access the cache filesystem</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_cache_filesystem">Allows an application to read and write the cache filesystem.</string>
@@ -1232,14 +1362,18 @@
     <!-- Title of policy access to limiting the user's password choices -->
     <string name="policylab_limitPassword">Set password rules</string>
     <!-- Description of policy access to limiting the user's password choices -->
-    <string name="policydesc_limitPassword">Control the length and the characters allowed in
-    screen-unlock passwords</string>
+    <string name="policydesc_limitPassword">Control the length and the characters 
+    allowed in screen-unlock passwords</string>
     <!-- Title of policy access to watch user login attempts -->
     <string name="policylab_watchLogin">Monitor screen-unlock attempts</string>
     <!-- Description of policy access to watch user login attempts -->
-    <string name="policydesc_watchLogin">Monitor the number of incorrect passwords entered when unlocking 
-    the screen, and lock the phone or erase all the phone\'s data if too many incorrect passwords are entered
-    </string>
+    <string name="policydesc_watchLogin" product="tablet">Monitor the number of incorrect passwords 
+    entered when unlocking the screen, and lock the tablet or erase all the tablet\'s 
+    data if too many incorrect passwords are entered</string>
+    <!-- Description of policy access to watch user login attempts -->
+    <string name="policydesc_watchLogin" product="default">Monitor the number of incorrect passwords 
+    entered when unlocking the screen, and lock the phone or erase all the phone\'s 
+    data if too many incorrect passwords are entered</string>
     <!-- Title of policy access to reset user's password -->
     <string name="policylab_resetPassword">Change the screen-unlock password</string>
     <!-- Description of policy access to reset user's password -->
@@ -1251,7 +1385,16 @@
     <!-- Title of policy access to wipe the user's data -->
     <string name="policylab_wipeData">Erase all data</string>
     <!-- Description of policy access to wipe the user's data -->
-    <string name="policydesc_wipeData">Erase the phone\'s data without warning, by performing a factory data reset</string>
+    <string name="policydesc_wipeData" product="tablet">Erase the tablet\'s data without warning, 
+    by performing a factory data reset</string>
+    <!-- Description of policy access to wipe the user's data -->
+    <string name="policydesc_wipeData" product="default">Erase the phone\'s data without warning, 
+    by performing a factory data reset</string>
+    <string name="policylab_setGlobalProxy">Set the device global proxy</string>
+    <!-- Description of policy access to wipe the user's data -->
+    <string name="policydesc_setGlobalProxy">Set the device global proxy
+        to be used while policy is enabled. Only the first device admin
+        sets the effective global proxy.</string>
 
     <!-- The order of these is important, don't reorder without changing Contacts.java --> <skip />
     <!-- Phone number types from android.provider.Contacts. This could be used when adding a new phone number for a contact, for example. -->
@@ -1357,12 +1500,14 @@
     <!-- MMS phone number type -->
     <string name="phoneTypeMms">MMS</string>
 
-    <!-- Label for a birthday event -->
+    <!-- Label for custom events  [CHAR LIMIT=20] -->
+    <string name="eventTypeCustom">Custom</string>
+    <!-- Label for a birthday event  [CHAR LIMIT=20] -->
     <string name="eventTypeBirthday">Birthday</string>
-    <!-- Label for an anniversary event -->
+    <!-- Label for an anniversary event  [CHAR LIMIT=20] -->
     <string name="eventTypeAnniversary">Anniversary</string>
-    <!-- Label for other events -->
-    <string name="eventTypeOther">Event</string>
+    <!-- Label for other events  [CHAR LIMIT=20] -->
+    <string name="eventTypeOther">Other</string>
 
     <!-- Custom email type -->
     <string name="emailTypeCustom">Custom</string>
@@ -1421,6 +1566,37 @@
     <!-- Custom organization type -->
     <string name="orgTypeCustom">Custom</string>
 
+    <!-- Custom relationship custom [CHAR LIMIT=20] -->
+    <string name="relationTypeCustom">Custom</string>
+    <!-- Assistant relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeAssistant">Assistant</string>
+    <!-- Brother relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeBrother">Brother</string>
+    <!-- Child relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeChild">Child</string>
+    <!-- Domestic Partner relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeDomesticPartner">Domestic Partner</string>
+    <!-- Father relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeFather">Father</string>
+    <!-- Friend relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeFriend">Friend</string>
+    <!-- Manager relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeManager">Manager</string>
+    <!-- Mother relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeMother">Mother</string>
+    <!-- Parent relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeParent">Parent</string>
+    <!-- Partner relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypePartner">Partner</string>
+    <!-- Referred by relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeReferredBy">Referred by</string>
+    <!-- Relative relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeRelative">Relative</string>
+    <!-- Sister relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeSister">Sister</string>
+    <!-- Spouse relationship type [CHAR LIMIT=20] -->
+    <string name="relationTypeSpouse">Spouse</string>
+    
     <!-- Custom SIP address type -->
     <string name="sipAddressTypeCustom">Custom</string>
     <!-- Home SIP address type -->
@@ -1430,20 +1606,18 @@
     <!-- Other SIP address type -->
     <string name="sipAddressTypeOther">Other</string>
 
-    <!-- Attbution of a contact status update, when the time of update is unknown -->
-    <string name="contact_status_update_attribution">via <xliff:g id="source" example="Google Talk">%1$s</xliff:g></string>
-
-    <!-- Attbution of a contact status update, when the time of update is known -->
-    <string name="contact_status_update_attribution_with_date"><xliff:g id="date" example="3 hours ago">%1$s</xliff:g> via <xliff:g id="source" example="Google Talk">%2$s</xliff:g></string>
-
     <!-- Instructions telling the user to enter their SIM PIN to unlock the keyguard.
          Displayed in one line in a large font.  -->
     <string name="keyguard_password_enter_pin_code">Enter PIN code</string>
 
-    <!-- Instructions telling the user to enter their PIN password to unlock the keyguard.
+    <!-- Instructions telling the user to enter their text password to unlock the keyguard.
          Displayed in one line in a large font.  -->
     <string name="keyguard_password_enter_password_code">Enter password to unlock</string>
 
+    <!-- Instructions telling the user to enter their PIN password to unlock the keyguard.
+         Displayed in one line in a large font.  -->
+    <string name="keyguard_password_enter_pin_password_code">Enter PIN to unlock</string>
+
     <!-- Instructions telling the user that they entered the wrong pin while trying
          to unlock the keyguard.  Displayed in one line in a large font.  -->
     <string name="keyguard_password_wrong_pin_code">Incorrect PIN code!</string>
@@ -1480,6 +1654,8 @@
     <string name="lockscreen_pattern_correct">Correct!</string>
     <!-- On the unlock pattern screen, shown when the user enters the wrong lock pattern and must try again. -->
     <string name="lockscreen_pattern_wrong">Sorry, try again</string>
+    <!-- On the unlock password screen, shown when the user enters the wrong lock password and must try again. -->
+    <string name="lockscreen_password_wrong">Sorry, try again</string>
 
     <!-- When the lock screen is showing and the phone plugged in, and the battery
          is not fully charged, show the current charge %.  -->
@@ -1497,7 +1673,9 @@
     <!-- Shown in the lock screen when there is no SIM card. -->
     <string name="lockscreen_missing_sim_message_short">No SIM card.</string>
     <!-- Shown in the lock screen when there is no SIM card. -->
-    <string name="lockscreen_missing_sim_message">No SIM card in phone.</string>
+    <string name="lockscreen_missing_sim_message" product="tablet">No SIM card in tablet.</string>
+    <!-- Shown in the lock screen when there is no SIM card. -->
+    <string name="lockscreen_missing_sim_message" product="default">No SIM card in phone.</string>
     <!-- Shown in the lock screen to ask the user to insert a SIM card. -->
     <string name="lockscreen_missing_sim_instructions">Please insert a SIM card.</string>
 
@@ -1523,15 +1701,38 @@
          progress dialog in the meantime.  this is the emssage. -->
     <string name="lockscreen_sim_unlock_progress_dialog_message">Unlocking SIM card\u2026</string>
 
-    <!-- For the unlock screen, Information message shown in dialog when user has too many failed attempts -->
+    <!-- For the unlock screen, Information message shown in dialog when user has too many failed attempts at
+         drawing the unlock pattern -->
     <string name="lockscreen_too_many_failed_attempts_dialog_message">
         You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
         \n\nPlease try again in <xliff:g id="number">%d</xliff:g> seconds.
     </string>
 
+    <!-- For the unlock screen, Information message shown in dialog when user has too many failed attempts at
+         entering the password -->
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message">
+        You have incorrectly entered your password <xliff:g id="number">%d</xliff:g> times.
+        \n\nPlease try again in <xliff:g id="number">%d</xliff:g> seconds.
+    </string>
+
+    <!-- For the unlock screen, Information message shown in dialog when user has too many failed attempts at
+         entering the PIN -->
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message">
+        You have incorrectly entered your PIN <xliff:g id="number">%d</xliff:g> times.
+        \n\nPlease try again in <xliff:g id="number">%d</xliff:g> seconds.
+    </string>
+
     <!-- For the unlock screen, Information message shown in dialog when user is almost at the limit
          where they will be locked out and may have to enter an alternate username/password to unlock the phone -->
-    <string name="lockscreen_failed_attempts_almost_glogin">
+    <string name="lockscreen_failed_attempts_almost_glogin" product="tablet">
+        You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
+       After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
+       you will be asked to unlock your tablet using your Google sign-in.\n\n
+       Please try again in <xliff:g id="number">%d</xliff:g> seconds.
+    </string>
+    <!-- For the unlock screen, Information message shown in dialog when user is almost at the limit
+         where they will be locked out and may have to enter an alternate username/password to unlock the phone -->
+    <string name="lockscreen_failed_attempts_almost_glogin" product="default">
         You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times.
        After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts,
        you will be asked to unlock your phone using your Google sign-in.\n\n
@@ -1598,8 +1799,10 @@
     <string name="factorytest_reboot">Reboot</string>
 
     <!-- Do not translate.  WebView User Agent string -->
-    <string name="web_user_agent" translatable="false"><xliff:g id="x">Mozilla/5.0 (Linux; U; Android %s)
-        AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1</xliff:g></string>
+    <string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
+        AppleWebKit/534.11 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.11</string>
+    <!-- Do not translate.  WebView User Agent targeted content -->
+    <string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
 
     <!-- Title for a JavaScript dialog. "The page at <url of current page> says:" -->
     <string name="js_dialog_title">The page at \'<xliff:g id="title">%s</xliff:g>\' says:</string>
@@ -1615,6 +1818,23 @@
     <!-- Toast for double-tap -->
     <string name="double_tap_toast">Tip: double-tap to zoom in and out.</string>
 
+    <!-- Text to show in the auto complete drop down list on a text view when the browser can auto fill the entire form -->
+    <string name="autofill_this_form">AutoFill</string>
+
+    <!-- String used to separate FirstName and LastName when writing out a local name
+         e.g. John<separator>Smith [CHAR-LIMIT=NONE]-->
+    <string name="autofill_address_name_separator">\u0020</string>
+    <!-- Format string for displaying a name. $1 is First Name, $2 is autofill_address_name_separator, $3 is Last Name.
+         e.g. (John)( )(Smith) -->
+    <string name="autofill_address_summary_name_format">$1$2$3</string>
+
+    <!-- String used to separate Name and Address Line 1
+         e.g. John Smith<separator>123 Main Street [CHAR-LIMIT=NONE]-->
+    <string name="autofill_address_summary_separator">,\u0020</string>
+    <!-- Format string for displaying a name and address summary. $1 is the Full Name, $2 is autofill_address_summary_separator, $3 is the Address
+         e.g. (John Smith)(, )(123 Main Street) -->
+    <string name="autofill_address_summary_format">$1$2$3</string>
+
     <!-- Title of an application permission, listed so the user can choose whether
         they want to allow the application to do this. -->
     <string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string>
@@ -1627,7 +1847,12 @@
     <string name="permlab_writeHistoryBookmarks">write Browser\'s history and bookmarks</string>
     <!-- Description of an application permission, listed so the user can choose whether
         they want to allow the application to do this. -->
-    <string name="permdesc_writeHistoryBookmarks">Allows an application to modify the
+    <string name="permdesc_writeHistoryBookmarks" product="tablet">Allows an application to modify the
+        Browser\'s history or bookmarks stored on your tablet. Malicious applications
+        can use this to erase or modify your Browser\'s data.</string>
+    <!-- Description of an application permission, listed so the user can choose whether
+        they want to allow the application to do this. -->
+    <string name="permdesc_writeHistoryBookmarks" product="default">Allows an application to modify the
         Browser\'s history or bookmarks stored on your phone. Malicious applications
         can use this to erase or modify your Browser\'s data.</string>
 
@@ -1871,35 +2096,39 @@
     <!-- Item on EditText context menu. This action is used to select all text in the edit field. -->
     <string name="selectAll">Select all</string>
 
-    <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. [CHAR LIMIT=20] -->
-    <string name="selectText">Select word</string>
-
     <!-- Item on EditText context menu.  This action is used to cut selected the text into the clipboard.  -->
     <string name="cut">Cut</string>
 
     <!-- Item on EditText context menu. This action is used to cut selected the text into the clipboard. -->
     <string name="copy">Copy</string>
 
-    <!-- Item on EditText context menu. This action is used t o paste from the clipboard into the eidt field -->
+    <!-- Item on EditText context menu. This action is used to paste from the clipboard into the eidt field -->
     <string name="paste">Paste</string>
 
+    <!-- Text displayed in a popup dialog in TextEdit when the clipboard is empty. 'paste' is used otherwise. [CHAR LIMIT=20] -->
+    <string name="pasteDisabled">Nothing to paste</string>
+
     <!-- Item on EditText context menu. This action is used to copy a URL from the edit field into the clipboard. -->
     <string name="copyUrl">Copy URL</string>
 
+    <!-- Item on EditText context menu. Added only when the context menu is not empty, it enable selection context mode. [CHAR LIMIT=20] -->
+    <string name="selectTextMode">Select text...</string>
+
+    <!-- Text selection contextual mode title, displayed in the CAB. [CHAR LIMIT=20] -->
+    <string name="textSelectionCABTitle">Text selection</string>
+
     <!-- EditText context menu -->
     <string name="inputMethod">Input method</string>
 
-    <!-- Item on EditText context menu, used to add a word to the
-         input method dictionary. -->
-    <string name="addToDictionary">"Add \"<xliff:g id="word" example="rickroll">%s</xliff:g>\" to dictionary</string>
-
-    <!-- Title for EditText context menu -->
-    <string name="editTextMenuTitle">Edit text</string>
+    <!-- Title for EditText context menu [CHAR LIMIT=20] -->
+    <string name="editTextMenuTitle">Text actions</string>
 
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the title of that notification. -->
     <string name="low_internal_storage_view_title">Low on space</string>
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the message of that notification. -->
-    <string name="low_internal_storage_view_text">Phone storage space is getting low.</string>
+    <string name="low_internal_storage_view_text" product="tablet">Tablet storage space is getting low.</string>
+    <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the message of that notification. -->
+    <string name="low_internal_storage_view_text" product="default">Phone storage space is getting low.</string>
 
     <!-- Preference framework strings. -->
     <string name="ok">OK</string>
@@ -1913,6 +2142,9 @@
          combined with setIcon(android.R.drawable.ic_dialog_alert) -->
     <string name="dialog_alert_title">Attention</string>
 
+    <!-- Text shown by list fragment when waiting for data to display. -->
+    <string name="loading">Loading...</string>
+
     <!-- Default text for a button that can be toggled on and off. -->
     <string name="capital_on">ON</string>
     <!-- Default text for a button that can be toggled on and off. -->
@@ -1969,23 +2201,23 @@
 
     <!-- Notification text to tell the user that a heavy-weight application is running. -->
     <string name="heavy_weight_notification"><xliff:g id="app">%1$s</xliff:g> running</string>
-    
+
     <!-- Notification details to tell the user that a heavy-weight application is running. -->
     <string name="heavy_weight_notification_detail">Select to switch to application</string>
-    
+
     <!-- Title of dialog prompting whether user wants to switch between heavy-weight apps. -->
     <string name="heavy_weight_switcher_title">Switch applications?</string>
-    
+
     <!-- Descriptive text for switching to a new heavy-weight application. -->
     <string name="heavy_weight_switcher_text">Another application is already running
     that must be stopped before you can start a new one.</string>
-    
+
     <string name="old_app_action">Return to <xliff:g id="old_app">%1$s</xliff:g></string>
     <string name="old_app_description">Don\'t start the new application.</string>
-    
+
     <string name="new_app_action">Start <xliff:g id="old_app">%1$s</xliff:g></string>
     <string name="new_app_description">Stop the old application without saving.</string>
-    
+
     <!-- Displayed in the title of the chooser for things to do with text that
          is to be sent to another application. For example, I can send
          text through SMS or IM.  A dialog with those choices would be shown,
@@ -2073,9 +2305,11 @@
     <!-- See USB_STORAGE.  USB_STORAGE_DIALOG:  After the user selects the notification, a dialog is shown asking if he wants to mount.  This is the title. -->
     <string name="usb_storage_title">USB connected</string>
     <!-- See USB_STORAGE.    This is the message. [CHAR LIMIT=NONE] -->
-    <string name="usb_storage_message" product="nosdcard">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s USB storage.</string>
+    <string name="usb_storage_message" product="tablet">You have connected your tablet to your computer via USB. Touch the button below if you want to copy files between your computer and your Android\u2018s USB storage.</string>
+    <!-- See USB_STORAGE.    This is the message. [CHAR LIMIT=NONE] -->
+    <string name="usb_storage_message" product="nosdcard">You have connected your phone to your computer via USB. Touch the button below if you want to copy files between your computer and your Android\u2018s USB storage.</string>
     <!-- See USB_STORAGE.    This is the message. -->
-    <string name="usb_storage_message" product="default">You have connected your phone to your computer via USB. Select the button below if you want to copy files between your computer and your Android\u2018s SD card.</string>
+    <string name="usb_storage_message" product="default">You have connected your phone to your computer via USB. Touch the button below if you want to copy files between your computer and your Android\u2018s SD card.</string>
     <!-- See USB_STORAGE.    This is the button text to mount the phone on the computer. -->
     <string name="usb_storage_button_mount">Turn on USB storage</string>
     <!-- See USB_STORAGE_DIALOG.  If there was an error mounting, this is the text. [CHAR LIMIT=NONE] -->
@@ -2232,6 +2466,9 @@
     <!-- Long label for a button on a full-screen input method for the "Done" action. -->
     <string name="ime_action_done">Done</string>
 
+    <!-- [CHAR LIMIT=6] Long label for a button on a full-screen input method for the "Previous" action. -->
+    <string name="ime_action_previous">Prev</string>
+
     <!-- Long label for a button on a full-screen input method for an unknown action. -->
     <string name="ime_action_default">Execute</string>
 
@@ -2246,10 +2483,9 @@
     <!-- This string appears (on two lines) when you type a number into contacts search, to let you create a contact whose phone number is the number you typed.  The first line will be in bigger type than the second. -->
     <string name="create_contact_using">Create contact\nusing <xliff:g id="number" example="555">%s</xliff:g></string>
 
-    <!-- This string array should be overridden by the manufacture to present a list of carrier-id,locale,wifi-channel sets.  This is used at startup to set system defaults by checking the system property ro.carrier for the carrier-id and searching through this array -->
+    <!-- This string array should be overridden by the manufacture to present a list of carrier-id,locale. The wifi regulatory domain is extracted from the locale information. This is used at startup to set system defaults by checking the system property ro.carrier for the carrier-id and searching through this array -->
     <!-- An Array of [[Carrier-ID]                     -->
-    <!--              [default-locale]                 -->
-    <!--              [default-wifi-allowed-channels]] -->
+    <!--              [default-locale]]                -->
     <string-array translatable="false" name="carrier_properties">
     </string-array>
 
@@ -2289,17 +2525,13 @@
     <!-- Localized strings for WebView -->
     <!-- Label for button in a WebView that will open a chooser to choose a file to upload -->
     <string name="upload_file">Choose file</string>
+    <!-- Label for the file upload control when no file has been chosen yet -->
+    <string name="no_file_chosen">No file chosen</string>
     <!-- Label for <input type="reset"> button in html -->
     <string name="reset">Reset</string>
     <!-- Label for <input type="submit"> button in html -->
     <string name="submit">Submit</string>
 
-    <!-- String describing the Star/Favorite checkbox
-
-         Used by AccessibilityService to announce the purpose of the view.
-    -->
-    <string name="description_star">favorite</string>
-
     <!-- Strings for car mode notification -->
     <!-- Shown when car mode is enabled -->
     <string name="car_mode_disable_notification_title">Car mode enabled</string>
@@ -2310,6 +2542,13 @@
     <string name="tethered_notification_title">Tethering or hotspot active</string>
     <string name="tethered_notification_message">Touch to configure</string>
 
+    <!--  Strings for possible PreferenceActivity Back/Next buttons -->
+    <string name="back_button_label">Back</string>
+    <string name="next_button_label">Next</string>
+
+    <!-- Optional button to Skip a PreferenceActivity [CHAR LIMIT=20] -->
+    <string name="skip_button_label">Skip</string>
+
     <!-- Strings for throttling notification -->
     <!-- Shown when the user is in danger of being throttled -->
     <string name="throttle_warning_notification_title">High mobile data use</string>
@@ -2320,6 +2559,24 @@
     <string name="throttled_notification_title">Mobile data limit exceeded</string>
     <string name="throttled_notification_message">Touch to learn more about mobile data use</string>
 
+    <!-- Displayed on the Find dialog when there are no matches [CHAR LIMIT=NONE]-->
+    <string name="no_matches">No matches</string>
+
+    <!-- Find dialog hint text.  Also used in the menu item to open find on page [CHAR LIMIT=NONE] -->
+    <string name="find_on_page">Find on page</string>
+
+    <!-- Displayed on the Find dialog to display the index of the highlighted
+         match and total number of matches found in the current page. [CHAR LIMIT=NONE] -->
+    <plurals name="matches_found">
+        <!-- Case of one match -->
+        <item quantity="one">1 match</item>
+        <!-- Case of multiple total matches -->
+        <item quantity="other"><xliff:g id="index" example="2">%d</xliff:g> of <xliff:g id="total" example="137">%d</xliff:g></item>
+    </plurals>
+
+    <!-- Label for the "Done" button on the far left of action mode toolbars. -->
+    <string name="action_mode_done">Done</string>
+
     <!-- Strings for ExternalStorageFormatter service. -->
     <!-- Text for progress dialog while unmounting USB storage volume [CHAR LIMIT=NONE] -->
     <string name="progress_unmounting" product="nosdcard">Unmounting USB storage...</string>
@@ -2348,4 +2605,10 @@
     <!-- Text for message for an unknown external media state [CHAR LIMIT=NONE] -->
     <string name="media_unknown_state">External media in unknown state.</string>
 
+    <!-- Text for WebView's text selection Action Mode -->
+    <!-- ActionBar action to share the current selection [CHAR LIMIT=10] -->
+    <string name="share">Share</string>
+    <!-- ActionBar action to use the current selection to open the Find on page functionality [CHAR LIMIT=10]-->
+    <string name="find">Find</string>
+
 </resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 52c046d..adf1715 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -74,6 +74,14 @@
         <item name="wallpaperIntraOpenExitAnimation">@anim/wallpaper_intra_open_exit</item>
         <item name="wallpaperIntraCloseEnterAnimation">@anim/wallpaper_intra_close_enter</item>
         <item name="wallpaperIntraCloseExitAnimation">@anim/wallpaper_intra_close_exit</item>
+        <item name="fragmentOpenEnterAnimation">@anim/fragment_open_enter</item>
+        <item name="fragmentOpenExitAnimation">@anim/fragment_open_exit</item>
+        <item name="fragmentCloseEnterAnimation">@anim/fragment_close_enter</item>
+        <item name="fragmentCloseExitAnimation">@anim/fragment_close_exit</item>
+        <item name="fragmentNextEnterAnimation">@anim/fragment_next_enter</item>
+        <item name="fragmentNextExitAnimation">@anim/fragment_next_exit</item>
+        <item name="fragmentPrevEnterAnimation">@anim/fragment_prev_enter</item>
+        <item name="fragmentPrevExitAnimation">@anim/fragment_prev_exit</item>
     </style>
 
     <!-- Standard animations for a non-full-screen window or activity. -->
@@ -88,6 +96,13 @@
         <item name="windowExitAnimation">@anim/status_bar_exit</item>
     </style>
 
+    <!-- {@hide} -->
+    <style name="Animation.StatusBar.IntruderAlert"
+        parent="@android:style/Animation.StatusBar">
+        <item name="android:windowEnterAnimation">@anim/priority_alert_enter</item>
+        <item name="android:windowExitAnimation">@anim/priority_alert_exit</item>
+    </style>
+
     <!-- Standard animations for a translucent window or activity.  This
          style is <em>not<em> used by default for the translucent theme
          (since translucent activities are a special case that have no
@@ -191,8 +206,7 @@
     <!-- Status Bar Styles -->
 
     <style name="TextAppearance.StatusBar">
-        <item name="android:textSize">14sp</item>
-        <item name="android:textStyle">normal</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
     <style name="TextAppearance.StatusBar.Ticker">
@@ -205,12 +219,11 @@
         <item name="android:textStyle">bold</item>
     </style>
     <style name="TextAppearance.StatusBar.EventContent">
-        <item name="android:textColor">#ff6b6b6b</item>
+        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
     </style>
     <style name="TextAppearance.StatusBar.EventContent.Title">
-        <item name="android:textSize">16sp</item>
+        <item name="android:textSize">18sp</item>
         <item name="android:textStyle">bold</item>
-        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
     </style>
 
     <!-- Widget Styles -->
@@ -221,7 +234,7 @@
 
     <style name="Widget.AbsListView">
         <item name="android:scrollbars">vertical</item>
-        <item name="android:fadingEdge">vertical</item>
+        <item name="android:fadingEdge">none</item>
     </style>
 
     <style name="Widget.GestureOverlayView">
@@ -274,12 +287,12 @@
 
     <style name="Widget.CompoundButton.CheckBox">
         <item name="android:background">@android:drawable/btn_check_label_background</item>
-        <item name="android:button">@android:drawable/btn_check</item>
+        <item name="android:button">?android:attr/listChoiceIndicatorMultiple</item>
     </style>
 
     <style name="Widget.CompoundButton.RadioButton">
         <item name="android:background">@android:drawable/btn_radio_label_background</item>
-        <item name="android:button">@android:drawable/btn_radio</item>
+        <item name="android:button">?android:attr/listChoiceIndicatorSingle</item>
     </style>
 
     <style name="Widget.CompoundButton.Star">
@@ -390,12 +403,14 @@
         <item name="android:textSelectHandleLeft">?android:attr/textSelectHandleLeft</item>
         <item name="android:textSelectHandleRight">?android:attr/textSelectHandleRight</item>
         <item name="android:textSelectHandle">?android:attr/textSelectHandle</item>
+        <item name="android:textEditPasteWindowLayout">?android:attr/textEditPasteWindowLayout</item>
+        <item name="android:textEditNoPasteWindowLayout">?android:attr/textEditNoPasteWindowLayout</item>
     </style>
     
     <style name="Widget.TextView.ListSeparator">
         <item name="android:background">@android:drawable/dark_header_dither</item>
         <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">25dip</item>
+        <item name="android:layout_height">wrap_content</item>
         <item name="android:textStyle">bold</item>
         <item name="android:textColor">?textColorSecondary</item>
         <item name="android:textSize">14sp</item>
@@ -412,9 +427,9 @@
         <item name="android:focusable">true</item>
         <item name="android:focusableInTouchMode">true</item>
         <item name="android:clickable">true</item>
-        <item name="android:background">@android:drawable/edit_text</item>
+        <item name="android:background">?android:attr/editTextBackground</item>
         <item name="android:textAppearance">?android:attr/textAppearanceMediumInverse</item>
-        <item name="android:textColor">@android:color/primary_text_light</item>
+        <item name="android:textColor">?android:attr/editTextColor</item>
         <item name="android:gravity">center_vertical</item>
     </style>
     
@@ -440,14 +455,8 @@
         <item name="android:background">@android:drawable/btn_default</item>
     </style>
 
-    <style name="Widget.AutoCompleteTextView">
-        <item name="android:focusable">true</item>
-        <item name="android:focusableInTouchMode">true</item>
-        <item name="android:clickable">true</item>
-        <item name="android:background">@android:drawable/edit_text</item>
+    <style name="Widget.AutoCompleteTextView" parent="Widget.EditText">
         <item name="android:completionHintView">@android:layout/simple_dropdown_hint</item>
-        <item name="android:textAppearance">?android:attr/textAppearanceMediumInverse</item>
-        <item name="android:gravity">center_vertical</item>
         <item name="android:completionThreshold">2</item>
         <item name="android:dropDownSelector">@android:drawable/list_selector_background</item>
         <item name="android:popupBackground">@android:drawable/spinner_dropdown_background</item>
@@ -459,6 +468,18 @@
     <style name="Widget.Spinner">
         <item name="android:background">@android:drawable/btn_dropdown</item>
         <item name="android:clickable">true</item>
+        <item name="android:spinnerMode">dialog</item>
+
+        <item name="android:dropDownSelector">@android:drawable/list_selector_background</item>
+        <item name="android:popupBackground">@android:drawable/spinner_dropdown_background</item>
+        <item name="android:dropDownVerticalOffset">-10dip</item>
+        <item name="android:dropDownHorizontalOffset">0dip</item>
+        <item name="android:dropDownWidth">wrap_content</item>
+        <item name="android:popupPromptView">@android:layout/simple_dropdown_hint</item>
+    </style>
+
+    <style name="Widget.Spinner.DropDown">
+        <item name="android:spinnerMode">dropdown</item>
     </style>
 
     <style name="Widget.TextView.PopupMenu">
@@ -478,17 +499,17 @@
     </style>
     
     <style name="Widget.DropDownItem.Spinner">
-        <item name="android:checkMark">@android:drawable/btn_radio</item>
+        <item name="android:checkMark">?android:attr/listChoiceIndicatorSingle</item>
     </style>
 
     <style name="Widget.ScrollView">
         <item name="android:scrollbars">vertical</item>
-        <item name="android:fadingEdge">vertical</item>
+        <item name="android:fadingEdge">none</item>
     </style>
 
     <style name="Widget.HorizontalScrollView">
         <item name="android:scrollbars">horizontal</item>
-        <item name="android:fadingEdge">horizontal</item>
+        <item name="android:fadingEdge">none</item>
     </style>
 
     <style name="Widget.ListView" parent="Widget.AbsListView">
@@ -498,7 +519,7 @@
     </style>
     
     <style name="Widget.ListView.White" parent="Widget.AbsListView">
-        <item name="android:listSelector">@android:drawable/list_selector_background</item>
+        <item name="android:listSelector">@android:drawable/list_selector_background_light</item>
         <item name="android:cacheColorHint">?android:attr/colorBackgroundCacheHint</item>
         <item name="android:divider">@android:drawable/divider_horizontal_bright_opaque</item>
     </style>    
@@ -511,7 +532,7 @@
     <style name="Widget.ListView.Menu">
 		<item name="android:cacheColorHint">@null</item>
         <item name="android:scrollbars">vertical</item>
-        <item name="android:fadingEdge">vertical</item>
+        <item name="android:fadingEdge">none</item>
         <item name="listSelector">@android:drawable/menu_selector</item>
         <!-- Light background for the list in menus, so the divider for bright themes -->
         <item name="android:divider">@android:drawable/divider_horizontal_dark</item>
@@ -541,10 +562,16 @@
         <item name="android:textAppearance">@style/TextAppearance.Widget.TabWidget</item>
         <item name="ellipsize">marquee</item>
         <item name="singleLine">true</item>
+        <item name="android:tabStripLeft">@android:drawable/tab_bottom_left</item>
+        <item name="android:tabStripRight">@android:drawable/tab_bottom_right</item>
+        <item name="android:tabStripEnabled">true</item>
+        <item name="android:divider">@null</item>
+        <item name="android:gravity">fill_horizontal|center_vertical</item>
+        <item name="android:tabLayout">@android:layout/tab_indicator</item>
     </style>
 
     <style name="Widget.Gallery">
-        <item name="android:fadingEdge">horizontal</item>
+        <item name="android:fadingEdge">none</item>
         <item name="android:gravity">center_vertical</item>
         <item name="android:spacing">-20dip</item>
         <item name="android:unselectedAlpha">0.85</item>
@@ -555,6 +582,12 @@
         <item name="android:popupAnimationStyle">@android:style/Animation.PopupWindow</item>
     </style>
 
+    <!-- Default style for {@link android.app.FragmentBreadCrumbs} view. -->
+    <style name="Widget.FragmentBreadCrumbs">
+        <item name="android:padding">4dp</item>
+        <item name="android:animateLayoutChanges">true</item>
+    </style>
+
     <style name="Widget.KeyboardView" parent="android:Widget">
         <item name="android:background">@android:drawable/keyboard_background</item>
         <item name="android:keyBackground">@android:drawable/btn_keyboard_key</item>
@@ -576,6 +609,7 @@
         <item name="android:background">@android:drawable/quickcontact_badge</item>
         <item name="android:clickable">true</item>
         <item name="android:scaleType">fitCenter</item>
+        <item name="android:src">@android:drawable/ic_contact_picture</item>
     </style>
     
     <style name="Widget.QuickContactBadgeSmall">
@@ -615,9 +649,9 @@
 
     <style name="TextAppearance">
         <item name="android:textColor">?textColorPrimary</item>
-        <item name="android:textColorHighlight">#FFFF9200</item>
+        <item name="android:textColorHighlight">?textColorHighlight</item>
         <item name="android:textColorHint">?textColorHint</item>
-        <item name="android:textColorLink">#5C5CFF</item>
+        <item name="android:textColorLink">?textColorLink</item>
         <item name="android:textSize">16sp</item>
         <item name="android:textStyle">normal</item>
     </style>
@@ -625,7 +659,8 @@
     <style name="TextAppearance.Inverse">
         <item name="textColor">?textColorPrimaryInverse</item>
         <item name="android:textColorHint">?textColorHintInverse</item>
-        <item name="android:textColorLink">#0000EE</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
     </style>
 
     <style name="TextAppearance.Theme">
@@ -633,41 +668,40 @@
 
     <style name="TextAppearance.DialogWindowTitle">
         <item name="android:textSize">18sp</item>
-        <item name="android:textStyle">normal</item>
-        <item name="android:textColor">?textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.Large">
         <item name="android:textSize">22sp</item>
-        <item name="android:textStyle">normal</item>
-        <item name="android:textColor">?textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.Large.Inverse">
         <item name="android:textColor">?textColorPrimaryInverse</item>
         <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
     </style>
 
     <style name="TextAppearance.Medium">
         <item name="android:textSize">18sp</item>
-        <item name="android:textStyle">normal</item>
-        <item name="android:textColor">?textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.Medium.Inverse">
         <item name="android:textColor">?textColorPrimaryInverse</item>
         <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
     </style>
 
     <style name="TextAppearance.Small">
         <item name="android:textSize">14sp</item>
-        <item name="android:textStyle">normal</item>
         <item name="android:textColor">?textColorSecondary</item>
     </style>
 
     <style name="TextAppearance.Small.Inverse">
         <item name="android:textColor">?textColorSecondaryInverse</item>
         <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
     </style>
 
     <style name="TextAppearance.Theme.Dialog" parent="TextAppearance.Theme">
@@ -743,22 +777,22 @@
     
 
     <!-- @hide -->	
-     <style name="TextAppearance.SearchResult">	
-         <item name="android:textStyle">normal</item>	
-         <item name="android:textColor">?textColorPrimaryInverse</item>	
-         <item name="android:textColorHint">?textColorHintInverse</item>	
+     <style name="TextAppearance.SearchResult">
+         <item name="android:textStyle">normal</item>
+         <item name="android:textColor">?textColorPrimaryInverse</item>
+         <item name="android:textColorHint">?textColorHintInverse</item>
      </style>	
      	
      <!-- @hide -->	
-     <style name="TextAppearance.SearchResult.Title">	
-         <item name="android:textSize">16sp</item>	
-     </style>	
+     <style name="TextAppearance.SearchResult.Title">
+         <item name="android:textSize">16sp</item>
+     </style>
      	
      <!-- @hide -->	
-     <style name="TextAppearance.SearchResult.Subtitle">	
-         <item name="android:textSize">13sp</item>	
-         <item name="android:textColor">?textColorSecondaryInverse</item>	
-     </style>	
+     <style name="TextAppearance.SearchResult.Subtitle">
+         <item name="android:textSize">13sp</item>
+         <item name="android:textColor">?textColorSecondaryInverse</item>
+     </style>
 
     <style name="TextAppearance.WindowTitle">
         <item name="android:textColor">#fff</item>
@@ -874,4 +908,951 @@
         <item name="windowEnterAnimation">@android:anim/fade_in</item>
         <item name="windowExitAnimation">@android:anim/fade_out</item>
     </style>
+
+    <style name="Widget.ActionBar">
+        <item name="android:background">@android:drawable/action_bar_background</item>
+        <item name="android:displayOptions">useLogo|showHome|showTitle</item>
+        <item name="android:divider">@android:drawable/action_bar_divider</item>
+        <item name="android:height">?android:attr/actionBarSize</item>
+        <item name="android:paddingLeft">0dip</item>
+        <item name="android:paddingTop">0dip</item>
+        <item name="android:paddingRight">0dip</item>
+        <item name="android:paddingBottom">0dip</item>
+        <item name="android:titleTextStyle">@android:style/TextAppearance.Widget.ActionBar.Title</item>
+        <item name="android:subtitleTextStyle">@android:style/TextAppearance.Widget.ActionBar.Subtitle</item>
+    </style>
+
+    <style name="Widget.ActionMode">
+        <item name="android:background">?android:attr/actionModeBackground</item>
+        <item name="android:height">?android:attr/actionBarSize</item>
+        <item name="android:titleTextStyle">@android:style/TextAppearance.Widget.ActionMode.Title</item>
+        <item name="android:subtitleTextStyle">@android:style/TextAppearance.Widget.ActionMode.Subtitle</item>
+    </style>
+
+    <style name="TextAppearance.Widget.ActionBar.Title"
+           parent="@android:style/TextAppearance.Medium.Inverse">
+    </style>
+
+    <style name="TextAppearance.Widget.ActionBar.Subtitle"
+           parent="@android:style/TextAppearance.Small.Inverse">
+    </style>
+
+    <style name="TextAppearance.Widget.ActionMode.Title"
+           parent="@android:style/TextAppearance.Medium.Inverse">
+    </style>
+
+    <style name="TextAppearance.Widget.ActionMode.Subtitle"
+           parent="@android:style/TextAppearance.Small.Inverse">
+    </style>
+
+    <style name="Widget.ActionButton">
+        <item name="android:background">@null</item>
+        <item name="android:paddingLeft">16dip</item>
+        <item name="android:paddingRight">16dip</item>
+    </style>
+
+    <style name="Widget.ActionButton.Overflow">
+        <item name="android:src">@drawable/ic_menu_more</item>
+        <item name="android:contentDescription">@string/more_item_label</item>
+    </style>
+
+    <style name="Widget.ActionButton.CloseMode">
+        <item name="android:src">?android:attr/actionModeCloseDrawable</item>
+    </style>
+
+    <style name="Widget.ActionBarView_TabView">
+        <item name="android:background">@drawable/minitab_lt</item>
+        <item name="android:paddingLeft">4dip</item>
+        <item name="android:paddingRight">4dip</item>
+    </style>
+
+    <style name="Widget.ActionBarView_TabBar">
+    </style>
+
+    <style name="Widget.ActionBarView_TabText">
+        <item name="android:textAppearance">@style/TextAppearance.Widget.TextView.PopupMenu</item>
+        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
+        <item name="android:textSize">18sp</item>
+    </style>
+
+    <style name="Widget.ListPopupWindow">
+        <item name="android:dropDownSelector">@android:drawable/list_selector_background</item>
+        <item name="android:popupBackground">@android:drawable/spinner_dropdown_background</item>
+        <item name="android:dropDownVerticalOffset">-10dip</item>
+        <item name="android:dropDownHorizontalOffset">0dip</item>
+        <item name="android:dropDownWidth">wrap_content</item>        
+    </style>
+
+    <style name="Widget.PopupMenu" parent="Widget.ListPopupWindow">
+    </style>
+
+    <style name="TextAppearance.Widget.PopupMenu">
+        <item name="android:textColor">@android:color/primary_text_light</item>
+        <item name="android:textColorHint">@android:color/hint_foreground_light</item>
+        <item name="android:textColorHighlight">@android:color/highlighted_text_light</item>
+        <item name="android:textColorLink">@android:color/link_text_light</item>
+    </style>
+
+    <style name="TextAppearance.Widget.PopupMenu.Large">
+        <item name="android:textSize">22sp</item>
+    </style>
+
+    <style name="TextAppearance.Widget.PopupMenu.Small">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">@android:color/secondary_text_light</item>
+    </style>
+
+    <style name="Widget.ButtonGroup">
+        <item name="buttonBackground">?android:attr/groupButtonBackground</item>
+        <item name="showDividers"></item>
+    </style>
+
+    <!-- Begin Holo theme styles -->
+
+    <!-- Text Styles -->
+    <style name="TextAppearance.Holo" parent="TextAppearance">
+    </style>
+
+    <style name="TextAppearance.Holo.Inverse" parent="TextAppearance.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Large" parent="TextAppearance.Large">
+    </style>
+
+    <style name="TextAppearance.Holo.Medium" parent="TextAppearance.Medium">
+        <item name="android:textLineHeight">24dip</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Small" parent="TextAppearance.Small">
+    </style>
+
+    <style name="TextAppearance.Holo.Large.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Medium.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Small.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.SearchResult">
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.SearchResult.Title">
+         <item name="android:textSize">16sp</item>
+    </style>
+
+    <style name="TextAppearance.Holo.SearchResult.Subtitle">
+        <item name="android:textSize">13sp</item>
+        <item name="android:textColor">?textColorSecondaryInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget" parent="TextAppearance.Widget">
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.Button" parent="TextAppearance.Holo.Small.Inverse">
+        <item name="android:textColor">@android:color/primary_text_light_nodisable</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.IconMenu.Item" parent="TextAppearance.Holo.Small">
+        <item name="android:textColor">?textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.TabWidget">
+        <item name="android:textSize">18sp</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">@android:color/tab_indicator_text</item>
+    </style>
+    
+    <style name="TextAppearance.Holo.Widget.TextView">
+        <item name="android:textColor">?textColorPrimaryDisableOnly</item>
+        <item name="android:textColorHint">?textColorHint</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.TextView.PopupMenu">
+        <item name="android:textSize">18sp</item>
+        <item name="android:textColor">?textColorPrimaryDisableOnly</item>
+        <item name="android:textColorHint">?textColorHint</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.DropDownHint">
+        <item name="android:textColor">?textColorPrimary</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.DropDownItem">
+        <item name="android:textColor">?textColorPrimaryDisableOnly</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.TextView.SpinnerItem">
+        <item name="android:textColor">?textColorPrimaryDisableOnly</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.EditText">
+        <item name="android:textColor">@color/widget_edittext_holo_dark</item>
+        <item name="android:textColorHint">@android:color/hint_foreground_holo_light</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.PopupMenu" parent="TextAppearance.Widget.PopupMenu">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.PopupMenu.Large">
+        <item name="android:textSize">22sp</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.PopupMenu.Small">
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.ActionBar.Title"
+           parent="TextAppearance.Holo.Medium">
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.ActionBar.Subtitle"
+           parent="TextAppearance.Holo.Small">
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.ActionMode">
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.ActionMode.Title" parent="TextAppearance.Widget.ActionMode.Title">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Widget.ActionMode.Subtitle" parent="TextAppearance.Widget.ActionMode.Subtitle">
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+    </style>
+
+    <style name="TextAppearance.Holo.WindowTitle">
+        <item name="android:textColor">#fff</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+
+    <style name="TextAppearance.Holo.DialogWindowTitle">
+        <item name="android:textSize">18sp</item>
+    </style>
+
+    <!-- Light text styles -->
+    <style name="TextAppearance.Holo.Light" parent="TextAppearance.Holo">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Large" parent="TextAppearance.Holo.Large">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Medium" parent="TextAppearance.Holo.Medium">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Small" parent="TextAppearance.Holo.Small">
+    </style>
+ 
+   <style name="TextAppearance.Holo.Light.Large.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Medium.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Small.Inverse">
+        <item name="android:textColor">?textColorPrimaryInverse</item>
+        <item name="android:textColorHint">?textColorHintInverse</item>
+        <item name="android:textColorHighlight">?textColorHighlightInverse</item>
+        <item name="android:textColorLink">?textColorLinkInverse</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.SearchResult" parent="TextAppearance.Holo.SearchResult">
+         <item name="android:textColor">?textColorPrimary</item>
+         <item name="android:textColorHint">?textColorHint</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.SearchResult.Title">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.SearchResult.Subtitle">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget" parent="TextAppearance.Widget">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.Button">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.EditText">
+        <item name="android:textColor">@color/widget_edittext_holo_light</item>
+        <item name="android:textColorHint">@android:color/hint_foreground_holo_dark</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.PopupMenu" parent="TextAppearance.Holo.Widget.PopupMenu">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.PopupMenu.Large" parent="TextAppearance.Holo.Widget.PopupMenu.Large">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.PopupMenu.Small" parent="TextAppearance.Holo.Widget.PopupMenu.Small">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.DropDownHint" parent="TextAppearance.Holo.Widget.DropDownHint">
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.ActionMode.Title" parent="TextAppearance.Widget.ActionMode.Title">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.Widget.ActionMode.Subtitle" parent="TextAppearance.Widget.ActionMode.Subtitle">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.WindowTitle">
+        <item name="android:textColor">#fff</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DialogWindowTitle">
+        <item name="android:textSize">18sp</item>
+    </style>
+
+    <!-- Widget Styles -->
+
+    <style name="Widget.Holo" parent="Widget">
+    </style>
+
+    <style name="Widget.Holo.Button" parent="Widget.Button">
+        <item name="android:background">@android:drawable/btn_default_holo_dark</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
+        <item name="android:textColor">@android:color/primary_text_holo_dark</item>
+        <item name="android:minHeight">48dip</item>
+        <item name="android:paddingLeft">32dip</item>
+        <item name="android:paddingRight">32dip</item>
+        <item name="android:paddingTop">4dip</item>
+        <item name="android:paddingBottom">4dip</item>
+    </style>
+
+    <style name="Widget.Holo.Button.Small">
+        <item name="android:background">@android:drawable/btn_default_holo_dark</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+        <item name="android:textColor">@android:color/primary_text_holo_dark</item>
+        <item name="android:minHeight">48dip</item>
+        <item name="android:paddingLeft">24dip</item>
+        <item name="android:paddingRight">24dip</item>
+        <item name="android:paddingTop">4dip</item>
+        <item name="android:paddingBottom">4dip</item>
+    </style>
+
+    <style name="Widget.Holo.Button.Inset">
+        <item name="android:background">@android:drawable/button_inset</item>
+    </style>
+
+    <style name="Widget.Holo.Button.Toggle">
+        <item name="android:background">@android:drawable/btn_toggle_holo_dark</item>
+        <item name="android:textOn">@android:string/capital_on</item>
+        <item name="android:textOff">@android:string/capital_off</item>
+        <item name="android:disabledAlpha">?android:attr/disabledAlpha</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+        <item name="android:minHeight">48dip</item>
+        <item name="android:paddingLeft">24dip</item>
+        <item name="android:paddingRight">24dip</item>
+    </style>
+
+    <style name="Widget.Holo.ButtonGroup" parent="Widget.ButtonGroup">
+        <item name="divider">?android:attr/dividerVertical</item>
+        <item name="showDividers">middle</item>
+        <item name="android:background">@android:drawable/btn_default_holo_dark</item>
+    </style>
+
+    <style name="Widget.Holo.ButtonGroup.AlertDialog">
+        <item name="android:background">@android:drawable/dialog_divider_horizontal_holo_dark</item>
+    </style>
+
+    <style name="Widget.Holo.TextView" parent="Widget.TextView">
+    </style>
+
+    <style name="Widget.Holo.TextView.ListSeparator" parent="Widget.TextView.ListSeparator">
+        <item name="android:background">@android:drawable/list_section_divider_holo_dark</item>
+    </style>
+
+    <style name="Widget.Holo.TextSelectHandle" parent="Widget.TextSelectHandle">
+    </style>
+
+    <style name="Widget.Holo.AbsListView" parent="Widget.AbsListView">
+    </style>
+
+    <style name="Widget.Holo.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
+    </style>
+
+    <style name="Widget.Holo.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">
+    </style>
+
+    <style name="Widget.Holo.ListView.DropDown">
+    </style>
+
+    <style name="Widget.Holo.EditText" parent="Widget.EditText">
+    </style>
+
+    <style name="Widget.Holo.ExpandableListView" parent="Widget.ExpandableListView">
+    </style>
+
+    <style name="Widget.Holo.ExpandableListView.White">
+    </style>
+
+    <style name="Widget.Holo.Gallery" parent="Widget.Gallery">
+    </style>
+
+    <style name="Widget.Holo.GestureOverlayView" parent="Widget.GestureOverlayView">
+    </style>
+
+    <style name="Widget.Holo.GridView" parent="Widget.GridView">
+    </style>
+
+    <style name="Widget.Holo.ImageButton" parent="Widget.ImageButton">
+    </style>
+
+    <style name="Widget.Holo.ImageWell" parent="Widget.ImageWell">
+    </style>
+
+    <style name="Widget.Holo.ListView" parent="Widget.ListView">
+        <item name="android:divider">?android:attr/listDivider</item>
+        <item name="android:listSelector">?android:attr/listChoiceBackgroundIndicator</item>
+    </style>
+
+    <style name="Widget.Holo.ListView.White">
+    </style>
+
+    <style name="Widget.Holo.PopupWindow" parent="Widget.PopupWindow">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar" parent="Widget.ProgressBar">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Horizontal" parent="Widget.ProgressBar.Horizontal">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Small" parent="Widget.ProgressBar.Horizontal">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Small.Title" parent="Widget.ProgressBar.Small.Title">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Large" parent="Widget.ProgressBar.Large">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Inverse" parent="Widget.ProgressBar.Inverse">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Small.Inverse" parent="Widget.ProgressBar.Small.Inverse">
+    </style>
+
+    <style name="Widget.Holo.ProgressBar.Large.Inverse" parent="Widget.ProgressBar.Large.Inverse">
+    </style>
+
+    <style name="Widget.Holo.SeekBar">
+        <item name="android:indeterminateOnly">false</item>
+        <item name="android:progressDrawable">@android:drawable/scrubber_progress_horizontal_holo_dark</item>
+        <item name="android:indeterminateDrawable">@android:drawable/scrubber_progress_horizontal_holo_dark</item>
+        <item name="android:minHeight">16dip</item>
+        <item name="android:maxHeight">16dip</item>
+        <item name="android:thumb">@android:drawable/scrubber_control_holo</item>
+        <item name="android:thumbOffset">16dip</item>
+        <item name="android:focusable">true</item>
+    </style>
+
+    <style name="Widget.Holo.RatingBar" parent="Widget.RatingBar">
+    </style>
+
+    <style name="Widget.Holo.RatingBar.Indicator" parent="Widget.RatingBar.Indicator">
+    </style>
+
+    <style name="Widget.Holo.RatingBar.Small" parent="Widget.RatingBar.Small">
+    </style>
+
+    <style name="Widget.Holo.CompoundButton.RadioButton" parent="Widget.CompoundButton.RadioButton">
+    </style>
+
+    <style name="Widget.Holo.ScrollView" parent="Widget.ScrollView">
+    </style>
+
+    <style name="Widget.Holo.HorizontalScrollView" parent="Widget.HorizontalScrollView">
+    </style>
+
+    <style name="Widget.Holo.Spinner" parent="Widget.Spinner.DropDown">
+        <item name="android:background">@android:drawable/spinner_background_holo_dark</item>
+        <item name="android:dropDownSelector">@android:drawable/list_selector_holo_dark</item>
+        <item name="android:popupBackground">@android:drawable/menu_dropdown_panel_holo_dark</item>
+        <item name="android:dropDownVerticalOffset">0dip</item>
+        <item name="android:dropDownHorizontalOffset">0dip</item>
+        <item name="android:dropDownWidth">wrap_content</item>
+        <item name="android:popupPromptView">@android:layout/simple_dropdown_hint</item>
+    </style>
+
+    <style name="Widget.Holo.Spinner.DropDown">
+    </style>
+
+    <style name="Widget.Holo.Spinner.DropDown.ActionBar">
+        <item name="android:background">@android:drawable/spinner_cab_background_holo_dark</item>
+    </style>
+
+    <style name="Widget.Holo.CompoundButton.Star" parent="Widget.CompoundButton.Star">
+    </style>
+
+    <style name="Widget.Holo.TabWidget" parent="Widget.TabWidget">
+        <item name="android:textAppearance">@style/TextAppearance.Holo.Widget.TabWidget</item>
+        <item name="android:tabStripLeft">@null</item>
+        <item name="android:tabStripRight">@null</item>
+        <item name="android:tabStripEnabled">false</item>
+        <item name="android:divider">@null</item>
+        <item name="android:gravity">left|center_vertical</item>
+        <item name="android:tabLayout">@android:layout/tab_indicator_holo</item>
+    </style>
+
+    <style name="Widget.Holo.WebTextView" parent="Widget.WebTextView">
+    </style>
+
+    <style name="Widget.Holo.WebView" parent="Widget.WebView">
+    </style>
+
+    <style name="Widget.Holo.DropDownItem" parent="Widget.DropDownItem">
+        <item name="android:textAppearance">@style/TextAppearance.Holo.Widget.DropDownItem</item>
+    </style>
+
+    <style name="Widget.Holo.DropDownItem.Spinner">
+    </style>
+
+    <style name="Widget.Holo.TextView.SpinnerItem" parent="Widget.TextView.SpinnerItem">
+        <item name="android:textAppearance">@style/TextAppearance.Holo.Widget.TextView.SpinnerItem</item>
+    </style>
+
+    <style name="Widget.Holo.KeyboardView" parent="Widget.KeyboardView">
+    </style>
+
+    <style name="Widget.Holo.QuickContactBadge.WindowSmall" parent="Widget.QuickContactBadge.WindowSmall">
+    </style>
+
+    <style name="Widget.Holo.QuickContactBadge.WindowMedium" parent="Widget.QuickContactBadge.WindowMedium">
+    </style>
+
+    <style name="Widget.Holo.QuickContactBadge.WindowLarge" parent="Widget.QuickContactBadge.WindowLarge">
+    </style>
+
+    <style name="Widget.Holo.QuickContactBadgeSmall.WindowSmall" parent="Widget.QuickContactBadgeSmall.WindowSmall">
+    </style>
+
+    <style name="Widget.Holo.QuickContactBadgeSmall.WindowMedium" parent="Widget.QuickContactBadgeSmall.WindowMedium">
+    </style>
+
+    <style name="Widget.Holo.QuickContactBadgeSmall.WindowLarge" parent="Widget.QuickContactBadgeSmall.WindowLarge">
+    </style>
+
+    <style name="Widget.Holo.ListPopupWindow" parent="Widget.ListPopupWindow">
+        <item name="android:dropDownSelector">@android:drawable/list_selector_holo_dark</item>
+        <item name="android:popupBackground">@android:drawable/menu_dropdown_panel_holo_dark</item>
+        <item name="android:dropDownVerticalOffset">0dip</item>
+        <item name="android:dropDownHorizontalOffset">0dip</item>
+        <item name="android:dropDownWidth">wrap_content</item>
+    </style>
+
+    <style name="Widget.Holo.PopupMenu" parent="Widget.Holo.ListPopupWindow">
+    </style>
+
+    <style name="Widget.Holo.ButtonBar">
+        <item name="android:divider">?android:attr/dividerVertical</item>
+    </style>
+
+    <style name="Widget.Holo.ButtonBar.Button">
+    </style>
+
+    <style name="Widget.Holo.ActionButton" parent="Widget.ActionButton">
+        <item name="android:paddingLeft">4dip</item>
+        <item name="android:paddingRight">4dip</item>
+        <item name="android:minHeight">56dip</item>
+        <item name="android:scaleType">center</item>
+    </style>
+
+    <style name="Widget.Holo.ActionButton.Overflow">
+        <item name="android:src">@android:drawable/ic_menu_moreoverflow_holo_dark</item>
+        <item name="android:background">?android:attr/selectableItemBackground</item>
+    </style>
+
+    <style name="Widget.Holo.ActionButton.TextButton" parent="Widget.Holo.ButtonBar.Button">
+    </style>
+
+    <style name="Widget.Holo.ActionBarView_TabView" parent="Widget.ActionBarView_TabView">
+    </style>
+
+    <style name="Widget.Holo.ActionBarView_TabBar" parent="Widget.ActionBarView_TabBar">
+    </style>
+
+    <style name="Widget.Holo.ActionBarView_TabText" parent="Widget.ActionBarView_TabText">
+        <item name="android:textAppearance">@style/TextAppearance.Holo.Medium</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">18sp</item>
+    </style>
+
+    <style name="Widget.Holo.ActionMode" parent="Widget.ActionMode">
+        <item name="android:background">@android:drawable/cab_holo_dark</item>
+        <item name="android:titleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionMode.Title</item>
+        <item name="android:subtitleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionMode.Subtitle</item>
+    </style>
+
+    <style name="Widget.Holo.ActionButton.CloseMode">
+        <item name="android:src">@drawable/cab_ic_close_holo</item>
+    </style>
+
+    <style name="Widget.Holo.ActionBar" parent="Widget.ActionBar">
+        <item name="android:titleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionBar.Title</item>
+        <item name="android:subtitleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionBar.Subtitle</item>
+        <item name="android:background">@null</item>
+        <item name="android:divider">?android:attr/dividerVertical</item>
+        <item name="android:homeAsUpIndicator">@android:drawable/ic_ab_back_holo_dark</item>
+    </style>
+
+    <!-- Light widget styles -->
+
+    <style name="Widget.Holo.Light">
+    </style>
+
+    <style name="Widget.Holo.Light.Button" parent="Widget.Button">
+        <item name="android:background">@android:drawable/btn_default_holo_light</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
+        <item name="android:textColor">@android:color/primary_text_holo_light</item>
+        <item name="android:minHeight">48dip</item>
+        <item name="android:paddingLeft">32dip</item>
+        <item name="android:paddingRight">32dip</item>
+        <item name="android:paddingTop">4dip</item>
+        <item name="android:paddingBottom">4dip</item>
+    </style>
+
+    <style name="Widget.Holo.Light.Button.Small">
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+        <item name="android:textColor">@android:color/primary_text_holo_light</item>
+        <item name="android:minHeight">48dip</item>
+        <item name="android:paddingLeft">24dip</item>
+        <item name="android:paddingRight">24dip</item>
+        <item name="android:paddingTop">4dip</item>
+        <item name="android:paddingBottom">4dip</item>
+    </style>
+
+    <style name="Widget.Holo.Light.Button.Inset">
+    </style>
+
+    <style name="Widget.Holo.Light.Button.Toggle">
+        <item name="android:background">@android:drawable/btn_toggle_holo_light</item>
+        <item name="android:textOn">@android:string/capital_on</item>
+        <item name="android:textOff">@android:string/capital_off</item>
+        <item name="android:disabledAlpha">?android:attr/disabledAlpha</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+        <item name="android:minHeight">48dip</item>
+        <item name="android:paddingLeft">24dip</item>
+        <item name="android:paddingRight">24dip</item>
+    </style>
+
+    <style name="Widget.Holo.Light.ButtonGroup" parent="Widget.Holo.ButtonGroup">
+        <item name="android:background">@android:drawable/btn_default_holo_light</item>
+    </style>
+
+    <style name="Widget.Holo.Light.ButtonGroup.AlertDialog">
+        <item name="android:background">@android:drawable/dialog_divider_horizontal_holo_light</item>
+    </style>
+
+    <style name="Widget.Holo.Light.TextView" parent="Widget.TextView">
+    </style>
+
+    <style name="Widget.Holo.Light.TextView.ListSeparator" parent="Widget.TextView.ListSeparator">
+        <item name="android:background">@android:drawable/list_section_divider_holo_light</item>
+    </style>
+
+    <style name="Widget.Holo.Light.TextSelectHandle" parent="Widget.TextSelectHandle">
+    </style>
+
+    <style name="Widget.Holo.Light.AbsListView" parent="Widget.AbsListView">
+    </style>
+
+    <style name="Widget.Holo.Light.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
+    </style>
+
+    <style name="Widget.Holo.Light.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">
+    </style>
+
+    <style name="Widget.Holo.Light.ListView.DropDown">
+    </style>
+
+    <style name="Widget.Holo.Light.EditText" parent="Widget.EditText">
+    </style>
+
+    <style name="Widget.Holo.Light.ExpandableListView" parent="Widget.ExpandableListView">
+    </style>
+
+    <style name="Widget.Holo.Light.ExpandableListView.White">
+    </style>
+
+    <style name="Widget.Holo.Light.Gallery" parent="Widget.Gallery">
+    </style>
+
+    <style name="Widget.Holo.Light.GestureOverlayView" parent="Widget.GestureOverlayView">
+    </style>
+
+    <style name="Widget.Holo.Light.GridView" parent="Widget.GridView">
+    </style>
+
+    <style name="Widget.Holo.Light.ImageButton" parent="Widget.ImageButton">
+    </style>
+
+    <style name="Widget.Holo.Light.ImageWell" parent="Widget.ImageWell">
+    </style>
+
+    <style name="Widget.Holo.Light.ListView" parent="Widget.Holo.ListView">
+    </style>
+
+    <style name="Widget.Holo.Light.ListView.White">
+    </style>
+
+    <style name="Widget.Holo.Light.PopupWindow" parent="Widget.PopupWindow">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar" parent="Widget.ProgressBar">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Horizontal" parent="Widget.ProgressBar.Horizontal">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Small" parent="Widget.ProgressBar.Small">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Small.Title" parent="Widget.ProgressBar.Small.Title">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Large" parent="Widget.ProgressBar.Large">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Inverse" parent="Widget.ProgressBar.Inverse">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Small.Inverse" parent="Widget.ProgressBar.Small.Inverse">
+    </style>
+
+    <style name="Widget.Holo.Light.ProgressBar.Large.Inverse" parent="Widget.ProgressBar.Large.Inverse">
+    </style>
+
+    <style name="Widget.Holo.Light.SeekBar" parent="Widget.Holo.SeekBar">
+        <item name="android:progressDrawable">@android:drawable/scrubber_progress_horizontal_holo_light</item>
+        <item name="android:indeterminateDrawable">@android:drawable/scrubber_progress_horizontal_holo_light</item>
+    </style>
+
+    <style name="Widget.Holo.Light.RatingBar" parent="Widget.RatingBar">
+    </style>
+
+    <style name="Widget.Holo.Light.RatingBar.Indicator" parent="Widget.RatingBar.Indicator">
+    </style>
+
+    <style name="Widget.Holo.Light.RatingBar.Small" parent="Widget.RatingBar.Small">
+    </style>
+
+    <style name="Widget.Holo.Light.CompoundButton.RadioButton" parent="Widget.CompoundButton.RadioButton">
+    </style>
+
+    <style name="Widget.Holo.Light.ScrollView" parent="Widget.ScrollView">
+    </style>
+
+    <style name="Widget.Holo.Light.HorizontalScrollView" parent="Widget.HorizontalScrollView">
+    </style>
+
+    <style name="Widget.Holo.Light.Spinner" parent="Widget.Holo.Spinner">
+        <item name="android:background">@android:drawable/spinner_background_holo_light</item>
+        <item name="android:dropDownSelector">@android:drawable/list_selector_holo_light</item>
+        <item name="android:popupBackground">@android:drawable/menu_dropdown_panel_holo_light</item>
+        <item name="android:dropDownVerticalOffset">0dip</item>
+        <item name="android:dropDownHorizontalOffset">0dip</item>
+        <item name="android:dropDownWidth">wrap_content</item>
+        <item name="android:popupPromptView">@android:layout/simple_dropdown_hint</item>
+    </style>
+
+    <style name="Widget.Holo.Light.Spinner.DropDown">
+    </style>
+
+    <style name="Widget.Holo.Light.Spinner.DropDown.ActionBar">
+        <item name="android:background">@android:drawable/spinner_cab_background_holo_light</item>
+    </style>
+
+    <style name="Widget.Holo.Light.CompoundButton.Star" parent="Widget.CompoundButton.Star">
+    </style>
+
+    <style name="Widget.Holo.Light.TabWidget" parent="Widget.Holo.TabWidget">
+    </style>
+
+    <style name="Widget.Holo.Light.WebTextView" parent="Widget.WebTextView">
+    </style>
+
+    <style name="Widget.Holo.Light.WebView" parent="Widget.WebView">
+    </style>
+
+    <style name="Widget.Holo.Light.DropDownItem" parent="Widget.Holo.DropDownItem">
+    </style>
+
+    <style name="Widget.Holo.Light.DropDownItem.Spinner">
+    </style>
+
+    <style name="Widget.Holo.Light.TextView.SpinnerItem" parent="Widget.Holo.TextView.SpinnerItem">
+    </style>
+
+    <style name="Widget.Holo.Light.KeyboardView" parent="Widget.KeyboardView">
+    </style>
+
+    <style name="Widget.Holo.Light.QuickContactBadge.WindowSmall" parent="Widget.QuickContactBadge.WindowSmall">
+    </style>
+
+    <style name="Widget.Holo.Light.QuickContactBadge.WindowMedium" parent="Widget.QuickContactBadge.WindowMedium">
+    </style>
+
+    <style name="Widget.Holo.Light.QuickContactBadge.WindowLarge" parent="Widget.QuickContactBadge.WindowLarge">
+    </style>
+
+    <style name="Widget.Holo.Light.QuickContactBadgeSmall.WindowSmall" parent="Widget.QuickContactBadgeSmall.WindowSmall">
+    </style>
+
+    <style name="Widget.Holo.Light.QuickContactBadgeSmall.WindowMedium" parent="Widget.QuickContactBadgeSmall.WindowMedium">
+    </style>
+
+    <style name="Widget.Holo.Light.QuickContactBadgeSmall.WindowLarge" parent="Widget.QuickContactBadgeSmall.WindowLarge">
+    </style>
+
+    <style name="Widget.Holo.Light.ListPopupWindow" parent="Widget.ListPopupWindow">
+        <item name="android:dropDownSelector">@android:drawable/list_selector_holo_light</item>
+        <item name="android:popupBackground">@android:drawable/menu_dropdown_panel_holo_light</item>
+        <item name="android:dropDownVerticalOffset">0dip</item>
+        <item name="android:dropDownHorizontalOffset">0dip</item>
+        <item name="android:dropDownWidth">wrap_content</item>
+    </style>
+
+    <style name="Widget.Holo.Light.PopupMenu" parent="Widget.Holo.Light.ListPopupWindow">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionButton" parent="Widget.Holo.ActionButton">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionButton.Overflow">
+        <item name="android:src">@android:drawable/ic_menu_moreoverflow_holo_light</item>
+        <item name="android:background">?android:attr/selectableItemBackground</item>
+    </style>
+
+    <style name="Widget.Holo.Light.ActionBarView_TabView" parent="Widget.ActionBarView_TabView">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionBarView_TabBar" parent="Widget.ActionBarView_TabBar">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionBarView_TabText" parent="Widget.ActionBarView_TabText">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionMode" parent="Widget.Holo.ActionMode">
+        <item name="android:background">@android:drawable/cab_holo_light</item>
+        <item name="android:titleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionMode.Title</item>
+        <item name="android:subtitleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionMode.Subtitle</item>
+    </style>
+
+    <style name="Widget.Holo.Light.ActionButton.CloseMode">
+    </style>
+
+    <style name="Widget.Holo.Light.ActionBar" parent="Widget.Holo.ActionBar">
+        <item name="android:titleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionBar.Title</item>
+        <item name="android:subtitleTextStyle">@android:style/TextAppearance.Holo.Widget.ActionBar.Subtitle</item>
+        <item name="android:background">@null</item>
+        <item name="android:homeAsUpIndicator">@android:drawable/ic_ab_back_holo_light</item>
+    </style>
+
+    <!-- Animation Styles -->
+
+    <style name="Animation.Holo" parent="Animation">
+    </style>
+
+    <style name="Animation.Holo.Activity" parent="Animation.Activity">
+    </style>
+
+    <style name="Animation.Holo.Dialog" parent="Animation.Dialog">
+    </style>
+
+    <!-- Dialog styles -->
+
+    <style name="AlertDialog.Holo" parent="AlertDialog">
+        <item name="fullDark">@android:drawable/dialog_full_holo_dark</item>
+        <item name="topDark">@android:drawable/dialog_top_holo_dark</item>
+        <item name="centerDark">@android:drawable/dialog_middle_holo_dark</item>
+        <item name="bottomDark">@android:drawable/dialog_bottom_holo_dark</item>
+        <item name="fullBright">@android:drawable/dialog_full_holo_dark</item>
+        <item name="topBright">@android:drawable/dialog_top_holo_dark</item>
+        <item name="centerBright">@android:drawable/dialog_middle_holo_dark</item>
+        <item name="bottomBright">@android:drawable/dialog_bottom_holo_dark</item>
+        <item name="bottomMedium">@android:drawable/dialog_bottom_holo_dark</item>
+        <item name="centerMedium">@android:drawable/dialog_middle_holo_dark</item>
+    </style>
+
+    <style name="AlertDialog.Holo.Light">
+        <item name="fullDark">@android:drawable/dialog_full_holo_light</item>
+        <item name="topDark">@android:drawable/dialog_top_holo_light</item>
+        <item name="centerDark">@android:drawable/dialog_middle_holo_light</item>
+        <item name="bottomDark">@android:drawable/dialog_bottom_holo_light</item>
+        <item name="fullBright">@android:drawable/dialog_full_holo_light</item>
+        <item name="topBright">@android:drawable/dialog_top_holo_light</item>
+        <item name="centerBright">@android:drawable/dialog_middle_holo_light</item>
+        <item name="bottomBright">@android:drawable/dialog_bottom_holo_light</item>
+        <item name="bottomMedium">@android:drawable/dialog_bottom_holo_light</item>
+        <item name="centerMedium">@android:drawable/dialog_middle_holo_light</item>
+    </style>
+
+    <!-- Window title -->
+    <style name="WindowTitleBackground.Holo">
+        <item name="android:background">@android:drawable/title_bar</item>
+    </style>
+
+    <style name="WindowTitle.Holo">
+        <item name="android:singleLine">true</item>
+        <item name="android:textAppearance">@style/TextAppearance.Holo.WindowTitle</item>
+        <item name="android:shadowColor">#BB000000</item>
+        <item name="android:shadowRadius">2.75</item>
+    </style>
+
+    <style name="DialogWindowTitle.Holo">
+        <item name="android:maxLines">1</item>
+        <item name="android:scrollHorizontally">true</item>
+        <item name="android:textAppearance">@style/TextAppearance.Holo.DialogWindowTitle</item>
+    </style>
+
+    <style name="DialogWindowTitle.Holo.Light">
+        <item name="android:maxLines">1</item>
+        <item name="android:scrollHorizontally">true</item>
+        <item name="android:textAppearance">@style/TextAppearance.Holo.Light.DialogWindowTitle</item>
+    </style>
+
 </resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 040e732..cefc039 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -53,6 +53,11 @@
         <item name="textColorHint">@android:color/hint_foreground_dark</item>
         <item name="textColorHintInverse">@android:color/hint_foreground_light</item>
         <item name="textColorSearchUrl">@android:color/search_url_text</item>
+        <item name="textColorHighlight">@android:color/highlighted_text_dark</item>
+        <item name="textColorHighlightInverse">@android:color/highlighted_text_light</item>
+        <item name="textColorLink">@android:color/link_text_dark</item>
+        <item name="textColorLinkInverse">@android:color/link_text_light</item>
+        <item name="textColorAlertDialogListItem">@android:color/primary_text_light_disable_only</item>
 
         <item name="textAppearanceLarge">@android:style/TextAppearance.Large</item>
         <item name="textAppearanceMedium">@android:style/TextAppearance.Medium</item>
@@ -65,11 +70,17 @@
         
         <item name="textAppearanceButton">@android:style/TextAppearance.Widget.Button</item>
         
+        <item name="editTextColor">?android:attr/textColorPrimaryInverse</item>
+        <item name="editTextBackground">@android:drawable/edit_text</item>
+        
         <item name="candidatesTextStyleSpans">@android:string/candidates_style</item>
         
         <item name="textCheckMark">@android:drawable/indicator_check_mark_dark</item>
         <item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_light</item>
 
+        <item name="textAppearanceLargePopupMenu">@android:style/TextAppearance.Widget.PopupMenu.Large</item>
+        <item name="textAppearanceSmallPopupMenu">@android:style/TextAppearance.Widget.PopupMenu.Small</item>
+
         <!-- Button styles -->
         <item name="buttonStyle">@android:style/Widget.Button</item>
 
@@ -78,15 +89,24 @@
 
         <item name="buttonStyleToggle">@android:style/Widget.Button.Toggle</item>
 
+        <item name="groupButtonBackground">@null</item>
+        <item name="selectableItemBackground">@android:drawable/item_background</item>
+
         <!-- List attributes -->
         <item name="listPreferredItemHeight">64dip</item>
         <!-- @hide -->
         <item name="searchResultListItemHeight">58dip</item>
         <item name="listDivider">@drawable/divider_horizontal_dark</item>
         <item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator</item>   
-        
-		<item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item>
-    	<item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item>    
+
+        <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item>
+        <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item>
+
+        <item name="listChoiceBackgroundIndicator">@android:drawable/list_selected_background</item>
+
+        <item name="activatedBackgroundIndicator">@android:drawable/activated_background</item>
+
+        <item name="listDividerAlertDialog">@android:drawable/divider_horizontal_bright</item>
 
         <item name="expandableListPreferredItemPaddingLeft">40dip</item>
         <item name="expandableListPreferredChildPaddingLeft">
@@ -115,9 +135,15 @@
         <item name="windowTitleBackgroundStyle">@android:style/WindowTitleBackground</item>
         <item name="android:windowAnimationStyle">@android:style/Animation.Activity</item>
         <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
+        <item name="windowActionBar">false</item>
+        <item name="windowActionModeOverlay">false</item>
 
         <!-- Dialog attributes -->
         <item name="alertDialogStyle">@android:style/AlertDialog</item>
+        <item name="dialogTheme">@android:style/Theme.Dialog</item>
+        <item name="alertDialogTheme">@android:style/Theme.Dialog.Alert</item>
+        <item name="alertDialogButtonGroupStyle">?android:attr/buttonGroupStyle</item>
+        <item name="alertDialogCenterButtons">true</item>
         
         <!-- Panel attributes -->
         <item name="panelBackground">@android:drawable/menu_background</item>
@@ -141,6 +167,8 @@
         <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
         <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
         <item name="textSelectHandleWindowStyle">@android:style/Widget.TextSelectHandle</item>
+        <item name="textEditPasteWindowLayout">@android:layout/text_edit_paste_window</item>
+        <item name="textEditNoPasteWindowLayout">@android:layout/text_edit_no_paste_window</item>
 
         <!-- Widget styles -->
         <item name="absListViewStyle">@android:style/Widget.AbsListView</item>
@@ -164,8 +192,8 @@
         <item name="progressBarStyleSmallTitle">@android:style/Widget.ProgressBar.Small.Title</item>
         <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large</item>
         <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar.Inverse</item>
-		<item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>
-	    <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item> 
+        <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>
+        <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item>
         <item name="seekBarStyle">@android:style/Widget.SeekBar</item>
         <item name="ratingBarStyle">@android:style/Widget.RatingBar</item>
         <item name="ratingBarStyleIndicator">@android:style/Widget.RatingBar.Indicator</item>
@@ -174,6 +202,7 @@
         <item name="scrollViewStyle">@android:style/Widget.ScrollView</item>
         <item name="horizontalScrollViewStyle">@android:style/Widget.HorizontalScrollView</item>
         <item name="spinnerStyle">@android:style/Widget.Spinner</item>
+        <item name="dropDownSpinnerStyle">@android:style/Widget.Spinner.DropDown</item>
         <item name="starStyle">@android:style/Widget.CompoundButton.Star</item>
         <item name="tabWidgetStyle">@android:style/Widget.TabWidget</item>
         <item name="textViewStyle">@android:style/Widget.TextView</item>
@@ -190,6 +219,8 @@
         <item name="quickContactBadgeStyleSmallWindowSmall">@android:style/Widget.QuickContactBadgeSmall.WindowSmall</item>
         <item name="quickContactBadgeStyleSmallWindowMedium">@android:style/Widget.QuickContactBadgeSmall.WindowMedium</item>
         <item name="quickContactBadgeStyleSmallWindowLarge">@android:style/Widget.QuickContactBadgeSmall.WindowLarge</item>
+        <item name="listPopupWindowStyle">@android:style/Widget.ListPopupWindow</item>
+        <item name="popupMenuStyle">@android:style/Widget.PopupMenu</item>
         
         <!-- Preference styles -->
         <item name="preferenceScreenStyle">@android:style/Preference.PreferenceScreen</item>
@@ -205,6 +236,29 @@
 
         <!-- Search widget styles -->
         <item name="searchWidgetCorpusItemBackground">@android:color/search_widget_corpus_item_background</item>
+
+        <!-- Action bar styles -->
+        <item name="actionDropDownStyle">@android:style/Widget.Spinner.DropDown</item>
+        <item name="actionButtonStyle">@android:style/Widget.ActionButton</item>
+        <item name="actionOverflowButtonStyle">@android:style/Widget.ActionButton.Overflow</item>
+        <item name="actionModeBackground">@android:drawable/action_bar_context_background</item>
+        <item name="actionModeCloseDrawable">@android:drawable/ic_menu_close_clear_cancel</item>
+        <item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_dark</item>
+        <item name="actionModeCopyDrawable">@android:drawable/ic_menu_copy_dark</item>
+        <item name="actionModePasteDrawable">@android:drawable/ic_menu_paste_dark</item>
+        <item name="actionBarTabStyle">@style/Widget.ActionBarView_TabView</item>
+        <item name="actionBarTabBarStyle">@style/Widget.ActionBarView_TabBar</item>
+        <item name="actionBarTabTextStyle">@style/Widget.ActionBarView_TabText</item>
+        <item name="actionModeStyle">@style/Widget.ActionMode</item>
+        <item name="actionModeCloseButtonStyle">@style/Widget.ActionButton.CloseMode</item>
+        <item name="actionBarStyle">@android:style/Widget.ActionBar</item>
+        <item name="actionBarSize">56dip</item>
+
+        <item name="dividerVertical">@drawable/divider_vertical_dark</item>
+        <item name="buttonGroupStyle">@android:style/Widget.ButtonGroup</item>
+
+        <!-- SearchView attributes -->
+        <item name="searchDropdownBackground">@android:drawable/search_dropdown_dark</item>
     </style>
     
     <!-- Variant of the default (dark) theme with no title bar -->
@@ -228,7 +282,7 @@
         <item name="colorBackground">@android:color/background_light</item>
         <item name="colorForeground">@color/bright_foreground_light</item>
         <item name="colorForegroundInverse">@android:color/bright_foreground_light_inverse</item>
-
+        
         <item name="textColorPrimary">@android:color/primary_text_light</item>
         <item name="textColorSecondary">@android:color/secondary_text_light</item>
         <item name="textColorTertiary">@android:color/tertiary_text_light</item>
@@ -242,8 +296,17 @@
         <item name="textColorPrimaryInverseNoDisable">@android:color/primary_text_dark_nodisable</item>
         <item name="textColorSecondaryInverseNoDisable">@android:color/secondary_text_dark_nodisable</item>
         <item name="textColorHint">@android:color/hint_foreground_light</item>
-        <item name="textColorHintInverse">@android:color/hint_foreground_dark</item>
+        <item name="textColorHintInverse">@android:color/hint_foreground_dark</item>        
+        <item name="textColorHighlight">@android:color/highlighted_text_light</item>
+        <item name="textColorHighlightInverse">@android:color/highlighted_text_dark</item>
+        <item name="textColorLink">@android:color/link_text_light</item>
+        <item name="textColorLinkInverse">@android:color/link_text_dark</item>
         
+        <item name="editTextColor">?android:attr/textColorPrimary</item>
+        <item name="listChoiceBackgroundIndicator">@android:drawable/list_selected_background_light</item>
+
+        <item name="activatedBackgroundIndicator">@android:drawable/activated_background_light</item>
+
         <item name="popupWindowStyle">@android:style/Widget.PopupWindow</item>
         
         <item name="textCheckMark">@android:drawable/indicator_check_mark_light</item>
@@ -254,13 +317,18 @@
         <item name="listViewStyle">@android:style/Widget.ListView.White</item>
         <item name="listDivider">@drawable/divider_horizontal_bright</item>
         <item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator.White</item>
-        
+
         <item name="progressBarStyle">@android:style/Widget.ProgressBar.Inverse</item>
-		<item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item>
-		<item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item>
-		<item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item>
-		<item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item>
-		<item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item> 
+        <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item>
+        <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item>
+        <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item>
+        <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item>
+        <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item>
+        <item name="actionModeCutDrawable">@android:drawable/ic_menu_cut_light</item>
+        <item name="actionModeCopyDrawable">@android:drawable/ic_menu_copy_light</item>
+        <item name="actionModePasteDrawable">@android:drawable/ic_menu_paste_light</item>
+        <!-- SearchView attributes -->
+        <item name="searchDropdownBackground">@android:drawable/search_dropdown_light</item>
     </style>
     
     <!-- Variant of the light theme with no title bar -->
@@ -423,13 +491,25 @@
         <item name="textAppearanceSmallInverse">@android:style/TextAppearance.Small.Inverse</item>
     </style>
 
+    <!-- Variation of Theme.Dialog that does not include a frame (or background).
+         The view hierarchy of the dialog is responsible for drawing all of
+         its pixels. -->
+    <style name="Theme.Dialog.NoFrame">
+        <item name="windowBackground">@android:color/transparent</item>
+        <item name="android:windowFrame">@null</item>
+        <item name="windowContentOverlay">@null</item>
+        <item name="android:windowAnimationStyle">@null</item>
+        <item name="android:backgroundDimEnabled">false</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
     <!-- Default theme for alert dialog windows, which is used by the
          {@link android.app.AlertDialog} class.  This is basically a dialog
          but sets the background to empty so it can do two-tone backgrounds. -->
     <style name="Theme.Dialog.Alert">
         <item name="windowBackground">@android:color/transparent</item>
         <item name="windowTitleStyle">@android:style/DialogWindowTitle</item>
-        <item name="windowIsFloating">true</item>
         <item name="windowContentOverlay">@null</item>
     </style>
     
@@ -511,7 +591,7 @@
     </style>
 
     <!-- @hide -->
-    <style name="Theme.Dialog.AppError">
+    <style name="Theme.Dialog.AppError" parent="Theme.Holo.Dialog">
         <item name="windowFrame">@null</item>
         <item name="windowTitleStyle">@android:style/DialogWindowTitle</item>
         <item name="windowBackground">@android:color/transparent</item>
@@ -535,5 +615,611 @@
         <item name="android:windowAnimationStyle">@android:style/Animation.Toast</item>
         <item name="android:backgroundDimEnabled">false</item>
     </style>
+
+    <!-- Default theme with an Action Bar. -->
+    <style name="Theme.WithActionBar">
+        <item name="android:windowActionBar">true</item>
+    </style>
+
+    <!-- No title bar, but Action Mode bars will overlay application content
+         instead of pushing it down to make room. -->
+    <style name="Theme.NoTitleBar.OverlayActionModes">
+        <item name="android:windowActionModeOverlay">true</item>
+    </style>
     
+    <!-- New Honeycomb holographic theme. Dark version.  The widgets in the
+         holographic theme are translucent on their brackground, so applications
+         must ensure that any background they use with this theme is itself
+         dark; otherwise, it will be difficult to see the widgets.  The new
+         UI style also includes a full action bar by default.
+
+         Styles used by the Holo theme are named using the convention Type.Holo.Etc.
+         (For example, Widget.Holo.Button, TextAppearance.Holo.Widget.PopupMenu.Large.)
+         Specific resources used by Holo are named using the convention @type/foo_bar_baz_holo
+         with trailing _dark or _light specifiers if they are not shared between both light and
+         dark versions of the theme. -->
+    <style name="Theme.Holo">
+        <item name="colorForeground">@android:color/bright_foreground_holo_dark</item>
+        <item name="colorForegroundInverse">@android:color/bright_foreground_inverse_holo_dark</item>
+        <item name="colorBackground">@android:color/background_holo_dark</item>
+        <item name="colorBackgroundCacheHint">?android:attr/colorBackground</item>
+        <item name="disabledAlpha">0.5</item>
+        <item name="backgroundDimAmount">0.6</item>
+
+        <!-- Text styles -->
+        <item name="textAppearance">@android:style/TextAppearance.Holo</item>
+        <item name="textAppearanceInverse">@android:style/TextAppearance.Holo.Inverse</item>
+
+        <item name="textColorPrimary">@android:color/primary_text_holo_dark</item>
+        <item name="textColorSecondary">@android:color/secondary_text_holo_dark</item>
+        <item name="textColorTertiary">@android:color/tertiary_text_holo_dark</item>
+        <item name="textColorPrimaryInverse">@android:color/primary_text_holo_light</item>
+        <item name="textColorSecondaryInverse">@android:color/secondary_text_holo_light</item>
+        <item name="textColorTertiaryInverse">@android:color/tertiary_text_holo_light</item>
+        <item name="textColorPrimaryDisableOnly">@android:color/primary_text_disable_only_holo_dark</item>
+        <item name="textColorPrimaryInverseDisableOnly">@android:color/primary_text_disable_only_holo_light</item>
+        <item name="textColorPrimaryNoDisable">@android:color/primary_text_nodisable_holo_dark</item>
+        <item name="textColorSecondaryNoDisable">@android:color/secondary_text_nodisable_holo_dark</item>
+        <item name="textColorPrimaryInverseNoDisable">@android:color/primary_text_nodisable_holo_light</item>
+        <item name="textColorSecondaryInverseNoDisable">@android:color/secondary_text_nodisable_holo_light</item>
+        <item name="textColorHint">@android:color/hint_foreground_holo_dark</item>
+        <item name="textColorHintInverse">@android:color/hint_foreground_holo_light</item>
+        <item name="textColorSearchUrl">@android:color/search_url_text_holo</item>
+        <item name="textColorHighlight">@android:color/highlighted_text_holo_dark</item>
+        <item name="textColorHighlightInverse">@android:color/highlighted_text_holo_light</item>
+        <item name="textColorLink">@android:color/link_text_holo_dark</item>
+        <item name="textColorLinkInverse">@android:color/link_text_holo_light</item>
+        <item name="textColorAlertDialogListItem">@android:color/primary_text_holo_dark</item>
+
+        <item name="textAppearanceLarge">@android:style/TextAppearance.Holo.Large</item>
+        <item name="textAppearanceMedium">@android:style/TextAppearance.Holo.Medium</item>
+        <item name="textAppearanceSmall">@android:style/TextAppearance.Holo.Small</item>
+        <item name="textAppearanceLargeInverse">@android:style/TextAppearance.Holo.Large.Inverse</item>
+        <item name="textAppearanceMediumInverse">@android:style/TextAppearance.Holo.Medium.Inverse</item>
+        <item name="textAppearanceSmallInverse">@android:style/TextAppearance.Holo.Small.Inverse</item>
+        <item name="textAppearanceSearchResultTitle">@android:style/TextAppearance.Holo.SearchResult.Title</item>
+        <item name="textAppearanceSearchResultSubtitle">@android:style/TextAppearance.Holo.SearchResult.Subtitle</item>
+        
+        <item name="textAppearanceButton">@android:style/TextAppearance.Holo.Widget.Button</item>
+        
+        <item name="editTextColor">?android:attr/textColorPrimary</item>
+        <item name="editTextBackground">@android:drawable/edit_text_holo_dark</item>
+        
+        <item name="candidatesTextStyleSpans">@android:string/candidates_style</item>
+        
+        <item name="textCheckMark">@android:drawable/indicator_check_mark_dark</item>
+        <item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_light</item>
+
+        <item name="textAppearanceLargePopupMenu">@android:style/TextAppearance.Holo.Widget.PopupMenu.Large</item>
+        <item name="textAppearanceSmallPopupMenu">@android:style/TextAppearance.Holo.Widget.PopupMenu.Small</item>
+
+        <!-- Button styles -->
+        <item name="buttonStyle">@android:style/Widget.Holo.Button</item>
+
+        <item name="buttonStyleSmall">@android:style/Widget.Holo.Button.Small</item>
+        <item name="buttonStyleInset">@android:style/Widget.Holo.Button.Inset</item>
+
+        <item name="buttonStyleToggle">@android:style/Widget.Holo.Button.Toggle</item>
+
+        <item name="groupButtonBackground">@android:drawable/group_button_background_holo_dark</item>
+        <item name="selectableItemBackground">@android:drawable/item_background_holo_dark</item>
+
+        <!-- List attributes -->
+        <item name="listPreferredItemHeight">64dip</item>
+        <!-- @hide -->
+        <item name="searchResultListItemHeight">58dip</item>
+        <item name="listDivider">@drawable/list_divider_holo_dark</item>
+        <item name="listSeparatorTextViewStyle">@android:style/Widget.Holo.TextView.ListSeparator</item>   
+
+        <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio_holo_dark</item>
+        <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check_holo_dark</item>
+
+        <item name="listChoiceBackgroundIndicator">@android:drawable/list_selector_holo_dark</item>
+
+        <item name="activatedBackgroundIndicator">@android:drawable/activated_background_holo_dark</item>
+
+        <item name="listDividerAlertDialog">@android:drawable/list_divider_holo_dark</item>
+
+        <item name="expandableListPreferredItemPaddingLeft">40dip</item>
+        <item name="expandableListPreferredChildPaddingLeft">
+                ?android:attr/expandableListPreferredItemPaddingLeft</item>
+
+        <item name="expandableListPreferredItemIndicatorLeft">3dip</item>
+        <item name="expandableListPreferredItemIndicatorRight">33dip</item>
+        <item name="expandableListPreferredChildIndicatorLeft">
+                ?android:attr/expandableListPreferredItemIndicatorLeft</item>
+        <item name="expandableListPreferredChildIndicatorRight">
+                ?android:attr/expandableListPreferredItemIndicatorRight</item>
+
+        <!-- Gallery attributes -->
+        <item name="galleryItemBackground">@android:drawable/gallery_item_background</item>
+        
+        <!-- Window attributes -->
+        <item name="windowBackground">?android:attr/colorBackground</item>
+        <item name="windowFrame">@null</item>
+        <item name="windowNoTitle">false</item>
+        <item name="windowFullscreen">false</item>
+        <item name="windowIsFloating">false</item>
+        <item name="windowContentOverlay">@null</item>
+        <item name="windowShowWallpaper">false</item>
+        <item name="windowTitleStyle">@android:style/WindowTitle.Holo</item>
+        <item name="windowTitleSize">25dip</item>
+        <item name="windowTitleBackgroundStyle">@android:style/WindowTitleBackground.Holo</item>
+        <item name="android:windowAnimationStyle">@android:style/Animation.Holo.Activity</item>
+        <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
+        <item name="windowActionBar">true</item>
+        <item name="windowActionModeOverlay">false</item>
+
+        <!-- Dialog attributes -->
+        <item name="alertDialogStyle">@android:style/AlertDialog.Holo</item>
+        <item name="dialogTheme">@android:style/Theme.Holo.Dialog</item>
+        <item name="alertDialogTheme">@android:style/Theme.Holo.Dialog.Alert</item>
+        <item name="alertDialogButtonGroupStyle">@android:style/Widget.Holo.ButtonGroup.AlertDialog</item>
+        <item name="alertDialogCenterButtons">false</item>
+        
+        <!-- Panel attributes -->
+        <item name="panelBackground">@android:drawable/menu_background</item>
+        <item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
+        <!-- These three attributes do not seems to be used by the framework. Declared public though -->
+        <item name="panelColorBackground">#000</item>
+        <item name="panelColorForeground">?android:attr/textColorPrimary</item>
+        <item name="panelTextAppearance">?android:attr/textAppearance</item>
+
+        <!-- Scrollbar attributes -->
+        <item name="scrollbarFadeDuration">250</item>
+        <item name="scrollbarDefaultDelayBeforeFade">300</item> 
+        <item name="scrollbarSize">10dip</item>
+        <item name="scrollbarThumbHorizontal">@android:drawable/scrollbar_handle_horizontal</item>
+        <item name="scrollbarThumbVertical">@android:drawable/scrollbar_handle_vertical</item>
+        <item name="scrollbarTrackHorizontal">@null</item>
+        <item name="scrollbarTrackVertical">@null</item>
+
+        <!-- Text selection handle attributes -->
+        <item name="textSelectHandleLeft">@android:drawable/text_select_handle_left</item>
+        <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
+        <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
+        <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
+
+        <!-- Widget styles -->
+        <item name="absListViewStyle">@android:style/Widget.Holo.AbsListView</item>
+        <item name="autoCompleteTextViewStyle">@android:style/Widget.Holo.AutoCompleteTextView</item>
+        <item name="checkboxStyle">@android:style/Widget.Holo.CompoundButton.CheckBox</item>
+        <item name="dropDownListViewStyle">@android:style/Widget.Holo.ListView.DropDown</item>
+        <item name="editTextStyle">@android:style/Widget.Holo.EditText</item>
+        <item name="expandableListViewStyle">@android:style/Widget.Holo.ExpandableListView</item>
+        <item name="expandableListViewWhiteStyle">@android:style/Widget.Holo.ExpandableListView.White</item>
+        <item name="galleryStyle">@android:style/Widget.Holo.Gallery</item>
+        <item name="gestureOverlayViewStyle">@android:style/Widget.Holo.GestureOverlayView</item>
+        <item name="gridViewStyle">@android:style/Widget.Holo.GridView</item>
+        <item name="imageButtonStyle">@android:style/Widget.Holo.ImageButton</item>
+        <item name="imageWellStyle">@android:style/Widget.Holo.ImageWell</item>
+        <item name="listViewStyle">@android:style/Widget.Holo.ListView</item>
+        <item name="listViewWhiteStyle">@android:style/Widget.Holo.ListView.White</item>
+        <item name="popupWindowStyle">@android:style/Widget.Holo.PopupWindow</item>
+        <item name="progressBarStyle">@android:style/Widget.Holo.ProgressBar</item>
+        <item name="progressBarStyleHorizontal">@android:style/Widget.Holo.ProgressBar.Horizontal</item>
+        <item name="progressBarStyleSmall">@android:style/Widget.Holo.ProgressBar.Small</item>
+        <item name="progressBarStyleSmallTitle">@android:style/Widget.Holo.ProgressBar.Small.Title</item>
+        <item name="progressBarStyleLarge">@android:style/Widget.Holo.ProgressBar.Large</item>
+        <item name="progressBarStyleInverse">@android:style/Widget.Holo.ProgressBar.Inverse</item>
+        <item name="progressBarStyleSmallInverse">@android:style/Widget.Holo.ProgressBar.Small.Inverse</item>
+        <item name="progressBarStyleLargeInverse">@android:style/Widget.Holo.ProgressBar.Large.Inverse</item>
+        <item name="seekBarStyle">@android:style/Widget.Holo.SeekBar</item>
+        <item name="ratingBarStyle">@android:style/Widget.Holo.RatingBar</item>
+        <item name="ratingBarStyleIndicator">@android:style/Widget.Holo.RatingBar.Indicator</item>
+        <item name="ratingBarStyleSmall">@android:style/Widget.Holo.RatingBar.Small</item>
+        <item name="radioButtonStyle">@android:style/Widget.Holo.CompoundButton.RadioButton</item>
+        <item name="scrollViewStyle">@android:style/Widget.Holo.ScrollView</item>
+        <item name="horizontalScrollViewStyle">@android:style/Widget.Holo.HorizontalScrollView</item>
+        <item name="spinnerStyle">?android:attr/dropDownSpinnerStyle</item>
+        <item name="dropDownSpinnerStyle">@android:style/Widget.Holo.Spinner.DropDown</item>
+        <item name="starStyle">@android:style/Widget.Holo.CompoundButton.Star</item>
+        <item name="tabWidgetStyle">@android:style/Widget.Holo.TabWidget</item>
+        <item name="textViewStyle">@android:style/Widget.Holo.TextView</item>
+        <item name="webTextViewStyle">@android:style/Widget.Holo.WebTextView</item>
+        <item name="webViewStyle">@android:style/Widget.Holo.WebView</item>
+        <item name="dropDownItemStyle">@android:style/Widget.Holo.DropDownItem</item>
+        <item name="spinnerDropDownItemStyle">@android:style/Widget.Holo.DropDownItem.Spinner</item>
+        <item name="spinnerItemStyle">@android:style/Widget.Holo.TextView.SpinnerItem</item>
+        <item name="dropDownHintAppearance">@android:style/TextAppearance.Holo.Widget.DropDownHint</item>
+        <item name="keyboardViewStyle">@android:style/Widget.Holo.KeyboardView</item>
+        <item name="quickContactBadgeStyleWindowSmall">@android:style/Widget.Holo.QuickContactBadge.WindowSmall</item>
+        <item name="quickContactBadgeStyleWindowMedium">@android:style/Widget.Holo.QuickContactBadge.WindowMedium</item>
+        <item name="quickContactBadgeStyleWindowLarge">@android:style/Widget.Holo.QuickContactBadge.WindowLarge</item>
+        <item name="quickContactBadgeStyleSmallWindowSmall">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowSmall</item>
+        <item name="quickContactBadgeStyleSmallWindowMedium">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowMedium</item>
+        <item name="quickContactBadgeStyleSmallWindowLarge">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowLarge</item>
+        <item name="listPopupWindowStyle">@android:style/Widget.Holo.ListPopupWindow</item>
+        <item name="popupMenuStyle">@android:style/Widget.Holo.PopupMenu</item>
+        
+        <!-- Preference styles -->
+        <item name="preferenceScreenStyle">@android:style/Preference.PreferenceScreen</item>
+        <item name="preferenceCategoryStyle">@android:style/Preference.Category</item>
+        <item name="preferenceStyle">@android:style/Preference</item>
+        <item name="preferenceInformationStyle">@android:style/Preference.Information</item>
+        <item name="checkBoxPreferenceStyle">@android:style/Preference.CheckBoxPreference</item>
+        <item name="yesNoPreferenceStyle">@android:style/Preference.DialogPreference.YesNoPreference</item>
+        <item name="dialogPreferenceStyle">@android:style/Preference.DialogPreference</item>
+        <item name="editTextPreferenceStyle">@android:style/Preference.DialogPreference.EditTextPreference</item>
+        <item name="ringtonePreferenceStyle">@android:style/Preference.RingtonePreference</item>
+        <item name="preferenceLayoutChild">@android:layout/preference_child</item>
+
+        <!-- Search widget styles -->
+        <item name="searchWidgetCorpusItemBackground">@android:color/search_widget_corpus_item_background</item>
+
+        <!-- Action bar styles -->
+        <item name="actionDropDownStyle">@android:style/Widget.Holo.Spinner.DropDown.ActionBar</item>
+        <item name="actionButtonStyle">@android:style/Widget.Holo.ActionButton</item>
+        <item name="actionOverflowButtonStyle">@android:style/Widget.Holo.ActionButton.Overflow</item>
+        <item name="actionModeBackground">@android:drawable/cab_holo_dark</item>
+        <item name="actionModeCloseDrawable">@android:drawable/cab_ic_close_holo</item>
+        <item name="actionBarTabStyle">@style/Widget.Holo.ActionBarView_TabView</item>
+        <item name="actionBarTabBarStyle">@style/Widget.Holo.ActionBarView_TabBar</item>
+        <item name="actionBarTabTextStyle">@style/Widget.Holo.ActionBarView_TabText</item>
+        <item name="actionModeStyle">@style/Widget.Holo.ActionMode</item>
+        <item name="actionModeCloseButtonStyle">@style/Widget.Holo.ActionButton.CloseMode</item>
+        <item name="actionBarStyle">@android:style/Widget.Holo.ActionBar</item>
+        <item name="actionBarSize">56dip</item>
+
+        <item name="dividerVertical">@drawable/divider_vertical_holo_dark</item>
+        <item name="buttonGroupStyle">@android:style/Widget.Holo.ButtonGroup</item>
+
+        <!-- SearchView attributes -->
+        <item name="searchDropdownBackground">@android:drawable/search_dropdown_dark</item>
+    </style>
+
+    <!-- New Honeycomb holographic theme. Light version.  The widgets in the
+         holographic theme are translucent on their brackground, so applications
+         must ensure that any background they use with this theme is itself
+         light; otherwise, it will be difficult to see the widgets.  The new
+         UI style also includes a full action bar by default. -->
+    <style name="Theme.Holo.Light" parent="Theme.Light">
+        <item name="colorForeground">@android:color/bright_foreground_holo_light</item>
+        <item name="colorForegroundInverse">@android:color/bright_foreground_inverse_holo_light</item>
+        <item name="colorBackground">@android:color/background_holo_light</item>
+        <item name="colorBackgroundCacheHint">?android:attr/colorBackground</item>
+        <item name="disabledAlpha">0.5</item>
+        <item name="backgroundDimAmount">0.6</item>
+
+        <!-- Text styles -->
+        <item name="textAppearance">@android:style/TextAppearance.Holo.Light</item>
+        <item name="textAppearanceInverse">@android:style/TextAppearance.Holo.Light.Inverse</item>
+
+        <item name="textColorPrimary">@android:color/primary_text_holo_light</item>
+        <item name="textColorSecondary">@android:color/secondary_text_holo_light</item>
+        <item name="textColorTertiary">@android:color/tertiary_text_holo_light</item>
+        <item name="textColorPrimaryInverse">@android:color/primary_text_holo_dark</item>
+        <item name="textColorSecondaryInverse">@android:color/secondary_text_holo_dark</item>
+        <item name="textColorTertiaryInverse">@android:color/tertiary_text_holo_dark</item>
+        <item name="textColorPrimaryDisableOnly">@android:color/primary_text_disable_only_holo_light</item>
+        <item name="textColorPrimaryInverseDisableOnly">@android:color/primary_text_disable_only_holo_dark</item>
+        <item name="textColorPrimaryNoDisable">@android:color/primary_text_nodisable_holo_light</item>
+        <item name="textColorSecondaryNoDisable">@android:color/secondary_text_nodisable_holo_light</item>
+        <item name="textColorPrimaryInverseNoDisable">@android:color/primary_text_nodisable_holo_dark</item>
+        <item name="textColorSecondaryInverseNoDisable">@android:color/secondary_text_nodisable_holo_dark</item>
+        <item name="textColorHint">@android:color/hint_foreground_holo_light</item>
+        <item name="textColorHintInverse">@android:color/hint_foreground_holo_dark</item>
+        <item name="textColorSearchUrl">@android:color/search_url_text_holo</item>
+        <item name="textColorHighlight">@android:color/highlighted_text_holo_light</item>
+        <item name="textColorHighlightInverse">@android:color/highlighted_text_holo_dark</item>
+        <item name="textColorLink">@android:color/link_text_holo_light</item>
+        <item name="textColorLinkInverse">@android:color/link_text_holo_dark</item>
+        <item name="textColorAlertDialogListItem">@android:color/primary_text_holo_light</item>
+
+        <item name="textAppearanceLarge">@android:style/TextAppearance.Holo.Light.Large</item>
+        <item name="textAppearanceMedium">@android:style/TextAppearance.Holo.Light.Medium</item>
+        <item name="textAppearanceSmall">@android:style/TextAppearance.Holo.Light.Small</item>
+        <item name="textAppearanceLargeInverse">@android:style/TextAppearance.Holo.Light.Large.Inverse</item>
+        <item name="textAppearanceMediumInverse">@android:style/TextAppearance.Holo.Light.Medium.Inverse</item>
+        <item name="textAppearanceSmallInverse">@android:style/TextAppearance.Holo.Light.Small.Inverse</item>
+        <item name="textAppearanceSearchResultTitle">@android:style/TextAppearance.Holo.Light.SearchResult.Title</item>
+        <item name="textAppearanceSearchResultSubtitle">@android:style/TextAppearance.Holo.Light.SearchResult.Subtitle</item>
+        
+        <item name="textAppearanceButton">@android:style/TextAppearance.Holo.Light.Widget.Button</item>
+        
+        <item name="editTextColor">?android:attr/textColorPrimary</item>
+        <item name="editTextBackground">@android:drawable/edit_text_holo_light</item>
+        
+        <item name="candidatesTextStyleSpans">@android:string/candidates_style</item>
+        
+        <item name="textCheckMark">@android:drawable/indicator_check_mark_light</item>
+        <item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_dark</item>
+
+        <item name="textAppearanceLargePopupMenu">@android:style/TextAppearance.Holo.Light.Widget.PopupMenu.Large</item>
+        <item name="textAppearanceSmallPopupMenu">@android:style/TextAppearance.Holo.Light.Widget.PopupMenu.Small</item>
+
+        <!-- Button styles -->
+        <item name="buttonStyle">@android:style/Widget.Holo.Light.Button</item>
+
+        <item name="buttonStyleSmall">@android:style/Widget.Holo.Light.Button.Small</item>
+        <item name="buttonStyleInset">@android:style/Widget.Holo.Light.Button.Inset</item>
+
+        <item name="buttonStyleToggle">@android:style/Widget.Holo.Light.Button.Toggle</item>
+
+        <item name="groupButtonBackground">@android:drawable/group_button_background_holo_light</item>
+        <item name="selectableItemBackground">@android:drawable/item_background_holo_light</item>
+
+        <!-- List attributes -->
+        <item name="listPreferredItemHeight">64dip</item>
+        <!-- @hide -->
+        <item name="searchResultListItemHeight">58dip</item>
+        <item name="listDivider">@drawable/list_divider_holo_light</item>
+        <item name="listSeparatorTextViewStyle">@android:style/Widget.Holo.Light.TextView.ListSeparator</item>   
+
+        <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio_holo_light</item>
+        <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check_holo_light</item>
+
+        <item name="listChoiceBackgroundIndicator">@android:drawable/list_selector_holo_light</item>
+
+        <item name="activatedBackgroundIndicator">@android:drawable/activated_background_holo_light</item>
+
+        <item name="expandableListPreferredItemPaddingLeft">40dip</item>
+        <item name="expandableListPreferredChildPaddingLeft">
+                ?android:attr/expandableListPreferredItemPaddingLeft</item>
+
+        <item name="expandableListPreferredItemIndicatorLeft">3dip</item>
+        <item name="expandableListPreferredItemIndicatorRight">33dip</item>
+        <item name="expandableListPreferredChildIndicatorLeft">
+                ?android:attr/expandableListPreferredItemIndicatorLeft</item>
+        <item name="expandableListPreferredChildIndicatorRight">
+                ?android:attr/expandableListPreferredItemIndicatorRight</item>
+
+        <item name="listDividerAlertDialog">@android:drawable/list_divider_holo_light</item>
+
+        <!-- Gallery attributes -->
+        <item name="galleryItemBackground">@android:drawable/gallery_item_background</item>
+        
+        <!-- Window attributes -->
+        <item name="windowBackground">?android:attr/colorBackground</item>
+        <item name="windowFrame">@null</item>
+        <item name="windowNoTitle">false</item>
+        <item name="windowFullscreen">false</item>
+        <item name="windowIsFloating">false</item>
+        <item name="windowContentOverlay">@null</item>
+        <item name="windowShowWallpaper">false</item>
+        <item name="windowTitleStyle">@android:style/WindowTitle.Holo</item>
+        <item name="windowTitleSize">25dip</item>
+        <item name="windowTitleBackgroundStyle">@android:style/WindowTitleBackground.Holo</item>
+        <item name="android:windowAnimationStyle">@android:style/Animation.Holo.Activity</item>
+        <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
+        <item name="windowActionBar">true</item>
+        <item name="windowActionModeOverlay">false</item>
+
+        <!-- Dialog attributes -->
+        <item name="alertDialogStyle">@android:style/AlertDialog.Holo.Light</item>
+        <item name="dialogTheme">@android:style/Theme.Holo.Light.Dialog</item>
+        <item name="alertDialogTheme">@android:style/Theme.Holo.Light.Dialog.Alert</item>
+        <item name="alertDialogButtonGroupStyle">@android:style/Widget.Holo.Light.ButtonGroup.AlertDialog</item>
+        
+        <!-- Panel attributes -->
+        <item name="panelBackground">@android:drawable/menu_background</item>
+        <item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
+        <!-- These three attributes do not seems to be used by the framework. Declared public though -->
+        <item name="panelColorBackground">#000</item>
+        <item name="panelColorForeground">?android:attr/textColorPrimary</item>
+        <item name="panelTextAppearance">?android:attr/textAppearance</item>
+
+        <!-- Scrollbar attributes -->
+        <item name="scrollbarFadeDuration">250</item>
+        <item name="scrollbarDefaultDelayBeforeFade">300</item>
+        <item name="scrollbarSize">10dip</item>
+        <item name="scrollbarThumbHorizontal">@android:drawable/scrollbar_handle_horizontal</item>
+        <item name="scrollbarThumbVertical">@android:drawable/scrollbar_handle_vertical</item>
+        <item name="scrollbarTrackHorizontal">@null</item>
+        <item name="scrollbarTrackVertical">@null</item>
+
+        <!-- Text selection handle attributes -->
+        <item name="textSelectHandleLeft">@android:drawable/text_select_handle_left</item>
+        <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
+        <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
+        <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item>
+
+        <!-- Widget styles -->
+        <item name="absListViewStyle">@android:style/Widget.Holo.AbsListView</item>
+        <item name="autoCompleteTextViewStyle">@android:style/Widget.Holo.AutoCompleteTextView</item>
+        <item name="checkboxStyle">@android:style/Widget.Holo.CompoundButton.CheckBox</item>
+        <item name="dropDownListViewStyle">@android:style/Widget.Holo.ListView.DropDown</item>
+        <item name="editTextStyle">@android:style/Widget.Holo.EditText</item>
+        <item name="expandableListViewStyle">@android:style/Widget.Holo.ExpandableListView</item>
+        <item name="expandableListViewWhiteStyle">@android:style/Widget.Holo.ExpandableListView.White</item>
+        <item name="galleryStyle">@android:style/Widget.Holo.Gallery</item>
+        <item name="gestureOverlayViewStyle">@android:style/Widget.Holo.GestureOverlayView</item>
+        <item name="gridViewStyle">@android:style/Widget.Holo.GridView</item>
+        <item name="imageButtonStyle">@android:style/Widget.Holo.ImageButton</item>
+        <item name="imageWellStyle">@android:style/Widget.Holo.ImageWell</item>
+        <item name="listViewStyle">@android:style/Widget.Holo.ListView</item>
+        <item name="listViewWhiteStyle">@android:style/Widget.Holo.ListView.White</item>
+        <item name="popupWindowStyle">@android:style/Widget.Holo.PopupWindow</item>
+        <item name="progressBarStyle">@android:style/Widget.Holo.ProgressBar</item>
+        <item name="progressBarStyleHorizontal">@android:style/Widget.Holo.ProgressBar.Horizontal</item>
+        <item name="progressBarStyleSmall">@android:style/Widget.Holo.ProgressBar.Small</item>
+        <item name="progressBarStyleSmallTitle">@android:style/Widget.Holo.ProgressBar.Small.Title</item>
+        <item name="progressBarStyleLarge">@android:style/Widget.Holo.ProgressBar.Large</item>
+        <item name="progressBarStyleInverse">@android:style/Widget.Holo.ProgressBar.Inverse</item>
+        <item name="progressBarStyleSmallInverse">@android:style/Widget.Holo.ProgressBar.Small.Inverse</item>
+        <item name="progressBarStyleLargeInverse">@android:style/Widget.Holo.ProgressBar.Large.Inverse</item>
+        <item name="seekBarStyle">@android:style/Widget.Holo.SeekBar</item>
+        <item name="ratingBarStyle">@android:style/Widget.Holo.RatingBar</item>
+        <item name="ratingBarStyleIndicator">@android:style/Widget.Holo.RatingBar.Indicator</item>
+        <item name="ratingBarStyleSmall">@android:style/Widget.Holo.RatingBar.Small</item>
+        <item name="radioButtonStyle">@android:style/Widget.Holo.CompoundButton.RadioButton</item>
+        <item name="scrollViewStyle">@android:style/Widget.Holo.ScrollView</item>
+        <item name="horizontalScrollViewStyle">@android:style/Widget.Holo.HorizontalScrollView</item>
+        <item name="spinnerStyle">?android:attr/dropDownSpinnerStyle</item>
+        <item name="dropDownSpinnerStyle">@android:style/Widget.Holo.Light.Spinner.DropDown</item>
+        <item name="starStyle">@android:style/Widget.Holo.CompoundButton.Star</item>
+        <item name="tabWidgetStyle">@android:style/Widget.Holo.TabWidget</item>
+        <item name="textViewStyle">@android:style/Widget.Holo.TextView</item>
+        <item name="webTextViewStyle">@android:style/Widget.Holo.WebTextView</item>
+        <item name="webViewStyle">@android:style/Widget.Holo.WebView</item>
+        <item name="dropDownItemStyle">@android:style/Widget.Holo.DropDownItem</item>
+        <item name="spinnerDropDownItemStyle">@android:style/Widget.Holo.DropDownItem.Spinner</item>
+        <item name="spinnerItemStyle">@android:style/Widget.Holo.TextView.SpinnerItem</item>
+        <item name="dropDownHintAppearance">@android:style/TextAppearance.Holo.Widget.DropDownHint</item>
+        <item name="keyboardViewStyle">@android:style/Widget.Holo.KeyboardView</item>
+        <item name="quickContactBadgeStyleWindowSmall">@android:style/Widget.Holo.QuickContactBadge.WindowSmall</item>
+        <item name="quickContactBadgeStyleWindowMedium">@android:style/Widget.Holo.QuickContactBadge.WindowMedium</item>
+        <item name="quickContactBadgeStyleWindowLarge">@android:style/Widget.Holo.QuickContactBadge.WindowLarge</item>
+        <item name="quickContactBadgeStyleSmallWindowSmall">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowSmall</item>
+        <item name="quickContactBadgeStyleSmallWindowMedium">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowMedium</item>
+        <item name="quickContactBadgeStyleSmallWindowLarge">@android:style/Widget.Holo.QuickContactBadgeSmall.WindowLarge</item>
+        <item name="listPopupWindowStyle">@android:style/Widget.Holo.Light.ListPopupWindow</item>
+        <item name="popupMenuStyle">@android:style/Widget.Holo.Light.PopupMenu</item>
+        
+        <!-- Preference styles -->
+        <item name="preferenceScreenStyle">@android:style/Preference.PreferenceScreen</item>
+        <item name="preferenceCategoryStyle">@android:style/Preference.Category</item>
+        <item name="preferenceStyle">@android:style/Preference</item>
+        <item name="preferenceInformationStyle">@android:style/Preference.Information</item>
+        <item name="checkBoxPreferenceStyle">@android:style/Preference.CheckBoxPreference</item>
+        <item name="yesNoPreferenceStyle">@android:style/Preference.DialogPreference.YesNoPreference</item>
+        <item name="dialogPreferenceStyle">@android:style/Preference.DialogPreference</item>
+        <item name="editTextPreferenceStyle">@android:style/Preference.DialogPreference.EditTextPreference</item>
+        <item name="ringtonePreferenceStyle">@android:style/Preference.RingtonePreference</item>
+        <item name="preferenceLayoutChild">@android:layout/preference_child</item>
+
+        <!-- Search widget styles -->
+        <item name="searchWidgetCorpusItemBackground">@android:color/search_widget_corpus_item_background</item>
+
+        <!-- Action bar styles -->
+        <item name="actionDropDownStyle">@android:style/Widget.Holo.Light.Spinner.DropDown.ActionBar</item>
+        <item name="actionButtonStyle">@android:style/Widget.Holo.Light.ActionButton</item>
+        <item name="actionOverflowButtonStyle">@android:style/Widget.Holo.Light.ActionButton.Overflow</item>
+        <item name="actionModeBackground">@android:drawable/cab_holo_light</item>
+        <item name="actionModeCloseDrawable">@android:drawable/cab_ic_close_holo</item>
+        <item name="actionBarTabStyle">@style/Widget.Holo.Light.ActionBarView_TabView</item>
+        <item name="actionBarTabBarStyle">@style/Widget.Holo.Light.ActionBarView_TabBar</item>
+        <item name="actionBarTabTextStyle">@style/Widget.Holo.Light.ActionBarView_TabText</item>
+        <item name="actionModeStyle">@style/Widget.Holo.Light.ActionMode</item>
+        <item name="actionModeCloseButtonStyle">@style/Widget.Holo.Light.ActionButton.CloseMode</item>
+        <item name="actionBarStyle">@android:style/Widget.Holo.Light.ActionBar</item>
+        <item name="actionBarSize">56dip</item>
+
+        <item name="dividerVertical">@drawable/divider_vertical_holo_light</item>
+        <item name="buttonGroupStyle">@android:style/Widget.Holo.Light.ButtonGroup</item>
+
+        <!-- SearchView attributes -->
+        <item name="searchDropdownBackground">@android:drawable/search_dropdown_light</item>
+    </style>
+
+    <!-- Development legacy name; if you're using this, switch. -->
+    <style name="Theme.Light.Holo" parent="Theme.Holo.Light">
+    </style>
+    
+    <!-- Variant of the holographic (dark) theme with no action bar. -->
+    <style name="Theme.Holo.NoActionBar">
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+    
+    <!-- Variant of the holographic (dark) theme that has no title bar and fills
+         the entire screen -->
+    <style name="Theme.Holo.NoActionBar.Fullscreen">
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+
+    <!-- Variant of the holographic light theme with no action bar -->
+    <style name="Theme.Light.Holo.NoActionBar">
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
+    <!-- Variant of the holographic light theme that has no title bar and fills
+         the entire screen -->
+    <style name="Theme.Light.Holo.NoActionBar.Fullscreen">
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+ 
+    <!-- Dialog themes for Holo -->
+
+    <!-- Holo theme for dialog windows and activities, which is used by the
+         {@link android.app.Dialog} class.  This changes the window to be
+         floating (not fill the entire screen), and puts a frame around its
+         contents.  You can set this theme on an activity if you would like to
+         make an activity that looks like a Dialog.
+         This is the default Dialog theme for applications targeting Honeycomb
+         or newer. -->
+    <style name="Theme.Holo.Dialog">
+        <item name="android:windowFrame">@null</item>
+        <item name="android:windowTitleStyle">@android:style/DialogWindowTitle.Holo</item>
+        <item name="android:windowBackground">@android:drawable/dialog_full_holo_dark</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowAnimationStyle">@android:style/Animation.Holo.Dialog</item>
+        <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowActionModeOverlay">true</item>
+
+        <item name="android:colorBackgroundCacheHint">@null</item>
+
+        <item name="android:groupButtonBackground">?android:attr/selectableItemBackground</item>
+        
+        <item name="textAppearance">@android:style/TextAppearance.Holo</item>
+        <item name="textAppearanceInverse">@android:style/TextAppearance.Holo.Inverse</item>
+    </style>
+
+    <!-- Variation of Theme.Holo.Dialog that does not include a frame (or background).
+         The view hierarchy of the dialog is responsible for drawing all of
+         its pixels. -->
+    <style name="Theme.Holo.Dialog.NoFrame">
+        <item name="windowBackground">@android:color/transparent</item>
+        <item name="android:windowFrame">@null</item>
+        <item name="windowContentOverlay">@null</item>
+        <item name="android:windowAnimationStyle">@null</item>
+        <item name="android:backgroundDimEnabled">false</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
+    <!-- Holo theme for alert dialog windows, which is used by the
+         {@link android.app.AlertDialog} class.  This is basically a dialog
+         but sets the background to empty so it can do two-tone backgrounds.
+         For applications targeting Honeycomb or newer, this is the default
+         AlertDialog theme. -->
+    <style name="Theme.Holo.Dialog.Alert">
+        <item name="windowBackground">@android:color/transparent</item>
+        <item name="windowTitleStyle">@android:style/DialogWindowTitle.Holo</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
+    <!-- Light holo dialog themes -->
+
+    <!-- Holo light theme for dialog windows and activities, which is used by the
+         {@link android.app.Dialog} class.  This changes the window to be
+         floating (not fill the entire screen), and puts a frame around its
+         contents.  You can set this theme on an activity if you would like to
+         make an activity that looks like a Dialog.
+         This is the default Dialog theme for applications targeting Honeycomb
+         or newer. -->
+    <style name="Theme.Holo.Light.Dialog">
+        <item name="android:windowFrame">@null</item>
+        <item name="android:windowTitleStyle">@android:style/DialogWindowTitle.Holo.Light</item>
+        <item name="android:windowBackground">@android:drawable/dialog_full_holo_light</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowAnimationStyle">@android:style/Animation.Holo.Dialog</item>
+        <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
+        <item name="android:windowActionBar">false</item>
+
+        <item name="android:colorBackgroundCacheHint">@null</item>
+
+        <item name="android:groupButtonBackground">?android:attr/selectableItemBackground</item>
+
+        <item name="textAppearance">@android:style/TextAppearance.Holo.Light</item>
+        <item name="textAppearanceInverse">@android:style/TextAppearance.Holo.Light.Inverse</item>
+    </style>
+
+    <!-- Holo light theme for alert dialog windows, which is used by the
+         {@link android.app.AlertDialog} class.  This is basically a dialog
+         but sets the background to empty so it can do two-tone backgrounds.
+         For applications targeting Honeycomb or newer, this is the default
+         AlertDialog theme. -->
+    <style name="Theme.Holo.Light.Dialog.Alert">
+        <item name="windowBackground">@android:color/transparent</item>
+        <item name="windowTitleStyle">@android:style/DialogWindowTitle.Holo.Light</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
 </resources>
diff --git a/core/res/res/values/themes_extended.xml b/core/res/res/values/themes_extended.xml
new file mode 100644
index 0000000..064e935
--- /dev/null
+++ b/core/res/res/values/themes_extended.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!--
+    This theme file includes empty stubs for the "Extended" themes. These
+    by default are identical to their parent themes. Device overlays in
+    the build tree should include an overlay for this file with appropriate
+    enhancements for the device. Those defining the device-specific overlay can
+    assume that any activity using an extended theme is also requesting
+    hardware acceleration.
+-->
+<resources>
+    <!-- Variant of the holographic dark theme that includes extra decorations
+         appropriate for when hardware acceleration is in use. -->
+    <style name="Theme.Holo.Extended">
+    </style>
+
+    <!-- Variant of the holographic light theme that includes extra decorations
+         appropriate for when hardware acceleration is in use. -->
+    <style name="Theme.Holo.Light.Extended">
+    </style>
+</resources>
diff --git a/core/res/res/xml-land/password_kbd_qwerty.xml b/core/res/res/xml-land/password_kbd_qwerty.xml
index 700c527..fd8bd49 100755
--- a/core/res/res/xml-land/password_kbd_qwerty.xml
+++ b/core/res/res/xml-land/password_kbd_qwerty.xml
@@ -22,7 +22,7 @@
     android:keyWidth="10%p"
     android:horizontalGap="0px"
     android:verticalGap="0px"
-    android:keyHeight="@dimen/password_keyboard_key_height"
+    android:keyHeight="@dimen/password_keyboard_key_height_alpha"
     >
 
     <Row>
diff --git a/core/res/res/xml-land/password_kbd_qwerty_shifted.xml b/core/res/res/xml-land/password_kbd_qwerty_shifted.xml
index 1e37b6c..9ff6fd7 100755
--- a/core/res/res/xml-land/password_kbd_qwerty_shifted.xml
+++ b/core/res/res/xml-land/password_kbd_qwerty_shifted.xml
@@ -22,7 +22,7 @@
     android:keyWidth="10%p"
     android:horizontalGap="0px"
     android:verticalGap="0px"
-    android:keyHeight="@dimen/password_keyboard_key_height"
+    android:keyHeight="@dimen/password_keyboard_key_height_alpha"
     >
 
     <Row>
diff --git a/core/res/res/xml-mdpi/password_kbd_qwerty.xml b/core/res/res/xml-mdpi/password_kbd_qwerty.xml
index bae1b42..82a7c75 100755
--- a/core/res/res/xml-mdpi/password_kbd_qwerty.xml
+++ b/core/res/res/xml-mdpi/password_kbd_qwerty.xml
@@ -22,7 +22,7 @@
     android:keyWidth="10%p"
     android:horizontalGap="0px"
     android:verticalGap="0px"
-    android:keyHeight="@dimen/password_keyboard_key_height"
+    android:keyHeight="@dimen/password_keyboard_key_height_alpha"
     >
 
     <Row>
diff --git a/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml b/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml
index 612df9c..9fff3cc 100755
--- a/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml
+++ b/core/res/res/xml-mdpi/password_kbd_qwerty_shifted.xml
@@ -22,7 +22,7 @@
     android:keyWidth="10%p"
     android:horizontalGap="0px"
     android:verticalGap="0px"
-    android:keyHeight="@dimen/password_keyboard_key_height"
+    android:keyHeight="@dimen/password_keyboard_key_height_alpha"
     >
 
     <Row>
diff --git a/core/res/res/xml-xlarge/password_kbd_qwerty.xml b/core/res/res/xml-xlarge/password_kbd_qwerty.xml
new file mode 100755
index 0000000..0a35040
--- /dev/null
+++ b/core/res/res/xml-xlarge/password_kbd_qwerty.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height_alpha"
+    >
+
+    <Row android:rowEdgeFlags="top">
+        <Key android:keyLabel="1" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="2"/>
+        <Key android:keyLabel="3"/>
+        <Key android:keyLabel="4"/>
+        <Key android:keyLabel="5"/>
+        <Key android:keyLabel="6"/>
+        <Key android:keyLabel="7"/>
+        <Key android:keyLabel="8"/>
+        <Key android:keyLabel="9"/>
+        <Key android:keyLabel="0" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:keyLabel="q" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="w"/>
+        <Key android:keyLabel="e"/>
+        <Key android:keyLabel="r"/>
+        <Key android:keyLabel="t"/>
+        <Key android:keyLabel="y"/>
+        <Key android:keyLabel="u"/>
+        <Key android:keyLabel="i"/>
+        <Key android:keyLabel="o"/>
+        <Key android:keyLabel="p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:keyLabel="a" android:horizontalGap="5%p"
+            android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="s"/>
+        <Key android:keyLabel="d"/>
+        <Key android:keyLabel="f"/>
+        <Key android:keyLabel="g"/>
+        <Key android:keyLabel="h"/>
+        <Key android:keyLabel="j"/>
+        <Key android:keyLabel="k"/>
+        <Key android:keyLabel="l" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+            android:keyWidth="15%p" android:isModifier="true"
+            android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+            android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="z"/>
+        <Key android:keyLabel="x"/>
+        <Key android:keyLabel="c"/>
+        <Key android:keyLabel="v"/>
+        <Key android:keyLabel="b"/>
+        <Key android:keyLabel="n"/>
+        <Key android:keyLabel="m"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+            android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key"
+            android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="," />
+        <Key android:keyLabel="-" />
+        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+            android:iconPreview="@drawable/sym_keyboard_feedback_space"
+            android:keyWidth="20%p"/>
+        <Key android:keyLabel="=" />
+        <Key android:keyLabel="."
+            android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok"
+            android:iconPreview="@drawable/sym_keyboard_feedback_ok"
+            android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+</Keyboard>
+
diff --git a/core/res/res/xml-xlarge/password_kbd_qwerty_shifted.xml b/core/res/res/xml-xlarge/password_kbd_qwerty_shifted.xml
new file mode 100755
index 0000000..9e9db81
--- /dev/null
+++ b/core/res/res/xml-xlarge/password_kbd_qwerty_shifted.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/password_keyboard_key_height_alpha"
+    >
+
+    <Row android:rowEdgeFlags="top">
+        <Key android:keyLabel="\@" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="\#"/>
+        <Key android:keyLabel="$"/>
+        <Key android:keyLabel="%"/>
+        <Key android:keyLabel="&amp;"/>
+        <Key android:keyLabel="*"/>
+        <Key android:keyLabel="-"/>
+        <Key android:keyLabel="+"/>
+        <Key android:keyLabel="("/>
+        <Key android:keyLabel=")" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:keyLabel="q" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="w"/>
+        <Key android:keyLabel="e"/>
+        <Key android:keyLabel="r"/>
+        <Key android:keyLabel="t"/>
+        <Key android:keyLabel="y"/>
+        <Key android:keyLabel="u"/>
+        <Key android:keyLabel="i"/>
+        <Key android:keyLabel="o"/>
+        <Key android:keyLabel="p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:keyLabel="a" android:horizontalGap="5%p"
+            android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="s"/>
+        <Key android:keyLabel="d"/>
+        <Key android:keyLabel="f"/>
+        <Key android:keyLabel="g"/>
+        <Key android:keyLabel="h"/>
+        <Key android:keyLabel="j"/>
+        <Key android:keyLabel="k"/>
+        <Key android:keyLabel="l" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+            android:keyWidth="15%p" android:isModifier="true"
+            android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+            android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="z"/>
+        <Key android:keyLabel="x"/>
+        <Key android:keyLabel="c"/>
+        <Key android:keyLabel="v"/>
+        <Key android:keyLabel="b"/>
+        <Key android:keyLabel="n"/>
+        <Key android:keyLabel="m"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+            android:keyWidth="15%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+            android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/password_keyboard_label_symbol_key"
+            android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="," />
+        <Key android:keyLabel="_" />
+        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+            android:iconPreview="@drawable/sym_keyboard_feedback_space"
+            android:keyWidth="20%p"/>
+        <Key android:keyLabel="+" />
+        <Key android:keyLabel="."/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok"
+            android:iconPreview="@drawable/sym_keyboard_feedback_ok"
+            android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+</Keyboard>
+
diff --git a/core/res/res/xml/password_kbd_extension.xml b/core/res/res/xml/password_kbd_extension.xml
index 28b7efe..f3fa57b 100755
--- a/core/res/res/xml/password_kbd_extension.xml
+++ b/core/res/res/xml/password_kbd_extension.xml
@@ -22,7 +22,7 @@
     android:keyWidth="10%p"
     android:horizontalGap="0px"
     android:verticalGap="0px"
-    android:keyHeight="@dimen/password_keyboard_key_height"
+    android:keyHeight="@dimen/password_keyboard_key_height_alpha"
     >
 
     <Row android:rowEdgeFlags="top">
diff --git a/core/res/res/xml/password_kbd_numeric.xml b/core/res/res/xml/password_kbd_numeric.xml
index e3f1612..2270b8a 100755
--- a/core/res/res/xml/password_kbd_numeric.xml
+++ b/core/res/res/xml/password_kbd_numeric.xml
@@ -19,35 +19,41 @@
 -->
 <Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
     android:keyWidth="33.33%p"
-    android:horizontalGap="2px"
     android:verticalGap="0px"
-    android:keyHeight="@dimen/password_keyboard_key_height"
+    android:keyHeight="@dimen/password_keyboard_key_height_numeric"
     >
 
-    <Row>
-        <Key android:codes="49" android:keyIcon="@drawable/sym_keyboard_num1" android:keyEdgeFlags="left"/>
+    <Row android:rowEdgeFlags="top">
+        <Key android:codes="49" android:keyIcon="@drawable/sym_keyboard_num1"
+             android:keyEdgeFlags="left"/>
         <Key android:codes="50" android:keyIcon="@drawable/sym_keyboard_num2"/>
-        <Key android:codes="51" android:keyIcon="@drawable/sym_keyboard_num3"/>
+        <Key android:codes="51" android:keyIcon="@drawable/sym_keyboard_num3"
+             android:keyEdgeFlags="right"/>
     </Row>
 
     <Row>
-        <Key android:codes="52" android:keyIcon="@drawable/sym_keyboard_num4" android:keyEdgeFlags="left"/>
+        <Key android:codes="52" android:keyIcon="@drawable/sym_keyboard_num4"
+             android:keyEdgeFlags="left"/>
         <Key android:codes="53" android:keyIcon="@drawable/sym_keyboard_num5"/>
-        <Key android:codes="54" android:keyIcon="@drawable/sym_keyboard_num6"/>
+        <Key android:codes="54" android:keyIcon="@drawable/sym_keyboard_num6"
+             android:keyEdgeFlags="right"/>
     </Row>
 
     <Row>
-        <Key android:codes="55" android:keyIcon="@drawable/sym_keyboard_num7" android:keyEdgeFlags="left"/>
+        <Key android:codes="55" android:keyIcon="@drawable/sym_keyboard_num7"
+             android:keyEdgeFlags="left"/>
         <Key android:codes="56" android:keyIcon="@drawable/sym_keyboard_num8"/>
-        <Key android:codes="57" android:keyIcon="@drawable/sym_keyboard_num9"/>
+        <Key android:codes="57" android:keyIcon="@drawable/sym_keyboard_num9"
+             android:keyEdgeFlags="right"/>
     </Row>
 
     <Row android:rowEdgeFlags="bottom">
-        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_ok"
+             android:keyEdgeFlags="left"/>
         <Key android:codes="48" android:keyIcon="@drawable/sym_keyboard_num0_no_plus"/>
         <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
-            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
-            android:isRepeatable="true" android:keyEdgeFlags="right"/>
+             android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+             android:isRepeatable="true" android:keyEdgeFlags="right"/>
     </Row>
 
 </Keyboard>
diff --git a/core/res/res/xml/password_kbd_popup_template.xml b/core/res/res/xml/password_kbd_popup_template.xml
index 5ddfd3e..9b853e2 100644
--- a/core/res/res/xml/password_kbd_popup_template.xml
+++ b/core/res/res/xml/password_kbd_popup_template.xml
@@ -22,6 +22,6 @@
     android:keyWidth="10%p"
     android:horizontalGap="0px"
     android:verticalGap="0px"
-    android:keyHeight="@dimen/password_keyboard_key_height"
+    android:keyHeight="@dimen/password_keyboard_key_height_alpha"
     >
 </Keyboard>
diff --git a/core/res/res/xml/password_kbd_qwerty.xml b/core/res/res/xml/password_kbd_qwerty.xml
index 5fa9b8a..0a35040 100755
--- a/core/res/res/xml/password_kbd_qwerty.xml
+++ b/core/res/res/xml/password_kbd_qwerty.xml
@@ -22,7 +22,7 @@
     android:keyWidth="10%p"
     android:horizontalGap="0px"
     android:verticalGap="0px"
-    android:keyHeight="@dimen/password_keyboard_key_height"
+    android:keyHeight="@dimen/password_keyboard_key_height_alpha"
     >
 
     <Row android:rowEdgeFlags="top">
diff --git a/core/res/res/xml/password_kbd_qwerty_shifted.xml b/core/res/res/xml/password_kbd_qwerty_shifted.xml
index e491aff..9e9db81 100755
--- a/core/res/res/xml/password_kbd_qwerty_shifted.xml
+++ b/core/res/res/xml/password_kbd_qwerty_shifted.xml
@@ -22,7 +22,7 @@
     android:keyWidth="10%p"
     android:horizontalGap="0px"
     android:verticalGap="0px"
-    android:keyHeight="@dimen/password_keyboard_key_height"
+    android:keyHeight="@dimen/password_keyboard_key_height_alpha"
     >
 
     <Row android:rowEdgeFlags="top">
diff --git a/core/res/res/xml/password_kbd_symbols.xml b/core/res/res/xml/password_kbd_symbols.xml
index 14a7ec8..9a94930 100755
--- a/core/res/res/xml/password_kbd_symbols.xml
+++ b/core/res/res/xml/password_kbd_symbols.xml
@@ -22,10 +22,10 @@
     android:keyWidth="10%p"
     android:horizontalGap="0px"
     android:verticalGap="0px"
-    android:keyHeight="@dimen/password_keyboard_key_height"
+    android:keyHeight="@dimen/password_keyboard_key_height_alpha"
     >
 
-    <Row>
+    <Row android:rowEdgeFlags="top">
         <Key android:keyLabel="1" android:keyEdgeFlags="left"/>
         <Key android:keyLabel="2"/>
         <Key android:keyLabel="3"/>
diff --git a/core/res/res/xml/password_kbd_symbols_shift.xml b/core/res/res/xml/password_kbd_symbols_shift.xml
index 4b84f4b..a972eb2 100755
--- a/core/res/res/xml/password_kbd_symbols_shift.xml
+++ b/core/res/res/xml/password_kbd_symbols_shift.xml
@@ -22,10 +22,10 @@
     android:keyWidth="10%p"
     android:horizontalGap="0px"
     android:verticalGap="0px"
-    android:keyHeight="@dimen/password_keyboard_key_height"
+    android:keyHeight="@dimen/password_keyboard_key_height_alpha"
     >
 
-    <Row>
+    <Row android:rowEdgeFlags="top">
         <Key android:keyLabel="~" android:keyEdgeFlags="left"/>
         <Key android:keyLabel="`"/>
         <Key android:keyLabel="|"/>
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java
index 6adfc74..3a78f26 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java
@@ -20,6 +20,7 @@
 import android.test.InstrumentationTestRunner;
 import android.test.InstrumentationTestSuite;
 import android.util.Log;
+import com.android.connectivitymanagertest.unit.WifiClientTest;
 import com.android.connectivitymanagertest.unit.WifiSoftAPTest;
 
 import junit.framework.TestSuite;
@@ -35,6 +36,7 @@
     @Override
     public TestSuite getAllTests() {
         TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(WifiClientTest.class);
         suite.addTestSuite(WifiSoftAPTest.class);
         return suite;
     }
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
new file mode 100644
index 0000000..6717bda
--- /dev/null
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.connectivitymanagertest.unit;
+
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.Context;
+import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.Message;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiConfiguration.Status;
+
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.AndroidTestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.util.Log;
+
+/**
+ * Test wifi client
+ */
+public class WifiClientTest extends AndroidTestCase {
+
+    private WifiManager mWifiManager;
+    private final String TAG = "WifiClientTest";
+
+    //10s delay for turning on wifi
+    private static final int DELAY = 10000;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        mWifiManager.setWifiEnabled(true);
+        assertNotNull(mWifiManager);
+    }
+
+    private void sleepAfterWifiEnable() {
+        try {
+            Thread.sleep(DELAY);
+        } catch (Exception e) {
+            fail("Sleep timeout " + e);
+        }
+    }
+
+    // Test case 1: add/remove a open network
+    @LargeTest
+    public void testAddRemoveNetwork() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = "\"TestSSID1\"";
+        config.allowedKeyManagement.set(KeyMgmt.NONE);
+
+        //add
+        int netId = mWifiManager.addNetwork(config);
+        assertTrue(netId != -1);
+
+        //check config list
+        List<WifiConfiguration> configList = mWifiManager.getConfiguredNetworks();
+        boolean found = false;
+        for (WifiConfiguration c : configList) {
+            if (c.networkId == netId) {
+                found = true;
+            }
+        }
+        assertTrue(found);
+
+        //remove
+        boolean ret = mWifiManager.removeNetwork(netId);
+        assertTrue(ret);
+
+        //check config list
+        configList = mWifiManager.getConfiguredNetworks();
+        found = false;
+        for (WifiConfiguration c : configList) {
+            if (c.networkId == netId) {
+                found = true;
+            }
+        }
+
+        assertFalse(found);
+    }
+
+    // Test case 2: enable/disable a open network
+    @LargeTest
+    public void testEnableDisableNetwork() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = "\"TestSSID2\"";
+        config.allowedKeyManagement.set(KeyMgmt.NONE);
+
+        //add
+        int netId = mWifiManager.addNetwork(config);
+        assertTrue(netId != -1);
+
+        //enable network and disable others
+        boolean ret = mWifiManager.enableNetwork(netId, true);
+        assertTrue(ret);
+
+        //check config list
+        List<WifiConfiguration> configList = mWifiManager.getConfiguredNetworks();
+        for (WifiConfiguration c : configList) {
+            if (c.networkId == netId) {
+                assertTrue(c.status == Status.ENABLED);
+            } else {
+                assertFalse(c.status == Status.ENABLED);
+            }
+        }
+
+        //disable network
+        ret = mWifiManager.disableNetwork(netId);
+        assertTrue(ret);
+
+        //check config list
+        configList = mWifiManager.getConfiguredNetworks();
+        for (WifiConfiguration c : configList) {
+            if (c.networkId == netId) {
+                assertTrue(c.status == Status.DISABLED);
+            }
+        }
+    }
+
+    // Test case 3: ping supplicant
+    @LargeTest
+    public void testPingSupplicant() {
+        assertTrue(mWifiManager.pingSupplicant());
+        mWifiManager.setWifiEnabled(false);
+        sleepAfterWifiEnable();
+
+        assertFalse(mWifiManager.pingSupplicant());
+        mWifiManager.setWifiEnabled(true);
+        sleepAfterWifiEnable();
+    }
+
+    // Test case 4: save config
+    @LargeTest
+    public void testSaveConfig() {
+        WifiConfiguration config = new WifiConfiguration();
+        config.SSID = "\"TestSSID3\"";
+        config.allowedKeyManagement.set(KeyMgmt.NONE);
+
+        //add
+        int netId = mWifiManager.addNetwork(config);
+        assertTrue(netId != -1);
+
+        mWifiManager.saveConfiguration();
+
+        //restart wifi
+        mWifiManager.setWifiEnabled(false);
+        mWifiManager.setWifiEnabled(true);
+
+        sleepAfterWifiEnable();
+
+        //check config list
+        List<WifiConfiguration> configList = mWifiManager.getConfiguredNetworks();
+        boolean found = false;
+        for (WifiConfiguration c : configList) {
+            if (c.SSID.equals("TestSSID3")) {
+                found = true;
+            }
+        }
+        assertTrue(found);
+
+        //restore config
+        boolean ret = mWifiManager.removeNetwork(netId);
+        assertTrue(ret);
+        mWifiManager.saveConfiguration();
+    }
+}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 487a00d..b1e38ee 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1023,7 +1023,13 @@
                 <action android:name="android.accessibilityservice.AccessibilityService" />
             </intent-filter>
         </service>
-        
+
+        <service android:name="android.webkit.AccessibilityInjectorTest$MockAccessibilityService">
+            <intent-filter>
+                <action android:name="android.accessibilityservice.AccessibilityService" />
+            </intent-filter>
+        </service>
+
         <activity android:name="android.widget.RadioGroupActivity" android:label="RadioGroupActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/core/tests/coretests/res/raw/v21_backslash.vcf b/core/tests/coretests/res/raw/v21_backslash.vcf
deleted file mode 100644
index bd3002b..0000000
--- a/core/tests/coretests/res/raw/v21_backslash.vcf
+++ /dev/null
@@ -1,5 +0,0 @@
-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
deleted file mode 100644
index de34e16..0000000
--- a/core/tests/coretests/res/raw/v21_complicated.vcf
+++ /dev/null
@@ -1,106 +0,0 @@
-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_im.vcf b/core/tests/coretests/res/raw/v21_im.vcf
deleted file mode 100644
index cc1aabb..0000000
--- a/core/tests/coretests/res/raw/v21_im.vcf
+++ /dev/null
@@ -1,5 +0,0 @@
-BEGIN:VCARD
-VERSION:2.1
-X-ANDROID-CUSTOM:vnd.android.cursor.item/nickname;Nick;1;;;;;;;;;;;;;
-X-GOOGLE-TALK:hhh@gmail.com
-END:VCARD
diff --git a/core/tests/coretests/res/raw/v21_invalid_comment_line.vcf b/core/tests/coretests/res/raw/v21_invalid_comment_line.vcf
deleted file mode 100644
index f910710..0000000
--- a/core/tests/coretests/res/raw/v21_invalid_comment_line.vcf
+++ /dev/null
@@ -1,10 +0,0 @@
-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_invalid_multiple_line.vcf b/core/tests/coretests/res/raw/v21_invalid_multiple_line.vcf
deleted file mode 100644
index 9c81fd5..0000000
--- a/core/tests/coretests/res/raw/v21_invalid_multiple_line.vcf
+++ /dev/null
@@ -1,7 +0,0 @@
-BEGIN:VCARD
-VERSION:2.1
-N:;Omega;;;
-EMAIL;INTERNET:"Omega"
-  <omega@example.com>
-FN:Omega
-END:VCARD
diff --git a/core/tests/coretests/res/raw/v21_japanese_1.vcf b/core/tests/coretests/res/raw/v21_japanese_1.vcf
deleted file mode 100644
index d05e2ff..0000000
--- a/core/tests/coretests/res/raw/v21_japanese_1.vcf
+++ /dev/null
@@ -1,6 +0,0 @@
-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
deleted file mode 100644
index fa54acb..0000000
--- a/core/tests/coretests/res/raw/v21_japanese_2.vcf
+++ /dev/null
@@ -1,10 +0,0 @@
-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
deleted file mode 100644
index ebbb19a..0000000
--- a/core/tests/coretests/res/raw/v21_multiple_entry.vcf
+++ /dev/null
@@ -1,33 +0,0 @@
-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
deleted file mode 100644
index 8ff1190..0000000
--- a/core/tests/coretests/res/raw/v21_org_before_title.vcf
+++ /dev/null
@@ -1,6 +0,0 @@
-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
deleted file mode 100644
index 5105310..0000000
--- a/core/tests/coretests/res/raw/v21_pref_handling.vcf
+++ /dev/null
@@ -1,15 +0,0 @@
-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
deleted file mode 100644
index 6aabb4c..0000000
--- a/core/tests/coretests/res/raw/v21_simple_1.vcf
+++ /dev/null
@@ -1,3 +0,0 @@
-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
deleted file mode 100644
index f0d5ab5..0000000
--- a/core/tests/coretests/res/raw/v21_simple_2.vcf
+++ /dev/null
@@ -1,3 +0,0 @@
-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
deleted file mode 100644
index beddabb..0000000
--- a/core/tests/coretests/res/raw/v21_simple_3.vcf
+++ /dev/null
@@ -1,4 +0,0 @@
-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
deleted file mode 100644
index 9fdc738..0000000
--- a/core/tests/coretests/res/raw/v21_title_before_org.vcf
+++ /dev/null
@@ -1,6 +0,0 @@
-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
deleted file mode 100644
index f380d0d..0000000
--- a/core/tests/coretests/res/raw/v21_winmo_65.vcf
+++ /dev/null
@@ -1,10 +0,0 @@
-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
deleted file mode 100644
index f1baf88..0000000
--- a/core/tests/coretests/res/raw/v30_comma_separated.vcf
+++ /dev/null
@@ -1,5 +0,0 @@
-BEGIN:VCARD

-VERSION:3.0

-N;TYPE=PREF,HOME:F;G;M;;

-TEL;TYPE="COMMA,SEPARATED:INSIDE.DQUOTE",PREF:1

-END:VCARD

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

-VERSION:3.0

-N:F;G;M;;

-TEL;TYPE="费":1

-END:VCARD

diff --git a/core/tests/coretests/res/raw/v30_pager.vcf b/core/tests/coretests/res/raw/v30_pager.vcf
deleted file mode 100644
index 98a7f20..0000000
--- a/core/tests/coretests/res/raw/v30_pager.vcf
+++ /dev/null
@@ -1,5 +0,0 @@
-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
deleted file mode 100644
index 418661f..0000000
--- a/core/tests/coretests/res/raw/v30_simple.vcf
+++ /dev/null
@@ -1,13 +0,0 @@
-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/raw/v40_sort_as.vcf b/core/tests/coretests/res/raw/v40_sort_as.vcf
deleted file mode 100644
index 6f6bc3b..0000000
--- a/core/tests/coretests/res/raw/v40_sort_as.vcf
+++ /dev/null
@@ -1,6 +0,0 @@
-BEGIN:VCARD

-VERSION:4.0

-FN:安藤 ロイド

-N;SORT-AS="あんどう;ろいど":安藤;ロイド;;;

-ORG;TYPE=WORK;SORT-AS="ぐーぐる;けんさくぶもん":グーグル;検索部門

-END:VCARD

diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityTestService.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityTestService.java
deleted file mode 100644
index 2a51eea..0000000
--- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityTestService.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accessibilityservice;
-
-import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.app.Notification;
-import android.util.Log;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-
-import java.util.Timer;
-import java.util.TimerTask;
-
-/**
- * This class text the accessibility framework end to end.
- * <p>
- * Note: Since accessibility is provided by {@link AccessibilityService}s we create one,
- * and it generates an event and an interruption dispatching them through the
- * {@link AccessibilityManager}. We verify the received result. To trigger the test
- * go to Settings->Accessibility and select the enable accessibility check and then
- * select the check for this service (same name as the class).
- */
-public class AccessibilityTestService extends AccessibilityService {
-
-    private static final String LOG_TAG = "AccessibilityTestService";
-
-    private static final String CLASS_NAME = "foo.bar.baz.Test";
-    private static final String PACKAGE_NAME = "foo.bar.baz";
-    private static final String TEXT = "Some stuff";
-    private static final String BEFORE_TEXT = "Some other stuff";
-
-    private static final String CONTENT_DESCRIPTION = "Content description";
-
-    private static final int ITEM_COUNT = 10;
-    private static final int CURRENT_ITEM_INDEX = 1;
-    private static final int INTERRUPT_INVOCATION_TYPE = 0x00000200;
-
-    private static final int FROM_INDEX = 1;
-    private static final int ADDED_COUNT = 2;
-    private static final int REMOVED_COUNT = 1;
-
-    private static final int NOTIFICATION_TIMEOUT_MILLIS = 80;
-
-    private int mReceivedResult;
-
-    private Timer mTimer = new Timer();
-
-    @Override
-    public void onServiceConnected() {
-        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
-        info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
-        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE;
-        info.notificationTimeout = NOTIFICATION_TIMEOUT_MILLIS;
-        info.flags &= AccessibilityServiceInfo.DEFAULT;
-        setServiceInfo(info);
-
-        // we need to wait until the system picks our configuration
-        // otherwise it will not notify us
-        mTimer.schedule(new TimerTask() {
-            @Override
-            public void run() {
-                try {
-                    testAccessibilityEventDispatching();
-                    testInterrupt();
-                } catch (Exception e) {
-                    Log.e(LOG_TAG, "Error in testing Accessibility feature", e);
-                }
-            }
-        }, 1000);
-    }
-
-    /**
-     * Check here if the event we received is actually the one we sent.
-     */
-    @Override
-    public void onAccessibilityEvent(AccessibilityEvent event) {
-        assert(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED == event.getEventType());
-        assert(event != null);
-        assert(event.getEventTime() > 0);
-        assert(CLASS_NAME.equals(event.getClassName()));
-        assert(PACKAGE_NAME.equals(event.getPackageName()));
-        assert(1 == event.getText().size());
-        assert(TEXT.equals(event.getText().get(0)));
-        assert(BEFORE_TEXT.equals(event.getBeforeText()));
-        assert(event.isChecked());
-        assert(CONTENT_DESCRIPTION.equals(event.getContentDescription()));
-        assert(ITEM_COUNT == event.getItemCount());
-        assert(CURRENT_ITEM_INDEX == event.getCurrentItemIndex());
-        assert(event.isEnabled());
-        assert(event.isPassword());
-        assert(FROM_INDEX == event.getFromIndex());
-        assert(ADDED_COUNT == event.getAddedCount());
-        assert(REMOVED_COUNT == event.getRemovedCount());
-        assert(event.getParcelableData() != null);
-        assert(1 == ((Notification) event.getParcelableData()).icon);
-
-        // set the type of the receved request
-        mReceivedResult = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED;
-    }
-
-    /**
-     * Set a flag that we received the interrupt request.
-     */
-    @Override
-    public void onInterrupt() {
-
-        // set the type of the receved request
-        mReceivedResult = INTERRUPT_INVOCATION_TYPE;
-    }
-
-    /**
-     * If an {@link AccessibilityEvent} is sent and received correctly.
-     */
-   public void testAccessibilityEventDispatching() throws Exception {
-       AccessibilityEvent event =
-           AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
-
-       assert(event != null);
-       event.setClassName(CLASS_NAME);
-       event.setPackageName(PACKAGE_NAME);
-       event.getText().add(TEXT);
-       event.setBeforeText(BEFORE_TEXT);
-       event.setChecked(true);
-       event.setContentDescription(CONTENT_DESCRIPTION);
-       event.setItemCount(ITEM_COUNT);
-       event.setCurrentItemIndex(CURRENT_ITEM_INDEX);
-       event.setEnabled(true);
-       event.setPassword(true);
-       event.setFromIndex(FROM_INDEX);
-       event.setAddedCount(ADDED_COUNT);
-       event.setRemovedCount(REMOVED_COUNT);
-       event.setParcelableData(new Notification(1, "Foo", 1234));
-
-       AccessibilityManager.getInstance(this).sendAccessibilityEvent(event);
-
-       assert(mReceivedResult == event.getEventType());
-
-       Log.i(LOG_TAG, "AccessibilityTestService#testAccessibilityEventDispatching: Success");
-   }
-
-   /**
-    * If accessibility feedback interruption is triggered and received correctly.
-    */
-   public void testInterrupt() throws Exception {
-       AccessibilityManager.getInstance(this).interrupt();
-
-       assert(INTERRUPT_INVOCATION_TYPE == mReceivedResult);
-
-       Log.i(LOG_TAG, "AccessibilityTestService#testInterrupt: Success");
-   }
-}
-
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
index d30c5e8..f019599 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
@@ -42,6 +42,10 @@
 
     public void testEnable() {
         int iterations = BluetoothTestRunner.sEnableIterations;
+        if (iterations == 0) {
+            return;
+        }
+
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
 
         for (int i = 0; i < iterations; i++) {
@@ -53,6 +57,10 @@
 
     public void testDiscoverable() {
         int iterations = BluetoothTestRunner.sDiscoverableIterations;
+        if (iterations == 0) {
+            return;
+        }
+
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         mTestUtils.enable(adapter);
 
@@ -67,6 +75,10 @@
 
     public void testScan() {
         int iterations = BluetoothTestRunner.sScanIterations;
+        if (iterations == 0) {
+            return;
+        }
+
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         mTestUtils.enable(adapter);
 
@@ -81,6 +93,10 @@
 
     public void testPair() {
         int iterations = BluetoothTestRunner.sPairIterations;
+        if (iterations == 0) {
+            return;
+        }
+
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sPairAddress);
         mTestUtils.enable(adapter);
@@ -94,34 +110,22 @@
         mTestUtils.disable(adapter);
     }
 
-    public void testAcceptPair() {
-        int iterations = BluetoothTestRunner.sPairIterations;
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sPairAddress);
-        mTestUtils.enable(adapter);
-
-        for (int i = 0; i < iterations; i++) {
-            mTestUtils.writeOutput("acceptPair iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.acceptPair(adapter, device, BluetoothTestRunner.sPairPasskey,
-                    BluetoothTestRunner.sPairPin);
-            mTestUtils.unpair(adapter, device);
-        }
-        mTestUtils.disable(adapter);
-    }
-
     public void testConnectA2dp() {
         int iterations = BluetoothTestRunner.sConnectA2dpIterations;
+        if (iterations == 0) {
+            return;
+        }
+
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sA2dpAddress);
-
         mTestUtils.enable(adapter);
         mTestUtils.pair(adapter, device, BluetoothTestRunner.sPairPasskey,
                 BluetoothTestRunner.sPairPin);
 
         for (int i = 0; i < iterations; i++) {
             mTestUtils.writeOutput("connectA2dp iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.connectA2dp(adapter, device);
-            mTestUtils.disconnectA2dp(adapter, device);
+            mTestUtils.connectProfile(adapter, device, BluetoothProfile.A2DP);
+            mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.A2DP);
         }
 
         // TODO: Unpair from device if device can accept pairing after unpairing
@@ -130,17 +134,20 @@
 
     public void testConnectHeadset() {
         int iterations = BluetoothTestRunner.sConnectHeadsetIterations;
+        if (iterations == 0) {
+            return;
+        }
+
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         BluetoothDevice device = adapter.getRemoteDevice(BluetoothTestRunner.sHeadsetAddress);
-
         mTestUtils.enable(adapter);
         mTestUtils.pair(adapter, device, BluetoothTestRunner.sPairPasskey,
                 BluetoothTestRunner.sPairPin);
 
         for (int i = 0; i < iterations; i++) {
             mTestUtils.writeOutput("connectHeadset iteration " + (i + 1) + " of " + iterations);
-            mTestUtils.connectHeadset(adapter, device);
-            mTestUtils.disconnectHeadset(adapter, device);
+            mTestUtils.connectProfile(adapter, device, BluetoothProfile.HEADSET);
+            mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET);
         }
 
         // TODO: Unpair from device if device can accept pairing after unpairing
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
index 050dc30..3e589fc 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
@@ -21,8 +21,11 @@
 import android.os.Bundle;
 import android.test.InstrumentationTestRunner;
 import android.test.InstrumentationTestSuite;
+import android.util.Log;
 
 public class BluetoothTestRunner extends InstrumentationTestRunner {
+    private static final String TAG = "BluetoothTestRunner";
+
     public static int sEnableIterations = 100;
     public static int sDiscoverableIterations = 1000;
     public static int sScanIterations = 1000;
@@ -51,8 +54,6 @@
 
     @Override
     public void onCreate(Bundle arguments) {
-        super.onCreate(arguments);
-
         String val = arguments.getString("enable_iterations");
         if (val != null) {
             try {
@@ -135,5 +136,20 @@
                 // Invalid argument, fall back to default value
             }
         }
+
+        Log.i(TAG, String.format("enable_iterations=%d", sEnableIterations));
+        Log.i(TAG, String.format("discoverable_iterations=%d", sDiscoverableIterations));
+        Log.i(TAG, String.format("scan_iterations=%d", sScanIterations));
+        Log.i(TAG, String.format("pair_iterations=%d", sPairIterations));
+        Log.i(TAG, String.format("connect_a2dp_iterations=%d", sConnectA2dpIterations));
+        Log.i(TAG, String.format("connect_headset_iterations=%d", sConnectHeadsetIterations));
+        Log.i(TAG, String.format("pair_address=%s", sPairAddress));
+        Log.i(TAG, String.format("a2dp_address=%s", sA2dpAddress));
+        Log.i(TAG, String.format("headset_address=%s", sHeadsetAddress));
+        Log.i(TAG, String.format("pair_pin=%s", new String(sPairPin)));
+        Log.i(TAG, String.format("pair_passkey=%d", sPairPasskey));
+
+        // Call onCreate last since we want to set the static variables first.
+        super.onCreate(arguments);
     }
 }
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
index f40d857..328891c 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
@@ -16,7 +16,6 @@
 
 package android.bluetooth;
 
-import android.bluetooth.BluetoothHeadset.ServiceListener;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -71,65 +70,57 @@
     private static final int UNPAIR_TIMEOUT = 20000;
 
     /**
-     * Timeout for {@link BluetoothA2dp#connectSink(BluetoothDevice)} in ms.
+     * Timeout for {@link BluetoothProfile#connect(BluetoothDevice)} in ms.
      */
-    private static final int CONNECT_A2DP_TIMEOUT = 20000;
+    private static final int CONNECT_PROFILE_TIMEOUT = 20000;
 
     /**
-     * Timeout for {@link BluetoothA2dp#disconnectSink(BluetoothDevice)} in ms.
+     * Timeout for {@link BluetoothProfile#disconnect(BluetoothDevice)} in ms.
      */
-    private static final int DISCONNECT_A2DP_TIMEOUT = 20000;
+    private static final int DISCONNECT_PROFILE_TIMEOUT = 20000;
 
     /**
-     * Timeout for {@link BluetoothHeadset#connectHeadset(BluetoothDevice)} in ms.
+     * Timeout to connect a profile proxy in ms.
      */
-    private static final int CONNECT_HEADSET_TIMEOUT = 20000;
-
-    /**
-     * Timeout for {@link BluetoothHeadset#disconnectHeadset(BluetoothDevice)} in ms.
-     */
-    private static final int DISCONNECT_HEADSET_TIMEOUT = 20000;
+    private static final int CONNECT_PROXY_TIMEOUT = 5000;
 
     /**
      * Time between polls in ms.
      */
     private static final int POLL_TIME = 100;
 
-    private Context mContext;
+    private abstract class FlagReceiver extends BroadcastReceiver {
+        private int mExpectedFlags = 0;
+        private int mFiredFlags = 0;
+        private long mCompletedTime = -1;
 
-    private BufferedWriter mOutputWriter;
+        public FlagReceiver(int expectedFlags) {
+            mExpectedFlags = expectedFlags;
+        }
 
-    private BluetoothA2dp mA2dp;
-
-    private BluetoothHeadset mHeadset;
-
-    private String mOutputFile;
-    private String mTag;
-    private class HeadsetServiceListener implements ServiceListener {
-        private boolean mConnected = false;
-
-        public void onServiceConnected() {
+        public int getFiredFlags() {
             synchronized (this) {
-                mConnected = true;
+                return mFiredFlags;
             }
         }
 
-        public void onServiceDisconnected() {
+        public long getCompletedTime() {
             synchronized (this) {
-                mConnected = false;
+                return mCompletedTime;
             }
         }
 
-        public boolean isConnected() {
+        protected void setFiredFlag(int flag) {
             synchronized (this) {
-                return mConnected;
+                mFiredFlags |= flag;
+                if (mFiredFlags == mExpectedFlags) {
+                    mCompletedTime = System.currentTimeMillis();
+                }
             }
         }
     }
 
-    private HeadsetServiceListener mHeadsetServiceListener = new HeadsetServiceListener();
-
-    private class BluetoothReceiver extends BroadcastReceiver {
+    private class BluetoothReceiver extends FlagReceiver {
         private static final int DISCOVERY_STARTED_FLAG = 1;
         private static final int DISCOVERY_FINISHED_FLAG = 1 << 1;
         private static final int SCAN_MODE_NONE_FLAG = 1 << 2;
@@ -139,148 +130,65 @@
         private static final int STATE_TURNING_ON_FLAG = 1 << 6;
         private static final int STATE_ON_FLAG = 1 << 7;
         private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
-        private static final int PROFILE_A2DP_FLAG = 1 << 9;
-        private static final int PROFILE_HEADSET_FLAG = 1 << 10;
 
-        private static final int A2DP_STATE_DISCONNECTED = 1;
-        private static final int A2DP_STATE_CONNECTING = 1 << 1;
-        private static final int A2DP_STATE_CONNECTED = 1 << 2;
-        private static final int A2DP_STATE_DISCONNECTING = 1 << 3;
-        private static final int A2DP_STATE_PLAYING = 1 << 4;
-
-        private static final int HEADSET_STATE_DISCONNECTED = 1;
-        private static final int HEADSET_STATE_CONNECTING = 1 << 1;
-        private static final int HEADSET_STATE_CONNECTED = 1 << 2;
-
-        private int mFiredFlags = 0;
-        private int mA2dpFiredFlags = 0;
-        private int mHeadsetFiredFlags = 0;
+        public BluetoothReceiver(int expectedFlags) {
+            super(expectedFlags);
+        }
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            synchronized (this) {
-                if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
-                    mFiredFlags |= DISCOVERY_STARTED_FLAG;
-                } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
-                    mFiredFlags |= DISCOVERY_FINISHED_FLAG;
-                } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
-                    int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
-                            BluetoothAdapter.ERROR);
-                    assertNotSame(mode, BluetoothAdapter.ERROR);
-                    switch (mode) {
-                        case BluetoothAdapter.SCAN_MODE_NONE:
-                            mFiredFlags |= SCAN_MODE_NONE_FLAG;
-                            break;
-                        case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
-                            mFiredFlags |= SCAN_MODE_CONNECTABLE_FLAG;
-                            break;
-                        case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
-                            mFiredFlags |= SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
-                            break;
-                    }
-                } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
-                    int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
-                            BluetoothAdapter.ERROR);
-                    assertNotSame(state, BluetoothAdapter.ERROR);
-                    switch (state) {
-                        case BluetoothAdapter.STATE_OFF:
-                            mFiredFlags |= STATE_OFF_FLAG;
-                            break;
-                        case BluetoothAdapter.STATE_TURNING_ON:
-                            mFiredFlags |= STATE_TURNING_ON_FLAG;
-                            break;
-                        case BluetoothAdapter.STATE_ON:
-                            mFiredFlags |= STATE_ON_FLAG;
-                            break;
-                        case BluetoothAdapter.STATE_TURNING_OFF:
-                            mFiredFlags |= STATE_TURNING_OFF_FLAG;
-                            break;
-                    }
-                } else if (BluetoothA2dp.ACTION_SINK_STATE_CHANGED.equals(intent.getAction())) {
-                    mFiredFlags |= PROFILE_A2DP_FLAG;
-                    int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, -1);
-                    assertNotSame(state, -1);
-                    switch (state) {
-                        case BluetoothA2dp.STATE_DISCONNECTED:
-                            mA2dpFiredFlags |= A2DP_STATE_DISCONNECTED;
-                            break;
-                        case BluetoothA2dp.STATE_CONNECTING:
-                            mA2dpFiredFlags |= A2DP_STATE_CONNECTING;
-                            break;
-                        case BluetoothA2dp.STATE_CONNECTED:
-                            mA2dpFiredFlags |= A2DP_STATE_CONNECTED;
-                            break;
-                        case BluetoothA2dp.STATE_DISCONNECTING:
-                            mA2dpFiredFlags |= A2DP_STATE_DISCONNECTING;
-                            break;
-                        case BluetoothA2dp.STATE_PLAYING:
-                            mA2dpFiredFlags |= A2DP_STATE_PLAYING;
-                            break;
-                    }
-                } else if (BluetoothHeadset.ACTION_STATE_CHANGED.equals(intent.getAction())) {
-                    mFiredFlags |= PROFILE_HEADSET_FLAG;
-                    int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                            BluetoothHeadset.STATE_ERROR);
-                    assertNotSame(state, BluetoothHeadset.STATE_ERROR);
-                    switch (state) {
-                        case BluetoothHeadset.STATE_DISCONNECTED:
-                            mHeadsetFiredFlags |= HEADSET_STATE_DISCONNECTED;
-                            break;
-                        case BluetoothHeadset.STATE_CONNECTING:
-                            mHeadsetFiredFlags |= HEADSET_STATE_CONNECTING;
-                            break;
-                        case BluetoothHeadset.STATE_CONNECTED:
-                            mHeadsetFiredFlags |= HEADSET_STATE_CONNECTED;
-                            break;
-                    }
+            Log.i("BT", intent.toString());
+            if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
+                setFiredFlag(DISCOVERY_STARTED_FLAG);
+            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
+                setFiredFlag(DISCOVERY_FINISHED_FLAG);
+            } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
+                int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, -1);
+                assertNotSame(-1, mode);
+                switch (mode) {
+                    case BluetoothAdapter.SCAN_MODE_NONE:
+                        setFiredFlag(SCAN_MODE_NONE_FLAG);
+                        break;
+                    case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
+                        setFiredFlag(SCAN_MODE_CONNECTABLE_FLAG);
+                        break;
+                    case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
+                        setFiredFlag(SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG);
+                        break;
                 }
-            }
-        }
-
-        public int getFiredFlags() {
-            synchronized (this) {
-                return mFiredFlags;
-            }
-        }
-
-        public int getA2dpFiredFlags() {
-            synchronized (this) {
-                return mA2dpFiredFlags;
-            }
-        }
-
-        public int getHeadsetFiredFlags() {
-            synchronized (this) {
-                return mHeadsetFiredFlags;
-            }
-        }
-
-        public void resetFiredFlags() {
-            synchronized (this) {
-                mFiredFlags = 0;
-                mA2dpFiredFlags = 0;
-                mHeadsetFiredFlags = 0;
+            } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+                assertNotSame(-1, state);
+                switch (state) {
+                    case BluetoothAdapter.STATE_OFF:
+                        setFiredFlag(STATE_OFF_FLAG);
+                        break;
+                    case BluetoothAdapter.STATE_TURNING_ON:
+                        setFiredFlag(STATE_TURNING_ON_FLAG);
+                        break;
+                    case BluetoothAdapter.STATE_ON:
+                        setFiredFlag(STATE_ON_FLAG);
+                        break;
+                    case BluetoothAdapter.STATE_TURNING_OFF:
+                        setFiredFlag(STATE_TURNING_OFF_FLAG);
+                        break;
+                }
             }
         }
     }
 
-    private BluetoothReceiver mBluetoothReceiver = new BluetoothReceiver();
-
-    private class PairReceiver extends BroadcastReceiver {
-        private final static int PAIR_FLAG = 1;
-        private static final int PAIR_STATE_BONDED = 1;
-        private static final int PAIR_STATE_BONDING = 1 << 1;
-        private static final int PAIR_STATE_NONE = 1 << 2;
-
-        private int mFiredFlags = 0;
-        private int mPairFiredFlags = 0;
+    private class PairReceiver extends FlagReceiver {
+        private static final int STATE_BONDED_FLAG = 1;
+        private static final int STATE_BONDING_FLAG = 1 << 1;
+        private static final int STATE_NONE_FLAG = 1 << 2;
 
         private BluetoothDevice mDevice;
         private int mPasskey;
         private byte[] mPin;
 
-        public PairReceiver(BluetoothDevice device, int passkey, byte[] pin) {
-            super();
+        public PairReceiver(BluetoothDevice device, int passkey, byte[] pin, int expectedFlags) {
+            super(expectedFlags);
+
             mDevice = device;
             mPasskey = passkey;
             mPin = pin;
@@ -288,72 +196,141 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            synchronized (this) {
-                if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction())
-                        && mDevice.equals(intent.getParcelableExtra(
-                                BluetoothDevice.EXTRA_DEVICE))) {
-                    int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
-                            BluetoothDevice.ERROR);
-                    assertNotSame(type, BluetoothDevice.ERROR);
-                    switch (type) {
-                        case BluetoothDevice.PAIRING_VARIANT_PIN:
-                            mDevice.setPin(mPin);
-                            break;
-                        case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
-                            mDevice.setPasskey(mPasskey);
-                            break;
-                        case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
-                        case BluetoothDevice.PAIRING_VARIANT_CONSENT:
-                            mDevice.setPairingConfirmation(true);
-                            break;
-                        case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
-                            mDevice.setRemoteOutOfBandData();
-                            break;
-                    }
-                } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())
-                        && mDevice.equals(intent.getParcelableExtra(
-                                BluetoothDevice.EXTRA_DEVICE))) {
-                    mFiredFlags |= PAIR_FLAG;
-                    int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
-                            BluetoothDevice.ERROR);
-                    assertNotSame(state, BluetoothDevice.ERROR);
-                    switch (state) {
-                        case BluetoothDevice.BOND_BONDED:
-                            mPairFiredFlags |= PAIR_STATE_BONDED;
-                            break;
-                        case BluetoothDevice.BOND_BONDING:
-                            mPairFiredFlags |= PAIR_STATE_BONDING;
-                            break;
-                        case BluetoothDevice.BOND_NONE:
-                            mPairFiredFlags |= PAIR_STATE_NONE;
-                            break;
-                    }
+            if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) {
+                return;
+            }
+
+            if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(intent.getAction())) {
+                int varient = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, -1);
+                assertNotSame(-1, varient);
+                switch(varient) {
+                    case BluetoothDevice.PAIRING_VARIANT_PIN:
+                        mDevice.setPin(mPin);
+                        break;
+                    case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+                        mDevice.setPasskey(mPasskey);
+                        break;
+                    case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
+                    case BluetoothDevice.PAIRING_VARIANT_CONSENT:
+                        mDevice.setPairingConfirmation(true);
+                        break;
+                    case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
+                        mDevice.setRemoteOutOfBandData();
+                        break;
                 }
-            }
-        }
-
-        public int getFiredFlags() {
-            synchronized (this) {
-                return mFiredFlags;
-            }
-        }
-
-        public int getPairFiredFlags() {
-            synchronized (this) {
-                return mPairFiredFlags;
-            }
-        }
-
-        public void resetFiredFlags() {
-            synchronized (this) {
-                mFiredFlags = 0;
-                mPairFiredFlags = 0;
+            } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
+                int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
+                assertNotSame(-1, state);
+                switch (state) {
+                    case BluetoothDevice.BOND_NONE:
+                        setFiredFlag(STATE_NONE_FLAG);
+                        break;
+                    case BluetoothDevice.BOND_BONDING:
+                        setFiredFlag(STATE_BONDING_FLAG);
+                        break;
+                    case BluetoothDevice.BOND_BONDED:
+                        setFiredFlag(STATE_BONDED_FLAG);
+                        break;
+                }
             }
         }
     }
 
+    private class ConnectProfileReceiver extends FlagReceiver {
+        private static final int STATE_DISCONNECTED_FLAG = 1;
+        private static final int STATE_CONNECTING_FLAG = 1 << 1;
+        private static final int STATE_CONNECTED_FLAG = 1 << 2;
+        private static final int STATE_DISCONNECTING_FLAG = 1 << 3;
+
+        private BluetoothDevice mDevice;
+        private int mProfile;
+        private String mConnectionAction;
+
+        public ConnectProfileReceiver(BluetoothDevice device, int profile, int expectedFlags) {
+            super(expectedFlags);
+
+            mDevice = device;
+            mProfile = profile;
+
+            switch(mProfile) {
+                case BluetoothProfile.A2DP:
+                    mConnectionAction = BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED;
+                    break;
+                case BluetoothProfile.HEADSET:
+                    mConnectionAction = BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED;
+                    break;
+                default:
+                    mConnectionAction = null;
+            }
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+
+            if (mConnectionAction != null && mConnectionAction.equals(intent.getAction())) {
+                if (!mDevice.equals(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE))) {
+                    return;
+                }
+
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+                assertNotSame(-1, state);
+                switch (state) {
+                    case BluetoothProfile.STATE_DISCONNECTED:
+                        setFiredFlag(STATE_DISCONNECTED_FLAG);
+                        break;
+                    case BluetoothProfile.STATE_CONNECTING:
+                        setFiredFlag(STATE_CONNECTING_FLAG);
+                        break;
+                    case BluetoothProfile.STATE_CONNECTED:
+                        setFiredFlag(STATE_CONNECTED_FLAG);
+                        break;
+                    case BluetoothProfile.STATE_DISCONNECTING:
+                        setFiredFlag(STATE_DISCONNECTING_FLAG);
+                        break;
+                }
+            }
+        }
+    }
+
+    private BluetoothProfile.ServiceListener mServiceListener =
+            new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            synchronized (this) {
+                switch (profile) {
+                    case BluetoothProfile.A2DP:
+                        mA2dp = (BluetoothA2dp) proxy;
+                        break;
+                    case BluetoothProfile.HEADSET:
+                        mHeadset = (BluetoothHeadset) proxy;
+                        break;
+                }
+            }
+        }
+
+        public void onServiceDisconnected(int profile) {
+            synchronized (this) {
+                switch (profile) {
+                    case BluetoothProfile.A2DP:
+                        mA2dp = null;
+                        break;
+                    case BluetoothProfile.HEADSET:
+                        mHeadset = null;
+                        break;
+                }
+            }
+        }
+    };
+
     private List<BroadcastReceiver> mReceivers = new ArrayList<BroadcastReceiver>();
 
+    private BufferedWriter mOutputWriter;
+    private String mTag;
+    private String mOutputFile;
+
+    private Context mContext;
+    private BluetoothA2dp mA2dp;
+    private BluetoothHeadset mHeadset;
+
     public BluetoothTestUtils(Context context, String tag) {
         this(context, tag, null);
     }
@@ -374,11 +351,6 @@
                 mOutputWriter = null;
             }
         }
-
-        mA2dp = new BluetoothA2dp(mContext);
-        mHeadset = new BluetoothHeadset(mContext, mHeadsetServiceListener);
-        mBluetoothReceiver = getBluetoothReceiver(mContext);
-        mReceivers.add(mBluetoothReceiver);
     }
 
     public void close() {
@@ -398,24 +370,28 @@
     public void enable(BluetoothAdapter adapter) {
         int mask = (BluetoothReceiver.STATE_TURNING_ON_FLAG | BluetoothReceiver.STATE_ON_FLAG
                 | BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG);
-        mBluetoothReceiver.resetFiredFlags();
+        long start = -1;
+        BluetoothReceiver receiver = getBluetoothReceiver(mask);
 
         int state = adapter.getState();
         switch (state) {
             case BluetoothAdapter.STATE_ON:
                 assertTrue(adapter.isEnabled());
+                removeReceiver(receiver);
                 return;
-            case BluetoothAdapter.STATE_OFF:
-            case BluetoothAdapter.STATE_TURNING_OFF:
-                assertFalse(adapter.isEnabled());
-                assertTrue(adapter.enable());
-                break;
             case BluetoothAdapter.STATE_TURNING_ON:
                 assertFalse(adapter.isEnabled());
                 mask = 0; // Don't check for received intents since we might have missed them.
                 break;
+            case BluetoothAdapter.STATE_OFF:
+            case BluetoothAdapter.STATE_TURNING_OFF:
+                assertFalse(adapter.isEnabled());
+                start = System.currentTimeMillis();
+                assertTrue(adapter.enable());
+                break;
             default:
-                fail("enable() invalid state: state=" + state);
+                removeReceiver(receiver);
+                fail(String.format("enable() invalid state: state=%d", state));
         }
 
         long s = System.currentTimeMillis();
@@ -423,10 +399,14 @@
             state = adapter.getState();
             if (state == BluetoothAdapter.STATE_ON) {
                 assertTrue(adapter.isEnabled());
-                if ((mBluetoothReceiver.getFiredFlags() & mask) == mask) {
-                    mBluetoothReceiver.resetFiredFlags();
-                    writeOutput(String.format("enable() completed in %d ms",
-                            (System.currentTimeMillis() - s)));
+                if ((receiver.getFiredFlags() & mask) == mask) {
+                    long finish = receiver.getCompletedTime();
+                    if (start != -1 && finish != -1) {
+                        writeOutput(String.format("enable() completed in %d ms", (finish - start)));
+                    } else {
+                        writeOutput("enable() completed");
+                    }
+                    removeReceiver(receiver);
                     return;
                 }
             } else {
@@ -436,8 +416,8 @@
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mBluetoothReceiver.getFiredFlags();
-        mBluetoothReceiver.resetFiredFlags();
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
         fail(String.format("enable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
                 state, BluetoothAdapter.STATE_ON, firedFlags, mask));
     }
@@ -445,19 +425,23 @@
     public void disable(BluetoothAdapter adapter) {
         int mask = (BluetoothReceiver.STATE_TURNING_OFF_FLAG | BluetoothReceiver.STATE_OFF_FLAG
                 | BluetoothReceiver.SCAN_MODE_NONE_FLAG);
-        mBluetoothReceiver.resetFiredFlags();
+        long start = -1;
+        BluetoothReceiver receiver = getBluetoothReceiver(mask);
 
         int state = adapter.getState();
         switch (state) {
             case BluetoothAdapter.STATE_OFF:
                 assertFalse(adapter.isEnabled());
+                removeReceiver(receiver);
                 return;
-            case BluetoothAdapter.STATE_ON:
-                assertTrue(adapter.isEnabled());
-                assertTrue(adapter.disable());
-                break;
             case BluetoothAdapter.STATE_TURNING_ON:
                 assertFalse(adapter.isEnabled());
+                start = System.currentTimeMillis();
+                assertTrue(adapter.disable());
+                break;
+            case BluetoothAdapter.STATE_ON:
+                assertTrue(adapter.isEnabled());
+                start = System.currentTimeMillis();
                 assertTrue(adapter.disable());
                 break;
             case BluetoothAdapter.STATE_TURNING_OFF:
@@ -465,7 +449,8 @@
                 mask = 0; // Don't check for received intents since we might have missed them.
                 break;
             default:
-                fail("disable() invalid state: state=" + state);
+                removeReceiver(receiver);
+                fail(String.format("disable() invalid state: state=%d", state));
         }
 
         long s = System.currentTimeMillis();
@@ -473,10 +458,15 @@
             state = adapter.getState();
             if (state == BluetoothAdapter.STATE_OFF) {
                 assertFalse(adapter.isEnabled());
-                if ((mBluetoothReceiver.getFiredFlags() & mask) == mask) {
-                    mBluetoothReceiver.resetFiredFlags();
-                    writeOutput(String.format("disable() completed in %d ms",
-                            (System.currentTimeMillis() - s)));
+                if ((receiver.getFiredFlags() & mask) == mask) {
+                    long finish = receiver.getCompletedTime();
+                    if (start != -1 && finish != -1) {
+                        writeOutput(String.format("disable() completed in %d ms",
+                                (finish - start)));
+                    } else {
+                        writeOutput("disable() completed");
+                    }
+                    removeReceiver(receiver);
                     return;
                 }
             } else {
@@ -486,15 +476,14 @@
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mBluetoothReceiver.getFiredFlags();
-        mBluetoothReceiver.resetFiredFlags();
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
         fail(String.format("disable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
                 state, BluetoothAdapter.STATE_OFF, firedFlags, mask));
     }
 
     public void discoverable(BluetoothAdapter adapter) {
         int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
-        mBluetoothReceiver.resetFiredFlags();
 
         if (!adapter.isEnabled()) {
             fail("discoverable() bluetooth not enabled");
@@ -505,27 +494,29 @@
             return;
         }
 
-        assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+        BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+        assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE, scanMode);
+        long start = System.currentTimeMillis();
         assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
 
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
+        while (System.currentTimeMillis() - start < SET_SCAN_MODE_TIMEOUT) {
             scanMode = adapter.getScanMode();
             if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
-                if ((mBluetoothReceiver.getFiredFlags() & mask) == mask) {
-                    mBluetoothReceiver.resetFiredFlags();
+                if ((receiver.getFiredFlags() & mask) == mask) {
                     writeOutput(String.format("discoverable() completed in %d ms",
-                            (System.currentTimeMillis() - s)));
+                            (receiver.getCompletedTime() - start)));
+                    removeReceiver(receiver);
                     return;
                 }
             } else {
-                assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+                assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE, scanMode);
             }
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mBluetoothReceiver.getFiredFlags();
-        mBluetoothReceiver.resetFiredFlags();
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
         fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
                 + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,
                 firedFlags, mask));
@@ -533,7 +524,6 @@
 
     public void undiscoverable(BluetoothAdapter adapter) {
         int mask = BluetoothReceiver.SCAN_MODE_CONNECTABLE_FLAG;
-        mBluetoothReceiver.resetFiredFlags();
 
         if (!adapter.isEnabled()) {
             fail("undiscoverable() bluetooth not enabled");
@@ -544,27 +534,29 @@
             return;
         }
 
-        assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+        BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+        assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, scanMode);
+        long start = System.currentTimeMillis();
         assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
 
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
+        while (System.currentTimeMillis() - start < SET_SCAN_MODE_TIMEOUT) {
             scanMode = adapter.getScanMode();
             if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
-                if ((mBluetoothReceiver.getFiredFlags() & mask) == mask) {
-                    mBluetoothReceiver.resetFiredFlags();
+                if ((receiver.getFiredFlags() & mask) == mask) {
                     writeOutput(String.format("undiscoverable() completed in %d ms",
-                            (System.currentTimeMillis() - s)));
+                            (receiver.getCompletedTime() - start)));
+                    removeReceiver(receiver);
                     return;
                 }
             } else {
-                assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+                assertEquals(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, scanMode);
             }
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mBluetoothReceiver.getFiredFlags();
-        mBluetoothReceiver.resetFiredFlags();
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
         fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
                 + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags,
                 mask));
@@ -572,7 +564,6 @@
 
     public void startScan(BluetoothAdapter adapter) {
         int mask = BluetoothReceiver.DISCOVERY_STARTED_FLAG;
-        mBluetoothReceiver.resetFiredFlags();
 
         if (!adapter.isEnabled()) {
             fail("startScan() bluetooth not enabled");
@@ -582,28 +573,29 @@
             return;
         }
 
+        BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+        long start = System.currentTimeMillis();
         assertTrue(adapter.startDiscovery());
 
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < START_DISCOVERY_TIMEOUT) {
-            if (adapter.isDiscovering() && ((mBluetoothReceiver.getFiredFlags() & mask) == mask)) {
-                mBluetoothReceiver.resetFiredFlags();
+        while (System.currentTimeMillis() - start < START_DISCOVERY_TIMEOUT) {
+            if (adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) {
                 writeOutput(String.format("startScan() completed in %d ms",
-                        (System.currentTimeMillis() - s)));
+                        (receiver.getCompletedTime() - start)));
+                removeReceiver(receiver);
                 return;
             }
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mBluetoothReceiver.getFiredFlags();
-        mBluetoothReceiver.resetFiredFlags();
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
         fail(String.format("startScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
                 adapter.isDiscovering(), firedFlags, mask));
     }
 
     public void stopScan(BluetoothAdapter adapter) {
         int mask = BluetoothReceiver.DISCOVERY_FINISHED_FLAG;
-        mBluetoothReceiver.resetFiredFlags();
 
         if (!adapter.isEnabled()) {
             fail("stopScan() bluetooth not enabled");
@@ -613,66 +605,55 @@
             return;
         }
 
+        BluetoothReceiver receiver = getBluetoothReceiver(mask);
+
+        long start = System.currentTimeMillis();
         // TODO: put assertTrue() around cancelDiscovery() once it starts returning true.
         adapter.cancelDiscovery();
 
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CANCEL_DISCOVERY_TIMEOUT) {
-            if (!adapter.isDiscovering() && ((mBluetoothReceiver.getFiredFlags() & mask) == mask)) {
-                mBluetoothReceiver.resetFiredFlags();
+        while (System.currentTimeMillis() - start < CANCEL_DISCOVERY_TIMEOUT) {
+            if (!adapter.isDiscovering() && ((receiver.getFiredFlags() & mask) == mask)) {
                 writeOutput(String.format("stopScan() completed in %d ms",
-                        (System.currentTimeMillis() - s)));
+                        (receiver.getCompletedTime() - start)));
+                removeReceiver(receiver);
                 return;
             }
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mBluetoothReceiver.getFiredFlags();
-        mBluetoothReceiver.resetFiredFlags();
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
         fail(String.format("stopScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
                 adapter.isDiscovering(), firedFlags, mask));
 
     }
 
     public void pair(BluetoothAdapter adapter, BluetoothDevice device, int passkey, byte[] pin) {
-        pairOrAcceptPair(adapter, device, passkey, pin, true);
-    }
-
-    public void acceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey,
-            byte[] pin) {
-        pairOrAcceptPair(adapter, device, passkey, pin, false);
-    }
-
-    private void pairOrAcceptPair(BluetoothAdapter adapter, BluetoothDevice device, int passkey,
-            byte[] pin, boolean pair) {
-        String methodName = pair ? "pair()" : "acceptPair()";
-        int mask = PairReceiver.PAIR_FLAG;
-        int pairMask = PairReceiver.PAIR_STATE_BONDING | PairReceiver.PAIR_STATE_BONDED;
-
-        PairReceiver pairReceiver = getPairReceiver(mContext, device, passkey, pin);
-        mReceivers.add(pairReceiver);
+        int mask = PairReceiver.STATE_BONDING_FLAG | PairReceiver.STATE_BONDED_FLAG;
+        long start = -1;
 
         if (!adapter.isEnabled()) {
-            fail(methodName + " bluetooth not enabled");
+            fail("pair() bluetooth not enabled");
         }
 
+        PairReceiver receiver = getPairReceiver(device, passkey, pin, mask);
+
         int state = device.getBondState();
         switch (state) {
+            case BluetoothDevice.BOND_NONE:
+                assertFalse(adapter.getBondedDevices().contains(device));
+                start = System.currentTimeMillis();
+                assertTrue(device.createBond());
+                break;
+            case BluetoothDevice.BOND_BONDING:
+                mask = 0; // Don't check for received intents since we might have missed them.
+                break;
             case BluetoothDevice.BOND_BONDED:
                 assertTrue(adapter.getBondedDevices().contains(device));
                 return;
-            case BluetoothDevice.BOND_BONDING:
-                // Don't check for received intents since we might have missed them.
-                mask = pairMask = 0;
-                break;
-            case BluetoothDevice.BOND_NONE:
-                assertFalse(adapter.getBondedDevices().contains(device));
-                if (pair) {
-                    assertTrue(device.createBond());
-                }
-                break;
             default:
-                fail(methodName + " invalide state: state=" + state);
+                removeReceiver(receiver);
+                fail(String.format("pair() invalid state: device=%s, state=%d", device, state));
         }
 
         long s = System.currentTimeMillis();
@@ -680,327 +661,219 @@
             state = device.getBondState();
             if (state == BluetoothDevice.BOND_BONDED) {
                 assertTrue(adapter.getBondedDevices().contains(device));
-                if ((pairReceiver.getFiredFlags() & mask) == mask
-                        && (pairReceiver.getPairFiredFlags() & pairMask) == pairMask) {
-                    writeOutput(String.format("%s completed in %d ms: device=%s",
-                            methodName, (System.currentTimeMillis() - s), device));
-                    mReceivers.remove(pairReceiver);
-                    mContext.unregisterReceiver(pairReceiver);
+                if ((receiver.getFiredFlags() & mask) == mask) {
+                    long finish = receiver.getCompletedTime();
+                    if (start != -1 && finish != -1) {
+                        writeOutput(String.format("pair() completed in %d ms: device=%s",
+                                (finish - start), device));
+                    } else {
+                        writeOutput(String.format("pair() completed: device=%s", device));
+                    }
+                    removeReceiver(receiver);
                     return;
                 }
             }
             sleep(POLL_TIME);
         }
 
-        int firedFlags = pairReceiver.getFiredFlags();
-        int pairFiredFlags = pairReceiver.getPairFiredFlags();
-        pairReceiver.resetFiredFlags();
-        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%x), "
-                + "pairFlags=0x%x (expected 0x%x)", methodName, state, BluetoothDevice.BOND_BONDED,
-                firedFlags, mask, pairFiredFlags, pairMask));
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
+        fail(String.format("pair() timeout: device=%s, state=%d (expected %d), "
+                + "flags=0x%x (expected 0x%x)", device, state, BluetoothDevice.BOND_BONDED,
+                firedFlags, mask));
     }
 
     public void unpair(BluetoothAdapter adapter, BluetoothDevice device) {
-        int mask = PairReceiver.PAIR_FLAG;
-        int pairMask = PairReceiver.PAIR_STATE_NONE;
-
-        PairReceiver pairReceiver = getPairReceiver(mContext, device, 0, null);
-        mReceivers.add(pairReceiver);
+        int mask = PairReceiver.STATE_NONE_FLAG;
+        long start = -1;
 
         if (!adapter.isEnabled()) {
             fail("unpair() bluetooth not enabled");
         }
 
+        PairReceiver receiver = getPairReceiver(device, 0, null, mask);
+
         int state = device.getBondState();
         switch (state) {
-            case BluetoothDevice.BOND_BONDED:
-                assertTrue(adapter.getBondedDevices().contains(device));
-                assertTrue(device.removeBond());
-                break;
-            case BluetoothDevice.BOND_BONDING:
-                assertTrue(device.removeBond());
-                break;
             case BluetoothDevice.BOND_NONE:
                 assertFalse(adapter.getBondedDevices().contains(device));
+                removeReceiver(receiver);
                 return;
+            case BluetoothDevice.BOND_BONDING:
+                start = System.currentTimeMillis();
+                assertTrue(device.removeBond());
+                break;
+            case BluetoothDevice.BOND_BONDED:
+                assertTrue(adapter.getBondedDevices().contains(device));
+                start = System.currentTimeMillis();
+                assertTrue(device.removeBond());
+                break;
             default:
-                fail("unpair() invalid state: state=" + state);
+                removeReceiver(receiver);
+                fail(String.format("unpair() invalid state: device=%s, state=%d", device, state));
         }
 
-        assertTrue(device.removeBond());
-
         long s = System.currentTimeMillis();
         while (System.currentTimeMillis() - s < UNPAIR_TIMEOUT) {
             if (device.getBondState() == BluetoothDevice.BOND_NONE) {
                 assertFalse(adapter.getBondedDevices().contains(device));
-                if ((pairReceiver.getFiredFlags() & mask) == mask
-                       && (pairReceiver.getPairFiredFlags() & pairMask) == pairMask) {
-                    writeOutput(String.format("unpair() completed in %d ms: device=%s",
-                            (System.currentTimeMillis() - s), device));
-                    mReceivers.remove(pairReceiver);
-                    mContext.unregisterReceiver(pairReceiver);
+                if ((receiver.getFiredFlags() & mask) == mask) {
+                    long finish = receiver.getCompletedTime();
+                    if (start != -1 && finish != -1) {
+                        writeOutput(String.format("unpair() completed in %d ms: device=%s",
+                                (finish - start), device));
+                    } else {
+                        writeOutput(String.format("unpair() completed: device=%s", device));
+                    }
+                    removeReceiver(receiver);
                     return;
                 }
             }
         }
 
-        int firedFlags = pairReceiver.getFiredFlags();
-        int pairFiredFlags = pairReceiver.getPairFiredFlags();
-        pairReceiver.resetFiredFlags();
-        fail(String.format("unpair() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x), "
-                + "pairFlags=0x%x (expected 0x%x)", state, BluetoothDevice.BOND_BONDED, firedFlags,
-                mask, pairFiredFlags, pairMask));
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
+        fail(String.format("unpair() timeout: device=%s, state=%d (expected %d), "
+                + "flags=0x%x (expected 0x%x)", device, state, BluetoothDevice.BOND_BONDED,
+                firedFlags, mask));
     }
 
-    public void connectA2dp(BluetoothAdapter adapter, BluetoothDevice device) {
-        int mask = BluetoothReceiver.PROFILE_A2DP_FLAG;
-        int a2dpMask1 = (BluetoothReceiver.A2DP_STATE_CONNECTING
-                | BluetoothReceiver.A2DP_STATE_CONNECTED | BluetoothReceiver.A2DP_STATE_PLAYING);
-        int a2dpMask2 = a2dpMask1 ^ BluetoothReceiver.A2DP_STATE_CONNECTED;
-        int a2dpMask3 = a2dpMask1 ^ BluetoothReceiver.A2DP_STATE_PLAYING;
-        mBluetoothReceiver.resetFiredFlags();
+    public void connectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile) {
+        int mask = (ConnectProfileReceiver.STATE_CONNECTING_FLAG
+                | ConnectProfileReceiver.STATE_CONNECTED_FLAG);
+        long start = -1;
 
         if (!adapter.isEnabled()) {
-            fail("connectA2dp() bluetooth not enabled");
+            fail(String.format("connectProfile() bluetooth not enabled: device=%s, profile=%d",
+                    device, profile));
         }
 
         if (!adapter.getBondedDevices().contains(device)) {
-            fail("connectA2dp() device not paired: device=" + device);
+            fail(String.format("connectProfile() device not paired: device=%s, profile=%d",
+                    device, profile));
         }
 
-        int state = mA2dp.getSinkState(device);
+        BluetoothProfile proxy = connectProxy(adapter, profile);
+        if (proxy == null) {
+            fail(String.format("connectProfile() unknown profile: device=%s, profile=%d",
+                    device, profile));
+        }
+
+        ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask);
+
+        int state = proxy.getConnectionState(device);
         switch (state) {
-            case BluetoothA2dp.STATE_CONNECTED:
-            case BluetoothA2dp.STATE_PLAYING:
-                assertTrue(mA2dp.isSinkConnected(device));
+            case BluetoothProfile.STATE_CONNECTED:
+                removeReceiver(receiver);
                 return;
-            case BluetoothA2dp.STATE_DISCONNECTING:
-            case BluetoothA2dp.STATE_DISCONNECTED:
-                assertFalse(mA2dp.isSinkConnected(device));
-                assertTrue(mA2dp.connectSink(device));
+            case BluetoothProfile.STATE_CONNECTING:
+                mask = 0; // Don't check for received intents since we might have missed them.
                 break;
-            case BluetoothA2dp.STATE_CONNECTING:
-                assertFalse(mA2dp.isSinkConnected(device));
-                // Don't check for received intents since we might have missed them.
-                mask = a2dpMask1 = a2dpMask2 = a2dpMask3 = 0;
+            case BluetoothProfile.STATE_DISCONNECTED:
+            case BluetoothProfile.STATE_DISCONNECTING:
+                start = System.currentTimeMillis();
+                assertTrue(proxy.connect(device));
                 break;
             default:
-                fail("connectA2dp() invalid state: state=" + state);
+                removeReceiver(receiver);
+                fail(String.format("connectProfile() invalid state: device=%s, profile=%d, "
+                        + "state=%d", device, profile, state));
         }
 
         long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CONNECT_A2DP_TIMEOUT) {
-            state = mA2dp.getSinkState(device);
-            if (state == BluetoothA2dp.STATE_CONNECTED || state == BluetoothA2dp.STATE_PLAYING) {
-                assertTrue(mA2dp.isSinkConnected(device));
-                // Check whether STATE_CONNECTING and (STATE_CONNECTED or STATE_PLAYING) intents
-                // have fired if we are checking if intents should be fired.
-                int firedFlags = mBluetoothReceiver.getFiredFlags();
-                int a2dpFiredFlags = mBluetoothReceiver.getA2dpFiredFlags();
-                if ((mBluetoothReceiver.getFiredFlags() & mask) == mask
-                        && ((a2dpFiredFlags & a2dpMask1) == a2dpMask1
-                                || (a2dpFiredFlags & a2dpMask2) == a2dpMask2
-                                || (a2dpFiredFlags & a2dpMask3) == a2dpMask3)) {
-                    mBluetoothReceiver.resetFiredFlags();
-                    writeOutput(String.format("connectA2dp() completed in %d ms: device=%s",
-                            (System.currentTimeMillis() - s), device));
+        while (System.currentTimeMillis() - s < CONNECT_PROFILE_TIMEOUT) {
+            state = proxy.getConnectionState(device);
+            if (state == BluetoothProfile.STATE_CONNECTED) {
+                if ((receiver.getFiredFlags() & mask) == mask) {
+                    long finish = receiver.getCompletedTime();
+                    if (start != -1 && finish != -1) {
+                        writeOutput(String.format("connectProfile() completed in %d ms: "
+                                +"device=%s, profile=%d", (finish - start), device, profile));
+                    } else {
+                        writeOutput(String.format("connectProfile() completed: device=%s, "
+                                + "profile=%d", device, profile));
+                    }
+                    removeReceiver(receiver);
                     return;
                 }
             }
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mBluetoothReceiver.getFiredFlags();
-        int a2dpFiredFlags = mBluetoothReceiver.getA2dpFiredFlags();
-        mBluetoothReceiver.resetFiredFlags();
-        fail(String.format("connectA2dp() timeout: state=%d (expected %d or %d), "
-                + "flags=0x%x (expected 0x%x), a2dpFlags=0x%x (expected 0x%x or 0x%x or 0x%x)",
-                state, BluetoothHeadset.STATE_CONNECTED, BluetoothA2dp.STATE_PLAYING, firedFlags,
-                mask, a2dpFiredFlags, a2dpMask1, a2dpMask2, a2dpMask3));
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
+        fail(String.format("connectProfile() timeout: device=%s, profile=%s, "
+                + "state=%d (expected %d), flags=0x%x (expected 0x%x)", device, profile, state,
+                BluetoothProfile.STATE_CONNECTED, firedFlags, mask));
     }
 
-    public void disconnectA2dp(BluetoothAdapter adapter, BluetoothDevice device) {
-        int mask = BluetoothReceiver.PROFILE_A2DP_FLAG;
-        int a2dpMask = (BluetoothReceiver.A2DP_STATE_DISCONNECTING
-                | BluetoothReceiver.A2DP_STATE_DISCONNECTED);
-        mBluetoothReceiver.resetFiredFlags();
+    public void disconnectProfile(BluetoothAdapter adapter, BluetoothDevice device, int profile) {
+        int mask = (ConnectProfileReceiver.STATE_DISCONNECTING_FLAG
+                | ConnectProfileReceiver.STATE_DISCONNECTED_FLAG);
+        long start = -1;
 
         if (!adapter.isEnabled()) {
-            fail("disconnectA2dp() bluetooth not enabled");
+            fail(String.format("disconnectProfile() bluetooth not enabled: device=%s, profile=%d",
+                    device, profile));
         }
 
         if (!adapter.getBondedDevices().contains(device)) {
-            fail("disconnectA2dp() device not paired: device=" + device);
+            fail(String.format("disconnectProfile() device not paired: device=%s, profile=%d",
+                    device, profile));
         }
 
-        int state = mA2dp.getSinkState(device);
+        BluetoothProfile proxy = connectProxy(adapter, profile);
+        if (proxy == null) {
+            fail(String.format("disconnectProfile() unknown profile: device=%s, profile=%d",
+                    device, profile));
+        }
+
+        ConnectProfileReceiver receiver = getConnectProfileReceiver(device, profile, mask);
+
+        int state = proxy.getConnectionState(device);
         switch (state) {
-            case BluetoothA2dp.STATE_DISCONNECTED:
-                assertFalse(mA2dp.isSinkConnected(device));
+            case BluetoothProfile.STATE_CONNECTED:
+            case BluetoothProfile.STATE_CONNECTING:
+                start = System.currentTimeMillis();
+                assertTrue(proxy.disconnect(device));
+                break;
+            case BluetoothProfile.STATE_DISCONNECTED:
+                removeReceiver(receiver);
                 return;
-            case BluetoothA2dp.STATE_CONNECTED:
-            case BluetoothA2dp.STATE_PLAYING:
-                assertTrue(mA2dp.isSinkConnected(device));
-                assertTrue(mA2dp.disconnectSink(device));
-                break;
-            case BluetoothA2dp.STATE_CONNECTING:
-                assertFalse(mA2dp.isSinkConnected(device));
-                assertTrue(mA2dp.disconnectSink(device));
-                break;
-            case BluetoothA2dp.STATE_DISCONNECTING:
-                assertFalse(mA2dp.isSinkConnected(device));
-                // Don't check for received intents since we might have missed them.
-                mask = a2dpMask = 0;
+            case BluetoothProfile.STATE_DISCONNECTING:
+                mask = 0; // Don't check for received intents since we might have missed them.
                 break;
             default:
-                fail("disconnectA2dp() invalid state: state=" + state);
+                removeReceiver(receiver);
+                fail(String.format("disconnectProfile() invalid state: device=%s, profile=%d, "
+                        + "state=%d", device, profile, state));
         }
 
         long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < DISCONNECT_A2DP_TIMEOUT) {
-            state = mA2dp.getSinkState(device);
-            if (state == BluetoothA2dp.STATE_DISCONNECTED) {
-                assertFalse(mA2dp.isSinkConnected(device));
-                if ((mBluetoothReceiver.getFiredFlags() & mask) == mask
-                        && (mBluetoothReceiver.getA2dpFiredFlags() & a2dpMask) == a2dpMask) {
-                    mBluetoothReceiver.resetFiredFlags();
-                    writeOutput(String.format("disconnectA2dp() completed in %d ms: device=%s",
-                            (System.currentTimeMillis() - s), device));
+        while (System.currentTimeMillis() - s < DISCONNECT_PROFILE_TIMEOUT) {
+            state = proxy.getConnectionState(device);
+            if (state == BluetoothProfile.STATE_DISCONNECTED) {
+                if ((receiver.getFiredFlags() & mask) == mask) {
+                    long finish = receiver.getCompletedTime();
+                    if (start != -1 && finish != -1) {
+                        writeOutput(String.format("disconnectProfile() completed in %d ms: "
+                                +"device=%s, profile=%d", (finish - start), device, profile));
+                    } else {
+                        writeOutput(String.format("disconnectProfile() completed: device=%s, "
+                                + "profile=%d", device, profile));
+                    }
+                    removeReceiver(receiver);
                     return;
                 }
             }
             sleep(POLL_TIME);
         }
 
-        int firedFlags = mBluetoothReceiver.getFiredFlags();
-        int a2dpFiredFlags = mBluetoothReceiver.getA2dpFiredFlags();
-        mBluetoothReceiver.resetFiredFlags();
-        fail(String.format("disconnectA2dp() timeout: state=%d (expected %d), "
-                + "flags=0x%x (expected 0x%x), a2dpFlags=0x%x (expected 0x%x)", state,
-                BluetoothA2dp.STATE_DISCONNECTED, firedFlags, mask, a2dpFiredFlags, a2dpMask));
-    }
-
-    public void connectHeadset(BluetoothAdapter adapter, BluetoothDevice device) {
-        int mask = BluetoothReceiver.PROFILE_HEADSET_FLAG;
-        int headsetMask = (BluetoothReceiver.HEADSET_STATE_CONNECTING
-                | BluetoothReceiver.HEADSET_STATE_CONNECTED);
-        mBluetoothReceiver.resetFiredFlags();
-
-        if (!adapter.isEnabled()) {
-            fail("connectHeadset() bluetooth not enabled");
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail("connectHeadset() device not paired: device=" + device);
-        }
-
-        while (!mHeadsetServiceListener.isConnected()) {
-            sleep(POLL_TIME);
-        }
-
-        int state = mHeadset.getState(device);
-        switch (state) {
-            case BluetoothHeadset.STATE_CONNECTED:
-                assertTrue(mHeadset.isConnected(device));
-                return;
-            case BluetoothHeadset.STATE_DISCONNECTED:
-                assertFalse(mHeadset.isConnected(device));
-                mHeadset.connectHeadset(device);
-                break;
-            case BluetoothHeadset.STATE_CONNECTING:
-                assertFalse(mHeadset.isConnected(device));
-                // Don't check for received intents since we might have missed them.
-                mask = headsetMask = 0;
-                break;
-            case BluetoothHeadset.STATE_ERROR:
-                fail("connectHeadset() error state");
-                break;
-            default:
-                fail("connectHeadset() invalid state: state=" + state);
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < CONNECT_HEADSET_TIMEOUT) {
-            state = mHeadset.getState(device);
-            if (state == BluetoothHeadset.STATE_CONNECTED) {
-                assertTrue(mHeadset.isConnected(device));
-                if ((mBluetoothReceiver.getFiredFlags() & mask) == mask
-                        && (mBluetoothReceiver.getHeadsetFiredFlags() & headsetMask) == headsetMask) {
-                    mBluetoothReceiver.resetFiredFlags();
-                    writeOutput(String.format("connectHeadset() completed in %d ms: device=%s",
-                            (System.currentTimeMillis() - s), device));
-                    return;
-                }
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = mBluetoothReceiver.getFiredFlags();
-        int headsetFiredFlags = mBluetoothReceiver.getHeadsetFiredFlags();
-        mBluetoothReceiver.resetFiredFlags();
-        fail(String.format("connectHeadset() timeout: state=%d (expected %d), "
-                + "flags=0x%x (expected 0x%x), headsetFlags=0x%s (expected 0x%x)", state,
-                BluetoothHeadset.STATE_CONNECTED, firedFlags, mask, headsetFiredFlags,
-                headsetMask));
-    }
-
-    public void disconnectHeadset(BluetoothAdapter adapter, BluetoothDevice device) {
-        int mask = BluetoothReceiver.PROFILE_HEADSET_FLAG;
-        int headsetMask = BluetoothReceiver.HEADSET_STATE_DISCONNECTED;
-        mBluetoothReceiver.resetFiredFlags();
-
-        if (!adapter.isEnabled()) {
-            fail("disconnectHeadset() bluetooth not enabled");
-        }
-
-        if (!adapter.getBondedDevices().contains(device)) {
-            fail("disconnectHeadset() device not paired: device=" + device);
-        }
-
-        while (!mHeadsetServiceListener.isConnected()) {
-            sleep(POLL_TIME);
-        }
-
-        int state = mHeadset.getState(device);
-        switch (state) {
-            case BluetoothHeadset.STATE_CONNECTED:
-                mHeadset.disconnectHeadset(device);
-                break;
-            case BluetoothHeadset.STATE_CONNECTING:
-                mHeadset.disconnectHeadset(device);
-                break;
-            case BluetoothHeadset.STATE_DISCONNECTED:
-                return;
-            case BluetoothHeadset.STATE_ERROR:
-                fail("disconnectHeadset() error state");
-                break;
-            default:
-                fail("disconnectHeadset() invalid state: state=" + state);
-        }
-
-        long s = System.currentTimeMillis();
-        while (System.currentTimeMillis() - s < DISCONNECT_HEADSET_TIMEOUT) {
-            state = mHeadset.getState(device);
-            if (state == BluetoothHeadset.STATE_DISCONNECTED) {
-                assertFalse(mHeadset.isConnected(device));
-                if ((mBluetoothReceiver.getFiredFlags() & mask) == mask
-                        && (mBluetoothReceiver.getHeadsetFiredFlags() & headsetMask) == headsetMask) {
-                    mBluetoothReceiver.resetFiredFlags();
-                    writeOutput(String.format("disconnectHeadset() completed in %d ms: device=%s",
-                            (System.currentTimeMillis() - s), device));
-                    return;
-                }
-            }
-            sleep(POLL_TIME);
-        }
-
-        int firedFlags = mBluetoothReceiver.getFiredFlags();
-        int headsetFiredFlags = mBluetoothReceiver.getHeadsetFiredFlags();
-        mBluetoothReceiver.resetFiredFlags();
-        fail(String.format("disconnectHeadset() timeout: state=%d (expected %d), "
-                + "flags=0x%x (expected 0x%x), headsetFlags=0x%s (expected 0x%x)", state,
-                BluetoothHeadset.STATE_DISCONNECTED, firedFlags, mask, headsetFiredFlags,
-                headsetMask));
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
+        fail(String.format("disconnectProfile() timeout: device=%s, profile=%s, "
+                + "state=%d (expected %d), flags=0x%x (expected 0x%x)", device, profile, state,
+                BluetoothProfile.STATE_DISCONNECTED, firedFlags, mask));
     }
 
     public void writeOutput(String s) {
@@ -1016,29 +889,67 @@
         }
     }
 
-    private BluetoothReceiver getBluetoothReceiver(Context context) {
-        BluetoothReceiver receiver = new BluetoothReceiver();
+    private BluetoothReceiver getBluetoothReceiver(int expectedFlags) {
+        BluetoothReceiver receiver = new BluetoothReceiver(expectedFlags);
         IntentFilter filter = new IntentFilter();
         filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
         filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
         filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
-        filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
-        filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
-        context.registerReceiver(receiver, filter);
+        mContext.registerReceiver(receiver, filter);
+        mReceivers.add(receiver);
         return receiver;
     }
 
-    private PairReceiver getPairReceiver(Context context, BluetoothDevice device, int passkey,
-            byte[] pin) {
-        PairReceiver receiver = new PairReceiver(device, passkey, pin);
+    private PairReceiver getPairReceiver(BluetoothDevice device, int passkey, byte[] pin,
+            int expectedFlags) {
+        PairReceiver receiver = new PairReceiver(device, passkey, pin, expectedFlags);
         IntentFilter filter = new IntentFilter();
         filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
-        context.registerReceiver(receiver, filter);
+        mContext.registerReceiver(receiver, filter);
+        mReceivers.add(receiver);
         return receiver;
     }
 
+    private ConnectProfileReceiver getConnectProfileReceiver(BluetoothDevice device, int profile,
+            int expectedFlags) {
+        ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile,
+                expectedFlags);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        mContext.registerReceiver(receiver, filter);
+        mReceivers.add(receiver);
+        return receiver;
+    }
+
+    private void removeReceiver(BroadcastReceiver receiver) {
+        mContext.unregisterReceiver(receiver);
+        mReceivers.remove(receiver);
+    }
+
+    private BluetoothProfile connectProxy(BluetoothAdapter adapter, int profile) {
+        adapter.getProfileProxy(mContext, mServiceListener, profile);
+        long s = System.currentTimeMillis();
+        switch (profile) {
+            case BluetoothProfile.A2DP:
+                while (mA2dp != null
+                        && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
+                    sleep(POLL_TIME);
+                }
+                return mA2dp;
+            case BluetoothProfile.HEADSET:
+                while (mHeadset != null
+                        && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
+                    sleep(POLL_TIME);
+                }
+                return mHeadset;
+            default:
+                return null;
+        }
+    }
+
     private void sleep(long time) {
         try {
             Thread.sleep(time);
diff --git a/core/tests/coretests/src/android/content/MemoryFileProvider.java b/core/tests/coretests/src/android/content/MemoryFileProvider.java
index c4bc767..73530d7 100644
--- a/core/tests/coretests/src/android/content/MemoryFileProvider.java
+++ b/core/tests/coretests/src/android/content/MemoryFileProvider.java
@@ -16,16 +16,11 @@
 
 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.DatabaseUtils;
 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;
 
@@ -134,64 +129,34 @@
     }
 
     @Override
-    public AssetFileDescriptor openAssetFile(Uri url, String mode) throws FileNotFoundException {
+    public ParcelFileDescriptor openFile(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);
+                return getBlobColumnAsFile(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);
+                    return ParcelFileDescriptor.fromData(TEST_BLOB, null);
                 } 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);
+                return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
             default:
                 throw new FileNotFoundException("No files supported by provider at " + url);
         }
     }
 
-    private AssetFileDescriptor getBlobColumnAsAssetFile(Uri url, String mode, String sql)
+    private ParcelFileDescriptor getBlobColumnAsFile(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();
-            }
-        }
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        return DatabaseUtils.blobFileDescriptorForQuery(db, sql, null);
     }
 
     @Override
diff --git a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
index 62b4e7e..bbe7c10 100644
--- a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
+++ b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
@@ -56,7 +56,6 @@
             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);
diff --git a/core/tests/coretests/src/android/content/SyncQueueTest.java b/core/tests/coretests/src/android/content/SyncQueueTest.java
deleted file mode 100644
index 1da59d1..0000000
--- a/core/tests/coretests/src/android/content/SyncQueueTest.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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.content;
-
-import android.test.AndroidTestCase;
-import android.test.RenamingDelegatingContext;
-import android.test.mock.MockContext;
-import android.test.mock.MockContentResolver;
-import android.accounts.Account;
-import android.os.Bundle;
-import android.os.SystemClock;
-
-public class SyncQueueTest extends AndroidTestCase {
-    private static final Account ACCOUNT1 = new Account("test.account1", "test.type1");
-    private static final Account ACCOUNT2 = new Account("test.account2", "test.type2");
-    private static final String AUTHORITY1 = "test.authority1";
-    private static final String AUTHORITY2 = "test.authority2";
-    private static final String AUTHORITY3 = "test.authority3";
-
-    private SyncStorageEngine mSettings;
-    private SyncQueue mSyncQueue;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        MockContentResolver mockResolver = new MockContentResolver();
-        mSettings = SyncStorageEngine.newTestInstance(new TestContext(mockResolver, getContext()));
-        mSyncQueue = new SyncQueue(mSettings);
-    }
-
-    public void testSyncQueueOrder() throws Exception {
-        final SyncOperation op1 = new SyncOperation(
-                ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("1"), 0);
-        final SyncOperation op2 = new SyncOperation(
-                ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("2"), 100);
-        final SyncOperation op3 = new SyncOperation(
-                ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("3"), 150);
-        final SyncOperation op4 = new SyncOperation(
-                ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("4"), 60);
-        final SyncOperation op5 = new SyncOperation(
-                ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("5"), 80);
-        final SyncOperation op6 = new SyncOperation(
-                ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("6"), 0);
-        op6.expedited = true;
-
-        mSyncQueue.add(op1);
-        mSyncQueue.add(op2);
-        mSyncQueue.add(op3);
-        mSyncQueue.add(op4);
-        mSyncQueue.add(op5);
-        mSyncQueue.add(op6);
-
-        long now = SystemClock.elapsedRealtime() + 200;
-
-        assertEquals(op6, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op6);
-
-        assertEquals(op1, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op1);
-
-        assertEquals(op4, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op4);
-
-        assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op5);
-
-        assertEquals(op2, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op2);
-
-        assertEquals(op3, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op3);
-    }
-
-    public void testOrderWithBackoff() throws Exception {
-        final SyncOperation op1 = new SyncOperation(
-                ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("1"), 0);
-        final SyncOperation op2 = new SyncOperation(
-                ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("2"), 100);
-        final SyncOperation op3 = new SyncOperation(
-                ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("3"), 150);
-        final SyncOperation op4 = new SyncOperation(
-                ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY3, newTestBundle("4"), 60);
-        final SyncOperation op5 = new SyncOperation(
-                ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("5"), 80);
-        final SyncOperation op6 = new SyncOperation(
-                ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("6"), 0);
-        op6.expedited = true;
-
-        mSyncQueue.add(op1);
-        mSyncQueue.add(op2);
-        mSyncQueue.add(op3);
-        mSyncQueue.add(op4);
-        mSyncQueue.add(op5);
-        mSyncQueue.add(op6);
-
-        long now = SystemClock.elapsedRealtime() + 200;
-
-        assertEquals(op6, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op6);
-
-        assertEquals(op1, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op1);
-
-        mSettings.setBackoff(ACCOUNT2,  AUTHORITY3, now + 200, 5);
-        assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
-
-        mSettings.setBackoff(ACCOUNT2,  AUTHORITY3, SyncStorageEngine.NOT_IN_BACKOFF_MODE, 0);
-        assertEquals(op4, mSyncQueue.nextReadyToRun(now).first);
-
-        mSettings.setDelayUntilTime(ACCOUNT2,  AUTHORITY3, now + 200);
-        assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
-
-        mSettings.setDelayUntilTime(ACCOUNT2,  AUTHORITY3, 0);
-        assertEquals(op4, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op4);
-
-        assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op5);
-
-        assertEquals(op2, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op2);
-
-        assertEquals(op3, mSyncQueue.nextReadyToRun(now).first);
-        mSyncQueue.remove(op3);
-    }
-
-    Bundle newTestBundle(String val) {
-        Bundle bundle = new Bundle();
-        bundle.putString("test", val);
-        return bundle;
-    }
-
-    static class TestContext extends ContextWrapper {
-        ContentResolver mResolver;
-
-        public TestContext(ContentResolver resolver, Context realContext) {
-            super(new RenamingDelegatingContext(new MockContext(), realContext, "test."));
-            mResolver = resolver;
-        }
-
-        @Override
-        public void enforceCallingOrSelfPermission(String permission, String message) {
-        }
-
-        @Override
-        public ContentResolver getContentResolver() {
-            return mResolver;
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/database/DatabaseCursorTest.java b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
index fb5a36f..0733229 100644
--- a/core/tests/coretests/src/android/database/DatabaseCursorTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
@@ -33,7 +33,6 @@
 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;
 
@@ -41,8 +40,6 @@
 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";
@@ -92,43 +89,6 @@
     }
 
     @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(
@@ -164,24 +124,7 @@
         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();
+        assertEquals((long)l, c.getLong(lCol));        
     }
     
     @MediumTest
diff --git a/core/tests/coretests/src/android/database/DatabaseErrorHandlerTest.java b/core/tests/coretests/src/android/database/DatabaseErrorHandlerTest.java
new file mode 100644
index 0000000..48d25b9
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseErrorHandlerTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class DatabaseErrorHandlerTest extends AndroidTestCase {
+
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+    private static final String DB_NAME = "database_test.db";
+    private File dbDir;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
+        mDatabaseFile = new File(dbDir, DB_NAME);
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null,
+                new MyDatabaseCorruptionHandler());
+        assertNotNull(mDatabase);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    public void testNoCorruptionCase() {
+        new MyDatabaseCorruptionHandler().onCorruption(mDatabase);
+        // database file should still exist
+        assertTrue(mDatabaseFile.exists());
+    }
+
+    public void testDatabaseIsCorrupt() throws IOException {
+        mDatabase.execSQL("create table t (i int);");
+        // write junk into the database file
+        BufferedWriter writer = new BufferedWriter(new FileWriter(mDatabaseFile.getPath()));
+        writer.write("blah");
+        writer.close();
+        assertTrue(mDatabaseFile.exists());
+        // since the database file is now corrupt, doing any sql on this database connection
+        // should trigger call to MyDatabaseCorruptionHandler.onCorruption
+        try {
+            mDatabase.execSQL("select * from t;");
+            fail("expected exception");
+        } catch (SQLiteException e) {
+            // expected
+        }
+        // after corruption handler is called, the database file should be free of
+        // database corruption
+        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null,
+                new MyDatabaseCorruptionHandler());
+        assertTrue(db.isDatabaseIntegrityOk());
+    }
+
+    /**
+     * An example implementation of {@link DatabaseErrorHandler} to demonstrate
+     * database corruption handler which checks to make sure database is indeed
+     * corrupt before deleting the file.
+     */
+    public class MyDatabaseCorruptionHandler implements DatabaseErrorHandler {
+        public void onCorruption(SQLiteDatabase dbObj) {
+            boolean databaseOk = dbObj.isDatabaseIntegrityOk();
+            // close the database
+            try {
+                dbObj.close();
+            } catch (SQLiteException e) {
+                /* ignore */
+            }
+            if (databaseOk) {
+                // database is just fine. no need to delete the database file
+                Log.e("MyDatabaseCorruptionHandler", "no corruption in the database: " +
+                        mDatabaseFile.getPath());
+            } else {
+                // database is corrupt. delete the database file
+                Log.e("MyDatabaseCorruptionHandler", "deleting the database file: " +
+                        mDatabaseFile.getPath());
+                new File(dbDir, DB_NAME).delete();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
index 656029d..cd38bf07 100644
--- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -21,18 +21,21 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteStatement;
+import android.database.sqlite.SQLiteException;
 import android.os.Handler;
 import android.os.Parcel;
 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.util.Log;
+import android.util.Pair;
 
 import junit.framework.Assert;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Locale;
 
@@ -382,54 +385,28 @@
 
     @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);
+        mDatabase.execSQL("CREATE TABLE db1 (_id INTEGER PRIMARY KEY, data TEXT);");
+        Cursor cursor = mDatabase.query("db1", null, null, null, null, null, null);
+        assertNotNull(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();
-        */
+        cursor.close();
     }
 
     @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();
-        */
+        mDatabase.execSQL("CREATE TABLE db1 (_id INTEGER PRIMARY KEY, data TEXT);");
+        mDatabase.execSQL("INSERT INTO db1 (data) VALUES ('test');");
+        mDatabase.execSQL("ALTER TABLE db1 ADD COLUMN blah int;");
+        Cursor c = null;
+        try {
+            c = mDatabase.rawQuery("select blah from db1", null);
+        } catch (SQLiteException e) {
+            fail("unexpected exception: " + e.getMessage());
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
     }
 
     private class ChangeObserver extends ContentObserver {
@@ -467,45 +444,6 @@
     }
 
     @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);
@@ -989,21 +927,6 @@
     }
 
     @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.
-         */
-    }
-
-    @MediumTest
     public void testSemicolonsInStatements() throws Exception {
         mDatabase.execSQL("CREATE TABLE pragma_test (" +
                 "i INTEGER DEFAULT 1234, " +
@@ -1023,6 +946,34 @@
         }
     }
 
+    @MediumTest
+    public void testUnionsWithBindArgs() {
+        /* make sure unions with bindargs work http://b/issue?id=1061291 */
+        mDatabase.execSQL("CREATE TABLE A (i int);");
+        mDatabase.execSQL("create table B (k int);");
+        mDatabase.execSQL("create table C (n int);");
+        mDatabase.execSQL("insert into A values(1);");
+        mDatabase.execSQL("insert into A values(2);");
+        mDatabase.execSQL("insert into A values(3);");
+        mDatabase.execSQL("insert into B values(201);");
+        mDatabase.execSQL("insert into B values(202);");
+        mDatabase.execSQL("insert into B values(203);");
+        mDatabase.execSQL("insert into C values(901);");
+        mDatabase.execSQL("insert into C values(902);");
+        String s = "select i from A where i > 2 " +
+                "UNION select k from B where k > 201 " +
+                "UNION select n from C where n !=900;";
+        Cursor c = mDatabase.rawQuery(s, null);
+        int n = c.getCount();
+        c.close();
+        String s1 = "select i from A where i > ? " +
+                "UNION select k from B where k > ? " +
+                "UNION select n from C where n != ?;";
+        Cursor c1 = mDatabase.rawQuery(s1, new String[]{"2", "201", "900"});
+        assertEquals(n, c1.getCount());
+        c1.close();
+    }
+
     /**
      * This test is available only when the platform has a locale with the language "ja".
      * It finishes without failure when it is not available.  
@@ -1108,5 +1059,113 @@
                 }
             }
         }
-    }    
+    }
+
+    @SmallTest
+    public void testSetMaxCahesize() {
+        mDatabase.execSQL("CREATE TABLE test (i int, j int);");
+        mDatabase.execSQL("insert into test values(1,1);");
+        // set cache size
+        int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
+        mDatabase.setMaxSqlCacheSize(N);
+
+        // try reduce cachesize
+        try {
+            mDatabase.setMaxSqlCacheSize(1);
+        } catch (IllegalStateException e) {
+            assertTrue(e.getMessage().contains("cannot set cacheSize to a value less than"));
+        }
+    }
+
+    @LargeTest
+    public void testDefaultDatabaseErrorHandler() {
+        DefaultDatabaseErrorHandler errorHandler = new DefaultDatabaseErrorHandler();
+
+        // close the database. and call corruption handler.
+        // it should delete the database file.
+        File dbfile = new File(mDatabase.getPath());
+        mDatabase.close();
+        assertFalse(mDatabase.isOpen());
+        assertTrue(dbfile.exists());
+        try {
+            errorHandler.onCorruption(mDatabase);
+            assertFalse(dbfile.exists());
+        } catch (Exception e) {
+            fail("unexpected");
+        }
+
+        // create an in-memory database. and corruption handler shouldn't try to delete it
+        SQLiteDatabase memoryDb = SQLiteDatabase.openOrCreateDatabase(":memory:", null);
+        assertNotNull(memoryDb);
+        memoryDb.close();
+        assertFalse(memoryDb.isOpen());
+        try {
+            errorHandler.onCorruption(memoryDb);
+        } catch (Exception e) {
+            fail("unexpected");
+        }
+
+        // create a database, keep it open, call corruption handler. database file should be deleted
+        SQLiteDatabase dbObj = SQLiteDatabase.openOrCreateDatabase(mDatabase.getPath(), null);
+        assertTrue(dbfile.exists());
+        assertNotNull(dbObj);
+        assertTrue(dbObj.isOpen());
+        try {
+            errorHandler.onCorruption(dbObj);
+            assertFalse(dbfile.exists());
+        } catch (Exception e) {
+            fail("unexpected");
+        }
+
+        // create a database, attach 2 more databases to it
+        //    attached database # 1: ":memory:"
+        //    attached database # 2: mDatabase.getPath() + "1";
+        // call corruption handler. database files including the one for attached database # 2
+        // should be deleted
+        String attachedDb1File = mDatabase.getPath() + "1";
+        dbObj = SQLiteDatabase.openOrCreateDatabase(mDatabase.getPath(), null);
+        dbObj.execSQL("ATTACH DATABASE ':memory:' as memoryDb");
+        dbObj.execSQL("ATTACH DATABASE '" +  attachedDb1File + "' as attachedDb1");
+        assertTrue(dbfile.exists());
+        assertTrue(new File(attachedDb1File).exists());
+        assertNotNull(dbObj);
+        assertTrue(dbObj.isOpen());
+        ArrayList<Pair<String, String>> attachedDbs = dbObj.getAttachedDbs();
+        try {
+            errorHandler.onCorruption(dbObj);
+            assertFalse(dbfile.exists());
+            assertFalse(new File(attachedDb1File).exists());
+        } catch (Exception e) {
+            fail("unexpected");
+        }
+
+        // same as above, except this is a bit of stress testing. attach 5 database files
+        // and make sure they are all removed.
+        int N = 5;
+        ArrayList<String> attachedDbFiles = new ArrayList<String>(N);
+        for (int i = 0; i < N; i++) {
+            attachedDbFiles.add(mDatabase.getPath() + i);
+        }
+        dbObj = SQLiteDatabase.openOrCreateDatabase(mDatabase.getPath(), null);
+        dbObj.execSQL("ATTACH DATABASE ':memory:' as memoryDb");
+        for (int i = 0; i < N; i++) {
+            dbObj.execSQL("ATTACH DATABASE '" +  attachedDbFiles.get(i) + "' as attachedDb" + i);
+        }
+        assertTrue(dbfile.exists());
+        for (int i = 0; i < N; i++) {
+            assertTrue(new File(attachedDbFiles.get(i)).exists());
+        }
+        assertNotNull(dbObj);
+        assertTrue(dbObj.isOpen());
+        attachedDbs = dbObj.getAttachedDbs();
+        try {
+            errorHandler.onCorruption(dbObj);
+            assertFalse(dbfile.exists());
+            for (int i = 0; i < N; i++) {
+                assertFalse(new File(attachedDbFiles.get(i)).exists());
+            }
+        } catch (Exception e) {
+            fail("unexpected");
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/database/sqlite/DatabaseConnectionPoolTest.java b/core/tests/coretests/src/android/database/sqlite/DatabaseConnectionPoolTest.java
new file mode 100644
index 0000000..525dd2d
--- /dev/null
+++ b/core/tests/coretests/src/android/database/sqlite/DatabaseConnectionPoolTest.java
@@ -0,0 +1,368 @@
+/*
+ * 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.sqlite;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabaseTest.ClassToTestSqlCompilationAndCaching;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class DatabaseConnectionPoolTest extends AndroidTestCase {
+    private static final String TAG = "DatabaseConnectionPoolTest";
+
+    private static final int MAX_CONN = 5;
+    private static final String TEST_SQL = "select * from test where i = ? AND j = 1";
+    private static final String[] TEST_SQLS = new String[] {
+        TEST_SQL, TEST_SQL + 1, TEST_SQL + 2, TEST_SQL + 3, TEST_SQL + 4
+    };
+
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+    private DatabaseConnectionPool mTestPool;
+
+    @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.execSQL("create table test (i int, j int);");
+        mTestPool = new DatabaseConnectionPool(mDatabase);
+        assertNotNull(mTestPool);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mTestPool.close();
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    @SmallTest
+    public void testGetAndRelease() {
+        mTestPool.setMaxPoolSize(MAX_CONN);
+        // connections should be lazily created.
+        assertEquals(0, mTestPool.getSize());
+        // MAX pool size should be set to MAX_CONN
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        // get a connection
+        SQLiteDatabase db = mTestPool.get(TEST_SQL);
+        // pool size should be one - since only one should be allocated for the above get()
+        assertEquals(1, mTestPool.getSize());
+        assertEquals(mDatabase, db.mParentConnObj);
+        // no free connections should be available
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertFalse(mTestPool.isDatabaseObjFree(db));
+        // release the connection
+        mTestPool.release(db);
+        assertEquals(1, mTestPool.getFreePoolSize());
+        assertEquals(1, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        assertTrue(mTestPool.isDatabaseObjFree(db));
+        // release the same object again and expect IllegalStateException
+        try {
+            mTestPool.release(db);
+            fail("illegalStateException expected");
+        } catch (IllegalStateException e ) {
+            // expected.
+        }
+    }
+
+    /**
+     * get all connections from the pool and ask for one more.
+     * should get one of the connections already got so far. 
+     */
+    @SmallTest
+    public void testGetAllConnAndOneMore() {
+        mTestPool.setMaxPoolSize(MAX_CONN);
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        ArrayList<SQLiteDatabase> dbObjs = new ArrayList<SQLiteDatabase>();
+        for (int i = 0; i < MAX_CONN; i++) {
+            SQLiteDatabase db = mTestPool.get(TEST_SQL);
+            assertFalse(dbObjs.contains(db));
+            dbObjs.add(db);
+            assertEquals(mDatabase, db.mParentConnObj);
+        }
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        // pool is maxed out and no free connections. ask for one more connection
+        SQLiteDatabase db1 = mTestPool.get(TEST_SQL);
+        // make sure db1 is one of the existing ones
+        assertTrue(dbObjs.contains(db1));
+        // pool size should remain at MAX_CONN
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        // release db1 but since it is allocated 2 times, it should still remain 'busy'
+        mTestPool.release(db1);
+        assertFalse(mTestPool.isDatabaseObjFree(db1));
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        // release all connections
+        for (int i = 0; i < MAX_CONN; i++) {
+            mTestPool.release(dbObjs.get(i));
+        }
+        // all objects in the pool should be freed now
+        assertEquals(MAX_CONN, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+    }
+    
+    /**
+     * same as above except that each connection has different SQL statement associated with it. 
+     */
+    @SmallTest
+    public void testConnRetrievalForPreviouslySeenSql() {
+        mTestPool.setMaxPoolSize(MAX_CONN);
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+
+        HashMap<String, SQLiteDatabase> dbObjs = new HashMap<String, SQLiteDatabase>();
+        for (int i = 0; i < MAX_CONN; i++) {
+            SQLiteDatabase db = mTestPool.get(TEST_SQLS[i]);
+            executeSqlOnDatabaseConn(db, TEST_SQLS[i]);
+            assertFalse(dbObjs.values().contains(db));
+            dbObjs.put(TEST_SQLS[i], db);
+        }
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        // pool is maxed out and no free connections. ask for one more connection
+        // use a previously seen SQL statement
+        String testSql = TEST_SQLS[MAX_CONN - 1];
+        SQLiteDatabase db1 = mTestPool.get(testSql);
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        // make sure db1 is one of the existing ones
+        assertTrue(dbObjs.values().contains(db1));
+        assertEquals(db1, dbObjs.get(testSql));
+        // do the same again
+        SQLiteDatabase db2 = mTestPool.get(testSql);
+        // make sure db1 is one of the existing ones
+        assertEquals(db2, dbObjs.get(testSql));
+
+        // pool size should remain at MAX_CONN
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+
+        // release db1 but since the same connection is allocated 3 times,
+        // it should still remain 'busy'
+        mTestPool.release(db1);
+        assertFalse(mTestPool.isDatabaseObjFree(dbObjs.get(testSql)));
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+
+        // release db2 but since the same connection is allocated 2 times,
+        // it should still remain 'busy'
+        mTestPool.release(db2);
+        assertFalse(mTestPool.isDatabaseObjFree(dbObjs.get(testSql)));
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+
+        // release all connections
+        for (int i = 0; i < MAX_CONN; i++) {
+            mTestPool.release(dbObjs.get(TEST_SQLS[i]));
+        }
+        // all objects in the pool should be freed now
+        assertEquals(MAX_CONN, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+    }
+
+    private void executeSqlOnDatabaseConn(SQLiteDatabase db, String sql) {
+        // get the given sql be compiled on the given database connection.
+        // this will help DatabaseConenctionPool figure out if a given SQL statement
+        // is already cached by a database connection.
+        ClassToTestSqlCompilationAndCaching c =
+                ClassToTestSqlCompilationAndCaching.create(db, sql);
+        c.close();
+    }
+
+    /**
+     * get a connection for a SQL statement 'blah'. (connection_s)
+     * make sure the pool has at least one free connection even after this get().
+     * and get a connection for the same SQL again.
+     *    this connection should be different from connection_s.
+     *    even though there is a connection with the given SQL pre-compiled, since is it not free
+     *    AND since the pool has free connections available, should get a new connection.
+     */
+    @SmallTest
+    public void testGetConnForTheSameSql() {
+        mTestPool.setMaxPoolSize(MAX_CONN);
+
+        SQLiteDatabase db = mTestPool.get(TEST_SQL);
+        executeSqlOnDatabaseConn(db, TEST_SQL);
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(1, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+
+        assertFalse(mTestPool.isDatabaseObjFree(db));
+
+        SQLiteDatabase db1 = mTestPool.get(TEST_SQL);
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(2, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+
+        assertFalse(mTestPool.isDatabaseObjFree(db1));
+        assertFalse(db1.equals(db));
+
+        mTestPool.release(db);
+        assertEquals(1, mTestPool.getFreePoolSize());
+        assertEquals(2, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+
+        mTestPool.release(db1);
+        assertEquals(2, mTestPool.getFreePoolSize());
+        assertEquals(2, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+    }
+
+    /**
+     * get the same connection N times and release it N times.
+     * this tests DatabaseConnectionPool.PoolObj.mNumHolders
+     */
+    @SmallTest
+    public void testGetSameConnNtimesAndReleaseItNtimes() {
+        mTestPool.setMaxPoolSize(MAX_CONN);
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+
+        HashMap<String, SQLiteDatabase> dbObjs = new HashMap<String, SQLiteDatabase>();
+        for (int i = 0; i < MAX_CONN; i++) {
+            SQLiteDatabase db = mTestPool.get(TEST_SQLS[i]);
+            executeSqlOnDatabaseConn(db, TEST_SQLS[i]);
+            assertFalse(dbObjs.values().contains(db));
+            dbObjs.put(TEST_SQLS[i], db);
+        }
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        // every connection in the pool should have numHolders = 1
+        for (int i = 0; i < MAX_CONN; i ++) {
+            assertEquals(1, mTestPool.getPool().get(i).getNumHolders());
+        }
+        // pool is maxed out and no free connections. ask for one more connection
+        // use a previously seen SQL statement
+        String testSql = TEST_SQLS[MAX_CONN - 1];
+        SQLiteDatabase db1 = mTestPool.get(testSql);
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        // make sure db1 is one of the existing ones
+        assertTrue(dbObjs.values().contains(db1));
+        assertEquals(db1, dbObjs.get(testSql));
+        assertFalse(mTestPool.isDatabaseObjFree(db1));
+        DatabaseConnectionPool.PoolObj poolObj = mTestPool.getPool().get(db1.mConnectionNum - 1);
+        int numHolders = poolObj.getNumHolders();
+        assertEquals(2, numHolders);
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        // get the same connection N times more
+        int N = 100;
+        for (int i = 0; i < N; i++) {
+            SQLiteDatabase db2 = mTestPool.get(testSql);
+            assertEquals(db1, db2);
+            assertFalse(mTestPool.isDatabaseObjFree(db2));
+            // numHolders for this object should be now up by 1
+            int prev = numHolders;
+            numHolders = poolObj.getNumHolders();
+            assertEquals(prev + 1, numHolders);
+        }
+        // release it N times
+        for (int i = 0; i < N; i++) {
+            mTestPool.release(db1);
+            int prev = numHolders;
+            numHolders = poolObj.getNumHolders();
+            assertEquals(prev - 1, numHolders);
+            assertFalse(mTestPool.isDatabaseObjFree(db1));
+        }
+        // the connection should still have 2 more holders
+        assertFalse(mTestPool.isDatabaseObjFree(db1));
+        assertEquals(2, poolObj.getNumHolders());
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        // release 2 more times
+        mTestPool.release(db1);
+        mTestPool.release(db1);
+        assertEquals(0, poolObj.getNumHolders());
+        assertEquals(1, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        assertTrue(mTestPool.isDatabaseObjFree(db1));
+    }
+
+    @SmallTest
+    public void testStressTest() {
+        mTestPool.setMaxPoolSize(MAX_CONN);
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+
+        HashMap<SQLiteDatabase, Integer> dbMap = new HashMap<SQLiteDatabase, Integer>();
+        for (int i = 0; i < MAX_CONN; i++) {
+            SQLiteDatabase db = mTestPool.get(TEST_SQLS[i]);
+            assertFalse(dbMap.containsKey(db));
+            dbMap.put(db, 1);
+            executeSqlOnDatabaseConn(db, TEST_SQLS[i]);
+        }
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        // ask for lot more connections but since the pool is maxed out, we should start receiving
+        // connections that we already got so far
+        for (int i = MAX_CONN; i < 1000; i++) {
+            SQLiteDatabase db = mTestPool.get(TEST_SQL + i);
+            assertTrue(dbMap.containsKey(db));
+            int k = dbMap.get(db);
+            dbMap.put(db, ++k);
+        }
+        assertEquals(0, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+        // print the distribution of the database connection handles received, should be uniform.
+        for (SQLiteDatabase d : dbMap.keySet()) {
+            Log.i(TAG, "connection # " + d.mConnectionNum + ", numHolders: " + dbMap.get(d));
+        }
+        // print the pool info
+        Log.i(TAG, mTestPool.toString());
+        // release all
+        for (SQLiteDatabase d : dbMap.keySet()) {
+            int num = dbMap.get(d);
+            for (int i = 0; i < num; i++) {
+                mTestPool.release(d);
+            }
+        }
+        assertEquals(MAX_CONN, mTestPool.getFreePoolSize());
+        assertEquals(MAX_CONN, mTestPool.getSize());
+        assertEquals(MAX_CONN, mTestPool.getMaxPoolSize());
+    }
+}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
new file mode 100644
index 0000000..f6b1d04
--- /dev/null
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCursorTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+
+public class SQLiteCursorTest extends AndroidTestCase {
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+    private static final String TABLE_NAME = "testCursor";
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
+        mDatabaseFile = new File(dbDir, "sqlitecursor_test.db");
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+        assertNotNull(mDatabase);
+        // create a test table
+        mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    @SmallTest
+    public void testQueryObjReassignment() {
+        mDatabase.enableWriteAheadLogging();
+        // have a few connections in the database connection pool
+        DatabaseConnectionPool pool = mDatabase.mConnectionPool;
+        pool.setMaxPoolSize(5);
+        SQLiteCursor cursor =
+                (SQLiteCursor) mDatabase.rawQuery("select * from " + TABLE_NAME, null);
+        assertNotNull(cursor);
+        // it should use a pooled database connection
+        SQLiteDatabase db = cursor.getDatabase();
+        assertTrue(db.mConnectionNum > 0);
+        assertFalse(mDatabase.equals(db));
+        assertEquals(mDatabase, db.mParentConnObj);
+        assertTrue(pool.getConnectionList().contains(db));
+        assertTrue(db.isOpen());
+        // do a requery. cursor should continue to use the above pooled connection
+        cursor.requery();
+        SQLiteDatabase dbAgain = cursor.getDatabase();
+        assertEquals(db, dbAgain);
+        // disable WAL so that the pooled connection held by the above cursor is closed
+        mDatabase.disableWriteAheadLogging();
+        assertFalse(db.isOpen());
+        assertNull(mDatabase.mConnectionPool);
+        // requery - which should make the cursor use mDatabase connection since the pooled
+        // connection is no longer available
+        cursor.requery();
+        SQLiteDatabase db1 = cursor.getDatabase();
+        assertTrue(db1.mConnectionNum == 0);
+        assertEquals(mDatabase, db1);
+        assertNull(mDatabase.mConnectionPool);
+        assertTrue(db1.isOpen());
+        assertFalse(mDatabase.equals(db));
+        // enable WAL and requery - this time a pooled connection should be used
+        mDatabase.enableWriteAheadLogging();
+        cursor.requery();
+        db = cursor.getDatabase();
+        assertTrue(db.mConnectionNum > 0);
+        assertFalse(mDatabase.equals(db));
+        assertEquals(mDatabase, db.mParentConnObj);
+        assertTrue(mDatabase.mConnectionPool.getConnectionList().contains(db));
+        assertTrue(db.isOpen());
+    }
+
+    /**
+     * this test could take a while to execute. so, designate it as LargetTest
+     */
+    @LargeTest
+    public void testFillWindow() {
+        // create schema
+        final String testTable = "testV";
+        mDatabase.beginTransaction();
+        mDatabase.execSQL("CREATE TABLE " + testTable + " (col1 int, desc text not null);");
+        mDatabase.setTransactionSuccessful();
+        mDatabase.endTransaction();
+
+        // populate the table with data
+        // create a big string that will almost fit a page but not quite.
+        // since sqlite wants to make sure each row is in a page, this string will allocate
+        // a new database page for each row.
+        StringBuilder buff = new StringBuilder();
+        for (int i = 0; i < 500; i++) {
+            buff.append(i % 10 + "");
+        }
+        ContentValues values = new ContentValues();
+        values.put("desc", buff.toString());
+
+        // insert more than 1MB of data in the table. this should ensure that the entire tabledata
+        // will need more than one CursorWindow
+        int N = 5000;
+        Set<Integer> rows = new HashSet<Integer>();
+        mDatabase.beginTransaction();
+        for (int j = 0; j < N; j++) {
+            values.put("col1", j);
+            mDatabase.insert(testTable, null, values);
+            rows.add(j); // store in a hashtable so we can verify the results from cursor later on
+        }
+        mDatabase.setTransactionSuccessful();
+        mDatabase.endTransaction();
+        assertEquals(N, rows.size());
+        Cursor c1 = mDatabase.rawQuery("select * from " + testTable, null);
+        assertEquals(N, c1.getCount());
+        c1.close();
+
+        // scroll through ALL data in the table using a cursor. should cause multiple calls to
+        // native_fill_window (and re-fills of the CursorWindow object)
+        Cursor c = mDatabase.query(testTable, new String[]{"col1", "desc"},
+                null, null, null, null, null);
+        int i = 0;
+        while (c.moveToNext()) {
+            int val = c.getInt(0);
+            assertTrue(rows.contains(val));
+            assertTrue(rows.remove(val));
+        }
+        // did I see all the rows in the table?
+        assertTrue(rows.isEmpty());
+
+        // change data and make sure the cursor picks up new data & count
+        rows = new HashSet<Integer>();
+        mDatabase.beginTransaction();
+        int M = N + 1000;
+        for (int j = 0; j < M; j++) {
+            rows.add(j);
+            if (j < N) {
+                continue;
+            }
+            values.put("col1", j);
+            mDatabase.insert(testTable, null, values);
+        }
+        mDatabase.setTransactionSuccessful();
+        mDatabase.endTransaction();
+        assertEquals(M, rows.size());
+        c.requery();
+        i = 0;
+        while (c.moveToNext()) {
+            int val = c.getInt(0);
+            assertTrue(rows.contains(val));
+            assertTrue(rows.remove(val));
+        }
+        // did I see all data from the modified table
+        assertTrue(rows.isEmpty());
+
+        // move cursor back to 1st row and scroll to about halfway in the result set
+        // and then delete 75% of data - and then do requery
+        c.moveToFirst();
+        int K = N / 2;
+        for (int p = 0; p < K && c.moveToNext(); p++) {
+            // nothing to do - just scrolling to about half-point in the resultset
+        }
+        mDatabase.beginTransaction();
+        mDatabase.delete(testTable, "col1 < ?", new String[]{ (3 * M / 4) + ""});
+        mDatabase.setTransactionSuccessful();
+        mDatabase.endTransaction();
+        c.requery();
+        assertEquals(M / 4, c.getCount());
+        while (c.moveToNext()) {
+            // just move the cursor to next row - to make sure it can go through the entire
+            // resultset without any problems
+        }
+        c.close();
+    }
+}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
new file mode 100644
index 0000000..39258ae
--- /dev/null
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -0,0 +1,926 @@
+/*
+ * 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.sqlite;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseErrorHandler;
+import android.database.DatabaseUtils;
+import android.database.DefaultDatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.database.sqlite.SQLiteStatement;
+import android.test.AndroidTestCase;
+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.ArrayList;
+
+public class SQLiteDatabaseTest extends AndroidTestCase {
+    private static final String TAG = "DatabaseGeneralTest";
+    private static final String TEST_TABLE = "test";
+    private static final int CURRENT_DATABASE_VERSION = 42;
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+    private static final int INSERT = 1;
+    private static final int UPDATE = 2;
+    private static final int DELETE = 3;
+    private static final String DB_NAME = "database_test.db";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        dbSetUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        dbTeardown();
+        super.tearDown();
+    }
+
+    private void dbTeardown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+    }
+
+    private void dbSetUp() throws Exception {
+        File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
+        mDatabaseFile = new File(dbDir, DB_NAME);
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null, null);
+        assertNotNull(mDatabase);
+        mDatabase.setVersion(CURRENT_DATABASE_VERSION);
+    }
+
+    @SmallTest
+    public void testEnableWriteAheadLogging() {
+        mDatabase.disableWriteAheadLogging();
+        assertNull(mDatabase.mConnectionPool);
+        mDatabase.enableWriteAheadLogging();
+        DatabaseConnectionPool pool = mDatabase.mConnectionPool;
+        assertNotNull(pool);
+        // make the same call again and make sure the pool already setup is not re-created
+        mDatabase.enableWriteAheadLogging();
+        assertEquals(pool, mDatabase.mConnectionPool);
+    }
+
+    @SmallTest
+    public void testDisableWriteAheadLogging() {
+        mDatabase.execSQL("create table test (i int);");
+        mDatabase.enableWriteAheadLogging();
+        assertNotNull(mDatabase.mConnectionPool);
+        // get a pooled database connection
+        SQLiteDatabase db = mDatabase.getDbConnection("select * from test");
+        assertNotNull(db);
+        assertFalse(mDatabase.equals(db));
+        assertTrue(db.isOpen());
+        // disable WAL - which should close connection pool and all pooled connections
+        mDatabase.disableWriteAheadLogging();
+        assertNull(mDatabase.mConnectionPool);
+        assertFalse(db.isOpen());
+    }
+
+    @SmallTest
+    public void testCursorsWithClosedDbConnAfterDisableWriteAheadLogging() {
+        mDatabase.disableWriteAheadLogging();
+        mDatabase.beginTransactionNonExclusive();
+        mDatabase.execSQL("create table test (i int);");
+        mDatabase.execSQL("insert into test values(1);");
+        mDatabase.setTransactionSuccessful();
+        mDatabase.endTransaction();
+        mDatabase.enableWriteAheadLogging();
+        assertNotNull(mDatabase.mConnectionPool);
+        assertEquals(0, mDatabase.mConnectionPool.getSize());
+        assertEquals(0, mDatabase.mConnectionPool.getFreePoolSize());
+        // get a cursor which should use pooled database connection
+        Cursor c = mDatabase.rawQuery("select * from test", null);
+        assertEquals(1, c.getCount());
+        assertEquals(1, mDatabase.mConnectionPool.getSize());
+        assertEquals(1, mDatabase.mConnectionPool.getFreePoolSize());
+        SQLiteDatabase db = mDatabase.mConnectionPool.getConnectionList().get(0);
+        assertTrue(mDatabase.mConnectionPool.isDatabaseObjFree(db));
+        // disable WAL - which should close connection pool and all pooled connections
+        mDatabase.disableWriteAheadLogging();
+        assertNull(mDatabase.mConnectionPool);
+        assertFalse(db.isOpen());
+        // cursor data should still be accessible because it is fetching data from CursorWindow
+        c.moveToNext();
+        assertEquals(1, c.getInt(0));
+        c.requery();
+        assertEquals(1, c.getCount());
+        c.moveToNext();
+        assertEquals(1, c.getInt(0));
+        c.close();
+    }
+
+    /**
+     * a transaction should be started before a standalone-update/insert/delete statement
+     */
+    @SmallTest
+    public void testStartXactBeforeUpdateSql() throws InterruptedException {
+        runTestForStartXactBeforeUpdateSql(INSERT);
+        runTestForStartXactBeforeUpdateSql(UPDATE);
+        runTestForStartXactBeforeUpdateSql(DELETE);
+    }
+    private void runTestForStartXactBeforeUpdateSql(int stmtType) throws InterruptedException {
+        createTableAndClearCache();
+
+        ContentValues values = new ContentValues();
+        // make some changes to data in TEST_TABLE
+        for (int i = 0; i < 5; i++) {
+            values.put("i", i);
+            values.put("j", "i" + System.currentTimeMillis());
+            mDatabase.insert(TEST_TABLE, null, values);
+            switch (stmtType) {
+                case UPDATE:
+                    values.put("j", "u" + System.currentTimeMillis());
+                    mDatabase.update(TEST_TABLE, values, "i = " + i, null);
+                    break;
+                case DELETE:
+                    mDatabase.delete(TEST_TABLE, "i = 1", null);
+                    break;
+            }
+        }
+        // do a query. even though query uses a different database connection,
+        // it should still see the above changes to data because the above standalone
+        // insert/update/deletes are done in transactions automatically.
+        String sql = "select count(*) from " + TEST_TABLE;
+        SQLiteStatement stmt = mDatabase.compileStatement(sql);
+        final int expectedValue = (stmtType == DELETE) ? 4 : 5;
+        assertEquals(expectedValue, stmt.simpleQueryForLong());
+        stmt.close();
+        Cursor c = mDatabase.rawQuery(sql, null);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        assertEquals(expectedValue, c.getLong(0));
+        c.close();
+
+        // do 5 more changes in a transaction but do a query before and after the commit
+        mDatabase.beginTransaction();
+        for (int i = 10; i < 15; i++) {
+            values.put("i", i);
+            values.put("j", "i" + System.currentTimeMillis());
+            mDatabase.insert(TEST_TABLE, null, values);
+            switch (stmtType) {
+                case UPDATE:
+                    values.put("j", "u" + System.currentTimeMillis());
+                    mDatabase.update(TEST_TABLE, values, "i = " + i, null);
+                    break;
+                case DELETE:
+                    mDatabase.delete(TEST_TABLE, "i = 1", null);
+                    break;
+            }
+        }
+        mDatabase.setTransactionSuccessful();
+        // do a query before commit - should still have 5 rows
+        // this query should run in a different thread to force it to use a different database
+        // connection
+        Thread t = new Thread() {
+            @Override public void run() {
+                String sql = "select count(*) from " + TEST_TABLE;
+                SQLiteStatement stmt = getDb().compileStatement(sql);
+                assertEquals(expectedValue, stmt.simpleQueryForLong());
+                stmt.close();
+                Cursor c = getDb().rawQuery(sql, null);
+                assertEquals(1, c.getCount());
+                c.moveToFirst();
+                assertEquals(expectedValue, c.getLong(0));
+                c.close();
+            }
+        };
+        t.start();
+        // wait until the above thread is done
+        t.join();
+        // commit and then query. should see changes from the transaction
+        mDatabase.endTransaction();
+        stmt = mDatabase.compileStatement(sql);
+        final int expectedValue2 = (stmtType == DELETE) ? 9 : 10;
+        assertEquals(expectedValue2, stmt.simpleQueryForLong());
+        stmt.close();
+        c = mDatabase.rawQuery(sql, null);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        assertEquals(expectedValue2, c.getLong(0));
+        c.close();
+    }
+    private synchronized SQLiteDatabase getDb() {
+        return mDatabase;
+    }
+
+    /**
+     * Test to ensure that readers are able to read the database data (old versions)
+     * EVEN WHEN the writer is in a transaction on the same database.
+     *<p>
+     * This test starts 1 Writer and 2 Readers and sets up connection pool for readers
+     * by calling the method {@link SQLiteDatabase#enableWriteAheadLogging()}.
+     * <p>
+     * Writer does the following in a tight loop
+     * <pre>
+     *     begin transaction
+     *     insert into table_1
+     *     insert into table_2
+     *     commit
+     * </pre>
+     * <p>
+     * As long a the writer is alive, Readers do the following in a tight loop at the same time
+     * <pre>
+     *     Reader_K does "select count(*) from table_K"  where K = 1 or 2
+     * </pre>
+     * <p>
+     * The test is run for TIME_TO_RUN_WAL_TEST_FOR sec.
+     * <p>
+     * The test is repeated for different connection-pool-sizes (1..3)
+     * <p>
+     * And at the end of of each test, the following statistics are printed
+     * <ul>
+     *    <li>connection-pool-size</li>
+     *    <li>number-of-transactions by writer</li>
+     *    <li>number of reads by reader_K while the writer is IN or NOT-IN xaction</li>
+     * </ul>
+     */
+    @LargeTest
+    @Suppress // run this test only if you need to collect the numbers from this test
+    public void testConcurrencyEffectsOfConnPool() throws Exception {
+        // run the test with sqlite WAL enable
+        runConnectionPoolTest(true);
+
+        // run the same test WITHOUT sqlite WAL enabled
+        runConnectionPoolTest(false);
+    }
+
+    private void runConnectionPoolTest(boolean useWal) throws Exception {
+        int M = 3;
+        StringBuilder[] buff = new StringBuilder[M];
+        for (int i = 0; i < M; i++) {
+            if (useWal) {
+                // set up connection pool
+                mDatabase.enableWriteAheadLogging();
+                mDatabase.mConnectionPool.setMaxPoolSize(i + 1);
+            } else {
+                mDatabase.disableWriteAheadLogging();
+            }
+            mDatabase.execSQL("CREATE TABLE t1 (i int, j int);");
+            mDatabase.execSQL("CREATE TABLE t2 (i int, j int);");
+            mDatabase.beginTransaction();
+            for (int k = 0; k < 5; k++) {
+                mDatabase.execSQL("insert into t1 values(?,?);", new String[] {k+"", k+""});
+                mDatabase.execSQL("insert into t2 values(?,?);", new String[] {k+"", k+""});
+            }
+            mDatabase.setTransactionSuccessful();
+            mDatabase.endTransaction();
+
+            // start a writer
+            Writer w = new Writer(mDatabase);
+
+            // initialize an array of counters to be passed to the readers
+            Reader r1 = new Reader(mDatabase, "t1", w, 0);
+            Reader r2 = new Reader(mDatabase, "t2", w, 1);
+            w.start();
+            r1.start();
+            r2.start();
+
+            // wait for all threads to die
+            w.join();
+            r1.join();
+            r2.join();
+
+            // print the stats
+            int[][] counts = getCounts();
+            buff[i] = new StringBuilder();
+            buff[i].append("connpool-size = ");
+            buff[i].append(i + 1);
+            buff[i].append(", num xacts by writer = ");
+            buff[i].append(getNumXacts());
+            buff[i].append(", num-reads-in-xact/NOT-in-xact by reader1 = ");
+            buff[i].append(counts[0][1] + "/" + counts[0][0]);
+            buff[i].append(", by reader2 = ");
+            buff[i].append(counts[1][1] + "/" + counts[1][0]);
+
+            Log.i(TAG, "done testing for conn-pool-size of " + (i+1));
+
+            dbTeardown();
+            dbSetUp();
+        }
+        Log.i(TAG, "duration of test " + TIME_TO_RUN_WAL_TEST_FOR + " sec");
+        for (int i = 0; i < M; i++) {
+            Log.i(TAG, buff[i].toString());
+        }
+    }
+
+    private boolean inXact = false;
+    private int numXacts;
+    private static final int TIME_TO_RUN_WAL_TEST_FOR = 15; // num sec this test should run
+    private int[][] counts = new int[2][2];
+
+    private synchronized boolean inXact() {
+        return inXact;
+    }
+
+    private synchronized void setInXactFlag(boolean flag) {
+        inXact = flag;
+    }
+
+    private synchronized void setCounts(int readerNum, int[] numReads) {
+        counts[readerNum][0] = numReads[0];
+        counts[readerNum][1] = numReads[1];
+    }
+
+    private synchronized int[][] getCounts() {
+        return counts;
+    }
+
+    private synchronized void setNumXacts(int num) {
+        numXacts = num;
+    }
+
+    private synchronized int getNumXacts() {
+        return numXacts;
+    }
+
+    private class Writer extends Thread {
+        private SQLiteDatabase db = null;
+        public Writer(SQLiteDatabase db) {
+            this.db = db;
+        }
+        @Override public void run() {
+            // in a loop, for N sec, do the following
+            //    BEGIN transaction
+            //    insert into table t1, t2
+            //    Commit
+            long now = System.currentTimeMillis();
+            int k;
+            for (k = 0;(System.currentTimeMillis() - now) / 1000 < TIME_TO_RUN_WAL_TEST_FOR; k++) {
+                db.beginTransactionNonExclusive();
+                setInXactFlag(true);
+                for (int i = 0; i < 10; i++) {
+                    db.execSQL("insert into t1 values(?,?);", new String[] {i+"", i+""});
+                    db.execSQL("insert into t2 values(?,?);", new String[] {i+"", i+""});
+                }
+                db.setTransactionSuccessful();
+                setInXactFlag(false);
+                db.endTransaction();
+            }
+            setNumXacts(k);
+        }
+    }
+
+    private class Reader extends Thread {
+        private SQLiteDatabase db = null;
+        private String table = null;
+        private Writer w = null;
+        private int readerNum;
+        private int[] numReads = new int[2];
+        public Reader(SQLiteDatabase db, String table, Writer w, int readerNum) {
+            this.db = db;
+            this.table = table;
+            this.w = w;
+            this.readerNum = readerNum;
+        }
+        @Override public void run() {
+            // while the write is alive, in a loop do the query on a table
+            while (w.isAlive()) {
+                for (int i = 0; i < 10; i++) {
+                    DatabaseUtils.longForQuery(db, "select count(*) from " + this.table, null);
+                    // update count of reads
+                    numReads[inXact() ? 1 : 0] += 1;
+                }
+            }
+            setCounts(readerNum, numReads);
+        }
+    }
+
+    public static class ClassToTestSqlCompilationAndCaching extends SQLiteProgram {
+        private ClassToTestSqlCompilationAndCaching(SQLiteDatabase db, String sql) {
+            super(db, sql);
+        }
+        public static ClassToTestSqlCompilationAndCaching create(SQLiteDatabase db, String sql) {
+            db.lock();
+            try {
+                return new ClassToTestSqlCompilationAndCaching(db, sql);
+            } finally {
+                db.unlock();
+            }
+        }
+    }
+
+    @SmallTest
+    public void testLruCachingOfSqliteCompiledSqlObjs() {
+        createTableAndClearCache();
+        // set cache size
+        int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
+        mDatabase.setMaxSqlCacheSize(N);
+
+        // do N+1 queries - and when the 0th entry is removed from LRU cache due to the
+        // insertion of (N+1)th entry, make sure 0th entry is closed
+        ArrayList<Integer> stmtObjs = new ArrayList<Integer>();
+        ArrayList<String> sqlStrings = new ArrayList<String>();
+        int stmt0 = 0;
+        for (int i = 0; i < N+1; i++) {
+            String s = "insert into test values(" + i + ",?);";
+            sqlStrings.add(s);
+            ClassToTestSqlCompilationAndCaching c =
+                    ClassToTestSqlCompilationAndCaching.create(mDatabase, s);
+            int n = c.getSqlStatementId();
+            stmtObjs.add(i, n);
+            if (i == 0) {
+                // save the statementId of this obj. we want to make sure it is thrown out of
+                // the cache at the end of this test.
+                stmt0 = n;
+            }
+            c.close();
+        }
+        // is 0'th entry out of the cache? it should be in the list of statementIds
+        // corresponding to the pre-compiled sql statements to be finalized.
+        assertTrue(mDatabase.getQueuedUpStmtList().contains(stmt0));
+        for (int i = 1; i < N+1; i++) {
+            SQLiteCompiledSql compSql = mDatabase.getCompiledStatementForSql(sqlStrings.get(i));
+            assertNotNull(compSql);
+            assertTrue(stmtObjs.contains(compSql.nStatement));
+        }
+    }
+
+    @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);
+        statementDoNotClose.bindLong(1, 1);
+        /* do not close statementDoNotClose object.
+         * That should leave it in SQLiteDatabase.mPrograms.
+         * mDatabase.close() in tearDown() should release it.
+         */
+    }
+
+    private void createTableAndClearCache() {
+        mDatabase.disableWriteAheadLogging();
+        mDatabase.execSQL("DROP TABLE IF EXISTS " + TEST_TABLE);
+        mDatabase.execSQL("CREATE TABLE " + TEST_TABLE + " (i int, j int);");
+        mDatabase.enableWriteAheadLogging();
+        mDatabase.lock();
+        // flush the above statement from cache and close all the pending statements to be released
+        mDatabase.deallocCachedSqlStatements();
+        mDatabase.closePendingStatements();
+        mDatabase.unlock();
+        assertEquals(0, mDatabase.getQueuedUpStmtList().size());
+    }
+
+    /**
+     * test to make sure the statement finalizations are not done right away but
+     * piggy-backed onto the next sql statement execution on the same database.
+     */
+    @SmallTest
+    public void testStatementClose() {
+        createTableAndClearCache();
+        // fill up statement cache in mDatabase
+        int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
+        mDatabase.setMaxSqlCacheSize(N);
+        SQLiteStatement stmt;
+        int stmt0Id = 0;
+        for (int i = 0; i < N; i ++) {
+            ClassToTestSqlCompilationAndCaching c =
+                    ClassToTestSqlCompilationAndCaching.create(mDatabase,
+                            "insert into test values(" + i + ", ?);");
+            // keep track of 0th entry
+            if (i == 0) {
+                stmt0Id = c.getSqlStatementId();
+            }
+            c.close();
+        }
+
+        // add one more to the cache - and the above 'stmt0Id' should fall out of cache
+        ClassToTestSqlCompilationAndCaching stmt1 =
+                ClassToTestSqlCompilationAndCaching.create(mDatabase,
+                        "insert into test values(100, ?);");
+        stmt1.close();
+
+        // the above close() should have queuedUp the statement for finalization
+        ArrayList<Integer> statementIds = mDatabase.getQueuedUpStmtList();
+        assertTrue(statementIds.contains(stmt0Id));
+
+        // execute something to see if this statement gets finalized
+        mDatabase.execSQL("delete from test where i = 10;");
+        statementIds = mDatabase.getQueuedUpStmtList();
+        assertFalse(statementIds.contains(stmt0Id));
+    }
+
+    /**
+     * same as above - except that the statement to be finalized is from Thread # 1.
+     * and it is eventually finalized in Thread # 2 when it executes a SQL statement.
+     * @throws InterruptedException
+     */
+    @LargeTest
+    public void testStatementCloseDiffThread() throws InterruptedException {
+        createTableAndClearCache();
+        final int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
+        mDatabase.setMaxSqlCacheSize(N);
+        // fill up statement cache in mDatabase in a thread
+        Thread t1 = new Thread() {
+            @Override public void run() {
+                SQLiteStatement stmt;
+                for (int i = 0; i < N; i++) {
+                    ClassToTestSqlCompilationAndCaching c =
+                        ClassToTestSqlCompilationAndCaching.create(getDb(),
+                                "insert into test values(" + i + ", ?);");
+                    // keep track of 0th entry
+                    if (i == 0) {
+                        stmt0Id = c.getSqlStatementId();
+                    }
+                    c.close();
+                }
+            }
+        };
+        t1.start();
+        // wait for the thread to finish
+        t1.join();
+        // mDatabase shouldn't have any statements to be released
+        assertEquals(0, mDatabase.getQueuedUpStmtList().size());
+
+        // add one more to the cache - and the above 'stmt0Id' should fall out of cache
+        // just for the heck of it, do it in a separate thread
+        Thread t2 = new Thread() {
+            @Override public void run() {
+                ClassToTestSqlCompilationAndCaching stmt1 =
+                    ClassToTestSqlCompilationAndCaching.create(getDb(),
+                            "insert into test values(100, ?);");
+                stmt1.bindLong(1, 1);
+                stmt1.close();
+            }
+        };
+        t2.start();
+        t2.join();
+
+        // close() in the above thread should have queuedUp the stmt0Id for finalization
+        ArrayList<Integer> statementIds = getDb().getQueuedUpStmtList();
+        assertTrue(statementIds.contains(getStmt0Id()));
+        assertEquals(1, statementIds.size());
+
+        // execute something to see if this statement gets finalized
+        // again do it in a separate thread
+        Thread t3 = new Thread() {
+            @Override public void run() {
+                getDb().execSQL("delete from test where i = 10;");
+            }
+        };
+        t3.start();
+        t3.join();
+
+        // is the statement finalized?
+        statementIds = getDb().getQueuedUpStmtList();
+        assertFalse(statementIds.contains(getStmt0Id()));
+    }
+
+    private volatile int stmt0Id = 0;
+    private synchronized int getStmt0Id() {
+        return this.stmt0Id;
+    }
+
+    /**
+     * same as above - except that the queue of statements to be finalized are finalized
+     * by database close() operation.
+     */
+    @LargeTest
+    public void testStatementCloseByDbClose() throws InterruptedException {
+        createTableAndClearCache();
+        // fill up statement cache in mDatabase in a thread
+        Thread t1 = new Thread() {
+            @Override public void run() {
+                int N = SQLiteDatabase.MAX_SQL_CACHE_SIZE;
+                getDb().setMaxSqlCacheSize(N);
+                SQLiteStatement stmt;
+                for (int i = 0; i < N; i ++) {
+                    ClassToTestSqlCompilationAndCaching c =
+                            ClassToTestSqlCompilationAndCaching.create(getDb(),
+                                    "insert into test values(" + i + ", ?);");
+                    // keep track of 0th entry
+                    if (i == 0) {
+                        stmt0Id = c.getSqlStatementId();
+                    }
+                    c.close();
+                }
+            }
+        };
+        t1.start();
+        // wait for the thread to finish
+        t1.join();
+
+        // add one more to the cache - and the above 'stmt0Id' should fall out of cache
+        // just for the heck of it, do it in a separate thread
+        Thread t2 = new Thread() {
+            @Override public void run() {
+                ClassToTestSqlCompilationAndCaching stmt1 =
+                        ClassToTestSqlCompilationAndCaching.create(getDb(),
+                                "insert into test values(100, ?);");
+                stmt1.bindLong(1, 1);
+                stmt1.close();
+            }
+        };
+        t2.start();
+        t2.join();
+
+        // close() in the above thread should have queuedUp the statement for finalization
+        ArrayList<Integer> statementIds = getDb().getQueuedUpStmtList();
+        assertTrue(getStmt0Id() > 0);
+        assertTrue(statementIds.contains(stmt0Id));
+        assertEquals(1, statementIds.size());
+
+        // close the database. everything from mClosedStatementIds in mDatabase
+        // should be finalized and cleared from the list
+        // again do it in a separate thread
+        Thread t3 = new Thread() {
+            @Override public void run() {
+                getDb().close();
+            }
+        };
+        t3.start();
+        t3.join();
+
+        // check mClosedStatementIds in mDatabase. it should be empty
+        statementIds = getDb().getQueuedUpStmtList();
+        assertEquals(0, statementIds.size());
+    }
+
+    /**
+     * This test tests usage execSQL() to begin transaction works in the following way
+     *   Thread #1 does
+     *       execSQL("begin transaction");
+     *       insert()
+     *   Thread # 2
+     *       query()
+     *   Thread#1 ("end transaction")
+     * Thread # 2 query will execute - because java layer will not have locked the SQLiteDatabase
+     * object and sqlite will consider this query to be part of the transaction.
+     *
+     * but if thread # 1 uses beginTransaction() instead of execSQL() to start transaction,
+     * then Thread # 2's query will have been blocked by java layer
+     * until Thread#1 ends transaction.
+     *
+     * @throws InterruptedException
+     */
+    @SmallTest
+    public void testExecSqlToStartAndEndTransaction() throws InterruptedException {
+        runExecSqlToStartAndEndTransaction("END");
+        // same as above, instead now do "COMMIT" or "ROLLBACK" instead of "END" transaction
+        runExecSqlToStartAndEndTransaction("COMMIT");
+        runExecSqlToStartAndEndTransaction("ROLLBACK");
+    }
+    private void runExecSqlToStartAndEndTransaction(String str) throws InterruptedException {
+        createTableAndClearCache();
+        // disable WAL just so queries and updates use the same database connection
+        mDatabase.disableWriteAheadLogging();
+        mDatabase.execSQL("BEGIN transaction");
+        // even though mDatabase.beginTransaction() is not called to start transaction,
+        // mDatabase connection should now be in transaction as a result of
+        // mDatabase.execSQL("BEGIN transaction")
+        // but mDatabase.mLock should not be held by any thread
+        assertTrue(mDatabase.inTransaction());
+        assertFalse(mDatabase.isDbLockedByCurrentThread());
+        assertFalse(mDatabase.isDbLockedByOtherThreads());
+        assertTrue(mDatabase.amIInTransaction());
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        assertTrue(mDatabase.inTransaction());
+        assertFalse(mDatabase.isDbLockedByCurrentThread());
+        assertFalse(mDatabase.isDbLockedByOtherThreads());
+        assertTrue(mDatabase.amIInTransaction());
+        Thread t = new Thread() {
+            @Override public void run() {
+                assertTrue(mDatabase.amIInTransaction());
+                assertEquals(999, DatabaseUtils.longForQuery(getDb(),
+                        "select j from " + TEST_TABLE + " WHERE i = 10", null));
+                assertTrue(getDb().inTransaction());
+                assertFalse(getDb().isDbLockedByCurrentThread());
+                assertFalse(getDb().isDbLockedByOtherThreads());
+                assertTrue(mDatabase.amIInTransaction());
+            }
+        };
+        t.start();
+        t.join();
+        assertTrue(mDatabase.amIInTransaction());
+        assertTrue(mDatabase.inTransaction());
+        assertFalse(mDatabase.isDbLockedByCurrentThread());
+        assertFalse(mDatabase.isDbLockedByOtherThreads());
+        mDatabase.execSQL(str);
+        assertFalse(mDatabase.amIInTransaction());
+        assertFalse(mDatabase.inTransaction());
+        assertFalse(mDatabase.isDbLockedByCurrentThread());
+        assertFalse(mDatabase.isDbLockedByOtherThreads());
+    }
+
+    /**
+     * test the following
+     * http://b/issue?id=2871037
+     *          Cursor cursor = db.query(...);
+     *          // with WAL enabled, the above uses a pooled database connection
+     *          db.beginTransaction()
+     *          try {
+     *            db.insert(......);
+     *            cursor.requery();
+     *            // since the cursor uses pooled database connection, the above requery
+     *            // will not return the results that were inserted above since the insert is
+     *            // done using main database connection AND the transaction is not committed yet.
+     *            // fix is to make the above cursor use the main database connection - and NOT
+     *            // the pooled database connection
+     *            db.setTransactionSuccessful()
+     *          } finally {
+     *            db.endTransaction()
+     *          }
+     *
+     * @throws InterruptedException
+     */
+    @SmallTest
+    public void testTransactionAndWalInterplay1() throws InterruptedException {
+        createTableAndClearCache();
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        String sql = "select * from " + TEST_TABLE;
+        Cursor c = mDatabase.rawQuery(sql, null);
+        // should have 1 row in the table
+        assertEquals(1, c.getCount());
+        mDatabase.beginTransactionNonExclusive();
+        try {
+            mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(100, 9909);");
+            assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+                    "select count(*) from " + TEST_TABLE, null));
+            // requery on the previously opened cursor
+            // cursor should now use the main database connection and see 2 rows
+            c.requery();
+            assertEquals(2, c.getCount());
+            mDatabase.setTransactionSuccessful();
+        } finally {
+            mDatabase.endTransaction();
+        }
+        c.close();
+
+        // do the same test but now do the requery in a separate thread.
+        createTableAndClearCache();
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        final Cursor c1 = mDatabase.rawQuery("select count(*) from " + TEST_TABLE, null);
+        // should have 1 row in the table
+        assertEquals(1, c1.getCount());
+        mDatabase.beginTransactionNonExclusive();
+        try {
+            mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(100, 9909);");
+            assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+                    "select count(*) from " + TEST_TABLE, null));
+            // query in a different thread. that causes the cursor to use a pooled connection
+            // and since this thread hasn't committed its changes, the cursor should still see only
+            // 1 row
+            Thread t = new Thread() {
+                @Override public void run() {
+                    c1.requery();
+                    assertEquals(1, c1.getCount());
+                }
+            };
+            t.start();
+            t.join();
+            // should be 2 rows now - including the the row inserted above
+            mDatabase.setTransactionSuccessful();
+        } finally {
+            mDatabase.endTransaction();
+        }
+        c1.close();
+    }
+
+    /**
+     * This test is same as {@link #testTransactionAndWalInterplay1()} except the following:
+     * instead of mDatabase.beginTransactionNonExclusive(), use execSQL("BEGIN transaction")
+     * and instead of mDatabase.endTransaction(), use execSQL("END");
+     */
+    @SmallTest
+    public void testTransactionAndWalInterplay2() throws InterruptedException {
+        createTableAndClearCache();
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        String sql = "select * from " + TEST_TABLE;
+        Cursor c = mDatabase.rawQuery(sql, null);
+        // should have 1 row in the table
+        assertEquals(1, c.getCount());
+        mDatabase.execSQL("BEGIN transaction");
+        try {
+            mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(100, 9909);");
+            assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+                    "select count(*) from " + TEST_TABLE, null));
+            // requery on the previously opened cursor
+            // cursor should now use the main database connection and see 2 rows
+            c.requery();
+            assertEquals(2, c.getCount());
+        } finally {
+            mDatabase.execSQL("commit;");
+        }
+        c.close();
+
+        // do the same test but now do the requery in a separate thread.
+        createTableAndClearCache();
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        final Cursor c1 = mDatabase.rawQuery("select count(*) from " + TEST_TABLE, null);
+        // should have 1 row in the table
+        assertEquals(1, c1.getCount());
+        mDatabase.execSQL("BEGIN transaction");
+        try {
+            mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(100, 9909);");
+            assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+                    "select count(*) from " + TEST_TABLE, null));
+            // query in a different thread. but since the transaction is started using
+            // execSQ() instead of beginTransaction(), cursor's query is considered part of
+            // the same transaction - and hence it should see the above inserted row
+            Thread t = new Thread() {
+                @Override public void run() {
+                    c1.requery();
+                    assertEquals(1, c1.getCount());
+                }
+            };
+            t.start();
+            t.join();
+            // should be 2 rows now - including the the row inserted above
+        } finally {
+            mDatabase.execSQL("commit");
+        }
+        c1.close();
+    }
+
+    /**
+     * This test is same as {@link #testTransactionAndWalInterplay2()} except the following:
+     * instead of committing the data, do rollback and make sure the data seen by the query
+     * within the transaction is now gone.
+     */
+    @SmallTest
+    public void testTransactionAndWalInterplay3() {
+        createTableAndClearCache();
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        String sql = "select * from " + TEST_TABLE;
+        Cursor c = mDatabase.rawQuery(sql, null);
+        // should have 1 row in the table
+        assertEquals(1, c.getCount());
+        mDatabase.execSQL("BEGIN transaction");
+        try {
+            mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(100, 9909);");
+            assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+                    "select count(*) from " + TEST_TABLE, null));
+            // requery on the previously opened cursor
+            // cursor should now use the main database connection and see 2 rows
+            c.requery();
+            assertEquals(2, c.getCount());
+        } finally {
+            // rollback the change
+            mDatabase.execSQL("rollback;");
+        }
+        // since the change is rolled back, do the same query again and should now find only 1 row
+        c.requery();
+        assertEquals(1, c.getCount());
+        assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+                "select count(*) from " + TEST_TABLE, null));
+        c.close();
+    }
+
+    /**
+     * http://b/issue?id=2943028
+     * SQLiteOpenHelper maintains a Singleton even if it is in bad state.
+     */
+    @SmallTest
+    public void testCloseAndReopen() {
+        mDatabase.close();
+        TestOpenHelper helper = new TestOpenHelper(getContext(), DB_NAME, null,
+                CURRENT_DATABASE_VERSION, new DefaultDatabaseErrorHandler());
+        mDatabase = helper.getWritableDatabase();
+        createTableAndClearCache();
+        mDatabase.execSQL("INSERT into " + TEST_TABLE + " values(10, 999);");
+        Cursor c = mDatabase.query(TEST_TABLE, new String[]{"i", "j"}, null, null, null, null, null);
+        assertEquals(1, c.getCount());
+        c.close();
+        mDatabase.close();
+        assertFalse(mDatabase.isOpen());
+        mDatabase = helper.getReadableDatabase();
+        assertTrue(mDatabase.isOpen());
+        c = mDatabase.query(TEST_TABLE, new String[]{"i", "j"}, null, null, null, null, null);
+        assertEquals(1, c.getCount());
+        c.close();
+    }
+    private class TestOpenHelper extends SQLiteOpenHelper {
+        public TestOpenHelper(Context context, String name, CursorFactory factory, int version,
+                DatabaseErrorHandler errorHandler) {
+            super(context, name, factory, version, errorHandler);
+        }
+        @Override public void onCreate(SQLiteDatabase db) {}
+        @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
+    }
+}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteGeneralTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteGeneralTest.java
deleted file mode 100644
index af7ccce..0000000
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteGeneralTest.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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.sqlite;
-
-import android.content.Context;
-import android.test.AndroidTestCase;
-import android.test.FlakyTest;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import java.io.File;
-
-public class SQLiteGeneralTest extends AndroidTestCase {
-
-    private SQLiteDatabase mDatabase;
-    private File mDatabaseFile;
-    Boolean exceptionRecvd = false;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        exceptionRecvd = false;
-        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);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mDatabase.close();
-        mDatabaseFile.delete();
-        super.tearDown();
-    }
-
-    @LargeTest
-    public void testUseOfSameSqlStatementBy2Threads() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test_pstmt (i INTEGER PRIMARY KEY, j text);");
-
-        // thread 1 creates a prepared statement
-        final String stmt = "SELECT * FROM test_pstmt WHERE i = ?";
-
-        // start 2 threads to do repeatedly execute "stmt"
-        // since these 2 threads are executing the same sql, they each should get
-        // their own copy and
-        // there SHOULD NOT be an error from sqlite: "prepared statement is busy"
-        class RunStmtThread extends Thread {
-            private static final int N = 1000;
-            @Override public void run() {
-                int i = 0;
-                try {
-                    // execute many times
-                    for (i = 0; i < N; i++) {
-                        SQLiteStatement s1 = mDatabase.compileStatement(stmt);
-                        s1.bindLong(1, i);
-                        s1.execute();
-                        s1.close();
-                    }
-                } catch (SQLiteException e) {
-                    fail("SQLiteException: " + e.getMessage());
-                    return;
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    fail("random unexpected exception: " + e.getMessage());
-                    return;
-                }
-            }
-        }
-        RunStmtThread t1 = new RunStmtThread();
-        t1.start();
-        RunStmtThread t2 = new RunStmtThread();
-        t2.start();
-        while (t1.isAlive() || t2.isAlive()) {
-            Thread.sleep(1000);
-        }
-    }
-
-    @FlakyTest
-    public void testUseOfSamePreparedStatementBy2Threads() throws Exception {
-        mDatabase.execSQL("CREATE TABLE test_pstmt (i INTEGER PRIMARY KEY, j text);");
-
-        // thread 1 creates a prepared statement
-        final String stmt = "SELECT * FROM test_pstmt WHERE i = ?";
-        final SQLiteStatement s1 = mDatabase.compileStatement(stmt);
-
-        // start 2 threads to do repeatedly execute "stmt"
-        // since these 2 threads are executing the same prepared statement,
-        // should see an error from sqlite: "prepared statement is busy"
-        class RunStmtThread extends Thread {
-            private static final int N = 1000;
-            @Override public void run() {
-                int i = 0;
-                try {
-                    // execute many times
-                    for (i = 0; i < N; i++) {
-                        s1.bindLong(1, i);
-                        s1.execute();
-                    }
-                } catch (SQLiteException e) {
-                    // expect it
-                    assertTrue(e.getMessage().contains("library routine called out of sequence:"));
-                    exceptionRecvd = true;
-                    return;
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    fail("random unexpected exception: " + e.getMessage());
-                    return;
-                }
-            }
-        }
-        RunStmtThread t1 = new RunStmtThread();
-        t1.start();
-        RunStmtThread t2 = new RunStmtThread();
-        t2.start();
-        while (t1.isAlive() || t2.isAlive()) {
-            Thread.sleep(1000);
-        }
-        assertTrue(exceptionRecvd);
-    }
-}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java
new file mode 100644
index 0000000..955336a
--- /dev/null
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteStatementTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.sqlite;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.File;
+import java.util.Random;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class SQLiteStatementTest extends AndroidTestCase {
+    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);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    /**
+     * Start 2 threads to repeatedly execute the above SQL statement.
+     * Even though 2 threads are executing the same SQL, they each should get their own copy of
+     * prepared SQL statement id and there SHOULD NOT be an error from sqlite or android.
+     * @throws InterruptedException thrown if the test threads started by this test are interrupted
+     */
+    @LargeTest
+    public void testUseOfSameSqlStatementBy2Threads() throws InterruptedException {
+        mDatabase.execSQL("CREATE TABLE test_pstmt (i INTEGER PRIMARY KEY, j text);");
+        final String stmt = "SELECT * FROM test_pstmt WHERE i = ?";
+        class RunStmtThread extends Thread {
+            @Override public void run() {
+                // do it enough times to make sure there are no corner cases going untested
+                for (int i = 0; i < 1000; i++) {
+                    SQLiteStatement s1 = mDatabase.compileStatement(stmt);
+                    s1.bindLong(1, i);
+                    s1.execute();
+                    s1.close();
+                 }
+            }
+        }
+        RunStmtThread t1 = new RunStmtThread();
+        t1.start();
+        RunStmtThread t2 = new RunStmtThread();
+        t2.start();
+         while (t1.isAlive() || t2.isAlive()) {
+             Thread.sleep(10);
+         }
+     }
+
+    /**
+     * A simple test: start 2 threads to repeatedly execute the same {@link SQLiteStatement}.
+     * The 2 threads take turns to use the {@link SQLiteStatement}; i.e., it is NOT in use
+     * by both the threads at the same time.
+     *
+     * @throws InterruptedException thrown if the test threads started by this test are interrupted
+     */
+    @LargeTest
+    public void testUseOfSameSqliteStatementBy2Threads() throws InterruptedException {
+        mDatabase.execSQL("CREATE TABLE test_pstmt (i INTEGER PRIMARY KEY, j text);");
+        final String stmt = "SELECT * FROM test_pstmt WHERE i = ?";
+        final SQLiteStatement s1 = mDatabase.compileStatement(stmt);
+        class RunStmtThread extends Thread {
+            @Override public void run() {
+                // do it enough times to make sure there are no corner cases going untested
+                for (int i = 0; i < 1000; i++) {
+                    lock();
+                    try {
+                        s1.bindLong(1, i);
+                        s1.execute();
+                    } finally {
+                        unlock();
+                    }
+                    Thread.yield();
+                }
+            }
+        }
+        RunStmtThread t1 = new RunStmtThread();
+        t1.start();
+        RunStmtThread t2 = new RunStmtThread();
+        t2.start();
+        while (t1.isAlive() || t2.isAlive()) {
+            Thread.sleep(10);
+        }
+    }
+    /** Synchronize on this when accessing the SqliteStatemet in the above */
+    private final ReentrantLock mLock = new ReentrantLock(true);
+    private void lock() {
+        mLock.lock();
+    }
+    private void unlock() {
+        mLock.unlock();
+    }
+
+    /**
+     * Tests the following: a {@link SQLiteStatement} object should not refer to a
+     * pre-compiled SQL statement id except in during the period of binding the arguments
+     * and executing the SQL statement.
+     */
+    @LargeTest
+    public void testReferenceToPrecompiledStatementId() {
+        mDatabase.execSQL("create table t (i int, j text);");
+        verifyReferenceToPrecompiledStatementId(false);
+        verifyReferenceToPrecompiledStatementId(true);
+
+        // a small stress test to make sure there are no side effects of
+        // the acquire & release of pre-compiled statement id by SQLiteStatement object.
+        for (int i = 0; i < 100; i++) {
+            verifyReferenceToPrecompiledStatementId(false);
+            verifyReferenceToPrecompiledStatementId(true);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private void verifyReferenceToPrecompiledStatementId(boolean wal) {
+        if (wal) {
+            mDatabase.enableWriteAheadLogging();
+        } else {
+            mDatabase.disableWriteAheadLogging();
+        }
+        // test with INSERT statement - doesn't use connection pool, if WAL is set
+        SQLiteStatement stmt = mDatabase.compileStatement("insert into t values(?,?);");
+        assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+        assertEquals(mDatabase, stmt.mDatabase);
+        // sql statement should not be compiled yet
+        assertEquals(0, stmt.nStatement);
+        assertEquals(0, stmt.getSqlStatementId());
+        int colValue = new Random().nextInt();
+        stmt.bindLong(1, colValue);
+        // verify that the sql statement is still not compiled
+        assertEquals(0, stmt.getSqlStatementId());
+        // should still be using the mDatabase connection - verify
+        assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+        assertEquals(mDatabase, stmt.mDatabase);
+        stmt.bindString(2, "blah" + colValue);
+        // verify that the sql statement is still not compiled
+        assertEquals(0, stmt.getSqlStatementId());
+        assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+        assertEquals(mDatabase, stmt.mDatabase);
+        stmt.executeInsert();
+        // now that the statement is executed, pre-compiled statement should be released
+        assertEquals(0, stmt.nStatement);
+        assertEquals(0, stmt.getSqlStatementId());
+        assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+        assertEquals(mDatabase, stmt.mDatabase);
+        stmt.close();
+        // pre-compiled SQL statement should still remain released from this object
+        assertEquals(0, stmt.nStatement);
+        assertEquals(0, stmt.getSqlStatementId());
+        // but the database handle should still be the same
+        assertEquals(mDatabase, stmt.mDatabase);
+
+        // test with a SELECT statement - uses connection pool if WAL is set
+        stmt = mDatabase.compileStatement("select i from t where j=?;");
+        // sql statement should not be compiled yet
+        assertEquals(0, stmt.nStatement);
+        assertEquals(0, stmt.getSqlStatementId());
+        assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+        assertEquals(mDatabase, stmt.mDatabase);
+        stmt.bindString(1, "blah" + colValue);
+        // verify that the sql statement is still not compiled
+        assertEquals(0, stmt.nStatement);
+        assertEquals(0, stmt.getSqlStatementId());
+        assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+        assertEquals(mDatabase, stmt.mDatabase);
+        // execute the statement
+        Long l = stmt.simpleQueryForLong();
+        assertEquals(colValue, l.intValue());
+        // now that the statement is executed, pre-compiled statement should be released
+        assertEquals(0, stmt.nStatement);
+        assertEquals(0, stmt.getSqlStatementId());
+        assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+        assertEquals(mDatabase, stmt.mDatabase);
+        stmt.close();
+        // pre-compiled SQL statement should still remain released from this object
+        assertEquals(0, stmt.nStatement);
+        assertEquals(0, stmt.getSqlStatementId());
+        // but the database handle should still remain attached to the statement
+        assertEquals(mDatabase.mNativeHandle, stmt.nHandle);
+        assertEquals(mDatabase, stmt.mDatabase);
+    }
+}
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteUnfinalizedExceptionTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteUnfinalizedExceptionTest.java
new file mode 100644
index 0000000..cd2005d
--- /dev/null
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteUnfinalizedExceptionTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabaseTest.ClassToTestSqlCompilationAndCaching;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.File;
+
+public class SQLiteUnfinalizedExceptionTest extends AndroidTestCase {
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+    private static final String TABLE_NAME = "testCursor";
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
+        mDatabaseFile = new File(dbDir, "UnfinalizedExceptionTest.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();
+    }
+
+    @SmallTest
+    public void testUnfinalizedExceptionNotExcpected() {
+        mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
+        // the above statement should be in SQLiteDatabase.mPrograms
+        // and should automatically be finalized when database is closed
+        mDatabase.lock();
+        try {
+            mDatabase.closeDatabase();
+        } finally {
+            mDatabase.unlock();
+        }
+    }
+
+    @SmallTest
+    public void testUnfinalizedException() {
+        mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (i int, j int);");
+        mDatabase.lock();
+        mDatabase.closePendingStatements(); // clears the above from finalizer queue in mdatabase
+        mDatabase.unlock();
+        ClassToTestSqlCompilationAndCaching.create(mDatabase, "select * from "  + TABLE_NAME);
+        // since the above is NOT closed, closing database should fail
+        mDatabase.lock();
+        try {
+            mDatabase.closeDatabase();
+            fail("exception expected");
+        } catch (SQLiteUnfinalizedObjectsException e) {
+            // expected
+        } finally {
+            mDatabase.unlock();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/net/LinkSocketTest.java b/core/tests/coretests/src/android/net/LinkSocketTest.java
new file mode 100644
index 0000000..af77d63
--- /dev/null
+++ b/core/tests/coretests/src/android/net/LinkSocketTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.LinkSocket;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+/**
+ * Test LinkSocket
+ */
+public class LinkSocketTest extends TestCase {
+
+    @SmallTest
+    public void testBasic() throws Exception {
+        LinkSocket ls;
+
+        ls = new LinkSocket();
+        ls.close();
+    }
+
+    @SmallTest
+    public void testLinkCapabilities() throws Exception {
+        LinkCapabilities lc;
+
+        lc = new LinkCapabilities();
+        assertEquals(0, lc.size());
+        assertEquals(true, lc.isEmpty());
+    }
+}
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index a5fda20..c8ad60d 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -24,6 +24,9 @@
 
 import java.io.File;
 import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
 
 public class UriTest extends TestCase {
 
@@ -52,20 +55,20 @@
 
     private void parcelAndUnparcel(Uri u) {
         Parcel p = Parcel.obtain();
-	try {
-		Uri.writeToParcel(p, u);
-		p.setDataPosition(0);
-		assertEquals(u, Uri.CREATOR.createFromParcel(p));
+        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();
-	}
+            p.setDataPosition(0);
+            u = u.buildUpon().build();
+            Uri.writeToParcel(p, u);
+            p.setDataPosition(0);
+            assertEquals(u, Uri.CREATOR.createFromParcel(p));
+        }
+        finally {
+            p.recycle();
+        }
     }
 
     @SmallTest
@@ -282,6 +285,14 @@
         assertEquals("d", uri.getQueryParameter("c"));
     }
 
+    // http://b/2337042
+    @SmallTest
+    public void testHostWithTrailingDot() {
+        Uri uri = Uri.parse("http://google.com./b/c/g");
+        assertEquals("google.com.", uri.getHost());
+        assertEquals("/b/c/g", uri.getPath());
+    }
+
     @SmallTest
     public void testSchemeOnly() {
         Uri uri = Uri.parse("empty:");
@@ -603,4 +614,122 @@
         assertEquals("", uri.getQueryParameter("b"));
         assertEquals("", uri.getQueryParameter("c"));
     }
+
+    public void testGetQueryParameterEmptyKey() {
+        Uri uri = Uri.parse("http://www.google.com/?=b");
+        assertEquals("b", uri.getQueryParameter(""));
+    }
+
+    public void testGetQueryParameterEmptyKey2() {
+      Uri uri = Uri.parse("http://www.google.com/?a=b&&c=d");
+      assertEquals("", uri.getQueryParameter(""));
+    }
+
+    public void testGetQueryParameterEmptyKey3() {
+      Uri uri = Uri.parse("http://www.google.com?");
+      assertEquals("", uri.getQueryParameter(""));
+    }
+
+    public void testGetQueryParameterEmptyKey4() {
+      Uri uri = Uri.parse("http://www.google.com?a=b&");
+      assertEquals("", uri.getQueryParameter(""));
+    }
+
+    public void testGetQueryParametersEmptyKey() {
+        Uri uri = Uri.parse("http://www.google.com/?=b&");
+        List<String> values = uri.getQueryParameters("");
+        assertEquals(2, values.size());
+        assertEquals("b", values.get(0));
+        assertEquals("", values.get(1));
+    }
+
+    public void testGetQueryParametersEmptyKey2() {
+        Uri uri = Uri.parse("http://www.google.com?");
+        List<String> values = uri.getQueryParameters("");
+        assertEquals(1, values.size());
+        assertEquals("", values.get(0));
+    }
+
+    public void testGetQueryParametersEmptyKey3() {
+      Uri uri = Uri.parse("http://www.google.com/?a=b&&c=d");
+      List<String> values = uri.getQueryParameters("");
+      assertEquals(1, values.size());
+      assertEquals("", values.get(0));
+    }
+
+    public void testGetQueryParameterNames() {
+        Uri uri = Uri.parse("http://test?a=1");
+        Set<String> names = uri.getQueryParameterNames();
+        assertEquals(1, names.size());
+        assertEquals("a", names.iterator().next());
+    }
+
+    public void testGetQueryParameterNamesEmptyKey() {
+        Uri uri = Uri.parse("http://www.google.com/?a=x&&c=z");
+        Set<String> names = uri.getQueryParameterNames();
+        Iterator<String> iter = names.iterator();
+        assertEquals(3, names.size());
+        assertEquals("a", iter.next());
+        assertEquals("", iter.next());
+        assertEquals("c", iter.next());
+    }
+
+    public void testGetQueryParameterNamesEmptyKey2() {
+        Uri uri = Uri.parse("http://www.google.com/?a=x&=d&c=z");
+        Set<String> names = uri.getQueryParameterNames();
+        Iterator<String> iter = names.iterator();
+        assertEquals(3, names.size());
+        assertEquals("a", iter.next());
+        assertEquals("", iter.next());
+        assertEquals("c", iter.next());
+    }
+
+    public void testGetQueryParameterNamesEmptyValues() {
+        Uri uri = Uri.parse("http://www.google.com/?a=foo&b=&c=");
+        Set<String> names = uri.getQueryParameterNames();
+        Iterator<String> iter = names.iterator();
+        assertEquals(3, names.size());
+        assertEquals("a", iter.next());
+        assertEquals("b", iter.next());
+        assertEquals("c", iter.next());
+    }
+
+    public void testGetQueryParameterNamesEdgeCases() {
+        Uri uri = Uri.parse("http://foo?a=bar&b=bar&c=&&d=baz&e&f&g=buzz&&&a&b=bar&h");
+        Set<String> names = uri.getQueryParameterNames();
+        Iterator<String> iter = names.iterator();
+        assertEquals(9, names.size());
+        assertEquals("a", iter.next());
+        assertEquals("b", iter.next());
+        assertEquals("c", iter.next());
+        assertEquals("", iter.next());
+        assertEquals("d", iter.next());
+        assertEquals("e", iter.next());
+        assertEquals("f", iter.next());
+        assertEquals("g", iter.next());
+        assertEquals("h", iter.next());
+    }
+
+    public void testGetQueryParameterNamesEscapedKeys() {
+        Uri uri = Uri.parse("http://www.google.com/?a%20b=foo&c%20d=");
+        Set<String> names = uri.getQueryParameterNames();
+        assertEquals(2, names.size());
+        Iterator<String> iter = names.iterator();
+        assertEquals("a b", iter.next());
+        assertEquals("c d", iter.next());
+    }
+
+    public void testGetQueryParameterEscapedKeys() {
+        Uri uri = Uri.parse("http://www.google.com/?a%20b=foo&c%20d=");
+        String value = uri.getQueryParameter("a b");
+        assertEquals("foo", value);
+    }
+    
+    public void testClearQueryParameters() {
+        Uri uri = Uri.parse("http://www.google.com/?a=x&b=y&c=z").buildUpon()
+            .clearQuery().appendQueryParameter("foo", "bar").build();
+        Set<String> names = uri.getQueryParameterNames();
+        assertEquals(1, names.size());
+        assertEquals("foo", names.iterator().next());
+    }
 }
diff --git a/core/tests/coretests/src/android/net/WebAddressTest.java b/core/tests/coretests/src/android/net/WebAddressTest.java
new file mode 100644
index 0000000..6fcb97e
--- /dev/null
+++ b/core/tests/coretests/src/android/net/WebAddressTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.WebAddress;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+public class WebAddressTest extends TestCase {
+
+    // http://b/2337042
+    @SmallTest
+    public void testHostWithTrailingDot() {
+        WebAddress webAddress = new WebAddress("http://google.com./b/c/g");
+        assertEquals("google.com.", webAddress.getHost());
+        assertEquals("/b/c/g", webAddress.getPath());
+    }
+
+    // http://b/1011602
+    @SmallTest
+    public void testPathWithoutLeadingSlash() {
+        WebAddress webAddress = new WebAddress("http://www.myspace.com?si=1");
+        assertEquals("www.myspace.com", webAddress.getHost());
+        assertEquals("/?si=1", webAddress.getPath());
+    }
+}
diff --git a/core/tests/coretests/src/android/os/AsyncChannelTest.java b/core/tests/coretests/src/android/os/AsyncChannelTest.java
new file mode 100644
index 0000000..43c8290
--- /dev/null
+++ b/core/tests/coretests/src/android/os/AsyncChannelTest.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+/**
+ * Test for AsyncChannel.
+ */
+public class AsyncChannelTest extends TestCase {
+    private static final boolean DBG = true;
+    private static final boolean WAIT_FOR_DEBUGGER = true;
+    private static final String TAG = "AsyncChannelTest";
+
+    @SmallTest
+    public void test1() throws Exception {
+        if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
+        Log.d(TAG, "test1");
+        assertTrue(1 == 1);
+    }
+}
diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java
index 009a0e2..e627bb4 100644
--- a/core/tests/coretests/src/android/os/MemoryFileTest.java
+++ b/core/tests/coretests/src/android/os/MemoryFileTest.java
@@ -237,51 +237,6 @@
         }
     }
 
-    @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,
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
index b7c2d1f..f82bfce 100644
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -41,6 +41,10 @@
         }
 
         public void handleMessage(Message msg) {
+            if (!msg.isInUse()) {
+                failure(new RuntimeException(
+                        "msg.isInuse is false, should always be true, #" + msg.what));
+            }
             if (mCount <= mLastMessage) {
                 if (msg.what != mCount) {
                     failure(new RuntimeException(
@@ -99,5 +103,174 @@
 
         tester.doTest(1000);
     }
-}
 
+    private static class TestFieldIntegrityHandler extends TestHandlerThread {
+        Handler mHandler;
+        int mLastMessage;
+        int mCount;
+
+        public TestFieldIntegrityHandler() {
+        }
+
+        public void go() {
+            mHandler = new Handler() {
+                public void handleMessage(Message msg) {
+                    TestFieldIntegrityHandler.this.handleMessage(msg);
+                }
+            };
+        }
+
+        public void handleMessage(Message msg) {
+            if (!msg.isInUse()) {
+                failure(new RuntimeException(
+                        "msg.isInuse is false, should always be true, #" + msg.what));
+            }
+            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 testFieldIntegrity() throws Exception {
+
+        TestHandlerThread tester = new TestFieldIntegrityHandler() {
+            Bundle mBundle;
+
+            public void go() {
+                super.go();
+                mLastMessage = 1;
+                mCount = 0;
+                mHandler.sendMessage(mHandler.obtainMessage(0));
+            }
+
+            public void handleMessage(Message msg) {
+                super.handleMessage(msg);
+                if (msg.what == 0) {
+                    msg.flags = -1;
+                    msg.what = 1;
+                    msg.arg1 = 456;
+                    msg.arg2 = 789;
+                    msg.obj = this;
+                    msg.replyTo = null;
+                    mBundle = new Bundle();
+                    msg.data = mBundle;
+                    msg.data.putString("key", "value");
+
+                    Message newMsg = mHandler.obtainMessage();
+                    newMsg.copyFrom(msg);
+                    if (newMsg.isInUse() != false) {
+                        failure(new RuntimeException(
+                                "newMsg.isInUse is true should be false after copyFrom"));
+                    }
+                    if (newMsg.flags != 0) {
+                        failure(new RuntimeException(String.format(
+                        "newMsg.flags is %d should be 0 after copyFrom", newMsg.flags)));
+                    }
+                    if (newMsg.what != 1) {
+                        failure(new RuntimeException(String.format(
+                                "newMsg.what is %d should be %d after copyFrom", newMsg.what, 1)));
+                    }
+                    if (newMsg.arg1 != 456) {
+                        failure(new RuntimeException(String.format(
+                                "newMsg.arg1 is %d should be %d after copyFrom", msg.arg1, 456)));
+                    }
+                    if (newMsg.arg2 != 789) {
+                        failure(new RuntimeException(String.format(
+                                "newMsg.arg2 is %d should be %d after copyFrom", msg.arg2, 789)));
+                    }
+                    if (newMsg.obj != this) {
+                        failure(new RuntimeException(
+                                "newMsg.obj should be 'this' after copyFrom"));
+                    }
+                    if (newMsg.replyTo != null) {
+                        failure(new RuntimeException(
+                                "newMsg.replyTo should be null after copyFrom"));
+                    }
+                    if (newMsg.data == mBundle) {
+                        failure(new RuntimeException(
+                                "newMsg.data should NOT be mBundle after copyFrom"));
+                    }
+                    if (!newMsg.data.getString("key").equals(mBundle.getString("key"))) {
+                        failure(new RuntimeException(String.format(
+                                "newMsg.data.getString(\"key\") is %s and does not equal" +
+                                " mBundle.getString(\"key\") which is %s after copyFrom",
+                                newMsg.data.getString("key"),  mBundle.getString("key"))));
+                    }
+                    if (newMsg.when != 0) {
+                        failure(new RuntimeException(String.format(
+                                "newMsg.when is %d should be 0 after copyFrom", newMsg.when)));
+                    }
+                    if (newMsg.target != mHandler) {
+                        failure(new RuntimeException(
+                                "newMsg.target is NOT mHandler after copyFrom"));
+                    }
+                    if (newMsg.callback != null) {
+                        failure(new RuntimeException(
+                                "newMsg.callback is NOT null after copyFrom"));
+                    }
+
+                    mHandler.sendMessage(newMsg);
+                } else if (msg.what == 1) {
+                    if (msg.isInUse() != true) {
+                        failure(new RuntimeException(String.format(
+                                "msg.isInUse is false should be true after when processing %d",
+                                msg.what)));
+                    }
+                    if (msg.arg1 != 456) {
+                        failure(new RuntimeException(String.format(
+                                "msg.arg1 is %d should be %d when processing # %d",
+                                msg.arg1, 456, msg.what)));
+                    }
+                    if (msg.arg2 != 789) {
+                        failure(new RuntimeException(String.format(
+                                "msg.arg2 is %d should be %d when processing # %d",
+                                msg.arg2, 789, msg.what)));
+                    }
+                    if (msg.obj != this) {
+                        failure(new RuntimeException(String.format(
+                                "msg.obj should be 'this' when processing # %d", msg.what)));
+                    }
+                    if (msg.replyTo != null) {
+                        failure(new RuntimeException(String.format(
+                                "msg.replyTo should be null when processing # %d", msg.what)));
+                    }
+                    if (!msg.data.getString("key").equals(mBundle.getString("key"))) {
+                        failure(new RuntimeException(String.format(
+                                "msg.data.getString(\"key\") is %s and does not equal" +
+                                " mBundle.getString(\"key\") which is %s when processing # %d",
+                                msg.data.getString("key"),  mBundle.getString("key"), msg.what)));
+                    }
+                    if (msg.when != 0) {
+                        failure(new RuntimeException(String.format(
+                                "msg.when is %d should be 0 when processing # %d",
+                                msg.when, msg.what)));
+                    }
+                    if (msg.target != null) {
+                        failure(new RuntimeException(String.format(
+                                "msg.target is NOT null when processing # %d", msg.what)));
+                    }
+                    if (msg.callback != null) {
+                        failure(new RuntimeException(String.format(
+                                "msg.callback is NOT null when processing # %d", msg.what)));
+                    }
+                } else {
+                    failure(new RuntimeException(String.format(
+                            "Unexpected msg.what is %d" + msg.what)));
+                }
+            }
+        };
+
+        tester.doTest(1000);
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java
deleted file mode 100644
index 2bec462..0000000
--- a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java
+++ /dev/null
@@ -1,1223 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.pim.vcard;
-
-import android.content.ContentValues;
-import android.pim.vcard.test_utils.ContactEntry;
-import android.pim.vcard.test_utils.PropertyNodesVerifierElem;
-import android.pim.vcard.test_utils.PropertyNodesVerifierElem.TypeSet;
-import android.pim.vcard.test_utils.VCardTestsBase;
-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 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) {
-        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.DISPLAY_NAME, "DISPLAY NAME");
-
-        mVerifier.addPropertyNodesVerifierElem()
-                .addExpectedNodeWithOrder("N",
-                        "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
-                        + "AppropriatePrefix;AppropriateSuffix",
-                        Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
-                                "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
-                .addExpectedNodeWithOrder("FN", "DISPLAY NAME");
-    }
-
-    public void testStructuredNameBasicV21() {
-        testStructuredNameBasic(V21);
-    }
-
-    public void testStructuredNameBasicV30() {
-        testStructuredNameBasic(V30);
-    }
-
-    public void testStructuredNameBasicV40() {
-        testStructuredNameBasic(V40);
-    }
-
-    /**
-     * 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) {
-        mVerifier.initForExportTest(vcardType);
-        final 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.DISPLAY_NAME, "DoNotEmitDisplayName1");
-
-        // 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.DISPLAY_NAME, "AppropriateDisplayName")
-                .put(StructuredName.IS_PRIMARY, 1);
-
-        // With "IS_PRIMARY=1", but we should ignore this time, since this is second, not first.
-        // vCard 2.1 does not specify anything about the number of N properties. We choose not
-        // emitting this property.
-        // vCard 3.0 does (There must be one N property)
-        // vCard 4.0 (rev13) does (cardinality (0, 1)).
-        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.DISPLAY_NAME, "DoNotEmitDisplayName2")
-                .put(StructuredName.IS_PRIMARY, 1);
-
-       mVerifier.addPropertyNodesVerifierElem()
-                .addExpectedNodeWithOrder("N",
-                        "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
-                        + "AppropriatePrefix;AppropriateSuffix",
-                        Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
-                                "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
-                .addExpectedNodeWithOrder("FN", "AppropriateDisplayName");
-    }
-
-    public void testStructuredNameUsePrimaryV21() {
-        testStructuredNameUsePrimaryCommon(V21);
-    }
-
-    public void testStructuredNameUsePrimaryV30() {
-        testStructuredNameUsePrimaryCommon(V30);
-    }
-
-    public void testStructuredNameUsePrimaryV40() {
-        testStructuredNameUsePrimaryCommon(V40);
-    }
-
-    /**
-     * Tests that only "super primary" StructuredName is emitted.
-     * See also the comment in {@link #testStructuredNameUsePrimaryCommon(int)}.
-     */
-    private void testStructuredNameUseSuperPrimaryCommon(int vcardType) {
-        mVerifier.initForExportTest(vcardType);
-        final 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.DISPLAY_NAME, "DoNotEmitDisplay1");
-
-        // 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.DISPLAY_NAME, "DoNotEmitDisplay2")
-                .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.DISPLAY_NAME, "AppropriateDisplayName")
-                .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.DISPLAY_NAME, "DoNotEmitDisplay3")
-                .put(StructuredName.IS_PRIMARY, 1);
-
-        final PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem();
-        elem.addExpectedNodeWithOrder("N",
-                "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
-                + "AppropriatePrefix;AppropriateSuffix",
-                Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
-                        "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"));
-
-        elem.addExpectedNodeWithOrder("FN", "AppropriateDisplayName");
-    }
-
-    public void testStructuredNameUseSuperPrimaryV21() {
-        testStructuredNameUseSuperPrimaryCommon(V21);
-    }
-
-    public void testStructuredNameUseSuperPrimaryV30() {
-        testStructuredNameUseSuperPrimaryCommon(V30);
-    }
-
-    public void testStructuredNameUseSuperPrimaryV40() {
-        testStructuredNameUseSuperPrimaryCommon(V40);
-    }
-
-    /**
-     * Tests phonetic names field are handled correctly.
-     *
-     * vCard 2.1 does not have any field corresponding to them.
-     * vCard 3.0 has SORT-STRING property, which does not support multiple values inside it.
-     * vCard 4.0 (rev13) has SORT-AS parameter, which has three values (family, given, middle)
-     * inside it.
-     */
-    private void testStructuredNamePhoneticNameCommon(int vcardType) {
-        mVerifier.initForExportTest(vcardType);
-        final ContactEntry entry = mVerifier.addInputEntry();
-        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.DISPLAY_NAME, "AppropriateDisplayName")
-                .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
-                .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
-                .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle");
-
-        final PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem();
-        if (VCardConfig.isVersion40(vcardType)) {
-            final ContentValues contentValues = new ContentValues();
-            contentValues.put("SORT-AS",
-                    "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName");
-            // vCard 4.0 (rev13) now uses SORT-AS parameter, which is not compatible with
-            // either 2.1 nor 3.0.
-            elem.addExpectedNodeWithOrder("N",
-                    "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
-                    + "AppropriatePrefix;AppropriateSuffix",
-                    Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
-                            "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"),
-                    contentValues);
-        } else {
-            elem.addExpectedNodeWithOrder("N",
-                    "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
-                    + "AppropriatePrefix;AppropriateSuffix",
-                    Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
-                            "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"));
-            if (VCardConfig.isVersion30(vcardType)) {
-                elem.addExpectedNode("SORT-STRING",
-                        "AppropriatePhoneticGiven AppropriatePhoneticMiddle"
-                        + " AppropriatePhoneticFamily");
-            }
-        }
-
-        elem.addExpectedNodeWithOrder("FN", "AppropriateDisplayName")
-            .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
-            .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
-            .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
-    }
-
-    public void testStructuredNamePhoneticNameV21() {
-        testStructuredNamePhoneticNameCommon(V21);
-    }
-
-    public void testStructuredNamePhoneticNameV30() {
-        testStructuredNamePhoneticNameCommon(V30);
-    }
-
-    public void testStructuredNamePhoneticNameV40() {
-        testStructuredNamePhoneticNameCommon(V40);
-    }
-
-    // TODO: need to add test cases confirming escaping, empty values, etc.
-
-    /**
-     * Confirms all the other sides of the handling is correctly interpreted at one time.
-     *
-     * A kind of regression test for StructuredName handling.
-     */
-    private void testStructuredNameComplicatedCommon(int vcardType) {
-        mVerifier.initForExportTest(vcardType);
-        final 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);
-
-        final PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem();
-        if (VCardConfig.isVersion40(vcardType)) {
-            final ContentValues contentValues = new ContentValues();
-            contentValues.put("SORT-AS",
-                    "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName");
-            // vCard 4.0 (rev13) now uses SORT-AS parameter, which is not compatible with
-            // either 2.1 nor 3.0.
-            elem.addExpectedNodeWithOrder("N",
-                    "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
-                    + "AppropriatePrefix;AppropriateSuffix",
-                    Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
-                            "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"),
-                    contentValues);
-        } else {
-            elem.addExpectedNodeWithOrder("N",
-                    "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
-                    + "AppropriatePrefix;AppropriateSuffix",
-                    Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
-                            "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"));
-            if (VCardConfig.isVersion30(vcardType)) {
-                elem.addExpectedNode("SORT-STRING",
-                        "AppropriatePhoneticGiven AppropriatePhoneticMiddle"
-                        + " AppropriatePhoneticFamily");
-            }
-        }
-
-        elem.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");
-    }
-
-    public void testStructuredNameComplicatedV21() {
-        testStructuredNameComplicatedCommon(V21);
-    }
-
-    public void testStructuredNameComplicatedV30() {
-        testStructuredNameComplicatedCommon(V30);
-    }
-
-    public void testStructuredNameComplicatedV40() {
-        testStructuredNameComplicatedCommon(V40);
-    }
-
-    public void testNickNameV30() {
-        mVerifier.initForExportTest(V30);
-        mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
-                .put(Nickname.NAME, "Nicky");
-
-        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
-            .addExpectedNodeWithOrder("NICKNAME", "Nicky");
-    }
-
-    public void testNickNameV40() {
-        mVerifier.initForExportTest(V40);
-        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);
-    }
-
-    public void testPhoneBasicV40() {
-        testPhoneBasicCommon(V40);
-    }
-
-    public void testPhoneRefrainFormatting() {
-        mVerifier.initForExportTest(V21 | VCardConfig.FLAG_REFRAIN_PHONE_NUMBER_FORMATTING);
-        mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE)
-                .put(Phone.NUMBER, "1234567890(abcdefghijklmnopqrstuvwxyz)")
-                .put(Phone.TYPE, Phone.TYPE_HOME);
-        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
-                .addExpectedNode("TEL", "1234567890(abcdefghijklmnopqrstuvwxyz)",
-                        new TypeSet("HOME"));
-    }
-
-    /**
-     * 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);
-    }
-
-    public void testPhoneVariousTypeSupportV40() {
-        testPhoneVariousTypeSupport(V40);
-    }
-
-    /**
-     * 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);
-    }
-
-    public void testPhonePrefHandlingV40() {
-        testPhonePrefHandlingCommon(V40);
-    }
-
-    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();
-        if (VCardConfig.isVersion30(vcardType) || VCardConfig.isVersion40(vcardType)) {
-            // vCard 3.0 accepts "invalid". Also stop using toUpper()
-            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("invalid"));
-        } else {
-            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);
-    }
-
-    public void testPhoneTypeHandlingV40() {
-        testMiscPhoneTypeHandling(V40);
-    }
-
-    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);
-    }
-
-    public void testEmailBasicV40() {
-        testEmailBasicCommon(V40);
-    }
-
-    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);
-    }
-
-    public void testEmailVariousTypeSupportV40() {
-        testEmailVariousTypeSupportCommon(V40);
-    }
-
-    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);
-    }
-
-    public void testEmailPrefHandlingV40() {
-        testEmailPrefHandlingCommon(V40);
-    }
-
-    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);
-    }
-
-    public void testPostalAddressV40() {
-        testPostalAddressCommon(V40);
-    }
-
-    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);
-    }
-
-    public void testPostalAddressNonNeighborhoodV40() {
-        testPostalAddressNonNeighborhood(V40);
-    }
-
-    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);
-    }
-
-    public void testPostalAddressNonCityV40() {
-        testPostalAddressNonCity(V40);
-    }
-
-    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);
-    }
-
-    public void testPostalOnlyWithFormattedAddressV40() {
-        testPostalOnlyWithFormattedAddressCommon(V40);
-    }
-
-    /**
-     * 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);
-    }
-
-    public void testPostalWithBothStructuredAndFormattedV40() {
-        testPostalWithBothStructuredAndFormattedCommon(V40);
-    }
-
-    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);
-    }
-
-    public void testOrganizationV40() {
-        testOrganizationCommon(V40);
-    }
-
-    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);
-    }
-
-    public void testImBasicV40() {
-        testImVariousTypeSupportCommon(V40);
-    }
-
-    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);
-    }
-
-    public void testImPrefHandlingV40() {
-        testImPrefHandlingCommon(V40);
-    }
-
-    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);
-    }
-
-    public void testWebsiteV40() {
-        testWebsiteCommon(V40);
-    }
-
-    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);
-    }
-
-    public void testEventV40() {
-        testEventCommon(V40);
-    }
-
-    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);
-    }
-
-    public void testNoteV40() {
-        testNoteCommon(V40);
-    }
-
-    private void testPhotoCommon(int vcardType) {
-        final boolean useB =
-            (VCardConfig.isVersion30(vcardType) || VCardConfig.isVersion40(vcardType));
-        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", (useB ? "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);
-    }
-
-    public void testPhotoV40() {
-        testPhotoCommon(V40);
-    }
-
-    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.
-                .put(StructuredName.DISPLAY_NAME, "display");
-        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
-                .put(StructuredName.IS_PRIMARY, 1)
-                .put(StructuredName.FAMILY_NAME, "family2")  // This entry is what we want.
-                .put(StructuredName.DISPLAY_NAME, "display");
-        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
-                .put(StructuredName.IS_PRIMARY, 1)
-                .put(StructuredName.FAMILY_NAME, "family3")
-                .put(StructuredName.DISPLAY_NAME, "display");
-        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
-                .put(StructuredName.FAMILY_NAME, "family4")
-                .put(StructuredName.DISPLAY_NAME, "display");
-        mVerifier.addPropertyNodesVerifierElem()
-                .addExpectedNode("N", Arrays.asList("family2", "", "", "", ""))
-                .addExpectedNode("FN", "display");
-    }
-
-    public void testPickUpNonEmptyContentValuesV21() {
-        testPickUpNonEmptyContentValuesCommon(V21);
-    }
-
-    public void testPickUpNonEmptyContentValuesV30() {
-        testPickUpNonEmptyContentValuesCommon(V30);
-    }
-
-    public void testPickUpNonEmptyContentValuesV40() {
-        testPickUpNonEmptyContentValuesCommon(V40);
-    }
-
-    public void testUseMultiByteTypeV30() {
-        mVerifier.initForExportTest(V30);
-        final ContactEntry entry = mVerifier.addInputEntry();
-        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
-                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
-                .put(Phone.LABEL, "\u96FB\u8A71")
-                .put(Phone.NUMBER, "1");
-        mVerifier.addLineVerifierElem()
-                .addExpected("N:")
-                .addExpected("FN:")
-                .addExpected("TEL;TYPE=\u96FB\u8A71:1");
-        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
-                .addExpectedNode("TEL", "1", new TypeSet("\u96FB\u8A71"));
-    }
-}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
deleted file mode 100644
index 09e2914..0000000
--- a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
+++ /dev/null
@@ -1,1108 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import com.android.frameworks.coretests.R;
-
-import android.content.ContentValues;
-import android.pim.vcard.test_utils.PropertyNodesVerifierElem.TypeSet;
-import android.pim.vcard.test_utils.VCardTestsBase;
-import android.pim.vcard.test_utils.ContentValuesVerifier;
-import android.pim.vcard.test_utils.ContentValuesVerifierElem;
-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.StructuredName;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.provider.ContactsContract.Data;
-
-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.addPropertyNodesVerifierElemWithoutVersion()  // no "VERSION:2.1" line.
-                .addExpectedNodeWithOrder("N", "Ando;Roid;", Arrays.asList("Ando", "Roid", ""));
-    }
-
-    public void testV21SimpleCase1_Type_Generic() {
-        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC, 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, 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, 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("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("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 testInvalidMultipleLineV21() {
-        mVerifier.initForImportTest(V21, R.raw.v21_invalid_multiple_line);
-        ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
-        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
-                .put(StructuredName.GIVEN_NAME, "Omega")
-                .put(StructuredName.DISPLAY_NAME, "Omega");
-        elem.addExpected(Email.CONTENT_ITEM_TYPE)
-                .put(Email.TYPE, Email.TYPE_CUSTOM)
-                .put(Email.LABEL, "INTERNET")
-                .put(Email.ADDRESS, "\"Omega\" <omega@example.com>");
-    }
-
-    public void testV30Simple_Parsing() {
-        mVerifier.initForImportTest(V30, R.raw.v30_simple);
-        mVerifier.addPropertyNodesVerifierElem()
-                .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, R.raw.v21_japanese_1);
-        mVerifier.addPropertyNodesVerifierElem()
-                .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 com.android.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC}.
-     */
-    public void testV21Japanese1_Type_Generic_Utf8() {
-        testV21Japanese1Common(
-                R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC, false);
-    }
-
-    /**
-     * Verifies vCard with Japanese can be parsed correctly with
-     * {@link com.android.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE}.
-     */
-    public void testV21Japanese1_Type_Japanese_Sjis() {
-        testV21Japanese1Common(
-                R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE, true);
-    }
-
-    /**
-     * Verifies vCard with Japanese can be parsed correctly with
-     * {@link com.android.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE}.
-     * 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, true);
-    }
-
-    public void testV21Japanese2_Parsing() {
-        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_2);
-        mVerifier.addPropertyNodesVerifierElem()
-                .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, R.raw.v21_multiple_entry);
-        mVerifier.addPropertyNodesVerifierElem()
-                .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("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("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, 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("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");
-        elem.addExpected(Event.CONTENT_ITEM_TYPE)
-                .put(Event.TYPE, Event.TYPE_ANNIVERSARY)
-                .put(Event.START_DATE, "20091010");
-    }
-
-    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_pager);
-        mVerifier.addPropertyNodesVerifierElem()
-                .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_pager);
-        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");
-    }
-
-    public void testMultiBytePropV30_Parse() {
-        mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param);
-        mVerifier.addPropertyNodesVerifierElem()
-                .addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""))
-                .addExpectedNodeWithOrder("TEL", "1", new TypeSet("\u8D39"));
-    }
-
-    public void testMultiBytePropV30() {
-        mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param);
-        final 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_CUSTOM)
-                .put(Phone.LABEL, "\u8D39")
-                .put(Phone.NUMBER, "1");
-    }
-
-    public void testCommaSeparatedParamsV30_Parse() {
-        mVerifier.initForImportTest(V30, R.raw.v30_comma_separated);
-        mVerifier.addPropertyNodesVerifierElem()
-                .addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""),
-                        new TypeSet("PREF", "HOME"))
-                .addExpectedNodeWithOrder("TEL", "1",
-                        new TypeSet("COMMA,SEPARATED:INSIDE.DQUOTE", "PREF"));
-    }
-
-    public void testSortAsV40_Parse() {
-        mVerifier.initForImportTest(V40, R.raw.v40_sort_as);
-
-        final ContentValues contentValuesForSortAsN = new ContentValues();
-        contentValuesForSortAsN.put("SORT-AS",
-                "\u3042\u3093\u3069\u3046;\u308D\u3044\u3069");
-        final ContentValues contentValuesForSortAsOrg = new ContentValues();
-        contentValuesForSortAsOrg.put("SORT-AS",
-                "\u3050\u30FC\u3050\u308B;\u3051\u3093\u3055\u304F\u3076\u3082\u3093");
-
-        mVerifier.addPropertyNodesVerifierElem()
-                .addExpectedNodeWithOrder("FN", "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9")
-                .addExpectedNodeWithOrder("N",
-                        Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9", "", "", ""),
-                        contentValuesForSortAsN)
-                .addExpectedNodeWithOrder("ORG",
-                        Arrays.asList("\u30B0\u30FC\u30B0\u30EB", "\u691C\u7D22\u90E8\u9580"),
-                        contentValuesForSortAsOrg, new TypeSet("WORK"));
-    }
-
-    public void testSortAsV40() {
-        mVerifier.initForImportTest(V40, R.raw.v40_sort_as);
-        final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
-        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
-                .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4")
-                .put(StructuredName.GIVEN_NAME, "\u30ED\u30A4\u30C9")
-                .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9")
-                .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3042\u3093\u3069\u3046")
-                .put(StructuredName.PHONETIC_GIVEN_NAME,
-                        "\u308D\u3044\u3069");
-        elem.addExpected(Organization.CONTENT_ITEM_TYPE)
-                .put(Organization.TYPE, Organization.TYPE_WORK)
-                .put(Organization.COMPANY, "\u30B0\u30FC\u30B0\u30EB")
-                .put(Organization.DEPARTMENT, "\u691C\u7D22\u90E8\u9580")
-                .put(Organization.PHONETIC_NAME,
-                        "\u3050\u30FC\u3050\u308B\u3051\u3093\u3055\u304F\u3076\u3082\u3093");
-    }
-
-    public void testIMV21_Parse() {
-        mVerifier.initForImportTest(V21, R.raw.v21_im);
-        mVerifier.addPropertyNodesVerifierElem()
-                .addExpectedNodeWithOrder("X-ANDROID-CUSTOM",
-                        Arrays.asList("vnd.android.cursor.item/nickname", "Nick", "1",
-                                "", "", "", "", "", "", "", "", "", "", "", "", ""))  // 13
-                .addExpectedNodeWithOrder("X-GOOGLE-TALK", "hhh@gmail.com");
-    }
-
-    public void testIMV21() {
-        mVerifier.initForImportTest(V21, R.raw.v21_im);
-        final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
-        elem.addExpected(Nickname.CONTENT_ITEM_TYPE)
-                .put(Nickname.NAME, "Nick")
-                .put(Nickname.TYPE, "1");
-        elem.addExpected(Im.CONTENT_ITEM_TYPE)
-                .put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK)
-                .put(Im.TYPE, Im.TYPE_HOME)
-                .put(Im.DATA, "hhh@gmail.com");
-    }
-}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java b/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java
deleted file mode 100644
index 8a99419..0000000
--- a/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.content.ContentValues;
-import android.pim.vcard.test_utils.ContactEntry;
-import android.pim.vcard.test_utils.ContentValuesBuilder;
-import android.pim.vcard.test_utils.PropertyNodesVerifierElem;
-import android.pim.vcard.test_utils.PropertyNodesVerifierElem.TypeSet;
-import android.pim.vcard.test_utils.VCardTestsBase;
-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 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.isVersion21(vcardType) ? mContentValuesForQPAndUtf8 : null);
-        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);
-    }
-
-    public void testNameUtf8V30() {
-        testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE);
-    }
-
-    public void testNameShiftJis() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS");
-        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, "Shift_JIS");
-        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, String charset) {
-        mVerifier.initForExportTest(vcardType, charset);
-        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 =
-            ("SHIFT_JIS".equalsIgnoreCase(charset) ?
-                    (VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndSJis :
-                        mContentValuesForSJis) :
-                    (VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndUtf8 : null));
-        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.isVersion21(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, null);
-    }
-
-    public void testPhoneticNameForJapaneseV21Sjis() {
-        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS");
-    }
-
-    public void testPhoneticNameForJapaneseV30Utf8() {
-        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, null);
-    }
-
-    public void testPhoneticNameForJapaneseV30SJis() {
-        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS");
-    }
-
-    public void testPhoneticNameForMobileV21_1() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE, "Shift_JIS");
-        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, "Shift_JIS");
-        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, String charset) {
-        mVerifier.initForExportTest(vcardType, charset);
-        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 = ("UTF-8".equalsIgnoreCase(charset) ?
-                (VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndSJis :
-                    mContentValuesForSJis) :
-                (VCardConfig.isVersion21(vcardType) ? mContentValuesForQPAndUtf8 :
-                    mContentValuesForUtf8));
-
-        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, "Shift_JIS");
-    }
-
-    /**
-     * 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, "Shift_JIS");
-        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, "Shift_JIS");
-        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, "Shift_JIS");
-        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, "Shift_JIS");
-        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);
-    }
-
-    public void testJapanesePhoneNumberV30() {
-        testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE);
-    }
-
-    public void testJapanesePhoneNumberDoCoMo() {
-        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS");
-        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, "Shift_JIS");
-        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);
-        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/VCardTestRunner.java b/core/tests/coretests/src/android/pim/vcard/VCardTestRunner.java
deleted file mode 100644
index a3a4393..0000000
--- a/core/tests/coretests/src/android/pim/vcard/VCardTestRunner.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.test.InstrumentationTestRunner;
-import android.test.InstrumentationTestSuite;
-
-import junit.framework.TestSuite;
-
-/**
- * Usage: adb shell am instrument -w com.android.vcard.tests/.VCardTestRunnerTestRunner
- */
-public class VCardTestRunner extends InstrumentationTestRunner {
-    @Override
-    public TestSuite getAllTests() {
-        TestSuite suite = new InstrumentationTestSuite(this);
-        suite.addTestSuite(VCardUtilsTests.class);
-        suite.addTestSuite(VCardTestUtilsTests.class);
-        suite.addTestSuite(VCardImporterTests.class);
-        suite.addTestSuite(VCardExporterTests.class);
-        suite.addTestSuite(VCardJapanizationTests.class);
-        return suite;
-    }
-
-    @Override
-    public ClassLoader getLoader() {
-        return VCardTestRunner.class.getClassLoader();
-    }
-}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardTestUtilsTests.java b/core/tests/coretests/src/android/pim/vcard/VCardTestUtilsTests.java
deleted file mode 100644
index 3ff5cf7..0000000
--- a/core/tests/coretests/src/android/pim/vcard/VCardTestUtilsTests.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import com.android.frameworks.coretests.R;
-
-import android.pim.vcard.test_utils.VCardVerifier;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.test.AndroidTestCase;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.TestCase;
-
-import java.util.Arrays;
-
-/**
- * Tests confirming utilities for vCard tests work fine.
- *
- * Now that the foundation classes for vCard test cases became too complicated to
- * rely on without testing itself.
- */
-public class VCardTestUtilsTests extends AndroidTestCase {
-    public void testShouldFailAtPropertyNodeVerification() {
-        boolean failureDetected = false;
-        try {
-            final VCardVerifier verifier = new VCardVerifier(this);
-            verifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC, R.raw.v21_backslash);
-            verifier.addPropertyNodesVerifierElem()
-                    .addExpectedNodeWithOrder("N", ";A;B\\;C\\;;D;:E;\\\\;--",  // wrong
-                            Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", ""))
-                    .addExpectedNodeWithOrder("FN", "A;B\\C\\;D:E\\\\");
-            verifier.verify();
-        } catch (AssertionFailedError e) {
-            failureDetected = true;
-        }
-        if (!failureDetected) {
-            TestCase.fail("Test case that should fail actually succeeded.");
-        }
-    }
-
-    public void testShouldFailAtContentValueVerification() {
-        boolean failureDetected = false;
-        try {
-            final VCardVerifier verifier = new VCardVerifier(this);
-            verifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC, R.raw.v21_backslash);
-            verifier.addContentValuesVerifierElem()
-                    .addExpected(StructuredName.CONTENT_ITEM_TYPE)
-                            .put(StructuredName.GIVEN_NAME, "A;B\\")
-                            .put(StructuredName.MIDDLE_NAME, "C\\;")
-                            .put(StructuredName.PREFIX, "D")
-                            .put(StructuredName.SUFFIX, ":E");
-            // DISPLAY_NAME is missing.
-            verifier.verify();
-        } catch (AssertionFailedError e) {
-            failureDetected = true;
-        }
-        if (!failureDetected) {
-            TestCase.fail("Test case that should fail actually succeeded.");
-        }
-    }
-
-    public void testShouldFailAtLineVerification() {
-        boolean failureDetected = false;
-        try {
-            final VCardVerifier verifier = new VCardVerifier(this);
-            verifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_GENERIC);
-            verifier.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}>]");
-            verifier.addLineVerifierElem()
-                    .addExpected("TEL:1")  // wrong
-                    .addExpected("FN:[<{Unescaped:Asciis}>]");
-            verifier.verify();
-        } catch (AssertionFailedError e) {
-            failureDetected = true;
-        }
-        if (!failureDetected) {
-            TestCase.fail("Test case that should fail actually succeeded.");
-        }
-    }
-
-}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java b/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
deleted file mode 100644
index 251fab2..0000000
--- a/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard;
-
-import android.text.TextUtils;
-
-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)));
-        }
-    }
-
-    public void testToStringAvailableAsV30ParamValue() {
-        // Smoke tests.
-        assertEquals("HOME", VCardUtils.toStringAsV30ParamValue("HOME"));
-        assertEquals("TEL", VCardUtils.toStringAsV30ParamValue("TEL"));
-        assertEquals("PAGER", VCardUtils.toStringAsV30ParamValue("PAGER"));
-
-        assertTrue(TextUtils.isEmpty(VCardUtils.toStringAsV30ParamValue("")));
-        assertTrue(TextUtils.isEmpty(VCardUtils.toStringAsV30ParamValue(null)));
-        assertTrue(TextUtils.isEmpty(VCardUtils.toStringAsV30ParamValue(" \t")));
-
-        // non-Ascii must be allowed
-        assertEquals("\u4E8B\u52D9\u6240",
-                VCardUtils.toStringAsV30ParamValue("\u4E8B\u52D9\u6240"));
-        // Reported as bug report.
-        assertEquals("\u8D39", VCardUtils.toStringAsV30ParamValue("\u8D39"));
-        assertEquals("\"comma,separated\"",
-                VCardUtils.toStringAsV30ParamValue("comma,separated"));
-        assertEquals("\"colon:aware\"",
-                VCardUtils.toStringAsV30ParamValue("colon:aware"));
-        // CTL characters.
-        assertEquals("CTLExample",
-                VCardUtils.toStringAsV30ParamValue("CTL\u0001Example"));
-        assertTrue(TextUtils.isEmpty(
-                VCardUtils.toStringAsV30ParamValue("\u0001\u0002\u0003")));
-        // DQUOTE must be removed.
-        assertEquals("quoted",
-                VCardUtils.toStringAsV30ParamValue("\"quoted\""));
-        // DQUOTE must be removed basically, but we should detect a space, which
-        // require us to use DQUOTE again.
-        // Right-side has one more illegal dquote to test quote-handle code thoroughly.
-        assertEquals("\"Already quoted\"",
-                VCardUtils.toStringAsV30ParamValue("\"Already quoted\"\""));
-    }
-}
diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/ContactEntry.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ContactEntry.java
deleted file mode 100644
index 843750e..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/ContactEntry.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.content.ContentValues;
-import android.provider.ContactsContract.Data;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * <p>
- * The class representing one contact, which should contain multiple ContentValues like
- * StructuredName, Email, etc.
- * </p>
- */
-public final 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;
-    }
-}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesBuilder.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesBuilder.java
deleted file mode 100644
index bdcccf9..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesBuilder.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.content.ContentValues;
-
-/**
- * ContentValues-like class which enables users to chain put() methods and restricts
- * the other methods.
- */
-public 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/test_utils/ContentValuesVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifier.java
deleted file mode 100644
index d0613c6..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifier.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.pim.vcard.VCardEntry;
-import android.pim.vcard.VCardEntryHandler;
-import android.test.AndroidTestCase;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ContentValuesVerifier implements VCardEntryHandler {
-    private List<ContentValuesVerifierElem> mContentValuesVerifierElemList =
-        new ArrayList<ContentValuesVerifierElem>();
-    private int mIndex;
-
-    public ContentValuesVerifierElem addElem(AndroidTestCase androidTestCase) {
-        ContentValuesVerifierElem elem = new ContentValuesVerifierElem(androidTestCase);
-        mContentValuesVerifierElemList.add(elem);
-        return elem;
-    }
-
-    public void onStart() {
-        for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) {
-            elem.onParsingStart();
-        }
-    }
-
-    public void onEntryCreated(VCardEntry entry) {
-        AndroidTestCase.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/test_utils/ContentValuesVerifierElem.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifierElem.java
deleted file mode 100644
index 03c18e1..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/ContentValuesVerifierElem.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.content.ContentValues;
-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.VCardUtils;
-import android.pim.vcard.exception.VCardException;
-import android.provider.ContactsContract.Data;
-import android.test.AndroidTestCase;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public 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 = VCardUtils.getAppropriateParser(vcardType);
-        final VCardEntryConstructor builder = new VCardEntryConstructor(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/test_utils/ExportTestProvider.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestProvider.java
deleted file mode 100644
index e095258..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestProvider.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.pim.vcard.test_utils;
-
-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.AndroidTestCase;
-import android.test.mock.MockContentProvider;
-import android.test.mock.MockCursor;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-public class ExportTestProvider extends MockContentProvider {
-    final private ArrayList<ContactEntry> mContactEntryList = new ArrayList<ContactEntry>();
-
-    private static 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 remove() {
-            throw new UnsupportedOperationException("remove not supported");
-        }
-
-        public void reset() {
-            mIterator = mEntityList.iterator();
-        }
-
-        public void close() {
-        }
-    }
-
-    public ExportTestProvider(AndroidTestCase androidTestCase) {
-    }
-
-    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>
-     */
-    public EntityIterator queryEntities(Uri uri,
-            String selection, String[] selectionArgs, String sortOrder) {
-        TestCase.assertTrue(uri != null);
-        TestCase.assertTrue(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()));
-        final String authority = uri.getAuthority();
-        TestCase.assertTrue(RawContacts.CONTENT_URI.getAuthority().equals(authority));
-        TestCase.assertTrue((Data.CONTACT_ID + "=?").equals(selection));
-        TestCase.assertEquals(1, selectionArgs.length);
-        final int id = Integer.parseInt(selectionArgs[0]);
-        TestCase.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) {
-        TestCase.assertTrue(VCardComposer.CONTACTS_TEST_CONTENT_URI.equals(uri));
-        // In this test, following arguments are not supported.
-        TestCase.assertNull(selection);
-        TestCase.assertNull(selectionArgs);
-        TestCase.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) {
-                TestCase.assertEquals(Contacts._ID, columnName);
-                return 0;
-            }
-
-            @Override
-            public int getInt(int columnIndex) {
-                TestCase.assertEquals(0, columnIndex);
-                TestCase.assertTrue(mCurrentPosition >= 0
-                        && mCurrentPosition < mContactEntryList.size());
-                return mCurrentPosition;
-            }
-
-            @Override
-            public String getString(int columnIndex) {
-                return String.valueOf(getInt(columnIndex));
-            }
-
-            @Override
-            public void close() {
-            }
-        };
-    }
-}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestResolver.java
deleted file mode 100644
index 606d91a..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/ExportTestResolver.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.pim.vcard.VCardComposer;
-import android.provider.ContactsContract.RawContacts;
-import android.test.AndroidTestCase;
-import android.test.mock.MockContentResolver;
-
-public class ExportTestResolver extends MockContentResolver {
-    private final ExportTestProvider mProvider;
-    public ExportTestResolver(AndroidTestCase androidTestCase) {
-        mProvider = new ExportTestProvider(androidTestCase);
-        addProvider(VCardComposer.VCARD_TEST_AUTHORITY, mProvider);
-        addProvider(RawContacts.CONTENT_URI.getAuthority(), mProvider);
-    }
-
-    public ContactEntry addInputContactEntry() {
-        return mProvider.buildInputEntry();
-    }
-
-    public ExportTestProvider getProvider() {
-        return mProvider;
-    }
-}
diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestProvider.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestProvider.java
deleted file mode 100644
index df89491..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestProvider.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentValues;
-import android.net.Uri;
-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.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.test.AndroidTestCase;
-import android.test.mock.MockContentProvider;
-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.Map.Entry;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-public 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;
-
-    public ImportTestProvider(AndroidTestCase androidTestCase) {
-        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)) {
-            TestCase.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) {
-            TestCase.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)) {
-                TestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME));
-                TestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE));
-            } else if (uri.equals(Data.CONTENT_URI)) {
-                final String mimeType = actualContentValues.getAsString(Data.MIMETYPE);
-                if (!sKnownMimeTypeSet.contains(mimeType)) {
-                    TestCase.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()) {
-                    TestCase.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)) {
-                        TestCase.assertTrue(contentValuesCollection.remove(expectedContentValues));
-                        checked = true;
-                        break;
-                    }
-                }
-                if (!checked) {
-                    final StringBuilder builder = new StringBuilder();
-                    builder.append("\n");
-                    builder.append("Unexpected: ");
-                    builder.append(convertToEasilyReadableString(actualContentValues));
-                    builder.append("\n");
-                    builder.append("Expected  : ");
-                    for (ContentValues expectedContentValues : contentValuesCollection) {
-                        builder.append(convertToEasilyReadableString(expectedContentValues));
-                    }
-                    TestCase.fail(builder.toString());
-                }
-            } else {
-                TestCase.fail("Unexpected Uri has come: " + uri);
-            }
-        }  // for (int i = 0; i < size; i++) {
-        return fakeResultArray;
-    }
-
-    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();
-            TestCase.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 {
-                TestCase.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(
-            final ContentValues expected, final 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;
-            }
-            // Type mismatch usuall happens as importer doesn't care the type of each value.
-            // For example, variable type might be Integer when importing the type of TEL,
-            // while variable type would be String when importing the type of RELATION.
-            final Object actualValue = actual.get(key);
-            if (value instanceof byte[]) {
-                if (!Arrays.equals((byte[])value, (byte[])actualValue)) {
-                    byte[] e = (byte[])value;
-                    byte[] a = (byte[])actualValue;
-                    return false;
-                }
-            } else if (!value.equals(actualValue) &&
-                    !value.toString().equals(actualValue.toString())) {
-                return false;
-            }
-        }
-        return true;
-    }
-}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestResolver.java
deleted file mode 100644
index 1822afe..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/ImportTestResolver.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentValues;
-import android.provider.ContactsContract.RawContacts;
-import android.test.AndroidTestCase;
-import android.test.mock.MockContentResolver;
-
-import java.util.ArrayList;
-
-public class ImportTestResolver extends MockContentResolver {
-    private final ImportTestProvider mProvider;
-
-    public ImportTestResolver(AndroidTestCase androidTestCase) {
-        mProvider = new ImportTestProvider(androidTestCase);
-    }
-
-    @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);
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifier.java
deleted file mode 100644
index e314ac5..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifier.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.content.Context;
-import android.pim.vcard.VCardComposer;
-import android.test.AndroidTestCase;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-
-public class LineVerifier implements VCardComposer.OneEntryHandler {
-    private final AndroidTestCase mAndroidTestCase;
-    private final ArrayList<LineVerifierElem> mLineVerifierElemList;
-    private int mVCardType;
-    private int index;
-
-    public LineVerifier(AndroidTestCase androidTestCase, int vcardType) {
-        mAndroidTestCase = androidTestCase;
-        mLineVerifierElemList = new ArrayList<LineVerifierElem>();
-        mVCardType = vcardType;
-    }
-
-    public LineVerifierElem addLineVerifierElem() {
-        LineVerifierElem lineVerifier = new LineVerifierElem(mAndroidTestCase, mVCardType);
-        mLineVerifierElemList.add(lineVerifier);
-        return lineVerifier;
-    }
-
-    public void verify(String vcard) {
-        if (index >= mLineVerifierElemList.size()) {
-            TestCase.fail("Insufficient number of LineVerifier (" + index + ")");
-        }
-
-        final 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/test_utils/LineVerifierElem.java b/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifierElem.java
deleted file mode 100644
index e23a9cd..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/LineVerifierElem.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.pim.vcard.VCardConfig;
-import android.test.AndroidTestCase;
-import android.text.TextUtils;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class LineVerifierElem {
-    private final List<String> mExpectedLineList = new ArrayList<String>();
-    private final int mVCardType;
-
-    public LineVerifierElem(AndroidTestCase androidTestCase, int vcardType) {
-        mVCardType = 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) {
-                    TestCase.fail("Multiple \"BEGIN:VCARD\" line found");
-                } else {
-                    beginExists = true;
-                    continue;
-                }
-            } else if ("END:VCARD".equalsIgnoreCase(line)) {
-                if (endExists) {
-                    TestCase.fail("Multiple \"END:VCARD\" line found");
-                } else {
-                    endExists = true;
-                    continue;
-                }
-            } else if ((VCardConfig.isVersion21(mVCardType) ? "VERSION:2.1" :
-                (VCardConfig.isVersion30(mVCardType) ? "VERSION:3.0" :
-                    "VERSION:4.0")).equalsIgnoreCase(line)) {
-                if (versionExists) {
-                    TestCase.fail("Multiple VERSION line + found");
-                } else {
-                    versionExists = true;
-                    continue;
-                }
-            }
-
-            if (!beginExists) {
-                TestCase.fail("Property other than BEGIN came before BEGIN property: " + line);
-            } else if (endExists) {
-                TestCase.fail("Property other than END came after END property: " + line);
-            }
-
-            final int index = mExpectedLineList.indexOf(line);
-            if (index >= 0) {
-                mExpectedLineList.remove(index);
-            } else {
-                TestCase.fail("Unexpected line: " + line);
-            }
-        }
-
-        if (!mExpectedLineList.isEmpty()) {
-            StringBuffer buffer = new StringBuffer();
-            for (String expectedLine : mExpectedLineList) {
-                buffer.append(expectedLine);
-                buffer.append("\n");
-            }
-
-            TestCase.fail("Expected line(s) not found:" + buffer.toString());
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNode.java b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNode.java
deleted file mode 100644
index de7ad8e..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNode.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.content.ContentValues;
-import android.pim.vcard.VCardEntry;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * <p>
- * The class representing one property (e.g. "N;ENCODING=UTF-8:family:given:middle:prefix:suffix").
- * </p>
- * <p>
- * Previously used in main vCard handling code but now exists only for testing.
- * </p>
- * <p>
- * 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
- * {@link VCardEntry} is wrong without this class.
- * </p>
- */
-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/test_utils/PropertyNodesVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifier.java
deleted file mode 100644
index 4229284..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifier.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.pim.vcard.VCardParser;
-import android.pim.vcard.VCardUtils;
-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;
-
-public class PropertyNodesVerifier extends VNodeBuilder {
-    private final List<PropertyNodesVerifierElem> mPropertyNodesVerifierElemList;
-    private final AndroidTestCase mAndroidTestCase;
-    private int mIndex;
-
-    public PropertyNodesVerifier(AndroidTestCase testCase) {
-        super();
-        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 parser)
-            throws IOException, VCardException {
-        verify(mAndroidTestCase.getContext().getResources().openRawResource(resId),
-                vcardType, parser);
-    }
-
-    public void verify(InputStream is, int vcardType) throws IOException, VCardException {
-        final VCardParser parser = VCardUtils.getAppropriateParser(vcardType);
-        verify(is, vcardType, parser);
-    }
-
-    public void verify(InputStream is, int vcardType, final VCardParser parser)
-            throws IOException, VCardException {
-        try {
-            parser.parse(is, this);
-        } finally {
-            if (is != null) {
-                try {
-                    is.close();
-                } catch (IOException e) {
-                }
-            }
-        }
-    }
-
-    @Override
-    public void endEntry() {
-        super.endEntry();
-        AndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size());
-        AndroidTestCase.assertTrue(mIndex < vNodeList.size());
-        mPropertyNodesVerifierElemList.get(mIndex).verify(vNodeList.get(mIndex));
-        mIndex++;
-    }
-}
diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifierElem.java b/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifierElem.java
deleted file mode 100644
index 44c6abc..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/PropertyNodesVerifierElem.java
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.content.ContentValues;
-import android.test.AndroidTestCase;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-
-/**
- * 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.
- */
-public 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;
-
-    public PropertyNodesVerifierElem(AndroidTestCase androidTestCase) {
-        mOrderedNodeMap = new HashMap<String, List<PropertyNode>>();
-        mUnorderedNodeList = new ArrayList<PropertyNode>();
-    }
-
-    // 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,
-            List<String> propValueList, ContentValues paramMap, TypeSet paramMap_TYPE) {
-        return addExpectedNodeWithOrder(propName, null, propValueList, null, paramMap,
-                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, ContentValues paramMap) {
-        return addExpectedNodeWithOrder(propName, propValue, propValueList, null, paramMap,
-                null, 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);
-        }
-        final 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()) {
-            final 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);
-                }
-            }
-            TestCase.fail("Expected property " + Arrays.toString(expectedProps.toArray())
-                    + " was not found.");
-        }
-    }
-
-    private void verifyNode(final String propName, final PropertyNode actualNode) {
-        final List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
-        final int size = (expectedNodeList != null ? expectedNodeList.size() : 0);
-        if (size > 0) {
-            for (int i = 0; i < size; i++) {
-                final PropertyNode expectedNode = expectedNodeList.get(i);
-                final 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.
-                    TestCase.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.
-                    TestCase.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");
-        }
-        TestCase.fail("Property \"" + propName + "\" has wrong value.\n"
-                + builder.toString()
-                + "  actual: " + actualNode.toString());
-    }
-}
diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/VCardTestsBase.java b/core/tests/coretests/src/android/pim/vcard/test_utils/VCardTestsBase.java
deleted file mode 100644
index e7413ab..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/VCardTestsBase.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.content.ContentValues;
-import android.pim.vcard.VCardConfig;
-import android.test.AndroidTestCase;
-
-/**
- * BaseClass for vCard unit tests with utility classes.
- * Please do not add each unit test here.
- */
-public class VCardTestsBase extends AndroidTestCase {
-    public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC;
-    public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC;
-    public static final int V40 = VCardConfig.VCARD_TYPE_V40_GENERIC;
-
-    // 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();
-        // Not using constants in vCard code since it may be wrong.
-        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/test_utils/VCardVerifier.java b/core/tests/coretests/src/android/pim/vcard/test_utils/VCardVerifier.java
deleted file mode 100644
index 7379a5b..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/VCardVerifier.java
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.pim.vcard.test_utils;
-
-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.VCardUtils;
-import android.pim.vcard.exception.VCardException;
-import android.test.AndroidTestCase;
-import android.test.mock.MockContext;
-import android.text.TextUtils;
-import android.util.Log;
-
-import junit.framework.TestCase;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-
-/**
- * <p>
- * The class lets users checks that given expected vCard data are same as given actual vCard data.
- * Able to verify both vCard importer/exporter.
- * </p>
- * <p>
- * First a user has to initialize the object by calling either
- * {@link #initForImportTest(int, int)} or {@link #initForExportTest(int)}.
- * "Round trip test" (import -> export -> import, or export -> import -> export) is not supported.
- * </p>
- */
-public class VCardVerifier {
-    private static final String LOG_TAG = "VCardVerifier";
-
-    private static class CustomMockContext extends MockContext {
-        final ContentResolver mResolver;
-        public CustomMockContext(ContentResolver resolver) {
-            mResolver = resolver;
-        }
-
-        @Override
-        public ContentResolver getContentResolver() {
-            return mResolver;
-        }
-    }
-
-    private class VCardVerifierInternal implements VCardComposer.OneEntryHandler {
-        public boolean onInit(Context context) {
-            return true;
-        }
-        public boolean onEntryCreated(String vcard) {
-            verifyOneVCardForExport(vcard);
-            return true;
-        }
-        public void onTerminate() {
-        }
-    }
-
-    private final AndroidTestCase mAndroidTestCase;
-    private final VCardVerifierInternal mVCardVerifierInternal;
-    private int mVCardType;
-    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;
-    private String mCharset;
-
-    // Called by VCardTestsBase
-    public VCardVerifier(AndroidTestCase androidTestCase) {
-        mAndroidTestCase = androidTestCase;
-        mVCardVerifierInternal = new VCardVerifierInternal();
-        mExportTestResolver = null;
-        mInputStream = null;
-        mInitialized = false;
-        mVerified = false;
-    }
-
-    // Should be called at the beginning of each import test.
-    public void initForImportTest(int vcardType, int resId) {
-        if (mInitialized) {
-            AndroidTestCase.fail("Already initialized");
-        }
-        mVCardType = vcardType;
-        mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
-        setInputResourceId(resId);
-        mInitialized = true;
-    }
-
-    // Should be called at the beginning of each export test.
-    public void initForExportTest(int vcardType) {
-        initForExportTest(vcardType, "UTF-8");
-    }
-
-    public void initForExportTest(int vcardType, String charset) {
-        if (mInitialized) {
-            AndroidTestCase.fail("Already initialized");
-        }
-        mExportTestResolver = new ExportTestResolver(mAndroidTestCase);
-        mVCardType = vcardType;
-        mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
-        mInitialized = true;
-        if (TextUtils.isEmpty(charset)) {
-            mCharset = "UTF-8";
-        } else {
-            mCharset = charset;
-        }
-    }
-
-    private void setInputResourceId(int resId) {
-        InputStream inputStream = mAndroidTestCase.getContext().getResources().openRawResource(resId);
-        if (inputStream == null) {
-            AndroidTestCase.fail("Wrong resId: " + resId);
-        }
-        setInputStream(inputStream);
-    }
-
-    private void setInputStream(InputStream inputStream) {
-        if (mExportTestResolver != null) {
-            AndroidTestCase.fail("addInputEntry() is called.");
-        } else if (mInputStream != null) {
-            AndroidTestCase.fail("InputStream is already set");
-        }
-        mInputStream = inputStream;
-    }
-
-    public ContactEntry addInputEntry() {
-        if (!mInitialized) {
-            AndroidTestCase.fail("Not initialized");
-        }
-        if (mInputStream != null) {
-            AndroidTestCase.fail("setInputStream is called");
-        }
-        return mExportTestResolver.addInputContactEntry();
-    }
-
-    public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithoutVersion() {
-        if (!mInitialized) {
-            AndroidTestCase.fail("Not initialized");
-        }
-        if (mPropertyNodesVerifier == null) {
-            mPropertyNodesVerifier = new PropertyNodesVerifier(mAndroidTestCase);
-        }
-        return mPropertyNodesVerifier.addPropertyNodesVerifierElem();
-    }
-    
-    public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
-        final PropertyNodesVerifierElem elem = addPropertyNodesVerifierElemWithoutVersion();
-        final String versionString;
-        if (VCardConfig.isVersion21(mVCardType)) {
-            versionString = "2.1";
-        } else if (VCardConfig.isVersion30(mVCardType)) {
-            versionString = "3.0";
-        } else if (VCardConfig.isVersion40(mVCardType)) {
-            versionString = "4.0";
-        } else {
-            throw new RuntimeException("Unexpected vcard type during a unit test");
-        }
-        elem.addExpectedNodeWithOrder("VERSION", versionString);
-
-        return elem;
-    }
-
-    public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithEmptyName() {
-        if (!mInitialized) {
-            AndroidTestCase.fail("Not initialized");
-        }
-        final PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem();
-        if (VCardConfig.isVersion40(mVCardType)) {
-            elem.addExpectedNodeWithOrder("FN", "");
-        } else if (VCardConfig.isVersion30(mVCardType)) {
-            elem.addExpectedNodeWithOrder("N", "");
-            elem.addExpectedNodeWithOrder("FN", "");
-        } else if (mIsDoCoMo) {
-            elem.addExpectedNodeWithOrder("N", "");
-        }
-        return elem;
-    }
-
-    public LineVerifierElem addLineVerifierElem() {
-        if (!mInitialized) {
-            AndroidTestCase.fail("Not initialized");
-        }
-        if (mLineVerifier == null) {
-            mLineVerifier = new LineVerifier(mAndroidTestCase, mVCardType);
-        }
-        return mLineVerifier.addLineVerifierElem();
-    }
-
-    public ContentValuesVerifierElem addContentValuesVerifierElem() {
-        if (!mInitialized) {
-            AndroidTestCase.fail("Not initialized");
-        }
-        if (mContentValuesVerifier == null) {
-            mContentValuesVerifier = new ContentValuesVerifier();
-        }
-
-        return mContentValuesVerifier.addElem(mAndroidTestCase);
-    }
-
-    /**
-     * Sets up sub-verifiers correctly and try parse given vCard as InputStream.
-     * Errors around InputStream must be handled outside this method.
-     *
-     * Used both from {@link #verifyForImportTest()} and from {@link #verifyForExportTest()}.
-     */
-    private void verifyWithInputStream(InputStream is) throws IOException {
-        final VCardInterpreter interpreter;
-        if (mContentValuesVerifier != null) {
-            final VCardEntryConstructor constructor = new VCardEntryConstructor(mVCardType);
-            constructor.addEntryHandler(mContentValuesVerifier);
-            if (mPropertyNodesVerifier != null) {
-                interpreter = new VCardInterpreterCollection(Arrays.asList(
-                        mPropertyNodesVerifier, constructor));
-            } else {
-                interpreter = constructor;
-            }
-        } else {
-            if (mPropertyNodesVerifier != null) {
-                interpreter = mPropertyNodesVerifier;
-            } else {
-                interpreter = null;
-            }
-        }
-
-        try {
-            // Note: we must not specify charset toward vCard parsers. This code checks whether
-            // those parsers are able to encode given binary without any extra information for
-            // charset.
-            final VCardParser parser = VCardUtils.getAppropriateParser(mVCardType);
-            parser.parse(is, interpreter);
-        } catch (VCardException e) {
-            AndroidTestCase.fail("Unexpected VCardException: " + e.getMessage());
-        }
-    }
-
-    private void verifyOneVCardForExport(final String vcard) {
-        Log.d(LOG_TAG, vcard);
-        InputStream is = null;
-        try {
-            is = new ByteArrayInputStream(vcard.getBytes(mCharset));
-            verifyWithInputStream(is);
-        } catch (IOException e) {
-            AndroidTestCase.fail("Unexpected IOException: " + e.getMessage());
-        } finally {
-            if (is != null) {
-                try {
-                    is.close();
-                } catch (IOException e) {
-                    AndroidTestCase.fail("Unexpected IOException: " + e.getMessage());
-                }
-            }
-        }
-    }
-
-    public void verify() {
-        if (!mInitialized) {
-            TestCase.fail("Not initialized.");
-        }
-        if (mVerified) {
-            TestCase.fail("verify() was called twice.");
-        }
-
-        if (mInputStream != null) {
-            if (mExportTestResolver != null){
-                TestCase.fail("There are two input sources.");
-            }
-            verifyForImportTest();
-        } else if (mExportTestResolver != null){
-            verifyForExportTest();
-        } else {
-            TestCase.fail("No input is determined");
-        }
-        mVerified = true;
-    }
-
-    private void verifyForImportTest() {
-        if (mLineVerifier != null) {
-            AndroidTestCase.fail("Not supported now.");
-        }
-
-        try {
-            verifyWithInputStream(mInputStream);
-        } catch (IOException e) {
-            AndroidTestCase.fail("IOException was thrown: " + e.getMessage());
-        } finally {
-            if (mInputStream != null) {
-                try {
-                    mInputStream.close();
-                } catch (IOException e) {
-                }
-            }
-        }
-    }
-
-    public static EntityIterator mockGetEntityIteratorMethod(
-            final ContentResolver resolver,
-            final Uri uri, final String selection,
-            final String[] selectionArgs, final String sortOrder) {
-        if (ExportTestResolver.class.equals(resolver.getClass())) {
-            return ((ExportTestResolver)resolver).getProvider().queryEntities(
-                    uri, selection, selectionArgs, sortOrder);
-        }
-
-        Log.e(LOG_TAG, "Unexpected provider given.");
-        return null;
-    }
-
-    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, mCharset);
-        composer.addHandler(mLineVerifier);
-        composer.addHandler(mVCardVerifierInternal);
-        if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
-            AndroidTestCase.fail("init() failed. Reason: " + composer.getErrorReason());
-        }
-        AndroidTestCase.assertFalse(composer.isAfterLast());
-        try {
-            while (!composer.isAfterLast()) {
-                try {
-                    final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod();
-                    AndroidTestCase.assertNotNull(mockGetEntityIteratorMethod);
-                    AndroidTestCase.assertTrue(
-                            composer.createOneEntry(mockGetEntityIteratorMethod));
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    AndroidTestCase.fail(e.toString());
-                }
-            }
-        } finally {
-            composer.terminate();
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/pim/vcard/test_utils/VNode.java b/core/tests/coretests/src/android/pim/vcard/test_utils/VNode.java
deleted file mode 100644
index b890e2c..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/VNode.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.test_utils;
-
-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/test_utils/VNodeBuilder.java b/core/tests/coretests/src/android/pim/vcard/test_utils/VNodeBuilder.java
deleted file mode 100644
index 8837034..0000000
--- a/core/tests/coretests/src/android/pim/vcard/test_utils/VNodeBuilder.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.pim.vcard.test_utils;
-
-import android.content.ContentValues;
-import android.pim.vcard.VCardConfig;
-import android.pim.vcard.VCardInterpreter;
-import android.pim.vcard.VCardUtils;
-import android.util.Base64;
-import android.util.CharsetUtils;
-import android.util.Log;
-
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * <p>
- * The class storing the parse result to custom datastruct:
- * {@link VNode}, and {@link PropertyNode}.
- * Maybe several vcard instance, so use vNodeList to store.
- * </p>
- * <p>
- * This is called VNode, not VCardNode, since it was used for expressing vCalendar (iCal).
- * </p>
- */
-public class VNodeBuilder implements VCardInterpreter {
-    static private String LOG_TAG = "VNodeBuilder"; 
-    
-    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_IMPORT_CHARSET, false);
-    }
-
-    public VNodeBuilder(String targetCharset, boolean strictLineBreakParsing) {
-        mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET;
-        if (targetCharset != null) {
-            mTargetCharset = targetCharset;
-        } else {
-            mTargetCharset = VCardConfig.DEFAULT_IMPORT_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;
-    }
-
-    public void propertyGroup(String group) {
-        mCurrentPropNode.propGroupSet.add(group);
-    }
-    
-    public void propertyParamType(String type) {
-        mCurrentParamType = type;
-    }
-
-    public void propertyParamValue(String value) {
-        if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) {
-            value = VCardUtils.convertStringCharset(value,
-                    VCardConfig.DEFAULT_INTERMEDIATE_CHARSET,
-                    VCardConfig.DEFAULT_IMPORT_CHARSET);
-        }
-
-        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.decode(value.getBytes(), Base64.NO_WRAP);
-                return value;
-            } else if (encoding.equals("QUOTED-PRINTABLE")) {
-                return VCardUtils.parseQuotedPrintable(
-                        value, mStrictLineBreakParsing, mSourceCharset, targetCharset);
-            }
-            // 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(){
-        throw new RuntimeException("Not supported");
-    }
-}
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index 370ae78..b82e698 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -144,4 +144,51 @@
 
         assertEquals(null, Settings.Bookmarks.getIntentForShortcut(r, '*'));
     }
+
+    @MediumTest
+    public void testParseProviderList() {
+        ContentResolver r = getContext().getContentResolver();
+
+        // Make sure we get out what we put in.
+        Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                "test1,test2,test3");
+        assertEquals(Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED),
+                "test1,test2,test3");
+
+        // Test adding a value
+        Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                "");
+        Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+test1");
+        assertEquals("test1",
+                Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+
+        // Test adding a second value
+        Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+test2");
+        assertEquals("test1,test2",
+                Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+
+        // Test adding a third value
+        Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+test3");
+        assertEquals("test1,test2,test3",
+                Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+
+        // Test deleting the first value in a 3 item list
+        Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-test1");
+        assertEquals("test2,test3",
+                Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+
+        // Test deleting the middle value in a 3 item list
+        Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                "test1,test2,test3");
+        Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-test2");
+        assertEquals("test1,test3",
+                Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+
+        // Test deleting the last value in a 3 item list
+        Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                "test1,test2,test3");
+        Settings.Secure.putString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-test3");
+        assertEquals("test1,test2",
+                Settings.Secure.getString(r, Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
+     }
 }
diff --git a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
index 8e7e63e..fb0f0c1 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
@@ -22,7 +22,7 @@
 import junit.framework.TestCase;
 
 /**
- * Tests StaticLayout bidi implementation.
+ * Quick check of native bidi implementation.
  */
 public class StaticLayoutBidiTest extends TestCase {
     
@@ -41,73 +41,47 @@
     
     //@SmallTest
     public void testAllLtr() {
-        expectBidi(REQ_DL, "a test", "000000", L);
+        expectNativeBidi(REQ_DL, "a test", "000000", L);
     }
     
     //@SmallTest
     public void testLtrRtl() {
-        expectBidi(REQ_DL, "abc " + ALEF + BET + GIMEL, "0000111", L);
+        expectNativeBidi(REQ_DL, "abc " + ALEF + BET + GIMEL, "0000111", L);
     }
     
     //@SmallTest
     public void testAllRtl() {
-        expectBidi(REQ_DL, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", R);
+        expectNativeBidi(REQ_DL, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", R);
     }
     
     //@SmallTest
     public void testRtlLtr() {
-        expectBidi(REQ_DL,  ALEF + BET + GIMEL + " abc", "1111000", R);
+        expectNativeBidi(REQ_DL,  ALEF + BET + GIMEL + " abc", "1111222", R);
     }
     
     //@SmallTest
     public void testRAllLtr() {
-        expectBidi(REQ_R, "a test", "000000", R);
+        expectNativeBidi(REQ_R, "a test", "222222", R);
     }
     
     //@SmallTest
     public void testRLtrRtl() {
-        expectBidi(REQ_R, "abc " + ALEF + BET + GIMEL, "0001111", R);
+        expectNativeBidi(REQ_R, "abc " + ALEF + BET + GIMEL, "2221111", R);
     }
     
     //@SmallTest
     public void testLAllRtl() {
-        expectBidi(REQ_L, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", L);
+        expectNativeBidi(REQ_L, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", L);
     }
     
     //@SmallTest
     public void testLRtlLtr() {
-        expectBidi(REQ_L,  ALEF + BET + GIMEL + " abc", "1110000", L);
-    }
-    
-    private void expectBidi(int dir, String text, 
-            String expectedLevels, int expectedDir) {
-        char[] chs = text.toCharArray();
-        int n = chs.length;
-        byte[] chInfo = new byte[n];
-        
-        int resultDir = StaticLayout.bidi(dir, chs, chInfo, n, false);
-        
-        {
-            StringBuilder sb = new StringBuilder("info:");
-            for (int i = 0; i < n; ++i) {
-                sb.append(" ").append(String.valueOf(chInfo[i]));
-            }
-            Log.i("BIDI", sb.toString());
-        }
-        
-        char[] resultLevelChars = new char[n];
-        for (int i = 0; i < n; ++i) {
-            resultLevelChars[i] = (char)('0' + chInfo[i]);
-        }
-        String resultLevels = new String(resultLevelChars);
-        assertEquals("direction", expectedDir, resultDir);
-        assertEquals("levels", expectedLevels, resultLevels);
+        expectNativeBidi(REQ_DL,  ALEF + BET + GIMEL + " abc", "1111222", R);
     }
     
     //@SmallTest
     public void testNativeBidi() {
-        // native bidi returns levels, not simply directions
-        expectNativeBidi(REQ_DL,  ALEF + BET + GIMEL + " abc", "1111222", R);
+        expectNativeBidi(REQ_L,  ALEF + BET + GIMEL + " abc", "1110000", L);
     }
     
     private void expectNativeBidi(int dir, String text, 
diff --git a/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
new file mode 100644
index 0000000..4fde849
--- /dev/null
+++ b/core/tests/coretests/src/android/text/StaticLayoutDirectionsTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.text;
+
+import android.text.Layout.Directions;
+import android.text.StaticLayoutTest.LayoutBuilder;
+
+import java.util.Arrays;
+import java.util.Formatter;
+
+import junit.framework.TestCase;
+
+public class StaticLayoutDirectionsTest extends TestCase {
+    private static final char ALEF = '\u05d0';
+
+    private static Directions dirs(int ... dirs) {
+        return new Directions(dirs);
+    }
+
+    // constants from Layout that are package-protected
+    private static final int RUN_LENGTH_MASK = 0x03ffffff;
+    private static final int RUN_LEVEL_SHIFT = 26;
+    private static final int RUN_LEVEL_MASK = 0x3f;
+    private static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT;
+
+    private static final Directions DIRS_ALL_LEFT_TO_RIGHT =
+        new Directions(new int[] { 0, RUN_LENGTH_MASK });
+    private static final Directions DIRS_ALL_RIGHT_TO_LEFT =
+        new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG });
+
+    private static final int LVL1_1 = 1 | (1 << RUN_LEVEL_SHIFT);
+    private static final int LVL2_1 = 1 | (2 << RUN_LEVEL_SHIFT);
+    private static final int LVL2_2 = 2 | (2 << RUN_LEVEL_SHIFT);
+
+    private static String[] texts = {
+        "",
+        " ",
+        "a",
+        "a1",
+        "aA",
+        "a1b",
+        "a1A",
+        "aA1",
+        "aAb",
+        "aA1B",
+        "aA1B2",
+
+        // rtl
+        "A",
+        "A1",
+        "Aa",
+        "A1B",
+        "A1a",
+        "Aa1",
+        "AaB"
+    };
+
+    // Expected directions are an array of start/length+level pairs,
+    // in visual order from the leading margin.
+    private static Directions[] expected = {
+        DIRS_ALL_LEFT_TO_RIGHT,
+        DIRS_ALL_LEFT_TO_RIGHT,
+        DIRS_ALL_LEFT_TO_RIGHT,
+        DIRS_ALL_LEFT_TO_RIGHT,
+        dirs(0, 1, 1, LVL1_1),
+        DIRS_ALL_LEFT_TO_RIGHT,
+        dirs(0, 2, 2, LVL1_1),
+        dirs(0, 1, 2, LVL2_1, 1, LVL1_1),
+        dirs(0, 1, 1, LVL1_1, 2, 1),
+        dirs(0, 1, 3, LVL1_1, 2, LVL2_1, 1, LVL1_1),
+        dirs(0, 1, 4, LVL2_1, 3, LVL1_1, 2, LVL2_1, 1, LVL1_1),
+
+        // rtl
+        DIRS_ALL_RIGHT_TO_LEFT,
+        dirs(0, LVL1_1, 1, LVL2_1),
+        dirs(0, LVL1_1, 1, LVL2_1),
+        dirs(0, LVL1_1, 1, LVL2_1, 2, LVL1_1),
+        dirs(0, LVL1_1, 1, LVL2_2),
+        dirs(0, LVL1_1, 1, LVL2_2),
+        dirs(0, LVL1_1, 1, LVL2_1, 2, LVL1_1),
+    };
+
+    private static String pseudoBidiToReal(String src) {
+        char[] chars = src.toCharArray();
+        for (int j = 0; j < chars.length; ++j) {
+            char c = chars[j];
+            if (c >= 'A' && c <= 'D') {
+                chars[j] = (char)(ALEF + c - 'A');
+            }
+        }
+
+        return new String(chars, 0, chars.length);
+    }
+
+    // @SmallTest
+    public void testDirections() {
+        StringBuilder buf = new StringBuilder("\n");
+        Formatter f = new Formatter(buf);
+
+        LayoutBuilder b = StaticLayoutTest.builder();
+        for (int i = 0; i < texts.length; ++i) {
+            b.setText(pseudoBidiToReal(texts[i]));
+            checkDirections(b.build(), i, b.text, expected, f);
+        }
+        if (buf.length() > 1) {
+            fail(buf.toString());
+        }
+    }
+
+    // @SmallTest
+    public void testTrailingWhitespace() {
+        LayoutBuilder b = StaticLayoutTest.builder();
+        b.setText(pseudoBidiToReal("Ab   c"));
+        float width = b.paint.measureText(b.text, 0, 5);  // exclude 'c'
+        b.setWidth(Math.round(width));
+        Layout l = b.build();
+        if (l.getLineCount() != 2) {
+            throw new RuntimeException("expected 2 lines, got: " + l.getLineCount());
+        }
+        Directions result = l.getLineDirections(0);
+        Directions expected = dirs(0, LVL1_1, 1, LVL2_1, 2, 3 | (1 << Layout.RUN_LEVEL_SHIFT));
+        expectDirections("split line", expected, result);
+    }
+
+    public void testNextToRightOf() {
+        LayoutBuilder b = StaticLayoutTest.builder();
+        b.setText(pseudoBidiToReal("aA1B2"));
+        // visual a2B1A positions 04321
+        // 0: |a2B1A, strong is sol, after -> 0
+        // 1: a|2B1A, strong is a, after ->, 1
+        // 2: a2|B1A, strong is B, after -> 4
+        // 3: a2B|1A, strong is B, before -> 3
+        // 4: a2B1|A, strong is A, after -> 2
+        // 5: a2B1A|, strong is eol, before -> 5
+        int[] expected = { 0, 1, 4, 3, 2, 5 };
+        Layout l = b.build();
+        int n = 0;
+        for (int i = 1; i < expected.length; ++i) {
+            int t = l.getOffsetToRightOf(n);
+            if (t != expected[i]) {
+                fail("offset[" + i + "] to right of: " + n + " expected: " +
+                        expected[i] + " got: " + t);
+            }
+            n = t;
+        }
+    }
+
+    public void testNextToLeftOf() {
+        LayoutBuilder b = StaticLayoutTest.builder();
+        b.setText(pseudoBidiToReal("aA1B2"));
+        int[] expected = { 0, 1, 4, 3, 2, 5 };
+        Layout l = b.build();
+        int n = 5;
+        for (int i = expected.length - 1; --i >= 0;) {
+            int t = l.getOffsetToLeftOf(n);
+            if (t != expected[i]) {
+                fail("offset[" + i + "] to left of: " + n + " expected: " +
+                        expected[i] + " got: " + t);
+            }
+            n = t;
+        }
+    }
+
+    // utility, not really a test
+    /*
+    public void testMeasureText1() {
+        LayoutBuilder b = StaticLayoutTest.builder();
+        String text = "ABC"; // "abAB"
+        b.setText(pseudoBidiToReal(text));
+        Layout l = b.build();
+        Directions directions = l.getLineDirections(0);
+
+        TextPaint workPaint = new TextPaint();
+
+        int dir = -1; // LEFT_TO_RIGHT
+        boolean trailing = true;
+        boolean alt = true;
+        do {
+            dir = -dir;
+            do {
+                trailing = !trailing;
+                for (int offset = 0, end = b.text.length(); offset <= end; ++offset) {
+                    float width = Layout.measureText(b.paint,
+                            workPaint,
+                            b.text,
+                            0, offset, end,
+                            dir, directions,
+                            trailing, false,
+                            null);
+                    Log.i("BIDI", "dir: " + dir + " trail: " + trailing +
+                            " offset: " + offset + " width: " + width);
+                }
+            } while (!trailing);
+        } while (dir > 0);
+    }
+    */
+
+    // utility for displaying arrays in hex
+    private static String hexArray(int[] array) {
+        StringBuilder sb = new StringBuilder();
+        sb.append('{');
+        for (int i : array) {
+            if (sb.length() > 1) {
+                sb.append(", ");
+            }
+            sb.append(Integer.toHexString(i));
+        }
+        sb.append('}');
+        return sb.toString();
+    }
+
+    private void checkDirections(Layout l, int i, String text,
+            Directions[] expectedDirs, Formatter f) {
+        Directions expected = expectedDirs[i];
+        Directions result = l.getLineDirections(0);
+        if (!Arrays.equals(expected.mDirections, result.mDirections)) {
+            f.format("%n[%2d] '%s', %s != %s", i, text,
+                    hexArray(expected.mDirections),
+                    hexArray(result.mDirections));
+        }
+    }
+
+    private void expectDirections(String msg, Directions expected, Directions result) {
+        if (!Arrays.equals(expected.mDirections, result.mDirections)) {
+            fail("expected: " + hexArray(expected.mDirections) +
+                    " got: " + hexArray(result.mDirections));
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index 1f58a2c..d554a50 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -228,11 +228,11 @@
         }
     }
 
-    private static LayoutBuilder builder() {
+    /* package */ static LayoutBuilder builder() {
         return new LayoutBuilder();
     }
 
-    private static class LayoutBuilder {
+    /* package */ static class LayoutBuilder {
         String text = "This is a test";
         TextPaint paint = new TextPaint(); // default
         int width = 100;
diff --git a/core/tests/coretests/src/android/util/ExpandableListScenario.java b/core/tests/coretests/src/android/util/ExpandableListScenario.java
deleted file mode 100644
index 4a12b0d..0000000
--- a/core/tests/coretests/src/android/util/ExpandableListScenario.java
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * 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.util;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.BaseExpandableListAdapter;
-import android.widget.ExpandableListAdapter;
-import android.widget.ExpandableListView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-/**
- * Utility base class for creating various Expandable List scenarios.
- * <p>
- * WARNING: A lot of the features are mixed between ListView's expected position
- * (flat list position) and an ExpandableListView's expected position.  You must add/change
- * features as you need them.
- * 
- * @see ListScenario
- */
-public abstract class ExpandableListScenario extends ListScenario {
-    protected ExpandableListAdapter mAdapter; 
-    protected List<MyGroup> mGroups;
-    
-    @Override
-    protected ListView createListView() {
-        return new ExpandableListView(this);
-    }
-
-    @Override
-    protected Params createParams() {
-        return new ExpandableParams();
-    }
-
-    @Override
-    protected void setAdapter(ListView listView) {
-        ((ExpandableListView) listView).setAdapter(mAdapter = createAdapter());
-    }
-    
-    protected ExpandableListAdapter createAdapter() {
-        return new MyAdapter();
-    }
-    
-    @Override
-    protected void readAndValidateParams(Params params) {
-        ExpandableParams expandableParams = (ExpandableParams) params;
-        
-        int[] numChildren = expandableParams.mNumChildren;
-        
-        mGroups = new ArrayList<MyGroup>(numChildren.length);
-        for (int i = 0; i < numChildren.length; i++) {
-            mGroups.add(new MyGroup(numChildren[i]));
-        }
-        
-        expandableParams.superSetNumItems();
-        
-        super.readAndValidateParams(params);
-    }
-
-    /**
-     * Get the ExpandableListView widget.
-     * @return The main widget.
-     */
-    public ExpandableListView getExpandableListView() {
-        return (ExpandableListView) super.getListView();
-    }
-
-    public static class ExpandableParams extends Params {
-        private int[] mNumChildren;
-        
-        /**
-         * Sets the number of children per group.
-         *  
-         * @param numChildrenPerGroup The number of children per group.
-         */
-        public ExpandableParams setNumChildren(int[] numChildren) {
-            mNumChildren = numChildren;
-            return this;
-        }
-
-        /**
-         * Sets the number of items on the superclass based on the number of
-         * groups and children per group.
-         */
-        private ExpandableParams superSetNumItems() {
-            int numItems = 0;
-            
-            if (mNumChildren != null) {
-                for (int i = mNumChildren.length - 1; i >= 0; i--) {
-                    numItems += mNumChildren[i];
-                }
-            }
-            
-            super.setNumItems(numItems);
-            
-            return this;
-        }
-        
-        @Override
-        public Params setNumItems(int numItems) {
-            throw new IllegalStateException("Use setNumGroups and setNumChildren instead.");
-        }
-
-        @Override
-        public ExpandableParams setFadingEdgeScreenSizeFactor(double fadingEdgeScreenSizeFactor) {
-            return (ExpandableParams) super.setFadingEdgeScreenSizeFactor(fadingEdgeScreenSizeFactor);
-        }
-
-        @Override
-        public ExpandableParams setItemScreenSizeFactor(double itemScreenSizeFactor) {
-            return (ExpandableParams) super.setItemScreenSizeFactor(itemScreenSizeFactor);
-        }
-
-        @Override
-        public ExpandableParams setItemsFocusable(boolean itemsFocusable) {
-            return (ExpandableParams) super.setItemsFocusable(itemsFocusable);
-        }
-
-        @Override
-        public ExpandableParams setMustFillScreen(boolean fillScreen) {
-            return (ExpandableParams) super.setMustFillScreen(fillScreen);
-        }
-
-        @Override
-        public ExpandableParams setPositionScreenSizeFactorOverride(int position, double itemScreenSizeFactor) {
-            return (ExpandableParams) super.setPositionScreenSizeFactorOverride(position, itemScreenSizeFactor);
-        }
-
-        @Override
-        public ExpandableParams setPositionUnselectable(int position) {
-            return (ExpandableParams) super.setPositionUnselectable(position);
-        }
-
-        @Override
-        public ExpandableParams setStackFromBottom(boolean stackFromBottom) {
-            return (ExpandableParams) super.setStackFromBottom(stackFromBottom);
-        }
-
-        @Override
-        public ExpandableParams setStartingSelectionPosition(int startingSelectionPosition) {
-            return (ExpandableParams) super.setStartingSelectionPosition(startingSelectionPosition);
-        }
-
-        @Override
-        public ExpandableParams setConnectAdapter(boolean connectAdapter) {
-            return (ExpandableParams) super.setConnectAdapter(connectAdapter);
-        }
-    }
-
-    /**
-     * Gets a string for the value of some item.
-     * @param packedPosition The position of the item.
-     * @return The string.
-     */
-    public final String getValueAtPosition(long packedPosition) {
-        final int type = ExpandableListView.getPackedPositionType(packedPosition);
-        
-        if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
-            return mGroups.get(ExpandableListView.getPackedPositionGroup(packedPosition))
-                    .children.get(ExpandableListView.getPackedPositionChild(packedPosition))
-                    .name;
-        } else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
-            return mGroups.get(ExpandableListView.getPackedPositionGroup(packedPosition))
-                    .name;
-        } else {
-            throw new IllegalStateException("packedPosition is not a valid position.");
-        }
-    }
-
-    /**
-     * Whether a particular position is out of bounds.
-     * 
-     * @param packedPosition The packed position.
-     * @return Whether it's out of bounds.
-     */
-    private boolean isOutOfBounds(long packedPosition) {
-        final int type = ExpandableListView.getPackedPositionType(packedPosition);
-        
-        if (type == ExpandableListView.PACKED_POSITION_TYPE_NULL) {
-            throw new IllegalStateException("packedPosition is not a valid position.");
-        }
-
-        final int group = ExpandableListView.getPackedPositionGroup(packedPosition); 
-        if (group >= mGroups.size() || group < 0) {
-            return true;
-        }
-        
-        if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
-            final int child = ExpandableListView.getPackedPositionChild(packedPosition); 
-            if (child >= mGroups.get(group).children.size() || child < 0) {
-                return true;
-            }
-        }
-        
-        return false;
-    }
-    
-    /**
-     * Gets a view for the packed position, possibly reusing the convertView.
-     * 
-     * @param packedPosition The position to get a view for.
-     * @param convertView Optional view to convert.
-     * @param parent The future parent.
-     * @return A view.
-     */
-    private View getView(long packedPosition, View convertView, ViewGroup parent) {
-        if (isOutOfBounds(packedPosition)) {
-            throw new IllegalStateException("position out of range for adapter!");
-        }
-        
-        final ExpandableListView elv = getExpandableListView();
-        final int flPos = elv.getFlatListPosition(packedPosition); 
-        
-        if (convertView != null) {
-            ((TextView) convertView).setText(getValueAtPosition(packedPosition));
-            convertView.setId(flPos);
-            return convertView;
-        }
-
-        int desiredHeight = getHeightForPosition(flPos);
-        return createView(packedPosition, flPos, parent, desiredHeight);
-    }
-    
-    /**
-     * Create a view for a group or child position.
-     * 
-     * @param packedPosition The packed position (has type, group pos, and optionally child pos).
-     * @param flPos The flat list position (the position that the ListView goes by).
-     * @param parent The parent view.
-     * @param desiredHeight The desired height.
-     * @return A view.
-     */
-    protected View createView(long packedPosition, int flPos, ViewGroup parent, int desiredHeight) {
-        TextView result = new TextView(parent.getContext());
-        result.setHeight(desiredHeight);
-        result.setText(getValueAtPosition(packedPosition));
-        final ViewGroup.LayoutParams lp = new AbsListView.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.WRAP_CONTENT);
-        result.setLayoutParams(lp);
-        result.setGravity(Gravity.CENTER_VERTICAL);
-        result.setPadding(36, 0, 0, 0);
-        result.setId(flPos);
-        return result;
-    }
-    
-    /**
-     * Returns a group index containing either the number of children or at
-     * least one child.
-     * 
-     * @param numChildren The group must have this amount, or -1 if using
-     *            atLeastOneChild.
-     * @param atLeastOneChild The group must have at least one child, or false
-     *            if using numChildren.
-     * @return A group index with the requirements.
-     */
-    public int findGroupWithNumChildren(int numChildren, boolean atLeastOneChild) {
-        final ExpandableListAdapter adapter = mAdapter;
-        
-        for (int i = adapter.getGroupCount() - 1; i >= 0; i--) {
-            final int curNumChildren = adapter.getChildrenCount(i);
-            
-            if (numChildren == curNumChildren || atLeastOneChild && curNumChildren > 0) {
-                return i;
-            }
-        }
-        
-        return -1;
-    }
-    
-    public List<MyGroup> getGroups() {
-        return mGroups;
-    }
-    
-    public ExpandableListAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Simple expandable list adapter.
-     */
-    protected class MyAdapter extends BaseExpandableListAdapter {
-        public Object getChild(int groupPosition, int childPosition) {
-            return getValueAtPosition(ExpandableListView.getPackedPositionForChild(groupPosition,
-                    childPosition));
-        }
-
-        public long getChildId(int groupPosition, int childPosition) {
-            return mGroups.get(groupPosition).children.get(childPosition).id;
-        }
-
-        public int getChildrenCount(int groupPosition) {
-            return mGroups.get(groupPosition).children.size();
-        }
-
-        public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
-                View convertView, ViewGroup parent) {
-            return getView(ExpandableListView.getPackedPositionForChild(groupPosition,
-                    childPosition), convertView, parent);
-        }
-
-        public Object getGroup(int groupPosition) {
-            return getValueAtPosition(ExpandableListView.getPackedPositionForGroup(groupPosition));
-        }
-
-        public int getGroupCount() {
-            return mGroups.size();
-        }
-
-        public long getGroupId(int groupPosition) {
-            return mGroups.get(groupPosition).id;
-        }
-
-        public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
-                ViewGroup parent) {
-            return getView(ExpandableListView.getPackedPositionForGroup(groupPosition),
-                    convertView, parent);
-        }
-
-        public boolean isChildSelectable(int groupPosition, int childPosition) {
-            return true;
-        }
-
-        public boolean hasStableIds() {
-            return true;
-        }
-        
-    }
-
-    public static class MyGroup {
-        private static long mNextId = 1000;
-        
-        String name;
-        long id = mNextId++;
-        List<MyChild> children;
-        
-        public MyGroup(int numChildren) {
-            name = "Group " + id;
-            children = new ArrayList<MyChild>(numChildren);
-            for (int i = 0; i < numChildren; i++) {
-                children.add(new MyChild());
-            }
-        }
-    }
-    
-    public static class MyChild {
-        private static long mNextId = 2000;
-        
-        String name;
-        long id = mNextId++;
-        
-        public MyChild() {
-            name = "Child " + id;
-        }
-    }
-    
-    @Override
-    protected final void init(Params params) {
-        init((ExpandableParams) params);
-    }
-
-    /**
-     * @see ListScenario#init
-     */
-    protected abstract void init(ExpandableParams params);
-}
diff --git a/core/tests/coretests/src/android/util/JsonReaderTest.java b/core/tests/coretests/src/android/util/JsonReaderTest.java
new file mode 100644
index 0000000..ced9310
--- /dev/null
+++ b/core/tests/coretests/src/android/util/JsonReaderTest.java
@@ -0,0 +1,683 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+public final class JsonReaderTest extends TestCase {
+
+    public void testReadArray() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[true, true]"));
+        reader.beginArray();
+        assertEquals(true, reader.nextBoolean());
+        assertEquals(true, reader.nextBoolean());
+        reader.endArray();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+    }
+
+    public void testReadEmptyArray() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[]"));
+        reader.beginArray();
+        assertFalse(reader.hasNext());
+        reader.endArray();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+    }
+
+    public void testReadObject() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader(
+                "{\"a\": \"android\", \"b\": \"banana\"}"));
+        reader.beginObject();
+        assertEquals("a", reader.nextName());
+        assertEquals("android", reader.nextString());
+        assertEquals("b", reader.nextName());
+        assertEquals("banana", reader.nextString());
+        reader.endObject();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+    }
+
+    public void testReadEmptyObject() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("{}"));
+        reader.beginObject();
+        assertFalse(reader.hasNext());
+        reader.endObject();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+    }
+
+    public void testSkipObject() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader(
+                "{\"a\": { \"c\": [], \"d\": [true, true, {}] }, \"b\": \"banana\"}"));
+        reader.beginObject();
+        assertEquals("a", reader.nextName());
+        reader.skipValue();
+        assertEquals("b", reader.nextName());
+        reader.skipValue();
+        reader.endObject();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+    }
+
+    public void testHelloWorld() throws IOException {
+        String json = "{\n" +
+                "   \"hello\": true,\n" +
+                "   \"foo\": [\"world\"]\n" +
+                "}";
+        JsonReader reader = new JsonReader(new StringReader(json));
+        reader.beginObject();
+        assertEquals("hello", reader.nextName());
+        assertEquals(true, reader.nextBoolean());
+        assertEquals("foo", reader.nextName());
+        reader.beginArray();
+        assertEquals("world", reader.nextString());
+        reader.endArray();
+        reader.endObject();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+    }
+
+    public void testNulls() {
+        try {
+            new JsonReader(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    public void testEmptyString() throws IOException {
+        try {
+            new JsonReader(new StringReader("")).beginArray();
+        } catch (IOException expected) {
+        }
+        try {
+            new JsonReader(new StringReader("")).beginObject();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testNoTopLevelObject() throws IOException {
+        try {
+            new JsonReader(new StringReader("true")).nextBoolean();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testCharacterUnescaping() throws IOException {
+        String json = "[\"a\","
+                + "\"a\\\"\","
+                + "\"\\\"\","
+                + "\":\","
+                + "\",\","
+                + "\"\\b\","
+                + "\"\\f\","
+                + "\"\\n\","
+                + "\"\\r\","
+                + "\"\\t\","
+                + "\" \","
+                + "\"\\\\\","
+                + "\"{\","
+                + "\"}\","
+                + "\"[\","
+                + "\"]\","
+                + "\"\\u0000\","
+                + "\"\\u0019\","
+                + "\"\\u20AC\""
+                + "]";
+        JsonReader reader = new JsonReader(new StringReader(json));
+        reader.beginArray();
+        assertEquals("a", reader.nextString());
+        assertEquals("a\"", reader.nextString());
+        assertEquals("\"", reader.nextString());
+        assertEquals(":", reader.nextString());
+        assertEquals(",", reader.nextString());
+        assertEquals("\b", reader.nextString());
+        assertEquals("\f", reader.nextString());
+        assertEquals("\n", reader.nextString());
+        assertEquals("\r", reader.nextString());
+        assertEquals("\t", reader.nextString());
+        assertEquals(" ", reader.nextString());
+        assertEquals("\\", reader.nextString());
+        assertEquals("{", reader.nextString());
+        assertEquals("}", reader.nextString());
+        assertEquals("[", reader.nextString());
+        assertEquals("]", reader.nextString());
+        assertEquals("\0", reader.nextString());
+        assertEquals("\u0019", reader.nextString());
+        assertEquals("\u20AC", reader.nextString());
+        reader.endArray();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+    }
+
+    public void testIntegersWithFractionalPartSpecified() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[1.0,1.0,1.0]"));
+        reader.beginArray();
+        assertEquals(1.0, reader.nextDouble());
+        assertEquals(1, reader.nextInt());
+        assertEquals(1L, reader.nextLong());
+    }
+
+    public void testDoubles() throws IOException {
+        String json = "[-0.0,"
+                + "1.0,"
+                + "1.7976931348623157E308,"
+                + "4.9E-324,"
+                + "0.0,"
+                + "-0.5,"
+                + "2.2250738585072014E-308,"
+                + "3.141592653589793,"
+                + "2.718281828459045]";
+        JsonReader reader = new JsonReader(new StringReader(json));
+        reader.beginArray();
+        assertEquals(-0.0, reader.nextDouble());
+        assertEquals(1.0, reader.nextDouble());
+        assertEquals(1.7976931348623157E308, reader.nextDouble());
+        assertEquals(4.9E-324, reader.nextDouble());
+        assertEquals(0.0, reader.nextDouble());
+        assertEquals(-0.5, reader.nextDouble());
+        assertEquals(2.2250738585072014E-308, reader.nextDouble());
+        assertEquals(3.141592653589793, reader.nextDouble());
+        assertEquals(2.718281828459045, reader.nextDouble());
+        reader.endArray();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+    }
+
+    public void testNonFiniteDoubles() throws IOException {
+        String json = "[NaN]";
+        JsonReader reader = new JsonReader(new StringReader(json));
+        reader.beginArray();
+        try {
+            reader.nextDouble();
+            fail();
+        } catch (NumberFormatException expected) {
+        }
+    }
+
+    public void testLongs() throws IOException {
+        String json = "[0,0,0,"
+                + "1,1,1,"
+                + "-1,-1,-1,"
+                + "-9223372036854775808,"
+                + "9223372036854775807]";
+        JsonReader reader = new JsonReader(new StringReader(json));
+        reader.beginArray();
+        assertEquals(0L, reader.nextLong());
+        assertEquals(0, reader.nextInt());
+        assertEquals(0.0, reader.nextDouble());
+        assertEquals(1L, reader.nextLong());
+        assertEquals(1, reader.nextInt());
+        assertEquals(1.0, reader.nextDouble());
+        assertEquals(-1L, reader.nextLong());
+        assertEquals(-1, reader.nextInt());
+        assertEquals(-1.0, reader.nextDouble());
+        try {
+            reader.nextInt();
+            fail();
+        } catch (NumberFormatException expected) {
+        }
+        assertEquals(Long.MIN_VALUE, reader.nextLong());
+        try {
+            reader.nextInt();
+            fail();
+        } catch (NumberFormatException expected) {
+        }
+        assertEquals(Long.MAX_VALUE, reader.nextLong());
+        reader.endArray();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+    }
+
+    /**
+     * This test fails because there's no double for 9223372036854775806, and
+     * our long parsing uses Double.parseDouble() for fractional values.
+     */
+    public void testHighPrecisionLong() throws IOException {
+        String json = "[9223372036854775806.000]";
+        JsonReader reader = new JsonReader(new StringReader(json));
+        reader.beginArray();
+        assertEquals(9223372036854775806L, reader.nextLong());
+        reader.endArray();
+    }
+
+    public void testNumberWithOctalPrefix() throws IOException {
+        String json = "[01]";
+        JsonReader reader = new JsonReader(new StringReader(json));
+        reader.beginArray();
+        try {
+            reader.nextInt();
+            fail();
+        } catch (NumberFormatException expected) {
+        }
+        try {
+            reader.nextLong();
+            fail();
+        } catch (NumberFormatException expected) {
+        }
+        try {
+            reader.nextDouble();
+            fail();
+        } catch (NumberFormatException expected) {
+        }
+        assertEquals("01", reader.nextString());
+        reader.endArray();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+    }
+
+    public void testBooleans() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[true,false]"));
+        reader.beginArray();
+        assertEquals(true, reader.nextBoolean());
+        assertEquals(false, reader.nextBoolean());
+        reader.endArray();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+    }
+
+    public void testMixedCaseLiterals() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[True,TruE,False,FALSE,NULL,nulL]"));
+        reader.beginArray();
+        assertEquals(true, reader.nextBoolean());
+        assertEquals(true, reader.nextBoolean());
+        assertEquals(false, reader.nextBoolean());
+        assertEquals(false, reader.nextBoolean());
+        reader.nextNull();
+        reader.nextNull();
+        reader.endArray();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+    }
+
+    public void testMissingValue() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("{\"a\":}"));
+        reader.beginObject();
+        assertEquals("a", reader.nextName());
+        try {
+            reader.nextString();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testPrematureEndOfInput() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("{\"a\":true,"));
+        reader.beginObject();
+        assertEquals("a", reader.nextName());
+        assertEquals(true, reader.nextBoolean());
+        try {
+            reader.nextName();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testPrematurelyClosed() throws IOException {
+        try {
+            JsonReader reader = new JsonReader(new StringReader("{\"a\":[]}"));
+            reader.beginObject();
+            reader.close();
+            reader.nextName();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+
+        try {
+            JsonReader reader = new JsonReader(new StringReader("{\"a\":[]}"));
+            reader.close();
+            reader.beginObject();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+
+        try {
+            JsonReader reader = new JsonReader(new StringReader("{\"a\":true}"));
+            reader.beginObject();
+            reader.nextName();
+            reader.peek();
+            reader.close();
+            reader.nextBoolean();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    public void testNextFailuresDoNotAdvance() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("{\"a\":true}"));
+        reader.beginObject();
+        try {
+            reader.nextString();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+        assertEquals("a", reader.nextName());
+        try {
+            reader.nextName();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+        try {
+            reader.beginArray();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+        try {
+            reader.endArray();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+        try {
+            reader.beginObject();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+        try {
+            reader.endObject();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+        assertEquals(true, reader.nextBoolean());
+        try {
+            reader.nextString();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+        try {
+            reader.nextName();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+        try {
+            reader.beginArray();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+        try {
+            reader.endArray();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+        reader.endObject();
+        assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+        reader.close();
+    }
+
+    public void testStringNullIsNotNull() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[\"null\"]"));
+        reader.beginArray();
+        try {
+            reader.nextNull();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    public void testNullLiteralIsNotAString() throws IOException {
+       JsonReader reader = new JsonReader(new StringReader("[null]"));
+        reader.beginArray();
+        try {
+            reader.nextString();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    public void testStrictNameValueSeparator() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("{\"a\"=true}"));
+        reader.beginObject();
+        assertEquals("a", reader.nextName());
+        try {
+            reader.nextBoolean();
+            fail();
+        } catch (IOException expected) {
+        }
+
+        reader = new JsonReader(new StringReader("{\"a\"=>true}"));
+        reader.beginObject();
+        assertEquals("a", reader.nextName());
+        try {
+            reader.nextBoolean();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testLenientNameValueSeparator() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("{\"a\"=true}"));
+        reader.setLenient(true);
+        reader.beginObject();
+        assertEquals("a", reader.nextName());
+        assertEquals(true, reader.nextBoolean());
+
+        reader = new JsonReader(new StringReader("{\"a\"=>true}"));
+        reader.setLenient(true);
+        reader.beginObject();
+        assertEquals("a", reader.nextName());
+        assertEquals(true, reader.nextBoolean());
+    }
+
+    public void testStrictComments() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[// comment \n true]"));
+        reader.beginArray();
+        try {
+            reader.nextBoolean();
+            fail();
+        } catch (IOException expected) {
+        }
+
+        reader = new JsonReader(new StringReader("[# comment \n true]"));
+        reader.beginArray();
+        try {
+            reader.nextBoolean();
+            fail();
+        } catch (IOException expected) {
+        }
+
+        reader = new JsonReader(new StringReader("[/* comment */ true]"));
+        reader.beginArray();
+        try {
+            reader.nextBoolean();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testLenientComments() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[// comment \n true]"));
+        reader.setLenient(true);
+        reader.beginArray();
+        assertEquals(true, reader.nextBoolean());
+
+        reader = new JsonReader(new StringReader("[# comment \n true]"));
+        reader.setLenient(true);
+        reader.beginArray();
+        assertEquals(true, reader.nextBoolean());
+
+        reader = new JsonReader(new StringReader("[/* comment */ true]"));
+        reader.setLenient(true);
+        reader.beginArray();
+        assertEquals(true, reader.nextBoolean());
+    }
+
+    public void testStrictUnquotedNames() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("{a:true}"));
+        reader.beginObject();
+        try {
+            reader.nextName();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testLenientUnquotedNames() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("{a:true}"));
+        reader.setLenient(true);
+        reader.beginObject();
+        assertEquals("a", reader.nextName());
+    }
+
+    public void testStrictSingleQuotedNames() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("{'a':true}"));
+        reader.beginObject();
+        try {
+            reader.nextName();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testLenientSingleQuotedNames() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("{'a':true}"));
+        reader.setLenient(true);
+        reader.beginObject();
+        assertEquals("a", reader.nextName());
+    }
+
+    public void testStrictUnquotedStrings() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[a]"));
+        reader.beginArray();
+        try {
+            reader.nextString();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testLenientUnquotedStrings() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[a]"));
+        reader.setLenient(true);
+        reader.beginArray();
+        assertEquals("a", reader.nextString());
+    }
+
+    public void testStrictSingleQuotedStrings() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("['a']"));
+        reader.beginArray();
+        try {
+            reader.nextString();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testLenientSingleQuotedStrings() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("['a']"));
+        reader.setLenient(true);
+        reader.beginArray();
+        assertEquals("a", reader.nextString());
+    }
+
+    public void testStrictSemicolonDelimitedArray() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[true;true]"));
+        reader.beginArray();
+        try {
+            reader.nextBoolean();
+            reader.nextBoolean();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testLenientSemicolonDelimitedArray() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[true;true]"));
+        reader.setLenient(true);
+        reader.beginArray();
+        assertEquals(true, reader.nextBoolean());
+        assertEquals(true, reader.nextBoolean());
+    }
+
+    public void testStrictSemicolonDelimitedNameValuePair() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("{\"a\":true;\"b\":true}"));
+        reader.beginObject();
+        assertEquals("a", reader.nextName());
+        try {
+            reader.nextBoolean();
+            reader.nextName();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testLenientSemicolonDelimitedNameValuePair() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("{\"a\":true;\"b\":true}"));
+        reader.setLenient(true);
+        reader.beginObject();
+        assertEquals("a", reader.nextName());
+        assertEquals(true, reader.nextBoolean());
+        assertEquals("b", reader.nextName());
+    }
+
+    public void testStrictUnnecessaryArraySeparators() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[true,,true]"));
+        reader.beginArray();
+        assertEquals(true, reader.nextBoolean());
+        try {
+            reader.nextNull();
+            fail();
+        } catch (IOException expected) {
+        }
+
+        reader = new JsonReader(new StringReader("[,true]"));
+        reader.beginArray();
+        try {
+            reader.nextNull();
+            fail();
+        } catch (IOException expected) {
+        }
+
+        reader = new JsonReader(new StringReader("[true,]"));
+        reader.beginArray();
+        assertEquals(true, reader.nextBoolean());
+        try {
+            reader.nextNull();
+            fail();
+        } catch (IOException expected) {
+        }
+
+        reader = new JsonReader(new StringReader("[,]"));
+        reader.beginArray();
+        try {
+            reader.nextNull();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testLenientUnnecessaryArraySeparators() throws IOException {
+        JsonReader reader = new JsonReader(new StringReader("[true,,true]"));
+        reader.setLenient(true);
+        reader.beginArray();
+        assertEquals(true, reader.nextBoolean());
+        reader.nextNull();
+        assertEquals(true, reader.nextBoolean());
+        reader.endArray();
+
+        reader = new JsonReader(new StringReader("[,true]"));
+        reader.setLenient(true);
+        reader.beginArray();
+        reader.nextNull();
+        assertEquals(true, reader.nextBoolean());
+        reader.endArray();
+
+        reader = new JsonReader(new StringReader("[true,]"));
+        reader.setLenient(true);
+        reader.beginArray();
+        assertEquals(true, reader.nextBoolean());
+        reader.nextNull();
+        reader.endArray();
+
+        reader = new JsonReader(new StringReader("[,]"));
+        reader.setLenient(true);
+        reader.beginArray();
+        reader.nextNull();
+        reader.nextNull();
+        reader.endArray();
+    }
+}
diff --git a/core/tests/coretests/src/android/util/JsonWriterTest.java b/core/tests/coretests/src/android/util/JsonWriterTest.java
new file mode 100644
index 0000000..fa84023
--- /dev/null
+++ b/core/tests/coretests/src/android/util/JsonWriterTest.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+public final class JsonWriterTest extends TestCase {
+
+    public void testWrongTopLevelType() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        try {
+            jsonWriter.value("a");
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    public void testTwoNames() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginObject();
+        jsonWriter.name("a");
+        try {
+            jsonWriter.name("a");
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    public void testNameWithoutValue() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginObject();
+        jsonWriter.name("a");
+        try {
+            jsonWriter.endObject();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    public void testValueWithoutName() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginObject();
+        try {
+            jsonWriter.value(true);
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    public void testMultipleTopLevelValues() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginArray().endArray();
+        try {
+            jsonWriter.beginArray();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    public void testBadNestingObject() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginArray();
+        jsonWriter.beginObject();
+        try {
+            jsonWriter.endArray();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    public void testBadNestingArray() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginArray();
+        jsonWriter.beginArray();
+        try {
+            jsonWriter.endObject();
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    public void testNullName() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginObject();
+        try {
+            jsonWriter.name(null);
+            fail();
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    public void testNullStringValue() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginObject();
+        jsonWriter.name("a");
+        jsonWriter.value(null);
+        jsonWriter.endObject();
+        assertEquals("{\"a\":null}", stringWriter.toString());
+    }
+
+    public void testNonFiniteDoubles() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginArray();
+        try {
+            jsonWriter.value(Double.NaN);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            jsonWriter.value(Double.NEGATIVE_INFINITY);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            jsonWriter.value(Double.POSITIVE_INFINITY);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    public void testDoubles() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginArray();
+        jsonWriter.value(-0.0);
+        jsonWriter.value(1.0);
+        jsonWriter.value(Double.MAX_VALUE);
+        jsonWriter.value(Double.MIN_VALUE);
+        jsonWriter.value(0.0);
+        jsonWriter.value(-0.5);
+        jsonWriter.value(Double.MIN_NORMAL);
+        jsonWriter.value(Math.PI);
+        jsonWriter.value(Math.E);
+        jsonWriter.endArray();
+        jsonWriter.close();
+        assertEquals("[-0.0,"
+                + "1.0,"
+                + "1.7976931348623157E308,"
+                + "4.9E-324,"
+                + "0.0,"
+                + "-0.5,"
+                + "2.2250738585072014E-308,"
+                + "3.141592653589793,"
+                + "2.718281828459045]", stringWriter.toString());
+    }
+
+    public void testLongs() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginArray();
+        jsonWriter.value(0);
+        jsonWriter.value(1);
+        jsonWriter.value(-1);
+        jsonWriter.value(Long.MIN_VALUE);
+        jsonWriter.value(Long.MAX_VALUE);
+        jsonWriter.endArray();
+        jsonWriter.close();
+        assertEquals("[0,"
+                + "1,"
+                + "-1,"
+                + "-9223372036854775808,"
+                + "9223372036854775807]", stringWriter.toString());
+    }
+
+    public void testBooleans() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginArray();
+        jsonWriter.value(true);
+        jsonWriter.value(false);
+        jsonWriter.endArray();
+        assertEquals("[true,false]", stringWriter.toString());
+    }
+
+    public void testNulls() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginArray();
+        jsonWriter.nullValue();
+        jsonWriter.endArray();
+        assertEquals("[null]", stringWriter.toString());
+    }
+
+    public void testStrings() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginArray();
+        jsonWriter.value("a");
+        jsonWriter.value("a\"");
+        jsonWriter.value("\"");
+        jsonWriter.value(":");
+        jsonWriter.value(",");
+        jsonWriter.value("\b");
+        jsonWriter.value("\f");
+        jsonWriter.value("\n");
+        jsonWriter.value("\r");
+        jsonWriter.value("\t");
+        jsonWriter.value(" ");
+        jsonWriter.value("\\");
+        jsonWriter.value("{");
+        jsonWriter.value("}");
+        jsonWriter.value("[");
+        jsonWriter.value("]");
+        jsonWriter.value("\0");
+        jsonWriter.value("\u0019");
+        jsonWriter.endArray();
+        assertEquals("[\"a\","
+                + "\"a\\\"\","
+                + "\"\\\"\","
+                + "\":\","
+                + "\",\","
+                + "\"\\b\","
+                + "\"\\f\","
+                + "\"\\n\","
+                + "\"\\r\","
+                + "\"\\t\","
+                + "\" \","
+                + "\"\\\\\","
+                + "\"{\","
+                + "\"}\","
+                + "\"[\","
+                + "\"]\","
+                + "\"\\u0000\","
+                + "\"\\u0019\"]", stringWriter.toString());
+    }
+
+    public void testEmptyArray() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginArray();
+        jsonWriter.endArray();
+        assertEquals("[]", stringWriter.toString());
+    }
+
+    public void testEmptyObject() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginObject();
+        jsonWriter.endObject();
+        assertEquals("{}", stringWriter.toString());
+    }
+
+    public void testObjectsInArrays() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginArray();
+        jsonWriter.beginObject();
+        jsonWriter.name("a").value(5);
+        jsonWriter.name("b").value(false);
+        jsonWriter.endObject();
+        jsonWriter.beginObject();
+        jsonWriter.name("c").value(6);
+        jsonWriter.name("d").value(true);
+        jsonWriter.endObject();
+        jsonWriter.endArray();
+        assertEquals("[{\"a\":5,\"b\":false},"
+                + "{\"c\":6,\"d\":true}]", stringWriter.toString());
+    }
+
+    public void testArraysInObjects() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginObject();
+        jsonWriter.name("a");
+        jsonWriter.beginArray();
+        jsonWriter.value(5);
+        jsonWriter.value(false);
+        jsonWriter.endArray();
+        jsonWriter.name("b");
+        jsonWriter.beginArray();
+        jsonWriter.value(6);
+        jsonWriter.value(true);
+        jsonWriter.endArray();
+        jsonWriter.endObject();
+        assertEquals("{\"a\":[5,false],"
+                + "\"b\":[6,true]}", stringWriter.toString());
+    }
+
+    public void testDeepNestingArrays() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        for (int i = 0; i < 20; i++) {
+            jsonWriter.beginArray();
+        }
+        for (int i = 0; i < 20; i++) {
+            jsonWriter.endArray();
+        }
+        assertEquals("[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]", stringWriter.toString());
+    }
+
+    public void testDeepNestingObjects() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginObject();
+        for (int i = 0; i < 20; i++) {
+            jsonWriter.name("a");
+            jsonWriter.beginObject();
+        }
+        for (int i = 0; i < 20; i++) {
+            jsonWriter.endObject();
+        }
+        jsonWriter.endObject();
+        assertEquals("{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":"
+                + "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{"
+                + "}}}}}}}}}}}}}}}}}}}}}", stringWriter.toString());
+    }
+
+    public void testRepeatedName() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.beginObject();
+        jsonWriter.name("a").value(true);
+        jsonWriter.name("a").value(false);
+        jsonWriter.endObject();
+        // JsonWriter doesn't attempt to detect duplicate names
+        assertEquals("{\"a\":true,\"a\":false}", stringWriter.toString());
+    }
+
+    public void testPrettyPrintObject() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.setIndent("   ");
+
+        jsonWriter.beginObject();
+        jsonWriter.name("a").value(true);
+        jsonWriter.name("b").value(false);
+        jsonWriter.name("c").value(5.0);
+        jsonWriter.name("e").nullValue();
+        jsonWriter.name("f").beginArray();
+        jsonWriter.value(6.0);
+        jsonWriter.value(7.0);
+        jsonWriter.endArray();
+        jsonWriter.name("g").beginObject();
+        jsonWriter.name("h").value(8.0);
+        jsonWriter.name("i").value(9.0);
+        jsonWriter.endObject();
+        jsonWriter.endObject();
+
+        String expected = "{\n"
+                + "   \"a\": true,\n"
+                + "   \"b\": false,\n"
+                + "   \"c\": 5.0,\n"
+                + "   \"e\": null,\n"
+                + "   \"f\": [\n"
+                + "      6.0,\n"
+                + "      7.0\n"
+                + "   ],\n"
+                + "   \"g\": {\n"
+                + "      \"h\": 8.0,\n"
+                + "      \"i\": 9.0\n"
+                + "   }\n"
+                + "}";
+        assertEquals(expected, stringWriter.toString());
+    }
+
+    public void testPrettyPrintArray() throws IOException {
+        StringWriter stringWriter = new StringWriter();
+        JsonWriter jsonWriter = new JsonWriter(stringWriter);
+        jsonWriter.setIndent("   ");
+
+        jsonWriter.beginArray();
+        jsonWriter.value(true);
+        jsonWriter.value(false);
+        jsonWriter.value(5.0);
+        jsonWriter.nullValue();
+        jsonWriter.beginObject();
+        jsonWriter.name("a").value(6.0);
+        jsonWriter.name("b").value(7.0);
+        jsonWriter.endObject();
+        jsonWriter.beginArray();
+        jsonWriter.value(8.0);
+        jsonWriter.value(9.0);
+        jsonWriter.endArray();
+        jsonWriter.endArray();
+
+        String expected = "[\n"
+                + "   true,\n"
+                + "   false,\n"
+                + "   5.0,\n"
+                + "   null,\n"
+                + "   {\n"
+                + "      \"a\": 6.0,\n"
+                + "      \"b\": 7.0\n"
+                + "   },\n"
+                + "   [\n"
+                + "      8.0,\n"
+                + "      9.0\n"
+                + "   ]\n"
+                + "]";
+        assertEquals(expected, stringWriter.toString());
+    }
+}
diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index b90c97b..aad3fe1 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -68,6 +68,13 @@
         t = Patterns.WEB_URL.matcher("xn--fsqu00a.xn--0zwm56d").matches();
         assertTrue("Valid URL", t);
 
+        // Url for testing top level Arabic country code domain in Punycode:
+        //   http://xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx
+        t = Patterns.WEB_URL.matcher("http://xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx").matches();
+        assertTrue("Valid URL", t);
+        t = Patterns.WEB_URL.matcher("xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c/ar/default.aspx").matches();
+        assertTrue("Valid URL", t);
+
         // Internationalized URL.
         t = Patterns.WEB_URL.matcher("http://\uD604\uAE08\uC601\uC218\uC99D.kr").matches();
         assertTrue("Valid URL", t);
@@ -109,7 +116,7 @@
         t = Patterns.DOMAIN_NAME.matcher("mail.example.com").matches();
         assertTrue("Valid domain", t);
 
-        t = Patterns.WEB_URL.matcher("google.me").matches();
+        t = Patterns.DOMAIN_NAME.matcher("google.me").matches();
         assertTrue("Valid domain", t);
 
         // Internationalized domains.
@@ -118,6 +125,14 @@
 
         t = Patterns.DOMAIN_NAME.matcher("__+&42.xer").matches();
         assertFalse("Invalid domain", t);
+
+        // Obsolete domain .yu
+        t = Patterns.DOMAIN_NAME.matcher("test.yu").matches();
+        assertFalse("Obsolete country code top level domain", t);
+
+        // Testing top level Arabic country code domain in Punycode:
+        t = Patterns.DOMAIN_NAME.matcher("xn--4gbrim.xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c").matches();
+        assertTrue("Valid domain", t);
     }
 
     @SmallTest
diff --git a/core/tests/coretests/src/android/webkit/AccessibilityInjectorTest.java b/core/tests/coretests/src/android/webkit/AccessibilityInjectorTest.java
new file mode 100644
index 0000000..9c9d9fe
--- /dev/null
+++ b/core/tests/coretests/src/android/webkit/AccessibilityInjectorTest.java
@@ -0,0 +1,945 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.KeyEvent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+/**
+ * This is a test for the behavior of the {@link AccessibilityInjector}
+ * which is used by {@link WebView} to provide basic accessibility support
+ * in case JavaScript is disabled.
+ * </p>
+ * Note: This test works against the generated {@link AccessibilityEvent}s
+ *       to so it also checks if the test for announcing navigation axis and
+ *       status messages as appropriate.
+ */
+public class AccessibilityInjectorTest extends AndroidTestCase {
+
+    /** The timeout to wait for the expected selection. */
+    private static final long TIMEOUT_WAIT_FOR_SELECTION_STRING = 1000;
+
+    /** The timeout to wait for accessibility and the mock service to be enabled. */
+    private static final long TIMEOUT_ENABLE_ACCESSIBILITY_AND_MOCK_SERVICE = 500;
+
+    /** The count of tests to detect when to shut down the service. */
+    private static final int TEST_CASE_COUNT = 8;
+
+    /** The meta state for pressed left ALT. */
+    private static final int META_STATE_ALT_LEFT_ON = KeyEvent.META_ALT_ON
+            | KeyEvent.META_ALT_LEFT_ON;
+
+    /** The value for not specified selection string since null is a valid value. */
+    private static final String SELECTION_STRING_UNKNOWN = "Unknown";
+
+    /** Lock for locking the test. */
+    private static final Object sTestLock = new Object();
+
+    /** Handle to the test for use by the mock service. */
+    private static AccessibilityInjectorTest sInstance;
+
+    /** Flag indicating if the accessibility service is ready to receive events. */
+    private static boolean sIsAccessibilityServiceReady;
+
+    /** The count of executed tests to detect when to toggle accessibility and the service. */
+    private static int sExecutedTestCount;
+
+    /** Worker thread with a handler to perform non test thread processing. */
+    private Worker mWorker;
+
+    /** Handle to the {@link WebView} to load data in. */
+    private WebView mWebView;
+
+    /** The received selection string for assertion checking. */
+    private static String sReceivedSelectionString = SELECTION_STRING_UNKNOWN;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mWorker = new Worker();
+        sInstance = this;
+        if (sExecutedTestCount == 0) {
+            // until JUnit4 comes to play with @BeforeTest
+            disableAccessibilityAndMockAccessibilityService();
+            enableAccessibilityAndMockAccessibilityService();
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mWorker != null) {
+            mWorker.stop();
+        }
+        if (sExecutedTestCount == TEST_CASE_COUNT) {
+            // until JUnit4 comes to play with @AfterTest
+            disableAccessibilityAndMockAccessibilityService();
+        }
+        super.tearDown();
+    }
+
+    /**
+     * Tests navigation by character.
+     */
+    @LargeTest
+    public void testNavigationByCharacter() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<html>" +
+               "<head>" +
+               "</head>" +
+               "<body>" +
+                   "<p>" +
+                      "a <b>b</b> c" +
+                   "</p>" +
+                   "<p>" +
+                     "d" +
+                     "<input>e</input>" +
+                   "</p>" +
+               "</body>" +
+             "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // change navigation axis to word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("1"); // expect the word navigation axis
+
+        // change navigation axis to character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("0"); // expect the character navigation axis
+
+        // go to the first character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("a");
+
+        // go to the second character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<b>b</b>");
+
+        // go to the third character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("c");
+
+        // go to the fourth character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("d");
+
+        // go to the fifth character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("e");
+
+        // try to go past the last character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the fourth character (reverse)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("d");
+
+        // go to the third character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("c");
+
+        // go to the second character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<b>b</b>");
+
+        // go to the first character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("a");
+
+        // try to go before the first character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the second character (reverse again)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<b>b</b>");
+    }
+
+    /**
+     * Tests navigation by word.
+     */
+    @LargeTest
+    public void testNavigationByWord() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<html>" +
+               "<head>" +
+               "</head>" +
+               "<body>" +
+                   "<p>" +
+                      "This is <b>a</b> sentence" +
+                   "</p>" +
+                   "<p>" +
+                     " scattered " +
+                     "<input>all</input>" +
+                     " over " +
+                   "</p>" +
+                   "<div>" +
+                     "<button>the place.</button>" +
+                   "</div>" +
+               "</body>" +
+             "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // change navigation axis to word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("1"); // expect the word navigation axis
+
+        // go to the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("This");
+
+        // go to the second word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("is");
+
+        // go to the third word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<b>a</b>");
+
+        // go to the fourth word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("sentence");
+
+        // go to the fifth word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("scattered");
+
+        // go to the sixth word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("all");
+
+        // go to the seventh word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("over");
+
+        // go to the eight word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("the");
+
+        // go to the ninth word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("place");
+
+        // NOTE: WebKit selection returns the dot as a word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(".");
+
+        // try to go past the last word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the last word (reverse)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("place");
+
+        // go to the eight word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("the");
+
+        // go to the seventh word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("over");
+
+        // go to the sixth word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("all");
+
+        // go to the fifth word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("scattered");
+
+        // go to the fourth word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("sentence");
+
+        // go to the third word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<b>a</b>");
+
+        // go to the second word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("is");
+
+        // go to the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("This");
+
+        // try to go before the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the second word (reverse again)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("is");
+    }
+
+    /**
+     * Tests navigation by sentence.
+     */
+    @LargeTest
+    public void testNavigationBySentence() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<div>" +
+                  "<p>" +
+                    "This is the first sentence of the first paragraph and has an <b>inline bold tag</b>." +
+                    "This is the second sentence of the first paragraph." +
+                  "</p>" +
+                  "<h1>This is a heading</h1>" +
+                  "<p>" +
+                    "This is the first sentence of the second paragraph." +
+                    "This is the second sentence of the second paragraph." +
+                  "</p>" +
+                "</div>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // Sentence axis is the default
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("This is the first sentence of the first paragraph and has an "
+                + "<b>inline bold tag</b>.");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("This is the second sentence of the first paragraph.");
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("This is a heading");
+
+        // go to the fourth sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("This is the first sentence of the second paragraph.");
+
+        // go to the fifth sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("This is the second sentence of the second paragraph.");
+
+        // try to go past the last sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the fourth sentence (reverse)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("This is the first sentence of the second paragraph.");
+
+        // go to the third sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("This is a heading");
+
+        // go to the second sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("This is the second sentence of the first paragraph.");
+
+        // go to the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("This is the first sentence of the first paragraph and has an "
+                + "<b>inline bold tag</b>.");
+
+        // try to go before the first sentence
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the second sentence (reverse again)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("This is the second sentence of the first paragraph.");
+    }
+
+    /**
+     * Tests navigation by heading.
+     */
+    @LargeTest
+    public void testNavigationByHeading() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<h1>Heading one</h1>" +
+                "<p>" +
+                  "This is some text" +
+                "</p>" +
+                "<h2>Heading two</h2>" +
+                "<p>" +
+                  "This is some text" +
+                "</p>" +
+                "<h3>Heading three</h3>" +
+                "<p>" +
+                  "This is some text" +
+                "</p>" +
+                "<h4>Heading four</h4>" +
+                "<p>" +
+                  "This is some text" +
+                "</p>" +
+                "<h5>Heading five</h5>" +
+                "<p>" +
+                  "This is some text" +
+                "</p>" +
+                "<h6>Heading six</h6>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // change navigation axis to heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("3"); // expect the heading navigation axis
+
+        // go to the first heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<h1>Heading one</h1>");
+
+        // go to the second heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<h2>Heading two</h2>");
+
+        // go to the third heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<h3>Heading three</h3>");
+
+        // go to the fourth heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<h4>Heading four</h4>");
+
+        // go to the fifth heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<h5>Heading five</h5>");
+
+        // go to the sixth heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<h6>Heading six</h6>");
+
+        // try to go past the last heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the fifth heading (reverse)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<h5>Heading five</h5>");
+
+        // go to the fourth heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<h4>Heading four</h4>");
+
+        // go to the third heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<h3>Heading three</h3>");
+
+        // go to the second heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<h2>Heading two</h2>");
+
+        // go to the first heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<h1>Heading one</h1>");
+
+        // try to go before the first heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the second heading (reverse again)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<h2>Heading two</h2>");
+    }
+
+    /**
+     * Tests navigation by sibling.
+     */
+    @LargeTest
+    public void testNavigationBySibing() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<h1>Heading one</h1>" +
+                "<p>" +
+                  "This is some text" +
+                "</p>" +
+                "<div>" +
+                  "<button>Input</button>" +
+                "</div>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // change navigation axis to heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("3"); // expect the heading navigation axis
+
+        // change navigation axis to sibling
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("4"); // expect the sibling navigation axis
+
+        // change navigation axis to parent/first child
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("5"); // expect the parent/first child navigation axis
+
+        // go to the first child of the body
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<h1>Heading one</h1>");
+
+        // change navigation axis to sibling
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("4"); // expect the sibling navigation axis
+
+        // go to the next sibling
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<p>This is some text</p>");
+
+        // go to the next sibling
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<div><button>Input</button></div>");
+
+        // try to go past the last sibling
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the previous sibling (reverse)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<p>This is some text</p>");
+
+        // go to the previous sibling
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<h1>Heading one</h1>");
+
+        // try to go before the previous sibling
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the next sibling (reverse again)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<p>This is some text</p>");
+    }
+
+    /**
+     * Tests navigation by parent/first child.
+     */
+    @LargeTest
+    public void testNavigationByParentFirstChild() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<div>" +
+                  "<button>Input</button>" +
+                "</div>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // change navigation axis to document
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("6"); // expect the document navigation axis
+
+        // change navigation axis to parent/first child
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("5"); // expect the parent/first child navigation axis
+
+        // go to the first child
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<div><button>Input</button></div>");
+
+        // go to the first child
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<button>Input</button>");
+
+        // try to go to the first child of a leaf element
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString(null);
+
+        // go to the parent (reverse)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<div><button>Input</button></div>");
+
+        // go to the parent
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<body><div><button>Input</button></div></body>");
+
+        // try to go to the body parent
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString(null);
+
+        // go to the first child (reverse again)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<div><button>Input</button></div>");
+    }
+
+    /**
+     * Tests navigation by document.
+     */
+    @LargeTest
+    public void testNavigationByDocument() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<button>Click</button>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // change navigation axis to document
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_LEFT, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("6"); // expect the document navigation axis
+
+        // go to the bottom of the document
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("Click");
+
+        // go to the top of the document (reverse)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, 0);
+        assertSelectionString("<body><button>Click</button></body>");
+
+        // go to the bottom of the document (reverse again)
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("Click");
+    }
+
+    /**
+     * Tests the sync between the text navigation and navigation by DOM elements.
+     */
+    @LargeTest
+    public void testSyncBetweenTextAndDomNodeNavigation() throws Exception {
+        // a bit ugly but helps detect beginning and end of all tests so accessibility
+        // and the mock service are not toggled on every test (expensive)
+        sExecutedTestCount++;
+
+        String html =
+            "<!DOCTYPE html>" +
+            "<html>" +
+              "<head>" +
+              "</head>" +
+              "<body>" +
+                "<p>" +
+                  "First" +
+                "</p>" +
+                "<button>Second</button>" +
+                "<p>" +
+                  "Third" +
+                "</p>" +
+              "</body>" +
+            "</html>";
+
+        WebView webView = createWebVewWithHtml(html);
+
+        // change navigation axis to word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("1"); // expect the word navigation axis
+
+        // go to the first word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("First");
+
+        // change navigation axis to heading
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("3"); // expect the heading navigation axis
+
+        // change navigation axis to sibling
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_RIGHT, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("4"); // expect the sibling navigation axis
+
+        // go to the next sibling
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("<button>Second</button>");
+
+        // change navigation axis to character
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("0"); // expect the character navigation axis
+
+        // change navigation axis to word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_UP, META_STATE_ALT_LEFT_ON);
+        assertSelectionString("1"); // expect the word navigation axis
+
+        // go to the next word
+        sendKeyEvent(webView, KeyEvent.KEYCODE_DPAD_DOWN, 0);
+        assertSelectionString("Third");
+    }
+
+    /**
+     * Enable accessibility and the mock accessibility service.
+     */
+    private void enableAccessibilityAndMockAccessibilityService() {
+        // make sure the manager is instantiated so the system initializes it
+        AccessibilityManager.getInstance(getContext());
+
+        // enable accessibility and the mock accessibility service
+        Settings.Secure.putInt(getContext().getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_ENABLED, 1);
+        String enabledServices = new ComponentName(getContext().getPackageName(),
+                MockAccessibilityService.class.getName()).flattenToShortString();
+        Settings.Secure.putString(getContext().getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, enabledServices);
+
+        // poll within a timeout and let be interrupted in case of success
+        long incrementStep = TIMEOUT_ENABLE_ACCESSIBILITY_AND_MOCK_SERVICE / 5;
+        long start = SystemClock.uptimeMillis();
+        while (SystemClock.uptimeMillis() - start < TIMEOUT_ENABLE_ACCESSIBILITY_AND_MOCK_SERVICE &&
+                !sIsAccessibilityServiceReady) {
+            synchronized (sTestLock) {
+                try {
+                    sTestLock.wait(incrementStep);
+                } catch (InterruptedException ie) {
+                    /* ignore */
+                }
+            }
+        }
+
+        if (!sIsAccessibilityServiceReady) {
+            throw new IllegalStateException("MockAccessibilityService not ready. Did you add " +
+                    "tests and forgot to update AccessibilityInjectorTest#TEST_CASE_COUNT?");
+        }
+    }
+
+    @Override
+    protected void scrubClass(Class<?> testCaseClass) {
+        /* do nothing - avoid superclass behavior */
+    }
+
+    /**
+     * Disables accessibility and the mock accessibility service.
+     */
+    private void disableAccessibilityAndMockAccessibilityService() {
+        // disable accessibility and the mock accessibility service
+        Settings.Secure.putInt(getContext().getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_ENABLED, 0);
+        Settings.Secure.putString(getContext().getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
+    }
+
+    /**
+     * Asserts the next <code>expectedSelectionString</code> to be received.
+     */
+    private void assertSelectionString(String expectedSelectionString) {
+        assertTrue("MockAccessibilityService not ready", sIsAccessibilityServiceReady);
+
+        long incrementStep = TIMEOUT_WAIT_FOR_SELECTION_STRING / 5;
+        long start = SystemClock.uptimeMillis();
+        while (SystemClock.uptimeMillis() - start < TIMEOUT_WAIT_FOR_SELECTION_STRING &&
+                sReceivedSelectionString == SELECTION_STRING_UNKNOWN) {
+            synchronized (sTestLock) {
+                try {
+                    sTestLock.wait(incrementStep);
+                } catch (InterruptedException ie) {
+                    /* ignore */
+                }
+            }
+        }
+        try {
+            if (sReceivedSelectionString == SELECTION_STRING_UNKNOWN) {
+                fail("No selection string received. Expected: " + expectedSelectionString);
+            }
+            assertEquals(expectedSelectionString, sReceivedSelectionString);
+        } finally {
+            sReceivedSelectionString = SELECTION_STRING_UNKNOWN;
+        }
+    }
+
+    /**
+     * Sends a {@link KeyEvent} (up and down) to the {@link WebView}.
+     *
+     * @param keyCode The event key code.
+     */
+    private void sendKeyEvent(WebView webView, int keyCode, int metaState) {
+        webView.onKeyDown(keyCode, new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 1, metaState));
+        webView.onKeyUp(keyCode, new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 1, metaState));
+    }
+
+    /**
+     * Creates a {@link WebView} with with a given HTML content.
+     *
+     * @param html The HTML content;
+     * @return The created view.
+     */
+    private WebView createWebVewWithHtml(final String html) {
+        mWorker.getHandler().post(new Runnable() {
+            public void run() {
+                mWebView = new WebView(getContext());
+                mWebView.loadData(html, "text/html", "utf-8");
+                mWebView.setWebViewClient(new WebViewClient() {
+                    @Override
+                    public void onPageFinished(WebView view, String url) {
+                        mWorker.getHandler().post(new Runnable() {
+                            public void run() {
+                                synchronized (sTestLock) {
+                                    sTestLock.notifyAll();
+                                }
+                            }
+                        });
+                    }
+                });
+            }
+        });
+        synchronized (sTestLock) {
+            try {
+                sTestLock.wait();
+            } catch (InterruptedException ie) {
+                /* ignore */
+            }
+        }
+        return mWebView;
+    }
+
+    /**
+     * This is a worker thread responsible for creating the {@link WebView}.
+     */
+    private class Worker implements Runnable {
+        private final Object mWorkerLock = new Object();
+        private Handler mHandler;
+
+       public Worker() {
+            new Thread(this).start();
+            synchronized (mWorkerLock) {
+                while (mHandler == null) {
+                    try {
+                        mWorkerLock.wait();
+                    } catch (InterruptedException ex) {
+                        /* ignore */
+                    }
+                }
+            }
+        }
+
+        public void run() {
+            synchronized (mWorkerLock) {
+                Looper.prepare();
+                mHandler = new Handler();
+                mWorkerLock.notifyAll();
+            }
+            Looper.loop();
+        }
+
+        public Handler getHandler() {
+            return mHandler;
+        }
+
+        public void stop() {
+            mHandler.getLooper().quit();
+        }
+    }
+
+    /**
+     * Mock accessibility service to receive the accessibility events
+     * with the current {@link WebView} selection.
+     */
+    public static class MockAccessibilityService extends AccessibilityService {
+        private boolean mIsServiceInfoSet;
+
+        @Override
+        protected void onServiceConnected() {
+            if (mIsServiceInfoSet) {
+                return;
+            }
+            AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+            info.eventTypes = AccessibilityEvent.TYPE_VIEW_SELECTED;
+            info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
+            setServiceInfo(info);
+            mIsServiceInfoSet = true;
+
+            sIsAccessibilityServiceReady = true;
+
+            if (sInstance == null) {
+                return;
+            }
+            synchronized (sTestLock) {
+                sTestLock.notifyAll();
+            }
+        }
+
+        @Override
+        public void onAccessibilityEvent(AccessibilityEvent event) {
+            if (sInstance == null) {
+                return;
+            }
+            if (!event.getText().isEmpty()) {
+                CharSequence text = event.getText().get(0);
+                sReceivedSelectionString = (text != null) ? text.toString() : null;
+            }
+            synchronized (sTestLock) {
+                sTestLock.notifyAll();
+            }
+        }
+
+        @Override
+        public void onInterrupt() {
+            /* do nothing */
+        }
+
+        @Override
+        public boolean onUnbind(Intent intent) {
+            sIsAccessibilityServiceReady = false;
+            return false;
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/webkit/ZoomManagerTest.java b/core/tests/coretests/src/android/webkit/ZoomManagerTest.java
new file mode 100644
index 0000000..1c9defe
--- /dev/null
+++ b/core/tests/coretests/src/android/webkit/ZoomManagerTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.webkit;
+
+import android.test.AndroidTestCase;
+
+public class ZoomManagerTest extends AndroidTestCase {
+
+    private ZoomManager zoomManager;
+
+    @Override
+    public void setUp() {
+        WebView webView = new WebView(this.getContext());
+        CallbackProxy callbackProxy = new CallbackProxy(this.getContext(), webView);
+        zoomManager = new ZoomManager(webView, callbackProxy);
+
+        zoomManager.init(1.00f);
+    }
+
+    public void testInit() {
+        testInit(0.01f);
+        testInit(1.00f);
+        testInit(1.25f);
+    }
+
+    private void testInit(float density) {
+        zoomManager.init(density);
+        actualScaleTest(density);
+        defaultScaleTest(density);
+        assertEquals(zoomManager.getDefaultMaxZoomScale(), zoomManager.getMaxZoomScale());
+        assertEquals(zoomManager.getDefaultMinZoomScale(), zoomManager.getMinZoomScale());
+        assertEquals(density, zoomManager.getTextWrapScale());
+    }
+
+    public void testUpdateDefaultZoomDensity() {
+        // test the basic case where the actual values are equal to the defaults
+        testUpdateDefaultZoomDensity(0.01f);
+        testUpdateDefaultZoomDensity(1.00f);
+        testUpdateDefaultZoomDensity(1.25f);
+    }
+
+    private void testUpdateDefaultZoomDensity(float density) {
+        zoomManager.updateDefaultZoomDensity(density);
+        defaultScaleTest(density);
+    }
+
+    public void testUpdateDefaultZoomDensityWithSmallMinZoom() {
+        // test the case where the minZoomScale has changed to be < the default
+        float newDefaultScale = 1.50f;
+        float minZoomScale = ZoomManager.DEFAULT_MIN_ZOOM_SCALE_FACTOR * newDefaultScale;
+        WebViewCore.ViewState minViewState = new WebViewCore.ViewState();
+        minViewState.mMinScale = minZoomScale - 0.1f;
+        zoomManager.updateZoomRange(minViewState, 0, 0);
+        zoomManager.updateDefaultZoomDensity(newDefaultScale);
+        defaultScaleTest(newDefaultScale);
+    }
+
+    public void testUpdateDefaultZoomDensityWithLargeMinZoom() {
+        // test the case where the minZoomScale has changed to be > the default
+        float newDefaultScale = 1.50f;
+        float minZoomScale = ZoomManager.DEFAULT_MIN_ZOOM_SCALE_FACTOR * newDefaultScale;
+        WebViewCore.ViewState minViewState = new WebViewCore.ViewState();
+        minViewState.mMinScale = minZoomScale + 0.1f;
+        zoomManager.updateZoomRange(minViewState, 0, 0);
+        zoomManager.updateDefaultZoomDensity(newDefaultScale);
+        defaultScaleTest(newDefaultScale);
+    }
+
+    public void testUpdateDefaultZoomDensityWithSmallMaxZoom() {
+        // test the case where the maxZoomScale has changed to be < the default
+        float newDefaultScale = 1.50f;
+        float maxZoomScale = ZoomManager.DEFAULT_MAX_ZOOM_SCALE_FACTOR * newDefaultScale;
+        WebViewCore.ViewState maxViewState = new WebViewCore.ViewState();
+        maxViewState.mMaxScale = maxZoomScale - 0.1f;
+        zoomManager.updateZoomRange(maxViewState, 0, 0);
+        zoomManager.updateDefaultZoomDensity(newDefaultScale);
+        defaultScaleTest(newDefaultScale);
+    }
+
+    public void testUpdateDefaultZoomDensityWithLargeMaxZoom() {
+        // test the case where the maxZoomScale has changed to be > the default
+        float newDefaultScale = 1.50f;
+        float maxZoomScale = ZoomManager.DEFAULT_MAX_ZOOM_SCALE_FACTOR * newDefaultScale;
+        WebViewCore.ViewState maxViewState = new WebViewCore.ViewState();
+        maxViewState.mMaxScale = maxZoomScale + 0.1f;
+        zoomManager.updateZoomRange(maxViewState, 0, 0);
+        zoomManager.updateDefaultZoomDensity(newDefaultScale);
+        defaultScaleTest(newDefaultScale);
+    }
+
+    public void testComputeScaleWithLimits() {
+        final float maxScale = zoomManager.getMaxZoomScale();
+        final float minScale = zoomManager.getMinZoomScale();
+        assertTrue(maxScale > minScale);
+        assertEquals(maxScale, zoomManager.computeScaleWithLimits(maxScale));
+        assertEquals(maxScale, zoomManager.computeScaleWithLimits(maxScale + .01f));
+        assertEquals(minScale, zoomManager.computeScaleWithLimits(minScale));
+        assertEquals(minScale, zoomManager.computeScaleWithLimits(minScale - .01f));
+    }
+
+    private void actualScaleTest(float actualScale) {
+        assertEquals(actualScale, zoomManager.getScale());
+        assertEquals(1 / actualScale, zoomManager.getInvScale());
+    }
+
+    private void defaultScaleTest(float defaultScale) {
+        final float maxDefault = ZoomManager.DEFAULT_MAX_ZOOM_SCALE_FACTOR * defaultScale;
+        final float minDefault = ZoomManager.DEFAULT_MIN_ZOOM_SCALE_FACTOR * defaultScale;
+        assertEquals(defaultScale, zoomManager.getDefaultScale());
+        assertEquals(1 / defaultScale, zoomManager.getInvDefaultScale());
+        assertEquals(maxDefault, zoomManager.getDefaultMaxZoomScale());
+        assertEquals(minDefault, zoomManager.getDefaultMinZoomScale());
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListBasicTest.java b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListBasicTest.java
deleted file mode 100644
index e23b516..0000000
--- a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListBasicTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * 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.expandablelistview;
-
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.ExpandableListScenario;
-import android.util.ListUtil;
-import android.util.ExpandableListScenario.MyGroup;
-import android.view.KeyEvent;
-import android.widget.BaseExpandableListAdapter;
-import android.widget.ExpandableListAdapter;
-import android.widget.ExpandableListView;
-
-import java.util.List;
-
-public class ExpandableListBasicTest extends ActivityInstrumentationTestCase2<ExpandableListSimple> {
-    private ExpandableListScenario mActivity;
-    private ExpandableListView mExpandableListView;
-    private ExpandableListAdapter mAdapter;
-    private ListUtil mListUtil;
-    
-    public ExpandableListBasicTest() {
-        super(ExpandableListSimple.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        
-        mActivity = getActivity();
-        mExpandableListView = mActivity.getExpandableListView();
-        mAdapter = mExpandableListView.getExpandableListAdapter();
-        mListUtil = new ListUtil(mExpandableListView, getInstrumentation());
-    }
-    
-    @MediumTest
-    public void testPreconditions() {
-        assertNotNull(mActivity);
-        assertNotNull(mExpandableListView);
-    }
-    
-    private int expandGroup(int numChildren, boolean atLeastOneChild) {
-        final int groupPos = mActivity.findGroupWithNumChildren(numChildren, atLeastOneChild);
-        assertTrue("Could not find group to expand", groupPos >= 0);
-
-        assertFalse("Group is already expanded", mExpandableListView.isGroupExpanded(groupPos));
-        mListUtil.arrowScrollToSelectedPosition(groupPos);
-        getInstrumentation().waitForIdleSync();
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-        getInstrumentation().waitForIdleSync();
-        assertTrue("Group did not expand", mExpandableListView.isGroupExpanded(groupPos));
-
-        return groupPos;
-    }
-
-    @MediumTest
-    public void testExpandGroup() {
-        expandGroup(-1, true);
-    }
-    
-    @MediumTest
-    public void testCollapseGroup() {
-        final int groupPos = expandGroup(-1, true);
-        
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-        getInstrumentation().waitForIdleSync();
-        assertFalse("Group did not collapse", mExpandableListView.isGroupExpanded(groupPos));
-    }
-    
-    @MediumTest
-    public void testExpandedGroupMovement() {
-        // Expand the first group
-        mListUtil.arrowScrollToSelectedPosition(0);
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-        getInstrumentation().waitForIdleSync();
-
-        // Ensure it expanded
-        assertTrue("Group did not expand", mExpandableListView.isGroupExpanded(0));
-        
-        // Wait until that's all good
-        getInstrumentation().waitForIdleSync();
-        
-        // Make sure it expanded
-        assertTrue("Group did not expand", mExpandableListView.isGroupExpanded(0));
-        
-        // Insert a collapsed group in front of the one just expanded
-        List<MyGroup> groups = mActivity.getGroups();
-        MyGroup insertedGroup = new MyGroup(1);
-        groups.add(0, insertedGroup);
-        
-        // Notify data change
-        assertTrue("Adapter is not an instance of the base adapter",
-                mAdapter instanceof BaseExpandableListAdapter);
-        final BaseExpandableListAdapter adapter = (BaseExpandableListAdapter) mAdapter;
-     
-        mActivity.runOnUiThread(new Runnable() {
-            public void run() {
-                adapter.notifyDataSetChanged();
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-        
-        // Make sure the right group is expanded
-        assertTrue("The expanded state didn't stay with the proper group",
-                mExpandableListView.isGroupExpanded(1));
-        assertFalse("The expanded state was given to the inserted group",
-                mExpandableListView.isGroupExpanded(0));
-    }
-
-    @MediumTest
-    public void testContextMenus() {
-        ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this);
-        tester.testContextMenus();
-    }
-
-    @MediumTest
-    public void testConvertionBetweenFlatAndPacked() {
-        ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this);
-        tester.testConvertionBetweenFlatAndPackedOnGroups();
-        tester.testConvertionBetweenFlatAndPackedOnChildren();
-    }
-
-    @MediumTest
-    public void testSelectedPosition() {
-        ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this);
-        tester.testSelectedPositionOnGroups();
-        tester.testSelectedPositionOnChildren();
-    }
-}
diff --git a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListSimple.java b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListSimple.java
deleted file mode 100644
index 78db28c..0000000
--- a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListSimple.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.expandablelistview;
-
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MenuItem.OnMenuItemClickListener;
-import android.widget.BaseExpandableListAdapter;
-
-import android.util.ExpandableListScenario;
-
-public class ExpandableListSimple extends ExpandableListScenario {
-    private static final int[] NUM_CHILDREN = {4, 3, 2, 1, 0};
-
-    @Override
-    protected void init(ExpandableParams params) {
-        params.setNumChildren(NUM_CHILDREN)
-                .setItemScreenSizeFactor(0.14);
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-
-        menu.add("Add item").setOnMenuItemClickListener(new OnMenuItemClickListener() {
-            public boolean onMenuItemClick(MenuItem item) {
-                mGroups.add(0, new MyGroup(2));
-                ((BaseExpandableListAdapter) mAdapter).notifyDataSetChanged();
-                return true;
-            }
-        });
-        
-        return true;
-    }
-    
-}
diff --git a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListTester.java b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListTester.java
deleted file mode 100644
index dfb10fb..0000000
--- a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListTester.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * 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.expandablelistview;
-
-import android.app.Instrumentation;
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.ExpandableListScenario;
-import android.util.ListUtil;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.ExpandableListAdapter;
-import android.widget.ExpandableListView;
-
-import junit.framework.Assert;
-
-public class ExpandableListTester {
-    private final ExpandableListView mExpandableListView;
-    private final ExpandableListAdapter mAdapter;
-    private final ListUtil mListUtil;
-
-    private final ActivityInstrumentationTestCase2<? extends ExpandableListScenario>
-        mActivityInstrumentation;
-
-    Instrumentation mInstrumentation;
-
-    public ExpandableListTester(
-            ExpandableListView expandableListView,
-            ActivityInstrumentationTestCase2<? extends ExpandableListScenario>
-            activityInstrumentation) {
-        mExpandableListView = expandableListView;
-        Instrumentation instrumentation = activityInstrumentation.getInstrumentation();
-        mListUtil = new ListUtil(mExpandableListView, instrumentation);
-        mAdapter = mExpandableListView.getExpandableListAdapter();
-        mActivityInstrumentation = activityInstrumentation;
-        mInstrumentation = mActivityInstrumentation.getInstrumentation();
-    }
-
-    private void expandGroup(final int groupIndex, int flatPosition) {
-        Assert.assertFalse("Group is already expanded", mExpandableListView
-                .isGroupExpanded(groupIndex));
-        mListUtil.arrowScrollToSelectedPosition(flatPosition);
-        mInstrumentation.waitForIdleSync();
-        mActivityInstrumentation.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-        mActivityInstrumentation.getInstrumentation().waitForIdleSync();
-        Assert.assertTrue("Group did not expand " + groupIndex, 
-                mExpandableListView.isGroupExpanded(groupIndex));
-    }
-
-    void testContextMenus() {
-        // Add a position tester ContextMenu listener to the ExpandableListView
-        PositionTesterContextMenuListener menuListener = new PositionTesterContextMenuListener();
-        mExpandableListView.setOnCreateContextMenuListener(menuListener);
-
-        int index = 0;
-
-        // Scrolling on header elements should trigger an AdapterContextMenu
-        for (int i=0; i<mExpandableListView.getHeaderViewsCount(); i++) {
-            // Check group index in context menu
-            menuListener.expectAdapterContextMenu(i);
-            // Make sure the group is visible so that getChild finds it
-            mListUtil.arrowScrollToSelectedPosition(index);
-            View headerChild = mExpandableListView.getChildAt(index
-                    - mExpandableListView.getFirstVisiblePosition());
-            mExpandableListView.showContextMenuForChild(headerChild);
-            mInstrumentation.waitForIdleSync();
-            Assert.assertNull(menuListener.getErrorMessage(), menuListener.getErrorMessage());
-            index++;
-        }
-
-        int groupCount = mAdapter.getGroupCount();
-        for (int groupIndex = 0; groupIndex < groupCount; groupIndex++) {
-
-            // Expand group
-            expandGroup(groupIndex, index);
-
-            // Check group index in context menu
-            menuListener.expectGroupContextMenu(groupIndex);
-            // Make sure the group is visible so that getChild finds it
-            mListUtil.arrowScrollToSelectedPosition(index);
-            View groupChild = mExpandableListView.getChildAt(index
-                    - mExpandableListView.getFirstVisiblePosition());
-            mExpandableListView.showContextMenuForChild(groupChild);
-            mInstrumentation.waitForIdleSync();
-            Assert.assertNull(menuListener.getErrorMessage(), menuListener.getErrorMessage());
-            index++;
-
-            final int childrenCount = mAdapter.getChildrenCount(groupIndex);
-            for (int childIndex = 0; childIndex < childrenCount; childIndex++) {
-                // Check child index in context menu
-                mListUtil.arrowScrollToSelectedPosition(index);
-                menuListener.expectChildContextMenu(groupIndex, childIndex);
-                View child = mExpandableListView.getChildAt(index
-                        - mExpandableListView.getFirstVisiblePosition());
-                mExpandableListView.showContextMenuForChild(child);
-                mInstrumentation.waitForIdleSync();
-                Assert.assertNull(menuListener.getErrorMessage(), menuListener.getErrorMessage());
-                index++;
-            }
-        }
-
-        // Scrolling on footer elements should trigger an AdapterContextMenu
-        for (int i=0; i<mExpandableListView.getFooterViewsCount(); i++) {
-            // Check group index in context menu
-            menuListener.expectAdapterContextMenu(index);
-            // Make sure the group is visible so that getChild finds it
-            mListUtil.arrowScrollToSelectedPosition(index);
-            View footerChild = mExpandableListView.getChildAt(index
-                    - mExpandableListView.getFirstVisiblePosition());
-            mExpandableListView.showContextMenuForChild(footerChild);
-            mInstrumentation.waitForIdleSync();
-            Assert.assertNull(menuListener.getErrorMessage(), menuListener.getErrorMessage());
-            index++;
-        }
-
-        // Cleanup: remove the listener we added.
-        mExpandableListView.setOnCreateContextMenuListener(null);
-    }
-
-    private int expandAGroup() {
-        final int groupIndex = 2;
-        final int headerCount = mExpandableListView.getHeaderViewsCount();
-        Assert.assertTrue("Not enough groups", groupIndex < mAdapter.getGroupCount());
-        expandGroup(groupIndex, groupIndex + headerCount);
-        return groupIndex;
-    }
-
-    // This method assumes that NO group is expanded when called
-    void testConvertionBetweenFlatAndPackedOnGroups() {
-        final int headerCount = mExpandableListView.getHeaderViewsCount();
-
-        for (int i=0; i<headerCount; i++) {
-            Assert.assertEquals("Non NULL position for header item",
-                    ExpandableListView.PACKED_POSITION_VALUE_NULL,
-                    mExpandableListView.getExpandableListPosition(i));
-        }
-
-        // Test all (non expanded) groups
-        final int groupCount = mAdapter.getGroupCount();
-        for (int groupIndex = 0; groupIndex < groupCount; groupIndex++) {
-            int expectedFlatPosition = headerCount + groupIndex;
-            long packedPositionForGroup = ExpandableListView.getPackedPositionForGroup(groupIndex);
-            Assert.assertEquals("Group not found at flat position " + expectedFlatPosition,
-                    packedPositionForGroup,
-                    mExpandableListView.getExpandableListPosition(expectedFlatPosition));
-
-            Assert.assertEquals("Wrong flat position for group " + groupIndex,
-                    expectedFlatPosition,
-                    mExpandableListView.getFlatListPosition(packedPositionForGroup));
-        }
-
-        for (int i=0; i<mExpandableListView.getFooterViewsCount(); i++) {
-            Assert.assertEquals("Non NULL position for header item",
-                    ExpandableListView.PACKED_POSITION_VALUE_NULL,
-                    mExpandableListView.getExpandableListPosition(headerCount + groupCount + i));
-        }
-    }
-
-    // This method assumes that NO group is expanded when called
-    void testConvertionBetweenFlatAndPackedOnChildren() {
-        // Test with an expanded group
-        final int headerCount = mExpandableListView.getHeaderViewsCount();
-        final int groupIndex = expandAGroup();
-
-        final int childrenCount = mAdapter.getChildrenCount(groupIndex);
-        for (int childIndex = 0; childIndex < childrenCount; childIndex++) {
-            int expectedFlatPosition = headerCount + groupIndex + 1 + childIndex;
-            long childPos = ExpandableListView.getPackedPositionForChild(groupIndex, childIndex);
-
-            Assert.assertEquals("Wrong flat position for child ",
-                    childPos,
-                    mExpandableListView.getExpandableListPosition(expectedFlatPosition));
-
-            Assert.assertEquals("Wrong flat position for child ",
-                    expectedFlatPosition,
-                    mExpandableListView.getFlatListPosition(childPos));
-        }
-    }
-
-    // This method assumes that NO group is expanded when called
-    void testSelectedPositionOnGroups() {
-        int index = 0;
-
-        // Scrolling on header elements should not give a valid selected position.
-        for (int i=0; i<mExpandableListView.getHeaderViewsCount(); i++) {
-            mListUtil.arrowScrollToSelectedPosition(index);
-            Assert.assertEquals("Header item is selected",
-                    ExpandableListView.PACKED_POSITION_VALUE_NULL,
-                    mExpandableListView.getSelectedPosition());
-            index++;
-        }
-
-        // Check selection on group items
-        final int groupCount = mAdapter.getGroupCount();
-        for (int groupIndex = 0; groupIndex < groupCount; groupIndex++) {
-            mListUtil.arrowScrollToSelectedPosition(index);
-            Assert.assertEquals("Group item is not selected",
-                    ExpandableListView.getPackedPositionForGroup(groupIndex),
-                    mExpandableListView.getSelectedPosition());
-            index++;
-        }
-
-        // Scrolling on footer elements should not give a valid selected position.
-        for (int i=0; i<mExpandableListView.getFooterViewsCount(); i++) {
-            mListUtil.arrowScrollToSelectedPosition(index);
-            Assert.assertEquals("Footer item is selected",
-                    ExpandableListView.PACKED_POSITION_VALUE_NULL,
-                    mExpandableListView.getSelectedPosition());
-            index++;
-        }
-    }
-
-    // This method assumes that NO group is expanded when called
-    void testSelectedPositionOnChildren() {
-        // Test with an expanded group
-        final int headerCount = mExpandableListView.getHeaderViewsCount();
-        final int groupIndex = expandAGroup();
-
-        final int childrenCount = mAdapter.getChildrenCount(groupIndex);
-        for (int childIndex = 0; childIndex < childrenCount; childIndex++) {
-            int childFlatPosition = headerCount + groupIndex + 1 + childIndex;
-            mListUtil.arrowScrollToSelectedPosition(childFlatPosition);
-            Assert.assertEquals("Group item is not selected",
-                    ExpandableListView.getPackedPositionForChild(groupIndex, childIndex),
-                    mExpandableListView.getSelectedPosition());
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeaders.java b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeaders.java
deleted file mode 100644
index 2251c1d..0000000
--- a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeaders.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.expandablelistview;
-
-import android.os.Bundle;
-import android.util.ExpandableListScenario;
-import android.widget.Button;
-import android.widget.ExpandableListView;
-
-public class ExpandableListWithHeaders extends ExpandableListScenario {
-    private static final int[] sNumChildren = {1, 4, 3, 2, 6};
-    private static final int sNumOfHeadersAndFooters = 12;
-    
-    @Override
-    protected void init(ExpandableParams params) {
-        params.setStackFromBottom(false)
-                .setStartingSelectionPosition(-1)
-                .setNumChildren(sNumChildren)
-                .setItemScreenSizeFactor(0.14)
-                .setConnectAdapter(false);
-    }
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        final ExpandableListView expandableListView = getExpandableListView();
-        expandableListView.setItemsCanFocus(true);
-
-        for (int i = 0; i < sNumOfHeadersAndFooters; i++) {
-            Button header = new Button(this);
-            header.setText("Header View " + i);
-            expandableListView.addHeaderView(header);
-        }
-
-        for (int i = 0; i < sNumOfHeadersAndFooters; i++) {
-            Button footer = new Button(this);
-            footer.setText("Footer View " + i);
-            expandableListView.addFooterView(footer);
-        }
-        
-        // Set adapter here AFTER we set header and footer views
-        setAdapter(expandableListView);
-    }
-    
-    /**
-     * @return The number of headers (and the same number of footers)
-     */
-    public int getNumOfHeadersAndFooters() {
-        return sNumOfHeadersAndFooters;
-    }
-
-}
diff --git a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeadersTest.java b/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeadersTest.java
deleted file mode 100644
index c74c853..0000000
--- a/core/tests/coretests/src/android/widget/expandablelistview/ExpandableListWithHeadersTest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.expandablelistview;
-
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.ListUtil;
-import android.view.KeyEvent;
-import android.widget.ExpandableListView;
-
-public class ExpandableListWithHeadersTest extends
-        ActivityInstrumentationTestCase2<ExpandableListWithHeaders> {
-    private ExpandableListView mExpandableListView;
-    private ListUtil mListUtil;
-    
-    public ExpandableListWithHeadersTest() {
-        super(ExpandableListWithHeaders.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        
-        mExpandableListView = getActivity().getExpandableListView();
-        mListUtil = new ListUtil(mExpandableListView, getInstrumentation());
-    }
-    
-    @MediumTest
-    public void testPreconditions() {
-        assertNotNull(mExpandableListView);
-    }
-    
-    @MediumTest
-    public void testExpandOnFirstPosition() {
-        // Should be a header, and hence the first group should NOT have expanded
-        mListUtil.arrowScrollToSelectedPosition(0);
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-        getInstrumentation().waitForIdleSync();
-        assertFalse(mExpandableListView.isGroupExpanded(0));
-    }
-
-    @LargeTest
-    public void testExpandOnFirstGroup() {
-        mListUtil.arrowScrollToSelectedPosition(getActivity().getNumOfHeadersAndFooters());
-        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
-        getInstrumentation().waitForIdleSync();
-        assertTrue(mExpandableListView.isGroupExpanded(0));
-    }
-
-    @LargeTest
-    public void testContextMenus() {
-        ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this);
-        tester.testContextMenus();
-    }
-
-    @LargeTest
-    public void testConvertionBetweenFlatAndPacked() {
-        ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this);
-        tester.testConvertionBetweenFlatAndPackedOnGroups();
-        tester.testConvertionBetweenFlatAndPackedOnChildren();
-    }
-
-    @LargeTest
-    public void testSelectedPosition() {
-        ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this);
-        tester.testSelectedPositionOnGroups();
-        tester.testSelectedPositionOnChildren();
-    }
-}
diff --git a/core/tests/coretests/src/android/widget/expandablelistview/InflatedExpandableListView.java b/core/tests/coretests/src/android/widget/expandablelistview/InflatedExpandableListView.java
deleted file mode 100644
index f4c9d56..0000000
--- a/core/tests/coretests/src/android/widget/expandablelistview/InflatedExpandableListView.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.expandablelistview;
-
-import com.android.frameworks.coretests.R;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.BaseExpandableListAdapter;
-import android.widget.ExpandableListView;
-import android.widget.TextView;
-
-public class InflatedExpandableListView extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        
-        setContentView(R.layout.inflated_expandablelistview);
-        
-        ExpandableListView elv = (ExpandableListView) findViewById(R.id.elv);
-        elv.setAdapter(new MyExpandableListAdapter());
-    }
-
-    public class MyExpandableListAdapter extends BaseExpandableListAdapter {
-        // Sample data set.  children[i] contains the children (String[]) for groups[i].
-        private String[] groups = { "People Names", "Dog Names", "Cat Names", "Fish Names" };
-        private String[][] children = {
-                { "Arnold", "Barry", "Chuck", "David" },
-                { "Ace", "Bandit", "Cha-Cha", "Deuce" },
-                { "Fluffy", "Snuggles" },
-                { "Goldy", "Bubbles" }
-        };
-        
-        public Object getChild(int groupPosition, int childPosition) {
-            return children[groupPosition][childPosition];
-        }
-
-        public long getChildId(int groupPosition, int childPosition) {
-            return childPosition;
-        }
-
-        public int getChildrenCount(int groupPosition) {
-            return children[groupPosition].length;
-        }
-
-        public TextView getGenericView() {
-            // Layout parameters for the ExpandableListView
-            AbsListView.LayoutParams lp = new AbsListView.LayoutParams(
-                    ViewGroup.LayoutParams.MATCH_PARENT, 64);
-
-            TextView textView = new TextView(InflatedExpandableListView.this);
-            textView.setLayoutParams(lp);
-            // Center the text vertically
-            textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
-            // Set the text starting position
-            textView.setPadding(36, 0, 0, 0);
-            return textView;
-        }
-        
-        public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
-                View convertView, ViewGroup parent) {
-            TextView textView = getGenericView();
-            textView.setText(getChild(groupPosition, childPosition).toString());
-            return textView;
-        }
-
-        public Object getGroup(int groupPosition) {
-            return groups[groupPosition];
-        }
-
-        public int getGroupCount() {
-            return groups.length;
-        }
-
-        public long getGroupId(int groupPosition) {
-            return groupPosition;
-        }
-
-        public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
-                ViewGroup parent) {
-            TextView textView = getGenericView();
-            textView.setText(getGroup(groupPosition).toString());
-            return textView;
-        }
-
-        public boolean isChildSelectable(int groupPosition, int childPosition) {
-            return true;
-        }
-
-        public boolean hasStableIds() {
-            return true;
-        }
-
-    }
-    
-}
diff --git a/core/tests/coretests/src/android/widget/expandablelistview/PositionTesterContextMenuListener.java b/core/tests/coretests/src/android/widget/expandablelistview/PositionTesterContextMenuListener.java
deleted file mode 100644
index 2dbdff8..0000000
--- a/core/tests/coretests/src/android/widget/expandablelistview/PositionTesterContextMenuListener.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.widget.expandablelistview;
-
-import android.view.ContextMenu;
-import android.view.View;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.OnCreateContextMenuListener;
-import android.widget.ExpandableListView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-
-public class PositionTesterContextMenuListener implements OnCreateContextMenuListener {
-
-    private int groupPosition, childPosition;
-
-    // Fake constant to store in testType a test type specific to headers and footers
-    private static final int ADAPTER_TYPE = -1;
-    private int testType; // as returned by getPackedPositionType
-
-    // Will be set to null by each call to onCreateContextMenu, unless an error occurred. 
-    private String errorMessage;
-
-    public void expectGroupContextMenu(int groupPosition) {
-        this.groupPosition = groupPosition;
-        testType = ExpandableListView.PACKED_POSITION_TYPE_GROUP;
-    }
-
-    public void expectChildContextMenu(int groupPosition, int childPosition) {
-        this.groupPosition = groupPosition;
-        this.childPosition = childPosition;
-        testType = ExpandableListView.PACKED_POSITION_TYPE_CHILD;
-    }
-
-    public void expectAdapterContextMenu(int flatPosition) {
-        this.groupPosition = flatPosition;
-        testType = ADAPTER_TYPE;
-    }
-
-    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
-        errorMessage = null;
-        if (testType == ADAPTER_TYPE) {
-            if (!isTrue("MenuInfo is not an AdapterContextMenuInfo",
-                    menuInfo instanceof AdapterContextMenuInfo)) {
-                return;
-            }
-            AdapterContextMenuInfo adapterContextMenuInfo = (AdapterContextMenuInfo) menuInfo;
-            if (!areEqual("Wrong flat position", groupPosition, adapterContextMenuInfo.position)) {
-                return;
-            }
-        } else {
-            if (!isTrue("MenuInfo is not an ExpandableListContextMenuInfo",
-                    menuInfo instanceof ExpandableListView.ExpandableListContextMenuInfo)) {
-                return;
-            }
-            ExpandableListView.ExpandableListContextMenuInfo elvMenuInfo =
-                (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
-            long packedPosition = elvMenuInfo.packedPosition;
-
-            int packedPositionType = ExpandableListView.getPackedPositionType(packedPosition);
-            if (!areEqual("Wrong packed position type", testType, packedPositionType)) {
-                return;
-            }
-
-            int packedPositionGroup = ExpandableListView.getPackedPositionGroup(packedPosition);
-            if (!areEqual("Wrong group position", groupPosition, packedPositionGroup)) {
-                return;
-            }
-
-            if (testType == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
-                int packedPositionChild = ExpandableListView.getPackedPositionChild(packedPosition);
-                if (!areEqual("Wrong child position", childPosition, packedPositionChild)) {
-                    return;
-                }
-            }
-        }
-    }
-
-    private boolean areEqual(String message, int expected, int actual) {
-        if (expected != actual) {
-            errorMessage = String.format(message + " (%d vs %d", expected, actual);
-            return false;
-        }
-        return true;
-    }
-
-    private boolean isTrue(String message, boolean value) {
-        if (!value) {
-            errorMessage = message;
-            return false;
-        }
-        return true;
-    }
-
-    public String getErrorMessage() {
-        return errorMessage;
-    }
-}
diff --git a/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java
index ebe13e1..c698e99 100644
--- a/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java
+++ b/core/tests/hosttests/src/android/content/pm/PackageManagerHostTestUtils.java
@@ -152,7 +152,11 @@
         }
 
         CollectingTestRunListener listener = new CollectingTestRunListener();
-        testRunner.run(listener);
+        try {
+            testRunner.run(listener);
+        } catch (IOException ioe) {
+            Log.w(LOG_TAG, "encountered IOException " + ioe);
+        }
         return listener;
     }
 
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 6f8afff..f458576 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -58,6 +58,14 @@
         <group gid="sdcard_rw" />
     </permission>
 
+    <permission name="android.permission.WRITE_MEDIA_STORAGE" >
+        <group gid="media_rw" />
+    </permission>
+
+    <permission name="android.permission.ACCESS_USB" >
+        <group gid="usb" />
+    </permission>
+
     <!-- The group that /cache belongs to, linked to the permission
          set on the applications that can access /cache -->
     <permission name="android.permission.ACCESS_CACHE_FILESYSTEM" >
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
new file mode 100644
index 0000000..952d0782
--- /dev/null
+++ b/data/etc/tablet_core_hardware.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- These are the hardware components that all handheld devices
+     must include. Devices with optional hardware must also include extra
+     hardware files, per the comments below.
+
+     Handheld devices include phones, mobile Internet devices (MIDs),
+     Personal Media Players (PMPs), small tablets (7" or less), and similar
+     devices.
+-->
+<permissions>
+    <feature name="android.hardware.location" />
+    <feature name="android.hardware.location.network" />
+    <feature name="android.hardware.sensor.compass" />
+    <feature name="android.hardware.sensor.accelerometer" />
+    <feature name="android.hardware.bluetooth" />
+    <feature name="android.hardware.touchscreen" />
+    <feature name="android.hardware.touchscreen.multitouch" />
+    <feature name="android.hardware.touchscreen.multitouch.distinct" />
+    <feature name="android.hardware.microphone" />
+    <!-- devices with GPS must include android.hardware.location.gps.xml -->
+    <!-- devices with a rear-facing camera must include one of these as appropriate:
+         android.hardware.camera.xml or 
+         android.hardware.camera.autofocus.xml or 
+         android.hardware.camera.autofocus-flash.xml -->
+    <!-- devices with a front facing camera must include
+         android.hardware.camera.front.xml -->
+    <!-- devices with WiFi must also include android.hardware.wifi.xml -->
+    <!-- devices with an ambient light sensor must also include
+         android.hardware.sensor.light.xml -->
+    <!-- devices with a proximity sensor must also include
+         android.hardware.sensor.proximity.xml -->
+    <!-- devices with a barometer must also include
+         android.hardware.sensor.barometer.xml -->
+    <!-- devices with a gyroscope must also include
+         android.hardware.sensor.gyroscope.xml -->
+    <!-- GSM phones must also include android.hardware.telephony.gsm.xml -->
+    <!-- CDMA phones must also include android.hardware.telephony.cdma.xml -->
+</permissions>
diff --git a/data/fonts/Ahem.ttf b/data/fonts/Ahem.ttf
deleted file mode 100644
index 17e6c60..0000000
--- a/data/fonts/Ahem.ttf
+++ /dev/null
Binary files differ
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
deleted file mode 100644
index 4c40646..0000000
--- a/data/fonts/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-copy_from :=                \
-    DroidSans.ttf           \
-    DroidSans-Bold.ttf      \
-    DroidSansArabic.ttf     \
-    DroidSansHebrew.ttf     \
-    DroidSansThai.ttf       \
-    DroidSerif-Regular.ttf  \
-    DroidSerif-Bold.ttf     \
-    DroidSerif-Italic.ttf   \
-    DroidSerif-BoldItalic.ttf   \
-    DroidSansMono.ttf        \
-    Clockopia.ttf
-
-ifneq ($(NO_FALLBACK_FONT),true)
-ifeq ($(filter %system/fonts/DroidSansFallback.ttf,$(PRODUCT_COPY_FILES)),)
-    # if the product makefile has set the the fallback font, don't override it.
-    copy_from += DroidSansFallback.ttf
-endif
-endif
-
-copy_file_pairs := $(foreach cf,$(copy_from),$(LOCAL_PATH)/$(cf):system/fonts/$(cf))
-PRODUCT_COPY_FILES += $(copy_file_pairs)
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
new file mode 100644
index 0000000..34d0331
--- /dev/null
+++ b/data/fonts/fonts.mk
@@ -0,0 +1,29 @@
+# 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.
+
+# Warning: this is actually a product definition, to be inherited from
+
+PRODUCT_COPY_FILES := \
+    frameworks/base/data/fonts/DroidSans.ttf:system/fonts/DroidSans.ttf \
+    frameworks/base/data/fonts/DroidSans-Bold.ttf:system/fonts/DroidSans-Bold.ttf \
+    frameworks/base/data/fonts/DroidSansArabic.ttf:system/fonts/DroidSansArabic.ttf \
+    frameworks/base/data/fonts/DroidSansHebrew.ttf:system/fonts/DroidSansHebrew.ttf \
+    frameworks/base/data/fonts/DroidSansThai.ttf:system/fonts/DroidSansThai.ttf \
+    frameworks/base/data/fonts/DroidSerif-Regular.ttf:system/fonts/DroidSerif-Regular.ttf \
+    frameworks/base/data/fonts/DroidSerif-Bold.ttf:system/fonts/DroidSerif-Bold.ttf \
+    frameworks/base/data/fonts/DroidSerif-Italic.ttf:system/fonts/DroidSerif-Italic.ttf \
+    frameworks/base/data/fonts/DroidSerif-BoldItalic.ttf:system/fonts/DroidSerif-BoldItalic.ttf \
+    frameworks/base/data/fonts/DroidSansMono.ttf:system/fonts/DroidSansMono.ttf \
+    frameworks/base/data/fonts/Clockopia.ttf:system/fonts/Clockopia.ttf \
+    frameworks/base/data/fonts/DroidSansFallback.ttf:system/fonts/DroidSansFallback.ttf
diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk
new file mode 100644
index 0000000..7a0eae0
--- /dev/null
+++ b/data/keyboards/Android.mk
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This makefile builds the key character map binary files.  (*.kcm.bin)
+
+LOCAL_PATH := $(call my-dir)
+include $(LOCAL_PATH)/common.mk
+
+$(foreach file,$(keycharmaps), \
+    $(eval include $(CLEAR_VARS)) \
+    $(eval LOCAL_SRC_FILES := $(file)) \
+    $(eval LOCAL_MODULE_TAGS := optional) \
+    $(eval include $(BUILD_KEY_CHAR_MAP)) \
+)
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
new file mode 100644
index 0000000..f3c52a7
--- /dev/null
+++ b/data/keyboards/Generic.kcm
@@ -0,0 +1,100 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Generic key character map for full alphabetic US English PC style external keyboards.
+#
+# This file is intentionally very generic and is intended to support a broad rang of keyboards.
+# Do not edit the generic key character map to support a specific keyboard; instead, create
+# a new key character map file with the required keyboard configuration.
+#
+
+[type=QWERTY]
+
+# keycode               display number  base    caps    fn      caps_fn
+
+A                       'A'     '2'     'a'     'A'     'a'     'A'
+B                       'B'     '2'     'b'     'B'     'b'     'B'
+C                       'C'     '2'     'c'     'C'     'c'     'C'
+D                       'D'     '3'     'd'     'D'     'd'     'D'
+E                       'E'     '3'     'e'     'E'     'e'     'E'
+F                       'F'     '3'     'f'     'F'     'f'     'F'
+G                       'G'     '4'     'g'     'G'     'g'     'G'
+H                       'H'     '4'     'h'     'H'     'h'     'H'
+I                       'I'     '4'     'i'     'I'     'i'     'I'
+J                       'J'     '5'     'j'     'J'     'j'     'J'
+K                       'K'     '5'     'k'     'K'     'k'     'K'
+L                       'L'     '5'     'l'     'L'     'l'     'L'
+M                       'M'     '6'     'm'     'M'     'm'     'M'
+N                       'N'     '6'     'n'     'N'     'n'     'N'
+O                       'O'     '6'     'o'     'O'     'o'     'O'
+P                       'P'     '7'     'p'     'P'     'p'     'P'
+Q                       'Q'     '7'     'q'     'Q'     'q'     'Q'
+R                       'R'     '7'     'r'     'R'     'r'     'R'
+S                       'S'     '7'     's'     'S'     's'     'S'
+T                       'T'     '8'     't'     'T'     't'     'T'
+U                       'U'     '8'     'u'     'U'     'u'     'U'
+V                       'V'     '8'     'v'     'V'     'v'     'V'
+W                       'W'     '9'     'w'     'W'     'w'     'W'
+X                       'X'     '9'     'x'     'X'     'x'     'X'
+Y                       'Y'     '9'     'y'     'Y'     'y'     'Y'
+Z                       'Z'     '9'     'z'     'Z'     'z'     'Z'
+
+0                       '0'     '0'     '0'     ')'     '0'     ')'
+1                       '1'     '1'     '1'     '!'     '1'     '!'
+2                       '2'     '2'     '2'     '@'     '2'     '@'
+3                       '3'     '3'     '3'     '#'     '3'     '#'
+4                       '4'     '4'     '4'     '$'     '4'     '$'
+5                       '5'     '5'     '5'     '%'     '5'     '%'
+6                       '6'     '6'     '6'     '^'     '6'     '^'
+7                       '7'     '7'     '7'     '&'     '7'     '&'
+8                       '8'     '8'     '8'     '*'     '8'     '*'
+9                       '9'     '9'     '9'     '('     '9'     '('
+
+SPACE                   0x20    0x20    0x20    0x20    0x20    0x20
+ENTER                   0xa     0xa     0xa     0xa     0xa     0xa
+TAB                     0x9     0x9     0x9     0x9     0x9     0x9
+
+COMMA                   ','     ','     ','     '<'     ','     '<'
+PERIOD                  '.'     '.'     '.'     '>'     '.'     '>'
+SLASH                   '/'     '/'     '/'     '?'     '/'     '?'
+GRAVE                   '`'     '`'     '`'     '~'     '`'     '~'
+MINUS                   '-'     '-'     '-'     '_'     '-'     '_'
+EQUALS                  '='     '='     '='     '+'     '='     '+'
+LEFT_BRACKET            '['     '['     '['     '{'     '['     '{'
+RIGHT_BRACKET           ']'     ']'     ']'     '}'     ']'     '}'
+BACKSLASH               '\'     '\'     '\'     '|'     '\'     '|'
+SEMICOLON               ';'     ';'     ';'     ':'     ';'     ':'
+APOSTROPHE              '''     '''     '''     '"'     '''     '"'
+
+NUMPAD_0                '0'     '0'     '0'     '0'     '0'     '0'
+NUMPAD_1                '1'     '1'     '1'     '1'     '1'     '1'
+NUMPAD_2                '2'     '2'     '2'     '2'     '2'     '2'
+NUMPAD_3                '3'     '3'     '3'     '3'     '3'     '3'
+NUMPAD_4                '4'     '4'     '4'     '4'     '4'     '4'
+NUMPAD_5                '5'     '5'     '5'     '5'     '5'     '5'
+NUMPAD_6                '6'     '6'     '6'     '6'     '6'     '6'
+NUMPAD_7                '7'     '7'     '7'     '7'     '7'     '7'
+NUMPAD_8                '8'     '8'     '8'     '8'     '8'     '8'
+NUMPAD_9                '9'     '9'     '9'     '9'     '9'     '9'
+NUMPAD_LEFT_PAREN       '('     '('     '('     '('     '('     '('
+NUMPAD_RIGHT_PAREN      ')'     ')'     ')'     ')'     ')'     ')'
+NUMPAD_DIVIDE           '/'     '/'     '/'     '/'     '/'     '/'
+NUMPAD_MULTIPLY         '*'     '*'     '*'     '*'     '*'     '*'
+NUMPAD_SUBTRACT         '-'     '-'     '-'     '-'     '-'     '-'
+NUMPAD_ADD              '+'     '+'     '+'     '+'     '+'     '+'
+NUMPAD_DOT              '.'     '.'     '.'     '.'     '.'     '.'
+NUMPAD_COMMA            ','     ','     ','     ','     ','     ','
+NUMPAD_EQUALS           '='     '='     '='     '='     '='     '='
+NUMPAD_ENTER            0xa     0xa     0xa     0xa     0xa     0xa
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
new file mode 100644
index 0000000..e98000d
--- /dev/null
+++ b/data/keyboards/Generic.kl
@@ -0,0 +1,384 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Generic key layout file for full alphabetic US English PC style external keyboards.
+#
+# This file is intentionally very generic and is intended to support a broad rang of keyboards.
+# Do not edit the generic key layout to support a specific keyboard; instead, create
+# a new key layout file with the required keyboard configuration.
+#
+
+key 1     ESCAPE
+key 2     1
+key 3     2
+key 4     3
+key 5     4
+key 6     5
+key 7     6
+key 8     7
+key 9     8
+key 10    9
+key 11    0
+key 12    MINUS
+key 13    EQUALS
+key 14    DEL
+key 15    TAB
+key 16    Q
+key 17    W
+key 18    E
+key 19    R
+key 20    T
+key 21    Y
+key 22    U
+key 23    I
+key 24    O
+key 25    P
+key 26    LEFT_BRACKET
+key 27    RIGHT_BRACKET
+key 28    ENTER
+key 29    CTRL_LEFT
+key 30    A
+key 31    S
+key 32    D
+key 33    F
+key 34    G
+key 35    H
+key 36    J
+key 37    K
+key 38    L
+key 39    SEMICOLON
+key 40    APOSTROPHE
+key 41    GRAVE
+key 42    SHIFT_LEFT
+key 43    BACKSLASH
+key 44    Z
+key 45    X
+key 46    C
+key 47    V
+key 48    B
+key 49    N
+key 50    M
+key 51    COMMA
+key 52    PERIOD
+key 53    SLASH
+key 54    SHIFT_RIGHT
+key 55    NUMPAD_MULTIPLY
+key 56    ALT_LEFT
+key 57    SPACE
+key 58    CAPS_LOCK
+key 59    F1
+key 60    F2
+key 61    F3
+key 62    F4
+key 63    F5
+key 64    F6
+key 65    F7
+key 66    F8
+key 67    F9
+key 68    F10
+key 69    NUM_LOCK
+key 70    SCROLL_LOCK
+key 71    NUMPAD_7
+key 72    NUMPAD_8
+key 73    NUMPAD_9
+key 74    NUMPAD_SUBTRACT
+key 75    NUMPAD_4
+key 76    NUMPAD_5
+key 77    NUMPAD_6
+key 78    NUMPAD_ADD
+key 79    NUMPAD_1
+key 80    NUMPAD_2
+key 81    NUMPAD_3
+key 82    NUMPAD_0
+key 83    NUMPAD_DOT
+# key 84 (undefined)
+# key 85 "KEY_ZENKAKUHANKAKU"
+# key 86 "KEY_102ND"
+key 87    F11
+key 88    F12
+# key 89 "KEY_RO"
+# key 90 "KEY_KATAKANA"
+# key 91 "KEY_HIRAGANA"
+# key 92 "KEY_HENKAN"
+# key 93 "KEY_KATAKANAHIRAGANA"
+# key 94 "KEY_MUHENKAN"
+key 95    NUMPAD_COMMA
+key 96    NUMPAD_ENTER
+key 97    CTRL_RIGHT
+key 98    NUMPAD_DIVIDE
+key 99    SYSRQ
+key 100   ALT_RIGHT
+# key 101 "KEY_LINEFEED"
+key 102   MOVE_HOME
+key 103   DPAD_UP
+key 104   PAGE_UP
+key 105   DPAD_LEFT
+key 106   DPAD_RIGHT
+key 107   MOVE_END
+key 108   DPAD_DOWN
+key 109   PAGE_DOWN
+key 110   INSERT
+key 111   FORWARD_DEL
+# key 112 "KEY_MACRO"
+key 113   VOLUME_MUTE
+key 114   VOLUME_DOWN
+key 115   VOLUME_UP
+key 116   POWER             WAKE
+key 117   NUMPAD_EQUALS
+# key 118 "KEY_KPPLUSMINUS"
+key 119   BREAK
+# key 120 (undefined)
+key 121   NUMPAD_COMMA
+# key 122 "KEY_HANGEUL"
+# key 123 "KEY_HANJA"
+# key 124 "KEY_YEN"
+key 125   META_LEFT
+key 126   META_RIGHT
+key 127   MENU              WAKE_DROPPED
+key 128   MEDIA_STOP
+# key 129 "KEY_AGAIN"
+# key 130 "KEY_PROPS"
+# key 131 "KEY_UNDO"
+# key 132 "KEY_FRONT"
+# key 133 "KEY_COPY"
+# key 134 "KEY_OPEN"
+# key 135 "KEY_PASTE"
+# key 136 "KEY_FIND"
+# key 137 "KEY_CUT"
+# key 138 "KEY_HELP"
+key 139   MENU              WAKE_DROPPED
+# key 140 "KEY_CALC"
+# key 141 "KEY_SETUP"
+# key 142 "KEY_SLEEP"
+# key 143 "KEY_WAKEUP"
+# key 144 "KEY_FILE"
+# key 145 "KEY_SENDFILE"
+# key 146 "KEY_DELETEFILE"
+# key 147 "KEY_XFER"
+# key 148 "KEY_PROG1"
+# key 149 "KEY_PROG2"
+key 150   EXPLORER
+# key 151 "KEY_MSDOS"
+# key 152 "KEY_COFFEE"
+# key 153 "KEY_DIRECTION"
+# key 154 "KEY_CYCLEWINDOWS"
+key 155   ENVELOPE
+# key 156 "KEY_BOOKMARKS"
+# key 157 "KEY_COMPUTER"
+key 158   BACK              WAKE_DROPPED
+key 159   FORWARD
+key 160   MEDIA_CLOSE
+key 161   MEDIA_EJECT
+key 162   MEDIA_EJECT
+key 163   MEDIA_NEXT
+key 164   MEDIA_PLAY_PAUSE
+key 165   MEDIA_PREVIOUS
+key 166   MEDIA_STOP
+key 167   MEDIA_RECORD
+key 168   MEDIA_REWIND
+key 169   CALL
+# key 170 "KEY_ISO"
+# key 171 "KEY_CONFIG"
+key 172   HOME
+# key 173 "KEY_REFRESH"
+# key 174 "KEY_EXIT"
+# key 175 "KEY_MOVE"
+# key 176 "KEY_EDIT"
+key 177   PAGE_UP
+key 178   PAGE_DOWN
+key 179   NUMPAD_LEFT_PAREN
+key 180   NUMPAD_RIGHT_PAREN
+# key 181 "KEY_NEW"
+# key 182 "KEY_REDO"
+# key 183   F13
+# key 184   F14
+# key 185   F15
+# key 186   F16
+# key 187   F17
+# key 188   F18
+# key 189   F19
+# key 190   F20
+# key 191   F21
+# key 192   F22
+# key 193   F23
+# key 194   F24
+# key 195 (undefined)
+# key 196 (undefined)
+# key 197 (undefined)
+# key 198 (undefined)
+# key 199 (undefined)
+key 200   MEDIA_PLAY
+key 201   MEDIA_PAUSE
+# key 202 "KEY_PROG3"
+# key 203 "KEY_PROG4"
+# key 204 (undefined)
+# key 205 "KEY_SUSPEND"
+# key 206 "KEY_CLOSE"
+key 207   MEDIA_PLAY
+key 208   MEDIA_FAST_FORWARD
+# key 209 "KEY_BASSBOOST"
+# key 210 "KEY_PRINT"
+# key 211 "KEY_HP"
+key 212   CAMERA
+# key 213 "KEY_SOUND"
+# key 214 "KEY_QUESTION"
+key 215   ENVELOPE
+# key 216 "KEY_CHAT"
+key 217   SEARCH
+# key 218 "KEY_CONNECT"
+# key 219 "KEY_FINANCE"
+# key 220 "KEY_SPORT"
+# key 221 "KEY_SHOP"
+# key 222 "KEY_ALTERASE"
+# key 223 "KEY_CANCEL"
+# key 224 "KEY_BRIGHTNESSDOWN"
+# key 225 "KEY_BRIGHTNESSUP"
+key 226   HEADSETHOOK
+key 227   STAR
+key 228   POUND
+key 229   SOFT_LEFT
+key 230   SOFT_RIGHT
+key 231   CALL
+key 232   DPAD_CENTER
+key 233   HEADSETHOOK
+# key 234 "KEY_0_5" or "KEY_SAVE"
+# key 235 "KEY_2_5" or "KEY_DOCUMENTS"
+# key 236 "KEY_SWITCHVIDEOMODE" or "KEY_BATTERY"
+# key 237 "KEY_KBDILLUMTOGGLE"
+# key 238 "KEY_KBDILLUMDOWN"
+# key 239 "KEY_KBDILLUMUP"
+# key 240 "KEY_UNKNOWN"
+
+
+key 304   BUTTON_A
+key 305   BUTTON_B
+key 306   BUTTON_C
+key 307   BUTTON_X
+key 308   BUTTON_Y
+key 309   BUTTON_Z
+key 310   BUTTON_L1
+key 311   BUTTON_R1
+key 312   BUTTON_L2
+key 313   BUTTON_R2
+key 314   BUTTON_SELECT
+key 315   BUTTON_START
+key 316   BUTTON_MODE
+key 317   BUTTON_THUMBL
+key 318   BUTTON_THUMBR
+
+
+# key 352 "KEY_OK"
+# key 353 "KEY_SELECT"
+# key 354 "KEY_GOTO"
+# key 355 "KEY_CLEAR"
+# key 356 "KEY_POWER2"
+# key 357 "KEY_OPTION"
+# key 358 "KEY_INFO"
+# key 359 "KEY_TIME"
+# key 360 "KEY_VENDOR"
+# key 361 "KEY_ARCHIVE"
+# key 362 "KEY_PROGRAM"
+# key 363 "KEY_CHANNEL"
+# key 364 "KEY_FAVORITES"
+# key 365 "KEY_EPG"
+# key 366 "KEY_PVR"
+# key 367 "KEY_MHP"
+# key 368 "KEY_LANGUAGE"
+# key 369 "KEY_TITLE"
+# key 370 "KEY_SUBTITLE"
+# key 371 "KEY_ANGLE"
+# key 372 "KEY_ZOOM"
+# key 373 "KEY_MODE"
+# key 374 "KEY_KEYBOARD"
+# key 375 "KEY_SCREEN"
+# key 376 "KEY_PC"
+# key 377 "KEY_TV"
+# key 378 "KEY_TV2"
+# key 379 "KEY_VCR"
+# key 380 "KEY_VCR2"
+# key 381 "KEY_SAT"
+# key 382 "KEY_SAT2"
+# key 383 "KEY_CD"
+# key 384 "KEY_TAPE"
+# key 385 "KEY_RADIO"
+# key 386 "KEY_TUNER"
+# key 387 "KEY_PLAYER"
+# key 388 "KEY_TEXT"
+# key 389 "KEY_DVD"
+# key 390 "KEY_AUX"
+# key 391 "KEY_MP3"
+# key 392 "KEY_AUDIO"
+# key 393 "KEY_VIDEO"
+# key 394 "KEY_DIRECTORY"
+# key 395 "KEY_LIST"
+# key 396 "KEY_MEMO"
+# key 397 "KEY_CALENDAR"
+# key 398 "KEY_RED"
+# key 399 "KEY_GREEN"
+# key 400 "KEY_YELLOW"
+# key 401 "KEY_BLUE"
+# key 402 "KEY_CHANNELUP"
+# key 403 "KEY_CHANNELDOWN"
+# key 404 "KEY_FIRST"
+# key 405 "KEY_LAST"
+# key 406 "KEY_AB"
+# key 407 "KEY_NEXT"
+# key 408 "KEY_RESTART"
+# key 409 "KEY_SLOW"
+# key 410 "KEY_SHUFFLE"
+# key 411 "KEY_BREAK"
+# key 412 "KEY_PREVIOUS"
+# key 413 "KEY_DIGITS"
+# key 414 "KEY_TEEN"
+# key 415 "KEY_TWEN"
+
+
+# key 448 "KEY_DEL_EOL"
+# key 449 "KEY_DEL_EOS"
+# key 450 "KEY_INS_LINE"
+# key 451 "KEY_DEL_LINE"
+
+
+key 464   FUNCTION
+key 465   ESCAPE            FUNCTION
+key 466   F1                FUNCTION
+key 467   F2                FUNCTION
+key 468   F3                FUNCTION
+key 469   F4                FUNCTION
+key 470   F5                FUNCTION
+key 471   F6                FUNCTION
+key 472   F7                FUNCTION
+key 473   F8                FUNCTION
+key 474   F9                FUNCTION
+key 475   F10               FUNCTION
+key 476   F11               FUNCTION
+key 477   F12               FUNCTION
+key 478   1                 FUNCTION
+key 479   2                 FUNCTION
+key 480   D                 FUNCTION
+key 481   E                 FUNCTION
+key 482   F                 FUNCTION
+key 483   S                 FUNCTION
+key 484   B                 FUNCTION
+
+
+# key 497 KEY_BRL_DOT1
+# key 498 KEY_BRL_DOT2
+# key 499 KEY_BRL_DOT3
+# key 500 KEY_BRL_DOT4
+# key 501 KEY_BRL_DOT5
+# key 502 KEY_BRL_DOT6
+# key 503 KEY_BRL_DOT7
+# key 504 KEY_BRL_DOT8
diff --git a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm
new file mode 100644
index 0000000..0f31683
--- /dev/null
+++ b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kcm
@@ -0,0 +1,71 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+[type=QWERTY]
+
+# keycode       display number  base    caps    fn      caps_fn
+
+A               'A'     '2'     'a'     'A'     'a'     'A'
+B               'B'     '2'     'b'     'B'     'b'     'B'
+C               'C'     '2'     'c'     'C'     'c'     'C'
+D               'D'     '3'     'd'     'D'     'd'     'D'
+E               'E'     '3'     'e'     'E'     'e'     'E'
+F               'F'     '3'     'f'     'F'     'f'     'F'
+G               'G'     '4'     'g'     'G'     'g'     'G'
+H               'H'     '4'     'h'     'H'     'h'     'H'
+I               'I'     '4'     'i'     'I'     'i'     'I'
+J               'J'     '5'     'j'     'J'     'j'     'J'
+K               'K'     '5'     'k'     'K'     'k'     'K'
+L               'L'     '5'     'l'     'L'     'l'     'L'
+M               'M'     '6'     'm'     'M'     'm'     'M'
+N               'N'     '6'     'n'     'N'     'n'     'N'
+O               'O'     '6'     'o'     'O'     'o'     'O'
+P               'P'     '7'     'p'     'P'     'p'     'P'
+Q               'Q'     '7'     'q'     'Q'     'q'     'Q'
+R               'R'     '7'     'r'     'R'     'r'     'R'
+S               'S'     '7'     's'     'S'     's'     'S'
+T               'T'     '8'     't'     'T'     't'     'T'
+U               'U'     '8'     'u'     'U'     'u'     'U'
+V               'V'     '8'     'v'     'V'     'v'     'V'
+W               'W'     '9'     'w'     'W'     'w'     'W'
+X               'X'     '9'     'x'     'X'     'x'     'X'
+Y               'Y'     '9'     'y'     'Y'     'y'     'Y'
+Z               'Z'     '9'     'z'     'Z'     'z'     'Z'
+
+0               '0'     '0'     '0'     ')'     '0'     ')'
+1               '1'     '1'     '1'     '!'     '1'     '!'
+2               '2'     '2'     '2'     '@'     '2'     '@'
+3               '3'     '3'     '3'     '#'     '3'     '#'
+4               '4'     '4'     '4'     '$'     '4'     '$'
+5               '5'     '5'     '5'     '%'     '5'     '%'
+6               '6'     '6'     '6'     '^'     '6'     '^'
+7               '7'     '7'     '7'     '&'     '7'     '&'
+8               '8'     '8'     '8'     '*'     '8'     '*'
+9               '9'     '9'     '9'     '('     '9'     '('
+
+SPACE           0x20    0x20    0x20    0x20    0x20    0x20
+ENTER           0xa     0xa     0xa     0xa     0xa     0xa
+TAB             0x9     0x9     0x9     0x9     0x9     0x9
+
+COMMA           ','     ','     ','     '<'     ','     '<'
+PERIOD          '.'     '.'     '.'     '>'     '.'     '>'
+SLASH           '/'     '/'     '/'     '?'     '/'     '?'
+GRAVE           '`'     '`'     '`'     '~'     '`'     '~'
+MINUS           '-'     '-'     '-'     '_'     '-'     '_'
+EQUALS          '='     '='     '='     '+'     '='     '+'
+LEFT_BRACKET    '['     '['     '['     '{'     '['     '{'
+RIGHT_BRACKET   ']'     ']'     ']'     '}'     ']'     '}'
+BACKSLASH       '\'     '\'     '\'     '|'     '\'     '|'
+SEMICOLON       ';'     ';'     ';'     ':'     ';'     ':'
+APOSTROPHE      '''     '''     '''     '"'     '''     '"'
diff --git a/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl
new file mode 100644
index 0000000..eab78a0
--- /dev/null
+++ b/data/keyboards/Motorola_Bluetooth_Wireless_Keyboard.kl
@@ -0,0 +1,97 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+key 1     BACK
+key 2     1
+key 3     2
+key 4     3
+key 5     4
+key 6     5
+key 7     6
+key 8     7
+key 9     8
+key 10    9
+key 11    0
+key 12    MINUS
+key 13    EQUALS
+key 14    DEL
+key 15    TAB
+key 16    Q
+key 17    W
+key 18    E
+key 19    R
+key 20    T
+key 21    Y
+key 22    U
+key 23    I
+key 24    O
+key 25    P
+key 26    LEFT_BRACKET
+key 27    RIGHT_BRACKET
+key 28    ENTER
+key 29    CTRL_LEFT
+key 30    A
+key 31    S
+key 32    D
+key 33    F
+key 34    G
+key 35    H
+key 36    J
+key 37    K
+key 38    L
+key 39    SEMICOLON
+key 40    APOSTROPHE
+key 41    GRAVE
+key 42    SHIFT_LEFT
+key 43    BACKSLASH
+key 44    Z
+key 45    X
+key 46    C
+key 47    V
+key 48    B
+key 49    N
+key 50    M
+key 51    COMMA
+key 52    PERIOD
+key 53    SLASH
+key 54    SHIFT_RIGHT
+key 57    SPACE
+key 58    CAPS_LOCK
+key 59    F1
+key 60    F2
+key 61    F3
+key 62    F4
+key 63    F5
+key 64    F6
+key 65    F7
+key 66    F8
+key 67    F9
+key 97    CTRL_RIGHT
+key 102   HOME
+key 103   DPAD_UP
+key 105   DPAD_LEFT
+key 106   DPAD_RIGHT
+key 107   MOVE_END
+key 108   DPAD_DOWN
+key 110   INSERT
+key 111   FORWARD_DEL
+key 113   VOLUME_MUTE
+key 114   VOLUME_DOWN
+key 115   VOLUME_UP
+key 125   MENU
+key 127   SEARCH
+key 163   MEDIA_NEXT
+key 164   MEDIA_PLAY_PAUSE
+key 165   MEDIA_PREVIOUS
+key 166   MEDIA_STOP
diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk
new file mode 100644
index 0000000..7a58b07
--- /dev/null
+++ b/data/keyboards/common.mk
@@ -0,0 +1,24 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This is the list of keylayouts and key character maps to build.
+# Used by Android.mk and keyboards.mk.
+
+keylayouts := \
+    Generic.kl \
+    Motorola_Bluetooth_Wireless_Keyboard.kl
+
+keycharmaps := \
+    Generic.kcm \
+    Motorola_Bluetooth_Wireless_Keyboard.kcm
diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk
new file mode 100644
index 0000000..665c8bf
--- /dev/null
+++ b/data/keyboards/keyboards.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Warning: this is actually a product definition, to be inherited from
+
+include $(LOCAL_PATH)/common.mk
+
+PRODUCT_COPY_FILES := $(foreach file,$(keylayouts),\
+    frameworks/base/data/keyboards/$(file):system/usr/keylayout/$(file))
+
+PRODUCT_PACKAGES := $(keycharmaps)
diff --git a/data/sounds/AudioPackage5.mk b/data/sounds/AudioPackage5.mk
deleted file mode 100755
index 550f990..0000000
--- a/data/sounds/AudioPackage5.mk
+++ /dev/null
@@ -1,72 +0,0 @@
-#
-# Audio Package 5 - Crespo/Soju
-# 
-# Include this file in a product makefile to include these audio files
-#
-# 
-
-LOCAL_PATH:= frameworks/base/data/sounds
-
-PRODUCT_COPY_FILES += \
-	$(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
-	$(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
-	$(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
-	$(LOCAL_PATH)/effects/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
-	$(LOCAL_PATH)/effects/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg \
-	$(LOCAL_PATH)/effects/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
-	$(LOCAL_PATH)/effects/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg \
-	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
-	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
-	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
-	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
-	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
-	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
-	$(LOCAL_PATH)/effects/Lock.ogg:system/media/audio/ui/Lock.ogg \
-	$(LOCAL_PATH)/effects/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
-	$(LOCAL_PATH)/notifications/Aldebaran.ogg:system/media/audio/notifications/Aldebaran.ogg \
-	$(LOCAL_PATH)/notifications/Altair.ogg:system/media/audio/notifications/Altair.ogg \
-	$(LOCAL_PATH)/notifications/Antares.ogg:system/media/audio/notifications/Antares.ogg \
-	$(LOCAL_PATH)/notifications/arcturus.ogg:system/media/audio/notifications/arcturus.ogg \
-	$(LOCAL_PATH)/notifications/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
-	$(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
-	$(LOCAL_PATH)/notifications/Capella.ogg:system/media/audio/notifications/Capella.ogg \
-	$(LOCAL_PATH)/notifications/Castor.ogg:system/media/audio/notifications/Castor.ogg \
-	$(LOCAL_PATH)/notifications/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
-	$(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
-	$(LOCAL_PATH)/notifications/Electra.ogg:system/media/audio/notifications/Electra.ogg \
-	$(LOCAL_PATH)/notifications/Fomalhaut.ogg:system/media/audio/notifications/Fomalhaut.ogg \
-	$(LOCAL_PATH)/notifications/Merope.ogg:system/media/audio/notifications/Merope.ogg \
-	$(LOCAL_PATH)/notifications/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
-	$(LOCAL_PATH)/notifications/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
-	$(LOCAL_PATH)/notifications/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
-	$(LOCAL_PATH)/notifications/regulus.ogg:system/media/audio/notifications/regulus.ogg \
-	$(LOCAL_PATH)/notifications/sirius.ogg:system/media/audio/notifications/sirius.ogg \
-	$(LOCAL_PATH)/notifications/Sirrah.ogg:system/media/audio/notifications/Sirrah.ogg \
-	$(LOCAL_PATH)/notifications/vega.ogg:system/media/audio/notifications/vega.ogg \
-	$(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:system/media/audio/ringtones/ANDROMEDA.ogg \
-	$(LOCAL_PATH)/ringtones/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
-	$(LOCAL_PATH)/ringtones/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
-	$(LOCAL_PATH)/ringtones/BOOTES.ogg:system/media/audio/ringtones/BOOTES.ogg \
-	$(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:system/media/audio/ringtones/CANISMAJOR.ogg \
-	$(LOCAL_PATH)/ringtones/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
-	$(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:system/media/audio/ringtones/CASSIOPEIA.ogg \
-	$(LOCAL_PATH)/ringtones/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
-	$(LOCAL_PATH)/ringtones/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
-	$(LOCAL_PATH)/ringtones/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
-	$(LOCAL_PATH)/ringtones/Eridani.ogg:system/media/audio/ringtones/Eridani.ogg \
-	$(LOCAL_PATH)/ringtones/hydra.ogg:system/media/audio/ringtones/hydra.ogg \
-	$(LOCAL_PATH)/ringtones/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
-	$(LOCAL_PATH)/ringtones/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
-	$(LOCAL_PATH)/ringtones/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
-	$(LOCAL_PATH)/ringtones/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
-	$(LOCAL_PATH)/ringtones/PERSEUS.ogg:system/media/audio/ringtones/PERSEUS.ogg \
-	$(LOCAL_PATH)/ringtones/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
-	$(LOCAL_PATH)/ringtones/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
-	$(LOCAL_PATH)/ringtones/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
-	$(LOCAL_PATH)/ringtones/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
-	$(LOCAL_PATH)/ringtones/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
-	$(LOCAL_PATH)/ringtones/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
-	$(LOCAL_PATH)/ringtones/URSAMINOR.ogg:system/media/audio/ringtones/URSAMINOR.ogg \
-	$(LOCAL_PATH)/ringtones/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg
diff --git a/data/sounds/notifications/Aldebaran.ogg b/data/sounds/notifications/Aldebaran.ogg
deleted file mode 100755
index fe30137..0000000
--- a/data/sounds/notifications/Aldebaran.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Altair.ogg b/data/sounds/notifications/Altair.ogg
deleted file mode 100755
index 8b56f91..0000000
--- a/data/sounds/notifications/Altair.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Antares.ogg b/data/sounds/notifications/Antares.ogg
deleted file mode 100755
index f4f94d7..0000000
--- a/data/sounds/notifications/Antares.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Betelgeuse.ogg b/data/sounds/notifications/Betelgeuse.ogg
deleted file mode 100755
index 7145a16..0000000
--- a/data/sounds/notifications/Betelgeuse.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Canopus.ogg b/data/sounds/notifications/Canopus.ogg
deleted file mode 100755
index 0d55925..0000000
--- a/data/sounds/notifications/Canopus.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Capella.ogg b/data/sounds/notifications/Capella.ogg
deleted file mode 100755
index 22e1d37..0000000
--- a/data/sounds/notifications/Capella.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Castor.ogg b/data/sounds/notifications/Castor.ogg
deleted file mode 100755
index 4ad22d0..0000000
--- a/data/sounds/notifications/Castor.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/CetiAlpha.ogg b/data/sounds/notifications/CetiAlpha.ogg
deleted file mode 100755
index cd09526..0000000
--- a/data/sounds/notifications/CetiAlpha.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/CetiAlpha.wav b/data/sounds/notifications/CetiAlpha.wav
deleted file mode 100755
index d209645..0000000
--- a/data/sounds/notifications/CetiAlpha.wav
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Deneb.ogg b/data/sounds/notifications/Deneb.ogg
deleted file mode 100755
index 096ff0a..0000000
--- a/data/sounds/notifications/Deneb.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Electra.ogg b/data/sounds/notifications/Electra.ogg
deleted file mode 100755
index 313cb6f..0000000
--- a/data/sounds/notifications/Electra.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Fomalhaut.ogg b/data/sounds/notifications/Fomalhaut.ogg
deleted file mode 100755
index 39c71ad..0000000
--- a/data/sounds/notifications/Fomalhaut.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Merope.ogg b/data/sounds/notifications/Merope.ogg
deleted file mode 100755
index fb18c73..0000000
--- a/data/sounds/notifications/Merope.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Polaris.ogg b/data/sounds/notifications/Polaris.ogg
deleted file mode 100755
index 8104178..0000000
--- a/data/sounds/notifications/Polaris.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Pollux.ogg b/data/sounds/notifications/Pollux.ogg
deleted file mode 100755
index ede9e27..0000000
--- a/data/sounds/notifications/Pollux.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Procyon.ogg b/data/sounds/notifications/Procyon.ogg
deleted file mode 100755
index 02dbc3f..0000000
--- a/data/sounds/notifications/Procyon.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/Sirrah.ogg b/data/sounds/notifications/Sirrah.ogg
deleted file mode 100755
index ee79e49..0000000
--- a/data/sounds/notifications/Sirrah.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/arcturus.ogg b/data/sounds/notifications/arcturus.ogg
deleted file mode 100644
index 7b61963..0000000
--- a/data/sounds/notifications/arcturus.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/regulus.ogg b/data/sounds/notifications/regulus.ogg
deleted file mode 100644
index dc52054..0000000
--- a/data/sounds/notifications/regulus.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/sirius.ogg b/data/sounds/notifications/sirius.ogg
deleted file mode 100644
index a54481b..0000000
--- a/data/sounds/notifications/sirius.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/notifications/vega.ogg b/data/sounds/notifications/vega.ogg
deleted file mode 100644
index e596e60..0000000
--- a/data/sounds/notifications/vega.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/ANDROMEDA.ogg b/data/sounds/ringtones/ANDROMEDA.ogg
deleted file mode 100644
index 33e43e3..0000000
--- a/data/sounds/ringtones/ANDROMEDA.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Aquila.ogg b/data/sounds/ringtones/Aquila.ogg
deleted file mode 100755
index 3f9266d..0000000
--- a/data/sounds/ringtones/Aquila.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/ArgoNavis.ogg b/data/sounds/ringtones/ArgoNavis.ogg
deleted file mode 100755
index 11c6092..0000000
--- a/data/sounds/ringtones/ArgoNavis.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/BOOTES.ogg b/data/sounds/ringtones/BOOTES.ogg
deleted file mode 100644
index 485a9f6..0000000
--- a/data/sounds/ringtones/BOOTES.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/CANISMAJOR.ogg b/data/sounds/ringtones/CANISMAJOR.ogg
deleted file mode 100644
index e51d6e0..0000000
--- a/data/sounds/ringtones/CANISMAJOR.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/CASSIOPEIA.ogg b/data/sounds/ringtones/CASSIOPEIA.ogg
deleted file mode 100644
index 0456a59..0000000
--- a/data/sounds/ringtones/CASSIOPEIA.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Carina.ogg b/data/sounds/ringtones/Carina.ogg
deleted file mode 100755
index 25cf36e..0000000
--- a/data/sounds/ringtones/Carina.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Carina.wav b/data/sounds/ringtones/Carina.wav
deleted file mode 100755
index ecaeb58..0000000
--- a/data/sounds/ringtones/Carina.wav
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Centaurus.ogg b/data/sounds/ringtones/Centaurus.ogg
deleted file mode 100755
index 58b413d..0000000
--- a/data/sounds/ringtones/Centaurus.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Cygnus.ogg b/data/sounds/ringtones/Cygnus.ogg
deleted file mode 100755
index 763926e..0000000
--- a/data/sounds/ringtones/Cygnus.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Draco.ogg b/data/sounds/ringtones/Draco.ogg
deleted file mode 100755
index 8749b3a..0000000
--- a/data/sounds/ringtones/Draco.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Eridani.ogg b/data/sounds/ringtones/Eridani.ogg
deleted file mode 100755
index a290a6e..0000000
--- a/data/sounds/ringtones/Eridani.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Lyra.ogg b/data/sounds/ringtones/Lyra.ogg
deleted file mode 100755
index 16d47e9..0000000
--- a/data/sounds/ringtones/Lyra.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Machina.ogg b/data/sounds/ringtones/Machina.ogg
deleted file mode 100755
index ab0105e..0000000
--- a/data/sounds/ringtones/Machina.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Orion.ogg b/data/sounds/ringtones/Orion.ogg
deleted file mode 100755
index 8e36024..0000000
--- a/data/sounds/ringtones/Orion.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/PERSEUS.ogg b/data/sounds/ringtones/PERSEUS.ogg
deleted file mode 100644
index ad06021..0000000
--- a/data/sounds/ringtones/PERSEUS.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Pegasus.ogg b/data/sounds/ringtones/Pegasus.ogg
deleted file mode 100755
index 17baaed..0000000
--- a/data/sounds/ringtones/Pegasus.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Pyxis.ogg b/data/sounds/ringtones/Pyxis.ogg
deleted file mode 100644
index 2d3adce..0000000
--- a/data/sounds/ringtones/Pyxis.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Rigel.ogg b/data/sounds/ringtones/Rigel.ogg
deleted file mode 100644
index af2c176..0000000
--- a/data/sounds/ringtones/Rigel.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Scarabaeus.ogg b/data/sounds/ringtones/Scarabaeus.ogg
deleted file mode 100755
index 50b9fab..0000000
--- a/data/sounds/ringtones/Scarabaeus.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Sceptrum.ogg b/data/sounds/ringtones/Sceptrum.ogg
deleted file mode 100755
index 9be353e..0000000
--- a/data/sounds/ringtones/Sceptrum.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Solarium.ogg b/data/sounds/ringtones/Solarium.ogg
deleted file mode 100755
index 2aed475..0000000
--- a/data/sounds/ringtones/Solarium.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Testudo.ogg b/data/sounds/ringtones/Testudo.ogg
deleted file mode 100755
index 063dacf..0000000
--- a/data/sounds/ringtones/Testudo.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/URSAMINOR.ogg b/data/sounds/ringtones/URSAMINOR.ogg
deleted file mode 100644
index c0010e82..0000000
--- a/data/sounds/ringtones/URSAMINOR.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/Vespa.ogg b/data/sounds/ringtones/Vespa.ogg
deleted file mode 100755
index eaac1bd..0000000
--- a/data/sounds/ringtones/Vespa.ogg
+++ /dev/null
Binary files differ
diff --git a/data/sounds/ringtones/hydra.ogg b/data/sounds/ringtones/hydra.ogg
deleted file mode 100644
index b48614f..0000000
--- a/data/sounds/ringtones/hydra.ogg
+++ /dev/null
Binary files differ
diff --git a/docs/html/guide/topics/fundamentals.jd b/docs/html/guide/topics/fundamentals.jd
index a095087..84c2ed2 100644
--- a/docs/html/guide/topics/fundamentals.jd
+++ b/docs/html/guide/topics/fundamentals.jd
@@ -294,8 +294,8 @@
 <li><p>A service is started (or new instructions are given to an ongoing 
 service) by passing an Intent object to <code>{@link 
 android.content.Context#startService Context.startService()}</code>.  
-Android calls the service's <code>{@link android.app.Service#onStart 
-onStart()}</code> method and passes it the Intent object.</p>
+Android calls the service's <code>{@link android.app.Service#onStartCommand
+onStartCommand()}</code> method and passes it the Intent object.</p>
 
 <p>
 Similarly, an intent can be passed to <code>{@link 
@@ -1510,9 +1510,9 @@
 in {@code onCreate()}, and then stop the thread in {@code onDestroy()}.</li>
 
 <li><p>The <b>active lifetime</b> of a service begins with a call to 
-<code>{@link android.app.Service#onStart onStart()}</code>.  This method 
+<code>{@link android.app.Service#onStartCommand onStartCommand()}</code>.  This method
 is handed the Intent object that was passed to {@code startService()}.
-The music service would open the Intent to discover which music to 
+The music service would open the Intent to discover which music to
 play, and begin the playback.</p>
 
 <p>
@@ -1527,7 +1527,7 @@
 <code>{@link android.content.Context#startService Context.startService()}</code>
 or 
 <code>{@link android.content.Context#bindService Context.bindService()}</code>.
-However, {@code onStart()} is called only for services started by {@code
+However, {@code onStartCommand()} is called only for services started by {@code
 startService()}.
 </p>
 
@@ -1631,7 +1631,7 @@
 
 <li><p>It has a {@link android.app.Service} object that's executing
 one of its lifecycle callbacks (<code>{@link android.app.Service#onCreate 
-onCreate()}</code>, <code>{@link android.app.Service#onStart onStart()}</code>, 
+onCreate()}</code>, <code>{@link android.app.Service#onStartCommand onStartCommand()}</code>,
 or <code>{@link android.app.Service#onDestroy onDestroy()}</code>).</p></li>
 
 <li><p>It has a {@link android.content.BroadcastReceiver} object that's 
diff --git a/docs/html/guide/topics/fundamentals/activities.jd b/docs/html/guide/topics/fundamentals/activities.jd
new file mode 100644
index 0000000..b616ab8
--- /dev/null
+++ b/docs/html/guide/topics/fundamentals/activities.jd
@@ -0,0 +1,762 @@
+page.title=Activities
+parent.title=Application Fundamentals
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>Quickview</h2>
+<ul>
+  <li>An activity provides a user interface for a single screen in your application</li>
+  <li>Activities can move into the background and then be resumed with their state restored</li>
+</ul>
+
+<h2>In this document</h2>
+<ol>
+  <li><a href="#Creating">Creating an Activity</a>
+    <ol>
+      <li><a href="#UI">Implementing a user interface</a></li>
+      <li><a href="#Declaring">Declaring the activity in the manifest</a></li>
+    </ol>
+  </li>
+  <li><a href="#StartingAnActivity">Starting an Activity</a>
+    <ol>
+      <li><a href="#StartingAnActivityForResult">Starting an Activity for a Result</a></li>
+    </ol>
+  </li>
+  <li><a href="#Lifecycle">Managing the Activity Lifecycle</a>
+    <ol>
+      <li><a href="#ImplementingLifecycleCallbacks">Implementing the lifecycle callbacks</a></li>
+      <li><a href="#SavingActivityState">Saving activity state</a></li>
+      <li><a href="#ConfigurationChanges">Handling configuration changes</a></li>
+      <li><a href="#CoordinatingActivities">Coordinating activities</a></li>
+    </ol>
+  </li>
+</ol>
+
+<h2>Key classes</h2>
+<ol>
+  <li>{@link android.app.Activity}</li>
+</ol>
+
+<h2>See also</h2>
+<ol>
+  <li><a href="{@docRoot}resources/tutorials/hello-world.html">Hello World Tutorial</a></li>
+  <li><a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack">Tasks and Back
+Stack</a></li>
+</ol>
+
+</div>
+</div>
+
+
+
+<p>An {@link android.app.Activity} is an application component that provides a screen with which
+users can interact in order to do something, such as dial the phone, take a photo, send an email, or
+view a map. Each activity is given a window in which to draw its user interface. The window
+typically fills the screen, but may be smaller than the screen and float on top of other
+windows.</p>
+
+<p> An application usually consists of multiple activities that are loosely bound
+to each other. Typically, one activity in an application is specified as the "main" activity, which
+is presented to the user when launching the application for the first time. Each
+activity can then start another activity in order to perform different actions. Each time a new
+activity starts, the previous activity is stopped, but the system preserves the activity
+in a stack (the "back stack"). When a new activity starts, it is pushed onto the back stack and
+takes user focus. The back stack abides to the basic "last in, first out" queue mechanism,
+so, when the user is done with the current activity and presses the BACK key, it
+is popped from the stack (and destroyed) and the previous activity resumes. (The back stack is
+discussed more in the <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks
+and Back Stack</a> document.)</p>
+
+<p>When an activity is stopped because a new activity starts, it is notified of this change in state
+through the activity's lifecycle callback methods.
+There are several callback methods that an activity might receive, due to a change in its
+state&mdash;whether the system is creating it, stopping it, resuming it, or destroying it&mdash;and
+each callback provides you the opportunity to perform specific work that's
+appropriate to that state change. For instance, when stopped, your activity should release any
+large objects, such as network or database connections. When the activity resumes, you can
+reacquire the necessary resources and resume actions that were interrupted. These state transitions
+are all part of the activity lifecycle.</p>
+
+<p>The rest of this document discusses the basics of how to build and use an activity,
+including a complete discussion of how the activity lifecycle works, so you can properly manage
+the transition between various activity states.</p>
+
+
+
+<h2 id="Creating">Creating an Activity</h2>
+
+<p>To create an activity, you must create a subclass of {@link android.app.Activity} (or
+an existing subclass of it). In your subclass, you need to implement callback methods that the
+system calls when the activity transitions between various states of its lifecycle, such as when
+the activity is being created, stopped, resumed, or destroyed. The two most important callback
+methods are:</p>
+
+<dl>
+  <dt>{@link android.app.Activity#onCreate onCreate()}</dt>
+  <dd>You must implement this method. The system calls this when creating your
+    activity. Within your implementation, you should initialize the essential components of your
+activity.
+    Most importantly, this is where you must call {@link android.app.Activity#setContentView
+    setContentView()} to define the layout for the activity's user interface.</dd>
+  <dt>{@link android.app.Activity#onPause onPause()}</dt>
+  <dd>The system calls this method as the first indication that the user is leaving your
+activity (though it does not always mean the activity is being destroyed). This is usually where you
+should commit any changes that should be persisted beyond the current user session (because
+the user might not come back).</dd>
+</dl>
+
+<p>There are several other lifecycle callback methods that you should use in order to provide a
+fluid user experience between activities and handle unexpected interuptions that cause your activity
+to be stopped and even destroyed. All of the lifecycle callback methods are discussed later, in
+the section about <a href="#Lifecycle">Managing the Activity Lifecycle</a>.</p>
+
+
+
+<h3 id="UI">Implementing a user interface</h3>
+
+<p> The user interface for an activity is provided by a hierarchy of views&mdash;objects derived
+from the {@link android.view.View} class.  Each view controls a particular rectangular space
+within the activity's window and can respond to user interaction. For example, a view might be a
+button that initiates an action when the user touches it.</p>
+
+<p>Android provides a number of ready-made views that you can use to design and organize your
+layout. "Widgets" are views that provide a visual (and interactive) elements for the screen, such
+as a button, text field, checkbox, or just an image. "Layouts" are views derived from {@link
+android.view.ViewGroup} that provide a unique layout model for its child views, such as a linear
+layout, a grid layout, or relative layout. You can also subclass the {@link android.view.View} and
+{@link android.view.ViewGroup} classes (or existing subclasses) to create your own widgets and
+layouts and apply them to your activity layout.</p>
+
+<p>The most common way to define a layout using views is with an XML layout file saved in your
+application resources. This way, you can maintain the design of your user interface separately from
+the source code that defines the activity's behavior. You can set the layout as the UI for your
+activity with {@link android.app.Activity#setContentView(int) setContentView()}, passing the
+resource ID for the layout. However, you can also create new {@link android.view.View}s in your
+activity code and build a view hierarchy by inserting new {@link
+android.view.View}s into a {@link android.view.ViewGroup}, then use that layout by passing the root
+{@link android.view.ViewGroup} to {@link android.app.Activity#setContentView(View)
+setContentView()}.</p>
+
+<p>For information about creating a user interface, see the <a
+href="{@docRoot}guide/topics/ui/index.html">User Interface</a> documentation.</p>
+
+
+
+<h3 id="Declaring">Declaring the activity in the manifest</h3>
+
+<p>You must declare your activity in the manifest file in order for it to
+be accessible to the system. To decalare your activity, open your manifest file and add an <a
+href="{@docRoot}guide/topics/manifest/activity-element.html">{@code &lt;activity&gt;}</a> element
+as a child of the <a
+href="{@docRoot}guide/topics/manifest/application-element.html">{@code &lt;application&gt;}</a>
+element. For example:</p>
+
+<pre>
+&lt;manifest ... &gt;
+  &lt;application ... &gt;
+      &lt;activity android:name=".ExampleActivity" /&gt;
+      ...
+  &lt;/application ... &gt;
+  ...
+&lt;/manifest &gt;
+</pre>
+
+<p>There are several other attributes that you can include in this element, to define properties
+such as the label for the activity, an icon for the activity, or a theme to style the activity's
+UI. See the <a
+href="{@docRoot}guide/topics/manifest/activity-element.html">{@code &lt;activity&gt;}</a> element
+reference for more information about available attributes.</p>
+
+
+<h4>Using intent filters</h4>
+
+<p>An <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code
+&lt;activity&gt;}</a> element can also specify various intent filters&mdash;using the <a
+href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code
+&lt;intent-filter&gt;}</a> element&mdash;in order to declare how other application components may
+activate it.</p>
+
+<p>When you create a new application using the Android SDK tools, the stub activity
+that's created for you automatically includes an intent filter that declares the activity
+responds to the "main" action and should be placed in the "launcher" category. The intent filter
+looks like this:</p>
+
+<pre>
+&lt;activity android:name=".ExampleActivity" android:icon="@drawable/app_icon"&gt;
+    &lt;intent-filter&gt;
+        &lt;action android:name="android.intent.action.MAIN" /&gt;
+        &lt;category android:name="android.intent.category.LAUNCHER" /&gt;
+    &lt;/intent-filter&gt;
+&lt;/activity&gt;
+</pre>
+
+<p>The <a href="{@docRoot}guide/topics/manifest/action-element.html">{@code
+&lt;action&gt;}</a> element specifies that this is the "main" entry point to the application. The <a
+href="{@docRoot}guide/topics/manifest/category-element.html">{@code
+&lt;category&gt;}</a> element specifies that this activity should be listed in the
+system's application launcher (to allow users to launch this activity).</p>
+
+<p>If you intend for your application to be self-contained and not allow other applications to
+activate its activities, then you don't need any other intent filters. Only one activity should
+have the "main" action and "launcher" category, as in the previous example. Activities that
+you don't want to make available to other applications should have no intent filters and you can
+start them yourself using explicit intents (as discussed in the following section).</p>
+
+<p>However, if you want your activity to respond to implicit intents that are delivered from
+other applications (and your own), then you must define additional intent filters for your
+activity. For each type of intent to which you want to respond, you must include an <a
+href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code
+&lt;intent-filter&gt;}</a> that includes an
+<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code
+&lt;action&gt;}</a> element and, optionally, a <a
+href="{@docRoot}guide/topics/manifest/category-element.html">{@code
+&lt;category&gt;}</a> element and/or a <a
+href="{@docRoot}guide/topics/manifest/data-element.html">{@code
+&lt;data&gt;}</a> element. These elements specify the type of intent to which your activity can
+respond.</p>
+
+<p>For more information about how your activities can respond to intents, see the <a
+href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>
+document.</p>
+
+
+
+<h2 id="StartingAnActivity">Starting an Activity</h2>
+
+<p>You can start another activity by calling {@link android.app.Activity#startActivity
+  startActivity()}, passing it an {@link android.content.Intent} that describes the activity you
+  want to start. The intent specifies either the exact activity you want to start or describes the
+  type of action you want to perform (and the system selects the appropriate activity for you,
+which
+  can even be from a different application). An intent can also carry small amounts of data to be
+  used by the activity that is started.</p>
+
+<p>When working within your own application, you'll often need to simply launch a known activity.
+ You can do so by creating an intent that explicitly defines the activity you want to start,
+using the class name. For example, here's how one activity starts another activity named {@code
+SignInActivity}:</p>
+
+<pre>
+Intent intent = new Intent(this, SignInActivity.class);
+startActivity(intent);
+</pre>
+
+<p>However, your application might also want to perform some action, such as send an email, text
+  message, or status update, using data from your activity. In this case, your application might
+ not have its own activities to perform such actions, so you can instead leverage the activities
+  provided by other applications on the device, which can perform the actions for you. This is where
+intents are really valuable&mdash;you can create an intent that describes an action you want to
+perform and the system
+  launches the appropriate activity from another application. If there are
+  multiple activities that can handle the intent, then the user can select which one to use. For
+  example, if you want to allow the user to send an email message, you can create the
+  following intent:</p>
+
+<pre>
+Intent intent = new Intent(Intent.ACTION_SEND);
+intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
+startActivity(intent);
+</pre>
+
+<p>The {@link android.content.Intent#EXTRA_EMAIL} extra added to the intent is a string array of
+  email addresses to which the email should be sent. When an email application responds to this
+  intent, it reads the string array provided in the extra and places them in the "to" field of the
+  email composition form. In this situation, the email application's activity starts and when the
+  user is done, your activity resumes.</p>
+
+
+
+
+<h3 id="StartingAnActivityForResult">Starting an activity for a result</h3>
+
+<p>Sometimes, you might want to receive a result from the activity that you start. In that case,
+  start the activity by calling {@link android.app.Activity#startActivityForResult
+  startActivityForResult()} (instead of {@link android.app.Activity#startActivity
+  startActivity()}). To then receive the result from the subsequent
+activity, implement the {@link android.app.Activity#onActivityResult onActivityResult()} callback
+  method. When the subsequent activity is done, it returns a result in an {@link
+android.content.Intent} to your {@link android.app.Activity#onActivityResult onActivityResult()}
+method.</p>
+
+<p>For example, perhaps you want the user to pick one of their contacts, so your activity can
+do something with the information in that contact. Here's how you can create such an intent and
+handle the result:</p>
+
+<pre>
+private void pickContact() {
+    // Create an intent to "pick" a contact, as defined by the content provider URI
+    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
+    startActivityForResult(intent, PICK_CONTACT_REQUEST);
+}
+
+&#64;Override
+protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
+    if (resultCode == Activity.RESULT_OK &amp;&amp; requestCode == PICK_CONTACT_REQUEST) {
+        // Perform a query to the contact's content provider for the contact's name
+        Cursor cursor = getContentResolver().query(data.getData(),
+        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
+        if (cursor.moveToFirst()) { // True if the cursor is not empty
+            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
+            String name = cursor.getString(columnIndex);
+            // Do something with the selected contact's name...
+        }
+    }
+}
+</pre>
+
+<p>This example shows the basic logic you should use in your {@link
+android.app.Activity#onActivityResult onActivityResult()} method in order to handle an
+activity result. The first condition checks whether the request was successful&mdash;if it was, then
+the {@code resultCode} will be {@link android.app.Activity#RESULT_OK}&mdash;and whether the request
+to which this result is responding is known&mdash;in this case, the {@code requestCode} matches the
+second parameter sent with {@link android.app.Activity#startActivityForResult
+startActivityForResult()}. From there, the code handles the activity result by querying the
+data returned in an {@link android.content.Intent} (the {@code data} parameter).</p>
+
+<p>What happens is, a {@link
+android.content.ContentResolver} performs a query against a content provider, which returns a
+{@link android.database.Cursor} that allows the queried data to be read. For more information, see
+the <a
+href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> document.</p>
+
+<p>For more information about using intents, see the <a
+href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent
+Filters</a> document.</p>
+
+
+<h2 id="ShuttingDown">Shutting Down an Activity</h2>
+
+<p>You can shut down an activity by calling its {@link android.app.Activity#finish
+finish()} method. You can also shut down a separate activity that you previously started by calling
+{@link android.app.Activity#finishActivity finishActivity()}.</p>
+
+<p class="note"><strong>Note:</strong> In most cases, you should not explicitly finish an activity
+using these methods. As discussed in the following section about the activity lifecycle, the
+Android system manages the life of an activity for you, so you do not need to finish your own
+activities. Calling these methods could adversely affect the expected user
+experience and should only be used when you absolutely do not want the user to return to this
+instance of the activity.</p>
+
+
+<h2 id="Lifecycle">Managing the Activity Lifecycle</h2>
+
+<p>Managing the lifecycle of your activities by implementing callback methods is
+crucial to developing a strong
+and flexible application. The lifecycle of an activity is directly affected by its association with
+other activities, its task and back stack.</p>
+
+<p>An activity can exist in essentially three states:</p>
+
+<dl>
+  <dt><i>Resumed</i></dt>
+    <dd>The activity is in the foreground of the screen and has user focus. (This state is
+also sometimes referred to as "running".)</dd>
+
+  <dt><i>Paused</i></dt>
+    <dd>Another activity is in the foreground and has focus, but this one is still visible. That is,
+another activity is visible on top of this one and that activity is partially transparent or doesn't
+cover the entire screen. A paused activity is completely alive (the {@link android.app.Activity}
+object is retained in memory, it maintains all state and member information, and remains attached to
+the window manager), but can be killed by the system in extremely low memory situations.</dd>
+
+  <dt><i>Stopped</i></dt>
+    <dd>The activity is completely obscured by another activity (the activity is now in the
+"background"). A stopped activity is also still alive (the {@link android.app.Activity}
+object is retained in memory, it maintains all state and member information, but is <em>not</em>
+attached to the window manager). However, it is no longer visible to the user and it
+can be killed by the system when memory is needed elsewhere.</dd>
+</dl>
+
+<p>If an activity is paused or stopped, the system can drop it from memory either by asking it to
+finish (calling its {@link android.app.Activity#finish finish()} method), or simply killing its
+process.  When the activity is opened again (after being finished or killed), it must be created all
+over.</p>
+
+
+
+<h3 id="ImplementingLifecycleCallbacks">Implementing the lifecycle callbacks</h3>
+
+<p>When an activity transitions into and out of the different states described above, it is notified
+through various callback methods. All of the callback methods are hooks that you
+can override to do appropriate work when the state of your activity changes. The following skeleton
+activity includes each of the fundamental lifecycle methods:</p>
+
+
+<pre>
+public class ExampleActivity extends Activity {
+    &#64;Override
+    public void {@link android.app.Activity#onCreate onCreate}(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // The activity is being created.
+    }
+    &#64;Override
+    protected void {@link android.app.Activity#onStart onStart()} {
+        super.onStart();
+        // The activity is about to become visible.
+    }
+    &#64;Override
+    protected void {@link android.app.Activity#onResume onResume()} {
+        super.onResume();
+        // The activity has become visible (it is now "resumed").
+    }
+    &#64;Override
+    protected void {@link android.app.Activity#onPause onPause()} {
+        super.onPause();
+        // Another activity is taking focus (this activity is about to be "paused").
+    }
+    &#64;Override
+    protected void {@link android.app.Activity#onStop onStop()} {
+        super.onStop();
+        // The activity is no longer visible (it is now "stopped")
+    }
+    &#64;Override
+    protected void {@link android.app.Activity#onDestroy onDestroy()} {
+        super.onDestroy();
+        // The activity is about to be destroyed.
+    }
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> Your implementation of these lifecycle methods must
+always call the superclass implementation before doing any work, as shown in the examples above.</p>
+
+<p>Taken together, these methods define the entire lifecycle of an activity. By implementing these
+methods, you can monitor three nested loops in the activity lifecycle: </p>
+
+<ul>
+<li>The <b>entire lifetime</b> of an activity happens between the call to {@link
+android.app.Activity#onCreate onCreate()} and the call to {@link
+android.app.Activity#onDestroy}. Your activity should perform setup of
+"global" state (such as defining layout) in {@link android.app.Activity#onCreate onCreate()}, and
+release all remaining resources in {@link android.app.Activity#onDestroy}. For example, if your
+activity has a thread running in the background to download data from the network, it might create
+that thread in {@link android.app.Activity#onCreate onCreate()} and then stop the thread in {@link
+android.app.Activity#onDestroy}.</li>
+
+<li><p>The <b>visible lifetime</b> of an activity happens between the call to {@link
+android.app.Activity#onStart onStart()} and the call to {@link
+android.app.Activity#onStop onStop()}. During this time, the user can see the activity
+on-screen and interact with it. For example, {@link android.app.Activity#onStop onStop()} is called
+when a new activity starts and this one is no longer visible. Between these two methods, you can
+maintain resources that are needed to show the activity to the user. For example, you can register a
+{@link android.content.BroadcastReceiver} in {@link
+android.app.Activity#onStart onStart()} to monitor changes that impact your UI, and unregister
+it in {@link android.app.Activity#onStop onStop()} when the user can no longer see what you are
+displaying. The system might call {@link android.app.Activity#onStart onStart()} and {@link
+android.app.Activity#onStop onStop()} multiple times during the entire lifetime of the activity, as
+the activity alternates between being visible and hidden to the user.</p></li>
+
+<li><p>The <b>foreground lifetime</b> of an activity happens between the call to {@link
+android.app.Activity#onResume onResume()} and the call to {@link android.app.Activity#onPause
+onPause()}. During this time, the activity is in front of all other activities on screen and has
+user input focus.  An activity can frequently transition in and out of the foreground&mdash;for
+example, {@link android.app.Activity#onPause onPause()} is called when the device goes to sleep or
+when a dialog appears. Because this state can transition often, the code in these two methods should
+be fairly lightweight in order to avoid slow transitions that make the user wait.</p></li>
+</ul>
+
+<p>Figure 1 illustrates these loops and the paths an activity might take between states.
+The rectangles represent the callback methods you can implement to perform operations when
+the activity transitions between states. <p>
+
+<img src="{@docRoot}images/activity_lifecycle.png" alt="" />
+<p class="img-caption"><strong>Figure 1.</strong> The activity lifecycle.</p>
+
+<p>The same lifecycle callback methods are listed in table 1, which describes each of the callback
+methods in more detail and locates each one within the
+activity's overall lifecycle, including whether the system can kill the activity after the
+callback method completes.</p>
+
+<p class="table-caption"><strong>Table 1.</strong> A summary of the activity lifecycle's
+callback methods.</p>
+
+<table border="2" width="85%" frame="hsides" rules="rows">
+<colgroup align="left" span="3"></colgroup>
+<colgroup align="left"></colgroup>
+<colgroup align="center"></colgroup>
+<colgroup align="center"></colgroup>
+
+<thead>
+<tr><th colspan="3">Method</th> <th>Description</th> <th>Killable after?</th> <th>Next</th></tr>
+</thead>
+
+<tbody>
+<tr>
+  <td colspan="3" align="left"><code>{@link android.app.Activity#onCreate onCreate()}</code></td>
+  <td>Called when the activity is first created.
+      This is where you should do all of your normal static set up &mdash;
+      create views, bind data to lists, and so on.  This method is passed
+      a Bundle object containing the activity's previous state, if that
+      state was captured (see <a href="#actstate">Saving Activity State</a>,
+      later).
+      <p>Always followed by {@code onStart()}.</p></td>
+  <td align="center">No</td>
+      <td align="center">{@code onStart()}</td>
+</tr>
+
+<tr>
+   <td rowspan="5" style="border-left: none; border-right: none;">&nbsp;&nbsp;&nbsp;&nbsp;</td>
+   <td colspan="2" align="left"><code>{@link android.app.Activity#onRestart
+onRestart()}</code></td>
+   <td>Called after the activity has been stopped, just prior to it being
+       started again.
+       <p>Always followed by {@code onStart()}</p></td>
+   <td align="center">No</td>
+   <td align="center">{@code onStart()}</td>
+</tr>
+
+<tr>
+   <td colspan="2" align="left"><code>{@link android.app.Activity#onStart onStart()}</code></td>
+   <td>Called just before the activity becomes visible to the user.
+       <p>Followed by {@code onResume()} if the activity comes
+       to the foreground, or {@code onStop()} if it becomes hidden.</p></td>
+    <td align="center">No</td>
+    <td align="center">{@code onResume()} <br/>or<br/> {@code onStop()}</td>
+</tr>
+
+<tr>
+   <td rowspan="2" style="border-left: none;">&nbsp;&nbsp;&nbsp;&nbsp;</td>
+   <td align="left"><code>{@link android.app.Activity#onResume onResume()}</code></td>
+   <td>Called just before the activity starts
+       interacting with the user.  At this point the activity is at
+       the top of the activity stack, with user input going to it.
+       <p>Always followed by {@code onPause()}.</p></td>
+   <td align="center">No</td>
+   <td align="center">{@code onPause()}</td>
+</tr>
+
+<tr>
+   <td align="left"><code>{@link android.app.Activity#onPause onPause()}</code></td>
+   <td>Called when the system is about to start resuming another
+       activity.  This method is typically used to commit unsaved changes to
+       persistent data, stop animations and other things that may be consuming
+       CPU, and so on.  It should do whatever it does very quickly, because
+       the next activity will not be resumed until it returns.
+       <p>Followed either by {@code onResume()} if the activity
+       returns back to the front, or by {@code onStop()} if it becomes
+       invisible to the user.</td>
+   <td align="center"><strong style="color:#800000">Yes</strong></td>
+   <td align="center">{@code onResume()} <br/>or<br/> {@code onStop()}</td>
+</tr>
+
+<tr>
+   <td colspan="2" align="left"><code>{@link android.app.Activity#onStop onStop()}</code></td>
+   <td>Called when the activity is no longer visible to the user.  This
+       may happen because it is being destroyed, or because another activity
+       (either an existing one or a new one) has been resumed and is covering it.
+       <p>Followed either by {@code onRestart()} if
+       the activity is coming back to interact with the user, or by
+       {@code onDestroy()} if this activity is going away.</p></td>
+   <td align="center"><strong style="color:#800000">Yes</strong></td>
+   <td align="center">{@code onRestart()} <br/>or<br/> {@code onDestroy()}</td>
+</tr>
+
+<tr>
+   <td colspan="3" align="left"><code>{@link android.app.Activity#onDestroy
+onDestroy()}</code></td>
+   <td>Called before the activity is destroyed.  This is the final call
+       that the activity will receive.  It could be called either because the
+       activity is finishing (someone called <code>{@link android.app.Activity#finish
+       finish()}</code> on it), or because the system is temporarily destroying this
+       instance of the activity to save space.  You can distinguish
+       between these two scenarios with the <code>{@link
+       android.app.Activity#isFinishing isFinishing()}</code> method.</td>
+   <td align="center"><strong style="color:#800000">Yes</strong></td>
+   <td align="center"><em>nothing</em></td>
+</tr>
+</tbody>
+</table>
+
+<p>The column labeled "Killable after?" indicates whether or not the system can
+kill the process hosting the activity at any time <em>after the method returns</em>, without
+executing another line of the activity's code.  Three methods are marked "yes": ({@link
+android.app.Activity#onPause
+onPause()}, {@link android.app.Activity#onStop onStop()}, and {@link android.app.Activity#onDestroy
+onDestroy()}). Because {@link android.app.Activity#onPause onPause()} is the first
+of the three, once the activity is created, {@link android.app.Activity#onPause onPause()} is the
+last method that's guaranteed to be called before the process <em>can</em> be killed&mdash;if
+the system must recover memory in an emergency, then {@link
+android.app.Activity#onStop onStop()} and {@link android.app.Activity#onDestroy onDestroy()} might
+not be called. Therefore, you should use {@link android.app.Activity#onPause onPause()} to write
+crucial persistent data (such as user edits) to storage. However, you should be selective about
+what information must be retained during {@link android.app.Activity#onPause onPause()}, because any
+blocking procedures in this method block the transition to the next activity and slow the user
+experience.</p>
+
+<p> Methods that are marked "No" in the <b>Killable</b> column protect the process hosting the
+activity from being killed from the moment they are called.  Thus, an activity is killable
+from the time {@link android.app.Activity#onPause onPause()} returns to the time
+{@link android.app.Activity#onResume onResume()} is called. It will not again be killable until
+{@link android.app.Activity#onPause onPause()} is again called and returns. </p>
+
+<p class="note"><strong>Note:</strong> An activity that's not technically "killable" by this
+definition in table 1 might still be killed by the system&mdash;but that would happen only in
+extreme circumstances when there is no other recourse. When an activity might be killed is
+discussed more in the <a
+href="{@docRoot}guide/topics/fundamentals/processes-and-threading.html">Processes and
+Threading</a> document.</p>
+
+
+<h3 id="SavingActivityState">Saving activity state</h3>
+
+<p>The introduction to <a href="Lifecycle">Managing the Activity Lifecycle</a> briefly mentions that
+when an activity is paused or stopped, the state of the activity is retained. This is true because
+the {@link android.app.Activity} object is still held in memory when it is paused or
+stopped&mdash;all information about its members and current state is still alive. Thus, any changes
+the user made within the activity are retained in memory, so that when the activity returns to the
+foreground (when it "resumes"), those changes are still there.</p>
+
+<div class="figure" style="width:615px">
+<img src="{@docRoot}images/fundamentals/restore_instance.png" alt="" />
+<p class="img-caption"><strong>Figure 2.</strong> The two ways in which an activity returns to user
+focus with its state intact: either the activity is stopped, then resumed and the activity state
+remains intact (left), or the activity is destroyed, then recreated and the activity must restore
+the previous activity state (right).</p>
+</div>
+
+<p>However, when the system destroys an activity in order to recover memory, the {@link
+android.app.Activity} object is destroyed, so the system cannot simply resume it with its state
+intact. Instead, the system must recreate the {@link android.app.Activity} object if the user
+navigates back to it. Yet, the user is unaware
+that the system destroyed the activity and recreated it and, thus, probably
+expects the activity to be exactly as it was. In this situation, you can ensure that
+important information about the activity state is preserved by implementing an additional
+callback method that allows you to save information about the state of your activity and then
+restore it when the the system recreates the activity.</p>
+
+<p>The callback method in which you can save information about the current state of your activity is
+{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}. The system calls this method
+before making the activity vulnerable to being destroyed and passes it
+a {@link android.os.Bundle} object. The {@link android.os.Bundle} is where you can store 
+state information about the activity as name-value pairs, using methods such as {@link
+android.os.Bundle#putString putString()}. Then, if the system kills your activity's
+process and the user navigates back to your activity, the system passes the {@link
+android.os.Bundle} to {@link android.app.Activity#onCreate onCreate()} so you can restore the
+activity state you saved during {@link android.app.Activity#onSaveInstanceState
+onSaveInstanceState()}. If there is no state information to restore, then the {@link
+android.os.Bundle} passed to {@link android.app.Activity#onCreate onCreate()} is null.</p>
+
+<p class="note"><strong>Note:</strong> There's no guarantee that {@link
+android.app.Activity#onSaveInstanceState onSaveInstanceState()} will be called before your
+activity is destroyed, because there are cases in which it won't be necessary to save the state
+(such as when the user leaves your activity using the BACK key, because the user is explicitly
+closing the activity). If the method is called, it is always called before {@link
+android.app.Activity#onStop onStop()} and possibly before {@link android.app.Activity#onPause
+onPause()}.</p>
+
+<p>However, even if you do nothing and do not implement {@link
+android.app.Activity#onSaveInstanceState onSaveInstanceState()}, some of the activity state is
+restored by the {@link android.app.Activity} class's default implementation of {@link
+android.app.Activity#onSaveInstanceState onSaveInstanceState()}. Specifically, the default
+implementation calls {@link
+android.view.View#onSaveInstanceState onSaveInstanceState()} for every {@link android.view.View}
+in the layout, which allows each view to provide information about itself
+that should be saved. Almost every widget in the Android framework implements this method as
+appropriate, such that any visible changes to the UI are automatically saved and restored when your
+activity is recreated. For example, the {@link android.widget.EditText} widget saves any text
+entered by the user and the {@link android.widget.CheckBox} widget saves whether it's checked or
+not. The only work required by you is to provide a unique ID (with the <a
+href="{@docRoot}guide/topics/resources/layout-resource.html#idvalue">{@code android:id}</a>
+attribute) for each widget you want to save its state. If a widget does not have an ID, then it
+cannot save its state.</p>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<p>You can also explicitly stop a view in your layout from saving its state by setting the
+{@link android.R.attr#saveEnabled android:saveEnabled} attribute to {@code "false"} or by calling
+the {@link android.view.View#setSaveEnabled setSaveEnabled()} method. Usually, you should not
+disable this, but you might if you want to restore the state of the activity UI differently.</p>
+</div>
+</div>
+
+<p>Although the default implementation of {@link
+android.app.Activity#onSaveInstanceState onSaveInstanceState()} saves useful information about
+your activity's UI, you still might need to override it to save additional information.
+For example, you might need to save member values that changed during the activity's life (which
+might correlate to values restored in the UI, but the members that hold those UI values are not
+restored, by default).</p>
+
+<p>Because the default implementation of {@link
+android.app.Activity#onSaveInstanceState onSaveInstanceState()} helps save the state of the UI, if
+you override the method in order to save additional state information, you should always call the
+superclass implementation of {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}
+before doing any work.</p>
+
+<p class="note"><strong>Note:</strong> Because {@link android.app.Activity#onSaveInstanceState
+onSaveInstanceState()} is not guaranteed
+to be called, you should use it only to record the transient state of the activity (the state of
+the UI)&mdash;you should never use it to store persistent data.  Instead, you should use  {@link
+android.app.Activity#onPause onPause()} to store persistent data (such as data that should be saved
+to a database) when the user leaves the activity.</p>
+
+<p>A good way to test your application's ability to restore its state is to simply rotate the
+device so that the screen orientation changes. When the screen orientation changes, the system
+destroys and recreates the activity in order to apply alternative resources that might be available
+for the new orientation. For this reason alone, it's very important that your activity
+completely restores its state when it is recreated, because users regularly rotate the screen while
+using applications.</p>
+
+
+<h3 id="ConfigurationChanges">Handling configuration changes</h3>
+
+<p>Some device configurations can change during runtime (such as screen orientation, keyboard
+availability, and language). When such a change occurs, Android restarts the running Activity
+({@link android.app.Activity#onDestroy} is called, followed immediately by {@link
+android.app.Activity#onCreate onCreate()}). The restart behavior is
+designed to help your application adapt to new configurations by automatically reloading your
+application with alternative resources that you've provided. If you design your activity to
+properly handle this event, it will be more resilient to unexpected events in the activity
+lifecycle.</p>
+
+<p>The best way to handle a configuration change, such as a change in the screen orientation, is
+  to simply preserve the state of your application using {@link
+  android.app.Activity#onSaveInstanceState onSaveInstanceState()} and {@link
+android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} (or {@link
+android.app.Activity#onCreate onCreate()}), as discussed in the previous section.</p>
+
+<p>For a detailed discussion about configuration changes that happen at runtime and how you should
+handle them, read <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling
+Runtime Changes</a>.</p>
+
+
+
+<h3 id="CoordinatingActivities">Coordinating activities</h3>
+
+ <p>When one activity starts another, they both experience lifecycle transitions. The first activity
+pauses and stops (though, it won't stop if it's still visible in the background), while the other
+activity is created. In case these activities share data saved to disc or elsewhere, it's important
+to understand that the first activity is not completely stopped before the second one is created.
+Rather, the process of starting the second one overlaps with the process of stopping the first
+one.</p>
+
+<p>The order of lifecycle callbacks is well defined, particularly when the two activities are in the
+same process and one is starting the other. Here's the order of operations that occur when Activity
+A starts Acivity B: </p>
+
+<ol>
+<li>Activity A's {@link android.app.Activity#onPause onPause()} method executes.</li>
+
+<li>Activity B's {@link android.app.Activity#onCreate onCreate()}, {@link
+android.app.Activity#onStart onStart()}, and {@link android.app.Activity#onResume onResume()}
+methods execute in sequence. (Activity B now has user focus.)</li>
+
+<li>Then, if Activity A is no longer visible on screen, its {@link
+android.app.Activity#onStop onStop()} method executes.</li>
+</ol>
+
+ <p>This predictable sequence of lifecycle callbacks allows you to manage the transition of
+information from one activity to another. For example, if you must write to a database when the
+first activity stops so that the following activity can read it, then you should write to the
+database during {@link android.app.Activity#onPause onPause()} instead of during {@link
+android.app.Activity#onStop onStop()}.</p>
+
+
+<h2>Beginner's Path</h2>
+
+<p>For more information about how Android maintains a history of activities and
+enables user multitasking, continue with the <b><a
+href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
+Stack</a></b> document.</p>
diff --git a/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd b/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd
new file mode 100644
index 0000000..47dc5471
--- /dev/null
+++ b/docs/html/guide/topics/fundamentals/tasks-and-back-stack.jd
@@ -0,0 +1,568 @@
+page.title=Tasks and Back Stack
+parent.title=Application Fundamentals
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>Quickview</h2>
+<ul>
+  <li>All activities belong to a task</li>
+  <li>A task contains a collection of activities in the order in which the user interacts with
+them</li>
+  <li>Tasks can move to the background and retain the state of each activity in order for the user
+to perform other tasks without loosing their work</li>
+</ul>
+
+<h2>In this document</h2>
+<ol>
+<li><a href="#ActivityState">Saving Activity State</a></li></li>
+<li><a href="#ManagingTasks">Managing Tasks</a>
+  <ol>
+    <li><a href="#TaskLaunchModes">Defining launch modes</a></li>
+    <li><a href="#Affinities">Handling affinities</a></li>
+    <li><a href="#Clearing">Clearing the back stack</a></li>
+    <li><a href="#Starting">Starting a task</a></li>
+  </ol>
+</li>
+</ol>
+
+<h2>See also</h2>
+<ol>
+  <li><a><a href="{@docRoot}videos/index.html#v=fL6gSd4ugSI">Application Lifecycle video</a></li>
+  <li><a
+href="{@docRoot}guide/topics/manifest/activity-element.html">{@code &lt;activity&gt;} manifest
+element</a></li>
+</ol>
+</div>
+</div>
+
+
+<p>An application usually contains multiple <a
+href="{@docRoot}guide/topics/fundamentals/activities.html">activities</a>. Each activity
+should be designed around a specific kind of action the user can perform and can start other
+activities. For example, an email application might have one activity to show a list of new email.
+When the user selects an email, a new activity opens to view that email.</p>
+
+<p>An activity can even start activities that exist in other applications on the device. For
+example, if your application wants to send an email, you can define an intent to perform a "send"
+action and include some data, such as an email address and a message. An activity from another
+application that declares itself to handle this kind of intent then opens. In this case, the intent
+is to send an email, so an email application's "compose" activity starts (if multiple activities
+support the same intent, then the system lets the user select which one to use). When the email is
+sent, your activity resumes and it seems as if the email activity was part of your application. Even
+though the activities may be from different applications, Android maintains this seamless user
+experience by keeping both activities in the same <em>task</em>.</p>
+
+<p>A task is a collection of activities that users interact with
+when performing a certain job. The activities are arranged in a stack (the "back stack"), in the
+order in which each activity is opened.</p>
+
+<!-- SAVE FOR WHEN THE FRAGMENT DOC IS ADDED
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h3>Adding fragments to a task's back stack</h3>
+
+<p>Your activity can also include {@link android.app.Fragment}s to the back stack. For example,
+suppose you have a two-pane layout using fragments, one of which is a list view (fragment A) and the
+other being a layout to display an item from the list (fragment B). When the user selects an item
+from the list, fragment B is replaced by a new fragment (fragment C). In this case, it might be
+desireable for the user to navigate back to reveal fragment B, using the BACK key.</p>
+<p>In order to add fragment B to the back stack so that this is possible, you must call {@link
+android.app.FragmentTransaction#addToBackStack addToBackStack()} before you {@link
+android.app.FragmentTransaction#commit()} the transaction that replaces fragment B with fragment
+C.</p>
+<p>For more information about using fragments and adding them to the back stack, see the {@link
+android.app.Fragment} class documentation.</p>
+
+</div>
+</div>
+-->
+
+<p>The device Home screen is the starting place for most tasks. When the user touches an icon in the
+application
+launcher (or a shortcut on the Home screen), that application's task comes to the foreground. If no
+task exists for the application (the application has not been used recently), then a new task
+is created and the "main" activity for that application opens as the root activity in the stack.</p>
+
+<p>When the current activity starts another, the new activity is pushed on the top of the stack and
+takes focus. The previous activity remains in the stack, but is stopped. When an activity
+stops, the system retains the current state of its user interface. When the user presses the BACK
+key, the current activity is popped from the top of the stack (the activity is destroyed) and the
+previous activity resumes (the previous state of its UI is restored). Activities in the stack are
+never rearranged, only pushed and popped from the stack&mdash;pushed onto the stack when started by
+the current activity and popped off when the user leaves it using the BACK key. As such, the back
+stack operates as a "last in, first out" object structure. Figure 1 visualizes
+this behavior with a timeline showing the progress between activities along with the current back
+stack at each point in time.</p>
+
+<img src="{@docRoot}images/fundamentals/diagram_backstack.png" alt="" />
+<p class="img-caption"><strong>Figure 1.</strong> A representation of how each new activity in a
+task adds an item to the back stack. When the user presses the BACK key, the current activity is
+destroyed and the previous activity resumes.</p>
+
+
+<p>If the user continues to press BACK, then each activity in the stack is popped off to reveal the
+previous one, until the user returns to the Home screen (or to whichever activity was running when
+the task began). When all activities are removed from the stack, the task no longer exists.</p>
+
+<div class="figure" style="width:369px">
+<img src="{@docRoot}images/fundamentals/diagram_multitasking.png" alt="" /> <p
+class="img-caption"><strong>Figure 2.</strong> Two tasks: Task A is in the background, waiting
+to be resumed, while Task B receives user interaction in the foreground.</p>
+</div>
+<div class="figure" style="width:178px">
+  <img src="{@docRoot}images/fundamentals/diagram_multiple_instances.png" alt="" /> <p
+class="img-caption"><strong>Figure 3.</strong> A single activity is instantiated multiple times.</p>
+</div>
+
+<p>A task is a cohesive unit that can move to the "background" when users begin a new task or go
+to the Home screen, via the HOME key. While in the background, all the activities in the task are
+stopped, but the back stack for the task remains intact&mdash;the task has simply lost focus while
+another task takes place, as shown in figure 2. A task can then return to the "foreground" so users
+can pick up where they left off. Suppose, for example, that the current task (Task A) has three
+activities in its stack&mdash;two under the current activity. The user presses the HOME key, then
+starts a new application from the application launcher. When the Home screen appears, Task A goes
+into the background. When the new application starts, the system starts a task for that application
+(Task B) with its own stack of activities. After interacting with
+that application, the user returns Home again and selects the application that originally
+started Task A. Now, Task A comes to the
+foreground&mdash;all three activities in its stack are intact and the activity at the top of the
+stack resumes. At
+this point, the user can also switch back to Task B by going Home and selecting the application icon
+that started that task (or by touching and holding the HOME key to reveal recent tasks and selecting
+one). This is an example of multitasking on Android.</p>
+
+<p class="note"><strong>Note:</strong> Multiple tasks can be held in the background at once.
+However, if the user is running many background tasks at the same time, the system might begin
+destroying background activities in order to recover memory, causing the activity states to be lost.
+See the following section about <a href="#ActivityState">Activity state</a>.</p>
+
+<p>Because the activities in the back stack are never rearranged, if your application allows
+users to start a particular activity from more than one activity, a new instance of
+that activity is created and popped onto the stack (rather than bringing any previous instance of
+the activity to the top). As such, one activity in your application might be instantiated multiple
+times (even from different tasks), as shown in figure 3. As such, if the user navigates backward
+using the BACK key, each instance of the activity is revealed in the order they were opened (each
+with their own UI state). However, you can modify this behavior if you do not want an activity to be
+instantiated more than once. How to do so is discussed in the later section about <a
+href="#ManagingTasks">Managing Tasks</a>.</p>
+
+
+<p>To summarize the default behavior for activities and tasks:</p>
+
+<ul>
+  <li>When Activity A starts Activity B, Activity A is stopped, but the system retains its state
+(such as scroll position and text entered into forms).
+If the user presses the BACK key while in Activity B, Activity A resumes with its state
+restored.</li>
+  <li>When the user leaves a task by pressing the HOME key, the current activity is stopped and
+its task goes into the background. The system retains the state of every activity in the task. If
+the user later resumes the task by selecting the launcher icon that began the task, the task comes
+to the foreground and resumes the activity at the top of the stack.</li>
+  <li>If the user presses the BACK key, the current activity is popped from the stack and
+destroyed. The previous activity in the stack is resumed. When an activity is destroyed, the system
+<em>does not</em> retain the activity's state.</li>
+  <li>Activities can be instantiated multiple times, even from other tasks.</li>
+</ul>
+
+
+<h2 id="ActivityState">Saving Activity State</h2>
+
+<p>As discussed above, the system's default behavior preserves the state of an activity when it is
+stopped. This way, when users navigate back to a previous activity, its user interface appears
+the way they left it. However, you can&mdash;and <strong>should</strong>&mdash;proactively retain
+the state of your activities using callback methods, in case the activity is destroyed and must
+be recreated.</p>
+
+<p>When the system stops one of your activities (such as when a new activity starts or the task
+moves to the background), the system might destroy that activity completely if it needs to recover
+system memory. When this happens, information about the activity state is lost. If this happens, the
+system still
+knows that the activity has a place in the back stack, but when the activity is brought to the
+top of the stack the system must recreate it (rather than resume it). In order to
+avoid loosing the user's work, you should proactively retain it by implementing the {@link
+android.app.Activity#onSaveInstanceState onSaveInstanceState()} callback
+methods in your activity.</p>
+
+<p>For more information about how to save your activity state, see the <a
+href="{@docRoot}guide/topics/fundamentals/activities.html#SavingActivityState">Activities</a>
+document.</p>
+
+
+
+<h2 id="ManagingTasks">Managing Tasks</h2>
+
+<p>The way Android manages tasks and the back stack, as described above&mdash;by placing all
+activities started in succession in the same task and in a "last in, first out" stack&mdash;works
+great for most applications and you shouldn't have to worry about how your activities are associated
+with tasks or how they exist in the back stack. However, you might decide that you want to interrupt
+the normal behavior. Perhaps you want an activity in your application to begin a new task when it is
+started (instead of being placed within the current task); or, when you start an activity, you want
+to bring forward an existing instance of it (instead of creating a new
+instance on top of the back stack); or, you want your back stack to be cleared of all
+activitiesstart an activity except for the root activity when the user leaves the task.</p>
+
+<p>You can do these things and more, with attributes in the
+<a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code
+&lt;activity&gt;}</a> manifest element and with flags in the intent that you pass to {@link
+android.app.Activity#startActivity startActivity()}.</p>
+
+<p>In this regard, the the principal <a
+href="{@docRoot}guide/topics/manifest/activity-element.html">{@code &lt;activity&gt;}</a>
+attributes you can use are:</p>
+
+<ul class="nolist">
+  <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code
+taskAffinity}</a></li>
+  <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code
+launchMode}</a></li>
+  <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#reparent">{@code
+allowTaskReparenting}</a></li>
+  <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#clear">{@code
+clearTaskOnLaunch}</a></li>
+  <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#always">{@code
+alwaysRetainTaskState}</a></li>
+  <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#finish">{@code
+finishOnTaskLaunch}</a></li>
+</ul>
+
+<p>And the principal intent flags you can use are:</p>
+
+<ul class="nolist">
+  <li>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</li>
+  <li>{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}</li>
+  <li>{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}</li>
+</ul>
+
+<p>In the following sections, you'll see how you can use these manifest attributes and intent
+flags to define how activities are associated with tasks and how the behave in the back stack.</p>
+
+
+<p class="caution"><strong>Caution:</strong> Most applications should not interrupt the default
+behavior for activities and tasks. If you determine that it's necessary for your activity to modify
+the default behaviors, use caution and be sure to test the usability of the activity during
+launch and when navigating back to it from other activities and tasks with the BACK key. Be sure 
+to test for navigation behaviors that might conflict with the user's expected behavior.</p>
+
+
+<h3 id="TaskLaunchModes">Defining launch modes</h3>
+
+<p>Launch modes allow you to define how a new instance of an activity is associated with the
+current task. You can define different launch modes in two ways:</p>
+<ul class="nolist">
+  <li><a href="#ManifestForTasks">Using the manifest file</a>
+    <p>When you declare an activity in your manifest file, you can specify how the activity
+should associate with tasks when it starts.</li>
+  <li><a href="#IntentFlagsForTasks">Using Intent flags</a>
+    <p>When you call {@link android.app.Activity#startActivity startActivity()},
+you can include a flag in the {@link android.content.Intent} that declares how (or
+whether) the new activity should associate with the current task.</p></li>
+</ul>
+
+<p>As such, if Activity A starts Activity B, Activity B can define in its manifest how it
+should associate with the current task (if at all) and Activity A can also request how Activity
+B should associate with current task. If both activities define how Activity B
+should associate with a task, then Activity A's request (as defined in the intent) is honored
+over Activity B's request (as defined in its manifest).</p>
+
+<p class="note"><strong>Note:</strong> Some the launch modes available in the manifest
+are not available as flags for an intent and, likewise, some launch modes available as flags
+for an intent cannot be defined in the manifest.</p>
+
+
+<h4 id="ManifestForTasks">Using the manifest file</h4>
+
+<p>When declaring an activity in your manifest file, you can specify how the activity should
+associate with a task using the <a
+href="{@docRoot}guide/topics/manifest/activity-element.html">{@code &lt;activity&gt;}</a>
+element's <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code
+launchMode}</a> attribute.</p>
+
+<p>The <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code
+launchMode}</a> attribute specifies an instruction on how the activity should be launched into a
+task. There are four different launch modes you can assign to the
+<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">launchMode</a></code>
+attribute:</p>
+
+<dl>
+<dt>{@code "standard"} (the default mode)</dt>
+  <dd>Default. The system creates a new instance of the activity in the task from
+which it was started and routes the intent to it. The activity can be instantiated multiple times,
+each instance can belong to different tasks, and one task can have multiple instances.</dd>
+<dt>{@code "singleTop"}</dt>
+  <dd>If an instance of the activity already exists at the top of the current task, the system
+routes the intent to that instance through a call to its {@link
+android.app.Activity#onNewIntent onNewIntent()} method, rather than creating a new instance of the
+activity. The activity can be instantiated multiple times, each instance can
+belong to different tasks, and one task can have multiple instances (but only if the the
+activity at the top of the back stack is <em>not</em> an existing instance of the activity).
+  <p>For example, suppose a task's back stack consists of root activity A with activities B, C,
+and D on top (the stack is A-B-C-D; D is on top). An intent arrives for an activity of type D.
+If D has the default {@code "standard"} launch mode, a new instance of the class is launched and the
+stack becomes A-B-C-D-D. However, if D's launch mode is {@code "singleTop"}, the existing instance
+of D is deliverd the intent through {@link
+android.app.Activity#onNewIntent onNewIntent()}, because it's at the top of the stack&mdash;the
+stack remains A-B-C-D. However, if an intent arrives for an activity of type B, then a new
+instance of B is added to the stack, even if its launch mode is {@code "singleTop"}.</p>
+  <p class="note"><strong>Note:</strong> When a new instance of an activity is created,
+the user can press the BACK key to return to the previous activity. But when an existing instance of
+an activity handles a new intent, the user cannot press the BACK key to return to the state of
+the activity before the new intent arrived in {@link android.app.Activity#onNewIntent
+onNewIntent()}.</p>
+</dd>
+
+<dt>{@code "singleTask"}</dt>
+  <dd>The system creates a new task and instantiates the activity at the root of the new task.
+However, if an instance of the activity already exists in a separate task, the system routes the
+intent to the existing instance through a call to its {@link
+android.app.Activity#onNewIntent onNewIntent()} method, rather than creating a new instance. Only
+one instance of the activity can exist at a time.
+  <p class="note"><strong>Note:</strong> Although the activity starts in a new task, the
+BACK key still returns the user to the previous activity.</p></dd>
+<dt>{@code "singleInstance"}.</dt>
+  <dd>Same as {@code "singleTask"}, except that the system doesn't launch any other activities into
+the task holding the instance. The activity is always the single and only member of its task;
+any activities started by this one open in a separate task.</dd>
+</dl>
+
+
+<p>As another example, the Android Browser application declares that the web browser activity should
+always open in its own task&mdash;by specifying the {@code singleTask} launch mode in the <a
+href="{@docRoot}guide/topics/manifest/activity-element.html">{@code &lt;activity&gt;}</a> element.
+This means that if your application issues an
+intent to open the Android Browser, its activity is <em>not</em> placed in the same
+task as your application. Instead, either a new task starts for the Browser or, if the Browser
+already has a task running in the background, that task is brought forward to handle the new
+intent.</p>
+
+<p>Regardless of whether an activity starts in a new task or in the same task as the activity that
+started it, the BACK key always takes the user to the previous activity. However, if you
+start an activity from your task (Task A) that specifies the {@code singleTask} launch mode, then
+that activity might have an instance in the background that belongs to a task with its own back
+stack (Task B). In this
+case, when Task B is brought forward to handle a new intent, the BACK key first navigates
+backward through the activities in Task B before returning to
+the top-most activity in Task A. Figure 4 visualizes this type of scenario.</p>
+
+<img src="{@docRoot}images/fundamentals/diagram_backstack_singletask_multiactivity.png" alt="" />
+<p class="img-caption"><strong>Figure 4.</strong> A representation of how an activity with
+launch mode "singleTask" is added to the back stack. If the activity is already a part of a
+background task with its own back stack (Task B), then the entire back stack also comes
+forward, on top of the current task (Task A).</p>
+
+<p>For more information about using launch modes in the manifest file, see the
+<code><a href="{@docRoot}guide/topics/manifest/activity-element.html">&lt;activity&gt;</a></code>
+element documentation, where the {@code launchMode} attribute and the accepted values are
+discussed more.</p>
+
+<p class="note"><strong>Note:</strong> The behaviors that you specify for your activity with the <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> attribute
+can be overriden by flags included with the intent that start your activity, as discussed in the
+next section.</p>
+
+
+
+<h4 id="#IntentFlagsForTasks">Using Intent flags</h4>
+
+<p>When starting an activity, you can modify the default association of an activity to its task
+by including flags in the intent that you deliver to {@link
+android.app.Activity#startActivity startActivity()}. The flags you can use to modify the
+default behavior are:</p>
+
+<p>
+  <dt>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</dt>
+    <dd>Start the activity in a new task. If a task is already running for the activity you are now
+starting, that task is brought to the foreground with its last state restored and the activity
+receives the new intent in {@link android.app.Activity#onNewIntent onNewIntent()}. 
+    <p>This produces the same behavior as the {@code "singleTask"} <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> value,
+discussed in the previous section.</p></dd>
+  <dt>{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}</dt>
+    <dd>If the activity being started is the current activity (at the top of the back stack), then
+the existing instance receives a call to {@link android.app.Activity#onNewIntent onNewIntent()},
+instead of creating a new instance of the activity.
+    <p>This produces the same behavior as the {@code "singleTop"} <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> value,
+discussed in the previous section.</p></dd>
+  <dt>{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}</dt>
+    <dd>If the activity being started is already running in the current task, then instead
+of launching a new instance of that activity, all of the other activities on top of it are
+destroyed and this intent is delivered to the resumed instance of the activity (now on top),
+through {@link android.app.Activity#onNewIntent onNewIntent()}).
+    <p>There is no value for the <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a>
+attribute that produces this behavior.</p>
+    <p>{@code FLAG_ACTIVITY_CLEAR_TOP} is most often used in conjunction with {@code
+FLAG_ACTIVITY_NEW_TASK}.  When used together, these flags are a way of locating an existing activity
+in another task and putting it in a position where it can respond to the intent. </p>
+    <p class="note"><strong>Note:</strong> If the launch mode of the designated activity is {@code
+"standard"}, it too is removed from the stack and a new instance is launched in its place to handle
+the incoming intent.  That's because a new instance is always created for a new intent when the
+launch mode is {@code "standard"}. </p>
+</dd>
+</dl>
+
+
+
+
+
+<h3 id="Affinities">Handling affinities</h3>
+
+<p>The <em>affinity</em> indicates which task an activity prefers to belong to. By default, all the
+activities from the same application have an affinity for each other. So, by default, all
+activities in the same application prefer to be in the same task. However, you can modify
+the default affinity for an activity. Activities defined in
+different applications can share an affinity, or activities defined in the same application can be
+assigned different task affinities.</p>
+
+<p>You can modify the affinity for any given activity with the <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> attribute
+of the <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code &lt;activity&gt;}</a>
+element.</p>
+
+<p>The <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a>
+attribute takes a string value, which must be unique from the default package name
+declared in the <a href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code
+&lt;manifest&gt;}</a> element, because the system uses that name to identify the default task
+affinity for the application.</p>
+
+<p>The affinity comes into play in two circumstances:</p>
+<ul>
+  <li>When the intent that launches an activity contains the {@link
+android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag.
+
+<p>A new activity is, by default, launched into the task of the activity
+that called {@link android.app.Activity#startActivity startActivity()}. It's pushed onto the same
+back stack as the caller.  However, if the intent passed to {@link
+android.app.Activity#startActivity startActivity()} contains the {@link
+android.content.Intent#FLAG_ACTIVITY_NEW_TASK}
+flag, the system looks for a different task to house the new activity. Often, it's a new task. 
+However, it doesn't have to be.  If there's already an existing task with the same affinity as the
+new activity, the activity is launched into that task.  If not, it begins a new task.</p>
+
+<p>If this flag causes an activity to begin a new task and the user presses the HOME key to leave
+it, there must be some way for the user to navigate back to the task. Some entities (such as the
+notification manager) always start activities in an external task, never as part of their own, so
+they always put {@code FLAG_ACTIVITY_NEW_TASK} in the intents they pass to {@link
+android.app.Activity#startActivity startActivity()}.  If you have an activity that can be invoked by
+an external entity that might use this flag, take care that the user has a independent way to get
+back to the task that's started, such as with a launcher icon (the root activity of the task
+has a {@link android.content.Intent#CATEGORY_LAUNCHER} intent filter; see the <a
+href="#Starting">Starting a task</a> section below).</p>
+</li>
+
+  <li>When an activity has its <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#reparent">{@code
+allowTaskReparenting}</a> attribute set to {@code "true"}.
+  <p>In this case, the activity can move from the task it starts to the task it has an affinity
+for, when that task comes to the foreground.</p>
+  <p>For example, suppose that an activity that reports weather conditions in selected cities is
+defined as part of a travel application.  It has the same affinity as other activities in the same
+application (the default application affinity) and it allows re-parenting with this attribute.
+When one of your activities starts the weather reporter activity, it initially belongs to the same
+task as your activity. However, when the travel application's task comes to the foreground, the
+weather reporter activity is reassigned to that task and displayed within it.</p>
+</li>
+</ul>
+
+<p class="note"><strong>Tip:</strong> If an {@code .apk} file contains more than one "application"
+from the user's point of view, you probably want to use the <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a>
+attribute to assign different affinities to the activities associated with each "application".</p>
+
+
+
+<h3 id="Clearing">Clearing the back stack</h3>
+
+<p>If the user leaves a task for a long time, the system clears the task of all activities except
+the root activity.  When the user returns to the task again, only the root activity is restored.
+The system behaves this way, because, after an extended amount of time, users likely have abandoned
+what they were doing before and are returning to the task to begin something new. </p>
+
+<p>There are some activity attributes that you can use to modify this behavior: </p>
+
+<dl>
+<dt><code><a
+href="{@docRoot}guide/topics/manifest/activity-element.html#always">alwaysRetainTaskState</a></code>
+</dt>
+<dd>If this attribute is set to {@code "true"} in the root activity of a task,
+the default behavior just described does not happen.
+The task retains all activities in its stack even after a long period.</dd>
+
+<dt><code><a
+href="{@docRoot}guide/topics/manifest/activity-element.html#clear">clearTaskOnLaunch</a></code></dt>
+<dd>If this attribute is set to {@code "true"} in the root activity of a task,
+the stack is cleared down to the root activity whenever the user leaves the task
+and returns to it.  In other words, it's the opposite of <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#always">{@code
+alwaysRetainTaskState}</a>.  The user always returns to the task in its
+initial state, even after a leaving the task for only a moment.</dd>
+
+<dt><code><a
+href="{@docRoot}guide/topics/manifest/activity-element.html#finish">finishOnTaskLaunch</a></code>
+</dt>
+<dd>This attribute is like <a
+href="{@docRoot}guide/topics/manifest/activity-element.html#clear">{@code clearTaskOnLaunch}</a>,
+but it operates on a
+single activity, not an entire task.  It can also cause any activity to go
+away, including the root activity.  When it's set to {@code "true"}, the
+activity remains part of the task only for the current session.  If the user
+leaves and then returns to the task, it is no longer present.</dd>
+</dl>
+
+
+
+
+<h3 id="Starting">Starting a task</h3>
+
+<p>You can set up an activity as the entry point for a task by giving it an intent filter with
+{@code "android.intent.action.MAIN"} as the specified action and {@code
+"android.intent.category.LAUNCHER"} as the specified category. For example:</p>
+
+<pre>
+&lt;activity ... &gt;
+    &lt;intent-filter ... &gt;
+        &lt;action android:name="android.intent.action.MAIN" /&gt;
+        &lt;category android:name="android.intent.category.LAUNCHER" /&gt;
+    &lt;/intent-filter&gt;
+    ...
+&lt;/activity&gt;
+</pre>
+
+<p>An intent filter of this kind causes an icon and label for the
+activity to be displayed in the application launcher, giving users a way to launch the activity and
+to return to the task that it creates any time after it has been launched.
+</p>
+
+<p>This second ability is important: Users must be able to leave a task and then come back to it
+later using this activity launcher.  For this reason, the two <a href="#LaunchModes">launch
+modes</a> that mark activities as always initiating a task, {@code "singleTask"} and "{@code
+"singleInstance"}, should be used only when the activity has an {@link
+android.content.Intent#ACTION_MAIN}
+and a {@link android.content.Intent#CATEGORY_LAUNCHER}
+filter. Imagine, for example, what could happen if the filter is missing: An intent launches a
+{@code "singleTask"} activity, initiating a new task, and the user spends some time working in
+that task.  The user then presses the HOME key. The task is now sent to the background and not
+visible. Because it is not represented in the application launcher, the user has no way to return to
+the task.
+</p>
+
+<p>For those cases where you don't want the user to be able to return to an activity, set the
+  <code><a
+href="{@docRoot}guide/topics/manifest/activity-element.html">&lt;activity&gt;</a></code> element's
+<a href="{@docRoot}guide/topics/manifest/activity-element.html#finish">{@code
+finishOnTaskLaunch}</a> to {@code "true"} (see <a
+href="#Clearing">Clearing the stack</a>).</p>
+
+
+
+
+<h2>Beginner's Path</h2>
+
+<p>For more information about how to use intents to
+activate other application components and publish the intents to which your components
+respond, continue with the <b><a
+href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent
+Filters</a></b> document.</p>
diff --git a/docs/html/guide/topics/resources/string-resource.jd b/docs/html/guide/topics/resources/string-resource.jd
index 81c5d55..2db38f1 100644
--- a/docs/html/guide/topics/resources/string-resource.jd
+++ b/docs/html/guide/topics/resources/string-resource.jd
@@ -12,8 +12,8 @@
     <dd>XML resource that provides a single string.</dd>
   <dt><a href="#StringArray">String Array</a></dt>
     <dd>XML resource that provides an array of strings.</dd>
-  <dt><a href="#Plurals">Plurals</a></dt>
-    <dd>XML resource that carries different strings for different pluralizations
+  <dt><a href="#Plurals">Quantity Strings (Plurals)</a></dt>
+    <dd>XML resource that carries different strings for different quantities
     of the same word or phrase.</dd>
 </dl>
 
@@ -218,13 +218,30 @@
 
 
 
-<h2 id="Plurals">Plurals</h2>
+<h2 id="Plurals">Quantity Strings (Plurals)</h2>
 
-<p>A pair of strings that each provide a different plural form of the same word or phrase,
-which you can collectively reference from the application. When you request the plurals
-resource using a method such as {@link android.content.res.Resources#getQuantityString(int,int)
-getQuantityString()}, you must pass a "count", which will determine the plural form you
-require and return that string to you.</p>
+<p>Different languages have different rules for grammatical agreement with quantity. In English,
+for example, the quantity 1 is a special case. We write "1 book", but for any other quantity we'd
+write "<i>n</i> books". This distinction between singular and plural is very common, but other
+languages make finer distinctions. The full set supported by Android is <code>zero</code>,
+<code>one</code>, <code>two</code>, <code>few</code>, <code>many</code>, and <code>other</code>.
+
+<p>The rules for deciding which case to use for a given language and quantity can be very complex,
+so Android provides you with methods such as
+{@link android.content.res.Resources#getQuantityString(int,int) getQuantityString()} to select
+the appropriate resource for you.
+
+<p>Note that the selection is made based on grammatical necessity. A string for <code>zero</code>
+in English will be ignored even if the quantity is 0, because 0 isn't grammatically different
+from 2, or any other number except 1 ("zero books", "one book", "two books", et cetera).
+Don't be misled either by the fact that, say, <code>two</code> sounds like it could only apply to
+the quantity 2: a language may require that 2, 12, 102 (et cetera) are all treated like one
+another but differently to other quantities. Rely on your translator to know what distinctions
+their language actually insists upon.
+
+<p>It's often possible to avoid quantity strings by using quantity-neutral formulations such as
+"Books: 1". This will make your life and your translators' lives easier, if it's a style that's
+in keeping with your application.
 
 <p class="note"><strong>Note:</strong> A plurals collection is a simple resource that is
 referenced using the value provided in the {@code name} attribute (not the name of the XML
@@ -251,7 +268,7 @@
     &lt;<a href="#plurals-element">plurals</a>
         name="<em>plural_name</em>">
         &lt;<a href="#plurals-item-element">item</a>
-            quantity=["one" | "other"]
+            quantity=["zero" | "one" | "two" | "few" | "many" | "other"]
             &gt;<em>text_string</em>&lt;/item>
     &lt;/plurals>
 &lt;/resources>
@@ -285,16 +302,27 @@
       <p class="caps">attributes:</p>
       <dl class="atn-list">
         <dt><code>quantity</code></dt>
-        <dd><em>Keyword</em>. A value indicating the case in which this string should be used. Valid
-values:
+        <dd><em>Keyword</em>. A value indicating when this string should be used. Valid
+values, with non-exhaustive examples in parentheses:
           <table>
             <tr><th>Value</th><th>Description</th></tr>
             <tr>
-              <td>{@code one}</td><td>When there is one (a singular string).</td>
+              <td>{@code zero}</td><td>When the language requires special treatment of the number 0 (as in Arabic).</td>
             </tr>
             <tr>
-              <td>{@code other}</td><td>When the quantity is anything other than one (a plural
-string, but also used when the count is zero).</td>
+              <td>{@code one}</td><td>When the language requires special treatment of numbers like one (as with the number 1 in English and most other languages; in Russian, any number ending in 1 but not ending in 11 is in this class).</td>
+            </tr>
+            <tr>
+              <td>{@code two}</td><td>When the language requires special treatment of numbers like two (as in Welsh).</td>
+            </tr>
+            <tr>
+              <td>{@code few}</td><td>When the language requires special treatment of "small" numbers (as with 2, 3, and 4 in Czech; or numbers ending 2, 3, or 4 but not 12, 13, or 14 in Polish).</td>
+            </tr>
+            <tr>
+              <td>{@code many}</td><td>When the language requires special treatment of "large" numbers (as with numbers ending 11-99 in Maltese).</td>
+            </tr>
+            <tr>
+              <td>{@code other}</td><td>When the language does not require special treatment of the given quantity.</td>
             </tr>
           </table>
         </dd>
@@ -315,6 +343,17 @@
     &lt;/plurals>
 &lt;/resources>
 </pre>
+    <p>XML file saved at {@code res/values-pl/strings.xml}:</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;resources>
+    &lt;plurals name="numberOfSongsAvailable">
+        &lt;item quantity="one">Znaleziono jedn&#x0105; piosenk&#x0119;.&lt;/item>
+        &lt;item quantity="few">Znaleziono %d piosenki.&lt;/item>
+        &lt;item quantity="other">Znaleziono %d piosenek.&lt;/item>
+    &lt;/plurals>
+&lt;/resources>
+</pre>
     <p>Java code:</p>
 <pre>
 int count = getNumberOfsongsAvailable();
diff --git a/docs/html/guide/topics/ui/dialogs.jd b/docs/html/guide/topics/ui/dialogs.jd
index 1a997f9..879eb8b 100644
--- a/docs/html/guide/topics/ui/dialogs.jd
+++ b/docs/html/guide/topics/ui/dialogs.jd
@@ -473,18 +473,25 @@
             progressDialog = new ProgressDialog(NotificationTest.this);
             progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
             progressDialog.setMessage("Loading...");
-            progressThread = new ProgressThread(handler);
-            progressThread.start();
             return progressDialog;
         default:
             return null;
         }
     }
 
+    &#64;Override
+    protected void onPrepareDialog(int id, Dialog dialog) {
+        switch(id) {
+        case PROGRESS_DIALOG:
+            progressDialog.setProgress(0);
+            progressThread = new ProgressThread(handler);
+            progressThread.start();
+    }
+
     // Define the Handler that receives messages from the thread and update the progress
     final Handler handler = new Handler() {
         public void handleMessage(Message msg) {
-            int total = msg.getData().getInt("total");
+            int total = msg.arg1;
             progressDialog.setProgress(total);
             if (total >= 100){
                 dismissDialog(PROGRESS_DIALOG);
@@ -515,9 +522,7 @@
                     Log.e("ERROR", "Thread Interrupted");
                 }
                 Message msg = mHandler.obtainMessage();
-                Bundle b = new Bundle();
-                b.putInt("total", total);
-                msg.setData(b);
+                msg.arg1 = total;
                 mHandler.sendMessage(msg);
                 total++;
             }
diff --git a/docs/html/guide/tutorials/notepad/notepad-ex2.jd b/docs/html/guide/tutorials/notepad/notepad-ex2.jd
index a945a62..854731f 100644
--- a/docs/html/guide/tutorials/notepad/notepad-ex2.jd
+++ b/docs/html/guide/tutorials/notepad/notepad-ex2.jd
@@ -87,8 +87,8 @@
     menu callback used for the options menu. Here, we add just one line, which will add a menu item
     to delete a note. Call <code>menu.add()</code> like so:
       <pre>
-public boolean onCreateContextMenu(Menu menu, View v
-        ContextMenuInfo menuInfo) {
+public void onCreateContextMenu(Menu menu, View v,
+        ContextMenu.ContextMenuInfo menuInfo) {
     super.onCreateContextMenu(menu, v, menuInfo);
     menu.add(0, DELETE_ID, 0, R.string.menu_delete);
 }</pre>
diff --git a/docs/html/images/fundamentals/diagram_backstack.png b/docs/html/images/fundamentals/diagram_backstack.png
new file mode 100644
index 0000000..2c6e33f
--- /dev/null
+++ b/docs/html/images/fundamentals/diagram_backstack.png
Binary files differ
diff --git a/docs/html/images/fundamentals/diagram_backstack_singletask_multiactivity.png b/docs/html/images/fundamentals/diagram_backstack_singletask_multiactivity.png
new file mode 100644
index 0000000..d6a21d7
--- /dev/null
+++ b/docs/html/images/fundamentals/diagram_backstack_singletask_multiactivity.png
Binary files differ
diff --git a/docs/html/images/fundamentals/diagram_multiple_instances.png b/docs/html/images/fundamentals/diagram_multiple_instances.png
new file mode 100644
index 0000000..380e7788
--- /dev/null
+++ b/docs/html/images/fundamentals/diagram_multiple_instances.png
Binary files differ
diff --git a/docs/html/images/fundamentals/diagram_multitasking.png b/docs/html/images/fundamentals/diagram_multitasking.png
new file mode 100644
index 0000000..b8c7b45
--- /dev/null
+++ b/docs/html/images/fundamentals/diagram_multitasking.png
Binary files differ
diff --git a/docs/html/images/fundamentals/restore_instance.png b/docs/html/images/fundamentals/restore_instance.png
new file mode 100644
index 0000000..fa428a7
--- /dev/null
+++ b/docs/html/images/fundamentals/restore_instance.png
Binary files differ
diff --git a/docs/html/resources/browser.jd b/docs/html/resources/browser.jd
new file mode 100644
index 0000000..8a08769
--- /dev/null
+++ b/docs/html/resources/browser.jd
@@ -0,0 +1,49 @@
+page.title=Technical Resources
+@jd:body
+
+<style type="text/css">
+  {@literal @import} "{@docRoot}assets/android-developer-resource-browser.css";
+</style>
+
+<script type="text/javascript" src="{@docRoot}assets/android-developer-resource-browser.js"></script>
+<script type="text/javascript" src="{@docRoot}assets/microtemplate.js"></script>
+
+<div>
+  <p style="display: none; float: right">Filter: <input id="resource-browser-keyword-filter"/></p>
+  <p id="resource-browser-search-params">Showing all technical resources:</p>
+</div>
+
+<noscript>
+  <p class="note"><strong>Error:</strong>
+    You must have JavaScript enabled to view this page. Resources are also
+    available offline in the SDK.
+  </p>
+</noscript>
+
+<div id="resource-browser-results">
+  <div class="no-results">No results.</div>
+</div>
+
+<script type="text/html" id="tmpl_resource_browser_result">
+<div class="result">
+  <h3>
+    <% if ('external' in tagsHash) { %><strong>External: </strong> <% } %>
+    <a href="<%= path %>"><%= title.en %></a>
+    <% if ('new' in tagsHash) { %><span class="new">new!</span> <% } %>
+  </h3>
+  <p class="resource-meta"><%
+    var __g = ['', ''];
+    if ('article' in tagsHash) {
+      __g = ['Article', 'about'];
+    } else if ('tutorial' in tagsHash) {
+      __g = ['Tutorial', 'on'];
+    } else if ('sample' in tagsHash) {
+      __g = ['Sample', 'for'];
+    } else if ('video' in tagsHash) {
+      __g = ['Video', 'about'];
+    }
+  %>
+  <%= __g[0] %><% if (topicsHtml) { %> <%= __g[1] %><% } %> <%= topicsHtml %></p>
+  <p><%= description.en %></p>
+</div>
+</script>
diff --git a/docs/html/resources/index.jd b/docs/html/resources/index.jd
index 1668721..9055868 100644
--- a/docs/html/resources/index.jd
+++ b/docs/html/resources/index.jd
@@ -1,38 +1,90 @@
 page.title=Developer Resources
 @jd:body
 
+<style type="text/css">
+  #resource-list-table td {
+    border: 0;
+    padding: 0 24px;
+    width: 33%;
+    max-width: 250px;
+    border-right: 1px solid #ddd;
+  }
+
+  #resource-list-table td.last {
+    border-right: 0;
+    padding-right: 0;
+  }
+</style>
+
 <p>
-This section provides technical articles, tutorials, sample code, and other
+This section provides articles, tutorials, sample code, and other
 information to help you quickly implement the features you want in your
-application.
+application. To return to this page later, just click the "Resources"
+tab while any Resources page is loaded.
 </p>
 
+<h2>Technical Resources</h2>
+
+<table id="resource-list-table">
+<tr>
+  <td>
+    <a href="{@docRoot}resources/browser.html?tag=sample">
+      <img src="{@docRoot}assets/images/resource-big-sample.png"/>
+    </a>
+    <h3><a href="{@docRoot}resources/browser.html?tag=sample">
+      Sample Code
+    </a></h3>
+    <p>Fully-functioning sample applications that you can build and run
+    to learn about how Android works. Feel free to reuse any of the code or
+    techniques in the samples.</p>
+  </td>
+  <td>
+    <a href="{@docRoot}resources/browser.html?tag=article">
+      <img src="{@docRoot}assets/images/resource-big-article.png"/>
+    </a>
+    <h3><a href="{@docRoot}resources/browser.html?tag=article">
+      Articles
+    </a></h3>
+    <p>Focused discussions about Android development subjects, including
+    optimizations, tips, interesting implementations, "how-tos",
+    and so on.</p>
+  </td>
+  <td>
+    <a href="{@docRoot}resources/browser.html?tag=tutorial">
+      <img src="{@docRoot}assets/images/resource-big-tutorial.png"/>
+    </a>
+    <h3><a href="{@docRoot}resources/browser.html?tag=tutorial">
+      Tutorials
+    </a></h3>
+    <p>Step-by-step instructions demonstrating how to build an Android application
+    that has the specific features you want.</p>
+  </td>
+  <!-- <td class="last">
+    <a href="{@docRoot}resources/browser.html?tag=video">
+      <img src="{@docRoot}assets/images/resource-big-video.png"/>
+    </a>
+    <h3><a href="{@docRoot}resources/browser.html?tag=video">
+      Videos &amp; Screencasts
+    </a></h3>
+    <p>Videos and presentation slides from developer events, along with
+    screencasts to walk you through common Android development
+    workflows.</p>
+  </td> -->
+</tr>
+</table>
+
+<h2>Other Resources</h2>
+
 <dl>
-<dt><b>Technical Articles</b></dt>
-<dd>Focused discussions about Android development subjects, including
-optimizations, tips, interesting implementations,
-and so on. Most of the articles provide "how-to" instructions for adding
-features or functionality to your app. The articles are drawn from posts to the
-Android Developers Blog.
-</dd>
-
-<dt><b>Tutorials</b></dt>
-<dd>Step-by-step instructions demonstrating how to build an Android application
-that has the specific features you want. </dd>
-
-<dt><b>Sample Code</b></dt>
-<dd>Fully-functioning sample applications that you can look at or build and run,
-to learn about how Android works. Feel free to reuse any of the code or
-techniques that you find in the samples!</dd>
-
 <dt><b>Community</b></dt>
 <dd>Links to the Android discussion groups and information about other ways to
 collaborate with other developers. </dd>
 
+<dt><b>Device Dashboard</b></dt>
+<dd>Device distribution data, grouped by various dimensions such as screen size
+and Android platform version. </dd>
+
 <dt><b>More</b></dt>
 <dd>Quick development tips, troubleshooting information, and frequently asked
 questions (FAQs). </dd>
 </dl>
-
-<p>To return to this page later, just click the "Resources" tab while any
-Resources page is loaded. </p>
\ No newline at end of file
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
new file mode 100644
index 0000000..d06b695
--- /dev/null
+++ b/docs/html/resources/resources-data.js
@@ -0,0 +1,632 @@
+var ANDROID_TAGS = {
+  type: {
+    'article': 'Article',
+    'tutorial': 'Tutorial',
+    'sample': 'Sample',
+    'video': 'Video',
+    'library': 'Code Library'
+  },
+  topic: {
+    'accessibility': 'Accessibility',
+    'accountsync': 'Accounts &amp; Sync',
+    'bestpractice': 'Best Practices',
+    'communication': 'Communication',
+    'compatibility': 'Compatibility',
+    'data': 'Data Access',
+    'drawing': 'Canvas Drawing',
+    'gamedev': 'Game Development',
+    'gl': 'OpenGL ES',
+    'input': 'Input Methods',
+    'intent': 'Intents',
+    'layout': 'Layouts/Views',
+    'media': 'Multimedia',
+    'newfeature': 'New Features',
+    'performance': 'Performance',
+    'search': 'Search',
+    'testing': 'Testing',
+    'ui': 'User Interface',
+    'web': 'Web Content'
+  },
+  misc: {
+    'external': 'External',
+    'new': 'New'
+  }
+};
+
+var ANDROID_RESOURCES = [
+
+//////////////////////////
+/// TECHNICAL ARTICLES ///
+//////////////////////////
+
+  {
+    tags: ['article', 'performance', 'bestpractice'],
+    path: 'articles/avoiding-memory-leaks.html',
+    title: {
+      en: 'Avoiding Memory Leaks'
+    },
+    description: {
+      en: 'Mobile devices often have limited memory, and memory leaks can cause your application to waste this valuable resource without your knowledge. This article provides tips to help you avoid common causes of memory leaks on the Android platform.'
+    }
+  },
+  {
+    tags: ['article', 'compatibility'],
+    path: 'articles/backward-compatibility.html',
+    title: {
+      en: 'Backward Compatibility'
+    },
+    description: {
+      en: 'The Android platform strives to ensure backwards compatibility. However, sometimes you want to use new features which aren\'t supported on older platforms. This article discusses strategies for selectively using these features based on availability, allowing you to keep your applications portable across a wide range of devices.'
+    }
+  },
+  {
+    tags: ['article', 'intent'],
+    path: 'articles/can-i-use-this-intent.html',
+    title: {
+      en: 'Can I Use this Intent?'
+    },
+    description: {
+      en: 'Android offers a very powerful and yet easy-to-use message type called an intent. You can use intents to turn applications into high-level libraries and make code modular and reusable. While it is nice to be able to make use of a loosely coupled API, there is no guarantee that the intent you send will be received by another application. This article describes a technique you can use to find out whether the system contains any application capable of responding to the intent you want to use.'
+    }
+  },
+  {
+    tags: ['article', 'input'],
+    path: 'articles/creating-input-method.html',
+    title: {
+      en: 'Creating an Input Method'
+    },
+    description: {
+      en: 'Input Method Editors (IMEs) provide the mechanism for entering text into text fields and other Views. Android devices come bundled with at least one IME, but users can install additional IMEs. This article covers the basics of developing an IME for the Android platform.'
+    }
+  },
+  {
+    tags: ['article', 'drawing', 'ui'],
+    path: 'articles/drawable-mutations.html',
+    title: {
+      en: 'Drawable Mutations'
+    },
+    description: {
+      en: 'Drawables are pluggable drawing containers that allow applications to display graphics. This article explains some common pitfalls when trying to modify the properties of multiple Drawables.'
+    }
+  },
+  {
+    tags: ['article', 'bestpractice', 'ui'],
+    path: 'articles/faster-screen-orientation-change.html',
+    title: {
+      en: 'Faster Screen Orientation Change'
+    },
+    description: {
+      en: 'When an Android device changes its orientation, the default behavior is to automatically restart the current activity with a new configuration. However, this can become a bottleneck in applications that access a large amount of external data. This article discusses how to gracefully handle this situation without resorting to manually processing configuration changes.'
+    }
+  },
+  {
+    tags: ['article', 'compatibility'],
+    path: 'articles/future-proofing.html',
+    title: {
+      en: 'Future-Proofing Your Apps'
+    },
+    description: {
+      en: 'A collection of common sense advice to help you ensure that your applications don\'t break when new versions of the Android platform are released.'
+    }
+  },
+  {
+    tags: ['article', 'input'],
+    path: 'articles/gestures.html',
+    title: {
+      en: 'Gestures'
+    },
+    description: {
+      en: 'Touch screens allow users to perform gestures, such as tapping, dragging, flinging, or sliding, to perform various actions. The gestures API enables your application to recognize even complicated gestures with ease. This article explains how to integrate this API into an application.'
+    }
+  },
+  {
+    tags: ['article', 'gamedev', 'gl'],
+    path: 'articles/glsurfaceview.html',
+    title: {
+      en: 'Introducing GLSurfaceView'
+    },
+    description: {
+      en: 'This article provides an overview of GLSurfaceView, a class that makes it easy to implement 2D or 3D OpenGL rendering inside of an Android application.'
+    }
+  },
+  {
+    tags: ['article', 'ui', 'layout'],
+    path: 'articles/layout-tricks-reuse.html',
+    title: {
+      en: 'Layout Tricks: Creating Reusable UI Components'
+    },
+    description: {
+      en: 'Learn how to combine multiple standard UI widgets into a single high-level component, which can be reused throughout your application.'
+    }
+  },
+  {
+    tags: ['article', 'layout', 'ui', 'performance', 'bestpractice'],
+    path: 'articles/layout-tricks-efficiency.html',
+    title: {
+      en: 'Layout Tricks: Creating Efficient Layouts'
+    },
+    description: {
+      en: 'Learn how to optimize application layouts as this article walks you through converting a LinearLayout into a RelativeLayout, and analyzes the resulting implications on performance.'
+    }
+  },
+  {
+    tags: ['article', 'layout', 'ui', 'performance', 'bestpractice'],
+    path: 'articles/layout-tricks-stubs.html',
+    title: {
+      en: 'Layout Tricks: Using ViewStubs'
+    },
+    description: {
+      en: 'Learn about using ViewStubs inside an application\'s layout in order to inflate rarely used UI elements, without the performance implications which would otherwise be caused by using the <code>&lt;include&gt;</code> tag.'
+    }
+  },
+  {
+    tags: ['article', 'layout', 'ui', 'performance', 'bestpractice'],
+    path: 'articles/layout-tricks-merge.html',
+    title: {
+      en: 'Layout Tricks: Merging Layouts'
+    },
+    description: {
+      en: 'Learn how to use the <code>&lt;merge&gt;</code> tag in your XML layouts in order to avoid unnecessary levels of hierarchy within an application\'s view tree.'
+    }
+  },
+  {
+    tags: ['article', 'ui', 'performance'],
+    path: 'articles/listview-backgrounds.html',
+    title: {
+      en: 'ListView Backgrounds: An Optimization'
+    },
+    description: {
+      en: 'ListViews are very popular widgets within the Android framework. This article describes some of the optimizations used by the ListView widget, and how to avoid some common issues that this causes when trying to use a custom background.'
+    }
+  },
+  {
+    tags: ['article', 'ui', 'newfeature'],
+    path: 'articles/live-folders.html',
+    title: {
+      en: 'Live Folders'
+    },
+    description: {
+      en: 'Live Folders allow users to display any source of data on their home screen without launching an application. This article discusses how to export an application\'s data in a format suitable for display inside of a live folder.'
+    }
+  },
+  {
+    tags: ['article', 'ui', 'newfeature'],
+    path: 'articles/live-wallpapers.html',
+    title: {
+      en: 'Live Wallpapers'
+    },
+    description: {
+      en: 'Live wallpapers are richer, animated, interactive backgrounds that users can display in their home screens. Learn how to create a live wallpaper and bundle it in an application that users can install on their devices.'
+    }
+  },
+  {
+    tags: ['article', 'input'],
+    path: 'articles/on-screen-inputs.html',
+    title: {
+      en: 'Onscreen Input Methods'
+    },
+    description: {
+      en: 'The Input Method Framework (IMF) allows users to take advantage of on-screen input methods, such as software keyboards. This article provides an overview of Input Method Editors (IMEs) and how applications interact with them.'
+    }
+  },
+  {
+    tags: ['article', 'performance', 'bestpractice'],
+    path: 'articles/painless-threading.html',
+    title: {
+      en: 'Painless Threading'
+    },
+    description: {
+      en: 'This article discusses the threading model used by Android applications and how applications can ensure best UI performance by spawning worker threads to handle long-running operations, rather than handling them in the main thread. The article also explains the API that your application can use to interact with Android UI toolkit components running on the main thread and spawn managed worker threads.'
+    }
+  },
+  {
+    tags: ['article', 'ui', 'search'],
+    path: 'articles/qsb.html',
+    title: {
+      en: 'Quick Search Box'
+    },
+    description: {
+      en: 'Quick Search Box (QSB) is a powerful, system-wide search framework. QSB makes it possible for users to quickly and easily find what they\'re looking for, both on their devices and on the web. This article discusses how to work with the QSB framework to add new search results for an installed application.'
+    }
+  },
+  {
+    tags: ['article', 'ui'],
+    path: 'articles/touch-mode.html',
+    title: {
+      en: 'Touch Mode'
+    },
+    description: {
+      en: 'This article explains the touch mode, one of the most important principles of Android\'s UI toolkit. Whenever a user interacts with a device\'s touch screen, the system enters touch mode. While simple in concept, there are important implications touch mode that are often overlooked.'
+    }
+  },
+  {
+    tags: ['article', 'performance', 'bestpractice'],
+    path: 'articles/track-mem.html',
+    title: {
+      en: 'Tracking Memory Allocations'
+    },
+    description: {
+      en: 'This article discusses how to use the Allocation Tracker tool to observe memory allocations and avoid performance problems that would otherwise be caused by ignoring the effect of Dalvik\'s garbage collector.'
+    }
+  },
+  {
+    tags: ['article', 'newfeature'],
+    path: 'articles/ui-1.5.html',
+    title: {
+      en: 'UI Framework Changes in Android 1.5'
+    },
+    description: {
+      en: 'Explore the UI changes that were introduced in Android 1.5, compared with the UI provided in Android 1.0 and 1.1.'
+    }
+  },
+  {
+    tags: ['article', 'newfeature'],
+    path: 'articles/ui-1.6.html',
+    title: {
+      en: 'UI Framework Changes in Android 1.6'
+    },
+    description: {
+      en: 'Explore the UI changes that were introduced in Android 1.6, compared with the UI provided in Android 1.5. In particular, this article discusses changes to RelativeLayouts and click listeners.'
+    }
+  },
+  {
+    tags: ['article', 'ui', 'bestpractice'],
+    path: 'articles/timed-ui-updates.html',
+    title: {
+      en: 'Updating the UI from a Timer'
+    },
+    description: {
+      en: 'Learn about how to use Handlers as a more efficient replacement for java.util.Timer on the Android platform.'
+    }
+  },
+  {
+    tags: ['article', 'ui', 'accessibility'],
+    path: 'articles/tts.html',
+    title: {
+      en: 'Using Text-to-Speech'
+    },
+    description: {
+      en: 'The text-to-speech API lets your application "speak" to users, in any of several languages. This article provides an overview of the TTS API and how you use to add speech capabilities to your application.'
+    }
+  },
+  {
+    tags: ['article', 'ui', 'web'],
+    path: 'articles/using-webviews.html',
+    title: {
+      en: 'Using WebViews'
+    },
+    description: {
+      en: 'WebViews allow an application to dynamically display HTML and execute JavaScript, without relinquishing control to a separate browser application. This article introduces the WebView classes and provides a sample application that demonstrates its use.'
+    }
+  },
+  {
+    tags: ['article', 'ui'],
+    path: 'articles/wikinotes-linkify.html',
+    title: {
+      en: 'WikiNotes: Linkify your Text!'
+    },
+    description: {
+      en: 'This article introduces WikiNotes for Android, part of the Apps for Android project. It covers the use of Linkify to turn ordinary text views into richer, link-oriented content that causes Android intents to fire when a link is selected.'
+    }
+  },
+  {
+    tags: ['article', 'intent'],
+    path: 'articles/wikinotes-intents.html',
+    title: {
+      en: 'WikiNotes: Routing Intents'
+    },
+    description: {
+      en: 'This article illustrates how an application, in this case the WikiNotes sample app, can use intents to route various types of linked text to the application that handles that type of data. For example, an app can use intents to route a linked telephone number to a dialer app and a web URL to a browser.'
+    }
+  },
+  {
+    tags: ['article', 'ui', 'performance'],
+    path: 'articles/window-bg-speed.html',
+    title: {
+      en: 'Window Backgrounds & UI Speed'
+    },
+    description: {
+      en: 'Some Android applications need to squeeze every bit of performance out of the UI toolkit and there are many ways to do so. In this article, you will discover how to speed up the drawing and the perceived startup time of your activities. Both of these techniques rely on a single feature, the window\'s background drawable.'
+    }
+  },
+  {
+    tags: ['article', 'performance', 'bestpractice'],
+    path: 'articles/zipalign.html',
+    title: {
+      en: 'Zipalign: an Easy Optimization'
+    },
+    description: {
+      en: 'The Android SDK includes a tool called zipalign that optimizes the way an application is packaged. Running zipalign against your application enables Android to interact with it more efficiently at run time and thus has the potential to make it and the overall system run faster. This article provides a high-level overview of the zipalign tool and its use.'
+    }
+  },
+
+///////////////////
+/// SAMPLE CODE ///
+///////////////////
+
+  {
+    tags: ['sample', 'layout', 'ui'],
+    path: 'samples/ApiDemos/index.html',
+    title: {
+      en: 'API Demos'
+    },
+    description: {
+      en: 'A variety of small applications that demonstrate an extensive collection of framework topics.'
+    }
+  },
+  {
+    tags: ['sample', 'data', 'newfeature', 'accountsync', 'new'],
+    path: 'samples/BackupRestore/index.html',
+    title: {
+      en: 'Backup and Restore'
+    },
+    description: {
+      en: 'Illustrates a few different approaches that an application developer can take when integrating with the Android Backup Manager using the BackupAgent API introduced in Android 2.2.'
+    }
+  },
+  {
+    tags: ['sample', 'communication'],
+    path: 'samples/BluetoothChat/index.html',
+    title: {
+      en: 'Bluetooth Chat'
+    },
+    description: {
+      en: 'An application for two-way text messaging over Bluetooth.'
+    }
+  },
+  {
+    tags: ['sample', 'accountsync'],
+    path: 'samples/BusinessCard/index.html',
+    title: {
+      en: 'BusinessCard'
+    },
+    description: {
+      en: 'An application that demonstrates how to launch the built-in contact picker from within an activity. This sample also uses reflection to ensure that the correct version of the contacts API is used, depending on which API level the application is running under.'
+    }
+  },
+  {
+    tags: ['sample', 'accountsync'],
+    path: 'samples/ContactManager/index.html',
+    title: {
+      en: 'Contact Manager'
+    },
+    description: {
+      en: 'An application that demonstrates how to query the system contacts provider  using the <code>ContactsContract</code> API, as well as insert contacts into a specific account.'
+    }
+  },
+  {
+    tags: ['sample'],
+    path: 'samples/Home/index.html',
+    title: {
+      en: 'Home'
+    },
+    description: {
+      en: 'A home screen replacement application.'
+    }
+  },
+  {
+    tags: ['sample', 'gamedev', 'media'],
+    path: 'samples/JetBoy/index.html',
+    title: {
+      en: 'JetBoy'
+    },
+    description: {
+      en: 'A game that demonstrates the SONiVOX JET interactive music technology, with <code><a href="/reference/android/media/JetPlayer.html">JetPlayer</a></code>.'
+    }
+  },
+  {
+    tags: ['sample', 'ui', 'newfeature'],
+    path: 'samples/CubeLiveWallpaper/index.html',
+    title: {
+      en: 'Live Wallpaper'
+    },
+    description: {
+      en: 'An application that demonstrates how to create a live wallpaper and  bundle it in an application that users can install on their devices.'
+    }
+  },
+  {
+    tags: ['sample', 'gamedev', 'media'],
+    path: 'samples/LunarLander/index.html',
+    title: {
+      en: 'Lunar Lander'
+    },
+    description: {
+      en: 'A classic Lunar Lander game.'
+    }
+  },
+  {
+    tags: ['sample', 'ui', 'bestpractice', 'layout'],
+    path: 'samples/MultiResolution/index.html',
+    title: {
+      en: 'Multiple Resolutions'
+    },
+    description: {
+      en: 'A sample application that shows how to use resource directory qualifiers to provide different resources for different screen configurations.'
+    }
+  },
+  {
+    tags: ['sample', 'data'],
+    path: 'samples/NotePad/index.html',
+    title: {
+      en: 'Note Pad'
+    },
+    description: {
+      en: 'An application for saving notes. Similar (but not identical) to the <a href="/resources/tutorials/notepad/index.html">Notepad tutorial</a>.'
+    }
+  },
+  {
+    tags: ['sample', 'accountsync'],
+    path: 'samples/SampleSyncAdapter/index.html',
+    title: {
+      en: 'SampleSyncAdapter'
+    },
+    description: {
+      en: 'Demonstrates how an application can communicate with a cloud-based service and synchronize its data with data stored locally in a content provider. The sample uses two related parts of the Android framework &mdash; the account manager and the synchronization manager (through a sync adapter).'
+    }
+  },
+  {
+    tags: ['sample', 'ui', 'search', 'new'],
+    path: 'samples/SearchableDictionary/index.html',
+    title: {
+      en: 'Searchable Dictionary v2'
+    },
+    description: {
+      en: 'A sample application that demonstrates Android\'s search framework, including how to provide search suggestions for Quick Search Box.'
+    }
+  },
+  {
+    tags: ['sample', 'layout', 'ui'],
+    path: 'samples/Snake/index.html',
+    title: {
+      en: 'Snake'
+    },
+    description: {
+      en: 'An implementation of the classic game "Snake."'
+    }
+  },
+  {
+    tags: ['sample', 'testing', 'new'],
+    path: 'samples/Spinner/index.html',
+    title: {
+      en: 'Spinner'
+    },
+    description: {
+      en: 'A simple application that serves as an application under test for the SpinnerTest example.'
+    }
+  },
+  {
+    tags: ['sample', 'testing', 'new'],
+    path: 'samples/SpinnerTest/index.html',
+    title: {
+      en: 'SpinnerTest'
+    },
+    description: {
+      en: 'The test application for the Activity Testing tutorial. It tests the Spinner example application.'
+    }
+  },
+  {
+    tags: ['sample', 'newfeature', 'new'],
+    path: 'samples/TicTacToeLib/index.html',
+    title: {
+      en: 'TicTacToeLib'
+    },
+    description: {
+      en: 'An example of an Android library project, a type of project that lets you store and manage shared code and resources in one place, then make them available to your other Android applications.'
+    }
+  },
+  {
+    tags: ['sample', 'newfeature', 'new'],
+    path: 'samples/TicTacToeMain/index.html',
+    title: {
+      en: 'TicTacToeMain'
+    },
+    description: {
+      en: 'Demonstrates how an application can make use of shared code and resources stored in an Android library project.'
+    }
+  },
+  {
+    tags: ['sample', 'input'],
+    path: 'samples/SoftKeyboard/index.html',
+    title: {
+      en: 'Soft Keyboard'
+    },
+    description: {
+      en: 'An example of writing an input method for a software keyboard.'
+    }
+  },
+  {
+    tags: ['sample', 'ui'],
+    path: 'samples/Wiktionary/index.html',
+    title: {
+      en: 'Wiktionary'
+    },
+    description: {
+      en: 'An example of creating interactive widgets for display on the Android home screen.'
+    }
+  },
+  {
+    tags: ['sample', 'ui'],
+    path: 'samples/WiktionarySimple/index.html',
+    title: {
+      en: 'Wiktionary (Simplified)'
+    },
+    description: {
+      en: 'A simple Android home screen widgets example.'
+    }
+  },
+  {
+    tags: ['sample', 'layout', 'new'],
+    path: 'samples/XmlAdapters/index.html',
+    title: {
+      en: 'XML Adapters'
+    },
+    description: {
+      en: 'Binding data to views using XML Adapters examples.'
+    }
+  },
+
+/////////////////
+/// TUTORIALS ///
+/////////////////
+
+  {
+    tags: ['tutorial'],
+    path: 'tutorials/hello-world.html',
+    title: {
+      en: 'Hello World'
+    },
+    description: {
+      en: 'Beginning basic application development with the Android SDK.'
+    }
+  },
+  {
+    tags: ['tutorial', 'ui', 'layout'],
+    path: 'tutorials/views/index.html',
+    title: {
+      en: 'Hello Views'
+    },
+    description: {
+      en: 'A walk-through of the various types of layouts and views available in the Android SDK.'
+    }
+  },
+  {
+    tags: ['tutorial', 'ui', 'bestpractice'],
+    path: 'tutorials/localization/index.html',
+    title: {
+      en: 'Hello Localization'
+    },
+    description: {
+      en: 'The basics of localizing your applications for multiple languages and locales.'
+    }
+  },
+  {
+    tags: ['tutorial', 'data'],
+    path: 'tutorials/notepad/index.html',
+    title: {
+      en: 'Notepad Tutorial'
+    },
+    description: {
+      en: 'A multi-part tutorial discussing intermediate-level concepts such as data access.'
+    }
+  },
+  {
+    tags: ['tutorial', 'testing', 'new'],
+    path: 'tutorials/testing/helloandroid_test.html',
+    title: {
+      en: 'Hello Testing'
+    },
+    description: {
+      en: 'A basic introduction to the Android testing framework.'
+    }
+  },
+  {
+    tags: ['tutorial', 'testing', 'new'],
+    path: 'tutorials/testing/activity_test.html',
+    title: {
+      en: 'Activity Testing'
+    },
+    description: {
+      en: 'A more advanced demonstration of the Android testing framework and tools.'
+    }
+  }
+];
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index 735a00e..1c1ece9 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -1,5 +1,56 @@
 <ul>
   <li>
+    <h2><span class="en">Technical Resources</span>
+    </h2>
+    <ul>
+      <li class="toggle-list">
+        <div><a href="<?cs var:toroot ?>resources/browser.html?tag=sample">
+            <span class="en">Sample Code</span>
+            <span class="de" style="display:none">Beispielcode</span>
+            <span class="es" style="display:none">Código de ejemplo</span>
+            <span class="fr" style="display:none">Exemple de code</span>
+            <span class="it" style="display:none">Codice di esempio</span>
+            <span class="ja" style="display:none">サンプル コード</span>
+            <span class="zh-CN" style="display:none"></span>
+            <span class="zh-TW" style="display:none"></span>
+          </a></div>
+        <ul id="devdoc-nav-sample-list">
+          <li><a href="<?cs var:toroot ?>resources/samples/get.html">
+                <span class="en">Getting the Samples</span>
+              </a></li>
+        </ul>
+      </li>
+      <li class="toggle-list">
+        <div><a href="<?cs var:toroot ?>resources/browser.html?tag=article">
+               <span class="en">Articles</span>
+             </a></div>
+        <ul id="devdoc-nav-article-list">
+        </ul>
+      </li>
+      <li class="toggle-list">
+        <div><a href="<?cs var:toroot ?>resources/browser.html?tag=tutorial">
+               <span class="en">Tutorials</span>
+               <span class="de" style="display:none">Lernprogramme</span>
+               <span class="es" style="display:none">Tutoriales</span>
+               <span class="fr" style="display:none">Didacticiels</span>
+               <span class="it" style="display:none">Esercitazioni</span>
+               <span class="ja" style="display:none">チュートリアル</span>
+               <span class="zh-CN" style="display:none"></span>
+               <span class="zh-TW" style="display:none"></span>
+             </a></div>
+        <ul id="devdoc-nav-tutorial-list">
+        </ul>
+      </li>
+      <li class="toggle-list">
+        <div><a href="<?cs var:toroot ?>resources/topics.html">
+               <span class="en">Topics</span>
+             </a></div>
+        <ul id="devdoc-nav-topic-list">
+        </ul>
+      </li>
+    </ul>
+  </li>
+  <li>
     <h2><span class="en">Community</span>
                <span style="display:none" class="de"></span>
                <span style="display:none" class="es">Comunidad</span>
@@ -36,242 +87,7 @@
 ?>
 
   <li>
-    <h2><span class="en">Technical Articles</span>
-    </h2>
-    <ul>
-      <li class="toggle-list">
-        <div><a href="<?cs var:toroot ?>resources/articles/index.html">
-               <span class="en">List of Articles</span>
-             </a></div>
-        <ul>
-        <li><a href="<?cs var:toroot ?>resources/articles/avoiding-memory-leaks.html">
-                <span class="en">Avoiding Memory Leaks</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/backward-compatibility.html">
-                <span class="en">Backward Compatibility</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/can-i-use-this-intent.html">
-                <span class="en">Can I Use this Intent?</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/creating-input-method.html">
-                <span class="en">Creating an Input Method</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/drawable-mutations.html">
-                <span class="en">Drawable Mutations</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/faster-screen-orientation-change.html">
-                <span class="en">Faster Screen Orientation Change</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/future-proofing.html">
-                <span class="en">Future-Proofing Your Apps</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/gestures.html">
-                <span class="en">Gestures</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/glsurfaceview.html">
-                <span class="en">Introducing GLSurfaceView</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/layout-tricks-reuse.html">
-                <span class="en">Layout Tricks: Reusing </span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/layout-tricks-efficiency.html">
-                <span class="en">Layout Tricks: Efficiency</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/layout-tricks-stubs.html">
-                <span class="en">Layout Tricks: ViewStubs </span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/layout-tricks-merge.html">
-                <span class="en">Layout Tricks: Merging </span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/listview-backgrounds.html">
-                <span class="en">ListView Backgrounds</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/live-folders.html">
-                <span class="en">Live Folders</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/live-wallpapers.html">
-                <span class="en">Live Wallpapers</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/on-screen-inputs.html">
-                <span class="en">Onscreen Input Methods</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/painless-threading.html">
-                <span class="en">Painless Threading</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/qsb.html">
-                <span class="en">Quick Search Box</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/speech-input.html">
-                <span class="en">Speech Input</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/touch-mode.html">
-                <span class="en">Touch Mode</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/track-mem.html">
-                <span class="en">Tracking Memory Allocations</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/ui-1.5.html">
-                <span class="en">UI Framework Changes in Android 1.5</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/ui-1.6.html">
-                <span class="en">UI Framework Changes in Android 1.6</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/timed-ui-updates.html">
-                <span class="en">Updating the UI from a Timer</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/tts.html">
-                <span class="en">Using Text-to-Speech</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/contacts.html">
-                <span class="en">Using the Contacts API</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/using-webviews.html">
-                <span class="en">Using WebViews</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/wikinotes-linkify.html">
-                <span class="en">WikiNotes: Linkify your Text!</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/wikinotes-intents.html">
-                <span class="en">WikiNotes: Routing Intents</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/window-bg-speed.html">
-                <span class="en">Window Backgrounds &amp; UI Speed</span>
-                </a></li>
-        <li><a href="<?cs var:toroot ?>resources/articles/zipalign.html">
-                <span class="en">Zipalign: An Easy Optimization</span>
-                </a></li>
-        </ul>
-      </li>
-    </ul>
-   </li>
-
-  <li>
-    <h2><span class="en">Tutorials</span>
-               <span class="de" style="display:none">Lernprogramme</span>
-               <span class="es" style="display:none">Tutoriales</span>
-               <span class="fr" style="display:none">Didacticiels</span>
-               <span class="it" style="display:none">Esercitazioni</span>
-               <span class="ja" style="display:none">チュートリアル</span>
-               <span class="zh-CN" style="display:none"></span>
-               <span class="zh-TW" style="display:none"></span>
-    </h2>
-    <ul>
-      <li><a href="<?cs var:toroot ?>resources/tutorials/hello-world.html">
-            <span class="en">Hello World</span>
-          </a></li>
-      <li><a href="<?cs var:toroot ?>resources/tutorials/views/index.html">
-            <span class="en">Hello Views</span>
-          </a></li>
-      <li><a href="<?cs var:toroot ?>resources/tutorials/localization/index.html">
-            <span class="en">Hello Localization</span>
-          </a></li>
-      <li><a href="<?cs var:toroot ?>resources/tutorials/testing/helloandroid_test.html">
-            <span class="en">Hello Testing</span></a>
-      </li>
-      <li><a href="<?cs var:toroot ?>resources/tutorials/notepad/index.html">
-            <span class="en">Notepad Tutorial</span>
-          </a></li>
-      <li><a href="<?cs var:toroot ?>resources/tutorials/testing/activity_test.html">
-            <span class="en">Activity Testing</span></a>
-      </li>
-    </ul>
-  </li>
-
-
-  <li>
-    <h2><span class="en">Sample Code</span>
-               <span class="de" style="display:none">Beispielcode</span>
-               <span class="es" style="display:none">Código de ejemplo</span>
-               <span class="fr" style="display:none">Exemple de code</span>
-               <span class="it" style="display:none">Codice di esempio</span>
-               <span class="ja" style="display:none">サンプル コード</span>
-               <span class="zh-CN" style="display:none"></span>
-               <span class="zh-TW" style="display:none"></span>
-    </h2>
-    <ul>
-      <li><a href="<?cs var:toroot ?>resources/samples/get.html">
-            <span class="en">Getting the Samples</span>
-          </a></li>
-      <li class="toggle-list">
-        <div><a href="<?cs var:toroot ?>resources/samples/index.html">
-               <span class="en">List of Samples</span>
-             </a></div>
-        <ul>
-          <li><a href="<?cs var:toroot ?>resources/samples/ApiDemos/index.html">
-                <span class="en">API Demos</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/BackupRestore/index.html">
-                <span class="en">Backup and Restore</span>
-              </a> <span class="new">new!</span></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/BluetoothChat/index.html">
-                <span class="en">Bluetooth Chat</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/BusinessCard/index.html">
-                <span class="en">Business Card</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/ContactManager/index.html">
-                <span class="en">Contact Manager</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/Home/index.html">
-                <span class="en">Home</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/JetBoy/index.html">
-                <span class="en">JetBoy</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/CubeLiveWallpaper/index.html">
-                <span class="en">Live Wallpaper</span>
-                </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/LunarLander/index.html">
-                <span class="en">Lunar Lander</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/MultiResolution/index.html">
-                <span class="en">Multiple Resolutions</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/NotePad/index.html">
-                <span class="en">Note Pad</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/SampleSyncAdapter/index.html">
-                <span class="en">Sample Sync Adapter</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/SearchableDictionary/index.html">
-                <span class="en">Searchable Dictionary v2</span>
-              </a> <span class="new">new!</span></li>
-	  <li><a href="<?cs var:toroot ?>resources/samples/SipDemo/index.html">
-	        <span class="en">SIP Demo</span>
-	      </a> <span class="new">new!</span></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/Snake/index.html">
-                <span class="en">Snake</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/SoftKeyboard/index.html">
-                <span class="en">Soft Keyboard</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/Spinner/index.html">
-                <span class="en">Spinner</span>
-                </a> <span class="new">new!</span></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/SpinnerTest/index.html">
-                <span class="en">SpinnerTest</span>
-                </a> <span class="new">new!</span></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/TicTacToeLib/index.html">
-                <span class="en">TicTacToeLib</span>
-                </a> <span class="new">new!</span></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/TicTacToeMain/index.html">
-                <span class="en">TicTacToeMain</span>
-                </a> <span class="new">new!</span></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/Wiktionary/index.html">
-                <span class="en">Wiktionary</span>
-              </a></li>
-          <li><a href="<?cs var:toroot ?>resources/samples/WiktionarySimple/index.html">
-                <span class="en">Wiktionary (Simplified)</span>
-              </a></li>
-        </ul>
-      </li>
-    </ul>
-  </li>
-
-
-
-  <li>
-    <h2><span class="en">More</span>
+   <h2><span class="en">More</span>
     </h2>
     <ul>
       <li><a href="<?cs var:toroot ?>resources/faq/commontasks.html">
@@ -298,8 +114,6 @@
      </li>
     </ul>
   </li>
-
-
 </ul>
 
 <script type="text/javascript">
diff --git a/docs/html/resources/samples/images/XmlPhotosAdapter.png b/docs/html/resources/samples/images/XmlPhotosAdapter.png
new file mode 100644
index 0000000..c018d54
--- /dev/null
+++ b/docs/html/resources/samples/images/XmlPhotosAdapter.png
Binary files differ
diff --git a/docs/html/resources/samples/images/XmlRssReader.png b/docs/html/resources/samples/images/XmlRssReader.png
new file mode 100644
index 0000000..00f841b
--- /dev/null
+++ b/docs/html/resources/samples/images/XmlRssReader.png
Binary files differ
diff --git a/docs/html/resources/samples/images/sample_notepadtest_junit.png b/docs/html/resources/samples/images/sample_notepadtest_junit.png
new file mode 100644
index 0000000..4cda54e
--- /dev/null
+++ b/docs/html/resources/samples/images/sample_notepadtest_junit.png
Binary files differ
diff --git a/docs/html/resources/topics.jd b/docs/html/resources/topics.jd
new file mode 100644
index 0000000..b5960ff
--- /dev/null
+++ b/docs/html/resources/topics.jd
@@ -0,0 +1,72 @@
+page.title=Technical Resource Topics
+@jd:body
+
+<style type="text/css">
+  #resource-topic-table td {
+    border: 0;
+    padding: 0;
+    margin: 0;
+    padding-left: 1em;
+    width: 18em;
+  }
+
+  #resource-topic-table ul {
+    padding: 0;
+    margin: 0;
+  }
+
+  #resource-topic-table li {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+  }
+</style>
+
+<p>
+You can browse the list of technical resources by topic by clicking on the
+links below. Over time, as more topics are added, they will be added to the
+list below.
+</p>
+
+<noscript>
+  <p class="note"><strong>Error:</strong>
+    You must have JavaScript enabled to view this page. Resources are also
+    available offline in the SDK.
+  </p>
+</noscript>
+
+<table id="resource-topic-table">
+  <tr></tr>
+</table>
+
+<script type="text/javascript">
+<!-- 
+(function() {
+  var topics = [];
+  for (var topic in ANDROID_TAGS['topic']) {
+    topics.push({name:topic,title:ANDROID_TAGS['topic'][topic]});
+  }
+  topics.sort(function(x,y){ return (x.title < y.title) ? -1 : 1; });
+  var topicParent = null;
+  for (var i = 0; i < topics.length; i++) {
+    if (topicParent == null || i % 10 == 0) {
+      // create a new column
+      topicParent = $('ul', $('<td><ul>').appendTo('#resource-topic-table tr'));
+    }
+
+    topicParent.append(
+        $('<li>').append(
+          $('<h3>').append(
+            $('<a>')
+              .attr('href', toRoot + "resources/browser.html?tag=" + topics[i].name)
+              .append($('<span>')
+                .addClass('en')
+                .html(topics[i].title)
+              )
+            )
+          )
+        );
+  }
+})();
+//-->
+</script>
\ No newline at end of file
diff --git a/docs/html/resources/tutorials/notepad/notepad-ex2.jd b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
index 289b5fe..499b796 100644
--- a/docs/html/resources/tutorials/notepad/notepad-ex2.jd
+++ b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
@@ -87,8 +87,8 @@
     menu callback used for the options menu. Here, we add just one line, which will add a menu item
     to delete a note. Call <code>menu.add()</code> like so:
       <pre>
-public boolean onCreateContextMenu(Menu menu, View v
-        ContextMenuInfo menuInfo) {
+public void onCreateContextMenu(Menu menu, View v,
+        ContextMenu.ContextMenuInfo menuInfo) {
     super.onCreateContextMenu(menu, v, menuInfo);
     menu.add(0, DELETE_ID, 0, R.string.menu_delete);
 }</pre>
diff --git a/drm/common/IDrmManagerService.cpp b/drm/common/IDrmManagerService.cpp
index b8ae852..c3d12f0 100644
--- a/drm/common/IDrmManagerService.cpp
+++ b/drm/common/IDrmManagerService.cpp
@@ -539,8 +539,6 @@
             handle->decryptInfo = new DecryptInfo();
             handle->decryptInfo->decryptBufferLength = reply.readInt32();
         }
-    } else {
-        LOGE("no decryptHandle is generated in service side");
     }
     return handle;
 }
@@ -1223,8 +1221,6 @@
                 reply->writeInt32(handle->decryptInfo->decryptBufferLength);
                 delete handle->decryptInfo; handle->decryptInfo = NULL;
             }
-        } else {
-            LOGE("NULL decryptHandle is returned");
         }
         delete handle; handle = NULL;
         return DRM_NO_ERROR;
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index b7a035f..35e62f3 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -393,7 +393,6 @@
     }
     if (DRM_NO_ERROR != result) {
         delete handle; handle = NULL;
-        LOGE("DrmManager::openDecryptSession: no capable plug-in found");
     }
     return handle;
 }
diff --git a/drm/libdrmframework/plugins/widevine/Android.mk b/drm/libdrmframework/plugins/widevine/Android.mk
new file mode 100644
index 0000000..2c388da
--- /dev/null
+++ b/drm/libdrmframework/plugins/widevine/Android.mk
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libdrmwvmplugin
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/drm/plugins/native
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index dd83c3b..d283dea3 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -19,12 +19,16 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.DisplayMetrics;
+import android.util.Finalizers;
 
 import java.io.OutputStream;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
 import java.nio.ShortBuffer;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 public final class Bitmap implements Parcelable {
     /**
@@ -35,9 +39,13 @@
      */
     public static final int DENSITY_NONE = 0;
     
-    // Note:  mNativeBitmap is used by FaceDetector_jni.cpp
-    // Don't change/rename without updating FaceDetector_jni.cpp
-    private final int mNativeBitmap;
+    /**
+     * Note:  mNativeBitmap is used by FaceDetector_jni.cpp
+     * Don't change/rename without updating FaceDetector_jni.cpp
+     * 
+     * @hide
+     */
+    public final int mNativeBitmap;
 
     private final boolean mIsMutable;
     private byte[] mNinePatchChunk;   // may be null
@@ -51,7 +59,7 @@
     private static volatile Matrix sScaleMatrix;
 
     private static volatile int sDefaultDensity = -1;
-    
+
     /**
      * For backwards compatibility, allows the app layer to change the default
      * density when running old apps.
@@ -77,8 +85,7 @@
 
         This can be called from JNI code.
     */
-    private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk,
-            int density) {
+    private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk, int density) {
         if (nativeBitmap == 0) {
             throw new RuntimeException("internal error: native bitmap is 0");
         }
@@ -90,6 +97,13 @@
         if (density >= 0) {
             mDensity = density;
         }
+
+        // If the finalizers queue is null, we are running in zygote and the
+        // bitmap will never be reclaimed, so we don't need to run our native
+        // destructor
+        if (Finalizers.getQueue() != null) {
+            new BitmapFinalizer(this);
+        }
     }
 
     /**
@@ -172,6 +186,19 @@
     }
 
     /**
+     * Returns the generation ID of this bitmap. The generation ID changes
+     * whenever the bitmap is modified. This can be used as an efficient way to
+     * check if a bitmap has changed.
+     * 
+     * @return The current generation ID for this bitmap.
+     * 
+     * @hide
+     */
+    public int getGenerationId() {
+        return nativeGenerationId(mNativeBitmap);
+    }
+    
+    /**
      * This is called by methods that want to throw an exception if the bitmap
      * has already been recycled.
      */
@@ -428,28 +455,30 @@
         Rect srcR = new Rect(x, y, x + width, y + height);
         RectF dstR = new RectF(0, 0, width, height);
 
+        final Config newConfig = source.getConfig() == Config.ARGB_8888 ?
+                Config.ARGB_8888 : Config.RGB_565;
+
         if (m == null || m.isIdentity()) {
-            bitmap = createBitmap(neww, newh,
-                    source.hasAlpha() ? Config.ARGB_8888 : Config.RGB_565);
+            bitmap = createBitmap(neww, newh, newConfig, source.hasAlpha());
             paint = null;   // not needed
         } else {
-            /*  the dst should have alpha if the src does, or if our matrix
-                doesn't preserve rectness
-            */
-            boolean hasAlpha = source.hasAlpha() || !m.rectStaysRect();
+            final boolean transformed = !m.rectStaysRect();
+
             RectF deviceR = new RectF();
             m.mapRect(deviceR, dstR);
+
             neww = Math.round(deviceR.width());
             newh = Math.round(deviceR.height());
-            bitmap = createBitmap(neww, newh, hasAlpha ? Config.ARGB_8888 : Config.RGB_565);
-            if (hasAlpha) {
-                bitmap.eraseColor(0);
-            }
+
+            bitmap = createBitmap(neww, newh, transformed ? Config.ARGB_8888 : newConfig,
+                    transformed || source.hasAlpha());
+
             canvas.translate(-deviceR.left, -deviceR.top);
             canvas.concat(m);
+
             paint = new Paint();
             paint.setFilterBitmap(filter);
-            if (!m.rectStaysRect()) {
+            if (transformed) {
                 paint.setAntiAlias(true);
             }
         }
@@ -474,8 +503,30 @@
      * @throws IllegalArgumentException if the width or height are <= 0
      */
     public static Bitmap createBitmap(int width, int height, Config config) {
+        return createBitmap(width, height, config, true);
+    }
+
+    /**
+     * Returns a mutable bitmap with the specified width and height.  Its
+     * initial density is as per {@link #getDensity}.
+     *
+     * @param width    The width of the bitmap
+     * @param height   The height of the bitmap
+     * @param config   The bitmap config to create.
+     * @param hasAlpha If the bitmap is ARGB_8888 this flag can be used to mark the
+     *                 bitmap as opaque. Doing so will clear the bitmap in black
+     *                 instead of transparent.  
+     * 
+     * @throws IllegalArgumentException if the width or height are <= 0
+     */
+    private static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) {
         Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true);
-        bm.eraseColor(0);    // start with black/transparent pixels
+        if (config == Config.ARGB_8888 && !hasAlpha) {
+            bm.eraseColor(0xff000000);
+            nativeSetHasAlpha(bm.mNativeBitmap, hasAlpha);
+        } else {
+            bm.eraseColor(0);
+        }
         return bm;
     }
 
@@ -1008,12 +1059,22 @@
         nativePrepareToDraw(mNativeBitmap);
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
+    private static class BitmapFinalizer extends Finalizers.ReclaimableReference<Bitmap> {
+        private static final Set<BitmapFinalizer> sFinalizers = Collections.synchronizedSet(
+                new HashSet<BitmapFinalizer>());
+
+        private int mNativeBitmap;
+
+        BitmapFinalizer(Bitmap b) {
+            super(b, Finalizers.getQueue());
+            mNativeBitmap = b.mNativeBitmap;
+            sFinalizers.add(this);
+        }
+
+        @Override
+        public void reclaim() {
             nativeDestructor(mNativeBitmap);
-        } finally {
-            super.finalize();
+            sFinalizers.remove(this);
         }
     }
 
@@ -1050,6 +1111,7 @@
     private static native void nativeCopyPixelsToBuffer(int nativeBitmap,
                                                         Buffer dst);
     private static native void nativeCopyPixelsFromBuffer(int nb, Buffer src);
+    private static native int nativeGenerationId(int nativeBitmap);
 
     private static native Bitmap nativeCreateFromParcel(Parcel p);
     // returns true on success
@@ -1065,7 +1127,7 @@
     private static native void nativePrepareToDraw(int nativeBitmap);
     private static native void nativeSetHasAlpha(int nBitmap, boolean hasAlpha);
     private static native boolean nativeSameAs(int nb0, int nb1);
-
+    
     /* package */ final int ni() {
         return mNativeBitmap;
     }
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 7b49bc5..ea5ed6b 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -18,7 +18,6 @@
 
 import android.content.res.AssetManager;
 import android.content.res.Resources;
-import android.os.MemoryFile;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 
@@ -84,7 +83,7 @@
         /**
          * The pixel density to use for the bitmap.  This will always result
          * in the returned bitmap having a density set for it (see
-         * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)).  In addition,
+         * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)}).  In addition,
          * if {@link #inScaled} is set (which it is by default} and this
          * density does not match {@link #inTargetDensity}, then the bitmap
          * will be scaled to the target density before being returned.
@@ -507,9 +506,7 @@
      *
      * @param is The input stream that holds the raw data to be decoded into a
      *           bitmap.
-     * @return The decoded bitmap, or null if the image data could not be
-     *         decoded, or, if opts is non-null, if opts requested only the
-     *         size be returned (in opts.outWidth and opts.outHeight)
+     * @return The decoded bitmap, or null if the image data could not be decoded.
      */
     public static Bitmap decodeStream(InputStream is) {
         return decodeStream(is, null, null);
@@ -530,18 +527,6 @@
      * @return the decoded bitmap, or null
      */
     public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
-        try {
-            if (MemoryFile.isMemoryFile(fd)) {
-                int mappedlength = MemoryFile.getSize(fd);
-                MemoryFile file = new MemoryFile(fd, mappedlength, "r");
-                InputStream is = file.getInputStream();
-                Bitmap bm = decodeStream(is, outPadding, opts);
-                return finishDecode(bm, outPadding, opts);
-            }
-        } catch (IOException ex) {
-            // invalid filedescriptor, no need to call nativeDecodeFileDescriptor()
-            return null;
-        }
         Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
         return finishDecode(bm, outPadding, opts);
     }
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
new file mode 100644
index 0000000..454eb4a
--- /dev/null
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -0,0 +1,263 @@
+/* Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.content.res.AssetManager;
+
+import java.io.BufferedInputStream;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * BitmapRegionDecoder can be used to decode a rectangle region from an image.
+ * BitmapRegionDecoder is particularly useful when an original image is large and
+ * you only need parts of the image.
+ *
+ * <p>To create a BitmapRegionDecoder, call newInstance(...).
+ * Given a BitmapRegionDecoder, users can call decodeRegion() repeatedly
+ * to get a decoded Bitmap of the specified region.
+ *
+ */
+public final class BitmapRegionDecoder {
+    private int mNativeBitmapRegionDecoder;
+    private boolean mRecycled;
+
+    /**
+     * Create a BitmapRegionDecoder from the specified byte array.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param data byte array of compressed image data.
+     * @param offset offset into data for where the decoder should begin
+     *               parsing.
+     * @param length the number of bytes, beginning at offset, to parse
+     * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the BitmapRegionDecoder will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return BitmapRegionDecoder, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     */
+    public static BitmapRegionDecoder newInstance(byte[] data,
+            int offset, int length, boolean isShareable) throws IOException {
+        if ((offset | length) < 0 || data.length < offset + length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        return nativeNewInstance(data, offset, length, isShareable);
+    }
+
+    /**
+     * Create a BitmapRegionDecoder from the file descriptor.
+     * The position within the descriptor will not be changed when
+     * this returns, so the descriptor can be used again as is.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param fd The file descriptor containing the data to decode
+     * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the BitmapRegionDecoder will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return BitmapRegionDecoder, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     */
+    public static BitmapRegionDecoder newInstance(
+            FileDescriptor fd, boolean isShareable) throws IOException {
+        return nativeNewInstance(fd, isShareable);
+    }
+
+    /**
+     * Create a BitmapRegionDecoder from an input stream.
+     * The stream's position will be where ever it was after the encoded data
+     * was read.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param is The input stream that holds the raw data to be decoded into a
+     *           BitmapRegionDecoder.
+     * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the BitmapRegionDecoder will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return BitmapRegionDecoder, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     */
+    public static BitmapRegionDecoder newInstance(InputStream is,
+            boolean isShareable) throws IOException {
+        // we need mark/reset to work properly in JNI
+
+        if (!is.markSupported()) {
+            is = new BufferedInputStream(is, 16 * 1024);
+        }
+
+        if (is instanceof AssetManager.AssetInputStream) {
+            return nativeNewInstance(
+                    ((AssetManager.AssetInputStream) is).getAssetInt(),
+                    isShareable);
+        } else {
+            // pass some temp storage down to the native code. 1024 is made up,
+            // but should be large enough to avoid too many small calls back
+            // into is.read(...).
+            byte [] tempStorage = new byte[16 * 1024];
+            return nativeNewInstance(is, tempStorage, isShareable);
+        }
+    }
+
+    /**
+     * Create a BitmapRegionDecoder from a file path.
+     * Currently only the Jpeg format is supported.
+     *
+     * @param pathName complete path name for the file to be decoded.
+     * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
+     *                    shallow reference to the input. If this is false,
+     *                    then the BitmapRegionDecoder will explicitly make a copy of the
+     *                    input data, and keep that. Even if sharing is allowed,
+     *                    the implementation may still decide to make a deep
+     *                    copy of the input data. If an image is progressively encoded,
+     *                    allowing sharing may degrade the decoding speed.
+     * @return BitmapRegionDecoder, or null if the image data could not be decoded.
+     * @throws IOException if the image format is not supported or can not be decoded.
+     */
+    public static BitmapRegionDecoder newInstance(String pathName,
+            boolean isShareable) throws IOException {
+        BitmapRegionDecoder decoder = null;
+        InputStream stream = null;
+
+        try {
+            stream = new FileInputStream(pathName);
+            decoder = newInstance(stream, isShareable);
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    // do nothing here
+                }
+            }
+        }
+        return decoder;
+    }
+
+    /*  Private constructor that must receive an already allocated native
+        region decoder int (pointer).
+
+        This can be called from JNI code.
+    */
+    private BitmapRegionDecoder(int decoder) {
+        mNativeBitmapRegionDecoder = decoder;
+        mRecycled = false;
+    }
+
+    /**
+     * Decodes a rectangle region in the image specified by rect.
+     *
+     * @param rect The rectangle that specified the region to be decode.
+     * @param options null-ok; Options that control downsampling.
+     *             inPurgeable is not supported.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded.
+     */
+    public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
+        checkRecycled("decodeRegion called on recycled region decoder");
+        if (rect.left < 0 || rect.top < 0 || rect.right > getWidth()
+                || rect.bottom > getHeight())
+            throw new IllegalArgumentException("rectangle is not inside the image");
+        return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
+                rect.right - rect.left, rect.bottom - rect.top, options);
+    }
+
+    /** Returns the original image's width */
+    public int getWidth() {
+        checkRecycled("getWidth called on recycled region decoder");
+        return nativeGetWidth(mNativeBitmapRegionDecoder);
+    }
+
+    /** Returns the original image's height */
+    public int getHeight() {
+        checkRecycled("getHeight called on recycled region decoder");
+        return nativeGetHeight(mNativeBitmapRegionDecoder);
+    }
+
+    /**
+     * Frees up the memory associated with this region decoder, and mark the
+     * region decoder as "dead", meaning it will throw an exception if decodeRegion(),
+     * getWidth() or getHeight() is called.
+     *
+     * <p>This operation cannot be reversed, so it should only be called if you are
+     * sure there are no further uses for the region decoder. This is an advanced call,
+     * and normally need not be called, since the normal GC process will free up this
+     * memory when there are no more references to this region decoder.
+     */
+    public void recycle() {
+        if (!mRecycled) {
+            nativeClean(mNativeBitmapRegionDecoder);
+            mRecycled = true;
+        }
+    }
+
+    /**
+     * Returns true if this region decoder has been recycled.
+     * If so, then it is an error to try use its method.
+     *
+     * @return true if the region decoder has been recycled
+     */
+    public final boolean isRecycled() {
+        return mRecycled;
+    }
+
+    /**
+     * Called by methods that want to throw an exception if the region decoder
+     * has already been recycled.
+     */
+    private void checkRecycled(String errorMessage) {
+        if (mRecycled) {
+            throw new IllegalStateException(errorMessage);
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            recycle();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private static native Bitmap nativeDecodeRegion(int lbm,
+            int start_x, int start_y, int width, int height,
+            BitmapFactory.Options options);
+    private static native int nativeGetWidth(int lbm);
+    private static native int nativeGetHeight(int lbm);
+    private static native void nativeClean(int lbm);
+
+    private static native BitmapRegionDecoder nativeNewInstance(
+            byte[] data, int offset, int length, boolean isShareable);
+    private static native BitmapRegionDecoder nativeNewInstance(
+            FileDescriptor fd, boolean isShareable);
+    private static native BitmapRegionDecoder nativeNewInstance(
+            InputStream is, byte[] storage, boolean isShareable);
+    private static native BitmapRegionDecoder nativeNewInstance(
+            int asset, boolean isShareable);
+}
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 612b0ab..4ba679b 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -16,10 +16,16 @@
 
 package android.graphics;
 
+/**
+ * Shader used to draw a bitmap as a texture. The bitmap can be repeated or
+ * mirrored by setting the tiling mode.
+ */
 public class BitmapShader extends Shader {
-
-    // we hold on just for the GC, since our native counterpart is using it
-    private Bitmap mBitmap;
+    /**
+     * Prevent garbage collection.
+     */
+    @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
+    private final Bitmap mBitmap;
 
     /**
      * Call this to create a new shader that will draw with a bitmap.
@@ -30,12 +36,13 @@
      */
     public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY) {
         mBitmap = bitmap;
-        native_instance = nativeCreate(bitmap.ni(),
-                                       tileX.nativeInt, tileY.nativeInt);
+        final int b = bitmap.ni();
+        native_instance = nativeCreate(b, tileX.nativeInt, tileY.nativeInt);
+        native_shader = nativePostCreate(native_instance, b, tileX.nativeInt, tileY.nativeInt);
     }
 
-    private static native int nativeCreate(int native_bitmap,
-                                           int shaderTileModeX,
-                                           int shaderTileModeY);    
+    private static native int nativeCreate(int native_bitmap, int shaderTileModeX,
+            int shaderTileModeY);
+    private static native int nativePostCreate(int native_shader, int native_bitmap,
+            int shaderTileModeX, int shaderTileModeY);
 }
-
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index a587d0d1..07fe66c 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -16,11 +16,10 @@
 
 package android.graphics;
 
-import android.text.TextUtils;
-import android.text.SpannedString;
-import android.text.SpannableString;
 import android.text.GraphicsOperations;
-import android.util.DisplayMetrics;
+import android.text.SpannableString;
+import android.text.SpannedString;
+import android.text.TextUtils;
 
 import javax.microedition.khronos.opengles.GL;
 
@@ -43,22 +42,39 @@
         for both to be null.
     */
     private Bitmap  mBitmap;    // if not null, mGL must be null
-    private GL      mGL;        // if not null, mBitmap must be null
     
     // optional field set by the caller
-    private DrawFilter  mDrawFilter;
+    private DrawFilter mDrawFilter;
 
-    // Package-scoped for quick access.
-    /*package*/ int mDensity = Bitmap.DENSITY_NONE;
+    /**
+     * @hide
+     */
+    protected int mDensity = Bitmap.DENSITY_NONE;
 
-    // Used to determine when compatibility scaling is in effect.
-    private int mScreenDensity = Bitmap.DENSITY_NONE;
+    /**
+     * Used to determine when compatibility scaling is in effect.
+     * 
+     * @hide
+     */
+    protected int mScreenDensity = Bitmap.DENSITY_NONE;
     
     // Used by native code
     @SuppressWarnings({"UnusedDeclaration"})
     private int         mSurfaceFormat;
 
     /**
+     * Flag for drawTextRun indicating left-to-right run direction.
+     * @hide
+     */
+    public static final int DIRECTION_LTR = 0;
+    
+    /**
+     * Flag for drawTextRun indicating right-to-left run direction.
+     * @hide
+     */
+    public static final int DIRECTION_RTL = 1;
+    
+    /**
      * Construct an empty raster canvas. Use setBitmap() to specify a bitmap to
      * draw into.  The initial target density is {@link Bitmap#DENSITY_NONE};
      * this will typically be replaced when a target bitmap is set for the
@@ -89,47 +105,37 @@
         mDensity = bitmap.mDensity;
     }
     
-    /*package*/ Canvas(int nativeCanvas) {
+    Canvas(int nativeCanvas) {
         if (nativeCanvas == 0) {
             throw new IllegalStateException();
         }
         mNativeCanvas = nativeCanvas;
         mDensity = Bitmap.getDefaultDensity();
     }
-    
+
     /**
-     * Construct a canvas with the specified gl context. All drawing through
-     * this canvas will be redirected to OpenGL. Note: some features may not
-     * be supported in this mode (e.g. some GL implementations may not support
-     * antialiasing or certain effects like ColorMatrix or certain Xfermodes).
-     * However, no exception will be thrown in those cases.
+     * Returns null.
      * 
-     * <p>The initial target density of the canvas is the same as the initial
-     * density of bitmaps as per {@link Bitmap#getDensity() Bitmap.getDensity()}.
+     * @deprecated This method is not supported and should not be invoked.
      */
-    public Canvas(GL gl) {
-        mNativeCanvas = initGL();
-        mGL = gl;
-        mDensity = Bitmap.getDefaultDensity();
+    @Deprecated
+    protected GL getGL() {
+        return null;
     }
-    
+
     /**
-     * Return the GL object associated with this canvas, or null if it is not
-     * backed by GL.
+     * Indicates whether this Canvas uses hardware acceleration.
+     * 
+     * Note that this method does not define what type of hardware acceleration
+     * may or may not be used.
+     * 
+     * @return True if drawing operations are hardware accelerated,
+     *         false otherwise.
      */
-    public GL getGL() {
-        return mGL;
+    public boolean isHardwareAccelerated() {
+        return false;
     }
-    
-    /**
-     * Call this to free up OpenGL resources that may be cached or allocated
-     * on behalf of the Canvas. Any subsequent drawing with a GL-backed Canvas
-     * will have to recreate those resources.
-     */
-    public static void freeGlCaches() {
-        freeCaches();
-    }
-        
+
     /**
      * Specify a bitmap for the canvas to draw into.  As a side-effect, also
      * updates the canvas's target density to match that of the bitmap.
@@ -143,7 +149,7 @@
         if (!bitmap.isMutable()) {
             throw new IllegalStateException();
         }
-        if (mGL != null) {
+        if (isHardwareAccelerated()) {
             throw new RuntimeException("Can't set a bitmap device on a GL canvas");
         }
         throwIfRecycled(bitmap);
@@ -157,13 +163,12 @@
      * Set the viewport dimensions if this canvas is GL based. If it is not,
      * this method is ignored and no exception is thrown.
      *
-     *  @param width    The width of the viewport
-     *  @param height   The height of the viewport
+     * @param width The width of the viewport
+     * @param height The height of the viewport
+     * 
+     * @hide
      */
     public void setViewport(int width, int height) {
-        if (mGL != null) {
-            nativeSetViewport(mNativeCanvas, width, height);
-        }
     }
 
     /**
@@ -377,8 +382,8 @@
      *
      * @param sx The amount to scale in X
      * @param sy The amount to scale in Y
-     * @param px The x-coord for the pivot point (unchanged by the rotation)
-     * @param py The y-coord for the pivot point (unchanged by the rotation)
+     * @param px The x-coord for the pivot point (unchanged by the scale)
+     * @param py The y-coord for the pivot point (unchanged by the scale)
      */
     public final void scale(float sx, float sy, float px, float py) {
         translate(px, py);
@@ -442,6 +447,16 @@
     public void getMatrix(Matrix ctm) {
         native_getCTM(mNativeCanvas, ctm.native_instance);
     }
+
+    /**
+     * Returns a pointer to an internal 4x4 native matrix. The returned
+     * pointer is a pointer to an array of 16 floats.
+     * 
+     * @hide
+     */
+    public int getNativeMatrix() {
+        return 0;
+    }
     
     /**
      * Return a new matrix with a copy of the canvas' current transformation
@@ -621,7 +636,11 @@
         EdgeType(int nativeInt) {
             this.nativeInt = nativeInt;
         }
-        final int nativeInt;
+
+        /**
+         * @hide
+         */
+        public final int nativeInt;
     }
 
     /**
@@ -958,6 +977,21 @@
     }
 
     /**
+     * Draws the specified bitmap as an N-patch (most often, a 9-patches.)
+     *
+     * Note: Only supported by hardware accelerated canvas at the moment.
+     *
+     * @param bitmap The bitmap to draw as an N-patch
+     * @param chunks The patches information (matches the native struct Res_png_9patch)
+     * @param dst The destination rectangle.
+     * @param paint The paint to draw the bitmap with. may be null
+     * 
+     * @hide
+     */
+    public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
+    }    
+    
+    /**
      * Draw the specified bitmap, with its top/left corner at (x,y), using
      * the specified paint, transformed by the current matrix.
      * 
@@ -1246,8 +1280,8 @@
             (text.length - index - count)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        native_drawText(mNativeCanvas, text, index, count, x, y,
-                        paint.mNativePaint);
+        native_drawText(mNativeCanvas, text, index, count, x, y, paint.mBidiFlags,
+                paint.mNativePaint);
     }
 
     /**
@@ -1259,7 +1293,10 @@
      * @param y     The y-coordinate of the origin of the text being drawn
      * @param paint The paint used for the text (e.g. color, size, style)
      */
-    public native void drawText(String text, float x, float y, Paint paint);
+    public void drawText(String text, float x, float y, Paint paint) {
+        native_drawText(mNativeCanvas, text, 0, text.length(), x, y, paint.mBidiFlags,
+                paint.mNativePaint);
+    }
 
     /**
      * Draw the text, with origin at (x,y), using the specified paint.
@@ -1277,8 +1314,8 @@
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        native_drawText(mNativeCanvas, text, start, end, x, y,
-                        paint.mNativePaint);
+        native_drawText(mNativeCanvas, text, start, end, x, y, paint.mBidiFlags,
+                paint.mNativePaint);
     }
 
     /**
@@ -1299,16 +1336,108 @@
         if (text instanceof String || text instanceof SpannedString ||
             text instanceof SpannableString) {
             native_drawText(mNativeCanvas, text.toString(), start, end, x, y,
-                            paint.mNativePaint);
-        }
-        else if (text instanceof GraphicsOperations) {
+                            paint.mBidiFlags, paint.mNativePaint);
+        } else if (text instanceof GraphicsOperations) {
             ((GraphicsOperations) text).drawText(this, start, end, x, y,
                                                      paint);
-        }
-        else {
+        } else {
             char[] buf = TemporaryBuffer.obtain(end - start);
             TextUtils.getChars(text, start, end, buf, 0);
-            drawText(buf, 0, end - start, x, y, paint);
+            native_drawText(mNativeCanvas, buf, 0, end - start, x, y, 
+                    paint.mBidiFlags, paint.mNativePaint);
+            TemporaryBuffer.recycle(buf);
+        }
+    }
+
+    /**
+     * Render a run of all LTR or all RTL text, with shaping. This does not run
+     * bidi on the provided text, but renders it as a uniform right-to-left or
+     * left-to-right run, as indicated by dir. Alignment of the text is as
+     * determined by the Paint's TextAlign value.
+     * 
+     * @param text the text to render
+     * @param index the start of the text to render
+     * @param count the count of chars to render
+     * @param contextIndex the start of the context for shaping.  Must be
+     *         no greater than index.
+     * @param contextCount the number of characters in the context for shaping.
+     *         ContexIndex + contextCount must be no less than index
+     *         + count.
+     * @param x the x position at which to draw the text
+     * @param y the y position at which to draw the text
+     * @param dir the run direction, either {@link #DIRECTION_LTR} or
+     *         {@link #DIRECTION_RTL}.
+     * @param paint the paint
+     * @hide
+     */
+    public void drawTextRun(char[] text, int index, int count,
+            int contextIndex, int contextCount, float x, float y, int dir,
+            Paint paint) {
+
+        if (text == null) {
+            throw new NullPointerException("text is null");
+        }
+        if (paint == null) {
+            throw new NullPointerException("paint is null");
+        }
+        if ((index | count | text.length - index - count) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) {
+            throw new IllegalArgumentException("unknown dir: " + dir);
+        }
+
+        native_drawTextRun(mNativeCanvas, text, index, count,
+                contextIndex, contextCount, x, y, dir, paint.mNativePaint);
+    }
+
+    /**
+     * Render a run of all LTR or all RTL text, with shaping. This does not run
+     * bidi on the provided text, but renders it as a uniform right-to-left or
+     * left-to-right run, as indicated by dir. Alignment of the text is as
+     * determined by the Paint's TextAlign value.
+     *
+     * @param text the text to render
+     * @param start the start of the text to render. Data before this position
+     *            can be used for shaping context.
+     * @param end the end of the text to render. Data at or after this
+     *            position can be used for shaping context.
+     * @param x the x position at which to draw the text
+     * @param y the y position at which to draw the text
+     * @param dir the run direction, either 0 for LTR or 1 for RTL.
+     * @param paint the paint
+     * @hide
+     */
+    public void drawTextRun(CharSequence text, int start, int end,
+            int contextStart, int contextEnd, float x, float y, int dir,
+            Paint paint) {
+
+        if (text == null) {
+            throw new NullPointerException("text is null");
+        }
+        if (paint == null) {
+            throw new NullPointerException("paint is null");
+        }
+        if ((start | end | end - start | text.length() - end) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        int flags = dir == 0 ? 0 : 1;
+
+        if (text instanceof String || text instanceof SpannedString ||
+                text instanceof SpannableString) {
+            native_drawTextRun(mNativeCanvas, text.toString(), start, end,
+                    contextStart, contextEnd, x, y, flags, paint.mNativePaint);
+        } else if (text instanceof GraphicsOperations) {
+            ((GraphicsOperations) text).drawTextRun(this, start, end,
+                    contextStart, contextEnd, x, y, flags, paint);
+        } else {
+            int contextLen = contextEnd - contextStart;
+            int len = end - start;
+            char[] buf = TemporaryBuffer.obtain(contextLen);
+            TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
+            native_drawTextRun(mNativeCanvas, buf, start - contextStart, len,
+                    0, contextLen, x, y, flags, paint.mNativePaint);
             TemporaryBuffer.recycle(buf);
         }
     }
@@ -1368,7 +1497,7 @@
         }
         native_drawTextOnPath(mNativeCanvas, text, index, count,
                               path.ni(), hOffset, vOffset,
-                              paint.mNativePaint);
+                              paint.mBidiFlags, paint.mNativePaint);
     }
 
     /**
@@ -1388,7 +1517,8 @@
                                float vOffset, Paint paint) {
         if (text.length() > 0) {
             native_drawTextOnPath(mNativeCanvas, text, path.ni(),
-                                  hOffset, vOffset, paint.mNativePaint);
+                                  hOffset, vOffset, paint.mBidiFlags,
+                                  paint.mNativePaint);
         }
     }
 
@@ -1431,28 +1561,74 @@
         drawPicture(picture);
         restore();
     }
-    
+
+    /**
+     * <p>Acquires the Canvas context. After invoking this method, the Canvas
+     * context  can be modified by the caller. For instance, if you acquire
+     * the context of an OpenGL Canvas you can reset the GL viewport, scissor,
+     * etc.</p>
+     * 
+     * <p>A call to {@link #acquireContext()} should aways be followed by
+     * a call to {@link #releaseContext()}, preferrably using a try block:</p>
+     * 
+     * <pre>
+     * try {
+     *     if (canvas.acquireContext()) {
+     *         // Use the canvas and/or its context
+     *     }
+     * } finally {
+     *     canvas.releaseContext();
+     * }
+     * </pre>
+     * 
+     * <p>Acquiring the context can be an expensive operation and should not
+     * be done unless absolutely necessary.</p>
+     * 
+     * <p>Applications should never invoke this method directly.</p>
+     * 
+     * @return True if the context could be acquired successfully, false
+     *         otherwise (if the context is already acquired for instance.)
+     * 
+     * @see #releaseContext() 
+     * 
+     * @hide
+     */
+    public boolean acquireContext() {
+        return false;
+    }
+
+    /**
+     * <p>Release the context acquired with {@link #acquireContext()}.</p>
+     * 
+     * @see #acquireContext() 
+     * 
+     * @hide
+     */
+    public void releaseContext() {
+    }
+
+    @Override
     protected void finalize() throws Throwable {
-        super.finalize();
-        // If the constructor threw an exception before setting mNativeCanvas, the native finalizer
-        // must not be invoked.
-        if (mNativeCanvas != 0) {
-            finalizer(mNativeCanvas);
+        try {
+            super.finalize();
+        } finally {
+            // If the constructor threw an exception before setting mNativeCanvas,
+            // the native finalizer must not be invoked.
+            if (mNativeCanvas != 0) {
+                finalizer(mNativeCanvas);
+            }
         }
     }
 
     /**
-     * Free up as much memory as possible from private caches (e.g. fonts,
-     * images)
+     * Free up as much memory as possible from private caches (e.g. fonts, images)
      *
-     * @hide - for now
+     * @hide
      */
     public static native void freeCaches();
 
     private static native int initRaster(int nativeBitmapOrZero);
-    private static native int initGL();
     private static native void native_setBitmap(int nativeCanvas, int bitmap);
-    private static native void nativeSetViewport(int nCanvas, int w, int h);
     private static native int native_saveLayer(int nativeCanvas, RectF bounds,
                                                int paint, int layerFlags);
     private static native int native_saveLayer(int nativeCanvas, float l,
@@ -1555,10 +1731,19 @@
     
     private static native void native_drawText(int nativeCanvas, char[] text,
                                                int index, int count, float x,
-                                               float y, int paint);
+                                               float y, int flags, int paint);
     private static native void native_drawText(int nativeCanvas, String text,
                                                int start, int end, float x,
-                                               float y, int paint);
+                                               float y, int flags, int paint);
+
+    private static native void native_drawTextRun(int nativeCanvas, String text,
+            int start, int end, int contextStart, int contextEnd,
+            float x, float y, int flags, int paint);
+
+    private static native void native_drawTextRun(int nativeCanvas, char[] text,
+            int start, int count, int contextStart, int contextCount,
+            float x, float y, int flags, int paint);
+
     private static native void native_drawPosText(int nativeCanvas,
                                                   char[] text, int index,
                                                   int count, float[] pos,
@@ -1570,11 +1755,13 @@
                                                      char[] text, int index,
                                                      int count, int path,
                                                      float hOffset,
-                                                     float vOffset, int paint);
+                                                     float vOffset, int bidiFlags,
+                                                     int paint);
     private static native void native_drawTextOnPath(int nativeCanvas,
                                                      String text, int path,
-                                                     float hOffset,
-                                                     float vOffset, int paint);
+                                                     float hOffset, 
+                                                     float vOffset, 
+                                                     int flags, int paint);
     private static native void native_drawPicture(int nativeCanvas,
                                                   int nativePicture);
     private static native void finalizer(int nativeCanvas);
diff --git a/graphics/java/android/graphics/ColorFilter.java b/graphics/java/android/graphics/ColorFilter.java
index 76f2c7f..e5cf830 100644
--- a/graphics/java/android/graphics/ColorFilter.java
+++ b/graphics/java/android/graphics/ColorFilter.java
@@ -23,12 +23,20 @@
 
 
 public class ColorFilter {
+    int native_instance;
+
+    /**
+     * @hide
+     */
+    public int nativeColorFilter;
 
     protected void finalize() throws Throwable {
-        finalizer(native_instance);
+        try {
+            super.finalize();
+        } finally {
+            finalizer(native_instance, nativeColorFilter);
+        }
     }
 
-    private static native void finalizer(int native_instance);
-
-    int native_instance;
+    private static native void finalizer(int native_instance, int nativeColorFilter);
 }
diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java
index 5d73cff..4f32342 100644
--- a/graphics/java/android/graphics/ColorMatrixColorFilter.java
+++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java
@@ -25,7 +25,9 @@
      *               is constructed will not be reflected in the filter.
      */
     public ColorMatrixColorFilter(ColorMatrix matrix) {
-        native_instance = nativeColorMatrixFilter(matrix.getArray());
+        final float[] colorMatrix = matrix.getArray();
+        native_instance = nativeColorMatrixFilter(colorMatrix);
+        nativeColorFilter = nColorMatrixFilter(native_instance, colorMatrix);
     }
 
     /**
@@ -40,7 +42,9 @@
             throw new ArrayIndexOutOfBoundsException();
         }
         native_instance = nativeColorMatrixFilter(array);
+        nativeColorFilter = nColorMatrixFilter(native_instance, array);
     }
 
     private static native int nativeColorMatrixFilter(float[] array);
+    private static native int nColorMatrixFilter(int nativeFilter, float[] array);
 }
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index a06d30b..8d5c913 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -20,6 +20,14 @@
     an {@link android.graphics.Xfermode} subclass.
 */
 public class ComposeShader extends Shader {
+    /**
+     * Hold onto the shaders to avoid GC.
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    private final Shader mShaderA;
+    @SuppressWarnings({"UnusedDeclaration"})
+    private final Shader mShaderB;
+
     /** Create a new compose shader, given shaders A, B, and a combining mode.
         When the mode is applied, it will be given the result from shader A as its
         "dst", and the result of from shader B as its "src".
@@ -29,8 +37,18 @@
                         is null, then SRC_OVER is assumed.
     */
     public ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode) {
+        mShaderA = shaderA;
+        mShaderB = shaderB;
         native_instance = nativeCreate1(shaderA.native_instance, shaderB.native_instance,
-                                        (mode != null) ? mode.native_instance : 0);
+                (mode != null) ? mode.native_instance : 0);
+        if (mode instanceof PorterDuffXfermode) {
+            PorterDuff.Mode pdMode = ((PorterDuffXfermode) mode).mode;
+            native_shader = nativePostCreate2(native_instance, shaderA.native_shader,
+                    shaderB.native_shader, pdMode != null ? pdMode.nativeInt : 0);
+        } else {
+            native_shader = nativePostCreate1(native_instance, shaderA.native_shader,
+                    shaderB.native_shader, mode != null ? mode.native_instance : 0);
+        }
     }
 
     /** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
@@ -41,11 +59,20 @@
         @param mode     The PorterDuff mode that combines the colors from the two shaders.
     */
     public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) {
+        mShaderA = shaderA;
+        mShaderB = shaderB;
         native_instance = nativeCreate2(shaderA.native_instance, shaderB.native_instance,
-                                        mode.nativeInt);
+                mode.nativeInt);
+        native_shader = nativePostCreate2(native_instance, shaderA.native_shader,
+                shaderB.native_shader, mode.nativeInt);
     }
 
-    private static native int nativeCreate1(int native_shaderA, int native_shaderB, int native_mode);
-    private static native int nativeCreate2(int native_shaderA, int native_shaderB, int porterDuffMode);
+    private static native int nativeCreate1(int native_shaderA, int native_shaderB,
+            int native_mode);
+    private static native int nativeCreate2(int native_shaderA, int native_shaderB,
+            int porterDuffMode);
+    private static native int nativePostCreate1(int native_shader, int native_skiaShaderA,
+            int native_skiaShaderB, int native_mode);
+    private static native int nativePostCreate2(int native_shader, int native_skiaShaderA,
+            int native_skiaShaderB, int porterDuffMode);
 }
-
diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java
index 5562389..c621de6 100644
--- a/graphics/java/android/graphics/LightingColorFilter.java
+++ b/graphics/java/android/graphics/LightingColorFilter.java
@@ -30,7 +30,9 @@
      */
     public LightingColorFilter(int mul, int add) {
         native_instance = native_CreateLightingFilter(mul, add);
+        nativeColorFilter = nCreateLightingFilter(native_instance, mul, add);
     }
 
     private static native int native_CreateLightingFilter(int mul, int add);
+    private static native int nCreateLightingFilter(int nativeFilter, int mul, int add);
 }
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index e3db105..82ed199 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -17,7 +17,6 @@
 package android.graphics;
 
 public class LinearGradient extends Shader {
-
 	/**	Create a shader that draws a linear gradient along a line.
         @param x0           The x-coordinate for the start of the gradient line
         @param y0           The y-coordinate for the start of the gradient line
@@ -38,6 +37,8 @@
             throw new IllegalArgumentException("color and position arrays must be of equal length");
         }
         native_instance = nativeCreate1(x0, y0, x1, y1, colors, positions, tile.nativeInt);
+        native_shader = nativePostCreate1(native_instance, x0, y0, x1, y1, colors, positions,
+                tile.nativeInt);
     }
 
 	/**	Create a shader that draws a linear gradient along a line.
@@ -52,12 +53,16 @@
 	public LinearGradient(float x0, float y0, float x1, float y1,
                           int color0, int color1, TileMode tile) {
         native_instance = nativeCreate2(x0, y0, x1, y1, color0, color1, tile.nativeInt);
+        native_shader = nativePostCreate2(native_instance, x0, y0, x1, y1, color0, color1,
+                tile.nativeInt);
     }
 
-
-	private static native int nativeCreate1(float x0, float y0, float x1, float y1,
-                                            int colors[], float positions[], int tileMode);
-	private static native int nativeCreate2(float x0, float y0, float x1, float y1,
-                                            int color0, int color1, int tileMode);
+	private native int nativeCreate1(float x0, float y0, float x1, float y1,
+            int colors[], float positions[], int tileMode);
+	private native int nativeCreate2(float x0, float y0, float x1, float y1,
+            int color0, int color1, int tileMode);
+    private native int nativePostCreate1(int native_shader, float x0, float y0, float x1, float y1,
+            int colors[], float positions[], int tileMode);
+    private native int nativePostCreate2(int native_shader, float x0, float y0, float x1, float y1,
+            int color0, int color1, int tileMode);
 }
-
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index f549900..66ed104a 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -37,7 +37,10 @@
     public static final int MPERSP_1 = 7;   //!< use with getValues/setValues
     public static final int MPERSP_2 = 8;   //!< use with getValues/setValues
 
-    /* package */ int native_instance;
+    /**
+     * @hide
+     */
+    public int native_instance;
 
     /**
      * Create an identity matrix
@@ -419,6 +422,10 @@
      * the transformed vectors into the array of vectors specified by dst. The
      * two arrays represent their "vectors" as pairs of floats [x, y].
      *
+     * Note: this method does not apply the translation associated with the matrix. Use
+     * {@link Matrix#mapPoints(float[], int, float[], int, int)} if you want the translation
+     * to be applied.
+     *
      * @param dst   The array of dst vectors (x,y pairs)
      * @param dstIndex The index of the first [x,y] pair of dst floats
      * @param src   The array of src vectors (x,y pairs)
@@ -452,6 +459,9 @@
      * the transformed vectors into the array of vectors specified by dst. The
      * two arrays represent their "vectors" as pairs of floats [x, y].
      *
+     * Note: this method does not apply the translation associated with the matrix. Use
+     * {@link Matrix#mapPoints(float[], float[])} if you want the translation to be applied.
+     *
      * @param dst   The array of dst vectors (x,y pairs)
      * @param src   The array of src vectors (x,y pairs)
      */
@@ -475,6 +485,10 @@
     /**
      * Apply this matrix to the array of 2D vectors, and write the transformed
      * vectors back into the array.
+     *
+     * Note: this method does not apply the translation associated with the matrix. Use
+     * {@link Matrix#mapPoints(float[])} if you want the translation to be applied.
+     *
      * @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform.
      */
     public void mapVectors(float[] vecs) {
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 88dfd67..6de4d84 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -35,6 +35,12 @@
  * </p>
  */
 public class NinePatch {
+    private final Bitmap mBitmap;
+    private final byte[] mChunk;
+    private Paint mPaint;
+    private String mSrcName;  // Useful for debugging
+    private final RectF mRect = new RectF();
+    
     /** 
      * Create a drawable projection from a bitmap to nine patches.
      *
@@ -74,10 +80,14 @@
      * @param location  Where to draw the bitmap.
      */
     public void draw(Canvas canvas, RectF location) {
-        nativeDraw(canvas.mNativeCanvas, location,
-                   mBitmap.ni(), mChunk,
-                   mPaint != null ? mPaint.mNativePaint : 0,
-                   canvas.mDensity, mBitmap.mDensity);
+        if (!canvas.isHardwareAccelerated()) {
+            nativeDraw(canvas.mNativeCanvas, location,
+                       mBitmap.ni(), mChunk,
+                       mPaint != null ? mPaint.mNativePaint : 0,
+                       canvas.mDensity, mBitmap.mDensity);
+        } else {
+            canvas.drawPatch(mBitmap, mChunk, location, mPaint);
+        }
     }
     
     /** 
@@ -87,10 +97,15 @@
      * @param location  Where to draw the bitmap.
      */
     public void draw(Canvas canvas, Rect location) {
-        nativeDraw(canvas.mNativeCanvas, location,
-                mBitmap.ni(), mChunk,
-                mPaint != null ? mPaint.mNativePaint : 0,
-                canvas.mDensity, mBitmap.mDensity);
+        if (!canvas.isHardwareAccelerated()) {
+            nativeDraw(canvas.mNativeCanvas, location,
+                        mBitmap.ni(), mChunk,
+                        mPaint != null ? mPaint.mNativePaint : 0,
+                        canvas.mDensity, mBitmap.mDensity);
+        } else {
+            mRect.set(location);
+            canvas.drawPatch(mBitmap, mChunk, mRect, mPaint);
+        }
     }
 
     /** 
@@ -101,9 +116,14 @@
      * @param paint     The Paint to draw through.
      */
     public void draw(Canvas canvas, Rect location, Paint paint) {
-        nativeDraw(canvas.mNativeCanvas, location,
-                mBitmap.ni(), mChunk, paint != null ? paint.mNativePaint : 0,
-                canvas.mDensity, mBitmap.mDensity);
+        if (!canvas.isHardwareAccelerated()) {
+            nativeDraw(canvas.mNativeCanvas, location,
+                    mBitmap.ni(), mChunk, paint != null ? paint.mNativePaint : 0,
+                    canvas.mDensity, mBitmap.mDensity);
+        } else {
+            mRect.set(location);
+            canvas.drawPatch(mBitmap, mChunk, mRect, paint);
+        }
     }
 
     /**
@@ -133,11 +153,6 @@
     
     public native static boolean isNinePatchChunk(byte[] chunk);
 
-    private final Bitmap mBitmap;
-    private final byte[] mChunk;
-    private Paint        mPaint;
-    private String       mSrcName;  // Useful for debugging
-
     private static native void validateNinePatchChunk(int bitmap, byte[] chunk);
     private static native void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance,
                                           byte[] c, int paint_instance_or_null,
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 3e3f87b..62fbfb4 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -16,10 +16,10 @@
 
 package android.graphics;
 
-import android.text.TextUtils;
+import android.text.GraphicsOperations;
 import android.text.SpannableString;
 import android.text.SpannedString;
-import android.text.GraphicsOperations;
+import android.text.TextUtils;
 
 /**
  * The Paint class holds the style and color information about how to draw
@@ -27,7 +27,11 @@
  */
 public class Paint {
 
-    /*package*/ int     mNativePaint;
+    /**
+     * @hide
+     */
+    public int mNativePaint;
+
     private ColorFilter mColorFilter;
     private MaskFilter  mMaskFilter;
     private PathEffect  mPathEffect;
@@ -39,6 +43,32 @@
     private boolean     mHasCompatScaling;
     private float       mCompatScaling;
     private float       mInvCompatScaling;
+
+    /**
+     * @hide
+     */
+    public boolean hasShadow;
+    /**
+     * @hide
+     */
+    public float shadowDx;
+    /**
+     * @hide
+     */
+    public float shadowDy;
+    /**
+     * @hide
+     */
+    public float shadowRadius;
+    /**
+     * @hide
+     */
+    public int shadowColor;
+
+    /**
+     * @hide
+     */
+    public  int         mBidiFlags = BIDI_DEFAULT_LTR;
     
     private static final Style[] sStyleArray = {
         Style.FILL, Style.STROKE, Style.FILL_AND_STROKE
@@ -76,8 +106,116 @@
     private static final int DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG;
 
     /**
-     * The Style specifies if the primitive being drawn is filled,
-     * stroked, or both (in the same color). The default is FILL.
+     * Bidi flag to set LTR paragraph direction.
+     * 
+     * @hide
+     */
+    public static final int BIDI_LTR = 0x0;
+
+    /**
+     * Bidi flag to set RTL paragraph direction.
+     * 
+     * @hide
+     */
+    public static final int BIDI_RTL = 0x1;
+
+    /**
+     * Bidi flag to detect paragraph direction via heuristics, defaulting to
+     * LTR.
+     * 
+     * @hide
+     */
+    public static final int BIDI_DEFAULT_LTR = 0x2;
+
+    /**
+     * Bidi flag to detect paragraph direction via heuristics, defaulting to
+     * RTL.
+     * 
+     * @hide
+     */
+    public static final int BIDI_DEFAULT_RTL = 0x3;
+
+    /**
+     * Bidi flag to override direction to all LTR (ignore bidi).
+     * 
+     * @hide
+     */
+    public static final int BIDI_FORCE_LTR = 0x4;
+
+    /**
+     * Bidi flag to override direction to all RTL (ignore bidi).
+     * 
+     * @hide
+     */
+    public static final int BIDI_FORCE_RTL = 0x5;
+
+    /**
+     * Maximum Bidi flag value.
+     * @hide
+     */
+    private static final int BIDI_MAX_FLAG_VALUE = BIDI_FORCE_RTL;
+
+    /**
+     * Mask for bidi flags.
+     * @hide
+     */
+    private static final int BIDI_FLAG_MASK = 0x7;
+
+    /**
+     * Flag for getTextRunAdvances indicating left-to-right run direction.
+     * @hide
+     */
+    public static final int DIRECTION_LTR = 0;
+
+    /**
+     * Flag for getTextRunAdvances indicating right-to-left run direction.
+     * @hide
+     */
+    public static final int DIRECTION_RTL = 1;
+
+    /**
+     * Option for getTextRunCursor to compute the valid cursor after
+     * offset or the limit of the context, whichever is less.
+     * @hide
+     */
+    public static final int CURSOR_AFTER = 0;
+
+    /**
+     * Option for getTextRunCursor to compute the valid cursor at or after
+     * the offset or the limit of the context, whichever is less.
+     * @hide
+     */
+    public static final int CURSOR_AT_OR_AFTER = 1;
+
+     /**
+     * Option for getTextRunCursor to compute the valid cursor before
+     * offset or the start of the context, whichever is greater.
+     * @hide
+     */
+    public static final int CURSOR_BEFORE = 2;
+
+   /**
+     * Option for getTextRunCursor to compute the valid cursor at or before
+     * offset or the start of the context, whichever is greater.
+     * @hide
+     */
+    public static final int CURSOR_AT_OR_BEFORE = 3;
+
+    /**
+     * Option for getTextRunCursor to return offset if the cursor at offset
+     * is valid, or -1 if it isn't.
+     * @hide
+     */
+    public static final int CURSOR_AT = 4;
+
+    /**
+     * Maximum cursor option value.
+     */
+    private static final int CURSOR_OPT_MAX_VALUE = CURSOR_AT;
+
+    /**
+     * The Style specifies if the primitive being drawn is filled, stroked, or
+     * both (in the same color). The default is FILL.
      */
     public enum Style {
         /**
@@ -93,7 +231,9 @@
         /**
          * Geometry and text drawn with this style will be both filled and
          * stroked at the same time, respecting the stroke-related fields on
-         * the paint.
+         * the paint. This mode can give unexpected results if the geometry
+         * is oriented counter-clockwise. This restriction does not apply to
+         * either FILL or STROKE.
          */
         FILL_AND_STROKE (2);
         
@@ -208,6 +348,7 @@
         mHasCompatScaling = paint.mHasCompatScaling;
         mCompatScaling = paint.mCompatScaling;
         mInvCompatScaling = paint.mInvCompatScaling;
+        mBidiFlags = paint.mBidiFlags;
     }
 
     /** Restores the paint to its default settings. */
@@ -216,6 +357,7 @@
         setFlags(DEFAULT_PAINT_FLAGS);
         mHasCompatScaling = false;
         mCompatScaling = mInvCompatScaling = 1;
+        mBidiFlags = BIDI_DEFAULT_LTR;
     }
     
     /**
@@ -238,6 +380,7 @@
             mHasCompatScaling = src.mHasCompatScaling;
             mCompatScaling    = src.mCompatScaling;
             mInvCompatScaling = src.mInvCompatScaling;
+            mBidiFlags      = src.mBidiFlags;
         }
     }
 
@@ -252,10 +395,33 @@
             mInvCompatScaling = 1.0f/factor;
         }
     }
-    
+
+    /**
+     * Return the bidi flags on the paint.
+     * 
+     * @return the bidi flags on the paint
+     * @hide
+     */
+    public int getBidiFlags() {
+        return mBidiFlags;
+    }
+
+    /**
+     * Set the bidi flags on the paint.
+     * @hide
+     */
+    public void setBidiFlags(int flags) {
+        // only flag value is the 3-bit BIDI control setting
+        flags &= BIDI_FLAG_MASK;
+        if (flags > BIDI_MAX_FLAG_VALUE) {
+            throw new IllegalArgumentException("unknown bidi flag: " + flags);
+        }
+        mBidiFlags = flags;
+    }
+
     /**
      * Return the paint's flags. Use the Flag enum to test flag values.
-     *
+     * 
      * @return the paint's flags (see enums ending in _Flag for bit masks)
      */
     public native int getFlags();
@@ -787,18 +953,27 @@
     }
     
     /**
-     * Temporary API to expose layer drawing. This draws a shadow layer below
-     * the main layer, with the specified offset and color, and blur radius.
-     * If radius is 0, then the shadow layer is removed.
+     * This draws a shadow layer below the main layer, with the specified
+     * offset and color, and blur radius. If radius is 0, then the shadow
+     * layer is removed.
      */
-    public native void setShadowLayer(float radius, float dx, float dy,
-                                      int color);
+    public void setShadowLayer(float radius, float dx, float dy, int color) {
+        hasShadow = radius > 0.0f;
+        shadowRadius = radius;
+        shadowDx = dx;
+        shadowDy = dy;
+        shadowColor = color;
+        nSetShadowLayer(radius, dx, dy, color);
+    }
+    
+    private native void nSetShadowLayer(float radius, float dx, float dy, int color);
 
     /**
-     * Temporary API to clear the shadow layer.
+     * Clear the shadow layer.
      */
     public void clearShadowLayer() {
-        setShadowLayer(0, 0, 0, 0);
+        hasShadow = false;
+        nSetShadowLayer(0, 0, 0, 0);
     }
 
     /**
@@ -1232,10 +1407,10 @@
         }
 
         char[] buf = TemporaryBuffer.obtain(end - start);
-    	TextUtils.getChars(text, start, end, buf, 0);
-    	int result = getTextWidths(buf, 0, end - start, widths);
+        TextUtils.getChars(text, start, end, buf, 0);
+        int result = getTextWidths(buf, 0, end - start, widths);
         TemporaryBuffer.recycle(buf);
-    	return result;
+        return result;
     }
 
     /**
@@ -1282,6 +1457,284 @@
     }
 
     /**
+     * Convenience overload that takes a char array instead of a
+     * String.
+     *
+     * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
+     * @hide
+     */
+    public float getTextRunAdvances(char[] chars, int index, int count,
+            int contextIndex, int contextCount, int flags, float[] advances,
+            int advancesIndex) {
+
+        if ((index | count | contextIndex | contextCount | advancesIndex
+                | (index - contextIndex)
+                | ((contextIndex + contextCount) - (index + count))
+                | (chars.length - (contextIndex + contextCount))
+                | (advances == null ? 0 :
+                    (advances.length - (advancesIndex + count)))) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
+            throw new IllegalArgumentException("unknown flags value: " + flags);
+        }
+
+        if (!mHasCompatScaling) {
+            return native_getTextRunAdvances(mNativePaint, chars, index, count,
+                    contextIndex, contextCount, flags, advances, advancesIndex);
+        }
+
+        final float oldSize = getTextSize();
+        setTextSize(oldSize * mCompatScaling);
+        float res = native_getTextRunAdvances(mNativePaint, chars, index, count,
+                contextIndex, contextCount, flags, advances, advancesIndex);
+        setTextSize(oldSize);
+
+        if (advances != null) {
+            for (int i = advancesIndex, e = i + count; i < e; i++) {
+                advances[i] *= mInvCompatScaling;
+            }
+        }
+        return res * mInvCompatScaling; // assume errors are not significant
+    }
+
+    /**
+     * Convenience overload that takes a CharSequence instead of a
+     * String.
+     *
+     * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int)
+     * @hide
+     */
+    public float getTextRunAdvances(CharSequence text, int start, int end,
+            int contextStart, int contextEnd, int flags, float[] advances,
+            int advancesIndex) {
+
+        if (text instanceof String) {
+            return getTextRunAdvances((String) text, start, end,
+                    contextStart, contextEnd, flags, advances, advancesIndex);
+        }
+        if (text instanceof SpannedString ||
+            text instanceof SpannableString) {
+            return getTextRunAdvances(text.toString(), start, end,
+                    contextStart, contextEnd, flags, advances, advancesIndex);
+        }
+        if (text instanceof GraphicsOperations) {
+            return ((GraphicsOperations) text).getTextRunAdvances(start, end,
+                    contextStart, contextEnd, flags, advances, advancesIndex, this);
+        }
+
+        int contextLen = contextEnd - contextStart;
+        int len = end - start;
+        char[] buf = TemporaryBuffer.obtain(contextLen);
+        TextUtils.getChars(text, start, end, buf, 0);
+        float result = getTextRunAdvances(buf, start - contextStart, len,
+                0, contextLen, flags, advances, advancesIndex);
+        TemporaryBuffer.recycle(buf);
+        return result;
+    }
+
+    /**
+     * Returns the total advance width for the characters in the run
+     * between start and end, and if advances is not null, the advance
+     * assigned to each of these characters (java chars).
+     *
+     * <p>The trailing surrogate in a valid surrogate pair is assigned
+     * an advance of 0.  Thus the number of returned advances is
+     * always equal to count, not to the number of unicode codepoints
+     * represented by the run.
+     *
+     * <p>In the case of conjuncts or combining marks, the total
+     * advance is assigned to the first logical character, and the
+     * following characters are assigned an advance of 0.
+     *
+     * <p>This generates the sum of the advances of glyphs for
+     * characters in a reordered cluster as the width of the first
+     * logical character in the cluster, and 0 for the widths of all
+     * other characters in the cluster.  In effect, such clusters are
+     * treated like conjuncts.
+     *
+     * <p>The shaping bounds limit the amount of context available
+     * outside start and end that can be used for shaping analysis.
+     * These bounds typically reflect changes in bidi level or font
+     * metrics across which shaping does not occur.
+     *
+     * @param text the text to measure
+     * @param start the index of the first character to measure
+     * @param end the index past the last character to measure
+     * @param contextStart the index of the first character to use for shaping context,
+     * must be <= start
+     * @param contextEnd the index past the last character to use for shaping context,
+     * must be >= end
+     * @param flags the flags to control the advances, either {@link #DIRECTION_LTR}
+     * or {@link #DIRECTION_RTL}
+     * @param advances array to receive the advances, must have room for all advances,
+     * can be null if only total advance is needed
+     * @param advancesIndex the position in advances at which to put the
+     * advance corresponding to the character at start
+     * @return the total advance
+     *
+     * @hide
+     */
+    public float getTextRunAdvances(String text, int start, int end, int contextStart,
+            int contextEnd, int flags, float[] advances, int advancesIndex) {
+
+        if ((start | end | contextStart | contextEnd | advancesIndex | (end - start)
+                | (start - contextStart) | (contextEnd - end)
+                | (text.length() - contextEnd)
+                | (advances == null ? 0 :
+                    (advances.length - advancesIndex - (end - start)))) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
+            throw new IllegalArgumentException("unknown flags value: " + flags);
+        }
+
+        if (!mHasCompatScaling) {
+            return native_getTextRunAdvances(mNativePaint, text, start, end,
+                    contextStart, contextEnd, flags, advances, advancesIndex);
+        }
+
+        final float oldSize = getTextSize();
+        setTextSize(oldSize * mCompatScaling);
+        float totalAdvance = native_getTextRunAdvances(mNativePaint, text, start, end,
+                contextStart, contextEnd, flags, advances, advancesIndex);
+        setTextSize(oldSize);
+
+        if (advances != null) {
+            for (int i = advancesIndex, e = i + (end - start); i < e; i++) {
+                advances[i] *= mInvCompatScaling;
+            }
+        }
+        return totalAdvance * mInvCompatScaling; // assume errors are insignificant
+    }
+
+    /**
+     * Returns the next cursor position in the run.  This avoids placing the
+     * cursor between surrogates, between characters that form conjuncts,
+     * between base characters and combining marks, or within a reordering
+     * cluster.
+     *
+     * <p>ContextStart and offset are relative to the start of text.
+     * The context is the shaping context for cursor movement, generally
+     * the bounds of the metric span enclosing the cursor in the direction of
+     * movement.
+     *
+     * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
+     * cursor position, this returns -1.  Otherwise this will never return a
+     * value before contextStart or after contextStart + contextLength.
+     *
+     * @param text the text
+     * @param contextStart the start of the context
+     * @param contextLength the length of the context
+     * @param flags either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+     * @param offset the cursor position to move from
+     * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
+     * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
+     * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+     * @return the offset of the next position, or -1
+     * @hide
+     */
+    public int getTextRunCursor(char[] text, int contextStart, int contextLength,
+            int flags, int offset, int cursorOpt) {
+        int contextEnd = contextStart + contextLength;
+        if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
+                | (offset - contextStart) | (contextEnd - offset)
+                | (text.length - contextEnd) | cursorOpt) < 0)
+                || cursorOpt > CURSOR_OPT_MAX_VALUE) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        return native_getTextRunCursor(mNativePaint, text,
+                contextStart, contextLength, flags, offset, cursorOpt);
+    }
+
+    /**
+     * Returns the next cursor position in the run.  This avoids placing the
+     * cursor between surrogates, between characters that form conjuncts,
+     * between base characters and combining marks, or within a reordering
+     * cluster.
+     *
+     * <p>ContextStart, contextEnd, and offset are relative to the start of
+     * text.  The context is the shaping context for cursor movement, generally
+     * the bounds of the metric span enclosing the cursor in the direction of
+     * movement.
+     *
+     * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
+     * cursor position, this returns -1.  Otherwise this will never return a
+     * value before contextStart or after contextEnd.
+     *
+     * @param text the text
+     * @param contextStart the start of the context
+     * @param contextEnd the end of the context
+     * @param flags either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+     * @param offset the cursor position to move from
+     * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
+     * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
+     * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+     * @return the offset of the next position, or -1
+     * @hide
+     */
+    public int getTextRunCursor(CharSequence text, int contextStart,
+           int contextEnd, int flags, int offset, int cursorOpt) {
+
+        if (text instanceof String || text instanceof SpannedString ||
+                text instanceof SpannableString) {
+            return getTextRunCursor(text.toString(), contextStart, contextEnd,
+                    flags, offset, cursorOpt);
+        }
+        if (text instanceof GraphicsOperations) {
+            return ((GraphicsOperations) text).getTextRunCursor(
+                    contextStart, contextEnd, flags, offset, cursorOpt, this);
+        }
+
+        int contextLen = contextEnd - contextStart;
+        char[] buf = TemporaryBuffer.obtain(contextLen);
+        TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
+        int result = getTextRunCursor(buf, 0, contextLen, flags, offset, cursorOpt);
+        TemporaryBuffer.recycle(buf);
+        return result;
+    }
+
+    /**
+     * Returns the next cursor position in the run.  This avoids placing the
+     * cursor between surrogates, between characters that form conjuncts,
+     * between base characters and combining marks, or within a reordering
+     * cluster.
+     *
+     * <p>ContextStart, contextEnd, and offset are relative to the start of
+     * text.  The context is the shaping context for cursor movement, generally
+     * the bounds of the metric span enclosing the cursor in the direction of
+     * movement.
+     *
+     * <p>If cursorOpt is {@link #CURSOR_AT} and the offset is not a valid
+     * cursor position, this returns -1.  Otherwise this will never return a
+     * value before contextStart or after contextEnd.
+     *
+     * @param text the text
+     * @param contextStart the start of the context
+     * @param contextEnd the end of the context
+     * @param flags either {@link #DIRECTION_RTL} or {@link #DIRECTION_LTR}
+     * @param offset the cursor position to move from
+     * @param cursorOpt how to move the cursor, one of {@link #CURSOR_AFTER},
+     * {@link #CURSOR_AT_OR_AFTER}, {@link #CURSOR_BEFORE},
+     * {@link #CURSOR_AT_OR_BEFORE}, or {@link #CURSOR_AT}
+     * @return the offset of the next position, or -1
+     * @hide
+     */
+    public int getTextRunCursor(String text, int contextStart, int contextEnd,
+            int flags, int offset, int cursorOpt) {
+        if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
+                | (offset - contextStart) | (contextEnd - offset)
+                | (text.length() - contextEnd) | cursorOpt) < 0)
+                || cursorOpt > CURSOR_OPT_MAX_VALUE) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        return native_getTextRunCursor(mNativePaint, text,
+                contextStart, contextEnd, flags, offset, cursorOpt);
+    }
+
+    /**
      * Return the path (outline) for the specified text.
      * Note: just like Canvas.drawText, this will respect the Align setting in
      * the paint.
@@ -1299,7 +1752,8 @@
         if ((index | count) < 0 || index + count > text.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        native_getTextPath(mNativePaint, text, index, count, x, y, path.ni());
+        native_getTextPath(mNativePaint, mBidiFlags, text, index, count, x, y, 
+                path.ni());
     }
 
     /**
@@ -1320,7 +1774,8 @@
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        native_getTextPath(mNativePaint, text, start, end, x, y, path.ni());
+        native_getTextPath(mNativePaint, mBidiFlags, text, start, end, x, y, 
+                path.ni());
     }
     
     /**
@@ -1404,9 +1859,22 @@
                             char[] text, int index, int count, float[] widths);
     private static native int native_getTextWidths(int native_object,
                             String text, int start, int end, float[] widths);
-    private static native void native_getTextPath(int native_object,
+
+    private static native float native_getTextRunAdvances(int native_object,
+            char[] text, int index, int count, int contextIndex, int contextCount,
+            int flags, float[] advances, int advancesIndex);
+    private static native float native_getTextRunAdvances(int native_object,
+            String text, int start, int end, int contextStart, int contextEnd,
+            int flags, float[] advances, int advancesIndex);
+
+    private native int native_getTextRunCursor(int native_object, char[] text,
+            int contextStart, int contextLength, int flags, int offset, int cursorOpt);
+    private native int native_getTextRunCursor(int native_object, String text,
+            int contextStart, int contextEnd, int flags, int offset, int cursorOpt);
+
+    private static native void native_getTextPath(int native_object, int bidiFlags,
                 char[] text, int index, int count, float x, float y, int path);
-    private static native void native_getTextPath(int native_object,
+    private static native void native_getTextPath(int native_object, int bidiFlags,
                 String text, int start, int end, float x, float y, int path);
     private static native void nativeGetStringBounds(int nativePaint,
                                 String text, int start, int end, Rect bounds);
@@ -1414,4 +1882,3 @@
                                 char[] text, int index, int count, Rect bounds);
     private static native void finalizer(int nativePaint);
 }
-
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 281823a..1324431 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import android.view.HardwareRenderer;
+
 /**
  * The Path class encapsulates compound (multiple contour) geometric paths
  * consisting of straight line segments, quadratic curves, and cubic curves.
@@ -24,12 +26,27 @@
  * text on a path.
  */
 public class Path {
+    /**
+     * @hide
+     */
+    public final int mNativePath;
+
+    /**
+     * @hide
+     */
+    public boolean isSimplePath = true;
+    /**
+     * @hide
+     */
+    public Region rects;
+    private boolean mDetectSimplePaths;
 
     /**
      * Create an empty path
      */
     public Path() {
         mNativePath = init1();
+        mDetectSimplePaths = HardwareRenderer.isAvailable();
     }
 
     /**
@@ -43,6 +60,7 @@
             valNative = src.mNativePath;
         }
         mNativePath = init2(valNative);
+        mDetectSimplePaths = HardwareRenderer.isAvailable();
     }
     
     /**
@@ -50,6 +68,10 @@
      * This does NOT change the fill-type setting.
      */
     public void reset() {
+        isSimplePath = true;
+        if (mDetectSimplePaths) {
+            if (rects != null) rects.setEmpty();
+        }
         native_reset(mNativePath);
     }
 
@@ -58,6 +80,10 @@
      * keeps the internal data structure for faster reuse.
      */
     public void rewind() {
+        isSimplePath = true;
+        if (mDetectSimplePaths) {
+            if (rects != null) rects.setEmpty();
+        }
         native_rewind(mNativePath);
     }
 
@@ -65,6 +91,7 @@
     */
     public void set(Path src) {
         if (this != src) {
+            isSimplePath = src.isSimplePath;
             native_set(mNativePath, src.mNativePath);
         }
     }
@@ -160,6 +187,7 @@
      * @param bounds Returns the computed bounds of the path's control points.
      * @param exact This parameter is no longer used.
      */
+    @SuppressWarnings({"UnusedDeclaration"})
     public void computeBounds(RectF bounds, boolean exact) {
         native_computeBounds(mNativePath, bounds);
     }
@@ -208,6 +236,7 @@
      * @param y The y-coordinate of the end of a line
      */
     public void lineTo(float x, float y) {
+        isSimplePath = false;
         native_lineTo(mNativePath, x, y);
     }
 
@@ -222,6 +251,7 @@
      *           this contour, to specify a line
      */
     public void rLineTo(float dx, float dy) {
+        isSimplePath = false;
         native_rLineTo(mNativePath, dx, dy);
     }
 
@@ -236,6 +266,7 @@
      * @param y2 The y-coordinate of the end point on a quadratic curve
      */
     public void quadTo(float x1, float y1, float x2, float y2) {
+        isSimplePath = false;
         native_quadTo(mNativePath, x1, y1, x2, y2);
     }
 
@@ -254,6 +285,7 @@
      *            this contour, for the end point of a quadratic curve
      */
     public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
+        isSimplePath = false;
         native_rQuadTo(mNativePath, dx1, dy1, dx2, dy2);
     }
 
@@ -271,6 +303,7 @@
      */
     public void cubicTo(float x1, float y1, float x2, float y2,
                         float x3, float y3) {
+        isSimplePath = false;
         native_cubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
     }
 
@@ -281,6 +314,7 @@
      */
     public void rCubicTo(float x1, float y1, float x2, float y2,
                          float x3, float y3) {
+        isSimplePath = false;
         native_rCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
     }
 
@@ -299,6 +333,7 @@
      */
     public void arcTo(RectF oval, float startAngle, float sweepAngle,
                       boolean forceMoveTo) {
+        isSimplePath = false;
         native_arcTo(mNativePath, oval, startAngle, sweepAngle, forceMoveTo);
     }
     
@@ -314,6 +349,7 @@
      * @param sweepAngle  Sweep angle (in degrees) measured clockwise
      */
     public void arcTo(RectF oval, float startAngle, float sweepAngle) {
+        isSimplePath = false;
         native_arcTo(mNativePath, oval, startAngle, sweepAngle, false);
     }
     
@@ -322,6 +358,7 @@
      * first point of the contour, a line segment is automatically added.
      */
     public void close() {
+        isSimplePath = false;
         native_close(mNativePath);
     }
 
@@ -351,6 +388,11 @@
         if (rect == null) {
             throw new NullPointerException("need rect parameter");
         }
+        if (mDetectSimplePaths) {
+            if (rects == null) rects = new Region();
+            rects.op((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom,
+                    Region.Op.UNION);
+        }
         native_addRect(mNativePath, rect, dir.nativeInt);
     }
 
@@ -363,8 +405,11 @@
      * @param bottom The bottom of a rectangle to add to the path
      * @param dir    The direction to wind the rectangle's contour
      */
-    public void addRect(float left, float top, float right, float bottom,
-                        Direction dir) {
+    public void addRect(float left, float top, float right, float bottom, Direction dir) {
+        if (mDetectSimplePaths) {
+            if (rects == null) rects = new Region();
+            rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION);
+        }
         native_addRect(mNativePath, left, top, right, bottom, dir.nativeInt);
     }
 
@@ -378,6 +423,7 @@
         if (oval == null) {
             throw new NullPointerException("need oval parameter");
         }
+        isSimplePath = false;
         native_addOval(mNativePath, oval, dir.nativeInt);
     }
 
@@ -390,6 +436,7 @@
      * @param dir    The direction to wind the circle's contour
      */
     public void addCircle(float x, float y, float radius, Direction dir) {
+        isSimplePath = false;
         native_addCircle(mNativePath, x, y, radius, dir.nativeInt);
     }
 
@@ -404,6 +451,7 @@
         if (oval == null) {
             throw new NullPointerException("need oval parameter");
         }
+        isSimplePath = false;
         native_addArc(mNativePath, oval, startAngle, sweepAngle);
     }
 
@@ -419,6 +467,7 @@
         if (rect == null) {
             throw new NullPointerException("need rect parameter");
         }
+        isSimplePath = false;
         native_addRoundRect(mNativePath, rect, rx, ry, dir.nativeInt);
     }
     
@@ -438,6 +487,7 @@
         if (radii.length < 8) {
             throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
         }
+        isSimplePath = false;
         native_addRoundRect(mNativePath, rect, radii, dir.nativeInt);
     }
     
@@ -448,6 +498,7 @@
      * @param dx  The amount to translate the path in X as it is added
      */
     public void addPath(Path src, float dx, float dy) {
+        isSimplePath = false;
         native_addPath(mNativePath, src.mNativePath, dx, dy);
     }
 
@@ -457,6 +508,7 @@
      * @param src The path that is appended to the current path
      */
     public void addPath(Path src) {
+        isSimplePath = false;
         native_addPath(mNativePath, src.mNativePath);
     }
 
@@ -466,6 +518,7 @@
      * @param src The path to add as a new contour
      */
     public void addPath(Path src, Matrix matrix) {
+        if (!src.isSimplePath) isSimplePath = false;
         native_addPath(mNativePath, src.mNativePath, matrix.native_instance);
     }
 
@@ -502,6 +555,7 @@
      * @param dy The new Y coordinate for the last point
      */
     public void setLastPoint(float dx, float dy) {
+        isSimplePath = false;
         native_setLastPoint(mNativePath, dx, dy);
     }
 
@@ -537,8 +591,8 @@
             super.finalize();
         }
     }
-    
-    /*package*/ final int ni() {
+
+    final int ni() {
         return mNativePath;
     }
 
@@ -592,6 +646,4 @@
                                                 int dst_path);
     private static native void native_transform(int nPath, int matrix);
     private static native void finalizer(int nPath);
-
-    private final int mNativePath;
 }
diff --git a/graphics/java/android/graphics/PorterDuff.java b/graphics/java/android/graphics/PorterDuff.java
index 3904234..2ef1662 100644
--- a/graphics/java/android/graphics/PorterDuff.java
+++ b/graphics/java/android/graphics/PorterDuff.java
@@ -53,11 +53,18 @@
         /** [Sa * Da, Sc * Dc] */
         MULTIPLY    (14),
         /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
-        SCREEN      (15);
+        SCREEN      (15),
+        /** Saturate(S + D) */
+        ADD         (16),
+        OVERLAY     (17);
 
         Mode(int nativeInt) {
             this.nativeInt = nativeInt;
         }
-        final int nativeInt;
+
+        /**
+         * @hide
+         */
+        public final int nativeInt;
     }
 }
diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index 06724bd..ecc7c24 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -25,10 +25,11 @@
      * @param mode           The porter-duff mode that is applied
      */
     public PorterDuffColorFilter(int srcColor, PorterDuff.Mode mode) {
-        native_instance = native_CreatePorterDuffFilter(srcColor,
-                                                        mode.nativeInt);
+        native_instance = native_CreatePorterDuffFilter(srcColor, mode.nativeInt);
+        nativeColorFilter = nCreatePorterDuffFilter(native_instance, srcColor, mode.nativeInt);
     }
 
-    private static native int native_CreatePorterDuffFilter(int srcColor,
-                                                            int porterDuffMode);
+    private static native int native_CreatePorterDuffFilter(int srcColor, int porterDuffMode);
+    private static native int nCreatePorterDuffFilter(int nativeFilter, int srcColor,
+            int porterDuffMode);
 }
diff --git a/graphics/java/android/graphics/PorterDuffXfermode.java b/graphics/java/android/graphics/PorterDuffXfermode.java
index cb127fd..6ba064c 100644
--- a/graphics/java/android/graphics/PorterDuffXfermode.java
+++ b/graphics/java/android/graphics/PorterDuffXfermode.java
@@ -18,11 +18,17 @@
 
 public class PorterDuffXfermode extends Xfermode {
     /**
+     * @hide
+     */
+    public final PorterDuff.Mode mode;
+
+    /**
      * Create an xfermode that uses the specified porter-duff mode.
      *
      * @param mode           The porter-duff mode that is applied
      */
     public PorterDuffXfermode(PorterDuff.Mode mode) {
+        this.mode = mode;
         native_instance = nativeCreateXfermode(mode.nativeInt);
     }
     
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index b4e902d..897762c 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -40,6 +40,8 @@
             throw new IllegalArgumentException("color and position arrays must be of equal length");
         }
         native_instance = nativeCreate1(x, y, radius, colors, positions, tile.nativeInt);
+        native_shader = nativePostCreate1(native_instance, x, y, radius, colors, positions,
+                tile.nativeInt);
     }
 
 	/**	Create a shader that draws a radial gradient given the center and radius.
@@ -56,11 +58,18 @@
             throw new IllegalArgumentException("radius must be > 0");
         }
         native_instance = nativeCreate2(x, y, radius, color0, color1, tile.nativeInt);
+        native_shader = nativePostCreate2(native_instance, x, y, radius, color0, color1,
+                tile.nativeInt);
     }
 
 	private static native int nativeCreate1(float x, float y, float radius,
-                                            int colors[], float positions[], int tileMode);
+            int colors[], float positions[], int tileMode);
 	private static native int nativeCreate2(float x, float y, float radius,
-                                            int color0, int color1, int tileMode);
+            int color0, int color1, int tileMode);
+
+    private static native int nativePostCreate1(int native_shader, float x, float y, float radius,
+            int colors[], float positions[], int tileMode);
+    private static native int nativePostCreate2(int native_shader, float x, float y, float radius,
+            int color0, int color1, int tileMode);
 }
 
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index 2b080aa..e540806 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -20,6 +20,10 @@
 import android.os.Parcelable;
 
 public class Region implements Parcelable {
+    /**
+     * @hide
+     */
+    public final int mNativeRegion;
 
     // the native values for these must match up with the enum in SkRegion.h
     public enum Op {
@@ -33,7 +37,11 @@
         Op(int nativeInt) {
             this.nativeInt = nativeInt;
         }
-        final int nativeInt;
+
+        /**
+         * @hide
+         */
+        public final int nativeInt;
     }
 
     /** Create an empty region
@@ -325,10 +333,14 @@
     }
 
     protected void finalize() throws Throwable {
-        nativeDestructor(mNativeRegion);
+        try {
+            nativeDestructor(mNativeRegion);
+        } finally {
+            super.finalize();
+        }
     }
     
-    /*package*/ Region(int ni) {
+    Region(int ni) {
         if (ni == 0) {
             throw new RuntimeException();
         }
@@ -341,7 +353,7 @@
         this(ni);
     }
 
-    /*package*/ final int ni() {
+    final int ni() {
         return mNativeRegion;
     }
 
@@ -370,6 +382,4 @@
                                                       Parcel p);
 
     private static native boolean nativeEquals(int native_r1, int native_r2);
-
-    private final int mNativeRegion;
 }
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index ae0304e..0400b5c 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -23,9 +23,16 @@
  * drawn with that paint will get its color(s) from the shader.
  */
 public class Shader {
-
-    // this is set by subclasses, but don't make it public
-    /* package */ int native_instance;
+    /**
+     * This is set by subclasses, but don't make it public.
+     * 
+     * @hide 
+     */
+    public int native_instance;
+    /**
+     * @hide
+     */
+    public int native_shader;
 
     public enum TileMode {
         /**
@@ -64,17 +71,21 @@
      * @param localM The shader's new local matrix, or null to specify identity
      */
     public void setLocalMatrix(Matrix localM) {
-        nativeSetLocalMatrix(native_instance,
-                             localM != null ? localM.native_instance : 0);
+        nativeSetLocalMatrix(native_instance, native_shader,
+                localM == null ? 0 : localM.native_instance);
     }
 
     protected void finalize() throws Throwable {
-        nativeDestructor(native_instance);
+        try {
+            super.finalize();
+        } finally {
+            nativeDestructor(native_instance, native_shader);
+        }
     }
 
-    private static native void nativeDestructor(int native_shader);
+    private static native void nativeDestructor(int native_shader, int native_skiaShader);
     private static native boolean nativeGetLocalMatrix(int native_shader,
-                                                       int matrix_instance);
+            int matrix_instance); 
     private static native void nativeSetLocalMatrix(int native_shader,
-                                                    int matrix_instance);
+            int native_skiaShader, int matrix_instance);
 }
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 7456993..2afdd4d 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -42,6 +42,7 @@
                         "color and position arrays must be of equal length");
         }
         native_instance = nativeCreate1(cx, cy, colors, positions);
+        native_shader = nativePostCreate1(native_instance, cx, cy, colors, positions);
     }
 
     /**
@@ -54,11 +55,15 @@
      */
     public SweepGradient(float cx, float cy, int color0, int color1) {
         native_instance = nativeCreate2(cx, cy, color0, color1);
+        native_shader = nativePostCreate2(native_instance, cx, cy, color0, color1);
     }
 
-    private static native int nativeCreate1(float x, float y,
-                                            int colors[], float positions[]);
-    private static native int nativeCreate2(float x, float y,
-                                            int color0, int color1);
+    private static native int nativeCreate1(float x, float y, int colors[], float positions[]);
+    private static native int nativeCreate2(float x, float y, int color0, int color1);
+
+    private static native int nativePostCreate1(int native_shader, float cx, float cy,
+            int[] colors, float[] positions);    
+    private static native int nativePostCreate2(int native_shader, float cx, float cy,
+            int color0, int color1);
 }
 
diff --git a/graphics/java/android/graphics/TemporaryBuffer.java b/graphics/java/android/graphics/TemporaryBuffer.java
index 1d7fe01..c5b8143 100644
--- a/graphics/java/android/graphics/TemporaryBuffer.java
+++ b/graphics/java/android/graphics/TemporaryBuffer.java
@@ -18,9 +18,11 @@
 
 import com.android.internal.util.ArrayUtils;
 
-/* package */ class TemporaryBuffer
-{
-    /* package */ static char[] obtain(int len) {
+/**
+ * @hide
+ */
+public class TemporaryBuffer {
+    public static char[] obtain(int len) {
         char[] buf;
 
         synchronized (TemporaryBuffer.class) {
@@ -28,15 +30,15 @@
             sTemp = null;
         }
 
-        if (buf == null || buf.length < len)
+        if (buf == null || buf.length < len) {
             buf = new char[ArrayUtils.idealCharArraySize(len)];
+        }
 
         return buf;
     }
 
-    /* package */ static void recycle(char[] temp) {
-        if (temp.length > 1000)
-            return;
+    public static void recycle(char[] temp) {
+        if (temp.length > 1000) return;
 
         synchronized (TemporaryBuffer.class) {
             sTemp = temp;
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index 42c410e..2467bdc 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -31,7 +31,11 @@
 public class Xfermode {
 
     protected void finalize() throws Throwable {
-        finalizer(native_instance);
+        try {
+            finalizer(native_instance);
+        } finally {
+            super.finalize();
+        }
     }
 
     private static native void finalizer(int native_instance);
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index 604c602..289348a 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -26,10 +26,8 @@
 import java.io.IOException;
 
 /**
- * A specialized Drawable that fills the Canvas with a specified color,
- * with respect to the clip region. Note that a ColorDrawable ignores the ColorFilter.
- * It also ignores the Bounds, meaning it will draw everywhere in the current clip,
- * even if setBounds(...) was called with a smaller area.
+ * A specialized Drawable that fills the Canvas with a specified color.
+ * Note that a ColorDrawable ignores the ColorFilter.
  *
  * <p>It can be defined in an XML file with the <code>&lt;color></code> element.</p>
  *
@@ -37,6 +35,7 @@
  */
 public class ColorDrawable extends Drawable {
     private ColorState mState;
+    private final Paint mPaint = new Paint();
 
     /**
      * Creates a new black ColorDrawable.
@@ -52,7 +51,7 @@
      */
     public ColorDrawable(int color) {
         this(null);
-        mState.mBaseColor = mState.mUseColor = color;
+        setColor(color);
     }
 
     private ColorDrawable(ColorState state) {
@@ -66,7 +65,29 @@
 
     @Override
     public void draw(Canvas canvas) {
-        canvas.drawColor(mState.mUseColor);
+        if ((mState.mUseColor >>> 24) != 0) {
+            mPaint.setColor(mState.mUseColor);
+            canvas.drawRect(getBounds(), mPaint);
+        }
+    }
+
+    /**
+     * Gets the drawable's color value.
+     *
+     * @return int The color to draw.
+     */
+    public int getColor() {
+        return mState.mUseColor;
+    }
+
+    /**
+     * Sets the drawable's color value. This action will clobber the results of prior calls to
+     * {@link #setAlpha(int)} on this object, which side-affected the underlying color.
+     *
+     * @param color The color to draw.
+     */
+    public void setColor(int color) {
+        mState.mBaseColor = mState.mUseColor = color;
     }
 
     /**
@@ -129,7 +150,7 @@
     }
 
     final static class ColorState extends ConstantState {
-        int mBaseColor; // initial color. never changes
+        int mBaseColor; // base color, independent of setAlpha()
         int mUseColor;  // basecolor modulated by setAlpha()
         int mChangingConfigurations;
 
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 3125321..baa9d62 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -16,21 +16,30 @@
 
 package android.graphics.drawable;
 
-import java.io.InputStream;
-import java.io.IOException;
-import java.util.Arrays;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.*;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.NinePatch;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
+import android.graphics.Region;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.StateSet;
-import android.util.Xml;
 import android.util.TypedValue;
+import android.util.Xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
 
 /**
  * A Drawable is a general abstraction for "something that can be drawn."  Most
@@ -414,6 +423,13 @@
     }
 
     /**
+     * If this Drawable does transition animations between states, ask that
+     * it immediately jump to the current state and skip any active animations.
+     */
+    public void jumpToCurrentState() {
+    }
+
+    /**
      * @return The current drawable that will be used by this drawable. For simple drawables, this
      *         is just the drawable itself. For drawables that change state like
      *         {@link StateListDrawable} and {@link LevelListDrawable} this will be the child drawable
@@ -645,6 +661,8 @@
      * Calling this method on a mutable Drawable will have no effect.
      *
      * @return This drawable.
+     * @see ConstantState
+     * @see #getConstantState()
      */
     public Drawable mutate() {
         return this;
@@ -749,6 +767,8 @@
             drawable = new StateListDrawable();
         } else if (name.equals("level-list")) {
             drawable = new LevelListDrawable();
+        } else if (name.equals("mipmap")) {
+            drawable = new MipmapDrawable();
         } else if (name.equals("layer-list")) {
             drawable = new LayerDrawable();
         } else if (name.equals("transition")) {
@@ -770,7 +790,7 @@
         } else if (name.equals("inset")) {
             drawable = new InsetDrawable();
         } else if (name.equals("bitmap")) {
-            drawable = new BitmapDrawable();
+            drawable = new BitmapDrawable(r);
             if (r != null) {
                ((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics());
             }
@@ -805,6 +825,9 @@
         return null;
     }
 
+    /**
+     * Inflate this Drawable from an XML resource.
+     */
     public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
             throws XmlPullParserException, IOException {
 
@@ -813,6 +836,12 @@
         a.recycle();
     }
 
+    /**
+     * Inflate a Drawable from an XML resource.
+     *
+     * @throws XmlPullParserException
+     * @throws IOException
+     */
     void inflateWithAttributes(Resources r, XmlPullParser parser,
             TypedArray attrs, int visibleAttr)
             throws XmlPullParserException, IOException {
@@ -820,12 +849,27 @@
         mVisible = attrs.getBoolean(visibleAttr, mVisible);
     }
 
+    /**
+     * This abstract class is used by {@link Drawable}s to store shared constant state and data
+     * between Drawables. {@link BitmapDrawable}s created from the same resource will for instance
+     * share a unique bitmap stored in their ConstantState.
+     *
+     * <p>
+     * {@link #newDrawable(Resources)} can be used as a factory to create new Drawable instances
+     * from this ConstantState.
+     * </p>
+     *
+     * Use {@link Drawable#getConstantState()} to retrieve the ConstantState of a Drawable. Calling
+     * {@link Drawable#mutate()} on a Drawable should typically create a new ConstantState for that
+     * Drawable.
+     */
     public static abstract class ConstantState {
         /**
          * Create a new drawable without supplying resources the caller
          * is running in.  Note that using this means the density-dependent
          * drawables (like bitmaps) will not be able to update their target
-         * density correctly.
+         * density correctly. One should use {@link #newDrawable(Resources)}
+         * instead to provide a resource.
          */
         public abstract Drawable newDrawable();
         /**
@@ -844,6 +888,13 @@
         public abstract int getChangingConfigurations();
     }
 
+    /**
+     * Return a {@link ConstantState} instance that holds the shared state of this Drawable.
+     *q
+     * @return The ConstantState associated to that Drawable.
+     * @see ConstantState
+     * @see Drawable#mutate()
+     */
     public ConstantState getConstantState() {
         return null;
     }
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index c6f57d4..e55a746 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -17,9 +17,20 @@
 package android.graphics.drawable;
 
 import android.content.res.Resources;
-import android.graphics.*;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.SystemClock;
 
+/**
+ * A helper class that contains several {@link Drawable}s and selects which one to use.
+ *
+ * You can subclass it to create your own DrawableContainers or directly use one its child classes.
+ */
 public class DrawableContainer extends Drawable implements Drawable.Callback {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "DrawableContainer";
 
     /**
      * To be proper, we should have a getter for dither (and alpha, etc.)
@@ -40,6 +51,12 @@
     private int mCurIndex = -1;
     private boolean mMutated;
 
+    // Animations.
+    private Runnable mAnimationRunnable;
+    private long mEnterAnimationEnd;
+    private long mExitAnimationEnd;
+    private Drawable mLastDrawable;
+
     // overrides from Drawable
 
     @Override
@@ -47,6 +64,9 @@
         if (mCurrDrawable != null) {
             mCurrDrawable.draw(canvas);
         }
+        if (mLastDrawable != null) {
+            mLastDrawable.draw(canvas);
+        }
     }
 
     @Override
@@ -75,7 +95,11 @@
         if (mAlpha != alpha) {
             mAlpha = alpha;
             if (mCurrDrawable != null) {
-                mCurrDrawable.setAlpha(alpha);
+                if (mEnterAnimationEnd == 0) {
+                    mCurrDrawable.setAlpha(alpha);
+                } else {
+                    animate(false);
+                }
             }
         }
     }
@@ -100,8 +124,29 @@
         }
     }
     
+    /**
+     * Change the global fade duration when a new drawable is entering
+     * the scene.
+     * @param ms The amount of time to fade in milliseconds.
+     */
+    public void setEnterFadeDuration(int ms) {
+        mDrawableContainerState.mEnterFadeDuration = ms;
+    }
+    
+    /**
+     * Change the global fade duration when a new drawable is leaving
+     * the scene.
+     * @param ms The amount of time to fade in milliseconds.
+     */
+    public void setExitFadeDuration(int ms) {
+        mDrawableContainerState.mExitFadeDuration = ms;
+    }
+    
     @Override
     protected void onBoundsChange(Rect bounds) {
+        if (mLastDrawable != null) {
+            mLastDrawable.setBounds(bounds);
+        }
         if (mCurrDrawable != null) {
             mCurrDrawable.setBounds(bounds);
         }
@@ -113,7 +158,34 @@
     }
     
     @Override
+    public void jumpToCurrentState() {
+        boolean changed = false;
+        if (mLastDrawable != null) {
+            mLastDrawable.jumpToCurrentState();
+            mLastDrawable = null;
+            changed = true;
+        }
+        if (mCurrDrawable != null) {
+            mCurrDrawable.jumpToCurrentState();
+        }
+        if (mExitAnimationEnd != 0) {
+            mExitAnimationEnd = 0;
+            changed = true;
+        }
+        if (mEnterAnimationEnd != 0) {
+            mEnterAnimationEnd = 0;
+            changed = true;
+        }
+        if (changed) {
+            invalidateSelf();
+        }
+    }
+
+    @Override
     protected boolean onStateChange(int[] state) {
+        if (mLastDrawable != null) {
+            return mLastDrawable.setState(state);
+        }
         if (mCurrDrawable != null) {
             return mCurrDrawable.setState(state);
         }
@@ -122,6 +194,9 @@
 
     @Override
     protected boolean onLevelChange(int level) {
+        if (mLastDrawable != null) {
+            return mLastDrawable.setLevel(level);
+        }
         if (mCurrDrawable != null) {
             return mCurrDrawable.setLevel(level);
         }
@@ -160,22 +235,19 @@
         return mCurrDrawable != null ? mCurrDrawable.getMinimumHeight() : 0;
     }
 
-    public void invalidateDrawable(Drawable who)
-    {
+    public void invalidateDrawable(Drawable who) {
         if (who == mCurrDrawable && mCallback != null) {
             mCallback.invalidateDrawable(this);
         }
     }
 
-    public void scheduleDrawable(Drawable who, Runnable what, long when)
-    {
+    public void scheduleDrawable(Drawable who, Runnable what, long when) {
         if (who == mCurrDrawable && mCallback != null) {
             mCallback.scheduleDrawable(this, what, when);
         }
     }
 
-    public void unscheduleDrawable(Drawable who, Runnable what)
-    {
+    public void unscheduleDrawable(Drawable who, Runnable what) {
         if (who == mCurrDrawable && mCallback != null) {
             mCallback.unscheduleDrawable(this, what);
         }
@@ -184,6 +256,9 @@
     @Override
     public boolean setVisible(boolean visible, boolean restart) {
         boolean changed = super.setVisible(visible, restart);
+        if (mLastDrawable != null) {
+            mLastDrawable.setVisible(visible, restart);
+        }
         if (mCurrDrawable != null) {
             mCurrDrawable.setVisible(visible, restart);
         }
@@ -196,21 +271,43 @@
                 mDrawableContainerState.getOpacity();
     }
 
-    public boolean selectDrawable(int idx)
-    {
+    public boolean selectDrawable(int idx) {
         if (idx == mCurIndex) {
             return false;
         }
+
+        final long now = SystemClock.uptimeMillis();
+
+        if (DEBUG) android.util.Log.i(TAG, toString() + " from " + mCurIndex + " to " + idx
+                + ": exit=" + mDrawableContainerState.mExitFadeDuration
+                + " enter=" + mDrawableContainerState.mEnterFadeDuration);
+
+        if (mDrawableContainerState.mExitFadeDuration > 0) {
+            if (mLastDrawable != null) {
+                mLastDrawable.setVisible(false, false);
+            }
+            if (mCurrDrawable != null) {
+                mLastDrawable = mCurrDrawable;
+                mExitAnimationEnd = now + mDrawableContainerState.mExitFadeDuration;
+            } else {
+                mLastDrawable = null;
+                mExitAnimationEnd = 0;
+            }
+        } else if (mCurrDrawable != null) {
+            mCurrDrawable.setVisible(false, false);
+        }
+
         if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
             Drawable d = mDrawableContainerState.mDrawables[idx];
-            if (mCurrDrawable != null) {
-                mCurrDrawable.setVisible(false, false);
-            }
             mCurrDrawable = d;
             mCurIndex = idx;
             if (d != null) {
+                if (mDrawableContainerState.mEnterFadeDuration > 0) {
+                    mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
+                } else {
+                    d.setAlpha(mAlpha);
+                }
                 d.setVisible(isVisible(), true);
-                d.setAlpha(mAlpha);
                 d.setDither(mDrawableContainerState.mDither);
                 d.setColorFilter(mColorFilter);
                 d.setState(getState());
@@ -218,16 +315,72 @@
                 d.setBounds(getBounds());
             }
         } else {
-            if (mCurrDrawable != null) {
-                mCurrDrawable.setVisible(false, false);
-            }
             mCurrDrawable = null;
             mCurIndex = -1;
         }
+
+        if (mEnterAnimationEnd != 0 || mExitAnimationEnd != 0) {
+            if (mAnimationRunnable == null) {
+                mAnimationRunnable = new Runnable() {
+                    @Override public void run() {
+                        animate(true);
+                        invalidateSelf();
+                    }
+                };
+            } else {
+                unscheduleSelf(mAnimationRunnable);
+            }
+            // Compute first frame and schedule next animation.
+            animate(true);
+        }
+
         invalidateSelf();
+
         return true;
     }
     
+    void animate(boolean schedule) {
+        final long now = SystemClock.uptimeMillis();
+        boolean animating = false;
+        if (mCurrDrawable != null) {
+            if (mEnterAnimationEnd != 0) {
+                if (mEnterAnimationEnd <= now) {
+                    mCurrDrawable.setAlpha(mAlpha);
+                    mEnterAnimationEnd = 0;
+                } else {
+                    int animAlpha = (int)((mEnterAnimationEnd-now)*255)
+                            / mDrawableContainerState.mEnterFadeDuration;
+                    if (DEBUG) android.util.Log.i(TAG, toString() + " cur alpha " + animAlpha);
+                    mCurrDrawable.setAlpha(((255-animAlpha)*mAlpha)/255);
+                    animating = true;
+                }
+            }
+        } else {
+            mEnterAnimationEnd = 0;
+        }
+        if (mLastDrawable != null) {
+            if (mExitAnimationEnd != 0) {
+                if (mExitAnimationEnd <= now) {
+                    mLastDrawable.setVisible(false, false);
+                    mLastDrawable = null;
+                    mExitAnimationEnd = 0;
+                } else {
+                    int animAlpha = (int)((mExitAnimationEnd-now)*255)
+                            / mDrawableContainerState.mExitFadeDuration;
+                    if (DEBUG) android.util.Log.i(TAG, toString() + " last alpha " + animAlpha);
+                    mLastDrawable.setAlpha((animAlpha*mAlpha)/255);
+                    animating = true;
+                }
+            }
+        } else {
+            mExitAnimationEnd = 0;
+        }
+
+        if (schedule && animating) {
+            scheduleSelf(mAnimationRunnable, now + 1000/60);
+        }
+    }
+
     @Override
     public Drawable getCurrent() {
         return mCurrDrawable;
@@ -255,6 +408,12 @@
         return this;
     }
 
+    /**
+     * A ConstantState that can contain several {@link Drawable}s.
+     *
+     * This class was made public to enable testing, and its visibility may change in a future
+     * release.
+     */
     public abstract static class DrawableContainerState extends ConstantState {
         final DrawableContainer mOwner;
 
@@ -287,6 +446,9 @@
         
         boolean     mDither = DEFAULT_DITHER;        
 
+        int         mEnterFadeDuration;
+        int         mExitFadeDuration;
+
         DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
                 Resources res) {
             mOwner = owner;
@@ -327,6 +489,9 @@
                 
                 mDither = orig.mDither;
 
+                mEnterFadeDuration = orig.mEnterFadeDuration;
+                mExitFadeDuration = orig.mExitFadeDuration;
+
             } else {
                 mDrawables = new Drawable[10];
                 mNumChildren = 0;
@@ -443,12 +608,12 @@
             return mConstantMinimumHeight;
         }
 
-        private void computeConstantSize() {
+        protected void computeConstantSize() {
             mComputedConstantSize = true;
 
             final int N = getChildCount();
             final Drawable[] drawables = mDrawables;
-            mConstantWidth = mConstantHeight = 0;
+            mConstantWidth = mConstantHeight = -1;
             mConstantMinimumWidth = mConstantMinimumHeight = 0;
             for (int i = 0; i < N; i++) {
                 Drawable dr = drawables[i];
@@ -463,6 +628,22 @@
             }
         }
 
+        public final void setEnterFadeDuration(int duration) {
+            mEnterFadeDuration = duration;
+        }
+
+        public final int getEnterFadeDuration() {
+            return mEnterFadeDuration;
+        }
+
+        public final void setExitFadeDuration(int duration) {
+            mExitFadeDuration = duration;
+        }
+
+        public final int getExitFadeDuration() {
+            return mExitFadeDuration;
+        }
+
         public final int getOpacity() {
             if (mHaveOpacity) {
                 return mOpacity;
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 33ecbea..c558632 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -126,7 +126,7 @@
     private boolean mRectIsDirty;   // internal state
     private boolean mMutated;
     private Path mRingPath;
-    private boolean mPathIsDirty;
+    private boolean mPathIsDirty = true;
 
     /**
      * Controls how the gradient is oriented relative to the drawable's bounds
@@ -179,6 +179,7 @@
      */
     public void setCornerRadii(float[] radii) {
         mGradientState.setCornerRadii(radii);
+        mPathIsDirty = true;
     }
     
     /**
@@ -187,6 +188,7 @@
      */
     public void setCornerRadius(float radius) {
         mGradientState.setCornerRadius(radius);
+        mPathIsDirty = true;
     }
     
     /**
@@ -215,11 +217,13 @@
     }
     
     public void setSize(int width, int height) {
-        mGradientState.setSize(width, height); 
+        mGradientState.setSize(width, height);
+        mPathIsDirty = true;
     }
     
     public void setShape(int shape) {
         mRingPath = null;
+        mPathIsDirty = true;
         mGradientState.setShape(shape);
     }
 
@@ -312,19 +316,20 @@
         switch (st.mShape) {
             case RECTANGLE:
                 if (st.mRadiusArray != null) {
-                    mPath.reset();
-                    mPath.addRoundRect(mRect, st.mRadiusArray,
-                                       Path.Direction.CW);
+                    if (mPathIsDirty || mRectIsDirty) {
+                        mPath.reset();
+                        mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW);
+                        mPathIsDirty = mRectIsDirty = false;
+                    }
                     canvas.drawPath(mPath, mFillPaint);
                     if (haveStroke) {
                         canvas.drawPath(mPath, mStrokePaint);
                     }
-                }
-                else {
+                } else if (st.mRadius > 0.0f) {
                     // since the caller is only giving us 1 value, we will force
                     // it to be square if the rect is too small in one dimension
                     // to show it. If we did nothing, Skia would clamp the rad
-                    // independently along each axis, giving us a thin ellips
+                    // independently along each axis, giving us a thin ellipse
                     // if the rect were very wide but not very tall
                     float rad = st.mRadius;
                     float r = Math.min(mRect.width(), mRect.height()) * 0.5f;
@@ -335,6 +340,11 @@
                     if (haveStroke) {
                         canvas.drawRoundRect(mRect, rad, rad, mStrokePaint);
                     }
+                } else {
+                    canvas.drawRect(mRect, mFillPaint);
+                    if (haveStroke) {
+                        canvas.drawRect(mRect, mStrokePaint);
+                    }
                 }
                 break;
             case OVAL:
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 8047dd4..501cca9 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -266,6 +266,7 @@
      */
     public boolean setDrawableByLayerId(int id, Drawable drawable) {
         final ChildDrawable[] layers = mLayerState.mChildren;
+        drawable.setCallback(this);
         
         for (int i = mLayerState.mNum - 1; i >= 0; i--) {
             if (layers[i].mId == id) {
diff --git a/graphics/java/android/graphics/drawable/MipmapDrawable.java b/graphics/java/android/graphics/drawable/MipmapDrawable.java
new file mode 100644
index 0000000..75fdeed
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/MipmapDrawable.java
@@ -0,0 +1,309 @@
+/*
+ * 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.graphics.drawable;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+
+import java.io.IOException;
+
+/**
+ * A resource that manages a number of alternate Drawables, and which actually draws the one which
+ * size matches the most closely the drawing bounds. Providing several pre-scaled version of the
+ * drawable helps minimizing the aliasing artifacts that can be introduced by the scaling.
+ *
+ * <p>
+ * Use {@link #addDrawable(Drawable)} to define the different Drawables that will represent the
+ * mipmap levels of this MipmapDrawable. The mipmap Drawable that will actually be used when this
+ * MipmapDrawable is drawn is the one which has the smallest intrinsic height greater or equal than
+ * the bounds' height. This selection ensures that the best available mipmap level is scaled down to
+ * draw this MipmapDrawable.
+ * </p>
+ *
+ * If the bounds' height is larger than the largest mipmap, the largest mipmap will be scaled up.
+ * Note that Drawables without intrinsic height (i.e. with a negative value, such as Color) will
+ * only be used if no other mipmap Drawable are provided. The Drawables' intrinsic heights should
+ * not be changed after the Drawable has been added to this MipmapDrawable.
+ *
+ * <p>
+ * The different mipmaps' parameters (opacity, padding, color filter, gravity...) should typically
+ * be similar to ensure a continuous visual appearance when the MipmapDrawable is scaled. The aspect
+ * ratio of the different mipmaps should especially be equal.
+ * </p>
+ *
+ * A typical example use of a MipmapDrawable would be for an image which is intended to be scaled at
+ * various sizes, and for which one wants to provide pre-scaled versions to precisely control its
+ * appearance.
+ *
+ * <p>
+ * The intrinsic size of a MipmapDrawable are inferred from those of the largest mipmap (in terms of
+ * {@link Drawable#getIntrinsicHeight()}). On the opposite, its minimum
+ * size is defined by the smallest provided mipmap.
+ * </p>
+
+ * It can be defined in an XML file with the <code>&lt;mipmap></code> element.
+ * Each mipmap Drawable is defined in a nested <code>&lt;item></code>. For example:
+ * <pre>
+ * &lt;mipmap xmlns:android="http://schemas.android.com/apk/res/android">
+ *  &lt;item android:drawable="@drawable/my_image_8" />
+ *  &lt;item android:drawable="@drawable/my_image_32" />
+ *  &lt;item android:drawable="@drawable/my_image_128" />
+ * &lt;/mipmap>
+ *</pre>
+ * <p>
+ * With this XML saved into the res/drawable/ folder of the project, it can be referenced as
+ * the drawable for an {@link android.widget.ImageView}. Assuming that the heights of the provided
+ * drawables are respectively 8, 32 and 128 pixels, the first one will be scaled down when the
+ * bounds' height is lower or equal than 8 pixels. The second drawable will then be used up to a
+ * height of 32 pixels and the largest drawable will be used for greater heights.
+ * </p>
+ * @attr ref android.R.styleable#MipmapDrawableItem_drawable
+ */
+public class MipmapDrawable extends DrawableContainer {
+    private final MipmapContainerState mMipmapContainerState;
+    private boolean mMutated;
+
+    public MipmapDrawable() {
+        this(null, null);
+    }
+
+    /**
+     * Adds a Drawable to the list of available mipmap Drawables. The Drawable actually used when
+     * this MipmapDrawable is drawn is determined from its bounds.
+     *
+     * This method has no effect if drawable is null.
+     *
+     * @param drawable The Drawable that will be added to list of available mipmap Drawables.
+     */
+
+    public void addDrawable(Drawable drawable) {
+        if (drawable != null) {
+            mMipmapContainerState.addDrawable(drawable);
+            onDrawableAdded();
+        }
+    }
+
+    private void onDrawableAdded() {
+        // selectDrawable assumes that the container content does not change.
+        // When a Drawable is added, the same index can correspond to a new Drawable, and since
+        // selectDrawable has a fast exit case when oldIndex==newIndex, the new drawable could end
+        // up not being used in place of the previous one if they happen to share the same index.
+        // This make sure the new computed index can actually replace the previous one.
+        selectDrawable(-1);
+        onBoundsChange(getBounds());
+    }
+
+    // overrides from Drawable
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        final int index = mMipmapContainerState.indexForBounds(bounds);
+
+        // Will call invalidateSelf() if needed
+        selectDrawable(index);
+
+        super.onBoundsChange(bounds);
+    }
+
+    @Override
+    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+    throws XmlPullParserException, IOException {
+
+        super.inflate(r, parser, attrs);
+
+        int type;
+
+        final int innerDepth = parser.getDepth() + 1;
+        int depth;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && ((depth = parser.getDepth()) >= innerDepth
+                        || type != XmlPullParser.END_TAG)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            if (depth > innerDepth || !parser.getName().equals("item")) {
+                continue;
+            }
+
+            TypedArray a = r.obtainAttributes(attrs,
+                    com.android.internal.R.styleable.MipmapDrawableItem);
+
+            int drawableRes = a.getResourceId(
+                    com.android.internal.R.styleable.MipmapDrawableItem_drawable, 0);
+
+            a.recycle();
+
+            Drawable dr;
+            if (drawableRes != 0) {
+                dr = r.getDrawable(drawableRes);
+            } else {
+                while ((type = parser.next()) == XmlPullParser.TEXT) {
+                }
+                if (type != XmlPullParser.START_TAG) {
+                    throw new XmlPullParserException(
+                            parser.getPositionDescription()
+                            + ": <item> tag requires a 'drawable' attribute or "
+                            + "child tag defining a drawable");
+                }
+                dr = Drawable.createFromXmlInner(r, parser, attrs);
+            }
+
+            mMipmapContainerState.addDrawable(dr);
+        }
+
+        onDrawableAdded();
+    }
+
+    @Override
+    public Drawable mutate() {
+        if (!mMutated && super.mutate() == this) {
+            mMipmapContainerState.mMipmapHeights = mMipmapContainerState.mMipmapHeights.clone();
+            mMutated = true;
+        }
+        return this;
+    }
+
+    private final static class MipmapContainerState extends DrawableContainerState {
+        private int[] mMipmapHeights;
+
+        MipmapContainerState(MipmapContainerState orig, MipmapDrawable owner, Resources res) {
+            super(orig, owner, res);
+
+            if (orig != null) {
+                mMipmapHeights = orig.mMipmapHeights;
+            } else {
+                mMipmapHeights = new int[getChildren().length];
+            }
+
+            // Change the default value
+            setConstantSize(true);
+        }
+
+        /**
+         * Returns the index of the child mipmap drawable that will best fit the provided bounds.
+         * This index is determined by comparing bounds' height and children intrinsic heights.
+         * The returned mipmap index is the smallest mipmap which height is greater or equal than
+         * the bounds' height. If the bounds' height is larger than the largest mipmap, the largest
+         * mipmap index is returned.
+         *
+         * @param bounds The bounds of the MipMapDrawable.
+         * @return The index of the child Drawable that will best fit these bounds, or -1 if there
+         * are no children mipmaps.
+         */
+        public int indexForBounds(Rect bounds) {
+            final int boundsHeight = bounds.height();
+            final int N = getChildCount();
+            for (int i = 0; i < N; i++) {
+                if (boundsHeight <= mMipmapHeights[i]) {
+                    return i;
+                }
+            }
+
+            // No mipmap larger than bounds found. Use largest one which will be scaled up.
+            if (N > 0) {
+                return N - 1;
+            }
+            // No Drawable mipmap at all
+            return -1;
+        }
+
+        /**
+         * Adds a Drawable to the list of available mipmap Drawables. This list can be retrieved
+         * using {@link DrawableContainer.DrawableContainerState#getChildren()} and this method
+         * ensures that it is always sorted by increasing {@link Drawable#getIntrinsicHeight()}.
+         *
+         * @param drawable The Drawable that will be added to children list
+         */
+        public void addDrawable(Drawable drawable) {
+            // Insert drawable in last position, correctly resetting cached values and
+            // especially mComputedConstantSize
+            int pos = addChild(drawable);
+
+            // Bubble sort the last drawable to restore the sort by intrinsic height
+            final int drawableHeight = drawable.getIntrinsicHeight();
+
+            while (pos > 0) {
+                final Drawable previousDrawable = mDrawables[pos-1];
+                final int previousIntrinsicHeight = previousDrawable.getIntrinsicHeight();
+
+                if (drawableHeight < previousIntrinsicHeight) {
+                    mDrawables[pos] = previousDrawable;
+                    mMipmapHeights[pos] = previousIntrinsicHeight;
+
+                    mDrawables[pos-1] = drawable;
+                    mMipmapHeights[pos-1] = drawableHeight;
+                    pos--;
+                } else {
+                    break;
+                }
+            }
+        }
+
+        /**
+         * Intrinsic sizes are those of the largest available mipmap.
+         * Minimum sizes are those of the smallest available mipmap.
+         */
+        @Override
+        protected void computeConstantSize() {
+            final int N = getChildCount();
+            if (N > 0) {
+                final Drawable smallestDrawable = mDrawables[0];
+                mConstantMinimumWidth = smallestDrawable.getMinimumWidth();
+                mConstantMinimumHeight = smallestDrawable.getMinimumHeight();
+
+                final Drawable largestDrawable = mDrawables[N-1];
+                mConstantWidth = largestDrawable.getIntrinsicWidth();
+                mConstantHeight = largestDrawable.getIntrinsicHeight();
+            } else {
+                mConstantWidth = mConstantHeight = -1;
+                mConstantMinimumWidth = mConstantMinimumHeight = 0;
+            }
+            mComputedConstantSize = true;
+        }
+
+        @Override
+        public Drawable newDrawable() {
+            return new MipmapDrawable(this, null);
+        }
+
+        @Override
+        public Drawable newDrawable(Resources res) {
+            return new MipmapDrawable(this, res);
+        }
+
+        @Override
+        public void growArray(int oldSize, int newSize) {
+            super.growArray(oldSize, newSize);
+            int[] newInts = new int[newSize];
+            System.arraycopy(mMipmapHeights, 0, newInts, 0, oldSize);
+            mMipmapHeights = newInts;
+        }
+    }
+
+    private MipmapDrawable(MipmapContainerState state, Resources res) {
+        MipmapContainerState as = new MipmapContainerState(state, this, res);
+        mMipmapContainerState = as;
+        setConstantState(as);
+        onDrawableAdded();
+    }
+}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 00416d8..50b4b75 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -21,7 +21,6 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
-import android.util.Log;
 import android.util.TypedValue;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -99,7 +98,8 @@
         mPadding = state.mPadding;
         mTargetDensity = res != null ? res.getDisplayMetrics().densityDpi
                 : state.mTargetDensity;
-        if (DEFAULT_DITHER != state.mDither) {
+        //noinspection PointlessBooleanExpression
+        if (state.mDither != DEFAULT_DITHER) {
             // avoid calling the setter unless we need to, since it does a
             // lazy allocation of a paint
             setDither(state.mDither);
@@ -177,16 +177,9 @@
             }
         }
     }
-    
-    // overrides
 
     @Override
     public void draw(Canvas canvas) {
-        if (false) {
-            float[] pts = new float[2];
-            canvas.getMatrix().mapPoints(pts);
-            Log.v("9patch", "Drawing 9-patch @ " + pts[0] + "," + pts[1] + ": " + getBounds());
-        }
         mNinePatch.draw(canvas, getBounds(), mPaint);
     }
 
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 239be40..384ca81 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -20,6 +20,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.util.Arrays;
 
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -44,6 +45,7 @@
  * @attr ref android.R.styleable#DrawableStates_state_checkable
  * @attr ref android.R.styleable#DrawableStates_state_checked
  * @attr ref android.R.styleable#DrawableStates_state_selected
+ * @attr ref android.R.styleable#DrawableStates_state_activated
  * @attr ref android.R.styleable#DrawableStates_state_active
  * @attr ref android.R.styleable#DrawableStates_state_single
  * @attr ref android.R.styleable#DrawableStates_state_first
@@ -52,6 +54,9 @@
  * @attr ref android.R.styleable#DrawableStates_state_pressed
  */
 public class StateListDrawable extends DrawableContainer {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "StateListDrawable";
+
     /**
      * To be proper, we should have a getter for dither (and alpha, etc.)
      * so that proxy classes like this can save/restore their delegates'
@@ -93,6 +98,8 @@
     @Override
     protected boolean onStateChange(int[] stateSet) {
         int idx = mStateListState.indexOfStateSet(stateSet);
+        if (DEBUG) android.util.Log.i(TAG, "onStateChange " + this + " states "
+                + Arrays.toString(stateSet) + " found " + idx);
         if (idx < 0) {
             idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD);
         }
@@ -117,6 +124,10 @@
                 com.android.internal.R.styleable.StateListDrawable_variablePadding, false));
         mStateListState.setConstantSize(a.getBoolean(
                 com.android.internal.R.styleable.StateListDrawable_constantSize, false));
+        mStateListState.setEnterFadeDuration(a.getInt(
+                com.android.internal.R.styleable.StateListDrawable_enterFadeDuration, 0));
+        mStateListState.setExitFadeDuration(a.getInt(
+                com.android.internal.R.styleable.StateListDrawable_exitFadeDuration, 0));
 
         setDither(a.getBoolean(com.android.internal.R.styleable.StateListDrawable_dither,
                                DEFAULT_DITHER));
@@ -251,7 +262,7 @@
     }
 
     static final class StateListState extends DrawableContainerState {
-        private int[][] mStateSets;
+        int[][] mStateSets;
 
         StateListState(StateListState orig, StateListDrawable owner, Resources res) {
             super(orig, owner, res);
diff --git a/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java b/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java
index f4cf15c..b469d2a 100644
--- a/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java
+++ b/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java
@@ -57,13 +57,11 @@
      */
     public RoundRectShape(float[] outerRadii, RectF inset,
                           float[] innerRadii) {
-        if (outerRadii.length < 8) {
-            throw new ArrayIndexOutOfBoundsException(
-                                        "outer radii must have >= 8 values");
+        if (outerRadii != null && outerRadii.length < 8) {
+            throw new ArrayIndexOutOfBoundsException("outer radii must have >= 8 values");
         }
         if (innerRadii != null && innerRadii.length < 8) {
-            throw new ArrayIndexOutOfBoundsException(
-                                        "inner radii must have >= 8 values");
+            throw new ArrayIndexOutOfBoundsException("inner radii must have >= 8 values");
         }
         mOuterRadii = outerRadii;
         mInset = inset;
@@ -97,8 +95,7 @@
                            r.right - mInset.right, r.bottom - mInset.bottom);
             if (mInnerRect.width() < w && mInnerRect.height() < h) {
                 if (mInnerRadii != null) {
-                    mPath.addRoundRect(mInnerRect, mInnerRadii,
-                                       Path.Direction.CCW);
+                    mPath.addRoundRect(mInnerRect, mInnerRadii, Path.Direction.CCW);
                 } else {
                     mPath.addRect(mInnerRect, Path.Direction.CCW);
                 }
@@ -109,8 +106,8 @@
     @Override
     public RoundRectShape clone() throws CloneNotSupportedException {
         RoundRectShape shape = (RoundRectShape) super.clone();
-        shape.mOuterRadii = mOuterRadii.clone();
-        shape.mInnerRadii = mInnerRadii.clone();
+        shape.mOuterRadii = mOuterRadii != null ? mOuterRadii.clone() : null;
+        shape.mInnerRadii = mInnerRadii != null ? mInnerRadii.clone() : null;
         shape.mInset = new RectF(mInset);
         shape.mInnerRect = new RectF(mInnerRect);
         shape.mPath = new Path(mPath);
diff --git a/graphics/java/android/graphics/utils/BoundaryPatch.java b/graphics/java/android/graphics/utils/BoundaryPatch.java
deleted file mode 100644
index 1cd5e13..0000000
--- a/graphics/java/android/graphics/utils/BoundaryPatch.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.utils;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Shader;
-import android.graphics.Xfermode;
-
-/**
- * @hide
- */
-public class BoundaryPatch {
-    private Paint   mPaint;
-    private Bitmap  mTexture;
-    private int     mRows;
-    private int     mCols;
-    private float[] mCubicPoints;
-    private boolean mDirty;
-    // these are the computed output of the native code
-    private float[] mVerts;
-    private short[] mIndices;
-
-    public BoundaryPatch() {
-        mRows = mCols = 2;  // default minimum
-        mCubicPoints = new float[24];
-        mPaint = new Paint();
-        mPaint.setDither(true);
-        mPaint.setFilterBitmap(true);
-        mDirty = true;
-    }
-
-    /**
-     * Set the boundary to be 4 cubics. This takes a single array of floats,
-     * and picks up the 12 pairs starting at offset, and treats them as
-     * the x,y coordinates of the cubic control points. The points wrap around
-     * a patch, as follows. For documentation purposes, pts[i] will mean the
-     * x,y pair of floats, as if pts[] were an array of "points".
-     *
-     * Top: pts[0..3]
-     * Right: pts[3..6]
-     * Bottom: pts[6..9]
-     * Right: pts[9..11], pts[0]
-     *
-     * The coordinates are copied from the input array, so subsequent changes
-     * to pts[] will not be reflected in the boundary.
-     *
-     * @param pts The src array of x,y pairs for the boundary cubics
-     * @param offset The index into pts of the first pair
-     * @param rows The number of points across to approximate the boundary.
-     *             Must be >= 2, though very large values may slow down drawing
-     * @param cols The number of points down to approximate the boundary.
-     *             Must be >= 2, though very large values may slow down drawing
-     */
-    public void setCubicBoundary(float[] pts, int offset, int rows, int cols) {
-        if (rows < 2 || cols < 2) {
-            throw new RuntimeException("rows and cols must be >= 2");
-        }
-        System.arraycopy(pts, offset, mCubicPoints, 0, 24);
-        if (mRows != rows || mCols != cols) {
-            mRows = rows;
-            mCols = cols;
-        }
-        mDirty = true;
-    }
-
-    /**
-     * Reference a bitmap texture to be mapped onto the patch.
-     */
-    public void setTexture(Bitmap texture) {
-        if (mTexture != texture) {
-            if (mTexture == null ||
-                    mTexture.getWidth() != texture.getWidth() ||
-                    mTexture.getHeight() != texture.getHeight()) {
-                // need to recompute texture coordinates
-                mDirty = true;
-            }
-            mTexture = texture;
-            mPaint.setShader(new BitmapShader(texture,
-                                              Shader.TileMode.CLAMP,
-                                              Shader.TileMode.CLAMP));
-        }
-    }
-
-    /**
-     * Return the paint flags for the patch
-     */
-    public int getPaintFlags() {
-        return mPaint.getFlags();
-    }
-
-    /**
-     * Set the paint flags for the patch
-     */
-    public void setPaintFlags(int flags) {
-        mPaint.setFlags(flags);
-    }
-
-    /**
-     * Set the xfermode for the patch
-     */
-    public void setXfermode(Xfermode mode) {
-        mPaint.setXfermode(mode);
-    }
-
-    /**
-     * Set the alpha for the patch
-     */
-    public void setAlpha(int alpha) {
-        mPaint.setAlpha(alpha);
-    }
-
-    /**
-     * Draw the patch onto the canvas.
-     *
-     * setCubicBoundary() and setTexture() must be called before drawing.
-     */
-    public void draw(Canvas canvas) {
-        if (mDirty) {
-            buildCache();
-            mDirty = false;
-        }
-
-        // cut the count in half, since mVerts.length is really the length of
-        // the verts[] and tex[] arrays combined
-        // (tex[] are stored after verts[])
-        int vertCount = mVerts.length >> 1;
-        canvas.drawVertices(Canvas.VertexMode.TRIANGLES, vertCount,
-                            mVerts, 0, mVerts, vertCount, null, 0,
-                            mIndices, 0, mIndices.length,
-                            mPaint);
-    }
-
-    private void buildCache() {
-        // we need mRows * mCols points, for verts and another set for textures
-        // so *2 for going from points -> floats, and *2 for verts and textures
-        int vertCount = mRows * mCols * 4;
-        if (mVerts == null || mVerts.length != vertCount) {
-            mVerts = new float[vertCount];
-        }
-
-        int indexCount = (mRows - 1) * (mCols - 1) * 6;
-        if (mIndices == null || mIndices.length != indexCount) {
-            mIndices = new short[indexCount];
-        }
-
-        nativeComputeCubicPatch(mCubicPoints,
-                                mTexture.getWidth(), mTexture.getHeight(),
-                                mRows, mCols, mVerts, mIndices);
-    }
-
-    private static native
-    void nativeComputeCubicPatch(float[] cubicPoints,
-                                 int texW, int texH, int rows, int cols,
-                                 float[] verts, short[] indices);
-}
-
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index b27c7f5..dfd6ac8 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -35,11 +35,25 @@
     Bitmap mBitmap;
 
     Allocation(int id, RenderScript rs, Type t) {
-        super(rs);
-        mID = id;
+        super(id, rs);
         mType = t;
     }
 
+    Allocation(int id, RenderScript rs) {
+        super(id, rs);
+    }
+
+    @Override
+    void updateFromNative() {
+        mRS.validate();
+        mName = mRS.nGetName(mID);
+        int typeID = mRS.nAllocationGetType(mID);
+        if(typeID != 0) {
+            mType = new Type(typeID, mRS);
+            mType.updateFromNative();
+        }
+    }
+
     public Type getType() {
         return mType;
     }
@@ -76,10 +90,62 @@
         subData1D(0, mType.getElementCount(), d);
     }
 
+    public void updateFromBitmap(Bitmap b)
+        throws IllegalArgumentException {
+
+        mRS.validate();
+        if(mType.getX() != b.getWidth() ||
+           mType.getY() != b.getHeight()) {
+            throw new IllegalArgumentException("Cannot update allocation from bitmap, sizes mismatch");
+        }
+
+        mRS.nAllocationUpdateFromBitmap(mID, b);
+    }
+
+    public void subData(int xoff, FieldPacker fp) {
+        int eSize = mType.mElement.getSizeBytes();
+        final byte[] data = fp.getData();
+
+        int count = data.length / eSize;
+        if ((eSize * count) != data.length) {
+            throw new IllegalArgumentException("Field packer length " + data.length +
+                                               " not divisible by element size " + eSize + ".");
+        }
+        data1DChecks(xoff, count, data.length, data.length);
+        mRS.nAllocationSubData1D(mID, xoff, count, data, data.length);
+    }
+
+
+    public void subElementData(int xoff, int component_number, FieldPacker fp) {
+        if (component_number >= mType.mElement.mElements.length) {
+            throw new IllegalArgumentException("Component_number " + component_number + " out of range.");
+        }
+        if(xoff < 0) {
+            throw new IllegalArgumentException("Offset must be >= 0.");
+        }
+
+        final byte[] data = fp.getData();
+        int eSize = mType.mElement.mElements[component_number].getSizeBytes();
+
+        if (data.length != eSize) {
+            throw new IllegalArgumentException("Field packer sizelength " + data.length +
+                                               " does not match component size " + eSize + ".");
+        }
+
+        mRS.nAllocationSubElementData1D(mID, xoff, component_number, data, data.length);
+    }
+
     private void data1DChecks(int off, int count, int len, int dataSize) {
         mRS.validate();
-        if((off < 0) || (count < 1) || ((off + count) > mType.getElementCount())) {
-            throw new IllegalArgumentException("Offset or Count out of bounds.");
+        if(off < 0) {
+            throw new IllegalArgumentException("Offset must be >= 0.");
+        }
+        if(count < 1) {
+            throw new IllegalArgumentException("Count must be >= 1.");
+        }
+        if((off + count) > mType.getElementCount()) {
+            throw new IllegalArgumentException("Overflow, Available count " + mType.getElementCount() +
+                                               ", got " + count + " at offset " + off + ".");
         }
         if((len) < dataSize) {
             throw new IllegalArgumentException("Array too small for allocation type.");
@@ -108,7 +174,6 @@
     }
 
 
-
     public void subData2D(int xoff, int yoff, int w, int h, int[] d) {
         mRS.validate();
         mRS.nAllocationSubData2D(mID, xoff, yoff, w, h, d, d.length * 4);
@@ -129,25 +194,33 @@
         mRS.nAllocationRead(mID, d);
     }
 
-    public void data(Object o) {
-        mRS.validate();
-        mRS.nAllocationSubDataFromObject(mID, mType, 0, o);
+    public synchronized void resize(int dimX) {
+        if ((mType.getY() > 0)|| (mType.getZ() > 0) || mType.getFaces() || mType.getLOD()) {
+            throw new IllegalStateException("Resize only support for 1D allocations at this time.");
+        }
+        mRS.nAllocationResize1D(mID, dimX);
+        mRS.finish();  // Necessary because resize is fifoed and update is async.
+
+        int typeID = mRS.nAllocationGetType(mID);
+        mType = new Type(typeID, mRS);
+        mType.updateFromNative();
     }
 
-    public void read(Object o) {
-        mRS.validate();
-        mRS.nAllocationSubReadFromObject(mID, mType, 0, o);
+    /*
+    public void resize(int dimX, int dimY) {
+        if ((mType.getZ() > 0) || mType.getFaces() || mType.getLOD()) {
+            throw new IllegalStateException("Resize only support for 2D allocations at this time.");
+        }
+        if (mType.getY() == 0) {
+            throw new IllegalStateException("Resize only support for 2D allocations at this time.");
+        }
+        mRS.nAllocationResize2D(mID, dimX, dimY);
     }
-
-    public void subData(int offset, Object o) {
-        mRS.validate();
-        mRS.nAllocationSubDataFromObject(mID, mType, offset, o);
-    }
+    */
 
     public class Adapter1D extends BaseObj {
         Adapter1D(int id, RenderScript rs) {
-            super(rs);
-            mID = id;
+            super(id, rs);
         }
 
         public void setConstraint(Dimension dim, int value) {
@@ -189,8 +262,7 @@
 
     public class Adapter2D extends BaseObj {
         Adapter2D(int id, RenderScript rs) {
-            super(rs);
-            mID = id;
+            super(id, rs);
         }
 
         public void setConstraint(Dimension dim, int value) {
@@ -317,17 +389,6 @@
         return a;
     }
 
-    static Allocation createFromBitmapBoxed(RenderScript rs, Bitmap b, Element dstFmt, boolean genMips)
-        throws IllegalArgumentException {
-
-        rs.validate();
-        int id = rs.nAllocationCreateFromBitmapBoxed(dstFmt.mID, genMips, b);
-        if(id == 0) {
-            throw new IllegalStateException("Load failed.");
-        }
-        return new Allocation(id, rs, null);
-    }
-
     static public Allocation createFromBitmapResource(RenderScript rs, Resources res, int id, Element dstFmt, boolean genMips)
         throws IllegalArgumentException {
 
@@ -360,22 +421,19 @@
         return null;
     }
 
-    static public Allocation createFromBitmapResourceBoxed(RenderScript rs, Resources res, int id, Element dstFmt, boolean genMips)
+    static public Allocation createFromString(RenderScript rs, String str)
         throws IllegalArgumentException {
-
-        mBitmapOptions.inPreferredConfig = null;
-        if (dstFmt == rs.mElement_RGBA_8888) {
-            mBitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
-        } else if (dstFmt == rs.mElement_RGB_888) {
-            mBitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
-        } else if (dstFmt == rs.mElement_RGBA_4444) {
-            mBitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_4444;
-        } else if (dstFmt == rs.mElement_RGB_565) {
-            mBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
+        byte[] allocArray = null;
+        try {
+            allocArray = str.getBytes("UTF-8");
+            Allocation alloc = Allocation.createSized(rs, Element.U8(rs), allocArray.length);
+            alloc.data(allocArray);
+            return alloc;
         }
-
-        Bitmap b = BitmapFactory.decodeResource(res, id, mBitmapOptions);
-        return createFromBitmapBoxed(rs, b, dstFmt, genMips);
+        catch (Exception e) {
+            Log.e("rs", "could not convert string to utf-8");
+        }
+        return null;
     }
 }
 
diff --git a/graphics/java/android/renderscript/BaseObj.java b/graphics/java/android/renderscript/BaseObj.java
index 002fc78..69907d9 100644
--- a/graphics/java/android/renderscript/BaseObj.java
+++ b/graphics/java/android/renderscript/BaseObj.java
@@ -24,14 +24,17 @@
  **/
 class BaseObj {
 
-    BaseObj(RenderScript rs) {
+    BaseObj(int id, RenderScript rs) {
         rs.validate();
         mRS = rs;
-        mID = 0;
+        mID = id;
         mDestroyed = false;
     }
 
     public int getID() {
+        if (mDestroyed) {
+            throw new RSInvalidStateException("using a destroyed object.");
+        }
         return mID;
     }
 
@@ -40,13 +43,12 @@
     String mName;
     RenderScript mRS;
 
-    public void setName(String s) throws IllegalStateException, IllegalArgumentException
-    {
+    public void setName(String s) {
         if(s.length() < 1) {
-            throw new IllegalArgumentException("setName does not accept a zero length string.");
+            throw new RSIllegalArgumentException("setName does not accept a zero length string.");
         }
         if(mName != null) {
-            throw new IllegalArgumentException("setName object already has a name.");
+            throw new RSIllegalArgumentException("setName object already has a name.");
         }
 
         try {
@@ -58,11 +60,10 @@
         }
     }
 
-    protected void finalize() throws Throwable
-    {
+    protected void finalize() throws Throwable {
         if (!mDestroyed) {
             if(mID != 0 && mRS.isAlive()) {
-                mRS.nObjDestroyOOB(mID);
+                mRS.nObjDestroy(mID);
             }
             mRS = null;
             mID = 0;
@@ -75,11 +76,16 @@
 
     public void destroy() {
         if(mDestroyed) {
-            throw new IllegalStateException("Object already destroyed.");
+            throw new RSInvalidStateException("Object already destroyed.");
         }
         mDestroyed = true;
         mRS.nObjDestroy(mID);
     }
 
+    // If an object came from an a3d file, java fields need to be
+    // created with objects from the native layer
+    void updateFromNative() {
+    }
+
 }
 
diff --git a/graphics/java/android/renderscript/Byte2.java b/graphics/java/android/renderscript/Byte2.java
new file mode 100644
index 0000000..95cf88c
--- /dev/null
+++ b/graphics/java/android/renderscript/Byte2.java
@@ -0,0 +1,37 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Byte2 {
+    public Byte2() {
+    }
+
+    public byte x;
+    public byte y;
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/Byte3.java b/graphics/java/android/renderscript/Byte3.java
new file mode 100644
index 0000000..a6c0ca9
--- /dev/null
+++ b/graphics/java/android/renderscript/Byte3.java
@@ -0,0 +1,38 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Byte3 {
+    public Byte3() {
+    }
+
+    public byte x;
+    public byte y;
+    public byte z;
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/Byte4.java b/graphics/java/android/renderscript/Byte4.java
new file mode 100644
index 0000000..a5bfc61
--- /dev/null
+++ b/graphics/java/android/renderscript/Byte4.java
@@ -0,0 +1,38 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Byte4 {
+    public Byte4() {
+    }
+
+    public byte x;
+    public byte y;
+    public byte z;
+    public byte w;
+}
+
+
+
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index 10ef05a..f844b7e 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -17,6 +17,7 @@
 package android.renderscript;
 
 import java.lang.reflect.Field;
+import android.util.Log;
 
 /**
  * @hide
@@ -26,6 +27,7 @@
     int mSize;
     Element[] mElements;
     String[] mElementNames;
+    int[] mArraySizes;
 
     DataType mType;
     DataKind mKind;
@@ -37,30 +39,36 @@
     public enum DataType {
         //FLOAT_16 (1, 2),
         FLOAT_32 (2, 4),
-        //FLOAT_64 (3, 8),
+        FLOAT_64 (3, 8),
         SIGNED_8 (4, 1),
         SIGNED_16 (5, 2),
         SIGNED_32 (6, 4),
-        //SIGNED_64 (7, 8),
+        SIGNED_64 (7, 8),
         UNSIGNED_8 (8, 1),
         UNSIGNED_16 (9, 2),
         UNSIGNED_32 (10, 4),
-        //UNSIGNED_64 (11, 8),
+        UNSIGNED_64 (11, 8),
 
-        UNSIGNED_5_6_5 (12, 2),
-        UNSIGNED_5_5_5_1 (13, 2),
-        UNSIGNED_4_4_4_4 (14, 2),
+        BOOLEAN(12, 1),
 
-        RS_ELEMENT (15, 4),
-        RS_TYPE (16, 4),
-        RS_ALLOCATION (17, 4),
-        RS_SAMPLER (18, 4),
-        RS_SCRIPT (19, 4),
-        RS_MESH (20, 4),
-        RS_PROGRAM_FRAGMENT (21, 4),
-        RS_PROGRAM_VERTEX (22, 4),
-        RS_PROGRAM_RASTER (23, 4),
-        RS_PROGRAM_STORE (24, 4);
+        UNSIGNED_5_6_5 (13, 2),
+        UNSIGNED_5_5_5_1 (14, 2),
+        UNSIGNED_4_4_4_4 (15, 2),
+
+        MATRIX_4X4 (16, 64),
+        MATRIX_3X3 (17, 36),
+        MATRIX_2X2 (18, 16),
+
+        RS_ELEMENT (1000, 4),
+        RS_TYPE (1001, 4),
+        RS_ALLOCATION (1002, 4),
+        RS_SAMPLER (1003, 4),
+        RS_SCRIPT (1004, 4),
+        RS_MESH (1005, 4),
+        RS_PROGRAM_FRAGMENT (1006, 4),
+        RS_PROGRAM_VERTEX (1007, 4),
+        RS_PROGRAM_RASTER (1008, 4),
+        RS_PROGRAM_STORE (1009, 4);
 
         int mID;
         int mSize;
@@ -72,12 +80,6 @@
 
     public enum DataKind {
         USER (0),
-        COLOR (1),
-        POSITION (2),
-        TEXTURE (3),
-        NORMAL (4),
-        INDEX (5),
-        POINT_SIZE(6),
 
         PIXEL_L (7),
         PIXEL_A (8),
@@ -91,41 +93,166 @@
         }
     }
 
-    public static Element USER_U8(RenderScript rs) {
-        if(rs.mElement_USER_U8 == null) {
-            rs.mElement_USER_U8 = createUser(rs, DataType.UNSIGNED_8);
+    public boolean isComplex() {
+        if (mElements == null) {
+            return false;
         }
-        return rs.mElement_USER_U8;
+        for (int ct=0; ct < mElements.length; ct++) {
+            if (mElements[ct].mElements != null) {
+                return true;
+            }
+        }
+        return false;
     }
 
-    public static Element USER_I8(RenderScript rs) {
-        if(rs.mElement_USER_I8 == null) {
-            rs.mElement_USER_I8 = createUser(rs, DataType.SIGNED_8);
+    public static Element BOOLEAN(RenderScript rs) {
+        if(rs.mElement_BOOLEAN == null) {
+            rs.mElement_BOOLEAN = createUser(rs, DataType.BOOLEAN);
         }
-        return rs.mElement_USER_I8;
+        return rs.mElement_BOOLEAN;
     }
 
-    public static Element USER_U32(RenderScript rs) {
-        if(rs.mElement_USER_U32 == null) {
-            rs.mElement_USER_U32 = createUser(rs, DataType.UNSIGNED_32);
+    public static Element U8(RenderScript rs) {
+        if(rs.mElement_U8 == null) {
+            rs.mElement_U8 = createUser(rs, DataType.UNSIGNED_8);
         }
-        return rs.mElement_USER_U32;
+        return rs.mElement_U8;
     }
 
-    public static Element USER_I32(RenderScript rs) {
-        if(rs.mElement_USER_I32 == null) {
-            rs.mElement_USER_I32 = createUser(rs, DataType.SIGNED_32);
+    public static Element I8(RenderScript rs) {
+        if(rs.mElement_I8 == null) {
+            rs.mElement_I8 = createUser(rs, DataType.SIGNED_8);
         }
-        return rs.mElement_USER_I32;
+        return rs.mElement_I8;
     }
 
-    public static Element USER_F32(RenderScript rs) {
-        if(rs.mElement_USER_F32 == null) {
-            rs.mElement_USER_F32 = createUser(rs, DataType.FLOAT_32);
+    public static Element U16(RenderScript rs) {
+        if(rs.mElement_U16 == null) {
+            rs.mElement_U16 = createUser(rs, DataType.UNSIGNED_16);
         }
-        return rs.mElement_USER_F32;
+        return rs.mElement_U16;
     }
 
+    public static Element I16(RenderScript rs) {
+        if(rs.mElement_I16 == null) {
+            rs.mElement_I16 = createUser(rs, DataType.SIGNED_16);
+        }
+        return rs.mElement_I16;
+    }
+
+    public static Element U32(RenderScript rs) {
+        if(rs.mElement_U32 == null) {
+            rs.mElement_U32 = createUser(rs, DataType.UNSIGNED_32);
+        }
+        return rs.mElement_U32;
+    }
+
+    public static Element I32(RenderScript rs) {
+        if(rs.mElement_I32 == null) {
+            rs.mElement_I32 = createUser(rs, DataType.SIGNED_32);
+        }
+        return rs.mElement_I32;
+    }
+
+    public static Element U64(RenderScript rs) {
+        if(rs.mElement_U64 == null) {
+            rs.mElement_U64 = createUser(rs, DataType.UNSIGNED_64);
+        }
+        return rs.mElement_U64;
+    }
+
+    public static Element I64(RenderScript rs) {
+        if(rs.mElement_I64 == null) {
+            rs.mElement_I64 = createUser(rs, DataType.SIGNED_64);
+        }
+        return rs.mElement_I64;
+    }
+
+    public static Element F32(RenderScript rs) {
+        if(rs.mElement_F32 == null) {
+            rs.mElement_F32 = createUser(rs, DataType.FLOAT_32);
+        }
+        return rs.mElement_F32;
+    }
+
+    public static Element F64(RenderScript rs) {
+        if(rs.mElement_F64 == null) {
+            rs.mElement_F64 = createUser(rs, DataType.FLOAT_64);
+        }
+        return rs.mElement_F64;
+    }
+
+    public static Element ELEMENT(RenderScript rs) {
+        if(rs.mElement_ELEMENT == null) {
+            rs.mElement_ELEMENT = createUser(rs, DataType.RS_ELEMENT);
+        }
+        return rs.mElement_ELEMENT;
+    }
+
+    public static Element TYPE(RenderScript rs) {
+        if(rs.mElement_TYPE == null) {
+            rs.mElement_TYPE = createUser(rs, DataType.RS_TYPE);
+        }
+        return rs.mElement_TYPE;
+    }
+
+    public static Element ALLOCATION(RenderScript rs) {
+        if(rs.mElement_ALLOCATION == null) {
+            rs.mElement_ALLOCATION = createUser(rs, DataType.RS_ALLOCATION);
+        }
+        return rs.mElement_ALLOCATION;
+    }
+
+    public static Element SAMPLER(RenderScript rs) {
+        if(rs.mElement_SAMPLER == null) {
+            rs.mElement_SAMPLER = createUser(rs, DataType.RS_SAMPLER);
+        }
+        return rs.mElement_SAMPLER;
+    }
+
+    public static Element SCRIPT(RenderScript rs) {
+        if(rs.mElement_SCRIPT == null) {
+            rs.mElement_SCRIPT = createUser(rs, DataType.RS_SCRIPT);
+        }
+        return rs.mElement_SCRIPT;
+    }
+
+    public static Element MESH(RenderScript rs) {
+        if(rs.mElement_MESH == null) {
+            rs.mElement_MESH = createUser(rs, DataType.RS_MESH);
+        }
+        return rs.mElement_MESH;
+    }
+
+    public static Element PROGRAM_FRAGMENT(RenderScript rs) {
+        if(rs.mElement_PROGRAM_FRAGMENT == null) {
+            rs.mElement_PROGRAM_FRAGMENT = createUser(rs, DataType.RS_PROGRAM_FRAGMENT);
+        }
+        return rs.mElement_PROGRAM_FRAGMENT;
+    }
+
+    public static Element PROGRAM_VERTEX(RenderScript rs) {
+        if(rs.mElement_PROGRAM_VERTEX == null) {
+            rs.mElement_PROGRAM_VERTEX = createUser(rs, DataType.RS_PROGRAM_VERTEX);
+        }
+        return rs.mElement_PROGRAM_VERTEX;
+    }
+
+    public static Element PROGRAM_RASTER(RenderScript rs) {
+        if(rs.mElement_PROGRAM_RASTER == null) {
+            rs.mElement_PROGRAM_RASTER = createUser(rs, DataType.RS_PROGRAM_RASTER);
+        }
+        return rs.mElement_PROGRAM_RASTER;
+    }
+
+    public static Element PROGRAM_STORE(RenderScript rs) {
+        if(rs.mElement_PROGRAM_STORE == null) {
+            rs.mElement_PROGRAM_STORE = createUser(rs, DataType.RS_PROGRAM_STORE);
+        }
+        return rs.mElement_PROGRAM_STORE;
+    }
+
+
     public static Element A_8(RenderScript rs) {
         if(rs.mElement_A_8 == null) {
             rs.mElement_A_8 = createPixel(rs, DataType.UNSIGNED_8, DataKind.PIXEL_A);
@@ -168,168 +295,141 @@
         return rs.mElement_RGBA_8888;
     }
 
-    public static Element INDEX_16(RenderScript rs) {
-        if(rs.mElement_INDEX_16 == null) {
-            rs.mElement_INDEX_16 = createIndex(rs);
+    public static Element F32_2(RenderScript rs) {
+        if(rs.mElement_FLOAT_2 == null) {
+            rs.mElement_FLOAT_2 = createVector(rs, DataType.FLOAT_32, 2);
         }
-        return rs.mElement_INDEX_16;
+        return rs.mElement_FLOAT_2;
     }
 
-    public static Element ATTRIB_POSITION_2(RenderScript rs) {
-        if(rs.mElement_POSITION_2 == null) {
-            rs.mElement_POSITION_2 = createAttrib(rs, DataType.FLOAT_32, DataKind.POSITION, 2);
+    public static Element F32_3(RenderScript rs) {
+        if(rs.mElement_FLOAT_3 == null) {
+            rs.mElement_FLOAT_3 = createVector(rs, DataType.FLOAT_32, 3);
         }
-        return rs.mElement_POSITION_2;
+        return rs.mElement_FLOAT_3;
     }
 
-    public static Element ATTRIB_POSITION_3(RenderScript rs) {
-        if(rs.mElement_POSITION_3 == null) {
-            rs.mElement_POSITION_3 = createAttrib(rs, DataType.FLOAT_32, DataKind.POSITION, 3);
+    public static Element F32_4(RenderScript rs) {
+        if(rs.mElement_FLOAT_4 == null) {
+            rs.mElement_FLOAT_4 = createVector(rs, DataType.FLOAT_32, 4);
         }
-        return rs.mElement_POSITION_3;
+        return rs.mElement_FLOAT_4;
     }
 
-    public static Element ATTRIB_TEXTURE_2(RenderScript rs) {
-        if(rs.mElement_TEXTURE_2 == null) {
-            rs.mElement_TEXTURE_2 = createAttrib(rs, DataType.FLOAT_32, DataKind.TEXTURE, 2);
+    public static Element U8_4(RenderScript rs) {
+        if(rs.mElement_UCHAR_4 == null) {
+            rs.mElement_UCHAR_4 = createVector(rs, DataType.UNSIGNED_8, 4);
         }
-        return rs.mElement_TEXTURE_2;
+        return rs.mElement_UCHAR_4;
     }
 
-    public static Element ATTRIB_NORMAL_3(RenderScript rs) {
-        if(rs.mElement_NORMAL_3 == null) {
-            rs.mElement_NORMAL_3 = createAttrib(rs, DataType.FLOAT_32, DataKind.NORMAL, 3);
+    public static Element MATRIX_4X4(RenderScript rs) {
+        if(rs.mElement_MATRIX_4X4 == null) {
+            rs.mElement_MATRIX_4X4 = createUser(rs, DataType.MATRIX_4X4);
         }
-        return rs.mElement_NORMAL_3;
+        return rs.mElement_MATRIX_4X4;
+    }
+    public static Element MATRIX4X4(RenderScript rs) {
+        return MATRIX_4X4(rs);
     }
 
-    public static Element ATTRIB_COLOR_U8_4(RenderScript rs) {
-        if(rs.mElement_COLOR_U8_4 == null) {
-            rs.mElement_COLOR_U8_4 = createAttrib(rs, DataType.UNSIGNED_8, DataKind.COLOR, 4);
+    public static Element MATRIX_3X3(RenderScript rs) {
+        if(rs.mElement_MATRIX_3X3 == null) {
+            rs.mElement_MATRIX_3X3 = createUser(rs, DataType.MATRIX_3X3);
         }
-        return rs.mElement_COLOR_U8_4;
+        return rs.mElement_MATRIX_4X4;
     }
 
-    public static Element ATTRIB_COLOR_F32_4(RenderScript rs) {
-        if(rs.mElement_COLOR_F32_4 == null) {
-            rs.mElement_COLOR_F32_4 = createAttrib(rs, DataType.FLOAT_32, DataKind.COLOR, 4);
+    public static Element MATRIX_2X2(RenderScript rs) {
+        if(rs.mElement_MATRIX_2X2 == null) {
+            rs.mElement_MATRIX_2X2 = createUser(rs, DataType.MATRIX_2X2);
         }
-        return rs.mElement_COLOR_F32_4;
+        return rs.mElement_MATRIX_2X2;
     }
 
-    Element(RenderScript rs, Element[] e, String[] n) {
-        super(rs);
+    Element(int id, RenderScript rs, Element[] e, String[] n, int[] as) {
+        super(id, rs);
         mSize = 0;
         mElements = e;
         mElementNames = n;
-        int[] ids = new int[mElements.length];
+        mArraySizes = as;
         for (int ct = 0; ct < mElements.length; ct++ ) {
-            mSize += mElements[ct].mSize;
-            ids[ct] = mElements[ct].mID;
+            mSize += mElements[ct].mSize * mArraySizes[ct];
         }
-        mID = rs.nElementCreate2(ids, mElementNames);
     }
 
-    Element(RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
-        super(rs);
+    Element(int id, RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
+        super(id, rs);
         mSize = dt.mSize * size;
         mType = dt;
         mKind = dk;
         mNormalized = norm;
         mVectorSize = size;
-        mID = rs.nElementCreate(dt.mID, dk.mID, norm, size);
     }
 
-    public void destroy() throws IllegalStateException {
+    Element(int id, RenderScript rs) {
+        super(id, rs);
+    }
+
+    @Override
+    void updateFromNative() {
+
+        // we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
+        int[] dataBuffer = new int[5];
+        mRS.nElementGetNativeData(mID, dataBuffer);
+
+        mNormalized = dataBuffer[2] == 1 ? true : false;
+        mVectorSize = dataBuffer[3];
+        mSize = 0;
+        for (DataType dt: DataType.values()) {
+            if(dt.mID == dataBuffer[0]){
+                mType = dt;
+                mSize = mType.mSize * mVectorSize;
+            }
+        }
+        for (DataKind dk: DataKind.values()) {
+            if(dk.mID == dataBuffer[1]){
+                mKind = dk;
+            }
+        }
+
+        int numSubElements = dataBuffer[4];
+        if(numSubElements > 0) {
+            mElements = new Element[numSubElements];
+            mElementNames = new String[numSubElements];
+
+            int[] subElementIds = new int[numSubElements];
+            mRS.nElementGetSubElements(mID, subElementIds, mElementNames);
+            for(int i = 0; i < numSubElements; i ++) {
+                mElements[i] = new Element(subElementIds[i], mRS);
+                mElements[i].updateFromNative();
+                mSize += mElements[i].mSize;
+            }
+        }
+
+    }
+
+    public void destroy() {
         super.destroy();
     }
 
-    public static Element createFromClass(RenderScript rs, Class c) {
-        rs.validate();
-        Field[] fields = c.getFields();
-        Builder b = new Builder(rs);
-
-        for(Field f: fields) {
-            Class fc = f.getType();
-            if(fc == int.class) {
-                b.add(createUser(rs, DataType.SIGNED_32), f.getName());
-            } else if(fc == short.class) {
-                b.add(createUser(rs, DataType.SIGNED_16), f.getName());
-            } else if(fc == byte.class) {
-                b.add(createUser(rs, DataType.SIGNED_8), f.getName());
-            } else if(fc == float.class) {
-                b.add(createUser(rs, DataType.FLOAT_32), f.getName());
-            } else {
-                throw new IllegalArgumentException("Unkown field type");
-            }
-        }
-        return b.create();
-    }
-
-
     /////////////////////////////////////////
     public static Element createUser(RenderScript rs, DataType dt) {
-        return new Element(rs, dt, DataKind.USER, false, 1);
+        DataKind dk = DataKind.USER;
+        boolean norm = false;
+        int vecSize = 1;
+        int id = rs.nElementCreate(dt.mID, dk.mID, norm, vecSize);
+        return new Element(id, rs, dt, dk, norm, vecSize);
     }
 
     public static Element createVector(RenderScript rs, DataType dt, int size) {
         if (size < 2 || size > 4) {
-            throw new IllegalArgumentException("Bad size");
+            throw new RSIllegalArgumentException("Vector size out of rance 2-4.");
         }
-        return new Element(rs, dt, DataKind.USER, false, size);
-    }
-
-    public static Element createIndex(RenderScript rs) {
-        return new Element(rs, DataType.UNSIGNED_16, DataKind.INDEX, false, 1);
-    }
-
-    public static Element createAttrib(RenderScript rs, DataType dt, DataKind dk, int size) {
-        if (!(dt == DataType.FLOAT_32 ||
-              dt == DataType.UNSIGNED_8 ||
-              dt == DataType.UNSIGNED_16 ||
-              dt == DataType.UNSIGNED_32 ||
-              dt == DataType.SIGNED_8 ||
-              dt == DataType.SIGNED_16 ||
-              dt == DataType.SIGNED_32)) {
-            throw new IllegalArgumentException("Unsupported DataType");
-        }
-
-        if (!(dk == DataKind.COLOR ||
-              dk == DataKind.POSITION ||
-              dk == DataKind.TEXTURE ||
-              dk == DataKind.NORMAL ||
-              dk == DataKind.POINT_SIZE ||
-              dk == DataKind.USER)) {
-            throw new IllegalArgumentException("Unsupported DataKind");
-        }
-
-        if (dk == DataKind.COLOR &&
-            ((dt != DataType.FLOAT_32 && dt != DataType.UNSIGNED_8) ||
-             size < 3 || size > 4)) {
-            throw new IllegalArgumentException("Bad combo");
-        }
-        if (dk == DataKind.POSITION && (size < 1 || size > 4)) {
-            throw new IllegalArgumentException("Bad combo");
-        }
-        if (dk == DataKind.TEXTURE &&
-            (dt != DataType.FLOAT_32 || size < 1 || size > 4)) {
-            throw new IllegalArgumentException("Bad combo");
-        }
-        if (dk == DataKind.NORMAL &&
-            (dt != DataType.FLOAT_32 || size != 3)) {
-            throw new IllegalArgumentException("Bad combo");
-        }
-        if (dk == DataKind.POINT_SIZE &&
-            (dt != DataType.FLOAT_32 || size != 1)) {
-            throw new IllegalArgumentException("Bad combo");
-        }
-
+        DataKind dk = DataKind.USER;
         boolean norm = false;
-        if (dk == DataKind.COLOR && dt == DataType.UNSIGNED_8) {
-            norm = true;
-        }
-
-        return new Element(rs, dt, dk, norm, size);
+        int id = rs.nElementCreate(dt.mID, dk.mID, norm, size);
+        return new Element(id, rs, dt, dk, norm, size);
     }
 
     public static Element createPixel(RenderScript rs, DataType dt, DataKind dk) {
@@ -338,22 +438,22 @@
               dk == DataKind.PIXEL_LA ||
               dk == DataKind.PIXEL_RGB ||
               dk == DataKind.PIXEL_RGBA)) {
-            throw new IllegalArgumentException("Unsupported DataKind");
+            throw new RSIllegalArgumentException("Unsupported DataKind");
         }
         if (!(dt == DataType.UNSIGNED_8 ||
               dt == DataType.UNSIGNED_5_6_5 ||
               dt == DataType.UNSIGNED_4_4_4_4 ||
               dt == DataType.UNSIGNED_5_5_5_1)) {
-            throw new IllegalArgumentException("Unsupported DataType");
+            throw new RSIllegalArgumentException("Unsupported DataType");
         }
         if (dt == DataType.UNSIGNED_5_6_5 && dk != DataKind.PIXEL_RGB) {
-            throw new IllegalArgumentException("Bad kind and type combo");
+            throw new RSIllegalArgumentException("Bad kind and type combo");
         }
         if (dt == DataType.UNSIGNED_5_5_5_1 && dk != DataKind.PIXEL_RGBA) {
-            throw new IllegalArgumentException("Bad kind and type combo");
+            throw new RSIllegalArgumentException("Bad kind and type combo");
         }
         if (dt == DataType.UNSIGNED_4_4_4_4 && dk != DataKind.PIXEL_RGBA) {
-            throw new IllegalArgumentException("Bad kind and type combo");
+            throw new RSIllegalArgumentException("Bad kind and type combo");
         }
 
         int size = 1;
@@ -367,13 +467,16 @@
             size = 4;
         }
 
-        return new Element(rs, dt, dk, true, size);
+        boolean norm = true;
+        int id = rs.nElementCreate(dt.mID, dk.mID, norm, size);
+        return new Element(id, rs, dt, dk, norm, size);
     }
 
     public static class Builder {
         RenderScript mRS;
         Element[] mElements;
         String[] mElementNames;
+        int[] mArraySizes;
         int mCount;
 
         public Builder(RenderScript rs) {
@@ -381,29 +484,49 @@
             mCount = 0;
             mElements = new Element[8];
             mElementNames = new String[8];
+            mArraySizes = new int[8];
         }
 
-        public void add(Element element, String name) {
+        public void add(Element element, String name, int arraySize) {
+            if (arraySize < 1) {
+                throw new RSIllegalArgumentException("Array size cannot be less than 1.");
+            }
             if(mCount == mElements.length) {
                 Element[] e = new Element[mCount + 8];
                 String[] s = new String[mCount + 8];
+                int[] as = new int[mCount + 8];
                 System.arraycopy(mElements, 0, e, 0, mCount);
                 System.arraycopy(mElementNames, 0, s, 0, mCount);
+                System.arraycopy(mArraySizes, 0, as, 0, mCount);
                 mElements = e;
                 mElementNames = s;
+                mArraySizes = as;
             }
             mElements[mCount] = element;
             mElementNames[mCount] = name;
+            mArraySizes[mCount] = arraySize;
             mCount++;
         }
 
+        public void add(Element element, String name) {
+            add(element, name, 1);
+        }
+
         public Element create() {
             mRS.validate();
             Element[] ein = new Element[mCount];
             String[] sin = new String[mCount];
+            int[] asin = new int[mCount];
             java.lang.System.arraycopy(mElements, 0, ein, 0, mCount);
             java.lang.System.arraycopy(mElementNames, 0, sin, 0, mCount);
-            return new Element(mRS, ein, sin);
+            java.lang.System.arraycopy(mArraySizes, 0, asin, 0, mCount);
+
+            int[] ids = new int[ein.length];
+            for (int ct = 0; ct < ein.length; ct++ ) {
+                ids[ct] = ein[ct].mID;
+            }
+            int id = mRS.nElementCreate2(ids, sin, asin);
+            return new Element(id, mRS, ein, sin, asin);
         }
     }
 
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
index b26e47d..ff3e22b 100644
--- a/graphics/java/android/renderscript/FieldPacker.java
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -33,21 +33,28 @@
         }
     }
 
-    void reset() {
+    public void reset() {
         mPos = 0;
     }
+    public void reset(int i) {
+        mPos = i;
+    }
 
-    void addI8(byte v) {
+    public void skip(int i) {
+        mPos += i;
+    }
+
+    public void addI8(byte v) {
         mData[mPos++] = v;
     }
 
-    void addI16(short v) {
+    public void addI16(short v) {
         align(2);
         mData[mPos++] = (byte)(v & 0xff);
         mData[mPos++] = (byte)(v >> 8);
     }
 
-    void addI32(int v) {
+    public void addI32(int v) {
         align(4);
         mData[mPos++] = (byte)(v & 0xff);
         mData[mPos++] = (byte)((v >> 8) & 0xff);
@@ -55,7 +62,7 @@
         mData[mPos++] = (byte)((v >> 24) & 0xff);
     }
 
-    void addI64(long v) {
+    public void addI64(long v) {
         align(8);
         mData[mPos++] = (byte)(v & 0xff);
         mData[mPos++] = (byte)((v >> 8) & 0xff);
@@ -67,15 +74,17 @@
         mData[mPos++] = (byte)((v >> 56) & 0xff);
     }
 
-    void addU8(short v) {
+    public void addU8(short v) {
         if ((v < 0) || (v > 0xff)) {
+            android.util.Log.e("rs", "FieldPacker.addU8( " + v + " )");
             throw new IllegalArgumentException("Saving value out of range for type");
         }
         mData[mPos++] = (byte)v;
     }
 
-    void addU16(int v) {
+    public void addU16(int v) {
         if ((v < 0) || (v > 0xffff)) {
+            android.util.Log.e("rs", "FieldPacker.addU16( " + v + " )");
             throw new IllegalArgumentException("Saving value out of range for type");
         }
         align(2);
@@ -83,8 +92,9 @@
         mData[mPos++] = (byte)(v >> 8);
     }
 
-    void addU32(long v) {
-        if ((v < 0) || (v > 0xffffffff)) {
+    public void addU32(long v) {
+        if ((v < 0) || (v > 0xffffffffL)) {
+            android.util.Log.e("rs", "FieldPacker.addU32( " + v + " )");
             throw new IllegalArgumentException("Saving value out of range for type");
         }
         align(4);
@@ -94,8 +104,9 @@
         mData[mPos++] = (byte)((v >> 24) & 0xff);
     }
 
-    void addU64(long v) {
+    public void addU64(long v) {
         if (v < 0) {
+            android.util.Log.e("rs", "FieldPacker.addU64( " + v + " )");
             throw new IllegalArgumentException("Saving value out of range for type");
         }
         align(8);
@@ -109,15 +120,157 @@
         mData[mPos++] = (byte)((v >> 56) & 0xff);
     }
 
-    void addF32(float v) {
+    public void addF32(float v) {
         addI32(Float.floatToRawIntBits(v));
     }
 
-    void addF64(float v) {
+    public void addF64(double v) {
         addI64(Double.doubleToRawLongBits(v));
     }
 
-    final byte[] getData() {
+    public void addObj(BaseObj obj) {
+        if (obj != null) {
+            addI32(obj.getID());
+        } else {
+            addI32(0);
+        }
+    }
+
+    public void addF32(Float2 v) {
+        addF32(v.x);
+        addF32(v.y);
+    }
+    public void addF32(Float3 v) {
+        addF32(v.x);
+        addF32(v.y);
+        addF32(v.z);
+    }
+    public void addF32(Float4 v) {
+        addF32(v.x);
+        addF32(v.y);
+        addF32(v.z);
+        addF32(v.w);
+    }
+
+    public void addI8(Byte2 v) {
+        addI8(v.x);
+        addI8(v.y);
+    }
+    public void addI8(Byte3 v) {
+        addI8(v.x);
+        addI8(v.y);
+        addI8(v.z);
+    }
+    public void addI8(Byte4 v) {
+        addI8(v.x);
+        addI8(v.y);
+        addI8(v.z);
+        addI8(v.w);
+    }
+
+    public void addU8(Short2 v) {
+        addU8(v.x);
+        addU8(v.y);
+    }
+    public void addU8(Short3 v) {
+        addU8(v.x);
+        addU8(v.y);
+        addU8(v.z);
+    }
+    public void addU8(Short4 v) {
+        addU8(v.x);
+        addU8(v.y);
+        addU8(v.z);
+        addU8(v.w);
+    }
+
+    public void addI16(Short2 v) {
+        addI16(v.x);
+        addI16(v.y);
+    }
+    public void addI16(Short3 v) {
+        addI16(v.x);
+        addI16(v.y);
+        addI16(v.z);
+    }
+    public void addI16(Short4 v) {
+        addI16(v.x);
+        addI16(v.y);
+        addI16(v.z);
+        addI16(v.w);
+    }
+
+    public void addU16(Int2 v) {
+        addU16(v.x);
+        addU16(v.y);
+    }
+    public void addU16(Int3 v) {
+        addU16(v.x);
+        addU16(v.y);
+        addU16(v.z);
+    }
+    public void addU16(Int4 v) {
+        addU16(v.x);
+        addU16(v.y);
+        addU16(v.z);
+        addU16(v.w);
+    }
+
+    public void addI32(Int2 v) {
+        addI32(v.x);
+        addI32(v.y);
+    }
+    public void addI32(Int3 v) {
+        addI32(v.x);
+        addI32(v.y);
+        addI32(v.z);
+    }
+    public void addI32(Int4 v) {
+        addI32(v.x);
+        addI32(v.y);
+        addI32(v.z);
+        addI32(v.w);
+    }
+
+    public void addU32(Int2 v) {
+        addU32(v.x);
+        addU32(v.y);
+    }
+    public void addU32(Int3 v) {
+        addU32(v.x);
+        addU32(v.y);
+        addU32(v.z);
+    }
+    public void addU32(Int4 v) {
+        addU32(v.x);
+        addU32(v.y);
+        addU32(v.z);
+        addU32(v.w);
+    }
+
+    public void addObj(Matrix4f v) {
+        for (int i=0; i < v.mMat.length; i++) {
+            addF32(v.mMat[i]);
+        }
+    }
+
+    public void addObj(Matrix3f v) {
+        for (int i=0; i < v.mMat.length; i++) {
+            addF32(v.mMat[i]);
+        }
+    }
+
+    public void addObj(Matrix2f v) {
+        for (int i=0; i < v.mMat.length; i++) {
+            addF32(v.mMat[i]);
+        }
+    }
+
+    public void addBoolean(boolean v) {
+        addI8((byte)(v ? 1 : 0));
+    }
+
+    public final byte[] getData() {
         return mData;
     }
 
diff --git a/graphics/java/android/renderscript/FileA3D.java b/graphics/java/android/renderscript/FileA3D.java
new file mode 100644
index 0000000..fc74fc4
--- /dev/null
+++ b/graphics/java/android/renderscript/FileA3D.java
@@ -0,0 +1,208 @@
+/*
+ * 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.renderscript;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.content.res.Resources;
+import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.Log;
+import android.util.TypedValue;
+
+/**
+ * @hide
+ *
+ **/
+public class FileA3D extends BaseObj {
+
+    public enum ClassID {
+
+        UNKNOWN,
+        MESH,
+        TYPE,
+        ELEMENT,
+        ALLOCATION,
+        PROGRAM_VERTEX,
+        PROGRAM_RASTER,
+        PROGRAM_FRAGMENT,
+        PROGRAM_STORE,
+        SAMPLER,
+        ANIMATION,
+        ADAPTER_1D,
+        ADAPTER_2D,
+        SCRIPT_C;
+
+        public static ClassID toClassID(int intID) {
+            return ClassID.values()[intID];
+        }
+    }
+
+    // Read only class with index entries
+    public static class IndexEntry {
+        RenderScript mRS;
+        int mIndex;
+        int mID;
+        String mName;
+        ClassID mClassID;
+        BaseObj mLoadedObj;
+
+        public String getName() {
+            return mName;
+        }
+
+        public ClassID getClassID() {
+            return mClassID;
+        }
+
+        public BaseObj getObject() {
+            mRS.validate();
+            BaseObj obj = internalCreate(mRS, this);
+            return obj;
+        }
+
+        static synchronized BaseObj internalCreate(RenderScript rs, IndexEntry entry) {
+            if(entry.mLoadedObj != null) {
+                return entry.mLoadedObj;
+            }
+
+            if(entry.mClassID == ClassID.UNKNOWN) {
+                return null;
+            }
+
+            int objectID = rs.nFileA3DGetEntryByIndex(entry.mID, entry.mIndex);
+            if(objectID == 0) {
+                return null;
+            }
+
+            switch (entry.mClassID) {
+            case MESH:
+                entry.mLoadedObj = new Mesh(objectID, rs);
+                break;
+            case TYPE:
+                entry.mLoadedObj = new Type(objectID, rs);
+                break;
+            case ELEMENT:
+                entry.mLoadedObj = null;
+                break;
+            case ALLOCATION:
+                entry.mLoadedObj = null;
+                break;
+            case PROGRAM_VERTEX:
+                entry.mLoadedObj = new ProgramVertex(objectID, rs);
+                break;
+            case PROGRAM_RASTER:
+                break;
+            case PROGRAM_FRAGMENT:
+                break;
+            case PROGRAM_STORE:
+                break;
+            case SAMPLER:
+                break;
+            case ANIMATION:
+                break;
+            case ADAPTER_1D:
+                break;
+            case ADAPTER_2D:
+                break;
+            case SCRIPT_C:
+                break;
+            }
+
+            entry.mLoadedObj.updateFromNative();
+
+            return entry.mLoadedObj;
+        }
+
+        IndexEntry(RenderScript rs, int index, int id, String name, ClassID classID) {
+            mRS = rs;
+            mIndex = index;
+            mID = id;
+            mName = name;
+            mClassID = classID;
+            mLoadedObj = null;
+        }
+    }
+
+    IndexEntry[] mFileEntries;
+    InputStream mInputStream;
+
+    FileA3D(int id, RenderScript rs, InputStream stream) {
+        super(id, rs);
+        mInputStream = stream;
+    }
+
+    private void initEntries() {
+        int numFileEntries = mRS.nFileA3DGetNumIndexEntries(mID);
+        if(numFileEntries <= 0) {
+            return;
+        }
+
+        mFileEntries = new IndexEntry[numFileEntries];
+        int[] ids = new int[numFileEntries];
+        String[] names = new String[numFileEntries];
+
+        mRS.nFileA3DGetIndexEntries(mID, numFileEntries, ids, names);
+
+        for(int i = 0; i < numFileEntries; i ++) {
+            mFileEntries[i] = new IndexEntry(mRS, i, mID, names[i], ClassID.toClassID(ids[i]));
+        }
+    }
+
+    public int getNumIndexEntries() {
+        if(mFileEntries == null) {
+            return 0;
+        }
+        return mFileEntries.length;
+    }
+
+    public IndexEntry getIndexEntry(int index) {
+        if(getNumIndexEntries() == 0 || index < 0 || index >= mFileEntries.length) {
+            return null;
+        }
+        return mFileEntries[index];
+    }
+
+    static public FileA3D createFromResource(RenderScript rs, Resources res, int id)
+        throws IllegalArgumentException {
+
+        rs.validate();
+        InputStream is = null;
+        try {
+            final TypedValue value = new TypedValue();
+            is = res.openRawResource(id, value);
+
+            int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
+
+            int fileId = rs.nFileA3DCreateFromAssetStream(asset);
+
+            if(fileId == 0) {
+                throw new IllegalStateException("Load failed.");
+            }
+            FileA3D fa3d = new FileA3D(fileId, rs, is);
+            fa3d.initEntries();
+            return fa3d;
+
+        } catch (Exception e) {
+            // Ignore
+        }
+
+        return null;
+    }
+}
diff --git a/graphics/java/android/renderscript/Float2.java b/graphics/java/android/renderscript/Float2.java
new file mode 100644
index 0000000..889bf7b
--- /dev/null
+++ b/graphics/java/android/renderscript/Float2.java
@@ -0,0 +1,42 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Float2 {
+    public Float2() {
+    }
+
+    public Float2(float initX, float initY) {
+        x = initX;
+        y = initY;
+    }
+
+    public float x;
+    public float y;
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/Float3.java b/graphics/java/android/renderscript/Float3.java
new file mode 100644
index 0000000..ebe140d
--- /dev/null
+++ b/graphics/java/android/renderscript/Float3.java
@@ -0,0 +1,43 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Float3 {
+    public Float3() {
+    }
+    public Float3(float initX, float initY, float initZ) {
+        x = initX;
+        y = initY;
+        z = initZ;
+    }
+
+    public float x;
+    public float y;
+    public float z;
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/Float4.java b/graphics/java/android/renderscript/Float4.java
new file mode 100644
index 0000000..847732f
--- /dev/null
+++ b/graphics/java/android/renderscript/Float4.java
@@ -0,0 +1,45 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Float4 {
+    public Float4() {
+    }
+
+    public Float4(float initX, float initY, float initZ, float initW) {
+        x = initX;
+        y = initY;
+        z = initZ;
+        w = initW;
+    }
+
+    public float x;
+    public float y;
+    public float z;
+    public float w;
+}
+
+
+
diff --git a/graphics/java/android/renderscript/Font.java b/graphics/java/android/renderscript/Font.java
new file mode 100644
index 0000000..de25014
--- /dev/null
+++ b/graphics/java/android/renderscript/Font.java
@@ -0,0 +1,165 @@
+/*
+ * 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.renderscript;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.HashMap;
+
+import android.content.res.Resources;
+import android.content.res.AssetManager;
+import android.util.Log;
+import android.util.TypedValue;
+
+/**
+ * @hide
+ *
+ **/
+public class Font extends BaseObj {
+
+    //These help us create a font by family name
+    private static final String[] sSansNames = {
+        "sans-serif", "arial", "helvetica", "tahoma", "verdana"
+    };
+
+    private static final String[] sSerifNames = {
+        "serif", "times", "times new roman", "palatino", "georgia", "baskerville",
+        "goudy", "fantasy", "cursive", "ITC Stone Serif"
+    };
+
+    private static final String[] sMonoNames = {
+        "monospace", "courier", "courier new", "monaco"
+    };
+
+    private static class FontFamily {
+        String[] mNames;
+        String mNormalFileName;
+        String mBoldFileName;
+        String mItalicFileName;
+        String mBoldItalicFileName;
+    }
+
+    private static Map<String, FontFamily> sFontFamilyMap;
+
+    public enum Style {
+        NORMAL,
+        BOLD,
+        ITALIC,
+        BOLD_ITALIC;
+    }
+
+    private static void addFamilyToMap(FontFamily family) {
+        for(int i = 0; i < family.mNames.length; i ++) {
+            sFontFamilyMap.put(family.mNames[i], family);
+        }
+    }
+
+    private static void initFontFamilyMap() {
+        sFontFamilyMap = new HashMap<String, FontFamily>();
+
+        FontFamily sansFamily = new FontFamily();
+        sansFamily.mNames = sSansNames;
+        sansFamily.mNormalFileName = "DroidSans.ttf";
+        sansFamily.mBoldFileName = "DroidSans-Bold.ttf";
+        sansFamily.mItalicFileName = "DroidSans.ttf";
+        sansFamily.mBoldItalicFileName = "DroidSans-Bold.ttf";
+        addFamilyToMap(sansFamily);
+
+        FontFamily serifFamily = new FontFamily();
+        serifFamily.mNames = sSerifNames;
+        serifFamily.mNormalFileName = "DroidSerif-Regular.ttf";
+        serifFamily.mBoldFileName = "DroidSerif-Bold.ttf";
+        serifFamily.mItalicFileName = "DroidSerif-Italic.ttf";
+        serifFamily.mBoldItalicFileName = "DroidSerif-BoldItalic.ttf";
+        addFamilyToMap(serifFamily);
+
+        FontFamily monoFamily = new FontFamily();
+        monoFamily.mNames = sMonoNames;
+        monoFamily.mNormalFileName = "DroidSansMono.ttf";
+        monoFamily.mBoldFileName = "DroidSansMono.ttf";
+        monoFamily.mItalicFileName = "DroidSansMono.ttf";
+        monoFamily.mBoldItalicFileName = "DroidSansMono.ttf";
+        addFamilyToMap(monoFamily);
+    }
+
+    static {
+        initFontFamilyMap();
+    }
+
+    static String getFontFileName(String familyName, Style style) {
+        FontFamily family = sFontFamilyMap.get(familyName);
+        if(family != null) {
+            switch(style) {
+                case NORMAL:
+                    return family.mNormalFileName;
+                case BOLD:
+                    return family.mBoldFileName;
+                case ITALIC:
+                    return family.mItalicFileName;
+                case BOLD_ITALIC:
+                    return family.mBoldItalicFileName;
+            }
+        }
+        // Fallback if we could not find the desired family
+        return "DroidSans.ttf";
+    }
+
+    Font(int id, RenderScript rs) {
+        super(id, rs);
+    }
+
+    /**
+     * Takes a specific file name as an argument
+     */
+    static public Font create(RenderScript rs, Resources res, String fileName, int size)
+        throws IllegalArgumentException {
+
+        rs.validate();
+        try {
+            int dpi = res.getDisplayMetrics().densityDpi;
+            int fontId = rs.nFontCreateFromFile(fileName, size, dpi);
+
+            if(fontId == 0) {
+                throw new IllegalStateException("Failed loading a font");
+            }
+            Font rsFont = new Font(fontId, rs);
+
+            return rsFont;
+
+        } catch (Exception e) {
+            // Ignore
+        }
+
+        return null;
+    }
+
+    /**
+     * Accepts one of the following family names as an argument
+     * and will attemp to produce the best match with a system font
+     * "sans-serif" "arial" "helvetica" "tahoma" "verdana"
+     * "serif" "times" "times new roman" "palatino" "georgia" "baskerville"
+     * "goudy" "fantasy" "cursive" "ITC Stone Serif"
+     * "monospace" "courier" "courier new" "monaco"
+     * Returns default font if no match could be found
+     */
+    static public Font createFromFamily(RenderScript rs, Resources res, String familyName, Style fontStyle, int size)
+    throws IllegalArgumentException {
+        String fileName = getFontFileName(familyName, fontStyle);
+        return create(rs, res, fileName, size);
+    }
+}
diff --git a/graphics/java/android/renderscript/Int2.java b/graphics/java/android/renderscript/Int2.java
new file mode 100644
index 0000000..56e2fe9
--- /dev/null
+++ b/graphics/java/android/renderscript/Int2.java
@@ -0,0 +1,37 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Int2 {
+    public Int2() {
+    }
+
+    public int x;
+    public int y;
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/Int3.java b/graphics/java/android/renderscript/Int3.java
new file mode 100644
index 0000000..1b27509
--- /dev/null
+++ b/graphics/java/android/renderscript/Int3.java
@@ -0,0 +1,38 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Int3 {
+    public Int3() {
+    }
+
+    public int x;
+    public int y;
+    public int z;
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/Int4.java b/graphics/java/android/renderscript/Int4.java
new file mode 100644
index 0000000..3d6f3f5
--- /dev/null
+++ b/graphics/java/android/renderscript/Int4.java
@@ -0,0 +1,38 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Int4 {
+    public Int4() {
+    }
+
+    public int x;
+    public int y;
+    public int z;
+    public int w;
+}
+
+
+
diff --git a/graphics/java/android/renderscript/Light.java b/graphics/java/android/renderscript/Light.java
deleted file mode 100644
index aab656f..0000000
--- a/graphics/java/android/renderscript/Light.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.renderscript;
-
-import android.util.Config;
-import android.util.Log;
-
-/**
- * @hide
- *
- **/
-public class Light extends BaseObj {
-    Light(int id, RenderScript rs) {
-        super(rs);
-        mID = id;
-    }
-
-    public void setColor(float r, float g, float b) {
-        mRS.validate();
-        mRS.nLightSetColor(mID, r, g, b);
-    }
-
-    public void setPosition(float x, float y, float z) {
-        mRS.validate();
-        mRS.nLightSetPosition(mID, x, y, z);
-    }
-
-    public static class Builder {
-        RenderScript mRS;
-        boolean mIsMono;
-        boolean mIsLocal;
-
-        public Builder(RenderScript rs) {
-            mRS = rs;
-            mIsMono = false;
-            mIsLocal = false;
-        }
-
-        public void lightSetIsMono(boolean isMono) {
-            mIsMono = isMono;
-        }
-
-        public void lightSetIsLocal(boolean isLocal) {
-            mIsLocal = isLocal;
-        }
-
-        static synchronized Light internalCreate(RenderScript rs, Builder b) {
-            rs.nSamplerBegin();
-            rs.nLightSetIsMono(b.mIsMono);
-            rs.nLightSetIsLocal(b.mIsLocal);
-            int id = rs.nLightCreate();
-            return new Light(id, rs);
-        }
-
-        public Light create() {
-            mRS.validate();
-            return internalCreate(mRS, this);
-        }
-    }
-
-}
-
diff --git a/graphics/java/android/renderscript/Long2.java b/graphics/java/android/renderscript/Long2.java
new file mode 100644
index 0000000..11ead2f
--- /dev/null
+++ b/graphics/java/android/renderscript/Long2.java
@@ -0,0 +1,37 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Long2 {
+    public Long2() {
+    }
+
+    public long x;
+    public long y;
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/Long3.java b/graphics/java/android/renderscript/Long3.java
new file mode 100644
index 0000000..1604532
--- /dev/null
+++ b/graphics/java/android/renderscript/Long3.java
@@ -0,0 +1,38 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Long3 {
+    public Long3() {
+    }
+
+    public long x;
+    public long y;
+    public long z;
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/Long4.java b/graphics/java/android/renderscript/Long4.java
new file mode 100644
index 0000000..2fd2747
--- /dev/null
+++ b/graphics/java/android/renderscript/Long4.java
@@ -0,0 +1,38 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Long4 {
+    public Long4() {
+    }
+
+    public long x;
+    public long y;
+    public long z;
+    public long w;
+}
+
+
+
diff --git a/graphics/java/android/renderscript/Matrix2f.java b/graphics/java/android/renderscript/Matrix2f.java
index 4b5e61b..99d23db 100644
--- a/graphics/java/android/renderscript/Matrix2f.java
+++ b/graphics/java/android/renderscript/Matrix2f.java
@@ -51,6 +51,57 @@
         System.arraycopy(mMat, 0, src, 0, 4);
     }
 
+    public void loadRotate(float rot) {
+        float c, s;
+        rot *= (float)(java.lang.Math.PI / 180.0f);
+        c = (float)java.lang.Math.cos(rot);
+        s = (float)java.lang.Math.sin(rot);
+        mMat[0] = c;
+        mMat[1] = -s;
+        mMat[2] = s;
+        mMat[3] = c;
+    }
+
+    public void loadScale(float x, float y) {
+        loadIdentity();
+        mMat[0] = x;
+        mMat[3] = y;
+    }
+    public void loadMultiply(Matrix2f lhs, Matrix2f rhs) {
+        for (int i=0 ; i<2 ; i++) {
+            float ri0 = 0;
+            float ri1 = 0;
+            for (int j=0 ; j<2 ; j++) {
+                float rhs_ij = rhs.get(i,j);
+                ri0 += lhs.get(j,0) * rhs_ij;
+                ri1 += lhs.get(j,1) * rhs_ij;
+            }
+            set(i,0, ri0);
+            set(i,1, ri1);
+        }
+    }
+
+    public void multiply(Matrix2f rhs) {
+        Matrix2f tmp = new Matrix2f();
+        tmp.loadMultiply(this, rhs);
+        load(tmp);
+    }
+    public void rotate(float rot) {
+        Matrix2f tmp = new Matrix2f();
+        tmp.loadRotate(rot);
+        multiply(tmp);
+    }
+    public void scale(float x, float y) {
+        Matrix2f tmp = new Matrix2f();
+        tmp.loadScale(x, y);
+        multiply(tmp);
+    }
+    public void transpose() {
+        float temp = mMat[1];
+        mMat[1] = mMat[2];
+        mMat[2] = temp;
+    }
+
     final float[] mMat;
 }
 
diff --git a/graphics/java/android/renderscript/Matrix3f.java b/graphics/java/android/renderscript/Matrix3f.java
index 19d7b43..961bc5d 100644
--- a/graphics/java/android/renderscript/Matrix3f.java
+++ b/graphics/java/android/renderscript/Matrix3f.java
@@ -57,6 +57,124 @@
         System.arraycopy(mMat, 0, src, 0, 9);
     }
 
+    public void loadRotate(float rot, float x, float y, float z) {
+        float c, s;
+        rot *= (float)(java.lang.Math.PI / 180.0f);
+        c = (float)java.lang.Math.cos(rot);
+        s = (float)java.lang.Math.sin(rot);
+
+        float len = (float)java.lang.Math.sqrt(x*x + y*y + z*z);
+        if (!(len != 1)) {
+            float recipLen = 1.f / len;
+            x *= recipLen;
+            y *= recipLen;
+            z *= recipLen;
+        }
+        float nc = 1.0f - c;
+        float xy = x * y;
+        float yz = y * z;
+        float zx = z * x;
+        float xs = x * s;
+        float ys = y * s;
+        float zs = z * s;
+        mMat[0] = x*x*nc +  c;
+        mMat[3] =  xy*nc - zs;
+        mMat[6] =  zx*nc + ys;
+        mMat[1] =  xy*nc + zs;
+        mMat[4] = y*y*nc +  c;
+        mMat[9] =  yz*nc - xs;
+        mMat[2] =  zx*nc - ys;
+        mMat[6] =  yz*nc + xs;
+        mMat[8] = z*z*nc +  c;
+    }
+
+    public void loadRotate(float rot) {
+        float c, s;
+        rot *= (float)(java.lang.Math.PI / 180.0f);
+        c = (float)java.lang.Math.cos(rot);
+        s = (float)java.lang.Math.sin(rot);
+        mMat[0] = c;
+        mMat[1] = -s;
+        mMat[3] = s;
+        mMat[4] = c;
+    }
+
+    public void loadScale(float x, float y) {
+        loadIdentity();
+        mMat[0] = x;
+        mMat[4] = y;
+    }
+
+    public void loadScale(float x, float y, float z) {
+        loadIdentity();
+        mMat[0] = x;
+        mMat[4] = y;
+        mMat[8] = z;
+    }
+
+    public void loadTranslate(float x, float y) {
+        loadIdentity();
+        mMat[6] = x;
+        mMat[7] = y;
+    }
+
+    public void loadMultiply(Matrix3f lhs, Matrix3f rhs) {
+        for (int i=0 ; i<3 ; i++) {
+            float ri0 = 0;
+            float ri1 = 0;
+            float ri2 = 0;
+            for (int j=0 ; j<3 ; j++) {
+                float rhs_ij = rhs.get(i,j);
+                ri0 += lhs.get(j,0) * rhs_ij;
+                ri1 += lhs.get(j,1) * rhs_ij;
+                ri2 += lhs.get(j,2) * rhs_ij;
+            }
+            set(i,0, ri0);
+            set(i,1, ri1);
+            set(i,2, ri2);
+        }
+    }
+
+    public void multiply(Matrix3f rhs) {
+        Matrix3f tmp = new Matrix3f();
+        tmp.loadMultiply(this, rhs);
+        load(tmp);
+    }
+    public void rotate(float rot, float x, float y, float z) {
+        Matrix3f tmp = new Matrix3f();
+        tmp.loadRotate(rot, x, y, z);
+        multiply(tmp);
+    }
+    public void rotate(float rot) {
+        Matrix3f tmp = new Matrix3f();
+        tmp.loadRotate(rot);
+        multiply(tmp);
+    }
+    public void scale(float x, float y) {
+        Matrix3f tmp = new Matrix3f();
+        tmp.loadScale(x, y);
+        multiply(tmp);
+    }
+    public void scale(float x, float y, float z) {
+        Matrix3f tmp = new Matrix3f();
+        tmp.loadScale(x, y, z);
+        multiply(tmp);
+    }
+    public void translate(float x, float y) {
+        Matrix3f tmp = new Matrix3f();
+        tmp.loadTranslate(x, y);
+        multiply(tmp);
+    }
+    public void transpose() {
+        for(int i = 0; i < 2; ++i) {
+            for(int j = i + 1; j < 3; ++j) {
+                float temp = mMat[i*3 + j];
+                mMat[i*3 + j] = mMat[j*3 + i];
+                mMat[j*3 + i] = temp;
+            }
+        }
+    }
+
     final float[] mMat;
 }
 
diff --git a/graphics/java/android/renderscript/Matrix4f.java b/graphics/java/android/renderscript/Matrix4f.java
index ebd5bde..5ffc21a 100644
--- a/graphics/java/android/renderscript/Matrix4f.java
+++ b/graphics/java/android/renderscript/Matrix4f.java
@@ -179,6 +179,85 @@
         tmp.loadTranslate(x, y, z);
         multiply(tmp);
     }
+    private float computeCofactor(int i, int j) {
+        int c0 = (i+1) % 4;
+        int c1 = (i+2) % 4;
+        int c2 = (i+3) % 4;
+        int r0 = (j+1) % 4;
+        int r1 = (j+2) % 4;
+        int r2 = (j+3) % 4;
+
+        float minor = (mMat[c0 + 4*r0] * (mMat[c1 + 4*r1] * mMat[c2 + 4*r2] -
+                                            mMat[c1 + 4*r2] * mMat[c2 + 4*r1]))
+                     - (mMat[c0 + 4*r1] * (mMat[c1 + 4*r0] * mMat[c2 + 4*r2] -
+                                            mMat[c1 + 4*r2] * mMat[c2 + 4*r0]))
+                     + (mMat[c0 + 4*r2] * (mMat[c1 + 4*r0] * mMat[c2 + 4*r1] -
+                                            mMat[c1 + 4*r1] * mMat[c2 + 4*r0]));
+
+        float cofactor = ((i+j) & 1) != 0 ? -minor : minor;
+        return cofactor;
+    }
+
+    public boolean inverse() {
+
+        Matrix4f result = new Matrix4f();
+
+        for (int i = 0; i < 4; ++i) {
+            for (int j = 0; j < 4; ++j) {
+                result.mMat[4*i + j] = computeCofactor(i, j);
+            }
+        }
+
+        // Dot product of 0th column of source and 0th row of result
+        float det = mMat[0]*result.mMat[0] + mMat[4]*result.mMat[1] +
+                     mMat[8]*result.mMat[2] + mMat[12]*result.mMat[3];
+
+        if (Math.abs(det) < 1e-6) {
+            return false;
+        }
+
+        det = 1.0f / det;
+        for (int i = 0; i < 16; ++i) {
+            mMat[i] = result.mMat[i] * det;
+        }
+
+        return true;
+    }
+
+    public boolean inverseTranspose() {
+
+        Matrix4f result = new Matrix4f();
+
+        for (int i = 0; i < 4; ++i) {
+            for (int j = 0; j < 4; ++j) {
+                result.mMat[4*j + i] = computeCofactor(i, j);
+            }
+        }
+
+        float det = mMat[0]*result.mMat[0] + mMat[4]*result.mMat[4] +
+                     mMat[8]*result.mMat[8] + mMat[12]*result.mMat[12];
+
+        if (Math.abs(det) < 1e-6) {
+            return false;
+        }
+
+        det = 1.0f / det;
+        for (int i = 0; i < 16; ++i) {
+            mMat[i] = result.mMat[i] * det;
+        }
+
+        return true;
+    }
+
+    public void transpose() {
+        for(int i = 0; i < 3; ++i) {
+            for(int j = i + 1; j < 4; ++j) {
+                float temp = mMat[i*4 + j];
+                mMat[i*4 + j] = mMat[j*4 + i];
+                mMat[j*4 + i] = temp;
+            }
+        }
+    }
 
     final float[] mMat;
 }
diff --git a/graphics/java/android/renderscript/Mesh.java b/graphics/java/android/renderscript/Mesh.java
new file mode 100644
index 0000000..d36b2f1
--- /dev/null
+++ b/graphics/java/android/renderscript/Mesh.java
@@ -0,0 +1,479 @@
+/*
+ * 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.renderscript;
+
+import java.util.Vector;
+
+import android.util.Config;
+import android.util.Log;
+
+/**
+ * @hide
+ *
+ **/
+public class Mesh extends BaseObj {
+
+    Allocation[] mVertexBuffers;
+    Allocation[] mIndexBuffers;
+    Primitive[] mPrimitives;
+
+    Mesh(int id, RenderScript rs) {
+        super(id, rs);
+    }
+
+    public int getVertexAllocationCount() {
+        if(mVertexBuffers == null) {
+            return 0;
+        }
+        return mVertexBuffers.length;
+    }
+    public Allocation getVertexAllocation(int slot) {
+        return mVertexBuffers[slot];
+    }
+
+    public int getPrimitiveCount() {
+        if(mIndexBuffers == null) {
+            return 0;
+        }
+        return mIndexBuffers.length;
+    }
+    public Allocation getIndexAllocation(int slot) {
+        return mIndexBuffers[slot];
+    }
+    public Primitive getPrimitive(int slot) {
+        return mPrimitives[slot];
+    }
+
+    @Override
+    void updateFromNative() {
+        mName = mRS.nGetName(mID);
+        int vtxCount = mRS.nMeshGetVertexBufferCount(mID);
+        int idxCount = mRS.nMeshGetIndexCount(mID);
+
+        int[] vtxIDs = new int[vtxCount];
+        int[] idxIDs = new int[idxCount];
+        int[] primitives = new int[idxCount];
+
+        mRS.nMeshGetVertices(mID, vtxIDs, vtxCount);
+        mRS.nMeshGetIndices(mID, idxIDs, primitives, idxCount);
+
+        mVertexBuffers = new Allocation[vtxCount];
+        mIndexBuffers = new Allocation[idxCount];
+        mPrimitives = new Primitive[idxCount];
+
+        for(int i = 0; i < vtxCount; i ++) {
+            if(vtxIDs[i] != 0) {
+                mVertexBuffers[i] = new Allocation(vtxIDs[i], mRS);
+                mVertexBuffers[i].updateFromNative();
+            }
+        }
+
+        for(int i = 0; i < idxCount; i ++) {
+            if(idxIDs[i] != 0) {
+                mIndexBuffers[i] = new Allocation(idxIDs[i], mRS);
+                mIndexBuffers[i].updateFromNative();
+            }
+            mPrimitives[i] = Primitive.values()[primitives[i]];
+        }
+    }
+
+    public static class Builder {
+        RenderScript mRS;
+
+        class Entry {
+            Type t;
+            Element e;
+            int size;
+            Primitive prim;
+        }
+
+        int mVertexTypeCount;
+        Entry[] mVertexTypes;
+        Vector mIndexTypes;
+
+        public Builder(RenderScript rs) {
+            mRS = rs;
+            mVertexTypeCount = 0;
+            mVertexTypes = new Entry[16];
+            mIndexTypes = new Vector();
+        }
+
+        public int addVertexType(Type t) throws IllegalStateException {
+            if (mVertexTypeCount >= mVertexTypes.length) {
+                throw new IllegalStateException("Max vertex types exceeded.");
+            }
+
+            int addedIndex = mVertexTypeCount;
+            mVertexTypes[mVertexTypeCount] = new Entry();
+            mVertexTypes[mVertexTypeCount].t = t;
+            mVertexTypes[mVertexTypeCount].e = null;
+            mVertexTypeCount++;
+            return addedIndex;
+        }
+
+        public int addVertexType(Element e, int size) throws IllegalStateException {
+            if (mVertexTypeCount >= mVertexTypes.length) {
+                throw new IllegalStateException("Max vertex types exceeded.");
+            }
+
+            int addedIndex = mVertexTypeCount;
+            mVertexTypes[mVertexTypeCount] = new Entry();
+            mVertexTypes[mVertexTypeCount].t = null;
+            mVertexTypes[mVertexTypeCount].e = e;
+            mVertexTypes[mVertexTypeCount].size = size;
+            mVertexTypeCount++;
+            return addedIndex;
+        }
+
+        public int addIndexType(Type t, Primitive p) {
+            int addedIndex  = mIndexTypes.size();
+            Entry indexType = new Entry();
+            indexType.t = t;
+            indexType.e = null;
+            indexType.size = 0;
+            indexType.prim = p;
+            mIndexTypes.addElement(indexType);
+            return addedIndex;
+        }
+
+        public int addIndexType(Primitive p) {
+            int addedIndex  = mIndexTypes.size();
+            Entry indexType = new Entry();
+            indexType.t = null;
+            indexType.e = null;
+            indexType.size = 0;
+            indexType.prim = p;
+            mIndexTypes.addElement(indexType);
+            return addedIndex;
+        }
+
+        public int addIndexType(Element e, int size, Primitive p) {
+            int addedIndex  = mIndexTypes.size();
+            Entry indexType = new Entry();
+            indexType.t = null;
+            indexType.e = e;
+            indexType.size = size;
+            indexType.prim = p;
+            mIndexTypes.addElement(indexType);
+            return addedIndex;
+        }
+
+        Type newType(Element e, int size) {
+            Type.Builder tb = new Type.Builder(mRS, e);
+            tb.add(Dimension.X, size);
+            return tb.create();
+        }
+
+        static synchronized Mesh internalCreate(RenderScript rs, Builder b) {
+
+            int id = rs.nMeshCreate(b.mVertexTypeCount, b.mIndexTypes.size());
+            Mesh newMesh = new Mesh(id, rs);
+            newMesh.mIndexBuffers = new Allocation[b.mIndexTypes.size()];
+            newMesh.mPrimitives = new Primitive[b.mIndexTypes.size()];
+            newMesh.mVertexBuffers = new Allocation[b.mVertexTypeCount];
+
+            for(int ct = 0; ct < b.mIndexTypes.size(); ct ++) {
+                Allocation alloc = null;
+                Entry entry = (Entry)b.mIndexTypes.elementAt(ct);
+                if (entry.t != null) {
+                    alloc = Allocation.createTyped(rs, entry.t);
+                }
+                else if(entry.e != null) {
+                    alloc = Allocation.createSized(rs, entry.e, entry.size);
+                }
+                int allocID = (alloc == null) ? 0 : alloc.getID();
+                rs.nMeshBindIndex(id, allocID, entry.prim.mID, ct);
+                newMesh.mIndexBuffers[ct] = alloc;
+                newMesh.mPrimitives[ct] = entry.prim;
+            }
+
+            for(int ct = 0; ct < b.mVertexTypeCount; ct ++) {
+                Allocation alloc = null;
+                Entry entry = b.mVertexTypes[ct];
+                if (entry.t != null) {
+                    alloc = Allocation.createTyped(rs, entry.t);
+                } else if(entry.e != null) {
+                    alloc = Allocation.createSized(rs, entry.e, entry.size);
+                }
+                rs.nMeshBindVertex(id, alloc.getID(), ct);
+                newMesh.mVertexBuffers[ct] = alloc;
+            }
+
+            return newMesh;
+        }
+
+        public Mesh create() {
+            mRS.validate();
+            Mesh sm = internalCreate(mRS, this);
+            return sm;
+        }
+    }
+
+    public static class AllocationBuilder {
+        RenderScript mRS;
+
+        class Entry {
+            Allocation a;
+            Primitive prim;
+        }
+
+        int mVertexTypeCount;
+        Entry[] mVertexTypes;
+
+        Vector mIndexTypes;
+
+        public AllocationBuilder(RenderScript rs) {
+            mRS = rs;
+            mVertexTypeCount = 0;
+            mVertexTypes = new Entry[16];
+            mIndexTypes = new Vector();
+        }
+
+        public int addVertexAllocation(Allocation a) throws IllegalStateException {
+            if (mVertexTypeCount >= mVertexTypes.length) {
+                throw new IllegalStateException("Max vertex types exceeded.");
+            }
+
+            int addedIndex = mVertexTypeCount;
+            mVertexTypes[mVertexTypeCount] = new Entry();
+            mVertexTypes[mVertexTypeCount].a = a;
+            mVertexTypeCount++;
+            return addedIndex;
+        }
+
+        public int addIndexAllocation(Allocation a, Primitive p) {
+            int addedIndex  = mIndexTypes.size();
+            Entry indexType = new Entry();
+            indexType.a = a;
+            indexType.prim = p;
+            mIndexTypes.addElement(indexType);
+            return addedIndex;
+        }
+
+        public int addIndexType(Primitive p) {
+            int addedIndex  = mIndexTypes.size();
+            Entry indexType = new Entry();
+            indexType.a = null;
+            indexType.prim = p;
+            mIndexTypes.addElement(indexType);
+            return addedIndex;
+        }
+
+        static synchronized Mesh internalCreate(RenderScript rs, AllocationBuilder b) {
+
+            int id = rs.nMeshCreate(b.mVertexTypeCount, b.mIndexTypes.size());
+            Mesh newMesh = new Mesh(id, rs);
+            newMesh.mIndexBuffers = new Allocation[b.mIndexTypes.size()];
+            newMesh.mPrimitives = new Primitive[b.mIndexTypes.size()];
+            newMesh.mVertexBuffers = new Allocation[b.mVertexTypeCount];
+
+            for(int ct = 0; ct < b.mIndexTypes.size(); ct ++) {
+                Entry entry = (Entry)b.mIndexTypes.elementAt(ct);
+                int allocID = (entry.a == null) ? 0 : entry.a.getID();
+                rs.nMeshBindIndex(id, allocID, entry.prim.mID, ct);
+                newMesh.mIndexBuffers[ct] = entry.a;
+                newMesh.mPrimitives[ct] = entry.prim;
+            }
+
+            for(int ct = 0; ct < b.mVertexTypeCount; ct ++) {
+                Entry entry = b.mVertexTypes[ct];
+                rs.nMeshBindVertex(id, entry.a.mID, ct);
+                newMesh.mVertexBuffers[ct] = entry.a;
+            }
+
+            return newMesh;
+        }
+
+        public Mesh create() {
+            mRS.validate();
+            Mesh sm = internalCreate(mRS, this);
+            return sm;
+        }
+    }
+
+
+    public static class TriangleMeshBuilder {
+        float mVtxData[];
+        int mVtxCount;
+        short mIndexData[];
+        int mIndexCount;
+        RenderScript mRS;
+        Element mElement;
+
+        float mNX = 0;
+        float mNY = 0;
+        float mNZ = -1;
+        float mS0 = 0;
+        float mT0 = 0;
+        float mR = 1;
+        float mG = 1;
+        float mB = 1;
+        float mA = 1;
+
+        int mVtxSize;
+        int mFlags;
+
+        public static final int COLOR = 0x0001;
+        public static final int NORMAL = 0x0002;
+        public static final int TEXTURE_0 = 0x0100;
+
+        public TriangleMeshBuilder(RenderScript rs, int vtxSize, int flags) {
+            mRS = rs;
+            mVtxCount = 0;
+            mIndexCount = 0;
+            mVtxData = new float[128];
+            mIndexData = new short[128];
+            mVtxSize = vtxSize;
+            mFlags = flags;
+
+            if (vtxSize < 2 || vtxSize > 3) {
+                throw new IllegalArgumentException("Vertex size out of range.");
+            }
+        }
+
+        private void makeSpace(int count) {
+            if ((mVtxCount + count) >= mVtxData.length) {
+                float t[] = new float[mVtxData.length * 2];
+                System.arraycopy(mVtxData, 0, t, 0, mVtxData.length);
+                mVtxData = t;
+            }
+        }
+
+        private void latch() {
+            if ((mFlags & COLOR) != 0) {
+                makeSpace(4);
+                mVtxData[mVtxCount++] = mR;
+                mVtxData[mVtxCount++] = mG;
+                mVtxData[mVtxCount++] = mB;
+                mVtxData[mVtxCount++] = mA;
+            }
+            if ((mFlags & TEXTURE_0) != 0) {
+                makeSpace(2);
+                mVtxData[mVtxCount++] = mS0;
+                mVtxData[mVtxCount++] = mT0;
+            }
+            if ((mFlags & NORMAL) != 0) {
+                makeSpace(3);
+                mVtxData[mVtxCount++] = mNX;
+                mVtxData[mVtxCount++] = mNY;
+                mVtxData[mVtxCount++] = mNZ;
+            }
+        }
+
+        public void addVertex(float x, float y) {
+            if (mVtxSize != 2) {
+                throw new IllegalStateException("add mistmatch with declared components.");
+            }
+            makeSpace(2);
+            mVtxData[mVtxCount++] = x;
+            mVtxData[mVtxCount++] = y;
+            latch();
+        }
+
+        public void addVertex(float x, float y, float z) {
+            if (mVtxSize != 3) {
+                throw new IllegalStateException("add mistmatch with declared components.");
+            }
+            makeSpace(3);
+            mVtxData[mVtxCount++] = x;
+            mVtxData[mVtxCount++] = y;
+            mVtxData[mVtxCount++] = z;
+            latch();
+        }
+
+        public void setTexture(float s, float t) {
+            if ((mFlags & TEXTURE_0) == 0) {
+                throw new IllegalStateException("add mistmatch with declared components.");
+            }
+            mS0 = s;
+            mT0 = t;
+        }
+
+        public void setNormal(float x, float y, float z) {
+            if ((mFlags & NORMAL) == 0) {
+                throw new IllegalStateException("add mistmatch with declared components.");
+            }
+            mNX = x;
+            mNY = y;
+            mNZ = z;
+        }
+
+        public void setColor(float r, float g, float b, float a) {
+            if ((mFlags & COLOR) == 0) {
+                throw new IllegalStateException("add mistmatch with declared components.");
+            }
+            mR = r;
+            mG = g;
+            mB = b;
+            mA = a;
+        }
+
+        public void addTriangle(int idx1, int idx2, int idx3) {
+            if((idx1 >= mVtxCount) || (idx1 < 0) ||
+               (idx2 >= mVtxCount) || (idx2 < 0) ||
+               (idx3 >= mVtxCount) || (idx3 < 0)) {
+               throw new IllegalStateException("Index provided greater than vertex count.");
+            }
+            if ((mIndexCount + 3) >= mIndexData.length) {
+                short t[] = new short[mIndexData.length * 2];
+                System.arraycopy(mIndexData, 0, t, 0, mIndexData.length);
+                mIndexData = t;
+            }
+            mIndexData[mIndexCount++] = (short)idx1;
+            mIndexData[mIndexCount++] = (short)idx2;
+            mIndexData[mIndexCount++] = (short)idx3;
+        }
+
+        public Mesh create(boolean uploadToBufferObject) {
+            Element.Builder b = new Element.Builder(mRS);
+            int floatCount = mVtxSize;
+            b.add(Element.createVector(mRS,
+                                       Element.DataType.FLOAT_32,
+                                       mVtxSize), "position");
+            if ((mFlags & COLOR) != 0) {
+                floatCount += 4;
+                b.add(Element.F32_4(mRS), "color");
+            }
+            if ((mFlags & TEXTURE_0) != 0) {
+                floatCount += 2;
+                b.add(Element.F32_2(mRS), "texture0");
+            }
+            if ((mFlags & NORMAL) != 0) {
+                floatCount += 3;
+                b.add(Element.F32_3(mRS), "normal");
+            }
+            mElement = b.create();
+
+            Builder smb = new Builder(mRS);
+            smb.addVertexType(mElement, mVtxCount / floatCount);
+            smb.addIndexType(Element.U16(mRS), mIndexCount, Primitive.TRIANGLE);
+
+            Mesh sm = smb.create();
+
+            sm.getVertexAllocation(0).data(mVtxData);
+            if(uploadToBufferObject) {
+                sm.getVertexAllocation(0).uploadToBufferObject();
+            }
+
+            sm.getIndexAllocation(0).data(mIndexData);
+            sm.getIndexAllocation(0).uploadToBufferObject();
+
+            return sm;
+        }
+    }
+}
+
diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java
index 1614ec5..35236ca 100644
--- a/graphics/java/android/renderscript/Program.java
+++ b/graphics/java/android/renderscript/Program.java
@@ -17,6 +17,11 @@
 package android.renderscript;
 
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+import android.content.res.Resources;
 import android.util.Config;
 import android.util.Log;
 
@@ -38,11 +43,17 @@
     String mShader;
 
     Program(int id, RenderScript rs) {
-        super(rs);
-        mID = id;
+        super(id, rs);
     }
 
     public void bindConstants(Allocation a, int slot) {
+        if (slot < 0 || slot >= mConstants.length) {
+            throw new IllegalArgumentException("Slot ID out of range.");
+        }
+        if (a != null &&
+            a.getType().getID() != mConstants[slot].getID()) {
+            throw new IllegalArgumentException("Allocation type does not match slot type.");
+        }
         mRS.nProgramBindConstants(mID, slot, a.mID);
     }
 
@@ -91,14 +102,56 @@
             mTextureCount = 0;
         }
 
-        public void setShader(String s) {
+        public BaseProgramBuilder setShader(String s) {
             mShader = s;
+            return this;
+        }
+
+        public BaseProgramBuilder setShader(Resources resources, int resourceID) {
+            byte[] str;
+            int strLength;
+            InputStream is = resources.openRawResource(resourceID);
+            try {
+                try {
+                    str = new byte[1024];
+                    strLength = 0;
+                    while(true) {
+                        int bytesLeft = str.length - strLength;
+                        if (bytesLeft == 0) {
+                            byte[] buf2 = new byte[str.length * 2];
+                            System.arraycopy(str, 0, buf2, 0, str.length);
+                            str = buf2;
+                            bytesLeft = str.length - strLength;
+                        }
+                        int bytesRead = is.read(str, strLength, bytesLeft);
+                        if (bytesRead <= 0) {
+                            break;
+                        }
+                        strLength += bytesRead;
+                    }
+                } finally {
+                    is.close();
+                }
+            } catch(IOException e) {
+                throw new Resources.NotFoundException();
+            }
+
+            try {
+                mShader = new String(str, 0, strLength, "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                Log.e("Renderscript shader creation", "Could not decode shader string");
+            }
+
+            return this;
         }
 
         public void addInput(Element e) throws IllegalStateException {
             // Should check for consistant and non-conflicting names...
             if(mInputCount >= MAX_INPUT) {
-                throw new IllegalArgumentException("Max input count exceeded.");
+                throw new RSIllegalArgumentException("Max input count exceeded.");
+            }
+            if (e.isComplex()) {
+                throw new RSIllegalArgumentException("Complex elements not allowed.");
             }
             mInputs[mInputCount++] = e;
         }
@@ -106,26 +159,40 @@
         public void addOutput(Element e) throws IllegalStateException {
             // Should check for consistant and non-conflicting names...
             if(mOutputCount >= MAX_OUTPUT) {
-                throw new IllegalArgumentException("Max output count exceeded.");
+                throw new RSIllegalArgumentException("Max output count exceeded.");
+            }
+            if (e.isComplex()) {
+                throw new RSIllegalArgumentException("Complex elements not allowed.");
             }
             mOutputs[mOutputCount++] = e;
         }
 
+        void resetConstant() {
+            mConstantCount = 0;
+            for(int i = 0; i < MAX_CONSTANT; i ++) {
+                mConstants[i] = null;
+            }
+        }
+
         public int addConstant(Type t) throws IllegalStateException {
             // Should check for consistant and non-conflicting names...
             if(mConstantCount >= MAX_CONSTANT) {
-                throw new IllegalArgumentException("Max input count exceeded.");
+                throw new RSIllegalArgumentException("Max input count exceeded.");
+            }
+            if (t.getElement().isComplex()) {
+                throw new RSIllegalArgumentException("Complex elements not allowed.");
             }
             mConstants[mConstantCount] = t;
             return mConstantCount++;
         }
 
-        public void setTextureCount(int count) throws IllegalArgumentException {
+        public BaseProgramBuilder setTextureCount(int count) throws IllegalArgumentException {
             // Should check for consistant and non-conflicting names...
-            if(count >= MAX_CONSTANT) {
+            if(count >= MAX_TEXTURE) {
                 throw new IllegalArgumentException("Max texture count exceeded.");
             }
             mTextureCount = count;
+            return this;
         }
 
         protected void initProgram(Program p) {
diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java
index 5e04f0c..8858b74 100644
--- a/graphics/java/android/renderscript/ProgramFragment.java
+++ b/graphics/java/android/renderscript/ProgramFragment.java
@@ -55,17 +55,18 @@
             tmp[idx++] = 3;
             tmp[idx++] = mTextureCount;
 
-            int id = mRS.nProgramFragmentCreate2(mShader, tmp);
+            int id = mRS.nProgramFragmentCreate(mShader, tmp);
             ProgramFragment pf = new ProgramFragment(id, mRS);
             initProgram(pf);
             return pf;
         }
     }
 
-    public static class Builder {
+    public static class Builder extends ShaderBuilder {
         public static final int MAX_TEXTURE = 2;
-        RenderScript mRS;
+        int mNumTextures;
         boolean mPointSpriteEnable;
+        boolean mVaryingColorEnable;
 
         public enum EnvMode {
             REPLACE (1),
@@ -100,39 +101,126 @@
         }
         Slot[] mSlots;
 
+        private void buildShaderString() {
+            mShader  = "//rs_shader_internal\n";
+            mShader += "varying lowp vec4 varColor;\n";
+            mShader += "varying vec2 varTex0;\n";
+
+            mShader += "void main() {\n";
+            if (mVaryingColorEnable) {
+                mShader += "  lowp vec4 col = varColor;\n";
+            } else {
+                mShader += "  lowp vec4 col = UNI_Color;\n";
+            }
+
+            if (mNumTextures != 0) {
+                if (mPointSpriteEnable) {
+                    mShader += "  vec2 t0 = gl_PointCoord;\n";
+                } else {
+                    mShader += "  vec2 t0 = varTex0.xy;\n";
+                }
+            }
+
+            for(int i = 0; i < mNumTextures; i ++) {
+                switch(mSlots[i].env) {
+                case REPLACE:
+                    switch (mSlots[i].format) {
+                    case ALPHA:
+                        mShader += "  col.a = texture2D(UNI_Tex0, t0).a;\n";
+                        break;
+                    case LUMINANCE_ALPHA:
+                        mShader += "  col.rgba = texture2D(UNI_Tex0, t0).rgba;\n";
+                        break;
+                    case RGB:
+                        mShader += "  col.rgb = texture2D(UNI_Tex0, t0).rgb;\n";
+                        break;
+                    case RGBA:
+                        mShader += "  col.rgba = texture2D(UNI_Tex0, t0).rgba;\n";
+                        break;
+                    }
+                    break;
+                case MODULATE:
+                    switch (mSlots[i].format) {
+                    case ALPHA:
+                        mShader += "  col.a *= texture2D(UNI_Tex0, t0).a;\n";
+                        break;
+                    case LUMINANCE_ALPHA:
+                        mShader += "  col.rgba *= texture2D(UNI_Tex0, t0).rgba;\n";
+                        break;
+                    case RGB:
+                        mShader += "  col.rgb *= texture2D(UNI_Tex0, t0).rgb;\n";
+                        break;
+                    case RGBA:
+                        mShader += "  col.rgba *= texture2D(UNI_Tex0, t0).rgba;\n";
+                        break;
+                    }
+                    break;
+                case DECAL:
+                    mShader += "  col = texture2D(UNI_Tex0, t0);\n";
+                    break;
+                }
+            }
+
+            mShader += "  gl_FragColor = col;\n";
+            mShader += "}\n";
+        }
+
         public Builder(RenderScript rs) {
+            super(rs);
             mRS = rs;
             mSlots = new Slot[MAX_TEXTURE];
             mPointSpriteEnable = false;
         }
 
-        public void setTexture(EnvMode env, Format fmt, int slot)
+        public Builder setTexture(EnvMode env, Format fmt, int slot)
             throws IllegalArgumentException {
             if((slot < 0) || (slot >= MAX_TEXTURE)) {
                 throw new IllegalArgumentException("MAX_TEXTURE exceeded.");
             }
             mSlots[slot] = new Slot(env, fmt);
+            return this;
         }
 
-        public void setPointSpriteTexCoordinateReplacement(boolean enable) {
+        public Builder setPointSpriteTexCoordinateReplacement(boolean enable) {
             mPointSpriteEnable = enable;
+            return this;
         }
 
+        public Builder setVaryingColor(boolean enable) {
+            mVaryingColorEnable = enable;
+            return this;
+        }
+
+        @Override
         public ProgramFragment create() {
-            mRS.validate();
-            int[] tmp = new int[MAX_TEXTURE * 2 + 1];
-            if (mSlots[0] != null) {
-                tmp[0] = mSlots[0].env.mID;
-                tmp[1] = mSlots[0].format.mID;
+            mNumTextures = 0;
+            for(int i = 0; i < MAX_TEXTURE; i ++) {
+                if(mSlots[i] != null) {
+                    mNumTextures ++;
+                }
             }
-            if (mSlots[1] != null) {
-                tmp[2] = mSlots[1].env.mID;
-                tmp[3] = mSlots[1].format.mID;
+            resetConstant();
+            buildShaderString();
+            Type constType = null;
+            if (!mVaryingColorEnable) {
+                Element.Builder b = new Element.Builder(mRS);
+                b.add(Element.F32_4(mRS), "Color");
+                Type.Builder typeBuilder = new Type.Builder(mRS, b.create());
+                typeBuilder.add(Dimension.X, 1);
+                constType = typeBuilder.create();
+                addConstant(constType);
             }
-            tmp[4] = mPointSpriteEnable ? 1 : 0;
-            int id = mRS.nProgramFragmentCreate(tmp);
-            ProgramFragment pf = new ProgramFragment(id, mRS);
+            setTextureCount(mNumTextures);
+
+            ProgramFragment pf = super.create();
             pf.mTextureCount = MAX_TEXTURE;
+            if (!mVaryingColorEnable) {
+                Allocation constantData = Allocation.createTyped(mRS,constType);
+                float[] data = new float[4];
+                data[0] = data[1] = data[2] = data[3] = 1.0f;
+                constantData.data(data);
+                pf.bindConstants(constantData, 0);
+            }
             return pf;
         }
     }
diff --git a/graphics/java/android/renderscript/ProgramRaster.java b/graphics/java/android/renderscript/ProgramRaster.java
index 56f9bf4..791dac8 100644
--- a/graphics/java/android/renderscript/ProgramRaster.java
+++ b/graphics/java/android/renderscript/ProgramRaster.java
@@ -26,76 +26,113 @@
  *
  **/
 public class ProgramRaster extends BaseObj {
+
+    public enum CullMode {
+        BACK (0),
+        FRONT (1),
+        NONE (2);
+
+        int mID;
+        CullMode(int id) {
+            mID = id;
+        }
+    }
+
     boolean mPointSmooth;
     boolean mLineSmooth;
     boolean mPointSprite;
-    float mPointSize;
     float mLineWidth;
-    Element mIn;
-    Element mOut;
+    CullMode mCullMode;
 
     ProgramRaster(int id, RenderScript rs) {
-        super(rs);
-        mID = id;
+        super(id, rs);
 
-        mPointSize = 1.0f;
         mLineWidth = 1.0f;
         mPointSmooth = false;
         mLineSmooth = false;
         mPointSprite = false;
+
+        mCullMode = CullMode.BACK;
     }
 
-    public void setLineWidth(float w) {
+    void setLineWidth(float w) {
         mRS.validate();
         mLineWidth = w;
         mRS.nProgramRasterSetLineWidth(mID, w);
     }
 
-    public void setPointSize(float s) {
+    void setCullMode(CullMode m) {
         mRS.validate();
-        mPointSize = s;
-        mRS.nProgramRasterSetPointSize(mID, s);
+        mCullMode = m;
+        mRS.nProgramRasterSetCullMode(mID, m.mID);
     }
 
-    void internalInit() {
-        int inID = 0;
-        int outID = 0;
-        if (mIn != null) {
-            inID = mIn.mID;
+    public static ProgramRaster CULL_BACK(RenderScript rs) {
+        if(rs.mProgramRaster_CULL_BACK == null) {
+            ProgramRaster.Builder builder = new ProgramRaster.Builder(rs);
+            builder.setCullMode(CullMode.BACK);
+            rs.mProgramRaster_CULL_BACK = builder.create();
         }
-        if (mOut != null) {
-            outID = mOut.mID;
-        }
-        mID = mRS.nProgramRasterCreate(inID, outID, mPointSmooth, mLineSmooth, mPointSprite);
+        return rs.mProgramRaster_CULL_BACK;
     }
 
+    public static ProgramRaster CULL_FRONT(RenderScript rs) {
+        if(rs.mProgramRaster_CULL_FRONT == null) {
+            ProgramRaster.Builder builder = new ProgramRaster.Builder(rs);
+            builder.setCullMode(CullMode.FRONT);
+            rs.mProgramRaster_CULL_FRONT = builder.create();
+        }
+        return rs.mProgramRaster_CULL_FRONT;
+    }
+
+    public static ProgramRaster CULL_NONE(RenderScript rs) {
+        if(rs.mProgramRaster_CULL_NONE == null) {
+            ProgramRaster.Builder builder = new ProgramRaster.Builder(rs);
+            builder.setCullMode(CullMode.NONE);
+            rs.mProgramRaster_CULL_NONE = builder.create();
+        }
+        return rs.mProgramRaster_CULL_NONE;
+    }
 
     public static class Builder {
         RenderScript mRS;
-        ProgramRaster mPR;
+        boolean mPointSprite;
+        boolean mPointSmooth;
+        boolean mLineSmooth;
+        CullMode mCullMode;
 
-        public Builder(RenderScript rs, Element in, Element out) {
+        public Builder(RenderScript rs) {
             mRS = rs;
-            mPR = new ProgramRaster(0, rs);
+            mPointSmooth = false;
+            mLineSmooth = false;
+            mPointSprite = false;
+            mCullMode = CullMode.BACK;
         }
 
-        public void setPointSpriteEnable(boolean enable) {
-            mPR.mPointSprite = enable;
+        public Builder setPointSpriteEnable(boolean enable) {
+            mPointSprite = enable;
+            return this;
         }
 
-        public void setPointSmoothEnable(boolean enable) {
-            mPR.mPointSmooth = enable;
+        public Builder setPointSmoothEnable(boolean enable) {
+            mPointSmooth = enable;
+            return this;
         }
 
-        public void setLineSmoothEnable(boolean enable) {
-            mPR.mLineSmooth = enable;
+        public Builder setLineSmoothEnable(boolean enable) {
+            mLineSmooth = enable;
+            return this;
         }
 
+        public Builder setCullMode(CullMode m) {
+            mCullMode = m;
+            return this;
+        }
 
         static synchronized ProgramRaster internalCreate(RenderScript rs, Builder b) {
-            b.mPR.internalInit();
-            ProgramRaster pr = b.mPR;
-            b.mPR = new ProgramRaster(0, b.mRS);
+            int id = rs.nProgramRasterCreate(b.mPointSmooth, b.mLineSmooth, b.mPointSprite);
+            ProgramRaster pr = new ProgramRaster(id, rs);
+            pr.setCullMode(b.mCullMode);
             return pr;
         }
 
@@ -111,3 +148,4 @@
 
 
 
+
diff --git a/graphics/java/android/renderscript/ProgramStore.java b/graphics/java/android/renderscript/ProgramStore.java
index 69be245..32c0d01 100644
--- a/graphics/java/android/renderscript/ProgramStore.java
+++ b/graphics/java/android/renderscript/ProgramStore.java
@@ -76,11 +76,143 @@
 
 
     ProgramStore(int id, RenderScript rs) {
-        super(rs);
-        mID = id;
+        super(id, rs);
     }
 
+    public static ProgramStore BLEND_NONE_DEPTH_TEST(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_NONE_DEPTH_TEST == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.LESS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ZERO);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(true);
+            rs.mProgramStore_BLEND_NONE_DEPTH_TEST = builder.create();
+        }
+        return rs.mProgramStore_BLEND_NONE_DEPTH_TEST;
+    }
+    public static ProgramStore BLEND_NONE_DEPTH_NO_DEPTH(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_NONE_DEPTH_NO_DEPTH == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ZERO);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(false);
+            rs.mProgramStore_BLEND_NONE_DEPTH_NO_DEPTH = builder.create();
+        }
+        return rs.mProgramStore_BLEND_NONE_DEPTH_NO_DEPTH;
+    }
+    public static ProgramStore BLEND_NONE_DEPTH_NO_TEST(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_NONE_DEPTH_NO_TEST == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ZERO);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(true);
+            rs.mProgramStore_BLEND_NONE_DEPTH_NO_TEST = builder.create();
+        }
+        return rs.mProgramStore_BLEND_NONE_DEPTH_NO_TEST;
+    }
+    public static ProgramStore BLEND_NONE_DEPTH_NO_WRITE(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_NONE_DEPTH_NO_WRITE == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.LESS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ZERO);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(false);
+            rs.mProgramStore_BLEND_NONE_DEPTH_NO_WRITE = builder.create();
+        }
+        return rs.mProgramStore_BLEND_NONE_DEPTH_NO_WRITE;
+    }
 
+    public static ProgramStore BLEND_ALPHA_DEPTH_TEST(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ALPHA_DEPTH_TEST == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.LESS);
+            builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(true);
+            rs.mProgramStore_BLEND_ALPHA_DEPTH_TEST = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ALPHA_DEPTH_TEST;
+    }
+    public static ProgramStore BLEND_ALPHA_DEPTH_NO_DEPTH(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_DEPTH == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(false);
+            rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_DEPTH = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_DEPTH;
+    }
+    public static ProgramStore BLEND_ALPHA_DEPTH_NO_TEST(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_TEST == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(true);
+            rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_TEST = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_TEST;
+    }
+    public static ProgramStore BLEND_ALPHA_DEPTH_NO_WRITE(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_WRITE == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.LESS);
+            builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(false);
+            rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_WRITE = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ALPHA_DEPTH_NO_WRITE;
+    }
+
+    public static ProgramStore BLEND_ADD_DEPTH_TEST(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ADD_DEPTH_TEST == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.LESS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(true);
+            rs.mProgramStore_BLEND_ADD_DEPTH_TEST = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ADD_DEPTH_TEST;
+    }
+    public static ProgramStore BLEND_ADD_DEPTH_NO_DEPTH(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ADD_DEPTH_NO_DEPTH == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(false);
+            rs.mProgramStore_BLEND_ADD_DEPTH_NO_DEPTH = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ADD_DEPTH_NO_DEPTH;
+    }
+    public static ProgramStore BLEND_ADD_DEPTH_NO_TEST(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ADD_DEPTH_NO_TEST == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(true);
+            rs.mProgramStore_BLEND_ADD_DEPTH_NO_DEPTH = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ADD_DEPTH_NO_TEST;
+    }
+    public static ProgramStore BLEND_ADD_DEPTH_NO_WRITE(RenderScript rs) {
+        if(rs.mProgramStore_BLEND_ADD_DEPTH_NO_WRITE == null) {
+            ProgramStore.Builder builder = new ProgramStore.Builder(rs);
+            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
+            builder.setDitherEnable(false);
+            builder.setDepthMask(false);
+            rs.mProgramStore_BLEND_ADD_DEPTH_NO_WRITE = builder.create();
+        }
+        return rs.mProgramStore_BLEND_ADD_DEPTH_NO_WRITE;
+    }
 
     public static class Builder {
         RenderScript mRS;
@@ -110,32 +242,49 @@
             mColorMaskA = true;
             mBlendSrc = BlendSrcFunc.ONE;
             mBlendDst = BlendDstFunc.ZERO;
-
-
         }
 
-        public void setDepthFunc(DepthFunc func) {
+        public Builder(RenderScript rs) {
+            mRS = rs;
+            mIn = null;
+            mOut = null;
+            mDepthFunc = DepthFunc.ALWAYS;
+            mDepthMask = false;
+            mColorMaskR = true;
+            mColorMaskG = true;
+            mColorMaskB = true;
+            mColorMaskA = true;
+            mBlendSrc = BlendSrcFunc.ONE;
+            mBlendDst = BlendDstFunc.ZERO;
+        }
+
+        public Builder setDepthFunc(DepthFunc func) {
             mDepthFunc = func;
+            return this;
         }
 
-        public void setDepthMask(boolean enable) {
+        public Builder setDepthMask(boolean enable) {
             mDepthMask = enable;
+            return this;
         }
 
-        public void setColorMask(boolean r, boolean g, boolean b, boolean a) {
+        public Builder setColorMask(boolean r, boolean g, boolean b, boolean a) {
             mColorMaskR = r;
             mColorMaskG = g;
             mColorMaskB = b;
             mColorMaskA = a;
+            return this;
         }
 
-        public void setBlendFunc(BlendSrcFunc src, BlendDstFunc dst) {
+        public Builder setBlendFunc(BlendSrcFunc src, BlendDstFunc dst) {
             mBlendSrc = src;
             mBlendDst = dst;
+            return this;
         }
 
-        public void setDitherEnable(boolean enable) {
+        public Builder setDitherEnable(boolean enable) {
             mDither = enable;
+            return this;
         }
 
         static synchronized ProgramStore internalCreate(RenderScript rs, Builder b) {
@@ -147,17 +296,17 @@
             if (b.mOut != null) {
                 outID = b.mOut.mID;
             }
-            rs.nProgramFragmentStoreBegin(inID, outID);
-            rs.nProgramFragmentStoreDepthFunc(b.mDepthFunc.mID);
-            rs.nProgramFragmentStoreDepthMask(b.mDepthMask);
-            rs.nProgramFragmentStoreColorMask(b.mColorMaskR,
+            rs.nProgramStoreBegin(inID, outID);
+            rs.nProgramStoreDepthFunc(b.mDepthFunc.mID);
+            rs.nProgramStoreDepthMask(b.mDepthMask);
+            rs.nProgramStoreColorMask(b.mColorMaskR,
                                               b.mColorMaskG,
                                               b.mColorMaskB,
                                               b.mColorMaskA);
-            rs.nProgramFragmentStoreBlendFunc(b.mBlendSrc.mID, b.mBlendDst.mID);
-            rs.nProgramFragmentStoreDither(b.mDither);
+            rs.nProgramStoreBlendFunc(b.mBlendSrc.mID, b.mBlendDst.mID);
+            rs.nProgramStoreDither(b.mDither);
 
-            int id = rs.nProgramFragmentStoreCreate();
+            int id = rs.nProgramStoreCreate();
             return new ProgramStore(id, rs);
         }
 
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
index 1b155d7..65a0af2 100644
--- a/graphics/java/android/renderscript/ProgramVertex.java
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -17,6 +17,7 @@
 package android.renderscript;
 
 
+import android.graphics.Matrix;
 import android.util.Config;
 import android.util.Log;
 
@@ -38,25 +39,6 @@
         bindConstants(va.mAlloc, 0);
     }
 
-
-    public static class Builder {
-        RenderScript mRS;
-        boolean mTextureMatrixEnable;
-
-        public Builder(RenderScript rs, Element in, Element out) {
-            mRS = rs;
-        }
-
-        public void setTextureMatrixEnable(boolean enable) {
-            mTextureMatrixEnable = enable;
-        }
-
-        public ProgramVertex create() {
-            int id = mRS.nProgramVertexCreate(mTextureMatrixEnable);
-            return new ProgramVertex(id, mRS);
-        }
-    }
-
     public static class ShaderBuilder extends BaseProgramBuilder {
         public ShaderBuilder(RenderScript rs) {
             super(rs);
@@ -82,13 +64,75 @@
             tmp[idx++] = 3;
             tmp[idx++] = mTextureCount;
 
-            int id = mRS.nProgramVertexCreate2(mShader, tmp);
+            int id = mRS.nProgramVertexCreate(mShader, tmp);
             ProgramVertex pv = new ProgramVertex(id, mRS);
             initProgram(pv);
             return pv;
         }
     }
 
+    public static class Builder extends ShaderBuilder {
+        boolean mTextureMatrixEnable;
+
+        public Builder(RenderScript rs, Element in, Element out) {
+            super(rs);
+        }
+        public Builder(RenderScript rs) {
+            super(rs);
+        }
+
+        public Builder setTextureMatrixEnable(boolean enable) {
+            mTextureMatrixEnable = enable;
+            return this;
+        }
+        static Type getConstantInputType(RenderScript rs) {
+            Element.Builder b = new Element.Builder(rs);
+            b.add(Element.MATRIX4X4(rs), "MV");
+            b.add(Element.MATRIX4X4(rs), "P");
+            b.add(Element.MATRIX4X4(rs), "TexMatrix");
+            b.add(Element.MATRIX4X4(rs), "MVP");
+
+            Type.Builder typeBuilder = new Type.Builder(rs, b.create());
+            typeBuilder.add(Dimension.X, 1);
+            return typeBuilder.create();
+        }
+
+        private void buildShaderString() {
+
+            mShader  = "//rs_shader_internal\n";
+            mShader += "varying vec4 varColor;\n";
+            mShader += "varying vec2 varTex0;\n";
+
+            mShader += "void main() {\n";
+            mShader += "  gl_Position = UNI_MVP * ATTRIB_position;\n";
+            mShader += "  gl_PointSize = 1.0;\n";
+
+            mShader += "  varColor = ATTRIB_color;\n";
+            if (mTextureMatrixEnable) {
+                mShader += "  varTex0 = (UNI_TexMatrix * vec4(ATTRIB_texture0, 0.0, 1.0)).xy;\n";
+            } else {
+                mShader += "  varTex0 = ATTRIB_texture0;\n";
+            }
+            mShader += "}\n";
+        }
+
+        @Override
+        public ProgramVertex create() {
+            buildShaderString();
+
+            addConstant(getConstantInputType(mRS));
+
+            Element.Builder b = new Element.Builder(mRS);
+            b.add(Element.F32_4(mRS), "position");
+            b.add(Element.F32_4(mRS), "color");
+            b.add(Element.F32_3(mRS), "normal");
+            b.add(Element.F32_2(mRS), "texture0");
+            addInput(b.create());
+
+            return super.create();
+        }
+    }
+
 
 
     public static class MatrixAllocation {
@@ -101,16 +145,17 @@
         Matrix4f mTexture;
 
         public Allocation mAlloc;
+        private FieldPacker mIOBuffer;
 
         public MatrixAllocation(RenderScript rs) {
-            mModel = new Matrix4f();
-            mProjection = new Matrix4f();
-            mTexture = new Matrix4f();
-
-            mAlloc = Allocation.createSized(rs, Element.createUser(rs, Element.DataType.FLOAT_32), 48);
-            mAlloc.subData1D(MODELVIEW_OFFSET, 16, mModel.mMat);
-            mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
-            mAlloc.subData1D(TEXTURE_OFFSET, 16, mTexture.mMat);
+            Type constInputType = ProgramVertex.Builder.getConstantInputType(rs);
+            mAlloc = Allocation.createTyped(rs, constInputType);
+            int bufferSize = constInputType.getElement().getSizeBytes()*
+                             constInputType.getElementCount();
+            mIOBuffer = new FieldPacker(bufferSize);
+            loadModelview(new Matrix4f());
+            loadProjection(new Matrix4f());
+            loadTexture(new Matrix4f());
         }
 
         public void destroy() {
@@ -118,24 +163,32 @@
             mAlloc = null;
         }
 
+        private void addToBuffer(int offset, Matrix4f m) {
+            mIOBuffer.reset(offset);
+            for(int i = 0; i < 16; i ++) {
+                mIOBuffer.addF32(m.mMat[i]);
+            }
+            mAlloc.data(mIOBuffer.getData());
+        }
+
         public void loadModelview(Matrix4f m) {
             mModel = m;
-            mAlloc.subData1D(MODELVIEW_OFFSET, 16, m.mMat);
+            addToBuffer(MODELVIEW_OFFSET*4, m);
         }
 
         public void loadProjection(Matrix4f m) {
             mProjection = m;
-            mAlloc.subData1D(PROJECTION_OFFSET, 16, m.mMat);
+            addToBuffer(PROJECTION_OFFSET*4, m);
         }
 
         public void loadTexture(Matrix4f m) {
             mTexture = m;
-            mAlloc.subData1D(TEXTURE_OFFSET, 16, m.mMat);
+            addToBuffer(TEXTURE_OFFSET*4, m);
         }
 
         public void setupOrthoWindow(int w, int h) {
             mProjection.loadOrtho(0,w, h,0, -1,1);
-            mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+            addToBuffer(PROJECTION_OFFSET*4, mProjection);
         }
 
         public void setupOrthoNormalized(int w, int h) {
@@ -147,7 +200,7 @@
                 float aspect = ((float)h) / w;
                 mProjection.loadOrtho(-1,1, -aspect,aspect,  -1,1);
             }
-            mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+            addToBuffer(PROJECTION_OFFSET*4, mProjection);
         }
 
         public void setupProjectionNormalized(int w, int h) {
@@ -173,7 +226,7 @@
             m1.loadMultiply(m1, m2);
 
             mProjection = m1;
-            mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+            addToBuffer(PROJECTION_OFFSET*4, mProjection);
         }
 
     }
diff --git a/graphics/java/android/renderscript/RSDriverException.java b/graphics/java/android/renderscript/RSDriverException.java
new file mode 100644
index 0000000..61787e6
--- /dev/null
+++ b/graphics/java/android/renderscript/RSDriverException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+
+/**
+ * Base class for all exceptions thrown by the Android
+ * Renderscript
+ * @hide
+ */
+public class RSDriverException extends RSRuntimeException {
+    public RSDriverException(String string) {
+        super(string);
+    }
+}
+
+
+
diff --git a/graphics/java/android/renderscript/RSIllegalArgumentException.java b/graphics/java/android/renderscript/RSIllegalArgumentException.java
new file mode 100644
index 0000000..8d67a8d
--- /dev/null
+++ b/graphics/java/android/renderscript/RSIllegalArgumentException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+
+/**
+ * Base class for all exceptions thrown by the Android
+ * Renderscript
+ * @hide
+ */
+public class RSIllegalArgumentException extends RSRuntimeException {
+    public RSIllegalArgumentException(String string) {
+        super(string);
+    }
+}
+
+
diff --git a/graphics/java/android/renderscript/RSInvalidStateException.java b/graphics/java/android/renderscript/RSInvalidStateException.java
new file mode 100644
index 0000000..53b9479
--- /dev/null
+++ b/graphics/java/android/renderscript/RSInvalidStateException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+
+/**
+ * Base class for all exceptions thrown by the Android
+ * Renderscript
+ * @hide
+ */
+public class RSInvalidStateException extends RSRuntimeException {
+    public RSInvalidStateException(String string) {
+        super(string);
+    }
+}
+
+
+
diff --git a/graphics/java/android/renderscript/RSRuntimeException.java b/graphics/java/android/renderscript/RSRuntimeException.java
new file mode 100644
index 0000000..4c97937
--- /dev/null
+++ b/graphics/java/android/renderscript/RSRuntimeException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+
+/**
+ * Base class for all exceptions thrown by the Android
+ * Renderscript
+ * @hide
+ */
+public class RSRuntimeException
+  extends java.lang.RuntimeException {
+    public RSRuntimeException(String string) {
+        super(string);
+    }
+}
+
diff --git a/graphics/java/android/renderscript/RSSurfaceView.java b/graphics/java/android/renderscript/RSSurfaceView.java
index f05e84c..2540d01 100644
--- a/graphics/java/android/renderscript/RSSurfaceView.java
+++ b/graphics/java/android/renderscript/RSSurfaceView.java
@@ -25,7 +25,6 @@
 import android.os.Message;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
@@ -146,22 +145,18 @@
 
     // ----------------------------------------------------------------------
 
-    public RenderScriptGL createRenderScript(boolean useDepth, boolean forceSW) {
+    public RenderScriptGL createRenderScript(RenderScriptGL.SurfaceConfig sc) {
         Log.v(RenderScript.LOG_TAG, "createRenderScript");
-        mRS = new RenderScriptGL(useDepth, forceSW);
+        mRS = new RenderScriptGL(sc);
         return mRS;
     }
 
-    public RenderScriptGL createRenderScript(boolean useDepth) {
-        return createRenderScript(useDepth, false);
-    }
-
     public void destroyRenderScript() {
         Log.v(RenderScript.LOG_TAG, "destroyRenderScript");
         mRS.destroy();
         mRS = null;
     }
-    
+
     public void createRenderScript(RenderScriptGL rs) {
         mRS = rs;
     }
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index a935243..21efa39 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -57,151 +57,480 @@
         }
     }
 
+    // Non-threadsafe functions.
     native void nInitElements(int a8, int rgba4444, int rgba8888, int rgb565);
-
     native int  nDeviceCreate();
     native void nDeviceDestroy(int dev);
     native void nDeviceSetConfig(int dev, int param, int value);
-    native int  nContextCreateGL(int dev, int ver, boolean useDepth);
-    native int  nContextCreate(int dev, int ver);
-    native void nContextDestroy(int con);
-    native void nContextSetSurface(int w, int h, Surface sur);
-    native void nContextSetPriority(int p);
-    native void nContextDump(int bits);
-
-    native void nContextBindRootScript(int script);
-    native void nContextBindSampler(int sampler, int slot);
-    native void nContextBindProgramFragmentStore(int pfs);
-    native void nContextBindProgramFragment(int pf);
-    native void nContextBindProgramVertex(int pf);
-    native void nContextBindProgramRaster(int pr);
-    native void nContextPause();
-    native void nContextResume();
-    native int nContextGetMessage(int[] data, boolean wait);
-    native void nContextInitToClient();
-    native void nContextDeinitToClient();
-
-    native void nAssignName(int obj, byte[] name);
-    native void nObjDestroy(int id);
-    native void nObjDestroyOOB(int id);
-    native int  nFileOpen(byte[] name);
+    native int  nContextGetMessage(int con, int[] data, boolean wait);
+    native void nContextInitToClient(int con);
+    native void nContextDeinitToClient(int con);
 
 
-    native int  nElementCreate(int type, int kind, boolean norm, int vecSize);
-    native int  nElementCreate2(int[] elements, String[] names);
+    // Methods below are wrapped to protect the non-threadsafe
+    // lockless fifo.
+    native int  rsnContextCreateGL(int dev, int ver,
+                 int colorMin, int colorPref,
+                 int alphaMin, int alphaPref,
+                 int depthMin, int depthPref,
+                 int stencilMin, int stencilPref,
+                 int samplesMin, int samplesPref, float samplesQ);
+    synchronized int nContextCreateGL(int dev, int ver,
+                 int colorMin, int colorPref,
+                 int alphaMin, int alphaPref,
+                 int depthMin, int depthPref,
+                 int stencilMin, int stencilPref,
+                 int samplesMin, int samplesPref, float samplesQ) {
+        return rsnContextCreateGL(dev, ver, colorMin, colorPref,
+                                  alphaMin, alphaPref, depthMin, depthPref,
+                                  stencilMin, stencilPref,
+                                  samplesMin, samplesPref, samplesQ);
+    }
+    native int  rsnContextCreate(int dev, int ver);
+    synchronized int nContextCreate(int dev, int ver) {
+        return rsnContextCreate(dev, ver);
+    }
+    native void rsnContextDestroy(int con);
+    synchronized void nContextDestroy() {
+        rsnContextDestroy(mContext);
+    }
+    native void rsnContextSetSurface(int con, int w, int h, Surface sur);
+    synchronized void nContextSetSurface(int w, int h, Surface sur) {
+        rsnContextSetSurface(mContext, w, h, sur);
+    }
+    native void rsnContextSetPriority(int con, int p);
+    synchronized void nContextSetPriority(int p) {
+        rsnContextSetPriority(mContext, p);
+    }
+    native void rsnContextDump(int con, int bits);
+    synchronized void nContextDump(int bits) {
+        rsnContextDump(mContext, bits);
+    }
+    native void rsnContextFinish(int con);
+    synchronized void nContextFinish() {
+        rsnContextFinish(mContext);
+    }
 
-    native void nTypeBegin(int elementID);
-    native void nTypeAdd(int dim, int val);
-    native int  nTypeCreate();
-    native void nTypeFinalDestroy(Type t);
-    native void nTypeSetupFields(Type t, int[] types, int[] bits, Field[] IDs);
+    native void rsnContextBindRootScript(int con, int script);
+    synchronized void nContextBindRootScript(int script) {
+        rsnContextBindRootScript(mContext, script);
+    }
+    native void rsnContextBindSampler(int con, int sampler, int slot);
+    synchronized void nContextBindSampler(int sampler, int slot) {
+        rsnContextBindSampler(mContext, sampler, slot);
+    }
+    native void rsnContextBindProgramStore(int con, int pfs);
+    synchronized void nContextBindProgramStore(int pfs) {
+        rsnContextBindProgramStore(mContext, pfs);
+    }
+    native void rsnContextBindProgramFragment(int con, int pf);
+    synchronized void nContextBindProgramFragment(int pf) {
+        rsnContextBindProgramFragment(mContext, pf);
+    }
+    native void rsnContextBindProgramVertex(int con, int pv);
+    synchronized void nContextBindProgramVertex(int pv) {
+        rsnContextBindProgramVertex(mContext, pv);
+    }
+    native void rsnContextBindProgramRaster(int con, int pr);
+    synchronized void nContextBindProgramRaster(int pr) {
+        rsnContextBindProgramRaster(mContext, pr);
+    }
+    native void rsnContextPause(int con);
+    synchronized void nContextPause() {
+        rsnContextPause(mContext);
+    }
+    native void rsnContextResume(int con);
+    synchronized void nContextResume() {
+        rsnContextResume(mContext);
+    }
 
-    native int  nAllocationCreateTyped(int type);
-    native int  nAllocationCreateFromBitmap(int dstFmt, boolean genMips, Bitmap bmp);
-    native int  nAllocationCreateBitmapRef(int type, Bitmap bmp);
-    native int  nAllocationCreateFromBitmapBoxed(int dstFmt, boolean genMips, Bitmap bmp);
-    native int  nAllocationCreateFromAssetStream(int dstFmt, boolean genMips, int assetStream);
+    native void rsnAssignName(int con, int obj, byte[] name);
+    synchronized void nAssignName(int obj, byte[] name) {
+        rsnAssignName(mContext, obj, name);
+    }
+    native String rsnGetName(int con, int obj);
+    synchronized String nGetName(int obj) {
+        return rsnGetName(mContext, obj);
+    }
+    native void rsnObjDestroy(int con, int id);
+    synchronized void nObjDestroy(int id) {
+        rsnObjDestroy(mContext, id);
+    }
 
-    native void nAllocationUploadToTexture(int alloc, boolean genMips, int baseMioLevel);
-    native void nAllocationUploadToBufferObject(int alloc);
+    native int  rsnElementCreate(int con, int type, int kind, boolean norm, int vecSize);
+    synchronized int nElementCreate(int type, int kind, boolean norm, int vecSize) {
+        return rsnElementCreate(mContext, type, kind, norm, vecSize);
+    }
+    native int  rsnElementCreate2(int con, int[] elements, String[] names, int[] arraySizes);
+    synchronized int nElementCreate2(int[] elements, String[] names, int[] arraySizes) {
+        return rsnElementCreate2(mContext, elements, names, arraySizes);
+    }
+    native void rsnElementGetNativeData(int con, int id, int[] elementData);
+    synchronized void nElementGetNativeData(int id, int[] elementData) {
+        rsnElementGetNativeData(mContext, id, elementData);
+    }
+    native void rsnElementGetSubElements(int con, int id, int[] IDs, String[] names);
+    synchronized void nElementGetSubElements(int id, int[] IDs, String[] names) {
+        rsnElementGetSubElements(mContext, id, IDs, names);
+    }
 
-    native void nAllocationSubData1D(int id, int off, int count, int[] d, int sizeBytes);
-    native void nAllocationSubData1D(int id, int off, int count, short[] d, int sizeBytes);
-    native void nAllocationSubData1D(int id, int off, int count, byte[] d, int sizeBytes);
-    native void nAllocationSubData1D(int id, int off, int count, float[] d, int sizeBytes);
+    native int rsnTypeCreate(int con, int eid, int[] dims, int[] vals);
+    synchronized int nTypeCreate(int eid, int[] dims, int[] vals) {
+        return rsnTypeCreate(mContext, eid, dims, vals);
+    }
+    native void rsnTypeGetNativeData(int con, int id, int[] typeData);
+    synchronized void nTypeGetNativeData(int id, int[] typeData) {
+        rsnTypeGetNativeData(mContext, id, typeData);
+    }
 
-    native void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, int[] d, int sizeBytes);
-    native void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, float[] d, int sizeBytes);
-    native void nAllocationRead(int id, int[] d);
-    native void nAllocationRead(int id, float[] d);
-    native void nAllocationSubDataFromObject(int id, Type t, int offset, Object o);
-    native void nAllocationSubReadFromObject(int id, Type t, int offset, Object o);
+    native int  rsnAllocationCreateTyped(int con, int type);
+    synchronized int nAllocationCreateTyped(int type) {
+        return rsnAllocationCreateTyped(mContext, type);
+    }
+    native void  rsnAllocationUpdateFromBitmap(int con, int alloc, Bitmap bmp);
+    synchronized void nAllocationUpdateFromBitmap(int alloc, Bitmap bmp) {
+        rsnAllocationUpdateFromBitmap(mContext, alloc, bmp);
+    }
+    native int  rsnAllocationCreateFromBitmap(int con, int dstFmt, boolean genMips, Bitmap bmp);
+    synchronized int nAllocationCreateFromBitmap(int dstFmt, boolean genMips, Bitmap bmp) {
+        return rsnAllocationCreateFromBitmap(mContext, dstFmt, genMips, bmp);
+    }
+    native int  rsnAllocationCreateBitmapRef(int con, int type, Bitmap bmp);
+    synchronized int nAllocationCreateBitmapRef(int type, Bitmap bmp) {
+        return rsnAllocationCreateBitmapRef(mContext, type, bmp);
+    }
+    native int  rsnAllocationCreateFromAssetStream(int con, int dstFmt, boolean genMips, int assetStream);
+    synchronized int nAllocationCreateFromAssetStream(int dstFmt, boolean genMips, int assetStream) {
+        return rsnAllocationCreateFromAssetStream(mContext, dstFmt, genMips, assetStream);
+    }
 
-    native void nAdapter1DBindAllocation(int ad, int alloc);
-    native void nAdapter1DSetConstraint(int ad, int dim, int value);
-    native void nAdapter1DData(int ad, int[] d);
-    native void nAdapter1DData(int ad, float[] d);
-    native void nAdapter1DSubData(int ad, int off, int count, int[] d);
-    native void nAdapter1DSubData(int ad, int off, int count, float[] d);
-    native int  nAdapter1DCreate();
+    native void rsnAllocationUploadToTexture(int con, int alloc, boolean genMips, int baseMioLevel);
+    synchronized void nAllocationUploadToTexture(int alloc, boolean genMips, int baseMioLevel) {
+        rsnAllocationUploadToTexture(mContext, alloc, genMips, baseMioLevel);
+    }
+    native void rsnAllocationUploadToBufferObject(int con, int alloc);
+    synchronized void nAllocationUploadToBufferObject(int alloc) {
+        rsnAllocationUploadToBufferObject(mContext, alloc);
+    }
 
-    native void nAdapter2DBindAllocation(int ad, int alloc);
-    native void nAdapter2DSetConstraint(int ad, int dim, int value);
-    native void nAdapter2DData(int ad, int[] d);
-    native void nAdapter2DData(int ad, float[] d);
-    native void nAdapter2DSubData(int ad, int xoff, int yoff, int w, int h, int[] d);
-    native void nAdapter2DSubData(int ad, int xoff, int yoff, int w, int h, float[] d);
-    native int  nAdapter2DCreate();
+    native void rsnAllocationSubData1D(int con, int id, int off, int count, int[] d, int sizeBytes);
+    synchronized void nAllocationSubData1D(int id, int off, int count, int[] d, int sizeBytes) {
+        rsnAllocationSubData1D(mContext, id, off, count, d, sizeBytes);
+    }
+    native void rsnAllocationSubData1D(int con, int id, int off, int count, short[] d, int sizeBytes);
+    synchronized void nAllocationSubData1D(int id, int off, int count, short[] d, int sizeBytes) {
+        rsnAllocationSubData1D(mContext, id, off, count, d, sizeBytes);
+    }
+    native void rsnAllocationSubData1D(int con, int id, int off, int count, byte[] d, int sizeBytes);
+    synchronized void nAllocationSubData1D(int id, int off, int count, byte[] d, int sizeBytes) {
+        rsnAllocationSubData1D(mContext, id, off, count, d, sizeBytes);
+    }
+    native void rsnAllocationSubElementData1D(int con, int id, int xoff, int compIdx, byte[] d, int sizeBytes);
+    synchronized void nAllocationSubElementData1D(int id, int xoff, int compIdx, byte[] d, int sizeBytes) {
+        rsnAllocationSubElementData1D(mContext, id, xoff, compIdx, d, sizeBytes);
+    }
+    native void rsnAllocationSubData1D(int con, int id, int off, int count, float[] d, int sizeBytes);
+    synchronized void nAllocationSubData1D(int id, int off, int count, float[] d, int sizeBytes) {
+        rsnAllocationSubData1D(mContext, id, off, count, d, sizeBytes);
+    }
 
-    native void nScriptBindAllocation(int script, int alloc, int slot);
-    native void nScriptSetClearColor(int script, float r, float g, float b, float a);
-    native void nScriptSetClearDepth(int script, float depth);
-    native void nScriptSetClearStencil(int script, int stencil);
-    native void nScriptSetTimeZone(int script, byte[] timeZone);
-    native void nScriptSetType(int type, boolean writable, String name, int slot);
-    native void nScriptSetRoot(boolean isRoot);
-    native void nScriptSetInvokable(String name, int slot);
-    native void nScriptInvoke(int id, int slot);
+    native void rsnAllocationSubData2D(int con, int id, int xoff, int yoff, int w, int h, int[] d, int sizeBytes);
+    synchronized void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, int[] d, int sizeBytes) {
+        rsnAllocationSubData2D(mContext, id, xoff, yoff, w, h, d, sizeBytes);
+    }
+    native void rsnAllocationSubData2D(int con, int id, int xoff, int yoff, int w, int h, float[] d, int sizeBytes);
+    synchronized void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, float[] d, int sizeBytes) {
+        rsnAllocationSubData2D(mContext, id, xoff, yoff, w, h, d, sizeBytes);
+    }
+    native void rsnAllocationRead(int con, int id, int[] d);
+    synchronized void nAllocationRead(int id, int[] d) {
+        rsnAllocationRead(mContext, id, d);
+    }
+    native void rsnAllocationRead(int con, int id, float[] d);
+    synchronized void nAllocationRead(int id, float[] d) {
+        rsnAllocationRead(mContext, id, d);
+    }
+    native int  rsnAllocationGetType(int con, int id);
+    synchronized int nAllocationGetType(int id) {
+        return rsnAllocationGetType(mContext, id);
+    }
 
-    native void nScriptCBegin();
-    native void nScriptCSetScript(byte[] script, int offset, int length);
-    native int  nScriptCCreate();
-    native void nScriptCAddDefineI32(String name, int value);
-    native void nScriptCAddDefineF(String name, float value);
+    native void rsnAllocationResize1D(int con, int id, int dimX);
+    synchronized void nAllocationResize1D(int id, int dimX) {
+        rsnAllocationResize1D(mContext, id, dimX);
+    }
+    native void rsnAllocationResize2D(int con, int id, int dimX, int dimY);
+    synchronized void nAllocationResize2D(int id, int dimX, int dimY) {
+        rsnAllocationResize2D(mContext, id, dimX, dimY);
+    }
 
-    native void nSamplerBegin();
-    native void nSamplerSet(int param, int value);
-    native int  nSamplerCreate();
+    native int  rsnFileA3DCreateFromAssetStream(int con, int assetStream);
+    synchronized int nFileA3DCreateFromAssetStream(int assetStream) {
+        return rsnFileA3DCreateFromAssetStream(mContext, assetStream);
+    }
+    native int  rsnFileA3DGetNumIndexEntries(int con, int fileA3D);
+    synchronized int nFileA3DGetNumIndexEntries(int fileA3D) {
+        return rsnFileA3DGetNumIndexEntries(mContext, fileA3D);
+    }
+    native void rsnFileA3DGetIndexEntries(int con, int fileA3D, int numEntries, int[] IDs, String[] names);
+    synchronized void nFileA3DGetIndexEntries(int fileA3D, int numEntries, int[] IDs, String[] names) {
+        rsnFileA3DGetIndexEntries(mContext, fileA3D, numEntries, IDs, names);
+    }
+    native int  rsnFileA3DGetEntryByIndex(int con, int fileA3D, int index);
+    synchronized int nFileA3DGetEntryByIndex(int fileA3D, int index) {
+        return rsnFileA3DGetEntryByIndex(mContext, fileA3D, index);
+    }
 
-    native void nProgramFragmentStoreBegin(int in, int out);
-    native void nProgramFragmentStoreDepthFunc(int func);
-    native void nProgramFragmentStoreDepthMask(boolean enable);
-    native void nProgramFragmentStoreColorMask(boolean r, boolean g, boolean b, boolean a);
-    native void nProgramFragmentStoreBlendFunc(int src, int dst);
-    native void nProgramFragmentStoreDither(boolean enable);
-    native int  nProgramFragmentStoreCreate();
+    native int  rsnFontCreateFromFile(int con, String fileName, int size, int dpi);
+    synchronized int nFontCreateFromFile(String fileName, int size, int dpi) {
+        return rsnFontCreateFromFile(mContext, fileName, size, dpi);
+    }
 
-    native int  nProgramRasterCreate(int in, int out, boolean pointSmooth, boolean lineSmooth, boolean pointSprite);
-    native void nProgramRasterSetLineWidth(int pr, float v);
-    native void nProgramRasterSetPointSize(int pr, float v);
+    native void rsnAdapter1DBindAllocation(int con, int ad, int alloc);
+    synchronized void nAdapter1DBindAllocation(int ad, int alloc) {
+        rsnAdapter1DBindAllocation(mContext, ad, alloc);
+    }
+    native void rsnAdapter1DSetConstraint(int con, int ad, int dim, int value);
+    synchronized void nAdapter1DSetConstraint(int ad, int dim, int value) {
+        rsnAdapter1DSetConstraint(mContext, ad, dim, value);
+    }
+    native void rsnAdapter1DData(int con, int ad, int[] d);
+    synchronized void nAdapter1DData(int ad, int[] d) {
+        rsnAdapter1DData(mContext, ad, d);
+    }
+    native void rsnAdapter1DData(int con, int ad, float[] d);
+    synchronized void nAdapter1DData(int ad, float[] d) {
+        rsnAdapter1DData(mContext, ad, d);
+    }
+    native void rsnAdapter1DSubData(int con, int ad, int off, int count, int[] d);
+    synchronized void nAdapter1DSubData(int ad, int off, int count, int[] d) {
+        rsnAdapter1DSubData(mContext, ad, off, count, d);
+    }
+    native void rsnAdapter1DSubData(int con, int ad, int off, int count, float[] d);
+    synchronized void nAdapter1DSubData(int ad, int off, int count, float[] d) {
+        rsnAdapter1DSubData(mContext, ad, off, count, d);
+    }
+    native int  rsnAdapter1DCreate(int con);
+    synchronized int nAdapter1DCreate() {
+        return rsnAdapter1DCreate(mContext);
+    }
 
-    native void nProgramBindConstants(int pv, int slot, int mID);
-    native void nProgramBindTexture(int vpf, int slot, int a);
-    native void nProgramBindSampler(int vpf, int slot, int s);
+    native void rsnAdapter2DBindAllocation(int con, int ad, int alloc);
+    synchronized void nAdapter2DBindAllocation(int ad, int alloc) {
+        rsnAdapter2DBindAllocation(mContext, ad, alloc);
+    }
+    native void rsnAdapter2DSetConstraint(int con, int ad, int dim, int value);
+    synchronized void nAdapter2DSetConstraint(int ad, int dim, int value) {
+        rsnAdapter2DSetConstraint(mContext, ad, dim, value);
+    }
+    native void rsnAdapter2DData(int con, int ad, int[] d);
+    synchronized void nAdapter2DData(int ad, int[] d) {
+        rsnAdapter2DData(mContext, ad, d);
+    }
+    native void rsnAdapter2DData(int con, int ad, float[] d);
+    synchronized void nAdapter2DData(int ad, float[] d) {
+        rsnAdapter2DData(mContext, ad, d);
+    }
+    native void rsnAdapter2DSubData(int con, int ad, int xoff, int yoff, int w, int h, int[] d);
+    synchronized void nAdapter2DSubData(int ad, int xoff, int yoff, int w, int h, int[] d) {
+        rsnAdapter2DSubData(mContext, ad, xoff, yoff, w, h, d);
+    }
+    native void rsnAdapter2DSubData(int con, int ad, int xoff, int yoff, int w, int h, float[] d);
+    synchronized void nAdapter2DSubData(int ad, int xoff, int yoff, int w, int h, float[] d) {
+        rsnAdapter2DSubData(mContext, ad, xoff, yoff, w, h, d);
+    }
+    native int  rsnAdapter2DCreate(int con);
+    synchronized int nAdapter2DCreate() {
+        return rsnAdapter2DCreate(mContext);
+    }
 
-    native int  nProgramFragmentCreate(int[] params);
-    native int  nProgramFragmentCreate2(String shader, int[] params);
+    native void rsnScriptBindAllocation(int con, int script, int alloc, int slot);
+    synchronized void nScriptBindAllocation(int script, int alloc, int slot) {
+        rsnScriptBindAllocation(mContext, script, alloc, slot);
+    }
+    native void rsnScriptSetTimeZone(int con, int script, byte[] timeZone);
+    synchronized void nScriptSetTimeZone(int script, byte[] timeZone) {
+        rsnScriptSetTimeZone(mContext, script, timeZone);
+    }
+    native void rsnScriptInvoke(int con, int id, int slot);
+    synchronized void nScriptInvoke(int id, int slot) {
+        rsnScriptInvoke(mContext, id, slot);
+    }
+    native void rsnScriptInvokeV(int con, int id, int slot, byte[] params);
+    synchronized void nScriptInvokeV(int id, int slot, byte[] params) {
+        rsnScriptInvokeV(mContext, id, slot, params);
+    }
+    native void rsnScriptSetVarI(int con, int id, int slot, int val);
+    synchronized void nScriptSetVarI(int id, int slot, int val) {
+        rsnScriptSetVarI(mContext, id, slot, val);
+    }
+    native void rsnScriptSetVarJ(int con, int id, int slot, long val);
+    synchronized void nScriptSetVarJ(int id, int slot, long val) {
+        rsnScriptSetVarJ(mContext, id, slot, val);
+    }
+    native void rsnScriptSetVarF(int con, int id, int slot, float val);
+    synchronized void nScriptSetVarF(int id, int slot, float val) {
+        rsnScriptSetVarF(mContext, id, slot, val);
+    }
+    native void rsnScriptSetVarD(int con, int id, int slot, double val);
+    synchronized void nScriptSetVarD(int id, int slot, double val) {
+        rsnScriptSetVarD(mContext, id, slot, val);
+    }
+    native void rsnScriptSetVarV(int con, int id, int slot, byte[] val);
+    synchronized void nScriptSetVarV(int id, int slot, byte[] val) {
+        rsnScriptSetVarV(mContext, id, slot, val);
+    }
 
-    native int  nProgramVertexCreate(boolean texMat);
-    native int  nProgramVertexCreate2(String shader, int[] params);
+    native void rsnScriptCBegin(int con);
+    synchronized void nScriptCBegin() {
+        rsnScriptCBegin(mContext);
+    }
+    native void rsnScriptCSetScript(int con, byte[] script, int offset, int length);
+    synchronized void nScriptCSetScript(byte[] script, int offset, int length) {
+        rsnScriptCSetScript(mContext, script, offset, length);
+    }
+    native int  rsnScriptCCreate(int con);
+    synchronized int nScriptCCreate() {
+        return rsnScriptCCreate(mContext);
+    }
 
-    native void nLightBegin();
-    native void nLightSetIsMono(boolean isMono);
-    native void nLightSetIsLocal(boolean isLocal);
-    native int  nLightCreate();
-    native void nLightSetColor(int l, float r, float g, float b);
-    native void nLightSetPosition(int l, float x, float y, float z);
+    native void rsnSamplerBegin(int con);
+    synchronized void nSamplerBegin() {
+        rsnSamplerBegin(mContext);
+    }
+    native void rsnSamplerSet(int con, int param, int value);
+    synchronized void nSamplerSet(int param, int value) {
+        rsnSamplerSet(mContext, param, value);
+    }
+    native void rsnSamplerSet2(int con, int param, float value);
+    synchronized void nSamplerSet2(int param, float value) {
+        rsnSamplerSet2(mContext, param, value);
+    }
+    native int  rsnSamplerCreate(int con);
+    synchronized int nSamplerCreate() {
+        return rsnSamplerCreate(mContext);
+    }
 
-    native int  nSimpleMeshCreate(int batchID, int idxID, int[] vtxID, int prim);
-    native void nSimpleMeshBindVertex(int id, int alloc, int slot);
-    native void nSimpleMeshBindIndex(int id, int alloc);
+    native void rsnProgramStoreBegin(int con, int in, int out);
+    synchronized void nProgramStoreBegin(int in, int out) {
+        rsnProgramStoreBegin(mContext, in, out);
+    }
+    native void rsnProgramStoreDepthFunc(int con, int func);
+    synchronized void nProgramStoreDepthFunc(int func) {
+        rsnProgramStoreDepthFunc(mContext, func);
+    }
+    native void rsnProgramStoreDepthMask(int con, boolean enable);
+    synchronized void nProgramStoreDepthMask(boolean enable) {
+        rsnProgramStoreDepthMask(mContext, enable);
+    }
+    native void rsnProgramStoreColorMask(int con, boolean r, boolean g, boolean b, boolean a);
+    synchronized void nProgramStoreColorMask(boolean r, boolean g, boolean b, boolean a) {
+        rsnProgramStoreColorMask(mContext, r, g, b, a);
+    }
+    native void rsnProgramStoreBlendFunc(int con, int src, int dst);
+    synchronized void nProgramStoreBlendFunc(int src, int dst) {
+        rsnProgramStoreBlendFunc(mContext, src, dst);
+    }
+    native void rsnProgramStoreDither(int con, boolean enable);
+    synchronized void nProgramStoreDither(boolean enable) {
+        rsnProgramStoreDither(mContext, enable);
+    }
+    native int  rsnProgramStoreCreate(int con);
+    synchronized int nProgramStoreCreate() {
+        return rsnProgramStoreCreate(mContext);
+    }
 
-    native void nAnimationBegin(int attribCount, int keyframeCount);
-    native void nAnimationAdd(float time, float[] attribs);
-    native int  nAnimationCreate();
+    native int  rsnProgramRasterCreate(int con, boolean pointSmooth, boolean lineSmooth, boolean pointSprite);
+    synchronized int nProgramRasterCreate(boolean pointSmooth, boolean lineSmooth, boolean pointSprite) {
+        return rsnProgramRasterCreate(mContext, pointSmooth, lineSmooth, pointSprite);
+    }
+    native void rsnProgramRasterSetLineWidth(int con, int pr, float v);
+    synchronized void nProgramRasterSetLineWidth(int pr, float v) {
+        rsnProgramRasterSetLineWidth(mContext, pr, v);
+    }
+    native void rsnProgramRasterSetCullMode(int con, int pr, int mode);
+    synchronized void nProgramRasterSetCullMode(int pr, int mode) {
+        rsnProgramRasterSetCullMode(mContext, pr, mode);
+    }
+
+    native void rsnProgramBindConstants(int con, int pv, int slot, int mID);
+    synchronized void nProgramBindConstants(int pv, int slot, int mID) {
+        rsnProgramBindConstants(mContext, pv, slot, mID);
+    }
+    native void rsnProgramBindTexture(int con, int vpf, int slot, int a);
+    synchronized void nProgramBindTexture(int vpf, int slot, int a) {
+        rsnProgramBindTexture(mContext, vpf, slot, a);
+    }
+    native void rsnProgramBindSampler(int con, int vpf, int slot, int s);
+    synchronized void nProgramBindSampler(int vpf, int slot, int s) {
+        rsnProgramBindSampler(mContext, vpf, slot, s);
+    }
+    native int  rsnProgramFragmentCreate(int con, String shader, int[] params);
+    synchronized int nProgramFragmentCreate(String shader, int[] params) {
+        return rsnProgramFragmentCreate(mContext, shader, params);
+    }
+    native int  rsnProgramVertexCreate(int con, String shader, int[] params);
+    synchronized int nProgramVertexCreate(String shader, int[] params) {
+        return rsnProgramVertexCreate(mContext, shader, params);
+    }
+
+    native int  rsnMeshCreate(int con, int vtxCount, int indexCount);
+    synchronized int nMeshCreate(int vtxCount, int indexCount) {
+        return rsnMeshCreate(mContext, vtxCount, indexCount);
+    }
+    native void rsnMeshBindVertex(int con, int id, int alloc, int slot);
+    synchronized void nMeshBindVertex(int id, int alloc, int slot) {
+        rsnMeshBindVertex(mContext, id, alloc, slot);
+    }
+    native void rsnMeshBindIndex(int con, int id, int alloc, int prim, int slot);
+    synchronized void nMeshBindIndex(int id, int alloc, int prim, int slot) {
+        rsnMeshBindIndex(mContext, id, alloc, prim, slot);
+    }
+    native int  rsnMeshGetVertexBufferCount(int con, int id);
+    synchronized int nMeshGetVertexBufferCount(int id) {
+        return rsnMeshGetVertexBufferCount(mContext, id);
+    }
+    native int  rsnMeshGetIndexCount(int con, int id);
+    synchronized int nMeshGetIndexCount(int id) {
+        return rsnMeshGetIndexCount(mContext, id);
+    }
+    native void rsnMeshGetVertices(int con, int id, int[] vtxIds, int vtxIdCount);
+    synchronized void nMeshGetVertices(int id, int[] vtxIds, int vtxIdCount) {
+        rsnMeshGetVertices(mContext, id, vtxIds, vtxIdCount);
+    }
+    native void rsnMeshGetIndices(int con, int id, int[] idxIds, int[] primitives, int vtxIdCount);
+    synchronized void nMeshGetIndices(int id, int[] idxIds, int[] primitives, int vtxIdCount) {
+        rsnMeshGetIndices(mContext, id, idxIds, primitives, vtxIdCount);
+    }
+
 
     protected int     mDev;
     protected int     mContext;
     @SuppressWarnings({"FieldCanBeLocal"})
     protected MessageThread mMessageThread;
 
-    Element mElement_USER_U8;
-    Element mElement_USER_I8;
-    Element mElement_USER_U16;
-    Element mElement_USER_I16;
-    Element mElement_USER_U32;
-    Element mElement_USER_I32;
-    Element mElement_USER_F32;
+    Element mElement_U8;
+    Element mElement_I8;
+    Element mElement_U16;
+    Element mElement_I16;
+    Element mElement_U32;
+    Element mElement_I32;
+    Element mElement_U64;
+    Element mElement_I64;
+    Element mElement_F32;
+    Element mElement_F64;
+    Element mElement_BOOLEAN;
+
+    Element mElement_ELEMENT;
+    Element mElement_TYPE;
+    Element mElement_ALLOCATION;
+    Element mElement_SAMPLER;
+    Element mElement_SCRIPT;
+    Element mElement_MESH;
+    Element mElement_PROGRAM_FRAGMENT;
+    Element mElement_PROGRAM_VERTEX;
+    Element mElement_PROGRAM_RASTER;
+    Element mElement_PROGRAM_STORE;
 
     Element mElement_A_8;
     Element mElement_RGB_565;
@@ -210,13 +539,38 @@
     Element mElement_RGBA_4444;
     Element mElement_RGBA_8888;
 
-    Element mElement_INDEX_16;
-    Element mElement_POSITION_2;
-    Element mElement_POSITION_3;
-    Element mElement_TEXTURE_2;
-    Element mElement_NORMAL_3;
-    Element mElement_COLOR_U8_4;
-    Element mElement_COLOR_F32_4;
+    Element mElement_FLOAT_2;
+    Element mElement_FLOAT_3;
+    Element mElement_FLOAT_4;
+    Element mElement_UCHAR_4;
+
+    Element mElement_MATRIX_4X4;
+    Element mElement_MATRIX_3X3;
+    Element mElement_MATRIX_2X2;
+
+    Sampler mSampler_CLAMP_NEAREST;
+    Sampler mSampler_CLAMP_LINEAR;
+    Sampler mSampler_CLAMP_LINEAR_MIP_LINEAR;
+    Sampler mSampler_WRAP_NEAREST;
+    Sampler mSampler_WRAP_LINEAR;
+    Sampler mSampler_WRAP_LINEAR_MIP_LINEAR;
+
+    ProgramStore mProgramStore_BLEND_NONE_DEPTH_TEST;
+    ProgramStore mProgramStore_BLEND_NONE_DEPTH_NO_DEPTH;
+    ProgramStore mProgramStore_BLEND_NONE_DEPTH_NO_TEST;
+    ProgramStore mProgramStore_BLEND_NONE_DEPTH_NO_WRITE;
+    ProgramStore mProgramStore_BLEND_ALPHA_DEPTH_TEST;
+    ProgramStore mProgramStore_BLEND_ALPHA_DEPTH_NO_DEPTH;
+    ProgramStore mProgramStore_BLEND_ALPHA_DEPTH_NO_TEST;
+    ProgramStore mProgramStore_BLEND_ALPHA_DEPTH_NO_WRITE;
+    ProgramStore mProgramStore_BLEND_ADD_DEPTH_TEST;
+    ProgramStore mProgramStore_BLEND_ADD_DEPTH_NO_DEPTH;
+    ProgramStore mProgramStore_BLEND_ADD_DEPTH_NO_TEST;
+    ProgramStore mProgramStore_BLEND_ADD_DEPTH_NO_WRITE;
+
+    ProgramRaster mProgramRaster_CULL_BACK;
+    ProgramRaster mProgramRaster_CULL_FRONT;
+    ProgramRaster mProgramRaster_CULL_NONE;
 
     ///////////////////////////////////////////////////////////////////////////////////
     //
@@ -241,7 +595,7 @@
 
     void validate() {
         if (mContext == 0) {
-            throw new IllegalStateException("Calling RS with no Context active.");
+            throw new RSInvalidStateException("Calling RS with no Context active.");
         }
     }
 
@@ -264,25 +618,32 @@
             // This function is a temporary solution.  The final solution will
             // used typed allocations where the message id is the type indicator.
             int[] rbuf = new int[16];
-            mRS.nContextInitToClient();
+            mRS.nContextInitToClient(mRS.mContext);
             while(mRun) {
-                int msg = mRS.nContextGetMessage(rbuf, true);
-                if (msg == 0) {
-                    // Should only happen during teardown.
-                    // But we want to avoid starving other threads during
-                    // teardown by yielding until the next line in the destructor
-                    // can execute to set mRun = false
-                    try {
-                        sleep(1, 0);
-                    } catch(InterruptedException e) {
+                rbuf[0] = 0;
+                int msg = mRS.nContextGetMessage(mRS.mContext, rbuf, true);
+                if ((msg == 0)) {
+                    // Can happen for two reasons
+                    if (rbuf[0] > 0 && mRun) {
+                        // 1: Buffer needs to be enlarged.
+                        rbuf = new int[rbuf[0] + 2];
+                    } else {
+                        // 2: teardown.
+                        // But we want to avoid starving other threads during
+                        // teardown by yielding until the next line in the destructor
+                        // can execute to set mRun = false
+                        try {
+                            sleep(1, 0);
+                        } catch(InterruptedException e) {
+                        }
                     }
+                    continue;
                 }
                 if(mRS.mMessageCallback != null) {
                     mRS.mMessageCallback.mData = rbuf;
                     mRS.mMessageCallback.mID = msg;
                     mRS.mMessageCallback.run();
                 }
-                //Log.d(LOG_TAG, "MessageThread msg " + msg + " v1 " + rbuf[0] + " v2 " + rbuf[1] + " v3 " +rbuf[2]);
             }
             Log.d(LOG_TAG, "MessageThread exiting.");
         }
@@ -307,12 +668,20 @@
         nContextDump(bits);
     }
 
+    public void finish() {
+        nContextFinish();
+    }
+
     public void destroy() {
         validate();
-        nContextDeinitToClient();
+        nContextDeinitToClient(mContext);
         mMessageThread.mRun = false;
+        try {
+            mMessageThread.join();
+        } catch(InterruptedException e) {
+        }
 
-        nContextDestroy(mContext);
+        nContextDestroy();
         mContext = 0;
 
         nDeviceDestroy(mDev);
@@ -335,3 +704,4 @@
 }
 
 
+
diff --git a/graphics/java/android/renderscript/RenderScriptGL.java b/graphics/java/android/renderscript/RenderScriptGL.java
index d1df23d..cace063 100644
--- a/graphics/java/android/renderscript/RenderScriptGL.java
+++ b/graphics/java/android/renderscript/RenderScriptGL.java
@@ -18,12 +18,14 @@
 
 import java.lang.reflect.Field;
 
+import android.graphics.PixelFormat;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.util.Config;
 import android.util.Log;
 import android.view.Surface;
-
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
 
 /**
  * @hide
@@ -34,16 +36,101 @@
     int mWidth;
     int mHeight;
 
+    public static class SurfaceConfig {
+        int mDepthMin       = 0;
+        int mDepthPref      = 0;
+        int mStencilMin     = 0;
+        int mStencilPref    = 0;
+        int mColorMin       = 8;
+        int mColorPref      = 8;
+        int mAlphaMin       = 0;
+        int mAlphaPref      = 0;
+        int mSamplesMin     = 1;
+        int mSamplesPref    = 1;
+        float mSamplesQ     = 1.f;
 
-    public RenderScriptGL(boolean useDepth, boolean forceSW) {
+        public SurfaceConfig() {
+        }
+
+        public SurfaceConfig(SurfaceConfig sc) {
+            mDepthMin = sc.mDepthMin;
+            mDepthPref = sc.mDepthPref;
+            mStencilMin = sc.mStencilMin;
+            mStencilPref = sc.mStencilPref;
+            mColorMin = sc.mColorMin;
+            mColorPref = sc.mColorPref;
+            mAlphaMin = sc.mAlphaMin;
+            mAlphaPref = sc.mAlphaPref;
+            mSamplesMin = sc.mSamplesMin;
+            mSamplesPref = sc.mSamplesPref;
+            mSamplesQ = sc.mSamplesQ;
+        }
+
+        private void validateRange(int umin, int upref, int rmin, int rmax) {
+            if (umin < rmin || umin > rmax) {
+                throw new RSIllegalArgumentException("Minimum value provided out of range.");
+            }
+            if (upref < umin) {
+                throw new RSIllegalArgumentException("Prefered must be >= Minimum.");
+            }
+        }
+
+        public void setColor(int minimum, int prefered) {
+            validateRange(minimum, prefered, 5, 8);
+            mColorMin = minimum;
+            mColorPref = prefered;
+        }
+        public void setAlpha(int minimum, int prefered) {
+            validateRange(minimum, prefered, 0, 8);
+            mAlphaMin = minimum;
+            mAlphaPref = prefered;
+        }
+        public void setDepth(int minimum, int prefered) {
+            validateRange(minimum, prefered, 0, 24);
+            mDepthMin = minimum;
+            mDepthPref = prefered;
+        }
+        public void setSamples(int minimum, int prefered, float Q) {
+            validateRange(minimum, prefered, 0, 24);
+            if (Q < 0.0f || Q > 1.0f) {
+                throw new RSIllegalArgumentException("Quality out of 0-1 range.");
+            }
+            mSamplesMin = minimum;
+            mSamplesPref = prefered;
+            mSamplesQ = Q;
+        }
+    };
+
+    SurfaceConfig mSurfaceConfig;
+
+    public void configureSurface(SurfaceHolder sh) {
+        if (mSurfaceConfig.mAlphaMin > 1) {
+            sh.setFormat(PixelFormat.RGBA_8888);
+        } else {
+            sh.setFormat(PixelFormat.RGBX_8888);
+        }
+    }
+
+    public void checkSurface(SurfaceHolder sh) {
+    }
+
+    public RenderScriptGL(SurfaceConfig sc) {
+        mSurfaceConfig = new SurfaceConfig(sc);
+
         mSurface = null;
         mWidth = 0;
         mHeight = 0;
         mDev = nDeviceCreate();
-        if(forceSW) {
-            nDeviceSetConfig(mDev, 0, 1);
+        mContext = nContextCreateGL(mDev, 0,
+                                    mSurfaceConfig.mColorMin, mSurfaceConfig.mColorPref,
+                                    mSurfaceConfig.mAlphaMin, mSurfaceConfig.mAlphaPref,
+                                    mSurfaceConfig.mDepthMin, mSurfaceConfig.mDepthPref,
+                                    mSurfaceConfig.mStencilMin, mSurfaceConfig.mStencilPref,
+                                    mSurfaceConfig.mSamplesMin, mSurfaceConfig.mSamplesPref,
+                                    mSurfaceConfig.mSamplesQ);
+        if (mContext == 0) {
+            throw new RSDriverException("Failed to create RS context.");
         }
-        mContext = nContextCreateGL(mDev, 0, useDepth);
         mMessageThread = new MessageThread(this);
         mMessageThread.start();
         Element.initPredefined(this);
@@ -57,6 +144,13 @@
         nContextSetSurface(w, h, mSurface);
     }
 
+    public int getHeight() {
+        return mHeight;
+    }
+
+    public int getWidth() {
+        return mWidth;
+    }
 
     void pause() {
         validate();
@@ -74,9 +168,9 @@
         nContextBindRootScript(safeID(s));
     }
 
-    public void contextBindProgramFragmentStore(ProgramStore p) {
+    public void contextBindProgramStore(ProgramStore p) {
         validate();
-        nContextBindProgramFragmentStore(safeID(p));
+        nContextBindProgramStore(safeID(p));
     }
 
     public void contextBindProgramFragment(ProgramFragment p) {
@@ -94,34 +188,6 @@
         nContextBindProgramVertex(safeID(p));
     }
 
-
-
-
-    //////////////////////////////////////////////////////////////////////////////////
-    // File
-
-    public class File extends BaseObj {
-        File(int id) {
-            super(RenderScriptGL.this);
-            mID = id;
-        }
-    }
-
-    public File fileOpen(String s) throws IllegalStateException, IllegalArgumentException
-    {
-        if(s.length() < 1) {
-            throw new IllegalArgumentException("fileOpen does not accept a zero length string.");
-        }
-
-        try {
-            byte[] bytes = s.getBytes("UTF-8");
-            int id = nFileOpen(bytes);
-            return new File(id);
-        } catch (java.io.UnsupportedEncodingException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
 }
 
 
diff --git a/graphics/java/android/renderscript/Sampler.java b/graphics/java/android/renderscript/Sampler.java
index 40ba722..b627207 100644
--- a/graphics/java/android/renderscript/Sampler.java
+++ b/graphics/java/android/renderscript/Sampler.java
@@ -47,10 +47,82 @@
     }
 
     Sampler(int id, RenderScript rs) {
-        super(rs);
-        mID = id;
+        super(id, rs);
     }
 
+    public static Sampler CLAMP_NEAREST(RenderScript rs) {
+        if(rs.mSampler_CLAMP_NEAREST == null) {
+            Builder b = new Builder(rs);
+            b.setMin(Value.NEAREST);
+            b.setMag(Value.NEAREST);
+            b.setWrapS(Value.CLAMP);
+            b.setWrapT(Value.CLAMP);
+            rs.mSampler_CLAMP_NEAREST = b.create();
+        }
+        return rs.mSampler_CLAMP_NEAREST;
+    }
+
+    public static Sampler CLAMP_LINEAR(RenderScript rs) {
+        if(rs.mSampler_CLAMP_LINEAR == null) {
+            Builder b = new Builder(rs);
+            b.setMin(Value.LINEAR);
+            b.setMag(Value.LINEAR);
+            b.setWrapS(Value.CLAMP);
+            b.setWrapT(Value.CLAMP);
+            rs.mSampler_CLAMP_LINEAR = b.create();
+        }
+        return rs.mSampler_CLAMP_LINEAR;
+    }
+
+    public static Sampler CLAMP_LINEAR_MIP_LINEAR(RenderScript rs) {
+        if(rs.mSampler_CLAMP_LINEAR_MIP_LINEAR == null) {
+            Builder b = new Builder(rs);
+            b.setMin(Value.LINEAR_MIP_LINEAR);
+            b.setMag(Value.LINEAR);
+            b.setWrapS(Value.CLAMP);
+            b.setWrapT(Value.CLAMP);
+            rs.mSampler_CLAMP_LINEAR_MIP_LINEAR = b.create();
+        }
+        return rs.mSampler_CLAMP_LINEAR_MIP_LINEAR;
+    }
+
+    public static Sampler WRAP_NEAREST(RenderScript rs) {
+        if(rs.mSampler_WRAP_NEAREST == null) {
+            Builder b = new Builder(rs);
+            b.setMin(Value.NEAREST);
+            b.setMag(Value.NEAREST);
+            b.setWrapS(Value.WRAP);
+            b.setWrapT(Value.WRAP);
+            rs.mSampler_WRAP_NEAREST = b.create();
+        }
+        return rs.mSampler_WRAP_NEAREST;
+    }
+
+    public static Sampler WRAP_LINEAR(RenderScript rs) {
+        if(rs.mSampler_WRAP_LINEAR == null) {
+            Builder b = new Builder(rs);
+            b.setMin(Value.LINEAR);
+            b.setMag(Value.LINEAR);
+            b.setWrapS(Value.WRAP);
+            b.setWrapT(Value.WRAP);
+            rs.mSampler_WRAP_LINEAR = b.create();
+        }
+        return rs.mSampler_WRAP_LINEAR;
+    }
+
+    public static Sampler WRAP_LINEAR_MIP_LINEAR(RenderScript rs) {
+        if(rs.mSampler_WRAP_LINEAR_MIP_LINEAR == null) {
+            Builder b = new Builder(rs);
+            b.setMin(Value.LINEAR_MIP_LINEAR);
+            b.setMag(Value.LINEAR);
+            b.setWrapS(Value.WRAP);
+            b.setWrapT(Value.WRAP);
+            rs.mSampler_WRAP_LINEAR_MIP_LINEAR = b.create();
+        }
+        return rs.mSampler_WRAP_LINEAR_MIP_LINEAR;
+    }
+
+
     public static class Builder {
         RenderScript mRS;
         Value mMin;
@@ -58,6 +130,7 @@
         Value mWrapS;
         Value mWrapT;
         Value mWrapR;
+        float mAniso;
 
         public Builder(RenderScript rs) {
             mRS = rs;
@@ -66,6 +139,7 @@
             mWrapS = Value.WRAP;
             mWrapT = Value.WRAP;
             mWrapR = Value.WRAP;
+            mAniso = 1.0f;
         }
 
         public void setMin(Value v) {
@@ -110,6 +184,14 @@
             }
         }
 
+        public void setAnisotropy(float v) {
+            if(v >= 0.0f) {
+                mAniso = v;
+            } else {
+                throw new IllegalArgumentException("Invalid value");
+            }
+        }
+
         static synchronized Sampler internalCreate(RenderScript rs, Builder b) {
             rs.nSamplerBegin();
             rs.nSamplerSet(0, b.mMin.mID);
@@ -117,6 +199,7 @@
             rs.nSamplerSet(2, b.mWrapS.mID);
             rs.nSamplerSet(3, b.mWrapT.mID);
             rs.nSamplerSet(4, b.mWrapR.mID);
+            rs.nSamplerSet2(5, b.mAniso);
             int id = rs.nSamplerCreate();
             return new Sampler(id, rs);
         }
diff --git a/graphics/java/android/renderscript/Script.java b/graphics/java/android/renderscript/Script.java
index 57ccfa3..430789a 100644
--- a/graphics/java/android/renderscript/Script.java
+++ b/graphics/java/android/renderscript/Script.java
@@ -42,29 +42,54 @@
         }
     }
 
+    protected void invoke(int slot) {
+        mRS.nScriptInvoke(mID, slot);
+    }
+
+    protected void invoke(int slot, FieldPacker v) {
+        if (v != null) {
+            mRS.nScriptInvokeV(mID, slot, v.getData());
+        } else {
+            mRS.nScriptInvoke(mID, slot);
+        }
+    }
+
+
     Script(int id, RenderScript rs) {
-        super(rs);
-        mID = id;
+        super(id, rs);
     }
 
     public void bindAllocation(Allocation va, int slot) {
         mRS.validate();
-        mRS.nScriptBindAllocation(mID, va.mID, slot);
+        if (va != null) {
+            mRS.nScriptBindAllocation(mID, va.mID, slot);
+        } else {
+            mRS.nScriptBindAllocation(mID, 0, slot);
+        }
     }
 
-    public void setClearColor(float r, float g, float b, float a) {
-        mRS.validate();
-        mRS.nScriptSetClearColor(mID, r, g, b, a);
+    public void setVar(int index, float v) {
+        mRS.nScriptSetVarF(mID, index, v);
     }
 
-    public void setClearDepth(float d) {
-        mRS.validate();
-        mRS.nScriptSetClearDepth(mID, d);
+    public void setVar(int index, double v) {
+        mRS.nScriptSetVarD(mID, index, v);
     }
 
-    public void setClearStencil(int stencil) {
-        mRS.validate();
-        mRS.nScriptSetClearStencil(mID, stencil);
+    public void setVar(int index, int v) {
+        mRS.nScriptSetVarI(mID, index, v);
+    }
+
+    public void setVar(int index, long v) {
+        mRS.nScriptSetVarJ(mID, index, v);
+    }
+
+    public void setVar(int index, boolean v) {
+        mRS.nScriptSetVarI(mID, index, v ? 1 : 0);
+    }
+
+    public void setVar(int index, FieldPacker v) {
+        mRS.nScriptSetVarV(mID, index, v.getData());
     }
 
     public void setTimeZone(String timeZone) {
@@ -78,72 +103,39 @@
 
     public static class Builder {
         RenderScript mRS;
-        boolean mIsRoot = false;
-        Type[] mTypes;
-        String[] mNames;
-        boolean[] mWritable;
-        int mInvokableCount = 0;
-        Invokable[] mInvokables;
 
         Builder(RenderScript rs) {
             mRS = rs;
-            mTypes = new Type[MAX_SLOT];
-            mNames = new String[MAX_SLOT];
-            mWritable = new boolean[MAX_SLOT];
-            mInvokables = new Invokable[MAX_SLOT];
         }
-
-        public void setType(Type t, int slot) {
-            mTypes[slot] = t;
-            mNames[slot] = null;
-        }
-
-        public void setType(Type t, String name, int slot) {
-            mTypes[slot] = t;
-            mNames[slot] = name;
-        }
-
-        public Invokable addInvokable(String func) {
-            Invokable i = new Invokable();
-            i.mName = func;
-            i.mRS = mRS;
-            i.mSlot = mInvokableCount;
-            mInvokables[mInvokableCount++] = i;
-            return i;
-        }
-
-        public void setType(boolean writable, int slot) {
-            mWritable[slot] = writable;
-        }
-
-        void transferCreate() {
-            mRS.nScriptSetRoot(mIsRoot);
-            for(int ct=0; ct < mTypes.length; ct++) {
-                if(mTypes[ct] != null) {
-                    mRS.nScriptSetType(mTypes[ct].mID, mWritable[ct], mNames[ct], ct);
-                }
-            }
-            for(int ct=0; ct < mInvokableCount; ct++) {
-                mRS.nScriptSetInvokable(mInvokables[ct].mName, ct);
-            }
-        }
-
-        void transferObject(Script s) {
-            s.mIsRoot = mIsRoot;
-            s.mTypes = mTypes;
-            s.mInvokables = new Invokable[mInvokableCount];
-            for(int ct=0; ct < mInvokableCount; ct++) {
-                s.mInvokables[ct] = mInvokables[ct];
-                s.mInvokables[ct].mScript = s;
-            }
-            s.mInvokables = null;
-        }
-
-        public void setRoot(boolean r) {
-            mIsRoot = r;
-        }
-
     }
 
+
+    public static class FieldBase {
+        protected Element mElement;
+        protected Allocation mAllocation;
+
+        protected void init(RenderScript rs, int dimx) {
+            mAllocation = Allocation.createSized(rs, mElement, dimx);
+        }
+
+        protected FieldBase() {
+        }
+
+        public Element getElement() {
+            return mElement;
+        }
+
+        public Type getType() {
+            return mAllocation.getType();
+        }
+
+        public Allocation getAllocation() {
+            return mAllocation;
+        }
+
+        //@Override
+        public void updateAllocation() {
+        }
+    }
 }
 
diff --git a/graphics/java/android/renderscript/ScriptC.java b/graphics/java/android/renderscript/ScriptC.java
index bb99e23..5215795 100644
--- a/graphics/java/android/renderscript/ScriptC.java
+++ b/graphics/java/android/renderscript/ScriptC.java
@@ -33,129 +33,48 @@
 public class ScriptC extends Script {
     private static final String TAG = "ScriptC";
 
-    ScriptC(int id, RenderScript rs) {
+    protected ScriptC(int id, RenderScript rs) {
         super(id, rs);
     }
 
-    public static class Builder extends Script.Builder {
-        byte[] mProgram;
-        int mProgramLength;
-        HashMap<String,Integer> mIntDefines = new HashMap();
-        HashMap<String,Float> mFloatDefines = new HashMap();
+    protected ScriptC(RenderScript rs, Resources resources, int resourceID) {
+        super(0, rs);
+        mID = internalCreate(rs, resources, resourceID);
+    }
 
-        public Builder(RenderScript rs) {
-            super(rs);
-        }
 
-        public void setScript(String s) {
+    private static synchronized int internalCreate(RenderScript rs, Resources resources, int resourceID) {
+        byte[] pgm;
+        int pgmLength;
+        InputStream is = resources.openRawResource(resourceID);
+        try {
             try {
-                mProgram = s.getBytes("UTF-8");
-                mProgramLength = mProgram.length;
-            } catch (java.io.UnsupportedEncodingException e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-        public void setScript(Resources resources, int id) {
-            InputStream is = resources.openRawResource(id);
-            try {
-                try {
-                    setScript(is);
-                } finally {
-                    is.close();
-                }
-            } catch(IOException e) {
-                throw new Resources.NotFoundException();
-            }
-        }
-
-        public void setScript(InputStream is) throws IOException {
-            byte[] buf = new byte[1024];
-            int currentPos = 0;
-            while(true) {
-                int bytesLeft = buf.length - currentPos;
-                if (bytesLeft == 0) {
-                    byte[] buf2 = new byte[buf.length * 2];
-                    System.arraycopy(buf, 0, buf2, 0, buf.length);
-                    buf = buf2;
-                    bytesLeft = buf.length - currentPos;
-                }
-                int bytesRead = is.read(buf, currentPos, bytesLeft);
-                if (bytesRead <= 0) {
-                    break;
-                }
-                currentPos += bytesRead;
-            }
-            mProgram = buf;
-            mProgramLength = currentPos;
-        }
-
-        static synchronized ScriptC internalCreate(Builder b) {
-            b.mRS.nScriptCBegin();
-            b.transferCreate();
-
-            for (Entry<String,Integer> e: b.mIntDefines.entrySet()) {
-                b.mRS.nScriptCAddDefineI32(e.getKey(), e.getValue().intValue());
-            }
-            for (Entry<String,Float> e: b.mFloatDefines.entrySet()) {
-                b.mRS.nScriptCAddDefineF(e.getKey(), e.getValue().floatValue());
-            }
-
-            b.mRS.nScriptCSetScript(b.mProgram, 0, b.mProgramLength);
-
-            int id = b.mRS.nScriptCCreate();
-            ScriptC obj = new ScriptC(id, b.mRS);
-            b.transferObject(obj);
-
-            return obj;
-        }
-
-        public void addDefine(String name, int value) {
-            mIntDefines.put(name, value);
-        }
-
-        public void addDefine(String name, float value) {
-            mFloatDefines.put(name, value);
-        }
-
-        /**
-         * Takes the all public static final fields for a class, and adds defines
-         * for them, using the name of the field as the name of the define.
-         */
-        public void addDefines(Class cl) {
-            addDefines(cl.getFields(), (Modifier.STATIC | Modifier.FINAL | Modifier.PUBLIC), null);
-        }
-
-        /**
-         * Takes the all public fields for an object, and adds defines
-         * for them, using the name of the field as the name of the define.
-         */
-        public void addDefines(Object o) {
-            addDefines(o.getClass().getFields(), Modifier.PUBLIC, o);
-        }
-
-        void addDefines(Field[] fields, int mask, Object o) {
-            for (Field f: fields) {
-                try {
-                    if ((f.getModifiers() & mask) == mask) {
-                        Class t = f.getType();
-                        if (t == int.class) {
-                            mIntDefines.put(f.getName(), f.getInt(o));
-                        }
-                        else if (t == float.class) {
-                            mFloatDefines.put(f.getName(), f.getFloat(o));
-                        }
+                pgm = new byte[1024];
+                pgmLength = 0;
+                while(true) {
+                    int bytesLeft = pgm.length - pgmLength;
+                    if (bytesLeft == 0) {
+                        byte[] buf2 = new byte[pgm.length * 2];
+                        System.arraycopy(pgm, 0, buf2, 0, pgm.length);
+                        pgm = buf2;
+                        bytesLeft = pgm.length - pgmLength;
                     }
-                } catch (IllegalAccessException ex) {
-                    // TODO: Do we want this log?
-                    Log.d(TAG, "addDefines skipping field " + f.getName());
+                    int bytesRead = is.read(pgm, pgmLength, bytesLeft);
+                    if (bytesRead <= 0) {
+                        break;
+                    }
+                    pgmLength += bytesRead;
                 }
+            } finally {
+                is.close();
             }
+        } catch(IOException e) {
+            throw new Resources.NotFoundException();
         }
 
-        public ScriptC create() {
-            return internalCreate(this);
-        }
+        rs.nScriptCBegin();
+        rs.nScriptCSetScript(pgm, 0, pgmLength);
+        return rs.nScriptCCreate();
     }
 }
 
diff --git a/graphics/java/android/renderscript/Short2.java b/graphics/java/android/renderscript/Short2.java
new file mode 100644
index 0000000..426801f
--- /dev/null
+++ b/graphics/java/android/renderscript/Short2.java
@@ -0,0 +1,37 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Short2 {
+    public Short2() {
+    }
+
+    public short x;
+    public short y;
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/Short3.java b/graphics/java/android/renderscript/Short3.java
new file mode 100644
index 0000000..7b9c305
--- /dev/null
+++ b/graphics/java/android/renderscript/Short3.java
@@ -0,0 +1,38 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Short3 {
+    public Short3() {
+    }
+
+    public short x;
+    public short y;
+    public short z;
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/Short4.java b/graphics/java/android/renderscript/Short4.java
new file mode 100644
index 0000000..9a474e2
--- /dev/null
+++ b/graphics/java/android/renderscript/Short4.java
@@ -0,0 +1,38 @@
+/*
+ * 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.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Short4 {
+    public Short4() {
+    }
+
+    public short x;
+    public short y;
+    public short z;
+    public short w;
+}
+
+
+
diff --git a/graphics/java/android/renderscript/SimpleMesh.java b/graphics/java/android/renderscript/SimpleMesh.java
deleted file mode 100644
index 4a217a9..0000000
--- a/graphics/java/android/renderscript/SimpleMesh.java
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.renderscript;
-
-import android.util.Config;
-import android.util.Log;
-
-/**
- * @hide
- *
- **/
-public class SimpleMesh extends BaseObj {
-    Type[] mVertexTypes;
-    Type mIndexType;
-    //Type mBatcheType;
-    Primitive mPrimitive;
-
-    SimpleMesh(int id, RenderScript rs) {
-        super(rs);
-        mID = id;
-    }
-
-    public void bindVertexAllocation(Allocation a, int slot) {
-        mRS.validate();
-        mRS.nSimpleMeshBindVertex(mID, a.mID, slot);
-    }
-
-    public void bindIndexAllocation(Allocation a) {
-        mRS.validate();
-        mRS.nSimpleMeshBindIndex(mID, a.mID);
-    }
-
-    public Allocation createVertexAllocation(int slot) {
-        mRS.validate();
-        return Allocation.createTyped(mRS, mVertexTypes[slot]);
-    }
-
-    public Allocation createIndexAllocation() {
-        mRS.validate();
-        return Allocation.createTyped(mRS, mIndexType);
-    }
-
-    public Type getVertexType(int slot) {
-        return mVertexTypes[slot];
-    }
-
-    public Type getIndexType() {
-        return mIndexType;
-    }
-
-    public static class Builder {
-        RenderScript mRS;
-
-        class Entry {
-            Type t;
-            Element e;
-            int size;
-        }
-
-        int mVertexTypeCount;
-        Entry[] mVertexTypes;
-        Entry mIndexType;
-        //Entry mBatchType;
-        Primitive mPrimitive;
-
-
-        public Builder(RenderScript rs) {
-            mRS = rs;
-            mVertexTypeCount = 0;
-            mVertexTypes = new Entry[16];
-            mIndexType = new Entry();
-        }
-
-        public int addVertexType(Type t) throws IllegalStateException {
-            if (mVertexTypeCount >= mVertexTypes.length) {
-                throw new IllegalStateException("Max vertex types exceeded.");
-            }
-
-            int addedIndex = mVertexTypeCount;
-            mVertexTypes[mVertexTypeCount] = new Entry();
-            mVertexTypes[mVertexTypeCount].t = t;
-            mVertexTypeCount++;
-            return addedIndex;
-        }
-
-        public int addVertexType(Element e, int size) throws IllegalStateException {
-            if (mVertexTypeCount >= mVertexTypes.length) {
-                throw new IllegalStateException("Max vertex types exceeded.");
-            }
-
-            int addedIndex = mVertexTypeCount;
-            mVertexTypes[mVertexTypeCount] = new Entry();
-            mVertexTypes[mVertexTypeCount].e = e;
-            mVertexTypes[mVertexTypeCount].size = size;
-            mVertexTypeCount++;
-            return addedIndex;
-        }
-
-        public void setIndexType(Type t) {
-            mIndexType.t = t;
-            mIndexType.e = null;
-            mIndexType.size = 0;
-        }
-
-        public void setIndexType(Element e, int size) {
-            mIndexType.t = null;
-            mIndexType.e = e;
-            mIndexType.size = size;
-        }
-
-        public void setPrimitive(Primitive p) {
-            mPrimitive = p;
-        }
-
-
-        Type newType(Element e, int size) {
-            Type.Builder tb = new Type.Builder(mRS, e);
-            tb.add(Dimension.X, size);
-            return tb.create();
-        }
-
-        static synchronized SimpleMesh internalCreate(RenderScript rs, Builder b) {
-            Type[] toDestroy = new Type[18];
-            int toDestroyCount = 0;
-
-            int indexID = 0;
-            if (b.mIndexType.t != null) {
-                indexID = b.mIndexType.t.mID;
-            } else if (b.mIndexType.size != 0) {
-                b.mIndexType.t = b.newType(b.mIndexType.e, b.mIndexType.size);
-                indexID = b.mIndexType.t.mID;
-                toDestroy[toDestroyCount++] = b.mIndexType.t;
-            }
-
-            int[] IDs = new int[b.mVertexTypeCount];
-            for(int ct=0; ct < b.mVertexTypeCount; ct++) {
-                if (b.mVertexTypes[ct].t != null) {
-                    IDs[ct] = b.mVertexTypes[ct].t.mID;
-                } else {
-                    b.mVertexTypes[ct].t = b.newType(b.mVertexTypes[ct].e, b.mVertexTypes[ct].size);
-                    IDs[ct] = b.mVertexTypes[ct].t.mID;
-                    toDestroy[toDestroyCount++] = b.mVertexTypes[ct].t;
-                }
-            }
-
-            int id = rs.nSimpleMeshCreate(0, indexID, IDs, b.mPrimitive.mID);
-            for(int ct=0; ct < toDestroyCount; ct++) {
-                toDestroy[ct].destroy();
-            }
-
-            return new SimpleMesh(id, rs);
-        }
-
-        public SimpleMesh create() {
-            mRS.validate();
-            SimpleMesh sm = internalCreate(mRS, this);
-            sm.mVertexTypes = new Type[mVertexTypeCount];
-            for(int ct=0; ct < mVertexTypeCount; ct++) {
-                sm.mVertexTypes[ct] = mVertexTypes[ct].t;
-            }
-            sm.mIndexType = mIndexType.t;
-            sm.mPrimitive = mPrimitive;
-            return sm;
-        }
-    }
-
-    public static class TriangleMeshBuilder {
-        float mVtxData[];
-        int mVtxCount;
-        short mIndexData[];
-        int mIndexCount;
-        RenderScript mRS;
-        Element mElement;
-
-        float mNX = 0;
-        float mNY = 0;
-        float mNZ = -1;
-        float mS0 = 0;
-        float mT0 = 0;
-        float mR = 1;
-        float mG = 1;
-        float mB = 1;
-        float mA = 1;
-
-        int mVtxSize;
-        int mFlags;
-
-        public static final int COLOR = 0x0001;
-        public static final int NORMAL = 0x0002;
-        public static final int TEXTURE_0 = 0x0100;
-
-        public TriangleMeshBuilder(RenderScript rs, int vtxSize, int flags) {
-            mRS = rs;
-            mVtxCount = 0;
-            mIndexCount = 0;
-            mVtxData = new float[128];
-            mIndexData = new short[128];
-            mVtxSize = vtxSize;
-            mFlags = flags;
-
-            if (vtxSize < 2 || vtxSize > 3) {
-                throw new IllegalArgumentException("Vertex size out of range.");
-            }
-        }
-
-        private void makeSpace(int count) {
-            if ((mVtxCount + count) >= mVtxData.length) {
-                float t[] = new float[mVtxData.length * 2];
-                System.arraycopy(mVtxData, 0, t, 0, mVtxData.length);
-                mVtxData = t;
-            }
-        }
-
-        private void latch() {
-            if ((mFlags & COLOR) != 0) {
-                makeSpace(4);
-                mVtxData[mVtxCount++] = mR;
-                mVtxData[mVtxCount++] = mG;
-                mVtxData[mVtxCount++] = mB;
-                mVtxData[mVtxCount++] = mA;
-            }
-            if ((mFlags & TEXTURE_0) != 0) {
-                makeSpace(2);
-                mVtxData[mVtxCount++] = mS0;
-                mVtxData[mVtxCount++] = mT0;
-            }
-            if ((mFlags & NORMAL) != 0) {
-                makeSpace(3);
-                mVtxData[mVtxCount++] = mNX;
-                mVtxData[mVtxCount++] = mNY;
-                mVtxData[mVtxCount++] = mNZ;
-            }
-        }
-
-        public void addVertex(float x, float y) {
-            if (mVtxSize != 2) {
-                throw new IllegalStateException("add mistmatch with declared components.");
-            }
-            makeSpace(2);
-            mVtxData[mVtxCount++] = x;
-            mVtxData[mVtxCount++] = y;
-            latch();
-        }
-
-        public void addVertex(float x, float y, float z) {
-            if (mVtxSize != 3) {
-                throw new IllegalStateException("add mistmatch with declared components.");
-            }
-            makeSpace(3);
-            mVtxData[mVtxCount++] = x;
-            mVtxData[mVtxCount++] = y;
-            mVtxData[mVtxCount++] = z;
-            latch();
-        }
-
-        public void setTexture(float s, float t) {
-            if ((mFlags & TEXTURE_0) == 0) {
-                throw new IllegalStateException("add mistmatch with declared components.");
-            }
-            mS0 = s;
-            mT0 = t;
-        }
-
-        public void setNormal(float x, float y, float z) {
-            if ((mFlags & NORMAL) == 0) {
-                throw new IllegalStateException("add mistmatch with declared components.");
-            }
-            mNX = x;
-            mNY = y;
-            mNZ = z;
-        }
-
-        public void setColor(float r, float g, float b, float a) {
-            if ((mFlags & COLOR) == 0) {
-                throw new IllegalStateException("add mistmatch with declared components.");
-            }
-            mR = r;
-            mG = g;
-            mB = b;
-            mA = a;
-        }
-
-        public void addTriangle(int idx1, int idx2, int idx3) {
-            if((idx1 >= mVtxCount) || (idx1 < 0) ||
-               (idx2 >= mVtxCount) || (idx2 < 0) ||
-               (idx3 >= mVtxCount) || (idx3 < 0)) {
-               throw new IllegalStateException("Index provided greater than vertex count.");
-            }
-            if ((mIndexCount + 3) >= mIndexData.length) {
-                short t[] = new short[mIndexData.length * 2];
-                System.arraycopy(mIndexData, 0, t, 0, mIndexData.length);
-                mIndexData = t;
-            }
-            mIndexData[mIndexCount++] = (short)idx1;
-            mIndexData[mIndexCount++] = (short)idx2;
-            mIndexData[mIndexCount++] = (short)idx3;
-        }
-
-        public SimpleMesh create() {
-            Element.Builder b = new Element.Builder(mRS);
-            int floatCount = mVtxSize;
-            b.add(Element.createAttrib(mRS,
-                                       Element.DataType.FLOAT_32,
-                                       Element.DataKind.POSITION,
-                                       mVtxSize), "position");
-            if ((mFlags & COLOR) != 0) {
-                floatCount += 4;
-                b.add(Element.createAttrib(mRS,
-                                           Element.DataType.FLOAT_32,
-                                           Element.DataKind.COLOR,
-                                           4), "color");
-            }
-            if ((mFlags & TEXTURE_0) != 0) {
-                floatCount += 2;
-                b.add(Element.createAttrib(mRS,
-                                           Element.DataType.FLOAT_32,
-                                           Element.DataKind.TEXTURE,
-                                           2), "texture");
-            }
-            if ((mFlags & NORMAL) != 0) {
-                floatCount += 3;
-                b.add(Element.createAttrib(mRS,
-                                           Element.DataType.FLOAT_32,
-                                           Element.DataKind.NORMAL,
-                                           3), "normal");
-            }
-            mElement = b.create();
-
-            Builder smb = new Builder(mRS);
-            smb.addVertexType(mElement, mVtxCount / floatCount);
-            smb.setIndexType(Element.createIndex(mRS), mIndexCount);
-            smb.setPrimitive(Primitive.TRIANGLE);
-            SimpleMesh sm = smb.create();
-
-            Allocation vertexAlloc = sm.createVertexAllocation(0);
-            Allocation indexAlloc = sm.createIndexAllocation();
-            sm.bindVertexAllocation(vertexAlloc, 0);
-            sm.bindIndexAllocation(indexAlloc);
-
-            vertexAlloc.data(mVtxData);
-            vertexAlloc.uploadToBufferObject();
-
-            indexAlloc.data(mIndexData);
-            indexAlloc.uploadToBufferObject();
-
-            return sm;
-        }
-    }
-}
-
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index 62d3867..0d65737 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -16,7 +16,9 @@
 
 package android.renderscript;
 
+
 import java.lang.reflect.Field;
+import android.util.Log;
 
 /**
  * @hide
@@ -31,9 +33,6 @@
     int mElementCount;
     Element mElement;
 
-    private int mNativeCache;
-    Class mJavaClass;
-
     public Element getElement() {
         return mElement;
     }
@@ -95,67 +94,38 @@
 
 
     Type(int id, RenderScript rs) {
-        super(rs);
-        mID = id;
-        mNativeCache = 0;
+        super(id, rs);
     }
 
     protected void finalize() throws Throwable {
-        if(mNativeCache != 0) {
-            mRS.nTypeFinalDestroy(this);
-            mNativeCache = 0;
-        }
         super.finalize();
     }
 
-    public static Type createFromClass(RenderScript rs, Class c, int size) {
-        Element e = Element.createFromClass(rs, c);
-        Builder b = new Builder(rs, e);
-        b.add(Dimension.X, size);
-        Type t = b.create();
-        e.destroy();
+    @Override
+    void updateFromNative() {
+        // We have 6 integer to obtain mDimX; mDimY; mDimZ;
+        // mDimLOD; mDimFaces; mElement;
+        int[] dataBuffer = new int[6];
+        mRS.nTypeGetNativeData(mID, dataBuffer);
 
-        // native fields
-        {
-            Field[] fields = c.getFields();
-            int[] arTypes = new int[fields.length];
-            int[] arBits = new int[fields.length];
+        mDimX = dataBuffer[0];
+        mDimY = dataBuffer[1];
+        mDimZ = dataBuffer[2];
+        mDimLOD = dataBuffer[3] == 1 ? true : false;
+        mDimFaces = dataBuffer[4] == 1 ? true : false;
 
-            for(int ct=0; ct < fields.length; ct++) {
-                Field f = fields[ct];
-                Class fc = f.getType();
-                if(fc == int.class) {
-                    arTypes[ct] = Element.DataType.SIGNED_32.mID;
-                    arBits[ct] = 32;
-                } else if(fc == short.class) {
-                    arTypes[ct] = Element.DataType.SIGNED_16.mID;
-                    arBits[ct] = 16;
-                } else if(fc == byte.class) {
-                    arTypes[ct] = Element.DataType.SIGNED_8.mID;
-                    arBits[ct] = 8;
-                } else if(fc == float.class) {
-                    arTypes[ct] = Element.DataType.FLOAT_32.mID;
-                    arBits[ct] = 32;
-                } else {
-                    throw new IllegalArgumentException("Unkown field type");
-                }
-            }
-            rs.nTypeSetupFields(t, arTypes, arBits, fields);
+        int elementID = dataBuffer[5];
+        if(elementID != 0) {
+            mElement = new Element(elementID, mRS);
+            mElement.updateFromNative();
         }
-        t.mJavaClass = c;
-        return t;
+        calcElementCount();
     }
 
-    public static Type createFromClass(RenderScript rs, Class c, int size, String scriptName) {
-        Type t = createFromClass(rs, c, size);
-        t.setName(scriptName);
-        return t;
-    }
-
-
     public static class Builder {
         RenderScript mRS;
-        Entry[] mEntries;
+        Dimension[] mDimensions;
+        int[] mDimensionValues;
         int mEntryCount;
         Element mElement;
 
@@ -166,58 +136,58 @@
 
         public Builder(RenderScript rs, Element e) {
             if(e.mID == 0) {
-                throw new IllegalArgumentException("Invalid element.");
+                throw new RSIllegalArgumentException("Invalid element.");
             }
 
             mRS = rs;
-            mEntries = new Entry[4];
+            mDimensions = new Dimension[4];
+            mDimensionValues = new int[4];
             mElement = e;
         }
 
         public void add(Dimension d, int value) {
             if(value < 1) {
-                throw new IllegalArgumentException("Values of less than 1 for Dimensions are not valid.");
+                throw new RSIllegalArgumentException("Values of less than 1 for Dimensions are not valid.");
             }
-            if(mEntries.length >= mEntryCount) {
-                Entry[] en = new Entry[mEntryCount + 8];
-                System.arraycopy(mEntries, 0, en, 0, mEntries.length);
-                mEntries = en;
+            if(mDimensions.length >= mEntryCount) {
+                Dimension[] dn = new Dimension[mEntryCount + 8];
+                System.arraycopy(mDimensions, 0, dn, 0, mEntryCount);
+                mDimensions = dn;
+
+                int[] in = new int[mEntryCount + 8];
+                System.arraycopy(mDimensionValues, 0, in, 0, mEntryCount);
+                mDimensionValues = in;
             }
-            mEntries[mEntryCount] = new Entry();
-            mEntries[mEntryCount].mDim = d;
-            mEntries[mEntryCount].mValue = value;
+            mDimensions[mEntryCount] = d;
+            mDimensionValues[mEntryCount] = value;
             mEntryCount++;
         }
 
-        static synchronized Type internalCreate(RenderScript rs, Builder b) {
-            rs.nTypeBegin(b.mElement.mID);
-            for (int ct=0; ct < b.mEntryCount; ct++) {
-                Entry en = b.mEntries[ct];
-                rs.nTypeAdd(en.mDim.mID, en.mValue);
-            }
-            int id = rs.nTypeCreate();
-            return new Type(id, rs);
-        }
-
         public Type create() {
-            Type t = internalCreate(mRS, this);
+            int dims[] = new int[mEntryCount];
+            for (int ct=0; ct < mEntryCount; ct++) {
+                dims[ct] = mDimensions[ct].mID;
+            }
+
+            int id = mRS.nTypeCreate(mElement.getID(), dims, mDimensionValues);
+            Type t = new Type(id, mRS);
             t.mElement = mElement;
 
             for(int ct=0; ct < mEntryCount; ct++) {
-                if(mEntries[ct].mDim == Dimension.X) {
-                    t.mDimX = mEntries[ct].mValue;
+                if(mDimensions[ct] == Dimension.X) {
+                    t.mDimX = mDimensionValues[ct];
                 }
-                if(mEntries[ct].mDim == Dimension.Y) {
-                    t.mDimY = mEntries[ct].mValue;
+                if(mDimensions[ct] == Dimension.Y) {
+                    t.mDimY = mDimensionValues[ct];
                 }
-                if(mEntries[ct].mDim == Dimension.Z) {
-                    t.mDimZ = mEntries[ct].mValue;
+                if(mDimensions[ct] == Dimension.Z) {
+                    t.mDimZ = mDimensionValues[ct];
                 }
-                if(mEntries[ct].mDim == Dimension.LOD) {
-                    t.mDimLOD = mEntries[ct].mValue != 0;
+                if(mDimensions[ct] == Dimension.LOD) {
+                    t.mDimLOD = mDimensionValues[ct] != 0;
                 }
-                if(mEntries[ct].mDim == Dimension.FACE) {
-                    t.mDimFaces = mEntries[ct].mValue != 0;
+                if(mDimensions[ct] == Dimension.FACE) {
+                    t.mDimFaces = mDimensionValues[ct] != 0;
                 }
             }
             t.calcElementCount();
diff --git a/graphics/java/android/renderscript/Vector2f.java b/graphics/java/android/renderscript/Vector2f.java
deleted file mode 100644
index 567d57fa..0000000
--- a/graphics/java/android/renderscript/Vector2f.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * @hide
- *
- **/
-public class Vector2f {
-    public Vector2f() {
-    }
-
-    public float x;
-    public float y;
-}
-
-
-
-
diff --git a/graphics/java/android/renderscript/Vector3f.java b/graphics/java/android/renderscript/Vector3f.java
deleted file mode 100644
index f2842f3..0000000
--- a/graphics/java/android/renderscript/Vector3f.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * @hide
- *
- **/
-public class Vector3f {
-    public Vector3f() {
-    }
-
-    public float x;
-    public float y;
-    public float z;
-}
-
-
-
-
diff --git a/graphics/java/android/renderscript/Vector4f.java b/graphics/java/android/renderscript/Vector4f.java
deleted file mode 100644
index fabd959..0000000
--- a/graphics/java/android/renderscript/Vector4f.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.renderscript;
-
-import java.lang.Math;
-import android.util.Log;
-
-
-/**
- * @hide
- *
- **/
-public class Vector4f {
-    public Vector4f() {
-    }
-
-    public float x;
-    public float y;
-    public float z;
-    public float w;
-}
-
-
-
diff --git a/graphics/jni/Android.mk b/graphics/jni/Android.mk
index 8476be1..4c4a128 100644
--- a/graphics/jni/Android.mk
+++ b/graphics/jni/Android.mk
@@ -13,14 +13,13 @@
 
 LOCAL_SHARED_LIBRARIES := \
         libandroid_runtime \
-        libacc \
         libnativehelper \
         libRS \
         libcutils \
         libskia \
         libutils \
         libui \
-        libsurfaceflinger_client 
+        libsurfaceflinger_client
 
 LOCAL_STATIC_LIBRARIES :=
 
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 45cc72e..1cc6dc1 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -37,6 +37,7 @@
 #include "jni.h"
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_view_Surface.h"
 
 #include <RenderScript.h>
 #include <RenderScriptEnv.h>
@@ -69,9 +70,6 @@
 
     jclass bitmapClass = _env->FindClass("android/graphics/Bitmap");
     gNativeBitmapID = _env->GetFieldID(bitmapClass, "mNativeBitmap", "I");
-
-    jclass typeClass = _env->FindClass("android/renderscript/Type");
-    gTypeNativeCache = _env->GetFieldID(typeClass, "mNativeCache", "I");
 }
 
 static void nInitElements(JNIEnv *_env, jobject _this, jint a8, jint rgba4444, jint rgba8888, jint rgb565)
@@ -85,48 +83,41 @@
 // ---------------------------------------------------------------------------
 
 static void
-nAssignName(JNIEnv *_env, jobject _this, jint obj, jbyteArray str)
+nContextFinish(JNIEnv *_env, jobject _this, RsContext con)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nAssignName, con(%p), obj(%p)", con, (void *)obj);
+    LOG_API("nContextFinish, con(%p)", con);
+    rsContextFinish(con);
+}
 
+static void
+nAssignName(JNIEnv *_env, jobject _this, RsContext con, jint obj, jbyteArray str)
+{
+    LOG_API("nAssignName, con(%p), obj(%p)", con, (void *)obj);
     jint len = _env->GetArrayLength(str);
     jbyte * cptr = (jbyte *) _env->GetPrimitiveArrayCritical(str, 0);
     rsAssignName(con, (void *)obj, (const char *)cptr, len);
     _env->ReleasePrimitiveArrayCritical(str, cptr, JNI_ABORT);
 }
 
-static void
-nObjDestroy(JNIEnv *_env, jobject _this, jint obj)
+static jstring
+nGetName(JNIEnv *_env, jobject _this, RsContext con, jint obj)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nGetName, con(%p), obj(%p)", con, (void *)obj);
+    const char *name = NULL;
+    rsaGetName(con, (void *)obj, &name);
+    if(name == NULL || strlen(name) == 0) {
+        return NULL;
+    }
+    return _env->NewStringUTF(name);
+}
+
+static void
+nObjDestroy(JNIEnv *_env, jobject _this, RsContext con, jint obj)
+{
     LOG_API("nObjDestroy, con(%p) obj(%p)", con, (void *)obj);
     rsObjDestroy(con, (void *)obj);
 }
 
-static void
-nObjDestroyOOB(JNIEnv *_env, jobject _this, jint obj)
-{
-    // This function only differs from nObjDestroy in that it calls the
-    // special Out Of Band version of ObjDestroy which is thread safe.
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nObjDestroyOOB, con(%p) obj(%p)", con, (void *)obj);
-    rsObjDestroyOOB(con, (void *)obj);
-}
-
-static jint
-nFileOpen(JNIEnv *_env, jobject _this, jbyteArray str)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nFileOpen, con(%p)", con);
-
-    jint len = _env->GetArrayLength(str);
-    jbyte * cptr = (jbyte *) _env->GetPrimitiveArrayCritical(str, 0);
-    jint ret = (jint)rsFileOpen(con, (const char *)cptr, len);
-    _env->ReleasePrimitiveArrayCritical(str, cptr, JNI_ABORT);
-    return ret;
-}
-
 // ---------------------------------------------------------------------------
 
 static jint
@@ -158,16 +149,31 @@
 }
 
 static jint
-nContextCreateGL(JNIEnv *_env, jobject _this, jint dev, jint ver, jboolean useDepth)
+nContextCreateGL(JNIEnv *_env, jobject _this, jint dev, jint ver,
+                 int colorMin, int colorPref,
+                 int alphaMin, int alphaPref,
+                 int depthMin, int depthPref,
+                 int stencilMin, int stencilPref,
+                 int samplesMin, int samplesPref, float samplesQ)
 {
+    RsSurfaceConfig sc;
+    sc.alphaMin = alphaMin;
+    sc.alphaPref = alphaPref;
+    sc.colorMin = colorMin;
+    sc.colorPref = colorPref;
+    sc.depthMin = depthMin;
+    sc.depthPref = depthPref;
+    sc.samplesMin = samplesMin;
+    sc.samplesPref = samplesPref;
+    sc.samplesQ = samplesQ;
+
     LOG_API("nContextCreateGL");
-    return (jint)rsContextCreateGL((RsDevice)dev, ver, useDepth);
+    return (jint)rsContextCreateGL((RsDevice)dev, ver, sc);
 }
 
 static void
-nContextSetPriority(JNIEnv *_env, jobject _this, jint p)
+nContextSetPriority(JNIEnv *_env, jobject _this, RsContext con, jint p)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("ContextSetPriority, con(%p), priority(%i)", con, p);
     rsContextSetPriority(con, p);
 }
@@ -175,101 +181,92 @@
 
 
 static void
-nContextSetSurface(JNIEnv *_env, jobject _this, jint width, jint height, jobject wnd)
+nContextSetSurface(JNIEnv *_env, jobject _this, RsContext con, jint width, jint height, jobject wnd)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nContextSetSurface, con(%p), width(%i), height(%i), surface(%p)", con, width, height, (Surface *)wnd);
 
     Surface * window = NULL;
     if (wnd == NULL) {
 
     } else {
-        jclass surface_class = _env->FindClass("android/view/Surface");
-        jfieldID surfaceFieldID = _env->GetFieldID(surface_class, ANDROID_VIEW_SURFACE_JNI_ID, "I");
-        window = (Surface*)_env->GetIntField(wnd, surfaceFieldID);
+        window = (Surface*) android_Surface_getNativeWindow(_env, wnd).get();
     }
 
     rsContextSetSurface(con, width, height, window);
 }
 
 static void
-nContextDestroy(JNIEnv *_env, jobject _this, jint con)
+nContextDestroy(JNIEnv *_env, jobject _this, RsContext con)
 {
-    LOG_API("nContextDestroy, con(%p)", (RsContext)con);
-    rsContextDestroy((RsContext)con);
+    LOG_API("nContextDestroy, con(%p)", con);
+    rsContextDestroy(con);
 }
 
 static void
-nContextDump(JNIEnv *_env, jobject _this, jint bits)
+nContextDump(JNIEnv *_env, jobject _this, RsContext con, jint bits)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nContextDump, con(%p)  bits(%i)", (RsContext)con, bits);
     rsContextDump((RsContext)con, bits);
 }
 
 static void
-nContextPause(JNIEnv *_env, jobject _this)
+nContextPause(JNIEnv *_env, jobject _this, RsContext con)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nContextPause, con(%p)", con);
     rsContextPause(con);
 }
 
 static void
-nContextResume(JNIEnv *_env, jobject _this)
+nContextResume(JNIEnv *_env, jobject _this, RsContext con)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nContextResume, con(%p)", con);
     rsContextResume(con);
 }
 
 static jint
-nContextGetMessage(JNIEnv *_env, jobject _this, jintArray data, jboolean wait)
+nContextGetMessage(JNIEnv *_env, jobject _this, RsContext con, jintArray data, jboolean wait)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nContextGetMessage, con(%p), len(%i)", con, len);
     jint *ptr = _env->GetIntArrayElements(data, NULL);
     size_t receiveLen;
     int id = rsContextGetMessage(con, ptr, &receiveLen, len * 4, wait);
     if (!id && receiveLen) {
-        LOGE("message receive buffer too small.  %i", receiveLen);
+        LOGV("message receive buffer too small.  %i", receiveLen);
+        *ptr = (jint)receiveLen;
     }
     _env->ReleaseIntArrayElements(data, ptr, 0);
     return id;
 }
 
-static void nContextInitToClient(JNIEnv *_env, jobject _this)
+static void nContextInitToClient(JNIEnv *_env, jobject _this, RsContext con)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nContextInitToClient, con(%p)", con);
     rsContextInitToClient(con);
 }
 
-static void nContextDeinitToClient(JNIEnv *_env, jobject _this)
+static void nContextDeinitToClient(JNIEnv *_env, jobject _this, RsContext con)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nContextDeinitToClient, con(%p)", con);
     rsContextDeinitToClient(con);
 }
 
 
 static jint
-nElementCreate(JNIEnv *_env, jobject _this, jint type, jint kind, jboolean norm, jint size)
+nElementCreate(JNIEnv *_env, jobject _this, RsContext con, jint type, jint kind, jboolean norm, jint size)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nElementCreate, con(%p), type(%i), kind(%i), norm(%i), size(%i)", con, type, kind, norm, size);
     return (jint)rsElementCreate(con, (RsDataType)type, (RsDataKind)kind, norm, size);
 }
 
 static jint
-nElementCreate2(JNIEnv *_env, jobject _this, jintArray _ids, jobjectArray _names)
+nElementCreate2(JNIEnv *_env, jobject _this, RsContext con, jintArray _ids, jobjectArray _names, jintArray _arraySizes)
 {
     int fieldCount = _env->GetArrayLength(_ids);
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nElementCreate2, con(%p)", con);
 
     jint *ids = _env->GetIntArrayElements(_ids, NULL);
+    jint *arraySizes = _env->GetIntArrayElements(_arraySizes, NULL);
     const char ** nameArray = (const char **)calloc(fieldCount, sizeof(char *));
     size_t* sizeArray = (size_t*)calloc(fieldCount, sizeof(size_t));
 
@@ -278,183 +275,112 @@
         nameArray[ct] = _env->GetStringUTFChars(s, NULL);
         sizeArray[ct] = _env->GetStringUTFLength(s);
     }
-    jint id = (jint)rsElementCreate2(con, fieldCount, (RsElement *)ids, nameArray, sizeArray);
+    jint id = (jint)rsElementCreate2(con, fieldCount, (RsElement *)ids, nameArray, sizeArray, (const uint32_t *)arraySizes);
     for (int ct=0; ct < fieldCount; ct++) {
         jstring s = (jstring)_env->GetObjectArrayElement(_names, ct);
         _env->ReleaseStringUTFChars(s, nameArray[ct]);
     }
     _env->ReleaseIntArrayElements(_ids, ids, JNI_ABORT);
+    _env->ReleaseIntArrayElements(_arraySizes, arraySizes, JNI_ABORT);
     free(nameArray);
     free(sizeArray);
     return (jint)id;
 }
 
-// -----------------------------------
+static void
+nElementGetNativeData(JNIEnv *_env, jobject _this, RsContext con, jint id, jintArray _elementData)
+{
+    int dataSize = _env->GetArrayLength(_elementData);
+    LOG_API("nElementGetNativeData, con(%p)", con);
+
+    // we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
+    assert(dataSize == 5);
+
+    uint32_t elementData[5];
+    rsaElementGetNativeData(con, (RsElement)id, elementData, dataSize);
+
+    for(jint i = 0; i < dataSize; i ++) {
+        _env->SetIntArrayRegion(_elementData, i, 1, (const jint*)&elementData[i]);
+    }
+}
+
 
 static void
-nTypeBegin(JNIEnv *_env, jobject _this, jint eID)
+nElementGetSubElements(JNIEnv *_env, jobject _this, RsContext con, jint id, jintArray _IDs, jobjectArray _names)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nTypeBegin, con(%p) e(%p)", con, (RsElement)eID);
-    rsTypeBegin(con, (RsElement)eID);
-}
+    int dataSize = _env->GetArrayLength(_IDs);
+    LOG_API("nElementGetSubElements, con(%p)", con);
 
-static void
-nTypeAdd(JNIEnv *_env, jobject _this, jint dim, jint val)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nTypeAdd, con(%p) dim(%i), val(%i)", con, dim, val);
-    rsTypeAdd(con, (RsDimension)dim, val);
-}
+    uint32_t *ids = (uint32_t *)malloc((uint32_t)dataSize * sizeof(uint32_t));
+    const char **names = (const char **)malloc((uint32_t)dataSize * sizeof(const char *));
 
-static jint
-nTypeCreate(JNIEnv *_env, jobject _this)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nTypeCreate, con(%p)", con);
-    return (jint)rsTypeCreate(con);
-}
+    rsaElementGetSubElements(con, (RsElement)id, ids, names, (uint32_t)dataSize);
 
-static void * SF_LoadInt(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
-{
-    ((int32_t *)buffer)[0] = _env->GetIntField(_obj, _field);
-    return ((uint8_t *)buffer) + 4;
-}
-
-static void * SF_LoadShort(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
-{
-    ((int16_t *)buffer)[0] = _env->GetShortField(_obj, _field);
-    return ((uint8_t *)buffer) + 2;
-}
-
-static void * SF_LoadByte(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
-{
-    ((int8_t *)buffer)[0] = _env->GetByteField(_obj, _field);
-    return ((uint8_t *)buffer) + 1;
-}
-
-static void * SF_LoadFloat(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
-{
-    ((float *)buffer)[0] = _env->GetFloatField(_obj, _field);
-    return ((uint8_t *)buffer) + 4;
-}
-
-static void * SF_SaveInt(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
-{
-    _env->SetIntField(_obj, _field, ((int32_t *)buffer)[0]);
-    return ((uint8_t *)buffer) + 4;
-}
-
-static void * SF_SaveShort(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
-{
-    _env->SetShortField(_obj, _field, ((int16_t *)buffer)[0]);
-    return ((uint8_t *)buffer) + 2;
-}
-
-static void * SF_SaveByte(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
-{
-    _env->SetByteField(_obj, _field, ((int8_t *)buffer)[0]);
-    return ((uint8_t *)buffer) + 1;
-}
-
-static void * SF_SaveFloat(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
-{
-    _env->SetFloatField(_obj, _field, ((float *)buffer)[0]);
-    return ((uint8_t *)buffer) + 4;
-}
-
-struct TypeFieldCache {
-    jfieldID field;
-    int bits;
-    void * (*ptr)(JNIEnv *, jobject, jfieldID, void *buffer);
-    void * (*readPtr)(JNIEnv *, jobject, jfieldID, void *buffer);
-};
-
-struct TypeCache {
-    int fieldCount;
-    int size;
-    TypeFieldCache fields[1];
-};
-
-//{"nTypeFinalDestroy",              "(Landroid/renderscript/Type;)V",       (void*)nTypeFinalDestroy },
-static void
-nTypeFinalDestroy(JNIEnv *_env, jobject _this, jobject _type)
-{
-    TypeCache *tc = (TypeCache *)_env->GetIntField(_type, gTypeNativeCache);
-    free(tc);
-}
-
-// native void nTypeSetupFields(Type t, int[] types, int[] bits, Field[] IDs);
-static void
-nTypeSetupFields(JNIEnv *_env, jobject _this, jobject _type, jintArray _types, jintArray _bits, jobjectArray _IDs)
-{
-    int fieldCount = _env->GetArrayLength(_types);
-    size_t structSize = sizeof(TypeCache) + (sizeof(TypeFieldCache) * (fieldCount-1));
-    TypeCache *tc = (TypeCache *)malloc(structSize);
-    memset(tc, 0, structSize);
-
-    TypeFieldCache *tfc = &tc->fields[0];
-    tc->fieldCount = fieldCount;
-    _env->SetIntField(_type, gTypeNativeCache, (jint)tc);
-
-    jint *fType = _env->GetIntArrayElements(_types, NULL);
-    jint *fBits = _env->GetIntArrayElements(_bits, NULL);
-    for (int ct=0; ct < fieldCount; ct++) {
-        jobject field = _env->GetObjectArrayElement(_IDs, ct);
-        tfc[ct].field = _env->FromReflectedField(field);
-        tfc[ct].bits = fBits[ct];
-
-        switch(fType[ct]) {
-        case RS_TYPE_FLOAT_32:
-            tfc[ct].ptr = SF_LoadFloat;
-            tfc[ct].readPtr = SF_SaveFloat;
-            break;
-        case RS_TYPE_UNSIGNED_32:
-        case RS_TYPE_SIGNED_32:
-            tfc[ct].ptr = SF_LoadInt;
-            tfc[ct].readPtr = SF_SaveInt;
-            break;
-        case RS_TYPE_UNSIGNED_16:
-        case RS_TYPE_SIGNED_16:
-            tfc[ct].ptr = SF_LoadShort;
-            tfc[ct].readPtr = SF_SaveShort;
-            break;
-        case RS_TYPE_UNSIGNED_8:
-        case RS_TYPE_SIGNED_8:
-            tfc[ct].ptr = SF_LoadByte;
-            tfc[ct].readPtr = SF_SaveByte;
-            break;
-        }
-        tc->size += 4;
+    for(jint i = 0; i < dataSize; i++) {
+        _env->SetObjectArrayElement(_names, i, _env->NewStringUTF(names[i]));
+        _env->SetIntArrayRegion(_IDs, i, 1, (const jint*)&ids[i]);
     }
 
-    _env->ReleaseIntArrayElements(_types, fType, JNI_ABORT);
-    _env->ReleaseIntArrayElements(_bits, fBits, JNI_ABORT);
+    free(ids);
+    free(names);
 }
 
+// -----------------------------------
+
+static int
+nTypeCreate(JNIEnv *_env, jobject _this, RsContext con, RsElement eid, jintArray _dims, jintArray _dimValues)
+{
+    int count = _env->GetArrayLength(_dims);
+    LOG_API("nTypeCreate, con(%p)", con);
+
+    jint *dimPtr = _env->GetIntArrayElements(_dims, NULL);
+    jint *dimValPtr = _env->GetIntArrayElements(_dimValues, NULL);
+
+    jint id = (jint)rsaTypeCreate(con, (RsElement)eid, count,
+                                  (RsDimension *)dimPtr, (uint32_t *)dimValPtr);
+
+    _env->ReleaseIntArrayElements(_dims, dimPtr, JNI_ABORT);
+    _env->ReleaseIntArrayElements(_dimValues, dimValPtr, JNI_ABORT);
+    return (jint)id;
+}
+
+static void
+nTypeGetNativeData(JNIEnv *_env, jobject _this, RsContext con, jint id, jintArray _typeData)
+{
+    // We are packing 6 items: mDimX; mDimY; mDimZ;
+    // mDimLOD; mDimFaces; mElement; into typeData
+    int elementCount = _env->GetArrayLength(_typeData);
+
+    assert(elementCount == 6);
+    LOG_API("nTypeCreate, con(%p)", con);
+
+    uint32_t typeData[6];
+    rsaTypeGetNativeData(con, (RsType)id, typeData, 6);
+
+    for(jint i = 0; i < elementCount; i ++) {
+        _env->SetIntArrayRegion(_typeData, i, 1, (const jint*)&typeData[i]);
+    }
+}
 
 // -----------------------------------
 
 static jint
-nAllocationCreateTyped(JNIEnv *_env, jobject _this, jint e)
+nAllocationCreateTyped(JNIEnv *_env, jobject _this, RsContext con, jint e)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nAllocationCreateTyped, con(%p), e(%p)", con, (RsElement)e);
-    return (jint) rsAllocationCreateTyped(con, (RsElement)e);
+    return (jint) rsaAllocationCreateTyped(con, (RsElement)e);
 }
 
 static void
-nAllocationUploadToTexture(JNIEnv *_env, jobject _this, jint a, jboolean genMip, jint mip)
+nAllocationUploadToTexture(JNIEnv *_env, jobject _this, RsContext con, jint a, jboolean genMip, jint mip)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nAllocationUploadToTexture, con(%p), a(%p), genMip(%i), mip(%i)", con, (RsAllocation)a, genMip, mip);
     rsAllocationUploadToTexture(con, (RsAllocation)a, genMip, mip);
 }
 
 static void
-nAllocationUploadToBufferObject(JNIEnv *_env, jobject _this, jint a)
+nAllocationUploadToBufferObject(JNIEnv *_env, jobject _this, RsContext con, jint a)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nAllocationUploadToBufferObject, con(%p), a(%p)", con, (RsAllocation)a);
     rsAllocationUploadToBufferObject(con, (RsAllocation)a);
 }
@@ -480,9 +406,8 @@
 }
 
 static int
-nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jobject jbitmap)
+nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, RsContext con, jint dstFmt, jboolean genMips, jobject jbitmap)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     SkBitmap const * nativeBitmap =
             (SkBitmap const *)_env->GetIntField(jbitmap, gNativeBitmapID);
     const SkBitmap& bitmap(*nativeBitmap);
@@ -494,13 +419,30 @@
         const int w = bitmap.width();
         const int h = bitmap.height();
         const void* ptr = bitmap.getPixels();
-        jint id = (jint)rsAllocationCreateFromBitmap(con, w, h, (RsElement)dstFmt, e, genMips, ptr);
+        jint id = (jint)rsaAllocationCreateFromBitmap(con, w, h, (RsElement)dstFmt, e, genMips, ptr);
         bitmap.unlockPixels();
         return id;
     }
     return 0;
 }
 
+static void
+nAllocationUpdateFromBitmap(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jobject jbitmap)
+{
+    SkBitmap const * nativeBitmap =
+            (SkBitmap const *)_env->GetIntField(jbitmap, gNativeBitmapID);
+    const SkBitmap& bitmap(*nativeBitmap);
+    SkBitmap::Config config = bitmap.getConfig();
+
+    RsElement e = SkBitmapToPredefined(config);
+    if (e) {
+        bitmap.lockPixels();
+        const void* ptr = bitmap.getPixels();
+        rsAllocationUpdateFromBitmap(con, (RsAllocation)alloc, e, ptr);
+        bitmap.unlockPixels();
+    }
+}
+
 static void ReleaseBitmapCallback(void *bmp)
 {
     SkBitmap const * nativeBitmap = (SkBitmap const *)bmp;
@@ -508,9 +450,8 @@
 }
 
 static int
-nAllocationCreateBitmapRef(JNIEnv *_env, jobject _this, jint type, jobject jbitmap)
+nAllocationCreateBitmapRef(JNIEnv *_env, jobject _this, RsContext con, jint type, jobject jbitmap)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     SkBitmap * nativeBitmap =
             (SkBitmap *)_env->GetIntField(jbitmap, gNativeBitmapID);
 
@@ -522,10 +463,8 @@
 }
 
 static int
-nAllocationCreateFromAssetStream(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jint native_asset)
+nAllocationCreateFromAssetStream(JNIEnv *_env, jobject _this, RsContext con, jint dstFmt, jboolean genMips, jint native_asset)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-
     Asset* asset = reinterpret_cast<Asset*>(native_asset);
     SkBitmap bitmap;
     SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(),
@@ -540,41 +479,16 @@
         const int w = bitmap.width();
         const int h = bitmap.height();
         const void* ptr = bitmap.getPixels();
-        jint id = (jint)rsAllocationCreateFromBitmap(con, w, h, (RsElement)dstFmt, e, genMips, ptr);
+        jint id = (jint)rsaAllocationCreateFromBitmap(con, w, h, (RsElement)dstFmt, e, genMips, ptr);
         bitmap.unlockPixels();
         return id;
     }
     return 0;
 }
 
-static int
-nAllocationCreateFromBitmapBoxed(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jobject jbitmap)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    SkBitmap const * nativeBitmap =
-            (SkBitmap const *)_env->GetIntField(jbitmap, gNativeBitmapID);
-    const SkBitmap& bitmap(*nativeBitmap);
-    SkBitmap::Config config = bitmap.getConfig();
-
-    RsElement e = SkBitmapToPredefined(config);
-
-    if (e) {
-        bitmap.lockPixels();
-        const int w = bitmap.width();
-        const int h = bitmap.height();
-        const void* ptr = bitmap.getPixels();
-        jint id = (jint)rsAllocationCreateFromBitmapBoxed(con, w, h, (RsElement)dstFmt, e, genMips, ptr);
-        bitmap.unlockPixels();
-        return id;
-    }
-    return 0;
-}
-
-
 static void
-nAllocationSubData1D_i(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jintArray data, int sizeBytes)
+nAllocationSubData1D_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint count, jintArray data, int sizeBytes)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAllocation1DSubData_i, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes);
     jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -583,9 +497,8 @@
 }
 
 static void
-nAllocationSubData1D_s(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jshortArray data, int sizeBytes)
+nAllocationSubData1D_s(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint count, jshortArray data, int sizeBytes)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAllocation1DSubData_s, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes);
     jshort *ptr = _env->GetShortArrayElements(data, NULL);
@@ -594,9 +507,8 @@
 }
 
 static void
-nAllocationSubData1D_b(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jbyteArray data, int sizeBytes)
+nAllocationSubData1D_b(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint count, jbyteArray data, int sizeBytes)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAllocation1DSubData_b, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes);
     jbyte *ptr = _env->GetByteArrayElements(data, NULL);
@@ -605,9 +517,8 @@
 }
 
 static void
-nAllocationSubData1D_f(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jfloatArray data, int sizeBytes)
+nAllocationSubData1D_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint count, jfloatArray data, int sizeBytes)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAllocation1DSubData_f, con(%p), adapter(%p), offset(%i), count(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, count, len, sizeBytes);
     jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
@@ -616,9 +527,19 @@
 }
 
 static void
-nAllocationSubData2D_i(JNIEnv *_env, jobject _this, jint alloc, jint xoff, jint yoff, jint w, jint h, jintArray data, int sizeBytes)
+//    native void rsnAllocationSubElementData1D(int con, int id, int xoff, int compIdx, byte[] d, int sizeBytes);
+nAllocationSubElementData1D(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint offset, jint compIdx, jbyteArray data, int sizeBytes)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    jint len = _env->GetArrayLength(data);
+    LOG_API("nAllocationSubElementData1D, con(%p), alloc(%p), offset(%i), comp(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, compIdx, len, sizeBytes);
+    jbyte *ptr = _env->GetByteArrayElements(data, NULL);
+    rsAllocation1DSubElementData(con, (RsAllocation)alloc, offset, ptr, compIdx, sizeBytes);
+    _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationSubData2D_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint w, jint h, jintArray data, int sizeBytes)
+{
     jint len = _env->GetArrayLength(data);
     LOG_API("nAllocation2DSubData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, w, h, len);
     jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -627,9 +548,8 @@
 }
 
 static void
-nAllocationSubData2D_f(JNIEnv *_env, jobject _this, jint alloc, jint xoff, jint yoff, jint w, jint h, jfloatArray data, int sizeBytes)
+nAllocationSubData2D_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint xoff, jint yoff, jint w, jint h, jfloatArray data, int sizeBytes)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAllocation2DSubData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, w, h, len);
     jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
@@ -638,9 +558,8 @@
 }
 
 static void
-nAllocationRead_i(JNIEnv *_env, jobject _this, jint alloc, jintArray data)
+nAllocationRead_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jintArray data)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
     jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -649,9 +568,8 @@
 }
 
 static void
-nAllocationRead_f(JNIEnv *_env, jobject _this, jint alloc, jfloatArray data)
+nAllocationRead_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jfloatArray data)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAllocationRead_f, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
     jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
@@ -659,70 +577,103 @@
     _env->ReleaseFloatArrayElements(data, ptr, 0);
 }
 
-
-//{"nAllocationDataFromObject",      "(ILandroid/renderscript/Type;Ljava/lang/Object;)V",   (void*)nAllocationDataFromObject },
-static void
-nAllocationSubDataFromObject(JNIEnv *_env, jobject _this, jint alloc, jobject _type, jint offset, jobject _o)
+static jint
+nAllocationGetType(JNIEnv *_env, jobject _this, RsContext con, jint a)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nAllocationDataFromObject con(%p), alloc(%p)", con, (RsAllocation)alloc);
-
-    const TypeCache *tc = (TypeCache *)_env->GetIntField(_type, gTypeNativeCache);
-
-    void * bufAlloc = malloc(tc->size);
-    void * buf = bufAlloc;
-    for (int ct=0; ct < tc->fieldCount; ct++) {
-        const TypeFieldCache *tfc = &tc->fields[ct];
-        buf = tfc->ptr(_env, _o, tfc->field, buf);
-    }
-    rsAllocation1DSubData(con, (RsAllocation)alloc, offset, 1, bufAlloc, tc->size);
-    free(bufAlloc);
+    LOG_API("nAllocationGetType, con(%p), a(%p)", con, (RsAllocation)a);
+    return (jint) rsaAllocationGetType(con, (RsAllocation)a);
 }
 
 static void
-nAllocationSubReadFromObject(JNIEnv *_env, jobject _this, jint alloc, jobject _type, jint offset, jobject _o)
+nAllocationResize1D(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint dimX)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nAllocationReadFromObject con(%p), alloc(%p)", con, (RsAllocation)alloc);
+    LOG_API("nAllocationResize1D, con(%p), alloc(%p), sizeX(%i)", con, (RsAllocation)alloc, dimX);
+    rsAllocationResize1D(con, (RsAllocation)alloc, dimX);
+}
 
-    assert(offset == 0);
+static void
+nAllocationResize2D(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint dimX, jint dimY)
+{
+    LOG_API("nAllocationResize1D, con(%p), alloc(%p), sizeX(%i), sizeY(%i)", con, (RsAllocation)alloc, dimX, dimY);
+    rsAllocationResize2D(con, (RsAllocation)alloc, dimX, dimY);
+}
 
-    const TypeCache *tc = (TypeCache *)_env->GetIntField(_type, gTypeNativeCache);
+// -----------------------------------
 
-    void * bufAlloc = malloc(tc->size);
-    void * buf = bufAlloc;
-    rsAllocationRead(con, (RsAllocation)alloc, bufAlloc);
+static int
+nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, RsContext con, jint native_asset)
+{
+    LOGV("______nFileA3D %u", (uint32_t) native_asset);
 
-    for (int ct=0; ct < tc->fieldCount; ct++) {
-        const TypeFieldCache *tfc = &tc->fields[ct];
-        buf = tfc->readPtr(_env, _o, tfc->field, buf);
+    Asset* asset = reinterpret_cast<Asset*>(native_asset);
+
+    jint id = (jint)rsaFileA3DCreateFromAssetStream(con, asset->getBuffer(false), asset->getLength());
+    return id;
+}
+
+static int
+nFileA3DGetNumIndexEntries(JNIEnv *_env, jobject _this, RsContext con, jint fileA3D)
+{
+    int32_t numEntries = 0;
+    rsaFileA3DGetNumIndexEntries(con, &numEntries, (RsFile)fileA3D);
+    return numEntries;
+}
+
+static void
+nFileA3DGetIndexEntries(JNIEnv *_env, jobject _this, RsContext con, jint fileA3D, jint numEntries, jintArray _ids, jobjectArray _entries)
+{
+    LOGV("______nFileA3D %u", (uint32_t) fileA3D);
+    RsFileIndexEntry *fileEntries = (RsFileIndexEntry*)malloc((uint32_t)numEntries * sizeof(RsFileIndexEntry));
+
+    rsaFileA3DGetIndexEntries(con, fileEntries, (uint32_t)numEntries, (RsFile)fileA3D);
+
+    for(jint i = 0; i < numEntries; i ++) {
+        _env->SetObjectArrayElement(_entries, i, _env->NewStringUTF(fileEntries[i].objectName));
+        _env->SetIntArrayRegion(_ids, i, 1, (const jint*)&fileEntries[i].classID);
     }
-    free(bufAlloc);
+
+    free(fileEntries);
+}
+
+static int
+nFileA3DGetEntryByIndex(JNIEnv *_env, jobject _this, RsContext con, jint fileA3D, jint index)
+{
+    LOGV("______nFileA3D %u", (uint32_t) fileA3D);
+    jint id = (jint)rsaFileA3DGetEntryByIndex(con, (uint32_t)index, (RsFile)fileA3D);
+    return id;
+}
+
+// -----------------------------------
+
+static int
+nFontCreateFromFile(JNIEnv *_env, jobject _this, RsContext con, jstring fileName, jint fontSize, jint dpi)
+{
+    const char* fileNameUTF = _env->GetStringUTFChars(fileName, NULL);
+
+    jint id = (jint)rsFontCreateFromFile(con, fileNameUTF, fontSize, dpi);
+    return id;
 }
 
 
 // -----------------------------------
 
 static void
-nAdapter1DBindAllocation(JNIEnv *_env, jobject _this, jint adapter, jint alloc)
+nAdapter1DBindAllocation(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint alloc)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nAdapter1DBindAllocation, con(%p), adapter(%p), alloc(%p)", con, (RsAdapter1D)adapter, (RsAllocation)alloc);
     rsAdapter1DBindAllocation(con, (RsAdapter1D)adapter, (RsAllocation)alloc);
 }
 
 static void
-nAdapter1DSetConstraint(JNIEnv *_env, jobject _this, jint adapter, jint dim, jint value)
+nAdapter1DSetConstraint(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint dim, jint value)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nAdapter1DSetConstraint, con(%p), adapter(%p), dim(%i), value(%i)", con, (RsAdapter1D)adapter, dim, value);
     rsAdapter1DSetConstraint(con, (RsAdapter1D)adapter, (RsDimension)dim, value);
 }
 
 static void
-nAdapter1DData_i(JNIEnv *_env, jobject _this, jint adapter, jintArray data)
+nAdapter1DData_i(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jintArray data)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAdapter1DData_i, con(%p), adapter(%p), len(%i)", con, (RsAdapter1D)adapter, len);
     jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -731,9 +682,8 @@
 }
 
 static void
-nAdapter1DSubData_i(JNIEnv *_env, jobject _this, jint adapter, jint offset, jint count, jintArray data)
+nAdapter1DSubData_i(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint offset, jint count, jintArray data)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAdapter1DSubData_i, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAdapter1D)adapter, offset, count, len);
     jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -742,9 +692,8 @@
 }
 
 static void
-nAdapter1DData_f(JNIEnv *_env, jobject _this, jint adapter, jfloatArray data)
+nAdapter1DData_f(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jfloatArray data)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAdapter1DData_f, con(%p), adapter(%p), len(%i)", con, (RsAdapter1D)adapter, len);
     jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
@@ -753,9 +702,8 @@
 }
 
 static void
-nAdapter1DSubData_f(JNIEnv *_env, jobject _this, jint adapter, jint offset, jint count, jfloatArray data)
+nAdapter1DSubData_f(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint offset, jint count, jfloatArray data)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAdapter1DSubData_f, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAdapter1D)adapter, offset, count, len);
     jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
@@ -764,9 +712,8 @@
 }
 
 static jint
-nAdapter1DCreate(JNIEnv *_env, jobject _this)
+nAdapter1DCreate(JNIEnv *_env, jobject _this, RsContext con)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nAdapter1DCreate, con(%p)", con);
     return (jint)rsAdapter1DCreate(con);
 }
@@ -774,25 +721,22 @@
 // -----------------------------------
 
 static void
-nAdapter2DBindAllocation(JNIEnv *_env, jobject _this, jint adapter, jint alloc)
+nAdapter2DBindAllocation(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint alloc)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nAdapter2DBindAllocation, con(%p), adapter(%p), alloc(%p)", con, (RsAdapter2D)adapter, (RsAllocation)alloc);
     rsAdapter2DBindAllocation(con, (RsAdapter2D)adapter, (RsAllocation)alloc);
 }
 
 static void
-nAdapter2DSetConstraint(JNIEnv *_env, jobject _this, jint adapter, jint dim, jint value)
+nAdapter2DSetConstraint(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint dim, jint value)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nAdapter2DSetConstraint, con(%p), adapter(%p), dim(%i), value(%i)", con, (RsAdapter2D)adapter, dim, value);
     rsAdapter2DSetConstraint(con, (RsAdapter2D)adapter, (RsDimension)dim, value);
 }
 
 static void
-nAdapter2DData_i(JNIEnv *_env, jobject _this, jint adapter, jintArray data)
+nAdapter2DData_i(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jintArray data)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAdapter2DData_i, con(%p), adapter(%p), len(%i)", con, (RsAdapter2D)adapter, len);
     jint *ptr = _env->GetIntArrayElements(data, NULL);
@@ -801,9 +745,8 @@
 }
 
 static void
-nAdapter2DData_f(JNIEnv *_env, jobject _this, jint adapter, jfloatArray data)
+nAdapter2DData_f(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jfloatArray data)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAdapter2DData_f, con(%p), adapter(%p), len(%i)", con, (RsAdapter2D)adapter, len);
     jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
@@ -812,9 +755,8 @@
 }
 
 static void
-nAdapter2DSubData_i(JNIEnv *_env, jobject _this, jint adapter, jint xoff, jint yoff, jint w, jint h, jintArray data)
+nAdapter2DSubData_i(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint xoff, jint yoff, jint w, jint h, jintArray data)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAdapter2DSubData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)",
             con, (RsAdapter2D)adapter, xoff, yoff, w, h, len);
@@ -824,9 +766,8 @@
 }
 
 static void
-nAdapter2DSubData_f(JNIEnv *_env, jobject _this, jint adapter, jint xoff, jint yoff, jint w, jint h, jfloatArray data)
+nAdapter2DSubData_f(JNIEnv *_env, jobject _this, RsContext con, jint adapter, jint xoff, jint yoff, jint w, jint h, jfloatArray data)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     jint len = _env->GetArrayLength(data);
     LOG_API("nAdapter2DSubData_f, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)",
             con, (RsAdapter2D)adapter, xoff, yoff, w, h, len);
@@ -836,9 +777,8 @@
 }
 
 static jint
-nAdapter2DCreate(JNIEnv *_env, jobject _this)
+nAdapter2DCreate(JNIEnv *_env, jobject _this, RsContext con)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nAdapter2DCreate, con(%p)", con);
     return (jint)rsAdapter2DCreate(con);
 }
@@ -846,41 +786,54 @@
 // -----------------------------------
 
 static void
-nScriptBindAllocation(JNIEnv *_env, jobject _this, jint script, jint alloc, jint slot)
+nScriptBindAllocation(JNIEnv *_env, jobject _this, RsContext con, jint script, jint alloc, jint slot)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nScriptBindAllocation, con(%p), script(%p), alloc(%p), slot(%i)", con, (RsScript)script, (RsAllocation)alloc, slot);
     rsScriptBindAllocation(con, (RsScript)script, (RsAllocation)alloc, slot);
 }
 
 static void
-nScriptSetClearColor(JNIEnv *_env, jobject _this, jint script, jfloat r, jfloat g, jfloat b, jfloat a)
+nScriptSetVarI(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jint val)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nScriptSetClearColor, con(%p), s(%p), r(%f), g(%f), b(%f), a(%f)", con, (void *)script, r, g, b, a);
-    rsScriptSetClearColor(con, (RsScript)script, r, g, b, a);
+    LOG_API("nScriptSetVarI, con(%p), s(%p), slot(%i), val(%i)", con, (void *)script, slot, val);
+    rsScriptSetVarI(con, (RsScript)script, slot, val);
 }
 
 static void
-nScriptSetClearDepth(JNIEnv *_env, jobject _this, jint script, jfloat d)
+nScriptSetVarJ(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jlong val)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nScriptCSetClearDepth, con(%p), s(%p), depth(%f)", con, (void *)script, d);
-    rsScriptSetClearDepth(con, (RsScript)script, d);
+    LOG_API("nScriptSetVarJ, con(%p), s(%p), slot(%i), val(%lli)", con, (void *)script, slot, val);
+    rsScriptSetVarJ(con, (RsScript)script, slot, val);
 }
 
 static void
-nScriptSetClearStencil(JNIEnv *_env, jobject _this, jint script, jint stencil)
+nScriptSetVarF(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, float val)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nScriptCSetClearStencil, con(%p), s(%p), stencil(%i)", con, (void *)script, stencil);
-    rsScriptSetClearStencil(con, (RsScript)script, stencil);
+    LOG_API("nScriptSetVarF, con(%p), s(%p), slot(%i), val(%f)", con, (void *)script, slot, val);
+    rsScriptSetVarF(con, (RsScript)script, slot, val);
 }
 
 static void
-nScriptSetTimeZone(JNIEnv *_env, jobject _this, jint script, jbyteArray timeZone)
+nScriptSetVarD(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, double val)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nScriptSetVarD, con(%p), s(%p), slot(%i), val(%lf)", con, (void *)script, slot, val);
+    rsScriptSetVarD(con, (RsScript)script, slot, val);
+}
+
+static void
+nScriptSetVarV(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jbyteArray data)
+{
+    LOG_API("nScriptSetVarV, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+    jint len = _env->GetArrayLength(data);
+    jbyte *ptr = _env->GetByteArrayElements(data, NULL);
+    rsScriptSetVarV(con, (RsScript)script, slot, ptr, len);
+    _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
+}
+
+
+static void
+nScriptSetTimeZone(JNIEnv *_env, jobject _this, RsContext con, jint script, jbyteArray timeZone)
+{
     LOG_API("nScriptCSetTimeZone, con(%p), s(%p), timeZone(%s)", con, (void *)script, (const char *)timeZone);
 
     jint length = _env->GetArrayLength(timeZone);
@@ -895,66 +848,36 @@
 }
 
 static void
-nScriptSetType(JNIEnv *_env, jobject _this, jint type, jboolean writable, jstring _str, jint slot)
+nScriptInvoke(JNIEnv *_env, jobject _this, RsContext con, jint obj, jint slot)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nScriptCAddType, con(%p), type(%p), writable(%i), slot(%i)", con, (RsType)type, writable, slot);
-    const char* n = NULL;
-    if (_str) {
-        n = _env->GetStringUTFChars(_str, NULL);
-    }
-    rsScriptSetType(con, (RsType)type, slot, writable, n);
-    if (n) {
-        _env->ReleaseStringUTFChars(_str, n);
-    }
-}
-
-static void
-nScriptSetInvoke(JNIEnv *_env, jobject _this, jstring _str, jint slot)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nScriptSetInvoke, con(%p)", con);
-    const char* n = NULL;
-    if (_str) {
-        n = _env->GetStringUTFChars(_str, NULL);
-    }
-    rsScriptSetInvoke(con, n, slot);
-    if (n) {
-        _env->ReleaseStringUTFChars(_str, n);
-    }
-}
-
-static void
-nScriptInvoke(JNIEnv *_env, jobject _this, jint obj, jint slot)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nScriptInvoke, con(%p), script(%p)", con, (void *)obj);
     rsScriptInvoke(con, (RsScript)obj, slot);
 }
 
 static void
-nScriptSetRoot(JNIEnv *_env, jobject _this, jboolean isRoot)
+nScriptInvokeV(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jbyteArray data)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nScriptCSetRoot, con(%p), isRoot(%i)", con, isRoot);
-    rsScriptSetRoot(con, isRoot);
+    LOG_API("nScriptInvokeV, con(%p), s(%p), slot(%i)", con, (void *)script, slot);
+    jint len = _env->GetArrayLength(data);
+    jbyte *ptr = _env->GetByteArrayElements(data, NULL);
+    rsScriptInvokeV(con, (RsScript)script, slot, ptr, len);
+    _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
 }
 
+
 // -----------------------------------
 
 static void
-nScriptCBegin(JNIEnv *_env, jobject _this)
+nScriptCBegin(JNIEnv *_env, jobject _this, RsContext con)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nScriptCBegin, con(%p)", con);
     rsScriptCBegin(con);
 }
 
 static void
-nScriptCSetScript(JNIEnv *_env, jobject _this, jbyteArray scriptRef,
+nScriptCSetScript(JNIEnv *_env, jobject _this, RsContext con, jbyteArray scriptRef,
                   jint offset, jint length)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("!!! nScriptCSetScript, con(%p)", con);
     jint _exception = 0;
     jint remaining;
@@ -995,114 +918,82 @@
 }
 
 static jint
-nScriptCCreate(JNIEnv *_env, jobject _this)
+nScriptCCreate(JNIEnv *_env, jobject _this, RsContext con)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nScriptCCreate, con(%p)", con);
     return (jint)rsScriptCCreate(con);
 }
 
-static void
-nScriptCAddDefineI32(JNIEnv *_env, jobject _this, jstring name, jint value)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    const char* n = _env->GetStringUTFChars(name, NULL);
-    LOG_API("nScriptCAddDefineI32, con(%p) name(%s) value(%d)", con, n, value);
-    rsScriptCSetDefineI32(con, n, value);
-    _env->ReleaseStringUTFChars(name, n);
-}
-
-static void
-nScriptCAddDefineF(JNIEnv *_env, jobject _this, jstring name, jfloat value)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    const char* n = _env->GetStringUTFChars(name, NULL);
-    LOG_API("nScriptCAddDefineF, con(%p) name(%s) value(%f)", con, n, value);
-    rsScriptCSetDefineF(con, n, value);
-    _env->ReleaseStringUTFChars(name, n);
-}
-
 // ---------------------------------------------------------------------------
 
 static void
-nProgramFragmentStoreBegin(JNIEnv *_env, jobject _this, jint in, jint out)
+nProgramStoreBegin(JNIEnv *_env, jobject _this, RsContext con, jint in, jint out)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nProgramFragmentStoreBegin, con(%p), in(%p), out(%p)", con, (RsElement)in, (RsElement)out);
-    rsProgramFragmentStoreBegin(con, (RsElement)in, (RsElement)out);
+    LOG_API("nProgramStoreBegin, con(%p), in(%p), out(%p)", con, (RsElement)in, (RsElement)out);
+    rsProgramStoreBegin(con, (RsElement)in, (RsElement)out);
 }
 
 static void
-nProgramFragmentStoreDepthFunc(JNIEnv *_env, jobject _this, jint func)
+nProgramStoreDepthFunc(JNIEnv *_env, jobject _this, RsContext con, jint func)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nProgramFragmentStoreDepthFunc, con(%p), func(%i)", con, func);
-    rsProgramFragmentStoreDepthFunc(con, (RsDepthFunc)func);
+    LOG_API("nProgramStoreDepthFunc, con(%p), func(%i)", con, func);
+    rsProgramStoreDepthFunc(con, (RsDepthFunc)func);
 }
 
 static void
-nProgramFragmentStoreDepthMask(JNIEnv *_env, jobject _this, jboolean enable)
+nProgramStoreDepthMask(JNIEnv *_env, jobject _this, RsContext con, jboolean enable)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nProgramFragmentStoreDepthMask, con(%p), enable(%i)", con, enable);
-    rsProgramFragmentStoreDepthMask(con, enable);
+    LOG_API("nProgramStoreDepthMask, con(%p), enable(%i)", con, enable);
+    rsProgramStoreDepthMask(con, enable);
 }
 
 static void
-nProgramFragmentStoreColorMask(JNIEnv *_env, jobject _this, jboolean r, jboolean g, jboolean b, jboolean a)
+nProgramStoreColorMask(JNIEnv *_env, jobject _this, RsContext con, jboolean r, jboolean g, jboolean b, jboolean a)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nProgramFragmentStoreColorMask, con(%p), r(%i), g(%i), b(%i), a(%i)", con, r, g, b, a);
-    rsProgramFragmentStoreColorMask(con, r, g, b, a);
+    LOG_API("nProgramStoreColorMask, con(%p), r(%i), g(%i), b(%i), a(%i)", con, r, g, b, a);
+    rsProgramStoreColorMask(con, r, g, b, a);
 }
 
 static void
-nProgramFragmentStoreBlendFunc(JNIEnv *_env, jobject _this, int src, int dst)
+nProgramStoreBlendFunc(JNIEnv *_env, jobject _this, RsContext con, int src, int dst)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nProgramFragmentStoreBlendFunc, con(%p), src(%i), dst(%i)", con, src, dst);
-    rsProgramFragmentStoreBlendFunc(con, (RsBlendSrcFunc)src, (RsBlendDstFunc)dst);
+    LOG_API("nProgramStoreBlendFunc, con(%p), src(%i), dst(%i)", con, src, dst);
+    rsProgramStoreBlendFunc(con, (RsBlendSrcFunc)src, (RsBlendDstFunc)dst);
 }
 
 static void
-nProgramFragmentStoreDither(JNIEnv *_env, jobject _this, jboolean enable)
+nProgramStoreDither(JNIEnv *_env, jobject _this, RsContext con, jboolean enable)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nProgramFragmentStoreDither, con(%p), enable(%i)", con, enable);
-    rsProgramFragmentStoreDither(con, enable);
+    LOG_API("nProgramStoreDither, con(%p), enable(%i)", con, enable);
+    rsProgramStoreDither(con, enable);
 }
 
 static jint
-nProgramFragmentStoreCreate(JNIEnv *_env, jobject _this)
+nProgramStoreCreate(JNIEnv *_env, jobject _this, RsContext con)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nProgramFragmentStoreCreate, con(%p)", con);
-
-    return (jint)rsProgramFragmentStoreCreate(con);
+    LOG_API("nProgramStoreCreate, con(%p)", con);
+    return (jint)rsProgramStoreCreate(con);
 }
 
 // ---------------------------------------------------------------------------
 
 static void
-nProgramBindConstants(JNIEnv *_env, jobject _this, jint vpv, jint slot, jint a)
+nProgramBindConstants(JNIEnv *_env, jobject _this, RsContext con, jint vpv, jint slot, jint a)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nProgramBindConstants, con(%p), vpf(%p), sloat(%i), a(%p)", con, (RsProgramVertex)vpv, slot, (RsAllocation)a);
     rsProgramBindConstants(con, (RsProgram)vpv, slot, (RsAllocation)a);
 }
 
 static void
-nProgramBindTexture(JNIEnv *_env, jobject _this, jint vpf, jint slot, jint a)
+nProgramBindTexture(JNIEnv *_env, jobject _this, RsContext con, jint vpf, jint slot, jint a)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nProgramBindTexture, con(%p), vpf(%p), slot(%i), a(%p)", con, (RsProgramFragment)vpf, slot, (RsAllocation)a);
     rsProgramBindTexture(con, (RsProgramFragment)vpf, slot, (RsAllocation)a);
 }
 
 static void
-nProgramBindSampler(JNIEnv *_env, jobject _this, jint vpf, jint slot, jint a)
+nProgramBindSampler(JNIEnv *_env, jobject _this, RsContext con, jint vpf, jint slot, jint a)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nProgramBindSampler, con(%p), vpf(%p), slot(%i), a(%p)", con, (RsProgramFragment)vpf, slot, (RsSampler)a);
     rsProgramBindSampler(con, (RsProgramFragment)vpf, slot, (RsSampler)a);
 }
@@ -1110,31 +1001,16 @@
 // ---------------------------------------------------------------------------
 
 static jint
-nProgramFragmentCreate(JNIEnv *_env, jobject _this, jintArray params)
+nProgramFragmentCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader, jintArray params)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    jint *paramPtr = _env->GetIntArrayElements(params, NULL);
-    jint paramLen = _env->GetArrayLength(params);
-
-    LOG_API("nProgramFragmentCreate, con(%p), paramLen(%i)", con, paramLen);
-
-    jint ret = (jint)rsProgramFragmentCreate(con, (uint32_t *)paramPtr, paramLen);
-    _env->ReleaseIntArrayElements(params, paramPtr, JNI_ABORT);
-    return ret;
-}
-
-static jint
-nProgramFragmentCreate2(JNIEnv *_env, jobject _this, jstring shader, jintArray params)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     const char* shaderUTF = _env->GetStringUTFChars(shader, NULL);
     jint shaderLen = _env->GetStringUTFLength(shader);
     jint *paramPtr = _env->GetIntArrayElements(params, NULL);
     jint paramLen = _env->GetArrayLength(params);
 
-    LOG_API("nProgramFragmentCreate2, con(%p), shaderLen(%i), paramLen(%i)", con, shaderLen, paramLen);
+    LOG_API("nProgramFragmentCreate, con(%p), shaderLen(%i), paramLen(%i)", con, shaderLen, paramLen);
 
-    jint ret = (jint)rsProgramFragmentCreate2(con, shaderUTF, shaderLen, (uint32_t *)paramPtr, paramLen);
+    jint ret = (jint)rsProgramFragmentCreate(con, shaderUTF, shaderLen, (uint32_t *)paramPtr, paramLen);
     _env->ReleaseStringUTFChars(shader, shaderUTF);
     _env->ReleaseIntArrayElements(params, paramPtr, JNI_ABORT);
     return ret;
@@ -1144,25 +1020,16 @@
 // ---------------------------------------------------------------------------
 
 static jint
-nProgramVertexCreate(JNIEnv *_env, jobject _this, jboolean texMat)
+nProgramVertexCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader, jintArray params)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nProgramVertexCreate, con(%p), texMat(%i)", con, texMat);
-    return (jint)rsProgramVertexCreate(con, texMat);
-}
-
-static jint
-nProgramVertexCreate2(JNIEnv *_env, jobject _this, jstring shader, jintArray params)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     const char* shaderUTF = _env->GetStringUTFChars(shader, NULL);
     jint shaderLen = _env->GetStringUTFLength(shader);
     jint *paramPtr = _env->GetIntArrayElements(params, NULL);
     jint paramLen = _env->GetArrayLength(params);
 
-    LOG_API("nProgramVertexCreate2, con(%p), shaderLen(%i), paramLen(%i)", con, shaderLen, paramLen);
+    LOG_API("nProgramVertexCreate, con(%p), shaderLen(%i), paramLen(%i)", con, shaderLen, paramLen);
 
-    jint ret = (jint)rsProgramVertexCreate2(con, shaderUTF, shaderLen, (uint32_t *)paramPtr, paramLen);
+    jint ret = (jint)rsProgramVertexCreate(con, shaderUTF, shaderLen, (uint32_t *)paramPtr, paramLen);
     _env->ReleaseStringUTFChars(shader, shaderUTF);
     _env->ReleaseIntArrayElements(params, paramPtr, JNI_ABORT);
     return ret;
@@ -1171,70 +1038,61 @@
 // ---------------------------------------------------------------------------
 
 static jint
-nProgramRasterCreate(JNIEnv *_env, jobject _this, jint in, jint out,
-                     jboolean pointSmooth, jboolean lineSmooth, jboolean pointSprite)
+nProgramRasterCreate(JNIEnv *_env, jobject _this, RsContext con, jboolean pointSmooth, jboolean lineSmooth, jboolean pointSprite)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nProgramRasterCreate, con(%p), in(%p), out(%p), pointSmooth(%i), lineSmooth(%i), pointSprite(%i)",
-            con, (RsElement)in, (RsElement)out, pointSmooth, lineSmooth, pointSprite);
-    return (jint)rsProgramRasterCreate(con, (RsElement)in, (RsElement)out, pointSmooth, lineSmooth, pointSprite);
+    LOG_API("nProgramRasterCreate, con(%p), pointSmooth(%i), lineSmooth(%i), pointSprite(%i)",
+            con, pointSmooth, lineSmooth, pointSprite);
+    return (jint)rsProgramRasterCreate(con, pointSmooth, lineSmooth, pointSprite);
 }
 
 static void
-nProgramRasterSetPointSize(JNIEnv *_env, jobject _this, jint vpr, jfloat v)
+nProgramRasterSetLineWidth(JNIEnv *_env, jobject _this, RsContext con, jint vpr, jfloat v)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nProgramRasterSetPointSize, con(%p), vpf(%p), value(%f)", con, (RsProgramRaster)vpr, v);
-    rsProgramRasterSetPointSize(con, (RsProgramFragment)vpr, v);
-}
-
-static void
-nProgramRasterSetLineWidth(JNIEnv *_env, jobject _this, jint vpr, jfloat v)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nProgramRasterSetLineWidth, con(%p), vpf(%p), value(%f)", con, (RsProgramRaster)vpr, v);
-    rsProgramRasterSetLineWidth(con, (RsProgramFragment)vpr, v);
+    rsProgramRasterSetLineWidth(con, (RsProgramRaster)vpr, v);
+}
+
+static void
+nProgramRasterSetCullMode(JNIEnv *_env, jobject _this, RsContext con, jint vpr, jint v)
+{
+    LOG_API("nProgramRasterSetCullMode, con(%p), vpf(%p), value(%i)", con, (RsProgramRaster)vpr, v);
+    rsProgramRasterSetCullMode(con, (RsProgramRaster)vpr, (RsCullMode)v);
 }
 
 
 // ---------------------------------------------------------------------------
 
 static void
-nContextBindRootScript(JNIEnv *_env, jobject _this, jint script)
+nContextBindRootScript(JNIEnv *_env, jobject _this, RsContext con, jint script)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nContextBindRootScript, con(%p), script(%p)", con, (RsScript)script);
     rsContextBindRootScript(con, (RsScript)script);
 }
 
 static void
-nContextBindProgramFragmentStore(JNIEnv *_env, jobject _this, jint pfs)
+nContextBindProgramStore(JNIEnv *_env, jobject _this, RsContext con, jint pfs)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nContextBindProgramFragmentStore, con(%p), pfs(%p)", con, (RsProgramFragmentStore)pfs);
-    rsContextBindProgramFragmentStore(con, (RsProgramFragmentStore)pfs);
+    LOG_API("nContextBindProgramStore, con(%p), pfs(%p)", con, (RsProgramStore)pfs);
+    rsContextBindProgramStore(con, (RsProgramStore)pfs);
 }
 
 static void
-nContextBindProgramFragment(JNIEnv *_env, jobject _this, jint pf)
+nContextBindProgramFragment(JNIEnv *_env, jobject _this, RsContext con, jint pf)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nContextBindProgramFragment, con(%p), pf(%p)", con, (RsProgramFragment)pf);
     rsContextBindProgramFragment(con, (RsProgramFragment)pf);
 }
 
 static void
-nContextBindProgramVertex(JNIEnv *_env, jobject _this, jint pf)
+nContextBindProgramVertex(JNIEnv *_env, jobject _this, RsContext con, jint pf)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nContextBindProgramVertex, con(%p), pf(%p)", con, (RsProgramVertex)pf);
     rsContextBindProgramVertex(con, (RsProgramVertex)pf);
 }
 
 static void
-nContextBindProgramRaster(JNIEnv *_env, jobject _this, jint pf)
+nContextBindProgramRaster(JNIEnv *_env, jobject _this, RsContext con, jint pf)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nContextBindProgramRaster, con(%p), pf(%p)", con, (RsProgramRaster)pf);
     rsContextBindProgramRaster(con, (RsProgramRaster)pf);
 }
@@ -1243,108 +1101,107 @@
 // ---------------------------------------------------------------------------
 
 static void
-nSamplerBegin(JNIEnv *_env, jobject _this)
+nSamplerBegin(JNIEnv *_env, jobject _this, RsContext con)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nSamplerBegin, con(%p)", con);
     rsSamplerBegin(con);
 }
 
 static void
-nSamplerSet(JNIEnv *_env, jobject _this, jint p, jint v)
+nSamplerSet(JNIEnv *_env, jobject _this, RsContext con, jint p, jint v)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("nSamplerSet, con(%p), param(%i), value(%i)", con, p, v);
     rsSamplerSet(con, (RsSamplerParam)p, (RsSamplerValue)v);
 }
 
-static jint
-nSamplerCreate(JNIEnv *_env, jobject _this)
+static void
+nSamplerSet2(JNIEnv *_env, jobject _this, RsContext con, jint p, jfloat v)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    LOG_API("nSamplerSet2, con(%p), param(%i), value(%f)", con, p, v);
+    rsSamplerSet2(con, (RsSamplerParam)p, v);
+}
+
+static jint
+nSamplerCreate(JNIEnv *_env, jobject _this, RsContext con)
+{
     LOG_API("nSamplerCreate, con(%p)", con);
     return (jint)rsSamplerCreate(con);
 }
 
 // ---------------------------------------------------------------------------
 
-static void
-nLightBegin(JNIEnv *_env, jobject _this)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nLightBegin, con(%p)", con);
-    rsLightBegin(con);
-}
-
-static void
-nLightSetIsMono(JNIEnv *_env, jobject _this, jboolean isMono)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nLightSetIsMono, con(%p), isMono(%i)", con, isMono);
-    rsLightSetMonochromatic(con, isMono);
-}
-
-static void
-nLightSetIsLocal(JNIEnv *_env, jobject _this, jboolean isLocal)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nLightSetIsLocal, con(%p), isLocal(%i)", con, isLocal);
-    rsLightSetLocal(con, isLocal);
-}
-
 static jint
-nLightCreate(JNIEnv *_env, jobject _this)
+nMeshCreate(JNIEnv *_env, jobject _this, RsContext con, jint vtxCount, jint idxCount)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nLightCreate, con(%p)", con);
-    return (jint)rsLightCreate(con);
-}
-
-static void
-nLightSetColor(JNIEnv *_env, jobject _this, jint light, float r, float g, float b)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nLightSetColor, con(%p), light(%p), r(%f), g(%f), b(%f)", con, (RsLight)light, r, g, b);
-    rsLightSetColor(con, (RsLight)light, r, g, b);
-}
-
-static void
-nLightSetPosition(JNIEnv *_env, jobject _this, jint light, float x, float y, float z)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nLightSetPosition, con(%p), light(%p), x(%f), y(%f), z(%f)", con, (RsLight)light, x, y, z);
-    rsLightSetPosition(con, (RsLight)light, x, y, z);
-}
-
-// ---------------------------------------------------------------------------
-
-static jint
-nSimpleMeshCreate(JNIEnv *_env, jobject _this, jint batchID, jint indexID, jintArray vtxIDs, jint primID)
-{
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    jint len = _env->GetArrayLength(vtxIDs);
-    LOG_API("nSimpleMeshCreate, con(%p), batchID(%i), indexID(%i), vtxIDs.len(%i), primID(%i)",
-            con, batchID, indexID, len, primID);
-    jint *ptr = _env->GetIntArrayElements(vtxIDs, NULL);
-    int id = (int)rsSimpleMeshCreate(con, (void *)batchID, (void *)indexID, (void **)ptr, len, primID);
-    _env->ReleaseIntArrayElements(vtxIDs, ptr, 0/*JNI_ABORT*/);
+    LOG_API("nMeshCreate, con(%p), vtxCount(%i), idxCount(%i)", con, vtxCount, idxCount);
+    int id = (int)rsMeshCreate(con, vtxCount, idxCount);
     return id;
 }
 
 static void
-nSimpleMeshBindVertex(JNIEnv *_env, jobject _this, jint s, jint alloc, jint slot)
+nMeshBindVertex(JNIEnv *_env, jobject _this, RsContext con, jint mesh, jint alloc, jint slot)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nSimpleMeshBindVertex, con(%p), SimpleMesh(%p), Alloc(%p), slot(%i)", con, (RsSimpleMesh)s, (RsAllocation)alloc, slot);
-    rsSimpleMeshBindVertex(con, (RsSimpleMesh)s, (RsAllocation)alloc, slot);
+    LOG_API("nMeshBindVertex, con(%p), Mesh(%p), Alloc(%p), slot(%i)", con, (RsMesh)mesh, (RsAllocation)alloc, slot);
+    rsMeshBindVertex(con, (RsMesh)mesh, (RsAllocation)alloc, slot);
 }
 
 static void
-nSimpleMeshBindIndex(JNIEnv *_env, jobject _this, jint s, jint alloc)
+nMeshBindIndex(JNIEnv *_env, jobject _this, RsContext con, jint mesh, jint alloc, jint primID, jint slot)
 {
-    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
-    LOG_API("nSimpleMeshBindIndex, con(%p), SimpleMesh(%p), Alloc(%p)", con, (RsSimpleMesh)s, (RsAllocation)alloc);
-    rsSimpleMeshBindIndex(con, (RsSimpleMesh)s, (RsAllocation)alloc);
+    LOG_API("nMeshBindIndex, con(%p), Mesh(%p), Alloc(%p)", con, (RsMesh)mesh, (RsAllocation)alloc);
+    rsMeshBindIndex(con, (RsMesh)mesh, (RsAllocation)alloc, primID, slot);
+}
+
+static jint
+nMeshGetVertexBufferCount(JNIEnv *_env, jobject _this, RsContext con, jint mesh)
+{
+    LOG_API("nMeshGetVertexBufferCount, con(%p), Mesh(%p)", con, (RsMesh)mesh);
+    jint vtxCount = 0;
+    rsaMeshGetVertexBufferCount(con, (RsMesh)mesh, &vtxCount);
+    return vtxCount;
+}
+
+static jint
+nMeshGetIndexCount(JNIEnv *_env, jobject _this, RsContext con, jint mesh)
+{
+    LOG_API("nMeshGetIndexCount, con(%p), Mesh(%p)", con, (RsMesh)mesh);
+    jint idxCount = 0;
+    rsaMeshGetIndexCount(con, (RsMesh)mesh, &idxCount);
+    return idxCount;
+}
+
+static void
+nMeshGetVertices(JNIEnv *_env, jobject _this, RsContext con, jint mesh, jintArray _ids, int numVtxIDs)
+{
+    LOG_API("nMeshGetVertices, con(%p), Mesh(%p)", con, (RsMesh)mesh);
+
+    RsAllocation *allocs = (RsAllocation*)malloc((uint32_t)numVtxIDs * sizeof(RsAllocation));
+    rsaMeshGetVertices(con, (RsMesh)mesh, allocs, (uint32_t)numVtxIDs);
+
+    for(jint i = 0; i < numVtxIDs; i ++) {
+        _env->SetIntArrayRegion(_ids, i, 1, (const jint*)&allocs[i]);
+    }
+
+    free(allocs);
+}
+
+static void
+nMeshGetIndices(JNIEnv *_env, jobject _this, RsContext con, jint mesh, jintArray _idxIds, jintArray _primitives, int numIndices)
+{
+    LOG_API("nMeshGetVertices, con(%p), Mesh(%p)", con, (RsMesh)mesh);
+
+    RsAllocation *allocs = (RsAllocation*)malloc((uint32_t)numIndices * sizeof(RsAllocation));
+    uint32_t *prims= (uint32_t*)malloc((uint32_t)numIndices * sizeof(uint32_t));
+
+    rsaMeshGetIndices(con, (RsMesh)mesh, allocs, prims, (uint32_t)numIndices);
+
+    for(jint i = 0; i < numIndices; i ++) {
+        _env->SetIntArrayRegion(_idxIds, i, 1, (const jint*)&allocs[i]);
+        _env->SetIntArrayRegion(_primitives, i, 1, (const jint*)&prims[i]);
+    }
+
+    free(allocs);
+    free(prims);
 }
 
 // ---------------------------------------------------------------------------
@@ -1359,124 +1216,129 @@
 {"nDeviceCreate",                  "()I",                                  (void*)nDeviceCreate },
 {"nDeviceDestroy",                 "(I)V",                                 (void*)nDeviceDestroy },
 {"nDeviceSetConfig",               "(III)V",                               (void*)nDeviceSetConfig },
-{"nContextCreate",                 "(II)I",                                (void*)nContextCreate },
-{"nContextCreateGL",               "(IIZ)I",                               (void*)nContextCreateGL },
-{"nContextSetPriority",            "(I)V",                                 (void*)nContextSetPriority },
-{"nContextSetSurface",             "(IILandroid/view/Surface;)V",          (void*)nContextSetSurface },
-{"nContextDestroy",                "(I)V",                                 (void*)nContextDestroy },
-{"nContextDump",                   "(I)V",                                 (void*)nContextDump },
-{"nContextPause",                  "()V",                                  (void*)nContextPause },
-{"nContextResume",                 "()V",                                  (void*)nContextResume },
-{"nAssignName",                    "(I[B)V",                               (void*)nAssignName },
-{"nObjDestroy",                    "(I)V",                                 (void*)nObjDestroy },
-{"nObjDestroyOOB",                 "(I)V",                                 (void*)nObjDestroyOOB },
-{"nContextGetMessage",             "([IZ)I",                               (void*)nContextGetMessage },
-{"nContextInitToClient",           "()V",                                  (void*)nContextInitToClient },
-{"nContextDeinitToClient",         "()V",                                  (void*)nContextDeinitToClient },
+{"nContextGetMessage",             "(I[IZ)I",                               (void*)nContextGetMessage },
+{"nContextInitToClient",           "(I)V",                                  (void*)nContextInitToClient },
+{"nContextDeinitToClient",         "(I)V",                                  (void*)nContextDeinitToClient },
 
-{"nFileOpen",                      "([B)I",                                (void*)nFileOpen },
 
-{"nElementCreate",                 "(IIZI)I",                              (void*)nElementCreate },
-{"nElementCreate2",                "([I[Ljava/lang/String;)I",             (void*)nElementCreate2 },
+// All methods below are thread protected in java.
+{"rsnContextCreate",                 "(II)I",                                 (void*)nContextCreate },
+{"rsnContextCreateGL",               "(IIIIIIIIIIIIF)I",                      (void*)nContextCreateGL },
+{"rsnContextFinish",                 "(I)V",                                  (void*)nContextFinish },
+{"rsnContextSetPriority",            "(II)V",                                 (void*)nContextSetPriority },
+{"rsnContextSetSurface",             "(IIILandroid/view/Surface;)V",          (void*)nContextSetSurface },
+{"rsnContextDestroy",                "(I)V",                                  (void*)nContextDestroy },
+{"rsnContextDump",                   "(II)V",                                 (void*)nContextDump },
+{"rsnContextPause",                  "(I)V",                                  (void*)nContextPause },
+{"rsnContextResume",                 "(I)V",                                  (void*)nContextResume },
+{"rsnAssignName",                    "(II[B)V",                               (void*)nAssignName },
+{"rsnGetName",                       "(II)Ljava/lang/String;",                (void*)nGetName },
+{"rsnObjDestroy",                    "(II)V",                                 (void*)nObjDestroy },
 
-{"nTypeBegin",                     "(I)V",                                 (void*)nTypeBegin },
-{"nTypeAdd",                       "(II)V",                                (void*)nTypeAdd },
-{"nTypeCreate",                    "()I",                                  (void*)nTypeCreate },
-{"nTypeFinalDestroy",              "(Landroid/renderscript/Type;)V",       (void*)nTypeFinalDestroy },
-{"nTypeSetupFields",               "(Landroid/renderscript/Type;[I[I[Ljava/lang/reflect/Field;)V", (void*)nTypeSetupFields },
+{"rsnFileA3DCreateFromAssetStream",  "(II)I",                                 (void*)nFileA3DCreateFromAssetStream },
+{"rsnFileA3DGetNumIndexEntries",     "(II)I",                                 (void*)nFileA3DGetNumIndexEntries },
+{"rsnFileA3DGetIndexEntries",        "(III[I[Ljava/lang/String;)V",           (void*)nFileA3DGetIndexEntries },
+{"rsnFileA3DGetEntryByIndex",        "(III)I",                                (void*)nFileA3DGetEntryByIndex },
 
-{"nAllocationCreateTyped",         "(I)I",                                 (void*)nAllocationCreateTyped },
-{"nAllocationCreateFromBitmap",    "(IZLandroid/graphics/Bitmap;)I",       (void*)nAllocationCreateFromBitmap },
-{"nAllocationCreateBitmapRef",     "(ILandroid/graphics/Bitmap;)I",        (void*)nAllocationCreateBitmapRef },
-{"nAllocationCreateFromBitmapBoxed","(IZLandroid/graphics/Bitmap;)I",      (void*)nAllocationCreateFromBitmapBoxed },
-{"nAllocationCreateFromAssetStream","(IZI)I",                              (void*)nAllocationCreateFromAssetStream },
-{"nAllocationUploadToTexture",     "(IZI)V",                               (void*)nAllocationUploadToTexture },
-{"nAllocationUploadToBufferObject","(I)V",                                 (void*)nAllocationUploadToBufferObject },
-{"nAllocationSubData1D",           "(III[II)V",                            (void*)nAllocationSubData1D_i },
-{"nAllocationSubData1D",           "(III[SI)V",                            (void*)nAllocationSubData1D_s },
-{"nAllocationSubData1D",           "(III[BI)V",                            (void*)nAllocationSubData1D_b },
-{"nAllocationSubData1D",           "(III[FI)V",                            (void*)nAllocationSubData1D_f },
-{"nAllocationSubData2D",           "(IIIII[II)V",                          (void*)nAllocationSubData2D_i },
-{"nAllocationSubData2D",           "(IIIII[FI)V",                          (void*)nAllocationSubData2D_f },
-{"nAllocationRead",                "(I[I)V",                               (void*)nAllocationRead_i },
-{"nAllocationRead",                "(I[F)V",                               (void*)nAllocationRead_f },
-{"nAllocationSubDataFromObject",   "(ILandroid/renderscript/Type;ILjava/lang/Object;)V",   (void*)nAllocationSubDataFromObject },
-{"nAllocationSubReadFromObject",   "(ILandroid/renderscript/Type;ILjava/lang/Object;)V",   (void*)nAllocationSubReadFromObject },
+{"rsnFontCreateFromFile",            "(ILjava/lang/String;II)I",              (void*)nFontCreateFromFile },
 
-{"nAdapter1DBindAllocation",       "(II)V",                                (void*)nAdapter1DBindAllocation },
-{"nAdapter1DSetConstraint",        "(III)V",                               (void*)nAdapter1DSetConstraint },
-{"nAdapter1DData",                 "(I[I)V",                               (void*)nAdapter1DData_i },
-{"nAdapter1DData",                 "(I[F)V",                               (void*)nAdapter1DData_f },
-{"nAdapter1DSubData",              "(III[I)V",                             (void*)nAdapter1DSubData_i },
-{"nAdapter1DSubData",              "(III[F)V",                             (void*)nAdapter1DSubData_f },
-{"nAdapter1DCreate",               "()I",                                  (void*)nAdapter1DCreate },
+{"rsnElementCreate",                 "(IIIZI)I",                              (void*)nElementCreate },
+{"rsnElementCreate2",                "(I[I[Ljava/lang/String;[I)I",           (void*)nElementCreate2 },
+{"rsnElementGetNativeData",          "(II[I)V",                               (void*)nElementGetNativeData },
+{"rsnElementGetSubElements",         "(II[I[Ljava/lang/String;)V",            (void*)nElementGetSubElements },
 
-{"nAdapter2DBindAllocation",       "(II)V",                                (void*)nAdapter2DBindAllocation },
-{"nAdapter2DSetConstraint",        "(III)V",                               (void*)nAdapter2DSetConstraint },
-{"nAdapter2DData",                 "(I[I)V",                               (void*)nAdapter2DData_i },
-{"nAdapter2DData",                 "(I[F)V",                               (void*)nAdapter2DData_f },
-{"nAdapter2DSubData",              "(IIIII[I)V",                           (void*)nAdapter2DSubData_i },
-{"nAdapter2DSubData",              "(IIIII[F)V",                           (void*)nAdapter2DSubData_f },
-{"nAdapter2DCreate",               "()I",                                  (void*)nAdapter2DCreate },
+{"rsnTypeCreate",                    "(II[I[I)I",                             (void*)nTypeCreate },
+{"rsnTypeGetNativeData",             "(II[I)V",                               (void*)nTypeGetNativeData },
 
-{"nScriptBindAllocation",          "(III)V",                               (void*)nScriptBindAllocation },
-{"nScriptSetClearColor",           "(IFFFF)V",                             (void*)nScriptSetClearColor },
-{"nScriptSetClearDepth",           "(IF)V",                                (void*)nScriptSetClearDepth },
-{"nScriptSetClearStencil",         "(II)V",                                (void*)nScriptSetClearStencil },
-{"nScriptSetTimeZone",             "(I[B)V",                               (void*)nScriptSetTimeZone },
-{"nScriptSetType",                 "(IZLjava/lang/String;I)V",             (void*)nScriptSetType },
-{"nScriptSetRoot",                 "(Z)V",                                 (void*)nScriptSetRoot },
-{"nScriptSetInvokable",            "(Ljava/lang/String;I)V",               (void*)nScriptSetInvoke },
-{"nScriptInvoke",                  "(II)V",                                (void*)nScriptInvoke },
+{"rsnAllocationCreateTyped",         "(II)I",                                 (void*)nAllocationCreateTyped },
+{"rsnAllocationUpdateFromBitmap",    "(IILandroid/graphics/Bitmap;)V",        (void*)nAllocationUpdateFromBitmap },
+{"rsnAllocationCreateFromBitmap",    "(IIZLandroid/graphics/Bitmap;)I",       (void*)nAllocationCreateFromBitmap },
+{"rsnAllocationCreateBitmapRef",     "(IILandroid/graphics/Bitmap;)I",        (void*)nAllocationCreateBitmapRef },
+{"rsnAllocationCreateFromAssetStream","(IIZI)I",                              (void*)nAllocationCreateFromAssetStream },
+{"rsnAllocationUploadToTexture",     "(IIZI)V",                               (void*)nAllocationUploadToTexture },
+{"rsnAllocationUploadToBufferObject","(II)V",                                 (void*)nAllocationUploadToBufferObject },
+{"rsnAllocationSubData1D",           "(IIII[II)V",                            (void*)nAllocationSubData1D_i },
+{"rsnAllocationSubData1D",           "(IIII[SI)V",                            (void*)nAllocationSubData1D_s },
+{"rsnAllocationSubData1D",           "(IIII[BI)V",                            (void*)nAllocationSubData1D_b },
+{"rsnAllocationSubData1D",           "(IIII[FI)V",                            (void*)nAllocationSubData1D_f },
+{"rsnAllocationSubElementData1D",    "(IIII[BI)V",                            (void*)nAllocationSubElementData1D },
+{"rsnAllocationSubData2D",           "(IIIIII[II)V",                          (void*)nAllocationSubData2D_i },
+{"rsnAllocationSubData2D",           "(IIIIII[FI)V",                          (void*)nAllocationSubData2D_f },
+{"rsnAllocationRead",                "(II[I)V",                               (void*)nAllocationRead_i },
+{"rsnAllocationRead",                "(II[F)V",                               (void*)nAllocationRead_f },
+{"rsnAllocationGetType",             "(II)I",                                 (void*)nAllocationGetType},
+{"rsnAllocationResize1D",            "(III)V",                                (void*)nAllocationResize1D },
+{"rsnAllocationResize2D",            "(IIII)V",                               (void*)nAllocationResize2D },
 
-{"nScriptCBegin",                  "()V",                                  (void*)nScriptCBegin },
-{"nScriptCSetScript",              "([BII)V",                              (void*)nScriptCSetScript },
-{"nScriptCCreate",                 "()I",                                  (void*)nScriptCCreate },
-{"nScriptCAddDefineI32",           "(Ljava/lang/String;I)V",               (void*)nScriptCAddDefineI32 },
-{"nScriptCAddDefineF",             "(Ljava/lang/String;F)V",               (void*)nScriptCAddDefineF },
+{"rsnAdapter1DBindAllocation",       "(III)V",                                (void*)nAdapter1DBindAllocation },
+{"rsnAdapter1DSetConstraint",        "(IIII)V",                               (void*)nAdapter1DSetConstraint },
+{"rsnAdapter1DData",                 "(II[I)V",                               (void*)nAdapter1DData_i },
+{"rsnAdapter1DData",                 "(II[F)V",                               (void*)nAdapter1DData_f },
+{"rsnAdapter1DSubData",              "(IIII[I)V",                             (void*)nAdapter1DSubData_i },
+{"rsnAdapter1DSubData",              "(IIII[F)V",                             (void*)nAdapter1DSubData_f },
+{"rsnAdapter1DCreate",               "(I)I",                                  (void*)nAdapter1DCreate },
 
-{"nProgramFragmentStoreBegin",     "(II)V",                                (void*)nProgramFragmentStoreBegin },
-{"nProgramFragmentStoreDepthFunc", "(I)V",                                 (void*)nProgramFragmentStoreDepthFunc },
-{"nProgramFragmentStoreDepthMask", "(Z)V",                                 (void*)nProgramFragmentStoreDepthMask },
-{"nProgramFragmentStoreColorMask", "(ZZZZ)V",                              (void*)nProgramFragmentStoreColorMask },
-{"nProgramFragmentStoreBlendFunc", "(II)V",                                (void*)nProgramFragmentStoreBlendFunc },
-{"nProgramFragmentStoreDither",    "(Z)V",                                 (void*)nProgramFragmentStoreDither },
-{"nProgramFragmentStoreCreate",    "()I",                                  (void*)nProgramFragmentStoreCreate },
+{"rsnAdapter2DBindAllocation",       "(III)V",                                (void*)nAdapter2DBindAllocation },
+{"rsnAdapter2DSetConstraint",        "(IIII)V",                               (void*)nAdapter2DSetConstraint },
+{"rsnAdapter2DData",                 "(II[I)V",                               (void*)nAdapter2DData_i },
+{"rsnAdapter2DData",                 "(II[F)V",                               (void*)nAdapter2DData_f },
+{"rsnAdapter2DSubData",              "(IIIIII[I)V",                           (void*)nAdapter2DSubData_i },
+{"rsnAdapter2DSubData",              "(IIIIII[F)V",                           (void*)nAdapter2DSubData_f },
+{"rsnAdapter2DCreate",               "(I)I",                                  (void*)nAdapter2DCreate },
 
-{"nProgramBindConstants",          "(III)V",                               (void*)nProgramBindConstants },
-{"nProgramBindTexture",            "(III)V",                               (void*)nProgramBindTexture },
-{"nProgramBindSampler",            "(III)V",                               (void*)nProgramBindSampler },
+{"rsnScriptBindAllocation",          "(IIII)V",                               (void*)nScriptBindAllocation },
+{"rsnScriptSetTimeZone",             "(II[B)V",                               (void*)nScriptSetTimeZone },
+{"rsnScriptInvoke",                  "(III)V",                                (void*)nScriptInvoke },
+{"rsnScriptInvokeV",                 "(III[B)V",                              (void*)nScriptInvokeV },
+{"rsnScriptSetVarI",                 "(IIII)V",                               (void*)nScriptSetVarI },
+{"rsnScriptSetVarJ",                 "(IIIJ)V",                               (void*)nScriptSetVarJ },
+{"rsnScriptSetVarF",                 "(IIIF)V",                               (void*)nScriptSetVarF },
+{"rsnScriptSetVarD",                 "(IIID)V",                               (void*)nScriptSetVarD },
+{"rsnScriptSetVarV",                 "(III[B)V",                              (void*)nScriptSetVarV },
 
-{"nProgramFragmentCreate",         "([I)I",                                (void*)nProgramFragmentCreate },
-{"nProgramFragmentCreate2",        "(Ljava/lang/String;[I)I",              (void*)nProgramFragmentCreate2 },
+{"rsnScriptCBegin",                  "(I)V",                                  (void*)nScriptCBegin },
+{"rsnScriptCSetScript",              "(I[BII)V",                              (void*)nScriptCSetScript },
+{"rsnScriptCCreate",                 "(I)I",                                  (void*)nScriptCCreate },
 
-{"nProgramRasterCreate",           "(IIZZZ)I",                             (void*)nProgramRasterCreate },
-{"nProgramRasterSetPointSize",     "(IF)V",                                (void*)nProgramRasterSetPointSize },
-{"nProgramRasterSetLineWidth",     "(IF)V",                                (void*)nProgramRasterSetLineWidth },
+{"rsnProgramStoreBegin",             "(III)V",                                (void*)nProgramStoreBegin },
+{"rsnProgramStoreDepthFunc",         "(II)V",                                 (void*)nProgramStoreDepthFunc },
+{"rsnProgramStoreDepthMask",         "(IZ)V",                                 (void*)nProgramStoreDepthMask },
+{"rsnProgramStoreColorMask",         "(IZZZZ)V",                              (void*)nProgramStoreColorMask },
+{"rsnProgramStoreBlendFunc",         "(III)V",                                (void*)nProgramStoreBlendFunc },
+{"rsnProgramStoreDither",            "(IZ)V",                                 (void*)nProgramStoreDither },
+{"rsnProgramStoreCreate",            "(I)I",                                  (void*)nProgramStoreCreate },
 
-{"nProgramVertexCreate",           "(Z)I",                                 (void*)nProgramVertexCreate },
-{"nProgramVertexCreate2",          "(Ljava/lang/String;[I)I",              (void*)nProgramVertexCreate2 },
+{"rsnProgramBindConstants",          "(IIII)V",                               (void*)nProgramBindConstants },
+{"rsnProgramBindTexture",            "(IIII)V",                               (void*)nProgramBindTexture },
+{"rsnProgramBindSampler",            "(IIII)V",                               (void*)nProgramBindSampler },
 
-{"nLightBegin",                    "()V",                                  (void*)nLightBegin },
-{"nLightSetIsMono",                "(Z)V",                                 (void*)nLightSetIsMono },
-{"nLightSetIsLocal",               "(Z)V",                                 (void*)nLightSetIsLocal },
-{"nLightCreate",                   "()I",                                  (void*)nLightCreate },
-{"nLightSetColor",                 "(IFFF)V",                              (void*)nLightSetColor },
-{"nLightSetPosition",              "(IFFF)V",                              (void*)nLightSetPosition },
+{"rsnProgramFragmentCreate",        "(ILjava/lang/String;[I)I",               (void*)nProgramFragmentCreate },
 
-{"nContextBindRootScript",         "(I)V",                                 (void*)nContextBindRootScript },
-{"nContextBindProgramFragmentStore","(I)V",                                (void*)nContextBindProgramFragmentStore },
-{"nContextBindProgramFragment",    "(I)V",                                 (void*)nContextBindProgramFragment },
-{"nContextBindProgramVertex",      "(I)V",                                 (void*)nContextBindProgramVertex },
-{"nContextBindProgramRaster",      "(I)V",                                 (void*)nContextBindProgramRaster },
+{"rsnProgramRasterCreate",           "(IZZZ)I",                               (void*)nProgramRasterCreate },
+{"rsnProgramRasterSetLineWidth",     "(IIF)V",                                (void*)nProgramRasterSetLineWidth },
+{"rsnProgramRasterSetCullMode",      "(III)V",                                (void*)nProgramRasterSetCullMode },
 
-{"nSamplerBegin",                  "()V",                                  (void*)nSamplerBegin },
-{"nSamplerSet",                    "(II)V",                                (void*)nSamplerSet },
-{"nSamplerCreate",                 "()I",                                  (void*)nSamplerCreate },
+{"rsnProgramVertexCreate",          "(ILjava/lang/String;[I)I",               (void*)nProgramVertexCreate },
 
-{"nSimpleMeshCreate",              "(II[II)I",                             (void*)nSimpleMeshCreate },
-{"nSimpleMeshBindVertex",          "(III)V",                               (void*)nSimpleMeshBindVertex },
-{"nSimpleMeshBindIndex",           "(II)V",                                (void*)nSimpleMeshBindIndex },
+{"rsnContextBindRootScript",         "(II)V",                                 (void*)nContextBindRootScript },
+{"rsnContextBindProgramStore",       "(II)V",                                 (void*)nContextBindProgramStore },
+{"rsnContextBindProgramFragment",    "(II)V",                                 (void*)nContextBindProgramFragment },
+{"rsnContextBindProgramVertex",      "(II)V",                                 (void*)nContextBindProgramVertex },
+{"rsnContextBindProgramRaster",      "(II)V",                                 (void*)nContextBindProgramRaster },
+
+{"rsnSamplerBegin",                  "(I)V",                                  (void*)nSamplerBegin },
+{"rsnSamplerSet",                    "(III)V",                                (void*)nSamplerSet },
+{"rsnSamplerSet2",                   "(IIF)V",                                (void*)nSamplerSet2 },
+{"rsnSamplerCreate",                 "(I)I",                                  (void*)nSamplerCreate },
+
+{"rsnMeshCreate",                    "(III)I",                                (void*)nMeshCreate },
+{"rsnMeshBindVertex",                "(IIII)V",                               (void*)nMeshBindVertex },
+{"rsnMeshBindIndex",                 "(IIIII)V",                              (void*)nMeshBindIndex },
+
+{"rsnMeshGetVertexBufferCount",      "(II)I",                                 (void*)nMeshGetVertexBufferCount },
+{"rsnMeshGetIndexCount",             "(II)I",                                 (void*)nMeshGetIndexCount },
+{"rsnMeshGetVertices",               "(II[II)V",                              (void*)nMeshGetVertices },
+{"rsnMeshGetIndices",                "(II[I[II)V",                            (void*)nMeshGetIndices },
 
 };
 
@@ -1510,3 +1372,4 @@
 bail:
     return result;
 }
+
diff --git a/graphics/tests/graphicstests/src/android/graphics/BitmapFactoryTest.java b/graphics/tests/graphicstests/src/android/graphics/BitmapFactoryTest.java
new file mode 100644
index 0000000..09820ef
--- /dev/null
+++ b/graphics/tests/graphicstests/src/android/graphics/BitmapFactoryTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.os.ParcelFileDescriptor;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
+
+import junit.framework.TestCase;
+
+
+public class BitmapFactoryTest extends TestCase {
+
+    // tests that we can decode bitmaps from MemoryFiles
+    @SmallTest
+    public void testBitmapParcelFileDescriptor() throws Exception {
+        Bitmap bitmap1 = Bitmap.createBitmap(
+                new int[] { Color.BLUE }, 1, 1, Bitmap.Config.RGB_565);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        bitmap1.compress(Bitmap.CompressFormat.PNG, 100, out);
+        ParcelFileDescriptor pfd = ParcelFileDescriptor.fromData(out.toByteArray(), null);
+        FileDescriptor fd = pfd.getFileDescriptor();
+        assertNotNull("Got null FileDescriptor", fd);
+        assertTrue("Got invalid FileDescriptor", fd.valid());
+        Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd);
+        assertNotNull("BitmapFactory returned null", bitmap);
+        assertEquals("Bitmap width", 1, bitmap.getWidth());
+        assertEquals("Bitmap height", 1, bitmap.getHeight());
+    }
+
+}
diff --git a/icu4j/java/android/icu/text/ArabicShaping.java b/icu4j/java/android/icu/text/ArabicShaping.java
new file mode 100644
index 0000000..13e2175
--- /dev/null
+++ b/icu4j/java/android/icu/text/ArabicShaping.java
@@ -0,0 +1,1947 @@
+/*
+*******************************************************************************
+*   Copyright (C) 2001-2009, International Business Machines
+*   Corporation and others.  All Rights Reserved.
+*******************************************************************************
+*/
+
+/*
+ * Ported with minor modifications from ICU4J 4.2's
+ * com.ibm.icu.text.ArabicShaping class.
+ */
+
+package android.icu.text;
+
+
+/**
+ * Shape Arabic text on a character basis.
+ *
+ * <p>ArabicShaping performs basic operations for "shaping" Arabic text. It is most
+ * useful for use with legacy data formats and legacy display technology
+ * (simple terminals). All operations are performed on Unicode characters.</p>
+ *
+ * <p>Text-based shaping means that some character code points in the text are
+ * replaced by others depending on the context. It transforms one kind of text
+ * into another. In comparison, modern displays for Arabic text select
+ * appropriate, context-dependent font glyphs for each text element, which means
+ * that they transform text into a glyph vector.</p>
+ *
+ * <p>Text transformations are necessary when modern display technology is not
+ * available or when text needs to be transformed to or from legacy formats that
+ * use "shaped" characters. Since the Arabic script is cursive, connecting
+ * adjacent letters to each other, computers select images for each letter based
+ * on the surrounding letters. This usually results in four images per Arabic
+ * letter: initial, middle, final, and isolated forms. In Unicode, on the other
+ * hand, letters are normally stored abstract, and a display system is expected
+ * to select the necessary glyphs. (This makes searching and other text
+ * processing easier because the same letter has only one code.) It is possible
+ * to mimic this with text transformations because there are characters in
+ * Unicode that are rendered as letters with a specific shape
+ * (or cursive connectivity). They were included for interoperability with
+ * legacy systems and codepages, and for unsophisticated display systems.</p>
+ *
+ * <p>A second kind of text transformations is supported for Arabic digits:
+ * For compatibility with legacy codepages that only include European digits,
+ * it is possible to replace one set of digits by another, changing the
+ * character code points. These operations can be performed for either
+ * Arabic-Indic Digits (U+0660...U+0669) or Eastern (Extended) Arabic-Indic
+ * digits (U+06f0...U+06f9).</p>
+ *
+ * <p>Some replacements may result in more or fewer characters (code points).
+ * By default, this means that the destination buffer may receive text with a
+ * length different from the source length. Some legacy systems rely on the
+ * length of the text to be constant. They expect extra spaces to be added
+ * or consumed either next to the affected character or at the end of the
+ * text.</p>
+ * @stable ICU 2.0
+ *
+ * @hide
+ */
+public class ArabicShaping {
+    private final int options;
+    private boolean isLogical; // convenience
+    private boolean spacesRelativeToTextBeginEnd;
+    private char tailChar;
+
+    public static final ArabicShaping SHAPER = new ArabicShaping(
+            ArabicShaping.TEXT_DIRECTION_LOGICAL |
+            ArabicShaping.LENGTH_FIXED_SPACES_NEAR |
+            ArabicShaping.LETTERS_SHAPE |
+            ArabicShaping.DIGITS_NOOP);
+
+    /**
+     * Convert a range of text in the source array, putting the result
+     * into a range of text in the destination array, and return the number
+     * of characters written.
+     *
+     * @param source An array containing the input text
+     * @param sourceStart The start of the range of text to convert
+     * @param sourceLength The length of the range of text to convert
+     * @param dest The destination array that will receive the result.
+     *   It may be <code>NULL</code> only if  <code>destSize</code> is 0.
+     * @param destStart The start of the range of the destination buffer to use.
+     * @param destSize The size (capacity) of the destination buffer.
+     *   If <code>destSize</code> is 0, then no output is produced,
+     *   but the necessary buffer size is returned ("preflighting").  This
+     *   does not validate the text against the options, for example,
+     *   if letters are being unshaped, and spaces are being consumed
+     *   following lamalef, this will not detect a lamalef without a
+     *   corresponding space.  An error will be thrown when the actual
+     *   conversion is attempted.
+     * @return The number of chars written to the destination buffer.
+     *   If an error occurs, then no output was written, or it may be
+     *   incomplete.
+     * @throws ArabicShapingException if the text cannot be converted according to the options.
+     * @stable ICU 2.0
+     */
+    public int shape(char[] source, int sourceStart, int sourceLength,
+                     char[] dest, int destStart, int destSize) throws ArabicShapingException {
+        if (source == null) {
+            throw new IllegalArgumentException("source can not be null");
+        }
+        if (sourceStart < 0 || sourceLength < 0 || sourceStart + sourceLength > source.length) {
+            throw new IllegalArgumentException("bad source start (" + sourceStart +
+                                               ") or length (" + sourceLength +
+                                               ") for buffer of length " + source.length);
+        }
+        if (dest == null && destSize != 0) {
+            throw new IllegalArgumentException("null dest requires destSize == 0");
+        }
+        if ((destSize != 0) &&
+            (destStart < 0 || destSize < 0 || destStart + destSize > dest.length)) {
+            throw new IllegalArgumentException("bad dest start (" + destStart +
+                                               ") or size (" + destSize +
+                                               ") for buffer of length " + dest.length);
+        }
+        /* Validate input options */
+        if ( ((options&TASHKEEL_MASK) > 0) &&
+             !(((options & TASHKEEL_MASK)==TASHKEEL_BEGIN)  ||
+               ((options & TASHKEEL_MASK)==TASHKEEL_END )   ||
+               ((options & TASHKEEL_MASK)==TASHKEEL_RESIZE )||
+               ((options & TASHKEEL_MASK)==TASHKEEL_REPLACE_BY_TATWEEL)) ){
+            throw new IllegalArgumentException("Wrong Tashkeel argument");
+        }
+
+       ///CLOVER:OFF
+       //According to Steven Loomis, the code is unreachable when you OR all the constants within the if statements
+       if(((options&LAMALEF_MASK) > 0)&&
+              !(((options & LAMALEF_MASK)==LAMALEF_BEGIN)  ||
+                ((options & LAMALEF_MASK)==LAMALEF_END )   ||
+                ((options & LAMALEF_MASK)==LAMALEF_RESIZE )||
+                 ((options & LAMALEF_MASK)==LAMALEF_AUTO)  ||
+                 ((options & LAMALEF_MASK)==LAMALEF_NEAR))){
+           throw new IllegalArgumentException("Wrong Lam Alef argument");
+       }
+       ///CLOVER:ON
+
+       /* Validate Tashkeel (Tashkeel replacement options should be enabled in shaping mode only)*/
+       if(((options&TASHKEEL_MASK) > 0) && (options&LETTERS_MASK) == LETTERS_UNSHAPE) {
+            throw new IllegalArgumentException("Tashkeel replacement should not be enabled in deshaping mode ");
+       }
+       return internalShape(source, sourceStart, sourceLength, dest, destStart, destSize);
+    }
+
+    /**
+     * Convert a range of text in place.  This may only be used if the Length option
+     * does not grow or shrink the text.
+     *
+     * @param source An array containing the input text
+     * @param start The start of the range of text to convert
+     * @param length The length of the range of text to convert
+     * @throws ArabicShapingException if the text cannot be converted according to the options.
+     * @stable ICU 2.0
+     */
+    public void shape(char[] source, int start, int length) throws ArabicShapingException {
+        if ((options & LAMALEF_MASK) == LAMALEF_RESIZE) {
+            throw new ArabicShapingException("Cannot shape in place with length option resize.");
+        }
+        shape(source, start, length, source, start, length);
+    }
+
+    /**
+     * Convert a string, returning the new string.
+     *
+     * @param text the string to convert
+     * @return the converted string
+     * @throws ArabicShapingException if the string cannot be converted according to the options.
+     * @stable ICU 2.0
+     */
+    public String shape(String text) throws ArabicShapingException {
+        char[] src = text.toCharArray();
+        char[] dest = src;
+        if (((options & LAMALEF_MASK) == LAMALEF_RESIZE) &&
+            ((options & LETTERS_MASK) == LETTERS_UNSHAPE)) {
+
+            dest = new char[src.length * 2]; // max
+        }
+        int len = shape(src, 0, src.length, dest, 0, dest.length);
+
+        return new String(dest, 0, len);
+    }
+
+    /**
+     * Construct ArabicShaping using the options flags.
+     * The flags are as follows:<br>
+     * 'LENGTH' flags control whether the text can change size, and if not,
+     * how to maintain the size of the text when LamAlef ligatures are
+     * formed or broken.<br>
+     * 'TEXT_DIRECTION' flags control whether the text is read and written
+     * in visual order or in logical order.<br>
+     * 'LETTERS_SHAPE' flags control whether conversion is to or from
+     * presentation forms.<br>
+     * 'DIGITS' flags control whether digits are shaped, and whether from
+     * European to Arabic-Indic or vice-versa.<br>
+     * 'DIGIT_TYPE' flags control whether standard or extended Arabic-Indic
+     * digits are used when performing digit conversion.
+     * @stable ICU 2.0
+     */
+    public ArabicShaping(int options) {
+        this.options = options;
+        if ((options & DIGITS_MASK) > 0x80) {
+            throw new IllegalArgumentException("bad DIGITS options");
+        }
+
+        isLogical = ( (options & TEXT_DIRECTION_MASK) == TEXT_DIRECTION_LOGICAL );
+        /* Validate options */
+        spacesRelativeToTextBeginEnd = ( (options & SPACES_RELATIVE_TO_TEXT_MASK) == SPACES_RELATIVE_TO_TEXT_BEGIN_END );
+        if ( (options&SHAPE_TAIL_TYPE_MASK) == SHAPE_TAIL_NEW_UNICODE){
+            tailChar = NEW_TAIL_CHAR;
+        } else {
+            tailChar = OLD_TAIL_CHAR;
+        }
+    }
+
+    /* Seen Tail options */
+    /**
+     * Memory option: the result must have the same length as the source.
+     * Shaping mode: The SEEN family character will expand into two characters using space near
+     *               the SEEN family character(i.e. the space after the character).
+     *               if there are no spaces found, ArabicShapingException will be thrown
+     *
+     * De-shaping mode: Any Seen character followed by Tail character will be
+     *                  replaced by one cell Seen and a space will replace the Tail.
+     * Affects: Seen options
+     */
+    public static final int SEEN_TWOCELL_NEAR = 0x200000;
+
+    /** Bit mask for Seen memory options. */
+    public static final int SEEN_MASK = 0x700000;
+
+    /* YehHamza options */
+    /**
+     * Memory option: the result must have the same length as the source.
+     * Shaping mode: The YEHHAMZA character will expand into two characters using space near it
+     *              (i.e. the space after the character)
+     *               if there are no spaces found, ArabicShapingException will be thrown
+     *
+     * De-shaping mode: Any Yeh (final or isolated) character followed by Hamza character will be
+     *                  replaced by one cell YehHamza and space will replace the Hamza.
+     * Affects: YehHamza options
+     */
+    public static final int YEHHAMZA_TWOCELL_NEAR  = 0x1000000;
+
+
+    /** Bit mask for YehHamza memory options. */
+    public static final int YEHHAMZA_MASK = 0x3800000;
+
+    /* New Tashkeel options */
+    /**
+     * Memory option: the result must have the same length as the source.
+     * Shaping mode: Tashkeel characters will be replaced by spaces.
+     *               Spaces will be placed at beginning of the buffer
+     *
+     * De-shaping mode: N/A
+     * Affects: Tashkeel options
+     */
+    public static final int TASHKEEL_BEGIN = 0x40000;
+
+    /**
+     * Memory option: the result must have the same length as the source.
+     * Shaping mode: Tashkeel characters will be replaced by spaces.
+     *               Spaces will be placed at end of the buffer
+     *
+     * De-shaping mode: N/A
+     * Affects: Tashkeel options
+     */
+    public static final int TASHKEEL_END = 0x60000;
+
+    /**
+     * Memory option: allow the result to have a different length than the source.
+     * Shaping mode: Tashkeel characters will be removed, buffer length will shrink.
+     * De-shaping mode: N/A
+     *
+     * Affects: Tashkeel options
+     */
+    public static final int TASHKEEL_RESIZE = 0x80000;
+
+    /**
+     * Memory option: the result must have the same length as the source.
+     * Shaping mode: Tashkeel characters will be replaced by Tatweel if it is connected to adjacent
+     *               characters (i.e. shaped on Tatweel) or replaced by space if it is not connected.
+     *
+     * De-shaping mode: N/A
+     * Affects: YehHamza options
+     */
+    public static final int TASHKEEL_REPLACE_BY_TATWEEL = 0xC0000;
+
+    /** Bit mask for Tashkeel replacement with Space or Tatweel memory options. */
+    public static final int TASHKEEL_MASK  = 0xE0000;
+
+    /* Space location Control options */
+    /**
+     * This option effects the meaning of BEGIN and END options. if this option is not used the default
+     * for BEGIN and END will be as following:
+     * The Default (for both Visual LTR, Visual RTL and Logical Text)
+     *           1. BEGIN always refers to the start address of physical memory.
+     *           2. END always refers to the end address of physical memory.
+     *
+     * If this option is used it will swap the meaning of BEGIN and END only for Visual LTR text.
+     *
+     * The affect on BEGIN and END Memory Options will be as following:
+     *    A. BEGIN For Visual LTR text: This will be the beginning (right side) of the visual text
+     *       (corresponding to the physical memory address end, same as END in default behavior)
+     *    B. BEGIN For Logical text: Same as BEGIN in default behavior.
+     *    C. END For Visual LTR text: This will be the end (left side) of the visual text. (corresponding to
+     *      the physical memory address beginning, same as BEGIN in default behavior)
+     *    D. END For Logical text: Same as END in default behavior.
+     * Affects: All LamAlef BEGIN, END and AUTO options.
+     */
+    public static final int SPACES_RELATIVE_TO_TEXT_BEGIN_END = 0x4000000;
+
+    /** Bit mask for swapping BEGIN and END for Visual LTR text */
+    public static final int SPACES_RELATIVE_TO_TEXT_MASK = 0x4000000;
+
+    /**
+     * If this option is used, shaping will use the new Unicode code point for TAIL (i.e. 0xFE73).
+     * If this option is not specified (Default), old unofficial Unicode TAIL code point is used (i.e. 0x200B)
+     * De-shaping will not use this option as it will always search for both the new Unicode code point for the
+     * TAIL (i.e. 0xFE73) or the old unofficial Unicode TAIL code point (i.e. 0x200B) and de-shape the
+     * Seen-Family letter accordingly.
+     *
+     * Shaping Mode: Only shaping.
+     * De-shaping Mode: N/A.
+     * Affects: All Seen options
+     */
+    public static final int SHAPE_TAIL_NEW_UNICODE = 0x8000000;
+
+    /** Bit mask for new Unicode Tail option */
+    public static final int SHAPE_TAIL_TYPE_MASK = 0x8000000;
+
+    /**
+     * Memory option: allow the result to have a different length than the source.
+     * @stable ICU 2.0
+     */
+    public static final int LENGTH_GROW_SHRINK = 0;
+
+    /**
+     * Memory option: allow the result to have a different length than the source.
+     * Affects: LamAlef options
+     * This option is an alias to LENGTH_GROW_SHRINK
+     */
+    public static final int LAMALEF_RESIZE   = 0;
+
+    /**
+     * Memory option: the result must have the same length as the source.
+     * If more room is necessary, then try to consume spaces next to modified characters.
+     * @stable ICU 2.0
+     */
+    public static final int LENGTH_FIXED_SPACES_NEAR = 1;
+
+    /**
+     * Memory option: the result must have the same length as the source.
+     * If more room is necessary, then try to consume spaces next to modified characters.
+     * Affects: LamAlef options
+     * This option is an alias to LENGTH_FIXED_SPACES_NEAR
+     */
+    public static final int LAMALEF_NEAR = 1 ;
+
+    /**
+     * Memory option: the result must have the same length as the source.
+     * If more room is necessary, then try to consume spaces at the end of the text.
+     * @stable ICU 2.0
+     */
+    public static final int LENGTH_FIXED_SPACES_AT_END = 2;
+
+
+    /**
+     * Memory option: the result must have the same length as the source.
+     * If more room is necessary, then try to consume spaces at the end of the text.
+     * Affects: LamAlef options
+     * This option is an alias to LENGTH_FIXED_SPACES_AT_END
+     */
+    public static final int LAMALEF_END = 2;
+
+    /**
+     * Memory option: the result must have the same length as the source.
+     * If more room is necessary, then try to consume spaces at the beginning of the text.
+     * @stable ICU 2.0
+     */
+    public static final int LENGTH_FIXED_SPACES_AT_BEGINNING = 3;
+
+    /**
+     * Memory option: the result must have the same length as the source.
+     * If more room is necessary, then try to consume spaces at the beginning of the text.
+     * Affects: LamAlef options
+     * This option is an alias to LENGTH_FIXED_SPACES_AT_BEGINNING
+     */
+    public static final int LAMALEF_BEGIN = 3;
+
+    /**
+     * Memory option: the result must have the same length as the source.
+     * Shaping Mode: For each LAMALEF character found, expand LAMALEF using space at end.
+     *               If there is no space at end, use spaces at beginning of the buffer. If there
+     *               is no space at beginning of the buffer, use spaces at the near (i.e. the space
+     *               after the LAMALEF character).
+     *
+     * Deshaping Mode: Perform the same function as the flag equals LAMALEF_END.
+     * Affects: LamAlef options
+     */
+    public static final int LAMALEF_AUTO  = 0x10000;
+
+    /**
+     * Bit mask for memory options.
+     * @stable ICU 2.0
+     */
+    public static final int LENGTH_MASK = 0x10003;
+
+    /** Bit mask for LamAlef memory options. */
+
+    public static final int LAMALEF_MASK  = 0x10003;
+
+    /**
+     * Direction indicator: the source is in logical (keyboard) order.
+     * @stable ICU 2.0
+     */
+    public static final int TEXT_DIRECTION_LOGICAL = 0;
+
+    /**
+     * Direction indicator:the source is in visual RTL order,
+     * the rightmost displayed character stored first.
+     * This option is an alias to U_SHAPE_TEXT_DIRECTION_LOGICAL
+     */
+    public static final int TEXT_DIRECTION_VISUAL_RTL = 0;
+
+    /**
+     * Direction indicator: the source is in visual (display) order, that is,
+     * the leftmost displayed character is stored first.
+     * @stable ICU 2.0
+     */
+    public static final int TEXT_DIRECTION_VISUAL_LTR = 4;
+
+    /**
+     * Bit mask for direction indicators.
+     * @stable ICU 2.0
+     */
+    public static final int TEXT_DIRECTION_MASK = 4;
+
+
+    /**
+     * Letter shaping option: do not perform letter shaping.
+     * @stable ICU 2.0
+     */
+    public static final int LETTERS_NOOP = 0;
+
+    /**
+     * Letter shaping option: replace normative letter characters in the U+0600 (Arabic) block,
+     * by shaped ones in the U+FE70 (Presentation Forms B) block. Performs Lam-Alef ligature
+     * substitution.
+     * @stable ICU 2.0
+     */
+    public static final int LETTERS_SHAPE = 8;
+
+    /**
+     * Letter shaping option: replace shaped letter characters in the U+FE70 (Presentation Forms B) block
+     * by normative ones in the U+0600 (Arabic) block.  Converts Lam-Alef ligatures to pairs of Lam and
+     * Alef characters, consuming spaces if required.
+     * @stable ICU 2.0
+     */
+    public static final int LETTERS_UNSHAPE = 0x10;
+
+    /**
+     * Letter shaping option: replace normative letter characters in the U+0600 (Arabic) block,
+     * except for the TASHKEEL characters at U+064B...U+0652, by shaped ones in the U+Fe70
+     * (Presentation Forms B) block.  The TASHKEEL characters will always be converted to
+     * the isolated forms rather than to their correct shape.
+     * @stable ICU 2.0
+     */
+    public static final int LETTERS_SHAPE_TASHKEEL_ISOLATED = 0x18;
+
+    /**
+     * Bit mask for letter shaping options.
+     * @stable ICU 2.0
+     */
+    public static final int LETTERS_MASK = 0x18;
+
+
+    /**
+     * Digit shaping option: do not perform digit shaping.
+     * @stable ICU 2.0
+     */
+    public static final int DIGITS_NOOP = 0;
+
+    /**
+     * Digit shaping option: Replace European digits (U+0030...U+0039) by Arabic-Indic digits.
+     * @stable ICU 2.0
+     */
+    public static final int DIGITS_EN2AN = 0x20;
+
+    /**
+     * Digit shaping option: Replace Arabic-Indic digits by European digits (U+0030...U+0039).
+     * @stable ICU 2.0
+     */
+    public static final int DIGITS_AN2EN = 0x40;
+
+    /**
+     * Digit shaping option:
+     * Replace European digits (U+0030...U+0039) by Arabic-Indic digits
+     * if the most recent strongly directional character
+     * is an Arabic letter (its Bidi direction value is RIGHT_TO_LEFT_ARABIC).
+     * The initial state at the start of the text is assumed to be not an Arabic,
+     * letter, so European digits at the start of the text will not change.
+     * Compare to DIGITS_ALEN2AN_INIT_AL.
+     * @stable ICU 2.0
+     */
+    public static final int DIGITS_EN2AN_INIT_LR = 0x60;
+
+    /**
+     * Digit shaping option:
+     * Replace European digits (U+0030...U+0039) by Arabic-Indic digits
+     * if the most recent strongly directional character
+     * is an Arabic letter (its Bidi direction value is RIGHT_TO_LEFT_ARABIC).
+     * The initial state at the start of the text is assumed to be an Arabic,
+     * letter, so European digits at the start of the text will change.
+     * Compare to DIGITS_ALEN2AN_INT_LR.
+     * @stable ICU 2.0
+     */
+    public static final int DIGITS_EN2AN_INIT_AL = 0x80;
+
+    /** Not a valid option value. */
+    //private static final int DIGITS_RESERVED = 0xa0;
+
+    /**
+     * Bit mask for digit shaping options.
+     * @stable ICU 2.0
+     */
+    public static final int DIGITS_MASK = 0xe0;
+
+    /**
+     * Digit type option: Use Arabic-Indic digits (U+0660...U+0669).
+     * @stable ICU 2.0
+     */
+    public static final int DIGIT_TYPE_AN = 0;
+
+    /**
+     * Digit type option: Use Eastern (Extended) Arabic-Indic digits (U+06f0...U+06f9).
+     * @stable ICU 2.0
+     */
+    public static final int DIGIT_TYPE_AN_EXTENDED = 0x100;
+
+    /**
+     * Bit mask for digit type options.
+     * @stable ICU 2.0
+     */
+    public static final int DIGIT_TYPE_MASK = 0x0100; // 0x3f00?
+
+    /**
+     * some constants
+     */
+    private static final char HAMZAFE_CHAR       = '\ufe80';
+    private static final char HAMZA06_CHAR       = '\u0621';
+    private static final char YEH_HAMZA_CHAR     = '\u0626';
+    private static final char YEH_HAMZAFE_CHAR   = '\uFE89';
+    private static final char LAMALEF_SPACE_SUB  = '\uffff';
+    private static final char TASHKEEL_SPACE_SUB = '\ufffe';
+    private static final char LAM_CHAR      = '\u0644';
+    private static final char SPACE_CHAR    = '\u0020';
+    private static final char SPACE_CHAR_FOR_LAMALEF = '\ufeff'; // XXX: tweak for TextLine use
+    private static final char SHADDA_CHAR   = '\uFE7C';
+    private static final char TATWEEL_CHAR  = '\u0640';
+    private static final char SHADDA_TATWEEL_CHAR = '\uFE7D';
+    private static final char NEW_TAIL_CHAR = '\uFE73';
+    private static final char OLD_TAIL_CHAR = '\u200B';
+    private static final int SHAPE_MODE      = 0;
+    private static final int DESHAPE_MODE    = 1;
+
+    /**
+     * @stable ICU 2.0
+     */
+    public boolean equals(Object rhs) {
+        return rhs != null &&
+            rhs.getClass() == ArabicShaping.class &&
+            options == ((ArabicShaping)rhs).options;
+    }
+
+    /**
+     * @stable ICU 2.0
+     */
+     ///CLOVER:OFF
+    public int hashCode() {
+        return options;
+    }
+
+    /**
+     * @stable ICU 2.0
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer(super.toString());
+        buf.append('[');
+
+        switch (options & LAMALEF_MASK) {
+        case LAMALEF_RESIZE: buf.append("LamAlef resize"); break;
+        case LAMALEF_NEAR: buf.append("LamAlef spaces at near"); break;
+        case LAMALEF_BEGIN: buf.append("LamAlef spaces at begin"); break;
+        case LAMALEF_END: buf.append("LamAlef spaces at end"); break;
+        case LAMALEF_AUTO: buf.append("lamAlef auto"); break;
+        }
+        switch (options & TEXT_DIRECTION_MASK) {
+        case TEXT_DIRECTION_LOGICAL: buf.append(", logical"); break;
+        case TEXT_DIRECTION_VISUAL_LTR: buf.append(", visual"); break;
+        }
+        switch (options & LETTERS_MASK) {
+        case LETTERS_NOOP: buf.append(", no letter shaping"); break;
+        case LETTERS_SHAPE: buf.append(", shape letters"); break;
+        case LETTERS_SHAPE_TASHKEEL_ISOLATED: buf.append(", shape letters tashkeel isolated"); break;
+        case LETTERS_UNSHAPE: buf.append(", unshape letters"); break;
+        }
+        switch (options & SEEN_MASK) {
+        case SEEN_TWOCELL_NEAR: buf.append(", Seen at near"); break;
+        }
+        switch (options & YEHHAMZA_MASK) {
+        case YEHHAMZA_TWOCELL_NEAR: buf.append(", Yeh Hamza at near"); break;
+        }
+        switch (options & TASHKEEL_MASK) {
+        case TASHKEEL_BEGIN: buf.append(", Tashkeel at begin"); break;
+        case TASHKEEL_END: buf.append(", Tashkeel at end"); break;
+        case TASHKEEL_REPLACE_BY_TATWEEL: buf.append(", Tashkeel replace with tatweel"); break;
+        case TASHKEEL_RESIZE: buf.append(", Tashkeel resize"); break;
+        }
+
+        switch (options & DIGITS_MASK) {
+        case DIGITS_NOOP: buf.append(", no digit shaping"); break;
+        case DIGITS_EN2AN: buf.append(", shape digits to AN"); break;
+        case DIGITS_AN2EN: buf.append(", shape digits to EN"); break;
+        case DIGITS_EN2AN_INIT_LR: buf.append(", shape digits to AN contextually: default EN"); break;
+        case DIGITS_EN2AN_INIT_AL: buf.append(", shape digits to AN contextually: default AL"); break;
+        }
+        switch (options & DIGIT_TYPE_MASK) {
+        case DIGIT_TYPE_AN: buf.append(", standard Arabic-Indic digits"); break;
+        case DIGIT_TYPE_AN_EXTENDED: buf.append(", extended Arabic-Indic digits"); break;
+        }
+        buf.append("]");
+
+        return buf.toString();
+    }
+    ///CLOVER:ON
+
+    //
+    // ported api
+    //
+
+    private static final int IRRELEVANT = 4;
+    private static final int LAMTYPE = 16;
+    private static final int ALEFTYPE = 32;
+
+    private static final int LINKR = 1;
+    private static final int LINKL = 2;
+    private static final int LINK_MASK = 3;
+
+    private static final int irrelevantPos[] = {
+        0x0, 0x2, 0x4, 0x6, 0x8, 0xA, 0xC, 0xE
+    };
+
+/*
+    private static final char convertLamAlef[] =  {
+        '\u0622', // FEF5
+        '\u0622', // FEF6
+        '\u0623', // FEF7
+        '\u0623', // FEF8
+        '\u0625', // FEF9
+        '\u0625', // FEFA
+        '\u0627', // FEFB
+        '\u0627'  // FEFC
+    };
+*/
+
+    private static final int tailFamilyIsolatedFinal[] = {
+        /* FEB1 */ 1,
+        /* FEB2 */ 1,
+        /* FEB3 */ 0,
+        /* FEB4 */ 0,
+        /* FEB5 */ 1,
+        /* FEB6 */ 1,
+        /* FEB7 */ 0,
+        /* FEB8 */ 0,
+        /* FEB9 */ 1,
+        /* FEBA */ 1,
+        /* FEBB */ 0,
+        /* FEBC */ 0,
+        /* FEBD */ 1,
+        /* FEBE */ 1
+    };
+
+    private static final int tashkeelMedial[] = {
+        /* FE70 */ 0,
+        /* FE71 */ 1,
+        /* FE72 */ 0,
+        /* FE73 */ 0,
+        /* FE74 */ 0,
+        /* FE75 */ 0,
+        /* FE76 */ 0,
+        /* FE77 */ 1,
+        /* FE78 */ 0,
+        /* FE79 */ 1,
+        /* FE7A */ 0,
+        /* FE7B */ 1,
+        /* FE7C */ 0,
+        /* FE7D */ 1,
+        /* FE7E */ 0,
+        /* FE7F */ 1
+    };
+
+    private static final char yehHamzaToYeh[] =
+    {
+    /* isolated*/ 0xFEEF,
+    /* final   */ 0xFEF0
+    };
+
+    private static final char convertNormalizedLamAlef[] = {
+        '\u0622', // 065C
+        '\u0623', // 065D
+        '\u0625', // 065E
+        '\u0627', // 065F
+    };
+
+    private static final int[] araLink = {
+        1           + 32 + 256 * 0x11,  /*0x0622*/
+        1           + 32 + 256 * 0x13,  /*0x0623*/
+        1                + 256 * 0x15,  /*0x0624*/
+        1           + 32 + 256 * 0x17,  /*0x0625*/
+        1 + 2            + 256 * 0x19,  /*0x0626*/
+        1           + 32 + 256 * 0x1D,  /*0x0627*/
+        1 + 2            + 256 * 0x1F,  /*0x0628*/
+        1                + 256 * 0x23,  /*0x0629*/
+        1 + 2            + 256 * 0x25,  /*0x062A*/
+        1 + 2            + 256 * 0x29,  /*0x062B*/
+        1 + 2            + 256 * 0x2D,  /*0x062C*/
+        1 + 2            + 256 * 0x31,  /*0x062D*/
+        1 + 2            + 256 * 0x35,  /*0x062E*/
+        1                + 256 * 0x39,  /*0x062F*/
+        1                + 256 * 0x3B,  /*0x0630*/
+        1                + 256 * 0x3D,  /*0x0631*/
+        1                + 256 * 0x3F,  /*0x0632*/
+        1 + 2            + 256 * 0x41,  /*0x0633*/
+        1 + 2            + 256 * 0x45,  /*0x0634*/
+        1 + 2            + 256 * 0x49,  /*0x0635*/
+        1 + 2            + 256 * 0x4D,  /*0x0636*/
+        1 + 2            + 256 * 0x51,  /*0x0637*/
+        1 + 2            + 256 * 0x55,  /*0x0638*/
+        1 + 2            + 256 * 0x59,  /*0x0639*/
+        1 + 2            + 256 * 0x5D,  /*0x063A*/
+        0, 0, 0, 0, 0,                  /*0x063B-0x063F*/
+        1 + 2,                          /*0x0640*/
+        1 + 2            + 256 * 0x61,  /*0x0641*/
+        1 + 2            + 256 * 0x65,  /*0x0642*/
+        1 + 2            + 256 * 0x69,  /*0x0643*/
+        1 + 2       + 16 + 256 * 0x6D,  /*0x0644*/
+        1 + 2            + 256 * 0x71,  /*0x0645*/
+        1 + 2            + 256 * 0x75,  /*0x0646*/
+        1 + 2            + 256 * 0x79,  /*0x0647*/
+        1                + 256 * 0x7D,  /*0x0648*/
+        1                + 256 * 0x7F,  /*0x0649*/
+        1 + 2            + 256 * 0x81,  /*0x064A*/
+        4, 4, 4, 4,                     /*0x064B-0x064E*/
+        4, 4, 4, 4,                     /*0x064F-0x0652*/
+        4, 4, 4, 0, 0,                  /*0x0653-0x0657*/
+        0, 0, 0, 0,                     /*0x0658-0x065B*/
+        1                + 256 * 0x85,  /*0x065C*/
+        1                + 256 * 0x87,  /*0x065D*/
+        1                + 256 * 0x89,  /*0x065E*/
+        1                + 256 * 0x8B,  /*0x065F*/
+        0, 0, 0, 0, 0,                  /*0x0660-0x0664*/
+        0, 0, 0, 0, 0,                  /*0x0665-0x0669*/
+        0, 0, 0, 0, 0, 0,               /*0x066A-0x066F*/
+        4,                              /*0x0670*/
+        0,                              /*0x0671*/
+        1           + 32,               /*0x0672*/
+        1           + 32,               /*0x0673*/
+        0,                              /*0x0674*/
+        1           + 32,               /*0x0675*/
+        1, 1,                           /*0x0676-0x0677*/
+        1+2, 1+2, 1+2, 1+2, 1+2, 1+2,   /*0x0678-0x067D*/
+        1+2, 1+2, 1+2, 1+2, 1+2, 1+2,   /*0x067E-0x0683*/
+        1+2, 1+2, 1+2, 1+2,             /*0x0684-0x0687*/
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1,   /*0x0688-0x0691*/
+        1, 1, 1, 1, 1, 1, 1, 1,         /*0x0692-0x0699*/
+        1+2, 1+2, 1+2, 1+2, 1+2, 1+2,   /*0x069A-0x06A3*/
+        1+2, 1+2, 1+2, 1+2,             /*0x069A-0x06A3*/
+        1+2, 1+2, 1+2, 1+2, 1+2, 1+2,   /*0x06A4-0x06AD*/
+        1+2, 1+2, 1+2, 1+2,             /*0x06A4-0x06AD*/
+        1+2, 1+2, 1+2, 1+2, 1+2, 1+2,   /*0x06AE-0x06B7*/
+        1+2, 1+2, 1+2, 1+2,             /*0x06AE-0x06B7*/
+        1+2, 1+2, 1+2, 1+2, 1+2, 1+2,   /*0x06B8-0x06BF*/
+        1+2, 1+2,                       /*0x06B8-0x06BF*/
+        1,                              /*0x06C0*/
+        1+2,                            /*0x06C1*/
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1,   /*0x06C2-0x06CB*/
+        1+2,                            /*0x06CC*/
+        1,                              /*0x06CD*/
+        1+2, 1+2, 1+2, 1+2,             /*0x06CE-0x06D1*/
+        1, 1                            /*0x06D2-0x06D3*/
+    };
+
+    private static final int[] presLink = {
+        1 + 2,                        /*0xFE70*/
+        1 + 2,                        /*0xFE71*/
+        1 + 2, 0, 1+ 2, 0, 1+ 2,      /*0xFE72-0xFE76*/
+        1 + 2,                        /*0xFE77*/
+        1+ 2, 1 + 2, 1+2, 1 + 2,      /*0xFE78-0xFE81*/
+        1+ 2, 1 + 2, 1+2, 1 + 2,      /*0xFE82-0xFE85*/
+        0, 0 + 32, 1 + 32, 0 + 32,    /*0xFE86-0xFE89*/
+        1 + 32, 0, 1,  0 + 32,        /*0xFE8A-0xFE8D*/
+        1 + 32, 0, 2,  1 + 2,         /*0xFE8E-0xFE91*/
+        1, 0 + 32, 1 + 32, 0,         /*0xFE92-0xFE95*/
+        2, 1 + 2, 1, 0,               /*0xFE96-0xFE99*/
+        1, 0, 2, 1 + 2,               /*0xFE9A-0xFE9D*/
+        1, 0, 2, 1 + 2,               /*0xFE9E-0xFEA1*/
+        1, 0, 2, 1 + 2,               /*0xFEA2-0xFEA5*/
+        1, 0, 2, 1 + 2,               /*0xFEA6-0xFEA9*/
+        1, 0, 2, 1 + 2,               /*0xFEAA-0xFEAD*/
+        1, 0, 1, 0,                   /*0xFEAE-0xFEB1*/
+        1, 0, 1, 0,                   /*0xFEB2-0xFEB5*/
+        1, 0, 2, 1+2,                 /*0xFEB6-0xFEB9*/
+        1, 0, 2, 1+2,                 /*0xFEBA-0xFEBD*/
+        1, 0, 2, 1+2,                 /*0xFEBE-0xFEC1*/
+        1, 0, 2, 1+2,                 /*0xFEC2-0xFEC5*/
+        1, 0, 2, 1+2,                 /*0xFEC6-0xFEC9*/
+        1, 0, 2, 1+2,                 /*0xFECA-0xFECD*/
+        1, 0, 2, 1+2,                 /*0xFECE-0xFED1*/
+        1, 0, 2, 1+2,                 /*0xFED2-0xFED5*/
+        1, 0, 2, 1+2,                 /*0xFED6-0xFED9*/
+        1, 0, 2, 1+2,                 /*0xFEDA-0xFEDD*/
+        1, 0, 2, 1+2,                 /*0xFEDE-0xFEE1*/
+        1, 0 + 16, 2 + 16, 1 + 2 +16, /*0xFEE2-0xFEE5*/
+        1 + 16, 0, 2, 1+2,            /*0xFEE6-0xFEE9*/
+        1, 0, 2, 1+2,                 /*0xFEEA-0xFEED*/
+        1, 0, 2, 1+2,                 /*0xFEEE-0xFEF1*/
+        1, 0, 1, 0,                   /*0xFEF2-0xFEF5*/
+        1, 0, 2, 1+2,                 /*0xFEF6-0xFEF9*/
+        1, 0, 1, 0,                   /*0xFEFA-0xFEFD*/
+        1, 0, 1, 0,
+        1
+    };
+
+    private static int[] convertFEto06 = {
+        /***********0******1******2******3******4******5******6******7******8******9******A******B******C******D******E******F***/
+        /*FE7*/   0x64B, 0x64B, 0x64C, 0x64C, 0x64D, 0x64D, 0x64E, 0x64E, 0x64F, 0x64F, 0x650, 0x650, 0x651, 0x651, 0x652, 0x652,
+        /*FE8*/   0x621, 0x622, 0x622, 0x623, 0x623, 0x624, 0x624, 0x625, 0x625, 0x626, 0x626, 0x626, 0x626, 0x627, 0x627, 0x628,
+        /*FE9*/   0x628, 0x628, 0x628, 0x629, 0x629, 0x62A, 0x62A, 0x62A, 0x62A, 0x62B, 0x62B, 0x62B, 0x62B, 0x62C, 0x62C, 0x62C,
+        /*FEA*/   0x62C, 0x62D, 0x62D, 0x62D, 0x62D, 0x62E, 0x62E, 0x62E, 0x62E, 0x62F, 0x62F, 0x630, 0x630, 0x631, 0x631, 0x632,
+        /*FEB*/   0x632, 0x633, 0x633, 0x633, 0x633, 0x634, 0x634, 0x634, 0x634, 0x635, 0x635, 0x635, 0x635, 0x636, 0x636, 0x636,
+        /*FEC*/   0x636, 0x637, 0x637, 0x637, 0x637, 0x638, 0x638, 0x638, 0x638, 0x639, 0x639, 0x639, 0x639, 0x63A, 0x63A, 0x63A,
+        /*FED*/   0x63A, 0x641, 0x641, 0x641, 0x641, 0x642, 0x642, 0x642, 0x642, 0x643, 0x643, 0x643, 0x643, 0x644, 0x644, 0x644,
+        /*FEE*/   0x644, 0x645, 0x645, 0x645, 0x645, 0x646, 0x646, 0x646, 0x646, 0x647, 0x647, 0x647, 0x647, 0x648, 0x648, 0x649,
+        /*FEF*/   0x649, 0x64A, 0x64A, 0x64A, 0x64A, 0x65C, 0x65C, 0x65D, 0x65D, 0x65E, 0x65E, 0x65F, 0x65F
+    };
+
+    private static final int shapeTable[][][] = {
+        { {0,0,0,0}, {0,0,0,0}, {0,1,0,3}, {0,1,0,1} },
+        { {0,0,2,2}, {0,0,1,2}, {0,1,1,2}, {0,1,1,3} },
+        { {0,0,0,0}, {0,0,0,0}, {0,1,0,3}, {0,1,0,3} },
+        { {0,0,1,2}, {0,0,1,2}, {0,1,1,2}, {0,1,1,3} }
+    };
+
+    /*
+     * This function shapes European digits to Arabic-Indic digits
+     * in-place, writing over the input characters.  Data is in visual
+     * order.
+     */
+    private void shapeToArabicDigitsWithContext(char[] dest,
+                                                int start,
+                                                int length,
+                                                char digitBase,
+                                                boolean lastStrongWasAL) {
+        digitBase -= '0'; // move common adjustment out of loop
+
+        for(int i = start + length; --i >= start;) {
+            char ch = dest[i];
+            switch (Character.getDirectionality(ch)) {
+            case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+                lastStrongWasAL = false;
+                break;
+            case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
+                lastStrongWasAL = true;
+                break;
+            case Character.DIRECTIONALITY_EUROPEAN_NUMBER:
+                if (lastStrongWasAL && ch <= '\u0039') {
+                    dest[i] = (char)(ch + digitBase);
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
+    /*
+     * Name    : invertBuffer
+     * Function: This function inverts the buffer, it's used
+     *           in case the user specifies the buffer to be
+     *           TEXT_DIRECTION_LOGICAL
+     */
+    private static void invertBuffer(char[] buffer,
+                                     int start,
+                                     int length) {
+
+        for(int i = start, j = start + length - 1; i < j; i++, --j) {
+            char temp = buffer[i];
+            buffer[i] = buffer[j];
+            buffer[j] = temp;
+        }
+    }
+
+    /*
+     * Name    : changeLamAlef
+     * Function: Converts the Alef characters into an equivalent
+     *           LamAlef location in the 0x06xx Range, this is an
+     *           intermediate stage in the operation of the program
+     *           later it'll be converted into the 0xFExx LamAlefs
+     *           in the shaping function.
+     */
+    private static char changeLamAlef(char ch) {
+        switch(ch) {
+        case '\u0622': return '\u065C';
+        case '\u0623': return '\u065D';
+        case '\u0625': return '\u065E';
+        case '\u0627': return '\u065F';
+        default:  return '\u0000'; // not a lamalef
+        }
+    }
+
+    /*
+     * Name    : specialChar
+     * Function: Special Arabic characters need special handling in the shapeUnicode
+     *           function, this function returns 1 or 2 for these special characters
+     */
+    private static int specialChar(char ch) {
+        if ((ch > '\u0621' && ch < '\u0626') ||
+            (ch == '\u0627') ||
+            (ch > '\u062E' && ch < '\u0633') ||
+            (ch > '\u0647' && ch < '\u064A') ||
+            (ch == '\u0629')) {
+            return 1;
+        } else if (ch >= '\u064B' && ch<= '\u0652') {
+            return 2;
+        } else if (ch >= 0x0653 && ch <= 0x0655 ||
+                   ch == 0x0670 ||
+                   ch >= 0xFE70 && ch <= 0xFE7F) {
+            return 3;
+        } else {
+            return 0;
+        }
+    }
+
+    /*
+     * Name    : getLink
+     * Function: Resolves the link between the characters as
+     *           Arabic characters have four forms :
+     *           Isolated, Initial, Middle and Final Form
+     */
+    private static int getLink(char ch) {
+        if (ch >= '\u0622' && ch <= '\u06D3') {
+            return araLink[ch - '\u0622'];
+        } else if (ch == '\u200D') {
+            return 3;
+        } else if (ch >= '\u206D' && ch <= '\u206F') {
+            return 4;
+        } else if (ch >= '\uFE70' && ch <= '\uFEFC') {
+            return presLink[ch - '\uFE70'];
+        } else {
+            return 0;
+        }
+    }
+
+    /*
+     * Name    : countSpaces
+     * Function: Counts the number of spaces
+     *           at each end of the logical buffer
+     */
+    private static int countSpacesLeft(char[] dest,
+                                       int start,
+                                       int count) {
+        for (int i = start, e = start + count; i < e; ++i) {
+            if (dest[i] != SPACE_CHAR) {
+                return i - start;
+            }
+        }
+        return count;
+    }
+
+    private static int countSpacesRight(char[] dest,
+                                        int start,
+                                        int count) {
+
+        for (int i = start + count; --i >= start;) {
+            if (dest[i] != SPACE_CHAR) {
+                return start + count - 1 - i;
+            }
+        }
+        return count;
+    }
+
+    /*
+     * Name    : isTashkeelChar
+     * Function: Returns true for Tashkeel characters else return false
+     */
+    private static boolean isTashkeelChar(char ch) {
+        return ( ch >='\u064B' && ch <= '\u0652' );
+    }
+
+    /*
+     *Name     : isSeenTailFamilyChar
+     *Function : returns 1 if the character is a seen family isolated character
+     *           in the FE range otherwise returns 0
+     */
+
+    private static int isSeenTailFamilyChar(char ch) {
+        if (ch >= 0xfeb1 && ch < 0xfebf){
+             return tailFamilyIsolatedFinal [ch - 0xFEB1];
+        } else {
+             return 0;
+        }
+    }
+
+     /* Name     : isSeenFamilyChar
+      * Function : returns 1 if the character is a seen family character in the Unicode
+      *            06 range otherwise returns 0
+     */
+
+    private static int isSeenFamilyChar(char  ch){
+        if (ch >= 0x633 && ch <= 0x636){
+            return 1;
+        }else {
+            return 0;
+        }
+    }
+
+    /*
+     *Name     : isTailChar
+     *Function : returns true if the character matches one of the tail characters
+     *           (0xfe73 or 0x200b) otherwise returns false
+     */
+
+    private static boolean isTailChar(char ch) {
+        if(ch == OLD_TAIL_CHAR || ch == NEW_TAIL_CHAR){
+                return true;
+        }else{
+                return false;
+        }
+    }
+
+    /*
+     *Name     : isAlefMaksouraChar
+     *Function : returns true if the character is a Alef Maksoura Final or isolated
+     *           otherwise returns false
+     */
+    private static boolean isAlefMaksouraChar(char ch) {
+        return ( (ch == 0xFEEF) || ( ch == 0xFEF0) || (ch == 0x0649));
+    }
+
+    /*
+     * Name     : isYehHamzaChar
+     * Function : returns true if the character is a yehHamza isolated or yehhamza
+     *            final is found otherwise returns false
+     */
+    private static boolean isYehHamzaChar(char ch) {
+        if((ch==0xFE89)||(ch==0xFE8A)){
+            return true;
+        }else{
+            return false;
+        }
+    }
+
+    /*
+     *Name     : isTashkeelCharFE
+     *Function : Returns true for Tashkeel characters in FE range else return false
+     */
+
+    private static boolean isTashkeelCharFE(char ch) {
+        return ( ch!=0xFE75 &&(ch>=0xFE70 && ch<= 0xFE7F) );
+    }
+
+    /*
+     * Name: isTashkeelOnTatweelChar
+     * Function: Checks if the Tashkeel Character is on Tatweel or not,if the
+     *           Tashkeel on tatweel (FE range), it returns 1 else if the
+     *           Tashkeel with shadda on tatweel (FC range)return 2 otherwise
+     *           returns 0
+     */
+    private static int isTashkeelOnTatweelChar(char ch){
+        if (ch >= 0xfe70 && ch <= 0xfe7f && ch != NEW_TAIL_CHAR && ch != 0xFE75 && ch != SHADDA_TATWEEL_CHAR)
+        {
+            return tashkeelMedial [ch - 0xFE70];
+        } else if( (ch >= 0xfcf2 && ch <= 0xfcf4) || (ch == SHADDA_TATWEEL_CHAR)) {
+            return 2;
+        } else {
+            return 0;
+        }
+    }
+
+    /*
+     * Name: isIsolatedTashkeelChar
+     * Function: Checks if the Tashkeel Character is in the isolated form
+     *           (i.e. Unicode FE range) returns 1 else if the Tashkeel
+     *           with shadda is in the isolated form (i.e. Unicode FC range)
+     *           returns 1 otherwise returns 0
+     */
+    private static int isIsolatedTashkeelChar(char ch){
+        if (ch >= 0xfe70 && ch <= 0xfe7f && ch != NEW_TAIL_CHAR && ch != 0xFE75){
+            return (1 - tashkeelMedial [ch - 0xFE70]);
+        } else if(ch >= 0xfc5e && ch <= 0xfc63){
+            return 1;
+        } else{
+            return 0;
+        }
+    }
+
+    /*
+     * Name    : isAlefChar
+     * Function: Returns 1 for Alef characters else return 0
+     */
+    private static boolean isAlefChar(char ch) {
+        return ch == '\u0622' || ch == '\u0623' || ch == '\u0625' || ch == '\u0627';
+    }
+
+    /*
+     * Name    : isLamAlefChar
+     * Function: Returns true for LamAlef characters else return false
+     */
+    private static boolean isLamAlefChar(char ch) {
+        return ch >= '\uFEF5' && ch <= '\uFEFC';
+    }
+
+    private static boolean isNormalizedLamAlefChar(char ch) {
+        return ch >= '\u065C' && ch <= '\u065F';
+    }
+
+    /*
+     * Name    : calculateSize
+     * Function: This function calculates the destSize to be used in preflighting
+     *           when the destSize is equal to 0
+     */
+    private int calculateSize(char[] source,
+                              int sourceStart,
+                              int sourceLength) {
+
+        int destSize = sourceLength;
+
+        switch (options & LETTERS_MASK) {
+        case LETTERS_SHAPE:
+        case LETTERS_SHAPE_TASHKEEL_ISOLATED:
+            if (isLogical) {
+                for (int i = sourceStart, e = sourceStart + sourceLength - 1; i < e; ++i) {
+                    if ((source[i] == LAM_CHAR && isAlefChar(source[i+1])) || isTashkeelCharFE(source[i])){
+                        --destSize;
+                    }
+                }
+            } else { // visual
+                for(int i = sourceStart + 1, e = sourceStart + sourceLength; i < e; ++i) {
+                    if ((source[i] == LAM_CHAR && isAlefChar(source[i-1])) || isTashkeelCharFE(source[i])) {
+                        --destSize;
+                    }
+                }
+            }
+            break;
+
+        case LETTERS_UNSHAPE:
+            for(int i = sourceStart, e = sourceStart + sourceLength; i < e; ++i) {
+                if (isLamAlefChar(source[i])) {
+                    destSize++;
+                }
+            }
+            break;
+
+        default:
+            break;
+        }
+
+        return destSize;
+    }
+
+
+    /*
+     * Name    : countSpaceSub
+     * Function: Counts number of times the subChar appears in the array
+     */
+    public static int countSpaceSub(char [] dest,int length, char subChar){
+        int i = 0;
+        int count = 0;
+        while (i < length) {
+          if (dest[i] == subChar) {
+              count++;
+              }
+          i++;
+        }
+        return count;
+    }
+
+    /*
+     * Name    : shiftArray
+     * Function: Shifts characters to replace space sub characters
+     */
+    public static void shiftArray(char [] dest,int start, int e, char subChar){
+        int w = e;
+        int r = e;
+        while (--r >= start) {
+          char ch = dest[r];
+          if (ch != subChar) {
+            --w;
+            if (w != r) {
+              dest[w] = ch;
+            }
+          }
+        }
+   }
+
+    /*
+     * Name    : flipArray
+     * Function: inverts array, so that start becomes end and vice versa
+     */
+      public static int flipArray(char [] dest, int start, int e, int w){
+        int r;
+        if (w > start) {
+        // shift, assume small buffer size so don't use arraycopy
+          r = w;
+          w = start;
+          while (r < e) {
+            dest[w++] = dest[r++];
+           }
+         } else {
+             w = e;
+         }
+        return w;
+      }
+
+    /*
+     * Name     : handleTashkeelWithTatweel
+     * Function : Replaces Tashkeel as following:
+     *            Case 1 :if the Tashkeel on tatweel, replace it with Tatweel.
+     *            Case 2 :if the Tashkeel aggregated with Shadda on Tatweel, replace
+     *                   it with Shadda on Tatweel.
+     *            Case 3: if the Tashkeel is isolated replace it with Space.
+     *
+     */
+    private static int handleTashkeelWithTatweel(char[] dest, int sourceLength) {
+                     int i;
+                     for(i = 0; i < sourceLength; i++){
+                         if((isTashkeelOnTatweelChar(dest[i]) == 1)){
+                             dest[i] = TATWEEL_CHAR;
+                        }else if((isTashkeelOnTatweelChar(dest[i]) == 2)){
+                             dest[i] = SHADDA_TATWEEL_CHAR;
+                        }else if((isIsolatedTashkeelChar(dest[i])==1) && dest[i] != SHADDA_CHAR){
+                             dest[i] = SPACE_CHAR;
+                        }
+                     }
+                     return sourceLength;
+    }
+
+    /*
+     *Name     : handleGeneratedSpaces
+     *Function : The shapeUnicode function converts Lam + Alef into LamAlef + space,
+     *           and Tashkeel to space.
+     *           handleGeneratedSpaces function puts these generated spaces
+     *           according to the options the user specifies. LamAlef and Tashkeel
+     *           spaces can be replaced at begin, at end, at near or decrease the
+     *           buffer size.
+     *
+     *           There is also Auto option for LamAlef and tashkeel, which will put
+     *           the spaces at end of the buffer (or end of text if the user used
+     *           the option SPACES_RELATIVE_TO_TEXT_BEGIN_END).
+     *
+     *           If the text type was visual_LTR and the option
+     *           SPACES_RELATIVE_TO_TEXT_BEGIN_END was selected the END
+     *           option will place the space at the beginning of the buffer and
+     *           BEGIN will place the space at the end of the buffer.
+     */
+  private int handleGeneratedSpaces(char[] dest,
+            int start,
+            int length) {
+
+      int lenOptionsLamAlef = options & LAMALEF_MASK;
+      int lenOptionsTashkeel = options & TASHKEEL_MASK;
+      boolean lamAlefOn = false;
+      boolean tashkeelOn = false;
+
+      if (!isLogical & !spacesRelativeToTextBeginEnd) {
+          switch (lenOptionsLamAlef) {
+          case LAMALEF_BEGIN: lenOptionsLamAlef = LAMALEF_END; break;
+          case LAMALEF_END: lenOptionsLamAlef = LAMALEF_BEGIN; break;
+          default: break;
+         }
+          switch (lenOptionsTashkeel){
+          case TASHKEEL_BEGIN: lenOptionsTashkeel = TASHKEEL_END; break;
+          case TASHKEEL_END: lenOptionsTashkeel = TASHKEEL_BEGIN; break;
+          default: break;
+          }
+        }
+
+
+      if (lenOptionsLamAlef == LAMALEF_NEAR) {
+          for (int i = start, e = i + length; i < e; ++i) {
+              if (dest[i] == LAMALEF_SPACE_SUB) {
+                  dest[i] = SPACE_CHAR_FOR_LAMALEF;
+              }
+          }
+
+      } else {
+
+          final int e = start + length;
+          int wL = countSpaceSub(dest, length, LAMALEF_SPACE_SUB);
+          int wT = countSpaceSub(dest, length, TASHKEEL_SPACE_SUB);
+
+          if (lenOptionsLamAlef == LAMALEF_END){
+            lamAlefOn = true;
+          }
+          if (lenOptionsTashkeel == TASHKEEL_END){
+            tashkeelOn = true;
+          }
+
+
+          if (lamAlefOn && (lenOptionsLamAlef == LAMALEF_END)) {
+            shiftArray(dest, start, e, LAMALEF_SPACE_SUB);
+            while (wL > start) {
+                dest[--wL] = SPACE_CHAR;
+            }
+          }
+
+          if (tashkeelOn && (lenOptionsTashkeel == TASHKEEL_END)){
+            shiftArray(dest, start, e, TASHKEEL_SPACE_SUB);
+            while (wT > start) {
+                 dest[--wT] = SPACE_CHAR;
+            }
+          }
+
+          lamAlefOn = false;
+          tashkeelOn = false;
+
+          if (lenOptionsLamAlef == LAMALEF_RESIZE){
+            lamAlefOn = true;
+          }
+          if (lenOptionsTashkeel == TASHKEEL_RESIZE){
+            tashkeelOn = true;
+          }
+
+          if (lamAlefOn && (lenOptionsLamAlef == LAMALEF_RESIZE)){
+              shiftArray(dest, start, e, LAMALEF_SPACE_SUB);
+              wL = flipArray(dest,start,e, wL);
+              length = wL - start;
+          }
+          if (tashkeelOn && (lenOptionsTashkeel == TASHKEEL_RESIZE)) {
+              shiftArray(dest, start, e, TASHKEEL_SPACE_SUB);
+              wT = flipArray(dest,start,e, wT);
+              length = wT - start;
+          }
+
+          lamAlefOn = false;
+          tashkeelOn = false;
+
+          if ((lenOptionsLamAlef == LAMALEF_BEGIN) ||
+              (lenOptionsLamAlef == LAMALEF_AUTO)){
+                lamAlefOn = true;
+          }
+          if (lenOptionsTashkeel == TASHKEEL_BEGIN){
+                tashkeelOn = true;
+          }
+
+          if (lamAlefOn && ((lenOptionsLamAlef == LAMALEF_BEGIN)||
+                            (lenOptionsLamAlef == LAMALEF_AUTO))) { // spaces at beginning
+              shiftArray(dest, start, e, LAMALEF_SPACE_SUB);
+               wL = flipArray(dest,start,e, wL);
+                  while (wL < e) {
+                      dest[wL++] = SPACE_CHAR;
+                  }
+              }
+              if(tashkeelOn && (lenOptionsTashkeel == TASHKEEL_BEGIN)){
+               shiftArray(dest, start, e, TASHKEEL_SPACE_SUB);
+               wT = flipArray(dest,start,e, wT);
+                  while (wT < e) {
+                      dest[wT++] = SPACE_CHAR;
+                  }
+              }
+           }
+
+      return length;
+  }
+
+
+  /*
+   *Name     :expandCompositCharAtBegin
+   *Function :Expands the LamAlef character to Lam and Alef consuming the required
+   *         space from beginning of the buffer. If the text type was visual_LTR
+   *         and the option SPACES_RELATIVE_TO_TEXT_BEGIN_END was selected
+   *         the spaces will be located at end of buffer.
+   *         If there are no spaces to expand the LamAlef, an exception is thrown.
+*/
+ private boolean expandCompositCharAtBegin(char[] dest,int start, int length,
+                            int lacount) {
+     boolean spaceNotFound = false;
+
+     if (lacount > countSpacesRight(dest, start, length)) {
+         spaceNotFound = true;
+         return spaceNotFound;
+     }
+     for (int r = start + length - lacount, w = start + length; --r >= start;) {
+         char ch = dest[r];
+         if (isNormalizedLamAlefChar(ch)) {
+             dest[--w] = LAM_CHAR;
+             dest[--w] = convertNormalizedLamAlef[ch - '\u065C'];
+         } else {
+             dest[--w] = ch;
+         }
+     }
+     return spaceNotFound;
+
+  }
+
+  /*
+   *Name     : expandCompositCharAtEnd
+   *Function : Expands the LamAlef character to Lam and Alef consuming the
+   *           required space from end of the buffer. If the text type was
+   *           Visual LTR and the option SPACES_RELATIVE_TO_TEXT_BEGIN_END
+   *           was used, the spaces will be consumed from begin of buffer. If
+   *           there are no spaces to expand the LamAlef, an exception is thrown.
+   */
+
+  private boolean  expandCompositCharAtEnd(char[] dest,int start, int length,
+                          int lacount){
+      boolean spaceNotFound = false;
+
+      if (lacount > countSpacesLeft(dest, start, length)) {
+          spaceNotFound = true;
+          return spaceNotFound;
+      }
+      for (int r = start + lacount, w = start, e = start + length; r < e; ++r) {
+          char ch = dest[r];
+          if (isNormalizedLamAlefChar(ch)) {
+              dest[w++] = convertNormalizedLamAlef[ch - '\u065C'];
+              dest[w++] = LAM_CHAR;
+          } else {
+              dest[w++] = ch;
+          }
+      }
+      return spaceNotFound;
+  }
+
+  /*
+   *Name     : expandCompositCharAtNear
+   *Function : Expands the LamAlef character into Lam + Alef, YehHamza character
+   *           into Yeh + Hamza, SeenFamily character into SeenFamily character
+   *           + Tail, while consuming the space next to the character.
+   */
+
+  private boolean expandCompositCharAtNear(char[] dest,int start, int length,
+                                       int yehHamzaOption, int seenTailOption, int lamAlefOption){
+
+      boolean spaceNotFound = false;
+
+
+
+      if (isNormalizedLamAlefChar(dest[start])) {
+          spaceNotFound = true;
+          return spaceNotFound;
+      }
+      for (int i = start + length; --i >=start;) {
+          char ch = dest[i];
+          if (lamAlefOption == 1 && isNormalizedLamAlefChar(ch)) {
+              if (i>start &&dest[i-1] == SPACE_CHAR) {
+                  dest[i] = LAM_CHAR;
+                  dest[--i] = convertNormalizedLamAlef[ch - '\u065C'];
+              } else {
+                  spaceNotFound = true;
+                  return spaceNotFound;
+              }
+          }else if(seenTailOption == 1 && isSeenTailFamilyChar(ch) == 1){
+              if(i>start &&dest[i-1] == SPACE_CHAR){
+                  dest[i-1] = tailChar;
+              } else{
+                  spaceNotFound = true;
+                  return spaceNotFound;
+              }
+          }else if(yehHamzaOption == 1 && isYehHamzaChar(ch)){
+
+               if(i>start &&dest[i-1] == SPACE_CHAR){
+                  dest[i] = yehHamzaToYeh[ch - YEH_HAMZAFE_CHAR];
+                  dest[i-1] = HAMZAFE_CHAR;
+              }else{
+                  spaceNotFound = true;
+                  return spaceNotFound;
+                }
+
+
+          }
+      }
+      return false;
+
+  }
+
+    /*
+     * Name    : expandCompositChar
+     * Function: LamAlef needs special handling as the LamAlef is
+     *           one character while expanding it will give two
+     *           characters Lam + Alef, so we need to expand the LamAlef
+     *           in near or far spaces according to the options the user
+     *           specifies or increase the buffer size.
+     *           Dest has enough room for the expansion if we are growing.
+     *           lamalef are normalized to the 'special characters'
+     */
+    private int expandCompositChar(char[] dest,
+                              int start,
+                              int length,
+                              int lacount,
+                              int shapingMode) throws ArabicShapingException {
+
+        int lenOptionsLamAlef = options & LAMALEF_MASK;
+        int lenOptionsSeen = options & SEEN_MASK;
+        int lenOptionsYehHamza = options & YEHHAMZA_MASK;
+        boolean spaceNotFound = false;
+
+        if (!isLogical && !spacesRelativeToTextBeginEnd) {
+            switch (lenOptionsLamAlef) {
+            case LAMALEF_BEGIN: lenOptionsLamAlef = LAMALEF_END; break;
+            case LAMALEF_END: lenOptionsLamAlef = LAMALEF_BEGIN; break;
+            default: break;
+            }
+        }
+
+        if(shapingMode == 1){
+            if(lenOptionsLamAlef == LAMALEF_AUTO){
+                if(isLogical){
+                    spaceNotFound = expandCompositCharAtEnd(dest, start, length, lacount);
+                    if(spaceNotFound){
+                        spaceNotFound = expandCompositCharAtBegin(dest, start, length, lacount);
+                    }
+                    if(spaceNotFound){
+                        spaceNotFound = expandCompositCharAtNear(dest, start, length,0,0,1);
+                    }
+                    if(spaceNotFound){
+                        throw new ArabicShapingException("No spacefor lamalef");
+                    }
+                }else{
+                    spaceNotFound = expandCompositCharAtBegin(dest, start, length, lacount);
+                    if(spaceNotFound){
+                        spaceNotFound = expandCompositCharAtEnd(dest, start, length, lacount);
+                    }
+                    if(spaceNotFound){
+                        spaceNotFound = expandCompositCharAtNear(dest, start, length,0,0,1);
+                    }
+                    if(spaceNotFound){
+                        throw new ArabicShapingException("No spacefor lamalef");
+                    }
+                }
+            }else if(lenOptionsLamAlef == LAMALEF_END){
+                spaceNotFound = expandCompositCharAtEnd(dest, start, length, lacount);
+                if(spaceNotFound){
+                    throw new ArabicShapingException("No spacefor lamalef");
+                }
+            }else if(lenOptionsLamAlef == LAMALEF_BEGIN){
+                spaceNotFound = expandCompositCharAtBegin(dest, start, length, lacount);
+                if(spaceNotFound){
+                    throw new ArabicShapingException("No spacefor lamalef");
+                }
+            }else if(lenOptionsLamAlef == LAMALEF_NEAR){
+                spaceNotFound = expandCompositCharAtNear(dest, start, length,0,0,1);
+                if(spaceNotFound){
+                    throw new ArabicShapingException("No spacefor lamalef");
+            }
+            }else if(lenOptionsLamAlef == LAMALEF_RESIZE){
+                for (int r = start + length, w = r + lacount; --r >= start;) {
+                    char ch = dest[r];
+                    if (isNormalizedLamAlefChar(ch)) {
+                        dest[--w] = '\u0644';
+                        dest[--w] = convertNormalizedLamAlef[ch - '\u065C'];
+                    } else {
+                        dest[--w] = ch;
+                    }
+                }
+                length += lacount;
+            }
+            }else{
+                if(lenOptionsSeen == SEEN_TWOCELL_NEAR){
+                spaceNotFound = expandCompositCharAtNear(dest, start, length,0,1,0);
+                if(spaceNotFound){
+                    throw new ArabicShapingException("No space for Seen tail expansion");
+                }
+            }
+            if(lenOptionsYehHamza == YEHHAMZA_TWOCELL_NEAR){
+                spaceNotFound = expandCompositCharAtNear(dest, start, length,1,0,0);
+                if(spaceNotFound){
+                    throw new ArabicShapingException("No space for YehHamza expansion");
+                }
+            }
+            }
+        return length;
+    }
+
+
+    /* Convert the input buffer from FExx Range into 06xx Range
+     * to put all characters into the 06xx range
+     * even the lamalef is converted to the special region in
+     * the 06xx range.  Return the number of lamalef chars found.
+     */
+    private int normalize(char[] dest, int start, int length) {
+        int lacount = 0;
+        for (int i = start, e = i + length; i < e; ++i) {
+            char ch = dest[i];
+            if (ch >= '\uFE70' && ch <= '\uFEFC') {
+                if (isLamAlefChar(ch)) {
+                    ++lacount;
+                }
+                dest[i] = (char)convertFEto06[ch - '\uFE70'];
+            }
+        }
+        return lacount;
+    }
+
+    /*
+     * Name    : deshapeNormalize
+     * Function: Convert the input buffer from FExx Range into 06xx Range
+     *           even the lamalef is converted to the special region in the 06xx range.
+     *           According to the options the user enters, all seen family characters
+     *           followed by a tail character are merged to seen tail family character and
+     *           any yeh followed by a hamza character are merged to yehhamza character.
+     *           Method returns the number of lamalef chars found.
+     */
+    private int deshapeNormalize(char[] dest, int start, int length) {
+        int lacount = 0;
+        int yehHamzaComposeEnabled = 0;
+        int seenComposeEnabled = 0;
+
+        yehHamzaComposeEnabled = ((options&YEHHAMZA_MASK) == YEHHAMZA_TWOCELL_NEAR) ? 1 : 0;
+        seenComposeEnabled = ((options&SEEN_MASK) == SEEN_TWOCELL_NEAR)? 1 : 0;
+
+        for (int i = start, e = i + length; i < e; ++i) {
+            char ch = dest[i];
+
+        if( (yehHamzaComposeEnabled == 1) && ((ch == HAMZA06_CHAR) || (ch == HAMZAFE_CHAR))
+               && (i < (length - 1)) && isAlefMaksouraChar(dest[i+1] )) {
+                dest[i] = SPACE_CHAR;
+                dest[i+1] = YEH_HAMZA_CHAR;
+       } else if ( (seenComposeEnabled == 1) && (isTailChar(ch)) && (i< (length - 1))
+                       && (isSeenTailFamilyChar(dest[i+1])==1) ) {
+               dest[i] = SPACE_CHAR;
+       }
+       else if (ch >= '\uFE70' && ch <= '\uFEFC') {
+                if (isLamAlefChar(ch)) {
+                    ++lacount;
+                }
+                dest[i] = (char)convertFEto06[ch - '\uFE70'];
+            }
+        }
+        return lacount;
+    }
+
+    /*
+     * Name    : shapeUnicode
+     * Function: Converts an Arabic Unicode buffer in 06xx Range into a shaped
+     *           arabic Unicode buffer in FExx Range
+     */
+    private int shapeUnicode(char[] dest,
+                             int start,
+                             int length,
+                             int destSize,
+                             int tashkeelFlag)throws ArabicShapingException {
+
+        int lamalef_count = normalize(dest, start, length);
+
+        // resolve the link between the characters.
+        // Arabic characters have four forms: Isolated, Initial, Medial and Final.
+        // Tashkeel characters have two, isolated or medial, and sometimes only isolated.
+        // tashkeelFlag == 0: shape normally, 1: shape isolated, 2: don't shape
+
+        boolean lamalef_found = false, seenfam_found = false;
+        boolean yehhamza_found = false, tashkeel_found = false;
+        int i = start + length - 1;
+        int currLink = getLink(dest[i]);
+        int nextLink = 0;
+        int prevLink = 0;
+        int lastLink = 0;
+        //int prevPos = i;
+        int lastPos = i;
+        int nx = -2;
+        int nw = 0;
+
+        while (i >= 0) {
+            // If high byte of currLink > 0 then there might be more than one shape
+            if ((currLink & '\uFF00') > 0 || isTashkeelChar(dest[i])) {
+                nw = i - 1;
+                nx = -2;
+                while (nx < 0) { // we need to know about next char
+                    if (nw == -1) {
+                        nextLink = 0;
+                        nx = Integer.MAX_VALUE;
+                    } else {
+                        nextLink = getLink(dest[nw]);
+                        if ((nextLink & IRRELEVANT) == 0) {
+                            nx = nw;
+                        } else {
+                            --nw;
+                        }
+                    }
+                }
+
+                if (((currLink & ALEFTYPE) > 0) && ((lastLink & LAMTYPE) > 0)) {
+                    lamalef_found = true;
+                    char wLamalef = changeLamAlef(dest[i]); // get from 0x065C-0x065f
+                    if (wLamalef != '\u0000') {
+                        // replace alef by marker, it will be removed later
+                        dest[i] = '\uffff';
+                        dest[lastPos] = wLamalef;
+                        i = lastPos;
+                    }
+
+                    lastLink = prevLink;
+                    currLink = getLink(wLamalef); // requires '\u0000', unfortunately
+                }
+                if ((i > 0) && (dest[i-1] == SPACE_CHAR))
+                {
+                    if ( isSeenFamilyChar(dest[i]) == 1){
+                        seenfam_found = true;
+                    } else if (dest[i] == YEH_HAMZA_CHAR) {
+                        yehhamza_found = true;
+                    }
+                }
+                else if(i==0){
+                    if ( isSeenFamilyChar(dest[i]) == 1){
+                        seenfam_found = true;
+                    } else if (dest[i] == YEH_HAMZA_CHAR) {
+                        yehhamza_found = true;
+                    }
+                }
+
+
+                // get the proper shape according to link ability of neighbors
+                // and of character; depends on the order of the shapes
+                // (isolated, initial, middle, final) in the compatibility area
+
+                int flag = specialChar(dest[i]);
+
+                int shape = shapeTable[nextLink & LINK_MASK]
+                    [lastLink & LINK_MASK]
+                    [currLink & LINK_MASK];
+
+                if (flag == 1) {
+                    shape &= 0x1;
+                } else if (flag == 2) {
+                    if (tashkeelFlag == 0 &&
+                        ((lastLink & LINKL) != 0) &&
+                        ((nextLink & LINKR) != 0) &&
+                        dest[i] != '\u064C' &&
+                        dest[i] != '\u064D' &&
+                        !((nextLink & ALEFTYPE) == ALEFTYPE &&
+                          (lastLink & LAMTYPE) == LAMTYPE)) {
+
+                        shape = 1;
+                    } else {
+                        shape = 0;
+                    }
+                }
+                if (flag == 2) {
+                    if (tashkeelFlag == 2) {
+                        dest[i] = TASHKEEL_SPACE_SUB;
+                        tashkeel_found = true;
+                    }
+                    else{
+                        dest[i] = (char)('\uFE70' + irrelevantPos[dest[i] - '\u064B'] + shape);
+                    }
+                    // else leave tashkeel alone
+                } else {
+                    dest[i] = (char)('\uFE70' + (currLink >> 8) + shape);
+                }
+            }
+
+            // move one notch forward
+            if ((currLink & IRRELEVANT) == 0) {
+                prevLink = lastLink;
+                lastLink = currLink;
+                //prevPos = lastPos;
+                lastPos = i;
+            }
+
+            --i;
+            if (i == nx) {
+                currLink = nextLink;
+                nx = -2;
+            } else if (i != -1) {
+                currLink = getLink(dest[i]);
+            }
+        }
+
+        // If we found a lam/alef pair in the buffer
+        // call handleGeneratedSpaces to remove the spaces that were added
+
+        destSize = length;
+        if (lamalef_found || tashkeel_found) {
+            destSize = handleGeneratedSpaces(dest, start, length);
+        }
+        if (seenfam_found || yehhamza_found){
+            destSize = expandCompositChar(dest, start, destSize, lamalef_count, SHAPE_MODE);
+        }
+        return destSize;
+    }
+
+    /*
+     * Name    : deShapeUnicode
+     * Function: Converts an Arabic Unicode buffer in FExx Range into unshaped
+     *           arabic Unicode buffer in 06xx Range
+     */
+    private int deShapeUnicode(char[] dest,
+                               int start,
+                               int length,
+                               int destSize) throws ArabicShapingException {
+
+        int lamalef_count = deshapeNormalize(dest, start, length);
+
+        // If there was a lamalef in the buffer call expandLamAlef
+        if (lamalef_count != 0) {
+            // need to adjust dest to fit expanded buffer... !!!
+            destSize = expandCompositChar(dest, start, length, lamalef_count,DESHAPE_MODE);
+        } else {
+            destSize = length;
+        }
+
+        return destSize;
+    }
+
+    private int internalShape(char[] source,
+                              int sourceStart,
+                              int sourceLength,
+                              char[] dest,
+                              int destStart,
+                              int destSize) throws ArabicShapingException {
+
+        if (sourceLength == 0) {
+            return 0;
+        }
+
+        if (destSize == 0) {
+            if (((options & LETTERS_MASK) != LETTERS_NOOP) &&
+                ((options & LAMALEF_MASK) == LAMALEF_RESIZE)) {
+
+                return calculateSize(source, sourceStart, sourceLength);
+            } else {
+                return sourceLength; // by definition
+            }
+        }
+
+        // always use temp buffer
+        char[] temp = new char[sourceLength * 2]; // all lamalefs requiring expansion
+        System.arraycopy(source, sourceStart, temp, 0, sourceLength);
+
+        if (isLogical) {
+            invertBuffer(temp, 0, sourceLength);
+        }
+
+        int outputSize = sourceLength;
+
+        switch (options & LETTERS_MASK) {
+        case LETTERS_SHAPE_TASHKEEL_ISOLATED:
+            outputSize = shapeUnicode(temp, 0, sourceLength, destSize, 1);
+            break;
+
+        case LETTERS_SHAPE:
+            if( ((options&TASHKEEL_MASK)> 0) &&
+                ((options&TASHKEEL_MASK) !=TASHKEEL_REPLACE_BY_TATWEEL)) {
+                   /* Call the shaping function with tashkeel flag == 2 for removal of tashkeel */
+                outputSize = shapeUnicode(temp, 0, sourceLength, destSize, 2);
+                }else {
+                   //default Call the shaping function with tashkeel flag == 1 */
+                    outputSize = shapeUnicode(temp, 0, sourceLength, destSize, 0);
+
+                   /*After shaping text check if user wants to remove tashkeel and replace it with tatweel*/
+                   if( (options&TASHKEEL_MASK) == TASHKEEL_REPLACE_BY_TATWEEL){
+                       outputSize = handleTashkeelWithTatweel(temp,sourceLength);
+                   }
+               }
+            break;
+
+        case LETTERS_UNSHAPE:
+            outputSize = deShapeUnicode(temp, 0, sourceLength, destSize);
+            break;
+
+        default:
+            break;
+        }
+
+        if (outputSize > destSize) {
+            throw new ArabicShapingException("not enough room for result data");
+        }
+
+        if ((options & DIGITS_MASK) != DIGITS_NOOP) {
+            char digitBase = '\u0030'; // European digits
+            switch (options & DIGIT_TYPE_MASK) {
+            case DIGIT_TYPE_AN:
+                digitBase = '\u0660';  // Arabic-Indic digits
+                break;
+
+            case DIGIT_TYPE_AN_EXTENDED:
+                digitBase = '\u06f0';  // Eastern Arabic-Indic digits (Persian and Urdu)
+                break;
+
+            default:
+                break;
+            }
+
+            switch (options & DIGITS_MASK) {
+            case DIGITS_EN2AN:
+                {
+                    int digitDelta = digitBase - '\u0030';
+                    for (int i = 0; i < outputSize; ++i) {
+                        char ch = temp[i];
+                        if (ch <= '\u0039' && ch >= '\u0030') {
+                            temp[i] += digitDelta;
+                        }
+                    }
+                }
+                break;
+
+            case DIGITS_AN2EN:
+                {
+                    char digitTop = (char)(digitBase + 9);
+                    int digitDelta = '\u0030' - digitBase;
+                    for (int i = 0; i < outputSize; ++i) {
+                        char ch = temp[i];
+                        if (ch <= digitTop && ch >= digitBase) {
+                            temp[i] += digitDelta;
+                        }
+                    }
+                }
+                break;
+
+            case DIGITS_EN2AN_INIT_LR:
+                shapeToArabicDigitsWithContext(temp, 0, outputSize, digitBase, false);
+                break;
+
+            case DIGITS_EN2AN_INIT_AL:
+                shapeToArabicDigitsWithContext(temp, 0, outputSize, digitBase, true);
+                break;
+
+            default:
+                break;
+            }
+        }
+
+        if (isLogical) {
+            invertBuffer(temp, 0, outputSize);
+        }
+
+        System.arraycopy(temp, 0, dest, destStart, outputSize);
+
+        return outputSize;
+    }
+
+    private static class ArabicShapingException extends RuntimeException {
+        ArabicShapingException(String msg) {
+            super(msg);
+        }
+    }
+}
diff --git a/icu4j/license.html b/icu4j/license.html
new file mode 100644
index 0000000..b905ddf
--- /dev/null
+++ b/icu4j/license.html
@@ -0,0 +1,51 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=us-ascii"></meta>
+<title>ICU License - ICU 1.8.1 and later</title>
+</head>
+
+<body BGCOLOR="#ffffff">
+<h2>ICU License - ICU 1.8.1 and later</h2>
+
+<p>COPYRIGHT AND PERMISSION NOTICE</p>
+
+<p>
+Copyright (c) 1995-2006 International Business Machines Corporation and others
+</p>
+<p>
+All rights reserved.
+</p>
+<p>
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, and/or sell
+copies of the Software, and to permit persons
+to whom the Software is furnished to do so, provided that the above
+copyright notice(s) and this permission notice appear in all copies
+of the Software and that both the above copyright notice(s) and this
+permission notice appear in supporting documentation.
+</p>
+<p>
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL
+THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM,
+OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
+USE OR PERFORMANCE OF THIS SOFTWARE.
+</p>
+<p>
+Except as contained in this notice, the name of a copyright holder shall not be
+used in advertising or otherwise to promote the sale, use or other dealings in
+this Software without prior written authorization of the copyright holder.
+</p>
+
+<hr>
+<p><small>
+All trademarks and registered trademarks mentioned herein are the property of their respective owners.
+</small></p>
+</body>
+</html>
diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h
index 09f0de1..22c9b72 100644
--- a/include/android_runtime/AndroidRuntime.h
+++ b/include/android_runtime/AndroidRuntime.h
@@ -30,7 +30,9 @@
 
 
 namespace android {
-    
+
+class CursorWindow;
+
 class AndroidRuntime
 {
 public:
@@ -122,6 +124,8 @@
 // Returns the Unix file descriptor for a ParcelFileDescriptor object
 extern int getParcelFileDescriptorFD(JNIEnv* env, jobject object);
 
+extern CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow);
+
 }
 
 #endif
diff --git a/include/binder/CursorWindow.h b/include/binder/CursorWindow.h
new file mode 100644
index 0000000..4fbff2a
--- /dev/null
+++ b/include/binder/CursorWindow.h
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID__DATABASE_WINDOW_H
+#define _ANDROID__DATABASE_WINDOW_H
+
+#include <cutils/log.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <binder/IMemory.h>
+#include <utils/RefBase.h>
+
+#define DEFAULT_WINDOW_SIZE 4096
+#define MAX_WINDOW_SIZE (1024 * 1024)
+#define WINDOW_ALLOCATION_SIZE 4096
+
+#define ROW_SLOT_CHUNK_NUM_ROWS 16
+
+// Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS,
+// with an offset after the rows that points to the next chunk
+#define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t))
+
+
+#if LOG_NDEBUG
+
+#define IF_LOG_WINDOW() if (false)
+#define LOG_WINDOW(...)
+
+#else
+
+#define IF_LOG_WINDOW() IF_LOG(LOG_DEBUG, "CursorWindow")
+#define LOG_WINDOW(...) LOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)
+
+#endif
+
+
+// When defined to true strings are stored as UTF8, otherwise they're UTF16
+#define WINDOW_STORAGE_UTF8 1
+
+// When defined to true numberic values are stored inline in the field_slot_t, otherwise they're allocated in the window
+#define WINDOW_STORAGE_INLINE_NUMERICS 1
+
+namespace android {
+
+typedef struct
+{
+    uint32_t numRows;
+    uint32_t numColumns;
+} window_header_t;
+
+typedef struct
+{
+    uint32_t offset;
+} row_slot_t;
+
+typedef struct
+{
+    uint8_t type;
+    union {
+        double d;
+        int64_t l;
+        struct {
+            uint32_t offset;
+            uint32_t size;
+        } buffer;
+    } data;
+} __attribute__((packed)) field_slot_t;
+
+#define FIELD_TYPE_NULL 0
+#define FIELD_TYPE_INTEGER 1
+#define FIELD_TYPE_FLOAT 2
+#define FIELD_TYPE_STRING 3
+#define FIELD_TYPE_BLOB 4
+
+/**
+ * This class stores a set of rows from a database in a buffer. The begining of the
+ * window has first chunk of row_slot_ts, which are offsets to the row directory, followed by
+ * an offset to the next chunk in a linked-list of additional chunk of row_slot_ts in case
+ * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a
+ * field_slot_t per column, which has the size, offset, and type of the data for that field.
+ * Note that the data types come from sqlite3.h.
+ */
+class CursorWindow
+{
+public:
+                        CursorWindow(size_t maxSize);
+                        CursorWindow(){}
+    bool                setMemory(const sp<IMemory>&);
+                        ~CursorWindow();
+
+    bool                initBuffer(bool localOnly);
+    sp<IMemory>         getMemory() {return mMemory;}
+
+    size_t              size() {return mSize;}
+    uint8_t *           data() {return mData;}
+    uint32_t            getNumRows() {return mHeader->numRows;}
+    uint32_t            getNumColumns() {return mHeader->numColumns;}
+    void                freeLastRow() {
+                            if (mHeader->numRows > 0) {
+                                mHeader->numRows--;
+                            }
+                        }
+    bool                setNumColumns(uint32_t numColumns)
+                            {
+                                uint32_t cur = mHeader->numColumns;
+                                if (cur > 0 && cur != numColumns) {
+                                    LOGE("Trying to go from %d columns to %d", cur, numColumns);
+                                    return false;
+                                }
+                                mHeader->numColumns = numColumns;
+                                return true;
+                            }
+
+    int32_t             freeSpace();
+
+    void                clear();
+
+                        /**
+                         * Allocate a row slot and its directory. The returned
+                         * pointer points to the begining of the row's directory
+                         * or NULL if there wasn't room. The directory is
+                         * initialied with NULL entries for each field.
+                         */
+    field_slot_t *      allocRow();
+
+                        /**
+                         * Allocate a portion of the window. Returns the offset
+                         * of the allocation, or 0 if there isn't enough space.
+                         * If aligned is true, the allocation gets 4 byte alignment.
+                         */
+    uint32_t            alloc(size_t size, bool aligned = false);
+
+    uint32_t            read_field_slot(int row, int column, field_slot_t * slot);
+
+                        /**
+                         * Copy data into the window at the given offset.
+                         */
+    void                copyIn(uint32_t offset, uint8_t const * data, size_t size);
+    void                copyIn(uint32_t offset, int64_t data);
+    void                copyIn(uint32_t offset, double data);
+
+    void                copyOut(uint32_t offset, uint8_t * data, size_t size);
+    int64_t             copyOutLong(uint32_t offset);
+    double              copyOutDouble(uint32_t offset);
+
+    bool                putLong(unsigned int row, unsigned int col, int64_t value);
+    bool                putDouble(unsigned int row, unsigned int col, double value);
+    bool                putNull(unsigned int row, unsigned int col);
+
+    bool                getLong(unsigned int row, unsigned int col, int64_t * valueOut);
+    bool                getDouble(unsigned int row, unsigned int col, double * valueOut);
+    bool                getNull(unsigned int row, unsigned int col, bool * valueOut);
+
+    uint8_t *           offsetToPtr(uint32_t offset) {return mData + offset;}
+
+    row_slot_t *        allocRowSlot();
+
+    row_slot_t *        getRowSlot(int row);
+
+                        /**
+                         * return NULL if Failed to find rowSlot or
+                         * Invalid rowSlot
+                         */
+    field_slot_t *      getFieldSlotWithCheck(int row, int column);
+    field_slot_t *      getFieldSlot(int row, int column)
+                            {
+                                int fieldDirOffset = getRowSlot(row)->offset;
+                                return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;
+                            }
+
+private:
+    uint8_t * mData;
+    size_t mSize;
+    size_t mMaxSize;
+    window_header_t * mHeader;
+    sp<IMemory> mMemory;
+
+    /**
+     * Offset of the lowest unused data byte in the array.
+     */
+    uint32_t mFreeOffset;
+};
+
+}; // namespace android
+
+#endif
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index e6d84ba..8d8edd6 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -22,8 +22,6 @@
 
 namespace android {
 
-class ISurface;
-
 /*
  * A set of bit masks for specifying how the received preview frames are
  * handled before the previewCallback() call.
@@ -96,6 +94,14 @@
     // or CAMERA_MSG_COMPRESSED_IMAGE. This is not allowed to be set during
     // preview.
     CAMERA_CMD_SET_DISPLAY_ORIENTATION = 3,
+
+    // cmdType to disable/enable shutter sound.
+    // In sendCommand passing arg1 = 0 will disable,
+    // while passing arg1 = 1 will enable the shutter sound.
+    CAMERA_CMD_ENABLE_SHUTTER_SOUND = 4,
+
+    // cmdType to play recording sound.
+    CAMERA_CMD_PLAY_RECORDING_SOUND = 5,
 };
 
 // camera fatal errors
@@ -164,9 +170,8 @@
 
             status_t    getStatus() { return mStatus; }
 
-            // pass the buffered ISurface to the camera service
+            // pass the buffered Surface to the camera service
             status_t    setPreviewDisplay(const sp<Surface>& surface);
-            status_t    setPreviewDisplay(const sp<ISurface>& surface);
 
             // start preview mode, must call setPreviewDisplay first
             status_t    startPreview();
@@ -207,6 +212,15 @@
             // send command to camera driver
             status_t    sendCommand(int32_t cmd, int32_t arg1, int32_t arg2);
 
+            // return the total number of available video buffers.
+            int32_t     getNumberOfVideoBuffers() const;
+
+            // return the individual video buffer corresponding to the given index.
+            sp<IMemory> getVideoBuffer(int32_t index) const;
+
+            // tell camera hal to store meta data or real YUV in video buffers.
+            status_t    storeMetaDataInBuffers(bool enabled);
+
             void        setListener(const sp<CameraListener>& listener);
             void        setPreviewCallbackFlags(int preview_callback_flag);
 
diff --git a/include/camera/CameraHardwareInterface.h b/include/camera/CameraHardwareInterface.h
index 6a66e3c..5465441 100644
--- a/include/camera/CameraHardwareInterface.h
+++ b/include/camera/CameraHardwareInterface.h
@@ -18,8 +18,11 @@
 #define ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
 
 #include <binder/IMemory.h>
+#include <ui/egl/android_natives.h>
 #include <utils/RefBase.h>
 #include <surfaceflinger/ISurface.h>
+#include <ui/android_native_buffer.h>
+#include <ui/GraphicBuffer.h>
 #include <camera/Camera.h>
 #include <camera/CameraParameters.h>
 
@@ -86,8 +89,8 @@
 public:
     virtual ~CameraHardwareInterface() { }
 
-    /** Return the IMemoryHeap for the preview image heap */
-    virtual sp<IMemoryHeap>         getPreviewHeap() const = 0;
+    /** Set the ANativeWindow to which preview frames are sent */
+    virtual status_t setPreviewWindow(const sp<ANativeWindow>& buf) = 0;
 
     /** Return the IMemoryHeap for the raw image heap */
     virtual sp<IMemoryHeap>         getRawHeap() const = 0;
@@ -143,6 +146,82 @@
     virtual bool        previewEnabled() = 0;
 
     /**
+     * Retrieve the total number of available buffers from camera hal for passing
+     * video frame data in a recording session. Must be called again if a new
+     * recording session is started.
+     *
+     * This method should be called after startRecording(), since
+     * the some camera hal may choose to allocate the video buffers only after
+     * recording is started.
+     *
+     * Some camera hal may not implement this method, and 0 can be returned to
+     * indicate that this feature is not available.
+     *
+     * @return the number of video buffers that camera hal makes available.
+     *      Zero (0) is returned to indicate that camera hal does not support
+     *      this feature.
+     */
+    virtual int32_t     getNumberOfVideoBuffers() const { return 0; }
+
+    /**
+     * Retrieve the video buffer corresponding to the given index in a
+     * recording session. Must be called again if a new recording session
+     * is started.
+     *
+     * It allows a client to retrieve all video buffers that camera hal makes
+     * available to passing video frame data by calling this method with all
+     * valid index values. The valid index value ranges from 0 to n, where
+     * n = getNumberOfVideoBuffers() - 1. With an index outside of the valid
+     * range, 0 must be returned. This method should be called after
+     * startRecording().
+     *
+     * The video buffers should NOT be modified/released by camera hal
+     * until stopRecording() is called and all outstanding video buffers
+     * previously sent out via CAMERA_MSG_VIDEO_FRAME have been released
+     * via releaseVideoBuffer().
+     *
+     * @param index an index to retrieve the corresponding video buffer.
+     *
+     * @return the video buffer corresponding to the given index.
+     */
+    virtual sp<IMemory> getVideoBuffer(int32_t index) const { return 0; }
+
+    /**
+     * Request the camera hal to store meta data or real YUV data in
+     * the video buffers send out via CAMERA_MSG_VIDEO_FRRAME for a
+     * recording session. If it is not called, the default camera
+     * hal behavior is to store real YUV data in the video buffers.
+     *
+     * This method should be called before startRecording() in order
+     * to be effective.
+     *
+     * If meta data is stored in the video buffers, it is up to the
+     * receiver of the video buffers to interpret the contents and
+     * to find the actual frame data with the help of the meta data
+     * in the buffer. How this is done is outside of the scope of
+     * this method.
+     *
+     * Some camera hal may not support storing meta data in the video
+     * buffers, but all camera hal should support storing real YUV data
+     * in the video buffers. If the camera hal does not support storing
+     * the meta data in the video buffers when it is requested to do
+     * do, INVALID_OPERATION must be returned. It is very useful for
+     * the camera hal to pass meta data rather than the actual frame
+     * data directly to the video encoder, since the amount of the
+     * uncompressed frame data can be very large if video size is large.
+     *
+     * @param enable if true to instruct the camera hal to store
+     *      meta data in the video buffers; false to instruct
+     *      the camera hal to store real YUV data in the video
+     *      buffers.
+     *
+     * @return OK on success.
+     */
+    virtual status_t    storeMetaDataInBuffers(bool enable) {
+                            return enable? INVALID_OPERATION: OK;
+                        }
+
+    /**
      * Start record mode. When a record image is available a CAMERA_MSG_VIDEO_FRAME
      * message is sent with the corresponding frame. Every record frame must be released
      * by calling releaseRecordingFrame().
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 53039a0..60031a4 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -59,6 +59,27 @@
     void setPreviewSize(int width, int height);
     void getPreviewSize(int *width, int *height) const;
     void getSupportedPreviewSizes(Vector<Size> &sizes) const;
+
+    // Set the dimensions in pixels to the given width and height
+    // for video frames. The given width and height must be one
+    // of the supported dimensions returned from
+    // getSupportedVideoSizes(). Must not be called if
+    // getSupportedVideoSizes() returns an empty Vector of Size.
+    void setVideoSize(int width, int height);
+    // Retrieve the current dimensions (width and height)
+    // in pixels for video frames, which must be one of the
+    // supported dimensions returned from getSupportedVideoSizes().
+    // Must not be called if getSupportedVideoSizes() returns an
+    // empty Vector of Size.
+    void getVideoSize(int *width, int *height) const;
+    // Retrieve a Vector of supported dimensions (width and height)
+    // in pixels for video frames. If sizes returned from the method
+    // is empty, the camera does not support calls to setVideoSize()
+    // or getVideoSize(). In adddition, it also indicates that
+    // the camera only has a single output, and does not have
+    // separate output for video frames and preview frame.
+    void getSupportedVideoSizes(Vector<Size> &sizes) const;
+
     void setPreviewFrameRate(int fps);
     int getPreviewFrameRate() const;
     void getPreviewFpsRange(int *min_fps, int *max_fps) const;
@@ -281,6 +302,16 @@
     // Example value: "0.95,1.9,Infinity" or "0.049,0.05,0.051". Read only.
     static const char KEY_FOCUS_DISTANCES[];
 
+    // The current dimensions in pixels (width x height) for video frames.
+    // The width and height must be one of the supported sizes retrieved
+    // via KEY_SUPPORTED_VIDEO_SIZES.
+    // Example value: "1280x720". Read/write.
+    static const char KEY_VIDEO_SIZE[];
+    // A list of the supported dimensions in pixels (width x height)
+    // for video frames. See CAMERA_MSG_VIDEO_FRAME for details in
+    // frameworks/base/include/camera/Camera.h.
+    // Example: "176x144,1280x720". Read only.
+    static const char KEY_SUPPORTED_VIDEO_SIZES[];
     // The image format for video frames. See CAMERA_MSG_VIDEO_FRAME in
     // frameworks/base/include/camera/Camera.h.
     // Example value: "yuv420sp" or PIXEL_FORMAT_XXX constants. Read only.
@@ -354,7 +385,10 @@
     // for barcode reading.
     static const char SCENE_MODE_BARCODE[];
 
-    // Formats for setPreviewFormat and setPictureFormat.
+    // Pixel color formats for KEY_PREVIEW_FORMAT, KEY_PICTURE_FORMAT,
+    // and KEY_VIDEO_FRAME_FORMAT
+    // Planar variant of the YUV420 color format
+    static const char PIXEL_FORMAT_YUV420P[];
     static const char PIXEL_FORMAT_YUV422SP[];
     static const char PIXEL_FORMAT_YUV420SP[]; // NV21
     static const char PIXEL_FORMAT_YUV422I[]; // YUY2
diff --git a/include/camera/ICamera.h b/include/camera/ICamera.h
index 6fcf9e5..b69e075 100644
--- a/include/camera/ICamera.h
+++ b/include/camera/ICamera.h
@@ -20,7 +20,7 @@
 #include <utils/RefBase.h>
 #include <binder/IInterface.h>
 #include <binder/Parcel.h>
-#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
 #include <binder/IMemory.h>
 #include <utils/String8.h>
 #include <camera/Camera.h>
@@ -45,8 +45,8 @@
     // allow other processes to use this ICamera interface
     virtual status_t        unlock() = 0;
 
-    // pass the buffered ISurface to the camera service
-    virtual status_t        setPreviewDisplay(const sp<ISurface>& surface) = 0;
+    // pass the buffered Surface to the camera service
+    virtual status_t        setPreviewDisplay(const sp<Surface>& surface) = 0;
 
     // set the preview callback flag to affect how the received frames from
     // preview are handled.
@@ -90,6 +90,15 @@
 
     // send command to camera driver
     virtual status_t        sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) = 0;
+
+    // return the total number of available video buffers
+    virtual int32_t         getNumberOfVideoBuffers() const  = 0;
+
+    // return the individual video buffer corresponding to the given index.
+    virtual sp<IMemory>     getVideoBuffer(int32_t index) const = 0;
+
+    // tell the camera hal to store meta data or real YUV data in video buffers.
+    virtual status_t        storeMetaDataInBuffers(bool enabled) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/drm/drm_framework_common.h b/include/drm/drm_framework_common.h
index c5765a9..1758cdd 100644
--- a/include/drm/drm_framework_common.h
+++ b/include/drm/drm_framework_common.h
@@ -217,6 +217,10 @@
      * POSIX based Decrypt API set for container based DRM
      */
     static const int CONTAINER_BASED = 0x02;
+    /**
+     * Decrypt API for Widevine streams
+     */
+    static const int WV_BASED = 0x3;
 };
 
 /**
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 9fd905f..1081c35 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -262,11 +262,15 @@
         DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
         DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,
         DEVICE_OUT_AUX_DIGITAL = 0x400,
+        DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800,
+        DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000,
         DEVICE_OUT_DEFAULT = 0x8000,
         DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADSET |
                 DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
                 DEVICE_OUT_BLUETOOTH_SCO_CARKIT | DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
-                DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | DEVICE_OUT_DEFAULT),
+                DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL |
+                DEVICE_OUT_ANLG_DOCK_HEADSET | DEVICE_OUT_DGTL_DOCK_HEADSET |
+                DEVICE_OUT_DEFAULT),
         DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
                 DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
 
@@ -309,6 +313,8 @@
         FORCE_WIRED_ACCESSORY,
         FORCE_BT_CAR_DOCK,
         FORCE_BT_DESK_DOCK,
+        FORCE_ANALOG_DOCK,
+        FORCE_DIGITAL_DOCK,
         NUM_FORCE_CONFIG,
         FORCE_DEFAULT = FORCE_NONE
     };
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index af9a7ed..3d77278 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -25,6 +25,7 @@
 
 class Parcel;
 class ISurface;
+class Surface;
 
 class IMediaPlayer: public IInterface
 {
@@ -33,7 +34,8 @@
 
     virtual void            disconnect() = 0;
 
-    virtual status_t        setVideoSurface(const sp<ISurface>& surface) = 0;
+    virtual status_t        setVideoISurface(const sp<ISurface>& surface) = 0;
+    virtual status_t        setVideoSurface(const sp<Surface>& surface) = 0;
     virtual status_t        prepareAsync() = 0;
     virtual status_t        start() = 0;
     virtual status_t        stop() = 0;
@@ -46,8 +48,6 @@
     virtual status_t        setAudioStreamType(int type) = 0;
     virtual status_t        setLooping(int loop) = 0;
     virtual status_t        setVolume(float leftVolume, float rightVolume) = 0;
-    virtual status_t        suspend() = 0;
-    virtual status_t        resume() = 0;
     virtual status_t        setAuxEffectSendLevel(float level) = 0;
     virtual status_t        attachAuxEffect(int effectId) = 0;
 
diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h
index 54adca8..28be7c1 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -22,7 +22,7 @@
 
 namespace android {
 
-class ISurface;
+class Surface;
 class ICamera;
 class IMediaRecorderClient;
 
@@ -32,7 +32,7 @@
     DECLARE_META_INTERFACE(MediaRecorder);
 
     virtual	status_t		setCamera(const sp<ICamera>& camera) = 0;
-    virtual	status_t		setPreviewSurface(const sp<ISurface>& surface) = 0;
+    virtual	status_t		setPreviewSurface(const sp<Surface>& surface) = 0;
     virtual	status_t		setVideoSource(int vs) = 0;
     virtual	status_t		setAudioSource(int as) = 0;
     virtual	status_t		setOutputFormat(int of) = 0;
@@ -40,6 +40,7 @@
     virtual	status_t		setAudioEncoder(int ae) = 0;
     virtual	status_t		setOutputFile(const char* path) = 0;
     virtual	status_t		setOutputFile(int fd, int64_t offset, int64_t length) = 0;
+    virtual	status_t		setOutputFileAuxiliary(int fd) = 0;
     virtual	status_t		setVideoSize(int width, int height) = 0;
     virtual	status_t		setVideoFrameRate(int frames_per_second) = 0;
     virtual     status_t                setParameters(const String8& params) = 0;
@@ -68,4 +69,3 @@
 }; // namespace android
 
 #endif // ANDROID_IMEDIARECORDER_H
-
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 2f61cbe..fa775e7 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -19,6 +19,7 @@
 #define ANDROID_IOMX_H_
 
 #include <binder/IInterface.h>
+#include <ui/GraphicBuffer.h>
 #include <utils/List.h>
 #include <utils/String8.h>
 
@@ -78,10 +79,20 @@
             node_id node, OMX_INDEXTYPE index,
             const void *params, size_t size) = 0;
 
+    virtual status_t storeMetaDataInBuffers(
+            node_id node, OMX_U32 port_index, OMX_BOOL enable) = 0;
+
+    virtual status_t enableGraphicBuffers(
+            node_id node, OMX_U32 port_index, OMX_BOOL enable) = 0;
+
     virtual status_t useBuffer(
             node_id node, OMX_U32 port_index, const sp<IMemory> &params,
             buffer_id *buffer) = 0;
 
+    virtual status_t useGraphicBuffer(
+            node_id node, OMX_U32 port_index,
+            const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) = 0;
+
     // This API clearly only makes sense if the caller lives in the
     // same process as the callee, i.e. is the media_server, as the
     // returned "buffer_data" pointer is just that, a pointer into local
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 0521709..2d55a55 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -33,6 +33,7 @@
 
 class Parcel;
 class ISurface;
+class Surface;
 
 template<typename T> class SortedVector;
 
@@ -105,7 +106,8 @@
             const KeyedVector<String8, String8> *headers = NULL) = 0;
 
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length) = 0;
-    virtual status_t    setVideoSurface(const sp<ISurface>& surface) = 0;
+    virtual status_t    setVideoISurface(const sp<ISurface>& surface) = 0;
+    virtual status_t    setVideoSurface(const sp<Surface>& surface) = 0;
     virtual status_t    prepare() = 0;
     virtual status_t    prepareAsync() = 0;
     virtual status_t    start() = 0;
@@ -118,8 +120,6 @@
     virtual status_t    reset() = 0;
     virtual status_t    setLooping(int loop) = 0;
     virtual player_type playerType() = 0;
-    virtual status_t    suspend() { return INVALID_OPERATION; }
-    virtual status_t    resume() { return INVALID_OPERATION; }
 
     virtual void        setNotifyCallback(void* cookie, notify_callback_f notifyFunc) {
                             mCookie = cookie; mNotify = notifyFunc; }
diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h
index c3cd361..aa97874 100644
--- a/include/media/MediaProfiles.h
+++ b/include/media/MediaProfiles.h
@@ -25,7 +25,20 @@
 
 enum camcorder_quality {
     CAMCORDER_QUALITY_LOW  = 0,
-    CAMCORDER_QUALITY_HIGH = 1
+    CAMCORDER_QUALITY_HIGH = 1,
+    CAMCORDER_QUALITY_QCIF = 2,
+    CAMCORDER_QUALITY_CIF = 3,
+    CAMCORDER_QUALITY_480P = 4,
+    CAMCORDER_QUALITY_720P = 5,
+    CAMCORDER_QUALITY_1080P = 6,
+
+    CAMCORDER_QUALITY_TIME_LAPSE_LOW  = 1000,
+    CAMCORDER_QUALITY_TIME_LAPSE_HIGH = 1001,
+    CAMCORDER_QUALITY_TIME_LAPSE_QCIF = 1002,
+    CAMCORDER_QUALITY_TIME_LAPSE_CIF = 1003,
+    CAMCORDER_QUALITY_TIME_LAPSE_480P = 1004,
+    CAMCORDER_QUALITY_TIME_LAPSE_720P = 1005,
+    CAMCORDER_QUALITY_TIME_LAPSE_1080P = 1006
 };
 
 enum video_decoder {
@@ -68,6 +81,12 @@
                                        camcorder_quality quality) const;
 
     /**
+     * Returns true if a profile for the given camera at the given quality exists,
+     * or false if not.
+     */
+    bool hasCamcorderProfile(int cameraId, camcorder_quality quality) const;
+
+    /**
      * Returns the output file formats supported.
      */
     Vector<output_format> getOutputFileFormats() const;
@@ -252,6 +271,8 @@
         Vector<int> mLevels;
     };
 
+    int getCamcorderProfileIndex(int cameraId, camcorder_quality quality) const;
+
     // Debug
     static void logVideoCodec(const VideoCodec& codec);
     static void logAudioCodec(const AudioCodec& codec);
@@ -281,8 +302,25 @@
 
     // If the xml configuration file does not exist, use hard-coded values
     static MediaProfiles* createDefaultInstance();
-    static CamcorderProfile *createDefaultCamcorderLowProfile();
-    static CamcorderProfile *createDefaultCamcorderHighProfile();
+
+    static CamcorderProfile *createDefaultCamcorderQcifProfile(camcorder_quality quality);
+    static CamcorderProfile *createDefaultCamcorderCifProfile(camcorder_quality quality);
+    static void createDefaultCamcorderLowProfiles(
+            MediaProfiles::CamcorderProfile **lowProfile,
+            MediaProfiles::CamcorderProfile **lowSpecificProfile);
+    static void createDefaultCamcorderHighProfiles(
+            MediaProfiles::CamcorderProfile **highProfile,
+            MediaProfiles::CamcorderProfile **highSpecificProfile);
+
+    static CamcorderProfile *createDefaultCamcorderTimeLapseQcifProfile(camcorder_quality quality);
+    static CamcorderProfile *createDefaultCamcorderTimeLapse480pProfile(camcorder_quality quality);
+    static void createDefaultCamcorderTimeLapseLowProfiles(
+            MediaProfiles::CamcorderProfile **lowTimeLapseProfile,
+            MediaProfiles::CamcorderProfile **lowSpecificTimeLapseProfile);
+    static void createDefaultCamcorderTimeLapseHighProfiles(
+            MediaProfiles::CamcorderProfile **highTimeLapseProfile,
+            MediaProfiles::CamcorderProfile **highSpecificTimeLapseProfile);
+
     static void createDefaultCamcorderProfiles(MediaProfiles *profiles);
     static void createDefaultVideoEncoders(MediaProfiles *profiles);
     static void createDefaultAudioEncoders(MediaProfiles *profiles);
diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index 5e9e368..c42346e 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -22,7 +22,7 @@
 
 namespace android {
 
-class ISurface;
+class Surface;
 
 struct MediaRecorderBase {
     MediaRecorderBase() {}
@@ -37,9 +37,10 @@
     virtual status_t setVideoSize(int width, int height) = 0;
     virtual status_t setVideoFrameRate(int frames_per_second) = 0;
     virtual status_t setCamera(const sp<ICamera>& camera) = 0;
-    virtual status_t setPreviewSurface(const sp<ISurface>& surface) = 0;
+    virtual status_t setPreviewSurface(const sp<Surface>& surface) = 0;
     virtual status_t setOutputFile(const char *path) = 0;
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0;
+    virtual status_t setOutputFileAuxiliary(int fd) {return INVALID_OPERATION;}
     virtual status_t setParameters(const String8& params) = 0;
     virtual status_t setListener(const sp<IMediaRecorderClient>& listener) = 0;
     virtual status_t prepare() = 0;
diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h
index c091c39..4b44ccc 100644
--- a/include/media/PVMediaRecorder.h
+++ b/include/media/PVMediaRecorder.h
@@ -23,7 +23,7 @@
 
 namespace android {
 
-class ISurface;
+class Surface;
 class ICamera;
 class AuthorDriverWrapper;
 
@@ -41,7 +41,7 @@
     virtual status_t setVideoSize(int width, int height);
     virtual status_t setVideoFrameRate(int frames_per_second);
     virtual status_t setCamera(const sp<ICamera>& camera);
-    virtual status_t setPreviewSurface(const sp<ISurface>& surface);
+    virtual status_t setPreviewSurface(const sp<Surface>& surface);
     virtual status_t setOutputFile(const char *path);
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
     virtual status_t setParameters(const String8& params);
@@ -66,4 +66,3 @@
 }; // namespace android
 
 #endif // ANDROID_PVMEDIARECORDER_H
-
diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h
index df50981..657e7a6 100644
--- a/include/media/PVPlayer.h
+++ b/include/media/PVPlayer.h
@@ -43,7 +43,8 @@
             const char *url, const KeyedVector<String8, String8> *headers);
 
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length);
-    virtual status_t    setVideoSurface(const sp<ISurface>& surface);
+    virtual status_t    setVideoISurface(const sp<ISurface>& surface);
+    virtual status_t    setVideoSurface(const sp<Surface>& surface);
     virtual status_t    prepare();
     virtual status_t    prepareAsync();
     virtual status_t    start();
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 207191d..88b0c3e 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -169,8 +169,6 @@
             status_t        invoke(const Parcel& request, Parcel *reply);
             status_t        setMetadataFilter(const Parcel& filter);
             status_t        getMetadata(bool update_only, bool apply_filter, Parcel *metadata);
-            status_t        suspend();
-            status_t        resume();
             status_t        setAudioSessionId(int sessionId);
             int             getAudioSessionId();
             status_t        setAuxEffectSendLevel(float level);
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 5ab1640..32b6fa1 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -173,6 +173,7 @@
     status_t    setAudioEncoder(int ae);
     status_t    setOutputFile(const char* path);
     status_t    setOutputFile(int fd, int64_t offset, int64_t length);
+    status_t    setOutputFileAuxiliary(int fd);
     status_t    setVideoSize(int width, int height);
     status_t    setVideoFrameRate(int frames_per_second);
     status_t    setParameters(const String8& params);
@@ -199,6 +200,7 @@
     bool                        mIsAudioEncoderSet;
     bool                        mIsVideoEncoderSet;
     bool                        mIsOutputFileSet;
+    bool                        mIsAuxiliaryOutputFileSet;
     Mutex                       mLock;
     Mutex                       mNotifyLock;
 };
diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h
index 0d397ac..74c9d5d 100644
--- a/include/media/mediascanner.h
+++ b/include/media/mediascanner.h
@@ -38,8 +38,7 @@
 
     typedef bool (*ExceptionCheck)(void* env);
     virtual status_t processDirectory(
-            const char *path, const char *extensions,
-            MediaScannerClient &client,
+            const char *path, MediaScannerClient &client,
             ExceptionCheck exceptionCheck, void *exceptionEnv);
 
     void setLocale(const char *locale);
@@ -55,9 +54,8 @@
     char *mLocale;
 
     status_t doProcessDirectory(
-            char *path, int pathRemaining, const char *extensions,
-            MediaScannerClient &client, ExceptionCheck exceptionCheck,
-            void *exceptionEnv);
+            char *path, int pathRemaining, MediaScannerClient &client,
+            ExceptionCheck exceptionCheck, void *exceptionEnv);
 
     MediaScanner(const MediaScanner &);
     MediaScanner &operator=(const MediaScanner &);
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index 3192d03..e6c9f93 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -20,39 +20,167 @@
 
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaSource.h>
+#include <camera/ICamera.h>
+#include <camera/CameraParameters.h>
 #include <utils/List.h>
 #include <utils/RefBase.h>
-#include <utils/threads.h>
 
 namespace android {
 
-class ICamera;
 class IMemory;
 class Camera;
+class Surface;
 
 class CameraSource : public MediaSource, public MediaBufferObserver {
 public:
+    /**
+     * Factory method to create a new CameraSource using the current
+     * settings (such as video size, frame rate, color format, etc)
+     * from the default camera.
+     *
+     * @return NULL on error.
+     */
     static CameraSource *Create();
-    static CameraSource *CreateFromCamera(const sp<Camera> &camera);
+
+    /**
+     * Factory method to create a new CameraSource.
+     *
+     * @param camera the video input frame data source. If it is NULL,
+     *          we will try to connect to the camera with the given
+     *          cameraId.
+     *
+     * @param cameraId the id of the camera that the source will connect
+     *          to if camera is NULL; otherwise ignored.
+     *
+     * @param videoSize the dimension (in pixels) of the video frame
+     * @param frameRate the target frames per second
+     * @param surface the preview surface for display where preview
+     *          frames are sent to
+     * @param storeMetaDataInVideoBuffers true to request the camera
+     *          source to store meta data in video buffers; false to
+     *          request the camera source to store real YUV frame data
+     *          in the video buffers. The camera source may not support
+     *          storing meta data in video buffers, if so, a request
+     *          to do that will NOT be honored. To find out whether
+     *          meta data is actually being stored in video buffers
+     *          during recording, call isMetaDataStoredInVideoBuffers().
+     *
+     * @return NULL on error.
+     */
+    static CameraSource *CreateFromCamera(const sp<ICamera> &camera,
+                                          int32_t cameraId,
+                                          Size videoSize,
+                                          int32_t frameRate,
+                                          const sp<Surface>& surface,
+                                          bool storeMetaDataInVideoBuffers = false);
 
     virtual ~CameraSource();
 
     virtual status_t start(MetaData *params = NULL);
     virtual status_t stop();
-
-    virtual sp<MetaData> getFormat();
-
     virtual status_t read(
             MediaBuffer **buffer, const ReadOptions *options = NULL);
 
+    /**
+     * Check whether a CameraSource object is properly initialized.
+     * Must call this method before stop().
+     * @return OK if initialization has successfully completed.
+     */
+    virtual status_t initCheck() const;
+
+    /**
+     * Returns the MetaData associated with the CameraSource,
+     * including:
+     * kKeyColorFormat: YUV color format of the video frames
+     * kKeyWidth, kKeyHeight: dimension (in pixels) of the video frames
+     * kKeySampleRate: frame rate in frames per second
+     * kKeyMIMEType: always fixed to be MEDIA_MIMETYPE_VIDEO_RAW
+     */
+    virtual sp<MetaData> getFormat();
+
+    /**
+     * Retrieve the total number of video buffers available from
+     * this source.
+     *
+     * This method is useful if these video buffers are used
+     * for passing video frame data to other media components,
+     * such as OMX video encoders, in order to eliminate the
+     * memcpy of the data.
+     *
+     * @return the total numbner of video buffers. Returns 0 to
+     *      indicate that this source does not make the video
+     *      buffer information availalble.
+     */
+    size_t getNumberOfVideoBuffers() const;
+
+    /**
+     * Retrieve the individual video buffer available from
+     * this source.
+     *
+     * @param index the index corresponding to the video buffer.
+     *      Valid range of the index is [0, n], where n =
+     *      getNumberOfVideoBuffers() - 1.
+     *
+     * @return the video buffer corresponding to the given index.
+     *      If index is out of range, 0 should be returned.
+     */
+    sp<IMemory> getVideoBuffer(size_t index) const;
+
+    /**
+     * Tell whether this camera source stores meta data or real YUV
+     * frame data in video buffers.
+     *
+     * @return true if meta data is stored in the video
+     *      buffers; false if real YUV data is stored in
+     *      the video buffers.
+     */
+    bool isMetaDataStoredInVideoBuffers() const;
+
     virtual void signalBufferReturned(MediaBuffer* buffer);
 
+protected:
+    enum CameraFlags {
+        FLAGS_SET_CAMERA = 1L << 0,
+        FLAGS_HOT_CAMERA = 1L << 1,
+    };
+
+    int32_t  mCameraFlags;
+    Size     mVideoSize;
+    int32_t  mVideoFrameRate;
+    int32_t  mColorFormat;
+    status_t mInitCheck;
+
+    sp<Camera>   mCamera;
+    sp<Surface>  mSurface;
+    sp<MetaData> mMeta;
+
+    int64_t mStartTimeUs;
+    int32_t mNumFramesReceived;
+    int64_t mLastFrameTimestampUs;
+    bool mStarted;
+
+    CameraSource(const sp<ICamera>& camera, int32_t cameraId,
+                 Size videoSize, int32_t frameRate,
+                 const sp<Surface>& surface,
+                 bool storeMetaDataInVideoBuffers);
+
+    virtual void startCameraRecording();
+    virtual void stopCameraRecording();
+    virtual void releaseRecordingFrame(const sp<IMemory>& frame);
+
+    // Returns true if need to skip the current frame.
+    // Called from dataCallbackTimestamp.
+    virtual bool skipCurrentFrame(int64_t timestampUs) {return false;}
+
+    // Callback called when still camera raw data is available.
+    virtual void dataCallback(int32_t msgType, const sp<IMemory> &data) {}
+
+    virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
+            const sp<IMemory> &data);
+
 private:
     friend class CameraSourceListener;
 
-    sp<Camera> mCamera;
-    sp<MetaData> mMeta;
-
     Mutex mLock;
     Condition mFrameAvailableCondition;
     Condition mFrameCompleteCondition;
@@ -60,25 +188,33 @@
     List<sp<IMemory> > mFramesBeingEncoded;
     List<int64_t> mFrameTimes;
 
-    int64_t mStartTimeUs;
     int64_t mFirstFrameTimeUs;
-    int64_t mLastFrameTimestampUs;
-    int32_t mNumFramesReceived;
     int32_t mNumFramesEncoded;
     int32_t mNumFramesDropped;
     int32_t mNumGlitches;
     int64_t mGlitchDurationThresholdUs;
     bool mCollectStats;
-    bool mStarted;
-
-    CameraSource(const sp<Camera> &camera);
-
-    void dataCallbackTimestamp(
-            int64_t timestampUs, int32_t msgType, const sp<IMemory> &data);
+    bool mIsMetaDataStoredInVideoBuffers;
 
     void releaseQueuedFrames();
     void releaseOneRecordingFrame(const sp<IMemory>& frame);
 
+
+    status_t init(const sp<ICamera>& camera, int32_t cameraId,
+                Size videoSize, int32_t frameRate,
+                bool storeMetaDataInVideoBuffers);
+    status_t isCameraAvailable(const sp<ICamera>& camera, int32_t cameraId);
+    status_t isCameraColorFormatSupported(const CameraParameters& params);
+    status_t configureCamera(CameraParameters* params,
+                    int32_t width, int32_t height,
+                    int32_t frameRate);
+
+    status_t checkVideoSize(const CameraParameters& params,
+                    int32_t width, int32_t height);
+
+    status_t checkFrameRate(const CameraParameters& params,
+                    int32_t frameRate);
+
     CameraSource(const CameraSource &);
     CameraSource &operator=(const CameraSource &);
 };
diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h
new file mode 100644
index 0000000..afe7287
--- /dev/null
+++ b/include/media/stagefright/CameraSourceTimeLapse.h
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CAMERA_SOURCE_TIME_LAPSE_H_
+
+#define CAMERA_SOURCE_TIME_LAPSE_H_
+
+#include <pthread.h>
+
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class ICamera;
+class IMemory;
+class Camera;
+
+class CameraSourceTimeLapse : public CameraSource {
+public:
+    static CameraSourceTimeLapse *CreateFromCamera(
+        const sp<ICamera> &camera,
+        int32_t cameraId,
+        Size videoSize,
+        int32_t videoFrameRate,
+        const sp<Surface>& surface,
+        int64_t timeBetweenTimeLapseFrameCaptureUs);
+
+    virtual ~CameraSourceTimeLapse();
+
+    // If the frame capture interval is large, read will block for a long time.
+    // Due to the way the mediaRecorder framework works, a stop() call from
+    // mediaRecorder waits until the read returns, causing a long wait for
+    // stop() to return. To avoid this, we can make read() return a copy of the
+    // last read frame with the same time stamp frequently. This keeps the
+    // read() call from blocking too long. Calling this function quickly
+    // captures another frame, keeps its copy, and enables this mode of read()
+    // returning quickly.
+    void startQuickReadReturns();
+
+private:
+    // If true, will use still camera takePicture() for time lapse frames
+    // If false, will use the videocamera frames instead.
+    bool mUseStillCameraForTimeLapse;
+
+    // Size of picture taken from still camera. This may be larger than the size
+    // of the video, as still camera may not support the exact video resolution
+    // demanded. See setPictureSizeToClosestSupported().
+    int32_t mPictureWidth;
+    int32_t mPictureHeight;
+
+    // size of the encoded video.
+    int32_t mVideoWidth;
+    int32_t mVideoHeight;
+
+    // True if we need to crop the still camera image to get the video frame.
+    bool mNeedCropping;
+
+    // Start location of the cropping rectangle.
+    int32_t mCropRectStartX;
+    int32_t mCropRectStartY;
+
+    // Time between capture of two frames during time lapse recording
+    // Negative value indicates that timelapse is disabled.
+    int64_t mTimeBetweenTimeLapseFrameCaptureUs;
+
+    // Time between two frames in final video (1/frameRate)
+    int64_t mTimeBetweenTimeLapseVideoFramesUs;
+
+    // Real timestamp of the last encoded time lapse frame
+    int64_t mLastTimeLapseFrameRealTimestampUs;
+
+    // Thread id of thread which takes still picture and sleeps in a loop.
+    pthread_t mThreadTimeLapse;
+
+    // Variable set in dataCallbackTimestamp() to help skipCurrentFrame()
+    // to know if current frame needs to be skipped.
+    bool mSkipCurrentFrame;
+
+    // Lock for accessing mCameraIdle
+    Mutex mCameraIdleLock;
+
+    // Condition variable to wait on if camera is is not yet idle. Once the
+    // camera gets idle, this variable will be signalled.
+    Condition mCameraIdleCondition;
+
+    // True if camera is in preview mode and ready for takePicture().
+    // False after a call to takePicture() but before the final compressed
+    // data callback has been called and preview has been restarted.
+    volatile bool mCameraIdle;
+
+    // True if stop() is waiting for camera to get idle, i.e. for the last
+    // takePicture() to complete. This is needed so that dataCallbackTimestamp()
+    // can return immediately.
+    volatile bool mStopWaitingForIdleCamera;
+
+    // Lock for accessing quick stop variables.
+    Mutex mQuickStopLock;
+
+    // Condition variable to wake up still picture thread.
+    Condition mTakePictureCondition;
+
+    // mQuickStop is set to true if we use quick read() returns, otherwise it is set
+    // to false. Once in this mode read() return a copy of the last read frame
+    // with the same time stamp. See startQuickReadReturns().
+    volatile bool mQuickStop;
+
+    // Forces the next frame passed to dataCallbackTimestamp() to be read
+    // as a time lapse frame. Used by startQuickReadReturns() so that the next
+    // frame wakes up any blocking read.
+    volatile bool mForceRead;
+
+    // Stores a copy of the MediaBuffer read in the last read() call after
+    // mQuickStop was true.
+    MediaBuffer* mLastReadBufferCopy;
+
+    // Status code for last read.
+    status_t mLastReadStatus;
+
+    CameraSourceTimeLapse(
+        const sp<ICamera> &camera,
+        int32_t cameraId,
+        Size videoSize,
+        int32_t videoFrameRate,
+        const sp<Surface>& surface,
+        int64_t timeBetweenTimeLapseFrameCaptureUs);
+
+    // Wrapper over CameraSource::signalBufferReturned() to implement quick stop.
+    // It only handles the case when mLastReadBufferCopy is signalled. Otherwise
+    // it calls the base class' function.
+    virtual void signalBufferReturned(MediaBuffer* buffer);
+
+    // Wrapper over CameraSource::read() to implement quick stop.
+    virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+    // For still camera case starts a thread which calls camera's takePicture()
+    // in a loop. For video camera case, just starts the camera's video recording.
+    virtual void startCameraRecording();
+
+    // For still camera case joins the thread created in startCameraRecording().
+    // For video camera case, just stops the camera's video recording.
+    virtual void stopCameraRecording();
+
+    // For still camera case don't need to do anything as memory is locally
+    // allocated with refcounting.
+    // For video camera case just tell the camera to release the frame.
+    virtual void releaseRecordingFrame(const sp<IMemory>& frame);
+
+    // mSkipCurrentFrame is set to true in dataCallbackTimestamp() if the current
+    // frame needs to be skipped and this function just returns the value of mSkipCurrentFrame.
+    virtual bool skipCurrentFrame(int64_t timestampUs);
+
+    // Handles the callback to handle raw frame data from the still camera.
+    // Creates a copy of the frame data as the camera can reuse the frame memory
+    // once this callback returns. The function also sets a new timstamp corresponding
+    // to one frame time ahead of the last encoded frame's time stamp. It then
+    // calls dataCallbackTimestamp() of the base class with the copied data and the
+    // modified timestamp, which will think that it recieved the frame from a video
+    // camera and proceed as usual.
+    virtual void dataCallback(int32_t msgType, const sp<IMemory> &data);
+
+    // In the video camera case calls skipFrameAndModifyTimeStamp() to modify
+    // timestamp and set mSkipCurrentFrame.
+    // Then it calls the base CameraSource::dataCallbackTimestamp()
+    virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
+            const sp<IMemory> &data);
+
+    // Convenience function to fill mLastReadBufferCopy from the just read
+    // buffer.
+    void fillLastReadBufferCopy(MediaBuffer& sourceBuffer);
+
+    // If the passed in size (width x height) is a supported preview size,
+    // the function sets the camera's preview size to it and returns true.
+    // Otherwise returns false.
+    bool trySettingPreviewSize(int32_t width, int32_t height);
+
+    // The still camera may not support the demanded video width and height.
+    // We look for the supported picture sizes from the still camera and
+    // choose the smallest one with either dimensions higher than the corresponding
+    // video dimensions. The still picture will be cropped to get the video frame.
+    // The function returns true if the camera supports picture sizes greater than
+    // or equal to the passed in width and height, and false otherwise.
+    bool setPictureSizeToClosestSupported(int32_t width, int32_t height);
+
+    // Computes the offset of the rectangle from where to start cropping the
+    // still image into the video frame. We choose the center of the image to be
+    // cropped. The offset is stored in (mCropRectStartX, mCropRectStartY).
+    bool computeCropRectangleOffset();
+
+    // Crops the source data into a smaller image starting at
+    // (mCropRectStartX, mCropRectStartY) and of the size of the video frame.
+    // The data is returned into a newly allocated IMemory.
+    sp<IMemory> cropYUVImage(const sp<IMemory> &source_data);
+
+    // When video camera is used for time lapse capture, returns true
+    // until enough time has passed for the next time lapse frame. When
+    // the frame needs to be encoded, it returns false and also modifies
+    // the time stamp to be one frame time ahead of the last encoded
+    // frame's time stamp.
+    bool skipFrameAndModifyTimeStamp(int64_t *timestampUs);
+
+    // Wrapper to enter threadTimeLapseEntry()
+    static void *ThreadTimeLapseWrapper(void *me);
+
+    // Runs a loop which sleeps until a still picture is required
+    // and then calls mCamera->takePicture() to take the still picture.
+    // Used only in the case mUseStillCameraForTimeLapse = true.
+    void threadTimeLapseEntry();
+
+    // Wrapper to enter threadStartPreview()
+    static void *ThreadStartPreviewWrapper(void *me);
+
+    // Starts the camera's preview.
+    void threadStartPreview();
+
+    // Starts thread ThreadStartPreviewWrapper() for restarting preview.
+    // Needs to be done in a thread so that dataCallback() which calls this function
+    // can return, and the camera can know that takePicture() is done.
+    void restartPreview();
+
+    // Creates a copy of source_data into a new memory of final type MemoryBase.
+    sp<IMemory> createIMemoryCopy(const sp<IMemory> &source_data);
+
+    CameraSourceTimeLapse(const CameraSourceTimeLapse &);
+    CameraSourceTimeLapse &operator=(const CameraSourceTimeLapse &);
+};
+
+}  // namespace android
+
+#endif  // CAMERA_SOURCE_TIME_LAPSE_H_
diff --git a/include/media/stagefright/HardwareAPI.h b/include/media/stagefright/HardwareAPI.h
index 221c679..4fd281b 100644
--- a/include/media/stagefright/HardwareAPI.h
+++ b/include/media/stagefright/HardwareAPI.h
@@ -21,10 +21,76 @@
 #include <media/stagefright/OMXPluginBase.h>
 #include <media/stagefright/VideoRenderer.h>
 #include <surfaceflinger/ISurface.h>
+#include <ui/android_native_buffer.h>
 #include <utils/RefBase.h>
 
 #include <OMX_Component.h>
 
+namespace android {
+
+// A pointer to this struct is passed to the OMX_SetParameter when the extension
+// index for the 'OMX.google.android.index.enableAndroidNativeBuffers' extension
+// is given.
+//
+// When Android native buffer use is disabled for a port (the default state),
+// the OMX node should operate as normal, and expect UseBuffer calls to set its
+// buffers.  This is the mode that will be used when CPU access to the buffer is
+// required.
+//
+// When Android native buffer use has been enabled for a given port, the video
+// color format for the port is to be interpreted as an Android pixel format
+// rather than an OMX color format.  The node should then expect to receive
+// UseAndroidNativeBuffer calls (via OMX_SetParameter) rather than UseBuffer
+// calls for that port.
+struct EnableAndroidNativeBuffersParams {
+    OMX_U32 nSize;
+    OMX_VERSIONTYPE nVersion;
+    OMX_U32 nPortIndex;
+    OMX_BOOL enable;
+};
+
+// A pointer to this struct is passed to OMX_SetParameter() when the extension
+// index "OMX.google.android.index.storeMetaDataInBuffers"
+// is given.
+//
+// When meta data is stored in the video buffers passed between OMX clients
+// and OMX components, interpretation of the buffer data is up to the
+// buffer receiver, and the data may or may not be the actual video data, but
+// some information helpful for the receiver to locate the actual data.
+// The buffer receiver thus needs to know how to interpret what is stored
+// in these buffers, with mechanisms pre-determined externally. How to
+// interpret the meta data is outside of the scope of this method.
+//
+// Currently, this is specifically used to pass meta data from video source
+// (camera component, for instance) to video encoder to avoid memcpying of
+// input video frame data. To do this, bStoreMetaDta is set to OMX_TRUE.
+// If bStoreMetaData is set to false, real YUV frame data will be stored
+// in the buffers. In addition, if no OMX_SetParameter() call is made
+// with the corresponding extension index, real YUV data is stored
+// in the buffers.
+struct StoreMetaDataInBuffersParams {
+    OMX_U32 nSize;
+    OMX_VERSIONTYPE nVersion;
+    OMX_U32 nPortIndex;
+    OMX_BOOL bStoreMetaData;
+};
+
+// A pointer to this struct is passed to OMX_SetParameter when the extension
+// index for the 'OMX.google.android.index.useAndroidNativeBuffer' extension is
+// given.  This call will only be performed if a prior call was made with the
+// 'OMX.google.android.index.enableAndroidNativeBuffers' extension index,
+// enabling use of Android native buffers.
+struct UseAndroidNativeBufferParams {
+    OMX_U32 nSize;
+    OMX_VERSIONTYPE nVersion;
+    OMX_U32 nPortIndex;
+    OMX_PTR pAppPrivate;
+    OMX_BUFFERHEADERTYPE **bufferHeader;
+    const sp<android_native_buffer_t>& nativeBuffer;
+};
+
+}  // namespace android
+
 extern android::VideoRenderer *createRenderer(
         const android::sp<android::ISurface> &surface,
         const char *componentName,
@@ -35,4 +101,3 @@
 extern android::OMXPluginBase *createOMXPlugin();
 
 #endif  // HARDWARE_API_H_
-
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
index 339e6fb..c1c4f94 100644
--- a/include/media/stagefright/MediaBuffer.h
+++ b/include/media/stagefright/MediaBuffer.h
@@ -25,6 +25,7 @@
 
 namespace android {
 
+class GraphicBuffer;
 class MediaBuffer;
 class MediaBufferObserver;
 class MetaData;
@@ -48,6 +49,8 @@
 
     MediaBuffer(size_t size);
 
+    MediaBuffer(const sp<GraphicBuffer>& graphicBuffer);
+
     // Decrements the reference count and returns the buffer to its
     // associated MediaBufferGroup if the reference count drops to 0.
     void release();
@@ -63,6 +66,8 @@
 
     void set_range(size_t offset, size_t length);
 
+    sp<GraphicBuffer> graphicBuffer() const;
+
     sp<MetaData> meta_data();
 
     // Clears meta data and resets the range to the full extent.
@@ -94,6 +99,7 @@
 
     void *mData;
     size_t mSize, mRangeOffset, mRangeLength;
+    sp<GraphicBuffer> mGraphicBuffer;
 
     bool mOwnsData;
 
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 92ce068..2d50ca5 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -44,6 +44,8 @@
 extern const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA;
 extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS;
 
+extern const char *MEDIA_MIMETYPE_CONTAINER_WVM;
+
 }  // namespace android
 
 #endif  // MEDIA_DEFS_H_
diff --git a/include/media/stagefright/MediaSourceSplitter.h b/include/media/stagefright/MediaSourceSplitter.h
new file mode 100644
index 0000000..568f4c2
--- /dev/null
+++ b/include/media/stagefright/MediaSourceSplitter.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This class provides a way to split a single media source into multiple sources.
+// The constructor takes in the real mediaSource and createClient() can then be
+// used to create multiple sources served from this real mediaSource.
+//
+// Usage:
+// - Create MediaSourceSplitter by passing in a real mediaSource from which
+// multiple duplicate channels are needed.
+// - Create a client using createClient() and use it as any other mediaSource.
+//
+// Note that multiple clients can be created using createClient() and
+// started/stopped in any order. MediaSourceSplitter stops the real source only
+// when all clients have been stopped.
+//
+// If a new client is created/started after some existing clients have already
+// started, the new client will start getting its read frames from the current
+// time.
+
+#ifndef MEDIA_SOURCE_SPLITTER_H_
+
+#define MEDIA_SOURCE_SPLITTER_H_
+
+#include <media/stagefright/MediaSource.h>
+#include <utils/threads.h>
+#include <utils/Vector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class MediaBuffer;
+class MetaData;
+
+class MediaSourceSplitter : public RefBase {
+public:
+    // Constructor
+    // mediaSource: The real mediaSource. The class keeps a reference to it to
+    // implement the various clients.
+    MediaSourceSplitter(sp<MediaSource> mediaSource);
+
+    ~MediaSourceSplitter();
+
+    // Creates a new client of base type MediaSource. Multiple clients can be
+    // created which get their data through the same real mediaSource. These
+    // clients can then be used like any other MediaSource, all of which provide
+    // data from the same real source.
+    sp<MediaSource> createClient();
+
+private:
+    // Total number of clients created through createClient().
+    int32_t mNumberOfClients;
+
+    // reference to the real MediaSource passed to the constructor.
+    sp<MediaSource> mSource;
+
+    // Stores pointer to the MediaBuffer read from the real MediaSource.
+    // All clients use this to implement the read() call.
+    MediaBuffer *mLastReadMediaBuffer;
+
+    // Status code for read from the real MediaSource. All clients return
+    // this for their read().
+    status_t mLastReadStatus;
+
+    // Boolean telling whether the real MediaSource has started.
+    bool mSourceStarted;
+
+    // List of booleans, one for each client, storing whether the corresponding
+    // client's start() has been called.
+    Vector<bool> mClientsStarted;
+
+    // Stores the number of clients which are currently started.
+    int32_t mNumberOfClientsStarted;
+
+    // Since different clients call read() asynchronously, we need to keep track
+    // of what data is currently read into the mLastReadMediaBuffer.
+    // mCurrentReadBit stores the bit for the current read buffer. This bit
+    // flips each time a new buffer is read from the source.
+    // mClientsDesiredReadBit stores the bit for the next desired read buffer
+    // for each client. This bit flips each time read() is completed for this
+    // client.
+    bool mCurrentReadBit;
+    Vector<bool> mClientsDesiredReadBit;
+
+    // Number of clients whose current read has been completed.
+    int32_t mNumberOfCurrentReads;
+
+    // Boolean telling whether the last read has been completed for all clients.
+    // The variable is reset to false each time buffer is read from the real
+    // source.
+    bool mLastReadCompleted;
+
+    // A global mutex for access to critical sections.
+    Mutex mLock;
+
+    // Condition variable for waiting on read from source to complete.
+    Condition mReadFromSourceCondition;
+
+    // Condition variable for waiting on all client's last read to complete.
+    Condition mAllReadsCompleteCondition;
+
+    // Functions used by Client to implement the MediaSource interface.
+
+    // If the real source has not been started yet by any client, starts it.
+    status_t start(int clientId, MetaData *params);
+
+    // Stops the real source after all clients have called stop().
+    status_t stop(int clientId);
+
+    // returns the real source's getFormat().
+    sp<MetaData> getFormat(int clientId);
+
+    // If the client's desired buffer has already been read into
+    // mLastReadMediaBuffer, points the buffer to that. Otherwise if it is the
+    // master client, reads the buffer from source or else waits for the master
+    // client to read the buffer and uses that.
+    status_t read(int clientId,
+            MediaBuffer **buffer, const MediaSource::ReadOptions *options = NULL);
+
+    // Not implemented right now.
+    status_t pause(int clientId);
+
+    // Function which reads a buffer from the real source into
+    // mLastReadMediaBuffer
+    void readFromSource_lock(const MediaSource::ReadOptions *options);
+
+    // Waits until read from the real source has been completed.
+    // _lock means that the function should be called when the thread has already
+    // obtained the lock for the mutex mLock.
+    void waitForReadFromSource_lock(int32_t clientId);
+
+    // Waits until all clients have read the current buffer in
+    // mLastReadCompleted.
+    void waitForAllClientsLastRead_lock(int32_t clientId);
+
+    // Each client calls this after it completes its read(). Once all clients
+    // have called this for the current buffer, the function calls
+    // mAllReadsCompleteCondition.broadcast() to signal the waiting clients.
+    void signalReadComplete_lock(bool readAborted);
+
+    // Make these constructors private.
+    MediaSourceSplitter();
+    MediaSourceSplitter(const MediaSourceSplitter &);
+    MediaSourceSplitter &operator=(const MediaSourceSplitter &);
+
+    // This class implements the MediaSource interface. Each client stores a
+    // reference to the parent MediaSourceSplitter and uses it to complete the
+    // various calls.
+    class Client : public MediaSource {
+    public:
+        // Constructor stores reference to the parent MediaSourceSplitter and it
+        // client id.
+        Client(sp<MediaSourceSplitter> splitter, int32_t clientId);
+
+        // MediaSource interface
+        virtual status_t start(MetaData *params = NULL);
+
+        virtual status_t stop();
+
+        virtual sp<MetaData> getFormat();
+
+        virtual status_t read(
+                MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+        virtual status_t pause();
+
+    private:
+        // Refernce to the parent MediaSourceSplitter
+        sp<MediaSourceSplitter> mSplitter;
+
+        // Id of this client.
+        int32_t mClientId;
+    };
+
+    friend class Client;
+};
+
+}  // namespace android
+
+#endif  // MEDIA_SOURCE_SPLITTER_H_
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index ac0624a..a72aabf 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -101,6 +101,9 @@
     kKeyValidSamples      = 'valD',  // int32_t
 
     kKeyIsUnreadable      = 'unre',  // bool (int32_t)
+
+    // An indication that a video buffer has been rendered.
+    kKeyRendered          = 'rend',  // bool (int32_t)
 };
 
 enum {
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index fed6761..517868c 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -18,6 +18,7 @@
 
 #define OMX_CODEC_H_
 
+#include <android/native_window.h>
 #include <media/IOMX.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaSource.h>
@@ -38,13 +39,22 @@
         // The client wants to access the output buffer's video
         // data for example for thumbnail extraction.
         kClientNeedsFramebuffer  = 4,
+
+        // Request for software or hardware codecs. If request
+        // can not be fullfilled, Create() returns NULL.
+        kSoftwareCodecsOnly      = 8,
+        kHardwareCodecsOnly      = 16,
+
+        // Store meta data in video buffers
+        kStoreMetaDataInVideoBuffers = 32,
     };
     static sp<MediaSource> Create(
             const sp<IOMX> &omx,
             const sp<MetaData> &meta, bool createEncoder,
             const sp<MediaSource> &source,
             const char *matchComponentName = NULL,
-            uint32_t flags = 0);
+            uint32_t flags = 0,
+            const sp<ANativeWindow> &nativeWindow = NULL);
 
     static void setComponentRole(
             const sp<IOMX> &omx, IOMX::node_id node, bool isEncoder,
@@ -109,12 +119,12 @@
         kAvoidMemcopyInputRecordingFrames     = 2048,
         kRequiresLargerEncoderOutputBuffer    = 4096,
         kOutputBuffersAreUnreadable           = 8192,
-        kStoreMetaDataInInputVideoBuffers     = 16384,
     };
 
     struct BufferInfo {
         IOMX::buffer_id mBuffer;
         bool mOwnedByComponent;
+        bool mOwnedByNativeWindow;
         sp<IMemory> mMem;
         size_t mSize;
         void *mData;
@@ -160,13 +170,23 @@
 
     bool mPaused;
 
+    sp<ANativeWindow> mNativeWindow;
+
+    // The index in each of the mPortBuffers arrays of the buffer that will be
+    // submitted to OMX next.  This only applies when using buffers from a
+    // native window.
+    size_t mNextNativeBufferIndex[2];
+
     // A list of indices into mPortStatus[kPortIndexOutput] filled with data.
     List<size_t> mFilledBuffers;
     Condition mBufferFilled;
 
+    bool mIsMetaDataStoredInVideoBuffers;
+
     OMXCodec(const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks,
              bool isEncoder, const char *mime, const char *componentName,
-             const sp<MediaSource> &source);
+             const sp<MediaSource> &source,
+             const sp<ANativeWindow> &nativeWindow);
 
     void addCodecSpecificData(const void *data, size_t size);
     void clearCodecSpecificData();
@@ -217,10 +237,17 @@
 
     status_t allocateBuffers();
     status_t allocateBuffersOnPort(OMX_U32 portIndex);
+    status_t allocateOutputBuffersFromNativeWindow();
+
+    status_t queueBufferToNativeWindow(BufferInfo *info);
+    status_t cancelBufferToNativeWindow(BufferInfo *info);
+    BufferInfo* dequeueBufferFromNativeWindow();
 
     status_t freeBuffersOnPort(
             OMX_U32 portIndex, bool onlyThoseWeOwn = false);
 
+    status_t freeBuffer(OMX_U32 portIndex, size_t bufIndex);
+
     void drainInputBuffer(IOMX::buffer_id buffer);
     void fillOutputBuffer(IOMX::buffer_id buffer);
     void drainInputBuffer(BufferInfo *info);
@@ -251,6 +278,7 @@
 
     status_t init();
     void initOutputFormat(const sp<MetaData> &inputFormat);
+    status_t initNativeWindow();
 
     void dumpPortStatus(OMX_U32 portIndex);
 
diff --git a/include/media/stagefright/VideoSourceDownSampler.h b/include/media/stagefright/VideoSourceDownSampler.h
new file mode 100644
index 0000000..439918c
--- /dev/null
+++ b/include/media/stagefright/VideoSourceDownSampler.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// VideoSourceDownSampler implements the MediaSource interface,
+// downsampling frames provided from a real video source.
+
+#ifndef VIDEO_SOURCE_DOWN_SAMPLER_H_
+
+#define VIDEO_SOURCE_DOWN_SAMPLER_H_
+
+#include <media/stagefright/MediaSource.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class IMemory;
+class MediaBuffer;
+class MetaData;
+
+class VideoSourceDownSampler : public MediaSource {
+public:
+    virtual ~VideoSourceDownSampler();
+
+    // Constructor:
+    // videoSource: The real video source which provides the original frames.
+    // width, height: The desired width, height. These should be less than or equal
+    // to those of the real video source. We then downsample the original frames to
+    // this size.
+    VideoSourceDownSampler(const sp<MediaSource> &videoSource,
+        int32_t width, int32_t height);
+
+    // MediaSource interface
+    virtual status_t start(MetaData *params = NULL);
+
+    virtual status_t stop();
+
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+    virtual status_t pause();
+
+private:
+    // Reference to the real video source.
+    sp<MediaSource> mRealVideoSource;
+
+    // Size of frames to be provided by this source.
+    int32_t mWidth;
+    int32_t mHeight;
+
+    // Size of frames provided by the real source.
+    int32_t mRealSourceWidth;
+    int32_t mRealSourceHeight;
+
+    // Down sampling paramters.
+    int32_t mDownSampleOffsetX;
+    int32_t mDownSampleOffsetY;
+    int32_t mDownSampleSkipX;
+    int32_t mDownSampleSkipY;
+
+    // True if we need to crop the still video image to get the video frame.
+    bool mNeedDownSampling;
+
+    // Meta data. This is a copy of the real source except for the width and
+    // height parameters.
+    sp<MetaData> mMeta;
+
+    // Computes the offset, skip parameters for downsampling the original frame
+    // to the desired size.
+    void computeDownSamplingParameters();
+
+    // Downsamples the frame in sourceBuffer to size (mWidth x mHeight). A new
+    // buffer is created which stores the downsampled image.
+    void downSampleYUVImage(const MediaBuffer &sourceBuffer, MediaBuffer **buffer) const;
+
+    // Disallow these.
+    VideoSourceDownSampler(const VideoSourceDownSampler &);
+    VideoSourceDownSampler &operator=(const VideoSourceDownSampler &);
+};
+
+}  // namespace android
+
+#endif  // VIDEO_SOURCE_DOWN_SAMPLER_H_
diff --git a/include/media/stagefright/YUVCanvas.h b/include/media/stagefright/YUVCanvas.h
new file mode 100644
index 0000000..ff70923
--- /dev/null
+++ b/include/media/stagefright/YUVCanvas.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// YUVCanvas holds a reference to a YUVImage on which it can do various
+// drawing operations. It provides various utility functions for filling,
+// cropping, etc.
+
+
+#ifndef YUV_CANVAS_H_
+
+#define YUV_CANVAS_H_
+
+#include <stdint.h>
+
+namespace android {
+
+class YUVImage;
+class Rect;
+
+class YUVCanvas {
+public:
+
+    // Constructor takes in reference to a yuvImage on which it can do
+    // various drawing opreations.
+    YUVCanvas(YUVImage &yuvImage);
+    ~YUVCanvas();
+
+    // Fills the entire image with the given YUV values.
+    void FillYUV(uint8_t yValue, uint8_t uValue, uint8_t vValue);
+
+    // Fills the rectangular region [startX,endX]x[startY,endY] with the given YUV values.
+    void FillYUVRectangle(const Rect& rect,
+            uint8_t yValue, uint8_t uValue, uint8_t vValue);
+
+    // Copies the region [startX,endX]x[startY,endY] from srcImage into the
+    // canvas' target image (mYUVImage) starting at
+    // (destinationStartX,destinationStartY).
+    // Note that undefined behavior may occur if srcImage is same as the canvas'
+    // target image.
+    void CopyImageRect(
+            const Rect& srcRect,
+            int32_t destStartX, int32_t destStartY,
+            const YUVImage &srcImage);
+
+    // Downsamples the srcImage into the canvas' target image (mYUVImage)
+    // The downsampling copies pixels from the source image starting at
+    // (srcOffsetX, srcOffsetY) to the target image, starting at (0, 0).
+    // For each X increment in the target image, skipX pixels are skipped
+    // in the source image.
+    // Similarly for each Y increment in the target image, skipY pixels
+    // are skipped in the source image.
+    void downsample(
+            int32_t srcOffsetX, int32_t srcOffsetY,
+            int32_t skipX, int32_t skipY,
+            const YUVImage &srcImage);
+
+private:
+    YUVImage& mYUVImage;
+
+    YUVCanvas(const YUVCanvas &);
+    YUVCanvas &operator=(const YUVCanvas &);
+};
+
+}  // namespace android
+
+#endif  // YUV_CANVAS_H_
diff --git a/include/media/stagefright/YUVImage.h b/include/media/stagefright/YUVImage.h
new file mode 100644
index 0000000..4e98618
--- /dev/null
+++ b/include/media/stagefright/YUVImage.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// A container class to hold YUV data and provide various utilities,
+// e.g. to set/get pixel values.
+// Supported formats:
+//  - YUV420 Planar
+//  - YUV420 Semi Planar
+//
+//  Currently does not support variable strides.
+//
+//  Implementation: Two simple abstractions are done to simplify access
+//  to YUV channels for different formats:
+//  - initializeYUVPointers() sets up pointers (mYdata, mUdata, mVdata) to
+//  point to the right start locations of the different channel data depending
+//  on the format.
+//  - getOffsets() returns the correct offset for the different channels
+//  depending on the format.
+//  Location of any pixel's YUV channels can then be easily computed using these.
+//
+
+#ifndef YUV_IMAGE_H_
+
+#define YUV_IMAGE_H_
+
+#include <stdint.h>
+#include <cstring>
+
+namespace android {
+
+class Rect;
+
+class YUVImage {
+public:
+    // Supported YUV formats
+    enum YUVFormat {
+        YUV420Planar,
+        YUV420SemiPlanar
+    };
+
+    // Constructs an image with the given size, format. Also allocates and owns
+    // the required memory.
+    YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height);
+
+    // Constructs an image with the given size, format. The memory is provided
+    // by the caller and we don't own it.
+    YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height, uint8_t *buffer);
+
+    // Destructor to delete the memory if it owns it.
+    ~YUVImage();
+
+    // Returns the size of the buffer required to store the YUV data for the given
+    // format and geometry. Useful when the caller wants to allocate the requisite
+    // memory.
+    static size_t bufferSize(YUVFormat yuvFormat, int32_t width, int32_t height);
+
+    int32_t width() const {return mWidth;}
+    int32_t height() const {return mHeight;}
+
+    // Returns true if pixel is the range [0, width-1] x [0, height-1]
+    // and false otherwise.
+    bool validPixel(int32_t x, int32_t y) const;
+
+    // Get the pixel YUV value at pixel (x,y).
+    // Note that the range of x is [0, width-1] and the range of y is [0, height-1].
+    // Returns true if get was successful and false otherwise.
+    bool getPixelValue(int32_t x, int32_t y,
+            uint8_t *yPtr, uint8_t *uPtr, uint8_t *vPtr) const;
+
+    // Set the pixel YUV value at pixel (x,y).
+    // Note that the range of x is [0, width-1] and the range of y is [0, height-1].
+    // Returns true if set was successful and false otherwise.
+    bool setPixelValue(int32_t x, int32_t y,
+            uint8_t yValue, uint8_t uValue, uint8_t vValue);
+
+    // Uses memcpy to copy an entire row of data
+    static void fastCopyRectangle420Planar(
+            const Rect& srcRect,
+            int32_t destStartX, int32_t destStartY,
+            const YUVImage &srcImage, YUVImage &destImage);
+
+    // Uses memcpy to copy an entire row of data
+    static void fastCopyRectangle420SemiPlanar(
+            const Rect& srcRect,
+            int32_t destStartX, int32_t destStartY,
+            const YUVImage &srcImage, YUVImage &destImage);
+
+    // Tries to use memcopy to copy entire rows of data.
+    // Returns false if fast copy is not possible for the passed image formats.
+    static bool fastCopyRectangle(
+            const Rect& srcRect,
+            int32_t destStartX, int32_t destStartY,
+            const YUVImage &srcImage, YUVImage &destImage);
+
+    // Convert the given YUV value to RGB.
+    void yuv2rgb(uint8_t yValue, uint8_t uValue, uint8_t vValue,
+        uint8_t *r, uint8_t *g, uint8_t *b) const;
+
+    // Write the image to a human readable PPM file.
+    // Returns true if write was succesful and false otherwise.
+    bool writeToPPM(const char *filename) const;
+
+private:
+    // YUV Format of the image.
+    YUVFormat mYUVFormat;
+
+    int32_t mWidth;
+    int32_t mHeight;
+
+    // Pointer to the memory buffer.
+    uint8_t *mBuffer;
+
+    // Boolean telling whether we own the memory buffer.
+    bool mOwnBuffer;
+
+    // Pointer to start of the Y data plane.
+    uint8_t *mYdata;
+
+    // Pointer to start of the U data plane. Note that in case of interleaved formats like
+    // YUV420 semiplanar, mUdata points to the start of the U data in the UV plane.
+    uint8_t *mUdata;
+
+    // Pointer to start of the V data plane. Note that in case of interleaved formats like
+    // YUV420 semiplanar, mVdata points to the start of the V data in the UV plane.
+    uint8_t *mVdata;
+
+    // Initialize the pointers mYdata, mUdata, mVdata to point to the right locations for
+    // the given format and geometry.
+    // Returns true if initialize was succesful and false otherwise.
+    bool initializeYUVPointers();
+
+    // For the given pixel location, this returns the offset of the location of y, u and v
+    // data from the corresponding base pointers -- mYdata, mUdata, mVdata.
+    // Note that the range of x is [0, width-1] and the range of y is [0, height-1].
+    // Returns true if getting offsets was succesful and false otherwise.
+    bool getOffsets(int32_t x, int32_t y,
+        int32_t *yOffset, int32_t *uOffset, int32_t *vOffset) const;
+
+    // Returns the offset increments incurred in going from one data row to the next data row
+    // for the YUV channels. Note that this corresponds to data rows and not pixel rows.
+    // E.g. depending on formats, U/V channels may have only one data row corresponding
+    // to two pixel rows.
+    bool getOffsetIncrementsPerDataRow(
+        int32_t *yDataOffsetIncrement,
+        int32_t *uDataOffsetIncrement,
+        int32_t *vDataOffsetIncrement) const;
+
+    // Given the offset return the address of the corresponding channel's data.
+    uint8_t* getYAddress(int32_t offset) const;
+    uint8_t* getUAddress(int32_t offset) const;
+    uint8_t* getVAddress(int32_t offset) const;
+
+    // Given the pixel location, returns the address of the corresponding channel's data.
+    // Note that the range of x is [0, width-1] and the range of y is [0, height-1].
+    bool getYUVAddresses(int32_t x, int32_t y,
+        uint8_t **yAddr, uint8_t **uAddr, uint8_t **vAddr) const;
+
+    // Disallow implicit casting and copying.
+    YUVImage(const YUVImage &);
+    YUVImage &operator=(const YUVImage &);
+};
+
+}  // namespace android
+
+#endif  // YUV_IMAGE_H_
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index da4d56f..db57859 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -43,6 +43,7 @@
         eSecure             = 0x00000080,
         eNonPremultiplied   = 0x00000100,
         ePushBuffers        = 0x00000200,
+        eOpaque             = 0x00000400,
 
         eFXSurfaceNormal    = 0x00000000,
         eFXSurfaceBlur      = 0x00010000,
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 22684db..cef439c 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -94,7 +94,7 @@
     friend class SurfaceComposerClient;
 
     // camera and camcorder need access to the ISurface binder interface for preview
-    friend class Camera;
+    friend class CameraService;
     friend class MediaRecorder;
     // mediaplayer needs access to ISurface for display
     friend class MediaPlayer;
@@ -173,11 +173,12 @@
      * (eventually this should go away and be replaced by proper APIs)
      */
     // camera and camcorder need access to the ISurface binder interface for preview
-    friend class Camera;
+    friend class CameraService;
     friend class MediaRecorder;
     // MediaPlayer needs access to ISurface for display
     friend class MediaPlayer;
     friend class IOMX;
+    friend class SoftwareRenderer;
     // this is just to be able to write some unit tests
     friend class Test;
 
@@ -314,4 +315,3 @@
 }; // namespace android
 
 #endif // ANDROID_SF_SURFACE_H
-
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index d78e35f..1431964 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -187,6 +187,9 @@
     virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
             uint8_t* outFlags) const = 0;
 
+    virtual bool hasLed(int32_t deviceId, int32_t led) const = 0;
+    virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0;
+
     virtual void dump(String8& dump) = 0;
 };
 
@@ -198,9 +201,9 @@
     status_t errorCheck() const;
 
     virtual uint32_t getDeviceClasses(int32_t deviceId) const;
-    
+
     virtual String8 getDeviceName(int32_t deviceId) const;
-    
+
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
             RawAbsoluteAxisInfo* outAxisInfo) const;
 
@@ -218,6 +221,9 @@
 
     virtual bool getEvent(RawEvent* outEvent);
 
+    virtual bool hasLed(int32_t deviceId, int32_t led) const;
+    virtual void setLedState(int32_t deviceId, int32_t led, bool on);
+
     virtual void dump(String8& dump);
 
 protected:
@@ -240,7 +246,10 @@
         uint32_t        classes;
         uint8_t*        keyBitmask;
         KeyLayoutMap*   layoutMap;
-        String8         keylayoutFilename;
+        String8         keyMapName;
+        bool            defaultKeyMap;
+        String8         keyLayoutFilename;
+        String8         keyCharacterMapFilename;
         int             fd;
         device_t*       next;
         
@@ -250,13 +259,19 @@
 
     device_t* getDeviceLocked(int32_t deviceId) const;
     bool hasKeycodeLocked(device_t* device, int keycode) const;
-    
+
     int32_t getScanCodeStateLocked(device_t* device, int32_t scanCode) const;
     int32_t getKeyCodeStateLocked(device_t* device, int32_t keyCode) const;
     int32_t getSwitchStateLocked(device_t* device, int32_t sw) const;
     bool markSupportedKeyCodesLocked(device_t* device, size_t numCodes,
             const int32_t* keyCodes, uint8_t* outFlags) const;
 
+    void configureKeyMap(device_t* device);
+    bool probeKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap);
+    void selectKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap);
+    void setKeyboardProperties(device_t* device, bool firstKeyboard);
+    void clearKeyboardProperties(device_t* device, bool firstKeyboard);
+
     // Protect all internal state.
     mutable Mutex   mLock;
     
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index a3e85a9..c446633 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -72,6 +72,9 @@
     GraphicBuffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage,
             uint32_t stride, native_handle_t* handle, bool keepOwnership);
 
+    // create a buffer from an existing android_native_buffer_t
+    GraphicBuffer(android_native_buffer_t* buffer, bool keepOwnership);
+
     // return status
     status_t initCheck() const;
 
@@ -137,6 +140,10 @@
     GraphicBufferMapper& mBufferMapper;
     ssize_t mInitCheck;
     int mIndex;
+
+    // If we're wrapping another buffer then this reference will make sure it
+    // doesn't get freed.
+    sp<android_native_buffer_t> mWrappedBuffer;
 };
 
 }; // namespace android
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 8c6018b..1355bab 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -76,7 +76,7 @@
  */
 enum {
     /* These flags originate in RawEvents and are generally set in the key map.
-     * See also labels for policy flags in KeycodeLabels.h. */
+     * NOTE: If you edit these flags, also edit labels in KeycodeLabels.h. */
 
     POLICY_FLAG_WAKE = 0x00000001,
     POLICY_FLAG_WAKE_DROPPED = 0x00000002,
@@ -87,6 +87,7 @@
     POLICY_FLAG_MENU = 0x00000040,
     POLICY_FLAG_LAUNCHER = 0x00000080,
     POLICY_FLAG_VIRTUAL = 0x00000100,
+    POLICY_FLAG_FUNCTION = 0x00000200,
 
     POLICY_FLAG_RAW_MASK = 0x0000ffff,
 
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 5f77cba..a5591ba 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -387,6 +387,14 @@
      */
     virtual void setInputDispatchMode(bool enabled, bool frozen) = 0;
 
+    /* Transfers touch focus from the window associated with one channel to the
+     * window associated with the other channel.
+     *
+     * Returns true on success.  False if the window did not actually have touch focus.
+     */
+    virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
+            const sp<InputChannel>& toChannel) = 0;
+
     /* Registers or unregister input channels that may be used as targets for input events.
      * If monitor is true, the channel will receive a copy of all input events.
      *
@@ -443,6 +451,9 @@
     virtual void setFocusedApplication(const InputApplication* inputApplication);
     virtual void setInputDispatchMode(bool enabled, bool frozen);
 
+    virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
+            const sp<InputChannel>& toChannel);
+
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor);
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
 
@@ -744,6 +755,9 @@
         // Clears the current state.
         void clear();
 
+        // Copies pointer-related parts of the input state to another instance.
+        void copyPointerStateTo(InputState& other) const;
+
     private:
         struct KeyMemento {
             int32_t deviceId;
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 49351b0..f3a2dd2 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -422,9 +422,18 @@
         Vector<KeyDown> keyDowns; // keys that are down
         int32_t metaState;
         nsecs_t downTime; // time of most recent key down
+
+        struct LedState {
+            bool avail; // led is available
+            bool on;    // we think the led is currently on
+        };
+        LedState capsLockLedState;
+        LedState numLockLedState;
+        LedState scrollLockLedState;
     } mLocked;
 
     void initializeLocked();
+    void initializeLedStateLocked(LockedState::LedState& ledState, int32_t led);
 
     bool isKeyboardOrGamepadKey(int32_t scanCode);
 
@@ -432,6 +441,10 @@
             uint32_t policyFlags);
 
     ssize_t findKeyDownLocked(int32_t scanCode);
+
+    void updateLedStateLocked(bool reset);
+    void updateLedStateForModifierLocked(LockedState::LedState& ledState, int32_t led,
+            int32_t modifier, bool reset);
 };
 
 
@@ -589,6 +602,16 @@
 
     // Immutable calibration parameters in parsed form.
     struct Calibration {
+        // Position
+        bool haveXOrigin;
+        int32_t xOrigin;
+        bool haveYOrigin;
+        int32_t yOrigin;
+        bool haveXScale;
+        float xScale;
+        bool haveYScale;
+        float yScale;
+
         // Touch Size
         enum TouchSizeCalibration {
             TOUCH_SIZE_CALIBRATION_DEFAULT,
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index f71d9cd..73f5863 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -135,6 +135,60 @@
     { "BUTTON_START", 108 },
     { "BUTTON_SELECT", 109 },
     { "BUTTON_MODE", 110 },
+    { "ESCAPE", 111 },
+    { "FORWARD_DEL", 112 },
+    { "CTRL_LEFT", 113 },
+    { "CTRL_RIGHT", 114 },
+    { "CAPS_LOCK", 115 },
+    { "SCROLL_LOCK", 116 },
+    { "META_LEFT", 117 },
+    { "META_RIGHT", 118 },
+    { "FUNCTION", 119 },
+    { "SYSRQ", 120 },
+    { "BREAK", 121 },
+    { "MOVE_HOME", 122 },
+    { "MOVE_END", 123 },
+    { "INSERT", 124 },
+    { "FORWARD", 125 },
+    { "MEDIA_PLAY", 126 },
+    { "MEDIA_PAUSE", 127 },
+    { "MEDIA_CLOSE", 128 },
+    { "MEDIA_EJECT", 129 },
+    { "MEDIA_RECORD", 130 },
+    { "F1", 131 },
+    { "F2", 132 },
+    { "F3", 133 },
+    { "F4", 134 },
+    { "F5", 135 },
+    { "F6", 136 },
+    { "F7", 137 },
+    { "F8", 138 },
+    { "F9", 139 },
+    { "F10", 140 },
+    { "F11", 141 },
+    { "F12", 142 },
+    { "NUM_LOCK", 143 },
+    { "NUMPAD_0", 144 },
+    { "NUMPAD_1", 145 },
+    { "NUMPAD_2", 146 },
+    { "NUMPAD_3", 147 },
+    { "NUMPAD_4", 148 },
+    { "NUMPAD_5", 149 },
+    { "NUMPAD_6", 150 },
+    { "NUMPAD_7", 151 },
+    { "NUMPAD_8", 152 },
+    { "NUMPAD_9", 153 },
+    { "NUMPAD_DIVIDE", 154 },
+    { "NUMPAD_MULTIPLY", 155 },
+    { "NUMPAD_SUBTRACT", 156 },
+    { "NUMPAD_ADD", 157 },
+    { "NUMPAD_DOT", 158 },
+    { "NUMPAD_COMMA", 159 },
+    { "NUMPAD_ENTER", 160 },
+    { "NUMPAD_EQUALS", 161 },
+    { "NUMPAD_LEFT_PAREN", 162 },
+    { "NUMPAD_RIGHT_PAREN", 163 },
+    { "VOLUME_MUTE", 164 },
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
@@ -142,7 +196,7 @@
     { NULL, 0 }
 };
 
-// See also policy flags in Input.h.
+// NOTE: If you edit these flags, also edit policy flags in Input.h.
 static const KeycodeLabel FLAGS[] = {
     { "WAKE", 0x00000001 },
     { "WAKE_DROPPED", 0x00000002 },
@@ -153,6 +207,7 @@
     { "MENU", 0x00000040 },
     { "LAUNCHER", 0x00000080 },
     { "VIRTUAL", 0x00000100 },
+    { "FUNCTION", 0x00000200 },
     { NULL, 0 }
 };
 
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index da86da4..ed7f53d1 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -1771,12 +1771,14 @@
      *
      * @return ssize_t Either a >= 0 table index or a negative error code.
      */
-    ssize_t getResource(uint32_t resID, Res_value* outValue, bool mayBeBag=false,
-            uint32_t* outSpecFlags=NULL, ResTable_config* outConfig=NULL) const;
+    ssize_t getResource(uint32_t resID, Res_value* outValue, bool mayBeBag = false,
+                    uint16_t density = 0,
+                    uint32_t* outSpecFlags = NULL,
+                    ResTable_config* outConfig = NULL) const;
 
     inline ssize_t getResource(const ResTable_ref& res, Res_value* outValue,
             uint32_t* outSpecFlags=NULL) const {
-        return getResource(res.ident, outValue, false, outSpecFlags, NULL);
+        return getResource(res.ident, outValue, false, 0, outSpecFlags, NULL);
     }
 
     ssize_t resolveReference(Res_value* inOutValue,
diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h
index 3b975b4..e1ee8eb 100644
--- a/include/utils/Singleton.h
+++ b/include/utils/Singleton.h
@@ -37,6 +37,11 @@
         }
         return *instance;
     }
+
+    static bool hasInstance() {
+        Mutex::Autolock _l(sLock);
+        return sInstance != 0;
+    }
     
 protected:
     ~Singleton() { };
diff --git a/include/utils/String8.h b/include/utils/String8.h
index ef0b51a..cef8eca 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -157,9 +157,12 @@
     inline  size_t              size() const;
     inline  size_t              length() const;
     inline  size_t              bytes() const;
+    inline  bool                isEmpty() const;
     
     inline  const SharedBuffer* sharedBuffer() const;
     
+            void                clear();
+
             void                setTo(const String8& other);
             status_t            setTo(const char* other);
             status_t            setTo(const char* other, size_t numChars);
@@ -345,6 +348,11 @@
     return length();
 }
 
+inline bool String8::isEmpty() const
+{
+    return length() == 0;
+}
+
 inline size_t String8::bytes() const
 {
     return SharedBuffer::sizeFromData(mString)-1;
diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk
index 13dc500..f9d9f25 100644
--- a/libs/binder/Android.mk
+++ b/libs/binder/Android.mk
@@ -16,6 +16,7 @@
 sources := \
     Binder.cpp \
     BpBinder.cpp \
+    CursorWindow.cpp \
     IInterface.cpp \
     IMemory.cpp \
     IPCThreadState.cpp \
diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp
new file mode 100644
index 0000000..fbba281
--- /dev/null
+++ b/libs/binder/CursorWindow.cpp
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2006-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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "CursorWindow"
+
+#include <utils/Log.h>
+#include <binder/CursorWindow.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+namespace android {
+
+CursorWindow::CursorWindow(size_t maxSize) :
+    mMaxSize(maxSize)
+{
+}
+
+bool CursorWindow::setMemory(const sp<IMemory>& memory)
+{
+    mMemory = memory;
+    mData = (uint8_t *) memory->pointer();
+    if (mData == NULL) {
+        return false;
+    }
+    mHeader = (window_header_t *) mData;
+
+    // Make the window read-only
+    ssize_t size = memory->size();
+    mSize = size;
+    mMaxSize = size;
+    mFreeOffset = size;
+LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData);
+    return true;
+}
+
+bool CursorWindow::initBuffer(bool localOnly)
+{
+    //TODO Use a non-memory dealer mmap region for localOnly
+
+    sp<MemoryHeapBase> heap;
+    heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow");
+    if (heap != NULL) {
+        mMemory = new MemoryBase(heap, 0, mMaxSize);
+        if (mMemory != NULL) {
+            mData = (uint8_t *) mMemory->pointer();
+            if (mData) {
+                mHeader = (window_header_t *) mData;
+                mSize = mMaxSize;
+
+                // Put the window into a clean state
+                clear();
+            LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData);
+                return true;                
+            }
+        } 
+        LOGE("CursorWindow heap allocation failed");
+        return false;
+    } else {
+        LOGE("failed to create the CursorWindow heap");
+        return false;
+    }
+}
+
+CursorWindow::~CursorWindow()
+{
+    // Everything that matters is a smart pointer
+}
+
+void CursorWindow::clear()
+{
+    mHeader->numRows = 0;
+    mHeader->numColumns = 0;
+    mFreeOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE;
+    // Mark the first chunk's next 'pointer' as null
+    *((uint32_t *)(mData + mFreeOffset - sizeof(uint32_t))) = 0;
+}
+
+int32_t CursorWindow::freeSpace()
+{
+    int32_t freeSpace = mSize - mFreeOffset;
+    if (freeSpace < 0) {
+        freeSpace = 0;
+    }
+    return freeSpace;
+}
+
+field_slot_t * CursorWindow::allocRow()
+{
+    // Fill in the row slot
+    row_slot_t * rowSlot = allocRowSlot();
+    if (rowSlot == NULL) {
+        return NULL;
+    }
+
+    // Allocate the slots for the field directory
+    size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t);
+    uint32_t fieldDirOffset = alloc(fieldDirSize);
+    if (!fieldDirOffset) {
+        mHeader->numRows--;
+        LOG_WINDOW("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows);
+        return NULL;
+    }
+    field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset);
+    memset(fieldDir, 0x0, fieldDirSize);
+
+LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", (mHeader->numRows - 1), ((uint8_t *)rowSlot) - mData, fieldDirSize, fieldDirOffset);
+    rowSlot->offset = fieldDirOffset;
+
+    return fieldDir;
+}
+
+uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned)
+{
+    int32_t size;
+    uint32_t padding;
+    if (aligned) {
+        // 4 byte alignment
+        padding = 4 - (mFreeOffset & 0x3);
+    } else {
+        padding = 0;
+    }
+
+    size = requestedSize + padding;
+
+    if (size > freeSpace()) {
+        LOGV("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d", mSize, size,
+                freeSpace(), mHeader->numRows);
+        // Only grow the window if the first row doesn't fit
+        if (mHeader->numRows > 1) {
+            LOGV("not growing since there are already %d row(s), max size %d", mHeader->numRows,
+                    mMaxSize);
+            return 0;
+        }
+
+        // Find a new size that will fit the allocation
+        int allocated = mSize - freeSpace();
+        int newSize = mSize + WINDOW_ALLOCATION_SIZE;
+        while (size > (newSize - allocated)) {
+            newSize += WINDOW_ALLOCATION_SIZE;
+            if (newSize > mMaxSize) {
+                LOGE("Attempting to grow window beyond max size (%d)", mMaxSize);
+                return 0;
+            }
+        }
+LOG_WINDOW("found size %d", newSize);
+        mSize = newSize;
+    }
+
+    uint32_t offset = mFreeOffset + padding;
+    mFreeOffset += size;
+    return offset;
+}
+
+row_slot_t * CursorWindow::getRowSlot(int row)
+{
+    LOG_WINDOW("enter getRowSlot current row num %d, this row %d", mHeader->numRows, row);
+    int chunkNum = row / ROW_SLOT_CHUNK_NUM_ROWS;
+    int chunkPos = row % ROW_SLOT_CHUNK_NUM_ROWS;
+    int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
+    uint8_t * rowChunk = mData + sizeof(window_header_t);
+    for (int i = 0; i < chunkNum; i++) {
+        rowChunk = offsetToPtr(*((uint32_t *)(mData + chunkPtrOffset)));
+        chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
+    }
+    return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
+    LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row);    
+}
+
+row_slot_t * CursorWindow::allocRowSlot()
+{
+    int chunkNum = mHeader->numRows / ROW_SLOT_CHUNK_NUM_ROWS;
+    int chunkPos = mHeader->numRows % ROW_SLOT_CHUNK_NUM_ROWS;
+    int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
+    uint8_t * rowChunk = mData + sizeof(window_header_t);
+LOG_WINDOW("Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d", mHeader->numRows, chunkNum, chunkPos);
+    for (int i = 0; i < chunkNum; i++) {
+        uint32_t nextChunkOffset = *((uint32_t *)(mData + chunkPtrOffset));
+LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset);
+        if (nextChunkOffset == 0) {
+            // Allocate a new row chunk
+            nextChunkOffset = alloc(ROW_SLOT_CHUNK_SIZE, true);
+            if (nextChunkOffset == 0) {
+                return NULL;
+            }
+            rowChunk = offsetToPtr(nextChunkOffset);
+LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk);
+            *((uint32_t *)(mData + chunkPtrOffset)) = rowChunk - mData;
+            // Mark the new chunk's next 'pointer' as null
+            *((uint32_t *)(rowChunk + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t))) = 0;
+        } else {
+LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset);
+            rowChunk = offsetToPtr(nextChunkOffset);
+            chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
+        }
+    }
+    mHeader->numRows++;
+
+    return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
+}
+
+field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column)
+{
+  if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
+      LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
+      return NULL;
+  }        
+  row_slot_t * rowSlot = getRowSlot(row);
+  if (!rowSlot) {
+      LOGE("Failed to find rowSlot for row %d", row);
+      return NULL;
+  }
+  if (rowSlot->offset == 0 || rowSlot->offset >= mSize) {
+      LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
+      return NULL;
+  }  
+  int fieldDirOffset = rowSlot->offset;
+  return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;  
+}
+
+uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotOut)
+{
+    if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
+        LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
+        return -1;
+    }        
+    row_slot_t * rowSlot = getRowSlot(row);
+    if (!rowSlot) {
+        LOGE("Failed to find rowSlot for row %d", row);
+        return -1;
+    }
+    if (rowSlot->offset == 0 || rowSlot->offset >= mSize) {
+        LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
+        return -1;
+    }
+LOG_WINDOW("Found field directory for %d,%d at rowSlot %d, offset %d", row, column, (uint8_t *)rowSlot - mData, rowSlot->offset);
+    field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(rowSlot->offset);
+LOG_WINDOW("Read field_slot_t %d,%d: offset = %d, size = %d, type = %d", row, column, fieldDir[column].data.buffer.offset, fieldDir[column].data.buffer.size, fieldDir[column].type);
+
+    // Copy the data to the out param
+    slotOut->data.buffer.offset = fieldDir[column].data.buffer.offset;
+    slotOut->data.buffer.size = fieldDir[column].data.buffer.size;
+    slotOut->type = fieldDir[column].type;
+    return 0;
+}
+
+void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size)
+{
+    assert(offset + size <= mSize);    
+    memcpy(mData + offset, data, size);
+}
+
+void CursorWindow::copyIn(uint32_t offset, int64_t data)
+{
+    assert(offset + sizeof(int64_t) <= mSize);
+    memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t));
+}
+
+void CursorWindow::copyIn(uint32_t offset, double data)
+{
+    assert(offset + sizeof(double) <= mSize);
+    memcpy(mData + offset, (uint8_t *)&data, sizeof(double));
+}
+
+void CursorWindow::copyOut(uint32_t offset, uint8_t * data, size_t size)
+{
+    assert(offset + size <= mSize);
+    memcpy(data, mData + offset, size);
+}
+
+int64_t CursorWindow::copyOutLong(uint32_t offset)
+{
+    int64_t value;
+    assert(offset + sizeof(int64_t) <= mSize);
+    memcpy(&value, mData + offset, sizeof(int64_t));
+    return value;
+}
+
+double CursorWindow::copyOutDouble(uint32_t offset)
+{
+    double value;
+    assert(offset + sizeof(double) <= mSize);
+    memcpy(&value, mData + offset, sizeof(double));
+    return value;
+}
+
+bool CursorWindow::putLong(unsigned int row, unsigned int col, int64_t value)
+{
+    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+    if (!fieldSlot) {
+        return false;
+    }
+
+#if WINDOW_STORAGE_INLINE_NUMERICS
+    fieldSlot->data.l = value;
+#else
+    int offset = alloc(sizeof(int64_t));
+    if (!offset) {
+        return false;
+    }
+
+    copyIn(offset, value);
+
+    fieldSlot->data.buffer.offset = offset;
+    fieldSlot->data.buffer.size = sizeof(int64_t);
+#endif
+    fieldSlot->type = FIELD_TYPE_INTEGER;
+    return true;
+}
+
+bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value)
+{
+    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+    if (!fieldSlot) {
+        return false;
+    }
+
+#if WINDOW_STORAGE_INLINE_NUMERICS
+    fieldSlot->data.d = value;
+#else
+    int offset = alloc(sizeof(int64_t));
+    if (!offset) {
+        return false;
+    }
+
+    copyIn(offset, value);
+
+    fieldSlot->data.buffer.offset = offset;
+    fieldSlot->data.buffer.size = sizeof(double);
+#endif
+    fieldSlot->type = FIELD_TYPE_FLOAT;
+    return true;
+}
+
+bool CursorWindow::putNull(unsigned int row, unsigned int col)
+{
+    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+    if (!fieldSlot) {
+        return false;
+    }
+
+    fieldSlot->type = FIELD_TYPE_NULL;
+    fieldSlot->data.buffer.offset = 0;
+    fieldSlot->data.buffer.size = 0;
+    return true;
+}
+
+bool CursorWindow::getLong(unsigned int row, unsigned int col, int64_t * valueOut)
+{
+    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+    if (!fieldSlot || fieldSlot->type != FIELD_TYPE_INTEGER) {
+        return false;
+    }
+    
+#if WINDOW_STORAGE_INLINE_NUMERICS
+    *valueOut = fieldSlot->data.l;
+#else
+    *valueOut = copyOutLong(fieldSlot->data.buffer.offset);
+#endif
+    return true;
+}
+
+bool CursorWindow::getDouble(unsigned int row, unsigned int col, double * valueOut)
+{
+    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+    if (!fieldSlot || fieldSlot->type != FIELD_TYPE_FLOAT) {
+        return false;
+    }
+
+#if WINDOW_STORAGE_INLINE_NUMERICS
+    *valueOut = fieldSlot->data.d;
+#else
+    *valueOut = copyOutDouble(fieldSlot->data.buffer.offset);
+#endif
+    return true;
+}
+
+bool CursorWindow::getNull(unsigned int row, unsigned int col, bool * valueOut)
+{
+    field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+    if (!fieldSlot) {
+        return false;
+    }
+    
+    if (fieldSlot->type != FIELD_TYPE_NULL) {
+        *valueOut = false;
+    } else {
+        *valueOut = true;
+    }
+    return true;
+}
+
+}; // namespace android
diff --git a/libs/camera/Camera.cpp b/libs/camera/Camera.cpp
index 7efc6d7..148e864 100644
--- a/libs/camera/Camera.cpp
+++ b/libs/camera/Camera.cpp
@@ -167,32 +167,20 @@
     return c->unlock();
 }
 
-// pass the buffered ISurface to the camera service
+// pass the buffered Surface to the camera service
 status_t Camera::setPreviewDisplay(const sp<Surface>& surface)
 {
-    LOGV("setPreviewDisplay");
+    LOGV("setPreviewDisplay(%p)", surface.get());
     sp <ICamera> c = mCamera;
     if (c == 0) return NO_INIT;
     if (surface != 0) {
-        return c->setPreviewDisplay(surface->getISurface());
+        return c->setPreviewDisplay(surface);
     } else {
         LOGD("app passed NULL surface");
         return c->setPreviewDisplay(0);
     }
 }
 
-status_t Camera::setPreviewDisplay(const sp<ISurface>& surface)
-{
-    LOGV("setPreviewDisplay");
-    if (surface == 0) {
-        LOGD("app passed NULL surface");
-    }
-    sp <ICamera> c = mCamera;
-    if (c == 0) return NO_INIT;
-    return c->setPreviewDisplay(surface);
-}
-
-
 // start preview mode
 status_t Camera::startPreview()
 {
@@ -202,6 +190,31 @@
     return c->startPreview();
 }
 
+int32_t Camera::getNumberOfVideoBuffers() const
+{
+    LOGV("getNumberOfVideoBuffers");
+    sp <ICamera> c = mCamera;
+    if (c == 0) return 0;
+    return c->getNumberOfVideoBuffers();
+}
+
+sp<IMemory> Camera::getVideoBuffer(int32_t index) const
+{
+    LOGV("getVideoBuffer: %d", index);
+    sp <ICamera> c = mCamera;
+    if (c == 0) return 0;
+    return c->getVideoBuffer(index);
+}
+
+status_t Camera::storeMetaDataInBuffers(bool enabled)
+{
+    LOGV("storeMetaDataInBuffers: %s",
+            enabled? "true": "false");
+    sp <ICamera> c = mCamera;
+    if (c == 0) return NO_INIT;
+    return c->storeMetaDataInBuffers(enabled);
+}
+
 // start recording mode, must call setPreviewDisplay first
 status_t Camera::startRecording()
 {
@@ -375,4 +388,3 @@
 }
 
 }; // namespace android
-
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index 83e5e57f..45b1b9a 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -73,6 +73,8 @@
 const char CameraParameters::KEY_SMOOTH_ZOOM_SUPPORTED[] = "smooth-zoom-supported";
 const char CameraParameters::KEY_FOCUS_DISTANCES[] = "focus-distances";
 const char CameraParameters::KEY_VIDEO_FRAME_FORMAT[] = "video-frame-format";
+const char CameraParameters::KEY_VIDEO_SIZE[] = "video-size";
+const char CameraParameters::KEY_SUPPORTED_VIDEO_SIZES[] = "video-size-values";
 
 const char CameraParameters::TRUE[] = "true";
 const char CameraParameters::FOCUS_DISTANCE_INFINITY[] = "Infinity";
@@ -129,7 +131,7 @@
 const char CameraParameters::SCENE_MODE_CANDLELIGHT[] = "candlelight";
 const char CameraParameters::SCENE_MODE_BARCODE[] = "barcode";
 
-// Formats for setPreviewFormat and setPictureFormat.
+const char CameraParameters::PIXEL_FORMAT_YUV420P[]  = "yuv420p";
 const char CameraParameters::PIXEL_FORMAT_YUV422SP[] = "yuv422sp";
 const char CameraParameters::PIXEL_FORMAT_YUV420SP[] = "yuv420sp";
 const char CameraParameters::PIXEL_FORMAT_YUV422I[] = "yuv422i-yuyv";
@@ -337,6 +339,27 @@
     parseSizesList(previewSizesStr, sizes);
 }
 
+void CameraParameters::setVideoSize(int width, int height)
+{
+    char str[32];
+    sprintf(str, "%dx%d", width, height);
+    set(KEY_VIDEO_SIZE, str);
+}
+
+void CameraParameters::getVideoSize(int *width, int *height) const
+{
+    *width = *height = -1;
+    const char *p = get(KEY_VIDEO_SIZE);
+    if (p == 0) return;
+    parse_pair(p, width, height, 'x');
+}
+
+void CameraParameters::getSupportedVideoSizes(Vector<Size> &sizes) const
+{
+    const char *videoSizesStr = get(KEY_SUPPORTED_VIDEO_SIZES);
+    parseSizesList(videoSizesStr, sizes);
+}
+
 void CameraParameters::setPreviewFrameRate(int fps)
 {
     set(KEY_PREVIEW_FRAME_RATE, fps);
diff --git a/libs/camera/ICamera.cpp b/libs/camera/ICamera.cpp
index 13673b5..7ba8d12 100644
--- a/libs/camera/ICamera.cpp
+++ b/libs/camera/ICamera.cpp
@@ -45,6 +45,9 @@
     STOP_RECORDING,
     RECORDING_ENABLED,
     RELEASE_RECORDING_FRAME,
+    GET_NUM_VIDEO_BUFFERS,
+    GET_VIDEO_BUFFER,
+    STORE_META_DATA_IN_BUFFERS,
 };
 
 class BpCamera: public BpInterface<ICamera>
@@ -64,13 +67,13 @@
         remote()->transact(DISCONNECT, data, &reply);
     }
 
-    // pass the buffered ISurface to the camera service
-    status_t setPreviewDisplay(const sp<ISurface>& surface)
+    // pass the buffered Surface to the camera service
+    status_t setPreviewDisplay(const sp<Surface>& surface)
     {
         LOGV("setPreviewDisplay");
         Parcel data, reply;
         data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
-        data.writeStrongBinder(surface->asBinder());
+        Surface::writeToParcel(surface, &data);
         remote()->transact(SET_PREVIEW_DISPLAY, data, &reply);
         return reply.readInt32();
     }
@@ -133,6 +136,37 @@
         remote()->transact(RELEASE_RECORDING_FRAME, data, &reply);
     }
 
+    int32_t getNumberOfVideoBuffers() const
+    {
+        LOGV("getNumberOfVideoBuffers");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
+        remote()->transact(GET_NUM_VIDEO_BUFFERS, data, &reply);
+        return reply.readInt32();
+    }
+
+    sp<IMemory> getVideoBuffer(int32_t index) const
+    {
+        LOGV("getVideoBuffer: %d", index);
+        Parcel data, reply;
+        data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
+        data.writeInt32(index);
+        remote()->transact(GET_VIDEO_BUFFER, data, &reply);
+        sp<IMemory> mem = interface_cast<IMemory>(
+                            reply.readStrongBinder());
+        return mem;
+    }
+
+    status_t storeMetaDataInBuffers(bool enabled)
+    {
+        LOGV("storeMetaDataInBuffers: %s", enabled? "true": "false");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
+        data.writeInt32(enabled);
+        remote()->transact(STORE_META_DATA_IN_BUFFERS, data, &reply);
+        return reply.readInt32();
+    }
+
     // check preview state
     bool previewEnabled()
     {
@@ -258,7 +292,7 @@
         case SET_PREVIEW_DISPLAY: {
             LOGV("SET_PREVIEW_DISPLAY");
             CHECK_INTERFACE(ICamera, data, reply);
-            sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder());
+            sp<Surface> surface = Surface::readFromParcel(data);
             reply->writeInt32(setPreviewDisplay(surface));
             return NO_ERROR;
         } break;
@@ -300,6 +334,26 @@
             releaseRecordingFrame(mem);
             return NO_ERROR;
         } break;
+        case GET_NUM_VIDEO_BUFFERS: {
+            LOGV("GET_NUM_VIDEO_BUFFERS");
+            CHECK_INTERFACE(ICamera, data, reply);
+            reply->writeInt32(getNumberOfVideoBuffers());
+            return NO_ERROR;
+        } break;
+        case GET_VIDEO_BUFFER: {
+            LOGV("GET_VIDEO_BUFFER");
+            CHECK_INTERFACE(ICamera, data, reply);
+            int32_t index = data.readInt32();
+            reply->writeStrongBinder(getVideoBuffer(index)->asBinder());
+            return NO_ERROR;
+        } break;
+        case STORE_META_DATA_IN_BUFFERS: {
+            LOGV("STORE_META_DATA_IN_BUFFERS");
+            CHECK_INTERFACE(ICamera, data, reply);
+            bool enabled = data.readInt32();
+            reply->writeInt32(storeMetaDataInBuffers(enabled));
+            return NO_ERROR;
+        } break;
         case PREVIEW_ENABLED: {
             LOGV("PREVIEW_ENABLED");
             CHECK_INTERFACE(ICamera, data, reply);
@@ -376,4 +430,3 @@
 // ----------------------------------------------------------------------------
 
 }; // namespace android
-
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
new file mode 100644
index 0000000..29158e5
--- /dev/null
+++ b/libs/hwui/Android.mk
@@ -0,0 +1,49 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Only build libhwui when USE_OPENGL_RENDERER is
+# defined in the current device/board configuration
+ifeq ($(USE_OPENGL_RENDERER),true)
+	LOCAL_SRC_FILES:= \
+		utils/SortedListImpl.cpp \
+		FontRenderer.cpp \
+		GammaFontRenderer.cpp \
+		Caches.cpp \
+		DisplayListRenderer.cpp \
+		FboCache.cpp \
+		GradientCache.cpp \
+		LayerCache.cpp \
+		Matrix.cpp \
+		OpenGLDebugRenderer.cpp \
+		OpenGLRenderer.cpp \
+		Patch.cpp \
+		PatchCache.cpp \
+		PathCache.cpp \
+		Program.cpp \
+		ProgramCache.cpp \
+		ResourceCache.cpp \
+		SkiaColorFilter.cpp \
+		SkiaShader.cpp \
+		TextureCache.cpp \
+		TextDropShadowCache.cpp
+	
+	LOCAL_C_INCLUDES += \
+		$(JNI_H_INCLUDE) \
+		$(LOCAL_PATH)/../../include/utils \
+		external/skia/include/core \
+		external/skia/include/effects \
+		external/skia/include/images \
+		external/skia/src/ports \
+		external/skia/include/utils
+
+	LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER
+	LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+	LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui
+	LOCAL_MODULE := libhwui
+	LOCAL_MODULE_TAGS := optional
+	LOCAL_PRELINK_MODULE := false
+	
+	include $(BUILD_SHARED_LIBRARY)
+
+    include $(call all-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
new file mode 100644
index 0000000..93c5b34
--- /dev/null
+++ b/libs/hwui/Caches.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "Caches.h"
+
+namespace android {
+
+#ifdef USE_OPENGL_RENDERER
+using namespace uirenderer;
+ANDROID_SINGLETON_STATIC_INSTANCE(Caches);
+#endif
+
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+Caches::Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO),
+        lastDstMode(GL_ZERO), currentProgram(NULL) {
+    GLint maxTextureUnits;
+    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+    if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
+        LOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
+    }
+
+    glGenBuffers(1, &meshBuffer);
+    glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
+    glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
+
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+
+    mCurrentBuffer = meshBuffer;
+    mRegionMesh = NULL;
+}
+
+Caches::~Caches() {
+    delete[] mRegionMesh;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// VBO
+///////////////////////////////////////////////////////////////////////////////
+
+void Caches::bindMeshBuffer() {
+    bindMeshBuffer(meshBuffer);
+}
+
+void Caches::bindMeshBuffer(const GLuint buffer) {
+    if (mCurrentBuffer != buffer) {
+        glBindBuffer(GL_ARRAY_BUFFER, buffer);
+        mCurrentBuffer = buffer;
+    }
+}
+
+void Caches::unbindMeshBuffer() {
+    if (mCurrentBuffer) {
+        glBindBuffer(GL_ARRAY_BUFFER, 0);
+        mCurrentBuffer = 0;
+    }
+}
+
+TextureVertex* Caches::getRegionMesh() {
+    // Create the mesh, 2 triangles and 4 vertices per rectangle in the region
+    if (!mRegionMesh) {
+        mRegionMesh = new TextureVertex[REGION_MESH_QUAD_COUNT * 4];
+
+        uint16_t* regionIndices = new uint16_t[REGION_MESH_QUAD_COUNT * 6];
+        for (int i = 0; i < REGION_MESH_QUAD_COUNT; i++) {
+            uint16_t quad = i * 4;
+            int index = i * 6;
+            regionIndices[index    ] = quad;       // top-left
+            regionIndices[index + 1] = quad + 1;   // top-right
+            regionIndices[index + 2] = quad + 2;   // bottom-left
+            regionIndices[index + 3] = quad + 2;   // bottom-left
+            regionIndices[index + 4] = quad + 1;   // top-right
+            regionIndices[index + 5] = quad + 3;   // bottom-right
+        }
+
+        glGenBuffers(1, &mRegionMeshIndices);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mRegionMeshIndices);
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, REGION_MESH_QUAD_COUNT * 6 * sizeof(uint16_t),
+                regionIndices, GL_STATIC_DRAW);
+
+        delete[] regionIndices;
+    } else {
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mRegionMeshIndices);
+    }
+
+    return mRegionMesh;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
new file mode 100644
index 0000000..c019fd1
--- /dev/null
+++ b/libs/hwui/Caches.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_CACHES_H
+#define ANDROID_HWUI_CACHES_H
+
+#ifndef LOG_TAG
+    #define LOG_TAG "OpenGLRenderer"
+#endif
+
+#include <utils/Singleton.h>
+
+#include "Extensions.h"
+#include "FontRenderer.h"
+#include "GammaFontRenderer.h"
+#include "TextureCache.h"
+#include "LayerCache.h"
+#include "GradientCache.h"
+#include "PatchCache.h"
+#include "ProgramCache.h"
+#include "PathCache.h"
+#include "TextDropShadowCache.h"
+#include "FboCache.h"
+#include "Line.h"
+#include "ResourceCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Globals
+///////////////////////////////////////////////////////////////////////////////
+
+#define REQUIRED_TEXTURE_UNITS_COUNT 3
+
+#define REGION_MESH_QUAD_COUNT 512
+
+// Generates simple and textured vertices
+#define FV(x, y, u, v) { { x, y }, { u, v } }
+
+// This array is never used directly but used as a memcpy source in the
+// OpenGLRenderer constructor
+static const TextureVertex gMeshVertices[] = {
+        FV(0.0f, 0.0f, 0.0f, 0.0f),
+        FV(1.0f, 0.0f, 1.0f, 0.0f),
+        FV(0.0f, 1.0f, 0.0f, 1.0f),
+        FV(1.0f, 1.0f, 1.0f, 1.0f)
+};
+static const GLsizei gMeshStride = sizeof(TextureVertex);
+static const GLsizei gMeshTextureOffset = 2 * sizeof(float);
+static const GLsizei gMeshCount = 4;
+
+///////////////////////////////////////////////////////////////////////////////
+// Debug
+///////////////////////////////////////////////////////////////////////////////
+
+struct CacheLogger {
+    CacheLogger() {
+        LOGD("Creating caches");
+    }
+}; // struct CacheLogger
+
+///////////////////////////////////////////////////////////////////////////////
+// Caches
+///////////////////////////////////////////////////////////////////////////////
+
+class Caches: public Singleton<Caches> {
+    Caches();
+    ~Caches();
+
+    friend class Singleton<Caches>;
+
+    CacheLogger mlogger;
+
+    GLuint mCurrentBuffer;
+
+    // Used to render layers
+    TextureVertex* mRegionMesh;
+    GLuint mRegionMeshIndices;
+
+public:
+    /**
+     * Binds the VBO used to render simple textured quads.
+     */
+    void bindMeshBuffer();
+
+    /**
+     * Binds the specified VBO if needed.
+     */
+    void bindMeshBuffer(const GLuint buffer);
+
+    /**
+     * Unbinds the VBO used to render simple textured quads.
+     */
+    void unbindMeshBuffer();
+
+    /**
+     * Returns the mesh used to draw regions. Calling this method will
+     * bind a VBO of type GL_ELEMENT_ARRAY_BUFFER that contains the
+     * indices for the region mesh.
+     */
+    TextureVertex* getRegionMesh();
+
+    bool blend;
+    GLenum lastSrcMode;
+    GLenum lastDstMode;
+    Program* currentProgram;
+
+    // VBO to draw with
+    GLuint meshBuffer;
+
+    // GL extensions
+    Extensions extensions;
+
+    // Misc
+    GLint maxTextureSize;
+
+    TextureCache textureCache;
+    LayerCache layerCache;
+    GradientCache gradientCache;
+    ProgramCache programCache;
+    PathCache pathCache;
+    PatchCache patchCache;
+    TextDropShadowCache dropShadowCache;
+    FboCache fboCache;
+    GammaFontRenderer fontRenderer;
+    ResourceCache resourceCache;
+
+    Line line;
+}; // class Caches
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHES_H
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
new file mode 100644
index 0000000..23ccef6
--- /dev/null
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "DisplayListRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define PATH_HEAP_SIZE 64
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers
+///////////////////////////////////////////////////////////////////////////////
+
+PathHeap::PathHeap(): mHeap(PATH_HEAP_SIZE * sizeof(SkPath)) {
+}
+
+PathHeap::PathHeap(SkFlattenableReadBuffer& buffer): mHeap(PATH_HEAP_SIZE * sizeof(SkPath)) {
+    int count = buffer.readS32();
+
+    mPaths.setCount(count);
+    SkPath** ptr = mPaths.begin();
+    SkPath* p = (SkPath*) mHeap.allocThrow(count * sizeof(SkPath));
+
+    for (int i = 0; i < count; i++) {
+        new (p) SkPath;
+        p->unflatten(buffer);
+        *ptr++ = p;
+        p++;
+    }
+}
+
+PathHeap::~PathHeap() {
+    SkPath** iter = mPaths.begin();
+    SkPath** stop = mPaths.end();
+    while (iter < stop) {
+        (*iter)->~SkPath();
+        iter++;
+    }
+}
+
+int PathHeap::append(const SkPath& path) {
+    SkPath* p = (SkPath*) mHeap.allocThrow(sizeof(SkPath));
+    new (p) SkPath(path);
+    *mPaths.append() = p;
+    return mPaths.count();
+}
+
+void PathHeap::flatten(SkFlattenableWriteBuffer& buffer) const {
+    int count = mPaths.count();
+
+    buffer.write32(count);
+    SkPath** iter = mPaths.begin();
+    SkPath** stop = mPaths.end();
+    while (iter < stop) {
+        (*iter)->flatten(buffer);
+        iter++;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Display list
+///////////////////////////////////////////////////////////////////////////////
+
+DisplayList::DisplayList(const DisplayListRenderer& recorder) {
+    const SkWriter32& writer = recorder.writeStream();
+    init();
+
+    if (writer.size() == 0) {
+        return;
+    }
+
+    size_t size = writer.size();
+    void* buffer = sk_malloc_throw(size);
+    writer.flatten(buffer);
+    mReader.setMemory(buffer, size);
+
+    mRCPlayback.reset(&recorder.mRCRecorder);
+    mRCPlayback.setupBuffer(mReader);
+
+    mTFPlayback.reset(&recorder.mTFRecorder);
+    mTFPlayback.setupBuffer(mReader);
+
+    Caches& caches = Caches::getInstance();
+
+    const Vector<SkBitmap*> &bitmapResources = recorder.getBitmapResources();
+    for (size_t i = 0; i < bitmapResources.size(); i++) {
+        SkBitmap* resource = bitmapResources.itemAt(i);
+        mBitmapResources.add(resource);
+        caches.resourceCache.incrementRefcount(resource);
+    }
+
+    const Vector<SkiaShader*> &shaderResources = recorder.getShaderResources();
+    for (size_t i = 0; i < shaderResources.size(); i++) {
+        SkiaShader* resource = shaderResources.itemAt(i);
+        mShaderResources.add(resource);
+        caches.resourceCache.incrementRefcount(resource);
+    }
+
+    const Vector<SkPaint*> &paints = recorder.getPaints();
+    for (size_t i = 0; i < paints.size(); i++) {
+        mPaints.add(paints.itemAt(i));
+    }
+
+    const Vector<SkMatrix*> &matrices = recorder.getMatrices();
+    for (size_t i = 0; i < matrices.size(); i++) {
+        mMatrices.add(matrices.itemAt(i));
+    }
+
+    mPathHeap = recorder.mPathHeap;
+    mPathHeap->safeRef();
+}
+
+DisplayList::~DisplayList() {
+    sk_free((void*) mReader.base());
+
+    Caches& caches = Caches::getInstance();
+
+    for (size_t i = 0; i < mBitmapResources.size(); i++) {
+        caches.resourceCache.decrementRefcount(mBitmapResources.itemAt(i));
+    }
+    mBitmapResources.clear();
+
+    for (size_t i = 0; i < mShaderResources.size(); i++) {
+        caches.resourceCache.decrementRefcount(mShaderResources.itemAt(i));
+    }
+    mShaderResources.clear();
+
+    for (size_t i = 0; i < mPaints.size(); i++) {
+        delete mPaints.itemAt(i);
+    }
+    mPaints.clear();
+
+    for (size_t i = 0; i < mMatrices.size(); i++) {
+        delete  mMatrices.itemAt(i);
+    }
+    mMatrices.clear();
+
+    mPathHeap->safeUnref();
+}
+
+void DisplayList::init() {
+    mPathHeap = NULL;
+}
+
+void DisplayList::replay(OpenGLRenderer& renderer) {
+    TextContainer text;
+    mReader.rewind();
+
+    int saveCount = renderer.getSaveCount() - 1;
+
+    while (!mReader.eof()) {
+        int op = mReader.readInt();
+        switch (op) {
+            case AcquireContext: {
+                renderer.acquireContext();
+            }
+            break;
+            case ReleaseContext: {
+                renderer.releaseContext();
+            }
+            break;
+            case Save: {
+                renderer.save(getInt());
+            }
+            break;
+            case Restore: {
+                renderer.restore();
+            }
+            break;
+            case RestoreToCount: {
+                renderer.restoreToCount(saveCount + getInt());
+            }
+            break;
+            case SaveLayer: {
+                renderer.saveLayer(getFloat(), getFloat(), getFloat(), getFloat(),
+                        getPaint(), getInt());
+            }
+            break;
+            case SaveLayerAlpha: {
+                renderer.saveLayerAlpha(getFloat(), getFloat(), getFloat(), getFloat(),
+                        getInt(), getInt());
+            }
+            break;
+            case Translate: {
+                renderer.translate(getFloat(), getFloat());
+            }
+            break;
+            case Rotate: {
+                renderer.rotate(getFloat());
+            }
+            break;
+            case Scale: {
+                renderer.scale(getFloat(), getFloat());
+            }
+            break;
+            case SetMatrix: {
+                renderer.setMatrix(getMatrix());
+            }
+            break;
+            case ConcatMatrix: {
+                renderer.concatMatrix(getMatrix());
+            }
+            break;
+            case ClipRect: {
+                renderer.clipRect(getFloat(), getFloat(), getFloat(), getFloat(),
+                        (SkRegion::Op) getInt());
+            }
+            break;
+            case DrawBitmap: {
+                renderer.drawBitmap(getBitmap(), getFloat(), getFloat(), getPaint());
+            }
+            break;
+            case DrawBitmapMatrix: {
+                renderer.drawBitmap(getBitmap(), getMatrix(), getPaint());
+            }
+            break;
+            case DrawBitmapRect: {
+                renderer.drawBitmap(getBitmap(), getFloat(), getFloat(), getFloat(), getFloat(),
+                        getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
+            }
+            break;
+            case DrawPatch: {
+                int32_t* xDivs = NULL;
+                int32_t* yDivs = NULL;
+                uint32_t* colors = NULL;
+                uint32_t xDivsCount = 0;
+                uint32_t yDivsCount = 0;
+                int8_t numColors = 0;
+
+                SkBitmap* bitmap = getBitmap();
+
+                xDivs = getInts(xDivsCount);
+                yDivs = getInts(yDivsCount);
+                colors = getUInts(numColors);
+
+                renderer.drawPatch(bitmap, xDivs, yDivs, colors, xDivsCount, yDivsCount,
+                        numColors, getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
+            }
+            break;
+            case DrawColor: {
+                renderer.drawColor(getInt(), (SkXfermode::Mode) getInt());
+            }
+            break;
+            case DrawRect: {
+                renderer.drawRect(getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
+            }
+            break;
+            case DrawPath: {
+                renderer.drawPath(getPath(), getPaint());
+            }
+            break;
+            case DrawLines: {
+                int count = 0;
+                float* points = getFloats(count);
+                renderer.drawLines(points, count, getPaint());
+            }
+            break;
+            case DrawText: {
+                getText(&text);
+                renderer.drawText(text.text(), text.length(), getInt(),
+                        getFloat(), getFloat(), getPaint());
+            }
+            break;
+            case ResetShader: {
+                renderer.resetShader();
+            }
+            break;
+            case SetupShader: {
+                renderer.setupShader(getShader());
+            }
+            break;
+            case ResetColorFilter: {
+                renderer.resetColorFilter();
+            }
+            break;
+            case SetupColorFilter: {
+                renderer.setupColorFilter(getColorFilter());
+            }
+            break;
+            case ResetShadow: {
+                renderer.resetShadow();
+            }
+            break;
+            case SetupShadow: {
+                renderer.setupShadow(getFloat(), getFloat(), getFloat(), getInt());
+            }
+            break;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Base structure
+///////////////////////////////////////////////////////////////////////////////
+
+DisplayListRenderer::DisplayListRenderer():
+        mHeap(HEAP_BLOCK_SIZE), mWriter(MIN_WRITER_SIZE) {
+    mPathHeap = NULL;
+}
+
+DisplayListRenderer::~DisplayListRenderer() {
+    reset();
+}
+
+void DisplayListRenderer::reset() {
+    if (mPathHeap) {
+        mPathHeap->unref();
+        mPathHeap = NULL;
+    }
+
+    mWriter.reset();
+    mHeap.reset();
+
+    mRCRecorder.reset();
+    mTFRecorder.reset();
+
+    Caches& caches = Caches::getInstance();
+    for (size_t i = 0; i < mBitmapResources.size(); i++) {
+        SkBitmap* resource = mBitmapResources.itemAt(i);
+        caches.resourceCache.decrementRefcount(resource);
+    }
+    mBitmapResources.clear();
+
+    for (size_t i = 0; i < mShaderResources.size(); i++) {
+        SkiaShader* resource = mShaderResources.itemAt(i);
+        caches.resourceCache.decrementRefcount(resource);
+    }
+    mShaderResources.clear();
+
+    mPaints.clear();
+    mPaintMap.clear();
+    mMatrices.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Operations
+///////////////////////////////////////////////////////////////////////////////
+
+void DisplayListRenderer::setViewport(int width, int height) {
+    mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
+
+    mWidth = width;
+    mHeight = height;
+}
+
+void DisplayListRenderer::prepare(bool opaque) {
+    mSnapshot = new Snapshot(mFirstSnapshot,
+            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    mSaveCount = 1;
+    mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight);
+}
+
+void DisplayListRenderer::acquireContext() {
+    addOp(DisplayList::AcquireContext);
+    OpenGLRenderer::acquireContext();
+}
+
+void DisplayListRenderer::releaseContext() {
+    addOp(DisplayList::ReleaseContext);
+    OpenGLRenderer::releaseContext();
+}
+
+int DisplayListRenderer::save(int flags) {
+    addOp(DisplayList::Save);
+    addInt(flags);
+    return OpenGLRenderer::save(flags);
+}
+
+void DisplayListRenderer::restore() {
+    addOp(DisplayList::Restore);
+    OpenGLRenderer::restore();
+}
+
+void DisplayListRenderer::restoreToCount(int saveCount) {
+    addOp(DisplayList::RestoreToCount);
+    addInt(saveCount);
+    OpenGLRenderer::restoreToCount(saveCount);
+}
+
+int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom,
+        SkPaint* p, int flags) {
+    addOp(DisplayList::SaveLayer);
+    addBounds(left, top, right, bottom);
+    addPaint(p);
+    addInt(flags);
+    return OpenGLRenderer::save(flags);
+}
+
+int DisplayListRenderer::saveLayerAlpha(float left, float top, float right, float bottom,
+        int alpha, int flags) {
+    addOp(DisplayList::SaveLayerAlpha);
+    addBounds(left, top, right, bottom);
+    addInt(alpha);
+    addInt(flags);
+    return OpenGLRenderer::save(flags);
+}
+
+void DisplayListRenderer::translate(float dx, float dy) {
+    addOp(DisplayList::Translate);
+    addPoint(dx, dy);
+    OpenGLRenderer::translate(dx, dy);
+}
+
+void DisplayListRenderer::rotate(float degrees) {
+    addOp(DisplayList::Rotate);
+    addFloat(degrees);
+    OpenGLRenderer::rotate(degrees);
+}
+
+void DisplayListRenderer::scale(float sx, float sy) {
+    addOp(DisplayList::Scale);
+    addPoint(sx, sy);
+    OpenGLRenderer::scale(sx, sy);
+}
+
+void DisplayListRenderer::setMatrix(SkMatrix* matrix) {
+    addOp(DisplayList::SetMatrix);
+    addMatrix(matrix);
+    OpenGLRenderer::setMatrix(matrix);
+}
+
+void DisplayListRenderer::concatMatrix(SkMatrix* matrix) {
+    addOp(DisplayList::ConcatMatrix);
+    addMatrix(matrix);
+    OpenGLRenderer::concatMatrix(matrix);
+}
+
+bool DisplayListRenderer::clipRect(float left, float top, float right, float bottom,
+        SkRegion::Op op) {
+    addOp(DisplayList::ClipRect);
+    addBounds(left, top, right, bottom);
+    addInt(op);
+    return OpenGLRenderer::clipRect(left, top, right, bottom, op);
+}
+
+void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top,
+        SkPaint* paint) {
+    addOp(DisplayList::DrawBitmap);
+    addBitmap(bitmap);
+    addPoint(left, top);
+    addPaint(paint);
+}
+
+void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix,
+        SkPaint* paint) {
+    addOp(DisplayList::DrawBitmapMatrix);
+    addBitmap(bitmap);
+    addMatrix(matrix);
+    addPaint(paint);
+}
+
+void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
+        float srcRight, float srcBottom, float dstLeft, float dstTop,
+        float dstRight, float dstBottom, SkPaint* paint) {
+    addOp(DisplayList::DrawBitmapRect);
+    addBitmap(bitmap);
+    addBounds(srcLeft, srcTop, srcRight, srcBottom);
+    addBounds(dstLeft, dstTop, dstRight, dstBottom);
+    addPaint(paint);
+}
+
+void DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
+        const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+        float left, float top, float right, float bottom, SkPaint* paint) {
+    addOp(DisplayList::DrawPatch);
+    addBitmap(bitmap);
+    addInts(xDivs, width);
+    addInts(yDivs, height);
+    addUInts(colors, numColors);
+    addBounds(left, top, right, bottom);
+    addPaint(paint);
+}
+
+void DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) {
+    addOp(DisplayList::DrawColor);
+    addInt(color);
+    addInt(mode);
+}
+
+void DisplayListRenderer::drawRect(float left, float top, float right, float bottom,
+        SkPaint* paint) {
+    addOp(DisplayList::DrawRect);
+    addBounds(left, top, right, bottom);
+    addPaint(paint);
+}
+
+void DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) {
+    addOp(DisplayList::DrawPath);
+    addPath(path);
+    addPaint(paint);
+}
+
+void DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) {
+    addOp(DisplayList::DrawLines);
+    addFloats(points, count);
+    addPaint(paint);
+}
+
+void DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
+        float x, float y, SkPaint* paint) {
+    addOp(DisplayList::DrawText);
+    addText(text, bytesCount);
+    addInt(count);
+    addPoint(x, y);
+    addPaint(paint);
+}
+
+void DisplayListRenderer::resetShader() {
+    addOp(DisplayList::ResetShader);
+}
+
+void DisplayListRenderer::setupShader(SkiaShader* shader) {
+    addOp(DisplayList::SetupShader);
+    addShader(shader);
+}
+
+void DisplayListRenderer::resetColorFilter() {
+    addOp(DisplayList::ResetColorFilter);
+}
+
+void DisplayListRenderer::setupColorFilter(SkiaColorFilter* filter) {
+    addOp(DisplayList::SetupColorFilter);
+    addColorFilter(filter);
+}
+
+void DisplayListRenderer::resetShadow() {
+    addOp(DisplayList::ResetShadow);
+}
+
+void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int color) {
+    addOp(DisplayList::SetupShadow);
+    addFloat(radius);
+    addPoint(dx, dy);
+    addInt(color);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
new file mode 100644
index 0000000..fd69fab
--- /dev/null
+++ b/libs/hwui/DisplayListRenderer.h
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
+#define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
+
+#include <SkChunkAlloc.h>
+#include <SkFlattenable.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkPictureFlat.h>
+#include <SkRefCnt.h>
+#include <SkTDArray.h>
+#include <SkTSearch.h>
+
+#include "OpenGLRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define MIN_WRITER_SIZE 16384
+#define HEAP_BLOCK_SIZE 4096
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers
+///////////////////////////////////////////////////////////////////////////////
+
+class PathHeap: public SkRefCnt {
+public:
+    PathHeap();
+    PathHeap(SkFlattenableReadBuffer& buffer);
+    ~PathHeap();
+
+    int append(const SkPath& path);
+
+    int count() const { return mPaths.count(); }
+
+    SkPath& operator[](int index) const {
+        return *mPaths[index];
+    }
+
+    void flatten(SkFlattenableWriteBuffer& buffer) const;
+
+private:
+    SkChunkAlloc mHeap;
+    SkTDArray<SkPath*> mPaths;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Display list
+///////////////////////////////////////////////////////////////////////////////
+
+class DisplayListRenderer;
+
+/**
+ * Replays recorded drawing commands.
+ */
+class DisplayList {
+public:
+    DisplayList(const DisplayListRenderer& recorder);
+    ~DisplayList();
+
+    enum Op {
+        AcquireContext,
+        ReleaseContext,
+        Save,
+        Restore,
+        RestoreToCount,
+        SaveLayer,
+        SaveLayerAlpha,
+        Translate,
+        Rotate,
+        Scale,
+        SetMatrix,
+        ConcatMatrix,
+        ClipRect,
+        DrawBitmap,
+        DrawBitmapMatrix,
+        DrawBitmapRect,
+        DrawPatch,
+        DrawColor,
+        DrawRect,
+        DrawPath,
+        DrawLines,
+        DrawText,
+        ResetShader,
+        SetupShader,
+        ResetColorFilter,
+        SetupColorFilter,
+        ResetShadow,
+        SetupShadow
+    };
+
+    void replay(OpenGLRenderer& renderer);
+
+private:
+    void init();
+
+    class TextContainer {
+    public:
+        size_t length() const {
+            return mByteLength;
+        }
+
+        const char* text() const {
+            return (const char*) mText;
+        }
+
+        size_t mByteLength;
+        const char* mText;
+    };
+
+    SkBitmap* getBitmap() {
+        return (SkBitmap*) getInt();
+    }
+
+    SkiaShader* getShader() {
+        return (SkiaShader*) getInt();
+    }
+
+    SkiaColorFilter* getColorFilter() {
+        return (SkiaColorFilter*) getInt();
+    }
+
+    inline int getIndex() {
+        return mReader.readInt();
+    }
+
+    inline int getInt() {
+        return mReader.readInt();
+    }
+
+    SkMatrix* getMatrix() {
+        return (SkMatrix*) getInt();
+    }
+
+    SkPath* getPath() {
+        return &(*mPathHeap)[getInt() - 1];
+    }
+
+    SkPaint* getPaint() {
+        return (SkPaint*) getInt();
+    }
+
+    inline float getFloat() {
+        return mReader.readScalar();
+    }
+
+    int32_t* getInts(uint32_t& count) {
+        count = getInt();
+        return (int32_t*) mReader.skip(count * sizeof(int32_t));
+    }
+
+    uint32_t* getUInts(int8_t& count) {
+        count = getInt();
+        return (uint32_t*) mReader.skip(count * sizeof(uint32_t));
+    }
+
+    float* getFloats(int& count) {
+        count = getInt();
+        return (float*) mReader.skip(count * sizeof(float));
+    }
+
+    void getText(TextContainer* text) {
+        size_t length = text->mByteLength = getInt();
+        text->mText = (const char*) mReader.skip(length);
+    }
+
+    PathHeap* mPathHeap;
+
+    Vector<SkBitmap*> mBitmapResources;
+    Vector<SkiaShader*> mShaderResources;
+    Vector<SkiaColorFilter*> mFilterResources;
+
+    Vector<SkPaint*> mPaints;
+    Vector<SkMatrix*> mMatrices;
+
+    mutable SkFlattenableReadBuffer mReader;
+
+    SkRefCntPlayback mRCPlayback;
+    SkTypefacePlayback mTFPlayback;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Renderer
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Records drawing commands in a display list for latter playback.
+ */
+class DisplayListRenderer: public OpenGLRenderer {
+public:
+    DisplayListRenderer();
+    ~DisplayListRenderer();
+
+    void setViewport(int width, int height);
+    void prepare(bool opaque);
+
+    void acquireContext();
+    void releaseContext();
+
+    int save(int flags);
+    void restore();
+    void restoreToCount(int saveCount);
+
+    int saveLayer(float left, float top, float right, float bottom,
+            SkPaint* p, int flags);
+    int saveLayerAlpha(float left, float top, float right, float bottom,
+                int alpha, int flags);
+
+    void translate(float dx, float dy);
+    void rotate(float degrees);
+    void scale(float sx, float sy);
+
+    void setMatrix(SkMatrix* matrix);
+    void concatMatrix(SkMatrix* matrix);
+
+    bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+
+    void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
+    void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
+    void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, SkPaint* paint);
+    void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
+            const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+            float left, float top, float right, float bottom, SkPaint* paint);
+    void drawColor(int color, SkXfermode::Mode mode);
+    void drawRect(float left, float top, float right, float bottom, SkPaint* paint);
+    void drawPath(SkPath* path, SkPaint* paint);
+    void drawLines(float* points, int count, SkPaint* paint);
+    void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint);
+
+    void resetShader();
+    void setupShader(SkiaShader* shader);
+
+    void resetColorFilter();
+    void setupColorFilter(SkiaColorFilter* filter);
+
+    void resetShadow();
+    void setupShadow(float radius, float dx, float dy, int color);
+
+    void reset();
+
+    DisplayList* getDisplayList() const {
+        return new DisplayList(*this);
+    }
+
+    const SkWriter32& writeStream() const {
+        return mWriter;
+    }
+
+    const Vector<SkBitmap*>& getBitmapResources() const {
+        return mBitmapResources;
+    }
+
+    const Vector<SkiaShader*>& getShaderResources() const {
+        return mShaderResources;
+    }
+
+    const Vector<SkPaint*>& getPaints() const {
+        return mPaints;
+    }
+
+    const Vector<SkMatrix*>& getMatrices() const {
+        return mMatrices;
+    }
+
+    const Vector<SkiaColorFilter*>& getFilterResources() const {
+        return mFilterResources;
+    }
+
+private:
+    inline void addOp(DisplayList::Op drawOp) {
+        mWriter.writeInt(drawOp);
+    }
+
+    inline void addInt(int value) {
+        mWriter.writeInt(value);
+    }
+
+    void addInts(const int32_t* values, uint32_t count) {
+        mWriter.writeInt(count);
+        for (uint32_t i = 0; i < count; i++) {
+            mWriter.writeInt(values[i]);
+        }
+    }
+
+    void addUInts(const uint32_t* values, int8_t count) {
+        mWriter.writeInt(count);
+        for (int8_t i = 0; i < count; i++) {
+            mWriter.writeInt(values[i]);
+        }
+    }
+
+    inline void addFloat(float value) {
+        mWriter.writeScalar(value);
+    }
+
+    void addFloats(const float* values, int count) {
+        mWriter.writeInt(count);
+        for (int i = 0; i < count; i++) {
+            mWriter.writeScalar(values[i]);
+        }
+    }
+
+    inline void addPoint(float x, float y) {
+        mWriter.writeScalar(x);
+        mWriter.writeScalar(y);
+    }
+
+    inline void addBounds(float left, float top, float right, float bottom) {
+        mWriter.writeScalar(left);
+        mWriter.writeScalar(top);
+        mWriter.writeScalar(right);
+        mWriter.writeScalar(bottom);
+    }
+
+    inline void addText(const void* text, size_t byteLength) {
+        mWriter.writeInt(byteLength);
+        mWriter.writePad(text, byteLength);
+    }
+
+    inline void addPath(const SkPath* path) {
+        if (mPathHeap == NULL) {
+            mPathHeap = new PathHeap();
+        }
+        addInt(mPathHeap->append(*path));
+    }
+
+    inline void addPaint(SkPaint* paint) {
+        if (paint == NULL) {
+            addInt((int)NULL);
+            return;
+        }
+        SkPaint *paintCopy =  mPaintMap.valueFor(paint);
+        if (paintCopy == NULL || paintCopy->getGenerationID() != paint->getGenerationID()) {
+            paintCopy = new SkPaint(*paint);
+            mPaintMap.add(paint, paintCopy);
+            mPaints.add(paintCopy);
+        }
+        addInt((int)paintCopy);
+    }
+
+    inline void addMatrix(SkMatrix* matrix) {
+        // Copying the matrix is cheap and prevents against the user changing the original
+        // matrix before the operation that uses it
+        addInt((int) new SkMatrix(*matrix));
+    }
+
+    inline void addBitmap(SkBitmap* bitmap) {
+        // Note that this assumes the bitmap is immutable. There are cases this won't handle
+        // correctly, such as creating the bitmap from scratch, drawing with it, changing its
+        // contents, and drawing again. The only fix would be to always copy it the first time,
+        // which doesn't seem worth the extra cycles for this unlikely case.
+        addInt((int)bitmap);
+        mBitmapResources.add(bitmap);
+        Caches& caches = Caches::getInstance();
+        caches.resourceCache.incrementRefcount(bitmap);
+    }
+
+    inline void addShader(SkiaShader* shader) {
+        addInt((int)shader);
+        mShaderResources.add(shader);
+        Caches& caches = Caches::getInstance();
+        caches.resourceCache.incrementRefcount(shader);
+    }
+
+    inline void addColorFilter(SkiaColorFilter* colorFilter) {
+        addInt((int)colorFilter);
+        mFilterResources.add(colorFilter);
+        Caches& caches = Caches::getInstance();
+        caches.resourceCache.incrementRefcount(colorFilter);
+    }
+
+    SkChunkAlloc mHeap;
+
+    Vector<SkBitmap*> mBitmapResources;
+    Vector<SkiaShader*> mShaderResources;
+    Vector<SkiaColorFilter*> mFilterResources;
+
+    Vector<SkPaint*> mPaints;
+    DefaultKeyedVector<SkPaint *, SkPaint *> mPaintMap;
+    Vector<SkMatrix*> mMatrices;
+
+    PathHeap* mPathHeap;
+    SkWriter32 mWriter;
+
+    SkRefCntRecorder mRCRecorder;
+    SkRefCntRecorder mTFRecorder;
+
+    friend class DisplayList;
+
+}; // class DisplayListRenderer
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
new file mode 100644
index 0000000..eceb5c1c
--- /dev/null
+++ b/libs/hwui/Extensions.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_EXTENSIONS_H
+#define ANDROID_HWUI_EXTENSIONS_H
+
+#include <utils/SortedVector.h>
+#include <utils/String8.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_EXTENSIONS 0
+
+// Debug
+#if DEBUG_EXTENSIONS
+    #define EXT_LOGD(...) LOGD(__VA_ARGS__)
+#else
+    #define EXT_LOGD(...)
+#endif
+
+class Extensions {
+public:
+    Extensions() {
+        const char* buffer = (const char*) glGetString(GL_EXTENSIONS);
+        const char* current = buffer;
+        const char* head = current;
+        EXT_LOGD("Available GL extensions:");
+        do {
+            head = strchr(current, ' ');
+            String8 s(current, head ? head - current : strlen(current));
+            if (s.length()) {
+                mExtensionList.add(s);
+                EXT_LOGD("  %s", s.string());
+            }
+            current = head + 1;
+        } while (head);
+
+        mHasNPot = hasExtension("GL_OES_texture_npot");
+        mHasDrawPath = hasExtension("GL_NV_draw_path");
+        mHasCoverageSample = hasExtension("GL_NV_coverage_sample");
+        mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");
+
+        mExtensions = buffer;
+    }
+
+    inline bool hasNPot() const { return mHasNPot; }
+    inline bool hasDrawPath() const { return mHasDrawPath; }
+    inline bool hasCoverageSample() const { return mHasCoverageSample; }
+    inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
+
+    bool hasExtension(const char* extension) const {
+        const String8 s(extension);
+        return mExtensionList.indexOf(s) >= 0;
+    }
+
+    void dump() {
+        LOGD("Supported extensions:\n%s", mExtensions);
+    }
+
+private:
+    SortedVector<String8> mExtensionList;
+
+    const char* mExtensions;
+
+    bool mHasNPot;
+    bool mHasDrawPath;
+    bool mHasCoverageSample;
+    bool mHasFramebufferFetch;
+}; // class Extensions
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_EXTENSIONS_H
diff --git a/libs/hwui/FboCache.cpp b/libs/hwui/FboCache.cpp
new file mode 100644
index 0000000..2ef71c2
--- /dev/null
+++ b/libs/hwui/FboCache.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <stdlib.h>
+
+#include "FboCache.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+FboCache::FboCache(): mMaxSize(DEFAULT_FBO_CACHE_SIZE) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_FBO_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting fbo cache size to %s", property);
+        mMaxSize = atoi(property);
+    } else {
+        LOGD("  Using default fbo cache size of %d", DEFAULT_FBO_CACHE_SIZE);
+    }
+}
+
+FboCache::~FboCache() {
+    clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t FboCache::getSize() {
+    return mCache.size();
+}
+
+uint32_t FboCache::getMaxSize() {
+    return mMaxSize;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void FboCache::clear() {
+    for (size_t i = 0; i < mCache.size(); i++) {
+        const GLuint fbo = mCache.itemAt(i);
+        glDeleteFramebuffers(1, &fbo);
+    }
+    mCache.clear();
+}
+
+GLuint FboCache::get() {
+    GLuint fbo;
+    if (mCache.size() > 0) {
+        fbo = mCache.itemAt(mCache.size() - 1);
+        mCache.removeAt(mCache.size() - 1);
+    } else {
+        glGenFramebuffers(1, &fbo);
+    }
+    return fbo;
+}
+
+bool FboCache::put(GLuint fbo) {
+    if (mCache.size() < mMaxSize) {
+        mCache.add(fbo);
+        return true;
+    }
+
+    glDeleteFramebuffers(1, &fbo);
+    return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/FboCache.h b/libs/hwui/FboCache.h
new file mode 100644
index 0000000..ad6cc3e
--- /dev/null
+++ b/libs/hwui/FboCache.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_FBO_CACHE_H
+#define ANDROID_HWUI_FBO_CACHE_H
+
+#include <GLES2/gl2.h>
+
+#include <utils/SortedVector.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
+class FboCache {
+public:
+    FboCache();
+    ~FboCache();
+
+    /**
+     * Returns an FBO from the cache. If no FBO is available, a new one
+     * is created. If creating a new FBO fails, 0 is returned.
+     *
+     * When an FBO is obtained from the cache, it is removed and the
+     * total number of FBOs available in the cache decreases.
+     *
+     * @return The name of the FBO, or 0 if no FBO can be obtained.
+     */
+    GLuint get();
+
+    /**
+     * Adds the specified FBO to the cache.
+     *
+     * @param fbo The FBO to add to the cache.
+     *
+     * @return True if the FBO was added, false otherwise.
+     */
+    bool put(GLuint fbo);
+
+    /**
+     * Clears the cache. This causes all FBOs to be deleted.
+     */
+    void clear();
+
+    /**
+     * Returns the current size of the cache.
+     */
+    uint32_t getSize();
+
+    /**
+     * Returns the maximum number of FBOs that the cache can hold.
+     */
+    uint32_t getMaxSize();
+
+private:
+    SortedVector<GLuint> mCache;
+    uint32_t mMaxSize;
+}; // class FboCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_FBO_CACHE_H
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
new file mode 100644
index 0000000..5224689
--- /dev/null
+++ b/libs/hwui/FontRenderer.cpp
@@ -0,0 +1,847 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <SkUtils.h>
+
+#include <cutils/properties.h>
+
+#include <utils/Log.h>
+
+#include "FontRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFAULT_TEXT_CACHE_WIDTH 1024
+#define DEFAULT_TEXT_CACHE_HEIGHT 256
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) :
+    mState(state), mFontId(fontId), mFontSize(fontSize) {
+}
+
+
+Font::~Font() {
+    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
+        if (mState->mActiveFonts[ct] == this) {
+            mState->mActiveFonts.removeAt(ct);
+            break;
+        }
+    }
+
+    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+        CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
+        delete glyph;
+    }
+}
+
+void Font::invalidateTextureCache() {
+    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+        mCachedGlyphs.valueAt(i)->mIsValid = false;
+    }
+}
+
+void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
+    int nPenX = x + glyph->mBitmapLeft;
+    int nPenY = y + glyph->mBitmapTop;
+
+    int width = (int) glyph->mBitmapWidth;
+    int height = (int) glyph->mBitmapHeight;
+
+    if (bounds->bottom > nPenY) {
+        bounds->bottom = nPenY;
+    }
+    if (bounds->left > nPenX) {
+        bounds->left = nPenX;
+    }
+    if (bounds->right < nPenX + width) {
+        bounds->right = nPenX + width;
+    }
+    if (bounds->top < nPenY + height) {
+        bounds->top = nPenY + height;
+    }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
+    int nPenX = x + glyph->mBitmapLeft;
+    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
+
+    float u1 = glyph->mBitmapMinU;
+    float u2 = glyph->mBitmapMaxU;
+    float v1 = glyph->mBitmapMinV;
+    float v2 = glyph->mBitmapMaxV;
+
+    int width = (int) glyph->mBitmapWidth;
+    int height = (int) glyph->mBitmapHeight;
+
+    mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
+            nPenX + width, nPenY, 0, u2, v2,
+            nPenX + width, nPenY - height, 0, u2, v1,
+            nPenX, nPenY - height, 0, u1, v1);
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+    int nPenX = x + glyph->mBitmapLeft;
+    int nPenY = y + glyph->mBitmapTop;
+
+    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
+    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
+
+    uint32_t cacheWidth = mState->getCacheWidth();
+    const uint8_t* cacheBuffer = mState->getTextTextureData();
+
+    uint32_t cacheX = 0, cacheY = 0;
+    int32_t bX = 0, bY = 0;
+    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
+        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
+            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
+                LOGE("Skipping invalid index");
+                continue;
+            }
+            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
+            bitmap[bY * bitmapW + bX] = tempCol;
+        }
+    }
+
+}
+
+Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
+    CachedGlyphInfo* cachedGlyph = NULL;
+    ssize_t index = mCachedGlyphs.indexOfKey(utfChar);
+    if (index >= 0) {
+        cachedGlyph = mCachedGlyphs.valueAt(index);
+    } else {
+        cachedGlyph = cacheGlyph(paint, utfChar);
+    }
+
+    // Is the glyph still in texture cache?
+    if (!cachedGlyph->mIsValid) {
+        const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
+        updateGlyphCache(paint, skiaGlyph, cachedGlyph);
+    }
+
+    return cachedGlyph;
+}
+
+void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
+        renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
+                bitmapW, bitmapH, NULL);
+    } else {
+        renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
+    }
+
+}
+
+void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+        int numGlyphs, Rect *bounds) {
+    if (bounds == NULL) {
+        LOGE("No return rectangle provided to measure text");
+        return;
+    }
+    bounds->set(1e6, -1e6, -1e6, 1e6);
+    renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
+}
+
+#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
+
+void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+        uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
+    if (numGlyphs == 0 || text == NULL || len == 0) {
+        return;
+    }
+
+    SkFixed penX = SkIntToFixed(x);
+    int penY = y;
+    int glyphsLeft = 1;
+    if (numGlyphs > 0) {
+        glyphsLeft = numGlyphs;
+    }
+
+    SkFixed prevRsbDelta = 0;
+    penX += SK_Fixed1 / 2;
+
+    text += start;
+
+    while (glyphsLeft > 0) {
+        int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
+
+        // Reached the end of the string
+        if (utfChar < 0) {
+            break;
+        }
+
+        CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar);
+        penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta);
+        prevRsbDelta = cachedGlyph->mRsbDelta;
+
+        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+        if (cachedGlyph->mIsValid) {
+            switch(mode) {
+            case FRAMEBUFFER:
+                drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY);
+                break;
+            case BITMAP:
+                drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH);
+                break;
+            case MEASURE:
+                measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds);
+                break;
+            }
+        }
+
+        penX += cachedGlyph->mAdvanceX;
+
+        // If we were given a specific number of glyphs, decrement
+        if (numGlyphs > 0) {
+            glyphsLeft--;
+        }
+    }
+}
+
+void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
+    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
+    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
+    glyph->mBitmapLeft = skiaGlyph.fLeft;
+    glyph->mBitmapTop = skiaGlyph.fTop;
+    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
+    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
+
+    uint32_t startX = 0;
+    uint32_t startY = 0;
+
+    // Get the bitmap for the glyph
+    paint->findImage(skiaGlyph);
+    glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
+
+    if (!glyph->mIsValid) {
+        return;
+    }
+
+    uint32_t endX = startX + skiaGlyph.fWidth;
+    uint32_t endY = startY + skiaGlyph.fHeight;
+
+    glyph->mStartX = startX;
+    glyph->mStartY = startY;
+    glyph->mBitmapWidth = skiaGlyph.fWidth;
+    glyph->mBitmapHeight = skiaGlyph.fHeight;
+
+    uint32_t cacheWidth = mState->getCacheWidth();
+    uint32_t cacheHeight = mState->getCacheHeight();
+
+    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
+    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
+    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
+    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
+
+    mState->mUploadTexture = true;
+}
+
+Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
+    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
+    mCachedGlyphs.add(glyph, newGlyph);
+
+    const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
+    newGlyph->mGlyphIndex = skiaGlyph.fID;
+    newGlyph->mIsValid = false;
+
+    updateGlyphCache(paint, skiaGlyph, newGlyph);
+
+    return newGlyph;
+}
+
+Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
+    Vector<Font*> &activeFonts = state->mActiveFonts;
+
+    for (uint32_t i = 0; i < activeFonts.size(); i++) {
+        Font* font = activeFonts[i];
+        if (font->mFontId == fontId && font->mFontSize == fontSize) {
+            return font;
+        }
+    }
+
+    Font* newFont = new Font(state, fontId, fontSize);
+    activeFonts.push(newFont);
+    return newFont;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FontRenderer
+///////////////////////////////////////////////////////////////////////////////
+
+FontRenderer::FontRenderer() {
+    LOGD("Creating FontRenderer");
+
+    mGammaTable = NULL;
+    mInitialized = false;
+    mMaxNumberOfQuads = 1024;
+    mCurrentQuadIndex = 0;
+    mTextureId = 0;
+
+    mTextMeshPtr = NULL;
+    mTextTexture = NULL;
+
+    mIndexBufferID = 0;
+
+    mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
+    mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
+
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
+        LOGD("  Setting text cache width to %s pixels", property);
+        mCacheWidth = atoi(property);
+    } else {
+        LOGD("  Using default text cache width of %i pixels", mCacheWidth);
+    }
+
+    if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
+        LOGD("  Setting text cache width to %s pixels", property);
+        mCacheHeight = atoi(property);
+    } else {
+        LOGD("  Using default text cache height of %i pixels", mCacheHeight);
+    }
+}
+
+FontRenderer::~FontRenderer() {
+    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+        delete mCacheLines[i];
+    }
+    mCacheLines.clear();
+
+    if (mInitialized) {
+        delete[] mTextMeshPtr;
+        delete[] mTextTexture;
+    }
+
+    if (mTextureId) {
+        glDeleteTextures(1, &mTextureId);
+    }
+
+    Vector<Font*> fontsToDereference = mActiveFonts;
+    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
+        delete fontsToDereference[i];
+    }
+}
+
+void FontRenderer::flushAllAndInvalidate() {
+    if (mCurrentQuadIndex != 0) {
+        issueDrawCommand();
+        mCurrentQuadIndex = 0;
+    }
+    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
+        mActiveFonts[i]->invalidateTextureCache();
+    }
+    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+        mCacheLines[i]->mCurrentCol = 0;
+    }
+}
+
+bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
+    // If the glyph is too tall, don't cache it
+    if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
+        LOGE("Font size to large to fit in cache. width, height = %i, %i",
+                (int) glyph.fWidth, (int) glyph.fHeight);
+        return false;
+    }
+
+    // Now copy the bitmap into the cache texture
+    uint32_t startX = 0;
+    uint32_t startY = 0;
+
+    bool bitmapFit = false;
+    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
+        if (bitmapFit) {
+            break;
+        }
+    }
+
+    // If the new glyph didn't fit, flush the state so far and invalidate everything
+    if (!bitmapFit) {
+        flushAllAndInvalidate();
+
+        // Try to fit it again
+        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
+            if (bitmapFit) {
+                break;
+            }
+        }
+
+        // if we still don't fit, something is wrong and we shouldn't draw
+        if (!bitmapFit) {
+            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
+                    (int) glyph.fWidth, (int) glyph.fHeight);
+            return false;
+        }
+    }
+
+    *retOriginX = startX;
+    *retOriginY = startY;
+
+    uint32_t endX = startX + glyph.fWidth;
+    uint32_t endY = startY + glyph.fHeight;
+
+    uint32_t cacheWidth = mCacheWidth;
+
+    uint8_t* cacheBuffer = mTextTexture;
+    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
+    unsigned int stride = glyph.rowBytes();
+
+    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
+    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
+        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
+            uint8_t tempCol = bitmapBuffer[bY * stride + bX];
+            cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
+        }
+    }
+
+    return true;
+}
+
+void FontRenderer::initTextTexture() {
+    mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
+    memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
+
+    mUploadTexture = false;
+
+    glGenTextures(1, &mTextureId);
+    glBindTexture(GL_TEXTURE_2D, mTextureId);
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+    // Initialize texture dimentions
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
+            GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+
+    mLinearFiltering = false;
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+    // Split up our cache texture into lines of certain widths
+    int nextLine = 0;
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
+}
+
+// Avoid having to reallocate memory and render quad by quad
+void FontRenderer::initVertexArrayBuffers() {
+    uint32_t numIndicies = mMaxNumberOfQuads * 6;
+    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
+    uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
+
+    // Four verts, two triangles , six indices per quad
+    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
+        int i6 = i * 6;
+        int i4 = i * 4;
+
+        indexBufferData[i6 + 0] = i4 + 0;
+        indexBufferData[i6 + 1] = i4 + 1;
+        indexBufferData[i6 + 2] = i4 + 2;
+
+        indexBufferData[i6 + 3] = i4 + 0;
+        indexBufferData[i6 + 4] = i4 + 2;
+        indexBufferData[i6 + 5] = i4 + 3;
+    }
+
+    glGenBuffers(1, &mIndexBufferID);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+    free(indexBufferData);
+
+    uint32_t coordSize = 3;
+    uint32_t uvSize = 2;
+    uint32_t vertsPerQuad = 4;
+    uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
+    mTextMeshPtr = new float[vertexBufferSize];
+}
+
+// We don't want to allocate anything unless we actually draw text
+void FontRenderer::checkInit() {
+    if (mInitialized) {
+        return;
+    }
+
+    initTextTexture();
+    initVertexArrayBuffers();
+
+    // We store a string with letters in a rough frequency of occurrence
+    mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
+    mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
+    mLatinPrecache += String16(",.?!()-+@;:`'");
+    mLatinPrecache += String16("0123456789");
+
+    mInitialized = true;
+}
+
+void FontRenderer::checkTextureUpdate() {
+    if (!mUploadTexture) {
+        return;
+    }
+
+    glBindTexture(GL_TEXTURE_2D, mTextureId);
+
+    // Iterate over all the cache lines and see which ones need to be updated
+    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+        CacheTextureLine* cl = mCacheLines[i];
+        if(cl->mDirty) {
+            uint32_t xOffset = 0;
+            uint32_t yOffset = cl->mCurrentRow;
+            uint32_t width   = mCacheWidth;
+            uint32_t height  = cl->mMaxHeight;
+            void* textureData = mTextTexture + yOffset*width;
+
+            glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
+                    GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
+
+            cl->mDirty = false;
+        }
+    }
+
+    mUploadTexture = false;
+}
+
+void FontRenderer::issueDrawCommand() {
+    checkTextureUpdate();
+
+    float* vtx = mTextMeshPtr;
+    float* tex = vtx + 3;
+
+    // position is slot 0
+    uint32_t slot = 0;
+    glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
+
+    // texture0 is slot 1
+    slot = 1;
+    glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
+
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
+    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
+
+    mDrawn = true;
+}
+
+void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
+        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
+        float x4, float y4, float z4, float u4, float v4) {
+    if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
+        return;
+    }
+
+    const uint32_t vertsPerQuad = 4;
+    const uint32_t floatsPerVert = 5;
+    float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
+
+    (*currentPos++) = x1;
+    (*currentPos++) = y1;
+    (*currentPos++) = z1;
+    (*currentPos++) = u1;
+    (*currentPos++) = v1;
+
+    (*currentPos++) = x2;
+    (*currentPos++) = y2;
+    (*currentPos++) = z2;
+    (*currentPos++) = u2;
+    (*currentPos++) = v2;
+
+    (*currentPos++) = x3;
+    (*currentPos++) = y3;
+    (*currentPos++) = z3;
+    (*currentPos++) = u3;
+    (*currentPos++) = v3;
+
+    (*currentPos++) = x4;
+    (*currentPos++) = y4;
+    (*currentPos++) = z4;
+    (*currentPos++) = u4;
+    (*currentPos++) = v4;
+
+    mCurrentQuadIndex++;
+
+    if (mBounds) {
+        mBounds->left = fmin(mBounds->left, x1);
+        mBounds->top = fmin(mBounds->top, y3);
+        mBounds->right = fmax(mBounds->right, x3);
+        mBounds->bottom = fmax(mBounds->bottom, y1);
+    }
+
+    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
+        issueDrawCommand();
+        mCurrentQuadIndex = 0;
+    }
+}
+
+uint32_t FontRenderer::getRemainingCacheCapacity() {
+    uint32_t remainingCapacity = 0;
+    float totalPixels = 0;
+    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
+         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
+         totalPixels += mCacheLines[i]->mMaxWidth;
+    }
+    remainingCapacity = (remainingCapacity * 100) / totalPixels;
+    return remainingCapacity;
+}
+
+void FontRenderer::precacheLatin(SkPaint* paint) {
+    // Remaining capacity is measured in %
+    uint32_t remainingCapacity = getRemainingCacheCapacity();
+    uint32_t precacheIdx = 0;
+    while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
+        mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]);
+        remainingCapacity = getRemainingCacheCapacity();
+        precacheIdx ++;
+    }
+}
+
+void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
+    uint32_t currentNumFonts = mActiveFonts.size();
+    mCurrentFont = Font::create(this, fontId, fontSize);
+
+    const float maxPrecacheFontSize = 40.0f;
+    bool isNewFont = currentNumFonts != mActiveFonts.size();
+
+    if (isNewFont && fontSize <= maxPrecacheFontSize) {
+        precacheLatin(paint);
+    }
+}
+
+FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
+        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
+    checkInit();
+
+    if (!mCurrentFont) {
+        DropShadow image;
+        image.width = 0;
+        image.height = 0;
+        image.image = NULL;
+        image.penX = 0;
+        image.penY = 0;
+        return image;
+    }
+
+    Rect bounds;
+    mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds);
+    uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
+    uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
+    uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight];
+    for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) {
+        dataBuffer[i] = 0;
+    }
+
+    int penX = radius - bounds.left;
+    int penY = radius - bounds.bottom;
+
+    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY,
+            dataBuffer, paddedWidth, paddedHeight);
+    blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
+
+    DropShadow image;
+    image.width = paddedWidth;
+    image.height = paddedHeight;
+    image.image = dataBuffer;
+    image.penX = penX;
+    image.penY = penY;
+    return image;
+}
+
+bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
+        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
+    checkInit();
+
+    if (!mCurrentFont) {
+        LOGE("No font set");
+        return false;
+    }
+
+    mDrawn = false;
+    mBounds = bounds;
+    mClip = clip;
+    mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
+    mBounds = NULL;
+
+    if (mCurrentQuadIndex != 0) {
+        issueDrawCommand();
+        mCurrentQuadIndex = 0;
+    }
+
+    return mDrawn;
+}
+
+void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
+    // Compute gaussian weights for the blur
+    // e is the euler's number
+    float e = 2.718281828459045f;
+    float pi = 3.1415926535897932f;
+    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
+    // x is of the form [-radius .. 0 .. radius]
+    // and sigma varies with radius.
+    // Based on some experimental radius values and sigma's
+    // we approximately fit sigma = f(radius) as
+    // sigma = radius * 0.3  + 0.6
+    // The larger the radius gets, the more our gaussian blur
+    // will resemble a box blur since with large sigma
+    // the gaussian curve begins to lose its shape
+    float sigma = 0.3f * (float)radius + 0.6f;
+
+    // Now compute the coefficints
+    // We will store some redundant values to save some math during
+    // the blur calculations
+    // precompute some values
+    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
+    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
+
+    float normalizeFactor = 0.0f;
+    for(int32_t r = -radius; r <= radius; r ++) {
+        float floatR = (float) r;
+        weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
+        normalizeFactor += weights[r + radius];
+    }
+
+    //Now we need to normalize the weights because all our coefficients need to add up to one
+    normalizeFactor = 1.0f / normalizeFactor;
+    for(int32_t r = -radius; r <= radius; r ++) {
+        weights[r + radius] *= normalizeFactor;
+    }
+}
+
+void FontRenderer::horizontalBlur(float* weights, int32_t radius,
+        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
+    float blurredPixel = 0.0f;
+    float currentPixel = 0.0f;
+
+    for(int32_t y = 0; y < height; y ++) {
+
+        const uint8_t* input = source + y * width;
+        uint8_t* output = dest + y * width;
+
+        for(int32_t x = 0; x < width; x ++) {
+            blurredPixel = 0.0f;
+            const float* gPtr = weights;
+            // Optimization for non-border pixels
+            if ((x > radius) && (x < (width - radius))) {
+                const uint8_t *i = input + (x - radius);
+                for(int r = -radius; r <= radius; r ++) {
+                    currentPixel = (float) (*i);
+                    blurredPixel += currentPixel * gPtr[0];
+                    gPtr++;
+                    i++;
+                }
+            } else {
+                for(int32_t r = -radius; r <= radius; r ++) {
+                    // Stepping left and right away from the pixel
+                    int validW = x + r;
+                    if(validW < 0) {
+                        validW = 0;
+                    }
+                    if(validW > width - 1) {
+                        validW = width - 1;
+                    }
+
+                    currentPixel = (float)(input[validW]);
+                    blurredPixel += currentPixel * gPtr[0];
+                    gPtr++;
+                }
+            }
+            *output = (uint8_t)blurredPixel;
+            output ++;
+        }
+    }
+}
+
+void FontRenderer::verticalBlur(float* weights, int32_t radius,
+        const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) {
+    float blurredPixel = 0.0f;
+    float currentPixel = 0.0f;
+
+    for(int32_t y = 0; y < height; y ++) {
+
+        uint8_t* output = dest + y * width;
+
+        for(int32_t x = 0; x < width; x ++) {
+            blurredPixel = 0.0f;
+            const float* gPtr = weights;
+            const uint8_t* input = source + x;
+            // Optimization for non-border pixels
+            if ((y > radius) && (y < (height - radius))) {
+                const uint8_t *i = input + ((y - radius) * width);
+                for(int32_t r = -radius; r <= radius; r ++) {
+                    currentPixel = (float)(*i);
+                    blurredPixel += currentPixel * gPtr[0];
+                    gPtr++;
+                    i += width;
+                }
+            } else {
+                for(int32_t r = -radius; r <= radius; r ++) {
+                    int validH = y + r;
+                    // Clamp to zero and width
+                    if(validH < 0) {
+                        validH = 0;
+                    }
+                    if(validH > height - 1) {
+                        validH = height - 1;
+                    }
+
+                    const uint8_t *i = input + validH * width;
+                    currentPixel = (float)(*i);
+                    blurredPixel += currentPixel * gPtr[0];
+                    gPtr++;
+                }
+            }
+            *output = (uint8_t)blurredPixel;
+            output ++;
+        }
+    }
+}
+
+
+void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
+    float *gaussian = new float[2 * radius + 1];
+    computeGaussianWeights(gaussian, radius);
+    uint8_t* scratch = new uint8_t[width * height];
+    horizontalBlur(gaussian, radius, image, scratch, width, height);
+    verticalBlur(gaussian, radius, scratch, image, width, height);
+    delete[] gaussian;
+    delete[] scratch;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
new file mode 100644
index 0000000..a76cb86
--- /dev/null
+++ b/libs/hwui/FontRenderer.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_FONT_RENDERER_H
+#define ANDROID_HWUI_FONT_RENDERER_H
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+
+#include <SkScalerContext.h>
+#include <SkPaint.h>
+
+#include <GLES2/gl2.h>
+
+#include "Rect.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+class FontRenderer;
+
+/**
+ * Represents a font, defined by a Skia font id and a font size. A font is used
+ * to generate glyphs and cache them in the FontState.
+ */
+class Font {
+public:
+    ~Font();
+
+    /**
+     * Renders the specified string of text.
+     * If bitmap is specified, it will be used as the render target
+     */
+    void renderUTF(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+                     int numGlyphs, int x, int y,
+                     uint8_t *bitmap = NULL, uint32_t bitmapW = 0, uint32_t bitmapH = 0);
+    /**
+     * Creates a new font associated with the specified font state.
+     */
+    static Font* create(FontRenderer* state, uint32_t fontId, float fontSize);
+
+protected:
+    friend class FontRenderer;
+
+    enum RenderMode {
+        FRAMEBUFFER,
+        BITMAP,
+        MEASURE,
+    };
+
+    void renderUTF(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+                     int numGlyphs, int x, int y, RenderMode mode,
+                     uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+                     Rect *bounds);
+
+    void measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+                      int numGlyphs, Rect *bounds);
+
+    struct CachedGlyphInfo {
+        // Has the cache been invalidated?
+        bool mIsValid;
+        // Location of the cached glyph in the bitmap
+        // in case we need to resize the texture or
+        // render to bitmap
+        uint32_t mStartX;
+        uint32_t mStartY;
+        uint32_t mBitmapWidth;
+        uint32_t mBitmapHeight;
+        // Also cache texture coords for the quad
+        float mBitmapMinU;
+        float mBitmapMinV;
+        float mBitmapMaxU;
+        float mBitmapMaxV;
+        // Minimize how much we call freetype
+        uint32_t mGlyphIndex;
+        uint32_t mAdvanceX;
+        uint32_t mAdvanceY;
+        // Values below contain a glyph's origin in the bitmap
+        int32_t mBitmapLeft;
+        int32_t mBitmapTop;
+        // Auto-kerning
+        SkFixed mLsbDelta;
+        SkFixed mRsbDelta;
+    };
+
+    Font(FontRenderer* state, uint32_t fontId, float fontSize);
+
+    DefaultKeyedVector<int32_t, CachedGlyphInfo*> mCachedGlyphs;
+
+    void invalidateTextureCache();
+
+    CachedGlyphInfo* cacheGlyph(SkPaint* paint, int32_t glyph);
+    void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph);
+    void measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds);
+    void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y);
+    void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
+                          uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH);
+
+    CachedGlyphInfo* getCachedUTFChar(SkPaint* paint, int32_t utfChar);
+
+    FontRenderer* mState;
+    uint32_t mFontId;
+    float mFontSize;
+};
+
+class FontRenderer {
+public:
+    FontRenderer();
+    ~FontRenderer();
+
+    void init();
+    void deinit();
+
+    void setGammaTable(const uint8_t* gammaTable) {
+        mGammaTable = gammaTable;
+    }
+
+    void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
+    bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
+            uint32_t len, int numGlyphs, int x, int y, Rect* bounds);
+
+    struct DropShadow {
+        DropShadow() { };
+
+        DropShadow(const DropShadow& dropShadow):
+            width(dropShadow.width), height(dropShadow.height),
+            image(dropShadow.image), penX(dropShadow.penX),
+            penY(dropShadow.penY) {
+        }
+
+        uint32_t width;
+        uint32_t height;
+        uint8_t* image;
+        int32_t penX;
+        int32_t penY;
+    };
+
+    // After renderDropShadow returns, the called owns the memory in DropShadow.image
+    // and is responsible for releasing it when it's done with it
+    DropShadow renderDropShadow(SkPaint* paint, const char *text, uint32_t startIndex,
+            uint32_t len, int numGlyphs, uint32_t radius);
+
+    GLuint getTexture(bool linearFiltering = false) {
+        checkInit();
+        if (linearFiltering != mLinearFiltering) {
+            mLinearFiltering = linearFiltering;
+            const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
+
+            glBindTexture(GL_TEXTURE_2D, mTextureId);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+        }
+        return mTextureId;
+    }
+
+protected:
+    friend class Font;
+
+    const uint8_t* mGammaTable;
+
+    struct CacheTextureLine {
+        uint16_t mMaxHeight;
+        uint16_t mMaxWidth;
+        uint32_t mCurrentRow;
+        uint32_t mCurrentCol;
+        bool mDirty;
+
+        CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow,
+                uint32_t currentCol):
+                    mMaxHeight(maxHeight),
+                    mMaxWidth(maxWidth),
+                    mCurrentRow(currentRow),
+                    mCurrentCol(currentCol),
+                    mDirty(false) {
+        }
+
+        bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
+            if (glyph.fHeight + 2 > mMaxHeight) {
+                return false;
+            }
+
+            if (mCurrentCol + glyph.fWidth + 2 < mMaxWidth) {
+                *retOriginX = mCurrentCol + 1;
+                *retOriginY = mCurrentRow + 1;
+                mCurrentCol += glyph.fWidth + 2;
+                mDirty = true;
+                return true;
+            }
+
+            return false;
+        }
+    };
+
+    uint32_t getCacheWidth() const {
+        return mCacheWidth;
+    }
+
+    uint32_t getCacheHeight() const {
+        return mCacheHeight;
+    }
+
+    void initTextTexture();
+    bool cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
+
+    void flushAllAndInvalidate();
+    void initVertexArrayBuffers();
+
+    void checkInit();
+
+    String16 mLatinPrecache;
+    void precacheLatin(SkPaint* paint);
+
+    void issueDrawCommand();
+    void appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, float y2,
+            float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
+            float x4, float y4, float z4, float u4, float v4);
+
+    uint32_t mCacheWidth;
+    uint32_t mCacheHeight;
+
+    Vector<CacheTextureLine*> mCacheLines;
+    uint32_t getRemainingCacheCapacity();
+
+    Font* mCurrentFont;
+    Vector<Font*> mActiveFonts;
+
+    // Texture to cache glyph bitmaps
+    uint8_t* mTextTexture;
+    const uint8_t* getTextTextureData() const {
+        return mTextTexture;
+    }
+    GLuint mTextureId;
+    void checkTextureUpdate();
+    bool mUploadTexture;
+
+    // Pointer to vertex data to speed up frame to frame work
+    float *mTextMeshPtr;
+    uint32_t mCurrentQuadIndex;
+    uint32_t mMaxNumberOfQuads;
+
+    uint32_t mIndexBufferID;
+
+    const Rect* mClip;
+    Rect* mBounds;
+    bool mDrawn;
+
+    bool mInitialized;
+
+    bool mLinearFiltering;
+
+    void computeGaussianWeights(float* weights, int32_t radius);
+    void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
+            int32_t width, int32_t height);
+    void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
+            int32_t width, int32_t height);
+    void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius);
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_FONT_RENDERER_H
diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp
new file mode 100644
index 0000000..6d087e3
--- /dev/null
+++ b/libs/hwui/GammaFontRenderer.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "GammaFontRenderer.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+GammaFontRenderer::GammaFontRenderer() {
+    LOGD("Creating gamma font renderer");
+
+    // Get the renderer properties
+    char property[PROPERTY_VALUE_MAX];
+
+    // Get the gamma
+    float gamma = DEFAULT_TEXT_GAMMA;
+    if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) {
+        LOGD("  Setting text gamma to %s", property);
+        gamma = atof(property);
+    } else {
+        LOGD("  Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA);
+    }
+
+    // Get the black gamma threshold
+    mBlackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
+    if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) {
+        LOGD("  Setting text black gamma threshold to %s", property);
+        mBlackThreshold = atoi(property);
+    } else {
+        LOGD("  Using default text black gamma threshold of %d",
+                DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD);
+    }
+
+    // Get the white gamma threshold
+    mWhiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
+    if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) {
+        LOGD("  Setting text white gamma threshold to %s", property);
+        mWhiteThreshold = atoi(property);
+    } else {
+        LOGD("  Using default white black gamma threshold of %d",
+                DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD);
+    }
+
+    // Compute the gamma tables
+    const float blackGamma = gamma;
+    const float whiteGamma = 1.0f / gamma;
+
+    for (uint32_t i = 0; i <= 255; i++) {
+        mDefault[i] = i;
+
+        const float v = i / 255.0f;
+        const float black = pow(v, blackGamma);
+        const float white = pow(v, whiteGamma);
+
+        mBlackGamma[i] = uint8_t((float)::floor(black * 255.0f + 0.5f));
+        mWhiteGamma[i] = uint8_t((float)::floor(white * 255.0f + 0.5f));
+    }
+
+    // Configure the font renderers
+    mDefaultRenderer.setGammaTable(&mDefault[0]);
+    mBlackGammaRenderer.setGammaTable(&mBlackGamma[0]);
+    mWhiteGammaRenderer.setGammaTable(&mWhiteGamma[0]);
+}
+
+FontRenderer& GammaFontRenderer::getFontRenderer(const SkPaint* paint) {
+    if (paint->getShader() == NULL) {
+        uint32_t c = paint->getColor();
+        const int r = (c >> 16) & 0xFF;
+        const int g = (c >>  8) & 0xFF;
+        const int b = (c      ) & 0xFF;
+        const int luminance = (r * 2 + g * 5 + b) >> 3;
+
+        if (luminance <= mBlackThreshold) {
+            return mBlackGammaRenderer;
+        } else if (luminance >= mWhiteThreshold) {
+            return mWhiteGammaRenderer;
+        }
+    }
+    return mDefaultRenderer;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
new file mode 100644
index 0000000..b59ae81
--- /dev/null
+++ b/libs/hwui/GammaFontRenderer.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_GAMMA_FONT_RENDERER_H
+#define ANDROID_HWUI_GAMMA_FONT_RENDERER_H
+
+#include <SkPaint.h>
+
+#include "FontRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+struct GammaFontRenderer {
+    GammaFontRenderer();
+
+    FontRenderer& getFontRenderer(const SkPaint* paint);
+
+private:
+    FontRenderer mDefaultRenderer;
+    FontRenderer mBlackGammaRenderer;
+    FontRenderer mWhiteGammaRenderer;
+
+    int mBlackThreshold;
+    int mWhiteThreshold;
+
+    uint8_t mDefault[256];
+    uint8_t mBlackGamma[256];
+    uint8_t mWhiteGamma[256];
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_GAMMA_FONT_RENDERER_H
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
new file mode 100644
index 0000000..97f4cb4
--- /dev/null
+++ b/libs/hwui/GradientCache.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+
+#include <SkCanvas.h>
+#include <SkGradientShader.h>
+
+#include <utils/threads.h>
+
+#include "GradientCache.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+GradientCache::GradientCache():
+        mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting gradient cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
+    }
+
+    mCache.setOnEntryRemovedListener(this);
+}
+
+GradientCache::GradientCache(uint32_t maxByteSize):
+        mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(maxByteSize) {
+    mCache.setOnEntryRemovedListener(this);
+}
+
+GradientCache::~GradientCache() {
+    Mutex::Autolock _l(mLock);
+    mCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t GradientCache::getSize() {
+    Mutex::Autolock _l(mLock);
+    return mSize;
+}
+
+uint32_t GradientCache::getMaxSize() {
+    Mutex::Autolock _l(mLock);
+    return mMaxSize;
+}
+
+void GradientCache::setMaxSize(uint32_t maxSize) {
+    Mutex::Autolock _l(mLock);
+    mMaxSize = maxSize;
+    while (mSize > mMaxSize) {
+        mCache.removeOldest();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void GradientCache::operator()(SkShader*& shader, Texture*& texture) {
+    // Already locked here
+    if (shader) {
+        const uint32_t size = texture->width * texture->height * 4;
+        mSize -= size;
+    }
+
+    if (texture) {
+        glDeleteTextures(1, &texture->id);
+        delete texture;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+Texture* GradientCache::get(SkShader* shader) {
+    Mutex::Autolock _l(mLock);
+    return mCache.get(shader);
+}
+
+void GradientCache::remove(SkShader* shader) {
+    Mutex::Autolock _l(mLock);
+    mCache.remove(shader);
+}
+
+void GradientCache::clear() {
+    Mutex::Autolock _l(mLock);
+    mCache.clear();
+}
+
+Texture* GradientCache::addLinearGradient(SkShader* shader, uint32_t* colors,
+        float* positions, int count, SkShader::TileMode tileMode) {
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1024, 1);
+    bitmap.allocPixels();
+    bitmap.eraseColor(0);
+
+    SkCanvas canvas(bitmap);
+
+    SkPoint points[2];
+    points[0].set(0.0f, 0.0f);
+    points[1].set(bitmap.width(), 0.0f);
+
+    SkShader* localShader = SkGradientShader::CreateLinear(points,
+            reinterpret_cast<const SkColor*>(colors), positions, count, tileMode);
+
+    SkPaint p;
+    p.setStyle(SkPaint::kStrokeAndFill_Style);
+    p.setShader(localShader)->unref();
+
+    canvas.drawRectCoords(0.0f, 0.0f, bitmap.width(), 1.0f, p);
+
+    mLock.lock();
+    // Asume the cache is always big enough
+    const uint32_t size = bitmap.rowBytes() * bitmap.height();
+    while (mSize + size > mMaxSize) {
+        mCache.removeOldest();
+    }
+    mLock.unlock();
+
+    Texture* texture = new Texture;
+    generateTexture(&bitmap, texture);
+
+    mLock.lock();
+    mSize += size;
+    mCache.put(shader, texture);
+    mLock.unlock();
+
+    return texture;
+}
+
+void GradientCache::generateTexture(SkBitmap* bitmap, Texture* texture) {
+    SkAutoLockPixels autoLock(*bitmap);
+    if (!bitmap->readyToDraw()) {
+        LOGE("Cannot generate texture from shader");
+        return;
+    }
+
+    texture->generation = bitmap->getGenerationID();
+    texture->width = bitmap->width();
+    texture->height = bitmap->height();
+
+    glGenTextures(1, &texture->id);
+
+    glBindTexture(GL_TEXTURE_2D, texture->id);
+    glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
+
+    texture->blend = !bitmap->isOpaque();
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0,
+            GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
new file mode 100644
index 0000000..c9553f4
--- /dev/null
+++ b/libs/hwui/GradientCache.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_GRADIENT_CACHE_H
+#define ANDROID_HWUI_GRADIENT_CACHE_H
+
+#include <SkShader.h>
+
+#include "Texture.h"
+#include "utils/GenerationCache.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A simple LRU gradient cache. The cache has a maximum size expressed in bytes.
+ * Any texture added to the cache causing the cache to grow beyond the maximum
+ * allowed size will also cause the oldest texture to be kicked out.
+ */
+class GradientCache: public OnEntryRemoved<SkShader*, Texture*> {
+public:
+    GradientCache();
+    GradientCache(uint32_t maxByteSize);
+    ~GradientCache();
+
+    /**
+     * Used as a callback when an entry is removed from the cache.
+     * Do not invoke directly.
+     */
+    void operator()(SkShader*& shader, Texture*& texture);
+
+    /**
+     * Adds a new linear gradient to the cache. The generated texture is
+     * returned.
+     */
+    Texture* addLinearGradient(SkShader* shader, uint32_t* colors, float* positions,
+            int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode);
+    /**
+     * Returns the texture associated with the specified shader.
+     */
+    Texture* get(SkShader* shader);
+    /**
+     * Removes the texture associated with the specified shader. Returns NULL
+     * if the texture cannot be found. Upon remove the texture is freed.
+     */
+    void remove(SkShader* shader);
+    /**
+     * Clears the cache. This causes all textures to be deleted.
+     */
+    void clear();
+
+    /**
+     * Sets the maximum size of the cache in bytes.
+     */
+    void setMaxSize(uint32_t maxSize);
+    /**
+     * Returns the maximum size of the cache in bytes.
+     */
+    uint32_t getMaxSize();
+    /**
+     * Returns the current size of the cache in bytes.
+     */
+    uint32_t getSize();
+
+private:
+    void generateTexture(SkBitmap* bitmap, Texture* texture);
+
+    GenerationCache<SkShader*, Texture*> mCache;
+
+    uint32_t mSize;
+    uint32_t mMaxSize;
+
+    /**
+     * Used to access mCache and mSize. All methods are accessed from a single
+     * thread except for remove().
+     */
+    mutable Mutex mLock;
+}; // class GradientCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_GRADIENT_CACHE_H
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
new file mode 100644
index 0000000..a780183
--- /dev/null
+++ b/libs/hwui/Layer.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_LAYER_H
+#define ANDROID_HWUI_LAYER_H
+
+#include <sys/types.h>
+
+#include <GLES2/gl2.h>
+
+#include <ui/Region.h>
+
+#include <SkXfermode.h>
+
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Layers
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * A layer has dimensions and is backed by an OpenGL texture or FBO.
+ */
+struct Layer {
+    Layer(const uint32_t layerWidth, const uint32_t layerHeight):
+            width(layerWidth), height(layerHeight) {
+    }
+
+    /**
+     * Bounds of the layer.
+     */
+    Rect layer;
+    /**
+     * Texture coordinates of the layer.
+     */
+    Rect texCoords;
+
+    /**
+     * Name of the FBO used to render the layer. If the name is 0
+     * this layer is not backed by an FBO, but a simple texture.
+     */
+    GLuint fbo;
+
+    /**
+     * Opacity of the layer.
+     */
+    int alpha;
+    /**
+     * Blending mode of the layer.
+     */
+    SkXfermode::Mode mode;
+    /**
+     * Indicates whether this layer should be blended.
+     */
+    bool blend;
+
+    /**
+     * Indicates whether this layer has been used already.
+     */
+    bool empty;
+
+    /**
+     * Name of the texture used to render the layer.
+     */
+    GLuint texture;
+    /**
+     * Width of the layer texture.
+     */
+    uint32_t width;
+    /**
+     * Height of the layer texture.
+     */
+    uint32_t height;
+
+    /**
+     * Dirty region indicating what parts of the layer
+     * have been drawn.
+     */
+    Region region;
+}; // struct Layer
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_LAYER_H
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
new file mode 100644
index 0000000..9ce0359
--- /dev/null
+++ b/libs/hwui/LayerCache.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+
+#include <utils/Log.h>
+
+#include "LayerCache.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+LayerCache::LayerCache(): mSize(0), mMaxSize(MB(DEFAULT_LAYER_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting layer cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE);
+    }
+}
+
+LayerCache::~LayerCache() {
+    clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t LayerCache::getSize() {
+    return mSize;
+}
+
+uint32_t LayerCache::getMaxSize() {
+    return mMaxSize;
+}
+
+void LayerCache::setMaxSize(uint32_t maxSize) {
+    clear();
+    mMaxSize = maxSize;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void LayerCache::deleteLayer(Layer* layer) {
+    if (layer) {
+        mSize -= layer->width * layer->height * 4;
+        glDeleteTextures(1, &layer->texture);
+        delete layer;
+    }
+}
+
+void LayerCache::clear() {
+    size_t count = mCache.size();
+    for (size_t i = 0; i < count; i++) {
+        deleteLayer(mCache.itemAt(i).mLayer);
+    }
+    mCache.clear();
+}
+
+Layer* LayerCache::get(const uint32_t width, const uint32_t height) {
+    Layer* layer = NULL;
+
+    LayerEntry entry(width, height);
+    ssize_t index = mCache.indexOf(entry);
+
+    if (index >= 0) {
+        entry = mCache.itemAt(index);
+        mCache.removeAt(index);
+
+        layer = entry.mLayer;
+        mSize -= layer->width * layer->height * 4;
+
+        LAYER_LOGD("Reusing layer %dx%d", layer->width, layer->height);
+    } else {
+        LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight);
+
+        layer = new Layer(entry.mWidth, entry.mHeight);
+        layer->blend = true;
+        layer->empty = true;
+        layer->fbo = 0;
+
+        glGenTextures(1, &layer->texture);
+        glBindTexture(GL_TEXTURE_2D, layer->texture);
+
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+#if DEBUG_LAYERS
+        size_t size = mCache.size();
+        for (size_t i = 0; i < size; i++) {
+            const LayerEntry& entry = mCache.itemAt(i);
+            LAYER_LOGD("  Layer size %dx%d", entry.mWidth, entry.mHeight);
+        }
+#endif
+    }
+
+    return layer;
+}
+
+bool LayerCache::put(Layer* layer) {
+    const uint32_t size = layer->width * layer->height * 4;
+    // Don't even try to cache a layer that's bigger than the cache
+    if (size < mMaxSize) {
+        // TODO: Use an LRU
+        while (mSize + size > mMaxSize) {
+            size_t position = 0;
+#if LAYER_REMOVE_BIGGEST
+            position = mCache.size() - 1;
+#endif
+            Layer* victim = mCache.itemAt(position).mLayer;
+            deleteLayer(victim);
+            mCache.removeAt(position);
+
+            LAYER_LOGD("  Deleting layer %.2fx%.2f", victim->layer.getWidth(),
+                    victim->layer.getHeight());
+        }
+
+        LayerEntry entry(layer);
+
+        mCache.add(entry);
+        mSize += size;
+
+        return true;
+    }
+    return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
new file mode 100644
index 0000000..4df8ab3
--- /dev/null
+++ b/libs/hwui/LayerCache.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_LAYER_CACHE_H
+#define ANDROID_HWUI_LAYER_CACHE_H
+
+#include "Layer.h"
+#include "utils/SortedList.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_LAYERS 0
+
+// Indicates whether to remove the biggest layers first, or the smaller ones
+#define LAYER_REMOVE_BIGGEST 0
+// Textures used by layers must have dimensions multiples of this number
+#define LAYER_SIZE 64
+
+// Debug
+#if DEBUG_LAYERS
+    #define LAYER_LOGD(...) LOGD(__VA_ARGS__)
+#else
+    #define LAYER_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
+class LayerCache {
+public:
+    LayerCache();
+    ~LayerCache();
+
+    /**
+     * Returns a layer large enough for the specified dimensions. If no suitable
+     * layer can be found, a new one is created and returned. If creating a new
+     * layer fails, NULL is returned.
+     *
+     * When a layer is obtained from the cache, it is removed and the total
+     * size of the cache goes down.
+     *
+     * @param width The desired width of the layer
+     * @param width The desired height of the layer
+     */
+    Layer* get(const uint32_t width, const uint32_t height);
+
+    /**
+     * Adds the layer to the cache. The layer will not be added if there is
+     * not enough space available. Adding a layer can cause other layers to
+     * be removed from the cache.
+     *
+     * @param layer The layer to add to the cache
+     *
+     * @return True if the layer was added, false otherwise.
+     */
+    bool put(Layer* layer);
+    /**
+     * Clears the cache. This causes all layers to be deleted.
+     */
+    void clear();
+
+    /**
+     * Sets the maximum size of the cache in bytes.
+     */
+    void setMaxSize(uint32_t maxSize);
+    /**
+     * Returns the maximum size of the cache in bytes.
+     */
+    uint32_t getMaxSize();
+    /**
+     * Returns the current size of the cache in bytes.
+     */
+    uint32_t getSize();
+
+private:
+    void deleteLayer(Layer* layer);
+
+    struct LayerEntry {
+        LayerEntry():
+            mLayer(NULL), mWidth(0), mHeight(0) {
+        }
+
+        LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(NULL) {
+            mWidth = uint32_t(ceilf(layerWidth / float(LAYER_SIZE)) * LAYER_SIZE);
+            mHeight = uint32_t(ceilf(layerHeight / float(LAYER_SIZE)) * LAYER_SIZE);
+        }
+
+        LayerEntry(const LayerEntry& entry):
+            mLayer(entry.mLayer), mWidth(entry.mWidth), mHeight(entry.mHeight) {
+        }
+
+        LayerEntry(Layer* layer):
+            mLayer(layer), mWidth(layer->width), mHeight(layer->height) {
+        }
+
+        bool operator<(const LayerEntry& rhs) const {
+            if (mWidth == rhs.mWidth) {
+                return mHeight < rhs.mHeight;
+            }
+            return mWidth < rhs.mWidth;
+        }
+
+        bool operator==(const LayerEntry& rhs) const {
+            return mWidth == rhs.mWidth && mHeight == rhs.mHeight;
+        }
+
+        Layer* mLayer;
+        uint32_t mWidth;
+        uint32_t mHeight;
+    }; // struct LayerEntry
+
+    SortedList<LayerEntry> mCache;
+
+    uint32_t mSize;
+    uint32_t mMaxSize;
+}; // class LayerCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_LAYER_CACHE_H
diff --git a/libs/hwui/Line.h b/libs/hwui/Line.h
new file mode 100644
index 0000000..5c6f3d8
--- /dev/null
+++ b/libs/hwui/Line.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_LINE_H
+#define ANDROID_HWUI_LINE_H
+
+#include <GLES2/gl2.h>
+
+#include <cmath>
+
+#include <sys/types.h>
+
+#include "Patch.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Globals
+///////////////////////////////////////////////////////////////////////////////
+
+// Alpha8 texture used to perform texture anti-aliasing
+static const uint8_t gLineTexture[] = {
+        0,   0,   0,   0, 0,
+        0, 255, 255, 255, 0,
+        0, 255, 255, 255, 0,
+        0, 255, 255, 255, 0,
+        0,   0,   0,   0, 0
+};
+static const GLsizei gLineTextureWidth = 5;
+static const GLsizei gLineTextureHeight = 5;
+static const float gLineAABias = 1.0f;
+
+///////////////////////////////////////////////////////////////////////////////
+// Line
+///////////////////////////////////////////////////////////////////////////////
+
+class Line {
+public:
+    Line(): mXDivsCount(2), mYDivsCount(2) {
+        mPatch = new Patch(mXDivsCount, mYDivsCount);
+        mXDivs = new int32_t[mXDivsCount];
+        mYDivs = new int32_t[mYDivsCount];
+
+        mXDivs[0] = mYDivs[0] = 2;
+        mXDivs[1] = mYDivs[1] = 3;
+
+        glGenTextures(1, &mTexture);
+        glBindTexture(GL_TEXTURE_2D, mTexture);
+
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, gLineTextureWidth, gLineTextureHeight, 0,
+                GL_ALPHA, GL_UNSIGNED_BYTE, gLineTexture);
+    }
+
+    ~Line() {
+        delete mPatch;
+        delete[] mXDivs;
+        delete[] mYDivs;
+
+        glDeleteTextures(1, &mTexture);
+    }
+
+    inline float getLength(float x1, float y1, float x2, float y2) {
+        return sqrtf((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
+    }
+
+    void update(float x1, float y1, float x2, float y2, float lineWidth, float& tx, float& ty) {
+        const float length = getLength(x1, y1, x2, y2);
+        const float half = lineWidth * 0.5f;
+
+        mPatch->updateVertices(gLineTextureWidth, gLineTextureHeight,
+                -gLineAABias, -half - gLineAABias, length + gLineAABias, half + gLineAABias,
+                mXDivs, mYDivs, mXDivsCount, mYDivsCount);
+
+        tx = -gLineAABias;
+        ty = lineWidth <= 1.0f ? -gLineAABias : -half - gLineAABias;
+    }
+
+    inline GLuint getMeshBuffer() const {
+        return mPatch->meshBuffer;
+    }
+
+    inline GLsizei getElementsCount() const {
+        return mPatch->verticesCount;
+    }
+
+    inline GLuint getTexture() const {
+        return mTexture;
+    }
+
+private:
+    uint32_t mXDivsCount;
+    uint32_t mYDivsCount;
+
+    int32_t* mXDivs;
+    int32_t* mYDivs;
+
+    Patch* mPatch;
+
+    GLuint mTexture;
+}; // class Line
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_LINE_H
diff --git a/libs/hwui/MODULE_LICENSE_APACHE2 b/libs/hwui/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/hwui/MODULE_LICENSE_APACHE2
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
new file mode 100644
index 0000000..7462d5b
--- /dev/null
+++ b/libs/hwui/Matrix.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/Log.h>
+
+#include <SkMatrix.h>
+
+#include "utils/Compare.h"
+#include "Matrix.h"
+
+namespace android {
+namespace uirenderer {
+
+void Matrix4::loadIdentity() {
+    data[kScaleX]       = 1.0f;
+    data[kSkewY]        = 0.0f;
+    data[2]             = 0.0f;
+    data[kPerspective0] = 0.0f;
+
+    data[kSkewX]        = 0.0f;
+    data[kScaleY]       = 1.0f;
+    data[6]             = 0.0f;
+    data[kPerspective1] = 0.0f;
+
+    data[8]             = 0.0f;
+    data[9]             = 0.0f;
+    data[kScaleZ]       = 1.0f;
+    data[11]            = 0.0f;
+
+    data[kTranslateX]   = 0.0f;
+    data[kTranslateY]   = 0.0f;
+    data[kTranslateZ]   = 0.0f;
+    data[kPerspective2] = 1.0f;
+
+    mSimpleMatrix = true;
+}
+
+bool Matrix4::changesBounds() {
+    return !(ALMOST_EQUAL(data[0], 1.0f) && ALMOST_EQUAL(data[1], 0.0f) &&
+             ALMOST_EQUAL(data[2], 0.0f) && ALMOST_EQUAL(data[4], 0.0f) &&
+             ALMOST_EQUAL(data[5], 1.0f) && ALMOST_EQUAL(data[6], 0.0f) &&
+             ALMOST_EQUAL(data[8], 0.0f) && ALMOST_EQUAL(data[9], 0.0f) &&
+             ALMOST_EQUAL(data[10], 1.0f));
+}
+
+void Matrix4::load(const float* v) {
+    memcpy(data, v, sizeof(data));
+    mSimpleMatrix = false;
+}
+
+void Matrix4::load(const Matrix4& v) {
+    memcpy(data, v.data, sizeof(data));
+    mSimpleMatrix = v.mSimpleMatrix;
+}
+
+void Matrix4::load(const SkMatrix& v) {
+    memset(data, 0, sizeof(data));
+
+    data[kScaleX]     = v[SkMatrix::kMScaleX];
+    data[kSkewX]      = v[SkMatrix::kMSkewX];
+    data[kTranslateX] = v[SkMatrix::kMTransX];
+
+    data[kSkewY]      = v[SkMatrix::kMSkewY];
+    data[kScaleY]     = v[SkMatrix::kMScaleY];
+    data[kTranslateY] = v[SkMatrix::kMTransY];
+
+    data[kPerspective0]  = v[SkMatrix::kMPersp0];
+    data[kPerspective1]  = v[SkMatrix::kMPersp1];
+    data[kPerspective2]  = v[SkMatrix::kMPersp2];
+
+    data[kScaleZ] = 1.0f;
+
+    mSimpleMatrix = (v.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask));
+}
+
+void Matrix4::copyTo(SkMatrix& v) const {
+    v.reset();
+
+    v.set(SkMatrix::kMScaleX, data[kScaleX]);
+    v.set(SkMatrix::kMSkewX,  data[kSkewX]);
+    v.set(SkMatrix::kMTransX, data[kTranslateX]);
+
+    v.set(SkMatrix::kMSkewY,  data[kSkewY]);
+    v.set(SkMatrix::kMScaleY, data[kScaleY]);
+    v.set(SkMatrix::kMTransY, data[kTranslateY]);
+
+    v.set(SkMatrix::kMPersp0, data[kPerspective0]);
+    v.set(SkMatrix::kMPersp1, data[kPerspective1]);
+    v.set(SkMatrix::kMPersp2, data[kPerspective2]);
+}
+
+void Matrix4::loadInverse(const Matrix4& v) {
+    double scale = 1.0 /
+            (v.data[kScaleX] * ((double) v.data[kScaleY]  * v.data[kPerspective2] -
+                    (double) v.data[kTranslateY] * v.data[kPerspective1]) +
+             v.data[kSkewX] * ((double) v.data[kTranslateY] * v.data[kPerspective0] -
+                     (double) v.data[kSkewY] * v.data[kPerspective2]) +
+             v.data[kTranslateX] * ((double) v.data[kSkewY] * v.data[kPerspective1] -
+                     (double) v.data[kScaleY] * v.data[kPerspective0]));
+
+    data[kScaleX] = (v.data[kScaleY] * v.data[kPerspective2] -
+            v.data[kTranslateY] * v.data[kPerspective1])  * scale;
+    data[kSkewX] = (v.data[kTranslateX] * v.data[kPerspective1] -
+            v.data[kSkewX]  * v.data[kPerspective2]) * scale;
+    data[kTranslateX] = (v.data[kSkewX] * v.data[kTranslateY] -
+            v.data[kTranslateX] * v.data[kScaleY])  * scale;
+
+    data[kSkewY] = (v.data[kTranslateY] * v.data[kPerspective0] -
+            v.data[kSkewY]  * v.data[kPerspective2]) * scale;
+    data[kScaleY] = (v.data[kScaleX] * v.data[kPerspective2] -
+            v.data[kTranslateX] * v.data[kPerspective0])  * scale;
+    data[kTranslateY] = (v.data[kTranslateX] * v.data[kSkewY] -
+            v.data[kScaleX]  * v.data[kTranslateY]) * scale;
+
+    data[kPerspective0] = (v.data[kSkewY] * v.data[kPerspective1] -
+            v.data[kScaleY] * v.data[kPerspective0]) * scale;
+    data[kPerspective1] = (v.data[kSkewX] * v.data[kPerspective0] -
+            v.data[kScaleX] * v.data[kPerspective1]) * scale;
+    data[kPerspective2] = (v.data[kScaleX] * v.data[kScaleY] -
+            v.data[kSkewX] * v.data[kSkewY]) * scale;
+
+    mSimpleMatrix = v.mSimpleMatrix;
+}
+
+void Matrix4::copyTo(float* v) const {
+    memcpy(v, data, sizeof(data));
+}
+
+float Matrix4::getTranslateX() {
+    return data[kTranslateX];
+}
+
+float Matrix4::getTranslateY() {
+    return data[kTranslateY];
+}
+
+void Matrix4::multiply(float v) {
+    for (int i = 0; i < 16; i++) {
+        data[i] *= v;
+    }
+}
+
+void Matrix4::loadTranslate(float x, float y, float z) {
+    loadIdentity();
+    data[kTranslateX] = x;
+    data[kTranslateY] = y;
+    data[kTranslateZ] = z;
+}
+
+void Matrix4::loadScale(float sx, float sy, float sz) {
+    loadIdentity();
+    data[kScaleX] = sx;
+    data[kScaleY] = sy;
+    data[kScaleZ] = sz;
+}
+
+void Matrix4::loadRotate(float angle, float x, float y, float z) {
+    data[kPerspective0]  = 0.0f;
+    data[kPerspective1]  = 0.0f;
+    data[11]             = 0.0f;
+    data[kTranslateX]    = 0.0f;
+    data[kTranslateY]    = 0.0f;
+    data[kTranslateZ]    = 0.0f;
+    data[kPerspective2]  = 1.0f;
+
+    angle *= float(M_PI / 180.0f);
+    float c = cosf(angle);
+    float s = sinf(angle);
+
+    const float length = sqrtf(x * x + y * y + z * z);
+    float recipLen = 1.0f / length;
+    x *= recipLen;
+    y *= recipLen;
+    z *= recipLen;
+
+    const float nc = 1.0f - c;
+    const float xy = x * y;
+    const float yz = y * z;
+    const float zx = z * x;
+    const float xs = x * s;
+    const float ys = y * s;
+    const float zs = z * s;
+
+    data[kScaleX] = x * x * nc +  c;
+    data[kSkewX]  =    xy * nc - zs;
+    data[8]       =    zx * nc + ys;
+    data[kSkewY]  =    xy * nc + zs;
+    data[kScaleY] = y * y * nc +  c;
+    data[9]       =    yz * nc - xs;
+    data[2]       =    zx * nc - ys;
+    data[6]       =    yz * nc + xs;
+    data[kScaleZ] = z * z * nc +  c;
+
+    mSimpleMatrix = false;
+}
+
+void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) {
+    for (int i = 0 ; i < 4 ; i++) {
+        float x = 0;
+        float y = 0;
+        float z = 0;
+        float w = 0;
+
+        for (int j = 0 ; j < 4 ; j++) {
+            const float e = v.get(i, j);
+            x += u.get(j, 0) * e;
+            y += u.get(j, 1) * e;
+            z += u.get(j, 2) * e;
+            w += u.get(j, 3) * e;
+        }
+
+        set(i, 0, x);
+        set(i, 1, y);
+        set(i, 2, z);
+        set(i, 3, w);
+    }
+
+    mSimpleMatrix = u.mSimpleMatrix && v.mSimpleMatrix;
+}
+
+void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) {
+    loadIdentity();
+    data[kScaleX] = 2.0f / (right - left);
+    data[kScaleY] = 2.0f / (top - bottom);
+    data[kScaleZ] = -2.0f / (far - near);
+    data[kTranslateX] = -(right + left) / (right - left);
+    data[kTranslateY] = -(top + bottom) / (top - bottom);
+    data[kTranslateZ] = -(far + near) / (far - near);
+}
+
+#define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c)
+
+void Matrix4::mapPoint(float& x, float& y) const {
+    if (mSimpleMatrix) {
+        MUL_ADD_STORE(x, data[kScaleX], data[kTranslateX]);
+        MUL_ADD_STORE(y, data[kScaleY], data[kTranslateY]);
+        return;
+    }
+
+    float dx = x * data[kScaleX] + y * data[kSkewX] + data[kTranslateX];
+    float dy = x * data[kSkewY] + y * data[kScaleY] + data[kTranslateY];
+    float dz = x * data[kPerspective0] + y * data[kPerspective1] + data[kPerspective2];
+    if (dz) dz = 1.0f / dz;
+
+    x = dx * dz;
+    y = dy * dz;
+}
+
+void Matrix4::mapRect(Rect& r) const {
+    if (mSimpleMatrix) {
+        MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]);
+        MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]);
+        MUL_ADD_STORE(r.top, data[kScaleY], data[kTranslateY]);
+        MUL_ADD_STORE(r.bottom, data[kScaleY], data[kTranslateY]);
+
+        if (r.left > r.right) {
+            float x = r.left;
+            r.left = r.right;
+            r.right = x;
+        }
+
+        if (r.top > r.bottom) {
+            float y = r.top;
+            r.top = r.bottom;
+            r.bottom = y;
+        }
+
+        return;
+    }
+
+    float vertices[] = {
+        r.left, r.top,
+        r.right, r.top,
+        r.right, r.bottom,
+        r.left, r.bottom
+    };
+
+    float x, y, z;
+
+    for (int i = 0; i < 8; i+= 2) {
+        float px = vertices[i];
+        float py = vertices[i + 1];
+
+        x = px * data[kScaleX] + py * data[kSkewX] + data[kTranslateX];
+        y = px * data[kSkewY] + py * data[kScaleY] + data[kTranslateY];
+        z = px * data[kPerspective0] + py * data[kPerspective1] + data[kPerspective2];
+        if (z) z = 1.0f / z;
+
+        vertices[i] = x * z;
+        vertices[i + 1] = y * z;
+    }
+
+    r.left = r.right = vertices[0];
+    r.top = r.bottom = vertices[1];
+
+    for (int i = 2; i < 8; i += 2) {
+        x = vertices[i];
+        y = vertices[i + 1];
+
+        if (x < r.left) r.left = x;
+        else if (x > r.right) r.right = x;
+        if (y < r.top) r.top = y;
+        else if (y > r.bottom) r.bottom = y;
+    }
+}
+
+void Matrix4::dump() const {
+    LOGD("Matrix4[simple=%d", mSimpleMatrix);
+    LOGD("  %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]);
+    LOGD("  %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]);
+    LOGD("  %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]);
+    LOGD("  %f %f %f %f", data[kPerspective0], data[kPerspective1], data[11], data[kPerspective2]);
+    LOGD("]");
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
new file mode 100644
index 0000000..d678bd0
--- /dev/null
+++ b/libs/hwui/Matrix.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_MATRIX_H
+#define ANDROID_HWUI_MATRIX_H
+
+#include <SkMatrix.h>
+
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes
+///////////////////////////////////////////////////////////////////////////////
+
+class Matrix4 {
+public:
+    float data[16];
+
+    enum Entry {
+        kScaleX = 0,
+        kSkewY = 1,
+        kPerspective0 = 3,
+        kSkewX = 4,
+        kScaleY = 5,
+        kPerspective1 = 7,
+        kScaleZ = 10,
+        kTranslateX = 12,
+        kTranslateY = 13,
+        kTranslateZ = 14,
+        kPerspective2 = 15
+    };
+
+    Matrix4() {
+        loadIdentity();
+    }
+
+    Matrix4(const float* v) {
+        load(v);
+    }
+
+    Matrix4(const Matrix4& v) {
+        load(v);
+    }
+
+    Matrix4(const SkMatrix& v) {
+        load(v);
+    }
+
+    void loadIdentity();
+
+    void load(const float* v);
+    void load(const Matrix4& v);
+    void load(const SkMatrix& v);
+
+    void loadInverse(const Matrix4& v);
+
+    void loadTranslate(float x, float y, float z);
+    void loadScale(float sx, float sy, float sz);
+    void loadRotate(float angle, float x, float y, float z);
+    void loadMultiply(const Matrix4& u, const Matrix4& v);
+
+    void loadOrtho(float left, float right, float bottom, float top, float near, float far);
+
+    void multiply(const Matrix4& v) {
+        Matrix4 u;
+        u.loadMultiply(*this, v);
+        load(u);
+    }
+
+    void multiply(float v);
+
+    void translate(float x, float y, float z) {
+        Matrix4 u;
+        u.loadTranslate(x, y, z);
+        multiply(u);
+    }
+
+    void scale(float sx, float sy, float sz) {
+        Matrix4 u;
+        u.loadScale(sx, sy, sz);
+        multiply(u);
+    }
+
+    void rotate(float angle, float x, float y, float z) {
+        Matrix4 u;
+        u.loadRotate(angle, x, y, z);
+        multiply(u);
+    }
+
+    bool changesBounds();
+
+    void copyTo(float* v) const;
+    void copyTo(SkMatrix& v) const;
+
+    void mapRect(Rect& r) const;
+    void mapPoint(float& x, float& y) const;
+
+    float getTranslateX();
+    float getTranslateY();
+
+    void dump() const;
+
+private:
+    bool mSimpleMatrix;
+
+    inline float get(int i, int j) const {
+        return data[i * 4 + j];
+    }
+
+    inline void set(int i, int j, float v) {
+        data[i * 4 + j] = v;
+    }
+}; // class Matrix4
+
+///////////////////////////////////////////////////////////////////////////////
+// Types
+///////////////////////////////////////////////////////////////////////////////
+
+typedef Matrix4 mat4;
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_MATRIX_H
diff --git a/libs/hwui/NOTICE b/libs/hwui/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/hwui/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-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.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libs/hwui/OpenGLDebugRenderer.cpp b/libs/hwui/OpenGLDebugRenderer.cpp
new file mode 100644
index 0000000..fe75ca2
--- /dev/null
+++ b/libs/hwui/OpenGLDebugRenderer.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/StopWatch.h>
+
+#include "OpenGLDebugRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+void OpenGLDebugRenderer::prepare(bool opaque) {
+    mPrimitivesCount = 0;
+    LOGD("========= Frame start =========");
+    OpenGLRenderer::prepare(opaque);
+}
+
+void OpenGLDebugRenderer::finish() {
+    LOGD("========= Frame end =========");
+    LOGD("Primitives draw count = %d", mPrimitivesCount);
+    OpenGLRenderer::finish();
+}
+
+void OpenGLDebugRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
+    mPrimitivesCount++;
+    StopWatch w("composeLayer");
+    return OpenGLRenderer::composeLayer(current, previous);
+}
+
+int OpenGLDebugRenderer::saveLayer(float left, float top, float right, float bottom,
+        SkPaint* p, int flags) {
+    mPrimitivesCount++;
+    StopWatch w("saveLayer");
+    return OpenGLRenderer::saveLayer(left, top, right, bottom, p, flags);
+}
+
+void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, float left, float top,
+        SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawBitmap");
+    OpenGLRenderer::drawBitmap(bitmap, left, top, paint);
+}
+
+void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix,
+        SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawBitmapMatrix");
+    OpenGLRenderer::drawBitmap(bitmap, matrix, paint);
+}
+
+void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
+        float srcRight, float srcBottom, float dstLeft, float dstTop,
+        float dstRight, float dstBottom, SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawBitmapRect");
+    OpenGLRenderer::drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
+            dstLeft, dstTop, dstRight, dstBottom, paint);
+}
+
+void OpenGLDebugRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
+        const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+        float left, float top, float right, float bottom, SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawPatch");
+    OpenGLRenderer::drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors,
+            left, top, right, bottom, paint);
+}
+
+void OpenGLDebugRenderer::drawColor(int color, SkXfermode::Mode mode) {
+    mPrimitivesCount++;
+    StopWatch w("drawColor");
+    OpenGLRenderer::drawColor(color, mode);
+}
+
+void OpenGLDebugRenderer::drawRect(float left, float top, float right, float bottom,
+        SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawRect");
+    OpenGLRenderer::drawRect(left, top, right, bottom, paint);
+}
+
+void OpenGLDebugRenderer::drawPath(SkPath* path, SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawPath");
+    OpenGLRenderer::drawPath(path, paint);
+}
+
+void OpenGLDebugRenderer::drawLines(float* points, int count, SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawLines");
+    OpenGLRenderer::drawLines(points, count, paint);
+}
+
+void OpenGLDebugRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
+        SkPaint* paint) {
+    mPrimitivesCount++;
+    StopWatch w("drawText");
+    OpenGLRenderer::drawText(text, bytesCount, count, x, y, paint);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/OpenGLDebugRenderer.h b/libs/hwui/OpenGLDebugRenderer.h
new file mode 100644
index 0000000..7787ff1
--- /dev/null
+++ b/libs/hwui/OpenGLDebugRenderer.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_OPENGL_DEBUG_RENDERER_H
+#define ANDROID_HWUI_OPENGL_DEBUG_RENDERER_H
+
+#include "OpenGLRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Renderer
+///////////////////////////////////////////////////////////////////////////////
+
+class OpenGLDebugRenderer: public OpenGLRenderer {
+public:
+    OpenGLDebugRenderer(): mPrimitivesCount(0) {
+    }
+
+    ~OpenGLDebugRenderer() {
+    }
+
+    void prepare(bool opaque);
+    void finish();
+
+    int saveLayer(float left, float top, float right, float bottom,
+            SkPaint* p, int flags);
+
+    void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
+    void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
+    void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, SkPaint* paint);
+    void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
+            const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+            float left, float top, float right, float bottom, SkPaint* paint);
+    void drawColor(int color, SkXfermode::Mode mode);
+    void drawRect(float left, float top, float right, float bottom, SkPaint* paint);
+    void drawPath(SkPath* path, SkPaint* paint);
+    void drawLines(float* points, int count, SkPaint* paint);
+    void drawText(const char* text, int bytesCount, int count, float x, float y,
+            SkPaint* paint);
+
+protected:
+    void composeLayer(sp<Snapshot> current, sp<Snapshot> previous);
+
+private:
+    uint32_t mPrimitivesCount;
+
+}; // class OpenGLDebugRenderer
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_OPENGL_DEBUG_RENDERER_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
new file mode 100644
index 0000000..cad991d
--- /dev/null
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -0,0 +1,1650 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <SkCanvas.h>
+#include <SkTypeface.h>
+
+#include <utils/Log.h>
+#include <utils/StopWatch.h>
+
+#include <ui/Rect.h>
+
+#include "OpenGLRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define RAD_TO_DEG (180.0f / 3.14159265f)
+#define MIN_ANGLE 0.001f
+
+// TODO: This should be set in properties
+#define ALPHA_THRESHOLD (0x7f / PANEL_BIT_DEPTH)
+
+///////////////////////////////////////////////////////////////////////////////
+// Globals
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Structure mapping Skia xfermodes to OpenGL blending factors.
+ */
+struct Blender {
+    SkXfermode::Mode mode;
+    GLenum src;
+    GLenum dst;
+}; // struct Blender
+
+// In this array, the index of each Blender equals the value of the first
+// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
+static const Blender gBlends[] = {
+        { SkXfermode::kClear_Mode,   GL_ZERO,                 GL_ZERO },
+        { SkXfermode::kSrc_Mode,     GL_ONE,                  GL_ZERO },
+        { SkXfermode::kDst_Mode,     GL_ZERO,                 GL_ONE },
+        { SkXfermode::kSrcOver_Mode, GL_ONE,                  GL_ONE_MINUS_SRC_ALPHA },
+        { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA,  GL_ONE },
+        { SkXfermode::kSrcIn_Mode,   GL_DST_ALPHA,            GL_ZERO },
+        { SkXfermode::kDstIn_Mode,   GL_ZERO,                 GL_SRC_ALPHA },
+        { SkXfermode::kSrcOut_Mode,  GL_ONE_MINUS_DST_ALPHA,  GL_ZERO },
+        { SkXfermode::kDstOut_Mode,  GL_ZERO,                 GL_ONE_MINUS_SRC_ALPHA },
+        { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA,            GL_ONE_MINUS_SRC_ALPHA },
+        { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA,  GL_SRC_ALPHA },
+        { SkXfermode::kXor_Mode,     GL_ONE_MINUS_DST_ALPHA,  GL_ONE_MINUS_SRC_ALPHA }
+};
+
+// This array contains the swapped version of each SkXfermode. For instance
+// this array's SrcOver blending mode is actually DstOver. You can refer to
+// createLayer() for more information on the purpose of this array.
+static const Blender gBlendsSwap[] = {
+        { SkXfermode::kClear_Mode,   GL_ZERO,                 GL_ZERO },
+        { SkXfermode::kSrc_Mode,     GL_ZERO,                 GL_ONE },
+        { SkXfermode::kDst_Mode,     GL_ONE,                  GL_ZERO },
+        { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA,  GL_ONE },
+        { SkXfermode::kDstOver_Mode, GL_ONE,                  GL_ONE_MINUS_SRC_ALPHA },
+        { SkXfermode::kSrcIn_Mode,   GL_ZERO,                 GL_SRC_ALPHA },
+        { SkXfermode::kDstIn_Mode,   GL_DST_ALPHA,            GL_ZERO },
+        { SkXfermode::kSrcOut_Mode,  GL_ZERO,                 GL_ONE_MINUS_SRC_ALPHA },
+        { SkXfermode::kDstOut_Mode,  GL_ONE_MINUS_DST_ALPHA,  GL_ZERO },
+        { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA,  GL_SRC_ALPHA },
+        { SkXfermode::kDstATop_Mode, GL_DST_ALPHA,            GL_ONE_MINUS_SRC_ALPHA },
+        { SkXfermode::kXor_Mode,     GL_ONE_MINUS_DST_ALPHA,  GL_ONE_MINUS_SRC_ALPHA }
+};
+
+static const GLenum gTextureUnits[] = {
+        GL_TEXTURE0,
+        GL_TEXTURE1,
+        GL_TEXTURE2
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()) {
+    mShader = NULL;
+    mColorFilter = NULL;
+    mHasShadow = false;
+
+    memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
+
+    mFirstSnapshot = new Snapshot;
+}
+
+OpenGLRenderer::~OpenGLRenderer() {
+    // The context has already been destroyed at this point, do not call
+    // GL APIs. All GL state should be kept in Caches.h
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Setup
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::setViewport(int width, int height) {
+    glViewport(0, 0, width, height);
+    mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
+
+    mWidth = width;
+    mHeight = height;
+
+    mFirstSnapshot->height = height;
+    mFirstSnapshot->viewport.set(0, 0, width, height);
+
+    mDirtyClip = false;
+}
+
+void OpenGLRenderer::prepare(bool opaque) {
+    mSnapshot = new Snapshot(mFirstSnapshot,
+            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    mSaveCount = 1;
+
+    glViewport(0, 0, mWidth, mHeight);
+
+    glDisable(GL_DITHER);
+
+    if (!opaque) {
+        glDisable(GL_SCISSOR_TEST);
+        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+        glClear(GL_COLOR_BUFFER_BIT);
+        glEnable(GL_SCISSOR_TEST);
+    } else {
+        glEnable(GL_SCISSOR_TEST);
+        glScissor(0, 0, mWidth, mHeight);
+        dirtyClip();
+    }
+
+    mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight);
+}
+
+void OpenGLRenderer::finish() {
+#if DEBUG_OPENGL
+    GLenum status = GL_NO_ERROR;
+    while ((status = glGetError()) != GL_NO_ERROR) {
+        LOGD("GL error from OpenGLRenderer: 0x%x", status);
+    }
+#endif
+}
+
+void OpenGLRenderer::acquireContext() {
+    if (mCaches.currentProgram) {
+        if (mCaches.currentProgram->isInUse()) {
+            mCaches.currentProgram->remove();
+            mCaches.currentProgram = NULL;
+        }
+    }
+    mCaches.unbindMeshBuffer();
+}
+
+void OpenGLRenderer::releaseContext() {
+    glViewport(0, 0, mSnapshot->viewport.getWidth(), mSnapshot->viewport.getHeight());
+
+    glEnable(GL_SCISSOR_TEST);
+    dirtyClip();
+
+    glDisable(GL_DITHER);
+
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+    mCaches.blend = true;
+    glEnable(GL_BLEND);
+    glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode);
+    glBlendEquation(GL_FUNC_ADD);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// State management
+///////////////////////////////////////////////////////////////////////////////
+
+int OpenGLRenderer::getSaveCount() const {
+    return mSaveCount;
+}
+
+int OpenGLRenderer::save(int flags) {
+    return saveSnapshot(flags);
+}
+
+void OpenGLRenderer::restore() {
+    if (mSaveCount > 1) {
+        restoreSnapshot();
+    }
+}
+
+void OpenGLRenderer::restoreToCount(int saveCount) {
+    if (saveCount < 1) saveCount = 1;
+
+    while (mSaveCount > saveCount) {
+        restoreSnapshot();
+    }
+}
+
+int OpenGLRenderer::saveSnapshot(int flags) {
+    mSnapshot = new Snapshot(mSnapshot, flags);
+    return mSaveCount++;
+}
+
+bool OpenGLRenderer::restoreSnapshot() {
+    bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet;
+    bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer;
+    bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho;
+
+    sp<Snapshot> current = mSnapshot;
+    sp<Snapshot> previous = mSnapshot->previous;
+
+    if (restoreOrtho) {
+        Rect& r = previous->viewport;
+        glViewport(r.left, r.top, r.right, r.bottom);
+        mOrthoMatrix.load(current->orthoMatrix);
+    }
+
+    mSaveCount--;
+    mSnapshot = previous;
+
+    if (restoreClip) {
+        dirtyClip();
+    }
+
+    if (restoreLayer) {
+        composeLayer(current, previous);
+    }
+
+    return restoreClip;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Layers
+///////////////////////////////////////////////////////////////////////////////
+
+int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
+        SkPaint* p, int flags) {
+    const GLuint previousFbo = mSnapshot->fbo;
+    const int count = saveSnapshot(flags);
+
+    if (!mSnapshot->invisible) {
+        int alpha = 255;
+        SkXfermode::Mode mode;
+
+        if (p) {
+            alpha = p->getAlpha();
+            if (!mCaches.extensions.hasFramebufferFetch()) {
+                const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
+                if (!isMode) {
+                    // Assume SRC_OVER
+                    mode = SkXfermode::kSrcOver_Mode;
+                }
+            } else {
+                mode = getXfermode(p->getXfermode());
+            }
+        } else {
+            mode = SkXfermode::kSrcOver_Mode;
+        }
+
+        createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags, previousFbo);
+    }
+
+    return count;
+}
+
+int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bottom,
+        int alpha, int flags) {
+    if (alpha >= 255 - ALPHA_THRESHOLD) {
+        return saveLayer(left, top, right, bottom, NULL, flags);
+    } else {
+        SkPaint paint;
+        paint.setAlpha(alpha);
+        return saveLayer(left, top, right, bottom, &paint, flags);
+    }
+}
+
+/**
+ * Layers are viewed by Skia are slightly different than layers in image editing
+ * programs (for instance.) When a layer is created, previously created layers
+ * and the frame buffer still receive every drawing command. For instance, if a
+ * layer is created and a shape intersecting the bounds of the layers and the
+ * framebuffer is draw, the shape will be drawn on both (unless the layer was
+ * created with the SkCanvas::kClipToLayer_SaveFlag flag.)
+ *
+ * A way to implement layers is to create an FBO for each layer, backed by an RGBA
+ * texture. Unfortunately, this is inefficient as it requires every primitive to
+ * be drawn n + 1 times, where n is the number of active layers. In practice this
+ * means, for every primitive:
+ *   - Switch active frame buffer
+ *   - Change viewport, clip and projection matrix
+ *   - Issue the drawing
+ *
+ * Switching rendering target n + 1 times per drawn primitive is extremely costly.
+ * To avoid this, layers are implemented in a different way here, at least in the
+ * general case. FBOs are used, as an optimization, when the "clip to layer" flag
+ * is set. When this flag is set we can redirect all drawing operations into a
+ * single FBO.
+ *
+ * This implementation relies on the frame buffer being at least RGBA 8888. When
+ * a layer is created, only a texture is created, not an FBO. The content of the
+ * frame buffer contained within the layer's bounds is copied into this texture
+ * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame
+ * buffer and drawing continues as normal. This technique therefore treats the
+ * frame buffer as a scratch buffer for the layers.
+ *
+ * To compose the layers back onto the frame buffer, each layer texture
+ * (containing the original frame buffer data) is drawn as a simple quad over
+ * the frame buffer. The trick is that the quad is set as the composition
+ * destination in the blending equation, and the frame buffer becomes the source
+ * of the composition.
+ *
+ * Drawing layers with an alpha value requires an extra step before composition.
+ * An empty quad is drawn over the layer's region in the frame buffer. This quad
+ * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the
+ * quad is used to multiply the colors in the frame buffer. This is achieved by
+ * changing the GL blend functions for the GL_FUNC_ADD blend equation to
+ * GL_ZERO, GL_SRC_ALPHA.
+ *
+ * Because glCopyTexImage2D() can be slow, an alternative implementation might
+ * be use to draw a single clipped layer. The implementation described above
+ * is correct in every case.
+ *
+ * (1) The frame buffer is actually not cleared right away. To allow the GPU
+ *     to potentially optimize series of calls to glCopyTexImage2D, the frame
+ *     buffer is left untouched until the first drawing operation. Only when
+ *     something actually gets drawn are the layers regions cleared.
+ */
+bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
+        float right, float bottom, int alpha, SkXfermode::Mode mode,
+        int flags, GLuint previousFbo) {
+    LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
+    LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
+
+    const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag;
+
+    // Window coordinates of the layer
+    Rect bounds(left, top, right, bottom);
+    if (fboLayer) {
+        // Clear the previous layer regions before we change the viewport
+        clearLayerRegions();
+    } else {
+        mSnapshot->transform->mapRect(bounds);
+
+        // Layers only make sense if they are in the framebuffer's bounds
+        bounds.intersect(*snapshot->clipRect);
+
+        // We cannot work with sub-pixels in this case
+        bounds.snapToPixelBoundaries();
+
+        // When the layer is not an FBO, we may use glCopyTexImage so we
+        // need to make sure the layer does not extend outside the bounds
+        // of the framebuffer
+        bounds.intersect(snapshot->previous->viewport);
+    }
+
+    if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
+            bounds.getHeight() > mCaches.maxTextureSize) {
+        snapshot->invisible = true;
+    } else {
+        snapshot->invisible = snapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
+    }
+
+    // Bail out if we won't draw in this snapshot
+    if (snapshot->invisible) {
+        return false;
+    }
+
+    glActiveTexture(gTextureUnits[0]);
+    Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight());
+    if (!layer) {
+        return false;
+    }
+
+    layer->mode = mode;
+    layer->alpha = alpha;
+    layer->layer.set(bounds);
+    layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->height),
+            bounds.getWidth() / float(layer->width), 0.0f);
+
+    // Save the layer in the snapshot
+    snapshot->flags |= Snapshot::kFlagIsLayer;
+    snapshot->layer = layer;
+
+    if (fboLayer) {
+        return createFboLayer(layer, bounds, snapshot, previousFbo);
+    } else {
+        // Copy the framebuffer into the layer
+        glBindTexture(GL_TEXTURE_2D, layer->texture);
+
+        if (layer->empty) {
+            glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left,
+                    snapshot->height - bounds.bottom, layer->width, layer->height, 0);
+            layer->empty = false;
+        } else {
+            glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left,
+                    snapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight());
+        }
+
+        // Enqueue the buffer coordinates to clear the corresponding region later
+        mLayers.push(new Rect(bounds));
+    }
+
+    return true;
+}
+
+bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot,
+        GLuint previousFbo) {
+    layer->fbo = mCaches.fboCache.get();
+
+#if RENDER_LAYERS_AS_REGIONS
+    snapshot->region = &snapshot->layer->region;
+    snapshot->flags |= Snapshot::kFlagFboTarget;
+#endif
+
+    Rect clip(bounds);
+    snapshot->transform->mapRect(clip);
+    clip.intersect(*snapshot->clipRect);
+    clip.snapToPixelBoundaries();
+    clip.intersect(snapshot->previous->viewport);
+
+    mat4 inverse;
+    inverse.loadInverse(*mSnapshot->transform);
+
+    inverse.mapRect(clip);
+    clip.snapToPixelBoundaries();
+    clip.intersect(bounds);
+    clip.translate(-bounds.left, -bounds.top);
+
+    snapshot->flags |= Snapshot::kFlagIsFboLayer;
+    snapshot->fbo = layer->fbo;
+    snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
+    //snapshot->resetClip(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
+    snapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
+    snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
+    snapshot->height = bounds.getHeight();
+    snapshot->flags |= Snapshot::kFlagDirtyOrtho;
+    snapshot->orthoMatrix.load(mOrthoMatrix);
+
+    // Bind texture to FBO
+    glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+    glBindTexture(GL_TEXTURE_2D, layer->texture);
+
+    // Initialize the texture if needed
+    if (layer->empty) {
+        layer->empty = false;
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0,
+                GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+    }
+
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+            layer->texture, 0);
+
+#if DEBUG_LAYERS_AS_REGIONS
+    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    if (status != GL_FRAMEBUFFER_COMPLETE) {
+        LOGE("Framebuffer incomplete (GL error code 0x%x)", status);
+
+        glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+        glDeleteTextures(1, &layer->texture);
+        mCaches.fboCache.put(layer->fbo);
+
+        delete layer;
+
+        return false;
+    }
+#endif
+
+    // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
+    glScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
+            clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    dirtyClip();
+
+    // Change the ortho projection
+    glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
+    mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f);
+
+    return true;
+}
+
+/**
+ * Read the documentation of createLayer() before doing anything in this method.
+ */
+void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
+    if (!current->layer) {
+        LOGE("Attempting to compose a layer that does not exist");
+        return;
+    }
+
+    const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
+
+    if (fboLayer) {
+        // Unbind current FBO and restore previous one
+        glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
+    }
+
+    Layer* layer = current->layer;
+    const Rect& rect = layer->layer;
+
+    if (!fboLayer && layer->alpha < 255) {
+        drawColorRect(rect.left, rect.top, rect.right, rect.bottom,
+                layer->alpha << 24, SkXfermode::kDstIn_Mode, true);
+        // Required below, composeLayerRect() will divide by 255
+        layer->alpha = 255;
+    }
+
+    mCaches.unbindMeshBuffer();
+
+    glActiveTexture(gTextureUnits[0]);
+
+    // When the layer is stored in an FBO, we can save a bit of fillrate by
+    // drawing only the dirty region
+    if (fboLayer) {
+        dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform);
+        composeLayerRegion(layer, rect);
+    } else {
+        dirtyLayer(rect.left, rect.top, rect.right, rect.bottom);
+        composeLayerRect(layer, rect, true);
+    }
+
+    if (fboLayer) {
+        // Detach the texture from the FBO
+        glBindFramebuffer(GL_FRAMEBUFFER, current->fbo);
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+        glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
+
+        // Put the FBO name back in the cache, if it doesn't fit, it will be destroyed
+        mCaches.fboCache.put(current->fbo);
+    }
+
+    dirtyClip();
+
+    // Failing to add the layer to the cache should happen only if the layer is too large
+    if (!mCaches.layerCache.put(layer)) {
+        LAYER_LOGD("Deleting layer");
+        glDeleteTextures(1, &layer->texture);
+        delete layer;
+    }
+}
+
+void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
+    const Rect& texCoords = layer->texCoords;
+    resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom);
+
+    drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture,
+            layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0],
+            &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap);
+
+    resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+}
+
+void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
+#if RENDER_LAYERS_AS_REGIONS
+    if (layer->region.isRect()) {
+        composeLayerRect(layer, rect);
+        layer->region.clear();
+        return;
+    }
+
+    if (!layer->region.isEmpty()) {
+        size_t count;
+        const android::Rect* rects = layer->region.getArray(&count);
+
+        setupDraw();
+
+        ProgramDescription description;
+        description.hasTexture = true;
+
+        const float alpha = layer->alpha / 255.0f;
+        const bool setColor = description.setColor(alpha, alpha, alpha, alpha);
+        chooseBlending(layer->blend || layer->alpha < 255, layer->mode, description, false);
+
+        useProgram(mCaches.programCache.get(description));
+
+        // Texture
+        bindTexture(layer->texture);
+        glUniform1i(mCaches.currentProgram->getUniform("sampler"), 0);
+
+        // Always premultiplied
+        if (setColor) {
+            mCaches.currentProgram->setColor(alpha, alpha, alpha, alpha);
+        }
+
+        // Mesh
+        int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
+        glEnableVertexAttribArray(texCoordsSlot);
+
+        mModelView.loadIdentity();
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+
+        const float texX = 1.0f / float(layer->width);
+        const float texY = 1.0f / float(layer->height);
+
+        TextureVertex* mesh = mCaches.getRegionMesh();
+        GLsizei numQuads = 0;
+
+        glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
+                gMeshStride, &mesh[0].position[0]);
+        glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE,
+                gMeshStride, &mesh[0].texture[0]);
+
+        for (size_t i = 0; i < count; i++) {
+            const android::Rect* r = &rects[i];
+
+            const float u1 = r->left * texX;
+            const float v1 = (rect.getHeight() - r->top) * texY;
+            const float u2 = r->right * texX;
+            const float v2 = (rect.getHeight() - r->bottom) * texY;
+
+            // TODO: Reject quads outside of the clip
+            TextureVertex::set(mesh++, r->left, r->top, u1, v1);
+            TextureVertex::set(mesh++, r->right, r->top, u2, v1);
+            TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
+            TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
+
+            numQuads++;
+
+            if (numQuads >= REGION_MESH_QUAD_COUNT) {
+                glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL);
+                numQuads = 0;
+                mesh = mCaches.getRegionMesh();
+            }
+        }
+
+        if (numQuads > 0) {
+            glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL);
+        }
+
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+        glDisableVertexAttribArray(texCoordsSlot);
+
+#if DEBUG_LAYERS_AS_REGIONS
+        uint32_t colors[] = {
+                0x7fff0000, 0x7f00ff00,
+                0x7f0000ff, 0x7fff00ff,
+        };
+
+        int offset = 0;
+        int32_t top = rects[0].top;
+        int i = 0;
+
+        for (size_t i = 0; i < count; i++) {
+            if (top != rects[i].top) {
+                offset ^= 0x2;
+                top = rects[i].top;
+            }
+
+            Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
+            drawColorRect(r.left, r.top, r.right, r.bottom, colors[offset + (i & 0x1)],
+                    SkXfermode::kSrcOver_Mode);
+        }
+#endif
+
+        layer->region.clear();
+    }
+#else
+    composeLayerRect(layer, rect);
+#endif
+}
+
+void OpenGLRenderer::dirtyLayer(const float left, const float top,
+        const float right, const float bottom, const mat4 transform) {
+#if RENDER_LAYERS_AS_REGIONS
+    if ((mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region) {
+        Rect bounds(left, top, right, bottom);
+        transform.mapRect(bounds);
+        bounds.intersect(*mSnapshot->clipRect);
+        bounds.snapToPixelBoundaries();
+
+        android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        if (!dirty.isEmpty()) {
+            mSnapshot->region->orSelf(dirty);
+        }
+    }
+#endif
+}
+
+void OpenGLRenderer::dirtyLayer(const float left, const float top,
+        const float right, const float bottom) {
+#if RENDER_LAYERS_AS_REGIONS
+    if ((mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region) {
+        Rect bounds(left, top, right, bottom);
+        bounds.intersect(*mSnapshot->clipRect);
+        bounds.snapToPixelBoundaries();
+
+        android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        if (!dirty.isEmpty()) {
+            mSnapshot->region->orSelf(dirty);
+        }
+    }
+#endif
+}
+
+void OpenGLRenderer::setupDraw() {
+    clearLayerRegions();
+    if (mDirtyClip) {
+        setScissorFromClip();
+    }
+}
+
+void OpenGLRenderer::clearLayerRegions() {
+    if (mLayers.size() == 0 || mSnapshot->invisible) return;
+
+    Rect clipRect(*mSnapshot->clipRect);
+    clipRect.snapToPixelBoundaries();
+
+    for (uint32_t i = 0; i < mLayers.size(); i++) {
+        Rect* bounds = mLayers.itemAt(i);
+        if (clipRect.intersects(*bounds)) {
+            // Clear the framebuffer where the layer will draw
+            glScissor(bounds->left, mSnapshot->height - bounds->bottom,
+                    bounds->getWidth(), bounds->getHeight());
+            glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+            glClear(GL_COLOR_BUFFER_BIT);
+
+            // Restore the clip
+            dirtyClip();
+        }
+
+        delete bounds;
+    }
+
+    mLayers.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Transforms
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::translate(float dx, float dy) {
+    mSnapshot->transform->translate(dx, dy, 0.0f);
+}
+
+void OpenGLRenderer::rotate(float degrees) {
+    mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f);
+}
+
+void OpenGLRenderer::scale(float sx, float sy) {
+    mSnapshot->transform->scale(sx, sy, 1.0f);
+}
+
+void OpenGLRenderer::setMatrix(SkMatrix* matrix) {
+    mSnapshot->transform->load(*matrix);
+}
+
+const float* OpenGLRenderer::getMatrix() const {
+    if (mSnapshot->fbo != 0) {
+        return &mSnapshot->transform->data[0];
+    }
+    return &mIdentity.data[0];
+}
+
+void OpenGLRenderer::getMatrix(SkMatrix* matrix) {
+    mSnapshot->transform->copyTo(*matrix);
+}
+
+void OpenGLRenderer::concatMatrix(SkMatrix* matrix) {
+    SkMatrix transform;
+    mSnapshot->transform->copyTo(transform);
+    transform.preConcat(*matrix);
+    mSnapshot->transform->load(transform);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Clipping
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::setScissorFromClip() {
+    Rect clip(*mSnapshot->clipRect);
+    clip.snapToPixelBoundaries();
+    glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight());
+    mDirtyClip = false;
+}
+
+const Rect& OpenGLRenderer::getClipBounds() {
+    return mSnapshot->getLocalClip();
+}
+
+bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
+    if (mSnapshot->invisible) {
+        return true;
+    }
+
+    Rect r(left, top, right, bottom);
+    mSnapshot->transform->mapRect(r);
+    r.snapToPixelBoundaries();
+
+    Rect clipRect(*mSnapshot->clipRect);
+    clipRect.snapToPixelBoundaries();
+
+    return !clipRect.intersects(r);
+}
+
+bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+    bool clipped = mSnapshot->clip(left, top, right, bottom, op);
+    if (clipped) {
+        dirtyClip();
+    }
+    return !mSnapshot->clipRect->isEmpty();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Drawing
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
+    const float right = left + bitmap->width();
+    const float bottom = top + bitmap->height();
+
+    if (quickReject(left, top, right, bottom)) {
+        return;
+    }
+
+    glActiveTexture(GL_TEXTURE0);
+    Texture* texture = mCaches.textureCache.get(bitmap);
+    if (!texture) return;
+    const AutoTexture autoCleanup(texture);
+
+    drawTextureRect(left, top, right, bottom, texture, paint);
+}
+
+void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
+    Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
+    const mat4 transform(*matrix);
+    transform.mapRect(r);
+
+    if (quickReject(r.left, r.top, r.right, r.bottom)) {
+        return;
+    }
+
+    glActiveTexture(GL_TEXTURE0);
+    Texture* texture = mCaches.textureCache.get(bitmap);
+    if (!texture) return;
+    const AutoTexture autoCleanup(texture);
+
+    // This could be done in a cheaper way, all we need is pass the matrix
+    // to the vertex shader. The save/restore is a bit overkill.
+    save(SkCanvas::kMatrix_SaveFlag);
+    concatMatrix(matrix);
+    drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint);
+    restore();
+}
+
+void OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
+         float srcLeft, float srcTop, float srcRight, float srcBottom,
+         float dstLeft, float dstTop, float dstRight, float dstBottom,
+         SkPaint* paint) {
+    if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) {
+        return;
+    }
+
+    glActiveTexture(gTextureUnits[0]);
+    Texture* texture = mCaches.textureCache.get(bitmap);
+    if (!texture) return;
+    const AutoTexture autoCleanup(texture);
+    setTextureWrapModes(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+
+    const float width = texture->width;
+    const float height = texture->height;
+
+    const float u1 = srcLeft / width;
+    const float v1 = srcTop / height;
+    const float u2 = srcRight / width;
+    const float v2 = srcBottom / height;
+
+    mCaches.unbindMeshBuffer();
+    resetDrawTextureTexCoords(u1, v1, u2, v2);
+
+    int alpha;
+    SkXfermode::Mode mode;
+    getAlphaAndMode(paint, &alpha, &mode);
+
+    drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom, texture->id, alpha / 255.0f,
+            mode, texture->blend, &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
+            GL_TRIANGLE_STRIP, gMeshCount);
+
+    resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+}
+
+void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
+        const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+        float left, float top, float right, float bottom, SkPaint* paint) {
+    if (quickReject(left, top, right, bottom)) {
+        return;
+    }
+
+    glActiveTexture(gTextureUnits[0]);
+    Texture* texture = mCaches.textureCache.get(bitmap);
+    if (!texture) return;
+    const AutoTexture autoCleanup(texture);
+    setTextureWrapModes(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+
+    int alpha;
+    SkXfermode::Mode mode;
+    getAlphaAndMode(paint, &alpha, &mode);
+
+    const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(),
+            right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
+
+    if (mesh) {
+        // Mark the current layer dirty where we are going to draw the patch
+        if ((mSnapshot->flags & Snapshot::kFlagFboTarget) &&
+                mSnapshot->region && mesh->hasEmptyQuads) {
+            const size_t count = mesh->quads.size();
+            for (size_t i = 0; i < count; i++) {
+                Rect bounds = mesh->quads.itemAt(i);
+                dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom,
+                        *mSnapshot->transform);
+            }
+        }
+
+        drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f,
+                mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
+                GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer,
+                true, !mesh->hasEmptyQuads);
+    }
+}
+
+void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
+    // TODO: Should do quickReject for each line
+    if (mSnapshot->invisible) return;
+
+    setupDraw();
+
+    int alpha;
+    SkXfermode::Mode mode;
+    getAlphaAndMode(paint, &alpha, &mode);
+
+    uint32_t color = paint->getColor();
+    const GLfloat a = alpha / 255.0f;
+    const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
+    const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
+    const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;
+
+    const bool isAA = paint->isAntiAlias();
+    if (isAA) {
+        GLuint textureUnit = 0;
+        glActiveTexture(gTextureUnits[textureUnit]);
+        setupTextureAlpha8(mCaches.line.getTexture(), 0, 0, textureUnit, 0.0f, 0.0f, r, g, b, a,
+                mode, false, true, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
+                mCaches.line.getMeshBuffer());
+    } else {
+        setupColorRect(0.0f, 0.0f, 1.0f, 1.0f, r, g, b, a, mode, false, true);
+    }
+
+    const float strokeWidth = paint->getStrokeWidth();
+    const GLsizei elementsCount = isAA ? mCaches.line.getElementsCount() : gMeshCount;
+    const GLenum drawMode = isAA ? GL_TRIANGLES : GL_TRIANGLE_STRIP;
+
+    for (int i = 0; i < count; i += 4) {
+        float tx = 0.0f;
+        float ty = 0.0f;
+
+        if (isAA) {
+            mCaches.line.update(points[i], points[i + 1], points[i + 2], points[i + 3],
+                    strokeWidth, tx, ty);
+        } else {
+            ty = strokeWidth <= 1.0f ? 0.0f : -strokeWidth * 0.5f;
+        }
+
+        const float dx = points[i + 2] - points[i];
+        const float dy = points[i + 3] - points[i + 1];
+        const float mag = sqrtf(dx * dx + dy * dy);
+        const float angle = acos(dx / mag);
+
+        mModelView.loadTranslate(points[i], points[i + 1], 0.0f);
+        if (angle > MIN_ANGLE || angle < -MIN_ANGLE) {
+            mModelView.rotate(angle * RAD_TO_DEG, 0.0f, 0.0f, 1.0f);
+        }
+        mModelView.translate(tx, ty, 0.0f);
+        if (!isAA) {
+            float length = mCaches.line.getLength(points[i], points[i + 1],
+                    points[i + 2], points[i + 3]);
+            mModelView.scale(length, strokeWidth, 1.0f);
+        }
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+        // TODO: Add bounds to the layer's region
+
+        if (mShader) {
+            mShader->updateTransforms(mCaches.currentProgram, mModelView, *mSnapshot);
+        }
+
+        glDrawArrays(drawMode, 0, elementsCount);
+    }
+
+    if (isAA) {
+        glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
+    }
+}
+
+void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
+    // No need to check against the clip, we fill the clip region
+    if (mSnapshot->invisible) return;
+
+    Rect& clip(*mSnapshot->clipRect);
+    clip.snapToPixelBoundaries();
+    drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true);
+}
+
+void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* p) {
+    if (quickReject(left, top, right, bottom)) {
+        return;
+    }
+
+    SkXfermode::Mode mode;
+    if (!mCaches.extensions.hasFramebufferFetch()) {
+        const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
+        if (!isMode) {
+            // Assume SRC_OVER
+            mode = SkXfermode::kSrcOver_Mode;
+        }
+    } else {
+        mode = getXfermode(p->getXfermode());
+    }
+
+    // Skia draws using the color's alpha channel if < 255
+    // Otherwise, it uses the paint's alpha
+    int color = p->getColor();
+    if (((color >> 24) & 0xff) == 255) {
+        color |= p->getAlpha() << 24;
+    }
+
+    drawColorRect(left, top, right, bottom, color, mode);
+}
+
+void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
+        float x, float y, SkPaint* paint) {
+    if (text == NULL || count == 0 || (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
+        return;
+    }
+    if (mSnapshot->invisible) return;
+
+    paint->setAntiAlias(true);
+
+    float length = -1.0f;
+    switch (paint->getTextAlign()) {
+        case SkPaint::kCenter_Align:
+            length = paint->measureText(text, bytesCount);
+            x -= length / 2.0f;
+            break;
+        case SkPaint::kRight_Align:
+            length = paint->measureText(text, bytesCount);
+            x -= length;
+            break;
+        default:
+            break;
+    }
+
+    int alpha;
+    SkXfermode::Mode mode;
+    getAlphaAndMode(paint, &alpha, &mode);
+
+    uint32_t color = paint->getColor();
+    const GLfloat a = alpha / 255.0f;
+    const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
+    const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
+    const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;
+
+    FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint);
+    fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
+            paint->getTextSize());
+
+    setupDraw();
+
+    if (mHasShadow) {
+        glActiveTexture(gTextureUnits[0]);
+        mCaches.dropShadowCache.setFontRenderer(fontRenderer);
+        const ShadowTexture* shadow = mCaches.dropShadowCache.get(paint, text, bytesCount,
+                count, mShadowRadius);
+        const AutoTexture autoCleanup(shadow);
+
+        setupShadow(shadow, x, y, mode, a);
+
+        // Draw the mesh
+        glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+        glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
+    }
+
+    GLuint textureUnit = 0;
+    glActiveTexture(gTextureUnits[textureUnit]);
+
+    // Assume that the modelView matrix does not force scales, rotates, etc.
+    const bool linearFilter = mSnapshot->transform->changesBounds();
+
+    // Dimensions are set to (0,0), the layer (if any) won't be dirtied
+    setupTextureAlpha8(fontRenderer.getTexture(linearFilter), 0, 0, textureUnit,
+            x, y, r, g, b, a, mode, false, true, NULL, NULL);
+
+    const Rect& clip = mSnapshot->getLocalClip();
+    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
+
+#if RENDER_LAYERS_AS_REGIONS
+    bool hasLayer = (mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region;
+#else
+    bool hasLayer = false;
+#endif
+
+    mCaches.unbindMeshBuffer();
+    if (fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y,
+            hasLayer ? &bounds : NULL)) {
+#if RENDER_LAYERS_AS_REGIONS
+        if (hasLayer) {
+            mSnapshot->transform->mapRect(bounds);
+            bounds.intersect(*mSnapshot->clipRect);
+            bounds.snapToPixelBoundaries();
+
+            android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
+            mSnapshot->region->orSelf(dirty);
+        }
+#endif
+    }
+
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+    glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
+
+    drawTextDecorations(text, bytesCount, length, x, y, paint);
+}
+
+void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
+    if (mSnapshot->invisible) return;
+
+    GLuint textureUnit = 0;
+    glActiveTexture(gTextureUnits[textureUnit]);
+
+    const PathTexture* texture = mCaches.pathCache.get(path, paint);
+    if (!texture) return;
+    const AutoTexture autoCleanup(texture);
+
+    const float x = texture->left - texture->offset;
+    const float y = texture->top - texture->offset;
+
+    if (quickReject(x, y, x + texture->width, y + texture->height)) {
+        return;
+    }
+
+    int alpha;
+    SkXfermode::Mode mode;
+    getAlphaAndMode(paint, &alpha, &mode);
+
+    uint32_t color = paint->getColor();
+    const GLfloat a = alpha / 255.0f;
+    const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
+    const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
+    const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;
+
+    setupTextureAlpha8(texture, textureUnit, x, y, r, g, b, a, mode, true, true);
+
+    setupDraw();
+
+    // Draw the mesh
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+    glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords"));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Shaders
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::resetShader() {
+    mShader = NULL;
+}
+
+void OpenGLRenderer::setupShader(SkiaShader* shader) {
+    mShader = shader;
+    if (mShader) {
+        mShader->set(&mCaches.textureCache, &mCaches.gradientCache);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Color filters
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::resetColorFilter() {
+    mColorFilter = NULL;
+}
+
+void OpenGLRenderer::setupColorFilter(SkiaColorFilter* filter) {
+    mColorFilter = filter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Drop shadow
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::resetShadow() {
+    mHasShadow = false;
+}
+
+void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
+    mHasShadow = true;
+    mShadowRadius = radius;
+    mShadowDx = dx;
+    mShadowDy = dy;
+    mShadowColor = color;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Drawing implementation
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::setupShadow(const ShadowTexture* texture, float x, float y,
+        SkXfermode::Mode mode, float alpha) {
+    const float sx = x - texture->left + mShadowDx;
+    const float sy = y - texture->top + mShadowDy;
+
+    const int shadowAlpha = ((mShadowColor >> 24) & 0xFF);
+    const GLfloat a = shadowAlpha < 255 ? shadowAlpha / 255.0f : alpha;
+    const GLfloat r = a * ((mShadowColor >> 16) & 0xFF) / 255.0f;
+    const GLfloat g = a * ((mShadowColor >>  8) & 0xFF) / 255.0f;
+    const GLfloat b = a * ((mShadowColor      ) & 0xFF) / 255.0f;
+
+    GLuint textureUnit = 0;
+    setupTextureAlpha8(texture, textureUnit, sx, sy, r, g, b, a, mode, true, false);
+}
+
+void OpenGLRenderer::setupTextureAlpha8(const Texture* texture, GLuint& textureUnit,
+        float x, float y, float r, float g, float b, float a, SkXfermode::Mode mode,
+        bool transforms, bool applyFilters) {
+    setupTextureAlpha8(texture->id, texture->width, texture->height, textureUnit,
+            x, y, r, g, b, a, mode, transforms, applyFilters,
+            (GLvoid*) 0, (GLvoid*) gMeshTextureOffset);
+}
+
+void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
+        GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
+        SkXfermode::Mode mode, bool transforms, bool applyFilters) {
+    setupTextureAlpha8(texture, width, height, textureUnit, x, y, r, g, b, a, mode,
+            transforms, applyFilters, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset);
+}
+
+void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
+        GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
+        SkXfermode::Mode mode, bool transforms, bool applyFilters,
+        GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
+     // Describe the required shaders
+     ProgramDescription description;
+     description.hasTexture = true;
+     description.hasAlpha8Texture = true;
+     const bool setColor = description.setAlpha8Color(r, g, b, a);
+
+     if (applyFilters) {
+         if (mShader) {
+             mShader->describe(description, mCaches.extensions);
+         }
+         if (mColorFilter) {
+             mColorFilter->describe(description, mCaches.extensions);
+         }
+     }
+
+     // Setup the blending mode
+     chooseBlending(true, mode, description);
+
+     // Build and use the appropriate shader
+     useProgram(mCaches.programCache.get(description));
+
+     bindTexture(texture);
+     glUniform1i(mCaches.currentProgram->getUniform("sampler"), textureUnit);
+
+     int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
+     glEnableVertexAttribArray(texCoordsSlot);
+
+     if (texCoords) {
+         // Setup attributes
+         if (!vertices) {
+             mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
+         } else {
+             mCaches.unbindMeshBuffer();
+         }
+         glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
+                 gMeshStride, vertices);
+         glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords);
+     }
+
+     // Setup uniforms
+     if (transforms) {
+         mModelView.loadTranslate(x, y, 0.0f);
+         mModelView.scale(width, height, 1.0f);
+     } else {
+         mModelView.loadIdentity();
+     }
+
+     mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+     if (width > 0 && height > 0) {
+         dirtyLayer(x, y, x + width, y + height, *mSnapshot->transform);
+     }
+
+     if (setColor) {
+         mCaches.currentProgram->setColor(r, g, b, a);
+     }
+
+     textureUnit++;
+     if (applyFilters) {
+         // Setup attributes and uniforms required by the shaders
+         if (mShader) {
+             mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit);
+         }
+         if (mColorFilter) {
+             mColorFilter->setupProgram(mCaches.currentProgram);
+         }
+     }
+}
+
+// Same values used by Skia
+#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
+#define kStdUnderline_Offset    (1.0f / 9.0f)
+#define kStdUnderline_Thickness (1.0f / 18.0f)
+
+void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float length,
+        float x, float y, SkPaint* paint) {
+    // Handle underline and strike-through
+    uint32_t flags = paint->getFlags();
+    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+        float underlineWidth = length;
+        // If length is > 0.0f, we already measured the text for the text alignment
+        if (length <= 0.0f) {
+            underlineWidth = paint->measureText(text, bytesCount);
+        }
+
+        float offsetX = 0;
+        switch (paint->getTextAlign()) {
+            case SkPaint::kCenter_Align:
+                offsetX = underlineWidth * 0.5f;
+                break;
+            case SkPaint::kRight_Align:
+                offsetX = underlineWidth;
+                break;
+            default:
+                break;
+        }
+
+        if (underlineWidth > 0.0f) {
+            const float textSize = paint->getTextSize();
+            const float strokeWidth = textSize * kStdUnderline_Thickness;
+
+            const float left = x - offsetX;
+            float top = 0.0f;
+
+            const int pointsCount = 4 * (flags & SkPaint::kStrikeThruText_Flag ? 2 : 1);
+            float points[pointsCount];
+            int currentPoint = 0;
+
+            if (flags & SkPaint::kUnderlineText_Flag) {
+                top = y + textSize * kStdUnderline_Offset;
+                points[currentPoint++] = left;
+                points[currentPoint++] = top;
+                points[currentPoint++] = left + underlineWidth;
+                points[currentPoint++] = top;
+            }
+
+            if (flags & SkPaint::kStrikeThruText_Flag) {
+                top = y + textSize * kStdStrikeThru_Offset;
+                points[currentPoint++] = left;
+                points[currentPoint++] = top;
+                points[currentPoint++] = left + underlineWidth;
+                points[currentPoint++] = top;
+            }
+
+            SkPaint linesPaint(*paint);
+            linesPaint.setStrokeWidth(strokeWidth);
+
+            drawLines(&points[0], pointsCount, &linesPaint);
+        }
+    }
+}
+
+void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
+        int color, SkXfermode::Mode mode, bool ignoreTransform) {
+    setupDraw();
+
+    // If a shader is set, preserve only the alpha
+    if (mShader) {
+        color |= 0x00ffffff;
+    }
+
+    // Render using pre-multiplied alpha
+    const int alpha = (color >> 24) & 0xFF;
+    const GLfloat a = alpha / 255.0f;
+    const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
+    const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
+    const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;
+
+    setupColorRect(left, top, right, bottom, r, g, b, a, mode, ignoreTransform);
+
+    // Draw the mesh
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+}
+
+void OpenGLRenderer::setupColorRect(float left, float top, float right, float bottom,
+        float r, float g, float b, float a, SkXfermode::Mode mode,
+        bool ignoreTransform, bool ignoreMatrix) {
+    GLuint textureUnit = 0;
+
+    // Describe the required shaders
+    ProgramDescription description;
+    const bool setColor = description.setColor(r, g, b, a);
+
+    if (mShader) {
+        mShader->describe(description, mCaches.extensions);
+    }
+    if (mColorFilter) {
+        mColorFilter->describe(description, mCaches.extensions);
+    }
+
+    // Setup the blending mode
+    chooseBlending(a < 1.0f || (mShader && mShader->blend()), mode, description);
+
+    // Build and use the appropriate shader
+    useProgram(mCaches.programCache.get(description));
+
+    // Setup attributes
+    mCaches.bindMeshBuffer();
+    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
+            gMeshStride, 0);
+
+    if (!ignoreMatrix) {
+        // Setup uniforms
+        mModelView.loadTranslate(left, top, 0.0f);
+        mModelView.scale(right - left, bottom - top, 1.0f);
+        if (!ignoreTransform) {
+            mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+            dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+        } else {
+            mat4 identity;
+            mCaches.currentProgram->set(mOrthoMatrix, mModelView, identity);
+            dirtyLayer(left, top, right, bottom);
+        }
+    }
+    mCaches.currentProgram->setColor(r, g, b, a);
+
+    // Setup attributes and uniforms required by the shaders
+    if (mShader) {
+        if (ignoreMatrix) mModelView.loadIdentity();
+        mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit);
+    }
+    if (mColorFilter) {
+        mColorFilter->setupProgram(mCaches.currentProgram);
+    }
+}
+
+void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
+        Texture* texture, SkPaint* paint) {
+    int alpha;
+    SkXfermode::Mode mode;
+    getAlphaAndMode(paint, &alpha, &mode);
+
+    setTextureWrapModes(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+
+    drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode,
+            texture->blend, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
+            GL_TRIANGLE_STRIP, gMeshCount);
+}
+
+void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
+        GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) {
+    drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend,
+            (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount);
+}
+
+void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
+        GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
+        GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
+        bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) {
+    setupDraw();
+
+    ProgramDescription description;
+    description.hasTexture = true;
+    const bool setColor = description.setColor(alpha, alpha, alpha, alpha);
+    if (mColorFilter) {
+        mColorFilter->describe(description, mCaches.extensions);
+    }
+
+    mModelView.loadTranslate(left, top, 0.0f);
+    if (!ignoreScale) {
+        mModelView.scale(right - left, bottom - top, 1.0f);
+    }
+
+    chooseBlending(blend || alpha < 1.0f, mode, description, swapSrcDst);
+
+    useProgram(mCaches.programCache.get(description));
+    if (!ignoreTransform) {
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+        if (dirty) dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+    } else {
+        mat4 identity;
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, identity);
+        if (dirty) dirtyLayer(left, top, right, bottom);
+    }
+
+    // Texture
+    bindTexture(texture);
+    glUniform1i(mCaches.currentProgram->getUniform("sampler"), 0);
+
+    // Always premultiplied
+    if (setColor) {
+        mCaches.currentProgram->setColor(alpha, alpha, alpha, alpha);
+    }
+
+    // Mesh
+    int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
+    glEnableVertexAttribArray(texCoordsSlot);
+
+    if (!vertices) {
+        mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
+    } else {
+        mCaches.unbindMeshBuffer();
+    }
+    glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
+            gMeshStride, vertices);
+    glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords);
+
+    // Color filter
+    if (mColorFilter) {
+        mColorFilter->setupProgram(mCaches.currentProgram);
+    }
+
+    glDrawArrays(drawMode, 0, elementsCount);
+    glDisableVertexAttribArray(texCoordsSlot);
+}
+
+void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
+        ProgramDescription& description, bool swapSrcDst) {
+    blend = blend || mode != SkXfermode::kSrcOver_Mode;
+    if (blend) {
+        if (mode < SkXfermode::kPlus_Mode) {
+            if (!mCaches.blend) {
+                glEnable(GL_BLEND);
+            }
+
+            GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src;
+            GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst;
+
+            if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
+                glBlendFunc(sourceMode, destMode);
+                mCaches.lastSrcMode = sourceMode;
+                mCaches.lastDstMode = destMode;
+            }
+        } else {
+            // These blend modes are not supported by OpenGL directly and have
+            // to be implemented using shaders. Since the shader will perform
+            // the blending, turn blending off here
+            if (mCaches.extensions.hasFramebufferFetch()) {
+                description.framebufferMode = mode;
+                description.swapSrcDst = swapSrcDst;
+            }
+
+            if (mCaches.blend) {
+                glDisable(GL_BLEND);
+            }
+            blend = false;
+        }
+    } else if (mCaches.blend) {
+        glDisable(GL_BLEND);
+    }
+    mCaches.blend = blend;
+}
+
+bool OpenGLRenderer::useProgram(Program* program) {
+    if (!program->isInUse()) {
+        if (mCaches.currentProgram != NULL) mCaches.currentProgram->remove();
+        program->use();
+        mCaches.currentProgram = program;
+        return false;
+    }
+    return true;
+}
+
+void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
+    TextureVertex* v = &mMeshVertices[0];
+    TextureVertex::setUV(v++, u1, v1);
+    TextureVertex::setUV(v++, u2, v1);
+    TextureVertex::setUV(v++, u1, v2);
+    TextureVertex::setUV(v++, u2, v2);
+}
+
+void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
+    if (paint) {
+        if (!mCaches.extensions.hasFramebufferFetch()) {
+            const bool isMode = SkXfermode::IsMode(paint->getXfermode(), mode);
+            if (!isMode) {
+                // Assume SRC_OVER
+                *mode = SkXfermode::kSrcOver_Mode;
+            }
+        } else {
+            *mode = getXfermode(paint->getXfermode());
+        }
+
+        // Skia draws using the color's alpha channel if < 255
+        // Otherwise, it uses the paint's alpha
+        int color = paint->getColor();
+        *alpha = (color >> 24) & 0xFF;
+        if (*alpha == 255) {
+            *alpha = paint->getAlpha();
+        }
+    } else {
+        *mode = SkXfermode::kSrcOver_Mode;
+        *alpha = 255;
+    }
+}
+
+SkXfermode::Mode OpenGLRenderer::getXfermode(SkXfermode* mode) {
+    if (mode == NULL) {
+        return SkXfermode::kSrcOver_Mode;
+    }
+    return mode->fMode;
+}
+
+void OpenGLRenderer::setTextureWrapModes(Texture* texture, GLenum wrapS, GLenum wrapT) {
+    bool bound = false;
+    if (wrapS != texture->wrapS) {
+        glBindTexture(GL_TEXTURE_2D, texture->id);
+        bound = true;
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
+        texture->wrapS = wrapS;
+    }
+    if (wrapT != texture->wrapT) {
+        if (!bound) {
+            glBindTexture(GL_TEXTURE_2D, texture->id);
+        }
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
+        texture->wrapT = wrapT;
+    }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
new file mode 100644
index 0000000..93c2e6c
--- /dev/null
+++ b/libs/hwui/OpenGLRenderer.h
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_OPENGL_RENDERER_H
+#define ANDROID_HWUI_OPENGL_RENDERER_H
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <SkBitmap.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkRegion.h>
+#include <SkShader.h>
+#include <SkXfermode.h>
+
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+#include "Extensions.h"
+#include "Matrix.h"
+#include "Program.h"
+#include "Rect.h"
+#include "Snapshot.h"
+#include "Vertex.h"
+#include "SkiaShader.h"
+#include "SkiaColorFilter.h"
+#include "Caches.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_OPENGL 1
+
+// If turned on, layers drawn inside FBOs are optimized with regions
+#define RENDER_LAYERS_AS_REGIONS 0
+#define DEBUG_LAYERS_AS_REGIONS 0
+
+///////////////////////////////////////////////////////////////////////////////
+// Renderer
+///////////////////////////////////////////////////////////////////////////////
+
+class DisplayListRenderer;
+
+/**
+ * OpenGL renderer used to draw accelerated 2D graphics. The API is a
+ * simplified version of Skia's Canvas API.
+ */
+class OpenGLRenderer {
+public:
+    OpenGLRenderer();
+    virtual ~OpenGLRenderer();
+
+    virtual void setViewport(int width, int height);
+
+    virtual void prepare(bool opaque);
+    virtual void finish();
+
+    virtual void acquireContext();
+    virtual void releaseContext();
+
+    int getSaveCount() const;
+    virtual int save(int flags);
+    virtual void restore();
+    virtual void restoreToCount(int saveCount);
+
+    virtual int saveLayer(float left, float top, float right, float bottom,
+            SkPaint* p, int flags);
+    virtual int saveLayerAlpha(float left, float top, float right, float bottom,
+            int alpha, int flags);
+
+    virtual void translate(float dx, float dy);
+    virtual void rotate(float degrees);
+    virtual void scale(float sx, float sy);
+
+    const float* getMatrix() const;
+    void getMatrix(SkMatrix* matrix);
+    virtual void setMatrix(SkMatrix* matrix);
+    virtual void concatMatrix(SkMatrix* matrix);
+
+    const Rect& getClipBounds();
+    bool quickReject(float left, float top, float right, float bottom);
+    virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+
+    virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
+    virtual void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
+    virtual void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
+            float srcRight, float srcBottom, float dstLeft, float dstTop,
+            float dstRight, float dstBottom, SkPaint* paint);
+    virtual void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
+            const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+            float left, float top, float right, float bottom, SkPaint* paint);
+    virtual void drawColor(int color, SkXfermode::Mode mode);
+    virtual void drawRect(float left, float top, float right, float bottom, SkPaint* paint);
+    virtual void drawPath(SkPath* path, SkPaint* paint);
+    virtual void drawLines(float* points, int count, SkPaint* paint);
+    virtual void drawText(const char* text, int bytesCount, int count, float x, float y,
+            SkPaint* paint);
+
+    virtual void resetShader();
+    virtual void setupShader(SkiaShader* shader);
+
+    virtual void resetColorFilter();
+    virtual void setupColorFilter(SkiaColorFilter* filter);
+
+    virtual void resetShadow();
+    virtual void setupShadow(float radius, float dx, float dy, int color);
+
+protected:
+    /**
+     * Compose the layer defined in the current snapshot with the layer
+     * defined by the previous snapshot.
+     *
+     * The current snapshot *must* be a layer (flag kFlagIsLayer set.)
+     *
+     * @param curent The current snapshot containing the layer to compose
+     * @param previous The previous snapshot to compose the current layer with
+     */
+    virtual void composeLayer(sp<Snapshot> current, sp<Snapshot> previous);
+
+private:
+    /**
+     * Saves the current state of the renderer as a new snapshot.
+     * The new snapshot is saved in mSnapshot and the previous snapshot
+     * is linked from mSnapshot->previous.
+     *
+     * @param flags The save flags; see SkCanvas for more information
+     *
+     * @return The new save count. This value can be passed to #restoreToCount()
+     */
+    int saveSnapshot(int flags);
+
+    /**
+     * Restores the current snapshot; mSnapshot becomes mSnapshot->previous.
+     *
+     * @return True if the clip was modified.
+     */
+    bool restoreSnapshot();
+
+    /**
+     * Sets the clipping rectangle using glScissor. The clip is defined by
+     * the current snapshot's clipRect member.
+     */
+    void setScissorFromClip();
+
+    /**
+     * Creates a new layer stored in the specified snapshot.
+     *
+     * @param snapshot The snapshot associated with the new layer
+     * @param left The left coordinate of the layer
+     * @param top The top coordinate of the layer
+     * @param right The right coordinate of the layer
+     * @param bottom The bottom coordinate of the layer
+     * @param alpha The translucency of the layer
+     * @param mode The blending mode of the layer
+     * @param flags The layer save flags
+     * @param previousFbo The name of the current framebuffer
+     *
+     * @return True if the layer was successfully created, false otherwise
+     */
+    bool createLayer(sp<Snapshot> snapshot, float left, float top, float right, float bottom,
+            int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo);
+
+    /**
+     * Creates a new layer stored in the specified snapshot as an FBO.
+     *
+     * @param layer The layer to store as an FBO
+     * @param snapshot The snapshot associated with the new layer
+     * @param bounds The bounds of the layer
+     * @param previousFbo The name of the current framebuffer
+     */
+    bool createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot,
+            GLuint previousFbo);
+
+    /**
+     * Compose the specified layer as a region.
+     *
+     * @param layer The layer to compose
+     * @param rect The layer's bounds
+     */
+    void composeLayerRegion(Layer* layer, const Rect& rect);
+
+    /**
+     * Compose the specified layer as a simple rectangle.
+     *
+     * @param layer The layer to compose
+     * @param rect The layer's bounds
+     * @param swap If true, the source and destination are swapped
+     */
+    void composeLayerRect(Layer* layer, const Rect& rect, bool swap = false);
+
+    /**
+     * Clears all the regions corresponding to the current list of layers.
+     * This method MUST be invoked before any drawing operation.
+     */
+    void clearLayerRegions();
+
+    /**
+     * Draws a colored rectangle with the specified color. The specified coordinates
+     * are transformed by the current snapshot's transform matrix.
+     *
+     * @param left The left coordinate of the rectangle
+     * @param top The top coordinate of the rectangle
+     * @param right The right coordinate of the rectangle
+     * @param bottom The bottom coordinate of the rectangle
+     * @param color The rectangle's ARGB color, defined as a packed 32 bits word
+     * @param mode The Skia xfermode to use
+     * @param ignoreTransform True if the current transform should be ignored
+     * @param ignoreBlending True if the blending is set by the caller
+     */
+    void drawColorRect(float left, float top, float right, float bottom,
+            int color, SkXfermode::Mode mode, bool ignoreTransform = false);
+
+    /**
+     * Setups shaders to draw a colored rect.
+     */
+    void setupColorRect(float left, float top, float right, float bottom,
+            float r, float g, float b, float a, SkXfermode::Mode mode,
+            bool ignoreTransform, bool ignoreMatrix = false);
+
+    /**
+     * Draws a textured rectangle with the specified texture. The specified coordinates
+     * are transformed by the current snapshot's transform matrix.
+     *
+     * @param left The left coordinate of the rectangle
+     * @param top The top coordinate of the rectangle
+     * @param right The right coordinate of the rectangle
+     * @param bottom The bottom coordinate of the rectangle
+     * @param texture The texture name to map onto the rectangle
+     * @param alpha An additional translucency parameter, between 0.0f and 1.0f
+     * @param mode The blending mode
+     * @param blend True if the texture contains an alpha channel
+     */
+    void drawTextureRect(float left, float top, float right, float bottom, GLuint texture,
+            float alpha, SkXfermode::Mode mode, bool blend);
+
+    /**
+     * Draws a textured rectangle with the specified texture. The specified coordinates
+     * are transformed by the current snapshot's transform matrix.
+     *
+     * @param left The left coordinate of the rectangle
+     * @param top The top coordinate of the rectangle
+     * @param right The right coordinate of the rectangle
+     * @param bottom The bottom coordinate of the rectangle
+     * @param texture The texture to use
+     * @param paint The paint containing the alpha, blending mode, etc.
+     */
+    void drawTextureRect(float left, float top, float right, float bottom,
+            Texture* texture, SkPaint* paint);
+
+    /**
+     * Draws a textured mesh with the specified texture. If the indices are omitted,
+     * the mesh is drawn as a simple quad. The mesh pointers become offsets when a
+     * VBO is bound.
+     *
+     * @param left The left coordinate of the rectangle
+     * @param top The top coordinate of the rectangle
+     * @param right The right coordinate of the rectangle
+     * @param bottom The bottom coordinate of the rectangle
+     * @param texture The texture name to map onto the rectangle
+     * @param alpha An additional translucency parameter, between 0.0f and 1.0f
+     * @param mode The blending mode
+     * @param blend True if the texture contains an alpha channel
+     * @param vertices The vertices that define the mesh
+     * @param texCoords The texture coordinates of each vertex
+     * @param elementsCount The number of elements in the mesh, required by indices
+     * @param swapSrcDst Whether or not the src and dst blending operations should be swapped
+     * @param ignoreTransform True if the current transform should be ignored
+     * @param vbo The VBO used to draw the mesh
+     * @param ignoreScale True if the model view matrix should not be scaled
+     * @param dirty True if calling this method should dirty the current layer
+     */
+    void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture,
+            float alpha, SkXfermode::Mode mode, bool blend,
+            GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
+            bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0,
+            bool ignoreScale = false, bool dirty = true);
+
+    /**
+     * Prepares the renderer to draw the specified shadow. The active texture
+     * unit must be 0 and the other units must be unbound.
+     *
+     * @param texture The shadow texture
+     * @param x The x coordinate of the shadow
+     * @param y The y coordinate of the shadow
+     * @param mode The blending mode
+     * @param alpha The alpha value
+     */
+    void setupShadow(const ShadowTexture* texture, float x, float y, SkXfermode::Mode mode,
+            float alpha);
+
+    /**
+     * Prepares the renderer to draw the specified Alpha8 texture as a rectangle.
+     *
+     * @param texture The texture to render with
+     * @param textureUnit The texture unit to use, may be modified
+     * @param x The x coordinate of the rectangle to draw
+     * @param y The y coordinate of the rectangle to draw
+     * @param r The red component of the color
+     * @param g The green component of the color
+     * @param b The blue component of the color
+     * @param a The alpha component of the color
+     * @param mode The blending mode
+     * @param transforms True if the matrix passed to the shader should be multiplied
+     *        by the model-view matrix
+     * @param applyFilters Whether or not to take color filters and
+     *        shaders into account
+     */
+    void setupTextureAlpha8(const Texture* texture, GLuint& textureUnit, float x, float y,
+            float r, float g, float b, float a, SkXfermode::Mode mode, bool transforms,
+            bool applyFilters);
+
+    /**
+     * Prepares the renderer to draw the specified Alpha8 texture as a rectangle.
+     *
+     * @param texture The texture to render with
+     * @param width The width of the texture
+     * @param height The height of the texture
+     * @param textureUnit The texture unit to use, may be modified
+     * @param x The x coordinate of the rectangle to draw
+     * @param y The y coordinate of the rectangle to draw
+     * @param r The red component of the color
+     * @param g The green component of the color
+     * @param b The blue component of the color
+     * @param a The alpha component of the color
+     * @param mode The blending mode
+     * @param transforms True if the matrix passed to the shader should be multiplied
+     *        by the model-view matrix
+     * @param applyFilters Whether or not to take color filters and
+     *        shaders into account
+     */
+    void setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
+            GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
+            SkXfermode::Mode mode, bool transforms, bool applyFilters);
+
+    /**
+     * Same as above setupTextureAlpha8() but specifies the mesh's vertices
+     * and texCoords pointers. The pointers become offsets when a VBO is bound.
+     */
+    void setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t height,
+            GLuint& textureUnit, float x, float y, float r, float g, float b, float a,
+            SkXfermode::Mode mode, bool transforms, bool applyFilters,
+            GLvoid* vertices, GLvoid* texCoords, GLuint vbo = 0);
+
+    /**
+     * Draws text underline and strike-through if needed.
+     *
+     * @param text The text to decor
+     * @param bytesCount The number of bytes in the text
+     * @param length The length in pixels of the text, can be <= 0.0f to force a measurement
+     * @param x The x coordinate where the text will be drawn
+     * @param y The y coordinate where the text will be drawn
+     * @param paint The paint to draw the text with
+     */
+    void drawTextDecorations(const char* text, int bytesCount, float length,
+            float x, float y, SkPaint* paint);
+
+    /**
+     * Resets the texture coordinates stored in mMeshVertices. Setting the values
+     * back to default is achieved by calling:
+     *
+     * resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+     *
+     * @param u1 The left coordinate of the texture
+     * @param v1 The bottom coordinate of the texture
+     * @param u2 The right coordinate of the texture
+     * @param v2 The top coordinate of the texture
+     */
+    void resetDrawTextureTexCoords(float u1, float v1, float u2, float v2);
+
+    /**
+     * Gets the alpha and xfermode out of a paint object. If the paint is null
+     * alpha will be 255 and the xfermode will be SRC_OVER.
+     *
+     * @param paint The paint to extract values from
+     * @param alpha Where to store the resulting alpha
+     * @param mode Where to store the resulting xfermode
+     */
+    inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode);
+
+    /**
+     * Binds the specified texture. The texture unit must have been selected
+     * prior to calling this method.
+     */
+    inline void bindTexture(GLuint texture) {
+        glBindTexture(GL_TEXTURE_2D, texture);
+    }
+
+    /**
+     * Sets the wrap modes for the specified texture. The wrap modes are modified
+     * only when needed.
+     */
+    inline void setTextureWrapModes(Texture* texture, GLenum wrapS, GLenum wrapT);
+
+    /**
+     * Enable or disable blending as necessary. This function sets the appropriate
+     * blend function based on the specified xfermode.
+     */
+    inline void chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description,
+            bool swapSrcDst = false);
+
+    /**
+     * Safely retrieves the mode from the specified xfermode. If the specified
+     * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode.
+     */
+    inline SkXfermode::Mode getXfermode(SkXfermode* mode);
+
+    /**
+     * Use the specified program with the current GL context. If the program is already
+     * in use, it will not be bound again. If it is not in use, the current program is
+     * marked unused and the specified program becomes used and becomes the new
+     * current program.
+     *
+     * @param program The program to use
+     *
+     * @return true If the specified program was already in use, false otherwise.
+     */
+    inline bool useProgram(Program* program);
+
+    /**
+     * Invoked before any drawing operation. This sets required state.
+     */
+    void setupDraw();
+
+    /**
+     * Should be invoked every time the glScissor is modified.
+     */
+    inline void dirtyClip() {
+        mDirtyClip = true;
+    }
+
+    /**
+     * Mark the layer as dirty at the specified coordinates. The coordinates
+     * are transformed with the supplied matrix.
+     */
+    void dirtyLayer(const float left, const float top, const float right, const float bottom,
+            const mat4 transform);
+
+    /**
+     * Mark the layer as dirty at the specified coordinates.
+     */
+    void dirtyLayer(const float left, const float top, const float right, const float bottom);
+
+    // Dimensions of the drawing surface
+    int mWidth, mHeight;
+
+    // Matrix used for ortho projection in shaders
+    mat4 mOrthoMatrix;
+
+    // Model-view matrix used to position/size objects
+    mat4 mModelView;
+
+    // Number of saved states
+    int mSaveCount;
+    // Base state
+    sp<Snapshot> mFirstSnapshot;
+    // Current state
+    sp<Snapshot> mSnapshot;
+
+    // Shaders
+    SkiaShader* mShader;
+
+    // Color filters
+    SkiaColorFilter* mColorFilter;
+
+    // Used to draw textured quads
+    TextureVertex mMeshVertices[4];
+
+    // Drop shadow
+    bool mHasShadow;
+    float mShadowRadius;
+    float mShadowDx;
+    float mShadowDy;
+    int mShadowColor;
+
+    // Various caches
+    Caches& mCaches;
+
+    // List of rectangles to clear due to calls to saveLayer()
+    Vector<Rect*> mLayers;
+
+    // Indentity matrix
+    const mat4 mIdentity;
+
+    // Indicates whether the clip must be restored
+    bool mDirtyClip;
+
+    friend class DisplayListRenderer;
+
+}; // class OpenGLRenderer
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_OPENGL_RENDERER_H
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
new file mode 100644
index 0000000..9b2d476
--- /dev/null
+++ b/libs/hwui/Patch.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <cmath>
+
+#include <utils/Log.h>
+
+#include "Patch.h"
+#include "Caches.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+Patch::Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads) {
+    // 2 triangles per patch, 3 vertices per triangle
+    verticesCount = ((xCount + 1) * (yCount + 1) - emptyQuads) * 2 * 3;
+    mVertices = new TextureVertex[verticesCount];
+    hasEmptyQuads = emptyQuads > 0;
+
+    glGenBuffers(1, &meshBuffer);
+}
+
+Patch::~Patch() {
+    delete[] mVertices;
+    glDeleteBuffers(1, &meshBuffer);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Vertices management
+///////////////////////////////////////////////////////////////////////////////
+
+void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
+        float left, float top, float right, float bottom,
+        const int32_t* xDivs, const int32_t* yDivs,
+        const uint32_t width, const uint32_t height, const uint32_t colorKey) {
+    if (hasEmptyQuads) quads.clear();
+
+    const uint32_t xStretchCount = (width + 1) >> 1;
+    const uint32_t yStretchCount = (height + 1) >> 1;
+
+    float stretchX = 0.0f;
+    float stretchY = 0.0;
+
+    const float meshWidth = right - left;
+
+    if (xStretchCount > 0) {
+        uint32_t stretchSize = 0;
+        for (uint32_t i = 1; i < width; i += 2) {
+            stretchSize += xDivs[i] - xDivs[i - 1];
+        }
+        const float xStretchTex = stretchSize;
+        const float fixed = bitmapWidth - stretchSize;
+        const float xStretch = right - left - fixed;
+        stretchX = xStretch / xStretchTex;
+    }
+
+    if (yStretchCount > 0) {
+        uint32_t stretchSize = 0;
+        for (uint32_t i = 1; i < height; i += 2) {
+            stretchSize += yDivs[i] - yDivs[i - 1];
+        }
+        const float yStretchTex = stretchSize;
+        const float fixed = bitmapHeight - stretchSize;
+        const float yStretch = bottom - top - fixed;
+        stretchY = yStretch / yStretchTex;
+    }
+
+    TextureVertex* vertex = mVertices;
+    uint32_t quadCount = 0;
+
+    float previousStepY = 0.0f;
+
+    float y1 = 0.0f;
+    float v1 = 0.0f;
+
+    for (uint32_t i = 0; i < height; i++) {
+        float stepY = yDivs[i];
+
+        float y2 = 0.0f;
+        if (i & 1) {
+            const float segment = stepY - previousStepY;
+            y2 = y1 + segment * stretchY;
+        } else {
+            y2 = y1 + stepY - previousStepY;
+        }
+        float v2 = fmax(0.0f, stepY - 0.5f) / bitmapHeight;
+
+        generateRow(vertex, y1, y2, v1, v2, xDivs, width, stretchX,
+                right - left, bitmapWidth, quadCount, colorKey);
+
+        y1 = y2;
+        v1 = (stepY + 0.5f) / bitmapHeight;
+
+        previousStepY = stepY;
+    }
+
+    generateRow(vertex, y1, bottom - top, v1, 1.0f, xDivs, width, stretchX,
+            right - left, bitmapWidth, quadCount, colorKey);
+
+    Caches::getInstance().bindMeshBuffer(meshBuffer);
+    glBufferData(GL_ARRAY_BUFFER, sizeof(TextureVertex) * verticesCount,
+                mVertices, GL_STATIC_DRAW);
+}
+
+void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2,
+        const int32_t xDivs[], uint32_t xCount, float stretchX, float width, float bitmapWidth,
+        uint32_t& quadCount, const uint32_t colorKey) {
+    float previousStepX = 0.0f;
+
+    float x1 = 0.0f;
+    float u1 = 0.0f;
+
+    // Generate the row quad by quad
+    for (uint32_t i = 0; i < xCount; i++) {
+        float stepX = xDivs[i];
+
+        float x2 = 0.0f;
+        if (i & 1) {
+            const float segment = stepX - previousStepX;
+            x2 = x1 + segment * stretchX;
+        } else {
+            x2 = x1 + stepX - previousStepX;
+        }
+        float u2 = fmax(0.0f, stepX - 0.5f) / bitmapWidth;
+
+        generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2, quadCount, colorKey);
+
+        x1 = x2;
+        u1 = (stepX + 0.5f) / bitmapWidth;
+
+        previousStepX = stepX;
+    }
+
+    generateQuad(vertex, x1, y1, width, y2, u1, v1, 1.0f, v2, quadCount, colorKey);
+}
+
+void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2,
+            float u1, float v1, float u2, float v2, uint32_t& quadCount, const uint32_t colorKey) {
+    if (((colorKey >> quadCount++) & 0x1) == 1) {
+        return;
+    }
+
+    if (hasEmptyQuads) {
+        Rect bounds(x1, y1, x2, y2);
+        quads.add(bounds);
+    }
+
+    // Left triangle
+    TextureVertex::set(vertex++, x1, y1, u1, v1);
+    TextureVertex::set(vertex++, x2, y1, u2, v1);
+    TextureVertex::set(vertex++, x1, y2, u1, v2);
+
+    // Right triangle
+    TextureVertex::set(vertex++, x1, y2, u1, v2);
+    TextureVertex::set(vertex++, x2, y1, u2, v1);
+    TextureVertex::set(vertex++, x2, y2, u2, v2);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
new file mode 100644
index 0000000..1e78b2f
--- /dev/null
+++ b/libs/hwui/Patch.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_PATCH_H
+#define ANDROID_HWUI_PATCH_H
+
+#include <sys/types.h>
+
+#include <GLES2/gl2.h>
+
+#include <utils/Vector.h>
+
+#include "Rect.h"
+#include "Vertex.h"
+#include "utils/Compare.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// 9-patch structures
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Description of a patch.
+ */
+struct PatchDescription {
+    PatchDescription(): bitmapWidth(0), bitmapHeight(0), pixelWidth(0), pixelHeight(0),
+            xCount(0), yCount(0), emptyCount(0), colorKey(0) { }
+    PatchDescription(const float bitmapWidth, const float bitmapHeight,
+            const float pixelWidth, const float pixelHeight,
+            const uint32_t xCount, const uint32_t yCount,
+            const int8_t emptyCount, const uint32_t colorKey):
+            bitmapWidth(bitmapWidth), bitmapHeight(bitmapHeight),
+            pixelWidth(pixelWidth), pixelHeight(pixelHeight),
+            xCount(xCount), yCount(yCount),
+            emptyCount(emptyCount), colorKey(colorKey) { }
+    PatchDescription(const PatchDescription& description):
+            bitmapWidth(description.bitmapWidth), bitmapHeight(description.bitmapHeight),
+            pixelWidth(description.pixelWidth), pixelHeight(description.pixelHeight),
+            xCount(description.xCount), yCount(description.yCount),
+            emptyCount(description.emptyCount), colorKey(description.colorKey) { }
+
+    float bitmapWidth;
+    float bitmapHeight;
+    float pixelWidth;
+    float pixelHeight;
+    uint32_t xCount;
+    uint32_t yCount;
+    int8_t emptyCount;
+    uint32_t colorKey;
+
+    bool operator<(const PatchDescription& rhs) const {
+        LTE_FLOAT(bitmapWidth) {
+            LTE_FLOAT(bitmapHeight) {
+                LTE_FLOAT(pixelWidth) {
+                    LTE_FLOAT(pixelHeight) {
+                        LTE_INT(xCount) {
+                            LTE_INT(yCount) {
+                                LTE_INT(emptyCount) {
+                                    LTE_INT(colorKey) return false;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+}; // struct PatchDescription
+
+/**
+ * An OpenGL patch. This contains an array of vertices and an array of
+ * indices to render the vertices.
+ */
+struct Patch {
+    Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads = 0);
+    ~Patch();
+
+    void updateVertices(const float bitmapWidth, const float bitmapHeight,
+            float left, float top, float right, float bottom,
+            const int32_t* xDivs, const int32_t* yDivs,
+            const uint32_t width, const uint32_t height,
+            const uint32_t colorKey = 0);
+
+    GLuint meshBuffer;
+    uint32_t verticesCount;
+    Vector<Rect> quads;
+    bool hasEmptyQuads;
+
+private:
+    TextureVertex* mVertices;
+
+    void generateRow(TextureVertex*& vertex, float y1, float y2,
+            float v1, float v2, const int32_t xDivs[], uint32_t xCount,
+            float stretchX, float width, float bitmapWidth,
+            uint32_t& quadCount, const uint32_t colorKey);
+    void generateQuad(TextureVertex*& vertex,
+            float x1, float y1, float x2, float y2,
+            float u1, float v1, float u2, float v2,
+            uint32_t& quadCount, const uint32_t colorKey);
+}; // struct Patch
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_PATCH_H
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
new file mode 100644
index 0000000..71bab91
--- /dev/null
+++ b/libs/hwui/PatchCache.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/Log.h>
+
+#include "PatchCache.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+PatchCache::PatchCache(): mMaxEntries(DEFAULT_PATCH_CACHE_SIZE) {
+}
+
+PatchCache::PatchCache(uint32_t maxEntries): mMaxEntries(maxEntries) {
+}
+
+PatchCache::~PatchCache() {
+    clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void PatchCache::clear() {
+    size_t count = mCache.size();
+    for (size_t i = 0; i < count; i++) {
+        delete mCache.valueAt(i);
+    }
+    mCache.clear();
+}
+
+Patch* PatchCache::get(const float bitmapWidth, const float bitmapHeight,
+        const float pixelWidth, const float pixelHeight,
+        const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors,
+        const uint32_t width, const uint32_t height, const int8_t numColors) {
+
+    int8_t transparentQuads = 0;
+    uint32_t colorKey = 0;
+
+    if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
+        for (int8_t i = 0; i < numColors; i++) {
+            if (colors[i] == 0x0) {
+                transparentQuads++;
+                colorKey |= 0x1 << i;
+            }
+        }
+    }
+
+    // If the 9patch is made of only transparent quads
+    if (transparentQuads == int8_t((width + 1) * (height + 1))) {
+        return NULL;
+    }
+
+    const PatchDescription description(bitmapWidth, bitmapHeight,
+            pixelWidth, pixelHeight, width, height, transparentQuads, colorKey);
+
+    ssize_t index = mCache.indexOfKey(description);
+    Patch* mesh = NULL;
+    if (index >= 0) {
+        mesh = mCache.valueAt(index);
+    }
+
+    if (!mesh) {
+        PATCH_LOGD("New patch mesh "
+                "xCount=%d yCount=%d, w=%.2f h=%.2f, bw=%.2f bh=%.2f",
+                width, height, pixelWidth, pixelHeight, bitmapWidth, bitmapHeight);
+
+        mesh = new Patch(width, height, transparentQuads);
+        mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f,
+                pixelWidth, pixelHeight, xDivs, yDivs, width, height, colorKey);
+
+        if (mCache.size() >= mMaxEntries) {
+            delete mCache.valueAt(mCache.size() - 1);
+            mCache.removeItemsAt(mCache.size() - 1, 1);
+        }
+
+        mCache.add(description, mesh);
+    }
+
+    return mesh;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
new file mode 100644
index 0000000..deba40d
--- /dev/null
+++ b/libs/hwui/PatchCache.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_PATCH_CACHE_H
+#define ANDROID_HWUI_PATCH_CACHE_H
+
+#include <utils/KeyedVector.h>
+
+#include "Patch.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_PATCHES 0
+
+// Debug
+#if DEBUG_PATCHES
+    #define PATCH_LOGD(...) LOGD(__VA_ARGS__)
+#else
+    #define PATCH_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
+class PatchCache {
+public:
+    PatchCache();
+    PatchCache(uint32_t maxCapacity);
+    ~PatchCache();
+
+    Patch* get(const float bitmapWidth, const float bitmapHeight,
+            const float pixelWidth, const float pixelHeight,
+            const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors,
+            const uint32_t width, const uint32_t height, const int8_t numColors);
+    void clear();
+
+private:
+    uint32_t mMaxEntries;
+    KeyedVector<PatchDescription, Patch*> mCache;
+}; // class PatchCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_PATCH_CACHE_H
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
new file mode 100644
index 0000000..b58785a
--- /dev/null
+++ b/libs/hwui/PathCache.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+
+#include <SkCanvas.h>
+#include <SkRect.h>
+
+#include <utils/threads.h>
+
+#include "PathCache.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+PathCache::PathCache():
+        mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting path cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE);
+    }
+    init();
+}
+
+PathCache::PathCache(uint32_t maxByteSize):
+        mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(maxByteSize) {
+    init();
+}
+
+PathCache::~PathCache() {
+    Mutex::Autolock _l(mLock);
+    mCache.clear();
+}
+
+void PathCache::init() {
+    mCache.setOnEntryRemovedListener(this);
+
+    GLint maxTextureSize;
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+    mMaxTextureSize = maxTextureSize;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t PathCache::getSize() {
+    Mutex::Autolock _l(mLock);
+    return mSize;
+}
+
+uint32_t PathCache::getMaxSize() {
+    Mutex::Autolock _l(mLock);
+    return mMaxSize;
+}
+
+void PathCache::setMaxSize(uint32_t maxSize) {
+    Mutex::Autolock _l(mLock);
+    mMaxSize = maxSize;
+    while (mSize > mMaxSize) {
+        mCache.removeOldest();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void PathCache::operator()(PathCacheEntry& path, PathTexture*& texture) {
+    const uint32_t size = texture->width * texture->height;
+    mSize -= size;
+
+    if (texture) {
+        glDeleteTextures(1, &texture->id);
+        delete texture;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void PathCache::remove(SkPath* path) {
+    Mutex::Autolock _l(mLock);
+    // TODO: Linear search...
+    for (uint32_t i = 0; i < mCache.size(); i++) {
+        if (mCache.getKeyAt(i).path == path) {
+            mCache.removeAt(i);
+        }
+    }
+}
+
+PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
+    PathCacheEntry entry(path, paint);
+
+    mLock.lock();
+    PathTexture* texture = mCache.get(entry);
+    mLock.unlock();
+
+    if (!texture) {
+        texture = addTexture(entry, path, paint);
+    } else if (path->getGenerationID() != texture->generation) {
+        mLock.lock();
+        mCache.remove(entry);
+        mLock.unlock();
+        texture = addTexture(entry, path, paint);
+    }
+
+    return texture;
+}
+
+PathTexture* PathCache::addTexture(const PathCacheEntry& entry,
+        const SkPath *path, const SkPaint* paint) {
+    const SkRect& bounds = path->getBounds();
+
+    const float pathWidth = fmax(bounds.width(), 1.0f);
+    const float pathHeight = fmax(bounds.height(), 1.0f);
+
+    if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) {
+        LOGW("Path too large to be rendered into a texture");
+        return NULL;
+    }
+
+    const float offset = entry.strokeWidth * 1.5f;
+    const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5);
+    const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+
+    const uint32_t size = width * height;
+    // Don't even try to cache a bitmap that's bigger than the cache
+    if (size < mMaxSize) {
+        mLock.lock();
+        while (mSize + size > mMaxSize) {
+            mCache.removeOldest();
+        }
+        mLock.unlock();
+    }
+
+    PathTexture* texture = new PathTexture;
+    texture->left = bounds.fLeft;
+    texture->top = bounds.fTop;
+    texture->offset = offset;
+    texture->width = width;
+    texture->height = height;
+    texture->generation = path->getGenerationID();
+
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kA8_Config, width, height);
+    bitmap.allocPixels();
+    bitmap.eraseColor(0);
+
+    SkPaint pathPaint(*paint);
+    if (!pathPaint.getXfermode()) {
+        SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
+        pathPaint.setXfermode(mode)->safeUnref();
+    }
+
+    SkCanvas canvas(bitmap);
+    canvas.translate(-bounds.fLeft + offset, -bounds.fTop + offset);
+    canvas.drawPath(*path, pathPaint);
+
+    generateTexture(bitmap, texture);
+
+    if (size < mMaxSize) {
+        mLock.lock();
+        mSize += size;
+        mCache.put(entry, texture);
+        mLock.unlock();
+    } else {
+        texture->cleanup = true;
+    }
+
+    return texture;
+}
+
+void PathCache::clear() {
+    Mutex::Autolock _l(mLock);
+    mCache.clear();
+}
+
+void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
+    SkAutoLockPixels alp(bitmap);
+    if (!bitmap.readyToDraw()) {
+        LOGE("Cannot generate texture from bitmap");
+        return;
+    }
+
+    glGenTextures(1, &texture->id);
+
+    glBindTexture(GL_TEXTURE_2D, texture->id);
+    // Textures are Alpha8
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+    texture->blend = true;
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
+            GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
new file mode 100644
index 0000000..db5ce08
--- /dev/null
+++ b/libs/hwui/PathCache.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_PATH_CACHE_H
+#define ANDROID_HWUI_PATH_CACHE_H
+
+#include <SkBitmap.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+
+#include "Texture.h"
+#include "utils/Compare.h"
+#include "utils/GenerationCache.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Describe a path in the path cache.
+ */
+struct PathCacheEntry {
+    PathCacheEntry() {
+        path = NULL;
+        join = SkPaint::kDefault_Join;
+        cap = SkPaint::kDefault_Cap;
+        style = SkPaint::kFill_Style;
+        miter = 4.0f;
+        strokeWidth = 1.0f;
+    }
+
+    PathCacheEntry(const PathCacheEntry& entry):
+        path(entry.path), join(entry.join), cap(entry.cap),
+        style(entry.style), miter(entry.miter),
+        strokeWidth(entry.strokeWidth) {
+    }
+
+    PathCacheEntry(SkPath* path, SkPaint* paint) {
+        this->path = path;
+        join = paint->getStrokeJoin();
+        cap = paint->getStrokeCap();
+        miter = paint->getStrokeMiter();
+        strokeWidth = paint->getStrokeWidth();
+        style = paint->getStyle();
+    }
+
+    SkPath* path;
+    SkPaint::Join join;
+    SkPaint::Cap cap;
+    SkPaint::Style style;
+    float miter;
+    float strokeWidth;
+
+    bool operator<(const PathCacheEntry& rhs) const {
+        LTE_INT(path) {
+            LTE_INT(join) {
+                LTE_INT(cap) {
+                    LTE_INT(style) {
+                        LTE_FLOAT(miter) {
+                            LTE_FLOAT(strokeWidth) return false;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+}; // struct PathCacheEntry
+
+/**
+ * Alpha texture used to represent a path.
+ */
+struct PathTexture: public Texture {
+    PathTexture(): Texture() {
+    }
+
+    /**
+     * Left coordinate of the path bounds.
+     */
+    float left;
+    /**
+     * Top coordinate of the path bounds.
+     */
+    float top;
+    /**
+     * Offset to draw the path at the correct origin.
+     */
+    float offset;
+}; // struct PathTexture
+
+/**
+ * A simple LRU path cache. The cache has a maximum size expressed in bytes.
+ * Any texture added to the cache causing the cache to grow beyond the maximum
+ * allowed size will also cause the oldest texture to be kicked out.
+ */
+class PathCache: public OnEntryRemoved<PathCacheEntry, PathTexture*> {
+public:
+    PathCache();
+    PathCache(uint32_t maxByteSize);
+    ~PathCache();
+
+    /**
+     * Used as a callback when an entry is removed from the cache.
+     * Do not invoke directly.
+     */
+    void operator()(PathCacheEntry& path, PathTexture*& texture);
+
+    /**
+     * Returns the texture associated with the specified path. If the texture
+     * cannot be found in the cache, a new texture is generated.
+     */
+    PathTexture* get(SkPath* path, SkPaint* paint);
+    /**
+     * Clears the cache. This causes all textures to be deleted.
+     */
+    void clear();
+    /**
+     * Removes an entry.
+     */
+    void remove(SkPath* path);
+
+    /**
+     * Sets the maximum size of the cache in bytes.
+     */
+    void setMaxSize(uint32_t maxSize);
+    /**
+     * Returns the maximum size of the cache in bytes.
+     */
+    uint32_t getMaxSize();
+    /**
+     * Returns the current size of the cache in bytes.
+     */
+    uint32_t getSize();
+
+private:
+    /**
+     * Generates the texture from a bitmap into the specified texture structure.
+     */
+    void generateTexture(SkBitmap& bitmap, Texture* texture);
+
+    PathTexture* addTexture(const PathCacheEntry& entry, const SkPath *path, const SkPaint* paint);
+
+    void init();
+
+    GenerationCache<PathCacheEntry, PathTexture*> mCache;
+
+    uint32_t mSize;
+    uint32_t mMaxSize;
+    GLuint mMaxTextureSize;
+
+    /**
+     * Used to access mCache and mSize. All methods are accessed from a single
+     * thread except for remove().
+     */
+    mutable Mutex mLock;
+}; // class PathCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_PATH_CACHE_H
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
new file mode 100644
index 0000000..baed5fd7
--- /dev/null
+++ b/libs/hwui/Program.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "Program.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Base program
+///////////////////////////////////////////////////////////////////////////////
+
+Program::Program(const char* vertex, const char* fragment) {
+    vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
+    fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
+
+    id = glCreateProgram();
+    glAttachShader(id, vertexShader);
+    glAttachShader(id, fragmentShader);
+    glLinkProgram(id);
+
+    GLint status;
+    glGetProgramiv(id, GL_LINK_STATUS, &status);
+    if (status != GL_TRUE) {
+        GLint infoLen = 0;
+        glGetProgramiv(id, GL_INFO_LOG_LENGTH, &infoLen);
+        if (infoLen > 1) {
+            GLchar log[infoLen];
+            glGetProgramInfoLog(id, infoLen, 0, &log[0]);
+            LOGE("Error while linking shaders: %s", log);
+        }
+        glDeleteShader(vertexShader);
+        glDeleteShader(fragmentShader);
+        glDeleteProgram(id);
+    }
+
+    mUse = false;
+
+    position = addAttrib("position");
+    transform = addUniform("transform");
+}
+
+Program::~Program() {
+    glDeleteShader(vertexShader);
+    glDeleteShader(fragmentShader);
+    glDeleteProgram(id);
+}
+
+int Program::addAttrib(const char* name) {
+    int slot = glGetAttribLocation(id, name);
+    attributes.add(name, slot);
+    return slot;
+}
+
+int Program::getAttrib(const char* name) {
+    ssize_t index = attributes.indexOfKey(name);
+    if (index >= 0) {
+        return attributes.valueAt(index);
+    }
+    return addAttrib(name);
+}
+
+int Program::addUniform(const char* name) {
+    int slot = glGetUniformLocation(id, name);
+    uniforms.add(name, slot);
+    return slot;
+}
+
+int Program::getUniform(const char* name) {
+    ssize_t index = uniforms.indexOfKey(name);
+    if (index >= 0) {
+        return uniforms.valueAt(index);
+    }
+    return addUniform(name);
+}
+
+GLuint Program::buildShader(const char* source, GLenum type) {
+    GLuint shader = glCreateShader(type);
+    glShaderSource(shader, 1, &source, 0);
+    glCompileShader(shader);
+
+    GLint status;
+    glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+    if (status != GL_TRUE) {
+        // Some drivers return wrong values for GL_INFO_LOG_LENGTH
+        // use a fixed size instead
+        GLchar log[512];
+        glGetShaderInfoLog(shader, sizeof(log), 0, &log[0]);
+        LOGE("Error while compiling shader: %s", log);
+        glDeleteShader(shader);
+    }
+
+    return shader;
+}
+
+void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
+        const mat4& transformMatrix) {
+    mat4 t(projectionMatrix);
+    t.multiply(transformMatrix);
+    t.multiply(modelViewMatrix);
+
+    glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]);
+}
+
+void Program::setColor(const float r, const float g, const float b, const float a) {
+    glUniform4f(getUniform("color"), r, g, b, a);
+}
+
+void Program::use() {
+    glUseProgram(id);
+    mUse = true;
+
+    glEnableVertexAttribArray(position);
+}
+
+void Program::remove() {
+    mUse = false;
+
+    glDisableVertexAttribArray(position);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
new file mode 100644
index 0000000..5981662
--- /dev/null
+++ b/libs/hwui/Program.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_PROGRAM_H
+#define ANDROID_HWUI_PROGRAM_H
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <utils/KeyedVector.h>
+
+#include "Matrix.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A program holds a vertex and a fragment shader. It offers several utility
+ * methods to query attributes and uniforms.
+ */
+class Program {
+public:
+    /**
+     * Creates a new program with the specified vertex and fragment
+     * shaders sources.
+     */
+    Program(const char* vertex, const char* fragment);
+    virtual ~Program();
+
+    /**
+     * Binds this program to the GL context.
+     */
+    virtual void use();
+
+    /**
+     * Marks this program as unused. This will not unbind
+     * the program from the GL context.
+     */
+    virtual void remove();
+
+    /**
+     * Returns the OpenGL name of the specified attribute.
+     */
+    int getAttrib(const char* name);
+
+    /**
+     * Returns the OpenGL name of the specified uniform.
+     */
+    int getUniform(const char* name);
+
+    /**
+     * Indicates whether this program is currently in use with
+     * the GL context.
+     */
+    inline bool isInUse() const {
+        return mUse;
+    }
+
+    /**
+     * Binds the program with the specified projection, modelView and
+     * transform matrices.
+     */
+    void set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
+             const mat4& transformMatrix);
+
+    /**
+     * Sets the color associated with this shader.
+     */
+    void setColor(const float r, const float g, const float b, const float a);
+
+    /**
+     * Name of the position attribute.
+     */
+    int position;
+
+    /**
+     * Name of the transform uniform.
+     */
+    int transform;
+
+protected:
+    /**
+     * Adds an attribute with the specified name.
+     *
+     * @return The OpenGL name of the attribute.
+     */
+    int addAttrib(const char* name);
+
+    /**
+     * Adds a uniform with the specified name.
+     *
+     * @return The OpenGL name of the uniform.
+     */
+    int addUniform(const char* name);
+
+private:
+    /**
+     * Compiles the specified shader of the specified type.
+     *
+     * @return The name of the compiled shader.
+     */
+    GLuint buildShader(const char* source, GLenum type);
+
+    // Name of the OpenGL program
+    GLuint id;
+
+    // Name of the shaders
+    GLuint vertexShader;
+    GLuint fragmentShader;
+
+    // Keeps track of attributes and uniforms slots
+    KeyedVector<const char*, int> attributes;
+    KeyedVector<const char*, int> uniforms;
+
+    bool mUse;
+}; // class Program
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_PROGRAM_H
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
new file mode 100644
index 0000000..3cd85c8
--- /dev/null
+++ b/libs/hwui/ProgramCache.cpp
@@ -0,0 +1,615 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/String8.h>
+
+#include "ProgramCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define MODULATE_OP_NO_MODULATE 0
+#define MODULATE_OP_MODULATE 1
+#define MODULATE_OP_MODULATE_A8 2
+
+///////////////////////////////////////////////////////////////////////////////
+// Vertex shaders snippets
+///////////////////////////////////////////////////////////////////////////////
+
+const char* gVS_Header_Attributes =
+        "attribute vec4 position;\n";
+const char* gVS_Header_Attributes_TexCoords =
+        "attribute vec2 texCoords;\n";
+const char* gVS_Header_Uniforms =
+        "uniform mat4 transform;\n";
+const char* gVS_Header_Uniforms_HasGradient[3] = {
+        // Linear
+        "uniform mat4 screenSpace;\n",
+        // Circular
+        "uniform mat4 screenSpace;\n",
+        // Sweep
+        "uniform mat4 screenSpace;\n"
+};
+const char* gVS_Header_Uniforms_HasBitmap =
+        "uniform mat4 textureTransform;\n"
+        "uniform vec2 textureDimension;\n";
+const char* gVS_Header_Varyings_HasTexture =
+        "varying vec2 outTexCoords;\n";
+const char* gVS_Header_Varyings_HasBitmap =
+        "varying vec2 outBitmapTexCoords;\n";
+const char* gVS_Header_Varyings_HasGradient[3] = {
+        // Linear
+        "varying vec2 linear;\n",
+        // Circular
+        "varying vec2 circular;\n",
+        // Sweep
+        "varying vec2 sweep;\n"
+};
+const char* gVS_Main =
+        "\nvoid main(void) {\n";
+const char* gVS_Main_OutTexCoords =
+        "    outTexCoords = texCoords;\n";
+const char* gVS_Main_OutGradient[3] = {
+        // Linear
+        "    linear = vec2((screenSpace * position).x, 0.5);\n",
+        // Circular
+        "    circular = (screenSpace * position).xy;\n",
+        // Sweep
+        "    sweep = (screenSpace * position).xy;\n"
+};
+const char* gVS_Main_OutBitmapTexCoords =
+        "    outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
+const char* gVS_Main_Position =
+        "    gl_Position = transform * position;\n";
+const char* gVS_Footer =
+        "}\n\n";
+
+///////////////////////////////////////////////////////////////////////////////
+// Fragment shaders snippets
+///////////////////////////////////////////////////////////////////////////////
+
+const char* gFS_Header_Extension_FramebufferFetch =
+        "#extension GL_NV_shader_framebuffer_fetch : enable\n\n";
+const char* gFS_Header =
+        "precision mediump float;\n\n";
+const char* gFS_Uniforms_Color =
+        "uniform vec4 color;\n";
+const char* gFS_Uniforms_TextureSampler =
+        "uniform sampler2D sampler;\n";
+const char* gFS_Uniforms_GradientSampler[3] = {
+        // Linear
+        "uniform sampler2D gradientSampler;\n",
+        // Circular
+        "uniform sampler2D gradientSampler;\n",
+        // Sweep
+        "uniform sampler2D gradientSampler;\n"
+};
+const char* gFS_Uniforms_BitmapSampler =
+        "uniform sampler2D bitmapSampler;\n";
+const char* gFS_Uniforms_ColorOp[4] = {
+        // None
+        "",
+        // Matrix
+        "uniform mat4 colorMatrix;\n"
+        "uniform vec4 colorMatrixVector;\n",
+        // Lighting
+        "uniform vec4 lightingMul;\n"
+        "uniform vec4 lightingAdd;\n",
+        // PorterDuff
+        "uniform vec4 colorBlend;\n"
+};
+const char* gFS_Main =
+        "\nvoid main(void) {\n"
+        "    lowp vec4 fragColor;\n";
+
+// Fast cases
+const char* gFS_Fast_SingleColor =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = color;\n"
+        "}\n\n";
+const char* gFS_Fast_SingleTexture =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = texture2D(sampler, outTexCoords);\n"
+        "}\n\n";
+const char* gFS_Fast_SingleModulateTexture =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = color.a * texture2D(sampler, outTexCoords);\n"
+        "}\n\n";
+const char* gFS_Fast_SingleA8Texture =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = texture2D(sampler, outTexCoords);\n"
+        "}\n\n";
+const char* gFS_Fast_SingleModulateA8Texture =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = color * texture2D(sampler, outTexCoords).a;\n"
+        "}\n\n";
+const char* gFS_Fast_SingleGradient =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = texture2D(gradientSampler, linear);\n"
+        "}\n\n";
+const char* gFS_Fast_SingleModulateGradient =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = color.a * texture2D(gradientSampler, linear);\n"
+        "}\n\n";
+
+// General case
+const char* gFS_Main_FetchColor =
+        "    fragColor = color;\n";
+const char* gFS_Main_FetchTexture[2] = {
+        // Don't modulate
+        "    fragColor = texture2D(sampler, outTexCoords);\n",
+        // Modulate
+        "    fragColor = color * texture2D(sampler, outTexCoords);\n"
+};
+const char* gFS_Main_FetchA8Texture[2] = {
+        // Don't modulate
+        "    fragColor = texture2D(sampler, outTexCoords);\n",
+        // Modulate
+        "    fragColor = color * texture2D(sampler, outTexCoords).a;\n"
+};
+const char* gFS_Main_FetchGradient[3] = {
+        // Linear
+        "    vec4 gradientColor = texture2D(gradientSampler, linear);\n",
+        // Circular
+        "    float index = length(circular);\n"
+        "    vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n",
+        // Sweep
+        "    float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
+        "    vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n"
+};
+const char* gFS_Main_FetchBitmap =
+        "    vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
+const char* gFS_Main_FetchBitmapNpot =
+        "    vec4 bitmapColor = texture2D(bitmapSampler, wrap(outBitmapTexCoords));\n";
+const char* gFS_Main_BlendShadersBG =
+        "    fragColor = blendShaders(gradientColor, bitmapColor)";
+const char* gFS_Main_BlendShadersGB =
+        "    fragColor = blendShaders(bitmapColor, gradientColor)";
+const char* gFS_Main_BlendShaders_Modulate[3] = {
+        // Don't modulate
+        ";\n",
+        // Modulate
+        " * fragColor.a;\n",
+        // Modulate with alpha 8 texture
+        " * texture2D(sampler, outTexCoords).a;\n"
+};
+const char* gFS_Main_GradientShader_Modulate[3] = {
+        // Don't modulate
+        "    fragColor = gradientColor;\n",
+        // Modulate
+        "    fragColor = gradientColor * fragColor.a;\n",
+        // Modulate with alpha 8 texture
+        "    fragColor = gradientColor * texture2D(sampler, outTexCoords).a;\n"
+    };
+const char* gFS_Main_BitmapShader_Modulate[3] = {
+        // Don't modulate
+        "    fragColor = bitmapColor;\n",
+        // Modulate
+        "    fragColor = bitmapColor * fragColor.a;\n",
+        // Modulate with alpha 8 texture
+        "    fragColor = bitmapColor * texture2D(sampler, outTexCoords).a;\n"
+    };
+const char* gFS_Main_FragColor =
+        "    gl_FragColor = fragColor;\n";
+const char* gFS_Main_FragColor_Blend =
+        "    gl_FragColor = blendFramebuffer(fragColor, gl_LastFragColor);\n";
+const char* gFS_Main_FragColor_Blend_Swap =
+        "    gl_FragColor = blendFramebuffer(gl_LastFragColor, fragColor);\n";
+const char* gFS_Main_ApplyColorOp[4] = {
+        // None
+        "",
+        // Matrix
+        // TODO: Fix premultiplied alpha computations for color matrix
+        "    fragColor *= colorMatrix;\n"
+        "    fragColor += colorMatrixVector;\n"
+        "    fragColor.rgb *= fragColor.a;\n",
+        // Lighting
+        "    float lightingAlpha = fragColor.a;\n"
+        "    fragColor = min(fragColor * lightingMul + (lightingAdd * lightingAlpha), lightingAlpha);\n"
+        "    fragColor.a = lightingAlpha;\n",
+        // PorterDuff
+        "    fragColor = blendColors(colorBlend, fragColor);\n"
+};
+const char* gFS_Footer =
+        "}\n\n";
+
+///////////////////////////////////////////////////////////////////////////////
+// PorterDuff snippets
+///////////////////////////////////////////////////////////////////////////////
+
+const char* gBlendOps[18] = {
+        // Clear
+        "return vec4(0.0, 0.0, 0.0, 0.0);\n",
+        // Src
+        "return src;\n",
+        // Dst
+        "return dst;\n",
+        // SrcOver
+        "return src + dst * (1.0 - src.a);\n",
+        // DstOver
+        "return dst + src * (1.0 - dst.a);\n",
+        // SrcIn
+        "return src * dst.a;\n",
+        // DstIn
+        "return dst * src.a;\n",
+        // SrcOut
+        "return src * (1.0 - dst.a);\n",
+        // DstOut
+        "return dst * (1.0 - src.a);\n",
+        // SrcAtop
+        "return vec4(src.rgb * dst.a + (1.0 - src.a) * dst.rgb, dst.a);\n",
+        // DstAtop
+        "return vec4(dst.rgb * src.a + (1.0 - dst.a) * src.rgb, src.a);\n",
+        // Xor
+        "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, "
+                "src.a + dst.a - 2.0 * src.a * dst.a);\n",
+        // Add
+        "return min(src + dst, 1.0);\n",
+        // Multiply
+        "return src * dst;\n",
+        // Screen
+        "return src + dst - src * dst;\n",
+        // Overlay
+        "return clamp(vec4(mix("
+                "2.0 * src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), "
+                "src.a * dst.a - 2.0 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), "
+                "step(dst.a, 2.0 * dst.rgb)), "
+                "src.a + dst.a - src.a * dst.a), 0.0, 1.0);\n",
+        // Darken
+        "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + "
+                "min(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n",
+        // Lighten
+        "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + "
+                "max(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n",
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructors
+///////////////////////////////////////////////////////////////////////////////
+
+ProgramCache::ProgramCache() {
+}
+
+ProgramCache::~ProgramCache() {
+    clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache management
+///////////////////////////////////////////////////////////////////////////////
+
+void ProgramCache::clear() {
+    size_t count = mCache.size();
+    for (size_t i = 0; i < count; i++) {
+        delete mCache.valueAt(i);
+    }
+    mCache.clear();
+}
+
+Program* ProgramCache::get(const ProgramDescription& description) {
+    programid key = description.key();
+    ssize_t index = mCache.indexOfKey(key);
+    Program* program = NULL;
+    if (index < 0) {
+        description.log("Could not find program");
+        program = generateProgram(description, key);
+        mCache.add(key, program);
+    } else {
+        program = mCache.valueAt(index);
+    }
+    return program;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Program generation
+///////////////////////////////////////////////////////////////////////////////
+
+Program* ProgramCache::generateProgram(const ProgramDescription& description, programid key) {
+    String8 vertexShader = generateVertexShader(description);
+    String8 fragmentShader = generateFragmentShader(description);
+
+    Program* program = new Program(vertexShader.string(), fragmentShader.string());
+    return program;
+}
+
+String8 ProgramCache::generateVertexShader(const ProgramDescription& description) {
+    // Add attributes
+    String8 shader(gVS_Header_Attributes);
+    if (description.hasTexture) {
+        shader.append(gVS_Header_Attributes_TexCoords);
+    }
+    // Uniforms
+    shader.append(gVS_Header_Uniforms);
+    if (description.hasGradient) {
+        shader.append(gVS_Header_Uniforms_HasGradient[description.gradientType]);
+    }
+    if (description.hasBitmap) {
+        shader.append(gVS_Header_Uniforms_HasBitmap);
+    }
+    // Varyings
+    if (description.hasTexture) {
+        shader.append(gVS_Header_Varyings_HasTexture);
+    }
+    if (description.hasGradient) {
+        shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
+    }
+    if (description.hasBitmap) {
+        shader.append(gVS_Header_Varyings_HasBitmap);
+    }
+
+    // Begin the shader
+    shader.append(gVS_Main); {
+        if (description.hasTexture) {
+            shader.append(gVS_Main_OutTexCoords);
+        }
+        if (description.hasGradient) {
+            shader.append(gVS_Main_OutGradient[description.gradientType]);
+        }
+        if (description.hasBitmap) {
+            shader.append(gVS_Main_OutBitmapTexCoords);
+        }
+        // Output transformed position
+        shader.append(gVS_Main_Position);
+    }
+    // End the shader
+    shader.append(gVS_Footer);
+
+    PROGRAM_LOGD("*** Generated vertex shader:\n\n%s", shader.string());
+
+    return shader;
+}
+
+String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) {
+    // Set the default precision
+    String8 shader;
+
+    const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode;
+    if (blendFramebuffer) {
+        shader.append(gFS_Header_Extension_FramebufferFetch);
+    }
+
+    shader.append(gFS_Header);
+
+    // Varyings
+    if (description.hasTexture) {
+        shader.append(gVS_Header_Varyings_HasTexture);
+    }
+    if (description.hasGradient) {
+        shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
+    }
+    if (description.hasBitmap) {
+        shader.append(gVS_Header_Varyings_HasBitmap);
+    }
+
+    // Uniforms
+    int modulateOp = MODULATE_OP_NO_MODULATE;
+    const bool singleColor = !description.hasTexture &&
+            !description.hasGradient && !description.hasBitmap;
+
+    if (description.modulate || singleColor) {
+        shader.append(gFS_Uniforms_Color);
+        if (!singleColor) modulateOp = MODULATE_OP_MODULATE;
+    }
+    if (description.hasTexture) {
+        shader.append(gFS_Uniforms_TextureSampler);
+    }
+    if (description.hasGradient) {
+        shader.append(gFS_Uniforms_GradientSampler[description.gradientType]);
+    }
+
+    // Optimization for common cases
+    if (!blendFramebuffer && description.colorOp == ProgramDescription::kColorNone) {
+        bool fast = false;
+
+        const bool noShader = !description.hasGradient && !description.hasBitmap;
+        const bool singleTexture = description.hasTexture &&
+                !description.hasAlpha8Texture && noShader;
+        const bool singleA8Texture = description.hasTexture &&
+                description.hasAlpha8Texture && noShader;
+        const bool singleGradient = !description.hasTexture &&
+                description.hasGradient && !description.hasBitmap &&
+                description.gradientType == ProgramDescription::kGradientLinear;
+
+        if (singleColor) {
+            shader.append(gFS_Fast_SingleColor);
+            fast = true;
+        } else if (singleTexture) {
+            if (!description.modulate) {
+                shader.append(gFS_Fast_SingleTexture);
+            } else {
+                shader.append(gFS_Fast_SingleModulateTexture);
+            }
+            fast = true;
+        } else if (singleA8Texture) {
+            if (!description.modulate) {
+                shader.append(gFS_Fast_SingleA8Texture);
+            } else {
+                shader.append(gFS_Fast_SingleModulateA8Texture);
+            }
+            fast = true;
+        } else if (singleGradient) {
+            if (!description.modulate) {
+                shader.append(gFS_Fast_SingleGradient);
+            } else {
+                shader.append(gFS_Fast_SingleModulateGradient);
+            }
+            fast = true;
+        }
+
+        if (fast) {
+            if (DEBUG_PROGRAM_CACHE) {
+                PROGRAM_LOGD("*** Fast case:\n");
+                PROGRAM_LOGD("*** Generated fragment shader:\n\n");
+                printLongString(shader);
+            }
+
+            return shader;
+        }
+    }
+
+    if (description.hasBitmap) {
+        shader.append(gFS_Uniforms_BitmapSampler);
+    }
+    shader.append(gFS_Uniforms_ColorOp[description.colorOp]);
+
+    // Generate required functions
+    if (description.hasGradient && description.hasBitmap) {
+        generateBlend(shader, "blendShaders", description.shadersMode);
+    }
+    if (description.colorOp == ProgramDescription::kColorBlend) {
+        generateBlend(shader, "blendColors", description.colorMode);
+    }
+    if (blendFramebuffer) {
+        generateBlend(shader, "blendFramebuffer", description.framebufferMode);
+    }
+    if (description.isBitmapNpot) {
+        generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
+    }
+
+    // Begin the shader
+    shader.append(gFS_Main); {
+        // Stores the result in fragColor directly
+        if (description.hasTexture) {
+            if (description.hasAlpha8Texture) {
+                if (!description.hasGradient && !description.hasBitmap) {
+                    shader.append(gFS_Main_FetchA8Texture[modulateOp]);
+                }
+            } else {
+                shader.append(gFS_Main_FetchTexture[modulateOp]);
+            }
+        } else {
+            if ((!description.hasGradient && !description.hasBitmap) || description.modulate) {
+                shader.append(gFS_Main_FetchColor);
+            }
+        }
+        if (description.hasGradient) {
+            shader.append(gFS_Main_FetchGradient[description.gradientType]);
+        }
+        if (description.hasBitmap) {
+            if (!description.isBitmapNpot) {
+                shader.append(gFS_Main_FetchBitmap);
+            } else {
+                shader.append(gFS_Main_FetchBitmapNpot);
+            }
+        }
+        // Case when we have two shaders set
+        if (description.hasGradient && description.hasBitmap) {
+            int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
+            if (description.isBitmapFirst) {
+                shader.append(gFS_Main_BlendShadersBG);
+            } else {
+                shader.append(gFS_Main_BlendShadersGB);
+            }
+            shader.append(gFS_Main_BlendShaders_Modulate[op]);
+        } else {
+            if (description.hasGradient) {
+                int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
+                shader.append(gFS_Main_GradientShader_Modulate[op]);
+            } else if (description.hasBitmap) {
+                int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp;
+                shader.append(gFS_Main_BitmapShader_Modulate[op]);
+            }
+        }
+        // Apply the color op if needed
+        shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
+        // Output the fragment
+        if (!blendFramebuffer) {
+            shader.append(gFS_Main_FragColor);
+        } else {
+            shader.append(!description.swapSrcDst ?
+                    gFS_Main_FragColor_Blend : gFS_Main_FragColor_Blend_Swap);
+        }
+    }
+    // End the shader
+    shader.append(gFS_Footer);
+
+    if (DEBUG_PROGRAM_CACHE) {
+        PROGRAM_LOGD("*** Generated fragment shader:\n\n");
+        printLongString(shader);
+    }
+
+    return shader;
+}
+
+void ProgramCache::generateBlend(String8& shader, const char* name, SkXfermode::Mode mode) {
+    shader.append("\nvec4 ");
+    shader.append(name);
+    shader.append("(vec4 src, vec4 dst) {\n");
+    shader.append("    ");
+    shader.append(gBlendOps[mode]);
+    shader.append("}\n");
+}
+
+void ProgramCache::generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT) {
+    shader.append("\nvec2 wrap(vec2 texCoords) {\n");
+    if (wrapS == GL_MIRRORED_REPEAT) {
+        shader.append("    float xMod2 = mod(texCoords.x, 2.0);\n");
+        shader.append("    if (xMod2 > 1.0) xMod2 = 2.0 - xMod2;\n");
+    }
+    if (wrapT == GL_MIRRORED_REPEAT) {
+        shader.append("    float yMod2 = mod(texCoords.y, 2.0);\n");
+        shader.append("    if (yMod2 > 1.0) yMod2 = 2.0 - yMod2;\n");
+    }
+    shader.append("    return vec2(");
+    switch (wrapS) {
+        case GL_CLAMP_TO_EDGE:
+            shader.append("texCoords.x");
+            break;
+        case GL_REPEAT:
+            shader.append("mod(texCoords.x, 1.0)");
+            break;
+        case GL_MIRRORED_REPEAT:
+            shader.append("xMod2");
+            break;
+    }
+    shader.append(", ");
+    switch (wrapT) {
+        case GL_CLAMP_TO_EDGE:
+            shader.append("texCoords.y");
+            break;
+        case GL_REPEAT:
+            shader.append("mod(texCoords.y, 1.0)");
+            break;
+        case GL_MIRRORED_REPEAT:
+            shader.append("yMod2");
+            break;
+    }
+    shader.append(");\n");
+    shader.append("}\n");
+}
+
+void ProgramCache::printLongString(const String8& shader) const {
+    ssize_t index = 0;
+    ssize_t lastIndex = 0;
+    const char* str = shader.string();
+    while ((index = shader.find("\n", index)) > -1) {
+        String8 line(str, index - lastIndex);
+        if (line.length() == 0) line.append("\n");
+        PROGRAM_LOGD("%s", line.string());
+        index++;
+        str += (index - lastIndex);
+        lastIndex = index;
+    }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
new file mode 100644
index 0000000..186e869
--- /dev/null
+++ b/libs/hwui/ProgramCache.h
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_PROGRAM_CACHE_H
+#define ANDROID_HWUI_PROGRAM_CACHE_H
+
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <GLES2/gl2.h>
+
+#include <SkXfermode.h>
+
+#include "Program.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_PROGRAM_CACHE 0
+
+// Debug
+#if DEBUG_PROGRAM_CACHE
+    #define PROGRAM_LOGD(...) LOGD(__VA_ARGS__)
+#else
+    #define PROGRAM_LOGD(...)
+#endif
+
+// TODO: This should be set in properties
+#define PANEL_BIT_DEPTH 20
+#define COLOR_COMPONENT_THRESHOLD (1.0f - (0.5f / PANEL_BIT_DEPTH))
+#define COLOR_COMPONENT_INV_THRESHOLD (0.5f / PANEL_BIT_DEPTH)
+
+#define PROGRAM_KEY_TEXTURE 0x1
+#define PROGRAM_KEY_A8_TEXTURE 0x2
+#define PROGRAM_KEY_BITMAP 0x4
+#define PROGRAM_KEY_GRADIENT 0x8
+#define PROGRAM_KEY_BITMAP_FIRST 0x10
+#define PROGRAM_KEY_COLOR_MATRIX 0x20
+#define PROGRAM_KEY_COLOR_LIGHTING 0x40
+#define PROGRAM_KEY_COLOR_BLEND 0x80
+#define PROGRAM_KEY_BITMAP_NPOT 0x100
+#define PROGRAM_KEY_SWAP_SRC_DST 0x2000
+
+#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
+#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
+
+// Encode the xfermodes on 6 bits
+#define PROGRAM_MAX_XFERMODE 0x1f
+#define PROGRAM_XFERMODE_SHADER_SHIFT 26
+#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
+#define PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT 14
+
+#define PROGRAM_BITMAP_WRAPS_SHIFT 9
+#define PROGRAM_BITMAP_WRAPT_SHIFT 11
+
+#define PROGRAM_GRADIENT_TYPE_SHIFT 33
+#define PROGRAM_MODULATE 35
+
+///////////////////////////////////////////////////////////////////////////////
+// Types
+///////////////////////////////////////////////////////////////////////////////
+
+typedef uint64_t programid;
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Describe the features required for a given program. The features
+ * determine the generation of both the vertex and fragment shaders.
+ * A ProgramDescription must be used in conjunction with a ProgramCache.
+ */
+struct ProgramDescription {
+    enum ColorModifier {
+        kColorNone,
+        kColorMatrix,
+        kColorLighting,
+        kColorBlend
+    };
+
+    enum Gradient {
+        kGradientLinear,
+        kGradientCircular,
+        kGradientSweep
+    };
+
+    ProgramDescription():
+        hasTexture(false), hasAlpha8Texture(false), modulate(false),
+        hasBitmap(false), isBitmapNpot(false), hasGradient(false),
+        gradientType(kGradientLinear),
+        shadersMode(SkXfermode::kClear_Mode), isBitmapFirst(false),
+        bitmapWrapS(GL_CLAMP_TO_EDGE), bitmapWrapT(GL_CLAMP_TO_EDGE),
+        colorOp(kColorNone), colorMode(SkXfermode::kClear_Mode),
+        framebufferMode(SkXfermode::kClear_Mode), swapSrcDst(false) {
+    }
+
+    // Texturing
+    bool hasTexture;
+    bool hasAlpha8Texture;
+
+    // Modulate, this should only be set when setColor() return true
+    bool modulate;
+
+    // Shaders
+    bool hasBitmap;
+    bool isBitmapNpot;
+
+    bool hasGradient;
+    Gradient gradientType;
+
+    SkXfermode::Mode shadersMode;
+
+    bool isBitmapFirst;
+    GLenum bitmapWrapS;
+    GLenum bitmapWrapT;
+
+    // Color operations
+    ColorModifier colorOp;
+    SkXfermode::Mode colorMode;
+
+    // Framebuffer blending (requires Extensions.hasFramebufferFetch())
+    // Ignored for all values < SkXfermode::kPlus_Mode
+    SkXfermode::Mode framebufferMode;
+    bool swapSrcDst;
+
+    /**
+     * Indicates, for a given color, whether color modulation is required in
+     * the fragment shader. When this method returns true, the program should
+     * be provided with a modulation color.
+     */
+    bool setColor(const float r, const float g, const float b, const float a) {
+        modulate = a < COLOR_COMPONENT_THRESHOLD || r < COLOR_COMPONENT_THRESHOLD ||
+                g < COLOR_COMPONENT_THRESHOLD || b < COLOR_COMPONENT_THRESHOLD;
+        return modulate;
+    }
+
+    /**
+     * Indicates, for a given color, whether color modulation is required in
+     * the fragment shader. When this method returns true, the program should
+     * be provided with a modulation color.
+     */
+    bool setAlpha8Color(const float r, const float g, const float b, const float a) {
+        modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD ||
+                g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD;
+        return modulate;
+    }
+
+    /**
+     * Computes the unique key identifying this program.
+     */
+    programid key() const {
+        programid key = 0;
+        if (hasTexture) key |= PROGRAM_KEY_TEXTURE;
+        if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE;
+        if (hasBitmap) {
+            key |= PROGRAM_KEY_BITMAP;
+            if (isBitmapNpot) {
+                key |= PROGRAM_KEY_BITMAP_NPOT;
+                key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT;
+                key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT;
+            }
+        }
+        if (hasGradient) key |= PROGRAM_KEY_GRADIENT;
+        key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT;
+        if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST;
+        if (hasBitmap && hasGradient) {
+            key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT;
+        }
+        switch (colorOp) {
+            case kColorMatrix:
+                key |= PROGRAM_KEY_COLOR_MATRIX;
+                break;
+            case kColorLighting:
+                key |= PROGRAM_KEY_COLOR_LIGHTING;
+                break;
+            case kColorBlend:
+                key |= PROGRAM_KEY_COLOR_BLEND;
+                key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT;
+                break;
+            case kColorNone:
+                break;
+        }
+        key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
+        if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
+        if (modulate) key |= programid(0x1) << PROGRAM_MODULATE;
+        return key;
+    }
+
+    /**
+     * Logs the specified message followed by the key identifying this program.
+     */
+    void log(const char* message) const {
+        programid k = key();
+        PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32),
+                uint32_t(k & 0xffffffff));
+    }
+
+private:
+    inline uint32_t getEnumForWrap(GLenum wrap) const {
+        switch (wrap) {
+            case GL_CLAMP_TO_EDGE:
+                return 0;
+            case GL_REPEAT:
+                return 1;
+            case GL_MIRRORED_REPEAT:
+                return 2;
+        }
+        return 0;
+    }
+
+}; // struct ProgramDescription
+
+/**
+ * Generates and caches program. Programs are generated based on
+ * ProgramDescriptions.
+ */
+class ProgramCache {
+public:
+    ProgramCache();
+    ~ProgramCache();
+
+    Program* get(const ProgramDescription& description);
+
+    void clear();
+
+private:
+    Program* generateProgram(const ProgramDescription& description, programid key);
+    String8 generateVertexShader(const ProgramDescription& description);
+    String8 generateFragmentShader(const ProgramDescription& description);
+    void generateBlend(String8& shader, const char* name, SkXfermode::Mode mode);
+    void generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT);
+
+    void printLongString(const String8& shader) const;
+
+    KeyedVector<programid, Program*> mCache;
+}; // class ProgramCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_PROGRAM_CACHE_H
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
new file mode 100644
index 0000000..813392b
--- /dev/null
+++ b/libs/hwui/Properties.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_PROPERTIES_H
+#define ANDROID_HWUI_PROPERTIES_H
+
+#include <cutils/properties.h>
+
+/**
+ * This file contains the list of system properties used to configure
+ * the OpenGLRenderer.
+ */
+
+// These properties are defined in mega-bytes
+#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
+#define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
+#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
+#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
+#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
+#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
+
+// These properties are defined in pixels
+#define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width"
+#define PROPERTY_TEXT_CACHE_HEIGHT "ro.hwui.text_cache_height"
+
+// Gamma (>= 1.0, <= 10.0)
+#define PROPERTY_TEXT_GAMMA "ro.text_gamma"
+#define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "ro.text_gamma.black_threshold"
+#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "ro.text_gamma.white_threshold"
+
+// Converts a number of mega-bytes into bytes
+#define MB(s) s * 1024 * 1024
+
+#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
+#define DEFAULT_LAYER_CACHE_SIZE 8.0f
+#define DEFAULT_PATH_CACHE_SIZE 4.0f
+#define DEFAULT_PATCH_CACHE_SIZE 512
+#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
+#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
+#define DEFAULT_FBO_CACHE_SIZE 12
+
+#define DEFAULT_TEXT_GAMMA 1.4f
+#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
+#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192
+
+#endif // ANDROID_HWUI_PROPERTIES_H
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
new file mode 100644
index 0000000..71951b7
--- /dev/null
+++ b/libs/hwui/Rect.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_RECT_H
+#define ANDROID_HWUI_RECT_H
+
+#include <cmath>
+
+#include <utils/Log.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Structs
+///////////////////////////////////////////////////////////////////////////////
+
+struct Rect {
+    float left;
+    float top;
+    float right;
+    float bottom;
+
+    // Used by Region
+    typedef float value_type;
+
+    inline Rect():
+            left(0),
+            top(0),
+            right(0),
+            bottom(0) {
+    }
+
+    inline Rect(float left, float top, float right, float bottom):
+            left(left),
+            top(top),
+            right(right),
+            bottom(bottom) {
+    }
+
+    inline Rect(float width, float height):
+            left(0.0f),
+            top(0.0f),
+            right(width),
+            bottom(height) {
+    }
+
+    inline Rect(const Rect& r) {
+        set(r);
+    }
+
+    inline Rect(Rect& r) {
+        set(r);
+    }
+
+    Rect& operator=(const Rect& r) {
+        set(r);
+        return *this;
+    }
+
+    Rect& operator=(Rect& r) {
+        set(r);
+        return *this;
+    }
+
+    friend int operator==(const Rect& a, const Rect& b) {
+        return !memcmp(&a, &b, sizeof(a));
+    }
+
+    friend int operator!=(const Rect& a, const Rect& b) {
+        return memcmp(&a, &b, sizeof(a));
+    }
+
+    inline void clear() {
+        left = top = right = bottom = 0.0f;
+    }
+
+    inline bool isEmpty() const {
+        return left >= right || top >= bottom;
+    }
+
+    inline void setEmpty() {
+        left = top = right = bottom = 0.0f;
+    }
+
+    inline void set(float left, float top, float right, float bottom) {
+        this->left = left;
+        this->right = right;
+        this->top = top;
+        this->bottom = bottom;
+    }
+
+    inline void set(const Rect& r) {
+        set(r.left, r.top, r.right, r.bottom);
+    }
+
+    inline float getWidth() const {
+        return right - left;
+    }
+
+    inline float getHeight() const {
+        return bottom - top;
+    }
+
+    bool intersects(float left, float top, float right, float bottom) const {
+        return left < right && top < bottom &&
+                this->left < this->right && this->top < this->bottom &&
+                this->left < right && left < this->right &&
+                this->top < bottom && top < this->bottom;
+    }
+
+    bool intersects(const Rect& r) const {
+        return intersects(r.left, r.top, r.right, r.bottom);
+    }
+
+    bool intersect(float left, float top, float right, float bottom) {
+        if (left < right && top < bottom && !this->isEmpty() &&
+                this->left < right && left < this->right &&
+                this->top < bottom && top < this->bottom) {
+
+            if (this->left < left) this->left = left;
+            if (this->top < top) this->top = top;
+            if (this->right > right) this->right = right;
+            if (this->bottom > bottom) this->bottom = bottom;
+
+            return true;
+        }
+        return false;
+    }
+
+    bool intersect(const Rect& r) {
+        return intersect(r.left, r.top, r.right, r.bottom);
+    }
+
+    bool unionWith(const Rect& r) {
+        if (r.left < r.right && r.top < r.bottom) {
+            if (left < right && top < bottom) {
+                if (left > r.left) left = r.left;
+                if (top > r.top) top = r.top;
+                if (right < r.right) right = r.right;
+                if (bottom < r.bottom) bottom = r.bottom;
+                return true;
+            } else {
+                left = r.left;
+                top = r.top;
+                right = r.right;
+                bottom = r.bottom;
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void translate(float dx, float dy) {
+        left += dx;
+        right += dx;
+        top += dy;
+        bottom += dy;
+    }
+
+    void snapToPixelBoundaries() {
+        left = floorf(left + 0.5f);
+        top = floorf(top + 0.5f);
+        right = floorf(right + 0.5f);
+        bottom = floorf(bottom + 0.5f);
+    }
+
+    void dump() const {
+        LOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom);
+    }
+
+}; // struct Rect
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_RECT_H
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
new file mode 100644
index 0000000..47c5d48
--- /dev/null
+++ b/libs/hwui/ResourceCache.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <SkPixelRef.h>
+#include "ResourceCache.h"
+#include "Caches.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Resource cache
+///////////////////////////////////////////////////////////////////////////////
+
+void ResourceCache::logCache() {
+    LOGD("ResourceCache: cacheReport:");
+    for (size_t i = 0; i < mCache->size(); ++i) {
+        ResourceReference* ref = mCache->valueAt(i);
+        LOGD("  ResourceCache: mCache(%d): resource, ref = 0x%p, 0x%p",
+                i, mCache->keyAt(i), mCache->valueAt(i));
+        LOGD("  ResourceCache: mCache(%d): refCount, recycled, destroyed, type = %d, %d, %d, %d",
+                i, ref->refCount, ref->recycled, ref->destroyed, ref->resourceType);
+    }
+}
+
+ResourceCache::ResourceCache() {
+    mCache = new KeyedVector<void *, ResourceReference *>();
+}
+
+ResourceCache::~ResourceCache() {
+    delete mCache;
+}
+
+void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
+    for (size_t i = 0; i < mCache->size(); ++i) {
+        void* ref = mCache->valueAt(i);
+    }
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL || mCache->size() == 0) {
+        ref = new ResourceReference(resourceType);
+        mCache->add(resource, ref);
+    }
+    ref->refCount++;
+}
+
+void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) {
+    bitmapResource->pixelRef()->safeRef();
+    bitmapResource->getColorTable()->safeRef();
+    incrementRefcount((void*)bitmapResource, kBitmap);
+}
+
+void ResourceCache::incrementRefcount(SkiaShader* shaderResource) {
+    shaderResource->getSkShader()->safeRef();
+    incrementRefcount((void*)shaderResource, kShader);
+}
+
+void ResourceCache::incrementRefcount(SkiaColorFilter* filterResource) {
+    filterResource->getSkColorFilter()->safeRef();
+    incrementRefcount((void*)filterResource, kColorFilter);
+}
+
+void ResourceCache::decrementRefcount(void* resource) {
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL) {
+        // Should not get here - shouldn't get a call to decrement if we're not yet tracking it
+        return;
+    }
+    ref->refCount--;
+    if (ref->refCount == 0) {
+        deleteResourceReference(resource, ref);
+    }
+}
+
+void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) {
+    bitmapResource->pixelRef()->safeUnref();
+    bitmapResource->getColorTable()->safeUnref();
+    decrementRefcount((void*)bitmapResource);
+}
+
+void ResourceCache::decrementRefcount(SkiaShader* shaderResource) {
+    shaderResource->getSkShader()->safeUnref();
+    decrementRefcount((void*)shaderResource);
+}
+
+void ResourceCache::decrementRefcount(SkiaColorFilter* filterResource) {
+    filterResource->getSkColorFilter()->safeUnref();
+    decrementRefcount((void*)filterResource);
+}
+
+void ResourceCache::recycle(SkBitmap* resource) {
+    if (mCache->indexOfKey(resource) < 0) {
+        // not tracking this resource; just recycle the pixel data
+        resource->setPixels(NULL, NULL);
+        return;
+    }
+    recycle((void*) resource);
+}
+
+void ResourceCache::recycle(void* resource) {
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL) {
+        // Should not get here - shouldn't get a call to recycle if we're not yet tracking it
+        return;
+    }
+    ref->recycled = true;
+    if (ref->refCount == 0) {
+        deleteResourceReference(resource, ref);
+    }
+}
+
+void ResourceCache::destructor(SkBitmap* resource) {
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL) {
+        // If we're not tracking this resource, just delete it
+        if (Caches::hasInstance()) {
+            Caches::getInstance().textureCache.remove(resource);
+        }
+        delete resource;
+        return;
+    }
+    ref->destroyed = true;
+    if (ref->refCount == 0) {
+        deleteResourceReference(resource, ref);
+        return;
+    }
+}
+
+void ResourceCache::destructor(SkiaShader* resource) {
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL) {
+        // If we're not tracking this resource, just delete it
+        if (Caches::hasInstance()) {
+            Caches::getInstance().gradientCache.remove(resource->getSkShader());
+        }
+        delete resource;
+        return;
+    }
+    ref->destroyed = true;
+    if (ref->refCount == 0) {
+        deleteResourceReference(resource, ref);
+        return;
+    }
+}
+
+void ResourceCache::destructor(SkiaColorFilter* resource) {
+    ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL;
+    if (ref == NULL) {
+        // If we're not tracking this resource, just delete it
+        delete resource;
+        return;
+    }
+    ref->destroyed = true;
+    if (ref->refCount == 0) {
+        deleteResourceReference(resource, ref);
+        return;
+    }
+}
+
+void ResourceCache::deleteResourceReference(void* resource, ResourceReference* ref) {
+    if (ref->recycled && ref->resourceType == kBitmap) {
+        ((SkBitmap*) resource)->setPixels(NULL, NULL);
+    }
+    if (ref->destroyed) {
+        switch (ref->resourceType) {
+            case kBitmap:
+            {
+                SkBitmap* bitmap = (SkBitmap*)resource;
+                if (Caches::hasInstance()) {
+                    Caches::getInstance().textureCache.remove(bitmap);
+                }
+                delete bitmap;
+            }
+            break;
+            case kShader:
+            {
+                SkiaShader* shader = (SkiaShader*)resource;
+                if (Caches::hasInstance()) {
+                    Caches::getInstance().gradientCache.remove(shader->getSkShader());
+                }
+                delete shader;
+            }
+            break;
+            case kColorFilter:
+            {
+                SkiaColorFilter* filter = (SkiaColorFilter*)resource;
+                delete filter;
+            }
+            break;
+        }
+    }
+    mCache->removeItem(resource);
+    delete ref;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
new file mode 100644
index 0000000..2256fd1
--- /dev/null
+++ b/libs/hwui/ResourceCache.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_RESOURCE_CACHE_H
+#define ANDROID_HWUI_RESOURCE_CACHE_H
+
+#include <SkBitmap.h>
+#include <SkiaColorFilter.h>
+#include <SkiaShader.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Type of Resource being cached
+ */
+enum ResourceType {
+    kBitmap,
+    kShader,
+    kColorFilter,
+};
+
+class ResourceReference {
+public:
+
+    ResourceReference() { refCount = 0; recycled = false; destroyed = false;}
+    ResourceReference(ResourceType type) {
+        refCount = 0; recycled = false; destroyed = false; resourceType = type;
+    }
+
+    int refCount;
+    bool recycled;
+    bool destroyed;
+    ResourceType resourceType;
+};
+
+class ResourceCache {
+    KeyedVector<void *, ResourceReference *>* mCache;
+public:
+    ResourceCache();
+    ~ResourceCache();
+    void incrementRefcount(SkBitmap* resource);
+    void incrementRefcount(SkiaShader* resource);
+    void incrementRefcount(SkiaColorFilter* resource);
+    void incrementRefcount(const void* resource, ResourceType resourceType);
+    void decrementRefcount(void* resource);
+    void decrementRefcount(SkBitmap* resource);
+    void decrementRefcount(SkiaShader* resource);
+    void decrementRefcount(SkiaColorFilter* resource);
+    void recycle(void* resource);
+    void recycle(SkBitmap* resource);
+    void destructor(SkBitmap* resource);
+    void destructor(SkiaShader* resource);
+    void destructor(SkiaColorFilter* resource);
+private:
+    void deleteResourceReference(void* resource, ResourceReference* ref);
+    void incrementRefcount(void* resource, ResourceType resourceType);
+    void logCache();
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_RESOURCE_CACHE_H
diff --git a/libs/hwui/SkiaColorFilter.cpp b/libs/hwui/SkiaColorFilter.cpp
new file mode 100644
index 0000000..91b1c32
--- /dev/null
+++ b/libs/hwui/SkiaColorFilter.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaColorFilter.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Base color filter
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaColorFilter::SkiaColorFilter(SkColorFilter *skFilter, Type type, bool blend):
+        mType(type), mBlend(blend), mSkFilter(skFilter) {
+}
+
+SkiaColorFilter::~SkiaColorFilter() {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Color matrix filter
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaColorMatrixFilter::SkiaColorMatrixFilter(SkColorFilter *skFilter, float* matrix, float* vector):
+        SkiaColorFilter(skFilter, kColorMatrix, true), mMatrix(matrix), mVector(vector) {
+}
+
+SkiaColorMatrixFilter::~SkiaColorMatrixFilter() {
+    delete[] mMatrix;
+    delete[] mVector;
+}
+
+void SkiaColorMatrixFilter::describe(ProgramDescription& description,
+        const Extensions& extensions) {
+    description.colorOp = ProgramDescription::kColorMatrix;
+}
+
+void SkiaColorMatrixFilter::setupProgram(Program* program) {
+    glUniformMatrix4fv(program->getUniform("colorMatrix"), 1, GL_FALSE, &mMatrix[0]);
+    glUniform4fv(program->getUniform("colorMatrixVector"), 1, mVector);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Lighting color filter
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaLightingFilter::SkiaLightingFilter(SkColorFilter *skFilter, int multiply, int add):
+        SkiaColorFilter(skFilter, kLighting, true) {
+    mMulR = ((multiply >> 16) & 0xFF) / 255.0f;
+    mMulG = ((multiply >>  8) & 0xFF) / 255.0f;
+    mMulB = ((multiply      ) & 0xFF) / 255.0f;
+
+    mAddR = ((add >> 16) & 0xFF) / 255.0f;
+    mAddG = ((add >>  8) & 0xFF) / 255.0f;
+    mAddB = ((add      ) & 0xFF) / 255.0f;
+}
+
+void SkiaLightingFilter::describe(ProgramDescription& description, const Extensions& extensions) {
+    description.colorOp = ProgramDescription::kColorLighting;
+}
+
+void SkiaLightingFilter::setupProgram(Program* program) {
+    glUniform4f(program->getUniform("lightingMul"), mMulR, mMulG, mMulB, 1.0f);
+    glUniform4f(program->getUniform("lightingAdd"), mAddR, mAddG, mAddB, 0.0f);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Blend color filter
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaBlendFilter::SkiaBlendFilter(SkColorFilter *skFilter, int color, SkXfermode::Mode mode):
+        SkiaColorFilter(skFilter, kBlend, true), mMode(mode) {
+    const int alpha = (color >> 24) & 0xFF;
+    mA = alpha / 255.0f;
+    mR = mA * ((color >> 16) & 0xFF) / 255.0f;
+    mG = mA * ((color >>  8) & 0xFF) / 255.0f;
+    mB = mA * ((color      ) & 0xFF) / 255.0f;
+}
+
+void SkiaBlendFilter::describe(ProgramDescription& description, const Extensions& extensions) {
+    description.colorOp = ProgramDescription::kColorBlend;
+    description.colorMode = mMode;
+}
+
+void SkiaBlendFilter::setupProgram(Program* program) {
+    glUniform4f(program->getUniform("colorBlend"), mR, mG, mB, mA);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SkiaColorFilter.h b/libs/hwui/SkiaColorFilter.h
new file mode 100644
index 0000000..bf45e13
--- /dev/null
+++ b/libs/hwui/SkiaColorFilter.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_SKIA_COLOR_FILTER_H
+#define ANDROID_HWUI_SKIA_COLOR_FILTER_H
+
+#include <GLES2/gl2.h>
+#include <SkColorFilter.h>
+
+#include "ProgramCache.h"
+#include "Extensions.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Base color filter
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Represents a Skia color filter. A color filter modifies a ProgramDescription
+ * and sets uniforms on the resulting shaders.
+ */
+struct SkiaColorFilter {
+    /**
+     * Type of Skia color filter in use.
+     */
+    enum Type {
+        kNone,
+        kColorMatrix,
+        kLighting,
+        kBlend,
+    };
+
+    SkiaColorFilter(SkColorFilter *skFilter, Type type, bool blend);
+    virtual ~SkiaColorFilter();
+
+    virtual void describe(ProgramDescription& description, const Extensions& extensions) = 0;
+    virtual void setupProgram(Program* program) = 0;
+
+    inline bool blend() const {
+        return mBlend;
+    }
+
+    Type type() const {
+        return mType;
+    }
+
+    SkColorFilter *getSkColorFilter() {
+        return mSkFilter;
+    }
+
+protected:
+    Type mType;
+    bool mBlend;
+
+private:
+    SkColorFilter *mSkFilter;
+}; // struct SkiaColorFilter
+
+///////////////////////////////////////////////////////////////////////////////
+// Implementations
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * A color filter that multiplies the source color with a matrix and adds a vector.
+ */
+struct SkiaColorMatrixFilter: public SkiaColorFilter {
+    SkiaColorMatrixFilter(SkColorFilter *skFilter, float* matrix, float* vector);
+    ~SkiaColorMatrixFilter();
+
+    void describe(ProgramDescription& description, const Extensions& extensions);
+    void setupProgram(Program* program);
+
+private:
+    float* mMatrix;
+    float* mVector;
+}; // struct SkiaColorMatrixFilter
+
+/**
+ * A color filters that multiplies the source color with a fixed value and adds
+ * another fixed value. Ignores the alpha channel of both arguments.
+ */
+struct SkiaLightingFilter: public SkiaColorFilter {
+    SkiaLightingFilter(SkColorFilter *skFilter, int multiply, int add);
+
+    void describe(ProgramDescription& description, const Extensions& extensions);
+    void setupProgram(Program* program);
+
+private:
+    GLfloat mMulR, mMulG, mMulB;
+    GLfloat mAddR, mAddG, mAddB;
+}; // struct SkiaLightingFilter
+
+/**
+ * A color filters that blends the source color with a specified destination color
+ * and PorterDuff blending mode.
+ */
+struct SkiaBlendFilter: public SkiaColorFilter {
+    SkiaBlendFilter(SkColorFilter *skFilter, int color, SkXfermode::Mode mode);
+
+    void describe(ProgramDescription& description, const Extensions& extensions);
+    void setupProgram(Program* program);
+
+private:
+    SkXfermode::Mode mMode;
+    GLfloat mR, mG, mB, mA;
+}; // struct SkiaBlendFilter
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_SKIA_COLOR_FILTER_H
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
new file mode 100644
index 0000000..e7e1187
--- /dev/null
+++ b/libs/hwui/SkiaShader.cpp
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/Log.h>
+
+#include <SkMatrix.h>
+
+#include "SkiaShader.h"
+#include "Texture.h"
+#include "Matrix.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Support
+///////////////////////////////////////////////////////////////////////////////
+
+static const GLenum gTextureUnitsMap[] = {
+        GL_TEXTURE0,
+        GL_TEXTURE1,
+        GL_TEXTURE2
+};
+
+static const GLint gTileModes[] = {
+        GL_CLAMP_TO_EDGE,   // == SkShader::kClamp_TileMode
+        GL_REPEAT,          // == SkShader::kRepeat_Mode
+        GL_MIRRORED_REPEAT  // == SkShader::kMirror_TileMode
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Base shader
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
+        SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
+        mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend) {
+    setMatrix(matrix);
+}
+
+SkiaShader::~SkiaShader() {
+}
+
+void SkiaShader::describe(ProgramDescription& description, const Extensions& extensions) {
+}
+
+void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+        GLuint* textureUnit) {
+}
+
+void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit) {
+    glActiveTexture(gTextureUnitsMap[textureUnit]);
+    glBindTexture(GL_TEXTURE_2D, texture->id);
+    if (wrapS != texture->wrapS) {
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
+        texture->wrapS = wrapS;
+    }
+    if (wrapT != texture->wrapT) {
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
+        texture->wrapT = wrapT;
+    }
+}
+
+void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) {
+    screenSpace.loadMultiply(mUnitMatrix, mShaderMatrix);
+    screenSpace.multiply(modelView);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Bitmap shader
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
+        SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
+        SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
+    updateLocalMatrix(matrix);
+}
+
+void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
+    Texture* texture = mTextureCache->get(mBitmap);
+    if (!texture) return;
+    mTexture = texture;
+
+    const float width = texture->width;
+    const float height = texture->height;
+
+    description.hasBitmap = true;
+    // The driver does not support non-power of two mirrored/repeated
+    // textures, so do it ourselves
+    if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
+            (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
+        description.isBitmapNpot = true;
+        description.bitmapWrapS = gTileModes[mTileX];
+        description.bitmapWrapT = gTileModes[mTileY];
+        mWrapS = GL_CLAMP_TO_EDGE;
+        mWrapT = GL_CLAMP_TO_EDGE;
+    } else {
+        mWrapS = gTileModes[mTileX];
+        mWrapT = gTileModes[mTileY];
+    }
+}
+
+void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
+        const Snapshot& snapshot, GLuint* textureUnit) {
+    GLuint textureSlot = (*textureUnit)++;
+    glActiveTexture(gTextureUnitsMap[textureSlot]);
+
+    Texture* texture = mTexture;
+    mTexture = NULL;
+    if (!texture) return;
+    const AutoTexture autoCleanup(texture);
+
+    const float width = texture->width;
+    const float height = texture->height;
+
+    mat4 textureTransform;
+    computeScreenSpaceMatrix(textureTransform, modelView);
+
+    // Uniforms
+    bindTexture(texture, mWrapS, mWrapT, textureSlot);
+    glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
+    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
+            GL_FALSE, &textureTransform.data[0]);
+    glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
+}
+
+void SkiaBitmapShader::updateTransforms(Program* program, const mat4& modelView,
+        const Snapshot& snapshot) {
+    mat4 textureTransform;
+    computeScreenSpaceMatrix(textureTransform, modelView);
+    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
+            GL_FALSE, &textureTransform.data[0]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Linear gradient shader
+///////////////////////////////////////////////////////////////////////////////
+
+static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
+    SkVector vec = pts[1] - pts[0];
+    const float mag = vec.length();
+    const float inv = mag ? 1.0f / mag : 0;
+
+    vec.scale(inv);
+    matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
+    matrix->postTranslate(-pts[0].fX, -pts[0].fY);
+    matrix->postScale(inv, inv);
+}
+
+SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors,
+        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
+        SkMatrix* matrix, bool blend):
+        SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend),
+        mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) {
+    SkPoint points[2];
+    points[0].set(bounds[0], bounds[1]);
+    points[1].set(bounds[2], bounds[3]);
+
+    SkMatrix unitMatrix;
+    toUnitMatrix(points, &unitMatrix);
+    mUnitMatrix.load(unitMatrix);
+
+    updateLocalMatrix(matrix);
+}
+
+SkiaLinearGradientShader::~SkiaLinearGradientShader() {
+    delete[] mBounds;
+    delete[] mColors;
+    delete[] mPositions;
+}
+
+void SkiaLinearGradientShader::describe(ProgramDescription& description,
+        const Extensions& extensions) {
+    description.hasGradient = true;
+    description.gradientType = ProgramDescription::kGradientLinear;
+}
+
+void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
+        const Snapshot& snapshot, GLuint* textureUnit) {
+    GLuint textureSlot = (*textureUnit)++;
+    glActiveTexture(gTextureUnitsMap[textureSlot]);
+
+    Texture* texture = mGradientCache->get(mKey);
+    if (!texture) {
+        texture = mGradientCache->addLinearGradient(mKey, mColors, mPositions, mCount, mTileX);
+    }
+
+    mat4 screenSpace;
+    computeScreenSpaceMatrix(screenSpace, modelView);
+
+    // Uniforms
+    bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY], textureSlot);
+    glUniform1i(program->getUniform("gradientSampler"), textureSlot);
+    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
+}
+
+void SkiaLinearGradientShader::updateTransforms(Program* program, const mat4& modelView,
+        const Snapshot& snapshot) {
+    mat4 screenSpace;
+    computeScreenSpaceMatrix(screenSpace, modelView);
+    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Circular gradient shader
+///////////////////////////////////////////////////////////////////////////////
+
+static void toCircularUnitMatrix(const float x, const float y, const float radius,
+        SkMatrix* matrix) {
+    const float inv = 1.0f / radius;
+    matrix->setTranslate(-x, -y);
+    matrix->postScale(inv, inv);
+}
+
+SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius,
+        uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
+        SkMatrix* matrix, bool blend):
+        SkiaSweepGradientShader(kCircularGradient, x, y, colors, positions, count, key,
+                tileMode, matrix, blend) {
+    SkMatrix unitMatrix;
+    toCircularUnitMatrix(x, y, radius, &unitMatrix);
+    mUnitMatrix.load(unitMatrix);
+
+    updateLocalMatrix(matrix);
+}
+
+void SkiaCircularGradientShader::describe(ProgramDescription& description,
+        const Extensions& extensions) {
+    description.hasGradient = true;
+    description.gradientType = ProgramDescription::kGradientCircular;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Sweep gradient shader
+///////////////////////////////////////////////////////////////////////////////
+
+static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) {
+    matrix->setTranslate(-x, -y);
+}
+
+SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors,
+        float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend):
+        SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode,
+                SkShader::kClamp_TileMode, matrix, blend),
+        mColors(colors), mPositions(positions), mCount(count) {
+    SkMatrix unitMatrix;
+    toSweepUnitMatrix(x, y, &unitMatrix);
+    mUnitMatrix.load(unitMatrix);
+
+    updateLocalMatrix(matrix);
+}
+
+SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors,
+        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
+        SkMatrix* matrix, bool blend):
+        SkiaShader(type, key, tileMode, tileMode, matrix, blend),
+        mColors(colors), mPositions(positions), mCount(count) {
+}
+
+SkiaSweepGradientShader::~SkiaSweepGradientShader() {
+    delete[] mColors;
+    delete[] mPositions;
+}
+
+void SkiaSweepGradientShader::describe(ProgramDescription& description,
+        const Extensions& extensions) {
+    description.hasGradient = true;
+    description.gradientType = ProgramDescription::kGradientSweep;
+}
+
+void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
+        const Snapshot& snapshot, GLuint* textureUnit) {
+    GLuint textureSlot = (*textureUnit)++;
+    glActiveTexture(gTextureUnitsMap[textureSlot]);
+
+    Texture* texture = mGradientCache->get(mKey);
+    if (!texture) {
+        texture = mGradientCache->addLinearGradient(mKey, mColors, mPositions, mCount);
+    }
+
+    mat4 screenSpace;
+    computeScreenSpaceMatrix(screenSpace, modelView);
+
+    // Uniforms
+    bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY], textureSlot);
+    glUniform1i(program->getUniform("gradientSampler"), textureSlot);
+    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
+}
+
+void SkiaSweepGradientShader::updateTransforms(Program* program, const mat4& modelView,
+        const Snapshot& snapshot) {
+    mat4 screenSpace;
+    computeScreenSpaceMatrix(screenSpace, modelView);
+    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Compose shader
+///////////////////////////////////////////////////////////////////////////////
+
+SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second,
+        SkXfermode::Mode mode, SkShader* key):
+        SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
+        NULL, first->blend() || second->blend()), mFirst(first), mSecond(second), mMode(mode) {
+}
+
+void SkiaComposeShader::set(TextureCache* textureCache, GradientCache* gradientCache) {
+    SkiaShader::set(textureCache, gradientCache);
+    mFirst->set(textureCache, gradientCache);
+    mSecond->set(textureCache, gradientCache);
+}
+
+void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
+    mFirst->describe(description, extensions);
+    mSecond->describe(description, extensions);
+    if (mFirst->type() == kBitmap) {
+        description.isBitmapFirst = true;
+    }
+    description.shadersMode = mMode;
+}
+
+void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
+        const Snapshot& snapshot, GLuint* textureUnit) {
+    mFirst->setupProgram(program, modelView, snapshot, textureUnit);
+    mSecond->setupProgram(program, modelView, snapshot, textureUnit);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
new file mode 100644
index 0000000..1d884ab
--- /dev/null
+++ b/libs/hwui/SkiaShader.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_SKIA_SHADER_H
+#define ANDROID_HWUI_SKIA_SHADER_H
+
+#include <SkShader.h>
+#include <SkXfermode.h>
+
+#include <GLES2/gl2.h>
+
+#include "Extensions.h"
+#include "ProgramCache.h"
+#include "TextureCache.h"
+#include "GradientCache.h"
+#include "Snapshot.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Base shader
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Represents a Skia shader. A shader will modify the GL context and active
+ * program to recreate the original effect.
+ */
+struct SkiaShader {
+    /**
+     * Type of Skia shader in use.
+     */
+    enum Type {
+        kNone,
+        kBitmap,
+        kLinearGradient,
+        kCircularGradient,
+        kSweepGradient,
+        kCompose
+    };
+
+    SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX, SkShader::TileMode tileY,
+            SkMatrix* matrix, bool blend);
+    virtual ~SkiaShader();
+
+    virtual void describe(ProgramDescription& description, const Extensions& extensions);
+    virtual void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+            GLuint* textureUnit);
+
+    inline SkShader *getSkShader() {
+        return mKey;
+    }
+
+    inline bool blend() const {
+        return mBlend;
+    }
+
+    Type type() const {
+        return mType;
+    }
+
+    virtual void set(TextureCache* textureCache, GradientCache* gradientCache) {
+        mTextureCache = textureCache;
+        mGradientCache = gradientCache;
+    }
+
+    virtual void updateTransforms(Program* program, const mat4& modelView,
+            const Snapshot& snapshot) {
+    }
+
+    void setMatrix(SkMatrix* matrix) {
+        updateLocalMatrix(matrix);
+    }
+
+    void updateLocalMatrix(const SkMatrix* matrix) {
+        if (matrix) {
+            mat4 localMatrix(*matrix);
+            mShaderMatrix.loadInverse(localMatrix);
+        } else {
+            mShaderMatrix.loadIdentity();
+        }
+    }
+
+    void computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView);
+
+protected:
+    inline void bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit);
+
+    Type mType;
+    SkShader* mKey;
+    SkShader::TileMode mTileX;
+    SkShader::TileMode mTileY;
+    bool mBlend;
+
+    TextureCache* mTextureCache;
+    GradientCache* mGradientCache;
+
+    mat4 mUnitMatrix;
+    mat4 mShaderMatrix;
+}; // struct SkiaShader
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Implementations
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * A shader that draws a bitmap.
+ */
+struct SkiaBitmapShader: public SkiaShader {
+    SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
+            SkShader::TileMode tileY, SkMatrix* matrix, bool blend);
+
+    void describe(ProgramDescription& description, const Extensions& extensions);
+    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+            GLuint* textureUnit);
+    void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
+
+private:
+    /**
+     * This method does not work for n == 0.
+     */
+    inline bool isPowerOfTwo(unsigned int n) {
+        return !(n & (n - 1));
+    }
+
+    SkBitmap* mBitmap;
+    Texture* mTexture;
+    GLenum mWrapS;
+    GLenum mWrapT;
+}; // struct SkiaBitmapShader
+
+/**
+ * A shader that draws a linear gradient.
+ */
+struct SkiaLinearGradientShader: public SkiaShader {
+    SkiaLinearGradientShader(float* bounds, uint32_t* colors, float* positions, int count,
+            SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
+    ~SkiaLinearGradientShader();
+
+    void describe(ProgramDescription& description, const Extensions& extensions);
+    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+            GLuint* textureUnit);
+    void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
+
+private:
+    float* mBounds;
+    uint32_t* mColors;
+    float* mPositions;
+    int mCount;
+}; // struct SkiaLinearGradientShader
+
+/**
+ * A shader that draws a sweep gradient.
+ */
+struct SkiaSweepGradientShader: public SkiaShader {
+    SkiaSweepGradientShader(float x, float y, uint32_t* colors, float* positions, int count,
+            SkShader* key, SkMatrix* matrix, bool blend);
+    ~SkiaSweepGradientShader();
+
+    virtual void describe(ProgramDescription& description, const Extensions& extensions);
+    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+            GLuint* textureUnit);
+    void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot);
+
+protected:
+    SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, float* positions,
+            int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
+
+    uint32_t* mColors;
+    float* mPositions;
+    int mCount;
+}; // struct SkiaSweepGradientShader
+
+/**
+ * A shader that draws a circular gradient.
+ */
+struct SkiaCircularGradientShader: public SkiaSweepGradientShader {
+    SkiaCircularGradientShader(float x, float y, float radius, uint32_t* colors, float* positions,
+            int count, SkShader* key,SkShader::TileMode tileMode, SkMatrix* matrix, bool blend);
+
+    void describe(ProgramDescription& description, const Extensions& extensions);
+}; // struct SkiaCircularGradientShader
+
+/**
+ * A shader that draws two shaders, composited with an xfermode.
+ */
+struct SkiaComposeShader: public SkiaShader {
+    SkiaComposeShader(SkiaShader* first, SkiaShader* second, SkXfermode::Mode mode, SkShader* key);
+
+    void set(TextureCache* textureCache, GradientCache* gradientCache);
+
+    void describe(ProgramDescription& description, const Extensions& extensions);
+    void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
+            GLuint* textureUnit);
+
+private:
+    SkiaShader* mFirst;
+    SkiaShader* mSecond;
+    SkXfermode::Mode mMode;
+}; // struct SkiaComposeShader
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_SKIA_SHADER_H
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
new file mode 100644
index 0000000..9f78063
--- /dev/null
+++ b/libs/hwui/Snapshot.h
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_SNAPSHOT_H
+#define ANDROID_HWUI_SNAPSHOT_H
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <utils/RefBase.h>
+#include <ui/Region.h>
+
+#include <SkCanvas.h>
+
+#include "Layer.h"
+#include "Matrix.h"
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A snapshot holds information about the current state of the rendering
+ * surface. A snapshot is usually created whenever the user calls save()
+ * and discarded when the user calls restore(). Once a snapshot is created,
+ * it can hold information for deferred rendering.
+ *
+ * Each snapshot has a link to a previous snapshot, indicating the previous
+ * state of the renderer.
+ */
+class Snapshot: public LightRefBase<Snapshot> {
+public:
+    Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), invisible(false) {
+        transform = &mTransformRoot;
+        clipRect = &mClipRectRoot;
+        region = NULL;
+    }
+
+    /**
+     * Copies the specified snapshot/ The specified snapshot is stored as
+     * the previous snapshot.
+     */
+    Snapshot(const sp<Snapshot>& s, int saveFlags):
+            flags(0), previous(s), layer(NULL), fbo(s->fbo),
+            invisible(s->invisible), viewport(s->viewport), height(s->height) {
+        if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
+            mTransformRoot.load(*s->transform);
+            transform = &mTransformRoot;
+        } else {
+            transform = s->transform;
+        }
+
+        if (saveFlags & SkCanvas::kClip_SaveFlag) {
+            mClipRectRoot.set(*s->clipRect);
+            clipRect = &mClipRectRoot;
+        } else {
+            clipRect = s->clipRect;
+        }
+
+        if ((s->flags & Snapshot::kFlagClipSet) &&
+                !(s->flags & Snapshot::kFlagDirtyLocalClip)) {
+            mLocalClip.set(s->mLocalClip);
+        } else {
+            flags |= Snapshot::kFlagDirtyLocalClip;
+        }
+
+        if (s->flags & Snapshot::kFlagFboTarget) {
+            flags |= Snapshot::kFlagFboTarget;
+            region = s->region;
+        } else {
+            region = NULL;
+        }
+    }
+
+    /**
+     * Various flags set on #flags.
+     */
+    enum Flags {
+        /**
+         * Indicates that the clip region was modified. When this
+         * snapshot is restored so must the clip.
+         */
+        kFlagClipSet = 0x1,
+        /**
+         * Indicates that this snapshot was created when saving
+         * a new layer.
+         */
+        kFlagIsLayer = 0x2,
+        /**
+         * Indicates that this snapshot is a special type of layer
+         * backed by an FBO. This flag only makes sense when the
+         * flag kFlagIsLayer is also set.
+         */
+        kFlagIsFboLayer = 0x4,
+        /**
+         * Indicates that the local clip should be recomputed.
+         */
+        kFlagDirtyLocalClip = 0x8,
+        /**
+         * Indicates that this snapshot has changed the ortho matrix.
+         */
+        kFlagDirtyOrtho = 0x10,
+        /**
+         * Indicates that this snapshot or an ancestor snapshot is
+         * an FBO layer.
+         */
+        kFlagFboTarget = 0x20
+    };
+
+    /**
+     * Modifies the current clip with the new clip rectangle and
+     * the specified operation. The specified rectangle is transformed
+     * by this snapshot's trasnformation.
+     */
+    bool clip(float left, float top, float right, float bottom,
+            SkRegion::Op op = SkRegion::kIntersect_Op) {
+        Rect r(left, top, right, bottom);
+        transform->mapRect(r);
+        return clipTransformed(r, op);
+    }
+
+    /**
+     * Modifies the current clip with the new clip rectangle and
+     * the specified operation. The specified rectangle is considered
+     * already transformed.
+     */
+    bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op) {
+        bool clipped = false;
+
+        // NOTE: The unimplemented operations require support for regions
+        // Supporting regions would require using a stencil buffer instead
+        // of the scissor. The stencil buffer itself is not too expensive
+        // (memory cost excluded) but on fillrate limited devices, managing
+        // the stencil might have a negative impact on the framerate.
+        switch (op) {
+            case SkRegion::kDifference_Op:
+                break;
+            case SkRegion::kIntersect_Op:
+                clipped = clipRect->intersect(r);
+                break;
+            case SkRegion::kUnion_Op:
+                clipped = clipRect->unionWith(r);
+                break;
+            case SkRegion::kXOR_Op:
+                break;
+            case SkRegion::kReverseDifference_Op:
+                break;
+            case SkRegion::kReplace_Op:
+                clipRect->set(r);
+                clipped = true;
+                break;
+        }
+
+        if (clipped) {
+            flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip;
+        }
+
+        return clipped;
+    }
+
+    /**
+     * Sets the current clip.
+     */
+    void setClip(float left, float top, float right, float bottom) {
+        clipRect->set(left, top, right, bottom);
+        flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip;
+    }
+
+    const Rect& getLocalClip() {
+        if (flags & Snapshot::kFlagDirtyLocalClip) {
+            mat4 inverse;
+            inverse.loadInverse(*transform);
+
+            mLocalClip.set(*clipRect);
+            inverse.mapRect(mLocalClip);
+
+            flags &= ~Snapshot::kFlagDirtyLocalClip;
+        }
+        return mLocalClip;
+    }
+
+    void resetTransform(float x, float y, float z) {
+        transform = &mTransformRoot;
+        transform->loadTranslate(x, y, z);
+    }
+
+    void resetClip(float left, float top, float right, float bottom) {
+        clipRect = &mClipRectRoot;
+        clipRect->set(left, top, right, bottom);
+        flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip;
+    }
+
+    /**
+     * Dirty flags.
+     */
+    int flags;
+
+    /**
+     * Previous snapshot.
+     */
+    sp<Snapshot> previous;
+
+    /**
+     * Only set when the flag kFlagIsLayer is set.
+     */
+    Layer* layer;
+
+    /**
+     * Only set when the flag kFlagIsFboLayer is set.
+     */
+    GLuint fbo;
+
+    /**
+     * Indicates that this snapshot is invisible and nothing should be drawn
+     * inside it.
+     */
+    bool invisible;
+
+    /**
+     * Current viewport.
+     */
+    Rect viewport;
+
+    /**
+     * Height of the framebuffer the snapshot is rendering into.
+     */
+    int height;
+
+    /**
+     * Contains the previous ortho matrix.
+     */
+    mat4 orthoMatrix;
+
+    /**
+     * Local transformation. Holds the current translation, scale and
+     * rotation values.
+     */
+    mat4* transform;
+
+    /**
+     * Current clip region. The clip is stored in canvas-space coordinates,
+     * (screen-space coordinates in the regular case.)
+     */
+    Rect* clipRect;
+
+    /**
+     * The ancestor layer's dirty region..
+     */
+    Region* region;
+
+private:
+    mat4 mTransformRoot;
+    Rect mClipRectRoot;
+    Rect mLocalClip;
+
+}; // class Snapshot
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_SNAPSHOT_H
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
new file mode 100644
index 0000000..9d54277
--- /dev/null
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "TextDropShadowCache.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+TextDropShadowCache::TextDropShadowCache():
+        mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting drop shadow cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default drop shadow cache size of %.2fMB", DEFAULT_DROP_SHADOW_CACHE_SIZE);
+    }
+
+    mCache.setOnEntryRemovedListener(this);
+}
+
+TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize):
+        mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(maxByteSize) {
+    mCache.setOnEntryRemovedListener(this);
+}
+
+TextDropShadowCache::~TextDropShadowCache() {
+    mCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t TextDropShadowCache::getSize() {
+    return mSize;
+}
+
+uint32_t TextDropShadowCache::getMaxSize() {
+    return mMaxSize;
+}
+
+void TextDropShadowCache::setMaxSize(uint32_t maxSize) {
+    mMaxSize = maxSize;
+    while (mSize > mMaxSize) {
+        mCache.removeOldest();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void TextDropShadowCache::operator()(ShadowText& text, ShadowTexture*& texture) {
+    const uint32_t size = texture->width * texture->height;
+    mSize -= size;
+
+    if (texture) {
+        glDeleteTextures(1, &texture->id);
+        delete texture;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void TextDropShadowCache::clear() {
+    mCache.clear();
+}
+
+ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len,
+        int numGlyphs, uint32_t radius) {
+    ShadowText entry(paint, radius, len, text);
+    ShadowTexture* texture = mCache.get(entry);
+
+    if (!texture) {
+        FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(paint, text, 0,
+                len, numGlyphs, radius);
+
+        texture = new ShadowTexture;
+        texture->left = shadow.penX;
+        texture->top = shadow.penY;
+        texture->width = shadow.width;
+        texture->height = shadow.height;
+        texture->generation = 0;
+        texture->blend = true;
+
+        const uint32_t size = shadow.width * shadow.height;
+        // Don't even try to cache a bitmap that's bigger than the cache
+        if (size < mMaxSize) {
+            while (mSize + size > mMaxSize) {
+                mCache.removeOldest();
+            }
+        }
+
+        glGenTextures(1, &texture->id);
+
+        glBindTexture(GL_TEXTURE_2D, texture->id);
+        // Textures are Alpha8
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
+                GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image);
+
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+        if (size < mMaxSize) {
+            mSize += size;
+            mCache.put(entry, texture);
+        } else {
+            texture->cleanup = true;
+        }
+
+        // Cleanup shadow
+        delete[] shadow.image;
+    }
+
+    return texture;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
new file mode 100644
index 0000000..adf09e2
--- /dev/null
+++ b/libs/hwui/TextDropShadowCache.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H
+#define ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H
+
+#include <GLES2/gl2.h>
+
+#include <SkPaint.h>
+
+#include "utils/GenerationCache.h"
+#include "FontRenderer.h"
+#include "Texture.h"
+
+namespace android {
+namespace uirenderer {
+
+struct ShadowText {
+    ShadowText() {
+        text = NULL;
+    }
+
+    ShadowText(SkPaint* paint, uint32_t radius, uint32_t len, const char* srcText):
+            radius(radius), len(len) {
+        text = new char[len];
+        memcpy(text, srcText, len);
+
+        textSize = paint->getTextSize();
+        typeface = paint->getTypeface();
+
+        hash = 0;
+        uint32_t multiplier = 1;
+        for (uint32_t i = 0; i < len; i++) {
+            hash += text[i] * multiplier;
+            uint32_t shifted = multiplier << 5;
+            multiplier = shifted - multiplier;
+        }
+    }
+
+    ShadowText(const ShadowText& shadow):
+            radius(shadow.radius), len(shadow.len), hash(shadow.hash),
+            textSize(shadow.textSize), typeface(shadow.typeface) {
+        text = new char[shadow.len];
+        memcpy(text, shadow.text, shadow.len);
+    }
+
+    ~ShadowText() {
+        delete[] text;
+    }
+
+    uint32_t radius;
+    uint32_t len;
+    uint32_t hash;
+    float textSize;
+    SkTypeface* typeface;
+    char *text;
+
+    bool operator<(const ShadowText& rhs) const {
+        if (hash < rhs.hash) return true;
+        else if (hash == rhs.hash) {
+            if (len < rhs.len) return true;
+            else if (len == rhs.len) {
+                if (radius < rhs.radius) return true;
+                else if (radius == rhs.radius) {
+                    if (textSize < rhs.textSize) return true;
+                    else if (textSize == rhs.textSize) {
+                        if (typeface < rhs.typeface) return true;
+                        else if (typeface == rhs.typeface) {
+                            return strncmp(text, rhs.text, len) < 0;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+}; // struct ShadowText
+
+/**
+ * Alpha texture used to represent a shadow.
+ */
+struct ShadowTexture: public Texture {
+    ShadowTexture(): Texture() {
+    }
+
+    float left;
+    float top;
+}; // struct ShadowTexture
+
+class TextDropShadowCache: public OnEntryRemoved<ShadowText, ShadowTexture*> {
+public:
+    TextDropShadowCache();
+    TextDropShadowCache(uint32_t maxByteSize);
+    ~TextDropShadowCache();
+
+    /**
+     * Used as a callback when an entry is removed from the cache.
+     * Do not invoke directly.
+     */
+    void operator()(ShadowText& text, ShadowTexture*& texture);
+
+    ShadowTexture* get(SkPaint* paint, const char* text, uint32_t len,
+            int numGlyphs, uint32_t radius);
+
+    /**
+     * Clears the cache. This causes all textures to be deleted.
+     */
+    void clear();
+
+    void setFontRenderer(FontRenderer& fontRenderer) {
+        mRenderer = &fontRenderer;
+    }
+
+    /**
+     * Sets the maximum size of the cache in bytes.
+     */
+    void setMaxSize(uint32_t maxSize);
+    /**
+     * Returns the maximum size of the cache in bytes.
+     */
+    uint32_t getMaxSize();
+    /**
+     * Returns the current size of the cache in bytes.
+     */
+    uint32_t getSize();
+
+private:
+    GenerationCache<ShadowText, ShadowTexture*> mCache;
+
+    uint32_t mSize;
+    uint32_t mMaxSize;
+    FontRenderer* mRenderer;
+}; // class TextDropShadowCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
new file mode 100644
index 0000000..4922bb3
--- /dev/null
+++ b/libs/hwui/Texture.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_TEXTURE_H
+#define ANDROID_HWUI_TEXTURE_H
+
+#include <GLES2/gl2.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Represents an OpenGL texture.
+ */
+struct Texture {
+    Texture() {
+        cleanup = false;
+        bitmapSize = 0;
+        wrapS = GL_CLAMP_TO_EDGE;
+        wrapT = GL_CLAMP_TO_EDGE;
+    }
+
+    /**
+     * Name of the texture.
+     */
+    GLuint id;
+    /**
+     * Generation of the backing bitmap,
+     */
+    uint32_t generation;
+    /**
+     * Indicates whether the texture requires blending.
+     */
+    bool blend;
+    /**
+     * Width of the backing bitmap.
+     */
+    uint32_t width;
+    /**
+     * Height of the backing bitmap.
+     */
+    uint32_t height;
+    /**
+     * Indicates whether this texture should be cleaned up after use.
+     */
+    bool cleanup;
+    /**
+     * Optional, size of the original bitmap.
+     */
+    uint32_t bitmapSize;
+
+    /**
+     * Last wrap modes set on this texture. Defaults to GL_CLAMP_TO_EDGE.
+     */
+    GLenum wrapS;
+    GLenum wrapT;
+}; // struct Texture
+
+class AutoTexture {
+public:
+    AutoTexture(const Texture* texture): mTexture(texture) { }
+    ~AutoTexture() {
+        if (mTexture && mTexture->cleanup) {
+            glDeleteTextures(1, &mTexture->id);
+            delete mTexture;
+        }
+    }
+
+private:
+    const Texture* mTexture;
+}; // class AutoTexture
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_TEXTURE_H
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
new file mode 100644
index 0000000..0c7948f
--- /dev/null
+++ b/libs/hwui/TextureCache.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+
+#include <SkCanvas.h>
+
+#include <utils/threads.h>
+
+#include "TextureCache.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+TextureCache::TextureCache():
+        mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting texture cache size to %sMB", property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE);
+    }
+
+    init();
+}
+
+TextureCache::TextureCache(uint32_t maxByteSize):
+        mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(maxByteSize) {
+    init();
+}
+
+TextureCache::~TextureCache() {
+    Mutex::Autolock _l(mLock);
+    mCache.clear();
+}
+
+void TextureCache::init() {
+    mCache.setOnEntryRemovedListener(this);
+
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+    LOGD("    Maximum texture dimension is %d pixels", mMaxTextureSize);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t TextureCache::getSize() {
+    Mutex::Autolock _l(mLock);
+    return mSize;
+}
+
+uint32_t TextureCache::getMaxSize() {
+    Mutex::Autolock _l(mLock);
+    return mMaxSize;
+}
+
+void TextureCache::setMaxSize(uint32_t maxSize) {
+    Mutex::Autolock _l(mLock);
+    mMaxSize = maxSize;
+    while (mSize > mMaxSize) {
+        mCache.removeOldest();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
+    // This will be called already locked
+    if (texture) {
+        mSize -= texture->bitmapSize;
+        TEXTURE_LOGD("TextureCache::callback: removed size, mSize = %d, %d",
+                texture->bitmapSize, mSize);
+        glDeleteTextures(1, &texture->id);
+        delete texture;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+Texture* TextureCache::get(SkBitmap* bitmap) {
+    mLock.lock();
+    Texture* texture = mCache.get(bitmap);
+    mLock.unlock();
+
+    if (!texture) {
+        if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
+            LOGW("Bitmap too large to be uploaded into a texture");
+            return NULL;
+        }
+
+        const uint32_t size = bitmap->rowBytes() * bitmap->height();
+        // Don't even try to cache a bitmap that's bigger than the cache
+        if (size < mMaxSize) {
+            mLock.lock();
+            while (mSize + size > mMaxSize) {
+                mCache.removeOldest();
+            }
+            mLock.unlock();
+        }
+
+        texture = new Texture;
+        texture->bitmapSize = size;
+        generateTexture(bitmap, texture, false);
+
+        if (size < mMaxSize) {
+            mLock.lock();
+            mSize += size;
+            TEXTURE_LOGD("TextureCache::get: create texture(0x%p): size, mSize = %d, %d",
+                     bitmap, size, mSize);
+            mCache.put(bitmap, texture);
+            mLock.unlock();
+        } else {
+            texture->cleanup = true;
+        }
+    } else if (bitmap->getGenerationID() != texture->generation) {
+        generateTexture(bitmap, texture, true);
+    }
+
+    return texture;
+}
+
+void TextureCache::remove(SkBitmap* bitmap) {
+    Mutex::Autolock _l(mLock);
+    mCache.remove(bitmap);
+}
+
+void TextureCache::clear() {
+    Mutex::Autolock _l(mLock);
+    mCache.clear();
+    TEXTURE_LOGD("TextureCache:clear(), miSize = %d", mSize);
+}
+
+void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) {
+    SkAutoLockPixels alp(*bitmap);
+
+    if (!bitmap->readyToDraw()) {
+        LOGE("Cannot generate texture from bitmap");
+        return;
+    }
+
+    const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
+            bitmap->height() != int(texture->height);
+
+    if (!regenerate) {
+        glGenTextures(1, &texture->id);
+    }
+
+    texture->generation = bitmap->getGenerationID();
+    texture->width = bitmap->width();
+    texture->height = bitmap->height();
+
+    glBindTexture(GL_TEXTURE_2D, texture->id);
+    glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
+
+    switch (bitmap->getConfig()) {
+    case SkBitmap::kA8_Config:
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+        uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height,
+                GL_UNSIGNED_BYTE, bitmap->getPixels());
+        texture->blend = true;
+        break;
+    case SkBitmap::kRGB_565_Config:
+        uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), texture->height,
+                GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
+        texture->blend = false;
+        break;
+    case SkBitmap::kARGB_8888_Config:
+        uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height,
+                GL_UNSIGNED_BYTE, bitmap->getPixels());
+        // Do this after calling getPixels() to make sure Skia's deferred
+        // decoding happened
+        texture->blend = !bitmap->isOpaque();
+        break;
+    case SkBitmap::kIndex8_Config:
+        uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
+        texture->blend = false;
+        break;
+    case SkBitmap::kARGB_4444_Config:
+        uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
+        texture->blend = true;
+        break;
+    default:
+        LOGW("Unsupported bitmap config: %d", bitmap->getConfig());
+        break;
+    }
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+void TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap,
+        uint32_t width, uint32_t height) {
+    SkBitmap rgbaBitmap;
+    rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    rgbaBitmap.allocPixels();
+    rgbaBitmap.eraseColor(0);
+
+    SkCanvas canvas(rgbaBitmap);
+    canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL);
+
+    uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), height,
+            GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
+}
+
+void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
+        GLenum type, const GLvoid * data) {
+    if (resize) {
+        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
+    } else {
+        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
+    }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
new file mode 100644
index 0000000..d9d2387
--- /dev/null
+++ b/libs/hwui/TextureCache.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_TEXTURE_CACHE_H
+#define ANDROID_HWUI_TEXTURE_CACHE_H
+
+#include <SkBitmap.h>
+
+#include "Texture.h"
+#include "utils/GenerationCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_TEXTURES 0
+
+// Debug
+#if DEBUG_TEXTURES
+    #define TEXTURE_LOGD(...) LOGD(__VA_ARGS__)
+#else
+    #define TEXTURE_LOGD(...)
+#endif
+
+/**
+ * A simple LRU texture cache. The cache has a maximum size expressed in bytes.
+ * Any texture added to the cache causing the cache to grow beyond the maximum
+ * allowed size will also cause the oldest texture to be kicked out.
+ */
+class TextureCache: public OnEntryRemoved<SkBitmap*, Texture*> {
+public:
+    TextureCache();
+    TextureCache(uint32_t maxByteSize);
+    ~TextureCache();
+
+    /**
+     * Used as a callback when an entry is removed from the cache.
+     * Do not invoke directly.
+     */
+    void operator()(SkBitmap*& bitmap, Texture*& texture);
+
+    /**
+     * Returns the texture associated with the specified bitmap. If the texture
+     * cannot be found in the cache, a new texture is generated.
+     */
+    Texture* get(SkBitmap* bitmap);
+    /**
+     * Removes the texture associated with the specified bitmap. Returns NULL
+     * if the texture cannot be found. Upon remove the texture is freed.
+     */
+    void remove(SkBitmap* bitmap);
+    /**
+     * Clears the cache. This causes all textures to be deleted.
+     */
+    void clear();
+
+    /**
+     * Sets the maximum size of the cache in bytes.
+     */
+    void setMaxSize(uint32_t maxSize);
+    /**
+     * Returns the maximum size of the cache in bytes.
+     */
+    uint32_t getMaxSize();
+    /**
+     * Returns the current size of the cache in bytes.
+     */
+    uint32_t getSize();
+
+private:
+    /**
+     * Generates the texture from a bitmap into the specified texture structure.
+     *
+     * @param regenerate If true, the bitmap data is reuploaded into the texture, but
+     *        no new texture is generated.
+     */
+    void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false);
+
+    void uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height);
+    void uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
+            GLenum type, const GLvoid * data);
+
+    void init();
+
+    GenerationCache<SkBitmap*, Texture*> mCache;
+
+    uint32_t mSize;
+    uint32_t mMaxSize;
+    GLint mMaxTextureSize;
+
+    /**
+     * Used to access mCache and mSize. All methods are accessed from a single
+     * thread except for remove().
+     */
+    mutable Mutex mLock;
+}; // class TextureCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_TEXTURE_CACHE_H
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
new file mode 100644
index 0000000..bbf4d4a
--- /dev/null
+++ b/libs/hwui/Vertex.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_VERTEX_H
+#define ANDROID_HWUI_VERTEX_H
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Simple structure to describe a vertex with a position and a texture.
+ */
+struct TextureVertex {
+    float position[2];
+    float texture[2];
+
+    static inline void set(TextureVertex* vertex, float x, float y, float u, float v) {
+        vertex[0].position[0] = x;
+        vertex[0].position[1] = y;
+        vertex[0].texture[0] = u;
+        vertex[0].texture[1] = v;
+    }
+
+    static inline void setUV(TextureVertex* vertex, float u, float v) {
+        vertex[0].texture[0] = u;
+        vertex[0].texture[1] = v;
+    }
+}; // struct TextureVertex
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_VERTEX_H
diff --git a/libs/hwui/utils/Compare.h b/libs/hwui/utils/Compare.h
new file mode 100644
index 0000000..6531e78
--- /dev/null
+++ b/libs/hwui/utils/Compare.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_COMPARE_H
+#define ANDROID_HWUI_COMPARE_H
+
+#include <cmath>
+
+#define EPSILON 0.00001f
+
+#define ALMOST_EQUAL(u, v) (fabs((u) - (v)) < EPSILON)
+
+/**
+ * Compare floats.
+ */
+#define LTE_FLOAT(a) \
+    if (a < rhs.a) return true; \
+    if (ALMOST_EQUAL(a, rhs.a))
+
+/**
+ * Compare integers.
+ */
+#define LTE_INT(a) \
+    if (a < rhs.a) return true; \
+    if (a == rhs.a)
+
+#endif // ANDROID_HWUI_COMPARE_H
diff --git a/libs/hwui/utils/GenerationCache.h b/libs/hwui/utils/GenerationCache.h
new file mode 100644
index 0000000..2e76236
--- /dev/null
+++ b/libs/hwui/utils/GenerationCache.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_GENERATION_CACHE_H
+#define ANDROID_HWUI_GENERATION_CACHE_H
+
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace uirenderer {
+
+template<typename EntryKey, typename EntryValue>
+class OnEntryRemoved {
+public:
+    virtual ~OnEntryRemoved() { };
+    virtual void operator()(EntryKey& key, EntryValue& value) = 0;
+}; // class OnEntryRemoved
+
+template<typename EntryKey, typename EntryValue>
+struct Entry: public LightRefBase<Entry<EntryKey, EntryValue> > {
+    Entry() { }
+    Entry(const Entry<EntryKey, EntryValue>& e):
+            key(e.key), value(e.value), parent(e.parent), child(e.child) { }
+    Entry(sp<Entry<EntryKey, EntryValue> > e):
+            key(e->key), value(e->value), parent(e->parent), child(e->child) { }
+
+    EntryKey key;
+    EntryValue value;
+
+    sp<Entry<EntryKey, EntryValue> > parent;
+    sp<Entry<EntryKey, EntryValue> > child;
+}; // struct Entry
+
+template<typename K, typename V>
+class GenerationCache {
+public:
+    GenerationCache(uint32_t maxCapacity);
+    virtual ~GenerationCache();
+
+    enum Capacity {
+        kUnlimitedCapacity,
+    };
+
+    void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener);
+
+    void clear();
+
+    bool contains(K key) const;
+    V get(K key);
+    K getKeyAt(uint32_t index) const;
+    bool put(K key, V value);
+    V remove(K key);
+    V removeOldest();
+    V getValueAt(uint32_t index) const;
+
+    uint32_t size() const;
+
+    void addToCache(sp<Entry<K, V> > entry, K key, V value);
+    void attachToCache(sp<Entry<K, V> > entry);
+    void detachFromCache(sp<Entry<K, V> > entry);
+
+    V removeAt(ssize_t index);
+
+    KeyedVector<K, sp<Entry<K, V> > > mCache;
+    uint32_t mMaxCapacity;
+
+    OnEntryRemoved<K, V>* mListener;
+
+    sp<Entry<K, V> > mOldest;
+    sp<Entry<K, V> > mYoungest;
+}; // class GenerationCache
+
+template<typename K, typename V>
+GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL) {
+};
+
+template<typename K, typename V>
+GenerationCache<K, V>::~GenerationCache() {
+    clear();
+};
+
+template<typename K, typename V>
+uint32_t GenerationCache<K, V>::size() const {
+    return mCache.size();
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
+    mListener = listener;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::clear() {
+    if (mListener) {
+        for (uint32_t i = 0; i < mCache.size(); i++) {
+            sp<Entry<K, V> > entry = mCache.valueAt(i);
+            if (mListener) {
+                (*mListener)(entry->key, entry->value);
+            }
+        }
+    }
+    mCache.clear();
+    mYoungest.clear();
+    mOldest.clear();
+}
+
+template<typename K, typename V>
+bool GenerationCache<K, V>::contains(K key) const {
+    return mCache.indexOfKey(key) >= 0;
+}
+
+template<typename K, typename V>
+K GenerationCache<K, V>::getKeyAt(uint32_t index) const {
+    return mCache.keyAt(index);
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::getValueAt(uint32_t index) const {
+    return mCache.valueAt(index);
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::get(K key) {
+    ssize_t index = mCache.indexOfKey(key);
+    if (index >= 0) {
+        sp<Entry<K, V> > entry = mCache.valueAt(index);
+        if (entry.get()) {
+            detachFromCache(entry);
+            attachToCache(entry);
+            return entry->value;
+        }
+    }
+
+    return NULL;
+}
+
+template<typename K, typename V>
+bool GenerationCache<K, V>::put(K key, V value) {
+    if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) {
+        removeOldest();
+    }
+
+    ssize_t index = mCache.indexOfKey(key);
+    if (index < 0) {
+        sp<Entry<K, V> > entry = new Entry<K, V>;
+        addToCache(entry, key, value);
+        return true;
+    }
+
+    return false;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::addToCache(sp<Entry<K, V> > entry, K key, V value) {
+    entry->key = key;
+    entry->value = value;
+    mCache.add(key, entry);
+    attachToCache(entry);
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::remove(K key) {
+    ssize_t index = mCache.indexOfKey(key);
+    if (index >= 0) {
+        return removeAt(index);
+    }
+
+    return NULL;
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::removeAt(ssize_t index) {
+    sp<Entry<K, V> > entry = mCache.valueAt(index);
+    if (mListener) {
+        (*mListener)(entry->key, entry->value);
+    }
+    mCache.removeItemsAt(index, 1);
+    detachFromCache(entry);
+
+    return entry->value;
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::removeOldest() {
+    if (mOldest.get()) {
+        ssize_t index = mCache.indexOfKey(mOldest->key);
+        if (index >= 0) {
+            return removeAt(index);
+        }
+    }
+
+    return NULL;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::attachToCache(sp<Entry<K, V> > entry) {
+    if (!mYoungest.get()) {
+        mYoungest = mOldest = entry;
+    } else {
+        entry->parent = mYoungest;
+        mYoungest->child = entry;
+        mYoungest = entry;
+    }
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::detachFromCache(sp<Entry<K, V> > entry) {
+    if (entry->parent.get()) {
+        entry->parent->child = entry->child;
+    }
+
+    if (entry->child.get()) {
+        entry->child->parent = entry->parent;
+    }
+
+    if (mOldest == entry) {
+        mOldest = entry->child;
+    }
+
+    if (mYoungest == entry) {
+        mYoungest = entry->parent;
+    }
+
+    entry->parent.clear();
+    entry->child.clear();
+}
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_GENERATION_CACHE_H
diff --git a/libs/hwui/utils/SortedList.h b/libs/hwui/utils/SortedList.h
new file mode 100644
index 0000000..2fa890a
--- /dev/null
+++ b/libs/hwui/utils/SortedList.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_SORTED_LIST_H
+#define ANDROID_HWUI_SORTED_LIST_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Vector.h>
+#include <utils/TypeHelpers.h>
+
+#include "SortedListImpl.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Sorted list
+///////////////////////////////////////////////////////////////////////////////
+
+template<class TYPE>
+class SortedList: private SortedListImpl {
+public:
+    typedef TYPE value_type;
+
+    SortedList();
+    SortedList(const SortedList<TYPE>& rhs);
+    virtual ~SortedList();
+
+    const SortedList<TYPE>& operator =(const SortedList<TYPE>& rhs) const;
+    SortedList<TYPE>& operator =(const SortedList<TYPE>& rhs);
+
+    inline void clear() {
+        VectorImpl::clear();
+    }
+
+    inline size_t size() const {
+        return VectorImpl::size();
+    }
+
+    inline bool isEmpty() const {
+        return VectorImpl::isEmpty();
+    }
+
+    inline size_t capacity() const {
+        return VectorImpl::capacity();
+    }
+
+    inline ssize_t setCapacity(size_t size) {
+        return VectorImpl::setCapacity(size);
+    }
+
+    inline const TYPE* array() const;
+
+    TYPE* editArray();
+
+    ssize_t indexOf(const TYPE& item) const;
+    size_t orderOf(const TYPE& item) const;
+
+    inline const TYPE& operator [](size_t index) const;
+    inline const TYPE& itemAt(size_t index) const;
+    const TYPE& top() const;
+    const TYPE& mirrorItemAt(ssize_t index) const;
+
+    ssize_t add(const TYPE& item);
+
+    TYPE& editItemAt(size_t index) {
+        return *(static_cast<TYPE *> (VectorImpl::editItemLocation(index)));
+    }
+
+    ssize_t merge(const Vector<TYPE>& vector);
+    ssize_t merge(const SortedList<TYPE>& vector);
+
+    ssize_t remove(const TYPE&);
+
+    inline ssize_t removeItemsAt(size_t index, size_t count = 1);
+    inline ssize_t removeAt(size_t index) {
+        return removeItemsAt(index);
+    }
+
+protected:
+    virtual void do_construct(void* storage, size_t num) const;
+    virtual void do_destroy(void* storage, size_t num) const;
+    virtual void do_copy(void* dest, const void* from, size_t num) const;
+    virtual void do_splat(void* dest, const void* item, size_t num) const;
+    virtual void do_move_forward(void* dest, const void* from, size_t num) const;
+    virtual void do_move_backward(void* dest, const void* from, size_t num) const;
+    virtual int do_compare(const void* lhs, const void* rhs) const;
+}; // class SortedList
+
+///////////////////////////////////////////////////////////////////////////////
+// Implementation
+///////////////////////////////////////////////////////////////////////////////
+
+template<class TYPE>
+inline SortedList<TYPE>::SortedList():
+        SortedListImpl(sizeof(TYPE), ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
+            | (traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
+            | (traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))) {
+}
+
+template<class TYPE>
+inline SortedList<TYPE>::SortedList(const SortedList<TYPE>& rhs): SortedListImpl(rhs) {
+}
+
+template<class TYPE> inline SortedList<TYPE>::~SortedList() {
+    finish_vector();
+}
+
+template<class TYPE>
+inline SortedList<TYPE>& SortedList<TYPE>::operator =(const SortedList<TYPE>& rhs) {
+    SortedListImpl::operator =(rhs);
+    return *this;
+}
+
+template<class TYPE>
+inline const SortedList<TYPE>& SortedList<TYPE>::operator =(
+        const SortedList<TYPE>& rhs) const {
+    SortedListImpl::operator =(rhs);
+    return *this;
+}
+
+template<class TYPE>
+inline const TYPE* SortedList<TYPE>::array() const {
+    return static_cast<const TYPE *> (arrayImpl());
+}
+
+template<class TYPE>
+inline TYPE* SortedList<TYPE>::editArray() {
+    return static_cast<TYPE *> (editArrayImpl());
+}
+
+template<class TYPE>
+inline const TYPE& SortedList<TYPE>::operator[](size_t index) const {
+    assert( index<size() );
+    return *(array() + index);
+}
+
+template<class TYPE>
+inline const TYPE& SortedList<TYPE>::itemAt(size_t index) const {
+    return operator[](index);
+}
+
+template<class TYPE>
+inline const TYPE& SortedList<TYPE>::mirrorItemAt(ssize_t index) const {
+    assert( (index>0 ? index : -index)<size() );
+    return *(array() + ((index < 0) ? (size() - index) : index));
+}
+
+template<class TYPE>
+inline const TYPE& SortedList<TYPE>::top() const {
+    return *(array() + size() - 1);
+}
+
+template<class TYPE>
+inline ssize_t SortedList<TYPE>::add(const TYPE& item) {
+    return SortedListImpl::add(&item);
+}
+
+template<class TYPE>
+inline ssize_t SortedList<TYPE>::indexOf(const TYPE& item) const {
+    return SortedListImpl::indexOf(&item);
+}
+
+template<class TYPE>
+inline size_t SortedList<TYPE>::orderOf(const TYPE& item) const {
+    return SortedListImpl::orderOf(&item);
+}
+
+template<class TYPE>
+inline ssize_t SortedList<TYPE>::merge(const Vector<TYPE>& vector) {
+    return SortedListImpl::merge(reinterpret_cast<const VectorImpl&> (vector));
+}
+
+template<class TYPE>
+inline ssize_t SortedList<TYPE>::merge(const SortedList<TYPE>& vector) {
+    return SortedListImpl::merge(reinterpret_cast<const SortedListImpl&> (vector));
+}
+
+template<class TYPE>
+inline ssize_t SortedList<TYPE>::remove(const TYPE& item) {
+    return SortedListImpl::remove(&item);
+}
+
+template<class TYPE>
+inline ssize_t SortedList<TYPE>::removeItemsAt(size_t index, size_t count) {
+    return VectorImpl::removeItemsAt(index, count);
+}
+
+template<class TYPE>
+void SortedList<TYPE>::do_construct(void* storage, size_t num) const {
+    construct_type(reinterpret_cast<TYPE*> (storage), num);
+}
+
+template<class TYPE>
+void SortedList<TYPE>::do_destroy(void* storage, size_t num) const {
+    destroy_type(reinterpret_cast<TYPE*> (storage), num);
+}
+
+template<class TYPE>
+void SortedList<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
+    copy_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (from), num);
+}
+
+template<class TYPE>
+void SortedList<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
+    splat_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (item), num);
+}
+
+template<class TYPE>
+void SortedList<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
+    move_forward_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (from), num);
+}
+
+template<class TYPE>
+void SortedList<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
+    move_backward_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (from), num);
+}
+
+template<class TYPE>
+int SortedList<TYPE>::do_compare(const void* lhs, const void* rhs) const {
+    return compare_type(*reinterpret_cast<const TYPE*> (lhs), *reinterpret_cast<const TYPE*> (rhs));
+}
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_SORTED_LIST_H
diff --git a/libs/hwui/utils/SortedListImpl.cpp b/libs/hwui/utils/SortedListImpl.cpp
new file mode 100644
index 0000000..35171d5
--- /dev/null
+++ b/libs/hwui/utils/SortedListImpl.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SortedListImpl.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Sorted list implementation, not for direct use
+///////////////////////////////////////////////////////////////////////////////
+
+SortedListImpl::SortedListImpl(size_t itemSize, uint32_t flags): VectorImpl(itemSize, flags) {
+}
+
+SortedListImpl::SortedListImpl(const VectorImpl& rhs): VectorImpl(rhs) {
+}
+
+SortedListImpl::~SortedListImpl() {
+}
+
+SortedListImpl& SortedListImpl::operator =(const SortedListImpl& rhs) {
+    return static_cast<SortedListImpl&>
+            (VectorImpl::operator =(static_cast<const VectorImpl&> (rhs)));
+}
+
+ssize_t SortedListImpl::indexOf(const void* item) const {
+    return _indexOrderOf(item);
+}
+
+size_t SortedListImpl::orderOf(const void* item) const {
+    size_t o;
+    _indexOrderOf(item, &o);
+    return o;
+}
+
+ssize_t SortedListImpl::_indexOrderOf(const void* item, size_t* order) const {
+    // binary search
+    ssize_t err = NAME_NOT_FOUND;
+    ssize_t l = 0;
+    ssize_t h = size() - 1;
+    ssize_t mid;
+    const void* a = arrayImpl();
+    const size_t s = itemSize();
+    while (l <= h) {
+        mid = l + (h - l) / 2;
+        const void* const curr = reinterpret_cast<const char *> (a) + (mid * s);
+        const int c = do_compare(curr, item);
+        if (c == 0) {
+            err = l = mid;
+            break;
+        } else if (c < 0) {
+            l = mid + 1;
+        } else {
+            h = mid - 1;
+        }
+    }
+    if (order) {
+        *order = l;
+    }
+    return err;
+}
+
+ssize_t SortedListImpl::add(const void* item) {
+    size_t order;
+    ssize_t index = _indexOrderOf(item, &order);
+    index = VectorImpl::insertAt(item, order, 1);
+    return index;
+}
+
+ssize_t SortedListImpl::merge(const VectorImpl& vector) {
+    // naive merge...
+    if (!vector.isEmpty()) {
+        const void* buffer = vector.arrayImpl();
+        const size_t is = itemSize();
+        size_t s = vector.size();
+        for (size_t i = 0; i < s; i++) {
+            ssize_t err = add(reinterpret_cast<const char*> (buffer) + i * is);
+            if (err < 0) {
+                return err;
+            }
+        }
+    }
+    return NO_ERROR;
+}
+
+ssize_t SortedListImpl::merge(const SortedListImpl& vector) {
+    // we've merging a sorted vector... nice!
+    ssize_t err = NO_ERROR;
+    if (!vector.isEmpty()) {
+        // first take care of the case where the vectors are sorted together
+        if (do_compare(vector.itemLocation(vector.size() - 1), arrayImpl()) <= 0) {
+            err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&> (vector), 0);
+        } else if (do_compare(vector.arrayImpl(), itemLocation(size() - 1)) >= 0) {
+            err = VectorImpl::appendVector(static_cast<const VectorImpl&> (vector));
+        } else {
+            // this could be made a little better
+            err = merge(static_cast<const VectorImpl&> (vector));
+        }
+    }
+    return err;
+}
+
+ssize_t SortedListImpl::remove(const void* item) {
+    ssize_t i = indexOf(item);
+    if (i >= 0) {
+        VectorImpl::removeItemsAt(i, 1);
+    }
+    return i;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/utils/SortedListImpl.h b/libs/hwui/utils/SortedListImpl.h
new file mode 100644
index 0000000..dc385b5
--- /dev/null
+++ b/libs/hwui/utils/SortedListImpl.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_SORTED_LIST_IMPL_H
+#define ANDROID_HWUI_SORTED_LIST_IMPL_H
+
+#include <utils/VectorImpl.h>
+
+namespace android {
+namespace uirenderer {
+
+class SortedListImpl: public VectorImpl {
+public:
+    SortedListImpl(size_t itemSize, uint32_t flags);
+    SortedListImpl(const VectorImpl& rhs);
+    virtual ~SortedListImpl();
+
+    SortedListImpl& operator =(const SortedListImpl& rhs);
+
+    ssize_t indexOf(const void* item) const;
+    size_t orderOf(const void* item) const;
+    ssize_t add(const void* item);
+    ssize_t merge(const VectorImpl& vector);
+    ssize_t merge(const SortedListImpl& vector);
+    ssize_t remove(const void* item);
+
+protected:
+    virtual int do_compare(const void* lhs, const void* rhs) const = 0;
+
+private:
+    ssize_t _indexOrderOf(const void* item, size_t* order = 0) const;
+
+    // these are made private, because they can't be used on a SortedVector
+    // (they don't have an implementation either)
+    ssize_t add();
+    void pop();
+    void push();
+    void push(const void* item);
+    ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
+    ssize_t appendVector(const VectorImpl& vector);
+    ssize_t insertArrayAt(const void* array, size_t index, size_t length);
+    ssize_t appendArray(const void* array, size_t length);
+    ssize_t insertAt(size_t where, size_t numItems = 1);
+    ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
+    ssize_t replaceAt(size_t index);
+    ssize_t replaceAt(const void* item, size_t index);
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_SORTED_LIST_IMPL_H
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
index 6f2cd07..2ec003f 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -23,7 +23,13 @@
 # TODO: This should go into build/core/config.mk
 RSG_GENERATOR:=$(LOCAL_BUILT_MODULE)
 
-
+include $(CLEAR_VARS)
+input_data_file := $(LOCAL_PATH)/rslib.bc
+slangdata_output_var_name := rs_runtime_lib_bc
+LOCAL_MODULE := librslib_rt
+LOCAL_MODULE_TAGS := optional
+include frameworks/compile/slang/SlangData.mk
+include $(BUILD_STATIC_LIBRARY)
 
 # Build render script lib ====================
 
@@ -76,37 +82,43 @@
 LOCAL_SRC_FILES:= \
 	rsAdapter.cpp \
 	rsAllocation.cpp \
+	rsAnimation.cpp \
 	rsComponent.cpp \
 	rsContext.cpp \
 	rsDevice.cpp \
 	rsElement.cpp \
-        rsFileA3D.cpp \
-	rsLight.cpp \
+	rsFileA3D.cpp \
+	rsFont.cpp \
 	rsLocklessFifo.cpp \
 	rsObjectBase.cpp \
 	rsMatrix.cpp \
-        rsMesh.cpp \
-	rsNoise.cpp \
+	rsMesh.cpp \
+	rsMutex.cpp \
 	rsProgram.cpp \
 	rsProgramFragment.cpp \
-	rsProgramFragmentStore.cpp \
+	rsProgramStore.cpp \
 	rsProgramRaster.cpp \
 	rsProgramVertex.cpp \
 	rsSampler.cpp \
 	rsScript.cpp \
 	rsScriptC.cpp \
 	rsScriptC_Lib.cpp \
-        rsShaderCache.cpp \
-	rsSimpleMesh.cpp \
+	rsScriptC_LibCL.cpp \
+	rsScriptC_LibGL.cpp \
+	rsShaderCache.cpp \
+	rsSignal.cpp \
+	rsStream.cpp \
 	rsThreadIO.cpp \
 	rsType.cpp \
 	rsVertexArray.cpp
 
-ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
-	LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
-endif
 
-LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libGLESv2 libui libacc
+LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libGLESv2 libui libbcc
+
+LOCAL_STATIC_LIBRARIES := libft2 librslib_rt
+
+LOCAL_C_INCLUDES += external/freetype/include
+
 LOCAL_LDLIBS := -lpthread -ldl
 LOCAL_MODULE:= libRS
 LOCAL_MODULE_TAGS := optional
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index d280f50..8c2081d 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -27,23 +27,27 @@
 //////////////////////////////////////////////////////
 //
 
+typedef void * RsAsyncVoidPtr;
+
 typedef void * RsAdapter1D;
 typedef void * RsAdapter2D;
 typedef void * RsAllocation;
+typedef void * RsAnimation;
 typedef void * RsContext;
 typedef void * RsDevice;
 typedef void * RsElement;
 typedef void * RsFile;
+typedef void * RsFont;
 typedef void * RsSampler;
 typedef void * RsScript;
-typedef void * RsSimpleMesh;
+typedef void * RsMesh;
 typedef void * RsType;
-typedef void * RsLight;
+typedef void * RsObjectBase;
 
 typedef void * RsProgram;
 typedef void * RsProgramVertex;
 typedef void * RsProgramFragment;
-typedef void * RsProgramFragmentStore;
+typedef void * RsProgramStore;
 typedef void * RsProgramRaster;
 
 typedef void (* RsBitmapCallback_t)(void *);
@@ -53,14 +57,27 @@
     RS_DEVICE_PARAM_COUNT
 };
 
+typedef struct {
+    uint32_t colorMin;
+    uint32_t colorPref;
+    uint32_t alphaMin;
+    uint32_t alphaPref;
+    uint32_t depthMin;
+    uint32_t depthPref;
+    uint32_t stencilMin;
+    uint32_t stencilPref;
+    uint32_t samplesMin;
+    uint32_t samplesPref;
+    float samplesQ;
+} RsSurfaceConfig;
+
 RsDevice rsDeviceCreate();
 void rsDeviceDestroy(RsDevice);
 void rsDeviceSetConfig(RsDevice, RsDeviceParam, int32_t value);
 
 RsContext rsContextCreate(RsDevice, uint32_t version);
-RsContext rsContextCreateGL(RsDevice, uint32_t version, bool useDepth);
+RsContext rsContextCreateGL(RsDevice, uint32_t version, RsSurfaceConfig sc);
 void rsContextDestroy(RsContext);
-void rsObjDestroyOOB(RsContext, void *);
 
 uint32_t rsContextGetMessage(RsContext, void *data, size_t *receiveLen, size_t bufferLen, bool wait);
 void rsContextInitToClient(RsContext);
@@ -83,11 +100,17 @@
     RS_TYPE_UNSIGNED_32,
     RS_TYPE_UNSIGNED_64,
 
+    RS_TYPE_BOOLEAN,
+
     RS_TYPE_UNSIGNED_5_6_5,
     RS_TYPE_UNSIGNED_5_5_5_1,
     RS_TYPE_UNSIGNED_4_4_4_4,
 
-    RS_TYPE_ELEMENT,
+    RS_TYPE_MATRIX_4X4,
+    RS_TYPE_MATRIX_3X3,
+    RS_TYPE_MATRIX_2X2,
+
+    RS_TYPE_ELEMENT = 1000,
     RS_TYPE_TYPE,
     RS_TYPE_ALLOCATION,
     RS_TYPE_SAMPLER,
@@ -96,24 +119,17 @@
     RS_TYPE_PROGRAM_FRAGMENT,
     RS_TYPE_PROGRAM_VERTEX,
     RS_TYPE_PROGRAM_RASTER,
-    RS_TYPE_PROGRAM_STORE
+    RS_TYPE_PROGRAM_STORE,
 };
 
 enum RsDataKind {
     RS_KIND_USER,
-    RS_KIND_COLOR,
-    RS_KIND_POSITION,
-    RS_KIND_TEXTURE,
-    RS_KIND_NORMAL,
-    RS_KIND_INDEX,
-    RS_KIND_POINT_SIZE,
 
-    RS_KIND_PIXEL_L,
+    RS_KIND_PIXEL_L = 7,
     RS_KIND_PIXEL_A,
     RS_KIND_PIXEL_LA,
     RS_KIND_PIXEL_RGB,
     RS_KIND_PIXEL_RGBA,
-
 };
 
 enum RsSamplerParam {
@@ -121,7 +137,8 @@
     RS_SAMPLER_MAG_FILTER,
     RS_SAMPLER_WRAP_S,
     RS_SAMPLER_WRAP_T,
-    RS_SAMPLER_WRAP_R
+    RS_SAMPLER_WRAP_R,
+    RS_SAMPLER_ANISO
 };
 
 enum RsSamplerValue {
@@ -205,9 +222,96 @@
 enum RsError {
     RS_ERROR_NONE,
     RS_ERROR_BAD_SHADER,
-    RS_ERROR_BAD_SCRIPT
+    RS_ERROR_BAD_SCRIPT,
+    RS_ERROR_BAD_VALUE,
+    RS_ERROR_OUT_OF_MEMORY
 };
 
+enum RsAnimationInterpolation {
+    RS_ANIMATION_INTERPOLATION_STEP,
+    RS_ANIMATION_INTERPOLATION_LINEAR,
+    RS_ANIMATION_INTERPOLATION_BEZIER,
+    RS_ANIMATION_INTERPOLATION_CARDINAL,
+    RS_ANIMATION_INTERPOLATION_HERMITE,
+    RS_ANIMATION_INTERPOLATION_BSPLINE
+};
+
+enum RsAnimationEdge {
+    RS_ANIMATION_EDGE_UNDEFINED,
+    RS_ANIMATION_EDGE_CONSTANT,
+    RS_ANIMATION_EDGE_GRADIENT,
+    RS_ANIMATION_EDGE_CYCLE,
+    RS_ANIMATION_EDGE_OSCILLATE,
+    RS_ANIMATION_EDGE_CYLE_RELATIVE
+};
+
+enum RsA3DClassID {
+    RS_A3D_CLASS_ID_UNKNOWN,
+    RS_A3D_CLASS_ID_MESH,
+    RS_A3D_CLASS_ID_TYPE,
+    RS_A3D_CLASS_ID_ELEMENT,
+    RS_A3D_CLASS_ID_ALLOCATION,
+    RS_A3D_CLASS_ID_PROGRAM_VERTEX,
+    RS_A3D_CLASS_ID_PROGRAM_RASTER,
+    RS_A3D_CLASS_ID_PROGRAM_FRAGMENT,
+    RS_A3D_CLASS_ID_PROGRAM_STORE,
+    RS_A3D_CLASS_ID_SAMPLER,
+    RS_A3D_CLASS_ID_ANIMATION,
+    RS_A3D_CLASS_ID_ADAPTER_1D,
+    RS_A3D_CLASS_ID_ADAPTER_2D,
+    RS_A3D_CLASS_ID_SCRIPT_C
+};
+
+enum RsCullMode {
+    RS_CULL_BACK,
+    RS_CULL_FRONT,
+    RS_CULL_NONE
+};
+
+typedef struct {
+    RsA3DClassID classID;
+    const char* objectName;
+} RsFileIndexEntry;
+
+// Script to Script
+typedef struct {
+    uint32_t xStart;
+    uint32_t xEnd;
+    uint32_t yStart;
+    uint32_t yEnd;
+    uint32_t zStart;
+    uint32_t zEnd;
+    uint32_t arrayStart;
+    uint32_t arrayEnd;
+
+} RsScriptCall;
+
+// A3D loading and object update code.
+// Should only be called at object creation, not thread safe
+RsObjectBase rsaFileA3DGetEntryByIndex(RsContext, uint32_t idx, RsFile);
+RsFile rsaFileA3DCreateFromAssetStream(RsContext, const void *data, uint32_t len);
+void rsaFileA3DGetNumIndexEntries(RsContext, int32_t *numEntries, RsFile);
+void rsaFileA3DGetIndexEntries(RsContext, RsFileIndexEntry *fileEntries,uint32_t numEntries, RsFile);
+void rsaGetName(RsContext, void * obj, const char **name);
+// Mesh update functions
+void rsaMeshGetVertexBufferCount(RsContext, RsMesh, int32_t *vtxCount);
+void rsaMeshGetIndexCount(RsContext, RsMesh, int32_t *idxCount);
+void rsaMeshGetVertices(RsContext, RsMesh, RsAllocation *vtxData, uint32_t vtxDataCount);
+void rsaMeshGetIndices(RsContext, RsMesh, RsAllocation *va, uint32_t *primType, uint32_t idxDataCount);
+// Allocation update
+const void* rsaAllocationGetType(RsContext con, RsAllocation va);
+// Type update
+void rsaTypeGetNativeData(RsContext, RsType, uint32_t *typeData, uint32_t typeDataSize);
+// Element update
+void rsaElementGetNativeData(RsContext, RsElement, uint32_t *elemData, uint32_t elemDataSize);
+void rsaElementGetSubElements(RsContext, RsElement, uint32_t *ids, const char **names, uint32_t dataSize);
+
+// Async commands for returning new IDS
+RsType rsaTypeCreate(RsContext, RsElement, uint32_t dimCount,
+                     const RsDimension *dims, const uint32_t *vals);
+RsAllocation rsaAllocationCreateTyped(RsContext rsc, RsType vtype);
+RsAllocation rsaAllocationCreateFromBitmap(RsContext con, uint32_t w, uint32_t h, RsElement _dst, RsElement _src,  bool genMips, const void *data);
+
 #ifndef NO_RS_FUNCS
 #include "rsgApiFuncDecl.h"
 #endif
diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h
index 99b8c04..b82eaf1 100644
--- a/libs/rs/RenderScriptEnv.h
+++ b/libs/rs/RenderScriptEnv.h
@@ -9,12 +9,10 @@
 typedef void * RsElement;
 typedef void * RsSampler;
 typedef void * RsScript;
-typedef void * RsSimpleMesh;
+typedef void * RsMesh;
 typedef void * RsType;
 typedef void * RsProgramFragment;
-typedef void * RsProgramFragmentStore;
-typedef void * RsLight;
-
+typedef void * RsProgramStore;
 
 typedef struct {
     float m[16];
@@ -28,4 +26,4 @@
 #define RS_PROGRAM_VERTEX_MODELVIEW_OFFSET 0
 #define RS_PROGRAM_VERTEX_PROJECTION_OFFSET 16
 #define RS_PROGRAM_VERTEX_TEXTURE_OFFSET 32
-
+#define RS_PROGRAM_VERTEX_MVP_OFFSET 48
diff --git a/libs/rs/java/Balls/Android.mk b/libs/rs/java/Balls/Android.mk
new file mode 100644
index 0000000..5b65628
--- /dev/null
+++ b/libs/rs/java/Balls/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+
+LOCAL_PACKAGE_NAME := Balls
+
+include $(BUILD_PACKAGE)
+
+endif
diff --git a/libs/rs/java/Balls/AndroidManifest.xml b/libs/rs/java/Balls/AndroidManifest.xml
new file mode 100644
index 0000000..2fffc5f
--- /dev/null
+++ b/libs/rs/java/Balls/AndroidManifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.balls">
+    <application 
+        android:label="Balls"
+        android:icon="@drawable/test_pattern">
+        <activity android:name="Balls"
+                  android:screenOrientation="landscape">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/libs/rs/java/Balls/res/drawable/flares.png b/libs/rs/java/Balls/res/drawable/flares.png
new file mode 100644
index 0000000..3a5c970
--- /dev/null
+++ b/libs/rs/java/Balls/res/drawable/flares.png
Binary files differ
diff --git a/libs/rs/java/Balls/res/drawable/test_pattern.png b/libs/rs/java/Balls/res/drawable/test_pattern.png
new file mode 100644
index 0000000..e7d1455
--- /dev/null
+++ b/libs/rs/java/Balls/res/drawable/test_pattern.png
Binary files differ
diff --git a/libs/rs/java/Balls/src/com/android/balls/Balls.java b/libs/rs/java/Balls/src/com/android/balls/Balls.java
new file mode 100644
index 0000000..5957c94
--- /dev/null
+++ b/libs/rs/java/Balls/src/com/android/balls/Balls.java
@@ -0,0 +1,128 @@
+/*
+ * 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.android.balls;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+
+public class Balls extends Activity implements SensorEventListener {
+    //EventListener mListener = new EventListener();
+
+    private static final String LOG_TAG = "libRS_jni";
+    private static final boolean DEBUG  = false;
+    private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+
+    private BallsView mView;
+    private SensorManager mSensorManager;
+
+    // get the current looper (from your Activity UI thread for instance
+
+
+    public void onSensorChanged(SensorEvent event) {
+        //android.util.Log.d("rs", "sensor: " + event.sensor + ", x: " + event.values[0] + ", y: " + event.values[1] + ", z: " + event.values[2]);
+        synchronized (this) {
+            if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
+                if(mView != null) {
+                    mView.setAccel(event.values[0], event.values[1], event.values[2]);
+                }
+            }
+        }
+    }
+
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new BallsView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        mSensorManager.registerListener(this,
+                                        mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
+                                        SensorManager.SENSOR_DELAY_FASTEST);
+
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mView.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        Log.e("rs", "onPause");
+
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onPause();
+        mView.onPause();
+
+
+
+        //Runtime.getRuntime().exit(0);
+    }
+
+    @Override
+    protected void onStop() {
+        mSensorManager.unregisterListener(this);
+        super.onStop();
+    }
+
+    static void log(String message) {
+        if (LOG_ENABLED) {
+            Log.v(LOG_TAG, message);
+        }
+    }
+
+
+}
+
diff --git a/libs/rs/java/Balls/src/com/android/balls/BallsRS.java b/libs/rs/java/Balls/src/com/android/balls/BallsRS.java
new file mode 100644
index 0000000..9c845aa
--- /dev/null
+++ b/libs/rs/java/Balls/src/com/android/balls/BallsRS.java
@@ -0,0 +1,146 @@
+/*
+ * 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.android.balls;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+public class BallsRS {
+    public static final int PART_COUNT = 800;
+
+    public BallsRS() {
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    private ScriptC_balls mScript;
+    private ScriptC_ball_physics mPhysicsScript;
+    private ProgramFragment mPF;
+    private ProgramVertex mPV;
+    private ProgramRaster mPR;
+    private ProgramStore mPS;
+    private ScriptField_Point mPoints;
+    private ScriptField_Point mArcs;
+    private ScriptField_VpConsts mVpConsts;
+
+    void updateProjectionMatrices() {
+        mVpConsts = new ScriptField_VpConsts(mRS, 1);
+        ScriptField_VpConsts.Item i = new ScriptField_VpConsts.Item();
+        Matrix4f mvp = new Matrix4f();
+        mvp.loadOrtho(0, mRS.getWidth(), mRS.getHeight(), 0, -1, 1);
+        i.MVP = mvp;
+        mVpConsts.set(i, 0, true);
+    }
+
+    private void createProgramRaster() {
+        ProgramRaster.Builder b = new ProgramRaster.Builder(mRS);
+        mPR = b.create();
+        mScript.set_gPR(mPR);
+    }
+
+    private void createProgramVertex() {
+        updateProjectionMatrices();
+
+        ProgramVertex.ShaderBuilder sb = new ProgramVertex.ShaderBuilder(mRS);
+        String t =  "varying vec4 varColor;\n" +
+                    "void main() {\n" +
+                    "  vec4 pos = vec4(0.0, 0.0, 0.0, 1.0);\n" +
+                    "  pos.xy = ATTRIB_position;\n" +
+                    "  gl_Position = UNI_MVP * pos;\n" +
+                    "  varColor = ATTRIB_color;\n" +
+                    "  gl_PointSize = ATTRIB_size;\n" +
+                    "}\n";
+        sb.setShader(t);
+        sb.addConstant(mVpConsts.getType());
+        sb.addInput(mPoints.getElement());
+        ProgramVertex pvs = sb.create();
+        pvs.bindConstants(mVpConsts.getAllocation(), 0);
+        mScript.set_gPV(pvs);
+    }
+
+    private Allocation loadTexture(int id) {
+        final Allocation allocation = Allocation.createFromBitmapResource(mRS, mRes,
+                id, Element.RGB_565(mRS), false);
+        allocation.uploadToTexture(0);
+        return allocation;
+    }
+
+    public void init(RenderScriptGL rs, Resources res, int width, int height) {
+        mRS = rs;
+        mRes = res;
+
+        ProgramFragment.Builder pfb = new ProgramFragment.Builder(rs);
+        pfb.setPointSpriteTexCoordinateReplacement(true);
+        pfb.setTexture(ProgramFragment.Builder.EnvMode.MODULATE,
+                           ProgramFragment.Builder.Format.RGBA, 0);
+        pfb.setVaryingColor(true);
+        mPF = pfb.create();
+        rs.contextBindProgramFragment(mPF);
+
+        mPF.bindTexture(loadTexture(R.drawable.flares), 0);
+
+        mPoints = new ScriptField_Point(mRS, PART_COUNT);
+        mArcs = new ScriptField_Point(mRS, PART_COUNT * 2);
+
+        Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
+        smb.addVertexAllocation(mPoints.getAllocation());
+        smb.addIndexType(Primitive.POINT);
+        Mesh smP = smb.create();
+
+        smb = new Mesh.AllocationBuilder(mRS);
+        smb.addVertexAllocation(mArcs.getAllocation());
+        smb.addIndexType(Primitive.LINE);
+        Mesh smA = smb.create();
+
+        mPhysicsScript = new ScriptC_ball_physics(mRS, mRes, R.raw.ball_physics);
+
+        mScript = new ScriptC_balls(mRS, mRes, R.raw.balls);
+        mScript.set_partMesh(smP);
+        mScript.set_arcMesh(smA);
+        mScript.set_physics_script(mPhysicsScript);
+        mScript.bind_point(mPoints);
+        mScript.bind_arc(mArcs);
+        mScript.bind_balls1(new ScriptField_Ball(mRS, PART_COUNT));
+        mScript.bind_balls2(new ScriptField_Ball(mRS, PART_COUNT));
+
+        mScript.set_gPF(mPF);
+        createProgramVertex();
+        createProgramRaster();
+
+        mPS = ProgramStore.BLEND_ADD_DEPTH_NO_DEPTH(mRS);
+        mScript.set_gPS(mPS);
+
+        mPhysicsScript.set_gMinPos(new Float2(5, 5));
+        mPhysicsScript.set_gMaxPos(new Float2(width - 5, height - 5));
+
+        mScript.invoke_initParts(width, height);
+
+        mRS.contextBindRootScript(mScript);
+    }
+
+    public void newTouchPosition(float x, float y, float pressure, int id) {
+        mPhysicsScript.set_touchX(x);
+        mPhysicsScript.set_touchY(y);
+        mPhysicsScript.set_touchPressure(pressure);
+    }
+
+    public void setAccel(float x, float y) {
+        mPhysicsScript.set_gGravityVector(new Float2(x, y));
+    }
+
+}
diff --git a/libs/rs/java/Balls/src/com/android/balls/BallsView.java b/libs/rs/java/Balls/src/com/android/balls/BallsView.java
new file mode 100644
index 0000000..635dac9
--- /dev/null
+++ b/libs/rs/java/Balls/src/com/android/balls/BallsView.java
@@ -0,0 +1,115 @@
+/*
+ * 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.android.balls;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class BallsView extends RSSurfaceView {
+
+    public BallsView(Context context) {
+        super(context);
+        //setFocusable(true);
+    }
+
+    private RenderScriptGL mRS;
+    private BallsRS mRender;
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            mRS = createRenderScript(sc);
+            mRS.contextSetSurface(w, h, holder.getSurface());
+            mRender = new BallsRS();
+            mRender.init(mRS, getResources(), w, h);
+        }
+        mRender.updateProjectionMatrices();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        if(mRS != null) {
+            mRS = null;
+            destroyRenderScript();
+        }
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        int act = ev.getActionMasked();
+        if (act == ev.ACTION_UP) {
+            mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
+            return false;
+        } else if (act == MotionEvent.ACTION_POINTER_UP) {
+            // only one pointer going up, we can get the index like this
+            int pointerIndex = ev.getActionIndex();
+            int pointerId = ev.getPointerId(pointerIndex);
+            mRender.newTouchPosition(0, 0, 0, pointerId);
+        }
+        int count = ev.getHistorySize();
+        int pcount = ev.getPointerCount();
+
+        for (int p=0; p < pcount; p++) {
+            int id = ev.getPointerId(p);
+            mRender.newTouchPosition(ev.getX(p),
+                                     ev.getY(p),
+                                     ev.getPressure(p),
+                                     id);
+
+            for (int i=0; i < count; i++) {
+                mRender.newTouchPosition(ev.getHistoricalX(p, i),
+                                         ev.getHistoricalY(p, i),
+                                         ev.getHistoricalPressure(p, i),
+                                         id);
+            }
+        }
+        return true;
+    }
+
+    void setAccel(float x, float y, float z) {
+        if (mRender == null) {
+            return;
+        }
+        mRender.setAccel(x, -y);
+    }
+
+}
+
+
diff --git a/libs/rs/java/Balls/src/com/android/balls/ball_physics.rs b/libs/rs/java/Balls/src/com/android/balls/ball_physics.rs
new file mode 100644
index 0000000..b5f149c
--- /dev/null
+++ b/libs/rs/java/Balls/src/com/android/balls/ball_physics.rs
@@ -0,0 +1,116 @@
+#pragma version(1)
+#pragma rs java_package_name(com.android.balls)
+
+#include "balls.rsh"
+
+float2 gGravityVector = {0.f, 9.8f};
+
+#pragma rs export_func(setGamma);
+
+float2 gMinPos = {0.f, 0.f};
+float2 gMaxPos = {1280.f, 700.f};
+
+float touchX;
+float touchY;
+float touchPressure = 0.f;
+
+void setGamma(float g) {
+}
+
+
+void root(const Ball_t *ballIn, Ball_t *ballOut, const BallControl_t *ctl, uint32_t x) {
+    float2 fv = {0, 0};
+    float2 pos = ballIn->position;
+    //rsDebug("physics pos in", pos);
+
+    int arcID = -1;
+    float arcInvStr = 100000;
+
+    const Ball_t * bPtr = rsGetElementAt(ctl->ain, 0);
+    for (uint32_t xin = 0; xin < ctl->dimX; xin++) {
+        float2 vec = bPtr[xin].position - pos;
+        float2 vec2 = vec * vec;
+        float len2 = vec2.x + vec2.y;
+
+        if (len2 < 1000) {
+            if (len2 > (4*4)) {
+                // Repulsion
+                float len = sqrt(len2);
+                if (len < arcInvStr) {
+                    arcInvStr = len;
+                    arcID = xin;
+                }
+                fv -= (vec / (len * len * len)) * 20000.f;
+            } else {
+                if (len2 < 0.1) {
+                    continue;
+                }
+                // Collision
+                float2 axis = normalize(vec);
+                float e1 = dot(axis, ballIn->delta);
+                float e2 = dot(axis, bPtr[xin].delta);
+                float e = (e1 - e2) * 0.45f;
+                if (e1 > 0) {
+                    fv -= axis * e;
+                } else {
+                    fv += axis * e;
+                }
+            }
+        }
+    }
+
+    fv -= gGravityVector;
+    fv *= ctl->dt;
+
+    {
+        float2 tp = {touchX, touchY};
+        float2 vec = tp - ballIn->position;
+        float2 vec2 = vec * vec;
+        float len2 = vec2.x + vec2.y;
+
+        if (len2 > 0.2) {
+            float len = sqrt(len2);
+            fv -= (vec / (len * len)) * touchPressure * 1000.f;
+        }
+    }
+
+    ballOut->delta = ballIn->delta * 0.998f;
+    ballOut->position = ballIn->position;
+
+    ballOut->delta += fv;
+    ballOut->position += ballOut->delta * ctl->dt;
+
+    if (ballOut->position.x > gMaxPos.x) {
+        if (ballOut->delta.x > 0) {
+            ballOut->delta.x *= -0.7;
+        }
+        ballOut->position.x = gMaxPos.x;
+    }
+    if (ballOut->position.y > gMaxPos.y) {
+        if (ballOut->delta.y > 0) {
+            ballOut->delta.y *= -0.7;
+        }
+        ballOut->position.y = gMaxPos.y - 1.f;
+    }
+    if (ballOut->position.x < gMinPos.x) {
+        if (ballOut->delta.x < 0) {
+            ballOut->delta.x *= -0.7;
+        }
+        ballOut->position.x = gMinPos.x + 1.f;
+    }
+    if (ballOut->position.y < gMinPos.y) {
+        if (ballOut->delta.y < 0) {
+            ballOut->delta.y *= -0.7;
+        }
+        ballOut->position.y = gMinPos.y + 1.f;
+    }
+
+    ballOut->color.b = 1.f;
+    ballOut->color.r = min(sqrt(length(ballOut->delta)) * 0.1f, 1.f);
+    ballOut->color.g = min(sqrt(length(fv) * 0.1f), 1.f);
+    ballOut->arcID = arcID;
+    ballOut->arcStr = 8 / arcInvStr;
+
+    //rsDebug("physics pos out", ballOut->position);
+}
+
diff --git a/libs/rs/java/Balls/src/com/android/balls/balls.rs b/libs/rs/java/Balls/src/com/android/balls/balls.rs
new file mode 100644
index 0000000..493633b
--- /dev/null
+++ b/libs/rs/java/Balls/src/com/android/balls/balls.rs
@@ -0,0 +1,106 @@
+#pragma version(1)
+#pragma rs java_package_name(com.android.balls)
+#include "rs_graphics.rsh"
+
+#include "balls.rsh"
+
+#pragma stateFragment(parent)
+
+rs_program_fragment gPF;
+rs_program_vertex gPV;
+rs_program_raster gPR;
+rs_program_store gPS;
+rs_mesh partMesh;
+rs_mesh arcMesh;
+
+typedef struct __attribute__((packed, aligned(4))) Point {
+    float2 position;
+    uchar4 color;
+    float size;
+} Point_t;
+Point_t *point;
+Point_t *arc;
+
+typedef struct VpConsts {
+    //rs_matrix4x4 Proj;
+    rs_matrix4x4 MVP;
+} VpConsts_t;
+VpConsts_t *vpConstants;
+
+
+#pragma rs export_func(initParts)
+
+rs_script physics_script;
+
+Ball_t *balls1;
+Ball_t *balls2;
+
+static int frame = 0;
+
+void initParts(int w, int h)
+{
+    uint32_t dimX = rsAllocationGetDimX(rsGetAllocation(balls1));
+
+    for (uint32_t ct=0; ct < dimX; ct++) {
+        balls1[ct].position.x = rsRand(0.f, (float)w);
+        balls1[ct].position.y = rsRand(0.f, (float)h);
+        balls1[ct].delta.x = 0.f;
+        balls1[ct].delta.y = 0.f;
+        balls1[ct].arcID = -1;
+        balls1[ct].color = 0.f;
+    }
+}
+
+
+
+int root() {
+    rsgClearColor(0.f, 0.f, 0.f, 1.f);
+
+    BallControl_t bc = {0};
+    Ball_t *bout;
+
+    if (frame & 1) {
+        rsSetObject(&bc.ain, rsGetAllocation(balls2));
+        rsSetObject(&bc.aout, rsGetAllocation(balls1));
+        bout = balls2;
+    } else {
+        rsSetObject(&bc.ain, rsGetAllocation(balls1));
+        rsSetObject(&bc.aout, rsGetAllocation(balls2));
+        bout = balls1;
+    }
+
+    bc.dimX = rsAllocationGetDimX(bc.ain);
+    bc.dt = 1.f / 30.f;
+
+    rsForEach(physics_script, bc.ain, bc.aout, &bc);
+
+    uint32_t arcIdx = 0;
+    for (uint32_t ct=0; ct < bc.dimX; ct++) {
+        point[ct].position = bout[ct].position;
+        point[ct].color = rsPackColorTo8888(bout[ct].color);
+        point[ct].size = 6.f + bout[ct].color.g * 6.f;
+
+        if (bout[ct].arcID >= 0) {
+            arc[arcIdx].position = bout[ct].position;
+            arc[arcIdx].color.r = min(bout[ct].arcStr, 1.f) * 0xff;
+            arc[arcIdx].color.g = 0;
+            arc[arcIdx].color.b = 0;
+            arc[arcIdx].color.a = 0xff;
+            arc[arcIdx+1].position = bout[bout[ct].arcID].position;
+            arc[arcIdx+1].color = arc[arcIdx].color;
+            arcIdx += 2;
+        }
+    }
+
+    frame++;
+    rsgBindProgramFragment(gPF);
+    rsgBindProgramVertex(gPV);
+    rsgBindProgramRaster(gPR);
+    rsgBindProgramStore(gPS);
+    rsgDrawMesh(arcMesh, 0, 0, arcIdx);
+    rsgDrawMesh(partMesh);
+    rsClearObject(&bc.ain);
+    rsClearObject(&bc.aout);
+    return 1;
+}
+
diff --git a/libs/rs/java/Balls/src/com/android/balls/balls.rsh b/libs/rs/java/Balls/src/com/android/balls/balls.rsh
new file mode 100644
index 0000000..ed3c31a
--- /dev/null
+++ b/libs/rs/java/Balls/src/com/android/balls/balls.rsh
@@ -0,0 +1,17 @@
+
+typedef struct __attribute__((packed, aligned(4))) Ball {
+    float2 delta;
+    float2 position;
+    float3 color;
+    int arcID;
+    float arcStr;
+} Ball_t;
+Ball_t *balls;
+
+
+typedef struct BallControl {
+    uint32_t dimX;
+    rs_allocation ain;
+    rs_allocation aout;
+    float dt;
+} BallControl_t;
diff --git a/libs/rs/java/Film/Android.mk b/libs/rs/java/Film/Android.mk
deleted file mode 100644
index 9e6ed7e..0000000
--- a/libs/rs/java/Film/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
-
-LOCAL_PACKAGE_NAME := Film
-
-include $(BUILD_PACKAGE)
diff --git a/libs/rs/java/Film/AndroidManifest.xml b/libs/rs/java/Film/AndroidManifest.xml
deleted file mode 100644
index a5ce8a1..0000000
--- a/libs/rs/java/Film/AndroidManifest.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.film">
-    <application android:label="Film">
-        <activity android:name="Film"
-                  android:screenOrientation="portrait"
-                  android:theme="@android:style/Theme.Black.NoTitleBar">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/libs/rs/java/Film/res/drawable/p01.png b/libs/rs/java/Film/res/drawable/p01.png
deleted file mode 100644
index a9b9bdb..0000000
--- a/libs/rs/java/Film/res/drawable/p01.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p02.png b/libs/rs/java/Film/res/drawable/p02.png
deleted file mode 100644
index 8162c82..0000000
--- a/libs/rs/java/Film/res/drawable/p02.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p03.png b/libs/rs/java/Film/res/drawable/p03.png
deleted file mode 100644
index e3e26c0..0000000
--- a/libs/rs/java/Film/res/drawable/p03.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p04.png b/libs/rs/java/Film/res/drawable/p04.png
deleted file mode 100644
index daee603..0000000
--- a/libs/rs/java/Film/res/drawable/p04.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p05.png b/libs/rs/java/Film/res/drawable/p05.png
deleted file mode 100644
index fac5248..0000000
--- a/libs/rs/java/Film/res/drawable/p05.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p06.png b/libs/rs/java/Film/res/drawable/p06.png
deleted file mode 100644
index 3b51261..0000000
--- a/libs/rs/java/Film/res/drawable/p06.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p07.png b/libs/rs/java/Film/res/drawable/p07.png
deleted file mode 100644
index d8bd938..0000000
--- a/libs/rs/java/Film/res/drawable/p07.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p08.png b/libs/rs/java/Film/res/drawable/p08.png
deleted file mode 100644
index ef175e8..0000000
--- a/libs/rs/java/Film/res/drawable/p08.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p09.png b/libs/rs/java/Film/res/drawable/p09.png
deleted file mode 100644
index 7bf3874..0000000
--- a/libs/rs/java/Film/res/drawable/p09.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p10.png b/libs/rs/java/Film/res/drawable/p10.png
deleted file mode 100644
index 908827d..0000000
--- a/libs/rs/java/Film/res/drawable/p10.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p11.png b/libs/rs/java/Film/res/drawable/p11.png
deleted file mode 100644
index 1289f71..0000000
--- a/libs/rs/java/Film/res/drawable/p11.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p12.png b/libs/rs/java/Film/res/drawable/p12.png
deleted file mode 100644
index e1af16a..0000000
--- a/libs/rs/java/Film/res/drawable/p12.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p13.png b/libs/rs/java/Film/res/drawable/p13.png
deleted file mode 100644
index d08bcbe..0000000
--- a/libs/rs/java/Film/res/drawable/p13.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Film/res/raw/filmimage.c b/libs/rs/java/Film/res/raw/filmimage.c
deleted file mode 100644
index d154c68..0000000
--- a/libs/rs/java/Film/res/raw/filmimage.c
+++ /dev/null
@@ -1,110 +0,0 @@
-// Fountain test script
-
-#pragma version(1)
-#pragma stateVertex(orthoWindow)
-#pragma stateRaster(flat)
-#pragma stateFragment(PgmFragBackground)
-#pragma stateStore(MyBlend)
-
-
-int main(void* con, int ft, int launchID) {
-    int count, touch, x, y, rate, maxLife, lifeShift;
-    int life;
-    int ct, ct2;
-    int newPart;
-    int drawCount;
-    int dx, dy, idx;
-    int posx,posy;
-    int c;
-    int srcIdx;
-    int dstIdx;
-
-    count = loadI32(con, 0, 1);
-    touch = loadI32(con, 0, 2);
-    x = loadI32(con, 0, 3);
-    y = loadI32(con, 0, 4);
-
-    rate = 4;
-    maxLife = (count / rate) - 1;
-    lifeShift = 0;
-    {
-        life = maxLife;
-        while (life > 255) {
-            life = life >> 1;
-            lifeShift ++;
-        }
-    }
-
-    drawRect(con, 0, 256, 0, 512);
-    contextBindProgramFragment(con, NAMED_PgmFragParts);
-
-    if (touch) {
-        newPart = loadI32(con, 2, 0);
-        for (ct2=0; ct2<rate; ct2++) {
-            dx = scriptRand(con, 0x10000) - 0x8000;
-            dy = scriptRand(con, 0x10000) - 0x8000;
-
-            idx = newPart * 5 + 1;
-            storeI32(con, 2, idx, dx);
-            storeI32(con, 2, idx + 1, dy);
-            storeI32(con, 2, idx + 2, maxLife);
-            storeI32(con, 2, idx + 3, x << 16);
-            storeI32(con, 2, idx + 4, y << 16);
-
-            newPart++;
-            if (newPart >= count) {
-                newPart = 0;
-            }
-        }
-        storeI32(con, 2, 0, newPart);
-    }
-
-    drawCount = 0;
-    for (ct=0; ct < count; ct++) {
-        srcIdx = ct * 5 + 1;
-
-        dx = loadI32(con, 2, srcIdx);
-        dy = loadI32(con, 2, srcIdx + 1);
-        life = loadI32(con, 2, srcIdx + 2);
-        posx = loadI32(con, 2, srcIdx + 3);
-        posy = loadI32(con, 2, srcIdx + 4);
-
-        if (life) {
-            if (posy < (480 << 16)) {
-                dstIdx = drawCount * 9;
-                c = 0xffafcf | ((life >> lifeShift) << 24);
-
-                storeU32(con, 1, dstIdx, c);
-                storeI32(con, 1, dstIdx + 1, posx);
-                storeI32(con, 1, dstIdx + 2, posy);
-
-                storeU32(con, 1, dstIdx + 3, c);
-                storeI32(con, 1, dstIdx + 4, posx + 0x10000);
-                storeI32(con, 1, dstIdx + 5, posy + dy * 4);
-
-                storeU32(con, 1, dstIdx + 6, c);
-                storeI32(con, 1, dstIdx + 7, posx - 0x10000);
-                storeI32(con, 1, dstIdx + 8, posy + dy * 4);
-                drawCount ++;
-            } else {
-                if (dy > 0) {
-                    dy = (-dy) >> 1;
-                }
-            }
-
-            posx = posx + dx;
-            posy = posy + dy;
-            dy = dy + 0x400;
-            life --;
-
-            //storeI32(con, 2, srcIdx, dx);
-            storeI32(con, 2, srcIdx + 1, dy);
-            storeI32(con, 2, srcIdx + 2, life);
-            storeI32(con, 2, srcIdx + 3, posx);
-            storeI32(con, 2, srcIdx + 4, posy);
-        }
-    }
-
-    drawTriangleArray(con, NAMED_PartBuffer, drawCount);
-    return 1;
-}
diff --git a/libs/rs/java/Film/res/raw/filmstrip.c b/libs/rs/java/Film/res/raw/filmstrip.c
deleted file mode 100644
index bf75675..0000000
--- a/libs/rs/java/Film/res/raw/filmstrip.c
+++ /dev/null
@@ -1,94 +0,0 @@
-// Fountain test script
-
-#pragma version(1)
-#pragma stateVertex(PVBackground)
-#pragma stateFragment(PFBackground)
-#pragma stateStore(PSBackground)
-
-#define STATE_TRIANGLE_OFFSET_COUNT 0
-#define STATE_LAST_FOCUS 1
-
-
-// The script enviroment has 3 env allocations.
-// bank0: (r) The enviroment structure
-// bank1: (r) The position information
-// bank2: (rw) The temporary texture state
-
-int lastFocus;
-
-int main(int index)
-{
-    float mat1[16];
-
-    float trans = Pos->translate;
-    float rot = Pos->rotate;
-
-    matrixLoadScale(mat1, 2.f, 2.f, 2.f);
-    matrixTranslate(mat1, 0.f, 0.f, trans);
-    matrixRotate(mat1, 90.f, 0.f, 0.f, 1.f);
-    matrixRotate(mat1, rot, 1.f, 0.f, 0.f);
-    vpLoadModelMatrix(mat1);
-
-    // Draw the lighting effect in the strip and fill the Z buffer.
-    drawSimpleMesh(NAMED_mesh);
-
-    // Start of images.
-    bindProgramStore(NAMED_PSImages);
-    bindProgramFragment(NAMED_PFImages);
-    bindProgramVertex(NAMED_PVImages);
-
-    float focusPos = Pos->focus;
-    int focusID = 0;
-    int lastFocusID = loadI32(2, STATE_LAST_FOCUS);
-    int imgCount = 13;
-
-    if (trans > (-.3f)) {
-        focusID = -1.0f - focusPos;
-        if (focusID >= imgCount) {
-            focusID = -1;
-        }
-    } else {
-        focusID = -1;
-    }
-
-    /*
-    if (focusID != lastFocusID) {
-        if (lastFocusID >= 0) {
-            uploadToTexture(con, env->tex[lastFocusID], 1);
-        }
-        if (focusID >= 0) {
-            uploadToTexture(con, env->tex[focusID], 0);
-        }
-    }
-    */
-    lastFocus = focusID;
-
-    int triangleOffsetsCount = Pos->triangleOffsetCount;
-
-    int imgId = 0;
-    for (imgId=1; imgId <= imgCount; imgId++) {
-        float pos = focusPos + imgId + 0.4f;
-        int offset = (int)floorf(pos * 2.f);
-        pos = pos - 0.75f;
-
-        offset = offset + triangleOffsetsCount / 2;
-        if (!((offset < 0) || (offset >= triangleOffsetsCount))) {
-            int start = offset -2;
-            int end = offset + 2;
-
-            if (start < 0) {
-                start = 0;
-            }
-            if (end >= triangleOffsetsCount) {
-                end = triangleOffsetsCount-1;
-            }
-
-            bindTexture(NAMED_PFImages, 0, loadI32(0, imgId - 1));
-            matrixLoadTranslate(mat1, -pos - loadF(5, triangleOffsetsCount / 2), 0, 0);
-            vpLoadTextureMatrix(mat1);
-            drawSimpleMeshRange(NAMED_mesh, loadI32(4, start), (loadI32(4, end) - loadI32(4, start)));
-        }
-    }
-    return 0;
-}
-
diff --git a/libs/rs/java/Film/src/com/android/film/Film.java b/libs/rs/java/Film/src/com/android/film/Film.java
deleted file mode 100644
index 6e99816..0000000
--- a/libs/rs/java/Film/src/com/android/film/Film.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.film;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings.System;
-import android.util.Config;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.Window;
-import android.widget.Button;
-import android.widget.ListView;
-
-import java.lang.Runtime;
-
-public class Film extends Activity {
-    //EventListener mListener = new EventListener();
-
-    private static final String LOG_TAG = "libRS_jni";
-    private static final boolean DEBUG  = false;
-    private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
-
-    private FilmView mView;
-
-    // get the current looper (from your Activity UI thread for instance
-
-
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        // Create our Preview view and set it as the content of our
-        // Activity
-        mView = new FilmView(this);
-        setContentView(mView);
-    }
-
-    @Override
-    protected void onResume() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onResume();
-        mView.onResume();
-    }
-
-    @Override
-    protected void onPause() {
-        // Ideally a game should implement onResume() and onPause()
-        // to take appropriate action when the activity looses focus
-        super.onPause();
-        mView.onPause();
-
-        Runtime.getRuntime().exit(0);
-    }
-
-
-    static void log(String message) {
-        if (LOG_ENABLED) {
-            Log.v(LOG_TAG, message);
-        }
-    }
-
-
-}
-
diff --git a/libs/rs/java/Film/src/com/android/film/FilmRS.java b/libs/rs/java/Film/src/com/android/film/FilmRS.java
deleted file mode 100644
index 7d04502..0000000
--- a/libs/rs/java/Film/src/com/android/film/FilmRS.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.film;
-
-import java.io.Writer;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.util.Log;
-
-import android.renderscript.*;
-
-public class FilmRS {
-    class StripPosition {
-        public float translate;
-        public float rotate;
-        public float focus;
-        public int triangleOffsetCount;
-    }
-    StripPosition mPos = new StripPosition();
-
-
-    private final int STATE_LAST_FOCUS = 1;
-
-    public FilmRS() {
-    }
-
-    public void init(RenderScriptGL rs, Resources res, int width, int height) {
-        mRS = rs;
-        mRes = res;
-        initRS();
-    }
-
-    public void setFilmStripPosition(int x, int y)
-    {
-        if (x < 50) {
-            x = 50;
-        }
-        if (x > 270) {
-            x = 270;
-        }
-
-        float anim = ((float)x-50) / 270.f;
-        mPos.translate = 2f * anim + 0.5f;   // translation
-        mPos.rotate = (anim * 40);  // rotation
-        mPos.focus = ((float)y) / 16.f - 10.f;  // focusPos
-        mPos.triangleOffsetCount = mFSM.mTriangleOffsetsCount;
-        mAllocPos.data(mPos);
-    }
-
-
-    private Resources mRes;
-    private RenderScriptGL mRS;
-    private Script mScriptStrip;
-    private Script mScriptImage;
-    private Sampler mSampler;
-    private ProgramStore mPSBackground;
-    private ProgramStore mPSImages;
-    private ProgramFragment mPFBackground;
-    private ProgramFragment mPFImages;
-    private ProgramVertex mPVBackground;
-    private ProgramVertex mPVImages;
-    private ProgramVertex.MatrixAllocation mPVA;
-    private Type mStripPositionType;
-
-    private Allocation mImages[];
-    private Allocation mAllocIDs;
-    private Allocation mAllocPos;
-    private Allocation mAllocState;
-    private Allocation mAllocPV;
-    private Allocation mAllocOffsetsTex;
-    private Allocation mAllocOffsets;
-
-    private SimpleMesh mMesh;
-    private Light mLight;
-
-    private FilmStripMesh mFSM;
-
-    private int[] mBufferIDs;
-    private float[] mBufferPos = new float[3];
-    private int[] mBufferState;
-
-    private void initPFS() {
-        ProgramStore.Builder b = new ProgramStore.Builder(mRS, null, null);
-
-        b.setDepthFunc(ProgramStore.DepthFunc.LESS);
-        b.setDitherEnable(true);
-        b.setDepthMask(true);
-        mPSBackground = b.create();
-        mPSBackground.setName("PSBackground");
-
-        b.setDepthFunc(ProgramStore.DepthFunc.EQUAL);
-        b.setDitherEnable(false);
-        b.setDepthMask(false);
-        b.setBlendFunc(ProgramStore.BlendSrcFunc.ONE,
-                       ProgramStore.BlendDstFunc.ONE);
-        mPSImages = b.create();
-        mPSImages.setName("PSImages");
-    }
-
-    private void initPF() {
-        Sampler.Builder bs = new Sampler.Builder(mRS);
-        bs.setMin(Sampler.Value.LINEAR);//_MIP_LINEAR);
-        bs.setMag(Sampler.Value.LINEAR);
-        bs.setWrapS(Sampler.Value.CLAMP);
-        bs.setWrapT(Sampler.Value.WRAP);
-        mSampler = bs.create();
-
-        ProgramFragment.Builder b = new ProgramFragment.Builder(mRS);
-        mPFBackground = b.create();
-        mPFBackground.setName("PFBackground");
-
-        b = new ProgramFragment.Builder(mRS);
-        b.setTexture(ProgramFragment.Builder.EnvMode.REPLACE,
-                     ProgramFragment.Builder.Format.RGBA, 0);
-        mPFImages = b.create();
-        mPFImages.bindSampler(mSampler, 0);
-        mPFImages.setName("PFImages");
-    }
-
-    private void initPV() {
-        mLight = (new Light.Builder(mRS)).create();
-        mLight.setPosition(0, -0.5f, -1.0f);
-
-        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
-        //pvb.addLight(mLight);
-        mPVBackground = pvb.create();
-        mPVBackground.setName("PVBackground");
-
-        pvb = new ProgramVertex.Builder(mRS, null, null);
-        pvb.setTextureMatrixEnable(true);
-        mPVImages = pvb.create();
-        mPVImages.setName("PVImages");
-    }
-
-    private void loadImages() {
-        mBufferIDs = new int[13];
-        mImages = new Allocation[13];
-        mAllocIDs = Allocation.createSized(mRS,
-            Element.createUser(mRS, Element.DataType.FLOAT_32),
-            mBufferIDs.length);
-
-        Element ie = Element.createPixel(mRS, Element.DataType.UNSIGNED_5_6_5, Element.DataKind.PIXEL_RGB);
-        mImages[0] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p01, ie, true);
-        mImages[1] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p02, ie, true);
-        mImages[2] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p03, ie, true);
-        mImages[3] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p04, ie, true);
-        mImages[4] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p05, ie, true);
-        mImages[5] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p06, ie, true);
-        mImages[6] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p07, ie, true);
-        mImages[7] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p08, ie, true);
-        mImages[8] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p09, ie, true);
-        mImages[9] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p10, ie, true);
-        mImages[10] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p11, ie, true);
-        mImages[11] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p12, ie, true);
-        mImages[12] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p13, ie, true);
-
-        int black[] = new int[1024];
-        for(int ct=0; ct < mImages.length; ct++) {
-            Allocation.Adapter2D a = mImages[ct].createAdapter2D();
-
-            int size = 512;
-            int mip = 0;
-            while(size >= 2) {
-                a.subData(0, 0, 2, size, black);
-                a.subData(size-2, 0, 2, size, black);
-                a.subData(0, 0, size, 2, black);
-                a.subData(0, size-2, size, 2, black);
-                size >>= 1;
-                mip++;
-                a.setConstraint(Dimension.LOD, mip);
-            }
-
-            mImages[ct].uploadToTexture(1);
-            mBufferIDs[ct] = mImages[ct].getID();
-        }
-        mAllocIDs.data(mBufferIDs);
-    }
-
-    private void initState()
-    {
-        mBufferState = new int[10];
-        mAllocState = Allocation.createSized(mRS,
-            Element.createUser(mRS, Element.DataType.FLOAT_32),
-            mBufferState.length);
-        mBufferState[STATE_LAST_FOCUS] = -1;
-        mAllocState.data(mBufferState);
-    }
-
-    private void initRS() {
-        mFSM = new FilmStripMesh();
-        mMesh = mFSM.init(mRS);
-        mMesh.setName("mesh");
-
-        initPFS();
-        initPF();
-        initPV();
-
-        Log.e("rs", "Done loading named");
-
-        mStripPositionType = Type.createFromClass(mRS, StripPosition.class, 1);
-
-        ScriptC.Builder sb = new ScriptC.Builder(mRS);
-        sb.setScript(mRes, R.raw.filmstrip);
-        sb.setRoot(true);
-        sb.setType(mStripPositionType, "Pos", 1);
-        mScriptStrip = sb.create();
-        mScriptStrip.setClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-
-        mAllocPos = Allocation.createTyped(mRS, mStripPositionType);
-
-        loadImages();
-        initState();
-
-        mPVA = new ProgramVertex.MatrixAllocation(mRS);
-        mPVBackground.bindAllocation(mPVA);
-        mPVImages.bindAllocation(mPVA);
-        mPVA.setupProjectionNormalized(320, 480);
-
-
-        mScriptStrip.bindAllocation(mAllocIDs, 0);
-        mScriptStrip.bindAllocation(mAllocPos, 1);
-        mScriptStrip.bindAllocation(mAllocState, 2);
-        mScriptStrip.bindAllocation(mPVA.mAlloc, 3);
-
-
-        mAllocOffsets = Allocation.createSized(mRS,
-            Element.createUser(mRS, Element.DataType.SIGNED_32), mFSM.mTriangleOffsets.length);
-        mAllocOffsets.data(mFSM.mTriangleOffsets);
-        mScriptStrip.bindAllocation(mAllocOffsets, 4);
-
-        mAllocOffsetsTex = Allocation.createSized(mRS,
-            Element.createUser(mRS, Element.DataType.FLOAT_32), mFSM.mTriangleOffsetsTex.length);
-        mAllocOffsetsTex.data(mFSM.mTriangleOffsetsTex);
-        mScriptStrip.bindAllocation(mAllocOffsetsTex, 5);
-
-        setFilmStripPosition(0, 0);
-        mRS.contextBindRootScript(mScriptStrip);
-    }
-}
-
-
-
diff --git a/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java b/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java
deleted file mode 100644
index 448cce0..0000000
--- a/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package com.android.film;
-
-import java.io.Writer;
-import java.lang.Math;
-import android.util.Log;
-
-import android.renderscript.RenderScript;
-import android.renderscript.SimpleMesh;
-
-
-class FilmStripMesh {
-
-    class Vertex {
-        float nx;
-        float ny;
-        float nz;
-        float s;
-        float t;
-        float x;
-        float y;
-        float z;
-
-        Vertex() {
-            nx = 0;
-            ny = 0;
-            nz = 0;
-            s = 0;
-            t = 0;
-            x = 0;
-            y = 0;
-            z = 0;
-        }
-
-        void xyz(float _x, float _y, float _z) {
-            x = _x;
-            y = _y;
-            z = _z;
-        }
-
-        void nxyz(float _x, float _y, float _z) {
-            nx = _x;
-            ny = _y;
-            nz = _z;
-        }
-
-        void st(float _s, float _t) {
-            s = _s;
-            t = _t;
-        }
-
-        void computeNorm(Vertex v1, Vertex v2) {
-            float dx = v1.x - v2.x;
-            float dy = v1.y - v2.y;
-            float dz = v1.z - v2.z;
-            float len = (float)java.lang.Math.sqrt(dx*dx + dy*dy + dz*dz);
-            dx /= len;
-            dy /= len;
-            dz /= len;
-
-            nx = dx * dz;
-            ny = dy * dz;
-            nz = (float)java.lang.Math.sqrt(dx*dx + dy*dy);
-
-            len = (float)java.lang.Math.sqrt(nx*nx + ny*ny + nz*nz);
-            nx /= len;
-            ny /= len;
-            nz /= len;
-        }
-    }
-
-    int[] mTriangleOffsets;
-    float[] mTriangleOffsetsTex;
-    int mTriangleOffsetsCount;
-
-    SimpleMesh init(RenderScript rs)
-    {
-        float vtx[] = new float[] {
-            60.431003f, 124.482050f,
-            60.862074f, 120.872604f,
-            61.705303f, 117.336662f,
-            62.949505f, 113.921127f,
-            64.578177f, 110.671304f,
-            66.569716f, 107.630302f,
-            68.897703f, 104.838457f,
-            71.531259f, 102.332803f,
-            74.435452f, 100.146577f,
-            77.571757f, 98.308777f,
-            80.898574f, 96.843781f,
-            84.371773f, 95.771023f,
-            87.945283f, 95.104731f,
-            98.958994f, 95.267098f,
-            109.489523f, 98.497596f,
-            118.699582f, 104.539366f,
-            125.856872f, 112.912022f,
-            130.392311f, 122.949849f,
-            131.945283f, 133.854731f,
-            130.392311f, 144.759613f,
-            125.856872f, 154.797439f,
-            118.699582f, 163.170096f,
-            109.489523f, 169.211866f,
-            98.958994f, 172.442364f,
-            87.945283f, 172.604731f,
-            72.507313f, 172.672927f,
-            57.678920f, 168.377071f,
-            44.668135f, 160.067134f,
-            34.534908f, 148.420104f,
-            28.104767f, 134.384831f,
-            25.901557f, 119.104731f,
-            28.104767f, 103.824631f,
-            34.534908f, 89.789358f,
-            44.668135f, 78.142327f,
-            57.678920f, 69.832390f,
-            72.507313f, 65.536534f,
-            87.945283f, 65.604731f,
-            106.918117f, 65.688542f,
-            125.141795f, 60.409056f,
-            141.131686f, 50.196376f,
-            153.585137f, 35.882502f,
-            161.487600f, 18.633545f,
-            164.195283f, -0.145269f,
-            161.487600f, -18.924084f,
-            153.585137f, -36.173040f,
-            141.131686f, -50.486914f,
-            125.141795f, -60.699594f,
-            106.918117f, -65.979081f,
-            87.945283f, -65.895269f,
-            80f, -65.895269f,
-            60f, -65.895269f,
-            40f, -65.895269f,
-            20f, -65.895269f,
-            0f, -65.895269f,
-            -20f, -65.895269f,
-            -40f, -65.895269f,
-            -60f, -65.895269f,
-            -80f, -65.895269f,
-            -87.945283f, -65.895269f,
-            -106.918117f, -65.979081f,
-            -125.141795f, -60.699594f,
-            -141.131686f, -50.486914f,
-            -153.585137f, -36.173040f,
-            -161.487600f, -18.924084f,
-            -164.195283f, -0.145269f,
-            -161.487600f, 18.633545f,
-             -153.585137f, 35.882502f,
-             -141.131686f, 50.196376f,
-             -125.141795f, 60.409056f,
-             -106.918117f, 65.688542f,
-             -87.945283f, 65.604731f,
-             -72.507313f, 65.536534f,
-             -57.678920f, 69.832390f,
-             -44.668135f, 78.142327f,
-             -34.534908f, 89.789358f,
-             -28.104767f, 103.824631f,
-             -25.901557f, 119.104731f,
-             -28.104767f, 134.384831f,
-             -34.534908f, 148.420104f,
-             -44.668135f, 160.067134f,
-             -57.678920f, 168.377071f,
-             -72.507313f, 172.672927f,
-             -87.945283f, 172.604731f,
-             -98.958994f, 172.442364f,
-             -109.489523f, 169.211866f,
-             -118.699582f, 163.170096f,
-             -125.856872f, 154.797439f,
-             -130.392311f, 144.759613f,
-             -131.945283f, 133.854731f,
-             -130.392311f, 122.949849f,
-             -125.856872f, 112.912022f,
-             -118.699582f, 104.539366f,
-             -109.489523f, 98.497596f,
-             -98.958994f, 95.267098f,
-             -87.945283f, 95.104731f,
-             -84.371773f, 95.771023f,
-             -80.898574f, 96.843781f,
-             -77.571757f, 98.308777f,
-             -74.435452f, 100.146577f,
-             -71.531259f, 102.332803f,
-             -68.897703f, 104.838457f,
-             -66.569716f, 107.630302f,
-             -64.578177f, 110.671304f,
-             -62.949505f, 113.921127f,
-             -61.705303f, 117.336662f,
-             -60.862074f, 120.872604f,
-             -60.431003f, 124.482050f
-        };
-
-
-        mTriangleOffsets = new int[64];
-        mTriangleOffsetsTex = new float[64];
-
-        mTriangleOffsets[0] = 0;
-        mTriangleOffsetsCount = 1;
-
-        Vertex t = new Vertex();
-        t.nxyz(1, 0, 0);
-        int count = vtx.length / 2;
-
-        SimpleMesh.TriangleMeshBuilder tm = new SimpleMesh.TriangleMeshBuilder(
-            rs, 3,
-            SimpleMesh.TriangleMeshBuilder.NORMAL | SimpleMesh.TriangleMeshBuilder.TEXTURE_0);
-
-        float runningS = 0;
-        for (int ct=0; ct < (count-1); ct++) {
-            t.x = -vtx[ct*2] / 100.f;
-            t.z = vtx[ct*2+1] / 100.f;
-            t.s = runningS;
-            t.nx =  (vtx[ct*2+3] - vtx[ct*2 +1]);
-            t.ny =  (vtx[ct*2+2] - vtx[ct*2   ]);
-            float len = (float)java.lang.Math.sqrt(t.nx * t.nx + t.ny * t.ny);
-            runningS += len / 100;
-            t.nx /= len;
-            t.ny /= len;
-            t.y = -0.5f;
-            t.t = 0;
-            tm.setNormal(t.nx, t.ny, t.nz);
-            tm.setTexture(t.s, t.t);
-            tm.addVertex(t.x, t.y, t.z);
-            //android.util.Log.e("rs", "vtx x="+t.x+" y="+t.y+" z="+t.z+" s="+t.s+" t="+t.t);
-            t.y = .5f;
-            t.t = 1;
-            tm.setTexture(t.s, t.t);
-            tm.addVertex(t.x, t.y, t.z);
-            //android.util.Log.e("rs", "vtx x="+t.x+" y="+t.y+" z="+t.z+" s="+t.s+" t="+t.t);
-
-            if((runningS*2) > mTriangleOffsetsCount) {
-                mTriangleOffsets[mTriangleOffsetsCount] = ct*2 * 3;
-                mTriangleOffsetsTex[mTriangleOffsetsCount] = t.s;
-                mTriangleOffsetsCount ++;
-            }
-        }
-
-        count = (count * 2 - 2);
-        for (int ct=0; ct < (count-2); ct+= 2) {
-            tm.addTriangle(ct, ct+1, ct+2);
-            tm.addTriangle(ct+1, ct+3, ct+2);
-        }
-        return tm.create();
-    }
-
-
-}
-
diff --git a/libs/rs/java/Film/src/com/android/film/FilmView.java b/libs/rs/java/Film/src/com/android/film/FilmView.java
deleted file mode 100644
index 5bc2811..0000000
--- a/libs/rs/java/Film/src/com/android/film/FilmView.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.film;
-
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.concurrent.Semaphore;
-
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScript;
-import android.renderscript.RenderScriptGL;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-public class FilmView extends RSSurfaceView {
-
-    public FilmView(Context context) {
-        super(context);
-        //setFocusable(true);
-    }
-
-    private RenderScriptGL mRS;
-    private FilmRS mRender;
-
-
-    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        super.surfaceChanged(holder, format, w, h);
-        if (mRS == null) {
-            mRS = createRenderScript(true);
-            mRS.contextSetSurface(w, h, holder.getSurface());
-            mRender = new FilmRS();
-            mRender.init(mRS, getResources(), w, h);
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        if(mRS != null) {
-            mRS = null;
-            destroyRenderScript();
-        }
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event)
-    {
-        // break point at here
-        // this method doesn't work when 'extends View' include 'extends ScrollView'.
-        return super.onKeyDown(keyCode, event);
-    }
-
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev)
-    {
-        boolean ret = true;
-        int act = ev.getAction();
-        if (act == ev.ACTION_UP) {
-            ret = false;
-        }
-        mRender.setFilmStripPosition((int)ev.getX(), (int)ev.getY() / 5);
-        return ret;
-    }
-}
-
-
diff --git a/libs/rs/java/Fountain/Android.mk b/libs/rs/java/Fountain/Android.mk
index f7e53a8..71944b2 100644
--- a/libs/rs/java/Fountain/Android.mk
+++ b/libs/rs/java/Fountain/Android.mk
@@ -14,14 +14,18 @@
 # limitations under the License.
 #
 
+ifneq ($(TARGET_SIMULATOR),true)
+
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
 #LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
 
 LOCAL_PACKAGE_NAME := Fountain
 
 include $(BUILD_PACKAGE)
+
+endif
diff --git a/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png b/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png
deleted file mode 100755
index e91bfb4..0000000
--- a/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png
+++ /dev/null
Binary files differ
diff --git a/libs/rs/java/Fountain/res/raw/fountain.c b/libs/rs/java/Fountain/res/raw/fountain.c
deleted file mode 100644
index 73b819b..0000000
--- a/libs/rs/java/Fountain/res/raw/fountain.c
+++ /dev/null
@@ -1,52 +0,0 @@
-// Fountain test script
-#pragma version(1)
-
-int newPart = 0;
-
-int main(int launchID) {
-    int ct;
-    int count = Control->count;
-    int rate = Control->rate;
-    float height = getHeight();
-    struct point_s * p = (struct point_s *)point;
-
-    if (rate) {
-        float rMax = ((float)rate) * 0.005f;
-        int x = Control->x;
-        int y = Control->y;
-        int color = ((int)(Control->r * 255.f)) |
-                    ((int)(Control->g * 255.f)) << 8 |
-                    ((int)(Control->b * 255.f)) << 16 |
-                    (0xf0 << 24);
-        struct point_s * np = &p[newPart];
-
-        while (rate--) {
-            vec2Rand((float *)&np->delta.x, rMax);
-            np->position.x = x;
-            np->position.y = y;
-            np->color = color;
-            newPart++;
-            np++;
-            if (newPart >= count) {
-                newPart = 0;
-                np = &p[newPart];
-            }
-        }
-    }
-
-    for (ct=0; ct < count; ct++) {
-        float dy = p->delta.y + 0.15f;
-        float posy = p->position.y + dy;
-        if ((posy > height) && (dy > 0)) {
-            dy *= -0.3f;
-        }
-        p->delta.y = dy;
-        p->position.x += p->delta.x;
-        p->position.y = posy;
-        p++;
-    }
-
-    uploadToBufferObject(NAMED_PartBuffer);
-    drawSimpleMesh(NAMED_PartMesh);
-    return 1;
-}
diff --git a/libs/rs/java/Fountain/res/raw/fountain2.rs b/libs/rs/java/Fountain/res/raw/fountain2.rs
deleted file mode 100644
index 3301140..0000000
--- a/libs/rs/java/Fountain/res/raw/fountain2.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-// Fountain test script
-#pragma version(1)
-
-#include "rs_types.rsh"
-#include "rs_math.rsh"
-#include "rs_graphics.rsh"
-
-static int newPart = 0;
-
-typedef struct Control_s {
-    int x, y;
-    int rate;
-    int count;
-    float r, g, b;
-    rs_allocation partBuffer;
-    rs_mesh partMesh;
-} Control_t;
-Control_t *Control;
-
-typedef struct Point_s{
-    float2 delta;
-    float2 position;
-    unsigned int color;
-} Point_t;
-Point_t *point;
-
-int main(int launchID) {
-    int ct;
-    int count = Control->count;
-    int rate = Control->rate;
-    float height = getHeight();
-    Point_t * p = point;
-
-    if (rate) {
-        float rMax = ((float)rate) * 0.005f;
-        int x = Control->x;
-        int y = Control->y;
-        int color = ((int)(Control->r * 255.f)) |
-                    ((int)(Control->g * 255.f)) << 8 |
-                    ((int)(Control->b * 255.f)) << 16 |
-                    (0xf0 << 24);
-        Point_t * np = &p[newPart];
-
-        while (rate--) {
-            np->delta = vec2Rand(rMax);
-            np->position.x = x;
-            np->position.y = y;
-            np->color = color;
-            newPart++;
-            np++;
-            if (newPart >= count) {
-                newPart = 0;
-                np = &p[newPart];
-            }
-        }
-    }
-
-    for (ct=0; ct < count; ct++) {
-        float dy = p->delta.y + 0.15f;
-        float posy = p->position.y + dy;
-        if ((posy > height) && (dy > 0)) {
-            dy *= -0.3f;
-        }
-        p->delta.y = dy;
-        p->position.x += p->delta.x;
-        p->position.y = posy;
-        p++;
-    }
-
-    uploadToBufferObject(Control->partBuffer);
-    drawSimpleMesh(Control->partMesh);
-    return 1;
-}
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
index 9356579..eb46e950 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
@@ -22,94 +22,50 @@
 
 
 public class FountainRS {
-    public static final int PART_COUNT = 20000;
-
-    static class SomeData {
-        public int x;
-        public int y;
-        public int rate;
-        public int count;
-        public float r;
-        public float g;
-        public float b;
-    }
+    public static final int PART_COUNT = 50000;
 
     public FountainRS() {
     }
 
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    private ScriptC_fountain mScript;
     public void init(RenderScriptGL rs, Resources res, int width, int height) {
         mRS = rs;
         mRes = res;
-        initRS();
+
+        ProgramFragment.Builder pfb = new ProgramFragment.Builder(rs);
+        pfb.setVaryingColor(true);
+        rs.contextBindProgramFragment(pfb.create());
+
+        ScriptField_Point points = new ScriptField_Point(mRS, PART_COUNT);
+
+        Mesh.AllocationBuilder smb = new Mesh.AllocationBuilder(mRS);
+        smb.addVertexAllocation(points.getAllocation());
+        smb.addIndexType(Primitive.POINT);
+        Mesh sm = smb.create();
+
+        mScript = new ScriptC_fountain(mRS, mRes, R.raw.fountain);
+        mScript.set_partMesh(sm);
+        mScript.bind_point(points);
+        mRS.contextBindRootScript(mScript);
     }
 
-    public void newTouchPosition(int x, int y, int rate) {
-        if (mSD.rate == 0) {
-            mSD.r = ((x & 0x1) != 0) ? 0.f : 1.f;
-            mSD.g = ((x & 0x2) != 0) ? 0.f : 1.f;
-            mSD.b = ((x & 0x4) != 0) ? 0.f : 1.f;
-            if ((mSD.r + mSD.g + mSD.b) < 0.9f) {
-                mSD.r = 0.8f;
-                mSD.g = 0.5f;
-                mSD.b = 1.f;
-            }
+    boolean holdingColor[] = new boolean[10];
+    public void newTouchPosition(float x, float y, float pressure, int id) {
+        if (id > holdingColor.length) {
+            return;
         }
-        mSD.rate = rate;
-        mSD.x = x;
-        mSD.y = y;
-        mIntAlloc.data(mSD);
+        int rate = (int)(pressure * pressure * 500.f);
+        if(rate > 500) {
+            rate = 500;
+        }
+        if (rate > 0) {
+            mScript.invoke_addParticles(rate, x, y, id, !holdingColor[id]);
+            holdingColor[id] = true;
+        } else {
+            holdingColor[id] = false;
+        }
+
     }
-
-
-    /////////////////////////////////////////
-
-    private Resources mRes;
-
-    private RenderScriptGL mRS;
-    private Allocation mIntAlloc;
-    private SimpleMesh mSM;
-    private SomeData mSD;
-    private Type mSDType;
-
-    private void initRS() {
-        mSD = new SomeData();
-        mSDType = Type.createFromClass(mRS, SomeData.class, 1, "SomeData");
-        mIntAlloc = Allocation.createTyped(mRS, mSDType);
-        mSD.count = PART_COUNT;
-        mIntAlloc.data(mSD);
-
-        Element.Builder eb = new Element.Builder(mRS);
-        eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 2), "delta");
-        eb.add(Element.createAttrib(mRS, Element.DataType.FLOAT_32, Element.DataKind.POSITION, 2), "position");
-        eb.add(Element.createAttrib(mRS, Element.DataType.UNSIGNED_8, Element.DataKind.COLOR, 4), "color");
-        Element primElement = eb.create();
-
-
-        SimpleMesh.Builder smb = new SimpleMesh.Builder(mRS);
-        int vtxSlot = smb.addVertexType(primElement, PART_COUNT);
-        smb.setPrimitive(Primitive.POINT);
-        mSM = smb.create();
-        mSM.setName("PartMesh");
-
-        Allocation partAlloc = mSM.createVertexAllocation(vtxSlot);
-        partAlloc.setName("PartBuffer");
-        mSM.bindVertexAllocation(partAlloc, 0);
-
-        // All setup of named objects should be done by this point
-        // because we are about to compile the script.
-        ScriptC.Builder sb = new ScriptC.Builder(mRS);
-        sb.setScript(mRes, R.raw.fountain);
-        sb.setRoot(true);
-        sb.setType(mSDType, "Control", 0);
-        sb.setType(mSM.getVertexType(0), "point", 1);
-        Script script = sb.create();
-        script.setClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-
-        script.bindAllocation(mIntAlloc, 0);
-        script.bindAllocation(partAlloc, 1);
-        mRS.contextBindRootScript(script);
-    }
-
 }
-
-
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
index dfd6a49..987bebe 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
@@ -52,7 +52,8 @@
     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
         super.surfaceChanged(holder, format, w, h);
         if (mRS == null) {
-            mRS = createRenderScript(false);
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            mRS = createRenderScript(sc);
             mRS.contextSetSurface(w, h, holder.getSurface());
             mRender = new FountainRS();
             mRender.init(mRS, getResources(), w, h);
@@ -71,17 +72,33 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev)
     {
-        int act = ev.getAction();
+        int act = ev.getActionMasked();
         if (act == ev.ACTION_UP) {
-            mRender.newTouchPosition(0, 0, 0);
+            mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0));
             return false;
+        } else if (act == MotionEvent.ACTION_POINTER_UP) {
+            // only one pointer going up, we can get the index like this
+            int pointerIndex = ev.getActionIndex();
+            int pointerId = ev.getPointerId(pointerIndex);
+            mRender.newTouchPosition(0, 0, 0, pointerId);
         }
-        float rate = (ev.getPressure() * 50.f);
-        rate *= rate;
-        if(rate > 2000.f) {
-            rate = 2000.f;
+        int count = ev.getHistorySize();
+        int pcount = ev.getPointerCount();
+
+        for (int p=0; p < pcount; p++) {
+            int id = ev.getPointerId(p);
+            mRender.newTouchPosition(ev.getX(p),
+                                     ev.getY(p),
+                                     ev.getPressure(p),
+                                     id);
+
+            for (int i=0; i < count; i++) {
+                mRender.newTouchPosition(ev.getHistoricalX(p, i),
+                                         ev.getHistoricalY(p, i),
+                                         ev.getHistoricalPressure(p, i),
+                                         id);
+            }
         }
-        mRender.newTouchPosition((int)ev.getX(), (int)ev.getY(), (int)rate);
         return true;
     }
 }
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/fountain.rs b/libs/rs/java/Fountain/src/com/android/fountain/fountain.rs
new file mode 100644
index 0000000..5445744
--- /dev/null
+++ b/libs/rs/java/Fountain/src/com/android/fountain/fountain.rs
@@ -0,0 +1,71 @@
+// Fountain test script
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.fountain)
+
+#pragma stateFragment(parent)
+
+#include "rs_graphics.rsh"
+
+static int newPart = 0;
+rs_mesh partMesh;
+
+typedef struct __attribute__((packed, aligned(4))) Point {
+    float2 delta;
+    float2 position;
+    uchar4 color;
+} Point_t;
+Point_t *point;
+
+#pragma rs export_func(addParticles)
+
+int root() {
+    float dt = min(rsGetDt(), 0.1f);
+    rsgClearColor(0.f, 0.f, 0.f, 1.f);
+    const float height = rsgGetHeight();
+    const int size = rsAllocationGetDimX(rsGetAllocation(point));
+    float dy2 = dt * (10.f);
+    Point_t * p = point;
+    for (int ct=0; ct < size; ct++) {
+        p->delta.y += dy2;
+        p->position += p->delta;
+        if ((p->position.y > height) && (p->delta.y > 0)) {
+            p->delta.y *= -0.3f;
+        }
+        p++;
+    }
+
+    rsgDrawMesh(partMesh);
+    return 1;
+}
+
+static float4 partColor[10];
+void addParticles(int rate, float x, float y, int index, bool newColor)
+{
+    if (newColor) {
+        partColor[index].x = rsRand(0.5f, 1.0f);
+        partColor[index].y = rsRand(1.0f);
+        partColor[index].z = rsRand(1.0f);
+    }
+    float rMax = ((float)rate) * 0.02f;
+    int size = rsAllocationGetDimX(rsGetAllocation(point));
+    uchar4 c = rsPackColorTo8888(partColor[index]);
+
+    Point_t * np = &point[newPart];
+    float2 p = {x, y};
+    while (rate--) {
+        float angle = rsRand(3.14f * 2.f);
+        float len = rsRand(rMax);
+        np->delta.x = len * sin(angle);
+        np->delta.y = len * cos(angle);
+        np->position = p;
+        np->color = c;
+        newPart++;
+        np++;
+        if (newPart >= size) {
+            newPart = 0;
+            np = &point[newPart];
+        }
+    }
+}
+
diff --git a/libs/rs/java/ImageProcessing/Android.mk b/libs/rs/java/ImageProcessing/Android.mk
index 833427b..7fa30d0 100644
--- a/libs/rs/java/ImageProcessing/Android.mk
+++ b/libs/rs/java/ImageProcessing/Android.mk
@@ -14,14 +14,19 @@
 # limitations under the License.
 #
 
+ifneq ($(TARGET_SIMULATOR),true)
+
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+                   $(call all-renderscript-files-under, src)
 #LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
 
 LOCAL_PACKAGE_NAME := ImageProcessing
 
 include $(BUILD_PACKAGE)
+
+endif
diff --git a/libs/rs/java/ImageProcessing/AndroidManifest.xml b/libs/rs/java/ImageProcessing/AndroidManifest.xml
index b48d208..d6a2db4 100644
--- a/libs/rs/java/ImageProcessing/AndroidManifest.xml
+++ b/libs/rs/java/ImageProcessing/AndroidManifest.xml
@@ -6,7 +6,8 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
     <application android:label="Image Processing">
-        <activity android:name="ImageProcessingActivity">
+        <activity android:name="ImageProcessingActivity"
+                  android:screenOrientation="portrait">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rs/java/ImageProcessing/res/drawable/data.jpg b/libs/rs/java/ImageProcessing/res/drawable/data.jpg
new file mode 100644
index 0000000..81a87b1
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/res/drawable/data.jpg
Binary files differ
diff --git a/libs/rs/java/ImageProcessing/res/layout/main.xml b/libs/rs/java/ImageProcessing/res/layout/main.xml
index 6770c18..c6ec729 100644
--- a/libs/rs/java/ImageProcessing/res/layout/main.xml
+++ b/libs/rs/java/ImageProcessing/res/layout/main.xml
@@ -25,9 +25,147 @@
         android:id="@+id/display"
         android:layout_width="320dip"
         android:layout_height="266dip" />
-    
+
+    <Button
+        android:layout_marginBottom="170dip"
+        android:layout_width="wrap_content"
+        android:layout_height="40dip"
+        android:text="@string/benchmark"
+        android:onClick="benchmark"
+        android:layout_gravity="bottom"/>
+
+    <TextView
+        android:id="@+id/benchmarkText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"
+        android:layout_marginLeft="100dip"
+        android:layout_marginBottom="175dip"
+        android:layout_gravity="bottom"
+        android:text="@string/saturation"/>
+
+     <SeekBar
+        android:id="@+id/inSaturation"
+        android:layout_marginBottom="140dip"
+        android:layout_marginLeft="10dip"
+        android:layout_marginRight="10dip"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom" />
+
+    <TextView
+        android:id="@+id/inSaturationText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"
+        android:layout_marginLeft="50dip"
+        android:layout_marginBottom="142dip"
+        android:textColor="#000"
+        android:layout_gravity="bottom"
+        android:text="@string/saturation"/>
+
     <SeekBar
-        android:id="@+id/threshold"
+        android:id="@+id/inGamma"
+        android:layout_marginBottom="110dip"
+        android:layout_marginLeft="10dip"
+        android:layout_marginRight="10dip"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom" />
+
+    <TextView
+        android:id="@+id/inGammaText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"
+        android:layout_marginLeft="50dip"
+        android:layout_marginBottom="112dip"
+        android:textColor="#000"
+        android:layout_gravity="bottom"
+        android:text="@string/gamma"/>
+
+    <SeekBar
+        android:id="@+id/outWhite"
+        android:layout_marginBottom="80dip"
+        android:layout_marginLeft="170dip"
+        android:layout_marginRight="10dip"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom" />
+
+    <TextView
+        android:id="@+id/outWhiteText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"
+        android:layout_marginLeft="220dip"
+        android:layout_marginBottom="82dip"
+        android:textColor="#000"
+        android:layout_gravity="bottom"
+        android:text="@string/out_white"/>
+
+    <SeekBar
+        android:id="@+id/inWhite"
+        android:layout_marginBottom="80dip"
+        android:layout_marginLeft="10dip"
+        android:layout_marginRight="170dip"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom" />
+
+    <TextView
+        android:id="@+id/inWhiteText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"
+        android:layout_marginLeft="50dip"
+        android:layout_marginBottom="82dip"
+        android:textColor="#000"
+        android:layout_gravity="bottom"
+        android:text="@string/in_white"/>
+
+    <SeekBar
+        android:id="@+id/outBlack"
+        android:layout_marginBottom="50dip"
+        android:layout_marginLeft="170dip"
+        android:layout_marginRight="10dip"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom" />
+
+    <TextView
+        android:id="@+id/outBlackText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"
+        android:layout_marginLeft="220dip"
+        android:layout_marginBottom="52dip"
+        android:textColor="#000"
+        android:layout_gravity="bottom"
+        android:text="@string/out_black"/>
+
+    <SeekBar
+        android:id="@+id/inBlack"
+        android:layout_marginBottom="50dip"
+        android:layout_marginLeft="10dip"
+        android:layout_marginRight="170dip"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom" />
+
+    <TextView
+        android:id="@+id/inBlackText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"
+        android:layout_marginLeft="50dip"
+        android:layout_marginBottom="52dip"
+        android:textColor="#000"
+        android:layout_gravity="bottom"
+        android:text="@string/in_black"/>
+
+    <SeekBar
+        android:id="@+id/radius"
         android:layout_marginBottom="10dip"
         android:layout_marginLeft="10dip"
         android:layout_marginRight="10dip"
@@ -35,4 +173,15 @@
         android:layout_height="wrap_content"
         android:layout_gravity="bottom" />
 
+     <TextView
+        android:id="@+id/blurText"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"
+        android:layout_marginLeft="50dip"
+        android:layout_marginBottom="12dip"
+        android:textColor="#000"
+        android:layout_gravity="bottom"
+        android:text="@string/blur_description"/>
+
 </merge>
\ No newline at end of file
diff --git a/libs/rs/java/ImageProcessing/res/raw/threshold.rs b/libs/rs/java/ImageProcessing/res/raw/threshold.rs
deleted file mode 100644
index 888f0cd..0000000
--- a/libs/rs/java/ImageProcessing/res/raw/threshold.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-// block of defines matching what RS will insert at runtime.
-struct Params_s{
-    int inHeight;
-    int inWidth;
-    int outHeight;
-    int outWidth;
-    float threshold;
-};
-struct Params_s * Params;
-struct InPixel_s{
-    char a;
-    char b;
-    char g;
-    char r;
-};
-struct InPixel_s * InPixel;
-struct OutPixel_s{
-    char a;
-    char b;
-    char g;
-    char r;
-};
-struct OutPixel_s * OutPixel;
-*/
-
-struct color_s {
-    char b;
-    char g;
-    char r;
-    char a;
-};
-
-void main() {
-    int t = uptimeMillis();
-
-    struct color_s *in = (struct color_s *) InPixel;
-    struct color_s *out = (struct color_s *) OutPixel;
-
-    int count = Params->inWidth * Params->inHeight;
-    int i;
-    float threshold = (Params->threshold * 255.f);
-
-    for (i = 0; i < count; i++) {
-        float luminance = 0.2125f * in->r +
-                          0.7154f * in->g +
-                          0.0721f * in->b;
-        if (luminance > threshold) {
-            *out = *in;
-        } else {
-            *((int *)out) = *((int *)in) & 0xff000000;
-        }
-
-        in++;
-        out++;
-    }
-
-    t= uptimeMillis() - t;
-    debugI32("Filter time", t);
-
-    sendToClient(&count, 1, 4, 0);
-}
diff --git a/libs/rs/java/ImageProcessing/res/values/strings.xml b/libs/rs/java/ImageProcessing/res/values/strings.xml
new file mode 100644
index 0000000..cc5cc4d
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- General -->
+    <skip />
+    <!--slider label -->
+    <string name="blur_description">Blur Radius</string>
+    <string name="in_white">In White</string>
+    <string name="out_white">Out White</string>
+    <string name="in_black">In Black</string>
+    <string name="out_black">Out Black</string>
+    <string name="gamma">Gamma</string>
+    <string name="saturation">Saturation</string>
+    <string name="benchmark">Benchmark</string>
+
+</resources>
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 9ce53d8..16f404f 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -31,53 +31,56 @@
 import android.view.SurfaceHolder;
 import android.widget.ImageView;
 import android.widget.SeekBar;
+import android.widget.TextView;
+import android.view.View;
 import java.lang.Math;
 
-public class ImageProcessingActivity extends Activity implements SurfaceHolder.Callback {
-    private Bitmap mBitmap;
-    private Params mParams;
-    private Script.Invokable mInvokable;
-    private int[] mInData;
-    private int[] mOutData;
+public class ImageProcessingActivity extends Activity
+                                       implements SurfaceHolder.Callback,
+                                       SeekBar.OnSeekBarChangeListener {
+    private Bitmap mBitmapIn;
+    private Bitmap mBitmapOut;
+    private Bitmap mBitmapScratch;
+    private ScriptC_threshold mScript;
+    private ScriptC_vertical_blur mScriptVBlur;
+    private ScriptC_horizontal_blur mScriptHBlur;
+    private int mRadius = 0;
+    private SeekBar mRadiusSeekBar;
+
+    private float mInBlack = 0.0f;
+    private SeekBar mInBlackSeekBar;
+    private float mOutBlack = 0.0f;
+    private SeekBar mOutBlackSeekBar;
+    private float mInWhite = 255.0f;
+    private SeekBar mInWhiteSeekBar;
+    private float mOutWhite = 255.0f;
+    private SeekBar mOutWhiteSeekBar;
+    private float mGamma = 1.0f;
+    private SeekBar mGammaSeekBar;
+
+    private float mSaturation = 1.0f;
+    private SeekBar mSaturationSeekBar;
+
+    private TextView mBenchmarkResult;
 
     @SuppressWarnings({"FieldCanBeLocal"})
     private RenderScript mRS;
     @SuppressWarnings({"FieldCanBeLocal"})
-    private Type mParamsType;
-    @SuppressWarnings({"FieldCanBeLocal"})
-    private Allocation mParamsAllocation;
-    @SuppressWarnings({"FieldCanBeLocal"})
     private Type mPixelType;
     @SuppressWarnings({"FieldCanBeLocal"})
     private Allocation mInPixelsAllocation;
     @SuppressWarnings({"FieldCanBeLocal"})
     private Allocation mOutPixelsAllocation;
+    @SuppressWarnings({"FieldCanBeLocal"})
+    private Allocation mScratchPixelsAllocation1;
+    private Allocation mScratchPixelsAllocation2;
 
     private SurfaceView mSurfaceView;
     private ImageView mDisplayView;
 
-    static class Params {
-        public int inWidth;
-        public int outWidth;
-        public int inHeight;
-        public int outHeight;
-
-        public float threshold;
-    }
-
-    static class Pixel {
-        public byte a;
-        public byte r;
-        public byte g;
-        public byte b;
-    }
-
     class FilterCallback extends RenderScript.RSMessage {
         private Runnable mAction = new Runnable() {
             public void run() {
-                mOutPixelsAllocation.readData(mOutData);
-                mBitmap.setPixels(mOutData, 0, mParams.outWidth, 0, 0,
-                        mParams.outWidth, mParams.outHeight);
                 mDisplayView.invalidate();
             }
         };
@@ -89,29 +92,218 @@
         }
     }
 
-    private void javaFilter() {
+    int in[];
+    int interm[];
+    int out[];
+    int MAX_RADIUS = 25;
+    // Store our coefficients here
+    float gaussian[];
+
+    private long javaFilter() {
+        final int width = mBitmapIn.getWidth();
+        final int height = mBitmapIn.getHeight();
+        final int count = width * height;
+
+        if (in == null) {
+            in = new int[count];
+            interm = new int[count];
+            out = new int[count];
+            gaussian = new float[MAX_RADIUS * 2 + 1];
+            mBitmapIn.getPixels(in, 0, width, 0, 0, width, height);
+        }
+
         long t = java.lang.System.currentTimeMillis();
-        int count = mParams.inWidth * mParams.inHeight;
-        float threshold = mParams.threshold * 255.f;
 
-        for (int i = 0; i < count; i++) {
-            final float r = (float)((mInData[i] >> 0) & 0xff);
-            final float g = (float)((mInData[i] >> 8) & 0xff);
-            final float b = (float)((mInData[i] >> 16) & 0xff);
+        int w, h, r;
 
-            final float luminance = 0.2125f * r +
-                              0.7154f * g +
-                              0.0721f * b;
-            if (luminance > threshold) {
-                mOutData[i] = mInData[i];
-            } else {
-                mOutData[i] = mInData[i] & 0xff000000;
+        float fRadius = (float)mRadius;
+        int radius = (int)mRadius;
+
+        // Compute gaussian weights for the blur
+        // e is the euler's number
+        float e = 2.718281828459045f;
+        float pi = 3.1415926535897932f;
+        // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
+        // x is of the form [-radius .. 0 .. radius]
+        // and sigma varies with radius.
+        // Based on some experimental radius values and sigma's
+        // we approximately fit sigma = f(radius) as
+        // sigma = radius * 0.4  + 0.6
+        // The larger the radius gets, the more our gaussian blur
+        // will resemble a box blur since with large sigma
+        // the gaussian curve begins to lose its shape
+        float sigma = 0.4f * fRadius + 0.6f;
+        // Now compute the coefficints
+        // We will store some redundant values to save some math during
+        // the blur calculations
+        // precompute some values
+        float coeff1 = 1.0f / (float)(Math.sqrt( 2.0f * pi ) * sigma);
+        float coeff2 = - 1.0f / (2.0f * sigma * sigma);
+        float normalizeFactor = 0.0f;
+        float floatR = 0.0f;
+        for(r = -radius; r <= radius; r ++) {
+            floatR = (float)r;
+            gaussian[r + radius] = coeff1 * (float)Math.pow(e, floatR * floatR * coeff2);
+            normalizeFactor += gaussian[r + radius];
+        }
+
+        //Now we need to normalize the weights because all our coefficients need to add up to one
+        normalizeFactor = 1.0f / normalizeFactor;
+        for(r = -radius; r <= radius; r ++) {
+            floatR = (float)r;
+            gaussian[r + radius] *= normalizeFactor;
+        }
+
+        float blurredPixelR = 0.0f;
+        float blurredPixelG = 0.0f;
+        float blurredPixelB = 0.0f;
+        float blurredPixelA = 0.0f;
+
+        for(h = 0; h < height; h ++) {
+            for(w = 0; w < width; w ++) {
+
+                blurredPixelR = 0.0f;
+                blurredPixelG = 0.0f;
+                blurredPixelB = 0.0f;
+                blurredPixelA = 0.0f;
+
+                for(r = -radius; r <= radius; r ++) {
+                    // Stepping left and right away from the pixel
+                    int validW = w + r;
+                    // Clamp to zero and width max() isn't exposed for ints yet
+                    if(validW < 0) {
+                        validW = 0;
+                    }
+                    if(validW > width - 1) {
+                        validW = width - 1;
+                    }
+
+                    int input = in[h*width + validW];
+
+                    int R = ((input >> 24) & 0xff);
+                    int G = ((input >> 16) & 0xff);
+                    int B = ((input >> 8) & 0xff);
+                    int A = (input & 0xff);
+
+                    float weight = gaussian[r + radius];
+
+                    blurredPixelR += (float)(R)*weight;
+                    blurredPixelG += (float)(G)*weight;
+                    blurredPixelB += (float)(B)*weight;
+                    blurredPixelA += (float)(A)*weight;
+                }
+
+                int R = (int)blurredPixelR;
+                int G = (int)blurredPixelG;
+                int B = (int)blurredPixelB;
+                int A = (int)blurredPixelA;
+
+                interm[h*width + w] = (R << 24) | (G << 16) | (B << 8) | (A);
+            }
+        }
+
+        for(h = 0; h < height; h ++) {
+            for(w = 0; w < width; w ++) {
+
+                blurredPixelR = 0.0f;
+                blurredPixelG = 0.0f;
+                blurredPixelB = 0.0f;
+                blurredPixelA = 0.0f;
+                for(r = -radius; r <= radius; r ++) {
+                    int validH = h + r;
+                    // Clamp to zero and width
+                    if(validH < 0) {
+                        validH = 0;
+                    }
+                    if(validH > height - 1) {
+                        validH = height - 1;
+                    }
+
+                    int input = interm[validH*width + w];
+
+                    int R = ((input >> 24) & 0xff);
+                    int G = ((input >> 16) & 0xff);
+                    int B = ((input >> 8) & 0xff);
+                    int A = (input & 0xff);
+
+                    float weight = gaussian[r + radius];
+
+                    blurredPixelR += (float)(R)*weight;
+                    blurredPixelG += (float)(G)*weight;
+                    blurredPixelB += (float)(B)*weight;
+                    blurredPixelA += (float)(A)*weight;
+                }
+
+                int R = (int)blurredPixelR;
+                int G = (int)blurredPixelG;
+                int B = (int)blurredPixelB;
+                int A = (int)blurredPixelA;
+
+                out[h*width + w] = (R << 24) | (G << 16) | (B << 8) | (A);
             }
         }
 
         t = java.lang.System.currentTimeMillis() - t;
+        android.util.Log.v("Img", "Java frame time ms " + t);
+        mBitmapOut.setPixels(out, 0, width, 0, 0, width, height);
+        return t;
+    }
 
-        android.util.Log.v("Img", "frame time ms " + t);
+    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+        if (fromUser) {
+
+            if(seekBar == mRadiusSeekBar) {
+                float fRadius = progress / 100.0f;
+                fRadius *= (float)(MAX_RADIUS);
+                mRadius = (int)fRadius;
+
+                mScript.set_radius(mRadius);
+            }
+            else if(seekBar == mInBlackSeekBar) {
+                mInBlack = (float)progress;
+                mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+            }
+            else if(seekBar == mOutBlackSeekBar) {
+                mOutBlack = (float)progress;
+                mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+            }
+            else if(seekBar == mInWhiteSeekBar) {
+                mInWhite = (float)progress + 127.0f;
+                mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+            }
+            else if(seekBar == mOutWhiteSeekBar) {
+                mOutWhite = (float)progress + 127.0f;
+                mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+            }
+            else if(seekBar == mGammaSeekBar) {
+                mGamma = (float)progress/100.0f;
+                mGamma = Math.max(mGamma, 0.1f);
+                mGamma = 1.0f / mGamma;
+                mScriptVBlur.invoke_setGamma(mGamma);
+            }
+            else if(seekBar == mSaturationSeekBar) {
+                mSaturation = (float)progress / 50.0f;
+                mScriptVBlur.invoke_setSaturation(mSaturation);
+            }
+
+            long t = java.lang.System.currentTimeMillis();
+            if (true) {
+                mScript.invoke_filter();
+                mRS.finish();
+            } else {
+                javaFilter();
+                mDisplayView.invalidate();
+            }
+
+            t = java.lang.System.currentTimeMillis() - t;
+            android.util.Log.v("Img", "Renderscript frame time core ms " + t);
+        }
+    }
+
+    public void onStartTrackingTouch(SeekBar seekBar) {
+    }
+
+    public void onStopTrackingTouch(SeekBar seekBar) {
     }
 
     @Override
@@ -119,45 +311,54 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
 
-        mBitmap = loadBitmap(R.drawable.data);
+        mBitmapIn = loadBitmap(R.drawable.data);
+        mBitmapOut = loadBitmap(R.drawable.data);
+        mBitmapScratch = loadBitmap(R.drawable.data);
 
         mSurfaceView = (SurfaceView) findViewById(R.id.surface);
         mSurfaceView.getHolder().addCallback(this);
 
         mDisplayView = (ImageView) findViewById(R.id.display);
-        mDisplayView.setImageBitmap(mBitmap);
+        mDisplayView.setImageBitmap(mBitmapOut);
 
-        ((SeekBar) findViewById(R.id.threshold)).setOnSeekBarChangeListener(
-                new SeekBar.OnSeekBarChangeListener() {
-            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-                if (fromUser) {
-                    mParams.threshold = progress / 100.0f;
-                    mParamsAllocation.data(mParams);
+        mRadiusSeekBar = (SeekBar) findViewById(R.id.radius);
+        mRadiusSeekBar.setOnSeekBarChangeListener(this);
 
-                    if (true) {
-                        mInvokable.execute();
-                    } else {
-                        javaFilter();
-                        mBitmap.setPixels(mOutData, 0, mParams.outWidth, 0, 0,
-                                mParams.outWidth, mParams.outHeight);
-                        mDisplayView.invalidate();
-                    }
-                }
-            }
+        mInBlackSeekBar = (SeekBar)findViewById(R.id.inBlack);
+        mInBlackSeekBar.setOnSeekBarChangeListener(this);
+        mInBlackSeekBar.setMax(128);
+        mInBlackSeekBar.setProgress(0);
+        mOutBlackSeekBar = (SeekBar)findViewById(R.id.outBlack);
+        mOutBlackSeekBar.setOnSeekBarChangeListener(this);
+        mOutBlackSeekBar.setMax(128);
+        mOutBlackSeekBar.setProgress(0);
 
-            public void onStartTrackingTouch(SeekBar seekBar) {
-            }
+        mInWhiteSeekBar = (SeekBar)findViewById(R.id.inWhite);
+        mInWhiteSeekBar.setOnSeekBarChangeListener(this);
+        mInWhiteSeekBar.setMax(128);
+        mInWhiteSeekBar.setProgress(128);
+        mOutWhiteSeekBar = (SeekBar)findViewById(R.id.outWhite);
+        mOutWhiteSeekBar.setOnSeekBarChangeListener(this);
+        mOutWhiteSeekBar.setMax(128);
+        mOutWhiteSeekBar.setProgress(128);
 
-            public void onStopTrackingTouch(SeekBar seekBar) {
-            }
-        });
+        mGammaSeekBar = (SeekBar)findViewById(R.id.inGamma);
+        mGammaSeekBar.setOnSeekBarChangeListener(this);
+        mGammaSeekBar.setMax(150);
+        mGammaSeekBar.setProgress(100);
+
+        mSaturationSeekBar = (SeekBar)findViewById(R.id.inSaturation);
+        mSaturationSeekBar.setOnSeekBarChangeListener(this);
+        mSaturationSeekBar.setProgress(50);
+
+        mBenchmarkResult = (TextView) findViewById(R.id.benchmarkText);
+        mBenchmarkResult.setText("Benchmark not yet run");
     }
 
     public void surfaceCreated(SurfaceHolder holder) {
-        mParams = createParams();
-        mInvokable = createScript();
-
-        mInvokable.execute();
+        createScript();
+        mScript.invoke_filter();
+        mRS.finish();
     }
 
     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
@@ -166,54 +367,38 @@
     public void surfaceDestroyed(SurfaceHolder holder) {
     }
 
-    private Script.Invokable createScript() {
+    private void createScript() {
         mRS = RenderScript.create();
         mRS.mMessageCallback = new FilterCallback();
 
-        mParamsType = Type.createFromClass(mRS, Params.class, 1, "Parameters");
-        mParamsAllocation = Allocation.createTyped(mRS, mParamsType);
-        mParamsAllocation.data(mParams);
+        mInPixelsAllocation = Allocation.createBitmapRef(mRS, mBitmapIn);
+        mOutPixelsAllocation = Allocation.createBitmapRef(mRS, mBitmapOut);
 
-        final int pixelCount = mParams.inWidth * mParams.inHeight;
+        Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS));
+        tb.add(android.renderscript.Dimension.X, mBitmapIn.getWidth());
+        tb.add(android.renderscript.Dimension.Y, mBitmapIn.getHeight());
+        mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
+        mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
 
-        mPixelType = Type.createFromClass(mRS, Pixel.class, 1, "Pixel");
-        mInPixelsAllocation = Allocation.createSized(mRS,
-                Element.createUser(mRS, Element.DataType.SIGNED_32),
-                pixelCount);
-        mOutPixelsAllocation = Allocation.createSized(mRS,
-                Element.createUser(mRS, Element.DataType.SIGNED_32),
-                pixelCount);
+        mScriptVBlur = new ScriptC_vertical_blur(mRS, getResources(), R.raw.vertical_blur);
+        mScriptHBlur = new ScriptC_horizontal_blur(mRS, getResources(), R.raw.horizontal_blur);
 
-        mInData = new int[pixelCount];
-        mBitmap.getPixels(mInData, 0, mParams.inWidth, 0, 0, mParams.inWidth, mParams.inHeight);
-        mInPixelsAllocation.data(mInData);
+        mScript = new ScriptC_threshold(mRS, getResources(), R.raw.threshold);
+        mScript.set_width(mBitmapIn.getWidth());
+        mScript.set_height(mBitmapIn.getHeight());
+        mScript.set_radius(mRadius);
 
-        mOutData = new int[pixelCount];
-        mOutPixelsAllocation.data(mOutData);
+        mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+        mScriptVBlur.invoke_setGamma(mGamma);
+        mScriptVBlur.invoke_setSaturation(mSaturation);
 
-        ScriptC.Builder sb = new ScriptC.Builder(mRS);
-        sb.setType(mParamsType, "Params", 0);
-        sb.setType(mPixelType, "InPixel", 1);
-        sb.setType(mPixelType, "OutPixel", 2);
-        sb.setType(true, 2);
-        Script.Invokable invokable = sb.addInvokable("main");
-        sb.setScript(getResources(), R.raw.threshold);
-        //sb.setRoot(true);
+        mScript.bind_InPixel(mInPixelsAllocation);
+        mScript.bind_OutPixel(mOutPixelsAllocation);
+        mScript.bind_ScratchPixel1(mScratchPixelsAllocation1);
+        mScript.bind_ScratchPixel2(mScratchPixelsAllocation2);
 
-        ScriptC script = sb.create();
-        script.bindAllocation(mParamsAllocation, 0);
-        script.bindAllocation(mInPixelsAllocation, 1);
-        script.bindAllocation(mOutPixelsAllocation, 2);
-
-        return invokable;
-    }
-
-    private Params createParams() {
-        final Params params = new Params();
-        params.inWidth = params.outWidth = mBitmap.getWidth();
-        params.inHeight = params.outHeight = mBitmap.getHeight();
-        params.threshold = 0.5f;
-        return params;
+        mScript.set_vBlurScript(mScriptVBlur);
+        mScript.set_hBlurScript(mScriptHBlur);
     }
 
     private Bitmap loadBitmap(int resource) {
@@ -229,4 +414,30 @@
         source.recycle();
         return b;
     }
+
+    // button hook
+    public void benchmark(View v) {
+        android.util.Log.v("Img", "Benchmarking");
+        int oldRadius = mRadius;
+        mRadius = MAX_RADIUS;
+        mScript.set_radius(mRadius);
+
+        long t = java.lang.System.currentTimeMillis();
+
+        mScript.invoke_filter();
+        mRS.finish();
+
+        t = java.lang.System.currentTimeMillis() - t;
+        android.util.Log.v("Img", "Renderscript frame time core ms " + t);
+
+        //long javaTime = javaFilter();
+        //mBenchmarkResult.setText("RS: " + t + " ms  Java: " + javaTime + " ms");
+        mBenchmarkResult.setText("RS: " + t + " ms");
+
+        mRadius = oldRadius;
+        mScript.set_radius(mRadius);
+
+        mScript.invoke_filter();
+        mRS.finish();
+    }
 }
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
new file mode 100644
index 0000000..cfffac8
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
@@ -0,0 +1,30 @@
+#pragma version(1)
+
+#include "ip.rsh"
+
+void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
+    float4 *output = (float4 *)v_out;
+    const FilterStruct *fs = (const FilterStruct *)usrData;
+    const float4 *input = (const float4 *)rsGetElementAt(fs->ain, 0, y);
+
+    float3 blurredPixel = 0;
+    const float *gPtr = fs->gaussian;
+    if ((x > fs->radius) && (x < (fs->width - fs->radius))) {
+        const float4 *i = input + (x - fs->radius);
+        for(int r = -fs->radius; r <= fs->radius; r ++) {
+            blurredPixel += i->xyz * gPtr[0];
+            gPtr++;
+            i++;
+        }
+    } else {
+        for(int r = -fs->radius; r <= fs->radius; r ++) {
+            // Stepping left and right away from the pixel
+            int validW = rsClamp(x + r, (uint)0, (uint)(fs->width - 1));
+            blurredPixel += input[validW].xyz * gPtr[0];
+            gPtr++;
+        }
+    }
+
+    output->xyz = blurredPixel;
+}
+
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ip.rsh b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ip.rsh
new file mode 100644
index 0000000..1d7a719
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ip.rsh
@@ -0,0 +1,15 @@
+#pragma rs java_package_name(com.android.rs.image)
+
+#define MAX_RADIUS 25
+
+typedef struct FilterStruct_s {
+    rs_allocation ain;
+
+    float *gaussian; //[MAX_RADIUS * 2 + 1];
+    int height;
+    int width;
+    int radius;
+
+} FilterStruct;
+
+
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
new file mode 100644
index 0000000..d05ed6f
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs
@@ -0,0 +1,96 @@
+#pragma version(1)
+
+#include "ip.rsh"
+
+int height;
+int width;
+int radius;
+
+uchar4 * InPixel;
+uchar4 * OutPixel;
+float4 * ScratchPixel1;
+float4 * ScratchPixel2;
+
+#pragma rs export_func(filter);
+
+rs_script vBlurScript;
+rs_script hBlurScript;
+
+const int CMD_FINISHED = 1;
+
+// Store our coefficients here
+static float gaussian[MAX_RADIUS * 2 + 1];
+
+
+static void computeGaussianWeights() {
+    // Compute gaussian weights for the blur
+    // e is the euler's number
+    float e = 2.718281828459045f;
+    float pi = 3.1415926535897932f;
+    // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
+    // x is of the form [-radius .. 0 .. radius]
+    // and sigma varies with radius.
+    // Based on some experimental radius values and sigma's
+    // we approximately fit sigma = f(radius) as
+    // sigma = radius * 0.4  + 0.6
+    // The larger the radius gets, the more our gaussian blur
+    // will resemble a box blur since with large sigma
+    // the gaussian curve begins to lose its shape
+    float sigma = 0.4f * (float)radius + 0.6f;
+
+    // Now compute the coefficints
+    // We will store some redundant values to save some math during
+    // the blur calculations
+    // precompute some values
+    float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
+    float coeff2 = - 1.0f / (2.0f * sigma * sigma);
+
+    float normalizeFactor = 0.0f;
+    float floatR = 0.0f;
+    int r;
+    for(r = -radius; r <= radius; r ++) {
+        floatR = (float)r;
+        gaussian[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
+        normalizeFactor += gaussian[r + radius];
+    }
+
+    //Now we need to normalize the weights because all our coefficients need to add up to one
+    normalizeFactor = 1.0f / normalizeFactor;
+    for(r = -radius; r <= radius; r ++) {
+        floatR = (float)r;
+        gaussian[r + radius] *= normalizeFactor;
+    }
+}
+
+
+static void copyInput() {
+    rs_allocation ain = {0};
+    rsSetObject(&ain,rsGetAllocation(InPixel));
+    uint32_t dimx = rsAllocationGetDimX(ain);
+    uint32_t dimy = rsAllocationGetDimY(ain);
+    for(uint32_t y = 0; y < dimy; y++) {
+        for(uint32_t x = 0; x < dimx; x++) {
+            ScratchPixel1[x + y * dimx] = convert_float4(InPixel[x + y * dimx]);
+        }
+    }
+    rsClearObject(&ain);
+}
+
+void filter() {
+    copyInput();
+    computeGaussianWeights();
+
+    FilterStruct fs;
+    fs.gaussian = gaussian;
+    fs.width = width;
+    fs.height = height;
+    fs.radius = radius;
+
+    fs.ain = rsGetAllocation(ScratchPixel1);
+    rsForEach(hBlurScript, fs.ain, rsGetAllocation(ScratchPixel2), &fs);
+
+    fs.ain = rsGetAllocation(ScratchPixel2);
+    rsForEach(vBlurScript, fs.ain, rsGetAllocation(OutPixel), &fs);
+    rsSendToClientBlocking(CMD_FINISHED);
+}
+
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
new file mode 100644
index 0000000..d901d2a
--- /dev/null
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
@@ -0,0 +1,92 @@
+#pragma version(1)
+
+#include "ip.rsh"
+
+static float inBlack;
+static float outBlack;
+static float inWhite;
+static float outWhite;
+static float3 gamma;
+static float saturation;
+
+static float inWMinInB;
+static float outWMinOutB;
+static float overInWMinInB;
+static rs_matrix3x3 colorMat;
+
+#pragma rs export_func(setLevels, setSaturation, setGamma);
+
+void setLevels(float iBlk, float oBlk, float iWht, float oWht) {
+    inBlack = iBlk;
+    outBlack = oBlk;
+    inWhite = iWht;
+    outWhite = oWht;
+
+    inWMinInB = inWhite - inBlack;
+    outWMinOutB = outWhite - outBlack;
+    overInWMinInB = 1.f / inWMinInB;
+}
+
+void setSaturation(float sat) {
+    saturation = sat;
+
+    // Saturation
+    // Linear weights
+    //float rWeight = 0.3086f;
+    //float gWeight = 0.6094f;
+    //float bWeight = 0.0820f;
+
+    // Gamma 2.2 weights (we haven't converted our image to linear space yet for perf reasons)
+    float rWeight = 0.299f;
+    float gWeight = 0.587f;
+    float bWeight = 0.114f;
+
+    float oneMinusS = 1.0f - saturation;
+    rsMatrixSet(&colorMat, 0, 0, oneMinusS * rWeight + saturation);
+    rsMatrixSet(&colorMat, 0, 1, oneMinusS * rWeight);
+    rsMatrixSet(&colorMat, 0, 2, oneMinusS * rWeight);
+    rsMatrixSet(&colorMat, 1, 0, oneMinusS * gWeight);
+    rsMatrixSet(&colorMat, 1, 1, oneMinusS * gWeight + saturation);
+    rsMatrixSet(&colorMat, 1, 2, oneMinusS * gWeight);
+    rsMatrixSet(&colorMat, 2, 0, oneMinusS * bWeight);
+    rsMatrixSet(&colorMat, 2, 1, oneMinusS * bWeight);
+    rsMatrixSet(&colorMat, 2, 2, oneMinusS * bWeight + saturation);
+}
+
+void setGamma(float g) {
+    gamma = (float3)g;
+}
+
+void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
+    uchar4 *output = (uchar4 *)v_out;
+    const FilterStruct *fs = (const FilterStruct *)usrData;
+    const float4 *input = (const float4 *)rsGetElementAt(fs->ain, x, 0);
+
+    float3 blurredPixel = 0;
+    const float *gPtr = fs->gaussian;
+    if ((y > fs->radius) && (y < (fs->height - fs->radius))) {
+        const float4 *i = input + ((y - fs->radius) * fs->width);
+        for(int r = -fs->radius; r <= fs->radius; r ++) {
+            blurredPixel += i->xyz * gPtr[0];
+            gPtr++;
+            i += fs->width;
+        }
+    } else {
+        for(int r = -fs->radius; r <= fs->radius; r ++) {
+            int validH = rsClamp(y + r, (uint)0, (uint)(fs->height - 1));
+            const float4 *i = input + validH * fs->width;
+            blurredPixel += i->xyz * gPtr[0];
+            gPtr++;
+        }
+    }
+
+    float3 temp = rsMatrixMultiply(&colorMat, blurredPixel);
+    temp = (clamp(temp, 0.f, 255.f) - inBlack) * overInWMinInB;
+    if (gamma.x != 1.0f)
+        temp = pow(temp, (float3)gamma);
+    temp = clamp(temp * outWMinOutB + outBlack, 0.f, 255.f);
+
+    output->xyz = convert_uchar3(temp);
+    //output->w = input->w;
+}
+
diff --git a/libs/rs/java/ModelViewer/Android.mk b/libs/rs/java/ModelViewer/Android.mk
new file mode 100644
index 0000000..efe77d7
--- /dev/null
+++ b/libs/rs/java/ModelViewer/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+
+LOCAL_PACKAGE_NAME := ModelViewer
+
+include $(BUILD_PACKAGE)
+
+endif
diff --git a/libs/rs/java/ModelViewer/AndroidManifest.xml b/libs/rs/java/ModelViewer/AndroidManifest.xml
new file mode 100644
index 0000000..959fe53
--- /dev/null
+++ b/libs/rs/java/ModelViewer/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.modelviewer">
+    <application android:label="ModelViewer">
+        <activity android:name="SimpleModel"
+                  android:label="SimpleModel"
+                  android:screenOrientation="portrait"
+                  android:theme="@android:style/Theme.Black.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name="SceneGraph"
+                  android:label="SceneGraph"
+                  android:theme="@android:style/Theme.Black.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/libs/rs/java/ModelViewer/res/drawable/robot.png b/libs/rs/java/ModelViewer/res/drawable/robot.png
new file mode 100644
index 0000000..f7353fd
--- /dev/null
+++ b/libs/rs/java/ModelViewer/res/drawable/robot.png
Binary files differ
diff --git a/libs/rs/java/ModelViewer/res/raw/robot.a3d b/libs/rs/java/ModelViewer/res/raw/robot.a3d
new file mode 100644
index 0000000..f48895c
--- /dev/null
+++ b/libs/rs/java/ModelViewer/res/raw/robot.a3d
Binary files differ
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraph.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraph.java
new file mode 100644
index 0000000..557e0cc
--- /dev/null
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraph.java
@@ -0,0 +1,71 @@
+/*
+ * 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.android.modelviewer;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class SceneGraph extends Activity {
+
+    private SceneGraphView mView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new SceneGraphView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mView.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onPause();
+        mView.onPause();
+    }
+
+}
+
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
new file mode 100644
index 0000000..1531d09
--- /dev/null
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphRS.java
@@ -0,0 +1,219 @@
+/*
+ * 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.android.modelviewer;
+
+import java.io.Writer;
+import java.util.Map;
+import java.util.Vector;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.Element.Builder;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.util.Log;
+
+
+public class SceneGraphRS {
+
+    private final int STATE_LAST_FOCUS = 1;
+
+    int mWidth;
+    int mHeight;
+    int mRotation;
+
+    public SceneGraphRS() {
+    }
+
+    public void init(RenderScriptGL rs, Resources res, int width, int height) {
+        mRS = rs;
+        mRes = res;
+        mWidth = width;
+        mHeight = height;
+        mRotation = 0;
+        initRS();
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    private Sampler mSampler;
+    private ProgramStore mPSBackground;
+    private ProgramFragment mPFBackground;
+    private ProgramVertex mPVBackground;
+    private ProgramVertex.MatrixAllocation mPVA;
+
+    private Allocation mGridImage;
+    private Allocation mAllocPV;
+
+    private Mesh mMesh;
+
+    private Font mItalic;
+    private Allocation mTextAlloc;
+
+    private ScriptC_scenegraph mScript;
+    private ScriptC_transform mTransformScript;
+
+    int mLastX;
+    int mLastY;
+
+    public void touchEvent(int x, int y) {
+        int dx = mLastX - x;
+        if(Math.abs(dx) > 50 || Math.abs(dx) < 3) {
+            dx = 0;
+        }
+
+        mRotation -= dx;
+        if(mRotation > 360) {
+            mRotation -= 360;
+        }
+        if(mRotation < 0) {
+            mRotation += 360;
+        }
+
+        mScript.set_gRotate(-(float)mRotation);
+
+        mLastX = x;
+        mLastY = y;
+    }
+
+    private void initPFS() {
+        ProgramStore.Builder b = new ProgramStore.Builder(mRS);
+
+        b.setDepthFunc(ProgramStore.DepthFunc.LESS);
+        b.setDitherEnable(false);
+        b.setDepthMask(true);
+        mPSBackground = b.create();
+
+        mScript.set_gPFSBackground(mPSBackground);
+    }
+
+    private void initPF() {
+        Sampler.Builder bs = new Sampler.Builder(mRS);
+        bs.setMin(Sampler.Value.LINEAR);
+        bs.setMag(Sampler.Value.LINEAR);
+        bs.setWrapS(Sampler.Value.CLAMP);
+        bs.setWrapT(Sampler.Value.CLAMP);
+        mSampler = bs.create();
+
+        ProgramFragment.Builder b = new ProgramFragment.Builder(mRS);
+        b.setTexture(ProgramFragment.Builder.EnvMode.REPLACE,
+                     ProgramFragment.Builder.Format.RGBA, 0);
+        mPFBackground = b.create();
+        mPFBackground.bindSampler(mSampler, 0);
+
+        mScript.set_gPFBackground(mPFBackground);
+    }
+
+    private void initPV() {
+        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS);
+        mPVBackground = pvb.create();
+
+        mPVA = new ProgramVertex.MatrixAllocation(mRS);
+        mPVBackground.bindAllocation(mPVA);
+
+        mScript.set_gPVBackground(mPVBackground);
+    }
+
+    private void loadImage() {
+        mGridImage = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot, Element.RGB_565(mRS), true);
+        mGridImage.uploadToTexture(0);
+
+        mScript.set_gTGrid(mGridImage);
+    }
+
+    private void initTextAllocation() {
+        String allocString = "Displaying file: R.raw.robot";
+        mTextAlloc = Allocation.createFromString(mRS, allocString);
+        mScript.set_gTextAlloc(mTextAlloc);
+    }
+
+    SgTransform mRootTransform;
+    SgTransform mGroup1;
+
+    SgTransform mRobot1;
+    SgTransform mRobot2;
+
+    void initTransformHierarchy() {
+        mRootTransform = new SgTransform(mRS);
+
+        mGroup1 = new SgTransform(mRS);
+        mRootTransform.addChild(mGroup1);
+
+        mRobot1 = new SgTransform(mRS);
+        mRobot2 = new SgTransform(mRS);
+
+        mGroup1.addChild(mRobot1);
+        mGroup1.addChild(mRobot2);
+
+        mGroup1.setTransform(0, new Float4(0.0f, 0.0f, -15.0f, 0.0f), TransformType.TRANSLATE);
+        mGroup1.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, 15.0f), TransformType.ROTATE);
+
+        mRobot1.setTransform(0, new Float4(-3.0f, -0.5f, 0.0f, 0.0f), TransformType.TRANSLATE);
+        mRobot1.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, 20.0f), TransformType.ROTATE);
+        mRobot1.setTransform(2, new Float4(0.2f, 0.2f, 0.2f, 0.0f), TransformType.SCALE);
+
+        mRobot2.setTransform(0, new Float4(3.0f, 0.0f, 0.0f, 0.0f), TransformType.TRANSLATE);
+        mRobot2.setTransform(1, new Float4(0.0f, 1.0f, 0.0f, -20.0f), TransformType.ROTATE);
+        mRobot2.setTransform(2, new Float4(0.3f, 0.3f, 0.3f, 0.0f), TransformType.SCALE);
+    }
+
+    private void initRS() {
+
+        mScript = new ScriptC_scenegraph(mRS, mRes, R.raw.scenegraph);
+        mTransformScript = new ScriptC_transform(mRS, mRes, R.raw.transform);
+        mTransformScript.set_transformScript(mTransformScript);
+
+        mScript.set_gTransformRS(mTransformScript);
+
+        initPFS();
+        initPF();
+        initPV();
+
+        loadImage();
+
+        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
+        FileA3D.IndexEntry entry = model.getIndexEntry(0);
+        if(entry == null || entry.getClassID() != FileA3D.ClassID.MESH) {
+            Log.e("rs", "could not load model");
+        }
+        else {
+            mMesh = (Mesh)entry.getObject();
+            mScript.set_gTestMesh(mMesh);
+        }
+
+        mItalic = Font.create(mRS, mRes, "DroidSerif-Italic.ttf", 8);
+        mScript.set_gItalic(mItalic);
+
+        initTextAllocation();
+
+        initTransformHierarchy();
+
+        Log.v("========SceneGraph========", "transform hierarchy initialized");
+
+        mScript.bind_gRootNode(mRootTransform.getField());
+
+        mScript.bind_gGroup(mGroup1.mParent.mChildField);
+        mScript.bind_gRobot1(mRobot1.mParent.mChildField);
+        mScript.set_gRobot1Index(mRobot1.mIndexInParentGroup);
+        mScript.bind_gRobot2(mRobot2.mParent.mChildField);
+        mScript.set_gRobot2Index(mRobot2.mIndexInParentGroup);
+
+        mRS.contextBindRootScript(mScript);
+    }
+}
+
+
+
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphView.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphView.java
new file mode 100644
index 0000000..9457fd7
--- /dev/null
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SceneGraphView.java
@@ -0,0 +1,96 @@
+/*
+ * 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.android.modelviewer;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class SceneGraphView extends RSSurfaceView {
+
+    public SceneGraphView(Context context) {
+        super(context);
+        //setFocusable(true);
+    }
+
+    private RenderScriptGL mRS;
+    private SceneGraphRS mRender;
+
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            sc.setDepth(16, 24);
+            mRS = createRenderScript(sc);
+            mRS.contextSetSurface(w, h, holder.getSurface());
+            mRender = new SceneGraphRS();
+            mRender.init(mRS, getResources(), w, h);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        if(mRS != null) {
+            mRS = null;
+            destroyRenderScript();
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event)
+    {
+        // break point at here
+        // this method doesn't work when 'extends View' include 'extends ScrollView'.
+        return super.onKeyDown(keyCode, event);
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        boolean ret = true;
+        int act = ev.getAction();
+        if (act == ev.ACTION_UP) {
+            ret = false;
+        }
+
+        mRender.touchEvent((int)ev.getX(), (int)ev.getY());
+        return ret;
+    }
+}
+
+
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java
new file mode 100644
index 0000000..8351f42
--- /dev/null
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SgTransform.java
@@ -0,0 +1,112 @@
+/*
+ * 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.android.modelviewer;
+
+import java.io.Writer;
+import java.util.Map;
+import java.util.Vector;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.Element.Builder;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.util.Log;
+
+enum TransformType {
+
+    NONE(0),
+    TRANSLATE(1),
+    ROTATE(2),
+    SCALE(3);
+
+    int mID;
+    TransformType(int id) {
+        mID = id;
+    }
+}
+
+public class SgTransform {
+
+
+    ScriptField_SgTransform mTransformField;
+    ScriptField_SgTransform mChildField;
+    public ScriptField_SgTransform.Item mTransformData;
+
+    RenderScript mRS;
+
+    Vector mChildren;
+    SgTransform mParent;
+    int mIndexInParentGroup;
+
+    public void setParent(SgTransform parent, int parentIndex) {
+        mParent = parent;
+        mIndexInParentGroup = parentIndex;
+    }
+
+    public void addChild(SgTransform child) {
+        mChildren.add(child);
+        child.setParent(this, mChildren.size() - 1);
+    }
+
+    public void setTransform(int index, Float4 value, TransformType type) {
+        mTransformData.transforms[index] = value;
+        mTransformData.transformTypes[index] = type.mID;
+    }
+
+    void initData() {
+        int numElements = mTransformData.transforms.length;
+        mTransformData.transformTypes = new int[numElements];
+        for(int i = 0; i < numElements; i ++) {
+            mTransformData.transforms[i] = new Float4(0, 0, 0, 0);
+            mTransformData.transformTypes[i] = TransformType.NONE.mID;
+        }
+
+        mTransformData.isDirty = 1;
+        mTransformData.children = null;
+    }
+
+    public SgTransform(RenderScript rs) {
+        mRS = rs;
+        mTransformData = new ScriptField_SgTransform.Item();
+        mChildren = new Vector();
+        initData();
+    }
+
+    public ScriptField_SgTransform.Item getData() {
+        if(mChildren.size() != 0) {
+            mChildField = new ScriptField_SgTransform(mRS, mChildren.size());
+            mTransformData.children = mChildField.getAllocation();
+
+            for(int i = 0; i < mChildren.size(); i ++) {
+                SgTransform child = (SgTransform)mChildren.get(i);
+                mChildField.set(child.getData(), i, false);
+            }
+            mChildField.copyAll();
+        }
+
+        return mTransformData;
+    }
+
+    public ScriptField_SgTransform getField() {
+        mTransformField = new ScriptField_SgTransform(mRS, 1);
+        mTransformField.set(getData(), 0, true);
+        return mTransformField;
+    }
+}
+
+
+
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java
new file mode 100644
index 0000000..cb7c39c
--- /dev/null
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModel.java
@@ -0,0 +1,71 @@
+/*
+ * 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.android.modelviewer;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class SimpleModel extends Activity {
+
+    private SimpleModelView mView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new SimpleModelView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mView.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onPause();
+        mView.onPause();
+    }
+
+}
+
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
new file mode 100644
index 0000000..edf40e9
--- /dev/null
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelRS.java
@@ -0,0 +1,169 @@
+/*
+ * 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.android.modelviewer;
+
+import java.io.Writer;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.util.Log;
+
+
+public class SimpleModelRS {
+
+    private final int STATE_LAST_FOCUS = 1;
+
+    int mWidth;
+    int mHeight;
+    int mRotation;
+
+    public SimpleModelRS() {
+    }
+
+    public void init(RenderScriptGL rs, Resources res, int width, int height) {
+        mRS = rs;
+        mRes = res;
+        mWidth = width;
+        mHeight = height;
+        mRotation = 0;
+        initRS();
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    private Sampler mSampler;
+    private ProgramStore mPSBackground;
+    private ProgramFragment mPFBackground;
+    private ProgramVertex mPVBackground;
+    private ProgramVertex.MatrixAllocation mPVA;
+
+    private Allocation mGridImage;
+    private Allocation mAllocPV;
+
+    private Mesh mMesh;
+
+    private Font mItalic;
+    private Allocation mTextAlloc;
+
+    private ScriptC_simplemodel mScript;
+
+    int mLastX;
+    int mLastY;
+
+    public void touchEvent(int x, int y) {
+        int dx = mLastX - x;
+        if(Math.abs(dx) > 50 || Math.abs(dx) < 3) {
+            dx = 0;
+        }
+
+        mRotation -= dx;
+        if(mRotation > 360) {
+            mRotation -= 360;
+        }
+        if(mRotation < 0) {
+            mRotation += 360;
+        }
+
+        mScript.set_gRotate((float)mRotation);
+
+        mLastX = x;
+        mLastY = y;
+    }
+
+    private void initPFS() {
+        ProgramStore.Builder b = new ProgramStore.Builder(mRS);
+
+        b.setDepthFunc(ProgramStore.DepthFunc.LESS);
+        b.setDitherEnable(false);
+        b.setDepthMask(true);
+        mPSBackground = b.create();
+
+        mScript.set_gPFSBackground(mPSBackground);
+    }
+
+    private void initPF() {
+        Sampler.Builder bs = new Sampler.Builder(mRS);
+        bs.setMin(Sampler.Value.LINEAR);
+        bs.setMag(Sampler.Value.LINEAR);
+        bs.setWrapS(Sampler.Value.CLAMP);
+        bs.setWrapT(Sampler.Value.CLAMP);
+        mSampler = bs.create();
+
+        ProgramFragment.Builder b = new ProgramFragment.Builder(mRS);
+        b.setTexture(ProgramFragment.Builder.EnvMode.REPLACE,
+                     ProgramFragment.Builder.Format.RGBA, 0);
+        mPFBackground = b.create();
+        mPFBackground.bindSampler(mSampler, 0);
+
+        mScript.set_gPFBackground(mPFBackground);
+    }
+
+    private void initPV() {
+        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS);
+        mPVBackground = pvb.create();
+
+        mPVA = new ProgramVertex.MatrixAllocation(mRS);
+        mPVBackground.bindAllocation(mPVA);
+
+        mScript.set_gPVBackground(mPVBackground);
+    }
+
+    private void loadImage() {
+        mGridImage = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.robot, Element.RGB_565(mRS), true);
+        mGridImage.uploadToTexture(0);
+
+        mScript.set_gTGrid(mGridImage);
+    }
+
+    private void initTextAllocation() {
+        String allocString = "Displaying file: R.raw.robot";
+        mTextAlloc = Allocation.createFromString(mRS, allocString);
+        mScript.set_gTextAlloc(mTextAlloc);
+    }
+
+    private void initRS() {
+
+        mScript = new ScriptC_simplemodel(mRS, mRes, R.raw.simplemodel);
+
+        initPFS();
+        initPF();
+        initPV();
+
+        loadImage();
+
+        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.robot);
+        FileA3D.IndexEntry entry = model.getIndexEntry(0);
+        if(entry == null || entry.getClassID() != FileA3D.ClassID.MESH) {
+            Log.e("rs", "could not load model");
+        }
+        else {
+            mMesh = (Mesh)entry.getObject();
+            mScript.set_gTestMesh(mMesh);
+        }
+
+        mItalic = Font.create(mRS, mRes, "DroidSerif-Italic.ttf", 8);
+        mScript.set_gItalic(mItalic);
+
+        initTextAllocation();
+
+        mRS.contextBindRootScript(mScript);
+    }
+}
+
+
+
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java
new file mode 100644
index 0000000..4253085
--- /dev/null
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/SimpleModelView.java
@@ -0,0 +1,96 @@
+/*
+ * 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.android.modelviewer;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class SimpleModelView extends RSSurfaceView {
+
+    public SimpleModelView(Context context) {
+        super(context);
+        //setFocusable(true);
+    }
+
+    private RenderScriptGL mRS;
+    private SimpleModelRS mRender;
+
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            sc.setDepth(16, 24);
+            mRS = createRenderScript(sc);
+            mRS.contextSetSurface(w, h, holder.getSurface());
+            mRender = new SimpleModelRS();
+            mRender.init(mRS, getResources(), w, h);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        if(mRS != null) {
+            mRS = null;
+            destroyRenderScript();
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event)
+    {
+        // break point at here
+        // this method doesn't work when 'extends View' include 'extends ScrollView'.
+        return super.onKeyDown(keyCode, event);
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        boolean ret = true;
+        int act = ev.getAction();
+        if (act == ev.ACTION_UP) {
+            ret = false;
+        }
+
+        mRender.touchEvent((int)ev.getX(), (int)ev.getY());
+        return ret;
+    }
+}
+
+
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs
new file mode 100644
index 0000000..3bee8d6
--- /dev/null
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/scenegraph.rs
@@ -0,0 +1,91 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.modelviewer)
+
+#include "rs_graphics.rsh"
+#include "transform_def.rsh"
+
+rs_program_vertex gPVBackground;
+rs_program_fragment gPFBackground;
+
+rs_allocation gTGrid;
+rs_mesh gTestMesh;
+
+rs_program_store gPFSBackground;
+
+float gRotate;
+
+rs_font gItalic;
+rs_allocation gTextAlloc;
+
+rs_script gTransformRS;
+
+SgTransform *gGroup;
+SgTransform *gRobot1;
+int gRobot1Index;
+SgTransform *gRobot2;
+int gRobot2Index;
+
+SgTransform *gRootNode;
+
+void init() {
+    gRotate = 0.0f;
+}
+
+int root(int launchID) {
+
+    gGroup->transforms[1].w += 0.5f;
+    gGroup->isDirty = 1;
+
+    SgTransform *robot1Ptr = gRobot1 + gRobot1Index;
+
+    robot1Ptr->transforms[1].w -= 1.5f;
+    robot1Ptr->isDirty = 1;
+
+    SgTransform *robot2Ptr = gRobot2 + gRobot2Index;
+    robot2Ptr->transforms[1].w += 2.5f;
+    robot2Ptr->isDirty = 1;
+
+    rsForEach(gTransformRS, gRootNode->children, gRootNode->children, 0);
+
+    rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgClearDepth(1.0f);
+
+    rsgBindProgramVertex(gPVBackground);
+    rs_matrix4x4 proj;
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
+
+    rsgBindProgramFragment(gPFBackground);
+    rsgBindProgramStore(gPFSBackground);
+    rsgBindTexture(gPFBackground, 0, gTGrid);
+
+    rsgProgramVertexLoadModelMatrix(&robot1Ptr->globalMat);
+    rsgDrawMesh(gTestMesh);
+
+    rsgProgramVertexLoadModelMatrix(&robot2Ptr->globalMat);
+    rsgDrawMesh(gTestMesh);
+
+    color(0.3f, 0.3f, 0.3f, 1.0f);
+    rsgDrawText("Renderscript transform test", 30, 695);
+
+    rsgBindFont(gItalic);
+    rsgDrawText(gTextAlloc, 30, 730);
+
+    return 10;
+}
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs
new file mode 100644
index 0000000..419de62
--- /dev/null
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/simplemodel.rs
@@ -0,0 +1,71 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.modelviewer)
+
+#include "rs_graphics.rsh"
+
+rs_program_vertex gPVBackground;
+rs_program_fragment gPFBackground;
+
+rs_allocation gTGrid;
+rs_mesh gTestMesh;
+
+rs_program_store gPFSBackground;
+
+float gRotate;
+
+rs_font gItalic;
+rs_allocation gTextAlloc;
+
+void init() {
+    gRotate = 0.0f;
+}
+
+int root(int launchID) {
+
+    rsgClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgClearDepth(1.0f);
+
+    rsgBindProgramVertex(gPVBackground);
+    rs_matrix4x4 proj;
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
+
+    rsgBindProgramFragment(gPFBackground);
+    rsgBindProgramStore(gPFSBackground);
+    rsgBindTexture(gPFBackground, 0, gTGrid);
+
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    // Position our model on the screen
+    rsMatrixTranslate(&matrix, 0.0f, -0.3f, -10.0f);
+    rsMatrixScale(&matrix, 0.2f, 0.2f, 0.2f);
+    rsMatrixRotate(&matrix, 25.0f, 1.0f, 0.0f, 0.0f);
+    rsMatrixRotate(&matrix, gRotate, 0.0f, 1.0f, 0.0f);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    rsgDrawMesh(gTestMesh);
+
+    rsgFontColor(0.3f, 0.3f, 0.3f, 1.0f);
+    rsgDrawText("Renderscript model test", 30, 695);
+
+    rsgBindFont(gItalic);
+    rsgDrawText(gTextAlloc, 30, 730);
+
+    return 10;
+}
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs
new file mode 100644
index 0000000..e7c04de
--- /dev/null
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform.rs
@@ -0,0 +1,97 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.modelviewer)
+
+#include "transform_def.rsh"
+
+rs_script transformScript;
+
+typedef struct {
+    int changed;
+    rs_matrix4x4 *mat;
+} ParentData;
+
+void appendTransformation(int type, float4 data, rs_matrix4x4 *mat) {
+    rs_matrix4x4 temp;
+
+    switch(type) {
+    case TRANSFORM_TRANSLATE:
+        rsMatrixLoadTranslate(&temp, data.x, data.y, data.z);
+        break;
+    case TRANSFORM_ROTATE:
+        rsMatrixLoadRotate(&temp, data.w, data.x, data.y, data.z);
+        break;
+    case TRANSFORM_SCALE:
+        rsMatrixLoadScale(&temp, data.x, data.y, data.z);
+        break;
+    }
+    rsMatrixMultiply(mat, &temp);
+}
+
+void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
+
+    SgTransform *data = (SgTransform *)v_out;
+    const ParentData *parent = (const ParentData *)usrData;
+
+    //rsDebug("Transform data", (int)data);
+    //rsDebug("Entering parent", (int)parent);
+
+    rs_matrix4x4 *localMat = &data->localMat;
+    rs_matrix4x4 *globalMat = &data->globalMat;
+
+    ParentData toChild;
+    toChild.changed = 0;
+    toChild.mat = globalMat;
+
+    //rsDebug("Transform is dirty", data->isDirty);
+
+    // Refresh matrices if dirty
+    if(data->isDirty) {
+        data->isDirty = 0;
+        toChild.changed = 1;
+
+        // Reset our local matrix
+        rsMatrixLoadIdentity(localMat);
+
+        for(int i = 0; i < 16; i ++) {
+            if(data->transformTypes[i] == TRANSFORM_NONE) {
+                break;
+            }
+            //rsDebug("Transform adding transformation", transformTypes[i]);
+            appendTransformation(data->transformTypes[i], data->transforms[i], localMat);
+        }
+    }
+
+    //rsDebug("Transform checking parent", (int)0);
+
+    if(parent) {
+        if(parent->changed) {
+            toChild.changed = 1;
+
+            rsMatrixLoad(globalMat, parent->mat);
+            rsMatrixMultiply(globalMat, localMat);
+        }
+    }
+    else {
+        rsMatrixLoad(globalMat, localMat);
+    }
+
+    //rsDebug("Transform calling self with child ", (int)data->children.p);
+    if(data->children.p) {
+        rsForEach(transformScript, data->children, data->children, (void*)&toChild);
+    }
+}
diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh
new file mode 100644
index 0000000..7d1721c
--- /dev/null
+++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/transform_def.rsh
@@ -0,0 +1,35 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.modelviewer)
+
+#define TRANSFORM_NONE 0
+#define TRANSFORM_TRANSLATE 1
+#define TRANSFORM_ROTATE 2
+#define TRANSFORM_SCALE 3
+
+typedef struct __attribute__((packed, aligned(4))) {
+    rs_matrix4x4 globalMat;
+    rs_matrix4x4 localMat;
+
+    float4 transforms[16];
+    int transformTypes[16];
+
+    int isDirty;
+
+    rs_allocation children;
+
+} SgTransform;
diff --git a/libs/rs/java/Samples/Android.mk b/libs/rs/java/Samples/Android.mk
new file mode 100644
index 0000000..65ae734
--- /dev/null
+++ b/libs/rs/java/Samples/Android.mk
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+
+LOCAL_PACKAGE_NAME := Samples
+
+include $(BUILD_PACKAGE)
+
+endif
diff --git a/libs/rs/java/Samples/AndroidManifest.xml b/libs/rs/java/Samples/AndroidManifest.xml
new file mode 100644
index 0000000..be191f2
--- /dev/null
+++ b/libs/rs/java/Samples/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.samples">
+    <application android:label="Samples"
+    android:icon="@drawable/test_pattern">
+        <activity android:name="RsList"
+                  android:label="RsList"                  
+                  android:theme="@android:style/Theme.Black.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity android:name="RsRenderStates"
+                  android:label="RsStates"                  
+                  android:theme="@android:style/Theme.Black.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/libs/rs/java/Samples/res/drawable/checker.png b/libs/rs/java/Samples/res/drawable/checker.png
new file mode 100644
index 0000000..b631e1e
--- /dev/null
+++ b/libs/rs/java/Samples/res/drawable/checker.png
Binary files differ
diff --git a/libs/rs/java/Samples/res/drawable/data.png b/libs/rs/java/Samples/res/drawable/data.png
new file mode 100644
index 0000000..8e34714
--- /dev/null
+++ b/libs/rs/java/Samples/res/drawable/data.png
Binary files differ
diff --git a/libs/rs/java/Samples/res/drawable/leaf.png b/libs/rs/java/Samples/res/drawable/leaf.png
new file mode 100644
index 0000000..3cd3775
--- /dev/null
+++ b/libs/rs/java/Samples/res/drawable/leaf.png
Binary files differ
diff --git a/libs/rs/java/Samples/res/drawable/test_pattern.png b/libs/rs/java/Samples/res/drawable/test_pattern.png
new file mode 100644
index 0000000..e7d1455
--- /dev/null
+++ b/libs/rs/java/Samples/res/drawable/test_pattern.png
Binary files differ
diff --git a/libs/rs/java/Samples/res/drawable/torusmap.png b/libs/rs/java/Samples/res/drawable/torusmap.png
new file mode 100644
index 0000000..1e08f3b
--- /dev/null
+++ b/libs/rs/java/Samples/res/drawable/torusmap.png
Binary files differ
diff --git a/libs/rs/java/Samples/res/raw/multitexf.glsl b/libs/rs/java/Samples/res/raw/multitexf.glsl
new file mode 100644
index 0000000..351ff9b
--- /dev/null
+++ b/libs/rs/java/Samples/res/raw/multitexf.glsl
@@ -0,0 +1,12 @@
+varying vec2 varTex0;
+
+void main() {
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col0 = texture2D(UNI_Tex0, t0).rgba;
+   lowp vec4 col1 = texture2D(UNI_Tex1, t0*4.0).rgba;
+   lowp vec4 col2 = texture2D(UNI_Tex2, t0).rgba;
+   col0.xyz = col0.xyz*col1.xyz*1.5;
+   col0.xyz = mix(col0.xyz, col2.xyz, col2.w);
+   gl_FragColor = col0;
+}
+
diff --git a/libs/rs/java/Samples/res/raw/shaderf.glsl b/libs/rs/java/Samples/res/raw/shaderf.glsl
new file mode 100644
index 0000000..fcbe7ee
--- /dev/null
+++ b/libs/rs/java/Samples/res/raw/shaderf.glsl
@@ -0,0 +1,16 @@
+
+varying lowp float light0_Diffuse;
+varying lowp float light0_Specular;
+varying lowp float light1_Diffuse;
+varying lowp float light1_Specular;
+varying vec2 varTex0;
+
+void main() {
+   vec2 t0 = varTex0.xy;
+   lowp vec4 col = texture2D(UNI_Tex0, t0).rgba;
+   col.xyz = col.xyz * (light0_Diffuse * UNI_light0_DiffuseColor + light1_Diffuse * UNI_light1_DiffuseColor);
+   col.xyz += light0_Specular * UNI_light0_SpecularColor;
+   col.xyz += light1_Specular * UNI_light1_SpecularColor;
+   gl_FragColor = col;
+}
+
diff --git a/libs/rs/java/Samples/res/raw/shaderv.glsl b/libs/rs/java/Samples/res/raw/shaderv.glsl
new file mode 100644
index 0000000..867589cf
--- /dev/null
+++ b/libs/rs/java/Samples/res/raw/shaderv.glsl
@@ -0,0 +1,30 @@
+varying float light0_Diffuse;
+varying float light0_Specular;
+varying float light1_Diffuse;
+varying float light1_Specular;
+varying vec2 varTex0;
+
+// This is where actual shader code begins
+void main() {
+   vec4 worldPos = UNI_model * ATTRIB_position;
+   gl_Position = UNI_proj * worldPos;
+
+   mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);
+   vec3 worldNorm = model3 * ATTRIB_normal;
+   vec3 V = normalize(-worldPos.xyz);
+
+   vec3 light0Vec = normalize(UNI_light0_Posision - worldPos.xyz);
+   vec3 light0R = reflect(light0Vec, worldNorm);
+   light0_Diffuse = clamp(dot(worldNorm, light0Vec), 0.0, 1.0) * UNI_light0_Diffuse;
+   float light0Spec = clamp(dot(light0R, V), 0.001, 1.0);
+   light0_Specular = pow(light0Spec, UNI_light0_CosinePower) * UNI_light0_Specular;
+
+   vec3 light1Vec = normalize(UNI_light1_Posision - worldPos.xyz);
+   vec3 light1R = reflect(light1Vec, worldNorm);
+   light1_Diffuse = clamp(dot(worldNorm, light1Vec), 0.0, 1.0) * UNI_light1_Diffuse;
+   float light1Spec = clamp(dot(light1R, V), 0.001, 1.0);
+   light1_Specular = pow(light1Spec, UNI_light1_CosinePower) * UNI_light1_Specular;
+
+   gl_PointSize = 1.0;
+   varTex0 = ATTRIB_texture0;
+}
diff --git a/libs/rs/java/Samples/res/raw/torus.a3d b/libs/rs/java/Samples/res/raw/torus.a3d
new file mode 100644
index 0000000..0322b01
--- /dev/null
+++ b/libs/rs/java/Samples/res/raw/torus.a3d
Binary files differ
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsList.java b/libs/rs/java/Samples/src/com/android/samples/RsList.java
new file mode 100644
index 0000000..0f6b1ac
--- /dev/null
+++ b/libs/rs/java/Samples/src/com/android/samples/RsList.java
@@ -0,0 +1,71 @@
+/*
+ * 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.android.samples;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class RsList extends Activity {
+
+    private RsListView mView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new RsListView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity loses focus
+        super.onResume();
+        mView.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity loses focus
+        super.onPause();
+        mView.onPause();
+    }
+
+}
+
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsListRS.java b/libs/rs/java/Samples/src/com/android/samples/RsListRS.java
new file mode 100644
index 0000000..ce9ab01
--- /dev/null
+++ b/libs/rs/java/Samples/src/com/android/samples/RsListRS.java
@@ -0,0 +1,145 @@
+/*
+ * 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.android.samples;
+
+import java.io.Writer;
+import java.util.Vector;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.util.Log;
+
+
+public class RsListRS {
+
+    private final int STATE_LAST_FOCUS = 1;
+
+    private static final String[] DATA_LIST = {
+    "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
+    "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
+    "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
+    "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
+    "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
+    "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil",
+    "British Indian Ocean Territory", "British Virgin Islands", "Brunei", "Bulgaria",
+    "Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
+    "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
+    "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
+    "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+    "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
+    "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
+    "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
+    "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
+    "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
+    "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
+    "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
+    "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
+    "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
+    "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
+    "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
+    "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
+    "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
+    "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
+    "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
+    "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
+    "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
+    "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
+    "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
+    "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
+    "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
+    "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
+    "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
+    "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
+    "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
+    "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
+    "Ukraine", "United Arab Emirates", "United Kingdom",
+    "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
+    "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
+    "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
+    };
+
+    int mWidth;
+    int mHeight;
+
+    public RsListRS() {
+    }
+
+    public void init(RenderScriptGL rs, Resources res, int width, int height) {
+        mRS = rs;
+        mRes = res;
+        mWidth = width;
+        mHeight = height;
+        initRS();
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+    private Font mItalic;
+
+    ScriptField_ListAllocs_s mListAllocs;
+
+    private ScriptC_rslist mScript;
+
+    int mLastX;
+    int mLastY;
+
+    public void onActionDown(int x, int y) {
+        mScript.set_gDY(0.0f);
+
+        mLastX = x;
+        mLastY = y;
+    }
+
+    public void onActionMove(int x, int y) {
+        int dx = mLastX - x;
+        int dy = mLastY - y;
+
+        if(Math.abs(dy) <= 2) {
+            dy = 0;
+        }
+
+        mScript.set_gDY(dy);
+
+        mLastX = x;
+        mLastY = y;
+    }
+
+    private void initRS() {
+
+        mScript = new ScriptC_rslist(mRS, mRes, R.raw.rslist);
+
+        mListAllocs = new ScriptField_ListAllocs_s(mRS, DATA_LIST.length);
+        for(int i = 0; i < DATA_LIST.length; i ++) {
+            ScriptField_ListAllocs_s.Item listElem = new ScriptField_ListAllocs_s.Item();
+            listElem.text = Allocation.createFromString(mRS, DATA_LIST[i]);
+            mListAllocs.set(listElem, i, false);
+        }
+
+        mListAllocs.copyAll();
+
+        mScript.bind_gList(mListAllocs);
+
+        mItalic = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
+        mScript.set_gItalic(mItalic);
+
+        mRS.contextBindRootScript(mScript);
+    }
+}
+
+
+
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsListView.java b/libs/rs/java/Samples/src/com/android/samples/RsListView.java
new file mode 100644
index 0000000..cd66fbb
--- /dev/null
+++ b/libs/rs/java/Samples/src/com/android/samples/RsListView.java
@@ -0,0 +1,100 @@
+/*
+ * 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.android.samples;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class RsListView extends RSSurfaceView {
+
+    public RsListView(Context context) {
+        super(context);
+        //setFocusable(true);
+    }
+
+    private RenderScriptGL mRS;
+    private RsListRS mRender;
+
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            sc.setDepth(16, 24);
+            mRS = createRenderScript(sc);
+            mRS.contextSetSurface(w, h, holder.getSurface());
+            mRender = new RsListRS();
+            mRender.init(mRS, getResources(), w, h);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        if(mRS != null) {
+            mRS = null;
+            destroyRenderScript();
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event)
+    {
+        // break point at here
+        // this method doesn't work when 'extends View' include 'extends ScrollView'.
+        return super.onKeyDown(keyCode, event);
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        boolean ret = false;
+        int act = ev.getAction();
+        if (act == ev.ACTION_DOWN) {
+            mRender.onActionDown((int)ev.getX(), (int)ev.getY());
+            ret = true;
+        }
+        else if (act == ev.ACTION_MOVE) {
+            mRender.onActionMove((int)ev.getX(), (int)ev.getY());
+            ret = true;
+        }
+
+        return ret;
+    }
+}
+
+
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsRenderStates.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStates.java
new file mode 100644
index 0000000..391007e
--- /dev/null
+++ b/libs/rs/java/Samples/src/com/android/samples/RsRenderStates.java
@@ -0,0 +1,71 @@
+/*
+ * 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.android.samples;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class RsRenderStates extends Activity {
+
+    private RsRenderStatesView mView;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new RsRenderStatesView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onResume();
+        mView.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity looses focus
+        super.onPause();
+        mView.onPause();
+    }
+
+}
+
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
new file mode 100644
index 0000000..85c2557
--- /dev/null
+++ b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java
@@ -0,0 +1,352 @@
+/*
+ * 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.android.samples;
+
+import java.io.Writer;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.renderscript.*;
+import android.renderscript.ProgramStore.DepthFunc;
+import android.renderscript.Sampler.Value;
+import android.util.Log;
+
+
+public class RsRenderStatesRS {
+
+    int mWidth;
+    int mHeight;
+
+    public RsRenderStatesRS() {
+    }
+
+    public void init(RenderScriptGL rs, Resources res, int width, int height) {
+        mRS = rs;
+        mRes = res;
+        mWidth = width;
+        mHeight = height;
+        mOptionsARGB.inScaled = false;
+        mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
+        mMode = 0;
+        mMaxModes = 9;
+        initRS();
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+
+    private Sampler mLinearClamp;
+    private Sampler mLinearWrap;
+    private Sampler mMipLinearWrap;
+    private Sampler mNearestClamp;
+    private Sampler mMipLinearAniso8;
+    private Sampler mMipLinearAniso15;
+
+    private ProgramStore mProgStoreBlendNoneDepth;
+    private ProgramStore mProgStoreBlendNone;
+    private ProgramStore mProgStoreBlendAlpha;
+    private ProgramStore mProgStoreBlendAdd;
+
+    private ProgramFragment mProgFragmentTexture;
+    private ProgramFragment mProgFragmentColor;
+
+    private ProgramVertex mProgVertex;
+    private ProgramVertex.MatrixAllocation mPVA;
+
+    // Custom shaders
+    private ProgramVertex mProgVertexCustom;
+    private ProgramFragment mProgFragmentCustom;
+    private ProgramFragment mProgFragmentMultitex;
+    private ScriptField_VertexShaderConstants_s mVSConst;
+    private ScriptField_FragentShaderConstants_s mFSConst;
+
+    private ProgramRaster mCullBack;
+    private ProgramRaster mCullFront;
+    private ProgramRaster mCullNone;
+
+    private Allocation mTexTorus;
+    private Allocation mTexOpaque;
+    private Allocation mTexTransparent;
+    private Allocation mTexChecker;
+
+    private Mesh mMbyNMesh;
+    private Mesh mTorus;
+
+    Font mFontSans;
+    Font mFontSerif;
+    Font mFontSerifBold;
+    Font mFontSerifItalic;
+    Font mFontSerifBoldItalic;
+    Font mFontMono;
+    private Allocation mTextAlloc;
+
+    private ScriptC_rsrenderstates mScript;
+
+    private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
+
+    int mMode;
+    int mMaxModes;
+
+    public void onActionDown(int x, int y) {
+        mMode ++;
+        mMode = mMode % mMaxModes;
+        mScript.set_gDisplayMode(mMode);
+    }
+
+    private Mesh getMbyNMesh(float width, float height, int wResolution, int hResolution) {
+
+        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
+                                           2, Mesh.TriangleMeshBuilder.TEXTURE_0);
+
+        for (int y = 0; y <= hResolution; y++) {
+            final float normalizedY = (float)y / hResolution;
+            final float yOffset = (normalizedY - 0.5f) * height;
+            for (int x = 0; x <= wResolution; x++) {
+                float normalizedX = (float)x / wResolution;
+                float xOffset = (normalizedX - 0.5f) * width;
+                tmb.setTexture(normalizedX, normalizedY);
+                tmb.addVertex(xOffset, yOffset);
+             }
+        }
+
+        for (int y = 0; y < hResolution; y++) {
+            final int curY = y * (wResolution + 1);
+            final int belowY = (y + 1) * (wResolution + 1);
+            for (int x = 0; x < wResolution; x++) {
+                int curV = curY + x;
+                int belowV = belowY + x;
+                tmb.addTriangle(curV, belowV, curV + 1);
+                tmb.addTriangle(belowV, belowV + 1, curV + 1);
+            }
+        }
+
+        return tmb.create(true);
+    }
+
+    private void initProgramStore() {
+        // Use stock the stock program store object
+        mProgStoreBlendNoneDepth = ProgramStore.BLEND_NONE_DEPTH_TEST(mRS);
+        mProgStoreBlendNone = ProgramStore.BLEND_NONE_DEPTH_NO_DEPTH(mRS);
+
+        // Create a custom program store
+        ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
+        builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+        builder.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA,
+                             ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+        builder.setDitherEnable(false);
+        builder.setDepthMask(false);
+        mProgStoreBlendAlpha = builder.create();
+
+        mProgStoreBlendAdd = ProgramStore.BLEND_ADD_DEPTH_NO_DEPTH(mRS);
+
+        mScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth);
+        mScript.set_gProgStoreBlendNone(mProgStoreBlendNone);
+        mScript.set_gProgStoreBlendAlpha(mProgStoreBlendAlpha);
+        mScript.set_gProgStoreBlendAdd(mProgStoreBlendAdd);
+    }
+
+    private void initProgramFragment() {
+
+        ProgramFragment.Builder texBuilder = new ProgramFragment.Builder(mRS);
+        texBuilder.setTexture(ProgramFragment.Builder.EnvMode.REPLACE,
+                              ProgramFragment.Builder.Format.RGBA, 0);
+        mProgFragmentTexture = texBuilder.create();
+        mProgFragmentTexture.bindSampler(mLinearClamp, 0);
+
+        ProgramFragment.Builder colBuilder = new ProgramFragment.Builder(mRS);
+        colBuilder.setVaryingColor(false);
+        mProgFragmentColor = colBuilder.create();
+
+        mScript.set_gProgFragmentColor(mProgFragmentColor);
+        mScript.set_gProgFragmentTexture(mProgFragmentTexture);
+    }
+
+    private void initProgramVertex() {
+        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS);
+        mProgVertex = pvb.create();
+
+        mPVA = new ProgramVertex.MatrixAllocation(mRS);
+        mProgVertex.bindAllocation(mPVA);
+        mPVA.setupOrthoWindow(mWidth, mHeight);
+
+        mScript.set_gProgVertex(mProgVertex);
+    }
+
+    private void initCustomShaders() {
+        mVSConst = new ScriptField_VertexShaderConstants_s(mRS, 1);
+        mFSConst = new ScriptField_FragentShaderConstants_s(mRS, 1);
+
+        mScript.bind_gVSConstants(mVSConst);
+        mScript.bind_gFSConstants(mFSConst);
+
+        // Initialize the shader builder
+        ProgramVertex.ShaderBuilder pvbCustom = new ProgramVertex.ShaderBuilder(mRS);
+        // Specify the resource that contains the shader string
+        pvbCustom.setShader(mRes, R.raw.shaderv);
+        // Use a script field to spcify the input layout
+        pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS));
+        // Define the constant input layout
+        pvbCustom.addConstant(mVSConst.getAllocation().getType());
+        mProgVertexCustom = pvbCustom.create();
+        // Bind the source of constant data
+        mProgVertexCustom.bindConstants(mVSConst.getAllocation(), 0);
+
+        ProgramFragment.ShaderBuilder pfbCustom = new ProgramFragment.ShaderBuilder(mRS);
+        // Specify the resource that contains the shader string
+        pfbCustom.setShader(mRes, R.raw.shaderf);
+        //Tell the builder how many textures we have
+        pfbCustom.setTextureCount(1);
+        // Define the constant input layout
+        pfbCustom.addConstant(mFSConst.getAllocation().getType());
+        mProgFragmentCustom = pfbCustom.create();
+        // Bind the source of constant data
+        mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 0);
+
+        pfbCustom = new ProgramFragment.ShaderBuilder(mRS);
+        pfbCustom.setShader(mRes, R.raw.multitexf);
+        pfbCustom.setTextureCount(3);
+        mProgFragmentMultitex = pfbCustom.create();
+
+        mScript.set_gProgVertexCustom(mProgVertexCustom);
+        mScript.set_gProgFragmentCustom(mProgFragmentCustom);
+        mScript.set_gProgFragmentMultitex(mProgFragmentMultitex);
+    }
+
+    private Allocation loadTextureRGB(int id) {
+        final Allocation allocation = Allocation.createFromBitmapResource(mRS, mRes,
+                id, Element.RGB_565(mRS), true);
+        allocation.uploadToTexture(0);
+        return allocation;
+    }
+
+    private Allocation loadTextureARGB(int id) {
+        Bitmap b = BitmapFactory.decodeResource(mRes, id, mOptionsARGB);
+        final Allocation allocation = Allocation.createFromBitmap(mRS, b, Element.RGBA_8888(mRS), true);
+        allocation.uploadToTexture(0);
+        return allocation;
+    }
+
+    private void loadImages() {
+        mTexTorus = loadTextureRGB(R.drawable.torusmap);
+        mTexOpaque = loadTextureRGB(R.drawable.data);
+        mTexTransparent = loadTextureARGB(R.drawable.leaf);
+        mTexChecker = loadTextureRGB(R.drawable.checker);
+
+        mScript.set_gTexTorus(mTexTorus);
+        mScript.set_gTexOpaque(mTexOpaque);
+        mScript.set_gTexTransparent(mTexTransparent);
+        mScript.set_gTexChecker(mTexChecker);
+    }
+
+    private void initFonts() {
+        // Sans font by family name
+        mFontSans = Font.createFromFamily(mRS, mRes, "sans-serif", Font.Style.NORMAL, 8);
+        // Create font by file name
+        mFontSerif = Font.create(mRS, mRes, "DroidSerif-Regular.ttf", 8);
+        // Create fonts by family and style
+        mFontSerifBold = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD, 8);
+        mFontSerifItalic = Font.createFromFamily(mRS, mRes, "serif", Font.Style.ITALIC, 8);
+        mFontSerifBoldItalic = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
+        mFontMono = Font.createFromFamily(mRS, mRes, "mono", Font.Style.NORMAL, 8);
+
+        mTextAlloc = Allocation.createFromString(mRS, "String from allocation");
+
+        mScript.set_gFontSans(mFontSans);
+        mScript.set_gFontSerif(mFontSerif);
+        mScript.set_gFontSerifBold(mFontSerifBold);
+        mScript.set_gFontSerifItalic(mFontSerifItalic);
+        mScript.set_gFontSerifBoldItalic(mFontSerifBoldItalic);
+        mScript.set_gFontMono(mFontMono);
+        mScript.set_gTextAlloc(mTextAlloc);
+    }
+
+    private void initMesh() {
+        mMbyNMesh = getMbyNMesh(256, 256, 10, 10);
+        mScript.set_gMbyNMesh(mMbyNMesh);
+
+        FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus);
+        FileA3D.IndexEntry entry = model.getIndexEntry(0);
+        if(entry == null || entry.getClassID() != FileA3D.ClassID.MESH) {
+            Log.e("rs", "could not load model");
+        }
+        else {
+            mTorus = (Mesh)entry.getObject();
+            mScript.set_gTorusMesh(mTorus);
+        }
+    }
+
+    private void initSamplers() {
+        Sampler.Builder bs = new Sampler.Builder(mRS);
+        bs.setMin(Sampler.Value.LINEAR);
+        bs.setMag(Sampler.Value.LINEAR);
+        bs.setWrapS(Sampler.Value.WRAP);
+        bs.setWrapT(Sampler.Value.WRAP);
+        mLinearWrap = bs.create();
+
+        mLinearClamp = Sampler.CLAMP_LINEAR(mRS);
+        mNearestClamp = Sampler.CLAMP_NEAREST(mRS);
+        mMipLinearWrap = Sampler.WRAP_LINEAR_MIP_LINEAR(mRS);
+
+        bs = new Sampler.Builder(mRS);
+        bs.setMin(Sampler.Value.LINEAR_MIP_LINEAR);
+        bs.setMag(Sampler.Value.LINEAR);
+        bs.setWrapS(Sampler.Value.WRAP);
+        bs.setWrapT(Sampler.Value.WRAP);
+        bs.setAnisotropy(8.0f);
+        mMipLinearAniso8 = bs.create();
+        bs.setAnisotropy(15.0f);
+        mMipLinearAniso15 = bs.create();
+
+        mScript.set_gLinearClamp(mLinearClamp);
+        mScript.set_gLinearWrap(mLinearWrap);
+        mScript.set_gMipLinearWrap(mMipLinearWrap);
+        mScript.set_gMipLinearAniso8(mMipLinearAniso8);
+        mScript.set_gMipLinearAniso15(mMipLinearAniso15);
+        mScript.set_gNearestClamp(mNearestClamp);
+    }
+
+    private void initProgramRaster() {
+        mCullBack = ProgramRaster.CULL_BACK(mRS);
+        mCullFront = ProgramRaster.CULL_FRONT(mRS);
+        mCullNone = ProgramRaster.CULL_NONE(mRS);
+
+        mScript.set_gCullBack(mCullBack);
+        mScript.set_gCullFront(mCullFront);
+        mScript.set_gCullNone(mCullNone);
+    }
+
+    private void initRS() {
+
+        mScript = new ScriptC_rsrenderstates(mRS, mRes, R.raw.rsrenderstates);
+
+        initSamplers();
+        initProgramStore();
+        initProgramFragment();
+        initProgramVertex();
+        initFonts();
+        loadImages();
+        initMesh();
+        initProgramRaster();
+        initCustomShaders();
+
+        mRS.contextBindRootScript(mScript);
+    }
+}
+
+
+
diff --git a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesView.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesView.java
new file mode 100644
index 0000000..c434c09
--- /dev/null
+++ b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesView.java
@@ -0,0 +1,96 @@
+/*
+ * 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.android.samples;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class RsRenderStatesView extends RSSurfaceView {
+
+    public RsRenderStatesView(Context context) {
+        super(context);
+        //setFocusable(true);
+    }
+
+    private RenderScriptGL mRS;
+    private RsRenderStatesRS mRender;
+
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            sc.setDepth(16, 24);
+            mRS = createRenderScript(sc);
+            mRS.contextSetSurface(w, h, holder.getSurface());
+            mRender = new RsRenderStatesRS();
+            mRender.init(mRS, getResources(), w, h);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        if(mRS != null) {
+            mRS = null;
+            destroyRenderScript();
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event)
+    {
+        // break point at here
+        // this method doesn't work when 'extends View' include 'extends ScrollView'.
+        return super.onKeyDown(keyCode, event);
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        boolean ret = false;
+        int act = ev.getAction();
+        if (act == ev.ACTION_DOWN) {
+            mRender.onActionDown((int)ev.getX(), (int)ev.getY());
+            ret = true;
+        }
+
+        return ret;
+    }
+}
+
+
diff --git a/libs/rs/java/Samples/src/com/android/samples/rslist.rs b/libs/rs/java/Samples/src/com/android/samples/rslist.rs
new file mode 100644
index 0000000..01b37ab
--- /dev/null
+++ b/libs/rs/java/Samples/src/com/android/samples/rslist.rs
@@ -0,0 +1,73 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.samples)
+
+#include "rs_graphics.rsh"
+
+float gDY;
+
+rs_font gItalic;
+
+typedef struct ListAllocs_s {
+    rs_allocation text;
+} ListAllocs;
+
+ListAllocs *gList;
+
+void init() {
+    gDY = 0.0f;
+}
+
+int textPos = 0;
+
+int root(int launchID) {
+
+    rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    rsgClearDepth(1.0f);
+
+    textPos -= (int)gDY*2;
+    gDY *= 0.95;
+
+    rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f);
+    rsgBindFont(gItalic);
+    color(0.2, 0.2, 0.2, 0);
+
+    rs_allocation listAlloc = {0};
+    rsSetObject(&listAlloc, rsGetAllocation(gList));
+    int allocSize = rsAllocationGetDimX(listAlloc);
+
+    int width = rsgGetWidth();
+    int height = rsgGetHeight();
+
+    int itemHeight = 80;
+    int currentYPos = itemHeight + textPos;
+
+    for(int i = 0; i < allocSize; i ++) {
+        if(currentYPos - itemHeight > height) {
+            break;
+        }
+
+        if(currentYPos > 0) {
+            rsgDrawRect(0, currentYPos - 1, width, currentYPos, 0);
+            rsgDrawText(gList[i].text, 30, currentYPos - 32);
+        }
+        currentYPos += itemHeight;
+    }
+    rsClearObject(&listAlloc);
+
+    return 10;
+}
diff --git a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
new file mode 100644
index 0000000..8be35f8
--- /dev/null
+++ b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs
@@ -0,0 +1,583 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.samples)
+
+#include "rs_graphics.rsh"
+#include "shader_def.rsh"
+
+rs_program_vertex gProgVertex;
+rs_program_fragment gProgFragmentColor;
+rs_program_fragment gProgFragmentTexture;
+
+rs_program_store gProgStoreBlendNoneDepth;
+rs_program_store gProgStoreBlendNone;
+rs_program_store gProgStoreBlendAlpha;
+rs_program_store gProgStoreBlendAdd;
+
+rs_allocation gTexOpaque;
+rs_allocation gTexTorus;
+rs_allocation gTexTransparent;
+rs_allocation gTexChecker;
+
+rs_mesh gMbyNMesh;
+rs_mesh gTorusMesh;
+
+rs_font gFontSans;
+rs_font gFontSerif;
+rs_font gFontSerifBold;
+rs_font gFontSerifItalic;
+rs_font gFontSerifBoldItalic;
+rs_font gFontMono;
+rs_allocation gTextAlloc;
+
+int gDisplayMode;
+
+rs_sampler gLinearClamp;
+rs_sampler gLinearWrap;
+rs_sampler gMipLinearWrap;
+rs_sampler gMipLinearAniso8;
+rs_sampler gMipLinearAniso15;
+rs_sampler gNearestClamp;
+
+rs_program_raster gCullBack;
+rs_program_raster gCullFront;
+rs_program_raster gCullNone;
+
+// Custom vertex shader compunents
+VertexShaderConstants *gVSConstants;
+FragentShaderConstants *gFSConstants;
+// Export these out to easily set the inputs to shader
+VertexShaderInputs *gVSInputs;
+// Custom shaders we use for lighting
+rs_program_vertex gProgVertexCustom;
+rs_program_fragment gProgFragmentCustom;
+rs_program_fragment gProgFragmentMultitex;
+
+float gDt = 0;
+
+void init() {
+}
+
+void displayFontSamples() {
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    int yPos = 100;
+    rsgBindFont(gFontSans);
+    rsgDrawText("Sans font sample", 30, yPos);
+    yPos += 30;
+    rsgFontColor(0.5f, 0.9f, 0.5f, 1.0f);
+    rsgBindFont(gFontSerif);
+    rsgDrawText("Serif font sample", 30, yPos);
+    yPos += 30;
+    rsgFontColor(0.7f, 0.7f, 0.7f, 1.0f);
+    rsgBindFont(gFontSerifBold);
+    rsgDrawText("Serif Bold font sample", 30, yPos);
+    yPos += 30;
+    rsgFontColor(0.5f, 0.5f, 0.9f, 1.0f);
+    rsgBindFont(gFontSerifItalic);
+    rsgDrawText("Serif Italic font sample", 30, yPos);
+    yPos += 30;
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontSerifBoldItalic);
+    rsgDrawText("Serif Bold Italic font sample", 30, yPos);
+    yPos += 30;
+    rsgBindFont(gFontMono);
+    rsgDrawText("Monospace font sample", 30, yPos);
+    yPos += 50;
+
+    // Now use text metrics to center the text
+    uint width = rsgGetWidth();
+    uint height = rsgGetHeight();
+    int left = 0, right = 0, top = 0, bottom = 0;
+
+    rsgFontColor(0.9f, 0.9f, 0.95f, 1.0f);
+    rsgBindFont(gFontSerifBoldItalic);
+
+    rsgMeasureText(gTextAlloc, &left, &right, &top, &bottom);
+    int centeredPos = width / 2 - (right - left) / 2;
+    rsgDrawText(gTextAlloc, centeredPos, yPos);
+    yPos += 30;
+
+    const char* text = "Centered Text Sample";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    centeredPos = width / 2 - (right - left) / 2;
+    rsgDrawText(text, centeredPos, yPos);
+    yPos += 30;
+
+    rsgBindFont(gFontSans);
+    text = "More Centered Text Samples";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    centeredPos = width / 2 - (right - left) / 2;
+    rsgDrawText(text, centeredPos, yPos);
+    yPos += 30;
+
+    // Now draw bottom and top right aligned text
+    text = "Top-right aligned text";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    rsgDrawText(text, width - right, top);
+
+    text = "Top-left";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    rsgDrawText(text, -left, top);
+
+    text = "Bottom-right aligned text";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    rsgDrawText(text, width - right, height + bottom);
+
+}
+
+void bindProgramVertexOrtho() {
+    // Default vertex sahder
+    rsgBindProgramVertex(gProgVertex);
+    // Setup the projectioni matrix
+    rs_matrix4x4 proj;
+    rsMatrixLoadOrtho(&proj, 0, rsgGetWidth(), rsgGetHeight(), 0, -500, 500);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
+}
+
+void displayShaderSamples() {
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+
+    float startX = 0, startY = 0;
+    float width = 256, height = 256;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1,
+                         startX + width, startY + height, 0, 1, 1,
+                         startX + width, startY, 0, 1, 0);
+
+    startX = 200; startY = 0;
+    width = 128; height = 128;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1,
+                         startX + width, startY + height, 0, 1, 1,
+                         startX + width, startY, 0, 1, 0);
+
+    rsgBindProgramStore(gProgStoreBlendAlpha);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexTransparent);
+    startX = 0; startY = 200;
+    width = 128; height = 128;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1,
+                         startX + width, startY + height, 0, 1, 1,
+                         startX + width, startY, 0, 1, 0);
+
+    // Fragment program with simple color
+    rsgBindProgramFragment(gProgFragmentColor);
+    rsgProgramFragmentConstantColor(gProgFragmentColor, 0.9, 0.3, 0.3, 1);
+    rsgDrawRect(200, 300, 350, 450, 0);
+    rsgProgramFragmentConstantColor(gProgFragmentColor, 0.3, 0.9, 0.3, 1);
+    rsgDrawRect(50, 400, 400, 600, 0);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Texture shader", 10, 50);
+    rsgDrawText("Alpha-blended texture shader", 10, 280);
+    rsgDrawText("Flat color shader", 100, 450);
+}
+
+void displayBlendingSamples() {
+    int i;
+
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    rsgBindProgramFragment(gProgFragmentColor);
+
+    rsgBindProgramStore(gProgStoreBlendNone);
+    for(i = 0; i < 3; i ++) {
+        float iPlusOne = (float)(i + 1);
+        rsgProgramFragmentConstantColor(gProgFragmentColor,
+                                        0.1f*iPlusOne, 0.2f*iPlusOne, 0.3f*iPlusOne, 1);
+        float yPos = 150 * (float)i;
+        rsgDrawRect(0, yPos, 200, yPos + 200, 0);
+    }
+
+    rsgBindProgramStore(gProgStoreBlendAlpha);
+    for(i = 0; i < 3; i ++) {
+        float iPlusOne = (float)(i + 1);
+        rsgProgramFragmentConstantColor(gProgFragmentColor,
+                                        0.2f*iPlusOne, 0.3f*iPlusOne, 0.1f*iPlusOne, 0.5);
+        float yPos = 150 * (float)i;
+        rsgDrawRect(150, yPos, 350, yPos + 200, 0);
+    }
+
+    rsgBindProgramStore(gProgStoreBlendAdd);
+    for(i = 0; i < 3; i ++) {
+        float iPlusOne = (float)(i + 1);
+        rsgProgramFragmentConstantColor(gProgFragmentColor,
+                                        0.3f*iPlusOne, 0.1f*iPlusOne, 0.2f*iPlusOne, 0.5);
+        float yPos = 150 * (float)i;
+        rsgDrawRect(300, yPos, 500, yPos + 200, 0);
+    }
+
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("No Blending", 10, 50);
+    rsgDrawText("Alpha Blending", 160, 150);
+    rsgDrawText("Additive Blending", 320, 250);
+
+}
+
+void displayMeshSamples() {
+
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadTranslate(&matrix, 128, 128, 0);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+
+    rsgDrawMesh(gMbyNMesh);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("User gen 10 by 10 grid mesh", 10, 250);
+}
+
+void displayTextureSamplers() {
+
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque);
+
+    // Linear clamp
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    float startX = 0, startY = 0;
+    float width = 300, height = 300;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1.1,
+                         startX + width, startY + height, 0, 1.1, 1.1,
+                         startX + width, startY, 0, 1.1, 0);
+
+    // Linear Wrap
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearWrap);
+    startX = 0; startY = 300;
+    width = 300; height = 300;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1.1,
+                         startX + width, startY + height, 0, 1.1, 1.1,
+                         startX + width, startY, 0, 1.1, 0);
+
+    // Nearest
+    rsgBindSampler(gProgFragmentTexture, 0, gNearestClamp);
+    startX = 300; startY = 0;
+    width = 300; height = 300;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1.1,
+                         startX + width, startY + height, 0, 1.1, 1.1,
+                         startX + width, startY, 0, 1.1, 0);
+
+    rsgBindSampler(gProgFragmentTexture, 0, gMipLinearWrap);
+    startX = 300; startY = 300;
+    width = 300; height = 300;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1.5,
+                         startX + width, startY + height, 0, 1.5, 1.5,
+                         startX + width, startY, 0, 1.5, 0);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Filtering: linear clamp", 10, 290);
+    rsgDrawText("Filtering: linear wrap", 10, 590);
+    rsgDrawText("Filtering: nearest clamp", 310, 290);
+    rsgDrawText("Filtering: miplinear wrap", 310, 590);
+}
+
+float gTorusRotation = 0;
+
+void displayCullingSamples() {
+    rsgBindProgramVertex(gProgVertex);
+    // Setup the projectioni matrix with 60 degree field of view
+    rs_matrix4x4 proj;
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNoneDepth);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentTexture, 0, gTexTorus);
+
+    // Aplly a rotation to our mesh
+    gTorusRotation += 50.0f * gDt;
+    if(gTorusRotation > 360.0f) {
+        gTorusRotation -= 360.0f;
+    }
+
+    rs_matrix4x4 matrix;
+    // Position our model on the screen
+    rsMatrixLoadTranslate(&matrix, -2.0f, 0.0f, -10.0f);
+    rsMatrixRotate(&matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+    // Use front face culling
+    rsgBindProgramRaster(gCullFront);
+    rsgDrawMesh(gTorusMesh);
+
+    rsMatrixLoadTranslate(&matrix, 2.0f, 0.0f, -10.0f);
+    rsMatrixRotate(&matrix, gTorusRotation, 1.0f, 0.0f, 0.0f);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+    // Use back face culling
+    rsgBindProgramRaster(gCullBack);
+    rsgDrawMesh(gTorusMesh);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Displaying mesh front/back face culling", 10, rsgGetHeight() - 10);
+}
+
+float gLight0Rotation = 0;
+float gLight1Rotation = 0;
+
+void setupCustomShaderLights() {
+    float4 light0Pos = {-5.0f, 5.0f, -10.0f, 1.0f};
+    float4 light1Pos = {2.0f, 5.0f, 15.0f, 1.0f};
+    float3 light0DiffCol = {0.9f, 0.7f, 0.7f};
+    float3 light0SpecCol = {0.9f, 0.6f, 0.6f};
+    float3 light1DiffCol = {0.5f, 0.5f, 0.9f};
+    float3 light1SpecCol = {0.5f, 0.5f, 0.9f};
+
+    gLight0Rotation += 50.0f * gDt;
+    if(gLight0Rotation > 360.0f) {
+        gLight0Rotation -= 360.0f;
+    }
+    gLight1Rotation -= 50.0f * gDt;
+    if(gLight1Rotation > 360.0f) {
+        gLight1Rotation -= 360.0f;
+    }
+
+    rs_matrix4x4 l0Mat;
+    rsMatrixLoadRotate(&l0Mat, gLight0Rotation, 1.0f, 0.0f, 0.0f);
+    light0Pos = rsMatrixMultiply(&l0Mat, light0Pos);
+    rs_matrix4x4 l1Mat;
+    rsMatrixLoadRotate(&l1Mat, gLight1Rotation, 0.0f, 0.0f, 1.0f);
+    light1Pos = rsMatrixMultiply(&l1Mat, light1Pos);
+
+    // Set light 0 properties
+    gVSConstants->light0_Posision.x = light0Pos.x;
+    gVSConstants->light0_Posision.y = light0Pos.y;
+    gVSConstants->light0_Posision.z = light0Pos.z;
+    gVSConstants->light0_Diffuse = 1.0f;
+    gVSConstants->light0_Specular = 0.5f;
+    gVSConstants->light0_CosinePower = 40.0f;
+    // Set light 1 properties
+    gVSConstants->light1_Posision.x = light1Pos.x;
+    gVSConstants->light1_Posision.y = light1Pos.y;
+    gVSConstants->light1_Posision.z = light1Pos.z;
+    gVSConstants->light1_Diffuse = 1.0f;
+    gVSConstants->light1_Specular = 0.7f;
+    gVSConstants->light1_CosinePower = 50.0f;
+    rsAllocationMarkDirty(rsGetAllocation(gVSConstants));
+
+    // Update fragmetn shader constants
+    // Set light 0 colors
+    gFSConstants->light0_DiffuseColor = light0DiffCol;
+    gFSConstants->light0_SpecularColor = light0SpecCol;
+    // Set light 1 colors
+    gFSConstants->light1_DiffuseColor = light1DiffCol;
+    gFSConstants->light1_SpecularColor = light1SpecCol;
+    rsAllocationMarkDirty(rsGetAllocation(gFSConstants));
+}
+
+void displayCustomShaderSamples() {
+
+    // Update vertex shader constants
+    // Load model matrix
+    // Aplly a rotation to our mesh
+    gTorusRotation += 50.0f * gDt;
+    if(gTorusRotation > 360.0f) {
+        gTorusRotation -= 360.0f;
+    }
+
+    // Position our model on the screen
+    rsMatrixLoadTranslate(&gVSConstants->model, 0.0f, 0.0f, -10.0f);
+    rsMatrixRotate(&gVSConstants->model, gTorusRotation, 1.0f, 0.0f, 0.0f);
+    rsMatrixRotate(&gVSConstants->model, gTorusRotation, 0.0f, 0.0f, 1.0f);
+    // Setup the projectioni matrix
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rsMatrixLoadPerspective(&gVSConstants->proj, 30.0f, aspect, 0.1f, 100.0f);
+    setupCustomShaderLights();
+
+    rsgBindProgramVertex(gProgVertexCustom);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNoneDepth);
+    rsgBindProgramFragment(gProgFragmentCustom);
+    rsgBindSampler(gProgFragmentCustom, 0, gLinearClamp);
+    rsgBindTexture(gProgFragmentCustom, 0, gTexTorus);
+
+    // Use back face culling
+    rsgBindProgramRaster(gCullBack);
+    rsgDrawMesh(gTorusMesh);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Custom shader sample", 10, rsgGetHeight() - 10);
+}
+
+void displayMultitextureSample() {
+    bindProgramVertexOrtho();
+    rs_matrix4x4 matrix;
+    rsMatrixLoadIdentity(&matrix);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentMultitex);
+    rsgBindSampler(gProgFragmentMultitex, 0, gLinearClamp);
+    rsgBindSampler(gProgFragmentMultitex, 1, gLinearWrap);
+    rsgBindSampler(gProgFragmentMultitex, 2, gLinearClamp);
+    rsgBindTexture(gProgFragmentMultitex, 0, gTexChecker);
+    rsgBindTexture(gProgFragmentMultitex, 1, gTexTorus);
+    rsgBindTexture(gProgFragmentMultitex, 2, gTexTransparent);
+
+    float startX = 0, startY = 0;
+    float width = 256, height = 256;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 1,
+                         startX + width, startY + height, 0, 1, 1,
+                         startX + width, startY, 0, 1, 0);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    rsgDrawText("Custom shader with multitexturing", 10, 280);
+}
+
+float gAnisoTime = 0.0f;
+uint anisoMode = 0;
+void displayAnisoSample() {
+
+    gAnisoTime += gDt;
+
+    rsgBindProgramVertex(gProgVertex);
+    float aspect = (float)rsgGetWidth() / (float)rsgGetHeight();
+    rs_matrix4x4 proj;
+    rsMatrixLoadPerspective(&proj, 30.0f, aspect, 0.1f, 100.0f);
+    rsgProgramVertexLoadProjectionMatrix(&proj);
+
+    rs_matrix4x4 matrix;
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendNone);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsMatrixLoadTranslate(&matrix, 0.0f, 0.0f, -10.0f);
+    rsMatrixRotate(&matrix, -80, 1.0f, 0.0f, 0.0f);
+    rsgProgramVertexLoadModelMatrix(&matrix);
+
+    rsgBindProgramRaster(gCullNone);
+
+    rsgBindTexture(gProgFragmentTexture, 0, gTexChecker);
+
+    if(gAnisoTime >= 5.0f) {
+        gAnisoTime = 0.0f;
+        anisoMode ++;
+        anisoMode = anisoMode % 3;
+    }
+
+    if(anisoMode == 0) {
+        rsgBindSampler(gProgFragmentTexture, 0, gMipLinearAniso8);
+    }
+    else if(anisoMode == 1) {
+        rsgBindSampler(gProgFragmentTexture, 0, gMipLinearAniso15);
+    }
+    else {
+        rsgBindSampler(gProgFragmentTexture, 0, gMipLinearWrap);
+    }
+
+    float startX = -15;
+    float startY = -15;
+    float width = 30;
+    float height = 30;
+    rsgDrawQuadTexCoords(startX, startY, 0, 0, 0,
+                         startX, startY + height, 0, 0, 10,
+                         startX + width, startY + height, 0, 10, 10,
+                         startX + width, startY, 0, 10, 0);
+
+    rsgBindProgramRaster(gCullBack);
+
+    rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
+    rsgBindFont(gFontMono);
+    if(anisoMode == 0) {
+        rsgDrawText("Anisotropic filtering 8", 10, 40);
+    }
+    else if(anisoMode == 1) {
+        rsgDrawText("Anisotropic filtering 15", 10, 40);
+    }
+    else {
+        rsgDrawText("Miplinear filtering", 10, 40);
+    }
+}
+
+int root(int launchID) {
+
+    gDt = rsGetDt();
+
+    rsgClearColor(0.2f, 0.2f, 0.2f, 0.0f);
+    rsgClearDepth(1.0f);
+
+    switch(gDisplayMode) {
+    case 0:
+        displayFontSamples();
+        break;
+    case 1:
+        displayShaderSamples();
+        break;
+    case 2:
+        displayBlendingSamples();
+        break;
+    case 3:
+        displayMeshSamples();
+        break;
+    case 4:
+        displayTextureSamplers();
+        break;
+    case 5:
+        displayCullingSamples();
+        break;
+    case 6:
+        displayCustomShaderSamples();
+        break;
+    case 7:
+        displayMultitextureSample();
+        break;
+    case 8:
+        displayAnisoSample();
+        break;
+    }
+
+    return 10;
+}
diff --git a/libs/rs/java/Samples/src/com/android/samples/shader_def.rsh b/libs/rs/java/Samples/src/com/android/samples/shader_def.rsh
new file mode 100644
index 0000000..e3f6206
--- /dev/null
+++ b/libs/rs/java/Samples/src/com/android/samples/shader_def.rsh
@@ -0,0 +1,47 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.samples)
+
+typedef struct VertexShaderConstants_s {
+    rs_matrix4x4 model;
+    rs_matrix4x4 proj;
+    float3 light0_Posision;
+    float light0_Diffuse;
+    float light0_Specular;
+    float light0_CosinePower;
+
+    float3 light1_Posision;
+    float light1_Diffuse;
+    float light1_Specular;
+    float light1_CosinePower;
+} VertexShaderConstants;
+
+typedef struct FragentShaderConstants_s {
+    float3 light0_DiffuseColor;
+    float3 light0_SpecularColor;
+
+    float3 light1_DiffuseColor;
+    float3 light1_SpecularColor;
+
+} FragentShaderConstants;
+
+typedef struct VertexShaderInputs_s {
+    float4 position;
+    float3 normal;
+    float2 texture0;
+} VertexShaderInputs;
+
diff --git a/libs/rs/java/tests/Android.mk b/libs/rs/java/tests/Android.mk
new file mode 100644
index 0000000..6c992d5
--- /dev/null
+++ b/libs/rs/java/tests/Android.mk
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := RSTest
+
+include $(BUILD_PACKAGE)
+
+endif
diff --git a/libs/rs/java/tests/AndroidManifest.xml b/libs/rs/java/tests/AndroidManifest.xml
new file mode 100644
index 0000000..b660398
--- /dev/null
+++ b/libs/rs/java/tests/AndroidManifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.rs.test">
+    <application 
+        android:label="_RS_Test"
+        android:icon="@drawable/test_pattern">
+        <activity android:name="RSTest"
+                  android:screenOrientation="portrait">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/libs/rs/java/tests/res/drawable/test_pattern.png b/libs/rs/java/tests/res/drawable/test_pattern.png
new file mode 100644
index 0000000..e7d1455
--- /dev/null
+++ b/libs/rs/java/tests/res/drawable/test_pattern.png
Binary files differ
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTest.java b/libs/rs/java/tests/src/com/android/rs/test/RSTest.java
new file mode 100644
index 0000000..c264649
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.android.rs.test;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class RSTest extends Activity {
+    //EventListener mListener = new EventListener();
+
+    private static final String LOG_TAG = "libRS_jni";
+    private static final boolean DEBUG  = false;
+    private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+
+    private RSTestView mView;
+
+    // get the current looper (from your Activity UI thread for instance
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Create our Preview view and set it as the content of our
+        // Activity
+        mView = new RSTestView(this);
+        setContentView(mView);
+    }
+
+    @Override
+    protected void onResume() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity loses focus
+        super.onResume();
+        mView.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        // Ideally a game should implement onResume() and onPause()
+        // to take appropriate action when the activity loses focus
+        super.onPause();
+        mView.onPause();
+    }
+
+    static void log(String message) {
+        if (LOG_ENABLED) {
+            Log.v(LOG_TAG, message);
+        }
+    }
+
+
+}
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
new file mode 100644
index 0000000..8054779
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java
@@ -0,0 +1,201 @@
+/*
+ * 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.android.rs.test;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.ListIterator;
+import java.util.Timer;
+import java.util.TimerTask;
+
+
+public class RSTestCore {
+    int mWidth;
+    int mHeight;
+
+    public RSTestCore() {
+    }
+
+    private Resources mRes;
+    private RenderScriptGL mRS;
+
+    private Font mFont;
+    ScriptField_ListAllocs_s mListAllocs;
+    int mLastX;
+    int mLastY;
+    private ScriptC_rslist mScript;
+
+    private ArrayList<UnitTest> unitTests;
+    private ListIterator<UnitTest> test_iter;
+    private UnitTest activeTest;
+    private boolean stopTesting;
+
+    /* Periodic timer for ensuring future tests get scheduled */
+    private Timer mTimer;
+    public static final int RS_TIMER_PERIOD = 100;
+
+    public void init(RenderScriptGL rs, Resources res, int width, int height) {
+        mRS = rs;
+        mRes = res;
+        mWidth = width;
+        mHeight = height;
+        stopTesting = false;
+
+        mScript = new ScriptC_rslist(mRS, mRes, R.raw.rslist);
+
+        unitTests = new ArrayList<UnitTest>();
+
+        unitTests.add(new UT_primitives(this, mRes));
+        unitTests.add(new UT_rsdebug(this, mRes));
+        unitTests.add(new UT_rstypes(this, mRes));
+        unitTests.add(new UT_fp_mad(this, mRes));
+        /*
+        unitTests.add(new UnitTest(null, "<Pass>", 1));
+        unitTests.add(new UnitTest());
+        unitTests.add(new UnitTest(null, "<Fail>", -1));
+
+        for (int i = 0; i < 20; i++) {
+            unitTests.add(new UnitTest(null, "<Pass>", 1));
+        }
+        */
+
+        UnitTest [] uta = new UnitTest[unitTests.size()];
+        uta = unitTests.toArray(uta);
+
+        mListAllocs = new ScriptField_ListAllocs_s(mRS, uta.length);
+        for (int i = 0; i < uta.length; i++) {
+            ScriptField_ListAllocs_s.Item listElem = new ScriptField_ListAllocs_s.Item();
+            listElem.text = Allocation.createFromString(mRS, uta[i].name);
+            listElem.result = uta[i].result;
+            mListAllocs.set(listElem, i, false);
+            uta[i].setItem(listElem);
+        }
+
+        mListAllocs.copyAll();
+
+        mScript.bind_gList(mListAllocs);
+
+        mFont = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD, 8);
+        mScript.set_gFont(mFont);
+
+        mRS.contextBindRootScript(mScript);
+
+        test_iter = unitTests.listIterator();
+        refreshTestResults(); /* Kick off the first test */
+
+        TimerTask pTask = new TimerTask() {
+            public void run() {
+                refreshTestResults();
+            }
+        };
+
+        mTimer = new Timer();
+        mTimer.schedule(pTask, RS_TIMER_PERIOD, RS_TIMER_PERIOD);
+    }
+
+    public void checkAndRunNextTest() {
+        if (activeTest != null) {
+            if (!activeTest.isAlive()) {
+                /* Properly clean up on our last test */
+                try {
+                    activeTest.join();
+                }
+                catch (InterruptedException e) {
+                }
+                activeTest = null;
+            }
+        }
+
+        if (!stopTesting && activeTest == null) {
+            if (test_iter.hasNext()) {
+                activeTest = test_iter.next();
+                activeTest.start();
+                /* This routine will only get called once when a new test
+                 * should start running. The message handler in UnitTest.java
+                 * ensures this. */
+            }
+            else {
+                if (mTimer != null) {
+                    mTimer.cancel();
+                    mTimer.purge();
+                    mTimer = null;
+                }
+            }
+        }
+    }
+
+    public void refreshTestResults() {
+        checkAndRunNextTest();
+
+        if (mListAllocs != null && mScript != null && mRS != null) {
+            mListAllocs.copyAll();
+
+            mScript.bind_gList(mListAllocs);
+            mRS.contextBindRootScript(mScript);
+        }
+    }
+
+    public void cleanup() {
+        stopTesting = true;
+        UnitTest t = activeTest;
+
+        /* Stop periodic refresh of testing */
+        if (mTimer != null) {
+            mTimer.cancel();
+            mTimer.purge();
+            mTimer = null;
+        }
+
+        /* Wait to exit until we finish the current test */
+        if (t != null) {
+            try {
+                t.join();
+            }
+            catch (InterruptedException e) {
+            }
+            t = null;
+        }
+
+    }
+
+    public void newTouchPosition(float x, float y, float pressure, int id) {
+    }
+
+    public void onActionDown(int x, int y) {
+        mScript.set_gDY(0.0f);
+        mLastX = x;
+        mLastY = y;
+        refreshTestResults();
+    }
+
+    public void onActionMove(int x, int y) {
+        int dx = mLastX - x;
+        int dy = mLastY - y;
+
+        if (Math.abs(dy) <= 2) {
+            dy = 0;
+        }
+
+        mScript.set_gDY(dy);
+
+        mLastX = x;
+        mLastY = y;
+        refreshTestResults();
+    }
+}
diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java
new file mode 100644
index 0000000..c65f8c6
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java
@@ -0,0 +1,96 @@
+/*
+ * 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.android.rs.test;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+import android.renderscript.RenderScriptGL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class RSTestView extends RSSurfaceView {
+
+    public RSTestView(Context context) {
+        super(context);
+        //setFocusable(true);
+    }
+
+    private RenderScriptGL mRS;
+    private RSTestCore mRender;
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        super.surfaceChanged(holder, format, w, h);
+        if (mRS == null) {
+            RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
+            mRS = createRenderScript(sc);
+            mRS.contextSetSurface(w, h, holder.getSurface());
+            mRender = new RSTestCore();
+            mRender.init(mRS, getResources(), w, h);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        if(mRS != null) {
+            mRender.cleanup();
+            mRS = null;
+            destroyRenderScript();
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event)
+    {
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev)
+    {
+        boolean ret = false;
+        int act = ev.getAction();
+        if (act == ev.ACTION_DOWN) {
+            mRender.onActionDown((int)ev.getX(), (int)ev.getY());
+            ret = true;
+        }
+        else if (act == ev.ACTION_MOVE) {
+            mRender.onActionMove((int)ev.getX(), (int)ev.getY());
+            ret = true;
+        }
+
+        return ret;
+    }
+}
+
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java b/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java
new file mode 100644
index 0000000..e2cff00
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.test;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class UT_fp_mad extends UnitTest {
+    private Resources mRes;
+
+    protected UT_fp_mad(RSTestCore rstc, Resources res) {
+        super(rstc, "Fp_Mad");
+        mRes = res;
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create();
+        ScriptC_fp_mad s = new ScriptC_fp_mad(pRS, mRes, R.raw.fp_mad);
+        pRS.mMessageCallback = mRsMessage;
+        s.invoke_fp_mad_test(0, 0);
+        pRS.finish();
+        waitForMessage();
+        pRS.destroy();
+    }
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java b/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java
new file mode 100644
index 0000000..2da43fc
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.test;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class UT_primitives extends UnitTest {
+    private Resources mRes;
+
+    protected UT_primitives(RSTestCore rstc, Resources res) {
+        super(rstc, "Primitives");
+        mRes = res;
+    }
+
+    private boolean initializeGlobals(ScriptC_primitives s) {
+        float pF = s.get_floatTest();
+        if (pF != 1.99f) {
+            return false;
+        }
+        s.set_floatTest(2.99f);
+
+        double pD = s.get_doubleTest();
+        if (pD != 2.05) {
+            return false;
+        }
+        s.set_doubleTest(3.05);
+
+        byte pC = s.get_charTest();
+        if (pC != -8) {
+            return false;
+        }
+        s.set_charTest((byte)-16);
+
+        short pS = s.get_shortTest();
+        if (pS != -16) {
+            return false;
+        }
+        s.set_shortTest((short)-32);
+
+        int pI = s.get_intTest();
+        if (pI != -32) {
+            return false;
+        }
+        s.set_intTest(-64);
+
+        long pL = s.get_longTest();
+        if (pL != 17179869184l) {
+            return false;
+        }
+        s.set_longTest(17179869185l);
+
+        long puL = s.get_ulongTest();
+        if (puL != 4611686018427387904L) {
+            return false;
+        }
+        s.set_ulongTest(4611686018427387903L);
+
+
+        long pLL = s.get_longlongTest();
+        if (pLL != 68719476736L) {
+            return false;
+        }
+        s.set_longlongTest(68719476735L);
+
+        long pu64 = s.get_uint64_tTest();
+        if (pu64 != 117179869184l) {
+            return false;
+        }
+        s.set_uint64_tTest(117179869185l);
+
+        return true;
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create();
+        ScriptC_primitives s = new ScriptC_primitives(pRS, mRes, R.raw.primitives);
+        pRS.mMessageCallback = mRsMessage;
+        if (!initializeGlobals(s)) {
+            // initializeGlobals failed
+            result = -1;
+        } else {
+            s.invoke_primitives_test(0, 0);
+            pRS.finish();
+            waitForMessage();
+        }
+        pRS.destroy();
+    }
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_rsdebug.java b/libs/rs/java/tests/src/com/android/rs/test/UT_rsdebug.java
new file mode 100644
index 0000000..7ad77c7
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/UT_rsdebug.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.test;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class UT_rsdebug extends UnitTest {
+    private Resources mRes;
+
+    protected UT_rsdebug(RSTestCore rstc, Resources res) {
+        super(rstc, "rsDebug");
+        mRes = res;
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create();
+        ScriptC_rsdebug s = new ScriptC_rsdebug(pRS, mRes, R.raw.rsdebug);
+        pRS.mMessageCallback = mRsMessage;
+        s.invoke_test_rsdebug(0, 0);
+        pRS.finish();
+        waitForMessage();
+        pRS.destroy();
+    }
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_rstypes.java b/libs/rs/java/tests/src/com/android/rs/test/UT_rstypes.java
new file mode 100644
index 0000000..55f3746
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/UT_rstypes.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.test;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class UT_rstypes extends UnitTest {
+    private Resources mRes;
+
+    protected UT_rstypes(RSTestCore rstc, Resources res) {
+        super(rstc, "rsTypes");
+        mRes = res;
+    }
+
+    public void run() {
+        RenderScript pRS = RenderScript.create();
+        ScriptC_rstypes s = new ScriptC_rstypes(pRS, mRes, R.raw.rstypes, true);
+        pRS.mMessageCallback = mRsMessage;
+        s.invoke_test_rstypes(0, 0);
+        pRS.finish();
+        waitForMessage();
+        pRS.destroy();
+    }
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
new file mode 100644
index 0000000..90bb8a3
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.test;
+import android.renderscript.RenderScript.RSMessage;
+import android.util.Log;
+
+public class UnitTest extends Thread {
+    public String name;
+    public int result;
+    private ScriptField_ListAllocs_s.Item mItem;
+    private RSTestCore mRSTC;
+    private boolean msgHandled;
+
+    /* These constants must match those in shared.rsh */
+    public static final int RS_MSG_TEST_PASSED = 100;
+    public static final int RS_MSG_TEST_FAILED = 101;
+
+    private static int numTests = 0;
+    public int testID;
+
+    protected UnitTest(RSTestCore rstc, String n, int initResult) {
+        super();
+        mRSTC = rstc;
+        name = n;
+        msgHandled = false;
+        result = initResult;
+        testID = numTests++;
+    }
+
+    protected UnitTest(RSTestCore rstc, String n) {
+        this(rstc, n, 0);
+    }
+
+    protected UnitTest(RSTestCore rstc) {
+        this (rstc, "<Unknown>");
+    }
+
+    protected UnitTest() {
+        this (null);
+    }
+
+    protected RSMessage mRsMessage = new RSMessage() {
+        public void run() {
+            if (result == 0) {
+                switch (mID) {
+                    case RS_MSG_TEST_PASSED:
+                        result = 1;
+                        break;
+                    case RS_MSG_TEST_FAILED:
+                        result = -1;
+                        break;
+                    default:
+                        android.util.Log.v("RenderScript", "Unit test got unexpected message");
+                        return;
+                }
+            }
+
+            if (mItem != null) {
+                mItem.result = result;
+                msgHandled = true;
+                try {
+                    mRSTC.refreshTestResults();
+                }
+                catch (IllegalStateException e) {
+                    /* Ignore the case where our message receiver has been
+                       disconnected. This happens when we leave the application
+                       before it finishes running all of the unit tests. */
+                }
+            }
+        }
+    };
+
+    public void waitForMessage() {
+        while (!msgHandled) {
+            yield();
+        }
+    }
+
+    public void setItem(ScriptField_ListAllocs_s.Item item) {
+        mItem = item;
+    }
+
+    public void run() {
+        /* This method needs to be implemented for each subclass */
+        if (mRSTC != null) {
+            mRSTC.refreshTestResults();
+        }
+    }
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
new file mode 100644
index 0000000..066fab8
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs
@@ -0,0 +1,175 @@
+#include "shared.rsh"
+
+const int TEST_COUNT = 1;
+
+#pragma rs export_func(fp_mad_test)
+
+static float data_f1[1025];
+static float4 data_f4[1025];
+
+static void test_mad4(uint32_t index) {
+    start();
+
+    float total = 0;
+    // Do ~1 billion ops
+    for (int ct=0; ct < 1000 * (1000 / 80); ct++) {
+        for (int i=0; i < (1000); i++) {
+            data_f4[i] = (data_f4[i] * 0.02f +
+                          data_f4[i+1] * 0.04f +
+                          data_f4[i+2] * 0.05f +
+                          data_f4[i+3] * 0.1f +
+                          data_f4[i+4] * 0.2f +
+                          data_f4[i+5] * 0.2f +
+                          data_f4[i+6] * 0.1f +
+                          data_f4[i+7] * 0.05f +
+                          data_f4[i+8] * 0.04f +
+                          data_f4[i+9] * 0.02f + 1.f);
+        }
+    }
+
+    float time = end(index);
+    rsDebug("fp_mad4 M ops", 1000.f / time);
+}
+
+static void test_mad(uint32_t index) {
+    start();
+
+    float total = 0;
+    // Do ~1 billion ops
+    for (int ct=0; ct < 1000 * (1000 / 20); ct++) {
+        for (int i=0; i < (1000); i++) {
+            data_f1[i] = (data_f1[i] * 0.02f +
+                          data_f1[i+1] * 0.04f +
+                          data_f1[i+2] * 0.05f +
+                          data_f1[i+3] * 0.1f +
+                          data_f1[i+4] * 0.2f +
+                          data_f1[i+5] * 0.2f +
+                          data_f1[i+6] * 0.1f +
+                          data_f1[i+7] * 0.05f +
+                          data_f1[i+8] * 0.04f +
+                          data_f1[i+9] * 0.02f + 1.f);
+        }
+    }
+
+    float time = end(index);
+    rsDebug("fp_mad M ops", 1000.f / time);
+}
+
+static void test_norm(uint32_t index) {
+    start();
+
+    float total = 0;
+    // Do ~10 M ops
+    for (int ct=0; ct < 1000 * 10; ct++) {
+        for (int i=0; i < (1000); i++) {
+            data_f4[i] = normalize(data_f4[i]);
+        }
+    }
+
+    float time = end(index);
+    rsDebug("fp_norm M ops", 10.f / time);
+}
+
+static void test_sincos4(uint32_t index) {
+    start();
+
+    float total = 0;
+    // Do ~10 M ops
+    for (int ct=0; ct < 1000 * 10 / 4; ct++) {
+        for (int i=0; i < (1000); i++) {
+            data_f4[i] = sin(data_f4[i]) * cos(data_f4[i]);
+        }
+    }
+
+    float time = end(index);
+    rsDebug("fp_sincos4 M ops", 10.f / time);
+}
+
+static void test_sincos(uint32_t index) {
+    start();
+
+    float total = 0;
+    // Do ~10 M ops
+    for (int ct=0; ct < 1000 * 10; ct++) {
+        for (int i=0; i < (1000); i++) {
+            data_f1[i] = sin(data_f1[i]) * cos(data_f1[i]);
+        }
+    }
+
+    float time = end(index);
+    rsDebug("fp_sincos M ops", 10.f / time);
+}
+
+static void test_clamp(uint32_t index) {
+    start();
+
+    // Do ~100 M ops
+    for (int ct=0; ct < 1000 * 100; ct++) {
+        for (int i=0; i < (1000); i++) {
+            data_f1[i] = clamp(data_f1[i], -1.f, 1.f);
+        }
+    }
+
+    float time = end(index);
+    rsDebug("fp_clamp M ops", 100.f / time);
+
+    start();
+    // Do ~100 M ops
+    for (ct=0; ct < 1000 * 100; ct++) {
+        for (int i=0; i < (1000); i++) {
+            if (data_f1[i] < -1.f) data_f1[i] = -1.f;
+            if (data_f1[i] > -1.f) data_f1[i] = 1.f;
+        }
+    }
+
+    time = end(index);
+    rsDebug("fp_clamp ref M ops", 100.f / time);
+}
+
+static void test_clamp4(uint32_t index) {
+    start();
+
+    float total = 0;
+    // Do ~100 M ops
+    for (int ct=0; ct < 1000 * 100 /4; ct++) {
+        for (int i=0; i < (1000); i++) {
+            data_f4[i] = clamp(data_f4[i], -1.f, 1.f);
+        }
+    }
+
+    float time = end(index);
+    rsDebug("fp_clamp4 M ops", 100.f / time);
+}
+
+void fp_mad_test(uint32_t index, int test_num) {
+    for (int x=0; x < 1025; x++) {
+        data_f1[x] = (x & 0xf) * 0.1f;
+        data_f4[x].x = (x & 0xf) * 0.1f;
+        data_f4[x].y = (x & 0xf0) * 0.1f;
+        data_f4[x].z = (x & 0x33) * 0.1f;
+        data_f4[x].w = (x & 0x77) * 0.1f;
+    }
+
+    test_mad4(index);
+    test_mad(index);
+
+    for (x=0; x < 1025; x++) {
+        data_f1[x] = (x & 0xf) * 0.1f + 1.f;
+        data_f4[x].x = (x & 0xf) * 0.1f + 1.f;
+        data_f4[x].y = (x & 0xf0) * 0.1f + 1.f;
+        data_f4[x].z = (x & 0x33) * 0.1f + 1.f;
+        data_f4[x].w = (x & 0x77) * 0.1f + 1.f;
+    }
+
+    test_norm(index);
+    test_sincos4(index);
+    test_sincos(index);
+    test_clamp4(index);
+    test_clamp(index);
+
+    // TODO Actually verify test result accuracy
+    rsDebug("fp_mad_test PASSED", 0);
+    rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+}
+
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/primitives.rs b/libs/rs/java/tests/src/com/android/rs/test/primitives.rs
new file mode 100644
index 0000000..351a8a5
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/primitives.rs
@@ -0,0 +1,63 @@
+#include "shared.rsh"
+
+#pragma rs export_func(primitives_test)
+
+// Testing primitive types
+float floatTest = 1.99f;
+double doubleTest = 2.05;
+char charTest = -8;
+short shortTest = -16;
+int intTest = -32;
+long longTest = 17179869184l; // 1 << 34
+long long longlongTest = 68719476736l; // 1 << 36
+
+uchar ucharTest = 8;
+ushort ushortTest = 16;
+uint uintTest = 32;
+ulong ulongTest = 4611686018427387904L;
+int64_t int64_tTest = -17179869184l; // - 1 << 34
+uint64_t uint64_tTest = 117179869184l;
+
+static bool test_primitive_types(uint32_t index) {
+    bool failed = false;
+    start();
+
+    _RS_ASSERT(floatTest == 2.99f);
+    _RS_ASSERT(doubleTest == 3.05);
+    _RS_ASSERT(charTest == -16);
+    _RS_ASSERT(shortTest == -32);
+    _RS_ASSERT(intTest == -64);
+    _RS_ASSERT(longTest == 17179869185l);
+    _RS_ASSERT(longlongTest == 68719476735l);
+
+    _RS_ASSERT(ucharTest == 8);
+    _RS_ASSERT(ushortTest == 16);
+    _RS_ASSERT(uintTest == 32);
+    _RS_ASSERT(ulongTest == 4611686018427387903L);
+    _RS_ASSERT(int64_tTest == -17179869184l);
+    _RS_ASSERT(uint64_tTest == 117179869185l);
+
+    float time = end(index);
+
+    if (failed) {
+        rsDebug("test_primitives FAILED", time);
+    }
+    else {
+        rsDebug("test_primitives PASSED", time);
+    }
+
+    return failed;
+}
+
+void primitives_test(uint32_t index, int test_num) {
+    bool failed = false;
+    failed |= test_primitive_types(index);
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+    }
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/rsdebug.rs b/libs/rs/java/tests/src/com/android/rs/test/rsdebug.rs
new file mode 100644
index 0000000..a849234
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/rsdebug.rs
@@ -0,0 +1,58 @@
+#include "shared.rsh"
+
+#pragma rs export_func(test_rsdebug)
+
+// Testing primitive types
+float floatTest = 1.99f;
+double doubleTest = 2.05;
+char charTest = -8;
+short shortTest = -16;
+int intTest = -32;
+long longTest = 17179869184l; // 1 << 34
+long long longlongTest = 68719476736l; // 1 << 36
+
+uchar ucharTest = 8;
+ushort ushortTest = 16;
+uint uintTest = 32;
+ulong ulongTest = 4611686018427387904L;
+int64_t int64_tTest = -17179869184l; // - 1 << 34
+uint64_t uint64_tTest = 117179869184l;
+
+static bool basic_test(uint32_t index) {
+    bool failed = false;
+
+    // This test focuses primarily on compilation-time, not run-time.
+    // For this reason, none of the outputs are actually checked.
+
+    rsDebug("floatTest", floatTest);
+    rsDebug("doubleTest", doubleTest);
+    rsDebug("charTest", charTest);
+    rsDebug("shortTest", shortTest);
+    rsDebug("intTest", intTest);
+    rsDebug("longTest", longTest);
+    rsDebug("longlongTest", longlongTest);
+
+    rsDebug("ucharTest", ucharTest);
+    rsDebug("ushortTest", ushortTest);
+    rsDebug("uintTest", uintTest);
+    rsDebug("ulongTest", ulongTest);
+    rsDebug("int64_tTest", int64_tTest);
+    rsDebug("uint64_tTest", uint64_tTest);
+
+    return failed;
+}
+
+void test_rsdebug(uint32_t index, int test_num) {
+    bool failed = false;
+    failed |= basic_test(index);
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+        rsDebug("rsdebug_test FAILED", -1);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+        rsDebug("rsdebug_test PASSED", 0);
+    }
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/rslist.rs b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs
new file mode 100644
index 0000000..d1fde57
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs
@@ -0,0 +1,109 @@
+// 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.
+
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.rs.test)
+
+#include "rs_graphics.rsh"
+
+float gDY;
+
+rs_font gFont;
+
+typedef struct ListAllocs_s {
+    rs_allocation text;
+    int result;
+} ListAllocs;
+
+ListAllocs *gList;
+
+void init() {
+    gDY = 0.0f;
+}
+
+int textPos = 0;
+
+int root(int launchID) {
+
+    rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    rsgClearDepth(1.0f);
+
+    textPos -= (int)gDY*2;
+    gDY *= 0.95;
+
+    rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f);
+    rsgBindFont(gFont);
+    color(0.2, 0.2, 0.2, 0);
+
+    rs_allocation listAlloc = {0};
+    rsSetObject(&listAlloc, rsGetAllocation(gList));
+    int allocSize = rsAllocationGetDimX(listAlloc);
+
+    int width = rsgGetWidth();
+    int height = rsgGetHeight();
+
+    int itemHeight = 80;
+    int totalItemHeight = itemHeight * allocSize;
+
+    /* Prevent scrolling above the top of the list */
+    int firstItem = height - totalItemHeight;
+    if (firstItem < 0) {
+        firstItem = 0;
+    }
+
+    /* Prevent scrolling past the last line of the list */
+    int lastItem = -1 * (totalItemHeight - height);
+    if (lastItem > 0) {
+        lastItem = 0;
+    }
+
+    if (textPos > firstItem) {
+        textPos = firstItem;
+    }
+    else if (textPos < lastItem) {
+        textPos = lastItem;
+    }
+
+    int currentYPos = itemHeight + textPos;
+
+    for(int i = 0; i < allocSize; i ++) {
+        if(currentYPos - itemHeight > height) {
+            break;
+        }
+
+        if(currentYPos > 0) {
+            switch(gList[i].result) {
+                case 1: /* Passed */
+                    rsgFontColor(0.5f, 0.9f, 0.5f, 1.0f);
+                    break;
+                case -1: /* Failed */
+                    rsgFontColor(0.9f, 0.5f, 0.5f, 1.0f);
+                    break;
+                case 0: /* Still Testing */
+                    rsgFontColor(0.9f, 0.9f, 0.5f, 1.0f);
+                    break;
+                default: /* Unknown */
+                    rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f);
+                    break;
+            }
+            rsgDrawRect(0, currentYPos - 1, width, currentYPos, 0);
+            rsgDrawText(gList[i].text, 30, currentYPos - 32);
+        }
+        currentYPos += itemHeight;
+    }
+    rsClearObject(&listAlloc);
+
+    return 10;
+}
diff --git a/libs/rs/java/tests/src/com/android/rs/test/rstypes.rs b/libs/rs/java/tests/src/com/android/rs/test/rstypes.rs
new file mode 100644
index 0000000..cb0c577
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/rstypes.rs
@@ -0,0 +1,81 @@
+#include "shared.rsh"
+#include "rs_graphics.rsh"
+
+#pragma rs export_func(test_rstypes)
+
+rs_element elementTest;
+rs_type typeTest;
+rs_allocation allocationTest;
+rs_sampler samplerTest;
+rs_script scriptTest;
+rs_mesh meshTest;
+rs_program_fragment program_fragmentTest;
+rs_program_vertex program_vertexTest;
+rs_program_raster program_rasterTest;
+rs_program_store program_storeTest;
+rs_font fontTest;
+
+rs_matrix4x4 matrix4x4Test;
+rs_matrix3x3 matrix3x3Test;
+rs_matrix2x2 matrix2x2Test;
+
+struct my_struct {
+    int i;
+    rs_font fontTestStruct;
+};
+
+static bool basic_test(uint32_t index) {
+    bool failed = false;
+
+    rs_matrix4x4 matrix4x4TestLocal;
+    rs_matrix3x3 matrix3x3TestLocal;
+    rs_matrix2x2 matrix2x2TestLocal;
+
+    // This test focuses primarily on compilation-time, not run-time.
+    rs_element elementTestLocal;
+    rs_type typeTestLocal;
+    rs_allocation allocationTestLocal;
+    rs_sampler samplerTestLocal;
+    rs_script scriptTestLocal;
+    rs_mesh meshTestLocal;
+    rs_program_fragment program_fragmentTestLocal;
+    rs_program_vertex program_vertexTestLocal;
+    rs_program_raster program_rasterTestLocal;
+    rs_program_store program_storeTestLocal;
+    rs_font fontTestLocal;
+
+    rs_font fontTestLocalArray[4];
+
+    rs_font fontTestLocalPreInit = fontTest;
+
+    struct my_struct structTest;
+
+    rsSetObject(&fontTestLocal, fontTest);
+    //allocationTestLocal = allocationTest;
+
+    rsSetObject(&fontTest, fontTestLocal);
+    //allocationTest = allocationTestLocal;
+
+    /*for (int i = 0; i < 4; i++) {
+        rsSetObject(&fontTestLocalArray[i], fontTestLocal);
+    }*/
+
+    /*rsSetObject(&fontTest, fontTestLocalArray[3]);*/
+
+    return failed;
+}
+
+void test_rstypes(uint32_t index, int test_num) {
+    bool failed = false;
+    failed |= basic_test(index);
+
+    if (failed) {
+        rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+        rsDebug("rstypes_test FAILED", -1);
+    }
+    else {
+        rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+        rsDebug("rstypes_test PASSED", 0);
+    }
+}
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/shared.rsh b/libs/rs/java/tests/src/com/android/rs/test/shared.rsh
new file mode 100644
index 0000000..21be9af
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/shared.rsh
@@ -0,0 +1,38 @@
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.rs.test)
+
+typedef struct TestResult_s {
+    rs_allocation name;
+    bool pass;
+    float score;
+    int64_t time;
+} TestResult;
+//TestResult *g_results;
+
+static int64_t g_time;
+
+static void start(void) {
+    g_time = rsUptimeMillis();
+}
+
+static float end(uint32_t idx) {
+    int64_t t = rsUptimeMillis() - g_time;
+    //g_results[idx].time = t;
+    //rsDebug("test time", (int)t);
+    return ((float)t) / 1000.f;
+}
+
+#define _RS_ASSERT(b) \
+do { \
+    if (!(b)) { \
+        failed = true; \
+        rsDebug(#b " FAILED", 0); \
+    } \
+\
+} while (0)
+
+/* These constants must match those in UnitTest.java */
+static const int RS_MSG_TEST_PASSED = 100;
+static const int RS_MSG_TEST_FAILED = 101;
+
diff --git a/libs/rs/java/tests/src/com/android/rs/test/test_root.rs b/libs/rs/java/tests/src/com/android/rs/test/test_root.rs
new file mode 100644
index 0000000..6dc83ba
--- /dev/null
+++ b/libs/rs/java/tests/src/com/android/rs/test/test_root.rs
@@ -0,0 +1,23 @@
+// Fountain test script
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.rs.test)
+
+#pragma stateFragment(parent)
+
+#include "rs_graphics.rsh"
+
+
+typedef struct TestResult {
+    rs_allocation name;
+    bool pass;
+    float score;
+} TestResult_t;
+TestResult_t *results;
+
+int root() {
+
+    return 0;
+}
+
+
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 5ae8d01..14809e9 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -1,11 +1,14 @@
 
+ContextFinish {
+	handcodeApi
+	}
 
 ContextBindRootScript {
 	param RsScript sampler
 	}
 
-ContextBindProgramFragmentStore {
-	param RsProgramFragmentStore pgm
+ContextBindProgramStore {
+	param RsProgramStore pgm
 	}
 
 ContextBindProgramFragment {
@@ -20,6 +23,10 @@
 	param RsProgramRaster pgm
 	}
 
+ContextBindFont {
+	param RsFont pgm
+	}
+
 ContextPause {
 	}
 
@@ -52,7 +59,7 @@
 	}
 
 ObjDestroy {
-	param void *obj
+	param RsAsyncVoidPtr objPtr
 	}
 
 ElementCreate {
@@ -68,62 +75,24 @@
 	param const RsElement * elements
 	param const char ** names
 	param const size_t * nameLengths
+	param const uint32_t * arraySize
 	ret RsElement
 	}
 
-TypeBegin {
-	param RsElement type
-	}
-
-TypeAdd {
-	param RsDimension dim
-	param size_t value
-	}
-
-TypeCreate {
-	ret RsType
-	}
-
-AllocationCreateTyped {
-	param RsType type
-	ret RsAllocation
-	}
-
-AllocationCreateSized {
-	param RsElement e
-	param size_t count
-	ret RsAllocation
+AllocationUpdateFromBitmap {
+	param RsAllocation alloc
+	param RsElement srcFmt
+	param const void * data
 	}
 
 AllocationCreateBitmapRef {
 	param RsType type
-	param void * bmpPtr
-	param void * callbackData
+	param RsAsyncVoidPtr bmpPtr
+	param RsAsyncVoidPtr callbackData
 	param RsBitmapCallback_t callback
 	ret RsAllocation
 	}
 
-AllocationCreateFromBitmap {
-	param uint32_t width
-	param uint32_t height
-	param RsElement dstFmt
-	param RsElement srcFmt
-	param bool genMips
-	param const void * data
-	ret RsAllocation
-	}
-
-AllocationCreateFromBitmapBoxed {
-	param uint32_t width
-	param uint32_t height
-	param RsElement dstFmt
-	param RsElement srcFmt
-	param bool genMips
-	param const void * data
-	ret RsAllocation
-	}
-
-
 AllocationUploadToTexture {
 	param RsAllocation alloc
 	param bool genMipMaps
@@ -153,6 +122,16 @@
 	togglePlay
 	}
 
+Allocation1DSubElementData {
+	param RsAllocation va
+	param uint32_t x
+	param const void *data
+	param uint32_t comp_offset
+	param uint32_t bytes
+	handcodeApi
+	togglePlay
+	}
+
 Allocation2DSubData {
 	param RsAllocation va
 	param uint32_t xoff
@@ -163,6 +142,15 @@
 	param uint32_t bytes
 	}
 
+Allocation2DSubElementData {
+	param RsAllocation va
+	param uint32_t x
+	param uint32_t y
+	param const void *data
+	param uint32_t element_offset
+	param uint32_t bytes
+	}
+
 AllocationRead {
 	param RsAllocation va
 	param void * data
@@ -224,6 +212,17 @@
 	param const void *data
 	}
 
+AllocationResize1D {
+	param RsAllocation va
+	param uint32_t dimX
+	}
+
+AllocationResize2D {
+	param RsAllocation va
+	param uint32_t dimX
+	param uint32_t dimY
+	}
+
 SamplerBegin {
 	}
 
@@ -232,6 +231,11 @@
 	param RsSamplerValue value
 	}
 
+SamplerSet2 {
+	param RsSamplerParam p
+	param float value
+	}
+
 SamplerCreate {
 	ret RsSampler
 	}
@@ -248,13 +252,6 @@
 ScriptCBegin {
 	}
 
-ScriptSetClearColor {
-	param RsScript s
-	param float r
-	param float g
-	param float b
-	param float a
-	}
 
 ScriptSetTimeZone {
 	param RsScript s
@@ -262,43 +259,55 @@
 	param uint32_t length
 	}
 
-ScriptSetClearDepth {
-	param RsScript s
-	param float depth
-	}
-
-ScriptSetClearStencil {
-	param RsScript s
-	param uint32_t stencil
-	}
-
-ScriptSetType {
-	param RsType type
-	param uint32_t slot
-	param bool isWritable
-	param const char * name
-	}
-
-ScriptSetInvoke {
-	param const char * name
-	param uint32_t slot
-	}
 
 ScriptInvoke {
 	param RsScript s
 	param uint32_t slot
 	}
 
-ScriptSetRoot {
-	param bool isRoot
+ScriptInvokeV {
+	param RsScript s
+	param uint32_t slot
+	param const void * data
+	param uint32_t dataLen
+	handcodeApi
+	togglePlay
 	}
 
-
-
-ScriptCSetScript {
-	param void * codePtr
+ScriptSetVarI {
+	param RsScript s
+	param uint32_t slot
+	param int value
 	}
 
+ScriptSetVarJ {
+	param RsScript s
+	param uint32_t slot
+	param int64_t value
+	}
+
+ScriptSetVarF {
+	param RsScript s
+	param uint32_t slot
+	param float value
+	}
+
+ScriptSetVarD {
+	param RsScript s
+	param uint32_t slot
+	param double value
+	}
+
+ScriptSetVarV {
+	param RsScript s
+	param uint32_t slot
+	param const void * data
+	param uint32_t dataLen
+	handcodeApi
+	togglePlay
+	}
+
+
 ScriptCSetText {
 	param const char * text
 	param uint32_t length
@@ -308,52 +317,41 @@
 	ret RsScript
 	}
 
-ScriptCSetDefineF {
-    param const char* name
-    param float value
-    }
 
-ScriptCSetDefineI32 {
-    param const char* name
-    param int32_t value
-    }
-
-ProgramFragmentStoreBegin {
+ProgramStoreBegin {
 	param RsElement in
 	param RsElement out
 	}
 
-ProgramFragmentStoreColorMask {
+ProgramStoreColorMask {
 	param bool r
 	param bool g
 	param bool b
 	param bool a
 	}
 
-ProgramFragmentStoreBlendFunc {
+ProgramStoreBlendFunc {
 	param RsBlendSrcFunc srcFunc
 	param RsBlendDstFunc destFunc
 	}
 
-ProgramFragmentStoreDepthMask {
+ProgramStoreDepthMask {
 	param bool enable
 }
 
-ProgramFragmentStoreDither {
+ProgramStoreDither {
 	param bool enable
 }
 
-ProgramFragmentStoreDepthFunc {
+ProgramStoreDepthFunc {
 	param RsDepthFunc func
 }
 
-ProgramFragmentStoreCreate {
-	ret RsProgramFragmentStore
+ProgramStoreCreate {
+	ret RsProgramStore
 	}
 
 ProgramRasterCreate {
-	param RsElement in
-	param RsElement out
 	param bool pointSmooth
 	param bool lineSmooth
 	param bool pointSprite
@@ -365,12 +363,11 @@
 	param float lw
 }
 
-ProgramRasterSetPointSize{
+ProgramRasterSetCullMode {
 	param RsProgramRaster pr
-	param float ps
+	param RsCullMode mode
 }
 
-
 ProgramBindConstants {
 	param RsProgram vp
 	param uint32_t slot
@@ -391,12 +388,6 @@
 	}
 
 ProgramFragmentCreate {
-	param const uint32_t * params
-	param uint32_t paramLength
-	ret RsProgramFragment
-	}
-
-ProgramFragmentCreate2 {
 	param const char * shaderText
 	param uint32_t shaderLength
 	param const uint32_t * params
@@ -405,11 +396,6 @@
 	}
 
 ProgramVertexCreate {
-	param bool texMat
-	ret RsProgramVertex
-	}
-
-ProgramVertexCreate2 {
 	param const char * shaderText
 	param uint32_t shaderLength
 	param const uint32_t * params
@@ -417,66 +403,45 @@
 	ret RsProgramVertex
 	}
 
-LightBegin {
-	}
-
-LightSetLocal {
-	param bool isLocal
-	}
-
-LightSetMonochromatic {
-	param bool isMono
-	}
-
-LightCreate {
-	ret RsLight light
-	}
-
-
-LightSetPosition {
-	param RsLight light
-	param float x
-	param float y
-	param float z
-	}
-
-LightSetColor {
-	param RsLight light
-	param float r
-	param float g
-	param float b
-	}
-
 FileOpen {
 	ret RsFile
 	param const char *name
 	param size_t len
 	}
 
+FontCreateFromFile {
+	param const char *name
+	param uint32_t fontSize
+	param uint32_t dpi
+	ret RsFont
+	}
 
-SimpleMeshCreate {
-	ret RsSimpleMesh
-	param RsAllocation prim
-	param RsAllocation index
-	param RsAllocation *vtx
+MeshCreate {
+	ret RsMesh
 	param uint32_t vtxCount
-	param uint32_t primType
+	param uint32_t idxCount
 	}
 
-
-SimpleMeshBindIndex {
-	param RsSimpleMesh mesh
+MeshBindIndex {
+	param RsMesh mesh
 	param RsAllocation idx
+	param uint32_t primType
+	param uint32_t slot
 	}
 
-SimpleMeshBindPrimitive {
-	param RsSimpleMesh mesh
-	param RsAllocation prim
-	}
-
-SimpleMeshBindVertex {
-	param RsSimpleMesh mesh
+MeshBindVertex {
+	param RsMesh mesh
 	param RsAllocation vtx
 	param uint32_t slot
 	}
 
+AnimationCreate {
+	param const float *inValues
+	param const float *outValues
+	param uint32_t valueCount
+	param RsAnimationInterpolation interp
+	param RsAnimationEdge pre
+	param RsAnimationEdge post
+	ret RsAnimation
+	}
+
diff --git a/libs/rs/rsAdapter.cpp b/libs/rs/rsAdapter.cpp
index 0d31fac..1d1425c 100644
--- a/libs/rs/rsAdapter.cpp
+++ b/libs/rs/rsAdapter.cpp
@@ -15,7 +15,11 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
+#else
+#include "rsContextHostStub.h"
+#endif
 
 using namespace android;
 using namespace android::renderscript;
@@ -23,15 +27,11 @@
 
 Adapter1D::Adapter1D(Context *rsc) : ObjectBase(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     reset();
 }
 
 Adapter1D::Adapter1D(Context *rsc, Allocation *a) : ObjectBase(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     reset();
     setAllocation(a);
 }
@@ -70,6 +70,16 @@
            mAllocation.get()->getType()->getSizeBytes());
 }
 
+void Adapter1D::serialize(OStream *stream) const
+{
+
+}
+
+Adapter1D *Adapter1D::createFromStream(Context *rsc, IStream *stream)
+{
+    return NULL;
+}
+
 namespace android {
 namespace renderscript {
 
@@ -131,15 +141,11 @@
 
 Adapter2D::Adapter2D(Context *rsc) : ObjectBase(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     reset();
 }
 
 Adapter2D::Adapter2D(Context *rsc, Allocation *a) : ObjectBase(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     reset();
     setAllocation(a);
 }
@@ -169,7 +175,6 @@
 
     uint32_t eSize = mAllocation.get()->getType()->getElementSizeBytes();
     uint32_t lineSize = eSize * w;
-    uint32_t destW = getDimX();
 
     const uint8_t *src = static_cast<const uint8_t *>(data);
     for (uint32_t line=yoff; line < (yoff+h); line++) {
@@ -185,6 +190,15 @@
            mAllocation.get()->getType()->getSizeBytes());
 }
 
+void Adapter2D::serialize(OStream *stream) const
+{
+
+}
+
+Adapter2D *Adapter2D::createFromStream(Context *rsc, IStream *stream)
+{
+    return NULL;
+}
 
 
 namespace android {
diff --git a/libs/rs/rsAdapter.h b/libs/rs/rsAdapter.h
index cb2872e..449e7ad 100644
--- a/libs/rs/rsAdapter.h
+++ b/libs/rs/rsAdapter.h
@@ -50,6 +50,10 @@
     void subData(uint32_t xoff, uint32_t count, const void *data);
     void data(const void *data);
 
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_ADAPTER_1D; }
+    static Adapter1D *createFromStream(Context *rsc, IStream *stream);
+
 protected:
     ObjectBaseRef<Allocation> mAllocation;
     uint32_t mY;
@@ -82,6 +86,10 @@
     void data(const void *data);
     void subData(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data);
 
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_ADAPTER_2D; }
+    static Adapter2D *createFromStream(Context *rsc, IStream *stream);
+
 protected:
     ObjectBaseRef<Allocation> mAllocation;
     uint32_t mZ;
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 4e8278d..b74fa8e 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -13,12 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
 
 #include <GLES/gl.h>
 #include <GLES2/gl2.h>
 #include <GLES/glext.h>
+#else
+#include "rsContextHostStub.h"
+
+#include <OpenGL/gl.h>
+#include <OpenGl/glext.h>
+#endif
+
+#include "utils/StopWatch.h"
 
 using namespace android;
 using namespace android::renderscript;
@@ -28,6 +36,9 @@
     init(rsc, type);
 
     mPtr = malloc(mType->getSizeBytes());
+    if (mType->getElement()->getHasReferences()) {
+        memset(mPtr, 0, mType->getSizeBytes());
+    }
     if (!mPtr) {
         LOGE("Allocation::Allocation, alloc failure");
     }
@@ -46,8 +57,6 @@
 
 void Allocation::init(Context *rsc, const Type *type)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     mPtr = NULL;
 
     mCpuWrite = false;
@@ -141,6 +150,8 @@
         return;
     }
 
+    bool isFirstUpload = false;
+
     if (!mTextureID) {
         glGenTextures(1, &mTextureID);
 
@@ -153,6 +164,7 @@
             mUploadDefered = true;
             return;
         }
+        isFirstUpload = true;
     }
     glBindTexture(GL_TEXTURE_2D, mTextureID);
     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
@@ -162,14 +174,23 @@
         adapt.setLOD(lod+mTextureLOD);
 
         uint16_t * ptr = static_cast<uint16_t *>(adapt.getElement(0,0));
-        glTexImage2D(GL_TEXTURE_2D, lod, format,
-                     adapt.getDimX(), adapt.getDimY(),
-                     0, format, type, ptr);
+        if(isFirstUpload) {
+            glTexImage2D(GL_TEXTURE_2D, lod, format,
+                         adapt.getDimX(), adapt.getDimY(),
+                         0, format, type, ptr);
+        } else {
+            glTexSubImage2D(GL_TEXTURE_2D, lod, 0, 0,
+                         adapt.getDimX(), adapt.getDimY(),
+                         format, type, ptr);
+        }
     }
     if (mTextureGenMipmap) {
+#ifndef ANDROID_RS_BUILD_FOR_HOST
         glGenerateMipmap(GL_TEXTURE_2D);
+#endif //ANDROID_RS_BUILD_FOR_HOST
     }
 
+    rsc->checkError("Allocation::uploadToTexture");
 }
 
 void Allocation::deferedUploadToBufferObject(const Context *rsc)
@@ -201,6 +222,7 @@
     glBindBuffer(GL_ARRAY_BUFFER, mBufferID);
     glBufferData(GL_ARRAY_BUFFER, mType->getSizeBytes(), getPtr(), GL_DYNAMIC_DRAW);
     glBindBuffer(GL_ARRAY_BUFFER, 0);
+    rsc->checkError("Allocation::uploadToBufferObject");
 }
 
 void Allocation::uploadCheck(const Context *rsc)
@@ -217,13 +239,19 @@
 }
 
 
-void Allocation::data(const void *data, uint32_t sizeBytes)
+void Allocation::data(Context *rsc, const void *data, uint32_t sizeBytes)
 {
     uint32_t size = mType->getSizeBytes();
     if (size != sizeBytes) {
         LOGE("Allocation::data called with mismatched size expected %i, got %i", size, sizeBytes);
         return;
     }
+
+    if (mType->getElement()->getHasReferences()) {
+        incRefs(data, sizeBytes / mType->getElement()->getSizeBytes());
+        decRefs(mPtr, sizeBytes / mType->getElement()->getSizeBytes());
+    }
+
     memcpy(mPtr, data, size);
     sendDirty();
     mUploadDefered = true;
@@ -234,7 +262,7 @@
     memcpy(data, mPtr, mType->getSizeBytes());
 }
 
-void Allocation::subData(uint32_t xoff, uint32_t count, const void *data, uint32_t sizeBytes)
+void Allocation::subData(Context *rsc, uint32_t xoff, uint32_t count, const void *data, uint32_t sizeBytes)
 {
     uint32_t eSize = mType->getElementSizeBytes();
     uint8_t * ptr = static_cast<uint8_t *>(mPtr);
@@ -246,12 +274,18 @@
         mType->dumpLOGV("type info");
         return;
     }
+
+    if (mType->getElement()->getHasReferences()) {
+        incRefs(data, count);
+        decRefs(ptr, count);
+    }
+
     memcpy(ptr, data, size);
     sendDirty();
     mUploadDefered = true;
 }
 
-void Allocation::subData(uint32_t xoff, uint32_t yoff,
+void Allocation::subData(Context *rsc, uint32_t xoff, uint32_t yoff,
              uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes)
 {
     uint32_t eSize = mType->getElementSizeBytes();
@@ -268,7 +302,10 @@
     }
 
     for (uint32_t line=yoff; line < (yoff+h); line++) {
-        uint8_t * ptr = static_cast<uint8_t *>(mPtr);
+        if (mType->getElement()->getHasReferences()) {
+            incRefs(src, w);
+            decRefs(dst, w);
+        }
         memcpy(dst, src, lineSize);
         src += lineSize;
         dst += destW * eSize;
@@ -277,14 +314,96 @@
     mUploadDefered = true;
 }
 
-void Allocation::subData(uint32_t xoff, uint32_t yoff, uint32_t zoff,
+void Allocation::subData(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t zoff,
              uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes)
 {
 }
 
+void Allocation::subElementData(Context *rsc, uint32_t x, const void *data,
+                                uint32_t cIdx, uint32_t sizeBytes)
+{
+    uint32_t eSize = mType->getElementSizeBytes();
+    uint8_t * ptr = static_cast<uint8_t *>(mPtr);
+    ptr += eSize * x;
+
+    if (cIdx >= mType->getElement()->getFieldCount()) {
+        LOGE("Error Allocation::subElementData component %i out of range.", cIdx);
+        rsc->setError(RS_ERROR_BAD_VALUE, "subElementData component out of range.");
+        return;
+    }
+
+    if (x >= mType->getDimX()) {
+        LOGE("Error Allocation::subElementData X offset %i out of range.", x);
+        rsc->setError(RS_ERROR_BAD_VALUE, "subElementData X offset out of range.");
+        return;
+    }
+
+    const Element * e = mType->getElement()->getField(cIdx);
+    ptr += mType->getElement()->getFieldOffsetBytes(cIdx);
+
+    if (sizeBytes != e->getSizeBytes()) {
+        LOGE("Error Allocation::subElementData data size %i does not match field size %i.", sizeBytes, e->getSizeBytes());
+        rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size.");
+        return;
+    }
+
+    if (e->getHasReferences()) {
+        e->incRefs(data);
+        e->decRefs(ptr);
+    }
+
+    memcpy(ptr, data, sizeBytes);
+    sendDirty();
+    mUploadDefered = true;
+}
+
+void Allocation::subElementData(Context *rsc, uint32_t x, uint32_t y,
+                                const void *data, uint32_t cIdx, uint32_t sizeBytes)
+{
+    uint32_t eSize = mType->getElementSizeBytes();
+    uint8_t * ptr = static_cast<uint8_t *>(mPtr);
+    ptr += eSize * (x + y * mType->getDimX());
+
+    if (x >= mType->getDimX()) {
+        LOGE("Error Allocation::subElementData X offset %i out of range.", x);
+        rsc->setError(RS_ERROR_BAD_VALUE, "subElementData X offset out of range.");
+        return;
+    }
+
+    if (y >= mType->getDimY()) {
+        LOGE("Error Allocation::subElementData X offset %i out of range.", x);
+        rsc->setError(RS_ERROR_BAD_VALUE, "subElementData X offset out of range.");
+        return;
+    }
+
+    if (cIdx >= mType->getElement()->getFieldCount()) {
+        LOGE("Error Allocation::subElementData component %i out of range.", cIdx);
+        rsc->setError(RS_ERROR_BAD_VALUE, "subElementData component out of range.");
+        return;
+    }
+
+    const Element * e = mType->getElement()->getField(cIdx);
+    ptr += mType->getElement()->getFieldOffsetBytes(cIdx);
+
+    if (sizeBytes != e->getSizeBytes()) {
+        LOGE("Error Allocation::subElementData data size %i does not match field size %i.", sizeBytes, e->getSizeBytes());
+        rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size.");
+        return;
+    }
+
+    if (e->getHasReferences()) {
+        e->incRefs(data);
+        e->decRefs(ptr);
+    }
+
+    memcpy(ptr, data, sizeBytes);
+    sendDirty();
+    mUploadDefered = true;
+}
+
 void Allocation::addProgramToDirty(const Program *p)
 {
-    mToDirtyList.add(p);
+    mToDirtyList.push(p);
 }
 
 void Allocation::removeProgramToDirty(const Program *p)
@@ -316,6 +435,61 @@
 
 }
 
+void Allocation::serialize(OStream *stream) const
+{
+    // Need to identify ourselves
+    stream->addU32((uint32_t)getClassId());
+
+    String8 name(getName());
+    stream->addString(&name);
+
+    // First thing we need to serialize is the type object since it will be needed
+    // to initialize the class
+    mType->serialize(stream);
+
+    uint32_t dataSize = mType->getSizeBytes();
+    // Write how much data we are storing
+    stream->addU32(dataSize);
+    // Now write the data
+    stream->addByteArray(mPtr, dataSize);
+}
+
+Allocation *Allocation::createFromStream(Context *rsc, IStream *stream)
+{
+    // First make sure we are reading the correct object
+    RsA3DClassID classID = (RsA3DClassID)stream->loadU32();
+    if(classID != RS_A3D_CLASS_ID_ALLOCATION) {
+        LOGE("allocation loading skipped due to invalid class id\n");
+        return NULL;
+    }
+
+    String8 name;
+    stream->loadString(&name);
+
+    Type *type = Type::createFromStream(rsc, stream);
+    if(!type) {
+        return NULL;
+    }
+    type->compute();
+
+    // Number of bytes we wrote out for this allocation
+    uint32_t dataSize = stream->loadU32();
+    if(dataSize != type->getSizeBytes()) {
+        LOGE("failed to read allocation because numbytes written is not the same loaded type wants\n");
+        ObjectBase::checkDelete(type);
+        return NULL;
+    }
+
+    Allocation *alloc = new Allocation(rsc, type);
+    alloc->setName(name.string(), name.size());
+
+    // Read in all of our allocation data
+    alloc->data(rsc, stream->getPtr() + stream->getPos(), dataSize);
+    stream->reset(stream->getPos() + dataSize);
+
+    return alloc;
+}
+
 void Allocation::sendDirty() const
 {
     for (size_t ct=0; ct < mToDirtyList.size(); ct++) {
@@ -323,6 +497,65 @@
     }
 }
 
+void Allocation::incRefs(const void *ptr, size_t ct, size_t startOff) const
+{
+    const uint8_t *p = static_cast<const uint8_t *>(ptr);
+    const Element *e = mType->getElement();
+    uint32_t stride = e->getSizeBytes();
+
+    p += stride * startOff;
+    while (ct > 0) {
+        e->incRefs(p);
+        ct --;
+        p += stride;
+    }
+}
+
+void Allocation::decRefs(const void *ptr, size_t ct, size_t startOff) const
+{
+    const uint8_t *p = static_cast<const uint8_t *>(ptr);
+    const Element *e = mType->getElement();
+    uint32_t stride = e->getSizeBytes();
+
+    p += stride * startOff;
+    while (ct > 0) {
+        e->decRefs(p);
+        ct --;
+        p += stride;
+    }
+}
+
+void Allocation::copyRange1D(Context *rsc, const Allocation *src, int32_t srcOff, int32_t destOff, int32_t len)
+{
+}
+
+void Allocation::resize1D(Context *rsc, uint32_t dimX)
+{
+    Type *t = mType->cloneAndResize1D(rsc, dimX);
+
+    uint32_t oldDimX = mType->getDimX();
+    if (dimX == oldDimX) {
+        return;
+    }
+
+    if (dimX < oldDimX) {
+        decRefs(mPtr, oldDimX - dimX, dimX);
+    }
+    mPtr = realloc(mPtr, t->getSizeBytes());
+
+    if (dimX > oldDimX) {
+        const Element *e = mType->getElement();
+        uint32_t stride = e->getSizeBytes();
+        memset(((uint8_t *)mPtr) + stride * oldDimX, 0, stride * (dimX - oldDimX));
+    }
+    mType.set(t);
+}
+
+void Allocation::resize2D(Context *rsc, uint32_t dimX, uint32_t dimY)
+{
+    LOGE("not implemented");
+}
+
 /////////////////
 //
 
@@ -330,24 +563,6 @@
 namespace android {
 namespace renderscript {
 
-RsAllocation rsi_AllocationCreateTyped(Context *rsc, RsType vtype)
-{
-    const Type * type = static_cast<const Type *>(vtype);
-
-    Allocation * alloc = new Allocation(rsc, type);
-    alloc->incUserRef();
-    return alloc;
-}
-
-RsAllocation rsi_AllocationCreateSized(Context *rsc, RsElement e, size_t count)
-{
-    Type * type = new Type(rsc);
-    type->setDimX(count);
-    type->setElement(static_cast<Element *>(e));
-    type->compute();
-    return rsi_AllocationCreateTyped(rsc, type);
-}
-
 void rsi_AllocationUploadToTexture(Context *rsc, RsAllocation va, bool genmip, uint32_t baseMipLevel)
 {
     Allocation *alloc = static_cast<Allocation *>(va);
@@ -495,7 +710,7 @@
     if (srcGLType == GL_UNSIGNED_BYTE &&
         srcGLFmt == GL_RGB &&
         dstGLType == GL_UNSIGNED_SHORT_5_6_5 &&
-        dstGLType == GL_RGB) {
+        dstGLFmt == GL_RGB) {
 
         return elementConverter_888_to_565;
     }
@@ -503,15 +718,21 @@
     if (srcGLType == GL_UNSIGNED_BYTE &&
         srcGLFmt == GL_RGBA &&
         dstGLType == GL_UNSIGNED_SHORT_5_6_5 &&
-        dstGLType == GL_RGB) {
+        dstGLFmt == GL_RGB) {
 
         return elementConverter_8888_to_565;
     }
 
     LOGE("pickConverter, unsuported combo, src %p,  dst %p", src, dst);
+    LOGE("pickConverter, srcGLType = %x,  srcGLFmt = %x", srcGLType, srcGLFmt);
+    LOGE("pickConverter, dstGLType = %x,  dstGLFmt = %x", dstGLType, dstGLFmt);
+    src->dumpLOGV("SRC ");
+    dst->dumpLOGV("DST ");
     return 0;
 }
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
+
 RsAllocation rsi_AllocationCreateBitmapRef(Context *rsc, RsType vtype,
                                            void *bmp, void *callbackData, RsBitmapCallback_t callback)
 {
@@ -521,90 +742,60 @@
     return alloc;
 }
 
-RsAllocation rsi_AllocationCreateFromBitmap(Context *rsc, uint32_t w, uint32_t h, RsElement _dst, RsElement _src,  bool genMips, const void *data)
+void rsi_AllocationUpdateFromBitmap(Context *rsc, RsAllocation va, RsElement _src, const void *data)
 {
+    Allocation *texAlloc = static_cast<Allocation *>(va);
     const Element *src = static_cast<const Element *>(_src);
-    const Element *dst = static_cast<const Element *>(_dst);
-
-    // Check for pow2 on pre es 2.0 versions.
-    rsAssert(rsc->checkVersion2_0() || (!(w & (w-1)) && !(h & (h-1))));
-
-    //LOGE("rsi_AllocationCreateFromBitmap %i %i %i %i %i", w, h, dstFmt, srcFmt, genMips);
-    rsi_TypeBegin(rsc, _dst);
-    rsi_TypeAdd(rsc, RS_DIMENSION_X, w);
-    rsi_TypeAdd(rsc, RS_DIMENSION_Y, h);
-    if (genMips) {
-        rsi_TypeAdd(rsc, RS_DIMENSION_LOD, 1);
-    }
-    RsType type = rsi_TypeCreate(rsc);
-
-    RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, type);
-    Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
-    if (texAlloc == NULL) {
-        LOGE("Memory allocation failure");
-        return NULL;
-    }
+    const Element *dst = texAlloc->getType()->getElement();
+    uint32_t w = texAlloc->getType()->getDimX();
+    uint32_t h = texAlloc->getType()->getDimY();
+    bool genMips = texAlloc->getType()->getDimLOD();
 
     ElementConverter_t cvt = pickConverter(dst, src);
-    cvt(texAlloc->getPtr(), data, w * h);
-
-    if (genMips) {
-        Adapter2D adapt(rsc, texAlloc);
-        Adapter2D adapt2(rsc, texAlloc);
-        for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
-            adapt.setLOD(lod);
-            adapt2.setLOD(lod + 1);
-            mip(adapt2, adapt);
+    if (cvt) {
+        cvt(texAlloc->getPtr(), data, w * h);
+        if (genMips) {
+            Adapter2D adapt(rsc, texAlloc);
+            Adapter2D adapt2(rsc, texAlloc);
+            for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
+                adapt.setLOD(lod);
+                adapt2.setLOD(lod + 1);
+                mip(adapt2, adapt);
+            }
         }
+    } else {
+        rsc->setError(RS_ERROR_BAD_VALUE, "Unsupported bitmap format");
     }
-
-    return texAlloc;
-}
-
-RsAllocation rsi_AllocationCreateFromBitmapBoxed(Context *rsc, uint32_t w, uint32_t h, RsElement _dst, RsElement _src, bool genMips, const void *data)
-{
-    const Element *srcE = static_cast<const Element *>(_src);
-    const Element *dstE = static_cast<const Element *>(_dst);
-    uint32_t w2 = rsHigherPow2(w);
-    uint32_t h2 = rsHigherPow2(h);
-
-    if ((w2 == w) && (h2 == h)) {
-        return rsi_AllocationCreateFromBitmap(rsc, w, h, _dst, _src, genMips, data);
-    }
-
-    uint32_t bpp = srcE->getSizeBytes();
-    size_t size = w2 * h2 * bpp;
-    uint8_t *tmp = static_cast<uint8_t *>(malloc(size));
-    memset(tmp, 0, size);
-
-    const uint8_t * src = static_cast<const uint8_t *>(data);
-    for (uint32_t y = 0; y < h; y++) {
-        uint8_t * ydst = &tmp[(y + ((h2 - h) >> 1)) * w2 * bpp];
-        memcpy(&ydst[((w2 - w) >> 1) * bpp], src, w * bpp);
-        src += w * bpp;
-    }
-
-    RsAllocation ret = rsi_AllocationCreateFromBitmap(rsc, w2, h2, _dst, _src, genMips, tmp);
-    free(tmp);
-    return ret;
 }
 
 void rsi_AllocationData(Context *rsc, RsAllocation va, const void *data, uint32_t sizeBytes)
 {
     Allocation *a = static_cast<Allocation *>(va);
-    a->data(data, sizeBytes);
+    a->data(rsc, data, sizeBytes);
 }
 
 void rsi_Allocation1DSubData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t count, const void *data, uint32_t sizeBytes)
 {
     Allocation *a = static_cast<Allocation *>(va);
-    a->subData(xoff, count, data, sizeBytes);
+    a->subData(rsc, xoff, count, data, sizeBytes);
+}
+
+void rsi_Allocation2DSubElementData(Context *rsc, RsAllocation va, uint32_t x, uint32_t y, const void *data, uint32_t eoff, uint32_t sizeBytes)
+{
+    Allocation *a = static_cast<Allocation *>(va);
+    a->subElementData(rsc, x, y, data, eoff, sizeBytes);
+}
+
+void rsi_Allocation1DSubElementData(Context *rsc, RsAllocation va, uint32_t x, const void *data, uint32_t eoff, uint32_t sizeBytes)
+{
+    Allocation *a = static_cast<Allocation *>(va);
+    a->subElementData(rsc, x, data, eoff, sizeBytes);
 }
 
 void rsi_Allocation2DSubData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes)
 {
     Allocation *a = static_cast<Allocation *>(va);
-    a->subData(xoff, yoff, w, h, data, sizeBytes);
+    a->subData(rsc, xoff, yoff, w, h, data, sizeBytes);
 }
 
 void rsi_AllocationRead(Context *rsc, RsAllocation va, void *data)
@@ -613,6 +804,72 @@
     a->read(data);
 }
 
+void rsi_AllocationResize1D(Context *rsc, RsAllocation va, uint32_t dimX)
+{
+    Allocation *a = static_cast<Allocation *>(va);
+    a->resize1D(rsc, dimX);
+}
+
+void rsi_AllocationResize2D(Context *rsc, RsAllocation va, uint32_t dimX, uint32_t dimY)
+{
+    Allocation *a = static_cast<Allocation *>(va);
+    a->resize2D(rsc, dimX, dimY);
+}
+
+#endif //ANDROID_RS_BUILD_FOR_HOST
 
 }
 }
+
+const void * rsaAllocationGetType(RsContext con, RsAllocation va)
+{
+    Allocation *a = static_cast<Allocation *>(va);
+    a->getType()->incUserRef();
+
+    return a->getType();
+}
+
+RsAllocation rsaAllocationCreateTyped(RsContext con, RsType vtype)
+{
+    Context *rsc = static_cast<Context *>(con);
+    Allocation * alloc = new Allocation(rsc, static_cast<Type *>(vtype));
+    alloc->incUserRef();
+    return alloc;
+}
+
+RsAllocation rsaAllocationCreateFromBitmap(RsContext con, uint32_t w, uint32_t h, RsElement _dst, RsElement _src,  bool genMips, const void *data)
+{
+    Context *rsc = static_cast<Context *>(con);
+    const Element *src = static_cast<const Element *>(_src);
+    const Element *dst = static_cast<const Element *>(_dst);
+
+    //LOGE("%p rsi_AllocationCreateFromBitmap %i %i %i", rsc, w, h, genMips);
+    RsDimension dims[] = {RS_DIMENSION_X, RS_DIMENSION_Y, RS_DIMENSION_LOD};
+    uint32_t dimValues[] = {w, h, genMips};
+    RsType type = rsaTypeCreate(rsc, _dst, 3, dims, dimValues);
+
+    RsAllocation vTexAlloc = rsaAllocationCreateTyped(rsc, type);
+    Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
+    if (texAlloc == NULL) {
+        LOGE("Memory allocation failure");
+        return NULL;
+    }
+
+    ElementConverter_t cvt = pickConverter(dst, src);
+    if (cvt) {
+        cvt(texAlloc->getPtr(), data, w * h);
+        if (genMips) {
+            Adapter2D adapt(rsc, texAlloc);
+            Adapter2D adapt2(rsc, texAlloc);
+            for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
+                adapt.setLOD(lod);
+                adapt2.setLOD(lod + 1);
+                mip(adapt2, adapt);
+            }
+        }
+    } else {
+        rsc->setError(RS_ERROR_BAD_VALUE, "Unsupported bitmap format");
+    }
+
+    return texAlloc;
+}
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
index 516f8b7..12cf832 100644
--- a/libs/rs/rsAllocation.h
+++ b/libs/rs/rsAllocation.h
@@ -30,8 +30,6 @@
     // The graphics equilivent of malloc.  The allocation contains a structure of elements.
 
 public:
-    // By policy this allocation will hold a pointer to the type
-    // but will not destroy it on destruction.
     Allocation(Context *rsc, const Type *);
     Allocation(Context *rsc, const Type *, void *bmp, void *callbackData, RsBitmapCallback_t callback);
 
@@ -55,14 +53,23 @@
     void uploadToBufferObject(const Context *rsc);
     uint32_t getBufferObjectID() const {return mBufferID;}
 
+    void copyRange1D(Context *rsc, const Allocation *src, int32_t srcOff, int32_t destOff, int32_t len);
 
-    void data(const void *data, uint32_t sizeBytes);
-    void subData(uint32_t xoff, uint32_t count, const void *data, uint32_t sizeBytes);
-    void subData(uint32_t xoff, uint32_t yoff,
+    void resize1D(Context *rsc, uint32_t dimX);
+    void resize2D(Context *rsc, uint32_t dimX, uint32_t dimY);
+
+    void data(Context *rsc, const void *data, uint32_t sizeBytes);
+    void subData(Context *rsc, uint32_t xoff, uint32_t count, const void *data, uint32_t sizeBytes);
+    void subData(Context *rsc, uint32_t xoff, uint32_t yoff,
                  uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes);
-    void subData(uint32_t xoff, uint32_t yoff, uint32_t zoff,
+    void subData(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t zoff,
                  uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes);
 
+    void subElementData(Context *rsc, uint32_t x,
+                        const void *data, uint32_t elementOff, uint32_t sizeBytes);
+    void subElementData(Context *rsc, uint32_t x, uint32_t y,
+                        const void *data, uint32_t elementOff, uint32_t sizeBytes);
+
     void read(void *data);
 
     void enableGLVertexBuffers() const;
@@ -72,12 +79,22 @@
     void removeProgramToDirty(const Program *);
 
     virtual void dumpLOGV(const char *prefix) const;
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_ALLOCATION; }
+    static Allocation *createFromStream(Context *rsc, IStream *stream);
 
     virtual void uploadCheck(const Context *rsc);
 
-protected:
-    void sendDirty() const;
+    bool getIsTexture() const {return mIsTexture;}
+    bool getIsBufferObject() const {return mIsVertexBuffer;}
 
+    void incRefs(const void *ptr, size_t ct, size_t startOff = 0) const;
+    void decRefs(const void *ptr, size_t ct, size_t startOff = 0) const;
+
+    void sendDirty() const;
+    bool getHasGraphicsMipmaps() const {return mTextureGenMipmap;}
+
+protected:
     ObjectBaseRef<const Type> mType;
     void * mPtr;
 
diff --git a/libs/rs/rsAnimation.cpp b/libs/rs/rsAnimation.cpp
new file mode 100644
index 0000000..6200715
--- /dev/null
+++ b/libs/rs/rsAnimation.cpp
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_BUILD_FOR_HOST
+#include "rsContext.h"
+#else
+#include "rsContextHostStub.h"
+#endif //ANDROID_RS_BUILD_FOR_HOST
+
+#include "rsAnimation.h"
+
+
+using namespace android;
+using namespace android::renderscript;
+
+void Animation::serialize(OStream *stream) const
+{
+    
+}
+
+Animation *Animation::createFromStream(Context *rsc, IStream *stream)
+{
+    return NULL;
+}
+
+/*
+Animation::Animation(Context *rsc) : ObjectBase(rsc)
+{
+    mAllocFile = __FILE__;
+    mAllocLine = __LINE__;
+
+    mValuesInput = NULL;
+    mValuesOutput = NULL;
+    mValueCount = 0;
+    mInterpolation = RS_ANIMATION_INTERPOLATION_STEP;
+    mEdgePre = RS_ANIMATION_EDGE_UNDEFINED;
+    mEdgePost = RS_ANIMATION_EDGE_UNDEFINED;
+    mInputMin = 0;
+    mInputMax = 0;
+}
+
+Animation * Animation::create(Context *rsc,
+                              const float *inValues, const float *outValues,
+                              uint32_t valueCount, RsAnimationInterpolation interp,
+                              RsAnimationEdge pre, RsAnimationEdge post)
+{
+    if (valueCount < 2) {
+        rsc->setError(RS_ERROR_BAD_VALUE, "Animations require more than 2 values.");
+        return NULL;
+    }
+    Animation *a = new Animation(rsc);
+    if (!a) {
+        rsc->setError(RS_ERROR_OUT_OF_MEMORY);
+        return NULL;
+    }
+
+    float *vin = (float *)malloc(valueCount * sizeof(float));
+    float *vout = (float *)malloc(valueCount * sizeof(float));
+    a->mValuesInput = vin;
+    a->mValuesOutput = vout;
+    if (a->mValuesInput == NULL || a->mValuesOutput == NULL) {
+        delete a;
+        rsc->setError(RS_ERROR_OUT_OF_MEMORY);
+        return NULL;
+    }
+
+    a->mEdgePre = pre;
+    a->mEdgePost = post;
+    a->mInterpolation = interp;
+    a->mValueCount = valueCount;
+
+    memcpy(vin, inValues, valueCount * sizeof(float));
+    memcpy(vout, outValues, valueCount * sizeof(float));
+    a->mInputMin = inValues[0];
+    a->mInputMax = inValues[0];
+
+    bool needSort = false;
+    for (uint32_t ct=1; ct < valueCount; ct++) {
+        if (a->mInputMin > vin[ct]) {
+            needSort = true;
+            a->mInputMin = vin[ct];
+        }
+        if (a->mInputMax < vin[ct]) {
+            a->mInputMax = vin[ct];
+        } else {
+            needSort = true;
+        }
+    }
+
+    while (1) {
+        bool changed = false;
+        for (uint32_t ct=1; ct < valueCount; ct++) {
+            if (vin[ct-1] > vin[ct]) {
+                float t = vin[ct-1];
+                vin[ct-1] = vin[ct];
+                vin[ct] = t;
+                t = vout[ct-1];
+                vout[ct-1] = vout[ct];
+                vout[ct] = t;
+                changed = true;
+            }
+        }
+        if (!changed) break;
+    }
+
+    return a;
+}
+*/
+
+
+/////////////////////////////////////////
+//
+
+namespace android {
+namespace renderscript {
+
+RsAnimation rsi_AnimationCreate(Context *rsc,
+                                const float *inValues,
+                                const float *outValues,
+                                uint32_t valueCount,
+                                RsAnimationInterpolation interp,
+                                RsAnimationEdge pre,
+                                RsAnimationEdge post)
+{
+    //LOGE("rsi_ElementCreate %i %i %i %i", dt, dk, norm, vecSize);
+    Animation *a = NULL;//Animation::create(rsc, inValues, outValues, valueCount, interp, pre, post);
+    if (a != NULL) {
+        a->incUserRef();
+    }
+    return (RsAnimation)a;
+}
+
+
+}
+}
+
diff --git a/libs/rs/rsAnimation.h b/libs/rs/rsAnimation.h
new file mode 100644
index 0000000..340314e
--- /dev/null
+++ b/libs/rs/rsAnimation.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_ANIMATION_H
+#define ANDROID_RS_ANIMATION_H
+
+#include "rsUtils.h"
+#include "rsObjectBase.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+class Animation : public ObjectBase
+{
+public:
+    ~Animation();
+
+    static Animation * create(Context *rsc,
+                              const float *inValues, const float *outValues,
+                              uint32_t valueCount, RsAnimationInterpolation,
+                              RsAnimationEdge pre, RsAnimationEdge post);
+
+    float eval(float) const;
+
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_ANIMATION; }
+    static Animation *createFromStream(Context *rsc, IStream *stream);
+
+protected:
+    Animation(Context *rsc);
+
+
+
+    float evalInRange(float) const;
+
+
+
+    const float *mValuesInput;
+    const float *mValuesOutput;
+    uint32_t mValueCount;
+    RsAnimationInterpolation mInterpolation;
+    RsAnimationEdge mEdgePre;
+    RsAnimationEdge mEdgePost;
+
+    // derived
+    float mInputMin;
+    float mInputMax;
+};
+
+
+
+
+}
+}
+#endif //ANDROID_STRUCTURED_ELEMENT_H
+
diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp
index 15a56f7..f51b23e 100644
--- a/libs/rs/rsComponent.cpp
+++ b/libs/rs/rsComponent.cpp
@@ -16,7 +16,11 @@
 
 #include "rsComponent.h"
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include <GLES/gl.h>
+#else
+#include <OpenGL/gl.h>
+#endif
 
 using namespace android;
 using namespace android::renderscript;
@@ -91,6 +95,26 @@
         mNormalized = true;
         rsAssert(mKind == RS_KIND_PIXEL_RGBA);
         return;
+
+    case RS_TYPE_MATRIX_4X4:
+        mTypeBits = 16 * 32;
+        rsAssert(mVectorSize == 1);
+        rsAssert(mNormalized == false);
+        rsAssert(mKind == RS_KIND_USER);
+        break;
+    case RS_TYPE_MATRIX_3X3:
+        mTypeBits = 9 * 32;
+        rsAssert(mVectorSize == 1);
+        rsAssert(mNormalized == false);
+        rsAssert(mKind == RS_KIND_USER);
+        break;
+    case RS_TYPE_MATRIX_2X2:
+        mTypeBits = 4 * 32;
+        rsAssert(mVectorSize == 1);
+        rsAssert(mNormalized == false);
+        rsAssert(mKind == RS_KIND_USER);
+        break;
+
     case RS_TYPE_ELEMENT:
     case RS_TYPE_TYPE:
     case RS_TYPE_ALLOCATION:
@@ -148,11 +172,19 @@
     case RS_TYPE_UNSIGNED_64:
         mTypeBits = 64;
         break;
+
+    case RS_TYPE_BOOLEAN:
+        mTypeBits = 8;
+        break;
     }
 
     mBits = mTypeBits * mVectorSize;
 }
 
+bool Component::isReference() const
+{
+    return (mType >= RS_TYPE_ELEMENT);
+}
 
 
 
@@ -188,82 +220,6 @@
     return 0;
 }
 
-static const char * gCTypeStrings[] = {
-    0,
-    0,//"F16",
-    "float",
-    "double",
-    "char",
-    "short",
-    "int",
-    0,//"S64",
-    "char",//U8",
-    "short",//U16",
-    "int",//U32",
-    0,//"U64",
-    0,//"UP_565",
-    0,//"UP_5551",
-    0,//"UP_4444",
-    0,//"ELEMENT",
-    0,//"TYPE",
-    0,//"ALLOCATION",
-    0,//"SAMPLER",
-    0,//"SCRIPT",
-    0,//"MESH",
-    0,//"PROGRAM_FRAGMENT",
-    0,//"PROGRAM_VERTEX",
-    0,//"PROGRAM_RASTER",
-    0,//"PROGRAM_STORE",
-};
-
-static const char * gCVecTypeStrings[] = {
-    0,
-    0,//"F16",
-    "vecF32",
-    "vecF64",
-    "vecI8",
-    "vecI16",
-    "vecI32",
-    0,//"S64",
-    "vecU8",//U8",
-    "vecU16",//U16",
-    "vecU32",//U32",
-    0,//"U64",
-    0,//"UP_565",
-    0,//"UP_5551",
-    0,//"UP_4444",
-    0,//"ELEMENT",
-    0,//"TYPE",
-    0,//"ALLOCATION",
-    0,//"SAMPLER",
-    0,//"SCRIPT",
-    0,//"MESH",
-    0,//"PROGRAM_FRAGMENT",
-    0,//"PROGRAM_VERTEX",
-    0,//"PROGRAM_RASTER",
-    0,//"PROGRAM_STORE",
-};
-
-String8 Component::getCType() const
-{
-    char buf[64];
-    if (mVectorSize == 1) {
-        return String8(gCTypeStrings[mType]);
-    }
-
-    // Yuck, acc WAR
-    // Appears to have problems packing chars
-    if (mVectorSize == 4 && mType == RS_TYPE_UNSIGNED_8) {
-        return String8("int");
-    }
-
-
-    String8 s(gCVecTypeStrings[mType]);
-    sprintf(buf, "_%i_t", mVectorSize);
-    s.append(buf);
-    return s;
-}
-
 String8 Component::getGLSLType() const
 {
     if (mType == RS_TYPE_SIGNED_32) {
@@ -282,10 +238,19 @@
         case 4: return String8("vec4");
         }
     }
+    if ((mType == RS_TYPE_MATRIX_4X4) && (mVectorSize == 1)) {
+        return String8("mat4");
+    }
+    if ((mType == RS_TYPE_MATRIX_3X3) && (mVectorSize == 1)) {
+        return String8("mat3");
+    }
+    if ((mType == RS_TYPE_MATRIX_2X2) && (mVectorSize == 1)) {
+        return String8("mat2");
+    }
     return String8();
 }
 
-static const char * gTypeStrings[] = {
+static const char * gTypeBasicStrings[] = {
     "NONE",
     "F16",
     "F32",
@@ -298,9 +263,16 @@
     "U16",
     "U32",
     "U64",
+    "BOOLEAN",
     "UP_565",
     "UP_5551",
     "UP_4444",
+    "MATRIX_4X4",
+    "MATRIX_3X3",
+    "MATRIX_2X2",
+};
+
+static const char * gTypeObjStrings[] = {
     "ELEMENT",
     "TYPE",
     "ALLOCATION",
@@ -330,8 +302,34 @@
 
 void Component::dumpLOGV(const char *prefix) const
 {
-    LOGV("%s   Component: %s, %s, vectorSize=%i, bits=%i",
-         prefix, gTypeStrings[mType], gKindStrings[mKind], mVectorSize, mBits);
+    if (mType >= RS_TYPE_ELEMENT) {
+        LOGV("%s   Component: %s, %s, vectorSize=%i, bits=%i",
+             prefix, gTypeObjStrings[mType - RS_TYPE_ELEMENT], gKindStrings[mKind], mVectorSize, mBits);
+    } else {
+        LOGV("%s   Component: %s, %s, vectorSize=%i, bits=%i",
+             prefix, gTypeBasicStrings[mType], gKindStrings[mKind], mVectorSize, mBits);
+    }
 }
 
+void Component::serialize(OStream *stream) const
+{
+    stream->addU8((uint8_t)mType);
+    stream->addU8((uint8_t)mKind);
+    stream->addU8((uint8_t)(mNormalized ? 1 : 0));
+    stream->addU32(mVectorSize);
+}
+
+void Component::loadFromStream(IStream *stream)
+{
+    mType = (RsDataType)stream->loadU8();
+    mKind = (RsDataKind)stream->loadU8();
+    uint8_t temp = stream->loadU8();
+    mNormalized = temp != 0;
+    mVectorSize = stream->loadU32();
+
+    set(mType, mKind, mNormalized, mVectorSize);
+}
+
+
+
 
diff --git a/libs/rs/rsComponent.h b/libs/rs/rsComponent.h
index 71de324..a775051 100644
--- a/libs/rs/rsComponent.h
+++ b/libs/rs/rsComponent.h
@@ -35,7 +35,6 @@
 
     uint32_t getGLType() const;
     uint32_t getGLFormat() const;
-    String8 getCType() const;
     String8 getGLSLType() const;
     void dumpLOGV(const char *prefix) const;
 
@@ -48,6 +47,12 @@
     bool getIsSigned() const {return mIsSigned;}
     uint32_t getBits() const {return mBits;}
 
+    // Helpers for reading / writing this class out
+    void serialize(OStream *stream) const;
+    void loadFromStream(IStream *stream);
+
+    bool isReference() const;
+
 protected:
     RsDataType mType;
     RsDataKind mKind;
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 92c6619..678d327 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -18,21 +18,24 @@
 #include "rsContext.h"
 #include "rsThreadIO.h"
 #include <ui/FramebufferNativeWindow.h>
+#include <ui/PixelFormat.h>
 #include <ui/EGLUtils.h>
 #include <ui/egl/android_natives.h>
 
 #include <sys/types.h>
 #include <sys/resource.h>
+#include <sched.h>
 
 #include <cutils/properties.h>
 
-#include <EGL/eglext.h>
 #include <GLES/gl.h>
 #include <GLES/glext.h>
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
 #include <cutils/sched_policy.h>
+#include <sys/syscall.h>
+#include <string.h>
 
 using namespace android;
 using namespace android::renderscript;
@@ -54,23 +57,65 @@
     }
 }
 
-void Context::initEGL(bool useGL2)
+void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) {
+
+#define X(VAL) {VAL, #VAL}
+    struct {EGLint attribute; const char* name;} names[] = {
+    X(EGL_BUFFER_SIZE),
+    X(EGL_ALPHA_SIZE),
+    X(EGL_BLUE_SIZE),
+    X(EGL_GREEN_SIZE),
+    X(EGL_RED_SIZE),
+    X(EGL_DEPTH_SIZE),
+    X(EGL_STENCIL_SIZE),
+    X(EGL_CONFIG_CAVEAT),
+    X(EGL_CONFIG_ID),
+    X(EGL_LEVEL),
+    X(EGL_MAX_PBUFFER_HEIGHT),
+    X(EGL_MAX_PBUFFER_PIXELS),
+    X(EGL_MAX_PBUFFER_WIDTH),
+    X(EGL_NATIVE_RENDERABLE),
+    X(EGL_NATIVE_VISUAL_ID),
+    X(EGL_NATIVE_VISUAL_TYPE),
+    X(EGL_SAMPLES),
+    X(EGL_SAMPLE_BUFFERS),
+    X(EGL_SURFACE_TYPE),
+    X(EGL_TRANSPARENT_TYPE),
+    X(EGL_TRANSPARENT_RED_VALUE),
+    X(EGL_TRANSPARENT_GREEN_VALUE),
+    X(EGL_TRANSPARENT_BLUE_VALUE),
+    X(EGL_BIND_TO_TEXTURE_RGB),
+    X(EGL_BIND_TO_TEXTURE_RGBA),
+    X(EGL_MIN_SWAP_INTERVAL),
+    X(EGL_MAX_SWAP_INTERVAL),
+    X(EGL_LUMINANCE_SIZE),
+    X(EGL_ALPHA_MASK_SIZE),
+    X(EGL_COLOR_BUFFER_TYPE),
+    X(EGL_RENDERABLE_TYPE),
+    X(EGL_CONFORMANT),
+   };
+#undef X
+
+    for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) {
+        EGLint value = -1;
+        EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value);
+        EGLint error = eglGetError();
+        if (returnVal && error == EGL_SUCCESS) {
+            LOGV(" %s: %d (0x%x)", names[j].name, value, value);
+        }
+    }
+}
+
+
+bool Context::initGLThread()
 {
+    pthread_mutex_lock(&gInitMutex);
+    LOGV("initGLThread start %p", this);
+
     mEGL.mNumConfigs = -1;
     EGLint configAttribs[128];
     EGLint *configAttribsPtr = configAttribs;
-    EGLint context_attribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2,
-            EGL_NONE, GL_NONE, EGL_NONE };
-
-#ifdef HAS_CONTEXT_PRIORITY
-#ifdef EGL_IMG_context_priority
-#warning "using EGL_IMG_context_priority"
-    if (mThreadPriority > 0) {
-        context_attribs2[2] = EGL_CONTEXT_PRIORITY_LEVEL_IMG;
-        context_attribs2[3] = EGL_CONTEXT_PRIORITY_LOW_IMG;
-    }
-#endif
-#endif
+    EGLint context_attribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
 
     memset(configAttribs, 0, sizeof(configAttribs));
 
@@ -78,15 +123,13 @@
     configAttribsPtr[1] = EGL_WINDOW_BIT;
     configAttribsPtr += 2;
 
-    if (useGL2) {
-        configAttribsPtr[0] = EGL_RENDERABLE_TYPE;
-        configAttribsPtr[1] = EGL_OPENGL_ES2_BIT;
-        configAttribsPtr += 2;
-    }
+    configAttribsPtr[0] = EGL_RENDERABLE_TYPE;
+    configAttribsPtr[1] = EGL_OPENGL_ES2_BIT;
+    configAttribsPtr += 2;
 
-    if (mUseDepth) {
+    if (mUserSurfaceConfig.depthMin > 0) {
         configAttribsPtr[0] = EGL_DEPTH_SIZE;
-        configAttribsPtr[1] = 16;
+        configAttribsPtr[1] = mUserSurfaceConfig.depthMin;
         configAttribsPtr += 2;
     }
 
@@ -99,38 +142,121 @@
     configAttribsPtr[0] = EGL_NONE;
     rsAssert(configAttribsPtr < (configAttribs + (sizeof(configAttribs) / sizeof(EGLint))));
 
-    LOGV("initEGL start");
+    LOGV("%p initEGL start", this);
     mEGL.mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     checkEglError("eglGetDisplay");
 
     eglInitialize(mEGL.mDisplay, &mEGL.mMajorVersion, &mEGL.mMinorVersion);
     checkEglError("eglInitialize");
 
-    status_t err = EGLUtils::selectConfigForNativeWindow(mEGL.mDisplay, configAttribs, mWndSurface, &mEGL.mConfig);
+#if 1
+    PixelFormat pf = PIXEL_FORMAT_RGBA_8888;
+    if (mUserSurfaceConfig.alphaMin == 0) {
+        pf = PIXEL_FORMAT_RGBX_8888;
+    }
+
+    status_t err = EGLUtils::selectConfigForPixelFormat(mEGL.mDisplay, configAttribs, pf, &mEGL.mConfig);
     if (err) {
-       LOGE("couldn't find an EGLConfig matching the screen format\n");
+       LOGE("%p, couldn't find an EGLConfig matching the screen format\n", this);
     }
-    //eglChooseConfig(mEGL.mDisplay, configAttribs, &mEGL.mConfig, 1, &mEGL.mNumConfigs);
-
-
-    if (useGL2) {
-        mEGL.mContext = eglCreateContext(mEGL.mDisplay, mEGL.mConfig, EGL_NO_CONTEXT, context_attribs2);
-    } else {
-        mEGL.mContext = eglCreateContext(mEGL.mDisplay, mEGL.mConfig, EGL_NO_CONTEXT, NULL);
+    if (props.mLogVisual) {
+        printEGLConfiguration(mEGL.mDisplay, mEGL.mConfig);
     }
+#else
+    eglChooseConfig(mEGL.mDisplay, configAttribs, &mEGL.mConfig, 1, &mEGL.mNumConfigs);
+#endif
+
+    mEGL.mContext = eglCreateContext(mEGL.mDisplay, mEGL.mConfig, EGL_NO_CONTEXT, context_attribs2);
     checkEglError("eglCreateContext");
     if (mEGL.mContext == EGL_NO_CONTEXT) {
-        LOGE("eglCreateContext returned EGL_NO_CONTEXT");
+        pthread_mutex_unlock(&gInitMutex);
+        LOGE("%p, eglCreateContext returned EGL_NO_CONTEXT", this);
+        return false;
     }
     gGLContextCount++;
+
+
+    EGLint pbuffer_attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
+    mEGL.mSurfaceDefault = eglCreatePbufferSurface(mEGL.mDisplay, mEGL.mConfig, pbuffer_attribs);
+    checkEglError("eglCreatePbufferSurface");
+    if (mEGL.mSurfaceDefault == EGL_NO_SURFACE) {
+        LOGE("eglCreatePbufferSurface returned EGL_NO_SURFACE");
+        pthread_mutex_unlock(&gInitMutex);
+        deinitEGL();
+        return false;
+    }
+
+    EGLBoolean ret = eglMakeCurrent(mEGL.mDisplay, mEGL.mSurfaceDefault, mEGL.mSurfaceDefault, mEGL.mContext);
+    if (ret == EGL_FALSE) {
+        LOGE("eglMakeCurrent returned EGL_FALSE");
+        checkEglError("eglMakeCurrent", ret);
+        pthread_mutex_unlock(&gInitMutex);
+        deinitEGL();
+        return false;
+    }
+
+    mGL.mVersion = glGetString(GL_VERSION);
+    mGL.mVendor = glGetString(GL_VENDOR);
+    mGL.mRenderer = glGetString(GL_RENDERER);
+    mGL.mExtensions = glGetString(GL_EXTENSIONS);
+
+    //LOGV("EGL Version %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion);
+    LOGV("GL Version %s", mGL.mVersion);
+    //LOGV("GL Vendor %s", mGL.mVendor);
+    LOGV("GL Renderer %s", mGL.mRenderer);
+    //LOGV("GL Extensions %s", mGL.mExtensions);
+
+    const char *verptr = NULL;
+    if (strlen((const char *)mGL.mVersion) > 9) {
+        if (!memcmp(mGL.mVersion, "OpenGL ES-CM", 12)) {
+            verptr = (const char *)mGL.mVersion + 12;
+        }
+        if (!memcmp(mGL.mVersion, "OpenGL ES ", 10)) {
+            verptr = (const char *)mGL.mVersion + 9;
+        }
+    }
+
+    if (!verptr) {
+        LOGE("Error, OpenGL ES Lite not supported");
+        pthread_mutex_unlock(&gInitMutex);
+        deinitEGL();
+        return false;
+    } else {
+        sscanf(verptr, " %i.%i", &mGL.mMajorVersion, &mGL.mMinorVersion);
+    }
+
+    glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &mGL.mMaxVertexAttribs);
+    glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &mGL.mMaxVertexUniformVectors);
+    glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGL.mMaxVertexTextureUnits);
+
+    glGetIntegerv(GL_MAX_VARYING_VECTORS, &mGL.mMaxVaryingVectors);
+    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGL.mMaxTextureImageUnits);
+
+    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mGL.mMaxFragmentTextureImageUnits);
+    glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGL.mMaxFragmentUniformVectors);
+
+    mGL.OES_texture_npot = NULL != strstr((const char *)mGL.mExtensions, "GL_OES_texture_npot");
+    mGL.GL_NV_texture_npot_2D_mipmap = NULL != strstr((const char *)mGL.mExtensions, "GL_NV_texture_npot_2D_mipmap");
+    mGL.EXT_texture_max_aniso = 1.0f;
+    bool hasAniso = NULL != strstr((const char *)mGL.mExtensions, "GL_EXT_texture_filter_anisotropic");
+    if(hasAniso) {
+        glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &mGL.EXT_texture_max_aniso);
+    }
+
+    LOGV("initGLThread end %p", this);
+    pthread_mutex_unlock(&gInitMutex);
+    return true;
 }
 
 void Context::deinitEGL()
 {
-    LOGV("deinitEGL");
-    setSurface(0, 0, NULL);
-    eglDestroyContext(mEGL.mDisplay, mEGL.mContext);
-    checkEglError("eglDestroyContext");
+    LOGV("%p, deinitEGL", this);
+
+    if (mEGL.mContext != EGL_NO_CONTEXT) {
+        eglMakeCurrent(mEGL.mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, mEGL.mContext);
+        eglDestroyContext(mEGL.mDisplay, mEGL.mContext);
+        checkEglError("eglDestroyContext");
+    }
 
     gGLContextCount--;
     if (!gGLContextCount) {
@@ -139,19 +265,21 @@
 }
 
 
-uint32_t Context::runScript(Script *s, uint32_t launchID)
+uint32_t Context::runScript(Script *s)
 {
     ObjectBaseRef<ProgramFragment> frag(mFragment);
     ObjectBaseRef<ProgramVertex> vtx(mVertex);
-    ObjectBaseRef<ProgramFragmentStore> store(mFragmentStore);
+    ObjectBaseRef<ProgramStore> store(mFragmentStore);
     ObjectBaseRef<ProgramRaster> raster(mRaster);
+    ObjectBaseRef<Font> font(mFont);
 
-    uint32_t ret = s->run(this, launchID);
+    uint32_t ret = s->run(this);
 
     mFragment.set(frag);
     mVertex.set(vtx);
     mFragmentStore.set(store);
     mRaster.set(raster);
+    mFont.set(font);
     return ret;
 }
 
@@ -159,42 +287,19 @@
 {
     GLenum err = glGetError();
     if (err != GL_NO_ERROR) {
-        LOGE("GL Error, 0x%x, from %s", err, msg);
+        LOGE("%p, GL Error, 0x%x, from %s", this, err, msg);
     }
 }
 
 uint32_t Context::runRootScript()
 {
-    timerSet(RS_TIMER_CLEAR_SWAP);
-    rsAssert(mRootScript->mEnviroment.mIsRoot);
-
-    eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_WIDTH, &mEGL.mWidth);
-    eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_HEIGHT, &mEGL.mHeight);
-    glViewport(0, 0, mEGL.mWidth, mEGL.mHeight);
-    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-
-    glClearColor(mRootScript->mEnviroment.mClearColor[0],
-                 mRootScript->mEnviroment.mClearColor[1],
-                 mRootScript->mEnviroment.mClearColor[2],
-                 mRootScript->mEnviroment.mClearColor[3]);
-    if (mUseDepth) {
-        glDepthMask(GL_TRUE);
-        glClearDepthf(mRootScript->mEnviroment.mClearDepth);
-        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-    } else {
-        glClear(GL_COLOR_BUFFER_BIT);
-    }
+    glViewport(0, 0, mWidth, mHeight);
 
     timerSet(RS_TIMER_SCRIPT);
     mStateFragmentStore.mLast.clear();
-    uint32_t ret = runScript(mRootScript.get(), 0);
+    uint32_t ret = runScript(mRootScript.get());
 
     checkError("runRootScript");
-    if (mError != RS_ERROR_NONE) {
-        // If we have an error condition we stop rendering until
-        // somthing changes that might fix it.
-        ret = 0;
-    }
     return ret;
 }
 
@@ -218,6 +323,9 @@
     mTimeFrame = mTimeLast;
     mTimeLastFrame = mTimeLast;
     mTimerActive = RS_TIMER_INTERNAL;
+    mAverageFPSFrameCount = 0;
+    mAverageFPSStartTime = mTimeLast;
+    mAverageFPS = 0;
     timerReset();
 }
 
@@ -225,6 +333,16 @@
 {
     mTimeLastFrame = mTimeFrame;
     mTimeFrame = getTime();
+    // Update average fps
+    const uint64_t averageFramerateInterval = 1000 * 1000000;
+    mAverageFPSFrameCount ++;
+    uint64_t inverval = mTimeFrame - mAverageFPSStartTime;
+    if(inverval >= averageFramerateInterval) {
+        inverval = inverval / 1000000;
+        mAverageFPS = (mAverageFPSFrameCount * 1000) / inverval;
+        mAverageFPSFrameCount = 0;
+        mAverageFPSStartTime = mTimeFrame;
+    }
 }
 
 void Context::timerSet(Timers tm)
@@ -248,37 +366,34 @@
 
 
     if (props.mLogTimes) {
-        LOGV("RS: Frame (%i),   Script %2.1f (%i),  Clear & Swap %2.1f (%i),  Idle %2.1f (%lli),  Internal %2.1f (%lli)",
+        LOGV("RS: Frame (%i),   Script %2.1f%% (%i),  Swap %2.1f%% (%i),  Idle %2.1f%% (%lli),  Internal %2.1f%% (%lli), Avg fps: %u",
              mTimeMSLastFrame,
              100.0 * mTimers[RS_TIMER_SCRIPT] / total, mTimeMSLastScript,
              100.0 * mTimers[RS_TIMER_CLEAR_SWAP] / total, mTimeMSLastSwap,
              100.0 * mTimers[RS_TIMER_IDLE] / total, mTimers[RS_TIMER_IDLE] / 1000000,
-             100.0 * mTimers[RS_TIMER_INTERNAL] / total, mTimers[RS_TIMER_INTERNAL] / 1000000);
+             100.0 * mTimers[RS_TIMER_INTERNAL] / total, mTimers[RS_TIMER_INTERNAL] / 1000000,
+             mAverageFPS);
     }
 }
 
 bool Context::setupCheck()
 {
-    if (checkVersion2_0()) {
-        if (!mShaderCache.lookup(this, mVertex.get(), mFragment.get())) {
-            LOGE("Context::setupCheck() 1 fail");
-            return false;
-        }
-
-        mFragmentStore->setupGL2(this, &mStateFragmentStore);
-        mFragment->setupGL2(this, &mStateFragment, &mShaderCache);
-        mRaster->setupGL2(this, &mStateRaster);
-        mVertex->setupGL2(this, &mStateVertex, &mShaderCache);
-
-    } else {
-        mFragmentStore->setupGL(this, &mStateFragmentStore);
-        mFragment->setupGL(this, &mStateFragment);
-        mRaster->setupGL(this, &mStateRaster);
-        mVertex->setupGL(this, &mStateVertex);
+    if (!mShaderCache.lookup(this, mVertex.get(), mFragment.get())) {
+        LOGE("Context::setupCheck() 1 fail");
+        return false;
     }
+
+    mFragmentStore->setupGL2(this, &mStateFragmentStore);
+    mFragment->setupGL2(this, &mStateFragment, &mShaderCache);
+    mRaster->setupGL2(this, &mStateRaster);
+    mVertex->setupGL2(this, &mStateVertex, &mShaderCache);
     return true;
 }
 
+void Context::setupProgramStore() {
+    mFragmentStore->setupGL2(this, &mStateFragmentStore);
+}
+
 static bool getProp(const char *str)
 {
     char buf[PROPERTY_VALUE_MAX];
@@ -286,6 +401,24 @@
     return 0 != strcmp(buf, "0");
 }
 
+void Context::displayDebugStats()
+{
+    char buffer[128];
+    sprintf(buffer, "Avg fps %u, Frame %i ms, Script %i ms", mAverageFPS, mTimeMSLastFrame, mTimeMSLastScript);
+    float oldR, oldG, oldB, oldA;
+    mStateFont.getFontColor(&oldR, &oldG, &oldB, &oldA);
+    uint32_t bufferLen = strlen(buffer);
+
+    float shadowCol = 0.1f;
+    mStateFont.setFontColor(shadowCol, shadowCol, shadowCol, 1.0f);
+    mStateFont.renderText(buffer, bufferLen, 5, getHeight() - 6);
+
+    mStateFont.setFontColor(1.0f, 0.7f, 0.0f, 1.0f);
+    mStateFont.renderText(buffer, bufferLen, 4, getHeight() - 7);
+
+    mStateFont.setFontColor(oldR, oldG, oldB, oldA);
+}
+
 void * Context::threadProc(void *vrsc)
 {
      Context *rsc = static_cast<Context *>(vrsc);
@@ -298,28 +431,40 @@
      rsc->props.mLogScripts = getProp("debug.rs.script");
      rsc->props.mLogObjects = getProp("debug.rs.object");
      rsc->props.mLogShaders = getProp("debug.rs.shader");
+     rsc->props.mLogShadersAttr = getProp("debug.rs.shader.attributes");
+     rsc->props.mLogShadersUniforms = getProp("debug.rs.shader.uniforms");
+     rsc->props.mLogVisual = getProp("debug.rs.visual");
 
-     ScriptTLSStruct *tlsStruct = new ScriptTLSStruct;
-     if (!tlsStruct) {
+     rsc->mTlsStruct = new ScriptTLSStruct;
+     if (!rsc->mTlsStruct) {
          LOGE("Error allocating tls storage");
+         rsc->setError(RS_ERROR_OUT_OF_MEMORY, "Failed allocation for TLS");
          return NULL;
      }
-     tlsStruct->mContext = rsc;
-     tlsStruct->mScript = NULL;
-     int status = pthread_setspecific(rsc->gThreadTLSKey, tlsStruct);
+     rsc->mTlsStruct->mContext = rsc;
+     rsc->mTlsStruct->mScript = NULL;
+     int status = pthread_setspecific(rsc->gThreadTLSKey, rsc->mTlsStruct);
      if (status) {
          LOGE("pthread_setspecific %i", status);
      }
 
+     if (!rsc->initGLThread()) {
+         rsc->setError(RS_ERROR_OUT_OF_MEMORY, "Failed initializing GL");
+         return NULL;
+     }
+
+     rsc->mScriptC.init(rsc);
      if (rsc->mIsGraphicsContext) {
-         rsc->mStateRaster.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight);
+         rsc->mStateRaster.init(rsc);
          rsc->setRaster(NULL);
-         rsc->mStateVertex.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight);
+         rsc->mStateVertex.init(rsc);
          rsc->setVertex(NULL);
-         rsc->mStateFragment.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight);
+         rsc->mStateFragment.init(rsc);
          rsc->setFragment(NULL);
-         rsc->mStateFragmentStore.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight);
+         rsc->mStateFragmentStore.init(rsc);
          rsc->setFragmentStore(NULL);
+         rsc->mStateFont.init(rsc);
+         rsc->setFont(NULL);
          rsc->mStateVertexArray.init(rsc);
      }
 
@@ -333,6 +478,11 @@
          uint32_t targetTime = 0;
          if (mDraw && rsc->mIsGraphicsContext) {
              targetTime = rsc->runRootScript();
+
+             if(rsc->props.mLogVisual) {
+                 rsc->displayDebugStats();
+             }
+
              mDraw = targetTime && !rsc->mPaused;
              rsc->timerSet(RS_TIMER_CLEAR_SWAP);
              eglSwapBuffers(rsc->mEGL.mDisplay, rsc->mEGL.mSurface);
@@ -341,10 +491,7 @@
              rsc->timerPrint();
              rsc->timerReset();
          }
-         if (rsc->mObjDestroy.mNeedToEmpty) {
-             rsc->objDestroyOOBRun();
-         }
-         if (rsc->mThreadPriority > 0 && targetTime) {
+         if (targetTime > 1) {
              int32_t t = (targetTime - (int32_t)(rsc->mTimeMSLastScript + rsc->mTimeMSLastSwap)) * 1000;
              if (t > 0) {
                  usleep(t);
@@ -352,33 +499,85 @@
          }
      }
 
-     LOGV("RS Thread exiting");
+     LOGV("%p, RS Thread exiting", rsc);
      if (rsc->mIsGraphicsContext) {
          rsc->mRaster.clear();
          rsc->mFragment.clear();
          rsc->mVertex.clear();
          rsc->mFragmentStore.clear();
+         rsc->mFont.clear();
          rsc->mRootScript.clear();
          rsc->mStateRaster.deinit(rsc);
          rsc->mStateVertex.deinit(rsc);
          rsc->mStateFragment.deinit(rsc);
          rsc->mStateFragmentStore.deinit(rsc);
+         rsc->mStateFont.deinit(rsc);
      }
      ObjectBase::zeroAllUserRef(rsc);
 
-     rsc->mObjDestroy.mNeedToEmpty = true;
-     rsc->objDestroyOOBRun();
-
      if (rsc->mIsGraphicsContext) {
          pthread_mutex_lock(&gInitMutex);
          rsc->deinitEGL();
          pthread_mutex_unlock(&gInitMutex);
      }
+     delete rsc->mTlsStruct;
 
-     LOGV("RS Thread exited");
+     LOGV("%p, RS Thread exited", rsc);
      return NULL;
 }
 
+void * Context::helperThreadProc(void *vrsc)
+{
+     Context *rsc = static_cast<Context *>(vrsc);
+     uint32_t idx = (uint32_t)android_atomic_inc(&rsc->mWorkers.mLaunchCount);
+
+     LOGV("RS helperThread starting %p idx=%i", rsc, idx);
+
+     rsc->mWorkers.mLaunchSignals[idx].init();
+     rsc->mWorkers.mNativeThreadId[idx] = gettid();
+
+#if 0
+     typedef struct {uint64_t bits[1024 / 64]; } cpu_set_t;
+     cpu_set_t cpuset;
+     memset(&cpuset, 0, sizeof(cpuset));
+     cpuset.bits[idx / 64] |= 1ULL << (idx % 64);
+     int ret = syscall(241, rsc->mWorkers.mNativeThreadId[idx],
+               sizeof(cpuset), &cpuset);
+     LOGE("SETAFFINITY ret = %i %s", ret, EGLUtils::strerror(ret));
+#endif
+
+     setpriority(PRIO_PROCESS, rsc->mWorkers.mNativeThreadId[idx], rsc->mThreadPriority);
+     int status = pthread_setspecific(rsc->gThreadTLSKey, rsc->mTlsStruct);
+     if (status) {
+         LOGE("pthread_setspecific %i", status);
+     }
+
+     while(rsc->mRunning) {
+         rsc->mWorkers.mLaunchSignals[idx].wait();
+         if (rsc->mWorkers.mLaunchCallback) {
+            rsc->mWorkers.mLaunchCallback(rsc->mWorkers.mLaunchData, idx);
+         }
+         android_atomic_dec(&rsc->mWorkers.mRunningCount);
+         rsc->mWorkers.mCompleteSignal.set();
+     }
+
+     LOGV("RS helperThread exiting %p idx=%i", rsc, idx);
+     return NULL;
+}
+
+void Context::launchThreads(WorkerCallback_t cbk, void *data)
+{
+    mWorkers.mLaunchData = data;
+    mWorkers.mLaunchCallback = cbk;
+    mWorkers.mRunningCount = (int)mWorkers.mCount;
+    for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) {
+        mWorkers.mLaunchSignals[ct].set();
+    }
+    while(mWorkers.mRunningCount) {
+        mWorkers.mCompleteSignal.wait();
+    }
+}
+
 void Context::setPriority(int32_t p)
 {
     // Note: If we put this in the proper "background" policy
@@ -395,27 +594,49 @@
         // success; reset the priority as well
     }
 #else
-        setpriority(PRIO_PROCESS, mNativeThreadId, p);
+    setpriority(PRIO_PROCESS, mNativeThreadId, p);
+    for (uint32_t ct=0; ct < mWorkers.mCount; ct++) {
+        setpriority(PRIO_PROCESS, mWorkers.mNativeThreadId[ct], p);
+    }
 #endif
 }
 
-Context::Context(Device *dev, bool isGraphics, bool useDepth)
+Context::Context()
+{
+    mDev = NULL;
+    mRunning = false;
+    mExit = false;
+    mPaused = false;
+    mObjHead = NULL;
+    mError = RS_ERROR_NONE;
+    mErrorMsg = NULL;
+}
+
+Context * Context::createContext(Device *dev, const RsSurfaceConfig *sc)
+{
+    Context * rsc = new Context();
+    if (!rsc->initContext(dev, sc)) {
+        delete rsc;
+        return NULL;
+    }
+    return rsc;
+}
+
+bool Context::initContext(Device *dev, const RsSurfaceConfig *sc)
 {
     pthread_mutex_lock(&gInitMutex);
 
     dev->addContext(this);
     mDev = dev;
-    mRunning = false;
-    mExit = false;
-    mUseDepth = useDepth;
-    mPaused = false;
-    mObjHead = NULL;
-    mError = RS_ERROR_NONE;
-    mErrorMsg = NULL;
+    if (sc) {
+        mUserSurfaceConfig = *sc;
+    } else {
+        memset(&mUserSurfaceConfig, 0, sizeof(mUserSurfaceConfig));
+    }
 
     memset(&mEGL, 0, sizeof(mEGL));
     memset(&mGL, 0, sizeof(mGL));
-    mIsGraphicsContext = isGraphics;
+    mIsGraphicsContext = sc != NULL;
 
     int status;
     pthread_attr_t threadAttr;
@@ -425,10 +646,11 @@
         if (status) {
             LOGE("Failed to init thread tls key.");
             pthread_mutex_unlock(&gInitMutex);
-            return;
+            return false;
         }
     }
     gThreadTLSKeyCount++;
+
     pthread_mutex_unlock(&gInitMutex);
 
     // Global init done at this point.
@@ -436,26 +658,49 @@
     status = pthread_attr_init(&threadAttr);
     if (status) {
         LOGE("Failed to init thread attribute.");
-        return;
+        return false;
     }
 
     mWndSurface = NULL;
 
-    objDestroyOOBInit();
     timerInit();
     timerSet(RS_TIMER_INTERNAL);
 
-    LOGV("RS Launching thread");
+    int cpu = sysconf(_SC_NPROCESSORS_ONLN);
+    LOGV("RS Launching thread(s), reported CPU count %i", cpu);
+    if (cpu < 2) cpu = 0;
+
+    mWorkers.mCount = (uint32_t)cpu;
+    mWorkers.mThreadId = (pthread_t *) calloc(mWorkers.mCount, sizeof(pthread_t));
+    mWorkers.mNativeThreadId = (pid_t *) calloc(mWorkers.mCount, sizeof(pid_t));
+    mWorkers.mLaunchSignals = new Signal[mWorkers.mCount];
+    mWorkers.mLaunchCallback = NULL;
     status = pthread_create(&mThreadId, &threadAttr, threadProc, this);
     if (status) {
         LOGE("Failed to start rs context thread.");
+        return false;
     }
-
-    while(!mRunning) {
+    while(!mRunning && (mError == RS_ERROR_NONE)) {
         usleep(100);
     }
 
+    if (mError != RS_ERROR_NONE) {
+        return false;
+    }
+
+    mWorkers.mCompleteSignal.init();
+    mWorkers.mRunningCount = 0;
+    mWorkers.mLaunchCount = 0;
+    for (uint32_t ct=0; ct < mWorkers.mCount; ct++) {
+        status = pthread_create(&mWorkers.mThreadId[ct], &threadAttr, helperThreadProc, this);
+        if (status) {
+            mWorkers.mCount = ct;
+            LOGE("Created fewer than expected number of RS threads.");
+            break;
+        }
+    }
     pthread_attr_destroy(&threadAttr);
+    return true;
 }
 
 Context::~Context()
@@ -467,8 +712,6 @@
 
     mIO.shutdown();
     int status = pthread_join(mThreadId, &res);
-    mObjDestroy.mNeedToEmpty = true;
-    objDestroyOOBRun();
 
     // Global structure cleanup.
     pthread_mutex_lock(&gInitMutex);
@@ -481,8 +724,6 @@
         mDev = NULL;
     }
     pthread_mutex_unlock(&gInitMutex);
-
-    objDestroyOOBDestroy();
 }
 
 void Context::setSurface(uint32_t w, uint32_t h, ANativeWindow *sur)
@@ -491,28 +732,21 @@
 
     EGLBoolean ret;
     if (mEGL.mSurface != NULL) {
-        ret = eglMakeCurrent(mEGL.mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+        ret = eglMakeCurrent(mEGL.mDisplay, mEGL.mSurfaceDefault, mEGL.mSurfaceDefault, mEGL.mContext);
         checkEglError("eglMakeCurrent", ret);
 
         ret = eglDestroySurface(mEGL.mDisplay, mEGL.mSurface);
         checkEglError("eglDestroySurface", ret);
 
         mEGL.mSurface = NULL;
-        mEGL.mWidth = 0;
-        mEGL.mHeight = 0;
-        mWidth = 0;
-        mHeight = 0;
+        mWidth = 1;
+        mHeight = 1;
     }
 
     mWndSurface = sur;
     if (mWndSurface != NULL) {
-        bool first = false;
-        if (!mEGL.mContext) {
-            first = true;
-            pthread_mutex_lock(&gInitMutex);
-            initEGL(true);
-            pthread_mutex_unlock(&gInitMutex);
-        }
+        mWidth = w;
+        mHeight = h;
 
         mEGL.mSurface = eglCreateWindowSurface(mEGL.mDisplay, mEGL.mConfig, mWndSurface, NULL);
         checkEglError("eglCreateWindowSurface");
@@ -523,57 +757,7 @@
         ret = eglMakeCurrent(mEGL.mDisplay, mEGL.mSurface, mEGL.mSurface, mEGL.mContext);
         checkEglError("eglMakeCurrent", ret);
 
-        eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_WIDTH, &mEGL.mWidth);
-        eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_HEIGHT, &mEGL.mHeight);
-        mWidth = w;
-        mHeight = h;
-        mStateVertex.updateSize(this, w, h);
-
-        if ((int)mWidth != mEGL.mWidth || (int)mHeight != mEGL.mHeight) {
-            LOGE("EGL/Surface mismatch  EGL (%i x %i)  SF (%i x %i)", mEGL.mWidth, mEGL.mHeight, mWidth, mHeight);
-        }
-
-        if (first) {
-            mGL.mVersion = glGetString(GL_VERSION);
-            mGL.mVendor = glGetString(GL_VENDOR);
-            mGL.mRenderer = glGetString(GL_RENDERER);
-            mGL.mExtensions = glGetString(GL_EXTENSIONS);
-
-            //LOGV("EGL Version %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion);
-            LOGV("GL Version %s", mGL.mVersion);
-            //LOGV("GL Vendor %s", mGL.mVendor);
-            LOGV("GL Renderer %s", mGL.mRenderer);
-            //LOGV("GL Extensions %s", mGL.mExtensions);
-
-            const char *verptr = NULL;
-            if (strlen((const char *)mGL.mVersion) > 9) {
-                if (!memcmp(mGL.mVersion, "OpenGL ES-CM", 12)) {
-                    verptr = (const char *)mGL.mVersion + 12;
-                }
-                if (!memcmp(mGL.mVersion, "OpenGL ES ", 10)) {
-                    verptr = (const char *)mGL.mVersion + 9;
-                }
-            }
-
-            if (!verptr) {
-                LOGE("Error, OpenGL ES Lite not supported");
-            } else {
-                sscanf(verptr, " %i.%i", &mGL.mMajorVersion, &mGL.mMinorVersion);
-            }
-
-            glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &mGL.mMaxVertexAttribs);
-            glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &mGL.mMaxVertexUniformVectors);
-            glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGL.mMaxVertexTextureUnits);
-
-            glGetIntegerv(GL_MAX_VARYING_VECTORS, &mGL.mMaxVaryingVectors);
-            glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGL.mMaxTextureImageUnits);
-
-            glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mGL.mMaxFragmentTextureImageUnits);
-            glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGL.mMaxFragmentUniformVectors);
-
-            mGL.OES_texture_npot = NULL != strstr((const char *)mGL.mExtensions, "GL_OES_texture_npot");
-        }
-
+        mStateVertex.updateSize(this);
     }
 }
 
@@ -595,7 +779,7 @@
     mRootScript.set(s);
 }
 
-void Context::setFragmentStore(ProgramFragmentStore *pfs)
+void Context::setFragmentStore(ProgramStore *pfs)
 {
     rsAssert(mIsGraphicsContext);
     if (pfs == NULL) {
@@ -635,6 +819,16 @@
     }
 }
 
+void Context::setFont(Font *f)
+{
+    rsAssert(mIsGraphicsContext);
+    if (f == NULL) {
+        mFont.set(mStateFont.mDefault);
+    } else {
+        mFont.set(f);
+    }
+}
+
 void Context::assignName(ObjectBase *obj, const char *name, uint32_t len)
 {
     rsAssert(!obj->getName());
@@ -652,90 +846,13 @@
     }
 }
 
-ObjectBase * Context::lookupName(const char *name) const
-{
-    for(size_t ct=0; ct < mNames.size(); ct++) {
-        if (!strcmp(name, mNames[ct]->getName())) {
-            return mNames[ct];
-        }
-    }
-    return NULL;
-}
-
-void Context::appendNameDefines(String8 *str) const
-{
-    char buf[256];
-    for (size_t ct=0; ct < mNames.size(); ct++) {
-        str->append("#define NAMED_");
-        str->append(mNames[ct]->getName());
-        str->append(" ");
-        sprintf(buf, "%i\n", (int)mNames[ct]);
-        str->append(buf);
-    }
-}
-
-bool Context::objDestroyOOBInit()
-{
-    int status = pthread_mutex_init(&mObjDestroy.mMutex, NULL);
-    if (status) {
-        LOGE("Context::ObjDestroyOOBInit mutex init failure");
-        return false;
-    }
-    return true;
-}
-
-void Context::objDestroyOOBRun()
-{
-    if (mObjDestroy.mNeedToEmpty) {
-        int status = pthread_mutex_lock(&mObjDestroy.mMutex);
-        if (status) {
-            LOGE("Context::ObjDestroyOOBRun: error %i locking for OOBRun.", status);
-            return;
-        }
-
-        for (size_t ct = 0; ct < mObjDestroy.mDestroyList.size(); ct++) {
-            mObjDestroy.mDestroyList[ct]->decUserRef();
-        }
-        mObjDestroy.mDestroyList.clear();
-        mObjDestroy.mNeedToEmpty = false;
-
-        status = pthread_mutex_unlock(&mObjDestroy.mMutex);
-        if (status) {
-            LOGE("Context::ObjDestroyOOBRun: error %i unlocking for set condition.", status);
-        }
-    }
-}
-
-void Context::objDestroyOOBDestroy()
-{
-    rsAssert(!mObjDestroy.mNeedToEmpty);
-    pthread_mutex_destroy(&mObjDestroy.mMutex);
-}
-
-void Context::objDestroyAdd(ObjectBase *obj)
-{
-    int status = pthread_mutex_lock(&mObjDestroy.mMutex);
-    if (status) {
-        LOGE("Context::ObjDestroyOOBRun: error %i locking for OOBRun.", status);
-        return;
-    }
-
-    mObjDestroy.mNeedToEmpty = true;
-    mObjDestroy.mDestroyList.add(obj);
-
-    status = pthread_mutex_unlock(&mObjDestroy.mMutex);
-    if (status) {
-        LOGE("Context::ObjDestroyOOBRun: error %i unlocking for set condition.", status);
-    }
-}
-
 uint32_t Context::getMessageToClient(void *data, size_t *receiveLen, size_t bufferLen, bool wait)
 {
     //LOGE("getMessageToClient %i %i", bufferLen, wait);
+    *receiveLen = 0;
     if (!wait) {
         if (mIO.mToClient.isEmpty()) {
             // No message to get and not going to wait for one.
-            receiveLen = 0;
             return 0;
         }
     }
@@ -763,15 +880,19 @@
         return false;
     }
     if (!waitForSpace) {
-        if (mIO.mToClient.getFreeSpace() < len) {
+        if (!mIO.mToClient.makeSpaceNonBlocking(len + 8)) {
             // Not enough room, and not waiting.
             return false;
         }
     }
     //LOGE("sendMessageToClient 2");
-    void *p = mIO.mToClient.reserve(len);
-    memcpy(p, data, len);
-    mIO.mToClient.commit(cmdID, len);
+    if (len > 0) {
+        void *p = mIO.mToClient.reserve(len);
+        memcpy(p, data, len);
+        mIO.mToClient.commit(cmdID, len);
+    } else {
+        mIO.mToClient.commit(cmdID, 0);
+    }
     //LOGE("sendMessageToClient 3");
     return true;
 }
@@ -811,15 +932,14 @@
     LOGE("RS Context debug");
 
     LOGE(" EGL ver %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion);
-    LOGE(" EGL context %p  surface %p,  w=%i h=%i  Display=%p", mEGL.mContext,
-         mEGL.mSurface, mEGL.mWidth, mEGL.mHeight, mEGL.mDisplay);
+    LOGE(" EGL context %p  surface %p,  Display=%p", mEGL.mContext, mEGL.mSurface, mEGL.mDisplay);
     LOGE(" GL vendor: %s", mGL.mVendor);
     LOGE(" GL renderer: %s", mGL.mRenderer);
     LOGE(" GL Version: %s", mGL.mVersion);
     LOGE(" GL Extensions: %s", mGL.mExtensions);
     LOGE(" GL int Versions %i %i", mGL.mMajorVersion, mGL.mMinorVersion);
     LOGE(" RS width %i, height %i", mWidth, mHeight);
-    LOGE(" RS running %i, exit %i, useDepth %i, paused %i", mRunning, mExit, mUseDepth, mPaused);
+    LOGE(" RS running %i, exit %i, paused %i", mRunning, mExit, mPaused);
     LOGE(" RS pThreadID %li, nativeThreadID %i", mThreadId, mNativeThreadId);
 
     LOGV("MAX Textures %i, %i  %i", mGL.mMaxVertexTextureUnits, mGL.mMaxFragmentTextureImageUnits, mGL.mMaxTextureImageUnits);
@@ -834,6 +954,9 @@
 namespace android {
 namespace renderscript {
 
+void rsi_ContextFinish(Context *rsc)
+{
+}
 
 void rsi_ContextBindRootScript(Context *rsc, RsScript vs)
 {
@@ -853,9 +976,9 @@
     s->bindToContext(&rsc->mStateSampler, slot);
 }
 
-void rsi_ContextBindProgramFragmentStore(Context *rsc, RsProgramFragmentStore vpfs)
+void rsi_ContextBindProgramStore(Context *rsc, RsProgramStore vpfs)
 {
-    ProgramFragmentStore *pfs = static_cast<ProgramFragmentStore *>(vpfs);
+    ProgramStore *pfs = static_cast<ProgramStore *>(vpfs);
     rsc->setFragmentStore(pfs);
 }
 
@@ -877,15 +1000,21 @@
     rsc->setVertex(pv);
 }
 
+void rsi_ContextBindFont(Context *rsc, RsFont vfont)
+{
+    Font *font = static_cast<Font *>(vfont);
+    rsc->setFont(font);
+}
+
 void rsi_AssignName(Context *rsc, void * obj, const char *name, uint32_t len)
 {
     ObjectBase *ob = static_cast<ObjectBase *>(obj);
     rsc->assignName(ob, name, len);
 }
 
-void rsi_ObjDestroy(Context *rsc, void *obj)
+void rsi_ObjDestroy(Context *rsc, void *optr)
 {
-    ObjectBase *ob = static_cast<ObjectBase *>(obj);
+    ObjectBase *ob = static_cast<ObjectBase *>(optr);
     rsc->removeName(ob);
     ob->decUserRef();
 }
@@ -932,15 +1061,16 @@
 {
     LOGV("rsContextCreate %p", vdev);
     Device * dev = static_cast<Device *>(vdev);
-    Context *rsc = new Context(dev, false, false);
+    Context *rsc = Context::createContext(dev, NULL);
     return rsc;
 }
 
-RsContext rsContextCreateGL(RsDevice vdev, uint32_t version, bool useDepth)
+RsContext rsContextCreateGL(RsDevice vdev, uint32_t version, RsSurfaceConfig sc)
 {
-    LOGV("rsContextCreateGL %p, %i", vdev, useDepth);
+    LOGV("rsContextCreateGL %p", vdev);
     Device * dev = static_cast<Device *>(vdev);
-    Context *rsc = new Context(dev, true, useDepth);
+    Context *rsc = Context::createContext(dev, &sc);
+    LOGV("rsContextCreateGL ret %p ", rsc);
     return rsc;
 }
 
@@ -950,12 +1080,6 @@
     delete rsc;
 }
 
-void rsObjDestroyOOB(RsContext vrsc, void *obj)
-{
-    Context * rsc = static_cast<Context *>(vrsc);
-    rsc->objDestroyAdd(static_cast<ObjectBase *>(obj));
-}
-
 uint32_t rsContextGetMessage(RsContext vrsc, void *data, size_t *receiveLen, size_t bufferLen, bool wait)
 {
     Context * rsc = static_cast<Context *>(vrsc);
@@ -974,3 +1098,10 @@
     rsc->deinitToClient();
 }
 
+// Only to be called at a3d load time, before object is visible to user
+// not thread safe
+void rsaGetName(RsContext con, void * obj, const char **name)
+{
+    ObjectBase *ob = static_cast<ObjectBase *>(obj);
+    (*name) = ob->getName();
+}
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 709730e..2017ceb7 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -18,21 +18,21 @@
 #define ANDROID_RS_CONTEXT_H
 
 #include "rsUtils.h"
+#include "rsMutex.h"
 
 #include "rsThreadIO.h"
 #include "rsType.h"
 #include "rsMatrix.h"
 #include "rsAllocation.h"
-#include "rsSimpleMesh.h"
 #include "rsMesh.h"
 #include "rsDevice.h"
 #include "rsScriptC.h"
 #include "rsAllocation.h"
 #include "rsAdapter.h"
 #include "rsSampler.h"
-#include "rsLight.h"
+#include "rsFont.h"
 #include "rsProgramFragment.h"
-#include "rsProgramFragmentStore.h"
+#include "rsProgramStore.h"
 #include "rsProgramRaster.h"
 #include "rsProgramVertex.h"
 #include "rsShaderCache.h"
@@ -48,10 +48,28 @@
 
 namespace renderscript {
 
+#if 0
+#define CHECK_OBJ(o) { \
+    GET_TLS(); \
+    if(!ObjectBase::isValid(rsc, (const ObjectBase *)o)) {  \
+        LOGE("Bad object %p at %s, %i", o, __FILE__, __LINE__);  \
+    } \
+}
+#define CHECK_OBJ_OR_NULL(o) { \
+    GET_TLS(); \
+    if(o && !ObjectBase::isValid(rsc, (const ObjectBase *)o)) {  \
+        LOGE("Bad object %p at %s, %i", o, __FILE__, __LINE__);  \
+    } \
+}
+#else
+#define CHECK_OBJ(o)
+#define CHECK_OBJ_OR_NULL(o)
+#endif
+
 class Context
 {
 public:
-    Context(Device *, bool isGraphics, bool useDepth);
+    static Context * createContext(Device *, const RsSurfaceConfig *sc);
     ~Context();
 
     static pthread_key_t gThreadTLSKey;
@@ -63,18 +81,21 @@
         Context * mContext;
         Script * mScript;
     };
+    ScriptTLSStruct *mTlsStruct;
+    RsSurfaceConfig mUserSurfaceConfig;
 
+    typedef void (*WorkerCallback_t)(void *usr, uint32_t idx);
 
     //StructuredAllocationContext mStateAllocation;
     ElementState mStateElement;
     TypeState mStateType;
     SamplerState mStateSampler;
     ProgramFragmentState mStateFragment;
-    ProgramFragmentStoreState mStateFragmentStore;
+    ProgramStoreState mStateFragmentStore;
     ProgramRasterState mStateRaster;
     ProgramVertexState mStateVertex;
-    LightState mStateLight;
     VertexArrayState mStateVertexArray;
+    FontState mStateFont;
 
     ScriptCState mScriptC;
     ShaderCache mShaderCache;
@@ -84,16 +105,19 @@
     void setRaster(ProgramRaster *);
     void setVertex(ProgramVertex *);
     void setFragment(ProgramFragment *);
-    void setFragmentStore(ProgramFragmentStore *);
+    void setFragmentStore(ProgramStore *);
+    void setFont(Font *);
 
     void updateSurface(void *sur);
 
     const ProgramFragment * getFragment() {return mFragment.get();}
-    const ProgramFragmentStore * getFragmentStore() {return mFragmentStore.get();}
+    const ProgramStore * getFragmentStore() {return mFragmentStore.get();}
     const ProgramRaster * getRaster() {return mRaster.get();}
     const ProgramVertex * getVertex() {return mVertex.get();}
+    Font * getFont() {return mFont.get();}
 
     bool setupCheck();
+    void setupProgramStore();
     bool checkDriver() const {return mEGL.mSurface != 0;}
 
     void pause();
@@ -103,12 +127,10 @@
 
     void assignName(ObjectBase *obj, const char *name, uint32_t len);
     void removeName(ObjectBase *obj);
-    ObjectBase * lookupName(const char *name) const;
-    void appendNameDefines(String8 *str) const;
 
     uint32_t getMessageToClient(void *data, size_t *receiveLen, size_t bufferLen, bool wait);
     bool sendMessageToClient(void *data, uint32_t cmdID, size_t len, bool waitForSpace);
-    uint32_t runScript(Script *s, uint32_t launchID);
+    uint32_t runScript(Script *s);
 
     void initToClient();
     void deinitToClient();
@@ -119,19 +141,21 @@
     ProgramVertex * getDefaultProgramVertex() const {
         return mStateVertex.mDefault.get();
     }
-    ProgramFragmentStore * getDefaultProgramFragmentStore() const {
+    ProgramStore * getDefaultProgramStore() const {
         return mStateFragmentStore.mDefault.get();
     }
     ProgramRaster * getDefaultProgramRaster() const {
         return mStateRaster.mDefault.get();
     }
+    Font* getDefaultFont() const {
+        return mStateFont.mDefault.get();
+    }
 
-    uint32_t getWidth() const {return mEGL.mWidth;}
-    uint32_t getHeight() const {return mEGL.mHeight;}
+    uint32_t getWidth() const {return mWidth;}
+    uint32_t getHeight() const {return mHeight;}
 
 
     ThreadIO mIO;
-    void objDestroyAdd(ObjectBase *);
 
     // Timers
     enum Timers {
@@ -148,24 +172,32 @@
     void timerPrint();
     void timerFrame();
 
-    bool checkVersion1_1() const {return (mGL.mMajorVersion > 1) || (mGL.mMinorVersion >= 1); }
-    bool checkVersion2_0() const {return mGL.mMajorVersion >= 2; }
-
     struct {
         bool mLogTimes;
         bool mLogScripts;
         bool mLogObjects;
         bool mLogShaders;
+        bool mLogShadersAttr;
+        bool mLogShadersUniforms;
+        bool mLogVisual;
     } props;
 
     void dumpDebug() const;
     void checkError(const char *) const;
     const char * getError(RsError *);
-    void setError(RsError e, const char *msg);
+    void setError(RsError e, const char *msg = NULL);
 
     mutable const ObjectBase * mObjHead;
 
     bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;}
+    bool ext_GL_NV_texture_npot_2D_mipmap() const {return mGL.GL_NV_texture_npot_2D_mipmap;}
+    float ext_texture_max_aniso() const {return mGL.EXT_texture_max_aniso; }
+    uint32_t getMaxFragmentTextures() const {return mGL.mMaxFragmentTextureImageUnits;}
+    uint32_t getMaxFragmentUniformVectors() const {return mGL.mMaxFragmentUniformVectors;}
+    uint32_t getMaxVertexUniformVectors() const {return mGL.mMaxVertexUniformVectors;}
+
+    void launchThreads(WorkerCallback_t cbk, void *data);
+    uint32_t getWorkerPoolSize() const {return (uint32_t)mWorkers.mCount;}
 
 protected:
     Device *mDev;
@@ -177,8 +209,7 @@
         EGLConfig mConfig;
         EGLContext mContext;
         EGLSurface mSurface;
-        EGLint mWidth;
-        EGLint mHeight;
+        EGLSurface mSurfaceDefault;
         EGLDisplay mDisplay;
     } mEGL;
 
@@ -202,6 +233,8 @@
         int32_t mMaxVertexTextureUnits;
 
         bool OES_texture_npot;
+        bool GL_NV_texture_npot_2D_mipmap;
+        float EXT_texture_max_aniso;
     } mGL;
 
     uint32_t mWidth;
@@ -211,7 +244,6 @@
 
     bool mRunning;
     bool mExit;
-    bool mUseDepth;
     bool mPaused;
     RsError mError;
     const char *mErrorMsg;
@@ -219,32 +251,41 @@
     pthread_t mThreadId;
     pid_t mNativeThreadId;
 
+    struct Workers {
+        volatile int mRunningCount;
+        volatile int mLaunchCount;
+        uint32_t mCount;
+        pthread_t *mThreadId;
+        pid_t *mNativeThreadId;
+        Signal mCompleteSignal;
+
+        Signal *mLaunchSignals;
+        WorkerCallback_t mLaunchCallback;
+        void *mLaunchData;
+    };
+    Workers mWorkers;
+
     ObjectBaseRef<Script> mRootScript;
     ObjectBaseRef<ProgramFragment> mFragment;
     ObjectBaseRef<ProgramVertex> mVertex;
-    ObjectBaseRef<ProgramFragmentStore> mFragmentStore;
+    ObjectBaseRef<ProgramStore> mFragmentStore;
     ObjectBaseRef<ProgramRaster> mRaster;
+    ObjectBaseRef<Font> mFont;
 
-
-    struct ObjDestroyOOB {
-        pthread_mutex_t mMutex;
-        Vector<ObjectBase *> mDestroyList;
-        bool mNeedToEmpty;
-    };
-    ObjDestroyOOB mObjDestroy;
-    bool objDestroyOOBInit();
-    void objDestroyOOBRun();
-    void objDestroyOOBDestroy();
+    void displayDebugStats();
 
 private:
     Context();
+    bool initContext(Device *, const RsSurfaceConfig *sc);
 
-    void initEGL(bool useGL2);
+
+    bool initGLThread();
     void deinitEGL();
 
     uint32_t runRootScript();
 
     static void * threadProc(void *);
+    static void * helperThreadProc(void *);
 
     ANativeWindow *mWndSurface;
 
@@ -258,6 +299,9 @@
     uint32_t mTimeMSLastFrame;
     uint32_t mTimeMSLastScript;
     uint32_t mTimeMSLastSwap;
+    uint32_t mAverageFPSFrameCount;
+    uint64_t mAverageFPSStartTime;
+    uint32_t mAverageFPS;
 };
 
 }
diff --git a/libs/rs/rsContextHostStub.h b/libs/rs/rsContextHostStub.h
new file mode 100644
index 0000000..f3e9dab
--- /dev/null
+++ b/libs/rs/rsContextHostStub.h
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_CONTEXT_HOST_STUB_H
+#define ANDROID_RS_CONTEXT_HOST_STUB_H
+
+#include "rsUtils.h"
+//#include "rsMutex.h"
+
+//#include "rsThreadIO.h"
+#include "rsType.h"
+#include "rsMatrix.h"
+#include "rsAllocation.h"
+#include "rsMesh.h"
+//#include "rsDevice.h"
+#include "rsScriptC.h"
+#include "rsAllocation.h"
+#include "rsAdapter.h"
+#include "rsSampler.h"
+#include "rsProgramFragment.h"
+#include "rsProgramStore.h"
+#include "rsProgramRaster.h"
+#include "rsProgramVertex.h"
+#include "rsShaderCache.h"
+#include "rsVertexArray.h"
+
+//#include "rsgApiStructs.h"
+//#include "rsLocklessFifo.h"
+
+//#include <ui/egl/android_natives.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+namespace renderscript {
+
+class Device;
+
+class Context
+{
+public:
+    Context(Device *, bool isGraphics, bool useDepth) {
+        mObjHead = NULL;
+    }
+    ~Context() {
+    }
+
+
+    //StructuredAllocationContext mStateAllocation;
+    ElementState mStateElement;
+    TypeState mStateType;
+    SamplerState mStateSampler;
+    //ProgramFragmentState mStateFragment;
+    ProgramStoreState mStateFragmentStore;
+    //ProgramRasterState mStateRaster;
+    //ProgramVertexState mStateVertex;
+    VertexArrayState mStateVertexArray;
+
+    //ScriptCState mScriptC;
+    ShaderCache mShaderCache;
+
+    RsSurfaceConfig mUserSurfaceConfig;
+
+    //bool setupCheck();
+    bool checkDriver() const {return false;}
+
+    ProgramFragment * getDefaultProgramFragment() const {
+        return NULL;
+    }
+    ProgramVertex * getDefaultProgramVertex() const {
+        return NULL;
+    }
+    ProgramStore * getDefaultProgramStore() const {
+        return NULL;
+    }
+    ProgramRaster * getDefaultProgramRaster() const {
+        return NULL;
+    }
+
+    uint32_t getWidth() const {return 0;}
+    uint32_t getHeight() const {return 0;}
+
+    // Timers
+    enum Timers {
+        RS_TIMER_IDLE,
+        RS_TIMER_INTERNAL,
+        RS_TIMER_SCRIPT,
+        RS_TIMER_CLEAR_SWAP,
+        _RS_TIMER_TOTAL
+    };
+
+    bool checkVersion1_1() const {return false; }
+    bool checkVersion2_0() const {return false; }
+
+    struct {
+        bool mLogTimes;
+        bool mLogScripts;
+        bool mLogObjects;
+        bool mLogShaders;
+        bool mLogShadersAttr;
+        bool mLogShadersUniforms;
+        bool mLogVisual;
+    } props;
+
+    void dumpDebug() const {    }
+    void checkError(const char *) const {  };
+    void setError(RsError e, const char *msg = NULL) {  }
+
+    mutable const ObjectBase * mObjHead;
+
+    bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;}
+    float ext_texture_max_aniso() const {return 1.0f;}
+    uint32_t getMaxFragmentTextures() const {return mGL.mMaxFragmentTextureImageUnits;}
+    uint32_t getMaxFragmentUniformVectors() const {return mGL.mMaxFragmentUniformVectors;}
+    uint32_t getMaxVertexUniformVectors() const {return mGL.mMaxVertexUniformVectors;}
+
+protected:
+
+    struct {
+        const uint8_t * mVendor;
+        const uint8_t * mRenderer;
+        const uint8_t * mVersion;
+        const uint8_t * mExtensions;
+
+        uint32_t mMajorVersion;
+        uint32_t mMinorVersion;
+
+        int32_t mMaxVaryingVectors;
+        int32_t mMaxTextureImageUnits;
+
+        int32_t mMaxFragmentTextureImageUnits;
+        int32_t mMaxFragmentUniformVectors;
+
+        int32_t mMaxVertexAttribs;
+        int32_t mMaxVertexUniformVectors;
+        int32_t mMaxVertexTextureUnits;
+
+        bool OES_texture_npot;
+    } mGL;
+
+};
+
+}
+}
+#endif
diff --git a/libs/rs/rsDevice.cpp b/libs/rs/rsDevice.cpp
index b670ad4..a96b114 100644
--- a/libs/rs/rsDevice.cpp
+++ b/libs/rs/rsDevice.cpp
@@ -15,7 +15,11 @@
  */
 
 #include "rsDevice.h"
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
+#else
+#include "rsContextHostStub.h"
+#endif
 
 using namespace android;
 using namespace android::renderscript;
@@ -33,7 +37,7 @@
 
 void Device::addContext(Context *rsc)
 {
-    mContexts.add(rsc);
+    mContexts.push(rsc);
 }
 
 void Device::removeContext(Context *rsc)
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index 6288bc4..d207dcf 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -14,9 +14,14 @@
  * limitations under the License.
  */
 
-#include "rsContext.h"
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
+#include "rsContext.h"
 #include <GLES/gl.h>
+#else
+#include "rsContextHostStub.h"
+#include <OpenGL/gl.h>
+#endif
 
 using namespace android;
 using namespace android::renderscript;
@@ -25,10 +30,9 @@
 Element::Element(Context *rsc) : ObjectBase(rsc)
 {
     mBits = 0;
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     mFields = NULL;
     mFieldCount = 0;
+    mHasReference = false;
 }
 
 
@@ -48,6 +52,7 @@
     delete [] mFields;
     mFields = NULL;
     mFieldCount = 0;
+    mHasReference = false;
 }
 
 size_t Element::getSizeBits() const
@@ -58,31 +63,117 @@
 
     size_t total = 0;
     for (size_t ct=0; ct < mFieldCount; ct++) {
-        total += mFields[ct].e->mBits;
+        total += mFields[ct].e->mBits * mFields[ct].arraySize;
     }
     return total;
 }
 
-size_t Element::getFieldOffsetBits(uint32_t componentNumber) const
-{
-    size_t offset = 0;
-    for (uint32_t ct = 0; ct < componentNumber; ct++) {
-        offset += mFields[ct].e->mBits;
-    }
-    return offset;
-}
-
 void Element::dumpLOGV(const char *prefix) const
 {
     ObjectBase::dumpLOGV(prefix);
-    LOGV("%s   Element: components %i,  size %i", prefix, mFieldCount, mBits);
+    LOGV("%s Element: fieldCount: %i,  size bytes: %i", prefix, mFieldCount, getSizeBytes());
     for (uint32_t ct = 0; ct < mFieldCount; ct++) {
-        char buf[1024];
-        sprintf(buf, "%s component %i: ", prefix, ct);
-        //mComponents[ct]->dumpLOGV(buf);
+        LOGV("%s Element field index: %u ------------------", prefix, ct);
+        LOGV("%s name: %s, offsetBits: %u, arraySize: %u",
+             prefix, mFields[ct].name.string(), mFields[ct].offsetBits, mFields[ct].arraySize);
+        mFields[ct].e->dumpLOGV(prefix);
     }
 }
 
+void Element::serialize(OStream *stream) const
+{
+    // Need to identify ourselves
+    stream->addU32((uint32_t)getClassId());
+
+    String8 name(getName());
+    stream->addString(&name);
+
+    mComponent.serialize(stream);
+
+    // Now serialize all the fields
+    stream->addU32(mFieldCount);
+    for(uint32_t ct = 0; ct < mFieldCount; ct++) {
+        stream->addString(&mFields[ct].name);
+        stream->addU32(mFields[ct].arraySize);
+        mFields[ct].e->serialize(stream);
+    }
+}
+
+Element *Element::createFromStream(Context *rsc, IStream *stream)
+{
+    // First make sure we are reading the correct object
+    RsA3DClassID classID = (RsA3DClassID)stream->loadU32();
+    if(classID != RS_A3D_CLASS_ID_ELEMENT) {
+        LOGE("element loading skipped due to invalid class id\n");
+        return NULL;
+    }
+
+    String8 name;
+    stream->loadString(&name);
+
+    Element *elem = new Element(rsc);
+    elem->mComponent.loadFromStream(stream);
+    elem->mBits = elem->mComponent.getBits();
+    elem->mHasReference = elem->mComponent.isReference();
+
+    elem->mFieldCount = stream->loadU32();
+    if(elem->mFieldCount) {
+        uint32_t offset = 0;
+        elem->mFields = new ElementField_t [elem->mFieldCount];
+        for(uint32_t ct = 0; ct < elem->mFieldCount; ct ++) {
+            stream->loadString(&elem->mFields[ct].name);
+            elem->mFields[ct].arraySize = stream->loadU32();
+            Element *fieldElem = Element::createFromStream(rsc, stream);
+            elem->mFields[ct].e.set(fieldElem);
+            elem->mFields[ct].offsetBits = offset;
+            offset += fieldElem->getSizeBits();
+            // Check if our sub-elements have references
+            if(fieldElem->mHasReference) {
+                elem->mHasReference = true;
+            }
+        }
+    }
+
+    // We need to check if this already exists
+    for (uint32_t ct=0; ct < rsc->mStateElement.mElements.size(); ct++) {
+        Element *ee = rsc->mStateElement.mElements[ct];
+        if(ee->isEqual(elem)) {
+            ObjectBase::checkDelete(elem);
+            ee->incUserRef();
+            return ee;
+        }
+    }
+
+    rsc->mStateElement.mElements.push(elem);
+    return elem;
+}
+
+bool Element::isEqual(const Element *other) const {
+    if(other == NULL) {
+        return false;
+    }
+    if (!other->getFieldCount() && !mFieldCount) {
+        if((other->getType() == getType()) &&
+           (other->getKind() == getKind()) &&
+           (other->getComponent().getIsNormalized() == getComponent().getIsNormalized()) &&
+           (other->getComponent().getVectorSize() == getComponent().getVectorSize())) {
+            return true;
+        }
+        return false;
+    }
+    if (other->getFieldCount() == mFieldCount) {
+        for (uint32_t i=0; i < mFieldCount; i++) {
+            if ((!other->mFields[i].e->isEqual(mFields[i].e.get())) ||
+                (other->mFields[i].name.length() != mFields[i].name.length()) ||
+                (other->mFields[i].name != mFields[i].name) ||
+                (other->mFields[i].arraySize != mFields[i].arraySize)) {
+                return false;
+            }
+        }
+        return true;
+    }
+    return false;
+}
 
 const Element * Element::create(Context *rsc, RsDataType dt, RsDataKind dk,
                             bool isNorm, uint32_t vecSize)
@@ -104,12 +195,13 @@
     Element *e = new Element(rsc);
     e->mComponent.set(dt, dk, isNorm, vecSize);
     e->mBits = e->mComponent.getBits();
+    e->mHasReference = e->mComponent.isReference();
     rsc->mStateElement.mElements.push(e);
     return e;
 }
 
 const Element * Element::create(Context *rsc, size_t count, const Element **ein,
-                            const char **nin, const size_t * lengths)
+                            const char **nin, const size_t * lengths, const uint32_t *asin)
 {
     // Look for an existing match.
     for (uint32_t ct=0; ct < rsc->mStateElement.mElements.size(); ct++) {
@@ -119,7 +211,8 @@
             for (uint32_t i=0; i < count; i++) {
                 if ((ee->mFields[i].e.get() != ein[i]) ||
                     (ee->mFields[i].name.length() != lengths[i]) ||
-                    (ee->mFields[i].name != nin[i])) {
+                    (ee->mFields[i].name != nin[i]) ||
+                    (ee->mFields[i].arraySize != asin[i])) {
                     match = false;
                     break;
                 }
@@ -134,54 +227,23 @@
     Element *e = new Element(rsc);
     e->mFields = new ElementField_t [count];
     e->mFieldCount = count;
+    size_t bits = 0;
     for (size_t ct=0; ct < count; ct++) {
         e->mFields[ct].e.set(ein[ct]);
         e->mFields[ct].name.setTo(nin[ct], lengths[ct]);
+        e->mFields[ct].offsetBits = bits;
+        e->mFields[ct].arraySize = asin[ct];
+        bits += ein[ct]->getSizeBits();
+
+        if (ein[ct]->mHasReference) {
+            e->mHasReference = true;
+        }
     }
 
     rsc->mStateElement.mElements.push(e);
     return e;
 }
 
-String8 Element::getCStructBody(uint32_t indent) const
-{
-    String8 si;
-    for (uint32_t ct=0; ct < indent; ct++) {
-        si.append(" ");
-    }
-
-    String8 s(si);
-    s.append("{\n");
-    for (uint32_t ct = 0; ct < mFieldCount; ct++) {
-        s.append(si);
-        s.append(mFields[ct].e->getCType(indent+4));
-        s.append(" ");
-        s.append(mFields[ct].name);
-        s.append(";\n");
-    }
-    s.append(si);
-    s.append("}");
-    return s;
-}
-
-String8 Element::getCType(uint32_t indent) const
-{
-    String8 s;
-    for (uint32_t ct=0; ct < indent; ct++) {
-        s.append(" ");
-    }
-
-    if (!mFieldCount) {
-        // Basic component.
-        s.append(mComponent.getCType());
-    } else {
-        s.append("struct ");
-        s.append(getCStructBody(indent));
-    }
-
-    return s;
-}
-
 String8 Element::getGLSLType(uint32_t indent) const
 {
     String8 s;
@@ -201,10 +263,60 @@
     return s;
 }
 
+void Element::incRefs(const void *ptr) const
+{
+    if (!mFieldCount) {
+        if (mComponent.isReference()) {
+            ObjectBase *const*obp = static_cast<ObjectBase *const*>(ptr);
+            ObjectBase *ob = obp[0];
+            if (ob) ob->incSysRef();
+        }
+        return;
+    }
+
+    const uint8_t *p = static_cast<const uint8_t *>(ptr);
+    for (uint32_t i=0; i < mFieldCount; i++) {
+        if (mFields[i].e->mHasReference) {
+            p = &p[mFields[i].offsetBits >> 3];
+            for (uint32_t ct=0; ct < mFields[i].arraySize; ct++) {
+                mFields[i].e->incRefs(p);
+                p += mFields[i].e->getSizeBytes();
+            }
+        }
+    }
+}
+
+void Element::decRefs(const void *ptr) const
+{
+    if (!mFieldCount) {
+        if (mComponent.isReference()) {
+            ObjectBase *const*obp = static_cast<ObjectBase *const*>(ptr);
+            ObjectBase *ob = obp[0];
+            if (ob) ob->decSysRef();
+        }
+        return;
+    }
+
+    const uint8_t *p = static_cast<const uint8_t *>(ptr);
+    for (uint32_t i=0; i < mFieldCount; i++) {
+        if (mFields[i].e->mHasReference) {
+            p = &p[mFields[i].offsetBits >> 3];
+            for (uint32_t ct=0; ct < mFields[i].arraySize; ct++) {
+                mFields[i].e->decRefs(p);
+                p += mFields[i].e->getSizeBytes();
+            }
+        }
+    }
+}
 
 
 ElementState::ElementState()
 {
+    const uint32_t initialCapacity = 32;
+    mBuilderElements.setCapacity(initialCapacity);
+    mBuilderNameStrings.setCapacity(initialCapacity);
+    mBuilderNameLengths.setCapacity(initialCapacity);
+    mBuilderArrays.setCapacity(initialCapacity);
 }
 
 ElementState::~ElementState()
@@ -212,6 +324,29 @@
     rsAssert(!mElements.size());
 }
 
+void ElementState::elementBuilderBegin() {
+    mBuilderElements.clear();
+    mBuilderNameStrings.clear();
+    mBuilderNameLengths.clear();
+    mBuilderArrays.clear();
+}
+
+void ElementState::elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize) {
+    mBuilderElements.push(e);
+    mBuilderNameStrings.push(nameStr);
+    mBuilderNameLengths.push(strlen(nameStr));
+    mBuilderArrays.push(arraySize);
+
+}
+
+const Element *ElementState::elementBuilderCreate(Context *rsc) {
+    return Element::create(rsc, mBuilderElements.size(),
+                                &(mBuilderElements.editArray()[0]),
+                                &(mBuilderNameStrings.editArray()[0]),
+                                mBuilderNameLengths.editArray(),
+                                mBuilderArrays.editArray());
+}
+
 
 /////////////////////////////////////////
 //
@@ -235,14 +370,41 @@
                              size_t count,
                              const RsElement * ein,
                              const char ** names,
-                             const size_t * nameLengths)
+                             const size_t * nameLengths,
+                             const uint32_t * arraySizes)
 {
     //LOGE("rsi_ElementCreate2 %i", count);
-    const Element *e = Element::create(rsc, count, (const Element **)ein, names, nameLengths);
+    const Element *e = Element::create(rsc, count, (const Element **)ein, names, nameLengths, arraySizes);
     e->incUserRef();
     return (RsElement)e;
 }
 
+}
+}
+
+void rsaElementGetNativeData(RsContext con, RsElement elem, uint32_t *elemData, uint32_t elemDataSize)
+{
+    rsAssert(elemDataSize == 5);
+    // we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
+    Element *e = static_cast<Element *>(elem);
+
+    (*elemData++) = (uint32_t)e->getType();
+    (*elemData++) = (uint32_t)e->getKind();
+    (*elemData++) = e->getComponent().getIsNormalized() ? 1 : 0;
+    (*elemData++) = e->getComponent().getVectorSize();
+    (*elemData++) = e->getFieldCount();
 
 }
+
+void rsaElementGetSubElements(RsContext con, RsElement elem, uint32_t *ids, const char **names, uint32_t dataSize)
+{
+    Element *e = static_cast<Element *>(elem);
+    rsAssert(e->getFieldCount() == dataSize);
+
+    for(uint32_t i = 0; i < dataSize; i ++) {
+        e->getField(i)->incUserRef();
+        ids[i] = (uint32_t)e->getField(i);
+        names[i] = e->getFieldName(i);
+    }
+
 }
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index 02a1ca2..70e2619 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -40,30 +40,40 @@
         return (getSizeBits() + 7) >> 3;
     }
 
-    size_t getFieldOffsetBits(uint32_t componentNumber) const;
+    size_t getFieldOffsetBits(uint32_t componentNumber) const {
+        return mFields[componentNumber].offsetBits;
+    }
     size_t getFieldOffsetBytes(uint32_t componentNumber) const {
-        return (getFieldOffsetBits(componentNumber) + 7) >> 3;
+        return mFields[componentNumber].offsetBits >> 3;
     }
 
     uint32_t getFieldCount() const {return mFieldCount;}
     const Element * getField(uint32_t idx) const {return mFields[idx].e.get();}
     const char * getFieldName(uint32_t idx) const {return mFields[idx].name.string();}
+    uint32_t getFieldArraySize(uint32_t idx) const {return mFields[idx].arraySize;}
 
     const Component & getComponent() const {return mComponent;}
     RsDataType getType() const {return mComponent.getType();}
     RsDataKind getKind() const {return mComponent.getKind();}
     uint32_t getBits() const {return mBits;}
 
-    String8 getCType(uint32_t indent=0) const;
-    String8 getCStructBody(uint32_t indent=0) const;
     String8 getGLSLType(uint32_t indent=0) const;
 
     void dumpLOGV(const char *prefix) const;
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_ELEMENT; }
+    static Element *createFromStream(Context *rsc, IStream *stream);
 
     static const Element * create(Context *rsc, RsDataType dt, RsDataKind dk,
                             bool isNorm, uint32_t vecSize);
     static const Element * create(Context *rsc, size_t count, const Element **,
-                            const char **, const size_t * lengths);
+                            const char **, const size_t * lengths, const uint32_t *asin);
+
+    void incRefs(const void *) const;
+    void decRefs(const void *) const;
+    bool getHasReferences() const {return mHasReference;}
+
+    bool isEqual(const Element *other) const;
 
 protected:
     // deallocate any components that are part of this element.
@@ -72,9 +82,12 @@
     typedef struct {
         String8 name;
         ObjectBaseRef<const Element> e;
+        uint32_t offsetBits;
+        uint32_t arraySize;
     } ElementField_t;
     ElementField_t *mFields;
     size_t mFieldCount;
+    bool mHasReference;
 
 
     Element(Context *);
@@ -89,8 +102,17 @@
     ElementState();
     ~ElementState();
 
+    void elementBuilderBegin();
+    void elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize);
+    const Element *elementBuilderCreate(Context *rsc);
+
     // Cache of all existing elements.
-    Vector<const Element *> mElements;
+    Vector<Element *> mElements;
+private:
+    Vector<const Element *> mBuilderElements;
+    Vector<const char*> mBuilderNameStrings;
+    Vector<size_t> mBuilderNameLengths;
+    Vector<uint32_t> mBuilderArrays;
 };
 
 
diff --git a/libs/rs/rsFileA3D.cpp b/libs/rs/rsFileA3D.cpp
index e3272c5..e4c6dbd 100644
--- a/libs/rs/rsFileA3D.cpp
+++ b/libs/rs/rsFileA3D.cpp
@@ -15,81 +15,178 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
+#else
+#include "rsContextHostStub.h"
+#endif
 
-
-#include <utils/String8.h>
 #include "rsFileA3D.h"
 
 #include "rsMesh.h"
+#include "rsAnimation.h"
+
 
 using namespace android;
 using namespace android::renderscript;
 
-
-
-FileA3D::FileA3D()
+FileA3D::FileA3D(Context *rsc) : ObjectBase(rsc)
 {
-    mRsc = NULL;
+    mAlloc = NULL;
+    mData = NULL;
+    mWriteStream = NULL;
+    mReadStream = NULL;
+
+    mMajorVersion = 0;
+    mMinorVersion = 1;
+    mDataSize = 0;
 }
 
 FileA3D::~FileA3D()
 {
+    for(size_t i = 0; i < mIndex.size(); i ++) {
+        delete mIndex[i];
+    }
+    for(size_t i = 0; i < mWriteIndex.size(); i ++) {
+        delete mWriteIndex[i];
+    }
+    if(mWriteStream) {
+        delete mWriteStream;
+    }
+    if(mReadStream) {
+        delete mWriteStream;
+    }
+    if(mAlloc) {
+        free(mAlloc);
+    }
 }
 
-bool FileA3D::load(Context *rsc, FILE *f)
+void FileA3D::parseHeader(IStream *headerStream)
+{
+    mMajorVersion = headerStream->loadU32();
+    mMinorVersion = headerStream->loadU32();
+    uint32_t flags = headerStream->loadU32();
+    mUse64BitOffsets = (flags & 1) != 0;
+
+    uint32_t numIndexEntries = headerStream->loadU32();
+    for(uint32_t i = 0; i < numIndexEntries; i ++) {
+        A3DIndexEntry *entry = new A3DIndexEntry();
+        headerStream->loadString(&entry->mObjectName);
+        LOGV("Header data, entry name = %s", entry->mObjectName.string());
+        entry->mType = (RsA3DClassID)headerStream->loadU32();
+        if(mUse64BitOffsets){
+            entry->mOffset = headerStream->loadOffset();
+            entry->mLength = headerStream->loadOffset();
+        }
+        else {
+            entry->mOffset = headerStream->loadU32();
+            entry->mLength = headerStream->loadU32();
+        }
+        entry->mRsObj = NULL;
+        mIndex.push(entry);
+    }
+}
+
+bool FileA3D::load(const void *data, size_t length)
+{
+    const uint8_t *localData = (const uint8_t *)data;
+
+    size_t lengthRemaining = length;
+    size_t magicStrLen = 12;
+    if ((length < magicStrLen) ||
+        memcmp(data, "Android3D_ff", magicStrLen)) {
+        return false;
+    }
+
+    localData += magicStrLen;
+    lengthRemaining -= magicStrLen;
+
+    // Next we get our header size
+    uint64_t headerSize = 0;
+    if(lengthRemaining < sizeof(headerSize)) {
+        return false;
+    }
+
+    memcpy(&headerSize, localData, sizeof(headerSize));
+    localData += sizeof(headerSize);
+    lengthRemaining -= sizeof(headerSize);
+
+    if(lengthRemaining < headerSize) {
+        return false;
+    }
+
+    // Now open the stream to parse the header
+    IStream headerStream(localData, false);
+    parseHeader(&headerStream);
+
+    localData += headerSize;
+    lengthRemaining -= headerSize;
+
+    if(lengthRemaining < sizeof(mDataSize)) {
+        return false;
+    }
+
+    // Read the size of the data
+    memcpy(&mDataSize, localData, sizeof(mDataSize));
+    localData += sizeof(mDataSize);
+    lengthRemaining -= sizeof(mDataSize);
+
+    if(lengthRemaining < mDataSize) {
+        return false;
+    }
+
+    // We should know enough to read the file in at this point.
+    mData = (uint8_t *)localData;
+    mReadStream = new IStream(mData, mUse64BitOffsets);
+
+    return true;
+}
+
+bool FileA3D::load(FILE *f)
 {
     char magicString[12];
     size_t len;
 
-    LOGE("file open 1");
+    LOGV("file open 1");
     len = fread(magicString, 1, 12, f);
     if ((len != 12) ||
         memcmp(magicString, "Android3D_ff", 12)) {
         return false;
     }
 
-    LOGE("file open 2");
-    len = fread(&mMajorVersion, 1, sizeof(mMajorVersion), f);
-    if (len != sizeof(mMajorVersion)) {
+    // Next thing is the size of the header
+    uint64_t headerSize = 0;
+    len = fread(&headerSize, 1, sizeof(headerSize), f);
+    if (len != sizeof(headerSize) || headerSize == 0) {
         return false;
     }
 
-    LOGE("file open 3");
-    len = fread(&mMinorVersion, 1, sizeof(mMinorVersion), f);
-    if (len != sizeof(mMinorVersion)) {
+    uint8_t *headerData = (uint8_t *)malloc(headerSize);
+    if(!headerData) {
         return false;
     }
 
-    LOGE("file open 4");
-    uint32_t flags;
-    len = fread(&flags, 1, sizeof(flags), f);
-    if (len != sizeof(flags)) {
+    len = fread(headerData, 1, headerSize, f);
+    if (len != headerSize) {
         return false;
     }
-    mUse64BitOffsets = (flags & 1) != 0;
 
-    LOGE("file open 64bit = %i", mUse64BitOffsets);
+    // Now open the stream to parse the header
+    IStream headerStream(headerData, false);
+    parseHeader(&headerStream);
 
-    if (mUse64BitOffsets) {
-        len = fread(&mDataSize, 1, sizeof(mDataSize), f);
-        if (len != sizeof(mDataSize)) {
-            return false;
-        }
-    } else {
-        uint32_t tmp;
-        len = fread(&tmp, 1, sizeof(tmp), f);
-        if (len != sizeof(tmp)) {
-            return false;
-        }
-        mDataSize = tmp;
+    free(headerData);
+
+    // Next thing is the size of the header
+    len = fread(&mDataSize, 1, sizeof(mDataSize), f);
+    if (len != sizeof(mDataSize) || mDataSize == 0) {
+        return false;
     }
 
-    LOGE("file open size = %lli", mDataSize);
+    LOGV("file open size = %lli", mDataSize);
 
     // We should know enough to read the file in at this point.
-    fseek(f, SEEK_SET, 0);
-    mAlloc= malloc(mDataSize);
+    mAlloc = malloc(mDataSize);
     if (!mAlloc) {
         return false;
     }
@@ -99,282 +196,187 @@
         return false;
     }
 
-    LOGE("file start processing");
-    return process(rsc);
+    mReadStream = new IStream(mData, mUse64BitOffsets);
+
+    LOGV("Header is read an stream initialized");
+    return true;
 }
 
-bool FileA3D::processIndex(Context *rsc, A3DIndexEntry *ie)
-{
-    bool ret = false;
-    IO io(mData + ie->mOffset, mUse64BitOffsets);
+size_t FileA3D::getNumIndexEntries() const {
+    return mIndex.size();
+}
 
-    LOGE("process index, type %i", ie->mType);
-
-    switch(ie->mType) {
-    case CHUNK_ELEMENT:
-        processChunk_Element(rsc, &io, ie);
-        break;
-    case CHUNK_ELEMENT_SOURCE:
-        processChunk_ElementSource(rsc, &io, ie);
-        break;
-    case CHUNK_VERTICIES:
-        processChunk_Verticies(rsc, &io, ie);
-        break;
-    case CHUNK_MESH:
-        processChunk_Mesh(rsc, &io, ie);
-        break;
-    case CHUNK_PRIMITIVE:
-        processChunk_Primitive(rsc, &io, ie);
-        break;
-    default:
-        LOGE("FileA3D Unknown chunk type");
-        break;
+const FileA3D::A3DIndexEntry *FileA3D::getIndexEntry(size_t index) const {
+    if(index < mIndex.size()) {
+        return mIndex[index];
     }
-    return (ie->mRsObj != NULL);
+    return NULL;
 }
 
-bool FileA3D::process(Context *rsc)
-{
-    LOGE("process");
-    IO io(mData + 12, mUse64BitOffsets);
-    bool ret = true;
-
-    // Build the index first
-    LOGE("process 1");
-    io.loadU32(); // major version, already loaded
-    io.loadU32(); // minor version, already loaded
-    LOGE("process 2");
-
-    io.loadU32();  // flags
-    io.loadOffset(); // filesize, already loaded.
-    LOGE("process 4");
-    uint64_t mIndexOffset = io.loadOffset();
-    uint64_t mStringOffset = io.loadOffset();
-
-    LOGE("process mIndexOffset= 0x%016llx", mIndexOffset);
-    LOGE("process mStringOffset= 0x%016llx", mStringOffset);
-
-    IO index(mData + mIndexOffset, mUse64BitOffsets);
-    IO stringTable(mData + mStringOffset, mUse64BitOffsets);
-
-    uint32_t stringEntryCount = stringTable.loadU32();
-    LOGE("stringEntryCount %i", stringEntryCount);
-    mStrings.setCapacity(stringEntryCount);
-    mStringIndexValues.setCapacity(stringEntryCount);
-    if (stringEntryCount) {
-        uint32_t stringType = stringTable.loadU32();
-        LOGE("stringType %i", stringType);
-        rsAssert(stringType==0);
-        for (uint32_t ct = 0; ct < stringEntryCount; ct++) {
-            uint64_t offset = stringTable.loadOffset();
-            LOGE("string offset 0x%016llx", offset);
-            IO tmp(mData + offset, mUse64BitOffsets);
-            String8 s;
-            tmp.loadString(&s);
-            LOGE("string %s", s.string());
-            mStrings.push(s);
-        }
+ObjectBase *FileA3D::initializeFromEntry(size_t index) {
+    if(index >= mIndex.size()) {
+        return NULL;
     }
 
-    LOGE("strings done");
-    uint32_t indexEntryCount = index.loadU32();
-    LOGE("index count %i", indexEntryCount);
-    mIndex.setCapacity(indexEntryCount);
-    for (uint32_t ct = 0; ct < indexEntryCount; ct++) {
-        A3DIndexEntry e;
-        uint32_t stringIndex = index.loadU32();
-        LOGE("index %i", ct);
-        LOGE("  string index %i", stringIndex);
-        e.mType = (A3DChunkType)index.loadU32();
-        LOGE("  type %i", e.mType);
-        e.mOffset = index.loadOffset();
-        LOGE("  offset 0x%016llx", e.mOffset);
-
-        if (stringIndex && (stringIndex < mStrings.size())) {
-            e.mID = mStrings[stringIndex];
-            mStringIndexValues.editItemAt(stringIndex) = ct;
-            LOGE("  id %s", e.mID.string());
-        }
-
-        mIndex.push(e);
-    }
-    LOGE("index done");
-
-    // At this point the index should be fully populated.
-    // We can now walk though it and load all the objects.
-    for (uint32_t ct = 0; ct < indexEntryCount; ct++) {
-        LOGE("processing index entry %i", ct);
-        processIndex(rsc, &mIndex.editItemAt(ct));
+    FileA3D::A3DIndexEntry *entry = mIndex[index];
+    if(!entry) {
+        return NULL;
     }
 
-    return ret;
-}
-
-
-FileA3D::IO::IO(const uint8_t *buf, bool use64)
-{
-    mData = buf;
-    mPos = 0;
-    mUse64 = use64;
-}
-
-uint64_t FileA3D::IO::loadOffset()
-{
-    uint64_t tmp;
-    if (mUse64) {
-        mPos = (mPos + 7) & (~7);
-        tmp = reinterpret_cast<const uint64_t *>(&mData[mPos])[0];
-        mPos += sizeof(uint64_t);
-        return tmp;
+    if(entry->mRsObj) {
+        entry->mRsObj->incUserRef();
+        return entry->mRsObj;
     }
-    return loadU32();
-}
 
-void FileA3D::IO::loadString(String8 *s)
-{
-    LOGE("loadString");
-    uint32_t len = loadU32();
-    LOGE("loadString len %i", len);
-    s->setTo((const char *)&mData[mPos], len);
-    mPos += len;
-}
-
-
-void FileA3D::processChunk_Mesh(Context *rsc, IO *io, A3DIndexEntry *ie)
-{
-    Mesh * m = new Mesh(rsc);
-
-    m->mPrimitivesCount = io->loadU32();
-    m->mPrimitives = new Mesh::Primitive_t *[m->mPrimitivesCount];
-
-    for (uint32_t ct = 0; ct < m->mPrimitivesCount; ct++) {
-        uint32_t index = io->loadU32();
-
-        m->mPrimitives[ct] = (Mesh::Primitive_t *)mIndex[index].mRsObj;
-    }
-    ie->mRsObj = m;
-}
-
-void FileA3D::processChunk_Primitive(Context *rsc, IO *io, A3DIndexEntry *ie)
-{
-    Mesh::Primitive_t * p = new Mesh::Primitive_t;
-
-    p->mIndexCount = io->loadU32();
-    uint32_t vertIdx = io->loadU32();
-    p->mRestartCounts = io->loadU16();
-    uint32_t bits = io->loadU8();
-    p->mType = (RsPrimitive)io->loadU8();
-
-    LOGE("processChunk_Primitive count %i, bits %i", p->mIndexCount, bits);
-
-    p->mVerticies = (Mesh::Verticies_t *)mIndex[vertIdx].mRsObj;
-
-    p->mIndicies = new uint16_t[p->mIndexCount];
-    for (uint32_t ct = 0; ct < p->mIndexCount; ct++) {
-        switch(bits) {
-        case 8:
-            p->mIndicies[ct] = io->loadU8();
+    // Seek to the beginning of object
+    mReadStream->reset(entry->mOffset);
+    switch (entry->mType) {
+        case RS_A3D_CLASS_ID_UNKNOWN:
+            return NULL;
+        case RS_A3D_CLASS_ID_MESH:
+            entry->mRsObj = Mesh::createFromStream(mRSC, mReadStream);
             break;
-        case 16:
-            p->mIndicies[ct] = io->loadU16();
+        case RS_A3D_CLASS_ID_TYPE:
+            entry->mRsObj = Type::createFromStream(mRSC, mReadStream);
             break;
-        case 32:
-            p->mIndicies[ct] = io->loadU32();
+        case RS_A3D_CLASS_ID_ELEMENT:
+            entry->mRsObj = Element::createFromStream(mRSC, mReadStream);
             break;
+        case RS_A3D_CLASS_ID_ALLOCATION:
+            entry->mRsObj = Allocation::createFromStream(mRSC, mReadStream);
+            break;
+        case RS_A3D_CLASS_ID_PROGRAM_VERTEX:
+            entry->mRsObj = ProgramVertex::createFromStream(mRSC, mReadStream);
+            break;
+        case RS_A3D_CLASS_ID_PROGRAM_RASTER:
+            entry->mRsObj = ProgramRaster::createFromStream(mRSC, mReadStream);
+            break;
+        case RS_A3D_CLASS_ID_PROGRAM_FRAGMENT:
+            entry->mRsObj = ProgramFragment::createFromStream(mRSC, mReadStream);
+            break;
+        case RS_A3D_CLASS_ID_PROGRAM_STORE:
+            entry->mRsObj = ProgramStore::createFromStream(mRSC, mReadStream);
+            break;
+        case RS_A3D_CLASS_ID_SAMPLER:
+            entry->mRsObj = Sampler::createFromStream(mRSC, mReadStream);
+            break;
+        case RS_A3D_CLASS_ID_ANIMATION:
+            entry->mRsObj = Animation::createFromStream(mRSC, mReadStream);
+            break;
+        case RS_A3D_CLASS_ID_ADAPTER_1D:
+            entry->mRsObj = Adapter1D::createFromStream(mRSC, mReadStream);
+            break;
+        case RS_A3D_CLASS_ID_ADAPTER_2D:
+            entry->mRsObj = Adapter2D::createFromStream(mRSC, mReadStream);
+            break;
+        case RS_A3D_CLASS_ID_SCRIPT_C:
+            return NULL;
+    }
+    if(entry->mRsObj) {
+        entry->mRsObj->incUserRef();
+    }
+    return entry->mRsObj;
+}
+
+bool FileA3D::writeFile(const char *filename)
+{
+    if(!mWriteStream) {
+        LOGE("No objects to write\n");
+        return false;
+    }
+    if(mWriteStream->getPos() == 0) {
+        LOGE("No objects to write\n");
+        return false;
+    }
+
+    FILE *writeHandle = fopen(filename, "wb");
+    if(!writeHandle) {
+        LOGE("Couldn't open the file for writing\n");
+        return false;
+    }
+
+    // Open a new stream to make writing the header easier
+    OStream headerStream(5*1024, false);
+    headerStream.addU32(mMajorVersion);
+    headerStream.addU32(mMinorVersion);
+    uint32_t is64Bit = 0;
+    headerStream.addU32(is64Bit);
+
+    uint32_t writeIndexSize = mWriteIndex.size();
+    headerStream.addU32(writeIndexSize);
+    for(uint32_t i = 0; i < writeIndexSize; i ++) {
+        headerStream.addString(&mWriteIndex[i]->mObjectName);
+        headerStream.addU32((uint32_t)mWriteIndex[i]->mType);
+        if(mUse64BitOffsets){
+            headerStream.addOffset(mWriteIndex[i]->mOffset);
+            headerStream.addOffset(mWriteIndex[i]->mLength);
         }
-        LOGE("  idx %i", p->mIndicies[ct]);
-    }
-
-    if (p->mRestartCounts) {
-        p->mRestarts = new uint16_t[p->mRestartCounts];
-        for (uint32_t ct = 0; ct < p->mRestartCounts; ct++) {
-            switch(bits) {
-            case 8:
-                p->mRestarts[ct] = io->loadU8();
-                break;
-            case 16:
-                p->mRestarts[ct] = io->loadU16();
-                break;
-            case 32:
-                p->mRestarts[ct] = io->loadU32();
-                break;
-            }
-            LOGE("  idx %i", p->mRestarts[ct]);
+        else {
+            uint32_t offset = (uint32_t)mWriteIndex[i]->mOffset;
+            headerStream.addU32(offset);
+            offset = (uint32_t)mWriteIndex[i]->mLength;
+            headerStream.addU32(offset);
         }
-    } else {
-        p->mRestarts = NULL;
     }
 
-    ie->mRsObj = p;
+    // Write our magic string so we know we are reading the right file
+    String8 magicString(A3D_MAGIC_KEY);
+    fwrite(magicString.string(), sizeof(char), magicString.size(), writeHandle);
+
+    // Store the size of the header to make it easier to parse when we read it
+    uint64_t headerSize = headerStream.getPos();
+    fwrite(&headerSize, sizeof(headerSize), 1, writeHandle);
+
+    // Now write our header
+    fwrite(headerStream.getPtr(), sizeof(uint8_t), headerStream.getPos(), writeHandle);
+
+    // Now write the size of the data part of the file for easier parsing later
+    uint64_t fileDataSize = mWriteStream->getPos();
+    fwrite(&fileDataSize, sizeof(fileDataSize), 1, writeHandle);
+
+    fwrite(mWriteStream->getPtr(), sizeof(uint8_t), mWriteStream->getPos(), writeHandle);
+
+    int status = fclose(writeHandle);
+
+    if(status != 0) {
+        LOGE("Couldn't close file\n");
+        return false;
+    }
+
+    return true;
 }
 
-void FileA3D::processChunk_Verticies(Context *rsc, IO *io, A3DIndexEntry *ie)
-{
-    Mesh::Verticies_t *cv = new Mesh::Verticies_t;
-    cv->mAllocationCount = io->loadU32();
-    cv->mAllocations = new Allocation *[cv->mAllocationCount];
-    LOGE("processChunk_Verticies count %i", cv->mAllocationCount);
-    for (uint32_t ct = 0; ct < cv->mAllocationCount; ct++) {
-        uint32_t i = io->loadU32();
-        cv->mAllocations[ct] = (Allocation *)mIndex[i].mRsObj;
-        LOGE("  idx %i", i);
+void FileA3D::appendToFile(ObjectBase *obj) {
+    if(!obj) {
+        return;
     }
-    ie->mRsObj = cv;
-}
-
-void FileA3D::processChunk_Element(Context *rsc, IO *io, A3DIndexEntry *ie)
-{
-    /*
-    rsi_ElementBegin(rsc);
-
-    uint32_t count = io->loadU32();
-    LOGE("processChunk_Element count %i", count);
-    while (count--) {
-        RsDataKind dk = (RsDataKind)io->loadU8();
-        RsDataType dt = (RsDataType)io->loadU8();
-        uint32_t bits = io->loadU8();
-        bool isNorm = io->loadU8() != 0;
-        LOGE("  %i %i %i %i", dk, dt, bits, isNorm);
-        rsi_ElementAdd(rsc, dk, dt, isNorm, bits, 0);
+    if(!mWriteStream) {
+        const uint64_t initialStreamSize = 256*1024;
+        mWriteStream = new OStream(initialStreamSize, false);
     }
-    LOGE("processChunk_Element create");
-    ie->mRsObj = rsi_ElementCreate(rsc);
-    */
-}
-
-void FileA3D::processChunk_ElementSource(Context *rsc, IO *io, A3DIndexEntry *ie)
-{
-    uint32_t index = io->loadU32();
-    uint32_t count = io->loadU32();
-
-    LOGE("processChunk_ElementSource count %i, index %i", count, index);
-
-    RsElement e = (RsElement)mIndex[index].mRsObj;
-
-    RsAllocation a = rsi_AllocationCreateSized(rsc, e, count);
-    Allocation * alloc = static_cast<Allocation *>(a);
-
-    float * data = (float *)alloc->getPtr();
-    while(count--) {
-        *data = io->loadF();
-        LOGE("  %f", *data);
-        data++;
-    }
-    ie->mRsObj = alloc;
+    A3DIndexEntry *indexEntry = new A3DIndexEntry();
+    indexEntry->mObjectName.setTo(obj->getName());
+    indexEntry->mType = obj->getClassId();
+    indexEntry->mOffset = mWriteStream->getPos();
+    indexEntry->mRsObj = obj;
+    mWriteIndex.push(indexEntry);
+    obj->serialize(mWriteStream);
+    indexEntry->mLength = mWriteStream->getPos() - indexEntry->mOffset;
+    mWriteStream->align(4);
 }
 
 namespace android {
 namespace renderscript {
 
-
 RsFile rsi_FileOpen(Context *rsc, char const *path, unsigned int len)
 {
-    FileA3D *fa3d = new FileA3D;
+    FileA3D *fa3d = new FileA3D(rsc);
 
     FILE *f = fopen("/sdcard/test.a3d", "rb");
     if (f) {
-        fa3d->load(rsc, f);
+        fa3d->load(f);
         fclose(f);
+        fa3d->incUserRef();
         return fa3d;
     }
     delete fa3d;
@@ -384,3 +386,68 @@
 
 }
 }
+
+RsObjectBase rsaFileA3DGetEntryByIndex(RsContext con, uint32_t index, RsFile file)
+{
+    FileA3D *fa3d = static_cast<FileA3D *>(file);
+    if(!fa3d) {
+        LOGE("Can't load entry. No valid file");
+        return NULL;
+    }
+
+    ObjectBase *obj = fa3d->initializeFromEntry(index);
+    LOGV("Returning object with name %s", obj->getName());
+
+    return obj;
+}
+
+
+void rsaFileA3DGetNumIndexEntries(RsContext con, int32_t *numEntries, RsFile file)
+{
+    FileA3D *fa3d = static_cast<FileA3D *>(file);
+
+    if(fa3d) {
+        *numEntries = fa3d->getNumIndexEntries();
+    }
+    else {
+        *numEntries = 0;
+    }
+}
+
+void rsaFileA3DGetIndexEntries(RsContext con, RsFileIndexEntry *fileEntries, uint32_t numEntries, RsFile file)
+{
+    FileA3D *fa3d = static_cast<FileA3D *>(file);
+
+    if(!fa3d) {
+        LOGE("Can't load index entries. No valid file");
+        return;
+    }
+
+    uint32_t numFileEntries = fa3d->getNumIndexEntries();
+    if(numFileEntries != numEntries || numEntries == 0 || fileEntries == NULL) {
+        LOGE("Can't load index entries. Invalid number requested");
+        return;
+    }
+
+    for(uint32_t i = 0; i < numFileEntries; i ++) {
+        const FileA3D::A3DIndexEntry *entry = fa3d->getIndexEntry(i);
+        fileEntries[i].classID = entry->getType();
+        fileEntries[i].objectName = entry->getObjectName().string();
+    }
+
+}
+
+RsFile rsaFileA3DCreateFromAssetStream(RsContext con, const void *data, uint32_t len)
+{
+    if (data == NULL) {
+        LOGE("File load failed. Asset stream is NULL");
+        return NULL;
+    }
+
+    Context *rsc = static_cast<Context *>(con);
+    FileA3D *fa3d = new FileA3D(rsc);
+    fa3d->incUserRef();
+
+    fa3d->load(data, len);
+    return fa3d;
+}
diff --git a/libs/rs/rsFileA3D.h b/libs/rs/rsFileA3D.h
index 9ee08ec..b985907 100644
--- a/libs/rs/rsFileA3D.h
+++ b/libs/rs/rsFileA3D.h
@@ -18,20 +18,23 @@
 #define ANDROID_RS_FILE_A3D_H
 
 #include "RenderScript.h"
-#include "rsFileA3DDecls.h"
 #include "rsMesh.h"
 
 #include <utils/String8.h>
+#include "rsStream.h"
 #include <stdio.h>
 
+#define A3D_MAGIC_KEY "Android3D_ff"
+
 // ---------------------------------------------------------------------------
 namespace android {
+
 namespace renderscript {
 
-class FileA3D
+class FileA3D : public ObjectBase
 {
 public:
-    FileA3D();
+    FileA3D(Context *rsc);
     ~FileA3D();
 
     uint32_t mMajorVersion;
@@ -40,78 +43,53 @@
     uint64_t mStringTableOffset;
     bool mUse64BitOffsets;
 
-    struct A3DIndexEntry {
-        String8 mID;
-        A3DChunkType mType;
+    class A3DIndexEntry {
+        String8 mObjectName;
+        RsA3DClassID mType;
         uint64_t mOffset;
-        void * mRsObj;
+        uint64_t mLength;
+        ObjectBase *mRsObj;
+    public:
+        friend class FileA3D;
+        const String8 &getObjectName() const {
+            return mObjectName;
+        }
+        RsA3DClassID getType() const {
+            return mType;
+        }
     };
 
-    bool load(Context *rsc, FILE *f);
+    bool load(FILE *f);
+    bool load(const void *data, size_t length);
+
+    size_t getNumIndexEntries() const;
+    const A3DIndexEntry* getIndexEntry(size_t index) const;
+    ObjectBase *initializeFromEntry(size_t index);
+
+    void appendToFile(ObjectBase *obj);
+    bool writeFile(const char *filename);
+
+    // Currently files do not get serialized,
+    // but we need to inherit from ObjectBase for ref tracking
+    virtual void serialize(OStream *stream) const {
+    }
+    virtual RsA3DClassID getClassId() const {
+        return RS_A3D_CLASS_ID_UNKNOWN;
+    }
 
 protected:
-    class IO
-    {
-    public:
-        IO(const uint8_t *, bool use64);
-    
-        float loadF() {
-            mPos = (mPos + 3) & (~3);
-            float tmp = reinterpret_cast<const float *>(&mData[mPos])[0];
-            mPos += sizeof(float);
-            return tmp;
-        }
-        int32_t loadI32() {
-            mPos = (mPos + 3) & (~3);
-            int32_t tmp = reinterpret_cast<const int32_t *>(&mData[mPos])[0];
-            mPos += sizeof(int32_t);
-            return tmp;
-        }
-        uint32_t loadU32() {
-            mPos = (mPos + 3) & (~3);
-            uint32_t tmp = reinterpret_cast<const uint32_t *>(&mData[mPos])[0];
-            mPos += sizeof(uint32_t);
-            return tmp;
-        }
-        uint16_t loadU16() {
-            mPos = (mPos + 1) & (~1);
-            uint16_t tmp = reinterpret_cast<const uint16_t *>(&mData[mPos])[0];
-            mPos += sizeof(uint16_t);
-            return tmp;
-        }
-        uint8_t loadU8() {
-            uint8_t tmp = reinterpret_cast<const uint8_t *>(&mData[mPos])[0];
-            mPos += sizeof(uint8_t);
-            return tmp;
-        }
-        uint64_t loadOffset();
-        void loadString(String8 *s);
-        uint64_t getPos() const {return mPos;}
-        const uint8_t * getPtr() const;
-    protected:
-        const uint8_t * mData;
-        uint64_t mPos;
-        bool mUse64;
-    };
 
-
-    bool process(Context *rsc);
-    bool processIndex(Context *rsc, A3DIndexEntry *);
-    void processChunk_Mesh(Context *rsc, IO *io, A3DIndexEntry *ie);
-    void processChunk_Primitive(Context *rsc, IO *io, A3DIndexEntry *ie);
-    void processChunk_Verticies(Context *rsc, IO *io, A3DIndexEntry *ie);
-    void processChunk_Element(Context *rsc, IO *io, A3DIndexEntry *ie);
-    void processChunk_ElementSource(Context *rsc, IO *io, A3DIndexEntry *ie);
+    void parseHeader(IStream *headerStream);
 
     const uint8_t * mData;
     void * mAlloc;
     uint64_t mDataSize;
-    Context * mRsc;
 
-    Vector<A3DIndexEntry> mIndex;
-    Vector<String8> mStrings;
-    Vector<uint32_t> mStringIndexValues;
+    OStream *mWriteStream;
+    Vector<A3DIndexEntry*> mWriteIndex;
 
+    IStream *mReadStream;
+    Vector<A3DIndexEntry*> mIndex;
 };
 
 
diff --git a/libs/rs/rsFileA3DDecls.h b/libs/rs/rsFileA3DDecls.h
deleted file mode 100644
index 2a08bd3..0000000
--- a/libs/rs/rsFileA3DDecls.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_RS_FILE_A3D_DECLS_H
-#define ANDROID_RS_FILE_A3D_DECLS_H
-
-
-#define A3D_MAGIC_KEY "Android3D_ff"
-
-namespace android {
-namespace renderscript {
-
-    enum A3DChunkType {
-        CHUNK_EMPTY,
-
-        CHUNK_ELEMENT,
-        CHUNK_ELEMENT_SOURCE,
-        CHUNK_VERTICIES,
-        CHUNK_MESH,
-        CHUNK_PRIMITIVE,
-
-        CHUNK_LAST
-    };
-
-
-}
-}
-#endif //ANDROID_RS_FILE_A3D_H
-
-
-
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
new file mode 100644
index 0000000..9e76215
--- /dev/null
+++ b/libs/rs/rsFont.cpp
@@ -0,0 +1,866 @@
+
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_BUILD_FOR_HOST
+#include "rsContext.h"
+#else
+#include "rsContextHostStub.h"
+#endif
+
+#include "rsFont.h"
+#include "rsProgramFragment.h"
+#include <cutils/properties.h>
+#include FT_BITMAP_H
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL)
+{
+    mInitialized = false;
+    mHasKerning = false;
+    mFace = NULL;
+}
+
+bool Font::init(const char *name, uint32_t fontSize, uint32_t dpi)
+{
+    if(mInitialized) {
+        LOGE("Reinitialization of fonts not supported");
+        return false;
+    }
+
+    String8 fontsDir("/fonts/");
+    String8 fullPath(getenv("ANDROID_ROOT"));
+    fullPath += fontsDir;
+    fullPath += name;
+
+    FT_Error error = FT_New_Face(mRSC->mStateFont.getLib(), fullPath.string(), 0, &mFace);
+    if(error) {
+        LOGE("Unable to initialize font %s", fullPath.string());
+        return false;
+    }
+
+    mFontName = name;
+    mFontSize = fontSize;
+    mDpi = dpi;
+
+    error = FT_Set_Char_Size(mFace, fontSize * 64, 0, dpi, 0);
+    if(error) {
+        LOGE("Unable to set font size on %s", fullPath.string());
+        return false;
+    }
+
+    mHasKerning = FT_HAS_KERNING(mFace);
+
+    mInitialized = true;
+    return true;
+}
+
+void Font::invalidateTextureCache()
+{
+    for(uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
+        mCachedGlyphs.valueAt(i)->mIsValid = false;
+    }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y)
+{
+    FontState *state = &mRSC->mStateFont;
+
+    int32_t nPenX = x + glyph->mBitmapLeft;
+    int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
+
+    float u1 = glyph->mBitmapMinU;
+    float u2 = glyph->mBitmapMaxU;
+    float v1 = glyph->mBitmapMinV;
+    float v2 = glyph->mBitmapMaxV;
+
+    int32_t width = (int32_t) glyph->mBitmapWidth;
+    int32_t height = (int32_t) glyph->mBitmapHeight;
+
+    state->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
+                          nPenX + width, nPenY, 0, u2, v2,
+                          nPenX + width, nPenY - height, 0, u2, v1,
+                          nPenX, nPenY - height, 0, u1, v1);
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int32_t x, int32_t y,
+                           uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+    int32_t nPenX = x + glyph->mBitmapLeft;
+    int32_t nPenY = y + glyph->mBitmapTop;
+
+    uint32_t endX = glyph->mBitmapMinX + glyph->mBitmapWidth;
+    uint32_t endY = glyph->mBitmapMinY + glyph->mBitmapHeight;
+
+    FontState *state = &mRSC->mStateFont;
+    uint32_t cacheWidth = state->getCacheTextureType()->getDimX();
+    const uint8_t* cacheBuffer = state->getTextTextureData();
+
+    uint32_t cacheX = 0, cacheY = 0;
+    int32_t bX = 0, bY = 0;
+    for (cacheX = glyph->mBitmapMinX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
+        for (cacheY = glyph->mBitmapMinY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
+            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
+                LOGE("Skipping invalid index");
+                continue;
+            }
+            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
+            bitmap[bY * bitmapW + bX] = tempCol;
+        }
+    }
+
+}
+
+void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y, Rect *bounds) {
+    int32_t nPenX = x + glyph->mBitmapLeft;
+    int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
+
+    int32_t width = (int32_t) glyph->mBitmapWidth;
+    int32_t height = (int32_t) glyph->mBitmapHeight;
+
+    if (bounds->bottom > nPenY) {
+        bounds->bottom = nPenY;
+    }
+    if (bounds->left > nPenX) {
+        bounds->left = nPenX;
+    }
+    if (bounds->right < nPenX + width) {
+        bounds->right = nPenX + width;
+    }
+    if (bounds->top < nPenY + height) {
+        bounds->top = nPenY + height;
+    }
+}
+
+void Font::renderUTF(const char *text, uint32_t len, int32_t x, int32_t y,
+                     uint32_t start, int32_t numGlyphs,
+                     RenderMode mode, Rect *bounds,
+                     uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH)
+{
+    if(!mInitialized || numGlyphs == 0 || text == NULL || len == 0) {
+        return;
+    }
+
+    if(mode == Font::MEASURE) {
+        if (bounds == NULL) {
+            LOGE("No return rectangle provided to measure text");
+            return;
+        }
+        // Reset min and max of the bounding box to something large
+        bounds->set(1e6, -1e6, -1e6, 1e6);
+    }
+
+    int32_t penX = x, penY = y;
+    int32_t glyphsLeft = 1;
+    if(numGlyphs > 0) {
+        glyphsLeft = numGlyphs;
+    }
+
+    size_t index = start;
+    size_t nextIndex = 0;
+
+    while (glyphsLeft > 0) {
+
+        int32_t utfChar = utf32_at(text, len, index, &nextIndex);
+
+        // Reached the end of the string or encountered
+        if(utfChar < 0) {
+            break;
+        }
+
+        // Move to the next character in the array
+        index = nextIndex;
+
+        CachedGlyphInfo *cachedGlyph = getCachedUTFChar(utfChar);
+
+        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+        if(cachedGlyph->mIsValid) {
+            switch(mode) {
+            case FRAMEBUFFER:
+                drawCachedGlyph(cachedGlyph, penX, penY);
+                break;
+            case BITMAP:
+                drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH);
+                break;
+            case MEASURE:
+                measureCachedGlyph(cachedGlyph, penX, penY, bounds);
+                break;
+            }
+        }
+
+        penX += (cachedGlyph->mAdvance.x >> 6);
+
+        // If we were given a specific number of glyphs, decrement
+        if(numGlyphs > 0) {
+            glyphsLeft --;
+        }
+    }
+}
+
+Font::CachedGlyphInfo* Font::getCachedUTFChar(int32_t utfChar) {
+
+    CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar);
+    if(cachedGlyph == NULL) {
+        cachedGlyph = cacheGlyph((uint32_t)utfChar);
+    }
+    // Is the glyph still in texture cache?
+    if(!cachedGlyph->mIsValid) {
+        updateGlyphCache(cachedGlyph);
+    }
+
+    return cachedGlyph;
+}
+
+void Font::updateGlyphCache(CachedGlyphInfo *glyph)
+{
+    FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER );
+    if(error) {
+        LOGE("Couldn't load glyph.");
+        return;
+    }
+
+    glyph->mAdvance = mFace->glyph->advance;
+    glyph->mBitmapLeft = mFace->glyph->bitmap_left;
+    glyph->mBitmapTop = mFace->glyph->bitmap_top;
+
+    FT_Bitmap *bitmap = &mFace->glyph->bitmap;
+
+    // Now copy the bitmap into the cache texture
+    uint32_t startX = 0;
+    uint32_t startY = 0;
+
+    // Let the font state figure out where to put the bitmap
+    FontState *state = &mRSC->mStateFont;
+    glyph->mIsValid = state->cacheBitmap(bitmap, &startX, &startY);
+
+    if(!glyph->mIsValid) {
+        return;
+    }
+
+    uint32_t endX = startX + bitmap->width;
+    uint32_t endY = startY + bitmap->rows;
+
+    glyph->mBitmapMinX = startX;
+    glyph->mBitmapMinY = startY;
+    glyph->mBitmapWidth = bitmap->width;
+    glyph->mBitmapHeight = bitmap->rows;
+
+    uint32_t cacheWidth = state->getCacheTextureType()->getDimX();
+    uint32_t cacheHeight = state->getCacheTextureType()->getDimY();
+
+    glyph->mBitmapMinU = (float)startX / (float)cacheWidth;
+    glyph->mBitmapMinV = (float)startY / (float)cacheHeight;
+    glyph->mBitmapMaxU = (float)endX / (float)cacheWidth;
+    glyph->mBitmapMaxV = (float)endY / (float)cacheHeight;
+}
+
+Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph)
+{
+    CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
+    mCachedGlyphs.add(glyph, newGlyph);
+
+    newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph);
+    newGlyph->mIsValid = false;
+
+    updateGlyphCache(newGlyph);
+
+    return newGlyph;
+}
+
+Font * Font::create(Context *rsc, const char *name, uint32_t fontSize, uint32_t dpi)
+{
+    rsc->mStateFont.checkInit();
+    Vector<Font*> &activeFonts = rsc->mStateFont.mActiveFonts;
+
+    for(uint32_t i = 0; i < activeFonts.size(); i ++) {
+        Font *ithFont = activeFonts[i];
+        if(ithFont->mFontName == name && ithFont->mFontSize == fontSize && ithFont->mDpi == dpi) {
+            return ithFont;
+        }
+    }
+
+    Font *newFont = new Font(rsc);
+    bool isInitialized = newFont->init(name, fontSize, dpi);
+    if(isInitialized) {
+        activeFonts.push(newFont);
+        rsc->mStateFont.precacheLatin(newFont);
+        return newFont;
+    }
+
+    ObjectBase::checkDelete(newFont);
+    return NULL;
+
+}
+
+Font::~Font()
+{
+    if(mFace) {
+        FT_Done_Face(mFace);
+    }
+
+    for (uint32_t ct = 0; ct < mRSC->mStateFont.mActiveFonts.size(); ct++) {
+        if (mRSC->mStateFont.mActiveFonts[ct] == this) {
+            mRSC->mStateFont.mActiveFonts.removeAt(ct);
+            break;
+        }
+    }
+
+    for(uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
+        CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i);
+        delete glyph;
+    }
+}
+
+FontState::FontState()
+{
+    mInitialized = false;
+    mMaxNumberOfQuads = 1024;
+    mCurrentQuadIndex = 0;
+    mRSC = NULL;
+    mLibrary = NULL;
+
+    // Get the renderer properties
+    char property[PROPERTY_VALUE_MAX];
+
+    // Get the gamma
+    float gamma = DEFAULT_TEXT_GAMMA;
+    if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) {
+        LOGD("  Setting text gamma to %s", property);
+        gamma = atof(property);
+    } else {
+        LOGD("  Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA);
+    }
+
+    // Get the black gamma threshold
+    int32_t blackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
+    if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) {
+        LOGD("  Setting text black gamma threshold to %s", property);
+        blackThreshold = atoi(property);
+    } else {
+        LOGD("  Using default text black gamma threshold of %d",
+                DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD);
+    }
+    mBlackThreshold = (float)(blackThreshold) / 255.0f;
+
+    // Get the white gamma threshold
+    int32_t whiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
+    if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) {
+        LOGD("  Setting text white gamma threshold to %s", property);
+        whiteThreshold = atoi(property);
+    } else {
+        LOGD("  Using default white black gamma threshold of %d",
+                DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD);
+    }
+    mWhiteThreshold = (float)(whiteThreshold) / 255.0f;
+
+    // Compute the gamma tables
+    mBlackGamma = gamma;
+    mWhiteGamma = 1.0f / gamma;
+
+    setFontColor(0.1f, 0.1f, 0.1f, 1.0f);
+}
+
+FontState::~FontState()
+{
+    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
+        delete mCacheLines[i];
+    }
+
+    rsAssert(!mActiveFonts.size());
+}
+
+FT_Library FontState::getLib()
+{
+    if(!mLibrary) {
+        FT_Error error = FT_Init_FreeType(&mLibrary);
+        if(error) {
+            LOGE("Unable to initialize freetype");
+            return NULL;
+        }
+    }
+
+    return mLibrary;
+}
+
+void FontState::init(Context *rsc)
+{
+    mRSC = rsc;
+}
+
+void FontState::flushAllAndInvalidate()
+{
+    if(mCurrentQuadIndex != 0) {
+        issueDrawCommand();
+        mCurrentQuadIndex = 0;
+    }
+    for(uint32_t i = 0; i < mActiveFonts.size(); i ++) {
+        mActiveFonts[i]->invalidateTextureCache();
+    }
+    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
+        mCacheLines[i]->mCurrentCol = 0;
+    }
+}
+
+bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY)
+{
+    // If the glyph is too tall, don't cache it
+    if((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) {
+        LOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
+        return false;
+    }
+
+    // Now copy the bitmap into the cache texture
+    uint32_t startX = 0;
+    uint32_t startY = 0;
+
+    bool bitmapFit = false;
+    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
+        bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
+        if(bitmapFit) {
+            break;
+        }
+    }
+
+    // If the new glyph didn't fit, flush the state so far and invalidate everything
+    if(!bitmapFit) {
+        flushAllAndInvalidate();
+
+        // Try to fit it again
+        for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
+            bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
+            if(bitmapFit) {
+                break;
+            }
+        }
+
+        // if we still don't fit, something is wrong and we shouldn't draw
+        if(!bitmapFit) {
+            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
+            return false;
+        }
+    }
+
+    *retOriginX = startX;
+    *retOriginY = startY;
+
+    uint32_t endX = startX + bitmap->width;
+    uint32_t endY = startY + bitmap->rows;
+
+    uint32_t cacheWidth = getCacheTextureType()->getDimX();
+
+    uint8_t *cacheBuffer = (uint8_t*)mTextTexture->getPtr();
+    uint8_t *bitmapBuffer = bitmap->buffer;
+
+    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
+    for(cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) {
+        for(cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) {
+            uint8_t tempCol = bitmapBuffer[bY * bitmap->width + bX];
+            cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol;
+        }
+    }
+
+    // This will dirty the texture and the shader so next time
+    // we draw it will upload the data
+    mTextTexture->deferedUploadToTexture(mRSC, false, 0);
+    mFontShaderF->bindTexture(mRSC, 0, mTextTexture.get());
+
+    // Some debug code
+    /*for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
+        LOGE("Cache Line: H: %u Empty Space: %f",
+             mCacheLines[i]->mMaxHeight,
+              (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f);
+
+    }*/
+
+    return true;
+}
+
+void FontState::initRenderState()
+{
+    String8 shaderString("varying vec2 varTex0;\n");
+    shaderString.append("void main() {\n");
+    shaderString.append("  lowp vec4 col = UNI_Color;\n");
+    shaderString.append("  col.a = texture2D(UNI_Tex0, varTex0.xy).a;\n");
+    shaderString.append("  col.a = pow(col.a, UNI_Gamma);\n");
+    shaderString.append("  gl_FragColor = col;\n");
+    shaderString.append("}\n");
+
+    const Element *colorElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
+    const Element *gammaElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1);
+    mRSC->mStateElement.elementBuilderBegin();
+    mRSC->mStateElement.elementBuilderAdd(colorElem, "Color", 1);
+    mRSC->mStateElement.elementBuilderAdd(gammaElem, "Gamma", 1);
+    const Element *constInput = mRSC->mStateElement.elementBuilderCreate(mRSC);
+
+    Type *inputType = Type::getType(mRSC, constInput, 1, 0, 0, false, false);
+
+    uint32_t tmp[4];
+    tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
+    tmp[1] = (uint32_t)inputType;
+    tmp[2] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
+    tmp[3] = 1;
+
+    mFontShaderFConstant.set(new Allocation(mRSC, inputType));
+    ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(),
+                                              shaderString.length(), tmp, 4);
+    mFontShaderF.set(pf);
+    mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0);
+
+    Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
+                                      RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP);
+    mFontSampler.set(sampler);
+    mFontShaderF->bindSampler(mRSC, 0, sampler);
+
+    ProgramStore *fontStore = new ProgramStore(mRSC);
+    mFontProgramStore.set(fontStore);
+    mFontProgramStore->setDepthFunc(RS_DEPTH_FUNC_ALWAYS);
+    mFontProgramStore->setBlendFunc(RS_BLEND_SRC_SRC_ALPHA, RS_BLEND_DST_ONE_MINUS_SRC_ALPHA);
+    mFontProgramStore->setDitherEnable(false);
+    mFontProgramStore->setDepthMask(false);
+}
+
+void FontState::initTextTexture()
+{
+    const Element *alphaElem = Element::create(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1);
+
+    // We will allocate a texture to initially hold 32 character bitmaps
+    Type *texType = Type::getType(mRSC, alphaElem, 1024, 256, 0, false, false);
+
+    Allocation *cacheAlloc = new Allocation(mRSC, texType);
+    mTextTexture.set(cacheAlloc);
+    mTextTexture->deferedUploadToTexture(mRSC, false, 0);
+
+    // Split up our cache texture into lines of certain widths
+    int32_t nextLine = 0;
+    mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0));
+}
+
+// Avoid having to reallocate memory and render quad by quad
+void FontState::initVertexArrayBuffers()
+{
+    // Now lets write index data
+    const Element *indexElem = Element::create(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1);
+    uint32_t numIndicies = mMaxNumberOfQuads * 6;
+    Type *indexType = Type::getType(mRSC, indexElem, numIndicies, 0, 0, false, false);
+
+    Allocation *indexAlloc = new Allocation(mRSC, indexType);
+    uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr();
+
+    // Four verts, two triangles , six indices per quad
+    for(uint32_t i = 0; i < mMaxNumberOfQuads; i ++) {
+        int32_t i6 = i * 6;
+        int32_t i4 = i * 4;
+
+        indexPtr[i6 + 0] = i4 + 0;
+        indexPtr[i6 + 1] = i4 + 1;
+        indexPtr[i6 + 2] = i4 + 2;
+
+        indexPtr[i6 + 3] = i4 + 0;
+        indexPtr[i6 + 4] = i4 + 2;
+        indexPtr[i6 + 5] = i4 + 3;
+    }
+
+    indexAlloc->deferedUploadToBufferObject(mRSC);
+    mIndexBuffer.set(indexAlloc);
+
+    const Element *posElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
+    const Element *texElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2);
+
+    mRSC->mStateElement.elementBuilderBegin();
+    mRSC->mStateElement.elementBuilderAdd(posElem, "position", 1);
+    mRSC->mStateElement.elementBuilderAdd(texElem, "texture0", 1);
+    const Element *vertexDataElem = mRSC->mStateElement.elementBuilderCreate(mRSC);
+
+    Type *vertexDataType = Type::getType(mRSC, vertexDataElem,
+                                         mMaxNumberOfQuads * 4,
+                                         0, 0, false, false);
+
+    Allocation *vertexAlloc = new Allocation(mRSC, vertexDataType);
+    mTextMeshPtr = (float*)vertexAlloc->getPtr();
+
+    mVertexArray.set(vertexAlloc);
+}
+
+// We don't want to allocate anything unless we actually draw text
+void FontState::checkInit()
+{
+    if(mInitialized) {
+        return;
+    }
+
+    initTextTexture();
+    initRenderState();
+
+    initVertexArrayBuffers();
+
+    // We store a string with letters in a rough frequency of occurrence
+    mLatinPrecache = String8(" eisarntolcdugpmhbyfvkwzxjq");
+    mLatinPrecache += String8("EISARNTOLCDUGPMHBYFVKWZXJQ");
+    mLatinPrecache += String8(",.?!()-+@;:`'");
+    mLatinPrecache += String8("0123456789");
+
+    mInitialized = true;
+}
+
+void FontState::issueDrawCommand() {
+
+    ObjectBaseRef<const ProgramVertex> tmpV(mRSC->getVertex());
+    mRSC->setVertex(mRSC->getDefaultProgramVertex());
+
+    ObjectBaseRef<const ProgramRaster> tmpR(mRSC->getRaster());
+    mRSC->setRaster(mRSC->getDefaultProgramRaster());
+
+    ObjectBaseRef<const ProgramFragment> tmpF(mRSC->getFragment());
+    mRSC->setFragment(mFontShaderF.get());
+
+    ObjectBaseRef<const ProgramStore> tmpPS(mRSC->getFragmentStore());
+    mRSC->setFragmentStore(mFontProgramStore.get());
+
+    if(mConstantsDirty) {
+        mFontShaderFConstant->data(mRSC, &mConstants, sizeof(mConstants));
+        mConstantsDirty = false;
+    }
+
+    if (!mRSC->setupCheck()) {
+        mRSC->setVertex((ProgramVertex *)tmpV.get());
+        mRSC->setRaster((ProgramRaster *)tmpR.get());
+        mRSC->setFragment((ProgramFragment *)tmpF.get());
+        mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
+        return;
+    }
+
+    float *vtx = (float*)mVertexArray->getPtr();
+    float *tex = vtx + 3;
+
+    VertexArray va;
+    va.add(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "ATTRIB_position");
+    va.add(GL_FLOAT, 2, 20, false, (uint32_t)tex, "ATTRIB_texture0");
+    va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache);
+
+    mIndexBuffer->uploadCheck(mRSC);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID());
+    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0));
+
+    // Reset the state
+    mRSC->setVertex((ProgramVertex *)tmpV.get());
+    mRSC->setRaster((ProgramRaster *)tmpR.get());
+    mRSC->setFragment((ProgramFragment *)tmpF.get());
+    mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
+}
+
+void FontState::appendMeshQuad(float x1, float y1, float z1,
+                                  float u1, float v1,
+                                  float x2, float y2, float z2,
+                                  float u2, float v2,
+                                  float x3, float y3, float z3,
+                                  float u3, float v3,
+                                  float x4, float y4, float z4,
+                                  float u4, float v4)
+{
+    const uint32_t vertsPerQuad = 4;
+    const uint32_t floatsPerVert = 5;
+    float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
+
+    // Cull things that are off the screen
+    float width = (float)mRSC->getWidth();
+    float height = (float)mRSC->getHeight();
+
+    if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) {
+        return;
+    }
+
+    /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1);
+    LOGE("V1 x: %f y: %f z: %f", x2, y2, z2);
+    LOGE("V2 x: %f y: %f z: %f", x3, y3, z3);
+    LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/
+
+    (*currentPos++) = x1;
+    (*currentPos++) = y1;
+    (*currentPos++) = z1;
+    (*currentPos++) = u1;
+    (*currentPos++) = v1;
+
+    (*currentPos++) = x2;
+    (*currentPos++) = y2;
+    (*currentPos++) = z2;
+    (*currentPos++) = u2;
+    (*currentPos++) = v2;
+
+    (*currentPos++) = x3;
+    (*currentPos++) = y3;
+    (*currentPos++) = z3;
+    (*currentPos++) = u3;
+    (*currentPos++) = v3;
+
+    (*currentPos++) = x4;
+    (*currentPos++) = y4;
+    (*currentPos++) = z4;
+    (*currentPos++) = u4;
+    (*currentPos++) = v4;
+
+    mCurrentQuadIndex ++;
+
+    if(mCurrentQuadIndex == mMaxNumberOfQuads) {
+        issueDrawCommand();
+        mCurrentQuadIndex = 0;
+    }
+}
+
+uint32_t FontState::getRemainingCacheCapacity() {
+    uint32_t remainingCapacity = 0;
+    uint32_t totalPixels = 0;
+    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
+         remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
+         totalPixels += mCacheLines[i]->mMaxWidth;
+    }
+    remainingCapacity = (remainingCapacity * 100) / totalPixels;
+    return remainingCapacity;
+}
+
+void FontState::precacheLatin(Font *font) {
+    // Remaining capacity is measured in %
+    uint32_t remainingCapacity = getRemainingCacheCapacity();
+    uint32_t precacheIdx = 0;
+    while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
+        font->getCachedUTFChar((int32_t)mLatinPrecache[precacheIdx]);
+        remainingCapacity = getRemainingCacheCapacity();
+        precacheIdx ++;
+    }
+}
+
+
+void FontState::renderText(const char *text, uint32_t len, int32_t x, int32_t y,
+                           uint32_t startIndex, int32_t numGlyphs,
+                           Font::RenderMode mode,
+                           Font::Rect *bounds,
+                           uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH)
+{
+    checkInit();
+
+    // Render code here
+    Font *currentFont = mRSC->getFont();
+    if(!currentFont) {
+        if(!mDefault.get()) {
+            mDefault.set(Font::create(mRSC, "DroidSans.ttf", 16, 96));
+        }
+        currentFont = mDefault.get();
+    }
+    if(!currentFont) {
+        LOGE("Unable to initialize any fonts");
+        return;
+    }
+
+    currentFont->renderUTF(text, len, x, y, startIndex, numGlyphs,
+                           mode, bounds, bitmap, bitmapW, bitmapH);
+
+    if(mCurrentQuadIndex != 0) {
+        issueDrawCommand();
+        mCurrentQuadIndex = 0;
+    }
+}
+
+void FontState::measureText(const char *text, uint32_t len, Font::Rect *bounds) {
+    renderText(text, len, 0, 0, 0, -1, Font::MEASURE, bounds);
+}
+
+void FontState::setFontColor(float r, float g, float b, float a) {
+    mConstants.mFontColor[0] = r;
+    mConstants.mFontColor[1] = g;
+    mConstants.mFontColor[2] = b;
+    mConstants.mFontColor[3] = a;
+
+    mConstants.mGamma = 1.0f;
+    const float luminance = (r * 2.0f + g * 5.0f + b) / 8.0f;
+    if (luminance <= mBlackThreshold) {
+        mConstants.mGamma = mBlackGamma;
+    } else if (luminance >= mWhiteThreshold) {
+        mConstants.mGamma = mWhiteGamma;
+    }
+
+    mConstantsDirty = true;
+}
+
+void FontState::getFontColor(float *r, float *g, float *b, float *a) const {
+    *r = mConstants.mFontColor[0];
+    *g = mConstants.mFontColor[1];
+    *b = mConstants.mFontColor[2];
+    *a = mConstants.mFontColor[3];
+}
+
+void FontState::deinit(Context *rsc)
+{
+    mInitialized = false;
+
+    mFontShaderFConstant.clear();
+
+    mIndexBuffer.clear();
+    mVertexArray.clear();
+
+    mFontShaderF.clear();
+    mFontSampler.clear();
+    mFontProgramStore.clear();
+
+    mTextTexture.clear();
+    for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
+        delete mCacheLines[i];
+    }
+    mCacheLines.clear();
+
+    mDefault.clear();
+
+    Vector<Font*> fontsToDereference = mActiveFonts;
+    for(uint32_t i = 0; i < fontsToDereference.size(); i ++) {
+        fontsToDereference[i]->zeroUserRef();
+    }
+
+    if(mLibrary) {
+        FT_Done_FreeType( mLibrary );
+        mLibrary = NULL;
+    }
+}
+
+namespace android {
+namespace renderscript {
+
+RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, uint32_t fontSize, uint32_t dpi)
+{
+    Font *newFont = Font::create(rsc, name, fontSize, dpi);
+    if(newFont) {
+        newFont->incUserRef();
+    }
+    return newFont;
+}
+
+} // renderscript
+} // android
diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h
new file mode 100644
index 0000000..0012b84
--- /dev/null
+++ b/libs/rs/rsFont.h
@@ -0,0 +1,271 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_FONT_H
+#define ANDROID_RS_FONT_H
+
+#include "RenderScript.h"
+#include "rsStream.h"
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+namespace renderscript {
+
+// Gamma (>= 1.0, <= 10.0)
+#define PROPERTY_TEXT_GAMMA "ro.text_gamma"
+#define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "ro.text_gamma.black_threshold"
+#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "ro.text_gamma.white_threshold"
+
+#define DEFAULT_TEXT_GAMMA 1.4f
+#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64
+#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192
+
+class FontState;
+
+class Font : public ObjectBase
+{
+public:
+    enum RenderMode {
+        FRAMEBUFFER,
+        BITMAP,
+        MEASURE,
+    };
+
+    struct Rect {
+        int32_t left;
+        int32_t top;
+        int32_t right;
+        int32_t bottom;
+        void set(int32_t l, int32_t r, int32_t t, int32_t b) {
+            left = l;
+            right = r;
+            top = t;
+            bottom = b;
+        }
+    };
+
+    ~Font();
+
+    // Currently files do not get serialized,
+    // but we need to inherit from ObjectBase for ref tracking
+    virtual void serialize(OStream *stream) const {
+    }
+    virtual RsA3DClassID getClassId() const {
+        return RS_A3D_CLASS_ID_UNKNOWN;
+    }
+
+    static Font * create(Context *rsc, const char *name, uint32_t fontSize, uint32_t dpi);
+
+protected:
+
+    friend class FontState;
+
+    // Pointer to the utf data, length of data, where to start, number of glyphs ot read
+    // (each glyph may be longer than a char because we are dealing with utf data)
+    // Last two variables are the initial pen position
+    void renderUTF(const char *text, uint32_t len, int32_t x, int32_t y,
+                   uint32_t start, int32_t numGlyphs,
+                   RenderMode mode = FRAMEBUFFER, Rect *bounds = NULL,
+                   uint8_t *bitmap = NULL, uint32_t bitmapW = 0, uint32_t bitmapH = 0);
+
+    void invalidateTextureCache();
+    struct CachedGlyphInfo
+    {
+        // Has the cache been invalidated?
+        bool mIsValid;
+        // Location of the cached glyph in the bitmap
+        // in case we need to resize the texture
+        uint32_t mBitmapMinX;
+        uint32_t mBitmapMinY;
+        uint32_t mBitmapWidth;
+        uint32_t mBitmapHeight;
+        // Also cache texture coords for the quad
+        float mBitmapMinU;
+        float mBitmapMinV;
+        float mBitmapMaxU;
+        float mBitmapMaxV;
+        // Minimize how much we call freetype
+        FT_UInt mGlyphIndex;
+        FT_Vector mAdvance;
+        // Values below contain a glyph's origin in the bitmap
+        FT_Int mBitmapLeft;
+        FT_Int mBitmapTop;
+    };
+
+    String8 mFontName;
+    uint32_t mFontSize;
+    uint32_t mDpi;
+
+    Font(Context *rsc);
+    bool init(const char *name, uint32_t fontSize, uint32_t dpi);
+
+    FT_Face mFace;
+    bool mInitialized;
+    bool mHasKerning;
+
+    DefaultKeyedVector<uint32_t, CachedGlyphInfo* > mCachedGlyphs;
+    CachedGlyphInfo* getCachedUTFChar(int32_t utfChar);
+
+    CachedGlyphInfo *cacheGlyph(uint32_t glyph);
+    void updateGlyphCache(CachedGlyphInfo *glyph);
+    void measureCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y, Rect *bounds);
+    void drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y);
+    void drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y,
+                         uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH);
+};
+
+class FontState
+{
+public:
+    FontState();
+    ~FontState();
+
+    void init(Context *rsc);
+    void deinit(Context *rsc);
+
+    ObjectBaseRef<Font> mDefault;
+    ObjectBaseRef<Font> mLast;
+
+    void renderText(const char *text, uint32_t len, int32_t x, int32_t y,
+                    uint32_t startIndex = 0, int numGlyphs = -1,
+                    Font::RenderMode mode = Font::FRAMEBUFFER,
+                    Font::Rect *bounds = NULL,
+                    uint8_t *bitmap = NULL, uint32_t bitmapW = 0, uint32_t bitmapH = 0);
+
+    void measureText(const char *text, uint32_t len, Font::Rect *bounds);
+
+    void setFontColor(float r, float g, float b, float a);
+    void getFontColor(float *r, float *g, float *b, float *a) const;
+
+protected:
+
+    friend class Font;
+
+    struct CacheTextureLine
+    {
+        uint32_t mMaxHeight;
+        uint32_t mMaxWidth;
+        uint32_t mCurrentRow;
+        uint32_t mCurrentCol;
+        bool mDirty;
+
+        CacheTextureLine(uint32_t maxHeight, uint32_t maxWidth, uint32_t currentRow, uint32_t currentCol) :
+            mMaxHeight(maxHeight), mMaxWidth(maxWidth), mCurrentRow(currentRow), mCurrentCol(currentCol),
+            mDirty(false)  {
+        }
+
+        bool fitBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) {
+            if((uint32_t)bitmap->rows > mMaxHeight) {
+                return false;
+            }
+
+            if(mCurrentCol + (uint32_t)bitmap->width < mMaxWidth) {
+               *retOriginX = mCurrentCol;
+               *retOriginY = mCurrentRow;
+               mCurrentCol += bitmap->width;
+               mDirty = true;
+               return true;
+            }
+
+            return false;
+        }
+    };
+
+    Vector<CacheTextureLine*> mCacheLines;
+    uint32_t getRemainingCacheCapacity();
+
+    void precacheLatin(Font *font);
+    String8 mLatinPrecache;
+
+    Context *mRSC;
+
+    struct {
+        float mFontColor[4];
+        float mGamma;
+    } mConstants;
+    bool mConstantsDirty;
+
+    float mBlackGamma;
+    float mWhiteGamma;
+
+    float mBlackThreshold;
+    float mWhiteThreshold;
+
+    // Free type library, we only need one copy
+    FT_Library mLibrary;
+    FT_Library getLib();
+    Vector<Font*> mActiveFonts;
+
+    // Render state for the font
+    ObjectBaseRef<Allocation> mFontShaderFConstant;
+    ObjectBaseRef<ProgramFragment> mFontShaderF;
+    ObjectBaseRef<Sampler> mFontSampler;
+    ObjectBaseRef<ProgramStore> mFontProgramStore;
+    void initRenderState();
+
+    // Texture to cache glyph bitmaps
+    ObjectBaseRef<Allocation> mTextTexture;
+    void initTextTexture();
+    const uint8_t* getTextTextureData() const {
+        return (uint8_t*)mTextTexture->getPtr();
+    }
+
+    bool cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY);
+    const Type* getCacheTextureType() {
+        return mTextTexture->getType();
+    }
+
+    void flushAllAndInvalidate();
+
+    // Pointer to vertex data to speed up frame to frame work
+    float *mTextMeshPtr;
+    uint32_t mCurrentQuadIndex;
+    uint32_t mMaxNumberOfQuads;
+
+    void initVertexArrayBuffers();
+    ObjectBaseRef<Allocation> mIndexBuffer;
+    ObjectBaseRef<Allocation> mVertexArray;
+
+
+    bool mInitialized;
+
+    void checkInit();
+
+    void issueDrawCommand();
+
+    void appendMeshQuad(float x1, float y1, float z1,
+                          float u1, float v1,
+                          float x2, float y2, float z2,
+                          float u2, float v2,
+                          float x3, float y3, float z3,
+                          float u3, float v3,
+                          float x4, float y4, float z4,
+                          float u4, float v4);
+
+};
+
+
+}
+}
+
+#endif
diff --git a/libs/rs/rsHandcode.h b/libs/rs/rsHandcode.h
index 800eddd..c02fd42 100644
--- a/libs/rs/rsHandcode.h
+++ b/libs/rs/rsHandcode.h
@@ -1,6 +1,57 @@
 
 #define DATA_SYNC_SIZE 1024
 
+static inline void rsHCAPI_ContextFinish (RsContext rsc)
+{
+    ThreadIO *io = &((Context *)rsc)->mIO;
+    uint32_t size = sizeof(RS_CMD_ContextFinish);
+    RS_CMD_ContextFinish *cmd = static_cast<RS_CMD_ContextFinish *>(io->mToCore.reserve(size));
+    io->mToCore.commitSync(RS_CMD_ID_ContextFinish, size);
+}
+
+static inline void rsHCAPI_ScriptInvokeV (RsContext rsc, RsScript va, uint32_t slot, const void * data, uint32_t sizeBytes)
+{
+    ThreadIO *io = &((Context *)rsc)->mIO;
+    uint32_t size = sizeof(RS_CMD_ScriptInvokeV);
+    if (sizeBytes < DATA_SYNC_SIZE) {
+        size += (sizeBytes + 3) & ~3;
+    }
+    RS_CMD_ScriptInvokeV *cmd = static_cast<RS_CMD_ScriptInvokeV *>(io->mToCore.reserve(size));
+    cmd->s = va;
+    cmd->slot = slot;
+    cmd->dataLen = sizeBytes;
+    cmd->data = data;
+    if (sizeBytes < DATA_SYNC_SIZE) {
+        cmd->data = (void *)(cmd+1);
+        memcpy(cmd+1, data, sizeBytes);
+        io->mToCore.commit(RS_CMD_ID_ScriptInvokeV, size);
+    } else {
+        io->mToCore.commitSync(RS_CMD_ID_ScriptInvokeV, size);
+    }
+}
+
+
+static inline void rsHCAPI_ScriptSetVarV (RsContext rsc, RsScript va, uint32_t slot, const void * data, uint32_t sizeBytes)
+{
+    ThreadIO *io = &((Context *)rsc)->mIO;
+    uint32_t size = sizeof(RS_CMD_ScriptSetVarV);
+    if (sizeBytes < DATA_SYNC_SIZE) {
+        size += (sizeBytes + 3) & ~3;
+    }
+    RS_CMD_ScriptSetVarV *cmd = static_cast<RS_CMD_ScriptSetVarV *>(io->mToCore.reserve(size));
+    cmd->s = va;
+    cmd->slot = slot;
+    cmd->dataLen = sizeBytes;
+    cmd->data = data;
+    if (sizeBytes < DATA_SYNC_SIZE) {
+        cmd->data = (void *)(cmd+1);
+        memcpy(cmd+1, data, sizeBytes);
+        io->mToCore.commit(RS_CMD_ID_ScriptSetVarV, size);
+    } else {
+        io->mToCore.commitSync(RS_CMD_ID_ScriptSetVarV, size);
+    }
+}
+
 static inline void rsHCAPI_AllocationData (RsContext rsc, RsAllocation va, const void * data, uint32_t sizeBytes)
 {
     ThreadIO *io = &((Context *)rsc)->mIO;
@@ -45,3 +96,26 @@
 
 }
 
+static inline void rsHCAPI_Allocation1DSubElementData (RsContext rsc, RsAllocation va, uint32_t x, const void * data, uint32_t comp_offset, uint32_t sizeBytes)
+{
+    ThreadIO *io = &((Context *)rsc)->mIO;
+    uint32_t size = sizeof(RS_CMD_Allocation1DSubElementData);
+    if (sizeBytes < DATA_SYNC_SIZE) {
+        size += (sizeBytes + 3) & ~3;
+    }
+    RS_CMD_Allocation1DSubElementData *cmd = static_cast<RS_CMD_Allocation1DSubElementData *>(io->mToCore.reserve(size));
+    cmd->va = va;
+    cmd->x = x;
+    cmd->data = data;
+    cmd->comp_offset = comp_offset;
+    cmd->bytes = sizeBytes;
+    if (sizeBytes < DATA_SYNC_SIZE) {
+        cmd->data = (void *)(cmd+1);
+        memcpy(cmd+1, data, sizeBytes);
+        io->mToCore.commit(RS_CMD_ID_Allocation1DSubElementData, size);
+    } else {
+        io->mToCore.commitSync(RS_CMD_ID_Allocation1DSubElementData, size);
+    }
+
+}
+
diff --git a/libs/rs/rsLight.cpp b/libs/rs/rsLight.cpp
deleted file mode 100644
index 6f2cf3e..0000000
--- a/libs/rs/rsLight.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "rsContext.h"
-
-#include <GLES/gl.h>
-
-using namespace android;
-using namespace android::renderscript;
-
-
-Light::Light(Context *rsc, bool isLocal, bool isMono) : ObjectBase(rsc)
-{
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
-    mIsLocal = isLocal;
-    mIsMono = isMono;
-
-    mPosition[0] = 0;
-    mPosition[1] = 0;
-    mPosition[2] = 1;
-    mPosition[3] = 0;
-
-    mColor[0] = 1.f;
-    mColor[1] = 1.f;
-    mColor[2] = 1.f;
-    mColor[3] = 1.f;
-}
-
-Light::~Light()
-{
-}
-
-void Light::setPosition(float x, float y, float z)
-{
-    mPosition[0] = x;
-    mPosition[1] = y;
-    mPosition[2] = z;
-}
-
-void Light::setColor(float r, float g, float b)
-{
-    mColor[0] = r;
-    mColor[1] = g;
-    mColor[2] = b;
-}
-
-void Light::setupGL(uint32_t num) const
-{
-    glLightfv(GL_LIGHT0 + num, GL_DIFFUSE, mColor);
-    glLightfv(GL_LIGHT0 + num, GL_SPECULAR, mColor);
-    glLightfv(GL_LIGHT0 + num, GL_POSITION, mPosition);
-}
-
-////////////////////////////////////////////
-
-LightState::LightState()
-{
-    clear();
-}
-
-LightState::~LightState()
-{
-}
-
-void LightState::clear()
-{
-    mIsLocal = false;
-    mIsMono = false;
-}
-
-
-////////////////////////////////////////////////////
-//
-
-namespace android {
-namespace renderscript {
-
-void rsi_LightBegin(Context *rsc)
-{
-    rsc->mStateLight.clear();
-}
-
-void rsi_LightSetLocal(Context *rsc, bool isLocal)
-{
-    rsc->mStateLight.mIsLocal = isLocal;
-}
-
-void rsi_LightSetMonochromatic(Context *rsc, bool isMono)
-{
-    rsc->mStateLight.mIsMono = isMono;
-}
-
-RsLight rsi_LightCreate(Context *rsc)
-{
-    Light *l = new Light(rsc, rsc->mStateLight.mIsLocal,
-                         rsc->mStateLight.mIsMono);
-    l->incUserRef();
-    return l;
-}
-
-void rsi_LightSetColor(Context *rsc, RsLight vl, float r, float g, float b)
-{
-    Light *l = static_cast<Light *>(vl);
-    l->setColor(r, g, b);
-}
-
-void rsi_LightSetPosition(Context *rsc, RsLight vl, float x, float y, float z)
-{
-    Light *l = static_cast<Light *>(vl);
-    l->setPosition(x, y, z);
-}
-
-
-
-}
-}
diff --git a/libs/rs/rsLight.h b/libs/rs/rsLight.h
deleted file mode 100644
index d8796e6..0000000
--- a/libs/rs/rsLight.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LIGHT_H
-#define ANDROID_LIGHT_H
-
-
-#include "rsObjectBase.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-namespace renderscript {
-
-
-// An element is a group of Components that occupies one cell in a structure.
-class Light : public ObjectBase
-{
-public:
-    Light(Context *, bool isLocal, bool isMono);
-    virtual ~Light();
-
-    // Values, mutable after creation.
-    void setPosition(float x, float y, float z);
-    void setColor(float r, float g, float b);
-
-    void setupGL(uint32_t num) const;
-
-protected:
-    float mColor[4];
-    float mPosition[4];
-    bool mIsLocal;
-    bool mIsMono;
-};
-
-
-class LightState {
-public:
-    LightState();
-    ~LightState();
-
-    void clear();
-
-    bool mIsMono;
-    bool mIsLocal;
-};
-
-
-}
-}
-#endif //ANDROID_LIGHT_H
-
diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp
index c796520..10b38af 100644
--- a/libs/rs/rsLocklessFifo.cpp
+++ b/libs/rs/rsLocklessFifo.cpp
@@ -15,9 +15,11 @@
  */
 
 #include "rsLocklessFifo.h"
+#include "utils/Timers.h"
+#include "utils/StopWatch.h"
 
 using namespace android;
-
+using namespace android::renderscript;
 
 LocklessCommandFifo::LocklessCommandFifo()
 {
@@ -115,6 +117,10 @@
     if (mInShutdown) {
         return;
     }
+
+    //char buf[1024];
+    //sprintf(buf, "RenderScript LocklessCommandFifo::commitSync  %p %i  %i", this, command, sizeInBytes);
+    //StopWatch compileTimer(buf);
     commit(command, sizeInBytes);
     flush();
 }
@@ -128,15 +134,19 @@
     //dumpState("flush 2");
 }
 
+void LocklessCommandFifo::wait()
+{
+    while(isEmpty() && !mInShutdown) {
+        mSignalToControl.set();
+        mSignalToWorker.wait();
+    }
+}
+
 const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData)
 {
     while(1) {
         //dumpState("get");
-        while(isEmpty() && !mInShutdown) {
-            mSignalToControl.set();
-            mSignalToWorker.wait();
-        }
-
+        wait();
         if (mInShutdown) {
             *command = 0;
             *bytesData = 0;
@@ -165,6 +175,30 @@
     //dumpState("next");
 }
 
+bool LocklessCommandFifo::makeSpaceNonBlocking(uint32_t bytes)
+{
+    //dumpState("make space non-blocking");
+    if ((mPut+bytes) > mEnd) {
+        // Need to loop regardless of where get is.
+        if((mGet > mPut) && (mBuffer+4 >= mGet)) {
+            return false;
+        }
+
+        // Toss in a reset then the normal wait for space will do the rest.
+        reinterpret_cast<uint16_t *>(mPut)[0] = 0;
+        reinterpret_cast<uint16_t *>(mPut)[1] = 0;
+        mPut = mBuffer;
+        mSignalToWorker.set();
+    }
+
+    // it will fit here so we just need to wait for space.
+    if(getFreeSpace() < bytes) {
+        return false;
+    }
+
+    return true;
+}
+
 void LocklessCommandFifo::makeSpace(uint32_t bytes)
 {
     //dumpState("make space");
@@ -178,6 +212,7 @@
         reinterpret_cast<uint16_t *>(mPut)[0] = 0;
         reinterpret_cast<uint16_t *>(mPut)[1] = 0;
         mPut = mBuffer;
+        mSignalToWorker.set();
     }
 
     // it will fit here so we just need to wait for space.
@@ -189,82 +224,6 @@
 
 void LocklessCommandFifo::dumpState(const char *s) const
 {
-    LOGV("%s  put %p, get %p,  buf %p,  end %p", s, mPut, mGet, mBuffer, mEnd);
-}
-
-LocklessCommandFifo::Signal::Signal()
-{
-    mSet = true;
-}
-
-LocklessCommandFifo::Signal::~Signal()
-{
-    pthread_mutex_destroy(&mMutex);
-    pthread_cond_destroy(&mCondition);
-}
-
-bool LocklessCommandFifo::Signal::init()
-{
-    int status = pthread_mutex_init(&mMutex, NULL);
-    if (status) {
-        LOGE("LocklessFifo mutex init failure");
-        return false;
-    }
-
-    status = pthread_cond_init(&mCondition, NULL);
-    if (status) {
-        LOGE("LocklessFifo condition init failure");
-        pthread_mutex_destroy(&mMutex);
-        return false;
-    }
-
-    return true;
-}
-
-void LocklessCommandFifo::Signal::set()
-{
-    int status;
-
-    status = pthread_mutex_lock(&mMutex);
-    if (status) {
-        LOGE("LocklessCommandFifo: error %i locking for set condition.", status);
-        return;
-    }
-
-    mSet = true;
-
-    status = pthread_cond_signal(&mCondition);
-    if (status) {
-        LOGE("LocklessCommandFifo: error %i on set condition.", status);
-    }
-
-    status = pthread_mutex_unlock(&mMutex);
-    if (status) {
-        LOGE("LocklessCommandFifo: error %i unlocking for set condition.", status);
-    }
-}
-
-void LocklessCommandFifo::Signal::wait()
-{
-    int status;
-
-    status = pthread_mutex_lock(&mMutex);
-    if (status) {
-        LOGE("LocklessCommandFifo: error %i locking for condition.", status);
-        return;
-    }
-
-    if (!mSet) {
-        status = pthread_cond_wait(&mCondition, &mMutex);
-        if (status) {
-            LOGE("LocklessCommandFifo: error %i waiting on condition.", status);
-        }
-    }
-    mSet = false;
-
-    status = pthread_mutex_unlock(&mMutex);
-    if (status) {
-        LOGE("LocklessCommandFifo: error %i unlocking for condition.", status);
-    }
+    LOGV("%s %p  put %p, get %p,  buf %p,  end %p", s, this, mPut, mGet, mBuffer, mEnd);
 }
 
diff --git a/libs/rs/rsLocklessFifo.h b/libs/rs/rsLocklessFifo.h
index d0a4356..b8ceeed 100644
--- a/libs/rs/rsLocklessFifo.h
+++ b/libs/rs/rsLocklessFifo.h
@@ -19,8 +19,10 @@
 
 
 #include "rsUtils.h"
+#include "rsSignal.h"
 
 namespace android {
+namespace renderscript {
 
 
 // A simple FIFO to be used as a producer / consumer between two
@@ -37,24 +39,7 @@
     LocklessCommandFifo();
     ~LocklessCommandFifo();
 
-
 protected:
-    class Signal {
-    public:
-        Signal();
-        ~Signal();
-
-        bool init();
-
-        void set();
-        void wait();
-
-    protected:
-        bool mSet;
-        pthread_mutex_t mMutex;
-        pthread_cond_t mCondition;
-    };
-
     uint8_t * volatile mPut;
     uint8_t * volatile mGet;
     uint8_t * mBuffer;
@@ -65,18 +50,19 @@
     Signal mSignalToWorker;
     Signal mSignalToControl;
 
-
-
 public:
     void * reserve(uint32_t bytes);
     void commit(uint32_t command, uint32_t bytes);
     void commitSync(uint32_t command, uint32_t bytes);
 
     void flush();
+    void wait();
+
     const void * get(uint32_t *command, uint32_t *bytesData);
     void next();
 
     void makeSpace(uint32_t bytes);
+    bool makeSpaceNonBlocking(uint32_t bytes);
 
     bool isEmpty() const;
     uint32_t getFreeSpace() const;
@@ -88,4 +74,5 @@
 
 
 }
+}
 #endif
diff --git a/libs/rs/rsMatrix.cpp b/libs/rs/rsMatrix.cpp
index 2f21405..94eef13 100644
--- a/libs/rs/rsMatrix.cpp
+++ b/libs/rs/rsMatrix.cpp
@@ -73,7 +73,7 @@
     s = sinf(rot);
 
     const float len = sqrtf(x*x + y*y + z*z);
-    if (!(len != 1)) {
+    if (len != 1) {
         const float recipLen = 1.f / len;
         x *= recipLen;
         y *= recipLen;
diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp
index d595b4e..fd604e5 100644
--- a/libs/rs/rsMesh.cpp
+++ b/libs/rs/rsMesh.cpp
@@ -14,28 +14,254 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
 
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+#include <GLES/glext.h>
+#else
+#include "rsContextHostStub.h"
+
+#include <OpenGL/gl.h>
+#include <OpenGl/glext.h>
+#endif
+
+
 using namespace android;
 using namespace android::renderscript;
 
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
 Mesh::Mesh(Context *rsc) : ObjectBase(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
-    mVerticies = NULL;
-    mVerticiesCount = 0;
     mPrimitives = NULL;
     mPrimitivesCount = 0;
+    mVertexBuffers = NULL;
+    mVertexBufferCount = 0;
 }
 
 Mesh::~Mesh()
 {
+    if(mVertexBuffers) {
+        delete[] mVertexBuffers;
+    }
+
+    if(mPrimitives) {
+        for(uint32_t i = 0; i < mPrimitivesCount; i ++) {
+            delete mPrimitives[i];
+        }
+        delete[] mPrimitives;
+    }
 }
 
+void Mesh::render(Context *rsc) const
+{
+    for(uint32_t ct = 0; ct < mPrimitivesCount; ct ++) {
+        renderPrimitive(rsc, ct);
+    }
+}
+
+void Mesh::renderPrimitive(Context *rsc, uint32_t primIndex) const {
+    if (primIndex >= mPrimitivesCount) {
+        LOGE("Invalid primitive index");
+        return;
+    }
+
+    Primitive_t *prim = mPrimitives[primIndex];
+
+    if (prim->mIndexBuffer.get()) {
+        renderPrimitiveRange(rsc, primIndex, 0, prim->mIndexBuffer->getType()->getDimX());
+        return;
+    }
+
+    renderPrimitiveRange(rsc, primIndex, 0, mVertexBuffers[0]->getType()->getDimX());
+}
+
+void Mesh::renderPrimitiveRange(Context *rsc, uint32_t primIndex, uint32_t start, uint32_t len) const
+{
+    if (len < 1 || primIndex >= mPrimitivesCount) {
+        return;
+    }
+
+    rsc->checkError("Mesh::renderPrimitiveRange 1");
+    VertexArray va;
+    for (uint32_t ct=0; ct < mVertexBufferCount; ct++) {
+        mVertexBuffers[ct]->uploadCheck(rsc);
+        if (mVertexBuffers[ct]->getIsBufferObject()) {
+            va.setActiveBuffer(mVertexBuffers[ct]->getBufferObjectID());
+        } else {
+            va.setActiveBuffer(mVertexBuffers[ct]->getPtr());
+        }
+        mVertexBuffers[ct]->getType()->enableGLVertexBuffer(&va);
+    }
+    va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache);
+
+    rsc->checkError("Mesh::renderPrimitiveRange 2");
+    Primitive_t *prim = mPrimitives[primIndex];
+    if (prim->mIndexBuffer.get()) {
+        prim->mIndexBuffer->uploadCheck(rsc);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prim->mIndexBuffer->getBufferObjectID());
+        glDrawElements(prim->mGLPrimitive, len, GL_UNSIGNED_SHORT, (uint16_t *)(start * 2));
+    } else {
+        glDrawArrays(prim->mGLPrimitive, start, len);
+    }
+
+    rsc->checkError("Mesh::renderPrimitiveRange");
+}
+
+
+void Mesh::uploadAll(Context *rsc)
+{
+    for (uint32_t ct = 0; ct < mVertexBufferCount; ct ++) {
+        if (mVertexBuffers[ct].get()) {
+            mVertexBuffers[ct]->deferedUploadToBufferObject(rsc);
+        }
+    }
+
+    for (uint32_t ct = 0; ct < mPrimitivesCount; ct ++) {
+        if (mPrimitives[ct]->mIndexBuffer.get()) {
+            mPrimitives[ct]->mIndexBuffer->deferedUploadToBufferObject(rsc);
+        }
+    }
+}
+
+void Mesh::updateGLPrimitives()
+{
+    for(uint32_t i = 0; i < mPrimitivesCount; i ++) {
+        switch(mPrimitives[i]->mPrimitive) {
+            case RS_PRIMITIVE_POINT:          mPrimitives[i]->mGLPrimitive = GL_POINTS; break;
+            case RS_PRIMITIVE_LINE:           mPrimitives[i]->mGLPrimitive = GL_LINES; break;
+            case RS_PRIMITIVE_LINE_STRIP:     mPrimitives[i]->mGLPrimitive = GL_LINE_STRIP; break;
+            case RS_PRIMITIVE_TRIANGLE:       mPrimitives[i]->mGLPrimitive = GL_TRIANGLES; break;
+            case RS_PRIMITIVE_TRIANGLE_STRIP: mPrimitives[i]->mGLPrimitive = GL_TRIANGLE_STRIP; break;
+            case RS_PRIMITIVE_TRIANGLE_FAN:   mPrimitives[i]->mGLPrimitive = GL_TRIANGLE_FAN; break;
+        }
+    }
+}
+
+void Mesh::serialize(OStream *stream) const
+{
+    // Need to identify ourselves
+    stream->addU32((uint32_t)getClassId());
+
+    String8 name(getName());
+    stream->addString(&name);
+
+    // Store number of vertex streams
+    stream->addU32(mVertexBufferCount);
+    for(uint32_t vCount = 0; vCount < mVertexBufferCount; vCount ++) {
+        mVertexBuffers[vCount]->serialize(stream);
+    }
+
+    stream->addU32(mPrimitivesCount);
+    // Store the primitives
+    for (uint32_t pCount = 0; pCount < mPrimitivesCount; pCount ++) {
+        Primitive_t * prim = mPrimitives[pCount];
+
+        stream->addU8((uint8_t)prim->mPrimitive);
+
+        if(prim->mIndexBuffer.get()) {
+            stream->addU32(1);
+            prim->mIndexBuffer->serialize(stream);
+        }
+        else {
+            stream->addU32(0);
+        }
+    }
+}
+
+Mesh *Mesh::createFromStream(Context *rsc, IStream *stream)
+{
+    // First make sure we are reading the correct object
+    RsA3DClassID classID = (RsA3DClassID)stream->loadU32();
+    if(classID != RS_A3D_CLASS_ID_MESH) {
+        LOGE("mesh loading skipped due to invalid class id");
+        return NULL;
+    }
+
+    Mesh * mesh = new Mesh(rsc);
+
+    String8 name;
+    stream->loadString(&name);
+    mesh->setName(name.string(), name.size());
+
+    mesh->mVertexBufferCount = stream->loadU32();
+    if(mesh->mVertexBufferCount) {
+        mesh->mVertexBuffers = new ObjectBaseRef<Allocation>[mesh->mVertexBufferCount];
+
+        for(uint32_t vCount = 0; vCount < mesh->mVertexBufferCount; vCount ++) {
+            Allocation *vertexAlloc = Allocation::createFromStream(rsc, stream);
+            mesh->mVertexBuffers[vCount].set(vertexAlloc);
+        }
+    }
+
+    mesh->mPrimitivesCount = stream->loadU32();
+    if(mesh->mPrimitivesCount) {
+        mesh->mPrimitives = new Primitive_t *[mesh->mPrimitivesCount];
+
+        // load all primitives
+        for (uint32_t pCount = 0; pCount < mesh->mPrimitivesCount; pCount ++) {
+            Primitive_t * prim = new Primitive_t;
+            mesh->mPrimitives[pCount] = prim;
+
+            prim->mPrimitive = (RsPrimitive)stream->loadU8();
+
+            // Check to see if the index buffer was stored
+            uint32_t isIndexPresent = stream->loadU32();
+            if(isIndexPresent) {
+                Allocation *indexAlloc = Allocation::createFromStream(rsc, stream);
+                prim->mIndexBuffer.set(indexAlloc);
+            }
+        }
+    }
+
+    mesh->updateGLPrimitives();
+    mesh->uploadAll(rsc);
+
+    return mesh;
+}
+
+void Mesh::computeBBox() {
+    float *posPtr = NULL;
+    uint32_t vectorSize = 0;
+    uint32_t stride = 0;
+    uint32_t numVerts = 0;
+    // First we need to find the position ptr and stride
+    for (uint32_t ct=0; ct < mVertexBufferCount; ct++) {
+        const Type *bufferType = mVertexBuffers[ct]->getType();
+        const Element *bufferElem = bufferType->getElement();
+
+        for (uint32_t ct=0; ct < bufferElem->getFieldCount(); ct++) {
+            if(strcmp(bufferElem->getFieldName(ct), "position") == 0) {
+                vectorSize = bufferElem->getField(ct)->getComponent().getVectorSize();
+                stride = bufferElem->getSizeBytes() / sizeof(float);
+                uint32_t offset = bufferElem->getFieldOffsetBytes(ct);
+                posPtr = (float*)((uint8_t*)mVertexBuffers[ct]->getPtr() + offset);
+                numVerts = bufferType->getDimX();
+                break;
+            }
+        }
+        if(posPtr) {
+            break;
+        }
+    }
+
+    mBBoxMin[0] = mBBoxMin[1] = mBBoxMin[2] = 1e6;
+    mBBoxMax[0] = mBBoxMax[1] = mBBoxMax[2] = -1e6;
+    if(!posPtr) {
+        LOGE("Unable to compute bounding box");
+        mBBoxMin[0] = mBBoxMin[1] = mBBoxMin[2] = 0.0f;
+        mBBoxMax[0] = mBBoxMax[1] = mBBoxMax[2] = 0.0f;
+        return;
+    }
+
+    for(uint32_t i = 0; i < numVerts; i ++) {
+        for(uint32_t v = 0; v < vectorSize; v ++) {
+            mBBoxMin[v] = rsMin(mBBoxMin[v], posPtr[v]);
+            mBBoxMax[v] = rsMax(mBBoxMax[v], posPtr[v]);
+        }
+        posPtr += stride;
+    }
+}
 
 
 MeshContext::MeshContext()
@@ -46,3 +272,80 @@
 {
 }
 
+namespace android {
+namespace renderscript {
+
+RsMesh rsi_MeshCreate(Context *rsc, uint32_t vtxCount, uint32_t idxCount)
+{
+    Mesh *sm = new Mesh(rsc);
+    sm->incUserRef();
+
+    sm->mPrimitivesCount = idxCount;
+    sm->mPrimitives = new Mesh::Primitive_t *[sm->mPrimitivesCount];
+    for(uint32_t ct = 0; ct < idxCount; ct ++) {
+        sm->mPrimitives[ct] = new Mesh::Primitive_t;
+    }
+
+    sm->mVertexBufferCount = vtxCount;
+    sm->mVertexBuffers = new ObjectBaseRef<Allocation>[vtxCount];
+
+    return sm;
+}
+
+void rsi_MeshBindVertex(Context *rsc, RsMesh mv, RsAllocation va, uint32_t slot)
+{
+    Mesh *sm = static_cast<Mesh *>(mv);
+    rsAssert(slot < sm->mVertexBufferCount);
+
+    sm->mVertexBuffers[slot].set((Allocation *)va);
+}
+
+void rsi_MeshBindIndex(Context *rsc, RsMesh mv, RsAllocation va, uint32_t primType, uint32_t slot)
+{
+    Mesh *sm = static_cast<Mesh *>(mv);
+    rsAssert(slot < sm->mPrimitivesCount);
+
+    sm->mPrimitives[slot]->mIndexBuffer.set((Allocation *)va);
+    sm->mPrimitives[slot]->mPrimitive = (RsPrimitive)primType;
+    sm->updateGLPrimitives();
+}
+
+}}
+
+void rsaMeshGetVertexBufferCount(RsContext con, RsMesh mv, int32_t *numVtx)
+{
+    Mesh *sm = static_cast<Mesh *>(mv);
+    *numVtx = sm->mVertexBufferCount;
+}
+
+void rsaMeshGetIndexCount(RsContext con, RsMesh mv, int32_t *numIdx)
+{
+    Mesh *sm = static_cast<Mesh *>(mv);
+    *numIdx = sm->mPrimitivesCount;
+}
+
+void rsaMeshGetVertices(RsContext con, RsMesh mv, RsAllocation *vtxData, uint32_t vtxDataCount)
+{
+    Mesh *sm = static_cast<Mesh *>(mv);
+    rsAssert(vtxDataCount == sm->mVertexBufferCount);
+
+    for(uint32_t ct = 0; ct < vtxDataCount; ct ++) {
+        vtxData[ct] = sm->mVertexBuffers[ct].get();
+        sm->mVertexBuffers[ct]->incUserRef();
+    }
+}
+
+void rsaMeshGetIndices(RsContext con, RsMesh mv, RsAllocation *va, uint32_t *primType, uint32_t idxDataCount)
+{
+    Mesh *sm = static_cast<Mesh *>(mv);
+    rsAssert(idxDataCount == sm->mPrimitivesCount);
+
+    for(uint32_t ct = 0; ct < idxDataCount; ct ++) {
+        va[ct] = sm->mPrimitives[ct]->mIndexBuffer.get();
+        primType[ct] = sm->mPrimitives[ct]->mPrimitive;
+        if(sm->mPrimitives[ct]->mIndexBuffer.get()) {
+            sm->mPrimitives[ct]->mIndexBuffer->incUserRef();
+        }
+    }
+
+}
diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h
index 5201abd..ed01c38 100644
--- a/libs/rs/rsMesh.h
+++ b/libs/rs/rsMesh.h
@@ -32,45 +32,40 @@
     Mesh(Context *);
     ~Mesh();
 
-    struct Verticies_t
-    {
-        Allocation ** mAllocations;
-        uint32_t mAllocationCount;
+    // Contains vertex data
+    // Position, normal, texcoord, etc could either be strided in one allocation
+    // of provided separetely in multiple ones
+    ObjectBaseRef<Allocation> *mVertexBuffers;
+    uint32_t mVertexBufferCount;
 
-        size_t mVertexDataSize;
-
-        size_t mOffsetCoord;
-        size_t mOffsetTex;
-        size_t mOffsetNorm;
-
-        size_t mSizeCoord;
-        size_t mSizeTex;
-        size_t mSizeNorm;
-
-        uint32_t mBufferObject;
-    };
-
+    // Either mIndexBuffer, mPrimitiveBuffer or both could have a NULL reference
+    // If both are null, mPrimitive only would be used to render the mesh
     struct Primitive_t
     {
-        RsPrimitive mType;
-        Verticies_t *mVerticies;
+        ObjectBaseRef<Allocation> mIndexBuffer;
 
-        uint32_t mIndexCount;
-        uint16_t *mIndicies;
-
-        uint32_t mRestartCounts;
-        uint16_t *mRestarts;
+        RsPrimitive mPrimitive;
+        uint32_t mGLPrimitive;
     };
 
-    Verticies_t * mVerticies;
-    uint32_t mVerticiesCount;
-
     Primitive_t ** mPrimitives;
     uint32_t mPrimitivesCount;
 
+    void render(Context *) const;
+    void renderPrimitive(Context *, uint32_t primIndex) const;
+    void renderPrimitiveRange(Context *, uint32_t primIndex, uint32_t start, uint32_t len) const;
+    void uploadAll(Context *);
+    void updateGLPrimitives();
 
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_MESH; }
+    static Mesh *createFromStream(Context *rsc, IStream *stream);
 
-    void analyzeElement();
+    // Bounding volumes
+    float mBBoxMin[3];
+    float mBBoxMax[3];
+    void computeBBox();
+
 protected:
 };
 
@@ -88,3 +83,4 @@
 #endif //ANDROID_RS_TRIANGLE_MESH_H
 
 
+
diff --git a/libs/rs/rsMutex.cpp b/libs/rs/rsMutex.cpp
new file mode 100644
index 0000000..37752f2
--- /dev/null
+++ b/libs/rs/rsMutex.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#include "rsMutex.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Mutex::Mutex()
+{
+}
+
+Mutex::~Mutex()
+{
+    pthread_mutex_destroy(&mMutex);
+}
+
+bool Mutex::init()
+{
+    int status = pthread_mutex_init(&mMutex, NULL);
+    if (status) {
+        LOGE("Mutex::Mutex init failure");
+        return false;
+    }
+    return true;
+}
+
+bool Mutex::lock()
+{
+    int status;
+    status = pthread_mutex_lock(&mMutex);
+    if (status) {
+        LOGE("Mutex: error %i locking.", status);
+        return false;
+    }
+    return true;
+}
+
+bool Mutex::unlock()
+{
+    int status;
+    status = pthread_mutex_unlock(&mMutex);
+    if (status) {
+        LOGE("Mutex error %i unlocking.", status);
+        return false;
+    }
+    return true;
+}
+
+
diff --git a/libs/rs/rsMutex.h b/libs/rs/rsMutex.h
new file mode 100644
index 0000000..47725d7
--- /dev/null
+++ b/libs/rs/rsMutex.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_MUTEX_H
+#define ANDROID_RS_MUTEX_H
+
+
+#include "rsUtils.h"
+
+namespace android {
+namespace renderscript {
+
+class Mutex {
+public:
+    Mutex();
+    ~Mutex();
+
+    bool init();
+    bool lock();
+    bool unlock();
+
+protected:
+    pthread_mutex_t mMutex;
+};
+
+}
+}
+
+#endif
+
diff --git a/libs/rs/rsNoise.cpp b/libs/rs/rsNoise.cpp
deleted file mode 100644
index 4b67586..0000000
--- a/libs/rs/rsNoise.cpp
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * This implementation of the noise functions was ported from the Java
- * implementation by Jerry Huxtable (http://www.jhlabs.com) under
- * Apache License 2.0 (see http://jhlabs.com/ip/filters/download.html)
- *
- * Original header:
- *
- * Copyright 2006 Jerry Huxtable
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "rsNoise.h"
-
-#include <math.h>
-#include <stdlib.h>
-#include <time.h>
-
-namespace android {
-namespace renderscript {
-
-#define B 0x100
-#define BM 0xff
-#define N 0x1000
-
-static int p[B + B + 2];
-static float g3[B + B + 2][3];
-static float g2[B + B + 2][2];
-static float g1[B + B + 2];
-static bool noise_start = true;
-
-#define lerpf(start, stop, amount) start + (stop - start) * amount
-
-static inline float noise_sCurve(float t)
-{
-    return t * t * (3.0f - 2.0f * t);
-}
-
-inline void SC_normalizef2(float v[])
-{
-    float s = (float)sqrtf(v[0] * v[0] + v[1] * v[1]);
-    v[0] = v[0] / s;
-    v[1] = v[1] / s;
-}
-
-inline void SC_normalizef3(float v[])
-{
-    float s = (float)sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
-    v[0] = v[0] / s;
-    v[1] = v[1] / s;
-    v[2] = v[2] / s;
-}
-
-static void noise_init()
-{
-    int i, j, k;
-    
-    for (i = 0; i < B; i++) {
-        p[i] = i;
-        
-        g1[i] = (float)((rand() % (B + B)) - B) / B;
-        
-        for (j = 0; j < 2; j++)
-            g2[i][j] = (float)((rand() % (B + B)) - B) / B;
-        SC_normalizef2(g2[i]);
-        
-        for (j = 0; j < 3; j++)
-            g3[i][j] = (float)((rand() % (B + B)) - B) / B;
-        SC_normalizef3(g3[i]);
-    }
-    
-    for (i = B-1; i >= 0; i--) {
-        k = p[i];
-        p[i] = p[j = rand() % B];
-        p[j] = k;
-    }
-    
-    for (i = 0; i < B + 2; i++) {
-        p[B + i] = p[i];
-        g1[B + i] = g1[i];
-        for (j = 0; j < 2; j++)
-            g2[B + i][j] = g2[i][j];
-        for (j = 0; j < 3; j++)
-            g3[B + i][j] = g3[i][j];
-    }
-}
-
-float SC_noisef(float x)
-{
-    srand(time(NULL));
-    int bx0, bx1;
-    float rx0, rx1, sx, t, u, v;
-    
-    if (noise_start) {
-        noise_start = false;
-        noise_init();
-    }
-    
-    t = x + N;
-    bx0 = ((int)t) & BM;
-    bx1 = (bx0+1) & BM;
-    rx0 = t - (int)t;
-    rx1 = rx0 - 1.0f;
-    
-    sx = noise_sCurve(rx0);
-    
-    u = rx0 * g1[p[bx0]];
-    v = rx1 * g1[p[bx1]];
-    return 2.3f * lerpf(u, v, sx);
-}
-
-float SC_noisef2(float x, float y)
-{
-    srand(time(NULL));
-    int bx0, bx1, by0, by1, b00, b10, b01, b11;
-    float rx0, rx1, ry0, ry1, sx, sy, a, b, t, u, v;
-    float *q;
-    int i, j;
-    
-    if (noise_start) {
-        noise_start = false;
-        noise_init();
-    }
-    
-    t = x + N;
-    bx0 = ((int)t) & BM;
-    bx1 = (bx0+1) & BM;
-    rx0 = t - (int)t;
-    rx1 = rx0 - 1.0f;
-	
-    t = y + N;
-    by0 = ((int)t) & BM;
-    by1 = (by0+1) & BM;
-    ry0 = t - (int)t;
-    ry1 = ry0 - 1.0f;
-	
-    i = p[bx0];
-    j = p[bx1];
-    
-    b00 = p[i + by0];
-    b10 = p[j + by0];
-    b01 = p[i + by1];
-    b11 = p[j + by1];
-    
-    sx = noise_sCurve(rx0);
-    sy = noise_sCurve(ry0);
-    
-    q = g2[b00]; u = rx0 * q[0] + ry0 * q[1];
-    q = g2[b10]; v = rx1 * q[0] + ry0 * q[1];
-    a = lerpf(u, v, sx);
-    
-    q = g2[b01]; u = rx0 * q[0] + ry1 * q[1];
-    q = g2[b11]; v = rx1 * q[0] + ry1 * q[1];
-    b = lerpf(u, v, sx);
-    
-    return 1.5f*lerpf(a, b, sy);
-}
-
-float SC_noisef3(float x, float y, float z)
-{
-    srand(time(NULL));
-    int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
-    float rx0, rx1, ry0, ry1, rz0, rz1, sy, sz, a, b, c, d, t, u, v;
-    float *q;
-    int i, j;
-    
-    if (noise_start) {
-        noise_start = false;
-        noise_init();
-    }
-    
-    t = x + N;
-    bx0 = ((int)t) & BM;
-    bx1 = (bx0+1) & BM;
-    rx0 = t - (int)t;
-    rx1 = rx0 - 1.0f;
-    
-    t = y + N;
-    by0 = ((int)t) & BM;
-    by1 = (by0+1) & BM;
-    ry0 = t - (int)t;
-    ry1 = ry0 - 1.0f;
-	
-    t = z + N;
-    bz0 = ((int)t) & BM;
-    bz1 = (bz0+1) & BM;
-    rz0 = t - (int)t;
-    rz1 = rz0 - 1.0f;
-	
-    i = p[bx0];
-    j = p[bx1];
-    
-    b00 = p[i + by0];
-    b10 = p[j + by0];
-    b01 = p[i + by1];
-    b11 = p[j + by1];
-    
-    t  = noise_sCurve(rx0);
-    sy = noise_sCurve(ry0);
-    sz = noise_sCurve(rz0);
-    
-    q = g3[b00 + bz0]; u = rx0 * q[0] + ry0 * q[1] + rz0 * q[2];
-    q = g3[b10 + bz0]; v = rx1 * q[0] + ry0 * q[1] + rz0 * q[2];
-    a = lerpf(u, v, t);
-    
-    q = g3[b01 + bz0]; u = rx0 * q[0] + ry1 * q[1] + rz0 * q[2];
-    q = g3[b11 + bz0]; v = rx1 * q[0] + ry1 * q[1] + rz0 * q[2];
-    b = lerpf(u, v, t);
-    
-    c = lerpf(a, b, sy);
-    
-    q = g3[b00 + bz1]; u = rx0 * q[0] + ry0 * q[1] + rz1 * q[2];
-    q = g3[b10 + bz1]; v = rx1 * q[0] + ry0 * q[1] + rz1 * q[2];
-    a = lerpf(u, v, t);
-    
-    q = g3[b01 + bz1]; u = rx0 * q[0] + ry1 * q[1] + rz1 * q[2];
-    q = g3[b11 + bz1]; v = rx1 * q[0] + ry1 * q[1] + rz1 * q[2];
-    b = lerpf(u, v, t);
-    
-    d = lerpf(a, b, sy);
-    
-    return 1.5f*lerpf(c, d, sz);
-}
-
-float SC_turbulencef2(float x, float y, float octaves)
-{
-    srand(time(NULL));
-    float t = 0.0f;
-    
-    for (float f = 1.0f; f <= octaves; f *= 2)
-        t += fabs(SC_noisef2(f * x, f * y)) / f;
-    return t;
-}
-
-float SC_turbulencef3(float x, float y, float z, float octaves)
-{
-    srand(time(NULL));
-    float t = 0.0f;
-    
-    for (float f = 1.0f; f <= octaves; f *= 2)
-        t += fabs(SC_noisef3(f * x, f * y, f * z)) / f;
-    return t;
-}
-
-}
-}
diff --git a/libs/rs/rsNoise.h b/libs/rs/rsNoise.h
deleted file mode 100644
index 9040751..0000000
--- a/libs/rs/rsNoise.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_RS_NOISE_H
-#define ANDROID_RS_NOISE_H
-
-// ---------------------------------------------------------------------------
-namespace android {
-namespace renderscript {
-
-void SC_normalizef2(float v[]);
-void SC_normalizef3(float v[]);
-float SC_noisef(float x);
-float SC_noisef2(float x, float y);
-float SC_noisef3(float x, float y, float z);
-float SC_turbulencef2(float x, float y, float octaves);
-float SC_turbulencef3(float x, float y, float z, float octaves);
-
-}
-}
-
-#endif
diff --git a/libs/rs/rsObjectBase.cpp b/libs/rs/rsObjectBase.cpp
index 677413e..44dc042 100644
--- a/libs/rs/rsObjectBase.cpp
+++ b/libs/rs/rsObjectBase.cpp
@@ -15,120 +15,172 @@
  */
 
 #include "rsObjectBase.h"
+
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
+#else
+#include "rsContextHostStub.h"
+#endif
+
 
 using namespace android;
 using namespace android::renderscript;
 
+pthread_mutex_t ObjectBase::gObjectInitMutex = PTHREAD_MUTEX_INITIALIZER;
+
 ObjectBase::ObjectBase(Context *rsc)
 {
     mUserRefCount = 0;
     mSysRefCount = 0;
-    mName = NULL;
-    mRSC = NULL;
+    mRSC = rsc;
     mNext = NULL;
     mPrev = NULL;
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
-    setContext(rsc);
+
+#if RS_OBJECT_DEBUG
+    mStack.update(2);
+#endif
+
+    rsAssert(rsc);
+    add();
+    //LOGV("ObjectBase %p con", this);
 }
 
 ObjectBase::~ObjectBase()
 {
     //LOGV("~ObjectBase %p  ref %i,%i", this, mUserRefCount, mSysRefCount);
+#if RS_OBJECT_DEBUG
+    mStack.dump();
+#endif
+
+    if(mPrev || mNext) {
+        // While the normal practice is to call remove before we call
+        // delete.  Its possible for objects without a re-use list
+        // for avoiding duplication to be created on the stack.  In those
+        // cases we need to remove ourself here.
+        asyncLock();
+        remove();
+        asyncUnlock();
+    }
+
     rsAssert(!mUserRefCount);
     rsAssert(!mSysRefCount);
-    remove();
-    delete[] mName;
 }
 
 void ObjectBase::dumpLOGV(const char *op) const
 {
-    if (mName) {
-        LOGV("%s RSobj %p, name %s, refs %i,%i  from %s,%i links %p,%p,%p",
-             op, this, mName, mUserRefCount, mSysRefCount, mAllocFile, mAllocLine, mNext, mPrev, mRSC);
+    if (mName.size()) {
+        LOGV("%s RSobj %p, name %s, refs %i,%i  links %p,%p,%p",
+             op, this, mName.string(), mUserRefCount, mSysRefCount, mNext, mPrev, mRSC);
     } else {
-        LOGV("%s RSobj %p, no-name, refs %i,%i  from %s,%i links %p,%p,%p",
-             op, this, mUserRefCount, mSysRefCount, mAllocFile, mAllocLine, mNext, mPrev, mRSC);
-    }
-}
-
-void ObjectBase::setContext(Context *rsc)
-{
-    if (mRSC) {
-        remove();
-    }
-    mRSC = rsc;
-    if (rsc) {
-        add();
+        LOGV("%s RSobj %p, no-name, refs %i,%i  links %p,%p,%p",
+             op, this, mUserRefCount, mSysRefCount, mNext, mPrev, mRSC);
     }
 }
 
 void ObjectBase::incUserRef() const
 {
-    mUserRefCount ++;
-    //LOGV("ObjectBase %p inc ref %i", this, mRefCount);
+    android_atomic_inc(&mUserRefCount);
+    //LOGV("ObjectBase %p incU ref %i, %i", this, mUserRefCount, mSysRefCount);
 }
 
 void ObjectBase::incSysRef() const
 {
-    mSysRefCount ++;
-    //LOGV("ObjectBase %p inc ref %i", this, mRefCount);
+    android_atomic_inc(&mSysRefCount);
+    //LOGV("ObjectBase %p incS ref %i, %i", this, mUserRefCount, mSysRefCount);
 }
 
-bool ObjectBase::checkDelete() const
+void ObjectBase::preDestroy() const
 {
-    if (!(mSysRefCount | mUserRefCount)) {
-        if (mRSC && mRSC->props.mLogObjects) {
-            dumpLOGV("checkDelete");
-        }
-        delete this;
-        return true;
-    }
-    return false;
 }
 
+bool ObjectBase::checkDelete(const ObjectBase *ref)
+{
+    if (!ref) {
+        return false;
+    }
+
+    asyncLock();
+    // This lock protects us against the non-RS threads changing
+    // the ref counts.  At this point we should be the only thread
+    // working on them.
+    if (ref->mUserRefCount || ref->mSysRefCount) {
+        asyncUnlock();
+        return false;
+    }
+
+    ref->remove();
+    // At this point we can unlock because there should be no possible way
+    // for another thread to reference this object.
+    ref->preDestroy();
+    asyncUnlock();
+    delete ref;
+    return true;
+}
+
+
 bool ObjectBase::decUserRef() const
 {
     rsAssert(mUserRefCount > 0);
-    mUserRefCount --;
-    //dumpObj("decUserRef");
-    return checkDelete();
+#if RS_OBJECT_DEBUG
+    LOGV("ObjectBase %p decU ref %i, %i", this, mUserRefCount, mSysRefCount);
+    if (mUserRefCount <= 0) {
+        mStack.dump();
+    }
+#endif
+
+
+    if ((android_atomic_dec(&mUserRefCount) <= 1) &&
+        (android_atomic_acquire_load(&mSysRefCount) <= 0)) {
+        return checkDelete(this);
+    }
+    return false;
 }
 
 bool ObjectBase::zeroUserRef() const
 {
-    mUserRefCount = 0;
-    //dumpObj("zeroUserRef");
-    return checkDelete();
+    //LOGV("ObjectBase %p zeroU ref %i, %i", this, mUserRefCount, mSysRefCount);
+    android_atomic_acquire_store(0, &mUserRefCount);
+    if (android_atomic_acquire_load(&mSysRefCount) <= 0) {
+        return checkDelete(this);
+    }
+    return false;
 }
 
 bool ObjectBase::decSysRef() const
 {
+    //LOGV("ObjectBase %p decS ref %i, %i", this, mUserRefCount, mSysRefCount);
     rsAssert(mSysRefCount > 0);
-    mSysRefCount --;
-    //dumpObj("decSysRef");
-    return checkDelete();
+    if ((android_atomic_dec(&mSysRefCount) <= 1) &&
+        (android_atomic_acquire_load(&mUserRefCount) <= 0)) {
+        return checkDelete(this);
+    }
+    return false;
 }
 
 void ObjectBase::setName(const char *name)
 {
-    setName(name, strlen(name));
+    mName.setTo(name);
 }
 
 void ObjectBase::setName(const char *name, uint32_t len)
 {
-    delete mName;
-    mName = NULL;
-    if (name) {
-        mName = new char[len + 1];
-        memcpy(mName, name, len);
-        mName[len] = 0;
-    }
+    mName.setTo(name, len);
+}
+
+void ObjectBase::asyncLock()
+{
+    pthread_mutex_lock(&gObjectInitMutex);
+}
+
+void ObjectBase::asyncUnlock()
+{
+    pthread_mutex_unlock(&gObjectInitMutex);
 }
 
 void ObjectBase::add() const
 {
+    asyncLock();
+
     rsAssert(!mNext);
     rsAssert(!mPrev);
     //LOGV("calling add  rsc %p", mRSC);
@@ -137,6 +189,8 @@
         mRSC->mObjHead->mPrev = this;
     }
     mRSC->mObjHead = this;
+
+    asyncUnlock();
 }
 
 void ObjectBase::remove() const
@@ -147,6 +201,7 @@
         rsAssert(!mNext);
         return;
     }
+
     if (mRSC->mObjHead == this) {
         mRSC->mObjHead = mNext;
     }
@@ -188,6 +243,8 @@
 
 void ObjectBase::dumpAll(Context *rsc)
 {
+    asyncLock();
+
     LOGV("Dumping all objects");
     const ObjectBase * o = rsc->mObjHead;
     while (o) {
@@ -195,5 +252,23 @@
         o->dumpLOGV("  ");
         o = o->mNext;
     }
+
+    asyncUnlock();
+}
+
+bool ObjectBase::isValid(const Context *rsc, const ObjectBase *obj)
+{
+    asyncLock();
+
+    const ObjectBase * o = rsc->mObjHead;
+    while (o) {
+        if (o == obj) {
+            asyncUnlock();
+            return true;
+        }
+        o = o->mNext;
+    }
+    asyncUnlock();
+    return false;
 }
 
diff --git a/libs/rs/rsObjectBase.h b/libs/rs/rsObjectBase.h
index bb03b87..5cc275a 100644
--- a/libs/rs/rsObjectBase.h
+++ b/libs/rs/rsObjectBase.h
@@ -19,18 +19,21 @@
 
 #include "rsUtils.h"
 
+#define RS_OBJECT_DEBUG 0
+
+#include <utils/CallStack.h>
 
 namespace android {
 namespace renderscript {
 
 class Context;
+class OStream;
 
 // An element is a group of Components that occupies one cell in a structure.
 class ObjectBase
 {
 public:
     ObjectBase(Context *rsc);
-    virtual ~ObjectBase();
 
     void incSysRef() const;
     bool decSysRef() const;
@@ -39,37 +42,55 @@
     bool decUserRef() const;
     bool zeroUserRef() const;
 
+    static bool checkDelete(const ObjectBase *);
+
     const char * getName() const {
-        return mName;
+        return mName.string();
     }
     void setName(const char *);
     void setName(const char *, uint32_t len);
 
     Context * getContext() const {return mRSC;}
-    void setContext(Context *);
 
     static void zeroAllUserRef(Context *rsc);
     static void dumpAll(Context *rsc);
 
     virtual void dumpLOGV(const char *prefix) const;
+    virtual void serialize(OStream *stream) const = 0;
+    virtual RsA3DClassID getClassId() const = 0;
+
+    static bool isValid(const Context *rsc, const ObjectBase *obj);
+
+    // The async lock is taken during object creation in non-rs threads
+    // and object deletion in the rs thread.
+    static void asyncLock();
+    static void asyncUnlock();
 
 protected:
-    const char *mAllocFile;
-    uint32_t mAllocLine;
+    // Called inside the async lock for any object list management that is
+    // necessary in derived classes.
+    virtual void preDestroy() const;
+
     Context *mRSC;
+    virtual ~ObjectBase();
 
 private:
+    static pthread_mutex_t gObjectInitMutex;
+
     void add() const;
     void remove() const;
 
-    bool checkDelete() const;
-
-    char * mName;
+    String8 mName;
     mutable int32_t mSysRefCount;
     mutable int32_t mUserRefCount;
 
     mutable const ObjectBase * mPrev;
     mutable const ObjectBase * mNext;
+
+#if RS_OBJECT_DEBUG
+    CallStack mStack;
+#endif
+
 };
 
 template<class T>
@@ -94,6 +115,10 @@
         }
     }
 
+    ObjectBaseRef & operator= (const ObjectBaseRef &ref) {
+        return ObjectBaseRef(ref);
+    }
+
     ~ObjectBaseRef() {
         clear();
     }
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
index 70e2868..72d1b02 100644
--- a/libs/rs/rsProgram.cpp
+++ b/libs/rs/rsProgram.cpp
@@ -14,25 +14,31 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
-#include "rsProgram.h"
-
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
+#else
+#include "rsContextHostStub.h"
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#endif //ANDROID_RS_BUILD_FOR_HOST
+
+#include "rsProgram.h"
 
 using namespace android;
 using namespace android::renderscript;
 
-
 Program::Program(Context *rsc) : ObjectBase(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     mDirty = true;
     mShaderID = 0;
     mAttribCount = 0;
     mUniformCount = 0;
+    mTextureCount = 0;
 
+    mTextures = NULL;
+    mSamplers = NULL;
     mInputElements = NULL;
     mOutputElements = NULL;
     mConstantTypes = NULL;
@@ -40,14 +46,13 @@
     mOutputCount = 0;
     mConstantCount = 0;
     mIsValid = false;
+    mIsInternal = false;
 }
 
 Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength,
                  const uint32_t * params, uint32_t paramLength) :
     ObjectBase(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     mDirty = true;
     mShaderID = 0;
     mAttribCount = 0;
@@ -73,6 +78,8 @@
         }
     }
 
+    mTextures = new ObjectBaseRef<Allocation>[mTextureCount];
+    mSamplers = new ObjectBaseRef<Sampler>[mTextureCount];
     mInputElements = new ObjectBaseRef<Element>[mInputCount];
     mOutputElements = new ObjectBaseRef<Element>[mOutputCount];
     mConstantTypes = new ObjectBaseRef<Type>[mConstantCount];
@@ -91,15 +98,37 @@
             mConstantTypes[constant++].set(reinterpret_cast<Type *>(params[ct+1]));
         }
     }
+    mIsInternal = false;
+    uint32_t internalTokenLen = strlen(RS_SHADER_INTERNAL);
+    if(shaderLength > internalTokenLen &&
+       strncmp(RS_SHADER_INTERNAL, shaderText, internalTokenLen) == 0) {
+        mIsInternal = true;
+        shaderText += internalTokenLen;
+        shaderLength -= internalTokenLen;
+    }
     mUserShader.setTo(shaderText, shaderLength);
 }
 
 Program::~Program()
 {
-    for (uint32_t ct=0; ct < MAX_UNIFORMS; ct++) {
-        bindAllocation(NULL, ct);
+    if(mRSC->props.mLogShaders) {
+        LOGV("Program::~Program with shader id %u", mShaderID);
     }
 
+    if(mShaderID) {
+        glDeleteShader(mShaderID);
+    }
+
+    for (uint32_t ct=0; ct < MAX_UNIFORMS; ct++) {
+        bindAllocation(NULL, NULL, ct);
+    }
+
+    for (uint32_t ct=0; ct < mTextureCount; ct++) {
+        bindTexture(NULL, ct, NULL);
+        bindSampler(NULL, ct, NULL);
+    }
+    delete[] mTextures;
+    delete[] mSamplers;
     delete[] mInputElements;
     delete[] mOutputElements;
     delete[] mConstantTypes;
@@ -109,8 +138,22 @@
 }
 
 
-void Program::bindAllocation(Allocation *alloc, uint32_t slot)
+void Program::bindAllocation(Context *rsc, Allocation *alloc, uint32_t slot)
 {
+    if (alloc != NULL) {
+        if (slot >= mConstantCount) {
+            LOGE("Attempt to bind alloc at slot %u, on shader id %u, but const count is %u",
+                 slot, (uint32_t)this, mConstantCount);
+            rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind allocation");
+            return;
+        }
+        if (!alloc->getType()->isEqual(mConstantTypes[slot].get())) {
+            LOGE("Attempt to bind alloc at slot %u, on shader id %u, but types mismatch",
+                 slot, (uint32_t)this);
+            rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind allocation");
+            return;
+        }
+    }
     if (mConstants[slot].get() == alloc) {
         return;
     }
@@ -124,10 +167,11 @@
     mDirty = true;
 }
 
-void Program::bindTexture(uint32_t slot, Allocation *a)
+void Program::bindTexture(Context *rsc, uint32_t slot, Allocation *a)
 {
-    if (slot >= MAX_TEXTURE) {
-        LOGE("Attempt to bind a texture to a slot > MAX_TEXTURE");
+    if (slot >= mTextureCount) {
+        LOGE("Attempt to bind texture to slot %u but tex count is %u", slot, mTextureCount);
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind texture");
         return;
     }
 
@@ -136,10 +180,11 @@
     mDirty = true;
 }
 
-void Program::bindSampler(uint32_t slot, Sampler *s)
+void Program::bindSampler(Context *rsc, uint32_t slot, Sampler *s)
 {
-    if (slot >= MAX_TEXTURE) {
-        LOGE("Attempt to bind a Sampler to a slot > MAX_TEXTURE");
+    if (slot >= mTextureCount) {
+        LOGE("Attempt to bind sampler to slot %u but tex count is %u", slot, mTextureCount);
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind sampler");
         return;
     }
 
@@ -235,7 +280,159 @@
     mUserShader.setTo(txt, len);
 }
 
+void Program::appendUserConstants() {
+    for (uint32_t ct=0; ct < mConstantCount; ct++) {
+        const Element *e = mConstantTypes[ct]->getElement();
+        for (uint32_t field=0; field < e->getFieldCount(); field++) {
+            const Element *f = e->getField(field);
+            const char *fn = e->getFieldName(field);
 
+            if (fn[0] == '#') {
+                continue;
+            }
+
+            // Cannot be complex
+            rsAssert(!f->getFieldCount());
+            if(f->getType() == RS_TYPE_MATRIX_4X4) {
+                mShader.append("uniform mat4 UNI_");
+            }
+            else if(f->getType() == RS_TYPE_MATRIX_3X3) {
+                mShader.append("uniform mat3 UNI_");
+            }
+            else if(f->getType() == RS_TYPE_MATRIX_2X2) {
+                mShader.append("uniform mat2 UNI_");
+            }
+            else {
+                switch(f->getComponent().getVectorSize()) {
+                case 1: mShader.append("uniform float UNI_"); break;
+                case 2: mShader.append("uniform vec2 UNI_"); break;
+                case 3: mShader.append("uniform vec3 UNI_"); break;
+                case 4: mShader.append("uniform vec4 UNI_"); break;
+                default:
+                    rsAssert(0);
+                }
+            }
+
+            mShader.append(fn);
+            mShader.append(";\n");
+        }
+    }
+}
+
+void Program::setupUserConstants(Context *rsc, ShaderCache *sc, bool isFragment) {
+    uint32_t uidx = 0;
+    for (uint32_t ct=0; ct < mConstantCount; ct++) {
+        Allocation *alloc = mConstants[ct].get();
+        if (!alloc) {
+            LOGE("Attempting to set constants on shader id %u, but alloc at slot %u is not set", (uint32_t)this, ct);
+            rsc->setError(RS_ERROR_BAD_SHADER, "No constant allocation bound");
+            continue;
+        }
+
+        const uint8_t *data = static_cast<const uint8_t *>(alloc->getPtr());
+        const Element *e = mConstantTypes[ct]->getElement();
+        for (uint32_t field=0; field < e->getFieldCount(); field++) {
+            const Element *f = e->getField(field);
+            const char *fieldName = e->getFieldName(field);
+            // If this field is padding, skip it
+            if(fieldName[0] == '#') {
+                continue;
+            }
+
+            uint32_t offset = e->getFieldOffsetBytes(field);
+            const float *fd = reinterpret_cast<const float *>(&data[offset]);
+
+            int32_t slot = -1;
+            if(!isFragment) {
+                slot = sc->vtxUniformSlot(uidx);
+            }
+            else {
+                slot = sc->fragUniformSlot(uidx);
+            }
+
+            if(rsc->props.mLogShadersUniforms) {
+                LOGV("Uniform  slot=%i, offset=%i, constant=%i, field=%i, uidx=%i, name=%s", slot, offset, ct, field, uidx, fieldName);
+            }
+            if (slot >= 0) {
+                if(f->getType() == RS_TYPE_MATRIX_4X4) {
+                    if(rsc->props.mLogShadersUniforms) {
+                        LOGV("Matrix4x4");
+                        LOGV("{%f, %f, %f, %f",  fd[0], fd[4], fd[8], fd[12]);
+                        LOGV(" %f, %f, %f, %f",  fd[1], fd[5], fd[9], fd[13]);
+                        LOGV(" %f, %f, %f, %f",  fd[2], fd[6], fd[10], fd[14]);
+                        LOGV(" %f, %f, %f, %f}", fd[3], fd[7], fd[11], fd[15]);
+                    }
+                    glUniformMatrix4fv(slot, 1, GL_FALSE, fd);
+                }
+                else if(f->getType() == RS_TYPE_MATRIX_3X3) {
+                    if(rsc->props.mLogShadersUniforms) {
+                        LOGV("Matrix3x3");
+                        LOGV("{%f, %f, %f",  fd[0], fd[3], fd[6]);
+                        LOGV(" %f, %f, %f",  fd[1], fd[4], fd[7]);
+                        LOGV(" %f, %f, %f}", fd[2], fd[5], fd[8]);
+                    }
+                    glUniformMatrix3fv(slot, 1, GL_FALSE, fd);
+                }
+                else if(f->getType() == RS_TYPE_MATRIX_2X2) {
+                    if(rsc->props.mLogShadersUniforms){
+                        LOGV("Matrix2x2");
+                        LOGV("{%f, %f",  fd[0], fd[2]);
+                        LOGV(" %f, %f}", fd[1], fd[3]);
+                    }
+                    glUniformMatrix2fv(slot, 1, GL_FALSE, fd);
+                }
+                else {
+                    switch(f->getComponent().getVectorSize()) {
+                    case 1:
+                        if(rsc->props.mLogShadersUniforms) {
+                            LOGV("Uniform 1 = %f", fd[0]);
+                        }
+                        glUniform1fv(slot, 1, fd);
+                        break;
+                    case 2:
+                        if(rsc->props.mLogShadersUniforms) {
+                            LOGV("Uniform 2 = %f %f", fd[0], fd[1]);
+                        }
+                        glUniform2fv(slot, 1, fd);
+                        break;
+                    case 3:
+                        if(rsc->props.mLogShadersUniforms) {
+                            LOGV("Uniform 3 = %f %f %f", fd[0], fd[1], fd[2]);
+                        }
+                        glUniform3fv(slot, 1, fd);
+                        break;
+                    case 4:
+                        if(rsc->props.mLogShadersUniforms) {
+                            LOGV("Uniform 4 = %f %f %f %f", fd[0], fd[1], fd[2], fd[3]);
+                        }
+                        glUniform4fv(slot, 1, fd);
+                        break;
+                    default:
+                        rsAssert(0);
+                    }
+                }
+            }
+            uidx ++;
+        }
+    }
+}
+
+void Program::initAddUserElement(const Element *e, String8 *names, uint32_t *count, const char *prefix)
+{
+    rsAssert(e->getFieldCount());
+    for (uint32_t ct=0; ct < e->getFieldCount(); ct++) {
+        const Element *ce = e->getField(ct);
+        if (ce->getFieldCount()) {
+            initAddUserElement(ce, names, count, prefix);
+        }
+        else if(e->getFieldName(ct)[0] != '#') {
+            String8 tmp(prefix);
+            tmp.append(e->getFieldName(ct));
+            names[*count].setTo(tmp.string());
+            (*count)++;
+        }
+    }
+}
 
 namespace android {
 namespace renderscript {
@@ -244,19 +441,19 @@
 void rsi_ProgramBindConstants(Context *rsc, RsProgram vp, uint32_t slot, RsAllocation constants)
 {
     Program *p = static_cast<Program *>(vp);
-    p->bindAllocation(static_cast<Allocation *>(constants), slot);
+    p->bindAllocation(rsc, static_cast<Allocation *>(constants), slot);
 }
 
 void rsi_ProgramBindTexture(Context *rsc, RsProgram vpf, uint32_t slot, RsAllocation a)
 {
     Program *p = static_cast<Program *>(vpf);
-    p->bindTexture(slot, static_cast<Allocation *>(a));
+    p->bindTexture(rsc, slot, static_cast<Allocation *>(a));
 }
 
 void rsi_ProgramBindSampler(Context *rsc, RsProgram vpf, uint32_t slot, RsSampler s)
 {
     Program *p = static_cast<Program *>(vpf);
-    p->bindSampler(slot, static_cast<Sampler *>(s));
+    p->bindSampler(rsc, slot, static_cast<Sampler *>(s));
 }
 
 }
diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h
index 86f85fb..c93033b 100644
--- a/libs/rs/rsProgram.h
+++ b/libs/rs/rsProgram.h
@@ -23,29 +23,30 @@
 // ---------------------------------------------------------------------------
 namespace android {
 namespace renderscript {
-
-
 class ShaderCache;
 
+#define RS_SHADER_INTERNAL "//rs_shader_internal\n"
+#define RS_SHADER_ATTR "ATTRIB_"
+#define RS_SHADER_UNI "UNI_"
+
 class Program : public ObjectBase
 {
 public:
     const static uint32_t MAX_ATTRIBS = 8;
     const static uint32_t MAX_UNIFORMS = 16;
-    const static uint32_t MAX_TEXTURE = 2;
 
     Program(Context *);
     Program(Context *, const char * shaderText, uint32_t shaderLength,
                        const uint32_t * params, uint32_t paramLength);
     virtual ~Program();
 
-    void bindAllocation(Allocation *, uint32_t slot);
+    void bindAllocation(Context *, Allocation *, uint32_t slot);
     virtual void createShader();
 
-    bool isUserProgram() const {return mUserShader.size() > 0;}
+    bool isUserProgram() const {return !mIsInternal;}
 
-    void bindTexture(uint32_t slot, Allocation *);
-    void bindSampler(uint32_t slot, Sampler *);
+    void bindTexture(Context *, uint32_t slot, Allocation *);
+    void bindSampler(Context *, uint32_t slot, Sampler *);
 
     uint32_t getShaderID() const {return mShaderID;}
     void setShader(const char *, uint32_t len);
@@ -71,6 +72,12 @@
     uint32_t mOutputCount;
     uint32_t mConstantCount;
     bool mIsValid;
+    bool mIsInternal;
+
+    // Applies to vertex and fragment shaders only
+    void appendUserConstants();
+    void setupUserConstants(Context *rsc, ShaderCache *sc, bool isFragment);
+    void initAddUserElement(const Element *e, String8 *names, uint32_t *count, const char *prefix);
 
     ObjectBaseRef<Allocation> mConstants[MAX_UNIFORMS];
 
@@ -91,8 +98,8 @@
     // and filtered.
     //
     // Constants are strictly accessed by programetic loads.
-    ObjectBaseRef<Allocation> mTextures[MAX_TEXTURE];
-    ObjectBaseRef<Sampler> mSamplers[MAX_TEXTURE];
+    ObjectBaseRef<Allocation> *mTextures;
+    ObjectBaseRef<Sampler> *mSamplers;
 
     bool loadShader(Context *, uint32_t type);
 
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index c17b94c..800854b 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -14,144 +14,90 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
-#include "rsProgramFragment.h"
-
 #include <GLES/gl.h>
 #include <GLES/glext.h>
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
+#else
+#include "rsContextHostStub.h"
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#endif //ANDROID_RS_BUILD_FOR_HOST
+
+#include "rsProgramFragment.h"
 
 using namespace android;
 using namespace android::renderscript;
 
-
-ProgramFragment::ProgramFragment(Context *rsc, const uint32_t * params,
-                                 uint32_t paramLength) :
-    Program(rsc)
-{
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
-    rsAssert(paramLength = 5);
-
-    mEnvModes[0] = (RsTexEnvMode)params[0];
-    mTextureFormats[0] = params[1];
-    mEnvModes[1] = (RsTexEnvMode)params[2];
-    mTextureFormats[1] = params[3];
-    mPointSpriteEnable = params[4] != 0;
-
-    mTextureEnableMask = 0;
-    if (mEnvModes[0]) {
-        mTextureEnableMask |= 1;
-    }
-    if (mEnvModes[1]) {
-        mTextureEnableMask |= 2;
-    }
-    init(rsc);
-}
-
 ProgramFragment::ProgramFragment(Context *rsc, const char * shaderText,
                                  uint32_t shaderLength, const uint32_t * params,
                                  uint32_t paramLength) :
     Program(rsc, shaderText, shaderLength, params, paramLength)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
+    mConstantColor[0] = 1.f;
+    mConstantColor[1] = 1.f;
+    mConstantColor[2] = 1.f;
+    mConstantColor[3] = 1.f;
 
     init(rsc);
-    mTextureEnableMask = (1 << mTextureCount) -1;
 }
 
-
 ProgramFragment::~ProgramFragment()
 {
+    if(mShaderID) {
+        mRSC->mShaderCache.cleanupFragment(mShaderID);
+    }
 }
 
-void ProgramFragment::setupGL(const Context *rsc, ProgramFragmentState *state)
+void ProgramFragment::setConstantColor(Context *rsc, float r, float g, float b, float a)
 {
+    if(isUserProgram()) {
+        LOGE("Attempting to set fixed function emulation color on user program");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot  set fixed function emulation color on user program");
+        return;
+    }
+    if(mConstants[0].get() == NULL) {
+        LOGE("Unable to set fixed function emulation color because allocation is missing");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Unable to set fixed function emulation color because allocation is missing");
+        return;
+    }
+    mConstantColor[0] = r;
+    mConstantColor[1] = g;
+    mConstantColor[2] = b;
+    mConstantColor[3] = a;
+    memcpy(mConstants[0]->getPtr(), mConstantColor, 4*sizeof(float));
+    mDirty = true;
+}
+
+void ProgramFragment::setupGL2(Context *rsc, ProgramFragmentState *state, ShaderCache *sc)
+{
+    //LOGE("sgl2 frag1 %x", glGetError());
     if ((state->mLast.get() == this) && !mDirty) {
         return;
     }
     state->mLast.set(this);
 
-    for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) {
-        glActiveTexture(GL_TEXTURE0 + ct);
-        if (!(mTextureEnableMask & (1 << ct)) || !mTextures[ct].get()) {
-            glDisable(GL_TEXTURE_2D);
-            continue;
-        }
-
-        glEnable(GL_TEXTURE_2D);
-        if (rsc->checkVersion1_1()) {
-            if (mPointSpriteEnable) {
-                glEnable(GL_POINT_SPRITE_OES);
-            } else {
-                glDisable(GL_POINT_SPRITE_OES);
-            }
-            glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, mPointSpriteEnable);
-        }
-        mTextures[ct]->uploadCheck(rsc);
-        glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID());
-
-        switch(mEnvModes[ct]) {
-        case RS_TEX_ENV_MODE_NONE:
-            rsAssert(0);
-            break;
-        case RS_TEX_ENV_MODE_REPLACE:
-            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-            break;
-        case RS_TEX_ENV_MODE_MODULATE:
-            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-            break;
-        case RS_TEX_ENV_MODE_DECAL:
-            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
-            break;
-        }
-
-        if (mSamplers[ct].get()) {
-            mSamplers[ct]->setupGL(rsc, mTextures[ct]->getType()->getIsNp2());
-        } else {
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-        }
-
-        // Gross hack.
-        if (ct == 2) {
-            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
-
-            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
-            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
-            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
-            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
-            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
-
-            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD);
-            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
-            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
-            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
-            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
-        }
-    }
-    glActiveTexture(GL_TEXTURE0);
-    mDirty = false;
-    rsc->checkError("ProgramFragment::setupGL");
-}
-
-void ProgramFragment::setupGL2(const Context *rsc, ProgramFragmentState *state, ShaderCache *sc)
-{
-
-    //LOGE("sgl2 frag1 %x", glGetError());
-    if ((state->mLast.get() == this) && !mDirty) {
-        //return;
-    }
-    state->mLast.set(this);
-
     rsc->checkError("ProgramFragment::setupGL2 start");
-    for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) {
+
+    rsc->checkError("ProgramFragment::setupGL2 begin uniforms");
+    setupUserConstants(rsc, sc, true);
+
+    uint32_t numTexturesToBind = mTextureCount;
+    uint32_t numTexturesAvailable = rsc->getMaxFragmentTextures();
+    if(numTexturesToBind >= numTexturesAvailable) {
+        LOGE("Attempting to bind %u textures on shader id %u, but only %u are available",
+             mTextureCount, (uint32_t)this, numTexturesAvailable);
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind more textuers than available");
+        numTexturesToBind = numTexturesAvailable;
+    }
+
+    for (uint32_t ct=0; ct < numTexturesToBind; ct++) {
         glActiveTexture(GL_TEXTURE0 + ct);
-        if (!(mTextureEnableMask & (1 << ct)) || !mTextures[ct].get()) {
+        if (!mTextures[ct].get()) {
+            LOGE("No texture bound for shader id %u, texture unit %u", (uint)this, ct);
+            rsc->setError(RS_ERROR_BAD_SHADER, "No texture bound");
             continue;
         }
 
@@ -159,16 +105,16 @@
         glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID());
         rsc->checkError("ProgramFragment::setupGL2 tex bind");
         if (mSamplers[ct].get()) {
-            mSamplers[ct]->setupGL(rsc, mTextures[ct]->getType()->getIsNp2());
+            mSamplers[ct]->setupGL(rsc, mTextures[ct].get());
         } else {
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
             rsc->checkError("ProgramFragment::setupGL2 tex env");
         }
 
-        glUniform1i(sc->fragUniformSlot(ct), ct);
+        glUniform1i(sc->fragUniformSlot(mTextureUniformIndexStart + ct), ct);
         rsc->checkError("ProgramFragment::setupGL2 uniforms");
     }
 
@@ -183,112 +129,49 @@
 
 void ProgramFragment::createShader()
 {
-    mShader.setTo("precision mediump float;\n");
-    mShader.append("varying vec4 varColor;\n");
-    mShader.append("varying vec4 varTex0;\n");
-
     if (mUserShader.length() > 1) {
+        mShader.append("precision mediump float;\n");
+        appendUserConstants();
+        char buf[256];
         for (uint32_t ct=0; ct < mTextureCount; ct++) {
-            char buf[256];
-            sprintf(buf, "uniform sampler2D uni_Tex%i;\n", ct);
+            sprintf(buf, "uniform sampler2D UNI_Tex%i;\n", ct);
             mShader.append(buf);
         }
-
         mShader.append(mUserShader);
     } else {
-        uint32_t mask = mTextureEnableMask;
-        uint32_t texNum = 0;
-        while (mask) {
-            if (mask & 1) {
-                char buf[64];
-                mShader.append("uniform sampler2D uni_Tex");
-                sprintf(buf, "%i", texNum);
-                mShader.append(buf);
-                mShader.append(";\n");
-            }
-            mask >>= 1;
-            texNum++;
-        }
-
-
-        mShader.append("void main() {\n");
-        mShader.append("  vec4 col = varColor;\n");
-
-        if (mTextureEnableMask) {
-            if (mPointSpriteEnable) {
-                mShader.append("  vec2 t0 = gl_PointCoord;\n");
-            } else {
-                mShader.append("  vec2 t0 = varTex0.xy;\n");
-            }
-        }
-
-        mask = mTextureEnableMask;
-        texNum = 0;
-        while (mask) {
-            if (mask & 1) {
-                switch(mEnvModes[texNum]) {
-                case RS_TEX_ENV_MODE_NONE:
-                    rsAssert(0);
-                    break;
-                case RS_TEX_ENV_MODE_REPLACE:
-                    switch(mTextureFormats[texNum]) {
-                    case 1:
-                        mShader.append("  col.a = texture2D(uni_Tex0, t0).a;\n");
-                        break;
-                    case 2:
-                        mShader.append("  col.rgba = texture2D(uni_Tex0, t0).rgba;\n");
-                        break;
-                    case 3:
-                        mShader.append("  col.rgb = texture2D(uni_Tex0, t0).rgb;\n");
-                        break;
-                    case 4:
-                        mShader.append("  col.rgba = texture2D(uni_Tex0, t0).rgba;\n");
-                        break;
-                    }
-                    break;
-                case RS_TEX_ENV_MODE_MODULATE:
-                    switch(mTextureFormats[texNum]) {
-                    case 1:
-                        mShader.append("  col.a *= texture2D(uni_Tex0, t0).a;\n");
-                        break;
-                    case 2:
-                        mShader.append("  col.rgba *= texture2D(uni_Tex0, t0).rgba;\n");
-                        break;
-                    case 3:
-                        mShader.append("  col.rgb *= texture2D(uni_Tex0, t0).rgb;\n");
-                        break;
-                    case 4:
-                        mShader.append("  col.rgba *= texture2D(uni_Tex0, t0).rgba;\n");
-                        break;
-                    }
-                    break;
-                case RS_TEX_ENV_MODE_DECAL:
-                    mShader.append("  col = texture2D(uni_Tex0, t0);\n");
-                    break;
-                }
-
-            }
-            mask >>= 1;
-            texNum++;
-        }
-
-        //mShader.append("  col.a = 1.0;\n");
-        //mShader.append("  col.r = 0.5;\n");
-
-        mShader.append("  gl_FragColor = col;\n");
-        mShader.append("}\n");
+        LOGE("ProgramFragment::createShader cannot create program, shader code not defined");
+        rsAssert(0);
     }
 }
 
 void ProgramFragment::init(Context *rsc)
 {
-    mUniformCount = 2;
-    mUniformNames[0].setTo("uni_Tex0");
-    mUniformNames[1].setTo("uni_Tex1");
+    mUniformCount = 0;
+    if (mUserShader.size() > 0) {
+        for (uint32_t ct=0; ct < mConstantCount; ct++) {
+            initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, &mUniformCount, RS_SHADER_UNI);
+        }
+    }
+    mTextureUniformIndexStart = mUniformCount;
+    char buf[256];
+    for (uint32_t ct=0; ct < mTextureCount; ct++) {
+        sprintf(buf, "UNI_Tex%i", ct);
+        mUniformNames[mUniformCount++].setTo(buf);
+    }
 
     createShader();
 }
 
+void ProgramFragment::serialize(OStream *stream) const
+{
+
+}
+
+ProgramFragment *ProgramFragment::createFromStream(Context *rsc, IStream *stream)
+{
+    return NULL;
+}
+
 ProgramFragmentState::ProgramFragmentState()
 {
     mPF = NULL;
@@ -296,20 +179,40 @@
 
 ProgramFragmentState::~ProgramFragmentState()
 {
-    delete mPF;
-
+    ObjectBase::checkDelete(mPF);
+    mPF = NULL;
 }
 
-void ProgramFragmentState::init(Context *rsc, int32_t w, int32_t h)
+void ProgramFragmentState::init(Context *rsc)
 {
-    uint32_t tmp[5] = {
-        RS_TEX_ENV_MODE_NONE, 0,
-        RS_TEX_ENV_MODE_NONE, 0,
-        0
-    };
-    ProgramFragment *pf = new ProgramFragment(rsc, tmp, 5);
+    String8 shaderString(RS_SHADER_INTERNAL);
+    shaderString.append("varying lowp vec4 varColor;\n");
+    shaderString.append("varying vec2 varTex0;\n");
+    shaderString.append("void main() {\n");
+    shaderString.append("  lowp vec4 col = UNI_Color;\n");
+    shaderString.append("  gl_FragColor = col;\n");
+    shaderString.append("}\n");
+
+    const Element *colorElem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
+    rsc->mStateElement.elementBuilderBegin();
+    rsc->mStateElement.elementBuilderAdd(colorElem, "Color", 1);
+    const Element *constInput = rsc->mStateElement.elementBuilderCreate(rsc);
+
+    Type *inputType = Type::getType(rsc, constInput, 1, 0, 0, false, false);
+
+    uint32_t tmp[4];
+    tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
+    tmp[1] = (uint32_t)inputType;
+    tmp[2] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
+    tmp[3] = 0;
+
+    Allocation *constAlloc = new Allocation(rsc, inputType);
+    ProgramFragment *pf = new ProgramFragment(rsc, shaderString.string(),
+                                              shaderString.length(), tmp, 4);
+    pf->bindAllocation(rsc, constAlloc, 0);
+    pf->setConstantColor(rsc, 1.0f, 1.0f, 1.0f, 1.0f);
+
     mDefault.set(pf);
-    pf->init(rsc);
 }
 
 void ProgramFragmentState::deinit(Context *rsc)
@@ -322,21 +225,13 @@
 namespace android {
 namespace renderscript {
 
-RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc,
-                                            const uint32_t * params,
-                                            uint32_t paramLength)
-{
-    ProgramFragment *pf = new ProgramFragment(rsc, params, paramLength);
-    pf->incUserRef();
-    return pf;
-}
-
-RsProgramFragment rsi_ProgramFragmentCreate2(Context *rsc, const char * shaderText,
+RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc, const char * shaderText,
                              uint32_t shaderLength, const uint32_t * params,
                              uint32_t paramLength)
 {
     ProgramFragment *pf = new ProgramFragment(rsc, shaderText, shaderLength, params, paramLength);
     pf->incUserRef();
+    //LOGE("rsi_ProgramFragmentCreate %p", pf);
     return pf;
 }
 
diff --git a/libs/rs/rsProgramFragment.h b/libs/rs/rsProgramFragment.h
index 9fa565d..1cf9ca7 100644
--- a/libs/rs/rsProgramFragment.h
+++ b/libs/rs/rsProgramFragment.h
@@ -28,26 +28,25 @@
 class ProgramFragment : public Program
 {
 public:
-    ProgramFragment(Context *, const uint32_t * params, uint32_t paramLength);
     ProgramFragment(Context *rsc, const char * shaderText,
                              uint32_t shaderLength, const uint32_t * params,
                              uint32_t paramLength);
     virtual ~ProgramFragment();
 
-    virtual void setupGL(const Context *, ProgramFragmentState *);
-    virtual void setupGL2(const Context *, ProgramFragmentState *, ShaderCache *sc);
+    virtual void setupGL2(Context *, ProgramFragmentState *, ShaderCache *sc);
 
     virtual void createShader();
     virtual void loadShader(Context *rsc);
     virtual void init(Context *rsc);
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_FRAGMENT; }
+    static ProgramFragment *createFromStream(Context *rsc, IStream *stream);
+
+    void setConstantColor(Context *, float, float, float, float);
 
 protected:
-    // Hacks to create a program for now
-    uint32_t mTextureFormats[MAX_TEXTURE];
-    uint32_t mTextureDimensions[MAX_TEXTURE];
-    RsTexEnvMode mEnvModes[MAX_TEXTURE];
-    uint32_t mTextureEnableMask;
-    bool mPointSpriteEnable;
+    float mConstantColor[4];
+    int32_t mTextureUniformIndexStart;
 };
 
 class ProgramFragmentState
@@ -57,10 +56,9 @@
     ~ProgramFragmentState();
 
     ProgramFragment *mPF;
-    void init(Context *rsc, int32_t w, int32_t h);
+    void init(Context *rsc);
     void deinit(Context *rsc);
 
-    ObjectBaseRef<Type> mTextureTypes[ProgramFragment::MAX_TEXTURE];
     ObjectBaseRef<ProgramFragment> mDefault;
     Vector<ProgramFragment *> mPrograms;
 
diff --git a/libs/rs/rsProgramFragmentStore.cpp b/libs/rs/rsProgramFragmentStore.cpp
deleted file mode 100644
index 8a2157f..0000000
--- a/libs/rs/rsProgramFragmentStore.cpp
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "rsContext.h"
-#include "rsProgramFragmentStore.h"
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-using namespace android;
-using namespace android::renderscript;
-
-
-ProgramFragmentStore::ProgramFragmentStore(Context *rsc) :
-    Program(rsc)
-{
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
-    mDitherEnable = true;
-    mBlendEnable = false;
-    mColorRWriteEnable = true;
-    mColorGWriteEnable = true;
-    mColorBWriteEnable = true;
-    mColorAWriteEnable = true;
-    mBlendSrc = GL_ONE;
-    mBlendDst = GL_ZERO;
-
-
-    mDepthTestEnable = false;
-    mDepthWriteEnable = true;
-    mDepthFunc = GL_LESS;
-
-
-}
-
-ProgramFragmentStore::~ProgramFragmentStore()
-{
-}
-
-void ProgramFragmentStore::setupGL(const Context *rsc, ProgramFragmentStoreState *state)
-{
-    if (state->mLast.get() == this) {
-        return;
-    }
-    state->mLast.set(this);
-
-    glColorMask(mColorRWriteEnable,
-                mColorGWriteEnable,
-                mColorBWriteEnable,
-                mColorAWriteEnable);
-    if (mBlendEnable) {
-        glEnable(GL_BLEND);
-        glBlendFunc(mBlendSrc, mBlendDst);
-    } else {
-        glDisable(GL_BLEND);
-    }
-
-    //LOGE("pfs  %i, %i, %x", mDepthWriteEnable, mDepthTestEnable, mDepthFunc);
-
-    glDepthMask(mDepthWriteEnable);
-    if(mDepthTestEnable || mDepthWriteEnable) {
-        glEnable(GL_DEPTH_TEST);
-        glDepthFunc(mDepthFunc);
-    } else {
-        glDisable(GL_DEPTH_TEST);
-    }
-
-    if (mDitherEnable) {
-        glEnable(GL_DITHER);
-    } else {
-        glDisable(GL_DITHER);
-    }
-}
-
-void ProgramFragmentStore::setupGL2(const Context *rsc, ProgramFragmentStoreState *state)
-{
-    if (state->mLast.get() == this) {
-        return;
-    }
-    state->mLast.set(this);
-
-    glColorMask(mColorRWriteEnable,
-                mColorGWriteEnable,
-                mColorBWriteEnable,
-                mColorAWriteEnable);
-    if (mBlendEnable) {
-        glEnable(GL_BLEND);
-        glBlendFunc(mBlendSrc, mBlendDst);
-    } else {
-        glDisable(GL_BLEND);
-    }
-
-    //LOGE("pfs  %i, %i, %x", mDepthWriteEnable, mDepthTestEnable, mDepthFunc);
-
-    glDepthMask(mDepthWriteEnable);
-    if(mDepthTestEnable || mDepthWriteEnable) {
-        glEnable(GL_DEPTH_TEST);
-        glDepthFunc(mDepthFunc);
-    } else {
-        glDisable(GL_DEPTH_TEST);
-    }
-
-    if (mDitherEnable) {
-        glEnable(GL_DITHER);
-    } else {
-        glDisable(GL_DITHER);
-    }
-}
-
-
-void ProgramFragmentStore::setDitherEnable(bool enable)
-{
-    mDitherEnable = enable;
-}
-
-void ProgramFragmentStore::setDepthFunc(RsDepthFunc func)
-{
-    mDepthTestEnable = true;
-
-    switch(func) {
-    case RS_DEPTH_FUNC_ALWAYS:
-        mDepthTestEnable = false;
-        mDepthFunc = GL_ALWAYS;
-        break;
-    case RS_DEPTH_FUNC_LESS:
-        mDepthFunc = GL_LESS;
-        break;
-    case RS_DEPTH_FUNC_LEQUAL:
-        mDepthFunc = GL_LEQUAL;
-        break;
-    case RS_DEPTH_FUNC_GREATER:
-        mDepthFunc = GL_GREATER;
-        break;
-    case RS_DEPTH_FUNC_GEQUAL:
-        mDepthFunc = GL_GEQUAL;
-        break;
-    case RS_DEPTH_FUNC_EQUAL:
-        mDepthFunc = GL_EQUAL;
-        break;
-    case RS_DEPTH_FUNC_NOTEQUAL:
-        mDepthFunc = GL_NOTEQUAL;
-        break;
-    }
-}
-
-void ProgramFragmentStore::setDepthMask(bool mask)
-{
-    mDepthWriteEnable = mask;
-}
-
-void ProgramFragmentStore::setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst)
-{
-    mBlendEnable = true;
-    if ((src == RS_BLEND_SRC_ONE) &&
-        (dst == RS_BLEND_DST_ZERO)) {
-        mBlendEnable = false;
-    }
-
-    switch(src) {
-    case RS_BLEND_SRC_ZERO:
-        mBlendSrc = GL_ZERO;
-        break;
-    case RS_BLEND_SRC_ONE:
-        mBlendSrc = GL_ONE;
-        break;
-    case RS_BLEND_SRC_DST_COLOR:
-        mBlendSrc = GL_DST_COLOR;
-        break;
-    case RS_BLEND_SRC_ONE_MINUS_DST_COLOR:
-        mBlendSrc = GL_ONE_MINUS_DST_COLOR;
-        break;
-    case RS_BLEND_SRC_SRC_ALPHA:
-        mBlendSrc = GL_SRC_ALPHA;
-        break;
-    case RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA:
-        mBlendSrc = GL_ONE_MINUS_SRC_ALPHA;
-        break;
-    case RS_BLEND_SRC_DST_ALPHA:
-        mBlendSrc = GL_DST_ALPHA;
-        break;
-    case RS_BLEND_SRC_ONE_MINUS_DST_ALPHA:
-        mBlendSrc = GL_ONE_MINUS_DST_ALPHA;
-        break;
-    case RS_BLEND_SRC_SRC_ALPHA_SATURATE:
-        mBlendSrc = GL_SRC_ALPHA_SATURATE;
-        break;
-    }
-
-    switch(dst) {
-    case RS_BLEND_DST_ZERO:
-        mBlendDst = GL_ZERO;
-        break;
-    case RS_BLEND_DST_ONE:
-        mBlendDst = GL_ONE;
-        break;
-    case RS_BLEND_DST_SRC_COLOR:
-        mBlendDst = GL_SRC_COLOR;
-        break;
-    case RS_BLEND_DST_ONE_MINUS_SRC_COLOR:
-        mBlendDst = GL_ONE_MINUS_SRC_COLOR;
-        break;
-    case RS_BLEND_DST_SRC_ALPHA:
-        mBlendDst = GL_SRC_ALPHA;
-        break;
-    case RS_BLEND_DST_ONE_MINUS_SRC_ALPHA:
-        mBlendDst = GL_ONE_MINUS_SRC_ALPHA;
-        break;
-    case RS_BLEND_DST_DST_ALPHA:
-        mBlendDst = GL_DST_ALPHA;
-        break;
-    case RS_BLEND_DST_ONE_MINUS_DST_ALPHA:
-        mBlendDst = GL_ONE_MINUS_DST_ALPHA;
-        break;
-    }
-}
-
-void ProgramFragmentStore::setColorMask(bool r, bool g, bool b, bool a)
-{
-    mColorRWriteEnable = r;
-    mColorGWriteEnable = g;
-    mColorBWriteEnable = b;
-    mColorAWriteEnable = a;
-}
-
-
-ProgramFragmentStoreState::ProgramFragmentStoreState()
-{
-    mPFS = NULL;
-}
-
-ProgramFragmentStoreState::~ProgramFragmentStoreState()
-{
-    delete mPFS;
-
-}
-
-void ProgramFragmentStoreState::init(Context *rsc, int32_t w, int32_t h)
-{
-    ProgramFragmentStore *pfs = new ProgramFragmentStore(rsc);
-    mDefault.set(pfs);
-}
-
-void ProgramFragmentStoreState::deinit(Context *rsc)
-{
-    mDefault.clear();
-    mLast.clear();
-}
-
-
-namespace android {
-namespace renderscript {
-
-void rsi_ProgramFragmentStoreBegin(Context * rsc, RsElement in, RsElement out)
-{
-    delete rsc->mStateFragmentStore.mPFS;
-    rsc->mStateFragmentStore.mPFS = new ProgramFragmentStore(rsc);
-
-}
-
-void rsi_ProgramFragmentStoreDepthFunc(Context *rsc, RsDepthFunc func)
-{
-    rsc->mStateFragmentStore.mPFS->setDepthFunc(func);
-}
-
-void rsi_ProgramFragmentStoreDepthMask(Context *rsc, bool mask)
-{
-    rsc->mStateFragmentStore.mPFS->setDepthMask(mask);
-}
-
-void rsi_ProgramFragmentStoreColorMask(Context *rsc, bool r, bool g, bool b, bool a)
-{
-    rsc->mStateFragmentStore.mPFS->setColorMask(r, g, b, a);
-}
-
-void rsi_ProgramFragmentStoreBlendFunc(Context *rsc, RsBlendSrcFunc src, RsBlendDstFunc dst)
-{
-    rsc->mStateFragmentStore.mPFS->setBlendFunc(src, dst);
-}
-
-RsProgramFragmentStore rsi_ProgramFragmentStoreCreate(Context *rsc)
-{
-    ProgramFragmentStore *pfs = rsc->mStateFragmentStore.mPFS;
-    pfs->incUserRef();
-    rsc->mStateFragmentStore.mPFS = 0;
-    return pfs;
-}
-
-void rsi_ProgramFragmentStoreDither(Context *rsc, bool enable)
-{
-    rsc->mStateFragmentStore.mPFS->setDitherEnable(enable);
-}
-
-
-}
-}
diff --git a/libs/rs/rsProgramFragmentStore.h b/libs/rs/rsProgramFragmentStore.h
deleted file mode 100644
index 3412c99..0000000
--- a/libs/rs/rsProgramFragmentStore.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_RS_PROGRAM_FRAGMENT_STORE_H
-#define ANDROID_RS_PROGRAM_FRAGMENT_STORE_H
-
-#include "rsProgram.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-namespace renderscript {
-
-class ProgramFragmentStoreState;
-
-class ProgramFragmentStore : public Program
-{
-public:
-    ProgramFragmentStore(Context *);
-    virtual ~ProgramFragmentStore();
-
-    virtual void setupGL(const Context *, ProgramFragmentStoreState *);
-    virtual void setupGL2(const Context *, ProgramFragmentStoreState *);
-
-    void setDepthFunc(RsDepthFunc);
-    void setDepthMask(bool);
-
-    void setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst);
-    void setColorMask(bool, bool, bool, bool);
-
-    void setDitherEnable(bool);
-
-protected:
-    bool mDitherEnable;
-
-    bool mBlendEnable;
-    bool mColorRWriteEnable;
-    bool mColorGWriteEnable;
-    bool mColorBWriteEnable;
-    bool mColorAWriteEnable;
-    int32_t mBlendSrc;
-    int32_t mBlendDst;
-
-    bool mDepthTestEnable;
-    bool mDepthWriteEnable;
-    int32_t mDepthFunc;
-
-    bool mStencilTestEnable;
-};
-
-class ProgramFragmentStoreState
-{
-public:
-    ProgramFragmentStoreState();
-    ~ProgramFragmentStoreState();
-    void init(Context *rsc, int32_t w, int32_t h);
-    void deinit(Context *rsc);
-
-    ObjectBaseRef<ProgramFragmentStore> mDefault;
-    ObjectBaseRef<ProgramFragmentStore> mLast;
-
-
-    ProgramFragmentStore *mPFS;
-};
-
-
-}
-}
-#endif
-
-
-
diff --git a/libs/rs/rsProgramRaster.cpp b/libs/rs/rsProgramRaster.cpp
index 13887d1..5f8c4ba 100644
--- a/libs/rs/rsProgramRaster.cpp
+++ b/libs/rs/rsProgramRaster.cpp
@@ -14,11 +14,17 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
-#include "rsProgramRaster.h"
-
 #include <GLES/gl.h>
 #include <GLES/glext.h>
+#else
+#include "rsContextHostStub.h"
+#include <OpenGL/gl.h>
+#include <OpenGl/glext.h>
+#endif //ANDROID_RS_BUILD_FOR_HOST
+
+#include "rsProgramRaster.h"
 
 using namespace android;
 using namespace android::renderscript;
@@ -30,14 +36,11 @@
                              bool pointSprite) :
     Program(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     mPointSmooth = pointSmooth;
     mLineSmooth = lineSmooth;
     mPointSprite = pointSprite;
-
-    mPointSize = 1.0f;
     mLineWidth = 1.0f;
+    mCull = RS_CULL_BACK;
 }
 
 ProgramRaster::~ProgramRaster()
@@ -47,52 +50,47 @@
 void ProgramRaster::setLineWidth(float s)
 {
     mLineWidth = s;
+    mDirty = true;
 }
 
-void ProgramRaster::setPointSize(float s)
+void ProgramRaster::setCullMode(RsCullMode mode)
 {
-    mPointSize = s;
-}
-
-void ProgramRaster::setupGL(const Context *rsc, ProgramRasterState *state)
-{
-    if (state->mLast.get() == this) {
-        return;
-    }
-    state->mLast.set(this);
-
-    glPointSize(mPointSize);
-    if (mPointSmooth) {
-        glEnable(GL_POINT_SMOOTH);
-    } else {
-        glDisable(GL_POINT_SMOOTH);
-    }
-
-    glLineWidth(mLineWidth);
-    if (mLineSmooth) {
-        glEnable(GL_LINE_SMOOTH);
-    } else {
-        glDisable(GL_LINE_SMOOTH);
-    }
-
-    if (rsc->checkVersion1_1()) {
-        if (mPointSprite) {
-            glEnable(GL_POINT_SPRITE_OES);
-        } else {
-            glDisable(GL_POINT_SPRITE_OES);
-        }
-    }
+    mCull = mode;
+    mDirty = true;
 }
 
 void ProgramRaster::setupGL2(const Context *rsc, ProgramRasterState *state)
 {
-    if (state->mLast.get() == this) {
+    if (state->mLast.get() == this && !mDirty) {
         return;
     }
     state->mLast.set(this);
+    mDirty = false;
+
+    switch(mCull) {
+        case RS_CULL_BACK:
+            glEnable(GL_CULL_FACE);
+            glCullFace(GL_BACK);
+            break;
+        case RS_CULL_FRONT:
+            glEnable(GL_CULL_FACE);
+            glCullFace(GL_FRONT);
+            break;
+        case RS_CULL_NONE:
+            glDisable(GL_CULL_FACE);
+            break;
+    }
 }
 
+void ProgramRaster::serialize(OStream *stream) const
+{
 
+}
+
+ProgramRaster *ProgramRaster::createFromStream(Context *rsc, IStream *stream)
+{
+    return NULL;
+}
 
 ProgramRasterState::ProgramRasterState()
 {
@@ -102,7 +100,7 @@
 {
 }
 
-void ProgramRasterState::init(Context *rsc, int32_t w, int32_t h)
+void ProgramRasterState::init(Context *rsc)
 {
     ProgramRaster *pr = new ProgramRaster(rsc, false, false, false);
     mDefault.set(pr);
@@ -118,7 +116,7 @@
 namespace android {
 namespace renderscript {
 
-RsProgramRaster rsi_ProgramRasterCreate(Context * rsc, RsElement in, RsElement out,
+RsProgramRaster rsi_ProgramRasterCreate(Context * rsc,
                                       bool pointSmooth,
                                       bool lineSmooth,
                                       bool pointSprite)
@@ -131,18 +129,18 @@
     return pr;
 }
 
-void rsi_ProgramRasterSetPointSize(Context * rsc, RsProgramRaster vpr, float s)
-{
-    ProgramRaster *pr = static_cast<ProgramRaster *>(vpr);
-    pr->setPointSize(s);
-}
-
 void rsi_ProgramRasterSetLineWidth(Context * rsc, RsProgramRaster vpr, float s)
 {
     ProgramRaster *pr = static_cast<ProgramRaster *>(vpr);
     pr->setLineWidth(s);
 }
 
+void rsi_ProgramRasterSetCullMode(Context * rsc, RsProgramRaster vpr, RsCullMode mode)
+{
+    ProgramRaster *pr = static_cast<ProgramRaster *>(vpr);
+    pr->setCullMode(mode);
+}
+
 
 }
 }
diff --git a/libs/rs/rsProgramRaster.h b/libs/rs/rsProgramRaster.h
index c3a9c90..d5ed686 100644
--- a/libs/rs/rsProgramRaster.h
+++ b/libs/rs/rsProgramRaster.h
@@ -34,21 +34,20 @@
                   bool pointSprite);
     virtual ~ProgramRaster();
 
-    virtual void setupGL(const Context *, ProgramRasterState *);
     virtual void setupGL2(const Context *, ProgramRasterState *);
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_RASTER; }
+    static ProgramRaster *createFromStream(Context *rsc, IStream *stream);
 
     void setLineWidth(float w);
-    void setPointSize(float s);
+    void setCullMode(RsCullMode mode);
 
 protected:
     bool mPointSmooth;
     bool mLineSmooth;
     bool mPointSprite;
-
-    float mPointSize;
     float mLineWidth;
-
-
+    RsCullMode mCull;
 };
 
 class ProgramRasterState
@@ -56,7 +55,7 @@
 public:
     ProgramRasterState();
     ~ProgramRasterState();
-    void init(Context *rsc, int32_t w, int32_t h);
+    void init(Context *rsc);
     void deinit(Context *rsc);
 
     ObjectBaseRef<ProgramRaster> mDefault;
diff --git a/libs/rs/rsProgramStore.cpp b/libs/rs/rsProgramStore.cpp
new file mode 100644
index 0000000..876299f
--- /dev/null
+++ b/libs/rs/rsProgramStore.cpp
@@ -0,0 +1,298 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_BUILD_FOR_HOST
+#include "rsContext.h"
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#else
+#include "rsContextHostStub.h"
+#include <OpenGL/gl.h>
+#include <OpenGl/glext.h>
+#endif //ANDROID_RS_BUILD_FOR_HOST
+
+#include "rsProgramStore.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+ProgramStore::ProgramStore(Context *rsc) :
+    Program(rsc)
+{
+    mDitherEnable = true;
+    mBlendEnable = false;
+    mColorRWriteEnable = true;
+    mColorGWriteEnable = true;
+    mColorBWriteEnable = true;
+    mColorAWriteEnable = true;
+    mBlendSrc = GL_ONE;
+    mBlendDst = GL_ZERO;
+
+
+    mDepthTestEnable = false;
+    mDepthWriteEnable = true;
+    mDepthFunc = GL_LESS;
+
+
+}
+
+ProgramStore::~ProgramStore()
+{
+}
+
+void ProgramStore::setupGL2(const Context *rsc, ProgramStoreState *state)
+{
+    if (state->mLast.get() == this) {
+        return;
+    }
+    state->mLast.set(this);
+
+    glColorMask(mColorRWriteEnable,
+                mColorGWriteEnable,
+                mColorBWriteEnable,
+                mColorAWriteEnable);
+    if (mBlendEnable) {
+        glEnable(GL_BLEND);
+        glBlendFunc(mBlendSrc, mBlendDst);
+    } else {
+        glDisable(GL_BLEND);
+    }
+
+    //LOGE("pfs  %i, %i, %x", mDepthWriteEnable, mDepthTestEnable, mDepthFunc);
+
+    if (rsc->mUserSurfaceConfig.depthMin > 0) {
+        glDepthMask(mDepthWriteEnable);
+        if(mDepthTestEnable || mDepthWriteEnable) {
+            glEnable(GL_DEPTH_TEST);
+            glDepthFunc(mDepthFunc);
+        } else {
+            glDisable(GL_DEPTH_TEST);
+        }
+    } else {
+        glDepthMask(false);
+        glDisable(GL_DEPTH_TEST);
+    }
+
+    if (rsc->mUserSurfaceConfig.stencilMin > 0) {
+    } else {
+        glStencilMask(0);
+        glDisable(GL_STENCIL_TEST);
+    }
+
+    if (mDitherEnable) {
+        glEnable(GL_DITHER);
+    } else {
+        glDisable(GL_DITHER);
+    }
+}
+
+
+void ProgramStore::setDitherEnable(bool enable)
+{
+    mDitherEnable = enable;
+}
+
+void ProgramStore::serialize(OStream *stream) const
+{
+
+}
+
+ProgramStore *ProgramStore::createFromStream(Context *rsc, IStream *stream)
+{
+    return NULL;
+}
+
+
+void ProgramStore::setDepthFunc(RsDepthFunc func)
+{
+    mDepthTestEnable = true;
+
+    switch(func) {
+    case RS_DEPTH_FUNC_ALWAYS:
+        mDepthTestEnable = false;
+        mDepthFunc = GL_ALWAYS;
+        break;
+    case RS_DEPTH_FUNC_LESS:
+        mDepthFunc = GL_LESS;
+        break;
+    case RS_DEPTH_FUNC_LEQUAL:
+        mDepthFunc = GL_LEQUAL;
+        break;
+    case RS_DEPTH_FUNC_GREATER:
+        mDepthFunc = GL_GREATER;
+        break;
+    case RS_DEPTH_FUNC_GEQUAL:
+        mDepthFunc = GL_GEQUAL;
+        break;
+    case RS_DEPTH_FUNC_EQUAL:
+        mDepthFunc = GL_EQUAL;
+        break;
+    case RS_DEPTH_FUNC_NOTEQUAL:
+        mDepthFunc = GL_NOTEQUAL;
+        break;
+    }
+}
+
+void ProgramStore::setDepthMask(bool mask)
+{
+    mDepthWriteEnable = mask;
+}
+
+void ProgramStore::setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst)
+{
+    mBlendEnable = true;
+    if ((src == RS_BLEND_SRC_ONE) &&
+        (dst == RS_BLEND_DST_ZERO)) {
+        mBlendEnable = false;
+    }
+
+    switch(src) {
+    case RS_BLEND_SRC_ZERO:
+        mBlendSrc = GL_ZERO;
+        break;
+    case RS_BLEND_SRC_ONE:
+        mBlendSrc = GL_ONE;
+        break;
+    case RS_BLEND_SRC_DST_COLOR:
+        mBlendSrc = GL_DST_COLOR;
+        break;
+    case RS_BLEND_SRC_ONE_MINUS_DST_COLOR:
+        mBlendSrc = GL_ONE_MINUS_DST_COLOR;
+        break;
+    case RS_BLEND_SRC_SRC_ALPHA:
+        mBlendSrc = GL_SRC_ALPHA;
+        break;
+    case RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA:
+        mBlendSrc = GL_ONE_MINUS_SRC_ALPHA;
+        break;
+    case RS_BLEND_SRC_DST_ALPHA:
+        mBlendSrc = GL_DST_ALPHA;
+        break;
+    case RS_BLEND_SRC_ONE_MINUS_DST_ALPHA:
+        mBlendSrc = GL_ONE_MINUS_DST_ALPHA;
+        break;
+    case RS_BLEND_SRC_SRC_ALPHA_SATURATE:
+        mBlendSrc = GL_SRC_ALPHA_SATURATE;
+        break;
+    }
+
+    switch(dst) {
+    case RS_BLEND_DST_ZERO:
+        mBlendDst = GL_ZERO;
+        break;
+    case RS_BLEND_DST_ONE:
+        mBlendDst = GL_ONE;
+        break;
+    case RS_BLEND_DST_SRC_COLOR:
+        mBlendDst = GL_SRC_COLOR;
+        break;
+    case RS_BLEND_DST_ONE_MINUS_SRC_COLOR:
+        mBlendDst = GL_ONE_MINUS_SRC_COLOR;
+        break;
+    case RS_BLEND_DST_SRC_ALPHA:
+        mBlendDst = GL_SRC_ALPHA;
+        break;
+    case RS_BLEND_DST_ONE_MINUS_SRC_ALPHA:
+        mBlendDst = GL_ONE_MINUS_SRC_ALPHA;
+        break;
+    case RS_BLEND_DST_DST_ALPHA:
+        mBlendDst = GL_DST_ALPHA;
+        break;
+    case RS_BLEND_DST_ONE_MINUS_DST_ALPHA:
+        mBlendDst = GL_ONE_MINUS_DST_ALPHA;
+        break;
+    }
+}
+
+void ProgramStore::setColorMask(bool r, bool g, bool b, bool a)
+{
+    mColorRWriteEnable = r;
+    mColorGWriteEnable = g;
+    mColorBWriteEnable = b;
+    mColorAWriteEnable = a;
+}
+
+
+ProgramStoreState::ProgramStoreState()
+{
+    mPFS = NULL;
+}
+
+ProgramStoreState::~ProgramStoreState()
+{
+    ObjectBase::checkDelete(mPFS);
+    mPFS = NULL;
+}
+
+void ProgramStoreState::init(Context *rsc)
+{
+    ProgramStore *pfs = new ProgramStore(rsc);
+    mDefault.set(pfs);
+}
+
+void ProgramStoreState::deinit(Context *rsc)
+{
+    mDefault.clear();
+    mLast.clear();
+}
+
+
+namespace android {
+namespace renderscript {
+
+void rsi_ProgramStoreBegin(Context * rsc, RsElement in, RsElement out)
+{
+    ObjectBase::checkDelete(rsc->mStateFragmentStore.mPFS);
+    rsc->mStateFragmentStore.mPFS = new ProgramStore(rsc);
+}
+
+void rsi_ProgramStoreDepthFunc(Context *rsc, RsDepthFunc func)
+{
+    rsc->mStateFragmentStore.mPFS->setDepthFunc(func);
+}
+
+void rsi_ProgramStoreDepthMask(Context *rsc, bool mask)
+{
+    rsc->mStateFragmentStore.mPFS->setDepthMask(mask);
+}
+
+void rsi_ProgramStoreColorMask(Context *rsc, bool r, bool g, bool b, bool a)
+{
+    rsc->mStateFragmentStore.mPFS->setColorMask(r, g, b, a);
+}
+
+void rsi_ProgramStoreBlendFunc(Context *rsc, RsBlendSrcFunc src, RsBlendDstFunc dst)
+{
+    rsc->mStateFragmentStore.mPFS->setBlendFunc(src, dst);
+}
+
+RsProgramStore rsi_ProgramStoreCreate(Context *rsc)
+{
+    ProgramStore *pfs = rsc->mStateFragmentStore.mPFS;
+    pfs->incUserRef();
+    rsc->mStateFragmentStore.mPFS = 0;
+    return pfs;
+}
+
+void rsi_ProgramStoreDither(Context *rsc, bool enable)
+{
+    rsc->mStateFragmentStore.mPFS->setDitherEnable(enable);
+}
+
+
+}
+}
diff --git a/libs/rs/rsProgramStore.h b/libs/rs/rsProgramStore.h
new file mode 100644
index 0000000..95bcf3c
--- /dev/null
+++ b/libs/rs/rsProgramStore.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_PROGRAM_FRAGMENT_STORE_H
+#define ANDROID_RS_PROGRAM_FRAGMENT_STORE_H
+
+#include "rsProgram.h"
+#include "rsStream.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class ProgramStoreState;
+
+class ProgramStore : public Program
+{
+public:
+    ProgramStore(Context *);
+    virtual ~ProgramStore();
+
+    virtual void setupGL2(const Context *, ProgramStoreState *);
+
+    void setDepthFunc(RsDepthFunc);
+    void setDepthMask(bool);
+
+    void setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst);
+    void setColorMask(bool, bool, bool, bool);
+
+    void setDitherEnable(bool);
+
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_STORE; }
+    static ProgramStore *createFromStream(Context *rsc, IStream *stream);
+
+protected:
+    bool mDitherEnable;
+
+    bool mBlendEnable;
+    bool mColorRWriteEnable;
+    bool mColorGWriteEnable;
+    bool mColorBWriteEnable;
+    bool mColorAWriteEnable;
+    int32_t mBlendSrc;
+    int32_t mBlendDst;
+
+    bool mDepthTestEnable;
+    bool mDepthWriteEnable;
+    int32_t mDepthFunc;
+
+    bool mStencilTestEnable;
+};
+
+class ProgramStoreState
+{
+public:
+    ProgramStoreState();
+    ~ProgramStoreState();
+    void init(Context *rsc);
+    void deinit(Context *rsc);
+
+    ObjectBaseRef<ProgramStore> mDefault;
+    ObjectBaseRef<ProgramStore> mLast;
+
+
+    ProgramStore *mPFS;
+};
+
+
+}
+}
+#endif
+
+
+
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index a2b2df4..4e64008 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -14,98 +14,37 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
-#include "rsProgramVertex.h"
-
 #include <GLES/gl.h>
 #include <GLES/glext.h>
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
+#else
+#include "rsContextHostStub.h"
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#endif //ANDROID_RS_BUILD_FOR_HOST
+
+#include "rsProgramVertex.h"
 
 using namespace android;
 using namespace android::renderscript;
 
 
-ProgramVertex::ProgramVertex(Context *rsc, bool texMat) :
-    Program(rsc)
-{
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
-    mTextureMatrixEnable = texMat;
-    mLightCount = 0;
-    init(rsc);
-}
-
 ProgramVertex::ProgramVertex(Context *rsc, const char * shaderText,
                              uint32_t shaderLength, const uint32_t * params,
                              uint32_t paramLength) :
     Program(rsc, shaderText, shaderLength, params, paramLength)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
-    mTextureMatrixEnable = false;
-    mLightCount = 0;
-
     init(rsc);
 }
 
 ProgramVertex::~ProgramVertex()
 {
-}
-
-static void logMatrix(const char *txt, const float *f)
-{
-    LOGV("Matrix %s, %p", txt, f);
-    LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[0], f[4], f[8], f[12]);
-    LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[1], f[5], f[9], f[13]);
-    LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[2], f[6], f[10], f[14]);
-    LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[3], f[7], f[11], f[15]);
-}
-
-void ProgramVertex::setupGL(const Context *rsc, ProgramVertexState *state)
-{
-    if ((state->mLast.get() == this) && !mDirty) {
-        return;
+    if(mShaderID) {
+        mRSC->mShaderCache.cleanupVertex(mShaderID);
     }
-    state->mLast.set(this);
-
-    const float *f = static_cast<const float *>(mConstants[0]->getPtr());
-
-    glMatrixMode(GL_TEXTURE);
-    if (mTextureMatrixEnable) {
-        glLoadMatrixf(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]);
-    } else {
-        glLoadIdentity();
-    }
-
-    glMatrixMode(GL_MODELVIEW);
-    glLoadIdentity();
-    if (mLightCount) {
-        int v = 0;
-        glEnable(GL_LIGHTING);
-        glLightModelxv(GL_LIGHT_MODEL_TWO_SIDE, &v);
-        for (uint32_t ct = 0; ct < mLightCount; ct++) {
-            const Light *l = mLights[ct].get();
-            glEnable(GL_LIGHT0 + ct);
-            l->setupGL(ct);
-        }
-        for (uint32_t ct = mLightCount; ct < MAX_LIGHTS; ct++) {
-            glDisable(GL_LIGHT0 + ct);
-        }
-    } else {
-        glDisable(GL_LIGHTING);
-    }
-
-    if (!f) {
-        LOGE("Must bind constants to vertex program");
-    }
-
-    glMatrixMode(GL_PROJECTION);
-    glLoadMatrixf(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
-    glMatrixMode(GL_MODELVIEW);
-    glLoadMatrixf(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
-
-    mDirty = false;
 }
 
 void ProgramVertex::loadShader(Context *rsc) {
@@ -114,42 +53,19 @@
 
 void ProgramVertex::createShader()
 {
-    mShader.setTo("");
-
-    mShader.append("varying vec4 varColor;\n");
-    mShader.append("varying vec4 varTex0;\n");
-
     if (mUserShader.length() > 1) {
-        mShader.append("uniform mat4 ");
-        mShader.append(mUniformNames[0]);
-        mShader.append(";\n");
 
-        for (uint32_t ct=0; ct < mConstantCount; ct++) {
-            const Element *e = mConstantTypes[ct]->getElement();
-            for (uint32_t field=0; field < e->getFieldCount(); field++) {
-                const Element *f = e->getField(field);
-
-                // Cannot be complex
-                rsAssert(!f->getFieldCount());
-                switch(f->getComponent().getVectorSize()) {
-                case 1: mShader.append("uniform float UNI_"); break;
-                case 2: mShader.append("uniform vec2 UNI_"); break;
-                case 3: mShader.append("uniform vec3 UNI_"); break;
-                case 4: mShader.append("uniform vec4 UNI_"); break;
-                default:
-                    rsAssert(0);
-                }
-
-                mShader.append(e->getFieldName(field));
-                mShader.append(";\n");
-            }
-        }
-
+        appendUserConstants();
 
         for (uint32_t ct=0; ct < mInputCount; ct++) {
             const Element *e = mInputElements[ct].get();
             for (uint32_t field=0; field < e->getFieldCount(); field++) {
                 const Element *f = e->getField(field);
+                const char *fn = e->getFieldName(field);
+
+                if (fn[0] == '#') {
+                    continue;
+                }
 
                 // Cannot be complex
                 rsAssert(!f->getFieldCount());
@@ -162,149 +78,121 @@
                     rsAssert(0);
                 }
 
-                mShader.append(e->getFieldName(field));
+                mShader.append(fn);
                 mShader.append(";\n");
             }
         }
         mShader.append(mUserShader);
     } else {
-        mShader.append("attribute vec4 ATTRIB_LegacyPosition;\n");
-        mShader.append("attribute vec4 ATTRIB_LegacyColor;\n");
-        mShader.append("attribute vec3 ATTRIB_LegacyNormal;\n");
-        mShader.append("attribute float ATTRIB_LegacyPointSize;\n");
-        mShader.append("attribute vec4 ATTRIB_LegacyTexture;\n");
-
-        for (uint32_t ct=0; ct < mUniformCount; ct++) {
-            mShader.append("uniform mat4 ");
-            mShader.append(mUniformNames[ct]);
-            mShader.append(";\n");
-        }
-
-        mShader.append("void main() {\n");
-        mShader.append("  gl_Position = UNI_MVP * ATTRIB_LegacyPosition;\n");
-        mShader.append("  gl_PointSize = ATTRIB_LegacyPointSize;\n");
-
-        mShader.append("  varColor = ATTRIB_LegacyColor;\n");
-        if (mTextureMatrixEnable) {
-            mShader.append("  varTex0 = UNI_TexMatrix * ATTRIB_LegacyTexture;\n");
-        } else {
-            mShader.append("  varTex0 = ATTRIB_LegacyTexture;\n");
-        }
-        //mShader.append("  pos.x = pos.x / 480.0;\n");
-        //mShader.append("  pos.y = pos.y / 800.0;\n");
-        //mShader.append("  gl_Position = pos;\n");
-        mShader.append("}\n");
+        LOGE("ProgramFragment::createShader cannot create program, shader code not defined");
+        rsAssert(0);
     }
 }
 
-void ProgramVertex::setupGL2(const Context *rsc, ProgramVertexState *state, ShaderCache *sc)
+void ProgramVertex::setupGL2(Context *rsc, ProgramVertexState *state, ShaderCache *sc)
 {
     //LOGE("sgl2 vtx1 %x", glGetError());
     if ((state->mLast.get() == this) && !mDirty) {
-        //return;
+        return;
     }
 
     rsc->checkError("ProgramVertex::setupGL2 start");
-    glVertexAttrib4f(1, state->color[0], state->color[1], state->color[2], state->color[3]);
 
-    const float *f = static_cast<const float *>(mConstants[0]->getPtr());
-
-    Matrix mvp;
-    mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
-    Matrix t;
-    t.load(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
-    mvp.multiply(&t);
-
-    glUniformMatrix4fv(sc->vtxUniformSlot(0), 1, GL_FALSE, mvp.m);
-    if (mTextureMatrixEnable) {
-        glUniformMatrix4fv(sc->vtxUniformSlot(1), 1, GL_FALSE,
-                           &f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]);
+    if(!isUserProgram()) {
+        if(mConstants[0].get() == NULL) {
+            LOGE("Unable to set fixed function emulation matrices because allocation is missing");
+            rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+            return;
+        }
+        float *f = static_cast<float *>(mConstants[0]->getPtr());
+        Matrix mvp;
+        mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
+        Matrix t;
+        t.load(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
+        mvp.multiply(&t);
+        for(uint32_t i = 0; i < 16; i ++) {
+            f[RS_PROGRAM_VERTEX_MVP_OFFSET + i] = mvp.m[i];
+        }
     }
 
     rsc->checkError("ProgramVertex::setupGL2 begin uniforms");
-    uint32_t uidx = 1;
-    for (uint32_t ct=0; ct < mConstantCount; ct++) {
-        Allocation *alloc = mConstants[ct+1].get();
-        if (!alloc) {
-            continue;
-        }
-
-        const uint8_t *data = static_cast<const uint8_t *>(alloc->getPtr());
-        const Element *e = mConstantTypes[ct]->getElement();
-        for (uint32_t field=0; field < e->getFieldCount(); field++) {
-            const Element *f = e->getField(field);
-            uint32_t offset = e->getFieldOffsetBytes(field);
-            int32_t slot = sc->vtxUniformSlot(uidx);
-
-            const float *fd = reinterpret_cast<const float *>(&data[offset]);
-
-            //LOGE("Uniform  slot=%i, offset=%i, constant=%i, field=%i, uidx=%i", slot, offset, ct, field, uidx);
-            if (slot >= 0) {
-                switch(f->getComponent().getVectorSize()) {
-                case 1:
-                    //LOGE("Uniform 1 = %f", fd[0]);
-                    glUniform1fv(slot, 1, fd);
-                    break;
-                case 2:
-                    //LOGE("Uniform 2 = %f %f", fd[0], fd[1]);
-                    glUniform2fv(slot, 1, fd);
-                    break;
-                case 3:
-                    //LOGE("Uniform 3 = %f %f %f", fd[0], fd[1], fd[2]);
-                    glUniform3fv(slot, 1, fd);
-                    break;
-                case 4:
-                    //LOGE("Uniform 4 = %f %f %f %f", fd[0], fd[1], fd[2], fd[3]);
-                    glUniform4fv(slot, 1, fd);
-                    break;
-                default:
-                    rsAssert(0);
-                }
-            }
-            uidx ++;
-        }
-    }
-
-    for (uint32_t ct=0; ct < mConstantCount; ct++) {
-        uint32_t glSlot = sc->vtxUniformSlot(ct + 1);
-
-    }
+    setupUserConstants(rsc, sc, false);
 
     state->mLast.set(this);
     rsc->checkError("ProgramVertex::setupGL2");
 }
 
-void ProgramVertex::addLight(const Light *l)
+void ProgramVertex::setProjectionMatrix(Context *rsc, const rsc_Matrix *m) const
 {
-    if (mLightCount < MAX_LIGHTS) {
-        mLights[mLightCount].set(l);
-        mLightCount++;
+    if(isUserProgram()) {
+        LOGE("Attempting to set fixed function emulation matrix projection on user program");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader");
+        return;
     }
-}
-
-void ProgramVertex::setProjectionMatrix(const rsc_Matrix *m) const
-{
+    if(mConstants[0].get() == NULL) {
+        LOGE("Unable to set fixed function emulation matrix projection because allocation is missing");
+        return;
+    }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
 }
 
-void ProgramVertex::setModelviewMatrix(const rsc_Matrix *m) const
+void ProgramVertex::setModelviewMatrix(Context *rsc, const rsc_Matrix *m) const
 {
+    if(isUserProgram()) {
+        LOGE("Attempting to set fixed function emulation matrix modelview on user program");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader");
+        return;
+    }
+    if(mConstants[0].get() == NULL) {
+        LOGE("Unable to set fixed function emulation matrix modelview because allocation is missing");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+        return;
+    }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
 }
 
-void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const
+void ProgramVertex::setTextureMatrix(Context *rsc, const rsc_Matrix *m) const
 {
+    if(isUserProgram()) {
+        LOGE("Attempting to set fixed function emulation matrix texture on user program");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader");
+        return;
+    }
+    if(mConstants[0].get() == NULL) {
+        LOGE("Unable to set fixed function emulation matrix texture because allocation is missing");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+        return;
+    }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
 }
 
-void ProgramVertex::transformToScreen(const Context *rsc, float *v4out, const float *v3in) const
+void ProgramVertex::getProjectionMatrix(Context *rsc, rsc_Matrix *m) const
 {
+    if(isUserProgram()) {
+        LOGE("Attempting to get fixed function emulation matrix projection on user program");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Cannot get emulation matrix on user shader");
+        return;
+    }
+    if(mConstants[0].get() == NULL) {
+        LOGE("Unable to get fixed function emulation matrix projection because allocation is missing");
+        rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing");
+        return;
+    }
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
+    memcpy(m, &f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], sizeof(rsc_Matrix));
+}
+
+void ProgramVertex::transformToScreen(Context *rsc, float *v4out, const float *v3in) const
+{
+    if(isUserProgram()) {
+        return;
+    }
     float *f = static_cast<float *>(mConstants[0]->getPtr());
     Matrix mvp;
     mvp.loadMultiply((Matrix *)&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET],
@@ -312,45 +200,31 @@
     mvp.vectorMultiply(v4out, v3in);
 }
 
-void ProgramVertex::initAddUserElement(const Element *e, String8 *names, uint32_t *count, const char *prefix)
-{
-    rsAssert(e->getFieldCount());
-    for (uint32_t ct=0; ct < e->getFieldCount(); ct++) {
-        const Element *ce = e->getField(ct);
-        if (ce->getFieldCount()) {
-            initAddUserElement(ce, names, count, prefix);
-        } else {
-            String8 tmp(prefix);
-            tmp.append(e->getFieldName(ct));
-            names[*count].setTo(tmp.string());
-            (*count)++;
-        }
-    }
-}
-
-
 void ProgramVertex::init(Context *rsc)
 {
     mAttribCount = 0;
     if (mUserShader.size() > 0) {
         for (uint32_t ct=0; ct < mInputCount; ct++) {
-            initAddUserElement(mInputElements[ct].get(), mAttribNames, &mAttribCount, "ATTRIB_");
+            initAddUserElement(mInputElements[ct].get(), mAttribNames, &mAttribCount, RS_SHADER_ATTR);
         }
-
-        mUniformCount = 1;
-        mUniformNames[0].setTo("UNI_MVP");
+        mUniformCount = 0;
         for (uint32_t ct=0; ct < mConstantCount; ct++) {
-            initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, &mUniformCount, "UNI_");
+            initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, &mUniformCount, RS_SHADER_UNI);
         }
-    } else {
-        mUniformCount = 2;
-        mUniformNames[0].setTo("UNI_MVP");
-        mUniformNames[1].setTo("UNI_TexMatrix");
     }
-
     createShader();
 }
 
+void ProgramVertex::serialize(OStream *stream) const
+{
+
+}
+
+ProgramVertex *ProgramVertex::createFromStream(Context *rsc, IStream *stream)
+{
+    return NULL;
+}
+
 
 ///////////////////////////////////////////////////////////////////////
 
@@ -362,44 +236,77 @@
 {
 }
 
-void ProgramVertexState::init(Context *rsc, int32_t w, int32_t h)
+void ProgramVertexState::init(Context *rsc)
 {
-    RsElement e = (RsElement) Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1);
+    const Element *matrixElem = Element::create(rsc, RS_TYPE_MATRIX_4X4, RS_KIND_USER, false, 1);
+    const Element *f2Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2);
+    const Element *f3Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
+    const Element *f4Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
 
-    rsi_TypeBegin(rsc, e);
-    rsi_TypeAdd(rsc, RS_DIMENSION_X, 48);
-    mAllocType.set((Type *)rsi_TypeCreate(rsc));
+    rsc->mStateElement.elementBuilderBegin();
+    rsc->mStateElement.elementBuilderAdd(matrixElem, "MV", 1);
+    rsc->mStateElement.elementBuilderAdd(matrixElem, "P", 1);
+    rsc->mStateElement.elementBuilderAdd(matrixElem, "TexMatrix", 1);
+    rsc->mStateElement.elementBuilderAdd(matrixElem, "MVP", 1);
+    const Element *constInput = rsc->mStateElement.elementBuilderCreate(rsc);
 
-    ProgramVertex *pv = new ProgramVertex(rsc, false);
-    Allocation *alloc = (Allocation *)rsi_AllocationCreateTyped(rsc, mAllocType.get());
+    rsc->mStateElement.elementBuilderBegin();
+    rsc->mStateElement.elementBuilderAdd(f4Elem, "position", 1);
+    rsc->mStateElement.elementBuilderAdd(f4Elem, "color", 1);
+    rsc->mStateElement.elementBuilderAdd(f3Elem, "normal", 1);
+    rsc->mStateElement.elementBuilderAdd(f2Elem, "texture0", 1);
+    const Element *attrElem = rsc->mStateElement.elementBuilderCreate(rsc);
+
+    Type *inputType = Type::getType(rsc, constInput, 1, 0, 0, false, false);
+
+    String8 shaderString(RS_SHADER_INTERNAL);
+    shaderString.append("varying vec4 varColor;\n");
+    shaderString.append("varying vec2 varTex0;\n");
+    shaderString.append("void main() {\n");
+    shaderString.append("  gl_Position = UNI_MVP * ATTRIB_position;\n");
+    shaderString.append("  gl_PointSize = 1.0;\n");
+    shaderString.append("  varColor = ATTRIB_color;\n");
+    shaderString.append("  varTex0 = ATTRIB_texture0;\n");
+    shaderString.append("}\n");
+
+    uint32_t tmp[6];
+    tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
+    tmp[1] = (uint32_t)inputType;
+    tmp[2] = RS_PROGRAM_PARAM_INPUT;
+    tmp[3] = (uint32_t)attrElem;
+    tmp[4] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
+    tmp[5] = 0;
+
+    ProgramVertex *pv = new ProgramVertex(rsc, shaderString.string(),
+                                          shaderString.length(), tmp, 6);
+    Allocation *alloc = new Allocation(rsc, inputType);
+    pv->bindAllocation(rsc, alloc, 0);
+
     mDefaultAlloc.set(alloc);
     mDefault.set(pv);
-    pv->init(rsc);
-    pv->bindAllocation(alloc, 0);
 
-    color[0] = 1.f;
-    color[1] = 1.f;
-    color[2] = 1.f;
-    color[3] = 1.f;
+    updateSize(rsc);
 
-    updateSize(rsc, w, h);
 }
 
-void ProgramVertexState::updateSize(Context *rsc, int32_t w, int32_t h)
+void ProgramVertexState::updateSize(Context *rsc)
 {
+    float *f = static_cast<float *>(mDefaultAlloc->getPtr());
+
     Matrix m;
-    m.loadOrtho(0,w, h,0, -1,1);
-    mDefaultAlloc->subData(RS_PROGRAM_VERTEX_PROJECTION_OFFSET, 16, &m.m[0], 16*4);
+    m.loadOrtho(0,rsc->getWidth(), rsc->getHeight(),0, -1,1);
+    memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m.m, sizeof(m));
+    memcpy(&f[RS_PROGRAM_VERTEX_MVP_OFFSET], m.m, sizeof(m));
 
     m.loadIdentity();
-    mDefaultAlloc->subData(RS_PROGRAM_VERTEX_MODELVIEW_OFFSET, 16, &m.m[0], 16*4);
+    memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m.m, sizeof(m));
+    memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m.m, sizeof(m));
 }
 
 void ProgramVertexState::deinit(Context *rsc)
 {
     mDefaultAlloc.clear();
     mDefault.clear();
-    mAllocType.clear();
     mLast.clear();
 }
 
@@ -407,15 +314,7 @@
 namespace android {
 namespace renderscript {
 
-
-RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, bool texMat)
-{
-    ProgramVertex *pv = new ProgramVertex(rsc, texMat);
-    pv->incUserRef();
-    return pv;
-}
-
-RsProgramVertex rsi_ProgramVertexCreate2(Context *rsc, const char * shaderText,
+RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, const char * shaderText,
                              uint32_t shaderLength, const uint32_t * params,
                              uint32_t paramLength)
 {
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index 28554cc..355df2b 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -28,40 +28,26 @@
 class ProgramVertex : public Program
 {
 public:
-    const static uint32_t MAX_LIGHTS = 8;
-
     ProgramVertex(Context *,const char * shaderText, uint32_t shaderLength,
                   const uint32_t * params, uint32_t paramLength);
-    ProgramVertex(Context *, bool texMat);
     virtual ~ProgramVertex();
 
-    virtual void setupGL(const Context *rsc, ProgramVertexState *state);
-    virtual void setupGL2(const Context *rsc, ProgramVertexState *state, ShaderCache *sc);
+    virtual void setupGL2(Context *rsc, ProgramVertexState *state, ShaderCache *sc);
 
+    void setProjectionMatrix(Context *, const rsc_Matrix *) const;
+    void getProjectionMatrix(Context *, rsc_Matrix *) const;
+    void setModelviewMatrix(Context *, const rsc_Matrix *) const;
+    void setTextureMatrix(Context *, const rsc_Matrix *) const;
 
-    void setTextureMatrixEnable(bool e) {mTextureMatrixEnable = e;}
-    void addLight(const Light *);
-
-    void setProjectionMatrix(const rsc_Matrix *) const;
-    void setModelviewMatrix(const rsc_Matrix *) const;
-    void setTextureMatrix(const rsc_Matrix *) const;
-
-    void transformToScreen(const Context *, float *v4out, const float *v3in) const;
+    void transformToScreen(Context *, float *v4out, const float *v3in) const;
 
     virtual void createShader();
     virtual void loadShader(Context *);
     virtual void init(Context *);
 
-
-protected:
-    uint32_t mLightCount;
-    ObjectBaseRef<const Light> mLights[MAX_LIGHTS];
-
-    // Hacks to create a program for now
-    bool mTextureMatrixEnable;
-
-private:
-    void initAddUserElement(const Element *e, String8 *names, uint32_t *count, const char *prefix);
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_VERTEX; }
+    static ProgramVertex *createFromStream(Context *rsc, IStream *stream);
 };
 
 
@@ -71,18 +57,13 @@
     ProgramVertexState();
     ~ProgramVertexState();
 
-    void init(Context *rsc, int32_t w, int32_t h);
+    void init(Context *rsc);
     void deinit(Context *rsc);
-    void updateSize(Context *rsc, int32_t w, int32_t h);
+    void updateSize(Context *rsc);
 
     ObjectBaseRef<ProgramVertex> mDefault;
     ObjectBaseRef<ProgramVertex> mLast;
     ObjectBaseRef<Allocation> mDefaultAlloc;
-
-    ObjectBaseRef<Type> mAllocType;
-
-
-    float color[4];
 };
 
 
diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp
index 71f508f..cbdc407 100644
--- a/libs/rs/rsSampler.cpp
+++ b/libs/rs/rsSampler.cpp
@@ -14,10 +14,16 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include <GLES/gl.h>
 #include <GLES/glext.h>
-
 #include "rsContext.h"
+#else
+#include "rsContextHostStub.h"
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#endif //ANDROID_RS_BUILD_FOR_HOST
+
 #include "rsSampler.h"
 
 
@@ -27,8 +33,6 @@
 
 Sampler::Sampler(Context *rsc) : ObjectBase(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     // Should not get called.
     rsAssert(0);
 }
@@ -38,22 +42,22 @@
                  RsSamplerValue minFilter,
                  RsSamplerValue wrapS,
                  RsSamplerValue wrapT,
-                 RsSamplerValue wrapR) : ObjectBase(rsc)
+                 RsSamplerValue wrapR,
+                 float aniso) : ObjectBase(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     mMagFilter = magFilter;
     mMinFilter = minFilter;
     mWrapS = wrapS;
     mWrapT = wrapT;
     mWrapR = wrapR;
+    mAniso = aniso;
 }
 
 Sampler::~Sampler()
 {
 }
 
-void Sampler::setupGL(const Context *rsc, bool npot)
+void Sampler::setupGL(const Context *rsc, const Allocation *tex)
 {
     GLenum trans[] = {
         GL_NEAREST, //RS_SAMPLER_NEAREST,
@@ -61,25 +65,42 @@
         GL_LINEAR_MIPMAP_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR,
         GL_REPEAT, //RS_SAMPLER_WRAP,
         GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP
-
     };
 
-    bool forceNonMip = false;
-    if (!rsc->ext_OES_texture_npot() && npot) {
-        forceNonMip = true;
-    }
+    GLenum transNP[] = {
+        GL_NEAREST, //RS_SAMPLER_NEAREST,
+        GL_LINEAR, //RS_SAMPLER_LINEAR,
+        GL_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR,
+        GL_CLAMP_TO_EDGE, //RS_SAMPLER_WRAP,
+        GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP
+    };
 
-    if ((mMinFilter == RS_SAMPLER_LINEAR_MIP_LINEAR) && forceNonMip) {
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    if (!rsc->ext_OES_texture_npot() && tex->getType()->getIsNp2()) {
+        if (tex->getHasGraphicsMipmaps() && rsc->ext_GL_NV_texture_npot_2D_mipmap()) {
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+        } else {
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, transNP[mMinFilter]);
+        }
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, transNP[mMagFilter]);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, transNP[mWrapS]);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, transNP[mWrapT]);
     } else {
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+        if (tex->getHasGraphicsMipmaps()) {
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+        } else {
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, transNP[mMinFilter]);
+        }
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, trans[mWrapS]);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, trans[mWrapT]);
     }
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, trans[mWrapS]);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, trans[mWrapT]);
 
+    float anisoValue = rsMin(rsc->ext_texture_max_aniso(), mAniso);
+    if(rsc->ext_texture_max_aniso() > 1.0f) {
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoValue);
+    }
 
-    rsc->checkError("ProgramFragment::setupGL2 tex env");
+    rsc->checkError("Sampler::setupGL2 tex env");
 }
 
 void Sampler::bindToContext(SamplerState *ss, uint32_t slot)
@@ -94,6 +115,17 @@
     mBoundSlot = -1;
     ss->mSamplers[slot].clear();
 }
+
+void Sampler::serialize(OStream *stream) const
+{
+
+}
+
+Sampler *Sampler::createFromStream(Context *rsc, IStream *stream)
+{
+    return NULL;
+}
+
 /*
 void SamplerState::setupGL()
 {
@@ -122,6 +154,7 @@
     ss->mWrapS = RS_SAMPLER_WRAP;
     ss->mWrapT = RS_SAMPLER_WRAP;
     ss->mWrapR = RS_SAMPLER_WRAP;
+    ss->mAniso = 1.0f;
 }
 
 void rsi_SamplerSet(Context *rsc, RsSamplerParam param, RsSamplerValue value)
@@ -144,21 +177,37 @@
     case RS_SAMPLER_WRAP_R:
         ss->mWrapR = value;
         break;
+    default:
+        LOGE("Attempting to set invalid value on sampler");
+        break;
     }
+}
 
+void rsi_SamplerSet2(Context *rsc, RsSamplerParam param, float value)
+{
+    SamplerState * ss = &rsc->mStateSampler;
+
+    switch(param) {
+    case RS_SAMPLER_ANISO:
+        ss->mAniso = value;
+        break;
+    default:
+        LOGE("Attempting to set invalid value on sampler");
+        break;
+    }
 }
 
 RsSampler rsi_SamplerCreate(Context *rsc)
 {
     SamplerState * ss = &rsc->mStateSampler;
 
-
     Sampler * s = new Sampler(rsc,
                               ss->mMagFilter,
                               ss->mMinFilter,
                               ss->mWrapS,
                               ss->mWrapT,
-                              ss->mWrapR);
+                              ss->mWrapR,
+                              ss->mAniso);
     s->incUserRef();
     return s;
 }
diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h
index 0506081..4946355 100644
--- a/libs/rs/rsSampler.h
+++ b/libs/rs/rsSampler.h
@@ -36,22 +36,28 @@
             RsSamplerValue minFilter,
             RsSamplerValue wrapS,
             RsSamplerValue wrapT,
-            RsSamplerValue wrapR);
+            RsSamplerValue wrapR,
+            float aniso = 1.0f);
 
     virtual ~Sampler();
 
     void bind(Allocation *);
-    void setupGL(const Context *, bool npot);
+    void setupGL(const Context *, const Allocation *);
 
     void bindToContext(SamplerState *, uint32_t slot);
     void unbindFromContext(SamplerState *);
 
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_SAMPLER; }
+    static Sampler *createFromStream(Context *rsc, IStream *stream);
+
 protected:
     RsSamplerValue mMagFilter;
     RsSamplerValue mMinFilter;
     RsSamplerValue mWrapS;
     RsSamplerValue mWrapT;
     RsSamplerValue mWrapR;
+    float mAniso;
 
     int32_t mBoundSlot;
 
@@ -70,6 +76,7 @@
     RsSamplerValue mWrapS;
     RsSamplerValue mWrapT;
     RsSamplerValue mWrapR;
+    float mAniso;
 
 
     ObjectBaseRef<Sampler> mSamplers[RS_MAX_SAMPLER_SLOT];
diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp
index a33933b..ef380d2 100644
--- a/libs/rs/rsScript.cpp
+++ b/libs/rs/rsScript.cpp
@@ -21,20 +21,53 @@
 
 Script::Script(Context *rsc) : ObjectBase(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     memset(&mEnviroment, 0, sizeof(mEnviroment));
-    mEnviroment.mClearColor[0] = 0;
-    mEnviroment.mClearColor[1] = 0;
-    mEnviroment.mClearColor[2] = 0;
-    mEnviroment.mClearColor[3] = 1;
-    mEnviroment.mClearDepth = 1;
-    mEnviroment.mClearStencil = 0;
-    mEnviroment.mIsRoot = false;
+
+    mSlots = NULL;
+    mTypes = NULL;
 }
 
 Script::~Script()
 {
+    if(mSlots) {
+        delete [] mSlots;
+        mSlots = NULL;
+    }
+    if(mTypes) {
+        delete [] mTypes;
+        mTypes = NULL;
+    }
+}
+
+void Script::initSlots() {
+    if(mEnviroment.mFieldCount > 0) {
+        mSlots = new ObjectBaseRef<Allocation>[mEnviroment.mFieldCount];
+        mTypes = new ObjectBaseRef<const Type>[mEnviroment.mFieldCount];
+    }
+}
+
+void Script::setSlot(uint32_t slot, Allocation *a) {
+    if(slot >= mEnviroment.mFieldCount) {
+        LOGE("Script::setSlot unable to set allocation, invalid slot index");
+        return;
+    }
+
+    mSlots[slot].set(a);
+}
+
+void Script::setVar(uint32_t slot, const void *val, uint32_t len)
+{
+    int32_t *destPtr = ((int32_t **)mEnviroment.mFieldAddress)[slot];
+    if (destPtr) {
+        //LOGE("setVar f1  %f", ((const float *)destPtr)[0]);
+        //LOGE("setVar %p %i", destPtr, len);
+        memcpy(destPtr, val, len);
+        //LOGE("setVar f2  %f", ((const float *)destPtr)[0]);
+    } else {
+        //if (rsc->props.mLogScripts) {
+            LOGV("Calling setVar on slot = %i which is null", slot);
+        //}
+    }
 }
 
 namespace android {
@@ -44,16 +77,9 @@
 void rsi_ScriptBindAllocation(Context * rsc, RsScript vs, RsAllocation va, uint32_t slot)
 {
     Script *s = static_cast<Script *>(vs);
-    s->mSlots[slot].set(static_cast<Allocation *>(va));
-}
-
-void rsi_ScriptSetClearColor(Context * rsc, RsScript vs, float r, float g, float b, float a)
-{
-    Script *s = static_cast<Script *>(vs);
-    s->mEnviroment.mClearColor[0] = r;
-    s->mEnviroment.mClearColor[1] = g;
-    s->mEnviroment.mClearColor[2] = b;
-    s->mEnviroment.mClearColor[3] = a;
+    Allocation *a = static_cast<Allocation *>(va);
+    s->setSlot(slot, a);
+    //LOGE("rsi_ScriptBindAllocation %i  %p  %p", slot, a, a->getPtr());
 }
 
 void rsi_ScriptSetTimeZone(Context * rsc, RsScript vs, const char * timeZone, uint32_t length)
@@ -62,53 +88,54 @@
     s->mEnviroment.mTimeZone = timeZone;
 }
 
-void rsi_ScriptSetClearDepth(Context * rsc, RsScript vs, float v)
-{
-    Script *s = static_cast<Script *>(vs);
-    s->mEnviroment.mClearDepth = v;
-}
-
-void rsi_ScriptSetClearStencil(Context * rsc, RsScript vs, uint32_t v)
-{
-    Script *s = static_cast<Script *>(vs);
-    s->mEnviroment.mClearStencil = v;
-}
-
-void rsi_ScriptSetType(Context * rsc, RsType vt, uint32_t slot, bool writable, const char *name)
-{
-    ScriptCState *ss = &rsc->mScriptC;
-    const Type *t = static_cast<const Type *>(vt);
-    ss->mConstantBufferTypes[slot].set(t);
-    ss->mSlotWritable[slot] = writable;
-    if (name) {
-        ss->mSlotNames[slot].setTo(name);
-    } else {
-        ss->mSlotNames[slot].setTo("");
-    }
-}
-
-void rsi_ScriptSetInvoke(Context *rsc, const char *name, uint32_t slot)
-{
-    ScriptCState *ss = &rsc->mScriptC;
-    ss->mInvokableNames[slot] = name;
-}
-
 void rsi_ScriptInvoke(Context *rsc, RsScript vs, uint32_t slot)
 {
     Script *s = static_cast<Script *>(vs);
-    if (s->mEnviroment.mInvokables[slot] == NULL) {
-        rsc->setError(RS_ERROR_BAD_SCRIPT, "Calling invoke on bad script");
-        return;
-    }
-    s->setupScript();
-    s->mEnviroment.mInvokables[slot]();
+    s->Invoke(rsc, slot, NULL, 0);
 }
 
 
-void rsi_ScriptSetRoot(Context * rsc, bool isRoot)
+void rsi_ScriptInvokeData(Context *rsc, RsScript vs, uint32_t slot, void *data)
 {
-    ScriptCState *ss = &rsc->mScriptC;
-    ss->mScript->mEnviroment.mIsRoot = isRoot;
+    Script *s = static_cast<Script *>(vs);
+    s->Invoke(rsc, slot, NULL, 0);
+}
+
+void rsi_ScriptInvokeV(Context *rsc, RsScript vs, uint32_t slot, const void *data, uint32_t len)
+{
+    Script *s = static_cast<Script *>(vs);
+    s->Invoke(rsc, slot, data, len);
+}
+
+void rsi_ScriptSetVarI(Context *rsc, RsScript vs, uint32_t slot, int value)
+{
+    Script *s = static_cast<Script *>(vs);
+    s->setVar(slot, &value, sizeof(value));
+}
+
+void rsi_ScriptSetVarJ(Context *rsc, RsScript vs, uint32_t slot, long long value)
+{
+    Script *s = static_cast<Script *>(vs);
+    s->setVar(slot, &value, sizeof(value));
+}
+
+void rsi_ScriptSetVarF(Context *rsc, RsScript vs, uint32_t slot, float value)
+{
+    Script *s = static_cast<Script *>(vs);
+    s->setVar(slot, &value, sizeof(value));
+}
+
+void rsi_ScriptSetVarD(Context *rsc, RsScript vs, uint32_t slot, double value)
+{
+    Script *s = static_cast<Script *>(vs);
+    s->setVar(slot, &value, sizeof(value));
+}
+
+void rsi_ScriptSetVarV(Context *rsc, RsScript vs, uint32_t slot, const void *data, uint32_t len)
+{
+    const float *fp = (const float *)data;
+    Script *s = static_cast<Script *>(vs);
+    s->setVar(slot, data, len);
 }
 
 
diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h
index 5f4a536..c73bb5e 100644
--- a/libs/rs/rsScript.h
+++ b/libs/rs/rsScript.h
@@ -27,9 +27,7 @@
 class ProgramVertex;
 class ProgramFragment;
 class ProgramRaster;
-class ProgramFragmentStore;
-
-#define MAX_SCRIPT_BANKS 16
+class ProgramStore;
 
 class Script : public ObjectBase
 {
@@ -39,38 +37,45 @@
     Script(Context *);
     virtual ~Script();
 
-
     struct Enviroment_t {
-        bool mIsRoot;
-        float mClearColor[4];
-        float mClearDepth;
-        uint32_t mClearStencil;
-
-        uint32_t mStartTimeMillis;
+        int64_t mStartTimeMillis;
+        int64_t mLastDtTime;
         const char* mTimeZone;
 
         ObjectBaseRef<ProgramVertex> mVertex;
         ObjectBaseRef<ProgramFragment> mFragment;
         ObjectBaseRef<ProgramRaster> mRaster;
-        ObjectBaseRef<ProgramFragmentStore> mFragmentStore;
-        InvokeFunc_t mInvokables[MAX_SCRIPT_BANKS];
+        ObjectBaseRef<ProgramStore> mFragmentStore;
+
+        uint32_t mInvokeFunctionCount;
+        InvokeFunc_t *mInvokeFunctions;
+        uint32_t mFieldCount;
+        void ** mFieldAddress;
+
         char * mScriptText;
         uint32_t mScriptTextLength;
+
+        bool mIsThreadable;
     };
     Enviroment_t mEnviroment;
 
-    uint32_t mCounstantBufferCount;
+    void initSlots();
+    void setSlot(uint32_t slot, Allocation *a);
+    void setVar(uint32_t slot, const void *val, uint32_t len);
 
+    virtual void runForEach(Context *rsc,
+                            const Allocation * ain,
+                            Allocation * aout,
+                            const void * usr,
+                            const RsScriptCall *sc = NULL) = 0;
 
-    ObjectBaseRef<Allocation> mSlots[MAX_SCRIPT_BANKS];
-    ObjectBaseRef<const Type> mTypes[MAX_SCRIPT_BANKS];
-    String8 mSlotNames[MAX_SCRIPT_BANKS];
-    bool mSlotWritable[MAX_SCRIPT_BANKS];
+    virtual void Invoke(Context *rsc, uint32_t slot, const void *data, uint32_t len) = 0;
+    virtual void setupScript(Context *rsc) = 0;
+    virtual uint32_t run(Context *) = 0;
+protected:
+    ObjectBaseRef<Allocation> *mSlots;
+    ObjectBaseRef<const Type> *mTypes;
 
-
-
-    virtual void setupScript() = 0;
-    virtual uint32_t run(Context *, uint32_t launchID) = 0;
 };
 
 
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index f4d2451..c692bc1 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -17,9 +17,9 @@
 #include "rsContext.h"
 #include "rsScriptC.h"
 #include "rsMatrix.h"
-
-#include "acc/acc.h"
+#include "../../compile/libbcc/include/bcc/bcc.h"
 #include "utils/Timers.h"
+#include "utils/StopWatch.h"
 
 #include <GLES/gl.h>
 #include <GLES/glext.h>
@@ -35,42 +35,82 @@
 
 ScriptC::ScriptC(Context *rsc) : Script(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
-    mAccScript = NULL;
+    mBccScript = NULL;
     memset(&mProgram, 0, sizeof(mProgram));
 }
 
 ScriptC::~ScriptC()
 {
-    if (mAccScript) {
-        accDeleteScript(mAccScript);
+    if (mBccScript) {
+        bccDeleteScript(mBccScript);
     }
     free(mEnviroment.mScriptText);
     mEnviroment.mScriptText = NULL;
 }
 
-void ScriptC::setupScript()
+void ScriptC::setupScript(Context *rsc)
 {
-    for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
-        if (mProgram.mSlotPointers[ct]) {
-            *mProgram.mSlotPointers[ct] = mSlots[ct]->getPtr();
+    setupGLState(rsc);
+    mEnviroment.mStartTimeMillis
+                = nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC));
+
+    for (uint32_t ct=0; ct < mEnviroment.mFieldCount; ct++) {
+        if (mSlots[ct].get() && !mTypes[ct].get()) {
+            mTypes[ct].set(mSlots[ct]->getType());
+        }
+
+        if (!mTypes[ct].get())
+            continue;
+        void *ptr = NULL;
+        if (mSlots[ct].get()) {
+            ptr = mSlots[ct]->getPtr();
+        }
+        void **dest = ((void ***)mEnviroment.mFieldAddress)[ct];
+
+        if (rsc->props.mLogScripts) {
+            LOGV("%p ScriptC::setupScript slot=%i  dst=%p  src=%p  type=%p", rsc, ct, dest, ptr, mSlots[ct]->getType());
+
+            //const uint32_t *p32 = (const uint32_t *)ptr;
+            //for (uint32_t ct2=0; ct2 < mSlots[ct]->getType()->getDimX(); ct2++) {
+                //LOGE("  %i = 0x%08x ", ct2, p32[ct2]);
+            //}
+        }
+
+        if (dest) {
+            *dest = ptr;
         }
     }
 }
 
-
-uint32_t ScriptC::run(Context *rsc, uint32_t launchIndex)
+const Allocation *ScriptC::ptrToAllocation(const void *ptr) const
 {
-    if (mProgram.mScript == NULL) {
-        rsc->setError(RS_ERROR_BAD_SCRIPT, "Attempted to run bad script");
-        return 0;
+    if (!ptr) {
+        return NULL;
     }
+    for (uint32_t ct=0; ct < mEnviroment.mFieldCount; ct++) {
+        if (!mSlots[ct].get())
+            continue;
+        if (mSlots[ct]->getPtr() == ptr) {
+            return mSlots[ct].get();
+        }
+    }
+    LOGE("ScriptC::ptrToAllocation, failed to find %p", ptr);
+    return NULL;
+}
 
-    Context::ScriptTLSStruct * tls =
-    (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey);
+Script * ScriptC::setTLS(Script *sc)
+{
+    Context::ScriptTLSStruct * tls = (Context::ScriptTLSStruct *)
+                                  pthread_getspecific(Context::gThreadTLSKey);
     rsAssert(tls);
+    Script *old = tls->mScript;
+    tls->mScript = sc;
+    return old;
+}
 
+
+void ScriptC::setupGLState(Context *rsc)
+{
     if (mEnviroment.mFragmentStore.get()) {
         rsc->setFragmentStore(mEnviroment.mFragmentStore.get());
     }
@@ -83,119 +123,349 @@
     if (mEnviroment.mRaster.get()) {
         rsc->setRaster(mEnviroment.mRaster.get());
     }
+}
 
-    if (launchIndex == 0) {
-        mEnviroment.mStartTimeMillis
-                = nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC));
+uint32_t ScriptC::run(Context *rsc)
+{
+    if (mProgram.mRoot == NULL) {
+        rsc->setError(RS_ERROR_BAD_SCRIPT, "Attempted to run bad script");
+        return 0;
     }
-    setupScript();
+
+    setupScript(rsc);
 
     uint32_t ret = 0;
-    tls->mScript = this;
-    ret = mProgram.mScript(launchIndex);
-    tls->mScript = NULL;
+    Script * oldTLS = setTLS(this);
+
+    if (rsc->props.mLogScripts) {
+        LOGV("%p ScriptC::run invoking root,  ptr %p", rsc, mProgram.mRoot);
+    }
+
+    ret = mProgram.mRoot();
+
+    if (rsc->props.mLogScripts) {
+        LOGV("%p ScriptC::run invoking complete, ret=%i", rsc, ret);
+    }
+
+    setTLS(oldTLS);
     return ret;
 }
 
+
+typedef struct {
+    Context *rsc;
+    ScriptC *script;
+    const Allocation * ain;
+    Allocation * aout;
+    const void * usr;
+
+    uint32_t mSliceSize;
+    volatile int mSliceNum;
+
+    const uint8_t *ptrIn;
+    uint32_t eStrideIn;
+    uint8_t *ptrOut;
+    uint32_t eStrideOut;
+
+    uint32_t xStart;
+    uint32_t xEnd;
+    uint32_t yStart;
+    uint32_t yEnd;
+    uint32_t zStart;
+    uint32_t zEnd;
+    uint32_t arrayStart;
+    uint32_t arrayEnd;
+
+    uint32_t dimX;
+    uint32_t dimY;
+    uint32_t dimZ;
+    uint32_t dimArray;
+} MTLaunchStruct;
+typedef int (*rs_t)(const void *, void *, const void *, uint32_t, uint32_t, uint32_t, uint32_t);
+
+static void wc_xy(void *usr, uint32_t idx)
+{
+    MTLaunchStruct *mtls = (MTLaunchStruct *)usr;
+
+    while (1) {
+        uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum);
+        uint32_t yStart = mtls->yStart + slice * mtls->mSliceSize;
+        uint32_t yEnd = yStart + mtls->mSliceSize;
+        yEnd = rsMin(yEnd, mtls->yEnd);
+        if (yEnd <= yStart) {
+            return;
+        }
+
+        //LOGE("usr idx %i, x %i,%i  y %i,%i", idx, mtls->xStart, mtls->xEnd, yStart, yEnd);
+        //LOGE("usr ptr in %p,  out %p", mtls->ptrIn, mtls->ptrOut);
+        for (uint32_t y = yStart; y < yEnd; y++) {
+            uint32_t offset = mtls->dimX * y;
+            uint8_t *xPtrOut = mtls->ptrOut + (mtls->eStrideOut * offset);
+            const uint8_t *xPtrIn = mtls->ptrIn + (mtls->eStrideIn * offset);
+
+            for (uint32_t x = mtls->xStart; x < mtls->xEnd; x++) {
+                ((rs_t)mtls->script->mProgram.mRoot) (xPtrIn, xPtrOut, mtls->usr, x, y, 0, 0);
+                xPtrIn += mtls->eStrideIn;
+                xPtrOut += mtls->eStrideOut;
+            }
+        }
+    }
+
+}
+
+static void wc_x(void *usr, uint32_t idx)
+{
+    MTLaunchStruct *mtls = (MTLaunchStruct *)usr;
+
+    while (1) {
+        uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum);
+        uint32_t xStart = mtls->xStart + slice * mtls->mSliceSize;
+        uint32_t xEnd = xStart + mtls->mSliceSize;
+        xEnd = rsMin(xEnd, mtls->xEnd);
+        if (xEnd <= xStart) {
+            return;
+        }
+
+        //LOGE("usr idx %i, x %i,%i  y %i,%i", idx, mtls->xStart, mtls->xEnd, yStart, yEnd);
+        //LOGE("usr ptr in %p,  out %p", mtls->ptrIn, mtls->ptrOut);
+        uint8_t *xPtrOut = mtls->ptrOut + (mtls->eStrideOut * xStart);
+        const uint8_t *xPtrIn = mtls->ptrIn + (mtls->eStrideIn * xStart);
+        for (uint32_t x = xStart; x < xEnd; x++) {
+            ((rs_t)mtls->script->mProgram.mRoot) (xPtrIn, xPtrOut, mtls->usr, x, 0, 0, 0);
+            xPtrIn += mtls->eStrideIn;
+            xPtrOut += mtls->eStrideOut;
+        }
+    }
+
+}
+
+void ScriptC::runForEach(Context *rsc,
+                         const Allocation * ain,
+                         Allocation * aout,
+                         const void * usr,
+                         const RsScriptCall *sc)
+{
+    MTLaunchStruct mtls;
+    memset(&mtls, 0, sizeof(mtls));
+
+    if (ain) {
+        mtls.dimX = ain->getType()->getDimX();
+        mtls.dimY = ain->getType()->getDimY();
+        mtls.dimZ = ain->getType()->getDimZ();
+        //mtls.dimArray = ain->getType()->getDimArray();
+    } else if (aout) {
+        mtls.dimX = aout->getType()->getDimX();
+        mtls.dimY = aout->getType()->getDimY();
+        mtls.dimZ = aout->getType()->getDimZ();
+        //mtls.dimArray = aout->getType()->getDimArray();
+    } else {
+        rsc->setError(RS_ERROR_BAD_SCRIPT, "rsForEach called with null allocations");
+        return;
+    }
+
+    if (!sc || (sc->xEnd == 0)) {
+        mtls.xEnd = mtls.dimX;
+    } else {
+        rsAssert(sc->xStart < mtls.dimX);
+        rsAssert(sc->xEnd <= mtls.dimX);
+        rsAssert(sc->xStart < sc->xEnd);
+        mtls.xStart = rsMin(mtls.dimX, sc->xStart);
+        mtls.xEnd = rsMin(mtls.dimX, sc->xEnd);
+        if (mtls.xStart >= mtls.xEnd) return;
+    }
+
+    if (!sc || (sc->yEnd == 0)) {
+        mtls.yEnd = mtls.dimY;
+    } else {
+        rsAssert(sc->yStart < mtls.dimY);
+        rsAssert(sc->yEnd <= mtls.dimY);
+        rsAssert(sc->yStart < sc->yEnd);
+        mtls.yStart = rsMin(mtls.dimY, sc->yStart);
+        mtls.yEnd = rsMin(mtls.dimY, sc->yEnd);
+        if (mtls.yStart >= mtls.yEnd) return;
+    }
+
+    mtls.xEnd = rsMax((uint32_t)1, mtls.xEnd);
+    mtls.yEnd = rsMax((uint32_t)1, mtls.yEnd);
+    mtls.zEnd = rsMax((uint32_t)1, mtls.zEnd);
+    mtls.arrayEnd = rsMax((uint32_t)1, mtls.arrayEnd);
+
+    rsAssert(ain->getType()->getDimZ() == 0);
+
+    setupScript(rsc);
+    Script * oldTLS = setTLS(this);
+
+
+    mtls.rsc = rsc;
+    mtls.ain = ain;
+    mtls.aout = aout;
+    mtls.script = this;
+    mtls.usr = usr;
+    mtls.mSliceSize = 10;
+    mtls.mSliceNum = 0;
+
+    mtls.ptrIn = NULL;
+    mtls.eStrideIn = 0;
+    if (ain) {
+        mtls.ptrIn = (const uint8_t *)ain->getPtr();
+        mtls.eStrideIn = ain->getType()->getElementSizeBytes();
+    }
+
+    mtls.ptrOut = NULL;
+    mtls.eStrideOut = 0;
+    if (aout) {
+        mtls.ptrOut = (uint8_t *)aout->getPtr();
+        mtls.eStrideOut = aout->getType()->getElementSizeBytes();
+    }
+
+    if ((rsc->getWorkerPoolSize() > 1) && mEnviroment.mIsThreadable) {
+        if (mtls.dimY > 1) {
+            rsc->launchThreads(wc_xy, &mtls);
+        } else {
+            rsc->launchThreads(wc_x, &mtls);
+        }
+
+        //LOGE("launch 1");
+    } else {
+        //LOGE("launch 3");
+        for (uint32_t ar = mtls.arrayStart; ar < mtls.arrayEnd; ar++) {
+            for (uint32_t z = mtls.zStart; z < mtls.zEnd; z++) {
+                for (uint32_t y = mtls.yStart; y < mtls.yEnd; y++) {
+                    uint32_t offset = mtls.dimX * mtls.dimY * mtls.dimZ * ar +
+                                      mtls.dimX * mtls.dimY * z +
+                                      mtls.dimX * y;
+                    uint8_t *xPtrOut = mtls.ptrOut + (mtls.eStrideOut * offset);
+                    const uint8_t *xPtrIn = mtls.ptrIn + (mtls.eStrideIn * offset);
+
+                    for (uint32_t x = mtls.xStart; x < mtls.xEnd; x++) {
+                        ((rs_t)mProgram.mRoot) (xPtrIn, xPtrOut, usr, x, y, z, ar);
+                        xPtrIn += mtls.eStrideIn;
+                        xPtrOut += mtls.eStrideOut;
+                    }
+                }
+            }
+        }
+    }
+
+    setTLS(oldTLS);
+}
+
+void ScriptC::Invoke(Context *rsc, uint32_t slot, const void *data, uint32_t len)
+{
+    //LOGE("rsi_ScriptInvoke %i", slot);
+    if ((slot >= mEnviroment.mInvokeFunctionCount) ||
+        (mEnviroment.mInvokeFunctions[slot] == NULL)) {
+        rsc->setError(RS_ERROR_BAD_SCRIPT, "Calling invoke on bad script");
+        return;
+    }
+    setupScript(rsc);
+    Script * oldTLS = setTLS(this);
+
+    if (rsc->props.mLogScripts) {
+        LOGV("%p ScriptC::Invoke invoking slot %i,  ptr %p", rsc, slot, mEnviroment.mInvokeFunctions[slot]);
+    }
+    ((void (*)(const void *, uint32_t))
+        mEnviroment.mInvokeFunctions[slot])(data, len);
+    if (rsc->props.mLogScripts) {
+        LOGV("%p ScriptC::Invoke complete", rsc);
+    }
+
+    setTLS(oldTLS);
+}
+
 ScriptCState::ScriptCState()
 {
-    mScript = NULL;
-    clear();
+    mScript.clear();
 }
 
 ScriptCState::~ScriptCState()
 {
-    delete mScript;
-    mScript = NULL;
+    mScript.clear();
 }
 
-void ScriptCState::clear()
+void ScriptCState::init(Context *rsc)
 {
-    for (uint32_t ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
-        mConstantBufferTypes[ct].clear();
-        mSlotNames[ct].setTo("");
-        mInvokableNames[ct].setTo("");
-        mSlotWritable[ct] = false;
+    clear(rsc);
+}
+
+void ScriptCState::clear(Context *rsc)
+{
+    rsAssert(rsc);
+    mScript.clear();
+    mScript.set(new ScriptC(rsc));
+}
+
+static BCCvoid* symbolLookup(BCCvoid* pContext, const BCCchar* name)
+{
+    const ScriptCState::SymbolTable_t *sym;
+    ScriptC *s = (ScriptC *)pContext;
+    sym = ScriptCState::lookupSymbol(name);
+    if (!sym) {
+        sym = ScriptCState::lookupSymbolCL(name);
     }
-
-    delete mScript;
-    mScript = new ScriptC(NULL);
-
-    mInt32Defines.clear();
-    mFloatDefines.clear();
-}
-
-static ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name)
-{
-    const ScriptCState::SymbolTable_t *sym = ScriptCState::lookupSymbol(name);
+    if (!sym) {
+        sym = ScriptCState::lookupSymbolGL(name);
+    }
     if (sym) {
+        s->mEnviroment.mIsThreadable &= sym->threadable;
         return sym->mPtr;
     }
     LOGE("ScriptC sym lookup failed for %s", name);
     return NULL;
 }
 
+extern const char rs_runtime_lib_bc[];
+extern unsigned rs_runtime_lib_bc_size;
+
 void ScriptCState::runCompiler(Context *rsc, ScriptC *s)
 {
-    s->mAccScript = accCreateScript();
-    String8 tmp;
-
-    rsc->appendNameDefines(&tmp);
-    appendDecls(&tmp);
-    appendVarDefines(rsc, &tmp);
-    appendTypes(rsc, &tmp);
-    tmp.append("#line 1\n");
-
-    const char* scriptSource[] = {tmp.string(), s->mEnviroment.mScriptText};
-    int scriptLength[] = {tmp.length(), s->mEnviroment.mScriptTextLength} ;
-    accScriptSource(s->mAccScript, sizeof(scriptLength) / sizeof(int), scriptSource, scriptLength);
-    accRegisterSymbolCallback(s->mAccScript, symbolLookup, NULL);
-    accCompileScript(s->mAccScript);
-    accGetScriptLabel(s->mAccScript, "main", (ACCvoid**) &s->mProgram.mScript);
-    accGetScriptLabel(s->mAccScript, "init", (ACCvoid**) &s->mProgram.mInit);
-    rsAssert(s->mProgram.mScript);
-
-    if (!s->mProgram.mScript) {
-        ACCchar buf[4096];
-        ACCsizei len;
-        accGetScriptInfoLog(s->mAccScript, sizeof(buf), &len, buf);
-        LOGE("%s", buf);
-        rsc->setError(RS_ERROR_BAD_SCRIPT, "Error compiling user script.");
-        return;
+    {
+        StopWatch compileTimer("RenderScript compile time");
+        s->mBccScript = bccCreateScript();
+        s->mEnviroment.mIsThreadable = true;
+        bccScriptBitcode(s->mBccScript, s->mEnviroment.mScriptText, s->mEnviroment.mScriptTextLength);
+        //bccLinkBitcode(s->mBccScript, rs_runtime_lib_bc, rs_runtime_lib_bc_size);
+        bccRegisterSymbolCallback(s->mBccScript, symbolLookup, s);
+        bccCompileScript(s->mBccScript);
+        bccGetScriptLabel(s->mBccScript, "root", (BCCvoid**) &s->mProgram.mRoot);
+        bccGetScriptLabel(s->mBccScript, "init", (BCCvoid**) &s->mProgram.mInit);
     }
+    LOGV("%p ScriptCState::runCompiler root %p,  init %p", rsc, s->mProgram.mRoot, s->mProgram.mInit);
 
     if (s->mProgram.mInit) {
         s->mProgram.mInit();
     }
 
-    for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
-        if (mSlotNames[ct].length() > 0) {
-            accGetScriptLabel(s->mAccScript,
-                              mSlotNames[ct].string(),
-                              (ACCvoid**) &s->mProgram.mSlotPointers[ct]);
-        }
+    bccGetExportFuncs(s->mBccScript, (BCCsizei*) &s->mEnviroment.mInvokeFunctionCount, 0, NULL);
+    if(s->mEnviroment.mInvokeFunctionCount <= 0)
+        s->mEnviroment.mInvokeFunctions = NULL;
+    else {
+        s->mEnviroment.mInvokeFunctions = (Script::InvokeFunc_t*) calloc(s->mEnviroment.mInvokeFunctionCount, sizeof(Script::InvokeFunc_t));
+        bccGetExportFuncs(s->mBccScript, NULL, s->mEnviroment.mInvokeFunctionCount, (BCCvoid **) s->mEnviroment.mInvokeFunctions);
     }
 
-    for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
-        if (mInvokableNames[ct].length() > 0) {
-            accGetScriptLabel(s->mAccScript,
-                              mInvokableNames[ct].string(),
-                              (ACCvoid**) &s->mEnviroment.mInvokables[ct]);
-        }
+    bccGetExportVars(s->mBccScript, (BCCsizei*) &s->mEnviroment.mFieldCount, 0, NULL);
+    if(s->mEnviroment.mFieldCount <= 0)
+        s->mEnviroment.mFieldAddress = NULL;
+    else {
+        s->mEnviroment.mFieldAddress = (void **) calloc(s->mEnviroment.mFieldCount, sizeof(void *));
+        bccGetExportVars(s->mBccScript, NULL, s->mEnviroment.mFieldCount, (BCCvoid **) s->mEnviroment.mFieldAddress);
+        s->initSlots();
     }
 
     s->mEnviroment.mFragment.set(rsc->getDefaultProgramFragment());
     s->mEnviroment.mVertex.set(rsc->getDefaultProgramVertex());
-    s->mEnviroment.mFragmentStore.set(rsc->getDefaultProgramFragmentStore());
+    s->mEnviroment.mFragmentStore.set(rsc->getDefaultProgramStore());
     s->mEnviroment.mRaster.set(rsc->getDefaultProgramRaster());
 
-    if (s->mProgram.mScript) {
+    if (s->mProgram.mRoot) {
         const static int pragmaMax = 16;
-        ACCsizei pragmaCount;
-        ACCchar * str[pragmaMax];
-        accGetPragmas(s->mAccScript, &pragmaCount, pragmaMax, &str[0]);
+        BCCsizei pragmaCount;
+        BCCchar * str[pragmaMax];
+        bccGetPragmas(s->mBccScript, &pragmaCount, pragmaMax, &str[0]);
 
         for (int ct=0; ct < pragmaCount; ct+=2) {
+            //LOGE("pragme %s %s", str[ct], str[ct+1]);
             if (!strcmp(str[ct], "version")) {
                 continue;
             }
@@ -208,11 +478,6 @@
                     s->mEnviroment.mVertex.clear();
                     continue;
                 }
-                ProgramVertex * pv = (ProgramVertex *)rsc->lookupName(str[ct+1]);
-                if (pv != NULL) {
-                    s->mEnviroment.mVertex.set(pv);
-                    continue;
-                }
                 LOGE("Unreconized value %s passed to stateVertex", str[ct+1]);
             }
 
@@ -224,11 +489,6 @@
                     s->mEnviroment.mRaster.clear();
                     continue;
                 }
-                ProgramRaster * pr = (ProgramRaster *)rsc->lookupName(str[ct+1]);
-                if (pr != NULL) {
-                    s->mEnviroment.mRaster.set(pr);
-                    continue;
-                }
                 LOGE("Unreconized value %s passed to stateRaster", str[ct+1]);
             }
 
@@ -240,11 +500,6 @@
                     s->mEnviroment.mFragment.clear();
                     continue;
                 }
-                ProgramFragment * pf = (ProgramFragment *)rsc->lookupName(str[ct+1]);
-                if (pf != NULL) {
-                    s->mEnviroment.mFragment.set(pf);
-                    continue;
-                }
                 LOGE("Unreconized value %s passed to stateFragment", str[ct+1]);
             }
 
@@ -256,12 +511,6 @@
                     s->mEnviroment.mFragmentStore.clear();
                     continue;
                 }
-                ProgramFragmentStore * pfs =
-                    (ProgramFragmentStore *)rsc->lookupName(str[ct+1]);
-                if (pfs != NULL) {
-                    s->mEnviroment.mFragmentStore.set(pfs);
-                    continue;
-                }
                 LOGE("Unreconized value %s passed to stateStore", str[ct+1]);
             }
 
@@ -273,111 +522,6 @@
     }
 }
 
-static void appendElementBody(String8 *s, const Element *e)
-{
-    s->append(" {\n");
-    for (size_t ct2=0; ct2 < e->getFieldCount(); ct2++) {
-        const Element *c = e->getField(ct2);
-        s->append("    ");
-        s->append(c->getCType());
-        s->append(" ");
-        s->append(e->getFieldName(ct2));
-        s->append(";\n");
-    }
-    s->append("}");
-}
-
-void ScriptCState::appendVarDefines(const Context *rsc, String8 *str)
-{
-    char buf[256];
-    if (rsc->props.mLogScripts) {
-        LOGD("appendVarDefines mInt32Defines.size()=%d mFloatDefines.size()=%d\n",
-                mInt32Defines.size(), mFloatDefines.size());
-    }
-    for (size_t ct=0; ct < mInt32Defines.size(); ct++) {
-        str->append("#define ");
-        str->append(mInt32Defines.keyAt(ct));
-        str->append(" ");
-        sprintf(buf, "%i\n", (int)mInt32Defines.valueAt(ct));
-        str->append(buf);
-    }
-    for (size_t ct=0; ct < mFloatDefines.size(); ct++) {
-        str->append("#define ");
-        str->append(mFloatDefines.keyAt(ct));
-        str->append(" ");
-        sprintf(buf, "%ff\n", mFloatDefines.valueAt(ct));
-        str->append(buf);
-    }
-}
-
-
-
-void ScriptCState::appendTypes(const Context *rsc, String8 *str)
-{
-    char buf[256];
-    String8 tmp;
-
-    str->append("struct vecF32_2_s {float x; float y;};\n");
-    str->append("struct vecF32_3_s {float x; float y; float z;};\n");
-    str->append("struct vecF32_4_s {float x; float y; float z; float w;};\n");
-    str->append("struct vecU8_4_s {char r; char g; char b; char a;};\n");
-    str->append("#define vecF32_2_t struct vecF32_2_s\n");
-    str->append("#define vecF32_3_t struct vecF32_3_s\n");
-    str->append("#define vecF32_4_t struct vecF32_4_s\n");
-    str->append("#define vecU8_4_t struct vecU8_4_s\n");
-    str->append("#define vecI8_4_t struct vecU8_4_s\n");
-
-    for (size_t ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
-        const Type *t = mConstantBufferTypes[ct].get();
-        if (!t) {
-            continue;
-        }
-        const Element *e = t->getElement();
-        if (e->getName() && (e->getFieldCount() > 1)) {
-            String8 s("struct struct_");
-            s.append(e->getName());
-            s.append(e->getCStructBody());
-            s.append(";\n");
-
-            s.append("#define ");
-            s.append(e->getName());
-            s.append("_t struct struct_");
-            s.append(e->getName());
-            s.append("\n\n");
-            if (rsc->props.mLogScripts) {
-                LOGV("%s", static_cast<const char*>(s));
-            }
-            str->append(s);
-        }
-
-        if (mSlotNames[ct].length() > 0) {
-            String8 s;
-            if (e->getName()) {
-                // Use the named struct
-                s.setTo(e->getName());
-            } else {
-                // create an struct named from the slot.
-                s.setTo("struct ");
-                s.append(mSlotNames[ct]);
-                s.append("_s");
-                s.append(e->getCStructBody());
-                //appendElementBody(&s, e);
-                s.append(";\n");
-                s.append("struct ");
-                s.append(mSlotNames[ct]);
-                s.append("_s");
-            }
-
-            s.append(" * ");
-            s.append(mSlotNames[ct]);
-            s.append(";\n");
-            if (rsc->props.mLogScripts) {
-                LOGV("%s", static_cast<const char*>(s));
-            }
-            str->append(s);
-        }
-    }
-}
 
 
 namespace android {
@@ -386,14 +530,7 @@
 void rsi_ScriptCBegin(Context * rsc)
 {
     ScriptCState *ss = &rsc->mScriptC;
-    ss->clear();
-}
-
-void rsi_ScriptCSetScript(Context * rsc, void *vp)
-{
-    rsAssert(0);
-    //ScriptCState *ss = &rsc->mScriptC;
-    //ss->mProgram.mScript = reinterpret_cast<ScriptC::RunScript_t>(vp);
+    ss->clear(rsc);
 }
 
 void rsi_ScriptCSetText(Context *rsc, const char *text, uint32_t len)
@@ -412,35 +549,14 @@
 {
     ScriptCState *ss = &rsc->mScriptC;
 
-    ScriptC *s = ss->mScript;
-    ss->mScript = NULL;
-
-    ss->runCompiler(rsc, s);
+    ObjectBaseRef<ScriptC> s(ss->mScript);
+    ss->mScript.clear();
     s->incUserRef();
-    s->setContext(rsc);
-    for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
-        s->mTypes[ct].set(ss->mConstantBufferTypes[ct].get());
-        s->mSlotNames[ct] = ss->mSlotNames[ct];
-        s->mSlotWritable[ct] = ss->mSlotWritable[ct];
-    }
 
-    ss->clear();
-    return s;
-}
-
-void rsi_ScriptCSetDefineF(Context *rsc, const char* name, float value)
-{
-    ScriptCState *ss = &rsc->mScriptC;
-    ss->mFloatDefines.add(String8(name), value);
-}
-
-void rsi_ScriptCSetDefineI32(Context *rsc, const char* name, int32_t value)
-{
-    ScriptCState *ss = &rsc->mScriptC;
-    ss->mInt32Defines.add(String8(name), value);
+    ss->runCompiler(rsc, s.get());
+    ss->clear(rsc);
+    return s.get();
 }
 
 }
 }
-
-
diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h
index 35abadf..86a7ad3 100644
--- a/libs/rs/rsScriptC.h
+++ b/libs/rs/rsScriptC.h
@@ -21,9 +21,7 @@
 
 #include "RenderScriptEnv.h"
 
-#include <utils/KeyedVector.h>
-
-struct ACCscript;
+struct BCCscript;
 
 // ---------------------------------------------------------------------------
 namespace android {
@@ -34,7 +32,7 @@
 class ScriptC : public Script
 {
 public:
-    typedef int (*RunScript_t)(uint32_t launchIndex);
+    typedef int (*RunScript_t)();
     typedef void (*VoidFunc_t)();
 
     ScriptC(Context *);
@@ -44,18 +42,35 @@
         int mVersionMajor;
         int mVersionMinor;
 
-        RunScript_t mScript;
+        RunScript_t mRoot;
         VoidFunc_t mInit;
-
-        void ** mSlotPointers[MAX_SCRIPT_BANKS];
     };
 
     Program_t mProgram;
 
-    ACCscript*    mAccScript;
+    BCCscript*    mBccScript;
 
-    virtual void setupScript();
-    virtual uint32_t run(Context *, uint32_t launchID);
+    const Allocation *ptrToAllocation(const void *) const;
+
+
+    virtual void Invoke(Context *rsc, uint32_t slot, const void *data, uint32_t len);
+
+    virtual uint32_t run(Context *);
+
+    virtual void runForEach(Context *rsc,
+                            const Allocation * ain,
+                            Allocation * aout,
+                            const void * usr,
+                            const RsScriptCall *sc = NULL);
+
+    virtual void serialize(OStream *stream) const {    }
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_SCRIPT_C; }
+    static Type *createFromStream(Context *rsc, IStream *stream) { return NULL; }
+
+protected:
+    void setupScript(Context *);
+    void setupGLState(Context *);
+    Script * setTLS(Script *);
 };
 
 class ScriptCState
@@ -64,30 +79,22 @@
     ScriptCState();
     ~ScriptCState();
 
-    ScriptC *mScript;
+    ObjectBaseRef<ScriptC> mScript;
 
-    ObjectBaseRef<const Type> mConstantBufferTypes[MAX_SCRIPT_BANKS];
-    String8 mSlotNames[MAX_SCRIPT_BANKS];
-    bool mSlotWritable[MAX_SCRIPT_BANKS];
-    String8 mInvokableNames[MAX_SCRIPT_BANKS];
+    void init(Context *rsc);
 
-    void clear();
+    void clear(Context *rsc);
     void runCompiler(Context *rsc, ScriptC *s);
-    void appendVarDefines(const Context *rsc, String8 *str);
-    void appendTypes(const Context *rsc, String8 *str);
 
     struct SymbolTable_t {
         const char * mName;
         void * mPtr;
-        const char * mRet;
-        const char * mParam;
+        bool threadable;
     };
-    static SymbolTable_t gSyms[];
+    //static SymbolTable_t gSyms[];
     static const SymbolTable_t * lookupSymbol(const char *);
-    static void appendDecls(String8 *str);
-
-    KeyedVector<String8,int> mInt32Defines;
-    KeyedVector<String8,float> mFloatDefines;
+    static const SymbolTable_t * lookupSymbolCL(const char *);
+    static const SymbolTable_t * lookupSymbolGL(const char *);
 };
 
 
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 202ca3d..ecae306 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -17,18 +17,9 @@
 #include "rsContext.h"
 #include "rsScriptC.h"
 #include "rsMatrix.h"
-#include "rsNoise.h"
 
-#include "acc/acc.h"
 #include "utils/Timers.h"
 
-#define GL_GLEXT_PROTOTYPES
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
 #include <time.h>
 
 using namespace android;
@@ -39,252 +30,11 @@
     Context * rsc = tls->mContext; \
     ScriptC * sc = (ScriptC *) tls->mScript
 
-typedef struct {
-    float x;
-    float y;
-    float z;
-} vec3_t;
-
-typedef struct {
-    float x;
-    float y;
-    float z;
-    float w;
-} vec4_t;
-
-typedef struct {
-    float x;
-    float y;
-} vec2_t;
-
-//////////////////////////////////////////////////////////////////////////////
-// IO routines
-//////////////////////////////////////////////////////////////////////////////
-
-static float SC_loadF(uint32_t bank, uint32_t offset)
-{
-    GET_TLS();
-    const void *vp = sc->mSlots[bank]->getPtr();
-    const float *f = static_cast<const float *>(vp);
-    //LOGE("loadF %i %i = %f %x", bank, offset, f, ((int *)&f)[0]);
-    return f[offset];
-}
-
-static int32_t SC_loadI32(uint32_t bank, uint32_t offset)
-{
-    GET_TLS();
-    const void *vp = sc->mSlots[bank]->getPtr();
-    const int32_t *i = static_cast<const int32_t *>(vp);
-    //LOGE("loadI32 %i %i = %i", bank, offset, t);
-    return i[offset];
-}
-
-static float* SC_loadArrayF(uint32_t bank, uint32_t offset)
-{
-    GET_TLS();
-    void *vp = sc->mSlots[bank]->getPtr();
-    float *f = static_cast<float *>(vp);
-    return f + offset;
-}
-
-static int32_t* SC_loadArrayI32(uint32_t bank, uint32_t offset)
-{
-    GET_TLS();
-    void *vp = sc->mSlots[bank]->getPtr();
-    int32_t *i = static_cast<int32_t *>(vp);
-    return i + offset;
-}
-
-static float* SC_loadSimpleMeshVerticesF(RsSimpleMesh mesh, uint32_t idx)
-{
-    SimpleMesh *tm = static_cast<SimpleMesh *>(mesh);
-    void *vp = tm->mVertexBuffers[idx]->getPtr();;
-    return static_cast<float *>(vp);
-}
-
-static void SC_updateSimpleMesh(RsSimpleMesh mesh)
-{
-    GET_TLS();
-    SimpleMesh *sm = static_cast<SimpleMesh *>(mesh);
-    sm->uploadAll(rsc);
-}
-
-static uint32_t SC_loadU32(uint32_t bank, uint32_t offset)
-{
-    GET_TLS();
-    const void *vp = sc->mSlots[bank]->getPtr();
-    const uint32_t *i = static_cast<const uint32_t *>(vp);
-    return i[offset];
-}
-
-static void SC_loadVec4(uint32_t bank, uint32_t offset, rsc_Vector4 *v)
-{
-    GET_TLS();
-    const void *vp = sc->mSlots[bank]->getPtr();
-    const float *f = static_cast<const float *>(vp);
-    memcpy(v, &f[offset], sizeof(rsc_Vector4));
-}
-
-static void SC_loadMatrix(uint32_t bank, uint32_t offset, rsc_Matrix *m)
-{
-    GET_TLS();
-    const void *vp = sc->mSlots[bank]->getPtr();
-    const float *f = static_cast<const float *>(vp);
-    memcpy(m, &f[offset], sizeof(rsc_Matrix));
-}
-
-
-static void SC_storeF(uint32_t bank, uint32_t offset, float v)
-{
-    //LOGE("storeF %i %i %f", bank, offset, v);
-    GET_TLS();
-    void *vp = sc->mSlots[bank]->getPtr();
-    float *f = static_cast<float *>(vp);
-    f[offset] = v;
-}
-
-static void SC_storeI32(uint32_t bank, uint32_t offset, int32_t v)
-{
-    GET_TLS();
-    void *vp = sc->mSlots[bank]->getPtr();
-    int32_t *f = static_cast<int32_t *>(vp);
-    static_cast<int32_t *>(sc->mSlots[bank]->getPtr())[offset] = v;
-}
-
-static void SC_storeU32(uint32_t bank, uint32_t offset, uint32_t v)
-{
-    GET_TLS();
-    void *vp = sc->mSlots[bank]->getPtr();
-    uint32_t *f = static_cast<uint32_t *>(vp);
-    static_cast<uint32_t *>(sc->mSlots[bank]->getPtr())[offset] = v;
-}
-
-static void SC_storeVec4(uint32_t bank, uint32_t offset, const rsc_Vector4 *v)
-{
-    GET_TLS();
-    void *vp = sc->mSlots[bank]->getPtr();
-    float *f = static_cast<float *>(vp);
-    memcpy(&f[offset], v, sizeof(rsc_Vector4));
-}
-
-static void SC_storeMatrix(uint32_t bank, uint32_t offset, const rsc_Matrix *m)
-{
-    GET_TLS();
-    void *vp = sc->mSlots[bank]->getPtr();
-    float *f = static_cast<float *>(vp);
-    memcpy(&f[offset], m, sizeof(rsc_Matrix));
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// Vec3 routines
-//////////////////////////////////////////////////////////////////////////////
-
-static void SC_vec3Norm(vec3_t *v)
-{
-    float len = sqrtf(v->x * v->x + v->y * v->y + v->z * v->z);
-    len = 1 / len;
-    v->x *= len;
-    v->y *= len;
-    v->z *= len;
-}
-
-static float SC_vec3Length(const vec3_t *v)
-{
-    return sqrtf(v->x * v->x + v->y * v->y + v->z * v->z);
-}
-
-static void SC_vec3Add(vec3_t *dest, const vec3_t *lhs, const vec3_t *rhs)
-{
-    dest->x = lhs->x + rhs->x;
-    dest->y = lhs->y + rhs->y;
-    dest->z = lhs->z + rhs->z;
-}
-
-static void SC_vec3Sub(vec3_t *dest, const vec3_t *lhs, const vec3_t *rhs)
-{
-    dest->x = lhs->x - rhs->x;
-    dest->y = lhs->y - rhs->y;
-    dest->z = lhs->z - rhs->z;
-}
-
-static void SC_vec3Cross(vec3_t *dest, const vec3_t *lhs, const vec3_t *rhs)
-{
-    float x = lhs->y * rhs->z  - lhs->z * rhs->y;
-    float y = lhs->z * rhs->x  - lhs->x * rhs->z;
-    float z = lhs->x * rhs->y  - lhs->y * rhs->x;
-    dest->x = x;
-    dest->y = y;
-    dest->z = z;
-}
-
-static float SC_vec3Dot(const vec3_t *lhs, const vec3_t *rhs)
-{
-    return lhs->x * rhs->x + lhs->y * rhs->y + lhs->z * rhs->z;
-}
-
-static void SC_vec3Scale(vec3_t *lhs, float scale)
-{
-    lhs->x *= scale;
-    lhs->y *= scale;
-    lhs->z *= scale;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// Vec4 routines
-//////////////////////////////////////////////////////////////////////////////
-
-static void SC_vec4Norm(vec4_t *v)
-{
-    float len = sqrtf(v->x * v->x + v->y * v->y + v->z * v->z + v->w * v->w);
-    len = 1 / len;
-    v->x *= len;
-    v->y *= len;
-    v->z *= len;
-    v->w *= len;
-}
-
-static float SC_vec4Length(const vec4_t *v)
-{
-    return sqrtf(v->x * v->x + v->y * v->y + v->z * v->z + v->w * v->w);
-}
-
-static void SC_vec4Add(vec4_t *dest, const vec4_t *lhs, const vec4_t *rhs)
-{
-    dest->x = lhs->x + rhs->x;
-    dest->y = lhs->y + rhs->y;
-    dest->z = lhs->z + rhs->z;
-    dest->w = lhs->w + rhs->w;
-}
-
-static void SC_vec4Sub(vec4_t *dest, const vec4_t *lhs, const vec4_t *rhs)
-{
-    dest->x = lhs->x - rhs->x;
-    dest->y = lhs->y - rhs->y;
-    dest->z = lhs->z - rhs->z;
-    dest->w = lhs->w - rhs->w;
-}
-
-static float SC_vec4Dot(const vec4_t *lhs, const vec4_t *rhs)
-{
-    return lhs->x * rhs->x + lhs->y * rhs->y + lhs->z * rhs->z + lhs->w * rhs->w;
-}
-
-static void SC_vec4Scale(vec4_t *lhs, float scale)
-{
-    lhs->x *= scale;
-    lhs->y *= scale;
-    lhs->z *= scale;
-    lhs->w *= scale;
-}
 
 //////////////////////////////////////////////////////////////////////////////
 // Math routines
 //////////////////////////////////////////////////////////////////////////////
 
-#define PI 3.1415926f
-#define DEG_TO_RAD PI / 180.0f
-#define RAD_TO_DEG 180.0f / PI
-
 static float SC_sinf_fast(float x)
 {
     const float A =   1.0f / (2.0f * M_PI);
@@ -323,6 +73,7 @@
     return 0.2215f * (y * fabsf(y) - y) + y;
 }
 
+
 static float SC_randf(float max)
 {
     float r = (float)rand();
@@ -335,104 +86,20 @@
     return r / RAND_MAX * (max - min) + min;
 }
 
-static int SC_sign(int value)
+static int SC_randi(int max)
 {
-    return (value > 0) - (value < 0);
+    return (int)SC_randf(max);
 }
 
-static float SC_signf(float value)
+static int SC_randi2(int min, int max)
 {
-    return (value > 0) - (value < 0);
+    return (int)SC_randf2(min, max);
 }
 
-static float SC_clampf(float amount, float low, float high)
+static float SC_frac(float v)
 {
-    return amount < low ? low : (amount > high ? high : amount);
-}
-
-static int SC_clamp(int amount, int low, int high)
-{
-    return amount < low ? low : (amount > high ? high : amount);
-}
-
-static float SC_maxf(float a, float b)
-{
-    return a > b ? a : b;
-}
-
-static float SC_minf(float a, float b)
-{
-    return a < b ? a : b;
-}
-
-static float SC_sqrf(float v)
-{
-    return v * v;
-}
-
-static int SC_sqr(int v)
-{
-    return v * v;
-}
-
-static float SC_fracf(float v)
-{
-    return v - floorf(v);
-}
-
-static float SC_roundf(float v)
-{
-    return floorf(v + 0.4999999999);
-}
-
-static float SC_distf2(float x1, float y1, float x2, float y2)
-{
-    float x = x2 - x1;
-    float y = y2 - y1;
-    return sqrtf(x * x + y * y);
-}
-
-static float SC_distf3(float x1, float y1, float z1, float x2, float y2, float z2)
-{
-    float x = x2 - x1;
-    float y = y2 - y1;
-    float z = z2 - z1;
-    return sqrtf(x * x + y * y + z * z);
-}
-
-static float SC_magf2(float a, float b)
-{
-    return sqrtf(a * a + b * b);
-}
-
-static float SC_magf3(float a, float b, float c)
-{
-    return sqrtf(a * a + b * b + c * c);
-}
-
-static float SC_radf(float degrees)
-{
-    return degrees * DEG_TO_RAD;
-}
-
-static float SC_degf(float radians)
-{
-    return radians * RAD_TO_DEG;
-}
-
-static float SC_lerpf(float start, float stop, float amount)
-{
-    return start + (stop - start) * amount;
-}
-
-static float SC_normf(float start, float stop, float value)
-{
-    return (value - start) / (stop - start);
-}
-
-static float SC_mapf(float minStart, float minStop, float maxStart, float maxStop, float value)
-{
-    return maxStart + (maxStart - maxStop) * ((value - minStart) / (minStop - minStart));
+    int i = (int)floor(v);
+    return fmin(v - i, 0x1.fffffep-1f);
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -511,348 +178,22 @@
     return timeinfo->tm_year;
 }
 
-static int32_t SC_uptimeMillis()
+static int64_t SC_uptimeMillis()
 {
     return nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC));
 }
 
-static int32_t SC_startTimeMillis()
+static int64_t SC_uptimeNanos()
+{
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+static float SC_getDt()
 {
     GET_TLS();
-    return sc->mEnviroment.mStartTimeMillis;
-}
-
-static int32_t SC_elapsedTimeMillis()
-{
-    GET_TLS();
-    return nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC))
-            - sc->mEnviroment.mStartTimeMillis;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// Matrix routines
-//////////////////////////////////////////////////////////////////////////////
-
-
-static void SC_matrixLoadIdentity(rsc_Matrix *mat)
-{
-    Matrix *m = reinterpret_cast<Matrix *>(mat);
-    m->loadIdentity();
-}
-
-static void SC_matrixLoadFloat(rsc_Matrix *mat, const float *f)
-{
-    Matrix *m = reinterpret_cast<Matrix *>(mat);
-    m->load(f);
-}
-
-static void SC_matrixLoadMat(rsc_Matrix *mat, const rsc_Matrix *newmat)
-{
-    Matrix *m = reinterpret_cast<Matrix *>(mat);
-    m->load(reinterpret_cast<const Matrix *>(newmat));
-}
-
-static void SC_matrixLoadRotate(rsc_Matrix *mat, float rot, float x, float y, float z)
-{
-    Matrix *m = reinterpret_cast<Matrix *>(mat);
-    m->loadRotate(rot, x, y, z);
-}
-
-static void SC_matrixLoadScale(rsc_Matrix *mat, float x, float y, float z)
-{
-    Matrix *m = reinterpret_cast<Matrix *>(mat);
-    m->loadScale(x, y, z);
-}
-
-static void SC_matrixLoadTranslate(rsc_Matrix *mat, float x, float y, float z)
-{
-    Matrix *m = reinterpret_cast<Matrix *>(mat);
-    m->loadTranslate(x, y, z);
-}
-
-static void SC_matrixLoadMultiply(rsc_Matrix *mat, const rsc_Matrix *lhs, const rsc_Matrix *rhs)
-{
-    Matrix *m = reinterpret_cast<Matrix *>(mat);
-    m->loadMultiply(reinterpret_cast<const Matrix *>(lhs),
-                    reinterpret_cast<const Matrix *>(rhs));
-}
-
-static void SC_matrixMultiply(rsc_Matrix *mat, const rsc_Matrix *rhs)
-{
-    Matrix *m = reinterpret_cast<Matrix *>(mat);
-    m->multiply(reinterpret_cast<const Matrix *>(rhs));
-}
-
-static void SC_matrixRotate(rsc_Matrix *mat, float rot, float x, float y, float z)
-{
-    Matrix *m = reinterpret_cast<Matrix *>(mat);
-    m->rotate(rot, x, y, z);
-}
-
-static void SC_matrixScale(rsc_Matrix *mat, float x, float y, float z)
-{
-    Matrix *m = reinterpret_cast<Matrix *>(mat);
-    m->scale(x, y, z);
-}
-
-static void SC_matrixTranslate(rsc_Matrix *mat, float x, float y, float z)
-{
-    Matrix *m = reinterpret_cast<Matrix *>(mat);
-    m->translate(x, y, z);
-}
-
-
-static void SC_vec2Rand(float *vec, float maxLen)
-{
-    float angle = SC_randf(PI * 2);
-    float len = SC_randf(maxLen);
-    vec[0] = len * sinf(angle);
-    vec[1] = len * cosf(angle);
-}
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-// Context
-//////////////////////////////////////////////////////////////////////////////
-
-static void SC_bindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va)
-{
-    GET_TLS();
-    rsi_ProgramBindTexture(rsc,
-                           static_cast<ProgramFragment *>(vpf),
-                           slot,
-                           static_cast<Allocation *>(va));
-
-}
-
-static void SC_bindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs)
-{
-    GET_TLS();
-    rsi_ProgramBindSampler(rsc,
-                           static_cast<ProgramFragment *>(vpf),
-                           slot,
-                           static_cast<Sampler *>(vs));
-
-}
-
-static void SC_bindProgramFragmentStore(RsProgramFragmentStore pfs)
-{
-    GET_TLS();
-    rsi_ContextBindProgramFragmentStore(rsc, pfs);
-
-}
-
-static void SC_bindProgramFragment(RsProgramFragment pf)
-{
-    GET_TLS();
-    rsi_ContextBindProgramFragment(rsc, pf);
-
-}
-
-static void SC_bindProgramVertex(RsProgramVertex pv)
-{
-    GET_TLS();
-    rsi_ContextBindProgramVertex(rsc, pv);
-
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// VP
-//////////////////////////////////////////////////////////////////////////////
-
-static void SC_vpLoadModelMatrix(const rsc_Matrix *m)
-{
-    GET_TLS();
-    rsc->getVertex()->setModelviewMatrix(m);
-}
-
-static void SC_vpLoadTextureMatrix(const rsc_Matrix *m)
-{
-    GET_TLS();
-    rsc->getVertex()->setTextureMatrix(m);
-}
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-// Drawing
-//////////////////////////////////////////////////////////////////////////////
-
-static void SC_drawLine(float x1, float y1, float z1,
-                        float x2, float y2, float z2)
-{
-    GET_TLS();
-    if (!rsc->setupCheck()) {
-        return;
-    }
-
-    float vtx[] = { x1, y1, z1, x2, y2, z2 };
-    VertexArray va;
-    va.addLegacy(GL_FLOAT, 3, 12, RS_KIND_POSITION, false, (uint32_t)vtx);
-    if (rsc->checkVersion2_0()) {
-        va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache);
-    } else {
-        va.setupGL(rsc, &rsc->mStateVertexArray);
-    }
-
-    glDrawArrays(GL_LINES, 0, 2);
-}
-
-static void SC_drawPoint(float x, float y, float z)
-{
-    GET_TLS();
-    if (!rsc->setupCheck()) {
-        return;
-    }
-
-    float vtx[] = { x, y, z };
-
-    VertexArray va;
-    va.addLegacy(GL_FLOAT, 3, 12, RS_KIND_POSITION, false, (uint32_t)vtx);
-    if (rsc->checkVersion2_0()) {
-        va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache);
-    } else {
-        va.setupGL(rsc, &rsc->mStateVertexArray);
-    }
-
-    glDrawArrays(GL_POINTS, 0, 1);
-}
-
-static void SC_drawQuadTexCoords(float x1, float y1, float z1,
-                                 float u1, float v1,
-                                 float x2, float y2, float z2,
-                                 float u2, float v2,
-                                 float x3, float y3, float z3,
-                                 float u3, float v3,
-                                 float x4, float y4, float z4,
-                                 float u4, float v4)
-{
-    GET_TLS();
-    if (!rsc->setupCheck()) {
-        return;
-    }
-
-    //LOGE("Quad");
-    //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1);
-    //LOGE("%4.2f, %4.2f, %4.2f", x2, y2, z2);
-    //LOGE("%4.2f, %4.2f, %4.2f", x3, y3, z3);
-    //LOGE("%4.2f, %4.2f, %4.2f", x4, y4, z4);
-
-    float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4};
-    const float tex[] = {u1,v1, u2,v2, u3,v3, u4,v4};
-
-    VertexArray va;
-    va.addLegacy(GL_FLOAT, 3, 12, RS_KIND_POSITION, false, (uint32_t)vtx);
-    va.addLegacy(GL_FLOAT, 2, 8, RS_KIND_TEXTURE, false, (uint32_t)tex);
-    if (rsc->checkVersion2_0()) {
-        va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache);
-    } else {
-        va.setupGL(rsc, &rsc->mStateVertexArray);
-    }
-
-
-    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-}
-
-static void SC_drawQuad(float x1, float y1, float z1,
-                        float x2, float y2, float z2,
-                        float x3, float y3, float z3,
-                        float x4, float y4, float z4)
-{
-    SC_drawQuadTexCoords(x1, y1, z1, 0, 1,
-                         x2, y2, z2, 1, 1,
-                         x3, y3, z3, 1, 0,
-                         x4, y4, z4, 0, 0);
-}
-
-static void SC_drawSpriteScreenspace(float x, float y, float z, float w, float h)
-{
-    GET_TLS();
-    ObjectBaseRef<const ProgramVertex> tmp(rsc->getVertex());
-    rsc->setVertex(rsc->getDefaultProgramVertex());
-    //rsc->setupCheck();
-
-    //GLint crop[4] = {0, h, w, -h};
-
-    float sh = rsc->getHeight();
-
-    SC_drawQuad(x,   sh - y,     z,
-                x+w, sh - y,     z,
-                x+w, sh - (y+h), z,
-                x,   sh - (y+h), z);
-    rsc->setVertex((ProgramVertex *)tmp.get());
-}
-
-static void SC_drawSpriteScreenspaceCropped(float x, float y, float z, float w, float h,
-        float cx0, float cy0, float cx1, float cy1)
-{
-    GET_TLS();
-    if (!rsc->setupCheck()) {
-        return;
-    }
-
-    GLint crop[4] = {cx0, cy0, cx1, cy1};
-    glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
-    glDrawTexfOES(x, y, z, w, h);
-}
-
-static void SC_drawSprite(float x, float y, float z, float w, float h)
-{
-    GET_TLS();
-    float vin[3] = {x, y, z};
-    float vout[4];
-
-    //LOGE("ds  in %f %f %f", x, y, z);
-    rsc->getVertex()->transformToScreen(rsc, vout, vin);
-    //LOGE("ds  out %f %f %f %f", vout[0], vout[1], vout[2], vout[3]);
-    vout[0] /= vout[3];
-    vout[1] /= vout[3];
-    vout[2] /= vout[3];
-
-    vout[0] *= rsc->getWidth() / 2;
-    vout[1] *= rsc->getHeight() / 2;
-    vout[0] += rsc->getWidth() / 2;
-    vout[1] += rsc->getHeight() / 2;
-
-    vout[0] -= w/2;
-    vout[1] -= h/2;
-
-    //LOGE("ds  out2 %f %f %f", vout[0], vout[1], vout[2]);
-
-    // U, V, W, H
-    SC_drawSpriteScreenspace(vout[0], vout[1], z, h, w);
-    //rsc->setupCheck();
-}
-
-
-static void SC_drawRect(float x1, float y1,
-                        float x2, float y2, float z)
-{
-    SC_drawQuad(x1, y2, z,
-                x2, y2, z,
-                x2, y1, z,
-                x1, y1, z);
-}
-
-static void SC_drawSimpleMesh(RsSimpleMesh vsm)
-{
-    GET_TLS();
-    SimpleMesh *sm = static_cast<SimpleMesh *>(vsm);
-    if (!rsc->setupCheck()) {
-        return;
-    }
-    sm->render(rsc);
-}
-
-static void SC_drawSimpleMeshRange(RsSimpleMesh vsm, uint32_t start, uint32_t len)
-{
-    GET_TLS();
-    SimpleMesh *sm = static_cast<SimpleMesh *>(vsm);
-    if (!rsc->setupCheck()) {
-        return;
-    }
-    sm->renderRange(rsc, start, len);
+    int64_t l = sc->mEnviroment.mLastDtTime;
+    sc->mEnviroment.mLastDtTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    return ((float)(sc->mEnviroment.mLastDtTime - l)) / 1.0e9;
 }
 
 
@@ -860,531 +201,355 @@
 //
 //////////////////////////////////////////////////////////////////////////////
 
-static void SC_color(float r, float g, float b, float a)
+static uint32_t SC_allocGetDimX(RsAllocation va)
 {
-    GET_TLS();
-    rsc->mStateVertex.color[0] = r;
-    rsc->mStateVertex.color[1] = g;
-    rsc->mStateVertex.color[2] = b;
-    rsc->mStateVertex.color[3] = a;
-    if (!rsc->checkVersion2_0()) {
-        glColor4f(r, g, b, a);
+    const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
+    //LOGE("SC_allocGetDimX a=%p  type=%p", a, a->getType());
+    return a->getType()->getDimX();
+}
+
+static uint32_t SC_allocGetDimY(RsAllocation va)
+{
+    const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
+    return a->getType()->getDimY();
+}
+
+static uint32_t SC_allocGetDimZ(RsAllocation va)
+{
+    const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
+    return a->getType()->getDimZ();
+}
+
+static uint32_t SC_allocGetDimLOD(RsAllocation va)
+{
+    const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
+    return a->getType()->getDimLOD();
+}
+
+static uint32_t SC_allocGetDimFaces(RsAllocation va)
+{
+    const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
+    return a->getType()->getDimFaces();
+}
+
+static const void * SC_getElementAtX(RsAllocation va, uint32_t x)
+{
+    const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
+    const Type *t = a->getType();
+    CHECK_OBJ(t);
+    const uint8_t *p = (const uint8_t *)a->getPtr();
+    return &p[t->getElementSizeBytes() * x];
+}
+
+static const void * SC_getElementAtXY(RsAllocation va, uint32_t x, uint32_t y)
+{
+    const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
+    const Type *t = a->getType();
+    CHECK_OBJ(t);
+    const uint8_t *p = (const uint8_t *)a->getPtr();
+    return &p[t->getElementSizeBytes() * (x + y*t->getDimX())];
+}
+
+static const void * SC_getElementAtXYZ(RsAllocation va, uint32_t x, uint32_t y, uint32_t z)
+{
+    const Allocation *a = static_cast<const Allocation *>(va);
+    CHECK_OBJ(a);
+    const Type *t = a->getType();
+    CHECK_OBJ(t);
+    const uint8_t *p = (const uint8_t *)a->getPtr();
+    return &p[t->getElementSizeBytes() * (x + y*t->getDimX())];
+}
+
+static void SC_setObject(void **vdst, void * vsrc) {
+    //LOGE("SC_setObject  %p,%p  %p", vdst, *vdst, vsrc);
+    if (vsrc) {
+        CHECK_OBJ(vsrc);
+        static_cast<ObjectBase *>(vsrc)->incSysRef();
     }
-}
-
-static void SC_ambient(float r, float g, float b, float a)
-{
-    GLfloat params[] = { r, g, b, a };
-    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, params);
-}
-
-static void SC_diffuse(float r, float g, float b, float a)
-{
-    GLfloat params[] = { r, g, b, a };
-    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, params);
-}
-
-static void SC_specular(float r, float g, float b, float a)
-{
-    GLfloat params[] = { r, g, b, a };
-    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, params);
-}
-
-static void SC_emission(float r, float g, float b, float a)
-{
-    GLfloat params[] = { r, g, b, a };
-    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, params);
-}
-
-static void SC_shininess(float s)
-{
-    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, s);
-}
-
-static void SC_pointAttenuation(float a, float b, float c)
-{
-    GLfloat params[] = { a, b, c };
-    glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, params);
-}
-
-static void SC_hsbToRgb(float h, float s, float b, float* rgb)
-{
-    float red = 0.0f;
-    float green = 0.0f;
-    float blue = 0.0f;
-
-    float x = h;
-    float y = s;
-    float z = b;
-
-    float hf = (x - (int) x) * 6.0f;
-    int ihf = (int) hf;
-    float f = hf - ihf;
-    float pv = z * (1.0f - y);
-    float qv = z * (1.0f - y * f);
-    float tv = z * (1.0f - y * (1.0f - f));
-
-    switch (ihf) {
-        case 0:         // Red is the dominant color
-            red = z;
-            green = tv;
-            blue = pv;
-            break;
-        case 1:         // Green is the dominant color
-            red = qv;
-            green = z;
-            blue = pv;
-            break;
-        case 2:
-            red = pv;
-            green = z;
-            blue = tv;
-            break;
-        case 3:         // Blue is the dominant color
-            red = pv;
-            green = qv;
-            blue = z;
-            break;
-        case 4:
-            red = tv;
-            green = pv;
-            blue = z;
-            break;
-        case 5:         // Red is the dominant color
-            red = z;
-            green = pv;
-            blue = qv;
-            break;
+    if (vdst[0]) {
+        CHECK_OBJ(vdst[0]);
+        static_cast<ObjectBase *>(vdst[0])->decSysRef();
     }
-
-    rgb[0] = red;
-    rgb[1] = green;
-    rgb[2] = blue;
+    *vdst = vsrc;
+    //LOGE("SC_setObject *");
 }
-
-static int SC_hsbToAbgr(float h, float s, float b, float a)
-{
-    float rgb[3];
-    SC_hsbToRgb(h, s, b, rgb);
-    return int(a      * 255.0f) << 24 |
-           int(rgb[2] * 255.0f) << 16 |
-           int(rgb[1] * 255.0f) <<  8 |
-           int(rgb[0] * 255.0f);
-}
-
-static void SC_hsb(float h, float s, float b, float a)
-{
-    GET_TLS();
-    float rgb[3];
-    SC_hsbToRgb(h, s, b, rgb);
-    if (rsc->checkVersion2_0()) {
-        glVertexAttrib4f(1, rgb[0], rgb[1], rgb[2], a);
-    } else {
-        glColor4f(rgb[0], rgb[1], rgb[2], a);
+static void SC_clearObject(void **vdst) {
+    //LOGE("SC_clearObject  %p,%p", vdst, *vdst);
+    if (vdst[0]) {
+        CHECK_OBJ(vdst[0]);
+        static_cast<ObjectBase *>(vdst[0])->decSysRef();
     }
+    *vdst = NULL;
+    //LOGE("SC_clearObject *");
+}
+static bool SC_isObject(RsAllocation vsrc) {
+    return vsrc != NULL;
 }
 
-static void SC_uploadToTexture(RsAllocation va, uint32_t baseMipLevel)
+
+
+static void SC_debugF(const char *s, float f) {
+    LOGE("%s %f, 0x%08x", s, f, *((int *) (&f)));
+}
+static void SC_debugFv2(const char *s, float f1, float f2) {
+    LOGE("%s {%f, %f}", s, f1, f2);
+}
+static void SC_debugFv3(const char *s, float f1, float f2, float f3) {
+    LOGE("%s {%f, %f, %f}", s, f1, f2, f3);
+}
+static void SC_debugFv4(const char *s, float f1, float f2, float f3, float f4) {
+    LOGE("%s {%f, %f, %f, %f}", s, f1, f2, f3, f4);
+}
+static void SC_debugD(const char *s, double d) {
+    LOGE("%s %f, 0x%08llx", s, d, *((long long *) (&d)));
+}
+static void SC_debugFM4v4(const char *s, const float *f) {
+    LOGE("%s {%f, %f, %f, %f", s, f[0], f[4], f[8], f[12]);
+    LOGE("%s  %f, %f, %f, %f", s, f[1], f[5], f[9], f[13]);
+    LOGE("%s  %f, %f, %f, %f", s, f[2], f[6], f[10], f[14]);
+    LOGE("%s  %f, %f, %f, %f}", s, f[3], f[7], f[11], f[15]);
+}
+static void SC_debugFM3v3(const char *s, const float *f) {
+    LOGE("%s {%f, %f, %f", s, f[0], f[3], f[6]);
+    LOGE("%s  %f, %f, %f", s, f[1], f[4], f[7]);
+    LOGE("%s  %f, %f, %f}",s, f[2], f[5], f[8]);
+}
+static void SC_debugFM2v2(const char *s, const float *f) {
+    LOGE("%s {%f, %f", s, f[0], f[2]);
+    LOGE("%s  %f, %f}",s, f[1], f[3]);
+}
+
+static void SC_debugI32(const char *s, int32_t i) {
+    LOGE("%s %i  0x%x", s, i, i);
+}
+static void SC_debugU32(const char *s, uint32_t i) {
+    LOGE("%s %u  0x%x", s, i, i);
+}
+static void SC_debugLL64(const char *s, long long ll) {
+    LOGE("%s %lld  0x%llx", s, ll, ll);
+}
+static void SC_debugULL64(const char *s, unsigned long long ll) {
+    LOGE("%s %llu  0x%llx", s, ll, ll);
+}
+
+static void SC_debugP(const char *s, const void *p) {
+    LOGE("%s %p", s, p);
+}
+
+static uint32_t SC_toClient2(int cmdID, void *data, int len)
 {
     GET_TLS();
-    rsi_AllocationUploadToTexture(rsc, va, false, baseMipLevel);
+    //LOGE("SC_toClient %i %i %i", cmdID, len);
+    return rsc->sendMessageToClient(data, cmdID, len, false);
 }
 
-static void SC_uploadToBufferObject(RsAllocation va)
+static uint32_t SC_toClient(int cmdID)
 {
     GET_TLS();
-    rsi_AllocationUploadToBufferObject(rsc, va);
+    //LOGE("SC_toClient %i", cmdID);
+    return rsc->sendMessageToClient(NULL, cmdID, 0, false);
 }
 
-static void SC_syncToGL(RsAllocation va)
+static uint32_t SC_toClientBlocking2(int cmdID, void *data, int len)
 {
     GET_TLS();
-    Allocation *a = static_cast<Allocation *>(va);
-
+    //LOGE("SC_toClientBlocking %i %i", cmdID, len);
+    return rsc->sendMessageToClient(data, cmdID, len, true);
 }
 
-static void SC_ClearColor(float r, float g, float b, float a)
-{
-    //LOGE("c %f %f %f %f", r, g, b, a);
-    GET_TLS();
-    sc->mEnviroment.mClearColor[0] = r;
-    sc->mEnviroment.mClearColor[1] = g;
-    sc->mEnviroment.mClearColor[2] = b;
-    sc->mEnviroment.mClearColor[3] = a;
-}
-
-static void SC_debugF(const char *s, float f)
-{
-    LOGE("%s %f", s, f);
-}
-
-static void SC_debugHexF(const char *s, float f)
-{
-    LOGE("%s 0x%x", s, *((int *) (&f)));
-}
-
-static void SC_debugI32(const char *s, int32_t i)
-{
-    LOGE("%s %i", s, i);
-}
-
-static void SC_debugHexI32(const char *s, int32_t i)
-{
-    LOGE("%s 0x%x", s, i);
-}
-
-static uint32_t SC_getWidth()
+static uint32_t SC_toClientBlocking(int cmdID)
 {
     GET_TLS();
-    return rsc->getWidth();
+    //LOGE("SC_toClientBlocking %i", cmdID);
+    return rsc->sendMessageToClient(NULL, cmdID, 0, true);
 }
 
-static uint32_t SC_getHeight()
+int SC_divsi3(int a, int b)
+{
+    return a / b;
+}
+
+int SC_getAllocation(const void *ptr)
 {
     GET_TLS();
-    return rsc->getHeight();
+    const Allocation *alloc = sc->ptrToAllocation(ptr);
+    return (int)alloc;
 }
 
-static uint32_t SC_colorFloatRGBAtoUNorm8(float r, float g, float b, float a)
+void SC_allocationMarkDirty(RsAllocation a)
 {
-    uint32_t c = 0;
-    c |= (uint32_t)(r * 255.f + 0.5f);
-    c |= ((uint32_t)(g * 255.f + 0.5f)) << 8;
-    c |= ((uint32_t)(b * 255.f + 0.5f)) << 16;
-    c |= ((uint32_t)(a * 255.f + 0.5f)) << 24;
-    return c;
+    Allocation *alloc = static_cast<Allocation *>(a);
+    alloc->sendDirty();
 }
 
-static uint32_t SC_colorFloatRGBAto565(float r, float g, float b)
-{
-    uint32_t ir = (uint32_t)(r * 255.f + 0.5f);
-    uint32_t ig = (uint32_t)(g * 255.f + 0.5f);
-    uint32_t ib = (uint32_t)(b * 255.f + 0.5f);
-    return rs888to565(ir, ig, ib);
-}
-
-static uint32_t SC_toClient(void *data, int cmdID, int len, int waitForSpace)
+void SC_ForEach(RsScript vs,
+                RsAllocation vin,
+                RsAllocation vout,
+                const void *usr)
 {
     GET_TLS();
-    return rsc->sendMessageToClient(data, cmdID, len, waitForSpace != 0);
+    const Allocation *ain = static_cast<const Allocation *>(vin);
+    Allocation *aout = static_cast<Allocation *>(vout);
+    Script *s = static_cast<Script *>(vs);
+    s->runForEach(rsc, ain, aout, usr);
 }
 
-static void SC_scriptCall(int scriptID)
+void SC_ForEach2(RsScript vs,
+                RsAllocation vin,
+                RsAllocation vout,
+                const void *usr,
+                const RsScriptCall *call)
 {
     GET_TLS();
-    rsc->runScript((Script *)scriptID, 0);
+    const Allocation *ain = static_cast<const Allocation *>(vin);
+    Allocation *aout = static_cast<Allocation *>(vout);
+    Script *s = static_cast<Script *>(vs);
+    s->runForEach(rsc, ain, aout, usr, call);
 }
 
-
 //////////////////////////////////////////////////////////////////////////////
 // Class implementation
 //////////////////////////////////////////////////////////////////////////////
 
-ScriptCState::SymbolTable_t ScriptCState::gSyms[] = {
-    // IO
-    { "loadI32", (void *)&SC_loadI32,
-        "int", "(int, int)" },
-    //{ "loadU32", (void *)&SC_loadU32, "unsigned int", "(int, int)" },
-    { "loadF", (void *)&SC_loadF,
-        "float", "(int, int)" },
-    { "loadArrayF", (void *)&SC_loadArrayF,
-        "float*", "(int, int)" },
-    { "loadArrayI32", (void *)&SC_loadArrayI32,
-        "int*", "(int, int)" },
-    { "loadVec4", (void *)&SC_loadVec4,
-        "void", "(int, int, float *)" },
-    { "loadMatrix", (void *)&SC_loadMatrix,
-        "void", "(int, int, float *)" },
-    { "storeI32", (void *)&SC_storeI32,
-        "void", "(int, int, int)" },
-    //{ "storeU32", (void *)&SC_storeU32, "void", "(int, int, unsigned int)" },
-    { "storeF", (void *)&SC_storeF,
-        "void", "(int, int, float)" },
-    { "storeVec4", (void *)&SC_storeVec4,
-        "void", "(int, int, float *)" },
-    { "storeMatrix", (void *)&SC_storeMatrix,
-        "void", "(int, int, float *)" },
-    { "loadSimpleMeshVerticesF", (void *)&SC_loadSimpleMeshVerticesF,
-        "float*", "(int, int)" },
-    { "updateSimpleMesh", (void *)&SC_updateSimpleMesh,
-        "void", "(int)" },
+// llvm name mangling ref
+//  <builtin-type> ::= v  # void
+//                 ::= b  # bool
+//                 ::= c  # char
+//                 ::= a  # signed char
+//                 ::= h  # unsigned char
+//                 ::= s  # short
+//                 ::= t  # unsigned short
+//                 ::= i  # int
+//                 ::= j  # unsigned int
+//                 ::= l  # long
+//                 ::= m  # unsigned long
+//                 ::= x  # long long, __int64
+//                 ::= y  # unsigned long long, __int64
+//                 ::= f  # float
+//                 ::= d  # double
 
-    // math
-    { "modf", (void *)&fmod,
-        "float", "(float, float)" },
-    { "abs", (void *)&abs,
-        "int", "(int)" },
-    { "absf", (void *)&fabsf,
-        "float", "(float)" },
-    { "sinf_fast", (void *)&SC_sinf_fast,
-        "float", "(float)" },
-    { "cosf_fast", (void *)&SC_cosf_fast,
-        "float", "(float)" },
-    { "sinf", (void *)&sinf,
-        "float", "(float)" },
-    { "cosf", (void *)&cosf,
-        "float", "(float)" },
-    { "asinf", (void *)&asinf,
-        "float", "(float)" },
-    { "acosf", (void *)&acosf,
-        "float", "(float)" },
-    { "atanf", (void *)&atanf,
-        "float", "(float)" },
-    { "atan2f", (void *)&atan2f,
-        "float", "(float, float)" },
-    { "fabsf", (void *)&fabsf,
-        "float", "(float)" },
-    { "randf", (void *)&SC_randf,
-        "float", "(float)" },
-    { "randf2", (void *)&SC_randf2,
-        "float", "(float, float)" },
-    { "floorf", (void *)&floorf,
-        "float", "(float)" },
-    { "fracf", (void *)&SC_fracf,
-        "float", "(float)" },
-    { "ceilf", (void *)&ceilf,
-        "float", "(float)" },
-    { "roundf", (void *)&SC_roundf,
-        "float", "(float)" },
-    { "expf", (void *)&expf,
-        "float", "(float)" },
-    { "logf", (void *)&logf,
-        "float", "(float)" },
-    { "powf", (void *)&powf,
-        "float", "(float, float)" },
-    { "maxf", (void *)&SC_maxf,
-        "float", "(float, float)" },
-    { "minf", (void *)&SC_minf,
-        "float", "(float, float)" },
-    { "sqrt", (void *)&sqrt,
-        "int", "(int)" },
-    { "sqrtf", (void *)&sqrtf,
-        "float", "(float)" },
-    { "sqr", (void *)&SC_sqr,
-        "int", "(int)" },
-    { "sqrf", (void *)&SC_sqrf,
-        "float", "(float)" },
-    { "sign", (void *)&SC_sign,
-        "int", "(int)" },
-    { "signf", (void *)&SC_signf,
-        "float", "(float)" },
-    { "clamp", (void *)&SC_clamp,
-        "int", "(int, int, int)" },
-    { "clampf", (void *)&SC_clampf,
-        "float", "(float, float, float)" },
-    { "distf2", (void *)&SC_distf2,
-        "float", "(float, float, float, float)" },
-    { "distf3", (void *)&SC_distf3,
-        "float", "(float, float, float, float, float, float)" },
-    { "magf2", (void *)&SC_magf2,
-        "float", "(float, float)" },
-    { "magf3", (void *)&SC_magf3,
-        "float", "(float, float, float)" },
-    { "radf", (void *)&SC_radf,
-        "float", "(float)" },
-    { "degf", (void *)&SC_degf,
-        "float", "(float)" },
-    { "lerpf", (void *)&SC_lerpf,
-        "float", "(float, float, float)" },
-    { "normf", (void *)&SC_normf,
-        "float", "(float, float, float)" },
-    { "mapf", (void *)&SC_mapf,
-        "float", "(float, float, float, float, float)" },
-    { "noisef", (void *)&SC_noisef,
-        "float", "(float)" },
-    { "noisef2", (void *)&SC_noisef2,
-        "float", "(float, float)" },
-    { "noisef3", (void *)&SC_noisef3,
-        "float", "(float, float, float)" },
-    { "turbulencef2", (void *)&SC_turbulencef2,
-        "float", "(float, float, float)" },
-    { "turbulencef3", (void *)&SC_turbulencef3,
-        "float", "(float, float, float, float)" },
+static ScriptCState::SymbolTable_t gSyms[] = {
+    { "__divsi3", (void *)&SC_divsi3, true },
+
+    // allocation
+    { "_Z19rsAllocationGetDimX13rs_allocation", (void *)&SC_allocGetDimX, true },
+    { "_Z19rsAllocationGetDimY13rs_allocation", (void *)&SC_allocGetDimY, true },
+    { "_Z19rsAllocationGetDimZ13rs_allocation", (void *)&SC_allocGetDimZ, true },
+    { "_Z21rsAllocationGetDimLOD13rs_allocation", (void *)&SC_allocGetDimLOD, true },
+    { "_Z23rsAllocationGetDimFaces13rs_allocation", (void *)&SC_allocGetDimFaces, true },
+    { "_Z15rsGetAllocationPKv", (void *)&SC_getAllocation, true },
+
+    { "_Z14rsGetElementAt13rs_allocationj", (void *)&SC_getElementAtX, true },
+    { "_Z14rsGetElementAt13rs_allocationjj", (void *)&SC_getElementAtXY, true },
+    { "_Z14rsGetElementAt13rs_allocationjjj", (void *)&SC_getElementAtXYZ, true },
+
+    { "_Z11rsSetObjectP10rs_elementS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP10rs_element", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject10rs_element", (void *)&SC_isObject, true },
+
+    { "_Z11rsSetObjectP7rs_typeS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP7rs_type", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject7rs_type", (void *)&SC_isObject, true },
+
+    { "_Z11rsSetObjectP13rs_allocationS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP13rs_allocation", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject13rs_allocation", (void *)&SC_isObject, true },
+
+    { "_Z11rsSetObjectP10rs_samplerS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP10rs_sampler", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject10rs_sampler", (void *)&SC_isObject, true },
+
+    { "_Z11rsSetObjectP9rs_scriptS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP9rs_script", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject9rs_script", (void *)&SC_isObject, true },
+
+    { "_Z11rsSetObjectP7rs_meshS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP7rs_mesh", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject7rs_mesh", (void *)&SC_isObject, true },
+
+    { "_Z11rsSetObjectP19rs_program_fragmentS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP19rs_program_fragment", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject19rs_program_fragment", (void *)&SC_isObject, true },
+
+    { "_Z11rsSetObjectP17rs_program_vertexS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP17rs_program_vertex", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject17rs_program_vertex", (void *)&SC_isObject, true },
+
+    { "_Z11rsSetObjectP17rs_program_rasterS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP17rs_program_raster", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject17rs_program_raster", (void *)&SC_isObject, true },
+
+    { "_Z11rsSetObjectP16rs_program_storeS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP16rs_program_store", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject16rs_program_store", (void *)&SC_isObject, true },
+
+    { "_Z11rsSetObjectP7rs_fontS_", (void *)&SC_setObject, true },
+    { "_Z13rsClearObjectP7rs_font", (void *)&SC_clearObject, true },
+    { "_Z10rsIsObject7rs_font", (void *)&SC_isObject, true },
+
+
+    { "_Z21rsAllocationMarkDirty13rs_allocation", (void *)&SC_allocationMarkDirty, true },
+
+
+    // Debug
+    { "_Z7rsDebugPKcf", (void *)&SC_debugF, true },
+    { "_Z7rsDebugPKcff", (void *)&SC_debugFv2, true },
+    { "_Z7rsDebugPKcfff", (void *)&SC_debugFv3, true },
+    { "_Z7rsDebugPKcffff", (void *)&SC_debugFv4, true },
+    { "_Z7rsDebugPKcd", (void *)&SC_debugD, true },
+    { "_Z7rsDebugPKcPK12rs_matrix4x4", (void *)&SC_debugFM4v4, true },
+    { "_Z7rsDebugPKcPK12rs_matrix3x3", (void *)&SC_debugFM3v3, true },
+    { "_Z7rsDebugPKcPK12rs_matrix2x2", (void *)&SC_debugFM2v2, true },
+    { "_Z7rsDebugPKci", (void *)&SC_debugI32, true },
+    { "_Z7rsDebugPKcj", (void *)&SC_debugU32, true },
+    // Both "long" and "unsigned long" need to be redirected to their
+    // 64-bit counterparts, since we have hacked Slang to use 64-bit
+    // for "long" on Arm (to be similar to Java).
+    { "_Z7rsDebugPKcl", (void *)&SC_debugLL64, true },
+    { "_Z7rsDebugPKcm", (void *)&SC_debugULL64, true },
+    { "_Z7rsDebugPKcx", (void *)&SC_debugLL64, true },
+    { "_Z7rsDebugPKcy", (void *)&SC_debugULL64, true },
+    { "_Z7rsDebugPKcPKv", (void *)&SC_debugP, true },
+
+    // RS Math
+    { "_Z6rsRandi", (void *)&SC_randi, true },
+    { "_Z6rsRandii", (void *)&SC_randi2, true },
+    { "_Z6rsRandf", (void *)&SC_randf, true },
+    { "_Z6rsRandff", (void *)&SC_randf2, true },
+    { "_Z6rsFracf", (void *)&SC_frac, true },
 
     // time
-    { "second", (void *)&SC_second,
-        "int", "()" },
-    { "minute", (void *)&SC_minute,
-        "int", "()" },
-    { "hour", (void *)&SC_hour,
-        "int", "()" },
-    { "day", (void *)&SC_day,
-        "int", "()" },
-    { "month", (void *)&SC_month,
-        "int", "()" },
-    { "year", (void *)&SC_year,
-        "int", "()" },
-    { "uptimeMillis", (void*)&SC_uptimeMillis,
-        "int", "()" },      // TODO: use long instead
-    { "startTimeMillis", (void*)&SC_startTimeMillis,
-        "int", "()" },      // TODO: use long instead
-    { "elapsedTimeMillis", (void*)&SC_elapsedTimeMillis,
-        "int", "()" },      // TODO: use long instead
+    { "_Z8rsSecondv", (void *)&SC_second, true },
+    { "_Z8rsMinutev", (void *)&SC_minute, true },
+    { "_Z6rsHourv", (void *)&SC_hour, true },
+    { "_Z5rsDayv", (void *)&SC_day, true },
+    { "_Z7rsMonthv", (void *)&SC_month, true },
+    { "_Z6rsYearv", (void *)&SC_year, true },
+    { "_Z14rsUptimeMillisv", (void*)&SC_uptimeMillis, true },
+    { "_Z13rsUptimeNanosv", (void*)&SC_uptimeNanos, true },
+    { "_Z7rsGetDtv", (void*)&SC_getDt, false },
 
-    // matrix
-    { "matrixLoadIdentity", (void *)&SC_matrixLoadIdentity,
-        "void", "(float *mat)" },
-    { "matrixLoadFloat", (void *)&SC_matrixLoadFloat,
-        "void", "(float *mat, float *f)" },
-    { "matrixLoadMat", (void *)&SC_matrixLoadMat,
-        "void", "(float *mat, float *newmat)" },
-    { "matrixLoadRotate", (void *)&SC_matrixLoadRotate,
-        "void", "(float *mat, float rot, float x, float y, float z)" },
-    { "matrixLoadScale", (void *)&SC_matrixLoadScale,
-        "void", "(float *mat, float x, float y, float z)" },
-    { "matrixLoadTranslate", (void *)&SC_matrixLoadTranslate,
-        "void", "(float *mat, float x, float y, float z)" },
-    { "matrixLoadMultiply", (void *)&SC_matrixLoadMultiply,
-        "void", "(float *mat, float *lhs, float *rhs)" },
-    { "matrixMultiply", (void *)&SC_matrixMultiply,
-        "void", "(float *mat, float *rhs)" },
-    { "matrixRotate", (void *)&SC_matrixRotate,
-        "void", "(float *mat, float rot, float x, float y, float z)" },
-    { "matrixScale", (void *)&SC_matrixScale,
-        "void", "(float *mat, float x, float y, float z)" },
-    { "matrixTranslate", (void *)&SC_matrixTranslate,
-        "void", "(float *mat, float x, float y, float z)" },
+    { "_Z14rsSendToClienti", (void *)&SC_toClient, false },
+    { "_Z14rsSendToClientiPKvj", (void *)&SC_toClient2, false },
+    { "_Z22rsSendToClientBlockingi", (void *)&SC_toClientBlocking, false },
+    { "_Z22rsSendToClientBlockingiPKvj", (void *)&SC_toClientBlocking2, false },
 
-    // vector
-    { "vec2Rand", (void *)&SC_vec2Rand,
-        "void", "(float *vec, float maxLen)" },
+    { "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach, false },
+    //{ "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach2, true },
 
-    // vec3
-    { "vec3Norm", (void *)&SC_vec3Norm,
-        "void", "(struct vecF32_3_s *)" },
-    { "vec3Length", (void *)&SC_vec3Length,
-        "float", "(struct vecF32_3_s *)" },
-    { "vec3Add", (void *)&SC_vec3Add,
-        "void", "(struct vecF32_3_s *dest, struct vecF32_3_s *lhs, struct vecF32_3_s *rhs)" },
-    { "vec3Sub", (void *)&SC_vec3Sub,
-        "void", "(struct vecF32_3_s *dest, struct vecF32_3_s *lhs, struct vecF32_3_s *rhs)" },
-    { "vec3Cross", (void *)&SC_vec3Cross,
-        "void", "(struct vecF32_3_s *dest, struct vecF32_3_s *lhs, struct vecF32_3_s *rhs)" },
-    { "vec3Dot", (void *)&SC_vec3Dot,
-        "float", "(struct vecF32_3_s *lhs, struct vecF32_3_s *rhs)" },
-    { "vec3Scale", (void *)&SC_vec3Scale,
-        "void", "(struct vecF32_3_s *lhs, float scale)" },
+////////////////////////////////////////////////////////////////////
 
-    // vec4
-    { "vec4Norm", (void *)&SC_vec4Norm,
-        "void", "(struct vecF32_4_s *)" },
-    { "vec4Length", (void *)&SC_vec4Length,
-        "float", "(struct vecF32_4_s *)" },
-    { "vec4Add", (void *)&SC_vec4Add,
-        "void", "(struct vecF32_4_s *dest, struct vecF32_4_s *lhs, struct vecF32_4_s *rhs)" },
-    { "vec4Sub", (void *)&SC_vec4Sub,
-        "void", "(struct vecF32_4_s *dest, struct vecF32_4_s *lhs, struct vecF32_4_s *rhs)" },
-    { "vec4Dot", (void *)&SC_vec4Dot,
-        "float", "(struct vecF32_4_s *lhs, struct vecF32_4_s *rhs)" },
-    { "vec4Scale", (void *)&SC_vec4Scale,
-        "void", "(struct vecF32_4_s *lhs, float scale)" },
+    //{ "sinf_fast", (void *)&SC_sinf_fast, true },
+    //{ "cosf_fast", (void *)&SC_cosf_fast, true },
 
-    // context
-    { "bindProgramFragment", (void *)&SC_bindProgramFragment,
-        "void", "(int)" },
-    { "bindProgramFragmentStore", (void *)&SC_bindProgramFragmentStore,
-        "void", "(int)" },
-    { "bindProgramStore", (void *)&SC_bindProgramFragmentStore,
-        "void", "(int)" },
-    { "bindProgramVertex", (void *)&SC_bindProgramVertex,
-        "void", "(int)" },
-    { "bindSampler", (void *)&SC_bindSampler,
-        "void", "(int, int, int)" },
-    { "bindTexture", (void *)&SC_bindTexture,
-        "void", "(int, int, int)" },
-
-    // vp
-    { "vpLoadModelMatrix", (void *)&SC_vpLoadModelMatrix,
-        "void", "(void *)" },
-    { "vpLoadTextureMatrix", (void *)&SC_vpLoadTextureMatrix,
-        "void", "(void *)" },
-
-
-
-    // drawing
-    { "drawRect", (void *)&SC_drawRect,
-        "void", "(float x1, float y1, float x2, float y2, float z)" },
-    { "drawQuad", (void *)&SC_drawQuad,
-        "void", "(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4)" },
-    { "drawQuadTexCoords", (void *)&SC_drawQuadTexCoords,
-        "void", "(float x1, float y1, float z1, float u1, float v1, float x2, float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, float x4, float y4, float z4, float u4, float v4)" },
-    { "drawSprite", (void *)&SC_drawSprite,
-        "void", "(float x, float y, float z, float w, float h)" },
-    { "drawSpriteScreenspace", (void *)&SC_drawSpriteScreenspace,
-        "void", "(float x, float y, float z, float w, float h)" },
-    { "drawSpriteScreenspaceCropped", (void *)&SC_drawSpriteScreenspaceCropped,
-        "void", "(float x, float y, float z, float w, float h, float cx0, float cy0, float cx1, float cy1)" },
-    { "drawLine", (void *)&SC_drawLine,
-        "void", "(float x1, float y1, float z1, float x2, float y2, float z2)" },
-    { "drawPoint", (void *)&SC_drawPoint,
-        "void", "(float x1, float y1, float z1)" },
-    { "drawSimpleMesh", (void *)&SC_drawSimpleMesh,
-        "void", "(int ism)" },
-    { "drawSimpleMeshRange", (void *)&SC_drawSimpleMeshRange,
-        "void", "(int ism, int start, int len)" },
-
-
-    // misc
-    { "pfClearColor", (void *)&SC_ClearColor,
-        "void", "(float, float, float, float)" },
-    { "color", (void *)&SC_color,
-        "void", "(float, float, float, float)" },
-    { "hsb", (void *)&SC_hsb,
-        "void", "(float, float, float, float)" },
-    { "hsbToRgb", (void *)&SC_hsbToRgb,
-        "void", "(float, float, float, float*)" },
-    { "hsbToAbgr", (void *)&SC_hsbToAbgr,
-        "int", "(float, float, float, float)" },
-    { "ambient", (void *)&SC_ambient,
-        "void", "(float, float, float, float)" },
-    { "diffuse", (void *)&SC_diffuse,
-        "void", "(float, float, float, float)" },
-    { "specular", (void *)&SC_specular,
-        "void", "(float, float, float, float)" },
-    { "emission", (void *)&SC_emission,
-        "void", "(float, float, float, float)" },
-    { "shininess", (void *)&SC_shininess,
-        "void", "(float)" },
-    { "pointAttenuation", (void *)&SC_pointAttenuation,
-        "void", "(float, float, float)" },
-
-    { "uploadToTexture", (void *)&SC_uploadToTexture,
-        "void", "(int, int)" },
-    { "uploadToBufferObject", (void *)&SC_uploadToBufferObject,
-        "void", "(int)" },
-
-    { "syncToGL", (void *)&SC_syncToGL,
-        "void", "(int)" },
-
-    { "colorFloatRGBAtoUNorm8", (void *)&SC_colorFloatRGBAtoUNorm8,
-        "int", "(float, float, float, float)" },
-    { "colorFloatRGBto565", (void *)&SC_colorFloatRGBAto565,
-        "int", "(float, float, float)" },
-
-
-    { "getWidth", (void *)&SC_getWidth,
-        "int", "()" },
-    { "getHeight", (void *)&SC_getHeight,
-        "int", "()" },
-
-    { "sendToClient", (void *)&SC_toClient,
-        "int", "(void *data, int cmdID, int len, int waitForSpace)" },
-
-
-    { "debugF", (void *)&SC_debugF,
-        "void", "(void *, float)" },
-    { "debugI32", (void *)&SC_debugI32,
-        "void", "(void *, int)" },
-    { "debugHexF", (void *)&SC_debugHexF,
-        "void", "(void *, float)" },
-    { "debugHexI32", (void *)&SC_debugHexI32,
-        "void", "(void *, int)" },
-
-    { "scriptCall", (void *)&SC_scriptCall,
-        "void", "(int)" },
-
-
-    { NULL, NULL, NULL, NULL }
+    { NULL, NULL, false }
 };
 
 const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbol(const char *sym)
@@ -1400,17 +565,3 @@
     return NULL;
 }
 
-void ScriptCState::appendDecls(String8 *str)
-{
-    ScriptCState::SymbolTable_t *syms = gSyms;
-    while (syms->mPtr) {
-        str->append(syms->mRet);
-        str->append(" ");
-        str->append(syms->mName);
-        str->append(syms->mParam);
-        str->append(";\n");
-        syms++;
-    }
-}
-
-
diff --git a/libs/rs/rsScriptC_LibCL.cpp b/libs/rs/rsScriptC_LibCL.cpp
new file mode 100644
index 0000000..7031f84
--- /dev/null
+++ b/libs/rs/rsScriptC_LibCL.cpp
@@ -0,0 +1,309 @@
+/*
+ * 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.
+ */
+
+#include "rsContext.h"
+#include "rsScriptC.h"
+
+// Implements rs_cl.rsh
+
+
+using namespace android;
+using namespace android::renderscript;
+
+
+static float SC_acospi(float v) {
+    return acosf(v)/ M_PI;
+}
+
+static float SC_asinpi(float v) {
+    return asinf(v) / M_PI;
+}
+
+static float SC_atanpi(float v) {
+    return atanf(v) / M_PI;
+}
+
+static float SC_atan2pi(float y, float x) {
+    return atan2f(y, x) / M_PI;
+}
+
+static float SC_cospi(float v) {
+    return cosf(v * M_PI);
+}
+
+static float SC_exp10(float v) {
+    return pow(10.f, v);
+
+}
+
+static float SC_fract(float v, int *iptr) {
+    int i = (int)floor(v);
+    iptr[0] = i;
+    return fmin(v - i, 0x1.fffffep-1f);
+}
+
+static float SC_log2(float v) {
+    return log10(v) / log10(2.f);
+}
+
+static float SC_pown(float v, int p) {
+    return powf(v, (float)p);
+}
+
+static float SC_powr(float v, float p) {
+    return powf(v, p);
+}
+
+float SC_rootn(float v, int r) {
+    return pow(v, 1.f / r);
+}
+
+float SC_rsqrt(float v) {
+    return 1.f / sqrtf(v);
+}
+
+float SC_sincos(float v, float *cosptr) {
+    *cosptr = cosf(v);
+    return sinf(v);
+}
+
+static float SC_sinpi(float v) {
+    return sinf(v * M_PI);
+}
+
+static float SC_tanpi(float v) {
+    return tanf(v * M_PI);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Integer
+//////////////////////////////////////////////////////////////////////////////
+
+
+static uint32_t SC_abs_i32(int32_t v) {return abs(v);}
+static uint16_t SC_abs_i16(int16_t v) {return (uint16_t)abs(v);}
+static uint8_t SC_abs_i8(int8_t v) {return (uint8_t)abs(v);}
+
+static uint32_t SC_clz_u32(uint32_t v) {return __builtin_clz(v);}
+static uint16_t SC_clz_u16(uint16_t v) {return (uint16_t)__builtin_clz(v);}
+static uint8_t SC_clz_u8(uint8_t v) {return (uint8_t)__builtin_clz(v);}
+static int32_t SC_clz_i32(int32_t v) {return (int32_t)__builtin_clz((uint32_t)v);}
+static int16_t SC_clz_i16(int16_t v) {return (int16_t)__builtin_clz(v);}
+static int8_t SC_clz_i8(int8_t v) {return (int8_t)__builtin_clz(v);}
+
+static uint32_t SC_max_u32(uint32_t v, uint32_t v2) {return rsMax(v, v2);}
+static uint16_t SC_max_u16(uint16_t v, uint16_t v2) {return rsMax(v, v2);}
+static uint8_t SC_max_u8(uint8_t v, uint8_t v2) {return rsMax(v, v2);}
+static int32_t SC_max_i32(int32_t v, int32_t v2) {return rsMax(v, v2);}
+static int16_t SC_max_i16(int16_t v, int16_t v2) {return rsMax(v, v2);}
+static int8_t SC_max_i8(int8_t v, int8_t v2) {return rsMax(v, v2);}
+
+static uint32_t SC_min_u32(uint32_t v, uint32_t v2) {return rsMin(v, v2);}
+static uint16_t SC_min_u16(uint16_t v, uint16_t v2) {return rsMin(v, v2);}
+static uint8_t SC_min_u8(uint8_t v, uint8_t v2) {return rsMin(v, v2);}
+static int32_t SC_min_i32(int32_t v, int32_t v2) {return rsMin(v, v2);}
+static int16_t SC_min_i16(int16_t v, int16_t v2) {return rsMin(v, v2);}
+static int8_t SC_min_i8(int8_t v, int8_t v2) {return rsMin(v, v2);}
+
+//////////////////////////////////////////////////////////////////////////////
+// Float util
+//////////////////////////////////////////////////////////////////////////////
+
+static float SC_clamp_f32(float amount, float low, float high)
+{
+    return amount < low ? low : (amount > high ? high : amount);
+}
+
+static float SC_degrees(float radians)
+{
+    return radians * (180.f / M_PI);
+}
+
+static float SC_max_f32(float v, float v2)
+{
+    return rsMax(v, v2);
+}
+
+static float SC_min_f32(float v, float v2)
+{
+    return rsMin(v, v2);
+}
+
+static float SC_mix_f32(float start, float stop, float amount)
+{
+    //LOGE("lerpf %f  %f  %f", start, stop, amount);
+    return start + (stop - start) * amount;
+}
+
+static float SC_radians(float degrees)
+{
+    return degrees * (M_PI / 180.f);
+}
+
+static float SC_step_f32(float edge, float v)
+{
+    if (v < edge) return 0.f;
+    return 1.f;
+}
+
+static float SC_sign_f32(float value)
+{
+    if (value > 0) return 1.f;
+    if (value < 0) return -1.f;
+    return value;
+}
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Class implementation
+//////////////////////////////////////////////////////////////////////////////
+
+// llvm name mangling ref
+//  <builtin-type> ::= v  # void
+//                 ::= b  # bool
+//                 ::= c  # char
+//                 ::= a  # signed char
+//                 ::= h  # unsigned char
+//                 ::= s  # short
+//                 ::= t  # unsigned short
+//                 ::= i  # int
+//                 ::= j  # unsigned int
+//                 ::= l  # long
+//                 ::= m  # unsigned long
+//                 ::= x  # long long, __int64
+//                 ::= y  # unsigned long long, __int64
+//                 ::= f  # float
+//                 ::= d  # double
+
+static ScriptCState::SymbolTable_t gSyms[] = {
+    // OpenCL math
+    { "_Z4acosf", (void *)&acosf, true },
+    { "_Z5acoshf", (void *)&acoshf, true },
+    { "_Z6acospif", (void *)&SC_acospi, true },
+    { "_Z4asinf", (void *)&asinf, true },
+    { "_Z5asinhf", (void *)&asinhf, true },
+    { "_Z6asinpif", (void *)&SC_asinpi, true },
+    { "_Z4atanf", (void *)&atanf, true },
+    { "_Z5atan2ff", (void *)&atan2f, true },
+    { "_Z6atanpif", (void *)&SC_atanpi, true },
+    { "_Z7atan2piff", (void *)&SC_atan2pi, true },
+    { "_Z4cbrtf", (void *)&cbrtf, true },
+    { "_Z4ceilf", (void *)&ceilf, true },
+    { "_Z8copysignff", (void *)&copysignf, true },
+    { "_Z3cosf", (void *)&cosf, true },
+    { "_Z4coshf", (void *)&coshf, true },
+    { "_Z5cospif", (void *)&SC_cospi, true },
+    { "_Z4erfcf", (void *)&erfcf, true },
+    { "_Z3erff", (void *)&erff, true },
+    { "_Z3expf", (void *)&expf, true },
+    { "_Z4exp2f", (void *)&exp2f, true },
+    { "_Z5exp10f", (void *)&SC_exp10, true },
+    { "_Z5expm1f", (void *)&expm1f, true },
+    { "_Z4fabsf", (void *)&fabsf, true },
+    { "_Z4fdimff", (void *)&fdimf, true },
+    { "_Z5floorf", (void *)&floorf, true },
+    { "_Z3fmafff", (void *)&fmaf, true },
+    { "_Z4fmaxff", (void *)&fmaxf, true },
+    { "_Z4fminff", (void *)&fminf, true },  // float fmin(float, float)
+    { "_Z4fmodff", (void *)&fmodf, true },
+    { "_Z5fractfPf", (void *)&SC_fract, true },
+    { "_Z5frexpfPi", (void *)&frexpf, true },
+    { "_Z5hypotff", (void *)&hypotf, true },
+    { "_Z5ilogbf", (void *)&ilogbf, true },
+    { "_Z5ldexpfi", (void *)&ldexpf, true },
+    { "_Z6lgammaf", (void *)&lgammaf, true },
+    { "_Z3logf", (void *)&logf, true },
+    { "_Z4log2f", (void *)&SC_log2, true },
+    { "_Z5log10f", (void *)&log10f, true },
+    { "_Z5log1pf", (void *)&log1pf, true },
+    //{ "logb", (void *)&, true },
+    //{ "mad", (void *)&, true },
+    { "modf", (void *)&modff, true },
+    //{ "nan", (void *)&, true },
+    { "_Z9nextafterff", (void *)&nextafterf, true },
+    { "_Z3powff", (void *)&powf, true },
+    { "_Z4pownfi", (void *)&SC_pown, true },
+    { "_Z4powrff", (void *)&SC_powr, true },
+    { "_Z9remainderff", (void *)&remainderf, true },
+    { "remquo", (void *)&remquof, true },
+    { "_Z4rintf", (void *)&rintf, true },
+    { "_Z5rootnfi", (void *)&SC_rootn, true },
+    { "_Z5roundf", (void *)&roundf, true },
+    { "_Z5rsqrtf", (void *)&SC_rsqrt, true },
+    { "_Z3sinf", (void *)&sinf, true },
+    { "sincos", (void *)&SC_sincos, true },
+    { "_Z4sinhf", (void *)&sinhf, true },
+    { "_Z5sinpif", (void *)&SC_sinpi, true },
+    { "_Z4sqrtf", (void *)&sqrtf, true },
+    { "_Z3tanf", (void *)&tanf, true },
+    { "_Z4tanhf", (void *)&tanhf, true },
+    { "_Z5tanpif", (void *)&SC_tanpi, true },
+    //{ "tgamma", (void *)&, true },
+    { "_Z5truncf", (void *)&truncf, true },
+
+    // OpenCL Int
+    { "_Z3absi", (void *)&SC_abs_i32, true },
+    { "_Z3abss", (void *)&SC_abs_i16, true },
+    { "_Z3absc", (void *)&SC_abs_i8, true },
+    { "_Z3clzj", (void *)&SC_clz_u32, true },
+    { "_Z3clzt", (void *)&SC_clz_u16, true },
+    { "_Z3clzh", (void *)&SC_clz_u8, true },
+    { "_Z3clzi", (void *)&SC_clz_i32, true },
+    { "_Z3clzs", (void *)&SC_clz_i16, true },
+    { "_Z3clzc", (void *)&SC_clz_i8, true },
+    { "_Z3maxjj", (void *)&SC_max_u32, true },
+    { "_Z3maxtt", (void *)&SC_max_u16, true },
+    { "_Z3maxhh", (void *)&SC_max_u8, true },
+    { "_Z3maxii", (void *)&SC_max_i32, true },
+    { "_Z3maxss", (void *)&SC_max_i16, true },
+    { "_Z3maxcc", (void *)&SC_max_i8, true },
+    { "_Z3minjj", (void *)&SC_min_u32, true },
+    { "_Z3mintt", (void *)&SC_min_u16, true },
+    { "_Z3minhh", (void *)&SC_min_u8, true },
+    { "_Z3minii", (void *)&SC_min_i32, true },
+    { "_Z3minss", (void *)&SC_min_i16, true },
+    { "_Z3mincc", (void *)&SC_min_i8, true },
+
+    // OpenCL 6.11.4
+    { "_Z5clampfff", (void *)&SC_clamp_f32, true },
+    { "_Z7degreesf", (void *)&SC_degrees, true },
+    { "_Z3maxff", (void *)&SC_max_f32, true },
+    { "_Z3minff", (void *)&SC_min_f32, true },
+    { "_Z3mixfff", (void *)&SC_mix_f32, true },
+    { "_Z7radiansf", (void *)&SC_radians, true },
+    { "_Z4stepff", (void *)&SC_step_f32, true },
+    //{ "smoothstep", (void *)&, true },
+    { "_Z4signf", (void *)&SC_sign_f32, true },
+
+    { NULL, NULL, false }
+};
+
+const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbolCL(const char *sym)
+{
+    ScriptCState::SymbolTable_t *syms = gSyms;
+
+    while (syms->mPtr) {
+        if (!strcmp(syms->mName, sym)) {
+            return syms;
+        }
+        syms++;
+    }
+    return NULL;
+}
+
diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp
new file mode 100644
index 0000000..5b07e7b
--- /dev/null
+++ b/libs/rs/rsScriptC_LibGL.cpp
@@ -0,0 +1,505 @@
+/*
+ * 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.
+ */
+
+#include "rsContext.h"
+#include "rsScriptC.h"
+#include "rsMatrix.h"
+
+#include "utils/Timers.h"
+
+#define GL_GLEXT_PROTOTYPES
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <time.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+#define GET_TLS()  Context::ScriptTLSStruct * tls = \
+    (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \
+    Context * rsc = tls->mContext; \
+    ScriptC * sc = (ScriptC *) tls->mScript
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Context
+//////////////////////////////////////////////////////////////////////////////
+
+static void SC_bindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va)
+{
+    CHECK_OBJ_OR_NULL(va);
+    CHECK_OBJ(vpf);
+    GET_TLS();
+    rsi_ProgramBindTexture(rsc,
+                           static_cast<ProgramFragment *>(vpf),
+                           slot,
+                           static_cast<Allocation *>(va));
+
+}
+
+static void SC_bindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs)
+{
+    CHECK_OBJ_OR_NULL(vs);
+    CHECK_OBJ(vpf);
+    GET_TLS();
+    rsi_ProgramBindSampler(rsc,
+                           static_cast<ProgramFragment *>(vpf),
+                           slot,
+                           static_cast<Sampler *>(vs));
+
+}
+
+static void SC_bindProgramStore(RsProgramStore pfs)
+{
+    CHECK_OBJ_OR_NULL(pfs);
+    GET_TLS();
+    rsi_ContextBindProgramStore(rsc, pfs);
+}
+
+static void SC_bindProgramFragment(RsProgramFragment pf)
+{
+    CHECK_OBJ_OR_NULL(pf);
+    GET_TLS();
+    rsi_ContextBindProgramFragment(rsc, pf);
+}
+
+static void SC_bindProgramVertex(RsProgramVertex pv)
+{
+    CHECK_OBJ_OR_NULL(pv);
+    GET_TLS();
+    rsi_ContextBindProgramVertex(rsc, pv);
+}
+
+static void SC_bindProgramRaster(RsProgramRaster pv)
+{
+    CHECK_OBJ_OR_NULL(pv);
+    GET_TLS();
+    rsi_ContextBindProgramRaster(rsc, pv);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// VP
+//////////////////////////////////////////////////////////////////////////////
+
+static void SC_vpLoadProjectionMatrix(const rsc_Matrix *m)
+{
+    GET_TLS();
+    rsc->getVertex()->setProjectionMatrix(rsc, m);
+}
+
+static void SC_vpLoadModelMatrix(const rsc_Matrix *m)
+{
+    GET_TLS();
+    rsc->getVertex()->setModelviewMatrix(rsc, m);
+}
+
+static void SC_vpLoadTextureMatrix(const rsc_Matrix *m)
+{
+    GET_TLS();
+    rsc->getVertex()->setTextureMatrix(rsc, m);
+}
+
+
+static void SC_pfConstantColor(RsProgramFragment vpf, float r, float g, float b, float a)
+{
+    GET_TLS();
+    CHECK_OBJ(vpf);
+    ProgramFragment *pf = static_cast<ProgramFragment *>(vpf);
+    pf->setConstantColor(rsc, r, g, b, a);
+}
+
+static void SC_vpGetProjectionMatrix(rsc_Matrix *m)
+{
+    GET_TLS();
+    rsc->getVertex()->getProjectionMatrix(rsc, m);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Drawing
+//////////////////////////////////////////////////////////////////////////////
+
+static void SC_drawQuadTexCoords(float x1, float y1, float z1,
+                                 float u1, float v1,
+                                 float x2, float y2, float z2,
+                                 float u2, float v2,
+                                 float x3, float y3, float z3,
+                                 float u3, float v3,
+                                 float x4, float y4, float z4,
+                                 float u4, float v4)
+{
+    GET_TLS();
+    if (!rsc->setupCheck()) {
+        return;
+    }
+
+    //LOGE("Quad");
+    //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1);
+    //LOGE("%4.2f, %4.2f, %4.2f", x2, y2, z2);
+    //LOGE("%4.2f, %4.2f, %4.2f", x3, y3, z3);
+    //LOGE("%4.2f, %4.2f, %4.2f", x4, y4, z4);
+
+    float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4};
+    const float tex[] = {u1,v1, u2,v2, u3,v3, u4,v4};
+
+    VertexArray va;
+    va.add(GL_FLOAT, 3, 12, false, (uint32_t)vtx, "ATTRIB_position");
+    va.add(GL_FLOAT, 2, 8, false, (uint32_t)tex, "ATTRIB_texture0");
+    va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache);
+
+    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+}
+
+static void SC_drawQuad(float x1, float y1, float z1,
+                        float x2, float y2, float z2,
+                        float x3, float y3, float z3,
+                        float x4, float y4, float z4)
+{
+    SC_drawQuadTexCoords(x1, y1, z1, 0, 1,
+                         x2, y2, z2, 1, 1,
+                         x3, y3, z3, 1, 0,
+                         x4, y4, z4, 0, 0);
+}
+
+static void SC_drawSpriteScreenspace(float x, float y, float z, float w, float h)
+{
+    GET_TLS();
+    ObjectBaseRef<const ProgramVertex> tmp(rsc->getVertex());
+    rsc->setVertex(rsc->getDefaultProgramVertex());
+    //rsc->setupCheck();
+
+    //GLint crop[4] = {0, h, w, -h};
+
+    float sh = rsc->getHeight();
+
+    SC_drawQuad(x,   sh - y,     z,
+                x+w, sh - y,     z,
+                x+w, sh - (y+h), z,
+                x,   sh - (y+h), z);
+    rsc->setVertex((ProgramVertex *)tmp.get());
+}
+/*
+static void SC_drawSprite(float x, float y, float z, float w, float h)
+{
+    GET_TLS();
+    float vin[3] = {x, y, z};
+    float vout[4];
+
+    //LOGE("ds  in %f %f %f", x, y, z);
+    rsc->getVertex()->transformToScreen(rsc, vout, vin);
+    //LOGE("ds  out %f %f %f %f", vout[0], vout[1], vout[2], vout[3]);
+    vout[0] /= vout[3];
+    vout[1] /= vout[3];
+    vout[2] /= vout[3];
+
+    vout[0] *= rsc->getWidth() / 2;
+    vout[1] *= rsc->getHeight() / 2;
+    vout[0] += rsc->getWidth() / 2;
+    vout[1] += rsc->getHeight() / 2;
+
+    vout[0] -= w/2;
+    vout[1] -= h/2;
+
+    //LOGE("ds  out2 %f %f %f", vout[0], vout[1], vout[2]);
+
+    // U, V, W, H
+    SC_drawSpriteScreenspace(vout[0], vout[1], z, h, w);
+    //rsc->setupCheck();
+}
+*/
+
+static void SC_drawRect(float x1, float y1,
+                        float x2, float y2, float z)
+{
+    //LOGE("SC_drawRect %f,%f  %f,%f  %f", x1, y1, x2, y2, z);
+    SC_drawQuad(x1, y2, z,
+                x2, y2, z,
+                x2, y1, z,
+                x1, y1, z);
+}
+
+static void SC_drawMesh(RsMesh vsm)
+{
+    CHECK_OBJ(vsm);
+    GET_TLS();
+    Mesh *sm = static_cast<Mesh *>(vsm);
+    if (!rsc->setupCheck()) {
+        return;
+    }
+    sm->render(rsc);
+}
+
+static void SC_drawMeshPrimitive(RsMesh vsm, uint32_t primIndex)
+{
+    CHECK_OBJ(vsm);
+    GET_TLS();
+    Mesh *sm = static_cast<Mesh *>(vsm);
+    if (!rsc->setupCheck()) {
+        return;
+    }
+    sm->renderPrimitive(rsc, primIndex);
+}
+
+static void SC_drawMeshPrimitiveRange(RsMesh vsm, uint32_t primIndex, uint32_t start, uint32_t len)
+{
+    CHECK_OBJ(vsm);
+    GET_TLS();
+    Mesh *sm = static_cast<Mesh *>(vsm);
+    if (!rsc->setupCheck()) {
+        return;
+    }
+    sm->renderPrimitiveRange(rsc, primIndex, start, len);
+}
+
+static void SC_meshComputeBoundingBox(RsMesh vsm, float *minX, float *minY, float *minZ,
+                                                     float *maxX, float *maxY, float *maxZ)
+{
+    CHECK_OBJ(vsm);
+    GET_TLS();
+    Mesh *sm = static_cast<Mesh *>(vsm);
+    sm->computeBBox();
+    *minX = sm->mBBoxMin[0];
+    *minY = sm->mBBoxMin[1];
+    *minZ = sm->mBBoxMin[2];
+    *maxX = sm->mBBoxMax[0];
+    *maxY = sm->mBBoxMax[1];
+    *maxZ = sm->mBBoxMax[2];
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//////////////////////////////////////////////////////////////////////////////
+
+
+static void SC_color(float r, float g, float b, float a)
+{
+    GET_TLS();
+    ProgramFragment *pf = (ProgramFragment *)rsc->getFragment();
+    pf->setConstantColor(rsc, r, g, b, a);
+}
+
+static void SC_uploadToTexture2(RsAllocation va, uint32_t baseMipLevel)
+{
+    CHECK_OBJ(va);
+    GET_TLS();
+    rsi_AllocationUploadToTexture(rsc, va, false, baseMipLevel);
+}
+static void SC_uploadToTexture(RsAllocation va)
+{
+    CHECK_OBJ(va);
+    GET_TLS();
+    rsi_AllocationUploadToTexture(rsc, va, false, 0);
+}
+
+static void SC_uploadToBufferObject(RsAllocation va)
+{
+    CHECK_OBJ(va);
+    GET_TLS();
+    rsi_AllocationUploadToBufferObject(rsc, va);
+}
+
+static void SC_ClearColor(float r, float g, float b, float a)
+{
+    GET_TLS();
+    rsc->setupProgramStore();
+
+    glClearColor(r, g, b, a);
+    glClear(GL_COLOR_BUFFER_BIT);
+}
+
+static void SC_ClearDepth(float v)
+{
+    GET_TLS();
+    rsc->setupProgramStore();
+
+    glClearDepthf(v);
+    glClear(GL_DEPTH_BUFFER_BIT);
+}
+
+static uint32_t SC_getWidth()
+{
+    GET_TLS();
+    return rsc->getWidth();
+}
+
+static uint32_t SC_getHeight()
+{
+    GET_TLS();
+    return rsc->getHeight();
+}
+
+static void SC_DrawTextAlloc(RsAllocation va, int x, int y)
+{
+    CHECK_OBJ(va);
+    GET_TLS();
+    Allocation *alloc = static_cast<Allocation *>(va);
+    const char *text = (const char *)alloc->getPtr();
+    size_t allocSize = alloc->getType()->getSizeBytes();
+    rsc->mStateFont.renderText(text, allocSize, x, y);
+}
+
+static void SC_DrawText(const char *text, int x, int y)
+{
+    GET_TLS();
+    size_t textLen = strlen(text);
+    rsc->mStateFont.renderText(text, textLen, x, y);
+}
+
+static void SC_setMetrics(Font::Rect *metrics,
+                          int32_t *left, int32_t *right,
+                          int32_t *top, int32_t *bottom)
+{
+    if(left) {
+        *left = metrics->left;
+    }
+    if(right) {
+        *right = metrics->right;
+    }
+    if(top) {
+        *top = metrics->top;
+    }
+    if(bottom) {
+        *bottom = metrics->bottom;
+    }
+}
+
+static void SC_MeasureTextAlloc(RsAllocation va,
+                                int32_t *left, int32_t *right,
+                                int32_t *top, int32_t *bottom)
+{
+    CHECK_OBJ(va);
+    GET_TLS();
+    Allocation *alloc = static_cast<Allocation *>(va);
+    const char *text = (const char *)alloc->getPtr();
+    size_t textLen = alloc->getType()->getSizeBytes();
+    Font::Rect metrics;
+    rsc->mStateFont.measureText(text, textLen, &metrics);
+    SC_setMetrics(&metrics, left, right, top, bottom);
+}
+
+static void SC_MeasureText(const char *text,
+                           int32_t *left, int32_t *right,
+                           int32_t *top, int32_t *bottom)
+{
+    GET_TLS();
+    size_t textLen = strlen(text);
+    Font::Rect metrics;
+    rsc->mStateFont.measureText(text, textLen, &metrics);
+    SC_setMetrics(&metrics, left, right, top, bottom);
+}
+
+static void SC_BindFont(RsFont font)
+{
+    CHECK_OBJ(font);
+    GET_TLS();
+    rsi_ContextBindFont(rsc, font);
+}
+
+static void SC_FontColor(float r, float g, float b, float a)
+{
+    GET_TLS();
+    rsc->mStateFont.setFontColor(r, g, b, a);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Class implementation
+//////////////////////////////////////////////////////////////////////////////
+
+// llvm name mangling ref
+//  <builtin-type> ::= v  # void
+//                 ::= b  # bool
+//                 ::= c  # char
+//                 ::= a  # signed char
+//                 ::= h  # unsigned char
+//                 ::= s  # short
+//                 ::= t  # unsigned short
+//                 ::= i  # int
+//                 ::= j  # unsigned int
+//                 ::= l  # long
+//                 ::= m  # unsigned long
+//                 ::= x  # long long, __int64
+//                 ::= y  # unsigned long long, __int64
+//                 ::= f  # float
+//                 ::= d  # double
+
+static ScriptCState::SymbolTable_t gSyms[] = {
+    { "_Z22rsgBindProgramFragment19rs_program_fragment", (void *)&SC_bindProgramFragment, false },
+    { "_Z19rsgBindProgramStore16rs_program_store", (void *)&SC_bindProgramStore, false },
+    { "_Z20rsgBindProgramVertex17rs_program_vertex", (void *)&SC_bindProgramVertex, false },
+    { "_Z20rsgBindProgramRaster17rs_program_raster", (void *)&SC_bindProgramRaster, false },
+    { "_Z14rsgBindSampler19rs_program_fragmentj10rs_sampler", (void *)&SC_bindSampler, false },
+    { "_Z14rsgBindTexture19rs_program_fragmentj13rs_allocation", (void *)&SC_bindTexture, false },
+
+    { "_Z36rsgProgramVertexLoadProjectionMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadProjectionMatrix, false },
+    { "_Z31rsgProgramVertexLoadModelMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadModelMatrix, false },
+    { "_Z33rsgProgramVertexLoadTextureMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadTextureMatrix, false },
+
+    { "_Z35rsgProgramVertexGetProjectionMatrixP12rs_matrix4x4", (void *)&SC_vpGetProjectionMatrix, false },
+
+    { "_Z31rsgProgramFragmentConstantColor19rs_program_fragmentffff", (void *)&SC_pfConstantColor, false },
+
+    { "_Z11rsgGetWidthv", (void *)&SC_getWidth, false },
+    { "_Z12rsgGetHeightv", (void *)&SC_getHeight, false },
+
+    { "_Z18rsgUploadToTexture13rs_allocationj", (void *)&SC_uploadToTexture2, false },
+    { "_Z18rsgUploadToTexture13rs_allocation", (void *)&SC_uploadToTexture, false },
+    { "_Z23rsgUploadToBufferObject13rs_allocation", (void *)&SC_uploadToBufferObject, false },
+
+    { "_Z11rsgDrawRectfffff", (void *)&SC_drawRect, false },
+    { "_Z11rsgDrawQuadffffffffffff", (void *)&SC_drawQuad, false },
+    { "_Z20rsgDrawQuadTexCoordsffffffffffffffffffff", (void *)&SC_drawQuadTexCoords, false },
+    { "_Z24rsgDrawSpriteScreenspacefffff", (void *)&SC_drawSpriteScreenspace, false },
+
+    { "_Z11rsgDrawMesh7rs_mesh", (void *)&SC_drawMesh, false },
+    { "_Z11rsgDrawMesh7rs_meshj", (void *)&SC_drawMeshPrimitive, false },
+    { "_Z11rsgDrawMesh7rs_meshjjj", (void *)&SC_drawMeshPrimitiveRange, false },
+    { "_Z25rsgMeshComputeBoundingBox7rs_meshPfS0_S0_S0_S0_S0_", (void *)&SC_meshComputeBoundingBox, false },
+
+    { "_Z13rsgClearColorffff", (void *)&SC_ClearColor, false },
+    { "_Z13rsgClearDepthf", (void *)&SC_ClearDepth, false },
+
+    { "_Z11rsgDrawTextPKcii", (void *)&SC_DrawText, false },
+    { "_Z11rsgDrawText13rs_allocationii", (void *)&SC_DrawTextAlloc, false },
+    { "_Z14rsgMeasureTextPKcPiS1_S1_S1_", (void *)&SC_MeasureText, false },
+    { "_Z14rsgMeasureText13rs_allocationPiS0_S0_S0_", (void *)&SC_MeasureTextAlloc, false },
+
+    { "_Z11rsgBindFont7rs_font", (void *)&SC_BindFont, false },
+    { "_Z12rsgFontColorffff", (void *)&SC_FontColor, false },
+
+    // misc
+    { "_Z5colorffff", (void *)&SC_color, false },
+
+    { NULL, NULL, false }
+};
+
+const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbolGL(const char *sym)
+{
+    ScriptCState::SymbolTable_t *syms = gSyms;
+
+    while (syms->mPtr) {
+        if (!strcmp(syms->mName, sym)) {
+            return syms;
+        }
+        syms++;
+    }
+    return NULL;
+}
+
diff --git a/libs/rs/rsShaderCache.cpp b/libs/rs/rsShaderCache.cpp
index 4711d1b..45f6207 100644
--- a/libs/rs/rsShaderCache.cpp
+++ b/libs/rs/rsShaderCache.cpp
@@ -14,10 +14,14 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
-
 #include <GLES/gl.h>
 #include <GLES2/gl2.h>
+#else
+#include "rsContextHostStub.h"
+#include <OpenGL/gl.h>
+#endif //ANDROID_RS_BUILD_FOR_HOST
 
 using namespace android;
 using namespace android::renderscript;
@@ -25,20 +29,15 @@
 
 ShaderCache::ShaderCache()
 {
-    mEntryCount = 0;
-    mEntryAllocationCount = 16;
-    mEntries = (entry_t *)calloc(mEntryAllocationCount, sizeof(entry_t));
+    mEntries.setCapacity(16);
 }
 
 ShaderCache::~ShaderCache()
 {
-    for (uint32_t ct=0; ct < mEntryCount; ct++) {
-        glDeleteProgram(mEntries[ct].program);
+    for (uint32_t ct=0; ct < mEntries.size(); ct++) {
+        glDeleteProgram(mEntries[ct]->program);
+        free(mEntries[ct]);
     }
-
-    mEntryCount = 0;
-    mEntryAllocationCount = 0;
-    free(mEntries);
 }
 
 bool ShaderCache::lookup(Context *rsc, ProgramVertex *vtx, ProgramFragment *frag)
@@ -49,61 +48,46 @@
     if (!frag->getShaderID()) {
         frag->loadShader(rsc);
     }
+
+    // Don't try to cache if shaders failed to load
+    if(!vtx->getShaderID() || !frag->getShaderID()) {
+        return false;
+    }
     //LOGV("ShaderCache lookup  vtx %i, frag %i", vtx->getShaderID(), frag->getShaderID());
+    uint32_t entryCount = mEntries.size();
+    for(uint32_t ct = 0; ct < entryCount; ct ++) {
+        if ((mEntries[ct]->vtx == vtx->getShaderID()) &&
+            (mEntries[ct]->frag == frag->getShaderID())) {
 
-    for (uint32_t ct=0; ct < mEntryCount; ct++) {
-        if ((mEntries[ct].vtx == vtx->getShaderID()) &&
-            (mEntries[ct].frag == frag->getShaderID())) {
-
-            //LOGV("SC using program %i", mEntries[ct].program);
-            glUseProgram(mEntries[ct].program);
-            mCurrent = &mEntries[ct];
+            //LOGV("SC using program %i", mEntries[ct]->program);
+            glUseProgram(mEntries[ct]->program);
+            mCurrent = mEntries[ct];
             //LOGV("ShaderCache hit, using %i", ct);
             rsc->checkError("ShaderCache::lookup (hit)");
             return true;
         }
     }
-    // Not in cache, add it.
 
-    if (mEntryAllocationCount == mEntryCount) {
-        // Out of space, make some.
-        mEntryAllocationCount *= 2;
-        entry_t *e = (entry_t *)calloc(mEntryAllocationCount, sizeof(entry_t));
-        if (!e) {
-            LOGE("Out of memory for ShaderCache::lookup");
-            return false;
-        }
-        memcpy(e, mEntries, sizeof(entry_t) * mEntryCount);
-        free(mEntries);
-        mEntries = e;
-    }
-
-    //LOGV("ShaderCache miss, using %i", mEntryCount);
+    //LOGV("ShaderCache miss");
     //LOGE("e0 %x", glGetError());
-
-    entry_t *e = &mEntries[mEntryCount];
+    entry_t *e = (entry_t *)malloc(sizeof(entry_t));
+    mEntries.push(e);
     mCurrent = e;
     e->vtx = vtx->getShaderID();
     e->frag = frag->getShaderID();
     e->program = glCreateProgram();
-    e->mUserVertexProgram = vtx->isUserProgram();
-    if (mEntries[mEntryCount].program) {
+    e->vtxAttrCount = vtx->getAttribCount();
+    if (e->program) {
         GLuint pgm = e->program;
         glAttachShader(pgm, vtx->getShaderID());
         //LOGE("e1 %x", glGetError());
         glAttachShader(pgm, frag->getShaderID());
 
         if (!vtx->isUserProgram()) {
-            glBindAttribLocation(pgm, 0, "ATTRIB_LegacyPosition");
-            glBindAttribLocation(pgm, 1, "ATTRIB_LegacyColor");
-            glBindAttribLocation(pgm, 2, "ATTRIB_LegacyNormal");
-            glBindAttribLocation(pgm, 3, "ATTRIB_LegacyPointSize");
-            glBindAttribLocation(pgm, 4, "ATTRIB_LegacyTexture");
-            e->mVtxAttribSlots[RS_KIND_POSITION] = 0;
-            e->mVtxAttribSlots[RS_KIND_COLOR] = 1;
-            e->mVtxAttribSlots[RS_KIND_NORMAL] = 2;
-            e->mVtxAttribSlots[RS_KIND_POINT_SIZE] = 3;
-            e->mVtxAttribSlots[RS_KIND_TEXTURE] = 4;
+            glBindAttribLocation(pgm, 0, "ATTRIB_position");
+            glBindAttribLocation(pgm, 1, "ATTRIB_color");
+            glBindAttribLocation(pgm, 2, "ATTRIB_normal");
+            glBindAttribLocation(pgm, 3, "ATTRIB_texture0");
         }
 
         //LOGE("e2 %x", glGetError());
@@ -126,14 +110,15 @@
             rsc->setError(RS_ERROR_BAD_SHADER, "Error linking GL Programs");
             return false;
         }
-        if (vtx->isUserProgram()) {
-            for (uint32_t ct=0; ct < vtx->getAttribCount(); ct++) {
-                e->mVtxAttribSlots[ct] = glGetAttribLocation(pgm, vtx->getAttribName(ct));
-                if (rsc->props.mLogShaders) {
-                    LOGV("vtx A %i, %s = %d\n", ct, vtx->getAttribName(ct).string(), e->mVtxAttribSlots[ct]);
-                }
+
+        for (uint32_t ct=0; ct < e->vtxAttrCount; ct++) {
+            e->mVtxAttribSlots[ct] = glGetAttribLocation(pgm, vtx->getAttribName(ct));
+            e->mVtxAttribNames[ct] = vtx->getAttribName(ct).string();
+            if (rsc->props.mLogShaders) {
+                LOGV("vtx A %i, %s = %d\n", ct, vtx->getAttribName(ct).string(), e->mVtxAttribSlots[ct]);
             }
         }
+
         for (uint32_t ct=0; ct < vtx->getUniformCount(); ct++) {
             e->mVtxUniformSlots[ct] = glGetUniformLocation(pgm, vtx->getUniformName(ct));
             if (rsc->props.mLogShaders) {
@@ -151,17 +136,47 @@
     e->mIsValid = true;
     //LOGV("SC made program %i", e->program);
     glUseProgram(e->program);
-    mEntryCount++;
     rsc->checkError("ShaderCache::lookup (miss)");
     return true;
 }
 
+int32_t ShaderCache::vtxAttribSlot(const String8 &attrName) const {
+    for (uint32_t ct=0; ct < mCurrent->vtxAttrCount; ct++) {
+        if(attrName == mCurrent->mVtxAttribNames[ct]) {
+            return mCurrent->mVtxAttribSlots[ct];
+        }
+    }
+    return -1;
+}
+
 void ShaderCache::cleanupVertex(uint32_t id)
 {
+    int32_t numEntries = (int32_t)mEntries.size();
+    for(int32_t ct = 0; ct < numEntries; ct ++) {
+        if (mEntries[ct]->vtx == id) {
+            glDeleteProgram(mEntries[ct]->program);
+
+            free(mEntries[ct]);
+            mEntries.removeAt(ct);
+            numEntries = (int32_t)mEntries.size();
+            ct --;
+        }
+    }
 }
 
 void ShaderCache::cleanupFragment(uint32_t id)
 {
+    int32_t numEntries = (int32_t)mEntries.size();
+    for(int32_t ct = 0; ct < numEntries; ct ++) {
+        if (mEntries[ct]->frag == id) {
+            glDeleteProgram(mEntries[ct]->program);
+
+            free(mEntries[ct]);
+            mEntries.removeAt(ct);
+            numEntries = (int32_t)mEntries.size();
+            ct --;
+        }
+    }
 }
 
 void ShaderCache::cleanupAll()
diff --git a/libs/rs/rsShaderCache.h b/libs/rs/rsShaderCache.h
index df99ccc..35ff95b 100644
--- a/libs/rs/rsShaderCache.h
+++ b/libs/rs/rsShaderCache.h
@@ -40,29 +40,30 @@
 
     void cleanupAll();
 
-    int32_t vtxAttribSlot(uint32_t a) const {return mCurrent->mVtxAttribSlots[a];}
+    int32_t vtxAttribSlot(const String8 &attrName) const;
     int32_t vtxUniformSlot(uint32_t a) const {return mCurrent->mVtxUniformSlots[a];}
     int32_t fragAttribSlot(uint32_t a) const {return mCurrent->mFragAttribSlots[a];}
     int32_t fragUniformSlot(uint32_t a) const {return mCurrent->mFragUniformSlots[a];}
-    bool isUserVertexProgram() const {return mCurrent->mUserVertexProgram;}
 
 protected:
     typedef struct {
         uint32_t vtx;
         uint32_t frag;
         uint32_t program;
+        uint32_t vtxAttrCount;
+        const char* mVtxAttribNames[Program::MAX_ATTRIBS];
         int32_t mVtxAttribSlots[Program::MAX_ATTRIBS];
         int32_t mVtxUniformSlots[Program::MAX_UNIFORMS];
         int32_t mFragAttribSlots[Program::MAX_ATTRIBS];
         int32_t mFragUniformSlots[Program::MAX_UNIFORMS];
-        bool mUserVertexProgram;
         bool mIsValid;
     } entry_t;
-    entry_t *mEntries;
+    //entry_t *mEntries;
+    Vector<entry_t*> mEntries;
     entry_t *mCurrent;
 
-    uint32_t mEntryCount;
-    uint32_t mEntryAllocationCount;
+    /*uint32_t mEntryCount;
+    uint32_t mEntryAllocationCount;*/
 
 };
 
diff --git a/libs/rs/rsSignal.cpp b/libs/rs/rsSignal.cpp
new file mode 100644
index 0000000..9239bfd
--- /dev/null
+++ b/libs/rs/rsSignal.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#include "rsSignal.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Signal::Signal()
+{
+    mSet = true;
+}
+
+Signal::~Signal()
+{
+    pthread_mutex_destroy(&mMutex);
+    pthread_cond_destroy(&mCondition);
+}
+
+bool Signal::init()
+{
+    int status = pthread_mutex_init(&mMutex, NULL);
+    if (status) {
+        LOGE("LocklessFifo mutex init failure");
+        return false;
+    }
+
+    status = pthread_cond_init(&mCondition, NULL);
+    if (status) {
+        LOGE("LocklessFifo condition init failure");
+        pthread_mutex_destroy(&mMutex);
+        return false;
+    }
+
+    return true;
+}
+
+void Signal::set()
+{
+    int status;
+
+    status = pthread_mutex_lock(&mMutex);
+    if (status) {
+        LOGE("LocklessCommandFifo: error %i locking for set condition.", status);
+        return;
+    }
+
+    mSet = true;
+
+    status = pthread_cond_signal(&mCondition);
+    if (status) {
+        LOGE("LocklessCommandFifo: error %i on set condition.", status);
+    }
+
+    status = pthread_mutex_unlock(&mMutex);
+    if (status) {
+        LOGE("LocklessCommandFifo: error %i unlocking for set condition.", status);
+    }
+}
+
+void Signal::wait()
+{
+    int status;
+
+    status = pthread_mutex_lock(&mMutex);
+    if (status) {
+        LOGE("LocklessCommandFifo: error %i locking for condition.", status);
+        return;
+    }
+
+    if (!mSet) {
+        status = pthread_cond_wait(&mCondition, &mMutex);
+        if (status) {
+            LOGE("LocklessCommandFifo: error %i waiting on condition.", status);
+        }
+    }
+    mSet = false;
+
+    status = pthread_mutex_unlock(&mMutex);
+    if (status) {
+        LOGE("LocklessCommandFifo: error %i unlocking for condition.", status);
+    }
+}
+
diff --git a/libs/rs/rsSignal.h b/libs/rs/rsSignal.h
new file mode 100644
index 0000000..2e760f1
--- /dev/null
+++ b/libs/rs/rsSignal.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_SIGNAL_H
+#define ANDROID_RS_SIGNAL_H
+
+
+#include "rsUtils.h"
+
+namespace android {
+namespace renderscript {
+
+class Signal {
+public:
+    Signal();
+    ~Signal();
+
+    bool init();
+
+    void set();
+    void wait();
+
+protected:
+    bool mSet;
+    pthread_mutex_t mMutex;
+    pthread_cond_t mCondition;
+};
+
+}
+}
+
+#endif
+
diff --git a/libs/rs/rsSimpleMesh.cpp b/libs/rs/rsSimpleMesh.cpp
deleted file mode 100644
index 53ce5cd..0000000
--- a/libs/rs/rsSimpleMesh.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "rsContext.h"
-
-using namespace android;
-using namespace android::renderscript;
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-SimpleMesh::SimpleMesh(Context *rsc) : ObjectBase(rsc)
-{
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
-}
-
-SimpleMesh::~SimpleMesh()
-{
-    delete[] mVertexTypes;
-    delete[] mVertexBuffers;
-}
-
-void SimpleMesh::render(Context *rsc) const
-{
-    if (mPrimitiveType.get()) {
-        renderRange(rsc, 0, mPrimitiveType->getDimX());
-        return;
-    }
-
-    if (mIndexType.get()) {
-        renderRange(rsc, 0, mIndexType->getDimX());
-        return;
-    }
-
-    renderRange(rsc, 0, mVertexTypes[0]->getDimX());
-}
-
-void SimpleMesh::renderRange(Context *rsc, uint32_t start, uint32_t len) const
-{
-    if (len < 1) {
-        return;
-    }
-
-    rsc->checkError("SimpleMesh::renderRange 1");
-    VertexArray va;
-    if (rsc->checkVersion2_0()) {
-        for (uint32_t ct=0; ct < mVertexTypeCount; ct++) {
-            mVertexBuffers[ct]->uploadCheck(rsc);
-            va.setActiveBuffer(mVertexBuffers[ct]->getBufferObjectID());
-            mVertexTypes[ct]->enableGLVertexBuffer2(&va);
-        }
-        va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache);
-    } else {
-        for (uint32_t ct=0; ct < mVertexTypeCount; ct++) {
-            mVertexBuffers[ct]->uploadCheck(rsc);
-            va.setActiveBuffer(mVertexBuffers[ct]->getBufferObjectID());
-            mVertexTypes[ct]->enableGLVertexBuffer(&va);
-        }
-        va.setupGL(rsc, 0);
-    }
-
-    rsc->checkError("SimpleMesh::renderRange 2");
-    if (mIndexType.get()) {
-        mIndexBuffer->uploadCheck(rsc);
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID());
-        glDrawElements(mGLPrimitive, len, GL_UNSIGNED_SHORT, (uint16_t *)(start * 2));
-    } else {
-        glDrawArrays(mGLPrimitive, start, len);
-    }
-
-    rsc->checkError("SimpleMesh::renderRange");
-}
-
-void SimpleMesh::uploadAll(Context *rsc)
-{
-    for (uint32_t ct=0; ct < mVertexTypeCount; ct++) {
-        if (mVertexBuffers[ct].get()) {
-            mVertexBuffers[ct]->deferedUploadToBufferObject(rsc);
-        }
-    }
-    if (mIndexBuffer.get()) {
-        mIndexBuffer->deferedUploadToBufferObject(rsc);
-    }
-    if (mPrimitiveBuffer.get()) {
-        mPrimitiveBuffer->deferedUploadToBufferObject(rsc);
-    }
-    rsc->checkError("SimpleMesh::uploadAll");
-}
-
-
-SimpleMeshContext::SimpleMeshContext()
-{
-}
-
-SimpleMeshContext::~SimpleMeshContext()
-{
-}
-
-
-namespace android {
-namespace renderscript {
-
-
-RsSimpleMesh rsi_SimpleMeshCreate(Context *rsc, RsType prim, RsType idx, RsType *vtx, uint32_t vtxCount, uint32_t primType)
-{
-    SimpleMesh *sm = new SimpleMesh(rsc);
-    sm->incUserRef();
-
-    sm->mIndexType.set((const Type *)idx);
-    sm->mPrimitiveType.set((const Type *)prim);
-
-    sm->mVertexTypeCount = vtxCount;
-    sm->mVertexTypes = new ObjectBaseRef<const Type>[vtxCount];
-    sm->mVertexBuffers = new ObjectBaseRef<Allocation>[vtxCount];
-    for (uint32_t ct=0; ct < vtxCount; ct++) {
-        sm->mVertexTypes[ct].set((const Type *)vtx[ct]);
-    }
-
-    sm->mPrimitive = (RsPrimitive)primType;
-    switch(sm->mPrimitive) {
-    case RS_PRIMITIVE_POINT:          sm->mGLPrimitive = GL_POINTS; break;
-    case RS_PRIMITIVE_LINE:           sm->mGLPrimitive = GL_LINES; break;
-    case RS_PRIMITIVE_LINE_STRIP:     sm->mGLPrimitive = GL_LINE_STRIP; break;
-    case RS_PRIMITIVE_TRIANGLE:       sm->mGLPrimitive = GL_TRIANGLES; break;
-    case RS_PRIMITIVE_TRIANGLE_STRIP: sm->mGLPrimitive = GL_TRIANGLE_STRIP; break;
-    case RS_PRIMITIVE_TRIANGLE_FAN:   sm->mGLPrimitive = GL_TRIANGLE_FAN; break;
-    }
-    return sm;
-}
-
-void rsi_SimpleMeshBindVertex(Context *rsc, RsSimpleMesh mv, RsAllocation va, uint32_t slot)
-{
-    SimpleMesh *sm = static_cast<SimpleMesh *>(mv);
-    rsAssert(slot < sm->mVertexTypeCount);
-
-    sm->mVertexBuffers[slot].set((Allocation *)va);
-}
-
-void rsi_SimpleMeshBindIndex(Context *rsc, RsSimpleMesh mv, RsAllocation va)
-{
-    SimpleMesh *sm = static_cast<SimpleMesh *>(mv);
-    sm->mIndexBuffer.set((Allocation *)va);
-}
-
-void rsi_SimpleMeshBindPrimitive(Context *rsc, RsSimpleMesh mv, RsAllocation va)
-{
-    SimpleMesh *sm = static_cast<SimpleMesh *>(mv);
-    sm->mPrimitiveBuffer.set((Allocation *)va);
-}
-
-
-
-
-}}
-
diff --git a/libs/rs/rsSimpleMesh.h b/libs/rs/rsSimpleMesh.h
deleted file mode 100644
index 6defbda..0000000
--- a/libs/rs/rsSimpleMesh.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_RS_SIMPLE_MESH_H
-#define ANDROID_RS_SIMPLE_MESH_H
-
-
-#include "RenderScript.h"
-
-// ---------------------------------------------------------------------------
-namespace android {
-namespace renderscript {
-
-
-// An element is a group of Components that occupies one cell in a structure.
-class SimpleMesh : public ObjectBase
-{
-public:
-    SimpleMesh(Context *);
-    ~SimpleMesh();
-
-    ObjectBaseRef<const Type> mIndexType;
-    ObjectBaseRef<const Type> mPrimitiveType;
-    ObjectBaseRef<const Type> *mVertexTypes;
-    uint32_t mVertexTypeCount;
-
-    ObjectBaseRef<Allocation> mIndexBuffer;
-    ObjectBaseRef<Allocation> mPrimitiveBuffer;
-    ObjectBaseRef<Allocation> *mVertexBuffers;
-
-    RsPrimitive mPrimitive;
-    uint32_t mGLPrimitive;
-
-
-    void render(Context *) const;
-    void renderRange(Context *, uint32_t start, uint32_t len) const;
-    void uploadAll(Context *);
-
-
-protected:
-};
-
-class SimpleMeshContext
-{
-public:
-    SimpleMeshContext();
-    ~SimpleMeshContext();
-
-
-};
-
-
-}
-}
-#endif //ANDROID_RS_SIMPLE_MESH_H
-
diff --git a/libs/rs/rsStream.cpp b/libs/rs/rsStream.cpp
new file mode 100644
index 0000000..68241fa
--- /dev/null
+++ b/libs/rs/rsStream.cpp
@@ -0,0 +1,131 @@
+
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_BUILD_FOR_HOST
+#include "rsContext.h"
+#else
+#include "rsContextHostStub.h"
+#endif
+
+#include "rsStream.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+IStream::IStream(const uint8_t *buf, bool use64)
+{
+    mData = buf;
+    mPos = 0;
+    mUse64 = use64;
+}
+
+void IStream::loadByteArray(void *dest, size_t numBytes)
+{
+    memcpy(dest, mData + mPos, numBytes);
+    mPos += numBytes;
+}
+
+uint64_t IStream::loadOffset()
+{
+    uint64_t tmp;
+    if (mUse64) {
+        mPos = (mPos + 7) & (~7);
+        tmp = reinterpret_cast<const uint64_t *>(&mData[mPos])[0];
+        mPos += sizeof(uint64_t);
+        return tmp;
+    }
+    return loadU32();
+}
+
+void IStream::loadString(String8 *s)
+{
+    uint32_t len = loadU32();
+    s->setTo((const char *)&mData[mPos], len);
+    mPos += len;
+}
+
+
+// Output stream implementation
+
+OStream::OStream(uint64_t len, bool use64)
+{
+    mData = (uint8_t*)malloc(len);
+    mLength = len;
+    mPos = 0;
+    mUse64 = use64;
+}
+
+OStream::~OStream()
+{
+    free(mData);
+}
+
+void OStream::addByteArray(const void *src, size_t numBytes)
+{
+    // We need to potentially grow more than once if the number of byes we write is substantial
+    while(mPos + numBytes >= mLength) {
+        growSize();
+    }
+    memcpy(mData + mPos, src, numBytes);
+    mPos += numBytes;
+}
+
+void OStream::addOffset(uint64_t v)
+{
+    if (mUse64) {
+        mPos = (mPos + 7) & (~7);
+        if(mPos + sizeof(v) >= mLength) {
+            growSize();
+        }
+        mData[mPos++] = (uint8_t)(v & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 8) & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 16) & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 24) & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 32) & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 40) & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 48) & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 56) & 0xff);
+    }
+    else {
+        addU32(v);
+    }
+}
+
+void OStream::addString(String8 *s)
+{
+    uint32_t len = s->size();
+    addU32(len);
+    if(mPos + len*sizeof(char) >= mLength) {
+        growSize();
+    }
+    char *stringData = reinterpret_cast<char *>(&mData[mPos]);
+    for(uint32_t i = 0; i < len; i ++) {
+        stringData[i] = s->string()[i];
+    }
+    mPos += len*sizeof(char);
+}
+
+void OStream::growSize()
+{
+    uint8_t *newData = (uint8_t*)malloc(mLength*2);
+    memcpy(newData, mData, mLength*sizeof(uint8_t));
+    mLength = mLength * 2;
+    free(mData);
+    mData = newData;
+}
+
+
diff --git a/libs/rs/rsStream.h b/libs/rs/rsStream.h
new file mode 100644
index 0000000..d401cd12
--- /dev/null
+++ b/libs/rs/rsStream.h
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_STREAM_H
+#define ANDROID_RS_STREAM_H
+
+#include <utils/String8.h>
+#include <stdio.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class IStream
+{
+public:
+    IStream(const uint8_t *, bool use64);
+
+    float loadF() {
+        mPos = (mPos + 3) & (~3);
+        float tmp = reinterpret_cast<const float *>(&mData[mPos])[0];
+        mPos += sizeof(float);
+        return tmp;
+    }
+    int32_t loadI32() {
+        mPos = (mPos + 3) & (~3);
+        int32_t tmp = reinterpret_cast<const int32_t *>(&mData[mPos])[0];
+        mPos += sizeof(int32_t);
+        return tmp;
+    }
+    uint32_t loadU32() {
+        mPos = (mPos + 3) & (~3);
+        uint32_t tmp = reinterpret_cast<const uint32_t *>(&mData[mPos])[0];
+        mPos += sizeof(uint32_t);
+        return tmp;
+    }
+    uint16_t loadU16() {
+        mPos = (mPos + 1) & (~1);
+        uint16_t tmp = reinterpret_cast<const uint16_t *>(&mData[mPos])[0];
+        mPos += sizeof(uint16_t);
+        return tmp;
+    }
+    inline uint8_t loadU8() {
+        uint8_t tmp = reinterpret_cast<const uint8_t *>(&mData[mPos])[0];
+        mPos += sizeof(uint8_t);
+        return tmp;
+    }
+    void loadByteArray(void *dest, size_t numBytes);
+    uint64_t loadOffset();
+    void loadString(String8 *s);
+    uint64_t getPos() const {
+        return mPos;
+    }
+    void reset(uint64_t pos) {
+        mPos = pos;
+    }
+    void reset() {
+        mPos = 0;
+    }
+    
+    const uint8_t * getPtr() const {
+        return mData;
+    }
+protected:
+    const uint8_t * mData;
+    uint64_t mPos;
+    bool mUse64;
+};
+
+class OStream
+{
+public:
+    OStream(uint64_t length, bool use64);
+    ~OStream();
+    
+    void align(uint32_t bytes) {
+        mPos = (mPos + (bytes - 1)) & (~(bytes - 1));
+        if(mPos >= mLength) {
+            growSize();
+        }
+    }
+    
+    void addF(float v) {
+        uint32_t uintV = *reinterpret_cast<uint32_t*> (&v);
+        addU32(uintV);
+    }
+    void addI32(int32_t v) {
+        mPos = (mPos + 3) & (~3);
+        if(mPos + sizeof(v) >= mLength) {
+            growSize();
+        }
+        mData[mPos++] = (uint8_t)(v & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 8) & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 16) & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 24) & 0xff);
+    }
+    void addU32(uint32_t v) {
+        mPos = (mPos + 3) & (~3);
+        if(mPos + sizeof(v) >= mLength) {
+            growSize();
+        }
+        mData[mPos++] = (uint8_t)(v & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 8) & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 16) & 0xff);
+        mData[mPos++] = (uint8_t)((v >> 24) & 0xff);
+    }
+    void addU16(uint16_t v) {
+        mPos = (mPos + 1) & (~1);
+        if(mPos + sizeof(v) >= mLength) {
+            growSize();
+        }
+        mData[mPos++] = (uint8_t)(v & 0xff);
+        mData[mPos++] = (uint8_t)(v >> 8);
+    }
+    inline void addU8(uint8_t v) {
+        if(mPos + 1 >= mLength) {
+            growSize();
+        }
+        reinterpret_cast<uint8_t *>(&mData[mPos])[0] = v;
+        mPos ++;
+    }
+    void addByteArray(const void *src, size_t numBytes);
+    void addOffset(uint64_t v);
+    void addString(String8 *s);
+    uint64_t getPos() const {
+        return mPos;
+    }
+    void reset(uint64_t pos) {
+        mPos = pos;
+    }
+    void reset() {
+        mPos = 0;
+    }
+    const uint8_t * getPtr() const {
+        return mData;
+    }
+protected:
+    void growSize();
+    uint8_t * mData;
+    uint64_t mLength;
+    uint64_t mPos;
+    bool mUse64;
+};
+    
+
+} // renderscript
+} // android
+#endif //ANDROID_RS_STREAM_H
+
+
diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp
index c09e979..caaa9f5 100644
--- a/libs/rs/rsType.cpp
+++ b/libs/rs/rsType.cpp
@@ -14,22 +14,27 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
 #include <GLES/gl.h>
+#else
+#include "rsContextHostStub.h"
+#include <OpenGL/gl.h>
+#endif
 
 using namespace android;
 using namespace android::renderscript;
 
 Type::Type(Context *rsc) : ObjectBase(rsc)
 {
-    mAllocFile = __FILE__;
-    mAllocLine = __LINE__;
     mLODs = 0;
     mLODCount = 0;
+    mAttribs = NULL;
+    mAttribsSize = 0;
     clear();
 }
 
-Type::~Type()
+void Type::preDestroy()
 {
     for (uint32_t ct = 0; ct < mRSC->mStateType.mTypes.size(); ct++) {
         if (mRSC->mStateType.mTypes[ct] == this) {
@@ -37,8 +42,17 @@
             break;
         }
     }
+}
+
+Type::~Type()
+{
     if (mLODs) {
         delete [] mLODs;
+        mLODs = NULL;
+    }
+    if(mAttribs) {
+        delete [] mAttribs;
+        mAttribs = NULL;
     }
 }
 
@@ -84,7 +98,9 @@
         mLODCount = 1;
     }
     if (mLODCount != oldLODCount) {
-        delete [] mLODs;
+        if(mLODs){
+            delete [] mLODs;
+        }
         mLODs = new LOD[mLODCount];
     }
 
@@ -135,139 +151,81 @@
     return offset;
 }
 
+bool Type::isValidGLComponent(uint32_t fieldIdx) {
+    // Do not create attribs for padding
+    if(mElement->getFieldName(fieldIdx)[0] == '#') {
+        return false;
+    }
+
+    // Only GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, GL_FLOAT are accepted.
+    // Filter rs types accordingly
+    RsDataType dt = mElement->getField(fieldIdx)->getComponent().getType();
+    if(dt != RS_TYPE_FLOAT_32 && dt != RS_TYPE_UNSIGNED_8 &&
+       dt != RS_TYPE_UNSIGNED_16 && dt != RS_TYPE_SIGNED_8 &&
+       dt != RS_TYPE_SIGNED_16) {
+        return false;
+    }
+
+    // Now make sure they are not arrays
+    uint32_t arraySize = mElement->getFieldArraySize(fieldIdx);
+    if(arraySize != 1) {
+        return false;
+    }
+
+    return true;
+}
 
 void Type::makeGLComponents()
 {
-    uint32_t userNum = 0;
+    // Count the number of gl attrs to initialize
+    mAttribsSize = 0;
 
-    for (uint32_t ct=0; ct < getElement()->getFieldCount(); ct++) {
-        const Component &c = getElement()->getField(ct)->getComponent();
-
-        switch(c.getKind()) {
-        case RS_KIND_USER:
-            mGL.mUser[userNum].size = c.getVectorSize();
-            mGL.mUser[userNum].offset = mElement->getFieldOffsetBytes(ct);
-            mGL.mUser[userNum].type = c.getGLType();
-            mGL.mUser[userNum].normalized = c.getType() != RS_TYPE_FLOAT_32;//c.getIsNormalized();
-            mGL.mUser[userNum].name.setTo(getElement()->getFieldName(ct));
-            userNum ++;
-            break;
-
-        case RS_KIND_POSITION:
-            rsAssert(mGL.mVtx.size == 0);
-            mGL.mVtx.size = c.getVectorSize();
-            mGL.mVtx.offset = mElement->getFieldOffsetBytes(ct);
-            mGL.mVtx.type = c.getGLType();
-            mGL.mVtx.normalized = false;
-            mGL.mVtx.name.setTo("Position");
-            break;
-
-        case RS_KIND_COLOR:
-            rsAssert(mGL.mColor.size == 0);
-            mGL.mColor.size = c.getVectorSize();
-            mGL.mColor.offset = mElement->getFieldOffsetBytes(ct);
-            mGL.mColor.type = c.getGLType();
-            mGL.mColor.normalized = c.getType() != RS_TYPE_FLOAT_32;
-            mGL.mColor.name.setTo("Color");
-            break;
-
-        case RS_KIND_NORMAL:
-            rsAssert(mGL.mNorm.size == 0);
-            mGL.mNorm.size = c.getVectorSize();
-            mGL.mNorm.offset = mElement->getFieldOffsetBytes(ct);
-            mGL.mNorm.type = c.getGLType();
-            mGL.mNorm.normalized = false;
-            mGL.mNorm.name.setTo("Normal");
-            break;
-
-        case RS_KIND_TEXTURE:
-            rsAssert(mGL.mTex.size == 0);
-            mGL.mTex.size = c.getVectorSize();
-            mGL.mTex.offset = mElement->getFieldOffsetBytes(ct);
-            mGL.mTex.type = c.getGLType();
-            mGL.mTex.normalized = false;
-            mGL.mTex.name.setTo("Texture");
-            break;
-
-        case RS_KIND_POINT_SIZE:
-            rsAssert(!mGL.mPointSize.size);
-            mGL.mPointSize.size = c.getVectorSize();
-            mGL.mPointSize.offset = mElement->getFieldOffsetBytes(ct);
-            mGL.mPointSize.type = c.getGLType();
-            mGL.mPointSize.normalized = false;
-            mGL.mPointSize.name.setTo("PointSize");
-        break;
-
-        default:
-            break;
+    for (uint32_t ct=0; ct < mElement->getFieldCount(); ct++) {
+        if(isValidGLComponent(ct)) {
+            mAttribsSize ++;
         }
     }
+    if(mAttribs) {
+        delete [] mAttribs;
+        mAttribs = NULL;
+    }
+    if(mAttribsSize) {
+        mAttribs = new VertexArray::Attrib[mAttribsSize];
+    }
+
+    uint32_t userNum = 0;
+    for (uint32_t ct=0; ct < mElement->getFieldCount(); ct++) {
+        const Component &c = mElement->getField(ct)->getComponent();
+
+        if(!isValidGLComponent(ct)) {
+            continue;
+        }
+
+        mAttribs[userNum].size = c.getVectorSize();
+        mAttribs[userNum].offset = mElement->getFieldOffsetBytes(ct);
+        mAttribs[userNum].type = c.getGLType();
+        mAttribs[userNum].normalized = c.getType() != RS_TYPE_FLOAT_32;//c.getIsNormalized();
+        String8 tmp(RS_SHADER_ATTR);
+        tmp.append(mElement->getFieldName(ct));
+        mAttribs[userNum].name.setTo(tmp.string());
+
+        userNum ++;
+    }
 }
 
+
 void Type::enableGLVertexBuffer(VertexArray *va) const
 {
-    // Note: We are only going to enable buffers and never disable them
-    // here.  The reason is more than one Allocation may be used as a vertex
-    // source.  So we cannot disable arrays that may have been in use by
-    // another allocation.
-
     uint32_t stride = mElement->getSizeBytes();
-    if (mGL.mVtx.size) {
-        va->addLegacy(mGL.mVtx.type,
-                      mGL.mVtx.size,
-                      stride,
-                      RS_KIND_POSITION,
-                      false,
-                      mGL.mVtx.offset);
-    }
-
-    if (mGL.mNorm.size) {
-        va->addLegacy(mGL.mNorm.type,
-                     3,
-                     stride,
-                     RS_KIND_NORMAL,
-                     false,
-                     mGL.mNorm.offset);
-    }
-
-    if (mGL.mColor.size) {
-        va->addLegacy(mGL.mColor.type,
-                     mGL.mColor.size,
-                     stride,
-                     RS_KIND_COLOR,
-                     true,
-                     mGL.mColor.offset);
-    }
-
-    if (mGL.mTex.size) {
-        va->addLegacy(mGL.mTex.type,
-                     mGL.mTex.size,
-                     stride,
-                     RS_KIND_TEXTURE,
-                     false,
-                     mGL.mTex.offset);
-    }
-
-    if (mGL.mPointSize.size) {
-        va->addLegacy(mGL.mPointSize.type,
-                     1,
-                     stride,
-                     RS_KIND_POINT_SIZE,
-                     false,
-                     mGL.mPointSize.offset);
-    }
-
-}
-
-void Type::enableGLVertexBuffer2(VertexArray *va) const
-{
-    // Do legacy buffers
-    enableGLVertexBuffer(va);
-
-    uint32_t stride = mElement->getSizeBytes();
-    for (uint32_t ct=0; ct < RS_MAX_ATTRIBS; ct++) {
-        if (mGL.mUser[ct].size) {
-            va->addUser(mGL.mUser[ct], stride);
+    for (uint32_t ct=0; ct < mAttribsSize; ct++) {
+        // Load up to RS_MAX_ATTRIBS inputs
+        // TODO: grow vertexarray dynamically
+        if(ct >= RS_MAX_ATTRIBS) {
+            LOGE("More GL attributes than we can handle");
+            break;
+        }
+        if (mAttribs[ct].size) {
+            va->add(mAttribs[ct], stride);
         }
     }
 }
@@ -283,6 +241,49 @@
     mElement->dumpLOGV(buf);
 }
 
+void Type::serialize(OStream *stream) const
+{
+    // Need to identify ourselves
+    stream->addU32((uint32_t)getClassId());
+
+    String8 name(getName());
+    stream->addString(&name);
+
+    mElement->serialize(stream);
+
+    stream->addU32(mDimX);
+    stream->addU32(mDimY);
+    stream->addU32(mDimZ);
+
+    stream->addU8((uint8_t)(mDimLOD ? 1 : 0));
+    stream->addU8((uint8_t)(mFaces ? 1 : 0));
+}
+
+Type *Type::createFromStream(Context *rsc, IStream *stream)
+{
+    // First make sure we are reading the correct object
+    RsA3DClassID classID = (RsA3DClassID)stream->loadU32();
+    if(classID != RS_A3D_CLASS_ID_TYPE) {
+        LOGE("type loading skipped due to invalid class id\n");
+        return NULL;
+    }
+
+    String8 name;
+    stream->loadString(&name);
+
+    Element *elem = Element::createFromStream(rsc, stream);
+    if(!elem) {
+        return NULL;
+    }
+
+    uint32_t x = stream->loadU32();
+    uint32_t y = stream->loadU32();
+    uint32_t z = stream->loadU32();
+    uint8_t lod = stream->loadU8();
+    uint8_t faces = stream->loadU8();
+    return Type::getType(rsc, elem, x, y, z, lod != 0, faces !=0 );
+}
+
 bool Type::getIsNp2() const
 {
     uint32_t x = getDimX();
@@ -301,97 +302,123 @@
     return false;
 }
 
+bool Type::isEqual(const Type *other) const {
+    if(other == NULL) {
+        return false;
+    }
+    if (other->getElement()->isEqual(getElement()) &&
+        other->getDimX() == mDimX &&
+        other->getDimY() == mDimY &&
+        other->getDimZ() == mDimZ &&
+        other->getDimLOD() == mDimLOD &&
+        other->getDimFaces() == mFaces) {
+        return true;
+    }
+    return false;
+}
+
+Type * Type::getType(Context *rsc, const Element *e,
+                     uint32_t dimX, uint32_t dimY, uint32_t dimZ,
+                     bool dimLOD, bool dimFaces)
+{
+    TypeState * stc = &rsc->mStateType;
+
+    ObjectBase::asyncLock();
+    for (uint32_t ct=0; ct < stc->mTypes.size(); ct++) {
+        Type *t = stc->mTypes[ct];
+        if (t->getElement() != e) continue;
+        if (t->getDimX() != dimX) continue;
+        if (t->getDimY() != dimY) continue;
+        if (t->getDimZ() != dimZ) continue;
+        if (t->getDimLOD() != dimLOD) continue;
+        if (t->getDimFaces() != dimFaces) continue;
+        t->incUserRef();
+        ObjectBase::asyncUnlock();
+        return t;
+    }
+    ObjectBase::asyncUnlock();
+
+
+    Type *nt = new Type(rsc);
+    nt->mElement.set(e);
+    nt->mDimX = dimX;
+    nt->mDimY = dimY;
+    nt->mDimZ = dimZ;
+    nt->mDimLOD = dimLOD;
+    nt->mFaces = dimFaces;
+    nt->compute();
+    nt->incUserRef();
+
+    ObjectBase::asyncLock();
+    stc->mTypes.push(nt);
+    ObjectBase::asyncUnlock();
+
+    return nt;
+}
+
+Type * Type::cloneAndResize1D(Context *rsc, uint32_t dimX) const
+{
+    return getType(rsc, mElement.get(), dimX,
+                   mDimY, mDimZ, mDimLOD, mFaces);
+}
+
+Type * Type::cloneAndResize2D(Context *rsc, uint32_t dimX, uint32_t dimY) const
+{
+    return getType(rsc, mElement.get(), dimX, dimY,
+                   mDimZ, mDimLOD, mFaces);
+}
+
 
 //////////////////////////////////////////////////
 //
 namespace android {
 namespace renderscript {
 
-void rsi_TypeBegin(Context *rsc, RsElement vse)
+}
+}
+
+RsType rsaTypeCreate(RsContext con, RsElement _e, uint32_t dimCount,
+                     const RsDimension *dims, const uint32_t *vals)
 {
+    Context *rsc = static_cast<Context *>(con);
+    Element *e = static_cast<Element *>(_e);
     TypeState * stc = &rsc->mStateType;
 
-    stc->mX = 0;
-    stc->mY = 0;
-    stc->mZ = 0;
-    stc->mLOD = false;
-    stc->mFaces = false;
-    stc->mElement.set(static_cast<const Element *>(vse));
+    uint32_t dimX = 0;
+    uint32_t dimY = 0;
+    uint32_t dimZ = 0;
+    uint32_t dimLOD = 0;
+    uint32_t dimFaces = 0;
+
+    for (uint32_t ct=0; ct < dimCount; ct++) {
+        switch(dims[ct]) {
+        case RS_DIMENSION_X: dimX = vals[ct]; break;
+        case RS_DIMENSION_Y: dimY = vals[ct]; break;
+        case RS_DIMENSION_Z: dimZ = vals[ct]; break;
+        case RS_DIMENSION_LOD: dimLOD = vals[ct]; break;
+        case RS_DIMENSION_FACE: dimFaces = vals[ct]; break;
+
+        default:
+            LOGE("rsaTypeCreate: Bad dimension");
+            rsAssert(0);
+        }
+    }
+
+    return Type::getType(rsc, e, dimX, dimY, dimZ, dimLOD, dimFaces);
 }
 
-void rsi_TypeAdd(Context *rsc, RsDimension dim, size_t value)
+void rsaTypeGetNativeData(RsContext con, RsType type, uint32_t *typeData, uint32_t typeDataSize)
 {
-    TypeState * stc = &rsc->mStateType;
+    rsAssert(typeDataSize == 6);
+    // Pack the data in the follofing way mDimX; mDimY; mDimZ;
+    // mDimLOD; mDimFaces; mElement; into typeData
+    Type *t = static_cast<Type *>(type);
 
-    if (dim < 0) {
-        //error
-        return;
-    }
-
-
-    switch (dim) {
-    case RS_DIMENSION_X:
-        stc->mX = value;
-        return;
-    case RS_DIMENSION_Y:
-        stc->mY = value;
-        return;
-    case RS_DIMENSION_Z:
-        stc->mZ = value;
-        return;
-    case RS_DIMENSION_FACE:
-        stc->mFaces = (value != 0);
-        return;
-    case RS_DIMENSION_LOD:
-        stc->mLOD = (value != 0);
-        return;
-    default:
-        break;
-    }
-
-
-    int32_t arrayNum = dim - RS_DIMENSION_ARRAY_0;
-    if ((dim < 0) || (dim > RS_DIMENSION_MAX)) {
-        LOGE("rsTypeAdd: Bad dimension");
-        //error
-        return;
-    }
-
-    // todo: implement array support
-
+    (*typeData++) = t->getDimX();
+    (*typeData++) = t->getDimY();
+    (*typeData++) = t->getDimZ();
+    (*typeData++) = t->getDimLOD();
+    (*typeData++) = t->getDimFaces() ? 1 : 0;
+    (*typeData++) = (uint32_t)t->getElement();
+    t->getElement()->incUserRef();
 }
-
-RsType rsi_TypeCreate(Context *rsc)
-{
-    TypeState * stc = &rsc->mStateType;
-
-    for (uint32_t ct=0; ct < stc->mTypes.size(); ct++) {
-        Type *t = stc->mTypes[ct];
-        if (t->getElement() != stc->mElement.get()) continue;
-        if (t->getDimX() != stc->mX) continue;
-        if (t->getDimY() != stc->mY) continue;
-        if (t->getDimZ() != stc->mZ) continue;
-        if (t->getDimLOD() != stc->mLOD) continue;
-        if (t->getDimFaces() != stc->mFaces) continue;
-        t->incUserRef();
-        return t;
-    }
-
-    Type * st = new Type(rsc);
-    st->incUserRef();
-    st->setDimX(stc->mX);
-    st->setDimY(stc->mY);
-    st->setDimZ(stc->mZ);
-    st->setElement(stc->mElement.get());
-    st->setDimLOD(stc->mLOD);
-    st->setDimFaces(stc->mFaces);
-    st->compute();
-    stc->mElement.clear();
-    stc->mTypes.push(st);
-    return st;
-}
-
-
-}
-}
-
diff --git a/libs/rs/rsType.h b/libs/rs/rsType.h
index c25577c..0ca5bb6 100644
--- a/libs/rs/rsType.h
+++ b/libs/rs/rsType.h
@@ -28,9 +28,6 @@
 class Type : public ObjectBase
 {
 public:
-    Type(Context *);
-    virtual ~Type();
-
     Type * createTex2D(const Element *, size_t w, size_t h, bool mip);
 
 
@@ -58,22 +55,24 @@
     uint32_t getLODCount() const {return mLODCount;}
     bool getIsNp2() const;
 
-
-    void setElement(const Element *e) {mElement.set(e);}
-    void setDimX(uint32_t v) {mDimX = v;}
-    void setDimY(uint32_t v) {mDimY = v;}
-    void setDimZ(uint32_t v) {mDimZ = v;}
-    void setDimFaces(bool v) {mFaces = v;}
-    void setDimLOD(bool v) {mDimLOD = v;}
-
-
     void clear();
     void compute();
 
     void enableGLVertexBuffer(class VertexArray *) const;
-    void enableGLVertexBuffer2(class VertexArray *) const;
 
     void dumpLOGV(const char *prefix) const;
+    virtual void serialize(OStream *stream) const;
+    virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_TYPE; }
+    static Type *createFromStream(Context *rsc, IStream *stream);
+
+    bool isEqual(const Type *other) const;
+
+    Type * cloneAndResize1D(Context *rsc, uint32_t dimX) const;
+    Type * cloneAndResize2D(Context *rsc, uint32_t dimX, uint32_t dimY) const;
+
+    static Type * getType(Context *rsc, const Element *e,
+                      uint32_t dimX, uint32_t dimY, uint32_t dimZ,
+                      bool dimLOD, bool dimFaces);
 
 protected:
     struct LOD {
@@ -112,18 +111,18 @@
     LOD *mLODs;
     uint32_t mLODCount;
 
-    struct GLState_t {
-        VertexArray::Attrib mUser[RS_MAX_ATTRIBS];
-        VertexArray::Attrib mVtx;
-        VertexArray::Attrib mNorm;
-        VertexArray::Attrib mColor;
-        VertexArray::Attrib mTex;
-        VertexArray::Attrib mPointSize;
-    };
-    GLState_t mGL;
+    VertexArray::Attrib *mAttribs;
+    uint32_t mAttribsSize;
+    bool isValidGLComponent(uint32_t fieldIdx);
     void makeGLComponents();
 
+
+protected:
+    virtual void preDestroy();
+    virtual ~Type();
+
 private:
+    Type(Context *);
     Type(const Type &);
 };
 
@@ -133,14 +132,6 @@
     TypeState();
     ~TypeState();
 
-    size_t mX;
-    size_t mY;
-    size_t mZ;
-    uint32_t mLOD;
-    bool mFaces;
-    ObjectBaseRef<const Element> mElement;
-
-
     // Cache of all existing types.
     Vector<Type *> mTypes;
 };
diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h
index 07f8933..17feb22 100644
--- a/libs/rs/rsUtils.h
+++ b/libs/rs/rsUtils.h
@@ -19,15 +19,23 @@
 
 #define LOG_NDEBUG 0
 #define LOG_TAG "RenderScript"
+
 #include <utils/Log.h>
-#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
+
+#include "rsStream.h"
+
 #include <utils/String8.h>
+#include <utils/Vector.h>
+
 #include <stdlib.h>
 #include <pthread.h>
 #include <time.h>
+#include <cutils/atomic.h>
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include <EGL/egl.h>
+#endif
+
 #include <math.h>
 
 #include "RenderScript.h"
@@ -41,6 +49,26 @@
 #define rsAssert(v) while(0)
 #endif
 
+typedef float rsvF_2 __attribute__ ((vector_size (8)));
+typedef float rsvF_4 __attribute__ ((vector_size (16)));
+typedef uint8_t rsvU8_4 __attribute__ ((vector_size (4)));
+
+union float2 {
+    rsvF_2 v;
+    float f[2];
+};
+
+union float4 {
+    rsvF_4 v;
+    float f[4];
+};
+
+union uchar4 {
+    rsvU8_4 v;
+    uint8_t f[4];
+    uint32_t packed;
+};
+
 template<typename T>
 T rsMin(T in1, T in2)
 {
diff --git a/libs/rs/rsVertexArray.cpp b/libs/rs/rsVertexArray.cpp
index 6c2002d..4d50124 100644
--- a/libs/rs/rsVertexArray.cpp
+++ b/libs/rs/rsVertexArray.cpp
@@ -14,10 +14,15 @@
  * limitations under the License.
  */
 
+#ifndef ANDROID_RS_BUILD_FOR_HOST
 #include "rsContext.h"
-
 #include <GLES/gl.h>
 #include <GLES2/gl2.h>
+#else
+#include "rsContextHostStub.h"
+#include <OpenGL/gl.h>
+#endif
+
 
 using namespace android;
 using namespace android::renderscript;
@@ -39,6 +44,7 @@
         mAttribs[ct].clear();
     }
     mActiveBuffer = 0;
+    mActivePointer = NULL;
     mCount = 0;
 }
 
@@ -50,12 +56,12 @@
 void VertexArray::Attrib::set(const Attrib &a)
 {
     buffer = a.buffer;
+    ptr = a.ptr;
     offset = a.offset;
     type = a.type;
     size = a.size;
     stride = a.stride;
     normalized = a.normalized;
-    kind = RS_KIND_USER;
     name.setTo(a.name);
 }
 
@@ -66,6 +72,7 @@
     type = 0;
     size = 0;
     stride = 0;
+    ptr = NULL;
     normalized = false;
     name.setTo("");
 }
@@ -75,138 +82,72 @@
     mAttribs[n].clear();
 }
 
-void VertexArray::addUser(const Attrib &a, uint32_t stride)
+void VertexArray::add(const Attrib &a, uint32_t stride)
 {
-    assert(mCount < RS_MAX_ATTRIBS);
+    rsAssert(mCount < RS_MAX_ATTRIBS);
     mAttribs[mCount].set(a);
     mAttribs[mCount].buffer = mActiveBuffer;
+    mAttribs[mCount].ptr = mActivePointer;
     mAttribs[mCount].stride = stride;
-    mAttribs[mCount].kind = RS_KIND_USER;
     mCount ++;
 }
 
-void VertexArray::addLegacy(uint32_t type, uint32_t size, uint32_t stride, RsDataKind kind, bool normalized, uint32_t offset)
+void VertexArray::add(uint32_t type, uint32_t size, uint32_t stride, bool normalized, uint32_t offset, const char *name)
 {
-    assert(mCount < RS_MAX_ATTRIBS);
+    rsAssert(mCount < RS_MAX_ATTRIBS);
     mAttribs[mCount].clear();
     mAttribs[mCount].type = type;
     mAttribs[mCount].size = size;
     mAttribs[mCount].offset = offset;
     mAttribs[mCount].normalized = normalized;
-    mAttribs[mCount].buffer = mActiveBuffer;
     mAttribs[mCount].stride = stride;
-    mAttribs[mCount].kind = kind;
+    mAttribs[mCount].name.setTo(name);
+
+    mAttribs[mCount].buffer = mActiveBuffer;
+    mAttribs[mCount].ptr = mActivePointer;
     mCount ++;
 }
 
 void VertexArray::logAttrib(uint32_t idx, uint32_t slot) const {
-    LOGE("va %i: slot=%i name=%s buf=%i  size=%i  type=0x%x  kind=%i  stride=0x%x  norm=%i  offset=0x%x", idx, slot,
+    if(idx == 0) {
+        LOGV("Starting vertex attribute binding");
+    }
+    LOGV("va %i: slot=%i name=%s buf=%i ptr=%p size=%i  type=0x%x  stride=0x%x  norm=%i  offset=0x%x",
+         idx, slot,
          mAttribs[idx].name.string(),
          mAttribs[idx].buffer,
+         mAttribs[idx].ptr,
          mAttribs[idx].size,
          mAttribs[idx].type,
-         mAttribs[idx].kind,
          mAttribs[idx].stride,
          mAttribs[idx].normalized,
          mAttribs[idx].offset);
 }
 
-void VertexArray::setupGL(const Context *rsc, class VertexArrayState *state) const
-{
-    glClientActiveTexture(GL_TEXTURE0);
-    glDisableClientState(GL_NORMAL_ARRAY);
-    glDisableClientState(GL_COLOR_ARRAY);
-    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-    glDisableClientState(GL_POINT_SIZE_ARRAY_OES);
-
-    for (uint32_t ct=0; ct < mCount; ct++) {
-        switch(mAttribs[ct].kind) {
-        case RS_KIND_POSITION:
-            //logAttrib(POSITION);
-            glEnableClientState(GL_VERTEX_ARRAY);
-            glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer);
-            glVertexPointer(mAttribs[ct].size,
-                            mAttribs[ct].type,
-                            mAttribs[ct].stride,
-                            (void *)mAttribs[ct].offset);
-            break;
-
-        case RS_KIND_NORMAL:
-            //logAttrib(NORMAL);
-            glEnableClientState(GL_NORMAL_ARRAY);
-            rsAssert(mAttribs[ct].size == 3);
-            glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer);
-            glNormalPointer(mAttribs[ct].type,
-                            mAttribs[ct].stride,
-                            (void *)mAttribs[ct].offset);
-            break;
-
-        case RS_KIND_COLOR:
-            //logAttrib(COLOR);
-            glEnableClientState(GL_COLOR_ARRAY);
-            glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer);
-            glColorPointer(mAttribs[ct].size,
-                           mAttribs[ct].type,
-                           mAttribs[ct].stride,
-                           (void *)mAttribs[ct].offset);
-            break;
-
-        case RS_KIND_TEXTURE:
-            //logAttrib(TEXTURE);
-            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-            glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer);
-            glTexCoordPointer(mAttribs[ct].size,
-                              mAttribs[ct].type,
-                              mAttribs[ct].stride,
-                              (void *)mAttribs[ct].offset);
-            break;
-
-        case RS_KIND_POINT_SIZE:
-            //logAttrib(POINT_SIZE);
-            glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
-            glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer);
-            glPointSizePointerOES(mAttribs[ct].type,
-                                  mAttribs[ct].stride,
-                                  (void *)mAttribs[ct].offset);
-            break;
-
-        default:
-            rsAssert(0);
-        }
-    }
-
-    rsc->checkError("VertexArray::setupGL");
-}
-
 void VertexArray::setupGL2(const Context *rsc, class VertexArrayState *state, ShaderCache *sc) const
 {
     rsc->checkError("VertexArray::setupGL2 start");
-    for (uint32_t ct=1; ct <= state->mLastEnableCount; ct++) {
+    for (uint32_t ct=1; ct <= 0xf/*state->mLastEnableCount*/; ct++) {
         glDisableVertexAttribArray(ct);
     }
 
     rsc->checkError("VertexArray::setupGL2 disabled");
     for (uint32_t ct=0; ct < mCount; ct++) {
-        uint32_t slot = 0;
-        if (sc->isUserVertexProgram()) {
-            slot = sc->vtxAttribSlot(ct);
-        } else {
-            if (mAttribs[ct].kind == RS_KIND_USER) {
-                continue;
-            }
-            slot = sc->vtxAttribSlot(mAttribs[ct].kind);
+        int32_t slot = sc->vtxAttribSlot(mAttribs[ct].name);
+        if(rsc->props.mLogShadersAttr) {
+            logAttrib(ct, slot);
         }
-
-        //logAttrib(ct, slot);
+        if(slot < 0) {
+            continue;
+        }
         glEnableVertexAttribArray(slot);
         glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer);
-
         glVertexAttribPointer(slot,
                               mAttribs[ct].size,
                               mAttribs[ct].type,
                               mAttribs[ct].normalized,
                               mAttribs[ct].stride,
-                              (void *)mAttribs[ct].offset);
+                              mAttribs[ct].ptr + mAttribs[ct].offset);
     }
     state->mLastEnableCount = mCount;
     rsc->checkError("VertexArray::setupGL2 done");
diff --git a/libs/rs/rsVertexArray.h b/libs/rs/rsVertexArray.h
index 3904cb6..dea7d41 100644
--- a/libs/rs/rsVertexArray.h
+++ b/libs/rs/rsVertexArray.h
@@ -37,13 +37,13 @@
     class Attrib {
     public:
         uint32_t buffer;
+        const uint8_t * ptr;
         uint32_t offset;
         uint32_t type;
         uint32_t size;
         uint32_t stride;
         bool normalized;
         String8 name;
-        RsDataKind kind;
 
         Attrib();
         void set(const Attrib &);
@@ -52,17 +52,25 @@
 
 
     void clearAll();
-    void setActiveBuffer(uint32_t id) {mActiveBuffer = id;}
-    void addUser(const Attrib &, uint32_t stride);
-    void addLegacy(uint32_t type, uint32_t size, uint32_t stride, RsDataKind kind, bool normalized, uint32_t offset);
+    void setActiveBuffer(uint32_t id) {
+        mActiveBuffer = id;
+        mActivePointer = NULL;
+    }
+    void setActiveBuffer(const void *ptr) {
+        mActiveBuffer = 0;
+        mActivePointer = (const uint8_t *)ptr;
+    }
 
-    void setupGL(const Context *rsc, class VertexArrayState *) const;
+    void add(const Attrib &, uint32_t stride);
+    void add(uint32_t type, uint32_t size, uint32_t stride, bool normalized, uint32_t offset, const char *name);
+
     void setupGL2(const Context *rsc, class VertexArrayState *, ShaderCache *) const;
     void logAttrib(uint32_t idx, uint32_t slot) const;
 
 protected:
     void clear(uint32_t index);
     uint32_t mActiveBuffer;
+    const uint8_t * mActivePointer;
     uint32_t mCount;
 
     Attrib mAttribs[RS_MAX_ATTRIBS];
@@ -80,7 +88,7 @@
 
 }
 }
-#endif //ANDROID_LIGHT_H
+#endif //ANDROID_VERTEX_ARRAY_H
 
 
 
diff --git a/libs/rs/rsg_ScriptJavaClass.cpp b/libs/rs/rsg_ScriptJavaClass.cpp
index cee9f52..0169b98 100644
--- a/libs/rs/rsg_ScriptJavaClass.cpp
+++ b/libs/rs/rsg_ScriptJavaClass.cpp
@@ -7,8 +7,12 @@
 struct Element;
 
 struct ElementField {
+    // An Element Field is a combination of an Element with a name assigned.
+
     const char *name;
     Element *e;
+
+
     ElementField(const char *n, Element *_e) {
         name = n;
         e = _e;
@@ -20,12 +24,21 @@
 };
 
 struct Element {
+    // An Element can take one of two forms.
+    // 1: Basic.  It contains a single basic type and vector size.
+    // 2: Complex.  It contains a list of fields with names.  Each field
+    // will in turn be another element.
+
     ElementField *fields;
-    size_t fieldCount;
+    size_t fieldCount;  // If field count is 0, the element is a Basic type.
     const char *name;
     bool generated;
 
+    // The basic data type from RenderScript.h
     RsDataType compType;
+
+    // The vector size of the data type for float2, float3, ....
+    // Allowed sizes are 2,3,4,8,16
     uint32_t compVectorSize;
 
     Element() {
diff --git a/libs/rs/rslib.bc b/libs/rs/rslib.bc
new file mode 100644
index 0000000..1897c3b
--- /dev/null
+++ b/libs/rs/rslib.bc
Binary files differ
diff --git a/libs/rs/scriptc/rs_cl.rsh b/libs/rs/scriptc/rs_cl.rsh
new file mode 100644
index 0000000..64844a4
--- /dev/null
+++ b/libs/rs/scriptc/rs_cl.rsh
@@ -0,0 +1,785 @@
+#ifndef __RS_CL_RSH__
+#define __RS_CL_RSH__
+
+#define M_PI        3.14159265358979323846264338327950288f   /* pi */
+
+
+// Conversions
+#define CVT_FUNC_2(typeout, typein) \
+static typeout##2 __attribute__((overloadable)) convert_##typeout##2(typein##2 v) { \
+    typeout##2 r = {(typeout)v.x, (typeout)v.y}; \
+    return r; \
+} \
+static typeout##3 __attribute__((overloadable)) convert_##typeout##3(typein##3 v) { \
+    typeout##3 r = {(typeout)v.x, (typeout)v.y, (typeout)v.z}; \
+    return r; \
+} \
+static typeout##4 __attribute__((overloadable)) convert_##typeout##4(typein##4 v) { \
+    typeout##4 r = {(typeout)v.x, (typeout)v.y, (typeout)v.z, (typeout)v.w}; \
+    return r; \
+}
+
+#define CVT_FUNC(type)      CVT_FUNC_2(type, uchar) \
+                            CVT_FUNC_2(type, char) \
+                            CVT_FUNC_2(type, ushort) \
+                            CVT_FUNC_2(type, short) \
+                            CVT_FUNC_2(type, int) \
+                            CVT_FUNC_2(type, uint) \
+                            CVT_FUNC_2(type, float)
+
+CVT_FUNC(char)
+CVT_FUNC(uchar)
+CVT_FUNC(short)
+CVT_FUNC(ushort)
+CVT_FUNC(int)
+CVT_FUNC(uint)
+CVT_FUNC(float)
+
+
+
+// Float ops, 6.11.2
+
+#define DEF_FUNC_1(fnc) \
+static float2 __attribute__((overloadable)) fnc(float2 v) { \
+    float2 r; \
+    r.x = fnc(v.x); \
+    r.y = fnc(v.y); \
+    return r; \
+} \
+static float3 __attribute__((overloadable)) fnc(float3 v) { \
+    float3 r; \
+    r.x = fnc(v.x); \
+    r.y = fnc(v.y); \
+    r.z = fnc(v.z); \
+    return r; \
+} \
+static float4 __attribute__((overloadable)) fnc(float4 v) { \
+    float4 r; \
+    r.x = fnc(v.x); \
+    r.y = fnc(v.y); \
+    r.z = fnc(v.z); \
+    r.w = fnc(v.w); \
+    return r; \
+}
+
+#define DEF_FUNC_2(fnc) \
+static float2 __attribute__((overloadable)) fnc(float2 v1, float2 v2) { \
+    float2 r; \
+    r.x = fnc(v1.x, v2.x); \
+    r.y = fnc(v1.y, v2.y); \
+    return r; \
+} \
+static float3 __attribute__((overloadable)) fnc(float3 v1, float3 v2) { \
+    float3 r; \
+    r.x = fnc(v1.x, v2.x); \
+    r.y = fnc(v1.y, v2.y); \
+    r.z = fnc(v1.z, v2.z); \
+    return r; \
+} \
+static float4 __attribute__((overloadable)) fnc(float4 v1, float4 v2) { \
+    float4 r; \
+    r.x = fnc(v1.x, v2.x); \
+    r.y = fnc(v1.y, v2.y); \
+    r.z = fnc(v1.z, v2.z); \
+    r.w = fnc(v1.w, v2.z); \
+    return r; \
+}
+
+#define DEF_FUNC_2F(fnc) \
+static float2 __attribute__((overloadable)) fnc(float2 v1, float v2) { \
+    float2 r; \
+    r.x = fnc(v1.x, v2); \
+    r.y = fnc(v1.y, v2); \
+    return r; \
+} \
+static float3 __attribute__((overloadable)) fnc(float3 v1, float v2) { \
+    float3 r; \
+    r.x = fnc(v1.x, v2); \
+    r.y = fnc(v1.y, v2); \
+    r.z = fnc(v1.z, v2); \
+    return r; \
+} \
+static float4 __attribute__((overloadable)) fnc(float4 v1, float v2) { \
+    float4 r; \
+    r.x = fnc(v1.x, v2); \
+    r.y = fnc(v1.y, v2); \
+    r.z = fnc(v1.z, v2); \
+    r.w = fnc(v1.w, v2); \
+    return r; \
+}
+
+
+extern float __attribute__((overloadable)) acos(float);
+DEF_FUNC_1(acos)
+
+extern float __attribute__((overloadable)) acosh(float);
+DEF_FUNC_1(acosh)
+
+static float __attribute__((overloadable)) acospi(float v) {
+    return acos(v) / M_PI;
+}
+DEF_FUNC_1(acospi)
+
+extern float __attribute__((overloadable)) asin(float);
+DEF_FUNC_1(asin)
+
+extern float __attribute__((overloadable)) asinh(float);
+DEF_FUNC_1(asinh)
+
+static float __attribute__((overloadable)) asinpi(float v) {
+    return asin(v) / M_PI;
+}
+DEF_FUNC_1(asinpi)
+
+extern float __attribute__((overloadable)) atan(float);
+DEF_FUNC_1(atan)
+
+extern float __attribute__((overloadable)) atan2(float, float);
+DEF_FUNC_2(atan2)
+
+extern float __attribute__((overloadable)) atanh(float);
+DEF_FUNC_1(atanh)
+
+static float __attribute__((overloadable)) atanpi(float v) {
+    return atan(v) / M_PI;
+}
+DEF_FUNC_1(atanpi)
+
+static float __attribute__((overloadable)) atan2pi(float y, float x) {
+    return atan2(y, x) / M_PI;
+}
+DEF_FUNC_2(atan2pi)
+
+extern float __attribute__((overloadable)) cbrt(float);
+DEF_FUNC_1(cbrt)
+
+extern float __attribute__((overloadable)) ceil(float);
+DEF_FUNC_1(ceil)
+
+extern float __attribute__((overloadable)) copysign(float, float);
+DEF_FUNC_2(copysign)
+
+extern float __attribute__((overloadable)) cos(float);
+DEF_FUNC_1(cos)
+
+extern float __attribute__((overloadable)) cosh(float);
+DEF_FUNC_1(cosh)
+
+static float __attribute__((overloadable)) cospi(float v) {
+    return cos(v * M_PI);
+}
+DEF_FUNC_1(cospi)
+
+extern float __attribute__((overloadable)) erfc(float);
+DEF_FUNC_1(erfc)
+
+extern float __attribute__((overloadable)) erf(float);
+DEF_FUNC_1(erf)
+
+extern float __attribute__((overloadable)) exp(float);
+DEF_FUNC_1(exp)
+
+extern float __attribute__((overloadable)) exp2(float);
+DEF_FUNC_1(exp2)
+
+extern float __attribute__((overloadable)) pow(float, float);
+static float __attribute__((overloadable)) exp10(float v) {
+    return pow(10.f, v);
+}
+DEF_FUNC_1(exp10)
+
+extern float __attribute__((overloadable)) expm1(float);
+DEF_FUNC_1(expm1)
+
+extern float __attribute__((overloadable)) fabs(float);
+DEF_FUNC_1(fabs)
+
+extern float __attribute__((overloadable)) fdim(float, float);
+DEF_FUNC_2(fdim)
+
+extern float __attribute__((overloadable)) floor(float);
+DEF_FUNC_1(floor)
+
+extern float __attribute__((overloadable)) fma(float, float, float);
+extern float2 __attribute__((overloadable)) fma(float2, float2, float2);
+extern float3 __attribute__((overloadable)) fma(float3, float3, float3);
+extern float4 __attribute__((overloadable)) fma(float4, float4, float4);
+
+extern float __attribute__((overloadable)) fmax(float, float);
+DEF_FUNC_2(fmax);
+DEF_FUNC_2F(fmax);
+
+extern float __attribute__((overloadable)) fmin(float, float);
+DEF_FUNC_2(fmin);
+DEF_FUNC_2F(fmin);
+
+extern float __attribute__((overloadable)) fmod(float, float);
+DEF_FUNC_2(fmod)
+
+static float __attribute__((overloadable)) fract(float v, float *iptr) {
+    int i = (int)floor(v);
+    iptr[0] = i;
+    return fmin(v - i, 0x1.fffffep-1f);
+}
+static float2 __attribute__((overloadable)) fract(float2 v, float2 *iptr) {
+    float t[2];
+    float2 r;
+    r.x = fract(v.x, &t[0]);
+    r.y = fract(v.y, &t[1]);
+    iptr[0] = t[0];
+    iptr[1] = t[1];
+    return r;
+}
+static float3 __attribute__((overloadable)) fract(float3 v, float3 *iptr) {
+    float t[3];
+    float3 r;
+    r.x = fract(v.x, &t[0]);
+    r.y = fract(v.y, &t[1]);
+    r.z = fract(v.z, &t[2]);
+    iptr[0] = t[0];
+    iptr[1] = t[1];
+    iptr[2] = t[2];
+    return r;
+}
+static float4 __attribute__((overloadable)) fract(float4 v, float4 *iptr) {
+    float t[4];
+    float4 r;
+    r.x = fract(v.x, &t[0]);
+    r.y = fract(v.y, &t[1]);
+    r.z = fract(v.z, &t[2]);
+    r.w = fract(v.w, &t[3]);
+    iptr[0] = t[0];
+    iptr[1] = t[1];
+    iptr[2] = t[2];
+    iptr[3] = t[3];
+    return r;
+}
+
+extern float __attribute__((overloadable)) frexp(float, float *);
+extern float2 __attribute__((overloadable)) frexp(float2, float2 *);
+extern float3 __attribute__((overloadable)) frexp(float3, float3 *);
+extern float4 __attribute__((overloadable)) frexp(float4, float4 *);
+
+extern float __attribute__((overloadable)) hypot(float, float);
+DEF_FUNC_2(hypot)
+
+extern int __attribute__((overloadable)) ilogb(float);
+DEF_FUNC_1(ilogb)
+
+extern float __attribute__((overloadable)) ldexp(float, int);
+extern float2 __attribute__((overloadable)) ldexp(float2, int2);
+extern float3 __attribute__((overloadable)) ldexp(float3, int3);
+extern float4 __attribute__((overloadable)) ldexp(float4, int4);
+extern float2 __attribute__((overloadable)) ldexp(float2, int);
+extern float3 __attribute__((overloadable)) ldexp(float3, int);
+extern float4 __attribute__((overloadable)) ldexp(float4, int);
+
+extern float __attribute__((overloadable)) lgamma(float);
+DEF_FUNC_1(lgamma)
+extern float __attribute__((overloadable)) lgamma(float, float *);
+extern float2 __attribute__((overloadable)) lgamma(float2, float2 *);
+extern float3 __attribute__((overloadable)) lgamma(float3, float3 *);
+extern float4 __attribute__((overloadable)) lgamma(float4, float4 *);
+
+extern float __attribute__((overloadable)) log(float);
+DEF_FUNC_1(log)
+
+
+extern float __attribute__((overloadable)) log10(float);
+DEF_FUNC_1(log10)
+
+static float __attribute__((overloadable)) log2(float v) {
+    return log10(v) / log10(2.f);
+}
+DEF_FUNC_1(log2)
+
+extern float __attribute__((overloadable)) log1p(float);
+DEF_FUNC_1(log1p)
+
+extern float __attribute__((overloadable)) logb(float);
+DEF_FUNC_1(logb)
+
+extern float __attribute__((overloadable)) mad(float, float, float);
+extern float2 __attribute__((overloadable)) mad(float2, float2, float2);
+extern float3 __attribute__((overloadable)) mad(float3, float3, float3);
+extern float4 __attribute__((overloadable)) mad(float4, float4, float4);
+
+extern float __attribute__((overloadable)) modf(float, float *);
+extern float2 __attribute__((overloadable)) modf(float2, float2 *);
+extern float3 __attribute__((overloadable)) modf(float3, float3 *);
+extern float4 __attribute__((overloadable)) modf(float4, float4 *);
+
+//extern float __attribute__((overloadable)) nan(uint);
+
+extern float __attribute__((overloadable)) nextafter(float, float);
+DEF_FUNC_2(nextafter)
+
+DEF_FUNC_2(pow)
+
+static float __attribute__((overloadable)) pown(float v, int p) {
+    return pow(v, (float)p);
+}
+static float2 __attribute__((overloadable)) pown(float2 v, int2 p) {
+    return pow(v, (float2)p);
+}
+static float3 __attribute__((overloadable)) pown(float3 v, int3 p) {
+    return pow(v, (float3)p);
+}
+static float4 __attribute__((overloadable)) pown(float4 v, int4 p) {
+    return pow(v, (float4)p);
+}
+
+static float __attribute__((overloadable)) powr(float v, float p) {
+    return pow(v, p);
+}
+static float2 __attribute__((overloadable)) powr(float2 v, float2 p) {
+    return pow(v, p);
+}
+static float3 __attribute__((overloadable)) powr(float3 v, float3 p) {
+    return pow(v, p);
+}
+static float4 __attribute__((overloadable)) powr(float4 v, float4 p) {
+    return pow(v, p);
+}
+
+extern float __attribute__((overloadable)) remainder(float, float);
+DEF_FUNC_2(remainder)
+
+extern float __attribute__((overloadable)) remquo(float, float, float *);
+extern float2 __attribute__((overloadable)) remquo(float2, float2, float2 *);
+extern float3 __attribute__((overloadable)) remquo(float3, float3, float3 *);
+extern float4 __attribute__((overloadable)) remquo(float4, float4, float4 *);
+
+extern float __attribute__((overloadable)) rint(float);
+DEF_FUNC_1(rint)
+
+static float __attribute__((overloadable)) rootn(float v, int r) {
+    return pow(v, 1.f / r);
+}
+static float2 __attribute__((overloadable)) rootn(float2 v, int2 r) {
+    float2 t = {1.f / r.x, 1.f / r.y};
+    return pow(v, t);
+}
+static float3 __attribute__((overloadable)) rootn(float3 v, int3 r) {
+    float3 t = {1.f / r.x, 1.f / r.y, 1.f / r.z};
+    return pow(v, t);
+}
+static float4 __attribute__((overloadable)) rootn(float4 v, int4 r) {
+    float4 t = {1.f / r.x, 1.f / r.y, 1.f / r.z, 1.f / r.w};
+    return pow(v, t);
+}
+
+extern float __attribute__((overloadable)) round(float);
+DEF_FUNC_1(round)
+
+extern float __attribute__((overloadable)) sqrt(float);
+/*static float __attribute__((overloadable)) rsqrt(float v) {
+    return 1.f / sqrt(v);
+}
+DEF_FUNC_1(rsqrt)*/
+
+extern float __attribute__((overloadable)) sin(float);
+DEF_FUNC_1(sin)
+
+static float __attribute__((overloadable)) sincos(float v, float *cosptr) {
+    *cosptr = cos(v);
+    return sin(v);
+}
+static float2 __attribute__((overloadable)) sincos(float2 v, float2 *cosptr) {
+    *cosptr = cos(v);
+    return sin(v);
+}
+static float3 __attribute__((overloadable)) sincos(float3 v, float3 *cosptr) {
+    *cosptr = cos(v);
+    return sin(v);
+}
+static float4 __attribute__((overloadable)) sincos(float4 v, float4 *cosptr) {
+    *cosptr = cos(v);
+    return sin(v);
+}
+
+extern float __attribute__((overloadable)) sinh(float);
+DEF_FUNC_1(sinh)
+
+static float __attribute__((overloadable)) sinpi(float v) {
+    return sin(v * M_PI);
+}
+DEF_FUNC_1(sinpi)
+
+DEF_FUNC_1(sqrt)
+
+extern float __attribute__((overloadable)) tan(float);
+DEF_FUNC_1(tan)
+
+extern float __attribute__((overloadable)) tanh(float);
+DEF_FUNC_1(tanh)
+
+static float __attribute__((overloadable)) tanpi(float v) {
+    return tan(v * M_PI);
+}
+DEF_FUNC_1(tanpi)
+
+extern float __attribute__((overloadable)) tgamma(float);
+DEF_FUNC_1(tgamma)
+
+extern float __attribute__((overloadable)) trunc(float);
+DEF_FUNC_1(trunc)
+
+// Int ops (partial), 6.11.3
+extern uint __attribute__((overloadable)) abs(int);
+extern ushort __attribute__((overloadable)) abs(short);
+extern uchar __attribute__((overloadable)) abs(char);
+
+extern uint __attribute__((overloadable)) clz(uint);
+extern int __attribute__((overloadable)) clz(int);
+extern ushort __attribute__((overloadable)) clz(ushort);
+extern short __attribute__((overloadable)) clz(short);
+extern uchar __attribute__((overloadable)) clz(uchar);
+extern char __attribute__((overloadable)) clz(char);
+
+static uint __attribute__((overloadable)) min(uint v1, uint v2) {
+    return v1 < v2 ? v1 : v2;
+}
+static int __attribute__((overloadable)) min(int v1, int v2) {
+    return v1 < v2 ? v1 : v2;
+}
+static ushort __attribute__((overloadable)) min(ushort v1, ushort v2) {
+    return v1 < v2 ? v1 : v2;
+}
+static short __attribute__((overloadable)) min(short v1, short v2) {
+    return v1 < v2 ? v1 : v2;
+}
+static uchar __attribute__((overloadable)) min(uchar v1, uchar v2) {
+    return v1 < v2 ? v1 : v2;
+}
+static char __attribute__((overloadable)) min(char v1, char v2) {
+    return v1 < v2 ? v1 : v2;
+}
+
+static uint __attribute__((overloadable)) max(uint v1, uint v2) {
+    return v1 > v2 ? v1 : v2;
+}
+static int __attribute__((overloadable)) max(int v1, int v2) {
+    return v1 > v2 ? v1 : v2;
+}
+static ushort __attribute__((overloadable)) max(ushort v1, ushort v2) {
+    return v1 > v2 ? v1 : v2;
+}
+static short __attribute__((overloadable)) max(short v1, short v2) {
+    return v1 > v2 ? v1 : v2;
+}
+static uchar __attribute__((overloadable)) max(uchar v1, uchar v2) {
+    return v1 > v2 ? v1 : v2;
+}
+static char __attribute__((overloadable)) max(char v1, char v2) {
+    return v1 > v2 ? v1 : v2;
+}
+
+
+
+
+// 6.11.4
+
+static float __attribute__((overloadable)) clamp(float amount, float low, float high) {
+    return amount < low ? low : (amount > high ? high : amount);
+}
+static float2 __attribute__((overloadable)) clamp(float2 amount, float2 low, float2 high) {
+    float2 r;
+    r.x = amount.x < low.x ? low.x : (amount.x > high.x ? high.x : amount.x);
+    r.y = amount.y < low.y ? low.y : (amount.y > high.y ? high.y : amount.y);
+    return r;
+}
+static float3 __attribute__((overloadable)) clamp(float3 amount, float3 low, float3 high) {
+    float3 r;
+    r.x = amount.x < low.x ? low.x : (amount.x > high.x ? high.x : amount.x);
+    r.y = amount.y < low.y ? low.y : (amount.y > high.y ? high.y : amount.y);
+    r.z = amount.z < low.z ? low.z : (amount.z > high.z ? high.z : amount.z);
+    return r;
+}
+static float4 __attribute__((overloadable)) clamp(float4 amount, float4 low, float4 high) {
+    float4 r;
+    r.x = amount.x < low.x ? low.x : (amount.x > high.x ? high.x : amount.x);
+    r.y = amount.y < low.y ? low.y : (amount.y > high.y ? high.y : amount.y);
+    r.z = amount.z < low.z ? low.z : (amount.z > high.z ? high.z : amount.z);
+    r.w = amount.w < low.w ? low.w : (amount.w > high.w ? high.w : amount.w);
+    return r;
+}
+static float2 __attribute__((overloadable)) clamp(float2 amount, float low, float high) {
+    float2 r;
+    r.x = amount.x < low ? low : (amount.x > high ? high : amount.x);
+    r.y = amount.y < low ? low : (amount.y > high ? high : amount.y);
+    return r;
+}
+static float3 __attribute__((overloadable)) clamp(float3 amount, float low, float high) {
+    float3 r;
+    r.x = amount.x < low ? low : (amount.x > high ? high : amount.x);
+    r.y = amount.y < low ? low : (amount.y > high ? high : amount.y);
+    r.z = amount.z < low ? low : (amount.z > high ? high : amount.z);
+    return r;
+}
+static float4 __attribute__((overloadable)) clamp(float4 amount, float low, float high) {
+    float4 r;
+    r.x = amount.x < low ? low : (amount.x > high ? high : amount.x);
+    r.y = amount.y < low ? low : (amount.y > high ? high : amount.y);
+    r.z = amount.z < low ? low : (amount.z > high ? high : amount.z);
+    r.w = amount.w < low ? low : (amount.w > high ? high : amount.w);
+    return r;
+}
+
+static float __attribute__((overloadable)) degrees(float radians) {
+    return radians * (180.f / M_PI);
+}
+DEF_FUNC_1(degrees)
+
+static float __attribute__((overloadable)) max(float v1, float v2) {
+    return v1 > v2 ? v1 : v2;
+}
+static float2 __attribute__((overloadable)) max(float2 v1, float2 v2) {
+    float2 r;
+    r.x = v1.x > v2.x ? v1.x : v2.x;
+    r.y = v1.y > v2.y ? v1.y : v2.y;
+    return r;
+}
+static float3 __attribute__((overloadable)) max(float3 v1, float3 v2) {
+    float3 r;
+    r.x = v1.x > v2.x ? v1.x : v2.x;
+    r.y = v1.y > v2.y ? v1.y : v2.y;
+    r.z = v1.z > v2.z ? v1.z : v2.z;
+    return r;
+}
+static float4 __attribute__((overloadable)) max(float4 v1, float4 v2) {
+    float4 r;
+    r.x = v1.x > v2.x ? v1.x : v2.x;
+    r.y = v1.y > v2.y ? v1.y : v2.y;
+    r.z = v1.z > v2.z ? v1.z : v2.z;
+    r.w = v1.w > v2.w ? v1.w : v2.w;
+    return r;
+}
+static float2 __attribute__((overloadable)) max(float2 v1, float v2) {
+    float2 r;
+    r.x = v1.x > v2 ? v1.x : v2;
+    r.y = v1.y > v2 ? v1.y : v2;
+    return r;
+}
+static float3 __attribute__((overloadable)) max(float3 v1, float v2) {
+    float3 r;
+    r.x = v1.x > v2 ? v1.x : v2;
+    r.y = v1.y > v2 ? v1.y : v2;
+    r.z = v1.z > v2 ? v1.z : v2;
+    return r;
+}
+static float4 __attribute__((overloadable)) max(float4 v1, float v2) {
+    float4 r;
+    r.x = v1.x > v2 ? v1.x : v2;
+    r.y = v1.y > v2 ? v1.y : v2;
+    r.z = v1.z > v2 ? v1.z : v2;
+    r.w = v1.w > v2 ? v1.w : v2;
+    return r;
+}
+
+static float __attribute__((overloadable)) min(float v1, float v2) {
+    return v1 < v2 ? v1 : v2;
+}
+static float2 __attribute__((overloadable)) min(float2 v1, float2 v2) {
+    float2 r;
+    r.x = v1.x < v2.x ? v1.x : v2.x;
+    r.y = v1.y < v2.y ? v1.y : v2.y;
+    return r;
+}
+static float3 __attribute__((overloadable)) min(float3 v1, float3 v2) {
+    float3 r;
+    r.x = v1.x < v2.x ? v1.x : v2.x;
+    r.y = v1.y < v2.y ? v1.y : v2.y;
+    r.z = v1.z < v2.z ? v1.z : v2.z;
+    return r;
+}
+static float4 __attribute__((overloadable)) min(float4 v1, float4 v2) {
+    float4 r;
+    r.x = v1.x < v2.x ? v1.x : v2.x;
+    r.y = v1.y < v2.y ? v1.y : v2.y;
+    r.z = v1.z < v2.z ? v1.z : v2.z;
+    r.w = v1.w < v2.w ? v1.w : v2.w;
+    return r;
+}
+static float2 __attribute__((overloadable)) min(float2 v1, float v2) {
+    float2 r;
+    r.x = v1.x < v2 ? v1.x : v2;
+    r.y = v1.y < v2 ? v1.y : v2;
+    return r;
+}
+static float3 __attribute__((overloadable)) min(float3 v1, float v2) {
+    float3 r;
+    r.x = v1.x < v2 ? v1.x : v2;
+    r.y = v1.y < v2 ? v1.y : v2;
+    r.z = v1.z < v2 ? v1.z : v2;
+    return r;
+}
+static float4 __attribute__((overloadable)) min(float4 v1, float v2) {
+    float4 r;
+    r.x = v1.x < v2 ? v1.x : v2;
+    r.y = v1.y < v2 ? v1.y : v2;
+    r.z = v1.z < v2 ? v1.z : v2;
+    r.w = v1.w < v2 ? v1.w : v2;
+    return r;
+}
+
+static float __attribute__((overloadable)) mix(float start, float stop, float amount) {
+    return start + (stop - start) * amount;
+}
+static float2 __attribute__((overloadable)) mix(float2 start, float2 stop, float2 amount) {
+    return start + (stop - start) * amount;
+}
+static float3 __attribute__((overloadable)) mix(float3 start, float3 stop, float3 amount) {
+    return start + (stop - start) * amount;
+}
+static float4 __attribute__((overloadable)) mix(float4 start, float4 stop, float4 amount) {
+    return start + (stop - start) * amount;
+}
+static float2 __attribute__((overloadable)) mix(float2 start, float2 stop, float amount) {
+    return start + (stop - start) * amount;
+}
+static float3 __attribute__((overloadable)) mix(float3 start, float3 stop, float amount) {
+    return start + (stop - start) * amount;
+}
+static float4 __attribute__((overloadable)) mix(float4 start, float4 stop, float amount) {
+    return start + (stop - start) * amount;
+}
+
+static float __attribute__((overloadable)) radians(float degrees) {
+    return degrees * (M_PI / 180.f);
+}
+DEF_FUNC_1(radians)
+
+static float __attribute__((overloadable)) step(float edge, float v) {
+    return (v < edge) ? 0.f : 1.f;
+}
+static float2 __attribute__((overloadable)) step(float2 edge, float2 v) {
+    float2 r;
+    r.x = (v.x < edge.x) ? 0.f : 1.f;
+    r.y = (v.y < edge.y) ? 0.f : 1.f;
+    return r;
+}
+static float3 __attribute__((overloadable)) step(float3 edge, float3 v) {
+    float3 r;
+    r.x = (v.x < edge.x) ? 0.f : 1.f;
+    r.y = (v.y < edge.y) ? 0.f : 1.f;
+    r.z = (v.z < edge.z) ? 0.f : 1.f;
+    return r;
+}
+static float4 __attribute__((overloadable)) step(float4 edge, float4 v) {
+    float4 r;
+    r.x = (v.x < edge.x) ? 0.f : 1.f;
+    r.y = (v.y < edge.y) ? 0.f : 1.f;
+    r.z = (v.z < edge.z) ? 0.f : 1.f;
+    r.w = (v.w < edge.w) ? 0.f : 1.f;
+    return r;
+}
+static float2 __attribute__((overloadable)) step(float2 edge, float v) {
+    float2 r;
+    r.x = (v < edge.x) ? 0.f : 1.f;
+    r.y = (v < edge.y) ? 0.f : 1.f;
+    return r;
+}
+static float3 __attribute__((overloadable)) step(float3 edge, float v) {
+    float3 r;
+    r.x = (v < edge.x) ? 0.f : 1.f;
+    r.y = (v < edge.y) ? 0.f : 1.f;
+    r.z = (v < edge.z) ? 0.f : 1.f;
+    return r;
+}
+static float4 __attribute__((overloadable)) step(float4 edge, float v) {
+    float4 r;
+    r.x = (v < edge.x) ? 0.f : 1.f;
+    r.y = (v < edge.y) ? 0.f : 1.f;
+    r.z = (v < edge.z) ? 0.f : 1.f;
+    r.w = (v < edge.w) ? 0.f : 1.f;
+    return r;
+}
+
+extern float __attribute__((overloadable)) smoothstep(float, float, float);
+extern float2 __attribute__((overloadable)) smoothstep(float2, float2, float2);
+extern float3 __attribute__((overloadable)) smoothstep(float3, float3, float3);
+extern float4 __attribute__((overloadable)) smoothstep(float4, float4, float4);
+extern float2 __attribute__((overloadable)) smoothstep(float, float, float2);
+extern float3 __attribute__((overloadable)) smoothstep(float, float, float3);
+extern float4 __attribute__((overloadable)) smoothstep(float, float, float4);
+
+static float __attribute__((overloadable)) sign(float v) {
+    if (v > 0) return 1.f;
+    if (v < 0) return -1.f;
+    return v;
+}
+DEF_FUNC_1(sign)
+
+// 6.11.5
+static float3 __attribute__((overloadable)) cross(float3 lhs, float3 rhs) {
+    float3 r;
+    r.x = lhs.y * rhs.z  - lhs.z * rhs.y;
+    r.y = lhs.z * rhs.x  - lhs.x * rhs.z;
+    r.z = lhs.x * rhs.y  - lhs.y * rhs.x;
+    return r;
+}
+
+static float4 __attribute__((overloadable)) cross(float4 lhs, float4 rhs) {
+    float4 r;
+    r.x = lhs.y * rhs.z  - lhs.z * rhs.y;
+    r.y = lhs.z * rhs.x  - lhs.x * rhs.z;
+    r.z = lhs.x * rhs.y  - lhs.y * rhs.x;
+    r.w = 0.f;
+    return r;
+}
+
+static float __attribute__((overloadable)) dot(float lhs, float rhs) {
+    return lhs * rhs;
+}
+static float __attribute__((overloadable)) dot(float2 lhs, float2 rhs) {
+    return lhs.x*rhs.x + lhs.y*rhs.y;
+}
+static float __attribute__((overloadable)) dot(float3 lhs, float3 rhs) {
+    return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z;
+}
+static float __attribute__((overloadable)) dot(float4 lhs, float4 rhs) {
+    return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z + lhs.w*rhs.w;
+}
+
+static float __attribute__((overloadable)) length(float v) {
+    return v;
+}
+static float __attribute__((overloadable)) length(float2 v) {
+    return sqrt(v.x*v.x + v.y*v.y);
+}
+static float __attribute__((overloadable)) length(float3 v) {
+    return sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
+}
+static float __attribute__((overloadable)) length(float4 v) {
+    return sqrt(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w);
+}
+
+static float __attribute__((overloadable)) distance(float lhs, float rhs) {
+    return length(lhs - rhs);
+}
+static float __attribute__((overloadable)) distance(float2 lhs, float2 rhs) {
+    return length(lhs - rhs);
+}
+static float __attribute__((overloadable)) distance(float3 lhs, float3 rhs) {
+    return length(lhs - rhs);
+}
+static float __attribute__((overloadable)) distance(float4 lhs, float4 rhs) {
+    return length(lhs - rhs);
+}
+
+static float __attribute__((overloadable)) normalize(float v) {
+    return 1.f;
+}
+static float2 __attribute__((overloadable)) normalize(float2 v) {
+    return v / length(v);
+}
+static float3 __attribute__((overloadable)) normalize(float3 v) {
+    return v / length(v);
+}
+static float4 __attribute__((overloadable)) normalize(float4 v) {
+    return v / length(v);
+}
+
+
+#endif
diff --git a/libs/rs/scriptc/rs_core.rsh b/libs/rs/scriptc/rs_core.rsh
new file mode 100644
index 0000000..0e0532c
--- /dev/null
+++ b/libs/rs/scriptc/rs_core.rsh
@@ -0,0 +1,926 @@
+#ifndef __RS_CORE_RSH__
+#define __RS_CORE_RSH__
+
+// Debugging, print to the LOG a description string and a value.
+extern void __attribute__((overloadable))
+    rsDebug(const char *, float);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, float, float);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, float, float, float);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, float, float, float, float);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, double);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, const rs_matrix4x4 *);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, const rs_matrix3x3 *);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, const rs_matrix2x2 *);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, int);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, uint);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, long);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, unsigned long);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, long long);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, unsigned long long);
+extern void __attribute__((overloadable))
+    rsDebug(const char *, const void *);
+#define RS_DEBUG(a) rsDebug(#a, a)
+#define RS_DEBUG_MARKER rsDebug(__FILE__, __LINE__)
+
+static void __attribute__((overloadable)) rsDebug(const char *s, float2 v) {
+    rsDebug(s, v.x, v.y);
+}
+static void __attribute__((overloadable)) rsDebug(const char *s, float3 v) {
+    rsDebug(s, v.x, v.y, v.z);
+}
+static void __attribute__((overloadable)) rsDebug(const char *s, float4 v) {
+    rsDebug(s, v.x, v.y, v.z, v.w);
+}
+
+static uchar4 __attribute__((overloadable)) rsPackColorTo8888(float r, float g, float b)
+{
+    uchar4 c;
+    c.x = (uchar)(r * 255.f);
+    c.y = (uchar)(g * 255.f);
+    c.z = (uchar)(b * 255.f);
+    c.w = 255;
+    return c;
+}
+
+static uchar4 __attribute__((overloadable)) rsPackColorTo8888(float r, float g, float b, float a)
+{
+    uchar4 c;
+    c.x = (uchar)(r * 255.f);
+    c.y = (uchar)(g * 255.f);
+    c.z = (uchar)(b * 255.f);
+    c.w = (uchar)(a * 255.f);
+    return c;
+}
+
+static uchar4 __attribute__((overloadable)) rsPackColorTo8888(float3 color)
+{
+    color *= 255.f;
+    uchar4 c = {color.x, color.y, color.z, 255};
+    return c;
+}
+
+static uchar4 __attribute__((overloadable)) rsPackColorTo8888(float4 color)
+{
+    color *= 255.f;
+    uchar4 c = {color.x, color.y, color.z, color.w};
+    return c;
+}
+
+static float4 rsUnpackColor8888(uchar4 c)
+{
+    float4 ret = (float4)0.0039156862745f;
+    ret *= convert_float4(c);
+    return ret;
+}
+
+//extern uchar4 __attribute__((overloadable)) rsPackColorTo565(float r, float g, float b);
+//extern uchar4 __attribute__((overloadable)) rsPackColorTo565(float3);
+//extern float4 rsUnpackColor565(uchar4);
+
+
+/////////////////////////////////////////////////////
+// Matrix ops
+/////////////////////////////////////////////////////
+
+static void __attribute__((overloadable))
+rsMatrixSet(rs_matrix4x4 *m, uint32_t row, uint32_t col, float v) {
+    m->m[row * 4 + col] = v;
+}
+
+static float __attribute__((overloadable))
+rsMatrixGet(const rs_matrix4x4 *m, uint32_t row, uint32_t col) {
+    return m->m[row * 4 + col];
+}
+
+static void __attribute__((overloadable))
+rsMatrixSet(rs_matrix3x3 *m, uint32_t row, uint32_t col, float v) {
+    m->m[row * 3 + col] = v;
+}
+
+static float __attribute__((overloadable))
+rsMatrixGet(const rs_matrix3x3 *m, uint32_t row, uint32_t col) {
+    return m->m[row * 3 + col];
+}
+
+static void __attribute__((overloadable))
+rsMatrixSet(rs_matrix2x2 *m, uint32_t row, uint32_t col, float v) {
+    m->m[row * 2 + col] = v;
+}
+
+static float __attribute__((overloadable))
+rsMatrixGet(const rs_matrix2x2 *m, uint32_t row, uint32_t col) {
+    return m->m[row * 2 + col];
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoadIdentity(rs_matrix4x4 *m) {
+    m->m[0] = 1.f;
+    m->m[1] = 0.f;
+    m->m[2] = 0.f;
+    m->m[3] = 0.f;
+    m->m[4] = 0.f;
+    m->m[5] = 1.f;
+    m->m[6] = 0.f;
+    m->m[7] = 0.f;
+    m->m[8] = 0.f;
+    m->m[9] = 0.f;
+    m->m[10] = 1.f;
+    m->m[11] = 0.f;
+    m->m[12] = 0.f;
+    m->m[13] = 0.f;
+    m->m[14] = 0.f;
+    m->m[15] = 1.f;
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoadIdentity(rs_matrix3x3 *m) {
+    m->m[0] = 1.f;
+    m->m[1] = 0.f;
+    m->m[2] = 0.f;
+    m->m[3] = 0.f;
+    m->m[4] = 1.f;
+    m->m[5] = 0.f;
+    m->m[6] = 0.f;
+    m->m[7] = 0.f;
+    m->m[8] = 1.f;
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoadIdentity(rs_matrix2x2 *m) {
+    m->m[0] = 1.f;
+    m->m[1] = 0.f;
+    m->m[2] = 0.f;
+    m->m[3] = 1.f;
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoad(rs_matrix4x4 *m, const float *v) {
+    m->m[0] = v[0];
+    m->m[1] = v[1];
+    m->m[2] = v[2];
+    m->m[3] = v[3];
+    m->m[4] = v[4];
+    m->m[5] = v[5];
+    m->m[6] = v[6];
+    m->m[7] = v[7];
+    m->m[8] = v[8];
+    m->m[9] = v[9];
+    m->m[10] = v[10];
+    m->m[11] = v[11];
+    m->m[12] = v[12];
+    m->m[13] = v[13];
+    m->m[14] = v[14];
+    m->m[15] = v[15];
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoad(rs_matrix3x3 *m, const float *v) {
+    m->m[0] = v[0];
+    m->m[1] = v[1];
+    m->m[2] = v[2];
+    m->m[3] = v[3];
+    m->m[4] = v[4];
+    m->m[5] = v[5];
+    m->m[6] = v[6];
+    m->m[7] = v[7];
+    m->m[8] = v[8];
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoad(rs_matrix2x2 *m, const float *v) {
+    m->m[0] = v[0];
+    m->m[1] = v[1];
+    m->m[2] = v[2];
+    m->m[3] = v[3];
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoad(rs_matrix4x4 *m, const rs_matrix4x4 *v) {
+    m->m[0] = v->m[0];
+    m->m[1] = v->m[1];
+    m->m[2] = v->m[2];
+    m->m[3] = v->m[3];
+    m->m[4] = v->m[4];
+    m->m[5] = v->m[5];
+    m->m[6] = v->m[6];
+    m->m[7] = v->m[7];
+    m->m[8] = v->m[8];
+    m->m[9] = v->m[9];
+    m->m[10] = v->m[10];
+    m->m[11] = v->m[11];
+    m->m[12] = v->m[12];
+    m->m[13] = v->m[13];
+    m->m[14] = v->m[14];
+    m->m[15] = v->m[15];
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoad(rs_matrix4x4 *m, const rs_matrix3x3 *v) {
+    m->m[0] = v->m[0];
+    m->m[1] = v->m[1];
+    m->m[2] = v->m[2];
+    m->m[3] = 0.f;
+    m->m[4] = v->m[3];
+    m->m[5] = v->m[4];
+    m->m[6] = v->m[5];
+    m->m[7] = 0.f;
+    m->m[8] = v->m[6];
+    m->m[9] = v->m[7];
+    m->m[10] = v->m[8];
+    m->m[11] = 0.f;
+    m->m[12] = 0.f;
+    m->m[13] = 0.f;
+    m->m[14] = 0.f;
+    m->m[15] = 1.f;
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoad(rs_matrix4x4 *m, const rs_matrix2x2 *v) {
+    m->m[0] = v->m[0];
+    m->m[1] = v->m[1];
+    m->m[2] = 0.f;
+    m->m[3] = 0.f;
+    m->m[4] = v->m[3];
+    m->m[5] = v->m[4];
+    m->m[6] = 0.f;
+    m->m[7] = 0.f;
+    m->m[8] = v->m[6];
+    m->m[9] = v->m[7];
+    m->m[10] = 1.f;
+    m->m[11] = 0.f;
+    m->m[12] = 0.f;
+    m->m[13] = 0.f;
+    m->m[14] = 0.f;
+    m->m[15] = 1.f;
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoad(rs_matrix3x3 *m, const rs_matrix3x3 *v) {
+    m->m[0] = v->m[0];
+    m->m[1] = v->m[1];
+    m->m[2] = v->m[2];
+    m->m[3] = v->m[3];
+    m->m[4] = v->m[4];
+    m->m[5] = v->m[5];
+    m->m[6] = v->m[6];
+    m->m[7] = v->m[7];
+    m->m[8] = v->m[8];
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoad(rs_matrix2x2 *m, const rs_matrix2x2 *v) {
+    m->m[0] = v->m[0];
+    m->m[1] = v->m[1];
+    m->m[2] = v->m[2];
+    m->m[3] = v->m[3];
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoadRotate(rs_matrix4x4 *m, float rot, float x, float y, float z) {
+    float c, s;
+    m->m[3] = 0;
+    m->m[7] = 0;
+    m->m[11]= 0;
+    m->m[12]= 0;
+    m->m[13]= 0;
+    m->m[14]= 0;
+    m->m[15]= 1;
+    rot *= (float)(M_PI / 180.0f);
+    c = cos(rot);
+    s = sin(rot);
+
+    const float len = x*x + y*y + z*z;
+    if (len != 1) {
+        const float recipLen = 1.f / sqrt(len);
+        x *= recipLen;
+        y *= recipLen;
+        z *= recipLen;
+    }
+    const float nc = 1.0f - c;
+    const float xy = x * y;
+    const float yz = y * z;
+    const float zx = z * x;
+    const float xs = x * s;
+    const float ys = y * s;
+    const float zs = z * s;
+    m->m[ 0] = x*x*nc +  c;
+    m->m[ 4] =  xy*nc - zs;
+    m->m[ 8] =  zx*nc + ys;
+    m->m[ 1] =  xy*nc + zs;
+    m->m[ 5] = y*y*nc +  c;
+    m->m[ 9] =  yz*nc - xs;
+    m->m[ 2] =  zx*nc - ys;
+    m->m[ 6] =  yz*nc + xs;
+    m->m[10] = z*z*nc +  c;
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoadScale(rs_matrix4x4 *m, float x, float y, float z) {
+    rsMatrixLoadIdentity(m);
+    m->m[0] = x;
+    m->m[5] = y;
+    m->m[10] = z;
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoadTranslate(rs_matrix4x4 *m, float x, float y, float z) {
+    rsMatrixLoadIdentity(m);
+    m->m[12] = x;
+    m->m[13] = y;
+    m->m[14] = z;
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoadMultiply(rs_matrix4x4 *m, const rs_matrix4x4 *lhs, const rs_matrix4x4 *rhs) {
+    for (int i=0 ; i<4 ; i++) {
+        float ri0 = 0;
+        float ri1 = 0;
+        float ri2 = 0;
+        float ri3 = 0;
+        for (int j=0 ; j<4 ; j++) {
+            const float rhs_ij = rsMatrixGet(rhs, i,j);
+            ri0 += rsMatrixGet(lhs, j, 0) * rhs_ij;
+            ri1 += rsMatrixGet(lhs, j, 1) * rhs_ij;
+            ri2 += rsMatrixGet(lhs, j, 2) * rhs_ij;
+            ri3 += rsMatrixGet(lhs, j, 3) * rhs_ij;
+        }
+        rsMatrixSet(m, i, 0, ri0);
+        rsMatrixSet(m, i, 1, ri1);
+        rsMatrixSet(m, i, 2, ri2);
+        rsMatrixSet(m, i, 3, ri3);
+    }
+}
+
+static void __attribute__((overloadable))
+rsMatrixMultiply(rs_matrix4x4 *m, const rs_matrix4x4 *rhs) {
+    rs_matrix4x4 mt;
+    rsMatrixLoadMultiply(&mt, m, rhs);
+    rsMatrixLoad(m, &mt);
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoadMultiply(rs_matrix3x3 *m, const rs_matrix3x3 *lhs, const rs_matrix3x3 *rhs) {
+    for (int i=0 ; i<3 ; i++) {
+        float ri0 = 0;
+        float ri1 = 0;
+        float ri2 = 0;
+        for (int j=0 ; j<3 ; j++) {
+            const float rhs_ij = rsMatrixGet(rhs, i,j);
+            ri0 += rsMatrixGet(lhs, j, 0) * rhs_ij;
+            ri1 += rsMatrixGet(lhs, j, 1) * rhs_ij;
+            ri2 += rsMatrixGet(lhs, j, 2) * rhs_ij;
+        }
+        rsMatrixSet(m, i, 0, ri0);
+        rsMatrixSet(m, i, 1, ri1);
+        rsMatrixSet(m, i, 2, ri2);
+    }
+}
+
+static void __attribute__((overloadable))
+rsMatrixMultiply(rs_matrix3x3 *m, const rs_matrix3x3 *rhs) {
+    rs_matrix3x3 mt;
+    rsMatrixLoadMultiply(&mt, m, rhs);
+    rsMatrixLoad(m, &mt);
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoadMultiply(rs_matrix2x2 *m, const rs_matrix2x2 *lhs, const rs_matrix2x2 *rhs) {
+    for (int i=0 ; i<2 ; i++) {
+        float ri0 = 0;
+        float ri1 = 0;
+        for (int j=0 ; j<2 ; j++) {
+            const float rhs_ij = rsMatrixGet(rhs, i,j);
+            ri0 += rsMatrixGet(lhs, j, 0) * rhs_ij;
+            ri1 += rsMatrixGet(lhs, j, 1) * rhs_ij;
+        }
+        rsMatrixSet(m, i, 0, ri0);
+        rsMatrixSet(m, i, 1, ri1);
+    }
+}
+
+static void __attribute__((overloadable))
+rsMatrixMultiply(rs_matrix2x2 *m, const rs_matrix2x2 *rhs) {
+    rs_matrix2x2 mt;
+    rsMatrixLoadMultiply(&mt, m, rhs);
+    rsMatrixLoad(m, &mt);
+}
+
+static void __attribute__((overloadable))
+rsMatrixRotate(rs_matrix4x4 *m, float rot, float x, float y, float z) {
+    rs_matrix4x4 m1;
+    rsMatrixLoadRotate(&m1, rot, x, y, z);
+    rsMatrixMultiply(m, &m1);
+}
+
+static void __attribute__((overloadable))
+rsMatrixScale(rs_matrix4x4 *m, float x, float y, float z) {
+    rs_matrix4x4 m1;
+    rsMatrixLoadScale(&m1, x, y, z);
+    rsMatrixMultiply(m, &m1);
+}
+
+static void __attribute__((overloadable))
+rsMatrixTranslate(rs_matrix4x4 *m, float x, float y, float z) {
+    rs_matrix4x4 m1;
+    rsMatrixLoadTranslate(&m1, x, y, z);
+    rsMatrixMultiply(m, &m1);
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoadOrtho(rs_matrix4x4 *m, float left, float right, float bottom, float top, float near, float far) {
+    rsMatrixLoadIdentity(m);
+    m->m[0] = 2.f / (right - left);
+    m->m[5] = 2.f / (top - bottom);
+    m->m[10]= -2.f / (far - near);
+    m->m[12]= -(right + left) / (right - left);
+    m->m[13]= -(top + bottom) / (top - bottom);
+    m->m[14]= -(far + near) / (far - near);
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoadFrustum(rs_matrix4x4 *m, float left, float right, float bottom, float top, float near, float far) {
+    rsMatrixLoadIdentity(m);
+    m->m[0] = 2.f * near / (right - left);
+    m->m[5] = 2.f * near / (top - bottom);
+    m->m[8] = (right + left) / (right - left);
+    m->m[9] = (top + bottom) / (top - bottom);
+    m->m[10]= -(far + near) / (far - near);
+    m->m[11]= -1.f;
+    m->m[14]= -2.f * far * near / (far - near);
+    m->m[15]= 0.f;
+}
+
+static void __attribute__((overloadable))
+rsMatrixLoadPerspective(rs_matrix4x4* m, float fovy, float aspect, float near, float far) {
+    float top = near * tan((float) (fovy * M_PI / 360.0f));
+    float bottom = -top;
+    float left = bottom * aspect;
+    float right = top * aspect;
+    rsMatrixLoadFrustum(m, left, right, bottom, top, near, far);
+}
+
+static float4 __attribute__((overloadable))
+rsMatrixMultiply(rs_matrix4x4 *m, float4 in) {
+    float4 ret;
+    ret.x = (m->m[0] * in.x) + (m->m[4] * in.y) + (m->m[8] * in.z) + (m->m[12] * in.w);
+    ret.y = (m->m[1] * in.x) + (m->m[5] * in.y) + (m->m[9] * in.z) + (m->m[13] * in.w);
+    ret.z = (m->m[2] * in.x) + (m->m[6] * in.y) + (m->m[10] * in.z) + (m->m[14] * in.w);
+    ret.w = (m->m[3] * in.x) + (m->m[7] * in.y) + (m->m[11] * in.z) + (m->m[15] * in.w);
+    return ret;
+}
+
+static float4 __attribute__((overloadable))
+rsMatrixMultiply(rs_matrix4x4 *m, float3 in) {
+    float4 ret;
+    ret.x = (m->m[0] * in.x) + (m->m[4] * in.y) + (m->m[8] * in.z) + m->m[12];
+    ret.y = (m->m[1] * in.x) + (m->m[5] * in.y) + (m->m[9] * in.z) + m->m[13];
+    ret.z = (m->m[2] * in.x) + (m->m[6] * in.y) + (m->m[10] * in.z) + m->m[14];
+    ret.w = (m->m[3] * in.x) + (m->m[7] * in.y) + (m->m[11] * in.z) + m->m[15];
+    return ret;
+}
+
+static float4 __attribute__((overloadable))
+rsMatrixMultiply(rs_matrix4x4 *m, float2 in) {
+    float4 ret;
+    ret.x = (m->m[0] * in.x) + (m->m[4] * in.y) + m->m[12];
+    ret.y = (m->m[1] * in.x) + (m->m[5] * in.y) + m->m[13];
+    ret.z = (m->m[2] * in.x) + (m->m[6] * in.y) + m->m[14];
+    ret.w = (m->m[3] * in.x) + (m->m[7] * in.y) + m->m[15];
+    return ret;
+}
+
+static float3 __attribute__((overloadable))
+rsMatrixMultiply(rs_matrix3x3 *m, float3 in) {
+    float3 ret;
+    ret.x = (m->m[0] * in.x) + (m->m[3] * in.y) + (m->m[6] * in.z);
+    ret.y = (m->m[1] * in.x) + (m->m[4] * in.y) + (m->m[7] * in.z);
+    ret.z = (m->m[2] * in.x) + (m->m[5] * in.y) + (m->m[8] * in.z);
+    return ret;
+}
+
+static float3 __attribute__((overloadable))
+rsMatrixMultiply(rs_matrix3x3 *m, float2 in) {
+    float3 ret;
+    ret.x = (m->m[0] * in.x) + (m->m[3] * in.y);
+    ret.y = (m->m[1] * in.x) + (m->m[4] * in.y);
+    ret.z = (m->m[2] * in.x) + (m->m[5] * in.y);
+    return ret;
+}
+
+static float2 __attribute__((overloadable))
+rsMatrixMultiply(rs_matrix2x2 *m, float2 in) {
+    float2 ret;
+    ret.x = (m->m[0] * in.x) + (m->m[2] * in.y);
+    ret.y = (m->m[1] * in.x) + (m->m[3] * in.y);
+    return ret;
+}
+
+// Returns true if the matrix was successfully inversed
+static bool __attribute__((overloadable))
+rsMatrixInverse(rs_matrix4x4 *m) {
+    rs_matrix4x4 result;
+
+    int i, j;
+    for (i = 0; i < 4; ++i) {
+        for (j = 0; j < 4; ++j) {
+            // computeCofactor for int i, int j
+            int c0 = (i+1) % 4;
+            int c1 = (i+2) % 4;
+            int c2 = (i+3) % 4;
+            int r0 = (j+1) % 4;
+            int r1 = (j+2) % 4;
+            int r2 = (j+3) % 4;
+
+            float minor = (m->m[c0 + 4*r0] * (m->m[c1 + 4*r1] * m->m[c2 + 4*r2] - m->m[c1 + 4*r2] * m->m[c2 + 4*r1]))
+                         - (m->m[c0 + 4*r1] * (m->m[c1 + 4*r0] * m->m[c2 + 4*r2] - m->m[c1 + 4*r2] * m->m[c2 + 4*r0]))
+                         + (m->m[c0 + 4*r2] * (m->m[c1 + 4*r0] * m->m[c2 + 4*r1] - m->m[c1 + 4*r1] * m->m[c2 + 4*r0]));
+
+            float cofactor = (i+j) & 1 ? -minor : minor;
+
+            result.m[4*i + j] = cofactor;
+        }
+    }
+
+    // Dot product of 0th column of source and 0th row of result
+    float det = m->m[0]*result.m[0] + m->m[4]*result.m[1] +
+                 m->m[8]*result.m[2] + m->m[12]*result.m[3];
+
+    if (fabs(det) < 1e-6) {
+        return false;
+    }
+
+    det = 1.0f / det;
+    for (i = 0; i < 16; ++i) {
+        m->m[i] = result.m[i] * det;
+    }
+
+    return true;
+}
+
+// Returns true if the matrix was successfully inversed
+static bool __attribute__((overloadable))
+rsMatrixInverseTranspose(rs_matrix4x4 *m) {
+    rs_matrix4x4 result;
+
+    int i, j;
+    for (i = 0; i < 4; ++i) {
+        for (j = 0; j < 4; ++j) {
+            // computeCofactor for int i, int j
+            int c0 = (i+1) % 4;
+            int c1 = (i+2) % 4;
+            int c2 = (i+3) % 4;
+            int r0 = (j+1) % 4;
+            int r1 = (j+2) % 4;
+            int r2 = (j+3) % 4;
+
+            float minor = (m->m[c0 + 4*r0] * (m->m[c1 + 4*r1] * m->m[c2 + 4*r2] - m->m[c1 + 4*r2] * m->m[c2 + 4*r1]))
+                         - (m->m[c0 + 4*r1] * (m->m[c1 + 4*r0] * m->m[c2 + 4*r2] - m->m[c1 + 4*r2] * m->m[c2 + 4*r0]))
+                         + (m->m[c0 + 4*r2] * (m->m[c1 + 4*r0] * m->m[c2 + 4*r1] - m->m[c1 + 4*r1] * m->m[c2 + 4*r0]));
+
+            float cofactor = (i+j) & 1 ? -minor : minor;
+
+            result.m[4*j + i] = cofactor;
+        }
+    }
+
+    // Dot product of 0th column of source and 0th column of result
+    float det = m->m[0]*result.m[0] + m->m[4]*result.m[4] +
+                 m->m[8]*result.m[8] + m->m[12]*result.m[12];
+
+    if (fabs(det) < 1e-6) {
+        return false;
+    }
+
+    det = 1.0f / det;
+    for (i = 0; i < 16; ++i) {
+        m->m[i] = result.m[i] * det;
+    }
+
+    return true;
+}
+
+static void __attribute__((overloadable))
+rsMatrixTranspose(rs_matrix4x4 *m) {
+    int i, j;
+    float temp;
+    for (i = 0; i < 3; ++i) {
+        for (j = i + 1; j < 4; ++j) {
+            temp = m->m[i*4 + j];
+            m->m[i*4 + j] = m->m[j*4 + i];
+            m->m[j*4 + i] = temp;
+        }
+    }
+}
+
+static void __attribute__((overloadable))
+rsMatrixTranspose(rs_matrix3x3 *m) {
+    int i, j;
+    float temp;
+    for (i = 0; i < 2; ++i) {
+        for (j = i + 1; j < 3; ++j) {
+            temp = m->m[i*3 + j];
+            m->m[i*3 + j] = m->m[j*4 + i];
+            m->m[j*3 + i] = temp;
+        }
+    }
+}
+
+static void __attribute__((overloadable))
+rsMatrixTranspose(rs_matrix2x2 *m) {
+    float temp = m->m[1];
+    m->m[1] = m->m[2];
+    m->m[2] = temp;
+}
+
+/////////////////////////////////////////////////////
+// quaternion ops
+/////////////////////////////////////////////////////
+
+static void __attribute__((overloadable))
+rsQuaternionSet(rs_quaternion *q, float w, float x, float y, float z) {
+    q->w = w;
+    q->x = x;
+    q->y = y;
+    q->z = z;
+}
+
+static void __attribute__((overloadable))
+rsQuaternionSet(rs_quaternion *q, const rs_quaternion *rhs) {
+    q->w = rhs->w;
+    q->x = rhs->x;
+    q->y = rhs->y;
+    q->z = rhs->z;
+}
+
+static void __attribute__((overloadable))
+rsQuaternionMultiply(rs_quaternion *q, float s) {
+    q->w *= s;
+    q->x *= s;
+    q->y *= s;
+    q->z *= s;
+}
+
+static void __attribute__((overloadable))
+rsQuaternionMultiply(rs_quaternion *q, const rs_quaternion *rhs) {
+    q->w = -q->x*rhs->x - q->y*rhs->y - q->z*rhs->z + q->w*rhs->w;
+    q->x =  q->x*rhs->w + q->y*rhs->z - q->z*rhs->y + q->w*rhs->x;
+    q->y = -q->x*rhs->z + q->y*rhs->w + q->z*rhs->z + q->w*rhs->y;
+    q->z =  q->x*rhs->y - q->y*rhs->x + q->z*rhs->w + q->w*rhs->z;
+}
+
+static void
+rsQuaternionAdd(rs_quaternion *q, const rs_quaternion *rhs) {
+    q->w *= rhs->w;
+    q->x *= rhs->x;
+    q->y *= rhs->y;
+    q->z *= rhs->z;
+}
+
+static void
+rsQuaternionLoadRotateUnit(rs_quaternion *q, float rot, float x, float y, float z) {
+    rot *= (float)(M_PI / 180.0f) * 0.5f;
+    float c = cos(rot);
+    float s = sin(rot);
+
+    q->w = c;
+    q->x = x * s;
+    q->y = y * s;
+    q->z = z * s;
+}
+
+static void
+rsQuaternionLoadRotate(rs_quaternion *q, float rot, float x, float y, float z) {
+    const float len = x*x + y*y + z*z;
+    if (len != 1) {
+        const float recipLen = 1.f / sqrt(len);
+        x *= recipLen;
+        y *= recipLen;
+        z *= recipLen;
+    }
+    rsQuaternionLoadRotateUnit(q, rot, x, y, z);
+}
+
+static void
+rsQuaternionConjugate(rs_quaternion *q) {
+    q->x = -q->x;
+    q->y = -q->y;
+    q->z = -q->z;
+}
+
+static float
+rsQuaternionDot(const rs_quaternion *q0, const rs_quaternion *q1) {
+    return q0->w*q1->w + q0->x*q1->x + q0->y*q1->y + q0->z*q1->z;
+}
+
+static void
+rsQuaternionNormalize(rs_quaternion *q) {
+    const float len = rsQuaternionDot(q, q);
+    if (len != 1) {
+        const float recipLen = 1.f / sqrt(len);
+        rsQuaternionMultiply(q, recipLen);
+    }
+}
+
+static void
+rsQuaternionSlerp(rs_quaternion *q, const rs_quaternion *q0, const rs_quaternion *q1, float t) {
+    if(t <= 0.0f) {
+        rsQuaternionSet(q, q0);
+        return;
+    }
+    if(t >= 1.0f) {
+        rsQuaternionSet(q, q1);
+        return;
+    }
+
+    rs_quaternion tempq0, tempq1;
+    rsQuaternionSet(&tempq0, q0);
+    rsQuaternionSet(&tempq1, q1);
+
+    float angle = rsQuaternionDot(q0, q1);
+    if(angle < 0) {
+        rsQuaternionMultiply(&tempq0, -1.0f);
+        angle *= -1.0f;
+    }
+
+    float scale, invScale;
+    if (angle + 1.0f > 0.05f) {
+        if (1.0f - angle >= 0.05f) {
+            float theta = acos(angle);
+            float invSinTheta = 1.0f / sin(theta);
+            scale = sin(theta * (1.0f - t)) * invSinTheta;
+            invScale = sin(theta * t) * invSinTheta;
+        }
+        else {
+            scale = 1.0f - t;
+            invScale = t;
+        }
+    }
+    else {
+        rsQuaternionSet(&tempq1, tempq0.z, -tempq0.y, tempq0.x, -tempq0.w);
+        scale = sin(M_PI * (0.5f - t));
+        invScale = sin(M_PI * t);
+    }
+
+    rsQuaternionSet(q, tempq0.w*scale + tempq1.w*invScale, tempq0.x*scale + tempq1.x*invScale,
+                        tempq0.y*scale + tempq1.y*invScale, tempq0.z*scale + tempq1.z*invScale);
+}
+
+static void rsQuaternionGetMatrixUnit(rs_matrix4x4 *m, const rs_quaternion *q) {
+    float x2 = 2.0f * q->x * q->x;
+    float y2 = 2.0f * q->y * q->y;
+    float z2 = 2.0f * q->z * q->z;
+    float xy = 2.0f * q->x * q->y;
+    float wz = 2.0f * q->w * q->z;
+    float xz = 2.0f * q->x * q->z;
+    float wy = 2.0f * q->w * q->y;
+    float wx = 2.0f * q->w * q->x;
+    float yz = 2.0f * q->y * q->z;
+
+    m->m[0] = 1.0f - y2 - z2;
+    m->m[1] = xy - wz;
+    m->m[2] = xz + wy;
+    m->m[3] = 0.0f;
+
+    m->m[4] = xy + wz;
+    m->m[5] = 1.0f - x2 - z2;
+    m->m[6] = yz - wx;
+    m->m[7] = 0.0f;
+
+    m->m[8] = xz - wy;
+    m->m[9] = yz - wx;
+    m->m[10] = 1.0f - x2 - y2;
+    m->m[11] = 0.0f;
+
+    m->m[12] = 0.0f;
+    m->m[13] = 0.0f;
+    m->m[14] = 0.0f;
+    m->m[15] = 1.0f;
+}
+
+/////////////////////////////////////////////////////
+// utility funcs
+/////////////////////////////////////////////////////
+__inline__ static void __attribute__((overloadable, always_inline))
+rsExtractFrustumPlanes(const rs_matrix4x4 *modelViewProj,
+                         float4 *left, float4 *right,
+                         float4 *top, float4 *bottom,
+                         float4 *near, float4 *far) {
+    // x y z w = a b c d in the plane equation
+    left->x = modelViewProj->m[3] + modelViewProj->m[0];
+    left->y = modelViewProj->m[7] + modelViewProj->m[4];
+    left->z = modelViewProj->m[11] + modelViewProj->m[8];
+    left->w = modelViewProj->m[15] + modelViewProj->m[12];
+
+    right->x = modelViewProj->m[3] - modelViewProj->m[0];
+    right->y = modelViewProj->m[7] - modelViewProj->m[4];
+    right->z = modelViewProj->m[11] - modelViewProj->m[8];
+    right->w = modelViewProj->m[15] - modelViewProj->m[12];
+
+    top->x = modelViewProj->m[3] - modelViewProj->m[1];
+    top->y = modelViewProj->m[7] - modelViewProj->m[5];
+    top->z = modelViewProj->m[11] - modelViewProj->m[9];
+    top->w = modelViewProj->m[15] - modelViewProj->m[13];
+
+    bottom->x = modelViewProj->m[3] + modelViewProj->m[1];
+    bottom->y = modelViewProj->m[7] + modelViewProj->m[5];
+    bottom->z = modelViewProj->m[11] + modelViewProj->m[9];
+    bottom->w = modelViewProj->m[15] + modelViewProj->m[13];
+
+    near->x = modelViewProj->m[3] + modelViewProj->m[2];
+    near->y = modelViewProj->m[7] + modelViewProj->m[6];
+    near->z = modelViewProj->m[11] + modelViewProj->m[10];
+    near->w = modelViewProj->m[15] + modelViewProj->m[14];
+
+    far->x = modelViewProj->m[3] - modelViewProj->m[2];
+    far->y = modelViewProj->m[7] - modelViewProj->m[6];
+    far->z = modelViewProj->m[11] - modelViewProj->m[10];
+    far->w = modelViewProj->m[15] - modelViewProj->m[14];
+
+    float len = length(left->xyz);
+    *left /= len;
+    len = length(right->xyz);
+    *right /= len;
+    len = length(top->xyz);
+    *top /= len;
+    len = length(bottom->xyz);
+    *bottom /= len;
+    len = length(near->xyz);
+    *near /= len;
+    len = length(far->xyz);
+    *far /= len;
+}
+
+__inline__ static bool __attribute__((overloadable, always_inline))
+rsIsSphereInFrustum(float4 *sphere,
+                      float4 *left, float4 *right,
+                      float4 *top, float4 *bottom,
+                      float4 *near, float4 *far) {
+
+    float distToCenter = dot(left->xyz, sphere->xyz) + left->w;
+    if(distToCenter < -sphere->w) {
+        return false;
+    }
+    distToCenter = dot(right->xyz, sphere->xyz) + right->w;
+    if(distToCenter < -sphere->w) {
+        return false;
+    }
+    distToCenter = dot(top->xyz, sphere->xyz) + top->w;
+    if(distToCenter < -sphere->w) {
+        return false;
+    }
+    distToCenter = dot(bottom->xyz, sphere->xyz) + bottom->w;
+    if(distToCenter < -sphere->w) {
+        return false;
+    }
+    distToCenter = dot(near->xyz, sphere->xyz) + near->w;
+    if(distToCenter < -sphere->w) {
+        return false;
+    }
+    distToCenter = dot(far->xyz, sphere->xyz) + far->w;
+    if(distToCenter < -sphere->w) {
+        return false;
+    }
+    return true;
+}
+
+
+/////////////////////////////////////////////////////
+// int ops
+/////////////////////////////////////////////////////
+
+__inline__ static uint __attribute__((overloadable, always_inline)) rsClamp(uint amount, uint low, uint high) {
+    return amount < low ? low : (amount > high ? high : amount);
+}
+__inline__ static int __attribute__((overloadable, always_inline)) rsClamp(int amount, int low, int high) {
+    return amount < low ? low : (amount > high ? high : amount);
+}
+__inline__ static ushort __attribute__((overloadable, always_inline)) rsClamp(ushort amount, ushort low, ushort high) {
+    return amount < low ? low : (amount > high ? high : amount);
+}
+__inline__ static short __attribute__((overloadable, always_inline)) rsClamp(short amount, short low, short high) {
+    return amount < low ? low : (amount > high ? high : amount);
+}
+__inline__ static uchar __attribute__((overloadable, always_inline)) rsClamp(uchar amount, uchar low, uchar high) {
+    return amount < low ? low : (amount > high ? high : amount);
+}
+__inline__ static char __attribute__((overloadable, always_inline)) rsClamp(char amount, char low, char high) {
+    return amount < low ? low : (amount > high ? high : amount);
+}
+
+
+
+#endif
+
diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh
index 70cd562..54e6328 100644
--- a/libs/rs/scriptc/rs_graphics.rsh
+++ b/libs/rs/scriptc/rs_graphics.rsh
@@ -1,65 +1,109 @@
+#ifndef __RS_GRAPHICS_RSH__
+#define __RS_GRAPHICS_RSH__
 
+// Bind a ProgramFragment to the RS context.
+extern void __attribute__((overloadable))
+    rsgBindProgramFragment(rs_program_fragment);
+extern void __attribute__((overloadable))
+    rsgBindProgramStore(rs_program_store);
+extern void __attribute__((overloadable))
+    rsgBindProgramVertex(rs_program_vertex);
+extern void __attribute__((overloadable))
+    rsgBindProgramRaster(rs_program_raster);
 
-extern float2 vec2Rand(float len);
+extern void __attribute__((overloadable))
+    rsgBindSampler(rs_program_fragment, uint slot, rs_sampler);
+extern void __attribute__((overloadable))
+    rsgBindTexture(rs_program_fragment, uint slot, rs_allocation);
 
-extern float3 float3Norm(float3);
-extern float float3Length(float3);
-extern float3 float3Add(float3 lhs, float3 rhs);
-extern float3 float3Sub(float3 lhs, float3 rhs);
-extern float3 float3Cross(float3 lhs, float3 rhs);
-extern float float3Dot(float3 lhs, float3 rhs);
-extern float3 float3Scale(float3 v, float scale);
+extern void __attribute__((overloadable))
+    rsgProgramVertexLoadProjectionMatrix(const rs_matrix4x4 *);
+extern void __attribute__((overloadable))
+    rsgProgramVertexLoadModelMatrix(const rs_matrix4x4 *);
+extern void __attribute__((overloadable))
+    rsgProgramVertexLoadTextureMatrix(const rs_matrix4x4 *);
 
-extern float4 float4Add(float4 lhs, float4 rhs);
-extern float4 float4Sub(float4 lhs, float4 rhs);
-extern float4 float4Cross(float4 lhs, float4 rhs);
-extern float float4Dot(float4 lhs, float4 rhs);
-extern float4 float4Scale(float4 v, float scale);
+extern void __attribute__((overloadable))
+    rsgProgramVertexGetProjectionMatrix(rs_matrix4x4 *);
 
-    // context
-extern void bindProgramFragment(rs_program_fragment);
-extern void bindProgramStore(rs_program_store);
-extern void bindProgramVertex(rs_program_vertex);
+extern void __attribute__((overloadable))
+    rsgProgramFragmentConstantColor(rs_program_fragment, float, float, float, float);
 
-extern void bindSampler(rs_program_fragment, int slot, rs_sampler);
-extern void bindSampler(rs_program_fragment, int slot, rs_allocation);
+extern uint __attribute__((overloadable))
+    rsgGetWidth(void);
+extern uint __attribute__((overloadable))
+    rsgGetHeight(void);
 
-extern void vpLoadModelMatrix(const float *);
-extern void vpLoadTextureMatrix(const float *);
+extern void __attribute__((overloadable))
+    rsgUploadToTexture(rs_allocation);
+extern void __attribute__((overloadable))
+    rsgUploadToTexture(rs_allocation, uint mipLevel);
+extern void __attribute__((overloadable))
+    rsgUploadToBufferObject(rs_allocation);
 
+extern void __attribute__((overloadable))
+    rsgDrawRect(float x1, float y1, float x2, float y2, float z);
+extern void __attribute__((overloadable))
+    rsgDrawQuad(float x1, float y1, float z1,
+                float x2, float y2, float z2,
+                float x3, float y3, float z3,
+                float x4, float y4, float z4);
+extern void __attribute__((overloadable))
+    rsgDrawQuadTexCoords(float x1, float y1, float z1, float u1, float v1,
+                         float x2, float y2, float z2, float u2, float v2,
+                         float x3, float y3, float z3, float u3, float v3,
+                         float x4, float y4, float z4, float u4, float v4);
+extern void __attribute__((overloadable))
+    rsgDrawSpriteScreenspace(float x, float y, float z, float w, float h);
 
-// drawing
-extern void drawRect(float x1, float y1, float x2, float y2, float z);
-extern void drawQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4);
-extern void drawQuadTexCoords(float x1, float y1, float z1, float u1, float v1, float x2, float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, float x4, float y4, float z4, float u4, float v4);
-extern void drawSprite(float x, float y, float z, float w, float h);
-extern void drawSpriteScreenspace(float x, float y, float z, float w, float h);
-extern void drawLine(float x1, float y1, float z1, float x2, float y2, float z2);
-extern void drawPoint(float x1, float y1, float z1);
-extern void drawSimpleMesh(int ism);
-extern void drawSimpleMeshRange(int ism, int start, int len);
+extern void __attribute__((overloadable))
+    rsgDrawMesh(rs_mesh ism);
+extern void __attribute__((overloadable))
+    rsgDrawMesh(rs_mesh ism, uint primitiveIndex);
+extern void __attribute__((overloadable))
+    rsgDrawMesh(rs_mesh ism, uint primitiveIndex, uint start, uint len);
 
+extern void __attribute__((overloadable))
+    rsgClearColor(float, float, float, float);
+extern void __attribute__((overloadable))
+    rsgClearDepth(float);
+
+extern void __attribute__((overloadable))
+    rsgDrawText(const char *, int x, int y);
+extern void __attribute__((overloadable))
+    rsgDrawText(rs_allocation, int x, int y);
+extern void __attribute__((overloadable))
+    rsgBindFont(rs_font);
+extern void __attribute__((overloadable))
+    rsgFontColor(float, float, float, float);
+// Returns the bounding box of the text relative to (0, 0)
+// Any of left, right, top, bottom could be NULL
+extern void __attribute__((overloadable))
+    rsgMeasureText(const char *, int *left, int *right, int *top, int *bottom);
+extern void __attribute__((overloadable))
+    rsgMeasureText(rs_allocation, int *left, int *right, int *top, int *bottom);
+
+extern void __attribute__((overloadable))
+    rsgMeshComputeBoundingBox(rs_mesh mesh, float *minX, float *minY, float *minZ,
+                                                float *maxX, float *maxY, float *maxZ);
+__inline__ static void __attribute__((overloadable, always_inline))
+rsgMeshComputeBoundingBox(rs_mesh mesh, float3 *bBoxMin, float3 *bBoxMax) {
+    float x1, y1, z1, x2, y2, z2;
+    rsgMeshComputeBoundingBox(mesh, &x1, &y1, &z1, &x2, &y2, &z2);
+    bBoxMin->x = x1;
+    bBoxMin->y = y1;
+    bBoxMin->z = z1;
+    bBoxMax->x = x2;
+    bBoxMax->y = y2;
+    bBoxMax->z = z2;
+}
+
+///////////////////////////////////////////////////////
 // misc
-extern void pfClearColor(float, float, float, float);
-extern void color(float, float, float, float);
-extern void hsb(float, float, float, float);
-extern void hsbToRgb(float, float, float, float*);
-extern int hsbToAbgr(float, float, float, float);
 
-extern void uploadToTexture(int, int);
-extern void uploadToBufferObject(int);
+// Depricated
+extern void __attribute__((overloadable))
+    color(float, float, float, float);
 
-extern int colorFloatRGBAtoUNorm8(float, float, float, float);
-extern int colorFloatRGBto565(float, float, float);
-
-extern int getWidth();
-extern int getHeight();
-
-extern int sendToClient(void *data, int cmdID, int len, int waitForSpace);
-
-extern void debugF(const char *, float);
-extern void debugI32(const char *, int);
-extern void debugHexI32(const char *, int);
-
-
+#endif
 
diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh
index 613c7ca..d059997 100644
--- a/libs/rs/scriptc/rs_math.rsh
+++ b/libs/rs/scriptc/rs_math.rsh
@@ -1,287 +1,194 @@
-// Float ops
+#ifndef __RS_MATH_RSH__
+#define __RS_MATH_RSH__
 
-extern float __attribute__((overloadable)) abs(float);
-extern float2 __attribute__((overloadable)) abs(float2);
-extern float3 __attribute__((overloadable)) abs(float3);
-extern float4 __attribute__((overloadable)) abs(float4);
-extern float8 __attribute__((overloadable)) abs(float8);
-extern float16 __attribute__((overloadable)) abs(float16);
+extern void __attribute__((overloadable))
+    rsSetObject(rs_element *dst, rs_element src);
+extern void __attribute__((overloadable))
+    rsSetObject(rs_type *dst, rs_type src);
+extern void __attribute__((overloadable))
+    rsSetObject(rs_allocation *dst, rs_allocation src);
+extern void __attribute__((overloadable))
+    rsSetObject(rs_sampler *dst, rs_sampler src);
+extern void __attribute__((overloadable))
+    rsSetObject(rs_script *dst, rs_script src);
+extern void __attribute__((overloadable))
+    rsSetObject(rs_mesh *dst, rs_mesh src);
+extern void __attribute__((overloadable))
+    rsSetObject(rs_program_fragment *dst, rs_program_fragment src);
+extern void __attribute__((overloadable))
+    rsSetObject(rs_program_vertex *dst, rs_program_vertex src);
+extern void __attribute__((overloadable))
+    rsSetObject(rs_program_raster *dst, rs_program_raster src);
+extern void __attribute__((overloadable))
+    rsSetObject(rs_program_store *dst, rs_program_store src);
+extern void __attribute__((overloadable))
+    rsSetObject(rs_font *dst, rs_font src);
 
-extern float __attribute__((overloadable)) acos(float);
-extern float2 __attribute__((overloadable)) acos(float2);
-extern float3 __attribute__((overloadable)) acos(float3);
-extern float4 __attribute__((overloadable)) acos(float4);
-extern float8 __attribute__((overloadable)) acos(float8);
-extern float16 __attribute__((overloadable)) acos(float16);
+extern void __attribute__((overloadable))
+    rsClearObject(rs_element *dst);
+extern void __attribute__((overloadable))
+    rsClearObject(rs_type *dst);
+extern void __attribute__((overloadable))
+    rsClearObject(rs_allocation *dst);
+extern void __attribute__((overloadable))
+    rsClearObject(rs_sampler *dst);
+extern void __attribute__((overloadable))
+    rsClearObject(rs_script *dst);
+extern void __attribute__((overloadable))
+    rsClearObject(rs_mesh *dst);
+extern void __attribute__((overloadable))
+    rsClearObject(rs_program_fragment *dst);
+extern void __attribute__((overloadable))
+    rsClearObject(rs_program_vertex *dst);
+extern void __attribute__((overloadable))
+    rsClearObject(rs_program_raster *dst);
+extern void __attribute__((overloadable))
+    rsClearObject(rs_program_store *dst);
+extern void __attribute__((overloadable))
+    rsClearObject(rs_font *dst);
 
-extern float __attribute__((overloadable)) asin(float);
-extern float2 __attribute__((overloadable)) asin(float2);
-extern float3 __attribute__((overloadable)) asin(float3);
-extern float4 __attribute__((overloadable)) asin(float4);
-extern float8 __attribute__((overloadable)) asin(float8);
-extern float16 __attribute__((overloadable)) asin(float16);
-
-extern float __attribute__((overloadable)) atan(float);
-extern float2 __attribute__((overloadable)) atan(float2);
-extern float3 __attribute__((overloadable)) atan(float3);
-extern float4 __attribute__((overloadable)) atan(float4);
-extern float8 __attribute__((overloadable)) atan(float8);
-extern float16 __attribute__((overloadable)) atan(float16);
-
-extern float __attribute__((overloadable)) atan2(float, float);
-extern float2 __attribute__((overloadable)) atan2(float2, float2);
-extern float3 __attribute__((overloadable)) atan2(float3, float3);
-extern float4 __attribute__((overloadable)) atan2(float4, float4);
-extern float8 __attribute__((overloadable)) atan2(float8, float8);
-extern float16 __attribute__((overloadable)) atan2(float16, float16);
-
-extern float __attribute__((overloadable)) ceil(float);
-extern float2 __attribute__((overloadable)) ceil(float2);
-extern float3 __attribute__((overloadable)) ceil(float3);
-extern float4 __attribute__((overloadable)) ceil(float4);
-extern float8 __attribute__((overloadable)) ceil(float8);
-extern float16 __attribute__((overloadable)) ceil(float16);
-
-extern float __attribute__((overloadable)) clamp(float, float, float);
-extern float2 __attribute__((overloadable)) clamp(float2, float2, float2);
-extern float3 __attribute__((overloadable)) clamp(float3, float3, float3);
-extern float4 __attribute__((overloadable)) clamp(float4, float4, float4);
-extern float8 __attribute__((overloadable)) clamp(float8, float8, float8);
-extern float16 __attribute__((overloadable)) clamp(float16, float16, float16);
-extern float __attribute__((overloadable)) clamp(float, float, float);
-extern float2 __attribute__((overloadable)) clamp(float2, float, float);
-extern float3 __attribute__((overloadable)) clamp(float3, float, float);
-extern float4 __attribute__((overloadable)) clamp(float4, float, float);
-extern float8 __attribute__((overloadable)) clamp(float8, float, float);
-extern float16 __attribute__((overloadable)) clamp(float16, float, float);
-
-extern float __attribute__((overloadable)) copysign(float, float);
-extern float2 __attribute__((overloadable)) copysign(float2, float2);
-extern float3 __attribute__((overloadable)) copysign(float3, float3);
-extern float4 __attribute__((overloadable)) copysign(float4, float4);
-extern float8 __attribute__((overloadable)) copysign(float8, float8);
-extern float16 __attribute__((overloadable)) copysign(float16, float16);
-
-extern float __attribute__((overloadable)) cos(float);
-extern float2 __attribute__((overloadable)) cos(float2);
-extern float3 __attribute__((overloadable)) cos(float3);
-extern float4 __attribute__((overloadable)) cos(float4);
-extern float8 __attribute__((overloadable)) cos(float8);
-extern float16 __attribute__((overloadable)) cos(float16);
-
-extern float __attribute__((overloadable)) degrees(float);
-extern float2 __attribute__((overloadable)) degrees(float2);
-extern float3 __attribute__((overloadable)) degrees(float3);
-extern float4 __attribute__((overloadable)) degrees(float4);
-extern float8 __attribute__((overloadable)) degrees(float8);
-extern float16 __attribute__((overloadable)) degrees(float16);
-
-extern float __attribute__((overloadable)) exp(float);
-extern float2 __attribute__((overloadable)) exp(float2);
-extern float3 __attribute__((overloadable)) exp(float3);
-extern float4 __attribute__((overloadable)) exp(float4);
-extern float8 __attribute__((overloadable)) exp(float8);
-extern float16 __attribute__((overloadable)) exp(float16);
-
-extern float __attribute__((overloadable)) exp2(float);
-extern float2 __attribute__((overloadable)) exp2(float2);
-extern float3 __attribute__((overloadable)) exp2(float3);
-extern float4 __attribute__((overloadable)) exp2(float4);
-extern float8 __attribute__((overloadable)) exp2(float8);
-extern float16 __attribute__((overloadable)) exp2(float16);
-
-extern float __attribute__((overloadable)) exp10(float);
-extern float2 __attribute__((overloadable)) exp10(float2);
-extern float3 __attribute__((overloadable)) exp10(float3);
-extern float4 __attribute__((overloadable)) exp10(float4);
-extern float8 __attribute__((overloadable)) exp10(float8);
-extern float16 __attribute__((overloadable)) exp10(float16);
-
-extern float __attribute__((overloadable)) fabs(float);
-extern float2 __attribute__((overloadable)) fabs(float2);
-extern float3 __attribute__((overloadable)) fabs(float3);
-extern float4 __attribute__((overloadable)) fabs(float4);
-extern float8 __attribute__((overloadable)) fabs(float8);
-extern float16 __attribute__((overloadable)) fabs(float16);
-
-extern float __attribute__((overloadable)) floor(float);
-extern float2 __attribute__((overloadable)) floor(float2);
-extern float3 __attribute__((overloadable)) floor(float3);
-extern float4 __attribute__((overloadable)) floor(float4);
-extern float8 __attribute__((overloadable)) floor(float8);
-extern float16 __attribute__((overloadable)) floor(float16);
-
-extern float __attribute__((overloadable)) fmax(float, float);
-extern float2 __attribute__((overloadable)) fmax(float2, float2);
-extern float3 __attribute__((overloadable)) fmax(float3, float3);
-extern float4 __attribute__((overloadable)) fmax(float4, float4);
-extern float8 __attribute__((overloadable)) fmax(float8, float8);
-extern float16 __attribute__((overloadable)) fmax(float16, float16);
-extern float2 __attribute__((overloadable)) fmax(float2, float);
-extern float3 __attribute__((overloadable)) fmax(float3, float);
-extern float4 __attribute__((overloadable)) fmax(float4, float);
-extern float8 __attribute__((overloadable)) fmax(float8, float);
-extern float16 __attribute__((overloadable)) fmax(float16, float);
-
-extern float __attribute__((overloadable)) fmin(float, float);
-extern float2 __attribute__((overloadable)) fmin(float2, float2);
-extern float3 __attribute__((overloadable)) fmin(float3, float3);
-extern float4 __attribute__((overloadable)) fmin(float4, float4);
-extern float8 __attribute__((overloadable)) fmin(float8, float8);
-extern float16 __attribute__((overloadable)) fmin(float16, float16);
-extern float2 __attribute__((overloadable)) fmin(float2, float);
-extern float3 __attribute__((overloadable)) fmin(float3, float);
-extern float4 __attribute__((overloadable)) fmin(float4, float);
-extern float8 __attribute__((overloadable)) fmin(float8, float);
-extern float16 __attribute__((overloadable)) fmin(float16, float);
-
-extern float __attribute__((overloadable)) fmod(float, float);
-extern float2 __attribute__((overloadable)) fmod(float2, float2);
-extern float3 __attribute__((overloadable)) fmod(float3, float3);
-extern float4 __attribute__((overloadable)) fmod(float4, float4);
-extern float8 __attribute__((overloadable)) fmod(float8, float8);
-extern float16 __attribute__((overloadable)) fmod(float16, float16);
-
-extern float __attribute__((overloadable)) log(float);
-extern float2 __attribute__((overloadable)) log(float2);
-extern float3 __attribute__((overloadable)) log(float3);
-extern float4 __attribute__((overloadable)) log(float4);
-extern float8 __attribute__((overloadable)) log(float8);
-extern float16 __attribute__((overloadable)) log(float16);
-
-extern float __attribute__((overloadable)) log2(float);
-extern float2 __attribute__((overloadable)) log2(float2);
-extern float3 __attribute__((overloadable)) log2(float3);
-extern float4 __attribute__((overloadable)) log2(float4);
-extern float8 __attribute__((overloadable)) log2(float8);
-extern float16 __attribute__((overloadable)) log2(float16);
-
-extern float __attribute__((overloadable)) log10(float);
-extern float2 __attribute__((overloadable)) log10(float2);
-extern float3 __attribute__((overloadable)) log10(float3);
-extern float4 __attribute__((overloadable)) log10(float4);
-extern float8 __attribute__((overloadable)) log10(float8);
-extern float16 __attribute__((overloadable)) log10(float16);
-
-extern float __attribute__((overloadable)) max(float, float);
-extern float2 __attribute__((overloadable)) max(float2, float2);
-extern float3 __attribute__((overloadable)) max(float3, float3);
-extern float4 __attribute__((overloadable)) max(float4, float4);
-extern float8 __attribute__((overloadable)) max(float8, float8);
-extern float16 __attribute__((overloadable)) max(float16, float16);
-
-extern float __attribute__((overloadable)) min(float, float);
-extern float2 __attribute__((overloadable)) min(float2, float2);
-extern float3 __attribute__((overloadable)) min(float3, float3);
-extern float4 __attribute__((overloadable)) min(float4, float4);
-extern float8 __attribute__((overloadable)) min(float8, float8);
-extern float16 __attribute__((overloadable)) min(float16, float16);
-
-extern float __attribute__((overloadable)) mix(float, float, float);
-extern float2 __attribute__((overloadable)) mix(float2, float2, float2);
-extern float3 __attribute__((overloadable)) mix(float3, float3, float3);
-extern float4 __attribute__((overloadable)) mix(float4, float4, float4);
-extern float8 __attribute__((overloadable)) mix(float8, float8, float8);
-extern float16 __attribute__((overloadable)) mix(float16, float16, float16);
-extern float __attribute__((overloadable)) mix(float, float, float);
-extern float2 __attribute__((overloadable)) mix(float2, float2, float);
-extern float3 __attribute__((overloadable)) mix(float3, float3, float);
-extern float4 __attribute__((overloadable)) mix(float4, float4, float);
-extern float8 __attribute__((overloadable)) mix(float8, float8, float);
-extern float16 __attribute__((overloadable)) mix(float16, float16, float);
-
-extern float __attribute__((overloadable)) pow(float, float);
-extern float2 __attribute__((overloadable)) pow(float2, float2);
-extern float3 __attribute__((overloadable)) pow(float3, float3);
-extern float4 __attribute__((overloadable)) pow(float4, float4);
-extern float8 __attribute__((overloadable)) pow(float8, float8);
-extern float16 __attribute__((overloadable)) pow(float16, float16);
-
-extern float __attribute__((overloadable)) radians(float);
-extern float2 __attribute__((overloadable)) radians(float2);
-extern float3 __attribute__((overloadable)) radians(float3);
-extern float4 __attribute__((overloadable)) radians(float4);
-extern float8 __attribute__((overloadable)) radians(float8);
-extern float16 __attribute__((overloadable)) radians(float16);
-
-extern float __attribute__((overloadable)) rint(float);
-extern float2 __attribute__((overloadable)) rint(float2);
-extern float3 __attribute__((overloadable)) rint(float3);
-extern float4 __attribute__((overloadable)) rint(float4);
-extern float8 __attribute__((overloadable)) rint(float8);
-extern float16 __attribute__((overloadable)) rint(float16);
-
-extern float __attribute__((overloadable)) round(float);
-extern float2 __attribute__((overloadable)) round(float2);
-extern float3 __attribute__((overloadable)) round(float3);
-extern float4 __attribute__((overloadable)) round(float4);
-extern float8 __attribute__((overloadable)) round(float8);
-extern float16 __attribute__((overloadable)) round(float16);
-
-extern float __attribute__((overloadable)) rsqrt(float);
-extern float2 __attribute__((overloadable)) rsqrt(float2);
-extern float3 __attribute__((overloadable)) rsqrt(float3);
-extern float4 __attribute__((overloadable)) rsqrt(float4);
-extern float8 __attribute__((overloadable)) rsqrt(float8);
-extern float16 __attribute__((overloadable)) rsqrt(float16);
-
-extern float __attribute__((overloadable)) sign(float);
-extern float2 __attribute__((overloadable)) sign(float2);
-extern float3 __attribute__((overloadable)) sign(float3);
-extern float4 __attribute__((overloadable)) sign(float4);
-extern float8 __attribute__((overloadable)) sign(float8);
-extern float16 __attribute__((overloadable)) sign(float16);
-
-extern float __attribute__((overloadable)) sin(float);
-extern float2 __attribute__((overloadable)) sin(float2);
-extern float3 __attribute__((overloadable)) sin(float3);
-extern float4 __attribute__((overloadable)) sin(float4);
-extern float8 __attribute__((overloadable)) sin(float8);
-extern float16 __attribute__((overloadable)) sin(float16);
-
-extern float __attribute__((overloadable)) sqrt(float);
-extern float2 __attribute__((overloadable)) sqrt(float2);
-extern float3 __attribute__((overloadable)) sqrt(float3);
-extern float4 __attribute__((overloadable)) sqrt(float4);
-extern float8 __attribute__((overloadable)) sqrt(float8);
-extern float16 __attribute__((overloadable)) sqrt(float16);
-
-extern float __attribute__((overloadable)) tan(float);
-extern float2 __attribute__((overloadable)) tan(float2);
-extern float3 __attribute__((overloadable)) tan(float3);
-extern float4 __attribute__((overloadable)) tan(float4);
-extern float8 __attribute__((overloadable)) tan(float8);
-extern float16 __attribute__((overloadable)) tan(float16);
-
-extern float __attribute__((overloadable)) trunc(float);
-extern float2 __attribute__((overloadable)) trunc(float2);
-extern float3 __attribute__((overloadable)) trunc(float3);
-extern float4 __attribute__((overloadable)) trunc(float4);
-extern float8 __attribute__((overloadable)) trunc(float8);
-extern float16 __attribute__((overloadable)) trunc(float16);
+extern bool __attribute__((overloadable))
+    rsIsObject(rs_element);
+extern bool __attribute__((overloadable))
+    rsIsObject(rs_type);
+extern bool __attribute__((overloadable))
+    rsIsObject(rs_allocation);
+extern bool __attribute__((overloadable))
+    rsIsObject(rs_sampler);
+extern bool __attribute__((overloadable))
+    rsIsObject(rs_script);
+extern bool __attribute__((overloadable))
+    rsIsObject(rs_mesh);
+extern bool __attribute__((overloadable))
+    rsIsObject(rs_program_fragment);
+extern bool __attribute__((overloadable))
+    rsIsObject(rs_program_vertex);
+extern bool __attribute__((overloadable))
+    rsIsObject(rs_program_raster);
+extern bool __attribute__((overloadable))
+    rsIsObject(rs_program_store);
+extern bool __attribute__((overloadable))
+    rsIsObject(rs_font);
 
 
 
+// Allocations
+
+// Return the rs_allocation associated with a bound data
+// pointer.
+extern rs_allocation __attribute__((overloadable))
+    rsGetAllocation(const void *);
+
+// Mark the allocation dirty and notify those using it
+extern void __attribute__((overloadable))
+    rsAllocationMarkDirty(rs_allocation);
+
+// Return the dimensions associated with an allocation.
+extern uint32_t __attribute__((overloadable))
+    rsAllocationGetDimX(rs_allocation);
+extern uint32_t __attribute__((overloadable))
+    rsAllocationGetDimY(rs_allocation);
+extern uint32_t __attribute__((overloadable))
+    rsAllocationGetDimZ(rs_allocation);
+extern uint32_t __attribute__((overloadable))
+    rsAllocationGetDimLOD(rs_allocation);
+extern uint32_t __attribute__((overloadable))
+    rsAllocationGetDimFaces(rs_allocation);
+
+// Extract a single element from an allocation.
+extern const void * __attribute__((overloadable))
+    rsGetElementAt(rs_allocation, uint32_t x);
+extern const void * __attribute__((overloadable))
+    rsGetElementAt(rs_allocation, uint32_t x, uint32_t y);
+extern const void * __attribute__((overloadable))
+    rsGetElementAt(rs_allocation, uint32_t x, uint32_t y, uint32_t z);
+
+// Return a random value between 0 (or min_value) and max_malue.
+extern int __attribute__((overloadable))
+    rsRand(int max_value);
+extern int __attribute__((overloadable))
+    rsRand(int min_value, int max_value);
+extern float __attribute__((overloadable))
+    rsRand(float max_value);
+extern float __attribute__((overloadable))
+    rsRand(float min_value, float max_value);
+
+// return the fractional part of a float
+// min(v - ((int)floor(v)), 0x1.fffffep-1f);
+extern float __attribute__((overloadable))
+    rsFrac(float);
+
+// time
+extern int32_t __attribute__((overloadable))
+    rsSecond(void);
+extern int32_t __attribute__((overloadable))
+    rsMinute(void);
+extern int32_t __attribute__((overloadable))
+    rsHour(void);
+extern int32_t __attribute__((overloadable))
+    rsDay(void);
+extern int32_t __attribute__((overloadable))
+    rsMonth(void);
+extern int32_t __attribute__((overloadable))
+    rsYear(void);
+
+// Return the current system clock in milliseconds
+extern int64_t __attribute__((overloadable))
+    rsUptimeMillis(void);
+
+// Return the current system clock in nanoseconds
+extern int64_t __attribute__((overloadable))
+    rsUptimeNanos(void);
+
+// Return the time in seconds since function was last called in this script.
+extern float __attribute__((overloadable))
+    rsGetDt(void);
+
+// Send a message back to the client.  Will not block and returns true
+// if the message was sendable and false if the fifo was full.
+// A message ID is required.  Data payload is optional.
+extern bool __attribute__((overloadable))
+    rsSendToClient(int cmdID);
+extern bool __attribute__((overloadable))
+    rsSendToClient(int cmdID, const void *data, uint len);
+
+// Send a message back to the client, blocking until the message is queued.
+// A message ID is required.  Data payload is optional.
+extern void __attribute__((overloadable))
+    rsSendToClientBlocking(int cmdID);
+extern void __attribute__((overloadable))
+    rsSendToClientBlocking(int cmdID, const void *data, uint len);
 
 
+// Script to Script
+enum rs_for_each_strategy {
+    RS_FOR_EACH_STRATEGY_SERIAL,
+    RS_FOR_EACH_STRATEGY_DONT_CARE,
+    RS_FOR_EACH_STRATEGY_DST_LINEAR,
+    RS_FOR_EACH_STRATEGY_TILE_SMALL,
+    RS_FOR_EACH_STRATEGY_TILE_MEDIUM,
+    RS_FOR_EACH_STRATEGY_TILE_LARGE
+};
 
-// Int ops
+typedef struct rs_script_call {
+    enum rs_for_each_strategy strategy;
+    uint32_t xStart;
+    uint32_t xEnd;
+    uint32_t yStart;
+    uint32_t yEnd;
+    uint32_t zStart;
+    uint32_t zEnd;
+    uint32_t arrayStart;
+    uint32_t arrayEnd;
+} rs_script_call_t;
 
-extern int __attribute__((overloadable)) abs(int);
-extern int2 __attribute__((overloadable)) abs(int2);
-extern int3 __attribute__((overloadable)) abs(int3);
-extern int4 __attribute__((overloadable)) abs(int4);
-extern int8 __attribute__((overloadable)) abs(int8);
-extern int16 __attribute__((overloadable)) abs(int16);
+extern void __attribute__((overloadable))
+    rsForEach(rs_script script, rs_allocation input,
+              rs_allocation output, const void * usrData);
 
+extern void __attribute__((overloadable))
+    rsForEach(rs_script script, rs_allocation input,
+              rs_allocation output, const void * usrData,
+              const rs_script_call_t *);
 
-
-/*
-extern float modf(float, float);
-extern float randf(float);
-extern float randf2(float, float);
-extern float fracf(float);
-extern float lerpf(float, float, float);
-extern float mapf(float, float, float, float, float);
-*/
-
+#endif
diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh
index 4198a74..212eb83 100644
--- a/libs/rs/scriptc/rs_types.rsh
+++ b/libs/rs/scriptc/rs_types.rsh
@@ -1,71 +1,77 @@
+#ifndef __RS_TYPES_RSH__
+#define __RS_TYPES_RSH__
 
 typedef char int8_t;
 typedef short int16_t;
 typedef int int32_t;
-//typedef long int64_t;
+typedef long long int64_t;
 
 typedef unsigned char uint8_t;
 typedef unsigned short uint16_t;
 typedef unsigned int uint32_t;
-//typedef long uint64_t;
+typedef unsigned long long uint64_t;
 
 typedef uint8_t uchar;
 typedef uint16_t ushort;
 typedef uint32_t uint;
-//typedef uint64_t ulong;
+typedef uint64_t ulong;
 
-typedef int rs_element;
-typedef int rs_type;
-typedef int rs_allocation;
-typedef int rs_sampler;
-typedef int rs_script;
-typedef int rs_mesh;
-typedef int rs_program_fragment;
-typedef int rs_program_vertex;
-typedef int rs_program_raster;
-typedef int rs_program_store;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_element;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_type;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_allocation;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_sampler;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_script;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_mesh;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_fragment;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_vertex;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_raster;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_store;
+typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_font;
+
 
 typedef float float2 __attribute__((ext_vector_type(2)));
 typedef float float3 __attribute__((ext_vector_type(3)));
 typedef float float4 __attribute__((ext_vector_type(4)));
-typedef float float8 __attribute__((ext_vector_type(8)));
-typedef float float16 __attribute__((ext_vector_type(16)));
 
 typedef uchar uchar2 __attribute__((ext_vector_type(2)));
 typedef uchar uchar3 __attribute__((ext_vector_type(3)));
 typedef uchar uchar4 __attribute__((ext_vector_type(4)));
-typedef uchar uchar8 __attribute__((ext_vector_type(8)));
-typedef uchar uchar16 __attribute__((ext_vector_type(16)));
 
 typedef ushort ushort2 __attribute__((ext_vector_type(2)));
 typedef ushort ushort3 __attribute__((ext_vector_type(3)));
 typedef ushort ushort4 __attribute__((ext_vector_type(4)));
-typedef ushort ushort8 __attribute__((ext_vector_type(8)));
-typedef ushort ushort16 __attribute__((ext_vector_type(16)));
 
 typedef uint uint2 __attribute__((ext_vector_type(2)));
 typedef uint uint3 __attribute__((ext_vector_type(3)));
 typedef uint uint4 __attribute__((ext_vector_type(4)));
-typedef uint uint8 __attribute__((ext_vector_type(8)));
-typedef uint uint16 __attribute__((ext_vector_type(16)));
 
 typedef char char2 __attribute__((ext_vector_type(2)));
 typedef char char3 __attribute__((ext_vector_type(3)));
 typedef char char4 __attribute__((ext_vector_type(4)));
-typedef char char8 __attribute__((ext_vector_type(8)));
-typedef char char16 __attribute__((ext_vector_type(16)));
 
 typedef short short2 __attribute__((ext_vector_type(2)));
 typedef short short3 __attribute__((ext_vector_type(3)));
 typedef short short4 __attribute__((ext_vector_type(4)));
-typedef short short8 __attribute__((ext_vector_type(8)));
-typedef short short16 __attribute__((ext_vector_type(16)));
 
 typedef int int2 __attribute__((ext_vector_type(2)));
 typedef int int3 __attribute__((ext_vector_type(3)));
 typedef int int4 __attribute__((ext_vector_type(4)));
-typedef int int8 __attribute__((ext_vector_type(8)));
-typedef int int16 __attribute__((ext_vector_type(16)));
 
 
+typedef struct {
+    float m[16];
+} rs_matrix4x4;
 
+typedef struct {
+    float m[9];
+} rs_matrix3x3;
+
+typedef struct {
+    float m[4];
+} rs_matrix2x2;
+
+typedef float4 rs_quaternion;
+
+#define RS_PACKED __attribute__((packed, aligned(4)))
+
+#endif
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 9ff6930..b5f9c47 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -47,6 +47,7 @@
     TRANSACTION_unmountObb,
     TRANSACTION_isObbMounted,
     TRANSACTION_getMountedObbPath,
+    TRANSACTION_isExternalStorageEmulated,
 };
 
 class BpMountService: public BpInterface<IMountService>
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 4bc5d9e..8f583f06 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -380,11 +380,6 @@
 {
     SharedBufferStack& stack( *mSharedStack );
 
-    if (stack.head == tail && stack.available == mNumBuffers) {
-        LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d",
-                tail, stack.head, stack.available, stack.queued);
-    }
-
     RWLock::AutoRLock _rd(mLock);
 
     const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD);
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index 854a3c6..7af6ce8 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -364,6 +364,13 @@
         height   = surface->mHeight;
         format   = surface->mFormat;
         flags    = surface->mFlags;
+    } else if (surface != 0 && surface->mSurface != 0) {
+        LOGW("Parceling invalid surface with non-NULL ISurface as NULL: "
+             "mSurface = %p, mIdentity = %d, mWidth = %d, mHeight = %d, "
+             "mFormat = %d, mFlags = 0x%08x, mInitCheck = %d",
+             surface->mSurface.get(), surface->mIdentity, surface->mWidth,
+             surface->mHeight, surface->mFormat, surface->mFlags,
+             surface->mInitCheck);
     }
     parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
     parcel->writeInt32(identity);
@@ -438,21 +445,15 @@
             mSharedBufferClient = new SharedBufferClient(
                     mClient.getSharedClient(), token, 2, mIdentity);
             mInitCheck = mClient.getSharedClient()->validate(token);
+        } else {
+            LOGW("Not initializing the shared buffer client because token = %d",
+                    token);
         }
     }
 }
 
 Surface::~Surface()
 {
-    // this is a client-side operation, the surface is destroyed, unmap
-    // its buffers in this process.
-    size_t size = mBuffers.size();
-    for (size_t i=0 ; i<size ; i++) {
-        if (mBuffers[i] != 0 && mBuffers[i]->handle != 0) {
-            getBufferMapper().unregisterBuffer(mBuffers[i]->handle);
-        }
-    }
-
     // clear all references and trigger an IPC now, to make sure things
     // happen without delay, since these resources are quite heavy.
     mBuffers.clear();
@@ -1022,7 +1023,20 @@
 
 int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const
 {
-    return buffer->getIndex();
+    int idx = buffer->getIndex();
+    if (idx < 0) {
+        // The buffer doesn't have an index set.  See if the handle the same as
+        // one of the buffers for which we do know the index.  This can happen
+        // e.g. if GraphicBuffer is used to wrap an android_native_buffer_t that
+        // was dequeued from an ANativeWindow.
+        for (int i = 0; i < mBuffers.size(); i++) {
+            if (buffer->handle == mBuffers[i]->handle) {
+                idx = mBuffers[i]->getIndex();
+                break;
+            }
+        }
+    }
+    return idx;
 }
 
 status_t Surface::getBufferLocked(int index,
@@ -1036,7 +1050,6 @@
     // free the current buffer
     sp<GraphicBuffer>& currentBuffer(mBuffers.editItemAt(index));
     if (currentBuffer != 0) {
-        getBufferMapper().unregisterBuffer(currentBuffer->handle);
         currentBuffer.clear();
     }
 
@@ -1044,7 +1057,7 @@
     LOGE_IF(buffer==0,
             "ISurface::getBuffer(%d, %08x) returned NULL",
             index, usage);
-    if (buffer != 0) { // this should never happen by construction
+    if (buffer != 0) { // this should always happen by construction
         LOGE_IF(buffer->handle == NULL, 
                 "Surface (identity=%d) requestBuffer(%d, %u, %u, %u, %08x) "
                 "returned a buffer with a null handle",
@@ -1052,13 +1065,8 @@
         err = mSharedBufferClient->getStatus();
         LOGE_IF(err,  "Surface (identity=%d) state = %d", mIdentity, err);
         if (!err && buffer->handle != NULL) {
-            err = getBufferMapper().registerBuffer(buffer->handle);
-            LOGW_IF(err, "registerBuffer(...) failed %d (%s)",
-                    err, strerror(-err));
-            if (err == NO_ERROR) {
-                currentBuffer = buffer;
-                currentBuffer->setIndex(index);
-            }
+            currentBuffer = buffer;
+            currentBuffer->setIndex(index);
         } else {
             err = err<0 ? err : status_t(NO_MEMORY);
         }
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 41daa9c..9c7e7f4 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -94,7 +94,7 @@
 
 EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
     : id(_id), path(_path), name(name), classes(0)
-    , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), fd(-1), next(NULL) {
+    , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), defaultKeyMap(false), fd(-1), next(NULL) {
 }
 
 EventHub::device_t::~device_t() {
@@ -103,7 +103,7 @@
 }
 
 EventHub::EventHub(void)
-    : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0)
+    : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(-1)
     , mDevicesById(0), mNumDevicesById(0)
     , mOpeningDevices(0), mClosingDevices(0)
     , mDevices(0), mFDs(0), mFDCount(0), mOpened(false), mNeedToSendFinishedDeviceScan(false)
@@ -325,6 +325,39 @@
     mExcludedDevices.push_back(name);
 }
 
+bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
+    AutoMutex _l(mLock);
+    device_t* device = getDeviceLocked(deviceId);
+    if (device) {
+        uint8_t bitmask[sizeof_bit_array(LED_MAX + 1)];
+        memset(bitmask, 0, sizeof(bitmask));
+        if (ioctl(device->fd, EVIOCGBIT(EV_LED, sizeof(bitmask)), bitmask) >= 0) {
+            if (test_bit(led, bitmask)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) {
+    AutoMutex _l(mLock);
+    device_t* device = getDeviceLocked(deviceId);
+    if (device) {
+        struct input_event ev;
+        ev.time.tv_sec = 0;
+        ev.time.tv_usec = 0;
+        ev.type = EV_LED;
+        ev.code = led;
+        ev.value = on ? 1 : 0;
+
+        ssize_t nWrite;
+        do {
+            nWrite = write(device->fd, &ev, sizeof(struct input_event));
+        } while (nWrite == -1 && errno == EINTR);
+    }
+}
+
 EventHub::device_t* EventHub::getDeviceLocked(int32_t deviceId) const
 {
     if (deviceId == 0) deviceId = mFirstKeyboardId;
@@ -763,54 +796,42 @@
 #endif
 
     if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
-        char tmpfn[sizeof(name)];
-        char keylayoutFilename[300];
-
         // a more descriptive name
         device->name = name;
 
-        // replace all the spaces with underscores
-        strcpy(tmpfn, name);
-        for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
-            *p = '_';
+        // Configure the keymap for the device.
+        configureKeyMap(device);
 
-        // find the .kl file we need for this device
-        const char* root = getenv("ANDROID_ROOT");
-        snprintf(keylayoutFilename, sizeof(keylayoutFilename),
-                 "%s/usr/keylayout/%s.kl", root, tmpfn);
-        bool defaultKeymap = false;
-        if (access(keylayoutFilename, R_OK)) {
-            snprintf(keylayoutFilename, sizeof(keylayoutFilename),
-                     "%s/usr/keylayout/%s", root, "qwerty.kl");
-            defaultKeymap = true;
-        }
-        status_t status = device->layoutMap->load(keylayoutFilename);
-        if (status) {
-            LOGE("Error %d loading key layout.", status);
-        }
-
-        // tell the world about the devname (the descriptive name)
-        if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) {
+        // Tell the world about the devname (the descriptive name)
+        if (!mHaveFirstKeyboard && !device->defaultKeyMap && strstr(name, "-keypad")) {
             // the built-in keyboard has a well-known device ID of 0,
             // this device better not go away.
             mHaveFirstKeyboard = true;
             mFirstKeyboardId = device->id;
-            property_set("hw.keyboards.0.devname", name);
+            setKeyboardProperties(device, true);
         } else {
             // ensure mFirstKeyboardId is set to -something-.
-            if (mFirstKeyboardId == 0) {
+            if (mFirstKeyboardId == -1) {
                 mFirstKeyboardId = device->id;
+                setKeyboardProperties(device, true);
             }
         }
-        char propName[100];
-        sprintf(propName, "hw.keyboards.%u.devname", device->id);
-        property_set(propName, name);
+        setKeyboardProperties(device, false);
+
+        // Load the keylayout.
+        if (!device->keyLayoutFilename.isEmpty()) {
+            status_t status = device->layoutMap->load(device->keyLayoutFilename);
+            if (status) {
+                LOGE("Error %d loading key layout file '%s'.", status,
+                        device->keyLayoutFilename.string());
+            }
+        }
 
         // 'Q' key support = cheap test of whether this is an alpha-capable kbd
         if (hasKeycodeLocked(device, AKEYCODE_Q)) {
             device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
         }
-        
+
         // See if this device has a DPAD.
         if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
                 hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
@@ -819,7 +840,7 @@
                 hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
             device->classes |= INPUT_DEVICE_CLASS_DPAD;
         }
-        
+
         // See if this device has a gamepad.
         for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
             if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
@@ -828,8 +849,9 @@
             }
         }
 
-        LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
-                device->id, name, propName, keylayoutFilename);
+        LOGI("New keyboard: device->id=0x%x devname='%s' keylayout='%s' keycharactermap='%s'\n",
+                device->id, name,
+                device->keyLayoutFilename.string(), device->keyCharacterMapFilename.string());
     }
 
     // If the device isn't recognized as something we handle, don't monitor it.
@@ -855,6 +877,109 @@
     return 0;
 }
 
+void EventHub::configureKeyMap(device_t* device) {
+    // As an initial key map name, try using the device name.
+    String8 keyMapName(device->name);
+    char* p = keyMapName.lockBuffer(keyMapName.size());
+    while (*p) {
+        if (*p == ' ') *p = '_';
+        p++;
+    }
+    keyMapName.unlockBuffer();
+
+    if (probeKeyMap(device, keyMapName, false)) return;
+
+    // TODO Consider allowing the user to configure a specific key map somehow.
+
+    // Try the Generic key map.
+    // TODO Apply some additional heuristics here to figure out what kind of
+    //      generic key map to use (US English, etc.).
+    keyMapName.setTo("Generic");
+    if (probeKeyMap(device, keyMapName, true)) return;
+
+    // Fall back on the old style catchall qwerty key map.
+    keyMapName.setTo("qwerty");
+    if (probeKeyMap(device, keyMapName, true)) return;
+
+    // Give up!
+    keyMapName.setTo("unknown");
+    selectKeyMap(device, keyMapName, true);
+    LOGE("Could not determine key map for device '%s'.", device->name.string());
+}
+
+bool EventHub::probeKeyMap(device_t* device, const String8& keyMapName, bool defaultKeyMap) {
+    const char* root = getenv("ANDROID_ROOT");
+
+    // TODO Consider also looking somewhere in a writeable partition like /data for a
+    //      custom keymap supplied by the user for this device.
+    bool haveKeyLayout = !device->keyLayoutFilename.isEmpty();
+    if (!haveKeyLayout) {
+        device->keyLayoutFilename.setTo(root);
+        device->keyLayoutFilename.append("/usr/keylayout/");
+        device->keyLayoutFilename.append(keyMapName);
+        device->keyLayoutFilename.append(".kl");
+        if (access(device->keyLayoutFilename.string(), R_OK)) {
+            device->keyLayoutFilename.clear();
+        } else {
+            haveKeyLayout = true;
+        }
+    }
+
+    bool haveKeyCharacterMap = !device->keyCharacterMapFilename.isEmpty();
+    if (!haveKeyCharacterMap) {
+        device->keyCharacterMapFilename.setTo(root);
+        device->keyCharacterMapFilename.append("/usr/keychars/");
+        device->keyCharacterMapFilename.append(keyMapName);
+        device->keyCharacterMapFilename.append(".kcm.bin");
+        if (access(device->keyCharacterMapFilename.string(), R_OK)) {
+            device->keyCharacterMapFilename.clear();
+        } else {
+            haveKeyCharacterMap = true;
+        }
+    }
+
+    if (haveKeyLayout || haveKeyCharacterMap) {
+        selectKeyMap(device, keyMapName, defaultKeyMap);
+    }
+    return haveKeyLayout && haveKeyCharacterMap;
+}
+
+void EventHub::selectKeyMap(device_t* device,
+        const String8& keyMapName, bool defaultKeyMap) {
+    if (device->keyMapName.isEmpty()) {
+        device->keyMapName.setTo(keyMapName);
+        device->defaultKeyMap = defaultKeyMap;
+    }
+}
+
+void EventHub::setKeyboardProperties(device_t* device, bool firstKeyboard) {
+    int32_t id = firstKeyboard ? 0 : device->id;
+
+    char propName[100];
+    sprintf(propName, "hw.keyboards.%u.devname", id);
+    property_set(propName, device->name.string());
+    sprintf(propName, "hw.keyboards.%u.keymap", id);
+    property_set(propName, device->keyMapName.string());
+    sprintf(propName, "hw.keyboards.%u.klfile", id);
+    property_set(propName, device->keyLayoutFilename.string());
+    sprintf(propName, "hw.keyboards.%u.kcmfile", id);
+    property_set(propName, device->keyCharacterMapFilename.string());
+}
+
+void EventHub::clearKeyboardProperties(device_t* device, bool firstKeyboard) {
+    int32_t id = firstKeyboard ? 0 : device->id;
+
+    char propName[100];
+    sprintf(propName, "hw.keyboards.%u.devname", id);
+    property_set(propName, "");
+    sprintf(propName, "hw.keyboards.%u.keymap", id);
+    property_set(propName, "");
+    sprintf(propName, "hw.keyboards.%u.klfile", id);
+    property_set(propName, "");
+    sprintf(propName, "hw.keyboards.%u.kcmfile", id);
+    property_set(propName, "");
+}
+
 bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const
 {
     if (device->keyBitmask == NULL || device->layoutMap == NULL) {
@@ -912,13 +1037,10 @@
             if (device->id == mFirstKeyboardId) {
                 LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
                         device->path.string(), mFirstKeyboardId);
-                mFirstKeyboardId = 0;
-                property_set("hw.keyboards.0.devname", NULL);
+                mFirstKeyboardId = -1;
+                clearKeyboardProperties(device, true);
             }
-            // clear the property
-            char propName[100];
-            sprintf(propName, "hw.keyboards.%u.devname", device->id);
-            property_set(propName, NULL);
+            clearKeyboardProperties(device, false);
             return 0;
         }
     }
@@ -1017,7 +1139,11 @@
                 }
                 dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes);
                 dump.appendFormat(INDENT3 "Path: %s\n", device->path.string());
-                dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n", device->keylayoutFilename.string());
+                dump.appendFormat(INDENT3 "KeyMapName: %s\n", device->keyMapName.string());
+                dump.appendFormat(INDENT3 "KeyLayoutFilename: %s\n",
+                        device->keyLayoutFilename.string());
+                dump.appendFormat(INDENT3 "KeyCharacterMapFilename: %s\n",
+                        device->keyCharacterMapFilename.string());
             }
         }
     } // release lock
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 519c277..436e064 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -77,6 +77,19 @@
     handle = inHandle;
 }
 
+GraphicBuffer::GraphicBuffer(android_native_buffer_t* buffer, bool keepOwnership)
+    : BASE(), mOwner(keepOwnership ? ownHandle : ownNone),
+      mBufferMapper(GraphicBufferMapper::get()),
+      mInitCheck(NO_ERROR), mIndex(-1), mWrappedBuffer(buffer)
+{
+    width  = buffer->width;
+    height = buffer->height;
+    stride = buffer->stride;
+    format = buffer->format;
+    usage  = buffer->usage;
+    handle = buffer->handle;
+}
+
 GraphicBuffer::~GraphicBuffer()
 {
     if (handle) {
@@ -87,12 +100,14 @@
 void GraphicBuffer::free_handle()
 {
     if (mOwner == ownHandle) {
+        mBufferMapper.unregisterBuffer(handle);
         native_handle_close(handle);
         native_handle_delete(const_cast<native_handle*>(handle));
     } else if (mOwner == ownData) {
         GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
         allocator.free(handle);
     }
+    mWrappedBuffer = 0;
 }
 
 status_t GraphicBuffer::initCheck() const {
@@ -253,6 +268,11 @@
     }
 
     mOwner = ownHandle;
+
+    if (handle != 0) {
+        mBufferMapper.registerBuffer(handle);
+    }
+
     return NO_ERROR;
 }
 
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 811edaf..944a79b 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -33,6 +33,7 @@
         case AKEYCODE_ENDCALL:
         case AKEYCODE_VOLUME_UP:
         case AKEYCODE_VOLUME_DOWN:
+        case AKEYCODE_VOLUME_MUTE:
         case AKEYCODE_POWER:
         case AKEYCODE_CAMERA:
         case AKEYCODE_HEADSETHOOK:
@@ -40,11 +41,14 @@
         case AKEYCODE_NOTIFICATION:
         case AKEYCODE_FOCUS:
         case AKEYCODE_SEARCH:
+        case AKEYCODE_MEDIA_PLAY:
+        case AKEYCODE_MEDIA_PAUSE:
         case AKEYCODE_MEDIA_PLAY_PAUSE:
         case AKEYCODE_MEDIA_STOP:
         case AKEYCODE_MEDIA_NEXT:
         case AKEYCODE_MEDIA_PREVIOUS:
         case AKEYCODE_MEDIA_REWIND:
+        case AKEYCODE_MEDIA_RECORD:
         case AKEYCODE_MEDIA_FAST_FORWARD:
         case AKEYCODE_MUTE:
             return true;
@@ -67,14 +71,18 @@
         case AKEYCODE_ENDCALL:
         case AKEYCODE_VOLUME_UP:
         case AKEYCODE_VOLUME_DOWN:
+        case AKEYCODE_VOLUME_MUTE:
         case AKEYCODE_MUTE:
         case AKEYCODE_POWER:
         case AKEYCODE_HEADSETHOOK:
+        case AKEYCODE_MEDIA_PLAY:
+        case AKEYCODE_MEDIA_PAUSE:
         case AKEYCODE_MEDIA_PLAY_PAUSE:
         case AKEYCODE_MEDIA_STOP:
         case AKEYCODE_MEDIA_NEXT:
         case AKEYCODE_MEDIA_PREVIOUS:
         case AKEYCODE_MEDIA_REWIND:
+        case AKEYCODE_MEDIA_RECORD:
         case AKEYCODE_MEDIA_FAST_FORWARD:
         case AKEYCODE_CAMERA:
         case AKEYCODE_FOCUS:
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index c0b27fe..f9c0b91 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -619,6 +619,38 @@
 bool InputDispatcher::dispatchKeyLocked(
         nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout,
         DropReason* dropReason, nsecs_t* nextWakeupTime) {
+    // Preprocessing.
+    if (! entry->dispatchInProgress) {
+        if (entry->repeatCount == 0
+                && entry->action == AKEY_EVENT_ACTION_DOWN
+                && (entry->policyFlags & POLICY_FLAG_TRUSTED)
+                && !entry->isInjected()) {
+            if (mKeyRepeatState.lastKeyEntry
+                    && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
+                // We have seen two identical key downs in a row which indicates that the device
+                // driver is automatically generating key repeats itself.  We take note of the
+                // repeat here, but we disable our own next key repeat timer since it is clear that
+                // we will not need to synthesize key repeats ourselves.
+                entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
+                resetKeyRepeatLocked();
+                mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
+            } else {
+                // Not a repeat.  Save key down state in case we do see a repeat later.
+                resetKeyRepeatLocked();
+                mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
+            }
+            mKeyRepeatState.lastKeyEntry = entry;
+            entry->refCount += 1;
+        } else if (! entry->syntheticRepeat) {
+            resetKeyRepeatLocked();
+        }
+
+        entry->dispatchInProgress = true;
+        resetTargetsLocked();
+
+        logOutboundKeyDetailsLocked("dispatchKey - ", entry);
+    }
+
     // Give the policy a chance to intercept the key.
     if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
         if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
@@ -647,38 +679,6 @@
         return true;
     }
 
-    // Preprocessing.
-    if (! entry->dispatchInProgress) {
-        logOutboundKeyDetailsLocked("dispatchKey - ", entry);
-
-        if (entry->repeatCount == 0
-                && entry->action == AKEY_EVENT_ACTION_DOWN
-                && (entry->policyFlags & POLICY_FLAG_TRUSTED)
-                && !entry->isInjected()) {
-            if (mKeyRepeatState.lastKeyEntry
-                    && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
-                // We have seen two identical key downs in a row which indicates that the device
-                // driver is automatically generating key repeats itself.  We take note of the
-                // repeat here, but we disable our own next key repeat timer since it is clear that
-                // we will not need to synthesize key repeats ourselves.
-                entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
-                resetKeyRepeatLocked();
-                mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
-            } else {
-                // Not a repeat.  Save key down state in case we do see a repeat later.
-                resetKeyRepeatLocked();
-                mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout;
-            }
-            mKeyRepeatState.lastKeyEntry = entry;
-            entry->refCount += 1;
-        } else if (! entry->syntheticRepeat) {
-            resetKeyRepeatLocked();
-        }
-
-        entry->dispatchInProgress = true;
-        resetTargetsLocked();
-    }
-
     // Identify targets.
     if (! mCurrentInputTargetsValid) {
         int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
@@ -705,16 +705,24 @@
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, "
             "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, "
-            "downTime=%lld",
+            "repeatCount=%d, downTime=%lld",
             prefix,
             entry->eventTime, entry->deviceId, entry->source, entry->policyFlags,
             entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
-            entry->downTime);
+            entry->repeatCount, entry->downTime);
 #endif
 }
 
 bool InputDispatcher::dispatchMotionLocked(
         nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
+    // Preprocessing.
+    if (! entry->dispatchInProgress) {
+        entry->dispatchInProgress = true;
+        resetTargetsLocked();
+
+        logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
+    }
+
     // Clean up if dropping the event.
     if (*dropReason != DROP_REASON_NOT_DROPPED) {
         resetTargetsLocked();
@@ -723,14 +731,6 @@
         return true;
     }
 
-    // Preprocessing.
-    if (! entry->dispatchInProgress) {
-        logOutboundMotionDetailsLocked("dispatchMotion - ", entry);
-
-        entry->dispatchInProgress = true;
-        resetTargetsLocked();
-    }
-
     bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
 
     // Identify targets.
@@ -1163,7 +1163,10 @@
 
         // If the pointer is not currently down, then ignore the event.
         if (! mTempTouchState.down) {
-            LOGI("Dropping event because the pointer is not down.");
+#if DEBUG_INPUT_DISPATCHER_POLICY
+            LOGD("Dropping event because the pointer is not down or we previously "
+                    "dropped the pointer down event.");
+#endif
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
         }
@@ -1405,8 +1408,13 @@
 
 void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
     int32_t eventType = POWER_MANAGER_BUTTON_EVENT;
-    if (eventEntry->type == EventEntry::TYPE_MOTION) {
+    switch (eventEntry->type) {
+    case EventEntry::TYPE_MOTION: {
         const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry);
+        if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
+            return;
+        }
+
         if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
             switch (motionEntry->action) {
             case AMOTION_EVENT_ACTION_DOWN:
@@ -1424,6 +1432,15 @@
                 break;
             }
         }
+        break;
+    }
+    case EventEntry::TYPE_KEY: {
+        const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry);
+        if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) {
+            return;
+        }
+        break;
+    }
     }
 
     CommandEntry* commandEntry = postCommandLocked(
@@ -2615,6 +2632,82 @@
     }
 }
 
+bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel,
+        const sp<InputChannel>& toChannel) {
+#if DEBUG_FOCUS
+    LOGD("transferTouchFocus: fromChannel=%s, toChannel=%s",
+            fromChannel->getName().string(), toChannel->getName().string());
+#endif
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        const InputWindow* fromWindow = getWindowLocked(fromChannel);
+        const InputWindow* toWindow = getWindowLocked(toChannel);
+        if (! fromWindow || ! toWindow) {
+#if DEBUG_FOCUS
+            LOGD("Cannot transfer focus because from or to window not found.");
+#endif
+            return false;
+        }
+        if (fromWindow == toWindow) {
+#if DEBUG_FOCUS
+            LOGD("Trivial transfer to same window.");
+#endif
+            return true;
+        }
+
+        bool found = false;
+        for (size_t i = 0; i < mTouchState.windows.size(); i++) {
+            const TouchedWindow& touchedWindow = mTouchState.windows[i];
+            if (touchedWindow.window == fromWindow) {
+                int32_t oldTargetFlags = touchedWindow.targetFlags;
+                BitSet32 pointerIds = touchedWindow.pointerIds;
+
+                mTouchState.windows.removeAt(i);
+
+                int32_t newTargetFlags = 0;
+                if (oldTargetFlags & InputTarget::FLAG_FOREGROUND) {
+                    newTargetFlags |= InputTarget::FLAG_FOREGROUND;
+                    if (toWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH) {
+                        newTargetFlags |= InputTarget::FLAG_SPLIT;
+                    }
+                }
+                mTouchState.addOrUpdateWindow(toWindow, newTargetFlags, pointerIds);
+
+                found = true;
+                break;
+            }
+        }
+
+        if (! found) {
+#if DEBUG_FOCUS
+            LOGD("Focus transfer failed because from window did not have focus.");
+#endif
+            return false;
+        }
+
+        ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel);
+        ssize_t toConnectionIndex = getConnectionIndexLocked(toChannel);
+        if (fromConnectionIndex >= 0 && toConnectionIndex >= 0) {
+            sp<Connection> fromConnection = mConnectionsByReceiveFd.valueAt(fromConnectionIndex);
+            sp<Connection> toConnection = mConnectionsByReceiveFd.valueAt(toConnectionIndex);
+
+            fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
+            synthesizeCancelationEventsForConnectionLocked(fromConnection,
+                    InputState::CANCEL_POINTER_EVENTS,
+                    "transferring touch focus from this window to another window");
+        }
+
+#if DEBUG_FOCUS
+        logDispatchStateLocked();
+#endif
+    } // release lock
+
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mLooper->wake();
+    return true;
+}
+
 void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
 #if DEBUG_FOCUS
     LOGD("Resetting and dropping all events (%s).", reason);
@@ -2721,7 +2814,7 @@
         dump.append(INDENT "ActiveConnections:\n");
         for (size_t i = 0; i < mActiveConnections.size(); i++) {
             const Connection* connection = mActiveConnections[i];
-            dump.appendFormat(INDENT2 "%d: '%s', status=%s, outboundQueueLength=%u"
+            dump.appendFormat(INDENT2 "%d: '%s', status=%s, outboundQueueLength=%u, "
                     "inputState.isNeutral=%s\n",
                     i, connection->getInputChannelName(), connection->getStatusLabel(),
                     connection->outboundQueue.count(),
@@ -3356,6 +3449,24 @@
     mMotionMementos.clear();
 }
 
+void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const {
+    for (size_t i = 0; i < mMotionMementos.size(); i++) {
+        const MotionMemento& memento = mMotionMementos.itemAt(i);
+        if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
+            for (size_t j = 0; j < other.mMotionMementos.size(); ) {
+                const MotionMemento& otherMemento = other.mMotionMementos.itemAt(j);
+                if (memento.deviceId == otherMemento.deviceId
+                        && memento.source == otherMemento.source) {
+                    other.mMotionMementos.removeAt(j);
+                } else {
+                    j += 1;
+                }
+            }
+            other.mMotionMementos.push(memento);
+        }
+    }
+}
+
 bool InputDispatcher::InputState::shouldCancelEvent(int32_t eventSource,
         CancelationOptions options) {
     switch (options) {
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index d167439..b91e93a 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -70,32 +70,15 @@
     return value ? "true" : "false";
 }
 
-
-int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
-    int32_t mask;
-    switch (keyCode) {
-    case AKEYCODE_ALT_LEFT:
-        mask = AMETA_ALT_LEFT_ON;
-        break;
-    case AKEYCODE_ALT_RIGHT:
-        mask = AMETA_ALT_RIGHT_ON;
-        break;
-    case AKEYCODE_SHIFT_LEFT:
-        mask = AMETA_SHIFT_LEFT_ON;
-        break;
-    case AKEYCODE_SHIFT_RIGHT:
-        mask = AMETA_SHIFT_RIGHT_ON;
-        break;
-    case AKEYCODE_SYM:
-        mask = AMETA_SYM_ON;
-        break;
-    default:
-        return oldMetaState;
+int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
+    int32_t newMetaState;
+    if (down) {
+        newMetaState = oldMetaState | mask;
+    } else {
+        newMetaState = oldMetaState &
+                ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON);
     }
 
-    int32_t newMetaState = down ? oldMetaState | mask : oldMetaState & ~ mask
-            & ~ (AMETA_ALT_ON | AMETA_SHIFT_ON);
-
     if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
         newMetaState |= AMETA_ALT_ON;
     }
@@ -104,9 +87,58 @@
         newMetaState |= AMETA_SHIFT_ON;
     }
 
+    if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
+        newMetaState |= AMETA_CTRL_ON;
+    }
+
+    if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) {
+        newMetaState |= AMETA_META_ON;
+    }
     return newMetaState;
 }
 
+int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) {
+    if (down) {
+        return oldMetaState;
+    } else {
+        return oldMetaState ^ mask;
+    }
+}
+
+int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) {
+    int32_t mask;
+    switch (keyCode) {
+    case AKEYCODE_ALT_LEFT:
+        return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState);
+    case AKEYCODE_ALT_RIGHT:
+        return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState);
+    case AKEYCODE_SHIFT_LEFT:
+        return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState);
+    case AKEYCODE_SHIFT_RIGHT:
+        return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState);
+    case AKEYCODE_SYM:
+        return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState);
+    case AKEYCODE_FUNCTION:
+        return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState);
+    case AKEYCODE_CTRL_LEFT:
+        return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState);
+    case AKEYCODE_CTRL_RIGHT:
+        return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState);
+    case AKEYCODE_META_LEFT:
+        return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState);
+    case AKEYCODE_META_RIGHT:
+        return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState);
+    case AKEYCODE_CAPS_LOCK:
+        return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState);
+    case AKEYCODE_NUM_LOCK:
+        return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState);
+    case AKEYCODE_SCROLL_LOCK:
+        return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState);
+    default:
+        return oldMetaState;
+    }
+}
+
 static const int32_t keyCodeRotationMap[][4] = {
         // key codes enumerated counter-clockwise with the original (unrotated) key first
         // no rotation,        90 degree rotation,  180 degree rotation, 270 degree rotation
@@ -841,6 +873,17 @@
 void KeyboardInputMapper::initializeLocked() {
     mLocked.metaState = AMETA_NONE;
     mLocked.downTime = 0;
+
+    initializeLedStateLocked(mLocked.capsLockLedState, LED_CAPSL);
+    initializeLedStateLocked(mLocked.numLockLedState, LED_NUML);
+    initializeLedStateLocked(mLocked.scrollLockLedState, LED_SCROLLL);
+
+    updateLedStateLocked(true);
+}
+
+void KeyboardInputMapper::initializeLedStateLocked(LockedState::LedState& ledState, int32_t led) {
+    ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
+    ledState.on = false;
 }
 
 uint32_t KeyboardInputMapper::getSources() {
@@ -923,8 +966,8 @@
             // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
             if (mAssociatedDisplayId >= 0) {
                 int32_t orientation;
-                if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
-                    return;
+                if (!getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
+                    orientation = InputReaderPolicyInterface::ROTATION_0;
                 }
 
                 keyCode = rotateKeyCode(keyCode, orientation);
@@ -965,6 +1008,7 @@
         if (oldMetaState != newMetaState) {
             mLocked.metaState = newMetaState;
             metaStateChanged = true;
+            updateLedStateLocked(false);
         }
 
         downTime = mLocked.downTime;
@@ -974,6 +1018,9 @@
         getContext()->updateGlobalMetaState();
     }
 
+    if (policyFlags & POLICY_FLAG_FUNCTION) {
+        newMetaState |= AMETA_FUNCTION_ON;
+    }
     getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
             down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
             AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
@@ -1009,6 +1056,26 @@
     } // release lock
 }
 
+void KeyboardInputMapper::updateLedStateLocked(bool reset) {
+    updateLedStateForModifierLocked(mLocked.capsLockLedState, LED_CAPSL,
+            AMETA_CAPS_LOCK_ON, reset);
+    updateLedStateForModifierLocked(mLocked.numLockLedState, LED_NUML,
+            AMETA_NUM_LOCK_ON, reset);
+    updateLedStateForModifierLocked(mLocked.scrollLockLedState, LED_SCROLLL,
+            AMETA_SCROLL_LOCK_ON, reset);
+}
+
+void KeyboardInputMapper::updateLedStateForModifierLocked(LockedState::LedState& ledState,
+        int32_t led, int32_t modifier, bool reset) {
+    if (ledState.avail) {
+        bool desiredState = (mLocked.metaState & modifier) != 0;
+        if (ledState.on != desiredState) {
+            getEventHub()->setLedState(getDeviceId(), led, desiredState);
+            ledState.on = desiredState;
+        }
+    }
+}
+
 
 // --- TrackballInputMapper ---
 
@@ -1161,7 +1228,7 @@
             // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
             int32_t orientation;
             if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) {
-                return;
+                orientation = InputReaderPolicyInterface::ROTATION_0;
             }
 
             float temp;
@@ -1276,7 +1343,7 @@
         dumpRawAxes(dump);
         dumpCalibration(dump);
         dumpSurfaceLocked(dump);
-        dump.appendFormat(INDENT3 "Translation and Scaling Factors:");
+        dump.appendFormat(INDENT3 "Translation and Scaling Factors:\n");
         dump.appendFormat(INDENT4 "XOrigin: %d\n", mLocked.xOrigin);
         dump.appendFormat(INDENT4 "YOrigin: %d\n", mLocked.yOrigin);
         dump.appendFormat(INDENT4 "XScale: %0.3f\n", mLocked.xScale);
@@ -1413,10 +1480,18 @@
 
         // Configure X and Y factors.
         if (mRawAxes.x.valid && mRawAxes.y.valid) {
-            mLocked.xOrigin = mRawAxes.x.minValue;
-            mLocked.yOrigin = mRawAxes.y.minValue;
-            mLocked.xScale = float(width) / mRawAxes.x.getRange();
-            mLocked.yScale = float(height) / mRawAxes.y.getRange();
+            mLocked.xOrigin = mCalibration.haveXOrigin
+                    ? mCalibration.xOrigin
+                    : mRawAxes.x.minValue;
+            mLocked.yOrigin = mCalibration.haveYOrigin
+                    ? mCalibration.yOrigin
+                    : mRawAxes.y.minValue;
+            mLocked.xScale = mCalibration.haveXScale
+                    ? mCalibration.xScale
+                    : float(width) / mRawAxes.x.getRange();
+            mLocked.yScale = mCalibration.haveYScale
+                    ? mCalibration.yScale
+                    : float(height) / mRawAxes.y.getRange();
             mLocked.xPrecision = 1.0f / mLocked.xScale;
             mLocked.yPrecision = 1.0f / mLocked.yScale;
 
@@ -1682,6 +1757,12 @@
     const InputDeviceCalibration& in = getDevice()->getCalibration();
     Calibration& out = mCalibration;
 
+    // Position
+    out.haveXOrigin = in.tryGetProperty(String8("touch.position.xOrigin"), out.xOrigin);
+    out.haveYOrigin = in.tryGetProperty(String8("touch.position.yOrigin"), out.yOrigin);
+    out.haveXScale = in.tryGetProperty(String8("touch.position.xScale"), out.xScale);
+    out.haveYScale = in.tryGetProperty(String8("touch.position.yScale"), out.yScale);
+
     // Touch Size
     out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_DEFAULT;
     String8 touchSizeCalibrationString;
@@ -1891,6 +1972,20 @@
 void TouchInputMapper::dumpCalibration(String8& dump) {
     dump.append(INDENT3 "Calibration:\n");
 
+    // Position
+    if (mCalibration.haveXOrigin) {
+        dump.appendFormat(INDENT4 "touch.position.xOrigin: %d\n", mCalibration.xOrigin);
+    }
+    if (mCalibration.haveYOrigin) {
+        dump.appendFormat(INDENT4 "touch.position.yOrigin: %d\n", mCalibration.yOrigin);
+    }
+    if (mCalibration.haveXScale) {
+        dump.appendFormat(INDENT4 "touch.position.xScale: %0.3f\n", mCalibration.xScale);
+    }
+    if (mCalibration.haveYScale) {
+        dump.appendFormat(INDENT4 "touch.position.yScale: %0.3f\n", mCalibration.yScale);
+    }
+
     // Touch Size
     switch (mCalibration.touchSizeCalibration) {
     case Calibration::TOUCH_SIZE_CALIBRATION_NONE:
diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp
index e891181..870a45c 100644
--- a/libs/ui/KeyCharacterMap.cpp
+++ b/libs/ui/KeyCharacterMap.cpp
@@ -156,26 +156,38 @@
 KeyCharacterMap*
 KeyCharacterMap::load(int id)
 {
-    KeyCharacterMap* rv = NULL;
+    KeyCharacterMap* map;
     char path[PATH_MAX];
     char propName[100];
     char dev[PROPERTY_VALUE_MAX];
-    char tmpfn[PROPERTY_VALUE_MAX];
+    char fn[PROPERTY_VALUE_MAX];
     int err;
+
+    // Check whether the EventHub has set a key character map filename for us already.
+    sprintf(propName, "hw.keyboards.%u.kcmfile", id);
+    err = property_get(propName, fn, "");
+    if (err > 0) {
+        map = try_file(fn);
+        if (map) {
+            return map;
+        }
+        LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, fn);
+    }
+
+    // Try using the device name.
     const char* root = getenv("ANDROID_ROOT");
 
     sprintf(propName, "hw.keyboards.%u.devname", id);
     err = property_get(propName, dev, "");
     if (err > 0) {
         // replace all the spaces with underscores
-        strcpy(tmpfn, dev);
-        for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
+        strcpy(fn, dev);
+        for (char *p = strchr(fn, ' '); p && *p; p = strchr(p + 1, ' '))
             *p = '_';
-        snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, tmpfn);
-        //LOGD("load: dev='%s' path='%s'\n", dev, path);
-        rv = try_file(path);
-        if (rv != NULL) {
-            return rv;
+        snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, fn);
+        map = try_file(path);
+        if (map) {
+            return map;
         }
         LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, dev);
     } else {
@@ -183,14 +195,14 @@
     }
 
     snprintf(path, sizeof(path), "%s/usr/keychars/qwerty.kcm.bin", root);
-    rv = try_file(path);
-    if (rv == NULL) {
-        LOGE("Can't find any keycharmaps (also tried %s)", path);
-        return NULL;
+    map = try_file(path);
+    if (map) {
+        LOGW("Using default keymap: %s", path);
+        return map;
     }
-    LOGW("Using default keymap: %s", path);
 
-    return rv;
+    LOGE("Can't find any keycharmaps (also tried %s)", path);
+    return NULL;
 }
 
 KeyCharacterMap*
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 12db908..1994f6a 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -289,7 +289,7 @@
     void flushSpan() {
         bool merge = false;
         if (tail-head == ssize_t(span.size())) {
-            Rect const* p = cur;
+            Rect const* p = span.editArray();
             Rect const* q = head;
             if (p->top == q->bottom) {
                 merge = true;
diff --git a/libs/ui/tests/InputReader_test.cpp b/libs/ui/tests/InputReader_test.cpp
index de4b05a..ded0225 100644
--- a/libs/ui/tests/InputReader_test.cpp
+++ b/libs/ui/tests/InputReader_test.cpp
@@ -342,6 +342,12 @@
         ADD_FAILURE() << "Should never be called by input reader.";
     }
 
+    virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel,
+            const sp<InputChannel>& toChannel) {
+        ADD_FAILURE() << "Should never be called by input reader.";
+        return 0;
+    }
+
     virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) {
         ADD_FAILURE() << "Should never be called by input reader.";
         return 0;
@@ -370,6 +376,7 @@
         KeyedVector<int32_t, int32_t> scanCodeStates;
         KeyedVector<int32_t, int32_t> switchStates;
         KeyedVector<int32_t, KeyInfo> keys;
+        KeyedVector<int32_t, bool> leds;
 
         Device(const String8& name, uint32_t classes) :
                 name(name), classes(classes) {
@@ -444,6 +451,16 @@
         device->keys.add(scanCode, info);
     }
 
+    void addLed(int32_t deviceId, int32_t led, bool initialState) {
+        Device* device = getDevice(deviceId);
+        device->leds.add(led, initialState);
+    }
+
+    bool getLedState(int32_t deviceId, int32_t led) {
+        Device* device = getDevice(deviceId);
+        return device->leds.valueFor(led);
+    }
+
     Vector<String8>& getExcludedDevices() {
         return mExcludedDevices;
     }
@@ -577,6 +594,25 @@
         return result;
     }
 
+    virtual bool hasLed(int32_t deviceId, int32_t led) const {
+        Device* device = getDevice(deviceId);
+        return device && device->leds.indexOfKey(led) >= 0;
+    }
+
+    virtual void setLedState(int32_t deviceId, int32_t led, bool on) {
+        Device* device = getDevice(deviceId);
+        if (device) {
+            ssize_t index = device->leds.indexOfKey(led);
+            if (index >= 0) {
+                device->leds.replaceValueAt(led, on);
+            } else {
+                ADD_FAILURE()
+                        << "Attempted to set the state of an LED that the EventHub declared "
+                        "was not present.  led=" << led;
+            }
+        }
+    }
+
     virtual void dump(String8& dump) {
     }
 };
@@ -1690,6 +1726,81 @@
     ASSERT_FALSE(flags[1]);
 }
 
+TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) {
+    mFakeEventHub->addLed(DEVICE_ID, LED_CAPSL, true /*initially on*/);
+    mFakeEventHub->addLed(DEVICE_ID, LED_NUML, false /*initially off*/);
+    mFakeEventHub->addLed(DEVICE_ID, LED_SCROLLL, false /*initially off*/);
+
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1,
+            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    addMapperAndConfigure(mapper);
+
+    // Initialization should have turned all of the lights off.
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+
+    // Toggle caps lock on.
+    process(mapper, ARBITRARY_TIME, DEVICE_ID,
+            EV_KEY, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID,
+            EV_KEY, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 0, 0);
+    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper->getMetaState());
+
+    // Toggle num lock on.
+    process(mapper, ARBITRARY_TIME, DEVICE_ID,
+            EV_KEY, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID,
+            EV_KEY, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, 0, 0);
+    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper->getMetaState());
+
+    // Toggle caps lock off.
+    process(mapper, ARBITRARY_TIME, DEVICE_ID,
+            EV_KEY, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID,
+            EV_KEY, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 1, 0);
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper->getMetaState());
+
+    // Toggle scroll lock on.
+    process(mapper, ARBITRARY_TIME, DEVICE_ID,
+            EV_KEY, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID,
+            EV_KEY, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0, 0);
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
+    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper->getMetaState());
+
+    // Toggle num lock off.
+    process(mapper, ARBITRARY_TIME, DEVICE_ID,
+            EV_KEY, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID,
+            EV_KEY, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, 0, 0);
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
+    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper->getMetaState());
+
+    // Toggle scroll lock off.
+    process(mapper, ARBITRARY_TIME, DEVICE_ID,
+            EV_KEY, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 1, 0);
+    process(mapper, ARBITRARY_TIME, DEVICE_ID,
+            EV_KEY, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0, 0);
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
+}
+
 
 // --- TrackballInputMapperTest ---
 
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 8345cc3..f287298 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -317,6 +317,12 @@
             mStringPoolSize =
                 (mHeader->header.size-mHeader->stringsStart)/charSize;
         } else {
+            // check invariant: styles starts before end of data
+            if (mHeader->stylesStart >= (mHeader->header.size-sizeof(uint16_t))) {
+                LOGW("Bad style block: style block starts at %d past data size of %d\n",
+                    (int)mHeader->stylesStart, (int)mHeader->header.size);
+                return (mError=BAD_TYPE);
+            }
             // check invariant: styles follow the strings
             if (mHeader->stylesStart <= mHeader->stringsStart) {
                 LOGW("Bad style block: style block starts at %d, before strings at %d\n",
@@ -1878,13 +1884,19 @@
         outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen);
         outName->name = grp->basePackage->keyStrings.stringAt(
             dtohl(entry->key.index), &outName->nameLen);
+
+        // If we have a bad index for some reason, we should abort.
+        if (outName->type == NULL || outName->name == NULL) {
+            return false;
+        }
+
         return true;
     }
 
     return false;
 }
 
-ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag,
+ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density,
         uint32_t* outSpecFlags, ResTable_config* outConfig) const
 {
     if (mError != NO_ERROR) {
@@ -1914,7 +1926,7 @@
     memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up
 
     if (outSpecFlags != NULL) *outSpecFlags = 0;
-    
+
     // Look through all resource packages, starting with the most
     // recently added.
     const PackageGroup* const grp = mPackageGroups[p];
@@ -1922,6 +1934,22 @@
         LOGW("Bad identifier when getting value for resource number 0x%08x", resID);
         return BAD_INDEX;
     }
+
+    // Allow overriding density
+    const ResTable_config* desiredConfig = &mParams;
+    ResTable_config* overrideConfig = NULL;
+    if (density > 0) {
+        overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config));
+        if (overrideConfig == NULL) {
+            LOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno));
+            return BAD_INDEX;
+        }
+        memcpy(overrideConfig, &mParams, sizeof(ResTable_config));
+        overrideConfig->density = density;
+        desiredConfig = overrideConfig;
+    }
+
+    ssize_t rc = BAD_VALUE;
     size_t ip = grp->packages.size();
     while (ip > 0) {
         ip--;
@@ -1931,12 +1959,13 @@
         const ResTable_type* type;
         const ResTable_entry* entry;
         const Type* typeClass;
-        ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass);
+        ssize_t offset = getEntry(package, t, e, desiredConfig, &type, &entry, &typeClass);
         if (offset <= 0) {
             if (offset < 0) {
                 LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n",
                         resID, t, e, ip, (int)offset);
-                return offset;
+                rc = offset;
+                goto out;
             }
             continue;
         }
@@ -1951,13 +1980,14 @@
 
         TABLE_NOISY(aout << "Resource type data: "
               << HexDump(type, dtohl(type->header.size)) << endl);
-        
+
         if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) {
             LOGW("ResTable_item at %d is beyond type chunk data %d",
                  (int)offset, dtohl(type->header.size));
-            return BAD_TYPE;
+            rc = BAD_TYPE;
+            goto out;
         }
-        
+
         const Res_value* item =
             (const Res_value*)(((const uint8_t*)type) + offset);
         ResTable_config thisConfig;
@@ -1999,10 +2029,16 @@
                          outValue->data, &len)).string()
                      : "",
                      outValue->data));
-        return bestPackage->header->index;
+        rc = bestPackage->header->index;
+        goto out;
     }
 
-    return BAD_VALUE;
+out:
+    if (overrideConfig != NULL) {
+        free(overrideConfig);
+    }
+
+    return rc;
 }
 
 ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
@@ -2015,7 +2051,7 @@
         if (outLastRef) *outLastRef = value->data;
         uint32_t lastRef = value->data;
         uint32_t newFlags = 0;
-        const ssize_t newIndex = getResource(value->data, value, true, &newFlags,
+        const ssize_t newIndex = getResource(value->data, value, true, 0, &newFlags,
                 outConfig);
         if (newIndex == BAD_INDEX) {
             return BAD_INDEX;
@@ -2609,6 +2645,24 @@
         *outType = *defType;
     }
     *outName = String16(p, end-p);
+    if(**outPackage == 0) {
+        if(outErrorMsg) {
+            *outErrorMsg = "Resource package cannot be an empty string";
+        }
+        return false;
+    }
+    if(**outType == 0) {
+        if(outErrorMsg) {
+            *outErrorMsg = "Resource type cannot be an empty string";
+        }
+        return false;
+    }
+    if(**outName == 0) {
+        if(outErrorMsg) {
+            *outErrorMsg = "Resource id cannot be an empty string";
+        }
+        return false;
+    }
     return true;
 }
 
@@ -4127,13 +4181,16 @@
                                     | (0x00ff0000 & ((typeIndex+1)<<16))
                                     | (0x0000ffff & (entryIndex));
                         resource_name resName;
-                        this->getResourceName(resID, &resName);
-                        printf("      spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
-                            resID,
-                            CHAR16_TO_CSTR(resName.package, resName.packageLen),
-                            CHAR16_TO_CSTR(resName.type, resName.typeLen),
-                            CHAR16_TO_CSTR(resName.name, resName.nameLen),
-                            dtohl(typeConfigs->typeSpecFlags[entryIndex]));
+                        if (this->getResourceName(resID, &resName)) {
+                            printf("      spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
+                                resID,
+                                CHAR16_TO_CSTR(resName.package, resName.packageLen),
+                                CHAR16_TO_CSTR(resName.type, resName.typeLen),
+                                CHAR16_TO_CSTR(resName.name, resName.nameLen),
+                                dtohl(typeConfigs->typeSpecFlags[entryIndex]));
+                        } else {
+                            printf("      INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
+                        }
                     }
                 }
                 for (size_t configIndex=0; configIndex<NTC; configIndex++) {
@@ -4340,11 +4397,14 @@
                                     | (0x00ff0000 & ((typeIndex+1)<<16))
                                     | (0x0000ffff & (entryIndex));
                         resource_name resName;
-                        this->getResourceName(resID, &resName);
-                        printf("        resource 0x%08x %s:%s/%s: ", resID,
-                                CHAR16_TO_CSTR(resName.package, resName.packageLen),
-                                CHAR16_TO_CSTR(resName.type, resName.typeLen),
-                                CHAR16_TO_CSTR(resName.name, resName.nameLen));
+                        if (this->getResourceName(resID, &resName)) {
+                            printf("        resource 0x%08x %s:%s/%s: ", resID,
+                                    CHAR16_TO_CSTR(resName.package, resName.packageLen),
+                                    CHAR16_TO_CSTR(resName.type, resName.typeLen),
+                                    CHAR16_TO_CSTR(resName.name, resName.nameLen));
+                        } else {
+                            printf("        INVALID RESOURCE 0x%08x: ", resID);
+                        }
                         if ((thisOffset&0x3) != 0) {
                             printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset);
                             continue;
@@ -4402,18 +4462,19 @@
                                 print_value(pkg, value);
                             } else if (bagPtr != NULL) {
                                 const int N = dtohl(bagPtr->count);
-                                const ResTable_map* mapPtr = (const ResTable_map*)
-                                        (((const uint8_t*)ent) + esize);
+                                const uint8_t* baseMapPtr = (const uint8_t*)ent;
+                                size_t mapOffset = esize;
+                                const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
                                 printf("          Parent=0x%08x, Count=%d\n",
                                     dtohl(bagPtr->parent.ident), N);
-                                for (int i=0; i<N; i++) {
+                                for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
                                     printf("          #%i (Key=0x%08x): ",
                                         i, dtohl(mapPtr->name.ident));
                                     value.copyFrom_dtoh(mapPtr->value);
                                     print_value(pkg, value);
                                     const size_t size = dtohs(mapPtr->value.size);
-                                    mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr)
-                                            + size + sizeof(*mapPtr)-sizeof(mapPtr->value));
+                                    mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
+                                    mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
                                 }
                             }
                         }
diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp
index 1c4f80c..6358fc4 100644
--- a/libs/utils/String8.cpp
+++ b/libs/utils/String8.cpp
@@ -292,6 +292,11 @@
     SharedBuffer::bufferFromData(mString)->release();
 }
 
+void String8::clear() {
+    SharedBuffer::bufferFromData(mString)->release();
+    mString = getEmptyString();
+}
+
 void String8::setTo(const String8& other)
 {
     SharedBuffer::bufferFromData(other.mString)->acquire();
diff --git a/location/java/android/location/Country.aidl b/location/java/android/location/Country.aidl
new file mode 100644
index 0000000..c83d645
--- /dev/null
+++ b/location/java/android/location/Country.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+parcelable Country;
\ No newline at end of file
diff --git a/location/java/android/location/Country.java b/location/java/android/location/Country.java
new file mode 100755
index 0000000..3c05403
--- /dev/null
+++ b/location/java/android/location/Country.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class wraps the country information.
+ *
+ * @hide
+ */
+public class Country implements Parcelable {
+    /**
+     * The country code came from the mobile network
+     */
+    public static final int COUNTRY_SOURCE_NETWORK = 0;
+
+    /**
+     * The country code came from the location service
+     */
+    public static final int COUNTRY_SOURCE_LOCATION = 1;
+
+    /**
+     * The country code was read from the SIM card
+     */
+    public static final int COUNTRY_SOURCE_SIM = 2;
+
+    /**
+     * The country code came from the system locale setting
+     */
+    public static final int COUNTRY_SOURCE_LOCALE = 3;
+
+    /**
+     * The ISO 3166-1 two letters country code.
+     */
+    private final String mCountryIso;
+
+    /**
+     * Where the country code came from.
+     */
+    private final int mSource;
+
+    private int mHashCode;
+    /**
+     *
+     * @param countryIso the ISO 3166-1 two letters country code.
+     * @param source where the countryIso came from, could be one of below
+     *        values
+     *        <p>
+     *        <ul>
+     *        <li>{@link #COUNTRY_SOURCE_NETWORK}</li>
+     *        <li>{@link #COUNTRY_SOURCE_LOCATION}</li>
+     *        <li>{@link #COUNTRY_SOURCE_SIM}</li>
+     *        <li>{@link #COUNTRY_SOURCE_LOCALE}</li>
+     *        </ul>
+     */
+    public Country(final String countryIso, final int source) {
+        if (countryIso == null || source < COUNTRY_SOURCE_NETWORK
+                || source > COUNTRY_SOURCE_LOCALE) {
+            throw new IllegalArgumentException();
+        }
+        mCountryIso = countryIso.toLowerCase();
+        mSource = source;
+    }
+
+    public Country(Country country) {
+        mCountryIso = country.mCountryIso;
+        mSource = country.mSource;
+    }
+
+    /**
+     * @return the ISO 3166-1 two letters country code
+     */
+    public final String getCountryIso() {
+        return mCountryIso;
+    }
+
+    /**
+     * @return where the country code came from, could be one of below values
+     *         <p>
+     *         <ul>
+     *         <li>{@link #COUNTRY_SOURCE_NETWORK}</li>
+     *         <li>{@link #COUNTRY_SOURCE_LOCATION}</li>
+     *         <li>{@link #COUNTRY_SOURCE_SIM}</li>
+     *         <li>{@link #COUNTRY_SOURCE_LOCALE}</li>
+     *         </ul>
+     */
+    public final int getSource() {
+        return mSource;
+    }
+
+    public static final Parcelable.Creator<Country> CREATOR = new Parcelable.Creator<Country>() {
+        public Country createFromParcel(Parcel in) {
+            return new Country(in.readString(), in.readInt());
+        }
+
+        public Country[] newArray(int size) {
+            return new Country[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mCountryIso);
+        parcel.writeInt(mSource);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (object == this) {
+            return true;
+        }
+        if (object instanceof Country) {
+            Country c = (Country) object;
+            return mCountryIso.equals(c.getCountryIso()) && mSource == c.getSource();
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = mHashCode;
+        if (hash == 0) {
+            hash = 17;
+            hash = hash * 13 + mCountryIso.hashCode();
+            hash = hash * 13 + mSource;
+            mHashCode = hash;
+        }
+        return mHashCode;
+    }
+
+    /**
+     * Compare the specified country to this country object ignoring the mSource
+     * field, return true if the countryIso fields are equal
+     *
+     * @param country the country to compare
+     * @return true if the specified country's countryIso field is equal to this
+     *         country's, false otherwise.
+     */
+    public boolean equalsIgnoreSource(Country country) {
+        return country != null && mCountryIso.equals(country.getCountryIso());
+    }
+}
diff --git a/location/java/android/location/CountryDetector.java b/location/java/android/location/CountryDetector.java
new file mode 100644
index 0000000..0b780ce
--- /dev/null
+++ b/location/java/android/location/CountryDetector.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.location;
+
+import java.util.HashMap;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * This class provides access to the system country detector service. This
+ * service allows applications to obtain the country that the user is in.
+ * <p>
+ * The country will be detected in order of reliability, like
+ * <ul>
+ * <li>Mobile network</li>
+ * <li>Location</li>
+ * <li>SIM's country</li>
+ * <li>Phone's locale</li>
+ * </ul>
+ * <p>
+ * Call the {@link #detectCountry()} to get the available country immediately.
+ * <p>
+ * To be notified of the future country change, use the
+ * {@link #addCountryListener}
+ * <p>
+ * <p>
+ * You do not instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.COUNTRY_DETECTOR)}.
+ * <p>
+ * Both ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions are needed.
+ *
+ * @hide
+ */
+public class CountryDetector {
+
+    /**
+     * The class to wrap the ICountryListener.Stub and CountryListener objects
+     * together. The CountryListener will be notified through the specific
+     * looper once the country changed and detected.
+     */
+    private final static class ListenerTransport extends ICountryListener.Stub {
+
+        private final CountryListener mListener;
+
+        private final Handler mHandler;
+
+        public ListenerTransport(CountryListener listener, Looper looper) {
+            mListener = listener;
+            if (looper != null) {
+                mHandler = new Handler(looper);
+            } else {
+                mHandler = new Handler();
+            }
+        }
+
+        public void onCountryDetected(final Country country) {
+            mHandler.post(new Runnable() {
+                public void run() {
+                    mListener.onCountryDetected(country);
+                }
+            });
+        }
+    }
+
+    private final static String TAG = "CountryDetector";
+    private final ICountryDetector mService;
+    private final HashMap<CountryListener, ListenerTransport> mListeners;
+
+    /**
+     * @hide - hide this constructor because it has a parameter of type
+     *       ICountryDetector, which is a system private class. The right way to
+     *       create an instance of this class is using the factory
+     *       Context.getSystemService.
+     */
+    public CountryDetector(ICountryDetector service) {
+        mService = service;
+        mListeners = new HashMap<CountryListener, ListenerTransport>();
+    }
+
+    /**
+     * Start detecting the country that the user is in.
+     *
+     * @return the country if it is available immediately, otherwise null will
+     *         be returned.
+     */
+    public Country detectCountry() {
+        try {
+            return mService.detectCountry();
+        } catch (RemoteException e) {
+            Log.e(TAG, "detectCountry: RemoteException", e);
+            return null;
+        }
+    }
+
+    /**
+     * Add a listener to receive the notification when the country is detected
+     * or changed.
+     *
+     * @param listener will be called when the country is detected or changed.
+     * @param looper a Looper object whose message queue will be used to
+     *        implement the callback mechanism. If looper is null then the
+     *        callbacks will be called on the main thread.
+     */
+    public void addCountryListener(CountryListener listener, Looper looper) {
+        synchronized (mListeners) {
+            if (!mListeners.containsKey(listener)) {
+                ListenerTransport transport = new ListenerTransport(listener, looper);
+                try {
+                    mService.addCountryListener(transport);
+                    mListeners.put(listener, transport);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "addCountryListener: RemoteException", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Remove the listener
+     */
+    public void removeCountryListener(CountryListener listener) {
+        synchronized (mListeners) {
+            ListenerTransport transport = mListeners.get(listener);
+            if (transport != null) {
+                try {
+                    mListeners.remove(listener);
+                    mService.removeCountryListener(transport);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "removeCountryListener: RemoteException", e);
+                }
+            }
+        }
+    }
+}
diff --git a/location/java/android/location/CountryListener.java b/location/java/android/location/CountryListener.java
new file mode 100644
index 0000000..e36db41
--- /dev/null
+++ b/location/java/android/location/CountryListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+/**
+ * The listener for receiving the notification when the country is detected or
+ * changed
+ *
+ * @hide
+ */
+public interface CountryListener {
+    /**
+     * @param country the changed or detected country.
+     */
+    void onCountryDetected(Country country);
+}
diff --git a/location/java/android/location/ICountryDetector.aidl b/location/java/android/location/ICountryDetector.aidl
new file mode 100644
index 0000000..6eaf07c
--- /dev/null
+++ b/location/java/android/location/ICountryDetector.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.location.Country;
+import android.location.ICountryListener;
+
+/**
+ * The API for detecting the country where the user is.
+ *
+ * {@hide}
+ */
+interface ICountryDetector
+{
+    /**
+     * Start detecting the country that the user is in.
+     * @return the country if it is available immediately, otherwise null will be returned.
+     */
+    Country detectCountry();
+
+    /**
+     * Add a listener to receive the notification when the country is detected or changed.
+     */
+    void addCountryListener(in ICountryListener listener);
+
+    /**
+     * Remove the listener
+     */
+    void removeCountryListener(in ICountryListener listener);
+}
\ No newline at end of file
diff --git a/location/java/android/location/ICountryListener.aidl b/location/java/android/location/ICountryListener.aidl
new file mode 100644
index 0000000..76ecb13
--- /dev/null
+++ b/location/java/android/location/ICountryListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.location.Country;
+/**
+ * {@hide}
+ */
+oneway interface ICountryListener
+{
+    void onCountryDetected(in Country country);
+}
diff --git a/location/tests/locationtests/src/android/location/CountryTester.java b/location/tests/locationtests/src/android/location/CountryTester.java
new file mode 100644
index 0000000..9802d5a
--- /dev/null
+++ b/location/tests/locationtests/src/android/location/CountryTester.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.test.AndroidTestCase;
+
+public class CountryTester extends AndroidTestCase {
+    public void testCountryEquals() {
+        Country countryA = new Country("US", Country.COUNTRY_SOURCE_NETWORK);
+        Country countryB = new Country("US", Country.COUNTRY_SOURCE_LOCALE);
+        Country countryC = new Country("CN", Country.COUNTRY_SOURCE_LOCALE);
+        Country countryD = new Country("us", Country.COUNTRY_SOURCE_NETWORK);
+        assertTrue(countryA.equalsIgnoreSource(countryB));
+        assertFalse(countryA.equalsIgnoreSource(countryC));
+        assertFalse(countryA.equals(countryC));
+        assertTrue(countryA.equals(countryD));
+        assertTrue(countryA.hashCode() == countryD.hashCode());
+    }
+}
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index f1fa1e8..31e4631 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -31,9 +31,9 @@
     public static final int ENCODING_INVALID = 0;
     /** Default audio data format */
     public static final int ENCODING_DEFAULT = 1;
-    /** Audio data format: PCM 16 bit per sample */
+    /** Audio data format: PCM 16 bit per sample. Guaranteed to be supported by devices. */
     public static final int ENCODING_PCM_16BIT = 2; // accessed by native code
-    /** Audio data format: PCM 8 bit per sample */
+    /** Audio data format: PCM 8 bit per sample. Not guaranteed to be supported by devices. */
     public static final int ENCODING_PCM_8BIT = 3;  // accessed by native code
 
     /** Invalid audio channel configuration */
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index bbbba74..b23dcde 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1525,4 +1525,22 @@
       * {@hide}
       */
      private IBinder mICallBack = new Binder();
+
+    /**
+     * Checks whether the phone is in silent mode, with or without vibrate.
+     *
+     * @return true if phone is in silent mode, with or without vibrate.
+     *
+     * @see #getRingerMode()
+     *
+     * @hide pending API Council approval
+     */
+    public boolean isSilentMode() {
+        int ringerMode = getRingerMode();
+        boolean silentMode =
+            (ringerMode == RINGER_MODE_SILENT) ||
+            (ringerMode == RINGER_MODE_VIBRATE);
+        return silentMode;
+    }
+
 }
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index c48eaad..c567a6e 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -194,11 +194,13 @@
      * Class constructor.
      * @param audioSource the recording source. See {@link MediaRecorder.AudioSource} for
      *    recording source definitions.
-     * @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but
-     *   not limited to) 44100, 22050 and 11025.
+     * @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only
+     *   rate that is guaranteed to work on all devices, but other rates such as 22050,
+     *   16000, and 11025 may work on some devices.
      * @param channelConfig describes the configuration of the audio channels. 
      *   See {@link AudioFormat#CHANNEL_IN_MONO} and
-     *   {@link AudioFormat#CHANNEL_IN_STEREO}
+     *   {@link AudioFormat#CHANNEL_IN_STEREO}.  {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
+     *   to work on all devices.
      * @param audioFormat the format in which the audio data is represented. 
      *   See {@link AudioFormat#ENCODING_PCM_16BIT} and 
      *   {@link AudioFormat#ENCODING_PCM_8BIT}
@@ -444,6 +446,8 @@
      *  or {@link #ERROR} if the implementation was unable to query the hardware for its 
      *  output properties, 
      *   or the minimum buffer size expressed in bytes.
+     * @see #AudioRecord(int, int, int, int, int) for more information on valid
+     *   configuration values.
      */
     static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
         int channelCount = 0;
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 41d2cc5..3bee906 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -16,18 +16,19 @@
 
 package android.media;
 
-import java.util.NoSuchElementException;
 import android.app.ActivityManagerNative;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothClass;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.media.MediaPlayer.OnCompletionListener;
@@ -40,6 +41,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.provider.Settings;
 import android.provider.Settings.System;
 import android.telephony.PhoneStateListener;
@@ -47,7 +49,6 @@
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.VolumePanel;
-import android.os.SystemProperties;
 
 import com.android.internal.telephony.ITelephony;
 
@@ -57,7 +58,9 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.Stack;
 
@@ -258,8 +261,8 @@
     // BluetoothHeadset API to control SCO connection
     private BluetoothHeadset mBluetoothHeadset;
 
-    // Bluetooth headset connection state
-    private boolean mBluetoothHeadsetConnected;
+    // Bluetooth headset device
+    private BluetoothDevice mBluetoothHeadsetDevice;
 
     ///////////////////////////////////////////////////////////////////////////
     // Construction
@@ -294,17 +297,24 @@
         AudioSystem.setErrorCallback(mAudioSystemCallback);
         loadSoundEffects();
 
-        mBluetoothHeadsetConnected = false;
-        mBluetoothHeadset = new BluetoothHeadset(context,
-                                                 mBluetoothHeadsetServiceListener);
+        mBluetoothHeadsetDevice = null;
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+                                    BluetoothProfile.HEADSET);
+        }
 
         // Register for device connection intent broadcasts.
         IntentFilter intentFilter =
                 new IntentFilter(Intent.ACTION_HEADSET_PLUG);
-        intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
-        intentFilter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
-        intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
+
+        intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
         intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+        intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
+        intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
+        intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
+        intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG);
         context.registerReceiver(mReceiver, intentFilter);
 
         // Register for media button intent broadcasts.
@@ -1000,7 +1010,7 @@
 
         public void incCount() {
             synchronized(mScoClients) {
-                requestScoState(BluetoothHeadset.AUDIO_STATE_CONNECTED);
+                requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
                 if (mStartcount == 0) {
                     try {
                         mCb.linkToDeath(this, 0);
@@ -1026,7 +1036,7 @@
                             Log.w(TAG, "decCount() going to 0 but not registered to binder");
                         }
                     }
-                    requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
+                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
                 }
             }
         }
@@ -1042,7 +1052,7 @@
                 }
                 mStartcount = 0;
                 if (stopSco) {
-                    requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
+                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
                 }
             }
         }
@@ -1068,12 +1078,12 @@
 
         private void requestScoState(int state) {
             if (totalCount() == 0 &&
-                mBluetoothHeadsetConnected &&
+                mBluetoothHeadsetDevice != null &&
                 AudioService.this.mMode == AudioSystem.MODE_NORMAL) {
-                if (state == BluetoothHeadset.AUDIO_STATE_CONNECTED) {
-                    mBluetoothHeadset.startVoiceRecognition();
+                if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+                    mBluetoothHeadset.startVoiceRecognition(mBluetoothHeadsetDevice);
                 } else {
-                    mBluetoothHeadset.stopVoiceRecognition();
+                    mBluetoothHeadset.stopVoiceRecognition(mBluetoothHeadsetDevice);
                 }
             }
         }
@@ -1103,23 +1113,25 @@
         }
     }
 
-    private BluetoothHeadset.ServiceListener mBluetoothHeadsetServiceListener =
-        new BluetoothHeadset.ServiceListener() {
-        public void onServiceConnected() {
-            if (mBluetoothHeadset != null) {
-                BluetoothDevice device = mBluetoothHeadset.getCurrentHeadset();
-                if (mBluetoothHeadset.getState(device) == BluetoothHeadset.STATE_CONNECTED) {
-                    mBluetoothHeadsetConnected = true;
-                }
+    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            mBluetoothHeadset = (BluetoothHeadset) proxy;
+            List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
+            if (deviceList.size() > 0) {
+                mBluetoothHeadsetDevice = deviceList.get(0);
+            } else {
+                mBluetoothHeadsetDevice = null;
             }
         }
-        public void onServiceDisconnected() {
+        public void onServiceDisconnected(int profile) {
             if (mBluetoothHeadset != null) {
-                BluetoothDevice device = mBluetoothHeadset.getCurrentHeadset();
-                if (mBluetoothHeadset.getState(device) == BluetoothHeadset.STATE_DISCONNECTED) {
-                    mBluetoothHeadsetConnected = false;
+                List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
+                if (devices.size() == 0) {
+                    mBluetoothHeadsetDevice = null;
                     clearAllScoClients();
                 }
+                mBluetoothHeadset = null;
             }
         }
     };
@@ -1808,23 +1820,29 @@
                     case Intent.EXTRA_DOCK_STATE_CAR:
                         config = AudioSystem.FORCE_BT_CAR_DOCK;
                         break;
+                    case Intent.EXTRA_DOCK_STATE_LE_DESK:
+                        config = AudioSystem.FORCE_ANALOG_DOCK;
+                        break;
+                    case Intent.EXTRA_DOCK_STATE_HE_DESK:
+                        config = AudioSystem.FORCE_DIGITAL_DOCK;
+                        break;
                     case Intent.EXTRA_DOCK_STATE_UNDOCKED:
                     default:
                         config = AudioSystem.FORCE_NONE;
                 }
                 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
-            } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
-                int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE,
-                                               BluetoothA2dp.STATE_DISCONNECTED);
+            } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
+                                               BluetoothProfile.STATE_DISCONNECTED);
                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                 String address = btDevice.getAddress();
-                boolean isConnected = (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
-                                       ((String)mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)).equals(address));
+                boolean isConnected =
+                    (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
+                     mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
 
-                if (isConnected &&
-                    state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) {
+                if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                     if (btDevice.isBluetoothDock()) {
-                        if (state == BluetoothA2dp.STATE_DISCONNECTED) {
+                        if (state == BluetoothProfile.STATE_DISCONNECTED) {
                             // introduction of a delay for transient disconnections of docks when
                             // power is rapidly turned off/on, this message will be canceled if
                             // we reconnect the dock under a preset delay
@@ -1834,9 +1852,7 @@
                     } else {
                         makeA2dpDeviceUnavailableNow(address);
                     }
-                } else if (!isConnected &&
-                             (state == BluetoothA2dp.STATE_CONNECTED ||
-                              state == BluetoothA2dp.STATE_PLAYING)) {
+                } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                     if (btDevice.isBluetoothDock()) {
                         // this could be a reconnection after a transient disconnection
                         cancelA2dpDeviceTimeout();
@@ -1851,9 +1867,9 @@
                     }
                     makeA2dpDeviceAvailable(address);
                 }
-            } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
-                int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                                               BluetoothHeadset.STATE_ERROR);
+            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
+                                               BluetoothProfile.STATE_DISCONNECTED);
                 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                 String address = null;
@@ -1874,21 +1890,21 @@
                 }
 
                 boolean isConnected = (mConnectedDevices.containsKey(device) &&
-                                       ((String)mConnectedDevices.get(device)).equals(address));
+                                       mConnectedDevices.get(device).equals(address));
 
-                if (isConnected && state != BluetoothHeadset.STATE_CONNECTED) {
+                if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                     AudioSystem.setDeviceConnectionState(device,
                                                          AudioSystem.DEVICE_STATE_UNAVAILABLE,
                                                          address);
                     mConnectedDevices.remove(device);
-                    mBluetoothHeadsetConnected = false;
+                    mBluetoothHeadsetDevice = null;
                     clearAllScoClients();
-                } else if (!isConnected && state == BluetoothHeadset.STATE_CONNECTED) {
+                } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                     AudioSystem.setDeviceConnectionState(device,
                                                          AudioSystem.DEVICE_STATE_AVAILABLE,
                                                          address);
                     mConnectedDevices.put(new Integer(device), address);
-                    mBluetoothHeadsetConnected = true;
+                    mBluetoothHeadsetDevice = btDevice;
                 }
             } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
                 int state = intent.getIntExtra("state", 0);
@@ -1921,18 +1937,48 @@
                         mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
                     }
                 }
+            } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
+                int state = intent.getIntExtra("state", 0);
+                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
+                boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
+                if (state == 0 && isConnected) {
+                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
+                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
+                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
+                } else if (state == 1 && !isConnected)  {
+                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
+                                                         AudioSystem.DEVICE_STATE_AVAILABLE, "");
+                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
+                }
+            } else if ( (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) ||
+                        (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) ) {
+                int state = intent.getIntExtra("state", 0);
+                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state);
+                boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
+                if (state == 0 && isConnected) {
+                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
+                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
+                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
+                } else if (state == 1 && !isConnected)  {
+                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
+                                                         AudioSystem.DEVICE_STATE_AVAILABLE, "");
+                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
+                }
             } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
-                int state = intent.getIntExtra(BluetoothHeadset.EXTRA_AUDIO_STATE,
-                                               BluetoothHeadset.STATE_ERROR);
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
                 synchronized (mScoClients) {
                     if (!mScoClients.isEmpty()) {
                         switch (state) {
-                        case BluetoothHeadset.AUDIO_STATE_CONNECTED:
+                        case BluetoothHeadset.STATE_AUDIO_CONNECTED:
                             state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
                             break;
-                        case BluetoothHeadset.AUDIO_STATE_DISCONNECTED:
+                        case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
                             state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
                             break;
+                        case BluetoothHeadset.STATE_AUDIO_CONNECTING:
+                            // Todo(): Handle this, ignore for now as a public
+                            // API will break.
+                            break;
                         default:
                             state = AudioManager.SCO_AUDIO_STATE_ERROR;
                             break;
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index a4818ff..5442791 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -243,6 +243,8 @@
     public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
     public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
     public static final int DEVICE_OUT_AUX_DIGITAL = 0x400;
+    public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800;
+    public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000;
     public static final int DEVICE_OUT_DEFAULT = 0x8000;
     // input devices
     public static final int DEVICE_IN_COMMUNICATION = 0x10000;
@@ -273,6 +275,8 @@
     public static final int FORCE_WIRED_ACCESSORY = 5;
     public static final int FORCE_BT_CAR_DOCK = 6;
     public static final int FORCE_BT_DESK_DOCK = 7;
+    public static final int FORCE_ANALOG_DOCK = 8;
+    public static final int FORCE_DIGITAL_DOCK = 9;
     public static final int FORCE_DEFAULT = FORCE_NONE;
 
     // usage for serForceUse
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index a27df57..3e9429d 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -39,22 +39,80 @@
  */
 public class CamcorderProfile
 {
+    // Do not change these values/ordinals without updating their counterpart
+    // in include/media/MediaProfiles.h!
+
     /**
-     * The output from camcorder recording sessions can have different quality levels.
-     *
-     * Currently, we define two quality levels: high quality and low quality.
-     * A camcorder recording session with high quality level usually has higher output bit
-     * rate, better video and/or audio recording quality, larger video frame
-     * resolution and higher audio sampling rate, etc, than those with low quality
-     * level.
-     *
-     * Do not change these values/ordinals without updating their counterpart
-     * in include/media/MediaProfiles.h!
+     * Quality level corresponding to the lowest available resolution.
      */
     public static final int QUALITY_LOW  = 0;
+
+    /**
+     * Quality level corresponding to the highest available resolution.
+     */
     public static final int QUALITY_HIGH = 1;
 
     /**
+     * Quality level corresponding to the qcif (176 x 144) resolution.
+     */
+    public static final int QUALITY_QCIF = 2;
+
+    /**
+     * Quality level corresponding to the cif (352 x 288) resolution.
+     */
+    public static final int QUALITY_CIF = 3;
+
+    /**
+     * Quality level corresponding to the 480p (720 x 480) resolution.
+     */
+    public static final int QUALITY_480P = 4;
+
+    /**
+     * Quality level corresponding to the 720p (1280 x 720) resolution.
+     */
+    public static final int QUALITY_720P = 5;
+
+    /**
+     * Quality level corresponding to the 1080p (1920 x 1088) resolution.
+     */
+    public static final int QUALITY_1080P = 6;
+
+    /**
+     * Time lapse quality level corresponding to the lowest available resolution.
+     */
+    public static final int QUALITY_TIME_LAPSE_LOW  = 1000;
+
+    /**
+     * Time lapse quality level corresponding to the highest available resolution.
+     */
+    public static final int QUALITY_TIME_LAPSE_HIGH = 1001;
+
+    /**
+     * Time lapse quality level corresponding to the qcif (176 x 144) resolution.
+     */
+    public static final int QUALITY_TIME_LAPSE_QCIF = 1002;
+
+    /**
+     * Time lapse quality level corresponding to the cif (352 x 288) resolution.
+     */
+    public static final int QUALITY_TIME_LAPSE_CIF = 1003;
+
+    /**
+     * Time lapse quality level corresponding to the 480p (720 x 480) resolution.
+     */
+    public static final int QUALITY_TIME_LAPSE_480P = 1004;
+
+    /**
+     * Time lapse quality level corresponding to the 720p (1280 x 720) resolution.
+     */
+    public static final int QUALITY_TIME_LAPSE_720P = 1005;
+
+    /**
+     * Time lapse quality level corresponding to the 1080p (1920 x 1088) resolution.
+     */
+    public static final int QUALITY_TIME_LAPSE_1080P = 1006;
+
+    /**
      * Default recording duration in seconds before the session is terminated.
      * This is useful for applications like MMS has limited file size requirement.
      */
@@ -122,25 +180,79 @@
      * Returns the camcorder profile for the default camera at the given
      * quality level.
      * @param quality the target quality level for the camcorder profile
+     * @see #get(int, int)
      */
     public static CamcorderProfile get(int quality) {
-        return get(0, quality);
+        return get(android.hardware.Camera.CAMERA_ID_DEFAULT, quality);
     }
 
     /**
      * Returns the camcorder profile for the given camera at the given
      * quality level.
+     *
+     * Quality levels QUALITY_LOW, QUALITY_HIGH are guaranteed to be supported, while
+     * other levels may or may not be supported. The supported levels can be checked using
+     * {@link #hasProfile(int, int)}.
+     * QUALITY_LOW refers to the lowest quality available, while QUALITY_HIGH refers to
+     * the highest quality available.
+     * QUALITY_LOW/QUALITY_HIGH have to match one of qcif, cif, 480p, 720p, or 1080p.
+     * E.g. if the device supports 480p, 720p, and 1080p, then low is 480p and high is
+     * 1080p.
+     *
+     * The same is true for time lapse quality levels, i.e. QUALITY_TIME_LAPSE_LOW,
+     * QUALITY_TIME_LAPSE_HIGH are guaranteed to be supported and have to match one of
+     * qcif, cif, 480p, 720p, or 1080p.
+     *
+     * A camcorder recording session with higher quality level usually has higher output
+     * bit rate, better video and/or audio recording quality, larger video frame
+     * resolution and higher audio sampling rate, etc, than those with lower quality
+     * level.
+     *
      * @param cameraId the id for the camera
-     * @param quality the target quality level for the camcorder profile
+     * @param quality the target quality level for the camcorder profile.
+     * @see #QUALITY_LOW
+     * @see #QUALITY_HIGH
+     * @see #QUALITY_QCIF
+     * @see #QUALITY_CIF
+     * @see #QUALITY_480P
+     * @see #QUALITY_720P
+     * @see #QUALITY_1080P
+     * @see #QUALITY_TIME_LAPSE_LOW
+     * @see #QUALITY_TIME_LAPSE_HIGH
+     * @see #QUALITY_TIME_LAPSE_QCIF
+     * @see #QUALITY_TIME_LAPSE_CIF
+     * @see #QUALITY_TIME_LAPSE_480P
+     * @see #QUALITY_TIME_LAPSE_720P
+     * @see #QUALITY_TIME_LAPSE_1080P
      */
     public static CamcorderProfile get(int cameraId, int quality) {
-        if (quality < QUALITY_LOW || quality > QUALITY_HIGH) {
+        if (!((quality >= QUALITY_LOW && quality <= QUALITY_1080P) ||
+                (quality >= QUALITY_TIME_LAPSE_LOW && quality <= QUALITY_TIME_LAPSE_1080P))) {
             String errMessage = "Unsupported quality level: " + quality;
             throw new IllegalArgumentException(errMessage);
         }
         return native_get_camcorder_profile(cameraId, quality);
     }
 
+    /**
+     * Returns true if camcorder profile exists for the default camera at
+     * the given quality level.
+     * @param quality the target quality level for the camcorder profile
+     */
+    public static boolean hasProfile(int quality) {
+        return hasProfile(android.hardware.Camera.CAMERA_ID_DEFAULT, quality);
+    }
+
+    /**
+     * Returns true if camcorder profile exists for the given camera at
+     * the given quality level.
+     * @param cameraId the id for the camera
+     * @param quality the target quality level for the camcorder profile
+     */
+    public static boolean hasProfile(int cameraId, int quality) {
+        return native_has_camcorder_profile(cameraId, quality);
+    }
+
     static {
         System.loadLibrary("media_jni");
         native_init();
@@ -178,4 +290,6 @@
     private static native final void native_init();
     private static native final CamcorderProfile native_get_camcorder_profile(
             int cameraId, int quality);
+    private static native final boolean native_has_camcorder_profile(
+            int cameraId, int quality);
 }
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 74488c5..11e27a9 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -51,6 +51,12 @@
     public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
     /** Type is String. */
     public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+    /** Type is String. */
+    public static final String TAG_EXPOSURE_TIME = "ExposureTime";
+    /** Type is String. */
+    public static final String TAG_APERTURE = "FNumber";
+    /** Type is String. */
+    public static final String TAG_ISO = "ISOSpeedRatings";
 
     /**
      * The altitude (in meters) based on the reference in TAG_GPS_ALTITUDE_REF.
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 6e527d9..532a2df 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -34,8 +34,6 @@
  * {@hide}
  */
 public class MediaFile {
-    // comma separated list of all file extensions supported by the media scanner
-    public final static String sFileExtensions;
 
     // Audio file types
     public static final int FILE_TYPE_MP3     = 1;
@@ -84,6 +82,22 @@
     public static final int FILE_TYPE_WPL     = 43;
     private static final int FIRST_PLAYLIST_FILE_TYPE = FILE_TYPE_M3U;
     private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_WPL;
+
+    // Drm file types
+    public static final int FILE_TYPE_FL      = 51;
+    private static final int FIRST_DRM_FILE_TYPE = FILE_TYPE_FL;
+    private static final int LAST_DRM_FILE_TYPE = FILE_TYPE_FL;
+
+    // Other popular file types
+    public static final int FILE_TYPE_TEXT          = 100;
+    public static final int FILE_TYPE_HTML          = 101;
+    public static final int FILE_TYPE_PDF           = 102;
+    public static final int FILE_TYPE_XML           = 103;
+    public static final int FILE_TYPE_MS_WORD       = 104;
+    public static final int FILE_TYPE_MS_EXCEL      = 105;
+    public static final int FILE_TYPE_MS_POWERPOINT = 106;
+    public static final int FILE_TYPE_FLAC          = 107;
+    public static final int FILE_TYPE_ZIP           = 108;
     
     static class MediaFileType {
     
@@ -96,15 +110,32 @@
         }
     }
     
-    private static HashMap<String, MediaFileType> sFileTypeMap 
+    private static HashMap<String, MediaFileType> sFileTypeMap
             = new HashMap<String, MediaFileType>();
-    private static HashMap<String, Integer> sMimeTypeMap 
-            = new HashMap<String, Integer>();            
+    private static HashMap<String, Integer> sMimeTypeMap
+            = new HashMap<String, Integer>();
+    // maps file extension to MTP format code
+    private static HashMap<String, Integer> sFileTypeToFormatMap
+            = new HashMap<String, Integer>();
+    // maps mime type to MTP format code
+    private static HashMap<String, Integer> sMimeTypeToFormatMap
+            = new HashMap<String, Integer>();
+    // maps MTP format code to mime type
+    private static HashMap<Integer, String> sFormatToMimeTypeMap
+            = new HashMap<Integer, String>();
+
     static void addFileType(String extension, int fileType, String mimeType) {
         sFileTypeMap.put(extension, new MediaFileType(fileType, mimeType));
         sMimeTypeMap.put(mimeType, Integer.valueOf(fileType));
     }
 
+    static void addFileType(String extension, int fileType, String mimeType, int mtpFormatCode) {
+        addFileType(extension, fileType, mimeType);
+        sFileTypeToFormatMap.put(extension, Integer.valueOf(mtpFormatCode));
+        sMimeTypeToFormatMap.put(mimeType, Integer.valueOf(mtpFormatCode));
+        sFormatToMimeTypeMap.put(mtpFormatCode, mimeType);
+    }
+
     private static boolean isWMAEnabled() {
         List<AudioDecoder> decoders = DecoderCapabilities.getAudioDecoders();
         for (AudioDecoder decoder: decoders) {
@@ -115,28 +146,18 @@
         return false;
     }
 
-    private static boolean isWMVEnabled() {
-        List<VideoDecoder> decoders = DecoderCapabilities.getVideoDecoders();
-        for (VideoDecoder decoder: decoders) {
-            if (decoder == VideoDecoder.VIDEO_DECODER_WMV) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     static {
-        addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg");
-        addFileType("M4A", FILE_TYPE_M4A, "audio/mp4");
-        addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav");
+        addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg", MtpConstants.FORMAT_MP3);
+        addFileType("M4A", FILE_TYPE_M4A, "audio/mp4", MtpConstants.FORMAT_MPEG);
+        addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav", MtpConstants.FORMAT_WAV);
         addFileType("AMR", FILE_TYPE_AMR, "audio/amr");
         addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb");
         if (isWMAEnabled()) {
-            addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma");
+            addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma", MtpConstants.FORMAT_WMA);
         }
-        addFileType("OGG", FILE_TYPE_OGG, "application/ogg");
-        addFileType("OGA", FILE_TYPE_OGG, "application/ogg");
-        addFileType("AAC", FILE_TYPE_AAC, "audio/aac");
+        addFileType("OGG", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG);
+        addFileType("OGA", FILE_TYPE_OGG, "application/ogg", MtpConstants.FORMAT_OGG);
+        addFileType("AAC", FILE_TYPE_AAC, "audio/aac", MtpConstants.FORMAT_AAC);
         addFileType("MKA", FILE_TYPE_MKA, "audio/x-matroska");
  
         addFileType("MID", FILE_TYPE_MID, "audio/midi");
@@ -148,78 +169,131 @@
         addFileType("RTX", FILE_TYPE_MID, "audio/midi");
         addFileType("OTA", FILE_TYPE_MID, "audio/midi");
         
-        addFileType("MPEG", FILE_TYPE_MP4, "video/mpeg");
-        addFileType("MP4", FILE_TYPE_MP4, "video/mp4");
-        addFileType("M4V", FILE_TYPE_M4V, "video/mp4");
-        addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp");
-        addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp");
-        addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2");
-        addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2");
+        addFileType("MPEG", FILE_TYPE_MP4, "video/mpeg", MtpConstants.FORMAT_MPEG);
+        addFileType("MP4", FILE_TYPE_MP4, "video/mp4", MtpConstants.FORMAT_MPEG);
+        addFileType("M4V", FILE_TYPE_M4V, "video/mp4", MtpConstants.FORMAT_MPEG);
+        addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp",  MtpConstants.FORMAT_3GP_CONTAINER);
+        addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp", MtpConstants.FORMAT_3GP_CONTAINER);
+        addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2", MtpConstants.FORMAT_3GP_CONTAINER);
+        addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2", MtpConstants.FORMAT_3GP_CONTAINER);
         addFileType("MKV", FILE_TYPE_MKV, "video/x-matroska");
         addFileType("WEBM", FILE_TYPE_MKV, "video/x-matroska");
         addFileType("TS", FILE_TYPE_MP2TS, "video/mp2ts");
 
-        if (isWMVEnabled()) {
-            addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv");
-            addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf");
-        }
+        addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv", MtpConstants.FORMAT_WMV);
+        addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf");
 
-        addFileType("JPG", FILE_TYPE_JPEG, "image/jpeg");
-        addFileType("JPEG", FILE_TYPE_JPEG, "image/jpeg");
-        addFileType("GIF", FILE_TYPE_GIF, "image/gif");
-        addFileType("PNG", FILE_TYPE_PNG, "image/png");
-        addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp");
+        addFileType("JPG", FILE_TYPE_JPEG, "image/jpeg", MtpConstants.FORMAT_EXIF_JPEG);
+        addFileType("JPEG", FILE_TYPE_JPEG, "image/jpeg", MtpConstants.FORMAT_EXIF_JPEG);
+        addFileType("GIF", FILE_TYPE_GIF, "image/gif", MtpConstants.FORMAT_GIF);
+        addFileType("PNG", FILE_TYPE_PNG, "image/png", MtpConstants.FORMAT_PNG);
+        addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp", MtpConstants.FORMAT_BMP);
         addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp");
  
-        addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl");
-        addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls");
-        addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl");
+        addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
+        addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls", MtpConstants.FORMAT_PLS_PLAYLIST);
+        addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl", MtpConstants.FORMAT_WPL_PLAYLIST);
 
-        // compute file extensions list for native Media Scanner
-        StringBuilder builder = new StringBuilder();
-        Iterator<String> iterator = sFileTypeMap.keySet().iterator();
-        
-        while (iterator.hasNext()) {
-            if (builder.length() > 0) {
-                builder.append(',');
-            }
-            builder.append(iterator.next());
-        } 
-        sFileExtensions = builder.toString();
+        addFileType("FL", FILE_TYPE_FL, "application/x-android-drm-fl");
+
+        addFileType("TXT", FILE_TYPE_TEXT, "text/plain", MtpConstants.FORMAT_TEXT);
+        addFileType("HTM", FILE_TYPE_HTML, "text/html", MtpConstants.FORMAT_HTML);
+        addFileType("HTML", FILE_TYPE_HTML, "text/html", MtpConstants.FORMAT_HTML);
+        addFileType("PDF", FILE_TYPE_PDF, "application/pdf");
+        addFileType("DOC", FILE_TYPE_MS_WORD, "application/msword", MtpConstants.FORMAT_MS_WORD_DOCUMENT);
+        addFileType("XLS", FILE_TYPE_MS_EXCEL, "application/vnd.ms-excel", MtpConstants.FORMAT_MS_EXCEL_SPREADSHEET);
+        addFileType("PPT", FILE_TYPE_MS_POWERPOINT, "application/mspowerpoint", MtpConstants.FORMAT_MS_POWERPOINT_PRESENTATION);
+        addFileType("FLAC", FILE_TYPE_FLAC, "audio/flac", MtpConstants.FORMAT_FLAC);
+        addFileType("ZIP", FILE_TYPE_ZIP, "application/zip");
     }
-    
+
     public static boolean isAudioFileType(int fileType) {
         return ((fileType >= FIRST_AUDIO_FILE_TYPE &&
                 fileType <= LAST_AUDIO_FILE_TYPE) ||
                 (fileType >= FIRST_MIDI_FILE_TYPE &&
                 fileType <= LAST_MIDI_FILE_TYPE));
     }
-    
+
     public static boolean isVideoFileType(int fileType) {
         return (fileType >= FIRST_VIDEO_FILE_TYPE &&
                 fileType <= LAST_VIDEO_FILE_TYPE);
     }
-    
+
     public static boolean isImageFileType(int fileType) {
         return (fileType >= FIRST_IMAGE_FILE_TYPE &&
                 fileType <= LAST_IMAGE_FILE_TYPE);
     }
-    
+
     public static boolean isPlayListFileType(int fileType) {
         return (fileType >= FIRST_PLAYLIST_FILE_TYPE &&
                 fileType <= LAST_PLAYLIST_FILE_TYPE);
     }
-    
+
+    public static boolean isDrmFileType(int fileType) {
+        return (fileType >= FIRST_DRM_FILE_TYPE &&
+                fileType <= LAST_DRM_FILE_TYPE);
+    }
+
     public static MediaFileType getFileType(String path) {
         int lastDot = path.lastIndexOf(".");
         if (lastDot < 0)
             return null;
         return sFileTypeMap.get(path.substring(lastDot + 1).toUpperCase());
     }
-    
+
+    public static boolean isMimeTypeMedia(String mimeType) {
+        int fileType = getFileTypeForMimeType(mimeType);
+        return isAudioFileType(fileType) || isVideoFileType(fileType)
+                || isImageFileType(fileType) || isPlayListFileType(fileType);
+    }
+
+    // generates a title based on file name
+    public static String getFileTitle(String path) {
+        // extract file name after last slash
+        int lastSlash = path.lastIndexOf('/');
+        if (lastSlash >= 0) {
+            lastSlash++;
+            if (lastSlash < path.length()) {
+                path = path.substring(lastSlash);
+            }
+        }
+        // truncate the file extension (if any)
+        int lastDot = path.lastIndexOf('.');
+        if (lastDot > 0) {
+            path = path.substring(0, lastDot);
+        }
+        return path;
+    }
+
     public static int getFileTypeForMimeType(String mimeType) {
         Integer value = sMimeTypeMap.get(mimeType);
         return (value == null ? 0 : value.intValue());
     }
 
+    public static String getMimeTypeForFile(String path) {
+        MediaFileType mediaFileType = getFileType(path);
+        return (mediaFileType == null ? null : mediaFileType.mimeType);
+    }
+
+    public static int getFormatCode(String fileName, String mimeType) {
+        if (mimeType != null) {
+            Integer value = sMimeTypeToFormatMap.get(mimeType);
+            if (value != null) {
+                return value.intValue();
+            }
+        }
+        int lastDot = fileName.lastIndexOf('.');
+        if (lastDot > 0) {
+            String extension = fileName.substring(lastDot + 1);
+            Integer value = sFileTypeToFormatMap.get(extension);
+            if (value != null) {
+                return value.intValue();
+            }
+        }
+        return MtpConstants.FORMAT_UNDEFINED;
+    }
+
+    public static String getMimeTypeForFormatCode(int formatCode) {
+        return sFormatToMimeTypeMap.get(formatCode);
+    }
 }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index cb46a29..01134f2 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1087,53 +1087,6 @@
     private native void _reset();
 
     /**
-     * Suspends the MediaPlayer. The only methods that may be called while
-     * suspended are {@link #reset()}, {@link #release()} and {@link #resume()}.
-     * MediaPlayer will release its hardware resources as far as
-     * possible and reasonable. A successfully suspended MediaPlayer will
-     * cease sending events.
-     * If suspension is successful, this method returns true, otherwise
-     * false is returned and the player's state is not affected.
-     * @hide
-     */
-    public boolean suspend() {
-        if (native_suspend_resume(true) < 0) {
-            return false;
-        }
-
-        stayAwake(false);
-
-        // make sure none of the listeners get called anymore
-        mEventHandler.removeCallbacksAndMessages(null);
-
-        return true;
-    }
-
-    /**
-     * Resumes the MediaPlayer. Only to be called after a previous (successful)
-     * call to {@link #suspend()}.
-     * MediaPlayer will return to a state close to what it was in before
-     * suspension.
-     * @hide
-     */
-    public boolean resume() {
-        if (native_suspend_resume(false) < 0) {
-            return false;
-        }
-
-        if (isPlaying()) {
-            stayAwake(true);
-        }
-
-        return true;
-    }
-
-    /**
-     * @hide
-     */
-    private native int native_suspend_resume(boolean isSuspend);
-
-    /**
      * Sets the audio stream type for this MediaPlayer. See {@link AudioManager}
      * for a list of stream types. Must call this method before prepare() or
      * prepareAsync() in order for the target stream type to become effective
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index c102de4..866c853 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -72,6 +72,9 @@
 
     private String mPath;
     private FileDescriptor mFd;
+    private boolean mPrepareAuxiliaryFile = false;
+    private String mPathAux;
+    private FileDescriptor mFdAux;
     private EventHandler mEventHandler;
     private OnErrorListener mOnErrorListener;
     private OnInfoListener mOnInfoListener;
@@ -277,11 +280,37 @@
         setVideoFrameRate(profile.videoFrameRate);
         setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
         setVideoEncodingBitRate(profile.videoBitRate);
-        setAudioEncodingBitRate(profile.audioBitRate);
-        setAudioChannels(profile.audioChannels);
-        setAudioSamplingRate(profile.audioSampleRate);
         setVideoEncoder(profile.videoCodec);
-        setAudioEncoder(profile.audioCodec);
+        if (profile.quality >= CamcorderProfile.QUALITY_TIME_LAPSE_LOW &&
+             profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_1080P) {
+            // Enable time lapse. Also don't set audio for time lapse.
+            setParameter(String.format("time-lapse-enable=1"));
+        } else {
+            setAudioEncodingBitRate(profile.audioBitRate);
+            setAudioChannels(profile.audioChannels);
+            setAudioSamplingRate(profile.audioSampleRate);
+            setAudioEncoder(profile.audioCodec);
+        }
+    }
+
+    /**
+     * Set video frame capture rate. This can be used to set a different video frame capture
+     * rate than the recorded video's playback rate. Currently this works only for time lapse mode.
+     *
+     * @param fps Rate at which frames should be captured in frames per second.
+     * The fps can go as low as desired. However the fastest fps will be limited by the hardware.
+     * For resolutions that can be captured by the video camera, the fastest fps can be computed using
+     * {@link android.hardware.Camera.Parameters#getPreviewFpsRange(int[])}. For higher
+     * resolutions the fastest fps may be more restrictive.
+     * Note that the recorder cannot guarantee that frames will be captured at the
+     * given rate due to camera/encoder limitations. However it tries to be as close as
+     * possible.
+     */
+    public void setCaptureRate(double fps) {
+        double timeBetweenFrameCapture = 1 / fps;
+        int timeBetweenFrameCaptureMs = (int) (1000 * timeBetweenFrameCapture);
+        setParameter(String.format("time-between-time-lapse-frame-capture=%d",
+                    timeBetweenFrameCaptureMs));
     }
 
     /**
@@ -476,6 +505,87 @@
     }
 
     /**
+     * Sets the auxiliary time lapse video's resolution and bitrate.
+     *
+     * The auxiliary video's resolution and bitrate are determined by the CamcorderProfile
+     * quality level {@link android.media.CamcorderProfile#QUALITY_HIGH}.
+     */
+    private void setAuxVideoParameters() {
+        CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
+        setParameter(String.format("video-aux-param-width=%d", profile.videoFrameWidth));
+        setParameter(String.format("video-aux-param-height=%d", profile.videoFrameHeight));
+        setParameter(String.format("video-aux-param-encoding-bitrate=%d", profile.videoBitRate));
+    }
+
+    /**
+     * Pass in the file descriptor for the auxiliary time lapse video. Call this before
+     * prepare().
+     *
+     * Sets file descriptor and parameters for auxiliary time lapse video. Time lapse mode
+     * can capture video (using the still camera) at resolutions higher than that can be
+     * played back on the device. This function or
+     * {@link #setAuxiliaryOutputFile(String)} enable capture of a smaller video in
+     * parallel with the main time lapse video, which can be used to play back on the
+     * device. The smaller video is created by downsampling the main video. This call is
+     * optional and does not have to be called if parallel capture of a downsampled video
+     * is not desired.
+     *
+     * Note that while the main video resolution and bitrate is determined from the
+     * CamcorderProfile in {@link #setProfile(CamcorderProfile)}, the auxiliary video's
+     * resolution and bitrate are determined by the CamcorderProfile quality level
+     * {@link android.media.CamcorderProfile#QUALITY_HIGH}. All other encoding parameters
+     * remain the same for the main video and the auxiliary video.
+     *
+     * E.g. if the device supports the time lapse profile quality level
+     * {@link android.media.CamcorderProfile#QUALITY_TIME_LAPSE_1080P} but can playback at
+     * most 480p, the application might want to capture an auxiliary video of resolution
+     * 480p using this call.
+     *
+     * @param fd an open file descriptor to be written into.
+     */
+    public void setAuxiliaryOutputFile(FileDescriptor fd)
+    {
+        mPrepareAuxiliaryFile = true;
+        mPathAux = null;
+        mFdAux = fd;
+        setAuxVideoParameters();
+    }
+
+    /**
+     * Pass in the file path for the auxiliary time lapse video. Call this before
+     * prepare().
+     *
+     * Sets file path and parameters for auxiliary time lapse video. Time lapse mode can
+     * capture video (using the still camera) at resolutions higher than that can be
+     * played back on the device. This function or
+     * {@link #setAuxiliaryOutputFile(FileDescriptor)} enable capture of a smaller
+     * video in parallel with the main time lapse video, which can be used to play back on
+     * the device. The smaller video is created by downsampling the main video. This call
+     * is optional and does not have to be called if parallel capture of a downsampled
+     * video is not desired.
+     *
+     * Note that while the main video resolution and bitrate is determined from the
+     * CamcorderProfile in {@link #setProfile(CamcorderProfile)}, the auxiliary video's
+     * resolution and bitrate are determined by the CamcorderProfile quality level
+     * {@link android.media.CamcorderProfile#QUALITY_HIGH}. All other encoding parameters
+     * remain the same for the main video and the auxiliary video.
+     *
+     * E.g. if the device supports the time lapse profile quality level
+     * {@link android.media.CamcorderProfile#QUALITY_TIME_LAPSE_1080P} but can playback at
+     * most 480p, the application might want to capture an auxiliary video of resolution
+     * 480p using this call.
+     *
+     * @param path The pathname to use.
+     */
+    public void setAuxiliaryOutputFile(String path)
+    {
+        mPrepareAuxiliaryFile = true;
+        mFdAux = null;
+        mPathAux = path;
+        setAuxVideoParameters();
+    }
+
+    /**
      * Pass in the file descriptor of the file to be written. Call this after
      * setOutputFormat() but before prepare().
      *
@@ -506,6 +616,8 @@
     // native implementation
     private native void _setOutputFile(FileDescriptor fd, long offset, long length)
         throws IllegalStateException, IOException;
+    private native void _setOutputFileAux(FileDescriptor fd)
+        throws IllegalStateException, IOException;
     private native void _prepare() throws IllegalStateException, IOException;
 
     /**
@@ -531,6 +643,22 @@
         } else {
             throw new IOException("No valid output file");
         }
+
+        if (mPrepareAuxiliaryFile) {
+            if (mPathAux != null) {
+                FileOutputStream fos = new FileOutputStream(mPathAux);
+                try {
+                    _setOutputFileAux(fos.getFD());
+                } finally {
+                    fos.close();
+                }
+            } else if (mFdAux != null) {
+                _setOutputFileAux(mFdAux);
+            } else {
+                throw new IOException("No valid output file");
+            }
+        }
+
         _prepare();
     }
 
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 3333268..4fe2c43 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -26,14 +26,17 @@
 import android.content.IContentProvider;
 import android.database.Cursor;
 import android.database.SQLException;
+import android.drm.DrmManagerClient;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
+import android.os.Environment;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.provider.MediaStore.Audio;
+import android.provider.MediaStore.Files;
 import android.provider.MediaStore.Images;
 import android.provider.MediaStore.Video;
 import android.provider.MediaStore.Audio.Genres;
@@ -109,41 +112,21 @@
 
     private final static String TAG = "MediaScanner";
 
-    private static final String[] AUDIO_PROJECTION = new String[] {
-            Audio.Media._ID, // 0
-            Audio.Media.DATA, // 1
-            Audio.Media.DATE_MODIFIED, // 2
+    private static final String[] FILES_PRESCAN_PROJECTION = new String[] {
+            Files.FileColumns._ID, // 0
+            Files.FileColumns.DATA, // 1
+            Files.FileColumns.FORMAT, // 2
+            Files.FileColumns.DATE_MODIFIED, // 3
     };
 
-    private static final int ID_AUDIO_COLUMN_INDEX = 0;
-    private static final int PATH_AUDIO_COLUMN_INDEX = 1;
-    private static final int DATE_MODIFIED_AUDIO_COLUMN_INDEX = 2;
-
-    private static final String[] VIDEO_PROJECTION = new String[] {
-            Video.Media._ID, // 0
-            Video.Media.DATA, // 1
-            Video.Media.DATE_MODIFIED, // 2
+    private static final String[] ID_PROJECTION = new String[] {
+            Files.FileColumns._ID,
     };
 
-    private static final int ID_VIDEO_COLUMN_INDEX = 0;
-    private static final int PATH_VIDEO_COLUMN_INDEX = 1;
-    private static final int DATE_MODIFIED_VIDEO_COLUMN_INDEX = 2;
-
-    private static final String[] IMAGES_PROJECTION = new String[] {
-            Images.Media._ID, // 0
-            Images.Media.DATA, // 1
-            Images.Media.DATE_MODIFIED, // 2
-    };
-
-    private static final int ID_IMAGES_COLUMN_INDEX = 0;
-    private static final int PATH_IMAGES_COLUMN_INDEX = 1;
-    private static final int DATE_MODIFIED_IMAGES_COLUMN_INDEX = 2;
-
-    private static final String[] PLAYLISTS_PROJECTION = new String[] {
-            Audio.Playlists._ID, // 0
-            Audio.Playlists.DATA, // 1
-            Audio.Playlists.DATE_MODIFIED, // 2
-    };
+    private static final int FILES_PRESCAN_ID_COLUMN_INDEX = 0;
+    private static final int FILES_PRESCAN_PATH_COLUMN_INDEX = 1;
+    private static final int FILES_PRESCAN_FORMAT_COLUMN_INDEX = 2;
+    private static final int FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 3;
 
     private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
             Audio.Playlists.Members.PLAYLIST_ID, // 0
@@ -304,7 +287,12 @@
     private Uri mThumbsUri;
     private Uri mGenresUri;
     private Uri mPlaylistsUri;
+    private Uri mFilesUri;
     private boolean mProcessPlaylists, mProcessGenres;
+    private int mMtpObjectHandle;
+
+    private final String mMediaStoragePath;
+    private final String mExternalStoragePath;
 
     // used when scanning the image database so we know whether we have to prune
     // old thumbnail files
@@ -339,21 +327,23 @@
         long mRowId;
         String mPath;
         long mLastModified;
+        int mFormat;
         boolean mSeenInFileSystem;
         boolean mLastModifiedChanged;
 
-        FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified) {
+        FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified, int format) {
             mTableUri = tableUri;
             mRowId = rowId;
             mPath = path;
             mLastModified = lastModified;
+            mFormat = format;
             mSeenInFileSystem = false;
             mLastModifiedChanged = false;
         }
 
         @Override
         public String toString() {
-            return mPath;
+            return mPath + " mTableUri: " + mTableUri + " mRowId: " + mRowId;
         }
     }
 
@@ -364,6 +354,7 @@
     private ArrayList<FileCacheEntry> mPlayLists;
     private HashMap<String, Uri> mGenreCache;
 
+    private DrmManagerClient mDrmManagerClient = null;
 
     public MediaScanner(Context c) {
         native_setup();
@@ -372,6 +363,14 @@
         mBitmapOptions.inJustDecodeBounds = true;
 
         setDefaultRingtoneFileNames();
+
+        String mediaStoragePath = SystemProperties.get("ro.media.storage");
+        if (mediaStoragePath != null &&  mediaStoragePath.length() > 0) {
+            mMediaStoragePath = mediaStoragePath;
+        } else {
+            mMediaStoragePath = null;
+        }
+        mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath();
     }
 
     private void setDefaultRingtoneFileNames() {
@@ -385,6 +384,11 @@
 
     private MyMediaScannerClient mClient = new MyMediaScannerClient();
 
+    private boolean isDrmEnabled() {
+        String prop = SystemProperties.get("drm.service.enabled");
+        return prop != null && prop.equals("true");
+    }
+    
     private class MyMediaScannerClient implements MediaScannerClient {
 
         private String mArtist;
@@ -431,6 +435,9 @@
             }
 
             mMimeType = null;
+            mFileType = 0;
+            mFileSize = fileSize;
+
             // try mimeType first, if it is specified
             if (mimeType != null) {
                 mFileType = MediaFile.getFileTypeForMimeType(mimeType);
@@ -438,7 +445,6 @@
                     mMimeType = mimeType;
                 }
             }
-            mFileSize = fileSize;
 
             // if mimeType was not specified, compute file type based on file extension.
             if (mMimeType == null) {
@@ -449,13 +455,32 @@
                 }
             }
 
+            if (isDrmEnabled() && MediaFile.isDrmFileType(mFileType)) {
+                mFileType = getFileTypeFromDrm(path);
+            }
+
             String key = path;
+            if (mMediaStoragePath != null && key.startsWith(mMediaStoragePath)) {
+                // MediaProvider uses external variant of path for _data, so we need to match
+                // against that path instead.
+                key = mExternalStoragePath + key.substring(mMediaStoragePath.length());
+            }
             if (mCaseInsensitivePaths) {
                 key = path.toLowerCase();
             }
             FileCacheEntry entry = mFileCache.get(key);
             if (entry == null) {
-                entry = new FileCacheEntry(null, 0, path, 0);
+                Uri tableUri;
+                if (MediaFile.isVideoFileType(mFileType)) {
+                    tableUri = mVideoUri;
+                } else if (MediaFile.isImageFileType(mFileType)) {
+                    tableUri = mImagesUri;
+                } else if (MediaFile.isAudioFileType(mFileType)) {
+                    tableUri = mAudioUri;
+                } else {
+                    tableUri = mFilesUri;
+                }
+                entry = new FileCacheEntry(tableUri, 0, path, 0, 0);
                 mFileCache.put(key, entry);
             }
             entry.mSeenInFileSystem = true;
@@ -500,7 +525,8 @@
             doScanFile(path, mimeType, lastModified, fileSize, false);
         }
 
-        public Uri doScanFile(String path, String mimeType, long lastModified, long fileSize, boolean scanAlways) {
+        public Uri doScanFile(String path, String mimeType, long lastModified,
+                long fileSize, boolean scanAlways) {
             Uri result = null;
 //            long t1 = System.currentTimeMillis();
             try {
@@ -515,7 +541,9 @@
                     boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) ||
                         (!ringtones && !notifications && !alarms && !podcasts);
 
-                    if (!MediaFile.isImageFileType(mFileType)) {
+                    // we only extract metadata for audio and video files
+                    if (MediaFile.isAudioFileType(mFileType)
+                            || MediaFile.isVideoFileType(mFileType)) {
                         processFile(path, mimeType, this);
                     }
 
@@ -555,7 +583,8 @@
                 mTitle = value;
             } else if (name.equalsIgnoreCase("artist") || name.startsWith("artist;")) {
                 mArtist = value.trim();
-            } else if (name.equalsIgnoreCase("albumartist") || name.startsWith("albumartist;")) {
+            } else if (name.equalsIgnoreCase("albumartist") || name.startsWith("albumartist;")
+                    || name.equalsIgnoreCase("band") || name.startsWith("band;")) {
                 mAlbumArtist = value.trim();
             } else if (name.equalsIgnoreCase("album") || name.startsWith("album;")) {
                 mAlbum = value.trim();
@@ -654,21 +683,6 @@
                 boolean alarms, boolean music, boolean podcasts)
                 throws RemoteException {
             // update database
-            Uri tableUri;
-            boolean isAudio = MediaFile.isAudioFileType(mFileType);
-            boolean isVideo = MediaFile.isVideoFileType(mFileType);
-            boolean isImage = MediaFile.isImageFileType(mFileType);
-            if (isVideo) {
-                tableUri = mVideoUri;
-            } else if (isImage) {
-                tableUri = mImagesUri;
-            } else if (isAudio) {
-                tableUri = mAudioUri;
-            } else {
-                // don't add file to database if not audio, video or image
-                return null;
-            }
-            entry.mTableUri = tableUri;
 
              // use album artist if artist is missing
             if (mArtist == null || mArtist.length() == 0) {
@@ -678,20 +692,7 @@
             ContentValues values = toValues();
             String title = values.getAsString(MediaStore.MediaColumns.TITLE);
             if (title == null || TextUtils.isEmpty(title.trim())) {
-                title = values.getAsString(MediaStore.MediaColumns.DATA);
-                // extract file name after last slash
-                int lastSlash = title.lastIndexOf('/');
-                if (lastSlash >= 0) {
-                    lastSlash++;
-                    if (lastSlash < title.length()) {
-                        title = title.substring(lastSlash);
-                    }
-                }
-                // truncate the file extension (if any)
-                int lastDot = title.lastIndexOf('.');
-                if (lastDot > 0) {
-                    title = title.substring(0, lastDot);
-                }
+                title = MediaFile.getFileTitle(values.getAsString(MediaStore.MediaColumns.DATA));
                 values.put(MediaStore.MediaColumns.TITLE, title);
             }
             String album = values.getAsString(Audio.Media.ALBUM);
@@ -715,7 +716,7 @@
                 }
             }
             long rowId = entry.mRowId;
-            if (isAudio && rowId == 0) {
+            if (MediaFile.isAudioFileType(mFileType) && (rowId == 0 || mMtpObjectHandle != 0)) {
                 // Only set these for new entries. For existing entries, they
                 // may have been modified later, and we want to keep the current
                 // values so that custom ringtones still show up in the ringtone
@@ -768,8 +769,15 @@
                 }
             }
 
+            Uri tableUri = entry.mTableUri;
             Uri result = null;
             if (rowId == 0) {
+                if (mMtpObjectHandle != 0) {
+                    values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
+                }
+                if (tableUri == mFilesUri) {
+                    values.put(Files.FileColumns.FORMAT, MediaFile.getFormatCode(entry.mPath, mMimeType));
+                }
                 // new file, insert it
                 result = mMediaProvider.insert(tableUri, values);
                 if (result != null) {
@@ -883,9 +891,30 @@
             }
         }
 
+        private int getFileTypeFromDrm(String path) {
+            if (!isDrmEnabled()) {
+                return 0;
+            }
+
+            int resultFileType = 0;
+
+            if (mDrmManagerClient == null) {
+                mDrmManagerClient = new DrmManagerClient(mContext);
+            }
+
+            if (mDrmManagerClient.canHandle(path, null)) {
+                String drmMimetype = mDrmManagerClient.getOriginalMimeType(path);
+                if (drmMimetype != null) {
+                    mMimeType = drmMimetype;
+                    resultFileType = MediaFile.getFileTypeForMimeType(drmMimetype);
+                }
+            }
+            return resultFileType;
+        }
+
     }; // end of anonymous MediaScannerClient instance
 
-    private void prescan(String filePath) throws RemoteException {
+    private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
         Cursor c = null;
         String where = null;
         String[] selectionArgs = null;
@@ -901,138 +930,49 @@
             mPlayLists.clear();
         }
 
+        if (filePath != null) {
+            if (mMediaStoragePath != null && filePath.startsWith(mMediaStoragePath)) {
+                // MediaProvider uses external variant of path for _data, so we need to query
+                // using that path instead.
+                filePath = mExternalStoragePath + filePath.substring(mMediaStoragePath.length());
+            }
+
+            // query for only one file
+            where = Files.FileColumns.DATA + "=?";
+            selectionArgs = new String[] { filePath };
+        }
+
         // Build the list of files from the content provider
         try {
-            // Read existing files from the audio table
-            if (filePath != null) {
-                where = MediaStore.Audio.Media.DATA + "=?";
-                selectionArgs = new String[] { filePath };
-            }
-            c = mMediaProvider.query(mAudioUri, AUDIO_PROJECTION, where, selectionArgs, null);
+            if (prescanFiles) {
+                // First read existing files from the files table
 
-            if (c != null) {
-                try {
-                    while (c.moveToNext()) {
-                        long rowId = c.getLong(ID_AUDIO_COLUMN_INDEX);
-                        String path = c.getString(PATH_AUDIO_COLUMN_INDEX);
-                        long lastModified = c.getLong(DATE_MODIFIED_AUDIO_COLUMN_INDEX);
-
-                        // Only consider entries with absolute path names.
-                        // This allows storing URIs in the database without the
-                        // media scanner removing them.
-                        if (path.startsWith("/")) {
-                            String key = path;
-                            if (mCaseInsensitivePaths) {
-                                key = path.toLowerCase();
-                            }
-                            mFileCache.put(key, new FileCacheEntry(mAudioUri, rowId, path,
-                                    lastModified));
-                        }
-                    }
-                } finally {
-                    c.close();
-                    c = null;
-                }
-            }
-
-            // Read existing files from the video table
-            if (filePath != null) {
-                where = MediaStore.Video.Media.DATA + "=?";
-            } else {
-                where = null;
-            }
-            c = mMediaProvider.query(mVideoUri, VIDEO_PROJECTION, where, selectionArgs, null);
-
-            if (c != null) {
-                try {
-                    while (c.moveToNext()) {
-                        long rowId = c.getLong(ID_VIDEO_COLUMN_INDEX);
-                        String path = c.getString(PATH_VIDEO_COLUMN_INDEX);
-                        long lastModified = c.getLong(DATE_MODIFIED_VIDEO_COLUMN_INDEX);
-
-                        // Only consider entries with absolute path names.
-                        // This allows storing URIs in the database without the
-                        // media scanner removing them.
-                        if (path.startsWith("/")) {
-                            String key = path;
-                            if (mCaseInsensitivePaths) {
-                                key = path.toLowerCase();
-                            }
-                            mFileCache.put(key, new FileCacheEntry(mVideoUri, rowId, path,
-                                    lastModified));
-                        }
-                    }
-                } finally {
-                    c.close();
-                    c = null;
-                }
-            }
-
-            // Read existing files from the images table
-            if (filePath != null) {
-                where = MediaStore.Images.Media.DATA + "=?";
-            } else {
-                where = null;
-            }
-            mOriginalCount = 0;
-            c = mMediaProvider.query(mImagesUri, IMAGES_PROJECTION, where, selectionArgs, null);
-
-            if (c != null) {
-                try {
-                    mOriginalCount = c.getCount();
-                    while (c.moveToNext()) {
-                        long rowId = c.getLong(ID_IMAGES_COLUMN_INDEX);
-                        String path = c.getString(PATH_IMAGES_COLUMN_INDEX);
-                       long lastModified = c.getLong(DATE_MODIFIED_IMAGES_COLUMN_INDEX);
-
-                       // Only consider entries with absolute path names.
-                       // This allows storing URIs in the database without the
-                       // media scanner removing them.
-                       if (path.startsWith("/")) {
-                           String key = path;
-                           if (mCaseInsensitivePaths) {
-                               key = path.toLowerCase();
-                           }
-                           mFileCache.put(key, new FileCacheEntry(mImagesUri, rowId, path,
-                                   lastModified));
-                       }
-                    }
-                } finally {
-                    c.close();
-                    c = null;
-                }
-            }
-
-            if (mProcessPlaylists) {
-                // Read existing files from the playlists table
-                if (filePath != null) {
-                    where = MediaStore.Audio.Playlists.DATA + "=?";
-                } else {
-                    where = null;
-                }
-                c = mMediaProvider.query(mPlaylistsUri, PLAYLISTS_PROJECTION, where, selectionArgs, null);
+                c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
+                        where, selectionArgs, null);
 
                 if (c != null) {
-                    try {
-                        while (c.moveToNext()) {
-                            String path = c.getString(PATH_PLAYLISTS_COLUMN_INDEX);
+                    while (c.moveToNext()) {
+                        long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
+                        String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
+                        int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
+                        long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
 
-                            if (path != null && path.length() > 0) {
-                                long rowId = c.getLong(ID_PLAYLISTS_COLUMN_INDEX);
-                                long lastModified = c.getLong(DATE_MODIFIED_PLAYLISTS_COLUMN_INDEX);
-
-                                String key = path;
-                                if (mCaseInsensitivePaths) {
-                                    key = path.toLowerCase();
-                                }
-                                mFileCache.put(key, new FileCacheEntry(mPlaylistsUri, rowId, path,
-                                        lastModified));
+                        // Only consider entries with absolute path names.
+                        // This allows storing URIs in the database without the
+                        // media scanner removing them.
+                        if (path.startsWith("/")) {
+                            String key = path;
+                            if (mCaseInsensitivePaths) {
+                                key = path.toLowerCase();
                             }
+
+                            FileCacheEntry entry = new FileCacheEntry(mFilesUri, rowId, path,
+                                    lastModified, format);
+                            mFileCache.put(key, entry);
                         }
-                    } finally {
-                        c.close();
-                        c = null;
                     }
+                    c.close();
+                    c = null;
                 }
             }
         }
@@ -1041,6 +981,14 @@
                 c.close();
             }
         }
+
+        // compute original size of images
+        mOriginalCount = 0;
+        c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null);
+        if (c != null) {
+            mOriginalCount = c.getCount();
+            c.close();
+        }
     }
 
     private boolean inScanDirectory(String path, String[] directories) {
@@ -1107,12 +1055,14 @@
             // remove database entries for files that no longer exist.
             boolean fileMissing = false;
 
-            if (!entry.mSeenInFileSystem) {
-                if (inScanDirectory(path, directories)) {
+            if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) {
+                if (entry.mFormat != MtpConstants.FORMAT_ASSOCIATION &&
+                        inScanDirectory(path, directories)) {
                     // we didn't see this file in the scan directory.
                     fileMissing = true;
                 } else {
-                    // the file is outside of our scan directory,
+                    // the file actually a directory or other abstract object
+                    // or is outside of our scan directory,
                     // so we need to check for file existence here.
                     File testFile = new File(path);
                     if (!testFile.exists()) {
@@ -1132,9 +1082,11 @@
                     ContentValues values = new ContentValues();
                     values.put(MediaStore.Audio.Playlists.DATA, "");
                     values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, 0);
-                    mMediaProvider.update(ContentUris.withAppendedId(mPlaylistsUri, entry.mRowId), values, null, null);
+                    mMediaProvider.update(ContentUris.withAppendedId(mPlaylistsUri, entry.mRowId),
+                            values, null, null);
                 } else {
-                    mMediaProvider.delete(ContentUris.withAppendedId(entry.mTableUri, entry.mRowId), null, null);
+                    mMediaProvider.delete(ContentUris.withAppendedId(mFilesUri, entry.mRowId),
+                            null, null);
                     iterator.remove();
                 }
             }
@@ -1162,6 +1114,7 @@
         mVideoUri = Video.Media.getContentUri(volumeName);
         mImagesUri = Images.Media.getContentUri(volumeName);
         mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
+        mFilesUri = Files.getContentUri(volumeName);
 
         if (!volumeName.equals("internal")) {
             // we only support playlists on external media
@@ -1170,9 +1123,12 @@
             mGenreCache = new HashMap<String, Uri>();
             mGenresUri = Genres.getContentUri(volumeName);
             mPlaylistsUri = Playlists.getContentUri(volumeName);
-            // assuming external storage is FAT (case insensitive), except on the simulator.
-            if ( Process.supportsProcesses()) {
-                mCaseInsensitivePaths = true;
+
+            mCaseInsensitivePaths = !mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_caseSensitiveExternalStorage);
+            if (!Process.supportsProcesses()) {
+                // Simulator uses host file system, so it should be case sensitive.
+                mCaseInsensitivePaths = false;
             }
         }
     }
@@ -1181,11 +1137,11 @@
         try {
             long start = System.currentTimeMillis();
             initialize(volumeName);
-            prescan(null);
+            prescan(null, true);
             long prescan = System.currentTimeMillis();
 
             for (int i = 0; i < directories.length; i++) {
-                processDirectory(directories[i], MediaFile.sFileExtensions, mClient);
+                processDirectory(directories[i], mClient);
             }
             long scan = System.currentTimeMillis();
             postscan(directories);
@@ -1212,7 +1168,7 @@
     public Uri scanSingleFile(String path, String volumeName, String mimeType) {
         try {
             initialize(volumeName);
-            prescan(path);
+            prescan(path, true);
 
             File file = new File(path);
 
@@ -1227,6 +1183,36 @@
         }
     }
 
+    public void scanMtpFile(String path, String volumeName, int objectHandle, int format) {
+        MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
+        int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
+
+        if (!MediaFile.isAudioFileType(fileType) && !MediaFile.isVideoFileType(fileType) &&
+            !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType)) {
+            // nothing to do
+            return;
+        }
+
+        mMtpObjectHandle = objectHandle;
+        try {
+            initialize(volumeName);
+            // MTP will create a file entry for us so we don't want to do it in prescan
+            prescan(path, false);
+
+            File file = new File(path);
+
+            // lastModified is in milliseconds on Files.
+            long lastModifiedSeconds = file.lastModified() / 1000;
+
+            // always scan the file, so we can return the content://media Uri for existing files
+            mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(), true);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
+        } finally {
+            mMtpObjectHandle = 0;
+        }
+    }
+
     // returns the number of matching file/directory names, starting from the right
     private int matchPaths(String path1, String path2) {
         int result = 0;
@@ -1506,7 +1492,7 @@
         }
     }
 
-    private native void processDirectory(String path, String extensions, MediaScannerClient client);
+    private native void processDirectory(String path, MediaScannerClient client);
     private native void processFile(String path, String mimeType, MediaScannerClient client);
     public native void setLocale(String locale);
 
diff --git a/media/java/android/media/MtpClient.java b/media/java/android/media/MtpClient.java
new file mode 100644
index 0000000..98da1f6
--- /dev/null
+++ b/media/java/android/media/MtpClient.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+/**
+ * {@hide}
+ */
+public class MtpClient {
+
+    private static final String TAG = "MtpClient";
+
+    private final Listener mListener;
+
+    static {
+        System.loadLibrary("media_jni");
+    }
+
+    public MtpClient(Listener listener) {
+        native_setup();
+        if (listener == null) {
+            throw new NullPointerException("MtpClient: listener is null");
+        }
+        mListener = listener;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            native_finalize();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    public boolean start() {
+        return native_start();
+    }
+
+    public void stop() {
+        native_stop();
+    }
+
+    public boolean deleteObject(int deviceID, long objectID) {
+        return native_delete_object(deviceID, objectID);
+    }
+
+    public long getParent(int deviceID, long objectID) {
+        return native_get_parent(deviceID, objectID);
+    }
+
+    public long getStorageID(int deviceID, long objectID) {
+        return native_get_storage_id(deviceID, objectID);
+    }
+
+    // create a file descriptor for reading the contents of an object over MTP
+    public ParcelFileDescriptor openFile(int deviceID, long objectID) {
+        return native_open_file(deviceID, objectID);
+    }
+
+    public interface Listener {
+        // called when a new MTP device has been discovered
+        void deviceAdded(int id);
+
+        // called when an MTP device has been removed
+        void deviceRemoved(int id);
+    }
+
+    // called from native code
+    private void deviceAdded(int id) {
+        Log.d(TAG, "deviceAdded " + id);
+        mListener.deviceAdded(id);
+    }
+
+    // called from native code
+    private void deviceRemoved(int id) {
+        Log.d(TAG, "deviceRemoved " + id);
+        mListener.deviceRemoved(id);
+    }
+
+    // used by the JNI code
+    private int mNativeContext;
+
+    private native final void native_setup();
+    private native final void native_finalize();
+    private native boolean native_start();
+    private native void native_stop();
+    private native boolean native_delete_object(int deviceID, long objectID);
+    private native long native_get_parent(int deviceID, long objectID);
+    private native long native_get_storage_id(int deviceID, long objectID);
+    private native ParcelFileDescriptor native_open_file(int deviceID, long objectID);
+}
diff --git a/media/java/android/media/MtpConstants.java b/media/java/android/media/MtpConstants.java
new file mode 100644
index 0000000..a7d33ce
--- /dev/null
+++ b/media/java/android/media/MtpConstants.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+public final class MtpConstants {
+
+// MTP Response Codes
+    public static final int RESPONSE_UNDEFINED = 0x2000;
+    public static final int RESPONSE_OK = 0x2001;
+    public static final int RESPONSE_GENERAL_ERROR = 0x2002;
+    public static final int RESPONSE_SESSION_NOT_OPEN = 0x2003;
+    public static final int RESPONSE_INVALID_TRANSACTION_ID = 0x2004;
+    public static final int RESPONSE_OPERATION_NOT_SUPPORTED = 0x2005;
+    public static final int RESPONSE_PARAMETER_NOT_SUPPORTED = 0x2006;
+    public static final int RESPONSE_INCOMPLETE_TRANSFER = 0x2007;
+    public static final int RESPONSE_INVALID_STORAGE_ID = 0x2008;
+    public static final int RESPONSE_INVALID_OBJECT_HANDLE = 0x2009;
+    public static final int RESPONSE_DEVICE_PROP_NOT_SUPPORTED = 0x200A;
+    public static final int RESPONSE_INVALID_OBJECT_FORMAT_CODE = 0x200B;
+    public static final int RESPONSE_STORAGE_FULL = 0x200C;
+    public static final int RESPONSE_OBJECT_WRITE_PROTECTED = 0x200D;
+    public static final int RESPONSE_STORE_READ_ONLY = 0x200E;
+    public static final int RESPONSE_ACCESS_DENIED = 0x200F;
+    public static final int RESPONSE_NO_THUMBNAIL_PRESENT = 0x2010;
+    public static final int RESPONSE_SELF_TEST_FAILED = 0x2011;
+    public static final int RESPONSE_PARTIAL_DELETION = 0x2012;
+    public static final int RESPONSE_STORE_NOT_AVAILABLE = 0x2013;
+    public static final int RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED = 0x2014;
+    public static final int RESPONSE_NO_VALID_OBJECT_INFO = 0x2015;
+    public static final int RESPONSE_INVALID_CODE_FORMAT = 0x2016;
+    public static final int RESPONSE_UNKNOWN_VENDOR_CODE = 0x2017;
+    public static final int RESPONSE_CAPTURE_ALREADY_TERMINATED = 0x2018;
+    public static final int RESPONSE_DEVICE_BUSY = 0x2019;
+    public static final int RESPONSE_INVALID_PARENT_OBJECT = 0x201A;
+    public static final int RESPONSE_INVALID_DEVICE_PROP_FORMAT = 0x201B;
+    public static final int RESPONSE_INVALID_DEVICE_PROP_VALUE = 0x201C;
+    public static final int RESPONSE_INVALID_PARAMETER = 0x201D;
+    public static final int RESPONSE_SESSION_ALREADY_OPEN = 0x201E;
+    public static final int RESPONSE_TRANSACTION_CANCELLED = 0x201F;
+    public static final int RESPONSE_SPECIFICATION_OF_DESTINATION_UNSUPPORTED = 0x2020;
+    public static final int RESPONSE_INVALID_OBJECT_PROP_CODE = 0xA801;
+    public static final int RESPONSE_INVALID_OBJECT_PROP_FORMAT = 0xA802;
+    public static final int RESPONSE_INVALID_OBJECT_PROP_VALUE = 0xA803;
+    public static final int RESPONSE_INVALID_OBJECT_REFERENCE = 0xA804;
+    public static final int RESPONSE_GROUP_NOT_SUPPORTED = 0xA805;
+    public static final int RESPONSE_INVALID_DATASET = 0xA806;
+    public static final int RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED = 0xA807;
+    public static final int RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED = 0xA808;
+    public static final int RESPONSE_OBJECT_TOO_LARGE = 0xA809;
+    public static final int RESPONSE_OBJECT_PROP_NOT_SUPPORTED = 0xA80A;
+
+    // MTP format codes
+    public static final int FORMAT_UNDEFINED = 0x3000;
+    public static final int FORMAT_ASSOCIATION = 0x3001;
+    public static final int FORMAT_SCRIPT = 0x3002;
+    public static final int FORMAT_EXECUTABLE = 0x3003;
+    public static final int FORMAT_TEXT = 0x3004;
+    public static final int FORMAT_HTML = 0x3005;
+    public static final int FORMAT_DPOF = 0x3006;
+    public static final int FORMAT_AIFF = 0x3007;
+    public static final int FORMAT_WAV = 0x3008;
+    public static final int FORMAT_MP3 = 0x3009;
+    public static final int FORMAT_AVI = 0x300A;
+    public static final int FORMAT_MPEG = 0x300B;
+    public static final int FORMAT_ASF = 0x300C;
+    public static final int FORMAT_DEFINED = 0x3800;
+    public static final int FORMAT_EXIF_JPEG = 0x3801;
+    public static final int FORMAT_TIFF_EP = 0x3802;
+    public static final int FORMAT_FLASHPIX = 0x3803;
+    public static final int FORMAT_BMP = 0x3804;
+    public static final int FORMAT_CIFF = 0x3805;
+    public static final int FORMAT_GIF = 0x3807;
+    public static final int FORMAT_JFIF = 0x3808;
+    public static final int FORMAT_CD = 0x3809;
+    public static final int FORMAT_PICT = 0x380A;
+    public static final int FORMAT_PNG = 0x380B;
+    public static final int FORMAT_TIFF = 0x380D;
+    public static final int FORMAT_TIFF_IT = 0x380E;
+    public static final int FORMAT_JP2 = 0x380F;
+    public static final int FORMAT_JPX = 0x3810;
+    public static final int FORMAT_UNDEFINED_FIRMWARE = 0xB802;
+    public static final int FORMAT_WINDOWS_IMAGE_FORMAT = 0xB881;
+    public static final int FORMAT_UNDEFINED_AUDIO = 0xB900;
+    public static final int FORMAT_WMA = 0xB901;
+    public static final int FORMAT_OGG = 0xB902;
+    public static final int FORMAT_AAC = 0xB903;
+    public static final int FORMAT_AUDIBLE = 0xB904;
+    public static final int FORMAT_FLAC = 0xB906;
+    public static final int FORMAT_UNDEFINED_VIDEO = 0xB980;
+    public static final int FORMAT_WMV = 0xB981;
+    public static final int FORMAT_MP4_CONTAINER = 0xB982;
+    public static final int FORMAT_MP2 = 0xB983;
+    public static final int FORMAT_3GP_CONTAINER = 0xB984;
+    public static final int FORMAT_UNDEFINED_COLLECTION = 0xBA00;
+    public static final int FORMAT_ABSTRACT_MULTIMEDIA_ALBUM = 0xBA01;
+    public static final int FORMAT_ABSTRACT_IMAGE_ALBUM = 0xBA02;
+    public static final int FORMAT_ABSTRACT_AUDIO_ALBUM = 0xBA03;
+    public static final int FORMAT_ABSTRACT_VIDEO_ALBUM = 0xBA04;
+    public static final int FORMAT_ABSTRACT_AV_PLAYLIST = 0xBA05;
+    public static final int FORMAT_ABSTRACT_CONTACT_GROUP = 0xBA06;
+    public static final int FORMAT_ABSTRACT_MESSAGE_FOLDER = 0xBA07;
+    public static final int FORMAT_ABSTRACT_CHAPTERED_PRODUCTION = 0xBA08;
+    public static final int FORMAT_ABSTRACT_AUDIO_PLAYLIST = 0xBA09;
+    public static final int FORMAT_ABSTRACT_VIDEO_PLAYLIST = 0xBA0A;
+    public static final int FORMAT_ABSTRACT_MEDIACAST = 0xBA0B;
+    public static final int FORMAT_WPL_PLAYLIST = 0xBA10;
+    public static final int FORMAT_M3U_PLAYLIST = 0xBA11;
+    public static final int FORMAT_MPL_PLAYLIST = 0xBA12;
+    public static final int FORMAT_ASX_PLAYLIST = 0xBA13;
+    public static final int FORMAT_PLS_PLAYLIST = 0xBA14;
+    public static final int FORMAT_UNDEFINED_DOCUMENT = 0xBA80;
+    public static final int FORMAT_ABSTRACT_DOCUMENT = 0xBA81;
+    public static final int FORMAT_XML_DOCUMENT = 0xBA82;
+    public static final int FORMAT_MS_WORD_DOCUMENT = 0xBA83;
+    public static final int FORMAT_MHT_COMPILED_HTML_DOCUMENT = 0xBA84;
+    public static final int FORMAT_MS_EXCEL_SPREADSHEET = 0xBA85;
+    public static final int FORMAT_MS_POWERPOINT_PRESENTATION = 0xBA86;
+    public static final int FORMAT_UNDEFINED_MESSAGE = 0xBB00;
+    public static final int FORMAT_ABSTRACT_MESSSAGE = 0xBB01;
+    public static final int FORMAT_UNDEFINED_CONTACT = 0xBB80;
+    public static final int FORMAT_ABSTRACT_CONTACT = 0xBB81;
+    public static final int FORMAT_VCARD_2 = 0xBB82;
+
+    public static boolean isAbstractObject(int format) {
+        switch (format) {
+            case FORMAT_ABSTRACT_MULTIMEDIA_ALBUM:
+            case FORMAT_ABSTRACT_IMAGE_ALBUM:
+            case FORMAT_ABSTRACT_AUDIO_ALBUM:
+            case FORMAT_ABSTRACT_VIDEO_ALBUM:
+            case FORMAT_ABSTRACT_AV_PLAYLIST:
+            case FORMAT_ABSTRACT_CONTACT_GROUP:
+            case FORMAT_ABSTRACT_MESSAGE_FOLDER:
+            case FORMAT_ABSTRACT_CHAPTERED_PRODUCTION:
+            case FORMAT_ABSTRACT_AUDIO_PLAYLIST:
+            case FORMAT_ABSTRACT_VIDEO_PLAYLIST:
+            case FORMAT_ABSTRACT_MEDIACAST:
+            case FORMAT_ABSTRACT_DOCUMENT:
+            case FORMAT_ABSTRACT_MESSSAGE:
+            case FORMAT_ABSTRACT_CONTACT:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    // MTP object properties
+    public static final int PROPERTY_STORAGE_ID = 0xDC01;
+    public static final int PROPERTY_OBJECT_FORMAT = 0xDC02;
+    public static final int PROPERTY_PROTECTION_STATUS = 0xDC03;
+    public static final int PROPERTY_OBJECT_SIZE = 0xDC04;
+    public static final int PROPERTY_ASSOCIATION_TYPE = 0xDC05;
+    public static final int PROPERTY_ASSOCIATION_DESC = 0xDC06;
+    public static final int PROPERTY_OBJECT_FILE_NAME = 0xDC07;
+    public static final int PROPERTY_DATE_CREATED = 0xDC08;
+    public static final int PROPERTY_DATE_MODIFIED = 0xDC09;
+    public static final int PROPERTY_KEYWORDS = 0xDC0A;
+    public static final int PROPERTY_PARENT_OBJECT = 0xDC0B;
+    public static final int PROPERTY_ALLOWED_FOLDER_CONTENTS = 0xDC0C;
+    public static final int PROPERTY_HIDDEN = 0xDC0D;
+    public static final int PROPERTY_SYSTEM_OBJECT = 0xDC0E;
+    public static final int PROPERTY_PERSISTENT_UID = 0xDC41;
+    public static final int PROPERTY_SYNC_ID = 0xDC42;
+    public static final int PROPERTY_PROPERTY_BAG = 0xDC43;
+    public static final int PROPERTY_NAME = 0xDC44;
+    public static final int PROPERTY_CREATED_BY = 0xDC45;
+    public static final int PROPERTY_ARTIST = 0xDC46;
+    public static final int PROPERTY_DATE_AUTHORED = 0xDC47;
+    public static final int PROPERTY_DESCRIPTION = 0xDC48;
+    public static final int PROPERTY_URL_REFERENCE = 0xDC49;
+    public static final int PROPERTY_LANGUAGE_LOCALE = 0xDC4A;
+    public static final int PROPERTY_COPYRIGHT_INFORMATION = 0xDC4B;
+    public static final int PROPERTY_SOURCE = 0xDC4C;
+    public static final int PROPERTY_ORIGIN_LOCATION = 0xDC4D;
+    public static final int PROPERTY_DATE_ADDED = 0xDC4E;
+    public static final int PROPERTY_NON_CONSUMABLE = 0xDC4F;
+    public static final int PROPERTY_CORRUPT_UNPLAYABLE = 0xDC50;
+    public static final int PROPERTY_PRODUCER_SERIAL_NUMBER = 0xDC51;
+    public static final int PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT = 0xDC81;
+    public static final int PROPERTY_REPRESENTATIVE_SAMPLE_SIZE = 0xDC82;
+    public static final int PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT = 0xDC83;
+    public static final int PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH = 0xDC84;
+    public static final int PROPERTY_REPRESENTATIVE_SAMPLE_DURATION = 0xDC85;
+    public static final int PROPERTY_REPRESENTATIVE_SAMPLE_DATA = 0xDC86;
+    public static final int PROPERTY_WIDTH = 0xDC87;
+    public static final int PROPERTY_HEIGHT = 0xDC88;
+    public static final int PROPERTY_DURATION = 0xDC89;
+    public static final int PROPERTY_RATING = 0xDC8A;
+    public static final int PROPERTY_TRACK = 0xDC8B;
+    public static final int PROPERTY_GENRE = 0xDC8C;
+    public static final int PROPERTY_CREDITS = 0xDC8D;
+    public static final int PROPERTY_LYRICS = 0xDC8E;
+    public static final int PROPERTY_SUBSCRIPTION_CONTENT_ID = 0xDC8F;
+    public static final int PROPERTY_PRODUCED_BY = 0xDC90;
+    public static final int PROPERTY_USE_COUNT = 0xDC91;
+    public static final int PROPERTY_SKIP_COUNT = 0xDC92;
+    public static final int PROPERTY_LAST_ACCESSED = 0xDC93;
+    public static final int PROPERTY_PARENTAL_RATING = 0xDC94;
+    public static final int PROPERTY_META_GENRE = 0xDC95;
+    public static final int PROPERTY_COMPOSER = 0xDC96;
+    public static final int PROPERTY_EFFECTIVE_RATING = 0xDC97;
+    public static final int PROPERTY_SUBTITLE = 0xDC98;
+    public static final int PROPERTY_ORIGINAL_RELEASE_DATE = 0xDC99;
+    public static final int PROPERTY_ALBUM_NAME = 0xDC9A;
+    public static final int PROPERTY_ALBUM_ARTIST = 0xDC9B;
+    public static final int PROPERTY_MOOD = 0xDC9C;
+    public static final int PROPERTY_DRM_STATUS = 0xDC9D;
+    public static final int PROPERTY_SUB_DESCRIPTION = 0xDC9E;
+    public static final int PROPERTY_IS_CROPPED = 0xDCD1;
+    public static final int PROPERTY_IS_COLOUR_CORRECTED = 0xDCD2;
+    public static final int PROPERTY_IMAGE_BIT_DEPTH = 0xDCD3;
+    public static final int PROPERTY_F_NUMBER = 0xDCD4;
+    public static final int PROPERTY_EXPOSURE_TIME = 0xDCD5;
+    public static final int PROPERTY_EXPOSURE_INDEX = 0xDCD6;
+    public static final int PROPERTY_TOTAL_BITRATE = 0xDE91;
+    public static final int PROPERTY_BITRATE_TYPE = 0xDE92;
+    public static final int PROPERTY_SAMPLE_RATE = 0xDE93;
+    public static final int PROPERTY_NUMBER_OF_CHANNELS = 0xDE94;
+    public static final int PROPERTY_AUDIO_BIT_DEPTH = 0xDE95;
+    public static final int PROPERTY_SCAN_TYPE = 0xDE97;
+    public static final int PROPERTY_AUDIO_WAVE_CODEC = 0xDE99;
+    public static final int PROPERTY_AUDIO_BITRATE = 0xDE9A;
+    public static final int PROPERTY_VIDEO_FOURCC_CODEC = 0xDE9B;
+    public static final int PROPERTY_VIDEO_BITRATE = 0xDE9C;
+    public static final int PROPERTY_FRAMES_PER_THOUSAND_SECONDS = 0xDE9D;
+    public static final int PROPERTY_KEYFRAME_DISTANCE = 0xDE9E;
+    public static final int PROPERTY_BUFFER_SIZE = 0xDE9F;
+    public static final int PROPERTY_ENCODING_QUALITY = 0xDEA0;
+    public static final int PROPERTY_ENCODING_PROFILE = 0xDEA1;
+    public static final int PROPERTY_DISPLAY_NAME = 0xDCE0;
+    public static final int PROPERTY_BODY_TEXT = 0xDCE1;
+    public static final int PROPERTY_SUBJECT = 0xDCE2;
+    public static final int PROPERTY_PRIORITY = 0xDCE3;
+    public static final int PROPERTY_GIVEN_NAME = 0xDD00;
+    public static final int PROPERTY_MIDDLE_NAMES = 0xDD01;
+    public static final int PROPERTY_FAMILY_NAME = 0xDD02;
+    public static final int PROPERTY_PREFIX = 0xDD03;
+    public static final int PROPERTY_SUFFIX = 0xDD04;
+    public static final int PROPERTY_PHONETIC_GIVEN_NAME = 0xDD05;
+    public static final int PROPERTY_PHONETIC_FAMILY_NAME = 0xDD06;
+    public static final int PROPERTY_EMAIL_PRIMARY = 0xDD07;
+    public static final int PROPERTY_EMAIL_PERSONAL_1 = 0xDD08;
+    public static final int PROPERTY_EMAIL_PERSONAL_2 = 0xDD09;
+    public static final int PROPERTY_EMAIL_BUSINESS_1 = 0xDD0A;
+    public static final int PROPERTY_EMAIL_BUSINESS_2 = 0xDD0B;
+    public static final int PROPERTY_EMAIL_OTHERS = 0xDD0C;
+    public static final int PROPERTY_PHONE_NUMBER_PRIMARY = 0xDD0D;
+    public static final int PROPERTY_PHONE_NUMBER_PERSONAL = 0xDD0E;
+    public static final int PROPERTY_PHONE_NUMBER_PERSONAL_2 = 0xDD0F;
+    public static final int PROPERTY_PHONE_NUMBER_BUSINESS = 0xDD10;
+    public static final int PROPERTY_PHONE_NUMBER_BUSINESS_2 = 0xDD11;
+    public static final int PROPERTY_PHONE_NUMBER_MOBILE= 0xDD12;
+    public static final int PROPERTY_PHONE_NUMBER_MOBILE_2 = 0xDD13;
+    public static final int PROPERTY_FAX_NUMBER_PRIMARY = 0xDD14;
+    public static final int PROPERTY_FAX_NUMBER_PERSONAL= 0xDD15;
+    public static final int PROPERTY_FAX_NUMBER_BUSINESS= 0xDD16;
+    public static final int PROPERTY_PAGER_NUMBER = 0xDD17;
+    public static final int PROPERTY_PHONE_NUMBER_OTHERS= 0xDD18;
+    public static final int PROPERTY_PRIMARY_WEB_ADDRESS= 0xDD19;
+    public static final int PROPERTY_PERSONAL_WEB_ADDRESS = 0xDD1A;
+    public static final int PROPERTY_BUSINESS_WEB_ADDRESS = 0xDD1B;
+    public static final int PROPERTY_INSTANT_MESSANGER_ADDRESS = 0xDD1C;
+    public static final int PROPERTY_INSTANT_MESSANGER_ADDRESS_2 = 0xDD1D;
+    public static final int PROPERTY_INSTANT_MESSANGER_ADDRESS_3 = 0xDD1E;
+    public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL = 0xDD1F;
+    public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1 = 0xDD20;
+    public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2 = 0xDD21;
+    public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY = 0xDD22;
+    public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION = 0xDD23;
+    public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE = 0xDD24;
+    public static final int PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY = 0xDD25;
+    public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL = 0xDD26;
+    public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1 = 0xDD27;
+    public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2 = 0xDD28;
+    public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY = 0xDD29;
+    public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION = 0xDD2A;
+    public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE = 0xDD2B;
+    public static final int PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY = 0xDD2C;
+    public static final int PROPERTY_POSTAL_ADDRESS_OTHER_FULL = 0xDD2D;
+    public static final int PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1 = 0xDD2E;
+    public static final int PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2 = 0xDD2F;
+    public static final int PROPERTY_POSTAL_ADDRESS_OTHER_CITY = 0xDD30;
+    public static final int PROPERTY_POSTAL_ADDRESS_OTHER_REGION = 0xDD31;
+    public static final int PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE = 0xDD32;
+    public static final int PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY = 0xDD33;
+    public static final int PROPERTY_ORGANIZATION_NAME = 0xDD34;
+    public static final int PROPERTY_PHONETIC_ORGANIZATION_NAME = 0xDD35;
+    public static final int PROPERTY_ROLE = 0xDD36;
+    public static final int PROPERTY_BIRTHDATE = 0xDD37;
+    public static final int PROPERTY_MESSAGE_TO = 0xDD40;
+    public static final int PROPERTY_MESSAGE_CC = 0xDD41;
+    public static final int PROPERTY_MESSAGE_BCC = 0xDD42;
+    public static final int PROPERTY_MESSAGE_READ = 0xDD43;
+    public static final int PROPERTY_MESSAGE_RECEIVED_TIME = 0xDD44;
+    public static final int PROPERTY_MESSAGE_SENDER = 0xDD45;
+    public static final int PROPERTY_ACTIVITY_BEGIN_TIME = 0xDD50;
+    public static final int PROPERTY_ACTIVITY_END_TIME = 0xDD51;
+    public static final int PROPERTY_ACTIVITY_LOCATION = 0xDD52;
+    public static final int PROPERTY_ACTIVITY_REQUIRED_ATTENDEES = 0xDD54;
+    public static final int PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES = 0xDD55;
+    public static final int PROPERTY_ACTIVITY_RESOURCES = 0xDD56;
+    public static final int PROPERTY_ACTIVITY_ACCEPTED = 0xDD57;
+    public static final int PROPERTY_ACTIVITY_TENTATIVE = 0xDD58;
+    public static final int PROPERTY_ACTIVITY_DECLINED = 0xDD59;
+    public static final int PROPERTY_ACTIVITY_REMAINDER_TIME = 0xDD5A;
+    public static final int PROPERTY_ACTIVITY_OWNER = 0xDD5B;
+    public static final int PROPERTY_ACTIVITY_STATUS = 0xDD5C;
+    public static final int PROPERTY_OWNER = 0xDD5D;
+    public static final int PROPERTY_EDITOR = 0xDD5E;
+    public static final int PROPERTY_WEBMASTER = 0xDD5F;
+    public static final int PROPERTY_URL_SOURCE = 0xDD60;
+    public static final int PROPERTY_URL_DESTINATION = 0xDD61;
+    public static final int PROPERTY_TIME_BOOKMARK = 0xDD62;
+    public static final int PROPERTY_OBJECT_BOOKMARK = 0xDD63;
+    public static final int PROPERTY_BYTE_BOOKMARK = 0xDD64;
+    public static final int PROPERTY_LAST_BUILD_DATE = 0xDD70;
+    public static final int PROPERTY_TIME_TO_LIVE = 0xDD71;
+    public static final int PROPERTY_MEDIA_GUID = 0xDD72;
+
+    // MTP device properties
+    public static final int DEVICE_PROPERTY_UNDEFINED = 0x5000;
+    public static final int DEVICE_PROPERTY_BATTERY_LEVEL = 0x5001;
+    public static final int DEVICE_PROPERTY_FUNCTIONAL_MODE = 0x5002;
+    public static final int DEVICE_PROPERTY_IMAGE_SIZE = 0x5003;
+    public static final int DEVICE_PROPERTY_COMPRESSION_SETTING = 0x5004;
+    public static final int DEVICE_PROPERTY_WHITE_BALANCE = 0x5005;
+    public static final int DEVICE_PROPERTY_RGB_GAIN = 0x5006;
+    public static final int DEVICE_PROPERTY_F_NUMBER = 0x5007;
+    public static final int DEVICE_PROPERTY_FOCAL_LENGTH = 0x5008;
+    public static final int DEVICE_PROPERTY_FOCUS_DISTANCE = 0x5009;
+    public static final int DEVICE_PROPERTY_FOCUS_MODE = 0x500A;
+    public static final int DEVICE_PROPERTY_EXPOSURE_METERING_MODE = 0x500B;
+    public static final int DEVICE_PROPERTY_FLASH_MODE = 0x500C;
+    public static final int DEVICE_PROPERTY_EXPOSURE_TIME = 0x500D;
+    public static final int DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE = 0x500E;
+    public static final int DEVICE_PROPERTY_EXPOSURE_INDEX = 0x500F;
+    public static final int DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION = 0x5010;
+    public static final int DEVICE_PROPERTY_DATETIME = 0x5011;
+    public static final int DEVICE_PROPERTY_CAPTURE_DELAY = 0x5012;
+    public static final int DEVICE_PROPERTY_STILL_CAPTURE_MODE = 0x5013;
+    public static final int DEVICE_PROPERTY_CONTRAST = 0x5014;
+    public static final int DEVICE_PROPERTY_SHARPNESS = 0x5015;
+    public static final int DEVICE_PROPERTY_DIGITAL_ZOOM = 0x5016;
+    public static final int DEVICE_PROPERTY_EFFECT_MODE = 0x5017;
+    public static final int DEVICE_PROPERTY_BURST_NUMBER= 0x5018;
+    public static final int DEVICE_PROPERTY_BURST_INTERVAL = 0x5019;
+    public static final int DEVICE_PROPERTY_TIMELAPSE_NUMBER = 0x501A;
+    public static final int DEVICE_PROPERTY_TIMELAPSE_INTERVAL = 0x501B;
+    public static final int DEVICE_PROPERTY_FOCUS_METERING_MODE = 0x501C;
+    public static final int DEVICE_PROPERTY_UPLOAD_URL = 0x501D;
+    public static final int DEVICE_PROPERTY_ARTIST = 0x501E;
+    public static final int DEVICE_PROPERTY_COPYRIGHT_INFO = 0x501F;
+    public static final int DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER = 0xD401;
+    public static final int DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME = 0xD402;
+    public static final int DEVICE_PROPERTY_VOLUME = 0xD403;
+    public static final int DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED = 0xD404;
+    public static final int DEVICE_PROPERTY_DEVICE_ICON = 0xD405;
+    public static final int DEVICE_PROPERTY_PLAYBACK_RATE = 0xD410;
+    public static final int DEVICE_PROPERTY_PLAYBACK_OBJECT = 0xD411;
+    public static final int DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX = 0xD412;
+    public static final int DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO = 0xD406;
+    public static final int DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE = 0xD407;
+
+
+    /**
+     * Object is not protected. It may be modified and deleted, and its properties
+     * may be modified.
+     */
+    public static final int PROTECTION_STATUS_NONE = 0;
+
+    /**
+     * Object can not be modified or deleted and its properties can not be modified.
+     */
+    public static final int PROTECTION_STATUS_READ_ONLY = 0x8001;
+
+    /**
+     * Object can not be modified or deleted but its properties are modifiable.
+     */
+    public static final int PROTECTION_STATUS_READ_ONLY_DATA = 0x8002;
+
+    /**
+     * Object's contents can not be transfered from the device, but the object
+     * may be moved or deleted and its properties may be modified.
+     */
+    public static final int PROTECTION_STATUS_NON_TRANSFERABLE_DATA = 0x8003;
+
+    public static final int ASSOCIATION_TYPE_GENERIC_FOLDER = 0x0001;
+}
diff --git a/media/java/android/media/MtpCursor.java b/media/java/android/media/MtpCursor.java
new file mode 100644
index 0000000..9b5ab95
--- /dev/null
+++ b/media/java/android/media/MtpCursor.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.database.AbstractWindowedCursor;
+import android.database.CursorWindow;
+import android.provider.Mtp;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+  * Cursor class for MTP content provider
+  * @hide
+  */
+public final class MtpCursor extends AbstractWindowedCursor {
+    static final String TAG = "MtpCursor";
+    static final int NO_COUNT = -1;
+
+    /* constants for queryType */
+    public static final int DEVICE              = 1;
+    public static final int DEVICE_ID           = 2;
+    public static final int STORAGE             = 3;
+    public static final int STORAGE_ID          = 4;
+    public static final int OBJECT              = 5;
+    public static final int OBJECT_ID           = 6;
+    public static final int STORAGE_CHILDREN    = 7;
+    public static final int OBJECT_CHILDREN     = 8;
+
+    /** The names of the columns in the projection */
+    private String[] mColumns;
+
+    /** The number of rows in the cursor */
+    private int mCount = NO_COUNT;
+
+
+    public MtpCursor(MtpClient client, int queryType, int deviceID, long storageID, long objectID,
+            String[] projection) {
+        if (client == null) {
+            throw new NullPointerException("client null in MtpCursor constructor");
+        }
+        mColumns = projection;
+
+        HashMap<String, Integer> map;
+        switch (queryType) {
+            case DEVICE:
+            case DEVICE_ID:
+                map = sDeviceProjectionMap;
+                break;
+            case STORAGE:
+            case STORAGE_ID:
+                map = sStorageProjectionMap;
+                break;
+            case OBJECT:
+            case OBJECT_ID:
+            case STORAGE_CHILDREN:
+            case OBJECT_CHILDREN:
+                map = sObjectProjectionMap;
+                break;
+            default:
+                throw new IllegalArgumentException("unknown query type "  + queryType);
+        }
+
+        int[] columns = new int[projection.length];
+        for (int i = 0; i < projection.length; i++) {
+            Integer id = map.get(projection[i]);
+            if (id == null) {
+                throw new IllegalArgumentException("unknown column "  + projection[i]);
+            }
+            columns[i] = id.intValue();
+        }
+        native_setup(client, queryType, deviceID, storageID, objectID, columns);
+    }
+
+    @Override
+    protected void finalize() {
+        try {
+            native_finalize();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public int getCount() {
+        if (mCount == NO_COUNT) {
+            fillWindow(0);
+        }
+        return mCount;
+    }
+
+    @Override
+    public boolean requery() {
+        Log.d(TAG, "requery");
+        mCount = NO_COUNT;
+        if (mWindow != null) {
+            mWindow.clear();
+        }
+        return super.requery();
+    }
+
+    private void fillWindow(int startPos) {
+        if (mWindow == null) {
+            // If there isn't a window set already it will only be accessed locally
+            mWindow = new CursorWindow(true /* the window is local only */);
+        } else {
+                mWindow.clear();
+        }
+        mWindow.setStartPosition(startPos);
+        mCount = native_fill_window(mWindow, startPos);
+    }
+
+    @Override
+    public String[] getColumnNames() {
+        Log.d(TAG, "getColumnNames returning " + mColumns);
+        return mColumns;
+    }
+
+    /* Device Column IDs */
+     /* These must match the values in MtpCursor.cpp */
+   private static final int DEVICE_ROW_ID          = 1;
+    private static final int DEVICE_MANUFACTURER    = 2;
+    private static final int DEVICE_MODEL           = 3;
+
+    /* Storage Column IDs */
+    /* These must match the values in MtpCursor.cpp */
+    private static final int STORAGE_ROW_ID         = 101;
+    private static final int STORAGE_IDENTIFIER     = 102;
+    private static final int STORAGE_DESCRIPTION    = 103;
+
+    /* Object Column IDs */
+    /* These must match the values in MtpCursor.cpp */
+    private static final int OBJECT_ROW_ID              = 201;
+    private static final int OBJECT_STORAGE_ID          = 202;
+    private static final int OBJECT_FORMAT              = 203;
+    private static final int OBJECT_PROTECTION_STATUS   = 204;
+    private static final int OBJECT_SIZE                = 205;
+    private static final int OBJECT_THUMB_FORMAT        = 206;
+    private static final int OBJECT_THUMB_SIZE          = 207;
+    private static final int OBJECT_THUMB_WIDTH         = 208;
+    private static final int OBJECT_THUMB_HEIGHT        = 209;
+    private static final int OBJECT_IMAGE_WIDTH         = 210;
+    private static final int OBJECT_IMAGE_HEIGHT        = 211;
+    private static final int OBJECT_IMAGE_DEPTH         = 212;
+    private static final int OBJECT_PARENT              = 213;
+    private static final int OBJECT_ASSOCIATION_TYPE    = 214;
+    private static final int OBJECT_ASSOCIATION_DESC    = 215;
+    private static final int OBJECT_SEQUENCE_NUMBER     = 216;
+    private static final int OBJECT_NAME                = 217;
+    private static final int OBJECT_DATE_CREATED        = 218;
+    private static final int OBJECT_DATE_MODIFIED       = 219;
+    private static final int OBJECT_KEYWORDS            = 220;
+    private static final int OBJECT_THUMB               = 221;
+
+    private static HashMap<String, Integer> sDeviceProjectionMap;
+    private static HashMap<String, Integer> sStorageProjectionMap;
+    private static HashMap<String, Integer> sObjectProjectionMap;
+
+    static {
+        sDeviceProjectionMap = new HashMap<String, Integer>();
+        sDeviceProjectionMap.put(Mtp.Device._ID, new Integer(DEVICE_ROW_ID));
+        sDeviceProjectionMap.put(Mtp.Device.MANUFACTURER, new Integer(DEVICE_MANUFACTURER));
+        sDeviceProjectionMap.put(Mtp.Device.MODEL, new Integer(DEVICE_MODEL));
+
+        sStorageProjectionMap = new HashMap<String, Integer>();
+        sStorageProjectionMap.put(Mtp.Storage._ID, new Integer(STORAGE_ROW_ID));
+        sStorageProjectionMap.put(Mtp.Storage.IDENTIFIER, new Integer(STORAGE_IDENTIFIER));
+        sStorageProjectionMap.put(Mtp.Storage.DESCRIPTION, new Integer(STORAGE_DESCRIPTION));
+
+        sObjectProjectionMap = new HashMap<String, Integer>();
+        sObjectProjectionMap.put(Mtp.Object._ID, new Integer(OBJECT_ROW_ID));
+        sObjectProjectionMap.put(Mtp.Object.STORAGE_ID, new Integer(OBJECT_STORAGE_ID));
+        sObjectProjectionMap.put(Mtp.Object.FORMAT, new Integer(OBJECT_FORMAT));
+        sObjectProjectionMap.put(Mtp.Object.PROTECTION_STATUS, new Integer(OBJECT_PROTECTION_STATUS));
+        sObjectProjectionMap.put(Mtp.Object.SIZE, new Integer(OBJECT_SIZE));
+        sObjectProjectionMap.put(Mtp.Object.THUMB_FORMAT, new Integer(OBJECT_THUMB_FORMAT));
+        sObjectProjectionMap.put(Mtp.Object.THUMB_SIZE, new Integer(OBJECT_THUMB_SIZE));
+        sObjectProjectionMap.put(Mtp.Object.THUMB_WIDTH, new Integer(OBJECT_THUMB_WIDTH));
+        sObjectProjectionMap.put(Mtp.Object.THUMB_HEIGHT, new Integer(OBJECT_THUMB_HEIGHT));
+        sObjectProjectionMap.put(Mtp.Object.IMAGE_WIDTH, new Integer(OBJECT_IMAGE_WIDTH));
+        sObjectProjectionMap.put(Mtp.Object.IMAGE_HEIGHT, new Integer(OBJECT_IMAGE_HEIGHT));
+        sObjectProjectionMap.put(Mtp.Object.IMAGE_DEPTH, new Integer(OBJECT_IMAGE_DEPTH));
+        sObjectProjectionMap.put(Mtp.Object.PARENT, new Integer(OBJECT_PARENT));
+        sObjectProjectionMap.put(Mtp.Object.ASSOCIATION_TYPE, new Integer(OBJECT_ASSOCIATION_TYPE));
+        sObjectProjectionMap.put(Mtp.Object.ASSOCIATION_DESC, new Integer(OBJECT_ASSOCIATION_DESC));
+        sObjectProjectionMap.put(Mtp.Object.SEQUENCE_NUMBER, new Integer(OBJECT_SEQUENCE_NUMBER));
+        sObjectProjectionMap.put(Mtp.Object.NAME, new Integer(OBJECT_NAME));
+        sObjectProjectionMap.put(Mtp.Object.DATE_CREATED, new Integer(OBJECT_DATE_CREATED));
+        sObjectProjectionMap.put(Mtp.Object.DATE_MODIFIED, new Integer(OBJECT_DATE_MODIFIED));
+        sObjectProjectionMap.put(Mtp.Object.KEYWORDS, new Integer(OBJECT_KEYWORDS));
+        sObjectProjectionMap.put(Mtp.Object.THUMB, new Integer(OBJECT_THUMB));
+
+        sObjectProjectionMap.put(Mtp.Object.NAME, new Integer(OBJECT_NAME));
+    }
+
+    // used by the JNI code
+    private int mNativeContext;
+
+    private native final void native_setup(MtpClient client, int queryType,
+            int deviceID, long storageID, long objectID, int[] columns);
+    private native final void native_finalize();
+    private native void native_wait_for_event();
+    private native int native_fill_window(CursorWindow window, int startPos);
+}
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
new file mode 100644
index 0000000..57ab3a1
--- /dev/null
+++ b/media/java/android/media/MtpDatabase.java
@@ -0,0 +1,916 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.content.Context;
+import android.content.ContentValues;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.provider.MediaStore.Audio;
+import android.provider.MediaStore.Files;
+import android.provider.MediaStore.Images;
+import android.provider.MediaStore.MediaColumns;
+import android.provider.Mtp;
+import android.util.Log;
+
+import java.io.File;
+
+/**
+ * {@hide}
+ */
+public class MtpDatabase {
+
+    private static final String TAG = "MtpDatabase";
+
+    private final Context mContext;
+    private final IContentProvider mMediaProvider;
+    private final String mVolumeName;
+    private final Uri mObjectsUri;
+    private final String mMediaStoragePath;
+    private final String mExternalStoragePath;
+
+    // true if the database has been modified in the current MTP session
+    private boolean mDatabaseModified;
+
+    // database for writable MTP device properties
+    private SQLiteDatabase mDevicePropDb;
+    private static final int DEVICE_PROPERTIES_DATABASE_VERSION = 1;
+
+    // FIXME - this should be passed in via the constructor
+    private final int mStorageID = 0x00010001;
+
+    private static final String[] ID_PROJECTION = new String[] {
+            Files.FileColumns._ID, // 0
+    };
+    private static final String[] PATH_PROJECTION = new String[] {
+            Files.FileColumns._ID, // 0
+            Files.FileColumns.DATA, // 1
+    };
+    private static final String[] PATH_SIZE_PROJECTION = new String[] {
+            Files.FileColumns._ID, // 0
+            Files.FileColumns.DATA, // 1
+            Files.FileColumns.SIZE, // 2
+    };
+    private static final String[] OBJECT_INFO_PROJECTION = new String[] {
+            Files.FileColumns._ID, // 0
+            Files.FileColumns.DATA, // 1
+            Files.FileColumns.FORMAT, // 2
+            Files.FileColumns.PARENT, // 3
+            Files.FileColumns.SIZE, // 4
+            Files.FileColumns.DATE_MODIFIED, // 5
+    };
+    private static final String ID_WHERE = Files.FileColumns._ID + "=?";
+    private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
+    private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
+                                            + Files.FileColumns.FORMAT + "=?";
+
+    private static final String[] DEVICE_PROPERTY_PROJECTION = new String[] { "_id", "value" };
+    private  static final String DEVICE_PROPERTY_WHERE = "code=?";
+
+    private final MediaScanner mMediaScanner;
+
+    static {
+        System.loadLibrary("media_jni");
+    }
+
+    public MtpDatabase(Context context, String volumeName, String storagePath) {
+        native_setup();
+
+        mContext = context;
+        mMediaProvider = context.getContentResolver().acquireProvider("media");
+        mVolumeName = volumeName;
+        mMediaStoragePath = storagePath;
+        mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath();
+        mObjectsUri = Files.getMtpObjectsUri(volumeName);
+        mMediaScanner = new MediaScanner(context);
+        openDevicePropertiesDatabase(context);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            native_finalize();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private String externalToMediaPath(String path) {
+        // convert external storage path to media path
+        if (path != null && mMediaStoragePath != null
+                && mExternalStoragePath != null
+                && path.startsWith(mExternalStoragePath)) {
+            path = mMediaStoragePath + path.substring(mExternalStoragePath.length());
+        }
+        return path;
+    }
+
+    private void openDevicePropertiesDatabase(Context context) {
+        mDevicePropDb = context.openOrCreateDatabase("device-properties", Context.MODE_PRIVATE, null);
+        int version = mDevicePropDb.getVersion();
+
+        // initialize if necessary
+        if (version != DEVICE_PROPERTIES_DATABASE_VERSION) {
+            mDevicePropDb.execSQL("CREATE TABLE properties (" +
+                    "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
+                    "code INTEGER UNIQUE ON CONFLICT REPLACE," +
+                    "value TEXT" +
+                    ");");
+            mDevicePropDb.execSQL("CREATE INDEX property_index ON properties (code);");
+            mDevicePropDb.setVersion(DEVICE_PROPERTIES_DATABASE_VERSION);
+        }
+    }
+
+    private int beginSendObject(String path, int format, int parent,
+                         int storage, long size, long modified) {
+        mDatabaseModified = true;
+        ContentValues values = new ContentValues();
+        values.put(Files.FileColumns.DATA, path);
+        values.put(Files.FileColumns.FORMAT, format);
+        values.put(Files.FileColumns.PARENT, parent);
+        // storage is ignored for now
+        values.put(Files.FileColumns.SIZE, size);
+        values.put(Files.FileColumns.DATE_MODIFIED, modified);
+
+        try {
+            Uri uri = mMediaProvider.insert(mObjectsUri, values);
+            if (uri != null) {
+                return Integer.parseInt(uri.getPathSegments().get(2));
+            } else {
+                return -1;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in beginSendObject", e);
+            return -1;
+        }
+    }
+
+    private void endSendObject(String path, int handle, int format, boolean succeeded) {
+        if (succeeded) {
+            // handle abstract playlists separately
+            // they do not exist in the file system so don't use the media scanner here
+            if (format == MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST) {
+                // Strip Windows Media Player file extension
+                if (path.endsWith(".pla")) {
+                    path = path.substring(0, path.length() - 4);
+                }
+
+                // extract name from path
+                String name = path;
+                int lastSlash = name.lastIndexOf('/');
+                if (lastSlash >= 0) {
+                    name = name.substring(lastSlash + 1);
+                }
+
+                ContentValues values = new ContentValues(1);
+                values.put(Audio.Playlists.DATA, path);
+                values.put(Audio.Playlists.NAME, name);
+                values.put(MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, handle);
+                try {
+                    Uri uri = mMediaProvider.insert(Audio.Playlists.EXTERNAL_CONTENT_URI, values);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException in endSendObject", e);
+                }
+            } else {
+                mMediaScanner.scanMtpFile(path, mVolumeName, handle, format);
+            }
+        } else {
+            deleteFile(handle);
+        }
+    }
+
+    private int[] getObjectList(int storageID, int format, int parent) {
+        // we can ignore storageID until we support multiple storages
+        Log.d(TAG, "getObjectList parent: " + parent);
+        Cursor c = null;
+        try {
+            if (format != 0) {
+                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
+                            PARENT_FORMAT_WHERE,
+                            new String[] { Integer.toString(parent), Integer.toString(format) },
+                             null);
+            } else {
+                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
+                            PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
+            }
+            if (c == null) {
+                Log.d(TAG, "null cursor");
+                return null;
+            }
+            int count = c.getCount();
+            if (count > 0) {
+                int[] result = new int[count];
+                for (int i = 0; i < count; i++) {
+                    c.moveToNext();
+                    result[i] = c.getInt(0);
+                }
+                Log.d(TAG, "returning " + result);
+                return result;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getObjectList", e);
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+        return null;
+    }
+
+    private int getNumObjects(int storageID, int format, int parent) {
+        // we can ignore storageID until we support multiple storages
+        Log.d(TAG, "getObjectList parent: " + parent);
+        Cursor c = null;
+        try {
+            if (format != 0) {
+                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
+                            PARENT_FORMAT_WHERE,
+                            new String[] { Integer.toString(parent), Integer.toString(format) },
+                             null);
+            } else {
+                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
+                            PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
+            }
+            if (c != null) {
+                return c.getCount();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getNumObjects", e);
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+        return -1;
+    }
+
+    private int[] getSupportedPlaybackFormats() {
+        return new int[] {
+            // allow transfering arbitrary files
+            MtpConstants.FORMAT_UNDEFINED,
+
+            MtpConstants.FORMAT_ASSOCIATION,
+            MtpConstants.FORMAT_TEXT,
+            MtpConstants.FORMAT_HTML,
+            MtpConstants.FORMAT_WAV,
+            MtpConstants.FORMAT_MP3,
+            MtpConstants.FORMAT_MPEG,
+            MtpConstants.FORMAT_EXIF_JPEG,
+            MtpConstants.FORMAT_TIFF_EP,
+            MtpConstants.FORMAT_GIF,
+            MtpConstants.FORMAT_JFIF,
+            MtpConstants.FORMAT_PNG,
+            MtpConstants.FORMAT_TIFF,
+            MtpConstants.FORMAT_WMA,
+            MtpConstants.FORMAT_OGG,
+            MtpConstants.FORMAT_AAC,
+            MtpConstants.FORMAT_MP4_CONTAINER,
+            MtpConstants.FORMAT_MP2,
+            MtpConstants.FORMAT_3GP_CONTAINER,
+            MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST,
+            MtpConstants.FORMAT_WPL_PLAYLIST,
+            MtpConstants.FORMAT_M3U_PLAYLIST,
+            MtpConstants.FORMAT_PLS_PLAYLIST,
+            MtpConstants.FORMAT_XML_DOCUMENT,
+        };
+    }
+
+    private int[] getSupportedCaptureFormats() {
+        // no capture formats yet
+        return null;
+    }
+
+    static final int[] FILE_PROPERTIES = {
+            // NOTE must match beginning of AUDIO_PROPERTIES, VIDEO_PROPERTIES
+            // and IMAGE_PROPERTIES below
+            MtpConstants.PROPERTY_STORAGE_ID,
+            MtpConstants.PROPERTY_OBJECT_FORMAT,
+            MtpConstants.PROPERTY_PROTECTION_STATUS,
+            MtpConstants.PROPERTY_OBJECT_SIZE,
+            MtpConstants.PROPERTY_OBJECT_FILE_NAME,
+            MtpConstants.PROPERTY_DATE_MODIFIED,
+            MtpConstants.PROPERTY_PARENT_OBJECT,
+            MtpConstants.PROPERTY_PERSISTENT_UID,
+            MtpConstants.PROPERTY_NAME,
+            MtpConstants.PROPERTY_DATE_ADDED,
+    };
+
+    static final int[] AUDIO_PROPERTIES = {
+            // NOTE must match FILE_PROPERTIES above
+            MtpConstants.PROPERTY_STORAGE_ID,
+            MtpConstants.PROPERTY_OBJECT_FORMAT,
+            MtpConstants.PROPERTY_PROTECTION_STATUS,
+            MtpConstants.PROPERTY_OBJECT_SIZE,
+            MtpConstants.PROPERTY_OBJECT_FILE_NAME,
+            MtpConstants.PROPERTY_DATE_MODIFIED,
+            MtpConstants.PROPERTY_PARENT_OBJECT,
+            MtpConstants.PROPERTY_PERSISTENT_UID,
+            MtpConstants.PROPERTY_NAME,
+            MtpConstants.PROPERTY_DISPLAY_NAME,
+            MtpConstants.PROPERTY_DATE_ADDED,
+
+            // audio specific properties
+            MtpConstants.PROPERTY_ARTIST,
+            MtpConstants.PROPERTY_ALBUM_NAME,
+            MtpConstants.PROPERTY_ALBUM_ARTIST,
+            MtpConstants.PROPERTY_TRACK,
+            MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE,
+            MtpConstants.PROPERTY_DURATION,
+            MtpConstants.PROPERTY_GENRE,
+            MtpConstants.PROPERTY_COMPOSER,
+    };
+
+    static final int[] VIDEO_PROPERTIES = {
+            // NOTE must match FILE_PROPERTIES above
+            MtpConstants.PROPERTY_STORAGE_ID,
+            MtpConstants.PROPERTY_OBJECT_FORMAT,
+            MtpConstants.PROPERTY_PROTECTION_STATUS,
+            MtpConstants.PROPERTY_OBJECT_SIZE,
+            MtpConstants.PROPERTY_OBJECT_FILE_NAME,
+            MtpConstants.PROPERTY_DATE_MODIFIED,
+            MtpConstants.PROPERTY_PARENT_OBJECT,
+            MtpConstants.PROPERTY_PERSISTENT_UID,
+            MtpConstants.PROPERTY_NAME,
+            MtpConstants.PROPERTY_DISPLAY_NAME,
+            MtpConstants.PROPERTY_DATE_ADDED,
+
+            // video specific properties
+            MtpConstants.PROPERTY_ARTIST,
+            MtpConstants.PROPERTY_ALBUM_NAME,
+            MtpConstants.PROPERTY_DURATION,
+            MtpConstants.PROPERTY_DESCRIPTION,
+    };
+
+    static final int[] IMAGE_PROPERTIES = {
+            // NOTE must match FILE_PROPERTIES above
+            MtpConstants.PROPERTY_STORAGE_ID,
+            MtpConstants.PROPERTY_OBJECT_FORMAT,
+            MtpConstants.PROPERTY_PROTECTION_STATUS,
+            MtpConstants.PROPERTY_OBJECT_SIZE,
+            MtpConstants.PROPERTY_OBJECT_FILE_NAME,
+            MtpConstants.PROPERTY_DATE_MODIFIED,
+            MtpConstants.PROPERTY_PARENT_OBJECT,
+            MtpConstants.PROPERTY_PERSISTENT_UID,
+            MtpConstants.PROPERTY_NAME,
+            MtpConstants.PROPERTY_DISPLAY_NAME,
+            MtpConstants.PROPERTY_DATE_ADDED,
+
+            // image specific properties
+            MtpConstants.PROPERTY_DESCRIPTION,
+    };
+
+    private int[] getSupportedObjectProperties(int format) {
+        switch (format) {
+            case MtpConstants.FORMAT_MP3:
+            case MtpConstants.FORMAT_WAV:
+            case MtpConstants.FORMAT_WMA:
+            case MtpConstants.FORMAT_OGG:
+            case MtpConstants.FORMAT_AAC:
+                return AUDIO_PROPERTIES;
+            case MtpConstants.FORMAT_MPEG:
+            case MtpConstants.FORMAT_3GP_CONTAINER:
+            case MtpConstants.FORMAT_WMV:
+                return VIDEO_PROPERTIES;
+            case MtpConstants.FORMAT_EXIF_JPEG:
+            case MtpConstants.FORMAT_GIF:
+            case MtpConstants.FORMAT_PNG:
+            case MtpConstants.FORMAT_BMP:
+                return IMAGE_PROPERTIES;
+            default:
+                return FILE_PROPERTIES;
+        }
+    }
+
+    private int[] getSupportedDeviceProperties() {
+        return new int[] {
+            MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
+            MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
+        };
+    }
+
+    private String queryString(int id, String column) {
+        Cursor c = null;
+        try {
+            // for now we are only reading properties from the "objects" table
+            c = mMediaProvider.query(mObjectsUri,
+                            new String [] { Files.FileColumns._ID, column },
+                            ID_WHERE, new String[] { Integer.toString(id) }, null);
+            if (c != null && c.moveToNext()) {
+                return c.getString(1);
+            } else {
+                return "";
+            }
+        } catch (Exception e) {
+            return null;
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+    }
+
+    private String queryGenre(int id) {
+        Cursor c = null;
+        try {
+            Uri uri = Audio.Genres.getContentUriForAudioId(mVolumeName, id);
+            c = mMediaProvider.query(uri,
+                            new String [] { Files.FileColumns._ID, Audio.GenresColumns.NAME },
+                            null, null, null);
+            if (c != null && c.moveToNext()) {
+                return c.getString(1);
+            } else {
+                return "";
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "queryGenre exception", e);
+            return null;
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+    }
+
+    private boolean queryInt(int id, String column, long[] outValue) {
+        Cursor c = null;
+        try {
+            // for now we are only reading properties from the "objects" table
+            c = mMediaProvider.query(mObjectsUri,
+                            new String [] { Files.FileColumns._ID, column },
+                            ID_WHERE, new String[] { Integer.toString(id) }, null);
+            if (c != null && c.moveToNext()) {
+                outValue[0] = c.getLong(1);
+                return true;
+            }
+            return false;
+        } catch (Exception e) {
+            return false;
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+    }
+
+    private String nameFromPath(String path) {
+        // extract name from full path
+        int start = 0;
+        int lastSlash = path.lastIndexOf('/');
+        if (lastSlash >= 0) {
+            start = lastSlash + 1;
+        }
+        int end = path.length();
+        if (end - start > 255) {
+            end = start + 255;
+        }
+        return path.substring(start, end);
+    }
+
+    private int renameFile(int handle, String newName) {
+        Cursor c = null;
+
+        // first compute current path
+        String path = null;
+        String[] whereArgs = new String[] {  Integer.toString(handle) };
+        try {
+            c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE, whereArgs, null);
+            if (c != null && c.moveToNext()) {
+                path = externalToMediaPath(c.getString(1));
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getObjectFilePath", e);
+            return MtpConstants.RESPONSE_GENERAL_ERROR;
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+        if (path == null) {
+            return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+        }
+
+        // now rename the file.  make sure this succeeds before updating database
+        File oldFile = new File(path);
+        int lastSlash = path.lastIndexOf('/');
+        if (lastSlash <= 1) {
+            return MtpConstants.RESPONSE_GENERAL_ERROR;
+        }
+        String newPath = path.substring(0, lastSlash + 1) + newName;
+        File newFile = new File(newPath);
+        boolean success = oldFile.renameTo(newFile);
+        Log.d(TAG, "renaming "+ path + " to " + newPath + (success ? " succeeded" : " failed"));
+        if (!success) {
+            return MtpConstants.RESPONSE_GENERAL_ERROR;
+        }
+
+        // finally update database
+        ContentValues values = new ContentValues();
+        values.put(Files.FileColumns.DATA, newPath);
+        int updated = 0;
+        try {
+            // note - we are relying on a special case in MediaProvider.update() to update
+            // the paths for all children in the case where this is a directory.
+            updated = mMediaProvider.update(mObjectsUri, values, ID_WHERE, whereArgs);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in mMediaProvider.update", e);
+        }
+        if (updated == 0) {
+            Log.e(TAG, "Unable to update path for " + path + " to " + newPath);
+            // this shouldn't happen, but if it does we need to rename the file to its original name
+            newFile.renameTo(oldFile);
+            return MtpConstants.RESPONSE_GENERAL_ERROR;
+        }
+
+        return MtpConstants.RESPONSE_OK;
+    }
+
+    private int getObjectProperty(int handle, int property,
+                            long[] outIntValue, char[] outStringValue) {
+        Log.d(TAG, "getObjectProperty: " + property);
+        String column = null;
+        boolean isString = false;
+
+        switch (property) {
+            case MtpConstants.PROPERTY_STORAGE_ID:
+                outIntValue[0] = mStorageID;
+                return MtpConstants.RESPONSE_OK;
+            case MtpConstants.PROPERTY_OBJECT_FORMAT:
+                column = Files.FileColumns.FORMAT;
+                break;
+            case MtpConstants.PROPERTY_PROTECTION_STATUS:
+                // protection status is always 0
+                outIntValue[0] = 0;
+                return MtpConstants.RESPONSE_OK;
+            case MtpConstants.PROPERTY_OBJECT_SIZE:
+                column = Files.FileColumns.SIZE;
+                break;
+            case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
+                // special case - need to extract file name from full path
+                String value = queryString(handle, Files.FileColumns.DATA);
+                if (value != null) {
+                    value = nameFromPath(value);
+                    value.getChars(0, value.length(), outStringValue, 0);
+                    outStringValue[value.length()] = 0;
+                    return MtpConstants.RESPONSE_OK;
+                } else {
+                    return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+                }
+            case MtpConstants.PROPERTY_NAME:
+                // first try title
+                String name = queryString(handle, MediaColumns.TITLE);
+                // then try name
+                if (name == null) {
+                    name = queryString(handle, Audio.PlaylistsColumns.NAME);
+                }
+                // if title and name fail, extract name from full path
+                if (name == null) {
+                    name = queryString(handle, Files.FileColumns.DATA);
+                    if (name != null) {
+                        name = nameFromPath(name);
+                    }
+                }
+                if (name != null) {
+                    name.getChars(0, name.length(), outStringValue, 0);
+                    outStringValue[name.length()] = 0;
+                    return MtpConstants.RESPONSE_OK;
+                } else {
+                    return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+                }
+            case MtpConstants.PROPERTY_DATE_MODIFIED:
+                column = Files.FileColumns.DATE_MODIFIED;
+                break;
+            case MtpConstants.PROPERTY_DATE_ADDED:
+                column = Files.FileColumns.DATE_ADDED;
+                break;
+            case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE:
+                column = Audio.AudioColumns.YEAR;
+                break;
+            case MtpConstants.PROPERTY_PARENT_OBJECT:
+                column = Files.FileColumns.PARENT;
+                break;
+            case MtpConstants.PROPERTY_PERSISTENT_UID:
+                // PUID is concatenation of storageID and object handle
+                long puid = mStorageID;
+                puid <<= 32;
+                puid += handle;
+                outIntValue[0] = puid;
+                return MtpConstants.RESPONSE_OK;
+            case MtpConstants.PROPERTY_DURATION:
+                column = Audio.AudioColumns.DURATION;
+                break;
+            case MtpConstants.PROPERTY_TRACK:
+                if (queryInt(handle, Audio.AudioColumns.TRACK, outIntValue)) {
+                    // track is stored in lower 3 decimal digits
+                    outIntValue[0] %= 1000;
+                    return MtpConstants.RESPONSE_OK;
+                } else {
+                    return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+                }
+            case MtpConstants.PROPERTY_DISPLAY_NAME:
+                column = MediaColumns.DISPLAY_NAME;
+                isString = true;
+                break;
+            case MtpConstants.PROPERTY_ARTIST:
+                column = Audio.AudioColumns.ARTIST;
+                isString = true;
+                break;
+            case MtpConstants.PROPERTY_ALBUM_NAME:
+                column = Audio.AudioColumns.ALBUM;
+                isString = true;
+                break;
+            case MtpConstants.PROPERTY_ALBUM_ARTIST:
+                column = Audio.AudioColumns.ALBUM_ARTIST;
+                isString = true;
+                break;
+            case MtpConstants.PROPERTY_GENRE:
+                String genre = queryGenre(handle);
+                if (genre != null) {
+                    genre.getChars(0, genre.length(), outStringValue, 0);
+                    outStringValue[genre.length()] = 0;
+                    return MtpConstants.RESPONSE_OK;
+                } else {
+                    return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+                }
+            case MtpConstants.PROPERTY_COMPOSER:
+                column = Audio.AudioColumns.COMPOSER;
+                isString = true;
+                break;
+            case MtpConstants.PROPERTY_DESCRIPTION:
+                column = Images.ImageColumns.DESCRIPTION;
+                isString = true;
+                break;
+            default:
+                return MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+        }
+
+        if (isString) {
+            String value = queryString(handle, column);
+            if (value != null) {
+                value.getChars(0, value.length(), outStringValue, 0);
+                outStringValue[value.length()] = 0;
+                return MtpConstants.RESPONSE_OK;
+            }
+        } else {
+            if (queryInt(handle, column, outIntValue)) {
+                return MtpConstants.RESPONSE_OK;
+            }
+        }
+        // query failed if we get here
+        return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+    }
+
+    private int setObjectProperty(int handle, int property,
+                            long intValue, String stringValue) {
+        Log.d(TAG, "setObjectProperty: " + property);
+
+        switch (property) {
+            case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
+                return renameFile(handle, stringValue);
+
+            default:
+                return MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+        }
+    }
+
+    private int getDeviceProperty(int property, long[] outIntValue, char[] outStringValue) {
+        Log.d(TAG, "getDeviceProperty: " + property);
+
+        switch (property) {
+            case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+            case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+                // writable string properties kept in our device property database
+                Cursor c = null;
+                try {
+                    c = mDevicePropDb.query("properties", DEVICE_PROPERTY_PROJECTION,
+                        DEVICE_PROPERTY_WHERE, new String[] {  Integer.toString(property) },
+                        null, null, null);
+
+                    if (c != null && c.moveToNext()) {
+                        String value = c.getString(1);
+                        int length = value.length();
+                        if (length > 255) {
+                            length = 255;
+                        }
+                        value.getChars(0, length, outStringValue, 0);
+                        outStringValue[length] = 0;
+                    } else {
+                        outStringValue[0] = 0;
+                    }
+                    return MtpConstants.RESPONSE_OK;
+                } finally {
+                    if (c != null) {
+                        c.close();
+                    }
+                }
+        }
+
+        return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+    }
+
+    private int setDeviceProperty(int property, long intValue, String stringValue) {
+        Log.d(TAG, "setDeviceProperty: " + property + " : " + stringValue);
+
+        switch (property) {
+            case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+            case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+                // writable string properties kept in our device property database
+                try {
+                    ContentValues values = new ContentValues();
+                    values.put("code", property);
+                    values.put("value", stringValue);
+                    mDevicePropDb.insert("properties", "code", values);
+                    return MtpConstants.RESPONSE_OK;
+                } catch (Exception e) {
+                    return MtpConstants.RESPONSE_GENERAL_ERROR;
+                }
+        }
+
+        return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+    }
+
+    private boolean getObjectInfo(int handle, int[] outStorageFormatParent,
+                        char[] outName, long[] outSizeModified) {
+        Log.d(TAG, "getObjectInfo: " + handle);
+        Cursor c = null;
+        try {
+            c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
+                            ID_WHERE, new String[] {  Integer.toString(handle) }, null);
+            if (c != null && c.moveToNext()) {
+                outStorageFormatParent[0] = mStorageID;
+                outStorageFormatParent[1] = c.getInt(2);
+                outStorageFormatParent[2] = c.getInt(3);
+
+                // extract name from path
+                String path = c.getString(1);
+                int lastSlash = path.lastIndexOf('/');
+                int start = (lastSlash >= 0 ? lastSlash + 1 : 0);
+                int end = path.length();
+                if (end - start > 255) {
+                    end = start + 255;
+                }
+                path.getChars(start, end, outName, 0);
+                outName[end - start] = 0;
+
+                outSizeModified[0] = c.getLong(4);
+                outSizeModified[1] = c.getLong(5);
+                return true;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getObjectInfo", e);
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+        return false;
+    }
+
+    private int getObjectFilePath(int handle, char[] outFilePath, long[] outFileLength) {
+        Log.d(TAG, "getObjectFilePath: " + handle);
+        if (handle == 0) {
+            // special case root directory
+            mMediaStoragePath.getChars(0, mMediaStoragePath.length(), outFilePath, 0);
+            outFilePath[mMediaStoragePath.length()] = 0;
+            outFileLength[0] = 0;
+            return MtpConstants.RESPONSE_OK;
+        }
+        Cursor c = null;
+        try {
+            c = mMediaProvider.query(mObjectsUri, PATH_SIZE_PROJECTION,
+                            ID_WHERE, new String[] {  Integer.toString(handle) }, null);
+            if (c != null && c.moveToNext()) {
+                String path = externalToMediaPath(c.getString(1));
+                path.getChars(0, path.length(), outFilePath, 0);
+                outFilePath[path.length()] = 0;
+                outFileLength[0] = c.getLong(2);
+                return MtpConstants.RESPONSE_OK;
+            } else {
+                return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getObjectFilePath", e);
+            return MtpConstants.RESPONSE_GENERAL_ERROR;
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+    }
+
+    private int deleteRecursive(int handle) throws RemoteException {
+        int[] children = getObjectList(0 /* storageID */, 0 /* format */, handle);
+        Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
+        // delete parent first, to avoid potential infinite recursion
+        int count = mMediaProvider.delete(uri, null, null);
+        if (count == 1) {
+            if (children != null) {
+                for (int i = 0; i < children.length; i++) {
+                    count += deleteRecursive(children[i]);
+                }
+            }
+        }
+        return count;
+    }
+
+    private int deleteFile(int handle) {
+        Log.d(TAG, "deleteFile: " + handle);
+        mDatabaseModified = true;
+        try {
+            if (deleteRecursive(handle) > 0) {
+                return MtpConstants.RESPONSE_OK;
+            } else {
+                return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in deleteFile", e);
+            return MtpConstants.RESPONSE_GENERAL_ERROR;
+        }
+    }
+
+    private int[] getObjectReferences(int handle) {
+        Log.d(TAG, "getObjectReferences for: " + handle);
+        Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
+        Cursor c = null;
+        try {
+            c = mMediaProvider.query(uri, ID_PROJECTION, null, null, null);
+            if (c == null) {
+                return null;
+            }
+            int count = c.getCount();
+            if (count > 0) {
+                int[] result = new int[count];
+                for (int i = 0; i < count; i++) {
+                    c.moveToNext();
+                    result[i] = c.getInt(0);
+                }
+                return result;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in getObjectList", e);
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+        return null;
+    }
+
+    private int setObjectReferences(int handle, int[] references) {
+        mDatabaseModified = true;
+        Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
+        int count = references.length;
+        ContentValues[] valuesList = new ContentValues[count];
+        for (int i = 0; i < count; i++) {
+            ContentValues values = new ContentValues();
+            values.put(Files.FileColumns._ID, references[i]);
+            valuesList[i] = values;
+        }
+        try {
+            if (count == mMediaProvider.bulkInsert(uri, valuesList)) {
+                return MtpConstants.RESPONSE_OK;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException in setObjectReferences", e);
+        }
+        return MtpConstants.RESPONSE_GENERAL_ERROR;
+    }
+
+    private void sessionStarted() {
+        Log.d(TAG, "sessionStarted");
+        mDatabaseModified = false;
+    }
+
+    private void sessionEnded() {
+        Log.d(TAG, "sessionEnded");
+        if (mDatabaseModified) {
+            Log.d(TAG, "sending ACTION_MTP_SESSION_END");
+            mContext.sendBroadcast(new Intent(Mtp.ACTION_MTP_SESSION_END));
+            mDatabaseModified = false;
+        }
+    }
+
+    // used by the JNI code
+    private int mNativeContext;
+
+    private native final void native_setup();
+    private native final void native_finalize();
+}
diff --git a/media/java/android/media/MtpServer.java b/media/java/android/media/MtpServer.java
new file mode 100644
index 0000000..7f15276
--- /dev/null
+++ b/media/java/android/media/MtpServer.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.util.Log;
+
+/**
+ * Java wrapper for MTP/PTP support as USB responder.
+ * {@hide}
+ */
+public class MtpServer {
+
+    private static final String TAG = "MtpServer";
+
+    static {
+        System.loadLibrary("media_jni");
+    }
+
+    public MtpServer(MtpDatabase database, String storagePath) {
+        native_setup(database, storagePath);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            native_finalize();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    public void start() {
+        native_start();
+    }
+
+    public void stop() {
+        native_stop();
+    }
+
+    public void sendObjectAdded(int handle) {
+        native_send_object_added(handle);
+    }
+
+    public void sendObjectRemoved(int handle) {
+        native_send_object_removed(handle);
+    }
+
+    public void setPtpMode(boolean usePtp) {
+        native_set_ptp_mode(usePtp);
+    }
+
+    // used by the JNI code
+    private int mNativeContext;
+
+    private native final void native_setup(MtpDatabase database, String storagePath);
+    private native final void native_finalize();
+    private native final void native_start();
+    private native final void native_stop();
+    private native final void native_send_object_added(int handle);
+    private native final void native_send_object_removed(int handle);
+    private native final void native_set_ptp_mode(boolean usePtp);
+}
diff --git a/media/java/android/media/videoeditor/AudioTrack.java b/media/java/android/media/videoeditor/AudioTrack.java
new file mode 100755
index 0000000..d02709e
--- /dev/null
+++ b/media/java/android/media/videoeditor/AudioTrack.java
@@ -0,0 +1,487 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+import java.io.IOException;

+import java.lang.ref.SoftReference;

+

+/**

+ * This class allows to handle an audio track. This audio file is mixed with the

+ * audio samples of the media items.

+ * {@hide}

+ */

+public class AudioTrack {

+    // Instance variables

+    private final String mUniqueId;

+    private final String mFilename;

+    private long mStartTimeMs;

+    private long mTimelineDurationMs;

+    private int mVolumePercent;

+    private long mBeginBoundaryTimeMs;

+    private long mEndBoundaryTimeMs;

+    private boolean mLoop;

+    private boolean mMuted;

+

+    private final long mDurationMs;

+    private final int mAudioChannels;

+    private final int mAudioType;

+    private final int mAudioBitrate;

+    private final int mAudioSamplingFrequency;

+

+    // Ducking variables

+    private int mDuckingThreshold;

+    private int mDuckedTrackVolume;

+    private boolean mIsDuckingEnabled;

+

+    // The audio waveform filename

+    private String mAudioWaveformFilename;

+    // The audio waveform data

+    private SoftReference<WaveformData> mWaveformData;

+

+    /**

+     * An object of this type cannot be instantiated by using the default

+     * constructor

+     */

+    @SuppressWarnings("unused")

+    private AudioTrack() throws IOException {

+        this(null, null, null);

+    }

+

+    /**

+     * Constructor

+     *

+     * @param editor The video editor reference

+     * @param audioTrackId The audio track id

+     * @param filename The absolute file name

+     *

+     * @throws IOException if file is not found

+     * @throws IllegalArgumentException if file format is not supported or if

+     *             the codec is not supported

+     */

+    public AudioTrack(VideoEditor editor, String audioTrackId, String filename)

+            throws IOException {

+        mUniqueId = audioTrackId;

+        mFilename = filename;

+        mStartTimeMs = 0;

+        // TODO: This value represents to the duration of the audio file

+        mDurationMs = 300000;

+        // TODO: This value needs to be read from the audio track of the source

+        // file

+        mAudioChannels = 2;

+        mAudioType = MediaProperties.ACODEC_AAC_LC;

+        mAudioBitrate = 128000;

+        mAudioSamplingFrequency = 44100;

+

+        mTimelineDurationMs = mDurationMs;

+        mVolumePercent = 100;

+

+        // Play the entire audio track

+        mBeginBoundaryTimeMs = 0;

+        mEndBoundaryTimeMs = mDurationMs;

+

+        // By default loop is disabled

+        mLoop = false;

+

+        // By default the audio track is not muted

+        mMuted = false;

+

+        // Ducking is enabled by default

+        mDuckingThreshold = 0;

+        mDuckedTrackVolume = 0;

+        mIsDuckingEnabled = true;

+

+        // The audio waveform file is generated later

+        mAudioWaveformFilename = null;

+        mWaveformData = null;

+    }

+

+    /**

+     * Constructor

+     *

+     * @param editor The video editor reference

+     * @param audioTrackId The audio track id

+     * @param filename The audio filename

+     * @param startTimeMs the start time in milliseconds (relative to the

+     *              timeline)

+     * @param beginMs start time in the audio track in milliseconds (relative to

+     *            the beginning of the audio track)

+     * @param endMs end time in the audio track in milliseconds (relative to the

+     *            beginning of the audio track)

+     * @param loop true to loop the audio track

+     * @param volume The volume in percentage

+     * @param muted true if the audio track is muted

+     * @param threshold Ducking will be activated when the relative energy in

+     *      the media items audio signal goes above this value. The valid

+     *      range of values is 0 to 100.

+     * @param duckedTrackVolume The relative volume of the audio track when ducking

+     *      is active. The valid range of values is 0 to 100.

+     * @param audioWaveformFilename The name of the waveform file

+     *

+     * @throws IOException if file is not found

+     */

+    AudioTrack(VideoEditor editor, String audioTrackId, String filename, long startTimeMs,

+            long beginMs, long endMs, boolean loop, int volume, boolean muted,

+            boolean duckingEnabled, int duckThreshold, int duckedTrackVolume,

+            String audioWaveformFilename) throws IOException {

+        mUniqueId = audioTrackId;

+        mFilename = filename;

+        mStartTimeMs = startTimeMs;

+

+        // TODO: This value represents to the duration of the audio file

+        mDurationMs = 300000;

+

+        // TODO: This value needs to be read from the audio track of the source

+        // file

+        mAudioChannels = 2;

+        mAudioType = MediaProperties.ACODEC_AAC_LC;

+        mAudioBitrate = 128000;

+        mAudioSamplingFrequency = 44100;

+

+        mTimelineDurationMs = endMs - beginMs;

+        mVolumePercent = volume;

+

+        mBeginBoundaryTimeMs = beginMs;

+        mEndBoundaryTimeMs = endMs;

+

+        mLoop = loop;

+        mMuted = muted;

+

+        mIsDuckingEnabled = duckingEnabled;

+        mDuckingThreshold = duckThreshold;

+        mDuckedTrackVolume = duckedTrackVolume;

+

+        mAudioWaveformFilename = audioWaveformFilename;

+        if (audioWaveformFilename != null) {

+            mWaveformData =

+                new SoftReference<WaveformData>(new WaveformData(audioWaveformFilename));

+        } else {

+            mWaveformData = null;

+        }

+    }

+

+    /**

+     * @return The id of the audio track

+     */

+    public String getId() {

+        return mUniqueId;

+    }

+

+    /**

+     * Get the filename source for this audio track.

+     *

+     * @return The filename as an absolute file name

+     */

+    public String getFilename() {

+        return mFilename;

+    }

+

+    /**

+     * @return The number of audio channels in the source of this audio track

+     */

+    public int getAudioChannels() {

+        return mAudioChannels;

+    }

+

+    /**

+     * @return The audio codec of the source of this audio track

+     */

+    public int getAudioType() {

+        return mAudioType;

+    }

+

+    /**

+     * @return The audio sample frequency of the audio track

+     */

+    public int getAudioSamplingFrequency() {

+        return mAudioSamplingFrequency;

+    }

+

+    /**

+     * @return The audio bitrate of the audio track

+     */

+    public int getAudioBitrate() {

+        return mAudioBitrate;

+    }

+

+    /**

+     * Set the volume of this audio track as percentage of the volume in the

+     * original audio source file.

+     *

+     * @param volumePercent Percentage of the volume to apply. If it is set to

+     *            0, then volume becomes mute. It it is set to 100, then volume

+     *            is same as original volume. It it is set to 200, then volume

+     *            is doubled (provided that volume amplification is supported)

+     *

+     * @throws UnsupportedOperationException if volume amplification is

+     *             requested and is not supported.

+     */

+    public void setVolume(int volumePercent) {

+        mVolumePercent = volumePercent;

+    }

+

+    /**

+     * Get the volume of the audio track as percentage of the volume in the

+     * original audio source file.

+     *

+     * @return The volume in percentage

+     */

+    public int getVolume() {

+        return mVolumePercent;

+    }

+

+    /**

+     * @param muted true to mute the audio track

+     */

+    public void setMute(boolean muted) {

+        mMuted = muted;

+    }

+

+    /**

+     * @return true if the audio track is muted

+     */

+    public boolean isMuted() {

+        return mMuted;

+    }

+

+    /**

+     * Set the start time of this audio track relative to the storyboard

+     * timeline. Default value is 0.

+     *

+     * @param startTimeMs the start time in milliseconds

+     */

+    public void setStartTime(long startTimeMs) {

+        mStartTimeMs = startTimeMs;

+    }

+

+    /**

+     * Get the start time of this audio track relative to the storyboard

+     * timeline.

+     *

+     * @return The start time in milliseconds

+     */

+    public long getStartTime() {

+        return mStartTimeMs;

+    }

+

+    /**

+     * @return The duration in milliseconds. This value represents the audio

+     *         track duration (not looped)

+     */

+    public long getDuration() {

+        return mDurationMs;

+    }

+

+    /**

+     * @return The timeline duration. If looping is enabled this value

+     *         represents the duration of the looped audio track, otherwise it

+     *         is the duration of the audio track (mDurationMs).

+     */

+    public long getTimelineDuration() {

+        return mTimelineDurationMs;

+    }

+

+    /**

+     * Sets the start and end marks for trimming an audio track

+     *

+     * @param beginMs start time in the audio track in milliseconds (relative to

+     *            the beginning of the audio track)

+     * @param endMs end time in the audio track in milliseconds (relative to the

+     *            beginning of the audio track)

+     */

+    public void setExtractBoundaries(long beginMs, long endMs) {

+        if (beginMs > mDurationMs) {

+            throw new IllegalArgumentException("Invalid start time");

+        }

+        if (endMs > mDurationMs) {

+            throw new IllegalArgumentException("Invalid end time");

+        }

+

+        mBeginBoundaryTimeMs = beginMs;

+        mEndBoundaryTimeMs = endMs;

+        if (mLoop) {

+            // TODO: Compute mDurationMs (from the beginning of the loop until

+            // the end of all the loops.

+            mTimelineDurationMs = mEndBoundaryTimeMs - mBeginBoundaryTimeMs;

+        } else {

+            mTimelineDurationMs = mEndBoundaryTimeMs - mBeginBoundaryTimeMs;

+        }

+    }

+

+    /**

+     * @return The boundary begin time

+     */

+    public long getBoundaryBeginTime() {

+        return mBeginBoundaryTimeMs;

+    }

+

+    /**

+     * @return The boundary end time

+     */

+    public long getBoundaryEndTime() {

+        return mEndBoundaryTimeMs;

+    }

+

+    /**

+     * Enable the loop mode for this audio track. Note that only one of the

+     * audio tracks in the timeline can have the loop mode enabled. When looping

+     * is enabled the samples between mBeginBoundaryTimeMs and

+     * mEndBoundaryTimeMs are looped.

+     */

+    public void enableLoop() {

+        mLoop = true;

+    }

+

+    /**

+     * Disable the loop mode

+     */

+    public void disableLoop() {

+        mLoop = false;

+    }

+

+    /**

+     * @return true if looping is enabled

+     */

+    public boolean isLooping() {

+        return mLoop;

+    }

+

+    /**

+     * Disable the audio duck effect

+     */

+    public void disableDucking() {

+        mIsDuckingEnabled = false;

+    }

+

+    /**

+     * Enable ducking by specifying the required parameters

+     *

+     * @param threshold Ducking will be activated when the relative energy in

+     *      the media items audio signal goes above this value. The valid

+     *      range of values is 0 to 100.

+     * @param duckedTrackVolume The relative volume of the audio track when ducking

+     *      is active. The valid range of values is 0 to 100.

+     */

+    public void enableDucking(int threshold, int duckedTrackVolume) {

+        if (threshold < 0 || threshold > 100) {

+            throw new IllegalArgumentException("Invalid threshold value: " + threshold);

+        }

+

+        if (duckedTrackVolume < 0 || duckedTrackVolume > 100) {

+            throw new IllegalArgumentException("Invalid duckedTrackVolume value: "

+                    + duckedTrackVolume);

+        }

+

+        mDuckingThreshold = threshold;

+        mDuckedTrackVolume = duckedTrackVolume;

+        mIsDuckingEnabled = true;

+    }

+

+    /**

+     * @return true if ducking is enabled

+     */

+    public boolean isDuckingEnabled() {

+        return mIsDuckingEnabled;

+    }

+

+    /**

+     * @return The ducking threshold

+     */

+    public int getDuckingThreshhold() {

+        return mDuckingThreshold;

+    }

+

+    /**

+     * @return The ducked track volume

+     */

+    public int getDuckedTrackVolume() {

+        return mDuckedTrackVolume;

+    }

+

+    /**

+     * This API allows to generate a file containing the sample volume levels of

+     * this audio track object. This function may take significant time and is

+     * blocking. The filename can be retrieved using getAudioWaveformFilename().

+     *

+     * @param listener The progress listener

+     *

+     * @throws IOException if the output file cannot be created

+     * @throws IllegalArgumentException if the audio file does not have a valid

+     *             audio track

+     */

+    public void extractAudioWaveform(ExtractAudioWaveformProgressListener listener)

+            throws IOException {

+        // TODO: Set mAudioWaveformFilename at the end once the extract is

+        // complete

+        mWaveformData = new SoftReference<WaveformData>(new WaveformData(mAudioWaveformFilename));

+    }

+

+    /**

+     * Get the audio waveform file name if extractAudioWaveform was successful.

+     * The file format is as following:

+     * <ul>

+     * <li>first 4 bytes provide the number of samples for each value, as

+     * big-endian signed</li>

+     * <li>4 following bytes is the total number of values in the file, as

+     * big-endian signed</li>

+     * <li>then, all values follow as bytes</li>

+     * </ul>

+     *

+     * @return the name of the file, null if the file does not exist

+     */

+    String getAudioWaveformFilename() {

+        return mAudioWaveformFilename;

+    }

+

+    /**

+     * @return The waveform data

+     */

+    public WaveformData getWaveformData() {

+        if (mWaveformData == null) {

+            return null;

+        }

+

+        WaveformData waveformData = mWaveformData.get();

+        if (waveformData != null) {

+            return waveformData;

+        } else if (mAudioWaveformFilename != null) {

+            waveformData = new WaveformData(mAudioWaveformFilename);

+            mWaveformData = new SoftReference<WaveformData>(waveformData);

+            return waveformData;

+        } else {

+            return null;

+        }

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public boolean equals(Object object) {

+        if (!(object instanceof AudioTrack)) {

+            return false;

+        }

+        return mUniqueId.equals(((AudioTrack)object).mUniqueId);

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int hashCode() {

+        return mUniqueId.hashCode();

+    }

+}

diff --git a/media/java/android/media/videoeditor/Effect.java b/media/java/android/media/videoeditor/Effect.java
new file mode 100755
index 0000000..8547e13
--- /dev/null
+++ b/media/java/android/media/videoeditor/Effect.java
@@ -0,0 +1,168 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+/**

+ * This is the super class for all effects. An effect can only be applied to a

+ * single media item. If one wants to apply the same effect to multiple media

+ * items, multiple @{MediaItem.addEffect(Effect)} call must be invoked on each

+ * of the MediaItem objects.

+ * {@hide}

+ */

+public abstract class Effect {

+    // Instance variables

+    private final String mUniqueId;

+    // The effect owner

+    private final MediaItem mMediaItem;

+    protected long mDurationMs;

+    // The start time of the effect relative to the media item timeline

+    protected long mStartTimeMs;

+

+    /**

+     * Default constructor

+     */

+    @SuppressWarnings("unused")

+    private Effect() {

+        mMediaItem = null;

+        mUniqueId = null;

+        mStartTimeMs = 0;

+        mDurationMs = 0;

+    }

+

+    /**

+     * Constructor

+     *

+     * @param mediaItem The media item owner

+     * @param effectId The effect id

+     * @param startTimeMs The start time relative to the media item to which it

+     *            is applied

+     * @param durationMs The effect duration in milliseconds

+     */

+    public Effect(MediaItem mediaItem, String effectId, long startTimeMs, long durationMs) {

+        if (mediaItem == null) {

+            throw new IllegalArgumentException("Media item cannot be null");

+        }

+

+        if (startTimeMs + durationMs > mediaItem.getDuration()) {

+            throw new IllegalArgumentException("Invalid start time and duration");

+        }

+

+        mMediaItem = mediaItem;

+        mUniqueId = effectId;

+        mStartTimeMs = startTimeMs;

+        mDurationMs = durationMs;

+    }

+

+    /**

+     * @return The id of the effect

+     */

+    public String getId() {

+        return mUniqueId;

+    }

+

+    /**

+     * Set the duration of the effect. If a preview or export is in progress,

+     * then this change is effective for next preview or export session. s

+     *

+     * @param durationMs of the effect in milliseconds

+     */

+    public void setDuration(long durationMs) {

+        if (mStartTimeMs + durationMs > mMediaItem.getDuration()) {

+            throw new IllegalArgumentException("Duration is too large");

+        }

+

+        mDurationMs = durationMs;

+

+        mMediaItem.invalidateTransitions(this);

+    }

+

+    /**

+     * Get the duration of the effect

+     *

+     * @return The duration of the effect in milliseconds

+     */

+    public long getDuration() {

+        return mDurationMs;

+    }

+

+    /**

+     * Set start time of the effect. If a preview or export is in progress, then

+     * this change is effective for next preview or export session.

+     *

+     * @param startTimeMs The start time of the effect relative to the beginning

+     *            of the media item in milliseconds

+     */

+    public void setStartTime(long startTimeMs) {

+        if (startTimeMs + mDurationMs > mMediaItem.getDuration()) {

+            throw new IllegalArgumentException("Start time is too large");

+        }

+

+        mStartTimeMs = startTimeMs;

+

+        mMediaItem.invalidateTransitions(this);

+    }

+

+    /**

+     * @return The start time in milliseconds

+     */

+    public long getStartTime() {

+        return mStartTimeMs;

+    }

+

+    /**

+     * Set the start time and duration

+     *

+     * @param startTimeMs start time in milliseconds

+     * @param durationMs The duration in milliseconds

+     */

+    public void setStartTimeAndDuration(long startTimeMs, long durationMs) {

+        if (startTimeMs + durationMs > mMediaItem.getDuration()) {

+            throw new IllegalArgumentException("Invalid start time or duration");

+        }

+

+        mStartTimeMs = startTimeMs;

+        mDurationMs = durationMs;

+

+        mMediaItem.invalidateTransitions(this);

+    }

+

+    /**

+     * @return The media item owner

+     */

+    public MediaItem getMediaItem() {

+        return mMediaItem;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public boolean equals(Object object) {

+        if (!(object instanceof Effect)) {

+            return false;

+        }

+        return mUniqueId.equals(((Effect)object).mUniqueId);

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int hashCode() {

+        return mUniqueId.hashCode();

+    }

+}

diff --git a/media/java/android/media/videoeditor/EffectColor.java b/media/java/android/media/videoeditor/EffectColor.java
new file mode 100755
index 0000000..ac48e37
--- /dev/null
+++ b/media/java/android/media/videoeditor/EffectColor.java
@@ -0,0 +1,119 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+/**

+ * This class allows to apply color on a media item.

+ * {@hide}

+ */

+public class EffectColor extends Effect {

+

+    /**

+     * Change the video frame color to the RGB color value provided

+     */

+    public static final int TYPE_COLOR = 1;

+    /**

+     * Change the video frame color to a gradation from RGB color (at the top of

+     * the frame) to black (at the bottom of the frame).

+     */

+    public static final int TYPE_GRADIENT = 2;

+    /**

+     * Change the video frame color to sepia

+     */

+    public static final int TYPE_SEPIA = 3;

+    /**

+     * Invert the video frame color

+     */

+    public static final int TYPE_NEGATIVE = 4;

+    /**

+     * Make the video look like as if it was recorded in 50's

+     */

+    public static final int TYPE_FIFTIES = 5;

+

+    // Predefined colors

+    public static final int GREEN = 0x0000ff00;

+    public static final int PINK = 0x00ff66cc;

+    public static final int GRAY = 0x007f7f7f;

+

+    // The effect type

+    private final int mType;

+

+    // The effect color

+    private final int mColor;

+

+    /**

+     * An object of this type cannot be instantiated by using the default

+     * constructor

+     */

+    @SuppressWarnings("unused")

+    private EffectColor() {

+        this(null, null, 0, 0, 0, 0);

+    }

+

+    /**

+     * Constructor

+     *

+     * @param mediaItem The media item owner

+     * @param effectId The effect id

+     * @param startTimeMs The start time relative to the media item to which it

+     *            is applied

+     * @param durationMs The duration of this effect in milliseconds

+     * @param type type of the effect. type is one of: TYPE_COLOR,

+     *            TYPE_GRADIENT, TYPE_SEPIA, TYPE_NEGATIVE, TYPE_FIFTIES.

+     * @param color If type is TYPE_COLOR, color is the RGB color as 888.

+     *              If type is TYPE_GRADIENT, color is the RGB color at the

+     *              top of the frame. Otherwise, color is ignored

+     */

+    public EffectColor(MediaItem mediaItem, String effectId, long startTimeMs, long durationMs,

+            int type, int color) {

+        super(mediaItem, effectId, startTimeMs, durationMs);

+        switch (type) {

+            case TYPE_COLOR:

+            case TYPE_GRADIENT: {

+                mColor = color;

+                break;

+            }

+

+            case TYPE_SEPIA:

+            case TYPE_NEGATIVE:

+            case TYPE_FIFTIES: {

+                mColor = -1;

+                break;

+            }

+

+            default: {

+                throw new IllegalArgumentException("Invalid type: " + type);

+            }

+        }

+

+        mType = type;

+    }

+

+    /**

+     * @return The effect type

+     */

+    public int getType() {

+        return mType;

+    }

+

+    /**

+     * @return the color as RGB 888 if type is TYPE_COLOR or TYPE_GRADIENT.

+     */

+    public int getColor() {

+        return mColor;

+    }

+}

diff --git a/media/java/android/media/videoeditor/EffectKenBurns.java b/media/java/android/media/videoeditor/EffectKenBurns.java
new file mode 100755
index 0000000..ae2e70d
--- /dev/null
+++ b/media/java/android/media/videoeditor/EffectKenBurns.java
@@ -0,0 +1,88 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+import android.graphics.Rect;

+

+/**

+ * This class represents a Ken Burns effect.

+ * {@hide}

+ */

+public class EffectKenBurns extends Effect {

+    // Instance variables

+    private Rect mStartRect;

+    private Rect mEndRect;

+

+    /**

+     * Objects of this type cannot be instantiated by using the default

+     * constructor

+     */

+    @SuppressWarnings("unused")

+    private EffectKenBurns() {

+        this(null, null, null, null, 0, 0);

+    }

+

+    /**

+     * Constructor

+     *

+     * @param mediaItem The media item owner

+     * @param effectId The effect id

+     * @param startRect The start rectangle

+     * @param endRect The end rectangle

+     * @param startTimeMs The start time

+     * @param durationMs The duration of the Ken Burns effect in milliseconds

+     */

+    public EffectKenBurns(MediaItem mediaItem, String effectId, Rect startRect, Rect endRect,

+            long startTimeMs, long durationMs) {

+        super(mediaItem, effectId, startTimeMs, durationMs);

+

+        mStartRect = startRect;

+        mEndRect = endRect;

+    }

+

+    /**

+     * @param startRect The start rectangle

+     *

+     * @throws IllegalArgumentException if start rectangle is incorrectly set.

+     */

+    public void setStartRect(Rect startRect) {

+        mStartRect = startRect;

+    }

+

+    /**

+     * @return The start rectangle

+     */

+    public Rect getStartRect() {

+        return mStartRect;

+    }

+

+    /**

+     * @param endRect The end rectangle

+     *

+     * @throws IllegalArgumentException if end rectangle is incorrectly set.

+     */

+    public void setEndRect(Rect endRect) {

+        mEndRect = endRect;

+    }

+

+    /**

+     * @return The end rectangle

+     */

+    public Rect getEndRect() {

+        return mEndRect;

+    }

+}

diff --git a/media/java/android/media/videoeditor/ExtractAudioWaveformProgressListener.java b/media/java/android/media/videoeditor/ExtractAudioWaveformProgressListener.java
new file mode 100644
index 0000000..1cce148
--- /dev/null
+++ b/media/java/android/media/videoeditor/ExtractAudioWaveformProgressListener.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.videoeditor;
+
+/**
+ * This listener interface is used by
+ * {@link MediaVideoItem#extractAudioWaveform(ExtractAudioWaveformProgressListener listener)}
+ * or
+ * {@link AudioTrack#extractAudioWaveform(ExtractAudioWaveformProgressListener listener)}
+ * {@hide}
+ */
+public interface ExtractAudioWaveformProgressListener {
+    /**
+     * This method notifies the listener of the progress status of
+     * an extractAudioWaveform operation.
+     * This method may be called maximum 100 times for one operation.
+     *
+     * @param progress The progress in %. At the beginning of the operation,
+     *          this value is set to 0; at the end, the value is set to 100.
+     */
+    public void onProgress(int progress);
+}
+
diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
new file mode 100755
index 0000000..6dc36c2
--- /dev/null
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -0,0 +1,272 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+import java.io.IOException;

+

+import android.graphics.Bitmap;

+import android.graphics.BitmapFactory;

+import android.graphics.Canvas;

+import android.graphics.Paint;

+import android.graphics.Rect;

+import android.util.Log;

+import android.util.Pair;

+

+/**

+ * This class represents an image item on the storyboard. Note that images are

+ * scaled down to the maximum supported resolution by preserving the native

+ * aspect ratio. To learn the scaled image dimensions use

+ * {@link #getScaledWidth()} and {@link #getScaledHeight()} respectively.

+ *

+ * {@hide}

+ */

+public class MediaImageItem extends MediaItem {

+    // Logging

+    private static final String TAG = "MediaImageItem";

+

+    // The resize paint

+    private static final Paint sResizePaint = new Paint(Paint.FILTER_BITMAP_FLAG);

+

+    // Instance variables

+    private final int mWidth;

+    private final int mHeight;

+    private final int mAspectRatio;

+    private long mDurationMs;

+    private int mScaledWidth, mScaledHeight;

+

+    /**

+     * This class cannot be instantiated by using the default constructor

+     */

+    @SuppressWarnings("unused")

+    private MediaImageItem() throws IOException {

+        this(null, null, null, 0, RENDERING_MODE_BLACK_BORDER);

+    }

+

+    /**

+     * Constructor

+     *

+     * @param editor The video editor reference

+     * @param mediaItemId The media item id

+     * @param filename The image file name

+     * @param durationMs The duration of the image on the storyboard

+     * @param renderingMode The rendering mode

+     *

+     * @throws IOException

+     */

+    public MediaImageItem(VideoEditor editor, String mediaItemId, String filename, long durationMs,

+            int renderingMode)

+            throws IOException {

+        super(editor, mediaItemId, filename, renderingMode);

+

+        // Determine the dimensions of the image

+        final BitmapFactory.Options dbo = new BitmapFactory.Options();

+        dbo.inJustDecodeBounds = true;

+        BitmapFactory.decodeFile(filename, dbo);

+

+        mWidth = dbo.outWidth;

+        mHeight = dbo.outHeight;

+        mDurationMs = durationMs;

+

+        // TODO: Determine the aspect ratio from the width and height

+        mAspectRatio = MediaProperties.ASPECT_RATIO_4_3;

+

+        // Images are stored in memory scaled to the maximum resolution to

+        // save memory.

+        final Pair<Integer, Integer>[] resolutions =

+            MediaProperties.getSupportedResolutions(mAspectRatio);

+        // Get the highest resolution

+        final Pair<Integer, Integer> maxResolution = resolutions[resolutions.length - 1];

+        if (mHeight > maxResolution.second) {

+            // We need to scale the image

+            scaleImage(filename, maxResolution.first, maxResolution.second);

+            mScaledWidth = maxResolution.first;

+            mScaledHeight = maxResolution.second;

+        } else {

+            mScaledWidth = mWidth;

+            mScaledHeight = mHeight;

+        }

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int getFileType() {

+        if (mFilename.endsWith(".jpg") || mFilename.endsWith(".jpeg")) {

+            return MediaProperties.FILE_JPEG;

+        } else if (mFilename.endsWith(".png")) {

+            return MediaProperties.FILE_PNG;

+        } else {

+            return MediaProperties.FILE_UNSUPPORTED;

+        }

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int getWidth() {

+        return mWidth;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int getHeight() {

+        return mHeight;

+    }

+

+    /**

+     * @return The scaled width of the image.

+     */

+    public int getScaledWidth() {

+        return mScaledWidth;

+    }

+

+    /**

+     * @return The scaled height of the image.

+     */

+    public int getScaledHeight() {

+        return mScaledHeight;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int getAspectRatio() {

+        return mAspectRatio;

+    }

+

+    /**

+     * This method will adjust the duration of bounding transitions, effects

+     * and overlays if the current duration of the transactions become greater

+     * than the maximum allowable duration.

+     *

+     * @param durationMs The duration of the image in the storyboard timeline

+     */

+    public void setDuration(long durationMs) {

+        mDurationMs = durationMs;

+

+        adjustTransitions();

+        adjustOverlays();

+        adjustEffects();

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public long getDuration() {

+        return mDurationMs;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public long getTimelineDuration() {

+        return mDurationMs;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public Bitmap getThumbnail(int width, int height, long timeMs) throws IOException {

+        return scaleImage(mFilename, width, height);

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public Bitmap[] getThumbnailList(int width, int height, long startMs, long endMs,

+            int thumbnailCount) throws IOException {

+        final Bitmap thumbnail = scaleImage(mFilename, width, height);

+        final Bitmap[] thumbnailArray = new Bitmap[thumbnailCount];

+        for (int i = 0; i < thumbnailCount; i++) {

+            thumbnailArray[i] = thumbnail;

+        }

+        return thumbnailArray;

+    }

+

+    /**

+     * Resize a bitmap to the specified width and height

+     *

+     * @param filename The filename

+     * @param width The thumbnail width

+     * @param height The thumbnail height

+     *

+     * @return The resized bitmap

+     */

+    private Bitmap scaleImage(String filename, int width, int height)

+            throws IOException {

+        final BitmapFactory.Options dbo = new BitmapFactory.Options();

+        dbo.inJustDecodeBounds = true;

+        BitmapFactory.decodeFile(filename, dbo);

+

+        final int nativeWidth = dbo.outWidth;

+        final int nativeHeight = dbo.outHeight;

+        if (Log.isLoggable(TAG, Log.DEBUG)) {

+            Log.d(TAG, "generateThumbnail: Input: " + nativeWidth + "x" + nativeHeight

+                    + ", resize to: " + width + "x" + height);

+        }

+

+        final Bitmap srcBitmap;

+        float bitmapWidth, bitmapHeight;

+        if (nativeWidth > width || nativeHeight > height) {

+            float dx = ((float)nativeWidth) / ((float)width);

+            float dy = ((float)nativeHeight) / ((float)height);

+            if (dx > dy) {

+                bitmapWidth = width;

+                bitmapHeight = nativeHeight / dx;

+            } else {

+                bitmapWidth = nativeWidth / dy;

+                bitmapHeight = height;

+            }

+            // Create the bitmap from file

+            if (nativeWidth / bitmapWidth > 1) {

+                final BitmapFactory.Options options = new BitmapFactory.Options();

+                options.inSampleSize = nativeWidth / (int)bitmapWidth;

+                srcBitmap = BitmapFactory.decodeFile(filename, options);

+            } else {

+                srcBitmap = BitmapFactory.decodeFile(filename);

+            }

+        } else {

+            bitmapWidth = width;

+            bitmapHeight = height;

+            srcBitmap = BitmapFactory.decodeFile(filename);

+        }

+

+        if (srcBitmap == null) {

+            Log.e(TAG, "generateThumbnail: Cannot decode image bytes");

+            throw new IOException("Cannot decode file: " + mFilename);

+        }

+

+        // Create the canvas bitmap

+        final Bitmap bitmap = Bitmap.createBitmap((int)bitmapWidth, (int)bitmapHeight,

+                Bitmap.Config.ARGB_8888);

+        final Canvas canvas = new Canvas(bitmap);

+        canvas.drawBitmap(srcBitmap, new Rect(0, 0, srcBitmap.getWidth(), srcBitmap.getHeight()),

+                new Rect(0, 0, (int)bitmapWidth, (int)bitmapHeight), sResizePaint);

+        // Release the source bitmap

+        srcBitmap.recycle();

+        return bitmap;

+    }

+}

diff --git a/media/java/android/media/videoeditor/MediaItem.java b/media/java/android/media/videoeditor/MediaItem.java
new file mode 100755
index 0000000..04878f7
--- /dev/null
+++ b/media/java/android/media/videoeditor/MediaItem.java
@@ -0,0 +1,568 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+import java.io.IOException;

+import java.util.ArrayList;

+import java.util.List;

+

+import android.graphics.Bitmap;

+

+/**

+ * This abstract class describes the base class for any MediaItem. Objects are

+ * defined with a file path as a source data.

+ * {@hide}

+s */

+public abstract class MediaItem {

+    // A constant which can be used to specify the end of the file (instead of

+    // providing the actual duration of the media item).

+    public final static int END_OF_FILE = -1;

+

+    // Rendering modes

+    /**

+     * When using the RENDERING_MODE_BLACK_BORDER rendering mode video frames

+     * are resized by preserving the aspect ratio until the movie matches one of

+     * the dimensions of the output movie. The areas outside the resized video

+     * clip are rendered black.

+     */

+    public static final int RENDERING_MODE_BLACK_BORDER = 0;

+

+    /**

+     * When using the RENDERING_MODE_STRETCH rendering mode video frames are

+     * stretched horizontally or vertically to match the current aspect ratio of

+     * the video editor.

+     */

+    public static final int RENDERING_MODE_STRETCH = 1;

+

+    /**

+     * When using the RENDERING_MODE_CROPPING rendering mode video frames are

+     * scaled horizontally or vertically by preserving the original aspect

+     * ratio of the media item.

+     */

+    public static final int RENDERING_MODE_CROPPING = 2;

+

+

+    // The unique id of the MediaItem

+    private final String mUniqueId;

+

+    // The name of the file associated with the MediaItem

+    protected final String mFilename;

+

+    // List of effects

+    private final List<Effect> mEffects;

+

+    // List of overlays

+    private final List<Overlay> mOverlays;

+

+    // The rendering mode

+    private int mRenderingMode;

+

+    // Beginning and end transitions

+    protected Transition mBeginTransition;

+    protected Transition mEndTransition;

+

+    /**

+     * Constructor

+     *

+     * @param editor The video editor reference

+     * @param mediaItemId The MediaItem id

+     * @param filename name of the media file.

+     * @param renderingMode The rendering mode

+     *

+     * @throws IOException if file is not found

+     * @throws IllegalArgumentException if a capability such as file format is not

+     *             supported the exception object contains the unsupported

+     *             capability

+     */

+    protected MediaItem(VideoEditor editor, String mediaItemId, String filename,

+            int renderingMode) throws IOException {

+        mUniqueId = mediaItemId;

+        mFilename = filename;

+        mRenderingMode = renderingMode;

+        mEffects = new ArrayList<Effect>();

+        mOverlays = new ArrayList<Overlay>();

+        mBeginTransition = null;

+        mEndTransition = null;

+    }

+

+    /**

+     * @return The id of the media item

+     */

+    public String getId() {

+        return mUniqueId;

+    }

+

+    /**

+     * @return The media source file name

+     */

+    public String getFilename() {

+        return mFilename;

+    }

+

+    /**

+     * If aspect ratio of the MediaItem is different from the aspect ratio of

+     * the editor then this API controls the rendering mode.

+     *

+     * @param renderingMode rendering mode. It is one of:

+     *            {@link #RENDERING_MODE_BLACK_BORDER},

+     *            {@link #RENDERING_MODE_STRETCH}

+     */

+    public void setRenderingMode(int renderingMode) {

+        mRenderingMode = renderingMode;

+        if (mBeginTransition != null) {

+            mBeginTransition.invalidate();

+        }

+

+        if (mEndTransition != null) {

+            mEndTransition.invalidate();

+        }

+    }

+

+    /**

+     * @return The rendering mode

+     */

+    public int getRenderingMode() {

+        return mRenderingMode;

+    }

+

+    /**

+     * @param transition The beginning transition

+     */

+    void setBeginTransition(Transition transition) {

+        mBeginTransition = transition;

+    }

+

+    /**

+     * @return The begin transition

+     */

+    public Transition getBeginTransition() {

+        return mBeginTransition;

+    }

+

+    /**

+     * @param transition The end transition

+     */

+    void setEndTransition(Transition transition) {

+        mEndTransition = transition;

+    }

+

+    /**

+     * @return The end transition

+     */

+    public Transition getEndTransition() {

+        return mEndTransition;

+    }

+

+    /**

+     * @return The timeline duration. This is the actual duration in the

+     *      timeline (trimmed duration)

+     */

+    public abstract long getTimelineDuration();

+

+    /**

+     * @return The is the full duration of the media item (not trimmed)

+     */

+    public abstract long getDuration();

+

+    /**

+     * @return The source file type

+     */

+    public abstract int getFileType();

+

+    /**

+     * @return Get the native width of the media item

+     */

+    public abstract int getWidth();

+

+    /**

+     * @return Get the native height of the media item

+     */

+    public abstract int getHeight();

+

+    /**

+     * Get aspect ratio of the source media item.

+     *

+     * @return the aspect ratio as described in MediaProperties.

+     *  MediaProperties.ASPECT_RATIO_UNDEFINED if aspect ratio is not

+     *  supported as in MediaProperties

+     */

+    public abstract int getAspectRatio();

+

+    /**

+     * Add the specified effect to this media item.

+     *

+     * Note that certain types of effects cannot be applied to video and to

+     * image media items. For example in certain implementation a Ken Burns

+     * implementation cannot be applied to video media item.

+     *

+     * This method invalidates transition video clips if the

+     * effect overlaps with the beginning and/or the end transition.

+     *

+     * @param effect The effect to apply

+     * @throws IllegalStateException if a preview or an export is in progress

+     * @throws IllegalArgumentException if the effect start and/or duration are

+     *      invalid or if the effect cannot be applied to this type of media

+     *      item or if the effect id is not unique across all the Effects

+     *      added.

+     */

+    public void addEffect(Effect effect) {

+        if (effect.getMediaItem() != this) {

+            throw new IllegalArgumentException("Media item mismatch");

+        }

+

+        if (mEffects.contains(effect)) {

+            throw new IllegalArgumentException("Effect already exists: " + effect.getId());

+        }

+

+        if (effect.getStartTime() + effect.getDuration() > getDuration()) {

+            throw new IllegalArgumentException(

+                    "Effect start time + effect duration > media clip duration");

+        }

+

+        mEffects.add(effect);

+        invalidateTransitions(effect);

+    }

+

+    /**

+     * Remove the effect with the specified id.

+     *

+     * This method invalidates a transition video clip if the effect overlaps

+     * with a transition.

+     *

+     * @param effectId The id of the effect to be removed

+     *

+     * @return The effect that was removed

+     * @throws IllegalStateException if a preview or an export is in progress

+     */

+    public Effect removeEffect(String effectId) {

+        for (Effect effect : mEffects) {

+            if (effect.getId().equals(effectId)) {

+                mEffects.remove(effect);

+                invalidateTransitions(effect);

+                return effect;

+            }

+        }

+

+        return null;

+    }

+

+    /**

+     * Find the effect with the specified id

+     *

+     * @param effectId The effect id

+     *

+     * @return The effect with the specified id (null if it does not exist)

+     */

+    public Effect getEffect(String effectId) {

+        for (Effect effect : mEffects) {

+            if (effect.getId().equals(effectId)) {

+                return effect;

+            }

+        }

+

+        return null;

+    }

+

+    /**

+     * Get the list of effects.

+     *

+     * @return the effects list. If no effects exist an empty list will be returned.

+     */

+    public List<Effect> getAllEffects() {

+        return mEffects;

+    }

+

+    /**

+     * Add an overlay to the storyboard. This method invalidates a transition

+     * video clip if the overlay overlaps with a transition.

+     *

+     * @param overlay The overlay to add

+     * @throws IllegalStateException if a preview or an export is in progress or

+     *             if the overlay id is not unique across all the overlays

+     *             added or if the bitmap is not specified or if the dimensions of

+     *             the bitmap do not match the dimensions of the media item

+     */

+    public void addOverlay(Overlay overlay) {

+        if (overlay.getMediaItem() != this) {

+            throw new IllegalArgumentException("Media item mismatch");

+        }

+

+        if (mOverlays.contains(overlay)) {

+            throw new IllegalArgumentException("Overlay already exists: " + overlay.getId());

+        }

+

+        if (overlay.getStartTime() + overlay.getDuration() > getDuration()) {

+            throw new IllegalArgumentException(

+                    "Overlay start time + overlay duration > media clip duration");

+        }

+

+        if (overlay instanceof OverlayFrame) {

+            final OverlayFrame frame = (OverlayFrame)overlay;

+            final Bitmap bitmap = frame.getBitmap();

+            if (bitmap == null) {

+                throw new IllegalArgumentException("Overlay bitmap not specified");

+            }

+

+            final int scaledWidth, scaledHeight;

+            if (this instanceof MediaVideoItem) {

+                scaledWidth = getWidth();

+                scaledHeight = getHeight();

+            } else {

+                scaledWidth = ((MediaImageItem)this).getScaledWidth();

+                scaledHeight = ((MediaImageItem)this).getScaledHeight();

+            }

+

+            // The dimensions of the overlay bitmap must be the same as the

+            // media item dimensions

+            if (bitmap.getWidth() != scaledWidth || bitmap.getHeight() != scaledHeight) {

+                throw new IllegalArgumentException(

+                        "Bitmap dimensions must match media item dimensions");

+            }

+        } else {

+            throw new IllegalArgumentException("Overlay not supported");

+        }

+

+        mOverlays.add(overlay);

+        invalidateTransitions(overlay);

+    }

+

+    /**

+     * Remove the overlay with the specified id.

+     *

+     * This method invalidates a transition video clip if the overlay overlaps

+     * with a transition.

+     *

+     * @param overlayId The id of the overlay to be removed

+     *

+     * @return The overlay that was removed

+     * @throws IllegalStateException if a preview or an export is in progress

+     */

+    public Overlay removeOverlay(String overlayId) {

+        for (Overlay overlay : mOverlays) {

+            if (overlay.getId().equals(overlayId)) {

+                mOverlays.remove(overlay);

+                if (overlay instanceof OverlayFrame) {

+                    ((OverlayFrame)overlay).invalidate();

+                }

+                invalidateTransitions(overlay);

+                return overlay;

+            }

+        }

+

+        return null;

+    }

+

+    /**

+     * Find the overlay with the specified id

+     *

+     * @param overlayId The overlay id

+     *

+     * @return The overlay with the specified id (null if it does not exist)

+     */

+    public Overlay getOverlay(String overlayId) {

+        for (Overlay overlay : mOverlays) {

+            if (overlay.getId().equals(overlayId)) {

+                return overlay;

+            }

+        }

+

+        return null;

+    }

+

+    /**

+     * Get the list of overlays associated with this media item

+     *

+     * Note that if any overlay source files are not accessible anymore,

+     * this method will still provide the full list of overlays.

+     *

+     * @return The list of overlays. If no overlays exist an empty list will

+     *  be returned.

+     */

+    public List<Overlay> getAllOverlays() {

+        return mOverlays;

+    }

+

+    /**

+     * Create a thumbnail at specified time in a video stream in Bitmap format

+     *

+     * @param width width of the thumbnail in pixels

+     * @param height height of the thumbnail in pixels

+     * @param timeMs The time in the source video file at which the thumbnail is

+     *            requested (even if trimmed).

+     *

+     * @return The thumbnail as a Bitmap.

+     *

+     * @throws IOException if a file error occurs

+     * @throws IllegalArgumentException if time is out of video duration

+     */

+    public abstract Bitmap getThumbnail(int width, int height, long timeMs) throws IOException;

+

+    /**

+     * Get the array of Bitmap thumbnails between start and end.

+     *

+     * @param width width of the thumbnail in pixels

+     * @param height height of the thumbnail in pixels

+     * @param startMs The start of time range in milliseconds

+     * @param endMs The end of the time range in milliseconds

+     * @param thumbnailCount The thumbnail count

+     *

+     * @return The array of Bitmaps

+     *

+     * @throws IOException if a file error occurs

+     */

+    public abstract Bitmap[] getThumbnailList(int width, int height, long startMs, long endMs,

+            int thumbnailCount) throws IOException;

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public boolean equals(Object object) {

+        if (!(object instanceof MediaItem)) {

+            return false;

+        }

+        return mUniqueId.equals(((MediaItem)object).mUniqueId);

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int hashCode() {

+        return mUniqueId.hashCode();

+    }

+

+    /**

+     * Invalidate the start and end transitions if necessary

+     *

+     * @param effect The effect that was added or removed

+     */

+    void invalidateTransitions(Effect effect) {

+        // Check if the effect overlaps with the beginning and end transitions

+        if (mBeginTransition != null) {

+            if (effect.getStartTime() < mBeginTransition.getDuration()) {

+                mBeginTransition.invalidate();

+            }

+        }

+

+        if (mEndTransition != null) {

+            if (effect.getStartTime() + effect.getDuration() > getDuration()

+                    - mEndTransition.getDuration()) {

+                mEndTransition.invalidate();

+            }

+        }

+    }

+

+    /**

+     * Invalidate the start and end transitions if necessary

+     *

+     * @param overlay The effect that was added or removed

+     */

+    void invalidateTransitions(Overlay overlay) {

+        // Check if the overlay overlaps with the beginning and end transitions

+        if (mBeginTransition != null) {

+            if (overlay.getStartTime() < mBeginTransition.getDuration()) {

+                mBeginTransition.invalidate();

+            }

+        }

+

+        if (mEndTransition != null) {

+            if (overlay.getStartTime() + overlay.getDuration() > getDuration()

+                    - mEndTransition.getDuration()) {

+                mEndTransition.invalidate();

+            }

+        }

+    }

+

+    /**

+     * Adjust the duration transitions.

+     */

+    protected void adjustTransitions() {

+        // Check if the duration of transitions need to be adjusted

+        if (mBeginTransition != null) {

+            final long maxDurationMs = mBeginTransition.getMaximumDuration();

+            if (mBeginTransition.getDuration() > maxDurationMs) {

+                mBeginTransition.setDuration(maxDurationMs);

+            }

+        }

+

+        if (mEndTransition != null) {

+            final long maxDurationMs = mEndTransition.getMaximumDuration();

+            if (mEndTransition.getDuration() > maxDurationMs) {

+                mEndTransition.setDuration(maxDurationMs);

+            }

+        }

+    }

+

+    /**

+     * Adjust the start time and/or duration of effects.

+     */

+    protected void adjustEffects() {

+        final List<Effect> effects = getAllEffects();

+        for (Effect effect : effects) {

+            // Adjust the start time if necessary

+            final long effectStartTimeMs;

+            if (effect.getStartTime() > getDuration()) {

+                effectStartTimeMs = 0;

+            } else {

+                effectStartTimeMs = effect.getStartTime();

+            }

+

+            // Adjust the duration if necessary

+            final long effectDurationMs;

+            if (effectStartTimeMs + effect.getDuration() > getDuration()) {

+                effectDurationMs = getDuration() - effectStartTimeMs;

+            } else {

+                effectDurationMs = effect.getDuration();

+            }

+

+            if (effectStartTimeMs != effect.getStartTime() ||

+                    effectDurationMs != effect.getDuration()) {

+                effect.setStartTimeAndDuration(effectStartTimeMs, effectDurationMs);

+            }

+        }

+    }

+

+    /**

+     * Adjust the start time and/or duration of overlays.

+     */

+    protected void adjustOverlays() {

+        final List<Overlay> overlays = getAllOverlays();

+        for (Overlay overlay : overlays) {

+            // Adjust the start time if necessary

+            final long overlayStartTimeMs;

+            if (overlay.getStartTime() > getDuration()) {

+                overlayStartTimeMs = 0;

+            } else {

+                overlayStartTimeMs = overlay.getStartTime();

+            }

+

+            // Adjust the duration if necessary

+            final long overlayDurationMs;

+            if (overlayStartTimeMs + overlay.getDuration() > getDuration()) {

+                overlayDurationMs = getDuration() - overlayStartTimeMs;

+            } else {

+                overlayDurationMs = overlay.getDuration();

+            }

+

+            if (overlayStartTimeMs != overlay.getStartTime() ||

+                    overlayDurationMs != overlay.getDuration()) {

+                overlay.setStartTimeAndDuration(overlayStartTimeMs, overlayDurationMs);

+            }

+        }

+    }

+}

diff --git a/media/java/android/media/videoeditor/MediaProperties.java b/media/java/android/media/videoeditor/MediaProperties.java
new file mode 100755
index 0000000..34088fc
--- /dev/null
+++ b/media/java/android/media/videoeditor/MediaProperties.java
@@ -0,0 +1,257 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+import android.util.Pair;

+

+/**

+ * This class defines all properties of a media file such as supported height, aspect ratio,

+ * bitrate for export function.

+ * {@hide}

+ */

+public class MediaProperties {

+    // Supported heights

+    public static final int HEIGHT_144 = 144;

+    public static final int HEIGHT_360 = 360;

+    public static final int HEIGHT_480 = 480;

+    public static final int HEIGHT_720 = 720;

+    public static final int HEIGHT_1080 = 1080;

+

+    // Supported aspect ratios

+    public static final int ASPECT_RATIO_UNDEFINED = 0;

+    public static final int ASPECT_RATIO_3_2 = 1;

+    public static final int ASPECT_RATIO_16_9 = 2;

+    public static final int ASPECT_RATIO_4_3 = 3;

+    public static final int ASPECT_RATIO_5_3 = 4;

+    public static final int ASPECT_RATIO_11_9 = 5;

+

+    // The array of supported aspect ratios

+    private static final int[] ASPECT_RATIOS = new int[] {

+        ASPECT_RATIO_3_2,

+        ASPECT_RATIO_16_9,

+        ASPECT_RATIO_4_3,

+        ASPECT_RATIO_5_3,

+        ASPECT_RATIO_11_9

+    };

+

+    // Supported resolutions for specific aspect ratios

+    @SuppressWarnings({"unchecked"})

+    private static final Pair<Integer, Integer>[] ASPECT_RATIO_3_2_RESOLUTIONS =

+        new Pair[] {

+        new Pair<Integer, Integer>(720, HEIGHT_480),

+        new Pair<Integer, Integer>(1080, HEIGHT_720)

+    };

+

+    @SuppressWarnings({"unchecked"})

+    private static final Pair<Integer, Integer>[] ASPECT_RATIO_4_3_RESOLUTIONS =

+        new Pair[] {

+        new Pair<Integer, Integer>(640, HEIGHT_480),

+        new Pair<Integer, Integer>(960, HEIGHT_720)

+    };

+

+    @SuppressWarnings({"unchecked"})

+    private static final Pair<Integer, Integer>[] ASPECT_RATIO_5_3_RESOLUTIONS =

+        new Pair[] {

+        new Pair<Integer, Integer>(800, HEIGHT_480)

+    };

+

+    @SuppressWarnings({"unchecked"})

+    private static final Pair<Integer, Integer>[] ASPECT_RATIO_11_9_RESOLUTIONS =

+        new Pair[] {

+        new Pair<Integer, Integer>(176, HEIGHT_144)

+    };

+

+    @SuppressWarnings({"unchecked"})

+    private static final Pair<Integer, Integer>[] ASPECT_RATIO_16_9_RESOLUTIONS =

+        new Pair[] {

+        new Pair<Integer, Integer>(640, HEIGHT_360),

+        new Pair<Integer, Integer>(854, HEIGHT_480),

+        new Pair<Integer, Integer>(1280, HEIGHT_720),

+    };

+

+

+    // Bitrate values (in bits per second)

+    public static final int BITRATE_28K = 28000;

+    public static final int BITRATE_40K = 40000;

+    public static final int BITRATE_64K = 64000;

+    public static final int BITRATE_96K = 96000;

+    public static final int BITRATE_128K = 128000;

+    public static final int BITRATE_192K = 192000;

+    public static final int BITRATE_256K = 256000;

+    public static final int BITRATE_384K = 384000;

+    public static final int BITRATE_512K = 512000;

+    public static final int BITRATE_800K = 800000;

+

+    // The array of supported bitrates

+    private static final int[] SUPPORTED_BITRATES = new int[] {

+        BITRATE_28K,

+        BITRATE_40K,

+        BITRATE_64K,

+        BITRATE_96K,

+        BITRATE_128K,

+        BITRATE_192K,

+        BITRATE_256K,

+        BITRATE_384K,

+        BITRATE_512K,

+        BITRATE_800K

+    };

+

+    // Video codec types

+    public static final int VCODEC_H264BP = 1;

+    public static final int VCODEC_H264MP = 2;

+    public static final int VCODEC_H263 = 3;

+    public static final int VCODEC_MPEG4 = 4;

+

+    // The array of supported video codecs

+    private static final int[] SUPPORTED_VCODECS = new int[] {

+        VCODEC_H264BP,

+        VCODEC_H263,

+        VCODEC_MPEG4,

+    };

+

+    // Audio codec types

+    public static final int ACODEC_AAC_LC = 1;

+    public static final int ACODEC_AMRNB = 2;

+    public static final int ACODEC_AMRWB = 3;

+    public static final int ACODEC_MP3 = 4;

+    public static final int ACODEC_OGG = 5;

+

+    // The array of supported video codecs

+    private static final int[] SUPPORTED_ACODECS = new int[] {

+        ACODEC_AAC_LC,

+        ACODEC_AMRNB,

+        ACODEC_AMRWB

+    };

+

+    // File format types

+    public static final int FILE_UNSUPPORTED = 0;

+    public static final int FILE_3GP = 1;

+    public static final int FILE_MP4 = 2;

+    public static final int FILE_JPEG = 3;

+    public static final int FILE_PNG = 4;

+

+    // The array of the supported file formats

+    private static final int[] SUPPORTED_VIDEO_FILE_FORMATS = new int[] {

+        FILE_3GP,

+        FILE_MP4

+    };

+

+    // The maximum count of audio tracks supported

+    public static final int AUDIO_MAX_TRACK_COUNT = 1;

+

+    // The maximum volume supported (100 means that no amplification is

+    // supported, i.e. attenuation only)

+    public static final int AUDIO_MAX_VOLUME_PERCENT = 100;

+

+    /**

+     * This class cannot be instantiated

+     */

+    private MediaProperties() {

+    }

+

+    /**

+     * @return The array of supported aspect ratios

+     */

+    public static int[] getAllSupportedAspectRatios() {

+        return ASPECT_RATIOS;

+    }

+

+    /**

+     * Get the supported resolutions for the specified aspect ratio.

+     *

+     * @param aspectRatio The aspect ratio for which the resolutions are requested

+     *

+     * @return The array of width and height pairs

+     */

+    public static Pair<Integer, Integer>[] getSupportedResolutions(int aspectRatio) {

+        final Pair<Integer, Integer>[] resolutions;

+        switch(aspectRatio) {

+            case ASPECT_RATIO_3_2: {

+                resolutions = ASPECT_RATIO_3_2_RESOLUTIONS;

+                break;

+            }

+

+            case ASPECT_RATIO_4_3: {

+                resolutions = ASPECT_RATIO_4_3_RESOLUTIONS;

+                break;

+            }

+

+            case ASPECT_RATIO_5_3: {

+                resolutions = ASPECT_RATIO_5_3_RESOLUTIONS;

+                break;

+            }

+

+            case ASPECT_RATIO_11_9: {

+                resolutions = ASPECT_RATIO_11_9_RESOLUTIONS;

+                break;

+            }

+

+            case ASPECT_RATIO_16_9: {

+                resolutions = ASPECT_RATIO_16_9_RESOLUTIONS;

+                break;

+            }

+

+            default: {

+                throw new IllegalArgumentException("Unknown aspect ratio: " + aspectRatio);

+            }

+        }

+

+        return resolutions;

+    }

+

+    /**

+     * @return The array of supported video codecs

+     */

+    public static int[] getSupportedVideoCodecs() {

+        return SUPPORTED_VCODECS;

+    }

+

+    /**

+     * @return The array of supported audio codecs

+     */

+    public static int[] getSupportedAudioCodecs() {

+        return SUPPORTED_ACODECS;

+    }

+

+    /**

+     * @return The array of supported file formats

+     */

+    public static int[] getSupportedVideoFileFormat() {

+        return SUPPORTED_VIDEO_FILE_FORMATS;

+    }

+

+    /**

+     * @return The array of supported video bitrates

+     */

+    public static int[] getSupportedVideoBitrates() {

+        return SUPPORTED_BITRATES;

+    }

+

+    /**

+     * @return The maximum value for the audio volume

+     */

+    public static int getSupportedMaxVolume() {

+        return MediaProperties.AUDIO_MAX_VOLUME_PERCENT;

+    }

+

+    /**

+     * @return The maximum number of audio tracks supported

+     */

+    public static int getSupportedAudioTrackCount() {

+        return MediaProperties.AUDIO_MAX_TRACK_COUNT;

+    }

+}

diff --git a/media/java/android/media/videoeditor/MediaVideoItem.java b/media/java/android/media/videoeditor/MediaVideoItem.java
new file mode 100755
index 0000000..c1abf78
--- /dev/null
+++ b/media/java/android/media/videoeditor/MediaVideoItem.java
@@ -0,0 +1,426 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+import java.io.IOException;

+import java.lang.ref.SoftReference;

+

+import android.graphics.Bitmap;

+import android.view.SurfaceHolder;

+

+/**

+ * This class represents a video clip item on the storyboard

+ * {@hide}

+ */

+public class MediaVideoItem extends MediaItem {

+    // Instance variables

+    private final int mWidth;

+    private final int mHeight;

+    private final int mAspectRatio;

+    private final int mFileType;

+    private final int mVideoType;

+    private final int mVideoProfile;

+    private final int mVideoBitrate;

+    private final long mDurationMs;

+    private final int mAudioBitrate;

+    private final int mFps;

+    private final int mAudioType;

+    private final int mAudioChannels;

+    private final int mAudioSamplingFrequency;

+

+    private long mBeginBoundaryTimeMs;

+    private long mEndBoundaryTimeMs;

+    private int mVolumePercentage;

+    private boolean mMuted;

+    private String mAudioWaveformFilename;

+    // The audio waveform data

+    private SoftReference<WaveformData> mWaveformData;

+

+    /**

+     * An object of this type cannot be instantiated with a default constructor

+     */

+    @SuppressWarnings("unused")

+    private MediaVideoItem() throws IOException {

+        this(null, null, null, RENDERING_MODE_BLACK_BORDER);

+    }

+

+    /**

+     * Constructor

+     *

+     * @param editor The video editor reference

+     * @param mediaItemId The MediaItem id

+     * @param filename The image file name

+     * @param renderingMode The rendering mode

+     *

+     * @throws IOException if the file cannot be opened for reading

+     */

+    public MediaVideoItem(VideoEditor editor, String mediaItemId, String filename,

+            int renderingMode)

+        throws IOException {

+        this(editor, mediaItemId, filename, renderingMode, 0, END_OF_FILE, 100, false, null);

+    }

+

+    /**

+     * Constructor

+     *

+     * @param editor The video editor reference

+     * @param mediaItemId The MediaItem id

+     * @param filename The image file name

+     * @param renderingMode The rendering mode

+     * @param beginMs Start time in milliseconds. Set to 0 to extract from the

+     *           beginning

+     * @param endMs End time in milliseconds. Set to {@link #END_OF_FILE} to

+     *           extract until the end

+     * @param volumePercent in %/. 100% means no change; 50% means half value, 200%

+     *            means double, 0% means silent.

+     * @param muted true if the audio is muted

+     * @param audioWaveformFilename The name of the audio waveform file

+     *

+     * @throws IOException if the file cannot be opened for reading

+     */

+    MediaVideoItem(VideoEditor editor, String mediaItemId, String filename, int renderingMode,

+            long beginMs, long endMs, int volumePercent, boolean muted,

+            String audioWaveformFilename)  throws IOException {

+        super(editor, mediaItemId, filename, renderingMode);

+        // TODO: Set these variables correctly

+        mWidth = 1080;

+        mHeight = 720;

+        mAspectRatio = MediaProperties.ASPECT_RATIO_3_2;

+        mFileType = MediaProperties.FILE_MP4;

+        mVideoType = MediaProperties.VCODEC_H264BP;

+        // Do we have predefined values for this variable?

+        mVideoProfile = 0;

+        // Can video and audio duration be different?

+        mDurationMs = 10000;

+        mVideoBitrate = 800000;

+        mAudioBitrate = 30000;

+        mFps = 30;

+        mAudioType = MediaProperties.ACODEC_AAC_LC;

+        mAudioChannels = 2;

+        mAudioSamplingFrequency = 16000;

+

+        mBeginBoundaryTimeMs = beginMs;

+        mEndBoundaryTimeMs = endMs == END_OF_FILE ? mDurationMs : endMs;

+        mVolumePercentage = volumePercent;

+        mMuted = muted;

+        mAudioWaveformFilename = audioWaveformFilename;

+        if (audioWaveformFilename != null) {

+            mWaveformData =

+                new SoftReference<WaveformData>(new WaveformData(audioWaveformFilename));

+        } else {

+            mWaveformData = null;

+        }

+    }

+

+    /**

+     * Sets the start and end marks for trimming a video media item.

+     * This method will adjust the duration of bounding transitions, effects

+     * and overlays if the current duration of the transactions become greater

+     * than the maximum allowable duration.

+     *

+     * @param beginMs Start time in milliseconds. Set to 0 to extract from the

+     *           beginning

+     * @param endMs End time in milliseconds. Set to {@link #END_OF_FILE} to

+     *           extract until the end

+     *

+     * @throws IllegalArgumentException if the start time is greater or equal than

+     *           end time, the end time is beyond the file duration, the start time

+     *           is negative

+     */

+    public void setExtractBoundaries(long beginMs, long endMs) {

+        if (beginMs > mDurationMs) {

+            throw new IllegalArgumentException("Invalid start time");

+        }

+        if (endMs > mDurationMs) {

+            throw new IllegalArgumentException("Invalid end time");

+        }

+

+        if (beginMs != mBeginBoundaryTimeMs) {

+            if (mBeginTransition != null) {

+                mBeginTransition.invalidate();

+            }

+        }

+

+        if (endMs != mEndBoundaryTimeMs) {

+            if (mEndTransition != null) {

+                mEndTransition.invalidate();

+            }

+        }

+

+        mBeginBoundaryTimeMs = beginMs;

+        mEndBoundaryTimeMs = endMs;

+

+        adjustTransitions();

+

+        // Note that the start and duration of any effects and overlays are

+        // not adjusted nor are they automatically removed if they fall

+        // outside the new boundaries.

+    }

+

+    /**

+     * @return The boundary begin time

+     */

+    public long getBoundaryBeginTime() {

+        return mBeginBoundaryTimeMs;

+    }

+

+    /**

+     * @return The boundary end time

+     */

+    public long getBoundaryEndTime() {

+        return mEndBoundaryTimeMs;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public void addEffect(Effect effect) {

+        if (effect instanceof EffectKenBurns) {

+            throw new IllegalArgumentException("Ken Burns effects cannot be applied to MediaVideoItem");

+        }

+        super.addEffect(effect);

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public Bitmap getThumbnail(int width, int height, long timeMs) {

+        return null;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public Bitmap[] getThumbnailList(int width, int height, long startMs, long endMs,

+            int thumbnailCount) throws IOException {

+        return null;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int getAspectRatio() {

+        return mAspectRatio;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int getFileType() {

+        return mFileType;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int getWidth() {

+        return mWidth;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int getHeight() {

+        return mHeight;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public long getDuration() {

+        return mDurationMs;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public long getTimelineDuration() {

+        return mEndBoundaryTimeMs - mBeginBoundaryTimeMs;

+    }

+

+    /**

+     * Render a frame according to the playback (in the native aspect ratio) for

+     * the specified media item. All effects and overlays applied to the media

+     * item are ignored. The extract boundaries are also ignored. This method

+     * can be used to playback frames when implementing trimming functionality.

+     *

+     * @param surfaceHolder SurfaceHolder used by the application

+     * @param timeMs time corresponding to the frame to display (relative to the

+     *            the beginning of the media item).

+     * @return The accurate time stamp of the frame that is rendered .

+     * @throws IllegalStateException if a playback, preview or an export is

+     *             already in progress

+     * @throws IllegalArgumentException if time is negative or greater than the

+     *             media item duration

+     */

+    public long renderFrame(SurfaceHolder surfaceHolder, long timeMs) {

+        return timeMs;

+    }

+

+    /**

+     * This API allows to generate a file containing the sample volume levels of

+     * the Audio track of this media item. This function may take significant

+     * time and is blocking. The file can be retrieved using

+     * getAudioWaveformFilename().

+     *

+     * @param listener The progress listener

+     *

+     * @throws IOException if the output file cannot be created

+     * @throws IllegalArgumentException if the mediaItem does not have a valid

+     *             Audio track

+     */

+    public void extractAudioWaveform(ExtractAudioWaveformProgressListener listener)

+            throws IOException {

+        // TODO: Set mAudioWaveformFilename at the end once the export is complete

+        mWaveformData = new SoftReference<WaveformData>(new WaveformData(mAudioWaveformFilename));

+    }

+

+    /**

+     * Get the audio waveform file name if {@link #extractAudioWaveform()} was

+     * successful. The file format is as following:

+     * <ul>

+     *  <li>first 4 bytes provide the number of samples for each value, as big-endian signed</li>

+     *  <li>4 following bytes is the total number of values in the file, as big-endian signed</li>

+     *  <li>all values follow as bytes Name is unique.</li>

+     *</ul>

+     * @return the name of the file, null if the file has not been computed or

+     *         if there is no Audio track in the mediaItem

+     */

+    String getAudioWaveformFilename() {

+        return mAudioWaveformFilename;

+    }

+

+    /**

+     * @return The waveform data

+     */

+    public WaveformData getWaveformData() {

+        if (mWaveformData == null) {

+            return null;

+        }

+

+        WaveformData waveformData = mWaveformData.get();

+        if (waveformData != null) {

+            return waveformData;

+        } else if (mAudioWaveformFilename != null) {

+            waveformData = new WaveformData(mAudioWaveformFilename);

+            mWaveformData = new SoftReference<WaveformData>(waveformData);

+            return waveformData;

+        } else {

+            return null;

+        }

+    }

+

+    /**

+     * Set volume of the Audio track of this mediaItem

+     *

+     * @param volumePercent in %/. 100% means no change; 50% means half value, 200%

+     *            means double, 0% means silent.

+     * @throws UsupportedOperationException if volume value is not supported

+     */

+    public void setVolume(int volumePercent) {

+        mVolumePercentage = volumePercent;

+    }

+

+    /**

+     * Get the volume value of the audio track as percentage. Call of this

+     * method before calling setVolume will always return 100%

+     *

+     * @return the volume in percentage

+     */

+    public int getVolume() {

+        return mVolumePercentage;

+    }

+

+    /**

+     * @param muted true to mute the media item

+     */

+    public void setMute(boolean muted) {

+        mMuted = muted;

+    }

+

+    /**

+     * @return true if the media item is muted

+     */

+    public boolean isMuted() {

+        return mMuted;

+    }

+

+    /**

+     * @return The video type

+     */

+    public int getVideoType() {

+        return mVideoType;

+    }

+

+    /**

+     * @return The video profile

+     */

+    public int getVideoProfile() {

+        return mVideoProfile;

+    }

+

+    /**

+     * @return The video bitrate

+     */

+    public int getVideoBitrate() {

+        return mVideoBitrate;

+    }

+

+    /**

+     * @return The audio bitrate

+     */

+    public int getAudioBitrate() {

+        return mAudioBitrate;

+    }

+

+    /**

+     * @return The number of frames per second

+     */

+    public int getFps() {

+        return mFps;

+    }

+

+    /**

+     * @return The audio codec

+     */

+    public int getAudioType() {

+        return mAudioType;

+    }

+

+    /**

+     * @return The number of audio channels

+     */

+    public int getAudioChannels() {

+        return mAudioChannels;

+    }

+

+    /**

+     * @return The audio sample frequency

+     */

+    public int getAudioSamplingFrequency() {

+        return mAudioSamplingFrequency;

+    }

+}

diff --git a/media/java/android/media/videoeditor/Overlay.java b/media/java/android/media/videoeditor/Overlay.java
new file mode 100755
index 0000000..fff9ca2
--- /dev/null
+++ b/media/java/android/media/videoeditor/Overlay.java
@@ -0,0 +1,187 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+import java.util.HashMap;

+import java.util.Map;

+

+

+/**

+ * This is the super class for all Overlay classes.

+ * {@hide}

+ */

+public abstract class Overlay {

+    // Instance variables

+    private final String mUniqueId;

+    // The overlay owner

+    private final MediaItem mMediaItem;

+    // user attributes

+    private final Map<String, String> mUserAttributes;

+

+    protected long mStartTimeMs;

+    protected long mDurationMs;

+

+

+    /**

+     * Default constructor

+     */

+    @SuppressWarnings("unused")

+    private Overlay() {

+        this(null, null, 0, 0);

+    }

+

+    /**

+     * Constructor

+     *

+     * @param mediaItem The media item owner

+     * @param overlayId The overlay id

+     * @param startTimeMs The start time relative to the media item start time

+     * @param durationMs The duration

+     *

+     * @throws IllegalArgumentException if the file type is not PNG or the

+     *      startTimeMs and durationMs are incorrect.

+     */

+    public Overlay(MediaItem mediaItem, String overlayId, long startTimeMs, long durationMs) {

+        if (mediaItem == null) {

+            throw new IllegalArgumentException("Media item cannot be null");

+        }

+

+        if (startTimeMs + durationMs > mediaItem.getDuration()) {

+            throw new IllegalArgumentException("Invalid start time and duration");

+        }

+

+        mMediaItem = mediaItem;

+        mUniqueId = overlayId;

+        mStartTimeMs = startTimeMs;

+        mDurationMs = durationMs;

+        mUserAttributes = new HashMap<String, String>();

+    }

+

+    /**

+     * @return The of the overlay

+     */

+    public String getId() {

+        return mUniqueId;

+    }

+

+    /**

+     * @return The duration of the overlay effect

+     */

+    public long getDuration() {

+        return mDurationMs;

+    }

+

+    /**

+     * If a preview or export is in progress, then this change is effective for

+     * next preview or export session.

+     *

+     * @param durationMs The duration in milliseconds

+     */

+    public void setDuration(long durationMs) {

+        if (mStartTimeMs + durationMs > mMediaItem.getDuration()) {

+            throw new IllegalArgumentException("Duration is too large");

+        }

+

+        mDurationMs = durationMs;

+

+        mMediaItem.invalidateTransitions(this);

+    }

+

+    /**

+     * @return the start time of the overlay

+     */

+    public long getStartTime() {

+        return mStartTimeMs;

+    }

+

+    /**

+     * Set the start time for the overlay. If a preview or export is in

+     * progress, then this change is effective for next preview or export

+     * session.

+     *

+     * @param startTimeMs start time in milliseconds

+     */

+    public void setStartTime(long startTimeMs) {

+        if (startTimeMs + mDurationMs > mMediaItem.getDuration()) {

+            throw new IllegalArgumentException("Start time is too large");

+        }

+

+        mStartTimeMs = startTimeMs;

+

+        mMediaItem.invalidateTransitions(this);

+    }

+

+    /**

+     * Set the start time and duration

+     *

+     * @param startTimeMs start time in milliseconds

+     * @param durationMs The duration in milliseconds

+     */

+    public void setStartTimeAndDuration(long startTimeMs, long durationMs) {

+        if (startTimeMs + durationMs > mMediaItem.getDuration()) {

+            throw new IllegalArgumentException("Invalid start time or duration");

+        }

+

+        mStartTimeMs = startTimeMs;

+        mDurationMs = durationMs;

+

+        mMediaItem.invalidateTransitions(this);

+    }

+

+    /**

+     * @return The media item owner

+     */

+    public MediaItem getMediaItem() {

+        return mMediaItem;

+    }

+

+    /**

+     * Set a user attribute

+     *

+     * @param name The attribute name

+     * @param value The attribute value

+     */

+    public void setUserAttribute(String name, String value) {

+        mUserAttributes.put(name, value);

+    }

+

+    /**

+     * @return The user attributes

+     */

+    public Map<String, String> getUserAttributes() {

+        return mUserAttributes;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public boolean equals(Object object) {

+        if (!(object instanceof Overlay)) {

+            return false;

+        }

+        return mUniqueId.equals(((Overlay)object).mUniqueId);

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int hashCode() {

+        return mUniqueId.hashCode();

+    }

+}

diff --git a/media/java/android/media/videoeditor/OverlayFrame.java b/media/java/android/media/videoeditor/OverlayFrame.java
new file mode 100755
index 0000000..dcac4ba
--- /dev/null
+++ b/media/java/android/media/videoeditor/OverlayFrame.java
@@ -0,0 +1,149 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+import java.io.File;

+import java.io.FileNotFoundException;

+import java.io.FileOutputStream;

+import java.io.IOException;

+

+import android.graphics.Bitmap;

+import android.graphics.BitmapFactory;

+import android.graphics.Bitmap.CompressFormat;

+

+

+/**

+ * This class is used to overlay an image on top of a media item.

+ * {@hide}

+ */

+public class OverlayFrame extends Overlay {

+    // Instance variables

+    private Bitmap mBitmap;

+    private String mFilename;

+

+    /**

+     * An object of this type cannot be instantiated by using the default

+     * constructor

+     */

+    @SuppressWarnings("unused")

+    private OverlayFrame() {

+        this(null, null, (String)null, 0, 0);

+    }

+

+    /**

+     * Constructor for an OverlayFrame

+     *

+     * @param mediaItem The media item owner

+     * @param overlayId The overlay id

+     * @param bitmap The bitmap to be used as an overlay. The size of the

+     *      bitmap must equal to the size of the media item to which it is

+     *      added. The bitmap is typically a decoded PNG file.

+     * @param startTimeMs The overlay start time in milliseconds

+     * @param durationMs The overlay duration in milliseconds

+     *

+     * @throws IllegalArgumentException if the file type is not PNG or the

+     *      startTimeMs and durationMs are incorrect.

+     */

+    public OverlayFrame(MediaItem mediaItem, String overlayId, Bitmap bitmap, long startTimeMs,

+            long durationMs) {

+        super(mediaItem, overlayId, startTimeMs, durationMs);

+        mBitmap = bitmap;

+        mFilename = null;

+    }

+

+    /**

+     * Constructor for an OverlayFrame. This constructor can be used to

+     * restore the overlay after it was saved internally by the video editor.

+     *

+     * @param mediaItem The media item owner

+     * @param overlayId The overlay id

+     * @param filename The file name that contains the overlay.

+     * @param startTimeMs The overlay start time in milliseconds

+     * @param durationMs The overlay duration in milliseconds

+     *

+     * @throws IllegalArgumentException if the file type is not PNG or the

+     *      startTimeMs and durationMs are incorrect.

+     */

+    OverlayFrame(MediaItem mediaItem, String overlayId, String filename, long startTimeMs,

+            long durationMs) {

+        super(mediaItem, overlayId, startTimeMs, durationMs);

+        mFilename = filename;

+        mBitmap = BitmapFactory.decodeFile(mFilename);

+    }

+

+    /**

+     * @return Get the overlay bitmap

+     */

+    public Bitmap getBitmap() {

+        return mBitmap;

+    }

+

+    /**

+     * @param bitmap The overlay bitmap

+     */

+    public void setBitmap(Bitmap bitmap) {

+        mBitmap = bitmap;

+        if (mFilename != null) {

+            // Delete the file

+            new File(mFilename).delete();

+            // Invalidate the filename

+            mFilename = null;

+        }

+

+        // Invalidate the transitions if necessary

+        getMediaItem().invalidateTransitions(this);

+    }

+

+    /**

+     * Get the file name of this overlay

+     */

+    String getFilename() {

+        return mFilename;

+    }

+

+    /**

+     * Save the overlay to the project folder

+     *

+     * @param path The path where the overlay will be saved

+     *

+     * @return The filename

+     * @throws FileNotFoundException if the bitmap cannot be saved

+     * @throws IOException if the bitmap file cannot be saved

+     */

+    String save(String path) throws FileNotFoundException, IOException {

+        if (mFilename != null) {

+            return mFilename;

+        }

+

+        mFilename = path + "/" + getId() + ".png";

+        // Save the image to a local file

+        final FileOutputStream out = new FileOutputStream(mFilename);

+        mBitmap.compress(CompressFormat.PNG, 100, out);

+        out.flush();

+        out.close();

+        return mFilename;

+    }

+

+    /**

+     * Delete the overlay file

+     */

+    void invalidate() {

+        if (mFilename != null) {

+            new File(mFilename).delete();

+        }

+    }

+}

diff --git a/media/java/android/media/videoeditor/Transition.java b/media/java/android/media/videoeditor/Transition.java
new file mode 100755
index 0000000..1c82742
--- /dev/null
+++ b/media/java/android/media/videoeditor/Transition.java
@@ -0,0 +1,210 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+import java.io.File;

+

+/**

+ * This class is super class for all transitions. Transitions (with the

+ * exception of TransitionAtStart and TransitioAtEnd) can only be inserted

+ * between media items.

+ *

+ * Adding a transition between MediaItems makes the

+ * duration of the storyboard shorter by the duration of the Transition itself.

+ * As a result, if the duration of the transition is larger than the smaller

+ * duration of the two MediaItems associated with the Transition, an exception

+ * will be thrown.

+ *

+ * During a transition, the audio track are cross-fading

+ * automatically. {@hide}

+ */

+public abstract class Transition {

+    // The transition behavior

+    private static final int BEHAVIOR_MIN_VALUE = 0;

+    /** The transition starts slowly and speed up */

+    public static final int BEHAVIOR_SPEED_UP = 0;

+    /** The transition start fast and speed down */

+    public static final int BEHAVIOR_SPEED_DOWN = 1;

+    /** The transition speed is constant */

+    public static final int BEHAVIOR_LINEAR = 2;

+    /** The transition starts fast and ends fast with a slow middle */

+    public static final int BEHAVIOR_MIDDLE_SLOW = 3;

+    /** The transition starts slowly and ends slowly with a fast middle */

+    public static final int BEHAVIOR_MIDDLE_FAST = 4;

+

+    private static final int BEHAVIOR_MAX_VALUE = 4;

+

+    // The unique id of the transition

+    private final String mUniqueId;

+

+    // The transition is applied at the end of this media item

+    private final MediaItem mAfterMediaItem;

+    // The transition is applied at the beginning of this media item

+    private final MediaItem mBeforeMediaItem;

+

+    // The transition behavior

+    protected final int mBehavior;

+

+    // The transition duration

+    protected long mDurationMs;

+

+    // The transition filename

+    protected String mFilename;

+

+    /**

+     * An object of this type cannot be instantiated by using the default

+     * constructor

+     */

+    @SuppressWarnings("unused")

+    private Transition() {

+        this(null, null, null, 0, 0);

+    }

+

+    /**

+     * Constructor

+     *

+     * @param transitionId The transition id

+     * @param afterMediaItem The transition is applied to the end of this

+     *      media item

+     * @param beforeMediaItem The transition is applied to the beginning of

+     *      this media item

+     * @param durationMs The duration of the transition in milliseconds

+     * @param behavior The transition behavior

+     */

+    protected Transition(String transitionId, MediaItem afterMediaItem, MediaItem beforeMediaItem,

+            long durationMs, int behavior) {

+        if (behavior < BEHAVIOR_MIN_VALUE || behavior > BEHAVIOR_MAX_VALUE) {

+            throw new IllegalArgumentException("Invalid behavior: " + behavior);

+        }

+        mUniqueId = transitionId;

+        mAfterMediaItem = afterMediaItem;

+        mBeforeMediaItem = beforeMediaItem;

+        mDurationMs = durationMs;

+        mBehavior = behavior;

+    }

+

+    /**

+     * @return The of the transition

+     */

+    public String getId() {

+        return mUniqueId;

+    }

+

+    /**

+     * @return The media item at the end of which the transition is applied

+     */

+    public MediaItem getAfterMediaItem() {

+        return mAfterMediaItem;

+    }

+

+    /**

+     * @return The media item at the beginning of which the transition is applied

+     */

+    public MediaItem getBeforeMediaItem() {

+        return mBeforeMediaItem;

+    }

+

+    /**

+     * Set the duration of the transition.

+     *

+     * @param durationMs the duration of the transition in milliseconds

+     */

+    public void setDuration(long durationMs) {

+        if (durationMs > getMaximumDuration()) {

+            throw new IllegalArgumentException("The duration is too large");

+        }

+

+        mDurationMs = durationMs;

+        invalidate();

+    }

+

+    /**

+     * @return the duration of the transition in milliseconds

+     */

+    public long getDuration() {

+        return mDurationMs;

+    }

+

+    /**

+     * The duration of a transition cannot be greater than half of the minimum

+     * duration of the bounding media items.

+     *

+     * @return The maximum duration of this transition

+     */

+    public long getMaximumDuration() {

+        if (mAfterMediaItem == null) {

+            return mBeforeMediaItem.getTimelineDuration() / 2;

+        } else if (mBeforeMediaItem == null) {

+            return mAfterMediaItem.getTimelineDuration() / 2;

+        } else {

+            return (Math.min(mAfterMediaItem.getTimelineDuration(),

+                    mBeforeMediaItem.getTimelineDuration()) / 2);

+        }

+    }

+

+    /**

+     * @return The behavior

+     */

+    public int getBehavior() {

+        return mBehavior;

+    }

+

+    /**

+     * Generate the video clip for the specified transition.

+     * This method may block for a significant amount of time.

+     *

+     * Before the method completes execution it sets the mFilename to

+     * the name of the newly generated transition video clip file.

+     */

+    abstract void generate();

+

+    /**

+     * Remove any resources associated with this transition

+     */

+    void invalidate() {

+        if (mFilename != null) {

+            new File(mFilename).delete();

+            mFilename = null;

+        }

+    }

+

+    /**

+     * @return true if the transition is generated

+     */

+    boolean isGenerated() {

+        return (mFilename != null);

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public boolean equals(Object object) {

+        if (!(object instanceof Transition)) {

+            return false;

+        }

+        return mUniqueId.equals(((Transition)object).mUniqueId);

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public int hashCode() {

+        return mUniqueId.hashCode();

+    }

+}

diff --git a/media/java/android/media/videoeditor/TransitionAlpha.java b/media/java/android/media/videoeditor/TransitionAlpha.java
new file mode 100755
index 0000000..30e66fc
--- /dev/null
+++ b/media/java/android/media/videoeditor/TransitionAlpha.java
@@ -0,0 +1,114 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+

+/**

+ * This class allows to render an "alpha blending" transition according to a

+ * bitmap mask. The mask shows the shape of the transition all along the

+ * duration of the transition: just before the transition, video 1 is fully

+ * displayed. When the transition starts, as the time goes on, pixels of video 2

+ * replace pixels of video 1 according to the gray scale pixel value of the

+ * mask.

+ * {@hide}

+ */

+public class TransitionAlpha extends Transition {

+    /** This is the input JPEG file for the mask */

+    private final String mMaskFilename;

+

+    /**

+     * This is percentage (between 0 and 100) of blending between video 1 and

+     * video 2 if this value equals 0, then the mask is strictly applied if this

+     * value equals 100, then the mask is not at all applied (no transition

+     * effect)

+     */

+    private final int mBlendingPercent;

+

+    /**

+     * If true, this value inverts the direction of the mask: white pixels of

+     * the mask show video 2 pixels first black pixels of the mask show video 2

+     * pixels last.

+     */

+    private final boolean mIsInvert;

+

+    /**

+     * An object of this type cannot be instantiated by using the default

+     * constructor

+     */

+    @SuppressWarnings("unused")

+    private TransitionAlpha() {

+        this(null, null, null, 0, 0, null, 0, false);

+    }

+

+    /**

+     * Constructor

+     *

+     * @param transitionId The transition id

+     * @param afterMediaItem The transition is applied to the end of this media

+     *            item

+     * @param beforeMediaItem The transition is applied to the beginning of this

+     *            media item

+     * @param durationMs duration of the transition in milliseconds

+     * @param behavior behavior is one of the behavior defined in Transition

+     *            class

+     * @param maskFilename JPEG file name. The dimension of the image

+     *           corresponds to 720p (16:9 aspect ratio). Mask files are

+     *           shared between video editors and can be created in the

+     *           projects folder (the parent folder for all projects).

+     * @param blendingPercent The blending percent applied

+     * @param invert true to invert the direction of the alpha blending

+     * @throws IllegalArgumentException if behavior is not supported, or if

+     *             direction are not supported.

+     */

+    public TransitionAlpha(String transitionId, MediaItem afterMediaItem,

+            MediaItem beforeMediaItem, long durationMs, int behavior, String maskFilename,

+            int blendingPercent, boolean invert) {

+        super(transitionId, afterMediaItem, beforeMediaItem, durationMs, behavior);

+

+        mMaskFilename = maskFilename;

+        mBlendingPercent = blendingPercent;

+        mIsInvert = invert;

+    }

+

+    /**

+     * @return The blending percentage

+     */

+    public int getBlendingPercent() {

+        return mBlendingPercent;

+    }

+

+    /**

+     * @return The mask filename

+     */

+    public String getMaskFilename() {

+        return mMaskFilename;

+    }

+

+    /**

+     * @return true if the direction of the alpha blending is inverted

+     */

+    public boolean isInvert() {

+        return mIsInvert;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    public void generate() {

+    }

+}

diff --git a/media/java/android/media/videoeditor/TransitionCrossfade.java b/media/java/android/media/videoeditor/TransitionCrossfade.java
new file mode 100755
index 0000000..f8223e8
--- /dev/null
+++ b/media/java/android/media/videoeditor/TransitionCrossfade.java
@@ -0,0 +1,60 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+

+/**

+ * This class allows to render a crossfade (dissolve) effect transition between

+ * two videos

+ * {@hide}

+ */

+public class TransitionCrossfade extends Transition {

+    /**

+     * An object of this type cannot be instantiated by using the default

+     * constructor

+     */

+    @SuppressWarnings("unused")

+    private TransitionCrossfade() {

+        this(null, null, null, 0, 0);

+    }

+

+    /**

+     * Constructor

+     *

+     * @param transitionId The transition id

+     * @param afterMediaItem The transition is applied to the end of this

+     *      media item

+     * @param beforeMediaItem The transition is applied to the beginning of

+     *      this media item

+     * @param durationMs duration of the transition in milliseconds

+     * @param behavior behavior is one of the behavior defined in Transition

+     *            class

+     *

+     * @throws IllegalArgumentException if behavior is not supported.

+     */

+    public TransitionCrossfade(String transitionId, MediaItem afterMediaItem,

+            MediaItem beforeMediaItem, long durationMs, int behavior) {

+        super(transitionId, afterMediaItem, beforeMediaItem, durationMs, behavior);

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    void generate() {

+    }

+}

diff --git a/media/java/android/media/videoeditor/TransitionFadeBlack.java b/media/java/android/media/videoeditor/TransitionFadeBlack.java
new file mode 100755
index 0000000..a9bf4ce
--- /dev/null
+++ b/media/java/android/media/videoeditor/TransitionFadeBlack.java
@@ -0,0 +1,60 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+

+/**

+ * This class is used to render a fade to black and fade from black transition

+ * between two media items.

+ * {@hide}

+ */

+public class TransitionFadeBlack extends Transition {

+    /**

+     * An object of this type cannot be instantiated by using the default

+     * constructor

+     */

+    @SuppressWarnings("unused")

+    private TransitionFadeBlack() {

+        this(null, null, null, 0, 0);

+    }

+

+    /**

+     * Constructor

+     *

+     * @param transitionId The transition id

+     * @param afterMediaItem The transition is applied to the end of this

+     *      media item

+     * @param beforeMediaItem The transition is applied to the beginning of

+     *      this media item

+     * @param durationMs duration of the transition

+     * @param behavior behavior is one of the behavior defined in Transition

+     *            class

+     *

+     * @throws IllegalArgumentException if behavior is not supported.

+     */

+    public TransitionFadeBlack(String transitionId, MediaItem afterMediaItem,

+            MediaItem beforeMediaItem, long durationMs, int behavior) {

+        super(transitionId, afterMediaItem, beforeMediaItem, durationMs, behavior);

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    void generate() {

+    }

+}

diff --git a/media/java/android/media/videoeditor/TransitionSliding.java b/media/java/android/media/videoeditor/TransitionSliding.java
new file mode 100755
index 0000000..cc9f4b2
--- /dev/null
+++ b/media/java/android/media/videoeditor/TransitionSliding.java
@@ -0,0 +1,82 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+

+package android.media.videoeditor;

+

+/**

+ * This class allows to create sliding transitions

+ * {@hide}

+ */

+public class TransitionSliding extends Transition {

+

+    /** Video 1 is pushed to the right while video 2 is coming from left */

+    public final static int DIRECTION_RIGHT_OUT_LEFT_IN = 0;

+    /** Video 1 is pushed to the left while video 2 is coming from right */

+    public static final int DIRECTION_LEFT_OUT_RIGHT_IN = 1;

+    /** Video 1 is pushed to the top while video 2 is coming from bottom */

+    public static final int DIRECTION_TOP_OUT_BOTTOM_IN = 2;

+    /** Video 1 is pushed to the bottom while video 2 is coming from top */

+    public static final int DIRECTION_BOTTOM_OUT_TOP_IN = 3;

+

+    // The sliding transitions

+    private final int mSlidingDirection;

+

+    /**

+     * An object of this type cannot be instantiated by using the default

+     * constructor

+     */

+    @SuppressWarnings("unused")

+    private TransitionSliding() {

+        this(null, null, null, 0, 0, 0);

+    }

+

+    /**

+     * Constructor

+     *

+     * @param transitionId The transition id

+     * @param afterMediaItem The transition is applied to the end of this

+     *      media item

+     * @param beforeMediaItem The transition is applied to the beginning of

+     *      this media item

+     * @param durationMs duration of the transition in milliseconds

+     * @param behavior behavior is one of the behavior defined in Transition

+     *            class

+     * @param direction direction shall be one of the supported directions like

+     *            RIGHT_OUT_LEFT_IN

+     *

+     * @throws IllegalArgumentException if behavior is not supported.

+     */

+    public TransitionSliding(String transitionId, MediaItem afterMediaItem,

+            MediaItem beforeMediaItem, long durationMs, int behavior, int direction) {

+        super(transitionId, afterMediaItem, beforeMediaItem, durationMs, behavior);

+        mSlidingDirection = direction;

+    }

+

+    /**

+     * @return The sliding direction

+     */

+    public int getDirection() {

+        return mSlidingDirection;

+    }

+

+    /*

+     * {@inheritDoc}

+     */

+    @Override

+    void generate() {

+    }

+}

diff --git a/media/java/android/media/videoeditor/VideoEditor.java b/media/java/android/media/videoeditor/VideoEditor.java
new file mode 100755
index 0000000..64d3229
--- /dev/null
+++ b/media/java/android/media/videoeditor/VideoEditor.java
@@ -0,0 +1,536 @@
+/*

+ * Copyright (C) 2010 The Android Open Source Project

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package android.media.videoeditor;

+

+import java.io.IOException;

+import java.util.List;

+import java.util.concurrent.CancellationException;

+

+import android.view.SurfaceHolder;

+

+/**

+ * This is the interface implemented by classes which provide video editing

+ * functionality. The VideoEditor implementation class manages all input and

+ * output files. Unless specifically mentioned, methods are blocking. A

+ * typical editing session may consist of the following sequence of operations:

+ *

+ * <ul>

+ *  <li>Add a set of MediaItems</li>

+ *  <li>Apply a set of Transitions between MediaItems</li>

+ *  <li>Add Effects and Overlays to media items</li>

+ *  <li>Preview the movie at any time</li>

+ *  <li>Save the VideoEditor implementation class internal state</li>

+ *  <li>Release the VideoEditor implementation class instance by invoking

+ * {@link #release()}

+ * </ul>

+ * The internal VideoEditor state consists of the following elements:

+ * <ul>

+ *  <li>Ordered & trimmed MediaItems</li>

+ *  <li>Transition video clips</li>

+ *  <li>Overlays</li>

+ *  <li>Effects</li>

+ *  <li>Audio waveform for the background audio and MediaItems</li>

+ *  <li>Project thumbnail</li>

+ *  <li>Last exported movie.</li>

+ *  <li>Other project specific data such as the current aspect ratio.</li>

+ * </ul>

+ * {@hide}

+ */

+public interface VideoEditor {

+    // The file name of the project thumbnail

+    public static final String THUMBNAIL_FILENAME = "thumbnail.jpg";

+

+    // Use this value instead of the specific end of the storyboard timeline

+    // value.

+    public final static int DURATION_OF_STORYBOARD = -1;

+

+    /**

+     * This listener interface is used by the VideoEditor to emit preview

+     * progress notifications. This callback should be invoked after the

+     * number of frames specified by

+     * {@link #startPreview(SurfaceHolder surfaceHolder, long fromMs,

+     *           int callbackAfterFrameCount, PreviewProgressListener listener)}

+     */

+    public interface PreviewProgressListener {

+        /**

+         * This method notifies the listener of the current time position while

+         * previewing a project.

+         *

+         * @param videoEditor The VideoEditor instance

+         * @param timeMs The current preview position (expressed in milliseconds

+         *            since the beginning of the storyboard timeline).

+         * @param end true if the end of the timeline was reached

+         */

+        public void onProgress(VideoEditor videoEditor, long timeMs, boolean end);

+    }

+

+    /**

+     * This listener interface is used by the VideoEditor to emit export status

+     * notifications.

+     * {@link #export(String filename, ExportProgressListener listener, int height, int bitrate)}

+     */

+    public interface ExportProgressListener {

+        /**

+         * This method notifies the listener of the progress status of a export

+         * operation.

+         *

+         * @param videoEditor The VideoEditor instance

+         * @param filename The name of the file which is in the process of being

+         *            exported.

+         * @param progress The progress in %. At the beginning of the export, this

+         *            value is set to 0; at the end, the value is set to 100.

+         */

+        public void onProgress(VideoEditor videoEditor, String filename, int progress);

+    }

+

+    /**

+     * @return The path where the VideoEditor stores all files related to the

+     * project

+     */

+    public String getPath();

+

+    /**

+     * This method releases all in-memory resources used by the VideoEditor

+     * instance. All pending operations such as preview, export and extract

+     * audio waveform must be canceled.

+     */

+    public void release();

+

+    /**

+     * Persist the current internal state of VideoEditor to the project path.

+     * The VideoEditor state may be restored by invoking the

+     * {@link VideoEditorFactory#load(String)} method. This method does not

+     * release the internal in-memory state of the VideoEditor. To release

+     * the in-memory state of the VideoEditor the {@link #release()} method

+     * must be invoked.

+     *

+     * Pending transition generations must be allowed to complete before the

+     * state is saved.

+     * Pending audio waveform generations must be allowed to complete.

+     * Pending export operations must be allowed to continue.

+     */

+    public void save() throws IOException;

+

+    /**

+     * Create the output movie based on all media items added and the applied

+     * storyboard items. This method can take a long time to execute and is

+     * blocking. The application will receive progress notifications via the

+     * ExportProgressListener. Specific implementations may not support multiple

+     * simultaneous export operations. Note that invoking methods which would

+     * change the contents of the output movie throw an IllegalStateException

+     * while an export operation is pending.

+     *

+     * The audio and video codecs are automatically selected by the underlying

+     * implementation.

+     *

+     * @param filename The output file name (including the full path)

+     * @param height The height of the output video file. The supported values

+     *            for height are described in the MediaProperties class, for

+     *            example: HEIGHT_480. The width will be automatically computed

+     *            according to the aspect ratio provided by

+     *            {@link #setAspectRatio(int)}

+     * @param bitrate The bitrate of the output video file. This is approximate

+     *            value for the output movie. Supported bitrate values are

+     *            described in the MediaProperties class for example:

+     *            BITRATE_384K

+     * @param listener The listener for progress notifications. Use null if

+     *            export progress notifications are not needed.

+     * @throws IllegalArgumentException if height or bitrate are not supported

+     *             or if the audio or video codecs are not supported

+     * @throws IOException if output file cannot be created

+     * @throws IllegalStateException if a preview or an export is in progress or

+     *             if no MediaItem has been added

+     * @throws CancellationException if export is canceled by calling

+     *             {@link #cancelExport()}

+     * @throws UnsupportOperationException if multiple simultaneous export() are

+     *             not allowed

+     */

+    public void export(String filename, int height, int bitrate, ExportProgressListener listener)

+            throws IOException;

+

+    /**

+     * Create the output movie based on all media items added and the applied

+     * storyboard items. This method can take a long time to execute and is

+     * blocking. The application will receive progress notifications via the

+     * ExportProgressListener. Specific implementations may not support multiple

+     * simultaneous export operations. Note that invoking methods which would

+     * change the contents of the output movie throw an IllegalStateException

+     * while an export operation is pending.

+     *

+     * @param filename The output file name (including the full path)

+     * @param height The height of the output video file. The supported values

+     *            for height are described in the MediaProperties class, for

+     *            example: HEIGHT_480. The width will be automatically computed

+     *            according to the aspect ratio provided by

+     *            {@link #setAspectRatio(int)}

+     * @param bitrate The bitrate of the output video file. This is approximate

+     *            value for the output movie. Supported bitrate values are

+     *            described in the MediaProperties class for example:

+     *            BITRATE_384K

+     * @param audioCodec The audio codec to be used for the export. The audio

+     *            codec values are defined in the MediaProperties class (e.g.

+     *            ACODEC_AAC_LC). Note that not all audio codec types are

+     *            supported for export purposes.

+     * @param videoCodec The video codec to be used for the export. The video

+     *            codec values are defined in the MediaProperties class (e.g.

+     *            VCODEC_H264BP). Note that not all video codec types are

+     *            supported for export purposes.

+     * @param listener The listener for progress notifications. Use null if

+     *            export progress notifications are not needed.

+     * @throws IllegalArgumentException if height or bitrate are not supported

+     *             or if the audio or video codecs are not supported

+     * @throws IOException if output file cannot be created

+     * @throws IllegalStateException if a preview or an export is in progress or

+     *             if no MediaItem has been added

+     * @throws CancellationException if export is canceled by calling

+     *             {@link #cancelExport()}

+     * @throws UnsupportOperationException if multiple simultaneous export() are

+     *             not allowed

+     */

+    public void export(String filename, int height, int bitrate, int audioCodec, int videoCodec,

+            ExportProgressListener listener) throws IOException;

+

+    /**

+     * Cancel the running export operation. This method blocks until the

+     * export is canceled and the exported file (if any) is deleted. If the

+     * export completed by the time this method is invoked, the export file

+     * will be deleted.

+     *

+     * @param filename The filename which identifies the export operation to be

+     *            canceled.

+     **/

+    public void cancelExport(String filename);

+

+    /**

+     * Add a media item at the end of the storyboard.

+     *

+     * @param mediaItem The media item object to add

+     * @throws IllegalStateException if a preview or an export is in progress or

+     *             if the media item id is not unique across all the media items

+     *             added.

+     */

+    public void addMediaItem(MediaItem mediaItem);

+

+    /**

+     * Insert a media item after the media item with the specified id.

+     *

+     * @param mediaItem The media item object to insert

+     * @param afterMediaItemId Insert the mediaItem after the media item

+     *            identified by this id. If this parameter is null, the media

+     *            item is inserted at the beginning of the timeline.

+     *

+     * @throws IllegalStateException if a preview or an export is in progress

+     * @throws IllegalArgumentException if media item with the specified id does

+     *             not exist (null is a valid value) or if the media item id is

+     *             not unique across all the media items added.

+     */

+    public void insertMediaItem(MediaItem mediaItem, String afterMediaItemId);

+

+    /**

+     * Move a media item after the media item with the specified id.

+     *

+     * Note: The project thumbnail is regenerated if the media item is or

+     * becomes the first media item in the storyboard timeline.

+     *

+     * @param mediaItemId The id of the media item to move

+     * @param afterMediaItemId Move the media item identified by mediaItemId after

+     *          the media item identified by this parameter. If this parameter

+     *          is null, the media item is moved at the beginning of the

+     *          timeline.

+     *

+     * @throws IllegalStateException if a preview or an export is in progress

+     * @throws IllegalArgumentException if one of media item ids is invalid

+     *          (null is a valid value)

+     */

+    public void moveMediaItem(String mediaItemId, String afterMediaItemId);

+

+    /**

+     * Remove the media item with the specified id. If there are transitions

+     * before or after this media item, then this/these transition(s) are

+     * removed from the storyboard. If the extraction of the audio waveform is

+     * in progress, the extraction is canceled and the file is deleted.

+     *

+     * Effects and overlays associated with the media item will also be

+     * removed.

+     *

+     * Note: The project thumbnail is regenerated if the media item which

+     * is removed is the first media item in the storyboard or if the

+     * media item is the only one in the storyboard. If the

+     * media item is the only one in the storyboard, the project thumbnail

+     * will be set to a black frame and the aspect ratio will revert to the

+     * default aspect ratio, and this method is equivalent to

+     * removeAllMediaItems() in this case.

+     *

+     * @param mediaItemId The unique id of the media item to be removed

+     *

+     * @return The media item that was removed

+     *

+     * @throws IllegalStateException if a preview or an export is in progress

+     * @throws IllegalArgumentException if media item with the specified id

+     *          does not exist

+     */

+    public MediaItem removeMediaItem(String mediaItemId);

+

+    /**

+     * Remove all media items in the storyboard. All effects, overlays and all

+     * transitions are also removed.

+     *

+     * Note: The project thumbnail will be set to a black frame and the aspect

+     * ratio will revert to the default aspect ratio.

+     *

+     * @throws IllegalStateException if a preview or an export is in progress

+     */

+    public void removeAllMediaItems();

+

+    /**

+     * Get the list of media items in the order in which it they appear in the

+     * storyboard timeline.

+     *

+     * Note that if any media item source files are no longer

+     * accessible, this method will still provide the full list of media items.

+     *

+     * @return The list of media items. If no media item exist an empty list

+     *          will be returned.

+     */

+    public List<MediaItem> getAllMediaItems();

+

+    /**

+     * Find the media item with the specified id

+     *

+     * @param mediaItemId The media item id

+     *

+     * @return The media item with the specified id (null if it does not exist)

+     */

+    public MediaItem getMediaItem(String mediaItemId);

+

+    /**

+     * Add a transition between the media items specified by the transition.

+     * If a transition existed at the same position it is invalidated and then

+     * the transition is replaced. Note that the new transition video clip is

+     * not automatically generated by this method. The

+     * {@link Transition#generate()} method must be invoked to generate

+     * the transition video clip.

+     *

+     * Note that the TransitionAtEnd and TransitionAtStart are special kinds

+     * that can not be applied between two media items.

+     *

+     * A crossfade audio transition will be automatically applied regardless of

+     * the video transition.

+     *

+     * @param transition The transition to apply

+     *

+     * @throws IllegalStateException if a preview or an export is in progress

+     * @throws IllegalArgumentException if the transition duration is larger

+     *              than the smallest duration of the two media item files or

+     *              if the two media items specified in the transition are not

+     *              adjacent

+     */

+    public void addTransition(Transition transition);

+

+    /**

+     * Remove the transition with the specified id.

+     *

+     * @param transitionId The id of the transition to be removed

+     *

+     * @return The transition that was removed

+     * @throws IllegalStateException if a preview or an export is in progress

+     * @throws IllegalArgumentException if transition with the specified id does

+     *             not exist

+     */

+    public Transition removeTransition(String transitionId);

+

+    /**

+     * Get the list of transitions

+     *

+     * @return The list of transitions. If no transitions exist an empty list

+     *  will be returned.

+     */

+    public List<Transition> getAllTransitions();

+

+    /**

+     * Find the transition with the specified transition id.

+     *

+     * @param transitionId The transition id

+     *

+     * @return The transition

+     */

+    public Transition getTransition(String transitionId);

+

+    /**

+     * Add the specified AudioTrack to the storyboard. Note: Specific

+     * implementations may support a limited number of audio tracks (e.g. only

+     * one audio track)

+     *

+     * @param audioTrack The AudioTrack to add

+     * @throws UnsupportedOperationException if the implementation supports a

+     *             limited number of audio tracks.

+     * @throws IllegalArgumentException if media item is not unique across all

+     *             the audio tracks already added.

+     */

+    public void addAudioTrack(AudioTrack audioTrack);

+

+    /**

+     * Insert an audio track after the audio track with the specified id. Use

+     * addAudioTrack to add an audio track at the end of the storyboard

+     * timeline.

+     *

+     * @param audioTrack The audio track object to insert

+     * @param afterAudioTrackId Insert the audio track after the audio track

+     *            identified by this parameter. If this parameter is null the

+     *            audio track is added at the beginning of the timeline.

+     * @throws IllegalStateException if a preview or an export is in progress

+     * @throws IllegalArgumentException if media item with the specified id does

+     *             not exist (null is a valid value). if media item is not

+     *             unique across all the audio tracks already added.

+     * @throws UnsupportedOperationException if the implementation supports a

+     *             limited number of audio tracks

+     */

+    public void insertAudioTrack(AudioTrack audioTrack, String afterAudioTrackId);

+

+    /**

+     * Move an AudioTrack after the AudioTrack with the specified id.

+     *

+     * @param audioTrackId The id of the AudioTrack to move

+     * @param afterAudioTrackId Move the AudioTrack identified by audioTrackId

+     *            after the AudioTrack identified by this parameter. If this

+     *            parameter is null the audio track is added at the beginning of

+     *            the timeline.

+     * @throws IllegalStateException if a preview or an export is in progress

+     * @throws IllegalArgumentException if one of media item ids is invalid

+     *             (null is a valid value)

+     */

+    public void moveAudioTrack(String audioTrackId, String afterAudioTrackId);

+

+    /**

+     * Remove the audio track with the specified id. If the extraction of the

+     * audio waveform is in progress, the extraction is canceled and the file is

+     * deleted.

+     *

+     * @param audioTrackId The id of the audio track to be removed

+     *

+     * @return The audio track that was removed

+     * @throws IllegalStateException if a preview or an export is in progress

+     */

+    public AudioTrack removeAudioTrack(String audioTrackId);

+

+    /**

+     * Get the list of AudioTracks in order in which they appear in the storyboard.

+     *

+     * Note that if any AudioTrack source files are not accessible anymore,

+     * this method will still provide the full list of audio tracks.

+     *

+     * @return The list of AudioTracks. If no audio tracks exist an empty list

+     *  will be returned.

+     */

+    public List<AudioTrack> getAllAudioTracks();

+

+    /**

+     * Find the AudioTrack with the specified id

+     *

+     * @param audioTrackId The AudioTrack id

+     *

+     * @return The AudioTrack with the specified id (null if it does not exist)

+     */

+    public AudioTrack getAudioTrack(String audioTrackId);

+

+    /**

+     * Set the aspect ratio used in the preview and the export movie.

+     *

+     * The default aspect ratio is ASPECTRATIO_16_9 (16:9).

+     *

+     * @param aspectRatio to apply. If aspectRatio is the same as the current

+     *            aspect ratio, then this function just returns. The supported

+     *            aspect ratio are defined in the MediaProperties class for

+     *            example: ASPECTRATIO_16_9

+     *

+     * @throws IllegalStateException if a preview or an export is in progress

+     * @throws IllegalArgumentException if aspect ratio is not supported

+     */

+    public void setAspectRatio(int aspectRatio);

+

+    /**

+     * Get current aspect ratio.

+     *

+     * @return The aspect ratio as described in MediaProperties

+     */

+    public int getAspectRatio();

+

+    /**

+     * Get the preview (and output movie) duration.

+     *

+     * @return The duration of the preview (and output movie)

+     */

+    public long getDuration();

+

+    /**

+     * Render a frame according to the preview aspect ratio and activating all

+     * storyboard items relative to the specified time.

+     *

+     * @param surfaceHolder SurfaceHolder used by the application

+     * @param timeMs time corresponding to the frame to display

+     *

+     * @return The accurate time stamp of the frame that is rendered

+     * .

+     * @throws IllegalStateException if a preview or an export is already

+     *             in progress

+     * @throws IllegalArgumentException if time is negative or beyond the

+     *             preview duration

+     */

+    public long renderPreviewFrame(SurfaceHolder surfaceHolder, long timeMs);

+

+    /**

+     * This method must be called after the aspect ratio of the project changes

+     * and before startPreview is called. Note that this method may block for

+     * an extensive period of time.

+     */

+    public void generatePreview();

+

+    /**

+     * Start the preview of all the storyboard items applied on all MediaItems

+     * This method does not block (does not wait for the preview to complete).

+     * The PreviewProgressListener allows to track the progress at the time

+     * interval determined by the callbackAfterFrameCount parameter. The

+     * SurfaceHolder has to be created and ready for use before calling this

+     * method. The method is a no-op if there are no MediaItems in the

+     * storyboard.

+     *

+     * @param surfaceHolder SurfaceHolder where the preview is rendered.

+     * @param fromMs The time (relative to the timeline) at which the preview

+     *            will start

+     * @param toMs The time (relative to the timeline) at which the preview will

+     *            stop. Use -1 to play to the end of the timeline

+     * @param loop true if the preview should be looped once it reaches the end

+     * @param callbackAfterFrameCount The listener interface should be invoked

+     *            after the number of frames specified by this parameter.

+     * @param listener The listener which will be notified of the preview

+     *            progress

+     * @throws IllegalArgumentException if fromMs is beyond the preview duration

+     * @throws IllegalStateException if a preview or an export is already in

+     *             progress

+     */

+    public void startPreview(SurfaceHolder surfaceHolder, long fromMs, long toMs, boolean loop,

+            int callbackAfterFrameCount, PreviewProgressListener listener);

+

+    /**

+     * Stop the current preview. This method blocks until ongoing preview is

+     * stopped. Ignored if there is no preview running.

+     *

+     * @return The accurate current time when stop is effective expressed in

+     *          milliseconds

+     */

+    public long stopPreview();

+}

diff --git a/media/java/android/media/videoeditor/VideoEditorFactory.java b/media/java/android/media/videoeditor/VideoEditorFactory.java
new file mode 100755
index 0000000..41eed16
--- /dev/null
+++ b/media/java/android/media/videoeditor/VideoEditorFactory.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.videoeditor;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+
+/**
+ * The VideoEditorFactory class must be used to instantiate VideoEditor objects
+ * by creating a new project {@link #create(String)} or by loading an
+ * existing project {@link #load(String)}.
+ * {@hide}
+ */
+public class VideoEditorFactory {
+    // VideoEditor implementation classes
+    public static final String TEST_CLASS_IMPLEMENTATION
+            = "android.media.videoeditor.VideoEditorTestImpl";
+    public static final String DEFAULT_CLASS_IMPLEMENTATION
+            = "android.media.videoeditor.VideoEditorImpl";
+
+    /**
+     * This is the factory method for creating a new VideoEditor instance.
+     *
+     * @param projectPath The path where all VideoEditor internal
+     *            files are stored. When a project is deleted the application is
+     *            responsible for deleting the path and its contents.
+     * @param className The implementation class name
+     *
+     * @return The VideoEditor instance
+     *
+     * @throws IOException if path does not exist or if the path can
+     *             not be accessed in read/write mode
+     * @throws IllegalStateException if a previous VideoEditor instance has not
+     *             been released
+     * @throws ClassNotFoundException, NoSuchMethodException,
+     *             InvocationTargetException, IllegalAccessException,
+     *             InstantiationException if the implementation class cannot
+     *             be instantiated.
+     */
+    public static VideoEditor create(String projectPath, String className) throws IOException,
+            ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
+            IllegalAccessException, InstantiationException {
+        // If the project path does not exist create it
+        final File dir = new File(projectPath);
+        if (!dir.exists()) {
+            if (!dir.mkdirs()) {
+                throw new FileNotFoundException("Cannot create project path: " + projectPath);
+            } else {
+                // Create the file which hides the media files
+                // from the media scanner
+                if (!new File(dir, ".nomedia").createNewFile()) {
+                    throw new FileNotFoundException("Cannot create file .nomedia");
+                }
+            }
+        }
+
+        final Class<?> cls = Class.forName(className);
+        final Class<?> partypes[] = new Class[1];
+        partypes[0] = String.class;
+        final Constructor<?> ct = cls.getConstructor(partypes);
+        final Object arglist[] = new Object[1];
+        arglist[0] = projectPath;
+
+        return (VideoEditor)ct.newInstance(arglist);
+    }
+
+    /**
+     * This is the factory method for instantiating a VideoEditor from the
+     * internal state previously saved with the
+     * {@link VideoEditor#save(String)} method.
+     *
+     * @param projectPath The path where all VideoEditor internal files
+     *            are stored. When a project is deleted the application is
+     *            responsible for deleting the path and its contents.
+     * @param className The implementation class name
+     * @param generatePreview if set to true the
+     *      {@link MediaEditor#generatePreview()} will be called internally to
+     *      generate any needed transitions.
+     *
+     * @return The VideoEditor instance
+     *
+     * @throws IOException if path does not exist or if the path can
+     *             not be accessed in read/write mode or if one of the resource
+     *             media files cannot be retrieved
+     * @throws IllegalStateException if a previous VideoEditor instance has not
+     *             been released
+     */
+    public static VideoEditor load(String projectPath, String className, boolean generatePreview)
+            throws IOException, ClassNotFoundException, NoSuchMethodException,
+            InvocationTargetException, IllegalAccessException, InstantiationException {
+        final Class<?> cls = Class.forName(className);
+        final Class<?> partypes[] = new Class[1];
+        partypes[0] = String.class;
+        final Constructor<?> ct = cls.getConstructor(partypes);
+        final Object arglist[] = new Object[1];
+        arglist[0] = projectPath;
+
+        final VideoEditor videoEditor = (VideoEditor)ct.newInstance(arglist);
+        if (generatePreview) {
+            videoEditor.generatePreview();
+        }
+        return videoEditor;
+    }
+}
diff --git a/media/java/android/media/videoeditor/VideoEditorTestImpl.java b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
new file mode 100644
index 0000000..40b49a5
--- /dev/null
+++ b/media/java/android/media/videoeditor/VideoEditorTestImpl.java
@@ -0,0 +1,1222 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.videoeditor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.graphics.Rect;
+import android.util.Log;
+import android.util.Xml;
+import android.view.SurfaceHolder;
+
+/**
+ * The VideoEditor implementation {@hide}
+ */
+public class VideoEditorTestImpl implements VideoEditor {
+    // Logging
+    private static final String TAG = "VideoEditorImpl";
+
+    // The project filename
+    private static final String PROJECT_FILENAME = "videoeditor.xml";
+
+    // XML tags
+    private static final String TAG_PROJECT = "project";
+    private static final String TAG_MEDIA_ITEMS = "media_items";
+    private static final String TAG_MEDIA_ITEM = "media_item";
+    private static final String TAG_TRANSITIONS = "transitions";
+    private static final String TAG_TRANSITION = "transition";
+    private static final String TAG_OVERLAYS = "overlays";
+    private static final String TAG_OVERLAY = "overlay";
+    private static final String TAG_OVERLAY_USER_ATTRIBUTES = "overlay_user_attributes";
+    private static final String TAG_EFFECTS = "effects";
+    private static final String TAG_EFFECT = "effect";
+    private static final String TAG_AUDIO_TRACKS = "audio_tracks";
+    private static final String TAG_AUDIO_TRACK = "audio_track";
+
+    private static final String ATTR_ID = "id";
+    private static final String ATTR_FILENAME = "filename";
+    private static final String ATTR_AUDIO_WAVEFORM_FILENAME = "wavefoem";
+    private static final String ATTR_RENDERING_MODE = "rendering_mode";
+    private static final String ATTR_ASPECT_RATIO = "aspect_ratio";
+    private static final String ATTR_TYPE = "type";
+    private static final String ATTR_DURATION = "duration";
+    private static final String ATTR_START_TIME = "start_time";
+    private static final String ATTR_BEGIN_TIME = "begin_time";
+    private static final String ATTR_END_TIME = "end_time";
+    private static final String ATTR_VOLUME = "volume";
+    private static final String ATTR_BEHAVIOR = "behavior";
+    private static final String ATTR_DIRECTION = "direction";
+    private static final String ATTR_BLENDING = "blending";
+    private static final String ATTR_INVERT = "invert";
+    private static final String ATTR_MASK = "mask";
+    private static final String ATTR_BEFORE_MEDIA_ITEM_ID = "before_media_item";
+    private static final String ATTR_AFTER_MEDIA_ITEM_ID = "after_media_item";
+    private static final String ATTR_COLOR_EFFECT_TYPE = "color_type";
+    private static final String ATTR_COLOR_EFFECT_VALUE = "color_value";
+    private static final String ATTR_START_RECT_L = "start_l";
+    private static final String ATTR_START_RECT_T = "start_t";
+    private static final String ATTR_START_RECT_R = "start_r";
+    private static final String ATTR_START_RECT_B = "start_b";
+    private static final String ATTR_END_RECT_L = "end_l";
+    private static final String ATTR_END_RECT_T = "end_t";
+    private static final String ATTR_END_RECT_R = "end_r";
+    private static final String ATTR_END_RECT_B = "end_b";
+    private static final String ATTR_LOOP = "loop";
+    private static final String ATTR_MUTED = "muted";
+    private static final String ATTR_DUCK_ENABLED = "ducking_enabled";
+    private static final String ATTR_DUCK_THRESHOLD = "ducking_threshold";
+    private static final String ATTR_DUCKED_TRACK_VOLUME = "ducking_volume";
+
+    // Instance variables
+    private long mDurationMs;
+    private final String mProjectPath;
+    private final List<MediaItem> mMediaItems = new ArrayList<MediaItem>();
+    private final List<AudioTrack> mAudioTracks = new ArrayList<AudioTrack>();
+    private final List<Transition> mTransitions = new ArrayList<Transition>();
+    private PreviewThread mPreviewThread;
+    private int mAspectRatio;
+
+    /**
+     * The preview thread
+     */
+    private class PreviewThread extends Thread {
+        // Instance variables
+        private final static long FRAME_DURATION = 33;
+
+        // Instance variables
+        private final PreviewProgressListener mListener;
+        private final int mCallbackAfterFrameCount;
+        private final long mFromMs, mToMs;
+        private boolean mRun, mLoop;
+        private long mPositionMs;
+
+        /**
+         * Constructor
+         *
+         * @param fromMs Start preview at this position
+         * @param toMs The time (relative to the timeline) at which the preview
+         *            will stop. Use -1 to play to the end of the timeline
+         * @param callbackAfterFrameCount The listener interface should be
+         *            invoked after the number of frames specified by this
+         *            parameter.
+         * @param loop true if the preview should be looped once it reaches the
+         *            end
+         * @param listener The listener
+         */
+        public PreviewThread(long fromMs, long toMs, boolean loop, int callbackAfterFrameCount,
+                PreviewProgressListener listener) {
+            mPositionMs = mFromMs = fromMs;
+            if (toMs < 0) {
+                mToMs = mDurationMs;
+            } else {
+                mToMs = toMs;
+            }
+            mLoop = loop;
+            mCallbackAfterFrameCount = callbackAfterFrameCount;
+            mListener = listener;
+            mRun = true;
+        }
+
+        /*
+         * {@inheritDoc}
+         */
+        @Override
+        public void run() {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "===> PreviewThread.run enter");
+            }
+            int frameCount = 0;
+            while (mRun) {
+                try {
+                    sleep(FRAME_DURATION);
+                } catch (InterruptedException ex) {
+                    break;
+                }
+                frameCount++;
+                mPositionMs += FRAME_DURATION;
+
+                if (mPositionMs >= mToMs) {
+                    if (!mLoop) {
+                        if (mListener != null) {
+                            mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, true);
+                        }
+                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                            Log.d(TAG, "PreviewThread.run playback complete");
+                        }
+                        break;
+                    } else {
+                        // Fire a notification for the end of the clip
+                        if (mListener != null) {
+                            mListener.onProgress(VideoEditorTestImpl.this, mToMs, false);
+                        }
+
+                        // Rewind
+                        mPositionMs = mFromMs;
+                        if (mListener != null) {
+                            mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, false);
+                        }
+                        if (Log.isLoggable(TAG, Log.DEBUG)) {
+                            Log.d(TAG, "PreviewThread.run playback complete");
+                        }
+                        frameCount = 0;
+                    }
+                } else {
+                    if (frameCount == mCallbackAfterFrameCount) {
+                        if (mListener != null) {
+                            mListener.onProgress(VideoEditorTestImpl.this, mPositionMs, false);
+                        }
+                        frameCount = 0;
+                    }
+                }
+            }
+
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "===> PreviewThread.run exit");
+            }
+        }
+
+        /**
+         * Stop the preview
+         *
+         * @return The stop position
+         */
+        public long stopPreview() {
+            mRun = false;
+            try {
+                join();
+            } catch (InterruptedException ex) {
+            }
+            return mPositionMs;
+        }
+    };
+
+    /**
+     * Constructor
+     *
+     * @param projectPath
+     */
+    public VideoEditorTestImpl(String projectPath) throws IOException {
+        mProjectPath = projectPath;
+        final File projectXml = new File(projectPath, PROJECT_FILENAME);
+        if (projectXml.exists()) {
+            try {
+                load();
+            } catch (Exception ex) {
+                throw new IOException(ex);
+            }
+        } else {
+            mAspectRatio = MediaProperties.ASPECT_RATIO_16_9;
+            mDurationMs = 0;
+        }
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public String getPath() {
+        return mProjectPath;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized void addMediaItem(MediaItem mediaItem) {
+        if (mPreviewThread != null) {
+            throw new IllegalStateException("Previewing is in progress");
+        }
+
+        if (mMediaItems.contains(mediaItem)) {
+            throw new IllegalArgumentException("Media item already exists: " + mediaItem.getId());
+        }
+
+        // Invalidate the end transition if necessary
+        final int mediaItemsCount = mMediaItems.size();
+        if ( mediaItemsCount > 0) {
+            removeTransitionAfter(mediaItemsCount - 1);
+        }
+
+        // Add the new media item
+        mMediaItems.add(mediaItem);
+
+        computeTimelineDuration();
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized void insertMediaItem(MediaItem mediaItem, String afterMediaItemId) {
+        if (mPreviewThread != null) {
+            throw new IllegalStateException("Previewing is in progress");
+        }
+
+        if (mMediaItems.contains(mediaItem)) {
+            throw new IllegalArgumentException("Media item already exists: " + mediaItem.getId());
+        }
+
+        if (afterMediaItemId == null) {
+            if (mMediaItems.size() > 0) {
+                // Invalidate the transition at the beginning of the timeline
+                removeTransitionBefore(0);
+            }
+            mMediaItems.add(0, mediaItem);
+            computeTimelineDuration();
+        } else {
+            final int mediaItemCount = mMediaItems.size();
+            for (int i = 0; i < mediaItemCount; i++) {
+                final MediaItem mi = mMediaItems.get(i);
+                if (mi.getId().equals(afterMediaItemId)) {
+                    // Invalidate the transition at this position
+                    removeTransitionAfter(i);
+                    // Insert the new media item
+                    mMediaItems.add(i + 1, mediaItem);
+                    computeTimelineDuration();
+                    return;
+                }
+            }
+            throw new IllegalArgumentException("MediaItem not found: " + afterMediaItemId);
+        }
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized void moveMediaItem(String mediaItemId, String afterMediaItemId) {
+        if (mPreviewThread != null) {
+            throw new IllegalStateException("Previewing is in progress");
+        }
+
+        final MediaItem moveMediaItem = removeMediaItem(mediaItemId);
+        if (moveMediaItem == null) {
+            throw new IllegalArgumentException("Target MediaItem not found: " + mediaItemId);
+        }
+
+        if (afterMediaItemId == null) {
+            if (mMediaItems.size() > 0) {
+                // Invalidate adjacent transitions at the insertion point
+                removeTransitionBefore(0);
+
+                // Insert the media item at the new position
+                mMediaItems.add(0, moveMediaItem);
+                computeTimelineDuration();
+            } else {
+                throw new IllegalStateException("Cannot move media item (it is the only item)");
+            }
+        } else {
+            final int mediaItemCount = mMediaItems.size();
+            for (int i = 0; i < mediaItemCount; i++) {
+                final MediaItem mi = mMediaItems.get(i);
+                if (mi.getId().equals(afterMediaItemId)) {
+                    // Invalidate adjacent transitions at the insertion point
+                    removeTransitionAfter(i);
+                    // Insert the media item at the new position
+                    mMediaItems.add(i + 1, moveMediaItem);
+                    computeTimelineDuration();
+                    return;
+                }
+            }
+
+            throw new IllegalArgumentException("MediaItem not found: " + afterMediaItemId);
+        }
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized MediaItem removeMediaItem(String mediaItemId) {
+        if (mPreviewThread != null) {
+            throw new IllegalStateException("Previewing is in progress");
+        }
+
+        final MediaItem mediaItem = getMediaItem(mediaItemId);
+        if (mediaItem != null) {
+            // Remove the media item
+            mMediaItems.remove(mediaItem);
+            // Remove the adjacent transitions
+            removeAdjacentTransitions(mediaItem);
+            computeTimelineDuration();
+        }
+
+        return mediaItem;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized MediaItem getMediaItem(String mediaItemId) {
+        for (MediaItem mediaItem : mMediaItems) {
+            if (mediaItem.getId().equals(mediaItemId)) {
+                return mediaItem;
+            }
+        }
+
+        return null;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized List<MediaItem> getAllMediaItems() {
+        return mMediaItems;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized void removeAllMediaItems() {
+        mMediaItems.clear();
+
+        // Invalidate all transitions
+        for (Transition transition : mTransitions) {
+            transition.invalidate();
+        }
+        mTransitions.clear();
+
+        mDurationMs = 0;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized void addTransition(Transition transition) {
+        mTransitions.add(transition);
+
+        final MediaItem beforeMediaItem = transition.getBeforeMediaItem();
+        final MediaItem afterMediaItem = transition.getAfterMediaItem();
+
+        // Cross reference the transitions
+        if (afterMediaItem != null) {
+            // If a transition already exists at the specified position then
+            // invalidate it.
+            if (afterMediaItem.getEndTransition() != null) {
+                afterMediaItem.getEndTransition().invalidate();
+            }
+            afterMediaItem.setEndTransition(transition);
+        }
+
+        if (beforeMediaItem != null) {
+            // If a transition already exists at the specified position then
+            // invalidate it.
+            if (beforeMediaItem.getBeginTransition() != null) {
+                beforeMediaItem.getBeginTransition().invalidate();
+            }
+            beforeMediaItem.setBeginTransition(transition);
+        }
+
+        computeTimelineDuration();
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized Transition removeTransition(String transitionId) {
+        if (mPreviewThread != null) {
+            throw new IllegalStateException("Previewing is in progress");
+        }
+
+        final Transition transition = getTransition(transitionId);
+        if (transition == null) {
+            throw new IllegalStateException("Transition not found: " + transitionId);
+        }
+
+        // Remove the transition references
+        final MediaItem afterMediaItem = transition.getAfterMediaItem();
+        if (afterMediaItem != null) {
+            afterMediaItem.setEndTransition(null);
+        }
+
+        final MediaItem beforeMediaItem = transition.getBeforeMediaItem();
+        if (beforeMediaItem != null) {
+            beforeMediaItem.setBeginTransition(null);
+        }
+
+        mTransitions.remove(transition);
+        transition.invalidate();
+        computeTimelineDuration();
+
+        return transition;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public List<Transition> getAllTransitions() {
+        return mTransitions;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public Transition getTransition(String transitionId) {
+        for (Transition transition : mTransitions) {
+            if (transition.getId().equals(transitionId)) {
+                return transition;
+            }
+        }
+
+        return null;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized void addAudioTrack(AudioTrack audioTrack) {
+        if (mPreviewThread != null) {
+            throw new IllegalStateException("Previewing is in progress");
+        }
+
+        mAudioTracks.add(audioTrack);
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized void insertAudioTrack(AudioTrack audioTrack, String afterAudioTrackId) {
+        if (mPreviewThread != null) {
+            throw new IllegalStateException("Previewing is in progress");
+        }
+
+        if (afterAudioTrackId == null) {
+            mAudioTracks.add(0, audioTrack);
+        } else {
+            final int audioTrackCount = mAudioTracks.size();
+            for (int i = 0; i < audioTrackCount; i++) {
+                AudioTrack at = mAudioTracks.get(i);
+                if (at.getId().equals(afterAudioTrackId)) {
+                    mAudioTracks.add(i + 1, audioTrack);
+                    return;
+                }
+            }
+
+            throw new IllegalArgumentException("AudioTrack not found: " + afterAudioTrackId);
+        }
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized void moveAudioTrack(String audioTrackId, String afterAudioTrackId) {
+        throw new IllegalStateException("Not supported");
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized AudioTrack removeAudioTrack(String audioTrackId) {
+        if (mPreviewThread != null) {
+            throw new IllegalStateException("Previewing is in progress");
+        }
+
+        final AudioTrack audioTrack = getAudioTrack(audioTrackId);
+        if (audioTrack != null) {
+            mAudioTracks.remove(audioTrack);
+        }
+
+        return audioTrack;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public AudioTrack getAudioTrack(String audioTrackId) {
+        for (AudioTrack at : mAudioTracks) {
+            if (at.getId().equals(audioTrackId)) {
+                return at;
+            }
+        }
+
+        return null;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public List<AudioTrack> getAllAudioTracks() {
+        return mAudioTracks;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public void save() throws IOException {
+        final XmlSerializer serializer = Xml.newSerializer();
+        final StringWriter writer = new StringWriter();
+        serializer.setOutput(writer);
+        serializer.startDocument("UTF-8", true);
+        serializer.startTag("", TAG_PROJECT);
+        serializer.attribute("", ATTR_ASPECT_RATIO, Integer.toString(mAspectRatio));
+
+        serializer.startTag("", TAG_MEDIA_ITEMS);
+        for (MediaItem mediaItem : mMediaItems) {
+            serializer.startTag("", TAG_MEDIA_ITEM);
+            serializer.attribute("", ATTR_ID, mediaItem.getId());
+            serializer.attribute("", ATTR_TYPE, mediaItem.getClass().getSimpleName());
+            serializer.attribute("", ATTR_FILENAME, mediaItem.getFilename());
+            serializer.attribute("", ATTR_RENDERING_MODE, Integer.toString(
+                    mediaItem.getRenderingMode()));
+            if (mediaItem instanceof MediaVideoItem) {
+                final MediaVideoItem mvi = (MediaVideoItem)mediaItem;
+                serializer
+                        .attribute("", ATTR_BEGIN_TIME, Long.toString(mvi.getBoundaryBeginTime()));
+                serializer.attribute("", ATTR_END_TIME, Long.toString(mvi.getBoundaryEndTime()));
+                serializer.attribute("", ATTR_VOLUME, Integer.toString(mvi.getVolume()));
+                serializer.attribute("", ATTR_MUTED, Boolean.toString(mvi.isMuted()));
+                if (mvi.getAudioWaveformFilename() != null) {
+                    serializer.attribute("", ATTR_AUDIO_WAVEFORM_FILENAME,
+                            mvi.getAudioWaveformFilename());
+                }
+            } else if (mediaItem instanceof MediaImageItem) {
+                serializer.attribute("", ATTR_DURATION,
+                        Long.toString(mediaItem.getTimelineDuration()));
+            }
+
+            final List<Overlay> overlays = mediaItem.getAllOverlays();
+            if (overlays.size() > 0) {
+                serializer.startTag("", TAG_OVERLAYS);
+                for (Overlay overlay : overlays) {
+                    serializer.startTag("", TAG_OVERLAY);
+                    serializer.attribute("", ATTR_ID, overlay.getId());
+                    serializer.attribute("", ATTR_TYPE, overlay.getClass().getSimpleName());
+                    serializer.attribute("", ATTR_BEGIN_TIME,
+                            Long.toString(overlay.getStartTime()));
+                    serializer.attribute("", ATTR_DURATION, Long.toString(overlay.getDuration()));
+                    if (overlay instanceof OverlayFrame) {
+                        final OverlayFrame overlayFrame = (OverlayFrame)overlay;
+                        overlayFrame.save(getPath());
+                        if (overlayFrame.getFilename() != null) {
+                            serializer.attribute("", ATTR_FILENAME, overlayFrame.getFilename());
+                        }
+                    }
+
+                    // Save the user attributes
+                    serializer.startTag("", TAG_OVERLAY_USER_ATTRIBUTES);
+                    final Map<String, String> userAttributes = overlay.getUserAttributes();
+                    for (String name : userAttributes.keySet()) {
+                        final String value = userAttributes.get(name);
+                        if (value != null) {
+                            serializer.attribute("", name, value);
+                        }
+                    }
+                    serializer.endTag("", TAG_OVERLAY_USER_ATTRIBUTES);
+
+                    serializer.endTag("", TAG_OVERLAY);
+                }
+                serializer.endTag("", TAG_OVERLAYS);
+            }
+
+            final List<Effect> effects = mediaItem.getAllEffects();
+            if (effects.size() > 0) {
+                serializer.startTag("", TAG_EFFECTS);
+                for (Effect effect : effects) {
+                    serializer.startTag("", TAG_EFFECT);
+                    serializer.attribute("", ATTR_ID, effect.getId());
+                    serializer.attribute("", ATTR_TYPE, effect.getClass().getSimpleName());
+                    serializer.attribute("", ATTR_BEGIN_TIME,
+                            Long.toString(effect.getStartTime()));
+                    serializer.attribute("", ATTR_DURATION, Long.toString(effect.getDuration()));
+                    if (effect instanceof EffectColor) {
+                        final EffectColor colorEffect = (EffectColor)effect;
+                        serializer.attribute("", ATTR_COLOR_EFFECT_TYPE,
+                                Integer.toString(colorEffect.getType()));
+                        if (colorEffect.getType() == EffectColor.TYPE_COLOR ||
+                                colorEffect.getType() == EffectColor.TYPE_GRADIENT) {
+                            serializer.attribute("", ATTR_COLOR_EFFECT_VALUE,
+                                    Integer.toString(colorEffect.getColor()));
+                        }
+                    } else if (effect instanceof EffectKenBurns) {
+                        final Rect startRect = ((EffectKenBurns)effect).getStartRect();
+                        serializer.attribute("", ATTR_START_RECT_L,
+                                Integer.toString(startRect.left));
+                        serializer.attribute("", ATTR_START_RECT_T,
+                                Integer.toString(startRect.top));
+                        serializer.attribute("", ATTR_START_RECT_R,
+                                Integer.toString(startRect.right));
+                        serializer.attribute("", ATTR_START_RECT_B,
+                                Integer.toString(startRect.bottom));
+
+                        final Rect endRect = ((EffectKenBurns)effect).getEndRect();
+                        serializer.attribute("", ATTR_END_RECT_L, Integer.toString(endRect.left));
+                        serializer.attribute("", ATTR_END_RECT_T, Integer.toString(endRect.top));
+                        serializer.attribute("", ATTR_END_RECT_R, Integer.toString(endRect.right));
+                        serializer.attribute("", ATTR_END_RECT_B,
+                                Integer.toString(endRect.bottom));
+                    }
+
+                    serializer.endTag("", TAG_EFFECT);
+                }
+                serializer.endTag("", TAG_EFFECTS);
+            }
+
+            serializer.endTag("", TAG_MEDIA_ITEM);
+        }
+        serializer.endTag("", TAG_MEDIA_ITEMS);
+
+        serializer.startTag("", TAG_TRANSITIONS);
+
+        for (Transition transition : mTransitions) {
+            serializer.startTag("", TAG_TRANSITION);
+            serializer.attribute("", ATTR_ID, transition.getId());
+            serializer.attribute("", ATTR_TYPE, transition.getClass().getSimpleName());
+            serializer.attribute("", ATTR_DURATION, Long.toString(transition.getDuration()));
+            serializer.attribute("", ATTR_BEHAVIOR, Integer.toString(transition.getBehavior()));
+            final MediaItem afterMediaItem = transition.getAfterMediaItem();
+            if (afterMediaItem != null) {
+                serializer.attribute("", ATTR_AFTER_MEDIA_ITEM_ID, afterMediaItem.getId());
+            }
+
+            final MediaItem beforeMediaItem = transition.getBeforeMediaItem();
+            if (beforeMediaItem != null) {
+                serializer.attribute("", ATTR_BEFORE_MEDIA_ITEM_ID, beforeMediaItem.getId());
+            }
+
+            if (transition instanceof TransitionSliding) {
+                serializer.attribute("", ATTR_DIRECTION,
+                        Integer.toString(((TransitionSliding)transition).getDirection()));
+            } else if (transition instanceof TransitionAlpha) {
+                TransitionAlpha ta = (TransitionAlpha)transition;
+                serializer.attribute("", ATTR_BLENDING, Integer.toString(ta.getBlendingPercent()));
+                serializer.attribute("", ATTR_INVERT, Boolean.toString(ta.isInvert()));
+                if (ta.getMaskFilename() != null) {
+                    serializer.attribute("", ATTR_MASK, ta.getMaskFilename());
+                }
+            }
+            serializer.endTag("", TAG_TRANSITION);
+        }
+        serializer.endTag("", TAG_TRANSITIONS);
+
+        serializer.startTag("", TAG_AUDIO_TRACKS);
+        for (AudioTrack at : mAudioTracks) {
+            serializer.startTag("", TAG_AUDIO_TRACK);
+            serializer.attribute("", ATTR_ID, at.getId());
+            serializer.attribute("", ATTR_FILENAME, at.getFilename());
+            serializer.attribute("", ATTR_START_TIME, Long.toString(at.getStartTime()));
+            serializer.attribute("", ATTR_BEGIN_TIME, Long.toString(at.getBoundaryBeginTime()));
+            serializer.attribute("", ATTR_END_TIME, Long.toString(at.getBoundaryEndTime()));
+            serializer.attribute("", ATTR_VOLUME, Integer.toString(at.getVolume()));
+            serializer.attribute("", ATTR_DUCK_ENABLED, Boolean.toString(at.isDuckingEnabled()));
+            serializer.attribute("", ATTR_DUCKED_TRACK_VOLUME, Integer.toString(at.getDuckedTrackVolume()));
+            serializer.attribute("", ATTR_DUCK_THRESHOLD, Integer.toString(at.getDuckingThreshhold()));
+            serializer.attribute("", ATTR_MUTED, Boolean.toString(at.isMuted()));
+            serializer.attribute("", ATTR_LOOP, Boolean.toString(at.isLooping()));
+            if (at.getAudioWaveformFilename() != null) {
+                serializer.attribute("", ATTR_AUDIO_WAVEFORM_FILENAME,
+                at.getAudioWaveformFilename());
+            }
+
+            serializer.endTag("", TAG_AUDIO_TRACK);
+        }
+        serializer.endTag("", TAG_AUDIO_TRACKS);
+
+        serializer.endTag("", TAG_PROJECT);
+        serializer.endDocument();
+
+        // Save the metadata XML file
+        final FileOutputStream out = new FileOutputStream(new File(getPath(), PROJECT_FILENAME));
+        out.write(writer.toString().getBytes());
+        out.flush();
+        out.close();
+    }
+
+    /**
+     * Load the project form XML
+     */
+    private void load() throws FileNotFoundException, XmlPullParserException, IOException {
+        final File file = new File(mProjectPath, PROJECT_FILENAME);
+        // Load the metadata
+        final XmlPullParser parser = Xml.newPullParser();
+        final FileInputStream fis = new FileInputStream(file);
+        parser.setInput(fis, "UTF-8");
+        int eventType = parser.getEventType();
+        String name;
+        MediaItem currentMediaItem = null;
+        Overlay currentOverlay = null;
+        while (eventType != XmlPullParser.END_DOCUMENT) {
+            switch (eventType) {
+                case XmlPullParser.START_TAG: {
+                    name = parser.getName();
+                    if (TAG_PROJECT.equals(name)) {
+                        mAspectRatio = Integer.parseInt(parser.getAttributeValue("",
+                                ATTR_ASPECT_RATIO));
+                    } else if (TAG_MEDIA_ITEM.equals(name)) {
+                        final String mediaItemId = parser.getAttributeValue("", ATTR_ID);
+                        final String type = parser.getAttributeValue("", ATTR_TYPE);
+                        final String filename = parser.getAttributeValue("", ATTR_FILENAME);
+                        final int renderingMode = Integer.parseInt(parser.getAttributeValue("",
+                                ATTR_RENDERING_MODE));
+
+                        if (MediaImageItem.class.getSimpleName().equals(type)) {
+                            final long durationMs = Long.parseLong(parser.getAttributeValue("",
+                                    ATTR_DURATION));
+                            currentMediaItem = new MediaImageItem(this, mediaItemId, filename,
+                                    durationMs, renderingMode);
+                        } else if (MediaVideoItem.class.getSimpleName().equals(type)) {
+                            final long beginMs = Long.parseLong(parser.getAttributeValue("",
+                                    ATTR_BEGIN_TIME));
+                            final long endMs = Long.parseLong(parser.getAttributeValue("",
+                                    ATTR_END_TIME));
+                            final int volume = Integer.parseInt(parser.getAttributeValue("",
+                                    ATTR_VOLUME));
+                            final boolean muted = Boolean.parseBoolean(parser.getAttributeValue("",
+                                    ATTR_MUTED));
+                            final String audioWaveformFilename = parser.getAttributeValue("",
+                                    ATTR_AUDIO_WAVEFORM_FILENAME);
+                            currentMediaItem = new MediaVideoItem(this, mediaItemId, filename,
+                                    renderingMode, beginMs, endMs, volume, muted,
+                                    audioWaveformFilename);
+
+                            final long beginTimeMs = Long.parseLong(parser.getAttributeValue("",
+                                    ATTR_BEGIN_TIME));
+                            final long endTimeMs = Long.parseLong(parser.getAttributeValue("",
+                                    ATTR_END_TIME));
+                            ((MediaVideoItem)currentMediaItem).setExtractBoundaries(beginTimeMs,
+                                    endTimeMs);
+
+                            final int volumePercent = Integer.parseInt(parser.getAttributeValue("",
+                                    ATTR_VOLUME));
+                            ((MediaVideoItem)currentMediaItem).setVolume(volumePercent);
+                        } else {
+                            Log.e(TAG, "Unknown media item type: " + type);
+                            currentMediaItem = null;
+                        }
+
+                        if (currentMediaItem != null) {
+                            mMediaItems.add(currentMediaItem);
+                        }
+                    } else if (TAG_TRANSITION.equals(name)) {
+                        final Transition transition = parseTransition(parser);
+                        if (transition != null) {
+                            mTransitions.add(transition);
+                        }
+                    } else if (TAG_OVERLAY.equals(name)) {
+                        if (currentMediaItem != null) {
+                            currentOverlay = parseOverlay(parser, currentMediaItem);
+                            if (currentOverlay != null) {
+                                currentMediaItem.addOverlay(currentOverlay);
+                            }
+                        }
+                    } else if (TAG_OVERLAY_USER_ATTRIBUTES.equals(name)) {
+                        if (currentOverlay != null) {
+                            final int attributesCount = parser.getAttributeCount();
+                            for (int i = 0; i < attributesCount; i++) {
+                                currentOverlay.setUserAttribute(parser.getAttributeName(i),
+                                        parser.getAttributeValue(i));
+                            }
+                        }
+                    } else if (TAG_EFFECT.equals(name)) {
+                        if (currentMediaItem != null) {
+                            final Effect effect = parseEffect(parser, currentMediaItem);
+                            if (effect != null) {
+                                currentMediaItem.addEffect(effect);
+                            }
+                        }
+                    } else if (TAG_AUDIO_TRACK.equals(name)) {
+                        final AudioTrack audioTrack = parseAudioTrack(parser);
+                        if (audioTrack != null) {
+                            addAudioTrack(audioTrack);
+                        }
+                    }
+                    break;
+                }
+
+                case XmlPullParser.END_TAG: {
+                    name = parser.getName();
+                    if (TAG_MEDIA_ITEM.equals(name)) {
+                        currentMediaItem = null;
+                    } else if (TAG_OVERLAY.equals(name)) {
+                        currentOverlay = null;
+                    }
+                    break;
+                }
+
+                default: {
+                    break;
+                }
+            }
+            eventType = parser.next();
+        }
+
+        fis.close();
+        computeTimelineDuration();
+    }
+
+    /**
+     * Parse the transition
+     *
+     * @param parser The parser
+     * @return The transition
+     */
+    private Transition parseTransition(XmlPullParser parser) {
+        final String transitionId = parser.getAttributeValue("", ATTR_ID);
+        final String type = parser.getAttributeValue("", ATTR_TYPE);
+        final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION));
+        final int behavior = Integer.parseInt(parser.getAttributeValue("", ATTR_BEHAVIOR));
+
+        final String beforeMediaItemId = parser.getAttributeValue("", ATTR_BEFORE_MEDIA_ITEM_ID);
+        final MediaItem beforeMediaItem;
+        if (beforeMediaItemId != null) {
+            beforeMediaItem = getMediaItem(beforeMediaItemId);
+        } else {
+            beforeMediaItem = null;
+        }
+
+        final String afterMediaItemId = parser.getAttributeValue("", ATTR_AFTER_MEDIA_ITEM_ID);
+        final MediaItem afterMediaItem;
+        if (afterMediaItemId != null) {
+            afterMediaItem = getMediaItem(afterMediaItemId);
+        } else {
+            afterMediaItem = null;
+        }
+
+        final Transition transition;
+        if (TransitionAlpha.class.getSimpleName().equals(type)) {
+            final int blending = Integer.parseInt(parser.getAttributeValue("", ATTR_BLENDING));
+            final String maskFilename = parser.getAttributeValue("", ATTR_MASK);
+            final boolean invert = Boolean.getBoolean(parser.getAttributeValue("", ATTR_INVERT));
+            transition = new TransitionAlpha(transitionId, afterMediaItem, beforeMediaItem,
+                    durationMs, behavior, maskFilename, blending, invert);
+        } else if (TransitionCrossfade.class.getSimpleName().equals(type)) {
+            transition = new TransitionCrossfade(transitionId, afterMediaItem, beforeMediaItem,
+                    durationMs, behavior);
+        } else if (TransitionSliding.class.getSimpleName().equals(type)) {
+            final int direction = Integer.parseInt(parser.getAttributeValue("", ATTR_DIRECTION));
+            transition = new TransitionSliding(transitionId, afterMediaItem, beforeMediaItem,
+                    durationMs, behavior, direction);
+        } else if (TransitionFadeBlack.class.getSimpleName().equals(type)) {
+            transition = new TransitionFadeBlack(transitionId, afterMediaItem, beforeMediaItem,
+                    durationMs, behavior);
+        } else {
+            transition = null;
+        }
+
+        if (beforeMediaItem != null) {
+            beforeMediaItem.setBeginTransition(transition);
+        }
+
+        if (afterMediaItem != null) {
+            afterMediaItem.setEndTransition(transition);
+        }
+
+        return transition;
+    }
+
+    /**
+     * Parse the overlay
+     *
+     * @param parser The parser
+     * @param mediaItem The media item owner
+     *
+     * @return The overlay
+     */
+    private Overlay parseOverlay(XmlPullParser parser, MediaItem mediaItem) {
+        final String overlayId = parser.getAttributeValue("", ATTR_ID);
+        final String type = parser.getAttributeValue("", ATTR_TYPE);
+        final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION));
+        final long startTimeMs = Long.parseLong(parser.getAttributeValue("", ATTR_BEGIN_TIME));
+
+        final Overlay overlay;
+        if (OverlayFrame.class.getSimpleName().equals(type)) {
+            final String filename = parser.getAttributeValue("", ATTR_FILENAME);
+            overlay = new OverlayFrame(mediaItem, overlayId, filename, startTimeMs, durationMs);
+        } else {
+            overlay = null;
+        }
+
+        return overlay;
+    }
+
+    /**
+     * Parse the effect
+     *
+     * @param parser The parser
+     * @param mediaItem The media item owner
+     *
+     * @return The effect
+     */
+    private Effect parseEffect(XmlPullParser parser, MediaItem mediaItem) {
+        final String effectId = parser.getAttributeValue("", ATTR_ID);
+        final String type = parser.getAttributeValue("", ATTR_TYPE);
+        final long durationMs = Long.parseLong(parser.getAttributeValue("", ATTR_DURATION));
+        final long startTimeMs = Long.parseLong(parser.getAttributeValue("", ATTR_BEGIN_TIME));
+
+        final Effect effect;
+        if (EffectColor.class.getSimpleName().equals(type)) {
+            final int colorEffectType =
+                Integer.parseInt(parser.getAttributeValue("", ATTR_COLOR_EFFECT_TYPE));
+            final int color;
+            if (colorEffectType == EffectColor.TYPE_COLOR
+                    || colorEffectType == EffectColor.TYPE_GRADIENT) {
+                color = Integer.parseInt(parser.getAttributeValue("", ATTR_COLOR_EFFECT_VALUE));
+            } else {
+                color = 0;
+            }
+            effect = new EffectColor(mediaItem, effectId, startTimeMs, durationMs,
+                    colorEffectType, color);
+        } else if (EffectKenBurns.class.getSimpleName().equals(type)) {
+            final Rect startRect = new Rect(
+                    Integer.parseInt(parser.getAttributeValue("", ATTR_START_RECT_L)),
+                    Integer.parseInt(parser.getAttributeValue("", ATTR_START_RECT_T)),
+                    Integer.parseInt(parser.getAttributeValue("", ATTR_START_RECT_R)),
+                    Integer.parseInt(parser.getAttributeValue("", ATTR_START_RECT_B)));
+            final Rect endRect = new Rect(
+                    Integer.parseInt(parser.getAttributeValue("", ATTR_END_RECT_L)),
+                    Integer.parseInt(parser.getAttributeValue("", ATTR_END_RECT_T)),
+                    Integer.parseInt(parser.getAttributeValue("", ATTR_END_RECT_R)),
+                    Integer.parseInt(parser.getAttributeValue("", ATTR_END_RECT_B)));
+            effect = new EffectKenBurns(mediaItem, effectId, startRect, endRect, startTimeMs,
+                    durationMs);
+        } else {
+            effect = null;
+        }
+
+        return effect;
+    }
+
+    /**
+     * Parse the audio track
+     *
+     * @param parser The parser
+     *
+     * @return The audio track
+     */
+    private AudioTrack parseAudioTrack(XmlPullParser parser) {
+        final String audioTrackId = parser.getAttributeValue("", ATTR_ID);
+        final String filename = parser.getAttributeValue("", ATTR_FILENAME);
+        final long startTimeMs = Long.parseLong(parser.getAttributeValue("", ATTR_START_TIME));
+        final long beginMs = Long.parseLong(parser.getAttributeValue("", ATTR_BEGIN_TIME));
+        final long endMs = Long.parseLong(parser.getAttributeValue("", ATTR_END_TIME));
+        final int volume = Integer.parseInt(parser.getAttributeValue("", ATTR_VOLUME));
+        final boolean muted = Boolean.parseBoolean(parser.getAttributeValue("", ATTR_MUTED));
+        final boolean loop = Boolean.parseBoolean(parser.getAttributeValue("", ATTR_LOOP));
+        final boolean duckingEnabled = Boolean.parseBoolean(parser.getAttributeValue("", ATTR_DUCK_ENABLED));
+        final int duckThreshold = Integer.parseInt(parser.getAttributeValue("", ATTR_DUCK_THRESHOLD));
+        final int duckedTrackVolume = Integer.parseInt(parser.getAttributeValue("", ATTR_DUCKED_TRACK_VOLUME));
+        final String waveformFilename = parser.getAttributeValue("", ATTR_AUDIO_WAVEFORM_FILENAME);
+        try {
+            final AudioTrack audioTrack = new AudioTrack(this, audioTrackId, filename, startTimeMs,
+                    beginMs, endMs, loop, volume, muted, duckingEnabled, duckThreshold, duckedTrackVolume, waveformFilename);
+
+            return audioTrack;
+        } catch (IOException ex) {
+            return null;
+        }
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public void cancelExport(String filename) {
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public void export(String filename, int height, int bitrate, ExportProgressListener listener)
+            throws IOException {
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public void export(String filename, int height, int bitrate, int audioCodec, int videoCodec,
+            ExportProgressListener listener) throws IOException {
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public void generatePreview() {
+        // Generate all the needed transitions
+        for (Transition transition : mTransitions) {
+            if (!transition.isGenerated()) {
+                transition.generate();
+            }
+        }
+
+        // This is necessary because the user may had called setDuration on
+        // MediaImageItems
+        computeTimelineDuration();
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public void release() {
+        stopPreview();
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public long getDuration() {
+        // Since MediaImageItem can change duration we need to compute the
+        // duration here
+        computeTimelineDuration();
+        return mDurationMs;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public int getAspectRatio() {
+        return mAspectRatio;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public void setAspectRatio(int aspectRatio) {
+        mAspectRatio = aspectRatio;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public long renderPreviewFrame(SurfaceHolder surfaceHolder, long timeMs) {
+        if (mPreviewThread != null) {
+            throw new IllegalStateException("Previewing is in progress");
+        }
+        return timeMs;
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized void startPreview(SurfaceHolder surfaceHolder, long fromMs, long toMs,
+            boolean loop, int callbackAfterFrameCount, PreviewProgressListener listener) {
+        if (fromMs >= mDurationMs) {
+            return;
+        }
+        mPreviewThread = new PreviewThread(fromMs, toMs, loop, callbackAfterFrameCount, listener);
+        mPreviewThread.start();
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    public synchronized long stopPreview() {
+        final long stopTimeMs;
+        if (mPreviewThread != null) {
+            stopTimeMs = mPreviewThread.stopPreview();
+            mPreviewThread = null;
+        } else {
+            stopTimeMs = 0;
+        }
+        return stopTimeMs;
+    }
+
+    /**
+     * Compute the duration
+     */
+    private void computeTimelineDuration() {
+        mDurationMs = 0;
+        final int mediaItemsCount = mMediaItems.size();
+        for (int i = 0; i < mediaItemsCount; i++) {
+            final MediaItem mediaItem = mMediaItems.get(i);
+            mDurationMs += mediaItem.getTimelineDuration();
+            if (mediaItem.getEndTransition() != null) {
+                if (i < mediaItemsCount - 1) {
+                    mDurationMs -= mediaItem.getEndTransition().getDuration();
+                }
+            }
+        }
+    }
+
+    /**
+     * Remove transitions associated with the specified media item
+     *
+     * @param mediaItem The media item
+     */
+    private void removeAdjacentTransitions(MediaItem mediaItem) {
+        final Transition beginTransition = mediaItem.getBeginTransition();
+        if (beginTransition != null) {
+            if (beginTransition.getAfterMediaItem() != null) {
+                beginTransition.getAfterMediaItem().setEndTransition(null);
+            }
+            beginTransition.invalidate();
+            mTransitions.remove(beginTransition);
+        }
+
+        final Transition endTransition = mediaItem.getEndTransition();
+        if (endTransition != null) {
+            if (endTransition.getBeforeMediaItem() != null) {
+                endTransition.getBeforeMediaItem().setBeginTransition(null);
+            }
+            endTransition.invalidate();
+            mTransitions.remove(endTransition);
+        }
+
+        mediaItem.setBeginTransition(null);
+        mediaItem.setEndTransition(null);
+    }
+
+    /**
+     * Remove the transition before this media item
+     *
+     * @param index The media item index
+     */
+    private void removeTransitionBefore(int index) {
+        final MediaItem mediaItem = mMediaItems.get(index);
+        final Iterator<Transition> it = mTransitions.iterator();
+        while (it.hasNext()) {
+            Transition t = it.next();
+            if (t.getBeforeMediaItem() == mediaItem) {
+                it.remove();
+                t.invalidate();
+                mediaItem.setBeginTransition(null);
+                if (index > 0) {
+                    mMediaItems.get(index - 1).setEndTransition(null);
+                }
+                break;
+            }
+        }
+    }
+
+    /**
+     * Remove the transition after this media item
+     *
+     * @param index The media item index
+     */
+    private void removeTransitionAfter(int index) {
+        final MediaItem mediaItem = mMediaItems.get(index);
+        final Iterator<Transition> it = mTransitions.iterator();
+        while (it.hasNext()) {
+            Transition t = it.next();
+            if (t.getAfterMediaItem() == mediaItem) {
+                it.remove();
+                t.invalidate();
+                mediaItem.setEndTransition(null);
+                // Invalidate the reference in the next media item
+                if (index < mMediaItems.size() - 1) {
+                    mMediaItems.get(index + 1).setBeginTransition(null);
+                }
+                break;
+            }
+        }
+    }
+}
diff --git a/media/java/android/media/videoeditor/WaveformData.java b/media/java/android/media/videoeditor/WaveformData.java
new file mode 100644
index 0000000..b53bd7d
--- /dev/null
+++ b/media/java/android/media/videoeditor/WaveformData.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.videoeditor;
+
+/**
+ * Class which describes the waveform data of an audio track. The gain values
+ * represent the average gain for an audio frame. For audio codecs which do
+ * not operate on a per frame bases (eg. ALAW, ULAW) a reasonable audio frame
+ * duration will be assumed (eg. 50ms).
+ * {@hide}
+ */
+public class WaveformData {
+    // Instance variables
+    private final int mFrameDurationMs;
+    private final int mFramesCount;
+    private final short[] mGains;
+
+    /**
+     * This constructor shall not be used
+     */
+    @SuppressWarnings("unused")
+    private WaveformData() {
+        mFrameDurationMs = 0;
+        mFramesCount = 0;
+        mGains = null;
+    }
+
+    /**
+     * Constructor
+     *
+     * @param audioWaveformFilename The name of the audio waveform file
+     */
+    WaveformData(String audioWaveformFilename) {
+        // TODO: Read these values from the file
+        mFrameDurationMs = 20;
+        mFramesCount = 300000 / mFrameDurationMs;
+        mGains = new short[mFramesCount];
+        for (int i = 0; i < mFramesCount; i++) {
+            mGains[i] = (short)((i * 5) % 256);
+        }
+    }
+
+    /**
+     * @return The duration of a frame in milliseconds
+     */
+    public int getFrameDuration() {
+        return mFrameDurationMs;
+    }
+
+    /**
+     * @return The number of frames within the waveform data
+     */
+    public int getFramesCount() {
+        return mFramesCount;
+    }
+
+    /**
+     * @return The array of frame gains. The size of the array is the frames
+     *  count. The values of the frame gains range from 0 to 256.
+     */
+    public short[] getFrameGains() {
+        return mGains;
+    }
+}
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 70ed608..25d243b 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -8,7 +8,11 @@
     android_media_MediaMetadataRetriever.cpp \
     android_media_ResampleInputStream.cpp \
     android_media_MediaProfiles.cpp \
-    android_media_AmrInputStream.cpp
+    android_media_AmrInputStream.cpp \
+	android_media_MtpClient.cpp \
+	android_media_MtpCursor.cpp \
+	android_media_MtpDatabase.cpp \
+	android_media_MtpServer.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
     libandroid_runtime \
@@ -21,9 +25,12 @@
     libcutils \
     libsurfaceflinger_client \
     libstagefright \
-    libcamera_client
+    libcamera_client \
+	libsqlite
 
-LOCAL_STATIC_LIBRARIES :=
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_STATIC_LIBRARIES := libmtp libusbhost
+endif
 
 LOCAL_C_INCLUDES += \
     external/tremor/Tremor \
@@ -32,6 +39,7 @@
     frameworks/base/media/libstagefright/codecs/amrnb/enc/src \
     frameworks/base/media/libstagefright/codecs/amrnb/common \
     frameworks/base/media/libstagefright/codecs/amrnb/common/include \
+    frameworks/base/media/mtp \
     $(PV_INCLUDES) \
     $(JNI_H_INCLUDE) \
     $(call include-path-for, corecg graphics)
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 49e5e89..997d017 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -680,19 +680,6 @@
     android_media_MediaPlayer_release(env, thiz);
 }
 
-static jint
-android_media_MediaPlayer_native_suspend_resume(
-        JNIEnv *env, jobject thiz, jboolean isSuspend) {
-    LOGV("suspend_resume(%d)", isSuspend);
-    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL ) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
-        return UNKNOWN_ERROR;
-    }
-
-    return isSuspend ? mp->suspend() : mp->resume();
-}
-
 static void android_media_MediaPlayer_set_audio_session_id(JNIEnv *env,  jobject thiz, jint sessionId) {
     LOGV("set_session_id(): %d", sessionId);
     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
@@ -767,7 +754,6 @@
     {"native_init",         "()V",                              (void *)android_media_MediaPlayer_native_init},
     {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},
     {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
-    {"native_suspend_resume", "(Z)I",                           (void *)android_media_MediaPlayer_native_suspend_resume},
     {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer_get_audio_session_id},
     {"setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer_set_audio_session_id},
     {"setAuxEffectSendLevel", "(F)V",                           (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
@@ -788,6 +774,10 @@
 extern int register_android_media_MediaScanner(JNIEnv *env);
 extern int register_android_media_ResampleInputStream(JNIEnv *env);
 extern int register_android_media_MediaProfiles(JNIEnv *env);
+extern int register_android_media_MtpClient(JNIEnv *env);
+extern int register_android_media_MtpCursor(JNIEnv *env);
+extern int register_android_media_MtpDatabase(JNIEnv *env);
+extern int register_android_media_MtpServer(JNIEnv *env);
 extern int register_android_media_AmrInputStream(JNIEnv *env);
 
 jint JNI_OnLoad(JavaVM* vm, void* reserved)
@@ -836,6 +826,26 @@
         goto bail;
     }
 
+    if (register_android_media_MtpClient(env) < 0) {
+        LOGE("ERROR: MtpClient native registration failed");
+        goto bail;
+    }
+
+    if (register_android_media_MtpCursor(env) < 0) {
+        LOGE("ERROR: MtpCursor native registration failed");
+        goto bail;
+    }
+
+    if (register_android_media_MtpDatabase(env) < 0) {
+        LOGE("ERROR: MtpDatabase native registration failed");
+        goto bail;
+    }
+
+    if (register_android_media_MtpServer(env) < 0) {
+        LOGE("ERROR: MtpServer native registration failed");
+        goto bail;
+    }
+
     /* success -- return valid version number */
     result = JNI_VERSION_1_4;
 
diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp
index cce9fd0..08a6de1 100644
--- a/media/jni/android_media_MediaProfiles.cpp
+++ b/media/jni/android_media_MediaProfiles.cpp
@@ -165,7 +165,9 @@
 android_media_MediaProfiles_native_get_camcorder_profile(JNIEnv *env, jobject thiz, jint id, jint quality)
 {
     LOGV("native_get_camcorder_profile: %d %d", id, quality);
-    if (quality != CAMCORDER_QUALITY_HIGH && quality != CAMCORDER_QUALITY_LOW) {
+    if (!((quality >= CAMCORDER_QUALITY_LOW && quality <= CAMCORDER_QUALITY_1080P) ||
+                (quality >= CAMCORDER_QUALITY_TIME_LAPSE_LOW &&
+                quality <= CAMCORDER_QUALITY_TIME_LAPSE_1080P))) {
         jniThrowException(env, "java/lang/RuntimeException", "Unknown camcorder profile quality");
         return NULL;
     }
@@ -210,6 +212,20 @@
                           audioChannels);
 }
 
+static jboolean
+android_media_MediaProfiles_native_has_camcorder_profile(JNIEnv *env, jobject thiz, jint id, jint quality)
+{
+    LOGV("native_has_camcorder_profile: %d %d", id, quality);
+    if (!((quality >= CAMCORDER_QUALITY_LOW && quality <= CAMCORDER_QUALITY_1080P) ||
+                (quality >= CAMCORDER_QUALITY_TIME_LAPSE_LOW &&
+                quality <= CAMCORDER_QUALITY_TIME_LAPSE_1080P))) {
+        return false;
+    }
+
+    camcorder_quality q = static_cast<camcorder_quality>(quality);
+    return sProfiles->hasCamcorderProfile(id, q);
+}
+
 static jint
 android_media_MediaProfiles_native_get_num_video_decoders(JNIEnv *env, jobject thiz)
 {
@@ -289,6 +305,8 @@
     {"native_init",                            "()V",                    (void *)android_media_MediaProfiles_native_init},
     {"native_get_camcorder_profile",           "(II)Landroid/media/CamcorderProfile;",
                                                                          (void *)android_media_MediaProfiles_native_get_camcorder_profile},
+    {"native_has_camcorder_profile",           "(II)Z",
+                                                                         (void *)android_media_MediaProfiles_native_has_camcorder_profile},
 };
 
 static JNINativeMethod gMethodsForDecoderCapabilitiesClass[] = {
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index efa0813..82b4ac1 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -260,6 +260,20 @@
 }
 
 static void
+android_media_MediaRecorder_setOutputFileAuxFD(JNIEnv *env, jobject thiz, jobject fileDescriptor)
+{
+    LOGV("setOutputFile");
+    if (fileDescriptor == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+    int fd = getParcelFileDescriptorFD(env, fileDescriptor);
+    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+    status_t opStatus = mr->setOutputFileAuxiliary(fd);
+    process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed.");
+}
+
+static void
 android_media_MediaRecorder_setVideoSize(JNIEnv *env, jobject thiz, jint width, jint height)
 {
     LOGV("setVideoSize(%d, %d)", width, height);
@@ -466,6 +480,7 @@
     {"setAudioEncoder",      "(I)V",                            (void *)android_media_MediaRecorder_setAudioEncoder},
     {"setParameter",         "(Ljava/lang/String;)V",           (void *)android_media_MediaRecorder_setParameter},
     {"_setOutputFile",       "(Ljava/io/FileDescriptor;JJ)V",   (void *)android_media_MediaRecorder_setOutputFileFD},
+    {"_setOutputFileAux",    "(Ljava/io/FileDescriptor;)V",     (void *)android_media_MediaRecorder_setOutputFileAuxFD},
     {"setVideoSize",         "(II)V",                           (void *)android_media_MediaRecorder_setVideoSize},
     {"setVideoFrameRate",    "(I)V",                            (void *)android_media_MediaRecorder_setVideoFrameRate},
     {"setMaxDuration",       "(I)V",                            (void *)android_media_MediaRecorder_setMaxDuration},
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index 273f1af..fd0b233 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -146,7 +146,7 @@
 }
 
 static void
-android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jstring extensions, jobject client)
+android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jobject client)
 {
     MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
 
@@ -154,27 +154,16 @@
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
         return;
     }
-    if (extensions == NULL) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return;
-    }
-    
+
     const char *pathStr = env->GetStringUTFChars(path, NULL);
     if (pathStr == NULL) {  // Out of memory
         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
         return;
     }
-    const char *extensionsStr = env->GetStringUTFChars(extensions, NULL);
-    if (extensionsStr == NULL) {  // Out of memory
-        env->ReleaseStringUTFChars(path, pathStr);
-        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
-        return;
-    }
 
     MyMediaScannerClient myClient(env, client);
-    mp->processDirectory(pathStr, extensionsStr, myClient, ExceptionCheck, env);
+    mp->processDirectory(pathStr, myClient, ExceptionCheck, env);
     env->ReleaseStringUTFChars(path, pathStr);
-    env->ReleaseStringUTFChars(extensions, extensionsStr);
 }
 
 static void
@@ -309,9 +298,9 @@
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
-    {"processDirectory",  "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",    
+    {"processDirectory",  "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
                                                         (void *)android_media_MediaScanner_processDirectory},
-    {"processFile",       "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",    
+    {"processFile",       "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
                                                         (void *)android_media_MediaScanner_processFile},
     {"setLocale",         "(Ljava/lang/String;)V",      (void *)android_media_MediaScanner_setLocale},
     {"extractAlbumArt",   "(Ljava/io/FileDescriptor;)[B",     (void *)android_media_MediaScanner_extractAlbumArt},
diff --git a/media/jni/android_media_MtpClient.cpp b/media/jni/android_media_MtpClient.cpp
new file mode 100644
index 0000000..d23185b
--- /dev/null
+++ b/media/jni/android_media_MtpClient.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpClientJNI"
+#include "utils/Log.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include "MtpClient.h"
+#include "MtpDevice.h"
+#include "MtpObjectInfo.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+
+static jmethodID method_deviceAdded;
+static jmethodID method_deviceRemoved;
+static jfieldID field_context;
+
+static struct file_descriptor_offsets_t
+{
+    jclass mClass;
+    jmethodID mConstructor;
+    jfieldID mDescriptor;
+} gFileDescriptorOffsets;
+
+static struct parcel_file_descriptor_offsets_t
+{
+    jclass mClass;
+    jmethodID mConstructor;
+} gParcelFileDescriptorOffsets;
+
+#ifdef HAVE_ANDROID_OS
+
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+    if (env->ExceptionCheck()) {
+        LOGE("An exception was thrown by callback '%s'.", methodName);
+        LOGE_EX(env);
+        env->ExceptionClear();
+    }
+}
+
+class MyClient : public MtpClient {
+private:
+    virtual void    deviceAdded(MtpDevice *device);
+    virtual void    deviceRemoved(MtpDevice *device);
+
+    jobject         mClient;
+    MtpDevice*      mEventDevice;
+
+public:
+                    MyClient(JNIEnv *env, jobject client);
+    void            cleanup(JNIEnv *env);
+};
+
+MtpClient* get_client_from_object(JNIEnv* env, jobject javaClient)
+{
+    return (MtpClient*)env->GetIntField(javaClient, field_context);
+}
+
+
+MyClient::MyClient(JNIEnv *env, jobject client)
+    :   mClient(env->NewGlobalRef(client))
+{
+}
+
+void MyClient::cleanup(JNIEnv *env) {
+    env->DeleteGlobalRef(mClient);
+}
+
+void MyClient::deviceAdded(MtpDevice *device) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    const char* name = device->getDeviceName();
+    LOGD("MyClient::deviceAdded %s\n", name);
+
+    env->CallVoidMethod(mClient, method_deviceAdded, device->getID());
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void MyClient::deviceRemoved(MtpDevice *device) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    const char* name = device->getDeviceName();
+    LOGD("MyClient::deviceRemoved %s\n", name);
+
+    env->CallVoidMethod(mClient, method_deviceRemoved, device->getID());
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+#endif // HAVE_ANDROID_OS
+
+// ----------------------------------------------------------------------------
+
+static void
+android_media_MtpClient_setup(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("setup\n");
+    MyClient* client = new MyClient(env, thiz);
+    client->start();
+    env->SetIntField(thiz, field_context, (int)client);
+#endif
+}
+
+static void
+android_media_MtpClient_finalize(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("finalize\n");
+    MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
+    client->cleanup(env);
+    delete client;
+    env->SetIntField(thiz, field_context, 0);
+#endif
+}
+
+static jboolean
+android_media_MtpClient_start(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("start\n");
+    MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
+    return client->start();
+#else
+    return false;
+#endif
+}
+
+static void
+android_media_MtpClient_stop(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("stop\n");
+    MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
+    client->stop();
+#endif
+}
+
+static jboolean
+android_media_MtpClient_delete_object(JNIEnv *env, jobject thiz,
+        jint device_id, jlong object_id)
+{
+#ifdef HAVE_ANDROID_OS
+    MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
+    MtpDevice* device = client->getDevice(device_id);
+    if (device)
+        return device->deleteObject(object_id);
+    else
+ #endif
+        return NULL;
+}
+
+static jlong
+android_media_MtpClient_get_parent(JNIEnv *env, jobject thiz,
+        jint device_id, jlong object_id)
+{
+#ifdef HAVE_ANDROID_OS
+    MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
+    MtpDevice* device = client->getDevice(device_id);
+    if (device)
+        return device->getParent(object_id);
+    else
+#endif
+        return -1;
+}
+
+static jlong
+android_media_MtpClient_get_storage_id(JNIEnv *env, jobject thiz,
+        jint device_id, jlong object_id)
+{
+ #ifdef HAVE_ANDROID_OS
+    MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
+    MtpDevice* device = client->getDevice(device_id);
+    if (device)
+        return device->getStorageID(object_id);
+    else
+#endif
+        return -1;
+}
+
+static jobject
+android_media_MtpClient_open_file(JNIEnv *env, jobject thiz,
+        jint device_id, jlong object_id)
+{
+#ifdef HAVE_ANDROID_OS
+    MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
+    MtpDevice* device = client->getDevice(device_id);
+    if (!device)
+        return NULL;
+
+    MtpObjectInfo* info = device->getObjectInfo(object_id);
+    if (!info)
+        return NULL;
+    int object_size = info->mCompressedSize;
+    delete info;
+    int fd = device->readObject(object_id, object_size);
+    if (fd < 0)
+        return NULL;
+
+    jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass,
+        gFileDescriptorOffsets.mConstructor);
+    if (fileDescriptor != NULL) {
+        env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, fd);
+    } else {
+        return NULL;
+    }
+    return env->NewObject(gParcelFileDescriptorOffsets.mClass,
+        gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
+#endif
+    return NULL;
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gMethods[] = {
+    {"native_setup",            "()V",  (void *)android_media_MtpClient_setup},
+    {"native_finalize",         "()V",  (void *)android_media_MtpClient_finalize},
+    {"native_start",            "()Z",  (void *)android_media_MtpClient_start},
+    {"native_stop",             "()V",  (void *)android_media_MtpClient_stop},
+    {"native_delete_object",   "(IJ)Z", (void *)android_media_MtpClient_delete_object},
+    {"native_get_parent",      "(IJ)J", (void *)android_media_MtpClient_get_parent},
+    {"native_get_storage_id",  "(IJ)J", (void *)android_media_MtpClient_get_storage_id},
+    {"native_open_file",       "(IJ)Landroid/os/ParcelFileDescriptor;",
+                                        (void *)android_media_MtpClient_open_file},
+};
+
+static const char* const kClassPathName = "android/media/MtpClient";
+
+int register_android_media_MtpClient(JNIEnv *env)
+{
+    jclass clazz;
+
+    LOGD("register_android_media_MtpClient\n");
+
+    clazz = env->FindClass("android/media/MtpClient");
+    if (clazz == NULL) {
+        LOGE("Can't find android/media/MtpClient");
+        return -1;
+    }
+    method_deviceAdded = env->GetMethodID(clazz, "deviceAdded", "(I)V");
+    if (method_deviceAdded == NULL) {
+        LOGE("Can't find deviceAdded");
+        return -1;
+    }
+    method_deviceRemoved = env->GetMethodID(clazz, "deviceRemoved", "(I)V");
+    if (method_deviceRemoved == NULL) {
+        LOGE("Can't find deviceRemoved");
+        return -1;
+    }
+    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+    if (field_context == NULL) {
+        LOGE("Can't find MtpClient.mNativeContext");
+        return -1;
+    }
+
+   clazz = env->FindClass("java/io/FileDescriptor");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+    gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+    gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
+    gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
+    LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
+                 "Unable to find descriptor field in java.io.FileDescriptor");
+
+   clazz = env->FindClass("android/os/ParcelFileDescriptor");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
+    gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
+    gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
+    LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
+                 "Unable to find constructor for android.os.ParcelFileDescriptor");
+
+    return AndroidRuntime::registerNativeMethods(env,
+                "android/media/MtpClient", gMethods, NELEM(gMethods));
+}
diff --git a/media/jni/android_media_MtpCursor.cpp b/media/jni/android_media_MtpCursor.cpp
new file mode 100644
index 0000000..7a0ae8a
--- /dev/null
+++ b/media/jni/android_media_MtpCursor.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpCursorJNI"
+#include "utils/Log.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "binder/CursorWindow.h"
+
+#include "MtpClient.h"
+#include "MtpCursor.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+
+static jfieldID field_context;
+
+// From android_media_MtpClient.cpp
+MtpClient * get_client_from_object(JNIEnv * env, jobject javaClient);
+
+// ----------------------------------------------------------------------------
+
+static bool ExceptionCheck(void* env)
+{
+    return ((JNIEnv *)env)->ExceptionCheck();
+}
+
+static void
+android_media_MtpCursor_setup(JNIEnv *env, jobject thiz, jobject javaClient,
+        jint queryType, jint deviceID, jlong storageID, jlong objectID, jintArray javaColumns)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("android_media_MtpCursor_setup queryType: %d deviceID: %d storageID: %lld objectID: %lld\n",
+                queryType, deviceID, storageID, objectID);
+
+    int* columns = NULL;
+    int columnCount = 0;
+    if (javaColumns) {
+        columns = env->GetIntArrayElements(javaColumns, 0);
+        columnCount = env->GetArrayLength(javaColumns);
+    }
+
+    MtpClient* client = get_client_from_object(env, javaClient);
+    MtpCursor* cursor = new MtpCursor(client, queryType,
+            deviceID, storageID, objectID, columnCount, columns);
+
+    if (columns)
+        env->ReleaseIntArrayElements(javaColumns, columns, 0);
+    env->SetIntField(thiz, field_context, (int)cursor);
+#endif
+}
+
+static void
+android_media_MtpCursor_finalize(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("finalize\n");
+    MtpCursor *cursor = (MtpCursor *)env->GetIntField(thiz, field_context);
+    delete cursor;
+#endif
+}
+
+static jint
+android_media_MtpCursor_fill_window(JNIEnv *env, jobject thiz, jobject javaWindow, jint startPos)
+{
+#ifdef HAVE_ANDROID_OS
+    CursorWindow* window = get_window_from_object(env, javaWindow);
+    if (!window) {
+        LOGE("Invalid CursorWindow");
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "Bad CursorWindow");
+        return 0;
+    }
+    MtpCursor *cursor = (MtpCursor *)env->GetIntField(thiz, field_context);
+
+    return cursor->fillWindow(window, startPos);
+#else
+    return 0;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gMethods[] = {
+    {"native_setup",            "(Landroid/media/MtpClient;IIJJ[I)V",
+                                        (void *)android_media_MtpCursor_setup},
+    {"native_finalize",         "()V",  (void *)android_media_MtpCursor_finalize},
+    {"native_fill_window",      "(Landroid/database/CursorWindow;I)I",
+                                        (void *)android_media_MtpCursor_fill_window},
+
+};
+
+static const char* const kClassPathName = "android/media/MtpCursor";
+
+int register_android_media_MtpCursor(JNIEnv *env)
+{
+    jclass clazz;
+
+    LOGD("register_android_media_MtpCursor\n");
+
+    clazz = env->FindClass("android/media/MtpCursor");
+    if (clazz == NULL) {
+        LOGE("Can't find android/media/MtpCursor");
+        return -1;
+    }
+    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+    if (field_context == NULL) {
+        LOGE("Can't find MtpCursor.mNativeContext");
+        return -1;
+    }
+
+    return AndroidRuntime::registerNativeMethods(env,
+                "android/media/MtpCursor", gMethods, NELEM(gMethods));
+}
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
new file mode 100644
index 0000000..87cb82e
--- /dev/null
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -0,0 +1,1021 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpDatabaseJNI"
+#include "utils/Log.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include "MtpDatabase.h"
+#include "MtpDataPacket.h"
+#include "MtpProperty.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+#include "mtp.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+
+static jmethodID method_beginSendObject;
+static jmethodID method_endSendObject;
+static jmethodID method_getObjectList;
+static jmethodID method_getNumObjects;
+static jmethodID method_getSupportedPlaybackFormats;
+static jmethodID method_getSupportedCaptureFormats;
+static jmethodID method_getSupportedObjectProperties;
+static jmethodID method_getSupportedDeviceProperties;
+static jmethodID method_getObjectProperty;
+static jmethodID method_setObjectProperty;
+static jmethodID method_getDeviceProperty;
+static jmethodID method_setDeviceProperty;
+static jmethodID method_getObjectInfo;
+static jmethodID method_getObjectFilePath;
+static jmethodID method_deleteFile;
+static jmethodID method_getObjectReferences;
+static jmethodID method_setObjectReferences;
+static jmethodID method_sessionStarted;
+static jmethodID method_sessionEnded;
+
+static jfieldID field_context;
+
+MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
+    return (MtpDatabase *)env->GetIntField(database, field_context);
+}
+
+#ifdef HAVE_ANDROID_OS
+// ----------------------------------------------------------------------------
+
+class MyMtpDatabase : public MtpDatabase {
+private:
+    jobject         mDatabase;
+    jintArray       mIntBuffer;
+    jlongArray      mLongBuffer;
+    jcharArray      mStringBuffer;
+
+public:
+                                    MyMtpDatabase(JNIEnv *env, jobject client);
+    virtual                         ~MyMtpDatabase();
+    void                            cleanup(JNIEnv *env);
+
+    virtual MtpObjectHandle         beginSendObject(const char* path,
+                                            MtpObjectFormat format,
+                                            MtpObjectHandle parent,
+                                            MtpStorageID storage,
+                                            uint64_t size,
+                                            time_t modified);
+
+    virtual void                    endSendObject(const char* path,
+                                            MtpObjectHandle handle,
+                                            MtpObjectFormat format,
+                                            bool succeeded);
+
+    virtual MtpObjectHandleList*    getObjectList(MtpStorageID storageID,
+                                    MtpObjectFormat format,
+                                    MtpObjectHandle parent);
+
+    virtual int                     getNumObjects(MtpStorageID storageID,
+                                            MtpObjectFormat format,
+                                            MtpObjectHandle parent);
+
+    // callee should delete[] the results from these
+    // results can be NULL
+    virtual MtpObjectFormatList*    getSupportedPlaybackFormats();
+    virtual MtpObjectFormatList*    getSupportedCaptureFormats();
+    virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format);
+    virtual MtpDevicePropertyList*  getSupportedDeviceProperties();
+
+    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property);
+
+    virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
+                                            MtpDataPacket& packet);
+
+    virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
+                                            MtpString& filePath,
+                                            int64_t& fileLength);
+    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle);
+
+    bool                            getObjectPropertyInfo(MtpObjectProperty property, int& type);
+    bool                            getDevicePropertyInfo(MtpDeviceProperty property, int& type);
+
+    virtual MtpObjectHandleList*    getObjectReferences(MtpObjectHandle handle);
+
+    virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
+                                            MtpObjectHandleList* references);
+
+    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
+                                            MtpObjectFormat format);
+
+    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property);
+
+    virtual void                    sessionStarted();
+
+    virtual void                    sessionEnded();
+};
+
+// ----------------------------------------------------------------------------
+
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+    if (env->ExceptionCheck()) {
+        LOGE("An exception was thrown by callback '%s'.", methodName);
+        LOGE_EX(env);
+        env->ExceptionClear();
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client)
+    :   mDatabase(env->NewGlobalRef(client)),
+        mIntBuffer(NULL),
+        mLongBuffer(NULL),
+        mStringBuffer(NULL)
+{
+    jintArray intArray;
+    jlongArray longArray;
+    jcharArray charArray;
+
+    // create buffers for out arguments
+    // we don't need to be thread-safe so this is OK
+    intArray = env->NewIntArray(3);
+    if (!intArray)
+        goto out_of_memory;
+    mIntBuffer = (jintArray)env->NewGlobalRef(intArray);
+    longArray = env->NewLongArray(2);
+    if (!longArray)
+        goto out_of_memory;
+    mLongBuffer = (jlongArray)env->NewGlobalRef(longArray);
+    charArray = env->NewCharArray(256);
+    if (!charArray)
+        goto out_of_memory;
+    mStringBuffer = (jcharArray)env->NewGlobalRef(charArray);
+    return;
+
+out_of_memory:
+    env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), NULL);
+}
+
+void MyMtpDatabase::cleanup(JNIEnv *env) {
+    env->DeleteGlobalRef(mDatabase);
+    env->DeleteGlobalRef(mIntBuffer);
+    env->DeleteGlobalRef(mLongBuffer);
+    env->DeleteGlobalRef(mStringBuffer);
+}
+
+MyMtpDatabase::~MyMtpDatabase() {
+}
+
+MtpObjectHandle MyMtpDatabase::beginSendObject(const char* path,
+                                            MtpObjectFormat format,
+                                            MtpObjectHandle parent,
+                                            MtpStorageID storage,
+                                            uint64_t size,
+                                            time_t modified) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jstring pathStr = env->NewStringUTF(path);
+    MtpObjectHandle result = env->CallIntMethod(mDatabase, method_beginSendObject,
+            pathStr, (jint)format, (jint)parent, (jint)storage,
+            (jlong)size, (jlong)modified);
+
+    if (pathStr)
+        env->DeleteLocalRef(pathStr);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
+}
+
+void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle,
+                                MtpObjectFormat format, bool succeeded) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jstring pathStr = env->NewStringUTF(path);
+    env->CallVoidMethod(mDatabase, method_endSendObject, pathStr,
+                        (jint)handle, (jint)format, (jboolean)succeeded);
+
+    if (pathStr)
+        env->DeleteLocalRef(pathStr);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
+                                    MtpObjectFormat format,
+                                    MtpObjectHandle parent) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectList,
+                (jint)storageID, (jint)format, (jint)parent);
+    if (!array)
+        return NULL;
+    MtpObjectHandleList* list = new MtpObjectHandleList();
+    jint* handles = env->GetIntArrayElements(array, 0);
+    jsize length = env->GetArrayLength(array);
+    for (int i = 0; i < length; i++)
+        list->push(handles[i]);
+    env->ReleaseIntArrayElements(array, handles, 0);
+    env->DeleteLocalRef(array);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return list;
+}
+
+int MyMtpDatabase::getNumObjects(MtpStorageID storageID,
+                                MtpObjectFormat format,
+                                MtpObjectHandle parent) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    int result = env->CallIntMethod(mDatabase, method_getNumObjects,
+                (jint)storageID, (jint)format, (jint)parent);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
+}
+
+MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+            method_getSupportedPlaybackFormats);
+    if (!array)
+        return NULL;
+    MtpObjectFormatList* list = new MtpObjectFormatList();
+    jint* formats = env->GetIntArrayElements(array, 0);
+    jsize length = env->GetArrayLength(array);
+    for (int i = 0; i < length; i++)
+        list->push(formats[i]);
+    env->ReleaseIntArrayElements(array, formats, 0);
+    env->DeleteLocalRef(array);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return list;
+}
+
+MtpObjectFormatList* MyMtpDatabase::getSupportedCaptureFormats() {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+            method_getSupportedCaptureFormats);
+    if (!array)
+        return NULL;
+    MtpObjectFormatList* list = new MtpObjectFormatList();
+    jint* formats = env->GetIntArrayElements(array, 0);
+    jsize length = env->GetArrayLength(array);
+    for (int i = 0; i < length; i++)
+        list->push(formats[i]);
+    env->ReleaseIntArrayElements(array, formats, 0);
+    env->DeleteLocalRef(array);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return list;
+}
+
+MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+            method_getSupportedObjectProperties, (jint)format);
+    if (!array)
+        return NULL;
+    MtpObjectPropertyList* list = new MtpObjectPropertyList();
+    jint* properties = env->GetIntArrayElements(array, 0);
+    jsize length = env->GetArrayLength(array);
+    for (int i = 0; i < length; i++)
+        list->push(properties[i]);
+    env->ReleaseIntArrayElements(array, properties, 0);
+    env->DeleteLocalRef(array);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return list;
+}
+
+MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+            method_getSupportedDeviceProperties);
+    if (!array)
+        return NULL;
+    MtpDevicePropertyList* list = new MtpDevicePropertyList();
+    jint* properties = env->GetIntArrayElements(array, 0);
+    jsize length = env->GetArrayLength(array);
+    for (int i = 0; i < length; i++)
+        list->push(properties[i]);
+    env->ReleaseIntArrayElements(array, properties, 0);
+    env->DeleteLocalRef(array);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return list;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet) {
+    int         type;
+
+    if (!getObjectPropertyInfo(property, type))
+        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jint result = env->CallIntMethod(mDatabase, method_getObjectProperty,
+                (jint)handle, (jint)property, mLongBuffer, mStringBuffer);
+    if (result != MTP_RESPONSE_OK) {
+        checkAndClearExceptionFromCallback(env, __FUNCTION__);
+        return result;
+    }
+
+    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
+    jlong longValue = longValues[0];
+    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
+
+    // special case date properties, which are strings to MTP
+    // but stored internally as a uint64
+    if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) {
+        char    date[20];
+        formatDateTime(longValue, date, sizeof(date));
+        packet.putString(date);
+        return MTP_RESPONSE_OK;
+    }
+    // release date is stored internally as just the year
+    if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) {
+        char    date[20];
+        snprintf(date, sizeof(date), "%04lld0101T000000", longValue);
+        packet.putString(date);
+        return MTP_RESPONSE_OK;
+    }
+
+    switch (type) {
+        case MTP_TYPE_INT8:
+            packet.putInt8(longValue);
+            break;
+        case MTP_TYPE_UINT8:
+            packet.putUInt8(longValue);
+            break;
+        case MTP_TYPE_INT16:
+            packet.putInt16(longValue);
+            break;
+        case MTP_TYPE_UINT16:
+            packet.putUInt16(longValue);
+            break;
+        case MTP_TYPE_INT32:
+            packet.putInt32(longValue);
+            break;
+        case MTP_TYPE_UINT32:
+            packet.putUInt32(longValue);
+            break;
+        case MTP_TYPE_INT64:
+            packet.putInt64(longValue);
+            break;
+        case MTP_TYPE_UINT64:
+            packet.putUInt64(longValue);
+            break;
+        case MTP_TYPE_INT128:
+            packet.putInt128(longValue);
+            break;
+        case MTP_TYPE_UINT128:
+            packet.putInt128(longValue);
+            break;
+        case MTP_TYPE_STR:
+        {
+            jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
+            packet.putString(str);
+            env->ReleaseCharArrayElements(mStringBuffer, str, 0);
+            break;
+         }
+        default:
+            LOGE("unsupported type in getObjectPropertyValue\n");
+            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+    }
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet) {
+    int         type;
+
+    if (!getObjectPropertyInfo(property, type))
+        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jlong longValue = 0;
+    jstring stringValue = NULL;
+
+    switch (type) {
+        case MTP_TYPE_INT8:
+            longValue = packet.getInt8();
+            break;
+        case MTP_TYPE_UINT8:
+            longValue = packet.getUInt8();
+            break;
+        case MTP_TYPE_INT16:
+            longValue = packet.getInt16();
+            break;
+        case MTP_TYPE_UINT16:
+            longValue = packet.getUInt16();
+            break;
+        case MTP_TYPE_INT32:
+            longValue = packet.getInt32();
+            break;
+        case MTP_TYPE_UINT32:
+            longValue = packet.getUInt32();
+            break;
+        case MTP_TYPE_INT64:
+            longValue = packet.getInt64();
+            break;
+        case MTP_TYPE_UINT64:
+            longValue = packet.getUInt64();
+            break;
+        case MTP_TYPE_STR:
+        {
+            MtpStringBuffer buffer;
+            packet.getString(buffer);
+            stringValue = env->NewStringUTF((const char *)buffer);
+            break;
+         }
+        default:
+            LOGE("unsupported type in getObjectPropertyValue\n");
+            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+    }
+
+    jint result = env->CallIntMethod(mDatabase, method_setObjectProperty,
+                (jint)handle, (jint)property, longValue, stringValue);
+    if (stringValue)
+        env->DeleteLocalRef(stringValue);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
+}
+
+MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) {
+    int         type;
+
+    if (!getDevicePropertyInfo(property, type))
+        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty,
+                (jint)property, mLongBuffer, mStringBuffer);
+    if (result != MTP_RESPONSE_OK) {
+        checkAndClearExceptionFromCallback(env, __FUNCTION__);
+        return result;
+    }
+
+    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
+    jlong longValue = longValues[0];
+    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
+
+    switch (type) {
+        case MTP_TYPE_INT8:
+            packet.putInt8(longValue);
+            break;
+        case MTP_TYPE_UINT8:
+            packet.putUInt8(longValue);
+            break;
+        case MTP_TYPE_INT16:
+            packet.putInt16(longValue);
+            break;
+        case MTP_TYPE_UINT16:
+            packet.putUInt16(longValue);
+            break;
+        case MTP_TYPE_INT32:
+            packet.putInt32(longValue);
+            break;
+        case MTP_TYPE_UINT32:
+            packet.putUInt32(longValue);
+            break;
+        case MTP_TYPE_INT64:
+            packet.putInt64(longValue);
+            break;
+        case MTP_TYPE_UINT64:
+            packet.putUInt64(longValue);
+            break;
+        case MTP_TYPE_INT128:
+            packet.putInt128(longValue);
+            break;
+        case MTP_TYPE_UINT128:
+            packet.putInt128(longValue);
+            break;
+        case MTP_TYPE_STR:
+        {
+            jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
+            packet.putString(str);
+            env->ReleaseCharArrayElements(mStringBuffer, str, 0);
+            break;
+         }
+        default:
+            LOGE("unsupported type in getDevicePropertyValue\n");
+            return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
+    }
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) {
+    int         type;
+
+    if (!getDevicePropertyInfo(property, type))
+        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jlong longValue = 0;
+    jstring stringValue = NULL;
+
+    switch (type) {
+        case MTP_TYPE_INT8:
+            longValue = packet.getInt8();
+            break;
+        case MTP_TYPE_UINT8:
+            longValue = packet.getUInt8();
+            break;
+        case MTP_TYPE_INT16:
+            longValue = packet.getInt16();
+            break;
+        case MTP_TYPE_UINT16:
+            longValue = packet.getUInt16();
+            break;
+        case MTP_TYPE_INT32:
+            longValue = packet.getInt32();
+            break;
+        case MTP_TYPE_UINT32:
+            longValue = packet.getUInt32();
+            break;
+        case MTP_TYPE_INT64:
+            longValue = packet.getInt64();
+            break;
+        case MTP_TYPE_UINT64:
+            longValue = packet.getUInt64();
+            break;
+        case MTP_TYPE_STR:
+        {
+            MtpStringBuffer buffer;
+            packet.getString(buffer);
+            stringValue = env->NewStringUTF((const char *)buffer);
+            break;
+         }
+        default:
+            LOGE("unsupported type in setDevicePropertyValue\n");
+            return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+    }
+
+    jint result = env->CallIntMethod(mDatabase, method_setDeviceProperty,
+                (jint)property, longValue, stringValue);
+    if (stringValue)
+        env->DeleteLocalRef(stringValue);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
+}
+
+MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) {
+    return -1;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
+                                            MtpDataPacket& packet) {
+    char    date[20];
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jboolean result = env->CallBooleanMethod(mDatabase, method_getObjectInfo,
+                (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer);
+    if (!result)
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+
+    jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
+    MtpStorageID storageID = intValues[0];
+    MtpObjectFormat format = intValues[1];
+    MtpObjectHandle parent = intValues[2];
+    env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
+
+    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
+    uint64_t size = longValues[0];
+    uint64_t modified = longValues[1];
+    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
+
+//    int associationType = (format == MTP_FORMAT_ASSOCIATION ?
+//                            MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
+//                            MTP_ASSOCIATION_TYPE_UNDEFINED);
+    int associationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
+
+    packet.putUInt32(storageID);
+    packet.putUInt16(format);
+    packet.putUInt16(0);   // protection status
+    packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size));
+    packet.putUInt16(0);   // thumb format
+    packet.putUInt32(0);   // thumb compressed size
+    packet.putUInt32(0);   // thumb pix width
+    packet.putUInt32(0);   // thumb pix height
+    packet.putUInt32(0);   // image pix width
+    packet.putUInt32(0);   // image pix height
+    packet.putUInt32(0);   // image bit depth
+    packet.putUInt32(parent);
+    packet.putUInt16(associationType);
+    packet.putUInt32(0);   // association desc
+    packet.putUInt32(0);   // sequence number
+
+    jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
+    packet.putString(str);   // file name
+    env->ReleaseCharArrayElements(mStringBuffer, str, 0);
+
+    packet.putEmptyString();
+    formatDateTime(modified, date, sizeof(date));
+    packet.putString(date);   // date modified
+    packet.putEmptyString();   // keywords
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle,
+                                            MtpString& filePath,
+                                            int64_t& fileLength) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath,
+                (jint)handle, mStringBuffer, mLongBuffer);
+    if (result != MTP_RESPONSE_OK) {
+        checkAndClearExceptionFromCallback(env, __FUNCTION__);
+        return result;
+    }
+
+    jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
+    filePath.setTo(str, strlen16(str));
+    env->ReleaseCharArrayElements(mStringBuffer, str, 0);
+
+    jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
+    fileLength = longValues[0];
+    env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
+    
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
+}
+
+MtpResponseCode MyMtpDatabase::deleteFile(MtpObjectHandle handle) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    MtpResponseCode result = env->CallIntMethod(mDatabase, method_deleteFile, (jint)handle);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
+}
+
+struct PropertyTableEntry {
+    MtpObjectProperty   property;
+    int                 type;
+};
+
+static const PropertyTableEntry   kObjectPropertyTable[] = {
+    {   MTP_PROPERTY_STORAGE_ID,        MTP_TYPE_UINT32     },
+    {   MTP_PROPERTY_OBJECT_FORMAT,     MTP_TYPE_UINT16     },
+    {   MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16     },
+    {   MTP_PROPERTY_OBJECT_SIZE,       MTP_TYPE_UINT64     },
+    {   MTP_PROPERTY_OBJECT_FILE_NAME,  MTP_TYPE_STR        },
+    {   MTP_PROPERTY_DATE_MODIFIED,     MTP_TYPE_STR        },
+    {   MTP_PROPERTY_PARENT_OBJECT,     MTP_TYPE_UINT32     },
+    {   MTP_PROPERTY_PERSISTENT_UID,    MTP_TYPE_UINT128    },
+    {   MTP_PROPERTY_NAME,              MTP_TYPE_STR        },
+    {   MTP_PROPERTY_DISPLAY_NAME,      MTP_TYPE_STR        },
+    {   MTP_PROPERTY_DATE_ADDED,        MTP_TYPE_STR        },
+    {   MTP_PROPERTY_ARTIST,            MTP_TYPE_STR        },
+    {   MTP_PROPERTY_ALBUM_NAME,        MTP_TYPE_STR        },
+    {   MTP_PROPERTY_ALBUM_ARTIST,      MTP_TYPE_STR        },
+    {   MTP_PROPERTY_TRACK,             MTP_TYPE_UINT16     },
+    {   MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR    },
+    {   MTP_PROPERTY_GENRE,             MTP_TYPE_STR        },
+    {   MTP_PROPERTY_COMPOSER,          MTP_TYPE_STR        },
+    {   MTP_PROPERTY_DURATION,          MTP_TYPE_UINT32     },
+    {   MTP_PROPERTY_DESCRIPTION,       MTP_TYPE_STR        },
+};
+
+static const PropertyTableEntry   kDevicePropertyTable[] = {
+    {   MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,     MTP_TYPE_STR },
+    {   MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,        MTP_TYPE_STR },
+};
+
+bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
+    int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
+    const PropertyTableEntry* entry = kObjectPropertyTable;
+    for (int i = 0; i < count; i++, entry++) {
+        if (entry->property == property) {
+            type = entry->type;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool MyMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
+    int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
+    const PropertyTableEntry* entry = kDevicePropertyTable;
+    for (int i = 0; i < count; i++, entry++) {
+        if (entry->property == property) {
+            type = entry->type;
+            return true;
+        }
+    }
+    return false;
+}
+
+MtpObjectHandleList* MyMtpDatabase::getObjectReferences(MtpObjectHandle handle) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectReferences,
+                (jint)handle);
+    if (!array)
+        return NULL;
+    MtpObjectHandleList* list = new MtpObjectHandleList();
+    jint* handles = env->GetIntArrayElements(array, 0);
+    jsize length = env->GetArrayLength(array);
+    for (int i = 0; i < length; i++)
+        list->push(handles[i]);
+    env->ReleaseIntArrayElements(array, handles, 0);
+    env->DeleteLocalRef(array);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return list;
+}
+
+MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle,
+                                                    MtpObjectHandleList* references) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    int count = references->size();
+    jintArray array = env->NewIntArray(count);
+    if (!array) {
+        LOGE("out of memory in setObjectReferences");
+        return false;
+    }
+    jint* handles = env->GetIntArrayElements(array, 0);
+     for (int i = 0; i < count; i++)
+        handles[i] = (*references)[i];
+    env->ReleaseIntArrayElements(array, handles, 0);
+    MtpResponseCode result = env->CallIntMethod(mDatabase, method_setObjectReferences,
+                (jint)handle, array);
+    env->DeleteLocalRef(array);
+
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return result;
+}
+
+MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
+                                            MtpObjectFormat format) {
+    MtpProperty* result = NULL;
+    switch (property) {
+        case MTP_PROPERTY_OBJECT_FORMAT:
+        case MTP_PROPERTY_PROTECTION_STATUS:
+        case MTP_PROPERTY_TRACK:
+            result = new MtpProperty(property, MTP_TYPE_UINT16);
+            break;
+        case MTP_PROPERTY_STORAGE_ID:
+        case MTP_PROPERTY_PARENT_OBJECT:
+        case MTP_PROPERTY_DURATION:
+            result = new MtpProperty(property, MTP_TYPE_UINT32);
+            break;
+        case MTP_PROPERTY_OBJECT_SIZE:
+            result = new MtpProperty(property, MTP_TYPE_UINT64);
+            break;
+        case MTP_PROPERTY_PERSISTENT_UID:
+            result = new MtpProperty(property, MTP_TYPE_UINT128);
+            break;
+        case MTP_PROPERTY_NAME:
+        case MTP_PROPERTY_DATE_MODIFIED:
+        case MTP_PROPERTY_DISPLAY_NAME:
+        case MTP_PROPERTY_DATE_ADDED:
+        case MTP_PROPERTY_ARTIST:
+        case MTP_PROPERTY_ALBUM_NAME:
+        case MTP_PROPERTY_ALBUM_ARTIST:
+        case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
+        case MTP_PROPERTY_GENRE:
+        case MTP_PROPERTY_COMPOSER:
+        case MTP_PROPERTY_DESCRIPTION:
+            result = new MtpProperty(property, MTP_TYPE_STR);
+            break;
+        case MTP_PROPERTY_OBJECT_FILE_NAME:
+            // We allow renaming files and folders
+            result = new MtpProperty(property, MTP_TYPE_STR, true);
+            break;
+    }
+
+    return result;
+}
+
+MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
+    MtpProperty* result = NULL;
+    switch (property) {
+        case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+        case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+        {
+            // writeable string properties
+            result = new MtpProperty(property, MTP_TYPE_STR, true);
+
+            // set current value
+            JNIEnv* env = AndroidRuntime::getJNIEnv();
+            jint ret = env->CallIntMethod(mDatabase, method_getDeviceProperty,
+                        (jint)property, mLongBuffer, mStringBuffer);
+            if (ret == MTP_RESPONSE_OK) {
+                jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
+                result->setCurrentValue(str);
+                env->ReleaseCharArrayElements(mStringBuffer, str, 0);
+            } else {
+                LOGE("unable to read device property, response: %04X", ret);
+            }
+
+            checkAndClearExceptionFromCallback(env, __FUNCTION__);
+            break;
+        }
+    }
+
+    return result;
+}
+
+void MyMtpDatabase::sessionStarted() {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    env->CallVoidMethod(mDatabase, method_sessionStarted);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void MyMtpDatabase::sessionEnded() {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    env->CallVoidMethod(mDatabase, method_sessionEnded);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+#endif // HAVE_ANDROID_OS
+
+// ----------------------------------------------------------------------------
+
+static void
+android_media_MtpDatabase_setup(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("setup\n");
+    MyMtpDatabase* database = new MyMtpDatabase(env, thiz);
+    env->SetIntField(thiz, field_context, (int)database);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+#endif
+}
+
+static void
+android_media_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("finalize\n");
+    MyMtpDatabase* database = (MyMtpDatabase *)env->GetIntField(thiz, field_context);
+    database->cleanup(env);
+    delete database;
+    env->SetIntField(thiz, field_context, 0);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+#endif
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gMethods[] = {
+    {"native_setup",            "()V",  (void *)android_media_MtpDatabase_setup},
+    {"native_finalize",         "()V",  (void *)android_media_MtpDatabase_finalize},
+};
+
+static const char* const kClassPathName = "android/media/MtpDatabase";
+
+int register_android_media_MtpDatabase(JNIEnv *env)
+{
+    jclass clazz;
+
+    LOGD("register_android_media_MtpDatabase\n");
+
+    clazz = env->FindClass("android/media/MtpDatabase");
+    if (clazz == NULL) {
+        LOGE("Can't find android/media/MtpDatabase");
+        return -1;
+    }
+    method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I");
+    if (method_beginSendObject == NULL) {
+        LOGE("Can't find beginSendObject");
+        return -1;
+    }
+    method_endSendObject = env->GetMethodID(clazz, "endSendObject", "(Ljava/lang/String;IIZ)V");
+    if (method_endSendObject == NULL) {
+        LOGE("Can't find endSendObject");
+        return -1;
+    }
+    method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I");
+    if (method_getObjectList == NULL) {
+        LOGE("Can't find getObjectList");
+        return -1;
+    }
+    method_getNumObjects = env->GetMethodID(clazz, "getNumObjects", "(III)I");
+    if (method_getNumObjects == NULL) {
+        LOGE("Can't find getNumObjects");
+        return -1;
+    }
+    method_getSupportedPlaybackFormats = env->GetMethodID(clazz, "getSupportedPlaybackFormats", "()[I");
+    if (method_getSupportedPlaybackFormats == NULL) {
+        LOGE("Can't find getSupportedPlaybackFormats");
+        return -1;
+    }
+    method_getSupportedCaptureFormats = env->GetMethodID(clazz, "getSupportedCaptureFormats", "()[I");
+    if (method_getSupportedCaptureFormats == NULL) {
+        LOGE("Can't find getSupportedCaptureFormats");
+        return -1;
+    }
+    method_getSupportedObjectProperties = env->GetMethodID(clazz, "getSupportedObjectProperties", "(I)[I");
+    if (method_getSupportedObjectProperties == NULL) {
+        LOGE("Can't find getSupportedObjectProperties");
+        return -1;
+    }
+    method_getSupportedDeviceProperties = env->GetMethodID(clazz, "getSupportedDeviceProperties", "()[I");
+    if (method_getSupportedDeviceProperties == NULL) {
+        LOGE("Can't find getSupportedDeviceProperties");
+        return -1;
+    }
+    method_getObjectProperty = env->GetMethodID(clazz, "getObjectProperty", "(II[J[C)I");
+    if (method_getObjectProperty == NULL) {
+        LOGE("Can't find getObjectProperty");
+        return -1;
+    }
+    method_setObjectProperty = env->GetMethodID(clazz, "setObjectProperty", "(IIJLjava/lang/String;)I");
+    if (method_setObjectProperty == NULL) {
+        LOGE("Can't find setObjectProperty");
+        return -1;
+    }
+    method_getDeviceProperty = env->GetMethodID(clazz, "getDeviceProperty", "(I[J[C)I");
+    if (method_getDeviceProperty == NULL) {
+        LOGE("Can't find getDeviceProperty");
+        return -1;
+    }
+    method_setDeviceProperty = env->GetMethodID(clazz, "setDeviceProperty", "(IJLjava/lang/String;)I");
+    if (method_setDeviceProperty == NULL) {
+        LOGE("Can't find setDeviceProperty");
+        return -1;
+    }
+    method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z");
+    if (method_getObjectInfo == NULL) {
+        LOGE("Can't find getObjectInfo");
+        return -1;
+    }
+    method_getObjectFilePath = env->GetMethodID(clazz, "getObjectFilePath", "(I[C[J)I");
+    if (method_getObjectFilePath == NULL) {
+        LOGE("Can't find getObjectFilePath");
+        return -1;
+    }
+    method_deleteFile = env->GetMethodID(clazz, "deleteFile", "(I)I");
+    if (method_deleteFile == NULL) {
+        LOGE("Can't find deleteFile");
+        return -1;
+    }
+    method_getObjectReferences = env->GetMethodID(clazz, "getObjectReferences", "(I)[I");
+    if (method_getObjectReferences == NULL) {
+        LOGE("Can't find getObjectReferences");
+        return -1;
+    }
+    method_setObjectReferences = env->GetMethodID(clazz, "setObjectReferences", "(I[I)I");
+    if (method_setObjectReferences == NULL) {
+        LOGE("Can't find setObjectReferences");
+        return -1;
+    }
+    method_sessionStarted = env->GetMethodID(clazz, "sessionStarted", "()V");
+    if (method_sessionStarted == NULL) {
+        LOGE("Can't find sessionStarted");
+        return -1;
+    }
+    method_sessionEnded = env->GetMethodID(clazz, "sessionEnded", "()V");
+    if (method_sessionEnded == NULL) {
+        LOGE("Can't find sessionEnded");
+        return -1;
+    }
+
+    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+    if (field_context == NULL) {
+        LOGE("Can't find MtpDatabase.mNativeContext");
+        return -1;
+    }
+
+    return AndroidRuntime::registerNativeMethods(env,
+                "android/media/MtpDatabase", gMethods, NELEM(gMethods));
+}
diff --git a/media/jni/android_media_MtpServer.cpp b/media/jni/android_media_MtpServer.cpp
new file mode 100644
index 0000000..f16cdd9
--- /dev/null
+++ b/media/jni/android_media_MtpServer.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpServerJNI"
+#include "utils/Log.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <utils/threads.h>
+
+#ifdef HAVE_ANDROID_OS
+#include <linux/usb/f_mtp.h>
+#endif
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "private/android_filesystem_config.h"
+
+#include "MtpServer.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+
+static jfieldID field_context;
+static Mutex    sMutex;
+
+// in android_media_MtpDatabase.cpp
+extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
+
+// ----------------------------------------------------------------------------
+
+#ifdef HAVE_ANDROID_OS
+
+static bool ExceptionCheck(void* env)
+{
+    return ((JNIEnv *)env)->ExceptionCheck();
+}
+
+class MtpThread : public Thread {
+private:
+    MtpDatabase*    mDatabase;
+    MtpServer*      mServer;
+    String8         mStoragePath;
+    jobject         mJavaServer;
+    int             mFd;
+
+public:
+    MtpThread(MtpDatabase* database, const char* storagePath, jobject javaServer)
+        :   mDatabase(database),
+            mServer(NULL),
+            mStoragePath(storagePath),
+            mJavaServer(javaServer),
+            mFd(-1)
+    {
+    }
+
+    void setPtpMode(bool usePtp) {
+        sMutex.lock();
+        if (mFd >= 0) {
+            ioctl(mFd, MTP_SET_INTERFACE_MODE,
+                    (usePtp ? MTP_INTERFACE_MODE_PTP : MTP_INTERFACE_MODE_MTP));
+        } else {
+            int fd = open("/dev/mtp_usb", O_RDWR);
+            if (fd >= 0) {
+                ioctl(fd, MTP_SET_INTERFACE_MODE,
+                        (usePtp ? MTP_INTERFACE_MODE_PTP : MTP_INTERFACE_MODE_MTP));
+                close(fd);
+            }
+        }
+        sMutex.unlock();
+    }
+
+    virtual bool threadLoop() {
+        sMutex.lock();
+        mFd = open("/dev/mtp_usb", O_RDWR);
+        printf("open returned %d\n", mFd);
+        if (mFd < 0) {
+            LOGE("could not open MTP driver\n");
+            sMutex.unlock();
+            return false;
+        }
+
+        mServer = new MtpServer(mFd, mDatabase, AID_SDCARD_RW, 0664, 0775);
+        mServer->addStorage(mStoragePath);
+        sMutex.unlock();
+
+        LOGD("MtpThread mServer->run");
+        mServer->run();
+
+        sMutex.lock();
+        close(mFd);
+        mFd = -1;
+        delete mServer;
+        mServer = NULL;
+
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        env->SetIntField(mJavaServer, field_context, 0);
+        env->DeleteGlobalRef(mJavaServer);
+        sMutex.unlock();
+
+        LOGD("threadLoop returning");
+        return false;
+    }
+
+    void sendObjectAdded(MtpObjectHandle handle) {
+        sMutex.lock();
+        if (mServer)
+            mServer->sendObjectAdded(handle);
+        sMutex.unlock();
+    }
+
+    void sendObjectRemoved(MtpObjectHandle handle) {
+        sMutex.lock();
+        if (mServer)
+            mServer->sendObjectRemoved(handle);
+        sMutex.unlock();
+    }
+};
+
+#endif // HAVE_ANDROID_OS
+
+static void
+android_media_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jstring storagePath)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("setup\n");
+
+    MtpDatabase* database = getMtpDatabase(env, javaDatabase);
+    const char *storagePathStr = env->GetStringUTFChars(storagePath, NULL);
+
+    MtpThread* thread = new MtpThread(database, storagePathStr, env->NewGlobalRef(thiz));
+    env->SetIntField(thiz, field_context, (int)thread);
+
+    env->ReleaseStringUTFChars(storagePath, storagePathStr);
+#endif
+}
+
+static void
+android_media_MtpServer_finalize(JNIEnv *env, jobject thiz)
+{
+    LOGD("finalize\n");
+}
+
+
+static void
+android_media_MtpServer_start(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("start\n");
+    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
+    thread->run("MtpThread");
+#endif // HAVE_ANDROID_OS
+}
+
+static void
+android_media_MtpServer_stop(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("stop\n");
+#endif
+}
+
+static void
+android_media_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle)
+{
+#ifdef HAVE_ANDROID_OS
+    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
+    if (thread)
+        thread->sendObjectAdded(handle);
+#endif
+}
+
+static void
+android_media_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle)
+{
+#ifdef HAVE_ANDROID_OS
+    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
+    if (thread)
+        thread->sendObjectRemoved(handle);
+#endif
+}
+
+static void
+android_media_MtpServer_set_ptp_mode(JNIEnv *env, jobject thiz, jboolean usePtp)
+{
+#ifdef HAVE_ANDROID_OS
+    LOGD("set_ptp_mode\n");
+    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
+    if (thread)
+        thread->setPtpMode(usePtp);
+ #endif
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gMethods[] = {
+    {"native_setup",                "(Landroid/media/MtpDatabase;Ljava/lang/String;)V",
+                                            (void *)android_media_MtpServer_setup},
+    {"native_finalize",             "()V",  (void *)android_media_MtpServer_finalize},
+    {"native_start",                "()V",  (void *)android_media_MtpServer_start},
+    {"native_stop",                 "()V",  (void *)android_media_MtpServer_stop},
+    {"native_send_object_added",    "(I)V", (void *)android_media_MtpServer_send_object_added},
+    {"native_send_object_removed",  "(I)V", (void *)android_media_MtpServer_send_object_removed},
+    {"native_set_ptp_mode",         "(Z)V", (void *)android_media_MtpServer_set_ptp_mode},
+};
+
+static const char* const kClassPathName = "android/media/MtpServer";
+
+int register_android_media_MtpServer(JNIEnv *env)
+{
+    jclass clazz;
+
+    LOGD("register_android_media_MtpServer\n");
+
+    clazz = env->FindClass("android/media/MtpServer");
+    if (clazz == NULL) {
+        LOGE("Can't find android/media/MtpServer");
+        return -1;
+    }
+    field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+    if (field_context == NULL) {
+        LOGE("Can't find MtpServer.mNativeContext");
+        return -1;
+    }
+
+    return AndroidRuntime::registerNativeMethods(env,
+                "android/media/MtpServer", gMethods, NELEM(gMethods));
+}
diff --git a/media/libeffects/visualizer/Android.mk b/media/libeffects/visualizer/Android.mk
index 48b45ff..e6ff654 100644
--- a/media/libeffects/visualizer/Android.mk
+++ b/media/libeffects/visualizer/Android.mk
@@ -27,4 +27,4 @@
 
 LOCAL_PRELINK_MODULE := false
 
-include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 7e3b743..9c2a8ba 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -763,7 +763,8 @@
     if ((popCount(device) == 1 ) &&
         (device & (AudioSystem::DEVICE_OUT_BLUETOOTH_SCO |
                    AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
-                   AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT))) {
+                   AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT |
+                   AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET))) {
         return true;
     } else {
         return false;
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index 0f55b19d..1a46715 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -22,12 +22,14 @@
 
 #include <media/IMediaPlayer.h>
 #include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
 
 namespace android {
 
 enum {
     DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
     SET_VIDEO_SURFACE,
+    SET_VIDEO_ISURFACE,
     PREPARE_ASYNC,
     START,
     STOP,
@@ -43,8 +45,6 @@
     INVOKE,
     SET_METADATA_FILTER,
     GET_METADATA,
-    SUSPEND,
-    RESUME,
     SET_AUX_EFFECT_SEND_LEVEL,
     ATTACH_AUX_EFFECT
 };
@@ -65,11 +65,20 @@
         remote()->transact(DISCONNECT, data, &reply);
     }
 
-    status_t setVideoSurface(const sp<ISurface>& surface)
+    status_t setVideoISurface(const sp<ISurface>& surface)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
         data.writeStrongBinder(surface->asBinder());
+        remote()->transact(SET_VIDEO_ISURFACE, data, &reply);
+        return reply.readInt32();
+    }
+
+    status_t setVideoSurface(const sp<Surface>& surface)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+        Surface::writeToParcel(surface, &data);
         remote()->transact(SET_VIDEO_SURFACE, data, &reply);
         return reply.readInt32();
     }
@@ -204,26 +213,6 @@
         return reply->readInt32();
     }
 
-    status_t suspend() {
-        Parcel request;
-        request.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-
-        Parcel reply;
-        remote()->transact(SUSPEND, request, &reply);
-
-        return reply.readInt32();
-    }
-
-    status_t resume() {
-        Parcel request;
-        request.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
-
-        Parcel reply;
-        remote()->transact(RESUME, request, &reply);
-
-        return reply.readInt32();
-    }
-
     status_t setAuxEffectSendLevel(float level)
     {
         Parcel data, reply;
@@ -256,9 +245,15 @@
             disconnect();
             return NO_ERROR;
         } break;
-        case SET_VIDEO_SURFACE: {
+        case SET_VIDEO_ISURFACE: {
             CHECK_INTERFACE(IMediaPlayer, data, reply);
             sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder());
+            reply->writeInt32(setVideoISurface(surface));
+            return NO_ERROR;
+        } break;
+        case SET_VIDEO_SURFACE: {
+            CHECK_INTERFACE(IMediaPlayer, data, reply);
+            sp<Surface> surface = Surface::readFromParcel(data);
             reply->writeInt32(setVideoSurface(surface));
             return NO_ERROR;
         } break;
@@ -341,16 +336,6 @@
             reply->writeInt32(setMetadataFilter(data));
             return NO_ERROR;
         } break;
-        case SUSPEND: {
-            CHECK_INTERFACE(IMediaPlayer, data, reply);
-            reply->writeInt32(suspend());
-            return NO_ERROR;
-        } break;
-        case RESUME: {
-            CHECK_INTERFACE(IMediaPlayer, data, reply);
-            reply->writeInt32(resume());
-            return NO_ERROR;
-        } break;
         case GET_METADATA: {
             CHECK_INTERFACE(IMediaPlayer, data, reply);
             const status_t retcode = getMetadata(data.readInt32(), data.readInt32(), reply);
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 947ff34..59cd1b7 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -19,7 +19,7 @@
 #define LOG_TAG "IMediaRecorder"
 #include <utils/Log.h>
 #include <binder/Parcel.h>
-#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
 #include <camera/ICamera.h>
 #include <media/IMediaRecorderClient.h>
 #include <media/IMediaRecorder.h>
@@ -43,6 +43,7 @@
     SET_AUDIO_ENCODER,
     SET_OUTPUT_FILE_PATH,
     SET_OUTPUT_FILE_FD,
+    SET_OUTPUT_FILE_AUXILIARY_FD,
     SET_VIDEO_SIZE,
     SET_VIDEO_FRAMERATE,
     SET_PARAMETERS,
@@ -69,12 +70,12 @@
         return reply.readInt32();
     }
 
-    status_t setPreviewSurface(const sp<ISurface>& surface)
+    status_t setPreviewSurface(const sp<Surface>& surface)
     {
         LOGV("setPreviewSurface(%p)", surface.get());
         Parcel data, reply;
         data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
-        data.writeStrongBinder(surface->asBinder());
+        Surface::writeToParcel(surface, &data);
         remote()->transact(SET_PREVIEW_SURFACE, data, &reply);
         return reply.readInt32();
     }
@@ -159,6 +160,15 @@
         return reply.readInt32();
     }
 
+    status_t setOutputFileAuxiliary(int fd) {
+        LOGV("setOutputFileAuxiliary(%d)", fd);
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+        data.writeFileDescriptor(fd);
+        remote()->transact(SET_OUTPUT_FILE_AUXILIARY_FD, data, &reply);
+        return reply.readInt32();
+    }
+
     status_t setVideoSize(int width, int height)
     {
         LOGV("setVideoSize(%dx%d)", width, height);
@@ -377,6 +387,13 @@
             ::close(fd);
             return NO_ERROR;
         } break;
+        case SET_OUTPUT_FILE_AUXILIARY_FD: {
+            LOGV("SET_OUTPUT_FILE_AUXILIARY_FD");
+            CHECK_INTERFACE(IMediaRecorder, data, reply);
+            int fd = dup(data.readFileDescriptor());
+            reply->writeInt32(setOutputFileAuxiliary(fd));
+            return NO_ERROR;
+        } break;
         case SET_VIDEO_SIZE: {
             LOGV("SET_VIDEO_SIZE");
             CHECK_INTERFACE(IMediaRecorder, data, reply);
@@ -409,7 +426,7 @@
         case SET_PREVIEW_SURFACE: {
             LOGV("SET_PREVIEW_SURFACE");
             CHECK_INTERFACE(IMediaRecorder, data, reply);
-            sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder());
+            sp<Surface> surface = Surface::readFromParcel(data);
             reply->writeInt32(setPreviewSurface(surface));
             return NO_ERROR;
         } break;
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index f3804b8..f975217 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -21,7 +21,10 @@
     SET_PARAMETER,
     GET_CONFIG,
     SET_CONFIG,
+    ENABLE_GRAPHIC_BUFFERS,
     USE_BUFFER,
+    USE_GRAPHIC_BUFFER,
+    STORE_META_DATA_IN_BUFFERS,
     ALLOC_BUFFER,
     ALLOC_BUFFER_WITH_BACKUP,
     FREE_BUFFER,
@@ -216,6 +219,19 @@
         return reply.readInt32();
     }
 
+    virtual status_t enableGraphicBuffers(
+            node_id node, OMX_U32 port_index, OMX_BOOL enable) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeIntPtr((intptr_t)node);
+        data.writeInt32(port_index);
+        data.writeInt32((uint32_t)enable);
+        remote()->transact(ENABLE_GRAPHIC_BUFFERS, data, &reply);
+
+        status_t err = reply.readInt32();
+        return err;
+    }
+
     virtual status_t useBuffer(
             node_id node, OMX_U32 port_index, const sp<IMemory> &params,
             buffer_id *buffer) {
@@ -238,6 +254,42 @@
         return err;
     }
 
+
+    virtual status_t useGraphicBuffer(
+            node_id node, OMX_U32 port_index,
+            const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeIntPtr((intptr_t)node);
+        data.writeInt32(port_index);
+        data.write(*graphicBuffer);
+        remote()->transact(USE_GRAPHIC_BUFFER, data, &reply);
+
+        status_t err = reply.readInt32();
+        if (err != OK) {
+            *buffer = 0;
+
+            return err;
+        }
+
+        *buffer = (void*)reply.readIntPtr();
+
+        return err;
+    }
+
+    virtual status_t storeMetaDataInBuffers(
+            node_id node, OMX_U32 port_index, OMX_BOOL enable) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeIntPtr((intptr_t)node);
+        data.writeInt32(port_index);
+        data.writeInt32((uint32_t)enable);
+        remote()->transact(STORE_META_DATA_IN_BUFFERS, data, &reply);
+
+        status_t err = reply.readInt32();
+        return err;
+    }
+
     virtual status_t allocateBuffer(
             node_id node, OMX_U32 port_index, size_t size,
             buffer_id *buffer, void **buffer_data) {
@@ -541,6 +593,20 @@
             return NO_ERROR;
         }
 
+        case ENABLE_GRAPHIC_BUFFERS:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = (void*)data.readIntPtr();
+            OMX_U32 port_index = data.readInt32();
+            OMX_BOOL enable = (OMX_BOOL)data.readInt32();
+
+            status_t err = enableGraphicBuffers(node, port_index, enable);
+            reply->writeInt32(err);
+
+            return NO_ERROR;
+        }
+
         case USE_BUFFER:
         {
             CHECK_INTERFACE(IOMX, data, reply);
@@ -561,6 +627,41 @@
             return NO_ERROR;
         }
 
+        case USE_GRAPHIC_BUFFER:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = (void*)data.readIntPtr();
+            OMX_U32 port_index = data.readInt32();
+            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer();
+            data.read(*graphicBuffer);
+
+            buffer_id buffer;
+            status_t err = useGraphicBuffer(
+                    node, port_index, graphicBuffer, &buffer);
+            reply->writeInt32(err);
+
+            if (err == OK) {
+                reply->writeIntPtr((intptr_t)buffer);
+            }
+
+            return NO_ERROR;
+        }
+
+        case STORE_META_DATA_IN_BUFFERS:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = (void*)data.readIntPtr();
+            OMX_U32 port_index = data.readInt32();
+            OMX_BOOL enable = (OMX_BOOL)data.readInt32();
+
+            status_t err = storeMetaDataInBuffers(node, port_index, enable);
+            reply->writeInt32(err);
+
+            return NO_ERROR;
+        }
+
         case ALLOC_BUFFER:
         {
             CHECK_INTERFACE(IOMX, data, reply);
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index 3869389..9ad63f0 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -59,8 +59,21 @@
 };
 
 const MediaProfiles::NameToTagMap MediaProfiles::sCamcorderQualityNameMap[] = {
+    {"low", CAMCORDER_QUALITY_LOW},
     {"high", CAMCORDER_QUALITY_HIGH},
-    {"low",  CAMCORDER_QUALITY_LOW}
+    {"qcif", CAMCORDER_QUALITY_QCIF},
+    {"cif", CAMCORDER_QUALITY_CIF},
+    {"480p", CAMCORDER_QUALITY_480P},
+    {"720p", CAMCORDER_QUALITY_720P},
+    {"1080p", CAMCORDER_QUALITY_1080P},
+
+    {"timelapselow",  CAMCORDER_QUALITY_TIME_LAPSE_LOW},
+    {"timelapsehigh", CAMCORDER_QUALITY_TIME_LAPSE_HIGH},
+    {"timelapseqcif", CAMCORDER_QUALITY_TIME_LAPSE_QCIF},
+    {"timelapsecif", CAMCORDER_QUALITY_TIME_LAPSE_CIF},
+    {"timelapse480p", CAMCORDER_QUALITY_TIME_LAPSE_480P},
+    {"timelapse720p", CAMCORDER_QUALITY_TIME_LAPSE_720P},
+    {"timelapse1080p", CAMCORDER_QUALITY_TIME_LAPSE_1080P}
 };
 
 /*static*/ void
@@ -411,16 +424,16 @@
 }
 
 /*static*/ MediaProfiles::CamcorderProfile*
-MediaProfiles::createDefaultCamcorderHighProfile()
+MediaProfiles::createDefaultCamcorderTimeLapseQcifProfile(camcorder_quality quality)
 {
     MediaProfiles::VideoCodec *videoCodec =
-        new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 360000, 352, 288, 20);
+        new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 1000000, 176, 144, 20);
 
     AudioCodec *audioCodec = new AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
     CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
     profile->mCameraId = 0;
     profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
-    profile->mQuality = CAMCORDER_QUALITY_HIGH;
+    profile->mQuality = quality;
     profile->mDuration = 60;
     profile->mVideoCodec = videoCodec;
     profile->mAudioCodec = audioCodec;
@@ -428,7 +441,40 @@
 }
 
 /*static*/ MediaProfiles::CamcorderProfile*
-MediaProfiles::createDefaultCamcorderLowProfile()
+MediaProfiles::createDefaultCamcorderTimeLapse480pProfile(camcorder_quality quality)
+{
+    MediaProfiles::VideoCodec *videoCodec =
+        new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 20000000, 720, 480, 20);
+
+    AudioCodec *audioCodec = new AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
+    CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
+    profile->mCameraId = 0;
+    profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
+    profile->mQuality = quality;
+    profile->mDuration = 60;
+    profile->mVideoCodec = videoCodec;
+    profile->mAudioCodec = audioCodec;
+    return profile;
+}
+
+/*static*/ void
+MediaProfiles::createDefaultCamcorderTimeLapseLowProfiles(
+        MediaProfiles::CamcorderProfile **lowTimeLapseProfile,
+        MediaProfiles::CamcorderProfile **lowSpecificTimeLapseProfile) {
+    *lowTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile(CAMCORDER_QUALITY_TIME_LAPSE_LOW);
+    *lowSpecificTimeLapseProfile = createDefaultCamcorderTimeLapseQcifProfile(CAMCORDER_QUALITY_TIME_LAPSE_QCIF);
+}
+
+/*static*/ void
+MediaProfiles::createDefaultCamcorderTimeLapseHighProfiles(
+        MediaProfiles::CamcorderProfile **highTimeLapseProfile,
+        MediaProfiles::CamcorderProfile **highSpecificTimeLapseProfile) {
+    *highTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile(CAMCORDER_QUALITY_TIME_LAPSE_HIGH);
+    *highSpecificTimeLapseProfile = createDefaultCamcorderTimeLapse480pProfile(CAMCORDER_QUALITY_TIME_LAPSE_480P);
+}
+
+/*static*/ MediaProfiles::CamcorderProfile*
+MediaProfiles::createDefaultCamcorderQcifProfile(camcorder_quality quality)
 {
     MediaProfiles::VideoCodec *videoCodec =
         new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 192000, 176, 144, 20);
@@ -439,18 +485,72 @@
     MediaProfiles::CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
     profile->mCameraId = 0;
     profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
-    profile->mQuality = CAMCORDER_QUALITY_LOW;
+    profile->mQuality = quality;
     profile->mDuration = 30;
     profile->mVideoCodec = videoCodec;
     profile->mAudioCodec = audioCodec;
     return profile;
 }
 
+/*static*/ MediaProfiles::CamcorderProfile*
+MediaProfiles::createDefaultCamcorderCifProfile(camcorder_quality quality)
+{
+    MediaProfiles::VideoCodec *videoCodec =
+        new MediaProfiles::VideoCodec(VIDEO_ENCODER_H263, 360000, 352, 288, 20);
+
+    AudioCodec *audioCodec = new AudioCodec(AUDIO_ENCODER_AMR_NB, 12200, 8000, 1);
+    CamcorderProfile *profile = new MediaProfiles::CamcorderProfile;
+    profile->mCameraId = 0;
+    profile->mFileFormat = OUTPUT_FORMAT_THREE_GPP;
+    profile->mQuality = quality;
+    profile->mDuration = 60;
+    profile->mVideoCodec = videoCodec;
+    profile->mAudioCodec = audioCodec;
+    return profile;
+}
+
+/*static*/ void
+MediaProfiles::createDefaultCamcorderLowProfiles(
+        MediaProfiles::CamcorderProfile **lowProfile,
+        MediaProfiles::CamcorderProfile **lowSpecificProfile) {
+    *lowProfile = createDefaultCamcorderQcifProfile(CAMCORDER_QUALITY_LOW);
+    *lowSpecificProfile = createDefaultCamcorderQcifProfile(CAMCORDER_QUALITY_QCIF);
+}
+
+/*static*/ void
+MediaProfiles::createDefaultCamcorderHighProfiles(
+        MediaProfiles::CamcorderProfile **highProfile,
+        MediaProfiles::CamcorderProfile **highSpecificProfile) {
+    *highProfile = createDefaultCamcorderCifProfile(CAMCORDER_QUALITY_HIGH);
+    *highSpecificProfile = createDefaultCamcorderCifProfile(CAMCORDER_QUALITY_CIF);
+}
+
 /*static*/ void
 MediaProfiles::createDefaultCamcorderProfiles(MediaProfiles *profiles)
 {
-    profiles->mCamcorderProfiles.add(createDefaultCamcorderHighProfile());
-    profiles->mCamcorderProfiles.add(createDefaultCamcorderLowProfile());
+    // low camcorder profiles.
+    MediaProfiles::CamcorderProfile *lowProfile, *lowSpecificProfile;
+    createDefaultCamcorderLowProfiles(&lowProfile, &lowSpecificProfile);
+    profiles->mCamcorderProfiles.add(lowProfile);
+    profiles->mCamcorderProfiles.add(lowSpecificProfile);
+
+    // high camcorder profiles.
+    MediaProfiles::CamcorderProfile* highProfile, *highSpecificProfile;
+    createDefaultCamcorderHighProfiles(&highProfile, &highSpecificProfile);
+    profiles->mCamcorderProfiles.add(highProfile);
+    profiles->mCamcorderProfiles.add(highSpecificProfile);
+
+    // low camcorder time lapse profiles.
+    MediaProfiles::CamcorderProfile *lowTimeLapseProfile, *lowSpecificTimeLapseProfile;
+    createDefaultCamcorderTimeLapseLowProfiles(&lowTimeLapseProfile, &lowSpecificTimeLapseProfile);
+    profiles->mCamcorderProfiles.add(lowTimeLapseProfile);
+    profiles->mCamcorderProfiles.add(lowSpecificTimeLapseProfile);
+
+    // high camcorder time lapse profiles.
+    MediaProfiles::CamcorderProfile *highTimeLapseProfile, *highSpecificTimeLapseProfile;
+    createDefaultCamcorderTimeLapseHighProfiles(&highTimeLapseProfile, &highSpecificTimeLapseProfile);
+    profiles->mCamcorderProfiles.add(highTimeLapseProfile);
+    profiles->mCamcorderProfiles.add(highSpecificTimeLapseProfile);
 }
 
 /*static*/ void
@@ -668,13 +768,8 @@
     return decoders;  // copy out
 }
 
-int MediaProfiles::getCamcorderProfileParamByName(const char *name,
-                                                  int cameraId,
-                                                  camcorder_quality quality) const
+int MediaProfiles::getCamcorderProfileIndex(int cameraId, camcorder_quality quality) const
 {
-    LOGV("getCamcorderProfileParamByName: %s for camera %d, quality %d",
-         name, cameraId, quality);
-
     int index = -1;
     for (size_t i = 0, n = mCamcorderProfiles.size(); i < n; ++i) {
         if (mCamcorderProfiles[i]->mCameraId == cameraId &&
@@ -683,6 +778,17 @@
             break;
         }
     }
+    return index;
+}
+
+int MediaProfiles::getCamcorderProfileParamByName(const char *name,
+                                                  int cameraId,
+                                                  camcorder_quality quality) const
+{
+    LOGV("getCamcorderProfileParamByName: %s for camera %d, quality %d",
+         name, cameraId, quality);
+
+    int index = getCamcorderProfileIndex(cameraId, quality);
     if (index == -1) {
         LOGE("The given camcorder profile camera %d quality %d is not found",
              cameraId, quality);
@@ -705,6 +811,11 @@
     return -1;
 }
 
+bool MediaProfiles::hasCamcorderProfile(int cameraId, camcorder_quality quality) const
+{
+    return (getCamcorderProfileIndex(cameraId, quality) != -1);
+}
+
 Vector<int> MediaProfiles::getImageEncodingQualityLevels(int cameraId) const
 {
     Vector<int> result;
diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp
index c5112a5..c31b622 100644
--- a/media/libmedia/MediaScanner.cpp
+++ b/media/libmedia/MediaScanner.cpp
@@ -48,8 +48,7 @@
 }
 
 status_t MediaScanner::processDirectory(
-        const char *path, const char *extensions,
-        MediaScannerClient &client,
+        const char *path, MediaScannerClient &client,
         ExceptionCheck exceptionCheck, void *exceptionEnv) {
     int pathLength = strlen(path);
     if (pathLength >= PATH_MAX) {
@@ -72,35 +71,16 @@
 
     status_t result =
         doProcessDirectory(
-                pathBuffer, pathRemaining, extensions, client,
-                exceptionCheck, exceptionEnv);
+                pathBuffer, pathRemaining, client, exceptionCheck, exceptionEnv);
 
     free(pathBuffer);
 
     return result;
 }
 
-static bool fileMatchesExtension(const char* path, const char* extensions) {
-    const char* extension = strrchr(path, '.');
-    if (!extension) return false;
-    ++extension;    // skip the dot
-    if (extension[0] == 0) return false;
-
-    while (extensions[0]) {
-        const char* comma = strchr(extensions, ',');
-        size_t length = (comma ? comma - extensions : strlen(extensions));
-        if (length == strlen(extension) && strncasecmp(extension, extensions, length) == 0) return true;
-        extensions += length;
-        if (extensions[0] == ',') ++extensions;
-    }
-
-    return false;
-}
-
 status_t MediaScanner::doProcessDirectory(
-        char *path, int pathRemaining, const char *extensions,
-        MediaScannerClient &client, ExceptionCheck exceptionCheck,
-        void *exceptionEnv) {
+        char *path, int pathRemaining, MediaScannerClient &client,
+        ExceptionCheck exceptionCheck, void *exceptionEnv) {
     // place to copy file or directory name
     char* fileSpot = path + strlen(path);
     struct dirent* entry;
@@ -133,6 +113,13 @@
             continue;
         }
 
+        int nameLength = strlen(name);
+        if (nameLength + 1 > pathRemaining) {
+            // path too long!
+            continue;
+        }
+        strcpy(fileSpot, name);
+
         int type = entry->d_type;
         if (type == DT_UNKNOWN) {
             // If the type is unknown, stat() the file instead.
@@ -150,29 +137,20 @@
             }
         }
         if (type == DT_REG || type == DT_DIR) {
-            int nameLength = strlen(name);
-            bool isDirectory = (type == DT_DIR);
-
-            if (nameLength > pathRemaining || (isDirectory && nameLength + 1 > pathRemaining)) {
-                // path too long!
-                continue;
-            }
-
-            strcpy(fileSpot, name);
-            if (isDirectory) {
+            if (type == DT_DIR) {
                 // ignore directories with a name that starts with '.'
                 // for example, the Mac ".Trashes" directory
                 if (name[0] == '.') continue;
 
                 strcat(fileSpot, "/");
-                int err = doProcessDirectory(path, pathRemaining - nameLength - 1, extensions, client, exceptionCheck, exceptionEnv);
+                int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client, exceptionCheck, exceptionEnv);
                 if (err) {
                     // pass exceptions up - ignore other errors
                     if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
                     LOGE("Error processing '%s' - skipping\n", path);
                     continue;
                 }
-            } else if (fileMatchesExtension(path, extensions)) {
+            } else {
                 struct stat statbuf;
                 stat(path, &statbuf);
                 if (statbuf.st_size > 0) {
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index cc41e66..34e41a1 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -172,16 +172,6 @@
     return INVALID_OPERATION;
 }
 
-status_t MediaPlayer::suspend() {
-    Mutex::Autolock _l(mLock);
-    return mPlayer->suspend();
-}
-
-status_t MediaPlayer::resume() {
-    Mutex::Autolock _l(mLock);
-    return mPlayer->resume();
-}
-
 status_t MediaPlayer::setMetadataFilter(const Parcel& filter)
 {
     LOGD("setMetadataFilter");
@@ -207,10 +197,15 @@
     LOGV("setVideoSurface");
     Mutex::Autolock _l(mLock);
     if (mPlayer == 0) return NO_INIT;
-    if (surface != NULL)
-        return  mPlayer->setVideoSurface(surface->getISurface());
-    else
-        return  mPlayer->setVideoSurface(NULL);
+
+    status_t err = mPlayer->setVideoISurface(
+            surface == NULL ? NULL : surface->getISurface());
+
+    if (err != OK) {
+        return err;
+    }
+
+    return mPlayer->setVideoSurface(surface);
 }
 
 // must call with lock held
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index e20e3ba..fd575fe 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -65,7 +65,7 @@
         return INVALID_OPERATION;
     }
 
-    status_t ret = mMediaRecorder->setPreviewSurface(surface->getISurface());
+    status_t ret = mMediaRecorder->setPreviewSurface(surface);
     if (OK != ret) {
         LOGV("setPreviewSurface failed: %d", ret);
         mCurrentState = MEDIA_RECORDER_ERROR;
@@ -308,6 +308,32 @@
     return ret;
 }
 
+status_t MediaRecorder::setOutputFileAuxiliary(int fd)
+{
+    LOGV("setOutputFileAuxiliary(%d)", fd);
+    if(mMediaRecorder == NULL) {
+        LOGE("media recorder is not initialized yet");
+        return INVALID_OPERATION;
+    }
+    if (mIsAuxiliaryOutputFileSet) {
+        LOGE("output file has already been set");
+        return INVALID_OPERATION;
+    }
+    if (!(mCurrentState & MEDIA_RECORDER_DATASOURCE_CONFIGURED)) {
+        LOGE("setOutputFile called in an invalid state(%d)", mCurrentState);
+        return INVALID_OPERATION;
+    }
+
+    status_t ret = mMediaRecorder->setOutputFileAuxiliary(fd);
+    if (OK != ret) {
+        LOGV("setOutputFileAuxiliary failed: %d", ret);
+        mCurrentState = MEDIA_RECORDER_ERROR;
+        return ret;
+    }
+    mIsAuxiliaryOutputFileSet = true;
+    return ret;
+}
+
 status_t MediaRecorder::setVideoSize(int width, int height)
 {
     LOGV("setVideoSize(%d, %d)", width, height);
@@ -571,6 +597,7 @@
     mIsAudioEncoderSet = false;
     mIsVideoEncoderSet = false;
     mIsOutputFileSet   = false;
+    mIsAuxiliaryOutputFileSet = false;
 }
 
 // Release should be OK in any state
@@ -643,4 +670,3 @@
 }
 
 }; // namespace android
-
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index c43e9bb..bb86e05 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -891,7 +891,15 @@
     return mStatus;
 }
 
-status_t MediaPlayerService::Client::setVideoSurface(const sp<ISurface>& surface)
+status_t MediaPlayerService::Client::setVideoISurface(const sp<ISurface>& surface)
+{
+    LOGV("[%d] setVideoISurface(%p)", mConnId, surface.get());
+    sp<MediaPlayerBase> p = getPlayer();
+    if (p == 0) return UNKNOWN_ERROR;
+    return p->setVideoISurface(surface);
+}
+
+status_t MediaPlayerService::Client::setVideoSurface(const sp<Surface>& surface)
 {
     LOGV("[%d] setVideoSurface(%p)", mConnId, surface.get());
     sp<MediaPlayerBase> p = getPlayer();
@@ -966,20 +974,6 @@
     return OK;
 }
 
-status_t MediaPlayerService::Client::suspend() {
-    sp<MediaPlayerBase> p = getPlayer();
-    if (p == 0) return UNKNOWN_ERROR;
-
-    return p->suspend();
-}
-
-status_t MediaPlayerService::Client::resume() {
-    sp<MediaPlayerBase> p = getPlayer();
-    if (p == 0) return UNKNOWN_ERROR;
-
-    return p->resume();
-}
-
 status_t MediaPlayerService::Client::prepareAsync()
 {
     LOGV("[%d] prepareAsync", mConnId);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 4492e20..e197cde 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -206,7 +206,8 @@
 
         // IMediaPlayer interface
         virtual void            disconnect();
-        virtual status_t        setVideoSurface(const sp<ISurface>& surface);
+        virtual status_t        setVideoISurface(const sp<ISurface>& surface);
+        virtual status_t        setVideoSurface(const sp<Surface>& surface);
         virtual status_t        prepareAsync();
         virtual status_t        start();
         virtual status_t        stop();
@@ -224,8 +225,6 @@
         virtual status_t        getMetadata(bool update_only,
                                             bool apply_filter,
                                             Parcel *reply);
-        virtual status_t        suspend();
-        virtual status_t        resume();
         virtual status_t        setAuxEffectSendLevel(float level);
         virtual status_t        attachAuxEffect(int effectId);
 
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 19915f1..be6a8be 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -70,7 +70,7 @@
     return mRecorder->setCamera(camera);
 }
 
-status_t MediaRecorderClient::setPreviewSurface(const sp<ISurface>& surface)
+status_t MediaRecorderClient::setPreviewSurface(const sp<Surface>& surface)
 {
     LOGV("setPreviewSurface");
     Mutex::Autolock lock(mLock);
@@ -164,6 +164,17 @@
     return mRecorder->setOutputFile(fd, offset, length);
 }
 
+status_t MediaRecorderClient::setOutputFileAuxiliary(int fd)
+{
+    LOGV("setOutputFileAuxiliary(%d)", fd);
+    Mutex::Autolock lock(mLock);
+    if (mRecorder == NULL) {
+        LOGE("recorder is not initialized");
+        return NO_INIT;
+    }
+    return mRecorder->setOutputFileAuxiliary(fd);
+}
+
 status_t MediaRecorderClient::setVideoSize(int width, int height)
 {
     LOGV("setVideoSize(%dx%d)", width, height);
@@ -337,4 +348,3 @@
 }
 
 }; // namespace android
-
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index 1d1913d..fded98e 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -29,7 +29,7 @@
 {
 public:
     virtual     status_t        setCamera(const sp<ICamera>& camera);
-    virtual     status_t        setPreviewSurface(const sp<ISurface>& surface);
+    virtual     status_t        setPreviewSurface(const sp<Surface>& surface);
     virtual     status_t        setVideoSource(int vs);
     virtual     status_t        setAudioSource(int as);
     virtual     status_t        setOutputFormat(int of);
@@ -37,6 +37,7 @@
     virtual     status_t        setAudioEncoder(int ae);
     virtual     status_t        setOutputFile(const char* path);
     virtual     status_t        setOutputFile(int fd, int64_t offset, int64_t length);
+    virtual     status_t        setOutputFileAuxiliary(int fd);
     virtual     status_t        setVideoSize(int width, int height);
     virtual     status_t        setVideoFrameRate(int frames_per_second);
     virtual     status_t        setParameters(const String8& params);
@@ -66,4 +67,3 @@
 }; // namespace android
 
 #endif // ANDROID_MEDIARECORDERCLIENT_H
-
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
index 4a60ece..06e4b70 100644
--- a/media/libmediaplayerservice/MidiFile.h
+++ b/media/libmediaplayerservice/MidiFile.h
@@ -35,7 +35,8 @@
             const char* path, const KeyedVector<String8, String8> *headers);
 
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length);
-    virtual status_t    setVideoSurface(const sp<ISurface>& surface) { return UNKNOWN_ERROR; }
+    virtual status_t    setVideoISurface(const sp<ISurface>& surface) { return UNKNOWN_ERROR; }
+    virtual status_t    setVideoSurface(const sp<Surface>& surface) { return UNKNOWN_ERROR; }
     virtual status_t    prepare();
     virtual status_t    prepareAsync();
     virtual status_t    start();
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index 6bded09..e0957f6 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -44,13 +44,20 @@
     return mPlayer->setDataSource(dup(fd), offset, length);
 }
 
-status_t StagefrightPlayer::setVideoSurface(const sp<ISurface> &surface) {
-    LOGV("setVideoSurface");
+status_t StagefrightPlayer::setVideoISurface(const sp<ISurface> &surface) {
+    LOGV("setVideoISurface");
 
     mPlayer->setISurface(surface);
     return OK;
 }
 
+status_t StagefrightPlayer::setVideoSurface(const sp<Surface> &surface) {
+    LOGV("setVideoSurface");
+
+    mPlayer->setSurface(surface);
+    return OK;
+}
+
 status_t StagefrightPlayer::prepare() {
     return mPlayer->prepare();
 }
@@ -140,16 +147,6 @@
     return STAGEFRIGHT_PLAYER;
 }
 
-status_t StagefrightPlayer::suspend() {
-    LOGV("suspend");
-    return mPlayer->suspend();
-}
-
-status_t StagefrightPlayer::resume() {
-    LOGV("resume");
-    return mPlayer->resume();
-}
-
 status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) {
     return INVALID_OPERATION;
 }
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
index 781eb44..3899447 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.h
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -35,7 +35,8 @@
             const char *url, const KeyedVector<String8, String8> *headers);
 
     virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
-    virtual status_t setVideoSurface(const sp<ISurface> &surface);
+    virtual status_t setVideoISurface(const sp<ISurface> &surface);
+    virtual status_t setVideoSurface(const sp<Surface> &surface);
     virtual status_t prepare();
     virtual status_t prepareAsync();
     virtual status_t start();
@@ -50,8 +51,6 @@
     virtual player_type playerType();
     virtual status_t invoke(const Parcel &request, Parcel *reply);
     virtual void setAudioSink(const sp<AudioSink> &audioSink);
-    virtual status_t suspend();
-    virtual status_t resume();
 
     virtual status_t getMetadata(
             const media::Metadata::Filter& ids, Parcel *records);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index d37d83d..6a25dc5 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -20,10 +20,12 @@
 
 #include "StagefrightRecorder.h"
 
-#include <binder/IPCThreadState.h>
 #include <media/stagefright/AudioSource.h>
 #include <media/stagefright/AMRWriter.h>
 #include <media/stagefright/CameraSource.h>
+#include <media/stagefright/VideoSourceDownSampler.h>
+#include <media/stagefright/CameraSourceTimeLapse.h>
+#include <media/stagefright/MediaSourceSplitter.h>
 #include <media/stagefright/MPEG2TSWriter.h>
 #include <media/stagefright/MPEG4Writer.h>
 #include <media/stagefright/MediaDebug.h>
@@ -33,21 +35,20 @@
 #include <media/stagefright/OMXCodec.h>
 #include <media/MediaProfiles.h>
 #include <camera/ICamera.h>
-#include <camera/Camera.h>
 #include <camera/CameraParameters.h>
-#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
 #include <utils/Errors.h>
 #include <sys/types.h>
-#include <unistd.h>
 #include <ctype.h>
+#include <unistd.h>
 
 #include "ARTPWriter.h"
 
 namespace android {
 
 StagefrightRecorder::StagefrightRecorder()
-    : mWriter(NULL),
-      mOutputFd(-1) {
+    : mWriter(NULL), mWriterAux(NULL),
+      mOutputFd(-1), mOutputFdAux(-1) {
 
     LOGV("Constructor");
     reset();
@@ -164,7 +165,8 @@
 
 status_t StagefrightRecorder::setVideoFrameRate(int frames_per_second) {
     LOGV("setVideoFrameRate: %d", frames_per_second);
-    if (frames_per_second <= 0 || frames_per_second > 30) {
+    if ((frames_per_second <= 0 && frames_per_second != -1) ||
+        frames_per_second > 120) {
         LOGE("Invalid video frame rate: %d", frames_per_second);
         return BAD_VALUE;
     }
@@ -182,26 +184,11 @@
         return BAD_VALUE;
     }
 
-    int64_t token = IPCThreadState::self()->clearCallingIdentity();
-    mFlags &= ~FLAGS_HOT_CAMERA;
-    mCamera = Camera::create(camera);
-    if (mCamera == 0) {
-        LOGE("Unable to connect to camera");
-        IPCThreadState::self()->restoreCallingIdentity(token);
-        return -EBUSY;
-    }
-
-    LOGV("Connected to camera");
-    if (mCamera->previewEnabled()) {
-        LOGV("camera is hot");
-        mFlags |= FLAGS_HOT_CAMERA;
-    }
-    IPCThreadState::self()->restoreCallingIdentity(token);
-
+    mCamera = camera;
     return OK;
 }
 
-status_t StagefrightRecorder::setPreviewSurface(const sp<ISurface> &surface) {
+status_t StagefrightRecorder::setPreviewSurface(const sp<Surface> &surface) {
     LOGV("setPreviewSurface: %p", surface.get());
     mPreviewSurface = surface;
 
@@ -235,6 +222,24 @@
     return OK;
 }
 
+status_t StagefrightRecorder::setOutputFileAuxiliary(int fd) {
+    LOGV("setOutputFileAuxiliary: %d", fd);
+
+    if (fd < 0) {
+        LOGE("Invalid file descriptor: %d", fd);
+        return -EBADF;
+    }
+
+    mCaptureAuxVideo = true;
+
+    if (mOutputFdAux >= 0) {
+        ::close(mOutputFdAux);
+    }
+    mOutputFdAux = dup(fd);
+
+    return OK;
+}
+
 // Attempt to parse an int64 literal optionally surrounded by whitespace,
 // returns true on success, false otherwise.
 static bool safe_strtoi64(const char *s, int64_t *val) {
@@ -474,6 +479,68 @@
     return OK;
 }
 
+status_t StagefrightRecorder::setParamTimeLapseEnable(int32_t timeLapseEnable) {
+    LOGV("setParamTimeLapseEnable: %d", timeLapseEnable);
+
+    if(timeLapseEnable == 0) {
+        mCaptureTimeLapse = false;
+    } else if (timeLapseEnable == 1) {
+        mCaptureTimeLapse = true;
+    } else {
+        return BAD_VALUE;
+    }
+    return OK;
+}
+
+status_t StagefrightRecorder::setParamTimeBetweenTimeLapseFrameCapture(int64_t timeUs) {
+    LOGV("setParamTimeBetweenTimeLapseFrameCapture: %lld us", timeUs);
+
+    // Not allowing time more than a day
+    if (timeUs <= 0 || timeUs > 86400*1E6) {
+        LOGE("Time between time lapse frame capture (%lld) is out of range [0, 1 Day]", timeUs);
+        return BAD_VALUE;
+    }
+
+    mTimeBetweenTimeLapseFrameCaptureUs = timeUs;
+    return OK;
+}
+
+status_t StagefrightRecorder::setParamAuxVideoWidth(int32_t width) {
+    LOGV("setParamAuxVideoWidth : %d", width);
+
+    if (width <= 0) {
+        LOGE("Width (%d) is not positive", width);
+        return BAD_VALUE;
+    }
+
+    mAuxVideoWidth = width;
+    return OK;
+}
+
+status_t StagefrightRecorder::setParamAuxVideoHeight(int32_t height) {
+    LOGV("setParamAuxVideoHeight : %d", height);
+
+    if (height <= 0) {
+        LOGE("Height (%d) is not positive", height);
+        return BAD_VALUE;
+    }
+
+    mAuxVideoHeight = height;
+    return OK;
+}
+
+status_t StagefrightRecorder::setParamAuxVideoEncodingBitRate(int32_t bitRate) {
+    LOGV("StagefrightRecorder::setParamAuxVideoEncodingBitRate: %d", bitRate);
+
+    if (bitRate <= 0) {
+        LOGE("Invalid video encoding bit rate: %d", bitRate);
+        return BAD_VALUE;
+    }
+
+    mAuxVideoBitRate = bitRate;
+    return OK;
+}
+
 status_t StagefrightRecorder::setParameter(
         const String8 &key, const String8 &value) {
     LOGV("setParameter: key (%s) => value (%s)", key.string(), value.string());
@@ -557,6 +624,32 @@
         if (safe_strtoi32(value.string(), &timeScale)) {
             return setParamVideoTimeScale(timeScale);
         }
+    } else if (key == "time-lapse-enable") {
+        int32_t timeLapseEnable;
+        if (safe_strtoi32(value.string(), &timeLapseEnable)) {
+            return setParamTimeLapseEnable(timeLapseEnable);
+        }
+    } else if (key == "time-between-time-lapse-frame-capture") {
+        int64_t timeBetweenTimeLapseFrameCaptureMs;
+        if (safe_strtoi64(value.string(), &timeBetweenTimeLapseFrameCaptureMs)) {
+            return setParamTimeBetweenTimeLapseFrameCapture(
+                    1000LL * timeBetweenTimeLapseFrameCaptureMs);
+        }
+    } else if (key == "video-aux-param-width") {
+        int32_t auxWidth;
+        if (safe_strtoi32(value.string(), &auxWidth)) {
+            return setParamAuxVideoWidth(auxWidth);
+        }
+    } else if (key == "video-aux-param-height") {
+        int32_t auxHeight;
+        if (safe_strtoi32(value.string(), &auxHeight)) {
+            return setParamAuxVideoHeight(auxHeight);
+        }
+    } else if (key == "video-aux-param-encoding-bitrate") {
+        int32_t auxVideoBitRate;
+        if (safe_strtoi32(value.string(), &auxVideoBitRate)) {
+            return setParamAuxVideoEncodingBitRate(auxVideoBitRate);
+        }
     } else {
         LOGE("setParameter: failed to find key %s", key.string());
     }
@@ -790,7 +883,14 @@
     if (mAudioSource != AUDIO_SOURCE_LIST_END) {
         source = createAudioSource();
     } else {
-        status_t err = setupVideoEncoder(&source);
+
+        sp<CameraSource> cameraSource;
+        status_t err = setupCameraSource(&cameraSource);
+        if (err != OK) {
+            return err;
+        }
+
+        err = setupVideoEncoder(cameraSource, mVideoBitRate, &source);
         if (err != OK) {
             return err;
         }
@@ -826,8 +926,14 @@
             return ERROR_UNSUPPORTED;
         }
 
+        sp<CameraSource> cameraSource;
+        status_t err = setupCameraSource(&cameraSource);
+        if (err != OK) {
+            return err;
+        }
+
         sp<MediaSource> encoder;
-        status_t err = setupVideoEncoder(&encoder);
+        err = setupVideoEncoder(cameraSource, mVideoBitRate, &encoder);
 
         if (err != OK) {
             return err;
@@ -855,7 +961,7 @@
                         "enc.vid.fps.min", mVideoEncoder);
     int maxFrameRate = mEncoderProfiles->getVideoEncoderParamByName(
                         "enc.vid.fps.max", mVideoEncoder);
-    if (mFrameRate < minFrameRate) {
+    if (mFrameRate < minFrameRate && mFrameRate != -1) {
         LOGW("Intended video encoding frame rate (%d fps) is too small"
              " and will be set to (%d fps)", mFrameRate, minFrameRate);
         mFrameRate = minFrameRate;
@@ -900,57 +1006,15 @@
     }
 }
 
-status_t StagefrightRecorder::setupCameraSource() {
-    clipVideoBitRate();
-    clipVideoFrameRate();
-    clipVideoFrameWidth();
-    clipVideoFrameHeight();
-
-    int64_t token = IPCThreadState::self()->clearCallingIdentity();
-    if (mCamera == 0) {
-        mCamera = Camera::connect(mCameraId);
-        if (mCamera == 0) {
-            LOGE("Camera connection could not be established.");
-            return -EBUSY;
-        }
-        mFlags &= ~FLAGS_HOT_CAMERA;
-        mCamera->lock();
+status_t StagefrightRecorder::checkVideoEncoderCapabilities() {
+    if (!mCaptureTimeLapse) {
+        // Dont clip for time lapse capture as encoder will have enough
+        // time to encode because of slow capture rate of time lapse.
+        clipVideoBitRate();
+        clipVideoFrameRate();
+        clipVideoFrameWidth();
+        clipVideoFrameHeight();
     }
-
-    // Set the actual video recording frame size
-    CameraParameters params(mCamera->getParameters());
-    params.setPreviewSize(mVideoWidth, mVideoHeight);
-    params.setPreviewFrameRate(mFrameRate);
-    String8 s = params.flatten();
-    if (OK != mCamera->setParameters(s)) {
-        LOGE("Could not change settings."
-             " Someone else is using camera %d?", mCameraId);
-        return -EBUSY;
-    }
-    CameraParameters newCameraParams(mCamera->getParameters());
-
-    // Check on video frame size
-    int frameWidth = 0, frameHeight = 0;
-    newCameraParams.getPreviewSize(&frameWidth, &frameHeight);
-    if (frameWidth  < 0 || frameWidth  != mVideoWidth ||
-        frameHeight < 0 || frameHeight != mVideoHeight) {
-        LOGE("Failed to set the video frame size to %dx%d",
-                mVideoWidth, mVideoHeight);
-        IPCThreadState::self()->restoreCallingIdentity(token);
-        return UNKNOWN_ERROR;
-    }
-
-    // Check on video frame rate
-    int frameRate = newCameraParams.getPreviewFrameRate();
-    if (frameRate < 0 || (frameRate - mFrameRate) != 0) {
-        LOGE("Failed to set frame rate to %d fps. The actual "
-             "frame rate is %d", mFrameRate, frameRate);
-    }
-
-    // This CHECK is good, since we just passed the lock/unlock
-    // check earlier by calling mCamera->setParameters().
-    CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface));
-    IPCThreadState::self()->restoreCallingIdentity(token);
     return OK;
 }
 
@@ -971,17 +1035,55 @@
     }
 }
 
-status_t StagefrightRecorder::setupVideoEncoder(sp<MediaSource> *source) {
+status_t StagefrightRecorder::setupCameraSource(
+        sp<CameraSource> *cameraSource) {
+    status_t err = OK;
+    if ((err = checkVideoEncoderCapabilities()) != OK) {
+        return err;
+    }
+    Size videoSize;
+    videoSize.width = mVideoWidth;
+    videoSize.height = mVideoHeight;
+    if (mCaptureTimeLapse) {
+        mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(
+                mCamera, mCameraId,
+                videoSize, mFrameRate, mPreviewSurface,
+                mTimeBetweenTimeLapseFrameCaptureUs);
+        *cameraSource = mCameraSourceTimeLapse;
+    } else {
+        *cameraSource = CameraSource::CreateFromCamera(
+                mCamera, mCameraId, videoSize, mFrameRate,
+                mPreviewSurface, true /*storeMetaDataInVideoBuffers*/);
+    }
+    CHECK(*cameraSource != NULL);
+
+    // When frame rate is not set, the actual frame rate will be set to
+    // the current frame rate being used.
+    if (mFrameRate == -1) {
+        int32_t frameRate = 0;
+        CHECK ((*cameraSource)->getFormat()->findInt32(
+                    kKeySampleRate, &frameRate));
+        LOGI("Frame rate is not explicitly set. Use the current frame "
+             "rate (%d fps)", frameRate);
+        mFrameRate = frameRate;
+    }
+
+    CHECK(mFrameRate != -1);
+
+    mIsMetaDataStoredInVideoBuffers =
+        (*cameraSource)->isMetaDataStoredInVideoBuffers();
+
+    return OK;
+}
+
+status_t StagefrightRecorder::setupVideoEncoder(
+        sp<MediaSource> cameraSource,
+        int32_t videoBitRate,
+        sp<MediaSource> *source) {
     source->clear();
 
-    status_t err = setupCameraSource();
-    if (err != OK) return err;
-
-    sp<CameraSource> cameraSource = CameraSource::CreateFromCamera(mCamera);
-    CHECK(cameraSource != NULL);
-
     sp<MetaData> enc_meta = new MetaData;
-    enc_meta->setInt32(kKeyBitRate, mVideoBitRate);
+    enc_meta->setInt32(kKeyBitRate, videoBitRate);
     enc_meta->setInt32(kKeySampleRate, mFrameRate);
 
     switch (mVideoEncoder) {
@@ -1025,14 +1127,27 @@
     }
     if (mVideoEncoderLevel != -1) {
         enc_meta->setInt32(kKeyVideoLevel, mVideoEncoderLevel);
+    } else if (mCaptureTimeLapse) {
+        // Check if we are using high resolution and/or high bitrate and
+        // set appropriate level for the software AVCEncoder.
+        if ((width * height >= 921600) // 720p
+                || (videoBitRate >= 20000000)) {
+            enc_meta->setInt32(kKeyVideoLevel, 50);
+        }
     }
 
     OMXClient client;
     CHECK_EQ(client.connect(), OK);
 
+    // Use software codec for time lapse
+    uint32_t encoder_flags = (mCaptureTimeLapse) ? OMXCodec::kPreferSoftwareCodecs : 0;
+    if (mIsMetaDataStoredInVideoBuffers) {
+        encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers;
+    }
     sp<MediaSource> encoder = OMXCodec::Create(
             client.interface(), enc_meta,
-            true /* createEncoder */, cameraSource);
+            true /* createEncoder */, cameraSource,
+            NULL, encoder_flags);
     if (encoder == NULL) {
         return UNKNOWN_ERROR;
     }
@@ -1063,51 +1178,147 @@
     return OK;
 }
 
-status_t StagefrightRecorder::startMPEG4Recording() {
-    int32_t totalBitRate = 0;
+status_t StagefrightRecorder::setupMPEG4Recording(
+        bool useSplitCameraSource,
+        int outputFd,
+        int32_t videoWidth, int32_t videoHeight,
+        int32_t videoBitRate,
+        int32_t *totalBitRate,
+        sp<MediaWriter> *mediaWriter) {
+    mediaWriter->clear();
+    *totalBitRate = 0;
     status_t err = OK;
-    sp<MediaWriter> writer = new MPEG4Writer(dup(mOutputFd));
+    sp<MediaWriter> writer = new MPEG4Writer(dup(outputFd));
 
     // Add audio source first if it exists
-    if (mAudioSource != AUDIO_SOURCE_LIST_END) {
+    if (!mCaptureTimeLapse && (mAudioSource != AUDIO_SOURCE_LIST_END)) {
         err = setupAudioEncoder(writer);
         if (err != OK) return err;
-        totalBitRate += mAudioBitRate;
+        *totalBitRate += mAudioBitRate;
     }
     if (mVideoSource == VIDEO_SOURCE_DEFAULT
             || mVideoSource == VIDEO_SOURCE_CAMERA) {
+
+        sp<MediaSource> cameraMediaSource;
+        if (useSplitCameraSource) {
+            LOGV("Using Split camera source");
+            cameraMediaSource = mCameraSourceSplitter->createClient();
+        } else {
+            sp<CameraSource> cameraSource;
+            err = setupCameraSource(&cameraSource);
+            cameraMediaSource = cameraSource;
+        }
+        if ((videoWidth != mVideoWidth) || (videoHeight != mVideoHeight)) {
+            // Use downsampling from the original source.
+            cameraMediaSource =
+                new VideoSourceDownSampler(cameraMediaSource, videoWidth, videoHeight);
+        }
+        if (err != OK) {
+            return err;
+        }
+
         sp<MediaSource> encoder;
-        err = setupVideoEncoder(&encoder);
-        if (err != OK) return err;
+        err = setupVideoEncoder(cameraMediaSource, videoBitRate, &encoder);
+        if (err != OK) {
+            return err;
+        }
+
         writer->addSource(encoder);
-        totalBitRate += mVideoBitRate;
+        *totalBitRate += videoBitRate;
     }
 
     if (mInterleaveDurationUs > 0) {
         reinterpret_cast<MPEG4Writer *>(writer.get())->
             setInterleaveDuration(mInterleaveDurationUs);
     }
-
     if (mMaxFileDurationUs != 0) {
         writer->setMaxFileDuration(mMaxFileDurationUs);
     }
     if (mMaxFileSizeBytes != 0) {
         writer->setMaxFileSize(mMaxFileSizeBytes);
     }
-    sp<MetaData> meta = new MetaData;
-    meta->setInt64(kKeyTime, systemTime() / 1000);
-    meta->setInt32(kKeyFileType, mOutputFormat);
-    meta->setInt32(kKeyBitRate, totalBitRate);
-    meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
+
+    writer->setListener(mListener);
+    *mediaWriter = writer;
+    return OK;
+}
+
+void StagefrightRecorder::setupMPEG4MetaData(int64_t startTimeUs, int32_t totalBitRate,
+        sp<MetaData> *meta) {
+    (*meta)->setInt64(kKeyTime, startTimeUs);
+    (*meta)->setInt32(kKeyFileType, mOutputFormat);
+    (*meta)->setInt32(kKeyBitRate, totalBitRate);
+    (*meta)->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
     if (mMovieTimeScale > 0) {
-        meta->setInt32(kKeyTimeScale, mMovieTimeScale);
+        (*meta)->setInt32(kKeyTimeScale, mMovieTimeScale);
     }
     if (mTrackEveryTimeDurationUs > 0) {
-        meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
+        (*meta)->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
     }
-    writer->setListener(mListener);
-    mWriter = writer;
-    return mWriter->start(meta.get());
+}
+
+status_t StagefrightRecorder::startMPEG4Recording() {
+    if (mCaptureAuxVideo) {
+        if (!mCaptureTimeLapse) {
+            LOGE("Auxiliary video can be captured only in time lapse mode");
+            return UNKNOWN_ERROR;
+        }
+        LOGV("Creating MediaSourceSplitter");
+        sp<CameraSource> cameraSource;
+        status_t err = setupCameraSource(&cameraSource);
+        if (err != OK) {
+            return err;
+        }
+        mCameraSourceSplitter = new MediaSourceSplitter(cameraSource);
+    } else {
+        mCameraSourceSplitter = NULL;
+    }
+
+    int32_t totalBitRate;
+    status_t err = setupMPEG4Recording(mCaptureAuxVideo,
+            mOutputFd, mVideoWidth, mVideoHeight,
+            mVideoBitRate, &totalBitRate, &mWriter);
+    if (err != OK) {
+        return err;
+    }
+
+    int64_t startTimeUs = systemTime() / 1000;
+    sp<MetaData> meta = new MetaData;
+    setupMPEG4MetaData(startTimeUs, totalBitRate, &meta);
+
+    err = mWriter->start(meta.get());
+    if (err != OK) {
+        return err;
+    }
+
+    if (mCaptureAuxVideo) {
+        CHECK(mOutputFdAux >= 0);
+        if (mWriterAux != NULL) {
+            LOGE("Auxiliary File writer is not avaialble");
+            return UNKNOWN_ERROR;
+        }
+        if ((mAuxVideoWidth > mVideoWidth) || (mAuxVideoHeight > mVideoHeight) ||
+                ((mAuxVideoWidth == mVideoWidth) && mAuxVideoHeight == mVideoHeight)) {
+            LOGE("Auxiliary video size (%d x %d) same or larger than the main video size (%d x %d)",
+                    mAuxVideoWidth, mAuxVideoHeight, mVideoWidth, mVideoHeight);
+            return UNKNOWN_ERROR;
+        }
+
+        int32_t totalBitrateAux;
+        err = setupMPEG4Recording(mCaptureAuxVideo,
+                mOutputFdAux, mAuxVideoWidth, mAuxVideoHeight,
+                mAuxVideoBitRate, &totalBitrateAux, &mWriterAux);
+        if (err != OK) {
+            return err;
+        }
+
+        sp<MetaData> metaAux = new MetaData;
+        setupMPEG4MetaData(startTimeUs, totalBitrateAux, &metaAux);
+
+        return mWriterAux->start(metaAux.get());
+    }
+
+    return OK;
 }
 
 status_t StagefrightRecorder::pause() {
@@ -1116,35 +1327,50 @@
         return UNKNOWN_ERROR;
     }
     mWriter->pause();
+
+    if (mCaptureAuxVideo) {
+        if (mWriterAux == NULL) {
+            return UNKNOWN_ERROR;
+        }
+        mWriterAux->pause();
+    }
+
     return OK;
 }
 
 status_t StagefrightRecorder::stop() {
     LOGV("stop");
     status_t err = OK;
+
+    if (mCaptureTimeLapse && mCameraSourceTimeLapse != NULL) {
+        mCameraSourceTimeLapse->startQuickReadReturns();
+        mCameraSourceTimeLapse = NULL;
+    }
+
+    if (mCaptureAuxVideo) {
+        if (mWriterAux != NULL) {
+            mWriterAux->stop();
+            mWriterAux.clear();
+        }
+    }
+
     if (mWriter != NULL) {
         err = mWriter->stop();
         mWriter.clear();
     }
 
-    if (mCamera != 0) {
-        LOGV("Disconnect camera");
-        int64_t token = IPCThreadState::self()->clearCallingIdentity();
-        if ((mFlags & FLAGS_HOT_CAMERA) == 0) {
-            LOGV("Camera was cold when we started, stopping preview");
-            mCamera->stopPreview();
-        }
-        mCamera->unlock();
-        mCamera.clear();
-        IPCThreadState::self()->restoreCallingIdentity(token);
-        mFlags = 0;
-    }
-
     if (mOutputFd >= 0) {
         ::close(mOutputFd);
         mOutputFd = -1;
     }
 
+    if (mCaptureAuxVideo) {
+        if (mOutputFdAux >= 0) {
+            ::close(mOutputFdAux);
+            mOutputFdAux = -1;
+        }
+    }
+
     return err;
 }
 
@@ -1169,8 +1395,11 @@
     mVideoEncoder  = VIDEO_ENCODER_H263;
     mVideoWidth    = 176;
     mVideoHeight   = 144;
-    mFrameRate     = 20;
+    mAuxVideoWidth    = 176;
+    mAuxVideoHeight   = 144;
+    mFrameRate     = -1;
     mVideoBitRate  = 192000;
+    mAuxVideoBitRate = 192000;
     mSampleRate    = 8000;
     mAudioChannels = 1;
     mAudioBitRate  = 12200;
@@ -1187,10 +1416,16 @@
     mMaxFileDurationUs = 0;
     mMaxFileSizeBytes = 0;
     mTrackEveryTimeDurationUs = 0;
+    mCaptureTimeLapse = false;
+    mTimeBetweenTimeLapseFrameCaptureUs = -1;
+    mCaptureAuxVideo = false;
+    mCameraSourceSplitter = NULL;
+    mCameraSourceTimeLapse = NULL;
+    mIsMetaDataStoredInVideoBuffers = false;
     mEncoderProfiles = MediaProfiles::getInstance();
 
     mOutputFd = -1;
-    mFlags = 0;
+    mOutputFdAux = -1;
 
     return OK;
 }
@@ -1227,6 +1462,8 @@
     snprintf(buffer, SIZE, "   Recorder: %p\n", this);
     snprintf(buffer, SIZE, "   Output file (fd %d):\n", mOutputFd);
     result.append(buffer);
+    snprintf(buffer, SIZE, "   Output file Auxiliary (fd %d):\n", mOutputFdAux);
+    result.append(buffer);
     snprintf(buffer, SIZE, "     File format: %d\n", mOutputFormat);
     result.append(buffer);
     snprintf(buffer, SIZE, "     Max file size (bytes): %lld\n", mMaxFileSizeBytes);
@@ -1259,8 +1496,6 @@
     result.append(buffer);
     snprintf(buffer, SIZE, "     Camera Id: %d\n", mCameraId);
     result.append(buffer);
-    snprintf(buffer, SIZE, "     Camera flags: %d\n", mFlags);
-    result.append(buffer);
     snprintf(buffer, SIZE, "     Encoder: %d\n", mVideoEncoder);
     result.append(buffer);
     snprintf(buffer, SIZE, "     Encoder profile: %d\n", mVideoEncoderProfile);
@@ -1271,10 +1506,14 @@
     result.append(buffer);
     snprintf(buffer, SIZE, "     Frame size (pixels): %dx%d\n", mVideoWidth, mVideoHeight);
     result.append(buffer);
+    snprintf(buffer, SIZE, "     Aux Frame size (pixels): %dx%d\n", mAuxVideoWidth, mAuxVideoHeight);
+    result.append(buffer);
     snprintf(buffer, SIZE, "     Frame rate (fps): %d\n", mFrameRate);
     result.append(buffer);
     snprintf(buffer, SIZE, "     Bit rate (bps): %d\n", mVideoBitRate);
     result.append(buffer);
+    snprintf(buffer, SIZE, "     Aux Bit rate (bps): %d\n", mAuxVideoBitRate);
+    result.append(buffer);
     ::write(fd, result.string(), result.size());
     return OK;
 }
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index ad0dfa0..d11d7e0 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -19,13 +19,18 @@
 #define STAGEFRIGHT_RECORDER_H_
 
 #include <media/MediaRecorderBase.h>
+#include <camera/CameraParameters.h>
 #include <utils/String8.h>
 
 namespace android {
 
 class Camera;
+class CameraSource;
+class CameraSourceTimeLapse;
+class MediaSourceSplitter;
 struct MediaSource;
 struct MediaWriter;
+class MetaData;
 struct AudioSource;
 class MediaProfiles;
 
@@ -42,9 +47,10 @@
     virtual status_t setVideoSize(int width, int height);
     virtual status_t setVideoFrameRate(int frames_per_second);
     virtual status_t setCamera(const sp<ICamera>& camera);
-    virtual status_t setPreviewSurface(const sp<ISurface>& surface);
+    virtual status_t setPreviewSurface(const sp<Surface>& surface);
     virtual status_t setOutputFile(const char *path);
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
+    virtual status_t setOutputFileAuxiliary(int fd);
     virtual status_t setParameters(const String8& params);
     virtual status_t setListener(const sp<IMediaRecorderClient>& listener);
     virtual status_t prepare();
@@ -57,15 +63,10 @@
     virtual status_t dump(int fd, const Vector<String16>& args) const;
 
 private:
-    enum CameraFlags {
-        FLAGS_SET_CAMERA = 1L << 0,
-        FLAGS_HOT_CAMERA = 1L << 1,
-    };
-
-    sp<Camera> mCamera;
-    sp<ISurface> mPreviewSurface;
+    sp<ICamera> mCamera;
+    sp<Surface> mPreviewSurface;
     sp<IMediaRecorderClient> mListener;
-    sp<MediaWriter> mWriter;
+    sp<MediaWriter> mWriter, mWriterAux;
     sp<AudioSource> mAudioSourceNode;
 
     audio_source mAudioSource;
@@ -75,8 +76,9 @@
     video_encoder mVideoEncoder;
     bool mUse64BitFileOffset;
     int32_t mVideoWidth, mVideoHeight;
+    int32_t mAuxVideoWidth, mAuxVideoHeight;
     int32_t mFrameRate;
-    int32_t mVideoBitRate;
+    int32_t mVideoBitRate, mAuxVideoBitRate;
     int32_t mAudioBitRate;
     int32_t mAudioChannels;
     int32_t mSampleRate;
@@ -92,21 +94,40 @@
     int64_t mMaxFileDurationUs;
     int64_t mTrackEveryTimeDurationUs;
 
-    String8 mParams;
-    int mOutputFd;
-    int32_t mFlags;
+    bool mCaptureTimeLapse;
+    int64_t mTimeBetweenTimeLapseFrameCaptureUs;
+    bool mCaptureAuxVideo;
+    sp<MediaSourceSplitter> mCameraSourceSplitter;
+    sp<CameraSourceTimeLapse> mCameraSourceTimeLapse;
 
+    String8 mParams;
+    int mOutputFd, mOutputFdAux;
+
+    bool mIsMetaDataStoredInVideoBuffers;
     MediaProfiles *mEncoderProfiles;
 
+    status_t setupMPEG4Recording(
+        bool useSplitCameraSource,
+        int outputFd,
+        int32_t videoWidth, int32_t videoHeight,
+        int32_t videoBitRate,
+        int32_t *totalBitRate,
+        sp<MediaWriter> *mediaWriter);
+    void setupMPEG4MetaData(int64_t startTimeUs, int32_t totalBitRate,
+        sp<MetaData> *meta);
     status_t startMPEG4Recording();
     status_t startAMRRecording();
     status_t startAACRecording();
     status_t startRTPRecording();
     status_t startMPEG2TSRecording();
     sp<MediaSource> createAudioSource();
-    status_t setupCameraSource();
+    status_t checkVideoEncoderCapabilities();
+    status_t setupCameraSource(sp<CameraSource> *cameraSource);
     status_t setupAudioEncoder(const sp<MediaWriter>& writer);
-    status_t setupVideoEncoder(sp<MediaSource> *source);
+    status_t setupVideoEncoder(
+            sp<MediaSource> cameraSource,
+            int32_t videoBitRate,
+            sp<MediaSource> *source);
 
     // Encoding parameter handling utilities
     status_t setParameter(const String8 &key, const String8 &value);
@@ -114,6 +135,11 @@
     status_t setParamAudioNumberOfChannels(int32_t channles);
     status_t setParamAudioSamplingRate(int32_t sampleRate);
     status_t setParamAudioTimeScale(int32_t timeScale);
+    status_t setParamTimeLapseEnable(int32_t timeLapseEnable);
+    status_t setParamTimeBetweenTimeLapseFrameCapture(int64_t timeUs);
+    status_t setParamAuxVideoHeight(int32_t height);
+    status_t setParamAuxVideoWidth(int32_t width);
+    status_t setParamAuxVideoEncodingBitRate(int32_t bitRate);
     status_t setParamVideoEncodingBitRate(int32_t bitRate);
     status_t setParamVideoIFramesInterval(int32_t seconds);
     status_t setParamVideoEncoderProfile(int32_t profile);
@@ -138,4 +164,3 @@
 }  // namespace android
 
 #endif  // STAGEFRIGHT_RECORDER_H_
-
diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h
index 6e6c3cd..5eaf592 100644
--- a/media/libmediaplayerservice/TestPlayerStub.h
+++ b/media/libmediaplayerservice/TestPlayerStub.h
@@ -75,7 +75,10 @@
 
 
     // All the methods below wrap the mPlayer instance.
-    virtual status_t setVideoSurface(const android::sp<android::ISurface>& s)  {
+    virtual status_t setVideoISurface(const android::sp<android::ISurface>& s)  {
+        return mPlayer->setVideoISurface(s);
+    }
+    virtual status_t setVideoSurface(const android::sp<android::Surface>& s)  {
         return mPlayer->setVideoSurface(s);
     }
     virtual status_t prepare() {return mPlayer->prepare();}
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index d674547..562eb60 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -10,6 +10,8 @@
         AudioSource.cpp                   \
         AwesomePlayer.cpp                 \
         CameraSource.cpp                  \
+        CameraSourceTimeLapse.cpp         \
+        VideoSourceDownSampler.cpp        \
         DataSource.cpp                    \
         DRMExtractor.cpp                  \
         ESDS.cpp                          \
@@ -25,6 +27,7 @@
         MediaDefs.cpp                     \
         MediaExtractor.cpp                \
         MediaSource.cpp                   \
+        MediaSourceSplitter.cpp           \
         MetaData.cpp                      \
         NuCachedSource2.cpp               \
         NuHTTPDataSource.cpp              \
@@ -42,6 +45,7 @@
         TimedEventQueue.cpp               \
         Utils.cpp                         \
         WAVExtractor.cpp                  \
+        WVMExtractor.cpp                  \
         avc_utils.cpp                     \
         string.cpp
 
@@ -60,6 +64,7 @@
         libsonivox        \
         libvorbisidec     \
         libsurfaceflinger_client \
+        libstagefright_yuv \
         libcamera_client \
         libdrmframework
 
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index e0bcdc3..4e1969b 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -45,7 +45,7 @@
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/OMXCodec.h>
 
-#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
 
 #include <media/stagefright/foundation/ALooper.h>
 
@@ -101,13 +101,14 @@
             bool previewOnly,
             const char *componentName,
             OMX_COLOR_FORMATTYPE colorFormat,
-            const sp<ISurface> &surface,
+            const sp<ISurface> &isurface,
+            const sp<Surface> &surface,
             size_t displayWidth, size_t displayHeight,
             size_t decodedWidth, size_t decodedHeight)
         : mTarget(NULL),
           mLibHandle(NULL) {
             init(previewOnly, componentName,
-                 colorFormat, surface, displayWidth,
+                 colorFormat, isurface, surface, displayWidth,
                  displayHeight, decodedWidth, decodedHeight);
     }
 
@@ -139,7 +140,8 @@
             bool previewOnly,
             const char *componentName,
             OMX_COLOR_FORMATTYPE colorFormat,
-            const sp<ISurface> &surface,
+            const sp<ISurface> &isurface,
+            const sp<Surface> &surface,
             size_t displayWidth, size_t displayHeight,
             size_t decodedWidth, size_t decodedHeight);
 
@@ -151,7 +153,8 @@
         bool previewOnly,
         const char *componentName,
         OMX_COLOR_FORMATTYPE colorFormat,
-        const sp<ISurface> &surface,
+        const sp<ISurface> &isurface,
+        const sp<Surface> &surface,
         size_t displayWidth, size_t displayHeight,
         size_t decodedWidth, size_t decodedHeight) {
     if (!previewOnly) {
@@ -177,7 +180,7 @@
 
             if (func) {
                 mTarget =
-                    (*func)(surface, componentName, colorFormat,
+                    (*func)(isurface, componentName, colorFormat,
                         displayWidth, displayHeight,
                         decodedWidth, decodedHeight);
             }
@@ -191,6 +194,35 @@
     }
 }
 
+struct AwesomeNativeWindowRenderer : public AwesomeRenderer {
+    AwesomeNativeWindowRenderer(const sp<ANativeWindow> &nativeWindow)
+        : mNativeWindow(nativeWindow) {
+    }
+
+    virtual void render(MediaBuffer *buffer) {
+        status_t err = mNativeWindow->queueBuffer(
+                mNativeWindow.get(), buffer->graphicBuffer().get());
+        if (err != 0) {
+            LOGE("queueBuffer failed with error %s (%d)", strerror(-err),
+                    -err);
+            return;
+        }
+
+        sp<MetaData> metaData = buffer->meta_data();
+        metaData->setInt32(kKeyRendered, 1);
+    }
+
+protected:
+    virtual ~AwesomeNativeWindowRenderer() {}
+
+private:
+    sp<ANativeWindow> mNativeWindow;
+
+    AwesomeNativeWindowRenderer(const AwesomeNativeWindowRenderer &);
+    AwesomeNativeWindowRenderer &operator=(
+            const AwesomeNativeWindowRenderer &);
+};
+
 AwesomePlayer::AwesomePlayer()
     : mQueueStarted(false),
       mTimeSource(NULL),
@@ -384,6 +416,7 @@
     if (mDecryptHandle != NULL) {
             mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
                     Playback::STOP, 0);
+            mDrmManagerClient->closeDecryptSession(mDecryptHandle);
             mDecryptHandle = NULL;
             mDrmManagerClient = NULL;
     }
@@ -394,6 +427,12 @@
             LOGI("interrupting the connection process");
             mConnectingDataSource->disconnect();
         }
+
+        if (mFlags & PREPARING_CONNECTED) {
+            // We are basically done preparing, we're just buffering
+            // enough data to start playback, we can safely interrupt that.
+            finishAsyncPrepare_l();
+        }
     }
 
     while (mFlags & PREPARING) {
@@ -794,8 +833,18 @@
     return OK;
 }
 
+void AwesomePlayer::notifyVideoSize_l() {
+    sp<MetaData> meta = mVideoSource->getFormat();
+
+    int32_t decodedWidth, decodedHeight;
+    CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
+    CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
+
+    notifyListener_l(MEDIA_SET_VIDEO_SIZE, decodedWidth, decodedHeight);
+}
+
 void AwesomePlayer::initRenderer_l() {
-    if (mISurface != NULL) {
+    if (mSurface != NULL || mISurface != NULL) {
         sp<MetaData> meta = mVideoSource->getFormat();
 
         int32_t format;
@@ -812,7 +861,27 @@
         // before creating a new one.
         IPCThreadState::self()->flushCommands();
 
-        if (!strncmp("OMX.", component, 4)) {
+        if (mSurface != NULL) {
+            if (strncmp(component, "OMX.", 4) == 0) {
+                // Hardware decoders avoid the CPU color conversion by decoding
+                // directly to ANativeBuffers, so we must use a renderer that
+                // just pushes those buffers to the ANativeWindow.
+                mVideoRenderer = new AwesomeNativeWindowRenderer(mSurface);
+            } else {
+                // Other decoders are instantiated locally and as a consequence
+                // allocate their buffers in local address space.  This renderer
+                // then performs a color conversion and copy to get the data
+                // into the ANativeBuffer.
+                mVideoRenderer = new AwesomeLocalRenderer(
+                    false,  // previewOnly
+                    component,
+                    (OMX_COLOR_FORMATTYPE)format,
+                    mISurface,
+                    mSurface,
+                    mVideoWidth, mVideoHeight,
+                    decodedWidth, decodedHeight);
+            }
+        } else {
             // Our OMX codecs allocate buffers on the media_server side
             // therefore they require a remote IOMXRenderer that knows how
             // to display them.
@@ -822,16 +891,6 @@
                         (OMX_COLOR_FORMATTYPE)format,
                         decodedWidth, decodedHeight,
                         mVideoWidth, mVideoHeight));
-        } else {
-            // Other decoders are instantiated locally and as a consequence
-            // allocate their buffers in local address space.
-            mVideoRenderer = new AwesomeLocalRenderer(
-                false,  // previewOnly
-                component,
-                (OMX_COLOR_FORMATTYPE)format,
-                mISurface,
-                mVideoWidth, mVideoHeight,
-                decodedWidth, decodedHeight);
         }
     }
 }
@@ -882,6 +941,12 @@
     mISurface = isurface;
 }
 
+void AwesomePlayer::setSurface(const sp<Surface> &surface) {
+    Mutex::Autolock autoLock(mLock);
+
+    mSurface = surface;
+}
+
 void AwesomePlayer::setAudioSink(
         const sp<MediaPlayerBase::AudioSink> &audioSink) {
     Mutex::Autolock autoLock(mLock);
@@ -1067,7 +1132,7 @@
             mClient.interface(), mVideoTrack->getFormat(),
             false, // createEncoder
             mVideoTrack,
-            NULL, flags);
+            NULL, flags, mSurface);
 
     if (mVideoSource != NULL) {
         int64_t durationUs;
@@ -1098,7 +1163,7 @@
     }
 
     if (mAudioPlayer != NULL) {
-        LOGV("seeking audio to %lld us (%.2f secs).", timeUs, timeUs / 1E6);
+        LOGV("seeking audio to %lld us (%.2f secs).", videoTimeUs, videoTimeUs / 1E6);
 
         // If we don't have a video time, seek audio to the originally
         // requested seek time instead.
@@ -1116,6 +1181,13 @@
     mFlags |= FIRST_FRAME;
     mSeeking = false;
     mSeekNotificationSent = false;
+
+    if (mDecryptHandle != NULL) {
+        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                Playback::PAUSE, 0);
+        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+                Playback::START, videoTimeUs / 1000);
+    }
 }
 
 void AwesomePlayer::onVideoEvent() {
@@ -1172,6 +1244,8 @@
                 if (err == INFO_FORMAT_CHANGED) {
                     LOGV("VideoSource signalled format change.");
 
+                    notifyVideoSize_l();
+
                     if (mVideoRenderer != NULL) {
                         mVideoRendererIsPreview = false;
                         initRenderer_l();
@@ -1213,17 +1287,11 @@
         mVideoTimeUs = timeUs;
     }
 
+    bool wasSeeking = mSeeking;
     finishSeekIfNecessary(timeUs);
 
     TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource;
 
-    if (mDecryptHandle != NULL) {
-        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
-                Playback::PAUSE, 0);
-        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
-                Playback::START, timeUs / 1000);
-    }
-
     if (mFlags & FIRST_FRAME) {
         mFlags &= ~FIRST_FRAME;
 
@@ -1240,6 +1308,11 @@
 
     int64_t latenessUs = nowUs - timeUs;
 
+    if (wasSeeking) {
+        // Let's display the first frame after seeking right away.
+        latenessUs = 0;
+    }
+
     if (mRTPSession != NULL) {
         // We'll completely ignore timestamps for gtalk videochat
         // and we'll play incoming video as fast as we get it.
@@ -1595,9 +1668,15 @@
     }
 
     dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient);
-    if (mDecryptHandle != NULL
-            && RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
-        notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+    if (mDecryptHandle != NULL) {
+        if (RightsStatus::RIGHTS_VALID == mDecryptHandle->status) {
+            if (DecryptApiType::WV_BASED == mDecryptHandle->decryptApiType) {
+                LOGD("Setting mCachedSource to NULL for WVM\n");
+                mCachedSource.clear();
+            }
+        } else {
+            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+        }
     }
 
     return setDataSource_l(extractor);
@@ -1611,7 +1690,7 @@
     }
 
     mPrepareResult = err;
-    mFlags &= ~(PREPARING|PREPARE_CANCELLED);
+    mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED);
     mAsyncPrepareEvent = NULL;
     mPreparedCondition.broadcast();
 }
@@ -1659,6 +1738,8 @@
         }
     }
 
+    mFlags |= PREPARING_CONNECTED;
+
     if (mCachedSource != NULL || mRTSPController != NULL) {
         postBufferingEvent_l();
     } else {
@@ -1668,17 +1749,17 @@
 
 void AwesomePlayer::finishAsyncPrepare_l() {
     if (mIsAsyncPrepare) {
-        if (mVideoWidth < 0 || mVideoHeight < 0) {
+        if (mVideoSource == NULL) {
             notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
         } else {
-            notifyListener_l(MEDIA_SET_VIDEO_SIZE, mVideoWidth, mVideoHeight);
+            notifyVideoSize_l();
         }
 
         notifyListener_l(MEDIA_PREPARED);
     }
 
     mPrepareResult = OK;
-    mFlags &= ~(PREPARING|PREPARE_CANCELLED);
+    mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED);
     mFlags |= PREPARED;
     mAsyncPrepareEvent = NULL;
     mPreparedCondition.broadcast();
@@ -1793,13 +1874,14 @@
 
     mFlags = state->mFlags & (AUTO_LOOPING | LOOPING | AT_EOS);
 
-    if (state->mLastVideoFrame && mISurface != NULL) {
+    if (state->mLastVideoFrame && (mSurface != NULL || mISurface != NULL)) {
         mVideoRenderer =
             new AwesomeLocalRenderer(
                     true,  // previewOnly
                     "",
                     (OMX_COLOR_FORMATTYPE)state->mColorFormat,
                     mISurface,
+                    mSurface,
                     state->mVideoWidth,
                     state->mVideoHeight,
                     state->mDecodedWidth,
@@ -1834,4 +1916,3 @@
 }
 
 }  // namespace android
-
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 89cb135..159d937 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -27,6 +27,7 @@
 #include <media/stagefright/MetaData.h>
 #include <camera/Camera.h>
 #include <camera/CameraParameters.h>
+#include <surfaceflinger/Surface.h>
 #include <utils/String8.h>
 #include <cutils/properties.h>
 
@@ -65,6 +66,11 @@
 void CameraSourceListener::postData(int32_t msgType, const sp<IMemory> &dataPtr) {
     LOGV("postData(%d, ptr:%p, size:%d)",
          msgType, dataPtr->pointer(), dataPtr->size());
+
+    sp<CameraSource> source = mSource.promote();
+    if (source.get() != NULL) {
+        source->dataCallback(msgType, dataPtr);
+    }
 }
 
 void CameraSourceListener::postDataTimestamp(
@@ -77,6 +83,10 @@
 }
 
 static int32_t getColorFormat(const char* colorFormat) {
+    if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV420P)) {
+       return OMX_COLOR_FormatYUV420Planar;
+    }
+
     if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_YUV422SP)) {
        return OMX_COLOR_FormatYUV422SemiPlanar;
     }
@@ -99,72 +109,430 @@
     CHECK_EQ(0, "Unknown color format");
 }
 
-// static
 CameraSource *CameraSource::Create() {
-    sp<Camera> camera = Camera::connect(0);
+    Size size;
+    size.width = -1;
+    size.height = -1;
 
-    if (camera.get() == NULL) {
-        return NULL;
-    }
-
-    return new CameraSource(camera);
+    sp<ICamera> camera;
+    return new CameraSource(camera, 0, size, -1, NULL, false);
 }
 
 // static
-CameraSource *CameraSource::CreateFromCamera(const sp<Camera> &camera) {
-    if (camera.get() == NULL) {
-        return NULL;
-    }
+CameraSource *CameraSource::CreateFromCamera(
+    const sp<ICamera>& camera,
+    int32_t cameraId,
+    Size videoSize,
+    int32_t frameRate,
+    const sp<Surface>& surface,
+    bool storeMetaDataInVideoBuffers) {
 
-    return new CameraSource(camera);
+    CameraSource *source = new CameraSource(camera, cameraId,
+                    videoSize, frameRate, surface,
+                    storeMetaDataInVideoBuffers);
+
+    if (source != NULL) {
+        if (source->initCheck() != OK) {
+            delete source;
+            return NULL;
+        }
+    }
+    return source;
 }
 
-CameraSource::CameraSource(const sp<Camera> &camera)
-    : mCamera(camera),
-      mFirstFrameTimeUs(0),
-      mLastFrameTimestampUs(0),
+CameraSource::CameraSource(
+    const sp<ICamera>& camera,
+    int32_t cameraId,
+    Size videoSize,
+    int32_t frameRate,
+    const sp<Surface>& surface,
+    bool storeMetaDataInVideoBuffers)
+    : mCameraFlags(0),
+      mVideoFrameRate(-1),
+      mCamera(0),
+      mSurface(surface),
       mNumFramesReceived(0),
+      mLastFrameTimestampUs(0),
+      mStarted(false),
+      mFirstFrameTimeUs(0),
       mNumFramesEncoded(0),
       mNumFramesDropped(0),
       mNumGlitches(0),
       mGlitchDurationThresholdUs(200000),
-      mCollectStats(false),
-      mStarted(false) {
+      mCollectStats(false) {
 
+    mVideoSize.width  = -1;
+    mVideoSize.height = -1;
+
+    mInitCheck = init(camera, cameraId,
+                    videoSize, frameRate,
+                    storeMetaDataInVideoBuffers);
+}
+
+status_t CameraSource::initCheck() const {
+    return mInitCheck;
+}
+
+status_t CameraSource::isCameraAvailable(
+    const sp<ICamera>& camera, int32_t cameraId) {
+
+    if (camera == 0) {
+        mCamera = Camera::connect(cameraId);
+        mCameraFlags &= ~FLAGS_HOT_CAMERA;
+    } else {
+        mCamera = Camera::create(camera);
+        mCameraFlags |= FLAGS_HOT_CAMERA;
+    }
+
+    // Is camera available?
+    if (mCamera == 0) {
+        LOGE("Camera connection could not be established.");
+        return -EBUSY;
+    }
+    if (!(mCameraFlags & FLAGS_HOT_CAMERA)) {
+        mCamera->lock();
+    }
+    return OK;
+}
+
+
+/*
+ * Check to see whether the requested video width and height is one
+ * of the supported sizes.
+ * @param width the video frame width in pixels
+ * @param height the video frame height in pixels
+ * @param suppportedSizes the vector of sizes that we check against
+ * @return true if the dimension (width and height) is supported.
+ */
+static bool isVideoSizeSupported(
+    int32_t width, int32_t height,
+    const Vector<Size>& supportedSizes) {
+
+    LOGV("isVideoSizeSupported");
+    for (size_t i = 0; i < supportedSizes.size(); ++i) {
+        if (width  == supportedSizes[i].width &&
+            height == supportedSizes[i].height) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * If the preview and video output is separate, we only set the
+ * the video size, and applications should set the preview size
+ * to some proper value, and the recording framework will not
+ * change the preview size; otherwise, if the video and preview
+ * output is the same, we need to set the preview to be the same
+ * as the requested video size.
+ *
+ */
+/*
+ * Query the camera to retrieve the supported video frame sizes
+ * and also to see whether CameraParameters::setVideoSize()
+ * is supported or not.
+ * @param params CameraParameters to retrieve the information
+ * @@param isSetVideoSizeSupported retunrs whether method
+ *      CameraParameters::setVideoSize() is supported or not.
+ * @param sizes returns the vector of Size objects for the
+ *      supported video frame sizes advertised by the camera.
+ */
+static void getSupportedVideoSizes(
+    const CameraParameters& params,
+    bool *isSetVideoSizeSupported,
+    Vector<Size>& sizes) {
+
+    *isSetVideoSizeSupported = true;
+    params.getSupportedVideoSizes(sizes);
+    if (sizes.size() == 0) {
+        LOGD("Camera does not support setVideoSize()");
+        params.getSupportedPreviewSizes(sizes);
+        *isSetVideoSizeSupported = false;
+    }
+}
+
+/*
+ * Check whether the camera has the supported color format
+ * @param params CameraParameters to retrieve the information
+ * @return OK if no error.
+ */
+status_t CameraSource::isCameraColorFormatSupported(
+        const CameraParameters& params) {
+    mColorFormat = getColorFormat(params.get(
+            CameraParameters::KEY_VIDEO_FRAME_FORMAT));
+    if (mColorFormat == -1) {
+        return BAD_VALUE;
+    }
+    return OK;
+}
+
+/*
+ * Configure the camera to use the requested video size
+ * (width and height) and/or frame rate. If both width and
+ * height are -1, configuration on the video size is skipped.
+ * if frameRate is -1, configuration on the frame rate
+ * is skipped. Skipping the configuration allows one to
+ * use the current camera setting without the need to
+ * actually know the specific values (see Create() method).
+ *
+ * @param params the CameraParameters to be configured
+ * @param width the target video frame width in pixels
+ * @param height the target video frame height in pixels
+ * @param frameRate the target frame rate in frames per second.
+ * @return OK if no error.
+ */
+status_t CameraSource::configureCamera(
+        CameraParameters* params,
+        int32_t width, int32_t height,
+        int32_t frameRate) {
+
+    Vector<Size> sizes;
+    bool isSetVideoSizeSupportedByCamera = true;
+    getSupportedVideoSizes(*params, &isSetVideoSizeSupportedByCamera, sizes);
+    bool isCameraParamChanged = false;
+    if (width != -1 && height != -1) {
+        if (!isVideoSizeSupported(width, height, sizes)) {
+            LOGE("Video dimension (%dx%d) is unsupported", width, height);
+            return BAD_VALUE;
+        }
+        if (isSetVideoSizeSupportedByCamera) {
+            params->setVideoSize(width, height);
+        } else {
+            params->setPreviewSize(width, height);
+        }
+        isCameraParamChanged = true;
+    } else if ((width == -1 && height != -1) ||
+               (width != -1 && height == -1)) {
+        // If one and only one of the width and height is -1
+        // we reject such a request.
+        LOGE("Requested video size (%dx%d) is not supported", width, height);
+        return BAD_VALUE;
+    } else {  // width == -1 && height == -1
+        // Do not configure the camera.
+        // Use the current width and height value setting from the camera.
+    }
+
+    if (frameRate != -1) {
+        CHECK(frameRate > 0 && frameRate <= 120);
+        const char* supportedFrameRates =
+                params->get(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES);
+        CHECK(supportedFrameRates != NULL);
+        LOGV("Supported frame rates: %s", supportedFrameRates);
+        char buf[4];
+        snprintf(buf, 4, "%d", frameRate);
+        if (strstr(supportedFrameRates, buf) == NULL) {
+            LOGE("Requested frame rate (%d) is not supported: %s",
+                frameRate, supportedFrameRates);
+            return BAD_VALUE;
+        }
+
+        // The frame rate is supported, set the camera to the requested value.
+        params->setPreviewFrameRate(frameRate);
+        isCameraParamChanged = true;
+    } else {  // frameRate == -1
+        // Do not configure the camera.
+        // Use the current frame rate value setting from the camera
+    }
+
+    if (isCameraParamChanged) {
+        // Either frame rate or frame size needs to be changed.
+        String8 s = params->flatten();
+        if (OK != mCamera->setParameters(s)) {
+            LOGE("Could not change settings."
+                 " Someone else is using camera %p?", mCamera.get());
+            return -EBUSY;
+        }
+    }
+    return OK;
+}
+
+/*
+ * Check whether the requested video frame size
+ * has been successfully configured or not. If both width and height
+ * are -1, check on the current width and height value setting
+ * is performed.
+ *
+ * @param params CameraParameters to retrieve the information
+ * @param the target video frame width in pixels to check against
+ * @param the target video frame height in pixels to check against
+ * @return OK if no error
+ */
+status_t CameraSource::checkVideoSize(
+        const CameraParameters& params,
+        int32_t width, int32_t height) {
+
+    // The actual video size is the same as the preview size
+    // if the camera hal does not support separate video and
+    // preview output. In this case, we retrieve the video
+    // size from preview.
+    int32_t frameWidthActual = -1;
+    int32_t frameHeightActual = -1;
+    Vector<Size> sizes;
+    params.getSupportedVideoSizes(sizes);
+    if (sizes.size() == 0) {
+        // video size is the same as preview size
+        params.getPreviewSize(&frameWidthActual, &frameHeightActual);
+    } else {
+        // video size may not be the same as preview
+        params.getVideoSize(&frameWidthActual, &frameHeightActual);
+    }
+    if (frameWidthActual < 0 || frameHeightActual < 0) {
+        LOGE("Failed to retrieve video frame size (%dx%d)",
+                frameWidthActual, frameHeightActual);
+        return UNKNOWN_ERROR;
+    }
+
+    // Check the actual video frame size against the target/requested
+    // video frame size.
+    if (width != -1 && height != -1) {
+        if (frameWidthActual != width || frameHeightActual != height) {
+            LOGE("Failed to set video frame size to %dx%d. "
+                    "The actual video size is %dx%d ", width, height,
+                    frameWidthActual, frameHeightActual);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    // Good now.
+    mVideoSize.width = frameWidthActual;
+    mVideoSize.height = frameHeightActual;
+    return OK;
+}
+
+/*
+ * Check the requested frame rate has been successfully configured or not.
+ * If the target frameRate is -1, check on the current frame rate value
+ * setting is performed.
+ *
+ * @param params CameraParameters to retrieve the information
+ * @param the target video frame rate to check against
+ * @return OK if no error.
+ */
+status_t CameraSource::checkFrameRate(
+        const CameraParameters& params,
+        int32_t frameRate) {
+
+    int32_t frameRateActual = params.getPreviewFrameRate();
+    if (frameRateActual < 0) {
+        LOGE("Failed to retrieve preview frame rate (%d)", frameRateActual);
+        return UNKNOWN_ERROR;
+    }
+
+    // Check the actual video frame rate against the target/requested
+    // video frame rate.
+    if (frameRate != -1 && (frameRateActual - frameRate) != 0) {
+        LOGE("Failed to set preview frame rate to %d fps. The actual "
+                "frame rate is %d", frameRate, frameRateActual);
+        return UNKNOWN_ERROR;
+    }
+
+    // Good now.
+    mVideoFrameRate = frameRateActual;
+    return OK;
+}
+
+/*
+ * Initialize the CameraSource to so that it becomes
+ * ready for providing the video input streams as requested.
+ * @param camera the camera object used for the video source
+ * @param cameraId if camera == 0, use camera with this id
+ *      as the video source
+ * @param videoSize the target video frame size. If both
+ *      width and height in videoSize is -1, use the current
+ *      width and heigth settings by the camera
+ * @param frameRate the target frame rate in frames per second.
+ *      if it is -1, use the current camera frame rate setting.
+ * @param storeMetaDataInVideoBuffers request to store meta
+ *      data or real YUV data in video buffers. Request to
+ *      store meta data in video buffers may not be honored
+ *      if the source does not support this feature.
+ *
+ * @return OK if no error.
+ */
+status_t CameraSource::init(
+        const sp<ICamera>& camera,
+        int32_t cameraId,
+        Size videoSize,
+        int32_t frameRate,
+        bool storeMetaDataInVideoBuffers) {
+
+    status_t err = OK;
     int64_t token = IPCThreadState::self()->clearCallingIdentity();
-    String8 s = mCamera->getParameters();
+
+    if ((err  = isCameraAvailable(camera, cameraId)) != OK) {
+        return err;
+    }
+    CameraParameters params(mCamera->getParameters());
+    if ((err = isCameraColorFormatSupported(params)) != OK) {
+        return err;
+    }
+
+    // Set the camera to use the requested video frame size
+    // and/or frame rate.
+    if ((err = configureCamera(&params,
+                    videoSize.width, videoSize.height,
+                    frameRate))) {
+        return err;
+    }
+
+    // Check on video frame size and frame rate.
+    CameraParameters newCameraParams(mCamera->getParameters());
+    if ((err = checkVideoSize(newCameraParams,
+                videoSize.width, videoSize.height)) != OK) {
+        return err;
+    }
+    if ((err = checkFrameRate(newCameraParams, frameRate)) != OK) {
+        return err;
+    }
+
+    // This CHECK is good, since we just passed the lock/unlock
+    // check earlier by calling mCamera->setParameters().
+    CHECK_EQ(OK, mCamera->setPreviewDisplay(mSurface));
+
+    mIsMetaDataStoredInVideoBuffers = false;
+    if (storeMetaDataInVideoBuffers &&
+        OK == mCamera->storeMetaDataInBuffers(true)) {
+        mIsMetaDataStoredInVideoBuffers = true;
+    }
+
+    /*
+     * mCamera->startRecording() signals camera hal to make
+     * available the video buffers (for instance, allocation
+     * of the video buffers may be triggered when camera hal's
+     * startRecording() method is called). Making available these
+     * video buffers earlier (before calling start()) is critical,
+     * if one wants to configure omx video encoders to use these
+     * buffers for passing video frame data during video recording
+     * without the need to memcpy the video frame data stored
+     * in these buffers. Eliminating memcpy for video frame data
+     * is crucial in performance for HD quality video recording
+     * applications.
+     *
+     * Based on OMX IL spec, configuring the omx video encoders
+     * must occur in loaded state. When start() is called, omx
+     * video encoders are already in idle state, which is too
+     * late. Thus, we must call mCamera->startRecording() earlier.
+     */
+    startCameraRecording();
+
     IPCThreadState::self()->restoreCallingIdentity(token);
 
-    printf("params: \"%s\"\n", s.string());
-
-    int32_t width, height, stride, sliceHeight;
-    CameraParameters params(s);
-    params.getPreviewSize(&width, &height);
-
-    // Calculate glitch duraton threshold based on frame rate
-    int32_t frameRate = params.getPreviewFrameRate();
-    int64_t glitchDurationUs = (1000000LL / frameRate);
+    int64_t glitchDurationUs = (1000000LL / mVideoFrameRate);
     if (glitchDurationUs > mGlitchDurationThresholdUs) {
         mGlitchDurationThresholdUs = glitchDurationUs;
     }
 
-    const char *colorFormatStr = params.get(CameraParameters::KEY_VIDEO_FRAME_FORMAT);
-    CHECK(colorFormatStr != NULL);
-    int32_t colorFormat = getColorFormat(colorFormatStr);
-
     // XXX: query camera for the stride and slice height
     // when the capability becomes available.
-    stride = width;
-    sliceHeight = height;
-
     mMeta = new MetaData;
-    mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
-    mMeta->setInt32(kKeyColorFormat, colorFormat);
-    mMeta->setInt32(kKeyWidth, width);
-    mMeta->setInt32(kKeyHeight, height);
-    mMeta->setInt32(kKeyStride, stride);
-    mMeta->setInt32(kKeySliceHeight, sliceHeight);
-
+    mMeta->setCString(kKeyMIMEType,  MEDIA_MIMETYPE_VIDEO_RAW);
+    mMeta->setInt32(kKeyColorFormat, mColorFormat);
+    mMeta->setInt32(kKeyWidth,       mVideoSize.width);
+    mMeta->setInt32(kKeyHeight,      mVideoSize.height);
+    mMeta->setInt32(kKeyStride,      mVideoSize.width);
+    mMeta->setInt32(kKeySliceHeight, mVideoSize.height);
+    mMeta->setInt32(kKeySampleRate,  mVideoFrameRate);
+    return OK;
 }
 
 CameraSource::~CameraSource() {
@@ -173,8 +541,17 @@
     }
 }
 
+void CameraSource::startCameraRecording() {
+    CHECK_EQ(OK, mCamera->startRecording());
+    CHECK(mCamera->recordingEnabled());
+}
+
 status_t CameraSource::start(MetaData *meta) {
     CHECK(!mStarted);
+    if (mInitCheck != OK) {
+        LOGE("CameraSource is not initialized yet");
+        return mInitCheck;
+    }
 
     char value[PROPERTY_VALUE_MAX];
     if (property_get("media.stagefright.record-stats", value, NULL)
@@ -190,13 +567,17 @@
 
     int64_t token = IPCThreadState::self()->clearCallingIdentity();
     mCamera->setListener(new CameraSourceListener(this));
-    CHECK_EQ(OK, mCamera->startRecording());
     IPCThreadState::self()->restoreCallingIdentity(token);
 
     mStarted = true;
     return OK;
 }
 
+void CameraSource::stopCameraRecording() {
+    mCamera->setListener(NULL);
+    mCamera->stopRecording();
+}
+
 status_t CameraSource::stop() {
     LOGV("stop");
     Mutex::Autolock autoLock(mLock);
@@ -204,15 +585,23 @@
     mFrameAvailableCondition.signal();
 
     int64_t token = IPCThreadState::self()->clearCallingIdentity();
-    mCamera->setListener(NULL);
-    mCamera->stopRecording();
+    stopCameraRecording();
     releaseQueuedFrames();
     while (!mFramesBeingEncoded.empty()) {
         LOGI("Waiting for outstanding frames being encoded: %d",
                 mFramesBeingEncoded.size());
         mFrameCompleteCondition.wait(mLock);
     }
-    mCamera = NULL;
+
+    LOGV("Disconnect camera");
+    if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) {
+        LOGV("Camera was cold when we started, stopping preview");
+        mCamera->stopPreview();
+    }
+    mCamera->unlock();
+    mCamera.clear();
+    mCamera = 0;
+    mCameraFlags = 0;
     IPCThreadState::self()->restoreCallingIdentity(token);
 
     if (mCollectStats) {
@@ -225,11 +614,15 @@
     return OK;
 }
 
+void CameraSource::releaseRecordingFrame(const sp<IMemory>& frame) {
+    mCamera->releaseRecordingFrame(frame);
+}
+
 void CameraSource::releaseQueuedFrames() {
     List<sp<IMemory> >::iterator it;
     while (!mFramesReceived.empty()) {
         it = mFramesReceived.begin();
-        mCamera->releaseRecordingFrame(*it);
+        releaseRecordingFrame(*it);
         mFramesReceived.erase(it);
         ++mNumFramesDropped;
     }
@@ -241,7 +634,7 @@
 
 void CameraSource::releaseOneRecordingFrame(const sp<IMemory>& frame) {
     int64_t token = IPCThreadState::self()->clearCallingIdentity();
-    mCamera->releaseRecordingFrame(frame);
+    releaseRecordingFrame(frame);
     IPCThreadState::self()->restoreCallingIdentity(token);
 }
 
@@ -251,7 +644,6 @@
     for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin();
          it != mFramesBeingEncoded.end(); ++it) {
         if ((*it)->pointer() ==  buffer->data()) {
-
             releaseOneRecordingFrame((*it));
             mFramesBeingEncoded.erase(it);
             ++mNumFramesEncoded;
@@ -343,6 +735,13 @@
         ++mNumGlitches;
     }
 
+    // May need to skip frame or modify timestamp. Currently implemented
+    // by the subclass CameraSourceTimeLapse.
+    if(skipCurrentFrame(timestampUs)) {
+        releaseOneRecordingFrame(data);
+        return;
+    }
+
     mLastFrameTimestampUs = timestampUs;
     if (mNumFramesReceived == 0) {
         mFirstFrameTimeUs = timestampUs;
@@ -367,4 +766,31 @@
     mFrameAvailableCondition.signal();
 }
 
+size_t CameraSource::getNumberOfVideoBuffers() const {
+    LOGV("getNumberOfVideoBuffers");
+    size_t nBuffers = 0;
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
+    if (mInitCheck == OK && mCamera != 0) {
+        nBuffers = mCamera->getNumberOfVideoBuffers();
+    }
+    IPCThreadState::self()->restoreCallingIdentity(token);
+    return nBuffers;
+}
+
+sp<IMemory> CameraSource::getVideoBuffer(size_t index) const {
+    LOGV("getVideoBuffer: %d", index);
+    sp<IMemory> buffer = 0;
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
+    if (mInitCheck == OK && mCamera != 0) {
+        buffer = mCamera->getVideoBuffer(index);
+    }
+    IPCThreadState::self()->restoreCallingIdentity(token);
+    return buffer;
+}
+
+bool CameraSource::isMetaDataStoredInVideoBuffers() const {
+    LOGV("isMetaDataStoredInVideoBuffers");
+    return mIsMetaDataStoredInVideoBuffers;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
new file mode 100644
index 0000000..6fd1825
--- /dev/null
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "CameraSourceTimeLapse"
+
+#include <binder/IPCThreadState.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <media/stagefright/CameraSource.h>
+#include <media/stagefright/CameraSourceTimeLapse.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/YUVImage.h>
+#include <media/stagefright/YUVCanvas.h>
+#include <camera/Camera.h>
+#include <camera/CameraParameters.h>
+#include <ui/Rect.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include "OMX_Video.h"
+#include <limits.h>
+
+namespace android {
+
+// static
+CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera(
+        const sp<ICamera> &camera,
+        int32_t cameraId,
+        Size videoSize,
+        int32_t videoFrameRate,
+        const sp<Surface>& surface,
+        int64_t timeBetweenTimeLapseFrameCaptureUs) {
+
+    CameraSourceTimeLapse *source = new
+            CameraSourceTimeLapse(camera, cameraId,
+                videoSize, videoFrameRate, surface,
+                timeBetweenTimeLapseFrameCaptureUs);
+
+    if (source != NULL) {
+        if (source->initCheck() != OK) {
+            delete source;
+            return NULL;
+        }
+    }
+    return source;
+}
+
+CameraSourceTimeLapse::CameraSourceTimeLapse(
+        const sp<ICamera>& camera,
+        int32_t cameraId,
+        Size videoSize,
+        int32_t videoFrameRate,
+        const sp<Surface>& surface,
+        int64_t timeBetweenTimeLapseFrameCaptureUs)
+    : CameraSource(camera, cameraId, videoSize, videoFrameRate, surface, false),
+      mTimeBetweenTimeLapseFrameCaptureUs(timeBetweenTimeLapseFrameCaptureUs),
+      mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate),
+      mLastTimeLapseFrameRealTimestampUs(0),
+      mSkipCurrentFrame(false) {
+
+    LOGV("starting time lapse mode");
+    mVideoWidth = videoSize.width;
+    mVideoHeight = videoSize.height;
+
+    if (trySettingPreviewSize(videoSize.width, videoSize.height)) {
+        mUseStillCameraForTimeLapse = false;
+    } else {
+        // TODO: Add a check to see that mTimeBetweenTimeLapseFrameCaptureUs is greater
+        // than the fastest rate at which the still camera can take pictures.
+        mUseStillCameraForTimeLapse = true;
+        CHECK(setPictureSizeToClosestSupported(videoSize.width, videoSize.height));
+        mNeedCropping = computeCropRectangleOffset();
+        mMeta->setInt32(kKeyWidth, videoSize.width);
+        mMeta->setInt32(kKeyHeight, videoSize.height);
+    }
+
+    // Initialize quick stop variables.
+    mQuickStop = false;
+    mForceRead = false;
+    mLastReadBufferCopy = NULL;
+    mStopWaitingForIdleCamera = false;
+}
+
+CameraSourceTimeLapse::~CameraSourceTimeLapse() {
+}
+
+void CameraSourceTimeLapse::startQuickReadReturns() {
+    Mutex::Autolock autoLock(mQuickStopLock);
+    LOGV("Enabling quick read returns");
+
+    // Enable quick stop mode.
+    mQuickStop = true;
+
+    if (mUseStillCameraForTimeLapse) {
+        // wake up the thread right away.
+        mTakePictureCondition.signal();
+    } else {
+        // Force dataCallbackTimestamp() coming from the video camera to not skip the
+        // next frame as we want read() to get a get a frame right away.
+        mForceRead = true;
+    }
+}
+
+bool CameraSourceTimeLapse::trySettingPreviewSize(int32_t width, int32_t height) {
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
+    String8 s = mCamera->getParameters();
+    IPCThreadState::self()->restoreCallingIdentity(token);
+
+    CameraParameters params(s);
+    Vector<Size> supportedSizes;
+    params.getSupportedPreviewSizes(supportedSizes);
+
+    bool previewSizeSupported = false;
+    for (uint32_t i = 0; i < supportedSizes.size(); ++i) {
+        int32_t pictureWidth = supportedSizes[i].width;
+        int32_t pictureHeight = supportedSizes[i].height;
+
+        if ((pictureWidth == width) && (pictureHeight == height)) {
+            previewSizeSupported = true;
+        }
+    }
+
+    if (previewSizeSupported) {
+        LOGV("Video size (%d, %d) is a supported preview size", width, height);
+        params.setPreviewSize(width, height);
+        CHECK(mCamera->setParameters(params.flatten()));
+        return true;
+    }
+
+    return false;
+}
+
+bool CameraSourceTimeLapse::setPictureSizeToClosestSupported(int32_t width, int32_t height) {
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
+    String8 s = mCamera->getParameters();
+    IPCThreadState::self()->restoreCallingIdentity(token);
+
+    CameraParameters params(s);
+    Vector<Size> supportedSizes;
+    params.getSupportedPictureSizes(supportedSizes);
+
+    int32_t minPictureSize = INT_MAX;
+    for (uint32_t i = 0; i < supportedSizes.size(); ++i) {
+        int32_t pictureWidth = supportedSizes[i].width;
+        int32_t pictureHeight = supportedSizes[i].height;
+
+        if ((pictureWidth >= width) && (pictureHeight >= height)) {
+            int32_t pictureSize = pictureWidth*pictureHeight;
+            if (pictureSize < minPictureSize) {
+                minPictureSize = pictureSize;
+                mPictureWidth = pictureWidth;
+                mPictureHeight = pictureHeight;
+            }
+        }
+    }
+    LOGV("Picture size = (%d, %d)", mPictureWidth, mPictureHeight);
+    return (minPictureSize != INT_MAX);
+}
+
+bool CameraSourceTimeLapse::computeCropRectangleOffset() {
+    if ((mPictureWidth == mVideoWidth) && (mPictureHeight == mVideoHeight)) {
+        return false;
+    }
+
+    CHECK((mPictureWidth > mVideoWidth) && (mPictureHeight > mVideoHeight));
+
+    int32_t widthDifference = mPictureWidth - mVideoWidth;
+    int32_t heightDifference = mPictureHeight - mVideoHeight;
+
+    mCropRectStartX = widthDifference/2;
+    mCropRectStartY = heightDifference/2;
+
+    LOGV("setting crop rectangle offset to (%d, %d)", mCropRectStartX, mCropRectStartY);
+
+    return true;
+}
+
+void CameraSourceTimeLapse::signalBufferReturned(MediaBuffer* buffer) {
+    Mutex::Autolock autoLock(mQuickStopLock);
+    if (mQuickStop && (buffer == mLastReadBufferCopy)) {
+        buffer->setObserver(NULL);
+        buffer->release();
+    } else {
+        return CameraSource::signalBufferReturned(buffer);
+    }
+}
+
+void createMediaBufferCopy(const MediaBuffer& sourceBuffer, int64_t frameTime, MediaBuffer **newBuffer) {
+    size_t sourceSize = sourceBuffer.size();
+    void* sourcePointer = sourceBuffer.data();
+
+    (*newBuffer) = new MediaBuffer(sourceSize);
+    memcpy((*newBuffer)->data(), sourcePointer, sourceSize);
+
+    (*newBuffer)->meta_data()->setInt64(kKeyTime, frameTime);
+}
+
+void CameraSourceTimeLapse::fillLastReadBufferCopy(MediaBuffer& sourceBuffer) {
+    int64_t frameTime;
+    CHECK(sourceBuffer.meta_data()->findInt64(kKeyTime, &frameTime));
+    createMediaBufferCopy(sourceBuffer, frameTime, &mLastReadBufferCopy);
+    mLastReadBufferCopy->add_ref();
+    mLastReadBufferCopy->setObserver(this);
+}
+
+status_t CameraSourceTimeLapse::read(
+        MediaBuffer **buffer, const ReadOptions *options) {
+    if (mLastReadBufferCopy == NULL) {
+        mLastReadStatus = CameraSource::read(buffer, options);
+
+        // mQuickStop may have turned to true while read was blocked. Make a copy of
+        // the buffer in that case.
+        Mutex::Autolock autoLock(mQuickStopLock);
+        if (mQuickStop && *buffer) {
+            fillLastReadBufferCopy(**buffer);
+        }
+        return mLastReadStatus;
+    } else {
+        (*buffer) = mLastReadBufferCopy;
+        (*buffer)->add_ref();
+        return mLastReadStatus;
+    }
+}
+
+// static
+void *CameraSourceTimeLapse::ThreadTimeLapseWrapper(void *me) {
+    CameraSourceTimeLapse *source = static_cast<CameraSourceTimeLapse *>(me);
+    source->threadTimeLapseEntry();
+    return NULL;
+}
+
+void CameraSourceTimeLapse::threadTimeLapseEntry() {
+    while (mStarted) {
+        {
+            Mutex::Autolock autoLock(mCameraIdleLock);
+            if (!mCameraIdle) {
+                mCameraIdleCondition.wait(mCameraIdleLock);
+            }
+            CHECK(mCameraIdle);
+            mCameraIdle = false;
+        }
+
+        // Even if mQuickStop == true we need to take one more picture
+        // as a read() may be blocked, waiting for a frame to get available.
+        // After this takePicture, if mQuickStop == true, we can safely exit
+        // this thread as read() will make a copy of this last frame and keep
+        // returning it in the quick stop mode.
+        Mutex::Autolock autoLock(mQuickStopLock);
+        CHECK_EQ(OK, mCamera->takePicture());
+        if (mQuickStop) {
+            LOGV("threadTimeLapseEntry: Exiting due to mQuickStop = true");
+            return;
+        }
+        mTakePictureCondition.waitRelative(mQuickStopLock,
+                mTimeBetweenTimeLapseFrameCaptureUs * 1000);
+    }
+    LOGV("threadTimeLapseEntry: Exiting due to mStarted = false");
+}
+
+void CameraSourceTimeLapse::startCameraRecording() {
+    if (mUseStillCameraForTimeLapse) {
+        LOGV("start time lapse recording using still camera");
+
+        int64_t token = IPCThreadState::self()->clearCallingIdentity();
+        String8 s = mCamera->getParameters();
+        IPCThreadState::self()->restoreCallingIdentity(token);
+
+        CameraParameters params(s);
+        params.setPictureSize(mPictureWidth, mPictureHeight);
+        mCamera->setParameters(params.flatten());
+        mCameraIdle = true;
+        mStopWaitingForIdleCamera = false;
+
+        // disable shutter sound and play the recording sound.
+        mCamera->sendCommand(CAMERA_CMD_ENABLE_SHUTTER_SOUND, 0, 0);
+        mCamera->sendCommand(CAMERA_CMD_PLAY_RECORDING_SOUND, 0, 0);
+
+        // create a thread which takes pictures in a loop
+        pthread_attr_t attr;
+        pthread_attr_init(&attr);
+        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+        pthread_create(&mThreadTimeLapse, &attr, ThreadTimeLapseWrapper, this);
+        pthread_attr_destroy(&attr);
+    } else {
+        LOGV("start time lapse recording using video camera");
+        CHECK_EQ(OK, mCamera->startRecording());
+    }
+}
+
+void CameraSourceTimeLapse::stopCameraRecording() {
+    if (mUseStillCameraForTimeLapse) {
+        void *dummy;
+        pthread_join(mThreadTimeLapse, &dummy);
+
+        // Last takePicture may still be underway. Wait for the camera to get
+        // idle.
+        Mutex::Autolock autoLock(mCameraIdleLock);
+        mStopWaitingForIdleCamera = true;
+        if (!mCameraIdle) {
+            mCameraIdleCondition.wait(mCameraIdleLock);
+        }
+        CHECK(mCameraIdle);
+        mCamera->setListener(NULL);
+
+        // play the recording sound.
+        mCamera->sendCommand(CAMERA_CMD_PLAY_RECORDING_SOUND, 0, 0);
+    } else {
+        mCamera->setListener(NULL);
+        mCamera->stopRecording();
+    }
+    if (mLastReadBufferCopy) {
+        mLastReadBufferCopy->release();
+        mLastReadBufferCopy = NULL;
+    }
+}
+
+void CameraSourceTimeLapse::releaseRecordingFrame(const sp<IMemory>& frame) {
+    if (!mUseStillCameraForTimeLapse) {
+        mCamera->releaseRecordingFrame(frame);
+    }
+}
+
+sp<IMemory> CameraSourceTimeLapse::createIMemoryCopy(const sp<IMemory> &source_data) {
+    size_t source_size = source_data->size();
+    void* source_pointer = source_data->pointer();
+
+    sp<MemoryHeapBase> newMemoryHeap = new MemoryHeapBase(source_size);
+    sp<MemoryBase> newMemory = new MemoryBase(newMemoryHeap, 0, source_size);
+    memcpy(newMemory->pointer(), source_pointer, source_size);
+    return newMemory;
+}
+
+// Allocates IMemory of final type MemoryBase with the given size.
+sp<IMemory> allocateIMemory(size_t size) {
+    sp<MemoryHeapBase> newMemoryHeap = new MemoryHeapBase(size);
+    sp<MemoryBase> newMemory = new MemoryBase(newMemoryHeap, 0, size);
+    return newMemory;
+}
+
+// static
+void *CameraSourceTimeLapse::ThreadStartPreviewWrapper(void *me) {
+    CameraSourceTimeLapse *source = static_cast<CameraSourceTimeLapse *>(me);
+    source->threadStartPreview();
+    return NULL;
+}
+
+void CameraSourceTimeLapse::threadStartPreview() {
+    CHECK_EQ(OK, mCamera->startPreview());
+    Mutex::Autolock autoLock(mCameraIdleLock);
+    mCameraIdle = true;
+    mCameraIdleCondition.signal();
+}
+
+void CameraSourceTimeLapse::restartPreview() {
+    // Start this in a different thread, so that the dataCallback can return
+    LOGV("restartPreview");
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+    pthread_t threadPreview;
+    pthread_create(&threadPreview, &attr, ThreadStartPreviewWrapper, this);
+    pthread_attr_destroy(&attr);
+}
+
+sp<IMemory> CameraSourceTimeLapse::cropYUVImage(const sp<IMemory> &source_data) {
+    // find the YUV format
+    int32_t srcFormat;
+    CHECK(mMeta->findInt32(kKeyColorFormat, &srcFormat));
+    YUVImage::YUVFormat yuvFormat;
+    if (srcFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
+        yuvFormat = YUVImage::YUV420SemiPlanar;
+    } else {
+        CHECK_EQ(srcFormat, OMX_COLOR_FormatYUV420Planar);
+        yuvFormat = YUVImage::YUV420Planar;
+    }
+
+    // allocate memory for cropped image and setup a canvas using it.
+    sp<IMemory> croppedImageMemory = allocateIMemory(
+            YUVImage::bufferSize(yuvFormat, mVideoWidth, mVideoHeight));
+    YUVImage yuvImageCropped(yuvFormat,
+            mVideoWidth, mVideoHeight,
+            (uint8_t *)croppedImageMemory->pointer());
+    YUVCanvas yuvCanvasCrop(yuvImageCropped);
+
+    YUVImage yuvImageSource(yuvFormat,
+            mPictureWidth, mPictureHeight,
+            (uint8_t *)source_data->pointer());
+    yuvCanvasCrop.CopyImageRect(
+            Rect(mCropRectStartX, mCropRectStartY,
+                mCropRectStartX + mVideoWidth,
+                mCropRectStartY + mVideoHeight),
+            0, 0,
+            yuvImageSource);
+
+    return croppedImageMemory;
+}
+
+void CameraSourceTimeLapse::dataCallback(int32_t msgType, const sp<IMemory> &data) {
+    if (msgType == CAMERA_MSG_COMPRESSED_IMAGE) {
+        // takePicture will complete after this callback, so restart preview.
+        restartPreview();
+        return;
+    }
+    if (msgType != CAMERA_MSG_RAW_IMAGE) {
+        return;
+    }
+
+    LOGV("dataCallback for timelapse still frame");
+    CHECK_EQ(true, mUseStillCameraForTimeLapse);
+
+    int64_t timestampUs;
+    if (mNumFramesReceived == 0) {
+        timestampUs = mStartTimeUs;
+    } else {
+        timestampUs = mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs;
+    }
+
+    if (mNeedCropping) {
+        sp<IMemory> croppedImageData = cropYUVImage(data);
+        dataCallbackTimestamp(timestampUs, msgType, croppedImageData);
+    } else {
+        sp<IMemory> dataCopy = createIMemoryCopy(data);
+        dataCallbackTimestamp(timestampUs, msgType, dataCopy);
+    }
+}
+
+bool CameraSourceTimeLapse::skipCurrentFrame(int64_t timestampUs) {
+    if (mSkipCurrentFrame) {
+        mSkipCurrentFrame = false;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) {
+    if (!mUseStillCameraForTimeLapse) {
+        if (mLastTimeLapseFrameRealTimestampUs == 0) {
+            // First time lapse frame. Initialize mLastTimeLapseFrameRealTimestampUs
+            // to current time (timestampUs) and save frame data.
+            LOGV("dataCallbackTimestamp timelapse: initial frame");
+
+            mLastTimeLapseFrameRealTimestampUs = *timestampUs;
+            return false;
+        }
+
+        {
+            Mutex::Autolock autoLock(mQuickStopLock);
+
+            // mForceRead may be set to true by startQuickReadReturns(). In that
+            // case don't skip this frame.
+            if (mForceRead) {
+                LOGV("dataCallbackTimestamp timelapse: forced read");
+                mForceRead = false;
+                *timestampUs = mLastFrameTimestampUs;
+                return false;
+            }
+        }
+
+        if (*timestampUs <
+                (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenTimeLapseFrameCaptureUs)) {
+            // Skip all frames from last encoded frame until
+            // sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed.
+            // Tell the camera to release its recording frame and return.
+            LOGV("dataCallbackTimestamp timelapse: skipping intermediate frame");
+            return true;
+        } else {
+            // Desired frame has arrived after mTimeBetweenTimeLapseFrameCaptureUs time:
+            // - Reset mLastTimeLapseFrameRealTimestampUs to current time.
+            // - Artificially modify timestampUs to be one frame time (1/framerate) ahead
+            // of the last encoded frame's time stamp.
+            LOGV("dataCallbackTimestamp timelapse: got timelapse frame");
+
+            mLastTimeLapseFrameRealTimestampUs = *timestampUs;
+            *timestampUs = mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs;
+            return false;
+        }
+    }
+    return false;
+}
+
+void CameraSourceTimeLapse::dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
+            const sp<IMemory> &data) {
+    if (!mUseStillCameraForTimeLapse) {
+        mSkipCurrentFrame = skipFrameAndModifyTimeStamp(&timestampUs);
+    } else {
+        Mutex::Autolock autoLock(mCameraIdleLock);
+        // If we are using the still camera and stop() has been called, it may
+        // be waiting for the camera to get idle. In that case return
+        // immediately. Calling CameraSource::dataCallbackTimestamp() will lead
+        // to a deadlock since it tries to access CameraSource::mLock which in
+        // this case is held by CameraSource::stop() currently waiting for the
+        // camera to get idle. And camera will not get idle until this call
+        // returns.
+        if (mStopWaitingForIdleCamera) {
+            return;
+        }
+    }
+    CameraSource::dataCallbackTimestamp(timestampUs, msgType, data);
+}
+
+}  // namespace android
diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp
index aa9ad23..3c98932 100644
--- a/media/libstagefright/DRMExtractor.cpp
+++ b/media/libstagefright/DRMExtractor.cpp
@@ -280,18 +280,23 @@
         if (gDrmManagerClient == NULL) {
             gDrmManagerClient = new DrmManagerClient();
         }
+
+        if (gDrmManagerClient == NULL) {
+            return false;
+        }
     }
 
     DecryptHandle *decryptHandle = source->DrmInitialization(gDrmManagerClient);
 
     if (decryptHandle != NULL) {
         if (decryptHandle->decryptApiType == DecryptApiType::CONTAINER_BASED) {
-            *mimeType = String8("drm+container_based+");
+            *mimeType = String8("drm+container_based+") + decryptHandle->mimeType;
         } else if (decryptHandle->decryptApiType == DecryptApiType::ELEMENTARY_STREAM_BASED) {
-            *mimeType = String8("drm+es_based+");
+            *mimeType = String8("drm+es_based+") + decryptHandle->mimeType;
+        } else if (decryptHandle->decryptApiType == DecryptApiType::WV_BASED) {
+            *mimeType = MEDIA_MIMETYPE_CONTAINER_WVM;
+            LOGW("SniffWVM: found match\n");
         }
-
-        *mimeType += decryptHandle->mimeType;
         *confidence = 10.0f;
 
         return true;
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
index b46d8d0..1c1a9c5 100644
--- a/media/libstagefright/FileSource.cpp
+++ b/media/libstagefright/FileSource.cpp
@@ -21,7 +21,7 @@
 
 FileSource::FileSource(const char *filename)
     : mFile(fopen(filename, "rb")),
-      mFd(fileno(mFile)),
+      mFd(mFile == NULL ? -1 : fileno(mFile)),
       mOffset(0),
       mLength(-1),
       mDecryptHandle(NULL),
@@ -55,9 +55,6 @@
         delete[] mDrmBuf;
         mDrmBuf = NULL;
     }
-    if (mDecryptHandle != NULL) {
-        mDrmManagerClient->closeDecryptSession(mDecryptHandle);
-    }
 }
 
 status_t FileSource::initCheck() const {
@@ -113,7 +110,11 @@
 }
 
 DecryptHandle* FileSource::DrmInitialization(DrmManagerClient* client) {
+    if (client == NULL) {
+        return NULL;
+    }
     mDrmManagerClient = client;
+
     if (mDecryptHandle == NULL) {
         mDecryptHandle = mDrmManagerClient->openDecryptSession(
                 mFd, mOffset, mLength);
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index a15c274..b3ed845 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -506,7 +506,7 @@
 }
 
 void MPEG4Writer::stopWriterThread() {
-    LOGV("stopWriterThread");
+    LOGD("Stopping writer thread");
 
     {
         Mutex::Autolock autolock(mLock);
@@ -517,6 +517,7 @@
 
     void *dummy;
     pthread_join(mThread, &dummy);
+    LOGD("Writer thread stopped");
 }
 
 status_t MPEG4Writer::stop() {
@@ -1228,6 +1229,7 @@
 }
 
 status_t MPEG4Writer::Track::stop() {
+    LOGD("Stopping %s track", mIsAudio? "Audio": "Video");
     if (mDone) {
         return OK;
     }
@@ -1239,6 +1241,7 @@
 
     status_t err = (status_t) dummy;
 
+    LOGD("Stopping %s track source", mIsAudio? "Audio": "Video");
     {
         status_t status = mSource->stop();
         if (err == OK && status != OK && status != ERROR_END_OF_STREAM) {
@@ -1246,6 +1249,7 @@
         }
     }
 
+    LOGD("%s track stopped", mIsAudio? "Audio": "Video");
     return err;
 }
 
@@ -2217,6 +2221,9 @@
                         CHECK(mCodecSpecificData);
                         CHECK(mCodecSpecificDataSize > 0);
 
+                        // Make sure all sizes encode to a single byte.
+                        CHECK(mCodecSpecificDataSize + 23 < 128);
+
                         mOwner->writeInt32(0);     // version=0, flags=0
                         mOwner->writeInt8(0x03);   // ES_DescrTag
                         mOwner->writeInt8(23 + mCodecSpecificDataSize);
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
index b973745b..cbccd31 100644
--- a/media/libstagefright/MediaBuffer.cpp
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -25,6 +25,8 @@
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MetaData.h>
 
+#include <ui/GraphicBuffer.h>
+
 namespace android {
 
 // XXX make this truly atomic.
@@ -61,6 +63,20 @@
       mOriginal(NULL) {
 }
 
+MediaBuffer::MediaBuffer(const sp<GraphicBuffer>& graphicBuffer)
+    : mObserver(NULL),
+      mNextBuffer(NULL),
+      mRefCount(0),
+      mData(NULL),
+      mSize(1),
+      mRangeOffset(0),
+      mRangeLength(mSize),
+      mGraphicBuffer(graphicBuffer),
+      mOwnsData(false),
+      mMetaData(new MetaData),
+      mOriginal(NULL) {
+}
+
 void MediaBuffer::release() {
     if (mObserver == NULL) {
         CHECK_EQ(mRefCount, 0);
@@ -92,10 +108,12 @@
 }
 
 void *MediaBuffer::data() const {
+    CHECK(mGraphicBuffer == NULL);
     return mData;
 }
 
 size_t MediaBuffer::size() const {
+    CHECK(mGraphicBuffer == NULL);
     return mSize;
 }
 
@@ -108,15 +126,19 @@
 }
 
 void MediaBuffer::set_range(size_t offset, size_t length) {
-    if (offset + length > mSize) {
+    if ((mGraphicBuffer == NULL) && (offset + length > mSize)) {
         LOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize);
     }
-    CHECK(offset + length <= mSize);
+    CHECK((mGraphicBuffer != NULL) || (offset + length <= mSize));
 
     mRangeOffset = offset;
     mRangeLength = length;
 }
 
+sp<GraphicBuffer> MediaBuffer::graphicBuffer() const {
+    return mGraphicBuffer;
+}
+
 sp<MetaData> MediaBuffer::meta_data() {
     return mMetaData;
 }
@@ -158,6 +180,8 @@
 }
 
 MediaBuffer *MediaBuffer::clone() {
+    CHECK_EQ(mGraphicBuffer, NULL);
+
     MediaBuffer *buffer = new MediaBuffer(mData, mSize);
     buffer->set_range(mRangeOffset, mRangeLength);
     buffer->mMetaData = new MetaData(*mMetaData.get());
@@ -169,4 +193,3 @@
 }
 
 }  // namespace android
-
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 7648d42..4599fca 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -42,4 +42,6 @@
 const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska";
 const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts";
 
+const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm";
+
 }  // namespace android
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index ee03c52..d12ac64 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -25,6 +25,7 @@
 #include "include/OggExtractor.h"
 #include "include/MPEG2TSExtractor.h"
 #include "include/DRMExtractor.h"
+#include "include/WVMExtractor.h"
 
 #include "matroska/MatroskaExtractor.h"
 
@@ -65,7 +66,7 @@
     }
 
     if (!strncmp(mime, "drm", 3)) {
-        char *originalMime = strrchr(mime, '+') + 1;
+        const char *originalMime = strrchr(mime, '+') + 1;
 
         if (!strncmp(mime, "drm+es_based", 12)) {
             return new DRMExtractor(source, originalMime);
@@ -92,6 +93,8 @@
         return new MatroskaExtractor(source);
     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
         return new MPEG2TSExtractor(source);
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) {
+        return new WVMExtractor(source);
     }
 
     return NULL;
diff --git a/media/libstagefright/MediaSourceSplitter.cpp b/media/libstagefright/MediaSourceSplitter.cpp
new file mode 100644
index 0000000..abc7012
--- /dev/null
+++ b/media/libstagefright/MediaSourceSplitter.cpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaSourceSplitter"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaSourceSplitter.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+MediaSourceSplitter::MediaSourceSplitter(sp<MediaSource> mediaSource) {
+    mNumberOfClients = 0;
+    mSource = mediaSource;
+    mSourceStarted = false;
+
+    mNumberOfClientsStarted = 0;
+    mNumberOfCurrentReads = 0;
+    mCurrentReadBit = 0;
+    mLastReadCompleted = true;
+}
+
+MediaSourceSplitter::~MediaSourceSplitter() {
+}
+
+sp<MediaSource> MediaSourceSplitter::createClient() {
+    Mutex::Autolock autoLock(mLock);
+
+    sp<MediaSource> client = new Client(this, mNumberOfClients++);
+    mClientsStarted.push(false);
+    mClientsDesiredReadBit.push(0);
+    return client;
+}
+
+status_t MediaSourceSplitter::start(int clientId, MetaData *params) {
+    Mutex::Autolock autoLock(mLock);
+
+    LOGV("start client (%d)", clientId);
+    if (mClientsStarted[clientId]) {
+        return OK;
+    }
+
+    mNumberOfClientsStarted++;
+
+    if (!mSourceStarted) {
+        LOGV("Starting real source from client (%d)", clientId);
+        status_t err = mSource->start(params);
+
+        if (err == OK) {
+            mSourceStarted = true;
+            mClientsStarted.editItemAt(clientId) = true;
+            mClientsDesiredReadBit.editItemAt(clientId) = !mCurrentReadBit;
+        }
+
+        return err;
+    } else {
+        mClientsStarted.editItemAt(clientId) = true;
+        if (mLastReadCompleted) {
+            // Last read was completed. So join in the threads for the next read.
+            mClientsDesiredReadBit.editItemAt(clientId) = !mCurrentReadBit;
+        } else {
+            // Last read is ongoing. So join in the threads for the current read.
+            mClientsDesiredReadBit.editItemAt(clientId) = mCurrentReadBit;
+        }
+        return OK;
+    }
+}
+
+status_t MediaSourceSplitter::stop(int clientId) {
+    Mutex::Autolock autoLock(mLock);
+
+    LOGV("stop client (%d)", clientId);
+    CHECK(clientId >= 0 && clientId < mNumberOfClients);
+    CHECK(mClientsStarted[clientId]);
+
+    if (--mNumberOfClientsStarted == 0) {
+        LOGV("Stopping real source from client (%d)", clientId);
+        status_t err = mSource->stop();
+        mSourceStarted = false;
+        mClientsStarted.editItemAt(clientId) = false;
+        return err;
+    } else {
+        mClientsStarted.editItemAt(clientId) = false;
+        if (!mLastReadCompleted && (mClientsDesiredReadBit[clientId] == mCurrentReadBit)) {
+            // !mLastReadCompleted implies that buffer has been read from source, but all
+            // clients haven't read it.
+            // mClientsDesiredReadBit[clientId] == mCurrentReadBit implies that this
+            // client would have wanted to read from this buffer. (i.e. it has not yet
+            // called read() for the current read buffer.)
+            // Since other threads may be waiting for all the clients' reads to complete,
+            // signal that this read has been aborted.
+            signalReadComplete_lock(true);
+        }
+        return OK;
+    }
+}
+
+sp<MetaData> MediaSourceSplitter::getFormat(int clientId) {
+    Mutex::Autolock autoLock(mLock);
+
+    LOGV("getFormat client (%d)", clientId);
+    return mSource->getFormat();
+}
+
+status_t MediaSourceSplitter::read(int clientId,
+        MediaBuffer **buffer, const MediaSource::ReadOptions *options) {
+    Mutex::Autolock autoLock(mLock);
+
+    CHECK(clientId >= 0 && clientId < mNumberOfClients);
+
+    LOGV("read client (%d)", clientId);
+    *buffer = NULL;
+
+    if (!mClientsStarted[clientId]) {
+        return OK;
+    }
+
+    if (mCurrentReadBit != mClientsDesiredReadBit[clientId]) {
+        // Desired buffer has not been read from source yet.
+
+        // If the current client is the special client with clientId = 0
+        // then read from source, else wait until the client 0 has finished
+        // reading from source.
+        if (clientId == 0) {
+            // Wait for all client's last read to complete first so as to not
+            // corrupt the buffer at mLastReadMediaBuffer.
+            waitForAllClientsLastRead_lock(clientId);
+
+            readFromSource_lock(options);
+            *buffer = mLastReadMediaBuffer;
+        } else {
+            waitForReadFromSource_lock(clientId);
+
+            *buffer = mLastReadMediaBuffer;
+            (*buffer)->add_ref();
+        }
+        CHECK(mCurrentReadBit == mClientsDesiredReadBit[clientId]);
+    } else {
+        // Desired buffer has already been read from source. Use the cached data.
+        CHECK(clientId != 0);
+
+        *buffer = mLastReadMediaBuffer;
+        (*buffer)->add_ref();
+    }
+
+    mClientsDesiredReadBit.editItemAt(clientId) = !mClientsDesiredReadBit[clientId];
+    signalReadComplete_lock(false);
+
+    return mLastReadStatus;
+}
+
+void MediaSourceSplitter::readFromSource_lock(const MediaSource::ReadOptions *options) {
+    mLastReadStatus = mSource->read(&mLastReadMediaBuffer , options);
+
+    mCurrentReadBit = !mCurrentReadBit;
+    mLastReadCompleted = false;
+    mReadFromSourceCondition.broadcast();
+}
+
+void MediaSourceSplitter::waitForReadFromSource_lock(int32_t clientId) {
+    mReadFromSourceCondition.wait(mLock);
+}
+
+void MediaSourceSplitter::waitForAllClientsLastRead_lock(int32_t clientId) {
+    if (mLastReadCompleted) {
+        return;
+    }
+    mAllReadsCompleteCondition.wait(mLock);
+    CHECK(mLastReadCompleted);
+}
+
+void MediaSourceSplitter::signalReadComplete_lock(bool readAborted) {
+    if (!readAborted) {
+        mNumberOfCurrentReads++;
+    }
+
+    if (mNumberOfCurrentReads == mNumberOfClientsStarted) {
+        mLastReadCompleted = true;
+        mNumberOfCurrentReads = 0;
+        mAllReadsCompleteCondition.broadcast();
+    }
+}
+
+status_t MediaSourceSplitter::pause(int clientId) {
+    return ERROR_UNSUPPORTED;
+}
+
+// Client
+
+MediaSourceSplitter::Client::Client(
+        sp<MediaSourceSplitter> splitter,
+        int32_t clientId) {
+    mSplitter = splitter;
+    mClientId = clientId;
+}
+
+status_t MediaSourceSplitter::Client::start(MetaData *params) {
+    return mSplitter->start(mClientId, params);
+}
+
+status_t MediaSourceSplitter::Client::stop() {
+    return mSplitter->stop(mClientId);
+}
+
+sp<MetaData> MediaSourceSplitter::Client::getFormat() {
+    return mSplitter->getFormat(mClientId);
+}
+
+status_t MediaSourceSplitter::Client::read(
+        MediaBuffer **buffer, const ReadOptions *options) {
+    return mSplitter->read(mClientId, buffer, options);
+}
+
+status_t MediaSourceSplitter::Client::pause() {
+    return mSplitter->pause(mClientId);
+}
+
+}  // namespace android
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index b67002d..a69c396 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -510,5 +510,13 @@
     mSuspended = true;
 }
 
+DecryptHandle* NuCachedSource2::DrmInitialization(DrmManagerClient* client) {
+    return mSource->DrmInitialization(client);
+}
+
+void NuCachedSource2::getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {
+    mSource->getDrmInfo(handle, client);
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 2743b2f..7aac447 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -67,7 +67,9 @@
       mPort(0),
       mOffset(0),
       mContentLength(0),
-      mContentLengthValid(false) {
+      mContentLengthValid(false),
+      mDecryptHandle(NULL),
+      mDrmManagerClient(NULL) {
 }
 
 NuHTTPDataSource::~NuHTTPDataSource() {
@@ -89,6 +91,9 @@
         off_t offset) {
     String8 host, path;
     unsigned port;
+
+    mUri = uri;
+
     if (!ParseURL(uri, &host, &port, &path)) {
         return ERROR_MALFORMED;
     }
@@ -340,4 +345,30 @@
     }
 }
 
+DecryptHandle* NuHTTPDataSource::DrmInitialization(DrmManagerClient* client) {
+    if (client == NULL) {
+        return NULL;
+    }
+    mDrmManagerClient = client;
+
+    if (mDecryptHandle == NULL) {
+        /* Note if redirect occurs, mUri is the redirect uri instead of the
+         * original one
+         */
+        mDecryptHandle = mDrmManagerClient->openDecryptSession(mUri);
+    }
+
+    if (mDecryptHandle == NULL) {
+        mDrmManagerClient = NULL;
+    }
+
+    return mDecryptHandle;
+}
+
+void NuHTTPDataSource::getDrmInfo(DecryptHandle **handle, DrmManagerClient **client) {
+    *handle = mDecryptHandle;
+
+    *client = mDrmManagerClient;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 980da77..9f9c83a 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -39,6 +39,7 @@
 #include <binder/MemoryDealer.h>
 #include <binder/ProcessState.h>
 #include <media/IMediaPlayerService.h>
+#include <media/stagefright/HardwareAPI.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDebug.h>
@@ -152,37 +153,37 @@
 
 static const CodecInfo kDecoderInfo[] = {
     { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },
+//    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.Nvidia.mp3.decoder" },
 //    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },
     { MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" },
-//    { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.PV.mp3dec" },
 //    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" },
+//    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amr.decoder" },
     { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" },
-//    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrdec" },
+//    { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amrwb.decoder" },
     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" },
     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" },
-//    { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.PV.amrdec" },
+//    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.Nvidia.aac.decoder" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },
-//    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },
     { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" },
     { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" },
+    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.decode" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },
-//    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4dec" },
+    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.decode" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.decoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },
-//    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263dec" },
+    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.Nvidia.h264.decode" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.decoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },
-//    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcdec" },
     { MEDIA_MIMETYPE_AUDIO_VORBIS, "VorbisDecoder" },
     { MEDIA_MIMETYPE_VIDEO_VPX, "VPXDecoder" },
 };
@@ -194,25 +195,24 @@
     { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBEncoder" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
     { MEDIA_MIMETYPE_AUDIO_AAC, "AACEncoder" },
-//    { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacenc" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.encoder" },
+    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.encoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Encoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Encoder" },
-//    { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4enc" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.encoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.encoder.h263" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.encoder" },
+    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.encoder" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Encoder" },
     { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Encoder" },
-//    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263enc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.encoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.encoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" },
+    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.Nvidia.h264.encoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Encoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "AVCEncoder" },
-//    { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcenc" },
 };
 
 #undef OPTIONAL
@@ -307,16 +307,15 @@
 }
 
 static bool IsSoftwareCodec(const char *componentName) {
-    if (!strncmp("OMX.PV.", componentName, 7)) {
-        return true;
+    if (!strncmp("OMX.", componentName, 4)) {
+        return false;
     }
 
-    return false;
+    return true;
 }
 
 // A sort order in which non-OMX components are first,
-// followed by software codecs, i.e. OMX.PV.*, followed
-// by all the others.
+// followed by software codecs, and followed by all the others.
 static int CompareSoftwareCodecsFirst(
         const String8 *elem1, const String8 *elem2) {
     bool isNotOMX1 = strncmp(elem1->string(), "OMX.", 4);
@@ -350,9 +349,13 @@
         const char *componentName, bool isEncoder) {
     uint32_t quirks = 0;
 
-    if (!strcmp(componentName, "OMX.PV.avcdec")) {
-        quirks |= kWantsNALFragments;
+    if (!strcmp(componentName, "OMX.Nvidia.amr.decoder") ||
+         !strcmp(componentName, "OMX.Nvidia.amrwb.decoder") ||
+         !strcmp(componentName, "OMX.Nvidia.aac.decoder") ||
+         !strcmp(componentName, "OMX.Nvidia.mp3.decoder")) {
+        quirks |= kDecoderLiesAboutNumberOfChannels;
     }
+
     if (!strcmp(componentName, "OMX.TI.MP3.decode")) {
         quirks |= kNeedsFlushBeforeDisable;
         quirks |= kDecoderLiesAboutNumberOfChannels;
@@ -412,13 +415,6 @@
         quirks |= kOutputBuffersAreUnreadable;
     }
 
-    if (!strncmp(componentName, "OMX.SEC.", 8) && isEncoder) {
-        // These input buffers contain meta data (for instance,
-        // information helps locate the actual YUV data, or
-        // the physical address of the YUV data).
-        quirks |= kStoreMetaDataInInputVideoBuffers;
-    }
-
     return quirks;
 }
 
@@ -454,7 +450,16 @@
             continue;
         }
 
-        matchingCodecs->push(String8(componentName));
+        // When requesting software-only codecs, only push software codecs
+        // When requesting hardware-only codecs, only push hardware codecs
+        // When there is request neither for software-only nor for
+        // hardware-only codecs, push all codecs
+        if (((flags & kSoftwareCodecsOnly) &&   IsSoftwareCodec(componentName)) ||
+            ((flags & kHardwareCodecsOnly) &&  !IsSoftwareCodec(componentName)) ||
+            (!(flags & (kSoftwareCodecsOnly | kHardwareCodecsOnly)))) {
+
+            matchingCodecs->push(String8(componentName));
+        }
     }
 
     if (flags & kPreferSoftwareCodecs) {
@@ -468,7 +473,8 @@
         const sp<MetaData> &meta, bool createEncoder,
         const sp<MediaSource> &source,
         const char *matchComponentName,
-        uint32_t flags) {
+        uint32_t flags,
+        const sp<ANativeWindow> &nativeWindow) {
     const char *mime;
     bool success = meta->findCString(kKeyMIMEType, &mime);
     CHECK(success);
@@ -524,7 +530,7 @@
             sp<OMXCodec> codec = new OMXCodec(
                     omx, node, quirks,
                     createEncoder, mime, componentName,
-                    source);
+                    source, nativeWindow);
 
             observer->setCodec(codec);
 
@@ -542,6 +548,10 @@
 }
 
 status_t OMXCodec::configureCodec(const sp<MetaData> &meta, uint32_t flags) {
+    mIsMetaDataStoredInVideoBuffers = false;
+    if (flags & kStoreMetaDataInVideoBuffers) {
+        mIsMetaDataStoredInVideoBuffers = true;
+    }
     if (!(flags & kIgnoreCodecSpecificData)) {
         uint32_t type;
         const void *data;
@@ -732,6 +742,16 @@
         mQuirks &= ~kOutputBuffersAreUnreadable;
     }
 
+    if (mNativeWindow != NULL
+        && !mIsEncoder
+        && !strncasecmp(mMIME, "video/", 6)
+        && !strncmp(mComponentName, "OMX.", 4)) {
+        status_t err = initNativeWindow();
+        if (err != OK) {
+            return err;
+        }
+    }
+
     return OK;
 }
 
@@ -1290,6 +1310,10 @@
     h264type.bMBAFF = OMX_FALSE;
     h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;
 
+    if (!strcasecmp("OMX.Nvidia.h264.encoder", mComponentName)) {
+        h264type.eLevel = OMX_VIDEO_AVCLevelMax;
+    }
+
     err = mOMX->setParameter(
             mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
     CHECK_EQ(err, OK);
@@ -1415,7 +1439,8 @@
         bool isEncoder,
         const char *mime,
         const char *componentName,
-        const sp<MediaSource> &source)
+        const sp<MediaSource> &source,
+        const sp<ANativeWindow> &nativeWindow)
     : mOMX(omx),
       mOMXLivesLocally(omx->livesLocally(getpid())),
       mNode(node),
@@ -1435,7 +1460,8 @@
       mTargetTimeUs(-1),
       mSkipTimeUs(-1),
       mLeftOverBuffer(NULL),
-      mPaused(false) {
+      mPaused(false),
+      mNativeWindow(nativeWindow) {
     mPortStatus[kPortIndexInput] = ENABLED;
     mPortStatus[kPortIndexOutput] = ENABLED;
 
@@ -1579,6 +1605,10 @@
 }
 
 status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) {
+    if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
+        return allocateOutputBuffersFromNativeWindow();
+    }
+
     OMX_PARAM_PORTDEFINITIONTYPE def;
     InitOMXParams(&def);
     def.nPortIndex = portIndex;
@@ -1590,6 +1620,14 @@
         return err;
     }
 
+    if (mIsMetaDataStoredInVideoBuffers && portIndex == kPortIndexInput) {
+        err = mOMX->storeMetaDataInBuffers(mNode, kPortIndexInput, OMX_TRUE);
+        if (err != OK) {
+            LOGE("Storing meta data in video buffers is not supported");
+            return err;
+        }
+    }
+
     CODEC_LOGI("allocating %lu buffers of size %lu on %s port",
             def.nBufferCountActual, def.nBufferSize,
             portIndex == kPortIndexInput ? "input" : "output");
@@ -1645,6 +1683,7 @@
 
         info.mBuffer = buffer;
         info.mOwnedByComponent = false;
+        info.mOwnedByNativeWindow = false;
         info.mMem = mem;
         info.mMediaBuffer = NULL;
 
@@ -1671,6 +1710,173 @@
     return OK;
 }
 
+status_t OMXCodec::allocateOutputBuffersFromNativeWindow() {
+    // Get the number of buffers needed.
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->getParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    if (err != OK) {
+        return err;
+    }
+
+    err = native_window_set_buffers_geometry(
+            mNativeWindow.get(),
+            def.format.video.nFrameWidth,
+            def.format.video.nFrameHeight,
+            def.format.video.eColorFormat);
+
+    if (err != 0) {
+        LOGE("native_window_set_buffers_geometry failed: %s (%d)",
+                strerror(-err), -err);
+        return err;
+    }
+
+    // Increase the buffer count by one to allow for the ANativeWindow to hold
+    // on to one of the buffers.
+    def.nBufferCountActual++;
+    err = mOMX->setParameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    if (err != OK) {
+        return err;
+    }
+
+    // Set up the native window.
+    // XXX TODO: Get the gralloc usage flags from the OMX plugin!
+    err = native_window_set_usage(
+            mNativeWindow.get(), GRALLOC_USAGE_HW_TEXTURE);
+    if (err != 0) {
+        LOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
+        return err;
+    }
+
+    err = native_window_set_buffer_count(
+            mNativeWindow.get(), def.nBufferCountActual);
+    if (err != 0) {
+        LOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
+                -err);
+        return err;
+    }
+
+    // XXX TODO: Do something so the ANativeWindow knows that we'll need to get
+    // the same set of buffers.
+
+    CODEC_LOGI("allocating %lu buffers from a native window of size %lu on "
+            "output port", def.nBufferCountActual, def.nBufferSize);
+
+    // Dequeue buffers and send them to OMX
+    OMX_U32 i;
+    for (i = 0; i < def.nBufferCountActual; i++) {
+        android_native_buffer_t* buf;
+        err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
+        if (err != 0) {
+            LOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
+            break;
+        }
+
+        sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
+        IOMX::buffer_id bufferId;
+        err = mOMX->useGraphicBuffer(mNode, kPortIndexOutput, graphicBuffer,
+                &bufferId);
+        if (err != 0) {
+            break;
+        }
+
+        CODEC_LOGV("registered graphic buffer with ID %p (pointer = %p)",
+                bufferId, graphicBuffer.get());
+
+        BufferInfo info;
+        info.mData = NULL;
+        info.mSize = def.nBufferSize;
+        info.mBuffer = bufferId;
+        info.mOwnedByComponent = false;
+        info.mOwnedByNativeWindow = false;
+        info.mMem = NULL;
+        info.mMediaBuffer = new MediaBuffer(graphicBuffer);
+        info.mMediaBuffer->setObserver(this);
+
+        mPortBuffers[kPortIndexOutput].push(info);
+    }
+
+    OMX_U32 cancelStart;
+    OMX_U32 cancelEnd;
+
+    if (err != 0) {
+        // If an error occurred while dequeuing we need to cancel any buffers
+        // that were dequeued.
+        cancelStart = 0;
+        cancelEnd = i;
+    } else {
+        // Return the last two buffers to the native window.
+        // XXX TODO: The number of buffers the native window owns should probably be
+        // queried from it when we put the native window in fixed buffer pool mode
+        // (which needs to be implemented).  Currently it's hard-coded to 2.
+        cancelStart = def.nBufferCountActual - 2;
+        cancelEnd = def.nBufferCountActual;
+    }
+
+    for (OMX_U32 i = cancelStart; i < cancelEnd; i++) {
+        BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(i);
+        cancelBufferToNativeWindow(info);
+    }
+
+    return err;
+}
+
+status_t OMXCodec::cancelBufferToNativeWindow(BufferInfo *info) {
+    CHECK(!info->mOwnedByNativeWindow);
+    CODEC_LOGV("Calling cancelBuffer on buffer %p", info->mBuffer);
+    int err = mNativeWindow->cancelBuffer(
+        mNativeWindow.get(), info->mMediaBuffer->graphicBuffer().get());
+    if (err != 0) {
+      CODEC_LOGE("cancelBuffer failed w/ error 0x%08x", err);
+
+      setState(ERROR);
+      return err;
+    }
+    info->mOwnedByNativeWindow = true;
+    return OK;
+}
+
+OMXCodec::BufferInfo* OMXCodec::dequeueBufferFromNativeWindow() {
+    // Dequeue the next buffer from the native window.
+    android_native_buffer_t* buf;
+    int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf);
+    if (err != 0) {
+      CODEC_LOGE("dequeueBuffer failed w/ error 0x%08x", err);
+
+      setState(ERROR);
+      return 0;
+    }
+
+    // Determine which buffer we just dequeued.
+    Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
+    BufferInfo *bufInfo = 0;
+    for (size_t i = 0; i < buffers->size(); i++) {
+      sp<GraphicBuffer> graphicBuffer = buffers->itemAt(i).
+          mMediaBuffer->graphicBuffer();
+      if (graphicBuffer->handle == buf->handle) {
+        bufInfo = &buffers->editItemAt(i);
+        break;
+      }
+    }
+
+    if (bufInfo == 0) {
+        CODEC_LOGE("dequeued unrecognized buffer: %p", buf);
+
+        setState(ERROR);
+        return 0;
+    }
+
+    // The native window no longer owns the buffer.
+    CHECK(bufInfo->mOwnedByNativeWindow);
+    bufInfo->mOwnedByNativeWindow = false;
+
+    return bufInfo;
+}
+
 void OMXCodec::on_message(const omx_message &msg) {
     Mutex::Autolock autoLock(mLock);
 
@@ -1702,24 +1908,20 @@
                      "an EMPTY_BUFFER_DONE.", buffer);
             }
 
-            {
-                BufferInfo *info = &buffers->editItemAt(i);
-                info->mOwnedByComponent = false;
-                if (info->mMediaBuffer != NULL) {
-                    // It is time to release the media buffers storing meta data
-                    info->mMediaBuffer->release();
-                    info->mMediaBuffer = NULL;
-                }
+            BufferInfo* info = &buffers->editItemAt(i);
+            info->mOwnedByComponent = false;
+
+            // Buffer could not be released until empty buffer done is called.
+            if (info->mMediaBuffer != NULL) {
+                info->mMediaBuffer->release();
+                info->mMediaBuffer = NULL;
             }
 
             if (mPortStatus[kPortIndexInput] == DISABLING) {
                 CODEC_LOGV("Port is disabled, freeing buffer %p", buffer);
 
-                status_t err =
-                    mOMX->freeBuffer(mNode, kPortIndexInput, buffer);
+                status_t err = freeBuffer(kPortIndexInput, i);
                 CHECK_EQ(err, OK);
-
-                buffers->removeAt(i);
             } else if (mState != ERROR
                     && mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
                 CHECK_EQ(mPortStatus[kPortIndexInput], ENABLED);
@@ -1759,11 +1961,9 @@
             if (mPortStatus[kPortIndexOutput] == DISABLING) {
                 CODEC_LOGV("Port is disabled, freeing buffer %p", buffer);
 
-                status_t err =
-                    mOMX->freeBuffer(mNode, kPortIndexOutput, buffer);
+                status_t err = freeBuffer(kPortIndexOutput, i);
                 CHECK_EQ(err, OK);
 
-                buffers->removeAt(i);
 #if 0
             } else if (mPortStatus[kPortIndexOutput] == ENABLED
                        && (flags & OMX_BUFFERFLAG_EOS)) {
@@ -1791,8 +1991,10 @@
                 }
 
                 MediaBuffer *buffer = info->mMediaBuffer;
+                bool isGraphicBuffer = buffer->graphicBuffer() != NULL;
 
-                if (msg.u.extended_buffer_data.range_offset
+                if (!isGraphicBuffer
+                    && msg.u.extended_buffer_data.range_offset
                         + msg.u.extended_buffer_data.range_length
                             > buffer->size()) {
                     CODEC_LOGE(
@@ -1816,7 +2018,7 @@
                     buffer->meta_data()->setInt32(kKeyIsCodecConfig, true);
                 }
 
-                if (mQuirks & kOutputBuffersAreUnreadable) {
+                if (isGraphicBuffer || mQuirks & kOutputBuffersAreUnreadable) {
                     buffer->meta_data()->setInt32(kKeyIsUnreadable, true);
                 }
 
@@ -1886,7 +2088,43 @@
 
         case OMX_EventPortSettingsChanged:
         {
-            onPortSettingsChanged(data1);
+            CODEC_LOGV("OMX_EventPortSettingsChanged(port=%ld, data2=0x%08lx)",
+                       data1, data2);
+
+            if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
+                onPortSettingsChanged(data1);
+            } else if (data1 == kPortIndexOutput
+                    && data2 == OMX_IndexConfigCommonOutputCrop) {
+
+                OMX_CONFIG_RECTTYPE rect;
+                rect.nPortIndex = kPortIndexOutput;
+                InitOMXParams(&rect);
+
+                status_t err =
+                         mOMX->getConfig(
+                             mNode, OMX_IndexConfigCommonOutputCrop,
+                             &rect, sizeof(rect));
+
+                if (err == OK) {
+                    CODEC_LOGV(
+                            "output crop (%ld, %ld, %ld, %ld)",
+                            rect.nLeft, rect.nTop, rect.nWidth, rect.nHeight);
+
+                    if (mNativeWindow != NULL) {
+                        android_native_rect_t crop;
+                        crop.left = rect.nLeft;
+                        crop.top = rect.nTop;
+                        crop.right = crop.left + rect.nWidth - 1;
+                        crop.bottom = crop.top + rect.nHeight - 1;
+
+                        CHECK_EQ(0, native_window_set_crop(
+                                    mNativeWindow.get(), &crop));
+                    }
+                } else {
+                    CODEC_LOGE("getConfig(OMX_IndexConfigCommonOutputCrop) "
+                               "returned error 0x%08x", err);
+                }
+            }
             break;
         }
 
@@ -2203,24 +2441,12 @@
 
         CODEC_LOGV("freeing buffer %p on port %ld", info->mBuffer, portIndex);
 
-        status_t err =
-            mOMX->freeBuffer(mNode, portIndex, info->mBuffer);
+        status_t err = freeBuffer(portIndex, i);
 
         if (err != OK) {
             stickyErr = err;
         }
 
-        if (info->mMediaBuffer != NULL) {
-            info->mMediaBuffer->setObserver(NULL);
-
-            // Make sure nobody but us owns this buffer at this point.
-            CHECK_EQ(info->mMediaBuffer->refcount(), 0);
-
-            info->mMediaBuffer->release();
-            info->mMediaBuffer = NULL;
-        }
-
-        buffers->removeAt(i);
     }
 
     CHECK(onlyThoseWeOwn || buffers->isEmpty());
@@ -2228,6 +2454,35 @@
     return stickyErr;
 }
 
+status_t OMXCodec::freeBuffer(OMX_U32 portIndex, size_t bufIndex) {
+    Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+
+    BufferInfo *info = &buffers->editItemAt(bufIndex);
+
+    status_t err = mOMX->freeBuffer(mNode, portIndex, info->mBuffer);
+
+    if (err == OK && info->mMediaBuffer != NULL) {
+        info->mMediaBuffer->setObserver(NULL);
+
+        // Make sure nobody but us owns this buffer at this point.
+        CHECK_EQ(info->mMediaBuffer->refcount(), 0);
+
+        // Cancel the buffer if it belongs to an ANativeWindow.
+        sp<GraphicBuffer> graphicBuffer = info->mMediaBuffer->graphicBuffer();
+        if (!info->mOwnedByNativeWindow && graphicBuffer != 0) {
+            err = cancelBufferToNativeWindow(info);
+        }
+
+        info->mMediaBuffer->release();
+    }
+
+    if (err == OK) {
+        buffers->removeAt(bufIndex);
+    }
+
+    return err;
+}
+
 void OMXCodec::onPortSettingsChanged(OMX_U32 portIndex) {
     CODEC_LOGV("PORT_SETTINGS_CHANGED(%ld)", portIndex);
 
@@ -2277,6 +2532,7 @@
     CHECK_EQ(mPortStatus[portIndex], ENABLED);
     mPortStatus[portIndex] = DISABLING;
 
+    CODEC_LOGV("sending OMX_CommandPortDisable(%ld)", portIndex);
     status_t err =
         mOMX->sendCommand(mNode, OMX_CommandPortDisable, portIndex);
     CHECK_EQ(err, OK);
@@ -2290,6 +2546,7 @@
     CHECK_EQ(mPortStatus[portIndex], DISABLED);
     mPortStatus[portIndex] = ENABLING;
 
+    CODEC_LOGV("sending OMX_CommandPortEnable(%ld)", portIndex);
     status_t err =
         mOMX->sendCommand(mNode, OMX_CommandPortEnable, portIndex);
     CHECK_EQ(err, OK);
@@ -2315,7 +2572,10 @@
 
     Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
     for (size_t i = 0; i < buffers->size(); ++i) {
-        fillOutputBuffer(&buffers->editItemAt(i));
+        BufferInfo *info = &buffers->editItemAt(i);
+        if (!info->mOwnedByNativeWindow) {
+            fillOutputBuffer(&buffers->editItemAt(i));
+        }
     }
 }
 
@@ -2450,16 +2710,16 @@
             break;
         }
 
-        // Do not release the media buffer if it stores meta data
-        // instead of YUV data. The release is delayed until
-        // EMPTY_BUFFER_DONE callback is received.
         bool releaseBuffer = true;
         if (mIsEncoder && (mQuirks & kAvoidMemcopyInputRecordingFrames)) {
             CHECK(mOMXLivesLocally && offset == 0);
             OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *) info->mBuffer;
             header->pBuffer = (OMX_U8 *) srcBuffer->data() + srcBuffer->range_offset();
+            releaseBuffer = false;
+            info->mMediaBuffer = srcBuffer;
+            // FIXME: we are leaking memory
         } else {
-            if (mQuirks & kStoreMetaDataInInputVideoBuffers) {
+            if (mIsMetaDataStoredInVideoBuffers) {
                 releaseBuffer = false;
                 info->mMediaBuffer = srcBuffer;
             }
@@ -2542,7 +2802,23 @@
         return;
     }
 
-    CODEC_LOGV("Calling fill_buffer on buffer %p", info->mBuffer);
+    sp<GraphicBuffer> graphicBuffer = info->mMediaBuffer->graphicBuffer();
+    if (graphicBuffer != 0) {
+        // When using a native buffer we need to lock the buffer before giving
+        // it to OMX.
+        CHECK(!info->mOwnedByNativeWindow);
+        CODEC_LOGV("Calling lockBuffer on %p", info->mBuffer);
+        int err = mNativeWindow->lockBuffer(mNativeWindow.get(),
+                graphicBuffer.get());
+        if (err != 0) {
+            CODEC_LOGE("lockBuffer failed w/ error 0x%08x", err);
+
+            setState(ERROR);
+            return;
+        }
+    }
+
+    CODEC_LOGV("Calling fillBuffer on buffer %p", info->mBuffer);
     status_t err = mOMX->fillBuffer(mNode, info->mBuffer);
 
     if (err != OK) {
@@ -3125,7 +3401,32 @@
 
         if (info->mMediaBuffer == buffer) {
             CHECK_EQ(mPortStatus[kPortIndexOutput], ENABLED);
-            fillOutputBuffer(info);
+            if (buffer->graphicBuffer() == 0) {
+                fillOutputBuffer(info);
+            } else {
+                sp<MetaData> metaData = info->mMediaBuffer->meta_data();
+                int32_t rendered = 0;
+                if (!metaData->findInt32(kKeyRendered, &rendered)) {
+                    rendered = 0;
+                }
+                if (!rendered) {
+                    status_t err = cancelBufferToNativeWindow(info);
+                    if (err < 0) {
+                        return;
+                    }
+                } else {
+                    info->mOwnedByNativeWindow = true;
+                }
+
+                // Dequeue the next buffer from the native window.
+                BufferInfo *nextBufInfo = dequeueBufferFromNativeWindow();
+                if (nextBufInfo == 0) {
+                    return;
+                }
+
+                // Give the buffer to the OMX node to fill.
+                fillOutputBuffer(nextBufInfo);
+            }
             return;
         }
     }
@@ -3458,6 +3759,18 @@
     printf("}\n");
 }
 
+status_t OMXCodec::initNativeWindow() {
+    // Enable use of a GraphicBuffer as the output for this node.  This must
+    // happen before getting the IndexParamPortDefinition parameter because it
+    // will affect the pixel format that the node reports.
+    status_t err = mOMX->enableGraphicBuffers(mNode, kPortIndexOutput, OMX_TRUE);
+    if (err != 0) {
+        return err;
+    }
+
+    return OK;
+}
+
 void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {
     mOutputFormat = new MetaData;
     mOutputFormat->setCString(kKeyDecoderComponent, mComponentName);
@@ -3592,17 +3905,8 @@
                 CHECK(!"Unknown compression format.");
             }
 
-            if (!strcmp(mComponentName, "OMX.PV.avcdec")) {
-                // This component appears to be lying to me.
-                mOutputFormat->setInt32(
-                        kKeyWidth, (video_def->nFrameWidth + 15) & -16);
-                mOutputFormat->setInt32(
-                        kKeyHeight, (video_def->nFrameHeight + 15) & -16);
-            } else {
-                mOutputFormat->setInt32(kKeyWidth, video_def->nFrameWidth);
-                mOutputFormat->setInt32(kKeyHeight, video_def->nFrameHeight);
-            }
-
+            mOutputFormat->setInt32(kKeyWidth, video_def->nFrameWidth);
+            mOutputFormat->setInt32(kKeyHeight, video_def->nFrameHeight);
             mOutputFormat->setInt32(kKeyColorFormat, video_def->eColorFormat);
             break;
         }
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 27faf4f..092c33e 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -281,7 +281,7 @@
     mNumSyncSamples = U32_AT(&header[4]);
 
     if (mNumSyncSamples < 2) {
-        LOGW("Table of sync samples is empty or has only a single entry!");
+        LOGV("Table of sync samples is empty or has only a single entry!");
     }
 
     mSyncSamples = new uint32_t[mNumSyncSamples];
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 1629e9f..6c05e03 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -39,7 +39,7 @@
         ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
         ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
         ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
-        ".mkv", ".mka", ".webm", ".ts"
+        ".mkv", ".mka", ".webm", ".ts", ".fl"
     };
     static const size_t kNumValidExtensions =
         sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
diff --git a/media/libstagefright/VideoSourceDownSampler.cpp b/media/libstagefright/VideoSourceDownSampler.cpp
new file mode 100644
index 0000000..ea7b09a
--- /dev/null
+++ b/media/libstagefright/VideoSourceDownSampler.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "VideoSourceDownSampler"
+
+#include <media/stagefright/VideoSourceDownSampler.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/YUVImage.h>
+#include <media/stagefright/YUVCanvas.h>
+#include "OMX_Video.h"
+
+namespace android {
+
+VideoSourceDownSampler::VideoSourceDownSampler(const sp<MediaSource> &videoSource,
+        int32_t width, int32_t height) {
+    LOGV("Construct VideoSourceDownSampler");
+    CHECK(width > 0);
+    CHECK(height > 0);
+
+    mRealVideoSource = videoSource;
+    mWidth = width;
+    mHeight = height;
+
+    mMeta = new MetaData(*(mRealVideoSource->getFormat()));
+    CHECK(mMeta->findInt32(kKeyWidth, &mRealSourceWidth));
+    CHECK(mMeta->findInt32(kKeyHeight, &mRealSourceHeight));
+
+    if ((mWidth != mRealSourceWidth) || (mHeight != mRealSourceHeight)) {
+        // Change meta data for width and height.
+        CHECK(mWidth <= mRealSourceWidth);
+        CHECK(mHeight <= mRealSourceHeight);
+
+        mNeedDownSampling = true;
+        computeDownSamplingParameters();
+        mMeta->setInt32(kKeyWidth, mWidth);
+        mMeta->setInt32(kKeyHeight, mHeight);
+    } else {
+        mNeedDownSampling = false;
+    }
+}
+
+VideoSourceDownSampler::~VideoSourceDownSampler() {
+}
+
+void VideoSourceDownSampler::computeDownSamplingParameters() {
+    mDownSampleSkipX = mRealSourceWidth / mWidth;
+    mDownSampleSkipY = mRealSourceHeight / mHeight;
+
+    mDownSampleOffsetX = mRealSourceWidth - mDownSampleSkipX * mWidth;
+    mDownSampleOffsetY = mRealSourceHeight - mDownSampleSkipY * mHeight;
+}
+
+void VideoSourceDownSampler::downSampleYUVImage(
+        const MediaBuffer &sourceBuffer, MediaBuffer **buffer) const {
+    // find the YUV format
+    int32_t srcFormat;
+    CHECK(mMeta->findInt32(kKeyColorFormat, &srcFormat));
+    YUVImage::YUVFormat yuvFormat;
+    if (srcFormat == OMX_COLOR_FormatYUV420SemiPlanar) {
+        yuvFormat = YUVImage::YUV420SemiPlanar;
+    } else if (srcFormat == OMX_COLOR_FormatYUV420Planar) {
+        yuvFormat = YUVImage::YUV420Planar;
+    }
+
+    // allocate mediaBuffer for down sampled image and setup a canvas.
+    *buffer = new MediaBuffer(YUVImage::bufferSize(yuvFormat, mWidth, mHeight));
+    YUVImage yuvDownSampledImage(yuvFormat,
+            mWidth, mHeight,
+            (uint8_t *)(*buffer)->data());
+    YUVCanvas yuvCanvasDownSample(yuvDownSampledImage);
+
+    YUVImage yuvImageSource(yuvFormat,
+            mRealSourceWidth, mRealSourceHeight,
+            (uint8_t *)sourceBuffer.data());
+    yuvCanvasDownSample.downsample(mDownSampleOffsetX, mDownSampleOffsetY,
+            mDownSampleSkipX, mDownSampleSkipY,
+            yuvImageSource);
+}
+
+status_t VideoSourceDownSampler::start(MetaData *params) {
+    LOGV("start");
+    return mRealVideoSource->start();
+}
+
+status_t VideoSourceDownSampler::stop() {
+    LOGV("stop");
+    return mRealVideoSource->stop();
+}
+
+sp<MetaData> VideoSourceDownSampler::getFormat() {
+    LOGV("getFormat");
+    return mMeta;
+}
+
+status_t VideoSourceDownSampler::read(
+        MediaBuffer **buffer, const ReadOptions *options) {
+    LOGV("read");
+    MediaBuffer *realBuffer;
+    status_t err = mRealVideoSource->read(&realBuffer, options);
+
+    if (mNeedDownSampling) {
+        downSampleYUVImage(*realBuffer, buffer);
+
+        int64_t frameTime;
+        realBuffer->meta_data()->findInt64(kKeyTime, &frameTime);
+        (*buffer)->meta_data()->setInt64(kKeyTime, frameTime);
+
+        // We just want this buffer to be deleted when the encoder releases it.
+        // So don't add a reference to it and set the observer to NULL.
+        (*buffer)->setObserver(NULL);
+
+        // The original buffer is no longer required. Release it.
+        realBuffer->release();
+    } else {
+        *buffer = realBuffer;
+    }
+
+    return err;
+}
+
+status_t VideoSourceDownSampler::pause() {
+    LOGV("pause");
+    return mRealVideoSource->pause();
+}
+
+}  // namespace android
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
new file mode 100644
index 0000000..7c72852
--- /dev/null
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "WVMExtractor"
+#include <utils/Log.h>
+
+#include "include/WVMExtractor.h"
+
+#include <arpa/inet.h>
+#include <utils/String8.h>
+#include <media/stagefright/Utils.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDebug.h>
+#include <dlfcn.h>
+
+#include <utils/Errors.h>
+
+/* The extractor lifetime is short - just long enough to get
+ * the media sources constructed - so the shared lib needs to remain open
+ * beyond the lifetime of the extractor.  So keep the handle as a global
+ * rather than a member of the extractor
+ */
+void *gVendorLibHandle = NULL;
+
+namespace android {
+
+static Mutex gWVMutex;
+
+WVMExtractor::WVMExtractor(const sp<DataSource> &source)
+    : mDataSource(source) {
+    {
+        Mutex::Autolock autoLock(gWVMutex);
+        if (gVendorLibHandle == NULL) {
+            gVendorLibHandle = dlopen("libwvm.so", RTLD_NOW);
+        }
+
+        if (gVendorLibHandle == NULL) {
+            LOGE("Failed to open libwvm.so");
+            return;
+        }
+    }
+
+    typedef MediaExtractor *(*GetInstanceFunc)(sp<DataSource>);
+    GetInstanceFunc getInstanceFunc =
+        (GetInstanceFunc) dlsym(gVendorLibHandle,
+                "_ZN7android11GetInstanceENS_2spINS_10DataSourceEEE");
+
+    if (getInstanceFunc) {
+        LOGD("Calling GetInstanceFunc");
+        mImpl = (*getInstanceFunc)(source);
+        CHECK(mImpl != NULL);
+    } else {
+        LOGE("Failed to locate GetInstance in libwvm.so");
+    }
+}
+
+WVMExtractor::~WVMExtractor() {
+}
+
+size_t WVMExtractor::countTracks() {
+    return (mImpl != NULL) ? mImpl->countTracks() : 0;
+}
+
+sp<MediaSource> WVMExtractor::getTrack(size_t index) {
+    if (mImpl == NULL) {
+        return NULL;
+    }
+    return mImpl->getTrack(index);
+}
+
+sp<MetaData> WVMExtractor::getTrackMetaData(size_t index, uint32_t flags) {
+    if (mImpl == NULL) {
+        return NULL;
+    }
+    return mImpl->getTrackMetaData(index, flags);
+}
+
+sp<MetaData> WVMExtractor::getMetaData() {
+    if (mImpl == NULL) {
+        return NULL;
+    }
+    return mImpl->getMetaData();
+}
+
+} //namespace android
+
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index 52a391f..a6b179e 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -33,6 +33,80 @@
 
 namespace android {
 
+static status_t ConvertOmxAvcProfileToAvcSpecProfile(
+        int32_t omxProfile, AVCProfile* pvProfile) {
+    LOGV("ConvertOmxAvcProfileToAvcSpecProfile: %d", omxProfile);
+    switch (omxProfile) {
+        case OMX_VIDEO_AVCProfileBaseline:
+            *pvProfile = AVC_BASELINE;
+            return OK;
+        default:
+            LOGE("Unsupported omx profile: %d", omxProfile);
+    }
+    return BAD_VALUE;
+}
+
+static status_t ConvertOmxAvcLevelToAvcSpecLevel(
+        int32_t omxLevel, AVCLevel *pvLevel) {
+    LOGV("ConvertOmxAvcLevelToAvcSpecLevel: %d", omxLevel);
+    AVCLevel level = AVC_LEVEL5_1;
+    switch (omxLevel) {
+        case OMX_VIDEO_AVCLevel1:
+            level = AVC_LEVEL1_B;
+            break;
+        case OMX_VIDEO_AVCLevel1b:
+            level = AVC_LEVEL1;
+            break;
+        case OMX_VIDEO_AVCLevel11:
+            level = AVC_LEVEL1_1;
+            break;
+        case OMX_VIDEO_AVCLevel12:
+            level = AVC_LEVEL1_2;
+            break;
+        case OMX_VIDEO_AVCLevel13:
+            level = AVC_LEVEL1_3;
+            break;
+        case OMX_VIDEO_AVCLevel2:
+            level = AVC_LEVEL2;
+            break;
+        case OMX_VIDEO_AVCLevel21:
+            level = AVC_LEVEL2_1;
+            break;
+        case OMX_VIDEO_AVCLevel22:
+            level = AVC_LEVEL2_2;
+            break;
+        case OMX_VIDEO_AVCLevel3:
+            level = AVC_LEVEL3;
+            break;
+        case OMX_VIDEO_AVCLevel31:
+            level = AVC_LEVEL3_1;
+            break;
+        case OMX_VIDEO_AVCLevel32:
+            level = AVC_LEVEL3_2;
+            break;
+        case OMX_VIDEO_AVCLevel4:
+            level = AVC_LEVEL4;
+            break;
+        case OMX_VIDEO_AVCLevel41:
+            level = AVC_LEVEL4_1;
+            break;
+        case OMX_VIDEO_AVCLevel42:
+            level = AVC_LEVEL4_2;
+            break;
+        case OMX_VIDEO_AVCLevel5:
+            level = AVC_LEVEL5;
+            break;
+        case OMX_VIDEO_AVCLevel51:
+            level = AVC_LEVEL5_1;
+            break;
+        default:
+            LOGE("Unknown omx level: %d", omxLevel);
+            return BAD_VALUE;
+    }
+    *pvLevel = level;
+    return OK;
+}
+
 inline static void ConvertYUV420SemiPlanarToYUV420Planar(
         uint8_t *inyuv, uint8_t* outyuv,
         int32_t width, int32_t height) {
@@ -231,10 +305,16 @@
     mEncParams->level = AVC_LEVEL3_2;
     int32_t profile, level;
     if (meta->findInt32(kKeyVideoProfile, &profile)) {
-        mEncParams->profile = (AVCProfile) profile;
+        if (OK != ConvertOmxAvcProfileToAvcSpecProfile(
+                        profile, &mEncParams->profile)) {
+            return BAD_VALUE;
+        }
     }
     if (meta->findInt32(kKeyVideoLevel, &level)) {
-        mEncParams->level = (AVCLevel) level;
+        if (OK != ConvertOmxAvcLevelToAvcSpecLevel(
+                        level, &mEncParams->level)) {
+            return BAD_VALUE;
+        }
     }
 
 
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index a011137..d5a5313 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -32,6 +32,104 @@
 
 namespace android {
 
+static status_t ConvertOmxProfileLevel(
+        MP4EncodingMode mode,
+        int32_t omxProfile,
+        int32_t omxLevel,
+        ProfileLevelType* pvProfileLevel) {
+    LOGV("ConvertOmxProfileLevel: %d/%d/%d", mode, omxProfile, omxLevel);
+    ProfileLevelType profileLevel;
+    if (mode == H263_MODE) {
+        switch (omxProfile) {
+            case OMX_VIDEO_H263ProfileBaseline:
+                if (omxLevel > OMX_VIDEO_H263Level45) {
+                    LOGE("Unsupported level (%d) for H263", omxLevel);
+                    return BAD_VALUE;
+                } else {
+                    LOGW("PV does not support level configuration for H263");
+                    profileLevel = CORE_PROFILE_LEVEL2;
+                    break;
+                }
+            default:
+                LOGE("Unsupported profile (%d) for H263", omxProfile);
+                return BAD_VALUE;
+        }
+    } else {  // MPEG4
+        switch (omxProfile) {
+            case OMX_VIDEO_MPEG4ProfileSimple:
+                switch (omxLevel) {
+                    case OMX_VIDEO_MPEG4Level0b:
+                        profileLevel = SIMPLE_PROFILE_LEVEL0;
+                        break;
+                    case OMX_VIDEO_MPEG4Level1:
+                        profileLevel = SIMPLE_PROFILE_LEVEL1;
+                        break;
+                    case OMX_VIDEO_MPEG4Level2:
+                        profileLevel = SIMPLE_PROFILE_LEVEL2;
+                        break;
+                    case OMX_VIDEO_MPEG4Level3:
+                        profileLevel = SIMPLE_PROFILE_LEVEL3;
+                        break;
+                    default:
+                        LOGE("Unsupported level (%d) for MPEG4 simple profile",
+                            omxLevel);
+                        return BAD_VALUE;
+            }
+            case OMX_VIDEO_MPEG4ProfileSimpleScalable:
+                switch (omxLevel) {
+                    case OMX_VIDEO_MPEG4Level0b:
+                        profileLevel = SIMPLE_SCALABLE_PROFILE_LEVEL0;
+                        break;
+                    case OMX_VIDEO_MPEG4Level1:
+                        profileLevel = SIMPLE_SCALABLE_PROFILE_LEVEL1;
+                        break;
+                    case OMX_VIDEO_MPEG4Level2:
+                        profileLevel = SIMPLE_SCALABLE_PROFILE_LEVEL2;
+                        break;
+                    default:
+                        LOGE("Unsupported level (%d) for MPEG4 simple "
+                             "scalable profile", omxLevel);
+                        return BAD_VALUE;
+                }
+            case OMX_VIDEO_MPEG4ProfileCore:
+                switch (omxLevel) {
+                    case OMX_VIDEO_MPEG4Level1:
+                        profileLevel = CORE_PROFILE_LEVEL1;
+                        break;
+                    case OMX_VIDEO_MPEG4Level2:
+                        profileLevel = CORE_PROFILE_LEVEL2;
+                        break;
+                    default:
+                        LOGE("Unsupported level (%d) for MPEG4 core "
+                             "profile", omxLevel);
+                        return BAD_VALUE;
+                }
+            case OMX_VIDEO_MPEG4ProfileCoreScalable:
+                switch (omxLevel) {
+                    case OMX_VIDEO_MPEG4Level1:
+                        profileLevel = CORE_SCALABLE_PROFILE_LEVEL1;
+                        break;
+                    case OMX_VIDEO_MPEG4Level2:
+                        profileLevel = CORE_SCALABLE_PROFILE_LEVEL2;
+                        break;
+                    case OMX_VIDEO_MPEG4Level3:
+                        profileLevel = CORE_SCALABLE_PROFILE_LEVEL3;
+                        break;
+                    default:
+                        LOGE("Unsupported level (%d) for MPEG4 core "
+                             "scalable profile", omxLevel);
+                        return BAD_VALUE;
+                }
+            default:
+                LOGE("Unsupported MPEG4 profile (%d)", omxProfile);
+                return BAD_VALUE;
+        }
+    }
+
+    *pvProfileLevel = profileLevel;
+    return OK;
+}
+
 inline static void ConvertYUV420SemiPlanarToYUV420Planar(
         uint8_t *inyuv, uint8_t* outyuv,
         int32_t width, int32_t height) {
@@ -150,9 +248,14 @@
     // If profile and level setting is not correct, failure
     // is reported when the encoder is initialized.
     mEncParams->profile_level = CORE_PROFILE_LEVEL2;
-    int32_t profileLevel;
-    if (meta->findInt32(kKeyVideoLevel, &profileLevel)) {
-        mEncParams->profile_level = (ProfileLevelType)profileLevel;
+    int32_t profile, level;
+    if (meta->findInt32(kKeyVideoProfile, &profile) &&
+        meta->findInt32(kKeyVideoLevel, &level)) {
+        if (OK != ConvertOmxProfileLevel(
+                        mEncParams->encMode, profile, level,
+                        &mEncParams->profile_level)) {
+            return BAD_VALUE;
+        }
     }
 
     mEncParams->packetSize = 32;
diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk
index 0dcbd73..ef2dba0 100644
--- a/media/libstagefright/colorconversion/Android.mk
+++ b/media/libstagefright/colorconversion/Android.mk
@@ -6,7 +6,8 @@
         SoftwareRenderer.cpp
 
 LOCAL_C_INCLUDES := \
-        $(TOP)/frameworks/base/include/media/stagefright/openmax
+        $(TOP)/frameworks/base/include/media/stagefright/openmax \
+        $(TOP)/hardware/msm7k
 
 LOCAL_SHARED_LIBRARIES :=       \
         libbinder               \
@@ -17,6 +18,11 @@
         libsurfaceflinger_client\
         libcamera_client
 
+# ifeq ($(TARGET_BOARD_PLATFORM),msm7k)
+ifeq ($(TARGET_PRODUCT),passion)
+	LOCAL_CFLAGS += -DHAS_YCBCR420_SP_ADRENO
+endif
+
 LOCAL_MODULE:= libstagefright_color_conversion
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index a6dbf69..662a84a 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -22,65 +22,182 @@
 #include <binder/MemoryHeapBase.h>
 #include <binder/MemoryHeapPmem.h>
 #include <media/stagefright/MediaDebug.h>
-#include <surfaceflinger/ISurface.h>
+#include <surfaceflinger/Surface.h>
+#include <ui/android_native_buffer.h>
+#include <ui/GraphicBufferMapper.h>
+
+// XXX: Temporary hack to allow referencing the _ADRENO pixel format here.
+#include <libgralloc-qsd8k/gralloc_priv.h>
 
 namespace android {
 
 SoftwareRenderer::SoftwareRenderer(
         OMX_COLOR_FORMATTYPE colorFormat,
-        const sp<ISurface> &surface,
+        const sp<Surface> &surface,
         size_t displayWidth, size_t displayHeight,
         size_t decodedWidth, size_t decodedHeight)
     : mColorFormat(colorFormat),
-      mConverter(colorFormat, OMX_COLOR_Format16bitRGB565),
-      mISurface(surface),
+      mConverter(NULL),
+      mYUVMode(None),
+      mSurface(surface),
       mDisplayWidth(displayWidth),
       mDisplayHeight(displayHeight),
       mDecodedWidth(decodedWidth),
-      mDecodedHeight(decodedHeight),
-      mFrameSize(mDecodedWidth * mDecodedHeight * 2),  // RGB565
-      mIndex(0) {
-    mMemoryHeap = new MemoryHeapBase("/dev/pmem_adsp", 2 * mFrameSize);
-    if (mMemoryHeap->heapID() < 0) {
-        LOGI("Creating physical memory heap failed, reverting to regular heap.");
-        mMemoryHeap = new MemoryHeapBase(2 * mFrameSize);
-    } else {
-        sp<MemoryHeapPmem> pmemHeap = new MemoryHeapPmem(mMemoryHeap);
-        pmemHeap->slap();
-        mMemoryHeap = pmemHeap;
+      mDecodedHeight(decodedHeight) {
+    LOGI("input format = %d", mColorFormat);
+    LOGI("display = %d x %d, decoded = %d x %d",
+            mDisplayWidth, mDisplayHeight, mDecodedWidth, mDecodedHeight);
+
+    mDecodedWidth = mDisplayWidth;
+    mDecodedHeight = mDisplayHeight;
+
+    int halFormat;
+    switch (mColorFormat) {
+#if HAS_YCBCR420_SP_ADRENO
+        case OMX_COLOR_FormatYUV420Planar:
+        {
+            halFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO;
+            mYUVMode = YUV420ToYUV420sp;
+            break;
+        }
+
+        case 0x7fa30c00:
+        {
+            halFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO;
+            mYUVMode = YUV420spToYUV420sp;
+            break;
+        }
+#endif
+
+        default:
+            halFormat = HAL_PIXEL_FORMAT_RGB_565;
+
+            mConverter = new ColorConverter(
+                    mColorFormat, OMX_COLOR_Format16bitRGB565);
+            CHECK(mConverter->isValid());
+            break;
     }
 
-    CHECK(mISurface.get() != NULL);
+    CHECK(mSurface.get() != NULL);
     CHECK(mDecodedWidth > 0);
     CHECK(mDecodedHeight > 0);
-    CHECK(mMemoryHeap->heapID() >= 0);
-    CHECK(mConverter.isValid());
+    CHECK(mConverter == NULL || mConverter->isValid());
 
-    ISurface::BufferHeap bufferHeap(
-            mDisplayWidth, mDisplayHeight,
-            mDecodedWidth, mDecodedHeight,
-            PIXEL_FORMAT_RGB_565,
-            mMemoryHeap);
+    CHECK_EQ(0,
+            native_window_set_usage(
+            mSurface.get(),
+            GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN
+            | GRALLOC_USAGE_HW_TEXTURE));
 
-    status_t err = mISurface->registerBuffers(bufferHeap);
-    CHECK_EQ(err, OK);
+    CHECK_EQ(0, native_window_set_buffer_count(mSurface.get(), 2));
+
+    // Width must be multiple of 32???
+    CHECK_EQ(0, native_window_set_buffers_geometry(
+                mSurface.get(), mDecodedWidth, mDecodedHeight,
+                halFormat));
 }
 
 SoftwareRenderer::~SoftwareRenderer() {
-    mISurface->unregisterBuffers();
+    delete mConverter;
+    mConverter = NULL;
+}
+
+static inline size_t ALIGN(size_t x, size_t alignment) {
+    return (x + alignment - 1) & ~(alignment - 1);
 }
 
 void SoftwareRenderer::render(
         const void *data, size_t size, void *platformPrivate) {
-    size_t offset = mIndex * mFrameSize;
-    void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
+    android_native_buffer_t *buf;
+    int err;
+    if ((err = mSurface->dequeueBuffer(mSurface.get(), &buf)) != 0) {
+        LOGW("Surface::dequeueBuffer returned error %d", err);
+        return;
+    }
 
-    mConverter.convert(
-            mDecodedWidth, mDecodedHeight,
-            data, 0, dst, 2 * mDecodedWidth);
+    CHECK_EQ(0, mSurface->lockBuffer(mSurface.get(), buf));
 
-    mISurface->postBuffer(offset);
-    mIndex = 1 - mIndex;
+    GraphicBufferMapper &mapper = GraphicBufferMapper::get();
+
+    Rect bounds(mDecodedWidth, mDecodedHeight);
+
+    void *dst;
+    CHECK_EQ(0, mapper.lock(
+                buf->handle, GRALLOC_USAGE_SW_WRITE_OFTEN, bounds, &dst));
+
+    if (mConverter) {
+        mConverter->convert(
+                mDecodedWidth, mDecodedHeight,
+                data, 0, dst, buf->stride * 2);
+    } else if (mYUVMode == YUV420spToYUV420sp) {
+        // Input and output are both YUV420sp, but the alignment requirements
+        // are different.
+        size_t srcYStride = mDecodedWidth;
+        const uint8_t *srcY = (const uint8_t *)data;
+        uint8_t *dstY = (uint8_t *)dst;
+        for (size_t i = 0; i < mDecodedHeight; ++i) {
+            memcpy(dstY, srcY, mDecodedWidth);
+            srcY += srcYStride;
+            dstY += buf->stride;
+        }
+
+        size_t srcUVStride = (mDecodedWidth + 1) & ~1;
+        size_t dstUVStride = ALIGN(mDecodedWidth / 2, 32) * 2;
+
+        const uint8_t *srcUV = (const uint8_t *)data
+            + mDecodedHeight * mDecodedWidth;
+
+        size_t dstUVOffset = ALIGN(ALIGN(mDecodedHeight, 32) * buf->stride, 4096);
+        uint8_t *dstUV = (uint8_t *)dst + dstUVOffset;
+
+        for (size_t i = 0; i < (mDecodedHeight + 1) / 2; ++i) {
+            memcpy(dstUV, srcUV, (mDecodedWidth + 1) & ~1);
+            srcUV += srcUVStride;
+            dstUV += dstUVStride;
+        }
+    } else if (mYUVMode == YUV420ToYUV420sp) {
+        // Input is YUV420 planar, output is YUV420sp, adhere to proper
+        // alignment requirements.
+        size_t srcYStride = mDecodedWidth;
+        const uint8_t *srcY = (const uint8_t *)data;
+        uint8_t *dstY = (uint8_t *)dst;
+        for (size_t i = 0; i < mDecodedHeight; ++i) {
+            memcpy(dstY, srcY, mDecodedWidth);
+            srcY += srcYStride;
+            dstY += buf->stride;
+        }
+
+        size_t srcUVStride = (mDecodedWidth + 1) / 2;
+        size_t dstUVStride = ALIGN(mDecodedWidth / 2, 32) * 2;
+
+        const uint8_t *srcU = (const uint8_t *)data
+            + mDecodedHeight * mDecodedWidth;
+
+        const uint8_t *srcV =
+            srcU + ((mDecodedWidth + 1) / 2) * ((mDecodedHeight + 1) / 2);
+
+        size_t dstUVOffset = ALIGN(ALIGN(mDecodedHeight, 32) * buf->stride, 4096);
+        uint8_t *dstUV = (uint8_t *)dst + dstUVOffset;
+
+        for (size_t i = 0; i < (mDecodedHeight + 1) / 2; ++i) {
+            for (size_t j = 0; j < (mDecodedWidth + 1) / 2; ++j) {
+                dstUV[2 * j + 1] = srcU[j];
+                dstUV[2 * j] = srcV[j];
+            }
+            srcU += srcUVStride;
+            srcV += srcUVStride;
+            dstUV += dstUVStride;
+        }
+    } else {
+        memcpy(dst, data, size);
+    }
+
+    CHECK_EQ(0, mapper.unlock(buf->handle));
+
+    if ((err = mSurface->queueBuffer(mSurface.get(), buf)) != 0) {
+        LOGW("Surface::queueBuffer returned error %d", err);
+    }
+    buf = NULL;
 }
 
 }  // namespace android
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 4526bf1..a0a7436 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -80,6 +80,7 @@
     bool isPlaying() const;
 
     void setISurface(const sp<ISurface> &isurface);
+    void setSurface(const sp<Surface> &surface);
     void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
     status_t setLooping(bool shouldLoop);
 
@@ -114,6 +115,11 @@
         AUDIO_AT_EOS        = 256,
         VIDEO_AT_EOS        = 512,
         AUTO_LOOPING        = 1024,
+
+        // We are basically done preparing but are currently buffering
+        // sufficient data to begin playback and finish the preparation phase
+        // for good.
+        PREPARING_CONNECTED = 2048,
     };
 
     mutable Mutex mLock;
@@ -125,6 +131,7 @@
     wp<MediaPlayerBase> mListener;
 
     sp<ISurface> mISurface;
+    sp<Surface> mSurface;
     sp<MediaPlayerBase::AudioSink> mAudioSink;
 
     SystemTimeSource mSystemTimeSource;
@@ -233,6 +240,7 @@
     status_t seekTo_l(int64_t timeUs);
     status_t pause_l(bool at_eos = false);
     void initRenderer_l();
+    void notifyVideoSize_l();
     void seekAudioIfNecessary_l();
 
     void cancelPlayerEvents(bool keepBufferingGoing = false);
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index 1fb2088..4067585 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -37,6 +37,8 @@
     virtual status_t getSize(off_t *size);
     virtual uint32_t flags();
 
+    virtual DecryptHandle* DrmInitialization(DrmManagerClient *client);
+    virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client);
     ////////////////////////////////////////////////////////////////////////////
 
     size_t cachedSize();
diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h
index 8593a91..93b7a76 100644
--- a/media/libstagefright/include/NuHTTPDataSource.h
+++ b/media/libstagefright/include/NuHTTPDataSource.h
@@ -26,6 +26,9 @@
     virtual status_t getSize(off_t *size);
     virtual uint32_t flags();
 
+    virtual DecryptHandle* DrmInitialization(DrmManagerClient *client);
+    virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client);
+
 protected:
     virtual ~NuHTTPDataSource();
 
@@ -44,12 +47,16 @@
     unsigned mPort;
     String8 mPath;
     String8 mHeaders;
+    String8 mUri;
 
     HTTPStream mHTTP;
     off_t mOffset;
     off_t mContentLength;
     bool mContentLengthValid;
 
+    DecryptHandle *mDecryptHandle;
+    DrmManagerClient *mDrmManagerClient;
+
     status_t connect(
             const char *uri, const String8 &headers, off_t offset);
 
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index c99da59..5a6c96f9 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -59,10 +59,20 @@
             node_id node, OMX_INDEXTYPE index,
             const void *params, size_t size);
 
+    virtual status_t enableGraphicBuffers(
+            node_id node, OMX_U32 port_index, OMX_BOOL enable);
+
+    virtual status_t storeMetaDataInBuffers(
+            node_id node, OMX_U32 port_index, OMX_BOOL enable);
+
     virtual status_t useBuffer(
             node_id node, OMX_U32 port_index, const sp<IMemory> &params,
             buffer_id *buffer);
 
+    virtual status_t useGraphicBuffer(
+            node_id node, OMX_U32 port_index,
+            const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer);
+
     virtual status_t allocateBuffer(
             node_id node, OMX_U32 port_index, size_t size,
             buffer_id *buffer, void **buffer_data);
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index b5b31ac..86c102c 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -49,10 +49,17 @@
     status_t getConfig(OMX_INDEXTYPE index, void *params, size_t size);
     status_t setConfig(OMX_INDEXTYPE index, const void *params, size_t size);
 
+    status_t enableGraphicBuffers(OMX_U32 portIndex, OMX_BOOL enable);
+    status_t storeMetaDataInBuffers(OMX_U32 portIndex, OMX_BOOL enable);
+
     status_t useBuffer(
             OMX_U32 portIndex, const sp<IMemory> &params,
             OMX::buffer_id *buffer);
 
+    status_t useGraphicBuffer(
+            OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
+            OMX::buffer_id *buffer);
+
     status_t allocateBuffer(
             OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
             void **buffer_data);
@@ -125,4 +132,3 @@
 }  // namespace android
 
 #endif  // OMX_NODE_INSTANCE_H_
-
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 9eed089..8d58056 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -24,14 +24,14 @@
 
 namespace android {
 
-class ISurface;
+class Surface;
 class MemoryHeapBase;
 
 class SoftwareRenderer : public VideoRenderer {
 public:
     SoftwareRenderer(
             OMX_COLOR_FORMATTYPE colorFormat,
-            const sp<ISurface> &surface,
+            const sp<Surface> &surface,
             size_t displayWidth, size_t displayHeight,
             size_t decodedWidth, size_t decodedHeight);
 
@@ -41,14 +41,18 @@
             const void *data, size_t size, void *platformPrivate);
 
 private:
+    enum YUVMode {
+        None,
+        YUV420ToYUV420sp,
+        YUV420spToYUV420sp,
+    };
+
     OMX_COLOR_FORMATTYPE mColorFormat;
-    ColorConverter mConverter;
-    sp<ISurface> mISurface;
+    ColorConverter *mConverter;
+    YUVMode mYUVMode;
+    sp<Surface> mSurface;
     size_t mDisplayWidth, mDisplayHeight;
     size_t mDecodedWidth, mDecodedHeight;
-    size_t mFrameSize;
-    sp<MemoryHeapBase> mMemoryHeap;
-    int mIndex;
 
     SoftwareRenderer(const SoftwareRenderer &);
     SoftwareRenderer &operator=(const SoftwareRenderer &);
diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h
new file mode 100644
index 0000000..0da45a8
--- /dev/null
+++ b/media/libstagefright/include/WVMExtractor.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WVM_EXTRACTOR_H_
+
+#define WVM_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class DataSource;
+
+class WVMExtractor : public MediaExtractor {
+public:
+    WVMExtractor(const sp<DataSource> &source);
+
+    virtual size_t countTracks();
+    virtual sp<MediaSource> getTrack(size_t index);
+    virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
+    virtual sp<MetaData> getMetaData();
+
+protected:
+    virtual ~WVMExtractor();
+
+private:
+    sp<DataSource> mDataSource;
+    sp<MediaExtractor> mImpl;
+
+    WVMExtractor(const WVMExtractor &);
+    WVMExtractor &operator=(const WVMExtractor &);
+
+};
+
+}  // namespace android
+
+#endif  // DRM_EXTRACTOR_H_
+
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 7c7d69e..d16476d 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -252,7 +252,7 @@
 }
 
 void BlockIterator::seek(int64_t seekTimeUs) {
-    mCluster = mSegment->GetCluster(seekTimeUs * 1000ll);
+    mCluster = mSegment->FindCluster(seekTimeUs * 1000ll);
     mBlockEntry = mCluster != NULL ? mCluster->GetFirst() : NULL;
 
     while (!eos() && block()->GetTrackNumber() != mTrackNum) {
@@ -476,7 +476,7 @@
 
         size_t codecPrivateSize;
         const unsigned char *codecPrivate =
-            track->GetCodecPrivate(&codecPrivateSize);
+            track->GetCodecPrivate(codecPrivateSize);
 
         enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 };
 
diff --git a/media/libstagefright/matroska/mkvparser.cpp b/media/libstagefright/matroska/mkvparser.cpp
index 4e51004..455b1d6 100644
--- a/media/libstagefright/matroska/mkvparser.cpp
+++ b/media/libstagefright/matroska/mkvparser.cpp
@@ -1,3103 +1,4511 @@
-#include "mkvparser.hpp"
-#include <cassert>
-#include <cstring>
-
-mkvparser::IMkvReader::~IMkvReader()
-{
-}
-
-long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len)
-{
-    assert(pReader);
-    assert(pos >= 0);
-    
-    long long total, available;
-
-    long hr = pReader->Length(&total, &available);
-    assert(hr >= 0);
-    assert(pos < available);
-    assert((available - pos) >= 1);  //assume here max u-int len is 8
-    
-    unsigned char b;
-
-    hr = pReader->Read(pos, 1, &b);
-    if (hr < 0)
-        return hr;
-        
-    assert(hr == 0L);
-    
-    if (b & 0x80)       //1000 0000
-    {
-        len = 1;
-        b &= 0x7F;      //0111 1111
-    }        
-    else if (b & 0x40)  //0100 0000
-    {
-        len = 2;
-        b &= 0x3F;      //0011 1111
-    }
-    else if (b & 0x20)  //0010 0000
-    {
-        len = 3;
-        b &= 0x1F;      //0001 1111
-    }
-    else if (b & 0x10)  //0001 0000
-    {
-        len = 4;
-        b &= 0x0F;      //0000 1111
-    }
-    else if (b & 0x08)  //0000 1000
-    {
-        len = 5;
-        b &= 0x07;      //0000 0111
-    }
-    else if (b & 0x04)  //0000 0100
-    {
-        len = 6;
-        b &= 0x03;      //0000 0011
-    }
-    else if (b & 0x02)  //0000 0010
-    {
-        len = 7;
-        b &= 0x01;      //0000 0001
-    }
-    else 
-    {
-        assert(b & 0x01);  //0000 0001
-        len = 8;
-        b = 0;             //0000 0000
-    }
-    
-    assert((available - pos) >= len);
-    
-    long long result = b;
-    ++pos;
-    for (long i = 1; i < len; ++i)
-    {
-        hr = pReader->Read(pos, 1, &b);
-        
-        if (hr < 0)
-            return hr;
-            
-        assert(hr == 0L);
-        
-        result <<= 8;
-        result |= b;
-        
-        ++pos;
-    }
-    
-    return result;
-}
-    
-    
-long long mkvparser::GetUIntLength(
-    IMkvReader* pReader,
-    long long pos, 
-    long& len)
-{
-    assert(pReader);
-    assert(pos >= 0);
-    
-    long long total, available;
-
-    long hr = pReader->Length(&total, &available);
-    assert(hr >= 0);
-    assert(available <= total);
-    
-    if (pos >= available)
-        return pos;  //too few bytes available
-    
-    unsigned char b;
-    
-    hr = pReader->Read(pos, 1, &b);
-    
-    if (hr < 0)
-        return hr;
-
-    assert(hr == 0L);
-    
-    if (b == 0)  //we can't handle u-int values larger than 8 bytes
-        return E_FILE_FORMAT_INVALID;
-    
-    unsigned char m = 0x80;
-    len = 1;
-    
-    while (!(b & m))
-    {
-        m >>= 1;
-        ++len;
-    }
-    
-    return 0;  //success
-}
-
-
-long long mkvparser::SyncReadUInt(
-    IMkvReader* pReader,
-    long long pos, 
-    long long stop,
-    long& len)
-{
-    assert(pReader);
-
-    if (pos >= stop)
-        return E_FILE_FORMAT_INVALID;
-    
-    unsigned char b;
-    
-    long hr = pReader->Read(pos, 1, &b);
-    
-    if (hr < 0)
-        return hr;
-        
-    if (hr != 0L)
-        return E_BUFFER_NOT_FULL;
-
-    if (b == 0)  //we can't handle u-int values larger than 8 bytes
-        return E_FILE_FORMAT_INVALID;
-    
-    unsigned char m = 0x80;
-    len = 1;
-        
-    while (!(b & m))
-    {
-        m >>= 1;
-        ++len;
-    }
-    
-    if ((pos + len) > stop)
-        return E_FILE_FORMAT_INVALID;
-        
-    long long result = b & (~m);
-    ++pos;
-    
-    for (int i = 1; i < len; ++i)
-    {
-        hr = pReader->Read(pos, 1, &b);
-        
-        if (hr < 0)
-            return hr;
-           
-        if (hr != 0L)
-            return E_BUFFER_NOT_FULL;
-            
-        result <<= 8;
-        result |= b;
-        
-        ++pos;
-    }
-    
-    return result;
-}
-
-
-long long mkvparser::UnserializeUInt(
-    IMkvReader* pReader, 
-    long long pos,
-    long long size)
-{
-    assert(pReader);
-    assert(pos >= 0);
-    assert(size > 0);
-    assert(size <= 8);
-    
-    long long result = 0;
-    
-    for (long long i = 0; i < size; ++i)
-    {
-        unsigned char b;
-        
-        const long hr = pReader->Read(pos, 1, &b);
-        
-        if (hr < 0)      
-            return hr;
-        result <<= 8;
-        result |= b;
-        
-        ++pos;
-    }
-    
-    return result;
-}
-
-
-float mkvparser::Unserialize4Float(
-    IMkvReader* pReader, 
-    long long pos)
-{
-    assert(pReader);
-    assert(pos >= 0);
-    
-    long long total, available;
-    
-    long hr = pReader->Length(&total, &available);
-    assert(hr >= 0);
-    assert(available <= total);
-    assert((pos + 4) <= available);
-    
-    float result;
-    
-    unsigned char* const p = (unsigned char*)&result;
-    unsigned char* q = p + 4;
-    
-    for (;;)
-    {
-        hr = pReader->Read(pos, 1, --q);
-        assert(hr == 0L);
-        
-        if (q == p)
-            break;
-            
-        ++pos;
-    }
-    
-    return result;
-}
-
-
-double mkvparser::Unserialize8Double(
-    IMkvReader* pReader, 
-    long long pos)
-{
-    assert(pReader);
-    assert(pos >= 0);
-    
-    double result;
-    
-    unsigned char* const p = (unsigned char*)&result;
-    unsigned char* q = p + 8;
-    
-    for (;;)
-    {
-        const long hr = pReader->Read(pos, 1, --q);
-        assert(hr == 0L);
-        
-        if (q == p)
-            break;
-            
-        ++pos;
-    }
-    
-    return result;
-}
-
-signed char mkvparser::Unserialize1SInt(
-    IMkvReader* pReader,
-    long long pos)
-{
-    assert(pReader);
-    assert(pos >= 0);
- 
-    long long total, available;
-
-    long hr = pReader->Length(&total, &available);
-    assert(hr == 0);   
-    assert(available <= total);
-    assert(pos < available);
-
-    signed char result;
-
-    hr = pReader->Read(pos, 1, (unsigned char*)&result);
-    assert(hr == 0);
-
-    return result;
-}
-
-short mkvparser::Unserialize2SInt(
-    IMkvReader* pReader, 
-    long long pos)
-{
-    assert(pReader);
-    assert(pos >= 0);
-    
-    long long total, available;
-    
-    long hr = pReader->Length(&total, &available);
-    assert(hr >= 0);
-    assert(available <= total);
-    assert((pos + 2) <= available);
-    
-    short result;
-    
-    unsigned char* const p = (unsigned char*)&result;
-    unsigned char* q = p + 2;
-    
-    for (;;)
-    {
-        hr = pReader->Read(pos, 1, --q);
-        assert(hr == 0L);
-        
-        if (q == p)
-            break;
-            
-        ++pos;
-    }
-    
-    return result;
-}
-
-
-bool mkvparser::Match(
-    IMkvReader* pReader,
-    long long& pos,
-    unsigned long id_,
-    long long& val)
-
-{
-    assert(pReader);
-    assert(pos >= 0);
-    
-    long long total, available;
-
-    long hr = pReader->Length(&total, &available);
-    assert(hr >= 0);
-    assert(available <= total);
-    
-    long len;
-
-    const long long id = ReadUInt(pReader, pos, len);
-    assert(id >= 0);
-    assert(len > 0);
-    assert(len <= 8);
-    assert((pos + len) <= available);
-    
-    if ((unsigned long)id != id_)
-        return false;
-        
-    pos += len;  //consume id
-    
-    const long long size = ReadUInt(pReader, pos, len);
-    assert(size >= 0);
-    assert(size <= 8);
-    assert(len > 0);
-    assert(len <= 8);
-    assert((pos + len) <= available);
-    
-    pos += len;  //consume length of size of payload
-    
-    val = UnserializeUInt(pReader, pos, size);
-    assert(val >= 0);
-    
-    pos += size;  //consume size of payload
-    
-    return true;
-}
-
-bool mkvparser::Match(
-    IMkvReader* pReader,
-    long long& pos,
-    unsigned long id_,
-    char*& val)
-{
-    assert(pReader);
-    assert(pos >= 0);
-    
-    long long total, available;
-
-    long hr = pReader->Length(&total, &available);
-    assert(hr >= 0);
-    assert(available <= total);
-    
-    long len;
-
-    const long long id = ReadUInt(pReader, pos, len);
-    assert(id >= 0);
-    assert(len > 0);
-    assert(len <= 8);
-    assert((pos + len) <= available);
-    
-    if ((unsigned long)id != id_)
-        return false;
-    
-    pos += len;  //consume id
-    
-    const long long size_ = ReadUInt(pReader, pos, len);
-    assert(size_ >= 0);
-    assert(len > 0);
-    assert(len <= 8);
-    assert((pos + len) <= available);
-    
-    pos += len;  //consume length of size of payload
-    assert((pos + size_) <= available);
-
-    const size_t size = static_cast<size_t>(size_);    
-    val = new char[size+1];
-
-    for (size_t i = 0; i < size; ++i)
-    {
-        char c;
-
-        hr = pReader->Read(pos + i, 1, (unsigned char*)&c);
-        assert(hr == 0L);
-            
-        val[i] = c;
-   
-        if (c == '\0')
-            break;     
-   
-    }
-
-    val[size] = '\0';
-    pos += size_;  //consume size of payload
-    
-    return true;
-}
-
-#if 0
-bool mkvparser::Match(
-    IMkvReader* pReader,
-    long long& pos,
-    unsigned long id,
-    wchar_t*& val)
-{
-    char* str;
-    
-    if (!Match(pReader, pos, id, str))
-        return false;
-
-    const size_t size = mbstowcs(NULL, str, 0);
-       
-    if (size == 0) 
-        val = NULL;
-    else 
-    { 
-        val = new wchar_t[size+1];
-        mbstowcs(val, str, size);
-        val[size] = L'\0';
-    }
-
-    delete[] str;
-    return true;    
-}
-#endif
-
-
-bool mkvparser::Match(
-    IMkvReader* pReader,
-    long long& pos,
-    unsigned long id_,
-    unsigned char*& val,
-    size_t *optionalSize)
-{
-    assert(pReader);
-    assert(pos >= 0);
-    
-    long long total, available;
-
-    long hr = pReader->Length(&total, &available);
-    assert(hr >= 0);
-    assert(available <= total);
-    
-    long len;
-    const long long id = ReadUInt(pReader, pos, len);
-    assert(id >= 0);
-    assert(len > 0);
-    assert(len <= 8);
-    assert((pos + len) <= available);
-    
-    if ((unsigned long)id != id_)
-        return false;
-        
-    pos += len;  //consume id
-    
-    const long long size_ = ReadUInt(pReader, pos, len);
-    assert(size_ >= 0);
-    assert(len > 0);
-    assert(len <= 8);
-    assert((pos + len) <= available);
-    
-    pos += len;  //consume length of size of payload
-    assert((pos + size_) <= available);
-
-    const size_t size = static_cast<size_t>(size_);    
-    val = new unsigned char[size];
- 
-    if (optionalSize) {
-        *optionalSize = size;
-    }
-
-    for (size_t i = 0; i < size; ++i)
-    {
-        unsigned char b;
-
-        hr = pReader->Read(pos + i, 1, &b);
-        assert(hr == 0L);
-
-        val[i] = b; 
-    }
-    
-    pos += size_;  //consume size of payload    
-    return true;
-}
-
-
-bool mkvparser::Match(
-    IMkvReader* pReader,
-    long long& pos,
-    unsigned long id_,
-    double& val)
-{
-    assert(pReader);
-    assert(pos >= 0);
-    
-    long long total, available;
-
-    long hr = pReader->Length(&total, &available);
-    assert(hr >= 0);
-    assert(available <= total);
-    long idlen;
-    const long long id = ReadUInt(pReader, pos, idlen);
-    assert(id >= 0);  //TODO
-    
-    if ((unsigned long)id != id_)
-        return false;
-
-    long sizelen;
-    const long long size = ReadUInt(pReader, pos + idlen, sizelen);
-
-    switch (size)
-    {	
-        case 4:
-        case 8:
-            break;
-        default:
-            return false;
-    }
-
-    pos += idlen + sizelen;  //consume id and size fields
-    assert((pos + size) <= available);
-
-    if (size == 4)
-        val = Unserialize4Float(pReader, pos);
-    else
-    {
-        assert(size == 8);
-        val = Unserialize8Double(pReader, pos);
-    }
-    
-    pos += size;  //consume size of payload
-    
-    return true;
-}
-
-
-bool mkvparser::Match(
-    IMkvReader* pReader,
-    long long& pos,
-    unsigned long id_,
-    short& val)
-{
-    assert(pReader);
-    assert(pos >= 0);
-    
-    long long total, available;
-
-    long hr = pReader->Length(&total, &available);
-    assert(hr >= 0);
-    assert(available <= total);
-    
-    long len;
-    const long long id = ReadUInt(pReader, pos, len);
-    assert(id >= 0);
-    assert((pos + len) <= available);
-    
-    if ((unsigned long)id != id_)
-        return false;
-        
-    pos += len;  //consume id
-    
-    const long long size = ReadUInt(pReader, pos, len);
-    assert(size <= 2);
-    assert((pos + len) <= available);
-   
-    pos += len;  //consume length of size of payload
-    assert((pos + size) <= available);
-
-    //TODO:
-    // Generalize this to work for any size signed int
-    if (size == 1)
-        val = Unserialize1SInt(pReader, pos);
-    else 
-        val = Unserialize2SInt(pReader, pos);
-        
-    pos += size;  //consume size of payload
-    
-    return true;
-}
-
-
-namespace mkvparser
-{
-
-EBMLHeader::EBMLHeader():
-    m_docType(NULL)
-{
-}
-
-EBMLHeader::~EBMLHeader()
-{
-    delete[] m_docType;
-}
-
-long long EBMLHeader::Parse(
-    IMkvReader* pReader,
-    long long& pos)
-{
-    assert(pReader);
-    
-    long long total, available;
-    
-    long hr = pReader->Length(&total, &available);
-    
-    if (hr < 0) 
-        return hr;
-    
-    pos = 0;    
-    long long end = (1024 < available)? 1024: available;    
-
-    for (;;)
-    {    
-        unsigned char b = 0;
-    
-        while (pos < end)
-        {
-            hr = pReader->Read(pos, 1, &b);
-           
-            if (hr < 0)
-                return hr;
-            
-            if (b == 0x1A)
-                break;
-                
-            ++pos;
-        }
-    
-        if (b != 0x1A)
-        {
-            if ((pos >= 1024) ||
-                (available >= total) || 
-                ((total - available) < 5))
-                  return -1;
-                
-            return available + 5;  //5 = 4-byte ID + 1st byte of size
-        }
-    
-        if ((total - pos) < 5)
-            return E_FILE_FORMAT_INVALID;
-            
-        if ((available - pos) < 5)
-            return pos + 5;  //try again later
-
-        long len;            
-
-        const long long result = ReadUInt(pReader, pos, len);
-        
-        if (result < 0)  //error
-            return result;
-            
-        if (result == 0x0A45DFA3)  //ReadId masks-off length indicator bits
-        {
-            assert(len == 4);
-            pos += len;
-            break;
-        }
-
-        ++pos;  //throw away just the 0x1A byte, and try again
-    }
-        
-    long len;
-    long long result = GetUIntLength(pReader, pos, len);
-    
-    if (result < 0)  //error
-        return result;
-        
-    if (result > 0)  //need more data
-        return result;
-        
-    assert(len > 0);
-    assert(len <= 8);
-    
-    if ((total -  pos) < len)
-        return E_FILE_FORMAT_INVALID;
-    if ((available - pos) < len)
-        return pos + len;  //try again later
-        
-    result = ReadUInt(pReader, pos, len);
-    
-    if (result < 0)  //error
-        return result;
-        
-    pos += len;  //consume u-int
-    
-    if ((total - pos) < result)
-        return E_FILE_FORMAT_INVALID;
-
-    if ((available - pos) < result)
-        return pos + result;
-        
-    end = pos + result;
-    
-    m_version = 1;
-    m_readVersion = 1;
-    m_maxIdLength = 4;
-    m_maxSizeLength = 8;
-    m_docTypeVersion = 1;
-    m_docTypeReadVersion = 1;
-
-    while (pos < end)
-    {
-        if (Match(pReader, pos, 0x0286, m_version))   
-            ;
-        else if (Match(pReader, pos, 0x02F7, m_readVersion))        
-            ;
-        else if (Match(pReader, pos, 0x02F2, m_maxIdLength))        
-            ;
-        else if (Match(pReader, pos, 0x02F3, m_maxSizeLength))      
-            ;
-        else if (Match(pReader, pos, 0x0282, m_docType))            
-            ; 
-        else if (Match(pReader, pos, 0x0287, m_docTypeVersion))     
-            ;
-        else if (Match(pReader, pos, 0x0285, m_docTypeReadVersion)) 
-            ;
-        else
-        {
-            result = ReadUInt(pReader, pos, len);
-            assert(result > 0);
-            assert(len > 0);
-            assert(len <= 8);
-        
-            pos += len;
-            assert(pos < end);
-            
-            result = ReadUInt(pReader, pos, len);
-            assert(result >= 0);
-            assert(len > 0);
-            assert(len <= 8);
-            
-            pos += len + result;
-            assert(pos <= end);
-        }
-    }
-    
-    assert(pos == end);
-        
-    return 0;    
-}
-
-
-Segment::Segment(
-    IMkvReader* pReader,
-    long long start,
-    long long size) :
-    m_pReader(pReader),
-    m_start(start),
-    m_size(size),
-    m_pos(start),
-    m_pInfo(NULL),
-    m_pTracks(NULL),
-    m_clusterCount(0)
-    //m_clusterNumber(0)
-{
-}
-
-
-Segment::~Segment()
-{
-    Cluster** i = m_clusters;
-    Cluster** j = m_clusters + m_clusterCount;
-
-    while (i != j)
-    {
-        Cluster* p = *i++;
-        assert(p);		
-        delete p;
-    } 
-    
-    delete[] m_clusters;
-       
-    delete m_pTracks;
-    delete m_pInfo;
-}
-
-
-long long Segment::CreateInstance(
-    IMkvReader* pReader,
-    long long pos,
-    Segment*& pSegment)
-{
-    assert(pReader);
-    assert(pos >= 0);
-    
-    pSegment = NULL;
-    
-    long long total, available;
-    
-    long hr = pReader->Length(&total, &available);
-    assert(hr >= 0);
-    assert(available <= total);
-    
-    //I would assume that in practice this loop would execute
-    //exactly once, but we allow for other elements (e.g. Void)
-    //to immediately follow the EBML header.  This is fine for
-    //the source filter case (since the entire file is available),
-    //but in the splitter case over a network we should probably
-    //just give up early.  We could for example decide only to
-    //execute this loop a maximum of, say, 10 times.
-    
-    while (pos < total)
-    {    
-        //Read ID
-        
-        long len;
-        long long result = GetUIntLength(pReader, pos, len);
-        
-        if (result)  //error, or too few available bytes
-            return result;
-            
-        if ((pos + len) > total)
-            return E_FILE_FORMAT_INVALID;
-            
-        if ((pos + len) > available)
-            return pos + len;
-
-        //TODO: if we liberalize the behavior of ReadUInt, we can
-        //probably eliminate having to use GetUIntLength here.
-        const long long id = ReadUInt(pReader, pos, len);
-        
-        if (id < 0)  //error
-            return id;
-            
-        pos += len;  //consume ID
-        
-        //Read Size
-        
-        result = GetUIntLength(pReader, pos, len);
-        
-        if (result)  //error, or too few available bytes
-            return result;
-            
-        if ((pos + len) > total)
-            return E_FILE_FORMAT_INVALID;
-            
-        if ((pos + len) > available)
-            return pos + len;
-
-        //TODO: if we liberalize the behavior of ReadUInt, we can
-        //probably eliminate having to use GetUIntLength here.
-        const long long size = ReadUInt(pReader, pos, len);
-        
-        if (size < 0)
-            return size;
-            
-        pos += len;  //consume length of size of element
-        
-        //Pos now points to start of payload
-        
-        if ((pos + size) > total)
-            return E_FILE_FORMAT_INVALID;
-        
-        if (id == 0x08538067)  //Segment ID
-        {
-            pSegment = new  Segment(pReader, pos, size); 
-            assert(pSegment);  //TODO   
-
-            return 0;    //success
-        }
-        
-        pos += size;  //consume payload
-    }
-    
-    assert(pos == total);
-    
-    pSegment = new Segment(pReader, pos, 0); 
-    assert(pSegment);  //TODO   
-
-    return 0;  //success (sort of)
-}
-
-
-long long Segment::ParseHeaders()
-{
-    //Outermost (level 0) segment object has been constructed, 
-    //and pos designates start of payload.  We need to find the
-    //inner (level 1) elements.
-    long long total, available;
-    
-    long hr = m_pReader->Length(&total, &available);
-    assert(hr >= 0);
-    assert(available <= total);
-    
-    const long long stop = m_start + m_size;
-    assert(stop <= total);
-    assert(m_pos <= stop);
-    
-    bool bQuit = false;
-    while ((m_pos < stop) && !bQuit)
-    {
-        long long pos = m_pos;
-        
-        long len;
-        long long result = GetUIntLength(m_pReader, pos, len);
-        
-        if (result)  //error, or too few available bytes
-            return result;
-            
-        if ((pos + len) > stop)
-            return E_FILE_FORMAT_INVALID;
-            
-        if ((pos + len) > available)
-            return pos + len;
-            
-        const long long idpos = pos;
-        const long long id = ReadUInt(m_pReader, idpos, len);
-        
-        if (id < 0)  //error
-            return id;
-            
-        pos += len;  //consume ID
-        
-        //Read Size
-        result = GetUIntLength(m_pReader, pos, len);
-        
-        if (result)  //error, or too few available bytes
-            return result;
-            
-        if ((pos + len) > stop)
-            return E_FILE_FORMAT_INVALID;
-            
-        if ((pos + len) > available)
-            return pos + len;
-
-        const long long size = ReadUInt(m_pReader, pos, len);
-        
-        if (size < 0)
-            return size;
-            
-        pos += len;  //consume length of size of element
-        
-        //Pos now points to start of payload
-        
-        if ((pos + size) > stop)
-            return E_FILE_FORMAT_INVALID;
-            
-        //We read EBML elements either in total or nothing at all.
-            
-        if ((pos + size) > available)
-            return pos + size;
-        
-        if (id == 0x0549A966)  //Segment Info ID
-        {
-            assert(m_pInfo == NULL);
-            m_pInfo = new  SegmentInfo(this, pos, size);
-            assert(m_pInfo);  //TODO
-            
-            if (m_pTracks)
-                bQuit = true;
-        }
-        else if (id == 0x0654AE6B)  //Tracks ID
-        {
-            assert(m_pTracks == NULL);
-            m_pTracks = new  Tracks(this, pos, size);
-            assert(m_pTracks);  //TODO
-            
-            if (m_pInfo)
-                bQuit = true;
-        }
-        else if (id == 0x0F43B675)  //Cluster ID
-        {
-#if 0
-            if (m_pInfo == NULL)  //TODO: liberalize
-                ;  
-            else if (m_pTracks == NULL)
-                ;
-            else
-                //ParseCluster(idpos, pos, size);            
-                Cluster::Parse(this, m_clusters, pos, size);
-#endif
-            bQuit = true;
-        }
-        
-        m_pos = pos + size;  //consume payload
-    }
-    
-    assert(m_pos <= stop);
-    
-    return 0;  //success
-}
-
-
-long Segment::ParseCluster(Cluster*& pCluster, long long& pos_) const
-{
-    pCluster = NULL;
-    pos_ = -1;
-    
-    const long long stop = m_start + m_size;
-    assert(m_pos <= stop);
-    
-    long long pos = m_pos;
-    long long off = -1;
-   
- 
-    while (pos < stop)
-    {
-        long len;
-        const long long idpos = pos;
-        
-        const long long id = SyncReadUInt(m_pReader, pos, stop, len);
-        
-        if (id < 0)  //error
-            return static_cast<long>(id);
-            
-        if (id == 0)
-            return E_FILE_FORMAT_INVALID;
-            
-        pos += len;  //consume id        
-        assert(pos < stop);
-
-        const long long size = SyncReadUInt(m_pReader, pos, stop, len);
-        
-        if (size < 0)  //error
-            return static_cast<long>(size);
-            
-        pos += len;  //consume size
-        assert(pos <= stop);
-            
-        if (size == 0)  //weird
-            continue;
-            
-        //pos now points to start of payload
-            
-        pos += size;  //consume payload
-        assert(pos <= stop);
-
-        if (off >= 0)
-        {
-            pos_ = idpos;
-            break;
-        }
-
-        if (id == 0x0F43B675)  //Cluster ID
-            off = idpos - m_start;
-    }
-    
-    Segment* const this_ = const_cast<Segment*>(this);
-    const size_t idx = m_clusterCount;
-    
-    if (pos >= stop)
-    {
-        pos_ = stop;
-        
-#if 0        
-        if (off < 0)
-        {
-            pCluster = Cluster::CreateEndOfStream(this_, idx);
-            return 1L;
-        }
-#else
-        if (off < 0)
-            return 1L;
-#endif
-                
-        //Reading 0 bytes at pos might work too -- it would depend 
-        //on how the reader is implemented.
-        
-        unsigned char b;
-
-        const long hr = m_pReader->Read(pos - 1, 1, &b);
-        
-        if (hr < 0)
-            return hr;
-            
-        if (hr != 0L)
-            return E_BUFFER_NOT_FULL;
-    }
-    
-    assert(off >= 0);
-    assert(pos_ >= m_start);
-    assert(pos_ <= stop);
-
-    pCluster = Cluster::Parse(this_, idx, off);
-    return 0L;
-}
-
-
-bool Segment::AddCluster(Cluster* pCluster, long long pos)
-{
-    assert(pos >= m_start);
-    
-    const long long stop = m_start + m_size;
-    assert(pos <= stop);
-
-    if (pCluster)    
-        m_clusters[pos] = pCluster;
-        
-    m_pos = pos;  //m_pos >= stop is now we know we have all clusters
-    
-    return (pos >= stop);
-}
-
-
-long Segment::Load()
-{
-    //Outermost (level 0) segment object has been constructed, 
-    //and pos designates start of payload.  We need to find the
-    //inner (level 1) elements.
-    const long long stop = m_start + m_size;
-#ifdef _DEBUG
-    {
-        long long total, available;
-        
-        long hr = m_pReader->Length(&total, &available);
-        assert(hr >= 0);
-        assert(available >= total);
-        assert(stop <= total);
-    }
-#endif
-    long long index = m_pos;
-    
-    m_clusterCount = 0;
-
-    while (index < stop)
-    {
-        long len = 0;
-
-        long long result = GetUIntLength(m_pReader, index, len);
-       
-        if (result < 0)  //error
-            return static_cast<long>(result);
-            
-        if ((index + len) > stop)
-            return E_FILE_FORMAT_INVALID;
-            
-        const long long idpos = index;
-        const long long id = ReadUInt(m_pReader, idpos, len);
-        
-        if (id < 0)  //error
-            return static_cast<long>(id);
-            
-        index += len;  //consume ID
-        
-        //Read Size
-        result = GetUIntLength(m_pReader, index, len);
-        
-        if (result < 0)  //error
-            return static_cast<long>(result);
-            
-        if ((index + len) > stop)
-            return E_FILE_FORMAT_INVALID;
-            
-        const long long size = ReadUInt(m_pReader, index, len);
-        
-        if (size < 0)  //error
-            return static_cast<long>(size);
-            
-        index += len;  //consume length of size of element
- 
-        if (id == 0x0F43B675) // Cluster ID 
-            break;
-	
-        if (id == 0x014D9B74) // SeekHead ID 
-        {
-            ParseSeekHead(index, size, NULL); 
-            break;
-        }
-        index += size;
-    }
-        
-    if (m_clusterCount == 0)
-        return -1L;
-
-    while (m_pos < stop)
-    {
-        long long pos = m_pos;
-        
-        long len;
-
-        long long result = GetUIntLength(m_pReader, pos, len);
-        
-        if (result < 0)  //error
-            return static_cast<long>(result);
-            
-        if ((pos + len) > stop)
-            return E_FILE_FORMAT_INVALID;
-            
-        const long long idpos = pos;
-        const long long id = ReadUInt(m_pReader, idpos, len);
-        
-        if (id < 0)  //error
-            return static_cast<long>(id);
-            
-        pos += len;  //consume ID
-        
-        //Read Size
-        result = GetUIntLength(m_pReader, pos, len);
-        
-        if (result < 0)  //error
-            return static_cast<long>(result);
-            
-        if ((pos + len) > stop)
-	        return E_FILE_FORMAT_INVALID;
-            
-        const long long size = ReadUInt(m_pReader, pos, len);
-       
-        if (size < 0)  //error
-            return static_cast<long>(size);
-            
-        pos += len;  //consume length of size of element
-        
-        //Pos now points to start of payload
-        
-        if ((pos + size) > stop)
-            return E_FILE_FORMAT_INVALID;
-            
-        if (id == 0x0F43B675)  //Cluster ID
-            break;
-
-        if (id == 0x014D9B74)  //SeekHead ID
-        {
-            m_clusters = new Cluster*[m_clusterCount];   
-            size_t index = 0;
-            
-            ParseSeekHead(pos, size, &index);            
-            assert(index == m_clusterCount);
-        }            
-        else if (id == 0x0549A966)  //Segment Info ID
-        {
-            assert(m_pInfo == NULL);
-            m_pInfo = new  SegmentInfo(this, pos, size);
-            assert(m_pInfo);  //TODO
-        }
-        else if (id == 0x0654AE6B)  //Tracks ID
-        {
-            assert(m_pTracks == NULL);
-            m_pTracks = new Tracks(this, pos, size);
-            assert(m_pTracks);  //TODO
-        }
-
-        m_pos = pos + size;  //consume payload
-    }
-    
-    assert(m_clusters);
-    
-    //TODO: see notes above.  This check is here (temporarily) to ensure
-    //that the first seekhead has entries for the clusters (because that's
-    //when they're loaded).  In case we are given a file that lists the
-    //clusters in a second seekhead, the worst thing that happens is that
-    //we treat this as an invalid file (which is better then simply
-    //asserting somewhere).  But that's only a work-around.  What we need
-    //to do is be able to handle having multiple seekheads, and having
-    //clusters listed somewhere besides the first seekhead.
-    //    
-    //if (m_clusters == NULL)
-    //    return E_FILE_FORMAT_INVALID;
-        
-    //NOTE: we stop parsing when we reach the first cluster, under the
-    //assumption all clusters are named in some SeekHead.  Clusters
-    //will have been (pre)loaded, so we indicate that we have all clusters
-    //by adjusting the parse position:
-    m_pos = stop;  //means "we have all clusters"
-
-    return 0L;
-}
-
-
-void Segment::ParseSeekHead(long long start, long long size_, size_t* pIndex)
-{
-    long long pos = start;
-    const long long stop = start + size_;
-    while (pos < stop)
-    {
-        long len;
-        
-        const long long id = ReadUInt(m_pReader, pos, len);
-        assert(id >= 0);  //TODO
-        assert((pos + len) <= stop);
-        
-        pos += len;  //consume ID
-        
-        const long long size = ReadUInt(m_pReader, pos, len);
-        assert(size >= 0);
-        assert((pos + len) <= stop);
-        
-        pos += len;  //consume Size field
-        assert((pos + size) <= stop);
-
-        if (id == 0x0DBB)  //SeekEntry ID
-            ParseSeekEntry(pos, size, pIndex);
-        
-        pos += size;  //consume payload
-        assert(pos <= stop);
-    }
-    
-    assert(pos == stop);
-}
-
-
-void Segment::ParseSecondarySeekHead(long long off, size_t* pIndex)
-{
-    assert(off >= 0);
-    assert(off < m_size);
-
-    long long pos = m_start + off;
-    const long long stop = m_start + m_size;
-    
-    long len;
-
-    long long result = GetUIntLength(m_pReader, pos, len);
-    assert(result == 0);
-    assert((pos + len) <= stop);
-    
-    const long long idpos = pos;
-
-    const long long id = ReadUInt(m_pReader, idpos, len);
-    assert(id == 0x014D9B74);  //SeekHead ID
-    
-    pos += len;  //consume ID
-    assert(pos < stop);
-    
-    //Read Size
-    
-    result = GetUIntLength(m_pReader, pos, len);
-    assert(result == 0);
-    assert((pos + len) <= stop);
-    
-    const long long size = ReadUInt(m_pReader, pos, len);
-    assert(size >= 0);
-    
-    pos += len;  //consume length of size of element
-    assert((pos + size) <= stop);
-    
-    //Pos now points to start of payload
-    
-    ParseSeekHead(pos, size, pIndex);
-}
-
-
-void Segment::ParseSeekEntry(long long start, long long size_, size_t* pIndex)
-{
-    long long pos = start;
-
-    const long long stop = start + size_;
-    
-    long len;
-    
-    const long long seekIdId = ReadUInt(m_pReader, pos, len);
-    //seekIdId;
-    assert(seekIdId == 0x13AB);  //SeekID ID
-    assert((pos + len) <= stop);
-    
-    pos += len;  //consume id
-
-    const long long seekIdSize = ReadUInt(m_pReader, pos, len);
-    assert(seekIdSize >= 0);
-    assert((pos + len) <= stop);
-    
-    pos += len;  //consume size
-    
-    const long long seekId = ReadUInt(m_pReader, pos, len);  //payload
-    assert(seekId >= 0);
-    assert(len == seekIdSize);
-    assert((pos + len) <= stop);
-    
-    pos += seekIdSize;  //consume payload
-    
-    const long long seekPosId = ReadUInt(m_pReader, pos, len);
-    //seekPosId;
-    assert(seekPosId == 0x13AC);  //SeekPos ID
-    assert((pos + len) <= stop);
-    
-    pos += len;  //consume id
-    
-    const long long seekPosSize = ReadUInt(m_pReader, pos, len);
-    assert(seekPosSize >= 0);
-    assert((pos + len) <= stop);
-
-    pos += len;  //consume size
-    assert((pos + seekPosSize) <= stop);
-        
-    const long long seekOff = UnserializeUInt(m_pReader, pos, seekPosSize);
-    assert(seekOff >= 0);
-    assert(seekOff < m_size);
-    
-    pos += seekPosSize;  //consume payload
-    assert(pos == stop);
-    
-    const long long seekPos = m_start + seekOff;
-    assert(seekPos < (m_start + m_size));
-   
-    if (seekId == 0x0F43B675)  //cluster id
-    {       
-        if (pIndex == NULL)
-            ++m_clusterCount; 
-        else
-        {
-            assert(m_clusters);
-            assert(m_clusterCount > 0);
-            
-            size_t& index = *pIndex;
-            assert(index < m_clusterCount);
-            
-            Cluster*& pCluster = m_clusters[index];
-            
-            pCluster = Cluster::Parse(this, index, seekOff);
-            assert(pCluster);  //TODO
-            
-            ++index;
-        }
-    }
-    else if (seekId == 0x014D9B74)  //SeekHead ID
-    {
-        ParseSecondarySeekHead(seekOff, pIndex);
-    }
-}
-
-
-long long Segment::Unparsed() const
-{
-    const long long stop = m_start + m_size;
-
-    const long long result = stop - m_pos;
-    assert(result >= 0);
-    
-    return result;
-}
-
-
-#if 0  //NOTE: too inefficient
-long long Segment::Load(long long time_ns)
-{
-    if (Unparsed() <= 0)
-        return 0;
-    
-    while (m_clusters.empty())
-    {
-        const long long result = Parse();
-        
-        if (result)  //error, or not enough bytes available
-            return result;
-            
-        if (Unparsed() <= 0)
-            return 0;
-    }
-    
-    while (m_clusters.back()->GetTime() < time_ns)
-    {
-        const long long result = Parse();
-        
-        if (result)  //error, or not enough bytes available
-            return result;
-            
-        if (Unparsed() <= 0)
-            return 0;
-    }        
-
-    return 0;        
-}
-#endif
-
-
-Cluster* Segment::GetFirst()
-{
-    if ((m_clusters == NULL) || (m_clusterCount <= 0))
-       return &m_eos;
-
-    Cluster* const pCluster = m_clusters[0];
-    assert(pCluster);
-        
-    return pCluster;
-}
-
-
-Cluster* Segment::GetLast()
-{
-    if ((m_clusters == NULL) || (m_clusterCount <= 0))
-        return &m_eos;
-
-    const size_t idx = m_clusterCount - 1;    
-    Cluster* const pCluster = m_clusters[idx];
-    assert(pCluster);
-        
-    return pCluster;
-}
-
-
-unsigned long Segment::GetCount() const
-{
-    //TODO: m_clusterCount should not be long long.
-    return static_cast<unsigned long>(m_clusterCount);
-}
-
-
-Cluster* Segment::GetNext(const Cluster* pCurr)
-{
-    assert(pCurr);
-    assert(pCurr != &m_eos);
-    assert(m_clusters);
-    assert(m_clusterCount > 0);
-
-    size_t idx =  pCurr->m_index;
-    assert(idx < m_clusterCount);
-    assert(pCurr == m_clusters[idx]);
-    
-    idx++;
-    
-    if (idx >= m_clusterCount) 
-        return &m_eos;
-        
-    Cluster* const pNext = m_clusters[idx];
-    assert(pNext);
-    
-    return pNext;
-}
-
-
-Cluster* Segment::GetCluster(long long time_ns)
-{
-    if ((m_clusters == NULL) || (m_clusterCount <= 0))
-        return &m_eos;
-        
-    {
-        Cluster* const pCluster = m_clusters[0];
-        assert(pCluster);
-        assert(pCluster->m_index == 0);
-        
-        if (time_ns <= pCluster->GetTime())
-            return pCluster;
-    }
-    
-    //Binary search of cluster array
-       
-    size_t i = 0;
-    size_t j = m_clusterCount;
-    
-    while (i < j)
-    {
-        //INVARIANT:
-        //[0, i) <= time_ns
-        //[i, j) ?
-        //[j, m_clusterCount)  > time_ns
-        
-        const size_t k = i + (j - i) / 2;
-        assert(k < m_clusterCount);
-
-        Cluster* const pCluster = m_clusters[k];
-        assert(pCluster);
-        assert(pCluster->m_index == k);
-        
-        const long long t = pCluster->GetTime();
-        
-        if (t <= time_ns)
-            i = k + 1;
-        else
-            j = k;
-            
-        assert(i <= j);
-    }
-    
-    assert(i == j);
-    assert(i > 0);
-    assert(i <= m_clusterCount);
-    
-    const size_t k = i - 1;
-    
-    Cluster* const pCluster = m_clusters[k];
-    assert(pCluster);
-    assert(pCluster->m_index == k);
-    assert(pCluster->GetTime() <= time_ns);
-    
-    return pCluster;
-}
-
-
-Tracks* Segment::GetTracks() const
-{
-    return m_pTracks;
-}
-
-
-const SegmentInfo* const Segment::GetInfo() const
-{
-    return m_pInfo;
-}
-
-
-long long Segment::GetDuration() const
-{
-    assert(m_pInfo);
-    return m_pInfo->GetDuration();
-}
-
-
-SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_) :
-    m_pSegment(pSegment),
-    m_start(start),
-    m_size(size_),
-    m_pMuxingAppAsUTF8(NULL),
-    m_pWritingAppAsUTF8(NULL),
-    m_pTitleAsUTF8(NULL)
-{
-    IMkvReader* const pReader = m_pSegment->m_pReader;
-   
-    long long pos = start;
-    const long long stop = start + size_;
-    
-    m_timecodeScale = 1000000;
-    m_duration = 0;
-    
-    
-    while (pos < stop)
-    {
-        if (Match(pReader, pos, 0x0AD7B1, m_timecodeScale))
-            assert(m_timecodeScale > 0);
-
-        else if (Match(pReader, pos, 0x0489, m_duration))
-            assert(m_duration >= 0);
-
-        else if (Match(pReader, pos, 0x0D80, m_pMuxingAppAsUTF8))   //[4D][80] 
-            assert(m_pMuxingAppAsUTF8);
-
-        else if (Match(pReader, pos, 0x1741, m_pWritingAppAsUTF8))  //[57][41]
-            assert(m_pWritingAppAsUTF8);
-            
-        else if (Match(pReader, pos, 0x3BA9, m_pTitleAsUTF8))        //[7B][A9]
-            assert(m_pTitleAsUTF8);
-
-        else
-        {
-            long len;
-            
-            const long long id = ReadUInt(pReader, pos, len);
-            //id;
-            assert(id >= 0);
-            assert((pos + len) <= stop);
-            
-            pos += len;  //consume id
-            assert((stop - pos) > 0);
-            
-            const long long size = ReadUInt(pReader, pos, len);
-            assert(size >= 0);
-            assert((pos + len) <= stop);
-            
-            pos += len + size;  //consume size and payload
-            assert(pos <= stop);
-        }
-    }
-    
-    assert(pos == stop);
-}
-
-SegmentInfo::~SegmentInfo()
-{
-    if (m_pMuxingAppAsUTF8)
-    {
-        delete[] m_pMuxingAppAsUTF8;
-        m_pMuxingAppAsUTF8 = NULL;
-    }
-
-    if (m_pWritingAppAsUTF8)
-    {
-        delete[] m_pWritingAppAsUTF8;
-        m_pWritingAppAsUTF8 = NULL;
-    }
-   
-    if (m_pTitleAsUTF8)
-    {
-        delete[] m_pTitleAsUTF8;
-        m_pTitleAsUTF8 = NULL;
-    }
-}
-
-long long SegmentInfo::GetTimeCodeScale() const
-{
-    return m_timecodeScale;
-}
-
-
-long long SegmentInfo::GetDuration() const
-{
-    assert(m_duration >= 0);    
-    assert(m_timecodeScale >= 1);
-    
-    const double dd = double(m_duration) * double(m_timecodeScale);
-    const long long d = static_cast<long long>(dd);
-    
-    return d;
-}
-
-const char* SegmentInfo::GetMuxingAppAsUTF8() const
-{
-    return m_pMuxingAppAsUTF8;
-}
-
-const char* SegmentInfo::GetWritingAppAsUTF8() const
-{
-    return m_pWritingAppAsUTF8;
-}
-
-const char* SegmentInfo::GetTitleAsUTF8() const
-{
-    return m_pTitleAsUTF8;
-}
-
-Track::Track(Segment* pSegment, const Info& i) :
-    m_pSegment(pSegment),
-    m_info(i)
-{
-}
-
-Track::~Track()
-{
-    Info& info = const_cast<Info&>(m_info);
-    info.Clear();
-}
-
-Track::Info::Info():
-    type(-1),
-    number(-1),
-    uid(-1),
-    nameAsUTF8(NULL),
-    codecId(NULL),
-    codecPrivate(NULL),
-    codecPrivateSize(0),
-    codecNameAsUTF8(NULL)
-{
-}
-
-void Track::Info::Clear() 
-{
-    delete[] nameAsUTF8;
-    nameAsUTF8 = NULL;
-
-    delete[] codecId;
-    codecId = NULL;
-
-    delete[] codecPrivate;
-    codecPrivate = NULL;
-
-    delete[] codecNameAsUTF8;
-    codecNameAsUTF8 = NULL;
-}
-
-const BlockEntry* Track::GetEOS() const
-{
-    return &m_eos;
-}
-
-long long Track::GetType() const
-{
-    const unsigned long result = static_cast<unsigned long>(m_info.type);
-    return result;
-}
-
-unsigned long Track::GetNumber() const
-{
-    assert(m_info.number >= 0);
-    const unsigned long result = static_cast<unsigned long>(m_info.number);
-    return result;
-}
-
-const char* Track::GetNameAsUTF8() const
-{
-    return m_info.nameAsUTF8;
-}
-
-const char* Track::GetCodecNameAsUTF8() const
-{  
-    return m_info.codecNameAsUTF8;
-}
-
-
-const char* Track::GetCodecId() const
-{
-    return m_info.codecId;
-}
-
-
-const unsigned char* Track::GetCodecPrivate(size_t *optionalSize) const
-{
-    if (optionalSize) {
-        *optionalSize = m_info.codecPrivateSize;
-    }
-    return m_info.codecPrivate;
-}
-
-
-long Track::GetFirst(const BlockEntry*& pBlockEntry) const
-{
-    Cluster* const pCluster = m_pSegment->GetFirst();
-    
-    //If Segment::GetFirst returns NULL, then this must be a network 
-    //download, and we haven't loaded any clusters yet.  In this case,
-    //returning NULL from Track::GetFirst means the same thing.
-
-    if ((pCluster == NULL) || pCluster->EOS())
-    {
-        pBlockEntry = NULL;
-        return E_BUFFER_NOT_FULL;  //return 1L instead?
-    }
-        
-    pBlockEntry = pCluster->GetFirst();
-    
-    while (pBlockEntry)
-    {
-        const Block* const pBlock = pBlockEntry->GetBlock();
-        assert(pBlock);
-        
-        if (pBlock->GetTrackNumber() == (unsigned long)m_info.number)
-            return 0L;
-            
-        pBlockEntry = pCluster->GetNext(pBlockEntry);
-    }
-    
-    //NOTE: if we get here, it means that we didn't find a block with
-    //a matching track number.  We interpret that as an error (which
-    //might be too conservative).
-
-    pBlockEntry = GetEOS();  //so we can return a non-NULL value
-    return 1L;
-}
-
-
-long Track::GetNext(const BlockEntry* pCurrEntry, const BlockEntry*& pNextEntry) const
-{
-    assert(pCurrEntry);
-    assert(!pCurrEntry->EOS());  //?
-    assert(pCurrEntry->GetBlock()->GetTrackNumber() == (unsigned long)m_info.number);    
-    
-    const Cluster* const pCurrCluster = pCurrEntry->GetCluster();
-    assert(pCurrCluster);
-    assert(!pCurrCluster->EOS());
-    
-    pNextEntry = pCurrCluster->GetNext(pCurrEntry);
-            
-    while (pNextEntry)
-    {    
-        const Block* const pNextBlock = pNextEntry->GetBlock();
-        assert(pNextBlock);
-    
-        if (pNextBlock->GetTrackNumber() == (unsigned long)m_info.number)
-            return 0L;
-            
-        pNextEntry = pCurrCluster->GetNext(pNextEntry);
-    }
-
-    Segment* pSegment = pCurrCluster->m_pSegment;    
-    Cluster* const pNextCluster = pSegment->GetNext(pCurrCluster);
-    
-    if ((pNextCluster == NULL) || pNextCluster->EOS())
-    {
-        if (pSegment->Unparsed() <= 0)   //all clusters have been loaded
-        {
-            pNextEntry = GetEOS();
-            return 1L;
-        }
-        
-        pNextEntry = NULL;
-        return E_BUFFER_NOT_FULL;
-    }
-        
-    pNextEntry = pNextCluster->GetFirst();
-    
-    while (pNextEntry)
-    {
-        const Block* const pNextBlock = pNextEntry->GetBlock();
-        assert(pNextBlock);
-        
-        if (pNextBlock->GetTrackNumber() == (unsigned long)m_info.number)
-            return 0L;
-            
-        pNextEntry = pNextCluster->GetNext(pNextEntry);
-    }
-    
-    //TODO: what has happened here is that we did not find a block
-    //with a matching track number on the next cluster.  It might
-    //be the case that some cluster beyond the next cluster 
-    //contains a block having a matching track number, but for
-    //now we terminate the search immediately.  We do this so that
-    //we don't end up searching the entire file looking for the
-    //next block.  Another possibility is to try searching for the next
-    //block in a small, fixed number of clusters (intead searching
-    //just the next one), or to terminate the search when when the
-    //there is a large gap in time, or large gap in file position.  It
-    //might very well be the case that the approach we use here is
-    //unnecessarily conservative.
-    
-    //TODO: again, here's a case where we need to return the special
-    //EOS block.  Or something.  It's OK if pNext is NULL, because
-    //we only need it to set the stop time of the media sample.
-    //(The start time is determined from pCurr, which is non-NULL
-    //and non-EOS.)  The problem is when we set pCurr=pNext; when
-    //pCurr has the value NULL we interpret that to mean that we
-    //haven't fully initialized pCurr and we attempt to set it to
-    //point to the first block for this track.  But that's not what
-    //we want at all; we want the next call to PopulateSample to
-    //return end-of-stream, not (re)start from the beginning.
-    //
-    //One work-around is to send EOS immediately.  We would send 
-    //the EOS the next pass anyway, so maybe it's no great loss.  The 
-    //only problem is that if this the stream really does end one
-    //cluster early (relative to other tracks), or the last frame
-    //happens to be a keyframe ("CanSeekToEnd").
-    //
-    //The problem is that we need a way to mark as stream as
-    //"at end of stream" without actually being at end of stream.
-    //We need to give pCurr some value that means "you've reached EOS".
-    //We can't synthesize the special EOS Cluster immediately
-    //(when we first open the file, say), because we use the existance
-    //of that special cluster value to mean that we've read all of 
-    //the clusters (this is a network download, so we can't know apriori
-    //how many we have).
-    //
-    //Or, we could return E_FAIL, and set another bit in the stream
-    //object itself, to indicate that it should send EOS earlier
-    //than when (pCurr=pStop).
-    //
-    //Or, probably the best solution, when we actually load the 
-    //blocks into a cluster: if we notice that there's no block
-    //for a track, we synthesize a nonce EOS block for that track.
-    //That way we always have something to return.  But that will
-    //only work for sequential scan???
-
-    //pNext = NULL;    
-    //return E_FAIL;
-    pNextEntry = GetEOS();
-    return 1L;
-}
-
-
-Track::EOSBlock::EOSBlock()
-{
-}
-
-
-bool Track::EOSBlock::EOS() const
-{
-    return true;
-}
-
-
-Cluster* Track::EOSBlock::GetCluster() const
-{
-    return NULL;
-}
-
-
-size_t Track::EOSBlock::GetIndex() const
-{
-    return 0;
-}
-
-
-const Block* Track::EOSBlock::GetBlock() const
-{
-    return NULL;
-}
-
-
-bool Track::EOSBlock::IsBFrame() const
-{
-    return false;
-}
-
-
-VideoTrack::VideoTrack(Segment* pSegment, const Info& i) :
-    Track(pSegment, i),
-    m_width(-1),
-    m_height(-1),
-    m_rate(-1)
-{
-    assert(i.type == 1);
-    assert(i.number > 0);
-    
-    IMkvReader* const pReader = pSegment->m_pReader;
-    
-    const Settings& s = i.settings;
-    assert(s.start >= 0);
-    assert(s.size >= 0);
-    
-    long long pos = s.start;
-    assert(pos >= 0);
-    
-    const long long stop = pos + s.size;
-    
-    while (pos < stop)
-    {
-#ifdef _DEBUG
-        long len;
-        const long long id = ReadUInt(pReader, pos, len);
-        assert(id >= 0);  //TODO: handle error case
-        assert((pos + len) <= stop);
-#endif
-        if (Match(pReader, pos, 0x30, m_width))         
-            ;
-        else if (Match(pReader, pos, 0x3A, m_height))   
-            ;
-        else if (Match(pReader, pos, 0x0383E3, m_rate)) 
-            ;
-        else
-        {
-            long len;
-            const long long id = ReadUInt(pReader, pos, len);
-            assert(id >= 0);  //TODO: handle error case
-            assert((pos + len) <= stop);
-        
-            pos += len;  //consume id
-            
-            const long long size = ReadUInt(pReader, pos, len);
-            assert(size >= 0);  //TODO: handle error case
-            assert((pos + len) <= stop);
-            
-            pos += len;  //consume length of size
-            assert((pos + size) <= stop);
-            
-            //pos now designates start of payload
-            
-            pos += size;  //consume payload
-            assert(pos <= stop);
-        }
-    }
-    
-    return;
-}
-
-
-bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const
-{
-    assert(pBlockEntry);
-    
-    const Block* const pBlock = pBlockEntry->GetBlock();
-    assert(pBlock);    
-    assert(pBlock->GetTrackNumber() == (unsigned long)m_info.number);
-    
-    return pBlock->IsKey();
-}
-
-
-
-long long VideoTrack::GetWidth() const
-{
-    return m_width;
-}
-
-
-long long VideoTrack::GetHeight() const
-{
-    return m_height;
-}
-
-
-double VideoTrack::GetFrameRate() const
-{
-    return m_rate;
-}
-
-
-AudioTrack::AudioTrack(Segment* pSegment, const Info& i) :
-    Track(pSegment, i)
-{
-    assert(i.type == 2);
-    assert(i.number > 0);
-
-    IMkvReader* const pReader = pSegment->m_pReader;
-    
-    const Settings& s = i.settings;
-    assert(s.start >= 0);
-    assert(s.size >= 0);
-    
-    long long pos = s.start;
-    assert(pos >= 0);
-    
-    const long long stop = pos + s.size;
-    
-    while (pos < stop)
-    {
-#ifdef _DEBUG
-        long len;
-        const long long id = ReadUInt(pReader, pos, len);
-        assert(id >= 0);  //TODO: handle error case
-        assert((pos + len) <= stop);
-#endif
-        if (Match(pReader, pos, 0x35, m_rate))            
-            ;
-        else if (Match(pReader, pos, 0x1F, m_channels))   
-            ;
-        else if (Match(pReader, pos, 0x2264, m_bitDepth))  
-            ;            
-        else
-        {
-            long len;
-            const long long id = ReadUInt(pReader, pos, len);
-            assert(id >= 0);  //TODO: handle error case
-            assert((pos + len) <= stop);
-        
-            pos += len;  //consume id
-            
-            const long long size = ReadUInt(pReader, pos, len);
-            assert(size >= 0);  //TODO: handle error case
-            assert((pos + len) <= stop);
-            
-            pos += len;  //consume length of size
-            assert((pos + size) <= stop);
-            
-            //pos now designates start of payload
-            
-            pos += size;  //consume payload
-            assert(pos <= stop);
-        }
-    }
-
-    return;
-}
-
-bool AudioTrack::VetEntry(const BlockEntry* pBlockEntry) const
-{
-    assert(pBlockEntry);
-    
-    const Block* const pBlock = pBlockEntry->GetBlock();
-    assert(pBlock);
-    assert(pBlock->GetTrackNumber() == (unsigned long)m_info.number);
-
-    return true;
-}
-
-
-double AudioTrack::GetSamplingRate() const
-{
-    return m_rate;
-}
-
-
-long long AudioTrack::GetChannels() const
-{
-    return m_channels;
-}
-
-long long AudioTrack::GetBitDepth() const
-{
-    return m_bitDepth;
-}
-
-Tracks::Tracks(Segment* pSegment, long long start, long long size_) :
-    m_pSegment(pSegment),
-    m_start(start),
-    m_size(size_),
-    m_trackEntries(NULL),
-    m_trackEntriesEnd(NULL)
-{
-    long long stop = m_start + m_size;
-    IMkvReader* const pReader = m_pSegment->m_pReader;
-    
-    long long pos1 = m_start;
-    int count = 0;
-    
-    while (pos1 < stop)
-    {
-        long len;
-        const long long id = ReadUInt(pReader, pos1, len);
-        assert(id >= 0);
-        assert((pos1 + len) <= stop);
-        
-        pos1 += len;  //consume id
-        
-        const long long size = ReadUInt(pReader, pos1, len);
-        assert(size >= 0);
-        assert((pos1 + len) <= stop);
-        
-        pos1 += len;  //consume length of size
-        
-        //pos now desinates start of element
-        if (id == 0x2E)  //TrackEntry ID
-            ++count;
-            
-        pos1 += size;  //consume payload
-        assert(pos1 <= stop);
-    }    
-
-    if (count <= 0)
-        return;
-
-    m_trackEntries = new Track*[count];
-    m_trackEntriesEnd = m_trackEntries;
-
-    long long pos = m_start;
-
-    while (pos < stop)
-    {
-        long len;
-        const long long id = ReadUInt(pReader, pos, len);
-        assert(id >= 0);
-        assert((pos + len) <= stop);
-        
-        pos += len;  //consume id
-        
-        const long long size1 = ReadUInt(pReader, pos, len);
-        assert(size1 >= 0);
-        assert((pos + len) <= stop);
-        
-        pos += len;  //consume length of size
-        
-        //pos now desinates start of element
-        
-        if (id == 0x2E)  //TrackEntry ID
-            ParseTrackEntry(pos, size1, *m_trackEntriesEnd++);
-            
-        pos += size1;  //consume payload
-        assert(pos <= stop);
-    }    
-}
-
-unsigned long Tracks::GetTracksCount() const
-{
-    const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;
-    assert(result >= 0);
-    
-    return static_cast<unsigned long>(result);
-}
-
-
-void Tracks::ParseTrackEntry(
-    long long start,
-    long long size,
-    Track*& pTrack)
-{
-    IMkvReader* const pReader = m_pSegment->m_pReader;
-    
-    long long pos = start;
-    const long long stop = start + size;
-
-    Track::Info i;
-    
-    Track::Settings videoSettings;
-    videoSettings.start = -1;
-    
-    Track::Settings audioSettings;
-    audioSettings.start = -1;
-    
-    while (pos < stop)
-    {
-#ifdef _DEBUG
-        long len;
-        const long long id = ReadUInt(pReader, pos, len);
-        len;
-        id;
-#endif
-        if (Match(pReader, pos, 0x57, i.number))
-            assert(i.number > 0);
-
-        else if (Match(pReader, pos, 0x33C5, i.uid))           
-            ;  
-
-        else if (Match(pReader, pos, 0x03, i.type))            
-            ;  
-
-        else if (Match(pReader, pos, 0x136E, i.nameAsUTF8))          
-            assert(i.nameAsUTF8);  
-
-        else if (Match(pReader, pos, 0x06, i.codecId))         
-            ;  
-
-        else if (Match(pReader, pos, 0x23A2, i.codecPrivate, &i.codecPrivateSize))  
-            ;  
-
-        else if (Match(pReader, pos, 0x058688, i.codecNameAsUTF8))   
-            assert(i.codecNameAsUTF8);  
-
-        else
-        {
-            long len;
-            
-            const long long id = ReadUInt(pReader, pos, len);
-            assert(id >= 0);  //TODO: handle error case
-            assert((pos + len) <= stop);
-            
-            pos += len;  //consume id
-            
-            const long long size = ReadUInt(pReader, pos, len);
-            assert(size >= 0);  //TODO: handle error case
-            assert((pos + len) <= stop);
-            
-            pos += len;  //consume length of size
-            const long long start = pos;
-            
-            pos += size;  //consume payload
-            assert(pos <= stop);
-            
-            if (id == 0x60)
-            {
-                videoSettings.start = start;
-                videoSettings.size = size;
-            }
-            else if (id == 0x61)
-            {
-                audioSettings.start = start;
-                audioSettings.size = size;
-            }
-        }
-    }
-    
-    assert(pos == stop);
-    //TODO: propertly vet info.number, to ensure both its existence,
-    //and that it is unique among all tracks.
-    assert(i.number > 0);
-
-    //TODO: vet settings, to ensure that video settings (0x60)
-    //were specified when type = 1, and that audio settings (0x61)
-    //were specified when type = 2.    
-    if (i.type == 1)  //video
-    {
-        assert(audioSettings.start < 0);
-        assert(videoSettings.start >= 0);
-        
-        i.settings = videoSettings;
-        
-        VideoTrack* const t = new VideoTrack(m_pSegment, i);
-        assert(t);  //TODO
-        pTrack = t;    
-    }
-    else if (i.type == 2)  //audio
-    {
-        assert(videoSettings.start < 0);
-        assert(audioSettings.start >= 0);
-        
-        i.settings = audioSettings;
-        
-        AudioTrack* const t = new  AudioTrack(m_pSegment, i);
-        assert(t);  //TODO
-        pTrack = t;  
-    }
-    else
-    {
-        // for now we do not support other track types yet.
-        // TODO: support other track types
-        i.Clear();
-  
-        pTrack = NULL;
-    }
-    
-    return;
-}
-
-
-Tracks::~Tracks()
-{
-    Track** i = m_trackEntries;
-    Track** const j = m_trackEntriesEnd;
-    
-    while (i != j)
-    {
-        Track* pTrack = *i++;
-        delete pTrack;
-        pTrack = NULL;    
-    }
-
-    delete[] m_trackEntries;
-}
-
-
-Track* Tracks::GetTrackByNumber(unsigned long tn) const
-{
-    Track** i = m_trackEntries;
-    Track** const j = m_trackEntriesEnd;
-
-    while (i != j)
-    {
-        Track* const pTrack = *i++;
-       
-        if (pTrack == NULL)
-            continue;
-
-        if (tn == pTrack->GetNumber())
-            return pTrack;
-    }
-
-    return NULL;  //not found
-}
-
-
-Track* Tracks::GetTrackByIndex(unsigned long idx) const
-{
-    const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;
-       
-    if (idx >= static_cast<unsigned long>(count))
-         return NULL;
-
-    return m_trackEntries[idx];
-}
-
-
-void Cluster::Load()
-{
-    assert(m_pSegment);
-    
-    if (m_start > 0)
-    {
-        assert(m_size > 0);
-        assert(m_timecode >= 0);
-        return;
-    }
-    
-    assert(m_size == 0);
-    assert(m_timecode < 0);
-    
-    IMkvReader* const pReader = m_pSegment->m_pReader;
-
-    const long long off = -m_start;  //relative to segment
-    long long pos = m_pSegment->m_start + off;  //absolute
-    
-    long len;
-
-    const long long id_ = ReadUInt(pReader, pos, len);
-    assert(id_ >= 0);
-    assert(id_ == 0x0F43B675);  //Cluster ID
-    
-    pos += len;  //consume id
-    
-    const long long size_ = ReadUInt(pReader, pos, len);
-    assert(size_ >= 0);
-    
-    pos += len;  //consume size
-    
-    m_start = pos;
-    m_size = size_;
-    
-    const long long stop = m_start + size_;
-    
-    long long timecode = -1;
-    
-    while (pos < stop)
-    {
-        if (Match(pReader, pos, 0x67, timecode))
-            break;            
-        else
-        {
-            const long long id = ReadUInt(pReader, pos, len);
-            assert(id >= 0);  //TODO
-            assert((pos + len) <= stop);
-            
-            pos += len;  //consume id
-            
-            const long long size = ReadUInt(pReader, pos, len);
-            assert(size >= 0);  //TODO
-            assert((pos + len) <= stop);
-            
-            pos += len;  //consume size
-            
-            if (id == 0x20)  //BlockGroup ID
-                break;
-                
-            if (id == 0x23)  //SimpleBlock ID
-                break;
-
-            pos += size;  //consume payload
-            assert(pos <= stop);
-        }
-    }
-    
-    assert(pos <= stop);
-    assert(timecode >= 0);
-    
-    m_timecode = timecode;
-}
-
-
-Cluster* Cluster::Parse(
-    Segment* pSegment,
-    size_t idx,
-    long long off)
-{
-    assert(pSegment);
-    assert(off >= 0);
-    assert(off < pSegment->m_size);
-    Cluster* const pCluster = new Cluster(pSegment, idx, -off);
-    assert(pCluster);
-    
-    return pCluster;
-}
-
-
-Cluster::Cluster() :
-    m_pSegment(NULL),
-    m_index(0),
-    m_start(0),
-    m_size(0),
-    m_timecode(0),
-    m_pEntries(NULL),
-    m_entriesCount(0)
-{
-}
-
-Cluster::Cluster(
-    Segment* pSegment,
-    size_t idx,
-    long long off) :
-    m_pSegment(pSegment),
-    m_index(idx),
-    m_start(off),
-    m_size(0),
-    m_timecode(-1),
-    m_pEntries(NULL),
-    m_entriesCount(0)
-{
-}
-
-
-Cluster::~Cluster()
-{
-#if 0
-    while (!m_pEntries.empty())
-    {
-        BlockEntry* pBlockEntry = m_pEntries.front();
-        assert(pBlockEntry);
-        
-        m_pEntries.pop_front();
-        delete pBlockEntry;
-    }
-#else
-    BlockEntry** i = m_pEntries;
-    BlockEntry** const j = m_pEntries + m_entriesCount;
-    while (i != j)
-    {
-         BlockEntry* p = *i++;
-   
-         assert(p);
-         delete p;
-    }
- 
-    delete[] m_pEntries;
-#endif
-
-}
-
-bool Cluster::EOS() const
-{
-    return (m_pSegment == 0);
-}
-
-
-void Cluster::LoadBlockEntries()
-{
-    if (m_pEntries)
-        return;
-
-    Load();    
-    assert(m_timecode >= 0);
-    assert(m_start > 0);
-    assert(m_size > 0);
-    
-    IMkvReader* const pReader = m_pSegment->m_pReader;
-    
-    long long pos = m_start;
-    const long long stop = m_start + m_size;
-    long long timecode = -1;
-   
-    long long idx = pos;
-
-    m_entriesCount = 0;
-    
-    while (idx < stop)
-    {
-        if (Match(pReader, idx, 0x67, timecode))
-            assert(timecode == m_timecode);
-        else 
-        {
-            long len;
-            
-            const long long id = ReadUInt(pReader, idx, len);
-            assert(id >= 0);  //TODO
-            assert((idx + len) <= stop);
-            
-            idx += len;  //consume id
-            
-            const long long size = ReadUInt(pReader, idx, len);
-            assert(size >= 0);  //TODO
-            assert((idx + len) <= stop);
-            
-            idx += len;  //consume size
-            
-            if (id == 0x20)  //BlockGroup ID
-                ++m_entriesCount;
-            else if (id == 0x23)  //SimpleBlock ID
-                ++m_entriesCount;
-
-            idx += size;  //consume payload
-
-            assert(idx <= stop);
-        }  
-    }
-
-    if (m_entriesCount == 0)
-        return;
-     
-    m_pEntries = new BlockEntry*[m_entriesCount];
-    size_t index = 0;
-    
-    while (pos < stop)
-    {
-        if (Match(pReader, pos, 0x67, timecode))
-            assert(timecode == m_timecode);
-        else
-        {
-            long len;
-            const long long id = ReadUInt(pReader, pos, len);
-            assert(id >= 0);  //TODO
-            assert((pos + len) <= stop);
-            
-            pos += len;  //consume id
-            
-            const long long size = ReadUInt(pReader, pos, len);
-            assert(size >= 0);  //TODO
-            assert((pos + len) <= stop);
-            
-            pos += len;  //consume size
-            
-            if (id == 0x20)  //BlockGroup ID
-                ParseBlockGroup(pos, size, index++);
-            else if (id == 0x23)  //SimpleBlock ID
-                ParseSimpleBlock(pos, size, index++);
-
-            pos += size;  //consume payload
-            assert(pos <= stop);
-        }
-    }
-    
-    assert(pos == stop);
-    assert(timecode >= 0);
-    assert(index == m_entriesCount);
-}
-
-
-
-long long Cluster::GetTimeCode()
-{
-    Load();
-    return m_timecode;
-}
-
-
-long long Cluster::GetTime()
-{
-    const long long tc = GetTimeCode();
-    assert(tc >= 0);
-    
-    const SegmentInfo* const pInfo = m_pSegment->GetInfo();
-    assert(pInfo);
-    
-    const long long scale = pInfo->GetTimeCodeScale();
-    assert(scale >= 1);
-    
-    const long long t = m_timecode * scale;
-
-    return t;
-}
-
-
-void Cluster::ParseBlockGroup(long long start, long long size, size_t index)
-{
-    assert(m_pEntries);
-    assert(m_entriesCount);
-    assert(index < m_entriesCount);
-    
-    BlockGroup* const pGroup = new BlockGroup(this, index, start, size);
-    assert(pGroup);  //TODO
-        
-    m_pEntries[index] = pGroup;
-}
-
-
-
-void Cluster::ParseSimpleBlock(long long start, long long size, size_t index)
-{
-    assert(m_pEntries);
-    assert(m_entriesCount);
-    assert(index < m_entriesCount);
-
-    SimpleBlock* const pSimpleBlock = new SimpleBlock(this, index, start, size);
-    assert(pSimpleBlock);  //TODO
-        
-    m_pEntries[index] = pSimpleBlock;
-}
-
-
-const BlockEntry* Cluster::GetFirst()
-{
-    LoadBlockEntries();
-    
-    return m_pEntries[0];
-}
-
-        
-const BlockEntry* Cluster::GetLast()
-{ 
-    if (m_entriesCount == 0)
-        return m_pEntries[0];
-    
-    return m_pEntries[m_entriesCount-1];
-}
-
-        
-const BlockEntry* Cluster::GetNext(const BlockEntry* pEntry) const
-{
-    assert(pEntry);
-    
-    size_t idx = pEntry->GetIndex();
-    
-    ++idx;
-
-    if (idx == m_entriesCount) 
-      return NULL;
-
-    return m_pEntries[idx];
-
-}
-
-
-const BlockEntry* Cluster::GetEntry(const Track* pTrack)
-{
-
-    assert(pTrack);
-    
-    if (m_pSegment == NULL)  //EOS
-        return pTrack->GetEOS();
-    
-    LoadBlockEntries();
-    
-    BlockEntry* i = *m_pEntries;
-    BlockEntry* j = *m_pEntries + m_entriesCount;
-    while (i != j)
-    {
-        BlockEntry* pEntry = i;
-        i++;
-        assert(pEntry);
-        assert(!pEntry->EOS());
-        
-        const Block* const pBlock = pEntry->GetBlock();
-        assert(pBlock);
-        
-        if (pBlock->GetTrackNumber() != pTrack->GetNumber())
-            continue;
-
-        if (pTrack->VetEntry(pEntry))
-            return pEntry;
-    }
-    
-    return pTrack->GetEOS();  //no satisfactory block found
-}
-
-
-BlockEntry::BlockEntry()
-{
-}
-
-
-BlockEntry::~BlockEntry()
-{
-}
-
-
-
-SimpleBlock::SimpleBlock(
-    Cluster* pCluster, 
-    size_t idx, 
-    long long start, 
-    long long size) :
-    m_pCluster(pCluster),
-    m_index(idx),
-    m_block(start, size, pCluster->m_pSegment->m_pReader)
-{
-}
-
-
-bool SimpleBlock::EOS() const
-{
-    return false;
-}
-
-
-Cluster* SimpleBlock::GetCluster() const
-{
-    return m_pCluster;
-}
-
-
-size_t SimpleBlock::GetIndex() const
-{
-    return m_index;
-}
-
-
-const Block* SimpleBlock::GetBlock() const
-{
-    return &m_block;
-}
-
-
-bool SimpleBlock::IsBFrame() const
-{
-    return false;
-}
-
-
-BlockGroup::BlockGroup(
-    Cluster* pCluster, 
-    size_t idx, 
-    long long start, 
-    long long size_) :
-    m_pCluster(pCluster),
-    m_index(idx),
-    m_prevTimeCode(0),
-    m_nextTimeCode(0),
-    m_pBlock(NULL)  //TODO: accept multiple blocks within a block group
-{
-    IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;
-    
-    long long pos = start;
-    const long long stop = start + size_;
- 
-    bool bSimpleBlock = false;
-    
-    while (pos < stop)
-    {
-        short t;
-    
-        if (Match(pReader, pos, 0x7B, t))
-        {    
-            if (t < 0)
-                m_prevTimeCode = t;
-            else if (t > 0)
-                m_nextTimeCode = t;
-            else
-                assert(false);
-        }
-        else
-        {
-            long len;
-            const long long id = ReadUInt(pReader, pos, len);
-            assert(id >= 0);  //TODO
-            assert((pos + len) <= stop);
-            
-            pos += len;  //consume ID
-            
-            const long long size = ReadUInt(pReader, pos, len);
-            assert(size >= 0);  //TODO
-            assert((pos + len) <= stop);
-            
-            pos += len;  //consume size
-            
-            switch (id)
-            {
-                case 0x23:  //SimpleBlock ID
-                    bSimpleBlock = true;
-                    //YES, FALL THROUGH TO NEXT CASE
-
-                case 0x21:  //Block ID
-                    ParseBlock(pos, size);                    
-                    break;
-                    
-                default:
-                    break;
-            }
-                
-            pos += size;  //consume payload
-            assert(pos <= stop);
-        }
-    }
-    
-    assert(pos == stop);
-    assert(m_pBlock);
-    
-    if (!bSimpleBlock)
-        m_pBlock->SetKey(m_prevTimeCode >= 0);
-}
-
-
-BlockGroup::~BlockGroup()
-{
-    delete m_pBlock;
-}
-
-
-void BlockGroup::ParseBlock(long long start, long long size)
-{   
-    IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;
-    
-    Block* const pBlock = new Block(start, size, pReader);
-    assert(pBlock);  //TODO
-
-    //TODO: the Matroska spec says you have multiple blocks within the 
-    //same block group, with blocks ranked by priority (the flag bits).
-    //I haven't ever seen such a file (mkvmux certainly doesn't make
-    //one), so until then I'll just assume block groups contain a single
-    //block.
-#if 0    
-    m_blocks.push_back(pBlock);
-#else
-    assert(m_pBlock == NULL);
-    m_pBlock = pBlock;
-#endif
-
-#if 0
-    Track* const pTrack = pBlock->GetTrack();
-    assert(pTrack);
-    
-    pTrack->Insert(pBlock);
-#endif
-}
-
-
-bool BlockGroup::EOS() const
-{
-    return false;
-}
-
-
-Cluster* BlockGroup::GetCluster() const
-{
-    return m_pCluster;
-}
-
-
-size_t BlockGroup::GetIndex() const
-{
-    return m_index;
-}
-
-
-const Block* BlockGroup::GetBlock() const
-{
-    return m_pBlock;
-}
-
-
-short BlockGroup::GetPrevTimeCode() const
-{
-    return m_prevTimeCode;
-}
-
-
-short BlockGroup::GetNextTimeCode() const
-{
-    return m_nextTimeCode;
-}    
-
-
-bool BlockGroup::IsBFrame() const
-{
-    return (m_nextTimeCode > 0);
-}
-
-
-
-Block::Block(long long start, long long size_, IMkvReader* pReader) :
-    m_start(start),
-    m_size(size_)
-{
-    long long pos = start;
-    const long long stop = start + size_;
-
-    long len;
-    
-    m_track = ReadUInt(pReader, pos, len);
-    assert(m_track > 0);
-    assert((pos + len) <= stop);
-    
-    pos += len;  //consume track number
-    assert((stop - pos) >= 2);
-    
-    m_timecode = Unserialize2SInt(pReader, pos);
-
-    pos += 2;
-    assert((stop - pos) >= 1);
-    
-    const long hr = pReader->Read(pos, 1, &m_flags);
-    assert(hr == 0L);
-
-    ++pos;
-    assert(pos <= stop);
-    
-    m_frameOff = pos;
-    
-    const long long frame_size = stop - pos;
-
-    assert(frame_size <= 2147483647L);
-    
-    m_frameSize = static_cast<long>(frame_size);
-}
-
-
-long long Block::GetTimeCode(Cluster* pCluster) const
-{
-    assert(pCluster);
-    
-    const long long tc0 = pCluster->GetTimeCode();
-    assert(tc0 >= 0);
-    
-    const long long tc = tc0 + static_cast<long long>(m_timecode);
-    assert(tc >= 0);
-    
-    return tc;  //unscaled timecode units
-}
-
-
-long long Block::GetTime(Cluster* pCluster) const
-{
-    assert(pCluster);
-    
-    const long long tc = GetTimeCode(pCluster);
-    
-    const Segment* const pSegment = pCluster->m_pSegment;
-    const SegmentInfo* const pInfo = pSegment->GetInfo();
-    assert(pInfo);
-    
-    const long long scale = pInfo->GetTimeCodeScale();
-    assert(scale >= 1);
-    
-    const long long ns = tc * scale;
-
-    return ns;
-}
-
-
-unsigned long Block::GetTrackNumber() const
-{
-    assert(m_track > 0);
-    
-    return static_cast<unsigned long>(m_track);
-}
-
-
-bool Block::IsKey() const
-{
-    return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
-}
-
-
-void Block::SetKey(bool bKey)
-{
-    if (bKey)
-        m_flags |= static_cast<unsigned char>(1 << 7);
-    else
-        m_flags &= 0x7F;
-}
-
-
-long Block::GetSize() const
-{
-    return m_frameSize;
-}
-
-
-long Block::Read(IMkvReader* pReader, unsigned char* buf) const
-{
-
-    assert(pReader);
-    assert(buf);
-    
-    const long hr = pReader->Read(m_frameOff, m_frameSize, buf);
-    
-    return hr;
-}
-
-
-}  //end namespace mkvparser
+// Copyright (c) 2010 The WebM project authors. All Rights Reserved.

+//

+// Use of this source code is governed by a BSD-style license

+// that can be found in the LICENSE file in the root of the source

+// tree. An additional intellectual property rights grant can be found

+// in the file PATENTS.  All contributing project authors may

+// be found in the AUTHORS file in the root of the source tree.

+

+#include "mkvparser.hpp"

+#include <cassert>

+#include <cstring>

+#include <new>

+//#include <windows.h>

+//#include "odbgstream.hpp"

+//using std::endl;

+

+mkvparser::IMkvReader::~IMkvReader()

+{

+}

+

+

+void mkvparser::GetVersion(int& major, int& minor, int& build, int& revision)

+{

+    major = 1;

+    minor = 0;

+    build = 0;

+    revision = 4;

+}

+

+

+long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len)

+{

+    assert(pReader);

+    assert(pos >= 0);

+

+    long long total, available;

+

+    long hr = pReader->Length(&total, &available);

+    assert(hr >= 0);

+    assert(pos < available);

+    assert((available - pos) >= 1);  //assume here max u-int len is 8

+

+    unsigned char b;

+

+    hr = pReader->Read(pos, 1, &b);

+    if (hr < 0)

+        return hr;

+

+    assert(hr == 0L);

+

+    if (b & 0x80)       //1000 0000

+    {

+        len = 1;

+        b &= 0x7F;      //0111 1111

+    }

+    else if (b & 0x40)  //0100 0000

+    {

+        len = 2;

+        b &= 0x3F;      //0011 1111

+    }

+    else if (b & 0x20)  //0010 0000

+    {

+        len = 3;

+        b &= 0x1F;      //0001 1111

+    }

+    else if (b & 0x10)  //0001 0000

+    {

+        len = 4;

+        b &= 0x0F;      //0000 1111

+    }

+    else if (b & 0x08)  //0000 1000

+    {

+        len = 5;

+        b &= 0x07;      //0000 0111

+    }

+    else if (b & 0x04)  //0000 0100

+    {

+        len = 6;

+        b &= 0x03;      //0000 0011

+    }

+    else if (b & 0x02)  //0000 0010

+    {

+        len = 7;

+        b &= 0x01;      //0000 0001

+    }

+    else

+    {

+        assert(b & 0x01);  //0000 0001

+        len = 8;

+        b = 0;             //0000 0000

+    }

+

+    assert((available - pos) >= len);

+

+    long long result = b;

+    ++pos;

+    for (long i = 1; i < len; ++i)

+    {

+        hr = pReader->Read(pos, 1, &b);

+

+        if (hr < 0)

+            return hr;

+

+        assert(hr == 0L);

+

+        result <<= 8;

+        result |= b;

+

+        ++pos;

+    }

+

+    return result;

+}

+

+

+long long mkvparser::GetUIntLength(

+    IMkvReader* pReader,

+    long long pos,

+    long& len)

+{

+    assert(pReader);

+    assert(pos >= 0);

+

+    long long total, available;

+

+    long hr = pReader->Length(&total, &available);

+    assert(hr >= 0);

+    assert(available <= total);

+

+    if (pos >= available)

+        return pos;  //too few bytes available

+

+    unsigned char b;

+

+    hr = pReader->Read(pos, 1, &b);

+

+    if (hr < 0)

+        return hr;

+

+    assert(hr == 0L);

+

+    if (b == 0)  //we can't handle u-int values larger than 8 bytes

+        return E_FILE_FORMAT_INVALID;

+

+    unsigned char m = 0x80;

+    len = 1;

+

+    while (!(b & m))

+    {

+        m >>= 1;

+        ++len;

+    }

+

+    return 0;  //success

+}

+

+

+long long mkvparser::SyncReadUInt(

+    IMkvReader* pReader,

+    long long pos,

+    long long stop,

+    long& len)

+{

+    assert(pReader);

+

+    if (pos >= stop)

+        return E_FILE_FORMAT_INVALID;

+

+    unsigned char b;

+

+    long hr = pReader->Read(pos, 1, &b);

+

+    if (hr < 0)

+        return hr;

+

+    if (hr != 0L)

+        return E_BUFFER_NOT_FULL;

+

+    if (b == 0)  //we can't handle u-int values larger than 8 bytes

+        return E_FILE_FORMAT_INVALID;

+

+    unsigned char m = 0x80;

+    len = 1;

+

+    while (!(b & m))

+    {

+        m >>= 1;

+        ++len;

+    }

+

+    if ((pos + len) > stop)

+        return E_FILE_FORMAT_INVALID;

+

+    long long result = b & (~m);

+    ++pos;

+

+    for (int i = 1; i < len; ++i)

+    {

+        hr = pReader->Read(pos, 1, &b);

+

+        if (hr < 0)

+            return hr;

+

+        if (hr != 0L)

+            return E_BUFFER_NOT_FULL;

+

+        result <<= 8;

+        result |= b;

+

+        ++pos;

+    }

+

+    return result;

+}

+

+

+long long mkvparser::UnserializeUInt(

+    IMkvReader* pReader,

+    long long pos,

+    long long size)

+{

+    assert(pReader);

+    assert(pos >= 0);

+    assert(size > 0);

+    assert(size <= 8);

+

+    long long result = 0;

+

+    for (long long i = 0; i < size; ++i)

+    {

+        unsigned char b;

+

+        const long hr = pReader->Read(pos, 1, &b);

+

+        if (hr < 0)

+            return hr;

+        result <<= 8;

+        result |= b;

+

+        ++pos;

+    }

+

+    return result;

+}

+

+

+float mkvparser::Unserialize4Float(

+    IMkvReader* pReader,

+    long long pos)

+{

+    assert(pReader);

+    assert(pos >= 0);

+

+    long long total, available;

+

+    long hr = pReader->Length(&total, &available);

+    assert(hr >= 0);

+    assert(available <= total);

+    assert((pos + 4) <= available);

+

+    float result;

+

+    unsigned char* const p = (unsigned char*)&result;

+    unsigned char* q = p + 4;

+

+    for (;;)

+    {

+        hr = pReader->Read(pos, 1, --q);

+        assert(hr == 0L);

+

+        if (q == p)

+            break;

+

+        ++pos;

+    }

+

+    return result;

+}

+

+

+double mkvparser::Unserialize8Double(

+    IMkvReader* pReader,

+    long long pos)

+{

+    assert(pReader);

+    assert(pos >= 0);

+

+    double result;

+

+    unsigned char* const p = (unsigned char*)&result;

+    unsigned char* q = p + 8;

+

+    for (;;)

+    {

+        const long hr = pReader->Read(pos, 1, --q);

+        assert(hr == 0L);

+

+        if (q == p)

+            break;

+

+        ++pos;

+    }

+

+    return result;

+}

+

+signed char mkvparser::Unserialize1SInt(

+    IMkvReader* pReader,

+    long long pos)

+{

+    assert(pReader);

+    assert(pos >= 0);

+

+    long long total, available;

+

+    long hr = pReader->Length(&total, &available);

+    assert(hr == 0);

+    assert(available <= total);

+    assert(pos < available);

+

+    signed char result;

+

+    hr = pReader->Read(pos, 1, (unsigned char*)&result);

+    assert(hr == 0);

+

+    return result;

+}

+

+short mkvparser::Unserialize2SInt(

+    IMkvReader* pReader,

+    long long pos)

+{

+    assert(pReader);

+    assert(pos >= 0);

+

+    long long total, available;

+

+    long hr = pReader->Length(&total, &available);

+    assert(hr >= 0);

+    assert(available <= total);

+    assert((pos + 2) <= available);

+

+    short result;

+

+    unsigned char* const p = (unsigned char*)&result;

+    unsigned char* q = p + 2;

+

+    for (;;)

+    {

+        hr = pReader->Read(pos, 1, --q);

+        assert(hr == 0L);

+

+        if (q == p)

+            break;

+

+        ++pos;

+    }

+

+    return result;

+}

+

+

+bool mkvparser::Match(

+    IMkvReader* pReader,

+    long long& pos,

+    unsigned long id_,

+    long long& val)

+

+{

+    assert(pReader);

+    assert(pos >= 0);

+

+    long long total, available;

+

+    long hr = pReader->Length(&total, &available);

+    assert(hr >= 0);

+    assert(available <= total);

+

+    long len;

+

+    const long long id = ReadUInt(pReader, pos, len);

+    assert(id >= 0);

+    assert(len > 0);

+    assert(len <= 8);

+    assert((pos + len) <= available);

+

+    if ((unsigned long)id != id_)

+        return false;

+

+    pos += len;  //consume id

+

+    const long long size = ReadUInt(pReader, pos, len);

+    assert(size >= 0);

+    assert(size <= 8);

+    assert(len > 0);

+    assert(len <= 8);

+    assert((pos + len) <= available);

+

+    pos += len;  //consume length of size of payload

+

+    val = UnserializeUInt(pReader, pos, size);

+    assert(val >= 0);

+

+    pos += size;  //consume size of payload

+

+    return true;

+}

+

+bool mkvparser::Match(

+    IMkvReader* pReader,

+    long long& pos,

+    unsigned long id_,

+    char*& val)

+{

+    assert(pReader);

+    assert(pos >= 0);

+

+    long long total, available;

+

+    long hr = pReader->Length(&total, &available);

+    assert(hr >= 0);

+    assert(available <= total);

+

+    long len;

+

+    const long long id = ReadUInt(pReader, pos, len);

+    assert(id >= 0);

+    assert(len > 0);

+    assert(len <= 8);

+    assert((pos + len) <= available);

+

+    if ((unsigned long)id != id_)

+        return false;

+

+    pos += len;  //consume id

+

+    const long long size_ = ReadUInt(pReader, pos, len);

+    assert(size_ >= 0);

+    assert(len > 0);

+    assert(len <= 8);

+    assert((pos + len) <= available);

+

+    pos += len;  //consume length of size of payload

+    assert((pos + size_) <= available);

+

+    const size_t size = static_cast<size_t>(size_);

+    val = new char[size+1];

+

+    for (size_t i = 0; i < size; ++i)

+    {

+        char c;

+

+        hr = pReader->Read(pos + i, 1, (unsigned char*)&c);

+        assert(hr == 0L);

+

+        val[i] = c;

+

+        if (c == '\0')

+            break;

+

+    }

+

+    val[size] = '\0';

+    pos += size_;  //consume size of payload

+

+    return true;

+}

+

+bool mkvparser::Match(

+    IMkvReader* pReader,

+    long long& pos,

+    unsigned long id_,

+    unsigned char*& buf,

+    size_t& buflen)

+{

+    assert(pReader);

+    assert(pos >= 0);

+

+    long long total, available;

+

+    long hr = pReader->Length(&total, &available);

+    assert(hr >= 0);

+    assert(available <= total);

+

+    long len;

+    const long long id = ReadUInt(pReader, pos, len);

+    assert(id >= 0);

+    assert(len > 0);

+    assert(len <= 8);

+    assert((pos + len) <= available);

+

+    if ((unsigned long)id != id_)

+        return false;

+

+    pos += len;  //consume id

+

+    const long long size_ = ReadUInt(pReader, pos, len);

+    assert(size_ >= 0);

+    assert(len > 0);

+    assert(len <= 8);

+    assert((pos + len) <= available);

+

+    pos += len;  //consume length of size of payload

+    assert((pos + size_) <= available);

+

+    const long buflen_ = static_cast<long>(size_);

+

+    buf = new (std::nothrow) unsigned char[buflen_];

+    assert(buf);  //TODO

+

+    hr = pReader->Read(pos, buflen_, buf);

+    assert(hr == 0L);

+

+    buflen = buflen_;

+

+    pos += size_;  //consume size of payload

+    return true;

+}

+

+

+bool mkvparser::Match(

+    IMkvReader* pReader,

+    long long& pos,

+    unsigned long id_,

+    double& val)

+{

+    assert(pReader);

+    assert(pos >= 0);

+

+    long long total, available;

+

+    long hr = pReader->Length(&total, &available);

+    assert(hr >= 0);

+    assert(available <= total);

+    long idlen;

+    const long long id = ReadUInt(pReader, pos, idlen);

+    assert(id >= 0);  //TODO

+

+    if ((unsigned long)id != id_)

+        return false;

+

+    long sizelen;

+    const long long size = ReadUInt(pReader, pos + idlen, sizelen);

+

+    switch (size)

+    {

+        case 4:

+        case 8:

+            break;

+        default:

+            return false;

+    }

+

+    pos += idlen + sizelen;  //consume id and size fields

+    assert((pos + size) <= available);

+

+    if (size == 4)

+        val = Unserialize4Float(pReader, pos);

+    else

+    {

+        assert(size == 8);

+        val = Unserialize8Double(pReader, pos);

+    }

+

+    pos += size;  //consume size of payload

+

+    return true;

+}

+

+

+bool mkvparser::Match(

+    IMkvReader* pReader,

+    long long& pos,

+    unsigned long id_,

+    short& val)

+{

+    assert(pReader);

+    assert(pos >= 0);

+

+    long long total, available;

+

+    long hr = pReader->Length(&total, &available);

+    assert(hr >= 0);

+    assert(available <= total);

+

+    long len;

+    const long long id = ReadUInt(pReader, pos, len);

+    assert(id >= 0);

+    assert((pos + len) <= available);

+

+    if ((unsigned long)id != id_)

+        return false;

+

+    pos += len;  //consume id

+

+    const long long size = ReadUInt(pReader, pos, len);

+    assert(size <= 2);

+    assert((pos + len) <= available);

+

+    pos += len;  //consume length of size of payload

+    assert((pos + size) <= available);

+

+    //TODO:

+    // Generalize this to work for any size signed int

+    if (size == 1)

+        val = Unserialize1SInt(pReader, pos);

+    else

+        val = Unserialize2SInt(pReader, pos);

+

+    pos += size;  //consume size of payload

+

+    return true;

+}

+

+

+namespace mkvparser

+{

+

+EBMLHeader::EBMLHeader():

+    m_docType(NULL)

+{

+}

+

+EBMLHeader::~EBMLHeader()

+{

+    delete[] m_docType;

+}

+

+long long EBMLHeader::Parse(

+    IMkvReader* pReader,

+    long long& pos)

+{

+    assert(pReader);

+

+    long long total, available;

+

+    long hr = pReader->Length(&total, &available);

+

+    if (hr < 0)

+        return hr;

+

+    pos = 0;

+    long long end = (1024 < available)? 1024: available;

+

+    for (;;)

+    {

+        unsigned char b = 0;

+

+        while (pos < end)

+        {

+            hr = pReader->Read(pos, 1, &b);

+

+            if (hr < 0)

+                return hr;

+

+            if (b == 0x1A)

+                break;

+

+            ++pos;

+        }

+

+        if (b != 0x1A)

+        {

+            if ((pos >= 1024) ||

+                (available >= total) ||

+                ((total - available) < 5))

+                  return -1;

+

+            return available + 5;  //5 = 4-byte ID + 1st byte of size

+        }

+

+        if ((total - pos) < 5)

+            return E_FILE_FORMAT_INVALID;

+

+        if ((available - pos) < 5)

+            return pos + 5;  //try again later

+

+        long len;

+

+        const long long result = ReadUInt(pReader, pos, len);

+

+        if (result < 0)  //error

+            return result;

+

+        if (result == 0x0A45DFA3)  //ReadId masks-off length indicator bits

+        {

+            assert(len == 4);

+            pos += len;

+            break;

+        }

+

+        ++pos;  //throw away just the 0x1A byte, and try again

+    }

+

+    long len;

+    long long result = GetUIntLength(pReader, pos, len);

+

+    if (result < 0)  //error

+        return result;

+

+    if (result > 0)  //need more data

+        return result;

+

+    assert(len > 0);

+    assert(len <= 8);

+

+    if ((total -  pos) < len)

+        return E_FILE_FORMAT_INVALID;

+    if ((available - pos) < len)

+        return pos + len;  //try again later

+

+    result = ReadUInt(pReader, pos, len);

+

+    if (result < 0)  //error

+        return result;

+

+    pos += len;  //consume u-int

+

+    if ((total - pos) < result)

+        return E_FILE_FORMAT_INVALID;

+

+    if ((available - pos) < result)

+        return pos + result;

+

+    end = pos + result;

+

+    m_version = 1;

+    m_readVersion = 1;

+    m_maxIdLength = 4;

+    m_maxSizeLength = 8;

+    m_docTypeVersion = 1;

+    m_docTypeReadVersion = 1;

+

+    while (pos < end)

+    {

+        if (Match(pReader, pos, 0x0286, m_version))

+            ;

+        else if (Match(pReader, pos, 0x02F7, m_readVersion))

+            ;

+        else if (Match(pReader, pos, 0x02F2, m_maxIdLength))

+            ;

+        else if (Match(pReader, pos, 0x02F3, m_maxSizeLength))

+            ;

+        else if (Match(pReader, pos, 0x0282, m_docType))

+            ;

+        else if (Match(pReader, pos, 0x0287, m_docTypeVersion))

+            ;

+        else if (Match(pReader, pos, 0x0285, m_docTypeReadVersion))

+            ;

+        else

+        {

+            result = ReadUInt(pReader, pos, len);

+            assert(result > 0);

+            assert(len > 0);

+            assert(len <= 8);

+

+            pos += len;

+            assert(pos < end);

+

+            result = ReadUInt(pReader, pos, len);

+            assert(result >= 0);

+            assert(len > 0);

+            assert(len <= 8);

+

+            pos += len + result;

+            assert(pos <= end);

+        }

+    }

+

+    assert(pos == end);

+

+    return 0;

+}

+

+

+Segment::Segment(

+    IMkvReader* pReader,

+    long long start,

+    long long size) :

+    m_pReader(pReader),

+    m_start(start),

+    m_size(size),

+    m_pos(start),

+    m_pInfo(NULL),

+    m_pTracks(NULL),

+    m_pCues(NULL),

+    m_clusters(NULL),

+    m_clusterCount(0),

+    m_clusterPreloadCount(0),

+    m_clusterSize(0)

+{

+}

+

+

+Segment::~Segment()

+{

+    const long count = m_clusterCount + m_clusterPreloadCount;

+

+    Cluster** i = m_clusters;

+    Cluster** j = m_clusters + count;

+

+    while (i != j)

+    {

+        Cluster* const p = *i++;

+        assert(p);

+

+        delete p;

+    }

+

+    delete[] m_clusters;

+

+    delete m_pTracks;

+    delete m_pInfo;

+    delete m_pCues;

+}

+

+

+long long Segment::CreateInstance(

+    IMkvReader* pReader,

+    long long pos,

+    Segment*& pSegment)

+{

+    assert(pReader);

+    assert(pos >= 0);

+

+    pSegment = NULL;

+

+    long long total, available;

+

+    long hr = pReader->Length(&total, &available);

+    assert(hr >= 0);

+    assert(available <= total);

+

+    //I would assume that in practice this loop would execute

+    //exactly once, but we allow for other elements (e.g. Void)

+    //to immediately follow the EBML header.  This is fine for

+    //the source filter case (since the entire file is available),

+    //but in the splitter case over a network we should probably

+    //just give up early.  We could for example decide only to

+    //execute this loop a maximum of, say, 10 times.

+

+    while (pos < total)

+    {

+        //Read ID

+

+        long len;

+        long long result = GetUIntLength(pReader, pos, len);

+

+        if (result)  //error, or too few available bytes

+            return result;

+

+        if ((pos + len) > total)

+            return E_FILE_FORMAT_INVALID;

+

+        if ((pos + len) > available)

+            return pos + len;

+

+        //TODO: if we liberalize the behavior of ReadUInt, we can

+        //probably eliminate having to use GetUIntLength here.

+        const long long id = ReadUInt(pReader, pos, len);

+

+        if (id < 0)  //error

+            return id;

+

+        pos += len;  //consume ID

+

+        //Read Size

+

+        result = GetUIntLength(pReader, pos, len);

+

+        if (result)  //error, or too few available bytes

+            return result;

+

+        if ((pos + len) > total)

+            return E_FILE_FORMAT_INVALID;

+

+        if ((pos + len) > available)

+            return pos + len;

+

+        //TODO: if we liberalize the behavior of ReadUInt, we can

+        //probably eliminate having to use GetUIntLength here.

+        const long long size = ReadUInt(pReader, pos, len);

+

+        if (size < 0)

+            return size;

+

+        pos += len;  //consume length of size of element

+

+        //Pos now points to start of payload

+

+        if ((pos + size) > total)

+            return E_FILE_FORMAT_INVALID;

+

+        if (id == 0x08538067)  //Segment ID

+        {

+            pSegment = new  Segment(pReader, pos, size);

+            assert(pSegment);  //TODO

+

+            return 0;    //success

+        }

+

+        pos += size;  //consume payload

+    }

+

+    assert(pos == total);

+

+    pSegment = new Segment(pReader, pos, 0);

+    assert(pSegment);  //TODO

+

+    return 0;  //success (sort of)

+}

+

+

+long long Segment::ParseHeaders()

+{

+    //Outermost (level 0) segment object has been constructed,

+    //and pos designates start of payload.  We need to find the

+    //inner (level 1) elements.

+    long long total, available;

+

+    long hr = m_pReader->Length(&total, &available);

+    assert(hr >= 0);

+    assert(available <= total);

+

+    const long long stop = m_start + m_size;

+    assert(stop <= total);

+    assert(m_pos <= stop);

+

+    bool bQuit = false;

+

+    while ((m_pos < stop) && !bQuit)

+    {

+        long long pos = m_pos;

+

+        long len;

+        long long result = GetUIntLength(m_pReader, pos, len);

+

+        if (result)  //error, or too few available bytes

+            return result;

+

+        if ((pos + len) > stop)

+            return E_FILE_FORMAT_INVALID;

+

+        if ((pos + len) > available)

+            return pos + len;

+

+        const long long idpos = pos;

+        const long long id = ReadUInt(m_pReader, idpos, len);

+

+        if (id < 0)  //error

+            return id;

+

+        pos += len;  //consume ID

+

+        //Read Size

+        result = GetUIntLength(m_pReader, pos, len);

+

+        if (result)  //error, or too few available bytes

+            return result;

+

+        if ((pos + len) > stop)

+            return E_FILE_FORMAT_INVALID;

+

+        if ((pos + len) > available)

+            return pos + len;

+

+        const long long size = ReadUInt(m_pReader, pos, len);

+

+        if (size < 0)

+            return size;

+

+        pos += len;  //consume length of size of element

+

+        //Pos now points to start of payload

+

+        if ((pos + size) > stop)

+            return E_FILE_FORMAT_INVALID;

+

+        //We read EBML elements either in total or nothing at all.

+

+        if ((pos + size) > available)

+            return pos + size;

+

+        if (id == 0x0549A966)  //Segment Info ID

+        {

+            assert(m_pInfo == NULL);

+

+            m_pInfo = new SegmentInfo(this, pos, size);

+            assert(m_pInfo);  //TODO

+        }

+        else if (id == 0x0654AE6B)  //Tracks ID

+        {

+            assert(m_pTracks == NULL);

+

+            m_pTracks = new Tracks(this, pos, size);

+            assert(m_pTracks);  //TODO

+        }

+        else if (id == 0x0C53BB6B)  //Cues ID

+        {

+            if (m_pCues == NULL)

+            {

+                m_pCues = new Cues(this, pos, size);

+                assert(m_pCues);  //TODO

+            }

+        }

+        else if (id == 0x014D9B74)  //SeekHead ID

+        {

+            ParseSeekHead(pos, size);

+        }

+        else if (id == 0x0F43B675)  //Cluster ID

+        {

+            bQuit = true;

+        }

+

+        if (!bQuit)

+            m_pos = pos + size;  //consume payload

+    }

+

+    assert(m_pos <= stop);

+

+    if (m_pInfo == NULL)  //TODO: liberalize this behavior

+        return E_FILE_FORMAT_INVALID;

+

+    if (m_pTracks == NULL)

+        return E_FILE_FORMAT_INVALID;

+

+    return 0;  //success

+}

+

+

+#if 0

+long Segment::ParseCluster(Cluster*& pCluster, long long& pos_) const

+{

+    pCluster = NULL;

+    pos_ = -1;

+

+    const long long stop = m_start + m_size;

+    assert(m_pos <= stop);

+

+    long long pos = m_pos;

+    long long off = -1;

+

+    while (pos < stop)

+    {

+        long len;

+        const long long idpos = pos;

+

+        const long long id = SyncReadUInt(m_pReader, pos, stop, len);

+

+        if (id < 0)  //error

+            return static_cast<long>(id);

+

+        if (id == 0)

+            return E_FILE_FORMAT_INVALID;

+

+        pos += len;  //consume id

+        assert(pos < stop);

+

+        const long long size = SyncReadUInt(m_pReader, pos, stop, len);

+

+        if (size < 0)  //error

+            return static_cast<long>(size);

+

+        pos += len;  //consume size

+        assert(pos <= stop);

+

+        if (size == 0)  //weird

+            continue;

+

+        //pos now points to start of payload

+

+        pos += size;  //consume payload

+        assert(pos <= stop);

+

+        if (id == 0x0F43B675)  //Cluster ID

+        {

+            off = idpos - m_start;  // >= 0 means we found a cluster

+            break;

+        }

+    }

+

+    assert(pos <= stop);

+

+    //Indicate to caller how much of file has been consumed. This is

+    //used later in AddCluster to adjust the current parse position

+    //(the value cached in the segment object itself) to the

+    //file position value just past the cluster we parsed.

+

+    if (off < 0)  //we did not found any more clusters

+    {

+        pos_ = stop;  //pos_ >= 0 here means EOF (cluster is NULL)

+        return 0;     //TODO: confirm this return value

+    }

+

+    //We found a cluster.  Now read something, to ensure that it is

+    //fully loaded in the network cache.

+

+    if (pos >= stop)  //we parsed the entire segment

+    {

+        //We did find a cluster, but it was very last element in the segment.

+        //Our preference is that the loop above runs 1 1/2 times:

+        //the first pass finds the cluster, and the second pass

+        //finds the element the follows the cluster.  In this case, however,

+        //we reached the end of the file without finding another element,

+        //so we didn't actually read anything yet associated with "end of the

+        //cluster".  And we must perform an actual read, in order

+        //to guarantee that all of the data that belongs to this

+        //cluster has been loaded into the network cache.  So instead

+        //of reading the next element that follows the cluster, we

+        //read the last byte of the cluster (which is also the last

+        //byte in the file).

+

+        //Read the last byte of the file. (Reading 0 bytes at pos

+        //might work too -- it would depend on how the reader is

+        //implemented.  Here we take the more conservative approach,

+        //since this makes fewer assumptions about the network

+        //reader abstraction.)

+

+        unsigned char b;

+

+        const int result = m_pReader->Read(pos - 1, 1, &b);

+        assert(result == 0);

+

+        pos_ = stop;

+    }

+    else

+    {

+        long len;

+        const long long idpos = pos;

+

+        const long long id = SyncReadUInt(m_pReader, pos, stop, len);

+

+        if (id < 0)  //error

+            return static_cast<long>(id);

+

+        if (id == 0)

+            return E_BUFFER_NOT_FULL;

+

+        pos += len;  //consume id

+        assert(pos < stop);

+

+        const long long size = SyncReadUInt(m_pReader, pos, stop, len);

+

+        if (size < 0)  //error

+            return static_cast<long>(size);

+

+        pos_ = idpos;

+    }

+

+    //We found a cluster, and it has been completely loaded into the

+    //network cache.  (We can guarantee this because we actually read

+    //the EBML tag that follows the cluster, or, if we reached EOF,

+    //because we actually read the last byte of the cluster).

+

+    Segment* const this_ = const_cast<Segment*>(this);

+

+    pCluster = Cluster::Parse(this_, m_clusterCount, off);

+    assert(pCluster);

+    assert(pCluster->m_index == m_clusterCount);

+

+    return 0;

+}

+

+

+bool Segment::AddCluster(Cluster* pCluster, long long pos)

+{

+    assert(pos >= m_start);

+

+    const long long stop = m_start + m_size;

+    assert(pos <= stop);

+

+    if (pCluster)

+    {

+        AppendCluster(pCluster);

+        assert(m_clusters);

+        assert(m_clusterSize > pCluster->m_index);

+        assert(m_clusters[pCluster->m_index] == pCluster);

+    }

+

+    m_pos = pos;  //m_pos >= stop is now we know we have all clusters

+

+    return (pos >= stop);

+}

+#endif

+

+

+long Segment::LoadCluster()

+{

+    const long long stop = m_start + m_size;

+

+    while (m_pos < stop)

+    {

+        long long pos = m_pos;

+

+        long len;

+

+        long long result = GetUIntLength(m_pReader, pos, len);

+

+        if (result < 0)  //error

+            return static_cast<long>(result);

+

+        if ((pos + len) > stop)

+            return E_FILE_FORMAT_INVALID;

+

+        const long long idpos = pos;

+        const long long id = ReadUInt(m_pReader, idpos, len);

+

+        if (id < 0)  //error

+            return static_cast<long>(id);

+

+        pos += len;  //consume ID

+

+        //Read Size

+        result = GetUIntLength(m_pReader, pos, len);

+

+        if (result < 0)  //error

+            return static_cast<long>(result);

+

+        if ((pos + len) > stop)

+            return E_FILE_FORMAT_INVALID;

+

+        const long long size = ReadUInt(m_pReader, pos, len);

+

+        if (size < 0)  //error

+            return static_cast<long>(size);

+

+        pos += len;  //consume length of size of element

+

+        if (size == 0)  //weird

+        {

+            m_pos = pos;

+            continue;

+        }

+

+        //Pos now points to start of payload

+

+        if ((pos + size) > stop)

+            return E_FILE_FORMAT_INVALID;

+

+        if (id == 0x0C53BB6B)  //Cues ID

+        {

+            if (m_pCues == NULL)

+            {

+                m_pCues = new Cues(this, pos, size);

+                assert(m_pCues);  //TODO

+            }

+

+            m_pos = pos + size;  //consume payload

+            continue;

+        }

+

+        if (id != 0x0F43B675)  //Cluster ID

+        {

+            m_pos = pos + size;  //consume payload

+            continue;

+        }

+

+        const long idx = m_clusterCount;

+        const long long idoff = idpos - m_start;

+

+        if (m_clusterPreloadCount > 0)

+        {

+            assert(idx < m_clusterSize);

+

+            Cluster* const pCluster = m_clusters[idx];

+            assert(pCluster);

+            assert(pCluster->m_index < 0);

+

+            const long long off_ = pCluster->m_pos;

+            assert(off_);

+

+            const long long off = off_ * ((off_ >= 0) ? 1 : -1);

+            assert(idoff <= off);

+

+            if (idoff == off)  //cluster has been preloaded already

+            {

+                pCluster->m_index = idx;

+                ++m_clusterCount;

+                --m_clusterPreloadCount;

+

+                m_pos = pos + size;  //consume payload

+                break;

+            }

+        }

+

+        Cluster* const pCluster = Cluster::Parse(this, idx, idoff);

+        assert(pCluster);

+        assert(pCluster->m_index == idx);

+

+        AppendCluster(pCluster);

+        assert(m_clusters);

+        assert(idx < m_clusterSize);

+        assert(m_clusters[idx] == pCluster);

+

+        m_pos = pos + size;  //consume payload

+        break;

+    }

+

+    assert(m_pos <= stop);

+    return 0;

+}

+

+

+void Segment::AppendCluster(Cluster* pCluster)

+{

+    assert(pCluster);

+    assert(pCluster->m_index >= 0);

+

+    const long count = m_clusterCount + m_clusterPreloadCount;

+

+    long& size = m_clusterSize;

+    assert(size >= count);

+

+    const long idx = pCluster->m_index;

+    assert(idx == m_clusterCount);

+

+    if (count >= size)

+    {

+        long n;

+

+        if (size > 0)

+            n = 2 * size;

+        else if (m_pInfo == 0)

+            n = 2048;

+        else

+        {

+            const long long ns = m_pInfo->GetDuration();

+

+            if (ns <= 0)

+                n = 2048;

+            else

+            {

+                const long long sec = (ns + 999999999LL) / 1000000000LL;

+                n = static_cast<long>(sec);

+            }

+        }

+

+        Cluster** const qq = new Cluster*[n];

+        Cluster** q = qq;

+

+        Cluster** p = m_clusters;

+        Cluster** const pp = p + count;

+

+        while (p != pp)

+            *q++ = *p++;

+

+        delete[] m_clusters;

+

+        m_clusters = qq;

+        size = n;

+    }

+

+    if (m_clusterPreloadCount > 0)

+    {

+        assert(m_clusters);

+

+        Cluster** const p = m_clusters + m_clusterCount;

+        assert(*p);

+        assert((*p)->m_index < 0);

+

+        Cluster** q = p + m_clusterPreloadCount;

+        assert(q < (m_clusters + size));

+

+        for (;;)

+        {

+            Cluster** const qq = q - 1;

+            assert((*qq)->m_index < 0);

+

+            *q = *qq;

+            q = qq;

+

+            if (q == p)

+                break;

+        }

+    }

+

+    m_clusters[idx] = pCluster;

+    ++m_clusterCount;

+}

+

+

+void Segment::PreloadCluster(Cluster* pCluster, ptrdiff_t idx)

+{

+    assert(pCluster);

+    assert(pCluster->m_index < 0);

+    assert(idx >= m_clusterCount);

+

+    const long count = m_clusterCount + m_clusterPreloadCount;

+

+    long& size = m_clusterSize;

+    assert(size >= count);

+

+    if (count >= size)

+    {

+        long n;

+

+        if (size > 0)

+            n = 2 * size;

+        else if (m_pInfo == 0)

+            n = 2048;

+        else

+        {

+            const long long ns = m_pInfo->GetDuration();

+

+            if (ns <= 0)

+                n = 2048;

+            else

+            {

+                const long long sec = (ns + 999999999LL) / 1000000000LL;

+                n = static_cast<long>(sec);

+            }

+        }

+

+        Cluster** const qq = new Cluster*[n];

+        Cluster** q = qq;

+

+        Cluster** p = m_clusters;

+        Cluster** const pp = p + count;

+

+        while (p != pp)

+            *q++ = *p++;

+

+        delete[] m_clusters;

+

+        m_clusters = qq;

+        size = n;

+    }

+

+    assert(m_clusters);

+

+    Cluster** const p = m_clusters + idx;

+

+    Cluster** q = m_clusters + count;

+    assert(q >= p);

+    assert(q < (m_clusters + size));

+

+    while (q > p)

+    {

+        Cluster** const qq = q - 1;

+        assert((*qq)->m_index < 0);

+

+        *q = *qq;

+        q = qq;

+    }

+

+    m_clusters[idx] = pCluster;

+    ++m_clusterPreloadCount;

+}

+

+

+long Segment::Load()

+{

+    assert(m_clusters == NULL);

+    assert(m_clusterSize == 0);

+    assert(m_clusterCount == 0);

+

+    //Outermost (level 0) segment object has been constructed,

+    //and pos designates start of payload.  We need to find the

+    //inner (level 1) elements.

+    const long long stop = m_start + m_size;

+

+#ifdef _DEBUG  //TODO: this is really Microsoft-specific

+    {

+        long long total, available;

+

+        long hr = m_pReader->Length(&total, &available);

+        assert(hr >= 0);

+        assert(available >= total);

+        assert(stop <= total);

+    }

+#endif

+

+    while (m_pos < stop)

+    {

+        long long pos = m_pos;

+

+        long len;

+

+        long long result = GetUIntLength(m_pReader, pos, len);

+

+        if (result < 0)  //error

+            return static_cast<long>(result);

+

+        if ((pos + len) > stop)

+            return E_FILE_FORMAT_INVALID;

+

+        const long long idpos = pos;

+        const long long id = ReadUInt(m_pReader, idpos, len);

+

+        if (id < 0)  //error

+            return static_cast<long>(id);

+

+        pos += len;  //consume ID

+

+        //Read Size

+        result = GetUIntLength(m_pReader, pos, len);

+

+        if (result < 0)  //error

+            return static_cast<long>(result);

+

+        if ((pos + len) > stop)

+            return E_FILE_FORMAT_INVALID;

+

+        const long long size = ReadUInt(m_pReader, pos, len);

+

+        if (size < 0)  //error

+            return static_cast<long>(size);

+

+        pos += len;  //consume length of size of element

+

+        //Pos now points to start of payload

+

+        if ((pos + size) > stop)

+            return E_FILE_FORMAT_INVALID;

+

+        if (id == 0x0F43B675)  //Cluster ID

+        {

+            const long idx = m_clusterCount;

+            const long long off = idpos - m_start;

+

+            Cluster* const pCluster = Cluster::Parse(this, idx, off);

+            assert(pCluster);

+            assert(pCluster->m_index == idx);

+

+            AppendCluster(pCluster);

+            assert(m_clusters);

+            assert(m_clusterSize > idx);

+            assert(m_clusters[idx] == pCluster);

+        }

+        else if (id == 0x0C53BB6B)  //Cues ID

+        {

+            assert(m_pCues == NULL);

+

+            m_pCues = new Cues(this, pos, size);

+            assert(m_pCues);  //TODO

+        }

+        else if (id == 0x0549A966)  //SegmentInfo ID

+        {

+            assert(m_pInfo == NULL);

+

+            m_pInfo = new  SegmentInfo(this, pos, size);

+            assert(m_pInfo);

+        }

+        else if (id == 0x0654AE6B)  //Tracks ID

+        {

+            assert(m_pTracks == NULL);

+

+            m_pTracks = new Tracks(this, pos, size);

+            assert(m_pTracks);  //TODO

+        }

+

+        m_pos = pos + size;  //consume payload

+    }

+

+    assert(m_pos >= stop);

+

+    if (m_pInfo == NULL)

+        return E_FILE_FORMAT_INVALID;  //TODO: ignore this case?

+

+    if (m_pTracks == NULL)

+        return E_FILE_FORMAT_INVALID;

+

+    if (m_clusters == NULL)  //TODO: ignore this case?

+        return E_FILE_FORMAT_INVALID;

+

+    //TODO: decide whether we require Cues element

+    //if (m_pCues == NULL)

+    //   return E_FILE_FORMAT_INVALID;

+

+    return 0;

+}

+

+

+void Segment::ParseSeekHead(long long start, long long size_)

+{

+    long long pos = start;

+    const long long stop = start + size_;

+

+    while (pos < stop)

+    {

+        long len;

+

+        const long long id = ReadUInt(m_pReader, pos, len);

+        assert(id >= 0);  //TODO

+        assert((pos + len) <= stop);

+

+        pos += len;  //consume ID

+

+        const long long size = ReadUInt(m_pReader, pos, len);

+        assert(size >= 0);

+        assert((pos + len) <= stop);

+

+        pos += len;  //consume Size field

+        assert((pos + size) <= stop);

+

+        if (id == 0x0DBB)  //SeekEntry ID

+            ParseSeekEntry(pos, size);

+

+        pos += size;  //consume payload

+        assert(pos <= stop);

+    }

+

+    assert(pos == stop);

+}

+

+

+void Segment::ParseCues(long long off)

+{

+    if (m_pCues)

+        return;

+

+    //odbgstream os;

+    //os << "Segment::ParseCues (begin)" << endl;

+

+    long long pos = m_start + off;

+    const long long stop = m_start + m_size;

+

+    long len;

+

+    long long result = GetUIntLength(m_pReader, pos, len);

+    assert(result == 0);

+    assert((pos + len) <= stop);

+

+    const long long idpos = pos;

+

+    const long long id = ReadUInt(m_pReader, idpos, len);

+    assert(id == 0x0C53BB6B);  //Cues ID

+

+    pos += len;  //consume ID

+    assert(pos < stop);

+

+    //Read Size

+

+    result = GetUIntLength(m_pReader, pos, len);

+    assert(result == 0);

+    assert((pos + len) <= stop);

+

+    const long long size = ReadUInt(m_pReader, pos, len);

+    assert(size >= 0);

+

+    pos += len;  //consume length of size of element

+    assert((pos + size) <= stop);

+

+    //Pos now points to start of payload

+

+    m_pCues = new Cues(this, pos, size);

+    assert(m_pCues);  //TODO

+

+    //os << "Segment::ParseCues (end)" << endl;

+}

+

+

+void Segment::ParseSeekEntry(

+   long long start,

+   long long size_)

+{

+    long long pos = start;

+

+    const long long stop = start + size_;

+

+    long len;

+

+    const long long seekIdId = ReadUInt(m_pReader, pos, len);

+    //seekIdId;

+    assert(seekIdId == 0x13AB);  //SeekID ID

+    assert((pos + len) <= stop);

+

+    pos += len;  //consume id

+

+    const long long seekIdSize = ReadUInt(m_pReader, pos, len);

+    assert(seekIdSize >= 0);

+    assert((pos + len) <= stop);

+

+    pos += len;  //consume size

+

+    const long long seekId = ReadUInt(m_pReader, pos, len);  //payload

+    assert(seekId >= 0);

+    assert(len == seekIdSize);

+    assert((pos + len) <= stop);

+

+    pos += seekIdSize;  //consume payload

+

+    const long long seekPosId = ReadUInt(m_pReader, pos, len);

+    //seekPosId;

+    assert(seekPosId == 0x13AC);  //SeekPos ID

+    assert((pos + len) <= stop);

+

+    pos += len;  //consume id

+

+    const long long seekPosSize = ReadUInt(m_pReader, pos, len);

+    assert(seekPosSize >= 0);

+    assert((pos + len) <= stop);

+

+    pos += len;  //consume size

+    assert((pos + seekPosSize) <= stop);

+

+    const long long seekOff = UnserializeUInt(m_pReader, pos, seekPosSize);

+    assert(seekOff >= 0);

+    assert(seekOff < m_size);

+

+    pos += seekPosSize;  //consume payload

+    assert(pos == stop);

+

+    const long long seekPos = m_start + seekOff;

+    assert(seekPos < (m_start + m_size));

+

+    if (seekId == 0x0C53BB6B)  //Cues ID

+        ParseCues(seekOff);

+}

+

+

+Cues::Cues(Segment* pSegment, long long start_, long long size_) :

+    m_pSegment(pSegment),

+    m_start(start_),

+    m_size(size_),

+    m_cue_points(NULL),

+    m_count(0),

+    m_preload_count(0),

+    m_pos(start_)

+{

+}

+

+

+Cues::~Cues()

+{

+    const size_t n = m_count + m_preload_count;

+

+    CuePoint** p = m_cue_points;

+    CuePoint** const q = p + n;

+

+    while (p != q)

+    {

+        CuePoint* const pCP = *p++;

+        assert(pCP);

+

+        delete pCP;

+    }

+

+    delete[] m_cue_points;

+}

+

+

+void Cues::Init() const

+{

+    if (m_cue_points)

+        return;

+

+    assert(m_count == 0);

+    assert(m_preload_count == 0);

+

+    IMkvReader* const pReader = m_pSegment->m_pReader;

+

+    const long long stop = m_start + m_size;

+    long long pos = m_start;

+

+    size_t cue_points_size = 0;

+

+    while (pos < stop)

+    {

+        const long long idpos = pos;

+

+        long len;

+

+        const long long id = ReadUInt(pReader, pos, len);

+        assert(id >= 0);  //TODO

+        assert((pos + len) <= stop);

+

+        pos += len;  //consume ID

+

+        const long long size = ReadUInt(pReader, pos, len);

+        assert(size >= 0);

+        assert((pos + len) <= stop);

+

+        pos += len;  //consume Size field

+        assert((pos + size) <= stop);

+

+        if (id == 0x3B)  //CuePoint ID

+            PreloadCuePoint(cue_points_size, idpos);

+

+        pos += size;  //consume payload

+        assert(pos <= stop);

+    }

+}

+

+

+void Cues::PreloadCuePoint(

+    size_t& cue_points_size,

+    long long pos) const

+{

+    assert(m_count == 0);

+

+    if (m_preload_count >= cue_points_size)

+    {

+        size_t n;

+

+        if (cue_points_size > 0)

+            n = static_cast<size_t>(2 * cue_points_size);

+        else

+        {

+            const SegmentInfo* const pInfo = m_pSegment->GetInfo();

+

+            if (pInfo == NULL)

+                n = 2048;

+            else

+            {

+                const long long ns = pInfo->GetDuration();

+

+                if (ns <= 0)

+                    n = 2048;

+                else

+                {

+                    const long long sec = (ns + 999999999LL) / 1000000000LL;

+                    n = static_cast<size_t>(sec);

+                }

+            }

+        }

+

+        CuePoint** const qq = new CuePoint*[n];

+        CuePoint** q = qq;  //beginning of target

+

+        CuePoint** p = m_cue_points;                //beginning of source

+        CuePoint** const pp = p + m_preload_count;  //end of source

+

+        while (p != pp)

+            *q++ = *p++;

+

+        delete[] m_cue_points;

+

+        m_cue_points = qq;

+        cue_points_size = n;

+    }

+

+    CuePoint* const pCP = new CuePoint(m_preload_count, pos);

+    m_cue_points[m_preload_count++] = pCP;

+}

+

+

+bool Cues::LoadCuePoint() const

+{

+    //odbgstream os;

+    //os << "Cues::LoadCuePoint" << endl;

+

+    const long long stop = m_start + m_size;

+

+    if (m_pos >= stop)

+        return false;  //nothing else to do

+

+    Init();

+

+    IMkvReader* const pReader = m_pSegment->m_pReader;

+

+    while (m_pos < stop)

+    {

+        const long long idpos = m_pos;

+

+        long len;

+

+        const long long id = ReadUInt(pReader, m_pos, len);

+        assert(id >= 0);  //TODO

+        assert((m_pos + len) <= stop);

+

+        m_pos += len;  //consume ID

+

+        const long long size = ReadUInt(pReader, m_pos, len);

+        assert(size >= 0);

+        assert((m_pos + len) <= stop);

+

+        m_pos += len;  //consume Size field

+        assert((m_pos + size) <= stop);

+

+        if (id != 0x3B)  //CuePoint ID

+        {

+            m_pos += size;  //consume payload

+            assert(m_pos <= stop);

+

+            continue;

+        }

+

+        assert(m_preload_count > 0);

+

+        CuePoint* const pCP = m_cue_points[m_count];

+        assert(pCP);

+        assert((pCP->GetTimeCode() >= 0) || (-pCP->GetTimeCode() == idpos));

+

+        pCP->Load(pReader);

+        ++m_count;

+        --m_preload_count;

+

+        m_pos += size;  //consume payload

+        assert(m_pos <= stop);

+

+        break;

+    }

+

+    return (m_pos < stop);

+}

+

+

+bool Cues::Find(

+    long long time_ns,

+    const Track* pTrack,

+    const CuePoint*& pCP,

+    const CuePoint::TrackPosition*& pTP) const

+{

+    assert(time_ns >= 0);

+    assert(pTrack);

+

+    LoadCuePoint();

+

+    assert(m_cue_points);

+    assert(m_count > 0);

+

+    CuePoint** const ii = m_cue_points;

+    CuePoint** i = ii;

+

+    CuePoint** const jj = ii + m_count + m_preload_count;

+    CuePoint** j = jj;

+

+    pCP = *i;

+    assert(pCP);

+

+    if (time_ns <= pCP->GetTime(m_pSegment))

+    {

+        pTP = pCP->Find(pTrack);

+        return (pTP != NULL);

+    }

+

+    IMkvReader* const pReader = m_pSegment->m_pReader;

+

+    while (i < j)

+    {

+        //INVARIANT:

+        //[ii, i) <= time_ns

+        //[i, j)  ?

+        //[j, jj) > time_ns

+

+        CuePoint** const k = i + (j - i) / 2;

+        assert(k < jj);

+

+        CuePoint* const pCP = *k;

+        assert(pCP);

+

+        pCP->Load(pReader);

+

+        const long long t = pCP->GetTime(m_pSegment);

+

+        if (t <= time_ns)

+            i = k + 1;

+        else

+            j = k;

+

+        assert(i <= j);

+    }

+

+    assert(i == j);

+    assert(i <= jj);

+    assert(i > ii);

+

+    pCP = *--i;

+    assert(pCP);

+    assert(pCP->GetTime(m_pSegment) <= time_ns);

+

+    //TODO: here and elsewhere, it's probably not correct to search

+    //for the cue point with this time, and then search for a matching

+    //track.  In principle, the matching track could be on some earlier

+    //cue point, and with our current algorithm, we'd miss it.  To make

+    //this bullet-proof, we'd need to create a secondary structure,

+    //with a list of cue points that apply to a track, and then search

+    //that track-based structure for a matching cue point.

+

+    pTP = pCP->Find(pTrack);

+    return (pTP != NULL);

+}

+

+

+#if 0

+bool Cues::FindNext(

+    long long time_ns,

+    const Track* pTrack,

+    const CuePoint*& pCP,

+    const CuePoint::TrackPosition*& pTP) const

+{

+    pCP = 0;

+    pTP = 0;

+

+    if (m_count == 0)

+        return false;

+

+    assert(m_cue_points);

+

+    const CuePoint* const* const ii = m_cue_points;

+    const CuePoint* const* i = ii;

+

+    const CuePoint* const* const jj = ii + m_count;

+    const CuePoint* const* j = jj;

+

+    while (i < j)

+    {

+        //INVARIANT:

+        //[ii, i) <= time_ns

+        //[i, j)  ?

+        //[j, jj) > time_ns

+

+        const CuePoint* const* const k = i + (j - i) / 2;

+        assert(k < jj);

+

+        pCP = *k;

+        assert(pCP);

+

+        const long long t = pCP->GetTime(m_pSegment);

+

+        if (t <= time_ns)

+            i = k + 1;

+        else

+            j = k;

+

+        assert(i <= j);

+    }

+

+    assert(i == j);

+    assert(i <= jj);

+

+    if (i >= jj)  //time_ns is greater than max cue point

+        return false;

+

+    pCP = *i;

+    assert(pCP);

+    assert(pCP->GetTime(m_pSegment) > time_ns);

+

+    pTP = pCP->Find(pTrack);

+    return (pTP != NULL);

+}

+#endif

+

+

+const CuePoint* Cues::GetFirst() const

+{

+    LoadCuePoint();  //init cues

+

+    const size_t count = m_count + m_preload_count;

+

+    if (count == 0)  //weird

+        return NULL;

+

+    CuePoint* const* const pp = m_cue_points;

+    assert(pp);

+

+    CuePoint* const pCP = pp[0];

+    assert(pCP);

+    assert(pCP->GetTimeCode() >= 0);

+

+    return pCP;

+}

+

+

+const CuePoint* Cues::GetLast() const

+{

+    LoadCuePoint();  //init cues

+

+    const size_t count = m_count + m_preload_count;

+

+    if (count == 0)  //weird

+        return NULL;

+

+    const size_t index = count - 1;

+

+    CuePoint* const* const pp = m_cue_points;

+    assert(pp);

+

+    CuePoint* const pCP = pp[index];

+    assert(pCP);

+

+    pCP->Load(m_pSegment->m_pReader);

+    assert(pCP->GetTimeCode() >= 0);

+

+    return pCP;

+}

+

+

+const CuePoint* Cues::GetNext(const CuePoint* pCurr) const

+{

+    if (pCurr == NULL)

+        return NULL;

+

+    assert(pCurr->GetTimeCode() >= 0);

+    assert(m_cue_points);

+    assert(m_count >= 1);

+

+    const size_t count = m_count + m_preload_count;

+

+    size_t index = pCurr->m_index;

+    assert(index < count);

+

+    CuePoint* const* const pp = m_cue_points;

+    assert(pp);

+    assert(pp[index] == pCurr);

+

+    ++index;

+

+    if (index >= count)

+        return NULL;

+

+    CuePoint* const pNext = pp[index];

+    assert(pNext);

+

+    pNext->Load(m_pSegment->m_pReader);

+

+    return pNext;

+}

+

+

+const BlockEntry* Cues::GetBlock(

+    const CuePoint* pCP,

+    const CuePoint::TrackPosition* pTP) const

+{

+    if (pCP == NULL)

+        return NULL;

+

+    if (pTP == NULL)

+        return NULL;

+

+    return m_pSegment->GetBlock(*pCP, *pTP);

+}

+

+

+const BlockEntry* Segment::GetBlock(

+    const CuePoint& cp,

+    const CuePoint::TrackPosition& tp)

+{

+    Cluster** const ii = m_clusters;

+    Cluster** i = ii;

+

+    const long count = m_clusterCount + m_clusterPreloadCount;

+

+    Cluster** const jj = ii + count;

+    Cluster** j = jj;

+

+    while (i < j)

+    {

+        //INVARIANT:

+        //[ii, i) < pTP->m_pos

+        //[i, j) ?

+        //[j, jj)  > pTP->m_pos

+

+        Cluster** const k = i + (j - i) / 2;

+        assert(k < jj);

+

+        Cluster* const pCluster = *k;

+        assert(pCluster);

+

+        const long long pos_ = pCluster->m_pos;

+        assert(pos_);

+

+        const long long pos = pos_ * ((pos_ < 0) ? -1 : 1);

+

+        if (pos < tp.m_pos)

+            i = k + 1;

+        else if (pos > tp.m_pos)

+            j = k;

+        else

+            return pCluster->GetEntry(cp, tp);

+    }

+

+    assert(i == j);

+

+    Cluster* const pCluster = Cluster::Parse(this, -1, tp.m_pos);

+    const ptrdiff_t idx = i - m_clusters;

+

+    PreloadCluster(pCluster, idx);

+    assert(m_clusters);

+    assert(m_clusterPreloadCount > 0);

+    assert(m_clusters[idx] == pCluster);

+

+    return pCluster->GetEntry(cp, tp);

+}

+

+

+

+CuePoint::CuePoint(size_t idx, long long pos) :

+    m_index(idx),

+    m_timecode(-1 * pos),

+    m_track_positions(NULL),

+    m_track_positions_count(0)

+{

+    assert(pos > 0);

+}

+

+

+CuePoint::~CuePoint()

+{

+    delete[] m_track_positions;

+}

+

+

+void CuePoint::Load(IMkvReader* pReader)

+{

+    //odbgstream os;

+    //os << "CuePoint::Load(begin): timecode=" << m_timecode << endl;

+

+    if (m_timecode >= 0)  //already loaded

+        return;

+

+    assert(m_track_positions == NULL);

+    assert(m_track_positions_count == 0);

+

+    long long pos_ = -m_timecode;

+

+    long long stop;

+

+    {

+        long len;

+

+        const long long id = ReadUInt(pReader, pos_, len);

+        assert(id == 0x3B);  //CuePoint ID

+        //assert((pos + len) <= stop);

+

+        pos_ += len;  //consume ID

+

+        const long long size = ReadUInt(pReader, pos_, len);

+        assert(size >= 0);

+        //assert((pos + len) <= stop);

+

+        pos_ += len;  //consume Size field

+        //assert((pos + size) <= stop);

+

+        //pos_ now points to start of payload

+

+        stop = pos_ + size;

+    }

+

+    long long pos = pos_;

+

+    //First count number of track positions

+

+    while (pos < stop)

+    {

+        long len;

+

+        const long long id = ReadUInt(pReader, pos, len);

+        assert(id >= 0);  //TODO

+        assert((pos + len) <= stop);

+

+        pos += len;  //consume ID

+

+        const long long size = ReadUInt(pReader, pos, len);

+        assert(size >= 0);

+        assert((pos + len) <= stop);

+

+        pos += len;  //consume Size field

+        assert((pos + size) <= stop);

+

+        if (id == 0x33)  //CueTime ID

+            m_timecode = UnserializeUInt(pReader, pos, size);

+

+        else if (id == 0x37) //CueTrackPosition(s) ID

+            ++m_track_positions_count;

+

+        pos += size;  //consume payload

+        assert(pos <= stop);

+    }

+

+    assert(m_timecode >= 0);

+    assert(m_track_positions_count > 0);

+

+    //os << "CuePoint::Load(cont'd): idpos=" << idpos

+    //   << " timecode=" << m_timecode

+    //   << endl;

+

+    m_track_positions = new TrackPosition[m_track_positions_count];

+

+    //Now parse track positions

+

+    TrackPosition* p = m_track_positions;

+    pos = pos_;

+

+    while (pos < stop)

+    {

+        long len;

+

+        const long long id = ReadUInt(pReader, pos, len);

+        assert(id >= 0);  //TODO

+        assert((pos + len) <= stop);

+

+        pos += len;  //consume ID

+

+        const long long size = ReadUInt(pReader, pos, len);

+        assert(size >= 0);

+        assert((pos + len) <= stop);

+

+        pos += len;  //consume Size field

+        assert((pos + size) <= stop);

+

+        if (id == 0x37) //CueTrackPosition(s) ID

+        {

+            TrackPosition& tp = *p++;

+            tp.Parse(pReader, pos, size);

+        }

+

+        pos += size;  //consume payload

+        assert(pos <= stop);

+    }

+

+    assert(size_t(p - m_track_positions) == m_track_positions_count);

+}

+

+

+

+void CuePoint::TrackPosition::Parse(

+    IMkvReader* pReader,

+    long long start_,

+    long long size_)

+{

+    const long long stop = start_ + size_;

+    long long pos = start_;

+

+    m_track = -1;

+    m_pos = -1;

+    m_block = 1;  //default

+

+    while (pos < stop)

+    {

+        long len;

+

+        const long long id = ReadUInt(pReader, pos, len);

+        assert(id >= 0);  //TODO

+        assert((pos + len) <= stop);

+

+        pos += len;  //consume ID

+

+        const long long size = ReadUInt(pReader, pos, len);

+        assert(size >= 0);

+        assert((pos + len) <= stop);

+

+        pos += len;  //consume Size field

+        assert((pos + size) <= stop);

+

+        if (id == 0x77)  //CueTrack ID

+            m_track = UnserializeUInt(pReader, pos, size);

+

+        else if (id == 0x71)  //CueClusterPos ID

+            m_pos = UnserializeUInt(pReader, pos, size);

+

+        else if (id == 0x1378)  //CueBlockNumber

+            m_block = UnserializeUInt(pReader, pos, size);

+

+        pos += size;  //consume payload

+        assert(pos <= stop);

+    }

+

+    assert(m_pos >= 0);

+    //assert(m_track > 0);

+    //assert(m_block > 0);

+}

+

+

+const CuePoint::TrackPosition* CuePoint::Find(const Track* pTrack) const

+{

+    assert(pTrack);

+

+    const long long n = pTrack->GetNumber();

+

+    const TrackPosition* i = m_track_positions;

+    const TrackPosition* const j = i + m_track_positions_count;

+

+    while (i != j)

+    {

+        const TrackPosition& p = *i++;

+

+        if (p.m_track == n)

+            return &p;

+    }

+

+    return NULL;  //no matching track number found

+}

+

+

+long long CuePoint::GetTimeCode() const

+{

+    return m_timecode;

+}

+

+long long CuePoint::GetTime(Segment* pSegment) const

+{

+    assert(pSegment);

+    assert(m_timecode >= 0);

+

+    const SegmentInfo* const pInfo = pSegment->GetInfo();

+    assert(pInfo);

+

+    const long long scale = pInfo->GetTimeCodeScale();

+    assert(scale >= 1);

+

+    const long long time = scale * m_timecode;

+

+    return time;

+}

+

+

+long long Segment::Unparsed() const

+{

+    const long long stop = m_start + m_size;

+

+    const long long result = stop - m_pos;

+    assert(result >= 0);

+

+    return result;

+}

+

+

+Cluster* Segment::GetFirst()

+{

+    if ((m_clusters == NULL) || (m_clusterCount <= 0))

+       return &m_eos;

+

+    Cluster* const pCluster = m_clusters[0];

+    assert(pCluster);

+

+    return pCluster;

+}

+

+

+Cluster* Segment::GetLast()

+{

+    if ((m_clusters == NULL) || (m_clusterCount <= 0))

+        return &m_eos;

+

+    const long idx = m_clusterCount - 1;

+

+    Cluster* const pCluster = m_clusters[idx];

+    assert(pCluster);

+

+    return pCluster;

+}

+

+

+unsigned long Segment::GetCount() const

+{

+    return m_clusterCount;

+}

+

+

+Cluster* Segment::GetNext(const Cluster* pCurr)

+{

+    assert(pCurr);

+    assert(pCurr != &m_eos);

+    assert(m_clusters);

+

+    long idx =  pCurr->m_index;

+

+    if (idx >= 0)

+    {

+        assert(m_clusterCount > 0);

+        assert(idx < m_clusterCount);

+        assert(pCurr == m_clusters[idx]);

+

+        ++idx;

+

+        if (idx >= m_clusterCount)

+            return &m_eos;  //caller will LoadCluster as desired

+

+        Cluster* const pNext = m_clusters[idx];

+        assert(pNext);

+        assert(pNext->m_index >= 0);

+        assert(pNext->m_index == idx);

+

+        return pNext;

+    }

+

+    assert(m_clusterPreloadCount > 0);

+

+    const long long off_ = pCurr->m_pos;

+    const long long off = off_ * ((off_ < 0) ? -1 : 1);

+

+    long long pos = m_start + off;

+    const long long stop = m_start + m_size;  //end of segment

+

+    {

+        long len;

+

+        long long result = GetUIntLength(m_pReader, pos, len);

+        assert(result == 0);  //TODO

+        assert((pos + len) <= stop);  //TODO

+

+        const long long id = ReadUInt(m_pReader, pos, len);

+        assert(id == 0x0F43B675);  //Cluster ID   //TODO

+

+        pos += len;  //consume ID

+

+        //Read Size

+        result = GetUIntLength(m_pReader, pos, len);

+        assert(result == 0);  //TODO

+        assert((pos + len) <= stop);  //TODO

+

+        const long long size = ReadUInt(m_pReader, pos, len);

+        assert(size > 0);  //TODO

+        assert((pCurr->m_size <= 0) || (pCurr->m_size == size));

+

+        pos += len;  //consume length of size of element

+        assert((pos + size) <= stop);  //TODO

+

+        //Pos now points to start of payload

+

+        pos += size;  //consume payload

+    }

+

+    long long off_next = 0;

+

+    while (pos < stop)

+    {

+        long len;

+

+        long long result = GetUIntLength(m_pReader, pos, len);

+        assert(result == 0);  //TODO

+        assert((pos + len) <= stop);  //TODO

+

+        const long long idpos = pos;  //pos of next (potential) cluster

+

+        const long long id = ReadUInt(m_pReader, idpos, len);

+        assert(id > 0);  //TODO

+

+        pos += len;  //consume ID

+

+        //Read Size

+        result = GetUIntLength(m_pReader, pos, len);

+        assert(result == 0);  //TODO

+        assert((pos + len) <= stop);  //TODO

+

+        const long long size = ReadUInt(m_pReader, pos, len);

+        assert(size >= 0);  //TODO

+

+        pos += len;  //consume length of size of element

+        assert((pos + size) <= stop);  //TODO

+

+        //Pos now points to start of payload

+

+        if (size == 0)  //weird

+            continue;

+

+        if (id == 0x0F43B675)  //Cluster ID

+        {

+            off_next = idpos - m_start;

+            break;

+        }

+

+        pos += size;  //consume payload

+    }

+

+    if (off_next <= 0)

+        return 0;

+

+    Cluster** const ii = m_clusters + m_clusterCount;

+    Cluster** i = ii;

+

+    Cluster** const jj = ii + m_clusterPreloadCount;

+    Cluster** j = jj;

+

+    while (i < j)

+    {

+        //INVARIANT:

+        //[0, i) < pos_next

+        //[i, j) ?

+        //[j, jj)  > pos_next

+

+        Cluster** const k = i + (j - i) / 2;

+        assert(k < jj);

+

+        Cluster* const pNext = *k;

+        assert(pNext);

+        assert(pNext->m_index < 0);

+

+        const long long pos_ = pNext->m_pos;

+        assert(pos_);

+

+        pos = pos_ * ((pos_ < 0) ? -1 : 1);

+

+        if (pos < off_next)

+            i = k + 1;

+        else if (pos > off_next)

+            j = k;

+        else

+            return pNext;

+    }

+

+    assert(i == j);

+

+    Cluster* const pNext = Cluster::Parse(this, -1, off_next);

+    const ptrdiff_t idx_next = i - m_clusters;  //insertion position

+

+    PreloadCluster(pNext, idx_next);

+    assert(m_clusters);

+    assert(idx_next < m_clusterSize);

+    assert(m_clusters[idx_next] == pNext);

+

+    return pNext;

+}

+

+

+Cluster* Segment::FindCluster(long long time_ns)

+{

+    if ((m_clusters == NULL) || (m_clusterCount <= 0))

+        return &m_eos;

+

+    {

+        Cluster* const pCluster = m_clusters[0];

+        assert(pCluster);

+        assert(pCluster->m_index == 0);

+

+        if (time_ns <= pCluster->GetTime())

+            return pCluster;

+    }

+

+    //Binary search of cluster array

+

+    long i = 0;

+    long j = m_clusterCount;

+

+    while (i < j)

+    {

+        //INVARIANT:

+        //[0, i) <= time_ns

+        //[i, j) ?

+        //[j, m_clusterCount)  > time_ns

+

+        const long k = i + (j - i) / 2;

+        assert(k < m_clusterCount);

+

+        Cluster* const pCluster = m_clusters[k];

+        assert(pCluster);

+        assert(pCluster->m_index == k);

+

+        const long long t = pCluster->GetTime();

+

+        if (t <= time_ns)

+            i = k + 1;

+        else

+            j = k;

+

+        assert(i <= j);

+    }

+

+    assert(i == j);

+    assert(i > 0);

+    assert(i <= m_clusterCount);

+

+    const long k = i - 1;

+

+    Cluster* const pCluster = m_clusters[k];

+    assert(pCluster);

+    assert(pCluster->m_index == k);

+    assert(pCluster->GetTime() <= time_ns);

+

+    return pCluster;

+}

+

+

+const BlockEntry* Segment::Seek(

+    long long time_ns,

+    const Track* pTrack)

+{

+    assert(pTrack);

+

+    if ((m_clusters == NULL) || (m_clusterCount <= 0))

+        return pTrack->GetEOS();

+

+    Cluster** const i = m_clusters;

+    assert(i);

+

+    {

+        Cluster* const pCluster = *i;

+        assert(pCluster);

+        assert(pCluster->m_index == 0);  //m_clusterCount > 0

+        assert(pCluster->m_pSegment == this);

+

+        if (time_ns <= pCluster->GetTime())

+            return pCluster->GetEntry(pTrack);

+    }

+

+    Cluster** const j = i + m_clusterCount;

+

+    if (pTrack->GetType() == 2)  //audio

+    {

+        //TODO: we could decide to use cues for this, as we do for video.

+        //But we only use it for video because looking around for a keyframe

+        //can get expensive.  Audio doesn't require anything special so a

+        //straight cluster search is good enough (we assume).

+

+        Cluster** lo = i;

+        Cluster** hi = j;

+

+        while (lo < hi)

+        {

+            //INVARIANT:

+            //[i, lo) <= time_ns

+            //[lo, hi) ?

+            //[hi, j)  > time_ns

+

+            Cluster** const mid = lo + (hi - lo) / 2;

+            assert(mid < hi);

+

+            Cluster* const pCluster = *mid;

+            assert(pCluster);

+            assert(pCluster->m_index == long(mid - m_clusters));

+            assert(pCluster->m_pSegment == this);

+

+            const long long t = pCluster->GetTime();

+

+            if (t <= time_ns)

+                lo = mid + 1;

+            else

+                hi = mid;

+

+            assert(lo <= hi);

+        }

+

+        assert(lo == hi);

+        assert(lo > i);

+        assert(lo <= j);

+

+        Cluster* const pCluster = *--lo;

+        assert(pCluster);

+        assert(pCluster->GetTime() <= time_ns);

+

+        return pCluster->GetEntry(pTrack);

+    }

+

+    assert(pTrack->GetType() == 1);  //video

+

+    Cluster** lo = i;

+    Cluster** hi = j;

+

+    while (lo < hi)

+    {

+        //INVARIANT:

+        //[i, lo) <= time_ns

+        //[lo, hi) ?

+        //[hi, j)  > time_ns

+

+        Cluster** const mid = lo + (hi - lo) / 2;

+        assert(mid < hi);

+

+        Cluster* const pCluster = *mid;

+        assert(pCluster);

+

+        const long long t = pCluster->GetTime();

+

+        if (t <= time_ns)

+            lo = mid + 1;

+        else

+            hi = mid;

+

+        assert(lo <= hi);

+    }

+

+    assert(lo == hi);

+    assert(lo > i);

+    assert(lo <= j);

+

+    Cluster* pCluster = *--lo;

+    assert(pCluster);

+    assert(pCluster->GetTime() <= time_ns);

+

+    {

+        const BlockEntry* const pBlockEntry = pCluster->GetEntry(pTrack);

+        assert(pBlockEntry);

+

+        if (!pBlockEntry->EOS())  //found a keyframe

+        {

+            const Block* const pBlock = pBlockEntry->GetBlock();

+            assert(pBlock);

+

+            //TODO: this isn't necessarily the keyframe we want,

+            //since there might another keyframe on this same

+            //cluster with a greater timecode that but that is

+            //still less than the requested time.  For now we

+            //simply return the first keyframe we find.

+

+            if (pBlock->GetTime(pCluster) <= time_ns)

+                return pBlockEntry;

+        }

+    }

+

+    const VideoTrack* const pVideo = static_cast<const VideoTrack*>(pTrack);

+

+    while (lo != i)

+    {

+        pCluster = *--lo;

+        assert(pCluster);

+        assert(pCluster->GetTime() <= time_ns);

+

+        const BlockEntry* const pBlockEntry = pCluster->GetMaxKey(pVideo);

+        assert(pBlockEntry);

+

+        if (!pBlockEntry->EOS())

+            return pBlockEntry;

+    }

+

+    //weird: we're on the first cluster, but no keyframe found

+    //should never happen but we must return something anyway

+

+    return pTrack->GetEOS();

+}

+

+

+#if 0

+bool Segment::SearchCues(

+    long long time_ns,

+    Track* pTrack,

+    Cluster*& pCluster,

+    const BlockEntry*& pBlockEntry,

+    const CuePoint*& pCP,

+    const CuePoint::TrackPosition*& pTP)

+{

+    if (pTrack->GetType() != 1)  //not video

+        return false;  //TODO: for now, just handle video stream

+

+    if (m_pCues == NULL)

+        return false;

+

+    if (!m_pCues->Find(time_ns, pTrack, pCP, pTP))

+        return false;  //weird

+

+    assert(pCP);

+    assert(pTP);

+    assert(pTP->m_track == pTrack->GetNumber());

+

+    //We have the cue point and track position we want,

+    //so we now need to search for the cluster having

+    //the indicated position.

+

+    return GetCluster(pCP, pTP, pCluster, pBlockEntry);

+}

+#endif

+

+

+Tracks* Segment::GetTracks() const

+{

+    return m_pTracks;

+}

+

+

+const SegmentInfo* Segment::GetInfo() const

+{

+    return m_pInfo;

+}

+

+

+const Cues* Segment::GetCues() const

+{

+    return m_pCues;

+}

+

+

+long long Segment::GetDuration() const

+{

+    assert(m_pInfo);

+    return m_pInfo->GetDuration();

+}

+

+

+SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_) :

+    m_pSegment(pSegment),

+    m_start(start),

+    m_size(size_),

+    m_pMuxingAppAsUTF8(NULL),

+    m_pWritingAppAsUTF8(NULL),

+    m_pTitleAsUTF8(NULL)

+{

+    IMkvReader* const pReader = m_pSegment->m_pReader;

+

+    long long pos = start;

+    const long long stop = start + size_;

+

+    m_timecodeScale = 1000000;

+    m_duration = -1;

+

+    while (pos < stop)

+    {

+        if (Match(pReader, pos, 0x0AD7B1, m_timecodeScale))

+            assert(m_timecodeScale > 0);

+

+        else if (Match(pReader, pos, 0x0489, m_duration))

+            assert(m_duration >= 0);

+

+        else if (Match(pReader, pos, 0x0D80, m_pMuxingAppAsUTF8))   //[4D][80]

+            assert(m_pMuxingAppAsUTF8);

+

+        else if (Match(pReader, pos, 0x1741, m_pWritingAppAsUTF8))  //[57][41]

+            assert(m_pWritingAppAsUTF8);

+

+        else if (Match(pReader, pos, 0x3BA9, m_pTitleAsUTF8))       //[7B][A9]

+            assert(m_pTitleAsUTF8);

+

+        else

+        {

+            long len;

+

+            const long long id = ReadUInt(pReader, pos, len);

+            //id;

+            assert(id >= 0);

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume id

+            assert((stop - pos) > 0);

+

+            const long long size = ReadUInt(pReader, pos, len);

+            assert(size >= 0);

+            assert((pos + len) <= stop);

+

+            pos += len + size;  //consume size and payload

+            assert(pos <= stop);

+        }

+    }

+

+    assert(pos == stop);

+}

+

+SegmentInfo::~SegmentInfo()

+{

+    if (m_pMuxingAppAsUTF8)

+    {

+        delete[] m_pMuxingAppAsUTF8;

+        m_pMuxingAppAsUTF8 = NULL;

+    }

+

+    if (m_pWritingAppAsUTF8)

+    {

+        delete[] m_pWritingAppAsUTF8;

+        m_pWritingAppAsUTF8 = NULL;

+    }

+

+    if (m_pTitleAsUTF8)

+    {

+        delete[] m_pTitleAsUTF8;

+        m_pTitleAsUTF8 = NULL;

+    }

+}

+

+long long SegmentInfo::GetTimeCodeScale() const

+{

+    return m_timecodeScale;

+}

+

+

+long long SegmentInfo::GetDuration() const

+{

+    if (m_duration < 0)

+        return -1;

+

+    assert(m_timecodeScale >= 1);

+

+    const double dd = double(m_duration) * double(m_timecodeScale);

+    const long long d = static_cast<long long>(dd);

+

+    return d;

+}

+

+const char* SegmentInfo::GetMuxingAppAsUTF8() const

+{

+    return m_pMuxingAppAsUTF8;

+}

+

+

+const char* SegmentInfo::GetWritingAppAsUTF8() const

+{

+    return m_pWritingAppAsUTF8;

+}

+

+const char* SegmentInfo::GetTitleAsUTF8() const

+{

+    return m_pTitleAsUTF8;

+}

+

+Track::Track(Segment* pSegment, const Info& i) :

+    m_pSegment(pSegment),

+    m_info(i)

+{

+}

+

+Track::~Track()

+{

+    Info& info = const_cast<Info&>(m_info);

+    info.Clear();

+}

+

+Track::Info::Info():

+    type(-1),

+    number(-1),

+    uid(-1),

+    nameAsUTF8(NULL),

+    codecId(NULL),

+    codecPrivate(NULL),

+    codecPrivateSize(0),

+    codecNameAsUTF8(NULL)

+{

+}

+

+

+void Track::Info::Clear()

+{

+    delete[] nameAsUTF8;

+    nameAsUTF8 = NULL;

+

+    delete[] codecId;

+    codecId = NULL;

+

+    delete[] codecPrivate;

+    codecPrivate = NULL;

+

+    codecPrivateSize = 0;

+

+    delete[] codecNameAsUTF8;

+    codecNameAsUTF8 = NULL;

+}

+

+const BlockEntry* Track::GetEOS() const

+{

+    return &m_eos;

+}

+

+long long Track::GetType() const

+{

+    return m_info.type;

+}

+

+long long Track::GetNumber() const

+{

+    return m_info.number;

+}

+

+const char* Track::GetNameAsUTF8() const

+{

+    return m_info.nameAsUTF8;

+}

+

+const char* Track::GetCodecNameAsUTF8() const

+{

+    return m_info.codecNameAsUTF8;

+}

+

+

+const char* Track::GetCodecId() const

+{

+    return m_info.codecId;

+}

+

+const unsigned char* Track::GetCodecPrivate(size_t& size) const

+{

+    size = m_info.codecPrivateSize;

+    return m_info.codecPrivate;

+}

+

+

+long Track::GetFirst(const BlockEntry*& pBlockEntry) const

+{

+    Cluster* pCluster = m_pSegment->GetFirst();

+

+    //If Segment::GetFirst returns NULL, then this must be a network

+    //download, and we haven't loaded any clusters yet.  In this case,

+    //returning NULL from Track::GetFirst means the same thing.

+

+    for (int i = 0; i < 100; ++i)  //arbitrary upper bound

+    {

+        if (pCluster == NULL)

+        {

+            pBlockEntry = GetEOS();

+            return 1;

+        }

+

+        if (pCluster->EOS())

+        {

+            if (m_pSegment->Unparsed() <= 0)  //all clusters have been loaded

+            {

+                pBlockEntry = GetEOS();

+                return 1;

+            }

+

+            pBlockEntry = 0;

+            return E_BUFFER_NOT_FULL;

+        }

+

+        pBlockEntry = pCluster->GetFirst();

+

+        while (pBlockEntry)

+        {

+            const Block* const pBlock = pBlockEntry->GetBlock();

+            assert(pBlock);

+

+            if (pBlock->GetTrackNumber() == m_info.number)

+                return 0;

+

+            pBlockEntry = pCluster->GetNext(pBlockEntry);

+        }

+

+        pCluster = m_pSegment->GetNext(pCluster);

+    }

+

+    //NOTE: if we get here, it means that we didn't find a block with

+    //a matching track number.  We interpret that as an error (which

+    //might be too conservative).

+

+    pBlockEntry = GetEOS();  //so we can return a non-NULL value

+    return 1;

+}

+

+

+long Track::GetNext(

+    const BlockEntry* pCurrEntry,

+    const BlockEntry*& pNextEntry) const

+{

+    assert(pCurrEntry);

+    assert(!pCurrEntry->EOS());  //?

+

+    const Block* const pCurrBlock = pCurrEntry->GetBlock();

+    assert(pCurrBlock->GetTrackNumber() == m_info.number);

+

+    Cluster* pCluster = pCurrEntry->GetCluster();

+    assert(pCluster);

+    assert(!pCluster->EOS());

+

+    pNextEntry = pCluster->GetNext(pCurrEntry);

+

+    for (int i = 0; i < 100; ++i)  //arbitrary upper bound to search

+    {

+        while (pNextEntry)

+        {

+            const Block* const pNextBlock = pNextEntry->GetBlock();

+            assert(pNextBlock);

+

+            if (pNextBlock->GetTrackNumber() == m_info.number)

+                return 0;

+

+            pNextEntry = pCluster->GetNext(pNextEntry);

+        }

+

+        pCluster = m_pSegment->GetNext(pCluster);

+

+        if (pCluster == NULL)

+        {

+            pNextEntry = GetEOS();

+            return 1;

+        }

+

+        if (pCluster->EOS())

+        {

+            if (m_pSegment->Unparsed() <= 0)   //all clusters have been loaded

+            {

+                pNextEntry = GetEOS();

+                return 1;

+            }

+

+            //TODO: there is a potential O(n^2) problem here: we tell the

+            //caller to (pre)load another cluster, which he does, but then he

+            //calls GetNext again, which repeats the same search.  This is

+            //a pathological case, since the only way it can happen is if

+            //there exists a long sequence of clusters none of which contain a

+            // block from this track.  One way around this problem is for the

+            //caller to be smarter when he loads another cluster: don't call

+            //us back until you have a cluster that contains a block from this

+            //track. (Of course, that's not cheap either, since our caller

+            //would have to scan the each cluster as it's loaded, so that

+            //would just push back the problem.)

+

+            pNextEntry = NULL;

+            return E_BUFFER_NOT_FULL;

+        }

+

+        pNextEntry = pCluster->GetFirst();

+    }

+

+    //NOTE: if we get here, it means that we didn't find a block with

+    //a matching track number after lots of searching, so we give

+    //up trying.

+

+    pNextEntry = GetEOS();  //so we can return a non-NULL value

+    return 1;

+}

+

+

+Track::EOSBlock::EOSBlock()

+{

+}

+

+

+bool Track::EOSBlock::EOS() const

+{

+    return true;

+}

+

+

+Cluster* Track::EOSBlock::GetCluster() const

+{

+    return NULL;

+}

+

+

+size_t Track::EOSBlock::GetIndex() const

+{

+    return 0;

+}

+

+

+const Block* Track::EOSBlock::GetBlock() const

+{

+    return NULL;

+}

+

+

+bool Track::EOSBlock::IsBFrame() const

+{

+    return false;

+}

+

+

+VideoTrack::VideoTrack(Segment* pSegment, const Info& i) :

+    Track(pSegment, i),

+    m_width(-1),

+    m_height(-1),

+    m_rate(-1)

+{

+    assert(i.type == 1);

+    assert(i.number > 0);

+

+    IMkvReader* const pReader = pSegment->m_pReader;

+

+    const Settings& s = i.settings;

+    assert(s.start >= 0);

+    assert(s.size >= 0);

+

+    long long pos = s.start;

+    assert(pos >= 0);

+

+    const long long stop = pos + s.size;

+

+    while (pos < stop)

+    {

+#ifdef _DEBUG

+        long len;

+        const long long id = ReadUInt(pReader, pos, len);

+        assert(id >= 0);  //TODO: handle error case

+        assert((pos + len) <= stop);

+#endif

+        if (Match(pReader, pos, 0x30, m_width))

+            ;

+        else if (Match(pReader, pos, 0x3A, m_height))

+            ;

+        else if (Match(pReader, pos, 0x0383E3, m_rate))

+            ;

+        else

+        {

+            long len;

+            const long long id = ReadUInt(pReader, pos, len);

+            assert(id >= 0);  //TODO: handle error case

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume id

+

+            const long long size = ReadUInt(pReader, pos, len);

+            assert(size >= 0);  //TODO: handle error case

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume length of size

+            assert((pos + size) <= stop);

+

+            //pos now designates start of payload

+

+            pos += size;  //consume payload

+            assert(pos <= stop);

+        }

+    }

+

+    return;

+}

+

+

+bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const

+{

+    assert(pBlockEntry);

+

+    const Block* const pBlock = pBlockEntry->GetBlock();

+    assert(pBlock);

+    assert(pBlock->GetTrackNumber() == m_info.number);

+

+    return pBlock->IsKey();

+}

+

+

+long long VideoTrack::GetWidth() const

+{

+    return m_width;

+}

+

+

+long long VideoTrack::GetHeight() const

+{

+    return m_height;

+}

+

+

+double VideoTrack::GetFrameRate() const

+{

+    return m_rate;

+}

+

+

+AudioTrack::AudioTrack(Segment* pSegment, const Info& i) :

+    Track(pSegment, i),

+    m_rate(0.0),

+    m_channels(0),

+    m_bitDepth(-1)

+{

+    assert(i.type == 2);

+    assert(i.number > 0);

+

+    IMkvReader* const pReader = pSegment->m_pReader;

+

+    const Settings& s = i.settings;

+    assert(s.start >= 0);

+    assert(s.size >= 0);

+

+    long long pos = s.start;

+    assert(pos >= 0);

+

+    const long long stop = pos + s.size;

+

+    while (pos < stop)

+    {

+#ifdef _DEBUG

+        long len;

+        const long long id = ReadUInt(pReader, pos, len);

+        assert(id >= 0);  //TODO: handle error case

+        assert((pos + len) <= stop);

+#endif

+        if (Match(pReader, pos, 0x35, m_rate))

+            ;

+        else if (Match(pReader, pos, 0x1F, m_channels))

+            ;

+        else if (Match(pReader, pos, 0x2264, m_bitDepth))

+            ;

+        else

+        {

+            long len;

+            const long long id = ReadUInt(pReader, pos, len);

+            assert(id >= 0);  //TODO: handle error case

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume id

+

+            const long long size = ReadUInt(pReader, pos, len);

+            assert(size >= 0);  //TODO: handle error case

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume length of size

+            assert((pos + size) <= stop);

+

+            //pos now designates start of payload

+

+            pos += size;  //consume payload

+            assert(pos <= stop);

+        }

+    }

+

+    return;

+}

+

+

+bool AudioTrack::VetEntry(const BlockEntry* pBlockEntry) const

+{

+    assert(pBlockEntry);

+

+    const Block* const pBlock = pBlockEntry->GetBlock();

+    assert(pBlock);

+    assert(pBlock->GetTrackNumber() == m_info.number);

+

+    return true;

+}

+

+

+double AudioTrack::GetSamplingRate() const

+{

+    return m_rate;

+}

+

+

+long long AudioTrack::GetChannels() const

+{

+    return m_channels;

+}

+

+long long AudioTrack::GetBitDepth() const

+{

+    return m_bitDepth;

+}

+

+Tracks::Tracks(Segment* pSegment, long long start, long long size_) :

+    m_pSegment(pSegment),

+    m_start(start),

+    m_size(size_),

+    m_trackEntries(NULL),

+    m_trackEntriesEnd(NULL)

+{

+    long long stop = m_start + m_size;

+    IMkvReader* const pReader = m_pSegment->m_pReader;

+

+    long long pos1 = m_start;

+    int count = 0;

+

+    while (pos1 < stop)

+    {

+        long len;

+        const long long id = ReadUInt(pReader, pos1, len);

+        assert(id >= 0);

+        assert((pos1 + len) <= stop);

+

+        pos1 += len;  //consume id

+

+        const long long size = ReadUInt(pReader, pos1, len);

+        assert(size >= 0);

+        assert((pos1 + len) <= stop);

+

+        pos1 += len;  //consume length of size

+

+        //pos now desinates start of element

+        if (id == 0x2E)  //TrackEntry ID

+            ++count;

+

+        pos1 += size;  //consume payload

+        assert(pos1 <= stop);

+    }

+

+    if (count <= 0)

+        return;

+

+    m_trackEntries = new Track*[count];

+    m_trackEntriesEnd = m_trackEntries;

+

+    long long pos = m_start;

+

+    while (pos < stop)

+    {

+        long len;

+        const long long id = ReadUInt(pReader, pos, len);

+        assert(id >= 0);

+        assert((pos + len) <= stop);

+

+        pos += len;  //consume id

+

+        const long long size1 = ReadUInt(pReader, pos, len);

+        assert(size1 >= 0);

+        assert((pos + len) <= stop);

+

+        pos += len;  //consume length of size

+

+        //pos now desinates start of element

+

+        if (id == 0x2E)  //TrackEntry ID

+            ParseTrackEntry(pos, size1, *m_trackEntriesEnd++);

+

+        pos += size1;  //consume payload

+        assert(pos <= stop);

+    }

+}

+

+

+unsigned long Tracks::GetTracksCount() const

+{

+    const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;

+    assert(result >= 0);

+

+    return static_cast<unsigned long>(result);

+}

+

+

+void Tracks::ParseTrackEntry(

+    long long start,

+    long long size,

+    Track*& pTrack)

+{

+    IMkvReader* const pReader = m_pSegment->m_pReader;

+

+    long long pos = start;

+    const long long stop = start + size;

+

+    Track::Info i;

+

+    Track::Settings videoSettings;

+    videoSettings.start = -1;

+

+    Track::Settings audioSettings;

+    audioSettings.start = -1;

+

+    while (pos < stop)

+    {

+#ifdef _DEBUG

+        long len;

+        const long long id = ReadUInt(pReader, pos, len);

+        len;

+        id;

+#endif

+        if (Match(pReader, pos, 0x57, i.number))

+            assert(i.number > 0);

+        else if (Match(pReader, pos, 0x33C5, i.uid))

+            ;

+        else if (Match(pReader, pos, 0x03, i.type))

+            ;

+        else if (Match(pReader, pos, 0x136E, i.nameAsUTF8))

+            assert(i.nameAsUTF8);

+        else if (Match(pReader, pos, 0x06, i.codecId))

+            ;

+        else if (Match(pReader,

+                       pos,

+                       0x23A2,

+                       i.codecPrivate,

+                       i.codecPrivateSize))

+            ;

+        else if (Match(pReader, pos, 0x058688, i.codecNameAsUTF8))

+            assert(i.codecNameAsUTF8);

+        else

+        {

+            long len;

+

+            const long long id = ReadUInt(pReader, pos, len);

+            assert(id >= 0);  //TODO: handle error case

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume id

+

+            const long long size = ReadUInt(pReader, pos, len);

+            assert(size >= 0);  //TODO: handle error case

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume length of size

+            const long long start = pos;

+

+            pos += size;  //consume payload

+            assert(pos <= stop);

+

+            if (id == 0x60)

+            {

+                videoSettings.start = start;

+                videoSettings.size = size;

+            }

+            else if (id == 0x61)

+            {

+                audioSettings.start = start;

+                audioSettings.size = size;

+            }

+        }

+    }

+

+    assert(pos == stop);

+    //TODO: propertly vet info.number, to ensure both its existence,

+    //and that it is unique among all tracks.

+    assert(i.number > 0);

+

+    //TODO: vet settings, to ensure that video settings (0x60)

+    //were specified when type = 1, and that audio settings (0x61)

+    //were specified when type = 2.

+    if (i.type == 1)  //video

+    {

+        assert(audioSettings.start < 0);

+        assert(videoSettings.start >= 0);

+

+        i.settings = videoSettings;

+

+        VideoTrack* const t = new VideoTrack(m_pSegment, i);

+        assert(t);  //TODO

+        pTrack = t;

+    }

+    else if (i.type == 2)  //audio

+    {

+        assert(videoSettings.start < 0);

+        assert(audioSettings.start >= 0);

+

+        i.settings = audioSettings;

+

+        AudioTrack* const t = new  AudioTrack(m_pSegment, i);

+        assert(t);  //TODO

+        pTrack = t;

+    }

+    else

+    {

+        // for now we do not support other track types yet.

+        // TODO: support other track types

+        i.Clear();

+

+        pTrack = NULL;

+    }

+

+    return;

+}

+

+

+Tracks::~Tracks()

+{

+    Track** i = m_trackEntries;

+    Track** const j = m_trackEntriesEnd;

+

+    while (i != j)

+    {

+        Track* const pTrack = *i++;

+        delete pTrack;

+    }

+

+    delete[] m_trackEntries;

+}

+

+

+Track* Tracks::GetTrackByNumber(unsigned long tn_) const

+{

+    const long long tn = tn_;

+

+    Track** i = m_trackEntries;

+    Track** const j = m_trackEntriesEnd;

+

+    while (i != j)

+    {

+        Track* const pTrack = *i++;

+

+        if (pTrack == NULL)

+            continue;

+

+        if (tn == pTrack->GetNumber())

+            return pTrack;

+    }

+

+    return NULL;  //not found

+}

+

+

+Track* Tracks::GetTrackByIndex(unsigned long idx) const

+{

+    const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;

+

+    if (idx >= static_cast<unsigned long>(count))

+         return NULL;

+

+    return m_trackEntries[idx];

+}

+

+

+void Cluster::Load()

+{

+    assert(m_pSegment);

+    assert(m_pos);

+    assert(m_size);

+

+    if (m_pos > 0)  //loaded

+    {

+        assert(m_size > 0);

+        assert(m_timecode >= 0);

+        return;

+    }

+

+    assert(m_pos < 0);  //not loaded yet

+    assert(m_size < 0);

+    assert(m_timecode < 0);

+

+    IMkvReader* const pReader = m_pSegment->m_pReader;

+

+    m_pos *= -1;                                  //relative to segment

+    long long pos = m_pSegment->m_start + m_pos;  //absolute

+

+    long len;

+

+    const long long id_ = ReadUInt(pReader, pos, len);

+    assert(id_ >= 0);

+    assert(id_ == 0x0F43B675);  //Cluster ID

+

+    pos += len;  //consume id

+

+    const long long size_ = ReadUInt(pReader, pos, len);

+    assert(size_ >= 0);

+

+    pos += len;  //consume size

+

+    m_size = size_;

+    const long long stop = pos + size_;

+

+    long long timecode = -1;

+

+    while (pos < stop)

+    {

+        if (Match(pReader, pos, 0x67, timecode))

+            break;

+        else

+        {

+            const long long id = ReadUInt(pReader, pos, len);

+            assert(id >= 0);  //TODO

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume id

+

+            const long long size = ReadUInt(pReader, pos, len);

+            assert(size >= 0);  //TODO

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume size

+

+            if (id == 0x20)  //BlockGroup ID

+                break;

+

+            if (id == 0x23)  //SimpleBlock ID

+                break;

+

+            pos += size;  //consume payload

+            assert(pos <= stop);

+        }

+    }

+

+    assert(pos <= stop);

+    assert(timecode >= 0);

+

+    m_timecode = timecode;

+}

+

+

+Cluster* Cluster::Parse(

+    Segment* pSegment,

+    long idx,

+    long long off)

+{

+    assert(pSegment);

+    assert(off >= 0);

+    assert(off < pSegment->m_size);

+

+    Cluster* const pCluster = new Cluster(pSegment, idx, -off);

+    assert(pCluster);

+

+    return pCluster;

+}

+

+

+Cluster::Cluster() :

+    m_pSegment(NULL),

+    m_index(0),

+    m_pos(0),

+    m_size(0),

+    m_timecode(0),

+    m_entries(NULL),

+    m_entriesCount(0)

+{

+}

+

+

+Cluster::Cluster(

+    Segment* pSegment,

+    long idx,

+    long long off) :

+    m_pSegment(pSegment),

+    m_index(idx),

+    m_pos(off),

+    m_size(-1),

+    m_timecode(-1),

+    m_entries(NULL),

+    m_entriesCount(0)

+{

+}

+

+

+Cluster::~Cluster()

+{

+    BlockEntry** i = m_entries;

+    BlockEntry** const j = m_entries + m_entriesCount;

+

+    while (i != j)

+    {

+         BlockEntry* p = *i++;

+         assert(p);

+

+         delete p;

+    }

+

+    delete[] m_entries;

+}

+

+

+bool Cluster::EOS() const

+{

+    return (m_pSegment == NULL);

+}

+

+

+void Cluster::LoadBlockEntries()

+{

+    if (m_entries)

+        return;

+

+    assert(m_pSegment);

+    assert(m_pos);

+    assert(m_size);

+    assert(m_entriesCount == 0);

+

+    IMkvReader* const pReader = m_pSegment->m_pReader;

+

+    if (m_pos < 0)

+        m_pos *= -1;  //relative to segment

+

+    long long pos = m_pSegment->m_start + m_pos;  //absolute

+

+    {

+        long len;

+

+        const long long id = ReadUInt(pReader, pos, len);

+        id;

+        assert(id >= 0);

+        assert(id == 0x0F43B675);  //Cluster ID

+

+        pos += len;  //consume id

+

+        const long long size = ReadUInt(pReader, pos, len);

+        assert(size > 0);

+

+        pos += len;  //consume size

+

+        //pos now points to start of payload

+

+        if (m_size >= 0)

+            assert(size == m_size);

+        else

+            m_size = size;

+    }

+

+    const long long stop = pos + m_size;

+    long long timecode = -1;  //of cluster itself

+

+    //First count the number of entries

+

+    long long idx = pos;  //points to start of payload

+    m_entriesCount = 0;

+

+    while (idx < stop)

+    {

+        if (Match(pReader, idx, 0x67, timecode))

+        {

+            if (m_timecode >= 0)

+                assert(timecode == m_timecode);

+            else

+                m_timecode = timecode;

+        }

+        else

+        {

+            long len;

+

+            const long long id = ReadUInt(pReader, idx, len);

+            assert(id >= 0);  //TODO

+            assert((idx + len) <= stop);

+

+            idx += len;  //consume id

+

+            const long long size = ReadUInt(pReader, idx, len);

+            assert(size >= 0);  //TODO

+            assert((idx + len) <= stop);

+

+            idx += len;  //consume size

+

+            if (id == 0x20)  //BlockGroup ID

+                ++m_entriesCount;

+            else if (id == 0x23)  //SimpleBlock ID

+                ++m_entriesCount;

+

+            idx += size;  //consume payload

+            assert(idx <= stop);

+        }

+    }

+

+    assert(idx == stop);

+    assert(m_timecode >= 0);

+

+    if (m_entriesCount == 0)  //TODO: handle empty clusters

+        return;

+

+    m_entries = new BlockEntry*[m_entriesCount];

+    size_t index = 0;

+

+    while (pos < stop)

+    {

+        if (Match(pReader, pos, 0x67, timecode))

+            assert(timecode == m_timecode);

+        else

+        {

+            long len;

+            const long long id = ReadUInt(pReader, pos, len);

+            assert(id >= 0);  //TODO

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume id

+

+            const long long size = ReadUInt(pReader, pos, len);

+            assert(size >= 0);  //TODO

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume size

+

+            if (id == 0x20)  //BlockGroup ID

+                ParseBlockGroup(pos, size, index++);

+            else if (id == 0x23)  //SimpleBlock ID

+                ParseSimpleBlock(pos, size, index++);

+

+            pos += size;  //consume payload

+            assert(pos <= stop);

+        }

+    }

+

+    assert(pos == stop);

+    assert(timecode >= 0);

+    assert(index == m_entriesCount);

+}

+

+

+

+long long Cluster::GetTimeCode()

+{

+    Load();

+    return m_timecode;

+}

+

+

+long long Cluster::GetTime()

+{

+    const long long tc = GetTimeCode();

+    assert(tc >= 0);

+

+    const SegmentInfo* const pInfo = m_pSegment->GetInfo();

+    assert(pInfo);

+

+    const long long scale = pInfo->GetTimeCodeScale();

+    assert(scale >= 1);

+

+    const long long t = m_timecode * scale;

+

+    return t;

+}

+

+

+long long Cluster::GetFirstTime()

+{

+    const BlockEntry* const pEntry = GetFirst();

+

+    if (pEntry == NULL)  //empty cluster

+        return GetTime();

+

+    const Block* const pBlock = pEntry->GetBlock();

+    assert(pBlock);

+

+    return pBlock->GetTime(this);

+}

+

+

+long long Cluster::GetLastTime()

+{

+    const BlockEntry* const pEntry = GetLast();

+

+    if (pEntry == NULL)  //empty cluster

+        return GetTime();

+

+    const Block* const pBlock = pEntry->GetBlock();

+    assert(pBlock);

+

+    return pBlock->GetTime(this);

+}

+

+

+void Cluster::ParseBlockGroup(long long start, long long size, size_t index)

+{

+    assert(m_entries);

+    assert(m_entriesCount);

+    assert(index < m_entriesCount);

+

+    BlockGroup* const pGroup =

+        new (std::nothrow) BlockGroup(this, index, start, size);

+    assert(pGroup);  //TODO

+

+    m_entries[index] = pGroup;

+}

+

+

+

+void Cluster::ParseSimpleBlock(long long start, long long size, size_t index)

+{

+    assert(m_entries);

+    assert(m_entriesCount);

+    assert(index < m_entriesCount);

+

+    SimpleBlock* const pSimpleBlock =

+        new (std::nothrow) SimpleBlock(this, index, start, size);

+    assert(pSimpleBlock);  //TODO

+

+    m_entries[index] = pSimpleBlock;

+}

+

+

+const BlockEntry* Cluster::GetFirst()

+{

+    LoadBlockEntries();

+    //assert(m_entries);

+    //assert(m_entriesCount >= 1);

+

+    if ((m_entries == NULL) || (m_entriesCount == 0))

+        return NULL;

+

+    const BlockEntry* const pFirst = m_entries[0];

+    assert(pFirst);

+

+    return pFirst;

+}

+

+

+const BlockEntry* Cluster::GetLast()

+{

+    LoadBlockEntries();

+    //assert(m_entries);

+    //assert(m_entriesCount >= 1);

+

+    if ((m_entries == NULL) || (m_entriesCount == 0))

+        return NULL;

+

+    const size_t idx = m_entriesCount - 1;

+

+    const BlockEntry* const pLast = m_entries[idx];

+    assert(pLast);

+

+    return pLast;

+}

+

+

+const BlockEntry* Cluster::GetNext(const BlockEntry* pEntry) const

+{

+    assert(pEntry);

+    assert(m_entries);

+    assert(m_entriesCount);

+

+    size_t idx = pEntry->GetIndex();

+    assert(idx < m_entriesCount);

+    assert(m_entries[idx] == pEntry);

+

+    ++idx;

+

+    if (idx >= m_entriesCount)

+      return NULL;

+

+    return m_entries[idx];

+}

+

+

+const BlockEntry* Cluster::GetEntry(const Track* pTrack)

+{

+    assert(pTrack);

+

+    if (m_pSegment == NULL)  //EOS

+        return pTrack->GetEOS();

+

+    LoadBlockEntries();

+

+    if ((m_entries == NULL) || (m_entriesCount == 0))

+        return NULL;

+

+    BlockEntry** i = m_entries;

+    assert(i);

+

+    BlockEntry** const j = i + m_entriesCount;

+

+    while (i != j)

+    {

+        const BlockEntry* const pEntry = *i++;

+        assert(pEntry);

+        assert(!pEntry->EOS());

+

+        const Block* const pBlock = pEntry->GetBlock();

+        assert(pBlock);

+

+        if (pBlock->GetTrackNumber() != pTrack->GetNumber())

+            continue;

+

+        if (pTrack->VetEntry(pEntry))

+            return pEntry;

+    }

+

+    return pTrack->GetEOS();  //no satisfactory block found

+}

+

+

+const BlockEntry*

+Cluster::GetEntry(

+    const CuePoint& cp,

+    const CuePoint::TrackPosition& tp)

+{

+    assert(m_pSegment);

+

+    LoadBlockEntries();

+

+    if (m_entries == NULL)

+        return NULL;

+

+    const long long count = m_entriesCount;

+

+    if (count <= 0)

+        return NULL;

+

+    const long long tc = cp.GetTimeCode();

+

+    if ((tp.m_block > 0) && (tp.m_block <= count))

+    {

+        const size_t block = static_cast<size_t>(tp.m_block);

+        const size_t index = block - 1;

+

+        const BlockEntry* const pEntry = m_entries[index];

+        assert(pEntry);

+        assert(!pEntry->EOS());

+

+        const Block* const pBlock = pEntry->GetBlock();

+        assert(pBlock);

+

+        if ((pBlock->GetTrackNumber() == tp.m_track) &&

+            (pBlock->GetTimeCode(this) == tc))

+        {

+            return pEntry;

+        }

+    }

+

+    const BlockEntry* const* i = m_entries;

+    const BlockEntry* const* const j = i + count;

+

+    while (i != j)

+    {

+        const BlockEntry* const pEntry = *i++;

+        assert(pEntry);

+        assert(!pEntry->EOS());

+

+        const Block* const pBlock = pEntry->GetBlock();

+        assert(pBlock);

+

+        if (pBlock->GetTrackNumber() != tp.m_track)

+            continue;

+

+        const long long tc_ = pBlock->GetTimeCode(this);

+

+        if (tc_ < tc)

+            continue;

+

+        if (tc_ > tc)

+            return NULL;

+

+        const Tracks* const pTracks = m_pSegment->GetTracks();

+        assert(pTracks);

+

+        const long tn = static_cast<long>(tp.m_track);

+        const Track* const pTrack = pTracks->GetTrackByNumber(tn);

+

+        if (pTrack == NULL)

+            return NULL;

+

+        const long long type = pTrack->GetType();

+

+        if (type == 2)  //audio

+            return pEntry;

+

+        if (type != 1)  //not video

+            return NULL;

+

+        if (!pBlock->IsKey())

+            return NULL;

+

+        return pEntry;

+    }

+

+    return NULL;

+}

+

+

+const BlockEntry* Cluster::GetMaxKey(const VideoTrack* pTrack)

+{

+    assert(pTrack);

+

+    if (m_pSegment == NULL)  //EOS

+        return pTrack->GetEOS();

+

+    LoadBlockEntries();

+    //assert(m_entries);

+

+    BlockEntry** i = m_entries + m_entriesCount;

+    BlockEntry** const j = m_entries;

+

+    while (i != j)

+    {

+        const BlockEntry* const pEntry = *--i;

+        assert(pEntry);

+        assert(!pEntry->EOS());

+

+        const Block* const pBlock = pEntry->GetBlock();

+        assert(pBlock);

+

+        if (pBlock->GetTrackNumber() != pTrack->GetNumber())

+            continue;

+

+        if (pBlock->IsKey())

+            return pEntry;

+    }

+

+    return pTrack->GetEOS();  //no satisfactory block found

+}

+

+

+

+BlockEntry::BlockEntry()

+{

+}

+

+

+BlockEntry::~BlockEntry()

+{

+}

+

+

+SimpleBlock::SimpleBlock(

+    Cluster* pCluster,

+    size_t idx,

+    long long start,

+    long long size) :

+    m_pCluster(pCluster),

+    m_index(idx),

+    m_block(start, size, pCluster->m_pSegment->m_pReader)

+{

+}

+

+

+bool SimpleBlock::EOS() const

+{

+    return false;

+}

+

+

+Cluster* SimpleBlock::GetCluster() const

+{

+    return m_pCluster;

+}

+

+

+size_t SimpleBlock::GetIndex() const

+{

+    return m_index;

+}

+

+

+const Block* SimpleBlock::GetBlock() const

+{

+    return &m_block;

+}

+

+

+bool SimpleBlock::IsBFrame() const

+{

+    return false;

+}

+

+

+BlockGroup::BlockGroup(

+    Cluster* pCluster,

+    size_t idx,

+    long long start,

+    long long size_) :

+    m_pCluster(pCluster),

+    m_index(idx),

+    m_prevTimeCode(0),

+    m_nextTimeCode(0),

+    m_pBlock(NULL)  //TODO: accept multiple blocks within a block group

+{

+    IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;

+

+    long long pos = start;

+    const long long stop = start + size_;

+

+    bool bSimpleBlock = false;

+    bool bReferenceBlock = false;

+

+    while (pos < stop)

+    {

+        short t;

+

+        if (Match(pReader, pos, 0x7B, t))

+        {

+            if (t < 0)

+                m_prevTimeCode = t;

+            else if (t > 0)

+                m_nextTimeCode = t;

+            else

+                assert(false);

+

+            bReferenceBlock = true;

+        }

+        else

+        {

+            long len;

+            const long long id = ReadUInt(pReader, pos, len);

+            assert(id >= 0);  //TODO

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume ID

+

+            const long long size = ReadUInt(pReader, pos, len);

+            assert(size >= 0);  //TODO

+            assert((pos + len) <= stop);

+

+            pos += len;  //consume size

+

+            switch (id)

+            {

+                case 0x23:  //SimpleBlock ID

+                    bSimpleBlock = true;

+                    //YES, FALL THROUGH TO NEXT CASE

+

+                case 0x21:  //Block ID

+                    ParseBlock(pos, size);

+                    break;

+

+                default:

+                    break;

+            }

+

+            pos += size;  //consume payload

+            assert(pos <= stop);

+        }

+    }

+

+    assert(pos == stop);

+    assert(m_pBlock);

+

+    if (!bSimpleBlock)

+        m_pBlock->SetKey(!bReferenceBlock);

+}

+

+

+BlockGroup::~BlockGroup()

+{

+    delete m_pBlock;

+}

+

+

+void BlockGroup::ParseBlock(long long start, long long size)

+{

+    IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;

+

+    Block* const pBlock = new Block(start, size, pReader);

+    assert(pBlock);  //TODO

+

+    //TODO: the Matroska spec says you have multiple blocks within the

+    //same block group, with blocks ranked by priority (the flag bits).

+

+    assert(m_pBlock == NULL);

+    m_pBlock = pBlock;

+}

+

+

+bool BlockGroup::EOS() const

+{

+    return false;

+}

+

+

+Cluster* BlockGroup::GetCluster() const

+{

+    return m_pCluster;

+}

+

+

+size_t BlockGroup::GetIndex() const

+{

+    return m_index;

+}

+

+

+const Block* BlockGroup::GetBlock() const

+{

+    return m_pBlock;

+}

+

+

+short BlockGroup::GetPrevTimeCode() const

+{

+    return m_prevTimeCode;

+}

+

+

+short BlockGroup::GetNextTimeCode() const

+{

+    return m_nextTimeCode;

+}

+

+

+bool BlockGroup::IsBFrame() const

+{

+    return (m_nextTimeCode > 0);

+}

+

+

+

+Block::Block(long long start, long long size_, IMkvReader* pReader) :

+    m_start(start),

+    m_size(size_)

+{

+    long long pos = start;

+    const long long stop = start + size_;

+

+    long len;

+

+    m_track = ReadUInt(pReader, pos, len);

+    assert(m_track > 0);

+    assert((pos + len) <= stop);

+

+    pos += len;  //consume track number

+    assert((stop - pos) >= 2);

+

+    m_timecode = Unserialize2SInt(pReader, pos);

+

+    pos += 2;

+    assert((stop - pos) >= 1);

+

+    const long hr = pReader->Read(pos, 1, &m_flags);

+    assert(hr == 0L);

+

+    ++pos;

+    assert(pos <= stop);

+

+    m_frameOff = pos;

+

+    const long long frame_size = stop - pos;

+

+    assert(frame_size <= 2147483647L);

+

+    m_frameSize = static_cast<long>(frame_size);

+}

+

+

+long long Block::GetTimeCode(Cluster* pCluster) const

+{

+    assert(pCluster);

+

+    const long long tc0 = pCluster->GetTimeCode();

+    assert(tc0 >= 0);

+

+    const long long tc = tc0 + static_cast<long long>(m_timecode);

+    assert(tc >= 0);

+

+    return tc;  //unscaled timecode units

+}

+

+

+long long Block::GetTime(Cluster* pCluster) const

+{

+    assert(pCluster);

+

+    const long long tc = GetTimeCode(pCluster);

+

+    const Segment* const pSegment = pCluster->m_pSegment;

+    const SegmentInfo* const pInfo = pSegment->GetInfo();

+    assert(pInfo);

+

+    const long long scale = pInfo->GetTimeCodeScale();

+    assert(scale >= 1);

+

+    const long long ns = tc * scale;

+

+    return ns;

+}

+

+

+long long Block::GetTrackNumber() const

+{

+    return m_track;

+}

+

+

+bool Block::IsKey() const

+{

+    return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);

+}

+

+

+void Block::SetKey(bool bKey)

+{

+    if (bKey)

+        m_flags |= static_cast<unsigned char>(1 << 7);

+    else

+        m_flags &= 0x7F;

+}

+

+

+long long Block::GetOffset() const

+{

+  return m_frameOff;

+}

+

+

+long Block::GetSize() const

+{

+    return m_frameSize;

+}

+

+

+long Block::Read(IMkvReader* pReader, unsigned char* buf) const

+{

+

+    assert(pReader);

+    assert(buf);

+

+    const long hr = pReader->Read(m_frameOff, m_frameSize, buf);

+

+    return hr;

+}

+

+

+}  //end namespace mkvparser

diff --git a/media/libstagefright/matroska/mkvparser.hpp b/media/libstagefright/matroska/mkvparser.hpp
index 4d311b4..c46d349 100644
--- a/media/libstagefright/matroska/mkvparser.hpp
+++ b/media/libstagefright/matroska/mkvparser.hpp
@@ -1,428 +1,554 @@
-#ifndef MKVPARSER_HPP
-#define MKVPARSER_HPP
-
-#include <cstdlib>
-#include <cstdio>
-
-namespace mkvparser
-{
-
-const int E_FILE_FORMAT_INVALID = -2;
-const int E_BUFFER_NOT_FULL = -3;
-
-class IMkvReader
-{
-public:
-    virtual int Read(long long position, long length, unsigned char* buffer) = 0;
-    virtual int Length(long long* total, long long* available) = 0; 	
-protected:
-    virtual ~IMkvReader();
-};
-
-long long GetUIntLength(IMkvReader*, long long, long&);
-long long ReadUInt(IMkvReader*, long long, long&);
-long long SyncReadUInt(IMkvReader*, long long pos, long long stop, long&);
-long long UnserializeUInt(IMkvReader*, long long pos, long long size);
-float Unserialize4Float(IMkvReader*, long long);
-double Unserialize8Double(IMkvReader*, long long);
-short Unserialize2SInt(IMkvReader*, long long);
-signed char Unserialize1SInt(IMkvReader*, long long);
-bool Match(IMkvReader*, long long&, unsigned long, long long&);
-bool Match(IMkvReader*, long long&, unsigned long, char*&);
-bool Match(IMkvReader*, long long&, unsigned long,unsigned char*&,
-           size_t *optionalSize = NULL);
-bool Match(IMkvReader*, long long&, unsigned long, double&);
-bool Match(IMkvReader*, long long&, unsigned long, short&);
-
-
-struct EBMLHeader
-{
-    EBMLHeader();
-    ~EBMLHeader();  
-    long long m_version;
-    long long m_readVersion;
-    long long m_maxIdLength;
-    long long m_maxSizeLength;
-    char* m_docType;
-    long long m_docTypeVersion;
-    long long m_docTypeReadVersion;
-    
-    long long Parse(IMkvReader*, long long&);
-};
-
-
-class Segment;
-class Track;
-class Cluster;
-
-class Block
-{
-    Block(const Block&);
-    Block& operator=(const Block&);
-
-public:
-    const long long m_start;
-    const long long m_size;
-    
-    Block(long long start, long long size, IMkvReader*);
-    
-    unsigned long GetTrackNumber() const;
-    
-    long long GetTimeCode(Cluster*) const;  //absolute, but not scaled
-    long long GetTime(Cluster*) const;      //absolute, and scaled (nanosecond units)
-    bool IsKey() const;
-    void SetKey(bool);
-
-    long GetSize() const;
-    long Read(IMkvReader*, unsigned char*) const;
-    
-private:
-    long long m_track;   //Track::Number()
-    short m_timecode;  //relative to cluster
-    unsigned char m_flags;
-    long long m_frameOff;
-    long m_frameSize;    
-
-};
-
-
-class BlockEntry
-{
-    BlockEntry(const BlockEntry&);
-    BlockEntry& operator=(const BlockEntry&);
-    
-public:
-    virtual ~BlockEntry();
-    virtual bool EOS() const = 0;
-    virtual Cluster* GetCluster() const = 0;
-    virtual size_t GetIndex() const = 0;
-    virtual const Block* GetBlock() const = 0;
-    virtual bool IsBFrame() const = 0;
-    
-protected:
-    BlockEntry();
-
-};
-
-
-class SimpleBlock : public BlockEntry
-{
-    SimpleBlock(const SimpleBlock&);
-    SimpleBlock& operator=(const SimpleBlock&);
-
-public:
-    SimpleBlock(Cluster*, size_t, long long start, long long size);
-
-    bool EOS() const;
-    Cluster* GetCluster() const; 
-    size_t GetIndex() const;
-    const Block* GetBlock() const;
-    bool IsBFrame() const;
-
-protected:
-    Cluster* const m_pCluster;
-    const size_t m_index;
-    Block m_block;
-    
-};
-
-
-class BlockGroup : public BlockEntry
-{
-    BlockGroup(const BlockGroup&);
-    BlockGroup& operator=(const BlockGroup&);
-
-public:
-    BlockGroup(Cluster*, size_t, long long, long long);
-    ~BlockGroup();
-    
-    bool EOS() const;
-    Cluster* GetCluster() const; 
-    size_t GetIndex() const;
-    const Block* GetBlock() const;
-    bool IsBFrame() const;
-    
-    short GetPrevTimeCode() const;  //relative to block's time
-    short GetNextTimeCode() const;  //as above
-    
-protected:
-    Cluster* const m_pCluster;
-    const size_t m_index;
-    
-private:
-    BlockGroup(Cluster*, size_t, unsigned long);
-    void ParseBlock(long long start, long long size);
-
-    short m_prevTimeCode;
-    short m_nextTimeCode;
-    
-    //TODO: the Matroska spec says you can have multiple blocks within the 
-    //same block group, with blocks ranked by priority (the flag bits).
-    //For now we just cache a single block.
-#if 0
-    typedef std::deque<Block*> blocks_t;
-    blocks_t m_blocks;  //In practice should contain only a single element.
-#else
-    Block* m_pBlock;
-#endif
-    
-};
-
-
-class Track
-{
-    Track(const Track&);
-    Track& operator=(const Track&);
-
-public:    
-    Segment* const m_pSegment;
-    virtual ~Track();
-    
-    long long GetType() const; 
-    unsigned long GetNumber() const;
-    const char* GetNameAsUTF8() const;
-    const char* GetCodecNameAsUTF8() const;
-    const char* GetCodecId() const;
-    const unsigned char* GetCodecPrivate(
-            size_t *optionalSize = NULL) const;
-    
-    const BlockEntry* GetEOS() const;
-    
-    struct Settings 
-    {
-        long long start;
-        long long size;
-    };
-    
-    struct Info
-    {
-        long long type;
-        long long number;
-        long long uid;
-        char* nameAsUTF8;
-        char* codecId;
-        unsigned char* codecPrivate;
-        size_t codecPrivateSize;
-        char* codecNameAsUTF8;
-        Settings settings;
-        Info();
-        void Clear();
-    };
-    
-    long GetFirst(const BlockEntry*&) const;
-    long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const;
-    virtual bool VetEntry(const BlockEntry*) const = 0;
-        
-protected:
-    Track(Segment*, const Info&);        
-    const Info m_info;
-
-    class EOSBlock : public BlockEntry
-    {
-    public:
-        EOSBlock();
-
-        bool EOS() const;    
-        Cluster* GetCluster() const;
-        size_t GetIndex() const;
-        const Block* GetBlock() const;
-        bool IsBFrame() const;
-    };
-    
-    EOSBlock m_eos;
-    
-};
-
-
-class VideoTrack : public Track
-{
-    VideoTrack(const VideoTrack&);
-    VideoTrack& operator=(const VideoTrack&);
-    
-public:
-    VideoTrack(Segment*, const Info&);    
-    long long GetWidth() const;
-    long long GetHeight() const;
-    double GetFrameRate() const;
-    
-    bool VetEntry(const BlockEntry*) const;
-
-private:
-    long long m_width;
-    long long m_height;
-    double m_rate;
-    
-};
-
-
-class AudioTrack : public Track
-{
-    AudioTrack(const AudioTrack&);
-    AudioTrack& operator=(const AudioTrack&);
-
-public:
-    AudioTrack(Segment*, const Info&);    
-    double GetSamplingRate() const;
-    long long GetChannels() const;
-    long long GetBitDepth() const;    
-    bool VetEntry(const BlockEntry*) const;
-
-private:
-    double m_rate;
-    long long m_channels;
-    long long m_bitDepth;
-};
-
-
-class Tracks
-{
-    Tracks(const Tracks&);
-    Tracks& operator=(const Tracks&);
-
-public:
-    Segment* const m_pSegment;
-    const long long m_start;
-    const long long m_size;
-    
-    Tracks(Segment*, long long start, long long size);
-    virtual ~Tracks();
-
-    Track* GetTrackByNumber(unsigned long tn) const;
-    Track* GetTrackByIndex(unsigned long idx) const;
-    
-private:
-    Track** m_trackEntries; 
-    Track** m_trackEntriesEnd;
-
-    void ParseTrackEntry(long long, long long, Track*&);
-    
-public:
-    unsigned long GetTracksCount() const;
-};
-
-
-class SegmentInfo
-{
-    SegmentInfo(const SegmentInfo&);
-    SegmentInfo& operator=(const SegmentInfo&);
-    
-public:
-    Segment* const m_pSegment;
-    const long long m_start;
-    const long long m_size;
-    
-    SegmentInfo(Segment*, long long start, long long size);
-    ~SegmentInfo();
-    long long GetTimeCodeScale() const;
-    long long GetDuration() const;  //scaled
-    const char* GetMuxingAppAsUTF8() const;
-    const char* GetWritingAppAsUTF8() const;
-    const char* GetTitleAsUTF8() const;
-    
-private:
-    long long m_timecodeScale;
-    double m_duration;
-    char* m_pMuxingAppAsUTF8;
-    char* m_pWritingAppAsUTF8;
-    char* m_pTitleAsUTF8;
-};
-
-
-class Cluster
-{
-    Cluster(const Cluster&);
-    Cluster& operator=(const Cluster&);
-
-public:
-    Segment* const m_pSegment;
-    const size_t m_index;
-    
-public:    
-    static Cluster* Parse(Segment*, size_t, long long off);
-
-    Cluster();  //EndOfStream
-    ~Cluster();
-    
-    bool EOS() const;
-    
-    long long GetTimeCode();  //absolute, but not scaled
-    long long GetTime();      //absolute, and scaled (nanosecond units)
-
-    const BlockEntry* GetFirst();
-    const BlockEntry* GetLast();
-    const BlockEntry* GetNext(const BlockEntry*) const;    
-    const BlockEntry* GetEntry(const Track*);
-protected:    
-    Cluster(Segment*, size_t, long long off);
-    
-private:
-    long long m_start;
-    long long m_size;    
-    long long m_timecode;
-    BlockEntry** m_pEntries;
-    size_t m_entriesCount;
-   
-    void Load();
-    void LoadBlockEntries();
-    void ParseBlockGroup(long long, long long, size_t);
-    void ParseSimpleBlock(long long, long long, size_t);
-    
-};
-
-
-class Segment
-{
-    Segment(const Segment&);
-    Segment& operator=(const Segment&);
-
-private:
-    Segment(IMkvReader*, long long pos, long long size);
-
-public:
-    IMkvReader* const m_pReader;
-    const long long m_start;  //posn of segment payload
-    const long long m_size;   //size of segment payload
-    Cluster m_eos;  //TODO: make private?
-    
-    static long long CreateInstance(IMkvReader*, long long, Segment*&);
-    ~Segment();
-
-    //for big-bang loading (source filter)
-    long Load();
-
-    //for incremental loading (splitter)    
-    long long Unparsed() const;
-    long long ParseHeaders();
-    long ParseCluster(Cluster*&, long long& newpos) const;
-    bool AddCluster(Cluster*, long long);
-
-    Tracks* GetTracks() const;    
-    const SegmentInfo* const GetInfo() const;
-    long long GetDuration() const;
-    
-    //NOTE: this turned out to be too inefficient.
-    //long long Load(long long time_nanoseconds);
-
-    Cluster* GetFirst();
-    Cluster* GetLast();
-    unsigned long GetCount() const;
-    
-    Cluster* GetNext(const Cluster*);
-    Cluster* GetCluster(long long time_nanoseconds);
-    
-private:
-    long long m_pos;  //absolute file posn; what has been consumed so far    
-    SegmentInfo* m_pInfo;
-    Tracks* m_pTracks;    
-    Cluster** m_clusters;
-    size_t m_clusterCount;
-
-    void ParseSeekHead(long long pos, long long size, size_t*);
-    void ParseSeekEntry(long long pos, long long size, size_t*);
-    void ParseSecondarySeekHead(long long off, size_t*);
-};
-
-
-}  //end namespace mkvparser
-
-#endif  //MKVPARSER_HPP
+// Copyright (c) 2010 The WebM project authors. All Rights Reserved.

+//

+// Use of this source code is governed by a BSD-style license

+// that can be found in the LICENSE file in the root of the source

+// tree. An additional intellectual property rights grant can be found

+// in the file PATENTS.  All contributing project authors may

+// be found in the AUTHORS file in the root of the source tree.

+

+#ifndef MKVPARSER_HPP

+#define MKVPARSER_HPP

+

+#include <cstdlib>

+#include <cstdio>

+

+namespace mkvparser

+{

+

+const int E_FILE_FORMAT_INVALID = -2;

+const int E_BUFFER_NOT_FULL = -3;

+

+class IMkvReader

+{

+public:

+    virtual int Read(long long pos, long len, unsigned char* buf) = 0;

+    virtual int Length(long long* total, long long* available) = 0;

+protected:

+    virtual ~IMkvReader();

+};

+

+long long GetUIntLength(IMkvReader*, long long, long&);

+long long ReadUInt(IMkvReader*, long long, long&);

+long long SyncReadUInt(IMkvReader*, long long pos, long long stop, long&);

+long long UnserializeUInt(IMkvReader*, long long pos, long long size);

+float Unserialize4Float(IMkvReader*, long long);

+double Unserialize8Double(IMkvReader*, long long);

+short Unserialize2SInt(IMkvReader*, long long);

+signed char Unserialize1SInt(IMkvReader*, long long);

+bool Match(IMkvReader*, long long&, unsigned long, long long&);

+bool Match(IMkvReader*, long long&, unsigned long, char*&);

+bool Match(IMkvReader*, long long&, unsigned long,unsigned char*&, size_t&);

+bool Match(IMkvReader*, long long&, unsigned long, double&);

+bool Match(IMkvReader*, long long&, unsigned long, short&);

+

+void GetVersion(int& major, int& minor, int& build, int& revision);

+

+struct EBMLHeader

+{

+    EBMLHeader();

+    ~EBMLHeader();

+    long long m_version;

+    long long m_readVersion;

+    long long m_maxIdLength;

+    long long m_maxSizeLength;

+    char* m_docType;

+    long long m_docTypeVersion;

+    long long m_docTypeReadVersion;

+

+    long long Parse(IMkvReader*, long long&);

+};

+

+

+class Segment;

+class Track;

+class Cluster;

+

+class Block

+{

+    Block(const Block&);

+    Block& operator=(const Block&);

+

+public:

+    const long long m_start;

+    const long long m_size;

+

+    Block(long long start, long long size, IMkvReader*);

+

+    long long GetTrackNumber() const;

+    long long GetTimeCode(Cluster*) const;  //absolute, but not scaled

+    long long GetTime(Cluster*) const;      //absolute, and scaled (ns units)

+    bool IsKey() const;

+    void SetKey(bool);

+

+    long long GetOffset() const;

+    long GetSize() const;

+    long Read(IMkvReader*, unsigned char*) const;

+

+private:

+    long long m_track;   //Track::Number()

+    short m_timecode;  //relative to cluster

+    unsigned char m_flags;

+    long long m_frameOff;

+    long m_frameSize;

+

+};

+

+

+class BlockEntry

+{

+    BlockEntry(const BlockEntry&);

+    BlockEntry& operator=(const BlockEntry&);

+

+public:

+    virtual ~BlockEntry();

+    virtual bool EOS() const = 0;

+    virtual Cluster* GetCluster() const = 0;

+    virtual size_t GetIndex() const = 0;

+    virtual const Block* GetBlock() const = 0;

+    virtual bool IsBFrame() const = 0;

+

+protected:

+    BlockEntry();

+

+};

+

+

+class SimpleBlock : public BlockEntry

+{

+    SimpleBlock(const SimpleBlock&);

+    SimpleBlock& operator=(const SimpleBlock&);

+

+public:

+    SimpleBlock(Cluster*, size_t, long long start, long long size);

+

+    bool EOS() const;

+    Cluster* GetCluster() const;

+    size_t GetIndex() const;

+    const Block* GetBlock() const;

+    bool IsBFrame() const;

+

+protected:

+    Cluster* const m_pCluster;

+    const size_t m_index;

+    Block m_block;

+

+};

+

+

+class BlockGroup : public BlockEntry

+{

+    BlockGroup(const BlockGroup&);

+    BlockGroup& operator=(const BlockGroup&);

+

+public:

+    BlockGroup(Cluster*, size_t, long long, long long);

+    ~BlockGroup();

+

+    bool EOS() const;

+    Cluster* GetCluster() const;

+    size_t GetIndex() const;

+    const Block* GetBlock() const;

+    bool IsBFrame() const;

+

+    short GetPrevTimeCode() const;  //relative to block's time

+    short GetNextTimeCode() const;  //as above

+

+protected:

+    Cluster* const m_pCluster;

+    const size_t m_index;

+

+private:

+    BlockGroup(Cluster*, size_t, unsigned long);

+    void ParseBlock(long long start, long long size);

+

+    short m_prevTimeCode;

+    short m_nextTimeCode;

+

+    //TODO: the Matroska spec says you can have multiple blocks within the

+    //same block group, with blocks ranked by priority (the flag bits).

+    //For now we just cache a single block.

+#if 0

+    typedef std::deque<Block*> blocks_t;

+    blocks_t m_blocks;  //In practice should contain only a single element.

+#else

+    Block* m_pBlock;

+#endif

+

+};

+

+

+class Track

+{

+    Track(const Track&);

+    Track& operator=(const Track&);

+

+public:

+    Segment* const m_pSegment;

+    virtual ~Track();

+

+    long long GetType() const;

+    long long GetNumber() const;

+    const char* GetNameAsUTF8() const;

+    const char* GetCodecNameAsUTF8() const;

+    const char* GetCodecId() const;

+    const unsigned char* GetCodecPrivate(size_t&) const;

+

+    const BlockEntry* GetEOS() const;

+

+    struct Settings

+    {

+        long long start;

+        long long size;

+    };

+

+    struct Info

+    {

+        long long type;

+        long long number;

+        long long uid;

+        char* nameAsUTF8;

+        char* codecId;

+        unsigned char* codecPrivate;

+        size_t codecPrivateSize;

+        char* codecNameAsUTF8;

+        Settings settings;

+        Info();

+        void Clear();

+    };

+

+    long GetFirst(const BlockEntry*&) const;

+    long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const;

+    virtual bool VetEntry(const BlockEntry*) const = 0;

+

+protected:

+    Track(Segment*, const Info&);

+    const Info m_info;

+

+    class EOSBlock : public BlockEntry

+    {

+    public:

+        EOSBlock();

+

+        bool EOS() const;

+        Cluster* GetCluster() const;

+        size_t GetIndex() const;

+        const Block* GetBlock() const;

+        bool IsBFrame() const;

+    };

+

+    EOSBlock m_eos;

+

+};

+

+

+class VideoTrack : public Track

+{

+    VideoTrack(const VideoTrack&);

+    VideoTrack& operator=(const VideoTrack&);

+

+public:

+    VideoTrack(Segment*, const Info&);

+    long long GetWidth() const;

+    long long GetHeight() const;

+    double GetFrameRate() const;

+

+    bool VetEntry(const BlockEntry*) const;

+

+private:

+    long long m_width;

+    long long m_height;

+    double m_rate;

+

+};

+

+

+class AudioTrack : public Track

+{

+    AudioTrack(const AudioTrack&);

+    AudioTrack& operator=(const AudioTrack&);

+

+public:

+    AudioTrack(Segment*, const Info&);

+    double GetSamplingRate() const;

+    long long GetChannels() const;

+    long long GetBitDepth() const;

+    bool VetEntry(const BlockEntry*) const;

+

+private:

+    double m_rate;

+    long long m_channels;

+    long long m_bitDepth;

+};

+

+

+class Tracks

+{

+    Tracks(const Tracks&);

+    Tracks& operator=(const Tracks&);

+

+public:

+    Segment* const m_pSegment;

+    const long long m_start;

+    const long long m_size;

+

+    Tracks(Segment*, long long start, long long size);

+    virtual ~Tracks();

+

+    Track* GetTrackByNumber(unsigned long tn) const;

+    Track* GetTrackByIndex(unsigned long idx) const;

+

+private:

+    Track** m_trackEntries;

+    Track** m_trackEntriesEnd;

+

+    void ParseTrackEntry(long long, long long, Track*&);

+

+public:

+    unsigned long GetTracksCount() const;

+};

+

+

+class SegmentInfo

+{

+    SegmentInfo(const SegmentInfo&);

+    SegmentInfo& operator=(const SegmentInfo&);

+

+public:

+    Segment* const m_pSegment;

+    const long long m_start;

+    const long long m_size;

+

+    SegmentInfo(Segment*, long long start, long long size);

+    ~SegmentInfo();

+    long long GetTimeCodeScale() const;

+    long long GetDuration() const;  //scaled

+    const char* GetMuxingAppAsUTF8() const;

+    const char* GetWritingAppAsUTF8() const;

+    const char* GetTitleAsUTF8() const;

+

+private:

+    long long m_timecodeScale;

+    double m_duration;

+    char* m_pMuxingAppAsUTF8;

+    char* m_pWritingAppAsUTF8;

+    char* m_pTitleAsUTF8;

+};

+

+class Cues;

+class CuePoint

+{

+    friend class Cues;

+

+    CuePoint(size_t, long long);

+    ~CuePoint();

+

+    CuePoint(const CuePoint&);

+    CuePoint& operator=(const CuePoint&);

+

+public:

+    void Load(IMkvReader*);

+

+    long long GetTimeCode() const;      //absolute but unscaled

+    long long GetTime(Segment*) const;  //absolute and scaled (ns units)

+

+    struct TrackPosition

+    {

+        long long m_track;

+        long long m_pos;  //of cluster

+        long long m_block;

+        //codec_state  //defaults to 0

+        //reference = clusters containing req'd referenced blocks

+        //  reftime = timecode of the referenced block

+

+        void Parse(IMkvReader*, long long, long long);

+    };

+

+    const TrackPosition* Find(const Track*) const;

+

+private:

+    const size_t m_index;

+    long long m_timecode;

+    TrackPosition* m_track_positions;

+    size_t m_track_positions_count;

+

+};

+

+

+class Cues

+{

+    friend class Segment;

+

+    Cues(Segment*, long long start, long long size);

+    ~Cues();

+

+    Cues(const Cues&);

+    Cues& operator=(const Cues&);

+

+public:

+    Segment* const m_pSegment;

+    const long long m_start;

+    const long long m_size;

+

+    bool Find(  //lower bound of time_ns

+        long long time_ns,

+        const Track*,

+        const CuePoint*&,

+        const CuePoint::TrackPosition*&) const;

+

+#if 0

+    bool FindNext(  //upper_bound of time_ns

+        long long time_ns,

+        const Track*,

+        const CuePoint*&,

+        const CuePoint::TrackPosition*&) const;

+#endif

+

+    const CuePoint* GetFirst() const;

+    const CuePoint* GetLast() const;

+

+    const CuePoint* GetNext(const CuePoint*) const;

+

+    const BlockEntry* GetBlock(

+                        const CuePoint*,

+                        const CuePoint::TrackPosition*) const;

+

+private:

+    void Init() const;

+    bool LoadCuePoint() const;

+    void PreloadCuePoint(size_t&, long long) const;

+

+    mutable CuePoint** m_cue_points;

+    mutable size_t m_count;

+    mutable size_t m_preload_count;

+    mutable long long m_pos;

+

+};

+

+

+class Cluster

+{

+    Cluster(const Cluster&);

+    Cluster& operator=(const Cluster&);

+

+public:

+    Segment* const m_pSegment;

+

+public:

+    static Cluster* Parse(Segment*, long, long long off);

+

+    Cluster();  //EndOfStream

+    ~Cluster();

+

+    bool EOS() const;

+

+    long long GetTimeCode();   //absolute, but not scaled

+    long long GetTime();       //absolute, and scaled (nanosecond units)

+    long long GetFirstTime();  //time (ns) of first (earliest) block

+    long long GetLastTime();   //time (ns) of last (latest) block

+

+    const BlockEntry* GetFirst();

+    const BlockEntry* GetLast();

+    const BlockEntry* GetNext(const BlockEntry*) const;

+    const BlockEntry* GetEntry(const Track*);

+    const BlockEntry* GetEntry(

+        const CuePoint&,

+        const CuePoint::TrackPosition&);

+    const BlockEntry* GetMaxKey(const VideoTrack*);

+

+protected:

+    Cluster(Segment*, long, long long off);

+

+public:

+    //TODO: these should all be private, with public selector functions

+    long m_index;

+    long long m_pos;

+    long long m_size;

+

+private:

+    long long m_timecode;

+    BlockEntry** m_entries;

+    size_t m_entriesCount;

+

+    void Load();

+    void LoadBlockEntries();

+    void ParseBlockGroup(long long, long long, size_t);

+    void ParseSimpleBlock(long long, long long, size_t);

+

+};

+

+

+class Segment

+{

+    friend class Cues;

+

+    Segment(const Segment&);

+    Segment& operator=(const Segment&);

+

+private:

+    Segment(IMkvReader*, long long pos, long long size);

+

+public:

+    IMkvReader* const m_pReader;

+    const long long m_start;  //posn of segment payload

+    const long long m_size;   //size of segment payload

+    Cluster m_eos;  //TODO: make private?

+

+    static long long CreateInstance(IMkvReader*, long long, Segment*&);

+    ~Segment();

+

+    long Load();  //loads headers and all clusters

+

+    //for incremental loading (splitter)

+    long long Unparsed() const;

+    long long ParseHeaders();  //stops when first cluster is found

+    long LoadCluster();        //loads one cluster

+

+#if 0

+    //This pair parses one cluster, but only changes the state of the

+    //segment object when the cluster is actually added to the index.

+    long ParseCluster(Cluster*&, long long& newpos) const;

+    bool AddCluster(Cluster*, long long);

+#endif

+

+    Tracks* GetTracks() const;

+    const SegmentInfo* GetInfo() const;

+    const Cues* GetCues() const;

+

+    long long GetDuration() const;

+

+    unsigned long GetCount() const;

+    Cluster* GetFirst();

+    Cluster* GetLast();

+    Cluster* GetNext(const Cluster*);

+

+    Cluster* FindCluster(long long time_nanoseconds);

+    const BlockEntry* Seek(long long time_nanoseconds, const Track*);

+

+private:

+

+    long long m_pos;  //absolute file posn; what has been consumed so far

+    SegmentInfo* m_pInfo;

+    Tracks* m_pTracks;

+    Cues* m_pCues;

+    Cluster** m_clusters;

+    long m_clusterCount;         //number of entries for which m_index >= 0

+    long m_clusterPreloadCount;  //number of entries for which m_index < 0

+    long m_clusterSize;          //array size

+

+    void AppendCluster(Cluster*);

+    void PreloadCluster(Cluster*, ptrdiff_t);

+

+    void ParseSeekHead(long long pos, long long size);

+    void ParseSeekEntry(long long pos, long long size);

+    void ParseCues(long long);

+

+    const BlockEntry* GetBlock(

+        const CuePoint&,

+        const CuePoint::TrackPosition&);

+

+};

+

+

+}  //end namespace mkvparser

+

+#endif  //MKVPARSER_HPP

diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index c927da1..f9f638f 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -289,6 +289,16 @@
             index, params, size);
 }
 
+status_t OMX::enableGraphicBuffers(
+        node_id node, OMX_U32 port_index, OMX_BOOL enable) {
+    return findInstance(node)->enableGraphicBuffers(port_index, enable);
+}
+
+status_t OMX::storeMetaDataInBuffers(
+        node_id node, OMX_U32 port_index, OMX_BOOL enable) {
+    return findInstance(node)->storeMetaDataInBuffers(port_index, enable);
+}
+
 status_t OMX::useBuffer(
         node_id node, OMX_U32 port_index, const sp<IMemory> &params,
         buffer_id *buffer) {
@@ -296,6 +306,13 @@
             port_index, params, buffer);
 }
 
+status_t OMX::useGraphicBuffer(
+        node_id node, OMX_U32 port_index,
+        const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) {
+    return findInstance(node)->useGraphicBuffer(
+            port_index, graphicBuffer, buffer);
+}
+
 status_t OMX::allocateBuffer(
         node_id node, OMX_U32 port_index, size_t size,
         buffer_id *buffer, void **buffer_data) {
@@ -497,12 +514,17 @@
     }
 
     if (!impl) {
+#if 0
         LOGW("Using software renderer.");
         impl = new SoftwareRenderer(
                 colorFormat,
                 surface,
                 displayWidth, displayHeight,
                 encodedWidth, encodedHeight);
+#else
+        CHECK(!"Should not be here.");
+        return NULL;
+#endif
     }
 
     return new OMXRenderer(impl);
@@ -527,4 +549,3 @@
 }
 
 }  // namespace android
-
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 5db516e..9b6d441 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -24,6 +24,7 @@
 #include <OMX_Component.h>
 
 #include <binder/IMemory.h>
+#include <media/stagefright/HardwareAPI.h>
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaErrors.h>
 
@@ -40,6 +41,11 @@
           mIsBackup(false) {
     }
 
+    BufferMeta(const sp<GraphicBuffer> &graphicBuffer)
+        : mGraphicBuffer(graphicBuffer),
+          mIsBackup(false) {
+    }
+
     void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) {
         if (!mIsBackup) {
             return;
@@ -61,6 +67,7 @@
     }
 
 private:
+    sp<GraphicBuffer> mGraphicBuffer;
     sp<IMemory> mMem;
     size_t mSize;
     bool mIsBackup;
@@ -240,6 +247,74 @@
     return StatusFromOMXError(err);
 }
 
+status_t OMXNodeInstance::enableGraphicBuffers(
+        OMX_U32 portIndex, OMX_BOOL enable) {
+    Mutex::Autolock autoLock(mLock);
+
+    OMX_INDEXTYPE index;
+    OMX_ERRORTYPE err = OMX_GetExtensionIndex(
+            mHandle,
+            const_cast<OMX_STRING>("OMX.google.android.index.enableAndroidNativeBuffers"),
+            &index);
+
+    if (err != OMX_ErrorNone) {
+        LOGE("OMX_GetExtensionIndex failed");
+
+        return StatusFromOMXError(err);
+    }
+
+    OMX_VERSIONTYPE ver;
+    ver.s.nVersionMajor = 1;
+    ver.s.nVersionMinor = 0;
+    ver.s.nRevision = 0;
+    ver.s.nStep = 0;
+    EnableAndroidNativeBuffersParams params = {
+        sizeof(EnableAndroidNativeBuffersParams), ver, portIndex, enable,
+    };
+
+    err = OMX_SetParameter(mHandle, index, &params);
+
+    if (err != OMX_ErrorNone) {
+        LOGE("OMX_EnableAndroidNativeBuffers failed with error %d (0x%08x)",
+                err, err);
+
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t OMXNodeInstance::storeMetaDataInBuffers(
+        OMX_U32 portIndex,
+        OMX_BOOL enable) {
+    Mutex::Autolock autolock(mLock);
+
+    OMX_INDEXTYPE index;
+    OMX_STRING name = const_cast<OMX_STRING>(
+            "OMX.google.android.index.storeMetaDataInBuffers");
+
+    OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index);
+    if (err != OMX_ErrorNone) {
+        LOGE("OMX_GetExtensionIndex %s failed", name);
+        return StatusFromOMXError(err);
+    }
+
+    StoreMetaDataInBuffersParams params;
+    memset(&params, 0, sizeof(params));
+    params.nSize = sizeof(params);
+
+    // Version: 1.0.0.0
+    params.nVersion.s.nVersionMajor = 1;
+
+    params.nPortIndex = portIndex;
+    params.bStoreMetaData = enable;
+    if ((err = OMX_SetParameter(mHandle, index, &params)) != OMX_ErrorNone) {
+        LOGE("OMX_SetParameter() failed for StoreMetaDataInBuffers: 0x%08x", err);
+        return UNKNOWN_ERROR;
+    }
+    return err;
+}
+
 status_t OMXNodeInstance::useBuffer(
         OMX_U32 portIndex, const sp<IMemory> &params,
         OMX::buffer_id *buffer) {
@@ -273,6 +348,60 @@
     return OK;
 }
 
+status_t OMXNodeInstance::useGraphicBuffer(
+        OMX_U32 portIndex, const sp<GraphicBuffer>& graphicBuffer,
+        OMX::buffer_id *buffer) {
+    Mutex::Autolock autoLock(mLock);
+
+    OMX_INDEXTYPE index;
+    OMX_ERRORTYPE err = OMX_GetExtensionIndex(
+            mHandle,
+            const_cast<OMX_STRING>("OMX.google.android.index.useAndroidNativeBuffer"),
+            &index);
+
+    if (err != OMX_ErrorNone) {
+        LOGE("OMX_GetExtensionIndex failed");
+
+        return StatusFromOMXError(err);
+    }
+
+    BufferMeta *bufferMeta = new BufferMeta(graphicBuffer);
+
+    OMX_BUFFERHEADERTYPE *header;
+
+    OMX_VERSIONTYPE ver;
+    ver.s.nVersionMajor = 1;
+    ver.s.nVersionMinor = 0;
+    ver.s.nRevision = 0;
+    ver.s.nStep = 0;
+    UseAndroidNativeBufferParams params = {
+        sizeof(UseAndroidNativeBufferParams), ver, portIndex, bufferMeta,
+        &header, graphicBuffer,
+    };
+
+    err = OMX_SetParameter(mHandle, index, &params);
+
+    if (err != OMX_ErrorNone) {
+        LOGE("OMX_UseAndroidNativeBuffer failed with error %d (0x%08x)", err,
+                err);
+
+        delete bufferMeta;
+        bufferMeta = NULL;
+
+        *buffer = 0;
+
+        return UNKNOWN_ERROR;
+    }
+
+    CHECK_EQ(header->pAppPrivate, bufferMeta);
+
+    *buffer = header;
+
+    addActiveBuffer(portIndex, *buffer);
+
+    return OK;
+}
+
 status_t OMXNodeInstance::allocateBuffer(
         OMX_U32 portIndex, size_t size, OMX::buffer_id *buffer,
         void **buffer_data) {
@@ -498,4 +627,3 @@
 }
 
 }  // namespace android
-
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
index b0d2c64..bbde516 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
@@ -18,18 +18,381 @@
 
 #include "ARTPSource.h"
 
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include <ctype.h>
 
 namespace android {
 
-AMPEG4AudioAssembler::AMPEG4AudioAssembler(const sp<AMessage> &notify)
+static bool GetAttribute(const char *s, const char *key, AString *value) {
+    value->clear();
+
+    size_t keyLen = strlen(key);
+
+    for (;;) {
+        while (isspace(*s)) {
+            ++s;
+        }
+
+        const char *colonPos = strchr(s, ';');
+
+        size_t len =
+            (colonPos == NULL) ? strlen(s) : colonPos - s;
+
+        if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
+            value->setTo(&s[keyLen + 1], len - keyLen - 1);
+            return true;
+        }
+
+        if (colonPos == NULL) {
+            return false;
+        }
+
+        s = colonPos + 1;
+    }
+}
+
+static sp<ABuffer> decodeHex(const AString &s) {
+    if ((s.size() % 2) != 0) {
+        return NULL;
+    }
+
+    size_t outLen = s.size() / 2;
+    sp<ABuffer> buffer = new ABuffer(outLen);
+    uint8_t *out = buffer->data();
+
+    uint8_t accum = 0;
+    for (size_t i = 0; i < s.size(); ++i) {
+        char c = s.c_str()[i];
+        unsigned value;
+        if (c >= '0' && c <= '9') {
+            value = c - '0';
+        } else if (c >= 'a' && c <= 'f') {
+            value = c - 'a' + 10;
+        } else if (c >= 'A' && c <= 'F') {
+            value = c - 'A' + 10;
+        } else {
+            return NULL;
+        }
+
+        accum = (accum << 4) | value;
+
+        if (i & 1) {
+            *out++ = accum;
+
+            accum = 0;
+        }
+    }
+
+    return buffer;
+}
+
+static status_t parseAudioObjectType(
+        ABitReader *bits, unsigned *audioObjectType) {
+    *audioObjectType = bits->getBits(5);
+    if ((*audioObjectType) == 31) {
+        *audioObjectType = 32 + bits->getBits(6);
+    }
+
+    return OK;
+}
+
+static status_t parseGASpecificConfig(
+        ABitReader *bits,
+        unsigned audioObjectType, unsigned channelConfiguration) {
+    unsigned frameLengthFlag = bits->getBits(1);
+    unsigned dependsOnCoreCoder = bits->getBits(1);
+    if (dependsOnCoreCoder) {
+        /* unsigned coreCoderDelay = */bits->getBits(1);
+    }
+    unsigned extensionFlag = bits->getBits(1);
+
+    if (!channelConfiguration) {
+        // program_config_element
+        return ERROR_UNSUPPORTED;  // XXX to be implemented
+    }
+
+    if (audioObjectType == 6 || audioObjectType == 20) {
+        /* unsigned layerNr = */bits->getBits(3);
+    }
+
+    if (extensionFlag) {
+        if (audioObjectType == 22) {
+            /* unsigned numOfSubFrame = */bits->getBits(5);
+            /* unsigned layerLength = */bits->getBits(11);
+        } else if (audioObjectType == 17 || audioObjectType == 19
+                || audioObjectType == 20 || audioObjectType == 23) {
+            /* unsigned aacSectionDataResilienceFlag = */bits->getBits(1);
+            /* unsigned aacScalefactorDataResilienceFlag = */bits->getBits(1);
+            /* unsigned aacSpectralDataResilienceFlag = */bits->getBits(1);
+        }
+
+        unsigned extensionFlag3 = bits->getBits(1);
+        CHECK_EQ(extensionFlag3, 0u);  // TBD in version 3
+    }
+
+    return OK;
+}
+
+static status_t parseAudioSpecificConfig(ABitReader *bits) {
+    unsigned audioObjectType;
+    CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK);
+
+    unsigned samplingFreqIndex = bits->getBits(4);
+    if (samplingFreqIndex == 0x0f) {
+        /* unsigned samplingFrequency = */bits->getBits(24);
+    }
+
+    unsigned channelConfiguration = bits->getBits(4);
+
+    unsigned extensionAudioObjectType = 0;
+    unsigned sbrPresent = 0;
+
+    if (audioObjectType == 5) {
+        extensionAudioObjectType = audioObjectType;
+        sbrPresent = 1;
+        unsigned extensionSamplingFreqIndex = bits->getBits(4);
+        if (extensionSamplingFreqIndex == 0x0f) {
+            /* unsigned extensionSamplingFrequency = */bits->getBits(24);
+        }
+        CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK);
+    }
+
+    CHECK((audioObjectType >= 1 && audioObjectType <= 4)
+        || (audioObjectType >= 6 && audioObjectType <= 7)
+        || audioObjectType == 17
+        || (audioObjectType >= 19 && audioObjectType <= 23));
+
+    CHECK_EQ(parseGASpecificConfig(
+                bits, audioObjectType, channelConfiguration), (status_t)OK);
+
+    if (audioObjectType == 17
+            || (audioObjectType >= 19 && audioObjectType <= 27)) {
+        unsigned epConfig = bits->getBits(2);
+        if (epConfig == 2 || epConfig == 3) {
+            // ErrorProtectionSpecificConfig
+            return ERROR_UNSUPPORTED;  // XXX to be implemented
+
+            if (epConfig == 3) {
+                unsigned directMapping = bits->getBits(1);
+                CHECK_EQ(directMapping, 1u);
+            }
+        }
+    }
+
+#if 0
+    // This is not supported here as the upper layers did not explicitly
+    // signal the length of AudioSpecificConfig.
+
+    if (extensionAudioObjectType != 5 && bits->numBitsLeft() >= 16) {
+        unsigned syncExtensionType = bits->getBits(11);
+        if (syncExtensionType == 0x2b7) {
+            CHECK_EQ(parseAudioObjectType(bits, &extensionAudioObjectType),
+                     (status_t)OK);
+
+            sbrPresent = bits->getBits(1);
+
+            if (sbrPresent == 1) {
+                unsigned extensionSamplingFreqIndex = bits->getBits(4);
+                if (extensionSamplingFreqIndex == 0x0f) {
+                    /* unsigned extensionSamplingFrequency = */bits->getBits(24);
+                }
+            }
+        }
+    }
+#endif
+
+    return OK;
+}
+
+static status_t parseStreamMuxConfig(
+        ABitReader *bits,
+        unsigned *numSubFrames,
+        unsigned *frameLengthType,
+        bool *otherDataPresent,
+        unsigned *otherDataLenBits) {
+    unsigned audioMuxVersion = bits->getBits(1);
+
+    unsigned audioMuxVersionA = 0;
+    if (audioMuxVersion == 1) {
+        audioMuxVersionA = bits->getBits(1);
+    }
+
+    CHECK_EQ(audioMuxVersionA, 0u);  // otherwise future spec
+
+    if (audioMuxVersion != 0) {
+        return ERROR_UNSUPPORTED;  // XXX to be implemented;
+    }
+    CHECK_EQ(audioMuxVersion, 0u);  // XXX to be implemented
+
+    unsigned allStreamsSameTimeFraming = bits->getBits(1);
+    CHECK_EQ(allStreamsSameTimeFraming, 1u);  // There's only one stream.
+
+    *numSubFrames = bits->getBits(6);
+    unsigned numProgram = bits->getBits(4);
+    CHECK_EQ(numProgram, 0u);  // disabled in RTP LATM
+
+    unsigned numLayer = bits->getBits(3);
+    CHECK_EQ(numLayer, 0u);  // disabled in RTP LATM
+
+    if (audioMuxVersion == 0) {
+        // AudioSpecificConfig
+        CHECK_EQ(parseAudioSpecificConfig(bits), (status_t)OK);
+    } else {
+        TRESPASS();  // XXX to be implemented
+    }
+
+    *frameLengthType = bits->getBits(3);
+    switch (*frameLengthType) {
+        case 0:
+        {
+            /* unsigned bufferFullness = */bits->getBits(8);
+
+            // The "coreFrameOffset" does not apply since there's only
+            // a single layer.
+            break;
+        }
+
+        case 1:
+        {
+            /* unsigned frameLength = */bits->getBits(9);
+            break;
+        }
+
+        case 3:
+        case 4:
+        case 5:
+        {
+            /* unsigned CELPframeLengthTableIndex = */bits->getBits(6);
+            break;
+        }
+
+        case 6:
+        case 7:
+        {
+            /* unsigned HVXCframeLengthTableIndex = */bits->getBits(1);
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    *otherDataPresent = bits->getBits(1);
+    *otherDataLenBits = 0;
+    if (*otherDataPresent) {
+        if (audioMuxVersion == 1) {
+            TRESPASS();  // XXX to be implemented
+        } else {
+            *otherDataLenBits = 0;
+
+            unsigned otherDataLenEsc;
+            do {
+                (*otherDataLenBits) <<= 8;
+                otherDataLenEsc = bits->getBits(1);
+                unsigned otherDataLenTmp = bits->getBits(8);
+                (*otherDataLenBits) += otherDataLenTmp;
+            } while (otherDataLenEsc);
+        }
+    }
+
+    unsigned crcCheckPresent = bits->getBits(1);
+    if (crcCheckPresent) {
+        /* unsigned crcCheckSum = */bits->getBits(8);
+    }
+
+    return OK;
+}
+
+sp<ABuffer> AMPEG4AudioAssembler::removeLATMFraming(const sp<ABuffer> &buffer) {
+    CHECK(!mMuxConfigPresent);  // XXX to be implemented
+
+    sp<ABuffer> out = new ABuffer(buffer->size());
+    out->setRange(0, 0);
+
+    size_t offset = 0;
+    uint8_t *ptr = buffer->data();
+
+    for (size_t i = 0; i <= mNumSubFrames; ++i) {
+        // parse PayloadLengthInfo
+
+        unsigned payloadLength = 0;
+
+        switch (mFrameLengthType) {
+            case 0:
+            {
+                unsigned muxSlotLengthBytes = 0;
+                unsigned tmp;
+                do {
+                    CHECK_LT(offset, buffer->size());
+                    tmp = ptr[offset++];
+                    muxSlotLengthBytes += tmp;
+                } while (tmp == 0xff);
+
+                payloadLength = muxSlotLengthBytes;
+                break;
+            }
+
+            default:
+                TRESPASS();  // XXX to be implemented
+                break;
+        }
+
+        CHECK_LE(offset + payloadLength, buffer->size());
+
+        memcpy(out->data() + out->size(), &ptr[offset], payloadLength);
+        out->setRange(0, out->size() + payloadLength);
+
+        offset += payloadLength;
+
+        if (mOtherDataPresent) {
+            // We want to stay byte-aligned.
+
+            CHECK((mOtherDataLenBits % 8) == 0);
+            CHECK_LE(offset + (mOtherDataLenBits / 8), buffer->size());
+            offset += mOtherDataLenBits / 8;
+        }
+    }
+
+    CHECK_EQ(offset, buffer->size());
+
+    return out;
+}
+
+AMPEG4AudioAssembler::AMPEG4AudioAssembler(
+        const sp<AMessage> &notify, const AString &params)
     : mNotifyMsg(notify),
+      mMuxConfigPresent(false),
       mAccessUnitRTPTime(0),
       mNextExpectedSeqNoValid(false),
       mNextExpectedSeqNo(0),
       mAccessUnitDamaged(false) {
+    AString val;
+    if (!GetAttribute(params.c_str(), "cpresent", &val)) {
+        mMuxConfigPresent = true;
+    } else if (val == "0") {
+        mMuxConfigPresent = false;
+    } else {
+        CHECK(val == "1");
+        mMuxConfigPresent = true;
+    }
+
+    CHECK(GetAttribute(params.c_str(), "config", &val));
+
+    sp<ABuffer> config = decodeHex(val);
+    CHECK(config != NULL);
+
+    ABitReader bits(config->data(), config->size());
+    status_t err = parseStreamMuxConfig(
+            &bits, &mNumSubFrames, &mFrameLengthType,
+            &mOtherDataPresent, &mOtherDataLenBits);
+
+    CHECK_EQ(err, (status_t)NO_ERROR);
 }
 
 AMPEG4AudioAssembler::~AMPEG4AudioAssembler() {
@@ -108,13 +471,7 @@
     while (it != mPackets.end()) {
         const sp<ABuffer> &unit = *it;
 
-        size_t n = 0;
-        while (unit->data()[n] == 0xff) {
-            ++n;
-        }
-        ++n;
-
-        totalSize += unit->size() - n;
+        totalSize += unit->size();
         ++it;
     }
 
@@ -124,20 +481,13 @@
     while (it != mPackets.end()) {
         const sp<ABuffer> &unit = *it;
 
-        size_t n = 0;
-        while (unit->data()[n] == 0xff) {
-            ++n;
-        }
-        ++n;
-
         memcpy((uint8_t *)accessUnit->data() + offset,
-               unit->data() + n, unit->size() - n);
-
-        offset += unit->size() - n;
+               unit->data(), unit->size());
 
         ++it;
     }
 
+    accessUnit = removeLATMFraming(accessUnit);
     CopyTimes(accessUnit, *mPackets.begin());
 
 #if 0
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.h b/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
index bf9f204..9cef94c 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
@@ -27,9 +27,11 @@
 namespace android {
 
 struct AMessage;
+struct AString;
 
 struct AMPEG4AudioAssembler : public ARTPAssembler {
-    AMPEG4AudioAssembler(const sp<AMessage> &notify);
+    AMPEG4AudioAssembler(
+            const sp<AMessage> &notify, const AString &params);
 
 protected:
     virtual ~AMPEG4AudioAssembler();
@@ -40,6 +42,13 @@
 
 private:
     sp<AMessage> mNotifyMsg;
+
+    bool mMuxConfigPresent;
+    unsigned mNumSubFrames;
+    unsigned mFrameLengthType;
+    bool mOtherDataPresent;
+    unsigned mOtherDataLenBits;
+
     uint32_t mAccessUnitRTPTime;
     bool mNextExpectedSeqNoValid;
     uint32_t mNextExpectedSeqNo;
@@ -49,6 +58,8 @@
     AssemblyStatus addPacket(const sp<ARTPSource> &source);
     void submitAccessUnit();
 
+    sp<ABuffer> removeLATMFraming(const sp<ABuffer> &buffer);
+
     DISALLOW_EVIL_CONSTRUCTORS(AMPEG4AudioAssembler);
 };
 
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 2518264..5aae4e7 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -57,7 +57,7 @@
         mAssembler = new AAVCAssembler(notify);
         mIssueFIRRequests = true;
     } else if (!strncmp(desc.c_str(), "MP4A-LATM/", 10)) {
-        mAssembler = new AMPEG4AudioAssembler(notify);
+        mAssembler = new AMPEG4AudioAssembler(notify, params);
     } else if (!strncmp(desc.c_str(), "H263-1998/", 10)
             || !strncmp(desc.c_str(), "H263-2000/", 10)) {
         mAssembler = new AH263Assembler(notify);
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index f928c06..e936923 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -23,11 +23,13 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/base64.h>
 #include <media/stagefright/MediaErrors.h>
 
 #include <arpa/inet.h>
 #include <fcntl.h>
 #include <netdb.h>
+#include <openssl/md5.h>
 #include <sys/socket.h>
 
 namespace android {
@@ -37,6 +39,7 @@
 
 ARTSPConnection::ARTSPConnection()
     : mState(DISCONNECTED),
+      mAuthType(NONE),
       mSocket(-1),
       mConnectionID(0),
       mNextCSeq(0),
@@ -114,10 +117,13 @@
 
 // static
 bool ARTSPConnection::ParseURL(
-        const char *url, AString *host, unsigned *port, AString *path) {
+        const char *url, AString *host, unsigned *port, AString *path,
+        AString *user, AString *pass) {
     host->clear();
     *port = 0;
     path->clear();
+    user->clear();
+    pass->clear();
 
     if (strncasecmp("rtsp://", url, 7)) {
         return false;
@@ -133,6 +139,24 @@
         path->setTo(slashPos);
     }
 
+    ssize_t atPos = host->find("@");
+
+    if (atPos >= 0) {
+        // Split of user:pass@ from hostname.
+
+        AString userPass(*host, 0, atPos);
+        host->erase(0, atPos + 1);
+
+        ssize_t colonPos = userPass.find(":");
+
+        if (colonPos < 0) {
+            *user = userPass;
+        } else {
+            user->setTo(userPass, 0, colonPos);
+            pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1);
+        }
+    }
+
     const char *colonPos = strchr(host->c_str(), ':');
 
     if (colonPos != NULL) {
@@ -187,7 +211,12 @@
 
     AString host, path;
     unsigned port;
-    if (!ParseURL(url.c_str(), &host, &port, &path)) {
+    if (!ParseURL(url.c_str(), &host, &port, &path, &mUser, &mPass)
+            || (mUser.size() > 0 && mPass.size() == 0)) {
+        // If we have a user name but no password we have to give up
+        // right here, since we currently have no way of asking the user
+        // for this information.
+
         LOGE("Malformed rtsp url %s", url.c_str());
 
         reply->setInt32("result", ERROR_MALFORMED);
@@ -197,6 +226,10 @@
         return;
     }
 
+    if (mUser.size() > 0) {
+        LOGV("user = '%s', pass = '%s'", mUser.c_str(), mPass.c_str());
+    }
+
     struct hostent *ent = gethostbyname(host.c_str());
     if (ent == NULL) {
         LOGE("Unknown host %s", host.c_str());
@@ -262,6 +295,11 @@
     reply->setInt32("result", OK);
     mState = DISCONNECTED;
 
+    mUser.clear();
+    mPass.clear();
+    mAuthType = NONE;
+    mNonce.clear();
+
     reply->post();
 }
 
@@ -335,6 +373,12 @@
     AString request;
     CHECK(msg->findString("request", &request));
 
+    // Just in case we need to re-issue the request with proper authentication
+    // later, stash it away.
+    reply->setString("original-request", request.c_str(), request.size());
+
+    addAuthentication(&request);
+
     // Find the boundary between headers and the body.
     ssize_t i = request.find("\r\n\r\n");
     CHECK_GE(i, 0);
@@ -347,7 +391,7 @@
 
     request.insert(cseqHeader, i + 2);
 
-    LOGV("%s", request.c_str());
+    LOGV("request: '%s'", request.c_str());
 
     size_t numBytesSent = 0;
     while (numBytesSent < request.size()) {
@@ -612,6 +656,30 @@
         }
     }
 
+    if (response->mStatusCode == 401) {
+        if (mAuthType == NONE && mUser.size() > 0
+                && parseAuthMethod(response)) {
+            ssize_t i;
+            CHECK_EQ((status_t)OK, findPendingRequest(response, &i));
+            CHECK_GE(i, 0);
+
+            sp<AMessage> reply = mPendingRequests.valueAt(i);
+            mPendingRequests.removeItemsAt(i);
+
+            AString request;
+            CHECK(reply->findString("original-request", &request));
+
+            sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
+            msg->setMessage("reply", reply);
+            msg->setString("request", request.c_str(), request.size());
+
+            LOGI("re-sending request with authentication headers...");
+            onSendRequest(msg);
+
+            return true;
+        }
+    }
+
     return notifyResponseListener(response);
 }
 
@@ -628,26 +696,47 @@
     return true;
 }
 
-bool ARTSPConnection::notifyResponseListener(
-        const sp<ARTSPResponse> &response) {
+status_t ARTSPConnection::findPendingRequest(
+        const sp<ARTSPResponse> &response, ssize_t *index) const {
+    *index = 0;
+
     ssize_t i = response->mHeaders.indexOfKey("cseq");
 
     if (i < 0) {
-        return true;
+        // This is an unsolicited server->client message.
+        return OK;
     }
 
     AString value = response->mHeaders.valueAt(i);
 
     unsigned long cseq;
     if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) {
-        return false;
+        return ERROR_MALFORMED;
     }
 
     i = mPendingRequests.indexOfKey(cseq);
 
     if (i < 0) {
-        // Unsolicited response?
-        TRESPASS();
+        return -ENOENT;
+    }
+
+    *index = i;
+
+    return OK;
+}
+
+bool ARTSPConnection::notifyResponseListener(
+        const sp<ARTSPResponse> &response) {
+    ssize_t i;
+    status_t err = findPendingRequest(response, &i);
+
+    if (err == OK && i < 0) {
+        // An unsolicited server response is not a problem.
+        return true;
+    }
+
+    if (err != OK) {
+        return false;
     }
 
     sp<AMessage> reply = mPendingRequests.valueAt(i);
@@ -660,4 +749,160 @@
     return true;
 }
 
+bool ARTSPConnection::parseAuthMethod(const sp<ARTSPResponse> &response) {
+    ssize_t i = response->mHeaders.indexOfKey("www-authenticate");
+
+    if (i < 0) {
+        return false;
+    }
+
+    AString value = response->mHeaders.valueAt(i);
+
+    if (!strncmp(value.c_str(), "Basic", 5)) {
+        mAuthType = BASIC;
+    } else {
+#if !defined(HAVE_ANDROID_OS)
+        // We don't have access to the MD5 implementation on the simulator,
+        // so we won't support digest authentication.
+        return false;
+#endif
+
+        CHECK(!strncmp(value.c_str(), "Digest", 6));
+        mAuthType = DIGEST;
+
+        i = value.find("nonce=");
+        CHECK_GE(i, 0);
+        CHECK_EQ(value.c_str()[i + 6], '\"');
+        ssize_t j = value.find("\"", i + 7);
+        CHECK_GE(j, 0);
+
+        mNonce.setTo(value, i + 7, j - i - 7);
+    }
+
+    return true;
+}
+
+#if defined(HAVE_ANDROID_OS)
+static void H(const AString &s, AString *out) {
+    out->clear();
+
+    MD5_CTX m;
+    MD5_Init(&m);
+    MD5_Update(&m, s.c_str(), s.size());
+
+    uint8_t key[16];
+    MD5_Final(key, &m);
+
+    for (size_t i = 0; i < 16; ++i) {
+        char nibble = key[i] >> 4;
+        if (nibble <= 9) {
+            nibble += '0';
+        } else {
+            nibble += 'a' - 10;
+        }
+        out->append(&nibble, 1);
+
+        nibble = key[i] & 0x0f;
+        if (nibble <= 9) {
+            nibble += '0';
+        } else {
+            nibble += 'a' - 10;
+        }
+        out->append(&nibble, 1);
+    }
+}
+#endif
+
+static void GetMethodAndURL(
+        const AString &request, AString *method, AString *url) {
+    ssize_t space1 = request.find(" ");
+    CHECK_GE(space1, 0);
+
+    ssize_t space2 = request.find(" ", space1 + 1);
+    CHECK_GE(space2, 0);
+
+    method->setTo(request, 0, space1);
+    url->setTo(request, space1 + 1, space2 - space1);
+}
+
+void ARTSPConnection::addAuthentication(AString *request) {
+    if (mAuthType == NONE) {
+        return;
+    }
+
+    // Find the boundary between headers and the body.
+    ssize_t i = request->find("\r\n\r\n");
+    CHECK_GE(i, 0);
+
+    if (mAuthType == BASIC) {
+        AString tmp;
+        tmp.append(mUser);
+        tmp.append(":");
+        tmp.append(mPass);
+
+        AString out;
+        encodeBase64(tmp.c_str(), tmp.size(), &out);
+
+        AString fragment;
+        fragment.append("Authorization: Basic ");
+        fragment.append(out);
+        fragment.append("\r\n");
+
+        request->insert(fragment, i + 2);
+
+        return;
+    }
+
+#if defined(HAVE_ANDROID_OS)
+    CHECK_EQ((int)mAuthType, (int)DIGEST);
+
+    AString method, url;
+    GetMethodAndURL(*request, &method, &url);
+
+    AString A1;
+    A1.append(mUser);
+    A1.append(":");
+    A1.append("Streaming Server");
+    A1.append(":");
+    A1.append(mPass);
+
+    AString A2;
+    A2.append(method);
+    A2.append(":");
+    A2.append(url);
+
+    AString HA1, HA2;
+    H(A1, &HA1);
+    H(A2, &HA2);
+
+    AString tmp;
+    tmp.append(HA1);
+    tmp.append(":");
+    tmp.append(mNonce);
+    tmp.append(":");
+    tmp.append(HA2);
+
+    AString digest;
+    H(tmp, &digest);
+
+    AString fragment;
+    fragment.append("Authorization: Digest ");
+    fragment.append("nonce=\"");
+    fragment.append(mNonce);
+    fragment.append("\", ");
+    fragment.append("username=\"");
+    fragment.append(mUser);
+    fragment.append("\", ");
+    fragment.append("uri=\"");
+    fragment.append(url);
+    fragment.append("\", ");
+    fragment.append("response=\"");
+    fragment.append(digest);
+    fragment.append("\"");
+    fragment.append("\r\n");
+
+    request->insert(fragment, i + 2);
+#endif
+}
+
 }  // namespace android
diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h
index 96e0d5b..19be2a6 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.h
+++ b/media/libstagefright/rtsp/ARTSPConnection.h
@@ -42,6 +42,10 @@
 
     void observeBinaryData(const sp<AMessage> &reply);
 
+    static bool ParseURL(
+            const char *url, AString *host, unsigned *port, AString *path,
+            AString *user, AString *pass);
+
 protected:
     virtual ~ARTSPConnection();
     virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -62,9 +66,18 @@
         kWhatObserveBinaryData  = 'obin',
     };
 
+    enum AuthType {
+        NONE,
+        BASIC,
+        DIGEST
+    };
+
     static const int64_t kSelectTimeoutUs;
 
     State mState;
+    AString mUser, mPass;
+    AuthType mAuthType;
+    AString mNonce;
     int mSocket;
     int32_t mConnectionID;
     int32_t mNextCSeq;
@@ -90,8 +103,11 @@
     sp<ABuffer> receiveBinaryData();
     bool notifyResponseListener(const sp<ARTSPResponse> &response);
 
-    static bool ParseURL(
-            const char *url, AString *host, unsigned *port, AString *path);
+    bool parseAuthMethod(const sp<ARTSPResponse> &response);
+    void addAuthentication(AString *request);
+
+    status_t findPendingRequest(
+            const sp<ARTSPResponse> &response, ssize_t *index) const;
 
     static bool ParseSingleUnsignedLong(
             const char *from, unsigned long *x);
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 612caff..547fbab 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -53,21 +53,30 @@
     mFormats.push(AString("[root]"));
 
     AString desc((const char *)data, size);
-    LOGI("%s", desc.c_str());
 
     size_t i = 0;
     for (;;) {
-        ssize_t eolPos = desc.find("\r\n", i);
+        ssize_t eolPos = desc.find("\n", i);
+
         if (eolPos < 0) {
             break;
         }
 
-        AString line(desc, i, eolPos - i);
+        AString line;
+        if ((size_t)eolPos > i && desc.c_str()[eolPos - 1] == '\r') {
+            // We accept both '\n' and '\r\n' line endings, if it's
+            // the latter, strip the '\r' as well.
+            line.setTo(desc, i, eolPos - i - 1);
+        } else {
+            line.setTo(desc, i, eolPos - i);
+        }
 
         if (line.size() < 2 || line.c_str()[1] != '=') {
             return false;
         }
 
+        LOGI("%s", line.c_str());
+
         switch (line.c_str()[0]) {
             case 'v':
             {
@@ -141,7 +150,7 @@
             }
         }
 
-        i = eolPos + 2;
+        i = eolPos + 1;
     }
 
     return true;
@@ -245,7 +254,7 @@
         return false;
     }
 
-    if (value == "npt=now-") {
+    if (value == "npt=now-" || value == "npt=0-") {
         return false;
     }
 
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index 081ae32..0bbadc1 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -23,6 +23,7 @@
 	$(JNI_H_INCLUDE) \
 	$(TOP)/frameworks/base/include/media/stagefright/openmax \
         $(TOP)/frameworks/base/media/libstagefright/include \
+        $(TOP)/external/openssl/include
 
 LOCAL_MODULE:= libstagefright_rtsp
 
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 6943608..9bb8c46 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -96,6 +96,7 @@
           mNetLooper(new ALooper),
           mConn(new ARTSPConnection),
           mRTPConn(new ARTPConnection),
+          mOriginalSessionURL(url),
           mSessionURL(url),
           mSetupTracksSuccessful(false),
           mSeekPending(false),
@@ -113,6 +114,23 @@
         mNetLooper->start(false /* runOnCallingThread */,
                           false /* canCallJava */,
                           PRIORITY_HIGHEST);
+
+        // Strip any authentication info from the session url, we don't
+        // want to transmit user/pass in cleartext.
+        AString host, path, user, pass;
+        unsigned port;
+        if (ARTSPConnection::ParseURL(
+                    mSessionURL.c_str(), &host, &port, &path, &user, &pass)
+                && user.size() > 0) {
+            mSessionURL.clear();
+            mSessionURL.append("rtsp://");
+            mSessionURL.append(host);
+            mSessionURL.append(":");
+            mSessionURL.append(StringPrintf("%u", port));
+            mSessionURL.append(path);
+
+            LOGI("rewritten session url: '%s'", mSessionURL.c_str());
+        }
     }
 
     void connect(const sp<AMessage> &doneMsg) {
@@ -126,7 +144,7 @@
         mConn->observeBinaryData(notify);
 
         sp<AMessage> reply = new AMessage('conn', id());
-        mConn->connect(mSessionURL.c_str(), reply);
+        mConn->connect(mOriginalSessionURL.c_str(), reply);
     }
 
     void disconnect(const sp<AMessage> &doneMsg) {
@@ -312,7 +330,7 @@
                 int32_t reconnect;
                 if (msg->findInt32("reconnect", &reconnect) && reconnect) {
                     sp<AMessage> reply = new AMessage('conn', id());
-                    mConn->connect(mSessionURL.c_str(), reply);
+                    mConn->connect(mOriginalSessionURL.c_str(), reply);
                 } else {
                     (new AMessage('quit', id()))->post();
                 }
@@ -922,7 +940,7 @@
         CHECK(GetAttribute(range.c_str(), "npt", &val));
         float npt1, npt2;
 
-        if (val == "now-") {
+        if (val == "now-" || val == "0-") {
             // This is a live stream and therefore not seekable.
             return;
         } else {
@@ -992,6 +1010,7 @@
     sp<ARTSPConnection> mConn;
     sp<ARTPConnection> mRTPConn;
     sp<ASessionDescription> mSessionDesc;
+    AString mOriginalSessionURL;  // This one still has user:pass@
     AString mSessionURL;
     AString mBaseURL;
     AString mSessionID;
diff --git a/media/libstagefright/yuv/Android.mk b/media/libstagefright/yuv/Android.mk
new file mode 100644
index 0000000..0794ad1
--- /dev/null
+++ b/media/libstagefright/yuv/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=               \
+        YUVImage.cpp            \
+        YUVCanvas.cpp
+
+LOCAL_SHARED_LIBRARIES :=       \
+        libcutils
+
+LOCAL_MODULE:= libstagefright_yuv
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/yuv/YUVCanvas.cpp b/media/libstagefright/yuv/YUVCanvas.cpp
new file mode 100644
index 0000000..38aa779
--- /dev/null
+++ b/media/libstagefright/yuv/YUVCanvas.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "YUVCanvas"
+
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/YUVCanvas.h>
+#include <media/stagefright/YUVImage.h>
+#include <ui/Rect.h>
+
+namespace android {
+
+YUVCanvas::YUVCanvas(YUVImage &yuvImage)
+    : mYUVImage(yuvImage) {
+}
+
+YUVCanvas::~YUVCanvas() {
+}
+
+void YUVCanvas::FillYUV(uint8_t yValue, uint8_t uValue, uint8_t vValue) {
+    for (int32_t y = 0; y < mYUVImage.height(); ++y) {
+        for (int32_t x = 0; x < mYUVImage.width(); ++x) {
+            mYUVImage.setPixelValue(x, y, yValue, uValue, vValue);
+        }
+    }
+}
+
+void YUVCanvas::FillYUVRectangle(const Rect& rect,
+        uint8_t yValue, uint8_t uValue, uint8_t vValue) {
+    for (int32_t y = rect.top; y < rect.bottom; ++y) {
+        for (int32_t x = rect.left; x < rect.right; ++x) {
+            mYUVImage.setPixelValue(x, y, yValue, uValue, vValue);
+        }
+    }
+}
+
+void YUVCanvas::CopyImageRect(
+        const Rect& srcRect,
+        int32_t destStartX, int32_t destStartY,
+        const YUVImage &srcImage) {
+
+    // Try fast copy first
+    if (YUVImage::fastCopyRectangle(
+                srcRect,
+                destStartX, destStartY,
+                srcImage, mYUVImage)) {
+        return;
+    }
+
+    int32_t srcStartX = srcRect.left;
+    int32_t srcStartY = srcRect.top;
+    for (int32_t offsetY = 0; offsetY < srcRect.height(); ++offsetY) {
+        for (int32_t offsetX = 0; offsetX < srcRect.width(); ++offsetX) {
+            int32_t srcX = srcStartX + offsetX;
+            int32_t srcY = srcStartY + offsetY;
+
+            int32_t destX = destStartX + offsetX;
+            int32_t destY = destStartY + offsetY;
+
+            uint8_t yValue;
+            uint8_t uValue;
+            uint8_t vValue;
+
+            srcImage.getPixelValue(srcX, srcY, &yValue, &uValue, &vValue);
+            mYUVImage.setPixelValue(destX, destY, yValue, uValue, vValue);
+        }
+    }
+}
+
+void YUVCanvas::downsample(
+        int32_t srcOffsetX, int32_t srcOffsetY,
+        int32_t skipX, int32_t skipY,
+        const YUVImage &srcImage) {
+    // TODO: Add a low pass filter for downsampling.
+
+    // Check that srcImage is big enough to fill mYUVImage.
+    CHECK((srcOffsetX + (mYUVImage.width() - 1) * skipX) < srcImage.width());
+    CHECK((srcOffsetY + (mYUVImage.height() - 1) * skipY) < srcImage.height());
+
+    uint8_t yValue;
+    uint8_t uValue;
+    uint8_t vValue;
+
+    int32_t srcY = srcOffsetY;
+    for (int32_t y = 0; y < mYUVImage.height(); ++y) {
+        int32_t srcX = srcOffsetX;
+        for (int32_t x = 0; x < mYUVImage.width(); ++x) {
+            srcImage.getPixelValue(srcX, srcY, &yValue, &uValue, &vValue);
+            mYUVImage.setPixelValue(x, y, yValue, uValue, vValue);
+
+            srcX += skipX;
+        }
+        srcY += skipY;
+    }
+}
+
+}  // namespace android
diff --git a/media/libstagefright/yuv/YUVImage.cpp b/media/libstagefright/yuv/YUVImage.cpp
new file mode 100644
index 0000000..b712062
--- /dev/null
+++ b/media/libstagefright/yuv/YUVImage.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "YUVImage"
+
+#include <media/stagefright/YUVImage.h>
+#include <ui/Rect.h>
+#include <media/stagefright/MediaDebug.h>
+
+namespace android {
+
+YUVImage::YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height) {
+    mYUVFormat = yuvFormat;
+    mWidth = width;
+    mHeight = height;
+
+    size_t numberOfBytes = bufferSize(yuvFormat, width, height);
+    uint8_t *buffer = new uint8_t[numberOfBytes];
+    mBuffer = buffer;
+    mOwnBuffer = true;
+
+    initializeYUVPointers();
+}
+
+YUVImage::YUVImage(YUVFormat yuvFormat, int32_t width, int32_t height, uint8_t *buffer) {
+    mYUVFormat = yuvFormat;
+    mWidth = width;
+    mHeight = height;
+    mBuffer = buffer;
+    mOwnBuffer = false;
+
+    initializeYUVPointers();
+}
+
+//static
+size_t YUVImage::bufferSize(YUVFormat yuvFormat, int32_t width, int32_t height) {
+    int32_t numberOfPixels = width*height;
+    size_t numberOfBytes = 0;
+    if (yuvFormat == YUV420Planar || yuvFormat == YUV420SemiPlanar) {
+        // Y takes numberOfPixels bytes and U/V take numberOfPixels/4 bytes each.
+        numberOfBytes = (size_t)(numberOfPixels + (numberOfPixels >> 1));
+    } else {
+        LOGE("Format not supported");
+    }
+    return numberOfBytes;
+}
+
+bool YUVImage::initializeYUVPointers() {
+    int32_t numberOfPixels = mWidth * mHeight;
+
+    if (mYUVFormat == YUV420Planar) {
+        mYdata = (uint8_t *)mBuffer;
+        mUdata = mYdata + numberOfPixels;
+        mVdata = mUdata + (numberOfPixels >> 2);
+    } else if (mYUVFormat == YUV420SemiPlanar) {
+        // U and V channels are interleaved as VUVUVU.
+        // So V data starts at the end of Y channel and
+        // U data starts right after V's start.
+        mYdata = (uint8_t *)mBuffer;
+        mVdata = mYdata + numberOfPixels;
+        mUdata = mVdata + 1;
+    } else {
+        LOGE("Format not supported");
+        return false;
+    }
+    return true;
+}
+
+YUVImage::~YUVImage() {
+    if (mOwnBuffer) delete[] mBuffer;
+}
+
+bool YUVImage::getOffsets(int32_t x, int32_t y,
+        int32_t *yOffset, int32_t *uOffset, int32_t *vOffset) const {
+    *yOffset = y*mWidth + x;
+
+    int32_t uvOffset = (y >> 1) * (mWidth >> 1) + (x >> 1);
+    if (mYUVFormat == YUV420Planar) {
+        *uOffset = uvOffset;
+        *vOffset = uvOffset;
+    } else if (mYUVFormat == YUV420SemiPlanar) {
+        // Since U and V channels are interleaved, offsets need
+        // to be doubled.
+        *uOffset = 2*uvOffset;
+        *vOffset = 2*uvOffset;
+    } else {
+        LOGE("Format not supported");
+        return false;
+    }
+
+    return true;
+}
+
+bool YUVImage::getOffsetIncrementsPerDataRow(
+        int32_t *yDataOffsetIncrement,
+        int32_t *uDataOffsetIncrement,
+        int32_t *vDataOffsetIncrement) const {
+    *yDataOffsetIncrement = mWidth;
+
+    int32_t uvDataOffsetIncrement = mWidth >> 1;
+
+    if (mYUVFormat == YUV420Planar) {
+        *uDataOffsetIncrement = uvDataOffsetIncrement;
+        *vDataOffsetIncrement = uvDataOffsetIncrement;
+    } else if (mYUVFormat == YUV420SemiPlanar) {
+        // Since U and V channels are interleaved, offsets need
+        // to be doubled.
+        *uDataOffsetIncrement = 2*uvDataOffsetIncrement;
+        *vDataOffsetIncrement = 2*uvDataOffsetIncrement;
+    } else {
+        LOGE("Format not supported");
+        return false;
+    }
+
+    return true;
+}
+
+uint8_t* YUVImage::getYAddress(int32_t offset) const {
+    return mYdata + offset;
+}
+
+uint8_t* YUVImage::getUAddress(int32_t offset) const {
+    return mUdata + offset;
+}
+
+uint8_t* YUVImage::getVAddress(int32_t offset) const {
+    return mVdata + offset;
+}
+
+bool YUVImage::getYUVAddresses(int32_t x, int32_t y,
+        uint8_t **yAddr, uint8_t **uAddr, uint8_t **vAddr) const {
+    int32_t yOffset;
+    int32_t uOffset;
+    int32_t vOffset;
+    if (!getOffsets(x, y, &yOffset, &uOffset, &vOffset)) return false;
+
+    *yAddr = getYAddress(yOffset);
+    *uAddr = getUAddress(uOffset);
+    *vAddr = getVAddress(vOffset);
+
+    return true;
+}
+
+bool YUVImage::validPixel(int32_t x, int32_t y) const {
+    return (x >= 0 && x < mWidth &&
+            y >= 0 && y < mHeight);
+}
+
+bool YUVImage::getPixelValue(int32_t x, int32_t y,
+        uint8_t *yPtr, uint8_t *uPtr, uint8_t *vPtr) const {
+    CHECK(validPixel(x, y));
+
+    uint8_t *yAddr;
+    uint8_t *uAddr;
+    uint8_t *vAddr;
+    if (!getYUVAddresses(x, y, &yAddr, &uAddr, &vAddr)) return false;
+
+    *yPtr = *yAddr;
+    *uPtr = *uAddr;
+    *vPtr = *vAddr;
+
+    return true;
+}
+
+bool YUVImage::setPixelValue(int32_t x, int32_t y,
+        uint8_t yValue, uint8_t uValue, uint8_t vValue) {
+    CHECK(validPixel(x, y));
+
+    uint8_t *yAddr;
+    uint8_t *uAddr;
+    uint8_t *vAddr;
+    if (!getYUVAddresses(x, y, &yAddr, &uAddr, &vAddr)) return false;
+
+    *yAddr = yValue;
+    *uAddr = uValue;
+    *vAddr = vValue;
+
+    return true;
+}
+
+void YUVImage::fastCopyRectangle420Planar(
+        const Rect& srcRect,
+        int32_t destStartX, int32_t destStartY,
+        const YUVImage &srcImage, YUVImage &destImage) {
+    CHECK(srcImage.mYUVFormat == YUV420Planar);
+    CHECK(destImage.mYUVFormat == YUV420Planar);
+
+    int32_t srcStartX = srcRect.left;
+    int32_t srcStartY = srcRect.top;
+    int32_t width = srcRect.width();
+    int32_t height = srcRect.height();
+
+    // Get source and destination start addresses
+    uint8_t *ySrcAddrBase;
+    uint8_t *uSrcAddrBase;
+    uint8_t *vSrcAddrBase;
+    srcImage.getYUVAddresses(srcStartX, srcStartY,
+            &ySrcAddrBase, &uSrcAddrBase, &vSrcAddrBase);
+
+    uint8_t *yDestAddrBase;
+    uint8_t *uDestAddrBase;
+    uint8_t *vDestAddrBase;
+    destImage.getYUVAddresses(destStartX, destStartY,
+            &yDestAddrBase, &uDestAddrBase, &vDestAddrBase);
+
+    // Get source and destination offset increments incurred in going
+    // from one data row to next.
+    int32_t ySrcOffsetIncrement;
+    int32_t uSrcOffsetIncrement;
+    int32_t vSrcOffsetIncrement;
+    srcImage.getOffsetIncrementsPerDataRow(
+            &ySrcOffsetIncrement, &uSrcOffsetIncrement, &vSrcOffsetIncrement);
+
+    int32_t yDestOffsetIncrement;
+    int32_t uDestOffsetIncrement;
+    int32_t vDestOffsetIncrement;
+    destImage.getOffsetIncrementsPerDataRow(
+            &yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement);
+
+    // Copy Y
+    {
+        size_t numberOfYBytesPerRow = (size_t) width;
+        uint8_t *ySrcAddr = ySrcAddrBase;
+        uint8_t *yDestAddr = yDestAddrBase;
+        for (int32_t offsetY = 0; offsetY < height; ++offsetY) {
+            memcpy(yDestAddr, ySrcAddr, numberOfYBytesPerRow);
+
+            ySrcAddr += ySrcOffsetIncrement;
+            yDestAddr += yDestOffsetIncrement;
+        }
+    }
+
+    // Copy U
+    {
+        size_t numberOfUBytesPerRow = (size_t) (width >> 1);
+        uint8_t *uSrcAddr = uSrcAddrBase;
+        uint8_t *uDestAddr = uDestAddrBase;
+        // Every other row has an entry for U/V channel values. Hence only
+        // go half the height.
+        for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
+            memcpy(uDestAddr, uSrcAddr, numberOfUBytesPerRow);
+
+            uSrcAddr += uSrcOffsetIncrement;
+            uDestAddr += uDestOffsetIncrement;
+        }
+    }
+
+    // Copy V
+    {
+        size_t numberOfVBytesPerRow = (size_t) (width >> 1);
+        uint8_t *vSrcAddr = vSrcAddrBase;
+        uint8_t *vDestAddr = vDestAddrBase;
+        // Every other pixel row has a U/V data row. Hence only go half the height.
+        for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
+            memcpy(vDestAddr, vSrcAddr, numberOfVBytesPerRow);
+
+            vSrcAddr += vSrcOffsetIncrement;
+            vDestAddr += vDestOffsetIncrement;
+        }
+    }
+}
+
+void YUVImage::fastCopyRectangle420SemiPlanar(
+        const Rect& srcRect,
+        int32_t destStartX, int32_t destStartY,
+        const YUVImage &srcImage, YUVImage &destImage) {
+    CHECK(srcImage.mYUVFormat == YUV420SemiPlanar);
+    CHECK(destImage.mYUVFormat == YUV420SemiPlanar);
+
+    int32_t srcStartX = srcRect.left;
+    int32_t srcStartY = srcRect.top;
+    int32_t width = srcRect.width();
+    int32_t height = srcRect.height();
+
+    // Get source and destination start addresses
+    uint8_t *ySrcAddrBase;
+    uint8_t *uSrcAddrBase;
+    uint8_t *vSrcAddrBase;
+    srcImage.getYUVAddresses(srcStartX, srcStartY,
+            &ySrcAddrBase, &uSrcAddrBase, &vSrcAddrBase);
+
+    uint8_t *yDestAddrBase;
+    uint8_t *uDestAddrBase;
+    uint8_t *vDestAddrBase;
+    destImage.getYUVAddresses(destStartX, destStartY,
+            &yDestAddrBase, &uDestAddrBase, &vDestAddrBase);
+
+    // Get source and destination offset increments incurred in going
+    // from one data row to next.
+    int32_t ySrcOffsetIncrement;
+    int32_t uSrcOffsetIncrement;
+    int32_t vSrcOffsetIncrement;
+    srcImage.getOffsetIncrementsPerDataRow(
+            &ySrcOffsetIncrement, &uSrcOffsetIncrement, &vSrcOffsetIncrement);
+
+    int32_t yDestOffsetIncrement;
+    int32_t uDestOffsetIncrement;
+    int32_t vDestOffsetIncrement;
+    destImage.getOffsetIncrementsPerDataRow(
+            &yDestOffsetIncrement, &uDestOffsetIncrement, &vDestOffsetIncrement);
+
+    // Copy Y
+    {
+        size_t numberOfYBytesPerRow = (size_t) width;
+        uint8_t *ySrcAddr = ySrcAddrBase;
+        uint8_t *yDestAddr = yDestAddrBase;
+        for (int32_t offsetY = 0; offsetY < height; ++offsetY) {
+            memcpy(yDestAddr, ySrcAddr, numberOfYBytesPerRow);
+
+            ySrcAddr = ySrcAddr + ySrcOffsetIncrement;
+            yDestAddr = yDestAddr + yDestOffsetIncrement;
+        }
+    }
+
+    // Copy UV
+    {
+        // UV are interleaved. So number of UV bytes per row is 2*(width/2).
+        size_t numberOfUVBytesPerRow = (size_t) width;
+        uint8_t *vSrcAddr = vSrcAddrBase;
+        uint8_t *vDestAddr = vDestAddrBase;
+        // Every other pixel row has a U/V data row. Hence only go half the height.
+        for (int32_t offsetY = 0; offsetY < (height >> 1); ++offsetY) {
+            memcpy(vDestAddr, vSrcAddr, numberOfUVBytesPerRow);
+
+            vSrcAddr += vSrcOffsetIncrement;
+            vDestAddr += vDestOffsetIncrement;
+        }
+    }
+}
+
+// static
+bool YUVImage::fastCopyRectangle(
+        const Rect& srcRect,
+        int32_t destStartX, int32_t destStartY,
+        const YUVImage &srcImage, YUVImage &destImage) {
+    if (srcImage.mYUVFormat == destImage.mYUVFormat) {
+        if (srcImage.mYUVFormat == YUV420Planar) {
+            fastCopyRectangle420Planar(
+                    srcRect,
+                    destStartX, destStartY,
+                    srcImage, destImage);
+        } else if (srcImage.mYUVFormat == YUV420SemiPlanar) {
+            fastCopyRectangle420SemiPlanar(
+                    srcRect,
+                    destStartX, destStartY,
+                    srcImage, destImage);
+        }
+        return true;
+    }
+    return false;
+}
+
+uint8_t clamp(uint8_t v, uint8_t minValue, uint8_t maxValue) {
+    CHECK(maxValue >= minValue);
+
+    if (v < minValue) return minValue;
+    else if (v > maxValue) return maxValue;
+    else return v;
+}
+
+void YUVImage::yuv2rgb(uint8_t yValue, uint8_t uValue, uint8_t vValue,
+        uint8_t *r, uint8_t *g, uint8_t *b) const {
+    *r = yValue + (1.370705 * (vValue-128));
+    *g = yValue - (0.698001 * (vValue-128)) - (0.337633 * (uValue-128));
+    *b = yValue + (1.732446 * (uValue-128));
+
+    *r = clamp(*r, 0, 255);
+    *g = clamp(*g, 0, 255);
+    *b = clamp(*b, 0, 255);
+}
+
+bool YUVImage::writeToPPM(const char *filename) const {
+    FILE *fp = fopen(filename, "w");
+    if (fp == NULL) {
+        return false;
+    }
+    fprintf(fp, "P3\n");
+    fprintf(fp, "%d %d\n", mWidth, mHeight);
+    fprintf(fp, "255\n");
+    for (int32_t y = 0; y < mHeight; ++y) {
+        for (int32_t x = 0; x < mWidth; ++x) {
+            uint8_t yValue;
+            uint8_t uValue;
+            uint8_t vValue;
+            getPixelValue(x, y, &yValue, &uValue, & vValue);
+
+            uint8_t rValue;
+            uint8_t gValue;
+            uint8_t bValue;
+            yuv2rgb(yValue, uValue, vValue, &rValue, &gValue, &bValue);
+
+            fprintf(fp, "%d %d %d\n", (int32_t)rValue, (int32_t)gValue, (int32_t)bValue);
+        }
+    }
+    fclose(fp);
+    return true;
+}
+
+}  // namespace android
diff --git a/media/libwvm/Android.mk b/media/libwvm/Android.mk
new file mode 100644
index 0000000..7c75c0f
--- /dev/null
+++ b/media/libwvm/Android.mk
@@ -0,0 +1,10 @@
+# create an empty mk for libwvm
+# for integration purpose
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libwvm
+
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
new file mode 100644
index 0000000..7502f6e
--- /dev/null
+++ b/media/mtp/Android.mk
@@ -0,0 +1,78 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=                                       \
+                  MtpClient.cpp                         \
+                  MtpCursor.cpp                         \
+                  MtpDataPacket.cpp                     \
+                  MtpDebug.cpp                          \
+                  MtpDevice.cpp                         \
+                  MtpEventPacket.cpp                    \
+                  MtpDeviceInfo.cpp                     \
+                  MtpObjectInfo.cpp                     \
+                  MtpPacket.cpp                         \
+                  MtpProperty.cpp                       \
+                  MtpRequestPacket.cpp                  \
+                  MtpResponsePacket.cpp                 \
+                  MtpServer.cpp                         \
+                  MtpStorageInfo.cpp                    \
+                  MtpStringBuffer.cpp                   \
+                  MtpStorage.cpp                        \
+                  MtpUtils.cpp                          \
+
+LOCAL_MODULE:= libmtp
+
+LOCAL_CFLAGS := -DMTP_DEVICE -DMTP_HOST
+
+include $(BUILD_STATIC_LIBRARY)
+
+endif
+
+ifeq ($(HOST_OS),linux)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=                                       \
+                  MtpClient.cpp                         \
+                  MtpCursor.cpp                         \
+                  MtpDataPacket.cpp                     \
+                  MtpDebug.cpp                          \
+                  MtpDevice.cpp                         \
+                  MtpEventPacket.cpp                    \
+                  MtpDeviceInfo.cpp                     \
+                  MtpObjectInfo.cpp                     \
+                  MtpPacket.cpp                         \
+                  MtpProperty.cpp                       \
+                  MtpRequestPacket.cpp                  \
+                  MtpResponsePacket.cpp                 \
+                  MtpStorageInfo.cpp                    \
+                  MtpStringBuffer.cpp                   \
+                  MtpStorage.cpp                        \
+                  MtpUtils.cpp                          \
+
+LOCAL_MODULE:= libmtp
+
+LOCAL_CFLAGS := -DMTP_HOST
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+endif
diff --git a/media/mtp/MtpClient.cpp b/media/mtp/MtpClient.cpp
new file mode 100644
index 0000000..ceb6a43
--- /dev/null
+++ b/media/mtp/MtpClient.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpClient"
+
+#include "MtpDebug.h"
+#include "MtpClient.h"
+#include "MtpDevice.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <usbhost/usbhost.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
+#include <linux/usb/ch9.h>
+#else
+#include <linux/usb_ch9.h>
+#endif
+
+namespace android {
+
+static bool isMtpDevice(uint16_t vendor, uint16_t product) {
+    // Sandisk Sansa Fuze
+    if (vendor == 0x0781 && product == 0x74c2)
+        return true;
+    // Samsung YP-Z5
+    if (vendor == 0x04e8 && product == 0x503c)
+        return true;
+    return false;
+}
+
+class MtpClientThread : public Thread {
+private:
+    MtpClient*   mClient;
+
+public:
+    MtpClientThread(MtpClient* client)
+        : mClient(client)
+    {
+    }
+
+    virtual bool threadLoop() {
+        return mClient->threadLoop();
+    }
+};
+
+
+MtpClient::MtpClient()
+    :   mThread(NULL),
+        mUsbHostContext(NULL),
+        mDone(false)
+{
+}
+
+MtpClient::~MtpClient() {
+    usb_host_cleanup(mUsbHostContext);
+}
+
+bool MtpClient::start() {
+    Mutex::Autolock autoLock(mMutex);
+
+    if (mThread)
+        return true;
+
+    mUsbHostContext = usb_host_init();
+    if (!mUsbHostContext)
+        return false;
+
+    mThread = new MtpClientThread(this);
+    mThread->run("MtpClientThread");
+    // wait for the thread to do initial device discovery before returning
+    mThreadStartCondition.wait(mMutex);
+
+    return true;
+}
+
+void MtpClient::stop() {
+    mDone = true;
+}
+
+MtpDevice* MtpClient::getDevice(int id) {
+    for (int i = 0; i < mDeviceList.size(); i++) {
+        MtpDevice* device = mDeviceList[i];
+        if (device->getID() == id)
+            return device;
+    }
+    return NULL;
+}
+
+bool MtpClient::usbDeviceAdded(const char *devname) {
+    struct usb_descriptor_header* desc;
+    struct usb_descriptor_iter iter;
+
+    struct usb_device *device = usb_device_open(devname);
+    if (!device) {
+        LOGE("usb_device_open failed\n");
+        return mDone;
+    }
+
+    usb_descriptor_iter_init(device, &iter);
+
+    while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
+        if (desc->bDescriptorType == USB_DT_INTERFACE) {
+            struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
+
+            if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
+                interface->bInterfaceSubClass == 1 && // Still Image Capture
+                interface->bInterfaceProtocol == 1)     // Picture Transfer Protocol (PIMA 15470)
+            {
+                LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
+                        usb_device_get_product_name(device));
+            } else if (interface->bInterfaceClass == 0xFF &&
+                    interface->bInterfaceSubClass == 0xFF &&
+                    interface->bInterfaceProtocol == 0) {
+                char* interfaceName = usb_device_get_string(device, interface->iInterface);
+                if (!interfaceName || strcmp(interfaceName, "MTP"))
+                    continue;
+                // Looks like an android style MTP device
+                LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
+                        usb_device_get_product_name(device));
+            } else {
+                // look for special cased devices based on vendor/product ID
+                // we are doing this mainly for testing purposes
+                uint16_t vendor = usb_device_get_vendor_id(device);
+                uint16_t product = usb_device_get_product_id(device);
+                if (!isMtpDevice(vendor, product)) {
+                    // not an MTP or PTP device
+                    continue;
+                }
+                // request MTP OS string and descriptor
+                // some music players need to see this before entering MTP mode.
+                char buffer[256];
+                memset(buffer, 0, sizeof(buffer));
+                int ret = usb_device_send_control(device,
+                        USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
+                        USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
+                        0, sizeof(buffer), buffer);
+                printf("usb_device_send_control returned %d errno: %d\n", ret, errno);
+                if (ret > 0) {
+                    printf("got MTP string %s\n", buffer);
+                    ret = usb_device_send_control(device,
+                            USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
+                            0, 4, sizeof(buffer), buffer);
+                    printf("OS descriptor got %d\n", ret);
+                } else {
+                    printf("no MTP string\n");
+                }
+            }
+
+            // if we got here, then we have a likely MTP or PTP device
+
+            // interface should be followed by three endpoints
+            struct usb_endpoint_descriptor *ep;
+            struct usb_endpoint_descriptor *ep_in_desc = NULL;
+            struct usb_endpoint_descriptor *ep_out_desc = NULL;
+            struct usb_endpoint_descriptor *ep_intr_desc = NULL;
+            for (int i = 0; i < 3; i++) {
+                ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
+                if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
+                    LOGE("endpoints not found\n");
+                    return mDone;
+                }
+                if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+                    if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+                        ep_in_desc = ep;
+                    else
+                        ep_out_desc = ep;
+                } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
+                    ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+                    ep_intr_desc = ep;
+                }
+            }
+            if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
+                LOGE("endpoints not found\n");
+                return mDone;
+            }
+
+            struct usb_endpoint *ep_in = usb_endpoint_open(device, ep_in_desc);
+            struct usb_endpoint *ep_out = usb_endpoint_open(device, ep_out_desc);
+            struct usb_endpoint *ep_intr = usb_endpoint_open(device, ep_intr_desc);
+
+            if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
+                LOGE("usb_device_claim_interface failed errno: %d\n", errno);
+                usb_endpoint_close(ep_in);
+                usb_endpoint_close(ep_out);
+                usb_endpoint_close(ep_intr);
+                return mDone;
+            }
+
+            MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
+                        ep_in, ep_out, ep_intr);
+            mDeviceList.add(mtpDevice);
+            mtpDevice->initialize();
+            deviceAdded(mtpDevice);
+            return mDone;
+        }
+    }
+
+    usb_device_close(device);
+    return mDone;
+}
+
+bool MtpClient::usbDeviceRemoved(const char *devname) {
+    for (int i = 0; i < mDeviceList.size(); i++) {
+        MtpDevice* device = mDeviceList[i];
+        if (!strcmp(devname, device->getDeviceName())) {
+            deviceRemoved(device);
+            mDeviceList.removeAt(i);
+            delete device;
+            LOGD("Camera removed!\n");
+            break;
+        }
+    }
+    return mDone;
+}
+
+bool MtpClient::usbDiscoveryDone() {
+    Mutex::Autolock autoLock(mMutex);
+    mThreadStartCondition.signal();
+    return mDone;
+}
+
+bool MtpClient::threadLoop() {
+    usb_host_run(mUsbHostContext, usb_device_added, usb_device_removed, usb_discovery_done, this);
+    return false;
+}
+
+int MtpClient::usb_device_added(const char *devname, void* client_data) {
+    LOGD("usb_device_added %s\n", devname);
+    return ((MtpClient *)client_data)->usbDeviceAdded(devname);
+}
+
+int MtpClient::usb_device_removed(const char *devname, void* client_data) {
+    LOGD("usb_device_removed %s\n", devname);
+    return ((MtpClient *)client_data)->usbDeviceRemoved(devname);
+}
+
+int MtpClient::usb_discovery_done(void* client_data) {
+    LOGD("usb_discovery_done\n");
+    return ((MtpClient *)client_data)->usbDiscoveryDone();
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpClient.h b/media/mtp/MtpClient.h
new file mode 100644
index 0000000..fa5c527
--- /dev/null
+++ b/media/mtp/MtpClient.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_CLIENT_H
+#define _MTP_CLIENT_H
+
+#include "MtpTypes.h"
+
+#include <utils/threads.h>
+
+struct usb_host_context;
+
+namespace android {
+
+class MtpClientThread;
+
+class MtpClient {
+private:
+    MtpDeviceList               mDeviceList;
+    MtpClientThread*            mThread;
+    Condition                   mThreadStartCondition;
+    Mutex                       mMutex;
+    struct usb_host_context*    mUsbHostContext;
+    bool                        mDone;
+
+public:
+                            MtpClient();
+    virtual                 ~MtpClient();
+
+    bool                    start();
+    void                    stop();
+
+    inline MtpDeviceList&   getDeviceList() { return mDeviceList; }
+    MtpDevice*              getDevice(int id);
+
+
+    virtual void            deviceAdded(MtpDevice *device) = 0;
+    virtual void            deviceRemoved(MtpDevice *device) = 0;
+
+private:
+    // these return true if we should stop monitoring USB and clean up
+    bool                    usbDeviceAdded(const char *devname);
+    bool                    usbDeviceRemoved(const char *devname);
+    bool                    usbDiscoveryDone();
+
+    friend class MtpClientThread;
+    bool                    threadLoop();
+    static int              usb_device_added(const char *devname, void* client_data);
+    static int              usb_device_removed(const char *devname, void* client_data);
+    static int              usb_discovery_done(void* client_data);
+};
+
+}; // namespace android
+
+#endif // _MTP_CLIENT_H
diff --git a/media/mtp/MtpCursor.cpp b/media/mtp/MtpCursor.cpp
new file mode 100644
index 0000000..35d90dc
--- /dev/null
+++ b/media/mtp/MtpCursor.cpp
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpCursor"
+
+#include "MtpDebug.h"
+#include "MtpClient.h"
+#include "MtpCursor.h"
+#include "MtpDevice.h"
+#include "MtpDeviceInfo.h"
+#include "MtpObjectInfo.h"
+#include "MtpStorageInfo.h"
+
+
+#include "binder/CursorWindow.h"
+
+namespace android {
+
+/* Device Column IDs */
+/* These must match the values in MtpCursor.java */
+#define DEVICE_ROW_ID           1
+#define DEVICE_MANUFACTURER     2
+#define DEVICE_MODEL            3
+
+/* Storage Column IDs */
+/* These must match the values in MtpCursor.java */
+#define STORAGE_ROW_ID          101
+#define STORAGE_IDENTIFIER      102
+#define STORAGE_DESCRIPTION     103
+
+/* Object Column IDs */
+/* These must match the values in MtpCursor.java */
+#define OBJECT_ROW_ID               201
+#define OBJECT_STORAGE_ID           202
+#define OBJECT_FORMAT               203
+#define OBJECT_PROTECTION_STATUS    204
+#define OBJECT_SIZE                 205
+#define OBJECT_THUMB_FORMAT         206
+#define OBJECT_THUMB_SIZE           207
+#define OBJECT_THUMB_WIDTH          208
+#define OBJECT_THUMB_HEIGHT         209
+#define OBJECT_IMAGE_WIDTH          210
+#define OBJECT_IMAGE_HEIGHT         211
+#define OBJECT_IMAGE_DEPTH          212
+#define OBJECT_PARENT               213
+#define OBJECT_ASSOCIATION_TYPE     214
+#define OBJECT_ASSOCIATION_DESC     215
+#define OBJECT_SEQUENCE_NUMBER      216
+#define OBJECT_NAME                 217
+#define OBJECT_DATE_CREATED         218
+#define OBJECT_DATE_MODIFIED        219
+#define OBJECT_KEYWORDS             220
+#define OBJECT_THUMB                221
+
+MtpCursor::MtpCursor(MtpClient* client, int queryType, int deviceID,
+                MtpStorageID storageID, MtpObjectHandle objectID,
+                int columnCount, int* columns)
+        :   mClient(client),
+            mQueryType(queryType),
+            mDeviceID(deviceID),
+            mStorageID(storageID),
+            mQbjectID(objectID),
+            mColumnCount(columnCount),
+            mColumns(NULL)
+{
+    if (columns) {
+        mColumns = new int[columnCount];
+        memcpy(mColumns, columns, columnCount * sizeof(int));
+    }
+}
+
+MtpCursor::~MtpCursor() {
+    delete[] mColumns;
+}
+
+int MtpCursor::fillWindow(CursorWindow* window, int startPos) {
+    LOGD("MtpCursor::fillWindow mQueryType: %d\n", mQueryType);
+
+    switch (mQueryType) {
+        case DEVICE:
+            return fillDevices(window, startPos);
+        case DEVICE_ID:
+            return fillDevice(window, startPos);
+        case STORAGE:
+            return fillStorages(window, startPos);
+        case STORAGE_ID:
+            return fillStorage(window, startPos);
+        case OBJECT:
+            return fillObjects(window, 0, startPos);
+        case OBJECT_ID:
+            return fillObject(window, startPos);
+        case STORAGE_CHILDREN:
+            return fillObjects(window, -1, startPos);
+        case OBJECT_CHILDREN:
+            return fillObjects(window, mQbjectID, startPos);
+        default:
+            LOGE("MtpCursor::fillWindow: unknown query type %d\n", mQueryType);
+            return 0;
+    }
+}
+
+int MtpCursor::fillDevices(CursorWindow* window, int startPos) {
+    int count = 0;
+    MtpDeviceList& deviceList = mClient->getDeviceList();
+    for (int i = 0; i < deviceList.size(); i++) {
+        MtpDevice* device = deviceList[i];
+        if (fillDevice(window, device, startPos)) {
+            count++;
+            startPos++;
+        } else {
+            break;
+        }
+    }
+    return count;
+}
+
+int MtpCursor::fillDevice(CursorWindow* window, int startPos) {
+    MtpDevice* device = mClient->getDevice(mDeviceID);
+    if (device && fillDevice(window, device, startPos))
+        return 1;
+    else
+        return 0;
+}
+
+int MtpCursor::fillStorages(CursorWindow* window, int startPos) {
+    int count = 0;
+    MtpDevice* device = mClient->getDevice(mDeviceID);
+    if (!device)
+        return 0;
+    MtpStorageIDList* storageIDs = device->getStorageIDs();
+    if (!storageIDs)
+        return 0;
+
+    for (int i = 0; i < storageIDs->size(); i++) {
+        MtpStorageID storageID = (*storageIDs)[i];
+        if (fillStorage(window, device, storageID, startPos)) {
+            count++;
+            startPos++;
+        } else {
+            break;
+        }
+    }
+    delete storageIDs;
+    return count;
+}
+
+int MtpCursor::fillStorage(CursorWindow* window, int startPos) {
+    MtpDevice* device = mClient->getDevice(mDeviceID);
+    if (device && fillStorage(window, device, mStorageID, startPos))
+        return 1;
+    else
+        return 0;
+}
+
+int MtpCursor::fillObjects(CursorWindow* window, int parent, int startPos) {
+    int count = 0;
+    MtpDevice* device = mClient->getDevice(mDeviceID);
+    if (!device)
+        return 0;
+    MtpObjectHandleList* handles = device->getObjectHandles(mStorageID, 0, parent);
+    if (!handles)
+        return 0;
+
+    for (int i = 0; i < handles->size(); i++) {
+        MtpObjectHandle handle = (*handles)[i];
+        if (fillObject(window, device, handle, startPos)) {
+            count++;
+            startPos++;
+        } else {
+            break;
+        }
+    }
+    delete handles;
+    return count;
+}
+
+int MtpCursor::fillObject(CursorWindow* window, int startPos) {
+    MtpDevice* device = mClient->getDevice(mDeviceID);
+    if (device && fillObject(window, device, mQbjectID, startPos))
+        return 1;
+    else
+        return 0;
+}
+
+bool MtpCursor::fillDevice(CursorWindow* window, MtpDevice* device, int row) {
+    MtpDeviceInfo* deviceInfo = device->getDeviceInfo();
+    if (!deviceInfo)
+        return false;
+    if (!prepareRow(window))
+        return false;
+
+    for (int i = 0; i < mColumnCount; i++) {
+        switch (mColumns[i]) {
+            case DEVICE_ROW_ID:
+                if (!putLong(window, device->getID(), row, i))
+                    return false;
+                 break;
+            case DEVICE_MANUFACTURER:
+                if (!putString(window, deviceInfo->mManufacturer, row, i))
+                    return false;
+                 break;
+            case DEVICE_MODEL:
+                if (!putString(window, deviceInfo->mModel, row, i))
+                    return false;
+                 break;
+            default:
+                LOGE("fillDevice: unknown column %d\n", mColumns[i]);
+                return false;
+        }
+    }
+
+    return true;
+}
+
+bool MtpCursor::fillStorage(CursorWindow* window, MtpDevice* device,
+        MtpStorageID storageID, int row) {
+
+LOGD("fillStorage %d\n", storageID);
+
+    MtpStorageInfo* storageInfo = device->getStorageInfo(storageID);
+    if (!storageInfo)
+        return false;
+    if (!prepareRow(window)) {
+        delete storageInfo;
+        return false;
+    }
+
+    const char* text;
+    for (int i = 0; i < mColumnCount; i++) {
+        switch (mColumns[i]) {
+            case STORAGE_ROW_ID:
+                if (!putLong(window, storageID, row, i))
+                    goto fail;
+                 break;
+            case STORAGE_IDENTIFIER:
+                text = storageInfo->mVolumeIdentifier;
+                if (!text || !text[0])
+                    text = "Camera Storage";
+                if (!putString(window, text, row, i))
+                    goto fail;
+                 break;
+            case STORAGE_DESCRIPTION:
+                text = storageInfo->mStorageDescription;
+                if (!text || !text[0])
+                    text = "Storage Description";
+                if (!putString(window, text, row, i))
+                    goto fail;
+                 break;
+            default:
+                LOGE("fillStorage: unknown column %d\n", mColumns[i]);
+                goto fail;
+        }
+    }
+
+    delete storageInfo;
+    return true;
+
+fail:
+    delete storageInfo;
+    return false;
+}
+
+bool MtpCursor::fillObject(CursorWindow* window, MtpDevice* device,
+        MtpObjectHandle objectID, int row) {
+
+    MtpObjectInfo* objectInfo = device->getObjectInfo(objectID);
+    if (!objectInfo)
+        return false;
+    // objectInfo->print();
+    if (!prepareRow(window)) {
+        delete objectInfo;
+        return false;
+    }
+
+    for (int i = 0; i < mColumnCount; i++) {
+        switch (mColumns[i]) {
+            case OBJECT_ROW_ID:
+                if (!putLong(window, objectID, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_STORAGE_ID:
+                if (!putLong(window, objectInfo->mStorageID, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_FORMAT:
+                if (!putLong(window, objectInfo->mFormat, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_PROTECTION_STATUS:
+                if (!putLong(window, objectInfo->mProtectionStatus, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_SIZE:
+                if (!putLong(window, objectInfo->mCompressedSize, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_THUMB_FORMAT:
+                if (!putLong(window, objectInfo->mThumbFormat, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_THUMB_SIZE:
+                if (!putLong(window, objectInfo->mThumbCompressedSize, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_THUMB_WIDTH:
+                if (!putLong(window, objectInfo->mThumbPixWidth, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_THUMB_HEIGHT:
+                if (!putLong(window, objectInfo->mThumbPixHeight, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_IMAGE_WIDTH:
+                if (!putLong(window, objectInfo->mImagePixWidth, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_IMAGE_HEIGHT:
+                if (!putLong(window, objectInfo->mImagePixHeight, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_IMAGE_DEPTH:
+                if (!putLong(window, objectInfo->mImagePixDepth, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_PARENT:
+                if (!putLong(window, objectInfo->mParent, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_ASSOCIATION_TYPE:
+                if (!putLong(window, objectInfo->mAssociationType, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_ASSOCIATION_DESC:
+                if (!putLong(window, objectInfo->mAssociationDesc, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_SEQUENCE_NUMBER:
+                if (!putLong(window, objectInfo->mSequenceNumber, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_NAME:
+                if (!putString(window, objectInfo->mName, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_DATE_CREATED:
+                if (!putLong(window, objectInfo->mDateCreated, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_DATE_MODIFIED:
+                if (!putLong(window, objectInfo->mDateModified, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_KEYWORDS:
+                if (!putString(window, objectInfo->mKeywords, row, i))
+                    goto fail;
+                 break;
+            case OBJECT_THUMB:
+                if (!putThumbnail(window, objectID, objectInfo->mFormat, row, i))
+                    goto fail;
+                break;
+            default:
+                LOGE("fillObject: unknown column %d\n", mColumns[i]);
+                goto fail;
+        }
+    }
+
+    delete objectInfo;
+    return true;
+
+fail:
+    delete objectInfo;
+    return false;
+}
+
+bool MtpCursor::prepareRow(CursorWindow* window) {
+    if (!window->setNumColumns(mColumnCount)) {
+        LOGE("Failed to change column count from %d to %d", window->getNumColumns(), mColumnCount);
+        return false;
+    }
+    field_slot_t * fieldDir = window->allocRow();
+    if (!fieldDir) {
+        LOGE("Failed allocating fieldDir");
+        return false;
+    }
+    return true;
+}
+
+
+bool MtpCursor::putLong(CursorWindow* window, int64_t value, int row, int column) {
+    if (!window->putLong(row, column, value)) {
+        window->freeLastRow();
+        LOGE("Failed allocating space for a long in column %d", column);
+        return false;
+    }
+    return true;
+}
+
+bool MtpCursor::putString(CursorWindow* window, const char* text, int row, int column) {
+    int size = strlen(text) + 1;
+    int offset = window->alloc(size);
+    if (!offset) {
+        window->freeLastRow();
+        LOGE("Failed allocating %u bytes for text/blob %s", size, text);
+        return false;
+    }
+    window->copyIn(offset, (const uint8_t*)text, size);
+
+    // This must be updated after the call to alloc(), since that
+    // may move the field around in the window
+    field_slot_t * fieldSlot = window->getFieldSlot(row, column);
+    fieldSlot->type = FIELD_TYPE_STRING;
+    fieldSlot->data.buffer.offset = offset;
+    fieldSlot->data.buffer.size = size;
+    return true;
+}
+
+bool MtpCursor::putThumbnail(CursorWindow* window, MtpObjectHandle objectID,
+                            MtpObjectFormat format, int row, int column) {
+    MtpDevice* device = mClient->getDevice(mDeviceID);
+    void* thumbnail;
+    int size, offset;
+    if (format == MTP_FORMAT_ASSOCIATION) {
+        thumbnail = NULL;
+        size = offset = 0;
+    } else {
+        thumbnail = device->getThumbnail(objectID, size);
+
+        LOGV("putThumbnail: %p, size: %d\n", thumbnail, size);
+        offset = window->alloc(size);
+        if (!offset) {
+            window->freeLastRow();
+            LOGE("Failed allocating %u bytes for thumbnail", size);
+            return false;
+        }
+    }
+    if (thumbnail)
+        window->copyIn(offset, (const uint8_t*)thumbnail, size);
+
+    // This must be updated after the call to alloc(), since that
+    // may move the field around in the window
+    field_slot_t * fieldSlot = window->getFieldSlot(row, column);
+    fieldSlot->type = FIELD_TYPE_BLOB;
+    fieldSlot->data.buffer.offset = offset;
+    fieldSlot->data.buffer.size = size;
+    return true;
+}
+
+} // namespace android
diff --git a/media/mtp/MtpCursor.h b/media/mtp/MtpCursor.h
new file mode 100644
index 0000000..2e03c29
--- /dev/null
+++ b/media/mtp/MtpCursor.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_CURSOR_H
+#define _MTP_CURSOR_H
+
+#include "MtpTypes.h"
+
+namespace android {
+
+class CursorWindow;
+
+class MtpCursor {
+private:
+    enum {
+        DEVICE              = 1,
+        DEVICE_ID           = 2,
+        STORAGE             = 3,
+        STORAGE_ID          = 4,
+        OBJECT              = 5,
+        OBJECT_ID           = 6,
+        STORAGE_CHILDREN    = 7,
+        OBJECT_CHILDREN     = 8,
+    };
+
+    MtpClient*      mClient;
+    int             mQueryType;
+    int             mDeviceID;
+    MtpStorageID    mStorageID;
+    MtpObjectHandle mQbjectID;
+    int             mColumnCount;
+    int*            mColumns;
+
+public:
+                MtpCursor(MtpClient* client, int queryType, int deviceID,
+                        MtpStorageID storageID, MtpObjectHandle objectID,
+                        int columnCount, int* columns);
+    virtual     ~MtpCursor();
+
+    int         fillWindow(CursorWindow* window, int startPos);
+
+private:
+    int         fillDevices(CursorWindow* window, int startPos);
+    int         fillDevice(CursorWindow* window, int startPos);
+    int         fillStorages(CursorWindow* window, int startPos);
+    int         fillStorage(CursorWindow* window, int startPos);
+    int         fillObjects(CursorWindow* window, int parent, int startPos);
+    int         fillObject(CursorWindow* window, int startPos);
+
+    bool        fillDevice(CursorWindow* window, MtpDevice* device, int startPos);
+    bool        fillStorage(CursorWindow* window, MtpDevice* device,
+                        MtpStorageID storageID, int row);
+    bool        fillObject(CursorWindow* window, MtpDevice* device,
+                        MtpObjectHandle objectID, int row);
+
+    bool        prepareRow(CursorWindow* window);
+    bool        putLong(CursorWindow* window, int64_t value, int row, int column);
+    bool        putString(CursorWindow* window, const char* text, int row, int column);
+    bool        putThumbnail(CursorWindow* window, MtpObjectHandle objectID,
+                            MtpObjectFormat format, int row, int column);
+};
+
+}; // namespace android
+
+#endif // _MTP_CURSOR_H
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
new file mode 100644
index 0000000..ec78ff0
--- /dev/null
+++ b/media/mtp/MtpDataPacket.cpp
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpDataPacket"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <usbhost/usbhost.h>
+
+#include "MtpDataPacket.h"
+#include "MtpStringBuffer.h"
+
+namespace android {
+
+MtpDataPacket::MtpDataPacket()
+    :   MtpPacket(512),
+        mOffset(MTP_CONTAINER_HEADER_SIZE)
+{
+}
+
+MtpDataPacket::~MtpDataPacket() {
+}
+
+void MtpDataPacket::reset() {
+    MtpPacket::reset();
+    mOffset = MTP_CONTAINER_HEADER_SIZE;
+}
+
+void MtpDataPacket::setOperationCode(MtpOperationCode code) {
+    MtpPacket::putUInt16(MTP_CONTAINER_CODE_OFFSET, code);
+}
+
+void MtpDataPacket::setTransactionID(MtpTransactionID id) {
+    MtpPacket::putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id);
+}
+
+uint16_t MtpDataPacket::getUInt16() {
+    int offset = mOffset;
+    uint16_t result = (uint16_t)mBuffer[offset] | ((uint16_t)mBuffer[offset + 1] << 8);
+    mOffset += 2;
+    return result;
+}
+
+uint32_t MtpDataPacket::getUInt32() {
+    int offset = mOffset;
+    uint32_t result = (uint32_t)mBuffer[offset] | ((uint32_t)mBuffer[offset + 1] << 8) |
+           ((uint32_t)mBuffer[offset + 2] << 16)  | ((uint32_t)mBuffer[offset + 3] << 24);
+    mOffset += 4;
+    return result;
+}
+
+uint64_t MtpDataPacket::getUInt64() {
+    int offset = mOffset;
+    uint64_t result = (uint64_t)mBuffer[offset] | ((uint64_t)mBuffer[offset + 1] << 8) |
+           ((uint64_t)mBuffer[offset + 2] << 16) | ((uint64_t)mBuffer[offset + 3] << 24) |
+           ((uint64_t)mBuffer[offset + 4] << 32) | ((uint64_t)mBuffer[offset + 5] << 40) |
+           ((uint64_t)mBuffer[offset + 6] << 48)  | ((uint64_t)mBuffer[offset + 7] << 56);
+    mOffset += 8;
+    return result;
+}
+
+void MtpDataPacket::getUInt128(uint128_t& value) {
+    value[0] = getUInt32();
+    value[1] = getUInt32();
+    value[2] = getUInt32();
+    value[3] = getUInt32();
+}
+
+void MtpDataPacket::getString(MtpStringBuffer& string)
+{
+    string.readFromPacket(this);
+}
+
+Int8List* MtpDataPacket::getAInt8() {
+    Int8List* result = new Int8List;
+    int count = getUInt32();
+    for (int i = 0; i < count; i++)
+        result->push(getInt8());
+    return result;
+}
+
+UInt8List* MtpDataPacket::getAUInt8() {
+    UInt8List* result = new UInt8List;
+    int count = getUInt32();
+    for (int i = 0; i < count; i++)
+        result->push(getUInt8());
+    return result;
+}
+
+Int16List* MtpDataPacket::getAInt16() {
+    Int16List* result = new Int16List;
+    int count = getUInt32();
+    for (int i = 0; i < count; i++)
+        result->push(getInt16());
+    return result;
+}
+
+UInt16List* MtpDataPacket::getAUInt16() {
+    UInt16List* result = new UInt16List;
+    int count = getUInt32();
+    for (int i = 0; i < count; i++)
+        result->push(getUInt16());
+    return result;
+}
+
+Int32List* MtpDataPacket::getAInt32() {
+    Int32List* result = new Int32List;
+    int count = getUInt32();
+    for (int i = 0; i < count; i++)
+        result->push(getInt32());
+    return result;
+}
+
+UInt32List* MtpDataPacket::getAUInt32() {
+    UInt32List* result = new UInt32List;
+    int count = getUInt32();
+    for (int i = 0; i < count; i++)
+        result->push(getUInt32());
+    return result;
+}
+
+Int64List* MtpDataPacket::getAInt64() {
+    Int64List* result = new Int64List;
+    int count = getUInt32();
+    for (int i = 0; i < count; i++)
+        result->push(getInt64());
+    return result;
+}
+
+UInt64List* MtpDataPacket::getAUInt64() {
+    UInt64List* result = new UInt64List;
+    int count = getUInt32();
+    for (int i = 0; i < count; i++)
+        result->push(getUInt64());
+    return result;
+}
+
+void MtpDataPacket::putInt8(int8_t value) {
+    allocate(mOffset + 1);
+    mBuffer[mOffset++] = (uint8_t)value;
+    if (mPacketSize < mOffset)
+        mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putUInt8(uint8_t value) {
+    allocate(mOffset + 1);
+    mBuffer[mOffset++] = (uint8_t)value;
+    if (mPacketSize < mOffset)
+        mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putInt16(int16_t value) {
+    allocate(mOffset + 2);
+    mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+    if (mPacketSize < mOffset)
+        mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putUInt16(uint16_t value) {
+    allocate(mOffset + 2);
+    mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+    if (mPacketSize < mOffset)
+        mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putInt32(int32_t value) {
+    allocate(mOffset + 4);
+    mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
+    if (mPacketSize < mOffset)
+        mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putUInt32(uint32_t value) {
+    allocate(mOffset + 4);
+    mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
+    if (mPacketSize < mOffset)
+        mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putInt64(int64_t value) {
+    allocate(mOffset + 8);
+    mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 32) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 40) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 48) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 56) & 0xFF);
+    if (mPacketSize < mOffset)
+        mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putUInt64(uint64_t value) {
+    allocate(mOffset + 8);
+    mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 32) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 40) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 48) & 0xFF);
+    mBuffer[mOffset++] = (uint8_t)((value >> 56) & 0xFF);
+    if (mPacketSize < mOffset)
+        mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putInt128(const int128_t& value) {
+    putInt32(value[0]);
+    putInt32(value[1]);
+    putInt32(value[2]);
+    putInt32(value[3]);
+}
+
+void MtpDataPacket::putUInt128(const uint128_t& value) {
+    putUInt32(value[0]);
+    putUInt32(value[1]);
+    putUInt32(value[2]);
+    putUInt32(value[3]);
+}
+
+void MtpDataPacket::putInt128(int64_t value) {
+    putInt64(value);
+    putInt64(value < 0 ? -1 : 0);
+}
+
+void MtpDataPacket::putUInt128(uint64_t value) {
+    putUInt64(value);
+    putUInt64(0);
+}
+
+void MtpDataPacket::putAInt8(const int8_t* values, int count) {
+    putUInt32(count);
+    for (int i = 0; i < count; i++)
+        putInt8(*values++);
+}
+
+void MtpDataPacket::putAUInt8(const uint8_t* values, int count) {
+    putUInt32(count);
+    for (int i = 0; i < count; i++)
+        putUInt8(*values++);
+}
+
+void MtpDataPacket::putAInt16(const int16_t* values, int count) {
+    putUInt32(count);
+    for (int i = 0; i < count; i++)
+        putInt16(*values++);
+}
+
+void MtpDataPacket::putAUInt16(const uint16_t* values, int count) {
+    putUInt32(count);
+    for (int i = 0; i < count; i++)
+        putUInt16(*values++);
+}
+
+void MtpDataPacket::putAUInt16(const UInt16List* values) {
+    size_t count = (values ? values->size() : 0);
+    putUInt32(count);
+    for (size_t i = 0; i < count; i++)
+        putUInt16((*values)[i]);
+}
+
+void MtpDataPacket::putAInt32(const int32_t* values, int count) {
+    putUInt32(count);
+    for (int i = 0; i < count; i++)
+        putInt32(*values++);
+}
+
+void MtpDataPacket::putAUInt32(const uint32_t* values, int count) {
+    putUInt32(count);
+    for (int i = 0; i < count; i++)
+        putUInt32(*values++);
+}
+
+void MtpDataPacket::putAUInt32(const UInt32List* list) {
+    if (!list) {
+        putEmptyArray();
+    } else {
+        size_t size = list->size();
+        putUInt32(size);
+        for (size_t i = 0; i < size; i++)
+            putUInt32((*list)[i]);
+    }
+}
+
+void MtpDataPacket::putAInt64(const int64_t* values, int count) {
+    putUInt32(count);
+    for (int i = 0; i < count; i++)
+        putInt64(*values++);
+}
+
+void MtpDataPacket::putAUInt64(const uint64_t* values, int count) {
+    putUInt32(count);
+    for (int i = 0; i < count; i++)
+        putUInt64(*values++);
+}
+
+void MtpDataPacket::putString(const MtpStringBuffer& string) {
+    string.writeToPacket(this);
+}
+
+void MtpDataPacket::putString(const char* s) {
+    MtpStringBuffer string(s);
+    string.writeToPacket(this);
+}
+
+void MtpDataPacket::putString(const uint16_t* string) {
+    int count = 0;
+    for (int i = 0; i < 256; i++) {
+        if (string[i])
+            count++;
+        else
+            break;
+    }
+    putUInt8(count > 0 ? count + 1 : 0);
+    for (int i = 0; i < count; i++)
+        putUInt16(string[i]);
+    // only terminate with zero if string is not empty
+    if (count > 0)
+        putUInt16(0);
+}
+
+#ifdef MTP_DEVICE 
+int MtpDataPacket::read(int fd) {
+    // first read the header
+    int ret = ::read(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE);
+    if (ret != MTP_CONTAINER_HEADER_SIZE)
+        return -1;
+    // then the following data
+    int total = MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET);
+    int remaining = total - MTP_CONTAINER_HEADER_SIZE;
+    ret = ::read(fd, &mBuffer[0] + MTP_CONTAINER_HEADER_SIZE, remaining);
+    if (ret != remaining)
+        return -1;
+
+    mPacketSize = total;
+    mOffset = MTP_CONTAINER_HEADER_SIZE;
+    return total;
+}
+
+int MtpDataPacket::readDataHeader(int fd) {
+    int ret = ::read(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE);
+    if (ret > 0)
+        mPacketSize = ret;
+    else
+        mPacketSize = 0;
+    return ret;
+}
+
+int MtpDataPacket::write(int fd) {
+    MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+    MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+    dump();
+    // send header separately from data
+    int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE);
+    if (ret == MTP_CONTAINER_HEADER_SIZE)
+        ret = ::write(fd, mBuffer + MTP_CONTAINER_HEADER_SIZE,
+                        mPacketSize - MTP_CONTAINER_HEADER_SIZE);
+    return (ret < 0 ? ret : 0);
+}
+
+int MtpDataPacket::writeDataHeader(int fd, uint32_t length) {
+    MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length);
+    MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+    int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE);
+    return (ret < 0 ? ret : 0);
+}
+#endif // MTP_DEVICE
+
+#ifdef MTP_HOST
+int MtpDataPacket::read(struct usb_endpoint *ep) {
+    // first read the header
+    int length = transfer(ep, mBuffer, mBufferSize);
+    if (length >= MTP_CONTAINER_HEADER_SIZE) {
+        // look at the length field to see if the data spans multiple packets
+        uint32_t totalLength = MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET);
+        while (totalLength > length) {
+            allocate(length + mAllocationIncrement);
+            int ret = transfer(ep, mBuffer + length, mAllocationIncrement);
+            if (ret >= 0)
+                length += ret;
+            else {
+                length = ret;
+                break;
+            }
+        }
+    }
+    if (length >= 0)
+        mPacketSize = length;
+    return length;
+}
+
+int MtpDataPacket::readData(struct usb_endpoint *ep, void* buffer, int length) {
+    int packetSize = usb_endpoint_max_packet(ep);
+    int read = 0;
+    while (read < length) {
+        int ret = transfer(ep, (char *)buffer + read, packetSize);
+        if (ret < 0) {
+printf("MtpDataPacket::readData returning %d\n", ret);
+            return ret;
+        }
+        read += ret;
+    }
+printf("MtpDataPacket::readData returning %d\n", read);
+    return read;
+}
+
+int MtpDataPacket::readDataHeader(struct usb_endpoint *ep) {
+    int length = transfer(ep, mBuffer, usb_endpoint_max_packet(ep));
+    if (length >= 0)
+        mPacketSize = length;
+    return length;
+}
+
+int MtpDataPacket::writeDataHeader(struct usb_endpoint *ep, uint32_t length) {
+    MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length);
+    MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+    int ret = transfer(ep, mBuffer, MTP_CONTAINER_HEADER_SIZE);
+    return (ret < 0 ? ret : 0);
+}
+
+int MtpDataPacket::write(struct usb_endpoint *ep) {
+    MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+    MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+
+    // send header separately from data
+    int ret = transfer(ep, mBuffer, MTP_CONTAINER_HEADER_SIZE);
+    if (ret == MTP_CONTAINER_HEADER_SIZE)
+        ret = transfer(ep, mBuffer + MTP_CONTAINER_HEADER_SIZE,
+                        mPacketSize - MTP_CONTAINER_HEADER_SIZE);
+    return (ret < 0 ? ret : 0);
+}
+
+int MtpDataPacket::write(struct usb_endpoint *ep, void* buffer, uint32_t length) {
+    int ret = 0;
+    int packetSize = usb_endpoint_max_packet(ep);
+    while (length > 0) {
+        int write = (length > packetSize ? packetSize : length);
+        int ret = transfer(ep, buffer, write);
+        if (ret < 0)
+            break;
+        length -= ret;
+    }
+    return (ret < 0 ? ret : 0);
+}
+
+#endif // MTP_HOST
+
+void* MtpDataPacket::getData(int& outLength) const {
+    int length = mPacketSize - MTP_CONTAINER_HEADER_SIZE;
+    if (length > 0) {
+        void* result = malloc(length);
+        if (result) {
+            memcpy(result, mBuffer + MTP_CONTAINER_HEADER_SIZE, length);
+            outLength = length;
+            return result;
+        }
+    }
+    outLength = 0;
+    return NULL;
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
new file mode 100644
index 0000000..fab6a07
--- /dev/null
+++ b/media/mtp/MtpDataPacket.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_DATA_PACKET_H
+#define _MTP_DATA_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+namespace android {
+
+class MtpStringBuffer;
+
+class MtpDataPacket : public MtpPacket {
+private:
+    // current offset for get/put methods
+    int                 mOffset;
+
+public:
+                        MtpDataPacket();
+    virtual             ~MtpDataPacket();
+
+    virtual void        reset();
+
+    void                setOperationCode(MtpOperationCode code);
+    void                setTransactionID(MtpTransactionID id);
+
+    inline uint8_t      getUInt8() { return (uint8_t)mBuffer[mOffset++]; }
+    inline int8_t       getInt8() { return (int8_t)mBuffer[mOffset++]; }
+    uint16_t            getUInt16();
+    inline int16_t      getInt16() { return (int16_t)getUInt16(); }
+    uint32_t            getUInt32();
+    inline int32_t      getInt32() { return (int32_t)getUInt32(); }
+    uint64_t            getUInt64();
+    inline int64_t      getInt64() { return (int64_t)getUInt64(); }
+    void                getUInt128(uint128_t& value);
+    inline void         getInt128(int128_t& value) { getUInt128((uint128_t&)value); }
+    void                getString(MtpStringBuffer& string);
+
+    Int8List*           getAInt8();
+    UInt8List*          getAUInt8();
+    Int16List*          getAInt16();
+    UInt16List*         getAUInt16();
+    Int32List*          getAInt32();
+    UInt32List*         getAUInt32();
+    Int64List*          getAInt64();
+    UInt64List*         getAUInt64();
+
+    void                putInt8(int8_t value);
+    void                putUInt8(uint8_t value);
+    void                putInt16(int16_t value);
+    void                putUInt16(uint16_t value);
+    void                putInt32(int32_t value);
+    void                putUInt32(uint32_t value);
+    void                putInt64(int64_t value);
+    void                putUInt64(uint64_t value);
+    void                putInt128(const int128_t& value);
+    void                putUInt128(const uint128_t& value);
+    void                putInt128(int64_t value);
+    void                putUInt128(uint64_t value);
+
+    void                putAInt8(const int8_t* values, int count);
+    void                putAUInt8(const uint8_t* values, int count);
+    void                putAInt16(const int16_t* values, int count);
+    void                putAUInt16(const uint16_t* values, int count);
+    void                putAUInt16(const UInt16List* values);
+    void                putAInt32(const int32_t* values, int count);
+    void                putAUInt32(const uint32_t* values, int count);
+    void                putAUInt32(const UInt32List* list);
+    void                putAInt64(const int64_t* values, int count);
+    void                putAUInt64(const uint64_t* values, int count);
+    void                putString(const MtpStringBuffer& string);
+    void                putString(const char* string);
+    void                putString(const uint16_t* string);
+    inline void         putEmptyString() { putUInt8(0); }
+    inline void         putEmptyArray() { putUInt32(0); }
+
+
+#ifdef MTP_DEVICE
+    // fill our buffer with data from the given file descriptor
+    int                 read(int fd);
+    int                 readDataHeader(int fd);
+
+    // write our data to the given file descriptor
+    int                 write(int fd);
+    int                 writeDataHeader(int fd, uint32_t length);
+#endif
+
+#ifdef MTP_HOST
+    int                 read(struct usb_endpoint *ep);
+    int                 readData(struct usb_endpoint *ep, void* buffer, int length);
+    int                 readDataHeader(struct usb_endpoint *ep);
+
+    int                 writeDataHeader(struct usb_endpoint *ep, uint32_t length);
+    int                 write(struct usb_endpoint *ep);
+    int                 write(struct usb_endpoint *ep, void* buffer, uint32_t length);
+#endif
+
+    inline bool         hasData() const { return mPacketSize > MTP_CONTAINER_HEADER_SIZE; }
+    void*               getData(int& outLength) const;
+};
+
+}; // namespace android
+
+#endif // _MTP_DATA_PACKET_H
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
new file mode 100644
index 0000000..c8cb016
--- /dev/null
+++ b/media/mtp/MtpDatabase.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_DATABASE_H
+#define _MTP_DATABASE_H
+
+#include "MtpTypes.h"
+
+namespace android {
+
+class MtpDataPacket;
+class MtpProperty;
+
+class MtpDatabase {
+public:
+    virtual ~MtpDatabase() {}
+
+    // called from SendObjectInfo to reserve a database entry for the incoming file
+    virtual MtpObjectHandle         beginSendObject(const char* path,
+                                            MtpObjectFormat format,
+                                            MtpObjectHandle parent,
+                                            MtpStorageID storage,
+                                            uint64_t size,
+                                            time_t modified) = 0;
+
+    // called to report success or failure of the SendObject file transfer
+    // success should signal a notification of the new object's creation,
+    // failure should remove the database entry created in beginSendObject
+    virtual void                    endSendObject(const char* path,
+                                            MtpObjectHandle handle,
+                                            MtpObjectFormat format,
+                                            bool succeeded) = 0;
+
+    virtual MtpObjectHandleList*    getObjectList(MtpStorageID storageID,
+                                            MtpObjectFormat format,
+                                            MtpObjectHandle parent) = 0;
+
+    virtual int                     getNumObjects(MtpStorageID storageID,
+                                            MtpObjectFormat format,
+                                            MtpObjectHandle parent) = 0;
+
+    // callee should delete[] the results from these
+    // results can be NULL
+    virtual MtpObjectFormatList*    getSupportedPlaybackFormats() = 0;
+    virtual MtpObjectFormatList*    getSupportedCaptureFormats() = 0;
+    virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format) = 0;
+    virtual MtpDevicePropertyList*  getSupportedDeviceProperties() = 0;
+
+    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
+                                            MtpObjectProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property) = 0;
+
+    virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
+                                            MtpDataPacket& packet) = 0;
+
+    virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
+                                            MtpString& filePath,
+                                            int64_t& fileLength) = 0;
+
+    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle) = 0;
+
+    virtual MtpObjectHandleList*    getObjectReferences(MtpObjectHandle handle) = 0;
+
+    virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
+                                            MtpObjectHandleList* references) = 0;
+
+    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
+                                            MtpObjectFormat format) = 0;
+
+    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property) = 0;
+
+    virtual void                    sessionStarted() = 0;
+
+    virtual void                    sessionEnded() = 0;
+};
+
+}; // namespace android
+
+#endif // _MTP_DATABASE_H
diff --git a/media/mtp/MtpDebug.cpp b/media/mtp/MtpDebug.cpp
new file mode 100644
index 0000000..d6b107d
--- /dev/null
+++ b/media/mtp/MtpDebug.cpp
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MtpDebug.h"
+
+namespace android {
+
+struct CodeEntry {
+    const char* name;
+    uint16_t code;
+};
+
+static const CodeEntry sOperationCodes[] = {
+    { "MTP_OPERATION_GET_DEVICE_INFO",              0x1001 },
+    { "MTP_OPERATION_OPEN_SESSION",                 0x1002 },
+    { "MTP_OPERATION_CLOSE_SESSION",                0x1003 },
+    { "MTP_OPERATION_GET_STORAGE_IDS",              0x1004 },
+    { "MTP_OPERATION_GET_STORAGE_INFO",             0x1005 },
+    { "MTP_OPERATION_GET_NUM_OBJECTS",              0x1006 },
+    { "MTP_OPERATION_GET_OBJECT_HANDLES",           0x1007 },
+    { "MTP_OPERATION_GET_OBJECT_INFO",              0x1008 },
+    { "MTP_OPERATION_GET_OBJECT",                   0x1009 },
+    { "MTP_OPERATION_GET_THUMB",                    0x100A },
+    { "MTP_OPERATION_DELETE_OBJECT",                0x100B },
+    { "MTP_OPERATION_SEND_OBJECT_INFO",             0x100C },
+    { "MTP_OPERATION_SEND_OBJECT",                  0x100D },
+    { "MTP_OPERATION_INITIATE_CAPTURE",             0x100E },
+    { "MTP_OPERATION_FORMAT_STORE",                 0x100F },
+    { "MTP_OPERATION_RESET_DEVICE",                 0x1010 },
+    { "MTP_OPERATION_SELF_TEST",                    0x1011 },
+    { "MTP_OPERATION_SET_OBJECT_PROTECTION",        0x1012 },
+    { "MTP_OPERATION_POWER_DOWN",                   0x1013 },
+    { "MTP_OPERATION_GET_DEVICE_PROP_DESC",         0x1014 },
+    { "MTP_OPERATION_GET_DEVICE_PROP_VALUE",        0x1015 },
+    { "MTP_OPERATION_SET_DEVICE_PROP_VALUE",        0x1016 },
+    { "MTP_OPERATION_RESET_DEVICE_PROP_VALUE",      0x1017 },
+    { "MTP_OPERATION_TERMINATE_OPEN_CAPTURE",       0x1018 },
+    { "MTP_OPERATION_MOVE_OBJECT",                  0x1019 },
+    { "MTP_OPERATION_COPY_OBJECT",                  0x101A },
+    { "MTP_OPERATION_GET_PARTIAL_OBJECT",           0x101B },
+    { "MTP_OPERATION_INITIATE_OPEN_CAPTURE",        0x101C },
+    { "MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED",   0x9801 },
+    { "MTP_OPERATION_GET_OBJECT_PROP_DESC",         0x9802 },
+    { "MTP_OPERATION_GET_OBJECT_PROP_VALUE",        0x9803 },
+    { "MTP_OPERATION_SET_OBJECT_PROP_VALUE",        0x9804 },
+    { "MTP_OPERATION_GET_OBJECT_REFERENCES",        0x9810 },
+    { "MTP_OPERATION_SET_OBJECT_REFERENCES",        0x9811 },
+    { "MTP_OPERATION_SKIP",                         0x9820 },
+    { 0,                                            0      },
+};
+
+static const CodeEntry sFormatCodes[] = {
+    { "MTP_OPERATION_GET_DEVICE_INFO",              0x1001 },
+    { "MTP_FORMAT_UNDEFINED",                       0x3000 },
+    { "MTP_FORMAT_ASSOCIATION",                     0x3001 },
+    { "MTP_FORMAT_SCRIPT",                          0x3002 },
+    { "MTP_FORMAT_EXECUTABLE",                      0x3003 },
+    { "MTP_FORMAT_TEXT",                            0x3004 },
+    { "MTP_FORMAT_HTML",                            0x3005 },
+    { "MTP_FORMAT_DPOF",                            0x3006 },
+    { "MTP_FORMAT_AIFF",                            0x3007 },
+    { "MTP_FORMAT_WAV",                             0x3008 },
+    { "MTP_FORMAT_MP3",                             0x3009 },
+    { "MTP_FORMAT_AVI",                             0x300A },
+    { "MTP_FORMAT_MPEG",                            0x300B },
+    { "MTP_FORMAT_ASF",                             0x300C },
+    { "MTP_FORMAT_DEFINED",                         0x3800 },
+    { "MTP_FORMAT_EXIF_JPEG",                       0x3801 },
+    { "MTP_FORMAT_TIFF_EP",                         0x3802 },
+    { "MTP_FORMAT_FLASHPIX",                        0x3803 },
+    { "MTP_FORMAT_BMP",                             0x3804 },
+    { "MTP_FORMAT_CIFF",                            0x3805 },
+    { "MTP_FORMAT_GIF",                             0x3807 },
+    { "MTP_FORMAT_JFIF",                            0x3808 },
+    { "MTP_FORMAT_CD",                              0x3809 },
+    { "MTP_FORMAT_PICT",                            0x380A },
+    { "MTP_FORMAT_PNG",                             0x380B },
+    { "MTP_FORMAT_TIFF",                            0x380D },
+    { "MTP_FORMAT_TIFF_IT",                         0x380E },
+    { "MTP_FORMAT_JP2",                             0x380F },
+    { "MTP_FORMAT_JPX",                             0x3810 },
+    { "MTP_FORMAT_UNDEFINED_FIRMWARE",              0xB802 },
+    { "MTP_FORMAT_WINDOWS_IMAGE_FORMAT",            0xB881 },
+    { "MTP_FORMAT_UNDEFINED_AUDIO",                 0xB900 },
+    { "MTP_FORMAT_WMA",                             0xB901 },
+    { "MTP_FORMAT_OGG",                             0xB902 },
+    { "MTP_FORMAT_AAC",                             0xB903 },
+    { "MTP_FORMAT_AUDIBLE",                         0xB904 },
+    { "MTP_FORMAT_FLAC",                            0xB906 },
+    { "MTP_FORMAT_UNDEFINED_VIDEO",                 0xB980 },
+    { "MTP_FORMAT_WMV",                             0xB981 },
+    { "MTP_FORMAT_MP4_CONTAINER",                   0xB982 },
+    { "MTP_FORMAT_MP2",                             0xB983 },
+    { "MTP_FORMAT_3GP_CONTAINER",                   0xB984 },
+    { "MTP_FORMAT_UNDEFINED_COLLECTION",            0xBA00 },
+    { "MTP_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM",       0xBA01 },
+    { "MTP_FORMAT_ABSTRACT_IMAGE_ALBUM",            0xBA02 },
+    { "MTP_FORMAT_ABSTRACT_AUDIO_ALBUM",            0xBA03 },
+    { "MTP_FORMAT_ABSTRACT_VIDEO_ALBUM",            0xBA04 },
+    { "MTP_FORMAT_ABSTRACT_AV_PLAYLIST",            0xBA05 },
+    { "MTP_FORMAT_ABSTRACT_CONTACT_GROUP",          0xBA06 },
+    { "MTP_FORMAT_ABSTRACT_MESSAGE_FOLDER",         0xBA07 },
+    { "MTP_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION",   0xBA08 },
+    { "MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST",         0xBA09 },
+    { "MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST",         0xBA0A },
+    { "MTP_FORMAT_ABSTRACT_MEDIACAST",              0xBA0B },
+    { "MTP_FORMAT_WPL_PLAYLIST",                    0xBA10 },
+    { "MTP_FORMAT_M3U_PLAYLIST",                    0xBA11 },
+    { "MTP_FORMAT_MPL_PLAYLIST",                    0xBA12 },
+    { "MTP_FORMAT_ASX_PLAYLIST",                    0xBA13 },
+    { "MTP_FORMAT_PLS_PLAYLIST",                    0xBA14 },
+    { "MTP_FORMAT_UNDEFINED_DOCUMENT",              0xBA80 },
+    { "MTP_FORMAT_ABSTRACT_DOCUMENT",               0xBA81 },
+    { "MTP_FORMAT_XML_DOCUMENT",                    0xBA82 },
+    { "MTP_FORMAT_MS_WORD_DOCUMENT",                0xBA83 },
+    { "MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT",      0xBA84 },
+    { "MTP_FORMAT_MS_EXCEL_SPREADSHEET",            0xBA85 },
+    { "MTP_FORMAT_MS_POWERPOINT_PRESENTATION",      0xBA86 },
+    { "MTP_FORMAT_UNDEFINED_MESSAGE",               0xBB00 },
+    { "MTP_FORMAT_ABSTRACT_MESSSAGE",               0xBB01 },
+    { "MTP_FORMAT_UNDEFINED_CONTACT",               0xBB80 },
+    { "MTP_FORMAT_ABSTRACT_CONTACT",                0xBB81 },
+    { "MTP_FORMAT_VCARD_2",                         0xBB82 },
+    { 0,                                            0      },
+};
+
+static const CodeEntry sObjectPropCodes[] = {
+    { "MTP_PROPERTY_STORAGE_ID",                             0xDC01 },
+    { "MTP_PROPERTY_OBJECT_FORMAT",                          0xDC02 },
+    { "MTP_PROPERTY_PROTECTION_STATUS",                      0xDC03 },
+    { "MTP_PROPERTY_OBJECT_SIZE",                            0xDC04 },
+    { "MTP_PROPERTY_ASSOCIATION_TYPE",                       0xDC05 },
+    { "MTP_PROPERTY_ASSOCIATION_DESC",                       0xDC06 },
+    { "MTP_PROPERTY_OBJECT_FILE_NAME",                       0xDC07 },
+    { "MTP_PROPERTY_DATE_CREATED",                           0xDC08 },
+    { "MTP_PROPERTY_DATE_MODIFIED",                          0xDC09 },
+    { "MTP_PROPERTY_KEYWORDS",                               0xDC0A },
+    { "MTP_PROPERTY_PARENT_OBJECT",                          0xDC0B },
+    { "MTP_PROPERTY_ALLOWED_FOLDER_CONTENTS",                0xDC0C },
+    { "MTP_PROPERTY_HIDDEN",                                 0xDC0D },
+    { "MTP_PROPERTY_SYSTEM_OBJECT",                          0xDC0E },
+    { "MTP_PROPERTY_PERSISTENT_UID",                         0xDC41 },
+    { "MTP_PROPERTY_SYNC_ID",                                0xDC42 },
+    { "MTP_PROPERTY_PROPERTY_BAG",                           0xDC43 },
+    { "MTP_PROPERTY_NAME",                                   0xDC44 },
+    { "MTP_PROPERTY_CREATED_BY",                             0xDC45 },
+    { "MTP_PROPERTY_ARTIST",                                 0xDC46 },
+    { "MTP_PROPERTY_DATE_AUTHORED",                          0xDC47 },
+    { "MTP_PROPERTY_DESCRIPTION",                            0xDC48 },
+    { "MTP_PROPERTY_URL_REFERENCE",                          0xDC49 },
+    { "MTP_PROPERTY_LANGUAGE_LOCALE",                        0xDC4A },
+    { "MTP_PROPERTY_COPYRIGHT_INFORMATION",                  0xDC4B },
+    { "MTP_PROPERTY_SOURCE",                                 0xDC4C },
+    { "MTP_PROPERTY_ORIGIN_LOCATION",                        0xDC4D },
+    { "MTP_PROPERTY_DATE_ADDED",                             0xDC4E },
+    { "MTP_PROPERTY_NON_CONSUMABLE",                         0xDC4F },
+    { "MTP_PROPERTY_CORRUPT_UNPLAYABLE",                     0xDC50 },
+    { "MTP_PROPERTY_PRODUCER_SERIAL_NUMBER",                 0xDC51 },
+    { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT",           0xDC81 },
+    { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_SIZE",             0xDC82 },
+    { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT",           0xDC83 },
+    { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH",            0xDC84 },
+    { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DURATION",         0xDC85 },
+    { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DATA",             0xDC86 },
+    { "MTP_PROPERTY_WIDTH",                                  0xDC87 },
+    { "MTP_PROPERTY_HEIGHT",                                 0xDC88 },
+    { "MTP_PROPERTY_DURATION",                               0xDC89 },
+    { "MTP_PROPERTY_RATING",                                 0xDC8A },
+    { "MTP_PROPERTY_TRACK",                                  0xDC8B },
+    { "MTP_PROPERTY_GENRE",                                  0xDC8C },
+    { "MTP_PROPERTY_CREDITS",                                0xDC8D },
+    { "MTP_PROPERTY_LYRICS",                                 0xDC8E },
+    { "MTP_PROPERTY_SUBSCRIPTION_CONTENT_ID",                0xDC8F },
+    { "MTP_PROPERTY_PRODUCED_BY",                            0xDC90 },
+    { "MTP_PROPERTY_USE_COUNT",                              0xDC91 },
+    { "MTP_PROPERTY_SKIP_COUNT",                             0xDC92 },
+    { "MTP_PROPERTY_LAST_ACCESSED",                          0xDC93 },
+    { "MTP_PROPERTY_PARENTAL_RATING",                        0xDC94 },
+    { "MTP_PROPERTY_META_GENRE",                             0xDC95 },
+    { "MTP_PROPERTY_COMPOSER",                               0xDC96 },
+    { "MTP_PROPERTY_EFFECTIVE_RATING",                       0xDC97 },
+    { "MTP_PROPERTY_SUBTITLE",                               0xDC98 },
+    { "MTP_PROPERTY_ORIGINAL_RELEASE_DATE",                  0xDC99 },
+    { "MTP_PROPERTY_ALBUM_NAME",                             0xDC9A },
+    { "MTP_PROPERTY_ALBUM_ARTIST",                           0xDC9B },
+    { "MTP_PROPERTY_MOOD",                                   0xDC9C },
+    { "MTP_PROPERTY_DRM_STATUS",                             0xDC9D },
+    { "MTP_PROPERTY_SUB_DESCRIPTION",                        0xDC9E },
+    { "MTP_PROPERTY_IS_CROPPED",                             0xDCD1 },
+    { "MTP_PROPERTY_IS_COLOUR_CORRECTED",                    0xDCD2 },
+    { "MTP_PROPERTY_IMAGE_BIT_DEPTH",                        0xDCD3 },
+    { "MTP_PROPERTY_F_NUMBER",                               0xDCD4 },
+    { "MTP_PROPERTY_EXPOSURE_TIME",                          0xDCD5 },
+    { "MTP_PROPERTY_EXPOSURE_INDEX",                         0xDCD6 },
+    { "MTP_PROPERTY_TOTAL_BITRATE",                          0xDE91 },
+    { "MTP_PROPERTY_BITRATE_TYPE",                           0xDE92 },
+    { "MTP_PROPERTY_SAMPLE_RATE",                            0xDE93 },
+    { "MTP_PROPERTY_NUMBER_OF_CHANNELS",                     0xDE94 },
+    { "MTP_PROPERTY_AUDIO_BIT_DEPTH",                        0xDE95 },
+    { "MTP_PROPERTY_SCAN_TYPE",                              0xDE97 },
+    { "MTP_PROPERTY_AUDIO_WAVE_CODEC",                       0xDE99 },
+    { "MTP_PROPERTY_AUDIO_BITRATE",                          0xDE9A },
+    { "MTP_PROPERTY_VIDEO_FOURCC_CODEC",                     0xDE9B },
+    { "MTP_PROPERTY_VIDEO_BITRATE",                          0xDE9C },
+    { "MTP_PROPERTY_FRAMES_PER_THOUSAND_SECONDS",            0xDE9D },
+    { "MTP_PROPERTY_KEYFRAME_DISTANCE",                      0xDE9E },
+    { "MTP_PROPERTY_BUFFER_SIZE",                            0xDE9F },
+    { "MTP_PROPERTY_ENCODING_QUALITY",                       0xDEA0 },
+    { "MTP_PROPERTY_ENCODING_PROFILE",                       0xDEA1 },
+    { "MTP_PROPERTY_DISPLAY_NAME",                           0xDCE0 },
+    { "MTP_PROPERTY_BODY_TEXT",                              0xDCE1 },
+    { "MTP_PROPERTY_SUBJECT",                                0xDCE2 },
+    { "MTP_PROPERTY_PRIORITY",                               0xDCE3 },
+    { "MTP_PROPERTY_GIVEN_NAME",                             0xDD00 },
+    { "MTP_PROPERTY_MIDDLE_NAMES",                           0xDD01 },
+    { "MTP_PROPERTY_FAMILY_NAME",                            0xDD02 },
+    { "MTP_PROPERTY_PREFIX",                                 0xDD03 },
+    { "MTP_PROPERTY_SUFFIX",                                 0xDD04 },
+    { "MTP_PROPERTY_PHONETIC_GIVEN_NAME",                    0xDD05 },
+    { "MTP_PROPERTY_PHONETIC_FAMILY_NAME",                   0xDD06 },
+    { "MTP_PROPERTY_EMAIL_PRIMARY",                          0xDD07 },
+    { "MTP_PROPERTY_EMAIL_PERSONAL_1",                       0xDD08 },
+    { "MTP_PROPERTY_EMAIL_PERSONAL_2",                       0xDD09 },
+    { "MTP_PROPERTY_EMAIL_BUSINESS_1",                       0xDD0A },
+    { "MTP_PROPERTY_EMAIL_BUSINESS_2",                       0xDD0B },
+    { "MTP_PROPERTY_EMAIL_OTHERS",                           0xDD0C },
+    { "MTP_PROPERTY_PHONE_NUMBER_PRIMARY",                   0xDD0D },
+    { "MTP_PROPERTY_PHONE_NUMBER_PERSONAL",                  0xDD0E },
+    { "MTP_PROPERTY_PHONE_NUMBER_PERSONAL_2",                0xDD0F },
+    { "MTP_PROPERTY_PHONE_NUMBER_BUSINESS",                  0xDD10 },
+    { "MTP_PROPERTY_PHONE_NUMBER_BUSINESS_2",                0xDD11 },
+    { "MTP_PROPERTY_PHONE_NUMBER_MOBILE",                    0xDD12 },
+    { "MTP_PROPERTY_PHONE_NUMBER_MOBILE_2",                  0xDD13 },
+    { "MTP_PROPERTY_FAX_NUMBER_PRIMARY",                     0xDD14 },
+    { "MTP_PROPERTY_FAX_NUMBER_PERSONAL",                    0xDD15 },
+    { "MTP_PROPERTY_FAX_NUMBER_BUSINESS",                    0xDD16 },
+    { "MTP_PROPERTY_PAGER_NUMBER",                           0xDD17 },
+    { "MTP_PROPERTY_PHONE_NUMBER_OTHERS",                    0xDD18 },
+    { "MTP_PROPERTY_PRIMARY_WEB_ADDRESS",                    0xDD19 },
+    { "MTP_PROPERTY_PERSONAL_WEB_ADDRESS",                   0xDD1A },
+    { "MTP_PROPERTY_BUSINESS_WEB_ADDRESS",                   0xDD1B },
+    { "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS",              0xDD1C },
+    { "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_2",            0xDD1D },
+    { "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_3",            0xDD1E },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL",           0xDD1F },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1",         0xDD20 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2",         0xDD21 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY",           0xDD22 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION",         0xDD23 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE",    0xDD24 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY",        0xDD25 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL",           0xDD26 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1",         0xDD27 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2",         0xDD28 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY",           0xDD29 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION",         0xDD2A },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE",    0xDD2B },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY",        0xDD2C },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_FULL",              0xDD2D },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1",            0xDD2E },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2",            0xDD2F },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_CITY",              0xDD30 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_REGION",            0xDD31 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE",       0xDD32 },
+    { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY",           0xDD33 },
+    { "MTP_PROPERTY_ORGANIZATION_NAME",                      0xDD34 },
+    { "MTP_PROPERTY_PHONETIC_ORGANIZATION_NAME",             0xDD35 },
+    { "MTP_PROPERTY_ROLE",                                   0xDD36 },
+    { "MTP_PROPERTY_BIRTHDATE",                              0xDD37 },
+    { "MTP_PROPERTY_MESSAGE_TO",                             0xDD40 },
+    { "MTP_PROPERTY_MESSAGE_CC",                             0xDD41 },
+    { "MTP_PROPERTY_MESSAGE_BCC",                            0xDD42 },
+    { "MTP_PROPERTY_MESSAGE_READ",                           0xDD43 },
+    { "MTP_PROPERTY_MESSAGE_RECEIVED_TIME",                  0xDD44 },
+    { "MTP_PROPERTY_MESSAGE_SENDER",                         0xDD45 },
+    { "MTP_PROPERTY_ACTIVITY_BEGIN_TIME",                    0xDD50 },
+    { "MTP_PROPERTY_ACTIVITY_END_TIME",                      0xDD51 },
+    { "MTP_PROPERTY_ACTIVITY_LOCATION",                      0xDD52 },
+    { "MTP_PROPERTY_ACTIVITY_REQUIRED_ATTENDEES",            0xDD54 },
+    { "MTP_PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES",            0xDD55 },
+    { "MTP_PROPERTY_ACTIVITY_RESOURCES",                     0xDD56 },
+    { "MTP_PROPERTY_ACTIVITY_ACCEPTED",                      0xDD57 },
+    { "MTP_PROPERTY_ACTIVITY_TENTATIVE",                     0xDD58 },
+    { "MTP_PROPERTY_ACTIVITY_DECLINED",                      0xDD59 },
+    { "MTP_PROPERTY_ACTIVITY_REMAINDER_TIME",                0xDD5A },
+    { "MTP_PROPERTY_ACTIVITY_OWNER",                         0xDD5B },
+    { "MTP_PROPERTY_ACTIVITY_STATUS",                        0xDD5C },
+    { "MTP_PROPERTY_OWNER",                                  0xDD5D },
+    { "MTP_PROPERTY_EDITOR",                                 0xDD5E },
+    { "MTP_PROPERTY_WEBMASTER",                              0xDD5F },
+    { "MTP_PROPERTY_URL_SOURCE",                             0xDD60 },
+    { "MTP_PROPERTY_URL_DESTINATION",                        0xDD61 },
+    { "MTP_PROPERTY_TIME_BOOKMARK",                          0xDD62 },
+    { "MTP_PROPERTY_OBJECT_BOOKMARK",                        0xDD63 },
+    { "MTP_PROPERTY_BYTE_BOOKMARK",                          0xDD64 },
+    { "MTP_PROPERTY_LAST_BUILD_DATE",                        0xDD70 },
+    { "MTP_PROPERTY_TIME_TO_LIVE",                           0xDD71 },
+    { "MTP_PROPERTY_MEDIA_GUID",                             0xDD72 },
+    { 0,                                                     0      },
+};
+
+static const CodeEntry sDevicePropCodes[] = {
+    { "MTP_DEVICE_PROPERTY_UNDEFINED",                       0x5000 },
+    { "MTP_DEVICE_PROPERTY_BATTERY_LEVEL",                   0x5001 },
+    { "MTP_DEVICE_PROPERTY_FUNCTIONAL_MODE",                 0x5002 },
+    { "MTP_DEVICE_PROPERTY_IMAGE_SIZE",                      0x5003 },
+    { "MTP_DEVICE_PROPERTY_COMPRESSION_SETTING",             0x5004 },
+    { "MTP_DEVICE_PROPERTY_WHITE_BALANCE",                   0x5005 },
+    { "MTP_DEVICE_PROPERTY_RGB_GAIN",                        0x5006 },
+    { "MTP_DEVICE_PROPERTY_F_NUMBER",                        0x5007 },
+    { "MTP_DEVICE_PROPERTY_FOCAL_LENGTH",                    0x5008 },
+    { "MTP_DEVICE_PROPERTY_FOCUS_DISTANCE",                  0x5009 },
+    { "MTP_DEVICE_PROPERTY_FOCUS_MODE",                      0x500A },
+    { "MTP_DEVICE_PROPERTY_EXPOSURE_METERING_MODE",          0x500B },
+    { "MTP_DEVICE_PROPERTY_FLASH_MODE",                      0x500C },
+    { "MTP_DEVICE_PROPERTY_EXPOSURE_TIME",                   0x500D },
+    { "MTP_DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE",           0x500E },
+    { "MTP_DEVICE_PROPERTY_EXPOSURE_INDEX",                  0x500F },
+    { "MTP_DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION",      0x5010 },
+    { "MTP_DEVICE_PROPERTY_DATETIME",                        0x5011 },
+    { "MTP_DEVICE_PROPERTY_CAPTURE_DELAY",                   0x5012 },
+    { "MTP_DEVICE_PROPERTY_STILL_CAPTURE_MODE",              0x5013 },
+    { "MTP_DEVICE_PROPERTY_CONTRAST",                        0x5014 },
+    { "MTP_DEVICE_PROPERTY_SHARPNESS",                       0x5015 },
+    { "MTP_DEVICE_PROPERTY_DIGITAL_ZOOM",                    0x5016 },
+    { "MTP_DEVICE_PROPERTY_EFFECT_MODE",                     0x5017 },
+    { "MTP_DEVICE_PROPERTY_BURST_NUMBER",                    0x5018 },
+    { "MTP_DEVICE_PROPERTY_BURST_INTERVAL",                  0x5019 },
+    { "MTP_DEVICE_PROPERTY_TIMELAPSE_NUMBER",                0x501A },
+    { "MTP_DEVICE_PROPERTY_TIMELAPSE_INTERVAL",              0x501B },
+    { "MTP_DEVICE_PROPERTY_FOCUS_METERING_MODE",             0x501C },
+    { "MTP_DEVICE_PROPERTY_UPLOAD_URL",                      0x501D },
+    { "MTP_DEVICE_PROPERTY_ARTIST",                          0x501E },
+    { "MTP_DEVICE_PROPERTY_COPYRIGHT_INFO",                  0x501F },
+    { "MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER",         0xD401 },
+    { "MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME",            0xD402 },
+    { "MTP_DEVICE_PROPERTY_VOLUME",                          0xD403 },
+    { "MTP_DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED",       0xD404 },
+    { "MTP_DEVICE_PROPERTY_DEVICE_ICON",                     0xD405 },
+    { "MTP_DEVICE_PROPERTY_PLAYBACK_RATE",                   0xD410 },
+    { "MTP_DEVICE_PROPERTY_PLAYBACK_OBJECT",                 0xD411 },
+    { "MTP_DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX",        0xD412 },
+    { "MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO",  0xD406 },
+    { "MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE",           0xD407 },
+    { 0,                                                     0      },
+};
+
+static const char* getCodeName(uint16_t code, const CodeEntry* table) {
+    const CodeEntry* entry = table;
+    while (entry->name) {
+        if (entry->code == code)
+            return entry->name;
+        entry++;
+    }
+    return "UNKNOWN";
+}
+
+const char* MtpDebug::getOperationCodeName(MtpOperationCode code) {
+    return getCodeName(code, sOperationCodes);
+}
+
+const char* MtpDebug::getFormatCodeName(MtpOperationCode code) {
+    return getCodeName(code, sFormatCodes);
+}
+
+const char* MtpDebug::getObjectPropCodeName(MtpPropertyCode code) {
+    return getCodeName(code, sObjectPropCodes);
+}
+
+const char* MtpDebug::getDevicePropCodeName(MtpPropertyCode code) {
+    return getCodeName(code, sDevicePropCodes);
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpDebug.h b/media/mtp/MtpDebug.h
new file mode 100644
index 0000000..5b53e31
--- /dev/null
+++ b/media/mtp/MtpDebug.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_DEBUG_H
+#define _MTP_DEBUG_H
+
+// #define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "MtpTypes.h"
+
+namespace android {
+
+class MtpDebug {
+public:
+    static const char* getOperationCodeName(MtpOperationCode code);
+    static const char* getFormatCodeName(MtpObjectFormat code);
+    static const char* getObjectPropCodeName(MtpPropertyCode code);
+    static const char* getDevicePropCodeName(MtpPropertyCode code);
+};
+
+}; // namespace android
+
+#endif // _MTP_DEBUG_H
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
new file mode 100644
index 0000000..fca0142
--- /dev/null
+++ b/media/mtp/MtpDevice.cpp
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpDevice"
+
+#include "MtpDebug.h"
+#include "MtpDevice.h"
+#include "MtpDeviceInfo.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpStorageInfo.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <endian.h>
+
+#include <usbhost/usbhost.h>
+
+namespace android {
+
+MtpDevice::MtpDevice(struct usb_device* device, int interface,
+            struct usb_endpoint *ep_in, struct usb_endpoint *ep_out,
+            struct usb_endpoint *ep_intr)
+    :   mDevice(device),
+        mInterface(interface),
+        mEndpointIn(ep_in),
+        mEndpointOut(ep_out),
+        mEndpointIntr(ep_intr),
+        mDeviceInfo(NULL),
+        mID(usb_device_get_unique_id(device)),
+        mSessionID(0),
+        mTransactionID(0)
+{
+}
+
+MtpDevice::~MtpDevice() {
+    close();
+    for (int i = 0; i < mDeviceProperties.size(); i++)
+        delete mDeviceProperties[i];
+}
+
+void MtpDevice::initialize() {
+    openSession();
+    mDeviceInfo = getDeviceInfo();
+    if (mDeviceInfo) {
+        mDeviceInfo->print();
+
+        if (mDeviceInfo->mDeviceProperties) {
+            int count = mDeviceInfo->mDeviceProperties->size();
+            for (int i = 0; i < count; i++) {
+                MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
+                MtpProperty* property = getDevicePropDesc(propCode);
+                if (property) {
+                    property->print();
+                    mDeviceProperties.push(property);
+                }
+            }
+        }
+    }
+}
+
+void MtpDevice::close() {
+    if (mDevice) {
+        usb_device_release_interface(mDevice, mInterface);
+        usb_device_close(mDevice);
+        mDevice = NULL;
+    }
+}
+
+const char* MtpDevice::getDeviceName() {
+    if (mDevice)
+        return usb_device_get_name(mDevice);
+    else
+        return "???";
+}
+
+bool MtpDevice::openSession() {
+    Mutex::Autolock autoLock(mMutex);
+
+    mSessionID = 0;
+    mTransactionID = 0;
+    MtpSessionID newSession = 1;
+    mRequest.reset();
+    mRequest.setParameter(1, newSession);
+    if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
+        return false;
+    MtpResponseCode ret = readResponse();
+    if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
+        newSession = mResponse.getParameter(1);
+    else if (ret != MTP_RESPONSE_OK)
+        return false;
+
+    mSessionID = newSession;
+    mTransactionID = 1;
+    return true;
+}
+
+bool MtpDevice::closeSession() {
+    // FIXME
+    return true;
+}
+
+MtpDeviceInfo* MtpDevice::getDeviceInfo() {
+    Mutex::Autolock autoLock(mMutex);
+
+    mRequest.reset();
+    if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
+        return NULL;
+    if (!readData())
+        return NULL;
+    MtpResponseCode ret = readResponse();
+    if (ret == MTP_RESPONSE_OK) {
+        MtpDeviceInfo* info = new MtpDeviceInfo;
+        info->read(mData);
+        return info;
+    }
+    return NULL;
+}
+
+MtpStorageIDList* MtpDevice::getStorageIDs() {
+    Mutex::Autolock autoLock(mMutex);
+
+    mRequest.reset();
+    if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
+        return NULL;
+    if (!readData())
+        return NULL;
+    MtpResponseCode ret = readResponse();
+    if (ret == MTP_RESPONSE_OK) {
+        return mData.getAUInt32();
+    }
+    return NULL;
+}
+
+MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
+    Mutex::Autolock autoLock(mMutex);
+
+    mRequest.reset();
+    mRequest.setParameter(1, storageID);
+    if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
+        return NULL;
+    if (!readData())
+        return NULL;
+    MtpResponseCode ret = readResponse();
+    if (ret == MTP_RESPONSE_OK) {
+        MtpStorageInfo* info = new MtpStorageInfo(storageID);
+        info->read(mData);
+        return info;
+    }
+    return NULL;
+}
+
+MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
+            MtpObjectFormat format, MtpObjectHandle parent) {
+    Mutex::Autolock autoLock(mMutex);
+
+    mRequest.reset();
+    mRequest.setParameter(1, storageID);
+    mRequest.setParameter(2, format);
+    mRequest.setParameter(3, parent);
+    if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
+        return NULL;
+    if (!readData())
+        return NULL;
+    MtpResponseCode ret = readResponse();
+    if (ret == MTP_RESPONSE_OK) {
+        return mData.getAUInt32();
+    }
+    return NULL;
+}
+
+MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
+    Mutex::Autolock autoLock(mMutex);
+
+    // FIXME - we might want to add some caching here
+
+    mRequest.reset();
+    mRequest.setParameter(1, handle);
+    if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
+        return NULL;
+    if (!readData())
+        return NULL;
+    MtpResponseCode ret = readResponse();
+    if (ret == MTP_RESPONSE_OK) {
+        MtpObjectInfo* info = new MtpObjectInfo(handle);
+        info->read(mData);
+        return info;
+    }
+    return NULL;
+}
+
+void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
+    Mutex::Autolock autoLock(mMutex);
+
+    mRequest.reset();
+    mRequest.setParameter(1, handle);
+    if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
+        MtpResponseCode ret = readResponse();
+        if (ret == MTP_RESPONSE_OK) {
+            return mData.getData(outLength);
+        }
+    }
+    outLength = 0;
+    return NULL;
+}
+
+MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
+    Mutex::Autolock autoLock(mMutex);
+
+    mRequest.reset();
+    MtpObjectHandle parent = info->mParent;
+    if (parent == 0)
+        parent = MTP_PARENT_ROOT;
+
+    mRequest.setParameter(1, info->mStorageID);
+    mRequest.setParameter(2, info->mParent);
+
+    mData.putUInt32(info->mStorageID);
+    mData.putUInt16(info->mFormat);
+    mData.putUInt16(info->mProtectionStatus);
+    mData.putUInt32(info->mCompressedSize);
+    mData.putUInt16(info->mThumbFormat);
+    mData.putUInt32(info->mThumbCompressedSize);
+    mData.putUInt32(info->mThumbPixWidth);
+    mData.putUInt32(info->mThumbPixHeight);
+    mData.putUInt32(info->mImagePixWidth);
+    mData.putUInt32(info->mImagePixHeight);
+    mData.putUInt32(info->mImagePixDepth);
+    mData.putUInt32(info->mParent);
+    mData.putUInt16(info->mAssociationType);
+    mData.putUInt32(info->mAssociationDesc);
+    mData.putUInt32(info->mSequenceNumber);
+    mData.putString(info->mName);
+
+    char created[100], modified[100];
+    formatDateTime(info->mDateCreated, created, sizeof(created));
+    formatDateTime(info->mDateModified, modified, sizeof(modified));
+
+    mData.putString(created);
+    mData.putString(modified);
+    if (info->mKeywords)
+        mData.putString(info->mKeywords);
+    else
+        mData.putEmptyString();
+
+   if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
+        MtpResponseCode ret = readResponse();
+        if (ret == MTP_RESPONSE_OK) {
+            info->mStorageID = mResponse.getParameter(1);
+            info->mParent = mResponse.getParameter(2);
+            info->mHandle = mResponse.getParameter(3);
+            return info->mHandle;
+        }
+    }
+    return (MtpObjectHandle)-1;
+}
+
+bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
+    Mutex::Autolock autoLock(mMutex);
+
+    int remaining = info->mCompressedSize;
+    mRequest.reset();
+    mRequest.setParameter(1, info->mHandle);
+    if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
+        // send data header
+        writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
+
+        char buffer[65536];
+        while (remaining > 0) {
+            int count = read(srcFD, buffer, sizeof(buffer));
+            if (count > 0) {
+                int written = mData.write(mEndpointOut, buffer, count);
+                // FIXME check error
+                remaining -= count;
+            } else {
+                break;
+            }
+        }
+    }
+    MtpResponseCode ret = readResponse();
+    return (remaining == 0 && ret == MTP_RESPONSE_OK);
+}
+
+bool MtpDevice::deleteObject(MtpObjectHandle handle) {
+    Mutex::Autolock autoLock(mMutex);
+
+    mRequest.reset();
+    mRequest.setParameter(1, handle);
+    if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
+        MtpResponseCode ret = readResponse();
+        if (ret == MTP_RESPONSE_OK)
+            return true;
+    }
+    return false;
+}
+
+MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
+    MtpObjectInfo* info = getObjectInfo(handle);
+    if (info)
+        return info->mParent;
+    else
+        return -1;
+}
+
+MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
+    MtpObjectInfo* info = getObjectInfo(handle);
+    if (info)
+        return info->mStorageID;
+    else
+        return -1;
+}
+
+MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
+    Mutex::Autolock autoLock(mMutex);
+
+    mRequest.reset();
+    mRequest.setParameter(1, code);
+    if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
+        return NULL;
+    if (!readData())
+        return NULL;
+    MtpResponseCode ret = readResponse();
+    if (ret == MTP_RESPONSE_OK) {
+        MtpProperty* property = new MtpProperty;
+        property->read(mData);
+        return property;
+    }
+    return NULL;
+}
+
+class ReadObjectThread : public Thread {
+private:
+    MtpDevice*          mDevice;
+    MtpObjectHandle     mHandle;
+    int                 mObjectSize;
+    void*               mInitialData;
+    int                 mInitialDataLength;
+    int                 mFD;
+
+public:
+    ReadObjectThread(MtpDevice* device, MtpObjectHandle handle, int objectSize)
+        : mDevice(device),
+          mHandle(handle),
+          mObjectSize(objectSize),
+          mInitialData(NULL),
+          mInitialDataLength(0)
+    {
+    }
+
+    virtual ~ReadObjectThread() {
+        if (mFD >= 0)
+            close(mFD);
+        free(mInitialData);
+    }
+
+    // returns file descriptor
+    int init() {
+        mDevice->mRequest.reset();
+        mDevice->mRequest.setParameter(1, mHandle);
+        if (mDevice->sendRequest(MTP_OPERATION_GET_OBJECT)
+                && mDevice->mData.readDataHeader(mDevice->mEndpointIn)) {
+
+            // mData will contain header and possibly the beginning of the object data
+            mInitialData = mDevice->mData.getData(mInitialDataLength);
+
+            // create a pipe for the client to read from
+            int pipefd[2];
+            if (pipe(pipefd) < 0) {
+                LOGE("pipe failed (%s)", strerror(errno));
+                return -1;
+            }
+
+            mFD = pipefd[1];
+            return pipefd[0];
+        } else {
+           return -1;
+        }
+    }
+
+    virtual bool threadLoop() {
+        int remaining = mObjectSize;
+        if (mInitialData) {
+            write(mFD, mInitialData, mInitialDataLength);
+            remaining -= mInitialDataLength;
+            free(mInitialData);
+            mInitialData = NULL;
+        }
+
+        char buffer[16384];
+        while (remaining > 0) {
+            int readSize = (remaining > sizeof(buffer) ? sizeof(buffer) : remaining);
+            int count = mDevice->mData.readData(mDevice->mEndpointIn, buffer, readSize);
+            int written;
+            if (count >= 0) {
+                int written = write(mFD, buffer, count);
+                // FIXME check error
+                remaining -= count;
+            } else {
+                break;
+            }
+        }
+
+        MtpResponseCode ret = mDevice->readResponse();
+        mDevice->mMutex.unlock();
+        return false;
+    }
+};
+
+    // returns the file descriptor for a pipe to read the object's data
+int MtpDevice::readObject(MtpObjectHandle handle, int objectSize) {
+    mMutex.lock();
+
+    ReadObjectThread* thread = new ReadObjectThread(this, handle, objectSize);
+    int fd = thread->init();
+    if (fd < 0) {
+        delete thread;
+        mMutex.unlock();
+    } else {
+        thread->run("ReadObjectThread");
+    }
+    return fd;
+}
+
+bool MtpDevice::sendRequest(MtpOperationCode operation) {
+    LOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
+    mRequest.setOperationCode(operation);
+    if (mTransactionID > 0)
+        mRequest.setTransactionID(mTransactionID++);
+    int ret = mRequest.write(mEndpointOut);
+    mRequest.dump();
+    return (ret > 0);
+}
+
+bool MtpDevice::sendData() {
+    LOGV("sendData\n");
+    mData.setOperationCode(mRequest.getOperationCode());
+    mData.setTransactionID(mRequest.getTransactionID());
+    int ret = mData.write(mEndpointOut);
+    mData.dump();
+    return (ret > 0);
+}
+
+bool MtpDevice::readData() {
+    mData.reset();
+    int ret = mData.read(mEndpointIn);
+    LOGV("readData returned %d\n", ret);
+    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
+        mData.dump();
+        return true;
+    }
+    else {
+        LOGV("readResponse failed\n");
+        return false;
+    }
+}
+
+bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
+    mData.setOperationCode(operation);
+    mData.setTransactionID(mRequest.getTransactionID());
+    return (!mData.writeDataHeader(mEndpointOut, dataLength));
+}
+
+MtpResponseCode MtpDevice::readResponse() {
+    LOGV("readResponse\n");
+    int ret = mResponse.read(mEndpointIn);
+    if (ret >= MTP_CONTAINER_HEADER_SIZE) {
+        mResponse.dump();
+        return mResponse.getResponseCode();
+    }
+    else {
+        LOGD("readResponse failed\n");
+        return -1;
+    }
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h
new file mode 100644
index 0000000..57f492f
--- /dev/null
+++ b/media/mtp/MtpDevice.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_DEVICE_H
+#define _MTP_DEVICE_H
+
+#include "MtpRequestPacket.h"
+#include "MtpDataPacket.h"
+#include "MtpResponsePacket.h"
+#include "MtpTypes.h"
+
+#include <utils/threads.h>
+
+struct usb_device;
+
+namespace android {
+
+class MtpDeviceInfo;
+class MtpObjectInfo;
+class MtpStorageInfo;
+
+class MtpDevice {
+private:
+    struct usb_device*      mDevice;
+    int                     mInterface;
+    struct usb_endpoint*    mEndpointIn;
+    struct usb_endpoint*    mEndpointOut;
+    struct usb_endpoint*    mEndpointIntr;
+    MtpDeviceInfo*          mDeviceInfo;
+    MtpPropertyList         mDeviceProperties;
+
+    // a unique ID for the device
+    int                     mID;
+
+    // current session ID
+    MtpSessionID            mSessionID;
+    // current transaction ID
+    MtpTransactionID        mTransactionID;
+
+    MtpRequestPacket        mRequest;
+    MtpDataPacket           mData;
+    MtpResponsePacket       mResponse;
+
+    // to ensure only one MTP transaction at a time
+    Mutex                   mMutex;
+
+public:
+                            MtpDevice(struct usb_device* device, int interface,
+                                    struct usb_endpoint *ep_in, struct usb_endpoint *ep_out,
+                                    struct usb_endpoint *ep_intr);
+    virtual                 ~MtpDevice();
+
+    inline int              getID() const { return mID; }
+
+    void                    initialize();
+    void                    close();
+    const char*             getDeviceName();
+
+    bool                    openSession();
+    bool                    closeSession();
+
+    MtpDeviceInfo*          getDeviceInfo();
+    MtpStorageIDList*       getStorageIDs();
+    MtpStorageInfo*         getStorageInfo(MtpStorageID storageID);
+    MtpObjectHandleList*    getObjectHandles(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent);
+    MtpObjectInfo*          getObjectInfo(MtpObjectHandle handle);
+    void*                   getThumbnail(MtpObjectHandle handle, int& outLength);
+    MtpObjectHandle         sendObjectInfo(MtpObjectInfo* info);
+    bool                    sendObject(MtpObjectInfo* info, int srcFD);
+    bool                    deleteObject(MtpObjectHandle handle);
+    MtpObjectHandle         getParent(MtpObjectHandle handle);
+    MtpObjectHandle         getStorageID(MtpObjectHandle handle);
+
+    MtpProperty*            getDevicePropDesc(MtpDeviceProperty code);
+
+    // returns the file descriptor for a pipe to read the object's data
+    int                     readObject(MtpObjectHandle handle, int objectSize);
+
+private:
+    friend class ReadObjectThread;
+
+    bool                    sendRequest(MtpOperationCode operation);
+    bool                    sendData();
+    bool                    readData();
+    bool                    writeDataHeader(MtpOperationCode operation, int dataLength);
+    MtpResponseCode         readResponse();
+
+};
+
+}; // namespace android
+
+#endif // _MTP_DEVICE_H
diff --git a/media/mtp/MtpDeviceInfo.cpp b/media/mtp/MtpDeviceInfo.cpp
new file mode 100644
index 0000000..5a9322e
--- /dev/null
+++ b/media/mtp/MtpDeviceInfo.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpDeviceInfo"
+
+#include "MtpDebug.h"
+#include "MtpDataPacket.h"
+#include "MtpDeviceInfo.h"
+#include "MtpStringBuffer.h"
+
+namespace android {
+
+MtpDeviceInfo::MtpDeviceInfo()
+    :   mStandardVersion(0),
+        mVendorExtensionID(0),
+        mVendorExtensionVersion(0),
+        mVendorExtensionDesc(NULL),
+        mFunctionalCode(0),
+        mOperations(NULL),
+        mEvents(NULL),
+        mDeviceProperties(NULL),
+        mCaptureFormats(NULL),
+        mPlaybackFormats(NULL),
+        mManufacturer(NULL),
+        mModel(NULL),
+        mVersion(NULL),
+        mSerial(NULL)
+{
+}
+
+MtpDeviceInfo::~MtpDeviceInfo() {
+    if (mVendorExtensionDesc)
+        free(mVendorExtensionDesc);
+    delete mOperations;
+    delete mEvents;
+    delete mDeviceProperties;
+    delete mCaptureFormats;
+    delete mPlaybackFormats;
+    if (mManufacturer)
+        free(mManufacturer);
+    if (mModel)
+        free(mModel);
+    if (mVersion)
+        free(mVersion);
+    if (mSerial)
+        free(mSerial);
+}
+
+void MtpDeviceInfo::read(MtpDataPacket& packet) {
+    MtpStringBuffer string;
+
+    // read the device info
+    mStandardVersion = packet.getUInt16();
+    mVendorExtensionID = packet.getUInt32();
+    mVendorExtensionVersion = packet.getUInt16();
+
+    packet.getString(string);
+    mVendorExtensionDesc = strdup((const char *)string);
+
+    mFunctionalCode = packet.getUInt16();
+    mOperations = packet.getAUInt16();
+    mEvents = packet.getAUInt16();
+    mDeviceProperties = packet.getAUInt16();
+    mCaptureFormats = packet.getAUInt16();
+    mPlaybackFormats = packet.getAUInt16();
+
+    packet.getString(string);
+    mManufacturer = strdup((const char *)string);
+    packet.getString(string);
+    mModel = strdup((const char *)string);
+    packet.getString(string);
+    mVersion = strdup((const char *)string);
+    packet.getString(string);
+    mSerial = strdup((const char *)string);
+}
+
+void MtpDeviceInfo::print() {
+    LOGV("Device Info:\n\tmStandardVersion: %d\n\tmVendorExtensionID: %d\n\tmVendorExtensionVersiony: %d\n",
+            mStandardVersion, mVendorExtensionID, mVendorExtensionVersion);
+    LOGV("\tmVendorExtensionDesc: %s\n\tmFunctionalCode: %d\n\tmManufacturer: %s\n\tmModel: %s\n\tmVersion: %s\n\tmSerial: %s\n",
+            mVendorExtensionDesc, mFunctionalCode, mManufacturer, mModel, mVersion, mSerial);
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpDeviceInfo.h b/media/mtp/MtpDeviceInfo.h
new file mode 100644
index 0000000..2abaa10
--- /dev/null
+++ b/media/mtp/MtpDeviceInfo.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_DEVICE_INFO_H
+#define _MTP_DEVICE_INFO_H
+
+struct stat;
+
+namespace android {
+
+class MtpDataPacket;
+
+class MtpDeviceInfo {
+public:
+    uint16_t                mStandardVersion;
+    uint32_t                mVendorExtensionID;
+    uint16_t                mVendorExtensionVersion;
+    char*                   mVendorExtensionDesc;
+    uint16_t                mFunctionalCode;
+    UInt16List*             mOperations;
+    UInt16List*             mEvents;
+    MtpDevicePropertyList*  mDeviceProperties;
+    MtpObjectFormatList*    mCaptureFormats;
+    MtpObjectFormatList*    mPlaybackFormats;
+    char*                   mManufacturer;
+    char*                   mModel;
+    char*                   mVersion;
+    char*                   mSerial;
+
+public:
+                            MtpDeviceInfo();
+    virtual                 ~MtpDeviceInfo();
+
+    void                    read(MtpDataPacket& packet);
+
+    void                    print();
+};
+
+}; // namespace android
+
+#endif // _MTP_DEVICE_INFO_H
diff --git a/media/mtp/MtpEventPacket.cpp b/media/mtp/MtpEventPacket.cpp
new file mode 100644
index 0000000..fc74542
--- /dev/null
+++ b/media/mtp/MtpEventPacket.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpEventPacket"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#ifdef MTP_DEVICE
+#include <linux/usb/f_mtp.h>
+#endif
+
+#include "MtpEventPacket.h"
+
+namespace android {
+
+MtpEventPacket::MtpEventPacket()
+    :   MtpPacket(512)
+{
+}
+
+MtpEventPacket::~MtpEventPacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpEventPacket::write(int fd) {
+    struct mtp_event    event;
+
+    putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+    putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_EVENT);
+
+    event.data = mBuffer;
+    event.length = mPacketSize;
+    int ret = ::ioctl(fd, MTP_SEND_EVENT, (unsigned long)&event);
+    return (ret < 0 ? ret : 0);
+}
+#endif
+
+#ifdef MTP_HOST
+    // read our buffer from the given endpoint
+int MtpEventPacket::read(struct usb_endpoint *ep) {
+    int ret = transfer(ep, mBuffer, mBufferSize);
+     if (ret >= 0)
+        mPacketSize = ret;
+    else
+        mPacketSize = 0;
+    return ret;
+}
+#endif
+
+}  // namespace android
+
diff --git a/media/mtp/MtpEventPacket.h b/media/mtp/MtpEventPacket.h
new file mode 100644
index 0000000..30ae869
--- /dev/null
+++ b/media/mtp/MtpEventPacket.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_EVENT_PACKET_H
+#define _MTP_EVENT_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+namespace android {
+
+class MtpEventPacket : public MtpPacket {
+
+public:
+                        MtpEventPacket();
+    virtual             ~MtpEventPacket();
+
+#ifdef MTP_DEVICE
+    // write our data to the given file descriptor
+    int                 write(int fd);
+#endif
+
+#ifdef MTP_HOST
+    // read our buffer from the given endpoint
+    int                 read(struct usb_endpoint *ep);
+#endif
+
+    inline MtpEventCode     getEventCode() const { return getContainerCode(); }
+    inline void             setEventCode(MtpEventCode code)
+                                                     { return setContainerCode(code); }
+};
+
+}; // namespace android
+
+#endif // _MTP_EVENT_PACKET_H
diff --git a/media/mtp/MtpObjectInfo.cpp b/media/mtp/MtpObjectInfo.cpp
new file mode 100644
index 0000000..ea68c3b
--- /dev/null
+++ b/media/mtp/MtpObjectInfo.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpObjectInfo"
+
+#include "MtpDebug.h"
+#include "MtpDataPacket.h"
+#include "MtpObjectInfo.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+
+namespace android {
+
+MtpObjectInfo::MtpObjectInfo(MtpObjectHandle handle)
+    :   mHandle(handle),
+        mStorageID(0),
+        mFormat(0),
+        mProtectionStatus(0),
+        mCompressedSize(0),
+        mThumbFormat(0),
+        mThumbCompressedSize(0),
+        mThumbPixWidth(0),
+        mThumbPixHeight(0),
+        mImagePixWidth(0),
+        mImagePixHeight(0),
+        mImagePixDepth(0),
+        mParent(0),
+        mAssociationType(0),
+        mAssociationDesc(0),
+        mSequenceNumber(0),
+        mName(NULL),
+        mDateCreated(0),
+        mDateModified(0),
+        mKeywords(NULL)
+{
+}
+
+MtpObjectInfo::~MtpObjectInfo() {
+    if (mName)
+        free(mName);
+    if (mKeywords)
+        free(mKeywords);
+}
+
+void MtpObjectInfo::read(MtpDataPacket& packet) {
+    MtpStringBuffer string;
+    time_t time;
+
+    mStorageID = packet.getUInt32();
+    mFormat = packet.getUInt16();
+    mProtectionStatus = packet.getUInt16();
+    mCompressedSize = packet.getUInt32();
+    mThumbFormat = packet.getUInt16();
+    mThumbCompressedSize = packet.getUInt32();
+    mThumbPixWidth = packet.getUInt32();
+    mThumbPixHeight = packet.getUInt32();
+    mImagePixWidth = packet.getUInt32();
+    mImagePixHeight = packet.getUInt32();
+    mImagePixDepth = packet.getUInt32();
+    mParent = packet.getUInt32();
+    mAssociationType = packet.getUInt16();
+    mAssociationDesc = packet.getUInt32();
+    mSequenceNumber = packet.getUInt32();
+
+    packet.getString(string);
+    mName = strdup((const char *)string);
+
+    packet.getString(string);
+    if (parseDateTime((const char*)string, time))
+        mDateCreated = time;
+
+    packet.getString(string);
+    if (parseDateTime((const char*)string, time))
+        mDateModified = time;
+
+    packet.getString(string);
+    mKeywords = strdup((const char *)string);
+}
+
+void MtpObjectInfo::print() {
+    LOGD("MtpObject Info %08X: %s\n", mHandle, mName);
+    LOGD("  mStorageID: %08X mFormat: %04X mProtectionStatus: %d\n",
+            mStorageID, mFormat, mProtectionStatus);
+    LOGD("  mCompressedSize: %d mThumbFormat: %04X mThumbCompressedSize: %d\n",
+            mCompressedSize, mFormat, mThumbCompressedSize);
+    LOGD("  mThumbPixWidth: %d mThumbPixHeight: %d\n", mThumbPixWidth, mThumbPixHeight);
+    LOGD("  mImagePixWidth: %d mImagePixHeight: %d mImagePixDepth: %d\n",
+            mImagePixWidth, mImagePixHeight, mImagePixDepth);
+    LOGD("  mParent: %08X mAssociationType: %04X mAssociationDesc: %04X\n",
+            mParent, mAssociationType, mAssociationDesc);
+    LOGD("  mSequenceNumber: %d mDateCreated: %ld mDateModified: %ld mKeywords: %s\n",
+            mSequenceNumber, mDateCreated, mDateModified, mKeywords);
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpObjectInfo.h b/media/mtp/MtpObjectInfo.h
new file mode 100644
index 0000000..c7a449ca
--- /dev/null
+++ b/media/mtp/MtpObjectInfo.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_OBJECT_INFO_H
+#define _MTP_OBJECT_INFO_H
+
+#include "MtpTypes.h"
+
+namespace android {
+
+class MtpDataPacket;
+
+class MtpObjectInfo {
+public:
+    MtpObjectHandle     mHandle;
+    MtpStorageID        mStorageID;
+    MtpObjectFormat     mFormat;
+    uint16_t            mProtectionStatus;
+    uint32_t            mCompressedSize;
+    MtpObjectFormat     mThumbFormat;
+    uint32_t            mThumbCompressedSize;
+    uint32_t            mThumbPixWidth;
+    uint32_t            mThumbPixHeight;
+    uint32_t            mImagePixWidth;
+    uint32_t            mImagePixHeight;
+    uint32_t            mImagePixDepth;
+    MtpObjectHandle     mParent;
+    uint16_t            mAssociationType;
+    uint32_t            mAssociationDesc;
+    uint32_t            mSequenceNumber;
+    char*               mName;
+    time_t              mDateCreated;
+    time_t              mDateModified;
+    char*               mKeywords;
+
+public:
+                        MtpObjectInfo(MtpObjectHandle handle);
+    virtual             ~MtpObjectInfo();
+
+    void                read(MtpDataPacket& packet);
+
+    void                print();
+};
+
+}; // namespace android
+
+#endif // _MTP_OBJECT_INFO_H
diff --git a/media/mtp/MtpPacket.cpp b/media/mtp/MtpPacket.cpp
new file mode 100644
index 0000000..42bf8ba
--- /dev/null
+++ b/media/mtp/MtpPacket.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpPacket"
+
+#include "MtpDebug.h"
+#include "MtpPacket.h"
+#include "mtp.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <usbhost/usbhost.h>
+
+namespace android {
+
+MtpPacket::MtpPacket(int bufferSize)
+    :   mBuffer(NULL),
+        mBufferSize(bufferSize),
+        mAllocationIncrement(bufferSize),
+        mPacketSize(0)
+{
+    mBuffer = (uint8_t *)malloc(bufferSize);
+    if (!mBuffer) {
+        LOGE("out of memory!");
+        abort();
+    }
+}
+
+MtpPacket::~MtpPacket() {
+    if (mBuffer)
+        free(mBuffer);
+}
+
+void MtpPacket::reset() {
+    allocate(MTP_CONTAINER_HEADER_SIZE);
+    mPacketSize = MTP_CONTAINER_HEADER_SIZE;
+    memset(mBuffer, 0, mBufferSize);
+}
+
+void MtpPacket::allocate(int length) {
+    if (length > mBufferSize) {
+        int newLength = length + mAllocationIncrement;
+        mBuffer = (uint8_t *)realloc(mBuffer, newLength);
+        if (!mBuffer) {
+            LOGE("out of memory!");
+            abort();
+        }
+        mBufferSize = newLength;
+    }
+}
+
+void MtpPacket::dump() {
+#define DUMP_BYTES_PER_ROW  16
+    char buffer[500];
+    char* bufptr = buffer;
+
+    for (int i = 0; i < mPacketSize; i++) {
+        sprintf(bufptr, "%02X ", mBuffer[i]);
+        bufptr += strlen(bufptr);
+        if (i % DUMP_BYTES_PER_ROW == (DUMP_BYTES_PER_ROW - 1)) {
+            LOGV("%s", buffer);
+            bufptr = buffer;
+        }
+    }
+    if (bufptr != buffer) {
+        // print last line
+        LOGV("%s", buffer);
+    }
+    LOGV("\n");
+}
+
+uint16_t MtpPacket::getUInt16(int offset) const {
+    return ((uint16_t)mBuffer[offset + 1] << 8) | (uint16_t)mBuffer[offset];
+}
+
+uint32_t MtpPacket::getUInt32(int offset) const {
+    return ((uint32_t)mBuffer[offset + 3] << 24) | ((uint32_t)mBuffer[offset + 2] << 16) |
+           ((uint32_t)mBuffer[offset + 1] << 8)  | (uint32_t)mBuffer[offset];
+}
+
+void MtpPacket::putUInt16(int offset, uint16_t value) {
+    mBuffer[offset++] = (uint8_t)(value & 0xFF);
+    mBuffer[offset++] = (uint8_t)((value >> 8) & 0xFF);
+}
+
+void MtpPacket::putUInt32(int offset, uint32_t value) {
+    mBuffer[offset++] = (uint8_t)(value & 0xFF);
+    mBuffer[offset++] = (uint8_t)((value >> 8) & 0xFF);
+    mBuffer[offset++] = (uint8_t)((value >> 16) & 0xFF);
+    mBuffer[offset++] = (uint8_t)((value >> 24) & 0xFF);
+}
+
+uint16_t MtpPacket::getContainerCode() const {
+    return getUInt16(MTP_CONTAINER_CODE_OFFSET);
+}
+
+void MtpPacket::setContainerCode(uint16_t code) {
+    putUInt16(MTP_CONTAINER_CODE_OFFSET, code);
+}
+
+MtpTransactionID MtpPacket::getTransactionID() const {
+    return getUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET);
+}
+
+void MtpPacket::setTransactionID(MtpTransactionID id) {
+    putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id);
+}
+
+uint32_t MtpPacket::getParameter(int index) const {
+    if (index < 1 || index > 5) {
+        LOGE("index %d out of range in MtpRequestPacket::getParameter", index);
+        return 0;
+    }
+    return getUInt32(MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t));
+}
+
+void MtpPacket::setParameter(int index, uint32_t value) {
+    if (index < 1 || index > 5) {
+        LOGE("index %d out of range in MtpResponsePacket::setParameter", index);
+        return;
+    }
+    int offset = MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t);
+    if (mPacketSize < offset + sizeof(uint32_t))
+        mPacketSize = offset + sizeof(uint32_t);
+    putUInt32(offset, value);
+}
+
+#ifdef MTP_HOST
+int MtpPacket::transfer(struct usb_endpoint *ep, void* buffer, int length) {
+    if (usb_endpoint_queue(ep, buffer, length)) {
+        LOGE("usb_endpoint_queue failed, errno: %d", errno);
+        return -1;
+    }
+    int ep_num;
+    return usb_endpoint_wait(usb_endpoint_get_device(ep), &ep_num);
+}
+#endif
+
+}  // namespace android
diff --git a/media/mtp/MtpPacket.h b/media/mtp/MtpPacket.h
new file mode 100644
index 0000000..9c8d6da
--- /dev/null
+++ b/media/mtp/MtpPacket.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_PACKET_H
+#define _MTP_PACKET_H
+
+#include "MtpTypes.h"
+
+struct usb_endpoint;
+
+namespace android {
+
+class MtpPacket {
+
+protected:
+    uint8_t*            mBuffer;
+    // current size of the buffer
+    int                 mBufferSize;
+    // number of bytes to add when resizing the buffer
+    int                 mAllocationIncrement;
+    // size of the data in the packet
+    int                 mPacketSize;
+
+public:
+                        MtpPacket(int bufferSize);
+    virtual             ~MtpPacket();
+
+    // sets packet size to the default container size and sets buffer to zero
+    virtual void        reset();
+
+    void                allocate(int length);
+    void                dump();
+
+    uint16_t            getContainerCode() const;
+    void                setContainerCode(uint16_t code);
+
+    MtpTransactionID    getTransactionID() const;
+    void                setTransactionID(MtpTransactionID id);
+
+    uint32_t            getParameter(int index) const;
+    void                setParameter(int index, uint32_t value);
+
+#ifdef MTP_HOST
+    int                 transfer(struct usb_endpoint *ep, void* buffer, int length);
+#endif
+
+protected:
+    uint16_t            getUInt16(int offset) const;
+    uint32_t            getUInt32(int offset) const;
+    void                putUInt16(int offset, uint16_t value);
+    void                putUInt32(int offset, uint32_t value);
+};
+
+}; // namespace android
+
+#endif // _MTP_PACKET_H
diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp
new file mode 100644
index 0000000..bbd0237
--- /dev/null
+++ b/media/mtp/MtpProperty.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpProperty"
+
+#include "MtpDataPacket.h"
+#include "MtpProperty.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+
+namespace android {
+
+MtpProperty::MtpProperty()
+    :   mCode(0),
+        mType(0),
+        mWriteable(false),
+        mDefaultArrayLength(0),
+        mDefaultArrayValues(NULL),
+        mCurrentArrayLength(0),
+        mCurrentArrayValues(NULL),
+        mGroupCode(0),
+        mFormFlag(kFormNone),
+        mEnumLength(0),
+        mEnumValues(NULL)
+{
+    memset(&mDefaultValue, 0, sizeof(mDefaultValue));
+    memset(&mCurrentValue, 0, sizeof(mCurrentValue));
+    memset(&mMinimumValue, 0, sizeof(mMinimumValue));
+    memset(&mMaximumValue, 0, sizeof(mMaximumValue));
+}
+
+MtpProperty::MtpProperty(MtpPropertyCode propCode,
+                         MtpDataType type,
+                         bool writeable,
+                         int defaultValue)
+    :   mCode(propCode),
+        mType(type),
+        mWriteable(writeable),
+        mDefaultArrayLength(0),
+        mDefaultArrayValues(NULL),
+        mCurrentArrayLength(0),
+        mCurrentArrayValues(NULL),
+        mGroupCode(0),
+        mFormFlag(kFormNone),
+        mEnumLength(0),
+        mEnumValues(NULL)
+{
+    memset(&mDefaultValue, 0, sizeof(mDefaultValue));
+    memset(&mCurrentValue, 0, sizeof(mCurrentValue));
+    memset(&mMinimumValue, 0, sizeof(mMinimumValue));
+    memset(&mMaximumValue, 0, sizeof(mMaximumValue));
+
+    if (defaultValue) {
+        switch (type) {
+            case MTP_TYPE_INT8:
+                mDefaultValue.u.i8 = defaultValue;
+                break;
+            case MTP_TYPE_UINT8:
+                mDefaultValue.u.u8 = defaultValue;
+                break;
+            case MTP_TYPE_INT16:
+                mDefaultValue.u.i16 = defaultValue;
+                break;
+            case MTP_TYPE_UINT16:
+                mDefaultValue.u.u16 = defaultValue;
+                break;
+            case MTP_TYPE_INT32:
+                mDefaultValue.u.i32 = defaultValue;
+                break;
+            case MTP_TYPE_UINT32:
+                mDefaultValue.u.u32 = defaultValue;
+                break;
+            case MTP_TYPE_INT64:
+                mDefaultValue.u.i64 = defaultValue;
+                break;
+            case MTP_TYPE_UINT64:
+                mDefaultValue.u.u64 = defaultValue;
+                break;
+            default:
+                LOGE("unknown type %04X in MtpProperty::MtpProperty", type);
+        }
+    }
+}
+
+MtpProperty::~MtpProperty() {
+    if (mType == MTP_TYPE_STR) {
+        // free all strings
+        free(mDefaultValue.str);
+        free(mCurrentValue.str);
+        free(mMinimumValue.str);
+        free(mMaximumValue.str);
+        if (mDefaultArrayValues) {
+            for (int i = 0; i < mDefaultArrayLength; i++)
+                free(mDefaultArrayValues[i].str);
+        }
+        if (mCurrentArrayValues) {
+            for (int i = 0; i < mCurrentArrayLength; i++)
+                free(mCurrentArrayValues[i].str);
+        }
+        if (mEnumValues) {
+            for (int i = 0; i < mEnumLength; i++)
+                free(mEnumValues[i].str);
+        }
+    }
+    delete[] mDefaultArrayValues;
+    delete[] mCurrentArrayValues;
+    delete[] mEnumValues;
+}
+
+void MtpProperty::read(MtpDataPacket& packet) {
+    bool deviceProp = isDeviceProperty();
+
+    mCode = packet.getUInt16();
+    mType = packet.getUInt16();
+    mWriteable = (packet.getUInt8() == 1);
+    switch (mType) {
+        case MTP_TYPE_AINT8:
+        case MTP_TYPE_AUINT8:
+        case MTP_TYPE_AINT16:
+        case MTP_TYPE_AUINT16:
+        case MTP_TYPE_AINT32:
+        case MTP_TYPE_AUINT32:
+        case MTP_TYPE_AINT64:
+        case MTP_TYPE_AUINT64:
+        case MTP_TYPE_AINT128:
+        case MTP_TYPE_AUINT128:
+            mDefaultArrayValues = readArrayValues(packet, mDefaultArrayLength);
+            if (deviceProp)
+                mCurrentArrayValues = readArrayValues(packet, mCurrentArrayLength);
+            break;
+        default:
+            readValue(packet, mDefaultValue);
+            if (deviceProp)
+                readValue(packet, mCurrentValue);
+    }
+    if (!deviceProp)
+        mGroupCode = packet.getUInt32();
+    mFormFlag = packet.getUInt8();
+
+    if (mFormFlag == kFormRange) {
+            readValue(packet, mMinimumValue);
+            readValue(packet, mMaximumValue);
+            readValue(packet, mStepSize);
+    } else if (mFormFlag == kFormEnum) {
+        mEnumLength = packet.getUInt16();
+        mEnumValues = new MtpPropertyValue[mEnumLength];
+        for (int i = 0; i < mEnumLength; i++)
+            readValue(packet, mEnumValues[i]);
+    }
+}
+
+void MtpProperty::write(MtpDataPacket& packet) {
+    bool deviceProp = isDeviceProperty();
+
+    packet.putUInt16(mCode);
+    packet.putUInt16(mType);
+    packet.putUInt8(mWriteable ? 1 : 0);
+
+    switch (mType) {
+        case MTP_TYPE_AINT8:
+        case MTP_TYPE_AUINT8:
+        case MTP_TYPE_AINT16:
+        case MTP_TYPE_AUINT16:
+        case MTP_TYPE_AINT32:
+        case MTP_TYPE_AUINT32:
+        case MTP_TYPE_AINT64:
+        case MTP_TYPE_AUINT64:
+        case MTP_TYPE_AINT128:
+        case MTP_TYPE_AUINT128:
+            writeArrayValues(packet, mDefaultArrayValues, mDefaultArrayLength);
+            if (deviceProp)
+                writeArrayValues(packet, mCurrentArrayValues, mCurrentArrayLength);
+            break;
+        default:
+            writeValue(packet, mDefaultValue);
+            if (deviceProp)
+                writeValue(packet, mCurrentValue);
+    }
+    packet.putUInt32(mGroupCode);
+    if (!deviceProp)
+        packet.putUInt8(mFormFlag);
+    if (mFormFlag == kFormRange) {
+            writeValue(packet, mMinimumValue);
+            writeValue(packet, mMaximumValue);
+            writeValue(packet, mStepSize);
+    } else if (mFormFlag == kFormEnum) {
+        packet.putUInt16(mEnumLength);
+        for (int i = 0; i < mEnumLength; i++)
+            writeValue(packet, mEnumValues[i]);
+    }
+}
+
+void MtpProperty::setDefaultValue(const uint16_t* string) {
+    free(mDefaultValue.str);
+    if (string) {
+        MtpStringBuffer buffer(string);
+        mDefaultValue.str = strdup(buffer);
+    }
+    else
+        mDefaultValue.str = NULL;
+}
+
+void MtpProperty::setCurrentValue(const uint16_t* string) {
+    free(mCurrentValue.str);
+    if (string) {
+        MtpStringBuffer buffer(string);
+        mCurrentValue.str = strdup(buffer);
+    }
+    else
+        mCurrentValue.str = NULL;
+}
+
+void MtpProperty::print() {
+    LOGV("MtpProperty %04X\n", mCode);
+    LOGV("    type %04X\n", mType);
+    LOGV("    writeable %s\n", (mWriteable ? "true" : "false"));
+}
+
+void MtpProperty::readValue(MtpDataPacket& packet, MtpPropertyValue& value) {
+    MtpStringBuffer stringBuffer;
+
+    switch (mType) {
+        case MTP_TYPE_INT8:
+        case MTP_TYPE_AINT8:
+            value.u.i8 = packet.getInt8();
+            break;
+        case MTP_TYPE_UINT8:
+        case MTP_TYPE_AUINT8:
+            value.u.u8 = packet.getUInt8();
+            break;
+        case MTP_TYPE_INT16:
+        case MTP_TYPE_AINT16:
+            value.u.i16 = packet.getInt16();
+            break;
+        case MTP_TYPE_UINT16:
+        case MTP_TYPE_AUINT16:
+            value.u.u16 = packet.getUInt16();
+            break;
+        case MTP_TYPE_INT32:
+        case MTP_TYPE_AINT32:
+            value.u.i32 = packet.getInt32();
+            break;
+        case MTP_TYPE_UINT32:
+        case MTP_TYPE_AUINT32:
+            value.u.u32 = packet.getUInt32();
+            break;
+        case MTP_TYPE_INT64:
+        case MTP_TYPE_AINT64:
+            value.u.i64 = packet.getInt64();
+            break;
+        case MTP_TYPE_UINT64:
+        case MTP_TYPE_AUINT64:
+            value.u.u64 = packet.getUInt64();
+            break;
+        case MTP_TYPE_INT128:
+        case MTP_TYPE_AINT128:
+            packet.getInt128(value.u.i128);
+            break;
+        case MTP_TYPE_UINT128:
+        case MTP_TYPE_AUINT128:
+            packet.getUInt128(value.u.u128);
+            break;
+        case MTP_TYPE_STR:
+            packet.getString(stringBuffer);
+            value.str = strdup(stringBuffer);
+            break;
+        default:
+            LOGE("unknown type %04X in MtpProperty::readValue", mType);
+    }
+}
+
+void MtpProperty::writeValue(MtpDataPacket& packet, MtpPropertyValue& value) {
+    MtpStringBuffer stringBuffer;
+
+    switch (mType) {
+        case MTP_TYPE_INT8:
+        case MTP_TYPE_AINT8:
+            packet.putInt8(value.u.i8);
+            break;
+        case MTP_TYPE_UINT8:
+        case MTP_TYPE_AUINT8:
+            packet.putUInt8(value.u.u8);
+            break;
+        case MTP_TYPE_INT16:
+        case MTP_TYPE_AINT16:
+            packet.putInt16(value.u.i16);
+            break;
+        case MTP_TYPE_UINT16:
+        case MTP_TYPE_AUINT16:
+            packet.putUInt16(value.u.u16);
+            break;
+        case MTP_TYPE_INT32:
+        case MTP_TYPE_AINT32:
+            packet.putInt32(value.u.i32);
+            break;
+        case MTP_TYPE_UINT32:
+        case MTP_TYPE_AUINT32:
+            packet.putUInt32(value.u.u32);
+            break;
+        case MTP_TYPE_INT64:
+        case MTP_TYPE_AINT64:
+            packet.putInt64(value.u.i64);
+            break;
+        case MTP_TYPE_UINT64:
+        case MTP_TYPE_AUINT64:
+            packet.putUInt64(value.u.u64);
+            break;
+        case MTP_TYPE_INT128:
+        case MTP_TYPE_AINT128:
+            packet.putInt128(value.u.i128);
+            break;
+        case MTP_TYPE_UINT128:
+        case MTP_TYPE_AUINT128:
+            packet.putUInt128(value.u.u128);
+            break;
+        case MTP_TYPE_STR:
+            if (value.str)
+                packet.putString(value.str);
+            else
+                packet.putEmptyString();
+            break;
+        default:
+            LOGE("unknown type %04X in MtpProperty::writeValue", mType);
+    }
+}
+
+MtpPropertyValue* MtpProperty::readArrayValues(MtpDataPacket& packet, int& length) {
+    length = packet.getUInt32();
+    if (length == 0)
+        return NULL;
+    MtpPropertyValue* result = new MtpPropertyValue[length];
+    for (int i = 0; i < length; i++)
+        readValue(packet, result[i]);
+    return result;
+}
+
+void MtpProperty::writeArrayValues(MtpDataPacket& packet, MtpPropertyValue* values, int length) {
+    packet.putUInt32(length);
+    for (int i = 0; i < length; i++)
+        writeValue(packet, values[i]);
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpProperty.h b/media/mtp/MtpProperty.h
new file mode 100644
index 0000000..98b465a
--- /dev/null
+++ b/media/mtp/MtpProperty.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_PROPERTY_H
+#define _MTP_PROPERTY_H
+
+#include "MtpTypes.h"
+
+namespace android {
+
+class MtpDataPacket;
+
+struct MtpPropertyValue {
+    union {
+        int8_t          i8;
+        uint8_t         u8;
+        int16_t         i16;
+        uint16_t        u16;
+        int32_t         i32;
+        uint32_t        u32;
+        int64_t         i64;
+        uint64_t        u64;
+        int128_t        i128;
+        uint128_t       u128;
+    } u;
+    // string in UTF8 format
+    char*               str;
+};
+
+class MtpProperty {
+public:
+    MtpPropertyCode     mCode;
+    MtpDataType         mType;
+    bool                mWriteable;
+    MtpPropertyValue    mDefaultValue;
+    MtpPropertyValue    mCurrentValue;
+
+    // for array types
+    int                 mDefaultArrayLength;
+    MtpPropertyValue*   mDefaultArrayValues;
+    int                 mCurrentArrayLength;
+    MtpPropertyValue*   mCurrentArrayValues;
+
+    enum {
+        kFormNone = 0,
+        kFormRange = 1,
+        kFormEnum = 2,
+    };
+
+    uint32_t            mGroupCode;
+    uint8_t             mFormFlag;
+
+    // for range form
+    MtpPropertyValue    mMinimumValue;
+    MtpPropertyValue    mMaximumValue;
+    MtpPropertyValue    mStepSize;
+
+    // for enum form
+    int                 mEnumLength;
+    MtpPropertyValue*   mEnumValues;
+
+public:
+                        MtpProperty();
+                        MtpProperty(MtpPropertyCode propCode,
+                                     MtpDataType type,
+                                     bool writeable = false,
+                                     int defaultValue = 0);
+    virtual             ~MtpProperty();
+
+    inline MtpPropertyCode getPropertyCode() const { return mCode; }
+
+    void                read(MtpDataPacket& packet);
+    void                write(MtpDataPacket& packet);
+
+    void                setDefaultValue(const uint16_t* string);
+    void                setCurrentValue(const uint16_t* string);
+
+    void                print();
+
+    inline bool         isDeviceProperty() const {
+                            return (   ((mCode & 0xF000) == 0x5000)
+                                    || ((mCode & 0xF800) == 0xD000));
+                        }
+
+private:
+    void                readValue(MtpDataPacket& packet, MtpPropertyValue& value);
+    void                writeValue(MtpDataPacket& packet, MtpPropertyValue& value);
+    MtpPropertyValue*   readArrayValues(MtpDataPacket& packet, int& length);
+    void                writeArrayValues(MtpDataPacket& packet,
+                                            MtpPropertyValue* values, int length);
+};
+
+}; // namespace android
+
+#endif // _MTP_PROPERTY_H
diff --git a/media/mtp/MtpRequestPacket.cpp b/media/mtp/MtpRequestPacket.cpp
new file mode 100644
index 0000000..8ece580
--- /dev/null
+++ b/media/mtp/MtpRequestPacket.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpRequestPacket"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "MtpRequestPacket.h"
+
+namespace android {
+
+MtpRequestPacket::MtpRequestPacket()
+    :   MtpPacket(512)
+{
+}
+
+MtpRequestPacket::~MtpRequestPacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpRequestPacket::read(int fd) {
+    int ret = ::read(fd, mBuffer, mBufferSize);
+    if (ret >= 0)
+        mPacketSize = ret;
+    else
+        mPacketSize = 0;
+    return ret;
+}
+#endif
+
+#ifdef MTP_HOST
+    // write our buffer to the given endpoint (host mode)
+int MtpRequestPacket::write(struct usb_endpoint *ep)
+{
+    putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+    putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_COMMAND);
+    return transfer(ep, mBuffer, mPacketSize);
+}
+#endif
+
+}  // namespace android
diff --git a/media/mtp/MtpRequestPacket.h b/media/mtp/MtpRequestPacket.h
new file mode 100644
index 0000000..df518f2
--- /dev/null
+++ b/media/mtp/MtpRequestPacket.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_REQUEST_PACKET_H
+#define _MTP_REQUEST_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+namespace android {
+
+class MtpRequestPacket : public MtpPacket {
+
+public:
+                        MtpRequestPacket();
+    virtual             ~MtpRequestPacket();
+
+#ifdef MTP_DEVICE
+    // fill our buffer with data from the given file descriptor
+    int                 read(int fd);
+#endif
+
+#ifdef MTP_HOST
+    // write our buffer to the given endpoint
+    int                 write(struct usb_endpoint *ep);
+#endif
+
+    inline MtpOperationCode    getOperationCode() const { return getContainerCode(); }
+    inline void                setOperationCode(MtpOperationCode code)
+                                                    { return setContainerCode(code); }
+};
+
+}; // namespace android
+
+#endif // _MTP_REQUEST_PACKET_H
diff --git a/media/mtp/MtpResponsePacket.cpp b/media/mtp/MtpResponsePacket.cpp
new file mode 100644
index 0000000..3ef714e
--- /dev/null
+++ b/media/mtp/MtpResponsePacket.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpResponsePacket"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "MtpResponsePacket.h"
+
+namespace android {
+
+MtpResponsePacket::MtpResponsePacket()
+    :   MtpPacket(512)
+{
+}
+
+MtpResponsePacket::~MtpResponsePacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpResponsePacket::write(int fd) {
+    putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+    putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_RESPONSE);
+    int ret = ::write(fd, mBuffer, mPacketSize);
+    return (ret < 0 ? ret : 0);
+}
+#endif
+
+#ifdef MTP_HOST
+    // read our buffer from the given endpoint
+int MtpResponsePacket::read(struct usb_endpoint *ep) {
+    int ret = transfer(ep, mBuffer, mBufferSize);
+     if (ret >= 0)
+        mPacketSize = ret;
+    else
+        mPacketSize = 0;
+    return ret;
+}
+#endif
+
+}  // namespace android
+
diff --git a/media/mtp/MtpResponsePacket.h b/media/mtp/MtpResponsePacket.h
new file mode 100644
index 0000000..373f8f9
--- /dev/null
+++ b/media/mtp/MtpResponsePacket.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_RESPONSE_PACKET_H
+#define _MTP_RESPONSE_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+namespace android {
+
+class MtpResponsePacket : public MtpPacket {
+
+public:
+                        MtpResponsePacket();
+    virtual             ~MtpResponsePacket();
+
+#ifdef MTP_DEVICE
+    // write our data to the given file descriptor
+    int                 write(int fd);
+#endif
+
+#ifdef MTP_HOST
+    // read our buffer from the given endpoint
+    int                 read(struct usb_endpoint *ep);
+#endif
+
+    inline MtpResponseCode      getResponseCode() const { return getContainerCode(); }
+    inline void                 setResponseCode(MtpResponseCode code)
+                                                     { return setContainerCode(code); }
+};
+
+}; // namespace android
+
+#endif // _MTP_RESPONSE_PACKET_H
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
new file mode 100644
index 0000000..f74f395
--- /dev/null
+++ b/media/mtp/MtpServer.cpp
@@ -0,0 +1,797 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <cutils/properties.h>
+
+#define LOG_TAG "MtpServer"
+
+#include "MtpDebug.h"
+#include "MtpDatabase.h"
+#include "MtpProperty.h"
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "MtpStringBuffer.h"
+
+#include <linux/usb/f_mtp.h>
+
+namespace android {
+
+static const MtpOperationCode kSupportedOperationCodes[] = {
+    MTP_OPERATION_GET_DEVICE_INFO,
+    MTP_OPERATION_OPEN_SESSION,
+    MTP_OPERATION_CLOSE_SESSION,
+    MTP_OPERATION_GET_STORAGE_IDS,
+    MTP_OPERATION_GET_STORAGE_INFO,
+    MTP_OPERATION_GET_NUM_OBJECTS,
+    MTP_OPERATION_GET_OBJECT_HANDLES,
+    MTP_OPERATION_GET_OBJECT_INFO,
+    MTP_OPERATION_GET_OBJECT,
+//    MTP_OPERATION_GET_THUMB,
+    MTP_OPERATION_DELETE_OBJECT,
+    MTP_OPERATION_SEND_OBJECT_INFO,
+    MTP_OPERATION_SEND_OBJECT,
+//    MTP_OPERATION_INITIATE_CAPTURE,
+//    MTP_OPERATION_FORMAT_STORE,
+//    MTP_OPERATION_RESET_DEVICE,
+//    MTP_OPERATION_SELF_TEST,
+//    MTP_OPERATION_SET_OBJECT_PROTECTION,
+//    MTP_OPERATION_POWER_DOWN,
+    MTP_OPERATION_GET_DEVICE_PROP_DESC,
+    MTP_OPERATION_GET_DEVICE_PROP_VALUE,
+    MTP_OPERATION_SET_DEVICE_PROP_VALUE,
+    MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
+//    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
+//    MTP_OPERATION_MOVE_OBJECT,
+//    MTP_OPERATION_COPY_OBJECT,
+//    MTP_OPERATION_GET_PARTIAL_OBJECT,
+//    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
+    MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
+    MTP_OPERATION_GET_OBJECT_PROP_DESC,
+    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
+    MTP_OPERATION_SET_OBJECT_PROP_VALUE,
+    MTP_OPERATION_GET_OBJECT_REFERENCES,
+    MTP_OPERATION_SET_OBJECT_REFERENCES,
+//    MTP_OPERATION_SKIP,
+};
+
+static const MtpEventCode kSupportedEventCodes[] = {
+    MTP_EVENT_OBJECT_ADDED,
+    MTP_EVENT_OBJECT_REMOVED,
+};
+
+MtpServer::MtpServer(int fd, MtpDatabase* database,
+                    int fileGroup, int filePerm, int directoryPerm)
+    :   mFD(fd),
+        mDatabase(database),
+        mFileGroup(fileGroup),
+        mFilePermission(filePerm),
+        mDirectoryPermission(directoryPerm),
+        mSessionID(0),
+        mSessionOpen(false),
+        mSendObjectHandle(kInvalidObjectHandle),
+        mSendObjectFormat(0),
+        mSendObjectFileSize(0)
+{
+}
+
+MtpServer::~MtpServer() {
+}
+
+void MtpServer::addStorage(const char* filePath) {
+    int index = mStorages.size() + 1;
+    index |= index << 16;   // set high and low part to our index
+    MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
+    addStorage(storage);
+}
+
+MtpStorage* MtpServer::getStorage(MtpStorageID id) {
+    for (int i = 0; i < mStorages.size(); i++) {
+        MtpStorage* storage =  mStorages[i];
+        if (storage->getStorageID() == id)
+            return storage;
+    }
+    return NULL;
+}
+
+void MtpServer::run() {
+    int fd = mFD;
+
+    LOGV("MtpServer::run fd: %d\n", fd);
+
+    while (1) {
+        int ret = mRequest.read(fd);
+        if (ret < 0) {
+            LOGE("request read returned %d, errno: %d", ret, errno);
+            if (errno == ECANCELED) {
+                // return to top of loop and wait for next command
+                continue;
+            }
+            break;
+        }
+        MtpOperationCode operation = mRequest.getOperationCode();
+        MtpTransactionID transaction = mRequest.getTransactionID();
+
+        LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
+        mRequest.dump();
+
+        // FIXME need to generalize this
+        bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
+                    || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
+                    || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
+                    || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
+        if (dataIn) {
+            int ret = mData.read(fd);
+            if (ret < 0) {
+                LOGE("data read returned %d, errno: %d", ret, errno);
+                if (errno == ECANCELED) {
+                    // return to top of loop and wait for next command
+                    continue;
+                }
+                break;
+            }
+            LOGV("received data:");
+            mData.dump();
+        } else {
+            mData.reset();
+        }
+
+        if (handleRequest()) {
+            if (!dataIn && mData.hasData()) {
+                mData.setOperationCode(operation);
+                mData.setTransactionID(transaction);
+                LOGV("sending data:");
+                mData.dump();
+                ret = mData.write(fd);
+                if (ret < 0) {
+                    LOGE("request write returned %d, errno: %d", ret, errno);
+                    if (errno == ECANCELED) {
+                        // return to top of loop and wait for next command
+                        continue;
+                    }
+                    break;
+                }
+            }
+
+            mResponse.setTransactionID(transaction);
+            LOGV("sending response %04X", mResponse.getResponseCode());
+            ret = mResponse.write(fd);
+            mResponse.dump();
+            if (ret < 0) {
+                LOGE("request write returned %d, errno: %d", ret, errno);
+                if (errno == ECANCELED) {
+                    // return to top of loop and wait for next command
+                    continue;
+                }
+                break;
+            }
+        } else {
+            LOGV("skipping response\n");
+        }
+    }
+
+    if (mSessionOpen)
+        mDatabase->sessionEnded();
+}
+
+void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
+    if (mSessionOpen) {
+        LOGD("sendObjectAdded %d\n", handle);
+        mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
+        mEvent.setTransactionID(mRequest.getTransactionID());
+        mEvent.setParameter(1, handle);
+        int ret = mEvent.write(mFD);
+        LOGD("mEvent.write returned %d\n", ret);
+    }
+}
+
+void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
+    if (mSessionOpen) {
+        LOGD("sendObjectRemoved %d\n", handle);
+        mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
+        mEvent.setTransactionID(mRequest.getTransactionID());
+        mEvent.setParameter(1, handle);
+        int ret = mEvent.write(mFD);
+        LOGD("mEvent.write returned %d\n", ret);
+    }
+}
+
+bool MtpServer::handleRequest() {
+    MtpOperationCode operation = mRequest.getOperationCode();
+    MtpResponseCode response;
+
+    mResponse.reset();
+
+    if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
+        // FIXME - need to delete mSendObjectHandle from the database
+        LOGE("expected SendObject after SendObjectInfo");
+        mSendObjectHandle = kInvalidObjectHandle;
+    }
+
+    switch (operation) {
+        case MTP_OPERATION_GET_DEVICE_INFO:
+            response = doGetDeviceInfo();
+            break;
+        case MTP_OPERATION_OPEN_SESSION:
+            response = doOpenSession();
+            break;
+        case MTP_OPERATION_CLOSE_SESSION:
+            response = doCloseSession();
+            break;
+        case MTP_OPERATION_GET_STORAGE_IDS:
+            response = doGetStorageIDs();
+            break;
+         case MTP_OPERATION_GET_STORAGE_INFO:
+            response = doGetStorageInfo();
+            break;
+        case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
+            response = doGetObjectPropsSupported();
+            break;
+        case MTP_OPERATION_GET_OBJECT_HANDLES:
+            response = doGetObjectHandles();
+            break;
+        case MTP_OPERATION_GET_NUM_OBJECTS:
+            response = doGetNumObjects();
+            break;
+        case MTP_OPERATION_GET_OBJECT_REFERENCES:
+            response = doGetObjectReferences();
+            break;
+        case MTP_OPERATION_SET_OBJECT_REFERENCES:
+            response = doSetObjectReferences();
+            break;
+        case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
+            response = doGetObjectPropValue();
+            break;
+        case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
+            response = doSetObjectPropValue();
+            break;
+        case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
+            response = doGetDevicePropValue();
+            break;
+        case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
+            response = doSetDevicePropValue();
+            break;
+        case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
+            response = doResetDevicePropValue();
+            break;
+        case MTP_OPERATION_GET_OBJECT_INFO:
+            response = doGetObjectInfo();
+            break;
+        case MTP_OPERATION_GET_OBJECT:
+            response = doGetObject();
+            break;
+        case MTP_OPERATION_SEND_OBJECT_INFO:
+            response = doSendObjectInfo();
+            break;
+        case MTP_OPERATION_SEND_OBJECT:
+            response = doSendObject();
+            break;
+        case MTP_OPERATION_DELETE_OBJECT:
+            response = doDeleteObject();
+            break;
+        case MTP_OPERATION_GET_OBJECT_PROP_DESC:
+            response = doGetObjectPropDesc();
+            break;
+        case MTP_OPERATION_GET_DEVICE_PROP_DESC:
+            response = doGetDevicePropDesc();
+            break;
+        default:
+            LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
+            response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
+            break;
+    }
+
+    if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
+        return false;
+    mResponse.setResponseCode(response);
+    return true;
+}
+
+MtpResponseCode MtpServer::doGetDeviceInfo() {
+    MtpStringBuffer   string;
+    char prop_value[PROPERTY_VALUE_MAX];
+
+    MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
+    MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
+    MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
+
+    // fill in device info
+    mData.putUInt16(MTP_STANDARD_VERSION);
+    mData.putUInt32(6); // MTP Vendor Extension ID
+    mData.putUInt16(MTP_STANDARD_VERSION);
+    string.set("microsoft.com: 1.0;");
+    mData.putString(string); // MTP Extensions
+    mData.putUInt16(0); //Functional Mode
+    mData.putAUInt16(kSupportedOperationCodes,
+            sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
+    mData.putAUInt16(kSupportedEventCodes,
+            sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
+    mData.putAUInt16(deviceProperties); // Device Properties Supported
+    mData.putAUInt16(captureFormats); // Capture Formats
+    mData.putAUInt16(playbackFormats);  // Playback Formats
+    // FIXME
+    string.set("Google, Inc.");
+    mData.putString(string);   // Manufacturer
+
+    property_get("ro.product.model", prop_value, "MTP Device");
+    string.set(prop_value);
+    mData.putString(string);   // Model
+    string.set("1.0");
+    mData.putString(string);   // Device Version
+
+    property_get("ro.serialno", prop_value, "????????");
+    string.set(prop_value);
+    mData.putString(string);   // Serial Number
+
+    delete playbackFormats;
+    delete captureFormats;
+    delete deviceProperties;
+
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doOpenSession() {
+    if (mSessionOpen) {
+        mResponse.setParameter(1, mSessionID);
+        return MTP_RESPONSE_SESSION_ALREADY_OPEN;
+    }
+    mSessionID = mRequest.getParameter(1);
+    mSessionOpen = true;
+
+    mDatabase->sessionStarted();
+
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doCloseSession() {
+    if (!mSessionOpen)
+        return MTP_RESPONSE_SESSION_NOT_OPEN;
+    mSessionID = 0;
+    mSessionOpen = false;
+    mDatabase->sessionEnded();
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetStorageIDs() {
+    if (!mSessionOpen)
+        return MTP_RESPONSE_SESSION_NOT_OPEN;
+
+    int count = mStorages.size();
+    mData.putUInt32(count);
+    for (int i = 0; i < count; i++)
+        mData.putUInt32(mStorages[i]->getStorageID());
+
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetStorageInfo() {
+    MtpStringBuffer   string;
+
+    if (!mSessionOpen)
+        return MTP_RESPONSE_SESSION_NOT_OPEN;
+    MtpStorageID id = mRequest.getParameter(1);
+    MtpStorage* storage = getStorage(id);
+    if (!storage)
+        return MTP_RESPONSE_INVALID_STORAGE_ID;
+
+    mData.putUInt16(storage->getType());
+    mData.putUInt16(storage->getFileSystemType());
+    mData.putUInt16(storage->getAccessCapability());
+    mData.putUInt64(storage->getMaxCapacity());
+    mData.putUInt64(storage->getFreeSpace());
+    mData.putUInt32(1024*1024*1024); // Free Space in Objects
+    string.set(storage->getDescription());
+    mData.putString(string);
+    mData.putEmptyString();   // Volume Identifier
+
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetObjectPropsSupported() {
+    if (!mSessionOpen)
+        return MTP_RESPONSE_SESSION_NOT_OPEN;
+    MtpObjectFormat format = mRequest.getParameter(1);
+    MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format);
+    mData.putAUInt16(properties);
+    delete properties;
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetObjectHandles() {
+    if (!mSessionOpen)
+        return MTP_RESPONSE_SESSION_NOT_OPEN;
+    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
+    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
+    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
+                                                            // 0x00000000 for all objects?
+    if (parent == 0xFFFFFFFF)
+        parent = 0;
+
+    MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
+    mData.putAUInt32(handles);
+    delete handles;
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetNumObjects() {
+    if (!mSessionOpen)
+        return MTP_RESPONSE_SESSION_NOT_OPEN;
+    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
+    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
+    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
+                                                            // 0x00000000 for all objects?
+    if (parent == 0xFFFFFFFF)
+        parent = 0;
+
+    int count = mDatabase->getNumObjects(storageID, format, parent);
+    if (count >= 0) {
+        mResponse.setParameter(1, count);
+        return MTP_RESPONSE_OK;
+    } else {
+        mResponse.setParameter(1, 0);
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+    }
+}
+
+MtpResponseCode MtpServer::doGetObjectReferences() {
+    if (!mSessionOpen)
+        return MTP_RESPONSE_SESSION_NOT_OPEN;
+    MtpStorageID handle = mRequest.getParameter(1);
+
+    // FIXME - check for invalid object handle
+    MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
+    if (handles) {
+        mData.putAUInt32(handles);
+        delete handles;
+    } else {
+        mData.putEmptyArray();
+    }
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doSetObjectReferences() {
+    if (!mSessionOpen)
+        return MTP_RESPONSE_SESSION_NOT_OPEN;
+    MtpStorageID handle = mRequest.getParameter(1);
+    MtpObjectHandleList* references = mData.getAUInt32();
+    MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
+    delete references;
+    return result;
+}
+
+MtpResponseCode MtpServer::doGetObjectPropValue() {
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    MtpObjectProperty property = mRequest.getParameter(2);
+    LOGD("GetObjectPropValue %d %s\n", handle,
+            MtpDebug::getObjectPropCodeName(property));
+
+    return mDatabase->getObjectPropertyValue(handle, property, mData);
+}
+
+MtpResponseCode MtpServer::doSetObjectPropValue() {
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    MtpObjectProperty property = mRequest.getParameter(2);
+    LOGD("SetObjectPropValue %d %s\n", handle,
+            MtpDebug::getObjectPropCodeName(property));
+
+    return mDatabase->setObjectPropertyValue(handle, property, mData);
+}
+
+MtpResponseCode MtpServer::doGetDevicePropValue() {
+    MtpDeviceProperty property = mRequest.getParameter(1);
+    LOGD("GetDevicePropValue %s\n",
+            MtpDebug::getDevicePropCodeName(property));
+
+    return mDatabase->getDevicePropertyValue(property, mData);
+}
+
+MtpResponseCode MtpServer::doSetDevicePropValue() {
+    MtpDeviceProperty property = mRequest.getParameter(1);
+    LOGD("SetDevicePropValue %s\n",
+            MtpDebug::getDevicePropCodeName(property));
+
+    return mDatabase->setDevicePropertyValue(property, mData);
+}
+
+MtpResponseCode MtpServer::doResetDevicePropValue() {
+    MtpDeviceProperty property = mRequest.getParameter(1);
+    LOGD("ResetDevicePropValue %s\n",
+            MtpDebug::getDevicePropCodeName(property));
+
+    return mDatabase->resetDeviceProperty(property);
+}
+
+MtpResponseCode MtpServer::doGetObjectInfo() {
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    return mDatabase->getObjectInfo(handle, mData);
+}
+
+MtpResponseCode MtpServer::doGetObject() {
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    MtpString pathBuf;
+    int64_t fileLength;
+    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
+    if (result != MTP_RESPONSE_OK)
+        return result;
+
+    const char* filePath = (const char *)pathBuf;
+    mtp_file_range  mfr;
+    mfr.fd = open(filePath, O_RDONLY);
+    if (mfr.fd < 0) {
+        return MTP_RESPONSE_GENERAL_ERROR;
+    }
+    mfr.offset = 0;
+    mfr.length = fileLength;
+
+    // send data header
+    mData.setOperationCode(mRequest.getOperationCode());
+    mData.setTransactionID(mRequest.getTransactionID());
+    mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE);
+
+    // then transfer the file
+    int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
+    close(mfr.fd);
+    if (ret < 0) {
+        if (errno == ECANCELED)
+            return MTP_RESPONSE_TRANSACTION_CANCELLED;
+        else
+            return MTP_RESPONSE_GENERAL_ERROR;
+    }
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doSendObjectInfo() {
+    MtpString path;
+    MtpStorageID storageID = mRequest.getParameter(1);
+    MtpStorage* storage = getStorage(storageID);
+    MtpObjectHandle parent = mRequest.getParameter(2);
+    if (!storage)
+        return MTP_RESPONSE_INVALID_STORAGE_ID;
+
+    // special case the root
+    if (parent == MTP_PARENT_ROOT) {
+        path = storage->getPath();
+        parent = 0;
+    } else {
+        int64_t dummy;
+        int result = mDatabase->getObjectFilePath(parent, path, dummy);
+        if (result != MTP_RESPONSE_OK)
+            return result;
+    }
+
+    // read only the fields we need
+    mData.getUInt32();  // storage ID
+    MtpObjectFormat format = mData.getUInt16();
+    mData.getUInt16();  // protection status
+    mSendObjectFileSize = mData.getUInt32();
+    mData.getUInt16();  // thumb format
+    mData.getUInt32();  // thumb compressed size
+    mData.getUInt32();  // thumb pix width
+    mData.getUInt32();  // thumb pix height
+    mData.getUInt32();  // image pix width
+    mData.getUInt32();  // image pix height
+    mData.getUInt32();  // image bit depth
+    mData.getUInt32();  // parent
+    uint16_t associationType = mData.getUInt16();
+    uint32_t associationDesc = mData.getUInt32();   // association desc
+    mData.getUInt32();  // sequence number
+    MtpStringBuffer name, created, modified;
+    mData.getString(name);    // file name
+    mData.getString(created);      // date created
+    mData.getString(modified);     // date modified
+    // keywords follow
+
+    time_t modifiedTime;
+    if (!parseDateTime(modified, modifiedTime))
+        modifiedTime = 0;
+
+    if (path[path.size() - 1] != '/')
+        path += "/";
+    path += (const char *)name;
+
+    MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
+            format, parent, storageID, mSendObjectFileSize, modifiedTime);
+    if (handle == kInvalidObjectHandle) {
+        return MTP_RESPONSE_GENERAL_ERROR;
+    }
+
+  if (format == MTP_FORMAT_ASSOCIATION) {
+        mode_t mask = umask(0);
+        int ret = mkdir((const char *)path, mDirectoryPermission);
+        umask(mask);
+        if (ret && ret != -EEXIST)
+            return MTP_RESPONSE_GENERAL_ERROR;
+        chown((const char *)path, getuid(), mFileGroup);
+    } else {
+        mSendObjectFilePath = path;
+        // save the handle for the SendObject call, which should follow
+        mSendObjectHandle = handle;
+        mSendObjectFormat = format;
+    }
+
+    mResponse.setParameter(1, storageID);
+    mResponse.setParameter(2, parent);
+    mResponse.setParameter(3, handle);
+
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doSendObject() {
+    MtpResponseCode result = MTP_RESPONSE_OK;
+    mode_t mask;
+    int ret;
+
+    if (mSendObjectHandle == kInvalidObjectHandle) {
+        LOGE("Expected SendObjectInfo before SendObject");
+        result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
+        goto done;
+    }
+
+    // read the header
+    ret = mData.readDataHeader(mFD);
+    // FIXME - check for errors here.
+
+    // reset so we don't attempt to send this back
+    mData.reset();
+
+    mtp_file_range  mfr;
+    mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
+    if (mfr.fd < 0) {
+        result = MTP_RESPONSE_GENERAL_ERROR;
+        goto done;
+    }
+    fchown(mfr.fd, getuid(), mFileGroup);
+    // set permissions
+    mask = umask(0);
+    fchmod(mfr.fd, mFilePermission);
+    umask(mask);
+
+    mfr.offset = 0;
+    mfr.length = mSendObjectFileSize;
+
+    // transfer the file
+    ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
+    close(mfr.fd);
+
+    LOGV("MTP_RECEIVE_FILE returned %d", ret);
+
+    if (ret < 0) {
+        unlink(mSendObjectFilePath);
+        if (errno == ECANCELED)
+            result = MTP_RESPONSE_TRANSACTION_CANCELLED;
+        else
+            result = MTP_RESPONSE_GENERAL_ERROR;
+    }
+
+done:
+    mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
+            result == MTP_RESPONSE_OK);
+    mSendObjectHandle = kInvalidObjectHandle;
+    mSendObjectFormat = 0;
+    return result;
+}
+
+static void deleteRecursive(const char* path) {
+    char pathbuf[PATH_MAX];
+    int pathLength = strlen(path);
+    if (pathLength >= sizeof(pathbuf) - 1) {
+        LOGE("path too long: %s\n", path);
+    }
+    strcpy(pathbuf, path);
+    if (pathbuf[pathLength - 1] != '/') {
+        pathbuf[pathLength++] = '/';
+    }
+    char* fileSpot = pathbuf + pathLength;
+    int pathRemaining = sizeof(pathbuf) - pathLength - 1;
+
+    DIR* dir = opendir(path);
+    if (!dir) {
+        LOGE("opendir %s failed: %s", path, strerror(errno));
+        return;
+    }
+
+    struct dirent* entry;
+    while ((entry = readdir(dir))) {
+        const char* name = entry->d_name;
+
+        // ignore "." and ".."
+        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
+            continue;
+        }
+
+        int nameLength = strlen(name);
+        if (nameLength > pathRemaining) {
+            LOGE("path %s/%s too long\n", path, name);
+            continue;
+        }
+        strcpy(fileSpot, name);
+
+        int type = entry->d_type;
+        if (entry->d_type == DT_DIR) {
+            deleteRecursive(pathbuf);
+            rmdir(pathbuf);
+        } else {
+            unlink(pathbuf);
+        }
+    }
+}
+
+static void deletePath(const char* path) {
+    struct stat statbuf;
+    if (stat(path, &statbuf) == 0) {
+        if (S_ISDIR(statbuf.st_mode)) {
+            deleteRecursive(path);
+            rmdir(path);
+        } else {
+            unlink(path);
+        }
+    } else {
+        LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
+    }
+}
+
+MtpResponseCode MtpServer::doDeleteObject() {
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    MtpObjectFormat format = mRequest.getParameter(2);
+    // FIXME - support deleting all objects if handle is 0xFFFFFFFF
+    // FIXME - implement deleting objects by format
+
+    MtpString filePath;
+    int64_t fileLength;
+    int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
+    if (result == MTP_RESPONSE_OK) {
+        LOGV("deleting %s", (const char *)filePath);
+        deletePath((const char *)filePath);
+        return mDatabase->deleteFile(handle);
+    } else {
+        return result;
+    }
+}
+
+MtpResponseCode MtpServer::doGetObjectPropDesc() {
+    MtpObjectProperty propCode = mRequest.getParameter(1);
+    MtpObjectFormat format = mRequest.getParameter(2);
+    LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
+                                        MtpDebug::getFormatCodeName(format));
+    MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
+    if (!property)
+        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+    property->write(mData);
+    delete property;
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetDevicePropDesc() {
+    MtpDeviceProperty propCode = mRequest.getParameter(1);
+    LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
+    MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
+    if (!property)
+        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+    property->write(mData);
+    delete property;
+    return MTP_RESPONSE_OK;
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
new file mode 100644
index 0000000..68a6564
--- /dev/null
+++ b/media/mtp/MtpServer.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_SERVER_H
+#define _MTP_SERVER_H
+
+#include "MtpRequestPacket.h"
+#include "MtpDataPacket.h"
+#include "MtpResponsePacket.h"
+#include "MtpEventPacket.h"
+#include "mtp.h"
+
+#include "MtpUtils.h"
+
+namespace android {
+
+class MtpDatabase;
+class MtpStorage;
+
+class MtpServer {
+
+private:
+    // file descriptor for MTP kernel driver
+    int                 mFD;
+
+    MtpDatabase*        mDatabase;
+
+    // group to own new files and folders
+    int                 mFileGroup;
+    // permissions for new files and directories
+    int                 mFilePermission;
+    int                 mDirectoryPermission;
+
+    // current session ID
+    MtpSessionID        mSessionID;
+    // true if we have an open session and mSessionID is valid
+    bool                mSessionOpen;
+
+    MtpRequestPacket    mRequest;
+    MtpDataPacket       mData;
+    MtpResponsePacket   mResponse;
+    MtpEventPacket      mEvent;
+
+    MtpStorageList      mStorages;
+
+    // handle for new object, set by SendObjectInfo and used by SendObject
+    MtpObjectHandle     mSendObjectHandle;
+    MtpObjectFormat     mSendObjectFormat;
+    MtpString           mSendObjectFilePath;
+    size_t              mSendObjectFileSize;
+
+public:
+                        MtpServer(int fd, MtpDatabase* database,
+                                    int fileGroup, int filePerm, int directoryPerm);
+    virtual             ~MtpServer();
+
+    void                addStorage(const char* filePath);
+    inline void         addStorage(MtpStorage* storage) { mStorages.push(storage); }
+    MtpStorage*         getStorage(MtpStorageID id);
+    void                run();
+
+    void                sendObjectAdded(MtpObjectHandle handle);
+    void                sendObjectRemoved(MtpObjectHandle handle);
+
+private:
+    bool                handleRequest();
+
+    MtpResponseCode     doGetDeviceInfo();
+    MtpResponseCode     doOpenSession();
+    MtpResponseCode     doCloseSession();
+    MtpResponseCode     doGetStorageIDs();
+    MtpResponseCode     doGetStorageInfo();
+    MtpResponseCode     doGetObjectPropsSupported();
+    MtpResponseCode     doGetObjectHandles();
+    MtpResponseCode     doGetNumObjects();
+    MtpResponseCode     doGetObjectReferences();
+    MtpResponseCode     doSetObjectReferences();
+    MtpResponseCode     doGetObjectPropValue();
+    MtpResponseCode     doSetObjectPropValue();
+    MtpResponseCode     doGetDevicePropValue();
+    MtpResponseCode     doSetDevicePropValue();
+    MtpResponseCode     doResetDevicePropValue();
+    MtpResponseCode     doGetObjectInfo();
+    MtpResponseCode     doGetObject();
+    MtpResponseCode     doSendObjectInfo();
+    MtpResponseCode     doSendObject();
+    MtpResponseCode     doDeleteObject();
+    MtpResponseCode     doGetObjectPropDesc();
+    MtpResponseCode     doGetDevicePropDesc();
+};
+
+}; // namespace android
+
+#endif // _MTP_SERVER_H
diff --git a/media/mtp/MtpStorage.cpp b/media/mtp/MtpStorage.cpp
new file mode 100644
index 0000000..eccf186
--- /dev/null
+++ b/media/mtp/MtpStorage.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpStorage"
+
+#include "MtpDebug.h"
+#include "MtpDatabase.h"
+#include "MtpStorage.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+
+namespace android {
+
+MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, MtpDatabase* db)
+    :   mStorageID(id),
+        mFilePath(filePath),
+        mDatabase(db),
+        mMaxCapacity(0)
+{
+    LOGD("MtpStorage id: %d path: %s\n", id, filePath);
+}
+
+MtpStorage::~MtpStorage() {
+}
+
+int MtpStorage::getType() const {
+    return MTP_STORAGE_FIXED_RAM;
+}
+
+int MtpStorage::getFileSystemType() const {
+    return MTP_STORAGE_FILESYSTEM_HIERARCHICAL;
+}
+
+int MtpStorage::getAccessCapability() const {
+    return MTP_STORAGE_READ_WRITE;
+}
+
+uint64_t MtpStorage::getMaxCapacity() {
+    if (mMaxCapacity == 0) {
+        struct statfs   stat;
+        if (statfs(mFilePath, &stat))
+            return -1;
+        mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize;
+    }
+    return mMaxCapacity;
+}
+
+uint64_t MtpStorage::getFreeSpace() {
+    struct statfs   stat;
+    if (statfs(mFilePath, &stat))
+        return -1;
+    return (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize;
+}
+
+const char* MtpStorage::getDescription() const {
+    return "Device Storage";
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpStorage.h b/media/mtp/MtpStorage.h
new file mode 100644
index 0000000..b13b926
--- /dev/null
+++ b/media/mtp/MtpStorage.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_STORAGE_H
+#define _MTP_STORAGE_H
+
+#include "mtp.h"
+
+namespace android {
+
+class MtpDatabase;
+
+class MtpStorage {
+
+private:
+    MtpStorageID            mStorageID;
+    const char*             mFilePath;
+    MtpDatabase*            mDatabase;
+    uint64_t                mMaxCapacity;
+
+public:
+                            MtpStorage(MtpStorageID id, const char* filePath, MtpDatabase* db);
+    virtual                 ~MtpStorage();
+
+    inline MtpStorageID     getStorageID() const { return mStorageID; }
+    int                     getType() const;
+    int                     getFileSystemType() const;
+    int                     getAccessCapability() const;
+    uint64_t                getMaxCapacity();
+    uint64_t                getFreeSpace();
+    const char*             getDescription() const;
+    inline const char*      getPath() const { return mFilePath; }
+};
+
+}; // namespace android
+
+#endif // _MTP_STORAGE_H
diff --git a/media/mtp/MtpStorageInfo.cpp b/media/mtp/MtpStorageInfo.cpp
new file mode 100644
index 0000000..ca64ac0
--- /dev/null
+++ b/media/mtp/MtpStorageInfo.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpStorageInfo"
+
+#include "MtpDebug.h"
+#include "MtpDataPacket.h"
+#include "MtpStorageInfo.h"
+#include "MtpStringBuffer.h"
+
+namespace android {
+
+MtpStorageInfo::MtpStorageInfo(MtpStorageID id)
+    :   mStorageID(id),
+        mStorageType(0),
+        mFileSystemType(0),
+        mAccessCapability(0),
+        mMaxCapacity(0),
+        mFreeSpaceBytes(0),
+        mFreeSpaceObjects(0),
+        mStorageDescription(NULL),
+        mVolumeIdentifier(NULL)
+{
+}
+
+MtpStorageInfo::~MtpStorageInfo() {
+    if (mStorageDescription)
+        free(mStorageDescription);
+    if (mVolumeIdentifier)
+        free(mVolumeIdentifier);
+}
+
+void MtpStorageInfo::read(MtpDataPacket& packet) {
+    MtpStringBuffer string;
+
+    // read the device info
+    mStorageType = packet.getUInt16();
+    mFileSystemType = packet.getUInt16();
+    mAccessCapability = packet.getUInt16();
+    mMaxCapacity = packet.getUInt64();
+    mFreeSpaceBytes = packet.getUInt64();
+    mFreeSpaceObjects = packet.getUInt32();
+
+    packet.getString(string);
+    mStorageDescription = strdup((const char *)string);
+    packet.getString(string);
+    mVolumeIdentifier = strdup((const char *)string);
+}
+
+void MtpStorageInfo::print() {
+    LOGD("Storage Info %08X:\n\tmStorageType: %d\n\tmFileSystemType: %d\n\tmAccessCapability: %d\n",
+            mStorageID, mStorageType, mFileSystemType, mAccessCapability);
+    LOGD("\tmMaxCapacity: %lld\n\tmFreeSpaceBytes: %lld\n\tmFreeSpaceObjects: %d\n",
+            mMaxCapacity, mFreeSpaceBytes, mFreeSpaceObjects);
+    LOGD("\tmStorageDescription: %s\n\tmVolumeIdentifier: %s\n",
+            mStorageDescription, mVolumeIdentifier);
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpStorageInfo.h b/media/mtp/MtpStorageInfo.h
new file mode 100644
index 0000000..2cb626e
--- /dev/null
+++ b/media/mtp/MtpStorageInfo.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_STORAGE_INFO_H
+#define _MTP_STORAGE_INFO_H
+
+#include "MtpTypes.h"
+
+namespace android {
+
+class MtpDataPacket;
+
+class MtpStorageInfo {
+public:
+    MtpStorageID        mStorageID;
+    uint16_t            mStorageType;
+    uint16_t            mFileSystemType;
+    uint16_t            mAccessCapability;
+    uint64_t            mMaxCapacity;
+    uint64_t            mFreeSpaceBytes;
+    uint32_t            mFreeSpaceObjects;
+    char*               mStorageDescription;
+    char*               mVolumeIdentifier;
+
+public:
+                        MtpStorageInfo(MtpStorageID id);
+    virtual             ~MtpStorageInfo();
+
+    void                read(MtpDataPacket& packet);
+
+    void                print();
+};
+
+}; // namespace android
+
+#endif // _MTP_STORAGE_INFO_H
diff --git a/media/mtp/MtpStringBuffer.cpp b/media/mtp/MtpStringBuffer.cpp
new file mode 100644
index 0000000..fe8cf04
--- /dev/null
+++ b/media/mtp/MtpStringBuffer.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpStringBuffer"
+
+#include <string.h>
+
+#include "MtpDataPacket.h"
+#include "MtpStringBuffer.h"
+
+namespace android {
+
+MtpStringBuffer::MtpStringBuffer()
+    :   mCharCount(0),
+        mByteCount(1)
+{
+    mBuffer[0] = 0;
+}
+
+MtpStringBuffer::MtpStringBuffer(const char* src)
+    :   mCharCount(0),
+        mByteCount(1)
+{
+    set(src);
+}
+
+MtpStringBuffer::MtpStringBuffer(const uint16_t* src)
+    :   mCharCount(0),
+        mByteCount(1)
+{
+    set(src);
+}
+
+MtpStringBuffer::MtpStringBuffer(const MtpStringBuffer& src)
+    :   mCharCount(src.mCharCount),
+        mByteCount(src.mByteCount)
+{
+    memcpy(mBuffer, src.mBuffer, mByteCount);
+}
+
+
+MtpStringBuffer::~MtpStringBuffer() {
+}
+
+void MtpStringBuffer::set(const char* src) {
+    int length = strlen(src);
+    if (length >= sizeof(mBuffer))
+        length = sizeof(mBuffer) - 1;
+    memcpy(mBuffer, src, length);
+
+    // count the characters
+    int count = 0;
+    char ch;
+    while ((ch = *src++) != 0) {
+        if ((ch & 0x80) == 0) {
+            // single byte character
+        } else if ((ch & 0xE0) == 0xC0) {
+            // two byte character
+            if (! *src++) {
+                // last character was truncated, so ignore last byte
+                length--;
+                break;
+            }
+        } else if ((ch & 0xF0) == 0xE0) {
+            // 3 byte char
+            if (! *src++) {
+                // last character was truncated, so ignore last byte
+                length--;
+                break;
+            }
+            if (! *src++) {
+                // last character was truncated, so ignore last two bytes
+                length -= 2;
+                break;
+            }
+        }
+        count++;
+    }
+
+    mByteCount = length + 1;
+    mBuffer[length] = 0;
+    mCharCount = count;
+}
+
+void MtpStringBuffer::set(const uint16_t* src) {
+    int count = 0;
+    uint16_t ch;
+    uint8_t* dest = mBuffer;
+
+    while ((ch = *src++) != 0 && count < 255) {
+        if (ch >= 0x0800) {
+            *dest++ = (uint8_t)(0xE0 | (ch >> 12));
+            *dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F));
+            *dest++ = (uint8_t)(0x80 | (ch & 0x3F));
+        } else if (ch >= 0x80) {
+            *dest++ = (uint8_t)(0xC0 | (ch >> 6));
+            *dest++ = (uint8_t)(0x80 | (ch & 0x3F));
+        } else {
+            *dest++ = ch;
+        }
+        count++;
+    }
+    *dest++ = 0;
+    mCharCount = count;
+    mByteCount = dest - mBuffer;
+}
+
+void MtpStringBuffer::readFromPacket(MtpDataPacket* packet) {
+    int count = packet->getUInt8();
+    uint8_t* dest = mBuffer;
+    for (int i = 0; i < count; i++) {
+        uint16_t ch = packet->getUInt16();
+        if (ch >= 0x0800) {
+            *dest++ = (uint8_t)(0xE0 | (ch >> 12));
+            *dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F));
+            *dest++ = (uint8_t)(0x80 | (ch & 0x3F));
+        } else if (ch >= 0x80) {
+            *dest++ = (uint8_t)(0xC0 | (ch >> 6));
+            *dest++ = (uint8_t)(0x80 | (ch & 0x3F));
+        } else {
+            *dest++ = ch;
+        }
+    }
+    *dest++ = 0;
+    mCharCount = count;
+    mByteCount = dest - mBuffer;
+}
+
+void MtpStringBuffer::writeToPacket(MtpDataPacket* packet) const {
+    int count = mCharCount;
+    const uint8_t* src = mBuffer;
+    packet->putUInt8(count > 0 ? count + 1 : 0);
+
+    // expand utf8 to 16 bit chars
+    for (int i = 0; i < count; i++) {
+        uint16_t ch;
+        uint16_t ch1 = *src++;
+        if ((ch1 & 0x80) == 0) {
+            // single byte character
+            ch = ch1;
+        } else if ((ch1 & 0xE0) == 0xC0) {
+            // two byte character
+            uint16_t ch2 = *src++;
+            ch = ((ch1 & 0x1F) << 6) | (ch2 & 0x3F);
+        } else {
+            // three byte character
+            uint16_t ch2 = *src++;
+            uint16_t ch3 = *src++;
+            ch = ((ch1 & 0x0F) << 12) | ((ch2 & 0x3F) << 6) | (ch3 & 0x3F);
+        }
+        packet->putUInt16(ch);
+    }
+    // only terminate with zero if string is not empty
+    if (count > 0)
+        packet->putUInt16(0);
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpStringBuffer.h b/media/mtp/MtpStringBuffer.h
new file mode 100644
index 0000000..cbc8307
--- /dev/null
+++ b/media/mtp/MtpStringBuffer.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_STRING_BUFFER_H
+#define _MTP_STRING_BUFFER_H
+
+#include <stdint.h>
+
+namespace android {
+
+class MtpDataPacket;
+
+// Represents a utf8 string, with a maximum of 255 characters
+class MtpStringBuffer {
+
+private:
+    // mBuffer contains string in UTF8 format
+    // maximum 3 bytes/character, with 1 extra for zero termination
+    uint8_t         mBuffer[255 * 3 + 1];
+    int             mCharCount;
+    int             mByteCount;
+
+public:
+                    MtpStringBuffer();
+                    MtpStringBuffer(const char* src);
+                    MtpStringBuffer(const uint16_t* src);
+                    MtpStringBuffer(const MtpStringBuffer& src);
+    virtual         ~MtpStringBuffer();
+
+    void            set(const char* src);
+    void            set(const uint16_t* src);
+
+    void            readFromPacket(MtpDataPacket* packet);
+    void            writeToPacket(MtpDataPacket* packet) const;
+
+    inline int      getCharCount() const { return mCharCount; }
+    inline int      getByteCount() const { return mByteCount; }
+
+	inline operator const char*() const { return (const char *)mBuffer; }
+};
+
+}; // namespace android
+
+#endif // _MTP_STRING_BUFFER_H
diff --git a/media/mtp/MtpTypes.h b/media/mtp/MtpTypes.h
new file mode 100644
index 0000000..720c854
--- /dev/null
+++ b/media/mtp/MtpTypes.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_TYPES_H
+#define _MTP_TYPES_H
+
+#include <stdint.h>
+#include "utils/String8.h"
+#include "utils/Vector.h"
+
+namespace android {
+
+typedef int32_t int128_t[4];
+typedef uint32_t uint128_t[4];
+
+typedef uint16_t MtpOperationCode;
+typedef uint16_t MtpResponseCode;
+typedef uint16_t MtpEventCode;
+typedef uint32_t MtpSessionID;
+typedef uint32_t MtpStorageID;
+typedef uint32_t MtpTransactionID;
+typedef uint16_t MtpPropertyCode;
+typedef uint16_t MtpDataType;
+typedef uint16_t MtpObjectFormat;
+typedef MtpPropertyCode MtpDeviceProperty;
+typedef MtpPropertyCode MtpObjectProperty;
+
+// object handles are unique across all storage but only within a single session.
+// object handles cannot be reused after an object is deleted.
+// values 0x00000000 and 0xFFFFFFFF are reserved for special purposes.
+typedef uint32_t MtpObjectHandle;
+
+// Special values
+#define MTP_PARENT_ROOT         0xFFFFFFFF       // parent is root of the storage
+#define kInvalidObjectHandle    0xFFFFFFFF
+
+class MtpStorage;
+class MtpDevice;
+class MtpProperty;
+
+typedef Vector<MtpStorage *> MtpStorageList;
+typedef Vector<MtpDevice*> MtpDeviceList;
+typedef Vector<MtpProperty*> MtpPropertyList;
+
+typedef Vector<uint8_t> UInt8List;
+typedef Vector<uint16_t> UInt16List;
+typedef Vector<uint32_t> UInt32List;
+typedef Vector<uint64_t> UInt64List;
+typedef Vector<int8_t> Int8List;
+typedef Vector<int16_t> Int16List;
+typedef Vector<int32_t> Int32List;
+typedef Vector<int64_t> Int64List;
+
+typedef UInt16List MtpObjectPropertyList;
+typedef UInt16List MtpDevicePropertyList;
+typedef UInt16List MtpObjectFormatList;
+typedef UInt32List MtpObjectHandleList;
+typedef UInt16List MtpObjectPropertyList;
+typedef UInt32List MtpStorageIDList;
+
+typedef String8    MtpString;
+
+}; // namespace android
+
+#endif // _MTP_TYPES_H
diff --git a/media/mtp/MtpUtils.cpp b/media/mtp/MtpUtils.cpp
new file mode 100644
index 0000000..ab01ef5
--- /dev/null
+++ b/media/mtp/MtpUtils.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MtpUtils"
+
+#include <stdio.h>
+#include <time.h>
+
+#include <cutils/tztime.h>
+#include "MtpUtils.h"
+
+namespace android {
+
+/*
+DateTime strings follow a compatible subset of the definition found in ISO 8601, and
+take the form of a Unicode string formatted as: "YYYYMMDDThhmmss.s". In this
+representation, YYYY shall be replaced by the year, MM replaced by the month (01-12),
+DD replaced by the day (01-31), T is a constant character 'T' delimiting time from date,
+hh is replaced by the hour (00-23), mm is replaced by the minute (00-59), and ss by the
+second (00-59). The ".s" is optional, and represents tenths of a second.
+*/
+
+bool parseDateTime(const char* dateTime, time_t& outSeconds) {
+    int year, month, day, hour, minute, second;
+    struct tm tm;
+
+    if (sscanf(dateTime, "%04d%02d%02dT%02d%02d%02d",
+            &year, &month, &day, &hour, &minute, &second) != 6)
+        return false;
+    const char* tail = dateTime + 15;
+    // skip optional tenth of second
+    if (tail[0] == '.' && tail[1])
+        tail += 2;
+    //FIXME - support +/-hhmm
+    bool useUTC = (tail[0] == 'Z');
+
+    // hack to compute timezone
+    time_t dummy;
+    localtime_r(&dummy, &tm);
+
+    tm.tm_sec = second;
+    tm.tm_min = minute;
+    tm.tm_hour = hour;
+    tm.tm_mday = day;
+    tm.tm_mon = month;
+    tm.tm_year = year - 1900;
+    tm.tm_wday = 0;
+    tm.tm_isdst = -1;
+    if (useUTC)
+        outSeconds = mktime(&tm);
+    else
+        outSeconds = mktime_tz(&tm, tm.tm_zone);
+
+    return true;
+}
+
+void formatDateTime(time_t seconds, char* buffer, int bufferLength) {
+    struct tm tm;
+
+    localtime_r(&seconds, &tm);
+    snprintf(buffer, bufferLength, "%04d%02d%02dT%02d%02d%02d",
+        tm.tm_year + 1900, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpUtils.h b/media/mtp/MtpUtils.h
new file mode 100644
index 0000000..61f9055
--- /dev/null
+++ b/media/mtp/MtpUtils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_UTILS_H
+#define _MTP_UTILS_H
+
+#include <stdint.h>
+
+namespace android {
+
+bool parseDateTime(const char* dateTime, time_t& outSeconds);
+void formatDateTime(time_t seconds, char* buffer, int bufferLength);
+
+}; // namespace android
+
+#endif // _MTP_UTILS_H
diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h
new file mode 100644
index 0000000..b7afa66
--- /dev/null
+++ b/media/mtp/mtp.h
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_H
+#define _MTP_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define MTP_STANDARD_VERSION            100
+
+// Container Types
+#define MTP_CONTAINER_TYPE_UNDEFINED    0
+#define MTP_CONTAINER_TYPE_COMMAND      1
+#define MTP_CONTAINER_TYPE_DATA         2
+#define MTP_CONTAINER_TYPE_RESPONSE     3
+#define MTP_CONTAINER_TYPE_EVENT        4
+
+// Container Offsets
+#define MTP_CONTAINER_LENGTH_OFFSET             0
+#define MTP_CONTAINER_TYPE_OFFSET               4
+#define MTP_CONTAINER_CODE_OFFSET               6
+#define MTP_CONTAINER_TRANSACTION_ID_OFFSET     8
+#define MTP_CONTAINER_PARAMETER_OFFSET          12
+#define MTP_CONTAINER_HEADER_SIZE               12
+
+// MTP Types
+#define MTP_TYPE_UNDEFINED      0x0000          // Undefined
+#define MTP_TYPE_INT8           0x0001          // Signed 8-bit integer
+#define MTP_TYPE_UINT8          0x0002          // Unsigned 8-bit integer
+#define MTP_TYPE_INT16          0x0003          // Signed 16-bit integer
+#define MTP_TYPE_UINT16         0x0004          // Unsigned 16-bit integer
+#define MTP_TYPE_INT32          0x0005          // Signed 32-bit integer
+#define MTP_TYPE_UINT32         0x0006          // Unsigned 32-bit integer
+#define MTP_TYPE_INT64          0x0007          // Signed 64-bit integer
+#define MTP_TYPE_UINT64         0x0008          // Unsigned 64-bit integer
+#define MTP_TYPE_INT128         0x0009          // Signed 128-bit integer
+#define MTP_TYPE_UINT128        0x000A          // Unsigned 128-bit integer
+#define MTP_TYPE_AINT8          0x4001          // Array of signed 8-bit integers
+#define MTP_TYPE_AUINT8         0x4002          // Array of unsigned 8-bit integers
+#define MTP_TYPE_AINT16         0x4003          // Array of signed 16-bit integers
+#define MTP_TYPE_AUINT16        0x4004          // Array of unsigned 16-bit integers
+#define MTP_TYPE_AINT32         0x4005          // Array of signed 32-bit integers
+#define MTP_TYPE_AUINT32        0x4006          // Array of unsigned 32-bit integers
+#define MTP_TYPE_AINT64         0x4007          // Array of signed 64-bit integers
+#define MTP_TYPE_AUINT64        0x4008          // Array of unsigned 64-bit integers
+#define MTP_TYPE_AINT128        0x4009          // Array of signed 128-bit integers
+#define MTP_TYPE_AUINT128       0x400A          // Array of unsigned 128-bit integers
+#define MTP_TYPE_STR            0xFFFF          // Variable-length Unicode string
+
+// MTP Format Codes
+#define MTP_FORMAT_UNDEFINED                            0x3000   // Undefined object
+#define MTP_FORMAT_ASSOCIATION                          0x3001   // Association (for example, a folder)
+#define MTP_FORMAT_SCRIPT                               0x3002   // Device model-specific script
+#define MTP_FORMAT_EXECUTABLE                           0x3003   // Device model-specific binary executable
+#define MTP_FORMAT_TEXT                                 0x3004   // Text file
+#define MTP_FORMAT_HTML                                 0x3005   // Hypertext Markup Language file (text)
+#define MTP_FORMAT_DPOF                                 0x3006   // Digital Print Order Format file (text)
+#define MTP_FORMAT_AIFF                                 0x3007   // Audio clip
+#define MTP_FORMAT_WAV                                  0x3008   // Audio clip
+#define MTP_FORMAT_MP3                                  0x3009   // Audio clip
+#define MTP_FORMAT_AVI                                  0x300A   // Video clip
+#define MTP_FORMAT_MPEG                                 0x300B   // Video clip
+#define MTP_FORMAT_ASF                                  0x300C   // Microsoft Advanced Streaming Format (video)
+#define MTP_FORMAT_DEFINED                              0x3800   // Unknown image object
+#define MTP_FORMAT_EXIF_JPEG                            0x3801   // Exchangeable File Format, JEIDA standard
+#define MTP_FORMAT_TIFF_EP                              0x3802   // Tag Image File Format for Electronic Photography
+#define MTP_FORMAT_FLASHPIX                             0x3803   // Structured Storage Image Format
+#define MTP_FORMAT_BMP                                  0x3804   // Microsoft Windows Bitmap file
+#define MTP_FORMAT_CIFF                                 0x3805   // Canon Camera Image File Format
+#define MTP_FORMAT_GIF                                  0x3807   // Graphics Interchange Format
+#define MTP_FORMAT_JFIF                                 0x3808   // JPEG File Interchange Format
+#define MTP_FORMAT_CD                                   0x3809   // PhotoCD Image Pac
+#define MTP_FORMAT_PICT                                 0x380A   // Quickdraw Image Format
+#define MTP_FORMAT_PNG                                  0x380B   // Portable Network Graphics
+#define MTP_FORMAT_TIFF                                 0x380D   // Tag Image File Format
+#define MTP_FORMAT_TIFF_IT                              0x380E   // Tag Image File Format for Information Technology (graphic arts)
+#define MTP_FORMAT_JP2                                  0x380F   // JPEG2000 Baseline File Format
+#define MTP_FORMAT_JPX                                  0x3810   // JPEG2000 Extended File Format
+#define MTP_FORMAT_UNDEFINED_FIRMWARE                   0xB802
+#define MTP_FORMAT_WINDOWS_IMAGE_FORMAT                 0xB881
+#define MTP_FORMAT_UNDEFINED_AUDIO                      0xB900
+#define MTP_FORMAT_WMA                                  0xB901
+#define MTP_FORMAT_OGG                                  0xB902
+#define MTP_FORMAT_AAC                                  0xB903
+#define MTP_FORMAT_AUDIBLE                              0xB904
+#define MTP_FORMAT_FLAC                                 0xB906
+#define MTP_FORMAT_UNDEFINED_VIDEO                      0xB980
+#define MTP_FORMAT_WMV                                  0xB981
+#define MTP_FORMAT_MP4_CONTAINER                        0xB982  // ISO 14496-1
+#define MTP_FORMAT_MP2                                  0xB983
+#define MTP_FORMAT_3GP_CONTAINER                        0xB984  // 3GPP file format. Details: http://www.3gpp.org/ftp/Specs/html-info/26244.htm (page title - \u201cTransparent end-to-end packet switched streaming service, 3GPP file format\u201d).
+#define MTP_FORMAT_UNDEFINED_COLLECTION                 0xBA00
+#define MTP_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM            0xBA01
+#define MTP_FORMAT_ABSTRACT_IMAGE_ALBUM                 0xBA02
+#define MTP_FORMAT_ABSTRACT_AUDIO_ALBUM                 0xBA03
+#define MTP_FORMAT_ABSTRACT_VIDEO_ALBUM                 0xBA04
+#define MTP_FORMAT_ABSTRACT_AV_PLAYLIST                 0xBA05
+#define MTP_FORMAT_ABSTRACT_CONTACT_GROUP               0xBA06
+#define MTP_FORMAT_ABSTRACT_MESSAGE_FOLDER              0xBA07
+#define MTP_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION        0xBA08
+#define MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST              0xBA09
+#define MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST              0xBA0A
+#define MTP_FORMAT_ABSTRACT_MEDIACAST                   0xBA0B // For use with mediacasts; references multimedia enclosures of RSS feeds or episodic content
+#define MTP_FORMAT_WPL_PLAYLIST                         0xBA10
+#define MTP_FORMAT_M3U_PLAYLIST                         0xBA11
+#define MTP_FORMAT_MPL_PLAYLIST                         0xBA12
+#define MTP_FORMAT_ASX_PLAYLIST                         0xBA13
+#define MTP_FORMAT_PLS_PLAYLIST                         0xBA14
+#define MTP_FORMAT_UNDEFINED_DOCUMENT                   0xBA80
+#define MTP_FORMAT_ABSTRACT_DOCUMENT                    0xBA81
+#define MTP_FORMAT_XML_DOCUMENT                         0xBA82
+#define MTP_FORMAT_MS_WORD_DOCUMENT                     0xBA83
+#define MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT           0xBA84
+#define MTP_FORMAT_MS_EXCEL_SPREADSHEET                 0xBA85
+#define MTP_FORMAT_MS_POWERPOINT_PRESENTATION           0xBA86
+#define MTP_FORMAT_UNDEFINED_MESSAGE                    0xBB00
+#define MTP_FORMAT_ABSTRACT_MESSSAGE                    0xBB01
+#define MTP_FORMAT_UNDEFINED_CONTACT                    0xBB80
+#define MTP_FORMAT_ABSTRACT_CONTACT                     0xBB81
+#define MTP_FORMAT_VCARD_2                              0xBB82
+
+// MTP Object Property Codes
+#define MTP_PROPERTY_STORAGE_ID                             0xDC01
+#define MTP_PROPERTY_OBJECT_FORMAT                          0xDC02
+#define MTP_PROPERTY_PROTECTION_STATUS                      0xDC03
+#define MTP_PROPERTY_OBJECT_SIZE                            0xDC04
+#define MTP_PROPERTY_ASSOCIATION_TYPE                       0xDC05
+#define MTP_PROPERTY_ASSOCIATION_DESC                       0xDC06
+#define MTP_PROPERTY_OBJECT_FILE_NAME                       0xDC07
+#define MTP_PROPERTY_DATE_CREATED                           0xDC08
+#define MTP_PROPERTY_DATE_MODIFIED                          0xDC09
+#define MTP_PROPERTY_KEYWORDS                               0xDC0A
+#define MTP_PROPERTY_PARENT_OBJECT                          0xDC0B
+#define MTP_PROPERTY_ALLOWED_FOLDER_CONTENTS                0xDC0C
+#define MTP_PROPERTY_HIDDEN                                 0xDC0D
+#define MTP_PROPERTY_SYSTEM_OBJECT                          0xDC0E
+#define MTP_PROPERTY_PERSISTENT_UID                         0xDC41
+#define MTP_PROPERTY_SYNC_ID                                0xDC42
+#define MTP_PROPERTY_PROPERTY_BAG                           0xDC43
+#define MTP_PROPERTY_NAME                                   0xDC44
+#define MTP_PROPERTY_CREATED_BY                             0xDC45
+#define MTP_PROPERTY_ARTIST                                 0xDC46
+#define MTP_PROPERTY_DATE_AUTHORED                          0xDC47
+#define MTP_PROPERTY_DESCRIPTION                            0xDC48
+#define MTP_PROPERTY_URL_REFERENCE                          0xDC49
+#define MTP_PROPERTY_LANGUAGE_LOCALE                        0xDC4A
+#define MTP_PROPERTY_COPYRIGHT_INFORMATION                  0xDC4B
+#define MTP_PROPERTY_SOURCE                                 0xDC4C
+#define MTP_PROPERTY_ORIGIN_LOCATION                        0xDC4D
+#define MTP_PROPERTY_DATE_ADDED                             0xDC4E
+#define MTP_PROPERTY_NON_CONSUMABLE                         0xDC4F
+#define MTP_PROPERTY_CORRUPT_UNPLAYABLE                     0xDC50
+#define MTP_PROPERTY_PRODUCER_SERIAL_NUMBER                 0xDC51
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT           0xDC81
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_SIZE             0xDC82
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT           0xDC83
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH            0xDC84
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DURATION         0xDC85
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DATA             0xDC86
+#define MTP_PROPERTY_WIDTH                                  0xDC87
+#define MTP_PROPERTY_HEIGHT                                 0xDC88
+#define MTP_PROPERTY_DURATION                               0xDC89
+#define MTP_PROPERTY_RATING                                 0xDC8A
+#define MTP_PROPERTY_TRACK                                  0xDC8B
+#define MTP_PROPERTY_GENRE                                  0xDC8C
+#define MTP_PROPERTY_CREDITS                                0xDC8D
+#define MTP_PROPERTY_LYRICS                                 0xDC8E
+#define MTP_PROPERTY_SUBSCRIPTION_CONTENT_ID                0xDC8F
+#define MTP_PROPERTY_PRODUCED_BY                            0xDC90
+#define MTP_PROPERTY_USE_COUNT                              0xDC91
+#define MTP_PROPERTY_SKIP_COUNT                             0xDC92
+#define MTP_PROPERTY_LAST_ACCESSED                          0xDC93
+#define MTP_PROPERTY_PARENTAL_RATING                        0xDC94
+#define MTP_PROPERTY_META_GENRE                             0xDC95
+#define MTP_PROPERTY_COMPOSER                               0xDC96
+#define MTP_PROPERTY_EFFECTIVE_RATING                       0xDC97
+#define MTP_PROPERTY_SUBTITLE                               0xDC98
+#define MTP_PROPERTY_ORIGINAL_RELEASE_DATE                  0xDC99
+#define MTP_PROPERTY_ALBUM_NAME                             0xDC9A
+#define MTP_PROPERTY_ALBUM_ARTIST                           0xDC9B
+#define MTP_PROPERTY_MOOD                                   0xDC9C
+#define MTP_PROPERTY_DRM_STATUS                             0xDC9D
+#define MTP_PROPERTY_SUB_DESCRIPTION                        0xDC9E
+#define MTP_PROPERTY_IS_CROPPED                             0xDCD1
+#define MTP_PROPERTY_IS_COLOUR_CORRECTED                    0xDCD2
+#define MTP_PROPERTY_IMAGE_BIT_DEPTH                        0xDCD3
+#define MTP_PROPERTY_F_NUMBER                               0xDCD4
+#define MTP_PROPERTY_EXPOSURE_TIME                          0xDCD5
+#define MTP_PROPERTY_EXPOSURE_INDEX                         0xDCD6
+#define MTP_PROPERTY_TOTAL_BITRATE                          0xDE91
+#define MTP_PROPERTY_BITRATE_TYPE                           0xDE92
+#define MTP_PROPERTY_SAMPLE_RATE                            0xDE93
+#define MTP_PROPERTY_NUMBER_OF_CHANNELS                     0xDE94
+#define MTP_PROPERTY_AUDIO_BIT_DEPTH                        0xDE95
+#define MTP_PROPERTY_SCAN_TYPE                              0xDE97
+#define MTP_PROPERTY_AUDIO_WAVE_CODEC                       0xDE99
+#define MTP_PROPERTY_AUDIO_BITRATE                          0xDE9A
+#define MTP_PROPERTY_VIDEO_FOURCC_CODEC                     0xDE9B
+#define MTP_PROPERTY_VIDEO_BITRATE                          0xDE9C
+#define MTP_PROPERTY_FRAMES_PER_THOUSAND_SECONDS            0xDE9D
+#define MTP_PROPERTY_KEYFRAME_DISTANCE                      0xDE9E
+#define MTP_PROPERTY_BUFFER_SIZE                            0xDE9F
+#define MTP_PROPERTY_ENCODING_QUALITY                       0xDEA0
+#define MTP_PROPERTY_ENCODING_PROFILE                       0xDEA1
+#define MTP_PROPERTY_DISPLAY_NAME                           0xDCE0
+#define MTP_PROPERTY_BODY_TEXT                              0xDCE1
+#define MTP_PROPERTY_SUBJECT                                0xDCE2
+#define MTP_PROPERTY_PRIORITY                               0xDCE3
+#define MTP_PROPERTY_GIVEN_NAME                             0xDD00
+#define MTP_PROPERTY_MIDDLE_NAMES                           0xDD01
+#define MTP_PROPERTY_FAMILY_NAME                            0xDD02
+#define MTP_PROPERTY_PREFIX                                 0xDD03
+#define MTP_PROPERTY_SUFFIX                                 0xDD04
+#define MTP_PROPERTY_PHONETIC_GIVEN_NAME                    0xDD05
+#define MTP_PROPERTY_PHONETIC_FAMILY_NAME                   0xDD06
+#define MTP_PROPERTY_EMAIL_PRIMARY                          0xDD07
+#define MTP_PROPERTY_EMAIL_PERSONAL_1                       0xDD08
+#define MTP_PROPERTY_EMAIL_PERSONAL_2                       0xDD09
+#define MTP_PROPERTY_EMAIL_BUSINESS_1                       0xDD0A
+#define MTP_PROPERTY_EMAIL_BUSINESS_2                       0xDD0B
+#define MTP_PROPERTY_EMAIL_OTHERS                           0xDD0C
+#define MTP_PROPERTY_PHONE_NUMBER_PRIMARY                   0xDD0D
+#define MTP_PROPERTY_PHONE_NUMBER_PERSONAL                  0xDD0E
+#define MTP_PROPERTY_PHONE_NUMBER_PERSONAL_2                0xDD0F
+#define MTP_PROPERTY_PHONE_NUMBER_BUSINESS                  0xDD10
+#define MTP_PROPERTY_PHONE_NUMBER_BUSINESS_2                0xDD11
+#define MTP_PROPERTY_PHONE_NUMBER_MOBILE                    0xDD12
+#define MTP_PROPERTY_PHONE_NUMBER_MOBILE_2                  0xDD13
+#define MTP_PROPERTY_FAX_NUMBER_PRIMARY                     0xDD14
+#define MTP_PROPERTY_FAX_NUMBER_PERSONAL                    0xDD15
+#define MTP_PROPERTY_FAX_NUMBER_BUSINESS                    0xDD16
+#define MTP_PROPERTY_PAGER_NUMBER                           0xDD17
+#define MTP_PROPERTY_PHONE_NUMBER_OTHERS                    0xDD18
+#define MTP_PROPERTY_PRIMARY_WEB_ADDRESS                    0xDD19
+#define MTP_PROPERTY_PERSONAL_WEB_ADDRESS                   0xDD1A
+#define MTP_PROPERTY_BUSINESS_WEB_ADDRESS                   0xDD1B
+#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS              0xDD1C
+#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_2            0xDD1D
+#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_3            0xDD1E
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL           0xDD1F
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1         0xDD20
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2         0xDD21
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY           0xDD22
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION         0xDD23
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE    0xDD24
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY        0xDD25
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL           0xDD26
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1         0xDD27
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2         0xDD28
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY           0xDD29
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION         0xDD2A
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE    0xDD2B
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY        0xDD2C
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_FULL              0xDD2D
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1            0xDD2E
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2            0xDD2F
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_CITY              0xDD30
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_REGION            0xDD31
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE       0xDD32
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY           0xDD33
+#define MTP_PROPERTY_ORGANIZATION_NAME                      0xDD34
+#define MTP_PROPERTY_PHONETIC_ORGANIZATION_NAME             0xDD35
+#define MTP_PROPERTY_ROLE                                   0xDD36
+#define MTP_PROPERTY_BIRTHDATE                              0xDD37
+#define MTP_PROPERTY_MESSAGE_TO                             0xDD40
+#define MTP_PROPERTY_MESSAGE_CC                             0xDD41
+#define MTP_PROPERTY_MESSAGE_BCC                            0xDD42
+#define MTP_PROPERTY_MESSAGE_READ                           0xDD43
+#define MTP_PROPERTY_MESSAGE_RECEIVED_TIME                  0xDD44
+#define MTP_PROPERTY_MESSAGE_SENDER                         0xDD45
+#define MTP_PROPERTY_ACTIVITY_BEGIN_TIME                    0xDD50
+#define MTP_PROPERTY_ACTIVITY_END_TIME                      0xDD51
+#define MTP_PROPERTY_ACTIVITY_LOCATION                      0xDD52
+#define MTP_PROPERTY_ACTIVITY_REQUIRED_ATTENDEES            0xDD54
+#define MTP_PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES            0xDD55
+#define MTP_PROPERTY_ACTIVITY_RESOURCES                     0xDD56
+#define MTP_PROPERTY_ACTIVITY_ACCEPTED                      0xDD57
+#define MTP_PROPERTY_ACTIVITY_TENTATIVE                     0xDD58
+#define MTP_PROPERTY_ACTIVITY_DECLINED                      0xDD59
+#define MTP_PROPERTY_ACTIVITY_REMAINDER_TIME                0xDD5A
+#define MTP_PROPERTY_ACTIVITY_OWNER                         0xDD5B
+#define MTP_PROPERTY_ACTIVITY_STATUS                        0xDD5C
+#define MTP_PROPERTY_OWNER                                  0xDD5D
+#define MTP_PROPERTY_EDITOR                                 0xDD5E
+#define MTP_PROPERTY_WEBMASTER                              0xDD5F
+#define MTP_PROPERTY_URL_SOURCE                             0xDD60
+#define MTP_PROPERTY_URL_DESTINATION                        0xDD61
+#define MTP_PROPERTY_TIME_BOOKMARK                          0xDD62
+#define MTP_PROPERTY_OBJECT_BOOKMARK                        0xDD63
+#define MTP_PROPERTY_BYTE_BOOKMARK                          0xDD64
+#define MTP_PROPERTY_LAST_BUILD_DATE                        0xDD70
+#define MTP_PROPERTY_TIME_TO_LIVE                           0xDD71
+#define MTP_PROPERTY_MEDIA_GUID                             0xDD72
+
+// MTP Device Property Codes
+#define MTP_DEVICE_PROPERTY_UNDEFINED                       0x5000
+#define MTP_DEVICE_PROPERTY_BATTERY_LEVEL                   0x5001
+#define MTP_DEVICE_PROPERTY_FUNCTIONAL_MODE                 0x5002
+#define MTP_DEVICE_PROPERTY_IMAGE_SIZE                      0x5003
+#define MTP_DEVICE_PROPERTY_COMPRESSION_SETTING             0x5004
+#define MTP_DEVICE_PROPERTY_WHITE_BALANCE                   0x5005
+#define MTP_DEVICE_PROPERTY_RGB_GAIN                        0x5006
+#define MTP_DEVICE_PROPERTY_F_NUMBER                        0x5007
+#define MTP_DEVICE_PROPERTY_FOCAL_LENGTH                    0x5008
+#define MTP_DEVICE_PROPERTY_FOCUS_DISTANCE                  0x5009
+#define MTP_DEVICE_PROPERTY_FOCUS_MODE                      0x500A
+#define MTP_DEVICE_PROPERTY_EXPOSURE_METERING_MODE          0x500B
+#define MTP_DEVICE_PROPERTY_FLASH_MODE                      0x500C
+#define MTP_DEVICE_PROPERTY_EXPOSURE_TIME                   0x500D
+#define MTP_DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE           0x500E
+#define MTP_DEVICE_PROPERTY_EXPOSURE_INDEX                  0x500F
+#define MTP_DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION      0x5010
+#define MTP_DEVICE_PROPERTY_DATETIME                        0x5011
+#define MTP_DEVICE_PROPERTY_CAPTURE_DELAY                   0x5012
+#define MTP_DEVICE_PROPERTY_STILL_CAPTURE_MODE              0x5013
+#define MTP_DEVICE_PROPERTY_CONTRAST                        0x5014
+#define MTP_DEVICE_PROPERTY_SHARPNESS                       0x5015
+#define MTP_DEVICE_PROPERTY_DIGITAL_ZOOM                    0x5016
+#define MTP_DEVICE_PROPERTY_EFFECT_MODE                     0x5017
+#define MTP_DEVICE_PROPERTY_BURST_NUMBER                    0x5018
+#define MTP_DEVICE_PROPERTY_BURST_INTERVAL                  0x5019
+#define MTP_DEVICE_PROPERTY_TIMELAPSE_NUMBER                0x501A
+#define MTP_DEVICE_PROPERTY_TIMELAPSE_INTERVAL              0x501B
+#define MTP_DEVICE_PROPERTY_FOCUS_METERING_MODE             0x501C
+#define MTP_DEVICE_PROPERTY_UPLOAD_URL                      0x501D
+#define MTP_DEVICE_PROPERTY_ARTIST                          0x501E
+#define MTP_DEVICE_PROPERTY_COPYRIGHT_INFO                  0x501F
+#define MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER         0xD401
+#define MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME            0xD402
+#define MTP_DEVICE_PROPERTY_VOLUME                          0xD403
+#define MTP_DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED       0xD404
+#define MTP_DEVICE_PROPERTY_DEVICE_ICON                     0xD405
+#define MTP_DEVICE_PROPERTY_PLAYBACK_RATE                   0xD410
+#define MTP_DEVICE_PROPERTY_PLAYBACK_OBJECT                 0xD411
+#define MTP_DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX        0xD412
+#define MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO  0xD406
+#define MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE           0xD407
+
+// MTP Operation Codes
+#define MTP_OPERATION_GET_DEVICE_INFO                       0x1001
+#define MTP_OPERATION_OPEN_SESSION                          0x1002
+#define MTP_OPERATION_CLOSE_SESSION                         0x1003
+#define MTP_OPERATION_GET_STORAGE_IDS                       0x1004
+#define MTP_OPERATION_GET_STORAGE_INFO                      0x1005
+#define MTP_OPERATION_GET_NUM_OBJECTS                       0x1006
+#define MTP_OPERATION_GET_OBJECT_HANDLES                    0x1007
+#define MTP_OPERATION_GET_OBJECT_INFO                       0x1008
+#define MTP_OPERATION_GET_OBJECT                            0x1009
+#define MTP_OPERATION_GET_THUMB                             0x100A
+#define MTP_OPERATION_DELETE_OBJECT                         0x100B
+#define MTP_OPERATION_SEND_OBJECT_INFO                      0x100C
+#define MTP_OPERATION_SEND_OBJECT                           0x100D
+#define MTP_OPERATION_INITIATE_CAPTURE                      0x100E
+#define MTP_OPERATION_FORMAT_STORE                          0x100F
+#define MTP_OPERATION_RESET_DEVICE                          0x1010
+#define MTP_OPERATION_SELF_TEST                             0x1011
+#define MTP_OPERATION_SET_OBJECT_PROTECTION                 0x1012
+#define MTP_OPERATION_POWER_DOWN                            0x1013
+#define MTP_OPERATION_GET_DEVICE_PROP_DESC                  0x1014
+#define MTP_OPERATION_GET_DEVICE_PROP_VALUE                 0x1015
+#define MTP_OPERATION_SET_DEVICE_PROP_VALUE                 0x1016
+#define MTP_OPERATION_RESET_DEVICE_PROP_VALUE               0x1017
+#define MTP_OPERATION_TERMINATE_OPEN_CAPTURE                0x1018
+#define MTP_OPERATION_MOVE_OBJECT                           0x1019
+#define MTP_OPERATION_COPY_OBJECT                           0x101A
+#define MTP_OPERATION_GET_PARTIAL_OBJECT                    0x101B
+#define MTP_OPERATION_INITIATE_OPEN_CAPTURE                 0x101C
+#define MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED            0x9801
+#define MTP_OPERATION_GET_OBJECT_PROP_DESC                  0x9802
+#define MTP_OPERATION_GET_OBJECT_PROP_VALUE                 0x9803
+#define MTP_OPERATION_SET_OBJECT_PROP_VALUE                 0x9804
+#define MTP_OPERATION_GET_OBJECT_REFERENCES                 0x9810
+#define MTP_OPERATION_SET_OBJECT_REFERENCES                 0x9811
+#define MTP_OPERATION_SKIP                                  0x9820
+
+// MTP Response Codes
+#define MTP_RESPONSE_UNDEFINED                                  0x2000
+#define MTP_RESPONSE_OK                                         0x2001
+#define MTP_RESPONSE_GENERAL_ERROR                              0x2002
+#define MTP_RESPONSE_SESSION_NOT_OPEN                           0x2003
+#define MTP_RESPONSE_INVALID_TRANSACTION_ID                     0x2004
+#define MTP_RESPONSE_OPERATION_NOT_SUPPORTED                    0x2005
+#define MTP_RESPONSE_PARAMETER_NOT_SUPPORTED                    0x2006
+#define MTP_RESPONSE_INCOMPLETE_TRANSFER                        0x2007
+#define MTP_RESPONSE_INVALID_STORAGE_ID                         0x2008
+#define MTP_RESPONSE_INVALID_OBJECT_HANDLE                      0x2009
+#define MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED                  0x200A
+#define MTP_RESPONSE_INVALID_OBJECT_FORMAT_CODE                 0x200B
+#define MTP_RESPONSE_STORAGE_FULL                               0x200C
+#define MTP_RESPONSE_OBJECT_WRITE_PROTECTED                     0x200D
+#define MTP_RESPONSE_STORE_READ_ONLY                            0x200E
+#define MTP_RESPONSE_ACCESS_DENIED                              0x200F
+#define MTP_RESPONSE_NO_THUMBNAIL_PRESENT                       0x2010
+#define MTP_RESPONSE_SELF_TEST_FAILED                           0x2011
+#define MTP_RESPONSE_PARTIAL_DELETION                           0x2012
+#define MTP_RESPONSE_STORE_NOT_AVAILABLE                        0x2013
+#define MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED        0x2014
+#define MTP_RESPONSE_NO_VALID_OBJECT_INFO                       0x2015
+#define MTP_RESPONSE_INVALID_CODE_FORMAT                        0x2016
+#define MTP_RESPONSE_UNKNOWN_VENDOR_CODE                        0x2017
+#define MTP_RESPONSE_CAPTURE_ALREADY_TERMINATED                 0x2018
+#define MTP_RESPONSE_DEVICE_BUSY                                0x2019
+#define MTP_RESPONSE_INVALID_PARENT_OBJECT                      0x201A
+#define MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT                 0x201B
+#define MTP_RESPONSE_INVALID_DEVICE_PROP_VALUE                  0x201C
+#define MTP_RESPONSE_INVALID_PARAMETER                          0x201D
+#define MTP_RESPONSE_SESSION_ALREADY_OPEN                       0x201E
+#define MTP_RESPONSE_TRANSACTION_CANCELLED                      0x201F
+#define MTP_RESPONSE_SPECIFICATION_OF_DESTINATION_UNSUPPORTED   0x2020
+#define MTP_RESPONSE_INVALID_OBJECT_PROP_CODE                   0xA801
+#define MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT                 0xA802
+#define MTP_RESPONSE_INVALID_OBJECT_PROP_VALUE                  0xA803
+#define MTP_RESPONSE_INVALID_OBJECT_REFERENCE                   0xA804
+#define MTP_RESPONSE_GROUP_NOT_SUPPORTED                        0xA805
+#define MTP_RESPONSE_INVALID_DATASET                            0xA806
+#define MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED         0xA807
+#define MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED         0xA808
+#define MTP_RESPONSE_OBJECT_TOO_LARGE                           0xA809
+#define MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED                  0xA80A
+
+// MTP Event Codes
+#define MTP_EVENT_UNDEFINED                         0x4000
+#define MTP_EVENT_CANCEL_TRANSACTION                0x4001
+#define MTP_EVENT_OBJECT_ADDED                      0x4002
+#define MTP_EVENT_OBJECT_REMOVED                    0x4003
+#define MTP_EVENT_STORE_ADDED                       0x4004
+#define MTP_EVENT_STORE_REMOVED                     0x4005
+#define MTP_EVENT_DEVICE_PROP_CHANGED               0x4006
+#define MTP_EVENT_OBJECT_INFO_CHANGED               0x4007
+#define MTP_EVENT_DEVICE_INFO_CHANGED               0x4008
+#define MTP_EVENT_REQUEST_OBJECT_TRANSFER           0x4009
+#define MTP_EVENT_STORE_FULL                        0x400A
+#define MTP_EVENT_DEVICE_RESET                      0x400B
+#define MTP_EVENT_STORAGE_INFO_CHANGED              0x400C
+#define MTP_EVENT_CAPTURE_COMPLETE                  0x400D
+#define MTP_EVENT_UNREPORTED_STATUS                 0x400E
+#define MTP_EVENT_OBJECT_PROP_CHANGED               0xC801
+#define MTP_EVENT_OBJECT_PROP_DESC_CHANGED          0xC802
+#define MTP_EVENT_OBJECT_REFERENCES_CHANGED         0xC803
+
+// Storage Type
+#define MTP_STORAGE_FIXED_ROM                       0x0001
+#define MTP_STORAGE_REMOVABLE_ROM                   0x0002
+#define MTP_STORAGE_FIXED_RAM                       0x0003
+#define MTP_STORAGE_REMOVABLE_RAM                   0x0004
+
+// Storage File System
+#define MTP_STORAGE_FILESYSTEM_FLAT                 0x0001
+#define MTP_STORAGE_FILESYSTEM_HIERARCHICAL         0x0002
+#define MTP_STORAGE_FILESYSTEM_DCF                  0x0003
+
+// Storage Access Capability
+#define MTP_STORAGE_READ_WRITE                      0x0000
+#define MTP_STORAGE_READ_ONLY_WITHOUT_DELETE        0x0001
+#define MTP_STORAGE_READ_ONLY_WITH_DELETE           0x0002
+
+// Association Type
+#define MTP_ASSOCIATION_TYPE_UNDEFINED              0x0000
+#define MTP_ASSOCIATION_TYPE_GENERIC_FOLDER         0x0001
+
+#endif // _MTP_H
diff --git a/media/tests/CameraBrowser/Android.mk b/media/tests/CameraBrowser/Android.mk
new file mode 100644
index 0000000..1d81129
--- /dev/null
+++ b/media/tests/CameraBrowser/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CameraBrowser
+
+include $(BUILD_PACKAGE)
diff --git a/media/tests/CameraBrowser/AndroidManifest.xml b/media/tests/CameraBrowser/AndroidManifest.xml
new file mode 100644
index 0000000..eae0b01
--- /dev/null
+++ b/media/tests/CameraBrowser/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.camerabrowser">
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application android:label="@string/app_label">
+        <activity android:name="CameraBrowser" android:label="Camera Browser">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name="StorageBrowser" />
+        <activity android:name="ObjectBrowser" />
+        <activity android:name="ObjectViewer" />
+
+        <receiver android:name="UsbReceiver">
+            <intent-filter>
+                <action android:name="android.hardware.action.USB_CAMERA_ATTACHED" />
+                <data android:scheme="content"/>
+            </intent-filter>
+        </receiver>
+
+    </application>
+
+
+</manifest>
diff --git a/media/tests/CameraBrowser/res/layout/object_info.xml b/media/tests/CameraBrowser/res/layout/object_info.xml
new file mode 100644
index 0000000..ac210b9
--- /dev/null
+++ b/media/tests/CameraBrowser/res/layout/object_info.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/object_info"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+    <TableRow>
+        <TextView android:id="@+id/name_label"
+            android:text="@string/name_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/name"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/size_label"
+            android:text="@string/size_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/size"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/format_label"
+            android:text="@string/format_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/format"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/thumb_width_label"
+            android:text="@string/thumb_width_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/thumb_width"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/thumb_height_label"
+            android:text="@string/thumb_height_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/thumb_height"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/thumb_size_label"
+            android:text="@string/thumb_size_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/thumb_size"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/width_label"
+            android:text="@string/width_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/width"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/height_label"
+            android:text="@string/height_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/height"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/depth_label"
+            android:text="@string/depth_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/depth"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/sequence_label"
+            android:text="@string/sequence_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/sequence"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/created_label"
+            android:text="@string/created_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/created"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/modified_label"
+            android:text="@string/modified_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/modified"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <TextView android:id="@+id/keywords_label"
+            android:text="@string/keywords_label"
+            android:layout_gravity="right"
+            android:layout_marginRight="8dip"
+            style="@style/info_label" />
+
+        <TextView android:id="@+id/keywords"
+            style="@style/info_value" />
+    </TableRow>
+    <TableRow>
+        <ImageView android:id="@+id/thumbnail" />
+    </TableRow>
+</TableLayout>
+
diff --git a/media/tests/CameraBrowser/res/layout/object_list.xml b/media/tests/CameraBrowser/res/layout/object_list.xml
new file mode 100644
index 0000000..30c18bb
--- /dev/null
+++ b/media/tests/CameraBrowser/res/layout/object_list.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="fill_parent" 
+    android:layout_height="fill_parent">
+    
+    <ImageView android:id="@+id/thumbnail"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <TextView android:id="@+id/name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:gravity="center_vertical"
+        android:paddingLeft="6dip"
+        android:minHeight="?android:attr/listPreferredItemHeight" />
+</LinearLayout>
diff --git a/media/tests/CameraBrowser/res/menu/object_menu.xml b/media/tests/CameraBrowser/res/menu/object_menu.xml
new file mode 100644
index 0000000..a0865f0
--- /dev/null
+++ b/media/tests/CameraBrowser/res/menu/object_menu.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:id="@+id/save"
+        android:title="@string/save_item" />
+    <item android:id="@+id/delete"
+        android:title="@string/delete_item" />
+</menu>
diff --git a/media/tests/CameraBrowser/res/values/strings.xml b/media/tests/CameraBrowser/res/values/strings.xml
new file mode 100644
index 0000000..cd477f1
--- /dev/null
+++ b/media/tests/CameraBrowser/res/values/strings.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="app_label">Camera Browser</string>
+
+    <!-- for object info -->
+    <string name="name_label">Name: </string>
+    <string name="size_label">Size: </string>
+    <string name="format_label">Format: </string>
+    <string name="thumb_width_label">Thumb Width: </string>
+    <string name="thumb_height_label">Thumb Height: </string>
+    <string name="thumb_size_label">Thumb Size: </string>
+    <string name="width_label">Width: </string>
+    <string name="height_label">Height: </string>
+    <string name="depth_label">Depth: </string>
+    <string name="sequence_label">Sequence: </string>
+    <string name="created_label">Created: </string>
+    <string name="modified_label">Modified: </string>
+    <string name="keywords_label">Keywords: </string>
+
+    <!-- menu items -->
+    <string name="save_item">Save</string>
+    <string name="delete_item">Delete</string>
+
+    <!-- toasts -->
+    <string name="object_saved_message">Object saved</string>
+    <string name="save_failed_message">Could not save object</string>
+    <string name="object_deleted_message">Object deleted</string>
+    <string name="delete_failed_message">Could not delete object</string>
+
+</resources>
diff --git a/media/tests/CameraBrowser/res/values/styles.xml b/media/tests/CameraBrowser/res/values/styles.xml
new file mode 100644
index 0000000..c869985
--- /dev/null
+++ b/media/tests/CameraBrowser/res/values/styles.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+
+<resources>
+    <style name="info_label">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">bold</item>
+        <item name="android:paddingRight">4dip</item>
+    </style>
+
+    <style name="info_value">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">normal</item>
+    </style>
+
+</resources>
+
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java
new file mode 100644
index 0000000..c04873a
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/CameraBrowser.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camerabrowser;
+
+import android.app.ListActivity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Mtp;
+import android.util.Log;
+import android.view.View;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+
+ /**
+ * A list view displaying all connected cameras.
+ */
+public class CameraBrowser extends ListActivity {
+
+    private static final String TAG = "CameraBrowser";
+
+    private ListAdapter mAdapter;
+    private ContentResolver mResolver;
+    private DeviceObserver mDeviceObserver;
+    private Cursor mCursor;
+
+    private class DeviceObserver extends ContentObserver {
+        DeviceObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            Log.d(TAG, "DeviceObserver.onChange");
+            if (mCursor != null) {
+                mCursor.requery();
+            }
+        }
+    }
+
+    private static final String[] DEVICE_COLUMNS =
+         new String[] { Mtp.Device._ID, Mtp.Device.MANUFACTURER, Mtp.Device.MODEL };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mResolver = getContentResolver();
+        mDeviceObserver = new DeviceObserver(new Handler());
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        Cursor c = getContentResolver().query(Mtp.Device.CONTENT_URI,
+                DEVICE_COLUMNS, null, null, null);
+        Log.d(TAG, "query returned " + c);
+        startManagingCursor(c);
+        mCursor = c;
+
+        // Map Cursor columns to views defined in simple_list_item_2.xml
+        mAdapter = new SimpleCursorAdapter(this,
+                android.R.layout.simple_list_item_2, c,
+                        new String[] { Mtp.Device.MANUFACTURER, Mtp.Device.MODEL },
+                        new int[] { android.R.id.text1, android.R.id.text2 });
+        setListAdapter(mAdapter);
+
+        // register for changes to the device list
+        mResolver.registerContentObserver(Mtp.Device.CONTENT_URI, true, mDeviceObserver);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mResolver.unregisterContentObserver(mDeviceObserver);
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        Intent intent = new Intent(this, StorageBrowser.class);
+        intent.putExtra("device", (int)mAdapter.getItemId(position));
+        startActivity(intent);
+    }
+}
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
new file mode 100644
index 0000000..6d34fd4
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camerabrowser;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.MtpConstants;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Mtp;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.ResourceCursorAdapter;
+import android.widget.TextView;
+
+ /**
+ * A list view displaying all objects within a container (folder or storage unit).
+ */
+public class ObjectBrowser extends ListActivity {
+
+    private static final String TAG = "ObjectBrowser";
+
+    private Cursor mCursor;
+    private ObjectCursorAdapter mAdapter;
+    private int mDeviceID;
+    private long mStorageID;
+    private long mObjectID;
+
+    private static final String[] OBJECT_COLUMNS =
+        new String[] { Mtp.Object._ID, Mtp.Object.NAME, Mtp.Object.FORMAT, Mtp.Object.THUMB };
+
+    static final int ID_COLUMN = 0;
+    static final int NAME_COLUMN = 1;
+    static final int FORMAT_COLUMN = 2;
+    static final int THUMB_COLUMN = 3;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        mDeviceID = getIntent().getIntExtra("device", 0);
+        mStorageID = getIntent().getLongExtra("storage", 0);
+        mObjectID = getIntent().getLongExtra("object", 0);
+        if (mDeviceID != 0 && mStorageID != 0) {
+            Cursor c;
+            Uri uri;
+            if (mObjectID == 0) {
+                uri = Mtp.Object.getContentUriForStorageChildren(mDeviceID, mStorageID);
+            } else {
+                uri = Mtp.Object.getContentUriForObjectChildren(mDeviceID, mObjectID);
+            }
+            Log.d(TAG, "query " + uri);
+            c = getContentResolver().query(uri, OBJECT_COLUMNS, null, null, null);
+            startManagingCursor(c);
+            mCursor = c;
+
+            // Map Cursor columns to views defined in simple_list_item_1.xml
+            mAdapter = new ObjectCursorAdapter(this, c);
+            setListAdapter(mAdapter);
+        }
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        long rowID = mAdapter.getItemId(position);
+        Cursor c = getContentResolver().query(
+                        Mtp.Object.getContentUri(mDeviceID, rowID),
+                        OBJECT_COLUMNS, null, null, null);
+        Log.d(TAG, "query returned " + c + " count: " + c.getCount());
+        long format = 0;
+        if (c != null && c.getCount() == 1) {
+            c.moveToFirst();
+            long rowId = c.getLong(ID_COLUMN);
+            String name = c.getString(NAME_COLUMN);
+            format = c.getLong(FORMAT_COLUMN);
+            Log.d(TAG, "rowId: " + rowId + " name: " + name + " format: " + format);
+        }
+        if (format == MtpConstants.FORMAT_ASSOCIATION) {
+            Intent intent = new Intent(this, ObjectBrowser.class);
+            intent.putExtra("device", mDeviceID);
+            intent.putExtra("storage", mStorageID);
+            intent.putExtra("object", rowID);
+            startActivity(intent);
+        } else {
+            Intent intent = new Intent(this, ObjectViewer.class);
+            intent.putExtra("device", mDeviceID);
+            intent.putExtra("storage", mStorageID);
+            intent.putExtra("object", rowID);
+            startActivity(intent);
+        }
+    }
+
+    private class ObjectCursorAdapter extends ResourceCursorAdapter {
+
+        public ObjectCursorAdapter(Context context, Cursor c) {
+            super(context, R.layout.object_list, c);
+        }
+
+        @Override
+        public void bindView(View view, Context context, Cursor cursor) {
+            ImageView thumbView = (ImageView)view.findViewById(R.id.thumbnail);
+            TextView nameView = (TextView)view.findViewById(R.id.name);
+
+            // get the thumbnail
+            byte[] thumbnail = cursor.getBlob(THUMB_COLUMN);
+            if (thumbnail != null) {
+                Bitmap bitmap = BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length);
+                if (bitmap != null) {
+                    thumbView.setImageBitmap(bitmap);
+                }
+            }
+
+            // get the name
+            String name = cursor.getString(NAME_COLUMN);
+            if (name == null) {
+                name = "";
+            }
+            nameView.setText(name);
+        }
+    }
+}
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
new file mode 100644
index 0000000..9f2f98e
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camerabrowser;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.provider.Mtp;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * A view to display the properties of an object.
+ */
+public class ObjectViewer extends Activity {
+
+    private static final String TAG = "ObjectViewer";
+
+    private int mDeviceID;
+    private long mStorageID;
+    private long mObjectID;
+
+    private static final String[] OBJECT_COLUMNS =
+        new String[] {  Mtp.Object._ID,
+                        Mtp.Object.NAME,
+                        Mtp.Object.SIZE,
+                        Mtp.Object.THUMB_WIDTH,
+                        Mtp.Object.THUMB_HEIGHT,
+                        Mtp.Object.THUMB_SIZE,
+                        Mtp.Object.IMAGE_WIDTH,
+                        Mtp.Object.IMAGE_HEIGHT,
+                        Mtp.Object.IMAGE_DEPTH,
+                        Mtp.Object.SEQUENCE_NUMBER,
+                        Mtp.Object.DATE_CREATED,
+                        Mtp.Object.DATE_MODIFIED,
+                        Mtp.Object.KEYWORDS,
+                        Mtp.Object.THUMB,
+                        Mtp.Object.FORMAT,
+                        };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.object_info);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        mDeviceID = getIntent().getIntExtra("device", 0);
+        mStorageID = getIntent().getLongExtra("storage", 0);
+        mObjectID = getIntent().getLongExtra("object", 0);
+
+        if (mDeviceID != 0 && mObjectID != 0) {
+        Cursor c = getContentResolver().query(
+                        Mtp.Object.getContentUri(mDeviceID, mObjectID),
+                        OBJECT_COLUMNS, null, null, null);
+            c.moveToFirst();
+            TextView view = (TextView)findViewById(R.id.name);
+            view.setText(c.getString(1));
+            view = (TextView)findViewById(R.id.size);
+            view.setText(Long.toString(c.getLong(2)));
+            view = (TextView)findViewById(R.id.thumb_width);
+            view.setText(Long.toString(c.getLong(3)));
+            view = (TextView)findViewById(R.id.thumb_height);
+            view.setText(Long.toString(c.getLong(4)));
+            view = (TextView)findViewById(R.id.thumb_size);
+            view.setText(Long.toString(c.getLong(5)));
+            view = (TextView)findViewById(R.id.width);
+            view.setText(Long.toString(c.getLong(6)));
+            view = (TextView)findViewById(R.id.height);
+            view.setText(Long.toString(c.getLong(7)));
+            view = (TextView)findViewById(R.id.depth);
+            view.setText(Long.toString(c.getLong(8)));
+            view = (TextView)findViewById(R.id.sequence);
+            view.setText(Long.toString(c.getLong(9)));
+            view = (TextView)findViewById(R.id.created);
+            Date date = new Date(c.getLong(10) * 1000);
+            view.setText(date.toString());
+            view = (TextView)findViewById(R.id.modified);
+            date = new Date(c.getLong(11) * 1000);
+            view.setText(date.toString());
+            view = (TextView)findViewById(R.id.keywords);
+            view.setText(c.getString(12));
+            byte[] thumbnail = c.getBlob(13);
+            if (thumbnail != null) {
+                ImageView thumbView = (ImageView)findViewById(R.id.thumbnail);
+                Bitmap bitmap = BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length);
+                if (bitmap != null) {
+                    thumbView.setImageBitmap(bitmap);
+                }
+            }
+            view = (TextView)findViewById(R.id.format);
+            view.setText(Long.toHexString(c.getLong(14)).toUpperCase());
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.object_menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        MenuItem item = menu.findItem(R.id.save);
+        item.setEnabled(true);
+        item = menu.findItem(R.id.delete);
+        item.setEnabled(true);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.save:
+                save();
+                return true;
+            case R.id.delete:
+                delete();
+                return true;
+        }
+        return false;
+    }
+
+    private static String getTimestamp() {
+        Calendar c = Calendar.getInstance();
+        c.setTimeInMillis(System.currentTimeMillis());
+        return String.format("%tY-%tm-%td-%tH-%tM-%tS", c, c, c, c, c, c);
+    }
+
+    private void save() {
+        boolean success = false;
+        Uri uri = Mtp.Object.getContentUri(mDeviceID, mObjectID);
+        File destFile = null;
+        ParcelFileDescriptor pfd = null;
+        FileInputStream fis = null;
+        FileOutputStream fos = null;
+
+        try {
+            pfd = getContentResolver().openFileDescriptor(uri, "r");
+            Log.d(TAG, "save got pfd " + pfd);
+            if (pfd != null) {
+                fis = new FileInputStream(pfd.getFileDescriptor());
+                Log.d(TAG, "save got fis " + fis);
+                File destDir = Environment.getExternalStoragePublicDirectory(
+                        Environment.DIRECTORY_DCIM);
+                destDir.mkdirs();
+                destFile = new File(destDir, "CameraBrowser-" + getTimestamp() + ".jpeg");
+
+
+                Log.d(TAG, "save got destFile " + destFile);
+
+                if (destFile.exists()) {
+                    destFile.delete();
+                }
+                fos = new FileOutputStream(destFile);
+
+                byte[] buffer = new byte[65536];
+                int bytesRead;
+                while ((bytesRead = fis.read(buffer)) >= 0) {
+                    fos.write(buffer, 0, bytesRead);
+                }
+
+                // temporary workaround until we straighten out permissions in /data/media
+                FileUtils.setPermissions(destDir.getPath(), 0775, Process.myUid(), Process.SDCARD_RW_GID);
+                FileUtils.setPermissions(destFile.getPath(), 0664, Process.myUid(), Process.SDCARD_RW_GID);
+
+                success = true;
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Exception in ObjectView.save", e);
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (Exception e) {
+                }
+            }
+            if (fos != null) {
+                try {
+                    fos.close();
+                } catch (Exception e) {
+                }
+            }
+            if (pfd != null) {
+                try {
+                    pfd.close();
+                } catch (Exception e) {
+                }
+            }
+        }
+
+        if (success) {
+            Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+            intent.setData(Uri.fromFile(destFile));
+            sendBroadcast(intent);
+            Toast.makeText(this, R.string.object_saved_message, Toast.LENGTH_SHORT).show();
+        } else {
+            Toast.makeText(this, R.string.save_failed_message, Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    private void delete() {
+        Uri uri = Mtp.Object.getContentUri(mDeviceID, mObjectID);
+
+        Log.d(TAG, "deleting " + uri);
+
+        int result = getContentResolver().delete(uri, null, null);
+        if (result > 0) {
+            Toast.makeText(this, R.string.object_deleted_message, Toast.LENGTH_SHORT).show();
+            finish();
+        } else {
+            Toast.makeText(this, R.string.delete_failed_message, Toast.LENGTH_SHORT).show();
+        }
+    }
+}
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
new file mode 100644
index 0000000..63e036e
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camerabrowser;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Mtp;
+import android.util.Log;
+import android.view.View;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+
+/**
+ * A list view displaying all storage units on a device.
+ */
+public class StorageBrowser extends ListActivity {
+
+    private static final String TAG = "StorageBrowser";
+
+    private ListAdapter mAdapter;
+    private int mDeviceID;
+
+    private static final String[] STORAGE_COLUMNS =
+        new String[] { Mtp.Storage._ID, Mtp.Storage.DESCRIPTION };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        mDeviceID = getIntent().getIntExtra("device", 0);
+        if (mDeviceID != 0) {
+            Cursor c = getContentResolver().query(Mtp.Storage.getContentUri(mDeviceID),
+                    STORAGE_COLUMNS, null, null, null);
+            Log.d(TAG, "query returned " + c);
+            startManagingCursor(c);
+
+            // Map Cursor columns to views defined in simple_list_item_1.xml
+            mAdapter = new SimpleCursorAdapter(this,
+                    android.R.layout.simple_list_item_1, c,
+                            new String[] { Mtp.Storage.DESCRIPTION },
+                            new int[] { android.R.id.text1, android.R.id.text2 });
+            setListAdapter(mAdapter);
+        }
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        Intent intent = new Intent(this, ObjectBrowser.class);
+        intent.putExtra("device", mDeviceID);
+        intent.putExtra("storage", mAdapter.getItemId(position));
+        startActivity(intent);
+    }
+}
diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java
new file mode 100644
index 0000000..c05b239
--- /dev/null
+++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/UsbReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camerabrowser;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.hardware.Usb;
+import android.net.Uri;
+import android.util.Log;
+
+public class UsbReceiver extends BroadcastReceiver
+{
+    private static final String TAG = "UsbReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.d(TAG, "onReceive " + intent);
+        if (Usb.ACTION_USB_CAMERA_ATTACHED.equals(intent.getAction())) {
+            Uri uri = intent.getData();
+            intent = new Intent(context, StorageBrowser.class);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            try {
+                // TODO - add a wrapper to Mtp.Device for this
+                int id = Integer.parseInt(uri.getPathSegments().get(1));
+                intent.putExtra("device", id);
+                context.startActivity(intent);
+            } catch (NumberFormatException e) {
+                Log.e(TAG, "bad device Uri " + uri);
+            }
+        }
+    }
+}
diff --git a/media/tests/mtp/Android.mk b/media/tests/mtp/Android.mk
new file mode 100644
index 0000000..a9074ed
--- /dev/null
+++ b/media/tests/mtp/Android.mk
@@ -0,0 +1,61 @@
+LOCAL_PATH:= $(call my-dir)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	mtp.cpp \
+	MtpFile.cpp \
+
+LOCAL_C_INCLUDES += \
+    frameworks/base/media/mtp \
+
+LOCAL_CFLAGS := -DMTP_HOST
+
+LOCAL_MODULE := mtp
+
+LOCAL_STATIC_LIBRARIES := libmtp libusbhost libutils libcutils
+
+include $(BUILD_EXECUTABLE)
+
+endif
+
+ifeq ($(HOST_OS),linux)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	mtp.cpp \
+	MtpFile.cpp \
+	../../../libs/utils/RefBase.cpp \
+	../../../libs/utils/SharedBuffer.cpp \
+	../../../libs/utils/Threads.cpp \
+	../../../libs/utils/VectorImpl.cpp \
+
+LOCAL_C_INCLUDES += \
+    frameworks/base/media/mtp \
+
+LOCAL_CFLAGS := -DMTP_HOST -g -O0
+
+have_readline := $(wildcard /usr/include/readline/readline.h)
+have_history := $(wildcard /usr/lib/libhistory*)
+ifneq ($(strip $(have_readline)),)
+LOCAL_CFLAGS += -DHAVE_READLINE=1
+endif
+
+LOCAL_LDLIBS += -lpthread
+ifneq ($(strip $(have_readline)),)
+LOCAL_LDLIBS += -lreadline -lncurses
+endif
+ifneq ($(strip $(have_history)),)
+LOCAL_LDLIBS += -lhistory
+endif
+
+LOCAL_MODULE := mtp
+
+LOCAL_STATIC_LIBRARIES := libmtp libusbhost libcutils
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif
diff --git a/media/tests/mtp/MtpFile.cpp b/media/tests/mtp/MtpFile.cpp
new file mode 100644
index 0000000..00d328e
--- /dev/null
+++ b/media/tests/mtp/MtpFile.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MtpClient.h"
+#include "MtpDevice.h"
+#include "MtpDeviceInfo.h"
+#include "MtpObjectInfo.h"
+#include "MtpStorage.h"
+#include "MtpUtils.h"
+
+#include "MtpFile.h"
+
+namespace android {
+
+MtpClient* MtpFile::sClient = NULL;
+
+MtpFile::MtpFile(MtpDevice* device)
+    :   mDevice(device),
+        mStorage(0),
+        mHandle(0)
+{
+}
+
+MtpFile::MtpFile(MtpDevice* device, MtpStorageID storage)
+    :   mDevice(device),
+        mStorage(storage),
+        mHandle(0)
+{
+}
+
+MtpFile::MtpFile(MtpDevice* device, MtpStorageID storage, MtpObjectHandle handle)
+    :   mDevice(device),
+        mStorage(storage),
+        mHandle(handle)
+{
+}
+
+MtpFile::MtpFile(MtpFile* file)
+    :   mDevice(file->mDevice),
+        mStorage(file->mStorage),
+        mHandle(file->mHandle)
+{
+}
+
+MtpFile::~MtpFile() {
+}
+
+void MtpFile::print() {
+    if (mHandle) {
+
+    } else if (mStorage) {
+        printf("%x\n", mStorage);
+    } else {
+        int id = mDevice->getID();
+        MtpDeviceInfo* info = mDevice->getDeviceInfo();
+        if (info)
+            printf("%d\t%s %s %s\n", id, info->mManufacturer, info->mModel, info->mSerial);
+        else
+            printf("%d\t(no device info available)\n", id);
+        delete info;
+    }
+}
+
+MtpObjectInfo* MtpFile::getObjectInfo() {
+    return mDevice->getObjectInfo(mHandle);
+}
+
+void MtpFile::list() {
+    if (mStorage) {
+        MtpObjectHandleList* handles = mDevice->getObjectHandles(mStorage, 0,
+                                                (mHandle ? mHandle : -1));
+        if (handles) {
+            for (int i = 0; i < handles->size(); i++) {
+                MtpObjectHandle handle = (*handles)[i];
+                MtpObjectInfo* info = mDevice->getObjectInfo(handle);
+                if (info) {
+                    char modified[100];
+                    struct tm tm;
+
+                    gmtime_r(&info->mDateModified, &tm);
+                    strftime(modified, sizeof(modified), "%a %b %e %H:%M:%S GMT %Y", &tm);
+                    printf("%s Handle: %d Format: %04X Size: %d Modified: %s\n",
+                            info->mName, handle, info->mFormat, info->mCompressedSize, modified);
+                    delete info;
+                }
+            }
+            delete handles;
+        }
+    } else {
+        // list storage units for device
+        MtpStorageIDList* storageList = mDevice->getStorageIDs();
+        for (int i = 0; i < storageList->size(); i++) {
+            MtpStorageID storageID = (*storageList)[i];
+            printf("%x\n", storageID);
+        }
+    }
+}
+
+void MtpFile::init(MtpClient* client) {
+    sClient = client;
+}
+
+MtpFile* MtpFile::parsePath(MtpFile* base, char* path) {
+    MtpDevice* device = NULL;
+    MtpStorageID storage = 0;
+    MtpObjectHandle handle = 0;
+
+    if (path[0] != '/' && base) {
+        device = base->mDevice;
+        storage = base->mStorage;
+        handle = base->mHandle;
+    }
+
+    // parse an absolute path
+    if (path[0] == '/')
+        path++;
+    char* tok = strtok(path, "/");
+    while (tok) {
+        if (storage) {
+            // find child of current handle
+            MtpObjectHandleList* handles = device->getObjectHandles(storage, 0,
+                                                    (handle ? handle : -1));
+            MtpObjectHandle childHandle = 0;
+
+            if (handles) {
+                for (int i = 0; i < handles->size() && !childHandle; i++) {
+                    MtpObjectHandle handle = (*handles)[i];
+                    MtpObjectInfo* info = device->getObjectInfo(handle);
+                    if (info && !strcmp(tok, info->mName))
+                        childHandle = handle;
+                    delete info;
+                }
+                delete handles;
+            }
+            if (childHandle)
+                handle = childHandle;
+            else
+                return NULL;
+        } else if (device) {
+            unsigned int id;
+            // find storage for the device
+            if (sscanf(tok, "%x", &id) == 1) {
+                MtpStorageIDList* storageList = device->getStorageIDs();
+                bool found = false;
+                for (int i = 0; i < storageList->size(); i++) {
+                    if ((*storageList)[i] == id) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (found)
+                    storage = id;
+                else
+                    return NULL;
+            }
+        } else {
+            // find device
+            unsigned int id;
+            if (sscanf(tok, "%d", &id) == 1)
+                device = sClient->getDevice(id);
+            if (!device)
+                return NULL;
+        }
+
+        tok = strtok(NULL, "/");
+    }
+
+    if (device)
+        return new MtpFile(device, storage, handle);
+    else
+        return NULL;
+}
+
+}
diff --git a/media/tests/mtp/MtpFile.h b/media/tests/mtp/MtpFile.h
new file mode 100644
index 0000000..ab8762b
--- /dev/null
+++ b/media/tests/mtp/MtpFile.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_FILE_H
+#define _MTP_FILE_H
+
+#include "MtpTypes.h"
+
+namespace android {
+
+class MtpClient;
+class MtpDevice;
+class MtpObjectInfo;
+
+// File-like abstraction for the interactive shell.
+// This can be used to represent an MTP device, storage unit or object
+// (either file or association).
+class MtpFile {
+private:
+    MtpDevice*          mDevice;
+    MtpStorageID        mStorage;
+    MtpObjectHandle     mHandle;
+    static MtpClient*   sClient;
+
+public:
+    MtpFile(MtpDevice* device);
+    MtpFile(MtpDevice* device, MtpStorageID storage);
+    MtpFile(MtpDevice* device, MtpStorageID storage, MtpObjectHandle handle);
+    MtpFile(MtpFile* file);
+    virtual ~MtpFile();
+
+    MtpObjectInfo* getObjectInfo();
+    void print();
+    void list();
+
+    inline MtpDevice* getDevice() const { return mDevice; }
+
+    static void init(MtpClient* client);
+    static MtpFile* parsePath(MtpFile* base, char* path);
+};
+
+}
+
+#endif // _MTP_DIRECTORY_H
diff --git a/media/tests/mtp/mtp.cpp b/media/tests/mtp/mtp.cpp
new file mode 100644
index 0000000..3202cae
--- /dev/null
+++ b/media/tests/mtp/mtp.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#if HAVE_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#include "MtpClient.h"
+#include "MtpDevice.h"
+#include "MtpObjectInfo.h"
+
+#include "MtpFile.h"
+
+#define PROMPT  "mtp> "
+
+using namespace android;
+
+static MtpClient* sClient = NULL;
+
+// current working directory information for interactive shell
+static MtpFile* sCurrentDirectory = NULL;
+
+static MtpFile* parse_path(char* path) {
+    return MtpFile::parsePath(sCurrentDirectory, path);
+}
+
+class MyClient : public MtpClient {
+private:
+    virtual void deviceAdded(MtpDevice *device) {
+    }
+
+    virtual void deviceRemoved(MtpDevice *device) {
+    }
+
+public:
+};
+
+static void init() {
+    sClient = new MyClient;
+    sClient->start();
+    MtpFile::init(sClient);
+}
+
+static int set_cwd(int argc, char* argv[]) {
+    if (argc != 1) {
+        fprintf(stderr, "cd should have one argument\n");
+        return -1;
+    }
+    if (!strcmp(argv[0], "/")) {
+        delete sCurrentDirectory;
+        sCurrentDirectory = NULL;
+    }
+    else {
+        MtpFile* file = parse_path(argv[0]);
+        if (file) {
+            delete sCurrentDirectory;
+            sCurrentDirectory = file;
+        } else {
+            fprintf(stderr, "could not find %s\n", argv[0]);
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static void list_devices() {
+    // TODO - need to make sure the list will not change while iterating
+    MtpDeviceList& devices = sClient->getDeviceList();
+    for (int i = 0; i < devices.size(); i++) {
+        MtpDevice* device = devices[i];
+        MtpFile* file = new MtpFile(device);
+        file->print();
+        delete file;
+    }
+}
+
+static int list(int argc, char* argv[]) {
+    if (argc == 0) {
+        // list cwd
+        if (sCurrentDirectory) {
+            sCurrentDirectory->list();
+        } else {
+            list_devices();
+        }
+    }
+
+    for (int i = 0; i < argc; i++) {
+        char* path = argv[i];
+        if (!strcmp(path, "/")) {
+            list_devices();
+        } else {
+            MtpFile* file = parse_path(path);
+            if (!file) {
+                fprintf(stderr, "could not find %s\n", path);
+                return -1;
+            }
+            file->list();
+        }
+    }
+
+    return 0;
+}
+
+static int get_file(int argc, char* argv[]) {
+    int ret = -1;
+    int srcFD = -1;
+    int destFD = -1;
+    MtpFile* srcFile = NULL;
+    MtpObjectInfo* info = NULL;
+    char* dest;
+
+    if (argc < 1) {
+        fprintf(stderr, "not enough arguments\n");
+        return -1;
+    } else if (argc > 2) {
+        fprintf(stderr, "too many arguments\n");
+        return -1;
+    }
+
+    // find source object
+    char* src = argv[0];
+    srcFile = parse_path(src);
+    if (!srcFile) {
+        fprintf(stderr, "could not find %s\n", src);
+        return -1;
+    }
+    info = srcFile->getObjectInfo();
+    if (!info) {
+        fprintf(stderr, "could not find object info for %s\n", src);
+        goto fail;
+    }
+    if (info->mFormat == MTP_FORMAT_ASSOCIATION) {
+        fprintf(stderr, "copying directories not implemented yet\n");
+        goto fail;
+    }
+
+    dest = (argc > 1 ? argv[1] : info->mName);
+    destFD = open(dest, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+    if (destFD < 0) {
+        fprintf(stderr, "could not create %s\n", dest);
+        goto fail;
+    }
+    srcFD = srcFile->getDevice()->readObject(info->mHandle, info->mCompressedSize);
+    if (srcFD < 0)
+        goto fail;
+
+    char buffer[65536];
+    while (1) {
+        int count = read(srcFD, buffer, sizeof(buffer));
+        if (count <= 0)
+            break;
+        write(destFD, buffer, count);
+    }
+    // FIXME - error checking and reporting
+    ret = 0;
+
+fail:
+    delete srcFile;
+    delete info;
+    if (srcFD >= 0)
+        close(srcFD);
+    if (destFD >= 0)
+        close(destFD);
+    return ret;
+}
+
+static int put_file(int argc, char* argv[]) {
+    int ret = -1;
+    int srcFD = -1;
+    MtpFile* destFile = NULL;
+    MtpObjectInfo* srcInfo = NULL;
+    MtpObjectInfo* destInfo = NULL;
+    MtpObjectHandle handle;
+    struct stat statbuf;
+    const char* lastSlash;
+
+    if (argc < 1) {
+        fprintf(stderr, "not enough arguments\n");
+        return -1;
+    } else if (argc > 2) {
+        fprintf(stderr, "too many arguments\n");
+        return -1;
+    }
+    const char* src = argv[0];
+    srcFD = open(src, O_RDONLY);
+    if (srcFD < 0) {
+        fprintf(stderr, "could not open %s\n", src);
+        goto fail;
+    }
+    if (argc == 2) {
+         char* dest = argv[1];
+        destFile = parse_path(dest);
+        if (!destFile) {
+            fprintf(stderr, "could not find %s\n", dest);
+            goto fail;
+        }
+    } else {
+        if (!sCurrentDirectory) {
+            fprintf(stderr, "current working directory not set\n");
+            goto fail;
+        }
+        destFile = new MtpFile(sCurrentDirectory);
+    }
+
+    destInfo = destFile->getObjectInfo();
+    if (!destInfo) {
+        fprintf(stderr, "could not find object info destination directory\n");
+        goto fail;
+    }
+    if (destInfo->mFormat != MTP_FORMAT_ASSOCIATION) {
+        fprintf(stderr, "destination not a directory\n");
+        goto fail;
+    }
+
+    if (fstat(srcFD, &statbuf))
+        goto fail;
+
+    srcInfo = new MtpObjectInfo(0);
+    srcInfo->mStorageID = destInfo->mStorageID;
+    srcInfo->mFormat = MTP_FORMAT_EXIF_JPEG;  // FIXME
+    srcInfo->mCompressedSize = statbuf.st_size;
+    srcInfo->mParent = destInfo->mHandle;
+    lastSlash = strrchr(src, '/');
+    srcInfo->mName = strdup(lastSlash ? lastSlash + 1 : src);
+    srcInfo->mDateModified = statbuf.st_mtime;
+    handle = destFile->getDevice()->sendObjectInfo(srcInfo);
+    if (handle <= 0) {
+        printf("sendObjectInfo returned %04X\n", handle);
+        goto fail;
+    }
+    if (destFile->getDevice()->sendObject(srcInfo, srcFD))
+        ret = 0;
+
+fail:
+    delete destFile;
+    delete srcInfo;
+    delete destInfo;
+    if (srcFD >= 0)
+        close(srcFD);
+    printf("returning %d\n", ret);
+    return ret;
+}
+
+typedef int (* command_func)(int argc, char* argv[]);
+
+struct command_table_entry {
+    const char* name;
+    command_func func;
+};
+
+const command_table_entry command_list[] = {
+    {   "cd",       set_cwd         },
+    {   "ls",       list            },
+    {   "get",      get_file        },
+    {   "put",      put_file        },
+    {   NULL,       NULL            },
+};
+
+
+static int do_command(int argc, char* argv[]) {
+    const command_table_entry* command = command_list;
+    const char* name = *argv++;
+    argc--;
+
+    while (command->name) {
+        if (!strcmp(command->name, name))
+            return command->func(argc, argv);
+        else
+            command++;
+    }
+    fprintf(stderr, "unknown command %s\n", name);
+    return -1;
+}
+
+static int shell() {
+    int argc;
+    int result = 0;
+#define MAX_ARGS    100
+    char* argv[MAX_ARGS];
+
+#if HAVE_READLINE
+    using_history();
+#endif
+
+    while (1) {
+#if HAVE_READLINE
+        char* line = readline(PROMPT);
+        if (!line) {
+            printf("\n");
+            exit(0);
+        }
+#else
+        char    buffer[1000];
+        printf("%s", PROMPT);
+        char* line = NULL;
+        size_t length = 0;
+
+        buffer[0] = 0;
+        fgets(buffer, sizeof(buffer), stdin);
+        int count = strlen(buffer);
+        if (count > 0 && buffer[0] == (char)EOF) {
+            printf("\n");
+            exit(0);
+        }
+        if (count > 0 && line[count - 1] == '\n')
+            line[count - 1] == 0;
+#endif
+        char* tok = strtok(line, " \t\n\r");
+        if (!tok)
+            continue;
+        if (!strcmp(tok, "quit") || !strcmp(tok, "exit")) {
+            exit(0);
+        }
+#if HAVE_READLINE
+        add_history(line);
+#endif
+        argc = 0;
+        while (tok) {
+            if (argc + 1 == MAX_ARGS) {
+                fprintf(stderr, "too many arguments\n");
+                result = -1;
+                goto bottom_of_loop;
+            }
+
+            argv[argc++] = strdup(tok);
+            tok = strtok(NULL, " \t\n\r");
+        }
+
+        result = do_command(argc, argv);
+
+bottom_of_loop:
+        for (int i = 0; i < argc; i++)
+            free(argv[i]);
+        free(line);
+    }
+
+    return result;
+}
+
+int main(int argc, char* argv[]) {
+    init();
+
+    if (argc == 1)
+        return shell();
+    else
+        return do_command(argc - 1, argv + 1);
+}
diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp
index b3cc8b6..53308be 100644
--- a/media/tests/players/invoke_mock_media_player.cpp
+++ b/media/tests/players/invoke_mock_media_player.cpp
@@ -26,6 +26,7 @@
 
 using android::INVALID_OPERATION;
 using android::ISurface;
+using android::Surface;
 using android::MediaPlayerBase;
 using android::OK;
 using android::Parcel;
@@ -67,7 +68,8 @@
     }
 
     virtual status_t    setDataSource(int fd, int64_t offset, int64_t length) {return OK;}
-    virtual status_t    setVideoSurface(const sp<ISurface>& surface) {return OK;}
+    virtual status_t    setVideoISurface(const sp<ISurface>& surface) {return OK;}
+    virtual status_t    setVideoSurface(const sp<Surface>& surface) {return OK;}
     virtual status_t    prepare() {return OK;}
     virtual status_t    prepareAsync() {return OK;}
     virtual status_t    start() {return OK;}
diff --git a/native/include/android/input.h b/native/include/android/input.h
index 7df13c3..861ec8b 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -93,7 +93,37 @@
     AMETA_SHIFT_RIGHT_ON = 0x80,
 
     /* This mask is used to check whether the SYM meta key is pressed. */
-    AMETA_SYM_ON = 0x04
+    AMETA_SYM_ON = 0x04,
+
+    /* This mask is used to check whether the FUNCTION meta key is pressed. */
+    AMETA_FUNCTION_ON = 0x08,
+
+    /* This mask is used to check whether one of the CTRL meta keys is pressed. */
+    AMETA_CTRL_ON = 0x1000,
+
+    /* This mask is used to check whether the left CTRL meta key is pressed. */
+    AMETA_CTRL_LEFT_ON = 0x2000,
+
+    /* This mask is used to check whether the right CTRL meta key is pressed. */
+    AMETA_CTRL_RIGHT_ON = 0x4000,
+
+    /* This mask is used to check whether one of the META meta keys is pressed. */
+    AMETA_META_ON = 0x10000,
+
+    /* This mask is used to check whether the left META meta key is pressed. */
+    AMETA_META_LEFT_ON = 0x20000,
+
+    /* This mask is used to check whether the right META meta key is pressed. */
+    AMETA_META_RIGHT_ON = 0x40000,
+
+    /* This mask is used to check whether the CAPS LOCK meta key is on. */
+    AMETA_CAPS_LOCK_ON = 0x100000,
+
+    /* This mask is used to check whether the NUM LOCK meta key is on. */
+    AMETA_NUM_LOCK_ON = 0x200000,
+
+    /* This mask is used to check whether the SCROLL LOCK meta key is on. */
+    AMETA_SCROLL_LOCK_ON = 0x400000,
 };
 
 /*
diff --git a/native/include/android/keycodes.h b/native/include/android/keycodes.h
index 496eccc0..11142537 100644
--- a/native/include/android/keycodes.h
+++ b/native/include/android/keycodes.h
@@ -154,6 +154,60 @@
     AKEYCODE_BUTTON_START    = 108,
     AKEYCODE_BUTTON_SELECT   = 109,
     AKEYCODE_BUTTON_MODE     = 110,
+    AKEYCODE_ESCAPE          = 111,
+    AKEYCODE_FORWARD_DEL     = 112,
+    AKEYCODE_CTRL_LEFT       = 113,
+    AKEYCODE_CTRL_RIGHT      = 114,
+    AKEYCODE_CAPS_LOCK       = 115,
+    AKEYCODE_SCROLL_LOCK     = 116,
+    AKEYCODE_META_LEFT       = 117,
+    AKEYCODE_META_RIGHT      = 118,
+    AKEYCODE_FUNCTION        = 119,
+    AKEYCODE_SYSRQ           = 120,
+    AKEYCODE_BREAK           = 121,
+    AKEYCODE_MOVE_HOME       = 122,
+    AKEYCODE_MOVE_END        = 123,
+    AKEYCODE_INSERT          = 124,
+    AKEYCODE_FORWARD         = 125,
+    AKEYCODE_MEDIA_PLAY      = 126,
+    AKEYCODE_MEDIA_PAUSE     = 127,
+    AKEYCODE_MEDIA_CLOSE     = 128,
+    AKEYCODE_MEDIA_EJECT     = 129,
+    AKEYCODE_MEDIA_RECORD    = 130,
+    AKEYCODE_F1              = 131,
+    AKEYCODE_F2              = 132,
+    AKEYCODE_F3              = 133,
+    AKEYCODE_F4              = 134,
+    AKEYCODE_F5              = 135,
+    AKEYCODE_F6              = 136,
+    AKEYCODE_F7              = 137,
+    AKEYCODE_F8              = 138,
+    AKEYCODE_F9              = 139,
+    AKEYCODE_F10             = 140,
+    AKEYCODE_F11             = 141,
+    AKEYCODE_F12             = 142,
+    AKEYCODE_NUM_LOCK        = 143,
+    AKEYCODE_NUMPAD_0        = 144,
+    AKEYCODE_NUMPAD_1        = 145,
+    AKEYCODE_NUMPAD_2        = 146,
+    AKEYCODE_NUMPAD_3        = 147,
+    AKEYCODE_NUMPAD_4        = 148,
+    AKEYCODE_NUMPAD_5        = 149,
+    AKEYCODE_NUMPAD_6        = 150,
+    AKEYCODE_NUMPAD_7        = 151,
+    AKEYCODE_NUMPAD_8        = 152,
+    AKEYCODE_NUMPAD_9        = 153,
+    AKEYCODE_NUMPAD_DIVIDE   = 154,
+    AKEYCODE_NUMPAD_MULTIPLY = 155,
+    AKEYCODE_NUMPAD_SUBTRACT = 156,
+    AKEYCODE_NUMPAD_ADD      = 157,
+    AKEYCODE_NUMPAD_DOT      = 158,
+    AKEYCODE_NUMPAD_COMMA    = 159,
+    AKEYCODE_NUMPAD_ENTER    = 160,
+    AKEYCODE_NUMPAD_EQUALS   = 161,
+    AKEYCODE_NUMPAD_LEFT_PAREN = 162,
+    AKEYCODE_NUMPAD_RIGHT_PAREN = 163,
+    AKEYCODE_VOLUME_MUTE     = 164,
 
     // NOTE: If you add a new keycode here you must also add it to several other files.
     //       Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/native/include/android/tts.h b/native/include/android/tts.h
new file mode 100644
index 0000000..fb15108
--- /dev/null
+++ b/native/include/android/tts.h
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+#ifndef ANDROID_TTS_H
+#define ANDROID_TTS_H 
+
+// This header defines the interface used by the Android platform
+// to access Text-To-Speech functionality in shared libraries that implement
+// speech synthesis and the management of resources associated with the
+// synthesis.
+
+// The shared library must contain a function named "android_getTtsEngine"
+// that returns an 'android_tts_engine_t' instance.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ANDROID_TTS_ENGINE_PROPERTY_CONFIG "engineConfig"
+#define ANDROID_TTS_ENGINE_PROPERTY_PITCH  "pitch"
+#define ANDROID_TTS_ENGINE_PROPERTY_RATE   "rate"
+#define ANDROID_TTS_ENGINE_PROPERTY_VOLUME "volume"
+
+typedef enum {
+    ANDROID_TTS_SUCCESS                 = 0,
+    ANDROID_TTS_FAILURE                 = -1,
+    ANDROID_TTS_FEATURE_UNSUPPORTED     = -2,
+    ANDROID_TTS_VALUE_INVALID           = -3,
+    ANDROID_TTS_PROPERTY_UNSUPPORTED    = -4,
+    ANDROID_TTS_PROPERTY_SIZE_TOO_SMALL = -5,
+    ANDROID_TTS_MISSING_RESOURCES       = -6
+} android_tts_result_t;
+
+typedef enum {
+    ANDROID_TTS_LANG_COUNTRY_VAR_AVAILABLE = 2,
+    ANDROID_TTS_LANG_COUNTRY_AVAILABLE    = 1,
+    ANDROID_TTS_LANG_AVAILABLE            = 0,
+    ANDROID_TTS_LANG_MISSING_DATA         = -1,
+    ANDROID_TTS_LANG_NOT_SUPPORTED        = -2
+} android_tts_support_result_t;
+
+typedef enum {
+    ANDROID_TTS_SYNTH_DONE              = 0,
+    ANDROID_TTS_SYNTH_PENDING           = 1
+} android_tts_synth_status_t;
+
+typedef enum {
+    ANDROID_TTS_CALLBACK_HALT           = 0,
+    ANDROID_TTS_CALLBACK_CONTINUE       = 1
+} android_tts_callback_status_t;
+
+// Supported audio formats
+typedef enum {
+    ANDROID_TTS_AUDIO_FORMAT_INVALID    = -1,
+    ANDROID_TTS_AUDIO_FORMAT_DEFAULT    = 0,
+    ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT = 1,
+    ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT  = 2,
+} android_tts_audio_format_t;
+
+
+/* An android_tts_engine_t object can be anything, but must have,
+ * as its first field, a pointer to a table of functions.
+ *
+ * See the full definition of struct android_tts_engine_t_funcs_t
+ * below for details.
+ */
+typedef struct android_tts_engine_funcs_t  android_tts_engine_funcs_t;
+
+typedef struct {
+    android_tts_engine_funcs_t *funcs;
+} android_tts_engine_t;
+
+/* This function must be located in the TTS Engine shared library
+ * and must return the address of an android_tts_engine_t library.
+ */
+extern android_tts_engine_t *android_getTtsEngine();
+
+/* Including the old version for legacy support (Froyo compatibility).
+ * This should return the same thing as android_getTtsEngine.
+ */
+extern "C" android_tts_engine_t *getTtsEngine();
+
+// A callback type used to notify the framework of new synthetized
+// audio samples, status will be SYNTH_DONE for the last sample of
+// the last request, of SYNTH_PENDING otherwise.
+//
+// This is passed by the framework to the engine through the
+// 'engine_init' function (see below).
+//
+// The callback for synthesis completed takes:
+// @param [inout] void *&       - The userdata pointer set in the original
+//                                 synth call
+// @param [in]    uint32_t      - Track sampling rate in Hz
+// @param [in]    uint32_t      - The audio format
+// @param [in]    int           - The number of channels
+// @param [inout] int8_t *&     - A buffer of audio data only valid during the
+//                                execution of the callback
+// @param [inout] size_t  &     - The size of the buffer
+// @param [in] tts_synth_status - indicate whether the synthesis is done, or
+//                                 if more data is to be synthesized.
+// @return TTS_CALLBACK_HALT to indicate the synthesis must stop,
+//         TTS_CALLBACK_CONTINUE to indicate the synthesis must continue if
+//            there is more data to produce.
+typedef android_tts_callback_status_t (*android_tts_synth_cb_t)
+            (void **pUserData,
+             uint32_t trackSamplingHz,
+             android_tts_audio_format_t audioFormat,
+             int channelCount,
+             int8_t **pAudioBuffer,
+             size_t *pBufferSize,
+             android_tts_synth_status_t status);
+
+
+// The table of function pointers that the android_tts_engine_t must point to.
+// Note that each of these functions will take a handle to the engine itself
+// as their first parameter.
+//
+
+struct android_tts_engine_funcs_t {
+    // reserved fields, ignored by the framework
+    // they must be placed here to ensure binary compatibility
+    // of legacy binary plugins.
+    void *reserved[2];
+
+    // Initialize the TTS engine and returns whether initialization succeeded.
+    // @param synthDoneCBPtr synthesis callback function pointer
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    android_tts_result_t (*init)
+            (void *engine,
+             android_tts_synth_cb_t synthDonePtr,
+             const char *engineConfig);
+
+    // Shut down the TTS engine and releases all associated resources.
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    android_tts_result_t (*shutdown)
+            (void *engine);
+
+    // Interrupt synthesis and flushes any synthesized data that hasn't been
+    // output yet. This will block until callbacks underway are completed.
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    android_tts_result_t (*stop)
+            (void *engine);
+
+    // Returns the level of support for the language, country and variant.
+    // @return TTS_LANG_COUNTRY_VAR_AVAILABLE if the language, country and variant are supported,
+    //            and the corresponding resources are correctly installed
+    //         TTS_LANG_COUNTRY_AVAILABLE if the language and country are supported and the
+    //             corresponding resources are correctly installed, but there is no match for
+    //             the specified variant
+    //         TTS_LANG_AVAILABLE if the language is supported and the
+    //             corresponding resources are correctly installed, but there is no match for
+    //             the specified country and variant
+    //         TTS_LANG_MISSING_DATA if the required resources to provide any level of support
+    //             for the language are not correctly installed
+    //         TTS_LANG_NOT_SUPPORTED if the language is not supported by the TTS engine.
+    android_tts_support_result_t (*isLanguageAvailable)
+            (void *engine,
+             const char *lang,
+             const char *country,
+             const char *variant);
+
+    // Load the resources associated with the specified language. The loaded
+    // language will only be used once a call to setLanguage() with the same
+    // language value is issued. Language and country values are coded according to the ISO three
+    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
+    // instance. The variant value is encoded as the variant string retrieved from a
+    // java.util.Locale instance built with that variant data.
+    // @param lang pointer to the ISO three letter code for the language
+    // @param country pointer to the ISO three letter code for the country
+    // @param variant pointer to the variant code
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    android_tts_result_t (*loadLanguage)
+            (void *engine,
+             const char *lang,
+             const char *country,
+             const char *variant);
+
+    // Load the resources associated with the specified language, country and Locale variant.
+    // The loaded language will only be used once a call to setLanguageFromLocale() with the same
+    // language value is issued. Language and country values are coded according to the ISO three
+    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
+    // instance. The variant value is encoded as the variant string retrieved from a
+    // java.util.Locale instance built with that variant data.
+    // @param lang pointer to the ISO three letter code for the language
+    // @param country pointer to the ISO three letter code for the country
+    // @param variant pointer to the variant code
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    android_tts_result_t (*setLanguage)
+            (void *engine,
+             const char *lang,
+             const char *country,
+             const char *variant);
+
+    // Retrieve the currently set language, country and variant, or empty strings if none of
+    // parameters have been set. Language and country are represented by their 3-letter ISO code
+    // @param[out]   pointer to the retrieved 3-letter code language value
+    // @param[out]   pointer to the retrieved 3-letter code country value
+    // @param[out]   pointer to the retrieved variant value
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    android_tts_result_t (*getLanguage)
+            (void *engine,
+             char *language,
+             char *country,
+             char *variant);
+
+    // Notifies the engine what audio parameters should be used for the synthesis.
+    // This is meant to be used as a hint, the engine implementation will set the output values
+    // to those of the synthesis format, based on a given hint.
+    // @param[inout] encoding in: the desired audio sample format
+    //                         out: the format used by the TTS engine
+    // @param[inout] rate in: the desired audio sample rate
+    //                         out: the sample rate used by the TTS engine
+    // @param[inout] channels in: the desired number of audio channels
+    //                         out: the number of channels used by the TTS engine
+    // @return TTS_SUCCESS, or TTS_FAILURE
+    android_tts_result_t (*setAudioFormat)
+            (void *engine,
+             android_tts_audio_format_t* pEncoding,
+             uint32_t* pRate,
+             int* pChannels);
+
+    // Set a property for the the TTS engine
+    // "size" is the maximum size of "value" for properties "property"
+    // @param property pointer to the property name
+    // @param value    pointer to the property value
+    // @param size     maximum size required to store this type of property
+    // @return         TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS, or TTS_FAILURE,
+    //                  or TTS_VALUE_INVALID
+    android_tts_result_t (*setProperty)
+            (void *engine,
+             const char *property,
+             const char *value,
+             const size_t size);
+
+    // Retrieve a property from the TTS engine
+    // @param        property pointer to the property name
+    // @param[out]   value    pointer to the retrieved language value
+    // @param[inout] iosize   in: stores the size available to store the
+    //                          property value.
+    //                        out: stores the size required to hold the language
+    //                          value if getLanguage() returned
+    //                          TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise
+    // @return TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS,
+    //         or TTS_PROPERTY_SIZE_TOO_SMALL
+    android_tts_result_t (*getProperty)
+            (void *engine,
+             const char *property,
+             char *value,
+             size_t *iosize);
+
+    // Synthesize the text.
+    // As the synthesis is performed, the engine invokes the callback to notify
+    // the TTS framework that it has filled the given buffer, and indicates how
+    // many bytes it wrote. The callback is called repeatedly until the engine
+    // has generated all the audio data corresponding to the text.
+    // Note about the format of the input: the text parameter may use the
+    // following elements
+    // and their respective attributes as defined in the SSML 1.0 specification:
+    //    * lang
+    //    * say-as:
+    //          o interpret-as
+    //    * phoneme
+    //    * voice:
+    //          o gender,
+    //          o age,
+    //          o variant,
+    //          o name
+    //    * emphasis
+    //    * break:
+    //          o strength,
+    //          o time
+    //    * prosody:
+    //          o pitch,
+    //          o contour,
+    //          o range,
+    //          o rate,
+    //          o duration,
+    //          o volume
+    //    * mark
+    // Differences between this text format and SSML are:
+    //    * full SSML documents are not supported
+    //    * namespaces are not supported
+    // Text is coded in UTF-8.
+    // @param text      the UTF-8 text to synthesize
+    // @param userdata  pointer to be returned when the call is invoked
+    // @param buffer    the location where the synthesized data must be written
+    // @param bufferSize the number of bytes that can be written in buffer
+    // @return          TTS_SUCCESS or TTS_FAILURE
+    android_tts_result_t (*synthesizeText)
+            (void *engine,
+             const char *text,
+             int8_t *buffer,
+             size_t bufferSize,
+             void *userdata);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ANDROID_TTS_H */
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index 41207f7..8f2f974 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -155,6 +155,8 @@
  *
  */
 public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+    private final static String TAG = "GLSurfaceView";
+    private final static boolean LOG_ATTACH_DETACH = false;
     private final static boolean LOG_THREADS = false;
     private final static boolean LOG_PAUSE_RESUME = false;
     private final static boolean LOG_SURFACE = false;
@@ -306,6 +308,7 @@
         if (mEGLWindowSurfaceFactory == null) {
             mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
         }
+        mRenderer = renderer;
         mGLThread = new GLThread(renderer);
         mGLThread.start();
     }
@@ -525,12 +528,42 @@
     /**
      * This method is used as part of the View class and is not normally
      * called or subclassed by clients of GLSurfaceView.
+     */
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (LOG_ATTACH_DETACH) {
+            Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
+        }
+        if (mDetached && (mRenderer != null)) {
+            int renderMode = RENDERMODE_CONTINUOUSLY;
+            if (mGLThread != null) {
+                renderMode = mGLThread.getRenderMode();
+            }
+            mGLThread = new GLThread(mRenderer);
+            if (renderMode != RENDERMODE_CONTINUOUSLY) {
+                mGLThread.setRenderMode(renderMode);
+            }
+            mGLThread.start();
+        }
+        mDetached = false;
+    }
+
+    /**
+     * This method is used as part of the View class and is not normally
+     * called or subclassed by clients of GLSurfaceView.
      * Must not be called before a renderer has been set.
      */
     @Override
     protected void onDetachedFromWindow() {
+        if (LOG_ATTACH_DETACH) {
+            Log.d(TAG, "onDetachedFromWindow");
+        }
+        if (mGLThread != null) {
+            mGLThread.requestExitAndWait();
+        }
+        mDetached = true;
         super.onDetachedFromWindow();
-        mGLThread.requestExitAndWait();
     }
 
     // ----------------------------------------------------------------------
@@ -1727,6 +1760,8 @@
     private boolean mSizeChanged = true;
 
     private GLThread mGLThread;
+    private Renderer mRenderer;
+    private boolean mDetached;
     private EGLConfigChooser mEGLConfigChooser;
     private EGLContextFactory mEGLContextFactory;
     private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 5c09dcc..7c496e7 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -546,7 +546,9 @@
     if (!dirtyRegion.isEmpty()) {
         dirtyRegion.andSelf(Rect(buffer->width, buffer->height));
         if (previousBuffer) {
-            const Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
+            // This was const Region copyBack, but that causes an
+            // internal compile error on simulator builds
+            /*const*/ Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
             if (!copyBack.isEmpty()) {
                 void* prevBits;
                 if (lock(previousBuffer, 
@@ -1980,7 +1982,7 @@
     if (egl_display_t::is_valid(dpy) == EGL_FALSE)
         return setError(EGL_BAD_DISPLAY, EGL_FALSE);
     // TODO: eglSwapInterval()
-    return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+    return EGL_TRUE;
 }
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index ae924cd..c8041fc 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -8,6 +8,7 @@
 
 LOCAL_SRC_FILES:= 	       \
 	EGL/egl.cpp 	       \
+	EGL/trace.cpp              \
 	EGL/getProcAddress.cpp.arm \
 	EGL/hooks.cpp 	       \
 	EGL/Loader.cpp 	       \
@@ -33,6 +34,7 @@
 LOCAL_CFLAGS += -DLOG_TAG=\"libEGL\"
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 LOCAL_CFLAGS += -fvisibility=hidden
+LOCAL_CFLAGS += -DEGL_TRACE=1
 
 ifeq ($(TARGET_BOARD_PLATFORM),msm7k)
 LOCAL_CFLAGS += -DADRENO130=1
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 2d1a278..f744b72 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -59,6 +59,8 @@
         "EGL_KHR_image "
         "EGL_KHR_image_base "
         "EGL_KHR_image_pixmap "
+        "EGL_KHR_gl_texture_2D_image "
+        "EGL_KHR_fence_sync "
         "EGL_ANDROID_image_native_buffer "
         "EGL_ANDROID_swap_rectangle "
         ;
@@ -243,9 +245,23 @@
     EGLImageKHR images[IMPL_NUM_IMPLEMENTATIONS];
 };
 
+struct egl_sync_t : public egl_object_t
+{
+    typedef egl_object_t::LocalRef<egl_sync_t, EGLSyncKHR> Ref;
+
+    egl_sync_t(EGLDisplay dpy, EGLContext context, EGLSyncKHR sync)
+        : dpy(dpy), context(context), sync(sync)
+    {
+    }
+    EGLDisplay dpy;
+    EGLContext context;
+    EGLSyncKHR sync;
+};
+
 typedef egl_surface_t::Ref  SurfaceRef;
 typedef egl_context_t::Ref  ContextRef;
 typedef egl_image_t::Ref    ImageRef;
+typedef egl_sync_t::Ref     SyncRef;
 
 struct tls_t
 {
@@ -269,6 +285,58 @@
 EGLAPI gl_hooks_t gHooksNoContext;
 EGLAPI pthread_key_t gGLWrapperKey = -1;
 
+#if EGL_TRACE
+
+EGLAPI pthread_key_t gGLTraceKey = -1;
+
+// ----------------------------------------------------------------------------
+
+static int gEGLTraceLevel;
+static int gEGLApplicationTraceLevel;
+extern EGLAPI gl_hooks_t gHooksTrace;
+
+static inline void setGlTraceThreadSpecific(gl_hooks_t const *value) {
+    pthread_setspecific(gGLTraceKey, value);
+}
+
+gl_hooks_t const* getGLTraceThreadSpecific() {
+    return static_cast<gl_hooks_t*>(pthread_getspecific(gGLTraceKey));
+}
+
+static void initEglTraceLevel() {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.egl.trace", value, "0");
+    int propertyLevel = atoi(value);
+    int applicationLevel = gEGLApplicationTraceLevel;
+    gEGLTraceLevel = propertyLevel > applicationLevel ? propertyLevel : applicationLevel;
+}
+
+static void setGLHooksThreadSpecific(gl_hooks_t const *value) {
+    if (gEGLTraceLevel > 0) {
+        setGlTraceThreadSpecific(value);
+        setGlThreadSpecific(&gHooksTrace);
+    } else {
+        setGlThreadSpecific(value);
+    }
+}
+
+/*
+ * Global entry point to allow applications to modify their own trace level.
+ * The effective trace level is the max of this level and the value of debug.egl.trace.
+ */
+extern "C"
+void setGLTraceLevel(int level) {
+    gEGLApplicationTraceLevel = level;
+}
+
+#else
+
+static inline void setGLHooksThreadSpecific(gl_hooks_t const *value) {
+    setGlThreadSpecific(value);
+}
+
+#endif
+
 // ----------------------------------------------------------------------------
 
 static __attribute__((noinline))
@@ -443,13 +511,17 @@
 #if !USE_FAST_TLS_KEY
     pthread_key_create(&gGLWrapperKey, NULL);
 #endif
+#if EGL_TRACE
+    pthread_key_create(&gGLTraceKey, NULL);
+    initEglTraceLevel();
+#endif
     uint32_t addr = (uint32_t)((void*)gl_no_context);
     android_memset32(
             (uint32_t*)(void*)&gHooksNoContext, 
             addr, 
             sizeof(gHooksNoContext));
 
-    setGlThreadSpecific(&gHooksNoContext);
+    setGLHooksThreadSpecific(&gHooksNoContext);
 }
 
 static pthread_once_t once_control = PTHREAD_ONCE_INIT;
@@ -483,6 +555,11 @@
     return egl_to_native_cast<egl_image_t>(image);
 }
 
+static inline
+egl_sync_t* get_sync(EGLSyncKHR sync) {
+    return egl_to_native_cast<egl_sync_t>(sync);
+}
+
 static egl_connection_t* validate_display_config(
         EGLDisplay dpy, EGLConfig config,
         egl_display_t const*& dp)
@@ -656,9 +733,17 @@
         dp->refs++;
         return EGL_TRUE;
     }
-    
-    setGlThreadSpecific(&gHooksNoContext);
-    
+
+#if EGL_TRACE
+
+    // Called both at early_init time and at this time. (Early_init is pre-zygote, so
+    // the information from that call may be stale.)
+    initEglTraceLevel();
+
+#endif
+
+    setGLHooksThreadSpecific(&gHooksNoContext);
+
     // initialize each EGL and
     // build our own extension string first, based on the extension we know
     // and the extension supported by our client implementation
@@ -1217,11 +1302,11 @@
 
         // cur_c has to be valid here (but could be terminated)
         if (ctx != EGL_NO_CONTEXT) {
-            setGlThreadSpecific(c->cnx->hooks[c->version]);
+            setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
             setContext(ctx);
             _c.acquire();
         } else {
-            setGlThreadSpecific(&gHooksNoContext);
+            setGLHooksThreadSpecific(&gHooksNoContext);
             setContext(EGL_NO_CONTEXT);
         }
         _cur_c.release();
@@ -1413,6 +1498,9 @@
                     // Extensions are independent of the bound context
                     cnx->hooks[GLESv1_INDEX]->ext.extensions[slot] =
                     cnx->hooks[GLESv2_INDEX]->ext.extensions[slot] =
+#if EGL_TRACE
+                    gHooksTrace.ext.extensions[slot] =
+#endif
                             cnx->egl.eglGetProcAddress(procname);
                 }
             }
@@ -1790,6 +1878,111 @@
      return EGL_TRUE;
 }
 
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 5
+// ----------------------------------------------------------------------------
+
+
+EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)
+{
+    EGLContext ctx = eglGetCurrentContext();
+    ContextRef _c(ctx);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_NO_SYNC_KHR);
+    if (!validate_display_context(dpy, ctx))
+        return EGL_NO_SYNC_KHR;
+    egl_display_t const * const dp = get_display(dpy);
+    egl_context_t * const c = get_context(ctx);
+    EGLSyncKHR result = EGL_NO_SYNC_KHR;
+    if (c->cnx->egl.eglCreateSyncKHR) {
+        EGLSyncKHR sync = c->cnx->egl.eglCreateSyncKHR(
+                dp->disp[c->impl].dpy, type, attrib_list);
+        if (sync == EGL_NO_SYNC_KHR)
+            return sync;
+        result = (egl_sync_t*)new egl_sync_t(dpy, ctx, sync);
+    }
+    return (EGLSyncKHR)result;
+}
+
+EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync)
+{
+    egl_display_t const * const dp = get_display(dpy);
+    if (dp == 0) {
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    }
+
+    SyncRef _s(sync);
+    if (!_s.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+    egl_sync_t* syncObject = get_sync(sync);
+
+    EGLContext ctx = syncObject->context;
+    ContextRef _c(ctx);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+    if (!validate_display_context(dpy, ctx))
+        return EGL_FALSE;
+
+    egl_context_t * const c = get_context(ctx);
+
+    if (c->cnx->egl.eglDestroySyncKHR) {
+        return c->cnx->egl.eglDestroySyncKHR(
+                dp->disp[c->impl].dpy, syncObject->sync);
+    }
+
+    return EGL_FALSE;
+}
+
+EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout)
+{
+    egl_display_t const * const dp = get_display(dpy);
+    if (dp == 0) {
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    }
+
+    SyncRef _s(sync);
+    if (!_s.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+    egl_sync_t* syncObject = get_sync(sync);
+
+    EGLContext ctx = syncObject->context;
+    ContextRef _c(ctx);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+    if (!validate_display_context(dpy, ctx))
+        return EGL_FALSE;
+
+    egl_context_t * const c = get_context(ctx);
+
+    if (c->cnx->egl.eglClientWaitSyncKHR) {
+        return c->cnx->egl.eglClientWaitSyncKHR(
+                dp->disp[c->impl].dpy, syncObject->sync, flags, timeout);
+    }
+
+    return EGL_FALSE;
+}
+
+EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value)
+{
+    egl_display_t const * const dp = get_display(dpy);
+    if (dp == 0) {
+        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+    }
+
+    SyncRef _s(sync);
+    if (!_s.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+    egl_sync_t* syncObject = get_sync(sync);
+
+    EGLContext ctx = syncObject->context;
+    ContextRef _c(ctx);
+    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+    if (!validate_display_context(dpy, ctx))
+        return EGL_FALSE;
+
+    egl_context_t * const c = get_context(ctx);
+
+    if (c->cnx->egl.eglGetSyncAttribKHR) {
+        return c->cnx->egl.eglGetSyncAttribKHR(
+                dp->disp[c->impl].dpy, syncObject->sync, attribute, value);
+    }
+
+    return EGL_FALSE;
+}
 
 // ----------------------------------------------------------------------------
 // ANDROID extensions
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 5d89287..63c3c19 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -51,6 +51,13 @@
 EGL_ENTRY(EGLImageKHR, eglCreateImageKHR,   EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint *)
 EGL_ENTRY(EGLBoolean,  eglDestroyImageKHR,  EGLDisplay, EGLImageKHR)
 
+/* EGL_EGLEXT_VERSION 5 */
+
+EGL_ENTRY(EGLSyncKHR,   eglCreateSyncKHR,       EGLDisplay, EGLenum, const EGLint *)
+EGL_ENTRY(EGLBoolean,   eglDestroySyncKHR,      EGLDisplay, EGLSyncKHR)
+EGL_ENTRY(EGLint,       eglClientWaitSyncKHR,   EGLDisplay, EGLSyncKHR, EGLint, EGLTimeKHR)
+EGL_ENTRY(EGLBoolean,   eglGetSyncAttribKHR,    EGLDisplay, EGLSyncKHR, EGLint, EGLint *)
+
 /* ANDROID extensions */
 
 EGL_ENTRY(EGLBoolean, eglSetSwapRectangleANDROID, EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint)
diff --git a/opengl/libs/EGL/trace.cpp b/opengl/libs/EGL/trace.cpp
new file mode 100644
index 0000000..d3e96ba
--- /dev/null
+++ b/opengl/libs/EGL/trace.cpp
@@ -0,0 +1,355 @@
+/*
+ ** Copyright 2010, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#if EGL_TRACE
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <cutils/log.h>
+
+#include "hooks.h"
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+struct GLenumString {
+    GLenum e;
+    const char* s;
+};
+
+#undef GL_ENUM
+#define GL_ENUM(VAL,NAME) {VAL, #NAME},
+
+static GLenumString g_enumnames[] = {
+#include "enums.in"
+};
+#undef GL_ENUM
+
+static int compareGLEnum(const void* a, const void* b) {
+    return ((const GLenumString*) a)->e - ((const GLenumString*) b)->e;
+}
+
+static const char* GLEnumToString(GLenum e) {
+    GLenumString key = {e, ""};
+    const GLenumString* result = (const GLenumString*) bsearch(
+        &key, g_enumnames,
+        sizeof(g_enumnames) / sizeof(g_enumnames[0]),
+        sizeof(g_enumnames[0]), compareGLEnum);
+    if (result) {
+        return result->s;
+    }
+    return NULL;
+}
+
+static const char* GLbooleanToString(GLboolean arg) {
+    return arg ? "GL_TRUE" : "GL_FALSE";
+}
+
+static GLenumString g_bitfieldNames[] = {
+    {0x00004000, "GL_COLOR_BUFFER_BIT"},
+    {0x00000400, "GL_STENCIL_BUFFER_BIT"},
+    {0x00000100, "GL_DEPTH_BUFFER_BIT"}
+};
+
+class StringBuilder {
+    static const int lineSize = 500;
+    char line[lineSize];
+    int line_index;
+public:
+    StringBuilder() {
+        line_index = 0;
+        line[0] = '\0';
+    }
+    void append(const char* fmt, ...) {
+        va_list argp;
+        va_start(argp, fmt);
+        line_index += vsnprintf(line + line_index, lineSize-line_index, fmt, argp);
+        va_end(argp);
+    }
+    const char* getString() {
+        line_index = 0;
+        line[lineSize-1] = '\0';
+        return line;
+    }
+};
+
+
+static void TraceGLShaderSource(GLuint shader, GLsizei count,
+    const GLchar** string, const GLint* length) {
+    LOGD("const char* shaderSrc[] = {");
+    for (GLsizei i = 0; i < count; i++) {
+        const char* comma = i < count-1 ? "," : "";
+        const GLchar* s = string[i];
+        if (length) {
+            GLint len = length[i];
+            LOGD("    \"%*s\"%s", len, s, comma);
+        } else {
+            LOGD("    \"%s\"%s", s, comma);
+        }
+    }
+    LOGD("};");
+    if (length) {
+        LOGD("const GLint* shaderLength[] = {");
+        for (GLsizei i = 0; i < count; i++) {
+            const char* comma = i < count-1 ? "," : "";
+            GLint len = length[i];
+            LOGD("    \"%d\"%s", len, comma);
+        }
+        LOGD("};");
+        LOGD("glShaderSource(%u, %u, shaderSrc, shaderLength);",
+            shader, count);
+    } else {
+        LOGD("glShaderSource(%u, %u, shaderSrc, (const GLint*) 0);",
+            shader, count);
+    }
+}
+
+static void TraceValue(int elementCount, char type,
+        GLsizei chunkCount, GLsizei chunkSize, const void* value) {
+    StringBuilder stringBuilder;
+    GLsizei count = chunkCount * chunkSize;
+    bool isFloat = type == 'f';
+    const char* typeString = isFloat ? "GLfloat" : "GLint";
+    LOGD("const %s value[] = {", typeString);
+    for (GLsizei i = 0; i < count; i++) {
+        StringBuilder builder;
+        builder.append("    ");
+        for (int e = 0; e < elementCount; e++) {
+            const char* comma = ", ";
+            if (e == elementCount-1) {
+                if (i == count - 1) {
+                    comma = "";
+                } else {
+                    comma = ",";
+                }
+            }
+            if (isFloat) {
+                builder.append("%g%s", * (GLfloat*) value, comma);
+                value = (void*) (((GLfloat*) value) + 1);
+            } else {
+                builder.append("%d%s", * (GLint*) value, comma);
+                value = (void*) (((GLint*) value) + 1);
+            }
+        }
+        LOGD("%s", builder.getString());
+        if (chunkSize > 1 && i < count-1
+                && (i % chunkSize) == (chunkSize-1)) {
+            LOGD("%s", ""); // Print a blank line.
+        }
+    }
+    LOGD("};");
+}
+
+static void TraceUniformv(int elementCount, char type,
+        GLuint location, GLsizei count, const void* value) {
+    TraceValue(elementCount, type, count, 1, value);
+    LOGD("glUniform%d%c(%u, %u, value);", elementCount, type, location, count);
+}
+
+static void TraceUniformMatrix(int matrixSideLength,
+        GLuint location, GLsizei count, GLboolean transpose, const void* value) {
+    TraceValue(matrixSideLength, 'f', count, matrixSideLength, value);
+    LOGD("glUniformMatrix%dfv(%u, %u, %s, value);", matrixSideLength, location, count,
+            GLbooleanToString(transpose));
+}
+
+static void TraceGL(const char* name, int numArgs, ...) {
+    va_list argp;
+    va_start(argp, numArgs);
+    int nameLen = strlen(name);
+
+    // glShaderSource
+    if (nameLen == 14 && strcmp(name, "glShaderSource") == 0) {
+        va_arg(argp, const char*);
+        GLuint shader = va_arg(argp, GLuint);
+        va_arg(argp, const char*);
+        GLsizei count = va_arg(argp, GLsizei);
+        va_arg(argp, const char*);
+        const GLchar** string = (const GLchar**) va_arg(argp, void*);
+        va_arg(argp, const char*);
+        const GLint* length = (const GLint*) va_arg(argp, void*);
+        va_end(argp);
+        TraceGLShaderSource(shader, count, string, length);
+        return;
+    }
+
+    // glUniformXXv
+
+    if (nameLen == 12 && strncmp(name, "glUniform", 9) == 0 && name[11] == 'v') {
+        int elementCount = name[9] - '0'; // 1..4
+        char type = name[10]; // 'f' or 'i'
+        va_arg(argp, const char*);
+        GLuint location = va_arg(argp, GLuint);
+        va_arg(argp, const char*);
+        GLsizei count = va_arg(argp, GLsizei);
+        va_arg(argp, const char*);
+        const void* value = (const void*) va_arg(argp, void*);
+        va_end(argp);
+        TraceUniformv(elementCount, type, location, count, value);
+        return;
+    }
+
+    // glUniformMatrixXfv
+
+    if (nameLen == 18 && strncmp(name, "glUniformMatrix", 15) == 0
+            && name[16] == 'f' && name[17] == 'v') {
+        int matrixSideLength = name[15] - '0'; // 2..4
+        va_arg(argp, const char*);
+        GLuint location = va_arg(argp, GLuint);
+        va_arg(argp, const char*);
+        GLsizei count = va_arg(argp, GLsizei);
+        va_arg(argp, const char*);
+        GLboolean transpose = (GLboolean) va_arg(argp, int);
+        va_arg(argp, const char*);
+        const void* value = (const void*) va_arg(argp, void*);
+        va_end(argp);
+        TraceUniformMatrix(matrixSideLength, location, count, transpose, value);
+        return;
+    }
+
+    StringBuilder builder;
+    builder.append("%s(", name);
+    for (int i = 0; i < numArgs; i++) {
+        if (i > 0) {
+            builder.append(", ");
+        }
+        const char* type = va_arg(argp, const char*);
+        bool isPtr = type[strlen(type)-1] == '*'
+            || strcmp(type, "GLeglImageOES") == 0;
+        if (isPtr) {
+            const void* arg = va_arg(argp, const void*);
+            builder.append("(%s) 0x%08x", type, (size_t) arg);
+        } else if (strcmp(type, "GLbitfield") == 0) {
+            size_t arg = va_arg(argp, size_t);
+            bool first = true;
+            for (size_t i = 0; i < sizeof(g_bitfieldNames) / sizeof(g_bitfieldNames[0]); i++) {
+                const GLenumString* b = &g_bitfieldNames[i];
+                if (b->e & arg) {
+                    if (first) {
+                        first = false;
+                    } else {
+                        builder.append(" | ");
+                    }
+                    builder.append("%s", b->s);
+                    arg &= ~b->e;
+                }
+            }
+            if (first || arg != 0) {
+                if (!first) {
+                    builder.append(" | ");
+                }
+                builder.append("0x%08x", arg);
+            }
+        } else if (strcmp(type, "GLboolean") == 0) {
+            GLboolean arg = va_arg(argp, int);
+            builder.append("%s", GLbooleanToString(arg));
+        } else if (strcmp(type, "GLclampf") == 0) {
+            double arg = va_arg(argp, double);
+            builder.append("%g", arg);
+        } else if (strcmp(type, "GLenum") == 0) {
+            GLenum arg = va_arg(argp, int);
+            const char* s = GLEnumToString(arg);
+            if (s) {
+                builder.append("%s", s);
+            } else {
+                builder.append("0x%x", arg);
+            }
+        } else if (strcmp(type, "GLfixed") == 0) {
+            int arg = va_arg(argp, int);
+            builder.append("0x%08x", arg);
+        } else if (strcmp(type, "GLfloat") == 0) {
+            double arg = va_arg(argp, double);
+            builder.append("%g", arg);
+        } else if (strcmp(type, "GLint") == 0) {
+            int arg = va_arg(argp, int);
+            const char* s = NULL;
+            if (strcmp(name, "glTexParameteri") == 0) {
+                s = GLEnumToString(arg);
+            }
+            if (s) {
+                builder.append("%s", s);
+            } else {
+                builder.append("%d", arg);
+            }
+        } else if (strcmp(type, "GLintptr") == 0) {
+            int arg = va_arg(argp, unsigned int);
+            builder.append("%u", arg);
+        } else if (strcmp(type, "GLsizei") == 0) {
+            int arg = va_arg(argp, size_t);
+            builder.append("%u", arg);
+        } else if (strcmp(type, "GLsizeiptr") == 0) {
+            int arg = va_arg(argp, size_t);
+            builder.append("%u", arg);
+        } else if (strcmp(type, "GLuint") == 0) {
+            int arg = va_arg(argp, unsigned int);
+            builder.append("%u", arg);
+        } else {
+            builder.append("/* ??? %s */", type);
+            break;
+        }
+    }
+    builder.append(");");
+    LOGD("%s", builder.getString());
+    va_end(argp);
+}
+
+#undef TRACE_GL_VOID
+#undef TRACE_GL
+
+#define TRACE_GL_VOID(_api, _args, _argList, ...)                         \
+static void Tracing_ ## _api _args {                                      \
+    TraceGL(#_api, __VA_ARGS__);                                          \
+    gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl;  \
+    _c->_api _argList;                                                    \
+}
+
+#define TRACE_GL(_type, _api, _args, _argList, ...)                       \
+static _type Tracing_ ## _api _args {                                     \
+    TraceGL(#_api, __VA_ARGS__);                                          \
+    gl_hooks_t::gl_t const * const _c = &getGLTraceThreadSpecific()->gl;  \
+    return _c->_api _argList;                                             \
+}
+
+extern "C" {
+#include "../trace.in"
+}
+#undef TRACE_GL_VOID
+#undef TRACE_GL
+
+#define GL_ENTRY(_r, _api, ...) Tracing_ ## _api,
+
+EGLAPI gl_hooks_t gHooksTrace = {
+    {
+        #include "entries.in"
+    },
+    {
+        {0}
+    }
+};
+#undef GL_ENTRY
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+#endif // EGL_TRACE
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 18dd483..fee4609 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -39,6 +39,8 @@
 #undef CALL_GL_API
 #undef CALL_GL_API_RETURN
 
+#define DEBUG_CALL_GL_API 0
+
 #if USE_FAST_TLS_KEY
 
     #ifdef HAVE_ARM_TLS_REGISTER
@@ -74,10 +76,24 @@
 
     #define API_ENTRY(_api) _api
 
+#if DEBUG_CALL_GL_API
+
     #define CALL_GL_API(_api, ...)                                       \
         gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;  \
-        _c->_api(__VA_ARGS__)
-    
+        _c->_api(__VA_ARGS__); \
+        GLenum status = GL_NO_ERROR; \
+        while ((status = glGetError()) != GL_NO_ERROR) { \
+            LOGD("[" #_api "] 0x%x", status); \
+        }
+
+#else
+
+    #define CALL_GL_API(_api, ...)                                       \
+        gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;  \
+        _c->_api(__VA_ARGS__);
+
+#endif
+
     #define CALL_GL_API_RETURN(_api, ...)                                \
         gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;  \
         return _c->_api(__VA_ARGS__)
diff --git a/opengl/libs/enums.in b/opengl/libs/enums.in
new file mode 100644
index 0000000..f9752c2
--- /dev/null
+++ b/opengl/libs/enums.in
@@ -0,0 +1,594 @@
+GL_ENUM(0x0000,GL_POINTS)
+GL_ENUM(0x0001,GL_LINES)
+GL_ENUM(0x0002,GL_LINE_LOOP)
+GL_ENUM(0x0003,GL_LINE_STRIP)
+GL_ENUM(0x0004,GL_TRIANGLES)
+GL_ENUM(0x0005,GL_TRIANGLE_STRIP)
+GL_ENUM(0x0006,GL_TRIANGLE_FAN)
+GL_ENUM(0x0104,GL_ADD)
+GL_ENUM(0x0200,GL_NEVER)
+GL_ENUM(0x0201,GL_LESS)
+GL_ENUM(0x0202,GL_EQUAL)
+GL_ENUM(0x0203,GL_LEQUAL)
+GL_ENUM(0x0204,GL_GREATER)
+GL_ENUM(0x0205,GL_NOTEQUAL)
+GL_ENUM(0x0206,GL_GEQUAL)
+GL_ENUM(0x0207,GL_ALWAYS)
+GL_ENUM(0x0300,GL_SRC_COLOR)
+GL_ENUM(0x0301,GL_ONE_MINUS_SRC_COLOR)
+GL_ENUM(0x0302,GL_SRC_ALPHA)
+GL_ENUM(0x0303,GL_ONE_MINUS_SRC_ALPHA)
+GL_ENUM(0x0304,GL_DST_ALPHA)
+GL_ENUM(0x0305,GL_ONE_MINUS_DST_ALPHA)
+GL_ENUM(0x0306,GL_DST_COLOR)
+GL_ENUM(0x0307,GL_ONE_MINUS_DST_COLOR)
+GL_ENUM(0x0308,GL_SRC_ALPHA_SATURATE)
+GL_ENUM(0x0404,GL_FRONT)
+GL_ENUM(0x0405,GL_BACK)
+GL_ENUM(0x0408,GL_FRONT_AND_BACK)
+GL_ENUM(0x0500,GL_INVALID_ENUM)
+GL_ENUM(0x0501,GL_INVALID_VALUE)
+GL_ENUM(0x0502,GL_INVALID_OPERATION)
+GL_ENUM(0x0503,GL_STACK_OVERFLOW)
+GL_ENUM(0x0504,GL_STACK_UNDERFLOW)
+GL_ENUM(0x0505,GL_OUT_OF_MEMORY)
+GL_ENUM(0x0506,GL_INVALID_FRAMEBUFFER_OPERATION_OES)
+GL_ENUM(0x0800,GL_EXP)
+GL_ENUM(0x0801,GL_EXP2)
+GL_ENUM(0x0900,GL_CW)
+GL_ENUM(0x0901,GL_CCW)
+GL_ENUM(0x0B00,GL_CURRENT_COLOR)
+GL_ENUM(0x0B02,GL_CURRENT_NORMAL)
+GL_ENUM(0x0B03,GL_CURRENT_TEXTURE_COORDS)
+GL_ENUM(0x0B10,GL_POINT_SMOOTH)
+GL_ENUM(0x0B11,GL_POINT_SIZE)
+GL_ENUM(0x0B12,GL_SMOOTH_POINT_SIZE_RANGE)
+GL_ENUM(0x0B20,GL_LINE_SMOOTH)
+GL_ENUM(0x0B21,GL_LINE_WIDTH)
+GL_ENUM(0x0B22,GL_SMOOTH_LINE_WIDTH_RANGE)
+GL_ENUM(0x0B44,GL_CULL_FACE)
+GL_ENUM(0x0B45,GL_CULL_FACE_MODE)
+GL_ENUM(0x0B46,GL_FRONT_FACE)
+GL_ENUM(0x0B50,GL_LIGHTING)
+GL_ENUM(0x0B52,GL_LIGHT_MODEL_TWO_SIDE)
+GL_ENUM(0x0B53,GL_LIGHT_MODEL_AMBIENT)
+GL_ENUM(0x0B54,GL_SHADE_MODEL)
+GL_ENUM(0x0B57,GL_COLOR_MATERIAL)
+GL_ENUM(0x0B60,GL_FOG)
+GL_ENUM(0x0B62,GL_FOG_DENSITY)
+GL_ENUM(0x0B63,GL_FOG_START)
+GL_ENUM(0x0B64,GL_FOG_END)
+GL_ENUM(0x0B65,GL_FOG_MODE)
+GL_ENUM(0x0B66,GL_FOG_COLOR)
+GL_ENUM(0x0B70,GL_DEPTH_RANGE)
+GL_ENUM(0x0B71,GL_DEPTH_TEST)
+GL_ENUM(0x0B72,GL_DEPTH_WRITEMASK)
+GL_ENUM(0x0B73,GL_DEPTH_CLEAR_VALUE)
+GL_ENUM(0x0B74,GL_DEPTH_FUNC)
+GL_ENUM(0x0B90,GL_STENCIL_TEST)
+GL_ENUM(0x0B91,GL_STENCIL_CLEAR_VALUE)
+GL_ENUM(0x0B92,GL_STENCIL_FUNC)
+GL_ENUM(0x0B93,GL_STENCIL_VALUE_MASK)
+GL_ENUM(0x0B94,GL_STENCIL_FAIL)
+GL_ENUM(0x0B95,GL_STENCIL_PASS_DEPTH_FAIL)
+GL_ENUM(0x0B96,GL_STENCIL_PASS_DEPTH_PASS)
+GL_ENUM(0x0B97,GL_STENCIL_REF)
+GL_ENUM(0x0B98,GL_STENCIL_WRITEMASK)
+GL_ENUM(0x0BA0,GL_MATRIX_MODE)
+GL_ENUM(0x0BA1,GL_NORMALIZE)
+GL_ENUM(0x0BA2,GL_VIEWPORT)
+GL_ENUM(0x0BA3,GL_MODELVIEW_STACK_DEPTH)
+GL_ENUM(0x0BA4,GL_PROJECTION_STACK_DEPTH)
+GL_ENUM(0x0BA5,GL_TEXTURE_STACK_DEPTH)
+GL_ENUM(0x0BA6,GL_MODELVIEW_MATRIX)
+GL_ENUM(0x0BA7,GL_PROJECTION_MATRIX)
+GL_ENUM(0x0BA8,GL_TEXTURE_MATRIX)
+GL_ENUM(0x0BC0,GL_ALPHA_TEST)
+GL_ENUM(0x0BC1,GL_ALPHA_TEST_FUNC)
+GL_ENUM(0x0BC2,GL_ALPHA_TEST_REF)
+GL_ENUM(0x0BD0,GL_DITHER)
+GL_ENUM(0x0BE0,GL_BLEND_DST)
+GL_ENUM(0x0BE1,GL_BLEND_SRC)
+GL_ENUM(0x0BE2,GL_BLEND)
+GL_ENUM(0x0BF0,GL_LOGIC_OP_MODE)
+GL_ENUM(0x0BF2,GL_COLOR_LOGIC_OP)
+GL_ENUM(0x0C10,GL_SCISSOR_BOX)
+GL_ENUM(0x0C11,GL_SCISSOR_TEST)
+GL_ENUM(0x0C22,GL_COLOR_CLEAR_VALUE)
+GL_ENUM(0x0C23,GL_COLOR_WRITEMASK)
+GL_ENUM(0x0C50,GL_PERSPECTIVE_CORRECTION_HINT)
+GL_ENUM(0x0C51,GL_POINT_SMOOTH_HINT)
+GL_ENUM(0x0C52,GL_LINE_SMOOTH_HINT)
+GL_ENUM(0x0C54,GL_FOG_HINT)
+GL_ENUM(0x0CF5,GL_UNPACK_ALIGNMENT)
+GL_ENUM(0x0D05,GL_PACK_ALIGNMENT)
+GL_ENUM(0x0D1C,GL_ALPHA_SCALE)
+GL_ENUM(0x0D31,GL_MAX_LIGHTS)
+GL_ENUM(0x0D32,GL_MAX_CLIP_PLANES)
+GL_ENUM(0x0D33,GL_MAX_TEXTURE_SIZE)
+GL_ENUM(0x0D36,GL_MAX_MODELVIEW_STACK_DEPTH)
+GL_ENUM(0x0D38,GL_MAX_PROJECTION_STACK_DEPTH)
+GL_ENUM(0x0D39,GL_MAX_TEXTURE_STACK_DEPTH)
+GL_ENUM(0x0D3A,GL_MAX_VIEWPORT_DIMS)
+GL_ENUM(0x0D50,GL_SUBPIXEL_BITS)
+GL_ENUM(0x0D52,GL_RED_BITS)
+GL_ENUM(0x0D53,GL_GREEN_BITS)
+GL_ENUM(0x0D54,GL_BLUE_BITS)
+GL_ENUM(0x0D55,GL_ALPHA_BITS)
+GL_ENUM(0x0D56,GL_DEPTH_BITS)
+GL_ENUM(0x0D57,GL_STENCIL_BITS)
+GL_ENUM(0x0DE1,GL_TEXTURE_2D)
+GL_ENUM(0x1100,GL_DONT_CARE)
+GL_ENUM(0x1101,GL_FASTEST)
+GL_ENUM(0x1102,GL_NICEST)
+GL_ENUM(0x1200,GL_AMBIENT)
+GL_ENUM(0x1201,GL_DIFFUSE)
+GL_ENUM(0x1202,GL_SPECULAR)
+GL_ENUM(0x1203,GL_POSITION)
+GL_ENUM(0x1204,GL_SPOT_DIRECTION)
+GL_ENUM(0x1205,GL_SPOT_EXPONENT)
+GL_ENUM(0x1206,GL_SPOT_CUTOFF)
+GL_ENUM(0x1207,GL_CONSTANT_ATTENUATION)
+GL_ENUM(0x1208,GL_LINEAR_ATTENUATION)
+GL_ENUM(0x1209,GL_QUADRATIC_ATTENUATION)
+GL_ENUM(0x1400,GL_BYTE)
+GL_ENUM(0x1401,GL_UNSIGNED_BYTE)
+GL_ENUM(0x1402,GL_SHORT)
+GL_ENUM(0x1403,GL_UNSIGNED_SHORT)
+GL_ENUM(0x1404,GL_INT)
+GL_ENUM(0x1405,GL_UNSIGNED_INT)
+GL_ENUM(0x1406,GL_FLOAT)
+GL_ENUM(0x140C,GL_FIXED)
+GL_ENUM(0x1500,GL_CLEAR)
+GL_ENUM(0x1501,GL_AND)
+GL_ENUM(0x1502,GL_AND_REVERSE)
+GL_ENUM(0x1503,GL_COPY)
+GL_ENUM(0x1504,GL_AND_INVERTED)
+GL_ENUM(0x1505,GL_NOOP)
+GL_ENUM(0x1506,GL_XOR)
+GL_ENUM(0x1507,GL_OR)
+GL_ENUM(0x1508,GL_NOR)
+GL_ENUM(0x1509,GL_EQUIV)
+GL_ENUM(0x150A,GL_INVERT)
+GL_ENUM(0x150B,GL_OR_REVERSE)
+GL_ENUM(0x150C,GL_COPY_INVERTED)
+GL_ENUM(0x150D,GL_OR_INVERTED)
+GL_ENUM(0x150E,GL_NAND)
+GL_ENUM(0x150F,GL_SET)
+GL_ENUM(0x1600,GL_EMISSION)
+GL_ENUM(0x1601,GL_SHININESS)
+GL_ENUM(0x1602,GL_AMBIENT_AND_DIFFUSE)
+GL_ENUM(0x1700,GL_MODELVIEW)
+GL_ENUM(0x1701,GL_PROJECTION)
+GL_ENUM(0x1702,GL_TEXTURE)
+GL_ENUM(0x1800,GL_COLOR_EXT)
+GL_ENUM(0x1801,GL_DEPTH_EXT)
+GL_ENUM(0x1802,GL_STENCIL_EXT)
+GL_ENUM(0x1901,GL_STENCIL_INDEX)
+GL_ENUM(0x1902,GL_DEPTH_COMPONENT)
+GL_ENUM(0x1906,GL_ALPHA)
+GL_ENUM(0x1907,GL_RGB)
+GL_ENUM(0x1908,GL_RGBA)
+GL_ENUM(0x1909,GL_LUMINANCE)
+GL_ENUM(0x190A,GL_LUMINANCE_ALPHA)
+GL_ENUM(0x1D00,GL_FLAT)
+GL_ENUM(0x1D01,GL_SMOOTH)
+GL_ENUM(0x1E00,GL_KEEP)
+GL_ENUM(0x1E01,GL_REPLACE)
+GL_ENUM(0x1E02,GL_INCR)
+GL_ENUM(0x1E03,GL_DECR)
+GL_ENUM(0x1F00,GL_VENDOR)
+GL_ENUM(0x1F01,GL_RENDERER)
+GL_ENUM(0x1F02,GL_VERSION)
+GL_ENUM(0x1F03,GL_EXTENSIONS)
+GL_ENUM(0x2100,GL_MODULATE)
+GL_ENUM(0x2101,GL_DECAL)
+GL_ENUM(0x2200,GL_TEXTURE_ENV_MODE)
+GL_ENUM(0x2201,GL_TEXTURE_ENV_COLOR)
+GL_ENUM(0x2300,GL_TEXTURE_ENV)
+GL_ENUM(0x2500,GL_TEXTURE_GEN_MODE_OES)
+GL_ENUM(0x2600,GL_NEAREST)
+GL_ENUM(0x2601,GL_LINEAR)
+GL_ENUM(0x2700,GL_NEAREST_MIPMAP_NEAREST)
+GL_ENUM(0x2701,GL_LINEAR_MIPMAP_NEAREST)
+GL_ENUM(0x2702,GL_NEAREST_MIPMAP_LINEAR)
+GL_ENUM(0x2703,GL_LINEAR_MIPMAP_LINEAR)
+GL_ENUM(0x2800,GL_TEXTURE_MAG_FILTER)
+GL_ENUM(0x2801,GL_TEXTURE_MIN_FILTER)
+GL_ENUM(0x2802,GL_TEXTURE_WRAP_S)
+GL_ENUM(0x2803,GL_TEXTURE_WRAP_T)
+GL_ENUM(0x2901,GL_REPEAT)
+GL_ENUM(0x2A00,GL_POLYGON_OFFSET_UNITS)
+GL_ENUM(0x3000,GL_CLIP_PLANE0)
+GL_ENUM(0x3001,GL_CLIP_PLANE1)
+GL_ENUM(0x3002,GL_CLIP_PLANE2)
+GL_ENUM(0x3003,GL_CLIP_PLANE3)
+GL_ENUM(0x3004,GL_CLIP_PLANE4)
+GL_ENUM(0x3005,GL_CLIP_PLANE5)
+GL_ENUM(0x4000,GL_LIGHT0)
+GL_ENUM(0x4001,GL_LIGHT1)
+GL_ENUM(0x4002,GL_LIGHT2)
+GL_ENUM(0x4003,GL_LIGHT3)
+GL_ENUM(0x4004,GL_LIGHT4)
+GL_ENUM(0x4005,GL_LIGHT5)
+GL_ENUM(0x4006,GL_LIGHT6)
+GL_ENUM(0x4007,GL_LIGHT7)
+GL_ENUM(0x8000,GL_COVERAGE_BUFFER_BIT_NV)
+GL_ENUM(0x8001,GL_CONSTANT_COLOR)
+GL_ENUM(0x8002,GL_ONE_MINUS_CONSTANT_COLOR)
+GL_ENUM(0x8003,GL_CONSTANT_ALPHA)
+GL_ENUM(0x8004,GL_ONE_MINUS_CONSTANT_ALPHA)
+GL_ENUM(0x8005,GL_BLEND_COLOR)
+GL_ENUM(0x8006,GL_FUNC_ADD_OES)
+GL_ENUM(0x8007,GL_MIN_EXT)
+GL_ENUM(0x8008,GL_MAX_EXT)
+GL_ENUM(0x8009,GL_BLEND_EQUATION_RGB_OES)
+GL_ENUM(0x800A,GL_FUNC_SUBTRACT_OES)
+GL_ENUM(0x800B,GL_FUNC_REVERSE_SUBTRACT_OES)
+GL_ENUM(0x8033,GL_UNSIGNED_SHORT_4_4_4_4)
+GL_ENUM(0x8034,GL_UNSIGNED_SHORT_5_5_5_1)
+GL_ENUM(0x8037,GL_POLYGON_OFFSET_FILL)
+GL_ENUM(0x8038,GL_POLYGON_OFFSET_FACTOR)
+GL_ENUM(0x803A,GL_RESCALE_NORMAL)
+GL_ENUM(0x8051,GL_RGB8_OES)
+GL_ENUM(0x8056,GL_RGBA4_OES)
+GL_ENUM(0x8057,GL_RGB5_A1_OES)
+GL_ENUM(0x8058,GL_RGBA8_OES)
+GL_ENUM(0x8069,GL_TEXTURE_BINDING_2D)
+GL_ENUM(0x806A,GL_TEXTURE_BINDING_3D_OES)
+GL_ENUM(0x806F,GL_TEXTURE_3D_OES)
+GL_ENUM(0x8072,GL_TEXTURE_WRAP_R_OES)
+GL_ENUM(0x8073,GL_MAX_3D_TEXTURE_SIZE_OES)
+GL_ENUM(0x8074,GL_VERTEX_ARRAY)
+GL_ENUM(0x8075,GL_NORMAL_ARRAY)
+GL_ENUM(0x8076,GL_COLOR_ARRAY)
+GL_ENUM(0x8078,GL_TEXTURE_COORD_ARRAY)
+GL_ENUM(0x807A,GL_VERTEX_ARRAY_SIZE)
+GL_ENUM(0x807B,GL_VERTEX_ARRAY_TYPE)
+GL_ENUM(0x807C,GL_VERTEX_ARRAY_STRIDE)
+GL_ENUM(0x807E,GL_NORMAL_ARRAY_TYPE)
+GL_ENUM(0x807F,GL_NORMAL_ARRAY_STRIDE)
+GL_ENUM(0x8081,GL_COLOR_ARRAY_SIZE)
+GL_ENUM(0x8082,GL_COLOR_ARRAY_TYPE)
+GL_ENUM(0x8083,GL_COLOR_ARRAY_STRIDE)
+GL_ENUM(0x8088,GL_TEXTURE_COORD_ARRAY_SIZE)
+GL_ENUM(0x8089,GL_TEXTURE_COORD_ARRAY_TYPE)
+GL_ENUM(0x808A,GL_TEXTURE_COORD_ARRAY_STRIDE)
+GL_ENUM(0x808E,GL_VERTEX_ARRAY_POINTER)
+GL_ENUM(0x808F,GL_NORMAL_ARRAY_POINTER)
+GL_ENUM(0x8090,GL_COLOR_ARRAY_POINTER)
+GL_ENUM(0x8092,GL_TEXTURE_COORD_ARRAY_POINTER)
+GL_ENUM(0x809D,GL_MULTISAMPLE)
+GL_ENUM(0x809E,GL_SAMPLE_ALPHA_TO_COVERAGE)
+GL_ENUM(0x809F,GL_SAMPLE_ALPHA_TO_ONE)
+GL_ENUM(0x80A0,GL_SAMPLE_COVERAGE)
+GL_ENUM(0x80A8,GL_SAMPLE_BUFFERS)
+GL_ENUM(0x80A9,GL_SAMPLES)
+GL_ENUM(0x80AA,GL_SAMPLE_COVERAGE_VALUE)
+GL_ENUM(0x80AB,GL_SAMPLE_COVERAGE_INVERT)
+GL_ENUM(0x80C8,GL_BLEND_DST_RGB_OES)
+GL_ENUM(0x80C9,GL_BLEND_SRC_RGB_OES)
+GL_ENUM(0x80CA,GL_BLEND_DST_ALPHA_OES)
+GL_ENUM(0x80CB,GL_BLEND_SRC_ALPHA_OES)
+GL_ENUM(0x80E1,GL_BGRA_EXT)
+GL_ENUM(0x8126,GL_POINT_SIZE_MIN)
+GL_ENUM(0x8127,GL_POINT_SIZE_MAX)
+GL_ENUM(0x8128,GL_POINT_FADE_THRESHOLD_SIZE)
+GL_ENUM(0x8129,GL_POINT_DISTANCE_ATTENUATION)
+GL_ENUM(0x812F,GL_CLAMP_TO_EDGE)
+GL_ENUM(0x8191,GL_GENERATE_MIPMAP)
+GL_ENUM(0x8192,GL_GENERATE_MIPMAP_HINT)
+GL_ENUM(0x81A5,GL_DEPTH_COMPONENT16_OES)
+GL_ENUM(0x81A6,GL_DEPTH_COMPONENT24_OES)
+GL_ENUM(0x81A7,GL_DEPTH_COMPONENT32_OES)
+GL_ENUM(0x8363,GL_UNSIGNED_SHORT_5_6_5)
+GL_ENUM(0x8365,GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT)
+GL_ENUM(0x8366,GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT)
+GL_ENUM(0x8368,GL_UNSIGNED_INT_2_10_10_10_REV_EXT)
+GL_ENUM(0x8370,GL_MIRRORED_REPEAT_OES)
+GL_ENUM(0x83F0,GL_COMPRESSED_RGB_S3TC_DXT1_EXT)
+GL_ENUM(0x83F1,GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
+GL_ENUM(0x846D,GL_ALIASED_POINT_SIZE_RANGE)
+GL_ENUM(0x846E,GL_ALIASED_LINE_WIDTH_RANGE)
+GL_ENUM(0x84C0,GL_TEXTURE0)
+GL_ENUM(0x84C1,GL_TEXTURE1)
+GL_ENUM(0x84C2,GL_TEXTURE2)
+GL_ENUM(0x84C3,GL_TEXTURE3)
+GL_ENUM(0x84C4,GL_TEXTURE4)
+GL_ENUM(0x84C5,GL_TEXTURE5)
+GL_ENUM(0x84C6,GL_TEXTURE6)
+GL_ENUM(0x84C7,GL_TEXTURE7)
+GL_ENUM(0x84C8,GL_TEXTURE8)
+GL_ENUM(0x84C9,GL_TEXTURE9)
+GL_ENUM(0x84CA,GL_TEXTURE10)
+GL_ENUM(0x84CB,GL_TEXTURE11)
+GL_ENUM(0x84CC,GL_TEXTURE12)
+GL_ENUM(0x84CD,GL_TEXTURE13)
+GL_ENUM(0x84CE,GL_TEXTURE14)
+GL_ENUM(0x84CF,GL_TEXTURE15)
+GL_ENUM(0x84D0,GL_TEXTURE16)
+GL_ENUM(0x84D1,GL_TEXTURE17)
+GL_ENUM(0x84D2,GL_TEXTURE18)
+GL_ENUM(0x84D3,GL_TEXTURE19)
+GL_ENUM(0x84D4,GL_TEXTURE20)
+GL_ENUM(0x84D5,GL_TEXTURE21)
+GL_ENUM(0x84D6,GL_TEXTURE22)
+GL_ENUM(0x84D7,GL_TEXTURE23)
+GL_ENUM(0x84D8,GL_TEXTURE24)
+GL_ENUM(0x84D9,GL_TEXTURE25)
+GL_ENUM(0x84DA,GL_TEXTURE26)
+GL_ENUM(0x84DB,GL_TEXTURE27)
+GL_ENUM(0x84DC,GL_TEXTURE28)
+GL_ENUM(0x84DD,GL_TEXTURE29)
+GL_ENUM(0x84DE,GL_TEXTURE30)
+GL_ENUM(0x84DF,GL_TEXTURE31)
+GL_ENUM(0x84E0,GL_ACTIVE_TEXTURE)
+GL_ENUM(0x84E1,GL_CLIENT_ACTIVE_TEXTURE)
+GL_ENUM(0x84E2,GL_MAX_TEXTURE_UNITS)
+GL_ENUM(0x84E7,GL_SUBTRACT)
+GL_ENUM(0x84E8,GL_MAX_RENDERBUFFER_SIZE_OES)
+GL_ENUM(0x84F2,GL_ALL_COMPLETED_NV)
+GL_ENUM(0x84F3,GL_FENCE_STATUS_NV)
+GL_ENUM(0x84F4,GL_FENCE_CONDITION_NV)
+GL_ENUM(0x84F9,GL_DEPTH_STENCIL_OES)
+GL_ENUM(0x84FA,GL_UNSIGNED_INT_24_8_OES)
+GL_ENUM(0x84FD,GL_MAX_TEXTURE_LOD_BIAS_EXT)
+GL_ENUM(0x84FE,GL_TEXTURE_MAX_ANISOTROPY_EXT)
+GL_ENUM(0x84FF,GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT)
+GL_ENUM(0x8500,GL_TEXTURE_FILTER_CONTROL_EXT)
+GL_ENUM(0x8501,GL_TEXTURE_LOD_BIAS_EXT)
+GL_ENUM(0x8507,GL_INCR_WRAP_OES)
+GL_ENUM(0x8508,GL_DECR_WRAP_OES)
+GL_ENUM(0x8511,GL_NORMAL_MAP_OES)
+GL_ENUM(0x8512,GL_REFLECTION_MAP_OES)
+GL_ENUM(0x8513,GL_TEXTURE_CUBE_MAP_OES)
+GL_ENUM(0x8514,GL_TEXTURE_BINDING_CUBE_MAP_OES)
+GL_ENUM(0x8515,GL_TEXTURE_CUBE_MAP_POSITIVE_X_OES)
+GL_ENUM(0x8516,GL_TEXTURE_CUBE_MAP_NEGATIVE_X_OES)
+GL_ENUM(0x8517,GL_TEXTURE_CUBE_MAP_POSITIVE_Y_OES)
+GL_ENUM(0x8518,GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_OES)
+GL_ENUM(0x8519,GL_TEXTURE_CUBE_MAP_POSITIVE_Z_OES)
+GL_ENUM(0x851A,GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_OES)
+GL_ENUM(0x851C,GL_MAX_CUBE_MAP_TEXTURE_SIZE_OES)
+GL_ENUM(0x8570,GL_COMBINE)
+GL_ENUM(0x8571,GL_COMBINE_RGB)
+GL_ENUM(0x8572,GL_COMBINE_ALPHA)
+GL_ENUM(0x8573,GL_RGB_SCALE)
+GL_ENUM(0x8574,GL_ADD_SIGNED)
+GL_ENUM(0x8575,GL_INTERPOLATE)
+GL_ENUM(0x8576,GL_CONSTANT)
+GL_ENUM(0x8577,GL_PRIMARY_COLOR)
+GL_ENUM(0x8578,GL_PREVIOUS)
+GL_ENUM(0x8580,GL_SRC0_RGB)
+GL_ENUM(0x8581,GL_SRC1_RGB)
+GL_ENUM(0x8582,GL_SRC2_RGB)
+GL_ENUM(0x8588,GL_SRC0_ALPHA)
+GL_ENUM(0x8589,GL_SRC1_ALPHA)
+GL_ENUM(0x858A,GL_SRC2_ALPHA)
+GL_ENUM(0x8590,GL_OPERAND0_RGB)
+GL_ENUM(0x8591,GL_OPERAND1_RGB)
+GL_ENUM(0x8592,GL_OPERAND2_RGB)
+GL_ENUM(0x8598,GL_OPERAND0_ALPHA)
+GL_ENUM(0x8599,GL_OPERAND1_ALPHA)
+GL_ENUM(0x859A,GL_OPERAND2_ALPHA)
+GL_ENUM(0x85B5,GL_VERTEX_ARRAY_BINDING_OES)
+GL_ENUM(0x8622,GL_VERTEX_ATTRIB_ARRAY_ENABLED)
+GL_ENUM(0x8623,GL_VERTEX_ATTRIB_ARRAY_SIZE)
+GL_ENUM(0x8624,GL_VERTEX_ATTRIB_ARRAY_STRIDE)
+GL_ENUM(0x8625,GL_VERTEX_ATTRIB_ARRAY_TYPE)
+GL_ENUM(0x8626,GL_CURRENT_VERTEX_ATTRIB)
+GL_ENUM(0x8645,GL_VERTEX_ATTRIB_ARRAY_POINTER)
+GL_ENUM(0x86A2,GL_NUM_COMPRESSED_TEXTURE_FORMATS)
+GL_ENUM(0x86A3,GL_COMPRESSED_TEXTURE_FORMATS)
+GL_ENUM(0x86A4,GL_MAX_VERTEX_UNITS_OES)
+GL_ENUM(0x86A9,GL_WEIGHT_ARRAY_TYPE_OES)
+GL_ENUM(0x86AA,GL_WEIGHT_ARRAY_STRIDE_OES)
+GL_ENUM(0x86AB,GL_WEIGHT_ARRAY_SIZE_OES)
+GL_ENUM(0x86AC,GL_WEIGHT_ARRAY_POINTER_OES)
+GL_ENUM(0x86AD,GL_WEIGHT_ARRAY_OES)
+GL_ENUM(0x86AE,GL_DOT3_RGB)
+GL_ENUM(0x86AF,GL_DOT3_RGBA)
+GL_ENUM(0x8740,GL_Z400_BINARY_AMD)
+GL_ENUM(0x8741,GL_PROGRAM_BINARY_LENGTH_OES)
+GL_ENUM(0x8764,GL_BUFFER_SIZE)
+GL_ENUM(0x8765,GL_BUFFER_USAGE)
+GL_ENUM(0x87EE,GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD)
+GL_ENUM(0x87F9,GL_3DC_X_AMD)
+GL_ENUM(0x87FA,GL_3DC_XY_AMD)
+GL_ENUM(0x87FE,GL_NUM_PROGRAM_BINARY_FORMATS_OES)
+GL_ENUM(0x87FF,GL_PROGRAM_BINARY_FORMATS_OES)
+GL_ENUM(0x8800,GL_STENCIL_BACK_FUNC)
+GL_ENUM(0x8801,GL_STENCIL_BACK_FAIL)
+GL_ENUM(0x8802,GL_STENCIL_BACK_PASS_DEPTH_FAIL)
+GL_ENUM(0x8803,GL_STENCIL_BACK_PASS_DEPTH_PASS)
+GL_ENUM(0x8823,GL_WRITEONLY_RENDERING_QCOM)
+GL_ENUM(0x883D,GL_BLEND_EQUATION_ALPHA_OES)
+GL_ENUM(0x8840,GL_MATRIX_PALETTE_OES)
+GL_ENUM(0x8842,GL_MAX_PALETTE_MATRICES_OES)
+GL_ENUM(0x8843,GL_CURRENT_PALETTE_MATRIX_OES)
+GL_ENUM(0x8844,GL_MATRIX_INDEX_ARRAY_OES)
+GL_ENUM(0x8846,GL_MATRIX_INDEX_ARRAY_SIZE_OES)
+GL_ENUM(0x8847,GL_MATRIX_INDEX_ARRAY_TYPE_OES)
+GL_ENUM(0x8848,GL_MATRIX_INDEX_ARRAY_STRIDE_OES)
+GL_ENUM(0x8849,GL_MATRIX_INDEX_ARRAY_POINTER_OES)
+GL_ENUM(0x8861,GL_POINT_SPRITE_OES)
+GL_ENUM(0x8862,GL_COORD_REPLACE_OES)
+GL_ENUM(0x8869,GL_MAX_VERTEX_ATTRIBS)
+GL_ENUM(0x886A,GL_VERTEX_ATTRIB_ARRAY_NORMALIZED)
+GL_ENUM(0x8872,GL_MAX_TEXTURE_IMAGE_UNITS)
+GL_ENUM(0x8892,GL_ARRAY_BUFFER)
+GL_ENUM(0x8893,GL_ELEMENT_ARRAY_BUFFER)
+GL_ENUM(0x8894,GL_ARRAY_BUFFER_BINDING)
+GL_ENUM(0x8895,GL_ELEMENT_ARRAY_BUFFER_BINDING)
+GL_ENUM(0x8896,GL_VERTEX_ARRAY_BUFFER_BINDING)
+GL_ENUM(0x8897,GL_NORMAL_ARRAY_BUFFER_BINDING)
+GL_ENUM(0x8898,GL_COLOR_ARRAY_BUFFER_BINDING)
+GL_ENUM(0x889A,GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING)
+GL_ENUM(0x889E,GL_WEIGHT_ARRAY_BUFFER_BINDING_OES)
+GL_ENUM(0x889F,GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)
+GL_ENUM(0x88B9,GL_WRITE_ONLY_OES)
+GL_ENUM(0x88BB,GL_BUFFER_ACCESS_OES)
+GL_ENUM(0x88BC,GL_BUFFER_MAPPED_OES)
+GL_ENUM(0x88BD,GL_BUFFER_MAP_POINTER_OES)
+GL_ENUM(0x88E0,GL_STREAM_DRAW)
+GL_ENUM(0x88E4,GL_STATIC_DRAW)
+GL_ENUM(0x88E8,GL_DYNAMIC_DRAW)
+GL_ENUM(0x88F0,GL_DEPTH24_STENCIL8_OES)
+GL_ENUM(0x898A,GL_POINT_SIZE_ARRAY_TYPE_OES)
+GL_ENUM(0x898B,GL_POINT_SIZE_ARRAY_STRIDE_OES)
+GL_ENUM(0x898C,GL_POINT_SIZE_ARRAY_POINTER_OES)
+GL_ENUM(0x898D,GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES)
+GL_ENUM(0x898E,GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES)
+GL_ENUM(0x898F,GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES)
+GL_ENUM(0x8B30,GL_FRAGMENT_SHADER)
+GL_ENUM(0x8B31,GL_VERTEX_SHADER)
+GL_ENUM(0x8B4C,GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS)
+GL_ENUM(0x8B4D,GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)
+GL_ENUM(0x8B4F,GL_SHADER_TYPE)
+GL_ENUM(0x8B50,GL_FLOAT_VEC2)
+GL_ENUM(0x8B51,GL_FLOAT_VEC3)
+GL_ENUM(0x8B52,GL_FLOAT_VEC4)
+GL_ENUM(0x8B53,GL_INT_VEC2)
+GL_ENUM(0x8B54,GL_INT_VEC3)
+GL_ENUM(0x8B55,GL_INT_VEC4)
+GL_ENUM(0x8B56,GL_BOOL)
+GL_ENUM(0x8B57,GL_BOOL_VEC2)
+GL_ENUM(0x8B58,GL_BOOL_VEC3)
+GL_ENUM(0x8B59,GL_BOOL_VEC4)
+GL_ENUM(0x8B5A,GL_FLOAT_MAT2)
+GL_ENUM(0x8B5B,GL_FLOAT_MAT3)
+GL_ENUM(0x8B5C,GL_FLOAT_MAT4)
+GL_ENUM(0x8B5E,GL_SAMPLER_2D)
+GL_ENUM(0x8B5F,GL_SAMPLER_3D_OES)
+GL_ENUM(0x8B60,GL_SAMPLER_CUBE)
+GL_ENUM(0x8B80,GL_DELETE_STATUS)
+GL_ENUM(0x8B81,GL_COMPILE_STATUS)
+GL_ENUM(0x8B82,GL_LINK_STATUS)
+GL_ENUM(0x8B83,GL_VALIDATE_STATUS)
+GL_ENUM(0x8B84,GL_INFO_LOG_LENGTH)
+GL_ENUM(0x8B85,GL_ATTACHED_SHADERS)
+GL_ENUM(0x8B86,GL_ACTIVE_UNIFORMS)
+GL_ENUM(0x8B87,GL_ACTIVE_UNIFORM_MAX_LENGTH)
+GL_ENUM(0x8B88,GL_SHADER_SOURCE_LENGTH)
+GL_ENUM(0x8B89,GL_ACTIVE_ATTRIBUTES)
+GL_ENUM(0x8B8A,GL_ACTIVE_ATTRIBUTE_MAX_LENGTH)
+GL_ENUM(0x8B8B,GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES)
+GL_ENUM(0x8B8C,GL_SHADING_LANGUAGE_VERSION)
+GL_ENUM(0x8B8D,GL_CURRENT_PROGRAM)
+GL_ENUM(0x8B90,GL_PALETTE4_RGB8_OES)
+GL_ENUM(0x8B91,GL_PALETTE4_RGBA8_OES)
+GL_ENUM(0x8B92,GL_PALETTE4_R5_G6_B5_OES)
+GL_ENUM(0x8B93,GL_PALETTE4_RGBA4_OES)
+GL_ENUM(0x8B94,GL_PALETTE4_RGB5_A1_OES)
+GL_ENUM(0x8B95,GL_PALETTE8_RGB8_OES)
+GL_ENUM(0x8B96,GL_PALETTE8_RGBA8_OES)
+GL_ENUM(0x8B97,GL_PALETTE8_R5_G6_B5_OES)
+GL_ENUM(0x8B98,GL_PALETTE8_RGBA4_OES)
+GL_ENUM(0x8B99,GL_PALETTE8_RGB5_A1_OES)
+GL_ENUM(0x8B9A,GL_IMPLEMENTATION_COLOR_READ_TYPE_OES)
+GL_ENUM(0x8B9B,GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES)
+GL_ENUM(0x8B9C,GL_POINT_SIZE_ARRAY_OES)
+GL_ENUM(0x8B9D,GL_TEXTURE_CROP_RECT_OES)
+GL_ENUM(0x8B9E,GL_MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES)
+GL_ENUM(0x8B9F,GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES)
+GL_ENUM(0x8BC0,GL_COUNTER_TYPE_AMD)
+GL_ENUM(0x8BC1,GL_COUNTER_RANGE_AMD)
+GL_ENUM(0x8BC2,GL_UNSIGNED_INT64_AMD)
+GL_ENUM(0x8BC3,GL_PERCENTAGE_AMD)
+GL_ENUM(0x8BC4,GL_PERFMON_RESULT_AVAILABLE_AMD)
+GL_ENUM(0x8BC5,GL_PERFMON_RESULT_SIZE_AMD)
+GL_ENUM(0x8BC6,GL_PERFMON_RESULT_AMD)
+GL_ENUM(0x8BD2,GL_TEXTURE_WIDTH_QCOM)
+GL_ENUM(0x8BD3,GL_TEXTURE_HEIGHT_QCOM)
+GL_ENUM(0x8BD4,GL_TEXTURE_DEPTH_QCOM)
+GL_ENUM(0x8BD5,GL_TEXTURE_INTERNAL_FORMAT_QCOM)
+GL_ENUM(0x8BD6,GL_TEXTURE_FORMAT_QCOM)
+GL_ENUM(0x8BD7,GL_TEXTURE_TYPE_QCOM)
+GL_ENUM(0x8BD8,GL_TEXTURE_IMAGE_VALID_QCOM)
+GL_ENUM(0x8BD9,GL_TEXTURE_NUM_LEVELS_QCOM)
+GL_ENUM(0x8BDA,GL_TEXTURE_TARGET_QCOM)
+GL_ENUM(0x8BDB,GL_TEXTURE_OBJECT_VALID_QCOM)
+GL_ENUM(0x8BDC,GL_STATE_RESTORE)
+GL_ENUM(0x8C00,GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG)
+GL_ENUM(0x8C01,GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG)
+GL_ENUM(0x8C02,GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG)
+GL_ENUM(0x8C03,GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG)
+GL_ENUM(0x8C04,GL_MODULATE_COLOR_IMG)
+GL_ENUM(0x8C05,GL_RECIP_ADD_SIGNED_ALPHA_IMG)
+GL_ENUM(0x8C06,GL_TEXTURE_ALPHA_MODULATE_IMG)
+GL_ENUM(0x8C07,GL_FACTOR_ALPHA_MODULATE_IMG)
+GL_ENUM(0x8C08,GL_FRAGMENT_ALPHA_MODULATE_IMG)
+GL_ENUM(0x8C09,GL_ADD_BLEND_IMG)
+GL_ENUM(0x8C0A,GL_SGX_BINARY_IMG)
+GL_ENUM(0x8C92,GL_ATC_RGB_AMD)
+GL_ENUM(0x8C93,GL_ATC_RGBA_EXPLICIT_ALPHA_AMD)
+GL_ENUM(0x8CA3,GL_STENCIL_BACK_REF)
+GL_ENUM(0x8CA4,GL_STENCIL_BACK_VALUE_MASK)
+GL_ENUM(0x8CA5,GL_STENCIL_BACK_WRITEMASK)
+GL_ENUM(0x8CA6,GL_FRAMEBUFFER_BINDING_OES)
+GL_ENUM(0x8CA7,GL_RENDERBUFFER_BINDING_OES)
+GL_ENUM(0x8CD0,GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_OES)
+GL_ENUM(0x8CD1,GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_OES)
+GL_ENUM(0x8CD2,GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_OES)
+GL_ENUM(0x8CD3,GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_OES)
+GL_ENUM(0x8CD4,GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES)
+GL_ENUM(0x8CD5,GL_FRAMEBUFFER_COMPLETE_OES)
+GL_ENUM(0x8CD6,GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES)
+GL_ENUM(0x8CD7,GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES)
+GL_ENUM(0x8CD9,GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES)
+GL_ENUM(0x8CDA,GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES)
+GL_ENUM(0x8CDD,GL_FRAMEBUFFER_UNSUPPORTED_OES)
+GL_ENUM(0x8CE0,GL_COLOR_ATTACHMENT0_OES)
+GL_ENUM(0x8D00,GL_DEPTH_ATTACHMENT_OES)
+GL_ENUM(0x8D20,GL_STENCIL_ATTACHMENT_OES)
+GL_ENUM(0x8D40,GL_FRAMEBUFFER_OES)
+GL_ENUM(0x8D41,GL_RENDERBUFFER_OES)
+GL_ENUM(0x8D42,GL_RENDERBUFFER_WIDTH_OES)
+GL_ENUM(0x8D43,GL_RENDERBUFFER_HEIGHT_OES)
+GL_ENUM(0x8D44,GL_RENDERBUFFER_INTERNAL_FORMAT_OES)
+GL_ENUM(0x8D46,GL_STENCIL_INDEX1_OES)
+GL_ENUM(0x8D47,GL_STENCIL_INDEX4_OES)
+GL_ENUM(0x8D48,GL_STENCIL_INDEX8_OES)
+GL_ENUM(0x8D50,GL_RENDERBUFFER_RED_SIZE_OES)
+GL_ENUM(0x8D51,GL_RENDERBUFFER_GREEN_SIZE_OES)
+GL_ENUM(0x8D52,GL_RENDERBUFFER_BLUE_SIZE_OES)
+GL_ENUM(0x8D53,GL_RENDERBUFFER_ALPHA_SIZE_OES)
+GL_ENUM(0x8D54,GL_RENDERBUFFER_DEPTH_SIZE_OES)
+GL_ENUM(0x8D55,GL_RENDERBUFFER_STENCIL_SIZE_OES)
+GL_ENUM(0x8D60,GL_TEXTURE_GEN_STR_OES)
+GL_ENUM(0x8D61,GL_HALF_FLOAT_OES)
+GL_ENUM(0x8D62,GL_RGB565_OES)
+GL_ENUM(0x8D64,GL_ETC1_RGB8_OES)
+GL_ENUM(0x8D65,GL_TEXTURE_EXTERNAL_OES)
+GL_ENUM(0x8D66,GL_SAMPLER_EXTERNAL_OES)
+GL_ENUM(0x8D67,GL_TEXTURE_BINDING_EXTERNAL_OES)
+GL_ENUM(0x8D68,GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES)
+GL_ENUM(0x8DF0,GL_LOW_FLOAT)
+GL_ENUM(0x8DF1,GL_MEDIUM_FLOAT)
+GL_ENUM(0x8DF2,GL_HIGH_FLOAT)
+GL_ENUM(0x8DF3,GL_LOW_INT)
+GL_ENUM(0x8DF4,GL_MEDIUM_INT)
+GL_ENUM(0x8DF5,GL_HIGH_INT)
+GL_ENUM(0x8DF6,GL_UNSIGNED_INT_10_10_10_2_OES)
+GL_ENUM(0x8DF7,GL_INT_10_10_10_2_OES)
+GL_ENUM(0x8DF8,GL_SHADER_BINARY_FORMATS)
+GL_ENUM(0x8DF9,GL_NUM_SHADER_BINARY_FORMATS)
+GL_ENUM(0x8DFA,GL_SHADER_COMPILER)
+GL_ENUM(0x8DFB,GL_MAX_VERTEX_UNIFORM_VECTORS)
+GL_ENUM(0x8DFC,GL_MAX_VARYING_VECTORS)
+GL_ENUM(0x8DFD,GL_MAX_FRAGMENT_UNIFORM_VECTORS)
+GL_ENUM(0x8E2C,GL_DEPTH_COMPONENT16_NONLINEAR_NV)
+GL_ENUM(0x8ED0,GL_COVERAGE_COMPONENT_NV)
+GL_ENUM(0x8ED1,GL_COVERAGE_COMPONENT4_NV)
+GL_ENUM(0x8ED2,GL_COVERAGE_ATTACHMENT_NV)
+GL_ENUM(0x8ED3,GL_COVERAGE_BUFFERS_NV)
+GL_ENUM(0x8ED4,GL_COVERAGE_SAMPLES_NV)
+GL_ENUM(0x8ED5,GL_COVERAGE_ALL_FRAGMENTS_NV)
+GL_ENUM(0x8ED6,GL_COVERAGE_EDGE_FRAGMENTS_NV)
+GL_ENUM(0x8ED7,GL_COVERAGE_AUTOMATIC_NV)
+GL_ENUM(0x8FA0,GL_PERFMON_GLOBAL_MODE_QCOM)
+GL_ENUM(0x9130,GL_SGX_PROGRAM_BINARY_IMG)
+GL_ENUM(0x9133,GL_RENDERBUFFER_SAMPLES_IMG)
+GL_ENUM(0x9134,GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG)
+GL_ENUM(0x9135,GL_MAX_SAMPLES_IMG)
+GL_ENUM(0x9136,GL_TEXTURE_SAMPLES_IMG)
diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h
index 1ab58cc..812e26d 100644
--- a/opengl/libs/hooks.h
+++ b/opengl/libs/hooks.h
@@ -141,6 +141,11 @@
 
 #endif
 
+#if EGL_TRACE
+
+extern gl_hooks_t const* getGLTraceThreadSpecific();
+
+#endif
 
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/opengl/libs/tools/genfiles b/opengl/libs/tools/genfiles
index 120cb4b..50bbf08 100755
--- a/opengl/libs/tools/genfiles
+++ b/opengl/libs/tools/genfiles
@@ -31,3 +31,14 @@
         | sort -t, -k2 \
         | awk -F, '!_[$2]++' \
             > ../entries.in
+
+./gltracegen ../entries.in >../trace.in
+
+cat ../../include/GLES/gl.h \
+    ../../include/GLES/glext.h \
+    ../../include/GLES2/gl2.h \
+    ../../include/GLES2/gl2ext.h \
+        | ./glenumsgen \
+        | sort \
+        > ../enums.in
+
diff --git a/opengl/libs/tools/glenumsgen b/opengl/libs/tools/glenumsgen
new file mode 100755
index 0000000..2ae5fbf
--- /dev/null
+++ b/opengl/libs/tools/glenumsgen
@@ -0,0 +1,38 @@
+#! /usr/bin/perl
+#
+# Copyright (C) 2010 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.
+
+use strict;
+
+my %enumHash = ();
+
+while (my $line = <STDIN>) {
+  next if $line =~ /^\//;
+  # Skip bitfield definitions.
+  next if $line =~ /_BIT(\d+_|\s+)/;
+  if ($line !~ /^#define\s+(\S+)\s+(0x\S+)/) {
+    next;
+  }
+  my $enumName = $1;
+  my $enumValue = $2;
+  next if exists($enumHash { $enumValue });
+  $enumHash { $enumValue } = $enumName;
+  printf("GL_ENUM(%s,%s)\n", $enumValue, $enumName);
+}
+
+
+
+
+
diff --git a/opengl/libs/tools/gltracegen b/opengl/libs/tools/gltracegen
new file mode 100755
index 0000000..da42653
--- /dev/null
+++ b/opengl/libs/tools/gltracegen
@@ -0,0 +1,95 @@
+#! /usr/bin/perl
+#
+# Copyright (C) 2010 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.
+
+use strict;
+
+sub rtrim($)
+{
+    my $string = shift;
+    $string =~ s/\s+$//;
+    return $string;
+}
+
+while (my $line = <>) {
+  next if $line =~ /^\//;
+  next if $line =~ /^#/;
+  next if $line =~ /^\s*$/;
+  if ($line !~ /^GL_ENTRY\(([^,]+), ([^,]+), ([^\)]+)\)/) {
+    next;
+  }
+  my $type = $1;
+  my $name = $2;
+  my $args = $3;
+  
+  my @args = split ',', $args;
+  my $len = scalar(@args);
+  my $nonVoidArgLen = 0;
+  for (my $num = 0; $num < $len; $num++) {
+    if ($args[$num] ne "void") {
+      $nonVoidArgLen++;
+    }
+  }
+  if ($type eq "void") {
+    printf("TRACE_GL_VOID(");
+  } else {
+    printf("TRACE_GL(%s, ", $type);
+  }
+  
+  printf("%s, (%s), (", $name, $args);
+    for (my $num = 0; $num < $len; $num++) {
+    if ($args[$num] ne "void") {
+      if ($num > 0) {
+        print ", ";
+      }
+      #
+      # extract the name from the parameter
+      # type name
+      # const type *name
+      # type *name
+      # type name[4]
+      #
+      if ($args[$num] =~ /(\S+\s)+\**\s*([\w]+)/) {
+        printf("%s", $2);
+      }
+    }
+  }
+  printf("), %d", $nonVoidArgLen);
+  for (my $num = 0; $num < $len; $num++) {
+    if ($args[$num] ne "void") {
+      #
+      # extract the name from the parameter
+      # type name
+      # const type *name
+      # type *name
+      # type name[4]
+      #
+      my $arg = $args[$num];
+      if ($arg =~ /(\S+\s)+\**\s*([\w]+)/) {
+        my $name = $2;
+        if ($arg =~ /((const )*(\S+\s)+\**)\s*([\w]+)/) {
+          my $type = rtrim($1);
+          printf(", \"%s\", %s", $type, $name);
+        }
+      }
+    }
+  }
+  printf(")\n");
+}
+
+
+
+
+
diff --git a/opengl/libs/trace.in b/opengl/libs/trace.in
new file mode 100644
index 0000000..3d492af
--- /dev/null
+++ b/opengl/libs/trace.in
@@ -0,0 +1,376 @@
+TRACE_GL_VOID(glActiveTexture, (GLenum texture), (texture), 1, "GLenum", texture)
+TRACE_GL_VOID(glAlphaFunc, (GLenum func, GLclampf ref), (func, ref), 2, "GLenum", func, "GLclampf", ref)
+TRACE_GL_VOID(glAlphaFuncx, (GLenum func, GLclampx ref), (func, ref), 2, "GLenum", func, "GLclampx", ref)
+TRACE_GL_VOID(glAlphaFuncxOES, (GLenum func, GLclampx ref), (func, ref), 2, "GLenum", func, "GLclampx", ref)
+TRACE_GL_VOID(glAttachShader, (GLuint program, GLuint shader), (program, shader), 2, "GLuint", program, "GLuint", shader)
+TRACE_GL_VOID(glBeginPerfMonitorAMD, (GLuint monitor), (monitor), 1, "GLuint", monitor)
+TRACE_GL_VOID(glBindAttribLocation, (GLuint program, GLuint index, const GLchar* name), (program, index, name), 3, "GLuint", program, "GLuint", index, "const GLchar*", name)
+TRACE_GL_VOID(glBindBuffer, (GLenum target, GLuint buffer), (target, buffer), 2, "GLenum", target, "GLuint", buffer)
+TRACE_GL_VOID(glBindFramebuffer, (GLenum target, GLuint framebuffer), (target, framebuffer), 2, "GLenum", target, "GLuint", framebuffer)
+TRACE_GL_VOID(glBindFramebufferOES, (GLenum target, GLuint framebuffer), (target, framebuffer), 2, "GLenum", target, "GLuint", framebuffer)
+TRACE_GL_VOID(glBindRenderbuffer, (GLenum target, GLuint renderbuffer), (target, renderbuffer), 2, "GLenum", target, "GLuint", renderbuffer)
+TRACE_GL_VOID(glBindRenderbufferOES, (GLenum target, GLuint renderbuffer), (target, renderbuffer), 2, "GLenum", target, "GLuint", renderbuffer)
+TRACE_GL_VOID(glBindTexture, (GLenum target, GLuint texture), (target, texture), 2, "GLenum", target, "GLuint", texture)
+TRACE_GL_VOID(glBindVertexArrayOES, (GLuint array), (array), 1, "GLuint", array)
+TRACE_GL_VOID(glBlendColor, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha), (red, green, blue, alpha), 4, "GLclampf", red, "GLclampf", green, "GLclampf", blue, "GLclampf", alpha)
+TRACE_GL_VOID(glBlendEquation, ( GLenum mode ), (mode), 1, "GLenum", mode)
+TRACE_GL_VOID(glBlendEquationOES, (GLenum mode), (mode), 1, "GLenum", mode)
+TRACE_GL_VOID(glBlendEquationSeparate, (GLenum modeRGB, GLenum modeAlpha), (modeRGB, modeAlpha), 2, "GLenum", modeRGB, "GLenum", modeAlpha)
+TRACE_GL_VOID(glBlendEquationSeparateOES, (GLenum modeRGB, GLenum modeAlpha), (modeRGB, modeAlpha), 2, "GLenum", modeRGB, "GLenum", modeAlpha)
+TRACE_GL_VOID(glBlendFunc, (GLenum sfactor, GLenum dfactor), (sfactor, dfactor), 2, "GLenum", sfactor, "GLenum", dfactor)
+TRACE_GL_VOID(glBlendFuncSeparate, (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha), (srcRGB, dstRGB, srcAlpha, dstAlpha), 4, "GLenum", srcRGB, "GLenum", dstRGB, "GLenum", srcAlpha, "GLenum", dstAlpha)
+TRACE_GL_VOID(glBlendFuncSeparateOES, (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha), (srcRGB, dstRGB, srcAlpha, dstAlpha), 4, "GLenum", srcRGB, "GLenum", dstRGB, "GLenum", srcAlpha, "GLenum", dstAlpha)
+TRACE_GL_VOID(glBufferData, (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage), (target, size, data, usage), 4, "GLenum", target, "GLsizeiptr", size, "const GLvoid *", data, "GLenum", usage)
+TRACE_GL_VOID(glBufferSubData, (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data), (target, offset, size, data), 4, "GLenum", target, "GLintptr", offset, "GLsizeiptr", size, "const GLvoid *", data)
+TRACE_GL(GLenum, glCheckFramebufferStatus, (GLenum target), (target), 1, "GLenum", target)
+TRACE_GL(GLenum, glCheckFramebufferStatusOES, (GLenum target), (target), 1, "GLenum", target)
+TRACE_GL_VOID(glClear, (GLbitfield mask), (mask), 1, "GLbitfield", mask)
+TRACE_GL_VOID(glClearColor, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha), (red, green, blue, alpha), 4, "GLclampf", red, "GLclampf", green, "GLclampf", blue, "GLclampf", alpha)
+TRACE_GL_VOID(glClearColorx, (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha), (red, green, blue, alpha), 4, "GLclampx", red, "GLclampx", green, "GLclampx", blue, "GLclampx", alpha)
+TRACE_GL_VOID(glClearColorxOES, (GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha), (red, green, blue, alpha), 4, "GLclampx", red, "GLclampx", green, "GLclampx", blue, "GLclampx", alpha)
+TRACE_GL_VOID(glClearDepthf, (GLclampf depth), (depth), 1, "GLclampf", depth)
+TRACE_GL_VOID(glClearDepthfOES, (GLclampf depth), (depth), 1, "GLclampf", depth)
+TRACE_GL_VOID(glClearDepthx, (GLclampx depth), (depth), 1, "GLclampx", depth)
+TRACE_GL_VOID(glClearDepthxOES, (GLclampx depth), (depth), 1, "GLclampx", depth)
+TRACE_GL_VOID(glClearStencil, (GLint s), (s), 1, "GLint", s)
+TRACE_GL_VOID(glClientActiveTexture, (GLenum texture), (texture), 1, "GLenum", texture)
+TRACE_GL_VOID(glClipPlanef, (GLenum plane, const GLfloat *equation), (plane, equation), 2, "GLenum", plane, "const GLfloat *", equation)
+TRACE_GL_VOID(glClipPlanefIMG, (GLenum p, const GLfloat *eqn), (p, eqn), 2, "GLenum", p, "const GLfloat *", eqn)
+TRACE_GL_VOID(glClipPlanefOES, (GLenum plane, const GLfloat *equation), (plane, equation), 2, "GLenum", plane, "const GLfloat *", equation)
+TRACE_GL_VOID(glClipPlanex, (GLenum plane, const GLfixed *equation), (plane, equation), 2, "GLenum", plane, "const GLfixed *", equation)
+TRACE_GL_VOID(glClipPlanexIMG, (GLenum p, const GLfixed *eqn), (p, eqn), 2, "GLenum", p, "const GLfixed *", eqn)
+TRACE_GL_VOID(glClipPlanexOES, (GLenum plane, const GLfixed *equation), (plane, equation), 2, "GLenum", plane, "const GLfixed *", equation)
+TRACE_GL_VOID(glColor4f, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha), (red, green, blue, alpha), 4, "GLfloat", red, "GLfloat", green, "GLfloat", blue, "GLfloat", alpha)
+TRACE_GL_VOID(glColor4ub, (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha), (red, green, blue, alpha), 4, "GLubyte", red, "GLubyte", green, "GLubyte", blue, "GLubyte", alpha)
+TRACE_GL_VOID(glColor4x, (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha), (red, green, blue, alpha), 4, "GLfixed", red, "GLfixed", green, "GLfixed", blue, "GLfixed", alpha)
+TRACE_GL_VOID(glColor4xOES, (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha), (red, green, blue, alpha), 4, "GLfixed", red, "GLfixed", green, "GLfixed", blue, "GLfixed", alpha)
+TRACE_GL_VOID(glColorMask, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha), (red, green, blue, alpha), 4, "GLboolean", red, "GLboolean", green, "GLboolean", blue, "GLboolean", alpha)
+TRACE_GL_VOID(glColorPointer, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer), (size, type, stride, pointer), 4, "GLint", size, "GLenum", type, "GLsizei", stride, "const GLvoid *", pointer)
+TRACE_GL_VOID(glCompileShader, (GLuint shader), (shader), 1, "GLuint", shader)
+TRACE_GL_VOID(glCompressedTexImage2D, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data), (target, level, internalformat, width, height, border, imageSize, data), 8, "GLenum", target, "GLint", level, "GLenum", internalformat, "GLsizei", width, "GLsizei", height, "GLint", border, "GLsizei", imageSize, "const GLvoid *", data)
+TRACE_GL_VOID(glCompressedTexImage3DOES, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data), (target, level, internalformat, width, height, depth, border, imageSize, data), 9, "GLenum", target, "GLint", level, "GLenum", internalformat, "GLsizei", width, "GLsizei", height, "GLsizei", depth, "GLint", border, "GLsizei", imageSize, "const GLvoid*", data)
+TRACE_GL_VOID(glCompressedTexSubImage2D, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data), (target, level, xoffset, yoffset, width, height, format, imageSize, data), 9, "GLenum", target, "GLint", level, "GLint", xoffset, "GLint", yoffset, "GLsizei", width, "GLsizei", height, "GLenum", format, "GLsizei", imageSize, "const GLvoid *", data)
+TRACE_GL_VOID(glCompressedTexSubImage3DOES, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data), (target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data), 11, "GLenum", target, "GLint", level, "GLint", xoffset, "GLint", yoffset, "GLint", zoffset, "GLsizei", width, "GLsizei", height, "GLsizei", depth, "GLenum", format, "GLsizei", imageSize, "const GLvoid*", data)
+TRACE_GL_VOID(glCopyTexImage2D, (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border), (target, level, internalformat, x, y, width, height, border), 8, "GLenum", target, "GLint", level, "GLenum", internalformat, "GLint", x, "GLint", y, "GLsizei", width, "GLsizei", height, "GLint", border)
+TRACE_GL_VOID(glCopyTexSubImage2D, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height), (target, level, xoffset, yoffset, x, y, width, height), 8, "GLenum", target, "GLint", level, "GLint", xoffset, "GLint", yoffset, "GLint", x, "GLint", y, "GLsizei", width, "GLsizei", height)
+TRACE_GL_VOID(glCopyTexSubImage3DOES, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height), (target, level, xoffset, yoffset, zoffset, x, y, width, height), 9, "GLenum", target, "GLint", level, "GLint", xoffset, "GLint", yoffset, "GLint", zoffset, "GLint", x, "GLint", y, "GLsizei", width, "GLsizei", height)
+TRACE_GL_VOID(glCoverageMaskNV, (GLboolean mask), (mask), 1, "GLboolean", mask)
+TRACE_GL_VOID(glCoverageOperationNV, (GLenum operation), (operation), 1, "GLenum", operation)
+TRACE_GL(GLuint, glCreateProgram, (void), (), 0)
+TRACE_GL(GLuint, glCreateShader, (GLenum type), (type), 1, "GLenum", type)
+TRACE_GL_VOID(glCullFace, (GLenum mode), (mode), 1, "GLenum", mode)
+TRACE_GL_VOID(glCurrentPaletteMatrixOES, (GLuint matrixpaletteindex), (matrixpaletteindex), 1, "GLuint", matrixpaletteindex)
+TRACE_GL_VOID(glDeleteBuffers, (GLsizei n, const GLuint *buffers), (n, buffers), 2, "GLsizei", n, "const GLuint *", buffers)
+TRACE_GL_VOID(glDeleteFencesNV, (GLsizei n, const GLuint *fences), (n, fences), 2, "GLsizei", n, "const GLuint *", fences)
+TRACE_GL_VOID(glDeleteFramebuffers, (GLsizei n, const GLuint* framebuffers), (n, framebuffers), 2, "GLsizei", n, "const GLuint*", framebuffers)
+TRACE_GL_VOID(glDeleteFramebuffersOES, (GLsizei n, const GLuint* framebuffers), (n, framebuffers), 2, "GLsizei", n, "const GLuint*", framebuffers)
+TRACE_GL_VOID(glDeletePerfMonitorsAMD, (GLsizei n, GLuint *monitors), (n, monitors), 2, "GLsizei", n, "GLuint *", monitors)
+TRACE_GL_VOID(glDeleteProgram, (GLuint program), (program), 1, "GLuint", program)
+TRACE_GL_VOID(glDeleteRenderbuffers, (GLsizei n, const GLuint* renderbuffers), (n, renderbuffers), 2, "GLsizei", n, "const GLuint*", renderbuffers)
+TRACE_GL_VOID(glDeleteRenderbuffersOES, (GLsizei n, const GLuint* renderbuffers), (n, renderbuffers), 2, "GLsizei", n, "const GLuint*", renderbuffers)
+TRACE_GL_VOID(glDeleteShader, (GLuint shader), (shader), 1, "GLuint", shader)
+TRACE_GL_VOID(glDeleteTextures, (GLsizei n, const GLuint *textures), (n, textures), 2, "GLsizei", n, "const GLuint *", textures)
+TRACE_GL_VOID(glDeleteVertexArraysOES, (GLsizei n, const GLuint *arrays), (n, arrays), 2, "GLsizei", n, "const GLuint *", arrays)
+TRACE_GL_VOID(glDepthFunc, (GLenum func), (func), 1, "GLenum", func)
+TRACE_GL_VOID(glDepthMask, (GLboolean flag), (flag), 1, "GLboolean", flag)
+TRACE_GL_VOID(glDepthRangef, (GLclampf zNear, GLclampf zFar), (zNear, zFar), 2, "GLclampf", zNear, "GLclampf", zFar)
+TRACE_GL_VOID(glDepthRangefOES, (GLclampf zNear, GLclampf zFar), (zNear, zFar), 2, "GLclampf", zNear, "GLclampf", zFar)
+TRACE_GL_VOID(glDepthRangex, (GLclampx zNear, GLclampx zFar), (zNear, zFar), 2, "GLclampx", zNear, "GLclampx", zFar)
+TRACE_GL_VOID(glDepthRangexOES, (GLclampx zNear, GLclampx zFar), (zNear, zFar), 2, "GLclampx", zNear, "GLclampx", zFar)
+TRACE_GL_VOID(glDetachShader, (GLuint program, GLuint shader), (program, shader), 2, "GLuint", program, "GLuint", shader)
+TRACE_GL_VOID(glDisable, (GLenum cap), (cap), 1, "GLenum", cap)
+TRACE_GL_VOID(glDisableClientState, (GLenum array), (array), 1, "GLenum", array)
+TRACE_GL_VOID(glDisableDriverControlQCOM, (GLuint driverControl), (driverControl), 1, "GLuint", driverControl)
+TRACE_GL_VOID(glDisableVertexAttribArray, (GLuint index), (index), 1, "GLuint", index)
+TRACE_GL_VOID(glDiscardFramebufferEXT, (GLenum target, GLsizei numAttachments, const GLenum *attachments), (target, numAttachments, attachments), 3, "GLenum", target, "GLsizei", numAttachments, "const GLenum *", attachments)
+TRACE_GL_VOID(glDrawArrays, (GLenum mode, GLint first, GLsizei count), (mode, first, count), 3, "GLenum", mode, "GLint", first, "GLsizei", count)
+TRACE_GL_VOID(glDrawElements, (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices), (mode, count, type, indices), 4, "GLenum", mode, "GLsizei", count, "GLenum", type, "const GLvoid *", indices)
+TRACE_GL_VOID(glDrawTexfOES, (GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height), (x, y, z, width, height), 5, "GLfloat", x, "GLfloat", y, "GLfloat", z, "GLfloat", width, "GLfloat", height)
+TRACE_GL_VOID(glDrawTexfvOES, (const GLfloat *coords), (coords), 1, "const GLfloat *", coords)
+TRACE_GL_VOID(glDrawTexiOES, (GLint x, GLint y, GLint z, GLint width, GLint height), (x, y, z, width, height), 5, "GLint", x, "GLint", y, "GLint", z, "GLint", width, "GLint", height)
+TRACE_GL_VOID(glDrawTexivOES, (const GLint *coords), (coords), 1, "const GLint *", coords)
+TRACE_GL_VOID(glDrawTexsOES, (GLshort x, GLshort y, GLshort z, GLshort width, GLshort height), (x, y, z, width, height), 5, "GLshort", x, "GLshort", y, "GLshort", z, "GLshort", width, "GLshort", height)
+TRACE_GL_VOID(glDrawTexsvOES, (const GLshort *coords), (coords), 1, "const GLshort *", coords)
+TRACE_GL_VOID(glDrawTexxOES, (GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height), (x, y, z, width, height), 5, "GLfixed", x, "GLfixed", y, "GLfixed", z, "GLfixed", width, "GLfixed", height)
+TRACE_GL_VOID(glDrawTexxvOES, (const GLfixed *coords), (coords), 1, "const GLfixed *", coords)
+TRACE_GL_VOID(glEGLImageTargetRenderbufferStorageOES, (GLenum target, GLeglImageOES image), (target, image), 2, "GLenum", target, "GLeglImageOES", image)
+TRACE_GL_VOID(glEGLImageTargetTexture2DOES, (GLenum target, GLeglImageOES image), (target, image), 2, "GLenum", target, "GLeglImageOES", image)
+TRACE_GL_VOID(glEnable, (GLenum cap), (cap), 1, "GLenum", cap)
+TRACE_GL_VOID(glEnableClientState, (GLenum array), (array), 1, "GLenum", array)
+TRACE_GL_VOID(glEnableDriverControlQCOM, (GLuint driverControl), (driverControl), 1, "GLuint", driverControl)
+TRACE_GL_VOID(glEnableVertexAttribArray, (GLuint index), (index), 1, "GLuint", index)
+TRACE_GL_VOID(glEndPerfMonitorAMD, (GLuint monitor), (monitor), 1, "GLuint", monitor)
+TRACE_GL_VOID(glEndTilingQCOM, (GLbitfield preserveMask), (preserveMask), 1, "GLbitfield", preserveMask)
+TRACE_GL_VOID(glExtGetBufferPointervQCOM, (GLenum target, GLvoid **params), (target, params), 2, "GLenum", target, "GLvoid **", params)
+TRACE_GL_VOID(glExtGetBuffersQCOM, (GLuint *buffers, GLint maxBuffers, GLint *numBuffers), (buffers, maxBuffers, numBuffers), 3, "GLuint *", buffers, "GLint", maxBuffers, "GLint *", numBuffers)
+TRACE_GL_VOID(glExtGetFramebuffersQCOM, (GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers), (framebuffers, maxFramebuffers, numFramebuffers), 3, "GLuint *", framebuffers, "GLint", maxFramebuffers, "GLint *", numFramebuffers)
+TRACE_GL_VOID(glExtGetProgramBinarySourceQCOM, (GLuint program, GLenum shadertype, GLchar *source, GLint *length), (program, shadertype, source, length), 4, "GLuint", program, "GLenum", shadertype, "GLchar *", source, "GLint *", length)
+TRACE_GL_VOID(glExtGetProgramsQCOM, (GLuint *programs, GLint maxPrograms, GLint *numPrograms), (programs, maxPrograms, numPrograms), 3, "GLuint *", programs, "GLint", maxPrograms, "GLint *", numPrograms)
+TRACE_GL_VOID(glExtGetRenderbuffersQCOM, (GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers), (renderbuffers, maxRenderbuffers, numRenderbuffers), 3, "GLuint *", renderbuffers, "GLint", maxRenderbuffers, "GLint *", numRenderbuffers)
+TRACE_GL_VOID(glExtGetShadersQCOM, (GLuint *shaders, GLint maxShaders, GLint *numShaders), (shaders, maxShaders, numShaders), 3, "GLuint *", shaders, "GLint", maxShaders, "GLint *", numShaders)
+TRACE_GL_VOID(glExtGetTexLevelParameterivQCOM, (GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params), (texture, face, level, pname, params), 5, "GLuint", texture, "GLenum", face, "GLint", level, "GLenum", pname, "GLint *", params)
+TRACE_GL_VOID(glExtGetTexSubImageQCOM, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLvoid *texels), (target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, texels), 11, "GLenum", target, "GLint", level, "GLint", xoffset, "GLint", yoffset, "GLint", zoffset, "GLsizei", width, "GLsizei", height, "GLsizei", depth, "GLenum", format, "GLenum", type, "GLvoid *", texels)
+TRACE_GL_VOID(glExtGetTexturesQCOM, (GLuint *textures, GLint maxTextures, GLint *numTextures), (textures, maxTextures, numTextures), 3, "GLuint *", textures, "GLint", maxTextures, "GLint *", numTextures)
+TRACE_GL(GLboolean, glExtIsProgramBinaryQCOM, (GLuint program), (program), 1, "GLuint", program)
+TRACE_GL_VOID(glExtTexObjectStateOverrideiQCOM, (GLenum target, GLenum pname, GLint param), (target, pname, param), 3, "GLenum", target, "GLenum", pname, "GLint", param)
+TRACE_GL_VOID(glFinish, (void), (), 0)
+TRACE_GL_VOID(glFinishFenceNV, (GLuint fence), (fence), 1, "GLuint", fence)
+TRACE_GL_VOID(glFlush, (void), (), 0)
+TRACE_GL_VOID(glFogf, (GLenum pname, GLfloat param), (pname, param), 2, "GLenum", pname, "GLfloat", param)
+TRACE_GL_VOID(glFogfv, (GLenum pname, const GLfloat *params), (pname, params), 2, "GLenum", pname, "const GLfloat *", params)
+TRACE_GL_VOID(glFogx, (GLenum pname, GLfixed param), (pname, param), 2, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glFogxOES, (GLenum pname, GLfixed param), (pname, param), 2, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glFogxv, (GLenum pname, const GLfixed *params), (pname, params), 2, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glFogxvOES, (GLenum pname, const GLfixed *params), (pname, params), 2, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glFramebufferRenderbuffer, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer), (target, attachment, renderbuffertarget, renderbuffer), 4, "GLenum", target, "GLenum", attachment, "GLenum", renderbuffertarget, "GLuint", renderbuffer)
+TRACE_GL_VOID(glFramebufferRenderbufferOES, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer), (target, attachment, renderbuffertarget, renderbuffer), 4, "GLenum", target, "GLenum", attachment, "GLenum", renderbuffertarget, "GLuint", renderbuffer)
+TRACE_GL_VOID(glFramebufferTexture2D, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level), (target, attachment, textarget, texture, level), 5, "GLenum", target, "GLenum", attachment, "GLenum", textarget, "GLuint", texture, "GLint", level)
+TRACE_GL_VOID(glFramebufferTexture2DMultisampleIMG, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples), (target, attachment, textarget, texture, level, samples), 6, "GLenum", target, "GLenum", attachment, "GLenum", textarget, "GLuint", texture, "GLint", level, "GLsizei", samples)
+TRACE_GL_VOID(glFramebufferTexture2DOES, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level), (target, attachment, textarget, texture, level), 5, "GLenum", target, "GLenum", attachment, "GLenum", textarget, "GLuint", texture, "GLint", level)
+TRACE_GL_VOID(glFramebufferTexture3DOES, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset), (target, attachment, textarget, texture, level, zoffset), 6, "GLenum", target, "GLenum", attachment, "GLenum", textarget, "GLuint", texture, "GLint", level, "GLint", zoffset)
+TRACE_GL_VOID(glFrontFace, (GLenum mode), (mode), 1, "GLenum", mode)
+TRACE_GL_VOID(glFrustumf, (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar), (left, right, bottom, top, zNear, zFar), 6, "GLfloat", left, "GLfloat", right, "GLfloat", bottom, "GLfloat", top, "GLfloat", zNear, "GLfloat", zFar)
+TRACE_GL_VOID(glFrustumfOES, (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar), (left, right, bottom, top, zNear, zFar), 6, "GLfloat", left, "GLfloat", right, "GLfloat", bottom, "GLfloat", top, "GLfloat", zNear, "GLfloat", zFar)
+TRACE_GL_VOID(glFrustumx, (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar), (left, right, bottom, top, zNear, zFar), 6, "GLfixed", left, "GLfixed", right, "GLfixed", bottom, "GLfixed", top, "GLfixed", zNear, "GLfixed", zFar)
+TRACE_GL_VOID(glFrustumxOES, (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar), (left, right, bottom, top, zNear, zFar), 6, "GLfixed", left, "GLfixed", right, "GLfixed", bottom, "GLfixed", top, "GLfixed", zNear, "GLfixed", zFar)
+TRACE_GL_VOID(glGenBuffers, (GLsizei n, GLuint *buffers), (n, buffers), 2, "GLsizei", n, "GLuint *", buffers)
+TRACE_GL_VOID(glGenFencesNV, (GLsizei n, GLuint *fences), (n, fences), 2, "GLsizei", n, "GLuint *", fences)
+TRACE_GL_VOID(glGenFramebuffers, (GLsizei n, GLuint* framebuffers), (n, framebuffers), 2, "GLsizei", n, "GLuint*", framebuffers)
+TRACE_GL_VOID(glGenFramebuffersOES, (GLsizei n, GLuint* framebuffers), (n, framebuffers), 2, "GLsizei", n, "GLuint*", framebuffers)
+TRACE_GL_VOID(glGenPerfMonitorsAMD, (GLsizei n, GLuint *monitors), (n, monitors), 2, "GLsizei", n, "GLuint *", monitors)
+TRACE_GL_VOID(glGenRenderbuffers, (GLsizei n, GLuint* renderbuffers), (n, renderbuffers), 2, "GLsizei", n, "GLuint*", renderbuffers)
+TRACE_GL_VOID(glGenRenderbuffersOES, (GLsizei n, GLuint* renderbuffers), (n, renderbuffers), 2, "GLsizei", n, "GLuint*", renderbuffers)
+TRACE_GL_VOID(glGenTextures, (GLsizei n, GLuint *textures), (n, textures), 2, "GLsizei", n, "GLuint *", textures)
+TRACE_GL_VOID(glGenVertexArraysOES, (GLsizei n, GLuint *arrays), (n, arrays), 2, "GLsizei", n, "GLuint *", arrays)
+TRACE_GL_VOID(glGenerateMipmap, (GLenum target), (target), 1, "GLenum", target)
+TRACE_GL_VOID(glGenerateMipmapOES, (GLenum target), (target), 1, "GLenum", target)
+TRACE_GL_VOID(glGetActiveAttrib, (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name), (program, index, bufsize, length, size, type, name), 7, "GLuint", program, "GLuint", index, "GLsizei", bufsize, "GLsizei*", length, "GLint*", size, "GLenum*", type, "GLchar*", name)
+TRACE_GL_VOID(glGetActiveUniform, (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name), (program, index, bufsize, length, size, type, name), 7, "GLuint", program, "GLuint", index, "GLsizei", bufsize, "GLsizei*", length, "GLint*", size, "GLenum*", type, "GLchar*", name)
+TRACE_GL_VOID(glGetAttachedShaders, (GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders), (program, maxcount, count, shaders), 4, "GLuint", program, "GLsizei", maxcount, "GLsizei*", count, "GLuint*", shaders)
+TRACE_GL(int, glGetAttribLocation, (GLuint program, const GLchar* name), (program, name), 2, "GLuint", program, "const GLchar*", name)
+TRACE_GL_VOID(glGetBooleanv, (GLenum pname, GLboolean *params), (pname, params), 2, "GLenum", pname, "GLboolean *", params)
+TRACE_GL_VOID(glGetBufferParameteriv, (GLenum target, GLenum pname, GLint *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "GLint *", params)
+TRACE_GL_VOID(glGetBufferPointervOES, (GLenum target, GLenum pname, GLvoid ** params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "GLvoid **", params)
+TRACE_GL_VOID(glGetClipPlanef, (GLenum pname, GLfloat eqn[4]), (pname, eqn), 2, "GLenum", pname, "GLfloat", eqn)
+TRACE_GL_VOID(glGetClipPlanefOES, (GLenum pname, GLfloat eqn[4]), (pname, eqn), 2, "GLenum", pname, "GLfloat", eqn)
+TRACE_GL_VOID(glGetClipPlanex, (GLenum pname, GLfixed eqn[4]), (pname, eqn), 2, "GLenum", pname, "GLfixed", eqn)
+TRACE_GL_VOID(glGetClipPlanexOES, (GLenum pname, GLfixed eqn[4]), (pname, eqn), 2, "GLenum", pname, "GLfixed", eqn)
+TRACE_GL_VOID(glGetDriverControlStringQCOM, (GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString), (driverControl, bufSize, length, driverControlString), 4, "GLuint", driverControl, "GLsizei", bufSize, "GLsizei *", length, "GLchar *", driverControlString)
+TRACE_GL_VOID(glGetDriverControlsQCOM, (GLint *num, GLsizei size, GLuint *driverControls), (num, size, driverControls), 3, "GLint *", num, "GLsizei", size, "GLuint *", driverControls)
+TRACE_GL(GLenum, glGetError, (void), (), 0)
+TRACE_GL_VOID(glGetFenceivNV, (GLuint fence, GLenum pname, GLint *params), (fence, pname, params), 3, "GLuint", fence, "GLenum", pname, "GLint *", params)
+TRACE_GL_VOID(glGetFixedv, (GLenum pname, GLfixed *params), (pname, params), 2, "GLenum", pname, "GLfixed *", params)
+TRACE_GL_VOID(glGetFixedvOES, (GLenum pname, GLfixed *params), (pname, params), 2, "GLenum", pname, "GLfixed *", params)
+TRACE_GL_VOID(glGetFloatv, (GLenum pname, GLfloat *params), (pname, params), 2, "GLenum", pname, "GLfloat *", params)
+TRACE_GL_VOID(glGetFramebufferAttachmentParameteriv, (GLenum target, GLenum attachment, GLenum pname, GLint* params), (target, attachment, pname, params), 4, "GLenum", target, "GLenum", attachment, "GLenum", pname, "GLint*", params)
+TRACE_GL_VOID(glGetFramebufferAttachmentParameterivOES, (GLenum target, GLenum attachment, GLenum pname, GLint* params), (target, attachment, pname, params), 4, "GLenum", target, "GLenum", attachment, "GLenum", pname, "GLint*", params)
+TRACE_GL_VOID(glGetIntegerv, (GLenum pname, GLint *params), (pname, params), 2, "GLenum", pname, "GLint *", params)
+TRACE_GL_VOID(glGetLightfv, (GLenum light, GLenum pname, GLfloat *params), (light, pname, params), 3, "GLenum", light, "GLenum", pname, "GLfloat *", params)
+TRACE_GL_VOID(glGetLightxv, (GLenum light, GLenum pname, GLfixed *params), (light, pname, params), 3, "GLenum", light, "GLenum", pname, "GLfixed *", params)
+TRACE_GL_VOID(glGetLightxvOES, (GLenum light, GLenum pname, GLfixed *params), (light, pname, params), 3, "GLenum", light, "GLenum", pname, "GLfixed *", params)
+TRACE_GL_VOID(glGetMaterialfv, (GLenum face, GLenum pname, GLfloat *params), (face, pname, params), 3, "GLenum", face, "GLenum", pname, "GLfloat *", params)
+TRACE_GL_VOID(glGetMaterialxv, (GLenum face, GLenum pname, GLfixed *params), (face, pname, params), 3, "GLenum", face, "GLenum", pname, "GLfixed *", params)
+TRACE_GL_VOID(glGetMaterialxvOES, (GLenum face, GLenum pname, GLfixed *params), (face, pname, params), 3, "GLenum", face, "GLenum", pname, "GLfixed *", params)
+TRACE_GL_VOID(glGetPerfMonitorCounterDataAMD, (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten), (monitor, pname, dataSize, data, bytesWritten), 5, "GLuint", monitor, "GLenum", pname, "GLsizei", dataSize, "GLuint *", data, "GLint *", bytesWritten)
+TRACE_GL_VOID(glGetPerfMonitorCounterInfoAMD, (GLuint group, GLuint counter, GLenum pname, GLvoid *data), (group, counter, pname, data), 4, "GLuint", group, "GLuint", counter, "GLenum", pname, "GLvoid *", data)
+TRACE_GL_VOID(glGetPerfMonitorCounterStringAMD, (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString), (group, counter, bufSize, length, counterString), 5, "GLuint", group, "GLuint", counter, "GLsizei", bufSize, "GLsizei *", length, "GLchar *", counterString)
+TRACE_GL_VOID(glGetPerfMonitorCountersAMD, (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters), (group, numCounters, maxActiveCounters, counterSize, counters), 5, "GLuint", group, "GLint *", numCounters, "GLint *", maxActiveCounters, "GLsizei", counterSize, "GLuint *", counters)
+TRACE_GL_VOID(glGetPerfMonitorGroupStringAMD, (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString), (group, bufSize, length, groupString), 4, "GLuint", group, "GLsizei", bufSize, "GLsizei *", length, "GLchar *", groupString)
+TRACE_GL_VOID(glGetPerfMonitorGroupsAMD, (GLint *numGroups, GLsizei groupsSize, GLuint *groups), (numGroups, groupsSize, groups), 3, "GLint *", numGroups, "GLsizei", groupsSize, "GLuint *", groups)
+TRACE_GL_VOID(glGetPointerv, (GLenum pname, GLvoid **params), (pname, params), 2, "GLenum", pname, "GLvoid **", params)
+TRACE_GL_VOID(glGetProgramBinaryOES, (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary), (program, bufSize, length, binaryFormat, binary), 5, "GLuint", program, "GLsizei", bufSize, "GLsizei *", length, "GLenum *", binaryFormat, "GLvoid *", binary)
+TRACE_GL_VOID(glGetProgramInfoLog, (GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog), (program, bufsize, length, infolog), 4, "GLuint", program, "GLsizei", bufsize, "GLsizei*", length, "GLchar*", infolog)
+TRACE_GL_VOID(glGetProgramiv, (GLuint program, GLenum pname, GLint* params), (program, pname, params), 3, "GLuint", program, "GLenum", pname, "GLint*", params)
+TRACE_GL_VOID(glGetRenderbufferParameteriv, (GLenum target, GLenum pname, GLint* params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "GLint*", params)
+TRACE_GL_VOID(glGetRenderbufferParameterivOES, (GLenum target, GLenum pname, GLint* params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "GLint*", params)
+TRACE_GL_VOID(glGetShaderInfoLog, (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog), (shader, bufsize, length, infolog), 4, "GLuint", shader, "GLsizei", bufsize, "GLsizei*", length, "GLchar*", infolog)
+TRACE_GL_VOID(glGetShaderPrecisionFormat, (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision), (shadertype, precisiontype, range, precision), 4, "GLenum", shadertype, "GLenum", precisiontype, "GLint*", range, "GLint*", precision)
+TRACE_GL_VOID(glGetShaderSource, (GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source), (shader, bufsize, length, source), 4, "GLuint", shader, "GLsizei", bufsize, "GLsizei*", length, "GLchar*", source)
+TRACE_GL_VOID(glGetShaderiv, (GLuint shader, GLenum pname, GLint* params), (shader, pname, params), 3, "GLuint", shader, "GLenum", pname, "GLint*", params)
+TRACE_GL(const GLubyte *, glGetString, (GLenum name), (name), 1, "GLenum", name)
+TRACE_GL_VOID(glGetTexEnvfv, (GLenum env, GLenum pname, GLfloat *params), (env, pname, params), 3, "GLenum", env, "GLenum", pname, "GLfloat *", params)
+TRACE_GL_VOID(glGetTexEnviv, (GLenum env, GLenum pname, GLint *params), (env, pname, params), 3, "GLenum", env, "GLenum", pname, "GLint *", params)
+TRACE_GL_VOID(glGetTexEnvxv, (GLenum env, GLenum pname, GLfixed *params), (env, pname, params), 3, "GLenum", env, "GLenum", pname, "GLfixed *", params)
+TRACE_GL_VOID(glGetTexEnvxvOES, (GLenum env, GLenum pname, GLfixed *params), (env, pname, params), 3, "GLenum", env, "GLenum", pname, "GLfixed *", params)
+TRACE_GL_VOID(glGetTexGenfvOES, (GLenum coord, GLenum pname, GLfloat *params), (coord, pname, params), 3, "GLenum", coord, "GLenum", pname, "GLfloat *", params)
+TRACE_GL_VOID(glGetTexGenivOES, (GLenum coord, GLenum pname, GLint *params), (coord, pname, params), 3, "GLenum", coord, "GLenum", pname, "GLint *", params)
+TRACE_GL_VOID(glGetTexGenxvOES, (GLenum coord, GLenum pname, GLfixed *params), (coord, pname, params), 3, "GLenum", coord, "GLenum", pname, "GLfixed *", params)
+TRACE_GL_VOID(glGetTexParameterfv, (GLenum target, GLenum pname, GLfloat *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "GLfloat *", params)
+TRACE_GL_VOID(glGetTexParameteriv, (GLenum target, GLenum pname, GLint *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "GLint *", params)
+TRACE_GL_VOID(glGetTexParameterxv, (GLenum target, GLenum pname, GLfixed *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "GLfixed *", params)
+TRACE_GL_VOID(glGetTexParameterxvOES, (GLenum target, GLenum pname, GLfixed *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "GLfixed *", params)
+TRACE_GL(int, glGetUniformLocation, (GLuint program, const GLchar* name), (program, name), 2, "GLuint", program, "const GLchar*", name)
+TRACE_GL_VOID(glGetUniformfv, (GLuint program, GLint location, GLfloat* params), (program, location, params), 3, "GLuint", program, "GLint", location, "GLfloat*", params)
+TRACE_GL_VOID(glGetUniformiv, (GLuint program, GLint location, GLint* params), (program, location, params), 3, "GLuint", program, "GLint", location, "GLint*", params)
+TRACE_GL_VOID(glGetVertexAttribPointerv, (GLuint index, GLenum pname, GLvoid** pointer), (index, pname, pointer), 3, "GLuint", index, "GLenum", pname, "GLvoid**", pointer)
+TRACE_GL_VOID(glGetVertexAttribfv, (GLuint index, GLenum pname, GLfloat* params), (index, pname, params), 3, "GLuint", index, "GLenum", pname, "GLfloat*", params)
+TRACE_GL_VOID(glGetVertexAttribiv, (GLuint index, GLenum pname, GLint* params), (index, pname, params), 3, "GLuint", index, "GLenum", pname, "GLint*", params)
+TRACE_GL_VOID(glHint, (GLenum target, GLenum mode), (target, mode), 2, "GLenum", target, "GLenum", mode)
+TRACE_GL(GLboolean, glIsBuffer, (GLuint buffer), (buffer), 1, "GLuint", buffer)
+TRACE_GL(GLboolean, glIsEnabled, (GLenum cap), (cap), 1, "GLenum", cap)
+TRACE_GL(GLboolean, glIsFenceNV, (GLuint fence), (fence), 1, "GLuint", fence)
+TRACE_GL(GLboolean, glIsFramebuffer, (GLuint framebuffer), (framebuffer), 1, "GLuint", framebuffer)
+TRACE_GL(GLboolean, glIsFramebufferOES, (GLuint framebuffer), (framebuffer), 1, "GLuint", framebuffer)
+TRACE_GL(GLboolean, glIsProgram, (GLuint program), (program), 1, "GLuint", program)
+TRACE_GL(GLboolean, glIsRenderbuffer, (GLuint renderbuffer), (renderbuffer), 1, "GLuint", renderbuffer)
+TRACE_GL(GLboolean, glIsRenderbufferOES, (GLuint renderbuffer), (renderbuffer), 1, "GLuint", renderbuffer)
+TRACE_GL(GLboolean, glIsShader, (GLuint shader), (shader), 1, "GLuint", shader)
+TRACE_GL(GLboolean, glIsTexture, (GLuint texture), (texture), 1, "GLuint", texture)
+TRACE_GL(GLboolean, glIsVertexArrayOES, (GLuint array), (array), 1, "GLuint", array)
+TRACE_GL_VOID(glLightModelf, (GLenum pname, GLfloat param), (pname, param), 2, "GLenum", pname, "GLfloat", param)
+TRACE_GL_VOID(glLightModelfv, (GLenum pname, const GLfloat *params), (pname, params), 2, "GLenum", pname, "const GLfloat *", params)
+TRACE_GL_VOID(glLightModelx, (GLenum pname, GLfixed param), (pname, param), 2, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glLightModelxOES, (GLenum pname, GLfixed param), (pname, param), 2, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glLightModelxv, (GLenum pname, const GLfixed *params), (pname, params), 2, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glLightModelxvOES, (GLenum pname, const GLfixed *params), (pname, params), 2, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glLightf, (GLenum light, GLenum pname, GLfloat param), (light, pname, param), 3, "GLenum", light, "GLenum", pname, "GLfloat", param)
+TRACE_GL_VOID(glLightfv, (GLenum light, GLenum pname, const GLfloat *params), (light, pname, params), 3, "GLenum", light, "GLenum", pname, "const GLfloat *", params)
+TRACE_GL_VOID(glLightx, (GLenum light, GLenum pname, GLfixed param), (light, pname, param), 3, "GLenum", light, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glLightxOES, (GLenum light, GLenum pname, GLfixed param), (light, pname, param), 3, "GLenum", light, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glLightxv, (GLenum light, GLenum pname, const GLfixed *params), (light, pname, params), 3, "GLenum", light, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glLightxvOES, (GLenum light, GLenum pname, const GLfixed *params), (light, pname, params), 3, "GLenum", light, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glLineWidth, (GLfloat width), (width), 1, "GLfloat", width)
+TRACE_GL_VOID(glLineWidthx, (GLfixed width), (width), 1, "GLfixed", width)
+TRACE_GL_VOID(glLineWidthxOES, (GLfixed width), (width), 1, "GLfixed", width)
+TRACE_GL_VOID(glLinkProgram, (GLuint program), (program), 1, "GLuint", program)
+TRACE_GL_VOID(glLoadIdentity, (void), (), 0)
+TRACE_GL_VOID(glLoadMatrixf, (const GLfloat *m), (m), 1, "const GLfloat *", m)
+TRACE_GL_VOID(glLoadMatrixx, (const GLfixed *m), (m), 1, "const GLfixed *", m)
+TRACE_GL_VOID(glLoadMatrixxOES, (const GLfixed *m), (m), 1, "const GLfixed *", m)
+TRACE_GL_VOID(glLoadPaletteFromModelViewMatrixOES, (void), (), 0)
+TRACE_GL_VOID(glLogicOp, (GLenum opcode), (opcode), 1, "GLenum", opcode)
+TRACE_GL(void*, glMapBufferOES, (GLenum target, GLenum access), (target, access), 2, "GLenum", target, "GLenum", access)
+TRACE_GL_VOID(glMaterialf, (GLenum face, GLenum pname, GLfloat param), (face, pname, param), 3, "GLenum", face, "GLenum", pname, "GLfloat", param)
+TRACE_GL_VOID(glMaterialfv, (GLenum face, GLenum pname, const GLfloat *params), (face, pname, params), 3, "GLenum", face, "GLenum", pname, "const GLfloat *", params)
+TRACE_GL_VOID(glMaterialx, (GLenum face, GLenum pname, GLfixed param), (face, pname, param), 3, "GLenum", face, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glMaterialxOES, (GLenum face, GLenum pname, GLfixed param), (face, pname, param), 3, "GLenum", face, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glMaterialxv, (GLenum face, GLenum pname, const GLfixed *params), (face, pname, params), 3, "GLenum", face, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glMaterialxvOES, (GLenum face, GLenum pname, const GLfixed *params), (face, pname, params), 3, "GLenum", face, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glMatrixIndexPointerOES, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer), (size, type, stride, pointer), 4, "GLint", size, "GLenum", type, "GLsizei", stride, "const GLvoid *", pointer)
+TRACE_GL_VOID(glMatrixMode, (GLenum mode), (mode), 1, "GLenum", mode)
+TRACE_GL_VOID(glMultMatrixf, (const GLfloat *m), (m), 1, "const GLfloat *", m)
+TRACE_GL_VOID(glMultMatrixx, (const GLfixed *m), (m), 1, "const GLfixed *", m)
+TRACE_GL_VOID(glMultMatrixxOES, (const GLfixed *m), (m), 1, "const GLfixed *", m)
+TRACE_GL_VOID(glMultiDrawArraysEXT, (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount), (mode, first, count, primcount), 4, "GLenum", mode, "GLint *", first, "GLsizei *", count, "GLsizei", primcount)
+TRACE_GL_VOID(glMultiDrawElementsEXT, (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount), (mode, count, type, indices, primcount), 5, "GLenum", mode, "const GLsizei *", count, "GLenum", type, "const GLvoid* *", indices, "GLsizei", primcount)
+TRACE_GL_VOID(glMultiTexCoord4f, (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q), (target, s, t, r, q), 5, "GLenum", target, "GLfloat", s, "GLfloat", t, "GLfloat", r, "GLfloat", q)
+TRACE_GL_VOID(glMultiTexCoord4x, (GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q), (target, s, t, r, q), 5, "GLenum", target, "GLfixed", s, "GLfixed", t, "GLfixed", r, "GLfixed", q)
+TRACE_GL_VOID(glMultiTexCoord4xOES, (GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q), (target, s, t, r, q), 5, "GLenum", target, "GLfixed", s, "GLfixed", t, "GLfixed", r, "GLfixed", q)
+TRACE_GL_VOID(glNormal3f, (GLfloat nx, GLfloat ny, GLfloat nz), (nx, ny, nz), 3, "GLfloat", nx, "GLfloat", ny, "GLfloat", nz)
+TRACE_GL_VOID(glNormal3x, (GLfixed nx, GLfixed ny, GLfixed nz), (nx, ny, nz), 3, "GLfixed", nx, "GLfixed", ny, "GLfixed", nz)
+TRACE_GL_VOID(glNormal3xOES, (GLfixed nx, GLfixed ny, GLfixed nz), (nx, ny, nz), 3, "GLfixed", nx, "GLfixed", ny, "GLfixed", nz)
+TRACE_GL_VOID(glNormalPointer, (GLenum type, GLsizei stride, const GLvoid *pointer), (type, stride, pointer), 3, "GLenum", type, "GLsizei", stride, "const GLvoid *", pointer)
+TRACE_GL_VOID(glOrthof, (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar), (left, right, bottom, top, zNear, zFar), 6, "GLfloat", left, "GLfloat", right, "GLfloat", bottom, "GLfloat", top, "GLfloat", zNear, "GLfloat", zFar)
+TRACE_GL_VOID(glOrthofOES, (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat zNear, GLfloat zFar), (left, right, bottom, top, zNear, zFar), 6, "GLfloat", left, "GLfloat", right, "GLfloat", bottom, "GLfloat", top, "GLfloat", zNear, "GLfloat", zFar)
+TRACE_GL_VOID(glOrthox, (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar), (left, right, bottom, top, zNear, zFar), 6, "GLfixed", left, "GLfixed", right, "GLfixed", bottom, "GLfixed", top, "GLfixed", zNear, "GLfixed", zFar)
+TRACE_GL_VOID(glOrthoxOES, (GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, GLfixed zNear, GLfixed zFar), (left, right, bottom, top, zNear, zFar), 6, "GLfixed", left, "GLfixed", right, "GLfixed", bottom, "GLfixed", top, "GLfixed", zNear, "GLfixed", zFar)
+TRACE_GL_VOID(glPixelStorei, (GLenum pname, GLint param), (pname, param), 2, "GLenum", pname, "GLint", param)
+TRACE_GL_VOID(glPointParameterf, (GLenum pname, GLfloat param), (pname, param), 2, "GLenum", pname, "GLfloat", param)
+TRACE_GL_VOID(glPointParameterfv, (GLenum pname, const GLfloat *params), (pname, params), 2, "GLenum", pname, "const GLfloat *", params)
+TRACE_GL_VOID(glPointParameterx, (GLenum pname, GLfixed param), (pname, param), 2, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glPointParameterxOES, (GLenum pname, GLfixed param), (pname, param), 2, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glPointParameterxv, (GLenum pname, const GLfixed *params), (pname, params), 2, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glPointParameterxvOES, (GLenum pname, const GLfixed *params), (pname, params), 2, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glPointSize, (GLfloat size), (size), 1, "GLfloat", size)
+TRACE_GL_VOID(glPointSizePointerOES, (GLenum type, GLsizei stride, const GLvoid *pointer), (type, stride, pointer), 3, "GLenum", type, "GLsizei", stride, "const GLvoid *", pointer)
+TRACE_GL_VOID(glPointSizex, (GLfixed size), (size), 1, "GLfixed", size)
+TRACE_GL_VOID(glPointSizexOES, (GLfixed size), (size), 1, "GLfixed", size)
+TRACE_GL_VOID(glPolygonOffset, (GLfloat factor, GLfloat units), (factor, units), 2, "GLfloat", factor, "GLfloat", units)
+TRACE_GL_VOID(glPolygonOffsetx, (GLfixed factor, GLfixed units), (factor, units), 2, "GLfixed", factor, "GLfixed", units)
+TRACE_GL_VOID(glPolygonOffsetxOES, (GLfixed factor, GLfixed units), (factor, units), 2, "GLfixed", factor, "GLfixed", units)
+TRACE_GL_VOID(glPopMatrix, (void), (), 0)
+TRACE_GL_VOID(glProgramBinaryOES, (GLuint program, GLenum binaryFormat, const GLvoid *binary, GLint length), (program, binaryFormat, binary, length), 4, "GLuint", program, "GLenum", binaryFormat, "const GLvoid *", binary, "GLint", length)
+TRACE_GL_VOID(glPushMatrix, (void), (), 0)
+TRACE_GL(GLbitfield, glQueryMatrixxOES, (GLfixed mantissa[16], GLint exponent[16]), (mantissa, exponent), 2, "GLfixed", mantissa, "GLint", exponent)
+TRACE_GL_VOID(glReadPixels, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels), (x, y, width, height, format, type, pixels), 7, "GLint", x, "GLint", y, "GLsizei", width, "GLsizei", height, "GLenum", format, "GLenum", type, "GLvoid *", pixels)
+TRACE_GL_VOID(glReleaseShaderCompiler, (void), (), 0)
+TRACE_GL_VOID(glRenderbufferStorage, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height), (target, internalformat, width, height), 4, "GLenum", target, "GLenum", internalformat, "GLsizei", width, "GLsizei", height)
+TRACE_GL_VOID(glRenderbufferStorageMultisampleIMG, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height), (target, samples, internalformat, width, height), 5, "GLenum", target, "GLsizei", samples, "GLenum", internalformat, "GLsizei", width, "GLsizei", height)
+TRACE_GL_VOID(glRenderbufferStorageOES, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height), (target, internalformat, width, height), 4, "GLenum", target, "GLenum", internalformat, "GLsizei", width, "GLsizei", height)
+TRACE_GL_VOID(glRotatef, (GLfloat angle, GLfloat x, GLfloat y, GLfloat z), (angle, x, y, z), 4, "GLfloat", angle, "GLfloat", x, "GLfloat", y, "GLfloat", z)
+TRACE_GL_VOID(glRotatex, (GLfixed angle, GLfixed x, GLfixed y, GLfixed z), (angle, x, y, z), 4, "GLfixed", angle, "GLfixed", x, "GLfixed", y, "GLfixed", z)
+TRACE_GL_VOID(glRotatexOES, (GLfixed angle, GLfixed x, GLfixed y, GLfixed z), (angle, x, y, z), 4, "GLfixed", angle, "GLfixed", x, "GLfixed", y, "GLfixed", z)
+TRACE_GL_VOID(glSampleCoverage, (GLclampf value, GLboolean invert), (value, invert), 2, "GLclampf", value, "GLboolean", invert)
+TRACE_GL_VOID(glSampleCoveragex, (GLclampx value, GLboolean invert), (value, invert), 2, "GLclampx", value, "GLboolean", invert)
+TRACE_GL_VOID(glSampleCoveragexOES, (GLclampx value, GLboolean invert), (value, invert), 2, "GLclampx", value, "GLboolean", invert)
+TRACE_GL_VOID(glScalef, (GLfloat x, GLfloat y, GLfloat z), (x, y, z), 3, "GLfloat", x, "GLfloat", y, "GLfloat", z)
+TRACE_GL_VOID(glScalex, (GLfixed x, GLfixed y, GLfixed z), (x, y, z), 3, "GLfixed", x, "GLfixed", y, "GLfixed", z)
+TRACE_GL_VOID(glScalexOES, (GLfixed x, GLfixed y, GLfixed z), (x, y, z), 3, "GLfixed", x, "GLfixed", y, "GLfixed", z)
+TRACE_GL_VOID(glScissor, (GLint x, GLint y, GLsizei width, GLsizei height), (x, y, width, height), 4, "GLint", x, "GLint", y, "GLsizei", width, "GLsizei", height)
+TRACE_GL_VOID(glSelectPerfMonitorCountersAMD, (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList), (monitor, enable, group, numCounters, countersList), 5, "GLuint", monitor, "GLboolean", enable, "GLuint", group, "GLint", numCounters, "GLuint *", countersList)
+TRACE_GL_VOID(glSetFenceNV, (GLuint fence, GLenum condition), (fence, condition), 2, "GLuint", fence, "GLenum", condition)
+TRACE_GL_VOID(glShadeModel, (GLenum mode), (mode), 1, "GLenum", mode)
+TRACE_GL_VOID(glShaderBinary, (GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length), (n, shaders, binaryformat, binary, length), 5, "GLsizei", n, "const GLuint*", shaders, "GLenum", binaryformat, "const GLvoid*", binary, "GLsizei", length)
+TRACE_GL_VOID(glShaderSource, (GLuint shader, GLsizei count, const GLchar** string, const GLint* length), (shader, count, string, length), 4, "GLuint", shader, "GLsizei", count, "const GLchar**", string, "const GLint*", length)
+TRACE_GL_VOID(glStartTilingQCOM, (GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask), (x, y, width, height, preserveMask), 5, "GLuint", x, "GLuint", y, "GLuint", width, "GLuint", height, "GLbitfield", preserveMask)
+TRACE_GL_VOID(glStencilFunc, (GLenum func, GLint ref, GLuint mask), (func, ref, mask), 3, "GLenum", func, "GLint", ref, "GLuint", mask)
+TRACE_GL_VOID(glStencilFuncSeparate, (GLenum face, GLenum func, GLint ref, GLuint mask), (face, func, ref, mask), 4, "GLenum", face, "GLenum", func, "GLint", ref, "GLuint", mask)
+TRACE_GL_VOID(glStencilMask, (GLuint mask), (mask), 1, "GLuint", mask)
+TRACE_GL_VOID(glStencilMaskSeparate, (GLenum face, GLuint mask), (face, mask), 2, "GLenum", face, "GLuint", mask)
+TRACE_GL_VOID(glStencilOp, (GLenum fail, GLenum zfail, GLenum zpass), (fail, zfail, zpass), 3, "GLenum", fail, "GLenum", zfail, "GLenum", zpass)
+TRACE_GL_VOID(glStencilOpSeparate, (GLenum face, GLenum fail, GLenum zfail, GLenum zpass), (face, fail, zfail, zpass), 4, "GLenum", face, "GLenum", fail, "GLenum", zfail, "GLenum", zpass)
+TRACE_GL(GLboolean, glTestFenceNV, (GLuint fence), (fence), 1, "GLuint", fence)
+TRACE_GL_VOID(glTexCoordPointer, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer), (size, type, stride, pointer), 4, "GLint", size, "GLenum", type, "GLsizei", stride, "const GLvoid *", pointer)
+TRACE_GL_VOID(glTexEnvf, (GLenum target, GLenum pname, GLfloat param), (target, pname, param), 3, "GLenum", target, "GLenum", pname, "GLfloat", param)
+TRACE_GL_VOID(glTexEnvfv, (GLenum target, GLenum pname, const GLfloat *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "const GLfloat *", params)
+TRACE_GL_VOID(glTexEnvi, (GLenum target, GLenum pname, GLint param), (target, pname, param), 3, "GLenum", target, "GLenum", pname, "GLint", param)
+TRACE_GL_VOID(glTexEnviv, (GLenum target, GLenum pname, const GLint *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "const GLint *", params)
+TRACE_GL_VOID(glTexEnvx, (GLenum target, GLenum pname, GLfixed param), (target, pname, param), 3, "GLenum", target, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glTexEnvxOES, (GLenum target, GLenum pname, GLfixed param), (target, pname, param), 3, "GLenum", target, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glTexEnvxv, (GLenum target, GLenum pname, const GLfixed *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glTexEnvxvOES, (GLenum target, GLenum pname, const GLfixed *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glTexGenfOES, (GLenum coord, GLenum pname, GLfloat param), (coord, pname, param), 3, "GLenum", coord, "GLenum", pname, "GLfloat", param)
+TRACE_GL_VOID(glTexGenfvOES, (GLenum coord, GLenum pname, const GLfloat *params), (coord, pname, params), 3, "GLenum", coord, "GLenum", pname, "const GLfloat *", params)
+TRACE_GL_VOID(glTexGeniOES, (GLenum coord, GLenum pname, GLint param), (coord, pname, param), 3, "GLenum", coord, "GLenum", pname, "GLint", param)
+TRACE_GL_VOID(glTexGenivOES, (GLenum coord, GLenum pname, const GLint *params), (coord, pname, params), 3, "GLenum", coord, "GLenum", pname, "const GLint *", params)
+TRACE_GL_VOID(glTexGenxOES, (GLenum coord, GLenum pname, GLfixed param), (coord, pname, param), 3, "GLenum", coord, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glTexGenxvOES, (GLenum coord, GLenum pname, const GLfixed *params), (coord, pname, params), 3, "GLenum", coord, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glTexImage2D, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels), (target, level, internalformat, width, height, border, format, type, pixels), 9, "GLenum", target, "GLint", level, "GLint", internalformat, "GLsizei", width, "GLsizei", height, "GLint", border, "GLenum", format, "GLenum", type, "const GLvoid *", pixels)
+TRACE_GL_VOID(glTexImage3DOES, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels), (target, level, internalformat, width, height, depth, border, format, type, pixels), 10, "GLenum", target, "GLint", level, "GLenum", internalformat, "GLsizei", width, "GLsizei", height, "GLsizei", depth, "GLint", border, "GLenum", format, "GLenum", type, "const GLvoid*", pixels)
+TRACE_GL_VOID(glTexParameterf, (GLenum target, GLenum pname, GLfloat param), (target, pname, param), 3, "GLenum", target, "GLenum", pname, "GLfloat", param)
+TRACE_GL_VOID(glTexParameterfv, (GLenum target, GLenum pname, const GLfloat *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "const GLfloat *", params)
+TRACE_GL_VOID(glTexParameteri, (GLenum target, GLenum pname, GLint param), (target, pname, param), 3, "GLenum", target, "GLenum", pname, "GLint", param)
+TRACE_GL_VOID(glTexParameteriv, (GLenum target, GLenum pname, const GLint *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "const GLint *", params)
+TRACE_GL_VOID(glTexParameterx, (GLenum target, GLenum pname, GLfixed param), (target, pname, param), 3, "GLenum", target, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glTexParameterxOES, (GLenum target, GLenum pname, GLfixed param), (target, pname, param), 3, "GLenum", target, "GLenum", pname, "GLfixed", param)
+TRACE_GL_VOID(glTexParameterxv, (GLenum target, GLenum pname, const GLfixed *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glTexParameterxvOES, (GLenum target, GLenum pname, const GLfixed *params), (target, pname, params), 3, "GLenum", target, "GLenum", pname, "const GLfixed *", params)
+TRACE_GL_VOID(glTexSubImage2D, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels), (target, level, xoffset, yoffset, width, height, format, type, pixels), 9, "GLenum", target, "GLint", level, "GLint", xoffset, "GLint", yoffset, "GLsizei", width, "GLsizei", height, "GLenum", format, "GLenum", type, "const GLvoid *", pixels)
+TRACE_GL_VOID(glTexSubImage3DOES, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels), (target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels), 11, "GLenum", target, "GLint", level, "GLint", xoffset, "GLint", yoffset, "GLint", zoffset, "GLsizei", width, "GLsizei", height, "GLsizei", depth, "GLenum", format, "GLenum", type, "const GLvoid*", pixels)
+TRACE_GL_VOID(glTranslatef, (GLfloat x, GLfloat y, GLfloat z), (x, y, z), 3, "GLfloat", x, "GLfloat", y, "GLfloat", z)
+TRACE_GL_VOID(glTranslatex, (GLfixed x, GLfixed y, GLfixed z), (x, y, z), 3, "GLfixed", x, "GLfixed", y, "GLfixed", z)
+TRACE_GL_VOID(glTranslatexOES, (GLfixed x, GLfixed y, GLfixed z), (x, y, z), 3, "GLfixed", x, "GLfixed", y, "GLfixed", z)
+TRACE_GL_VOID(glUniform1f, (GLint location, GLfloat x), (location, x), 2, "GLint", location, "GLfloat", x)
+TRACE_GL_VOID(glUniform1fv, (GLint location, GLsizei count, const GLfloat* v), (location, count, v), 3, "GLint", location, "GLsizei", count, "const GLfloat*", v)
+TRACE_GL_VOID(glUniform1i, (GLint location, GLint x), (location, x), 2, "GLint", location, "GLint", x)
+TRACE_GL_VOID(glUniform1iv, (GLint location, GLsizei count, const GLint* v), (location, count, v), 3, "GLint", location, "GLsizei", count, "const GLint*", v)
+TRACE_GL_VOID(glUniform2f, (GLint location, GLfloat x, GLfloat y), (location, x, y), 3, "GLint", location, "GLfloat", x, "GLfloat", y)
+TRACE_GL_VOID(glUniform2fv, (GLint location, GLsizei count, const GLfloat* v), (location, count, v), 3, "GLint", location, "GLsizei", count, "const GLfloat*", v)
+TRACE_GL_VOID(glUniform2i, (GLint location, GLint x, GLint y), (location, x, y), 3, "GLint", location, "GLint", x, "GLint", y)
+TRACE_GL_VOID(glUniform2iv, (GLint location, GLsizei count, const GLint* v), (location, count, v), 3, "GLint", location, "GLsizei", count, "const GLint*", v)
+TRACE_GL_VOID(glUniform3f, (GLint location, GLfloat x, GLfloat y, GLfloat z), (location, x, y, z), 4, "GLint", location, "GLfloat", x, "GLfloat", y, "GLfloat", z)
+TRACE_GL_VOID(glUniform3fv, (GLint location, GLsizei count, const GLfloat* v), (location, count, v), 3, "GLint", location, "GLsizei", count, "const GLfloat*", v)
+TRACE_GL_VOID(glUniform3i, (GLint location, GLint x, GLint y, GLint z), (location, x, y, z), 4, "GLint", location, "GLint", x, "GLint", y, "GLint", z)
+TRACE_GL_VOID(glUniform3iv, (GLint location, GLsizei count, const GLint* v), (location, count, v), 3, "GLint", location, "GLsizei", count, "const GLint*", v)
+TRACE_GL_VOID(glUniform4f, (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w), (location, x, y, z, w), 5, "GLint", location, "GLfloat", x, "GLfloat", y, "GLfloat", z, "GLfloat", w)
+TRACE_GL_VOID(glUniform4fv, (GLint location, GLsizei count, const GLfloat* v), (location, count, v), 3, "GLint", location, "GLsizei", count, "const GLfloat*", v)
+TRACE_GL_VOID(glUniform4i, (GLint location, GLint x, GLint y, GLint z, GLint w), (location, x, y, z, w), 5, "GLint", location, "GLint", x, "GLint", y, "GLint", z, "GLint", w)
+TRACE_GL_VOID(glUniform4iv, (GLint location, GLsizei count, const GLint* v), (location, count, v), 3, "GLint", location, "GLsizei", count, "const GLint*", v)
+TRACE_GL_VOID(glUniformMatrix2fv, (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value), (location, count, transpose, value), 4, "GLint", location, "GLsizei", count, "GLboolean", transpose, "const GLfloat*", value)
+TRACE_GL_VOID(glUniformMatrix3fv, (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value), (location, count, transpose, value), 4, "GLint", location, "GLsizei", count, "GLboolean", transpose, "const GLfloat*", value)
+TRACE_GL_VOID(glUniformMatrix4fv, (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value), (location, count, transpose, value), 4, "GLint", location, "GLsizei", count, "GLboolean", transpose, "const GLfloat*", value)
+TRACE_GL(GLboolean, glUnmapBufferOES, (GLenum target), (target), 1, "GLenum", target)
+TRACE_GL_VOID(glUseProgram, (GLuint program), (program), 1, "GLuint", program)
+TRACE_GL_VOID(glValidateProgram, (GLuint program), (program), 1, "GLuint", program)
+TRACE_GL_VOID(glVertexAttrib1f, (GLuint indx, GLfloat x), (indx, x), 2, "GLuint", indx, "GLfloat", x)
+TRACE_GL_VOID(glVertexAttrib1fv, (GLuint indx, const GLfloat* values), (indx, values), 2, "GLuint", indx, "const GLfloat*", values)
+TRACE_GL_VOID(glVertexAttrib2f, (GLuint indx, GLfloat x, GLfloat y), (indx, x, y), 3, "GLuint", indx, "GLfloat", x, "GLfloat", y)
+TRACE_GL_VOID(glVertexAttrib2fv, (GLuint indx, const GLfloat* values), (indx, values), 2, "GLuint", indx, "const GLfloat*", values)
+TRACE_GL_VOID(glVertexAttrib3f, (GLuint indx, GLfloat x, GLfloat y, GLfloat z), (indx, x, y, z), 4, "GLuint", indx, "GLfloat", x, "GLfloat", y, "GLfloat", z)
+TRACE_GL_VOID(glVertexAttrib3fv, (GLuint indx, const GLfloat* values), (indx, values), 2, "GLuint", indx, "const GLfloat*", values)
+TRACE_GL_VOID(glVertexAttrib4f, (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w), (indx, x, y, z, w), 5, "GLuint", indx, "GLfloat", x, "GLfloat", y, "GLfloat", z, "GLfloat", w)
+TRACE_GL_VOID(glVertexAttrib4fv, (GLuint indx, const GLfloat* values), (indx, values), 2, "GLuint", indx, "const GLfloat*", values)
+TRACE_GL_VOID(glVertexAttribPointer, (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr), (indx, size, type, normalized, stride, ptr), 6, "GLuint", indx, "GLint", size, "GLenum", type, "GLboolean", normalized, "GLsizei", stride, "const GLvoid*", ptr)
+TRACE_GL_VOID(glVertexPointer, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer), (size, type, stride, pointer), 4, "GLint", size, "GLenum", type, "GLsizei", stride, "const GLvoid *", pointer)
+TRACE_GL_VOID(glViewport, (GLint x, GLint y, GLsizei width, GLsizei height), (x, y, width, height), 4, "GLint", x, "GLint", y, "GLsizei", width, "GLsizei", height)
+TRACE_GL_VOID(glWeightPointerOES, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer), (size, type, stride, pointer), 4, "GLint", size, "GLenum", type, "GLsizei", stride, "const GLvoid *", pointer)
diff --git a/opengl/tests/gl2_yuvtex/Android.mk b/opengl/tests/gl2_yuvtex/Android.mk
new file mode 100644
index 0000000..6304700
--- /dev/null
+++ b/opengl/tests/gl2_yuvtex/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	gl2_yuvtex.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+    libEGL \
+    libGLESv2 \
+    libui
+
+LOCAL_MODULE:= test-opengl-gl2_yuvtex
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+
+include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/gl2_yuvtex/gl2_yuvtex.cpp b/opengl/tests/gl2_yuvtex/gl2_yuvtex.cpp
new file mode 100644
index 0000000..602ea1a
--- /dev/null
+++ b/opengl/tests/gl2_yuvtex/gl2_yuvtex.cpp
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/resource.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <utils/Timers.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+static void printGLString(const char *name, GLenum s) {
+    // fprintf(stderr, "printGLString %s, %d\n", name, s);
+    const char *v = (const char *) glGetString(s);
+    // int error = glGetError();
+    // fprintf(stderr, "glGetError() = %d, result of glGetString = %x\n", error,
+    //        (unsigned int) v);
+    // if ((v < (const char*) 0) || (v > (const char*) 0x10000))
+    //    fprintf(stderr, "GL %s = %s\n", name, v);
+    // else
+    //    fprintf(stderr, "GL %s = (null) 0x%08x\n", name, (unsigned int) v);
+    fprintf(stderr, "GL %s = %s\n", name, v);
+}
+
+static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
+    if (returnVal != EGL_TRUE) {
+        fprintf(stderr, "%s() returned %d\n", op, returnVal);
+    }
+
+    for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
+            = eglGetError()) {
+        fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error),
+                error);
+    }
+}
+
+static void checkGlError(const char* op) {
+    for (GLint error = glGetError(); error; error
+            = glGetError()) {
+        fprintf(stderr, "after %s() glError (0x%x)\n", op, error);
+    }
+}
+
+static const char gVertexShader[] = "attribute vec4 vPosition;\n"
+    "varying vec2 yuvTexCoords;\n"
+    "void main() {\n"
+    "  yuvTexCoords = vPosition.xy + vec2(0.5, 0.5);\n"
+    "  gl_Position = vPosition;\n"
+    "}\n";
+
+static const char gFragmentShader[] = "#extension GL_OES_EGL_image_external : require\n"
+    "precision mediump float;\n"
+    "uniform samplerExternalOES yuvTexSampler;\n"
+    "varying vec2 yuvTexCoords;\n"
+    "void main() {\n"
+    "  gl_FragColor = texture2D(yuvTexSampler, yuvTexCoords);\n"
+    "}\n";
+
+GLuint loadShader(GLenum shaderType, const char* pSource) {
+    GLuint shader = glCreateShader(shaderType);
+    if (shader) {
+        glShaderSource(shader, 1, &pSource, NULL);
+        glCompileShader(shader);
+        GLint compiled = 0;
+        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+        if (!compiled) {
+            GLint infoLen = 0;
+            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+            if (infoLen) {
+                char* buf = (char*) malloc(infoLen);
+                if (buf) {
+                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
+                    fprintf(stderr, "Could not compile shader %d:\n%s\n",
+                            shaderType, buf);
+                    free(buf);
+                }
+            } else {
+                fprintf(stderr, "Guessing at GL_INFO_LOG_LENGTH size\n");
+                char* buf = (char*) malloc(0x1000);
+                if (buf) {
+                    glGetShaderInfoLog(shader, 0x1000, NULL, buf);
+                    fprintf(stderr, "Could not compile shader %d:\n%s\n",
+                            shaderType, buf);
+                    free(buf);
+                }
+            }
+            glDeleteShader(shader);
+            shader = 0;
+        }
+    }
+    return shader;
+}
+
+GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
+    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
+    if (!vertexShader) {
+        return 0;
+    }
+
+    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
+    if (!pixelShader) {
+        return 0;
+    }
+
+    GLuint program = glCreateProgram();
+    if (program) {
+        glAttachShader(program, vertexShader);
+        checkGlError("glAttachShader");
+        glAttachShader(program, pixelShader);
+        checkGlError("glAttachShader");
+        glLinkProgram(program);
+        GLint linkStatus = GL_FALSE;
+        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+        if (linkStatus != GL_TRUE) {
+            GLint bufLength = 0;
+            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+            if (bufLength) {
+                char* buf = (char*) malloc(bufLength);
+                if (buf) {
+                    glGetProgramInfoLog(program, bufLength, NULL, buf);
+                    fprintf(stderr, "Could not link program:\n%s\n", buf);
+                    free(buf);
+                }
+            }
+            glDeleteProgram(program);
+            program = 0;
+        }
+    }
+    return program;
+}
+
+GLuint gProgram;
+GLint gvPositionHandle;
+GLint gYuvTexSamplerHandle;
+
+bool setupGraphics(int w, int h) {
+    gProgram = createProgram(gVertexShader, gFragmentShader);
+    if (!gProgram) {
+        return false;
+    }
+    gvPositionHandle = glGetAttribLocation(gProgram, "vPosition");
+    checkGlError("glGetAttribLocation");
+    fprintf(stderr, "glGetAttribLocation(\"vPosition\") = %d\n",
+            gvPositionHandle);
+    gYuvTexSamplerHandle = glGetUniformLocation(gProgram, "yuvTexSampler");
+    checkGlError("glGetUniformLocation");
+    fprintf(stderr, "glGetUniformLocation(\"yuvTexSampler\") = %d\n",
+            gYuvTexSamplerHandle);
+
+    glViewport(0, 0, w, h);
+    checkGlError("glViewport");
+    return true;
+}
+
+int align(int x, int a) {
+    return (x + (a-1)) & (~(a-1));
+}
+
+const int yuvTexWidth = 608;
+const int yuvTexHeight = 480;
+const int yuvTexUsage = GraphicBuffer::USAGE_HW_TEXTURE |
+        GraphicBuffer::USAGE_SW_WRITE_RARELY;
+const int yuvTexFormat = HAL_PIXEL_FORMAT_YV12;
+const int yuvTexOffsetY = 0;
+const int yuvTexStrideY = (yuvTexWidth + 0xf) & ~0xf;
+const int yuvTexOffsetV = yuvTexStrideY * yuvTexHeight;
+const int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; 
+const int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * yuvTexHeight/2;
+const int yuvTexStrideU = yuvTexStrideV;
+const bool yuvTexSameUV = false;
+static sp<GraphicBuffer> yuvTexBuffer;
+static GLuint yuvTex;
+
+bool setupYuvTexSurface(EGLDisplay dpy, EGLContext context) {
+    int blockWidth = yuvTexWidth > 16 ? yuvTexWidth / 16 : 1;
+    int blockHeight = yuvTexHeight > 16 ? yuvTexHeight / 16 : 1;
+    yuvTexBuffer = new GraphicBuffer(yuvTexWidth, yuvTexHeight, yuvTexFormat,
+            yuvTexUsage);
+    char* buf = NULL;
+    status_t err = yuvTexBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf));
+    if (err != 0) {
+        fprintf(stderr, "yuvTexBuffer->lock(...) failed: %d\n", err);
+        return false;
+    }
+    for (int x = 0; x < yuvTexWidth; x++) {
+        for (int y = 0; y < yuvTexHeight; y++) {
+            int parityX = (x / blockWidth) & 1;
+            int parityY = (y / blockHeight) & 1;
+            unsigned char intensity = (parityX ^ parityY) ? 63 : 191;
+            buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity;
+            if (x < yuvTexWidth / 2 && y < yuvTexHeight / 2) {
+                buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity;
+                if (yuvTexSameUV) {
+                    buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = intensity;
+                } else if (x < yuvTexWidth / 4 && y < yuvTexHeight / 4) {
+                    buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] =
+                    buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] =
+                    buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] =
+                    buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = intensity;
+                }
+            }
+        }
+    }
+
+    err = yuvTexBuffer->unlock();
+    if (err != 0) {
+        fprintf(stderr, "yuvTexBuffer->unlock() failed: %d\n", err);
+        return false;
+    }
+
+    EGLClientBuffer clientBuffer = (EGLClientBuffer)yuvTexBuffer->getNativeBuffer();
+    EGLImageKHR img = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+            clientBuffer, 0);
+    checkEglError("eglCreateImageKHR");
+    if (img == EGL_NO_IMAGE_KHR) {
+        return false;
+    }
+
+    glGenTextures(1, &yuvTex);
+    checkGlError("glGenTextures");
+    glBindTexture(GL_TEXTURE_EXTERNAL_OES, yuvTex);
+    checkGlError("glBindTexture");
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)img);
+    checkGlError("glEGLImageTargetTexture2DOES");
+
+    return true;
+}
+
+const GLfloat gTriangleVertices[] = {
+    -0.5f, 0.5f,
+    -0.5f, -0.5f,
+    0.5f, -0.5f,
+    0.5f, 0.5f,
+};
+
+void renderFrame() {
+    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
+    checkGlError("glClearColor");
+    glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    checkGlError("glClear");
+
+    glUseProgram(gProgram);
+    checkGlError("glUseProgram");
+
+    glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices);
+    checkGlError("glVertexAttribPointer");
+    glEnableVertexAttribArray(gvPositionHandle);
+    checkGlError("glEnableVertexAttribArray");
+
+    glUniform1i(gYuvTexSamplerHandle, 0);
+    checkGlError("glUniform1i");
+    glBindTexture(GL_TEXTURE_EXTERNAL_OES, yuvTex);
+    checkGlError("glBindTexture");
+
+    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+    checkGlError("glDrawArrays");
+}
+
+void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) {
+
+#define X(VAL) {VAL, #VAL}
+    struct {EGLint attribute; const char* name;} names[] = {
+    X(EGL_BUFFER_SIZE),
+    X(EGL_ALPHA_SIZE),
+    X(EGL_BLUE_SIZE),
+    X(EGL_GREEN_SIZE),
+    X(EGL_RED_SIZE),
+    X(EGL_DEPTH_SIZE),
+    X(EGL_STENCIL_SIZE),
+    X(EGL_CONFIG_CAVEAT),
+    X(EGL_CONFIG_ID),
+    X(EGL_LEVEL),
+    X(EGL_MAX_PBUFFER_HEIGHT),
+    X(EGL_MAX_PBUFFER_PIXELS),
+    X(EGL_MAX_PBUFFER_WIDTH),
+    X(EGL_NATIVE_RENDERABLE),
+    X(EGL_NATIVE_VISUAL_ID),
+    X(EGL_NATIVE_VISUAL_TYPE),
+    X(EGL_SAMPLES),
+    X(EGL_SAMPLE_BUFFERS),
+    X(EGL_SURFACE_TYPE),
+    X(EGL_TRANSPARENT_TYPE),
+    X(EGL_TRANSPARENT_RED_VALUE),
+    X(EGL_TRANSPARENT_GREEN_VALUE),
+    X(EGL_TRANSPARENT_BLUE_VALUE),
+    X(EGL_BIND_TO_TEXTURE_RGB),
+    X(EGL_BIND_TO_TEXTURE_RGBA),
+    X(EGL_MIN_SWAP_INTERVAL),
+    X(EGL_MAX_SWAP_INTERVAL),
+    X(EGL_LUMINANCE_SIZE),
+    X(EGL_ALPHA_MASK_SIZE),
+    X(EGL_COLOR_BUFFER_TYPE),
+    X(EGL_RENDERABLE_TYPE),
+    X(EGL_CONFORMANT),
+   };
+#undef X
+
+    for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) {
+        EGLint value = -1;
+        EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value);
+        EGLint error = eglGetError();
+        if (returnVal && error == EGL_SUCCESS) {
+            printf(" %s: ", names[j].name);
+            printf("%d (0x%x)", value, value);
+        }
+    }
+    printf("\n");
+}
+
+int main(int argc, char** argv) {
+    EGLBoolean returnValue;
+    EGLConfig myConfig = {0};
+
+    EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+    EGLint s_configAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_NONE };
+    EGLint majorVersion;
+    EGLint minorVersion;
+    EGLContext context;
+    EGLSurface surface;
+    EGLint w, h;
+
+    EGLDisplay dpy;
+
+    checkEglError("<init>");
+    dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    checkEglError("eglGetDisplay");
+    if (dpy == EGL_NO_DISPLAY) {
+        printf("eglGetDisplay returned EGL_NO_DISPLAY.\n");
+        return 0;
+    }
+
+    returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);
+    checkEglError("eglInitialize", returnValue);
+    fprintf(stderr, "EGL version %d.%d\n", majorVersion, minorVersion);
+    if (returnValue != EGL_TRUE) {
+        printf("eglInitialize failed\n");
+        return 0;
+    }
+
+    EGLNativeWindowType window = android_createDisplaySurface();
+    returnValue = EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &myConfig);
+    if (returnValue) {
+        printf("EGLUtils::selectConfigForNativeWindow() returned %d", returnValue);
+        return 1;
+    }
+
+    checkEglError("EGLUtils::selectConfigForNativeWindow");
+
+    printf("Chose this configuration:\n");
+    printEGLConfiguration(dpy, myConfig);
+
+    surface = eglCreateWindowSurface(dpy, myConfig, window, NULL);
+    checkEglError("eglCreateWindowSurface");
+    if (surface == EGL_NO_SURFACE) {
+        printf("gelCreateWindowSurface failed.\n");
+        return 1;
+    }
+
+    context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, context_attribs);
+    checkEglError("eglCreateContext");
+    if (context == EGL_NO_CONTEXT) {
+        printf("eglCreateContext failed\n");
+        return 1;
+    }
+    returnValue = eglMakeCurrent(dpy, surface, surface, context);
+    checkEglError("eglMakeCurrent", returnValue);
+    if (returnValue != EGL_TRUE) {
+        return 1;
+    }
+    eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
+    checkEglError("eglQuerySurface");
+    eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
+    checkEglError("eglQuerySurface");
+    GLint dim = w < h ? w : h;
+
+    fprintf(stderr, "Window dimensions: %d x %d\n", w, h);
+
+    printGLString("Version", GL_VERSION);
+    printGLString("Vendor", GL_VENDOR);
+    printGLString("Renderer", GL_RENDERER);
+    printGLString("Extensions", GL_EXTENSIONS);
+
+    if(!setupYuvTexSurface(dpy, context)) {
+        fprintf(stderr, "Could not set up texture surface.\n");
+        return 1;
+    }
+
+    if(!setupGraphics(w, h)) {
+        fprintf(stderr, "Could not set up graphics.\n");
+        return 1;
+    }
+
+    for (;;) {
+        renderFrame();
+        eglSwapBuffers(dpy, surface);
+        checkEglError("eglSwapBuffers");
+    }
+
+    return 0;
+}
diff --git a/opengl/tests/gl_perf/Android.mk b/opengl/tests/gl_perf/Android.mk
new file mode 100644
index 0000000..37647ca
--- /dev/null
+++ b/opengl/tests/gl_perf/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	gl2_perf.cpp \
+	filltest.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+    libEGL \
+    libGLESv2 \
+    libui
+
+LOCAL_MODULE:= test-opengl-gl2_perf
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
+
+include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/gl_perf/fill_common.cpp b/opengl/tests/gl_perf/fill_common.cpp
new file mode 100644
index 0000000..a069f67
--- /dev/null
+++ b/opengl/tests/gl_perf/fill_common.cpp
@@ -0,0 +1,298 @@
+/*
+ * 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.
+ */
+
+#include "fragment_shaders.cpp"
+
+FILE * fOut = NULL;
+void ptSwap();
+
+static char gCurrentTestName[1024];
+static uint32_t gWidth = 0;
+static uint32_t gHeight = 0;
+
+static void checkGlError(const char* op) {
+    for (GLint error = glGetError(); error; error
+            = glGetError()) {
+        LOGE("after %s() glError (0x%x)\n", op, error);
+    }
+}
+
+GLuint loadShader(GLenum shaderType, const char* pSource) {
+    GLuint shader = glCreateShader(shaderType);
+    if (shader) {
+        glShaderSource(shader, 1, &pSource, NULL);
+        glCompileShader(shader);
+        GLint compiled = 0;
+        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+        if (!compiled) {
+            GLint infoLen = 0;
+            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+            if (infoLen) {
+                char* buf = (char*) malloc(infoLen);
+                if (buf) {
+                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
+                    LOGE("Could not compile shader %d:\n%s\n", shaderType, buf);
+                    free(buf);
+                }
+                glDeleteShader(shader);
+                shader = 0;
+            }
+        }
+    }
+    return shader;
+}
+
+enum {
+    A_POS,
+    A_COLOR,
+    A_TEX0,
+    A_TEX1
+};
+
+GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
+    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
+    if (!vertexShader) {
+        return 0;
+    }
+
+    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
+    if (!pixelShader) {
+        return 0;
+    }
+
+    GLuint program = glCreateProgram();
+    if (program) {
+        glAttachShader(program, vertexShader);
+        checkGlError("glAttachShader v");
+        glAttachShader(program, pixelShader);
+        checkGlError("glAttachShader p");
+
+        glBindAttribLocation(program, A_POS, "a_pos");
+        glBindAttribLocation(program, A_COLOR, "a_color");
+        glBindAttribLocation(program, A_TEX0, "a_tex0");
+        glBindAttribLocation(program, A_TEX1, "a_tex1");
+        glLinkProgram(program);
+        GLint linkStatus = GL_FALSE;
+        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+        if (linkStatus != GL_TRUE) {
+            GLint bufLength = 0;
+            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+            if (bufLength) {
+                char* buf = (char*) malloc(bufLength);
+                if (buf) {
+                    glGetProgramInfoLog(program, bufLength, NULL, buf);
+                    LOGE("Could not link program:\n%s\n", buf);
+                    free(buf);
+                }
+            }
+            glDeleteProgram(program);
+            program = 0;
+        }
+    }
+    checkGlError("createProgram");
+    glUseProgram(program);
+    return program;
+}
+
+uint64_t getTime() {
+    struct timespec t;
+    clock_gettime(CLOCK_MONOTONIC, &t);
+    return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000);
+}
+
+uint64_t gTime;
+void startTimer() {
+    gTime = getTime();
+}
+
+
+static void endTimer(int count) {
+    uint64_t t2 = getTime();
+    double delta = ((double)(t2 - gTime)) / 1000000000;
+    double pixels = (gWidth * gHeight) * count;
+    double mpps = pixels / delta / 1000000;
+    double dc60 = ((double)count) / delta / 60;
+
+    if (fOut) {
+        fprintf(fOut, "%s, %f, %f\r\n", gCurrentTestName, mpps, dc60);
+        fflush(fOut);
+    } else {
+        printf("%s, %f, %f\n", gCurrentTestName, mpps, dc60);
+    }
+    LOGI("%s, %f, %f\r\n", gCurrentTestName, mpps, dc60);
+}
+
+
+static const char gVertexShader[] =
+    "attribute vec4 a_pos;\n"
+    "attribute vec4 a_color;\n"
+    "attribute vec2 a_tex0;\n"
+    "attribute vec2 a_tex1;\n"
+    "varying vec4 v_color;\n"
+    "varying vec2 v_tex0;\n"
+    "varying vec2 v_tex1;\n"
+    "uniform vec2 u_texOff;\n"
+
+    "void main() {\n"
+    "    v_color = a_color;\n"
+    "    v_tex0 = a_tex0;\n"
+    "    v_tex1 = a_tex1;\n"
+    "    v_tex0.x += u_texOff.x;\n"
+    "    v_tex1.y += u_texOff.y;\n"
+    "    gl_Position = a_pos;\n"
+    "}\n";
+
+static void setupVA() {
+    static const float vtx[] = {
+        -1.0f,-1.0f,
+         1.0f,-1.0f,
+        -1.0f, 1.0f,
+         1.0f, 1.0f };
+    static const float color[] = {
+        1.0f,0.0f,1.0f,1.0f,
+        0.0f,0.0f,1.0f,1.0f,
+        1.0f,1.0f,0.0f,1.0f,
+        1.0f,1.0f,1.0f,1.0f };
+    static const float tex0[] = {
+        0.0f,0.0f,
+        1.0f,0.0f,
+        0.0f,1.0f,
+        1.0f,1.0f };
+    static const float tex1[] = {
+        1.0f,0.0f,
+        1.0f,1.0f,
+        0.0f,1.0f,
+        0.0f,0.0f };
+
+    glEnableVertexAttribArray(A_POS);
+    glEnableVertexAttribArray(A_COLOR);
+    glEnableVertexAttribArray(A_TEX0);
+    glEnableVertexAttribArray(A_TEX1);
+
+    glVertexAttribPointer(A_POS, 2, GL_FLOAT, false, 8, vtx);
+    glVertexAttribPointer(A_COLOR, 4, GL_FLOAT, false, 16, color);
+    glVertexAttribPointer(A_TEX0, 2, GL_FLOAT, false, 8, tex0);
+    glVertexAttribPointer(A_TEX1, 2, GL_FLOAT, false, 8, tex1);
+}
+
+static void randUniform(int pgm, const char *var) {
+    int loc = glGetUniformLocation(pgm, var);
+    if (loc >= 0) {
+        float x = ((float)rand()) / RAND_MAX;
+        float y = ((float)rand()) / RAND_MAX;
+        float z = ((float)rand()) / RAND_MAX;
+        float w = ((float)rand()) / RAND_MAX;
+        glUniform4f(loc, x, y, z, w);
+    }
+}
+
+static void doLoop(bool warmup, int pgm, uint32_t passCount) {
+    if (warmup) {
+        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+        ptSwap();
+        glFinish();
+        return;
+    }
+
+    startTimer();
+    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    for (uint32_t ct=0; ct < passCount; ct++) {
+        int loc = glGetUniformLocation(pgm, "u_texOff");
+        glUniform2f(loc, ((float)ct) / passCount, ((float)ct) / 2.f / passCount);
+
+        randUniform(pgm, "u_color");
+        randUniform(pgm, "u_0");
+        randUniform(pgm, "u_1");
+        randUniform(pgm, "u_2");
+        randUniform(pgm, "u_3");
+        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    }
+    ptSwap();
+    glFinish();
+    endTimer(passCount);
+}
+
+
+static uint32_t rgb(uint32_t r, uint32_t g, uint32_t b)
+{
+    uint32_t ret = 0xff000000;
+    ret |= r & 0xff;
+    ret |= (g & 0xff) << 8;
+    ret |= (b & 0xff) << 16;
+    return ret;
+}
+
+void genTextures() {
+    uint32_t *m = (uint32_t *)malloc(1024*1024*4);
+    for (int y=0; y < 1024; y++){
+        for (int x=0; x < 1024; x++){
+            m[y*1024 + x] = rgb(x, (((x+y) & 0xff) == 0x7f) * 0xff, y);
+        }
+    }
+    glBindTexture(GL_TEXTURE_2D, 1);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, m);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+    for (int y=0; y < 16; y++){
+        for (int x=0; x < 16; x++){
+            m[y*16 + x] = rgb(x << 4, (((x+y) & 0xf) == 0x7) * 0xff, y << 4);
+        }
+    }
+    glBindTexture(GL_TEXTURE_2D, 2);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, m);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    free(m);
+}
+
+static void doSingleTest(uint32_t pgmNum, int tex) {
+    const char *pgmTxt = gFragmentTests[pgmNum]->txt;
+    int pgm = createProgram(gVertexShader, pgmTxt);
+    if (!pgm) {
+        printf("error running test\n");
+        return;
+    }
+    int loc = glGetUniformLocation(pgm, "u_tex0");
+    if (loc >= 0) glUniform1i(loc, 0);
+    loc = glGetUniformLocation(pgm, "u_tex1");
+    if (loc >= 0) glUniform1i(loc, 1);
+
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glActiveTexture(GL_TEXTURE0);
+
+    glBlendFunc(GL_ONE, GL_ONE);
+    glDisable(GL_BLEND);
+    //sprintf(str2, "%i, %i, %i, %i, %i, 0",
+            //useVarColor, texCount, modulateFirstTex, extraMath, tex0);
+    //doLoop(true, pgm, w, h, str2);
+    //doLoop(false, pgm, w, h, str2);
+
+    glEnable(GL_BLEND);
+    sprintf(gCurrentTestName, "%s, %i, %i, 1", gFragmentTests[pgmNum]->name, pgmNum, tex);
+    doLoop(true, pgm, 100);
+    doLoop(false, pgm, 100);
+}
+
diff --git a/opengl/tests/gl_perf/filltest.cpp b/opengl/tests/gl_perf/filltest.cpp
new file mode 100644
index 0000000..3f8faca
--- /dev/null
+++ b/opengl/tests/gl_perf/filltest.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <string.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <utils/Timers.h>
+#include <EGL/egl.h>
+#include <utils/Log.h>
+
+
+using namespace android;
+
+
+#include "fill_common.cpp"
+
+
+bool doTest(uint32_t w, uint32_t h) {
+    gWidth = w;
+    gHeight = h;
+    setupVA();
+    genTextures();
+
+    printf("\nvarColor, texCount, modulate, extraMath, texSize, blend, Mpps, DC60\n");
+
+    for (uint32_t num = 0; num < gFragmentTestCount; num++) {
+        doSingleTest(num, 2);
+        if (gFragmentTests[num]->texCount) {
+            doSingleTest(num, 1);
+        }
+    }
+
+    exit(0);
+    return true;
+}
diff --git a/opengl/tests/gl_perf/fragment_shaders.cpp b/opengl/tests/gl_perf/fragment_shaders.cpp
new file mode 100644
index 0000000..79d5ead
--- /dev/null
+++ b/opengl/tests/gl_perf/fragment_shaders.cpp
@@ -0,0 +1,139 @@
+
+typedef struct FragmentTestRec {
+	const char * name;
+	uint32_t texCount;
+	const char * txt;
+} FragmentTest;
+
+static FragmentTest fpFill = {
+	"Solid color", 0,
+
+    "precision mediump float;\n"
+    "uniform vec4 u_color;\n"
+    "void main() {\n"
+    "  gl_FragColor = u_color;\n"
+    "}\n"
+};
+
+static FragmentTest fpGradient = {
+	"Solid gradient", 0,
+
+    "precision mediump float;\n"
+    "varying lowp vec4 v_color;\n"
+    "void main() {\n"
+    "  gl_FragColor = v_color;\n"
+    "}\n"
+};
+
+static FragmentTest fpCopyTex = {
+	"Texture copy", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "uniform sampler2D u_tex0;\n"
+    "void main() {\n"
+    "  gl_FragColor = texture2D(u_tex0, v_tex0);\n"
+    "}\n"
+};
+
+static FragmentTest fpCopyTexGamma = {
+	"Texture copy with gamma", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "uniform sampler2D u_tex0;\n"
+    "void main() {\n"
+    "  vec4 t = texture2D(u_tex0, v_tex0);\n"
+    "  t.rgb = pow(t.rgb, vec3(1.4, 1.4, 1.4));\n"
+    "  gl_FragColor = t;\n"
+    "}\n"
+};
+
+static FragmentTest fpTexSpec = {
+	"Texture spec", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "uniform sampler2D u_tex0;\n"
+    "void main() {\n"
+    "  vec4 t = texture2D(u_tex0, v_tex0);\n"
+    "  float simSpec = dot(gl_FragCoord.xyz, gl_FragCoord.xyz);\n"
+    "  simSpec = pow(clamp(simSpec, 0.1, 1.0), 40.0);\n"
+    "  gl_FragColor = t + vec4(simSpec, simSpec, simSpec, simSpec);\n"
+    "}\n"
+};
+
+static FragmentTest fpDepTex = {
+	"Dependent Lookup", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "uniform sampler2D u_tex0;\n"
+    "void main() {\n"
+    "  vec4 t = texture2D(u_tex0, v_tex0);\n"
+    "  t += texture2D(u_tex0, t.xy);\n"
+    "  gl_FragColor = t;\n"
+    "}\n"
+};
+
+static FragmentTest fpModulateConstantTex = {
+	"Texture modulate constant", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "uniform sampler2D u_tex0;\n"
+    "uniform vec4 u_color;\n"
+
+    "void main() {\n"
+    "  lowp vec4 c = texture2D(u_tex0, v_tex0);\n"
+	"  c *= u_color;\n"
+    "  gl_FragColor = c;\n"
+    "}\n"
+};
+
+static FragmentTest fpModulateVaryingTex = {
+	"Texture modulate gradient", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "varying lowp vec4 v_color;\n"
+    "uniform sampler2D u_tex0;\n"
+
+    "void main() {\n"
+    "  lowp vec4 c = texture2D(u_tex0, v_tex0);\n"
+	"  c *= v_color;\n"
+    "  gl_FragColor = c;\n"
+    "}\n"
+};
+
+static FragmentTest fpModulateVaryingConstantTex = {
+	"Texture modulate gradient constant", 1,
+
+    "precision mediump float;\n"
+    "varying vec2 v_tex0;\n"
+    "varying lowp vec4 v_color;\n"
+    "uniform sampler2D u_tex0;\n"
+    "uniform vec4 u_color;\n"
+
+    "void main() {\n"
+    "  lowp vec4 c = texture2D(u_tex0, v_tex0);\n"
+	"  c *= v_color;\n"
+	"  c *= u_color;\n"
+    "  gl_FragColor = c;\n"
+    "}\n"
+};
+
+static FragmentTest *gFragmentTests[] = {
+	&fpFill,
+	&fpGradient,
+	&fpCopyTex,
+	&fpCopyTexGamma,
+   &fpTexSpec,
+   &fpDepTex,
+	&fpModulateConstantTex,
+	&fpModulateVaryingTex,
+	&fpModulateVaryingConstantTex,
+
+};
+
+static const size_t gFragmentTestCount = sizeof(gFragmentTests) / sizeof(gFragmentTests[0]);
diff --git a/opengl/tests/gl_perf/gl2_perf.cpp b/opengl/tests/gl_perf/gl2_perf.cpp
new file mode 100644
index 0000000..9dfcf1c
--- /dev/null
+++ b/opengl/tests/gl_perf/gl2_perf.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/resource.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <utils/Timers.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+
+static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
+    if (returnVal != EGL_TRUE) {
+        fprintf(stderr, "%s() returned %d\n", op, returnVal);
+    }
+
+    for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
+            = eglGetError()) {
+        fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error),
+                error);
+    }
+}
+
+static void checkGlError(const char* op) {
+    for (GLint error = glGetError(); error; error
+            = glGetError()) {
+        fprintf(stderr, "after %s() glError (0x%x)\n", op, error);
+    }
+}
+
+bool doTest(uint32_t w, uint32_t h);
+
+static EGLDisplay dpy;
+static EGLSurface surface;
+
+int main(int argc, char** argv) {
+    EGLBoolean returnValue;
+    EGLConfig myConfig = {0};
+
+    EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+    EGLint s_configAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_NONE };
+    EGLint majorVersion;
+    EGLint minorVersion;
+    EGLContext context;
+    EGLint w, h;
+
+
+    checkEglError("<init>");
+    dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    checkEglError("eglGetDisplay");
+    if (dpy == EGL_NO_DISPLAY) {
+        printf("eglGetDisplay returned EGL_NO_DISPLAY.\n");
+        return 0;
+    }
+
+    returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);
+    checkEglError("eglInitialize", returnValue);
+    if (returnValue != EGL_TRUE) {
+        printf("eglInitialize failed\n");
+        return 0;
+    }
+
+    EGLNativeWindowType window = android_createDisplaySurface();

+    returnValue = EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &myConfig);
+    if (returnValue) {
+        printf("EGLUtils::selectConfigForNativeWindow() returned %d", returnValue);
+        return 0;
+    }
+
+    checkEglError("EGLUtils::selectConfigForNativeWindow");
+
+    surface = eglCreateWindowSurface(dpy, myConfig, window, NULL);
+    checkEglError("eglCreateWindowSurface");
+    if (surface == EGL_NO_SURFACE) {
+        printf("gelCreateWindowSurface failed.\n");
+        return 0;
+    }
+
+    context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, context_attribs);
+    checkEglError("eglCreateContext");
+    if (context == EGL_NO_CONTEXT) {
+        printf("eglCreateContext failed\n");
+        return 0;
+    }
+    returnValue = eglMakeCurrent(dpy, surface, surface, context);
+    checkEglError("eglMakeCurrent", returnValue);
+    if (returnValue != EGL_TRUE) {
+        return 0;
+    }
+    eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
+    checkEglError("eglQuerySurface");
+    eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
+    checkEglError("eglQuerySurface");
+    GLint dim = w < h ? w : h;
+
+    glViewport(0, 0, w, h);
+
+    for (;;) {
+        doTest(w, h);
+        eglSwapBuffers(dpy, surface);
+        checkEglError("eglSwapBuffers");
+    }
+
+    return 0;
+}
+
+void ptSwap() {
+    eglSwapBuffers(dpy, surface);
+}
+
diff --git a/opengl/tests/gl_perfapp/Android.mk b/opengl/tests/gl_perfapp/Android.mk
new file mode 100644
index 0000000..dd75a74
--- /dev/null
+++ b/opengl/tests/gl_perfapp/Android.mk
@@ -0,0 +1,54 @@
+#########################################################################
+# OpenGL ES Perf App
+# This makefile builds both an activity and a shared library.
+#########################################################################
+ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := GLPerf
+
+LOCAL_JNI_SHARED_LIBRARIES := libglperf
+
+# Run on Eclair
+LOCAL_SDK_VERSION := 7
+
+include $(BUILD_PACKAGE)
+
+#########################################################################
+# Build JNI Shared Library
+#########################################################################
+
+LOCAL_PATH:= $(LOCAL_PATH)/jni
+
+include $(CLEAR_VARS)
+
+# Optional tag would mean it doesn't get installed by default
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS := -Werror
+
+LOCAL_SRC_FILES:= \
+  gl_code.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libutils \
+	libEGL \
+	libGLESv2
+
+LOCAL_MODULE := libglperf
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif # TARGET_SIMULATOR
diff --git a/opengl/tests/gl_perfapp/AndroidManifest.xml b/opengl/tests/gl_perfapp/AndroidManifest.xml
new file mode 100644
index 0000000..305d95f
--- /dev/null
+++ b/opengl/tests/gl_perfapp/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.glperf"
+    android:versionName="1.0.0" android:versionCode="10000" >
+    <uses-sdk android:targetSdkVersion="7" android:minSdkVersion="7" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application
+            android:label="@string/glperf_activity">
+        <activity android:name="GLPerfActivity"
+                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+            	android:launchMode="singleTask"
+            	android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/gl_perfapp/jni/gl_code.cpp b/opengl/tests/gl_perfapp/jni/gl_code.cpp
new file mode 100644
index 0000000..f993371
--- /dev/null
+++ b/opengl/tests/gl_perfapp/jni/gl_code.cpp
@@ -0,0 +1,103 @@
+// OpenGL ES 2.0 code
+
+#include <nativehelper/jni.h>
+#define LOG_TAG "GLPerf gl_code.cpp"
+#include <utils/Log.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <utils/Timers.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "../../gl_perf/fill_common.cpp"
+
+
+//////////////////////////
+
+// Width and height of the screen
+
+uint32_t w;
+uint32_t h;
+
+// The stateClock starts at zero and increments by 1 every time we draw a frame. It is used to control which phase of the test we are in.
+
+int stateClock;
+const int doLoopStates = 2;
+const int doSingleTestStates = 2;
+bool done;
+
+// Saves the parameters of the test (so we can print them out when we finish the timing.)
+
+
+int pgm;
+
+void ptSwap() {
+}
+
+void doTest() {
+    uint32_t testNum = stateClock >> 2;
+    int texSize = ((stateClock >> 1) & 0x1) + 1;
+
+    if (testNum >= gFragmentTestCount) {
+       LOGI("done\n");
+       if (fOut) {
+           fclose(fOut);
+           fOut = NULL;
+       }
+       done = true;
+       return;
+    }
+
+    // LOGI("doTest %d %d %d\n", texCount, extraMath, testSubState);
+
+//        for (uint32_t num = 0; num < gFragmentTestCount; num++) {
+    doSingleTest(testNum, texSize);
+}
+
+extern "C" {
+    JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_init(JNIEnv * env, jobject obj,  jint width, jint height);
+    JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_step(JNIEnv * env, jobject obj);
+};
+
+JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_init(JNIEnv * env, jobject obj,  jint width, jint height)
+{
+    gWidth = width;
+    gHeight = height;
+    if (!done) {
+            stateClock = 0;
+            done = false;
+            setupVA();
+            genTextures();
+            const char* fileName = "/sdcard/glperf.csv";
+            if (fOut != NULL) {
+                 LOGI("Closing partially written output.n");
+                 fclose(fOut);
+                 fOut = NULL;
+            }
+            LOGI("Writing to: %s\n",fileName);
+            fOut = fopen(fileName, "w");
+            if (fOut == NULL) {
+                LOGE("Could not open: %s\n", fileName);
+            }
+
+            LOGI("\nvarColor, texCount, modulate, extraMath, texSize, blend, Mpps, DC60\n");
+            if (fOut) fprintf(fOut,"varColor, texCount, modulate, extraMath, texSize, blend, Mpps, DC60\r\n");
+    }
+}
+
+JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_step(JNIEnv * env, jobject obj)
+{
+    if (! done) {
+        if (stateClock > 0 && ((stateClock & 1) == 0)) {
+            //endTimer(100);
+        }
+        doTest();
+        stateClock++;
+    } else {
+            glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    }
+}
diff --git a/opengl/tests/gl_perfapp/res/values/strings.xml b/opengl/tests/gl_perfapp/res/values/strings.xml
new file mode 100644
index 0000000..dc21075
--- /dev/null
+++ b/opengl/tests/gl_perfapp/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?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.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+     them to be changed based on the locale and options. -->
+
+<resources>
+    <!-- Simple strings. -->
+    <string name="glperf_activity">GLPerf</string>
+
+</resources>
+
diff --git a/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfActivity.java b/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfActivity.java
new file mode 100644
index 0000000..e3f3abf
--- /dev/null
+++ b/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfActivity.java
@@ -0,0 +1,47 @@
+/*
+ * 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.glperf;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import java.io.File;
+
+
+public class GLPerfActivity extends Activity {
+
+    GLPerfView mView;
+
+    @Override protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+	getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        mView = new GLPerfView(getApplication());
+	setContentView(mView);
+    }
+
+    @Override protected void onPause() {
+        super.onPause();
+        mView.onPause();
+    }
+
+    @Override protected void onResume() {
+        super.onResume();
+        mView.onResume();
+    }
+}
diff --git a/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfLib.java b/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfLib.java
new file mode 100644
index 0000000..89a0e54
--- /dev/null
+++ b/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfLib.java
@@ -0,0 +1,33 @@
+/*
+ * 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.glperf;
+
+// Wrapper for native library
+
+public class GLPerfLib {
+
+     static {
+         System.loadLibrary("glperf");
+     }
+
+    /**
+     * @param width the current view width
+     * @param height the current view height
+     */
+     public static native void init(int width, int height);
+     public static native void step();
+}
diff --git a/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfView.java b/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfView.java
new file mode 100644
index 0000000..4ce4a4d
--- /dev/null
+++ b/opengl/tests/gl_perfapp/src/com/android/glperf/GLPerfView.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.glperf;
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying an OpenGL animation.  This allows the animation to run in a
+ * separate thread, without requiring that it be driven by the update mechanism
+ * of the view hierarchy.
+ *
+ * The application-specific rendering code is delegated to a GLView.Renderer
+ * instance.
+ */
+class GLPerfView extends GLSurfaceView {
+    private static String TAG = "GLPerfView";
+
+    public GLPerfView(Context context) {
+        super(context);
+        init(false, 0, 0);
+    }
+
+    public GLPerfView(Context context, boolean translucent, int depth, int stencil) {
+        super(context);
+        init(translucent, depth, stencil);
+    }
+
+    private void init(boolean translucent, int depth, int stencil) {
+        setEGLContextFactory(new ContextFactory());
+        setEGLConfigChooser( translucent ?
+              new ConfigChooser(8,8,8,8, depth, stencil) :
+              new ConfigChooser(5,6,5,0, depth, stencil));
+        setRenderer(new Renderer());
+    }
+
+    private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
+        private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+        public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
+            Log.w(TAG, "creating OpenGL ES 2.0 context");
+            checkEglError("Before eglCreateContext", egl);
+            int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
+            EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
+            checkEglError("After eglCreateContext", egl);
+            return context;
+        }
+
+        public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
+            egl.eglDestroyContext(display, context);
+        }
+    }
+
+    private static void checkEglError(String prompt, EGL10 egl) {
+        int error;
+        while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
+            Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
+        }
+    }
+
+    private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
+        private static int EGL_OPENGL_ES2_BIT = 4;
+        private static int[] s_configAttribs2 =
+        {
+            EGL10.EGL_RED_SIZE, 4,
+            EGL10.EGL_GREEN_SIZE, 4,
+            EGL10.EGL_BLUE_SIZE, 4,
+            EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL10.EGL_NONE
+        };
+
+        public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
+            mRedSize = r;
+            mGreenSize = g;
+            mBlueSize = b;
+            mAlphaSize = a;
+            mDepthSize = depth;
+            mStencilSize = stencil;
+        }
+
+        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+
+            int[] num_config = new int[1];
+            egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config);
+
+            int numConfigs = num_config[0];
+
+            if (numConfigs <= 0) {
+                throw new IllegalArgumentException("No configs match configSpec");
+            }
+            EGLConfig[] configs = new EGLConfig[numConfigs];
+            egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);
+            // printConfigs(egl, display, configs);
+            return chooseConfig(egl, display, configs);
+        }
+
+        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+                EGLConfig[] configs) {
+            EGLConfig closestConfig = null;
+            int closestDistance = 1000;
+            for(EGLConfig config : configs) {
+                int d = findConfigAttrib(egl, display, config,
+                        EGL10.EGL_DEPTH_SIZE, 0);
+                int s = findConfigAttrib(egl, display, config,
+                        EGL10.EGL_STENCIL_SIZE, 0);
+                if (d >= mDepthSize && s>= mStencilSize) {
+                    int r = findConfigAttrib(egl, display, config,
+                            EGL10.EGL_RED_SIZE, 0);
+                    int g = findConfigAttrib(egl, display, config,
+                             EGL10.EGL_GREEN_SIZE, 0);
+                    int b = findConfigAttrib(egl, display, config,
+                              EGL10.EGL_BLUE_SIZE, 0);
+                    int a = findConfigAttrib(egl, display, config,
+                            EGL10.EGL_ALPHA_SIZE, 0);
+                    int distance = Math.abs(r - mRedSize)
+                                + Math.abs(g - mGreenSize)
+                                + Math.abs(b - mBlueSize)
+                                + Math.abs(a - mAlphaSize);
+                    if (distance < closestDistance) {
+                        closestDistance = distance;
+                        closestConfig = config;
+                    }
+                }
+            }
+            return closestConfig;
+        }
+
+        private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+                EGLConfig config, int attribute, int defaultValue) {
+
+            if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+                return mValue[0];
+            }
+            return defaultValue;
+        }
+
+        private void printConfigs(EGL10 egl, EGLDisplay display,
+            EGLConfig[] configs) {
+            int numConfigs = configs.length;
+            Log.w(TAG, String.format("%d configurations", numConfigs));
+            for (int i = 0; i < numConfigs; i++) {
+                Log.w(TAG, String.format("Configuration %d:\n", i));
+                printConfig(egl, display, configs[i]);
+            }
+        }
+
+        private void printConfig(EGL10 egl, EGLDisplay display,
+                EGLConfig config) {
+            int[] attributes = {
+                    EGL10.EGL_BUFFER_SIZE,
+                    EGL10.EGL_ALPHA_SIZE,
+                    EGL10.EGL_BLUE_SIZE,
+                    EGL10.EGL_GREEN_SIZE,
+                    EGL10.EGL_RED_SIZE,
+                    EGL10.EGL_DEPTH_SIZE,
+                    EGL10.EGL_STENCIL_SIZE,
+                    EGL10.EGL_CONFIG_CAVEAT,
+                    EGL10.EGL_CONFIG_ID,
+                    EGL10.EGL_LEVEL,
+                    EGL10.EGL_MAX_PBUFFER_HEIGHT,
+                    EGL10.EGL_MAX_PBUFFER_PIXELS,
+                    EGL10.EGL_MAX_PBUFFER_WIDTH,
+                    EGL10.EGL_NATIVE_RENDERABLE,
+                    EGL10.EGL_NATIVE_VISUAL_ID,
+                    EGL10.EGL_NATIVE_VISUAL_TYPE,
+                    0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
+                    EGL10.EGL_SAMPLES,
+                    EGL10.EGL_SAMPLE_BUFFERS,
+                    EGL10.EGL_SURFACE_TYPE,
+                    EGL10.EGL_TRANSPARENT_TYPE,
+                    EGL10.EGL_TRANSPARENT_RED_VALUE,
+                    EGL10.EGL_TRANSPARENT_GREEN_VALUE,
+                    EGL10.EGL_TRANSPARENT_BLUE_VALUE,
+                    0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
+                    0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
+                    0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
+                    0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
+                    EGL10.EGL_LUMINANCE_SIZE,
+                    EGL10.EGL_ALPHA_MASK_SIZE,
+                    EGL10.EGL_COLOR_BUFFER_TYPE,
+                    EGL10.EGL_RENDERABLE_TYPE,
+                    0x3042 // EGL10.EGL_CONFORMANT
+            };
+            String[] names = {
+                    "EGL_BUFFER_SIZE",
+                    "EGL_ALPHA_SIZE",
+                    "EGL_BLUE_SIZE",
+                    "EGL_GREEN_SIZE",
+                    "EGL_RED_SIZE",
+                    "EGL_DEPTH_SIZE",
+                    "EGL_STENCIL_SIZE",
+                    "EGL_CONFIG_CAVEAT",
+                    "EGL_CONFIG_ID",
+                    "EGL_LEVEL",
+                    "EGL_MAX_PBUFFER_HEIGHT",
+                    "EGL_MAX_PBUFFER_PIXELS",
+                    "EGL_MAX_PBUFFER_WIDTH",
+                    "EGL_NATIVE_RENDERABLE",
+                    "EGL_NATIVE_VISUAL_ID",
+                    "EGL_NATIVE_VISUAL_TYPE",
+                    "EGL_PRESERVED_RESOURCES",
+                    "EGL_SAMPLES",
+                    "EGL_SAMPLE_BUFFERS",
+                    "EGL_SURFACE_TYPE",
+                    "EGL_TRANSPARENT_TYPE",
+                    "EGL_TRANSPARENT_RED_VALUE",
+                    "EGL_TRANSPARENT_GREEN_VALUE",
+                    "EGL_TRANSPARENT_BLUE_VALUE",
+                    "EGL_BIND_TO_TEXTURE_RGB",
+                    "EGL_BIND_TO_TEXTURE_RGBA",
+                    "EGL_MIN_SWAP_INTERVAL",
+                    "EGL_MAX_SWAP_INTERVAL",
+                    "EGL_LUMINANCE_SIZE",
+                    "EGL_ALPHA_MASK_SIZE",
+                    "EGL_COLOR_BUFFER_TYPE",
+                    "EGL_RENDERABLE_TYPE",
+                    "EGL_CONFORMANT"
+            };
+            int[] value = new int[1];
+            for (int i = 0; i < attributes.length; i++) {
+                int attribute = attributes[i];
+                String name = names[i];
+                if ( egl.eglGetConfigAttrib(display, config, attribute, value)) {
+                    Log.w(TAG, String.format("  %s: %d\n", name, value[0]));
+                } else {
+                    // Log.w(TAG, String.format("  %s: failed\n", name));
+                    while (egl.eglGetError() != EGL10.EGL_SUCCESS);
+                }
+            }
+        }
+
+        // Subclasses can adjust these values:
+        protected int mRedSize;
+        protected int mGreenSize;
+        protected int mBlueSize;
+        protected int mAlphaSize;
+        protected int mDepthSize;
+        protected int mStencilSize;
+        private int[] mValue = new int[1];
+    }
+
+    private static class Renderer implements GLSurfaceView.Renderer {
+        public void onDrawFrame(GL10 gl) {
+            GLPerfLib.step();
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            GLPerfLib.init(width, height);
+        }
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+            // Do nothing.
+        }
+    }
+}
+
diff --git a/opengl/tests/gl_yuvtex/Android.mk b/opengl/tests/gl_yuvtex/Android.mk
new file mode 100644
index 0000000..a78db25
--- /dev/null
+++ b/opengl/tests/gl_yuvtex/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	gl_yuvtex.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+    libEGL \
+    libGLESv1_CM \
+    libui
+
+LOCAL_MODULE:= test-opengl-gl_yuvtex
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+
+include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/gl_yuvtex/gl_yuvtex.cpp b/opengl/tests/gl_yuvtex/gl_yuvtex.cpp
new file mode 100644
index 0000000..fbe65f1
--- /dev/null
+++ b/opengl/tests/gl_yuvtex/gl_yuvtex.cpp
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/resource.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <utils/Timers.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+static void printGLString(const char *name, GLenum s) {
+    // fprintf(stderr, "printGLString %s, %d\n", name, s);
+    const char *v = (const char *) glGetString(s);
+    // int error = glGetError();
+    // fprintf(stderr, "glGetError() = %d, result of glGetString = %x\n", error,
+    //        (unsigned int) v);
+    // if ((v < (const char*) 0) || (v > (const char*) 0x10000))
+    //    fprintf(stderr, "GL %s = %s\n", name, v);
+    // else
+    //    fprintf(stderr, "GL %s = (null) 0x%08x\n", name, (unsigned int) v);
+    fprintf(stderr, "GL %s = %s\n", name, v);
+}
+
+static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
+    if (returnVal != EGL_TRUE) {
+        fprintf(stderr, "%s() returned %d\n", op, returnVal);
+    }
+
+    for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
+            = eglGetError()) {
+        fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error),
+                error);
+    }
+}
+
+static void checkGlError(const char* op) {
+    for (GLint error = glGetError(); error; error
+            = glGetError()) {
+        fprintf(stderr, "after %s() glError (0x%x)\n", op, error);
+    }
+}
+
+bool setupGraphics(int w, int h) {
+    glViewport(0, 0, w, h);
+    checkGlError("glViewport");
+    return true;
+}
+
+int align(int x, int a) {
+    return (x + (a-1)) & (~(a-1));
+}
+
+const int yuvTexWidth = 600;
+const int yuvTexHeight = 480;
+const int yuvTexUsage = GraphicBuffer::USAGE_HW_TEXTURE |
+        GraphicBuffer::USAGE_SW_WRITE_RARELY;
+const int yuvTexFormat = HAL_PIXEL_FORMAT_YV12;
+const int yuvTexOffsetY = 0;
+const int yuvTexStrideY = (yuvTexWidth + 0xf) & ~0xf;
+const int yuvTexOffsetV = yuvTexStrideY * yuvTexHeight;
+const int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; 
+const int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * yuvTexHeight/2;
+const int yuvTexStrideU = yuvTexStrideV;
+const bool yuvTexSameUV = false;
+static sp<GraphicBuffer> yuvTexBuffer;
+static GLuint yuvTex;
+
+bool setupYuvTexSurface(EGLDisplay dpy, EGLContext context) {
+    int blockWidth = yuvTexWidth > 16 ? yuvTexWidth / 16 : 1;
+    int blockHeight = yuvTexHeight > 16 ? yuvTexHeight / 16 : 1;
+    yuvTexBuffer = new GraphicBuffer(yuvTexWidth, yuvTexHeight, yuvTexFormat,
+            yuvTexUsage);
+    char* buf = NULL;
+    status_t err = yuvTexBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf));
+    if (err != 0) {
+        fprintf(stderr, "yuvTexBuffer->lock(...) failed: %d\n", err);
+        return false;
+    }
+    for (int x = 0; x < yuvTexWidth; x++) {
+        for (int y = 0; y < yuvTexHeight; y++) {
+            int parityX = (x / blockWidth) & 1;
+            int parityY = (y / blockHeight) & 1;
+            unsigned char intensity = (parityX ^ parityY) ? 63 : 191;
+            buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity;
+            if (x < yuvTexWidth / 2 && y < yuvTexHeight / 2) {
+                buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity;
+                if (yuvTexSameUV) {
+                    buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = intensity;
+                } else if (x < yuvTexWidth / 4 && y < yuvTexHeight / 4) {
+                    buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] =
+                    buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] =
+                    buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] =
+                    buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = intensity;
+                }
+            }
+        }
+    }
+
+    err = yuvTexBuffer->unlock();
+    if (err != 0) {
+        fprintf(stderr, "yuvTexBuffer->unlock() failed: %d\n", err);
+        return false;
+    }
+
+    EGLClientBuffer clientBuffer = (EGLClientBuffer)yuvTexBuffer->getNativeBuffer();
+    EGLImageKHR img = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+            clientBuffer, 0);
+    checkEglError("eglCreateImageKHR");
+    if (img == EGL_NO_IMAGE_KHR) {
+        return false;
+    }
+
+    glGenTextures(1, &yuvTex);
+    checkGlError("glGenTextures");
+    glBindTexture(GL_TEXTURE_EXTERNAL_OES, yuvTex);
+    checkGlError("glBindTexture");
+    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)img);
+    checkGlError("glEGLImageTargetTexture2DOES");
+    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    checkGlError("glTexParameteri");
+    glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    checkGlError("glTexParameteri");
+    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+    checkGlError("glTexEnvx");
+
+    GLint crop[4] = { 0, 0, yuvTexWidth, yuvTexHeight };
+    glTexParameteriv(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_CROP_RECT_OES, crop);
+    checkGlError("glTexParameteriv");
+
+    return true;
+}
+
+void renderFrame(int w, int h) {
+    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
+    checkGlError("glClearColor");
+    glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    checkGlError("glClear");
+    
+    glBindTexture(GL_TEXTURE_EXTERNAL_OES, yuvTex);
+    checkGlError("glBindTexture");
+    glEnable(GL_TEXTURE_EXTERNAL_OES);
+    checkGlError("glEnable");
+
+    glDrawTexiOES(0, 0, 0, w, h);
+    checkGlError("glDrawTexiOES");
+}
+
+void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) {
+
+#define X(VAL) {VAL, #VAL}
+    struct {EGLint attribute; const char* name;} names[] = {
+    X(EGL_BUFFER_SIZE),
+    X(EGL_ALPHA_SIZE),
+    X(EGL_BLUE_SIZE),
+    X(EGL_GREEN_SIZE),
+    X(EGL_RED_SIZE),
+    X(EGL_DEPTH_SIZE),
+    X(EGL_STENCIL_SIZE),
+    X(EGL_CONFIG_CAVEAT),
+    X(EGL_CONFIG_ID),
+    X(EGL_LEVEL),
+    X(EGL_MAX_PBUFFER_HEIGHT),
+    X(EGL_MAX_PBUFFER_PIXELS),
+    X(EGL_MAX_PBUFFER_WIDTH),
+    X(EGL_NATIVE_RENDERABLE),
+    X(EGL_NATIVE_VISUAL_ID),
+    X(EGL_NATIVE_VISUAL_TYPE),
+    X(EGL_SAMPLES),
+    X(EGL_SAMPLE_BUFFERS),
+    X(EGL_SURFACE_TYPE),
+    X(EGL_TRANSPARENT_TYPE),
+    X(EGL_TRANSPARENT_RED_VALUE),
+    X(EGL_TRANSPARENT_GREEN_VALUE),
+    X(EGL_TRANSPARENT_BLUE_VALUE),
+    X(EGL_BIND_TO_TEXTURE_RGB),
+    X(EGL_BIND_TO_TEXTURE_RGBA),
+    X(EGL_MIN_SWAP_INTERVAL),
+    X(EGL_MAX_SWAP_INTERVAL),
+    X(EGL_LUMINANCE_SIZE),
+    X(EGL_ALPHA_MASK_SIZE),
+    X(EGL_COLOR_BUFFER_TYPE),
+    X(EGL_RENDERABLE_TYPE),
+    X(EGL_CONFORMANT),
+   };
+#undef X
+
+    for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) {
+        EGLint value = -1;
+        EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value);
+        EGLint error = eglGetError();
+        if (returnVal && error == EGL_SUCCESS) {
+            printf(" %s: ", names[j].name);
+            printf("%d (0x%x)", value, value);
+        }
+    }
+    printf("\n");
+}
+
+int main(int argc, char** argv) {
+    EGLBoolean returnValue;
+    EGLConfig myConfig = {0};
+
+    EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE };
+    EGLint s_configAttribs[] = {
+            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
+            EGL_NONE };
+    EGLint majorVersion;
+    EGLint minorVersion;
+    EGLContext context;
+    EGLSurface surface;
+    EGLint w, h;
+
+    EGLDisplay dpy;
+
+    checkEglError("<init>");
+    dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    checkEglError("eglGetDisplay");
+    if (dpy == EGL_NO_DISPLAY) {
+        printf("eglGetDisplay returned EGL_NO_DISPLAY.\n");
+        return 0;
+    }
+
+    returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);
+    checkEglError("eglInitialize", returnValue);
+    fprintf(stderr, "EGL version %d.%d\n", majorVersion, minorVersion);
+    if (returnValue != EGL_TRUE) {
+        printf("eglInitialize failed\n");
+        return 0;
+    }
+
+    EGLNativeWindowType window = android_createDisplaySurface();
+    returnValue = EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &myConfig);
+    if (returnValue) {
+        printf("EGLUtils::selectConfigForNativeWindow() returned %d", returnValue);
+        return 1;
+    }
+
+    checkEglError("EGLUtils::selectConfigForNativeWindow");
+
+    printf("Chose this configuration:\n");
+    printEGLConfiguration(dpy, myConfig);
+
+    surface = eglCreateWindowSurface(dpy, myConfig, window, NULL);
+    checkEglError("eglCreateWindowSurface");
+    if (surface == EGL_NO_SURFACE) {
+        printf("gelCreateWindowSurface failed.\n");
+        return 1;
+    }
+
+    context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, context_attribs);
+    checkEglError("eglCreateContext");
+    if (context == EGL_NO_CONTEXT) {
+        printf("eglCreateContext failed\n");
+        return 1;
+    }
+    returnValue = eglMakeCurrent(dpy, surface, surface, context);
+    checkEglError("eglMakeCurrent", returnValue);
+    if (returnValue != EGL_TRUE) {
+        return 1;
+    }
+    eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
+    checkEglError("eglQuerySurface");
+    eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
+    checkEglError("eglQuerySurface");
+    GLint dim = w < h ? w : h;
+
+    fprintf(stderr, "Window dimensions: %d x %d\n", w, h);
+
+    printGLString("Version", GL_VERSION);
+    printGLString("Vendor", GL_VENDOR);
+    printGLString("Renderer", GL_RENDERER);
+    printGLString("Extensions", GL_EXTENSIONS);
+
+    if(!setupYuvTexSurface(dpy, context)) {
+        fprintf(stderr, "Could not set up texture surface.\n");
+        return 1;
+    }
+
+    if(!setupGraphics(w, h)) {
+        fprintf(stderr, "Could not set up graphics.\n");
+        return 1;
+    }
+
+    for (;;) {
+        static int dir = -1;
+
+        renderFrame(w, h);
+        eglSwapBuffers(dpy, surface);
+        checkEglError("eglSwapBuffers");
+
+        if (w <= 10 || h <= 10)
+        {
+            dir = -dir;
+        }
+
+        if (w >= 1300 || h >= 900)
+        {
+            dir = -dir;
+        }
+
+
+        w += dir;
+        h += dir;
+    }
+
+    return 0;
+}
diff --git a/opengl/tests/testFramerate/Android.mk b/opengl/tests/testFramerate/Android.mk
new file mode 100644
index 0000000..500abf3
--- /dev/null
+++ b/opengl/tests/testFramerate/Android.mk
@@ -0,0 +1,19 @@
+#########################################################################
+# Test framerate and look for hiccups
+#########################################################################
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := TestFramerate
+
+include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testFramerate/AndroidManifest.xml b/opengl/tests/testFramerate/AndroidManifest.xml
new file mode 100644
index 0000000..e04342c
--- /dev/null
+++ b/opengl/tests/testFramerate/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.testframerate">
+    <uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8" />
+
+    <application
+            android:label="@string/testFramerate_activity">
+        <activity android:name="TestFramerateActivity"
+                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+                android:launchMode="singleTask"
+                android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/testFramerate/res/values/strings.xml b/opengl/tests/testFramerate/res/values/strings.xml
new file mode 100644
index 0000000..e6b3088
--- /dev/null
+++ b/opengl/tests/testFramerate/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?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.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+     them to be changed based on the locale and options. -->
+
+<resources>
+    <!-- Simple strings. -->
+    <string name="testFramerate_activity">TestFramerate</string>
+
+</resources>
+
diff --git a/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateActivity.java b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateActivity.java
new file mode 100644
index 0000000..cbe279b
--- /dev/null
+++ b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateActivity.java
@@ -0,0 +1,47 @@
+/*
+ * 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.testframerate;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import java.io.File;
+
+
+public class TestFramerateActivity extends Activity {
+
+    TestFramerateView mView;
+
+    @Override protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mView = new TestFramerateView(getApplication());
+        setContentView(mView);
+        mView.setFocusableInTouchMode(true);
+    }
+
+    @Override protected void onPause() {
+        super.onPause();
+        mView.onPause();
+    }
+
+    @Override protected void onResume() {
+        super.onResume();
+        mView.onResume();
+    }
+}
diff --git a/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateView.java b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateView.java
new file mode 100644
index 0000000..f3fb5de
--- /dev/null
+++ b/opengl/tests/testFramerate/src/com/android/testframerate/TestFramerateView.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testframerate;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.SystemProperties;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.opengl.GLES20;
+
+class TestFramerateView extends GLSurfaceView {
+    private static String TAG = "TestFramerateView";
+
+    public TestFramerateView(Context context) {
+        super(context);
+        setEGLContextClientVersion(2);
+        setRenderer(new Renderer());
+    }
+
+    private long mLastTime_us = 0;
+    private long mNumShortFramesElapsed = 0;
+    private void registerTime(long now_us) {
+        long longFrameTime_ms = Integer.parseInt(SystemProperties.get("debug.longframe_ms", "16"));
+        long elapsedTime_us = now_us - mLastTime_us;
+        float fps = 1000000.f / elapsedTime_us;
+        if (mLastTime_us > 0 && elapsedTime_us > longFrameTime_ms*1000) {
+          Log.v(TAG, "Long frame: " + elapsedTime_us/1000.f + " ms (" + fps + " fps)");
+          if (mNumShortFramesElapsed > 0) {
+            Log.v(TAG, "  Short frames since last long frame: " + mNumShortFramesElapsed);
+            mNumShortFramesElapsed = 0;
+          }
+        } else {
+            ++mNumShortFramesElapsed;
+        }
+
+        mLastTime_us = now_us;
+    }
+
+    private class Renderer implements GLSurfaceView.Renderer {
+        public Renderer() {
+        }
+
+
+        public void onDrawFrame(GL10 gl) {
+            long now_us = System.nanoTime() / 1000;
+            registerTime(now_us);
+
+            float red = (now_us % 1000000) / 1000000.f;
+            float green = (now_us % 2000000) / 2000000.f;
+            float blue = (now_us % 3000000) / 3000000.f;
+            GLES20.glClearColor(red, green, blue, 1.0f);
+            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            GLES20.glViewport(0, 0, width, height);
+        }
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        }
+
+    }
+}
diff --git a/opengl/tests/testLatency/Android.mk b/opengl/tests/testLatency/Android.mk
new file mode 100644
index 0000000..96417c7
--- /dev/null
+++ b/opengl/tests/testLatency/Android.mk
@@ -0,0 +1,20 @@
+#########################################################################
+# Test end-to-end latency.
+#########################################################################
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SDK_VERSION := 8
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := TestLatency
+
+include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testLatency/AndroidManifest.xml b/opengl/tests/testLatency/AndroidManifest.xml
new file mode 100644
index 0000000..741266e
--- /dev/null
+++ b/opengl/tests/testLatency/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.testlatency">
+    <uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8" />
+
+    <application
+            android:label="@string/testLatency_activity">
+        <activity android:name="TestLatencyActivity"
+                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+                android:launchMode="singleTask"
+                android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/testLatency/res/values/strings.xml b/opengl/tests/testLatency/res/values/strings.xml
new file mode 100644
index 0000000..0309991
--- /dev/null
+++ b/opengl/tests/testLatency/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?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.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+     them to be changed based on the locale and options. -->
+
+<resources>
+    <!-- Simple strings. -->
+    <string name="testLatency_activity">TestLatency</string>
+
+</resources>
+
diff --git a/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyActivity.java b/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyActivity.java
new file mode 100644
index 0000000..ed993cb
--- /dev/null
+++ b/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyActivity.java
@@ -0,0 +1,47 @@
+/*
+ * 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.testlatency;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+import java.io.File;
+
+
+public class TestLatencyActivity extends Activity {
+
+    TestLatencyView mView;
+
+    @Override protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mView = new TestLatencyView(getApplication());
+        setContentView(mView);
+        mView.setFocusableInTouchMode(true);
+    }
+
+    @Override protected void onPause() {
+        super.onPause();
+        mView.onPause();
+    }
+
+    @Override protected void onResume() {
+        super.onResume();
+        mView.onResume();
+    }
+}
diff --git a/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyView.java b/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyView.java
new file mode 100644
index 0000000..d62bf17
--- /dev/null
+++ b/opengl/tests/testLatency/src/com/android/testlatency/TestLatencyView.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.testlatency;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.opengl.GLES20;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying an OpenGL animation.  This allows the animation to run in a
+ * separate thread, without requiring that it be driven by the update mechanism
+ * of the view hierarchy.
+ *
+ * The application-specific rendering code is delegated to a GLView.Renderer
+ * instance.
+ */
+class TestLatencyView extends GLSurfaceView {
+    private static String TAG = "TestLatencyiew";
+    private float mX;
+    private float mY;
+    private float mDX;
+    private float mDY;
+    private long  mT;
+    private long  mDT;
+
+    public TestLatencyView(Context context) {
+        super(context);
+        setEGLContextClientVersion(2);
+        setRenderer(new Renderer());
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        switch (event.getAction()) {
+        case MotionEvent.ACTION_MOVE:
+            float x = event.getX();
+            float y = event.getY();
+            long  t = event.getEventTime();
+            synchronized(this) {
+                mDT = t - mT;
+                mT = t;
+                mDX = x - mX;
+                mX = x;
+                mDY = y - mY;
+                mY = y;
+            }
+            break;
+        default:
+            break;
+        }
+        return true;
+    }
+
+    private class Renderer implements GLSurfaceView.Renderer {
+        private float mScaleX, mScaleY, mOffsetX, mOffsetY;
+        private final float MS_PER_FRAME = 1000 / 60;
+        public Renderer() {
+            mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length * 4)
+                .order(ByteOrder.nativeOrder()).asFloatBuffer();
+        }
+
+
+        public void onDrawFrame(GL10 gl) {
+            GLES20.glClearColor(0.4f, 0.4f, 0.4f, 1.0f);
+            GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
+            GLES20.glUseProgram(mProgram);
+            checkGlError("glUseProgram");
+
+            float x, y, dx, dy;
+            long t, dt;
+            synchronized(TestLatencyView.this) {
+                x = mX;
+                y = mY;
+                dx = mDX;
+                dy = mDY;
+                dt = mDT;
+            }
+
+            if (dt > 0) {
+                dx = dx * MS_PER_FRAME / dt;
+                dy = dy * MS_PER_FRAME / dt;
+            }
+
+            GLES20.glEnableVertexAttribArray(mvPositionHandle);
+            checkGlError("glEnableVertexAttribArray");
+            GLES20.glEnableVertexAttribArray(mvColorHandle);
+            checkGlError("glEnableVertexAttribArray");
+            for(int step = 0; step < 8; step++) {
+                float sx = (x + dx * step) * mScaleX + mOffsetX;
+                float sy = (y + dy * step) * mScaleY + mOffsetY;
+                int cbase = step * 4;
+
+                for (int i = 0; i < mTriangleVerticesData.length; i += 6) {
+                    mTriangleVerticesData2[i] = sx + mTriangleVerticesData[i];
+                    mTriangleVerticesData2[i+1] = -sy + mTriangleVerticesData[i+1];
+                    mTriangleVerticesData2[i+2] = mColors[cbase];
+                    mTriangleVerticesData2[i+3] = mColors[cbase+1];
+                    mTriangleVerticesData2[i+4] = mColors[cbase+2];
+                    mTriangleVerticesData2[i+5] = mColors[cbase+3];
+                }
+                mTriangleVertices.position(0);
+                mTriangleVertices.put(mTriangleVerticesData2).position(0);
+
+                GLES20.glVertexAttribPointer(mvPositionHandle, 2, GLES20.GL_FLOAT, false, 6*4, mTriangleVertices);
+                checkGlError("glVertexAttribPointer mvPosition");
+                mTriangleVertices.put(mTriangleVerticesData2).position(2);
+                GLES20.glVertexAttribPointer(mvColorHandle, 4, GLES20.GL_FLOAT, false, 6*4, mTriangleVertices);
+                checkGlError("glVertexAttribPointer mvColor");
+                GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
+                checkGlError("glDrawArrays");
+            }
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            GLES20.glViewport(0, 0, width, height);
+            mScaleX = 2.0f / width;
+            mScaleY = 2.0f / height;
+            mOffsetX = -1f;
+            mOffsetY = -1f;
+        }
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+            mProgram = createProgram(mVertexShader, mFragmentShader);
+            if (mProgram == 0) {
+                return;
+            }
+            mvPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
+            checkGlError("glGetAttribLocation");
+            if (mvPositionHandle == -1) {
+                throw new RuntimeException("Could not get attrib location for vPosition");
+            }
+            mvColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
+            checkGlError("glGetAttribLocation");
+            if (mvColorHandle == -1) {
+                throw new RuntimeException("Could not get attrib location for vColor");
+            }
+        }
+
+        private int loadShader(int shaderType, String source) {
+            int shader = GLES20.glCreateShader(shaderType);
+            if (shader != 0) {
+                GLES20.glShaderSource(shader, source);
+                GLES20.glCompileShader(shader);
+                int[] compiled = new int[1];
+                GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+                if (compiled[0] == 0) {
+                    Log.e(TAG, "Could not compile shader " + shaderType + ":");
+                    Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
+                    GLES20.glDeleteShader(shader);
+                    shader = 0;
+                }
+            }
+            return shader;
+        }
+
+        private int createProgram(String vertexSource, String fragmentSource) {
+            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+            if (vertexShader == 0) {
+                return 0;
+            }
+
+            int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+            if (pixelShader == 0) {
+                return 0;
+            }
+
+            int program = GLES20.glCreateProgram();
+            if (program != 0) {
+                GLES20.glAttachShader(program, vertexShader);
+                checkGlError("glAttachShader vertexShader");
+                GLES20.glAttachShader(program, pixelShader);
+                checkGlError("glAttachShader pixelShader");
+                GLES20.glLinkProgram(program);
+                int[] linkStatus = new int[1];
+                GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+                if (linkStatus[0] != GLES20.GL_TRUE) {
+                    Log.e(TAG, "Could not link program: ");
+                    Log.e(TAG, GLES20.glGetProgramInfoLog(program));
+                    GLES20.glDeleteProgram(program);
+                    program = 0;
+                }
+            }
+            return program;
+        }
+
+        private void checkGlError(String op) {
+            int error;
+            while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
+                Log.e(TAG, op + ": glError " + error);
+                throw new RuntimeException(op + ": glError " + error);
+            }
+        }
+
+        // X, Y, R G B A
+        private final float[] mTriangleVerticesData = {
+                -0.025f, 0.3f, 0.0f, 1.0f, 0.0f, 1.0f,
+                 0.0f  , 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
+                 0.025f, 0.3f, 1.0f, 1.0f, 255.0f, 1.0f
+                };
+
+        // Color cascade:
+        private final float[] mColors = {
+                0.0f, 0.0f, 0.0f, 1.0f,
+                0.5f, 0.0f, 0.0f, 1.0f,
+                0.0f, 0.5f, 0.0f, 1.0f,
+                0.5f, 0.5f, 0.0f, 1.0f,
+
+                0.0f, 0.0f, 0.5f, 1.0f,
+                1.0f, 0.0f, 0.0f, 1.0f,
+                1.0f, 1.0f, 1.0f, 1.0f,
+                0.0f, 1.0f, 0.0f, 1.0f
+        };
+
+        private float[] mTriangleVerticesData2 = new float[mTriangleVerticesData.length];
+        private FloatBuffer mTriangleVertices;
+
+        private final String mVertexShader = "attribute vec4 aPosition;\n"
+            + "attribute vec4 aColor;\n"
+            + "varying vec4 vColor;\n"
+            + "void main() {\n"
+            + "  gl_Position = aPosition;\n"
+            + "  vColor = aColor;\n"
+            + "}\n";
+
+        private final String mFragmentShader = "precision mediump float;\n"
+            + "varying vec4 vColor;\n"
+            + "void main() {\n"
+            + "  gl_FragColor = vColor;\n"
+            + "}\n";
+
+        private int mProgram;
+        private int mvPositionHandle;
+        private int mvColorHandle;
+
+    }
+}
+
diff --git a/opengl/tests/testViewport/Android.mk b/opengl/tests/testViewport/Android.mk
new file mode 100644
index 0000000..ab37809
--- /dev/null
+++ b/opengl/tests/testViewport/Android.mk
@@ -0,0 +1,26 @@
+#########################################################################
+# OpenGL ES JNI sample
+# This makefile builds both an activity and a shared library.
+#########################################################################
+ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := TestViewport
+
+# Set a specific SDK version so we can run on Froyo.
+
+LOCAL_SDK_VERSION := 8
+
+include $(BUILD_PACKAGE)
+
+endif # TARGET_SIMULATOR
diff --git a/opengl/tests/testViewport/AndroidManifest.xml b/opengl/tests/testViewport/AndroidManifest.xml
new file mode 100644
index 0000000..90a9d2d
--- /dev/null
+++ b/opengl/tests/testViewport/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.test">
+    <uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application
+            android:label="@string/test_activity">
+        <activity android:name="TestActivity"
+                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+            	android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/testViewport/README b/opengl/tests/testViewport/README
new file mode 100644
index 0000000..c06abc9
--- /dev/null
+++ b/opengl/tests/testViewport/README
@@ -0,0 +1,28 @@
+Repro steps:
+
+build, install and run the attached test program TestViewport.apk
+
+Run on Sapphire with Froyo.
+
+The program clears the screen to blue, then draws a full screen white quad that
+is alligned to the screen.
+(Therefore the whole screen should appear to be white.)
+
+
+Note that screen is all white.
+
+Rotate screen 90 degrees.
+
+Expected: screen is still all white.
+
+Actual: screen is blue with offset white rectangle.
+
+This bug only happens on Sapphire, it works correctly on Passion.
+
+What happens:
+
+I think the bug is that the gl.glViewport() call in onSurfaceChanged() is
+being ignored by the OpenGL driver.
+
+NOTE: If a gl.glViewport call is added at the beginning of the onDrawFrame()
+call (which means it is called before every draw), the program runs correctly.
diff --git a/opengl/tests/testViewport/res/values/strings.xml b/opengl/tests/testViewport/res/values/strings.xml
new file mode 100644
index 0000000..f4b8bbb
--- /dev/null
+++ b/opengl/tests/testViewport/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?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.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+     them to be changed based on the locale and options. -->
+
+<resources>
+    <!-- Simple strings. -->
+    <string name="test_activity">Test Viewport</string>
+
+</resources>
+
diff --git a/opengl/tests/testViewport/src/com/android/test/TestActivity.java b/opengl/tests/testViewport/src/com/android/test/TestActivity.java
new file mode 100644
index 0000000..cc7e450
--- /dev/null
+++ b/opengl/tests/testViewport/src/com/android/test/TestActivity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.test;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestActivity extends Activity {
+    private final static String TAG = "TestActivity";
+    TestView mView;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mView = new TestView(getApplication());
+	    mView.setFocusableInTouchMode(true);
+	    setContentView(mView);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mView.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mView.onResume();
+    }
+}
diff --git a/opengl/tests/testViewport/src/com/android/test/TestView.java b/opengl/tests/testViewport/src/com/android/test/TestView.java
new file mode 100644
index 0000000..23cc37d
--- /dev/null
+++ b/opengl/tests/testViewport/src/com/android/test/TestView.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test;
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.FloatBuffer;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL11;
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying an OpenGL animation.  This allows the animation to run in a
+ * separate thread, without requiring that it be driven by the update mechanism
+ * of the view hierarchy.
+ *
+ * The application-specific rendering code is delegated to a GLView.Renderer
+ * instance.
+ */
+class TestView extends GLSurfaceView {
+    TestView(Context context) {
+        super(context);
+        init();
+    }
+
+    public TestView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    private void init() {
+        setRenderer(new Renderer());
+        setRenderMode(RENDERMODE_WHEN_DIRTY);
+    }
+    
+        /** A grid is a topologically rectangular array of vertices.
+    *
+    * The vertex and index data are held in VBO objects because on most
+    * GPUs VBO objects are the fastest way of rendering static vertex
+    * and index data.
+    *
+    */
+
+   private static class Grid {
+       // Size of vertex data elements in bytes:
+       final static int FLOAT_SIZE = 4;
+       final static int CHAR_SIZE = 2;
+
+       // Vertex structure:
+       // float x, y, z;
+
+       final static int VERTEX_SIZE = 3 * FLOAT_SIZE;
+
+       private int mVertexBufferObjectId;
+       private int mElementBufferObjectId;
+
+       // These buffers are used to hold the vertex and index data while
+       // constructing the grid. Once createBufferObjects() is called
+       // the buffers are nulled out to save memory.
+
+       private ByteBuffer mVertexByteBuffer;
+       private FloatBuffer mVertexBuffer;
+       private CharBuffer mIndexBuffer;
+
+       private int mW;
+       private int mH;
+       private int mIndexCount;
+
+       public Grid(int w, int h) {
+           if (w < 0 || w >= 65536) {
+               throw new IllegalArgumentException("w");
+           }
+           if (h < 0 || h >= 65536) {
+               throw new IllegalArgumentException("h");
+           }
+           if (w * h >= 65536) {
+               throw new IllegalArgumentException("w * h >= 65536");
+           }
+
+           mW = w;
+           mH = h;
+           int size = w * h;
+
+           mVertexByteBuffer = ByteBuffer.allocateDirect(VERTEX_SIZE * size)
+               .order(ByteOrder.nativeOrder());
+           mVertexBuffer = mVertexByteBuffer.asFloatBuffer();
+
+           int quadW = mW - 1;
+           int quadH = mH - 1;
+           int quadCount = quadW * quadH;
+           int indexCount = quadCount * 6;
+           mIndexCount = indexCount;
+           mIndexBuffer = ByteBuffer.allocateDirect(CHAR_SIZE * indexCount)
+               .order(ByteOrder.nativeOrder()).asCharBuffer();
+
+           /*
+            * Initialize triangle list mesh.
+            *
+            *     [0]-----[  1] ...
+            *      |    /   |
+            *      |   /    |
+            *      |  /     |
+            *     [w]-----[w+1] ...
+            *      |       |
+            *
+            */
+
+           {
+               int i = 0;
+               for (int y = 0; y < quadH; y++) {
+                   for (int x = 0; x < quadW; x++) {
+                       char a = (char) (y * mW + x);
+                       char b = (char) (y * mW + x + 1);
+                       char c = (char) ((y + 1) * mW + x);
+                       char d = (char) ((y + 1) * mW + x + 1);
+
+                       mIndexBuffer.put(i++, a);
+                       mIndexBuffer.put(i++, c);
+                       mIndexBuffer.put(i++, b);
+
+                       mIndexBuffer.put(i++, b);
+                       mIndexBuffer.put(i++, c);
+                       mIndexBuffer.put(i++, d);
+                   }
+               }
+           }
+
+       }
+
+       public void set(int i, int j, float x, float y, float z) {
+           if (i < 0 || i >= mW) {
+               throw new IllegalArgumentException("i");
+           }
+           if (j < 0 || j >= mH) {
+               throw new IllegalArgumentException("j");
+           }
+
+           int index = mW * j + i;
+
+           mVertexBuffer.position(index * VERTEX_SIZE / FLOAT_SIZE);
+           mVertexBuffer.put(x);
+           mVertexBuffer.put(y);
+           mVertexBuffer.put(z);
+       }
+
+       public void createBufferObjects(GL gl) {
+           // Generate a the vertex and element buffer IDs
+           int[] vboIds = new int[2];
+           GL11 gl11 = (GL11) gl;
+           gl11.glGenBuffers(2, vboIds, 0);
+           mVertexBufferObjectId = vboIds[0];
+           mElementBufferObjectId = vboIds[1];
+
+           // Upload the vertex data
+           gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId);
+           mVertexByteBuffer.position(0);
+           gl11.glBufferData(GL11.GL_ARRAY_BUFFER, mVertexByteBuffer.capacity(), mVertexByteBuffer, GL11.GL_STATIC_DRAW);
+
+           gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId);
+           mIndexBuffer.position(0);
+           gl11.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer.capacity() * CHAR_SIZE, mIndexBuffer, GL11.GL_STATIC_DRAW);
+
+           // We don't need the in-memory data any more
+           mVertexBuffer = null;
+           mVertexByteBuffer = null;
+           mIndexBuffer = null;
+       }
+
+       public void draw(GL10 gl) {
+           GL11 gl11 = (GL11) gl;
+
+           gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+
+           gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId);
+           gl11.glVertexPointer(3, GL10.GL_FLOAT, VERTEX_SIZE, 0);
+           
+           gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId);
+           gl11.glDrawElements(GL10.GL_TRIANGLES, mIndexCount, GL10.GL_UNSIGNED_SHORT, 0);
+           gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
+           gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
+           gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
+       }
+   }
+
+
+    private class Renderer implements GLSurfaceView.Renderer {
+        private static final String TAG = "Renderer";
+        private Grid mGrid;
+        
+        public void onDrawFrame(GL10 gl) {
+			gl.glClearColor(0,0,1,1);
+			gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
+            mGrid.draw(gl);
+        }
+
+        public void onSurfaceChanged(GL10 gl, int width, int height) {
+            gl.glViewport(0, 0, width, height);
+			gl.glMatrixMode(GL11.GL_PROJECTION);
+			gl.glLoadIdentity();
+			gl.glOrthof(0, width, height, 0, -1, 1);
+			gl.glMatrixMode(GL11.GL_MODELVIEW);
+            createGrid(gl, width, height);
+        }
+
+        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        }
+        
+        private void createGrid(GL10 gl, float w, float h) {
+        mGrid = new Grid(2, 2);
+			for (int j = 0; j < 2; j++) {
+				for (int i = 0; i < 2; i++) {
+					float x = w * i;
+					float y = h * j;
+					float z = 0.0f;
+					mGrid.set(i,j, x, y, z);
+				}
+			}
+			mGrid.createBufferObjects(gl);
+		}
+    }
+}
+
diff --git a/packages/DefaultContainerService/res/values/strings.xml b/packages/DefaultContainerService/res/values/strings.xml
index 37f5b61..ffd6b59 100644
--- a/packages/DefaultContainerService/res/values/strings.xml
+++ b/packages/DefaultContainerService/res/values/strings.xml
@@ -18,6 +18,6 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- service name  -->
+    <!-- service name [CHAR LIMIT=25]  -->
     <string name="service_name">Package Access Helper</string>
 </resources>
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index ce10f5b..e6624ae 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -440,7 +440,7 @@
         String status = Environment.getExternalStorageState();
         long availSDSize = -1;
         boolean mediaAvailable = false;
-        if (status.equals(Environment.MEDIA_MOUNTED)) {
+        if (!Environment.isExternalStorageEmulated() && status.equals(Environment.MEDIA_MOUNTED)) {
             StatFs sdStats = new StatFs(
                     Environment.getExternalStorageDirectory().getPath());
             availSDSize = (long)sdStats.getAvailableBlocks() *
diff --git a/packages/SettingsProvider/res/values-rm/strings.xml b/packages/SettingsProvider/res/values-rm/strings.xml
new file mode 100644
index 0000000..861e625
--- /dev/null
+++ b/packages/SettingsProvider/res/values-rm/strings.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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4567566098528588863">"Memoria dals parameters"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 52e04d7..8a6e82d 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -3,16 +3,16 @@
 /**
  * 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 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0 
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software 
- * distributed under the License is distributed on an "AS IS" BASIS, 
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
- * See the License for the specific language governing permissions and 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 -->
@@ -24,6 +24,7 @@
     <string name="def_airplane_mode_radios" translatable="false">cell,bluetooth,wifi</string>
     <string name="airplane_mode_toggleable_radios" translatable="false">bluetooth,wifi</string>
     <bool name="def_auto_time">true</bool>
+    <bool name="def_auto_time_zone">true</bool>
     <bool name="def_accelerometer_rotation">true</bool>
     <!-- Default screen brightness, from 0 to 255.  102 is 40%. -->
     <integer name="def_screen_brightness">102</integer>
@@ -31,12 +32,12 @@
     <fraction name="def_window_animation_scale">100%</fraction>
     <fraction name="def_window_transition_scale">100%</fraction>
     <bool name="def_haptic_feedback">true</bool>
-    
+
     <bool name="def_bluetooth_on">false</bool>
     <bool name="def_install_non_market_apps">false</bool>
-    <!-- Comma-separated list of location providers. 
+    <!-- Comma-separated list of location providers.
          Network location is off by default because it requires
-         user opt-in via Setup Wizard or Settings.  
+         user opt-in via Setup Wizard or Settings.
     -->
     <string name="def_location_providers_allowed" translatable="false">gps</string>
     <bool name="assisted_gps_enabled">true</bool>
@@ -45,11 +46,11 @@
     <bool name="def_usb_mass_storage_enabled">true</bool>
     <bool name="def_wifi_on">false</bool>
     <bool name="def_networks_available_notification_on">true</bool>
-    
+
     <bool name="def_backup_enabled">false</bool>
     <string name="def_backup_transport" translatable="false">android/com.android.internal.backup.LocalTransport</string>
 
-    <!-- Default value for whether or not to pulse the notification LED when there is a 
+    <!-- Default value for whether or not to pulse the notification LED when there is a
          pending notification -->
     <bool name="def_notification_pulse">true</bool>
 
@@ -72,4 +73,36 @@
 
     <!-- Default for Settings.System.VIBRATE_IN_SILENT -->
     <bool name="def_vibrate_in_silent">true</bool>
+
+    <bool name="def_use_ptp_interface">false</bool>
+
+    <!-- Default for Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION -->
+    <bool name="def_accessibility_script_injection">false</bool>
+
+    <!-- Default for Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS -->
+    <string name="def_accessibility_web_content_key_bindings" translatable="false">
+            <!-- DPAD/Trackball UP maps to traverse previous on current axis and send an event. -->
+            0x13=0x01000100;
+            <!-- DPAD/Trackball DOWN maps to traverse next on current axis and send an event. -->
+            0x14=0x01010100;
+            <!-- DPAD/Trackball LEFT maps to action in non-android default navigation axis. -->
+            0x15=0x04000000;
+            <!-- DPAD/Trackball RIGHT maps to no action in non-android default navigation axis. -->
+            0x16=0x04000000;
+            <!-- Left Alt+DPAD/Trackball UP transitions from an axis to another and sends an event. -->
+            <!-- Axis transitions:  2 -> 7; 1 -> 2; 0 -> 1; 3 -> 0; 4 -> 0; 5 -> 0; 6 -> 0; -->
+            0x120013=0x03020701:0x03010201:0x03000101:0x03030001:0x03040001:0x03050001:0x03060001;
+            <!-- Left Alt+DPAD/Trackball DOWN transitions from an axis to another and sends an event. -->
+            <!-- Axis transitions: 1 -> 0; 2 -> 1; 7 -> 2; 3 -> 7; 4 -> 7; 5 -> 7; 6 -> 7; -->
+            0x120014=0x03010001:0x03020101:0x03070201:0x03030701:0x03040701:0x03050701:0x03060701;
+            <!-- Left Alt+DPAD/Trackball LEFT transitions from an axis to another and sends an event. -->
+            <!-- Axis transitions: 4 -> 3; 5 -> 4; 6 -> 5; 0 -> 6; 1 -> 6; 2 -> 6; 7 -> 6; -->
+            0x120015=0x03040301:0x03050401:0x03060501:0x03000601:0x03010601:0x03020601:0x03070601;
+            <!-- Left Alt+DPAD/Trackball RIGHT transitions from an axis to another and sends an event.  -->
+            <!-- Axis transitions: 5 -> 6; 4 -> 5; 3 -> 4; 2 -> 3; 7 -> 3; 1 -> 3; 0 -> 3; -->
+            0x120016=0x03050601:0x03040501:0x03030401:0x03020301:0x03070301:0x03010301:0x03000301;
+    </string>
+
+    <!-- Default for Settings.System.USER_ROTATION -->
+    <integer name="def_user_rotation">0</integer>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index ad04bb4..6c84afd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -16,12 +16,20 @@
 
 package com.android.providers.settings;
 
+import com.android.internal.content.PackageHelper;
+import com.android.internal.telephony.RILConstants;
+import com.android.internal.util.XmlUtils;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.res.XmlResourceParser;
 import android.database.Cursor;
@@ -35,18 +43,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Config;
 import android.util.Log;
-import android.util.Xml;
-
-import com.android.internal.content.PackageHelper;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.util.XmlUtils;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockPatternView;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 import java.util.HashSet;
@@ -64,7 +61,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 57;
+    private static final int DATABASE_VERSION = 61;
 
     private Context mContext;
 
@@ -734,6 +731,84 @@
             }
             upgradeVersion = 57;
         }
+
+        if (upgradeVersion == 57) {
+            /*
+             * New settings to:
+             *  1. Enable injection of accessibility scripts in WebViews.
+             *  2. Define the key bindings for traversing web content in WebViews.
+             */
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            try {
+                stmt = db.compileStatement("INSERT INTO secure(name,value)"
+                        + " VALUES(?,?);");
+                loadBooleanSetting(stmt, Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION,
+                        R.bool.def_accessibility_script_injection);
+                stmt.close();
+                stmt = db.compileStatement("INSERT INTO secure(name,value)"
+                        + " VALUES(?,?);");
+                loadStringSetting(stmt, Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS,
+                        R.string.def_accessibility_web_content_key_bindings);
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 58;
+        }
+
+        if (upgradeVersion == 58) {
+            /* Add default for new Auto Time Zone */
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            try {
+                stmt = db.compileStatement("INSERT INTO secure(name,value)"
+                        + " VALUES(?,?);");
+                loadBooleanSetting(stmt, Settings.System.AUTO_TIME_ZONE,
+                        R.bool.def_auto_time_zone); // Sync timezone to NITZ
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 59;
+        }
+
+        if (upgradeVersion == 59) {
+            // Persistence for the rotation lock feature.
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            try {
+                stmt = db.compileStatement("INSERT INTO system(name,value)"
+                        + " VALUES(?,?);");
+                loadBooleanSetting(stmt, Settings.System.USER_ROTATION,
+                        R.integer.def_user_rotation); // should be zero degrees
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 60;
+        }
+
+        if (upgradeVersion == 60) {
+            // Increase screen timeout for tablet
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            try {
+                stmt = db.compileStatement("INSERT OR REPLACE INTO system(name,value)"
+                        + " VALUES(?,?);");
+                loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT,
+                        R.integer.def_screen_off_timeout); 
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 61;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
@@ -876,7 +951,7 @@
                 String cls = parser.getAttributeValue(null, "class");
                 String shortcutStr = parser.getAttributeValue(null, "shortcut");
 
-                int shortcutValue = (int) shortcutStr.charAt(0);
+                int shortcutValue = shortcutStr.charAt(0);
                 if (TextUtils.isEmpty(shortcutStr)) {
                     Log.w(TAG, "Unable to get shortcut for: " + pkg + "/" + cls);
                 }
@@ -1044,7 +1119,10 @@
     
             loadBooleanSetting(stmt, Settings.System.AUTO_TIME,
                     R.bool.def_auto_time); // Sync time to NITZ
-    
+
+            loadBooleanSetting(stmt, Settings.System.AUTO_TIME_ZONE,
+                    R.bool.def_auto_time_zone); // Sync timezone to NITZ
+
             loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS,
                     R.integer.def_screen_brightness);
     
@@ -1068,6 +1146,9 @@
     
             loadBooleanSetting(stmt, Settings.System.VIBRATE_IN_SILENT,
                     R.bool.def_vibrate_in_silent);
+
+            loadBooleanSetting(stmt, Settings.System.USE_PTP_INTERFACE,
+                    R.bool.def_use_ptp_interface);
         } finally {
             if (stmt != null) stmt.close();
         }
@@ -1184,6 +1265,12 @@
     
             loadBooleanSetting(stmt, Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED,
                     R.bool.def_mount_ums_notify_enabled);
+
+            loadBooleanSetting(stmt, Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION,
+                    R.bool.def_accessibility_script_injection);
+
+            loadStringSetting(stmt, Settings.Secure.ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS,
+                    R.string.def_accessibility_web_content_key_bindings);
         } finally {
             if (stmt != null) stmt.close();
         }
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 4c83768..2c13069 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -3,11 +3,16 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES := $(call all-subdir-java-files) \
+    ../../../ex/carousel/java/com/android/ex/carousel/carousel.rs
 
 LOCAL_JAVA_LIBRARIES := services
 
+LOCAL_STATIC_JAVA_LIBRARIES := android-common-carousel
+
 LOCAL_PACKAGE_NAME := SystemUI
 LOCAL_CERTIFICATE := platform
 
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
 include $(BUILD_PACKAGE)
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 7995869..d824f30 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -5,16 +5,22 @@
         >
 
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.GET_TASKS" />
 
     <application
         android:persistent="true"
         android:allowClearUserData="false"
         android:label="@string/app_label"
         android:icon="@drawable/ic_launcher_settings">
-                 
-        <service
-            android:name=".statusbar.StatusBarService"
-            android:exported="false"
+
+        <!-- Broadcast receiver that gets the broadcast at boot time and starts
+             up everything else.
+             TODO: Should have an android:permission attribute
+             -->
+        <service android:name="SystemUIService"
+            android:exported="true"
             />
 
         <activity android:name=".usb.UsbStorageActivity"
@@ -25,5 +31,12 @@
                 android:excludeFromRecents="true">
         </activity>
 
+        <activity android:name=".recent.RecentApplicationsActivity"
+            android:theme="@android:style/Theme.NoTitleBar"
+            android:excludeFromRecents="true"
+            android:launchMode="singleInstance"
+            android:exported="true">
+        </activity>
+
     </application>
 </manifest>
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
new file mode 100644
index 0000000..5e48461
--- /dev/null
+++ b/packages/SystemUI/proguard.flags
@@ -0,0 +1,6 @@
+-keep class com.android.systemui.statusbar.tablet.TabletStatusBarService {
+  public void notificationIconsClicked(android.view.View);
+  public void systemInfoClicked(android.view.View);
+  public void recentButtonClicked(android.view.View);
+  public void toggleLightsOut(android.view.View);
+}
diff --git a/packages/SystemUI/res/anim/hydraulic_brake_interpolator.xml b/packages/SystemUI/res/anim/hydraulic_brake_interpolator.xml
new file mode 100644
index 0000000..5b6778e
--- /dev/null
+++ b/packages/SystemUI/res/anim/hydraulic_brake_interpolator.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/ease_out_interpolator.xml
+**
+** 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.
+*/
+-->
+
+<decelerateInterpolator 
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:factor="10.0" />
diff --git a/packages/SystemUI/res/anim/lights_out_in.xml b/packages/SystemUI/res/anim/lights_out_in.xml
new file mode 100644
index 0000000..f76a452
--- /dev/null
+++ b/packages/SystemUI/res/anim/lights_out_in.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <translate android:fromYDelta="-100%p" android:toYDelta="0"
+        android:duration="@android:integer/config_mediumAnimTime" 
+        android:interpolator="@anim/hydraulic_brake_interpolator"
+        />
+    <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/lights_out_out.xml b/packages/SystemUI/res/anim/lights_out_out.xml
new file mode 100644
index 0000000..610ac7a
--- /dev/null
+++ b/packages/SystemUI/res/anim/lights_out_out.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <translate android:toYDelta="-100%p" android:fromYDelta="0" 
+        android:duration="@android:integer/config_mediumAnimTime" 
+        android:interpolator="@anim/hydraulic_brake_interpolator"
+        />
+    <alpha android:toAlpha="0.5" android:fromAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/navigation_in.xml b/packages/SystemUI/res/anim/navigation_in.xml
new file mode 100644
index 0000000..630fd72
--- /dev/null
+++ b/packages/SystemUI/res/anim/navigation_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/navigation_out.xml b/packages/SystemUI/res/anim/navigation_out.xml
new file mode 100644
index 0000000..4717e47
--- /dev/null
+++ b/packages/SystemUI/res/anim/navigation_out.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <alpha android:toAlpha="0.0" android:fromAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/notification_buttons_in.xml b/packages/SystemUI/res/anim/notification_buttons_in.xml
new file mode 100644
index 0000000..630fd72
--- /dev/null
+++ b/packages/SystemUI/res/anim/notification_buttons_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/notification_buttons_out.xml b/packages/SystemUI/res/anim/notification_buttons_out.xml
new file mode 100644
index 0000000..4717e47
--- /dev/null
+++ b/packages/SystemUI/res/anim/notification_buttons_out.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <alpha android:toAlpha="0.0" android:fromAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/notification_dnd_off.xml b/packages/SystemUI/res/anim/notification_dnd_off.xml
new file mode 100644
index 0000000..4e88855
--- /dev/null
+++ b/packages/SystemUI/res/anim/notification_dnd_off.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <translate android:fromXDelta="100%p" android:toXDelta="0"
+        android:duration="@android:integer/config_longAnimTime" 
+        android:interpolator="@anim/hydraulic_brake_interpolator"
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/notification_dnd_on.xml b/packages/SystemUI/res/anim/notification_dnd_on.xml
new file mode 100644
index 0000000..309943b
--- /dev/null
+++ b/packages/SystemUI/res/anim/notification_dnd_on.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <translate android:toXDelta="100%p" android:fromXDelta="0"
+        android:duration="@android:integer/config_longAnimTime" 
+        android:interpolator="@anim/hydraulic_brake_interpolator"
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/notification_icons_in.xml b/packages/SystemUI/res/anim/notification_icons_in.xml
new file mode 100644
index 0000000..630fd72
--- /dev/null
+++ b/packages/SystemUI/res/anim/notification_icons_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/notification_icons_out.xml b/packages/SystemUI/res/anim/notification_icons_out.xml
new file mode 100644
index 0000000..4717e47
--- /dev/null
+++ b/packages/SystemUI/res/anim/notification_icons_out.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <alpha android:toAlpha="0.0" android:fromAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/recent_app_enter.xml b/packages/SystemUI/res/anim/recent_app_enter.xml
new file mode 100644
index 0000000..4947eee
--- /dev/null
+++ b/packages/SystemUI/res/anim/recent_app_enter.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- Special window zoom animation: this is the element that enters the screen,
+     it starts at 200% and scales down.  Goes with zoom_exit.xml. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@android:anim/decelerate_interpolator">
+    <scale android:fromXScale="0.25" android:toXScale="1.0"
+           android:fromYScale="0.25" android:toYScale="1.0"
+           android:pivotX="0%p" android:pivotY="0%p"
+           android:duration="500" />
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+            android:duration="500"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recent_app_leave.xml b/packages/SystemUI/res/anim/recent_app_leave.xml
new file mode 100644
index 0000000..3d83988
--- /dev/null
+++ b/packages/SystemUI/res/anim/recent_app_leave.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- Special window zoom animation: this is the element that enters the screen,
+     it starts at 200% and scales down.  Goes with zoom_exit.xml. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@android:anim/decelerate_interpolator">
+    <scale android:fromXScale="1.0" android:toXScale="0.25"
+           android:fromYScale="1.0" android:toYScale="0.25"
+           android:pivotX="0%p" android:pivotY="0%p"
+           android:duration="500" />
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+            android:duration="500"/>
+</set>
diff --git a/packages/SystemUI/res/anim/status_bar_in.xml b/packages/SystemUI/res/anim/status_bar_in.xml
new file mode 100644
index 0000000..79fe5f1
--- /dev/null
+++ b/packages/SystemUI/res/anim/status_bar_in.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <translate android:fromYDelta="100%p" android:toYDelta="0"
+        android:duration="@android:integer/config_longAnimTime" 
+        android:interpolator="@anim/hydraulic_brake_interpolator"
+        />
+    <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/status_bar_out.xml b/packages/SystemUI/res/anim/status_bar_out.xml
new file mode 100644
index 0000000..80863cf
--- /dev/null
+++ b/packages/SystemUI/res/anim/status_bar_out.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <translate android:toYDelta="100%p" android:fromYDelta="0"
+        android:duration="@android:integer/config_longAnimTime" 
+        android:interpolator="@anim/hydraulic_brake_interpolator"
+        />
+    <alpha android:toAlpha="0.5" android:fromAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/system_in.xml b/packages/SystemUI/res/anim/system_in.xml
new file mode 100644
index 0000000..630fd72
--- /dev/null
+++ b/packages/SystemUI/res/anim/system_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/anim/system_out.xml b/packages/SystemUI/res/anim/system_out.xml
new file mode 100644
index 0000000..4717e47
--- /dev/null
+++ b/packages/SystemUI/res/anim/system_out.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    >
+    <alpha android:toAlpha="0.0" android:fromAlpha="1.0"
+        android:duration="@android:integer/config_longAnimTime" 
+        />
+</set>
diff --git a/packages/SystemUI/res/drawable-hdpi/alert_bar_background_normal.9.png b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_normal.9.png
new file mode 100644
index 0000000..bc127bd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/alert_bar_background_pressed.9.png b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_pressed.9.png
new file mode 100644
index 0000000..59af804
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/alert_bar_background_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_0.png b/packages/SystemUI/res/drawable-hdpi/battery_0.png
new file mode 100644
index 0000000..f4103a8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/battery_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_100.png b/packages/SystemUI/res/drawable-hdpi/battery_100.png
new file mode 100644
index 0000000..061cbe5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/battery_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_20.png b/packages/SystemUI/res/drawable-hdpi/battery_20.png
new file mode 100644
index 0000000..0064027
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/battery_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_40.png b/packages/SystemUI/res/drawable-hdpi/battery_40.png
new file mode 100644
index 0000000..10de0e7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/battery_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_60.png b/packages/SystemUI/res/drawable-hdpi/battery_60.png
new file mode 100644
index 0000000..aa2b8ef
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/battery_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_80.png b/packages/SystemUI/res/drawable-hdpi/battery_80.png
new file mode 100644
index 0000000..fe231f0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/battery_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png
index 2c7a0d4..e74c22f 100644
--- a/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png
+++ b/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/button_frame_default.9.png b/packages/SystemUI/res/drawable-hdpi/button_frame_default.9.png
new file mode 100644
index 0000000..d809b84
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/button_frame_default.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/button_frame_pressed.9.png b/packages/SystemUI/res/drawable-hdpi/button_frame_pressed.9.png
new file mode 100644
index 0000000..5cc007c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/button_frame_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png b/packages/SystemUI/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png
deleted file mode 100644
index d21aad2..0000000
--- a/packages/SystemUI/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/divider_horizontal_light_opaque.9.png b/packages/SystemUI/res/drawable-hdpi/divider_horizontal_light_opaque.9.png
deleted file mode 100644
index f70f079..0000000
--- a/packages/SystemUI/res/drawable-hdpi/divider_horizontal_light_opaque.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/dots_empty.png b/packages/SystemUI/res/drawable-hdpi/dots_empty.png
new file mode 100644
index 0000000..31e7654
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/dots_empty.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/dots_full.png b/packages/SystemUI/res/drawable-hdpi/dots_full.png
new file mode 100644
index 0000000..8c5c604
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/dots_full.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_airplane_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_airplane_off.png
new file mode 100644
index 0000000..c7578f1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_airplane_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_airplane_on.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_airplane_on.png
new file mode 100644
index 0000000..b76c366
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_airplane_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..7f96b03
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_battery_mini.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_battery_mini.png
new file mode 100644
index 0000000..c541c7c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_battery_mini.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_battery_on.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_battery_on.png
new file mode 100644
index 0000000..1e55c88
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_battery_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_bluetooth_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_bluetooth_off.png
new file mode 100644
index 0000000..8045e96
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_bluetooth_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_bluetooth_on.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_bluetooth_on.png
new file mode 100644
index 0000000..858a191
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_bluetooth_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness.png
new file mode 100644
index 0000000..6e8995e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness_auto.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness_auto.png
new file mode 100644
index 0000000..35d9425e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness_auto.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness_low.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness_low.png
new file mode 100644
index 0000000..808af5c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_brightness_low.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_close.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_close.png
new file mode 100644
index 0000000..0ae98d6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_close.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_default_bg.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_default_bg.png
new file mode 100644
index 0000000..4272f32
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_default_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_gps_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_gps_off.png
new file mode 100644
index 0000000..8a4932b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_gps_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_gps_on.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_gps_on.png
new file mode 100644
index 0000000..029ea22
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_gps_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
new file mode 100644
index 0000000..c43a019
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_default.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_default.png
new file mode 100644
index 0000000..499244b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_lightsout.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_lightsout.png
new file mode 100644
index 0000000..97897b9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_lightsout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png
new file mode 100644
index 0000000..7af5454
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_avail.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_avail.png
new file mode 100644
index 0000000..1a2af70
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_avail.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_avail_open.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_avail_open.png
new file mode 100644
index 0000000..cdeadb8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_avail_open.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_dnd.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_dnd.png
new file mode 100644
index 0000000..2bccff0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_dnd.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_none.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_none.png
new file mode 100644
index 0000000..5417216
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_none.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_none_open.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_none_open.png
new file mode 100644
index 0000000..544e6ba
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_noti_none_open.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_open.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_open.png
new file mode 100644
index 0000000..5813a34
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_open.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_pocket.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_pocket.png
new file mode 100644
index 0000000..12b6cea
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_pocket.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_pocket_drag.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_pocket_drag.png
new file mode 100644
index 0000000..28590bc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_pocket_drag.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_pocket_hidden.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_pocket_hidden.png
new file mode 100644
index 0000000..c66ad77
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_pocket_hidden.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_pocket_holding.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_pocket_holding.png
new file mode 100644
index 0000000..6416969
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_pocket_holding.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_press_bg.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_press_bg.png
new file mode 100644
index 0000000..513f2a2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_press_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..2d80bb9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_off.png
new file mode 100644
index 0000000..cf5174a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_on.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_on.png
new file mode 100644
index 0000000..9acd6f4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_rotate_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_sound_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_sound_off.png
new file mode 100644
index 0000000..efdf566
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_sound_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_sound_on.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_sound_on.png
new file mode 100644
index 0000000..a1d5a2d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_sound_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_mini.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_mini.png
new file mode 100644
index 0000000..2475eb1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_mini.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_off.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_off.png
new file mode 100644
index 0000000..fd747a1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_on.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_on.png
new file mode 100644
index 0000000..7c889356
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_wifi_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_0.png b/packages/SystemUI/res/drawable-hdpi/signal_0.png
new file mode 100644
index 0000000..00e36c4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_100.png b/packages/SystemUI/res/drawable-hdpi/signal_100.png
new file mode 100644
index 0000000..96e52ff
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/signal_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_20.png b/packages/SystemUI/res/drawable-hdpi/signal_20.png
new file mode 100644
index 0000000..c0f652a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/signal_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_40.png b/packages/SystemUI/res/drawable-hdpi/signal_40.png
new file mode 100644
index 0000000..995dd8e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/signal_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_60.png b/packages/SystemUI/res/drawable-hdpi/signal_60.png
new file mode 100644
index 0000000..51e31ba
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/signal_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/signal_80.png b/packages/SystemUI/res/drawable-hdpi/signal_80.png
new file mode 100644
index 0000000..afa656e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/signal_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_notify_alarm.png b/packages/SystemUI/res/drawable-hdpi/stat_notify_alarm.png
index 2d3eb30..89daee1 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_notify_alarm.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_notify_alarm.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png
index e8fbc9e..96dc085 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
index 18c77df..1e4bbf5 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_1x.png
index 818e292..bdc8c27 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g.png
index 95866b1..d0d1345 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_e.png
index 016b30b..4211b8c 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_g.png
index ec672eb..d5ece7a 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_h.png
index 27bab73..6687b40 100755
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_connected_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_1x.png
old mode 100644
new mode 100755
index 0e6849b..ba24082
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_3g.png
old mode 100644
new mode 100755
index a86a324..5af2b05
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_e.png
old mode 100644
new mode 100755
index 24902d6..9909b09
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_g.png
old mode 100644
new mode 100755
index 4160a95..0e02b8d
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_h.png
old mode 100644
new mode 100755
index d459ee3..f84ad32
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_connected_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_1x.png
old mode 100644
new mode 100755
index 7b64751..d80a8ce
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_3g.png
old mode 100644
new mode 100755
index d82930c..31c976a
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_e.png
index 8096846..ae90cc8 100755
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_g.png
old mode 100644
new mode 100755
index e94e146..a487f29
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_h.png
old mode 100644
new mode 100755
index 8805a40..816085b
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_1x.png
old mode 100644
new mode 100755
index 07545d5..0132019
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_3g.png
old mode 100644
new mode 100755
index 7629cef..3903545
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_e.png
old mode 100644
new mode 100755
index 28578c6..ed099ff
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_g.png
old mode 100644
new mode 100755
index d9f7a30..c930e4c
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_h.png
old mode 100644
new mode 100755
index 505ccd4..407a06c
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_inandout_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_1x.png
old mode 100644
new mode 100755
index 783c2b6..6141f72
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_3g.png
old mode 100644
new mode 100755
index 001eaea..d44a4cf
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_e.png
old mode 100644
new mode 100755
index cbae7dc..54ebd9b
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_g.png
old mode 100644
new mode 100755
index 11f3a5c..2fe0bbf
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_h.png
old mode 100644
new mode 100755
index eb42294..e58e019
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_out_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_1x.png
index 66fb60e..00a29a4 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g.png
index 07ea499..11ee0f2 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e.png
index e39767a..fc135fc 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_g.png
index 47c1fca..3d33a62 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_h.png
index ac80dce..f36e1eb 100755
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_in_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_1x.png
index f88091b..ef5dbf4 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g.png
index 95bb3cd..dba9675 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_e.png
index 0ef4701..2e5d82e 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_g.png
index 31b926b..0985a09 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_h.png
index ed02984..7a32c43 100755
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_inandout_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_1x.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_1x.png
index 0ee5b08..b622556 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_1x.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g.png
index cac7802..04ec59e 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_e.png
index df6e195..a47b982 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_g.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_g.png
index 4a2f867..9d90e71 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_g.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_h.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_h.png
index 2b4628f..920f290 100755
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_h.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_out_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_gps_acquiring.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_gps_acquiring.png
index 150c9fc..9003d67 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_gps_acquiring.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_gps_acquiring.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0.png
index 1ef75d3..95ba181 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0_fully.png
index 1ef75d3..95ba181 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_0_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1.png
index a7dc07e..adf668d 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1_fully.png
index f1f4c4e..adf668d 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2.png
index 918a476..7bf6b51 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2_fully.png
index 6e34e66..7bf6b51 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_2_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3.png
index 5b2acc6..78738ac 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3_fully.png
index 3664ab4..78738ac 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_3_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4.png
index f1ff548..ac88143 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4_fully.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4_fully.png
index b4fa481..ac88143 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4_fully.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_r_signal_4_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_ringer_silent.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_ringer_silent.png
index 5a741bb..bdd37e1 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_ringer_silent.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_ringer_silent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_ringer_vibrate.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_ringer_vibrate.png
index 7ff375a..21c1c08 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_ringer_vibrate.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_ringer_vibrate.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png
index 01c7e2a..7a419f1 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_flightmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_null.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_null.png
index 03d2147..1adc05a 100644
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_null.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_back_default.png b/packages/SystemUI/res/drawable-hdpi/status_bar_back_default.png
new file mode 100644
index 0000000..a9f9540
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_back_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_back_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_back_pressed.png
new file mode 100644
index 0000000..b8aa190
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_back_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_close_on.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_close_on.9.png
deleted file mode 100644
index 1d0192c..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_close_on.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.png b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.png
new file mode 100644
index 0000000..92fc7f8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.png
new file mode 100644
index 0000000..77f09ac
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_home_default.png b/packages/SystemUI/res/drawable-hdpi/status_bar_home_default.png
new file mode 100644
index 0000000..cb951dd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_home_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_home_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_home_pressed.png
new file mode 100644
index 0000000..a835ad5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_home_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_icon_tray.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_icon_tray.9.png
new file mode 100644
index 0000000..e1f041c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_icon_tray.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_item_app_background_normal.9.png b/packages/SystemUI/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
new file mode 100644
index 0000000..4fbfa4f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_menu_default.png b/packages/SystemUI/res/drawable-hdpi/status_bar_menu_default.png
new file mode 100644
index 0000000..14779cf
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_menu_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_menu_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_menu_pressed.png
new file mode 100644
index 0000000..6c3d4c9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_menu_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_recent_default.png b/packages/SystemUI/res/drawable-hdpi/status_bar_recent_default.png
new file mode 100644
index 0000000..dc4cb51
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_recent_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_recent_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_recent_pressed.png
new file mode 100644
index 0000000..dc4cb51
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_recent_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_veto_normal.png b/packages/SystemUI/res/drawable-hdpi/status_bar_veto_normal.png
new file mode 100644
index 0000000..df532b8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_veto_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_veto_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_veto_pressed.png
new file mode 100644
index 0000000..118c01b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_veto_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/statusbar_background.9.png b/packages/SystemUI/res/drawable-hdpi/statusbar_background.9.png
deleted file mode 100644
index a933833..0000000
--- a/packages/SystemUI/res/drawable-hdpi/statusbar_background.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_0.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_0.png
new file mode 100644
index 0000000..35b765f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_10.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_10.png
new file mode 100644
index 0000000..39eeb06
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_10.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_100.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_100.png
new file mode 100644
index 0000000..b5787aa
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_20.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_20.png
new file mode 100644
index 0000000..3fc2852
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_30.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_30.png
new file mode 100644
index 0000000..6ba6da3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_30.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_40.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_40.png
new file mode 100644
index 0000000..f5ac131
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_50.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_50.png
new file mode 100644
index 0000000..fffb701
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_60.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_60.png
new file mode 100644
index 0000000..7b4fa44
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_70.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_70.png
new file mode 100644
index 0000000..3c9eeb1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_70.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_80.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_80.png
new file mode 100644
index 0000000..3945188
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_battery_90.png b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_90.png
new file mode 100644
index 0000000..8a9241c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_battery_90.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_batterymini_100.png b/packages/SystemUI/res/drawable-hdpi/sysbar_batterymini_100.png
new file mode 100644
index 0000000..67591fe
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_batterymini_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_batterymini_red.png b/packages/SystemUI/res/drawable-hdpi/sysbar_batterymini_red.png
new file mode 100644
index 0000000..697044b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_batterymini_red.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_hidenotification_handle.png b/packages/SystemUI/res/drawable-hdpi/sysbar_hidenotification_handle.png
new file mode 100644
index 0000000..64945d4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_hidenotification_handle.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_minimeter_bg.png b/packages/SystemUI/res/drawable-hdpi/sysbar_minimeter_bg.png
new file mode 100644
index 0000000..b59bf1d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_minimeter_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_panel_bg.9.png b/packages/SystemUI/res/drawable-hdpi/sysbar_panel_bg.9.png
new file mode 100644
index 0000000..2d5ccfa
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_panel_bg.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_panel_recents_bg.9.png b/packages/SystemUI/res/drawable-hdpi/sysbar_panel_recents_bg.9.png
new file mode 100644
index 0000000..cd8120b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_panel_recents_bg.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_0.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_0.png
new file mode 100644
index 0000000..8811252
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_10.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_10.png
new file mode 100644
index 0000000..9c6d641
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_10.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_100.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_100.png
new file mode 100644
index 0000000..58f0129
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_20.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_20.png
new file mode 100644
index 0000000..440bd50
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_30.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_30.png
new file mode 100644
index 0000000..cc17a9f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_30.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_40.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_40.png
new file mode 100644
index 0000000..87537f7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_50.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_50.png
new file mode 100644
index 0000000..2aa5967
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_60.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_60.png
new file mode 100644
index 0000000..bedb646
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_70.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_70.png
new file mode 100644
index 0000000..9b4a966
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_70.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_80.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_80.png
new file mode 100644
index 0000000..73ed185
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signal_90.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_90.png
new file mode 100644
index 0000000..8e0c706
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_signal_90.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_signalmini_100.png b/packages/SystemUI/res/drawable-hdpi/sysbar_signalmini_100.png
new file mode 100644
index 0000000..8bec533
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_signalmini_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_toggle_bg_off.9.png b/packages/SystemUI/res/drawable-hdpi/sysbar_toggle_bg_off.9.png
new file mode 100644
index 0000000..9dc0390
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_toggle_bg_off.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/sysbar_toggle_bg_on.9.png b/packages/SystemUI/res/drawable-hdpi/sysbar_toggle_bg_on.9.png
new file mode 100644
index 0000000..08fe94f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/sysbar_toggle_bg_on.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.png
new file mode 100644
index 0000000..e375ee9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.png
new file mode 100644
index 0000000..f62502b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.png
new file mode 100644
index 0000000..e887fb8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.png b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.png
new file mode 100644
index 0000000..58159d5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.png
new file mode 100644
index 0000000..6e857b5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_rotate_off.png b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_rotate_off.png
new file mode 100644
index 0000000..66f299c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-hdpi/ic_sysbar_rotate_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-land-mdpi/ic_sysbar_rotate_off.png b/packages/SystemUI/res/drawable-land-mdpi/ic_sysbar_rotate_off.png
new file mode 100644
index 0000000..adaadf7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-land-mdpi/ic_sysbar_rotate_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/alert_bar_background_normal.9.png b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_normal.9.png
new file mode 100644
index 0000000..258de13
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/alert_bar_background_pressed.9.png b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_pressed.9.png
new file mode 100644
index 0000000..258de13
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/alert_bar_background_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_0.png b/packages/SystemUI/res/drawable-mdpi/battery_0.png
new file mode 100644
index 0000000..00fb261
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_100.png b/packages/SystemUI/res/drawable-mdpi/battery_100.png
new file mode 100644
index 0000000..600da18
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_20.png b/packages/SystemUI/res/drawable-mdpi/battery_20.png
new file mode 100644
index 0000000..5545cdb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_40.png b/packages/SystemUI/res/drawable-mdpi/battery_40.png
new file mode 100644
index 0000000..f57ce03
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_60.png b/packages/SystemUI/res/drawable-mdpi/battery_60.png
new file mode 100644
index 0000000..c8affe2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_80.png b/packages/SystemUI/res/drawable-mdpi/battery_80.png
new file mode 100644
index 0000000..d877fe1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/battery_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
index 7417afc..e74c22f 100644
--- a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
+++ b/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/button_frame_default.9.png b/packages/SystemUI/res/drawable-mdpi/button_frame_default.9.png
new file mode 100644
index 0000000..7ab1f26
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/button_frame_default.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/button_frame_pressed.9.png b/packages/SystemUI/res/drawable-mdpi/button_frame_pressed.9.png
new file mode 100644
index 0000000..08f7a4d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/button_frame_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png b/packages/SystemUI/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png
deleted file mode 100644
index 9444f0d..0000000
--- a/packages/SystemUI/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/divider_horizontal_light_opaque.9.png b/packages/SystemUI/res/drawable-mdpi/divider_horizontal_light_opaque.9.png
deleted file mode 100644
index f70f079..0000000
--- a/packages/SystemUI/res/drawable-mdpi/divider_horizontal_light_opaque.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/dots_empty.png b/packages/SystemUI/res/drawable-mdpi/dots_empty.png
new file mode 100644
index 0000000..22ada41
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/dots_empty.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/dots_full.png b/packages/SystemUI/res/drawable-mdpi/dots_full.png
new file mode 100644
index 0000000..2a346d6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/dots_full.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notification_overlay.9.png b/packages/SystemUI/res/drawable-mdpi/ic_notification_overlay.9.png
index 8ed5e5c..1a3063c 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_notification_overlay.9.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_notification_overlay.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_off.png
new file mode 100644
index 0000000..d897ba61
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_on.png
new file mode 100644
index 0000000..0296b5b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_airplane_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..6baf5ea
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_mini.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_mini.png
new file mode 100644
index 0000000..9ababb7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_mini.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_on.png
new file mode 100644
index 0000000..668b472
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_battery_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_off.png
new file mode 100644
index 0000000..e463ba4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_on.png
new file mode 100644
index 0000000..1239d50
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_bluetooth_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness.png
new file mode 100644
index 0000000..97fa5fc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_auto.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_auto.png
new file mode 100644
index 0000000..37a1533
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_auto.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_low.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_low.png
new file mode 100644
index 0000000..8a55e3a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_brightness_low.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_close.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_close.png
new file mode 100644
index 0000000..53abcbc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_close.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_default_bg.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_default_bg.png
new file mode 100644
index 0000000..3e82d4e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_default_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_off.png
new file mode 100644
index 0000000..dc2ed34
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_on.png
new file mode 100644
index 0000000..1e39fdc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_gps_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
new file mode 100644
index 0000000..ed813dc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png
new file mode 100644
index 0000000..bf33c946
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lightsout.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lightsout.png
new file mode 100644
index 0000000..8a07acc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_lightsout.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
new file mode 100644
index 0000000..d7775f2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_avail.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_avail.png
new file mode 100644
index 0000000..9123fef
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_avail.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_avail_open.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_avail_open.png
new file mode 100644
index 0000000..8e56f2a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_avail_open.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_dnd.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_dnd.png
new file mode 100644
index 0000000..80cf99c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_dnd.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_none.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_none.png
new file mode 100644
index 0000000..e0d018b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_none.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_none_open.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_none_open.png
new file mode 100644
index 0000000..5db8c9c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_noti_none_open.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_open.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_open.png
new file mode 100644
index 0000000..b7d624e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_open.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket.png
new file mode 100644
index 0000000..90dffa9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_drag.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_drag.png
new file mode 100644
index 0000000..dcce495
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_drag.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_hidden.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_hidden.png
new file mode 100644
index 0000000..93b0d23
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_hidden.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_holding.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_holding.png
new file mode 100644
index 0000000..9eb9dc6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_pocket_holding.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_press_bg.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_press_bg.png
new file mode 100644
index 0000000..0958393
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_press_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..178af73
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off.png
new file mode 100644
index 0000000..9e6793a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_on.png
new file mode 100644
index 0000000..4517d1b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_rotate_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_off.png
new file mode 100644
index 0000000..bd11e86
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_on.png
new file mode 100644
index 0000000..1b3ba2f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_sound_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_mini.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_mini.png
new file mode 100644
index 0000000..ffbd2d3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_mini.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_off.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_off.png
new file mode 100644
index 0000000..87acc14
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_on.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_on.png
new file mode 100644
index 0000000..bbb1c74
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_wifi_on.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_0.png b/packages/SystemUI/res/drawable-mdpi/signal_0.png
new file mode 100644
index 0000000..00fb261
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_100.png b/packages/SystemUI/res/drawable-mdpi/signal_100.png
new file mode 100644
index 0000000..59df547
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_20.png b/packages/SystemUI/res/drawable-mdpi/signal_20.png
new file mode 100644
index 0000000..3bce724
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_40.png b/packages/SystemUI/res/drawable-mdpi/signal_40.png
new file mode 100644
index 0000000..570069c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_60.png b/packages/SystemUI/res/drawable-mdpi/signal_60.png
new file mode 100644
index 0000000..0cfc7a6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/signal_80.png b/packages/SystemUI/res/drawable-mdpi/signal_80.png
new file mode 100644
index 0000000..66201d0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/signal_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_notify_alarm.png b/packages/SystemUI/res/drawable-mdpi/stat_notify_alarm.png
index bd04206..1b01b85 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_notify_alarm.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_notify_alarm.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_notify_more.png b/packages/SystemUI/res/drawable-mdpi/stat_notify_more.png
index 63e3985..e129ba9 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_notify_more.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_notify_more.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth.png
index 640d612..3ddf728 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png
old mode 100644
new mode 100755
index a9a5385..f09b83b
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_1x.png
index 78ece9e..f8b6f11 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_3g.png
index 31fc1b0..3198abc 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_e.png
index 19adb4b..16e3b0b 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_g.png
index fd419ea..91db8e8 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_h.png
index 94e77ae..44ab1ec 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_connected_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_1x.png
index 91328c0..c9d6719 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_3g.png
index 2fee6924..e0a39cf 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_e.png
index d0968aa..93b8f94 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_g.png
index 991228b..5561011 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_h.png
index ae03e38..697b908 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_connected_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_1x.png
index 97b011e..4cced10 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_3g.png
index a826866..6f2f058 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_e.png
index f6a6891..b523d64 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_g.png
index 19b9816..c9bbf6c 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_h.png
index f8c0961..6bc637a 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_in_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_1x.png
index 22deb70..6e47d43 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_3g.png
index c7c1b49..a20e5bf 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_e.png
index d9a0702..8f135a5 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_g.png
index 6beed8a..cecf2c6 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_h.png
index e4179c1..3f86741 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_inandout_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_1x.png
index 4b2f86d..4740b9f 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_3g.png
index 6779604..5d0ac29 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_e.png
index 1309a97..768eac6 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_g.png
index 2fc1e8e..49d6279 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_h.png
index 0eef2c1..4d7df96 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_fully_out_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_1x.png
index f8904e2..3045a57 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_3g.png
index 3ef306e..661eccc 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_e.png
index 2ff6d90..401cb55 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_g.png
index 8ff49b0..cc4e26f 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_h.png
index f416203..a000da2 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_in_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_1x.png
index 24b7daa..32c547e 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_3g.png
index 5ea9142..d769174 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_e.png
index 5bf6aca..c895aeb 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_g.png
index 002bf46..c201350 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_h.png
index 924b84f..df2aa30 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_inandout_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_1x.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_1x.png
index bd0d1ca..6b1a914 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_1x.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_1x.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_3g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_3g.png
index f583eec..5edc35a 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_3g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_3g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_e.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_e.png
index 66940eaf..c94c001 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_e.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_e.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_g.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_g.png
index 0381f52..16c3d6b 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_g.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_g.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_h.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_h.png
index 0b84fe8..7daf6af 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_h.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_out_h.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_gps_acquiring.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_gps_acquiring.png
index e5e98f9..31bc94e 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_gps_acquiring.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_gps_acquiring.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0.png
index 7f6fa9c..8f5b3ef 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0_fully.png
index 7f6fa9c..8f5b3ef 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_0_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1.png
index ca20839..960c0e2 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1_fully.png
index 1ad5d8b..960c0e2 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2.png
index 6eba560..a115a23 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2_fully.png
index f2358dd..a115a23 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_2_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3.png
index 1ebe23e..0ef34df 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3_fully.png
index 468ad7d..0ef34df 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_3_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4.png
index d9f2a0d..648599b 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4_fully.png
index 290822a..648599b 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_r_signal_4_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_ringer_silent.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_ringer_silent.png
index e0a3141..d62f32d 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_ringer_silent.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_ringer_silent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_ringer_vibrate.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_ringer_vibrate.png
index 116b8e2..665ca38 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_ringer_vibrate.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_ringer_vibrate.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
index 168cbf8..e81cd7c 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.png
index 168cbf8..e81cd7c 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_0_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
index 8ce41ab..c56ae8a 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png
index 3736d58..bf720cbb 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
index a6fb566..38e3b24 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png
index 3469149..3559497 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_2_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
index fe0e93f..6f98e72 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png
index 36cd50c..8b932a6 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_3_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
index 4ca603f..759631c 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png
index 993216a..45c4509 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_4_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
index 4f0d1f1..da5b02c 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_flightmode.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png
index 5d9957a..cf13c79 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_signal_null.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png
index c8b82b5..2989939 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png
index d049baa..6f40cb4 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png
index 1b3b164..60e3794a 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_1_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png
index 28b7af6..ab1b0c67 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png
index b0a6bf9..eb25919 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_2_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png
index e39061c..b574115 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png
index d63f1ed..14cd8fa 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_3_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png
index cfd01ab..3b81ceb 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png
index 19165ab..ef72980 100644
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_back_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_back_default.png
new file mode 100644
index 0000000..f99a66d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_back_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png
new file mode 100644
index 0000000..94a0649
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_back_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_close_on.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_close_on.9.png
deleted file mode 100644
index 833ec10..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_close_on.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
new file mode 100644
index 0000000..ce64926
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
new file mode 100644
index 0000000..c63c426
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_home_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_home_default.png
new file mode 100644
index 0000000..7e8ade5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_home_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png
new file mode 100644
index 0000000..7e8ade5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_home_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_icon_tray.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_icon_tray.9.png
new file mode 100644
index 0000000..502acce
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_icon_tray.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_item_app_background_normal.9.png b/packages/SystemUI/res/drawable-mdpi/status_bar_item_app_background_normal.9.png
new file mode 100644
index 0000000..c079615
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_item_app_background_normal.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_menu_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_menu_default.png
new file mode 100644
index 0000000..bf3a755
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_menu_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_menu_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_menu_pressed.png
new file mode 100644
index 0000000..15e21d73
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_menu_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_recent_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_recent_default.png
new file mode 100644
index 0000000..7de67b0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_recent_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png
new file mode 100644
index 0000000..7de67b0
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_recent_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_veto_normal.png b/packages/SystemUI/res/drawable-mdpi/status_bar_veto_normal.png
new file mode 100644
index 0000000..3b7c9c7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_veto_normal.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_veto_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_veto_pressed.png
new file mode 100644
index 0000000..3b7c9c7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_veto_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/statusbar_background.9.png b/packages/SystemUI/res/drawable-mdpi/statusbar_background.9.png
deleted file mode 100644
index 6c588f7..0000000
--- a/packages/SystemUI/res/drawable-mdpi/statusbar_background.9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_0.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_0.png
new file mode 100644
index 0000000..ff75a51
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_10.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_10.png
new file mode 100644
index 0000000..66ab4c6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_10.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_100.png
new file mode 100644
index 0000000..3b50500
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_20.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_20.png
new file mode 100644
index 0000000..9119065
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_30.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_30.png
new file mode 100644
index 0000000..296f19f26b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_30.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_40.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_40.png
new file mode 100644
index 0000000..9daab23
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_50.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_50.png
new file mode 100644
index 0000000..62d24c4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_60.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_60.png
new file mode 100644
index 0000000..eea927a1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_70.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_70.png
new file mode 100644
index 0000000..6816088
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_70.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_80.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_80.png
new file mode 100644
index 0000000..b7dd9bb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_battery_90.png b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_90.png
new file mode 100644
index 0000000..6e36f53
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_battery_90.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_100.png
new file mode 100644
index 0000000..8eb0f29
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_red.png b/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_red.png
new file mode 100644
index 0000000..adcc6b9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_batterymini_red.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_hidenotification_handle.png b/packages/SystemUI/res/drawable-mdpi/sysbar_hidenotification_handle.png
new file mode 100644
index 0000000..e43edd7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_hidenotification_handle.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_minimeter_bg.png b/packages/SystemUI/res/drawable-mdpi/sysbar_minimeter_bg.png
new file mode 100644
index 0000000..0d265fc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_minimeter_bg.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_panel_bg.9.png b/packages/SystemUI/res/drawable-mdpi/sysbar_panel_bg.9.png
new file mode 100644
index 0000000..77e034b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_panel_bg.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_panel_recents_bg.9.png b/packages/SystemUI/res/drawable-mdpi/sysbar_panel_recents_bg.9.png
new file mode 100644
index 0000000..85726d2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_panel_recents_bg.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_0.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_0.png
new file mode 100644
index 0000000..7469372
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_10.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_10.png
new file mode 100644
index 0000000..6625d9a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_10.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_100.png
new file mode 100644
index 0000000..b2e763b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_20.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_20.png
new file mode 100644
index 0000000..fb66362
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_20.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_30.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_30.png
new file mode 100644
index 0000000..a87d94e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_30.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_40.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_40.png
new file mode 100644
index 0000000..8e229d5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_40.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_50.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_50.png
new file mode 100644
index 0000000..fe989d4
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_50.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_60.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_60.png
new file mode 100644
index 0000000..aac57dc
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_60.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_70.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_70.png
new file mode 100644
index 0000000..2281968
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_70.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_80.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_80.png
new file mode 100644
index 0000000..7177ae1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_80.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signal_90.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_90.png
new file mode 100644
index 0000000..7f60480
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signal_90.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_signalmini_100.png b/packages/SystemUI/res/drawable-mdpi/sysbar_signalmini_100.png
new file mode 100644
index 0000000..a5eaa63
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_signalmini_100.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_off.9.png b/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_off.9.png
new file mode 100644
index 0000000..94849d8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_off.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_on.9.png b/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_on.9.png
new file mode 100644
index 0000000..f11058c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/sysbar_toggle_bg_on.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.png
new file mode 100644
index 0000000..eb87532
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.png
new file mode 100644
index 0000000..3bfc83e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.png
new file mode 100644
index 0000000..0d8479c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.png b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.png
new file mode 100644
index 0000000..8f1d26c
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.png
new file mode 100644
index 0000000..22636d6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-port-hdpi/ic_sysbar_rotate_off.png b/packages/SystemUI/res/drawable-port-hdpi/ic_sysbar_rotate_off.png
new file mode 100644
index 0000000..fcdda31
--- /dev/null
+++ b/packages/SystemUI/res/drawable-port-hdpi/ic_sysbar_rotate_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-port-mdpi/ic_sysbar_rotate_off.png b/packages/SystemUI/res/drawable-port-mdpi/ic_sysbar_rotate_off.png
new file mode 100644
index 0000000..fdc0ac7
--- /dev/null
+++ b/packages/SystemUI/res/drawable-port-mdpi/ic_sysbar_rotate_off.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/alert_bar_background.xml b/packages/SystemUI/res/drawable/alert_bar_background.xml
new file mode 100644
index 0000000..24b6aa3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/alert_bar_background.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" 
+        android:drawable="@drawable/alert_bar_background_pressed" />
+    <item
+         android:drawable="@drawable/alert_bar_background_normal" />
+</selector>
+
diff --git a/packages/SystemUI/res/drawable/button_frame.xml b/packages/SystemUI/res/drawable/button_frame.xml
new file mode 100644
index 0000000..5db39a5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/button_frame.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@drawable/button_frame_pressed" />
+    <item android:drawable="@drawable/button_frame_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_icon_bg.xml b/packages/SystemUI/res/drawable/ic_sysbar_icon_bg.xml
new file mode 100644
index 0000000..d8ba2a8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_sysbar_icon_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_press_bg" />
+    <item android:drawable="@drawable/ic_sysbar_default_bg" />
+</selector>
+
diff --git a/packages/SystemUI/res/drawable/recent_overlay.png b/packages/SystemUI/res/drawable/recent_overlay.png
new file mode 100644
index 0000000..4dfa3d9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recent_overlay.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/recent_rez_border.png b/packages/SystemUI/res/drawable/recent_rez_border.png
new file mode 100644
index 0000000..ad025f5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recent_rez_border.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_back.xml b/packages/SystemUI/res/drawable/status_bar_back.xml
new file mode 100644
index 0000000..92bf147
--- /dev/null
+++ b/packages/SystemUI/res/drawable/status_bar_back.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@drawable/status_bar_back_pressed" />
+    <item android:drawable="@drawable/status_bar_back_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/drawable/status_bar_expand.xml b/packages/SystemUI/res/drawable/status_bar_expand.xml
new file mode 100644
index 0000000..f966920
--- /dev/null
+++ b/packages/SystemUI/res/drawable/status_bar_expand.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@drawable/status_bar_expand_pressed" />
+    <item android:drawable="@drawable/status_bar_expand_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/drawable/status_bar_home.xml b/packages/SystemUI/res/drawable/status_bar_home.xml
new file mode 100644
index 0000000..0011711
--- /dev/null
+++ b/packages/SystemUI/res/drawable/status_bar_home.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@drawable/status_bar_home_pressed" />
+    <item android:drawable="@drawable/status_bar_home_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/drawable/status_bar_menu.xml b/packages/SystemUI/res/drawable/status_bar_menu.xml
new file mode 100644
index 0000000..aa7286e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/status_bar_menu.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@drawable/status_bar_menu_pressed" />
+    <item android:drawable="@drawable/status_bar_menu_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/drawable/status_bar_recent.xml b/packages/SystemUI/res/drawable/status_bar_recent.xml
new file mode 100755
index 0000000..d708455
--- /dev/null
+++ b/packages/SystemUI/res/drawable/status_bar_recent.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@drawable/status_bar_recent_pressed" />
+    <item android:drawable="@drawable/status_bar_recent_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/drawable/status_bar_veto.xml b/packages/SystemUI/res/drawable/status_bar_veto.xml
new file mode 100644
index 0000000..6e1b681
--- /dev/null
+++ b/packages/SystemUI/res/drawable/status_bar_veto.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@drawable/status_bar_veto_pressed" />
+    <item android:drawable="@drawable/status_bar_veto_normal" />
+</selector>
+
diff --git a/packages/SystemUI/res/drawable/sysbar_battery.xml b/packages/SystemUI/res/drawable/sysbar_battery.xml
new file mode 100644
index 0000000..9551bf0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/sysbar_battery.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** 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.
+*/
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:maxLevel="01" android:drawable="@drawable/sysbar_battery_0" />
+    <item android:maxLevel="10" android:drawable="@drawable/sysbar_battery_10" />
+    <item android:maxLevel="20" android:drawable="@drawable/sysbar_battery_20" />
+    <item android:maxLevel="30" android:drawable="@drawable/sysbar_battery_30" />
+    <item android:maxLevel="40" android:drawable="@drawable/sysbar_battery_40" />
+    <item android:maxLevel="50" android:drawable="@drawable/sysbar_battery_50" />
+    <item android:maxLevel="60" android:drawable="@drawable/sysbar_battery_60" />
+    <item android:maxLevel="70" android:drawable="@drawable/sysbar_battery_70" />
+    <item android:maxLevel="80" android:drawable="@drawable/sysbar_battery_80" />
+    <item android:maxLevel="90" android:drawable="@drawable/sysbar_battery_90" />
+    <item android:maxLevel="101" android:drawable="@drawable/sysbar_battery_100" />
+</level-list>
diff --git a/packages/SystemUI/res/drawable/sysbar_batterymini.xml b/packages/SystemUI/res/drawable/sysbar_batterymini.xml
new file mode 100644
index 0000000..c7300e6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/sysbar_batterymini.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** 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.
+*/
+-->
+
+<clip xmlns:android="http://schemas.android.com/apk/res/android"
+        android:clipOrientation="horizontal"
+        android:maxLevel="100"
+        android:gravity="left">
+    <level-list>
+        <item android:maxLevel="1500"  android:drawable="@drawable/sysbar_batterymini_red" />
+        <item android:maxLevel="10000" android:drawable="@drawable/sysbar_batterymini_100" />
+    </level-list>
+</clip>
diff --git a/packages/SystemUI/res/drawable/sysbar_signal.xml b/packages/SystemUI/res/drawable/sysbar_signal.xml
new file mode 100644
index 0000000..9561c37
--- /dev/null
+++ b/packages/SystemUI/res/drawable/sysbar_signal.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** 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.
+*/
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:maxLevel="01" android:drawable="@drawable/sysbar_signal_0" />
+    <item android:maxLevel="10" android:drawable="@drawable/sysbar_signal_10" />
+    <item android:maxLevel="20" android:drawable="@drawable/sysbar_signal_20" />
+    <item android:maxLevel="30" android:drawable="@drawable/sysbar_signal_30" />
+    <item android:maxLevel="40" android:drawable="@drawable/sysbar_signal_40" />
+    <item android:maxLevel="50" android:drawable="@drawable/sysbar_signal_50" />
+    <item android:maxLevel="60" android:drawable="@drawable/sysbar_signal_60" />
+    <item android:maxLevel="70" android:drawable="@drawable/sysbar_signal_70" />
+    <item android:maxLevel="80" android:drawable="@drawable/sysbar_signal_80" />
+    <item android:maxLevel="90" android:drawable="@drawable/sysbar_signal_90" />
+    <item android:maxLevel="101" android:drawable="@drawable/sysbar_signal_100" />
+</level-list>
diff --git a/packages/SystemUI/res/drawable/sysbar_signalmini.xml b/packages/SystemUI/res/drawable/sysbar_signalmini.xml
new file mode 100644
index 0000000..598bf10
--- /dev/null
+++ b/packages/SystemUI/res/drawable/sysbar_signalmini.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** 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.
+*/
+-->
+
+<clip xmlns:android="http://schemas.android.com/apk/res/android"
+        android:clipOrientation="horizontal"
+        android:gravity="right"
+        android:maxLevel="10000"
+        android:drawable="@drawable/sysbar_signalmini_100" />
diff --git a/packages/SystemUI/res/drawable/sysbar_wifi.xml b/packages/SystemUI/res/drawable/sysbar_wifi.xml
new file mode 100644
index 0000000..9561c37
--- /dev/null
+++ b/packages/SystemUI/res/drawable/sysbar_wifi.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** 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.
+*/
+-->
+
+<level-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:maxLevel="01" android:drawable="@drawable/sysbar_signal_0" />
+    <item android:maxLevel="10" android:drawable="@drawable/sysbar_signal_10" />
+    <item android:maxLevel="20" android:drawable="@drawable/sysbar_signal_20" />
+    <item android:maxLevel="30" android:drawable="@drawable/sysbar_signal_30" />
+    <item android:maxLevel="40" android:drawable="@drawable/sysbar_signal_40" />
+    <item android:maxLevel="50" android:drawable="@drawable/sysbar_signal_50" />
+    <item android:maxLevel="60" android:drawable="@drawable/sysbar_signal_60" />
+    <item android:maxLevel="70" android:drawable="@drawable/sysbar_signal_70" />
+    <item android:maxLevel="80" android:drawable="@drawable/sysbar_signal_80" />
+    <item android:maxLevel="90" android:drawable="@drawable/sysbar_signal_90" />
+    <item android:maxLevel="101" android:drawable="@drawable/sysbar_signal_100" />
+</level-list>
diff --git a/packages/SystemUI/res/drawable/sysbar_wifimini.xml b/packages/SystemUI/res/drawable/sysbar_wifimini.xml
new file mode 100644
index 0000000..ca6c9ed
--- /dev/null
+++ b/packages/SystemUI/res/drawable/sysbar_wifimini.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/drawable/stat_sys_battery.xml
+**
+** 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.
+*/
+-->
+
+<clip xmlns:android="http://schemas.android.com/apk/res/android"
+        android:clipOrientation="horizontal"
+        android:gravity="right"
+        android:maxLevel="100"
+        android:drawable="@drawable/sysbar_signalmini_100" />
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
new file mode 100644
index 0000000..590132f
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<!--    android:background="@drawable/status_bar_closed_default_background" -->
+<com.android.systemui.statusbar.tablet.TabletStatusBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+    android:background="@drawable/status_bar_background"
+    >
+    <RelativeLayout
+        android:id="@+id/bar_contents"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        >
+
+        <ImageView
+            android:id="@+id/notificationTrigger"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_alignParentRight="true"
+            android:layout_marginLeft="6dip"
+            android:src="@drawable/ic_sysbar_noti_none"
+            android:background="@drawable/ic_sysbar_icon_bg"
+            android:gravity="center"
+            />
+
+        <LinearLayout
+            android:id="@+id/notificationButtons"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_toLeftOf="@+id/notificationTrigger"
+            android:gravity="center_vertical"
+            android:orientation="horizontal"
+            android:visibility="gone"
+            >
+
+            <TextView android:id="@+id/clear_all_button"
+                style="?android:attr/textAppearance"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="right|center_vertical"
+                android:layout_marginTop="2dip"
+                android:layout_marginBottom="1dip"
+                android:layout_marginRight="10dip"
+                android:padding="6dip"
+                android:textSize="14sp"
+                android:text="@string/status_bar_clear_all_button"
+                />
+
+            <TextView android:id="@+id/do_not_disturb"
+                style="?android:attr/textAppearance"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="right|center_vertical"
+                android:layout_marginTop="2dip"
+                android:layout_marginBottom="1dip"
+                android:layout_marginRight="10dip"
+                android:padding="6dip"
+                android:textSize="14sp"
+                android:text="@string/status_bar_do_not_disturb_button"
+                />
+
+        </LinearLayout>
+
+        <com.android.systemui.statusbar.tablet.NotificationIconArea
+            android:id="@+id/notificationIcons"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_toLeftOf="@+id/notificationTrigger"
+            android:gravity="center_vertical"
+            android:orientation="horizontal"
+            >
+            <com.android.systemui.statusbar.tablet.InputMethodButton
+                android:id="@+id/imeButton"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_marginLeft="8dip"
+                android:src="@drawable/ic_sysbar_ime_default"
+                android:background="@drawable/ic_sysbar_icon_bg"
+                android:visibility="invisible"
+                />
+            <view
+                class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout"
+                android:id="@+id/icons"
+                android:layout_width="wrap_content"
+                android:layout_height="@*android:dimen/status_bar_icon_size"
+                android:layout_marginLeft="8dip"
+                />
+        </com.android.systemui.statusbar.tablet.NotificationIconArea>
+
+
+        <FrameLayout
+            android:id="@+id/ticker"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_alignParentRight="true"
+            android:layout_toRightOf="@+id/systemInfo"
+            android:paddingLeft="6dip"
+            android:gravity="center_vertical"
+            android:animateLayoutChanges="true"
+            />
+
+        <include layout="@layout/status_bar_center"
+            android:layout_width="256dip"
+            android:layout_height="match_parent"
+            android:layout_centerInParent="true"
+            />
+
+        <LinearLayout
+            android:id="@+id/navigationArea"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_alignParentLeft="true"
+            android:orientation="horizontal"
+            >
+
+            <com.android.systemui.statusbar.KeyButtonView android:id="@+id/back"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:paddingLeft="15dip"
+                android:paddingRight="15dip"
+                android:src="@drawable/ic_sysbar_back"
+                android:background="@drawable/ic_sysbar_icon_bg"
+                systemui:keyCode="4"
+                />
+            <com.android.systemui.statusbar.KeyButtonView android:id="@+id/home"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:paddingLeft="15dip"
+                android:paddingRight="15dip"
+                android:src="@drawable/ic_sysbar_home"
+                android:background="@drawable/ic_sysbar_icon_bg"
+                systemui:keyCode="3"
+                />
+            <ImageButton android:id="@+id/recent_apps"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:src="@drawable/ic_sysbar_recent"
+                android:background="@drawable/ic_sysbar_icon_bg"
+                android:paddingLeft="15dip"
+                android:paddingRight="15dip"
+                />
+            <com.android.systemui.statusbar.KeyButtonView android:id="@+id/menu"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:paddingLeft="15dip"
+                android:paddingRight="15dip"
+                android:src="@drawable/ic_sysbar_menu"
+                android:background="@drawable/ic_sysbar_icon_bg"
+                systemui:keyCode="82"
+                android:visibility="invisible"
+                />
+            <com.android.systemui.statusbar.tablet.ShirtPocket
+                android:id="@+id/pocket"
+                android:layout_width="71dip"
+                android:layout_height="match_parent"
+                android:background="@drawable/ic_sysbar_icon_bg"
+                android:paddingLeft="15dip"
+                android:paddingRight="15dip"
+                android:animateLayoutChanges="true"
+                android:clickable="true"
+                android:descendantFocusability="blocksDescendants"
+                android:gravity="center"
+                >
+                <ImageView 
+                    android:id="@+id/pocket_icon"
+                    android:src="@drawable/ic_sysbar_pocket"
+                    android:visibility="invisible"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:gravity="center"
+                    />
+            </com.android.systemui.statusbar.tablet.ShirtPocket>
+        </LinearLayout>
+    </RelativeLayout>
+
+    <!-- It's curtains for you. -->
+    <ImageView
+        android:id="@+id/lights_out"
+        android:src="@drawable/ic_sysbar_lightsout"
+        android:gravity="center"
+        android:background="#FF000000"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="invisible"
+        android:clickable="true"
+        />
+</com.android.systemui.statusbar.tablet.TabletStatusBarView>
+
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
new file mode 100644
index 0000000..d4f0e50
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Center of status bar: System info display, system info panel trigger -->
+<RelativeLayout android:id="@+id/systemInfo"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_centerInParent="true"
+    android:clickable="true"
+    >
+    <com.android.systemui.statusbar.Clock
+        style="@*android:style/TextAppearance.StatusBar.Icon"
+        android:id="@+id/clock"
+        android:layout_width="64dip"
+        android:layout_height="48dip"
+        android:layout_centerInParent="true"
+        android:singleLine="true"
+        android:gravity="center"
+        android:textSize="16sp"
+        android:textStyle="bold"
+        android:padding="2dip"
+        />
+    <ImageView
+        android:id="@+id/battery"
+        android:layout_width="64dip"
+        android:layout_height="16dip"
+        android:layout_toLeftOf="@id/clock"
+        android:layout_centerInParent="true"
+        android:background="@drawable/sysbar_minimeter_bg" 
+        />
+    <ImageView
+        android:id="@+id/signal"
+        android:layout_width="64dip"
+        android:layout_height="16dip"
+        android:layout_toRightOf="@id/clock"
+        android:layout_centerInParent="true"
+        android:background="@drawable/sysbar_minimeter_bg" 
+        />
+    <ImageView
+        android:id="@+id/battery_icon"
+        android:layout_height="30dip"
+        android:layout_width="30dip"
+        android:layout_toLeftOf="@id/battery"
+        android:layout_centerInParent="true"
+        android:src="@drawable/ic_sysbar_battery_mini"
+        />
+    <ImageView
+        android:id="@+id/signal_icon"
+        android:layout_height="30dip"
+        android:layout_width="30dip"
+        android:layout_toRightOf="@id/signal"
+        android:layout_centerInParent="true"
+        android:src="@drawable/ic_sysbar_wifi_mini"
+        />
+</RelativeLayout>
+
+
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_latest_event.xml b/packages/SystemUI/res/layout-xlarge/status_bar_latest_event.xml
new file mode 100644
index 0000000..049a1cc
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_latest_event.xml
@@ -0,0 +1,38 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="65sp"
+    android:orientation="vertical"
+    android:background="@android:drawable/status_bar_item_background"
+    >
+
+    <ImageButton
+        android:id="@+id/veto"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_centerVertical="true"
+        android:layout_alignParentRight="true"
+        android:src="@drawable/status_bar_veto"
+        android:scaleType="center"
+        android:background="@null"
+        android:paddingLeft="16dip"
+        android:paddingRight="16dip"
+        />
+
+    <com.android.systemui.statusbar.LatestItemView android:id="@+id/content"
+        android:layout_alignParentTop="true"
+        android:layout_toLeftOf="@id/veto"
+        android:layout_width="match_parent"
+        android:layout_height="64sp"
+        android:focusable="true"
+        android:clickable="true"
+        android:paddingRight="6sp"
+        />
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1sp"
+        android:layout_alignParentBottom="true"
+        android:background="@android:drawable/divider_horizontal_dark"
+        />
+
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notification_peek.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notification_peek.xml
new file mode 100644
index 0000000..a7c91f5
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notification_peek.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** 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.
+*/
+-->
+
+<!--    android:background="@drawable/status_bar_closed_default_background" -->
+<com.android.systemui.statusbar.tablet.NotificationPanel
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:background="@*android:drawable/dialog_full_holo_dark"
+    android:orientation="vertical"
+    >
+
+    <FrameLayout 
+        android:id="@+id/content"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal|bottom"
+        android:animationCache="false"
+        android:orientation="vertical"
+        android:background="@drawable/status_bar_background"
+        android:clickable="true"
+        android:focusable="true"
+        android:descendantFocusability="afterDescendants"
+        >
+    </FrameLayout>
+</com.android.systemui.statusbar.tablet.NotificationPanel>
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
new file mode 100644
index 0000000..8cbf3e3
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** 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.
+*/
+-->
+
+<!--    android:background="@drawable/status_bar_closed_default_background" -->
+<com.android.systemui.statusbar.tablet.NotificationPanel
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:background="@*android:drawable/dialog_full_holo_dark"
+    android:orientation="vertical"
+    android:animateLayoutChanges="true"
+    >
+
+    <ScrollView
+        android:id="@+id/notificationScroller"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:animateLayoutChanges="true"
+        >
+        <LinearLayout 
+            android:id="@+id/content"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal|bottom"
+            android:animateLayoutChanges="true"
+            android:animationCache="false"
+            android:orientation="vertical"
+            android:background="@drawable/status_bar_background"
+            android:clickable="true"
+            android:focusable="true"
+            android:descendantFocusability="afterDescendants"
+            >
+        </LinearLayout>
+    </ScrollView>
+</com.android.systemui.statusbar.tablet.NotificationPanel>
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_pocket.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_pocket.xml
new file mode 100644
index 0000000..e4a6da4
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_pocket.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:background="@*android:drawable/dialog_full_holo_dark"
+    >
+    <TextView
+        android:id="@+id/description"
+        android:textAppearance="@android:style/TextAppearance.Small"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:textStyle="bold"
+        android:maxLines="1"
+        android:layout_alignParentBottom="true"
+        />
+
+    <FrameLayout
+        android:id="@+id/preview"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:layout_above="@+id/description"
+        android:descendantFocusability="blocksDescendants"
+        >
+        <ImageView
+            android:id="@+id/icon"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:scaleType="centerInside"
+            android:visibility="gone"
+            />
+        <TextView
+            android:id="@+id/alt"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:textAppearance="@android:style/TextAppearance.Large"
+            android:gravity="center"
+            android:visibility="gone"
+            />
+    </FrameLayout>
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_recent.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_recent.xml
new file mode 100644
index 0000000..2f9e0d7
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_recent.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<com.android.systemui.statusbar.tablet.RecentAppsPanel
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:background="@drawable/sysbar_panel_recents_bg"
+    android:orientation="vertical">
+
+    <TextView android:id="@+id/recents_no_recents"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:text="@string/recent_tasks_empty"
+        android:gravity="center_horizontal|center_vertical"
+        android:visibility="gone">
+    </TextView>
+
+    <HorizontalScrollView android:id="@+id/scroll_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <LinearLayout android:id="@+id/recents_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="right"
+            android:orientation="horizontal"
+        />
+
+    </HorizontalScrollView>
+
+</com.android.systemui.statusbar.tablet.RecentAppsPanel>
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
new file mode 100644
index 0000000..e6ec9f4
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<com.android.systemui.statusbar.tablet.SystemPanel
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:background="@drawable/sysbar_panel_bg"
+    android:orientation="vertical"
+    android:paddingLeft="70dip"
+    android:paddingRight="120dip"
+    >
+
+    <!-- top row: quick settings buttons -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="64dip"
+        android:orientation="horizontal"
+        android:gravity="center"
+        >
+        <ImageButton android:id="@+id/brightness"
+            android:layout_width="90dip"
+            android:layout_height="64dip"
+            android:src="@drawable/ic_sysbar_brightness"
+            android:background="@drawable/sysbar_toggle_bg_off"
+            />
+        <ImageButton android:id="@+id/sound"
+            android:layout_width="90dip"
+            android:layout_height="64dip"
+            android:layout_marginLeft="8dip"
+            android:src="@drawable/ic_sysbar_sound_on"
+            android:background="@drawable/sysbar_toggle_bg_off"
+            />
+        <ImageButton android:id="@+id/orientation"
+            android:layout_width="90dip"
+            android:layout_height="64dip"
+            android:layout_marginLeft="8dip"
+            android:src="@drawable/ic_sysbar_rotate_on"
+            android:background="@drawable/sysbar_toggle_bg_off"
+            />
+        <ImageButton android:id="@+id/airplane"
+            android:layout_width="90dip"
+            android:layout_height="64dip"
+            android:layout_marginLeft="8dip"
+            android:src="@drawable/ic_sysbar_airplane_on"
+            android:background="@drawable/sysbar_toggle_bg_off"
+            />
+        <ImageButton android:id="@+id/gps"
+            android:layout_width="90dip"
+            android:layout_height="64dip"
+            android:layout_marginLeft="8dip"
+            android:src="@drawable/ic_sysbar_gps_on"
+            android:background="@drawable/sysbar_toggle_bg_off"
+            />
+        <ImageButton android:id="@+id/bluetooth"
+            android:layout_width="90dip"
+            android:layout_height="64dip"
+            android:layout_marginLeft="8dip"
+            android:src="@drawable/ic_sysbar_bluetooth_on"
+            android:background="@drawable/sysbar_toggle_bg_off"
+            />
+    </LinearLayout>
+
+    <!-- main row: meters, clock -->
+    <RelativeLayout
+        android:padding="8dip"
+        android:layout_width="match_parent"
+        android:layout_height="192dip"
+        >
+        <RelativeLayout
+            android:layout_width="256dip"
+            android:layout_height="192dip"
+            android:layout_alignParentLeft="true"
+            android:layout_marginLeft="48dip"
+            >
+            <ImageView android:id="@+id/battery_meter"
+                android:layout_width="256dip"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:scaleType="centerCrop"
+                android:src="@drawable/sysbar_battery"
+                />
+            <TextView android:id="@+id/battery_info"
+                style="@style/TextAppearance.StatusBar.SystemPanel"
+                android:layout_width="match_parent"
+                android:layout_height="24dip"
+                android:gravity="center"
+                android:layout_above="@id/battery_meter"
+                />
+        </RelativeLayout>
+
+        <com.android.systemui.statusbar.Clock
+            style="@style/TextAppearance.StatusBar.SystemPanel"
+            android:id="@+id/clock"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textSize="50sp"
+            android:textStyle="normal"
+            android:textColor="#FFFFFFFF"
+            android:layout_centerHorizontal="true"
+            android:layout_alignParentBottom="true"
+            />
+
+        <RelativeLayout
+            android:layout_width="256dip"
+            android:layout_height="192dip"
+            android:layout_alignParentRight="true"
+            android:layout_marginRight="48dip"
+            >
+            <ImageView android:id="@+id/signal_meter"
+                android:layout_width="256dip"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:scaleType="centerCrop"
+                android:src="@drawable/sysbar_signal"
+                />
+
+            <TextView android:id="@+id/signal_info"
+                style="@style/TextAppearance.StatusBar.SystemPanel"
+                android:layout_width="match_parent"
+                android:layout_height="24dip"
+                android:gravity="center"
+                android:layout_above="@id/signal_meter"
+                />
+        </RelativeLayout>
+
+        <ImageView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="8dip"
+            android:layout_marginLeft="8dip"
+            android:src="@drawable/ic_sysbar_battery_on"
+            />
+        <ImageView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_alignParentRight="true"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="8dip"
+            android:layout_marginRight="8dip"
+            android:src="@drawable/ic_sysbar_wifi_on"
+            />
+    </RelativeLayout>
+
+    <!-- bottom row: transient indicators, settings button -->
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1sp"
+        android:background="@android:drawable/divider_horizontal_dark"
+        />
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="48dip"
+        >
+        <TextView android:id="@+id/settings_button"
+            style="@style/TextAppearance.StatusBar.TextButton"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_alignParentRight="true"
+            android:paddingRight="32dip"
+            android:paddingLeft="32dip"
+            android:textSize="20sp"
+            android:gravity="center"
+            android:text="@string/system_panel_settings_button"
+            />
+        <View
+            android:id="@+id/settings_left_divider"
+            android:layout_height="match_parent"
+            android:layout_width="1sp"
+            android:layout_toLeftOf="@id/settings_button"
+            android:background="@*android:drawable/divider_vertical_dark"
+            />
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_alignParentLeft="true"
+            android:layout_toLeftOf="@id/settings_left_divider"
+            android:orientation="horizontal"
+            android:gravity="left|center_vertical"
+            >
+            <!-- TODO: alarm -->
+            <!-- TODO: sync -->
+            <com.android.systemui.statusbar.DateView
+                android:id="@+id/date"
+                style="@style/TextAppearance.StatusBar.SystemPanel"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:singleLine="true"
+                android:gravity="center"
+                />
+        </LinearLayout>
+
+
+    </RelativeLayout>
+</com.android.systemui.statusbar.tablet.SystemPanel>
diff --git a/packages/SystemUI/res/layout-xlarge/ticker.xml b/packages/SystemUI/res/layout-xlarge/ticker.xml
new file mode 100644
index 0000000..c8d855f
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/ticker.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:background="#ff000000"
+    >
+
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:layout_gravity="center_vertical"
+        android:orientation="vertical"
+        android:paddingLeft="12dp"
+        >
+
+        <TextView android:id="@+id/title"
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textStyle="bold"
+            android:maxLines="1"
+            />
+        <TextView android:id="@+id/subtitle"
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="1"
+            />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/ticker_compat.xml b/packages/SystemUI/res/layout-xlarge/ticker_compat.xml
new file mode 100644
index 0000000..79c7543
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/ticker_compat.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:background="#ff000000"
+    >
+
+    <TextView android:id="@+id/text"
+        android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_vertical"
+        android:layout_marginLeft="12dp"
+        android:gravity="center_vertical"
+        android:maxLines="2"
+        />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/ticker_icon.xml b/packages/SystemUI/res/layout-xlarge/ticker_icon.xml
new file mode 100644
index 0000000..9efa987
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/ticker_icon.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<ImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="30dp"
+    android:layout_height="30dp"
+    android:layout_gravity="center"
+    android:layout_marginLeft="6dp"
+    />
+
diff --git a/packages/SystemUI/res/layout/intruder_alert.xml b/packages/SystemUI/res/layout/intruder_alert.xml
new file mode 100644
index 0000000..ba4a774
--- /dev/null
+++ b/packages/SystemUI/res/layout/intruder_alert.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** 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.
+*/
+-->
+
+<!--    android:background="@drawable/status_bar_closed_default_background" -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="32dip"
+    android:layout_width="match_parent"
+    android:paddingLeft="8dip"
+    android:paddingRight="8dip"
+    >
+        
+    <LinearLayout 
+        android:id="@+id/intruder_alert_content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:animationCache="false"
+        android:orientation="horizontal"
+        android:background="@drawable/alert_bar_background"
+        android:clickable="true"
+        android:focusable="true"
+        android:descendantFocusability="afterDescendants"
+        >
+
+        <ImageView
+            android:id="@+id/alertIcon"
+            android:layout_width="25dip"
+            android:layout_height="25dip"
+            android:layout_marginLeft="6dip"
+            android:layout_marginRight="8dip"
+            />
+        <TextView
+            android:id="@+id/alertText"
+            android:textAppearance="@style/TextAppearance.StatusBar.IntruderAlert"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            />
+    </LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/recent_apps_activity.xml b/packages/SystemUI/res/layout/recent_apps_activity.xml
new file mode 100644
index 0000000..ec661e8
--- /dev/null
+++ b/packages/SystemUI/res/layout/recent_apps_activity.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <!-- Title -->
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="#80FFFFFF"
+        android:textStyle="bold"
+        android:singleLine="true"
+        android:text="@string/recent_tasks_title"
+        android:visibility="gone"/>
+
+    <!-- This is only intended to be visible when carousel is invisible -->
+    <TextView
+        android:id="@+id/no_applications_message"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:text="@string/recent_tasks_empty"
+        android:visibility="gone"/>
+
+    <com.android.systemui.recent.RecentApplicationsCarouselView
+        android:id="@+id/carousel"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1">
+    </com.android.systemui.recent.RecentApplicationsCarouselView>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/recents_detail_view.xml b/packages/SystemUI/res/layout/recents_detail_view.xml
new file mode 100644
index 0000000..879d0f2
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_detail_view.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <!-- Application Title -->
+    <TextView android:id="@+id/app_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:singleLine="true"/>
+
+    <!-- Application Details -->
+    <TextView
+        android:id="@+id/app_description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index a5d6885..2f1b36e8 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -21,7 +21,8 @@
 <!--    android:background="@drawable/status_bar_closed_default_background" -->
 <com.android.systemui.statusbar.StatusBarView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:background="@drawable/statusbar_background"
+    xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+    android:background="@drawable/status_bar_background"
     android:orientation="vertical"
     android:focusable="true"
     android:descendantFocusability="afterDescendants"
@@ -55,6 +56,8 @@
             android:layout_height="match_parent"
             android:singleLine="true"
             android:paddingRight="6dip"
+            android:textSize="16sp"
+            android:textStyle="bold"
             android:gravity="center_vertical|left"
             />
     </LinearLayout>
@@ -108,6 +111,6 @@
         android:gravity="center_vertical|left"
         android:paddingLeft="6px"
         android:paddingRight="6px"
-        android:background="@drawable/statusbar_background"
+        android:background="@drawable/status_bar_background"
         />
 </com.android.systemui.statusbar.StatusBarView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index c744e4b..b5b1b50 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -31,7 +31,7 @@
         android:paddingTop="3dp"
         android:paddingBottom="5dp"
         android:paddingRight="3dp"
-        android:background="@drawable/title_bar_portrait"
+        android:background="@drawable/shade_header_background"
         >
         <com.android.systemui.statusbar.CarrierLabel
             android:layout_width="0dp"
@@ -43,7 +43,7 @@
             android:paddingBottom="1dp"
             android:paddingLeft="4dp"
             android:textAppearance="?android:attr/textAppearanceLarge"
-            android:textColor="#ffdfdfdf"
+            android:textColor="?android:attr/textColorSecondary"
             />
         <TextView android:id="@+id/clear_all_button"
             android:layout_width="wrap_content"
@@ -57,7 +57,7 @@
             style="?android:attr/buttonStyle"
             android:paddingLeft="15dp"
             android:paddingRight="15dp"
-            android:background="@android:drawable/btn_default_small"
+            android:background="@drawable/btn_default_small"
             />
     </LinearLayout>
 
@@ -71,7 +71,6 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:fadingEdge="none"
-            android:overScrollMode="ifContentScrolls"
             >
             <com.android.systemui.statusbar.NotificationLinearLayout
                 android:id="@+id/notificationLinearLayout"
@@ -83,7 +82,7 @@
                 <TextView android:id="@+id/noNotificationsTitle"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:background="@drawable/shade_bgcolor"
+                    android:background="@drawable/title_bar_portrait"
                     android:paddingLeft="5dp"
                     android:textAppearance="@style/TextAppearance.StatusBar.Title"
                     android:text="@string/status_bar_no_notifications_title"
@@ -92,7 +91,7 @@
                 <TextView android:id="@+id/ongoingTitle"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:background="@drawable/shade_bgcolor"
+                    android:background="@drawable/title_bar_portrait"
                     android:paddingLeft="5dp"
                     android:textAppearance="@style/TextAppearance.StatusBar.Title"
                     android:text="@string/status_bar_ongoing_events_title"
@@ -106,7 +105,7 @@
                 <TextView android:id="@+id/latestTitle"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:background="@drawable/shade_bgcolor"
+                    android:background="@drawable/title_bar_portrait"
                     android:paddingLeft="5dp"
                     android:textAppearance="@style/TextAppearance.StatusBar.Title"
                     android:text="@string/status_bar_latest_events_title"
diff --git a/packages/SystemUI/res/layout/status_bar_latest_event.xml b/packages/SystemUI/res/layout/status_bar_latest_event.xml
index b8a1cbe..88d9739 100644
--- a/packages/SystemUI/res/layout/status_bar_latest_event.xml
+++ b/packages/SystemUI/res/layout/status_bar_latest_event.xml
@@ -16,8 +16,8 @@
 
     <View
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@drawable/divider_horizontal_light_opaque"
+        android:layout_height="1sp"
+        android:background="@android:drawable/divider_horizontal_bright"
         />
 
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_tracking.xml b/packages/SystemUI/res/layout/status_bar_tracking.xml
index 3b9b775..a2b40e6 100644
--- a/packages/SystemUI/res/layout/status_bar_tracking.xml
+++ b/packages/SystemUI/res/layout/status_bar_tracking.xml
@@ -26,12 +26,11 @@
     android:paddingRight="0px"
     >
 
-    <View
-         android:layout_width="match_parent"
-         android:layout_height="wrap_content"
-         android:layout_weight="1"
-         android:background="#ff212121"
-         />
+    <com.android.systemui.statusbar.TrackingPatternView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        />
 
     <com.android.systemui.statusbar.CloseDragHandle android:id="@+id/close"
         android:layout_width="match_parent"
@@ -43,7 +42,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="bottom"
             android:scaleType="fitXY"
-            android:src="@drawable/status_bar_close_on"
+            android:src="@drawable/shade_handlebar"
             />
 
     </com.android.systemui.statusbar.CloseDragHandle>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index ff59db0..8785498 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Vymazat"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Žádná oznámení"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Probíhající"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Oznámení"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Baterie je vybitá:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"Zbývá <xliff:g id="NUMBER">%d%%</xliff:g> nebo méně."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Využití baterie"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Nejnovější"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 9389617..fe96883 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ryd"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ingen meddelelser"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"I gang"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meddelelser"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Batteriet er ved at være fladt:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"<xliff:g id="NUMBER">%d%%</xliff:g> eller mindre tilbage."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Batteriforbrug"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Seneste"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 3ea65d9..5338ab2 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Löschen"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Keine Benachrichtigungen"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Aktuell"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Benachrichtigungen"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Akku ist fast leer."</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"<xliff:g id="NUMBER">%d%%</xliff:g> oder weniger verbleiben."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Akkuverbrauch"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Zuletzt verwendet"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index cba1047..23db1d4 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Εκκαθάριση"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Δεν υπάρχουν ειδοποιήσεις"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Εν εξελίξει"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ειδοποιήσεις"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Η στάθμη της μπαταρίας είναι χαμηλή:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"Απομένει <xliff:g id="NUMBER">%d%%</xliff:g> ή λιγότερο."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Χρήση μπαταρίας"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Πρόσφατα"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 56dead5..c539856 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Borrar"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No hay notificaciones"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Continuo"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificaciones"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Hay poca batería:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"Restan <xliff:g id="NUMBER">%d%%</xliff:g> o menos."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Uso de la batería"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Reciente"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 4ebe492..569cef0 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Borrar"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No tienes notificaciones"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Entrante"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificaciones"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Se está agotando la batería:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"<xliff:g id="NUMBER">%d%%</xliff:g> o menos disponible"</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Uso de la batería"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Reciente"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 7aff506..b6b493f 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Effacer"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Aucune notification"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"En cours"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Le niveau de la batterie est bas :"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"Maximum <xliff:g id="NUMBER">%d%%</xliff:g> restants."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Utilisation de la batterie"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Récentes"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 496a82e..03f63db 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Cancella"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nessuna notifica"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"In corso"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifiche"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Batteria quasi scarica:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"<xliff:g id="NUMBER">%d%%</xliff:g> rimanente o meno."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Utilizzo batteria"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Recenti"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 24c018b..65cf46d 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"通知を消去"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"通知なし"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"実行中"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"電池が残り少なくなっています:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"残り<xliff:g id="NUMBER">%d%%</xliff:g>未満です。"</string>
     <string name="battery_low_why" msgid="7279169609518386372">"電池使用量"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"新着"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 0b6649d..988a7c5 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"지우기"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"알림 없음"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"진행 중"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"알림"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"배터리 전원이 부족합니다."</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"잔여 배터리가 <xliff:g id="NUMBER">%d%%</xliff:g> 이하입니다."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"배터리 사용량"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"최근 사용한 앱"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-land/strings.xml b/packages/SystemUI/res/values-land/strings.xml
new file mode 100644
index 0000000..e05e36b
--- /dev/null
+++ b/packages/SystemUI/res/values-land/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Rotation lock toast text: shown when rotation lock is turned on in landscape orientation.
+         -->
+    <string name="toast_rotation_locked">Screen is now locked in landscape orientation.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 5684e57..43b6aed 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Fjern"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ingen varslinger"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Aktiviteter"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Varslinger"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Batteriet er nesten tomt:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"mindre enn <xliff:g id="NUMBER">%d%%</xliff:g> igjen."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Batteribruk"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Nylig"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index cd10b29..ce066d5 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wissen"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Geen meldingen"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Actief"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meldingen"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"De accu raakt op:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"<xliff:g id="NUMBER">%d%%</xliff:g> of minder resterend."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Accugebruik"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Recent"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 70f3ad2..69d93f9 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wyczyść"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Brak powiadomień"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Bieżące"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Powiadomienia"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Bateria się rozładowuje:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"Pozostało: <xliff:g id="NUMBER">%d%%</xliff:g> lub mniej."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Użycie baterii"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Najnowsze"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-port/strings.xml b/packages/SystemUI/res/values-port/strings.xml
new file mode 100644
index 0000000..d3ab67b
--- /dev/null
+++ b/packages/SystemUI/res/values-port/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Rotation lock toast text: shown when rotation lock is turned on in portrait orientation.
+         -->
+    <string name="toast_rotation_locked">Screen is now locked in portrait orientation.</string>
+</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 492063a9..e8f93a2 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em curso"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"A bateria está a ficar fraca:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"Restam <xliff:g id="NUMBER">%d%%</xliff:g> ou menos."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Utilização da bateria"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Recente"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 08effa7..6dbbfb4 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"A bateria está ficando baixa:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"<xliff:g id="NUMBER">%d%%</xliff:g> ou menos restante(s)."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Uso da bateria"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Recente"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 7c5bcf7..5296e8c 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Очистить"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Нет уведомлений"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Текущие"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Уведомления"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Батарея разряжена:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"Осталось <xliff:g id="NUMBER">%d%%</xliff:g> или меньше."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Расход заряда батареи"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Недавние"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 1127c9d..4b4efe6 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ta bort"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Inga aviseringar"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Pågående"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meddelanden"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Batteriet håller på att ta slut:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"<xliff:g id="NUMBER">%d%%</xliff:g> eller mindre kvar."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Batteriförbrukning"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"Senaste"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index dd66d82..2783262 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Temizle"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Bildirim yok"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Sürüyor"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Bildirimler"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"Pil tükeniyor:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"<xliff:g id="NUMBER">%d%%</xliff:g> veya daha az kaldı."</string>
     <string name="battery_low_why" msgid="7279169609518386372">"Pil kullanımı"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"En Son Görevler"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-xlarge/colors.xml b/packages/SystemUI/res/values-xlarge/colors.xml
new file mode 100644
index 0000000..14161c3
--- /dev/null
+++ b/packages/SystemUI/res/values-xlarge/colors.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <drawable name="status_bar_background">#000000</drawable>
+</resources>
+
diff --git a/packages/SystemUI/res/values-xlarge/config.xml b/packages/SystemUI/res/values-xlarge/config.xml
new file mode 100644
index 0000000..6df883c
--- /dev/null
+++ b/packages/SystemUI/res/values-xlarge/config.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+    <integer name="config_status_bar_position">1</integer>
+
+    <!-- Component to be used as the status bar service.  Must implement the IStatusBar
+     interface.  This name is in the ComponentName flattened format (package/class)  -->
+    <string name="config_statusBarComponent">com.android.systemui.statusbar.tablet.TabletStatusBarService</string>
+
+</resources>
+
diff --git a/packages/SystemUI/res/values-xlarge/strings.xml b/packages/SystemUI/res/values-xlarge/strings.xml
new file mode 100644
index 0000000..3c59c92
--- /dev/null
+++ b/packages/SystemUI/res/values-xlarge/strings.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- The text for the button in the notification window-shade that clears
+         all of the currently visible notifications. -->
+    <string name="status_bar_clear_all_button">Clear all</string>
+
+    <!-- System panel ("Quick Settings") -->
+
+    <!-- Text to display underneath the graphical battery meter. Should
+         include the word for "battery" and a place for the percentage charge
+         available. [CHAR LIMIT=20] -->
+    <string name="system_panel_battery_meter_format">
+        Battery: <xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g>
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         no connection is available. [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_disconnected">
+        no internet connection
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying information about a connected, named Wi-Fi network.
+         Should include the word for "Wi-Fi" and a placeholder for the
+         wireless network's SSID. [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_wifi_ssid_format">
+        Wi-Fi: <xliff:g id="ssid">%s</xliff:g>
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying Wi-Fi status and Wi-Fi is connected to a network
+         whose SSID is not available.
+         [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_wifi_nossid">
+        Wi-Fi: connected
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying Wi-Fi status and Wi-Fi is in the process of
+         connecting to a network.  [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_wifi_connecting">
+        Wi-Fi: connecting…
+    </string>
+ 
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying mobile data (3G) status and a network connection is
+         available.
+         [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_data_connected">
+        Mobile data: connected
+    </string>
+
+    <!-- Text to display underneath the graphical signal strength meter when
+         it is displaying mobile data (3G) status and a network connection is
+         unavailable.
+         [CHAR LIMIT=20] -->
+    <string name="system_panel_signal_meter_data_connecting">
+        Mobile data: connecting…
+    </string>
+</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index cda0317..958c051 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"无通知"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"正在进行的"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"电量所剩不多:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"电量剩余 <xliff:g id="NUMBER">%d%%</xliff:g> 或更少。"</string>
     <string name="battery_low_why" msgid="7279169609518386372">"电量使用情况"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"近期任务"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 503087b..c4ecc32 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -22,6 +22,10 @@
     <!-- no translation found for app_label (4489004083283879149) -->
     <skip />
     <string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string>
+    <!-- no translation found for status_bar_do_not_disturb_button (5812628897510997853) -->
+    <skip />
+    <!-- no translation found for status_bar_please_disturb_button (3345398298841572813) -->
+    <skip />
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"沒有通知"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"進行中"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
@@ -29,4 +33,9 @@
     <string name="battery_low_subtitle" msgid="7388781709819722764">"電池電量即將不足:"</string>
     <string name="battery_low_percent_format" msgid="696154104579022959">"還剩 <xliff:g id="NUMBER">%d%%</xliff:g> 以下。"</string>
     <string name="battery_low_why" msgid="7279169609518386372">"電池使用狀況"</string>
+    <!-- no translation found for system_panel_settings_button (7832600575390861653) -->
+    <skip />
+    <string name="recent_tasks_title" msgid="3691764623638127888">"最新的"</string>
+    <!-- no translation found for recent_tasks_empty (1905484479067697884) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
new file mode 100644
index 0000000..23bcf20
--- /dev/null
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <declare-styleable name="KeyButtonView">
+        <attr name="keyCode" format="integer" />
+    </declare-styleable>
+</resources>
+
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 6550438..a0def6b 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -17,7 +17,5 @@
  */
 -->
 <resources>
-    <drawable name="shade_bgcolor">#ff282828</drawable>
-    <drawable name="notification_header_text_color">#ff969696</drawable>
     <drawable name="notification_number_text_color">#ffffffff</drawable>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 8ea46e5..4570d8e 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -20,7 +20,20 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <!-- Control whether status bar should distinguish HSPA data icon form UMTS data icon on devices -->
+
+    <!-- Control whether status bar should distinguish HSPA data icon form UMTS
+    data icon on devices -->
     <bool name="config_hspa_data_distinguishable">false</bool>
+
+    <!-- The location of the status bar.
+        0 - top
+        1 - bottom
+    -->
+    <integer name="config_status_bar_position">0</integer>
+
+    <!-- Component to be used as the status bar service.  Must implement the IStatusBar
+     interface.  This name is in the ComponentName flattened format (package/class)  -->
+    <string name="config_statusBarComponent">com.android.systemui.statusbar.PhoneStatusBarService</string>
+
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ba3a3d1..f34dc22 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -24,6 +24,18 @@
          all of the currently visible notifications. -->
     <string name="status_bar_clear_all_button">Clear</string>
 
+    <!-- The text for the button in the notification window-shade that turns
+         on do not disturb mode, where notifications no longer show their ticker,
+         no sound plays, and no icons are visible.  The windowshade continues to show
+         the notifications. -->
+    <string name="status_bar_do_not_disturb_button">Do not disturb</string>
+
+    <!-- The text for the button in the notification window-shade that turns
+         off do not disturb mode.  After clicking this, notifications will be
+         shown again. -->
+    <string name="status_bar_please_disturb_button">Show notifications</string>
+
+
     <!-- The label in the bar at the top of the status bar when there are no notifications
          showing. -->
     <string name="status_bar_no_notifications_title">No notifications</string>
@@ -41,14 +53,42 @@
     <string name="battery_low_title">Please connect charger</string>
 
     <!-- When the battery is low, this is displayed to the user in a dialog. The subtitle of the low battery alert. -->
-    <string name="battery_low_subtitle">The battery is getting low:</string>
+    <string name="battery_low_subtitle">The battery is getting low.</string>
 
-    <!-- A message that appears when the battery level is getting low in a dialog.  This is appened to the subtitle of the low battery alert. -->
-    <string name="battery_low_percent_format"><xliff:g id="number">%d%%</xliff:g>
-    or less remaining.</string>
+    <!-- A message that appears when the battery level is getting low in a dialog.  This is
+        appened to the subtitle of the low battery alert. -->
+    <string name="battery_low_percent_format"><xliff:g id="number">%d%%</xliff:g> remaining</string>
+
+    <!-- A message that appears when a USB charger is plugged in and the device does not
+    support charging on it.  That is, a charger that fits into the USB port and goes into
+    a wall socket, not into a computer. (This happens because some devices require more
+    current than the USB spec allows.  -->
+    <string name="invalid_charger">USB charging not supported.\nUse only the supplied charger.</string>
 
     <!-- When the battery is low, this is the label of the button to go to the
          power usage activity to find out what drained the battery. -->
     <string name="battery_low_why">Battery use</string>
 
+    <!-- Name of the button that links to the Settings app. [MAXCHARS=NONE] -->
+    <string name="system_panel_settings_button">Settings</string>
+
+
+    <!-- Recent Tasks dialog: title
+     TODO: this should move to SystemUI.apk, but the code for the old 
+            recent dialog is still in the framework
+     -->
+    <string name="recent_tasks_title">Recent</string>
+    <!-- Recent Tasks dialog: message when there are no recent applications
+     TODO: this should move to SystemUI.apk, but the code for the old 
+            recent dialog is still in the framework
+     -->
+    <string name="recent_tasks_empty">No recent applications.</string>
+
+    <!-- Rotation lock toast text: shown when rotation lock is turned off (and the screen will
+         auto-rotate based on the accelerometer). -->
+    <string name="toast_rotation_free">Screen will rotate automatically.</string>
+    
+    <!-- Rotation lock toast text: shown when rotation lock is turned on and the orientation is
+         undefined.  -->
+    <string name="toast_rotation_locked">Screen rotation is now locked.</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 86bcf3a..5aefb02 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -19,10 +19,24 @@
     <style name="TextAppearance.StatusBar.Title" parent="@android:style/TextAppearance.StatusBar">
         <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
         <item name="android:textStyle">bold</item>
-        <item name="android:textColor">@drawable/notification_header_text_color</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.StatusBar.IntruderAlert"
         parent="@android:style/TextAppearance.StatusBar">
     </style>
+
+    <style name="TextAppearance.StatusBar.SystemPanel"
+        parent="@android:style/TextAppearance.StatusBar">
+        <item name="android:textAppearance">?android:attr/textAppearance</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#FF808080</item>
+    </style>
+
+    <style name="TextAppearance.StatusBar.TextButton"
+        parent="@android:style/TextAppearance.StatusBar">
+        <item name="android:textAppearance">?android:attr/textAppearance</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#FFFFFFFF</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
new file mode 100644
index 0000000..53fe2ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import android.content.Context;
+
+public abstract class SystemUI {
+    public Context mContext;
+
+    public abstract void start();
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
new file mode 100644
index 0000000..65990ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.Slog;
+
+public class SystemUIService extends Service {
+    static final String TAG = "SystemUIService";
+
+    /**
+     * The class names of the stuff to start.
+     */
+    final Object[] SERVICES = new Object[] {
+            R.string.config_statusBarComponent,
+            com.android.systemui.power.PowerUI.class,
+        };
+
+    /**
+     * Hold a reference on the stuff we start.
+     */
+    SystemUI[] mServices;
+
+    private Class chooseClass(Object o) {
+        if (o instanceof Integer) {
+            final String cl = getString((Integer)o);
+            try {
+                return getClassLoader().loadClass(cl);
+            } catch (ClassNotFoundException ex) {
+                throw new RuntimeException(ex);
+            }
+        } else if (o instanceof Class) {
+            return (Class)o;
+        } else {
+            throw new RuntimeException("Unknown system ui service: " + o);
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        final int N = SERVICES.length;
+        mServices = new SystemUI[N];
+        for (int i=0; i<N; i++) {
+            Class cl = chooseClass(SERVICES[i]);
+            Slog.d(TAG, "loading: " + cl);
+            try {
+                mServices[i] = (SystemUI)cl.newInstance();
+            } catch (IllegalAccessException ex) {
+                throw new RuntimeException(ex);
+            } catch (InstantiationException ex) {
+                throw new RuntimeException(ex);
+            }
+            mServices[i].mContext = this;
+            Slog.d(TAG, "running: " + mServices[i]);
+            mServices[i].start();
+        }
+    }
+
+    /**
+     * Nobody binds to us.
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump StatusBar from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        if (args == null || args.length == 0) {
+            for (SystemUI ui: mServices) {
+                pw.println("dumping service: " + ui.getClass().getName());
+                ui.dump(fd, pw, args);
+            }
+        } else {
+            String svc = args[0];
+            for (SystemUI ui: mServices) {
+                String name = ui.getClass().getName();
+                if (name.endsWith(svc)) {
+                    ui.dump(fd, pw, args);
+                }
+            }
+        }
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
new file mode 100644
index 0000000..9dfc9c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -0,0 +1,289 @@
+/*
+ * 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.android.systemui.power;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.BatteryManager;
+import android.os.Handler;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+public class PowerUI extends SystemUI {
+    static final String TAG = "PowerUI";
+
+    Handler mHandler = new Handler();
+
+    int mBatteryLevel = 100;
+    int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
+    int mPlugType = 0;
+    int mInvalidCharger = 0;
+
+    int mLowBatteryAlertCloseLevel;
+    int[] mLowBatteryReminderLevels = new int[2];
+
+    AlertDialog mInvalidChargerDialog;
+    AlertDialog mLowBatteryDialog;
+    TextView mBatteryLevelTextView;
+
+    public void start() {
+
+        mLowBatteryAlertCloseLevel = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
+        mLowBatteryReminderLevels[0] = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryWarningLevel);
+        mLowBatteryReminderLevels[1] = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
+
+        // Register for Intent broadcasts for...
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        filter.addAction(Intent.ACTION_POWER_CONNECTED);
+        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
+    }
+
+    /**
+     * Buckets the battery level.
+     *
+     * The code in this function is a little weird because I couldn't comprehend
+     * the bucket going up when the battery level was going down. --joeo
+     *
+     * 1 means that the battery is "ok"
+     * 0 means that the battery is between "ok" and what we should warn about.
+     * less than 0 means that the battery is low
+     */
+    private int findBatteryLevelBucket(int level) {
+        if (level >= mLowBatteryAlertCloseLevel) {
+            return 1;
+        }
+        if (level >= mLowBatteryReminderLevels[0]) {
+            return 0;
+        }
+        final int N = mLowBatteryReminderLevels.length;
+        for (int i=N-1; i>=0; i--) {
+            if (level <= mLowBatteryReminderLevels[i]) {
+                return -1-i;
+            }
+        }
+        throw new RuntimeException("not possible!");
+    }
+
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+                final int oldBatteryLevel = mBatteryLevel;
+                mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
+                final int oldBatteryStatus = mBatteryStatus;
+                mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
+                        BatteryManager.BATTERY_STATUS_UNKNOWN);
+                final int oldPlugType = mPlugType;
+                mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
+                final int oldInvalidCharger = mInvalidCharger;
+                mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
+
+                final boolean plugged = mPlugType != 0;
+                final boolean oldPlugged = oldPlugType != 0;
+
+                int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
+                int bucket = findBatteryLevelBucket(mBatteryLevel);
+
+                if (false) {
+                    Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
+                            + " .. " + mLowBatteryReminderLevels[0]
+                            + " .. " + mLowBatteryReminderLevels[1]);
+                    Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);
+                    Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);
+                    Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);
+                    Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
+                    Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);
+                    Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);
+                }
+
+                if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
+                    Slog.d(TAG, "showing invalid charger warning");
+                    showInvalidChargerDialog();
+                    return;
+                } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
+                    Slog.d(TAG, "closing invalid charger warning");
+                    dismissInvalidChargerDialog();
+                } else if (mInvalidChargerDialog != null) {
+                    // if invalid charger is showing, don't show low battery
+                    return;
+                }
+
+                if (!plugged
+                        && (bucket < oldBucket || oldPlugged)
+                        && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+                        && bucket < 0) {
+                    Slog.d(TAG, "showing low battery warning: level=" + mBatteryLevel);
+                    showLowBatteryWarning();
+                } else if (plugged || (bucket > oldBucket && bucket > 0)) {
+                    Slog.d(TAG, "closing low battery warning: level=" + mBatteryLevel);
+                    dismissLowBatteryWarning();
+                } else if (mBatteryLevelTextView != null) {
+                    showLowBatteryWarning();
+                }
+            } else {
+                Slog.w(TAG, "unknown intent: " + intent);
+            }
+        }
+    };
+
+    void dismissLowBatteryWarning() {
+        if (mLowBatteryDialog != null) {
+            mLowBatteryDialog.dismiss();
+        }
+    }
+
+    void showLowBatteryWarning() {
+        CharSequence levelText = mContext.getString(
+                R.string.battery_low_percent_format, mBatteryLevel);
+
+        if (mBatteryLevelTextView != null) {
+            mBatteryLevelTextView.setText(levelText);
+        } else {
+            View v = View.inflate(mContext, R.layout.battery_low, null);
+            mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);
+
+            mBatteryLevelTextView.setText(levelText);
+
+            AlertDialog.Builder b = new AlertDialog.Builder(mContext);
+                b.setCancelable(true);
+                b.setTitle(R.string.battery_low_title);
+                b.setView(v);
+                b.setIcon(android.R.drawable.ic_dialog_alert);
+                b.setPositiveButton(android.R.string.ok, null);
+
+            final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                    | Intent.FLAG_ACTIVITY_NO_HISTORY);
+            if (intent.resolveActivity(mContext.getPackageManager()) != null) {
+                b.setNegativeButton(R.string.battery_low_why,
+                        new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int which) {
+                        mContext.startActivity(intent);
+                        if (mLowBatteryDialog != null) {
+                            mLowBatteryDialog.dismiss();
+                        }
+                    }
+                });
+            }
+
+            AlertDialog d = b.create();
+            d.setOnDismissListener(new DialogInterface.OnDismissListener() {
+                    public void onDismiss(DialogInterface dialog) {
+                        mLowBatteryDialog = null;
+                        mBatteryLevelTextView = null;
+                    }
+                });
+            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+            d.show();
+            mLowBatteryDialog = d;
+        }
+
+if (false) { // getRingtone ANRs
+        final ContentResolver cr = mContext.getContentResolver();
+        if (Settings.System.getInt(cr, Settings.System.POWER_SOUNDS_ENABLED, 1) == 1) {
+            final String soundPath = Settings.System.getString(cr,
+                    Settings.System.LOW_BATTERY_SOUND);
+            if (soundPath != null) {
+                final Uri soundUri = Uri.parse("file://" + soundPath);
+                if (soundUri != null) {
+                    final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
+                    if (sfx != null) {
+                        sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+                        sfx.play();
+                    }
+                }
+            }
+        }
+}
+    }
+
+    void dismissInvalidChargerDialog() {
+        if (mInvalidChargerDialog != null) {
+            mInvalidChargerDialog.dismiss();
+        }
+    }
+
+    void showInvalidChargerDialog() {
+        dismissLowBatteryWarning();
+
+        AlertDialog.Builder b = new AlertDialog.Builder(mContext);
+            b.setCancelable(true);
+            b.setMessage(R.string.invalid_charger);
+            b.setIcon(android.R.drawable.ic_dialog_alert);
+            b.setPositiveButton(android.R.string.ok, null);
+
+        AlertDialog d = b.create();
+            d.setOnDismissListener(new DialogInterface.OnDismissListener() {
+                    public void onDismiss(DialogInterface dialog) {
+                        mInvalidChargerDialog = null;
+                        mBatteryLevelTextView = null;
+                    }
+                });
+
+        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+        d.show();
+        mInvalidChargerDialog = d;
+    }
+    
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.print("mLowBatteryAlertCloseLevel=");
+        pw.println(mLowBatteryAlertCloseLevel);
+        pw.print("mLowBatteryReminderLevels=");
+        pw.println(Arrays.toString(mLowBatteryReminderLevels));
+        pw.print("mInvalidChargerDialog=");
+        pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());
+        pw.print("mLowBatteryDialog=");
+        pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());
+        pw.print("mBatteryLevel=");
+        pw.println(Integer.toString(mBatteryLevel));
+        pw.print("mBatteryStatus=");
+        pw.println(Integer.toString(mBatteryStatus));
+        pw.print("mPlugType=");
+        pw.println(Integer.toString(mPlugType));
+        pw.print("mInvalidCharger=");
+        pw.println(Integer.toString(mInvalidCharger));
+        pw.print("bucket: ");
+        pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
new file mode 100644
index 0000000..faea3fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.systemui.recent;
+
+import com.android.systemui.R;
+
+import com.android.ex.carousel.CarouselView;
+import com.android.ex.carousel.CarouselViewHelper;
+import com.android.ex.carousel.CarouselRS.CarouselCallback;
+import com.android.ex.carousel.CarouselViewHelper.DetailTextureParameters;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.IThumbnailReceiver;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.PorterDuff;
+import android.graphics.Bitmap.Config;
+import android.graphics.drawable.Drawable;
+import android.graphics.PixelFormat;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.widget.TextView;
+
+public class RecentApplicationsActivity extends Activity {
+    private static final String TAG = "RecentApplicationsActivity";
+    private static boolean DBG = false;
+    private static final int CARD_SLOTS = 56;
+    private static final int VISIBLE_SLOTS = 7;
+    private static final int MAX_TASKS = VISIBLE_SLOTS * 2;
+
+    // TODO: these should be configurable
+    private static final int DETAIL_TEXTURE_MAX_WIDTH = 200;
+    private static final int DETAIL_TEXTURE_MAX_HEIGHT = 80;
+    private static final int TEXTURE_WIDTH = 256;
+    private static final int TEXTURE_HEIGHT = 256;
+
+    private ActivityManager mActivityManager;
+    private List<RunningTaskInfo> mRunningTaskList;
+    private boolean mPortraitMode = true;
+    private ArrayList<ActivityDescription> mActivityDescriptions
+            = new ArrayList<ActivityDescription>();
+    private CarouselView mCarouselView;
+    private LocalCarouselViewHelper mHelper;
+    private View mNoRecentsView;
+    private Bitmap mLoadingBitmap;
+    private Bitmap mRecentOverlay;
+    private boolean mHidden = false;
+    private boolean mHiding = false;
+    private DetailInfo mDetailInfo;
+
+    /**
+     * This class is a container for all items associated with the DetailView we'll
+     * be drawing to a bitmap and sending to Carousel.
+     *
+     */
+    static final class DetailInfo {
+        public DetailInfo(View _view, TextView _title, TextView _desc) {
+            view = _view;
+            title = _title;
+            description = _desc;
+        }
+
+        /**
+         * Draws view into the given bitmap, if provided
+         * @param bitmap
+         */
+        public Bitmap draw(Bitmap bitmap) {
+            resizeView(view, DETAIL_TEXTURE_MAX_WIDTH, DETAIL_TEXTURE_MAX_HEIGHT);
+            int desiredWidth = view.getWidth();
+            int desiredHeight = view.getHeight();
+            if (bitmap == null || desiredWidth != bitmap.getWidth()
+                    || desiredHeight != bitmap.getHeight()) {
+                bitmap = Bitmap.createBitmap(desiredWidth, desiredHeight, Config.ARGB_8888);
+            }
+            Canvas canvas = new Canvas(bitmap);
+            view.draw(canvas);
+            return bitmap;
+        }
+
+        /**
+         * Force a layout pass on the given view.
+         */
+        private void resizeView(View view, int maxWidth, int maxHeight) {
+            int widthSpec = MeasureSpec.getMode(MeasureSpec.AT_MOST)
+                    | MeasureSpec.getSize(maxWidth);
+            int heightSpec = MeasureSpec.getMode(MeasureSpec.AT_MOST)
+                    | MeasureSpec.getSize(maxHeight);
+            view.measure(widthSpec, heightSpec);
+            view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
+            Log.v(TAG, "RESIZED VIEW: " + view.getWidth() + ", " + view.getHeight());
+        }
+
+        public View view;
+        public TextView title;
+        public TextView description;
+    }
+
+    static class ActivityDescription {
+        int id;
+        Bitmap thumbnail; // generated by Activity.onCreateThumbnail()
+        Drawable icon; // application package icon
+        String label; // application package label
+        CharSequence description; // generated by Activity.onCreateDescription()
+        Intent intent; // launch intent for application
+        Matrix matrix; // arbitrary rotation matrix to correct orientation
+        int position; // position in list
+
+        public ActivityDescription(Bitmap _thumbnail,
+                Drawable _icon, String _label, String _desc, int _id, int _pos)
+        {
+            thumbnail = _thumbnail;
+            icon = _icon;
+            label = _label;
+            description = _desc;
+            id = _id;
+            position = _pos;
+        }
+
+        public void clear() {
+            icon = null;
+            thumbnail = null;
+            label = null;
+            description = null;
+            intent = null;
+            matrix = null;
+            id = -1;
+            position = -1;
+        }
+    };
+
+    private ActivityDescription findActivityDescription(int id) {
+        for (int i = 0; i < mActivityDescriptions.size(); i++) {
+            ActivityDescription item = mActivityDescriptions.get(i);
+            if (item != null && item.id == id) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    private class LocalCarouselViewHelper extends CarouselViewHelper {
+        private DetailTextureParameters mDetailParams = new DetailTextureParameters(10.0f, 20.0f);
+
+        public LocalCarouselViewHelper(Context context) {
+            super(context);
+        }
+
+        @Override
+        public DetailTextureParameters getDetailTextureParameters(int id) {
+            return mDetailParams;
+        }
+
+        public void onCardSelected(int n) {
+            if (n < mActivityDescriptions.size()) {
+                ActivityDescription item = mActivityDescriptions.get(n);
+                // prepare a launch intent and send it
+                if (item.intent != null) {
+                    item.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+                    try {
+                        if (DBG) Log.v(TAG, "Starting intent " + item.intent);
+                        startActivity(item.intent);
+                        overridePendingTransition(R.anim.recent_app_enter, R.anim.recent_app_leave);
+                    } catch (ActivityNotFoundException e) {
+                        if (DBG) Log.w("Recent", "Unable to launch recent task", e);
+                    }
+                    finish();
+                }
+            }
+        }
+
+        @Override
+        public Bitmap getTexture(final int id) {
+            if (DBG) Log.v(TAG, "onRequestTexture(" + id + ")");
+            ActivityDescription info;
+            synchronized(mActivityDescriptions) {
+                info = mActivityDescriptions.get(id);
+            }
+            Bitmap bitmap = null;
+            if (info != null) {
+                bitmap = compositeBitmap(info);
+            }
+            return bitmap;
+        }
+
+        @Override
+        public Bitmap getDetailTexture(int n) {
+            Bitmap bitmap = null;
+            if (n < mActivityDescriptions.size()) {
+                ActivityDescription item = mActivityDescriptions.get(n);
+                mDetailInfo.title.setText(item.label);
+                mDetailInfo.description.setText(item.description);
+                bitmap = mDetailInfo.draw(null);
+            }
+            return bitmap;
+        }
+    };
+
+    private Bitmap compositeBitmap(ActivityDescription info) {
+        final int targetWidth = TEXTURE_WIDTH;
+        final int targetHeight = TEXTURE_HEIGHT;
+        final int border = 3; // inset along the edge for thumnnail content
+        final int overlap = 1; // how many pixels of overlap between border and thumbnail
+        final Resources res = getResources();
+        if (mRecentOverlay == null) {
+            mRecentOverlay = BitmapFactory.decodeResource(res, R.drawable.recent_overlay);
+        }
+
+        // Create a bitmap of the proper size/format and set the canvas to draw to it
+        final Bitmap result = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(result);
+        canvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, Paint.FILTER_BITMAP_FLAG));
+        Paint paint = new Paint();
+        paint.setFilterBitmap(false);
+
+        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+        canvas.save();
+        if (info.thumbnail != null) {
+            // Draw the thumbnail
+            int sourceWidth = targetWidth - 2 * (border - overlap);
+            int sourceHeight = targetHeight - 2 * (border - overlap);
+            final float scaleX = (float) sourceWidth / info.thumbnail.getWidth();
+            final float scaleY = (float) sourceHeight / info.thumbnail.getHeight();
+            canvas.translate(border * 0.5f, border * 0.5f);
+            canvas.scale(scaleX, scaleY);
+            canvas.drawBitmap(info.thumbnail, 0, 0, paint);
+        } else {
+            // Draw the Loading bitmap placeholder, TODO: Remove when RS handles blending
+            final float scaleX = (float) targetWidth / mLoadingBitmap.getWidth();
+            final float scaleY = (float) targetHeight / mLoadingBitmap.getHeight();
+            canvas.scale(scaleX, scaleY);
+            canvas.drawBitmap(mLoadingBitmap, 0, 0, paint);
+        }
+        canvas.restore();
+
+        // Draw overlay
+        canvas.save();
+        final float scaleOverlayX = (float) targetWidth / mRecentOverlay.getWidth();
+        final float scaleOverlayY = (float) targetHeight / mRecentOverlay.getHeight();
+        canvas.scale(scaleOverlayX, scaleOverlayY);
+        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
+        canvas.drawBitmap(mRecentOverlay, 0, 0, paint);
+        canvas.restore();
+
+        // Draw icon
+        if (info.icon != null) {
+            canvas.save();
+            info.icon.draw(canvas);
+            canvas.restore();
+        }
+
+        return result;
+    }
+
+    private final IThumbnailReceiver mThumbnailReceiver = new IThumbnailReceiver.Stub() {
+
+        public void finished() throws RemoteException {
+
+        }
+
+        public void newThumbnail(final int id, final Bitmap bitmap, CharSequence description)
+                throws RemoteException {
+            int w = bitmap.getWidth();
+            int h = bitmap.getHeight();
+            if (DBG) Log.v(TAG, "New thumbnail for id=" + id + ", dimensions=" + w + "x" + h
+                    + " description '" + description + "'");
+            ActivityDescription info = findActivityDescription(id);
+            if (info != null) {
+                info.thumbnail = bitmap;
+                info.description = description;
+                final int thumbWidth = bitmap.getWidth();
+                final int thumbHeight = bitmap.getHeight();
+                if ((mPortraitMode && thumbWidth > thumbHeight)
+                        || (!mPortraitMode && thumbWidth < thumbHeight)) {
+                    Matrix matrix = new Matrix();
+                    matrix.setRotate(90.0f, (float) thumbWidth / 2, (float) thumbHeight / 2);
+                    info.matrix = matrix;
+                } else {
+                    info.matrix = null;
+                }
+                // Force Carousel to request new textures for this item.
+                mCarouselView.setTextureForItem(info.position, null);
+                mCarouselView.setDetailTextureForItem(info.position, 0, 0, 0, 0, null);
+            } else {
+                if (DBG) Log.v(TAG, "Can't find view for id " + id);
+            }
+        }
+    };
+
+    /**
+     * We never really finish() RecentApplicationsActivity, since we don't want to
+     * get destroyed and pay the start-up cost to restart it.
+     */
+    @Override
+    public void finish() {
+        moveTaskToBack(true);
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        mHidden = !mHidden;
+        if (mHidden) {
+            mHiding = true;
+            moveTaskToBack(true);
+        } else {
+            mHiding = false;
+        }
+        super.onNewIntent(intent);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final Resources res = getResources();
+        final View decorView = getWindow().getDecorView();
+
+        getWindow().getDecorView().setBackgroundColor(0x80000000);
+
+        if (mCarouselView == null) {
+            long t = System.currentTimeMillis();
+            setContentView(R.layout.recent_apps_activity);
+            long elapsed = System.currentTimeMillis() - t;
+            Log.v(TAG, "Recents layout took " + elapsed + "ms to load");
+            mLoadingBitmap = BitmapFactory.decodeResource(res, R.drawable.recent_rez_border);
+            mCarouselView = (CarouselView)findViewById(R.id.carousel);
+            mHelper = new LocalCarouselViewHelper(this);
+            mHelper.setCarouselView(mCarouselView);
+
+            mCarouselView.setSlotCount(CARD_SLOTS);
+            mCarouselView.setVisibleSlots(VISIBLE_SLOTS);
+            mCarouselView.createCards(0);
+            mCarouselView.setStartAngle((float) -(2.0f*Math.PI * 5 / CARD_SLOTS));
+            mCarouselView.setDefaultBitmap(mLoadingBitmap);
+            mCarouselView.setLoadingBitmap(mLoadingBitmap);
+            mCarouselView.setRezInCardCount(3.0f);
+            mCarouselView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
+
+            mNoRecentsView = (View) findViewById(R.id.no_applications_message);
+
+            mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+            mPortraitMode = decorView.getHeight() > decorView.getWidth();
+
+            // Load detail view which will be used to render text
+            View detail = getLayoutInflater().inflate(R.layout.recents_detail_view, null);
+            TextView title = (TextView) detail.findViewById(R.id.app_title);
+            TextView description = (TextView) detail.findViewById(R.id.app_description);
+            mDetailInfo = new DetailInfo(detail, title, description);
+
+            refresh();
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        refresh();
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        mPortraitMode = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT;
+        if (DBG) Log.v(TAG, "CONFIG CHANGE, mPortraitMode = " + mPortraitMode);
+        refresh();
+    }
+
+    void updateRunningTasks() {
+        mRunningTaskList = mActivityManager.getRunningTasks(MAX_TASKS, 0, mThumbnailReceiver);
+        if (DBG) Log.v(TAG, "Portrait: " + mPortraitMode);
+        for (RunningTaskInfo r : mRunningTaskList) {
+            if (r.thumbnail != null) {
+                int thumbWidth = r.thumbnail.getWidth();
+                int thumbHeight = r.thumbnail.getHeight();
+                if (DBG) Log.v(TAG, "Got thumbnail " + thumbWidth + "x" + thumbHeight);
+                ActivityDescription desc = findActivityDescription(r.id);
+                if (desc != null) {
+                    desc.thumbnail = r.thumbnail;
+                    desc.description = r.description;
+                    if ((mPortraitMode && thumbWidth > thumbHeight)
+                            || (!mPortraitMode && thumbWidth < thumbHeight)) {
+                        Matrix matrix = new Matrix();
+                        matrix.setRotate(90.0f, (float) thumbWidth / 2, (float) thumbHeight / 2);
+                        desc.matrix = matrix;
+                    }
+                } else {
+                    if (DBG) Log.v(TAG, "Couldn't find ActivityDesc for id=" + r.id);
+                }
+            } else {
+                if (DBG) Log.v(TAG, "*** RUNNING THUMBNAIL WAS NULL ***");
+            }
+        }
+    }
+
+    private void updateRecentTasks() {
+        final PackageManager pm = getPackageManager();
+        final ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+
+        final List<ActivityManager.RecentTaskInfo> recentTasks =
+                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+
+        ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
+                    .resolveActivityInfo(pm, 0);
+
+        // IconUtilities iconUtilities = new IconUtilities(this); // FIXME
+
+        int numTasks = recentTasks.size();
+        mActivityDescriptions.clear();
+        for (int i = 0, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
+            final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
+
+            Intent intent = new Intent(recentInfo.baseIntent);
+            if (recentInfo.origActivity != null) {
+                intent.setComponent(recentInfo.origActivity);
+            }
+
+            // Skip the current home activity.
+            if (homeInfo != null
+                    && homeInfo.packageName.equals(intent.getComponent().getPackageName())
+                    && homeInfo.name.equals(intent.getComponent().getClassName())) {
+                continue;
+            }
+
+            intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+                    | Intent.FLAG_ACTIVITY_NEW_TASK);
+            final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
+            if (resolveInfo != null) {
+                final ActivityInfo info = resolveInfo.activityInfo;
+                final String title = info.loadLabel(pm).toString();
+                Drawable icon = info.loadIcon(pm);
+
+                int id = recentTasks.get(i).id;
+                if (id != -1 && title != null && title.length() > 0 && icon != null) {
+                    // icon = null; FIXME: iconUtilities.createIconDrawable(icon);
+                    ActivityDescription item = new ActivityDescription(
+                            null, icon, title, null, id, index);
+                    item.intent = intent;
+                    mActivityDescriptions.add(item);
+                    if (DBG) Log.v(TAG, "Added item[" + index
+                            + "], id=" + item.id
+                            + ", title=" + item.label);
+                    ++index;
+                } else {
+                    if (DBG) Log.v(TAG, "SKIPPING item " + id);
+                }
+            }
+        }
+    }
+
+    private final Runnable mRefreshRunnable = new Runnable() {
+        public void run() {
+            updateRecentTasks();
+            updateRunningTasks();
+            showCarousel(mActivityDescriptions.size() > 0);
+        }
+    };
+
+    private void showCarousel(boolean show) {
+        if (show) {
+            mCarouselView.createCards(mActivityDescriptions.size());
+            for (int i = 1; i < mActivityDescriptions.size(); i++) {
+                // Force Carousel to update textures. Note we don't do this for the first item,
+                // since it will be updated when mThumbnailReceiver returns a thumbnail.
+                // TODO: only do this for apps that have changed.
+                mCarouselView.setTextureForItem(i, null);
+                mCarouselView.setDetailTextureForItem(i, 0, 0, 0, 0, null);
+            }
+            // Make carousel visible
+            mNoRecentsView.setVisibility(View.GONE);
+            mCarouselView.setVisibility(View.VISIBLE);
+            mCarouselView.createCards(mActivityDescriptions.size());
+        } else {
+            // show "No Recent Tasks"
+            mNoRecentsView.setVisibility(View.VISIBLE);
+            mCarouselView.setVisibility(View.GONE);
+        }
+    }
+
+    private void refresh() {
+        if (!mHiding && mCarouselView != null) {
+            // Don't update the view now. Instead, post a request so it happens next time
+            // we reach the looper after a delay. This way we can fold multiple refreshes
+            // into just the latest.
+            mCarouselView.removeCallbacks(mRefreshRunnable);
+            mCarouselView.postDelayed(mRefreshRunnable, 50);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsCarouselView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsCarouselView.java
new file mode 100644
index 0000000..1c8ec95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsCarouselView.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recent;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.ex.carousel.CarouselView;
+import com.android.systemui.R;
+
+public class RecentApplicationsCarouselView extends CarouselView {
+
+    public RecentApplicationsCarouselView(Context context) {
+        this(context, null);
+    }
+
+    public RecentApplicationsCarouselView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public Info getRenderScriptInfo() {
+        return new Info(R.raw.carousel);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
index f45caf51..0f6723e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java
@@ -23,7 +23,7 @@
 
 
 public class CloseDragHandle extends LinearLayout {
-    StatusBarService mService;
+    PhoneStatusBarService mService;
 
     public CloseDragHandle(Context context, AttributeSet attrs) {
         super(context, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 4f080d5..ed2ed1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -32,7 +32,7 @@
  * coalescing these calls so they don't stack up.  For the calls
  * are coalesced, note that they are all idempotent.
  */
-class CommandQueue extends IStatusBar.Stub {
+public class CommandQueue extends IStatusBar.Stub {
     private static final String TAG = "StatusBar.CommandQueue";
 
     private static final int MSG_MASK = 0xffff0000;
@@ -52,6 +52,11 @@
     private static final int OP_EXPAND = 1;
     private static final int OP_COLLAPSE = 2;
 
+    private static final int MSG_SET_LIGHTS_ON = 0x00070000;
+
+    private static final int MSG_SHOW_MENU = 0x00080000;
+    private static final int MSG_SHOW_IME_BUTTON = 0x00090000;
+
     private StatusBarIconList mList;
     private Callbacks mCallbacks;
     private Handler mHandler = new H();
@@ -75,6 +80,9 @@
         public void disable(int state);
         public void animateExpand();
         public void animateCollapse();
+        public void setLightsOn(boolean on);
+        public void setMenuKeyVisible(boolean visible);
+        public void setIMEButtonVisible(boolean visible);
     }
 
     public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -143,6 +151,27 @@
         }
     }
 
+    public void setLightsOn(boolean on) {
+        synchronized (mList) {
+            mHandler.removeMessages(MSG_SET_LIGHTS_ON);
+            mHandler.obtainMessage(MSG_SET_LIGHTS_ON, on ? 1 : 0, 0, null).sendToTarget();
+        }
+    }
+
+    public void setMenuKeyVisible(boolean visible) {
+        synchronized (mList) {
+            mHandler.removeMessages(MSG_SHOW_MENU);
+            mHandler.obtainMessage(MSG_SHOW_MENU, visible ? 1 : 0, 0, null).sendToTarget();
+        }
+    }
+
+    public void setIMEButtonVisible(boolean visible) {
+        synchronized (mList) {
+            mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
+            mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, visible ? 1 : 0, 0, null).sendToTarget();
+        }
+    }
+
     private final class H extends Handler {
         public void handleMessage(Message msg) {
             final int what = msg.what & MSG_MASK;
@@ -196,6 +225,16 @@
                     } else {
                         mCallbacks.animateCollapse();
                     }
+                    break;
+                case MSG_SET_LIGHTS_ON:
+                    mCallbacks.setLightsOn(msg.arg1 != 0);
+                    break;
+                case MSG_SHOW_MENU:
+                    mCallbacks.setMenuKeyVisible(msg.arg1 != 0);
+                    break;
+                case MSG_SHOW_IME_BUTTON:
+                    mCallbacks.setIMEButtonVisible(msg.arg1 != 0);
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/DateView.java
index e6d3a7e..e127038 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DateView.java
@@ -70,7 +70,7 @@
         setText(DateFormat.getDateInstance(DateFormat.LONG).format(now));
     }
 
-    void setUpdates(boolean update) {
+    public void setUpdates(boolean update) {
         if (update != mUpdating) {
             mUpdating = update;
             if (update) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
index 3d85f27..a2d4b95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java
@@ -27,7 +27,7 @@
 
 
 public class ExpandedView extends LinearLayout {
-    StatusBarService mService;
+    PhoneStatusBarService mService;
     int mPrevHeight = -1;
 
     public ExpandedView(Context context, AttributeSet attrs) {
@@ -53,7 +53,7 @@
              //Slog.d(StatusBarService.TAG, "height changed old=" + mPrevHeight
              //     + " new=" + height);
              mPrevHeight = height;
-             mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
+             mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
          }
      }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyButtonView.java
new file mode 100644
index 0000000..b01c5e7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyButtonView.java
@@ -0,0 +1,143 @@
+/*
+ * 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.android.systemui.statusbar;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.ServiceManager;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.HapticFeedbackConstants;
+import android.view.IWindowManager;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.widget.ImageView;
+import android.widget.RemoteViews.RemoteView;
+
+import com.android.systemui.R;
+
+public class KeyButtonView extends ImageView {
+    IWindowManager mWindowManager;
+    long mDownTime;
+    boolean mSending, mLongPressed;
+    int mCode;
+    int mRepeat;
+    Runnable mCheckLongPress = new Runnable() {
+        public void run() {
+            Slog.d("KeyButtonView", "longpress");
+            if (isPressed()) {
+                mLongPressed = true;
+                mRepeat++;
+                sendEvent(KeyEvent.ACTION_DOWN,
+                        KeyEvent.FLAG_FROM_SYSTEM
+                        | KeyEvent.FLAG_VIRTUAL_HARD_KEY
+                        | KeyEvent.FLAG_LONG_PRESS);
+            }
+        }
+    };
+
+    public KeyButtonView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public KeyButtonView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView,
+                defStyle, 0);
+
+        mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0);
+        if (mCode == 0) {
+            Slog.w(StatusBarService.TAG, "KeyButtonView without key code id=0x"
+                    + Integer.toHexString(getId()));
+        }
+        
+        a.recycle();
+
+        mWindowManager = IWindowManager.Stub.asInterface(
+                ServiceManager.getService(Context.WINDOW_SERVICE));
+    }
+
+    public boolean onTouchEvent(MotionEvent ev) {
+        final int action = ev.getAction();
+        int x, y;
+
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                //Slog.d("KeyButtonView", "press");
+                mDownTime = SystemClock.uptimeMillis();
+                mRepeat = 0;
+                mSending = true;
+                mLongPressed = false;
+                sendEvent(KeyEvent.ACTION_DOWN,
+                        KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, mDownTime);
+                setPressed(true);
+                removeCallbacks(mCheckLongPress);
+                postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
+                break;
+            case MotionEvent.ACTION_MOVE:
+                if (mSending) {
+                    x = (int)ev.getX();
+                    y = (int)ev.getY();
+                    if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) {
+                        mSending = false;
+                        sendEvent(KeyEvent.ACTION_UP,
+                                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY
+                                        | KeyEvent.FLAG_CANCELED);
+                        setPressed(false);
+                        removeCallbacks(mCheckLongPress);
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                setPressed(false);
+                if (mSending && !mLongPressed) {
+                    mSending = false;
+                    sendEvent(KeyEvent.ACTION_UP,
+                            KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
+                    removeCallbacks(mCheckLongPress);
+                }
+                break;
+        }
+
+        return true;
+    }
+
+    void sendEvent(int action, int flags) {
+        sendEvent(action, flags, SystemClock.uptimeMillis());
+    }
+
+    void sendEvent(int action, int flags, long when) {
+        final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, mRepeat,
+                0, 0, 0, flags, InputDevice.SOURCE_KEYBOARD);
+        try {
+            //Slog.d(StatusBarService.TAG, "injecting event " + ev);
+            mWindowManager.injectInputEventNoWait(ev);
+        } catch (RemoteException ex) {
+            // System process is dead
+        }
+    }
+}
+
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
index 1e89624..64ec063 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
@@ -23,12 +23,11 @@
 import android.widget.FrameLayout;
 
 public class LatestItemView extends FrameLayout {
-
     public LatestItemView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        return onTouchEvent(ev);
+    public void setOnClickListener(OnClickListener l) {
+        super.setOnClickListener(l);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 7a82267..7e8a5c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -22,6 +22,7 @@
 
 import com.android.internal.statusbar.StatusBarNotification;
 
+import java.util.Comparator;
 import java.util.ArrayList;
 
 /**
@@ -35,26 +36,47 @@
         public View row; // the outer expanded view
         public View content; // takes the click events and sends the PendingIntent
         public View expanded; // the inflated RemoteViews
+        public Entry() {}
+        public Entry(IBinder key, StatusBarNotification n, StatusBarIconView ic) {
+            this.key = key;
+            this.notification = n;
+            this.icon = ic;
+        }
     }
     private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
+    private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() {
+        public int compare(Entry a, Entry b) {
+            return (int)(a.notification.notification.when - b.notification.notification.when);
+        }
+    };
 
     public int size() {
         return mEntries.size();
     }
 
-    public Entry getEntryAt(int index) {
-        return mEntries.get(index);
+    public Entry get(int i) {
+        return mEntries.get(i);
     }
 
-    public int findEntry(IBinder key) {
-        final int N = mEntries.size();
-        for (int i=0; i<N; i++) {
-            Entry entry = mEntries.get(i);
-            if (entry.key == key) {
-                return i;
+    public Entry findByKey(IBinder key) {
+        for (Entry e : mEntries) {
+            if (e.key == key) {
+                return e;
             }
         }
-        return -1;
+        return null;
+    }
+
+    public int add(Entry entry) {
+        int i;
+        int N = mEntries.size();
+        for (i=0; i<N; i++) {
+            if (mEntryCmp.compare(mEntries.get(i), entry) > 0) {
+                break;
+            }
+        }
+        mEntries.add(i, entry);
+        return i;
     }
 
     public int add(IBinder key, StatusBarNotification notification, View row, View content,
@@ -66,42 +88,23 @@
         entry.content = content;
         entry.expanded = expanded;
         entry.icon = icon;
-        final int index = chooseIndex(notification.notification.when);
-        mEntries.add(index, entry);
-        return index;
+        return add(entry);
     }
 
     public Entry remove(IBinder key) {
-        final int N = mEntries.size();
-        for (int i=0; i<N; i++) {
-            Entry entry = mEntries.get(i);
-            if (entry.key == key) {
-                mEntries.remove(i);
-                return entry;
-            }
+        Entry e = findByKey(key);
+        if (e != null) {
+            mEntries.remove(e);
         }
-        return null;
-    }
-
-    private int chooseIndex(final long when) {
-        final int N = mEntries.size();
-        for (int i=0; i<N; i++) {
-            Entry entry = mEntries.get(i);
-            if (entry.notification.notification.when > when) {
-                return i;
-            }
-        }
-        return N;
+        return e;
     }
 
     /**
      * Return whether there are any visible items (i.e. items without an error).
      */
     public boolean hasVisibleItems() {
-        final int N = mEntries.size();
-        for (int i=0; i<N; i++) {
-            Entry entry = mEntries.get(i);
-            if (entry.expanded != null) { // the view successfully inflated
+        for (Entry e : mEntries) {
+            if (e.expanded != null) { // the view successfully inflated
                 return true;
             }
         }
@@ -112,11 +115,9 @@
      * Return whether there are any clearable items (that aren't errors).
      */
     public boolean hasClearableItems() {
-        final int N = mEntries.size();
-        for (int i=0; i<N; i++) {
-            Entry entry = mEntries.get(i);
-            if (entry.expanded != null) { // the view successfully inflated
-                if ((entry.notification.notification.flags & Notification.FLAG_NO_CLEAR) == 0) {
+        for (Entry e : mEntries) {
+            if (e.expanded != null) { // the view successfully inflated
+                if (e.notification.isClearable()) {
                     return true;
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
new file mode 100644
index 0000000..b174973
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -0,0 +1,1555 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.app.Service;
+import android.app.ActivityManagerNative;
+import android.app.Dialog;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.Log;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.FrameLayout;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Set;
+
+import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIconList;
+import com.android.internal.statusbar.StatusBarNotification;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.StatusBarPolicy;
+
+
+
+public class PhoneStatusBarService extends StatusBarService {
+    static final String TAG = "PhoneStatusBarService";
+    static final boolean SPEW = false;
+
+    public static final String ACTION_STATUSBAR_START
+            = "com.android.internal.policy.statusbar.START";
+
+    static final int EXPANDED_LEAVE_ALONE = -10000;
+    static final int EXPANDED_FULL_OPEN = -10001;
+
+    private static final int MSG_ANIMATE = 1000;
+    private static final int MSG_ANIMATE_REVEAL = 1001;
+    private static final int MSG_SHOW_INTRUDER = 1002;
+    private static final int MSG_HIDE_INTRUDER = 1003;
+
+    // will likely move to a resource or other tunable param at some point
+    private static final int INTRUDER_ALERT_DECAY_MS = 10000;
+
+    StatusBarPolicy mIconPolicy;
+
+    int mIconSize;
+    Display mDisplay;
+
+    StatusBarView mStatusBarView;
+    int mPixelFormat;
+    H mHandler = new H();
+    Object mQueueLock = new Object();
+
+    // icons
+    LinearLayout mIcons;
+    IconMerger mNotificationIcons;
+    LinearLayout mStatusIcons;
+
+    // expanded notifications
+    Dialog mExpandedDialog;
+    ExpandedView mExpandedView;
+    WindowManager.LayoutParams mExpandedParams;
+    ScrollView mScrollView;
+    View mNotificationLinearLayout;
+    View mExpandedContents;
+    // top bar
+    TextView mNoNotificationsTitle;
+    TextView mClearButton;
+    // drag bar
+    CloseDragHandle mCloseView;
+    // ongoing
+    NotificationData mOngoing = new NotificationData();
+    TextView mOngoingTitle;
+    LinearLayout mOngoingItems;
+    // latest
+    NotificationData mLatest = new NotificationData();
+    TextView mLatestTitle;
+    LinearLayout mLatestItems;
+    // position
+    int[] mPositionTmp = new int[2];
+    boolean mExpanded;
+    boolean mExpandedVisible;
+
+    // the date view
+    DateView mDateView;
+
+    // for immersive activities
+    private View mIntruderAlertView;
+
+    // the tracker view
+    TrackingView mTrackingView;
+    WindowManager.LayoutParams mTrackingParams;
+    int mTrackingPosition; // the position of the top of the tracking view.
+    private boolean mPanelSlightlyVisible;
+
+    // ticker
+    private Ticker mTicker;
+    private View mTickerView;
+    private boolean mTicking;
+
+    // Tracking finger for opening/closing.
+    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
+    boolean mTracking;
+    VelocityTracker mVelocityTracker;
+
+    static final int ANIM_FRAME_DURATION = (1000/60);
+
+    boolean mAnimating;
+    long mCurAnimationTime;
+    float mDisplayHeight;
+    float mAnimY;
+    float mAnimVel;
+    float mAnimAccel;
+    long mAnimLastTime;
+    boolean mAnimatingReveal = false;
+    int mViewDelta;
+    int[] mAbsPos = new int[2];
+
+    // for disabling the status bar
+    int mDisabled = 0;
+
+    private class ExpandedDialog extends Dialog {
+        ExpandedDialog(Context context) {
+            super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
+        }
+
+        @Override
+        public boolean dispatchKeyEvent(KeyEvent event) {
+            boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+            switch (event.getKeyCode()) {
+            case KeyEvent.KEYCODE_BACK:
+                if (!down) {
+                    animateCollapse();
+                }
+                return true;
+            }
+            return super.dispatchKeyEvent(event);
+        }
+    }
+
+    @Override
+    public void start() {
+        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
+                .getDefaultDisplay();
+
+        super.start();
+
+        addIntruderView();
+
+        // Lastly, call to the icon policy to install/update all the icons.
+        mIconPolicy = new StatusBarPolicy(mContext);
+    }
+
+    // ================================================================================
+    // Constructing the view
+    // ================================================================================
+    protected View makeStatusBarView() {
+        final Context context = mContext;
+
+        Resources res = context.getResources();
+
+        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+
+        ExpandedView expanded = (ExpandedView)View.inflate(context,
+                R.layout.status_bar_expanded, null);
+        expanded.mService = this;
+
+        mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null);
+        mIntruderAlertView.setVisibility(View.GONE);
+        mIntruderAlertView.setClickable(true);
+
+        StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
+        sb.mService = this;
+
+        // figure out which pixel-format to use for the status bar.
+        mPixelFormat = PixelFormat.TRANSLUCENT;
+        Drawable bg = sb.getBackground();
+        if (bg != null) {
+            mPixelFormat = bg.getOpacity();
+        }
+
+        mStatusBarView = sb;
+        mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
+        mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
+        mIcons = (LinearLayout)sb.findViewById(R.id.icons);
+        mTickerView = sb.findViewById(R.id.ticker);
+        mDateView = (DateView)sb.findViewById(R.id.date);
+
+        mExpandedDialog = new ExpandedDialog(context);
+        mExpandedView = expanded;
+        mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
+        mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
+        mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
+        mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
+        mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
+        mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
+        mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
+        mClearButton.setOnClickListener(mClearButtonListener);
+        mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
+        mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
+
+        mOngoingTitle.setVisibility(View.GONE);
+        mLatestTitle.setVisibility(View.GONE);
+
+        mTicker = new MyTicker(context, sb);
+
+        TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
+        tickerView.mTicker = mTicker;
+
+        mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null);
+        mTrackingView.mService = this;
+        mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
+        mCloseView.mService = this;
+
+        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
+        // set the inital view visibility
+        setAreThereNotifications();
+        mDateView.setVisibility(View.INVISIBLE);
+
+        // receive broadcasts
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        context.registerReceiver(mBroadcastReceiver, filter);
+
+        return sb;
+    }
+
+    protected int getStatusBarGravity() {
+        return Gravity.TOP | Gravity.FILL_HORIZONTAL;
+    }
+
+    private void addIntruderView() {
+        final Resources res = mContext.getResources();
+        final int height= res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                PixelFormat.TRANSLUCENT);
+        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        lp.y += height * 1.5; // FIXME
+        lp.setTitle("IntruderAlert");
+        lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar_IntruderAlert;
+
+        WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp);
+    }
+
+    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
+        if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+                + " icon=" + icon);
+        StatusBarIconView view = new StatusBarIconView(mContext, slot);
+        view.set(icon);
+        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
+    }
+
+    public void updateIcon(String slot, int index, int viewIndex,
+            StatusBarIcon old, StatusBarIcon icon) {
+        if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
+                + " old=" + old + " icon=" + icon);
+        StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
+        view.set(icon);
+    }
+
+    public void removeIcon(String slot, int index, int viewIndex) {
+        if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
+        mStatusIcons.removeViewAt(viewIndex);
+    }
+
+    public void addNotification(IBinder key, StatusBarNotification notification) {
+        StatusBarIconView iconView = addNotificationViews(key, notification);
+        if (iconView == null) return;
+
+        boolean immersive = false;
+        try {
+            immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
+            Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
+        } catch (RemoteException ex) {
+        }
+        if (immersive) {
+            if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
+                Slog.d(TAG, "Presenting high-priority notification in immersive activity");
+                // special new transient ticker mode
+                // 1. Populate mIntruderAlertView
+
+                ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon);
+                TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText);
+                alertIcon.setImageDrawable(StatusBarIconView.getIcon(
+                    alertIcon.getContext(),
+                    iconView.getStatusBarIcon()));
+                alertText.setText(notification.notification.tickerText);
+
+                View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content);
+                button.setOnClickListener(
+                    new Launcher(notification.notification.contentIntent,
+                        notification.pkg, notification.tag, notification.id));
+
+                // 2. Animate mIntruderAlertView in
+                mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
+
+                // 3. Set alarm to age the notification off (TODO)
+                mHandler.removeMessages(MSG_HIDE_INTRUDER);
+                mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
+            }
+        } else if (notification.notification.fullScreenIntent != null) {
+            // not immersive & a full-screen alert should be shown
+            Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;"
+                    + " sending fullScreenIntent");
+            try {
+                notification.notification.fullScreenIntent.send();
+            } catch (PendingIntent.CanceledException e) {
+            }
+        } else {
+            // usual case: status bar visible & not immersive
+
+            // show the ticker
+            tick(notification);
+        }
+
+        // Recalculate the position of the sliding windows and the titles.
+        setAreThereNotifications();
+        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+    }
+
+    public void updateNotification(IBinder key, StatusBarNotification notification) {
+        Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification);
+
+        NotificationData oldList;
+        NotificationData.Entry oldEntry = mOngoing.findByKey(key);
+        if (oldEntry != null) {
+            oldList = mOngoing;
+        } else {
+            oldEntry = mLatest.findByKey(key);
+            if (oldEntry == null) {
+                Slog.w(TAG, "updateNotification for unknown key: " + key);
+                return;
+            }
+            oldList = mLatest;
+        }
+        final StatusBarNotification oldNotification = oldEntry.notification;
+        final RemoteViews oldContentView = oldNotification.notification.contentView;
+
+        final RemoteViews contentView = notification.notification.contentView;
+
+        if (false) {
+            Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
+                    + " ongoing=" + oldNotification.isOngoing()
+                    + " expanded=" + oldEntry.expanded
+                    + " contentView=" + oldContentView);
+            Slog.d(TAG, "new notification: when=" + notification.notification.when
+                    + " ongoing=" + oldNotification.isOngoing()
+                    + " contentView=" + contentView);
+        }
+
+        // Can we just reapply the RemoteViews in place?  If when didn't change, the order
+        // didn't change.
+        if (notification.notification.when == oldNotification.notification.when
+                && notification.isOngoing() == oldNotification.isOngoing()
+                && oldEntry.expanded != null
+                && contentView != null && oldContentView != null
+                && contentView.getPackage() != null
+                && oldContentView.getPackage() != null
+                && oldContentView.getPackage().equals(contentView.getPackage())
+                && oldContentView.getLayoutId() == contentView.getLayoutId()) {
+            if (SPEW) Slog.d(TAG, "reusing notification");
+            oldEntry.notification = notification;
+            try {
+                // Reapply the RemoteViews
+                contentView.reapply(mContext, oldEntry.content);
+                // update the contentIntent
+                final PendingIntent contentIntent = notification.notification.contentIntent;
+                if (contentIntent != null) {
+                    oldEntry.content.setOnClickListener(new Launcher(contentIntent,
+                                notification.pkg, notification.tag, notification.id));
+                } else {
+                    oldEntry.content.setOnClickListener(null);
+                }
+                // Update the icon.
+                final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
+                        notification.notification.icon, notification.notification.iconLevel,
+                        notification.notification.number);
+                if (!oldEntry.icon.set(ic)) {
+                    handleNotificationError(key, notification, "Couldn't update icon: " + ic);
+                    return;
+                }
+            }
+            catch (RuntimeException e) {
+                // It failed to add cleanly.  Log, and remove the view from the panel.
+                Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
+                removeNotificationViews(key);
+                addNotificationViews(key, notification);
+            }
+        } else {
+            if (SPEW) Slog.d(TAG, "not reusing notification");
+            removeNotificationViews(key);
+            addNotificationViews(key, notification);
+        }
+
+        // Restart the ticker if it's still running
+        if (notification.notification.tickerText != null
+                && !TextUtils.equals(notification.notification.tickerText,
+                    oldEntry.notification.notification.tickerText)) {
+            tick(notification);
+        }
+
+        // Recalculate the position of the sliding windows and the titles.
+        setAreThereNotifications();
+        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+    }
+
+    public void removeNotification(IBinder key) {
+        if (SPEW) Slog.d(TAG, "removeNotification key=" + key);
+        StatusBarNotification old = removeNotificationViews(key);
+
+        if (old != null) {
+            // Cancel the ticker if it's still running
+            mTicker.removeEntry(old);
+
+            // Recalculate the position of the sliding windows and the titles.
+            setAreThereNotifications();
+            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+        }
+    }
+
+    private int chooseIconIndex(boolean isOngoing, int viewIndex) {
+        final int latestSize = mLatest.size();
+        if (isOngoing) {
+            return latestSize + (mOngoing.size() - viewIndex);
+        } else {
+            return latestSize - viewIndex;
+        }
+    }
+
+    View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
+        Notification n = notification.notification;
+        RemoteViews remoteViews = n.contentView;
+        if (remoteViews == null) {
+            return null;
+        }
+
+        // create the row view
+        LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+        View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
+
+        // bind the click event to the content area
+        ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
+        content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+        content.setOnFocusChangeListener(mFocusChangeListener);
+        PendingIntent contentIntent = n.contentIntent;
+        if (contentIntent != null) {
+            content.setOnClickListener(new Launcher(contentIntent, notification.pkg,
+                        notification.tag, notification.id));
+        } else {
+            content.setOnClickListener(null);
+        }
+
+        View expanded = null;
+        Exception exception = null;
+        try {
+            expanded = remoteViews.apply(mContext, content);
+        }
+        catch (RuntimeException e) {
+            exception = e;
+        }
+        if (expanded == null) {
+            String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id);
+            Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
+            return null;
+        } else {
+            content.addView(expanded);
+            row.setDrawingCacheEnabled(true);
+        }
+
+        return new View[] { row, content, expanded };
+    }
+
+    StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
+        NotificationData list;
+        ViewGroup parent;
+        final boolean isOngoing = notification.isOngoing();
+        if (isOngoing) {
+            list = mOngoing;
+            parent = mOngoingItems;
+        } else {
+            list = mLatest;
+            parent = mLatestItems;
+        }
+        // Construct the expanded view.
+        final View[] views = makeNotificationView(notification, parent);
+        if (views == null) {
+            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+                    + notification);
+            return null;
+        }
+        final View row = views[0];
+        final View content = views[1];
+        final View expanded = views[2];
+        // Construct the icon.
+        final StatusBarIconView iconView = new StatusBarIconView(mContext,
+                notification.pkg + "/0x" + Integer.toHexString(notification.id));
+        final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
+                    notification.notification.iconLevel, notification.notification.number);
+        if (!iconView.set(ic)) {
+            handleNotificationError(key, notification, "Coulding create icon: " + ic);
+            return null;
+        }
+        // Add the expanded view.
+        final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
+        parent.addView(row, viewIndex);
+        // Add the icon.
+        final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
+        mNotificationIcons.addView(iconView, iconIndex);
+        return iconView;
+    }
+
+    StatusBarNotification removeNotificationViews(IBinder key) {
+        NotificationData.Entry entry = mOngoing.remove(key);
+        if (entry == null) {
+            entry = mLatest.remove(key);
+            if (entry == null) {
+                Slog.w(TAG, "removeNotification for unknown key: " + key);
+                return null;
+            }
+        }
+        // Remove the expanded view.
+        ((ViewGroup)entry.row.getParent()).removeView(entry.row);
+        // Remove the icon.
+        ((ViewGroup)entry.icon.getParent()).removeView(entry.icon);
+
+        return entry.notification;
+    }
+
+    private void setAreThereNotifications() {
+        boolean ongoing = mOngoing.hasVisibleItems();
+        boolean latest = mLatest.hasVisibleItems();
+
+        // (no ongoing notifications are clearable)
+        if (mLatest.hasClearableItems()) {
+            mClearButton.setVisibility(View.VISIBLE);
+        } else {
+            mClearButton.setVisibility(View.INVISIBLE);
+        }
+
+        mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
+        mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
+
+        if (ongoing || latest) {
+            mNoNotificationsTitle.setVisibility(View.GONE);
+        } else {
+            mNoNotificationsTitle.setVisibility(View.VISIBLE);
+        }
+    }
+
+
+    /**
+     * State is one or more of the DISABLE constants from StatusBarManager.
+     */
+    public void disable(int state) {
+        final int old = mDisabled;
+        final int diff = state ^ old;
+        mDisabled = state;
+
+        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
+                Slog.d(TAG, "DISABLE_EXPAND: yes");
+                animateCollapse();
+            }
+        }
+        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+                if (mTicking) {
+                    mTicker.halt();
+                } else {
+                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
+                }
+            } else {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+                if (!mExpandedVisible) {
+                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+                }
+            }
+        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+            if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
+                mTicker.halt();
+            }
+        }
+    }
+
+    /**
+     * All changes to the status bar and notifications funnel through here and are batched.
+     */
+    private class H extends Handler {
+        public void handleMessage(Message m) {
+            switch (m.what) {
+                case MSG_ANIMATE:
+                    doAnimation();
+                    break;
+                case MSG_ANIMATE_REVEAL:
+                    doRevealAnimation();
+                    break;
+                case MSG_SHOW_INTRUDER:
+                    setIntruderAlertVisibility(true);
+                    break;
+                case MSG_HIDE_INTRUDER:
+                    setIntruderAlertVisibility(false);
+                    break;
+            }
+        }
+    }
+
+    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
+        public void onFocusChange(View v, boolean hasFocus) {
+            // Because 'v' is a ViewGroup, all its children will be (un)selected
+            // too, which allows marqueeing to work.
+            v.setSelected(hasFocus);
+        }
+    };
+
+    private void makeExpandedVisible() {
+        if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
+        if (mExpandedVisible) {
+            return;
+        }
+        mExpandedVisible = true;
+        visibilityChanged(true);
+
+        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+        mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+        mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+        mExpandedView.requestFocus(View.FOCUS_FORWARD);
+        mTrackingView.setVisibility(View.VISIBLE);
+
+        if (!mTicking) {
+            setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+        }
+    }
+
+    public void animateExpand() {
+        if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+            return ;
+        }
+        if (mExpanded) {
+            return;
+        }
+
+        prepareTracking(0, true);
+        performFling(0, 2000.0f, true);
+    }
+
+    public void animateCollapse() {
+        if (SPEW) {
+            Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
+                    + " mExpandedVisible=" + mExpandedVisible
+                    + " mExpanded=" + mExpanded
+                    + " mAnimating=" + mAnimating
+                    + " mAnimY=" + mAnimY
+                    + " mAnimVel=" + mAnimVel);
+        }
+
+        if (!mExpandedVisible) {
+            return;
+        }
+
+        int y;
+        if (mAnimating) {
+            y = (int)mAnimY;
+        } else {
+            y = mDisplay.getHeight()-1;
+        }
+        // Let the fling think that we're open so it goes in the right direction
+        // and doesn't try to re-open the windowshade.
+        mExpanded = true;
+        prepareTracking(y, false);
+        performFling(y, -2000.0f, true);
+    }
+
+    void performExpand() {
+        if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+            return ;
+        }
+        if (mExpanded) {
+            return;
+        }
+
+        mExpanded = true;
+        makeExpandedVisible();
+        updateExpandedViewPos(EXPANDED_FULL_OPEN);
+
+        if (false) postStartTracing();
+    }
+
+    void performCollapse() {
+        if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
+                + " mExpandedVisible=" + mExpandedVisible);
+
+        if (!mExpandedVisible) {
+            return;
+        }
+        mExpandedVisible = false;
+        visibilityChanged(false);
+        mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+        mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+        mTrackingView.setVisibility(View.GONE);
+
+        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
+            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+        }
+        setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
+
+        if (!mExpanded) {
+            return;
+        }
+        mExpanded = false;
+    }
+
+    void doAnimation() {
+        if (mAnimating) {
+            if (SPEW) Slog.d(TAG, "doAnimation");
+            if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
+            incrementAnim();
+            if (SPEW) Slog.d(TAG, "doAnimation after  mAnimY=" + mAnimY);
+            if (mAnimY >= mDisplay.getHeight()-1) {
+                if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
+                mAnimating = false;
+                updateExpandedViewPos(EXPANDED_FULL_OPEN);
+                performExpand();
+            }
+            else if (mAnimY < mStatusBarView.getHeight()) {
+                if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
+                mAnimating = false;
+                updateExpandedViewPos(0);
+                performCollapse();
+            }
+            else {
+                updateExpandedViewPos((int)mAnimY);
+                mCurAnimationTime += ANIM_FRAME_DURATION;
+                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+            }
+        }
+    }
+
+    void stopTracking() {
+        mTracking = false;
+        mVelocityTracker.recycle();
+        mVelocityTracker = null;
+    }
+
+    void incrementAnim() {
+        long now = SystemClock.uptimeMillis();
+        float t = ((float)(now - mAnimLastTime)) / 1000;            // ms -> s
+        final float y = mAnimY;
+        final float v = mAnimVel;                                   // px/s
+        final float a = mAnimAccel;                                 // px/s/s
+        mAnimY = y + (v*t) + (0.5f*a*t*t);                          // px
+        mAnimVel = v + (a*t);                                       // px/s
+        mAnimLastTime = now;                                        // ms
+        //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
+        //        + " mAnimAccel=" + mAnimAccel);
+    }
+
+    void doRevealAnimation() {
+        final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
+        if (mAnimatingReveal && mAnimating && mAnimY < h) {
+            incrementAnim();
+            if (mAnimY >= h) {
+                mAnimY = h;
+                updateExpandedViewPos((int)mAnimY);
+            } else {
+                updateExpandedViewPos((int)mAnimY);
+                mCurAnimationTime += ANIM_FRAME_DURATION;
+                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+                        mCurAnimationTime);
+            }
+        }
+    }
+
+    void prepareTracking(int y, boolean opening) {
+        mTracking = true;
+        mVelocityTracker = VelocityTracker.obtain();
+        if (opening) {
+            mAnimAccel = 2000.0f;
+            mAnimVel = 200;
+            mAnimY = mStatusBarView.getHeight();
+            updateExpandedViewPos((int)mAnimY);
+            mAnimating = true;
+            mAnimatingReveal = true;
+            mHandler.removeMessages(MSG_ANIMATE);
+            mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+            long now = SystemClock.uptimeMillis();
+            mAnimLastTime = now;
+            mCurAnimationTime = now + ANIM_FRAME_DURATION;
+            mAnimating = true;
+            mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+                    mCurAnimationTime);
+            makeExpandedVisible();
+        } else {
+            // it's open, close it?
+            if (mAnimating) {
+                mAnimating = false;
+                mHandler.removeMessages(MSG_ANIMATE);
+            }
+            updateExpandedViewPos(y + mViewDelta);
+        }
+    }
+
+    void performFling(int y, float vel, boolean always) {
+        mAnimatingReveal = false;
+        mDisplayHeight = mDisplay.getHeight();
+
+        mAnimY = y;
+        mAnimVel = vel;
+
+        //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
+
+        if (mExpanded) {
+            if (!always && (
+                    vel > 200.0f
+                    || (y > (mDisplayHeight-25) && vel > -200.0f))) {
+                // We are expanded, but they didn't move sufficiently to cause
+                // us to retract.  Animate back to the expanded position.
+                mAnimAccel = 2000.0f;
+                if (vel < 0) {
+                    mAnimVel = 0;
+                }
+            }
+            else {
+                // We are expanded and are now going to animate away.
+                mAnimAccel = -2000.0f;
+                if (vel > 0) {
+                    mAnimVel = 0;
+                }
+            }
+        } else {
+            if (always || (
+                    vel > 200.0f
+                    || (y > (mDisplayHeight/2) && vel > -200.0f))) {
+                // We are collapsed, and they moved enough to allow us to
+                // expand.  Animate in the notifications.
+                mAnimAccel = 2000.0f;
+                if (vel < 0) {
+                    mAnimVel = 0;
+                }
+            }
+            else {
+                // We are collapsed, but they didn't move sufficiently to cause
+                // us to retract.  Animate back to the collapsed position.
+                mAnimAccel = -2000.0f;
+                if (vel > 0) {
+                    mAnimVel = 0;
+                }
+            }
+        }
+        //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
+        //        + " mAnimAccel=" + mAnimAccel);
+
+        long now = SystemClock.uptimeMillis();
+        mAnimLastTime = now;
+        mCurAnimationTime = now + ANIM_FRAME_DURATION;
+        mAnimating = true;
+        mHandler.removeMessages(MSG_ANIMATE);
+        mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+        mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+        stopTracking();
+    }
+
+    boolean interceptTouchEvent(MotionEvent event) {
+        if (SPEW) {
+            Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
+                + mDisabled);
+        }
+
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+            return false;
+        }
+
+        final int statusBarSize = mStatusBarView.getHeight();
+        final int hitSize = statusBarSize*2;
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            final int y = (int)event.getRawY();
+
+            if (!mExpanded) {
+                mViewDelta = statusBarSize - y;
+            } else {
+                mTrackingView.getLocationOnScreen(mAbsPos);
+                mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
+            }
+            if ((!mExpanded && y < hitSize) ||
+                    (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
+
+                // We drop events at the edge of the screen to make the windowshade come
+                // down by accident less, especially when pushing open a device with a keyboard
+                // that rotates (like g1 and droid)
+                int x = (int)event.getRawX();
+                final int edgeBorder = mEdgeBorder;
+                if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
+                    prepareTracking(y, !mExpanded);// opening if we're not already fully visible
+                    mVelocityTracker.addMovement(event);
+                }
+            }
+        } else if (mTracking) {
+            mVelocityTracker.addMovement(event);
+            final int minY = statusBarSize + mCloseView.getHeight();
+            if (event.getAction() == MotionEvent.ACTION_MOVE) {
+                int y = (int)event.getRawY();
+                if (mAnimatingReveal && y < minY) {
+                    // nothing
+                } else  {
+                    mAnimatingReveal = false;
+                    updateExpandedViewPos(y + mViewDelta);
+                }
+            } else if (event.getAction() == MotionEvent.ACTION_UP) {
+                mVelocityTracker.computeCurrentVelocity(1000);
+
+                float yVel = mVelocityTracker.getYVelocity();
+                boolean negative = yVel < 0;
+
+                float xVel = mVelocityTracker.getXVelocity();
+                if (xVel < 0) {
+                    xVel = -xVel;
+                }
+                if (xVel > 150.0f) {
+                    xVel = 150.0f; // limit how much we care about the x axis
+                }
+
+                float vel = (float)Math.hypot(yVel, xVel);
+                if (negative) {
+                    vel = -vel;
+                }
+
+                performFling((int)event.getRawY(), vel, false);
+            }
+
+        }
+        return false;
+    }
+
+    public void setLightsOn(boolean on) {
+        if (!on) {
+            // All we do for "lights out" mode on a phone is hide the status bar,
+            // which the window manager does.  But we do need to hide the windowshade
+            // on our own.
+            animateCollapse();
+        }
+    }
+
+    // Not supported
+    public void setMenuKeyVisible(boolean visible) { }
+    public void setIMEButtonVisible(boolean visible) { }
+
+    private class Launcher implements View.OnClickListener {
+        private PendingIntent mIntent;
+        private String mPkg;
+        private String mTag;
+        private int mId;
+
+        Launcher(PendingIntent intent, String pkg, String tag, int id) {
+            mIntent = intent;
+            mPkg = pkg;
+            mTag = tag;
+            mId = id;
+        }
+
+        public void onClick(View v) {
+            try {
+                // The intent we are sending is for the application, which
+                // won't have permission to immediately start an activity after
+                // the user switches to home.  We know it is safe to do at this
+                // point, so make sure new activity switches are now allowed.
+                ActivityManagerNative.getDefault().resumeAppSwitches();
+            } catch (RemoteException e) {
+            }
+
+            if (mIntent != null) {
+                int[] pos = new int[2];
+                v.getLocationOnScreen(pos);
+                Intent overlay = new Intent();
+                overlay.setSourceBounds(
+                        new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+                try {
+                    mIntent.send(mContext, 0, overlay);
+                } catch (PendingIntent.CanceledException e) {
+                    // the stack trace isn't very helpful here.  Just log the exception message.
+                    Slog.w(TAG, "Sending contentIntent failed: " + e);
+                }
+            }
+
+            try {
+                mBarService.onNotificationClick(mPkg, mTag, mId);
+            } catch (RemoteException ex) {
+                // system process is dead if we're here.
+            }
+
+            // close the shade if it was open
+            animateCollapse();
+
+            // If this click was on the intruder alert, hide that instead
+            mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
+        }
+    }
+
+    private void tick(StatusBarNotification n) {
+        // Show the ticker if one is requested. Also don't do this
+        // until status bar window is attached to the window manager,
+        // because...  well, what's the point otherwise?  And trying to
+        // run a ticker without being attached will crash!
+        if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
+            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
+                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
+                mTicker.addEntry(n);
+            }
+        }
+    }
+
+    /**
+     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+     * about the failure.
+     *
+     * WARNING: this will call back into us.  Don't hold any locks.
+     */
+    void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
+        removeNotification(key);
+        try {
+            mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
+        } catch (RemoteException ex) {
+            // The end is nigh.
+        }
+    }
+
+    private class MyTicker extends Ticker {
+        MyTicker(Context context, View sb) {
+            super(context, sb);
+        }
+
+        @Override
+        public void tickerStarting() {
+            mTicking = true;
+            mIcons.setVisibility(View.GONE);
+            mTickerView.setVisibility(View.VISIBLE);
+            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
+            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
+            if (mExpandedVisible) {
+                setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
+            }
+        }
+
+        @Override
+        public void tickerDone() {
+            mIcons.setVisibility(View.VISIBLE);
+            mTickerView.setVisibility(View.GONE);
+            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
+            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
+                        mTickingDoneListener));
+            if (mExpandedVisible) {
+                setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
+            }
+        }
+
+        public void tickerHalting() {
+            mIcons.setVisibility(View.VISIBLE);
+            mTickerView.setVisibility(View.GONE);
+            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
+            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
+                        mTickingDoneListener));
+            if (mExpandedVisible) {
+                setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+            }
+        }
+    }
+
+    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
+        public void onAnimationEnd(Animation animation) {
+            mTicking = false;
+        }
+        public void onAnimationRepeat(Animation animation) {
+        }
+        public void onAnimationStart(Animation animation) {
+        }
+    };
+
+    private Animation loadAnim(int id, Animation.AnimationListener listener) {
+        Animation anim = AnimationUtils.loadAnimation(mContext, id);
+        if (listener != null) {
+            anim.setAnimationListener(listener);
+        }
+        return anim;
+    }
+
+    public String viewInfo(View v) {
+        return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
+                + " " + v.getWidth() + "x" + v.getHeight() + ")";
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        synchronized (mQueueLock) {
+            pw.println("Current Status Bar state:");
+            pw.println("  mExpanded=" + mExpanded
+                    + ", mExpandedVisible=" + mExpandedVisible);
+            pw.println("  mTicking=" + mTicking);
+            pw.println("  mTracking=" + mTracking);
+            pw.println("  mAnimating=" + mAnimating
+                    + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
+                    + ", mAnimAccel=" + mAnimAccel);
+            pw.println("  mCurAnimationTime=" + mCurAnimationTime
+                    + " mAnimLastTime=" + mAnimLastTime);
+            pw.println("  mDisplayHeight=" + mDisplayHeight
+                    + " mAnimatingReveal=" + mAnimatingReveal
+                    + " mViewDelta=" + mViewDelta);
+            pw.println("  mDisplayHeight=" + mDisplayHeight);
+            pw.println("  mExpandedParams: " + mExpandedParams);
+            pw.println("  mExpandedView: " + viewInfo(mExpandedView));
+            pw.println("  mExpandedDialog: " + mExpandedDialog);
+            pw.println("  mTrackingParams: " + mTrackingParams);
+            pw.println("  mTrackingView: " + viewInfo(mTrackingView));
+            pw.println("  mOngoingTitle: " + viewInfo(mOngoingTitle));
+            pw.println("  mOngoingItems: " + viewInfo(mOngoingItems));
+            pw.println("  mLatestTitle: " + viewInfo(mLatestTitle));
+            pw.println("  mLatestItems: " + viewInfo(mLatestItems));
+            pw.println("  mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
+            pw.println("  mCloseView: " + viewInfo(mCloseView));
+            pw.println("  mTickerView: " + viewInfo(mTickerView));
+            pw.println("  mScrollView: " + viewInfo(mScrollView)
+                    + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
+            pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
+        }
+        /*
+        synchronized (mNotificationData) {
+            int N = mNotificationData.ongoingCount();
+            pw.println("  ongoingCount.size=" + N);
+            for (int i=0; i<N; i++) {
+                StatusBarNotification n = mNotificationData.getOngoing(i);
+                pw.println("    [" + i + "] key=" + n.key + " view=" + n.view);
+                pw.println("           data=" + n.data);
+            }
+            N = mNotificationData.latestCount();
+            pw.println("  ongoingCount.size=" + N);
+            for (int i=0; i<N; i++) {
+                StatusBarNotification n = mNotificationData.getLatest(i);
+                pw.println("    [" + i + "] key=" + n.key + " view=" + n.view);
+                pw.println("           data=" + n.data);
+            }
+        }
+        */
+
+        if (false) {
+            pw.println("see the logcat for a dump of the views we have created.");
+            // must happen on ui thread
+            mHandler.post(new Runnable() {
+                    public void run() {
+                        mStatusBarView.getLocationOnScreen(mAbsPos);
+                        Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+                                + ") " + mStatusBarView.getWidth() + "x"
+                                + mStatusBarView.getHeight());
+                        mStatusBarView.debug();
+
+                        mExpandedView.getLocationOnScreen(mAbsPos);
+                        Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+                                + ") " + mExpandedView.getWidth() + "x"
+                                + mExpandedView.getHeight());
+                        mExpandedView.debug();
+
+                        mTrackingView.getLocationOnScreen(mAbsPos);
+                        Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+                                + ") " + mTrackingView.getWidth() + "x"
+                                + mTrackingView.getHeight());
+                        mTrackingView.debug();
+                    }
+                });
+        }
+    }
+
+    void onBarViewAttached() {
+        WindowManager.LayoutParams lp;
+        int pixelFormat;
+        Drawable bg;
+
+        /// ---------- Tracking View --------------
+        pixelFormat = PixelFormat.RGBX_8888;
+        bg = mTrackingView.getBackground();
+        if (bg != null) {
+            pixelFormat = bg.getOpacity();
+        }
+
+        lp = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                pixelFormat);
+//        lp.token = mStatusBarView.getWindowToken();
+        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        lp.setTitle("TrackingView");
+        lp.y = mTrackingPosition;
+        mTrackingParams = lp;
+
+        WindowManagerImpl.getDefault().addView(mTrackingView, lp);
+    }
+
+    void onTrackingViewAttached() {
+        WindowManager.LayoutParams lp;
+        int pixelFormat;
+        Drawable bg;
+
+        /// ---------- Expanded View --------------
+        pixelFormat = PixelFormat.TRANSLUCENT;
+
+        final int disph = mDisplay.getHeight();
+        lp = mExpandedDialog.getWindow().getAttributes();
+        lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
+        lp.height = getExpandedHeight();
+        lp.x = 0;
+        mTrackingPosition = lp.y = -disph; // sufficiently large negative
+        lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+        lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | WindowManager.LayoutParams.FLAG_DITHER
+                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        lp.format = pixelFormat;
+        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        lp.setTitle("StatusBarExpanded");
+        mExpandedDialog.getWindow().setAttributes(lp);
+        mExpandedDialog.getWindow().setFormat(pixelFormat);
+        mExpandedParams = lp;
+
+        mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+        mExpandedDialog.setContentView(mExpandedView,
+                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                                           ViewGroup.LayoutParams.MATCH_PARENT));
+        mExpandedDialog.getWindow().setBackgroundDrawable(null);
+        mExpandedDialog.show();
+        FrameLayout hack = (FrameLayout)mExpandedView.getParent();
+    }
+
+    void setDateViewVisibility(boolean visible, int anim) {
+        mDateView.setUpdates(visible);
+        mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+        mDateView.startAnimation(loadAnim(anim, null));
+    }
+
+    void setNotificationIconVisibility(boolean visible, int anim) {
+        int old = mNotificationIcons.getVisibility();
+        int v = visible ? View.VISIBLE : View.INVISIBLE;
+        if (old != v) {
+            mNotificationIcons.setVisibility(v);
+            mNotificationIcons.startAnimation(loadAnim(anim, null));
+        }
+    }
+
+    void updateExpandedViewPos(int expandedPosition) {
+        if (SPEW) {
+            Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
+                    + " mTrackingParams.y=" + mTrackingParams.y
+                    + " mTrackingPosition=" + mTrackingPosition);
+        }
+
+        int h = mStatusBarView.getHeight();
+        int disph = mDisplay.getHeight();
+
+        // If the expanded view is not visible, make sure they're still off screen.
+        // Maybe the view was resized.
+        if (!mExpandedVisible) {
+            if (mTrackingView != null) {
+                mTrackingPosition = -disph;
+                if (mTrackingParams != null) {
+                    mTrackingParams.y = mTrackingPosition;
+                    WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+                }
+            }
+            if (mExpandedParams != null) {
+                mExpandedParams.y = -disph;
+                mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+            }
+            return;
+        }
+
+        // tracking view...
+        int pos;
+        if (expandedPosition == EXPANDED_FULL_OPEN) {
+            pos = h;
+        }
+        else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
+            pos = mTrackingPosition;
+        }
+        else {
+            if (expandedPosition <= disph) {
+                pos = expandedPosition;
+            } else {
+                pos = disph;
+            }
+            pos -= disph-h;
+        }
+        mTrackingPosition = mTrackingParams.y = pos;
+        mTrackingParams.height = disph-h;
+        WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+
+        if (mExpandedParams != null) {
+            mCloseView.getLocationInWindow(mPositionTmp);
+            final int closePos = mPositionTmp[1];
+
+            mExpandedContents.getLocationInWindow(mPositionTmp);
+            final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
+
+            mExpandedParams.y = pos + mTrackingView.getHeight()
+                    - (mTrackingParams.height-closePos) - contentsBottom;
+            int max = h;
+            if (mExpandedParams.y > max) {
+                mExpandedParams.y = max;
+            }
+            int min = mTrackingPosition;
+            if (mExpandedParams.y < min) {
+                mExpandedParams.y = min;
+            }
+
+            boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
+            if (!visible) {
+                // if the contents aren't visible, move the expanded view way off screen
+                // because the window itself extends below the content view.
+                mExpandedParams.y = -disph;
+            }
+            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+
+            // As long as this isn't just a repositioning that's not supposed to affect
+            // the user's perception of what's showing, call to say that the visibility
+            // has changed. (Otherwise, someone else will call to do that).
+            if (expandedPosition != EXPANDED_LEAVE_ALONE) {
+                if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
+                visibilityChanged(visible);
+            }
+        }
+
+        if (SPEW) {
+            Slog.d(TAG, "updateExpandedViewPos after  expandedPosition=" + expandedPosition
+                    + " mTrackingParams.y=" + mTrackingParams.y
+                    + " mTrackingPosition=" + mTrackingPosition
+                    + " mExpandedParams.y=" + mExpandedParams.y
+                    + " mExpandedParams.height=" + mExpandedParams.height);
+        }
+    }
+
+    int getExpandedHeight() {
+        return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
+    }
+
+    void updateExpandedHeight() {
+        if (mExpandedView != null) {
+            mExpandedParams.height = getExpandedHeight();
+            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+        }
+    }
+
+    /**
+     * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
+     * This was added last-minute and is inconsistent with the way the rest of the notifications
+     * are handled, because the notification isn't really cancelled.  The lights are just
+     * turned off.  If any other notifications happen, the lights will turn back on.  Steve says
+     * this is what he wants. (see bug 1131461)
+     */
+    void visibilityChanged(boolean visible) {
+        if (mPanelSlightlyVisible != visible) {
+            mPanelSlightlyVisible = visible;
+            try {
+                mBarService.onPanelRevealed();
+            } catch (RemoteException ex) {
+                // Won't fail unless the world has ended.
+            }
+        }
+    }
+
+    void performDisableActions(int net) {
+        int old = mDisabled;
+        int diff = net ^ old;
+        mDisabled = net;
+
+        // act accordingly
+        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+            if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
+                Slog.d(TAG, "DISABLE_EXPAND: yes");
+                animateCollapse();
+            }
+        }
+        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+            if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+                if (mTicking) {
+                    mNotificationIcons.setVisibility(View.INVISIBLE);
+                    mTicker.halt();
+                } else {
+                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
+                }
+            } else {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+                if (!mExpandedVisible) {
+                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+                }
+            }
+        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+                mTicker.halt();
+            }
+        }
+    }
+
+    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            try {
+                mBarService.onClearAllNotifications();
+            } catch (RemoteException ex) {
+                // system process is dead if we're here.
+            }
+            animateCollapse();
+        }
+    };
+
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+                    || Intent.ACTION_SCREEN_OFF.equals(action)) {
+                animateCollapse();
+            }
+            else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+                updateResources();
+            }
+        }
+    };
+
+    private void setIntruderAlertVisibility(boolean vis) {
+        mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
+    }
+
+    /**
+     * Reload some of our resources when the configuration changes.
+     *
+     * We don't reload everything when the configuration changes -- we probably
+     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
+     * meantime, just update the things that we know change.
+     */
+    void updateResources() {
+        final Context context = mContext;
+        final Resources res = context.getResources();
+
+        mClearButton.setText(context.getText(R.string.status_bar_clear_all_button));
+        mOngoingTitle.setText(context.getText(R.string.status_bar_ongoing_events_title));
+        mLatestTitle.setText(context.getText(R.string.status_bar_latest_events_title));
+        mNoNotificationsTitle.setText(context.getText(R.string.status_bar_no_notifications_title));
+
+        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
+        if (false) Slog.v(TAG, "updateResources");
+    }
+
+    //
+    // tracing
+    //
+
+    void postStartTracing() {
+        mHandler.postDelayed(mStartTracing, 3000);
+    }
+
+    void vibrate() {
+        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
+                Context.VIBRATOR_SERVICE);
+        vib.vibrate(250);
+    }
+
+    Runnable mStartTracing = new Runnable() {
+        public void run() {
+            vibrate();
+            SystemClock.sleep(250);
+            Slog.d(TAG, "startTracing");
+            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
+            mHandler.postDelayed(mStopTracing, 10000);
+        }
+    };
+
+    Runnable mStopTracing = new Runnable() {
+        public void run() {
+            android.os.Debug.stopMethodTracing();
+            Slog.d(TAG, "stopTracing");
+            vibrate();
+        }
+    };
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index 9ef6e6f..4f5c3ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -17,15 +17,11 @@
 package com.android.systemui.statusbar.policy;
 
 import android.app.StatusBarManager;
-import android.app.AlertDialog;
-import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothPbap;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.TypedArray;
@@ -34,8 +30,6 @@
 import android.graphics.drawable.Drawable;
 import android.location.LocationManager;
 import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
@@ -60,11 +54,8 @@
 import android.util.Slog;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.telephony.IccCard;
@@ -96,24 +87,12 @@
 
     private final Context mContext;
     private final StatusBarManager mService;
-    private final Handler mHandler = new StatusBarHandler();
+    private final Handler mHandler = new Handler();
     private final IBatteryStats mBatteryStats;
 
     // storage
     private StorageManager mStorageManager;
 
-    // battery
-    private boolean mBatteryFirst = true;
-    private boolean mBatteryPlugged;
-    private int mBatteryLevel;
-    private AlertDialog mLowBatteryDialog;
-    private TextView mBatteryLevelTextView;
-    private View mBatteryView;
-    private int mBatteryViewSequence;
-    private boolean mBatteryShowLowOnEndCall = false;
-    private static final boolean SHOW_LOW_BATTERY_WARNING = true;
-    private static final boolean SHOW_BATTERY_WARNINGS_IN_CALL = true;
-
     // phone
     private TelephonyManager mPhone;
     private int mPhoneSignalIconId;
@@ -319,9 +298,6 @@
     private boolean mVolumeVisible;
 
     // bluetooth device status
-    private int mBluetoothHeadsetState;
-    private boolean mBluetoothA2dpConnected;
-    private int mBluetoothPbapState;
     private boolean mBluetoothEnabled;
 
     // wifi
@@ -361,17 +337,8 @@
             else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
                 updateSyncState(intent);
             }
-            else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
-                onBatteryLow(intent);
-            }
-            else if (action.equals(Intent.ACTION_BATTERY_OKAY)
-                    || action.equals(Intent.ACTION_POWER_CONNECTED)) {
-                onBatteryOkay(intent);
-            }
             else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
-                    action.equals(BluetoothHeadset.ACTION_STATE_CHANGED) ||
-                    action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED) ||
-                    action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
+                    action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
                 updateBluetooth(intent);
             }
             else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) ||
@@ -454,9 +421,6 @@
         } else {
             mBluetoothEnabled = false;
         }
-        mBluetoothA2dpConnected = false;
-        mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED;
-        mBluetoothPbapState = BluetoothPbap.STATE_DISCONNECTED;
         mService.setIconVisibility("bluetooth", mBluetoothEnabled);
 
         // Gps status
@@ -482,17 +446,12 @@
 
         // Register for Intent broadcasts for...
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
-        filter.addAction(Intent.ACTION_BATTERY_LOW);
-        filter.addAction(Intent.ACTION_BATTERY_OKAY);
-        filter.addAction(Intent.ACTION_POWER_CONNECTED);
         filter.addAction(Intent.ACTION_ALARM_CHANGED);
         filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
         filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
-        filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
-        filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
-        filter.addAction(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
+        filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
@@ -531,179 +490,6 @@
         final int id = intent.getIntExtra("icon-small", 0);
         int level = intent.getIntExtra("level", 0);
         mService.setIcon("battery", id, level);
-
-        boolean plugged = intent.getIntExtra("plugged", 0) != 0;
-        level = intent.getIntExtra("level", -1);
-        if (false) {
-            Slog.d(TAG, "updateBattery level=" + level
-                    + " plugged=" + plugged
-                    + " mBatteryPlugged=" + mBatteryPlugged
-                    + " mBatteryLevel=" + mBatteryLevel
-                    + " mBatteryFirst=" + mBatteryFirst);
-        }
-
-        boolean oldPlugged = mBatteryPlugged;
-
-        mBatteryPlugged = plugged;
-        mBatteryLevel = level;
-
-        if (mBatteryFirst) {
-            mBatteryFirst = false;
-        }
-        /*
-         * No longer showing the battery view because it draws attention away
-         * from the USB storage notification. We could still show it when
-         * connected to a brick, but that could lead to the user into thinking
-         * the device does not charge when plugged into USB (since he/she would
-         * not see the same battery screen on USB as he sees on brick).
-         */
-        if (false) {
-            Slog.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
-        }
-    }
-
-    private void onBatteryLow(Intent intent) {
-        if (SHOW_LOW_BATTERY_WARNING) {
-            if (false) {
-                Slog.d(TAG, "mPhoneState=" + mPhoneState
-                      + " mLowBatteryDialog=" + mLowBatteryDialog
-                      + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
-            }
-
-            if (SHOW_BATTERY_WARNINGS_IN_CALL || mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
-                showLowBatteryWarning();
-            } else {
-                mBatteryShowLowOnEndCall = true;
-            }
-        }
-    }
-
-    private void onBatteryOkay(Intent intent) {
-        if (mLowBatteryDialog != null
-                && SHOW_LOW_BATTERY_WARNING) {
-            mLowBatteryDialog.dismiss();
-            mBatteryShowLowOnEndCall = false;
-        }
-    }
-
-    private void setBatteryLevel(View parent, int id, int height, int background, int level) {
-        ImageView v = (ImageView)parent.findViewById(id);
-        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)v.getLayoutParams();
-        lp.weight = height;
-        if (background != 0) {
-            v.setBackgroundResource(background);
-            Drawable bkg = v.getBackground();
-            bkg.setLevel(level);
-        }
-    }
-
-    private void showLowBatteryWarning() {
-        closeLastBatteryView();
-
-        // Show exact battery level.
-        CharSequence levelText = mContext.getString(
-                    R.string.battery_low_percent_format, mBatteryLevel);
-
-        if (mBatteryLevelTextView != null) {
-            mBatteryLevelTextView.setText(levelText);
-        } else {
-            View v = View.inflate(mContext, R.layout.battery_low, null);
-            mBatteryLevelTextView=(TextView)v.findViewById(R.id.level_percent);
-
-            mBatteryLevelTextView.setText(levelText);
-
-            AlertDialog.Builder b = new AlertDialog.Builder(mContext);
-                b.setCancelable(true);
-                b.setTitle(R.string.battery_low_title);
-                b.setView(v);
-                b.setIcon(android.R.drawable.ic_dialog_alert);
-                b.setPositiveButton(android.R.string.ok, null);
-
-                final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                        | Intent.FLAG_ACTIVITY_NO_HISTORY);
-                if (intent.resolveActivity(mContext.getPackageManager()) != null) {
-                    b.setNegativeButton(R.string.battery_low_why,
-                            new DialogInterface.OnClickListener() {
-                        public void onClick(DialogInterface dialog, int which) {
-                            mContext.startActivity(intent);
-                            if (mLowBatteryDialog != null) {
-                                mLowBatteryDialog.dismiss();
-                            }
-                        }
-                    });
-                }
-
-            AlertDialog d = b.create();
-            d.setOnDismissListener(mLowBatteryListener);
-            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
-            d.show();
-            mLowBatteryDialog = d;
-        }
-
-        final ContentResolver cr = mContext.getContentResolver();
-        if (Settings.System.getInt(cr,
-                Settings.System.POWER_SOUNDS_ENABLED, 1) == 1)
-        {
-            final String soundPath = Settings.System.getString(cr,
-                Settings.System.LOW_BATTERY_SOUND);
-            if (soundPath != null) {
-                final Uri soundUri = Uri.parse("file://" + soundPath);
-                if (soundUri != null) {
-                    final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
-                    if (sfx != null) {
-                        sfx.setStreamType(AudioManager.STREAM_SYSTEM);
-                        sfx.play();
-                    }
-                }
-            }
-        }
-    }
-
-    private final void updateCallState(int state) {
-        mPhoneState = state;
-        if (false) {
-            Slog.d(TAG, "mPhoneState=" + mPhoneState
-                    + " mLowBatteryDialog=" + mLowBatteryDialog
-                    + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
-        }
-        if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
-            if (mBatteryShowLowOnEndCall) {
-                if (!mBatteryPlugged) {
-                    showLowBatteryWarning();
-                }
-                mBatteryShowLowOnEndCall = false;
-            }
-        } else {
-            if (mLowBatteryDialog != null) {
-                mLowBatteryDialog.dismiss();
-                mBatteryShowLowOnEndCall = true;
-            }
-        }
-    }
-
-    private DialogInterface.OnDismissListener mLowBatteryListener
-            = new DialogInterface.OnDismissListener() {
-        public void onDismiss(DialogInterface dialog) {
-            mLowBatteryDialog = null;
-            mBatteryLevelTextView = null;
-        }
-    };
-
-    private void scheduleCloseBatteryView() {
-        Message m = mHandler.obtainMessage(EVENT_BATTERY_CLOSE);
-        m.arg1 = (++mBatteryViewSequence);
-        mHandler.sendMessageDelayed(m, 3000);
-    }
-
-    private void closeLastBatteryView() {
-        if (mBatteryView != null) {
-            //mBatteryView.debug();
-            WindowManagerImpl.getDefault().removeView(mBatteryView);
-            mBatteryView = null;
-        }
     }
 
     private void updateConnectivity(Intent intent) {
@@ -764,7 +550,6 @@
 
         @Override
         public void onCallStateChanged(int state, String incomingNumber) {
-            updateCallState(state);
             // In cdma, if a voice call is made, RSSI should switch to 1x.
             if (isCdma()) {
                 updateSignalStrength();
@@ -965,6 +750,7 @@
         case TelephonyManager.NETWORK_TYPE_EVDO_B:
             mDataIconList = sDataNetType_3g[mInetCondition];
             break;
+        // TODO - add support for NETWORK_TYPE_LTE and NETWORK_TYPE_EHRPD
         default:
             mDataIconList = sDataNetType_g[mInetCondition];
         break;
@@ -1063,28 +849,16 @@
         if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
             int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
             mBluetoothEnabled = state == BluetoothAdapter.STATE_ON;
-        } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
-            mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                    BluetoothHeadset.STATE_ERROR);
-        } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
-            BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
-            if (a2dp.getConnectedSinks().size() != 0) {
-                mBluetoothA2dpConnected = true;
-            } else {
-                mBluetoothA2dpConnected = false;
+        } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
+            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+                BluetoothAdapter.STATE_DISCONNECTED);
+            if (state == BluetoothAdapter.STATE_CONNECTED) {
+                iconId = R.drawable.stat_sys_data_bluetooth_connected;
             }
-        } else if (action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
-            mBluetoothPbapState = intent.getIntExtra(BluetoothPbap.PBAP_STATE,
-                    BluetoothPbap.STATE_DISCONNECTED);
         } else {
             return;
         }
 
-        if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED || mBluetoothA2dpConnected ||
-                mBluetoothPbapState == BluetoothPbap.STATE_CONNECTED) {
-            iconId = R.drawable.stat_sys_data_bluetooth_connected;
-        }
-
         mService.setIcon("bluetooth", iconId, 0);
         mService.setIconVisibility("bluetooth", mBluetoothEnabled);
     }
@@ -1204,18 +978,4 @@
         }
         mService.setIcon("phone_signal", mPhoneSignalIconId, 0);
     }
-
-
-    private class StatusBarHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-            case EVENT_BATTERY_CLOSE:
-                if (msg.arg1 == mBatteryViewSequence) {
-                    closeLastBatteryView();
-                }
-                break;
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
index de30ccd..00b39c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
@@ -17,185 +17,46 @@
 package com.android.systemui.statusbar;
 
 import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+
+import java.util.ArrayList;
+
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.statusbar.StatusBarIconList;
 import com.android.internal.statusbar.StatusBarNotification;
 
-import android.app.ActivityManagerNative;
-import android.app.Dialog;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.app.StatusBarManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Message;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.util.Log;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RemoteViews;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import android.widget.FrameLayout;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Set;
-
+import com.android.systemui.SystemUI;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.StatusBarPolicy;
 
-
-
-public class StatusBarService extends Service implements CommandQueue.Callbacks {
+public abstract class StatusBarService extends SystemUI implements CommandQueue.Callbacks {
     static final String TAG = "StatusBarService";
-    static final boolean SPEW_ICONS = false;
-    static final boolean SPEW = false;
+    private static final boolean SPEW = false;
 
-    public static final String ACTION_STATUSBAR_START
-            = "com.android.internal.policy.statusbar.START";
+    protected CommandQueue mCommandQueue;
+    protected IStatusBarService mBarService;
 
-    static final int EXPANDED_LEAVE_ALONE = -10000;
-    static final int EXPANDED_FULL_OPEN = -10001;
+    // Up-call methods
+    protected abstract View makeStatusBarView();
+    protected abstract int getStatusBarGravity();
 
-    private static final int MSG_ANIMATE = 1000;
-    private static final int MSG_ANIMATE_REVEAL = 1001;
-
-    StatusBarPolicy mIconPolicy;
-
-    CommandQueue mCommandQueue;
-    IStatusBarService mBarService;
-
-    int mIconSize;
-    Display mDisplay;
-    StatusBarView mStatusBarView;
-    int mPixelFormat;
-    H mHandler = new H();
-    Object mQueueLock = new Object();
-
-    // icons
-    LinearLayout mIcons;
-    IconMerger mNotificationIcons;
-    LinearLayout mStatusIcons;
-
-    // expanded notifications
-    Dialog mExpandedDialog;
-    ExpandedView mExpandedView;
-    WindowManager.LayoutParams mExpandedParams;
-    ScrollView mScrollView;
-    View mNotificationLinearLayout;
-    View mExpandedContents;
-    // top bar
-    TextView mNoNotificationsTitle;
-    TextView mClearButton;
-    // drag bar
-    CloseDragHandle mCloseView;
-    // ongoing
-    NotificationData mOngoing = new NotificationData();
-    TextView mOngoingTitle;
-    LinearLayout mOngoingItems;
-    // latest
-    NotificationData mLatest = new NotificationData();
-    TextView mLatestTitle;
-    LinearLayout mLatestItems;
-    // position
-    int[] mPositionTmp = new int[2];
-    boolean mExpanded;
-    boolean mExpandedVisible;
-
-    // the date view
-    DateView mDateView;
-
-    // the tracker view
-    TrackingView mTrackingView;
-    WindowManager.LayoutParams mTrackingParams;
-    int mTrackingPosition; // the position of the top of the tracking view.
-    private boolean mPanelSlightlyVisible;
-
-    // ticker
-    private Ticker mTicker;
-    private View mTickerView;
-    private boolean mTicking;
-
-    // Tracking finger for opening/closing.
-    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
-    boolean mTracking;
-    VelocityTracker mVelocityTracker;
-
-    static final int ANIM_FRAME_DURATION = (1000/60);
-
-    boolean mAnimating;
-    long mCurAnimationTime;
-    float mDisplayHeight;
-    float mAnimY;
-    float mAnimVel;
-    float mAnimAccel;
-    long mAnimLastTime;
-    boolean mAnimatingReveal = false;
-    int mViewDelta;
-    int[] mAbsPos = new int[2];
-
-    // for disabling the status bar
-    int mDisabled = 0;
-
-    private class ExpandedDialog extends Dialog {
-        ExpandedDialog(Context context) {
-            super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
-        }
-
-        @Override
-        public boolean dispatchKeyEvent(KeyEvent event) {
-            boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
-            switch (event.getKeyCode()) {
-            case KeyEvent.KEYCODE_BACK:
-                if (!down) {
-                    animateCollapse();
-                }
-                return true;
-            }
-            return super.dispatchKeyEvent(event);
-        }
-    }
-
-
-    @Override
-    public void onCreate() {
+    public void start() {
         // First set up our views and stuff.
-        mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
-        makeStatusBarView(this);
+        View sb = makeStatusBarView();
 
         // Connect in to the status bar manager service
         StatusBarIconList iconList = new StatusBarIconList();
@@ -204,12 +65,19 @@
         mCommandQueue = new CommandQueue(this, iconList);
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        int[] switches = new int[4];
         try {
-            mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications);
+            mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
+                    switches);
         } catch (RemoteException ex) {
             // If the system process isn't there we're doomed anyway.
         }
 
+        disable(switches[0]);
+        setLightsOn(switches[1] != 0);
+        setMenuKeyVisible(switches[2] != 0);
+        setIMEButtonVisible(switches[3] != 0);
+
         // Set up the initial icon state
         int N = iconList.size();
         int viewIndex = 0;
@@ -233,1269 +101,29 @@
         }
 
         // Put up the view
-        addStatusBarView();
-
-        // Lastly, call to the icon policy to install/update all the icons.
-        mIconPolicy = new StatusBarPolicy(this);
-    }
-
-    @Override
-    public void onDestroy() {
-        // we're never destroyed
-    }
-
-    /**
-     * Nobody binds to us.
-     */
-    @Override
-    public IBinder onBind(Intent intent) {
-        return null;
-    }
-
-    // ================================================================================
-    // Constructing the view
-    // ================================================================================
-    private void makeStatusBarView(Context context) {
-        Resources res = context.getResources();
-
-        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
-
-        ExpandedView expanded = (ExpandedView)View.inflate(context,
-                R.layout.status_bar_expanded, null);
-        expanded.mService = this;
-
-        StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
-        sb.mService = this;
-
-        // figure out which pixel-format to use for the status bar.
-        mPixelFormat = PixelFormat.TRANSLUCENT;
-        Drawable bg = sb.getBackground();
-        if (bg != null) {
-            mPixelFormat = bg.getOpacity();
-        }
-
-        mStatusBarView = sb;
-        mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
-        mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
-        mIcons = (LinearLayout)sb.findViewById(R.id.icons);
-        mTickerView = sb.findViewById(R.id.ticker);
-        mDateView = (DateView)sb.findViewById(R.id.date);
-
-        mExpandedDialog = new ExpandedDialog(context);
-        mExpandedView = expanded;
-        mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
-        mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
-        mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
-        mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
-        mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
-        mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
-        mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
-        mClearButton.setOnClickListener(mClearButtonListener);
-        mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
-        mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
-
-        mExpandedView.setVisibility(View.GONE);
-        mOngoingTitle.setVisibility(View.GONE);
-        mLatestTitle.setVisibility(View.GONE);
-
-        mTicker = new MyTicker(context, sb);
-
-        TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
-        tickerView.mTicker = mTicker;
-
-        mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null);
-        mTrackingView.mService = this;
-        mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
-        mCloseView.mService = this;
-
-        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
-        // set the inital view visibility
-        setAreThereNotifications();
-        mDateView.setVisibility(View.INVISIBLE);
-
-        // receive broadcasts
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        context.registerReceiver(mBroadcastReceiver, filter);
-    }
-
-    protected void addStatusBarView() {
-        Resources res = getResources();
+        final Resources res = mContext.getResources();
         final int height= res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
 
-        final StatusBarView view = mStatusBarView;
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 height,
                 WindowManager.LayoutParams.TYPE_STATUS_BAR,
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                     | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
                 PixelFormat.RGBX_8888);
-        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        lp.gravity = getStatusBarGravity();
         lp.setTitle("StatusBar");
-        lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar;
-
-        WindowManagerImpl.getDefault().addView(view, lp);
-    }
-
-    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
-        if (SPEW_ICONS) {
-            Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
-                    + " icon=" + icon);
-        }
-        StatusBarIconView view = new StatusBarIconView(this, slot);
-        view.set(icon);
-        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
-    }
-
-    public void updateIcon(String slot, int index, int viewIndex,
-            StatusBarIcon old, StatusBarIcon icon) {
-        if (SPEW_ICONS) {
-            Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
-                    + " old=" + old + " icon=" + icon);
-        }
-        StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
-        view.set(icon);
-    }
-
-    public void removeIcon(String slot, int index, int viewIndex) {
-        if (SPEW_ICONS) {
-            Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
-        }
-        mStatusIcons.removeViewAt(viewIndex);
-    }
-
-    public void addNotification(IBinder key, StatusBarNotification notification) {
-        boolean shouldTick = true;
-        if (notification.notification.fullScreenIntent != null) {
-            shouldTick = false;
-            Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
-            try {
-                notification.notification.fullScreenIntent.send();
-            } catch (PendingIntent.CanceledException e) {
-            }
-        } 
-
-        StatusBarIconView iconView = addNotificationViews(key, notification);
-        if (iconView == null) return;
-
-        if (shouldTick) {
-            tick(notification);
-        }
-        
-        // Recalculate the position of the sliding windows and the titles.
-        setAreThereNotifications();
-        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
-    }
-
-    public void updateNotification(IBinder key, StatusBarNotification notification) {
-        NotificationData oldList;
-        int oldIndex = mOngoing.findEntry(key);
-        if (oldIndex >= 0) {
-            oldList = mOngoing;
-        } else {
-            oldIndex = mLatest.findEntry(key);
-            if (oldIndex < 0) {
-                Slog.w(TAG, "updateNotification for unknown key: " + key);
-                return;
-            }
-            oldList = mLatest;
-        }
-        final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex);
-        final StatusBarNotification oldNotification = oldEntry.notification;
-        final RemoteViews oldContentView = oldNotification.notification.contentView;
-
-        final RemoteViews contentView = notification.notification.contentView;
-
-        if (false) {
-            Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
-                    + " ongoing=" + oldNotification.isOngoing()
-                    + " expanded=" + oldEntry.expanded
-                    + " contentView=" + oldContentView);
-            Slog.d(TAG, "new notification: when=" + notification.notification.when
-                    + " ongoing=" + oldNotification.isOngoing()
-                    + " contentView=" + contentView);
-        }
-
-        // Can we just reapply the RemoteViews in place?  If when didn't change, the order
-        // didn't change.
-        if (notification.notification.when == oldNotification.notification.when
-                && notification.isOngoing() == oldNotification.isOngoing()
-                && oldEntry.expanded != null
-                && contentView != null && oldContentView != null
-                && contentView.getPackage() != null
-                && oldContentView.getPackage() != null
-                && oldContentView.getPackage().equals(contentView.getPackage())
-                && oldContentView.getLayoutId() == contentView.getLayoutId()) {
-            if (SPEW) Slog.d(TAG, "reusing notification");
-            oldEntry.notification = notification;
-            try {
-                // Reapply the RemoteViews
-                contentView.reapply(this, oldEntry.content);
-                // update the contentIntent
-                final PendingIntent contentIntent = notification.notification.contentIntent;
-                if (contentIntent != null) {
-                    oldEntry.content.setOnClickListener(new Launcher(contentIntent,
-                                notification.pkg, notification.tag, notification.id));
-                }
-                // Update the icon.
-                final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
-                        notification.notification.icon, notification.notification.iconLevel,
-                        notification.notification.number);
-                if (!oldEntry.icon.set(ic)) {
-                    handleNotificationError(key, notification, "Couldn't update icon: " + ic);
-                    return;
-                }
-            }
-            catch (RuntimeException e) {
-                // It failed to add cleanly.  Log, and remove the view from the panel.
-                Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
-                removeNotificationViews(key);
-                addNotificationViews(key, notification);
-            }
-        } else {
-            if (SPEW) Slog.d(TAG, "not reusing notification");
-            removeNotificationViews(key);
-            addNotificationViews(key, notification);
-        }
-
-        // Restart the ticker if it's still running
-        if (notification.notification.tickerText != null
-                && !TextUtils.equals(notification.notification.tickerText,
-                    oldEntry.notification.notification.tickerText)) {
-            tick(notification);
-        }
-
-        // Recalculate the position of the sliding windows and the titles.
-        setAreThereNotifications();
-        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
-    }
-
-    public void removeNotification(IBinder key) {
-        if (SPEW) Slog.d(TAG, "removeNotification key=" + key);
-        StatusBarNotification old = removeNotificationViews(key);
-
-        if (old != null) {
-            // Cancel the ticker if it's still running
-            mTicker.removeEntry(old);
-
-            // Recalculate the position of the sliding windows and the titles.
-            setAreThereNotifications();
-            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
-        }
-    }
-
-    private int chooseIconIndex(boolean isOngoing, int viewIndex) {
-        final int latestSize = mLatest.size();
-        if (isOngoing) {
-            return latestSize + (mOngoing.size() - viewIndex);
-        } else {
-            return latestSize - viewIndex;
-        }
-    }
-
-    View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
-        Notification n = notification.notification;
-        RemoteViews remoteViews = n.contentView;
-        if (remoteViews == null) {
-            return null;
-        }
-
-        // create the row view
-        LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
-
-        // bind the click event to the content area
-        ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
-        content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-        content.setOnFocusChangeListener(mFocusChangeListener);
-        PendingIntent contentIntent = n.contentIntent;
-        if (contentIntent != null) {
-            content.setOnClickListener(new Launcher(contentIntent, notification.pkg,
-                        notification.tag, notification.id));
-        }
-
-        View expanded = null;
-        Exception exception = null;
-        try {
-            expanded = remoteViews.apply(this, content);
-        }
-        catch (RuntimeException e) {
-            exception = e;
-        }
-        if (expanded == null) {
-            String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id);
-            Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
-            return null;
-        } else {
-            content.addView(expanded);
-            row.setDrawingCacheEnabled(true);
-        }
-
-        return new View[] { row, content, expanded };
-    }
-
-    StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
-        NotificationData list;
-        ViewGroup parent;
-        final boolean isOngoing = notification.isOngoing();
-        if (isOngoing) {
-            list = mOngoing;
-            parent = mOngoingItems;
-        } else {
-            list = mLatest;
-            parent = mLatestItems;
-        }
-        // Construct the expanded view.
-        final View[] views = makeNotificationView(notification, parent);
-        if (views == null) {
-            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
-                    + notification);
-            return null;
-        }
-        final View row = views[0];
-        final View content = views[1];
-        final View expanded = views[2];
-        // Construct the icon.
-        final StatusBarIconView iconView = new StatusBarIconView(this,
-                notification.pkg + "/0x" + Integer.toHexString(notification.id));
-        final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
-                    notification.notification.iconLevel, notification.notification.number);
-        if (!iconView.set(ic)) {
-            handleNotificationError(key, notification, "Coulding create icon: " + ic);
-            return null;
-        }
-        // Add the expanded view.
-        final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
-        parent.addView(row, viewIndex);
-        // Add the icon.
-        final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
-        mNotificationIcons.addView(iconView, iconIndex);
-        return iconView;
-    }
-
-    StatusBarNotification removeNotificationViews(IBinder key) {
-        NotificationData.Entry entry = mOngoing.remove(key);
-        if (entry == null) {
-            entry = mLatest.remove(key);
-            if (entry == null) {
-                Slog.w(TAG, "removeNotification for unknown key: " + key);
-                return null;
-            }
-        }
-        // Remove the expanded view.
-        ((ViewGroup)entry.row.getParent()).removeView(entry.row);
-        // Remove the icon.
-        ((ViewGroup)entry.icon.getParent()).removeView(entry.icon);
-
-        return entry.notification;
-    }
-
-    private void setAreThereNotifications() {
-        boolean ongoing = mOngoing.hasVisibleItems();
-        boolean latest = mLatest.hasVisibleItems();
-
-        // (no ongoing notifications are clearable)
-        if (mLatest.hasClearableItems()) {
-            mClearButton.setVisibility(View.VISIBLE);
-        } else {
-            mClearButton.setVisibility(View.INVISIBLE);
-        }
-
-        mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
-        mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
-
-        if (ongoing || latest) {
-            mNoNotificationsTitle.setVisibility(View.GONE);
-        } else {
-            mNoNotificationsTitle.setVisibility(View.VISIBLE);
-        }
-    }
-
-
-    /**
-     * State is one or more of the DISABLE constants from StatusBarManager.
-     */
-    public void disable(int state) {
-        final int old = mDisabled;
-        final int diff = state ^ old;
-        mDisabled = state;
-
-        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
-            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
-                if (SPEW) Slog.d(TAG, "DISABLE_EXPAND: yes");
-                animateCollapse();
-            }
-        }
-        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
-            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
-                if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
-                if (mTicking) {
-                    mTicker.halt();
-                } else {
-                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
-                }
-            } else {
-                if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
-                if (!mExpandedVisible) {
-                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
-                }
-            }
-        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-            if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-                if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
-                mTicker.halt();
-            }
-        }
-    }
-
-    /**
-     * All changes to the status bar and notifications funnel through here and are batched.
-     */
-    private class H extends Handler {
-        public void handleMessage(Message m) {
-            switch (m.what) {
-                case MSG_ANIMATE:
-                    doAnimation();
-                    break;
-                case MSG_ANIMATE_REVEAL:
-                    doRevealAnimation();
-                    break;
-            }
-        }
-    }
-
-    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
-        public void onFocusChange(View v, boolean hasFocus) {
-            // Because 'v' is a ViewGroup, all its children will be (un)selected
-            // too, which allows marqueeing to work.
-            v.setSelected(hasFocus);
-        }
-    };
-
-    private void makeExpandedVisible() {
-        if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
-        if (mExpandedVisible) {
-            return;
-        }
-        mExpandedVisible = true;
-        visibilityChanged(true);
-
-        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
-        mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-        mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-        mExpandedView.requestFocus(View.FOCUS_FORWARD);
-        mTrackingView.setVisibility(View.VISIBLE);
-        mExpandedView.setVisibility(View.VISIBLE);
-
-        if (!mTicking) {
-            setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
-        }
-    }
-
-    public void animateExpand() {
-        if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
-        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
-            return ;
-        }
-        if (mExpanded) {
-            return;
-        }
-
-        prepareTracking(0, true);
-        performFling(0, 2000.0f, true);
-    }
-
-    public void animateCollapse() {
-        if (SPEW) {
-            Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
-                    + " mExpandedVisible=" + mExpandedVisible
-                    + " mExpanded=" + mExpanded
-                    + " mAnimating=" + mAnimating
-                    + " mAnimY=" + mAnimY
-                    + " mAnimVel=" + mAnimVel);
-        }
-
-        if (!mExpandedVisible) {
-            return;
-        }
-
-        int y;
-        if (mAnimating) {
-            y = (int)mAnimY;
-        } else {
-            y = mDisplay.getHeight()-1;
-        }
-        // Let the fling think that we're open so it goes in the right direction
-        // and doesn't try to re-open the windowshade.
-        mExpanded = true;
-        prepareTracking(y, false);
-        performFling(y, -2000.0f, true);
-    }
-
-    void performExpand() {
-        if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
-        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
-            return ;
-        }
-        if (mExpanded) {
-            return;
-        }
-
-        mExpanded = true;
-        makeExpandedVisible();
-        updateExpandedViewPos(EXPANDED_FULL_OPEN);
-
-        if (false) postStartTracing();
-    }
-
-    void performCollapse() {
-        if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
-                + " mExpandedVisible=" + mExpandedVisible
-                + " mTicking=" + mTicking);
-
-        if (!mExpandedVisible) {
-            return;
-        }
-        mExpandedVisible = false;
-        visibilityChanged(false);
-        mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-        mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-        mTrackingView.setVisibility(View.GONE);
-        mExpandedView.setVisibility(View.GONE);
-
-        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
-            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
-        }
-        if (mDateView.getVisibility() == View.VISIBLE) {
-            setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
-        }
-
-        if (!mExpanded) {
-            return;
-        }
-        mExpanded = false;
-    }
-
-    void doAnimation() {
-        if (mAnimating) {
-            if (SPEW) Slog.d(TAG, "doAnimation");
-            if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
-            incrementAnim();
-            if (SPEW) Slog.d(TAG, "doAnimation after  mAnimY=" + mAnimY);
-            if (mAnimY >= mDisplay.getHeight()-1) {
-                if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
-                mAnimating = false;
-                updateExpandedViewPos(EXPANDED_FULL_OPEN);
-                performExpand();
-            }
-            else if (mAnimY < mStatusBarView.getHeight()) {
-                if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
-                mAnimating = false;
-                updateExpandedViewPos(0);
-                performCollapse();
-            }
-            else {
-                updateExpandedViewPos((int)mAnimY);
-                mCurAnimationTime += ANIM_FRAME_DURATION;
-                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
-            }
-        }
-    }
-
-    void stopTracking() {
-        mTracking = false;
-        mVelocityTracker.recycle();
-        mVelocityTracker = null;
-    }
-
-    void incrementAnim() {
-        long now = SystemClock.uptimeMillis();
-        float t = ((float)(now - mAnimLastTime)) / 1000;            // ms -> s
-        final float y = mAnimY;
-        final float v = mAnimVel;                                   // px/s
-        final float a = mAnimAccel;                                 // px/s/s
-        mAnimY = y + (v*t) + (0.5f*a*t*t);                          // px
-        mAnimVel = v + (a*t);                                       // px/s
-        mAnimLastTime = now;                                        // ms
-        //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
-        //        + " mAnimAccel=" + mAnimAccel);
-    }
-
-    void doRevealAnimation() {
-        final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
-        if (mAnimatingReveal && mAnimating && mAnimY < h) {
-            incrementAnim();
-            if (mAnimY >= h) {
-                mAnimY = h;
-                updateExpandedViewPos((int)mAnimY);
-            } else {
-                updateExpandedViewPos((int)mAnimY);
-                mCurAnimationTime += ANIM_FRAME_DURATION;
-                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
-                        mCurAnimationTime);
-            }
-        }
-    }
-
-    void prepareTracking(int y, boolean opening) {
-        mTracking = true;
-        mVelocityTracker = VelocityTracker.obtain();
-        if (opening) {
-            mAnimAccel = 2000.0f;
-            mAnimVel = 200;
-            mAnimY = mStatusBarView.getHeight();
-            updateExpandedViewPos((int)mAnimY);
-            mAnimating = true;
-            mAnimatingReveal = true;
-            mHandler.removeMessages(MSG_ANIMATE);
-            mHandler.removeMessages(MSG_ANIMATE_REVEAL);
-            long now = SystemClock.uptimeMillis();
-            mAnimLastTime = now;
-            mCurAnimationTime = now + ANIM_FRAME_DURATION;
-            mAnimating = true;
-            mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
-                    mCurAnimationTime);
-            makeExpandedVisible();
-        } else {
-            // it's open, close it?
-            if (mAnimating) {
-                mAnimating = false;
-                mHandler.removeMessages(MSG_ANIMATE);
-            }
-            updateExpandedViewPos(y + mViewDelta);
-        }
-    }
-
-    void performFling(int y, float vel, boolean always) {
-        mAnimatingReveal = false;
-        mDisplayHeight = mDisplay.getHeight();
-
-        mAnimY = y;
-        mAnimVel = vel;
-
-        //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
-
-        if (mExpanded) {
-            if (!always && (
-                    vel > 200.0f
-                    || (y > (mDisplayHeight-25) && vel > -200.0f))) {
-                // We are expanded, but they didn't move sufficiently to cause
-                // us to retract.  Animate back to the expanded position.
-                mAnimAccel = 2000.0f;
-                if (vel < 0) {
-                    mAnimVel = 0;
-                }
-            }
-            else {
-                // We are expanded and are now going to animate away.
-                mAnimAccel = -2000.0f;
-                if (vel > 0) {
-                    mAnimVel = 0;
-                }
-            }
-        } else {
-            if (always || (
-                    vel > 200.0f
-                    || (y > (mDisplayHeight/2) && vel > -200.0f))) {
-                // We are collapsed, and they moved enough to allow us to
-                // expand.  Animate in the notifications.
-                mAnimAccel = 2000.0f;
-                if (vel < 0) {
-                    mAnimVel = 0;
-                }
-            }
-            else {
-                // We are collapsed, but they didn't move sufficiently to cause
-                // us to retract.  Animate back to the collapsed position.
-                mAnimAccel = -2000.0f;
-                if (vel > 0) {
-                    mAnimVel = 0;
-                }
-            }
-        }
-        //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
-        //        + " mAnimAccel=" + mAnimAccel);
-
-        long now = SystemClock.uptimeMillis();
-        mAnimLastTime = now;
-        mCurAnimationTime = now + ANIM_FRAME_DURATION;
-        mAnimating = true;
-        mHandler.removeMessages(MSG_ANIMATE);
-        mHandler.removeMessages(MSG_ANIMATE_REVEAL);
-        mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
-        stopTracking();
-    }
-
-    boolean interceptTouchEvent(MotionEvent event) {
-        if (SPEW) {
-            Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
-                + mDisabled);
-        }
-
-        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
-            return false;
-        }
-
-        final int statusBarSize = mStatusBarView.getHeight();
-        final int hitSize = statusBarSize*2;
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            final int y = (int)event.getRawY();
-
-            if (!mExpanded) {
-                mViewDelta = statusBarSize - y;
-            } else {
-                mTrackingView.getLocationOnScreen(mAbsPos);
-                mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
-            }
-            if ((!mExpanded && y < hitSize) ||
-                    (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
-
-                // We drop events at the edge of the screen to make the windowshade come
-                // down by accident less, especially when pushing open a device with a keyboard
-                // that rotates (like g1 and droid)
-                int x = (int)event.getRawX();
-                final int edgeBorder = mEdgeBorder;
-                if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
-                    prepareTracking(y, !mExpanded);// opening if we're not already fully visible
-                    mVelocityTracker.addMovement(event);
-                }
-            }
-        } else if (mTracking) {
-            mVelocityTracker.addMovement(event);
-            final int minY = statusBarSize + mCloseView.getHeight();
-            if (event.getAction() == MotionEvent.ACTION_MOVE) {
-                int y = (int)event.getRawY();
-                if (mAnimatingReveal && y < minY) {
-                    // nothing
-                } else  {
-                    mAnimatingReveal = false;
-                    updateExpandedViewPos(y + mViewDelta);
-                }
-            } else if (event.getAction() == MotionEvent.ACTION_UP) {
-                mVelocityTracker.computeCurrentVelocity(1000);
-
-                float yVel = mVelocityTracker.getYVelocity();
-                boolean negative = yVel < 0;
-
-                float xVel = mVelocityTracker.getXVelocity();
-                if (xVel < 0) {
-                    xVel = -xVel;
-                }
-                if (xVel > 150.0f) {
-                    xVel = 150.0f; // limit how much we care about the x axis
-                }
-
-                float vel = (float)Math.hypot(yVel, xVel);
-                if (negative) {
-                    vel = -vel;
-                }
-
-                performFling((int)event.getRawY(), vel, false);
-            }
-
-        }
-        return false;
-    }
-
-    private class Launcher implements View.OnClickListener {
-        private PendingIntent mIntent;
-        private String mPkg;
-        private String mTag;
-        private int mId;
-
-        Launcher(PendingIntent intent, String pkg, String tag, int id) {
-            mIntent = intent;
-            mPkg = pkg;
-            mTag = tag;
-            mId = id;
-        }
-
-        public void onClick(View v) {
-            try {
-                // The intent we are sending is for the application, which
-                // won't have permission to immediately start an activity after
-                // the user switches to home.  We know it is safe to do at this
-                // point, so make sure new activity switches are now allowed.
-                ActivityManagerNative.getDefault().resumeAppSwitches();
-            } catch (RemoteException e) {
-            }
-
-            if (mIntent != null) {
-                int[] pos = new int[2];
-                v.getLocationOnScreen(pos);
-                Intent overlay = new Intent();
-                overlay.setSourceBounds(
-                        new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
-                try {
-                    mIntent.send(StatusBarService.this, 0, overlay);
-                } catch (PendingIntent.CanceledException e) {
-                    // the stack trace isn't very helpful here.  Just log the exception message.
-                    Slog.w(TAG, "Sending contentIntent failed: " + e);
-                }
-            }
-
-            try {
-                mBarService.onNotificationClick(mPkg, mTag, mId);
-            } catch (RemoteException ex) {
-                // system process is dead if we're here.
-            }
-
-            // close the shade if it was open
-            animateCollapse();
-        }
-    }
-
-    private void tick(StatusBarNotification n) {
-        // Show the ticker if one is requested. Also don't do this
-        // until status bar window is attached to the window manager,
-        // because...  well, what's the point otherwise?  And trying to
-        // run a ticker without being attached will crash!
-        if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
-            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
-                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
-                mTicker.addEntry(n);
-            }
-        }
-    }
-
-    /**
-     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
-     * about the failure.
-     *
-     * WARNING: this will call back into us.  Don't hold any locks.
-     */
-    void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
-        removeNotification(key);
-        try {
-            mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
-        } catch (RemoteException ex) {
-            // The end is nigh.
-        }
-    }
-
-    private class MyTicker extends Ticker {
-        MyTicker(Context context, StatusBarView sb) {
-            super(context, sb);
-        }
-
-        @Override
-        void tickerStarting() {
-            if (SPEW) Slog.d(TAG, "tickerStarting");
-            mTicking = true;
-            mIcons.setVisibility(View.GONE);
-            mTickerView.setVisibility(View.VISIBLE);
-            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
-            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
-            if (mExpandedVisible) {
-                setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
-            }
-        }
-
-        @Override
-        void tickerDone() {
-            if (SPEW) Slog.d(TAG, "tickerDone");
-            mTicking = false;
-            mIcons.setVisibility(View.VISIBLE);
-            mTickerView.setVisibility(View.GONE);
-            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
-            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, null));
-            if (mExpandedVisible) {
-                setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
-            }
-        }
-
-        void tickerHalting() {
-            if (SPEW) Slog.d(TAG, "tickerHalting");
-            mTicking = false;
-            mIcons.setVisibility(View.VISIBLE);
-            mTickerView.setVisibility(View.GONE);
-            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
-            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out, null));
-            if (mExpandedVisible) {
-                setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
-            }
-        }
-    }
-
-    private Animation loadAnim(int id, Animation.AnimationListener listener) {
-        Animation anim = AnimationUtils.loadAnimation(StatusBarService.this, id);
-        if (listener != null) {
-            anim.setAnimationListener(listener);
-        }
-        return anim;
-    }
-
-    public String viewInfo(View v) {
-        return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
-                + " " + v.getWidth() + "x" + v.getHeight() + ")";
-    }
-
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
-                != PackageManager.PERMISSION_GRANTED) {
-            pw.println("Permission Denial: can't dump StatusBar from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
-            return;
-        }
-
-        synchronized (mQueueLock) {
-            pw.println("Current Status Bar state:");
-            pw.println("  mExpanded=" + mExpanded
-                    + ", mExpandedVisible=" + mExpandedVisible);
-            pw.println("  mTicking=" + mTicking);
-            pw.println("  mTracking=" + mTracking);
-            pw.println("  mAnimating=" + mAnimating
-                    + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
-                    + ", mAnimAccel=" + mAnimAccel);
-            pw.println("  mCurAnimationTime=" + mCurAnimationTime
-                    + " mAnimLastTime=" + mAnimLastTime);
-            pw.println("  mDisplayHeight=" + mDisplayHeight
-                    + " mAnimatingReveal=" + mAnimatingReveal
-                    + " mViewDelta=" + mViewDelta);
-            pw.println("  mDisplayHeight=" + mDisplayHeight);
-            pw.println("  mExpandedParams: " + mExpandedParams);
-            pw.println("  mExpandedView: " + viewInfo(mExpandedView));
-            pw.println("  mExpandedDialog: " + mExpandedDialog);
-            pw.println("  mTrackingParams: " + mTrackingParams);
-            pw.println("  mTrackingView: " + viewInfo(mTrackingView));
-            pw.println("  mOngoingTitle: " + viewInfo(mOngoingTitle));
-            pw.println("  mOngoingItems: " + viewInfo(mOngoingItems));
-            pw.println("  mLatestTitle: " + viewInfo(mLatestTitle));
-            pw.println("  mLatestItems: " + viewInfo(mLatestItems));
-            pw.println("  mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
-            pw.println("  mCloseView: " + viewInfo(mCloseView));
-            pw.println("  mTickerView: " + viewInfo(mTickerView));
-            pw.println("  mScrollView: " + viewInfo(mScrollView)
-                    + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
-            pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
-        }
-
-        if (true) {
-            // must happen on ui thread
-            mHandler.post(new Runnable() {
-                    public void run() {
-                        Slog.d(TAG, "mStatusIcons:");
-                        mStatusIcons.debug();
-                    }
-                });
-        }
-
-    }
-
-    void onBarViewAttached() {
-        WindowManager.LayoutParams lp;
-        int pixelFormat;
-        Drawable bg;
-
-        /// ---------- Tracking View --------------
-        pixelFormat = PixelFormat.RGBX_8888;
-        bg = mTrackingView.getBackground();
-        if (bg != null) {
-            pixelFormat = bg.getOpacity();
-        }
-
-        lp = new WindowManager.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
-                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
-                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
-                pixelFormat);
-//        lp.token = mStatusBarView.getWindowToken();
-        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
-        lp.setTitle("TrackingView");
-        lp.y = mTrackingPosition;
-        mTrackingParams = lp;
-
-        WindowManagerImpl.getDefault().addView(mTrackingView, lp);
-    }
-
-    void onTrackingViewAttached() {
-        WindowManager.LayoutParams lp;
-        int pixelFormat;
-        Drawable bg;
-
-        /// ---------- Expanded View --------------
-        pixelFormat = PixelFormat.TRANSLUCENT;
-
-        final int disph = mDisplay.getHeight();
-        lp = mExpandedDialog.getWindow().getAttributes();
-        lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
-        lp.height = getExpandedHeight();
-        lp.x = 0;
-        mTrackingPosition = lp.y = -disph; // sufficiently large negative
-        lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
-        lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
-                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                | WindowManager.LayoutParams.FLAG_DITHER
-                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        lp.format = pixelFormat;
-        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
-        lp.setTitle("StatusBarExpanded");
-        mExpandedDialog.getWindow().setAttributes(lp);
-        mExpandedDialog.getWindow().setFormat(pixelFormat);
-        mExpandedParams = lp;
-
-        mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
-        mExpandedDialog.setContentView(mExpandedView,
-                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                                           ViewGroup.LayoutParams.MATCH_PARENT));
-        mExpandedDialog.getWindow().setBackgroundDrawable(null);
-        mExpandedDialog.show();
-        FrameLayout hack = (FrameLayout)mExpandedView.getParent();
-    }
-
-    void setDateViewVisibility(boolean visible, int anim) {
-        mDateView.setUpdates(visible);
-        mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-        mDateView.startAnimation(loadAnim(anim, null));
-    }
-
-    void setNotificationIconVisibility(boolean visible, int anim) {
-        int old = mNotificationIcons.getVisibility();
-        int v = visible ? View.VISIBLE : View.INVISIBLE;
-        if (old != v) {
-            mNotificationIcons.setVisibility(v);
-            mNotificationIcons.startAnimation(loadAnim(anim, null));
-        }
-    }
-
-    void updateExpandedViewPos(int expandedPosition) {
-        if (SPEW) {
-            Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
-                    + " mTrackingParams.y=" 
-                    + ((mTrackingParams == null) ? "???" : mTrackingParams.y)
-                    + " mTrackingPosition=" + mTrackingPosition);
-        }
-
-        int h = mStatusBarView.getHeight();
-        int disph = mDisplay.getHeight();
-
-        // If the expanded view is not visible, make sure they're still off screen.
-        // Maybe the view was resized.
-        if (!mExpandedVisible) {
-            if (mTrackingView != null) {
-                mTrackingPosition = -disph;
-                if (mTrackingParams != null) {
-                    mTrackingParams.y = mTrackingPosition;
-                    WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
-                }
-            }
-            if (mExpandedParams != null) {
-                mExpandedParams.y = -disph;
-                mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-            }
-            return;
-        }
-
-        // tracking view...
-        int pos;
-        if (expandedPosition == EXPANDED_FULL_OPEN) {
-            pos = h;
-        }
-        else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
-            pos = mTrackingPosition;
-        }
-        else {
-            if (expandedPosition <= disph) {
-                pos = expandedPosition;
-            } else {
-                pos = disph;
-            }
-            pos -= disph-h;
-        }
-        mTrackingPosition = mTrackingParams.y = pos;
-        mTrackingParams.height = disph-h;
-        WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
-
-        if (mExpandedParams != null) {
-            mCloseView.getLocationInWindow(mPositionTmp);
-            final int closePos = mPositionTmp[1];
-
-            mExpandedContents.getLocationInWindow(mPositionTmp);
-            final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
-
-            if (expandedPosition != EXPANDED_LEAVE_ALONE) {
-                mExpandedParams.y = pos + mTrackingView.getHeight()
-                        - (mTrackingParams.height-closePos) - contentsBottom;
-                int max = h;
-                if (mExpandedParams.y > max) {
-                    mExpandedParams.y = max;
-                }
-                int min = mTrackingPosition;
-                if (mExpandedParams.y < min) {
-                    mExpandedParams.y = min;
-                }
-
-                boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
-                if (!visible) {
-                    // if the contents aren't visible, move the expanded view way off screen
-                    // because the window itself extends below the content view.
-                    mExpandedParams.y = -disph;
-                }
-                mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-
-                if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
-                visibilityChanged(visible);
-            }
-        }
+        // TODO lp.windowAnimations = R.style.Animation_StatusBar;
+        WindowManagerImpl.getDefault().addView(sb, lp);
 
         if (SPEW) {
-            Slog.d(TAG, "updateExpandedViewPos after  expandedPosition=" + expandedPosition
-                    + " mTrackingParams.y=" + mTrackingParams.y
-                    + " mTrackingPosition=" + mTrackingPosition
-                    + " mExpandedParams.y=" + mExpandedParams.y
-                    + " mExpandedParams.height=" + mExpandedParams.height);
+            Slog.d(TAG, "Added status bar view: gravity=0x" + Integer.toHexString(lp.gravity) 
+                   + " icons=" + iconList.size()
+                   + " disabled=0x" + Integer.toHexString(switches[0])
+                   + " lights=" + switches[1]
+                   + " menu=" + switches[2]
+                   + " imeButton=" + switches[3]
+                   );
         }
     }
-
-    int getExpandedHeight() {
-        return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
-    }
-
-    void updateExpandedHeight() {
-        if (mExpandedView != null) {
-            mExpandedParams.height = getExpandedHeight();
-            mExpandedDialog.getWindow().setAttributes(mExpandedParams);
-        }
-    }
-
-    /**
-     * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
-     * This was added last-minute and is inconsistent with the way the rest of the notifications
-     * are handled, because the notification isn't really cancelled.  The lights are just
-     * turned off.  If any other notifications happen, the lights will turn back on.  Steve says
-     * this is what he wants. (see bug 1131461)
-     */
-    void visibilityChanged(boolean visible) {
-        if (mPanelSlightlyVisible != visible) {
-            mPanelSlightlyVisible = visible;
-            try {
-                mBarService.onPanelRevealed();
-            } catch (RemoteException ex) {
-                // Won't fail unless the world has ended.
-            }
-        }
-    }
-
-    void performDisableActions(int net) {
-        int old = mDisabled;
-        int diff = net ^ old;
-        mDisabled = net;
-
-        // act accordingly
-        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
-            if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
-                Slog.d(TAG, "DISABLE_EXPAND: yes");
-                animateCollapse();
-            }
-        }
-        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
-            if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
-                if (mTicking) {
-                    mNotificationIcons.setVisibility(View.INVISIBLE);
-                    mTicker.halt();
-                } else {
-                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
-                }
-            } else {
-                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
-                if (!mExpandedVisible) {
-                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
-                }
-            }
-        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-            Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: "
-                + (((net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0)
-                    ? "yes" : "no"));
-            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-                mTicker.halt();
-            }
-        }
-    }
-
-    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
-        public void onClick(View v) {
-            try {
-                mBarService.onClearAllNotifications();
-            } catch (RemoteException ex) {
-                // system process is dead if we're here.
-            }
-            animateCollapse();
-        }
-    };
-
-    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
-                    || Intent.ACTION_SCREEN_OFF.equals(action)) {
-                animateCollapse();
-            }
-            else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
-                updateResources();
-            }
-        }
-    };
-
-    /**
-     * Reload some of our resources when the configuration changes.
-     *
-     * We don't reload everything when the configuration changes -- we probably
-     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
-     * meantime, just update the things that we know change.
-     */
-    void updateResources() {
-        Resources res = getResources();
-
-        mClearButton.setText(getText(R.string.status_bar_clear_all_button));
-        mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title));
-        mLatestTitle.setText(getText(R.string.status_bar_latest_events_title));
-        mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title));
-
-        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
-        if (false) Slog.v(TAG, "updateResources");
-    }
-
-    //
-    // tracing
-    //
-
-    void postStartTracing() {
-        mHandler.postDelayed(mStartTracing, 3000);
-    }
-
-    void vibrate() {
-        android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
-        vib.vibrate(250);
-    }
-
-    Runnable mStartTracing = new Runnable() {
-        public void run() {
-            vibrate();
-            SystemClock.sleep(250);
-            Slog.d(TAG, "startTracing");
-            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
-            mHandler.postDelayed(mStopTracing, 10000);
-        }
-    };
-
-    Runnable mStopTracing = new Runnable() {
-        public void run() {
-            android.os.Debug.stopMethodTracing();
-            Slog.d(TAG, "stopTracing");
-            vibrate();
-        }
-    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
index 117b126..20fc41f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
+import android.graphics.Rect;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -34,7 +35,7 @@
 
     static final int DIM_ANIM_TIME = 400;
     
-    StatusBarService mService;
+    PhoneStatusBarService mService;
     boolean mTracking;
     int mStartX, mStartY;
     ViewGroup mNotificationIcons;
@@ -42,6 +43,13 @@
     View mDate;
     FixedSizeDrawable mBackground;
     
+    boolean mNightMode = false;
+    int mStartAlpha = 0, mEndAlpha = 0;
+    long mEndTime = 0;
+
+    Rect mButtonBounds = new Rect();
+    boolean mCapturingEvents = true;
+
     public StatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -65,9 +73,32 @@
     }
     
     @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+                == Configuration.UI_MODE_NIGHT_YES;
+        if (mNightMode != nightMode) {
+            mNightMode = nightMode;
+            mStartAlpha = getCurAlpha();
+            mEndAlpha = mNightMode ? 0x80 : 0x00;
+            mEndTime = SystemClock.uptimeMillis() + DIM_ANIM_TIME;
+            invalidate();
+        }
+    }
+
+    int getCurAlpha() {
+        long time = SystemClock.uptimeMillis();
+        if (time > mEndTime) {
+            return mEndAlpha;
+        }
+        return mEndAlpha
+                - (int)(((mEndAlpha-mStartAlpha) * (mEndTime-time) / DIM_ANIM_TIME));
+    }
+    
+    @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
-        mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE);
+        mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
     }
 
     @Override
@@ -100,6 +131,18 @@
         mBackground.setFixedBounds(-mDate.getLeft(), -mDate.getTop(), (r-l), (b-t));
     }
 
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+        int alpha = getCurAlpha();
+        if (alpha != 0) {
+            canvas.drawARGB(alpha, 0, 0, 0);
+        }
+        if (alpha != mEndAlpha) {
+            invalidate();
+        }
+    }
+
     /**
      * Gets the left position of v in this view.  Throws if v is not
      * a child of this.
@@ -138,6 +181,9 @@
      */
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        if (!mCapturingEvents) {
+            return false;
+        }
         if (event.getAction() != MotionEvent.ACTION_DOWN) {
             mService.interceptTouchEvent(event);
         }
@@ -146,6 +192,13 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            if (mButtonBounds.contains((int)event.getX(), (int)event.getY())) {
+                mCapturingEvents = false;
+                return false;
+            }
+        }
+        mCapturingEvents = true;
         return mService.interceptTouchEvent(event)
                 ? true : super.onInterceptTouchEvent(event);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
index 3a697a6..e7b0509 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
@@ -141,7 +141,7 @@
         }
     };
 
-    Ticker(Context context, StatusBarView sb) {
+    public Ticker(Context context, View sb) {
         mContext = context;
         mTickerView = sb.findViewById(R.id.ticker);
 
@@ -163,7 +163,7 @@
     }
 
 
-    void addEntry(StatusBarNotification n) {
+    public void addEntry(StatusBarNotification n) {
         int initialCount = mSegments.size();
 
         // If what's being displayed has the same text and icon, just drop it
@@ -212,7 +212,7 @@
         }
     }
 
-    void removeEntry(StatusBarNotification n) {
+    public void removeEntry(StatusBarNotification n) {
         for (int i=mSegments.size()-1; i>=0; i--) {
             Segment seg = mSegments.get(i);
             if (n.id == seg.notification.id && n.pkg.equals(seg.notification.pkg)) {
@@ -221,13 +221,13 @@
         }
     }
 
-    void halt() {
+    public void halt() {
         mHandler.removeCallbacks(mAdvanceTicker);
         mSegments.clear();
         tickerHalting();
     }
 
-    void reflowText() {
+    public void reflowText() {
         if (mSegments.size() > 0) {
             Segment seg = mSegments.get(0);
             CharSequence text = seg.getText();
@@ -266,8 +266,8 @@
         mHandler.postDelayed(mAdvanceTicker, TICKER_SEGMENT_DELAY);
     }
 
-    abstract void tickerStarting();
-    abstract void tickerDone();
-    abstract void tickerHalting();
+    public abstract void tickerStarting();
+    public abstract void tickerDone();
+    public abstract void tickerHalting();
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
index 9749ae4..8140811 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
@@ -34,5 +34,9 @@
         super.onSizeChanged(w, h, oldw, oldh);
         mTicker.reflowText();
     }
+
+    public void setTicker(Ticker t) {
+        mTicker = t;
+    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java
index 9108eee..c59eb6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java
@@ -26,7 +26,7 @@
 
 public class TrackingView extends LinearLayout {
     final Display mDisplay;
-    StatusBarService mService;
+    PhoneStatusBarService mService;
     boolean mTracking;
     int mStartX, mStartY;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
new file mode 100644
index 0000000..a025d63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tablet;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+import android.util.AttributeSet;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.server.InputMethodManagerService;
+import com.android.systemui.R;
+
+import java.util.List;
+
+public class InputMethodButton extends ImageView {
+
+    private static final String  TAG = "StatusBar/InputMethodButton";
+    private static final boolean DEBUG = false;
+
+    private boolean mKeyboardShown;
+    private ImageView mIcon;
+    // other services we wish to talk to
+    private InputMethodManager mImm;
+
+    public InputMethodButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mKeyboardShown = false;
+        // IME hookup
+        mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+        // TODO: read the current icon & visibility state directly from the service
+
+        // TODO: register for notifications about changes to visibility & subtype from service
+
+        setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mImm.showInputMethodSubtypePicker();
+            }
+        });
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        mIcon = (ImageView) findViewById(R.id.imeButton);
+
+        refreshStatusIcon(mKeyboardShown);
+    }
+
+    private InputMethodInfo getCurrentInputMethodInfo() {
+        String curInputMethodId = Settings.Secure.getString(getContext()
+                .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+        List<InputMethodInfo> imis = mImm.getEnabledInputMethodList();
+        if (curInputMethodId != null) {
+            for (InputMethodInfo imi: imis) {
+                if (imi.getId().equals(curInputMethodId)) {
+                    return imi;
+                }
+            }
+        }
+        return null;
+    }
+
+    private Drawable getCurrentSubtypeIcon() {
+        final PackageManager pm = getContext().getPackageManager();
+        InputMethodInfo imi = getCurrentInputMethodInfo();
+        InputMethodSubtype subtype = mImm.getCurrentInputMethodSubtype();
+        Drawable icon = null;
+        if (imi != null) {
+            if (DEBUG) {
+                Log.d(TAG, "--- Update icons of IME: " + imi.getPackageName() + "," + subtype);
+            }
+            if (subtype != null) {
+                return pm.getDrawable(imi.getPackageName(), subtype.getIconResId(),
+                        imi.getServiceInfo().applicationInfo);
+            } else if (imi.getSubtypes().size() > 0) {
+                return pm.getDrawable(imi.getPackageName(),
+                        imi.getSubtypes().get(0).getIconResId(),
+                        imi.getServiceInfo().applicationInfo);
+            } else {
+                try {
+                    return pm.getApplicationInfo(imi.getPackageName(), 0).loadIcon(pm);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.w(TAG, "Current IME cann't be found: " + imi.getPackageName());
+                }
+            }
+        }
+        return null;
+    }
+
+    private void refreshStatusIcon(boolean keyboardShown) {
+        if (!keyboardShown) {
+            setVisibility(View.INVISIBLE);
+            return;
+        } else {
+            setVisibility(View.VISIBLE);
+        }
+        Drawable icon = getCurrentSubtypeIcon();
+        if (icon == null) {
+            mIcon.setImageResource(R.drawable.ic_sysbar_ime_default);
+        } else {
+            mIcon.setImageDrawable(icon);
+        }
+    }
+
+    public void setIMEButtonVisible(boolean visible) {
+        mKeyboardShown = visible;
+        refreshStatusIcon(mKeyboardShown);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
new file mode 100644
index 0000000..df29f95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tablet;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.ImageView;
+import android.view.MotionEvent;
+
+import com.android.systemui.R;
+
+
+public class NotificationIconArea extends LinearLayout {
+    private static final String TAG = "NotificationIconArea";
+
+    IconLayout mIconLayout;
+
+    public NotificationIconArea(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mIconLayout = (IconLayout)findViewById(R.id.icons);
+    }
+
+    static class IconLayout extends LinearLayout {
+        public IconLayout(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        public boolean onInterceptTouchEvent(MotionEvent e) {
+            return true;
+        }
+    }
+}
+
+
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
new file mode 100644
index 0000000..eee0a16
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tablet;
+
+import android.content.Context;
+import android.widget.LinearLayout;
+import android.util.AttributeSet;
+
+public class NotificationPanel extends LinearLayout implements StatusBarPanel {
+
+    public NotificationPanel(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NotificationPanel(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public boolean isInContentArea(int x, int y) {
+        final int l = getPaddingLeft();
+        final int r = getWidth() - getPaddingRight();
+        final int t = getPaddingTop();
+        final int b = getHeight() - getPaddingBottom();
+        return x >= l && x < r && y >= t && y < b;
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
new file mode 100644
index 0000000..6797958
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/RecentAppsPanel.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tablet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.ActivityManager;
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.net.wifi.WifiManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.BaseAdapter;
+import android.widget.Gallery;
+import android.widget.HorizontalScrollView;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.systemui.R;
+
+public class RecentAppsPanel extends LinearLayout implements StatusBarPanel, OnClickListener {
+    private static final String TAG = "RecentAppsPanel";
+    private static final boolean DEBUG = TabletStatusBarService.DEBUG;
+    private static final int MAX_RECENT_TASKS = 20;
+    private static final float ITEM_WIDTH = 75;
+    private static final float ITEM_HEIGHT = 75;
+    private TabletStatusBarService mBar;
+    private TextView mNoRecents;
+    private LinearLayout mRecentsContainer;
+    private float mDensity;
+    private HorizontalScrollView mScrollView;
+
+    public boolean isInContentArea(int x, int y) {
+        final int l = getPaddingLeft();
+        final int r = getWidth() - getPaddingRight();
+        final int t = getPaddingTop();
+        final int b = getHeight() - getPaddingBottom();
+        return x >= l && x < r && y >= t && y < b;
+    }
+
+    public void setBar(TabletStatusBarService bar) {
+        mBar = bar;
+    }
+
+    public RecentAppsPanel(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public RecentAppsPanel(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        mDensity = getResources().getDisplayMetrics().density;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mNoRecents = (TextView) findViewById(R.id.recents_no_recents);
+        mRecentsContainer = (LinearLayout) findViewById(R.id.recents_container);
+        mScrollView = (HorizontalScrollView) findViewById(R.id.scroll_view);
+        mScrollView.setHorizontalFadingEdgeEnabled(true);
+    }
+
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        Log.v(TAG, "onVisibilityChanged(" + changedView + ", " + visibility + ")");
+        if (visibility == View.VISIBLE && changedView == this) {
+            refreshIcons();
+            mRecentsContainer.setScrollbarFadingEnabled(true);
+            mRecentsContainer.scrollTo(0, 0);
+        }
+    }
+
+    private void refreshIcons() {
+        mRecentsContainer.removeAllViews();
+        final Context context = getContext();
+        final PackageManager pm = context.getPackageManager();
+        final ActivityManager am = (ActivityManager)
+                context.getSystemService(Context.ACTIVITY_SERVICE);
+        final List<ActivityManager.RecentTaskInfo> recentTasks =
+                am.getRecentTasks(MAX_RECENT_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+
+        ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .resolveActivityInfo(pm, 0);
+
+        int numTasks = recentTasks.size();
+        final int width = (int) (mDensity * ITEM_WIDTH + 0.5f);
+        final int height = (int) (mDensity * ITEM_HEIGHT + 0.5f);
+        ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(width, height);
+        for (int i = 0; i < numTasks; ++i) {
+            final ActivityManager.RecentTaskInfo info = recentTasks.get(i);
+
+            Intent intent = new Intent(info.baseIntent);
+            if (info.origActivity != null) {
+                intent.setComponent(info.origActivity);
+            }
+
+            // Exclude home activity.
+            if (homeInfo != null
+                    && homeInfo.packageName.equals(intent.getComponent().getPackageName())
+                    && homeInfo.name.equals(intent.getComponent().getClassName())) {
+                    continue;
+            }
+
+            intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+                    | Intent.FLAG_ACTIVITY_NEW_TASK);
+            final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
+            if (resolveInfo != null) {
+                final ActivityInfo activityInfo = resolveInfo.activityInfo;
+                final String title = activityInfo.loadLabel(pm).toString();
+                Drawable icon = activityInfo.loadIcon(pm);
+
+                if (title != null && title.length() > 0 && icon != null) {
+                    ImageView imageView = new ImageView(mContext);
+                    imageView.setScaleType(ImageView.ScaleType.FIT_XY);
+                    imageView.setLayoutParams(layoutParams);
+                    imageView.setOnClickListener(this);
+                    imageView.setTag(intent);
+                    imageView.setImageDrawable(icon);
+                    mRecentsContainer.addView(imageView);
+                }
+            }
+        }
+
+        int views = mRecentsContainer.getChildCount();
+        mNoRecents.setVisibility(views == 0 ? View.VISIBLE : View.GONE);
+        mRecentsContainer.setVisibility(views > 0 ? View.VISIBLE : View.GONE);
+    }
+
+    public void onClick(View v) {
+        Intent intent = (Intent) v.getTag();
+        if (DEBUG) Log.v(TAG, "Starting activity " + intent);
+        getContext().startActivity(intent);
+        mBar.animateCollapse();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
new file mode 100644
index 0000000..7ee3c19
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/ShirtPocket.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tablet;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.util.Slog;
+import android.view.View;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.view.DragEvent;
+import android.view.MotionEvent;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.graphics.Paint;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.view.WindowManagerImpl;
+import android.graphics.PixelFormat;
+import android.view.Gravity;
+
+import com.android.systemui.R;
+
+public class ShirtPocket extends FrameLayout {
+    private static final boolean DEBUG = false;
+    private static final String  TAG = "StatusBar/ShirtPocket";
+
+    private ClipData mClipping = null;
+
+    private View mWindow = null;
+    private ImageView mIcon;
+    private ImageView mPreviewIcon;
+    private TextView mDescription;
+    private TextView mAltText;
+
+    public ShirtPocket(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setupWindow();
+    }
+
+    // TODO: "pin area" panel, dragging things out
+    ObjectAnimator mAnimHide, mAnimShow;
+    
+    protected void onAttachedToWindow() {
+        // Drag API notes: we must be visible to receive drag events
+        setVisibility(View.VISIBLE);
+
+        mIcon = (ImageView) findViewById(R.id.pocket_icon);
+        refreshStatusIcon();
+
+        setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                if (mClipping != null) {
+                    if (mWindow.getVisibility() == View.VISIBLE) hideWindow(); 
+                    else showWindow();
+                }
+            }
+        });
+    }
+
+    private void refreshStatusIcon() {
+        setClickable(mClipping != null);
+        mIcon.setImageResource(mClipping == null
+                ? R.drawable.ic_sysbar_pocket_hidden
+                : R.drawable.ic_sysbar_pocket_holding);
+        mIcon.setVisibility(mClipping == null ? View.INVISIBLE : View.VISIBLE);
+    }
+    
+    private void showWindow() {
+        getHandler().post(new Runnable() {
+            public void run() {
+                mWindow.setVisibility(View.VISIBLE);
+                refreshStatusIcon();
+            }
+        });
+    }
+
+    private void hideWindow() {
+        getHandler().post(new Runnable() {
+            public void run() {
+                mWindow.setVisibility(View.GONE);
+                refreshStatusIcon();
+            }
+        });
+    }
+    
+    private void hideWindowInJustASec() {
+        getHandler().postDelayed(new Runnable() {
+            public void run() {
+                mWindow.setVisibility(View.GONE);
+                refreshStatusIcon();
+            }
+        },
+        250);
+    }
+
+    private void stash(ClipData clipping) {
+        mClipping = clipping;
+        if (mClipping != null) {
+            Bitmap icon = mClipping.getIcon();
+            mDescription.setText(mClipping.getDescription().getLabel());
+            if (icon != null) {
+                mPreviewIcon.setImageBitmap(icon);
+                mPreviewIcon.setVisibility(View.VISIBLE);
+                mAltText.setVisibility(View.GONE);
+            } else {
+                mPreviewIcon.setVisibility(View.GONE);
+                mAltText.setVisibility(View.VISIBLE);
+                if (mClipping.getItemCount() > 0) {
+                    // TODO: figure out how to visualize every kind of ClipData!
+                    mAltText.setText(mClipping.getItem(0).coerceToText(getContext()));
+                }
+            }
+        }
+    }
+
+    private boolean isInViewContentArea(View v, int x, int y) {
+        final int l = v.getPaddingLeft();
+        final int r = v.getWidth() - v.getPaddingRight();
+        final int t = v.getPaddingTop();
+        final int b = v.getHeight() - v.getPaddingBottom();
+        return x >= l && x < r && y >= t && y < b;
+    }
+
+    View.OnTouchListener mWindowTouchListener = new View.OnTouchListener() {
+        public boolean onTouch(View v, MotionEvent ev) {
+            final int action = ev.getAction();
+            if (action == MotionEvent.ACTION_OUTSIDE
+                    || (action == MotionEvent.ACTION_DOWN
+                        && !isInViewContentArea(mWindow, (int)ev.getX(), (int)ev.getY()))) {
+                hideWindow();
+                return true;
+            } else if (action == MotionEvent.ACTION_DOWN) {
+                Slog.d(TAG, "ACTION_DOWN");
+                final ClipData clip = mClipping;
+                if (clip != null) {
+                    final Bitmap icon = clip.getIcon();
+                    DragThumbnailBuilder thumb;
+                    if (icon != null) {
+                        thumb = new DragThumbnailBuilder(v) {
+                            public void onProvideThumbnailMetrics(Point thumbnailSize, Point thumbnailTouchPoint) {
+                                thumbnailSize.set(icon.getWidth(), icon.getHeight());
+                                thumbnailTouchPoint.set(thumbnailSize.x / 2, thumbnailSize.y / 2);
+                            }
+                            public void onDrawThumbnail(Canvas canvas) {
+                                canvas.drawBitmap(icon, 0, 0, new Paint());
+                            }
+                        };
+                    } else {
+                        // uhhh, what now?
+                        thumb = new DragThumbnailBuilder(mWindow.findViewById(R.id.preview));
+                    }
+
+                    v.startDrag(clip, thumb, false);
+
+                    // TODO: only discard the clipping if it was accepted
+                    stash(null);
+
+                    hideWindowInJustASec(); // will refresh the icon
+
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
+    private void setupWindow() {
+        mWindow = View.inflate(getContext(), R.layout.sysbar_panel_pocket, null);
+
+        mPreviewIcon = (ImageView) mWindow.findViewById(R.id.icon);
+        mDescription = (TextView) mWindow.findViewById(R.id.description);
+        mAltText = (TextView) mWindow.findViewById(R.id.alt);
+
+        mWindow.setVisibility(View.GONE);
+        mWindow.setOnTouchListener(mWindowTouchListener);
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                400,
+                250,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                PixelFormat.TRANSLUCENT);
+        lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+//        int pos[] = new int[2];
+//        getLocationOnScreen(pos);
+//        lp.x = pos[1];
+//        lp.y = 0;
+        lp.setTitle("ShirtPocket");
+        lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard;
+
+        WindowManagerImpl.getDefault().addView(mWindow, lp);
+
+    }
+
+    public boolean onDragEvent(DragEvent event) {
+        if (DEBUG) Slog.d(TAG, "onDragEvent: " + event);
+        if (mIcon != null) {
+            switch (event.getAction()) {
+                // We want to appear whenever a potential drag takes off from anywhere in the UI.
+                case DragEvent.ACTION_DRAG_STARTED:
+                    mIcon.setImageResource(mClipping == null
+                            ? R.drawable.ic_sysbar_pocket
+                            : R.drawable.ic_sysbar_pocket_holding);
+                    mIcon.setVisibility(View.VISIBLE);
+                    break;
+                case DragEvent.ACTION_DRAG_ENTERED:
+                    if (DEBUG) Slog.d(TAG, "entered!");
+                    mIcon.setImageResource(R.drawable.ic_sysbar_pocket_drag);
+                    break;
+                case DragEvent.ACTION_DRAG_EXITED:
+                    if (DEBUG) Slog.d(TAG, "exited!");
+                    mIcon.setImageResource(mClipping == null
+                            ? R.drawable.ic_sysbar_pocket
+                            : R.drawable.ic_sysbar_pocket_holding);
+                    break;
+                case DragEvent.ACTION_DROP:
+                    if (DEBUG) Slog.d(TAG, "dropped!");
+                    stash(event.getClipData());
+                    refreshStatusIcon();
+                    break;
+                case DragEvent.ACTION_DRAG_ENDED:
+                    refreshStatusIcon();
+                    break;
+            }
+        }
+        return true; // we want everything, thank you
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/StatusBarPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/StatusBarPanel.java
new file mode 100644
index 0000000..8fa01d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/StatusBarPanel.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tablet;
+
+public interface StatusBarPanel {
+    public boolean isInContentArea(int x, int y);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
new file mode 100644
index 0000000..c9a8d56
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
@@ -0,0 +1,766 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tablet;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.StatusBarManager;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.location.LocationManager;
+import android.media.AudioManager;
+import android.net.NetworkInfo;
+import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.IWindowManager;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextSwitcher;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.List;
+
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.cdma.EriInfo;
+import com.android.internal.telephony.cdma.TtyIntent;
+
+import com.android.server.WindowManagerService;
+
+import com.android.systemui.statusbar.*;
+import com.android.systemui.R;
+
+public class SystemPanel extends LinearLayout implements StatusBarPanel {
+    private static final String TAG = "SystemPanel";
+    private static final boolean DEBUG = TabletStatusBarService.DEBUG;
+    private static final boolean DEBUG_SIGNAL = false;
+
+    private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 5;
+    private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
+    private static final int DEFAULT_BACKLIGHT = (int) (android.os.Power.BRIGHTNESS_ON * 0.4f);
+
+    private TabletStatusBarService mBar;
+    private boolean mAirplaneMode;
+
+    private ImageButton mBrightnessButton;
+    private ImageButton mSoundButton;
+    private ImageButton mOrientationButton;
+    private ImageButton mAirplaneButton;
+    private ImageButton mGpsButton;
+    private ImageButton mBluetoothButton;
+
+    private ImageView mBatteryMeter;
+    private ImageView mSignalMeter;
+
+    private TextView mBatteryText;
+    private TextView mSignalText;
+
+    private final IWindowManager mWM;
+
+    private final AudioManager mAudioManager;
+    private final WifiManager mWifiManager;
+    private final TelephonyManager mPhone;
+    private final BluetoothAdapter mBluetoothAdapter;
+
+    // state trackers for telephony code
+    IccCard.State mSimState = IccCard.State.READY;
+    int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
+    int mDataState = TelephonyManager.DATA_DISCONNECTED;
+    ServiceState mServiceState;
+    SignalStrength mSignalStrength;
+
+    // state for the meters
+    boolean mWifiEnabled, mWifiConnected;
+    int mWifiLevel;
+    String mWifiSsid;
+
+    boolean mDataEnabled, mDataConnected, mDataRoaming;
+    int mDataLevel;
+
+    public boolean isInContentArea(int x, int y) {
+        final int l = getPaddingLeft();
+        final int r = getWidth() - getPaddingRight();
+        final int t = getPaddingTop();
+        final int b = getHeight() - getPaddingBottom();
+        return x >= l && x < r && y >= t && y < b;
+    }
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
+                refreshSound();
+            } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+                updateBattery(intent);
+            } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)
+                    || action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)
+                    || action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)
+                    || action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+                updateWifiState(intent);
+            } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+                updateSimState(intent);
+            } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+                refreshBluetooth();
+            }
+        }
+    };
+
+    private final void updateSimState(Intent intent) {
+        String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
+        if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
+            mSimState = IccCard.State.ABSENT;
+        }
+        else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+            mSimState = IccCard.State.READY;
+        }
+        else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+            final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
+            if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
+                mSimState = IccCard.State.PIN_REQUIRED;
+            }
+            else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+                mSimState = IccCard.State.PUK_REQUIRED;
+            }
+            else {
+                mSimState = IccCard.State.NETWORK_LOCKED;
+            }
+        } else {
+            mSimState = IccCard.State.UNKNOWN;
+        }
+        updateDataState();
+    }
+
+    private boolean isCdma() {
+        return (mSignalStrength != null) && !mSignalStrength.isGsm();
+    }
+
+    private boolean isEvdo() {
+        return ( (mServiceState != null)
+                 && ((mServiceState.getRadioTechnology()
+                        == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
+                     || (mServiceState.getRadioTechnology()
+                        == ServiceState.RADIO_TECHNOLOGY_EVDO_A)
+                     || (mServiceState.getRadioTechnology()
+                        == ServiceState.RADIO_TECHNOLOGY_EVDO_B)));
+    }
+
+    private boolean hasService() {
+        if (mServiceState != null) {
+            switch (mServiceState.getState()) {
+                case ServiceState.STATE_OUT_OF_SERVICE:
+                case ServiceState.STATE_POWER_OFF:
+                    return false;
+                default:
+                    return true;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    private int getCdmaLevel() {
+        if (mSignalStrength == null) return 0;
+        final int cdmaDbm = mSignalStrength.getCdmaDbm();
+        final int cdmaEcio = mSignalStrength.getCdmaEcio();
+        int levelDbm = 0;
+        int levelEcio = 0;
+
+        if (cdmaDbm >= -75) levelDbm = 4;
+        else if (cdmaDbm >= -85) levelDbm = 3;
+        else if (cdmaDbm >= -95) levelDbm = 2;
+        else if (cdmaDbm >= -100) levelDbm = 1;
+        else levelDbm = 0;
+
+        // Ec/Io are in dB*10
+        if (cdmaEcio >= -90) levelEcio = 4;
+        else if (cdmaEcio >= -110) levelEcio = 3;
+        else if (cdmaEcio >= -130) levelEcio = 2;
+        else if (cdmaEcio >= -150) levelEcio = 1;
+        else levelEcio = 0;
+
+        return (levelDbm < levelEcio) ? levelDbm : levelEcio;
+    }
+
+    private int getEvdoLevel() {
+        if (mSignalStrength == null) return 0;
+        int evdoDbm = mSignalStrength.getEvdoDbm();
+        int evdoSnr = mSignalStrength.getEvdoSnr();
+        int levelEvdoDbm = 0;
+        int levelEvdoSnr = 0;
+
+        if (evdoDbm >= -65) levelEvdoDbm = 4;
+        else if (evdoDbm >= -75) levelEvdoDbm = 3;
+        else if (evdoDbm >= -90) levelEvdoDbm = 2;
+        else if (evdoDbm >= -105) levelEvdoDbm = 1;
+        else levelEvdoDbm = 0;
+
+        if (evdoSnr >= 7) levelEvdoSnr = 4;
+        else if (evdoSnr >= 5) levelEvdoSnr = 3;
+        else if (evdoSnr >= 3) levelEvdoSnr = 2;
+        else if (evdoSnr >= 1) levelEvdoSnr = 1;
+        else levelEvdoSnr = 0;
+
+        return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+    }
+
+    private void updateDataState() {
+        mDataConnected = hasService() && (mDataState == TelephonyManager.DATA_CONNECTED);
+
+        if (isCdma()) {
+            // these functions return a value from 0 to 4, inclusive
+            if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){
+                mDataLevel = getEvdoLevel() * 25;
+            } else {
+                mDataLevel = getCdmaLevel() * 25;
+            }
+        } else {
+            // GSM
+            
+            int asu = (mSignalStrength == null) ? 0 : mSignalStrength.getGsmSignalStrength();
+
+            // asu on [0,31]; 99 = unknown
+            // Android has historically shown anything >=12 as "full"
+            // XXX: tune this based on Industry Best Practices(TM)
+            if (asu <= 2 || asu == 99) mDataLevel = 0;
+            else mDataLevel = (int)(((float)Math.max(asu, 15) / 15) * 100);
+
+            mDataRoaming = mPhone.isNetworkRoaming();
+
+            mDataConnected = mDataConnected
+                && (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN);
+        }
+
+        if (DEBUG_SIGNAL || DEBUG) {
+            Slog.d(TAG, "updateDataState: connected=" + mDataConnected 
+                    + " level=" + mDataLevel
+                    + " isEvdo=" + isEvdo()
+                    + " isCdma=" + isCdma()
+                    + " mPhoneState=" + mPhoneState
+                    + " mDataState=" + mDataState
+                    );
+        }
+
+        refreshSignalMeters();
+    }
+
+    private void updateWifiState(Intent intent) {
+        if (DEBUG)
+            Slog.d(TAG, "updateWifiState: " + intent);
+
+        final String action = intent.getAction();
+        final boolean wasConnected = mWifiConnected;
+
+        if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+            mWifiEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                    WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
+        } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+            final NetworkInfo networkInfo = (NetworkInfo)
+                    intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+            mWifiConnected = networkInfo != null && networkInfo.isConnected();
+        } else if (action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
+            final NetworkInfo.DetailedState detailedState = WifiInfo.getDetailedStateOf(
+                    (SupplicantState)intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE));
+            mWifiConnected = detailedState == NetworkInfo.DetailedState.CONNECTED;
+        } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+            final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
+            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 101);
+            mWifiLevel = mWifiConnected ? newSignalLevel : 0;
+        }
+
+        if (mWifiConnected && !wasConnected) {
+            WifiInfo info = mWifiManager.getConnectionInfo();
+            if (DEBUG)
+                Slog.d(TAG, "updateWifiState: just connected: info=" + info);
+
+            if (info != null) {
+                // grab the initial signal strength
+                mWifiLevel = WifiManager.calculateSignalLevel(info.getRssi(), 101);
+
+                // find the SSID
+                mWifiSsid = info.getSSID();
+                if (mWifiSsid == null) {
+                    // OK, it's not in the connectionInfo; we have to go hunting for it
+                    List<WifiConfiguration> networks = mWifiManager.getConfiguredNetworks();
+                    for (WifiConfiguration net : networks) {
+                        if (net.networkId == info.getNetworkId()) {
+                            mWifiSsid = net.SSID;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        refreshSignalMeters();
+    }
+
+    // figure out what to show: first wifi, then 3G, then nothing
+    void refreshSignalMeters() {
+        if (mSignalMeter == null) return; // no UI yet
+
+        Context ctxt = getContext();
+
+        String text = null;
+        int level = 0;
+
+        if (mWifiConnected) {
+            if (mWifiSsid == null) {
+                text = ctxt.getString(R.string.system_panel_signal_meter_wifi_nossid);
+            } else {
+                text = ctxt.getString(R.string.system_panel_signal_meter_wifi_ssid_format,
+                                      mWifiSsid);
+            }
+            level = mWifiLevel;
+        } else if (mDataConnected) {
+            text = ctxt.getString(R.string.system_panel_signal_meter_data_connected);
+            level = mDataLevel;
+        } else {
+            text = ctxt.getString(R.string.system_panel_signal_meter_disconnected);
+            level = 0;
+        }
+
+        mSignalMeter.setImageResource(R.drawable.sysbar_signal);
+        mSignalMeter.setImageLevel(level);
+        mSignalText.setText(text);
+
+        // hack for now
+        mBar.setSignalMeter(level, mWifiConnected);
+    }
+
+    public void setBar(TabletStatusBarService bar) {
+        mBar = bar;
+    }
+
+    public void updateBattery(Intent intent) {
+        final int level = intent.getIntExtra("level", 0);
+        final boolean plugged = intent.getIntExtra("plugged", 0) != 0;
+
+        mBatteryMeter.setImageResource(R.drawable.sysbar_battery);
+        mBatteryMeter.setImageLevel(level);
+        mBatteryText.setText(getContext()
+                .getString(R.string.system_panel_battery_meter_format, level));
+
+        // hack for now
+        mBar.setBatteryMeter(level, plugged);
+    }
+    
+    public SystemPanel(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SystemPanel(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        // our mighty overlord
+        mWM = IWindowManager.Stub.asInterface(
+                    ServiceManager.getService("window"));
+
+
+        // get notified of phone state changes
+        TelephonyManager telephonyManager =
+                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        telephonyManager.listen(mPhoneStateListener,
+                          PhoneStateListener.LISTEN_SERVICE_STATE
+                        | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
+                        | PhoneStateListener.LISTEN_CALL_STATE
+                        | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+                        | PhoneStateListener.LISTEN_DATA_ACTIVITY);
+
+        // wifi status info
+        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+        
+        // audio status 
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+
+        // mobile data 
+        mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+
+        // Bluetooth
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+    }
+
+    public void onAttachedToWindow() {
+        DateView date = (DateView)findViewById(R.id.date);
+        date.setUpdates(true);
+
+        TextView settingsButton = (TextView)findViewById(R.id.settings_button);
+        settingsButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                getContext().startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS)
+                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+                mBar.animateCollapse();
+            }});
+
+        mBrightnessButton = (ImageButton)findViewById(R.id.brightness);
+        mBrightnessButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                rotateBrightness();
+            }
+        });
+
+        mSoundButton = (ImageButton)findViewById(R.id.sound);
+        mSoundButton.setAlpha(getSilentMode() ? 0x7F : 0xFF);
+        mSoundButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                setSilentMode(!getSilentMode());
+                mSoundButton.setAlpha(getSilentMode() ? 0x7F : 0xFF);
+            }
+        });
+        mOrientationButton = (ImageButton)findViewById(R.id.orientation);
+        mOrientationButton.setImageResource(
+            getAutoRotate()
+                ? R.drawable.ic_sysbar_rotate_on
+                : R.drawable.ic_sysbar_rotate_off);
+        mOrientationButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                setAutoRotate(!getAutoRotate());
+                mOrientationButton.setImageResource(
+                    getAutoRotate()
+                        ? R.drawable.ic_sysbar_rotate_on
+                        : R.drawable.ic_sysbar_rotate_off);
+                Toast.makeText(getContext(), 
+                    getAutoRotate() 
+                        ? R.string.toast_rotation_free
+                        : R.string.toast_rotation_locked,
+                    Toast.LENGTH_SHORT).show();
+            }
+        });
+
+        mAirplaneButton = (ImageButton)findViewById(R.id.airplane);
+        mAirplaneButton.setAlpha(mAirplaneMode ? 0xFF : 0x7F);
+        mAirplaneButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                boolean newMode = !getAirplaneMode();
+                Toast.makeText(getContext(), "Attempting to turn "
+                    + (newMode ? "on" : "off") + " airplane mode (flaky).",
+                    Toast.LENGTH_SHORT).show();
+                setAirplaneMode(newMode);
+            }
+        });
+
+        mGpsButton = (ImageButton)findViewById(R.id.gps);
+        mGpsButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                toggleGps();
+                refreshGps();
+            }
+        });
+
+        mBluetoothButton = (ImageButton)findViewById(R.id.bluetooth);
+        mBluetoothButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                toggleBluetooth();
+                refreshBluetooth();
+            }
+        });
+
+        // register for broadcasts
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        getContext().registerReceiver(mReceiver, filter);
+        
+        mBatteryMeter = (ImageView)findViewById(R.id.battery_meter);
+        mBatteryMeter.setImageResource(R.drawable.sysbar_battery);
+        mBatteryMeter.setImageLevel(0);
+        mSignalMeter = (ImageView)findViewById(R.id.signal_meter);
+        mBatteryMeter.setImageResource(R.drawable.sysbar_signal);
+        mBatteryMeter.setImageLevel(0);
+
+        mBatteryText = (TextView)findViewById(R.id.battery_info);
+        mSignalText = (TextView)findViewById(R.id.signal_info);
+
+        refreshSignalMeters();
+        refreshBluetooth();
+        refreshGps();
+    }
+
+    public void onDetachedFromWindow() {
+        getContext().unregisterReceiver(mReceiver);
+    }
+
+    // ----------------------------------------------------------------------
+
+//    private boolean isAutoBrightness() {
+//        Context context = getContext();
+//        try {
+//            IPowerManager power = IPowerManager.Stub.asInterface(
+//                    ServiceManager.getService("power"));
+//            if (power != null) {
+//                int brightnessMode = Settings.System.getInt(context.getContentResolver(),
+//                        Settings.System.SCREEN_BRIGHTNESS_MODE);
+//                return brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
+//            }
+//        } catch (RemoteException e) {
+//        } catch (Settings.SettingNotFoundException e) {
+//        }
+//        return false;
+//    }
+
+    private void rotateBrightness() {
+        int icon = R.drawable.ic_sysbar_brightness;
+        int bg = R.drawable.sysbar_toggle_bg_on;
+        Context context = getContext();
+        try {
+            IPowerManager power = IPowerManager.Stub.asInterface(
+                    ServiceManager.getService("power"));
+            if (power != null) {
+                ContentResolver cr = context.getContentResolver();
+                int brightness = Settings.System.getInt(cr,
+                        Settings.System.SCREEN_BRIGHTNESS);
+                int brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
+                //Only get brightness setting if available
+                if (context.getResources().getBoolean(
+                        com.android.internal.R.bool.config_automatic_brightness_available)) {
+                    brightnessMode = Settings.System.getInt(cr,
+                            Settings.System.SCREEN_BRIGHTNESS_MODE);
+                }
+
+                // Rotate AUTO -> MINIMUM -> DEFAULT -> MAXIMUM
+                // Technically, not a toggle...
+                if (brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
+                    brightness = MINIMUM_BACKLIGHT;
+                    icon = R.drawable.ic_sysbar_brightness_low;
+                    brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
+                } else if (brightness < DEFAULT_BACKLIGHT) {
+                    brightness = DEFAULT_BACKLIGHT;
+                } else if (brightness < MAXIMUM_BACKLIGHT) {
+                    brightness = MAXIMUM_BACKLIGHT;
+                } else {
+                    brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
+                    brightness = MINIMUM_BACKLIGHT;
+                    icon = R.drawable.ic_sysbar_brightness_auto;
+                }
+
+                if (context.getResources().getBoolean(
+                        com.android.internal.R.bool.config_automatic_brightness_available)) {
+                    // Set screen brightness mode (automatic or manual)
+                    Settings.System.putInt(context.getContentResolver(),
+                            Settings.System.SCREEN_BRIGHTNESS_MODE,
+                            brightnessMode);
+                } else {
+                    // Make sure we set the brightness if automatic mode isn't available
+                    brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
+                }
+                if (brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL) {
+                    power.setBacklightBrightness(brightness);
+                    Settings.System.putInt(cr, Settings.System.SCREEN_BRIGHTNESS, brightness);
+                }
+            }
+        } catch (RemoteException e) {
+        } catch (Settings.SettingNotFoundException e) {
+        }
+
+        mBrightnessButton.setImageResource(icon);
+        mBrightnessButton.setBackgroundResource(bg);
+    }
+
+    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onServiceStateChanged(ServiceState serviceState) {
+            if (DEBUG_SIGNAL || DEBUG) {
+                Slog.d(TAG, "phone service state changed: " + serviceState.getState());
+            }
+            mServiceState = serviceState;
+            mAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
+            if (mAirplaneButton != null) {
+                mAirplaneButton.setImageResource(mAirplaneMode 
+                                                 ? R.drawable.ic_sysbar_airplane_on
+                                                 : R.drawable.ic_sysbar_airplane_off);
+                mAirplaneButton.setBackgroundResource(mAirplaneMode 
+                                                 ? R.drawable.sysbar_toggle_bg_on
+                                                 : R.drawable.sysbar_toggle_bg_off);
+            }
+            updateDataState();
+        }
+        @Override
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            if (DEBUG_SIGNAL || DEBUG) {
+                Slog.d(TAG, "onSignalStrengthsChanged: " + signalStrength);
+            }
+            mSignalStrength = signalStrength;
+            updateDataState();
+        }
+        @Override
+        public void onCallStateChanged(int state, String incomingNumber) {
+            mPhoneState = state;
+            // In cdma, if a voice call is made, RSSI should switch to 1x.
+            if (isCdma()) {
+                updateDataState();
+            }
+        }
+
+        @Override
+        public void onDataConnectionStateChanged(int state, int networkType) {
+            if (DEBUG_SIGNAL || DEBUG) {
+                Slog.d(TAG, "onDataConnectionStateChanged: state=" + state 
+                        + " type=" + networkType);
+            }
+            mDataState = state;
+//            updateDataNetType(networkType);
+            updateDataState();
+        }
+    };
+
+    private boolean getAirplaneMode() {
+        return mAirplaneMode;
+    }
+
+    private void setAirplaneMode(boolean on) {
+        Settings.System.putInt(
+                mContext.getContentResolver(),
+                Settings.System.AIRPLANE_MODE_ON,
+                on ? 1 : 0);
+        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        intent.putExtra("state", on);
+        getContext().sendBroadcast(intent);
+    }
+
+    boolean getSilentMode() {
+        return mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+    }
+
+    void setSilentMode(boolean on) {
+        if (on) {
+            mAudioManager.setRingerMode((Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.VIBRATE_IN_SILENT, 1) == 1)
+                ? AudioManager.RINGER_MODE_VIBRATE
+                : AudioManager.RINGER_MODE_SILENT);
+        } else {
+            mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+        }
+    }
+
+    void refreshSound() {
+        boolean silent = getSilentMode();
+        mSoundButton.setImageResource(!silent 
+                                         ? R.drawable.ic_sysbar_sound_on
+                                         : R.drawable.ic_sysbar_sound_off);
+        mSoundButton.setBackgroundResource(!silent 
+                                         ? R.drawable.sysbar_toggle_bg_on
+                                         : R.drawable.sysbar_toggle_bg_off);
+    }
+
+    void toggleBluetooth() {
+        if (mBluetoothAdapter == null) return;
+        if (mBluetoothAdapter.isEnabled()) {
+            mBluetoothAdapter.disable();
+        } else {
+            mBluetoothAdapter.enable();
+        }
+    }
+
+    void refreshBluetooth() {
+        boolean on = mBluetoothAdapter != null && mBluetoothAdapter.isEnabled();
+        mBluetoothButton.setImageResource(on ? R.drawable.ic_sysbar_bluetooth_on
+                                             : R.drawable.ic_sysbar_bluetooth_off);
+        mBluetoothButton.setBackgroundResource(on
+                                         ? R.drawable.sysbar_toggle_bg_on
+                                         : R.drawable.sysbar_toggle_bg_off);
+    }
+
+    private boolean isGpsEnabled() {
+        ContentResolver res = mContext.getContentResolver();
+        return Settings.Secure.isLocationProviderEnabled(
+                                res, LocationManager.GPS_PROVIDER);
+    }
+
+    private void toggleGps() {
+        Settings.Secure.setLocationProviderEnabled(mContext.getContentResolver(),
+                LocationManager.GPS_PROVIDER, !isGpsEnabled());
+    }
+
+    private void refreshGps() {
+        boolean on = isGpsEnabled();
+        mGpsButton.setImageResource(on ? R.drawable.ic_sysbar_gps_on
+                                       : R.drawable.ic_sysbar_gps_off);
+        mGpsButton.setBackgroundResource(on
+                                         ? R.drawable.sysbar_toggle_bg_on
+                                         : R.drawable.sysbar_toggle_bg_off);
+    }
+
+    void setAutoRotate(boolean rot) {
+        try {
+            ContentResolver cr = getContext().getContentResolver();
+            if (rot) {
+                mWM.thawRotation();
+            } else {
+                mWM.freezeRotation();
+            }
+        } catch (android.os.RemoteException exc) {
+        }
+    }
+
+    boolean getAutoRotate() {
+        ContentResolver cr = getContext().getContentResolver();
+        return 1 == Settings.System.getInt(cr,
+                Settings.System.ACCELEROMETER_ROTATION,
+                1);
+    }
+
+    int getDisplayRotation() {
+        try {
+            return mWM.getRotation();
+        } catch (android.os.RemoteException exc) {
+            return 0;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
new file mode 100644
index 0000000..29f0fe2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -0,0 +1,1241 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tablet;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
+import android.app.ActivityManagerNative;
+import android.app.PendingIntent;
+import android.app.Notification;
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextSwitcher;
+import android.widget.TextView;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarNotification;
+
+import com.android.systemui.statusbar.*;
+import com.android.systemui.recent.RecentApplicationsActivity;
+import com.android.systemui.R;
+
+public class TabletStatusBarService extends StatusBarService {
+    public static final boolean DEBUG = false;
+    public static final String TAG = "TabletStatusBarService";
+
+    public static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
+    public static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
+    public static final int MSG_OPEN_NOTIFICATION_PEEK = 1002;
+    public static final int MSG_CLOSE_NOTIFICATION_PEEK = 1003;
+    public static final int MSG_OPEN_SYSTEM_PANEL = 1010;
+    public static final int MSG_CLOSE_SYSTEM_PANEL = 1011;
+    public static final int MSG_OPEN_RECENTS_PANEL = 1020;
+    public static final int MSG_CLOSE_RECENTS_PANEL = 1021;
+    public static final int MSG_LIGHTS_ON = 1030;
+    public static final int MSG_LIGHTS_OUT = 1031;
+
+    private static final int MAX_IMAGE_LEVEL = 10000;
+    private static final boolean USE_2D_RECENTS = true;
+
+    int mIconSize;
+
+    H mHandler = new H();
+
+    // tracking all current notifications
+    private NotificationData mNotns = new NotificationData();
+
+    TabletStatusBarView mStatusBarView;
+    ImageView mNotificationTrigger;
+    NotificationIconArea mNotificationIconArea;
+    View mNotificationButtons;
+    View mSystemInfo;
+    View mNavigationArea;
+    View mMenuButton;
+    View mRecentButton;
+
+    InputMethodButton mInputMethodButton;
+
+    NotificationPanel mNotificationPanel;
+    SystemPanel mSystemPanel;
+    NotificationPanel mNotificationPeekWindow;
+    ViewGroup mNotificationPeekRow;
+    int mNotificationPeekIndex;
+    LayoutTransition mNotificationPeekScrubLeft, mNotificationPeekScrubRight;
+
+    int mNotificationPeekTapDuration;
+    int mNotificationFlingVelocity;
+
+    ViewGroup mPile;
+    TextView mClearButton;
+    TextView mDoNotDisturbButton;
+
+    ImageView mBatteryMeter;
+    ImageView mSignalMeter;
+    ImageView mSignalIcon;
+
+    View mBarContents;
+    View mCurtains;
+
+    NotificationIconArea.IconLayout mIconLayout;
+
+    TabletTicker mTicker;
+    View mTickerView;
+    boolean mTicking;
+
+    // for disabling the status bar
+    int mDisabled = 0;
+
+    boolean mNotificationsOn = true;
+    private RecentAppsPanel mRecentsPanel;
+
+    protected void addPanelWindows() {
+        final Context context = mContext;
+
+        final Resources res = context.getResources();
+        final int barHeight= res.getDimensionPixelSize(
+            com.android.internal.R.dimen.status_bar_height);
+
+        // Notification Panel
+        mNotificationPanel = (NotificationPanel)View.inflate(context,
+                R.layout.sysbar_panel_notifications, null);
+        mNotificationPanel.setVisibility(View.GONE);
+        mNotificationPanel.setOnTouchListener(
+                new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PANEL, mNotificationPanel));
+
+        mStatusBarView.setIgnoreChildren(0, mNotificationTrigger, mNotificationPanel);
+
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                512, // ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                PixelFormat.TRANSLUCENT);
+        lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+        lp.setTitle("NotificationPanel");
+        lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard;
+
+        WindowManagerImpl.getDefault().addView(mNotificationPanel, lp);
+
+        // Notification preview window
+        mNotificationPeekWindow = (NotificationPanel) View.inflate(context,
+                R.layout.sysbar_panel_notification_peek, null);
+        mNotificationPeekRow = (ViewGroup) mNotificationPeekWindow.findViewById(R.id.content);
+        mNotificationPeekWindow.setVisibility(View.GONE);
+        mNotificationPeekWindow.setOnTouchListener(
+                new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PANEL, mNotificationPeekWindow));
+        mNotificationPeekScrubRight = new LayoutTransition();
+        mNotificationPeekScrubRight.setAnimator(LayoutTransition.APPEARING, 
+                ObjectAnimator.ofInt(null, "left", -512, 0));
+        mNotificationPeekScrubRight.setAnimator(LayoutTransition.DISAPPEARING, 
+                ObjectAnimator.ofInt(null, "left", -512, 0));
+        mNotificationPeekScrubRight.setDuration(500);
+
+        mNotificationPeekScrubLeft = new LayoutTransition();
+        mNotificationPeekScrubLeft.setAnimator(LayoutTransition.APPEARING, 
+                ObjectAnimator.ofInt(null, "left", 512, 0));
+        mNotificationPeekScrubLeft.setAnimator(LayoutTransition.DISAPPEARING, 
+                ObjectAnimator.ofInt(null, "left", 512, 0));
+        mNotificationPeekScrubLeft.setDuration(500);
+
+        // XXX: setIgnoreChildren?
+        lp = new WindowManager.LayoutParams(
+                512, // ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                PixelFormat.TRANSLUCENT);
+        lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+        lp.setTitle("NotificationPeekWindow");
+        lp.windowAnimations = com.android.internal.R.style.Animation_Toast;
+
+        WindowManagerImpl.getDefault().addView(mNotificationPeekWindow, lp);
+
+        // System Panel
+        mSystemPanel = (SystemPanel) View.inflate(context, R.layout.sysbar_panel_system, null);
+        mSystemPanel.setVisibility(View.GONE);
+        mSystemPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_SYSTEM_PANEL,
+                mSystemPanel));
+        mStatusBarView.setIgnoreChildren(1, mSystemInfo, mSystemPanel);
+
+        lp = new WindowManager.LayoutParams(
+                800,
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                PixelFormat.TRANSLUCENT);
+        lp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+        lp.setTitle("SystemPanel");
+        lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard;
+
+        WindowManagerImpl.getDefault().addView(mSystemPanel, lp);
+        mSystemPanel.setBar(this);
+
+
+        // Recents Panel
+        if (USE_2D_RECENTS) {
+            mRecentsPanel = (RecentAppsPanel) View.inflate(context, R.layout.sysbar_panel_recent,
+                    null);
+            mRecentsPanel.setVisibility(View.GONE);
+            mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
+                    mRecentsPanel));
+            mStatusBarView.setIgnoreChildren(2, mRecentButton, mRecentsPanel);
+
+            lp = new WindowManager.LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT,
+                    ViewGroup.LayoutParams.WRAP_CONTENT,
+                    WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                    PixelFormat.TRANSLUCENT);
+            lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
+            lp.setTitle("RecentsPanel");
+            lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard;
+
+            WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
+            mRecentsPanel.setBar(this);
+        }
+    }
+
+    @Override
+    public void start() {
+        super.start(); // will add the main bar view
+    }
+
+    protected View makeStatusBarView() {
+        final Context context = mContext;
+        final Resources res = context.getResources();
+
+        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
+
+        final TabletStatusBarView sb = (TabletStatusBarView)View.inflate(
+                context, R.layout.status_bar, null);
+        mStatusBarView = sb;
+
+        sb.setHandler(mHandler);
+
+        mBarContents = sb.findViewById(R.id.bar_contents);
+        mCurtains = sb.findViewById(R.id.lights_out);
+        mSystemInfo = sb.findViewById(R.id.systemInfo);
+        mRecentButton = sb.findViewById(R.id.recent_apps);
+
+        mSystemInfo.setOnTouchListener(new ClockTouchListener());
+
+        mRecentButton = sb.findViewById(R.id.recent_apps);
+        mRecentButton.setOnClickListener(mOnClickListener);
+
+        SetLightsOnListener on = new SetLightsOnListener(true);
+        mCurtains.setOnClickListener(on);
+        mCurtains.setOnLongClickListener(on);
+
+        // the button to open the notification area
+        mNotificationTrigger = (ImageView) sb.findViewById(R.id.notificationTrigger);
+        mNotificationTrigger.setOnClickListener(mOnClickListener);
+
+        // the more notifications icon
+        mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
+
+        // the clear and dnd buttons
+        mNotificationButtons = sb.findViewById(R.id.notificationButtons);
+        mClearButton = (TextView)mNotificationButtons.findViewById(R.id.clear_all_button);
+        mClearButton.setOnClickListener(mOnClickListener);
+        mDoNotDisturbButton = (TextView)mNotificationButtons.findViewById(R.id.do_not_disturb);
+        mDoNotDisturbButton.setOnClickListener(mOnClickListener);
+
+        // where the icons go
+        mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
+        mIconLayout.setOnTouchListener(new NotificationIconTouchListener());
+
+        ViewConfiguration vc = ViewConfiguration.get(context);
+        mNotificationPeekTapDuration = vc.getTapTimeout();
+        mNotificationFlingVelocity = 300; // px/s
+
+        mTicker = new TabletTicker(context, (FrameLayout)sb.findViewById(R.id.ticker));
+
+        // System info (center)
+        mBatteryMeter = (ImageView) sb.findViewById(R.id.battery);
+        mSignalMeter = (ImageView) sb.findViewById(R.id.signal);
+        mSignalIcon = (ImageView) sb.findViewById(R.id.signal_icon);
+
+        // The navigation buttons
+        mNavigationArea = sb.findViewById(R.id.navigationArea);
+        mMenuButton = mNavigationArea.findViewById(R.id.menu);
+
+        // The bar contents buttons
+        mInputMethodButton = (InputMethodButton) mBarContents.findViewById(R.id.imeButton);
+
+        // set the initial view visibility
+        setAreThereNotifications();
+        refreshNotificationTrigger();
+
+        // Add the windows
+        addPanelWindows();
+
+        mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
+        mPile.removeAllViews();
+
+        ScrollView scroller = (ScrollView)mPile.getParent();
+        scroller.setFillViewport(true);
+
+        return sb;
+    }
+
+    protected int getStatusBarGravity() {
+        return Gravity.BOTTOM | Gravity.FILL_HORIZONTAL;
+    }
+
+    private class H extends Handler {
+        public void handleMessage(Message m) {
+            switch (m.what) {
+                case MSG_OPEN_NOTIFICATION_PEEK:
+                    if (DEBUG) Slog.d(TAG, "opening notification peek window; arg=" + m.arg1);
+                    if (m.arg1 >= 0) {
+                        final int N = mNotns.size();
+                        if (mNotificationPeekIndex < N) {
+                            NotificationData.Entry entry = mNotns.get(N-1-mNotificationPeekIndex);
+                            entry.icon.setBackgroundColor(0);
+                        }
+
+                        final int peekIndex = m.arg1;
+                        if (peekIndex < N) {
+                            Slog.d(TAG, "loading peek: " + peekIndex);
+                            NotificationData.Entry entry = mNotns.get(N-1-peekIndex);
+                            NotificationData.Entry copy = new NotificationData.Entry(
+                                    entry.key, 
+                                    entry.notification, 
+                                    entry.icon);
+                            inflateViews(copy, mNotificationPeekRow);
+
+                            entry.icon.setBackgroundColor(0x20FFFFFF);
+
+//                          mNotificationPeekRow.setLayoutTransition(
+//                              peekIndex < mNotificationPeekIndex 
+//                                  ? mNotificationPeekScrubLeft
+//                                  : mNotificationPeekScrubRight);
+
+                            mNotificationPeekRow.removeAllViews();
+                            mNotificationPeekRow.addView(copy.row);
+
+                            mNotificationPeekWindow.setVisibility(View.VISIBLE);
+                            mNotificationPanel.setVisibility(View.GONE);
+
+                            mNotificationPeekIndex = peekIndex;
+                        }
+                    }
+                    break;
+                case MSG_CLOSE_NOTIFICATION_PEEK:
+                    if (DEBUG) Slog.d(TAG, "closing notification peek window");
+                    mNotificationPeekWindow.setVisibility(View.GONE);
+                    mNotificationPeekRow.removeAllViews();
+                    final int N = mNotns.size();
+                    if (mNotificationPeekIndex < N) {
+                        NotificationData.Entry entry = mNotns.get(N-1-mNotificationPeekIndex);
+                        entry.icon.setBackgroundColor(0);
+                    }
+                    break;
+                case MSG_OPEN_NOTIFICATION_PANEL:
+                    if (DEBUG) Slog.d(TAG, "opening notifications panel");
+                    if (mNotificationPanel.getVisibility() == View.GONE) {
+                        mNotificationPeekWindow.setVisibility(View.GONE);
+
+                        mDoNotDisturbButton.setText(mNotificationsOn
+                                ? R.string.status_bar_do_not_disturb_button
+                                : R.string.status_bar_please_disturb_button);
+                        mNotificationPanel.setVisibility(View.VISIBLE);
+                        setViewVisibility(mNotificationIconArea, View.GONE,
+                                R.anim.notification_icons_out);
+                        setViewVisibility(mNotificationButtons, View.VISIBLE,
+                                R.anim.notification_buttons_in);
+                        refreshNotificationTrigger();
+                    }
+                    break;
+                case MSG_CLOSE_NOTIFICATION_PANEL:
+                    if (DEBUG) Slog.d(TAG, "closing notifications panel");
+                    if (mNotificationPanel.getVisibility() == View.VISIBLE) {
+                        mNotificationPanel.setVisibility(View.GONE);
+                        setViewVisibility(mNotificationIconArea, View.VISIBLE,
+                                R.anim.notification_icons_in);
+                        setViewVisibility(mNotificationButtons, View.GONE,
+                                R.anim.notification_buttons_out);
+                        refreshNotificationTrigger();
+                    }
+                    break;
+                case MSG_OPEN_SYSTEM_PANEL:
+                    if (DEBUG) Slog.d(TAG, "opening system panel");
+                    mSystemPanel.setVisibility(View.VISIBLE);
+                    break;
+                case MSG_CLOSE_SYSTEM_PANEL:
+                    if (DEBUG) Slog.d(TAG, "closing system panel");
+                    mSystemPanel.setVisibility(View.GONE);
+                    break;
+                case MSG_OPEN_RECENTS_PANEL:
+                    if (DEBUG) Slog.d(TAG, "opening recents panel");
+                    if (mRecentsPanel != null) mRecentsPanel.setVisibility(View.VISIBLE);
+                    break;
+                case MSG_CLOSE_RECENTS_PANEL:
+                    if (DEBUG) Slog.d(TAG, "closing recents panel");
+                    if (mRecentsPanel != null) mRecentsPanel.setVisibility(View.GONE);
+                    break;
+                case MSG_LIGHTS_ON:
+                    setViewVisibility(mCurtains, View.GONE, R.anim.lights_out_out);
+                    setViewVisibility(mBarContents, View.VISIBLE, R.anim.status_bar_in);
+                    break;
+                case MSG_LIGHTS_OUT:
+                    animateCollapse();
+                    setViewVisibility(mCurtains, View.VISIBLE, R.anim.lights_out_in);
+                    setViewVisibility(mBarContents, View.GONE, R.anim.status_bar_out);
+                    break;
+            }
+        }
+    }
+
+    public void refreshNotificationTrigger() {
+        if (mNotificationTrigger == null) return;
+
+        int resId;
+        boolean panel = (mNotificationPanel != null 
+                && mNotificationPanel.getVisibility() == View.VISIBLE);
+        if (!mNotificationsOn) {
+            resId = R.drawable.ic_sysbar_noti_dnd;
+        } else if (mNotns.size() > 0) {
+            resId = panel ? R.drawable.ic_sysbar_noti_avail_open : R.drawable.ic_sysbar_noti_avail;
+        } else {
+            resId = panel ? R.drawable.ic_sysbar_noti_none_open : R.drawable.ic_sysbar_noti_none;
+        }
+        mNotificationTrigger.setImageResource(resId);
+    }
+
+    public void setBatteryMeter(int level, boolean plugged) {
+        if (DEBUG) Slog.d(TAG, "battery=" + level + (plugged ? " - plugged" : " - unplugged"));
+        mBatteryMeter.setImageResource(R.drawable.sysbar_batterymini);
+        // adjust percent to permyriad for ClipDrawable's sake
+        mBatteryMeter.setImageLevel(level * (MAX_IMAGE_LEVEL / 100));
+    }
+
+    public void setSignalMeter(int level, boolean isWifi) {
+        if (DEBUG) Slog.d(TAG, "signal=" + level);
+        if (level < 0) {
+            mSignalMeter.setImageDrawable(null);
+            mSignalMeter.setImageLevel(0);
+            mSignalIcon.setImageDrawable(null);
+        } else {
+            mSignalMeter.setImageResource(R.drawable.sysbar_wifimini);
+            // adjust to permyriad
+            mSignalMeter.setImageLevel(level * (MAX_IMAGE_LEVEL / 100));
+            mSignalIcon.setImageResource(isWifi ? R.drawable.ic_sysbar_wifi_mini
+                                                : R.drawable.ic_sysbar_wifi_mini); // XXX
+        }
+    }
+
+    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
+        if (DEBUG) Slog.d(TAG, "addIcon(" + slot + ") -> " + icon);
+    }
+
+    public void updateIcon(String slot, int index, int viewIndex,
+            StatusBarIcon old, StatusBarIcon icon) {
+        if (DEBUG) Slog.d(TAG, "updateIcon(" + slot + ") -> " + icon);
+    }
+
+    public void removeIcon(String slot, int index, int viewIndex) {
+        if (DEBUG) Slog.d(TAG, "removeIcon(" + slot + ")");
+    }
+
+    public void addNotification(IBinder key, StatusBarNotification notification) {
+        if (DEBUG) Slog.d(TAG, "addNotification(" + key + " -> " + notification + ")");
+        addNotificationViews(key, notification);
+
+        boolean immersive = false;
+        try {
+            immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
+            Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
+        } catch (RemoteException ex) {
+        }
+        if (immersive) {
+            // TODO: immersive mode popups for tablet
+        } else if (notification.notification.fullScreenIntent != null) {
+            // not immersive & a full-screen alert should be shown
+            Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;"
+                    + " sending fullScreenIntent");
+            try {
+                notification.notification.fullScreenIntent.send();
+            } catch (PendingIntent.CanceledException e) {
+            }
+        } else {
+            tick(notification);
+        }
+
+        setAreThereNotifications();
+    }
+
+    public void updateNotification(IBinder key, StatusBarNotification notification) {
+        if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ") // TODO");
+
+        final NotificationData.Entry oldEntry = mNotns.findByKey(key);
+        if (oldEntry == null) {
+            Slog.w(TAG, "updateNotification for unknown key: " + key);
+            return;
+        }
+
+        final StatusBarNotification oldNotification = oldEntry.notification;
+        final RemoteViews oldContentView = oldNotification.notification.contentView;
+
+        final RemoteViews contentView = notification.notification.contentView;
+
+        if (false) {
+            Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
+                    + " ongoing=" + oldNotification.isOngoing()
+                    + " expanded=" + oldEntry.expanded
+                    + " contentView=" + oldContentView);
+            Slog.d(TAG, "new notification: when=" + notification.notification.when
+                    + " ongoing=" + oldNotification.isOngoing()
+                    + " contentView=" + contentView);
+        }
+
+        // Can we just reapply the RemoteViews in place?  If when didn't change, the order
+        // didn't change.
+        if (notification.notification.when == oldNotification.notification.when
+                && notification.isOngoing() == oldNotification.isOngoing()
+                && oldEntry.expanded != null
+                && contentView != null
+                && oldContentView != null
+                && contentView.getPackage() != null
+                && oldContentView.getPackage() != null
+                && oldContentView.getPackage().equals(contentView.getPackage())
+                && oldContentView.getLayoutId() == contentView.getLayoutId()) {
+            if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
+            oldEntry.notification = notification;
+            try {
+                // Reapply the RemoteViews
+                contentView.reapply(mContext, oldEntry.content);
+                // update the contentIntent
+                final PendingIntent contentIntent = notification.notification.contentIntent;
+                if (contentIntent != null) {
+                    oldEntry.content.setOnClickListener(new NotificationClicker(contentIntent,
+                                notification.pkg, notification.tag, notification.id));
+                } else {
+                    oldEntry.content.setOnClickListener(null);
+                }
+                // Update the icon.
+                final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
+                        notification.notification.icon, notification.notification.iconLevel,
+                        notification.notification.number);
+                if (!oldEntry.icon.set(ic)) {
+                    handleNotificationError(key, notification, "Couldn't update icon: " + ic);
+                    return;
+                }
+            }
+            catch (RuntimeException e) {
+                // It failed to add cleanly.  Log, and remove the view from the panel.
+                Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
+                removeNotificationViews(key);
+                addNotificationViews(key, notification);
+            }
+        } else {
+            if (DEBUG) Slog.d(TAG, "not reusing notification for key: " + key);
+            removeNotificationViews(key);
+            addNotificationViews(key, notification);
+        }
+        // TODO: ticker; immersive mode
+
+        setAreThereNotifications();
+    }
+
+    public void removeNotification(IBinder key) {
+        if (DEBUG) Slog.d(TAG, "removeNotification(" + key + ") // TODO");
+        removeNotificationViews(key);
+        setAreThereNotifications();
+    }
+
+    public void disable(int state) {
+        int old = mDisabled;
+        int diff = state ^ old;
+        Slog.d(TAG, "disable... old=0x" + Integer.toHexString(old)
+                + " diff=0x" + Integer.toHexString(diff)
+                + " state=0x" + Integer.toHexString(state));
+        mDisabled = state;
+
+        // act accordingly
+        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
+                Slog.d(TAG, "DISABLE_EXPAND: yes");
+                animateCollapse();
+            }
+        }
+        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+                setViewVisibility(mNotificationTrigger, View.GONE,
+                        R.anim.notification_icons_out);
+                setViewVisibility(mNotificationIconArea, View.GONE,
+                        R.anim.notification_icons_out);
+                mTicker.halt();
+            } else {
+                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+                setViewVisibility(mNotificationTrigger, View.VISIBLE,
+                        R.anim.notification_icons_in);
+                setViewVisibility(mNotificationIconArea, View.VISIBLE,
+                        R.anim.notification_icons_in);
+            }
+        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+            if ((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+                mTicker.halt();
+            }
+        }
+        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
+            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
+                Slog.d(TAG, "DISABLE_SYSTEM_INFO: yes");
+                setViewVisibility(mSystemInfo, View.GONE, R.anim.navigation_out);
+            } else {
+                Slog.d(TAG, "DISABLE_SYSTEM_INFO: no");
+                setViewVisibility(mSystemInfo, View.VISIBLE, R.anim.navigation_in);
+            }
+        }
+        if ((diff & StatusBarManager.DISABLE_NAVIGATION) != 0) {
+            if ((state & StatusBarManager.DISABLE_NAVIGATION) != 0) {
+                Slog.d(TAG, "DISABLE_NAVIGATION: yes");
+                setViewVisibility(mNavigationArea, View.GONE, R.anim.navigation_out);
+            } else {
+                Slog.d(TAG, "DISABLE_NAVIGATION: no");
+                setViewVisibility(mNavigationArea, View.VISIBLE, R.anim.navigation_in);
+            }
+        }
+    }
+
+    private boolean hasTicker(Notification n) {
+        return !TextUtils.isEmpty(n.tickerText)
+                || !TextUtils.isEmpty(n.tickerTitle)
+                || !TextUtils.isEmpty(n.tickerSubtitle);
+    }
+
+    private void tick(StatusBarNotification n) {
+        // Don't show the ticker when the windowshade is open.
+        if (mNotificationPanel.getVisibility() == View.VISIBLE) {
+            return;
+        }
+        // Show the ticker if one is requested. Also don't do this
+        // until status bar window is attached to the window manager,
+        // because...  well, what's the point otherwise?  And trying to
+        // run a ticker without being attached will crash!
+        if (hasTicker(n.notification) && mStatusBarView.getWindowToken() != null) {
+            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
+                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
+                mTicker.add(n);
+            }
+        }
+    }
+
+    public void animateExpand() {
+        mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL);
+        mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL);
+    }
+
+    public void animateCollapse() {
+        mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PANEL);
+        mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PANEL);
+        mHandler.removeMessages(MSG_CLOSE_SYSTEM_PANEL);
+        mHandler.sendEmptyMessage(MSG_CLOSE_SYSTEM_PANEL);
+        mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
+        mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
+    }
+
+    // called by StatusBarService
+    @Override
+    public void setLightsOn(boolean on) {
+        mHandler.removeMessages(MSG_LIGHTS_OUT);
+        mHandler.removeMessages(MSG_LIGHTS_ON);
+        mHandler.sendEmptyMessage(on ? MSG_LIGHTS_ON : MSG_LIGHTS_OUT);
+    }
+
+    public void setMenuKeyVisible(boolean visible) {
+        if (DEBUG) {
+            Slog.d(TAG, (visible?"showing":"hiding") + " the MENU button");
+        }
+        setViewVisibility(mMenuButton,
+                visible ? View.VISIBLE : View.INVISIBLE,
+                visible ? R.anim.navigation_in : R.anim.navigation_out);
+    }
+
+    public void setIMEButtonVisible(boolean visible) {
+        if (DEBUG) {
+            Slog.d(TAG, (visible?"showing":"hiding") + " the IME button");
+        }
+        mInputMethodButton.setIMEButtonVisible(visible);
+    }
+
+    private void setAreThereNotifications() {
+        final boolean hasClearable = mNotns.hasClearableItems();
+
+        //Slog.d(TAG, "setAreThereNotifications hasClerable=" + hasClearable);
+
+        // Show or hide the "Clear all" button.  Note that we don't do an animation
+        // if it's not on screen, so that if someone opens the bar right then they
+        // don't see the animation in progress.
+        // (no ongoing notifications are clearable)
+        if (hasClearable) {
+            if (mNotificationButtons.getVisibility() == View.VISIBLE) {
+                setViewVisibility(mClearButton, View.VISIBLE, R.anim.notification_buttons_in);
+            } else {
+                mClearButton.setVisibility(View.VISIBLE);
+            }
+        } else {
+            if (mNotificationButtons.getVisibility() == View.VISIBLE) {
+                setViewVisibility(mClearButton, View.GONE, R.anim.notification_buttons_out);
+            } else {
+                mClearButton.setVisibility(View.GONE);
+            }
+        }
+
+        /*
+        mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
+        mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
+
+        if (ongoing || latest) {
+            mNoNotificationsTitle.setVisibility(View.GONE);
+        } else {
+            mNoNotificationsTitle.setVisibility(View.VISIBLE);
+        }
+        */
+    }
+
+    /**
+     * Cancel this notification and tell the status bar service about the failure. Hold no locks.
+     */
+    void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
+        removeNotification(key);
+        try {
+            mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
+        } catch (RemoteException ex) {
+            // The end is nigh.
+        }
+    }
+
+    private class ClockTouchListener implements View.OnTouchListener {
+        VelocityTracker mVT = null;
+        int mInitX, mInitY;
+        public boolean onTouch (View v, MotionEvent event) {
+            final int x = (int) event.getX();
+            final int y = (int) event.getY();
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    mVT = VelocityTracker.obtain();
+                    mInitX = x;
+                    mInitY = y;
+                    mHandler.sendEmptyMessageDelayed(MSG_LIGHTS_OUT,
+                            ViewConfiguration.getLongPressTimeout());
+                    break;
+                case MotionEvent.ACTION_OUTSIDE:
+                case MotionEvent.ACTION_MOVE:
+                    final Rect r = new Rect();
+                    final float radius = mSystemInfo.getHeight() / 2;
+                    if (Math.abs(x - mInitX) > radius || Math.abs(y - mInitY) > radius) {
+                        mHandler.removeMessages(MSG_LIGHTS_OUT);
+                    }
+                    if (mVT == null) break;
+                    mVT.addMovement(event);
+                    mVT.computeCurrentVelocity(1000);
+                    if (mVT.getYVelocity() < -200 && mSystemPanel.getVisibility() == View.GONE) {
+                        mHandler.removeMessages(MSG_OPEN_SYSTEM_PANEL);
+                        mHandler.sendEmptyMessage(MSG_OPEN_SYSTEM_PANEL);
+                    } else if (mVT.getYVelocity() > 200) {
+                        mHandler.sendEmptyMessage(MSG_LIGHTS_OUT);
+                    }
+                    return true;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    mVT.recycle();
+                    mVT = null;
+                    mHandler.removeMessages(MSG_LIGHTS_OUT);
+                    return true;
+            }
+            return false;
+        }
+    }
+
+    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            if (v == mClearButton) {
+                onClickClearButton();
+            } else if (v == mDoNotDisturbButton) {
+                onClickDoNotDisturb();
+            } else if (v == mNotificationTrigger) {
+                onClickNotificationTrigger();
+            } else if (v == mRecentButton) {
+                onClickRecentButton();
+            }
+        }
+    };
+
+    void onClickClearButton() {
+        try {
+            mBarService.onClearAllNotifications();
+        } catch (RemoteException ex) {
+            // system process is dead if we're here.
+        }
+        animateCollapse();
+        refreshNotificationTrigger();
+    }
+
+    void onClickDoNotDisturb() {
+        mNotificationsOn = !mNotificationsOn;
+        mIconLayout.setVisibility(mNotificationsOn ? View.VISIBLE : View.INVISIBLE); // TODO: animation
+        animateCollapse();
+        refreshNotificationTrigger();
+    }
+
+    public void onClickNotificationTrigger() {
+        if (DEBUG) Slog.d(TAG, "clicked notification icons");
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
+            if (!mNotificationsOn) {
+                mNotificationsOn = true;
+                mIconLayout.setVisibility(View.VISIBLE); // TODO: animation
+                refreshNotificationTrigger();
+            } else {
+                int msg = (mNotificationPanel.getVisibility() == View.GONE)
+                    ? MSG_OPEN_NOTIFICATION_PANEL
+                    : MSG_CLOSE_NOTIFICATION_PANEL;
+                mHandler.removeMessages(msg);
+                mHandler.sendEmptyMessage(msg);
+            }
+        }
+    }
+
+    public void onClickSystemInfo() {
+        if (DEBUG) Slog.d(TAG, "clicked system info");
+        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
+            int msg = (mSystemPanel.getVisibility() == View.GONE)
+                ? MSG_OPEN_SYSTEM_PANEL
+                : MSG_CLOSE_SYSTEM_PANEL;
+            mHandler.removeMessages(msg);
+            mHandler.sendEmptyMessage(msg);
+        }
+    }
+
+    public void onClickRecentButton() {
+        if (DEBUG) Slog.d(TAG, "clicked recent apps");
+        if (mRecentsPanel == null) {
+            Intent intent = new Intent();
+            intent.setClass(mContext, RecentApplicationsActivity.class);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            mContext.startActivity(intent);
+        } else {
+            if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
+                int msg = (mRecentsPanel.getVisibility() == View.GONE)
+                    ? MSG_OPEN_RECENTS_PANEL
+                    : MSG_CLOSE_RECENTS_PANEL;
+                mHandler.removeMessages(msg);
+                mHandler.sendEmptyMessage(msg);
+            }
+        }
+    }
+
+    private class NotificationClicker implements View.OnClickListener {
+        private PendingIntent mIntent;
+        private String mPkg;
+        private String mTag;
+        private int mId;
+
+        NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
+            mIntent = intent;
+            mPkg = pkg;
+            mTag = tag;
+            mId = id;
+        }
+
+        public void onClick(View v) {
+            try {
+                // The intent we are sending is for the application, which
+                // won't have permission to immediately start an activity after
+                // the user switches to home.  We know it is safe to do at this
+                // point, so make sure new activity switches are now allowed.
+                ActivityManagerNative.getDefault().resumeAppSwitches();
+            } catch (RemoteException e) {
+            }
+
+            if (mIntent != null) {
+                int[] pos = new int[2];
+                v.getLocationOnScreen(pos);
+                Intent overlay = new Intent();
+                overlay.setSourceBounds(
+                        new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+                try {
+                    mIntent.send(mContext, 0, overlay);
+                } catch (PendingIntent.CanceledException e) {
+                    // the stack trace isn't very helpful here.  Just log the exception message.
+                    Slog.w(TAG, "Sending contentIntent failed: " + e);
+                }
+            }
+
+            try {
+                mBarService.onNotificationClick(mPkg, mTag, mId);
+            } catch (RemoteException ex) {
+                // system process is dead if we're here.
+            }
+
+            // close the shade if it was open
+            animateCollapse();
+
+            // If this click was on the intruder alert, hide that instead
+//            mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
+        }
+    }
+
+    StatusBarNotification removeNotificationViews(IBinder key) {
+        NotificationData.Entry entry = mNotns.remove(key);
+        if (entry == null) {
+            Slog.w(TAG, "removeNotification for unknown key: " + key);
+            return null;
+        }
+        // Remove the expanded view.
+        ViewGroup rowParent = (ViewGroup)entry.row.getParent();
+        if (rowParent != null) rowParent.removeView(entry.row);
+        // Remove the icon.
+//        ViewGroup iconParent = (ViewGroup)entry.icon.getParent();
+//        if (iconParent != null) iconParent.removeView(entry.icon);
+        refreshIcons();
+
+        return entry.notification;
+    }
+
+    private class NotificationIconTouchListener implements View.OnTouchListener {
+        VelocityTracker mVT;
+
+        public NotificationIconTouchListener() {
+        }
+
+        public boolean onTouch(View v, MotionEvent event) {
+            boolean peeking = mNotificationPeekWindow.getVisibility() != View.GONE;
+            boolean panelShowing = mNotificationPanel.getVisibility() != View.GONE;
+            if (panelShowing) return false;
+
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    mVT = VelocityTracker.obtain();
+
+                    // fall through
+                case MotionEvent.ACTION_OUTSIDE:
+                case MotionEvent.ACTION_MOVE:
+                    // peek and switch icons if necessary
+                    int numIcons = mIconLayout.getChildCount();
+                    int peekIndex = 
+                            (int)((float)event.getX() * numIcons / mIconLayout.getWidth());
+                    if (peekIndex > numIcons - 1) peekIndex = numIcons - 1;
+                    else if (peekIndex < 0) peekIndex = 0;
+
+                    if (!peeking || mNotificationPeekIndex != peekIndex) {
+                        if (DEBUG) Slog.d(TAG, "will peek at notification #" + peekIndex);
+                        Message peekMsg = mHandler.obtainMessage(MSG_OPEN_NOTIFICATION_PEEK);
+                        peekMsg.arg1 = peekIndex;
+
+                        mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
+
+                        // no delay if we're scrubbing left-right
+                        mHandler.sendMessageDelayed(peekMsg,
+                                peeking ? 0 : mNotificationPeekTapDuration);
+                    }
+
+                    // check for fling
+                    if (mVT != null) {
+                        mVT.addMovement(event);
+                        mVT.computeCurrentVelocity(1000);
+                        // require a little more oomph once we're already in peekaboo mode
+                        if (!panelShowing && (
+                               (peeking && mVT.getYVelocity() < -mNotificationFlingVelocity*3)
+                            || (mVT.getYVelocity() < -mNotificationFlingVelocity))) {
+                            mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
+                            mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL);
+                            mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK);
+                            mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL);
+                        }
+                    }
+                    return true;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
+                    if (peeking) {
+                        mHandler.sendEmptyMessageDelayed(MSG_CLOSE_NOTIFICATION_PEEK, 250);
+                    }
+                    mVT.recycle();
+                    mVT = null;
+                    return true;
+            }
+            return false;
+        }
+    }
+
+    StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
+        if (DEBUG) {
+            Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
+        }
+        // Construct the icon.
+        final StatusBarIconView iconView = new StatusBarIconView(mContext,
+                notification.pkg + "/0x" + Integer.toHexString(notification.id));
+        iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+
+        final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
+                    notification.notification.icon,
+                    notification.notification.iconLevel,
+                    notification.notification.number);
+        if (!iconView.set(ic)) {
+            handleNotificationError(key, notification, "Couldn't attach StatusBarIcon: " + ic);
+            return null;
+        }
+        // Construct the expanded view.
+        NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
+        if (!inflateViews(entry, mPile)) {
+            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+                    + notification);
+            return null;
+        }
+
+        // Add the icon.
+        mNotns.add(entry);
+        refreshIcons();
+
+        return iconView;
+    }
+
+    private void refreshIcons() {
+        // XXX: need to implement a new limited linear layout class
+        // to avoid removing & readding everything
+
+        final int ICON_LIMIT = 4;
+        final LinearLayout.LayoutParams params
+            = new LinearLayout.LayoutParams(mIconSize, mIconSize);
+
+        int N = mNotns.size();
+
+        if (DEBUG) {
+            Slog.d(TAG, "refreshing icons: " + N + " notifications, mIconLayout=" + mIconLayout);
+        }
+
+        ArrayList<View> toShow = new ArrayList<View>();
+
+        for (int i=0; i<ICON_LIMIT; i++) {
+            if (i>=N) break;
+            toShow.add(mNotns.get(N-i-1).icon);
+        }
+
+        ArrayList<View> toRemove = new ArrayList<View>();
+        for (int i=0; i<mIconLayout.getChildCount(); i++) {
+            View child = mIconLayout.getChildAt(i);
+            if (!toShow.contains(child)) {
+                toRemove.add(child);
+            }
+        }
+
+        for (View remove : toRemove) {
+            mIconLayout.removeView(remove);
+        }
+
+        for (int i=0; i<toShow.size(); i++) {
+            View v = toShow.get(i);
+            if (v.getParent() == null) {
+                mIconLayout.addView(toShow.get(i), i, params);
+            }
+        }
+
+        loadNotificationPanel();
+        refreshNotificationTrigger();
+    }
+
+    private void loadNotificationPanel() {
+        int N = mNotns.size();
+
+        ArrayList<View> toShow = new ArrayList<View>();
+
+        for (int i=0; i<N; i++) {
+            View row = mNotns.get(N-i-1).row;
+            toShow.add(row);
+        }
+
+        ArrayList<View> toRemove = new ArrayList<View>();
+        for (int i=0; i<mPile.getChildCount(); i++) {
+            View child = mPile.getChildAt(i);
+            if (!toShow.contains(child)) {
+                toRemove.add(child);
+            }
+        }
+
+        for (View remove : toRemove) {
+            mPile.removeView(remove);
+        }
+
+        for (int i=0; i<toShow.size(); i++) {
+            View v = toShow.get(i);
+            if (v.getParent() == null) {
+                mPile.addView(toShow.get(i));
+            }
+        }
+    }
+
+    private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
+        StatusBarNotification sbn = entry.notification;
+        RemoteViews remoteViews = sbn.notification.contentView;
+        if (remoteViews == null) {
+            return false;
+        }
+
+        // create the row view
+        LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+        View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
+        View vetoButton = row.findViewById(R.id.veto);
+        if (entry.notification.isClearable()) {
+            final String _pkg = sbn.pkg;
+            final String _tag = sbn.tag;
+            final int _id = sbn.id;
+            vetoButton.setOnClickListener(new View.OnClickListener() {
+                    public void onClick(View v) {
+                        try {
+                            mBarService.onNotificationClear(_pkg, _tag, _id);
+                        } catch (RemoteException ex) {
+                            // system process is dead if we're here.
+                        }
+    //                    animateCollapse();
+                    }
+                });
+        } else {
+            vetoButton.setVisibility(View.INVISIBLE);
+        }
+
+        // bind the click event to the content area
+        ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
+        // XXX: update to allow controls within notification views
+        content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+//        content.setOnFocusChangeListener(mFocusChangeListener);
+        PendingIntent contentIntent = sbn.notification.contentIntent;
+        if (contentIntent != null) {
+            content.setOnClickListener(new NotificationClicker(contentIntent,
+                        sbn.pkg, sbn.tag, sbn.id));
+        } else {
+            content.setOnClickListener(null);
+        }
+
+        View expanded = null;
+        Exception exception = null;
+        try {
+            expanded = remoteViews.apply(mContext, content);
+        }
+        catch (RuntimeException e) {
+            exception = e;
+        }
+        if (expanded == null) {
+            String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
+            Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
+            return false;
+        } else {
+            content.addView(expanded);
+            row.setDrawingCacheEnabled(true);
+        }
+
+        entry.row = row;
+        entry.content = content;
+        entry.expanded = expanded;
+
+        return true;
+    }
+
+    public class SetLightsOnListener implements View.OnLongClickListener,
+           View.OnClickListener {
+        private boolean mOn;
+
+        SetLightsOnListener(boolean on) {
+            mOn = on;
+        }
+
+        public void onClick(View v) {
+            setLightsOn(mOn);
+        }
+
+        public boolean onLongClick(View v) {
+            setLightsOn(mOn);
+            return true;
+        }
+
+    }
+
+    public class TouchOutsideListener implements View.OnTouchListener {
+        private int mMsg;
+        private StatusBarPanel mPanel;
+
+        public TouchOutsideListener(int msg, StatusBarPanel panel) {
+            mMsg = msg;
+            mPanel = panel;
+        }
+
+        public boolean onTouch(View v, MotionEvent ev) {
+            final int action = ev.getAction();
+            if (action == MotionEvent.ACTION_OUTSIDE
+                    || (action == MotionEvent.ACTION_DOWN
+                        && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
+                mHandler.removeMessages(mMsg);
+                mHandler.sendEmptyMessage(mMsg);
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private void setViewVisibility(View v, int vis, int anim) {
+        if (v.getVisibility() != vis) {
+            //Slog.d(TAG, "setViewVisibility vis=" + (vis == View.VISIBLE) + " v=" + v);
+            v.setAnimation(AnimationUtils.loadAnimation(mContext, anim));
+            v.setVisibility(vis);
+        }
+    }
+
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.print("mDisabled=0x");
+        pw.println(Integer.toHexString(mDisabled));
+    }
+}
+
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
new file mode 100644
index 0000000..15866fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tablet;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+
+public class TabletStatusBarView extends FrameLayout {
+    private Handler mHandler;
+
+    private View[] mIgnoreChildren = new View[3];
+    private View[] mPanels = new View[3];
+    private int[] mPos = new int[2];
+
+    public TabletStatusBarView(Context context) {
+        super(context);
+    }
+
+    public TabletStatusBarView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mHandler.removeMessages(TabletStatusBarService.MSG_CLOSE_NOTIFICATION_PANEL);
+            mHandler.sendEmptyMessage(TabletStatusBarService.MSG_CLOSE_NOTIFICATION_PANEL);
+            mHandler.removeMessages(TabletStatusBarService.MSG_CLOSE_SYSTEM_PANEL);
+            mHandler.sendEmptyMessage(TabletStatusBarService.MSG_CLOSE_SYSTEM_PANEL);
+            mHandler.removeMessages(TabletStatusBarService.MSG_CLOSE_RECENTS_PANEL);
+            mHandler.sendEmptyMessage(TabletStatusBarService.MSG_CLOSE_RECENTS_PANEL);
+
+            for (int i=0; i < mPanels.length; i++) {
+                if (mPanels[i] != null && mPanels[i].getVisibility() == View.VISIBLE) {
+                    if (eventInside(mIgnoreChildren[i], ev)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return super.onInterceptTouchEvent(ev);
+    }
+
+    private boolean eventInside(View v, MotionEvent ev) {
+        // assume that x and y are window coords because we are.
+        final int x = (int)ev.getX();
+        final int y = (int)ev.getY();
+
+        final int[] p = mPos;
+        v.getLocationInWindow(p);
+
+        final int l = p[0];
+        final int t = p[1];
+        final int r = p[0] + v.getWidth();
+        final int b = p[1] + v.getHeight();
+
+        return x >= l && x < r && y >= t && y < b;
+    }
+
+    public void setHandler(Handler h) {
+        mHandler = h;
+    }
+
+    public void setIgnoreChildren(int index, View ignore, View panel) {
+        mIgnoreChildren[index] = ignore;
+        mPanels[index] = panel;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
new file mode 100644
index 0000000..3c3139f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tablet;
+
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.statusbar.StatusBarNotification;
+
+import com.android.systemui.R;
+
+import java.util.Arrays;
+
+public class TabletTicker extends Handler {
+    private static final String TAG = "StatusBar.TabletTicker";
+
+    private static final int MSG_ADVANCE = 1;
+
+    private static final int ADVANCE_DELAY = 5000; // 5 seconds
+
+    private Context mContext;
+    private FrameLayout mParent;
+
+    private StatusBarNotification mCurrentNotification;
+    private View mCurrentView;
+
+    private StatusBarNotification[] mQueue;
+    private int mQueuePos;
+
+    public TabletTicker(Context context, FrameLayout parent) {
+        mContext = context;
+        mParent = parent;
+
+        // TODO: Make this a configuration value.
+        // 3 is enough to let us see most cases, but not get so far behind that it's annoying.
+        mQueue = new StatusBarNotification[3];
+    }
+
+    public void add(StatusBarNotification notification) {
+        if (false) {
+            Slog.d(TAG, "add mCurrentNotification=" + mCurrentNotification
+                    + " mQueuePos=" + mQueuePos + " mQueue=" + Arrays.toString(mQueue));
+        }
+        mQueue[mQueuePos] = notification;
+
+        // If nothing is running now, start the next one
+        if (mCurrentNotification == null) {
+            sendEmptyMessage(MSG_ADVANCE);
+        }
+
+        if (mQueuePos < mQueue.length - 1) {
+            mQueuePos++;
+        }
+    }
+
+    public void halt() {
+        removeMessages(MSG_ADVANCE);
+        if (mCurrentView != null) {
+            final int N = mQueue.length;
+            for (int i=0; i<N; i++) {
+                mQueue[i] = null;
+            }
+            mQueuePos = 0;
+            sendEmptyMessage(MSG_ADVANCE);
+        }
+    }
+
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case MSG_ADVANCE:
+                advance();
+                break;
+        }
+    }
+
+    private void advance() {
+        // Out with the old...
+        if (mCurrentView != null) {
+            mParent.removeView(mCurrentView);
+            mCurrentView = null;
+            mCurrentNotification = null;
+        }
+
+        // In with the new...
+        final StatusBarNotification next = dequeue();
+        if (next != null) {
+            mCurrentNotification = next;
+            mCurrentView = makeTickerView(next);
+            mParent.addView(mCurrentView);
+            sendEmptyMessageDelayed(MSG_ADVANCE, ADVANCE_DELAY);
+        }
+    }
+
+    private StatusBarNotification dequeue() {
+        StatusBarNotification notification = mQueue[0];
+        if (false) {
+            Slog.d(TAG, "dequeue mQueuePos=" + mQueuePos + " mQueue=" + Arrays.toString(mQueue));
+        }
+        final int N = mQueuePos;
+        for (int i=0; i<N; i++) {
+            mQueue[i] = mQueue[i+1];
+        }
+        mQueue[N] = null;
+        if (mQueuePos > 0) {
+            mQueuePos--;
+        }
+        return notification;
+    }
+
+    private View makeTickerView(StatusBarNotification notification) {
+        final Notification n = notification.notification;
+
+        LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+
+        int layoutId;
+        ViewGroup group;
+        if (n.tickerTitle != null || n.tickerSubtitle != null) {
+            group = (ViewGroup)inflater.inflate(R.layout.ticker, mParent, false);
+            if (n.tickerTitle != null) {
+                final TextView title = (TextView)group.findViewById(R.id.title);
+                title.setText(n.tickerTitle);
+            }
+            if (n.tickerSubtitle != null) {
+                final TextView subtitle = (TextView)group.findViewById(R.id.subtitle);
+                subtitle.setText(n.tickerSubtitle);
+            }
+        } else {
+            group = (ViewGroup)inflater.inflate(R.layout.ticker_compat, mParent, false);
+            TextView tv = (TextView)group.findViewById(R.id.text);
+            tv.setText(n.tickerText);
+        }
+
+        // No more than 2 icons.
+        if (n.tickerIcons != null) {
+            int N = n.tickerIcons.length;
+            if (N > 2) {
+                N = 2;
+            }
+            for (int i=N-1; i>= 0; i--) {
+                Bitmap b = n.tickerIcons[i];
+                if (b != null) {
+                    ImageView iv = (ImageView)inflater.inflate(R.layout.ticker_icon, group, false);
+                    iv.setImageBitmap(b);
+                    group.addView(iv, 0);
+                }
+            }
+        }
+
+        return group;
+    }
+}
+
diff --git a/packages/TtsService/jni/Android.mk b/packages/TtsService/jni/Android.mk
index b41759a..5dc0c30 100755
--- a/packages/TtsService/jni/Android.mk
+++ b/packages/TtsService/jni/Android.mk
@@ -5,6 +5,7 @@
 	android_tts_SynthProxy.cpp
 
 LOCAL_C_INCLUDES += \
+	frameworks/base/native/include \
 	$(JNI_H_INCLUDE)
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index 1d69361..8dc88db 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 Google Inc.
+ * Copyright (C) 2009-2010 Google Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,7 +23,7 @@
 #include <nativehelper/jni.h>
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
-#include <tts/TtsEngine.h>
+#include <android/tts.h>
 #include <media/AudioTrack.h>
 #include <math.h>
 
@@ -154,7 +154,7 @@
 class SynthProxyJniStorage {
     public :
         jobject                   tts_ref;
-        TtsEngine*                mNativeSynthInterface;
+        android_tts_engine_t*       mEngine;
         void*                     mEngineLibHandle;
         AudioTrack*               mAudioOut;
         int8_t                    mPlayState;
@@ -168,7 +168,7 @@
 
         SynthProxyJniStorage() {
             tts_ref = NULL;
-            mNativeSynthInterface = NULL;
+            mEngine = NULL;
             mEngineLibHandle = NULL;
             mAudioOut = NULL;
             mPlayState =  SYNTHPLAYSTATE_IS_STOPPED;
@@ -184,9 +184,9 @@
         ~SynthProxyJniStorage() {
             //LOGV("entering ~SynthProxyJniStorage()");
             killAudio();
-            if (mNativeSynthInterface) {
-                mNativeSynthInterface->shutdown();
-                mNativeSynthInterface = NULL;
+            if (mEngine) {
+                mEngine->funcs->shutdown(mEngine);
+                mEngine = NULL;
             }
             if (mEngineLibHandle) {
                 //LOGE("~SynthProxyJniStorage(): before close library");
@@ -273,28 +273,45 @@
  * Callback from TTS engine.
  * Directly speaks using AudioTrack or write to file
  */
-static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
-                           uint32_t format, int channel,
-                           int8_t *&wav, size_t &bufferSize, tts_synth_status status) {
+extern "C" android_tts_callback_status_t
+__ttsSynthDoneCB(void ** pUserdata, uint32_t rate,
+               android_tts_audio_format_t format, int channel,
+               int8_t **pWav, size_t *pBufferSize,
+               android_tts_synth_status_t status) 
+{
     //LOGV("ttsSynthDoneCallback: %d bytes", bufferSize);
+    AudioSystem::audio_format  encoding;
 
-    if (userdata == NULL){
+    if (*pUserdata == NULL){
         LOGE("userdata == NULL");
-        return TTS_CALLBACK_HALT;
+        return ANDROID_TTS_CALLBACK_HALT;
     }
-    afterSynthData_t* pForAfter = (afterSynthData_t*)userdata;
+    switch (format) {
+    case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
+        encoding = AudioSystem::PCM_8_BIT;
+        break;
+    case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
+        encoding = AudioSystem::PCM_16_BIT;
+        break;
+    default:
+        LOGE("Can't play, bad format");
+        return ANDROID_TTS_CALLBACK_HALT;
+    }
+    afterSynthData_t* pForAfter = (afterSynthData_t*) *pUserdata;
     SynthProxyJniStorage* pJniData = (SynthProxyJniStorage*)(pForAfter->jniStorage);
 
     if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY){
         //LOGV("Direct speech");
 
-        if (wav == NULL) {
+        if (*pWav == NULL) {
             delete pForAfter;
+            pForAfter = NULL;
             LOGV("Null: speech has completed");
+            return ANDROID_TTS_CALLBACK_HALT;
         }
 
-        if (bufferSize > 0) {
-            prepAudioTrack(pJniData, pForAfter->streamType, rate, (AudioSystem::audio_format)format, channel);
+        if (*pBufferSize > 0) {
+            prepAudioTrack(pJniData, pForAfter->streamType, rate, encoding, channel);
             if (pJniData->mAudioOut) {
                 pJniData->mPlayLock.lock();
                 if(pJniData->mAudioOut->stopped()
@@ -303,28 +320,31 @@
                 }
                 pJniData->mPlayLock.unlock();
                 if (bUseFilter) {
-                    applyFilter((int16_t*)wav, bufferSize/2);
+                    applyFilter((int16_t*)*pWav, *pBufferSize/2);
                 }
-                pJniData->mAudioOut->write(wav, bufferSize);
-                memset(wav, 0, bufferSize);
+                pJniData->mAudioOut->write(*pWav, *pBufferSize);
+                memset(*pWav, 0, *pBufferSize);
                 //LOGV("AudioTrack wrote: %d bytes", bufferSize);
             } else {
                 LOGE("Can't play, null audiotrack");
+                delete pForAfter;
+                pForAfter = NULL;
+                return ANDROID_TTS_CALLBACK_HALT;
             }
         }
     } else  if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) {
         //LOGV("Save to file");
-        if (wav == NULL) {
+        if (*pWav == NULL) {
             delete pForAfter;
             LOGV("Null: speech has completed");
-            return TTS_CALLBACK_HALT;
+            return ANDROID_TTS_CALLBACK_HALT;
         }
-        if (bufferSize > 0){
+        if (*pBufferSize > 0){
             if (bUseFilter) {
-                applyFilter((int16_t*)wav, bufferSize/2);
+                applyFilter((int16_t*)*pWav, *pBufferSize/2);
             }
-            fwrite(wav, 1, bufferSize, pForAfter->outputFile);
-            memset(wav, 0, bufferSize);
+            fwrite(*pWav, 1, *pBufferSize, pForAfter->outputFile);
+            memset(*pWav, 0, *pBufferSize);
         }
     }
     // Future update:
@@ -332,7 +352,7 @@
     //      javaTTSFields.synthProxyMethodPost methode to notify
     //      playback has completed if the synthesis is done or if a marker has been reached.
 
-    if (status == TTS_SYNTH_DONE) {
+    if (status == ANDROID_TTS_SYNTH_DONE) {
         // this struct was allocated in the original android_tts_SynthProxy_speak call,
         // all processing matching this call is now done.
         LOGV("Speech synthesis done.");
@@ -342,16 +362,16 @@
             delete pForAfter;
             pForAfter = NULL;
         }
-        return TTS_CALLBACK_HALT;
+        return ANDROID_TTS_CALLBACK_HALT;
     }
 
     // we don't update the wav (output) parameter as we'll let the next callback
     // write at the same location, we've consumed the data already, but we need
     // to update bufferSize to let the TTS engine know how much it can write the
     // next time it calls this function.
-    bufferSize = pJniData->mBufferSize;
+    *pBufferSize = pJniData->mBufferSize;
 
-    return TTS_CALLBACK_CONTINUE;
+    return ANDROID_TTS_CALLBACK_CONTINUE;
 }
 
 
@@ -360,7 +380,7 @@
 android_tts_SynthProxy_setLowShelf(JNIEnv *env, jobject thiz, jboolean applyFilter,
         jfloat filterGain, jfloat attenuationInDb, jfloat freqInHz, jfloat slope)
 {
-    int result = TTS_SUCCESS;
+    int result = ANDROID_TTS_SUCCESS;
 
     bUseFilter = applyFilter;
     if (applyFilter) {
@@ -373,7 +393,7 @@
             initializeEQ();
         } else {
             LOGE("Invalid slope, can't be null");
-            result = TTS_FAILURE;
+            result = ANDROID_TTS_FAILURE;
         }
     }
 
@@ -385,7 +405,7 @@
 android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
         jobject weak_this, jstring nativeSoLib, jstring engConfig)
 {
-    int result = TTS_FAILURE;
+    int result = ANDROID_TTS_FAILURE;
 
     bUseFilter = false;
 
@@ -402,18 +422,28 @@
     if (engine_lib_handle == NULL) {
        LOGE("android_tts_SynthProxy_native_setup(): engine_lib_handle == NULL");
     } else {
-        TtsEngine *(*get_TtsEngine)() =
-            reinterpret_cast<TtsEngine* (*)()>(dlsym(engine_lib_handle, "getTtsEngine"));
+        android_tts_engine_t * (*get_TtsEngine)() =
+            reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "android_getTtsEngine"));
 
-        pJniStorage->mNativeSynthInterface = (*get_TtsEngine)();
-        pJniStorage->mEngineLibHandle = engine_lib_handle;
-
-        if (pJniStorage->mNativeSynthInterface) {
-            Mutex::Autolock l(engineMutex);
-            pJniStorage->mNativeSynthInterface->init(ttsSynthDoneCB, engConfigString);
+        // Support obsolete/legacy binary modules
+        if (get_TtsEngine == NULL) {
+            get_TtsEngine =
+                reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "getTtsEngine"));
         }
 
-        result = TTS_SUCCESS;
+        pJniStorage->mEngine = (*get_TtsEngine)();
+        pJniStorage->mEngineLibHandle = engine_lib_handle;
+
+        android_tts_engine_t *engine = pJniStorage->mEngine;
+        if (engine) {
+            Mutex::Autolock l(engineMutex);
+            engine->funcs->init(
+                engine,
+                __ttsSynthDoneCB,
+                engConfigString);
+        }
+
+        result = ANDROID_TTS_SUCCESS;
     }
 
     // we use a weak reference so the SynthProxy object can be garbage collected.
@@ -462,7 +492,7 @@
 android_tts_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData,
         jstring language, jstring country, jstring variant)
 {
-    int result = TTS_LANG_NOT_SUPPORTED;
+    int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
 
     if (jniData == 0) {
         LOGE("android_tts_SynthProxy_isLanguageAvailable(): invalid JNI data");
@@ -474,8 +504,10 @@
     const char *countryNativeString = env->GetStringUTFChars(country, 0);
     const char *variantNativeString = env->GetStringUTFChars(variant, 0);
 
-    if (pSynthData->mNativeSynthInterface) {
-        result = pSynthData->mNativeSynthInterface->isLanguageAvailable(langNativeString,
+    android_tts_engine_t *engine = pSynthData->mEngine;
+
+    if (engine) {
+        result = engine->funcs->isLanguageAvailable(engine,langNativeString,
                 countryNativeString, variantNativeString);
     }
     env->ReleaseStringUTFChars(language, langNativeString);
@@ -487,7 +519,7 @@
 static int
 android_tts_SynthProxy_setConfig(JNIEnv *env, jobject thiz, jint jniData, jstring engineConfig)
 {
-    int result = TTS_FAILURE;
+    int result = ANDROID_TTS_FAILURE;
 
     if (jniData == 0) {
         LOGE("android_tts_SynthProxy_setConfig(): invalid JNI data");
@@ -498,9 +530,10 @@
 
     SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
     const char *engineConfigNativeString = env->GetStringUTFChars(engineConfig, 0);
+    android_tts_engine_t *engine = pSynthData->mEngine;
 
-    if (pSynthData->mNativeSynthInterface) {
-        result = pSynthData->mNativeSynthInterface->setProperty(ANDROID_TTS_ENGINE_PROPERTY_CONFIG,
+    if (engine) {
+        result = engine->funcs->setProperty(engine,ANDROID_TTS_ENGINE_PROPERTY_CONFIG,
                 engineConfigNativeString, strlen(engineConfigNativeString));
     }
     env->ReleaseStringUTFChars(engineConfig, engineConfigNativeString);
@@ -512,7 +545,7 @@
 android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
         jstring language, jstring country, jstring variant)
 {
-    int result = TTS_LANG_NOT_SUPPORTED;
+    int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
 
     if (jniData == 0) {
         LOGE("android_tts_SynthProxy_setLanguage(): invalid JNI data");
@@ -525,9 +558,10 @@
     const char *langNativeString = env->GetStringUTFChars(language, 0);
     const char *countryNativeString = env->GetStringUTFChars(country, 0);
     const char *variantNativeString = env->GetStringUTFChars(variant, 0);
+    android_tts_engine_t *engine = pSynthData->mEngine;
 
-    if (pSynthData->mNativeSynthInterface) {
-        result = pSynthData->mNativeSynthInterface->setLanguage(langNativeString,
+    if (engine) {
+        result = engine->funcs->setLanguage(engine, langNativeString,
                 countryNativeString, variantNativeString);
     }
     env->ReleaseStringUTFChars(language, langNativeString);
@@ -541,7 +575,7 @@
 android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData,
         jstring language, jstring country, jstring variant)
 {
-    int result = TTS_LANG_NOT_SUPPORTED;
+    int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
 
     if (jniData == 0) {
         LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data");
@@ -552,9 +586,10 @@
     const char *langNativeString = env->GetStringUTFChars(language, 0);
     const char *countryNativeString = env->GetStringUTFChars(country, 0);
     const char *variantNativeString = env->GetStringUTFChars(variant, 0);
+    android_tts_engine_t *engine = pSynthData->mEngine;
 
-    if (pSynthData->mNativeSynthInterface) {
-        result = pSynthData->mNativeSynthInterface->loadLanguage(langNativeString,
+    if (engine) {
+        result = engine->funcs->loadLanguage(engine, langNativeString,
                 countryNativeString, variantNativeString);
     }
     env->ReleaseStringUTFChars(language, langNativeString);
@@ -569,7 +604,7 @@
 android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData,
         jint speechRate)
 {
-    int result = TTS_FAILURE;
+    int result = ANDROID_TTS_FAILURE;
 
     if (jniData == 0) {
         LOGE("android_tts_SynthProxy_setSpeechRate(): invalid JNI data");
@@ -584,9 +619,10 @@
 
     SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
     LOGI("setting speech rate to %d", speechRate);
+    android_tts_engine_t *engine = pSynthData->mEngine;
 
-    if (pSynthData->mNativeSynthInterface) {
-        result = pSynthData->mNativeSynthInterface->setProperty("rate", buffer, bufSize);
+    if (engine) {
+        result = engine->funcs->setProperty(engine, "rate", buffer, bufSize);
     }
 
     return result;
@@ -597,7 +633,7 @@
 android_tts_SynthProxy_setPitch(JNIEnv *env, jobject thiz, jint jniData,
         jint pitch)
 {
-    int result = TTS_FAILURE;
+    int result = ANDROID_TTS_FAILURE;
 
     if (jniData == 0) {
         LOGE("android_tts_SynthProxy_setPitch(): invalid JNI data");
@@ -612,9 +648,10 @@
 
     SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
     LOGI("setting pitch to %d", pitch);
+    android_tts_engine_t *engine = pSynthData->mEngine;
 
-    if (pSynthData->mNativeSynthInterface) {
-        result = pSynthData->mNativeSynthInterface->setProperty("pitch", buffer, bufSize);
+    if (engine) {
+        result = engine->funcs->setProperty(engine, "pitch", buffer, bufSize);
     }
 
     return result;
@@ -625,7 +662,7 @@
 android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
         jstring textJavaString, jstring filenameJavaString)
 {
-    int result = TTS_FAILURE;
+    int result = ANDROID_TTS_FAILURE;
 
     if (jniData == 0) {
         LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid JNI data");
@@ -633,7 +670,7 @@
     }
 
     SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    if (!pSynthData->mNativeSynthInterface) {
+    if (!pSynthData->mEngine) {
         LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle");
         return result;
     }
@@ -643,12 +680,22 @@
     Mutex::Autolock l(engineMutex);
 
     // Retrieve audio parameters before writing the file header
-    AudioSystem::audio_format encoding = DEFAULT_TTS_FORMAT;
+    AudioSystem::audio_format encoding;
     uint32_t rate = DEFAULT_TTS_RATE;
     int channels = DEFAULT_TTS_NB_CHANNELS;
-    pSynthData->mNativeSynthInterface->setAudioFormat(encoding, rate, channels);
+    android_tts_engine_t *engine = pSynthData->mEngine;
+    android_tts_audio_format_t  format = ANDROID_TTS_AUDIO_FORMAT_DEFAULT;
 
-    if ((encoding != AudioSystem::PCM_16_BIT) && (encoding != AudioSystem::PCM_8_BIT)) {
+    engine->funcs->setAudioFormat(engine, &format, &rate, &channels);
+
+    switch (format) {
+    case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
+        encoding = AudioSystem::PCM_16_BIT;
+        break;
+    case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
+        encoding = AudioSystem::PCM_8_BIT;
+        break;
+    default:
         LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format");
         return result;
     }
@@ -677,7 +724,8 @@
     unsigned int unique_identifier;
 
     memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
-    result = pSynthData->mNativeSynthInterface->synthesizeText(textNativeString,
+
+    result = engine->funcs->synthesizeText(engine, textNativeString,
             pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
 
     long filelen = ftell(pForAfter->outputFile);
@@ -737,7 +785,7 @@
 android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
         jstring textJavaString, jint javaStreamType)
 {
-    int result = TTS_FAILURE;
+    int result = ANDROID_TTS_FAILURE;
 
     if (jniData == 0) {
         LOGE("android_tts_SynthProxy_speak(): invalid JNI data");
@@ -759,10 +807,12 @@
     pForAfter->usageMode  = USAGEMODE_PLAY_IMMEDIATELY;
     pForAfter->streamType = (AudioSystem::stream_type) javaStreamType;
 
-    if (pSynthData->mNativeSynthInterface) {
+    if (pSynthData->mEngine) {
         const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
         memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
-        result = pSynthData->mNativeSynthInterface->synthesizeText(textNativeString,
+        android_tts_engine_t *engine = pSynthData->mEngine;
+
+        result = engine->funcs->synthesizeText(engine, textNativeString,
                 pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
         env->ReleaseStringUTFChars(textJavaString, textNativeString);
     }
@@ -774,7 +824,7 @@
 static int
 android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
 {
-    int result = TTS_FAILURE;
+    int result = ANDROID_TTS_FAILURE;
 
     if (jniData == 0) {
         LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
@@ -790,8 +840,9 @@
     }
     pSynthData->mPlayLock.unlock();
 
-    if (pSynthData->mNativeSynthInterface) {
-        result = pSynthData->mNativeSynthInterface->stop();
+    android_tts_engine_t *engine = pSynthData->mEngine;
+    if (engine) {
+        result = engine->funcs->stop(engine);
     }
 
     return result;
@@ -801,7 +852,7 @@
 static int
 android_tts_SynthProxy_stopSync(JNIEnv *env, jobject thiz, jint jniData)
 {
-    int result = TTS_FAILURE;
+    int result = ANDROID_TTS_FAILURE;
 
     if (jniData == 0) {
         LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
@@ -829,7 +880,7 @@
 
     SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
 
-    if (pSynthData->mNativeSynthInterface) {
+    if (pSynthData->mEngine) {
         size_t bufSize = 100;
         char lang[bufSize];
         char country[bufSize];
@@ -839,7 +890,9 @@
         memset(variant, 0, bufSize);
         jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3,
                 env->FindClass("java/lang/String"), env->NewStringUTF(""));
-        pSynthData->mNativeSynthInterface->getLanguage(lang, country, variant);
+
+        android_tts_engine_t *engine = pSynthData->mEngine;
+        engine->funcs->getLanguage(engine, lang, country, variant);
         env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang));
         env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country));
         env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant));
@@ -864,8 +917,9 @@
     char buf[bufSize];
     memset(buf, 0, bufSize);
     // TODO check return codes
-    if (pSynthData->mNativeSynthInterface) {
-        pSynthData->mNativeSynthInterface->getProperty("rate", buf, &bufSize);
+    android_tts_engine_t *engine = pSynthData->mEngine;
+    if (engine) {
+        engine->funcs->getProperty(engine,"rate", buf, &bufSize);
     }
     return atoi(buf);
 }
diff --git a/packages/VpnServices/res/drawable/vpn_connected.png b/packages/VpnServices/res/drawable/vpn_connected.png
index c920876..65fc6db 100644
--- a/packages/VpnServices/res/drawable/vpn_connected.png
+++ b/packages/VpnServices/res/drawable/vpn_connected.png
Binary files differ
diff --git a/packages/VpnServices/res/drawable/vpn_disconnected.png b/packages/VpnServices/res/drawable/vpn_disconnected.png
index c920876..2440c69 100644
--- a/packages/VpnServices/res/drawable/vpn_disconnected.png
+++ b/packages/VpnServices/res/drawable/vpn_disconnected.png
Binary files differ
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
index 9dcbcb6..34dbace 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java
@@ -142,8 +142,10 @@
         final int keyCode = event.getKeyCode();
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
             switch (keyCode) {
+                case KeyEvent.KEYCODE_MEDIA_PLAY:
+                case KeyEvent.KEYCODE_MEDIA_PAUSE:
                 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                    /* Suppress PLAYPAUSE toggle when phone is ringing or
+                    /* Suppress PLAY/PAUSE toggle when phone is ringing or
                      * in-call to avoid music playback */
                     if (mTelephonyManager == null) {
                         mTelephonyManager = (TelephonyManager) getContext().getSystemService(
@@ -153,11 +155,13 @@
                             mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
                         return true;  // suppress key event
                     }
-                case KeyEvent.KEYCODE_HEADSETHOOK: 
-                case KeyEvent.KEYCODE_MEDIA_STOP: 
-                case KeyEvent.KEYCODE_MEDIA_NEXT: 
-                case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 
-                case KeyEvent.KEYCODE_MEDIA_REWIND: 
+                case KeyEvent.KEYCODE_MUTE:
+                case KeyEvent.KEYCODE_HEADSETHOOK:
+                case KeyEvent.KEYCODE_MEDIA_STOP:
+                case KeyEvent.KEYCODE_MEDIA_NEXT:
+                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+                case KeyEvent.KEYCODE_MEDIA_REWIND:
+                case KeyEvent.KEYCODE_MEDIA_RECORD:
                 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
                     Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                     intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
@@ -166,7 +170,8 @@
                 }
 
                 case KeyEvent.KEYCODE_VOLUME_UP:
-                case KeyEvent.KEYCODE_VOLUME_DOWN: {
+                case KeyEvent.KEYCODE_VOLUME_DOWN:
+                case KeyEvent.KEYCODE_VOLUME_MUTE: {
                     synchronized (this) {
                         if (mAudioManager == null) {
                             mAudioManager = (AudioManager) getContext().getSystemService(
@@ -175,6 +180,7 @@
                     }
                     // Volume buttons should only function for music.
                     if (mAudioManager.isMusicActive()) {
+                        // TODO: Actually handle MUTE.
                         mAudioManager.adjustStreamVolume(
                                     AudioManager.STREAM_MUSIC,
                                     keyCode == KeyEvent.KEYCODE_VOLUME_UP
@@ -189,12 +195,15 @@
         } else if (event.getAction() == KeyEvent.ACTION_UP) {
             switch (keyCode) {
                 case KeyEvent.KEYCODE_MUTE:
-                case KeyEvent.KEYCODE_HEADSETHOOK: 
-                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 
-                case KeyEvent.KEYCODE_MEDIA_STOP: 
-                case KeyEvent.KEYCODE_MEDIA_NEXT: 
-                case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 
-                case KeyEvent.KEYCODE_MEDIA_REWIND: 
+                case KeyEvent.KEYCODE_HEADSETHOOK:
+                case KeyEvent.KEYCODE_MEDIA_PLAY:
+                case KeyEvent.KEYCODE_MEDIA_PAUSE:
+                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                case KeyEvent.KEYCODE_MEDIA_STOP:
+                case KeyEvent.KEYCODE_MEDIA_NEXT:
+                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+                case KeyEvent.KEYCODE_MEDIA_REWIND:
+                case KeyEvent.KEYCODE_MEDIA_RECORD:
                 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
                     Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                     intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
index ba1d7f5..70a4b20 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
@@ -113,7 +113,15 @@
                     flags, PixelFormat.TRANSLUCENT);
             lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
             lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;
-            lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+
+            if (mContext.getResources().getBoolean(R.bool.config_enableLockScreenRotation)) {
+                Log.d(TAG, "Rotation sensor for lock screen On!");
+                lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
+            } else {
+                Log.d(TAG, "Rotation sensor for lock screen Off!");
+                lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+            }
+
             lp.setTitle("Keyguard");
             mWindowLayoutParams = lp;
 
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 9d10ce5..42b73b9 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.policy.impl;
 
+import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+
 import com.android.internal.telephony.IccCard;
 import com.android.internal.widget.LockPatternUtils;
 
@@ -41,6 +43,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 import android.telephony.TelephonyManager;
 import android.util.Config;
 import android.util.EventLog;
@@ -93,6 +96,7 @@
  */
 public class KeyguardViewMediator implements KeyguardViewCallback,
         KeyguardUpdateMonitor.SimStateCallback {
+    private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
     private final static boolean DEBUG = false;
     private final static boolean DBG_WAKE = false;
 
@@ -133,7 +137,7 @@
      * turning on the keyguard (i.e, the user has this much time to turn
      * the screen back on without having to face the keyguard).
      */
-    private static final int KEYGUARD_DELAY_MS = 5000;
+    private static final int KEYGUARD_LOCK_AFTER_DELAY_DEFAULT = 5000;
 
     /**
      * How long we'll wait for the {@link KeyguardViewCallback#keyguardDoneDrawing()}
@@ -244,6 +248,7 @@
      * the keyguard.
      */
     private boolean mWaitingUntilKeyguardVisible = false;
+    private LockPatternUtils mLockPatternUtils;
 
     public KeyguardViewMediator(Context context, PhoneWindowManager callback,
             LocalPowerManager powerManager) {
@@ -275,8 +280,9 @@
 
         mUpdateMonitor.registerSimStateCallback(this);
 
-        mKeyguardViewProperties = new LockPatternKeyguardViewProperties(
-                new LockPatternUtils(mContext), mUpdateMonitor);
+        mLockPatternUtils = new LockPatternUtils(mContext);
+        mKeyguardViewProperties 
+                = new LockPatternKeyguardViewProperties(mLockPatternUtils, mUpdateMonitor);
 
         mKeyguardViewManager = new KeyguardViewManager(
                 context, WindowManagerImpl.getDefault(), this,
@@ -326,15 +332,46 @@
                 // to enable it a little bit later (i.e, give the user a chance
                 // to turn the screen back on within a certain window without
                 // having to unlock the screen)
-                long when = SystemClock.elapsedRealtime() + KEYGUARD_DELAY_MS;
-                Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
-                intent.putExtra("seq", mDelayedShowingSequence);
-                PendingIntent sender = PendingIntent.getBroadcast(mContext,
-                        0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
-                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when,
-                        sender);
-                if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
-                                 + mDelayedShowingSequence);
+                final ContentResolver cr = mContext.getContentResolver();
+
+                // From DisplaySettings
+                long displayTimeout = Settings.System.getInt(cr, SCREEN_OFF_TIMEOUT,
+                        KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT);
+
+                // From SecuritySettings
+                final long lockAfterTimeout = Settings.Secure.getInt(cr,
+                        Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
+                        KEYGUARD_LOCK_AFTER_DELAY_DEFAULT);
+
+                // From DevicePolicyAdmin
+                final long policyTimeout = mLockPatternUtils.getDevicePolicyManager()
+                        .getMaximumTimeToLock(null);
+
+                long timeout;
+                if (policyTimeout > 0) {
+                    // policy in effect. Make sure we don't go beyond policy limit.
+                    displayTimeout = Math.max(displayTimeout, 0); // ignore negative values
+                    timeout = Math.min(policyTimeout - displayTimeout, lockAfterTimeout);
+                } else {
+                    timeout = lockAfterTimeout;
+                }
+
+                if (timeout <= 0) {
+                    // Lock now
+                    mSuppressNextLockSound = true;
+                    doKeyguard();
+                } else {
+                    // Lock in the future
+                    long when = SystemClock.elapsedRealtime() + timeout;
+                    Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
+                    intent.putExtra("seq", mDelayedShowingSequence);
+                    PendingIntent sender = PendingIntent.getBroadcast(mContext,
+                            0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when,
+                            sender);
+                    if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
+                                     + mDelayedShowingSequence);
+                }
             } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
                 // Do not enable the keyguard if the prox sensor forced the screen off.
             } else {
@@ -749,13 +786,17 @@
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN:
+            case KeyEvent.KEYCODE_VOLUME_MUTE:
             case KeyEvent.KEYCODE_MUTE:
             case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_STOP:
             case KeyEvent.KEYCODE_MEDIA_NEXT:
             case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
             case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
             case KeyEvent.KEYCODE_CAMERA:
                 return false;
@@ -1132,5 +1173,3 @@
         }
     }
 }
-
-
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 27706ef..822be46 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -645,7 +645,9 @@
             // Show LockScreen first for any screen other than Pattern unlock.
             final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
                     == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
-            if (isSecure() && usingLockPattern) {
+
+            boolean showSlidingTab = getResources().getBoolean(R.bool.config_enableSlidingTabFirst);
+            if (isSecure() && (usingLockPattern || !showSlidingTab)) {
                 return Mode.UnlockScreen;
             } else {
                 return Mode.LockScreen;
@@ -667,6 +669,7 @@
                 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
                 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
                 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+                case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
                     currentMode = UnlockMode.Password;
                     break;
                 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
@@ -687,8 +690,17 @@
 
     private void showTimeoutDialog() {
         int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+        int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message;
+        if(getUnlockMode() == UnlockMode.Password) {
+            if(mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
+                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
+                messageId = R.string.lockscreen_too_many_failed_pin_attempts_dialog_message;
+            } else {
+                messageId = R.string.lockscreen_too_many_failed_password_attempts_dialog_message;
+            }
+        }
         String message = mContext.getString(
-                R.string.lockscreen_too_many_failed_attempts_dialog_message,
+                messageId,
                 mUpdateMonitor.getFailedAttempts(),
                 timeoutInSeconds);
         final AlertDialog dialog = new AlertDialog.Builder(mContext)
diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index a5ef1fa..50d10bf 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -20,6 +20,8 @@
 import com.android.internal.telephony.IccCard;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.SlidingTab;
+import com.android.internal.widget.WaveView;
+import com.android.internal.widget.WaveView.OnTriggerListener;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -46,8 +48,9 @@
  * information about the device depending on its state, and how to get
  * past it, as applicable.
  */
-class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateMonitor.InfoCallback,
-        KeyguardUpdateMonitor.SimStateCallback, SlidingTab.OnTriggerListener {
+class LockScreen extends LinearLayout implements KeyguardScreen,
+        KeyguardUpdateMonitor.InfoCallback,
+        KeyguardUpdateMonitor.SimStateCallback {
 
     private static final boolean DBG = false;
     private static final String TAG = "LockScreen";
@@ -59,12 +62,7 @@
     private final KeyguardUpdateMonitor mUpdateMonitor;
     private final KeyguardScreenCallback mCallback;
 
-    private TextView mCarrier;
-    private SlidingTab mSelector;
-    private TextView mTime;
-    private TextView mDate;
-    private TextView mStatus1;
-    private TextView mStatus2;
+    private SlidingTab mSlidingTab;
     private TextView mScreenLocked;
     private TextView mEmergencyCallText;
     private Button mEmergencyCallButton;
@@ -93,6 +91,11 @@
     private java.text.DateFormat mTimeFormat;
     private boolean mEnableMenuKeyInLockScreen;
 
+    private StatusView mStatusView;
+    private WaveView mEnergyWave;
+    private SlidingTabMethods mSlidingTabMethods;
+    private WaveViewMethods mWaveViewMethods;
+
     /**
      * The status of this lock screen.
      */
@@ -144,6 +147,104 @@
         }
     }
 
+    class SlidingTabMethods implements SlidingTab.OnTriggerListener {
+
+        private void updateRightTabResources() {
+            boolean vibe = mSilentMode
+                && (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
+
+            mSlidingTab.setRightTabResources(
+                    mSilentMode ? ( vibe ? R.drawable.ic_jog_dial_vibrate_on
+                                         : R.drawable.ic_jog_dial_sound_off )
+                                : R.drawable.ic_jog_dial_sound_on,
+                    mSilentMode ? R.drawable.jog_tab_target_yellow
+                                : R.drawable.jog_tab_target_gray,
+                    mSilentMode ? R.drawable.jog_tab_bar_right_sound_on
+                                : R.drawable.jog_tab_bar_right_sound_off,
+                    mSilentMode ? R.drawable.jog_tab_right_sound_on
+                                : R.drawable.jog_tab_right_sound_off);
+        }
+
+        /** {@inheritDoc} */
+        public void onTrigger(View v, int whichHandle) {
+            if (whichHandle == SlidingTab.OnTriggerListener.LEFT_HANDLE) {
+                mCallback.goToUnlockScreen();
+            } else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
+                // toggle silent mode
+                mSilentMode = !mSilentMode;
+                if (mSilentMode) {
+                    final boolean vibe = (Settings.System.getInt(
+                        getContext().getContentResolver(),
+                        Settings.System.VIBRATE_IN_SILENT, 1) == 1);
+
+                    mAudioManager.setRingerMode(vibe
+                        ? AudioManager.RINGER_MODE_VIBRATE
+                        : AudioManager.RINGER_MODE_SILENT);
+                } else {
+                    mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+                }
+
+                updateRightTabResources();
+
+                String message = mSilentMode ?
+                        getContext().getString(R.string.global_action_silent_mode_on_status) :
+                        getContext().getString(R.string.global_action_silent_mode_off_status);
+
+                final int toastIcon = mSilentMode
+                    ? R.drawable.ic_lock_ringer_off
+                    : R.drawable.ic_lock_ringer_on;
+
+                final int toastColor = mSilentMode
+                    ? getContext().getResources().getColor(R.color.keyguard_text_color_soundoff)
+                    : getContext().getResources().getColor(R.color.keyguard_text_color_soundon);
+                toastMessage(mScreenLocked, message, toastColor, toastIcon);
+                mCallback.pokeWakelock();
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void onGrabbedStateChange(View v, int grabbedState) {
+            if (grabbedState == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
+                mSilentMode = isSilentMode();
+                mSlidingTab.setRightHintText(mSilentMode ? R.string.lockscreen_sound_on_label
+                        : R.string.lockscreen_sound_off_label);
+            }
+            // Don't poke the wake lock when returning to a state where the handle is
+            // not grabbed since that can happen when the system (instead of the user)
+            // cancels the grab.
+            if (grabbedState != SlidingTab.OnTriggerListener.NO_HANDLE) {
+                mCallback.pokeWakelock();
+            }
+        }
+    }
+
+    class WaveViewMethods implements WaveView.OnTriggerListener {
+        private static final int WAIT_FOR_ANIMATION_TIMEOUT = 0;
+        private static final int STAY_ON_WHILE_GRABBED_TIMEOUT = 30000;
+
+        /** {@inheritDoc} */
+        public void onTrigger(View v, int whichHandle) {
+            if (whichHandle == WaveView.OnTriggerListener.CENTER_HANDLE) {
+                // Delay hiding lock screen long enough for animation to finish
+                postDelayed(new Runnable() {
+                    public void run() {
+                        mCallback.goToUnlockScreen();
+                    }
+                }, WAIT_FOR_ANIMATION_TIMEOUT);
+            }
+        }
+
+        /** {@inheritDoc} */
+        public void onGrabbedStateChange(View v, int grabbedState) {
+            // Don't poke the wake lock when returning to a state where the handle is
+            // not grabbed since that can happen when the system (instead of the user)
+            // cancels the grab.
+            if (grabbedState == WaveView.OnTriggerListener.CENTER_HANDLE) {
+                mCallback.pokeWakelock(STAY_ON_WHILE_GRABBED_TIMEOUT);
+            }
+        }
+    }
+
     /**
      * In general, we enable unlocking the insecure key guard with the menu key. However, there are
      * some cases where we wish to disable it, notably when the menu button placement or technology
@@ -195,19 +296,9 @@
             inflater.inflate(R.layout.keyguard_screen_tab_unlock_land, this, true);
         }
 
-        mCarrier = (TextView) findViewById(R.id.carrier);
-        // Required for Marquee to work
-        mCarrier.setSelected(true);
-        mCarrier.setTextColor(0xffffffff);
-
-        mDate = (TextView) findViewById(R.id.date);
-        mStatus1 = (TextView) findViewById(R.id.status1);
-        mStatus2 = (TextView) findViewById(R.id.status2);
+        mStatusView = new StatusView(this, mUpdateMonitor, mLockPatternUtils);
 
         mScreenLocked = (TextView) findViewById(R.id.screenLocked);
-        mSelector = (SlidingTab) findViewById(R.id.tab_selector);
-        mSelector.setHoldAfterTrigger(true, false);
-        mSelector.setLeftHintText(R.string.lockscreen_unlock_label);
 
         mEmergencyCallText = (TextView) findViewById(R.id.emergencyCallText);
         mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
@@ -220,7 +311,6 @@
             }
         });
 
-
         setFocusable(true);
         setFocusableInTouchMode(true);
         setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
@@ -231,15 +321,25 @@
         mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
         mSilentMode = isSilentMode();
 
-        mSelector.setLeftTabResources(
-                R.drawable.ic_jog_dial_unlock,
-                R.drawable.jog_tab_target_green,
-                R.drawable.jog_tab_bar_left_unlock,
-                R.drawable.jog_tab_left_unlock);
-
-        updateRightTabResources();
-
-        mSelector.setOnTriggerListener(this);
+        mSlidingTab = (SlidingTab) findViewById(R.id.tab_selector);
+        mEnergyWave = (WaveView) findViewById(R.id.wave_view);
+        if (mSlidingTab != null) {
+            mSlidingTab.setHoldAfterTrigger(true, false);
+            mSlidingTab.setLeftHintText(R.string.lockscreen_unlock_label);
+            mSlidingTab.setLeftTabResources(
+                    R.drawable.ic_jog_dial_unlock,
+                    R.drawable.jog_tab_target_green,
+                    R.drawable.jog_tab_bar_left_unlock,
+                    R.drawable.jog_tab_left_unlock);
+            mSlidingTabMethods = new SlidingTabMethods();
+            mSlidingTab.setOnTriggerListener(mSlidingTabMethods);
+            mSlidingTabMethods.updateRightTabResources();
+        } else if (mEnergyWave != null) {
+            mWaveViewMethods = new WaveViewMethods();
+            mEnergyWave.setOnTriggerListener(mWaveViewMethods);
+        } else {
+            throw new IllegalStateException("Must have either SlidingTab or WaveView defined");
+        }
 
         resetStatusInfo(updateMonitor);
     }
@@ -248,22 +348,6 @@
         return mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
     }
 
-    private void updateRightTabResources() {
-        boolean vibe = mSilentMode
-            && (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
-
-        mSelector.setRightTabResources(
-                mSilentMode ? ( vibe ? R.drawable.ic_jog_dial_vibrate_on
-                                     : R.drawable.ic_jog_dial_sound_off )
-                            : R.drawable.ic_jog_dial_sound_on,
-                mSilentMode ? R.drawable.jog_tab_target_yellow
-                            : R.drawable.jog_tab_target_gray,
-                mSilentMode ? R.drawable.jog_tab_bar_right_sound_on
-                            : R.drawable.jog_tab_bar_right_sound_off,
-                mSilentMode ? R.drawable.jog_tab_right_sound_on
-                            : R.drawable.jog_tab_right_sound_off);
-    }
-
     private void resetStatusInfo(KeyguardUpdateMonitor updateMonitor) {
         mShowingBatteryInfo = updateMonitor.shouldShowBatteryInfo();
         mPluggedIn = updateMonitor.isDevicePluggedIn();
@@ -289,53 +373,6 @@
         return false;
     }
 
-    /** {@inheritDoc} */
-    public void onTrigger(View v, int whichHandle) {
-        if (whichHandle == SlidingTab.OnTriggerListener.LEFT_HANDLE) {
-            mCallback.goToUnlockScreen();
-        } else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
-            // toggle silent mode
-            mSilentMode = !mSilentMode;
-            if (mSilentMode) {
-                final boolean vibe = (Settings.System.getInt(
-                    getContext().getContentResolver(),
-                    Settings.System.VIBRATE_IN_SILENT, 1) == 1);
-
-                mAudioManager.setRingerMode(vibe
-                    ? AudioManager.RINGER_MODE_VIBRATE
-                    : AudioManager.RINGER_MODE_SILENT);
-            } else {
-                mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
-            }
-
-            updateRightTabResources();
-
-            String message = mSilentMode ?
-                    getContext().getString(R.string.global_action_silent_mode_on_status) :
-                    getContext().getString(R.string.global_action_silent_mode_off_status);
-
-            final int toastIcon = mSilentMode
-                ? R.drawable.ic_lock_ringer_off
-                : R.drawable.ic_lock_ringer_on;
-
-            final int toastColor = mSilentMode
-                ? getContext().getResources().getColor(R.color.keyguard_text_color_soundoff)
-                : getContext().getResources().getColor(R.color.keyguard_text_color_soundon);
-            toastMessage(mScreenLocked, message, toastColor, toastIcon);
-            mCallback.pokeWakelock();
-        }
-    }
-
-    /** {@inheritDoc} */
-    public void onGrabbedStateChange(View v, int grabbedState) {
-        if (grabbedState == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
-            mSilentMode = isSilentMode();
-            mSelector.setRightHintText(mSilentMode ? R.string.lockscreen_sound_on_label
-                    : R.string.lockscreen_sound_off_label);
-        }
-        mCallback.pokeWakelock();
-    }
-
     /**
      * Displays a message in a text view and then restores the previous text.
      * @param textView The text view.
@@ -428,38 +465,11 @@
     }
 
     private void refreshTimeAndDateDisplay() {
-        mDate.setText(DateFormat.format(mDateFormatString, new Date()));
+        mStatusView.refreshTimeAndDateDisplay();
     }
 
     private void updateStatusLines() {
-        if (!mStatus.showStatusLines()
-                || (mCharging == null && mNextAlarm == null)) {
-            mStatus1.setVisibility(View.INVISIBLE);
-            mStatus2.setVisibility(View.INVISIBLE);
-        } else if (mCharging != null && mNextAlarm == null) {
-            // charging only
-            mStatus1.setVisibility(View.VISIBLE);
-            mStatus2.setVisibility(View.INVISIBLE);
-
-            mStatus1.setText(mCharging);
-            mStatus1.setCompoundDrawablesWithIntrinsicBounds(mChargingIcon, null, null, null);
-        } else if (mNextAlarm != null && mCharging == null) {
-            // next alarm only
-            mStatus1.setVisibility(View.VISIBLE);
-            mStatus2.setVisibility(View.INVISIBLE);
-
-            mStatus1.setText(mNextAlarm);
-            mStatus1.setCompoundDrawablesWithIntrinsicBounds(mAlarmIcon, null, null, null);
-        } else if (mCharging != null && mNextAlarm != null) {
-            // both charging and next alarm
-            mStatus1.setVisibility(View.VISIBLE);
-            mStatus2.setVisibility(View.VISIBLE);
-
-            mStatus1.setText(mCharging);
-            mStatus1.setCompoundDrawablesWithIntrinsicBounds(mChargingIcon, null, null, null);
-            mStatus2.setText(mNextAlarm);
-            mStatus2.setCompoundDrawablesWithIntrinsicBounds(mAlarmIcon, null, null, null);
-        }
+        mStatusView.updateStatusLines(mStatus.showStatusLines(), mCharging, mChargingIcon, mAlarmIcon);
     }
 
     /** {@inheritDoc} */
@@ -498,6 +508,22 @@
     }
 
     /**
+     * Enables unlocking of this screen. Typically just shows the unlock widget.
+     */
+    private void enableUnlock() {
+        if (mEnergyWave != null) mEnergyWave.setVisibility(View.VISIBLE);
+        if (mSlidingTab != null) mSlidingTab.setVisibility(View.VISIBLE);
+    }
+
+    /**
+     * Disable unlocking of this screen. Typically just hides the unlock widget.
+     */
+    private void disableUnlock() {
+        if (mEnergyWave != null) mEnergyWave.setVisibility(View.GONE);
+        if (mSlidingTab != null) mSlidingTab.setVisibility(View.GONE);
+    }
+
+    /**
      * Update the layout to match the current status.
      */
     private void updateLayout(Status status) {
@@ -509,7 +535,7 @@
         switch (status) {
             case Normal:
                 // text
-                mCarrier.setText(
+                mStatusView.setCarrierText(
                         getCarrierString(
                                 mUpdateMonitor.getTelephonyPlmn(),
                                 mUpdateMonitor.getTelephonySpn()));
@@ -518,14 +544,15 @@
                 mScreenLocked.setText("");
 
                 // layout
-                mScreenLocked.setVisibility(View.VISIBLE);
-                mSelector.setVisibility(View.VISIBLE);
+                mScreenLocked.setVisibility(View.INVISIBLE);
                 mEmergencyCallText.setVisibility(View.GONE);
+                enableUnlock();
                 break;
+
             case NetworkLocked:
                 // The carrier string shows both sim card status (i.e. No Sim Card) and
                 // carrier's name and/or "Emergency Calls Only" status
-                mCarrier.setText(
+                mStatusView.setCarrierText(
                         getCarrierString(
                                 mUpdateMonitor.getTelephonyPlmn(),
                                 getContext().getText(R.string.lockscreen_network_locked_message)));
@@ -533,23 +560,24 @@
 
                 // layout
                 mScreenLocked.setVisibility(View.VISIBLE);
-                mSelector.setVisibility(View.VISIBLE);
                 mEmergencyCallText.setVisibility(View.GONE);
+                enableUnlock();
                 break;
+
             case SimMissing:
                 // text
-                mCarrier.setText(R.string.lockscreen_missing_sim_message_short);
+                mStatusView.setCarrierText(R.string.lockscreen_missing_sim_message_short);
                 mScreenLocked.setText(R.string.lockscreen_missing_sim_instructions);
 
                 // layout
                 mScreenLocked.setVisibility(View.VISIBLE);
-                mSelector.setVisibility(View.VISIBLE);
                 mEmergencyCallText.setVisibility(View.VISIBLE);
-                // do not need to show the e-call button; user may unlock
+                enableUnlock(); // do not need to show the e-call button; user may unlock
                 break;
+
             case SimMissingLocked:
                 // text
-                mCarrier.setText(
+                mStatusView.setCarrierText(
                         getCarrierString(
                                 mUpdateMonitor.getTelephonyPlmn(),
                                 getContext().getText(R.string.lockscreen_missing_sim_message_short)));
@@ -557,25 +585,27 @@
 
                 // layout
                 mScreenLocked.setVisibility(View.VISIBLE);
-                mSelector.setVisibility(View.GONE); // cannot unlock
                 mEmergencyCallText.setVisibility(View.VISIBLE);
                 mEmergencyCallButton.setVisibility(View.VISIBLE);
+                disableUnlock();
                 break;
+
             case SimLocked:
                 // text
-                mCarrier.setText(
+                mStatusView.setCarrierText(
                         getCarrierString(
                                 mUpdateMonitor.getTelephonyPlmn(),
                                 getContext().getText(R.string.lockscreen_sim_locked_message)));
 
                 // layout
                 mScreenLocked.setVisibility(View.INVISIBLE);
-                mSelector.setVisibility(View.VISIBLE);
                 mEmergencyCallText.setVisibility(View.GONE);
+                enableUnlock();
                 break;
+
             case SimPukLocked:
                 // text
-                mCarrier.setText(
+                mStatusView.setCarrierText(
                         getCarrierString(
                                 mUpdateMonitor.getTelephonyPlmn(),
                                 getContext().getText(R.string.lockscreen_sim_puk_locked_message)));
@@ -583,9 +613,9 @@
 
                 // layout
                 mScreenLocked.setVisibility(View.VISIBLE);
-                mSelector.setVisibility(View.GONE); // cannot unlock
                 mEmergencyCallText.setVisibility(View.VISIBLE);
                 mEmergencyCallButton.setVisibility(View.VISIBLE);
+                disableUnlock();
                 break;
         }
     }
@@ -652,13 +682,14 @@
 
     /** {@inheritDoc} */
     public void onPause() {
-
+        if (mEnergyWave != null) {
+            mEnergyWave.reset();
+        }
     }
 
     /** {@inheritDoc} */
     public void onResume() {
         resetStatusInfo(mUpdateMonitor);
-        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
     }
 
     /** {@inheritDoc} */
@@ -671,11 +702,10 @@
         boolean silent = AudioManager.RINGER_MODE_NORMAL != state;
         if (silent != mSilentMode) {
             mSilentMode = silent;
-            updateRightTabResources();
+            if (mSlidingTabMethods != null) mSlidingTabMethods.updateRightTabResources();
         }
     }
 
     public void onPhoneStateChanged(String newState) {
-        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
     }
 }
diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
index 39f2917..9db86aa 100644
--- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
@@ -30,6 +30,7 @@
 import android.telephony.TelephonyManager;
 import android.text.method.DigitsKeyListener;
 import android.text.method.TextKeyListener;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -50,19 +51,25 @@
 public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen,
         View.OnClickListener, KeyguardUpdateMonitor.InfoCallback, OnEditorActionListener {
 
+    private static final String TAG = "PasswordUnlockScreen";
     private final KeyguardUpdateMonitor mUpdateMonitor;
     private final KeyguardScreenCallback mCallback;
 
+    private boolean mIsAlpha;
+
     private EditText mPasswordEntry;
     private Button mEmergencyCallButton;
     private LockPatternUtils mLockPatternUtils;
     private PasswordEntryKeyboardView mKeyboardView;
+    private PasswordEntryKeyboardView mKeyboardViewAlpha;
     private PasswordEntryKeyboardHelper mKeyboardHelper;
+    private PasswordEntryKeyboardHelper mKeyboardHelperAlpha;
 
     private int mCreationOrientation;
     private int mCreationHardKeyboardHidden;
     private CountDownTimer mCountdownTimer;
-    private TextView mTitle;
+
+    private StatusView mStatusView;
 
     // To avoid accidental lockout due to events while the device in in the pocket, ignore
     // any passwords with length less than or equal to this length.
@@ -86,36 +93,65 @@
             layoutInflater.inflate(R.layout.keyguard_screen_password_landscape, this, true);
         }
 
+        mStatusView = new StatusView(this, mUpdateMonitor, mLockPatternUtils);
+
         final int quality = lockPatternUtils.getKeyguardStoredPasswordQuality();
-        final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality
-                || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality;
+        mIsAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality
+                || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality
+                || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == quality;
 
         mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
+        mKeyboardViewAlpha = (PasswordEntryKeyboardView) findViewById(R.id.keyboardAlpha);
         mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
         mPasswordEntry.setOnEditorActionListener(this);
         mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
         mEmergencyCallButton.setOnClickListener(this);
         mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
-        mTitle = (TextView) findViewById(R.id.enter_password_label);
 
-        mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this);
-        mKeyboardHelper.setKeyboardMode(isAlpha ? PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
-                : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
+        mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this, false);
+        if (mKeyboardViewAlpha == null || !mIsAlpha) {
+            mKeyboardHelper.setKeyboardMode(mIsAlpha ?
+                    PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
+                    : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
+            mKeyboardView.setVisibility(
+                    mCreationHardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
+                    ? View.INVISIBLE : View.VISIBLE);
+        } else {
+            mKeyboardHelperAlpha = new PasswordEntryKeyboardHelper(context, mKeyboardViewAlpha,
+                    this, false);
+            mKeyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
+            mKeyboardHelperAlpha.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
+            mKeyboardView.setVisibility(View.GONE);
+            mKeyboardViewAlpha.setVisibility(
+                    mCreationHardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
+                    ? View.INVISIBLE : View.VISIBLE);
+            mPasswordEntry.setWidth(mKeyboardViewAlpha.getLayoutParams().width);
+        }
 
-        mKeyboardView.setVisibility(mCreationHardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
-                ? View.INVISIBLE : View.VISIBLE);
+        mPasswordEntry.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0,
+                0, 0);
         mPasswordEntry.requestFocus();
 
         // This allows keyboards with overlapping qwerty/numeric keys to choose just the
         // numeric keys.
-        if (isAlpha) {
+        if (mIsAlpha) {
             mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
         } else {
             mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
+            mStatusView.setInstructionText(R.string.keyguard_password_enter_pin_password_code);
         }
 
         mKeyboardHelper.setVibratePattern(mLockPatternUtils.isTactileFeedbackEnabled() ?
                 com.android.internal.R.array.config_virtualKeyVibePattern : 0);
+        if (mKeyboardHelperAlpha != null) {
+            mKeyboardHelperAlpha.setVibratePattern(mLockPatternUtils.isTactileFeedbackEnabled() ?
+                    com.android.internal.R.array.config_virtualKeyVibePattern : 0);
+        }
+
+        // until we get an update...
+        mStatusView.setCarrierText(LockScreen.getCarrierString(
+                        mUpdateMonitor.getTelephonyPlmn(),
+                        mUpdateMonitor.getTelephonySpn()));
     }
 
     @Override
@@ -136,8 +172,12 @@
 
     /** {@inheritDoc} */
     public void onResume() {
+        // reset status
+        mStatusView.resetStatusInfo(mUpdateMonitor, mLockPatternUtils);
+
         // start fresh
         mPasswordEntry.setText("");
+        resetStatusInfo();
         mPasswordEntry.requestFocus();
         mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
 
@@ -174,6 +214,9 @@
                 long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
                 handleAttemptLockout(deadline);
             }
+            mStatusView.setInstructionText(R.string.lockscreen_password_wrong);
+        } else if (entry.length() > 0) {
+            mStatusView.setInstructionText(R.string.lockscreen_password_wrong);
         }
         mPasswordEntry.setText("");
     }
@@ -191,14 +234,14 @@
                 String instructions = getContext().getString(
                         R.string.lockscreen_too_many_failed_attempts_countdown,
                         secondsRemaining);
-                mTitle.setText(instructions);
+                mStatusView.setInstructionText(instructions);
             }
 
             @Override
             public void onFinish() {
                 mPasswordEntry.setEnabled(true);
-                mTitle.setText(R.string.keyguard_password_enter_password_code);
                 mKeyboardView.setEnabled(true);
+                resetStatusInfo();
             }
         }.start();
     }
@@ -244,24 +287,41 @@
         return false;
     }
 
+    // ---------- InfoCallback
+
+    /** {@inheritDoc} */
+    public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
+        mStatusView.onRefreshBatteryInfo(showBatteryInfo, pluggedIn, batteryLevel);
+    }
+
+    /** {@inheritDoc} */
+    public void onTimeChanged() {
+        mStatusView.onTimeChanged();
+    }
+
+    /** {@inheritDoc} */
+    public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+        mStatusView.onRefreshCarrierInfo(plmn, spn);
+    }
+
+    /** {@inheritDoc} */
+    public void onRingerModeChanged(int state) {
+        // not currently used
+    }
+
+    // ---------- SimStateCallback
+
+    /** {@inheritDoc} */
     public void onPhoneStateChanged(String newState) {
         mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
     }
 
-    public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
-
-    }
-
-    public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
-
-    }
-
-    public void onRingerModeChanged(int state) {
-
-    }
-
-    public void onTimeChanged() {
-
+    private void resetStatusInfo() {
+        if(mIsAlpha) {
+            mStatusView.setInstructionText(R.string.keyguard_password_enter_password_code);
+        } else {
+            mStatusView.setInstructionText(R.string.keyguard_password_enter_pin_password_code);
+        }
     }
 
 }
diff --git a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
index 418e243..35fa3e5 100644
--- a/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
@@ -25,9 +25,6 @@
 import android.view.ViewGroup;
 import android.view.MotionEvent;
 import android.widget.Button;
-import android.widget.TextView;
-import android.text.format.DateFormat;
-import android.text.TextUtils;
 import android.util.Log;
 import com.android.internal.R;
 import com.android.internal.telephony.IccCard;
@@ -37,7 +34,6 @@
 import com.android.internal.widget.LockPatternView.Cell;
 
 import java.util.List;
-import java.util.Date;
 
 /**
  * This is the screen that shows the 9 circle unlock widget and instructs
@@ -75,27 +71,7 @@
      */
     private boolean mEnableFallback;
 
-    private String mDateFormatString;
-
-    private TextView mCarrier;
-    private TextView mDate;
-
-    // are we showing battery information?
-    private boolean mShowingBatteryInfo = false;
-
-    // last known plugged in state
-    private boolean mPluggedIn = false;
-
-    // last known battery level
-    private int mBatteryLevel = 100;
-
-    private String mNextAlarm = null;
-
-    private String mInstructions = null;
-    private TextView mStatus1;
-    private TextView mStatusSep;
-    private TextView mStatus2;
-
+    private StatusView mStatusView;
 
     private LockPatternView mLockPatternView;
 
@@ -133,15 +109,18 @@
     private void updateFooter(FooterMode mode) {
         switch (mode) {
             case Normal:
+                Log.d(TAG, "mode normal");
                 mFooterNormal.setVisibility(View.VISIBLE);
                 mFooterForgotPattern.setVisibility(View.GONE);
                 break;
             case ForgotLockPattern:
+                Log.d(TAG, "mode ForgotLockPattern");
                 mFooterNormal.setVisibility(View.GONE);
                 mFooterForgotPattern.setVisibility(View.VISIBLE);
                 mForgotPatternButton.setVisibility(View.VISIBLE);
                 break;
             case VerifyUnlocked:
+                Log.d(TAG, "mode VerifyUnlocked");
                 mFooterNormal.setVisibility(View.GONE);
                 mFooterForgotPattern.setVisibility(View.GONE);
         }
@@ -180,24 +159,16 @@
         mCreationOrientation = configuration.orientation;
 
         LayoutInflater inflater = LayoutInflater.from(context);
+
         if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) {
+            Log.d(TAG, "portrait mode");
             inflater.inflate(R.layout.keyguard_screen_unlock_portrait, this, true);
         } else {
+            Log.d(TAG, "landscape mode");
             inflater.inflate(R.layout.keyguard_screen_unlock_landscape, this, true);
         }
 
-        mCarrier = (TextView) findViewById(R.id.carrier);
-        mDate = (TextView) findViewById(R.id.date);
-
-        mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);
-        refreshTimeAndDateDisplay();
-
-        mStatus1 = (TextView) findViewById(R.id.status1);
-        mStatusSep = (TextView) findViewById(R.id.statusSep);
-        mStatus2 = (TextView) findViewById(R.id.status2);
-
-        resetStatusInfo();
-
+        mStatusView = new StatusView(this, mUpdateMonitor, mLockPatternUtils);
 
         mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
 
@@ -249,15 +220,11 @@
         updateMonitor.registerSimStateCallback(this);
         setFocusableInTouchMode(true);
 
-        // Required to get Marquee to work.
-        mCarrier.setSelected(true);
-        mCarrier.setTextColor(0xffffffff);
-
         // until we get an update...
-        mCarrier.setText(
-                LockScreen.getCarrierString(
+        mStatusView.setCarrierText(LockScreen.getCarrierString(
                         mUpdateMonitor.getTelephonyPlmn(),
                         mUpdateMonitor.getTelephonySpn()));
+
     }
 
     private void refreshEmergencyButtonText() {
@@ -270,88 +237,6 @@
         mEnableFallback = state;
     }
 
-    private void resetStatusInfo() {
-        mInstructions = null;
-        mShowingBatteryInfo = mUpdateMonitor.shouldShowBatteryInfo();
-        mPluggedIn = mUpdateMonitor.isDevicePluggedIn();
-        mBatteryLevel = mUpdateMonitor.getBatteryLevel();
-        mNextAlarm = mLockPatternUtils.getNextAlarm();
-        updateStatusLines();
-    }
-
-    private void updateStatusLines() {
-        if (mInstructions != null) {
-            // instructions only
-            mStatus1.setText(mInstructions);
-            if (TextUtils.isEmpty(mInstructions)) {
-                mStatus1.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
-            } else {
-                mStatus1.setCompoundDrawablesWithIntrinsicBounds(
-                        R.drawable.ic_lock_idle_lock, 0, 0, 0);
-            }
-
-            mStatus1.setVisibility(View.VISIBLE);
-            mStatusSep.setVisibility(View.GONE);
-            mStatus2.setVisibility(View.GONE);
-        } else if (mShowingBatteryInfo && mNextAlarm == null) {
-            // battery only
-            if (mPluggedIn) {
-              if (mBatteryLevel >= 100) {
-                mStatus1.setText(getContext().getString(R.string.lockscreen_charged));
-              } else {
-                  mStatus1.setText(getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel));
-              }
-            } else {
-                mStatus1.setText(getContext().getString(R.string.lockscreen_low_battery));
-            }
-            mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0, 0, 0);
-
-            mStatus1.setVisibility(View.VISIBLE);
-            mStatusSep.setVisibility(View.GONE);
-            mStatus2.setVisibility(View.GONE);
-
-        } else if (mNextAlarm != null && !mShowingBatteryInfo) {
-            // alarm only
-            mStatus1.setText(mNextAlarm);
-            mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0, 0, 0);
-
-            mStatus1.setVisibility(View.VISIBLE);
-            mStatusSep.setVisibility(View.GONE);
-            mStatus2.setVisibility(View.GONE);
-        } else if (mNextAlarm != null && mShowingBatteryInfo) {
-            // both battery and next alarm
-            mStatus1.setText(mNextAlarm);
-            mStatusSep.setText("|");
-            mStatus2.setText(getContext().getString(
-                    R.string.lockscreen_battery_short,
-                    Math.min(100, mBatteryLevel)));
-            mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0, 0, 0);
-            if (mPluggedIn) {
-                mStatus2.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0, 0, 0);
-            } else {
-                mStatus2.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
-            }
-
-            mStatus1.setVisibility(View.VISIBLE);
-            mStatusSep.setVisibility(View.VISIBLE);
-            mStatus2.setVisibility(View.VISIBLE);
-        } else {
-            // nothing specific to show; show general instructions
-            mStatus1.setText(R.string.lockscreen_pattern_instructions);
-            mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0, 0, 0);
-
-            mStatus1.setVisibility(View.VISIBLE);
-            mStatusSep.setVisibility(View.GONE);
-            mStatus2.setVisibility(View.GONE);
-        }
-    }
-
-
-    private void refreshTimeAndDateDisplay() {
-        mDate.setText(DateFormat.format(mDateFormatString, new Date()));
-    }
-
-
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         // as long as the user is entering a pattern (i.e sending a touch
@@ -366,25 +251,21 @@
         return result;
     }
 
-
     // ---------- InfoCallback
 
     /** {@inheritDoc} */
     public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
-        mShowingBatteryInfo = showBatteryInfo;
-        mPluggedIn = pluggedIn;
-        mBatteryLevel = batteryLevel;
-        updateStatusLines();
+        mStatusView.onRefreshBatteryInfo(showBatteryInfo, pluggedIn, batteryLevel);
     }
 
     /** {@inheritDoc} */
     public void onTimeChanged() {
-        refreshTimeAndDateDisplay();
+        mStatusView.onTimeChanged();
     }
 
     /** {@inheritDoc} */
     public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
-        mCarrier.setText(LockScreen.getCarrierString(plmn, spn));
+        mStatusView.onRefreshCarrierInfo(plmn, spn);
     }
 
     /** {@inheritDoc} */
@@ -444,8 +325,8 @@
 
     /** {@inheritDoc} */
     public void onResume() {
-        // reset header
-        resetStatusInfo();
+        // reset status
+        mStatusView.resetStatusInfo(mUpdateMonitor, mLockPatternUtils);
 
         // reset lock pattern
         mLockPatternView.enableInput();
@@ -514,8 +395,8 @@
             if (mLockPatternUtils.checkPattern(pattern)) {
                 mLockPatternView
                         .setDisplayMode(LockPatternView.DisplayMode.Correct);
-                mInstructions = "";
-                updateStatusLines();
+                mStatusView.setInstructions("");
+                mStatusView.updateStatusLines();
                 mCallback.keyguardDone(true);
                 mCallback.reportSuccessfulUnlockAttempt();
             } else {
@@ -533,8 +414,9 @@
                     handleAttemptLockout(deadline);
                 } else {
                     // TODO mUnlockIcon.setVisibility(View.VISIBLE);
-                    mInstructions = getContext().getString(R.string.lockscreen_pattern_wrong);
-                    updateStatusLines();
+                    mStatusView.setInstructions(
+                            getContext().getString(R.string.lockscreen_pattern_wrong));
+                    mStatusView.updateStatusLines();
                     mLockPatternView.postDelayed(
                             mCancelPatternRunnable,
                             PATTERN_CLEAR_TIMEOUT_MS);
@@ -552,17 +434,18 @@
             @Override
             public void onTick(long millisUntilFinished) {
                 int secondsRemaining = (int) (millisUntilFinished / 1000);
-                mInstructions = getContext().getString(
+                mStatusView.setInstructions(getContext().getString(
                         R.string.lockscreen_too_many_failed_attempts_countdown,
-                        secondsRemaining);
-                updateStatusLines();
+                        secondsRemaining));
+                mStatusView.updateStatusLines();
             }
 
             @Override
             public void onFinish() {
                 mLockPatternView.setEnabled(true);
-                mInstructions = getContext().getString(R.string.lockscreen_pattern_instructions);
-                updateStatusLines();
+                mStatusView.setInstructions(getContext().getString(
+                        R.string.lockscreen_pattern_instructions));
+                mStatusView.updateStatusLines();
                 // TODO mUnlockIcon.setVisibility(View.VISIBLE);
                 mFailedPatternAttemptsSinceLastTimeout = 0;
                 if (mEnableFallback) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index dffccf8..cd88821 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -21,14 +21,19 @@
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY;
 
 import com.android.internal.view.BaseSurfaceHolder;
 import com.android.internal.view.RootViewSurfaceTaker;
+import com.android.internal.view.StandaloneActionMode;
 import com.android.internal.view.menu.ContextMenuBuilder;
 import com.android.internal.view.menu.MenuBuilder;
 import com.android.internal.view.menu.MenuDialogHelper;
+import com.android.internal.view.menu.MenuPopupHelper;
 import com.android.internal.view.menu.MenuView;
 import com.android.internal.view.menu.SubMenuBuilder;
+import com.android.internal.widget.ActionBarContextView;
+import com.android.internal.widget.ActionBarView;
 
 import android.app.KeyguardManager;
 import android.app.SearchManager;
@@ -53,6 +58,8 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.ActionMode;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.InputQueue;
@@ -66,6 +73,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewManager;
+import android.view.ViewStub;
 import android.view.VolumePanel;
 import android.view.Window;
 import android.view.WindowManager;
@@ -76,6 +84,7 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.PopupWindow;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
@@ -95,7 +104,7 @@
      * Simple callback used by the context menu and its submenus. The options
      * menu submenus do not use this (their behavior is more complex).
      */
-    ContextMenuCallback mContextMenuCallback = new ContextMenuCallback(FEATURE_CONTEXT_MENU);
+    DialogMenuCallback mContextMenuCallback = new DialogMenuCallback(FEATURE_CONTEXT_MENU);
 
     // This is the top-level view of the window, containing the window decor.
     private DecorView mDecor;
@@ -114,6 +123,8 @@
     private LayoutInflater mLayoutInflater;
 
     private TextView mTitleView;
+    
+    private ActionBarView mActionBar;
 
     private DrawableFeatureState[] mDrawables;
 
@@ -191,8 +202,12 @@
             /* Custom title feature is enabled and the user is trying to enable another feature */
             throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
         }
-        if (featureId == FEATURE_OPENGL) {
-            getAttributes().memoryType = WindowManager.LayoutParams.MEMORY_TYPE_GPU;
+        if ((features & (1 << FEATURE_NO_TITLE)) != 0 && featureId == FEATURE_ACTION_BAR) {
+            return false; // Ignore. No title dominates.
+        }
+        if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_NO_TITLE) {
+            // Remove the action bar feature if we have no title. No title dominates.
+            removeFeature(FEATURE_ACTION_BAR);
         }
         return super.requestFeature(featureId);
     }
@@ -276,6 +291,8 @@
     public void setTitle(CharSequence title) {
         if (mTitleView != null) {
             mTitleView.setText(title);
+        } else if (mActionBar != null) {
+            mActionBar.setWindowTitle(title);
         }
         mTitle = title;
     }
@@ -301,7 +318,7 @@
         // Already prepared (isPrepared will be reset to false later)
         if (st.isPrepared)
             return true;
-
+        
         if ((mPreparedPanel != null) && (mPreparedPanel != st)) {
             // Another Panel is prepared and possibly open, so close it
             closePanel(mPreparedPanel, false);
@@ -315,9 +332,11 @@
 
         if (st.createdPanelView == null) {
             // Init the panel state's menu--return false if init failed
-            if (st.menu == null) {
-                if (!initializePanelMenu(st) || (st.menu == null)) {
-                    return false;
+            if (st.menu == null || st.refreshMenuContent) {
+                if (st.menu == null) {
+                    if (!initializePanelMenu(st) || (st.menu == null)) {
+                        return false;
+                    }
                 }
                 // Call callback, and return if it doesn't want to display menu
                 if ((cb == null) || !cb.onCreatePanelMenu(st.featureId, st.menu)) {
@@ -326,6 +345,12 @@
 
                     return false;
                 }
+                
+                st.refreshMenuContent = false;
+
+                if (mActionBar != null) {
+                    mActionBar.setMenu(st.menu);
+                }
             }
 
             // Callback and return if the callback does not want to show the menu
@@ -349,36 +374,37 @@
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
-        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
-        if ((st != null) && (st.menu != null)) {
-            final MenuBuilder menuBuilder = (MenuBuilder) st.menu;
+        // Action bars handle their own menu state
+        if (mActionBar == null) {
+            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+            if ((st != null) && (st.menu != null)) {
+                final MenuBuilder menuBuilder = (MenuBuilder) st.menu;
 
-            if (st.isOpen) {
-                // Freeze state
-                final Bundle state = new Bundle();
-                menuBuilder.saveHierarchyState(state);
+                if (st.isOpen) {
+                    // Freeze state
+                    final Bundle state = new Bundle();
+                    menuBuilder.saveHierarchyState(state);
 
-                // Remove the menu views since they need to be recreated
-                // according to the new configuration
-                clearMenuViews(st);
+                    // Remove the menu views since they need to be recreated
+                    // according to the new configuration
+                    clearMenuViews(st);
 
-                // Re-open the same menu
-                reopenMenu(false);
+                    // Re-open the same menu
+                    reopenMenu(false);
 
-                // Restore state
-                menuBuilder.restoreHierarchyState(state);
+                    // Restore state
+                    menuBuilder.restoreHierarchyState(state);
 
-            } else {
-                // Clear menu views so on next menu opening, it will use
-                // the proper layout
-                clearMenuViews(st);
+                } else {
+                    // Clear menu views so on next menu opening, it will use
+                    // the proper layout
+                    clearMenuViews(st);
+                }
             }
         }
-
     }
 
     private static void clearMenuViews(PanelFeatureState st) {
-
         // This can be called on config changes, so we should make sure
         // the views will be reconstructed based on the new orientation, etc.
 
@@ -393,7 +419,12 @@
 
     @Override
     public final void openPanel(int featureId, KeyEvent event) {
-        openPanel(getPanelState(featureId, true), event);
+        if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
+                mActionBar.isOverflowReserved()) {
+            mActionBar.showOverflowMenu();
+        } else {
+            openPanel(getPanelState(featureId, true), event);
+        }
     }
 
     private void openPanel(PanelFeatureState st, KeyEvent event) {
@@ -484,7 +515,10 @@
 
     @Override
     public final void closePanel(int featureId) {
-        if (featureId == FEATURE_CONTEXT_MENU) {
+        if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
+                mActionBar.isOverflowReserved()) {
+            mActionBar.hideOverflowMenu();
+        } else if (featureId == FEATURE_CONTEXT_MENU) {
             closeContextMenu();
         } else {
             closePanel(getPanelState(featureId, true), true);
@@ -545,6 +579,26 @@
         }
     }
 
+    @Override
+    public void invalidatePanelMenu(int featureId) {
+        PanelFeatureState st = getPanelState(featureId, true);
+        if (st.menu != null) {
+            st.menu.clear();
+        }
+        st.refreshMenuContent = true;
+        st.refreshDecorView = true;
+        
+        // Prepare the options panel if we have an action bar
+        if ((featureId == FEATURE_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL)
+                && mActionBar != null) {
+            st = getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
+            if (st != null) {
+                st.isPrepared = false;
+                preparePanel(st, null);
+            }
+        }
+    }
+    
     /**
      * Called when the panel key is pushed down.
      * @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}.
@@ -558,7 +612,7 @@
             // The panel key was pushed, so set the chording key
             mPanelChordingKey = keyCode;
             mPanelMayLongPress = false;
-            
+
             PanelFeatureState st = getPanelState(featureId, true);
             if (!st.isOpen) {
                 if (getContext().getResources().getConfiguration().keyboard
@@ -567,20 +621,12 @@
                 }
                 return preparePanel(st, event);
             }
-            
         } else if (mPanelMayLongPress && mPanelChordingKey == keyCode
                 && (event.getFlags()&KeyEvent.FLAG_LONG_PRESS) != 0) {
             // We have had a long press while in a state where this
             // should be executed...  do it!
             mPanelChordingKey = 0;
             mPanelMayLongPress = false;
-            InputMethodManager imm = (InputMethodManager)
-                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
-            if (imm != null) {
-                mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-                imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
-            }
-            
         }
 
         return false;
@@ -602,25 +648,38 @@
             }
             
             boolean playSoundEffect = false;
-            PanelFeatureState st = getPanelState(featureId, true);
-            if (st.isOpen || st.isHandled) {
+            final PanelFeatureState st = getPanelState(featureId, true);
+            if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
+                    mActionBar.isOverflowReserved()) {
+                if (!mActionBar.isOverflowMenuShowing()) {
+                    final Callback cb = getCallback();
+                    if (cb != null &&
+                            cb.onPreparePanel(featureId, st.createdPanelView, st.menu)) {
+                        playSoundEffect = mActionBar.showOverflowMenu();
+                    }
+                } else {
+                    playSoundEffect = mActionBar.hideOverflowMenu();
+                }
+            } else {
+                if (st.isOpen || st.isHandled) {
 
-                // Play the sound effect if the user closed an open menu (and not if
-                // they just released a menu shortcut)
-                playSoundEffect = st.isOpen;
+                    // Play the sound effect if the user closed an open menu (and not if
+                    // they just released a menu shortcut)
+                    playSoundEffect = st.isOpen;
 
-                // Close menu
-                closePanel(st, true);
+                    // Close menu
+                    closePanel(st, true);
 
-            } else if (st.isPrepared) {
+                } else if (st.isPrepared) {
 
-                // Write 'menu opened' to event log
-                EventLog.writeEvent(50001, 0);
+                    // Write 'menu opened' to event log
+                    EventLog.writeEvent(50001, 0);
 
-                // Show menu
-                openPanel(st, event);
+                    // Show menu
+                    openPanel(st, event);
 
-                playSoundEffect = true;
+                    playSoundEffect = true;
+                }
             }
 
             if (playSoundEffect) {
@@ -777,8 +836,20 @@
             return true;
         }
 
-        // The window manager will give us a valid window token
-        new MenuDialogHelper(subMenu).show(null);
+        final Menu parentMenu = subMenu.getRootMenu();
+        final PanelFeatureState panel = findMenuPanel(parentMenu);
+
+        /*
+         * Use the panel open state to determine whether this is coming from an open panel
+         * or an action button. If it's an open panel we want to use MenuDialogHelper.
+         * If it's closed we want to grab the relevant view and create a popup anchored to it.
+         */
+        if (panel.isOpen) {
+            // The window manager will give us a valid window token
+            new MenuDialogHelper(subMenu).show(null);
+        } else {
+            new MenuPopupHelper(getContext(), subMenu).show();
+        }
 
         return true;
     }
@@ -788,6 +859,21 @@
     }
 
     private void reopenMenu(boolean toggleMenuMode) {
+        if (mActionBar != null) {
+            if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
+                final Callback cb = getCallback();
+                if (cb != null) {
+                    final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+                    if (cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
+                        mActionBar.showOverflowMenu();
+                    }
+                }
+            } else {
+                mActionBar.hideOverflowMenu();
+            }
+            return;
+        }
+
         PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
 
         // Save the future expanded mode state since closePanel will reset it
@@ -1144,7 +1230,8 @@
         
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_UP:
-            case KeyEvent.KEYCODE_VOLUME_DOWN: {
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+            case KeyEvent.KEYCODE_VOLUME_MUTE: {
                 AudioManager audioManager = (AudioManager) getContext().getSystemService(
                         Context.AUDIO_SERVICE);
                 if (audioManager != null) {
@@ -1152,6 +1239,7 @@
                      * Adjust the volume in on key down since it is more
                      * responsive to the user.
                      */
+                    // TODO: Actually handle MUTE.
                     audioManager.adjustSuggestedStreamVolume(
                             keyCode == KeyEvent.KEYCODE_VOLUME_UP
                                     ? AudioManager.ADJUST_RAISE
@@ -1163,8 +1251,10 @@
             }
 
 
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                /* Suppress PLAYPAUSE toggle when phone is ringing or in-call
+                /* Suppress PLAY/PAUSE toggle when phone is ringing or in-call
                  * to avoid music playback */
                 if (mTelephonyManager == null) {
                     mTelephonyManager = (TelephonyManager) getContext().getSystemService(
@@ -1180,6 +1270,7 @@
             case KeyEvent.KEYCODE_MEDIA_NEXT:
             case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
             case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
                 Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                 intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
@@ -1316,7 +1407,8 @@
         
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_UP:
-            case KeyEvent.KEYCODE_VOLUME_DOWN: {
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+            case KeyEvent.KEYCODE_VOLUME_MUTE: {
                 if (!event.isCanceled()) {
                     AudioManager audioManager = (AudioManager) getContext().getSystemService(
                             Context.AUDIO_SERVICE);
@@ -1325,6 +1417,7 @@
                          * Play a sound. This is done on key up since we don't want the
                          * sound to play when a user holds down volume down to mute.
                          */
+                        // TODO: Actually handle MUTE.
                         audioManager.adjustSuggestedStreamVolume(
                                 AudioManager.ADJUST_SAME,
                                 mVolumeControlStreamType,
@@ -1360,11 +1453,15 @@
             }
 
             case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MUTE:
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_STOP:
             case KeyEvent.KEYCODE_MEDIA_NEXT:
             case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
             case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
                 Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
                 intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
@@ -1414,7 +1511,11 @@
         sendCloseSystemWindows();
         Intent intent = new Intent(Intent.ACTION_CALL_BUTTON);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        getContext().startActivity(intent);
+        try {
+            getContext().startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Log.w(TAG, "No activity found for android.intent.action.CALL_BUTTON.");
+        }
     }
 
     @Override
@@ -1437,6 +1538,7 @@
     static private final String FOCUSED_ID_TAG = "android:focusedViewId";
     static private final String VIEWS_TAG = "android:views";
     static private final String PANELS_TAG = "android:Panels";
+    static private final String ACTION_BAR_TAG = "android:ActionBar";
 
     /** {@inheritDoc} */
     @Override
@@ -1470,6 +1572,10 @@
             outState.putSparseParcelableArray(PANELS_TAG, panelStates);
         }
 
+        if (mActionBar != null) {
+            outState.putBoolean(ACTION_BAR_TAG, mActionBar.isOverflowMenuShowing());
+        }
+
         return outState;
     }
 
@@ -1504,6 +1610,10 @@
         if (panelStates != null) {
             restorePanelState(panelStates);
         }
+
+        if (mActionBar != null && savedInstanceState.getBoolean(ACTION_BAR_TAG)) {
+            mActionBar.postShowOverflowMenu();
+        }
     }
 
     /**
@@ -1594,6 +1704,10 @@
         private boolean mWatchingForMenu;
         private int mDownY;
 
+        private ActionMode mActionMode;
+        private ActionBarContextView mActionModeView;
+        private PopupWindow mActionModePopup;
+
         public DecorView(Context context, int featureId) {
             super(context);
             mFeatureId = featureId;
@@ -1602,13 +1716,15 @@
         @Override
         public boolean dispatchKeyEvent(KeyEvent event) {
             final int keyCode = event.getKeyCode();
-            final boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;
+            final int action = event.getAction();
+            final boolean isDown = action == KeyEvent.ACTION_DOWN;
 
             /*
              * If the user hits another key within the play sound delay, then
              * cancel the sound
              */
             if (keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_VOLUME_UP
+                    && keyCode != KeyEvent.KEYCODE_VOLUME_MUTE
                     && mVolumeKeyUpTime + VolumePanel.PLAY_SOUND_DELAY
                             > SystemClock.uptimeMillis()) {
                 /*
@@ -1659,6 +1775,14 @@
                 }
             }
 
+            // Back cancels action modes first.
+            if (mActionMode != null && keyCode == KeyEvent.KEYCODE_BACK) {
+                if (action == KeyEvent.ACTION_UP) {
+                    mActionMode.finish();
+                }
+                return true;
+            }
+
             final Callback cb = getCallback();
             final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
                     : super.dispatchKeyEvent(event);
@@ -1881,6 +2005,68 @@
             return mContextMenuHelper != null;
         }
 
+        @Override
+        public ActionMode startActionModeForChild(View originalView,
+                ActionMode.Callback callback) {
+            // originalView can be used here to be sure that we don't obscure
+            // relevant content with the context mode UI.
+            return startActionMode(callback);
+        }
+
+        @Override
+        public ActionMode startActionMode(ActionMode.Callback callback) {
+            if (mActionMode != null) {
+                mActionMode.finish();
+            }
+
+            final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
+            ActionMode mode = getCallback().onStartActionMode(wrappedCallback);
+            if (mode != null) {
+                mActionMode = mode;
+            } else {
+                if (mActionModeView == null) {
+                    if (hasFeature(FEATURE_ACTION_MODE_OVERLAY)) {
+                        mActionModeView = new ActionBarContextView(mContext);
+                        mActionModePopup = new PopupWindow(mContext);
+                        mActionModePopup.setLayoutInScreenEnabled(true);
+                        mActionModePopup.setClippingEnabled(false);
+                        mActionModePopup.setContentView(mActionModeView);
+                        mActionModePopup.setWidth(MATCH_PARENT);
+
+                        TypedValue heightValue = new TypedValue();
+                        mContext.getTheme().resolveAttribute(
+                                com.android.internal.R.attr.actionBarSize, heightValue, false);
+                        final int height = TypedValue.complexToDimensionPixelSize(heightValue.data,
+                                mContext.getResources().getDisplayMetrics());
+                        mActionModePopup.setHeight(height);
+                    } else {
+                        ViewStub stub = (ViewStub) findViewById(
+                                com.android.internal.R.id.action_mode_bar_stub);
+                        if (stub != null) {
+                            mActionModeView = (ActionBarContextView) stub.inflate();
+                        }
+                    }
+                }
+
+                if (mActionModeView != null) {
+                    mode = new StandaloneActionMode(getContext(), mActionModeView, wrappedCallback);
+                    if (callback.onCreateActionMode(mode, mode.getMenu())) {
+                        mode.invalidate();
+                        mActionModeView.initForMode(mode);
+                        mActionModeView.setVisibility(View.VISIBLE);
+                        mActionMode = mode;
+                        if (mActionModePopup != null) {
+                            mActionModePopup.showAtLocation(this,
+                                    Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
+                        }
+                    } else {
+                        mActionMode = null;
+                    }
+                }
+            }
+            return mActionMode;
+        }
+
         public void startChanging() {
             mChanging = true;
         }
@@ -2060,6 +2246,42 @@
             if (keepOn) PhoneWindow.this.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
             else PhoneWindow.this.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         }
+
+        /**
+         * Clears out internal reference when the action mode is destroyed.
+         */
+        private class ActionModeCallbackWrapper implements ActionMode.Callback {
+            private ActionMode.Callback mWrapped;
+
+            public ActionModeCallbackWrapper(ActionMode.Callback wrapped) {
+                mWrapped = wrapped;
+            }
+
+            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+                return mWrapped.onCreateActionMode(mode, menu);
+            }
+
+            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+                return mWrapped.onPrepareActionMode(mode, menu);
+            }
+
+            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+                return mWrapped.onActionItemClicked(mode, item);
+            }
+
+            public void onDestroyActionMode(ActionMode mode) {
+                mWrapped.onDestroyActionMode(mode);
+                if (mActionModePopup != null) {
+                    mActionModePopup.dismiss();
+                } else if (mActionModeView != null) {
+                    mActionModeView.setVisibility(GONE);
+                }
+                if (mActionModeView != null) {
+                    mActionModeView.removeAllViews();
+                }
+                mActionMode = null;
+            }
+        }
     }
 
     protected DecorView generateDecor() {
@@ -2108,6 +2330,17 @@
 
         if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
             requestFeature(FEATURE_NO_TITLE);
+        } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) {
+            // Don't allow an action bar if there is no title.
+            requestFeature(FEATURE_ACTION_BAR);
+        }
+
+        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
+            requestFeature(FEATURE_ACTION_BAR_OVERLAY);
+        }
+
+        if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) {
+            requestFeature(FEATURE_ACTION_MODE_OVERLAY);
         }
 
         if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
@@ -2118,6 +2351,11 @@
             setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
         }
 
+        if (getContext().getApplicationInfo().targetSdkVersion
+                < android.os.Build.VERSION_CODES.HONEYCOMB) {
+            addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
+        }
+        
         WindowManager.LayoutParams params = getAttributes();
 
         if (!hasSoftInputMode()) {
@@ -2172,11 +2410,15 @@
             } else {
                 layoutResource = com.android.internal.R.layout.screen_title_icons;
             }
+            // XXX Remove this once action bar supports these features.
+            removeFeature(FEATURE_ACTION_BAR);
             // System.out.println("Title Icons!");
         } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0) {
             // Special case for a window with only a progress bar (and title).
             // XXX Need to have a no-title version of embedded windows.
             layoutResource = com.android.internal.R.layout.screen_progress;
+            // XXX Remove this once action bar supports these features.
+            removeFeature(FEATURE_ACTION_BAR);
             // System.out.println("Progress!");
         } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
             // Special case for a window with a custom title.
@@ -2186,11 +2428,19 @@
             } else {
                 layoutResource = com.android.internal.R.layout.screen_custom_title;
             }
+            // XXX Remove this once action bar supports these features.
+            removeFeature(FEATURE_ACTION_BAR);
         } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
             // If no other features and not embedded, only need a title.
             // If the window is floating, we need a dialog layout
             if (mIsFloating) {
                 layoutResource = com.android.internal.R.layout.dialog_title;
+            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
+                if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {
+                    layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;
+                } else {
+                    layoutResource = com.android.internal.R.layout.screen_action_bar;
+                }
             } else {
                 layoutResource = com.android.internal.R.layout.screen_title;
             }
@@ -2275,6 +2525,22 @@
                 } else {
                     mTitleView.setText(mTitle);
                 }
+            } else {
+                mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
+                if (mActionBar != null) {
+                    if (mActionBar.getTitle() == null) {
+                        mActionBar.setWindowTitle(mTitle);
+                    }
+                    // Post the panel invalidate for later; avoid application onCreateOptionsMenu
+                    // being called in the middle of onCreate or similar.
+                    mDecor.post(new Runnable() {
+                        public void run() {
+                            if (!isDestroyed()) {
+                                invalidatePanelMenu(FEATURE_ACTION_BAR);
+                            }
+                        }
+                    });
+                }
             }
         }
     }
@@ -2628,6 +2894,8 @@
 
         boolean refreshDecorView;
 
+        boolean refreshMenuContent;
+        
         boolean wasLastOpen;
         
         boolean wasLastExpanded;
@@ -2750,11 +3018,11 @@
      * <li> Calls back to the callback's onMenuItemSelected when an item is
      * selected.
      */
-    private final class ContextMenuCallback implements MenuBuilder.Callback {
+    private final class DialogMenuCallback implements MenuBuilder.Callback {
         private int mFeatureId;
         private MenuDialogHelper mSubMenuHelper;
 
-        public ContextMenuCallback(int featureId) {
+        public DialogMenuCallback(int featureId) {
             mFeatureId = featureId;
         }
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index a277bcb..30aab47 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -46,12 +46,15 @@
 import android.os.Vibrator;
 import android.provider.Settings;
 
+import com.android.internal.R;
+import com.android.internal.app.ShutdownThread;
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.view.BaseInputHandler;
 import com.android.internal.widget.PointerLocationView;
 
+import android.telephony.TelephonyManager;
 import android.util.Config;
 import android.util.EventLog;
 import android.util.Log;
@@ -61,6 +64,7 @@
 import android.view.HapticFeedbackConstants;
 import android.view.IWindowManager;
 import android.view.InputChannel;
+import android.view.InputDevice;
 import android.view.InputQueue;
 import android.view.InputHandler;
 import android.view.KeyEvent;
@@ -82,12 +86,14 @@
 import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_DRAG;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
@@ -128,6 +134,10 @@
     static final boolean DEBUG_LAYOUT = false;
     static final boolean SHOW_STARTING_ANIMATIONS = true;
     static final boolean SHOW_PROCESSES_ON_ALT_MENU = false;
+
+    static final int LONG_PRESS_POWER_NOTHING = 0;
+    static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
+    static final int LONG_PRESS_POWER_SHUT_OFF = 2;
     
     // wallpaper is at the bottom, though the window manager may move it.
     static final int WALLPAPER_LAYER = 2;
@@ -153,9 +163,12 @@
     // responsible for power management when displayed.
     static final int KEYGUARD_LAYER = 14;
     static final int KEYGUARD_DIALOG_LAYER = 15;
+    // the drag layer: input for drag-and-drop is associated with this window,
+    // which sits above all other focusable windows
+    static final int DRAG_LAYER = 16;
     // things in here CAN NOT take focus, but are shown on top of everything else.
-    static final int SYSTEM_OVERLAY_LAYER = 16;
-    static final int SECURE_SYSTEM_OVERLAY_LAYER = 17;
+    static final int SYSTEM_OVERLAY_LAYER = 17;
+    static final int SECURE_SYSTEM_OVERLAY_LAYER = 18;
 
     static final int APPLICATION_MEDIA_SUBLAYER = -2;
     static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
@@ -179,6 +192,7 @@
     Context mContext;
     IWindowManager mWindowManager;
     LocalPowerManager mPowerManager;
+    IStatusBarService mStatusBarService;
     Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
 
     // Vibrator pattern for haptic feedback of a long press.
@@ -201,11 +215,12 @@
     
     boolean mSafeMode;
     WindowState mStatusBar = null;
+    boolean mStatusBarCanHide;
     final ArrayList<WindowState> mStatusBarPanels = new ArrayList<WindowState>();
     WindowState mKeyguard = null;
     KeyguardViewMediator mKeyguardMediator;
     GlobalActions mGlobalActions;
-    boolean mShouldTurnOffOnKeyUp;
+    volatile boolean mPowerKeyHandled;
     RecentApplicationsDialog mRecentAppsDialog;
     Handler mHandler;
     
@@ -216,10 +231,15 @@
     int mLidOpenRotation;
     int mCarDockRotation;
     int mDeskDockRotation;
+
+    int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
+    int mUserRotation = Surface.ROTATION_0;
+
     boolean mCarDockEnablesAccelerometer;
     boolean mDeskDockEnablesAccelerometer;
     int mLidKeyboardAccessibility;
     int mLidNavigationAccessibility;
+    int mLongPressOnPowerBehavior = -1;
     boolean mScreenOn = false;
     boolean mOrientationSensorEnabled = false;
     int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -244,8 +264,10 @@
         }
     };
     
-    // The current size of the screen.
-    int mW, mH;
+    // The current size of the screen; these may be different than (0,0)-(dw,dh)
+    // if the status bar can't be hidden; in that case it effectively carves out
+    // that area of the display from all other windows.
+    int mScreenLeft, mScreenTop, mScreenWidth, mScreenHeight;
     // During layout, the current screen borders with all outer decoration
     // (status bar, input method dock) accounted for.
     int mCurLeft, mCurTop, mCurRight, mCurBottom;
@@ -267,6 +289,7 @@
     static final Rect mTmpVisibleFrame = new Rect();
     
     WindowState mTopFullscreenOpaqueWindowState;
+    boolean mTopIsFullscreen;
     boolean mForceStatusBar;
     boolean mHideLockScreen;
     boolean mDismissKeyguard;
@@ -276,6 +299,7 @@
     Intent mDeskDockIntent;
     boolean mSearchKeyPressed;
     boolean mConsumeSearchKeyUp;
+    boolean mShowMenuKey = false; // track FLAG_NEEDS_MENU_KEY on frontmost window
 
     // support for activating the lock screen while the screen is on
     boolean mAllowLockscreenWhenOn;
@@ -296,6 +320,9 @@
 
     // Nothing to see here, move along...
     int mFancyRotationAnimation;
+    
+    // Enable 3D recents based on config settings.
+    private Boolean mUse3dRecents;
 
     ShortcutManager mShortcutManager;
     PowerManager.WakeLock mBroadcastWakeLock;
@@ -314,6 +341,8 @@
             resolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.ACCELEROMETER_ROTATION), false, this);
             resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.USER_ROTATION), false, this);
+            resolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.SCREEN_OFF_TIMEOUT), false, this);
             resolver.registerContentObserver(Settings.System.getUriFor(
                     Settings.System.POINTER_LOCATION), false, this);
@@ -457,12 +486,48 @@
         }
     }
 
-    Runnable mPowerLongPress = new Runnable() {
+    private void interceptPowerKeyDown(boolean handled) {
+        mPowerKeyHandled = handled;
+        if (!handled) {
+            mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
+        }
+    }
+
+    private boolean interceptPowerKeyUp(boolean canceled) {
+        if (!mPowerKeyHandled) {
+            mHandler.removeCallbacks(mPowerLongPress);
+            return !canceled;
+        } else {
+            mPowerKeyHandled = true;
+            return false;
+        }
+    }
+
+    private final Runnable mPowerLongPress = new Runnable() {
         public void run() {
-            mShouldTurnOffOnKeyUp = false;
-            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
-            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
-            showGlobalActionsDialog();
+            if (!mPowerKeyHandled) {
+                // The context isn't read
+                if (mLongPressOnPowerBehavior < 0) {
+                    mLongPressOnPowerBehavior = mContext.getResources().getInteger(
+                            com.android.internal.R.integer.config_longPressOnPowerBehavior);
+                }
+                switch (mLongPressOnPowerBehavior) {
+                case LONG_PRESS_POWER_NOTHING:
+                    break;
+                case LONG_PRESS_POWER_GLOBAL_ACTIONS:
+                    mPowerKeyHandled = true;
+                    performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+                    sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
+                    showGlobalActionsDialog();
+                    break;
+                case LONG_PRESS_POWER_SHUT_OFF:
+                    mPowerKeyHandled = true;
+                    performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+                    sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
+                    ShutdownThread.shutdown(mContext, true);
+                    break;
+                }
+            }
         }
     };
 
@@ -504,6 +569,27 @@
      * Create (if necessary) and launch the recent apps dialog
      */
     void showRecentAppsDialog() {
+        // We can't initialize this in init() since the configuration hasn't been loaded yet.
+        if (mUse3dRecents == null) {
+            mUse3dRecents = mContext.getResources().getBoolean(R.bool.config_enableRecentApps3D);
+        }
+        
+        // Use 3d Recents dialog
+        if (mUse3dRecents) {
+            try {
+                Intent intent = new Intent();
+                intent.setClassName("com.android.systemui", 
+                        "com.android.systemui.statusbar.RecentApplicationsActivity");
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                mContext.startActivity(intent);
+                return;
+            } catch (ActivityNotFoundException e) {
+                Log.e(TAG, "Failed to launch RecentAppsIntent", e);
+            }
+        }
+
+        // Fallback to dialog if we fail to launch the above.
         if (mRecentAppsDialog == null) {
             mRecentAppsDialog = new RecentApplicationsDialog(mContext);
         }
@@ -535,6 +621,7 @@
         mDeskDockIntent.addCategory(Intent.CATEGORY_DESK_DOCK);
         mDeskDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mBroadcastWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                 "PhoneWindowManager.mBroadcastWakeLock");
@@ -577,6 +664,9 @@
                 com.android.internal.R.array.config_safeModeDisabledVibePattern);
         mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
                 com.android.internal.R.array.config_safeModeEnabledVibePattern);
+
+        // Note: the Configuration is not stable here, so we cannot load mStatusBarCanHide from
+        // config_statusBarCanHide because the latter depends on the screen size
     }
 
     public void updateSettings() {
@@ -595,10 +685,20 @@
                     "fancy_rotation_anim", 0) != 0 ? 0x80 : 0;
             int accelerometerDefault = Settings.System.getInt(resolver,
                     Settings.System.ACCELEROMETER_ROTATION, DEFAULT_ACCELEROMETER_ROTATION);
+            
+            // set up rotation lock state
+            mUserRotationMode = (accelerometerDefault == 0)
+                ? WindowManagerPolicy.USER_ROTATION_LOCKED
+                : WindowManagerPolicy.USER_ROTATION_FREE;
+            mUserRotation = Settings.System.getInt(resolver,
+                    Settings.System.USER_ROTATION,
+                    Surface.ROTATION_0);
+
             if (mAccelerometerDefault != accelerometerDefault) {
                 mAccelerometerDefault = accelerometerDefault;
                 updateOrientationListenerLp();
             }
+
             if (mSystemReady) {
                 int pointerLocation = Settings.System.getInt(resolver,
                         Settings.System.POINTER_LOCATION, 0);
@@ -816,6 +916,8 @@
             return TOAST_LAYER;
         case TYPE_WALLPAPER:
             return WALLPAPER_LAYER;
+        case TYPE_DRAG:
+            return DRAG_LAYER;
         }
         Log.e(TAG, "Unknown window type: " + type);
         return APPLICATION_LAYER;
@@ -992,6 +1094,11 @@
                     return WindowManagerImpl.ADD_MULTIPLE_SINGLETON;
                 }
                 mStatusBar = win;
+
+                // The Configuration will be stable by now, so we can load this
+                mStatusBarCanHide = mContext.getResources().getBoolean(
+                        com.android.internal.R.bool.config_statusBarCanHide);
+
                 break;
             case TYPE_STATUS_BAR_PANEL:
                 mContext.enforceCallingOrSelfPermission(
@@ -1042,12 +1149,22 @@
                 com.android.internal.R.anim.lock_screen_behind_enter);
     }
     
-    static ITelephony getPhoneInterface() {
-        return ITelephony.Stub.asInterface(ServiceManager.checkService(Context.TELEPHONY_SERVICE));
+    static ITelephony getTelephonyService() {
+        ITelephony telephonyService = ITelephony.Stub.asInterface(
+                ServiceManager.checkService(Context.TELEPHONY_SERVICE));
+        if (telephonyService == null) {
+            Log.w(TAG, "Unable to find ITelephony interface.");
+        }
+        return telephonyService;
     }
 
-    static IAudioService getAudioInterface() {
-        return IAudioService.Stub.asInterface(ServiceManager.checkService(Context.AUDIO_SERVICE));
+    static IAudioService getAudioService() {
+        IAudioService audioService = IAudioService.Stub.asInterface(
+                ServiceManager.checkService(Context.AUDIO_SERVICE));
+        if (audioService == null) {
+            Log.w(TAG, "Unable to find IAudioService interface.");
+        }
+        return audioService;
     }
 
     boolean keyguardOn() {
@@ -1062,7 +1179,7 @@
     /** {@inheritDoc} */
     @Override
     public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,
-            int keyCode, int metaState, int repeatCount, int policyFlags) {
+            int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) {
         final boolean keyguardOn = keyguardOn();
         final boolean down = (action == KeyEvent.ACTION_DOWN);
         final boolean canceled = ((flags & KeyEvent.FLAG_CANCELED) != 0);
@@ -1095,11 +1212,9 @@
                         // and his ONLY options are to answer or reject the call.)
                         boolean incomingRinging = false;
                         try {
-                            ITelephony phoneServ = getPhoneInterface();
-                            if (phoneServ != null) {
-                                incomingRinging = phoneServ.isRinging();
-                            } else {
-                                Log.w(TAG, "Unable to find ITelephony interface");
+                            ITelephony telephonyService = getTelephonyService();
+                            if (telephonyService != null) {
+                                incomingRinging = telephonyService.isRinging();
                             }
                         } catch (RemoteException ex) {
                             Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
@@ -1250,10 +1365,11 @@
     public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset) {
         final int fl = attrs.flags;
         
-        if ((fl &
-                (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
+        if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
                 == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
-            contentInset.set(mCurLeft, mCurTop, mW - mCurRight, mH - mCurBottom);
+            contentInset.set(mCurLeft, mCurTop,
+                    (mScreenLeft+mScreenWidth) - mCurRight,
+                    (mScreenTop+mScreenHeight) - mCurBottom);
         } else {
             contentInset.setEmpty();
         }
@@ -1261,8 +1377,9 @@
     
     /** {@inheritDoc} */
     public void beginLayoutLw(int displayWidth, int displayHeight) {
-        mW = displayWidth;
-        mH = displayHeight;
+        mScreenLeft = mScreenTop = 0;
+        mScreenWidth = displayWidth;
+        mScreenHeight = displayHeight;
         mDockLeft = mContentLeft = mCurLeft = 0;
         mDockTop = mContentTop = mCurTop = 0;
         mDockRight = mContentRight = mCurRight = displayWidth;
@@ -1283,15 +1400,40 @@
             if (mStatusBar.isVisibleLw()) {
                 // If the status bar is hidden, we don't want to cause
                 // windows behind it to scroll.
-                mDockTop = mContentTop = mCurTop = mStatusBar.getFrameLw().bottom;
-                if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: mDockBottom="
-                        + mDockBottom + " mContentBottom="
-                        + mContentBottom + " mCurBottom=" + mCurBottom);
+                final Rect r = mStatusBar.getFrameLw();
+                if (mStatusBarCanHide) {
+                    // Status bar may go away, so the screen area it occupies
+                    // is available to apps but just covering them when the
+                    // status bar is visible.
+                    if (mDockTop == r.top) mDockTop = r.bottom;
+                    else if (mDockBottom == r.bottom) mDockBottom = r.top;
+                    mContentTop = mCurTop = mDockTop;
+                    mContentBottom = mCurBottom = mDockBottom;
+                    if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: mDockTop=" + mDockTop
+                            + " mContentTop=" + mContentTop
+                            + " mCurTop=" + mCurTop
+                            + " mDockBottom=" + mDockBottom
+                            + " mContentBottom=" + mContentBottom
+                            + " mCurBottom=" + mCurBottom);
+                } else {
+                    // Status bar can't go away; the part of the screen it
+                    // covers does not exist for anything behind it.
+                    if (mScreenTop == r.top) {
+                        mScreenTop = r.bottom;
+                        mScreenHeight -= (r.bottom-r.top);
+                    } else if ((mScreenHeight-mScreenTop) == r.bottom) {
+                        mScreenHeight -= (r.bottom-r.top);
+                    }
+                    mContentTop = mCurTop = mDockTop = mScreenTop;
+                    mContentBottom = mCurBottom = mDockBottom = mScreenTop+mScreenHeight;
+                    if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: mScreenTop=" + mScreenTop
+                            + " mScreenHeight=" + mScreenHeight);
+                }
             }
         }
     }
 
-    void setAttachedWindowFrames(WindowState win, int fl, int sim,
+    void setAttachedWindowFrames(WindowState win, int fl, int adjust,
             WindowState attached, boolean insetDecors, Rect pf, Rect df, Rect cf, Rect vf) {
         if (win.getSurfaceLayer() > mDockLayer && attached.getSurfaceLayer() < mDockLayer) {
             // Here's a special case: if this attached window is a panel that is
@@ -1312,7 +1454,7 @@
             // window is positioned within that content.  Otherwise we can use
             // the display frame and let the attached window take care of
             // positioning its content appropriately.
-            if ((sim & SOFT_INPUT_MASK_ADJUST) != SOFT_INPUT_ADJUST_RESIZE) {
+            if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
                 cf.set(attached.getDisplayFrameLw());
             } else {
                 // If the window is resizing, then we want to base the content
@@ -1371,8 +1513,9 @@
             attrs.gravity = Gravity.BOTTOM;
             mDockLayer = win.getSurfaceLayer();
         } else {
-            if ((fl &
-                    (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
+            final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
+
+            if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
                     == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
                 // This is the case for a normal activity window: we want it
                 // to cover all of the screen space, and it can take care of
@@ -1383,11 +1526,11 @@
                     // frame is the same as the one we are attached to.
                     setAttachedWindowFrames(win, fl, sim, attached, true, pf, df, cf, vf);
                 } else {
-                    pf.left = df.left = 0;
-                    pf.top = df.top = 0;
-                    pf.right = df.right = mW;
-                    pf.bottom = df.bottom = mH;
-                    if ((sim & SOFT_INPUT_MASK_ADJUST) != SOFT_INPUT_ADJUST_RESIZE) {
+                    pf.left = df.left = mScreenLeft;
+                    pf.top = df.top = mScreenTop;
+                    pf.right = df.right = mScreenLeft+mScreenWidth;
+                    pf.bottom = df.bottom = mScreenTop+mScreenHeight;
+                    if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
                         cf.left = mDockLeft;
                         cf.top = mDockTop;
                         cf.right = mDockRight;
@@ -1398,26 +1541,34 @@
                         cf.right = mContentRight;
                         cf.bottom = mContentBottom;
                     }
-                    vf.left = mCurLeft;
-                    vf.top = mCurTop;
-                    vf.right = mCurRight;
-                    vf.bottom = mCurBottom;
+                    if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+                        vf.left = mCurLeft;
+                        vf.top = mCurTop;
+                        vf.right = mCurRight;
+                        vf.bottom = mCurBottom;
+                    } else {
+                        vf.set(cf);
+                    }
                 }
             } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0) {
                 // A window that has requested to fill the entire screen just
                 // gets everything, period.
-                pf.left = df.left = cf.left = 0;
-                pf.top = df.top = cf.top = 0;
-                pf.right = df.right = cf.right = mW;
-                pf.bottom = df.bottom = cf.bottom = mH;
-                vf.left = mCurLeft;
-                vf.top = mCurTop;
-                vf.right = mCurRight;
-                vf.bottom = mCurBottom;
+                pf.left = df.left = cf.left = mScreenLeft;
+                pf.top = df.top = cf.top = mScreenTop;
+                pf.right = df.right = cf.right = mScreenLeft+mScreenWidth;
+                pf.bottom = df.bottom = cf.bottom = mScreenTop+mScreenHeight;
+                if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+                    vf.left = mCurLeft;
+                    vf.top = mCurTop;
+                    vf.right = mCurRight;
+                    vf.bottom = mCurBottom;
+                } else {
+                    vf.set(cf);
+                }
             } else if (attached != null) {
                 // A child window should be placed inside of the same visible
                 // frame that its parent had.
-                setAttachedWindowFrames(win, fl, sim, attached, false, pf, df, cf, vf);
+                setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, cf, vf);
             } else {
                 // Otherwise, a normal window must be placed inside the content
                 // of all screen decorations.
@@ -1425,7 +1576,7 @@
                 pf.top = mContentTop;
                 pf.right = mContentRight;
                 pf.bottom = mContentBottom;
-                if ((sim & SOFT_INPUT_MASK_ADJUST) != SOFT_INPUT_ADJUST_RESIZE) {
+                if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
                     df.left = cf.left = mDockLeft;
                     df.top = cf.top = mDockTop;
                     df.right = cf.right = mDockRight;
@@ -1436,10 +1587,14 @@
                     df.right = cf.right = mContentRight;
                     df.bottom = cf.bottom = mContentBottom;
                 }
-                vf.left = mCurLeft;
-                vf.top = mCurTop;
-                vf.right = mCurRight;
-                vf.bottom = mCurBottom;
+                if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+                    vf.left = mCurLeft;
+                    vf.top = mCurTop;
+                    vf.right = mCurRight;
+                    vf.bottom = mCurBottom;
+                } else {
+                    vf.set(cf);
+                }
             }
         }
         
@@ -1532,8 +1687,12 @@
     /** {@inheritDoc} */
     public int finishAnimationLw() {
         int changes = 0;
-        
-        boolean hiding = false;
+        boolean topIsFullscreen = false;
+
+        final WindowManager.LayoutParams lp = (mTopFullscreenOpaqueWindowState != null)
+                ? mTopFullscreenOpaqueWindowState.getAttrs()
+                : null;
+
         if (mStatusBar != null) {
             if (localLOGV) Log.i(TAG, "force=" + mForceStatusBar
                     + " top=" + mTopFullscreenOpaqueWindowState);
@@ -1541,39 +1700,82 @@
                 if (DEBUG_LAYOUT) Log.v(TAG, "Showing status bar");
                 if (mStatusBar.showLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT;
             } else if (mTopFullscreenOpaqueWindowState != null) {
-                //Log.i(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
-                //        + " shown frame: " + mTopFullscreenOpaqueWindowState.getShownFrameLw());
-                //Log.i(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs());
-                WindowManager.LayoutParams lp =
-                    mTopFullscreenOpaqueWindowState.getAttrs();
-                boolean hideStatusBar =
-                    (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
-                if (hideStatusBar) {
-                    if (DEBUG_LAYOUT) Log.v(TAG, "Hiding status bar");
-                    if (mStatusBar.hideLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT;
-                    hiding = true;
+                if (localLOGV) {
+                    Log.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
+                            + " shown frame: " + mTopFullscreenOpaqueWindowState.getShownFrameLw());
+                    Log.d(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs()
+                            + " lp.flags=0x" + Integer.toHexString(lp.flags));
+                }
+                topIsFullscreen = (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
+                // The subtle difference between the window for mTopFullscreenOpaqueWindowState
+                // and mTopIsFullscreen is that that mTopIsFullscreen is set only if the window
+                // has the FLAG_FULLSCREEN set.  Not sure if there is another way that to be the
+                // case though.
+                if (topIsFullscreen) {
+                    if (mStatusBarCanHide) {
+                        if (DEBUG_LAYOUT) Log.v(TAG, "Hiding status bar");
+                        if (mStatusBar.hideLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT;
+                    } else if (localLOGV) {
+                        Log.v(TAG, "Preventing status bar from hiding by policy");
+                    }
                 } else {
                     if (DEBUG_LAYOUT) Log.v(TAG, "Showing status bar");
                     if (mStatusBar.showLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT;
                 }
             }
         }
-        
-        if (changes != 0 && hiding) {
-            IStatusBarService sbs = IStatusBarService.Stub.asInterface(ServiceManager.getService("statusbar"));
-            if (sbs != null) {
-                try {
-                    // Make sure the window shade is hidden.
-                    sbs.collapse();
-                } catch (RemoteException e) {
-                }
-            }
+
+        boolean topNeedsMenu = mShowMenuKey;
+        if (lp != null) {
+            topNeedsMenu = (lp.flags & WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY) != 0;
+        }
+
+        if (DEBUG_LAYOUT) Log.v(TAG, "Top window " 
+                + (topNeedsMenu ? "needs" : "does not need")
+                + " the MENU key");
+
+        final boolean changedFullscreen = (mTopIsFullscreen != topIsFullscreen);
+        final boolean changedMenu = (topNeedsMenu != mShowMenuKey);
+
+        if (changedFullscreen || changedMenu) {
+            final boolean topIsFullscreenF = topIsFullscreen;
+            final boolean topNeedsMenuF = topNeedsMenu;
+
+            mTopIsFullscreen = topIsFullscreen;
+            mShowMenuKey = topNeedsMenu;
+
+            mHandler.post(new Runnable() {
+                    public void run() {
+                        if (mStatusBarService == null) {
+                            // This is the one that can not go away, but it doesn't come up
+                            // before the window manager does, so don't fail if it doesn't
+                            // exist. This works as long as no fullscreen windows come up
+                            // before the status bar service does.
+                            mStatusBarService = IStatusBarService.Stub.asInterface(
+                                    ServiceManager.getService("statusbar"));
+                        }
+                        final IStatusBarService sbs = mStatusBarService;
+                        if (mStatusBarService != null) {
+                            try {
+                                if (changedFullscreen) {
+                                    sbs.setActiveWindowIsFullscreen(topIsFullscreenF);
+                                }
+                                if (changedMenu) {
+                                    sbs.setMenuKeyVisible(topNeedsMenuF);
+                                }
+                            } catch (RemoteException e) {
+                                // This should be impossible because we're in the same process.
+                                mStatusBarService = null;
+                            }
+                        }
+                    }
+                });
         }
 
         // Hide the key guard if a visible window explicitly specifies that it wants to be displayed
         // when the screen is locked
         if (mKeyguard != null) {
-            if (localLOGV) Log.v(TAG, "finishLayoutLw::mHideKeyguard="+mHideLockScreen);
+            if (localLOGV) Log.v(TAG, "finishAnimationLw::mHideKeyguard="+mHideLockScreen);
             if (mDismissKeyguard && !mKeyguardMediator.isSecure()) {
                 if (mKeyguard.hideLw(true)) {
                     changes |= FINISH_LAYOUT_REDO_LAYOUT
@@ -1623,9 +1825,9 @@
                     rect.union(w.getShownFrameLw());
                 }
             }
-            final int insetw = mW/10;
-            final int inseth = mH/10;
-            if (rect.contains(insetw, inseth, mW-insetw, mH-inseth)) {
+            final int insetw = mScreenWidth/10;
+            final int inseth = mScreenHeight/10;
+            if (rect.contains(insetw, inseth, mScreenWidth-insetw, mScreenHeight-inseth)) {
                 // All of the status bar windows put together cover the
                 // screen, so the app can't be seen.  (Note this test doesn't
                 // work if the rects of these windows are at off offsets or
@@ -1668,23 +1870,6 @@
     }
 
     /**
-     * @return Whether a telephone call is in progress right now.
-     */
-    boolean isInCall() {
-        final ITelephony phone = getPhoneInterface();
-        if (phone == null) {
-            Log.w(TAG, "couldn't get ITelephony reference");
-            return false;
-        }
-        try {
-            return phone.isOffhook();
-        } catch (RemoteException e) {
-            Log.w(TAG, "ITelephony.isOffhhook threw RemoteException " + e);
-            return false;
-        }
-    }
-
-    /**
      * @return Whether music is being played right now.
      */
     boolean isMusicActive() {
@@ -1701,17 +1886,17 @@
      * @param keycode
      */
     void handleVolumeKey(int stream, int keycode) {
-        final IAudioService audio = getAudioInterface();
-        if (audio == null) {
-            Log.w(TAG, "handleVolumeKey: couldn't get IAudioService reference");
+        IAudioService audioService = getAudioService();
+        if (audioService == null) {
             return;
         }
         try {
             // since audio is playing, we shouldn't have to hold a wake lock
             // during the call, but we do it as a precaution for the rare possibility
             // that the music stops right before we call this
+            // TODO: Actually handle MUTE.
             mBroadcastWakeLock.acquire();
-            audio.adjustStreamVolume(stream,
+            audioService.adjustStreamVolume(stream,
                 keycode == KeyEvent.KEYCODE_VOLUME_UP
                             ? AudioManager.ADJUST_RAISE
                             : AudioManager.ADJUST_LOWER,
@@ -1722,39 +1907,15 @@
             mBroadcastWakeLock.release();
         }
     }
-    
-    static boolean isMediaKey(int code) {
-        if (code == KeyEvent.KEYCODE_HEADSETHOOK || 
-                code == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
-                code == KeyEvent.KEYCODE_MEDIA_STOP || 
-                code == KeyEvent.KEYCODE_MEDIA_NEXT ||
-                code == KeyEvent.KEYCODE_MEDIA_PREVIOUS || 
-                code == KeyEvent.KEYCODE_MEDIA_REWIND ||
-                code == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) {
-            return true;
-        }
-        return false;    
-    }
- 
+
     /** {@inheritDoc} */
     @Override
-    public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
-            int policyFlags, boolean isScreenOn) {
-        int result = ACTION_PASS_TO_USER;
+    public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
+            int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {
+        final boolean down = action == KeyEvent.ACTION_DOWN;
+        final boolean canceled = (flags & KeyEvent.FLAG_CANCELED) != 0;
 
-        if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0) {
-            performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
-        }
-
-        final boolean isWakeKey = (policyFlags
-                & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
-
-        // If the key is injected, pretend that the screen is on and don't let the
-        // device go to sleep.  This feature is mainly used for testing purposes.
         final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
-        if (isInjected) {
-            isScreenOn = true;
-        }
 
         // If screen is off then we treat the case where the keyguard is open but hidden
         // the same as if it were open and in front.
@@ -1769,202 +1930,211 @@
                   + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive);
         }
 
-        if (keyguardActive) {
-            if (isScreenOn) {
-                // when the screen is on, always give the event to the keyguard
-                result |= ACTION_PASS_TO_USER;
-            } else {
-                // otherwise, don't pass it to the user
-                result &= ~ACTION_PASS_TO_USER;
+        if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0) {
+            performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
+        }
 
-                if (isWakeKey && down) {
+        // Basic policy based on screen state and keyguard.
+        // FIXME: This policy isn't quite correct.  We shouldn't care whether the screen
+        //        is on or off, really.  We should care about whether the device is in an
+        //        interactive state or is in suspend pretending to be "off".
+        //        The primary screen might be turned off due to proximity sensor or
+        //        because we are presenting media on an auxiliary screen or remotely controlling
+        //        the device some other way (which is why we have an exemption here for injected
+        //        events).
+        int result;
+        if (isScreenOn || isInjected) {
+            // When the screen is on or if the key is injected pass the key to the application.
+            result = ACTION_PASS_TO_USER;
+        } else {
+            // When the screen is off and the key is not injected, determine whether
+            // to wake the device but don't pass the key to the application.
+            result = 0;
 
-                    // tell the mediator about a wake key, it may decide to
-                    // turn on the screen depending on whether the key is
-                    // appropriate.
-                    if (!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode)
-                            && (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
-                                || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
-                        // when keyguard is showing and screen off, we need
-                        // to handle the volume key for calls and  music here
-                        if (isInCall()) {
-                            handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
-                        } else if (isMusicActive()) {
-                            handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode);
-                        }
-                    }
+            final boolean isWakeKey = (policyFlags
+                    & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
+            if (down && isWakeKey) {
+                if (keyguardActive) {
+                    // If the keyguard is showing, let it decide what to do with the wake key.
+                    mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);
+                } else {
+                    // Otherwise, wake the device ourselves.
+                    result |= ACTION_POKE_USER_ACTIVITY;
                 }
             }
-        } else if (!isScreenOn) {
-            // If we are in-call with screen off and keyguard is not showing,
-            // then handle the volume key ourselves.
-            // This is necessary because the phone app will disable the keyguard
-            // when the proximity sensor is in use.
-            if (isInCall() &&
-                     (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
-                                || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
-                result &= ~ACTION_PASS_TO_USER;
-                handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
-            }
-            if (isWakeKey) {
-                // a wake key has a sole purpose of waking the device; don't pass
-                // it to the user
-                result |= ACTION_POKE_USER_ACTIVITY;
-                result &= ~ACTION_PASS_TO_USER;
-            }
         }
 
-        if (keyCode == KeyEvent.KEYCODE_ENDCALL
-                || keyCode == KeyEvent.KEYCODE_POWER) {
-            if (down) {
-                boolean handled = false;
-                boolean hungUp = false;
-                // key repeats are generated by the window manager, and we don't see them
-                // here, so unless the driver is doing something it shouldn't be, we know
-                // this is the real press event.
-                ITelephony phoneServ = getPhoneInterface();
-                if (phoneServ != null) {
-                    try {
-                        if (keyCode == KeyEvent.KEYCODE_ENDCALL) {
-                            handled = hungUp = phoneServ.endCall();
-                        } else if (keyCode == KeyEvent.KEYCODE_POWER) {
-                            if (phoneServ.isRinging()) {
-                                // Pressing Power while there's a ringing incoming
-                                // call should silence the ringer.
-                                phoneServ.silenceRinger();
-                                handled = true;
-                            } else if (phoneServ.isOffhook() &&
-                                       ((mIncallPowerBehavior
-                                         & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP)
-                                        != 0)) {
-                                // Otherwise, if "Power button ends call" is enabled,
-                                // the Power button will hang up any current active call.
-                                handled = hungUp = phoneServ.endCall();
+        // Handle special keys.
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+            case KeyEvent.KEYCODE_VOLUME_UP:
+            case KeyEvent.KEYCODE_VOLUME_MUTE: {
+                if (down) {
+                    ITelephony telephonyService = getTelephonyService();
+                    if (telephonyService != null) {
+                        try {
+                            if (telephonyService.isRinging()) {
+                                // If an incoming call is ringing, either VOLUME key means
+                                // "silence ringer".  We handle these keys here, rather than
+                                // in the InCallScreen, to make sure we'll respond to them
+                                // even if the InCallScreen hasn't come to the foreground yet.
+                                // Look for the DOWN event here, to agree with the "fallback"
+                                // behavior in the InCallScreen.
+                                Log.i(TAG, "interceptKeyBeforeQueueing:"
+                                      + " VOLUME key-down while ringing: Silence ringer!");
+
+                                // Silence the ringer.  (It's safe to call this
+                                // even if the ringer has already been silenced.)
+                                telephonyService.silenceRinger();
+
+                                // And *don't* pass this key thru to the current activity
+                                // (which is probably the InCallScreen.)
+                                result &= ~ACTION_PASS_TO_USER;
+                                break;
+                            }
+                            if (telephonyService.isOffhook()
+                                    && (result & ACTION_PASS_TO_USER) == 0) {
+                                // If we are in call but we decided not to pass the key to
+                                // the application, handle the volume change here.
+                                handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
+                                break;
+                            }
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                        }
+                    }
+
+                    if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) {
+                        // If music is playing but we decided not to pass the key to the
+                        // application, handle the volume change here.
+                        handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode);
+                        break;
+                    }
+                }
+                break;
+            }
+
+            case KeyEvent.KEYCODE_ENDCALL: {
+                result &= ~ACTION_PASS_TO_USER;
+                if (down) {
+                    ITelephony telephonyService = getTelephonyService();
+                    boolean hungUp = false;
+                    if (telephonyService != null) {
+                        try {
+                            hungUp = telephonyService.endCall();
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                        }
+                    }
+                    interceptPowerKeyDown(!isScreenOn || hungUp);
+                } else {
+                    if (interceptPowerKeyUp(canceled)) {
+                        if ((mEndcallBehavior
+                                & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {
+                            if (goHome()) {
+                                break;
                             }
                         }
-                    } catch (RemoteException ex) {
-                        Log.w(TAG, "ITelephony threw RemoteException" + ex);
+                        if ((mEndcallBehavior
+                                & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
+                            result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;
+                        }
                     }
+                }
+                break;
+            }
+
+            case KeyEvent.KEYCODE_POWER: {
+                result &= ~ACTION_PASS_TO_USER;
+                if (down) {
+                    ITelephony telephonyService = getTelephonyService();
+                    boolean hungUp = false;
+                    if (telephonyService != null) {
+                        try {
+                            if (telephonyService.isRinging()) {
+                                // Pressing Power while there's a ringing incoming
+                                // call should silence the ringer.
+                                telephonyService.silenceRinger();
+                            } else if ((mIncallPowerBehavior
+                                    & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
+                                    && telephonyService.isOffhook()) {
+                                // Otherwise, if "Power button ends call" is enabled,
+                                // the Power button will hang up any current active call.
+                                hungUp = telephonyService.endCall();
+                            }
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                        }
+                    }
+                    interceptPowerKeyDown(!isScreenOn || hungUp);
                 } else {
-                    Log.w(TAG, "!!! Unable to find ITelephony interface !!!");
-                }
-
-                if (!isScreenOn
-                        || (handled && keyCode != KeyEvent.KEYCODE_POWER)
-                        || (handled && hungUp && keyCode == KeyEvent.KEYCODE_POWER)) {
-                    mShouldTurnOffOnKeyUp = false;
-                } else {
-                    // Only try to turn off the screen if we didn't already hang up.
-                    mShouldTurnOffOnKeyUp = true;
-                    mHandler.postDelayed(mPowerLongPress,
-                            ViewConfiguration.getGlobalActionKeyTimeout());
-                    result &= ~ACTION_PASS_TO_USER;
-                }
-            } else {
-                mHandler.removeCallbacks(mPowerLongPress);
-                if (mShouldTurnOffOnKeyUp) {
-                    mShouldTurnOffOnKeyUp = false;
-                    boolean gohome, sleeps;
-                    if (keyCode == KeyEvent.KEYCODE_ENDCALL) {
-                        gohome = (mEndcallBehavior
-                                  & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0;
-                        sleeps = (mEndcallBehavior
-                                  & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0;
-                    } else {
-                        gohome = false;
-                        sleeps = true;
+                    if (interceptPowerKeyUp(canceled)) {
+                        result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;
                     }
-                    if (keyguardActive
-                            || (sleeps && !gohome)
-                            || (gohome && !goHome() && sleeps)) {
-                        // They must already be on the keyguard or home screen,
-                        // go to sleep instead unless the event was injected.
-                        if (!isInjected) {
-                            Log.d(TAG, "I'm tired mEndcallBehavior=0x"
-                                    + Integer.toHexString(mEndcallBehavior));
-                            result &= ~ACTION_POKE_USER_ACTIVITY;
-                            result |= ACTION_GO_TO_SLEEP;
+                }
+                break;
+            }
+
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                if (down) {
+                    ITelephony telephonyService = getTelephonyService();
+                    if (telephonyService != null) {
+                        try {
+                            if (!telephonyService.isIdle()) {
+                                // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
+                                // to avoid music playback.
+                                break;
+                            }
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "ITelephony threw RemoteException", ex);
                         }
                     }
-                    result &= ~ACTION_PASS_TO_USER;
                 }
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MUTE:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+            case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+                if ((result & ACTION_PASS_TO_USER) == 0) {
+                    // Only do this if we would otherwise not pass it to the user. In that
+                    // case, the PhoneWindow class will do the same thing, except it will
+                    // only do it if the showing app doesn't process the key on its own.
+                    long when = whenNanos / 1000000;
+                    KeyEvent keyEvent = new KeyEvent(when, when, action, keyCode, 0, 0,
+                            0, scanCode, flags, InputDevice.SOURCE_KEYBOARD);
+                    mBroadcastWakeLock.acquire();
+                    mHandler.post(new PassHeadsetKey(keyEvent));
+                }
+                break;
             }
-        } else if (isMediaKey(keyCode)) {
-            // This key needs to be handled even if the screen is off.
-            // If others need to be handled while it's off, this is a reasonable
-            // pattern to follow.
-            if ((result & ACTION_PASS_TO_USER) == 0) {
-                // Only do this if we would otherwise not pass it to the user. In that
-                // case, the PhoneWindow class will do the same thing, except it will
-                // only do it if the showing app doesn't process the key on its own.
-                long when = whenNanos / 1000000;
-                KeyEvent keyEvent = new KeyEvent(when, when,
-                        down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
-                        keyCode, 0);
-                mBroadcastWakeLock.acquire();
-                mHandler.post(new PassHeadsetKey(keyEvent));
-            }
-        } else if (keyCode == KeyEvent.KEYCODE_CALL) {
-            // If an incoming call is ringing, answer it!
-            // (We handle this key here, rather than in the InCallScreen, to make
-            // sure we'll respond to the key even if the InCallScreen hasn't come to
-            // the foreground yet.)
 
-            // We answer the call on the DOWN event, to agree with
-            // the "fallback" behavior in the InCallScreen.
-            if (down) {
-                try {
-                    ITelephony phoneServ = getPhoneInterface();
-                    if (phoneServ != null) {
-                        if (phoneServ.isRinging()) {
-                            Log.i(TAG, "interceptKeyTq:"
-                                  + " CALL key-down while ringing: Answer the call!");
-                            phoneServ.answerRingingCall();
+            case KeyEvent.KEYCODE_CALL: {
+                if (down) {
+                    ITelephony telephonyService = getTelephonyService();
+                    if (telephonyService != null) {
+                        try {
+                            if (telephonyService.isRinging()) {
+                                Log.i(TAG, "interceptKeyBeforeQueueing:"
+                                      + " CALL key-down while ringing: Answer the call!");
+                                telephonyService.answerRingingCall();
 
-                            // And *don't* pass this key thru to the current activity
-                            // (which is presumably the InCallScreen.)
-                            result &= ~ACTION_PASS_TO_USER;
+                                // And *don't* pass this key thru to the current activity
+                                // (which is presumably the InCallScreen.)
+                                result &= ~ACTION_PASS_TO_USER;
+                            }
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "ITelephony threw RemoteException", ex);
                         }
-                    } else {
-                        Log.w(TAG, "CALL button: Unable to find ITelephony interface");
                     }
-                } catch (RemoteException ex) {
-                    Log.w(TAG, "CALL button: RemoteException from getPhoneInterface()", ex);
                 }
-            }
-        } else if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP)
-                   || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
-            // If an incoming call is ringing, either VOLUME key means
-            // "silence ringer".  We handle these keys here, rather than
-            // in the InCallScreen, to make sure we'll respond to them
-            // even if the InCallScreen hasn't come to the foreground yet.
-
-            // Look for the DOWN event here, to agree with the "fallback"
-            // behavior in the InCallScreen.
-            if (down) {
-                try {
-                    ITelephony phoneServ = getPhoneInterface();
-                    if (phoneServ != null) {
-                        if (phoneServ.isRinging()) {
-                            Log.i(TAG, "interceptKeyTq:"
-                                  + " VOLUME key-down while ringing: Silence ringer!");
-                            // Silence the ringer.  (It's safe to call this
-                            // even if the ringer has already been silenced.)
-                            phoneServ.silenceRinger();
-
-                            // And *don't* pass this key thru to the current activity
-                            // (which is probably the InCallScreen.)
-                            result &= ~ACTION_PASS_TO_USER;
-                        }
-                    } else {
-                        Log.w(TAG, "VOLUME button: Unable to find ITelephony interface");
-                    }
-                } catch (RemoteException ex) {
-                    Log.w(TAG, "VOLUME button: RemoteException from getPhoneInterface()", ex);
-                }
+                break;
             }
         }
-
         return result;
     }
 
@@ -2131,6 +2301,8 @@
                 return mCarDockRotation;
             } else if (mDockMode == Intent.EXTRA_DOCK_STATE_DESK && mDeskDockRotation >= 0) {
                 return mDeskDockRotation;
+            } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
+                return mUserRotation;
             } else {
                 if (useSensorForOrientationLp(orientation)) {
                     return mOrientationListener.getCurrentRotation(lastRotation);
@@ -2174,6 +2346,26 @@
         return sensorRotation == mPortraitRotation || sensorRotation == mUpsideDownRotation;
     }
 
+
+    // User rotation: to be used when all else fails in assigning an orientation to the device
+    public void setUserRotationMode(int mode, int rot) {
+        ContentResolver res = mContext.getContentResolver();
+        mUserRotationMode = mode;
+        if (mode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
+            mUserRotation = rot;
+            Settings.System.putInt(res,
+                    Settings.System.ACCELEROMETER_ROTATION,
+                    0);
+            Settings.System.putInt(res,
+                    Settings.System.USER_ROTATION,
+                    rot);
+        } else {
+            Settings.System.putInt(res,
+                    Settings.System.ACCELEROMETER_ROTATION,
+                    1);
+        }
+    }
+
     public boolean detectSafeMode() {
         try {
             int menuState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_MENU);
diff --git a/policy/src/com/android/internal/policy/impl/PowerDialog.java b/policy/src/com/android/internal/policy/impl/PowerDialog.java
deleted file mode 100644
index de35bd7..0000000
--- a/policy/src/com/android/internal/policy/impl/PowerDialog.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * 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.policy.impl;
-
-import com.android.internal.R;
-
-import android.app.Dialog;
-import android.app.StatusBarManager;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.LocalPowerManager;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-
-import com.android.internal.app.ShutdownThread;
-import com.android.internal.telephony.ITelephony;
-import android.view.KeyEvent;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.View.OnClickListener;
-import android.view.View.OnKeyListener;
-import android.widget.Button;
-
-/**
- * @deprecated use {@link GlobalActions} instead.
- */
-public class PowerDialog extends Dialog implements OnClickListener,
-        OnKeyListener {
-    private static final String TAG = "PowerDialog";
-
-    static private StatusBarManager sStatusBar;
-    private Button mKeyguard;
-    private Button mPower;
-    private Button mRadioPower;
-    private Button mSilent;
-
-    private LocalPowerManager mPowerManager;
-
-    public PowerDialog(Context context, LocalPowerManager powerManager) {
-        super(context);
-        mPowerManager = powerManager;
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        Context context = getContext();
-
-        if (sStatusBar == null) {
-            sStatusBar = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
-        }
-
-        setContentView(com.android.internal.R.layout.power_dialog);
-
-        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
-        if (!getContext().getResources().getBoolean(
-                com.android.internal.R.bool.config_sf_slowBlur)) {
-            getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
-                    WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
-        }
-        getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
-                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
-
-        setTitle(context.getText(R.string.power_dialog));
-
-        mKeyguard = (Button) findViewById(R.id.keyguard);
-        mPower = (Button) findViewById(R.id.off);
-        mRadioPower = (Button) findViewById(R.id.radio_power);
-        mSilent = (Button) findViewById(R.id.silent);
-
-        if (mKeyguard != null) {
-            mKeyguard.setOnKeyListener(this);
-            mKeyguard.setOnClickListener(this);
-        }
-        if (mPower != null) {
-            mPower.setOnClickListener(this);
-        }
-        if (mRadioPower != null) {
-            mRadioPower.setOnClickListener(this);
-        }
-        if (mSilent != null) {
-            mSilent.setOnClickListener(this);
-            // XXX: HACK for now hide the silent until we get mute support
-            mSilent.setVisibility(View.GONE);
-        }
-
-        CharSequence text;
-
-        // set the keyguard button's text
-        text = context.getText(R.string.screen_lock);
-        mKeyguard.setText(text);
-        mKeyguard.requestFocus();
-
-        try {
-            ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
-            if (phone != null) {
-                text = phone.isRadioOn() ? context
-                        .getText(R.string.turn_off_radio) : context
-                        .getText(R.string.turn_on_radio);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        mRadioPower.setText(text);
-    }
-
-    public void onClick(View v) {
-        this.dismiss();
-        if (v == mPower) {
-            // shutdown by making sure radio and power are handled accordingly.
-            ShutdownThread.shutdown(getContext(), true);
-        } else if (v == mRadioPower) {
-            try {
-                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
-                if (phone != null) {
-                    phone.toggleRadioOnOff();
-                }
-            } catch (RemoteException ex) {
-                // ignore it
-            }
-        } else if (v == mSilent) {
-            // do something
-        } else if (v == mKeyguard) {
-            if (v.isInTouchMode()) {
-                // only in touch mode for the reasons explained in onKey.
-                this.dismiss();
-                mPowerManager.goToSleep(SystemClock.uptimeMillis() + 1);
-            }
-        }
-    }
-
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-        // The activate keyguard button needs to put the device to sleep on the
-        // key up event. If we try to put it to sleep on the click or down
-        // action
-        // the the up action will cause the device to wake back up.
-
-        // Log.i(TAG, "keyCode: " + keyCode + " action: " + event.getAction());
-        if (keyCode != KeyEvent.KEYCODE_DPAD_CENTER
-                || event.getAction() != KeyEvent.ACTION_UP) {
-            // Log.i(TAG, "getting out of dodge...");
-            return false;
-        }
-
-        // Log.i(TAG, "Clicked mKeyguard! dimissing dialog");
-        this.dismiss();
-        // Log.i(TAG, "onKey: turning off the screen...");
-        // XXX: This is a hack for now
-        mPowerManager.goToSleep(event.getEventTime() + 1);
-        return true;
-    }
-
-    public void show() {
-        super.show();
-        Log.d(TAG, "show... disabling expand");
-        sStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
-    }
-
-    public void dismiss() {
-        super.dismiss();
-        Log.d(TAG, "dismiss... reenabling expand");
-        sStatusBar.disable(StatusBarManager.DISABLE_NONE);
-    }
-}
diff --git a/policy/src/com/android/internal/policy/impl/RecentApplicationsBackground.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsBackground.java
index 7c99e87..8d87728 100644
--- a/policy/src/com/android/internal/policy/impl/RecentApplicationsBackground.java
+++ b/policy/src/com/android/internal/policy/impl/RecentApplicationsBackground.java
@@ -72,6 +72,12 @@
     }
 
     @Override
+    public void jumpDrawablesToCurrentState() {
+        super.jumpDrawablesToCurrentState();
+        if (mBackground != null) mBackground.jumpToCurrentState();
+    }
+
+    @Override
     protected void drawableStateChanged() {
         Drawable d = mBackground;
         if (d != null && d.isStateful()) {
diff --git a/policy/src/com/android/internal/policy/impl/StatusView.java b/policy/src/com/android/internal/policy/impl/StatusView.java
new file mode 100644
index 0000000..2b78851
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/StatusView.java
@@ -0,0 +1,261 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+
+package com.android.internal.policy.impl;
+
+import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
+import com.google.android.util.AbstractMessageParser.Resources;
+
+import java.util.Date;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.format.DateFormat;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+class StatusView {
+    private String mDateFormatString;
+
+    private TextView mCarrier;
+    private TextView mDate;
+
+    // are we showing battery information?
+    private boolean mShowingBatteryInfo = false;
+
+    // last known plugged in state
+    private boolean mPluggedIn = false;
+
+    // last known battery level
+    private int mBatteryLevel = 100;
+
+    private String mNextAlarm = null;
+
+    private String mInstructions = null;
+    private TextView mStatus1;
+    private TextView mStatus2;
+    private TextView mPropertyOf;
+
+    private boolean mHasStatus2;
+    private boolean mHasCarrier;
+    private boolean mHasDate;
+    private boolean mHasProperty;
+
+    private View mView;
+
+    private View findViewById(int id) {
+        return mView.findViewById(id);
+    }
+
+    private Context getContext() {
+        return mView.getContext();
+    }
+
+    void setInstructions(String instructions) {
+        mInstructions = instructions;
+    }
+
+    void setCarrierText(CharSequence carrierText) {
+        if (mCarrier != null) {
+            mCarrier.setText(carrierText);
+        }
+    }
+
+    void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
+        mShowingBatteryInfo = showBatteryInfo;
+        mPluggedIn = pluggedIn;
+        mBatteryLevel = batteryLevel;
+        updateStatusLines();
+    }
+
+    void onTimeChanged() {
+        refreshTimeAndDateDisplay();
+    }
+
+    public void onRingerModeChanged(int state) {
+    }
+
+    void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+        setCarrierText(LockScreen.getCarrierString(plmn, spn));
+    }
+
+    public StatusView(View view, KeyguardUpdateMonitor updateMonitor,
+                  LockPatternUtils lockPatternUtils) {
+        mView = view;
+        mCarrier = (TextView) findViewById(R.id.carrier);
+        mHasCarrier = (mCarrier != null);
+        mDate = (TextView) findViewById(R.id.date);
+        mHasDate = (mDate != null);
+        mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);
+
+        refreshTimeAndDateDisplay();
+
+        mStatus1 = (TextView) findViewById(R.id.status1);
+        mStatus2 = (TextView) findViewById(R.id.status2);
+        mHasStatus2 = (mStatus2 != null);
+        mPropertyOf = (TextView) findViewById(R.id.propertyOf);
+        mHasProperty = (mPropertyOf != null);
+
+        resetStatusInfo(updateMonitor, lockPatternUtils);
+
+        // Required to get Marquee to work.
+        if (mHasCarrier) {
+            mCarrier.setSelected(true);
+            mCarrier.setTextColor(0xffffffff);
+        }
+
+    }
+
+    void resetStatusInfo(KeyguardUpdateMonitor updateMonitor, LockPatternUtils lockPatternUtils) {
+        mInstructions = null;
+        mShowingBatteryInfo = updateMonitor.shouldShowBatteryInfo();
+        mPluggedIn = updateMonitor.isDevicePluggedIn();
+        mBatteryLevel = updateMonitor.getBatteryLevel();
+        mNextAlarm = lockPatternUtils.getNextAlarm();
+        updateStatusLines();
+    }
+
+    void setInstructionText(int stringId) {
+        mStatus1.setText(stringId);
+        mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0, 0, 0);
+        mStatus1.setVisibility(View.VISIBLE);
+    }
+
+    void setInstructionText(String string) {
+        mStatus1.setText(string);
+        mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0, 0, 0);
+        mStatus1.setVisibility(View.VISIBLE);
+    }
+
+    void setCarrierText(int stringId) {
+        mCarrier.setText(stringId);
+    }
+    void setCarrierText(String string) {
+        mCarrier.setText(string);
+    }
+
+    /** Originated from PatternUnlockScreen **/
+    void updateStatusLines() {
+        if (mHasProperty) {
+            ContentResolver res = getContext().getContentResolver();
+            String info = Settings.Secure.getString(res, Settings.Secure.LOCK_SCREEN_OWNER_INFO);
+            boolean enabled = Settings.Secure.getInt(res,
+                    Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED, 1) != 0;
+
+            mPropertyOf.setText(info);
+            mPropertyOf.setVisibility(enabled && !TextUtils.isEmpty(info) ?
+                    View.VISIBLE : View.INVISIBLE);
+        }
+
+        if (!mHasStatus2) return;
+
+        if (mInstructions != null) {
+            // instructions only
+            mStatus1.setText(mInstructions);
+            if (TextUtils.isEmpty(mInstructions)) {
+                mStatus1.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+            } else {
+                mStatus1.setCompoundDrawablesWithIntrinsicBounds(
+                        R.drawable.ic_lock_idle_lock, 0, 0, 0);
+            }
+
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatus2.setVisibility(View.INVISIBLE);
+        } else if (mShowingBatteryInfo && mNextAlarm == null) {
+            // battery only
+            if (mPluggedIn) {
+              if (mBatteryLevel >= 100) {
+                mStatus1.setText(getContext().getString(R.string.lockscreen_charged));
+              } else {
+                  mStatus1.setText(getContext().getString(R.string.lockscreen_plugged_in,
+                          mBatteryLevel));
+              }
+            } else {
+                mStatus1.setText(getContext().getString(R.string.lockscreen_low_battery));
+            }
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0,
+                    0, 0);
+
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatus2.setVisibility(View.INVISIBLE);
+
+        } else if (mNextAlarm != null && !mShowingBatteryInfo) {
+            // alarm only
+            mStatus1.setText(mNextAlarm);
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0,
+                    0, 0);
+
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatus2.setVisibility(View.INVISIBLE);
+        } else if (mNextAlarm != null && mShowingBatteryInfo) {
+            // both battery and next alarm
+            mStatus1.setText(mNextAlarm);
+            mStatus2.setText(getContext().getString(
+                    R.string.lockscreen_battery_short,
+                    Math.min(100, mBatteryLevel)));
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0,
+                    0, 0);
+            if (mPluggedIn) {
+                mStatus2.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging,
+                        0, 0, 0);
+            } else {
+                mStatus2.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+            }
+
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatus2.setVisibility(View.VISIBLE);
+        } else {
+            // nothing specific to show; show general instructions
+            mStatus1.setText(R.string.lockscreen_pattern_instructions);
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0,
+                    0, 0);
+
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatus2.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    /** Originated from LockScreen **/
+    // TODO Merge with function above
+    void updateStatusLines(boolean showStatusLines, String charging, Drawable chargingIcon,
+            Drawable alarmIcon) {
+        if (!showStatusLines || (charging == null && mNextAlarm == null)) {
+            mStatus1.setVisibility(View.INVISIBLE);
+            mStatus2.setVisibility(View.INVISIBLE);
+        } else if (charging != null && mNextAlarm == null) {
+            // charging only
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatus2.setVisibility(View.INVISIBLE);
+
+            mStatus1.setText(charging);
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(chargingIcon, null, null, null);
+        } else if (mNextAlarm != null && charging == null) {
+            // next alarm only
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatus2.setVisibility(View.INVISIBLE);
+
+            mStatus1.setText(mNextAlarm);
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(alarmIcon, null, null, null);
+        } else if (charging != null && mNextAlarm != null) {
+            // both charging and next alarm
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatus2.setVisibility(View.VISIBLE);
+
+            mStatus1.setText(charging);
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(chargingIcon, null, null, null);
+            mStatus2.setText(mNextAlarm);
+            mStatus2.setCompoundDrawablesWithIntrinsicBounds(alarmIcon, null, null, null);
+        }
+    }
+
+    void refreshTimeAndDateDisplay() {
+        if (mHasDate) {
+            mDate.setText(DateFormat.format(mDateFormatString, new Date()));
+        }
+    }
+
+}
diff --git a/preloaded-classes b/preloaded-classes
index da03111..6d5144f 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -757,6 +757,7 @@
 android.widget.LinearLayout
 android.widget.LinearLayout$LayoutParams
 android.widget.ListAdapter
+android.widget.ListPopupWindow
 android.widget.ListView
 android.widget.ListView$ArrowScrollFocusResult
 android.widget.NumberPicker
@@ -865,6 +866,8 @@
 com.android.internal.view.menu.IconMenuView
 com.android.internal.view.menu.MenuBuilder
 com.android.internal.view.menu.MenuBuilder$Callback
+com.android.internal.view.menu.MenuItemImpl
+com.android.internal.view.menu.SubMenuBuilder
 com.android.internal.widget.ContactHeaderWidget
 com.android.internal.widget.DialogTitle
 com.android.internal.widget.WeightedLinearLayout
@@ -917,7 +920,6 @@
 java.io.File
 java.io.FileDescriptor
 java.io.FileInputStream
-java.io.FileInputStream$RepositioningLock
 java.io.FileNotFoundException
 java.io.FileOutputStream
 java.io.FilterInputStream
@@ -1437,6 +1439,7 @@
 org.apache.harmony.nio.FileChannelFactory
 org.apache.harmony.nio.internal.DirectBuffer
 org.apache.harmony.nio.internal.FileChannelImpl
+org.apache.harmony.nio.internal.FileLockImpl
 org.apache.harmony.nio.internal.FileChannelImpl$RepositioningLock
 org.apache.harmony.nio.internal.LockManager
 org.apache.harmony.nio.internal.LockManager$1
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index 425ca31..86d4c9f 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -356,7 +356,9 @@
         break;
     case AudioSystem::FOR_MEDIA:
         if (config != AudioSystem::FORCE_HEADPHONES && config != AudioSystem::FORCE_BT_A2DP &&
-            config != AudioSystem::FORCE_WIRED_ACCESSORY && config != AudioSystem::FORCE_NONE) {
+            config != AudioSystem::FORCE_WIRED_ACCESSORY &&
+            config != AudioSystem::FORCE_ANALOG_DOCK &&
+            config != AudioSystem::FORCE_DIGITAL_DOCK && config != AudioSystem::FORCE_NONE) {
             LOGW("setForceUse() invalid config %d for FOR_MEDIA", config);
             return;
         }
@@ -372,7 +374,10 @@
         break;
     case AudioSystem::FOR_DOCK:
         if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK &&
-            config != AudioSystem::FORCE_BT_DESK_DOCK && config != AudioSystem::FORCE_WIRED_ACCESSORY) {
+            config != AudioSystem::FORCE_BT_DESK_DOCK &&
+            config != AudioSystem::FORCE_WIRED_ACCESSORY &&
+            config != AudioSystem::FORCE_ANALOG_DOCK &&
+            config != AudioSystem::FORCE_DIGITAL_DOCK) {
             LOGW("setForceUse() invalid config %d for FOR_DOCK", config);
         }
         forceVolumeReeval = true;
@@ -1017,8 +1022,8 @@
 #ifdef AUDIO_POLICY_TEST
     Thread(false),
 #endif //AUDIO_POLICY_TEST
-    mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0),
-    mLimitRingtoneVolume(false), mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0)
+    mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false),
+    mLastVoiceVolume(-1.0f), mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0)
 {
     mpClientInterface = clientInterface;
 
@@ -1366,6 +1371,7 @@
 
 void AudioPolicyManagerBase::closeA2dpOutputs()
 {
+
     LOGV("setDeviceConnectionState() closing A2DP and duplicated output!");
 
     if (mDuplicatedOutput != 0) {
@@ -1558,6 +1564,8 @@
             if (device) break;
             device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
             if (device) break;
+            device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
+            if (device) break;
 #ifdef WITH_A2DP
             // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
             if (mPhoneState != AudioSystem::MODE_IN_CALL) {
@@ -1617,6 +1625,12 @@
         if (device2 == 0) {
             device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
         }
+        if (device2 == 0) {
+            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET;
+        }
+        if (device2 == 0) {
+            device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET;
+        }
 #ifdef WITH_A2DP
         if (mA2dpOutput != 0) {
             if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) {
@@ -1797,7 +1811,9 @@
         (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP |
         AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
         AudioSystem::DEVICE_OUT_WIRED_HEADSET |
-        AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) &&
+        AudioSystem::DEVICE_OUT_WIRED_HEADPHONE |
+        AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET |
+        AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET)) &&
         (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) &&
         streamDesc.mCanBeMuted) {
         volume *= SONIFICATION_HEADSET_VOLUME_FACTOR;
@@ -1835,29 +1851,38 @@
     }
 
     float volume = computeVolume(stream, index, output, device);
-    // do not set volume if the float value did not change
-    if (volume != mOutputs.valueFor(output)->mCurVolume[stream] || force) {
+    // We actually change the volume if:
+    // - the float value returned by computeVolume() changed
+    // - the force flag is set
+    if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
+            force) {
         mOutputs.valueFor(output)->mCurVolume[stream] = volume;
         LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
         if (stream == AudioSystem::VOICE_CALL ||
             stream == AudioSystem::DTMF ||
             stream == AudioSystem::BLUETOOTH_SCO) {
-            float voiceVolume = -1.0;
             // offset value to reflect actual hardware volume that never reaches 0
             // 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java)
             volume = 0.01 + 0.99 * volume;
-            if (stream == AudioSystem::VOICE_CALL) {
-                voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
-            } else if (stream == AudioSystem::BLUETOOTH_SCO) {
-                voiceVolume = 1.0;
-            }
-            if (voiceVolume >= 0 && output == mHardwareOutput) {
-                mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
-            }
         }
         mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
     }
 
+    if (stream == AudioSystem::VOICE_CALL ||
+        stream == AudioSystem::BLUETOOTH_SCO) {
+        float voiceVolume;
+        // Force voice volume to max for bluetooth SCO as volume is managed by the headset
+        if (stream == AudioSystem::VOICE_CALL) {
+            voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
+        } else {
+            voiceVolume = 1.0;
+        }
+        if (voiceVolume != mLastVoiceVolume && output == mHardwareOutput) {
+            mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
+            mLastVoiceVolume = voiceVolume;
+        }
+    }
+
     return NO_ERROR;
 }
 
diff --git a/services/camera/libcameraservice/CameraHardwareStub.cpp b/services/camera/libcameraservice/CameraHardwareStub.cpp
index b3e0ee6..07b5a37 100644
--- a/services/camera/libcameraservice/CameraHardwareStub.cpp
+++ b/services/camera/libcameraservice/CameraHardwareStub.cpp
@@ -101,9 +101,9 @@
     mFakeCamera = 0; // paranoia
 }
 
-sp<IMemoryHeap> CameraHardwareStub::getPreviewHeap() const
+status_t CameraHardwareStub::setPreviewWindow(const sp<ANativeWindow>& buf)
 {
-    return mPreviewHeap;
+    return NO_ERROR;
 }
 
 sp<IMemoryHeap> CameraHardwareStub::getRawHeap() const
diff --git a/services/camera/libcameraservice/CameraHardwareStub.h b/services/camera/libcameraservice/CameraHardwareStub.h
index d3427ba..9b66a76 100644
--- a/services/camera/libcameraservice/CameraHardwareStub.h
+++ b/services/camera/libcameraservice/CameraHardwareStub.h
@@ -29,7 +29,7 @@
 
 class CameraHardwareStub : public CameraHardwareInterface {
 public:
-    virtual sp<IMemoryHeap> getPreviewHeap() const;
+    virtual status_t setPreviewWindow(const sp<ANativeWindow>& buf);
     virtual sp<IMemoryHeap> getRawHeap() const;
 
     virtual void        setCallbacks(notify_callback notify_cb,
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index a64ddcf..535f07f 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -26,6 +26,7 @@
 #include <binder/MemoryBase.h>
 #include <binder/MemoryHeapBase.h>
 #include <cutils/atomic.h>
+#include <cutils/properties.h>
 #include <hardware/hardware.h>
 #include <media/AudioSystem.h>
 #include <media/mediaplayer.h>
@@ -307,7 +308,6 @@
     mClientPid = clientPid;
     mUseOverlay = mHardware->useOverlay();
     mMsgEnabled = 0;
-
     mHardware->setCallbacks(notifyCallback,
                             dataCallback,
                             dataCallbackTimestamp,
@@ -324,6 +324,7 @@
     mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
     mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT);
     mOrientationChanged = false;
+    mPlayShutterSound = true;
     cameraService->setCameraBusy(cameraId);
     cameraService->loadSound();
     LOG1("Client::Client X (pid %d)", callingPid);
@@ -341,18 +342,6 @@
     int callingPid = getCallingPid();
     LOG1("Client::~Client E (pid %d, this %p)", callingPid, this);
 
-    if (mSurface != 0 && !mUseOverlay) {
-        pthread_t thr;
-        // We unregister the buffers in a different thread because binder does
-        // not let us make sychronous transactions in a binder destructor (that
-        // is, upon our reaching a refcount of zero.)
-        pthread_create(&thr,
-                       NULL,  // attr
-                       unregister_surface,
-                       mSurface.get());
-        pthread_join(thr, NULL);
-    }
-
     // set mClientPid to let disconnet() tear down the hardware
     mClientPid = callingPid;
     disconnect();
@@ -470,6 +459,11 @@
     if (mUseOverlay) {
         mOverlayRef = 0;
     }
+    // Release the held ANativeWindow resources.
+    if (mPreviewWindow != 0) {
+        mPreviewWindow = 0;
+        mHardware->setPreviewWindow(mPreviewWindow);
+    }
     mHardware.clear();
 
     mCameraService->removeClient(mCameraClient);
@@ -480,8 +474,8 @@
 
 // ----------------------------------------------------------------------------
 
-// set the ISurface that the preview will use
-status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) {
+// set the Surface that the preview will use
+status_t CameraService::Client::setPreviewDisplay(const sp<Surface>& surface) {
     LOG1("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid());
     Mutex::Autolock lock(mLock);
     status_t result = checkPidAndHardware();
@@ -491,7 +485,7 @@
 
     // return if no change in surface.
     // asBinder() is safe on NULL (returns NULL)
-    if (surface->asBinder() == mSurface->asBinder()) {
+    if (getISurface(surface)->asBinder() == mSurface->asBinder()) {
         return result;
     }
 
@@ -502,44 +496,30 @@
             sp<Overlay> dummy;
             mHardware->setOverlay(dummy);
             mOverlayRef = 0;
-        } else {
-            mSurface->unregisterBuffers();
         }
     }
-    mSurface = surface;
+    if (surface != 0) {
+        mSurface = getISurface(surface);
+    } else {
+        mSurface = 0;
+    }
+    mPreviewWindow = surface;
     mOverlayRef = 0;
     // If preview has been already started, set overlay or register preview
     // buffers now.
     if (mHardware->previewEnabled()) {
         if (mUseOverlay) {
             result = setOverlay();
-        } else if (mSurface != 0) {
-            result = registerPreviewBuffers();
+        } else if (mPreviewWindow != 0) {
+            native_window_set_buffers_transform(mPreviewWindow.get(),
+                                                mOrientation);
+            result = mHardware->setPreviewWindow(mPreviewWindow);
         }
     }
 
     return result;
 }
 
-status_t CameraService::Client::registerPreviewBuffers() {
-    int w, h;
-    CameraParameters params(mHardware->getParameters());
-    params.getPreviewSize(&w, &h);
-
-    // FIXME: don't use a hardcoded format here.
-    ISurface::BufferHeap buffers(w, h, w, h,
-                                 HAL_PIXEL_FORMAT_YCrCb_420_SP,
-                                 mOrientation,
-                                 0,
-                                 mHardware->getPreviewHeap());
-
-    status_t result = mSurface->registerBuffers(buffers);
-    if (result != NO_ERROR) {
-        LOGE("registerBuffers failed with status %d", result);
-    }
-    return result;
-}
-
 status_t CameraService::Client::setOverlay() {
     int w, h;
     CameraParameters params(mHardware->getParameters());
@@ -597,16 +577,10 @@
     if (checkPidAndHardware() != NO_ERROR) return;
 
     mPreviewCallbackFlag = callback_flag;
-
-    // If we don't use overlay, we always need the preview frame for display.
-    // If we do use overlay, we only need the preview frame if the user
-    // wants the data.
-    if (mUseOverlay) {
-        if(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK) {
-            enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
-        } else {
-            disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
-        }
+    if (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK) {
+        enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+    } else {
+        disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
     }
 }
 
@@ -631,14 +605,14 @@
 
     switch(mode) {
         case CAMERA_PREVIEW_MODE:
-            if (mSurface == 0) {
+            if (mSurface == 0 && mPreviewWindow == 0) {
                 LOG1("mSurface is not set yet.");
                 // still able to start preview in this case.
             }
             return startPreviewMode();
         case CAMERA_RECORDING_MODE:
-            if (mSurface == 0) {
-                LOGE("mSurface must be set before startRecordingMode.");
+            if (mSurface == 0 && mPreviewWindow == 0) {
+                LOGE("mSurface or mPreviewWindow must be set before startRecordingMode.");
                 return INVALID_OPERATION;
             }
             return startRecordingMode();
@@ -664,16 +638,12 @@
         if (result != NO_ERROR) return result;
         result = mHardware->startPreview();
     } else {
-        enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
-        result = mHardware->startPreview();
-        if (result != NO_ERROR) return result;
-        // If preview display has been set, register preview buffers now.
-        if (mSurface != 0) {
-           // Unregister here because the surface may be previously registered
-           // with the raw (snapshot) heap.
-           mSurface->unregisterBuffers();
-           result = registerPreviewBuffers();
+        if (mPreviewWindow != 0) {
+            native_window_set_buffers_transform(mPreviewWindow.get(),
+                                                mOrientation);
         }
+        mHardware->setPreviewWindow(mPreviewWindow);
+        result = mHardware->startPreview();
     }
     return result;
 }
@@ -711,13 +681,10 @@
     Mutex::Autolock lock(mLock);
     if (checkPidAndHardware() != NO_ERROR) return;
 
+
     disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
     mHardware->stopPreview();
 
-    if (mSurface != 0 && !mUseOverlay) {
-        mSurface->unregisterBuffers();
-    }
-
     mPreviewBuffer.clear();
 }
 
@@ -741,6 +708,30 @@
     mHardware->releaseRecordingFrame(mem);
 }
 
+int32_t CameraService::Client::getNumberOfVideoBuffers() const {
+    LOG1("getNumberOfVideoBuffers");
+    Mutex::Autolock lock(mLock);
+    if (checkPidAndHardware() != NO_ERROR) return 0;
+    return mHardware->getNumberOfVideoBuffers();
+}
+
+sp<IMemory> CameraService::Client::getVideoBuffer(int32_t index) const {
+    LOG1("getVideoBuffer: %d", index);
+    Mutex::Autolock lock(mLock);
+    if (checkPidAndHardware() != NO_ERROR) return 0;
+    return mHardware->getVideoBuffer(index);
+}
+
+status_t CameraService::Client::storeMetaDataInBuffers(bool enabled)
+{
+    LOG1("storeMetaDataInBuffers: %s", enabled? "true": "false");
+    Mutex::Autolock lock(mLock);
+    if (checkPidAndHardware() != NO_ERROR) {
+        return UNKNOWN_ERROR;
+    }
+    return mHardware->storeMetaDataInBuffers(enabled);
+}
+
 bool CameraService::Client::previewEnabled() {
     LOG1("previewEnabled (pid %d)", getCallingPid());
 
@@ -815,6 +806,35 @@
     return params;
 }
 
+// enable shutter sound
+status_t CameraService::Client::enableShutterSound(bool enable) {
+    LOG1("enableShutterSound (pid %d)", getCallingPid());
+
+    status_t result = checkPidAndHardware();
+    if (result != NO_ERROR) return result;
+
+    if (enable) {
+        mPlayShutterSound = true;
+        return OK;
+    }
+
+    // Disabling shutter sound may not be allowed. In that case only
+    // allow the mediaserver process to disable the sound.
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.camera.sound.forced", value, "0");
+    if (strcmp(value, "0") != 0) {
+        // Disabling shutter sound is not allowed. Deny if the current
+        // process is not mediaserver.
+        if (getCallingPid() != getpid()) {
+            LOGE("Failed to disable shutter sound. Permission denied (pid %d)", getCallingPid());
+            return PERMISSION_DENIED;
+        }
+    }
+
+    mPlayShutterSound = false;
+    return OK;
+}
+
 status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) {
     LOG1("sendCommand (pid %d)", getCallingPid());
     int orientation;
@@ -836,6 +856,20 @@
             if (mOverlayRef != 0) mOrientationChanged = true;
         }
         return OK;
+    } else if (cmd == CAMERA_CMD_ENABLE_SHUTTER_SOUND) {
+        switch (arg1) {
+            case 0:
+                enableShutterSound(false);
+                break;
+            case 1:
+                enableShutterSound(true);
+                break;
+            default:
+                return BAD_VALUE;
+        }
+        return OK;
+    } else if (cmd == CAMERA_CMD_PLAY_RECORDING_SOUND) {
+        mCameraService->playSound(SOUND_RECORDING);
     }
 
     return mHardware->sendCommand(cmd, arg1, arg2);
@@ -996,11 +1030,8 @@
 // "size" is the width and height of yuv picture for registerBuffer.
 // If it is NULL, use the picture size from parameters.
 void CameraService::Client::handleShutter(image_rect_type *size) {
-    mCameraService->playSound(SOUND_SHUTTER);
-
-    // Screen goes black after the buffer is unregistered.
-    if (mSurface != 0 && !mUseOverlay) {
-        mSurface->unregisterBuffers();
+    if (mPlayShutterSound) {
+        mCameraService->playSound(SOUND_SHUTTER);
     }
 
     sp<ICameraClient> c = mCameraClient;
@@ -1030,7 +1061,6 @@
             HAL_PIXEL_FORMAT_YCrCb_420_SP, mOrientation, 0,
             mHardware->getRawHeap());
 
-        mSurface->registerBuffers(buffers);
         IPCThreadState::self()->flushCommands();
     }
 
@@ -1043,12 +1073,6 @@
     size_t size;
     sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
 
-    if (!mUseOverlay) {
-        if (mSurface != 0) {
-            mSurface->postBuffer(offset);
-        }
-    }
-
     // local copy of the callback flags
     int flags = mPreviewCallbackFlag;
 
@@ -1069,9 +1093,7 @@
         mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
                                   FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
                                   FRAME_CALLBACK_FLAG_ENABLE_MASK);
-        if (mUseOverlay) {
-            disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
-        }
+        disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
     }
 
     if (c != 0) {
@@ -1108,11 +1130,6 @@
     size_t size;
     sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
 
-    // Put the YUV version of the snapshot in the preview display.
-    if (mSurface != 0 && !mUseOverlay) {
-        mSurface->postBuffer(offset);
-    }
-
     sp<ICameraClient> c = mCameraClient;
     mLock.unlock();
     if (c != 0) {
@@ -1292,4 +1309,12 @@
     return NO_ERROR;
 }
 
+sp<ISurface> CameraService::getISurface(const sp<Surface>& surface) {
+    if (surface != 0) {
+        return surface->getISurface();
+    } else {
+        return sp<ISurface>(0);
+    }
+}
+
 }; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index f09773d..60e0d04 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -79,6 +79,12 @@
     sp<MediaPlayer>     mSoundPlayer[NUM_SOUNDS];
     int                 mSoundRef;  // reference count (release all MediaPlayer when 0)
 
+    // Used by Client objects to extract the ISurface from a Surface object.
+    // This is used because making Client a friend class of Surface would
+    // require including this header in Surface.h since Client is a nested
+    // class.
+    static sp<ISurface> getISurface(const sp<Surface>& surface);
+
     class Client : public BnCamera
     {
     public:
@@ -87,11 +93,14 @@
         virtual status_t        connect(const sp<ICameraClient>& client);
         virtual status_t        lock();
         virtual status_t        unlock();
-        virtual status_t        setPreviewDisplay(const sp<ISurface>& surface);
+        virtual status_t        setPreviewDisplay(const sp<Surface>& surface);
         virtual void            setPreviewCallbackFlag(int flag);
         virtual status_t        startPreview();
         virtual void            stopPreview();
         virtual bool            previewEnabled();
+        virtual int32_t         getNumberOfVideoBuffers() const;
+        virtual sp<IMemory>     getVideoBuffer(int32_t index) const;
+        virtual status_t        storeMetaDataInBuffers(bool enabled);
         virtual status_t        startRecording();
         virtual void            stopRecording();
         virtual bool            recordingEnabled();
@@ -133,6 +142,9 @@
         status_t                startPreviewMode();
         status_t                startRecordingMode();
 
+        // internal function used by sendCommand to enable/disable shutter sound.
+        status_t                enableShutterSound(bool enable);
+
         // these are static callback functions
         static void             notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user);
         static void             dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user);
@@ -171,10 +183,12 @@
         int                             mOrientation;     // Current display orientation
         // True if display orientation has been changed. This is only used in overlay.
         int                             mOrientationChanged;
+        bool                            mPlayShutterSound;
 
         // Ensures atomicity among the public methods
         mutable Mutex                   mLock;
         sp<ISurface>                    mSurface;
+        sp<ANativeWindow>               mPreviewWindow;
 
         // If the user want us to return a copy of the preview frame (instead
         // of the original one), we allocate mPreviewBuffer and reuse it if possible.
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java
index 87de79a..83ce3e3 100644
--- a/services/java/com/android/server/AccessibilityManagerService.java
+++ b/services/java/com/android/server/AccessibilityManagerService.java
@@ -269,14 +269,10 @@
             });
     }
 
-    public void addClient(IAccessibilityManagerClient client) {
+    public boolean addClient(IAccessibilityManagerClient client) {
         synchronized (mLock) {
-            try {
-                client.setEnabled(mIsEnabled);
-                mClients.add(client);
-            } catch (RemoteException re) {
-                Slog.w(LOG_TAG, "Dead AccessibilityManagerClient: " + client, re);
-            }
+            mClients.add(client);
+            return mIsEnabled;
         }
     }
 
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 731fb22..5ef3d35 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -426,6 +426,40 @@
         }
     }
 
+    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+        if (appWidgetIds == null) {
+            return;
+        }
+        if (appWidgetIds.length == 0) {
+            return;
+        }
+        final int N = appWidgetIds.length;
+
+        synchronized (mAppWidgetIds) {
+            for (int i=0; i<N; i++) {
+                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+                updateAppWidgetInstanceLocked(id, views, true);
+            }
+        }
+    }
+
+    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+        if (appWidgetIds == null) {
+            return;
+        }
+        if (appWidgetIds.length == 0) {
+            return;
+        }
+        final int N = appWidgetIds.length;
+
+        synchronized (mAppWidgetIds) {
+            for (int i=0; i<N; i++) {
+                AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+                notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
+            }
+        }
+    }
+
     public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
         synchronized (mAppWidgetIds) {
             Provider p = lookupProviderLocked(provider);
@@ -443,11 +477,17 @@
     }
 
     void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
+        updateAppWidgetInstanceLocked(id, views, false);
+    }
+
+    void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
         // allow for stale appWidgetIds and other badness
         // lookup also checks that the calling process can access the appWidgetId
         // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
         if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
-            id.views = views;
+
+            // We do not want to save this RemoteViews
+            if (!isPartialUpdate) id.views = views;
 
             // is anyone listening?
             if (id.host.callbacks != null) {
@@ -463,6 +503,25 @@
         }
     }
 
+    void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
+        // allow for stale appWidgetIds and other badness
+        // lookup also checks that the calling process can access the appWidgetId
+        // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+        if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+            // is anyone listening?
+            if (id.host.callbacks != null) {
+                try {
+                    // the lock is held, but this is a oneway call
+                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
+                } catch (RemoteException e) {
+                    // It failed; remove the callback. No need to prune because
+                    // we know that this host is still referenced by this instance.
+                    id.host.callbacks = null;
+                }
+            }
+        }
+    }
+
     public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
             List<RemoteViews> updatedViews) {
         int callingUid = enforceCallingUid(packageName);
@@ -742,6 +801,9 @@
             }
             info.label = activityInfo.loadLabel(mPackageManager).toString();
             info.icon = ri.getIconResource();
+            info.previewImage = sa.getResourceId(
+            		com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
+
             sa.recycle();
         } catch (Exception e) {
             // Ok to catch Exception here, because anything going wrong because
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index a7e02a5..90ebf3f 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -84,7 +84,7 @@
 
 class BackupManagerService extends IBackupManager.Stub {
     private static final String TAG = "BackupManagerService";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     // How often we perform a backup pass.  Privileged external callers can
     // trigger an immediate pass.
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index fc4e06f..40883bd 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -43,6 +43,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.Arrays;
 
 
 /**
@@ -76,7 +77,7 @@
 
     // Used locally for determining when to make a last ditch effort to log
     // discharge stats before the device dies.
-    private static final int CRITICAL_BATTERY_LEVEL = 4;
+    private int mCriticalBatteryLevel;
 
     private static final int DUMP_MAX_LENGTH = 24 * 1024;
     private static final String[] DUMPSYS_ARGS = new String[] { "--checkin", "-u" };
@@ -100,6 +101,7 @@
     private int mBatteryTemperature;
     private String mBatteryTechnology;
     private boolean mBatteryLevelCritical;
+    private int mInvalidCharger;
 
     private int mLastBatteryStatus;
     private int mLastBatteryHealth;
@@ -108,6 +110,7 @@
     private int mLastBatteryVoltage;
     private int mLastBatteryTemperature;
     private boolean mLastBatteryLevelCritical;
+    private int mLastInvalidCharger;
 
     private int mLowBatteryWarningLevel;
     private int mLowBatteryCloseWarningLevel;
@@ -118,18 +121,28 @@
     private long mDischargeStartTime;
     private int mDischargeStartLevel;
 
+    private Led mLed;
+
     private boolean mSentLowBatteryBroadcast = false;
 
-    public BatteryService(Context context) {
+    public BatteryService(Context context, LightsService lights) {
         mContext = context;
+        mLed = new Led(context, lights);
         mBatteryStats = BatteryStatsService.getService();
 
+        mCriticalBatteryLevel = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
         mLowBatteryWarningLevel = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
         mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
 
-        mUEventObserver.startObserving("SUBSYSTEM=power_supply");
+        mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply");
+
+        // watch for invalid charger messages if the invalid_charger switch exists
+        if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
+            mInvalidChargerObserver.startObserving("DEVPATH=/devices/virtual/switch/invalid_charger");
+        }
 
         // set initial status
         update();
@@ -163,13 +176,24 @@
         return mPlugType;
     }
 
-    private UEventObserver mUEventObserver = new UEventObserver() {
+    private UEventObserver mPowerSupplyObserver = new UEventObserver() {
         @Override
         public void onUEvent(UEventObserver.UEvent event) {
             update();
         }
     };
 
+    private UEventObserver mInvalidChargerObserver = new UEventObserver() {
+        @Override
+        public void onUEvent(UEventObserver.UEvent event) {
+            int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
+            if (mInvalidCharger != invalidCharger) {
+                mInvalidCharger = invalidCharger;
+                update();
+            }
+        }
+    };
+
     // returns battery level as a percentage
     final int getBatteryLevel() {
         return mBatteryLevel;
@@ -207,11 +231,14 @@
 
     private synchronized final void update() {
         native_update();
+        processValues();
+    }
 
+    private void processValues() {
         boolean logOutlier = false;
         long dischargeDuration = 0;
 
-        mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
+        mBatteryLevelCritical = mBatteryLevel <= mCriticalBatteryLevel;
         if (mAcOnline) {
             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
         } else if (mUsbOnline) {
@@ -238,7 +265,8 @@
                 mBatteryLevel != mLastBatteryLevel ||
                 mPlugType != mLastPlugType ||
                 mBatteryVoltage != mLastBatteryVoltage ||
-                mBatteryTemperature != mLastBatteryTemperature) {
+                mBatteryTemperature != mLastBatteryTemperature ||
+                mInvalidCharger != mLastInvalidCharger) {
 
             if (mPlugType != mLastPlugType) {
                 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
@@ -292,9 +320,9 @@
              *   (becomes <= mLowBatteryWarningLevel).
              */
             final boolean sendBatteryLow = !plugged
-                && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
-                && mBatteryLevel <= mLowBatteryWarningLevel
-                && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
+                    && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+                    && mBatteryLevel <= mLowBatteryWarningLevel
+                    && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
 
             sendIntent();
 
@@ -322,6 +350,9 @@
                 mContext.sendBroadcast(statusIntent);
             }
 
+            // Update the battery LED
+            mLed.updateLightsLocked();
+
             // This needs to be done after sendIntent() so that we get the lastest battery stats.
             if (logOutlier && dischargeDuration != 0) {
                 logOutlier(dischargeDuration);
@@ -335,6 +366,7 @@
             mLastBatteryVoltage = mBatteryVoltage;
             mLastBatteryTemperature = mBatteryTemperature;
             mLastBatteryLevelCritical = mBatteryLevelCritical;
+            mLastInvalidCharger = mInvalidCharger;
         }
     }
 
@@ -356,16 +388,17 @@
         intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryVoltage);
         intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryTemperature);
         intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryTechnology);
+        intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
 
-        if (false) {
-            Slog.d(TAG, "updateBattery level:" + mBatteryLevel +
+        if (true) {
+            Slog.d(TAG, "level:" + mBatteryLevel +
                     " scale:" + BATTERY_SCALE + " status:" + mBatteryStatus +
                     " health:" + mBatteryHealth +  " present:" + mBatteryPresent +
                     " voltage: " + mBatteryVoltage +
                     " temperature: " + mBatteryTemperature +
                     " technology: " + mBatteryTechnology +
                     " AC powered:" + mAcOnline + " USB powered:" + mUsbOnline +
-                    " icon:" + icon );
+                    " icon:" + icon  + " invalid charger:" + mInvalidCharger);
         }
 
         ActivityManagerNative.broadcastStickyIntent(intent, null);
@@ -460,18 +493,109 @@
             return;
         }
 
-        synchronized (this) {
-            pw.println("Current Battery Service state:");
-            pw.println("  AC powered: " + mAcOnline);
-            pw.println("  USB powered: " + mUsbOnline);
-            pw.println("  status: " + mBatteryStatus);
-            pw.println("  health: " + mBatteryHealth);
-            pw.println("  present: " + mBatteryPresent);
-            pw.println("  level: " + mBatteryLevel);
-            pw.println("  scale: " + BATTERY_SCALE);
-            pw.println("  voltage:" + mBatteryVoltage);
-            pw.println("  temperature: " + mBatteryTemperature);
-            pw.println("  technology: " + mBatteryTechnology);
+        if (args == null || args.length == 0) {
+            synchronized (this) {
+                pw.println("Current Battery Service state:");
+                pw.println("  AC powered: " + mAcOnline);
+                pw.println("  USB powered: " + mUsbOnline);
+                pw.println("  status: " + mBatteryStatus);
+                pw.println("  health: " + mBatteryHealth);
+                pw.println("  present: " + mBatteryPresent);
+                pw.println("  level: " + mBatteryLevel);
+                pw.println("  scale: " + BATTERY_SCALE);
+                pw.println("  voltage:" + mBatteryVoltage);
+                pw.println("  temperature: " + mBatteryTemperature);
+                pw.println("  technology: " + mBatteryTechnology);
+            }
+        } else if (false) {
+            // DO NOT SUBMIT WITH THIS TURNED ON
+            if (args.length == 3 && "set".equals(args[0])) {
+                String key = args[1];
+                String value = args[2];
+                try {
+                    boolean update = true;
+                    if ("ac".equals(key)) {
+                        mAcOnline = Integer.parseInt(value) != 0;
+                    } else if ("usb".equals(key)) {
+                        mUsbOnline = Integer.parseInt(value) != 0;
+                    } else if ("status".equals(key)) {
+                        mBatteryStatus = Integer.parseInt(value);
+                    } else if ("level".equals(key)) {
+                        mBatteryLevel = Integer.parseInt(value);
+                    } else if ("invalid".equals(key)) {
+                        mInvalidCharger = Integer.parseInt(value);
+                    } else {
+                        update = false;
+                    }
+                    if (update) {
+                        processValues();
+                    }
+                } catch (NumberFormatException ex) {
+                    pw.println("Bad value: " + value);
+                }
+            }
+        }
+    }
+
+    class Led {
+        private LightsService mLightsService;
+        private LightsService.Light mBatteryLight;
+
+        private int mBatteryLowARGB;
+        private int mBatteryMediumARGB;
+        private int mBatteryFullARGB;
+        private int mBatteryLedOn;
+        private int mBatteryLedOff;
+
+        private boolean mBatteryCharging;
+        private boolean mBatteryLow;
+        private boolean mBatteryFull;
+
+        Led(Context context, LightsService lights) {
+            mLightsService = lights;
+            mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
+
+            mBatteryLowARGB = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_notificationsBatteryLowARGB);
+            mBatteryMediumARGB = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
+            mBatteryFullARGB = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_notificationsBatteryFullARGB);
+            mBatteryLedOn = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_notificationsBatteryLedOn);
+            mBatteryLedOff = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_notificationsBatteryLedOff);
+        }
+
+        /**
+         * Synchronize on BatteryService.
+         */
+        void updateLightsLocked() {
+            final int level = mBatteryLevel;
+            final int status = mBatteryStatus;
+            if (level < mLowBatteryWarningLevel) {
+                if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
+                    // Solid red when battery is charging
+                    mBatteryLight.setColor(mBatteryLowARGB);
+                } else {
+                    // Flash red when battery is low and not charging
+                    mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
+                            mBatteryLedOn, mBatteryLedOff);
+                }
+            } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
+                    || status == BatteryManager.BATTERY_STATUS_FULL) {
+                if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
+                    // Solid green when full or charging and nearly full
+                    mBatteryLight.setColor(mBatteryFullARGB);
+                } else {
+                    // Solid orange when charging and halfway full
+                    mBatteryLight.setColor(mBatteryMediumARGB);
+                }
+            } else {
+                // No lights if not charging and not low
+                mBatteryLight.turnOff();
+            }
         }
     }
 }
+
diff --git a/services/java/com/android/server/ClipboardService.java b/services/java/com/android/server/ClipboardService.java
index aa8cded..30ea48c 100644
--- a/services/java/com/android/server/ClipboardService.java
+++ b/services/java/com/android/server/ClipboardService.java
@@ -16,42 +16,240 @@
 
 package com.android.server;
 
-import android.text.IClipboard;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.IClipboard;
+import android.content.IOnPrimaryClipChangedListener;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Pair;
+import android.util.Slog;
+
+import java.util.HashSet;
 
 /**
  * Implementation of the clipboard for copy and paste.
  */
 public class ClipboardService extends IClipboard.Stub {
-    private CharSequence mClipboard = "";
+    private final Context mContext;
+    private final IActivityManager mAm;
+    private final PackageManager mPm;
+    private final IBinder mPermissionOwner;
+
+    private final RemoteCallbackList<IOnPrimaryClipChangedListener> mPrimaryClipListeners
+            = new RemoteCallbackList<IOnPrimaryClipChangedListener>();
+
+    private ClipData mPrimaryClip;
+
+    private final HashSet<String> mActivePermissionOwners
+            = new HashSet<String>();
 
     /**
      * Instantiates the clipboard.
      */
-    public ClipboardService(Context context) { }
+    public ClipboardService(Context context) {
+        mContext = context;
+        mAm = ActivityManagerNative.getDefault();
+        mPm = context.getPackageManager();
+        IBinder permOwner = null;
+        try {
+            permOwner = mAm.newUriPermissionOwner("clipboard");
+        } catch (RemoteException e) {
+            Slog.w("clipboard", "AM dead", e);
+        }
+        mPermissionOwner = permOwner;
+    }
 
-    // javadoc from interface
-    public void setClipboardText(CharSequence text) {
+    @Override
+    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+            throws RemoteException {
+        try {
+            return super.onTransact(code, data, reply, flags);
+        } catch (RuntimeException e) {
+            Slog.w("clipboard", "Exception: ", e);
+            throw e;
+        }
+        
+    }
+
+    public void setPrimaryClip(ClipData clip) {
         synchronized (this) {
-            if (text == null) {
-                text = "";
+            if (clip != null && clip.getItemCount() <= 0) {
+                throw new IllegalArgumentException("No items");
             }
+            checkDataOwnerLocked(clip, Binder.getCallingUid());
+            clearActiveOwnersLocked();
+            mPrimaryClip = clip;
+            final int n = mPrimaryClipListeners.beginBroadcast();
+            for (int i = 0; i < n; i++) {
+                try {
+                    mPrimaryClipListeners.getBroadcastItem(i).dispatchPrimaryClipChanged();
+                } catch (RemoteException e) {
+
+                    // The RemoteCallbackList will take care of removing
+                    // the dead object for us.
+                }
+            }
+            mPrimaryClipListeners.finishBroadcast();
+        }
+    }
     
-            mClipboard = text;
-        }
-    }
-
-    // javadoc from interface
-    public CharSequence getClipboardText() {
+    public ClipData getPrimaryClip(String pkg) {
         synchronized (this) {
-            return mClipboard;
+            addActiveOwnerLocked(Binder.getCallingUid(), pkg);
+            return mPrimaryClip;
         }
     }
 
-    // javadoc from interface
+    public ClipDescription getPrimaryClipDescription() {
+        synchronized (this) {
+            return mPrimaryClip.getDescription();
+        }
+    }
+
+    public boolean hasPrimaryClip() {
+        synchronized (this) {
+            return mPrimaryClip != null;
+        }
+    }
+
+    public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
+        synchronized (this) {
+            mPrimaryClipListeners.register(listener);
+        }
+    }
+
+    public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
+        synchronized (this) {
+            mPrimaryClipListeners.unregister(listener);
+        }
+    }
+
     public boolean hasClipboardText() {
         synchronized (this) {
-            return mClipboard.length() > 0;
+            if (mPrimaryClip != null) {
+                CharSequence text = mPrimaryClip.getItem(0).getText();
+                return text != null && text.length() > 0;
+            }
+            return false;
+        }
+    }
+
+    private final void checkUriOwnerLocked(Uri uri, int uid) {
+        if (!"content".equals(uri.getScheme())) {
+            return;
+        }
+        long ident = Binder.clearCallingIdentity();
+        boolean allowed = false;
+        try {
+            // This will throw SecurityException for us.
+            mAm.checkGrantUriPermission(uid, null, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        } catch (RemoteException e) {
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private final void checkItemOwnerLocked(ClipData.Item item, int uid) {
+        if (item.getUri() != null) {
+            checkUriOwnerLocked(item.getUri(), uid);
+        }
+        Intent intent = item.getIntent();
+        if (intent != null && intent.getData() != null) {
+            checkUriOwnerLocked(intent.getData(), uid);
+        }
+    }
+
+    private final void checkDataOwnerLocked(ClipData data, int uid) {
+        final int N = data.getItemCount();
+        for (int i=0; i<N; i++) {
+            checkItemOwnerLocked(data.getItem(i), uid);
+        }
+    }
+
+    private final void grantUriLocked(Uri uri, String pkg) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            mAm.grantUriPermissionFromOwner(mPermissionOwner, Process.myUid(), pkg, uri,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        } catch (RemoteException e) {
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private final void grantItemLocked(ClipData.Item item, String pkg) {
+        if (item.getUri() != null) {
+            grantUriLocked(item.getUri(), pkg);
+        }
+        Intent intent = item.getIntent();
+        if (intent != null && intent.getData() != null) {
+            grantUriLocked(intent.getData(), pkg);
+        }
+    }
+
+    private final void addActiveOwnerLocked(int uid, String pkg) {
+        PackageInfo pi;
+        try {
+            pi = mPm.getPackageInfo(pkg, 0);
+            if (pi.applicationInfo.uid != uid) {
+                throw new SecurityException("Calling uid " + uid
+                        + " does not own package " + pkg);
+            }
+        } catch (NameNotFoundException e) {
+            throw new IllegalArgumentException("Unknown package " + pkg, e);
+        }
+        if (!mActivePermissionOwners.contains(pkg)) {
+            final int N = mPrimaryClip.getItemCount();
+            for (int i=0; i<N; i++) {
+                grantItemLocked(mPrimaryClip.getItem(i), pkg);
+            }
+            mActivePermissionOwners.add(pkg);
+        }
+    }
+
+    private final void revokeUriLocked(Uri uri) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            mAm.revokeUriPermissionFromOwner(mPermissionOwner, uri,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION
+                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        } catch (RemoteException e) {
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private final void revokeItemLocked(ClipData.Item item) {
+        if (item.getUri() != null) {
+            revokeUriLocked(item.getUri());
+        }
+        Intent intent = item.getIntent();
+        if (intent != null && intent.getData() != null) {
+            revokeUriLocked(intent.getData());
+        }
+    }
+
+    private final void clearActiveOwnersLocked() {
+        mActivePermissionOwners.clear();
+        if (mPrimaryClip == null) {
+            return;
+        }
+        final int N = mPrimaryClip.getItemCount();
+        for (int i=0; i<N; i++) {
+            revokeItemLocked(mPrimaryClip.getItem(i));
         }
     }
 }
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 7c9b547..c18262e 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -26,14 +26,18 @@
 import android.net.IConnectivityManager;
 import android.net.MobileDataStateTracker;
 import android.net.NetworkInfo;
+import android.net.LinkProperties;
 import android.net.NetworkStateTracker;
 import android.net.NetworkUtils;
 import android.net.wifi.WifiStateTracker;
+import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -47,8 +51,13 @@
 import com.android.server.connectivity.Tethering;
 
 import java.io.FileDescriptor;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.GregorianCalendar;
 import java.util.List;
 import java.net.InetAddress;
@@ -59,7 +68,7 @@
  */
 public class ConnectivityService extends IConnectivityManager.Stub {
 
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
     private static final String TAG = "ConnectivityService";
 
     // how long to wait before switching back to a radio's default network
@@ -68,7 +77,6 @@
     private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
             "android.telephony.apn-restore";
 
-
     private Tethering mTethering;
     private boolean mTetheringConfigValid = false;
 
@@ -85,6 +93,8 @@
      */
     private List mNetRequestersPids[];
 
+    private WifiWatchdogService mWifiWatchdogService;
+
     // priority order of the nettrackers
     // (excluding dynamically set mNetworkPreference)
     // TODO - move mNetworkTypePreference into this
@@ -162,6 +172,13 @@
     private static final int EVENT_SET_MOBILE_DATA =
             MAX_NETWORK_STATE_TRACKER_EVENT + 7;
 
+    /**
+     * used internally to clear a wakelock when transitioning
+     * from one net to another
+     */
+    private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK =
+            MAX_NETWORK_STATE_TRACKER_EVENT + 8;
+
     private Handler mHandler;
 
     // list of DeathRecipients used to make sure features are turned off when
@@ -171,6 +188,13 @@
     private boolean mSystemReady;
     private Intent mInitialBroadcast;
 
+    private PowerManager.WakeLock mNetTransitionWakeLock;
+    private String mNetTransitionWakeLockCausedBy = "";
+    private int mNetTransitionWakeLockSerialNumber;
+    private int mNetTransitionWakeLockTimeout;
+
+    private InetAddress mDefaultDns;
+
     // used in DBG mode to track inet condition reports
     private static final int INET_CONDITION_LOG_MAX_SIZE = 15;
     private ArrayList mInetLog;
@@ -210,52 +234,20 @@
     }
     RadioAttributes[] mRadioAttributes;
 
-    private static class ConnectivityThread extends Thread {
-        private Context mContext;
-
-        private ConnectivityThread(Context context) {
-            super("ConnectivityThread");
-            mContext = context;
+    public static synchronized ConnectivityService getInstance(Context context) {
+        if (sServiceInstance == null) {
+            sServiceInstance = new ConnectivityService(context);
         }
-
-        @Override
-        public void run() {
-            Looper.prepare();
-            synchronized (this) {
-                sServiceInstance = new ConnectivityService(mContext);
-                notifyAll();
-            }
-            Looper.loop();
-        }
-
-        public static ConnectivityService getServiceInstance(Context context) {
-            ConnectivityThread thread = new ConnectivityThread(context);
-            thread.start();
-
-            synchronized (thread) {
-                while (sServiceInstance == null) {
-                    try {
-                        // Wait until sServiceInstance has been initialized.
-                        thread.wait();
-                    } catch (InterruptedException ignore) {
-                        Slog.e(TAG,
-                            "Unexpected InterruptedException while waiting"+
-                            " for ConnectivityService thread");
-                    }
-                }
-            }
-
-            return sServiceInstance;
-        }
-    }
-
-    public static ConnectivityService getInstance(Context context) {
-        return ConnectivityThread.getServiceInstance(context);
+        return sServiceInstance;
     }
 
     private ConnectivityService(Context context) {
         if (DBG) Slog.v(TAG, "ConnectivityService starting up");
 
+        HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
+        handlerThread.start();
+        mHandler = new MyHandler(handlerThread.getLooper());
+
         // setup our unique device name
         String id = Settings.Secure.getString(context.getContentResolver(),
                 Settings.Secure.ANDROID_ID);
@@ -264,10 +256,28 @@
             SystemProperties.set("net.hostname", name);
         }
 
+        // read our default dns server ip
+        String dns = Settings.Secure.getString(context.getContentResolver(),
+                Settings.Secure.DEFAULT_DNS_SERVER);
+        if (dns == null || dns.length() == 0) {
+            dns = context.getResources().getString(
+                    com.android.internal.R.string.config_default_dns_server);
+        }
+        try {
+            mDefaultDns = InetAddress.getByName(dns);
+        } catch (UnknownHostException e) {
+            Slog.e(TAG, "Error setting defaultDns using " + dns);
+        }
+
         mContext = context;
+
+        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+        mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_networkTransitionTimeout);
+
         mNetTrackers = new NetworkStateTracker[
                 ConnectivityManager.MAX_NETWORK_TYPE+1];
-        mHandler = new MyHandler();
 
         mNetworkPreference = getPersistedNetworkPreference();
 
@@ -364,18 +374,21 @@
             switch (mNetAttributes[netType].mRadio) {
             case ConnectivityManager.TYPE_WIFI:
                 if (DBG) Slog.v(TAG, "Starting Wifi Service.");
-                WifiStateTracker wst = new WifiStateTracker(context, mHandler);
-                WifiService wifiService = new WifiService(context, wst);
+                WifiStateTracker wst = new WifiStateTracker();
+                WifiService wifiService = new WifiService(context);
                 ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
-                wifiService.startWifi();
+                wifiService.checkAndStartWifi();
                 mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
-                wst.startMonitoring();
+                wst.startMonitoring(context, mHandler);
+
+                //TODO: as part of WWS refactor, create only when needed
+                mWifiWatchdogService = new WifiWatchdogService(context);
 
                 break;
             case ConnectivityManager.TYPE_MOBILE:
-                mNetTrackers[netType] = new MobileDataStateTracker(context, mHandler,
-                    netType, mNetAttributes[netType].mName);
-                mNetTrackers[netType].startMonitoring();
+                mNetTrackers[netType] = new MobileDataStateTracker(netType,
+                        mNetAttributes[netType].mName);
+                mNetTrackers[netType].startMonitoring(context, mHandler);
                 if (noMobileData) {
                     if (DBG) Slog.d(TAG, "tearing down Mobile networks due to setting");
                     mNetTrackers[netType].teardown();
@@ -392,7 +405,8 @@
         mTetheringConfigValid = (((mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] != null) ||
                                   !mTethering.isDunRequired()) &&
                                  (mTethering.getTetherableUsbRegexs().length != 0 ||
-                                  mTethering.getTetherableWifiRegexs().length != 0) &&
+                                  mTethering.getTetherableWifiRegexs().length != 0 ||
+                                  mTethering.getTetherableBluetoothRegexs().length != 0) &&
                                  mTethering.getUpstreamIfaceRegexs().length != 0);
 
         if (DBG) {
@@ -528,6 +542,38 @@
         return result;
     }
 
+    /**
+     * Return LinkProperties for the active (i.e., connected) default
+     * network interface.  It is assumed that at most one default network
+     * is active at a time. If more than one is active, it is indeterminate
+     * which will be returned.
+     * @return the ip properties for the active network, or {@code null} if
+     * none is active
+     */
+    public LinkProperties getActiveLinkProperties() {
+        enforceAccessPermission();
+        for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
+            if (mNetAttributes[type] == null || !mNetAttributes[type].isDefault()) {
+                continue;
+            }
+            NetworkStateTracker t = mNetTrackers[type];
+            NetworkInfo info = t.getNetworkInfo();
+            if (info.isConnected()) {
+                return t.getLinkProperties();
+            }
+        }
+        return null;
+    }
+
+    public LinkProperties getLinkProperties(int networkType) {
+        enforceAccessPermission();
+        if (ConnectivityManager.isNetworkTypeValid(networkType)) {
+            NetworkStateTracker t = mNetTrackers[networkType];
+            if (t != null) return t.getLinkProperties();
+        }
+        return null;
+    }
+
     public boolean setRadios(boolean turnOn) {
         boolean result = true;
         enforceChangePermission();
@@ -676,14 +722,7 @@
                 network.reconnect();
                 return Phone.APN_REQUEST_STARTED;
             } else {
-                synchronized(this) {
-                    mFeatureUsers.add(f);
-                }
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK,
-                        f), getRestoreDefaultNetworkDelay());
-
-                return network.startUsingNetworkFeature(feature,
-                        getCallingPid(), getCallingUid());
+                return -1;
             }
         }
         return Phone.APN_TYPE_NOT_AVAILABLE;
@@ -802,8 +841,7 @@
             tracker.teardown();
             return 1;
         } else {
-            // do it the old fashioned way
-            return tracker.stopUsingNetworkFeature(feature, pid, uid);
+            return -1;
         }
     }
 
@@ -852,11 +890,37 @@
             }
             return false;
         }
-
         try {
-            InetAddress inetAddress = InetAddress.getByAddress(hostAddress);
-            return tracker.requestRouteToHost(inetAddress);
-        } catch (UnknownHostException e) {
+            InetAddress addr = InetAddress.getByAddress(hostAddress);
+            return addHostRoute(tracker, addr);
+        } catch (UnknownHostException e) {}
+        return false;
+    }
+
+    /**
+     * Ensure that a network route exists to deliver traffic to the specified
+     * host via the mobile data network.
+     * @param hostAddress the IP address of the host to which the route is desired,
+     * in network byte order.
+     * TODO - deprecate
+     * @return {@code true} on success, {@code false} on failure
+     */
+    private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress) {
+        if (nt.getNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI) {
+            return false;
+        }
+
+        LinkProperties p = nt.getLinkProperties();
+        if (p == null) return false;
+        String interfaceName = p.getInterfaceName();
+
+        if (DBG) {
+            Slog.d(TAG, "Requested host route to " + hostAddress + "(" + interfaceName + ")");
+        }
+        if (interfaceName != null) {
+            return NetworkUtils.addHostRoute(interfaceName, hostAddress, null);
+        } else {
+            if (DBG) Slog.e(TAG, "addHostRoute failed due to null interface name");
             return false;
         }
     }
@@ -975,6 +1039,12 @@
                 "ConnectivityService");
     }
 
+    private void enforceConnectivityInternalPermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CONNECTIVITY_INTERNAL,
+                "ConnectivityService");
+    }
+
     /**
      * Handle a {@code DISCONNECTED} event. If this pertains to the non-active
      * network, we ignore it. If it is for the active network, we send out a
@@ -1266,9 +1336,17 @@
                         Slog.e(TAG, "Network declined teardown request");
                         return;
                     }
-                    if (isFailover) {
-                        otherNet.releaseWakeLock();
-                    }
+                }
+            }
+            synchronized (ConnectivityService.this) {
+                // have a new default network, release the transition wakelock in a second
+                // if it's held.  The second pause is to allow apps to reconnect over the
+                // new network
+                if (mNetTransitionWakeLock.isHeld()) {
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                            EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
+                            mNetTransitionWakeLockSerialNumber, 0),
+                            1000);
                 }
             }
             mActiveDefaultNetwork = type;
@@ -1282,36 +1360,14 @@
             //reportNetworkCondition(mActiveDefaultNetwork, 100);
         }
         thisNet.setTeardownRequested(false);
-        thisNet.updateNetworkSettings();
+        updateNetworkSettings(thisNet);
         handleConnectivityChange(type);
         sendConnectedBroadcast(info);
     }
 
-    private void handleScanResultsAvailable(NetworkInfo info) {
-        int networkType = info.getType();
-        if (networkType != ConnectivityManager.TYPE_WIFI) {
-            if (DBG) Slog.v(TAG, "Got ScanResultsAvailable for " +
-                    info.getTypeName() + " network. Don't know how to handle.");
-        }
-
-        mNetTrackers[networkType].interpretScanResultsAvailable();
-    }
-
-    private void handleNotificationChange(boolean visible, int id,
-            Notification notification) {
-        NotificationManager notificationManager = (NotificationManager) mContext
-                .getSystemService(Context.NOTIFICATION_SERVICE);
-
-        if (visible) {
-            notificationManager.notify(id, notification);
-        } else {
-            notificationManager.cancel(id);
-        }
-    }
-
     /**
-     * After a change in the connectivity state of any network, We're mainly
-     * concerned with making sure that the list of DNS servers is setupup
+     * After a change in the connectivity state of a network. We're mainly
+     * concerned with making sure that the list of DNS servers is set up
      * according to which networks are connected, and ensuring that the
      * right routing table entries exist.
      */
@@ -1324,19 +1380,158 @@
 
         if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
             if (mNetAttributes[netType].isDefault()) {
-                mNetTrackers[netType].addDefaultRoute();
+                addDefaultRoute(mNetTrackers[netType]);
             } else {
-                mNetTrackers[netType].addPrivateDnsRoutes();
+                addPrivateDnsRoutes(mNetTrackers[netType]);
             }
         } else {
             if (mNetAttributes[netType].isDefault()) {
-                mNetTrackers[netType].removeDefaultRoute();
+                removeDefaultRoute(mNetTrackers[netType]);
             } else {
-                mNetTrackers[netType].removePrivateDnsRoutes();
+                removePrivateDnsRoutes(mNetTrackers[netType]);
             }
         }
     }
 
+    private void addPrivateDnsRoutes(NetworkStateTracker nt) {
+        boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet();
+        LinkProperties p = nt.getLinkProperties();
+        if (p == null) return;
+        String interfaceName = p.getInterfaceName();
+
+        if (DBG) {
+            Slog.d(TAG, "addPrivateDnsRoutes for " + nt +
+                    "(" + interfaceName + ") - mPrivateDnsRouteSet = " + privateDnsRouteSet);
+        }
+        if (interfaceName != null && !privateDnsRouteSet) {
+            Collection<InetAddress> dnsList = p.getDnses();
+            for (InetAddress dns : dnsList) {
+                if (DBG) Slog.d(TAG, "  adding " + dns);
+                NetworkUtils.addHostRoute(interfaceName, dns, null);
+            }
+            nt.privateDnsRouteSet(true);
+        }
+    }
+
+    private void removePrivateDnsRoutes(NetworkStateTracker nt) {
+        // TODO - we should do this explicitly but the NetUtils api doesnt
+        // support this yet - must remove all.  No worse than before
+        LinkProperties p = nt.getLinkProperties();
+        if (p == null) return;
+        String interfaceName = p.getInterfaceName();
+        boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet();
+        if (interfaceName != null && privateDnsRouteSet) {
+            if (DBG) {
+                Slog.d(TAG, "removePrivateDnsRoutes for " + nt.getNetworkInfo().getTypeName() +
+                        " (" + interfaceName + ")");
+            }
+            NetworkUtils.removeHostRoutes(interfaceName);
+            nt.privateDnsRouteSet(false);
+        }
+    }
+
+
+    private void addDefaultRoute(NetworkStateTracker nt) {
+        LinkProperties p = nt.getLinkProperties();
+        if (p == null) return;
+        String interfaceName = p.getInterfaceName();
+        InetAddress defaultGatewayAddr = p.getGateway();
+
+        if ((interfaceName != null) && (defaultGatewayAddr != null )) {
+            if (!NetworkUtils.addDefaultRoute(interfaceName, defaultGatewayAddr) && DBG) {
+                NetworkInfo networkInfo = nt.getNetworkInfo();
+                Slog.d(TAG, "addDefaultRoute for " + networkInfo.getTypeName() +
+                        " (" + interfaceName + "), GatewayAddr=" + defaultGatewayAddr);
+            }
+        }
+    }
+
+
+    public void removeDefaultRoute(NetworkStateTracker nt) {
+        LinkProperties p = nt.getLinkProperties();
+        if (p == null) return;
+        String interfaceName = p.getInterfaceName();
+
+        if (interfaceName != null) {
+            if ((NetworkUtils.removeDefaultRoute(interfaceName) >= 0) && DBG) {
+                NetworkInfo networkInfo = nt.getNetworkInfo();
+                Slog.d(TAG, "removeDefaultRoute for " + networkInfo.getTypeName() + " (" +
+                        interfaceName + ")");
+            }
+        }
+    }
+
+   /**
+     * Reads the network specific TCP buffer sizes from SystemProperties
+     * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
+     * wide use
+     */
+   public void updateNetworkSettings(NetworkStateTracker nt) {
+        String key = nt.getTcpBufferSizesPropName();
+        String bufferSizes = SystemProperties.get(key);
+
+        if (bufferSizes.length() == 0) {
+            Slog.e(TAG, key + " not found in system properties. Using defaults");
+
+            // Setting to default values so we won't be stuck to previous values
+            key = "net.tcp.buffersize.default";
+            bufferSizes = SystemProperties.get(key);
+        }
+
+        // Set values in kernel
+        if (bufferSizes.length() != 0) {
+            if (DBG) {
+                Slog.v(TAG, "Setting TCP values: [" + bufferSizes
+                        + "] which comes from [" + key + "]");
+            }
+            setBufferSize(bufferSizes);
+        }
+    }
+
+   /**
+     * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max]
+     * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem
+     *
+     * @param bufferSizes in the format of "readMin, readInitial, readMax,
+     *        writeMin, writeInitial, writeMax"
+     */
+    private void setBufferSize(String bufferSizes) {
+        try {
+            String[] values = bufferSizes.split(",");
+
+            if (values.length == 6) {
+              final String prefix = "/sys/kernel/ipv4/tcp_";
+                stringToFile(prefix + "rmem_min", values[0]);
+                stringToFile(prefix + "rmem_def", values[1]);
+                stringToFile(prefix + "rmem_max", values[2]);
+                stringToFile(prefix + "wmem_min", values[3]);
+                stringToFile(prefix + "wmem_def", values[4]);
+                stringToFile(prefix + "wmem_max", values[5]);
+            } else {
+                Slog.e(TAG, "Invalid buffersize string: " + bufferSizes);
+            }
+        } catch (IOException e) {
+            Slog.e(TAG, "Can't set tcp buffer sizes:" + e);
+        }
+    }
+
+   /**
+     * Writes string to file. Basically same as "echo -n $string > $filename"
+     *
+     * @param filename
+     * @param string
+     * @throws IOException
+     */
+    private void stringToFile(String filename, String string) throws IOException {
+        FileWriter out = new FileWriter(filename);
+        try {
+            out.write(string);
+        } finally {
+            out.close();
+        }
+    }
+
+
     /**
      * Adjust the per-process dns entries (net.dns<x>.<pid>) based
      * on the highest priority active net which this process requested.
@@ -1352,12 +1547,14 @@
             NetworkStateTracker nt = mNetTrackers[i];
             if (nt.getNetworkInfo().isConnected() &&
                     !nt.isTeardownRequested()) {
+                LinkProperties p = nt.getLinkProperties();
+                if (p == null) continue;
                 List pids = mNetRequestersPids[i];
                 for (int j=0; j<pids.size(); j++) {
                     Integer pid = (Integer)pids.get(j);
                     if (pid.intValue() == myPid) {
-                        String[] dnsList = nt.getNameServers();
-                        writePidDns(dnsList, myPid);
+                        Collection<InetAddress> dnses = p.getDnses();
+                        writePidDns(dnses, myPid);
                         if (doBump) {
                             bumpDns();
                         }
@@ -1379,12 +1576,10 @@
         }
     }
 
-    private void writePidDns(String[] dnsList, int pid) {
+    private void writePidDns(Collection <InetAddress> dnses, int pid) {
         int j = 1;
-        for (String dns : dnsList) {
-            if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
-                SystemProperties.set("net.dns" + j++ + "." + pid, dns);
-            }
+        for (InetAddress dns : dnses) {
+            SystemProperties.set("net.dns" + j++ + "." + pid, dns.getHostAddress());
         }
     }
 
@@ -1401,22 +1596,36 @@
             } catch (NumberFormatException e) {}
         }
         SystemProperties.set("net.dnschange", "" + (n+1));
+        /*
+         * Tell the VMs to toss their DNS caches
+         */
+        Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        mContext.sendBroadcast(intent);
     }
 
     private void handleDnsConfigurationChange(int netType) {
         // add default net's dns entries
         NetworkStateTracker nt = mNetTrackers[netType];
         if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
-            String[] dnsList = nt.getNameServers();
+            LinkProperties p = nt.getLinkProperties();
+            if (p == null) return;
+            Collection<InetAddress> dnses = p.getDnses();
             if (mNetAttributes[netType].isDefault()) {
                 int j = 1;
-                for (String dns : dnsList) {
-                    if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
+                if (dnses.size() == 0 && mDefaultDns != null) {
+                    if (DBG) {
+                        Slog.d(TAG, "no dns provided - using " + mDefaultDns.getHostAddress());
+                    }
+                    SystemProperties.set("net.dns1", mDefaultDns.getHostAddress());
+                    j++;
+                } else {
+                    for (InetAddress dns : dnses) {
                         if (DBG) {
                             Slog.d(TAG, "adding dns " + dns + " for " +
                                     nt.getNetworkInfo().getTypeName());
                         }
-                        SystemProperties.set("net.dns" + j++, dns);
+                        SystemProperties.set("net.dns" + j++, dns.getHostAddress());
                     }
                 }
                 for (int k=j ; k<mNumDnsEntries; k++) {
@@ -1429,11 +1638,11 @@
                 List pids = mNetRequestersPids[netType];
                 for (int y=0; y< pids.size(); y++) {
                     Integer pid = (Integer)pids.get(y);
-                    writePidDns(dnsList, pid.intValue());
+                    writePidDns(dnses, pid.intValue());
                 }
             }
+            bumpDns();
         }
-        bumpDns();
     }
 
     private int getRestoreDefaultNetworkDelay() {
@@ -1488,6 +1697,13 @@
         }
         pw.println();
 
+        synchronized (this) {
+            pw.println("NetworkTranstionWakeLock is currently " +
+                    (mNetTransitionWakeLock.isHeld() ? "" : "not ") + "held.");
+            pw.println("It was last requested for "+mNetTransitionWakeLockCausedBy);
+        }
+        pw.println();
+
         mTethering.dump(fd, pw, args);
 
         if (mInetLog != null) {
@@ -1501,6 +1717,10 @@
 
     // must be stateless - things change under us.
     private class MyHandler extends Handler {
+        public MyHandler(Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             NetworkInfo info;
@@ -1562,29 +1782,25 @@
                         handleConnect(info);
                     }
                     break;
-
-                case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
-                    info = (NetworkInfo) msg.obj;
-                    handleScanResultsAvailable(info);
-                    break;
-
-                case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
-                    handleNotificationChange(msg.arg1 == 1, msg.arg2,
-                            (Notification) msg.obj);
-                    break;
-
                 case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
+                    // TODO - make this handle ip/proxy/gateway/dns changes
                     info = (NetworkInfo) msg.obj;
                     type = info.getType();
                     handleDnsConfigurationChange(type);
                     break;
-
-                case NetworkStateTracker.EVENT_ROAMING_CHANGED:
-                    // fill me in
-                    break;
-
-                case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
-                    // fill me in
+                case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
+                    String causedBy = null;
+                    synchronized (ConnectivityService.this) {
+                        if (msg.arg1 == mNetTransitionWakeLockSerialNumber &&
+                                mNetTransitionWakeLock.isHeld()) {
+                            mNetTransitionWakeLock.release();
+                            causedBy = mNetTransitionWakeLockCausedBy;
+                        }
+                    }
+                    if (causedBy != null) {
+                        Slog.d(TAG, "NetTransition Wakelock for " +
+                                causedBy + " released by timeout");
+                    }
                     break;
                 case EVENT_RESTORE_DEFAULT_NETWORK:
                     FeatureUser u = (FeatureUser)msg.obj;
@@ -1678,6 +1894,15 @@
         }
     }
 
+    public String[] getTetherableBluetoothRegexs() {
+        enforceTetherAccessPermission();
+        if (isTetheringSupported()) {
+            return mTethering.getTetherableBluetoothRegexs();
+        } else {
+            return new String[0];
+        }
+    }
+
     // TODO - move iface listing, queries, etc to new module
     // javadoc from interface
     public String[] getTetherableIfaces() {
@@ -1706,6 +1931,25 @@
         return tetherEnabledInSettings && mTetheringConfigValid;
     }
 
+    // An API NetworkStateTrackers can call when they lose their network.
+    // This will automatically be cleared after X seconds or a network becomes CONNECTED,
+    // whichever happens first.  The timer is started by the first caller and not
+    // restarted by subsequent callers.
+    public void requestNetworkTransitionWakelock(String forWhom) {
+        enforceConnectivityInternalPermission();
+        synchronized (this) {
+            if (mNetTransitionWakeLock.isHeld()) return;
+            mNetTransitionWakeLockSerialNumber++;
+            mNetTransitionWakeLock.acquire();
+            mNetTransitionWakeLockCausedBy = forWhom;
+        }
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                EVENT_CLEAR_NET_TRANSITION_WAKELOCK,
+                mNetTransitionWakeLockSerialNumber, 0),
+                mNetTransitionWakeLockTimeout);
+        return;
+    }
+
     // 100 percent is full good, 0 is full bad.
     public void reportInetCondition(int networkType, int percentage) {
         if (DBG) Slog.d(TAG, "reportNetworkCondition(" + networkType + ", " + percentage + ")");
diff --git a/services/java/com/android/server/CountryDetectorService.java b/services/java/com/android/server/CountryDetectorService.java
new file mode 100644
index 0000000..3081ebe
--- /dev/null
+++ b/services/java/com/android/server/CountryDetectorService.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.server;
+
+import java.util.HashMap;
+
+import com.android.server.location.ComprehensiveCountryDetector;
+
+import android.content.Context;
+import android.location.Country;
+import android.location.CountryListener;
+import android.location.ICountryDetector;
+import android.location.ICountryListener;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * This class detects the country that the user is in through
+ * {@link ComprehensiveCountryDetector}.
+ *
+ * @hide
+ */
+public class CountryDetectorService extends ICountryDetector.Stub implements Runnable {
+
+    /**
+     * The class represents the remote listener, it will also removes itself
+     * from listener list when the remote process was died.
+     */
+    private final class Receiver implements IBinder.DeathRecipient {
+        private final ICountryListener mListener;
+        private final IBinder mKey;
+
+        public Receiver(ICountryListener listener) {
+            mListener = listener;
+            mKey = listener.asBinder();
+        }
+
+        public void binderDied() {
+            removeListener(mKey);
+        }
+
+        @Override
+        public boolean equals(Object otherObj) {
+            if (otherObj instanceof Receiver) {
+                return mKey.equals(((Receiver) otherObj).mKey);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return mKey.hashCode();
+        }
+
+        public ICountryListener getListener() {
+            return mListener;
+        }
+    }
+
+    private final static String TAG = "CountryDetectorService";
+
+    private final HashMap<IBinder, Receiver> mReceivers;
+    private final Context mContext;
+    private ComprehensiveCountryDetector mCountryDetector;
+    private boolean mSystemReady;
+    private Handler mHandler;
+    private CountryListener mLocationBasedDetectorListener;
+
+    public CountryDetectorService(Context context) {
+        super();
+        mReceivers = new HashMap<IBinder, Receiver>();
+        mContext = context;
+    }
+
+    @Override
+    public Country detectCountry() throws RemoteException {
+        if (!mSystemReady) {
+            throw new RemoteException();
+        }
+        return mCountryDetector.detectCountry();
+    }
+
+    /**
+     * Add the ICountryListener into the listener list.
+     */
+    @Override
+    public void addCountryListener(ICountryListener listener) throws RemoteException {
+        if (!mSystemReady) {
+            throw new RemoteException();
+        }
+        addListener(listener);
+    }
+
+    /**
+     * Remove the ICountryListener from the listener list.
+     */
+    @Override
+    public void removeCountryListener(ICountryListener listener) throws RemoteException {
+        if (!mSystemReady) {
+            throw new RemoteException();
+        }
+        removeListener(listener.asBinder());
+    }
+
+    private void addListener(ICountryListener listener) {
+        synchronized (mReceivers) {
+            Receiver r = new Receiver(listener);
+            try {
+                listener.asBinder().linkToDeath(r, 0);
+                mReceivers.put(listener.asBinder(), r);
+                if (mReceivers.size() == 1) {
+                    Slog.d(TAG, "The first listener is added");
+                    setCountryListener(mLocationBasedDetectorListener);
+                }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "linkToDeath failed:", e);
+            }
+        }
+    }
+
+    private void removeListener(IBinder key) {
+        synchronized (mReceivers) {
+            mReceivers.remove(key);
+            if (mReceivers.isEmpty()) {
+                setCountryListener(null);
+                Slog.d(TAG, "No listener is left");
+            }
+        }
+    }
+
+
+    protected void notifyReceivers(Country country) {
+        synchronized(mReceivers) {
+            for (Receiver receiver : mReceivers.values()) {
+                try {
+                    receiver.getListener().onCountryDetected(country);
+                } catch (RemoteException e) {
+                    // TODO: Shall we remove the receiver?
+                    Slog.e(TAG, "notifyReceivers failed:", e);
+                }
+            }
+        }
+    }
+
+    void systemReady() {
+        // Shall we wait for the initialization finish.
+        Thread thread = new Thread(this, "CountryDetectorService");
+        thread.start();
+    }
+
+    private void initialize() {
+        mCountryDetector = new ComprehensiveCountryDetector(mContext);
+        mLocationBasedDetectorListener = new CountryListener() {
+            public void onCountryDetected(final Country country) {
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        notifyReceivers(country);
+                    }
+                });
+            }
+        };
+    }
+
+    public void run() {
+        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+        Looper.prepare();
+        mHandler = new Handler();
+        initialize();
+        mSystemReady = true;
+        Looper.loop();
+    }
+
+    protected void setCountryListener(final CountryListener listener) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mCountryDetector.setCountryListener(listener);
+            }
+        });
+    }
+
+    // For testing
+    boolean isSystemReady() {
+        return mSystemReady;
+    }
+}
diff --git a/services/java/com/android/server/DemoDataSet.java b/services/java/com/android/server/DemoDataSet.java
deleted file mode 100644
index 277985f..0000000
--- a/services/java/com/android/server/DemoDataSet.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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.server;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.AssetManager;
-import android.net.Uri;
-import android.os.Environment;
-import android.provider.Contacts;
-import android.provider.Settings;
-import android.provider.MediaStore.Images;
-import android.util.Config;
-import android.util.Slog;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class DemoDataSet
-{
-    private final static String LOG_TAG = "DemoDataSet";
-
-    private ContentResolver mContentResolver;
-
-    public final void add(Context context)
-    {
-        mContentResolver = context.getContentResolver();
-
-        // Remove all the old data
-        mContentResolver.delete(Contacts.People.CONTENT_URI, null, null);
-
-        // Add the new data
-        addDefaultData();
-        
-        // Add images from /android/images
-        addDefaultImages();
-    }
-
-    private final void addDefaultImages()
-    {
-        File rootDirectory = Environment.getRootDirectory();
-        String [] files
-            = new File(rootDirectory, "images").list();
-        int count = files.length;
-
-        if (count == 0) {
-            Slog.i(LOG_TAG, "addDefaultImages: no images found!");
-            return;
-        }
-
-        for (int i = 0; i < count; i++)
-        {
-            String name = files[i];
-            String path = rootDirectory + "/" + name;
-            
-            try {
-                Images.Media.insertImage(mContentResolver, path, name, null);
-            } catch (FileNotFoundException e) {
-                Slog.e(LOG_TAG, "Failed to import image " + path, e);
-            }
-        }
-    }
-    
-    private final void addDefaultData()
-    {
-        Slog.i(LOG_TAG, "Adding default data...");
-
-//       addImage("Violet", "images/violet.png");
-//       addImage("Corky", "images/corky.png");
-
-        // PENDING: should this be done here?!?!
-        Intent intent = new Intent(
-                Intent.ACTION_CALL, Uri.fromParts("voicemail", "", null));
-        addShortcut("1", intent);
-    }
-
-    private final Uri addImage(String name, Uri file)
-    {
-        ContentValues imagev = new ContentValues();
-        imagev.put("name", name);
-
-        Uri url = null;
-
-        AssetManager ass = AssetManager.getSystem();
-        InputStream in = null;
-        OutputStream out = null;
-        
-        try
-        {
-            in = ass.open(file.toString());
-
-            url = mContentResolver.insert(Images.Media.INTERNAL_CONTENT_URI, imagev);
-            out = mContentResolver.openOutputStream(url);
-
-            final int size = 8 * 1024;
-            byte[] buf = new byte[size];
-
-            int count = 0;
-            do
-            {
-                count = in.read(buf, 0, size);
-                if (count > 0) {
-                    out.write(buf, 0, count);
-                }
-            } while (count > 0);
-        }
-        catch (Exception e)
-        {
-            Slog.e(LOG_TAG, "Failed to insert image '" + file + "'", e);
-            url = null;
-        }
-
-        return url;
-    }
-
-    private final Uri addShortcut(String shortcut, Intent intent)
-    {
-        if (Config.LOGV) Slog.v(LOG_TAG, "addShortcut: shortcut=" + shortcut + ", intent=" + intent);
-        return Settings.Bookmarks.add(mContentResolver, intent, null, null,
-                                      shortcut != null ? shortcut.charAt(0) : 0, 0);
-    }
-}
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 1538003..081bd30 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -34,6 +34,7 @@
 import android.app.admin.IDevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -48,6 +49,8 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.net.Proxy;
+import android.provider.Settings;
 import android.util.Slog;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
@@ -61,47 +64,67 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Implementation of the device policy APIs.
  */
 public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
     static final String TAG = "DevicePolicyManagerService";
-    
+
     final Context mContext;
     final MyPackageMonitor mMonitor;
     final PowerManager.WakeLock mWakeLock;
 
     IPowerManager mIPowerManager;
-    
+
     int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
     int mActivePasswordLength = 0;
+    int mActivePasswordUpperCase = 0;
+    int mActivePasswordLowerCase = 0;
+    int mActivePasswordLetters = 0;
+    int mActivePasswordNumeric = 0;
+    int mActivePasswordSymbols = 0;
+    int mActivePasswordNonLetter = 0;
     int mFailedPasswordAttempts = 0;
-    
+
     int mPasswordOwner = -1;
-    
+
     final HashMap<ComponentName, ActiveAdmin> mAdminMap
             = new HashMap<ComponentName, ActiveAdmin>();
     final ArrayList<ActiveAdmin> mAdminList
             = new ArrayList<ActiveAdmin>();
-    
+
     static class ActiveAdmin {
         final DeviceAdminInfo info;
-        
+
         int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
         int minimumPasswordLength = 0;
+        int passwordHistoryLength = 0;
+        int minimumPasswordUpperCase = 0;
+        int minimumPasswordLowerCase = 0;
+        int minimumPasswordLetters = 1;
+        int minimumPasswordNumeric = 1;
+        int minimumPasswordSymbols = 1;
+        int minimumPasswordNonLetter = 0;
         long maximumTimeToUnlock = 0;
         int maximumFailedPasswordsForWipe = 0;
-        
+
+        // TODO: review implementation decisions with frameworks team
+        boolean specifiesGlobalProxy = false;
+        String globalProxySpec = null;
+        String globalProxyExclusionList = null;
+
         ActiveAdmin(DeviceAdminInfo _info) {
             info = _info;
         }
-        
+
         int getUid() { return info.getActivityInfo().applicationInfo.uid; }
-        
+
         void writeToXml(XmlSerializer out)
                 throws IllegalArgumentException, IllegalStateException, IOException {
             out.startTag(null, "policies");
@@ -114,7 +137,42 @@
                 if (minimumPasswordLength > 0) {
                     out.startTag(null, "min-password-length");
                     out.attribute(null, "value", Integer.toString(minimumPasswordLength));
-                    out.endTag(null, "mn-password-length");
+                    out.endTag(null, "min-password-length");
+                }
+                if(passwordHistoryLength > 0) {
+                    out.startTag(null, "password-history-length");
+                    out.attribute(null, "value", Integer.toString(passwordHistoryLength));
+                    out.endTag(null, "password-history-length");
+                }
+                if (minimumPasswordUpperCase > 0) {
+                    out.startTag(null, "min-password-uppercase");
+                    out.attribute(null, "value", Integer.toString(minimumPasswordUpperCase));
+                    out.endTag(null, "min-password-uppercase");
+                }
+                if (minimumPasswordLowerCase > 0) {
+                    out.startTag(null, "min-password-lowercase");
+                    out.attribute(null, "value", Integer.toString(minimumPasswordLowerCase));
+                    out.endTag(null, "min-password-lowercase");
+                }
+                if (minimumPasswordLetters > 0) {
+                    out.startTag(null, "min-password-letters");
+                    out.attribute(null, "value", Integer.toString(minimumPasswordLetters));
+                    out.endTag(null, "min-password-letters");
+                }
+                if (minimumPasswordNumeric > 0) {
+                    out.startTag(null, "min-password-numeric");
+                    out.attribute(null, "value", Integer.toString(minimumPasswordNumeric));
+                    out.endTag(null, "min-password-numeric");
+                }
+                if (minimumPasswordSymbols > 0) {
+                    out.startTag(null, "min-password-symbols");
+                    out.attribute(null, "value", Integer.toString(minimumPasswordSymbols));
+                    out.endTag(null, "min-password-symbols");
+                }
+                if (minimumPasswordNonLetter > 0) {
+                    out.startTag(null, "min-password-nonletter");
+                    out.attribute(null, "value", Integer.toString(minimumPasswordNonLetter));
+                    out.endTag(null, "min-password-nonletter");
                 }
             }
             if (maximumTimeToUnlock != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
@@ -127,8 +185,23 @@
                 out.attribute(null, "value", Integer.toString(maximumFailedPasswordsForWipe));
                 out.endTag(null, "max-failed-password-wipe");
             }
+            if (specifiesGlobalProxy) {
+                out.startTag(null, "specifies-global-proxy");
+                out.attribute(null, "value", Boolean.toString(specifiesGlobalProxy));
+                out.endTag(null, "specifies_global_proxy");
+                if (globalProxySpec != null) {
+                    out.startTag(null, "global-proxy-spec");
+                    out.attribute(null, "value", globalProxySpec);
+                    out.endTag(null, "global-proxy-spec");
+                }
+                if (globalProxyExclusionList != null) {
+                    out.startTag(null, "global-proxy-exclusion-list");
+                    out.attribute(null, "value", globalProxyExclusionList);
+                    out.endTag(null, "global-proxy-exclusion-list");
+                }
+            }
         }
-        
+
         void readFromXml(XmlPullParser parser)
                 throws XmlPullParserException, IOException {
             int outerDepth = parser.getDepth();
@@ -147,19 +220,49 @@
                 } else if ("min-password-length".equals(tag)) {
                     minimumPasswordLength = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
+                } else if ("password-history-length".equals(tag)) {
+                    passwordHistoryLength = Integer.parseInt(
+                            parser.getAttributeValue(null, "value"));
+                } else if ("min-password-uppercase".equals(tag)) {
+                    minimumPasswordUpperCase = Integer.parseInt(
+                            parser.getAttributeValue(null, "value"));
+                } else if ("min-password-lowercase".equals(tag)) {
+                    minimumPasswordLowerCase = Integer.parseInt(
+                            parser.getAttributeValue(null, "value"));
+                } else if ("min-password-letters".equals(tag)) {
+                    minimumPasswordLetters = Integer.parseInt(
+                            parser.getAttributeValue(null, "value"));
+                } else if ("min-password-numeric".equals(tag)) {
+                    minimumPasswordNumeric = Integer.parseInt(
+                            parser.getAttributeValue(null, "value"));
+                } else if ("min-password-symbols".equals(tag)) {
+                    minimumPasswordSymbols = Integer.parseInt(
+                            parser.getAttributeValue(null, "value"));
+                } else if ("min-password-nonletter".equals(tag)) {
+                    minimumPasswordNonLetter = Integer.parseInt(
+                            parser.getAttributeValue(null, "value"));
                 } else if ("max-time-to-unlock".equals(tag)) {
                     maximumTimeToUnlock = Long.parseLong(
                             parser.getAttributeValue(null, "value"));
                 } else if ("max-failed-password-wipe".equals(tag)) {
                     maximumFailedPasswordsForWipe = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
+                } else if ("specifies-global-proxy".equals(tag)) {
+                    specifiesGlobalProxy = Boolean.getBoolean(
+                            parser.getAttributeValue(null, "value"));
+                } else if ("global-proxy-spec".equals(tag)) {
+                    globalProxySpec =
+                        parser.getAttributeValue(null, "value");
+                } else if ("global-proxy-exclusion-list".equals(tag)) {
+                    globalProxyExclusionList =
+                        parser.getAttributeValue(null, "value");
                 } else {
                     Slog.w(TAG, "Unknown admin tag: " + tag);
                 }
                 XmlUtils.skipCurrentTag(parser);
             }
         }
-        
+
         void dump(String prefix, PrintWriter pw) {
             pw.print(prefix); pw.print("uid="); pw.println(getUid());
             pw.print(prefix); pw.println("policies:");
@@ -170,23 +273,47 @@
                 }
             }
             pw.print(prefix); pw.print("passwordQuality=0x");
-                    pw.print(Integer.toHexString(passwordQuality));
-                    pw.print(" minimumPasswordLength=");
+                    pw.println(Integer.toHexString(passwordQuality));
+            pw.print(prefix); pw.print("minimumPasswordLength=");
                     pw.println(minimumPasswordLength);
+            pw.print(prefix); pw.print("passwordHistoryLength=");
+                    pw.println(passwordHistoryLength);
+            pw.print(prefix); pw.print("minimumPasswordUpperCase=");
+                    pw.println(minimumPasswordUpperCase);
+            pw.print(prefix); pw.print("minimumPasswordLowerCase=");
+                    pw.println(minimumPasswordLowerCase);
+            pw.print(prefix); pw.print("minimumPasswordLetters=");
+                    pw.println(minimumPasswordLetters);
+            pw.print(prefix); pw.print("minimumPasswordNumeric=");
+                    pw.println(minimumPasswordNumeric);
+            pw.print(prefix); pw.print("minimumPasswordSymbols=");
+                    pw.println(minimumPasswordSymbols);
+            pw.print(prefix); pw.print("minimumPasswordNonLetter=");
+                    pw.println(minimumPasswordNonLetter);
             pw.print(prefix); pw.print("maximumTimeToUnlock=");
                     pw.println(maximumTimeToUnlock);
             pw.print(prefix); pw.print("maximumFailedPasswordsForWipe=");
                     pw.println(maximumFailedPasswordsForWipe);
+            pw.print(prefix); pw.print("specifiesGlobalProxy=");
+                    pw.println(specifiesGlobalProxy);
+            if (globalProxySpec != null) {
+                pw.print(prefix); pw.print("globalProxySpec=");
+                        pw.println(globalProxySpec);
+            }
+            if (globalProxyExclusionList != null) {
+                pw.print(prefix); pw.print("globalProxyEclusionList=");
+                        pw.println(globalProxyExclusionList);
+            }
         }
     }
-    
+
     class MyPackageMonitor extends PackageMonitor {
         public void onSomePackagesChanged() {
             synchronized (DevicePolicyManagerService.this) {
                 boolean removed = false;
                 for (int i=mAdminList.size()-1; i>=0; i--) {
                     ActiveAdmin aa = mAdminList.get(i);
-                    int change = isPackageDisappearing(aa.info.getPackageName()); 
+                    int change = isPackageDisappearing(aa.info.getPackageName());
                     if (change == PACKAGE_PERMANENT_CHANGE
                             || change == PACKAGE_TEMPORARY_CHANGE) {
                         Slog.w(TAG, "Admin unexpectedly uninstalled: "
@@ -211,7 +338,7 @@
             }
         }
     }
-    
+
     /**
      * Instantiates the service.
      */
@@ -230,7 +357,7 @@
         }
         return mIPowerManager;
     }
-    
+
     ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who) {
         ActiveAdmin admin = mAdminMap.get(who);
         if (admin != null
@@ -240,7 +367,7 @@
         }
         return null;
     }
-    
+
     ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
             throws SecurityException {
         final int callingUid = Binder.getCallingUid();
@@ -271,13 +398,13 @@
                     + Binder.getCallingUid() + " for policy #" + reqPolicy);
         }
     }
-    
+
     void sendAdminCommandLocked(ActiveAdmin admin, String action) {
         Intent intent = new Intent(action);
         intent.setComponent(admin.info.getComponent());
         mContext.sendBroadcast(intent);
     }
-    
+
     void sendAdminCommandLocked(String action, int reqPolicy) {
         final int N = mAdminList.size();
         if (N > 0) {
@@ -289,19 +416,24 @@
             }
         }
     }
-    
+
     void removeActiveAdminLocked(ComponentName adminReceiver) {
         ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
         if (admin != null) {
+            boolean doProxyCleanup =
+                admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
             sendAdminCommandLocked(admin,
                     DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED);
             // XXX need to wait for it to complete.
             mAdminList.remove(admin);
             mAdminMap.remove(adminReceiver);
             validatePasswordOwnerLocked();
+            if (doProxyCleanup) {
+                resetGlobalProxy();
+            }
         }
     }
-    
+
     public DeviceAdminInfo findAdmin(ComponentName adminName) {
         Intent resolveIntent = new Intent();
         resolveIntent.setComponent(adminName);
@@ -310,7 +442,7 @@
         if (infos == null || infos.size() <= 0) {
             throw new IllegalArgumentException("Unknown admin: " + adminName);
         }
-        
+
         try {
             return new DeviceAdminInfo(mContext, infos.get(0));
         } catch (XmlPullParserException e) {
@@ -321,7 +453,7 @@
             return null;
         }
     }
-    
+
     private static JournaledFile makeJournaledFile() {
         final String base = "/data/system/device_policies.xml";
         return new JournaledFile(new File(base), new File(base + ".tmp"));
@@ -337,7 +469,7 @@
             out.startDocument(null, true);
 
             out.startTag(null, "policies");
-            
+
             final int N = mAdminList.size();
             for (int i=0; i<N; i++) {
                 ActiveAdmin ap = mAdminList.get(i);
@@ -348,26 +480,36 @@
                     out.endTag(null, "admin");
                 }
             }
-            
+
             if (mPasswordOwner >= 0) {
                 out.startTag(null, "password-owner");
                 out.attribute(null, "value", Integer.toString(mPasswordOwner));
                 out.endTag(null, "password-owner");
             }
-            
+
             if (mFailedPasswordAttempts != 0) {
                 out.startTag(null, "failed-password-attempts");
                 out.attribute(null, "value", Integer.toString(mFailedPasswordAttempts));
                 out.endTag(null, "failed-password-attempts");
             }
-            
-            if (mActivePasswordQuality != 0 || mActivePasswordLength != 0) {
+
+            if (mActivePasswordQuality != 0 || mActivePasswordLength != 0
+                    || mActivePasswordUpperCase != 0 || mActivePasswordLowerCase != 0
+                    || mActivePasswordLetters != 0 || mActivePasswordNumeric != 0
+                    || mActivePasswordSymbols != 0 || mActivePasswordNonLetter != 0) {
                 out.startTag(null, "active-password");
                 out.attribute(null, "quality", Integer.toString(mActivePasswordQuality));
                 out.attribute(null, "length", Integer.toString(mActivePasswordLength));
+                out.attribute(null, "uppercase", Integer.toString(mActivePasswordUpperCase));
+                out.attribute(null, "lowercase", Integer.toString(mActivePasswordLowerCase));
+                out.attribute(null, "letters", Integer.toString(mActivePasswordLetters));
+                out.attribute(null, "numeric", Integer
+                        .toString(mActivePasswordNumeric));
+                out.attribute(null, "symbols", Integer.toString(mActivePasswordSymbols));
+                out.attribute(null, "nonletter", Integer.toString(mActivePasswordNonLetter));
                 out.endTag(null, "active-password");
             }
-            
+
             out.endTag(null, "policies");
 
             out.endDocument();
@@ -445,6 +587,18 @@
                             parser.getAttributeValue(null, "quality"));
                     mActivePasswordLength = Integer.parseInt(
                             parser.getAttributeValue(null, "length"));
+                    mActivePasswordUpperCase = Integer.parseInt(
+                            parser.getAttributeValue(null, "uppercase"));
+                    mActivePasswordLowerCase = Integer.parseInt(
+                            parser.getAttributeValue(null, "lowercase"));
+                    mActivePasswordLetters = Integer.parseInt(
+                            parser.getAttributeValue(null, "letters"));
+                    mActivePasswordNumeric = Integer.parseInt(
+                            parser.getAttributeValue(null, "numeric"));
+                    mActivePasswordSymbols = Integer.parseInt(
+                            parser.getAttributeValue(null, "symbols"));
+                    mActivePasswordNonLetter = Integer.parseInt(
+                            parser.getAttributeValue(null, "nonletter"));
                     XmlUtils.skipCurrentTag(parser);
                 } else {
                     Slog.w(TAG, "Unknown tag: " + tag);
@@ -484,10 +638,16 @@
                     + Integer.toHexString(utils.getActivePasswordQuality()));
             mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
             mActivePasswordLength = 0;
+            mActivePasswordUpperCase = 0;
+            mActivePasswordLowerCase = 0;
+            mActivePasswordLetters = 0;
+            mActivePasswordNumeric = 0;
+            mActivePasswordSymbols = 0;
+            mActivePasswordNonLetter = 0;
         }
-        
+
         validatePasswordOwnerLocked();
-        
+
         long timeMs = getMaximumTimeToLock(null);
         if (timeMs <= 0) {
             timeMs = Integer.MAX_VALUE;
@@ -506,12 +666,13 @@
             case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
             case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
             case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+            case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
                 return;
         }
         throw new IllegalArgumentException("Invalid quality constant: 0x"
                 + Integer.toHexString(quality));
     }
-    
+
     void validatePasswordOwnerLocked() {
         if (mPasswordOwner >= 0) {
             boolean haveOwner = false;
@@ -528,17 +689,17 @@
             }
         }
     }
-    
+
     public void systemReady() {
         synchronized (this) {
             loadSettingsLocked();
         }
     }
-    
+
     public void setActiveAdmin(ComponentName adminReceiver) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-        
+
         DeviceAdminInfo info = findAdmin(adminReceiver);
         if (info == null) {
             throw new IllegalArgumentException("Bad admin: " + adminReceiver);
@@ -560,13 +721,13 @@
             }
         }
     }
-    
+
     public boolean isAdminActive(ComponentName adminReceiver) {
         synchronized (this) {
             return getActiveAdminUncheckedLocked(adminReceiver) != null;
         }
     }
-    
+
     public List<ComponentName> getActiveAdmins() {
         synchronized (this) {
             final int N = mAdminList.size();
@@ -580,7 +741,7 @@
             return res;
         }
     }
-    
+
     public boolean packageHasActiveAdmins(String packageName) {
         synchronized (this) {
             final int N = mAdminList.size();
@@ -592,7 +753,7 @@
             return false;
         }
     }
-    
+
     public void removeActiveAdmin(ComponentName adminReceiver) {
         synchronized (this) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver);
@@ -611,10 +772,10 @@
             }
         }
     }
-    
+
     public void setPasswordQuality(ComponentName who, int quality) {
         validateQualityConstant(quality);
-        
+
         synchronized (this) {
             if (who == null) {
                 throw new NullPointerException("ComponentName is null");
@@ -627,16 +788,16 @@
             }
         }
     }
-    
+
     public int getPasswordQuality(ComponentName who) {
         synchronized (this) {
             int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-            
+
             if (who != null) {
                 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
                 return admin != null ? admin.passwordQuality : mode;
             }
-            
+
             final int N = mAdminList.size();
             for  (int i=0; i<N; i++) {
                 ActiveAdmin admin = mAdminList.get(i);
@@ -647,7 +808,7 @@
             return mode;
         }
     }
-    
+
     public void setPasswordMinimumLength(ComponentName who, int length) {
         synchronized (this) {
             if (who == null) {
@@ -661,16 +822,16 @@
             }
         }
     }
-    
+
     public int getPasswordMinimumLength(ComponentName who) {
         synchronized (this) {
             int length = 0;
-            
+
             if (who != null) {
                 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
                 return admin != null ? admin.minimumPasswordLength : length;
             }
-            
+
             final int N = mAdminList.size();
             for  (int i=0; i<N; i++) {
                 ActiveAdmin admin = mAdminList.get(i);
@@ -681,18 +842,267 @@
             return length;
         }
     }
-    
+
+    public void setPasswordHistoryLength(ComponentName who, int length) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+            if (ap.passwordHistoryLength != length) {
+                ap.passwordHistoryLength = length;
+                saveSettingsLocked();
+            }
+        }
+    }
+
+    public int getPasswordHistoryLength(ComponentName who) {
+        synchronized (this) {
+            int length = 0;
+
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+                return admin != null ? admin.passwordHistoryLength : length;
+            }
+
+            final int N = mAdminList.size();
+            for (int i = 0; i < N; i++) {
+                ActiveAdmin admin = mAdminList.get(i);
+                if (length < admin.passwordHistoryLength) {
+                    length = admin.passwordHistoryLength;
+                }
+            }
+            return length;
+        }
+    }
+
+    public void setPasswordMinimumUpperCase(ComponentName who, int length) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+            if (ap.minimumPasswordUpperCase != length) {
+                ap.minimumPasswordUpperCase = length;
+                saveSettingsLocked();
+            }
+        }
+    }
+
+    public int getPasswordMinimumUpperCase(ComponentName who) {
+        synchronized (this) {
+            int length = 0;
+
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+                return admin != null ? admin.minimumPasswordUpperCase : length;
+            }
+
+            final int N = mAdminList.size();
+            for (int i=0; i<N; i++) {
+                ActiveAdmin admin = mAdminList.get(i);
+                if (length < admin.minimumPasswordUpperCase) {
+                    length = admin.minimumPasswordUpperCase;
+                }
+            }
+            return length;
+        }
+    }
+
+    public void setPasswordMinimumLowerCase(ComponentName who, int length) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+            if (ap.minimumPasswordLowerCase != length) {
+                ap.minimumPasswordLowerCase = length;
+                saveSettingsLocked();
+            }
+        }
+    }
+
+    public int getPasswordMinimumLowerCase(ComponentName who) {
+        synchronized (this) {
+            int length = 0;
+
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+                return admin != null ? admin.minimumPasswordLowerCase : length;
+            }
+
+            final int N = mAdminList.size();
+            for (int i=0; i<N; i++) {
+                ActiveAdmin admin = mAdminList.get(i);
+                if (length < admin.minimumPasswordLowerCase) {
+                    length = admin.minimumPasswordLowerCase;
+                }
+            }
+            return length;
+        }
+    }
+
+    public void setPasswordMinimumLetters(ComponentName who, int length) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+            if (ap.minimumPasswordLetters != length) {
+                ap.minimumPasswordLetters = length;
+                saveSettingsLocked();
+            }
+        }
+    }
+
+    public int getPasswordMinimumLetters(ComponentName who) {
+        synchronized (this) {
+            int length = 0;
+
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+                return admin != null ? admin.minimumPasswordLetters : length;
+            }
+
+            final int N = mAdminList.size();
+            for (int i=0; i<N; i++) {
+                ActiveAdmin admin = mAdminList.get(i);
+                if (length < admin.minimumPasswordLetters) {
+                    length = admin.minimumPasswordLetters;
+                }
+            }
+            return length;
+        }
+    }
+
+    public void setPasswordMinimumNumeric(ComponentName who, int length) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+            if (ap.minimumPasswordNumeric != length) {
+                ap.minimumPasswordNumeric = length;
+                saveSettingsLocked();
+            }
+        }
+    }
+
+    public int getPasswordMinimumNumeric(ComponentName who) {
+        synchronized (this) {
+            int length = 0;
+
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+                return admin != null ? admin.minimumPasswordNumeric : length;
+            }
+
+            final int N = mAdminList.size();
+            for (int i = 0; i < N; i++) {
+                ActiveAdmin admin = mAdminList.get(i);
+                if (length < admin.minimumPasswordNumeric) {
+                    length = admin.minimumPasswordNumeric;
+                }
+            }
+            return length;
+        }
+    }
+
+    public void setPasswordMinimumSymbols(ComponentName who, int length) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+            if (ap.minimumPasswordSymbols != length) {
+                ap.minimumPasswordSymbols = length;
+                saveSettingsLocked();
+            }
+        }
+    }
+
+    public int getPasswordMinimumSymbols(ComponentName who) {
+        synchronized (this) {
+            int length = 0;
+
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+                return admin != null ? admin.minimumPasswordSymbols : length;
+            }
+
+            final int N = mAdminList.size();
+            for  (int i=0; i<N; i++) {
+                ActiveAdmin admin = mAdminList.get(i);
+                if (length < admin.minimumPasswordSymbols) {
+                    length = admin.minimumPasswordSymbols;
+                }
+            }
+            return length;
+        }
+    }
+
+    public void setPasswordMinimumNonLetter(ComponentName who, int length) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+            if (ap.minimumPasswordNonLetter != length) {
+                ap.minimumPasswordNonLetter = length;
+                saveSettingsLocked();
+            }
+        }
+    }
+
+    public int getPasswordMinimumNonLetter(ComponentName who) {
+        synchronized (this) {
+            int length = 0;
+
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+                return admin != null ? admin.minimumPasswordNonLetter : length;
+            }
+
+            final int N = mAdminList.size();
+            for (int i=0; i<N; i++) {
+                ActiveAdmin admin = mAdminList.get(i);
+                if (length < admin.minimumPasswordNonLetter) {
+                    length = admin.minimumPasswordNonLetter;
+                }
+            }
+            return length;
+        }
+    }
+
     public boolean isActivePasswordSufficient() {
         synchronized (this) {
             // This API can only be called by an active device admin,
             // so try to retrieve it to check that the caller is one.
             getActiveAdminForCallerLocked(null,
                     DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
-            return mActivePasswordQuality >= getPasswordQuality(null)
-                    && mActivePasswordLength >= getPasswordMinimumLength(null);
+            if (mActivePasswordQuality < getPasswordQuality(null)
+                    || mActivePasswordLength < getPasswordMinimumLength(null)) {
+                return false;
+            }
+            if(mActivePasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
+                return true;
+            }
+            return mActivePasswordUpperCase >= getPasswordMinimumUpperCase(null)
+                    && mActivePasswordLowerCase >= getPasswordMinimumLowerCase(null)
+                    && mActivePasswordLetters >= getPasswordMinimumLetters(null)
+                    && mActivePasswordNumeric >= getPasswordMinimumNumeric(null)
+                    && mActivePasswordSymbols >= getPasswordMinimumSymbols(null)
+                    && mActivePasswordNonLetter >= getPasswordMinimumNonLetter(null);
         }
     }
-    
+
     public int getCurrentFailedPasswordAttempts() {
         synchronized (this) {
             // This API can only be called by an active device admin,
@@ -702,7 +1112,7 @@
             return mFailedPasswordAttempts;
         }
     }
-    
+
     public void setMaximumFailedPasswordsForWipe(ComponentName who, int num) {
         synchronized (this) {
             // This API can only be called by an active device admin,
@@ -717,16 +1127,16 @@
             }
         }
     }
-    
+
     public int getMaximumFailedPasswordsForWipe(ComponentName who) {
         synchronized (this) {
             int count = 0;
-            
+
             if (who != null) {
                 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
                 return admin != null ? admin.maximumFailedPasswordsForWipe : count;
             }
-            
+
             final int N = mAdminList.size();
             for  (int i=0; i<N; i++) {
                 ActiveAdmin admin = mAdminList.get(i);
@@ -740,7 +1150,7 @@
             return count;
         }
     }
-    
+
     public boolean resetPassword(String password, int flags) {
         int quality;
         synchronized (this) {
@@ -751,14 +1161,15 @@
             quality = getPasswordQuality(null);
             if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
                 int realQuality = LockPatternUtils.computePasswordQuality(password);
-                if (realQuality < quality) {
+                if (realQuality < quality
+                        && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
                     Slog.w(TAG, "resetPassword: password quality 0x"
                             + Integer.toHexString(quality)
                             + " does not meet required quality 0x"
                             + Integer.toHexString(quality));
                     return false;
                 }
-                quality = realQuality;
+                quality = Math.max(realQuality, quality);
             }
             int length = getPasswordMinimumLength(null);
             if (password.length() < length) {
@@ -766,14 +1177,86 @@
                         + " does not meet required length " + length);
                 return false;
             }
+            if (quality == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
+                int letters = 0;
+                int uppercase = 0;
+                int lowercase = 0;
+                int numbers = 0;
+                int symbols = 0;
+                int nonletter = 0;
+                for (int i = 0; i < password.length(); i++) {
+                    char c = password.charAt(i);
+                    if (c >= 'A' && c <= 'Z') {
+                        letters++;
+                        uppercase++;
+                    } else if (c >= 'a' && c <= 'z') {
+                        letters++;
+                        lowercase++;
+                    } else if (c >= '0' && c <= '9') {
+                        numbers++;
+                        nonletter++;
+                    } else {
+                        symbols++;
+                        nonletter++;
+                    }
+                }
+                int neededLetters = getPasswordMinimumLetters(null);
+                if(letters < neededLetters) {
+                    Slog.w(TAG, "resetPassword: number of letters " + letters
+                            + " does not meet required number of letters " + neededLetters);
+                    return false;
+                }
+                int neededNumbers = getPasswordMinimumNumeric(null);
+                if (numbers < neededNumbers) {
+                    Slog
+                            .w(TAG, "resetPassword: number of numerical digits " + numbers
+                                    + " does not meet required number of numerical digits "
+                                    + neededNumbers);
+                    return false;
+                }
+                int neededLowerCase = getPasswordMinimumLowerCase(null);
+                if (lowercase < neededLowerCase) {
+                    Slog.w(TAG, "resetPassword: number of lowercase letters " + lowercase
+                            + " does not meet required number of lowercase letters "
+                            + neededLowerCase);
+                    return false;
+                }
+                int neededUpperCase = getPasswordMinimumUpperCase(null);
+                if (uppercase < neededUpperCase) {
+                    Slog.w(TAG, "resetPassword: number of uppercase letters " + uppercase
+                            + " does not meet required number of uppercase letters "
+                            + neededUpperCase);
+                    return false;
+                }
+                int neededSymbols = getPasswordMinimumSymbols(null);
+                if (symbols < neededSymbols) {
+                    Slog.w(TAG, "resetPassword: number of special symbols " + symbols
+                            + " does not meet required number of special symbols " + neededSymbols);
+                    return false;
+                }
+                int neededNonLetter = getPasswordMinimumNonLetter(null);
+                if (nonletter < neededNonLetter) {
+                    Slog.w(TAG, "resetPassword: number of non-letter characters " + nonletter
+                            + " does not meet required number of non-letter characters "
+                            + neededNonLetter);
+                    return false;
+                }
+            }
+
+            LockPatternUtils utils = new LockPatternUtils(mContext);
+            if(utils.checkPasswordHistory(password)) {
+                Slog.w(TAG, "resetPassword: password is the same as one of the last "
+                        + getPasswordHistoryLength(null) + " passwords");
+                return false;
+            }
         }
-        
+
         int callingUid = Binder.getCallingUid();
         if (mPasswordOwner >= 0 && mPasswordOwner != callingUid) {
             Slog.w(TAG, "resetPassword: already set by another uid and not entered by user");
             return false;
         }
-        
+
         // Don't do this with the lock held, because it is going to call
         // back in to the service.
         long ident = Binder.clearCallingIdentity();
@@ -791,10 +1274,10 @@
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
-        
+
         return true;
     }
-    
+
     public void setMaximumTimeToLock(ComponentName who, long timeMs) {
         synchronized (this) {
             if (who == null) {
@@ -804,16 +1287,16 @@
                     DeviceAdminInfo.USES_POLICY_FORCE_LOCK);
             if (ap.maximumTimeToUnlock != timeMs) {
                 ap.maximumTimeToUnlock = timeMs;
-                
+
                 long ident = Binder.clearCallingIdentity();
                 try {
                     saveSettingsLocked();
-                    
+
                     timeMs = getMaximumTimeToLock(null);
                     if (timeMs <= 0) {
                         timeMs = Integer.MAX_VALUE;
                     }
-                    
+
                     try {
                         getIPowerManager().setMaximumScreenOffTimeount((int)timeMs);
                     } catch (RemoteException e) {
@@ -825,16 +1308,16 @@
             }
         }
     }
-    
+
     public long getMaximumTimeToLock(ComponentName who) {
         synchronized (this) {
             long time = 0;
-            
+
             if (who != null) {
                 ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
                 return admin != null ? admin.maximumTimeToUnlock : time;
             }
-            
+
             final int N = mAdminList.size();
             for  (int i=0; i<N; i++) {
                 ActiveAdmin admin = mAdminList.get(i);
@@ -848,7 +1331,7 @@
             return time;
         }
     }
-    
+
     public void lockNow() {
         synchronized (this) {
             // This API can only be called by an active device admin,
@@ -865,7 +1348,7 @@
             }
         }
     }
-    
+
     void wipeDataLocked(int flags) {
         if ((flags&DevicePolicyManager.WIPE_EXTERNAL_STORAGE) != 0) {
             Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
@@ -880,7 +1363,7 @@
             }
         }
     }
-    
+
     public void wipeData(int flags) {
         synchronized (this) {
             // This API can only be called by an active device admin,
@@ -895,11 +1378,11 @@
             }
         }
     }
-    
+
     public void getRemoveWarning(ComponentName comp, final RemoteCallback result) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-        
+
         synchronized (this) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(comp);
             if (admin == null) {
@@ -922,20 +1405,30 @@
             }, null, Activity.RESULT_OK, null, null);
         }
     }
-    
-    public void setActivePasswordState(int quality, int length) {
+
+    public void setActivePasswordState(int quality, int length, int letters, int uppercase,
+            int lowercase, int numbers, int symbols, int nonletter) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-        
+
         validateQualityConstant(quality);
-        
+
         synchronized (this) {
             if (mActivePasswordQuality != quality || mActivePasswordLength != length
-                    || mFailedPasswordAttempts != 0) {
+                    || mFailedPasswordAttempts != 0 || mActivePasswordLetters != letters
+                    || mActivePasswordUpperCase != uppercase
+                    || mActivePasswordLowerCase != lowercase || mActivePasswordNumeric != numbers
+                    || mActivePasswordSymbols != symbols || mActivePasswordNonLetter != nonletter) {
                 long ident = Binder.clearCallingIdentity();
                 try {
                     mActivePasswordQuality = quality;
                     mActivePasswordLength = length;
+                    mActivePasswordLetters = letters;
+                    mActivePasswordLowerCase = lowercase;
+                    mActivePasswordUpperCase = uppercase;
+                    mActivePasswordNumeric = numbers;
+                    mActivePasswordSymbols = symbols;
+                    mActivePasswordNonLetter = nonletter;
                     mFailedPasswordAttempts = 0;
                     saveSettingsLocked();
                     sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
@@ -946,11 +1439,11 @@
             }
         }
     }
-    
+
     public void reportFailedPasswordAttempt() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-        
+
         synchronized (this) {
             long ident = Binder.clearCallingIdentity();
             try {
@@ -967,11 +1460,11 @@
             }
         }
     }
-    
+
     public void reportSuccessfulPasswordAttempt() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
-        
+
         synchronized (this) {
             if (mFailedPasswordAttempts != 0 || mPasswordOwner >= 0) {
                 long ident = Binder.clearCallingIdentity();
@@ -987,7 +1480,95 @@
             }
         }
     }
-    
+
+    public ComponentName setGlobalProxy(ComponentName who, String proxySpec,
+            String exclusionList) {
+        synchronized(this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+
+            ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY);
+
+            // Scan through active admins and find if anyone has already
+            // set the global proxy.
+            final int N = mAdminList.size();
+            Set<ComponentName> compSet = mAdminMap.keySet();
+            for  (ComponentName component : compSet) {
+                ActiveAdmin ap = mAdminMap.get(component);
+                if ((ap.specifiesGlobalProxy) && (!component.equals(who))) {
+                    // Another admin already sets the global proxy
+                    // Return it to the caller.
+                    return component;
+                }
+            }
+            if (proxySpec == null) {
+                admin.specifiesGlobalProxy = false;
+                admin.globalProxySpec = null;
+                admin.globalProxyExclusionList = null;
+            } else {
+
+                admin.specifiesGlobalProxy = true;
+                admin.globalProxySpec = proxySpec;
+                admin.globalProxyExclusionList = exclusionList;
+            }
+
+            // Reset the global proxy accordingly
+            // Do this using system permissions, as apps cannot write to secure settings
+            long origId = Binder.clearCallingIdentity();
+            resetGlobalProxy();
+            Binder.restoreCallingIdentity(origId);
+            return null;
+        }
+    }
+
+    public ComponentName getGlobalProxyAdmin() {
+        synchronized(this) {
+            // Scan through active admins and find if anyone has already
+            // set the global proxy.
+            final int N = mAdminList.size();
+            for (int i = 0; i < N; i++) {
+                ActiveAdmin ap = mAdminList.get(i);
+                if (ap.specifiesGlobalProxy) {
+                    // Device admin sets the global proxy
+                    // Return it to the caller.
+                    return ap.info.getComponent();
+                }
+            }
+        }
+        // No device admin sets the global proxy.
+        return null;
+    }
+
+    private void resetGlobalProxy() {
+        final int N = mAdminList.size();
+        for (int i = 0; i < N; i++) {
+            ActiveAdmin ap = mAdminList.get(i);
+            if (ap.specifiesGlobalProxy) {
+                saveGlobalProxy(ap.globalProxySpec, ap.globalProxyExclusionList);
+                return;
+            }
+        }
+        // No device admins defining global proxies - reset global proxy settings to none
+        saveGlobalProxy(null, null);
+    }
+
+    private void saveGlobalProxy(String proxySpec, String exclusionList) {
+        if (exclusionList == null) {
+            exclusionList = "";
+        }
+        if (proxySpec == null) {
+            proxySpec = "";
+        }
+        // Remove white spaces
+        proxySpec = proxySpec.trim();
+        exclusionList = exclusionList.trim();
+        ContentResolver res = mContext.getContentResolver();
+        Settings.Secure.putString(res, Settings.Secure.HTTP_PROXY, proxySpec);
+        Settings.Secure.putString(res, Settings.Secure.HTTP_PROXY_EXCLUSION_LIST, exclusionList);
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -998,12 +1579,12 @@
                     + ", uid=" + Binder.getCallingUid());
             return;
         }
-        
+
         final Printer p = new PrintWriterPrinter(pw);
-        
+
         synchronized (this) {
             p.println("Current Device Policy Manager state:");
-            
+
             p.println("  Enabled Device Admins:");
             final int N = mAdminList.size();
             for (int i=0; i<N; i++) {
@@ -1014,11 +1595,17 @@
                     ap.dump("    ", pw);
                 }
             }
-            
+
             pw.println(" ");
             pw.print("  mActivePasswordQuality=0x");
                     pw.println(Integer.toHexString(mActivePasswordQuality));
             pw.print("  mActivePasswordLength="); pw.println(mActivePasswordLength);
+            pw.print("  mActivePasswordUpperCase="); pw.println(mActivePasswordUpperCase);
+            pw.print("  mActivePasswordLowerCase="); pw.println(mActivePasswordLowerCase);
+            pw.print("  mActivePasswordLetters="); pw.println(mActivePasswordLetters);
+            pw.print("  mActivePasswordNumeric="); pw.println(mActivePasswordNumeric);
+            pw.print("  mActivePasswordSymbols="); pw.println(mActivePasswordSymbols);
+            pw.print("  mActivePasswordNonLetter="); pw.println(mActivePasswordNonLetter);
             pw.print("  mFailedPasswordAttempts="); pw.println(mFailedPasswordAttempts);
             pw.print("  mPasswordOwner="); pw.println(mPasswordOwner);
         }
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index bee8872..dea9007 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -102,8 +102,8 @@
         try {
             FileReader file = new FileReader(DOCK_STATE_PATH);
             int len = file.read(buffer, 0, 1024);
+            file.close();
             mPreviousDockState = mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
-
         } catch (FileNotFoundException e) {
             Slog.w(TAG, "This kernel does not have dock station support");
         } catch (Exception e) {
@@ -158,13 +158,17 @@
                         {
                             String whichSound = null;
                             if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
-                                if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+                                if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+                                    (mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+                                    (mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
                                     whichSound = Settings.System.DESK_UNDOCK_SOUND;
                                 } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
                                     whichSound = Settings.System.CAR_UNDOCK_SOUND;
                                 }
                             } else {
-                                if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) {
+                                if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
+                                    (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
+                                    (mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
                                     whichSound = Settings.System.DESK_DOCK_SOUND;
                                 } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
                                     whichSound = Settings.System.CAR_DOCK_SOUND;
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
deleted file mode 100644
index 6f0a91d..0000000
--- a/services/java/com/android/server/HeadsetObserver.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.app.ActivityManagerNative;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.UEventObserver;
-import android.util.Slog;
-import android.media.AudioManager;
-
-import java.io.FileReader;
-import java.io.FileNotFoundException;
-
-/**
- * <p>HeadsetObserver monitors for a wired headset.
- */
-class HeadsetObserver extends UEventObserver {
-    private static final String TAG = HeadsetObserver.class.getSimpleName();
-    private static final boolean LOG = true;
-
-    private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w";
-    private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state";
-    private static final String HEADSET_NAME_PATH = "/sys/class/switch/h2w/name";
-
-    private static final int BIT_HEADSET = (1 << 0);
-    private static final int BIT_HEADSET_NO_MIC = (1 << 1);
-    private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC);
-    private static final int HEADSETS_WITH_MIC = BIT_HEADSET;
-
-    private int mHeadsetState;
-    private int mPrevHeadsetState;
-    private String mHeadsetName;
-
-    private final Context mContext;
-    private final WakeLock mWakeLock;  // held while there is a pending route change
-
-    public HeadsetObserver(Context context) {
-        mContext = context;
-        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetObserver");
-        mWakeLock.setReferenceCounted(false);
-
-        startObserving(HEADSET_UEVENT_MATCH);
-
-        init();  // set initial status
-    }
-
-    @Override
-    public void onUEvent(UEventObserver.UEvent event) {
-        if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
-
-        try {
-            update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE")));
-        } catch (NumberFormatException e) {
-            Slog.e(TAG, "Could not parse switch state from event " + event);
-        }
-    }
-
-    private synchronized final void init() {
-        char[] buffer = new char[1024];
-
-        String newName = mHeadsetName;
-        int newState = mHeadsetState;
-        mPrevHeadsetState = mHeadsetState;
-        try {
-            FileReader file = new FileReader(HEADSET_STATE_PATH);
-            int len = file.read(buffer, 0, 1024);
-            newState = Integer.valueOf((new String(buffer, 0, len)).trim());
-
-            file = new FileReader(HEADSET_NAME_PATH);
-            len = file.read(buffer, 0, 1024);
-            newName = new String(buffer, 0, len).trim();
-
-        } catch (FileNotFoundException e) {
-            Slog.w(TAG, "This kernel does not have wired headset support");
-        } catch (Exception e) {
-            Slog.e(TAG, "" , e);
-        }
-
-        update(newName, newState);
-    }
-
-    private synchronized final void update(String newName, int newState) {
-        // Retain only relevant bits
-        int headsetState = newState & SUPPORTED_HEADSETS;
-        int newOrOld = headsetState | mHeadsetState;
-        int delay = 0;
-        // reject all suspect transitions: only accept state changes from:
-        // - a: 0 heaset to 1 headset
-        // - b: 1 headset to 0 headset
-        if (mHeadsetState == headsetState || ((newOrOld & (newOrOld - 1)) != 0)) {
-            return;
-        }
-
-        mHeadsetName = newName;
-        mPrevHeadsetState = mHeadsetState;
-        mHeadsetState = headsetState;
-
-        if (headsetState == 0) {
-            Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
-            mContext.sendBroadcast(intent);
-            // It can take hundreds of ms flush the audio pipeline after
-            // apps pause audio playback, but audio route changes are
-            // immediate, so delay the route change by 1000ms.
-            // This could be improved once the audio sub-system provides an
-            // interface to clear the audio pipeline.
-            delay = 1000;
-        } else {
-            // Insert the same delay for headset connection so that the connection event is not
-            // broadcast before the disconnection event in case of fast removal/insertion
-            if (mHandler.hasMessages(0)) {
-                delay = 1000;
-            }
-        }
-        mWakeLock.acquire();
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(0,
-                                                           mHeadsetState,
-                                                           mPrevHeadsetState,
-                                                           mHeadsetName),
-                                    delay);
-    }
-
-    private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) {
-        int allHeadsets = SUPPORTED_HEADSETS;
-        for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
-            if ((curHeadset & allHeadsets) != 0) {
-                sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName);
-                allHeadsets &= ~curHeadset;
-            }
-        }
-    }
-
-    private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
-        if ((headsetState & headset) != (prevHeadsetState & headset)) {
-            //  Pack up the values and broadcast them to everyone
-            Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-            int state = 0;
-            int microphone = 0;
-
-            if ((headset & HEADSETS_WITH_MIC) != 0) {
-                microphone = 1;
-            }
-            if ((headsetState & headset) != 0) {
-                state = 1;
-            }
-            intent.putExtra("state", state);
-            intent.putExtra("name", headsetName);
-            intent.putExtra("microphone", microphone);
-
-            if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone);
-            // TODO: Should we require a permission?
-            ActivityManagerNative.broadcastStickyIntent(intent, null);
-        }
-    }
-
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            sendIntents(msg.arg1, msg.arg2, (String)msg.obj);
-            mWakeLock.release();
-        }
-    };
-}
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index df41264..13be984 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -79,6 +79,8 @@
     private static native InputDevice nativeGetInputDevice(int deviceId);
     private static native void nativeGetInputConfiguration(Configuration configuration);
     private static native int[] nativeGetInputDeviceIds();
+    private static native boolean nativeTransferTouchFocus(InputChannel fromChannel,
+            InputChannel toChannel);
     private static native String nativeDump();
     
     // Input event injection constants defined in InputDispatcher.h.
@@ -326,6 +328,29 @@
         nativeSetInputDispatchMode(enabled, frozen);
     }
     
+    /**
+     * Atomically transfers touch focus from one window to another as identified by
+     * their input channels.  It is possible for multiple windows to have
+     * touch focus if they support split touch dispatch
+     * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
+     * method only transfers touch focus of the specified window without affecting
+     * other windows that may also have touch focus at the same time.
+     * @param fromChannel The channel of a window that currently has touch focus.
+     * @param toChannel The channel of the window that should receive touch focus in
+     * place of the first.
+     * @return True if the transfer was successful.  False if the window with the
+     * specified channel did not actually have touch focus at the time of the request.
+     */
+    public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
+        if (fromChannel == null) {
+            throw new IllegalArgumentException("fromChannel must not be null.");
+        }
+        if (toChannel == null) {
+            throw new IllegalArgumentException("toChannel must not be null.");
+        }
+        return nativeTransferTouchFocus(fromChannel, toChannel);
+    }
+    
     public void dump(PrintWriter pw) {
         String dumpStr = nativeDump();
         if (dumpStr != null) {
@@ -379,17 +404,18 @@
         }
         
         @SuppressWarnings("unused")
-        public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
-                int policyFlags, boolean isScreenOn) {
+        public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
+                int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {
             return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(
-                    whenNanos, keyCode, down, policyFlags, isScreenOn);
+                    whenNanos, action, flags, keyCode, scanCode, policyFlags, isScreenOn);
         }
         
         @SuppressWarnings("unused")
         public boolean interceptKeyBeforeDispatching(InputChannel focus, int action,
-                int flags, int keyCode, int metaState, int repeatCount, int policyFlags) {
+                int flags, int keyCode, int scanCode, int metaState, int repeatCount,
+                int policyFlags) {
             return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus,
-                    action, flags, keyCode, metaState, repeatCount, policyFlags);
+                    action, flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
         }
         
         @SuppressWarnings("unused")
@@ -467,7 +493,9 @@
                     CALIBRATION_DIR_PATH + deviceName + ".idc");
             if (calibrationFile.exists()) {
                 try {
-                    properties.load(new FileInputStream(calibrationFile));
+                    FileInputStream fis = new FileInputStream(calibrationFile);
+                    properties.load(fis);
+                    fis.close();
                 } catch (IOException ex) {
                     Slog.w(TAG, "Error reading input device calibration properties for device "
                             + deviceName + " from " + calibrationFile + ".", ex);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index ecad3cc..d035eb5 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -61,18 +61,21 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
+import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
 import android.util.EventLog;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.view.IWindowManager;
 import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodSubtype;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -80,6 +83,7 @@
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
@@ -93,6 +97,8 @@
     static final String TAG = "InputManagerService";
 
     static final int MSG_SHOW_IM_PICKER = 1;
+    static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2;
+    static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 3;
 
     static final int MSG_UNBIND_INPUT = 1000;
     static final int MSG_BIND_INPUT = 1010;
@@ -109,8 +115,13 @@
 
     static final long TIME_TO_RECONNECT = 10*1000;
 
+    private static final int NOT_A_SUBTYPE_ID = -1;
+    // If IME doesn't support the system locale, the default subtype will be the first defined one.
+    private static final int DEFAULT_SUBTYPE_ID = 0;
+
     final Context mContext;
     final Handler mHandler;
+    final InputMethodSettings mSettings;
     final SettingsObserver mSettingsObserver;
     final StatusBarManagerService mStatusBar;
     final IWindowManager mIWindowManager;
@@ -120,13 +131,8 @@
 
     // All known input methods.  mMethodMap also serves as the global
     // lock for this class.
-    final ArrayList<InputMethodInfo> mMethodList
-            = new ArrayList<InputMethodInfo>();
-    final HashMap<String, InputMethodInfo> mMethodMap
-            = new HashMap<String, InputMethodInfo>();
-
-    final TextUtils.SimpleStringSplitter mStringColonSplitter
-            = new TextUtils.SimpleStringSplitter(':');
+    final ArrayList<InputMethodInfo> mMethodList = new ArrayList<InputMethodInfo>();
+    final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<String, InputMethodInfo>();
 
     class SessionState {
         final ClientState client;
@@ -224,6 +230,12 @@
     String mCurId;
 
     /**
+     * The current subtype of the current input method.
+     */
+    private InputMethodSubtype mCurrentSubtype;
+
+
+    /**
      * Set to true if our ServiceConnection is currently actively bound to
      * a service (whether or not we have gotten its IBinder back yet).
      */
@@ -292,6 +304,7 @@
     AlertDialog mSwitchingDialog;
     InputMethodInfo[] mIms;
     CharSequence[] mItems;
+    int[] mSubtypeIds;
 
     class SettingsObserver extends ContentObserver {
         SettingsObserver(Handler handler) {
@@ -299,6 +312,8 @@
             ContentResolver resolver = mContext.getContentResolver();
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
         }
 
         @Override public void onChange(boolean selfChange) {
@@ -351,9 +366,9 @@
                                     if (!doit) {
                                         return true;
                                     }
-                                    
                                     Settings.Secure.putString(mContext.getContentResolver(),
                                             Settings.Secure.DEFAULT_INPUT_METHOD, "");
+                                    resetSelectedInputMethodSubtype();
                                     chooseNewDefaultIMELocked();
                                     return true;
                                 }
@@ -409,16 +424,15 @@
                             if (!chooseNewDefaultIMELocked()) {
                                 changed = true;
                                 curIm = null;
-                                curInputMethodId = "";
                                 Slog.i(TAG, "Unsetting current input method");
                                 Settings.Secure.putString(mContext.getContentResolver(),
-                                        Settings.Secure.DEFAULT_INPUT_METHOD,
-                                        curInputMethodId);
+                                        Settings.Secure.DEFAULT_INPUT_METHOD, "");
+                                resetSelectedInputMethodSubtype();
                             }
                         }
                     }
                 }
-                
+
                 if (curIm == null) {
                     // We currently don't have a default input method... is
                     // one now available?
@@ -469,27 +483,18 @@
         mStatusBar = statusBar;
         statusBar.setIconVisibility("ime", false);
 
+        // mSettings should be created before buildInputMethodListLocked
+        mSettings = new InputMethodSettings(context.getContentResolver(), mMethodMap, mMethodList);
         buildInputMethodListLocked(mMethodList, mMethodMap);
+        mSettings.enableAllIMEsIfThereIsNoEnabledIME();
 
-        final String enabledStr = Settings.Secure.getString(
-                mContext.getContentResolver(),
-                Settings.Secure.ENABLED_INPUT_METHODS);
-        Slog.i(TAG, "Enabled input methods: " + enabledStr);
-        final String defaultIme = Settings.Secure.getString(mContext
-                .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
-        if (enabledStr == null || TextUtils.isEmpty(defaultIme)) {
-            Slog.i(TAG, "Enabled input methods or default IME has not been set, enabling all");
+        if (TextUtils.isEmpty(Settings.Secure.getString(
+                mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD))) {
             InputMethodInfo defIm = null;
-            StringBuilder sb = new StringBuilder(256);
-            final int N = mMethodList.size();
-            for (int i=0; i<N; i++) {
-                InputMethodInfo imi = mMethodList.get(i);
-                Slog.i(TAG, "Adding: " + imi.getId());
-                if (i > 0) sb.append(':');
-                sb.append(imi.getId());
+            for (InputMethodInfo imi: mMethodList) {
                 if (defIm == null && imi.getIsDefaultResourceId() != 0) {
                     try {
-                        Resources res = mContext.createPackageContext(
+                        Resources res = context.createPackageContext(
                                 imi.getPackageName(), 0).getResources();
                         if (res.getBoolean(imi.getIsDefaultResourceId())) {
                             defIm = imi;
@@ -500,15 +505,14 @@
                     }
                 }
             }
-            if (defIm == null && N > 0) {
+            if (defIm == null && mMethodList.size() > 0) {
                 defIm = mMethodList.get(0);
                 Slog.i(TAG, "No default found, using " + defIm.getId());
             }
-            Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.ENABLED_INPUT_METHODS, sb.toString());
             if (defIm != null) {
                 Settings.Secure.putString(mContext.getContentResolver(),
                         Settings.Secure.DEFAULT_INPUT_METHOD, defIm.getId());
+                putSelectedInputMethodSubtype(defIm, NOT_A_SUBTYPE_ID);
             }
         }
 
@@ -552,31 +556,10 @@
 
     public List<InputMethodInfo> getEnabledInputMethodList() {
         synchronized (mMethodMap) {
-            return getEnabledInputMethodListLocked();
+            return mSettings.getEnabledInputMethodListLocked();
         }
     }
 
-    List<InputMethodInfo> getEnabledInputMethodListLocked() {
-        final ArrayList<InputMethodInfo> res = new ArrayList<InputMethodInfo>();
-
-        final String enabledStr = Settings.Secure.getString(
-                mContext.getContentResolver(),
-                Settings.Secure.ENABLED_INPUT_METHODS);
-        if (enabledStr != null) {
-            final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
-            splitter.setString(enabledStr);
-
-            while (splitter.hasNext()) {
-                InputMethodInfo info = mMethodMap.get(splitter.next());
-                if (info != null) {
-                    res.add(info);
-                }
-            }
-        }
-
-        return res;
-    }
-
     public void addClient(IInputMethodClient client,
             IInputContext inputContext, int uid, int pid) {
         synchronized (mMethodMap) {
@@ -959,16 +942,33 @@
         }
     }
 
+    public void setIMEButtonVisible(IBinder token, boolean visible) {
+        int uid = Binder.getCallingUid();
+        long ident = Binder.clearCallingIdentity();
+        try {
+            if (token == null || mCurToken != token) {
+                Slog.w(TAG, "Ignoring setIMEButtonVisible of uid " + uid + " token: " + token);
+                return;
+            }
+
+            synchronized (mMethodMap) {
+                mStatusBar.setIMEButtonVisible(visible);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     void updateFromSettingsLocked() {
         // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
         // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
         // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
         // enabled.
         String id = Settings.Secure.getString(mContext.getContentResolver(),
-            Settings.Secure.DEFAULT_INPUT_METHOD);
+                Settings.Secure.DEFAULT_INPUT_METHOD);
         if (id != null && id.length() > 0) {
             try {
-                setInputMethodLocked(id);
+                setInputMethodLocked(id, getSelectedInputMethodSubtypeId(id));
             } catch (IllegalArgumentException e) {
                 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
                 mCurMethodId = null;
@@ -981,21 +981,46 @@
         }
     }
 
-    void setInputMethodLocked(String id) {
+    /* package */ void setInputMethodLocked(String id, int subtypeId) {
         InputMethodInfo info = mMethodMap.get(id);
         if (info == null) {
             throw new IllegalArgumentException("Unknown id: " + id);
         }
 
         if (id.equals(mCurMethodId)) {
+            if (subtypeId != NOT_A_SUBTYPE_ID) {
+                InputMethodSubtype subtype = info.getSubtypes().get(subtypeId);
+                if (subtype != mCurrentSubtype) {
+                    synchronized (mMethodMap) {
+                        if (mCurMethod != null) {
+                            try {
+                                putSelectedInputMethodSubtype(info, subtypeId);
+                                if (mInputShown) {
+                                    // If mInputShown is false, there is no IME button on the
+                                    // system bar.
+                                    // Thus there is no need to make it invisible explicitly.
+                                    mStatusBar.setIMEButtonVisible(true);
+                                }
+                                mCurMethod.changeInputMethodSubtype(subtype);
+                            } catch (RemoteException e) {
+                                return;
+                            }
+                        }
+                    }
+                }
+            }
             return;
         }
 
         final long ident = Binder.clearCallingIdentity();
         try {
             mCurMethodId = id;
+            // Set a subtype to this input method.
+            // subtypeId the name of a subtype which will be set.
+            putSelectedInputMethodSubtype(info, subtypeId);
+
             Settings.Secure.putString(mContext.getContentResolver(),
-                Settings.Secure.DEFAULT_INPUT_METHOD, id);
+                    Settings.Secure.DEFAULT_INPUT_METHOD, id);
 
             if (ActivityManagerNative.isSystemReady()) {
                 Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
@@ -1223,7 +1248,7 @@
         synchronized (mMethodMap) {
             if (mCurClient == null || client == null
                     || mCurClient.client.asBinder() != client.asBinder()) {
-                Slog.w(TAG, "Ignoring showInputMethodDialogFromClient of uid "
+                Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid "
                         + Binder.getCallingUid() + ": " + client);
             }
 
@@ -1231,7 +1256,35 @@
         }
     }
 
+    public void showInputMethodSubtypePickerFromClient(IInputMethodClient client) {
+        synchronized (mMethodMap) {
+            if (mCurClient == null || client == null
+                    || mCurClient.client.asBinder() != client.asBinder()) {
+                Slog.w(TAG, "Ignoring showInputMethodSubtypePickerFromClient of: " + client);
+            }
+
+            mHandler.sendEmptyMessage(MSG_SHOW_IM_SUBTYPE_PICKER);
+        }
+    }
+
+    public void showInputMethodAndSubtypeEnablerFromClient(
+            IInputMethodClient client, String topId) {
+        // TODO: Handle topId for setting the top position of the list activity
+        synchronized (mMethodMap) {
+            if (mCurClient == null || client == null
+                    || mCurClient.client.asBinder() != client.asBinder()) {
+                Slog.w(TAG, "Ignoring showInputMethodAndSubtypeEnablerFromClient of: " + client);
+            }
+
+            mHandler.sendEmptyMessage(MSG_SHOW_IM_SUBTYPE_ENABLER);
+        }
+    }
+
     public void setInputMethod(IBinder token, String id) {
+        setInputMethodWithSubtype(token, id, NOT_A_SUBTYPE_ID);
+    }
+
+    private void setInputMethodWithSubtype(IBinder token, String id, int subtypeId) {
         synchronized (mMethodMap) {
             if (token == null) {
                 if (mContext.checkCallingOrSelfPermission(
@@ -1249,7 +1302,7 @@
 
             long ident = Binder.clearCallingIdentity();
             try {
-                setInputMethodLocked(id);
+                setInputMethodLocked(id, subtypeId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -1315,6 +1368,14 @@
                 showInputMethodMenu();
                 return true;
 
+            case MSG_SHOW_IM_SUBTYPE_PICKER:
+                showInputMethodSubtypeMenu();
+                return true;
+
+            case MSG_SHOW_IM_SUBTYPE_ENABLER:
+                showInputMethodAndSubtypeEnabler();
+                return true;
+
             // ---------------------------------------------------------
 
             case MSG_UNBIND_INPUT:
@@ -1414,7 +1475,7 @@
     }
 
     private boolean chooseNewDefaultIMELocked() {
-        List<InputMethodInfo> enabled = getEnabledInputMethodListLocked();
+        List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
         if (enabled != null && enabled.size() > 0) {
             // We'd prefer to fall back on a system IME, since that is safer.
             int i=enabled.size();
@@ -1425,9 +1486,10 @@
                     break;
                 }
             }
+            InputMethodInfo imi = enabled.get(i);
             Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.DEFAULT_INPUT_METHOD,
-                    enabled.get(i).getId());
+                    Settings.Secure.DEFAULT_INPUT_METHOD, imi.getId());
+            putSelectedInputMethodSubtype(imi, NOT_A_SUBTYPE_ID);
             return true;
         }
 
@@ -1498,7 +1560,23 @@
 
     // ----------------------------------------------------------------------
 
-    void showInputMethodMenu() {
+    private void showInputMethodMenu() {
+        showInputMethodMenuInternal(false);
+    }
+
+    private void showInputMethodSubtypeMenu() {
+        showInputMethodMenuInternal(true);
+    }
+
+    private void showInputMethodAndSubtypeEnabler() {
+        Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_AND_SUBTYPE_ENABLER);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        mContext.startActivity(intent);
+    }
+
+    private void showInputMethodMenuInternal(boolean showSubtypes) {
         if (DEBUG) Slog.v(TAG, "Show switching menu");
 
         final Context context = mContext;
@@ -1507,48 +1585,88 @@
 
         String lastInputMethodId = Settings.Secure.getString(context
                 .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+        int lastInputMethodSubtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
         if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
 
-        final List<InputMethodInfo> immis = getEnabledInputMethodList();
-
-        if (immis == null) {
-            return;
-        }
-        
         synchronized (mMethodMap) {
+            final List<Pair<InputMethodInfo, ArrayList<String>>> immis =
+                    mSettings.getEnabledInputMethodAndSubtypeListLocked();
+            ArrayList<Integer> subtypeIds = new ArrayList<Integer>();
+
+            if (immis == null || immis.size() == 0) {
+                return;
+            }
+
             hideInputMethodMenuLocked();
 
             int N = immis.size();
 
-            final Map<CharSequence, InputMethodInfo> imMap =
-                new TreeMap<CharSequence, InputMethodInfo>(Collator.getInstance());
+            final Map<CharSequence, Pair<InputMethodInfo, Integer>> imMap =
+                new TreeMap<CharSequence, Pair<InputMethodInfo, Integer>>(Collator.getInstance());
 
             for (int i = 0; i < N; ++i) {
-                InputMethodInfo property = immis.get(i);
+                InputMethodInfo property = immis.get(i).first;
+                final ArrayList<String> enabledSubtypeIds = immis.get(i).second;
+                HashSet<String> enabledSubtypeSet = new HashSet<String>();
+                for (String s : enabledSubtypeIds) {
+                    enabledSubtypeSet.add(s);
+                }
                 if (property == null) {
                     continue;
                 }
-                imMap.put(property.loadLabel(pm), property);
+                ArrayList<InputMethodSubtype> subtypes = property.getSubtypes();
+                CharSequence label = property.loadLabel(pm);
+                if (showSubtypes && enabledSubtypeSet.size() > 0) {
+                    for (int j = 0; j < subtypes.size(); ++j) {
+                        InputMethodSubtype subtype = subtypes.get(j);
+                        if (enabledSubtypeSet.contains(String.valueOf(subtype.hashCode()))) {
+                            CharSequence title;
+                            int nameResId = subtype.getNameResId();
+                            String mode = subtype.getMode();
+                            if (nameResId != 0) {
+                                title = pm.getText(property.getPackageName(), nameResId,
+                                        property.getServiceInfo().applicationInfo);
+                            } else {
+                                CharSequence language = subtype.getLocale();
+                                // TODO: Use more friendly Title and UI
+                                title = label + "," + (mode == null ? "" : mode) + ","
+                                        + (language == null ? "" : language);
+                            }
+                            imMap.put(title, new Pair<InputMethodInfo, Integer>(property, j));
+                        }
+                    }
+                } else {
+                    imMap.put(label,
+                            new Pair<InputMethodInfo, Integer>(property, NOT_A_SUBTYPE_ID));
+                    subtypeIds.add(0);
+                }
             }
 
             N = imMap.size();
             mItems = imMap.keySet().toArray(new CharSequence[N]);
-            mIms = imMap.values().toArray(new InputMethodInfo[N]);
-
+            mIms = new InputMethodInfo[N];
+            mSubtypeIds = new int[N];
             int checkedItem = 0;
             for (int i = 0; i < N; ++i) {
+                Pair<InputMethodInfo, Integer> value = imMap.get(mItems[i]);
+                mIms[i] = value.first;
+                mSubtypeIds[i] = value.second;
                 if (mIms[i].getId().equals(lastInputMethodId)) {
-                    checkedItem = i;
-                    break;
+                    int subtypeId = mSubtypeIds[i];
+                    if ((subtypeId == NOT_A_SUBTYPE_ID)
+                            || (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID && subtypeId == 0)
+                            || (subtypeId == lastInputMethodSubtypeId)) {
+                        checkedItem = i;
+                    }
                 }
             }
-    
+
             AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() {
                 public void onClick(DialogInterface dialog, int which) {
                     hideInputMethodMenu();
                 }
             };
-    
+
             TypedArray a = context.obtainStyledAttributes(null,
                     com.android.internal.R.styleable.DialogPreference,
                     com.android.internal.R.attr.alertDialogStyle, 0);
@@ -1562,23 +1680,43 @@
                     .setIcon(a.getDrawable(
                             com.android.internal.R.styleable.DialogPreference_dialogTitle));
             a.recycle();
-    
+
             mDialogBuilder.setSingleChoiceItems(mItems, checkedItem,
                     new AlertDialog.OnClickListener() {
                         public void onClick(DialogInterface dialog, int which) {
                             synchronized (mMethodMap) {
-                                if (mIms == null || mIms.length <= which) {
+                                if (mIms == null || mIms.length <= which
+                                        || mSubtypeIds == null || mSubtypeIds.length <= which) {
                                     return;
                                 }
                                 InputMethodInfo im = mIms[which];
+                                int subtypeId = mSubtypeIds[which];
                                 hideInputMethodMenu();
                                 if (im != null) {
-                                    setInputMethodLocked(im.getId());
+                                    if ((subtypeId < 0)
+                                            || (subtypeId >= im.getSubtypes().size())) {
+                                        subtypeId = NOT_A_SUBTYPE_ID;
+                                    }
+                                    setInputMethodLocked(im.getId(), subtypeId);
                                 }
                             }
                         }
                     });
 
+            if (showSubtypes) {
+                mDialogBuilder.setPositiveButton(com.android.internal.R.string.more_item_label,
+                        new DialogInterface.OnClickListener() {
+                            public void onClick(DialogInterface dialog, int whichButton) {
+                                showInputMethodAndSubtypeEnabler();
+                            }
+                        });
+            }
+            mDialogBuilder.setNegativeButton(com.android.internal.R.string.cancel,
+                    new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int whichButton) {
+                            hideInputMethodMenu();
+                        }
+                    });
             mSwitchingDialog = mDialogBuilder.create();
             mSwitchingDialog.getWindow().setType(
                     WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
@@ -1630,80 +1768,323 @@
         // Make sure this is a valid input method.
         InputMethodInfo imm = mMethodMap.get(id);
         if (imm == null) {
-            if (imm == null) {
-                throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
-            }
+            throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
         }
 
-        StringBuilder builder = new StringBuilder(256);
+        List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
+                .getEnabledInputMethodsAndSubtypeListLocked();
 
-        boolean removed = false;
-        String firstId = null;
+        if (enabled) {
+            for (Pair<String, ArrayList<String>> pair: enabledInputMethodsList) {
+                if (pair.first.equals(id)) {
+                    // We are enabling this input method, but it is already enabled.
+                    // Nothing to do. The previous state was enabled.
+                    return true;
+                }
+            }
+            mSettings.appendAndPutEnabledInputMethodLocked(id, false);
+            // Previous state was disabled.
+            return false;
+        } else {
+            StringBuilder builder = new StringBuilder();
+            if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
+                    builder, enabledInputMethodsList, id)) {
+                // Disabled input method is currently selected, switch to another one.
+                String selId = Settings.Secure.getString(mContext.getContentResolver(),
+                        Settings.Secure.DEFAULT_INPUT_METHOD);
+                if (id.equals(selId)) {
+                    Settings.Secure.putString(
+                            mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD,
+                            enabledInputMethodsList.size() > 0
+                                    ? enabledInputMethodsList.get(0).first : "");
+                    resetSelectedInputMethodSubtype();
+                }
+                // Previous state was enabled.
+                return true;
+            } else {
+                // We are disabling the input method but it is already disabled.
+                // Nothing to do.  The previous state was disabled.
+                return false;
+            }
+        }
+    }
 
-        // Look through the currently enabled input methods.
-        String enabledStr = Settings.Secure.getString(mContext.getContentResolver(),
-                Settings.Secure.ENABLED_INPUT_METHODS);
-        if (enabledStr != null) {
-            final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
-            splitter.setString(enabledStr);
-            while (splitter.hasNext()) {
-                String curId = splitter.next();
-                if (curId.equals(id)) {
-                    if (enabled) {
-                        // We are enabling this input method, but it is
-                        // already enabled.  Nothing to do.  The previous
-                        // state was enabled.
-                        return true;
-                    }
-                    // We are disabling this input method, and it is
-                    // currently enabled.  Skip it to remove from the
-                    // new list.
-                    removed = true;
-                } else if (!enabled) {
-                    // We are building a new list of input methods that
-                    // doesn't contain the given one.
-                    if (firstId == null) firstId = curId;
-                    if (builder.length() > 0) builder.append(':');
-                    builder.append(curId);
+    private void resetSelectedInputMethodSubtype() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, NOT_A_SUBTYPE_ID);
+    }
+
+    private void putSelectedInputMethodSubtype(InputMethodInfo imi, int subtypeId) {
+        final ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+        if (subtypeId >= 0 && subtypeId < subtypes.size()) {
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
+                    subtypes.get(subtypeId).hashCode());
+            mCurrentSubtype = subtypes.get(subtypeId);
+        } else {
+            resetSelectedInputMethodSubtype();
+            mCurrentSubtype = null;
+        }
+    }
+
+    private int getSelectedInputMethodSubtypeId(String id) {
+        InputMethodInfo imi = mMethodMap.get(id);
+        if (imi == null) {
+            return NOT_A_SUBTYPE_ID;
+        }
+        ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+        int subtypeId;
+        try {
+            subtypeId = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE);
+        } catch (SettingNotFoundException e) {
+            return NOT_A_SUBTYPE_ID;
+        }
+        for (int i = 0; i < subtypes.size(); ++i) {
+            InputMethodSubtype ims = subtypes.get(i);
+            if (subtypeId == ims.hashCode()) {
+                return i;
+            }
+        }
+        return NOT_A_SUBTYPE_ID;
+    }
+
+    // If there are no selected subtypes, tries finding the most applicable one according to the
+    // current system locale
+    private int findApplicableSubtype(String id) {
+        InputMethodInfo imi = mMethodMap.get(id);
+        if (imi == null) {
+            return NOT_A_SUBTYPE_ID;
+        }
+        ArrayList<InputMethodSubtype> subtypes = imi.getSubtypes();
+        if (subtypes == null || subtypes.size() == 0) {
+            return NOT_A_SUBTYPE_ID;
+        }
+        final String locale = mContext.getResources().getConfiguration().locale.toString();
+        final String language = locale.substring(0, 2);
+        boolean partialMatchFound = false;
+        int applicableSubtypeId = DEFAULT_SUBTYPE_ID;
+        for (int i = 0; i < subtypes.size(); ++i) {
+            final String subtypeLocale = subtypes.get(i).getLocale();
+            // An applicable subtype should be a keyboard subtype
+            if (subtypes.get(i).getMode().equalsIgnoreCase("keyboard")) {
+                if (locale.equals(subtypeLocale)) {
+                    // Exact match (e.g. system locale is "en_US" and subtype locale is "en_US")
+                    applicableSubtypeId = i;
+                    break;
+                } else if (!partialMatchFound && subtypeLocale.startsWith(language)) {
+                    // Partial match (e.g. system locale is "en_US" and subtype locale is "en")
+                    applicableSubtypeId = i;
+                    partialMatchFound = true;
                 }
             }
         }
 
-        if (!enabled) {
-            if (!removed) {
-                // We are disabling the input method but it is already
-                // disabled.  Nothing to do.  The previous state was
-                // disabled.
-                return false;
+        // The first subtype applicable to the system locale will be defined as the most applicable
+        // subtype.
+        if (DEBUG) {
+            Slog.d(TAG, "Applicable InputMethodSubtype was found: " + applicableSubtypeId
+                    + subtypes.get(applicableSubtypeId).getLocale());
+        }
+        return applicableSubtypeId;
+    }
+
+    /**
+     * @return Return the current subtype of this input method.
+     */
+    public InputMethodSubtype getCurrentInputMethodSubtype() {
+        boolean subtypeIsSelected = false;
+        try {
+            subtypeIsSelected = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE) != NOT_A_SUBTYPE_ID;
+        } catch (SettingNotFoundException e) {
+        }
+        if (!subtypeIsSelected || mCurrentSubtype == null) {
+            String lastInputMethodId = Settings.Secure.getString(mContext
+                    .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+            int subtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId);
+            if (subtypeId == NOT_A_SUBTYPE_ID) {
+                subtypeId = findApplicableSubtype(lastInputMethodId);
             }
-            // Update the setting with the new list of input methods.
-            Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
-            // We the disabled input method is currently selected, switch
-            // to another one.
-            String selId = Settings.Secure.getString(mContext.getContentResolver(),
-                    Settings.Secure.DEFAULT_INPUT_METHOD);
-            if (id.equals(selId)) {
-                Settings.Secure.putString(mContext.getContentResolver(),
-                        Settings.Secure.DEFAULT_INPUT_METHOD,
-                        firstId != null ? firstId : "");
+            if (subtypeId != NOT_A_SUBTYPE_ID) {
+                mCurrentSubtype = mMethodMap.get(lastInputMethodId).getSubtypes().get(subtypeId);
             }
-            // Previous state was enabled.
-            return true;
+        }
+        return mCurrentSubtype;
+    }
+
+    /**
+     * Utility class for putting and getting settings for InputMethod
+     * TODO: Move all putters and getters of settings to this class.
+     */
+    private static class InputMethodSettings {
+        // The string for enabled input method is saved as follows:
+        // example: ("ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0")
+        private static final char INPUT_METHOD_SEPARATER = ':';
+        private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
+        private final TextUtils.SimpleStringSplitter mStringColonSplitter =
+                new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATER);
+
+        private final TextUtils.SimpleStringSplitter mStringSemiColonSplitter =
+                new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER);
+
+        private final ContentResolver mResolver;
+        private final HashMap<String, InputMethodInfo> mMethodMap;
+        private final ArrayList<InputMethodInfo> mMethodList;
+
+        private String mEnabledInputMethodsStrCache;
+
+        private static void buildEnabledInputMethodsSettingString(
+                StringBuilder builder, Pair<String, ArrayList<String>> pair) {
+            String id = pair.first;
+            ArrayList<String> subtypes = pair.second;
+            builder.append(id);
+            // Inputmethod and subtypes are saved in the settings as follows:
+            // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
+            for (String subtypeId: subtypes) {
+                builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
+            }
         }
 
-        // Add in the newly enabled input method.
-        if (enabledStr == null || enabledStr.length() == 0) {
-            enabledStr = id;
-        } else {
-            enabledStr = enabledStr + ':' + id;
+        public InputMethodSettings(
+                ContentResolver resolver, HashMap<String, InputMethodInfo> methodMap,
+                ArrayList<InputMethodInfo> methodList) {
+            mResolver = resolver;
+            mMethodMap = methodMap;
+            mMethodList = methodList;
         }
 
-        Settings.Secure.putString(mContext.getContentResolver(),
-                Settings.Secure.ENABLED_INPUT_METHODS, enabledStr);
+        public List<InputMethodInfo> getEnabledInputMethodListLocked() {
+            return createEnabledInputMethodListLocked(
+                    getEnabledInputMethodsAndSubtypeListLocked());
+        }
 
-        // Previous state was disabled.
-        return false;
+        public List<Pair<InputMethodInfo, ArrayList<String>>>
+                getEnabledInputMethodAndSubtypeListLocked() {
+            return createEnabledInputMethodAndSubtypeListLocked(
+                    getEnabledInputMethodsAndSubtypeListLocked());
+        }
+
+        // At the initial boot, the settings for input methods are not set,
+        // so we need to enable IME in that case.
+        public void enableAllIMEsIfThereIsNoEnabledIME() {
+            if (TextUtils.isEmpty(getEnabledInputMethodsStr())) {
+                StringBuilder sb = new StringBuilder();
+                final int N = mMethodList.size();
+                for (int i = 0; i < N; i++) {
+                    InputMethodInfo imi = mMethodList.get(i);
+                    Slog.i(TAG, "Adding: " + imi.getId());
+                    if (i > 0) sb.append(':');
+                    sb.append(imi.getId());
+                }
+                putEnabledInputMethodsStr(sb.toString());
+            }
+        }
+
+        public List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
+            ArrayList<Pair<String, ArrayList<String>>> imsList
+                    = new ArrayList<Pair<String, ArrayList<String>>>();
+            final String enabledInputMethodsStr = getEnabledInputMethodsStr();
+            if (TextUtils.isEmpty(enabledInputMethodsStr)) {
+                return imsList;
+            }
+            mStringColonSplitter.setString(enabledInputMethodsStr);
+            while (mStringColonSplitter.hasNext()) {
+                String nextImsStr = mStringColonSplitter.next();
+                mStringSemiColonSplitter.setString(nextImsStr);
+                if (mStringSemiColonSplitter.hasNext()) {
+                    ArrayList<String> subtypeHashes = new ArrayList<String>();
+                    // The first element is ime id.
+                    String imeId = mStringSemiColonSplitter.next();
+                    while (mStringSemiColonSplitter.hasNext()) {
+                        subtypeHashes.add(mStringSemiColonSplitter.next());
+                    }
+                    imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
+                }
+            }
+            return imsList;
+        }
+
+        public void appendAndPutEnabledInputMethodLocked(String id, boolean reloadInputMethodStr) {
+            if (reloadInputMethodStr) {
+                getEnabledInputMethodsStr();
+            }
+            if (TextUtils.isEmpty(mEnabledInputMethodsStrCache)) {
+                // Add in the newly enabled input method.
+                putEnabledInputMethodsStr(id);
+            } else {
+                putEnabledInputMethodsStr(
+                        mEnabledInputMethodsStrCache + INPUT_METHOD_SEPARATER + id);
+            }
+        }
+
+        /**
+         * Build and put a string of EnabledInputMethods with removing specified Id.
+         * @return the specified id was removed or not.
+         */
+        public boolean buildAndPutEnabledInputMethodsStrRemovingIdLocked(
+                StringBuilder builder, List<Pair<String, ArrayList<String>>> imsList, String id) {
+            boolean isRemoved = false;
+            boolean needsAppendSeparator = false;
+            for (Pair<String, ArrayList<String>> ims: imsList) {
+                String curId = ims.first;
+                if (curId.equals(id)) {
+                    // We are disabling this input method, and it is
+                    // currently enabled.  Skip it to remove from the
+                    // new list.
+                    isRemoved = true;
+                } else {
+                    if (needsAppendSeparator) {
+                        builder.append(INPUT_METHOD_SEPARATER);
+                    } else {
+                        needsAppendSeparator = true;
+                    }
+                    buildEnabledInputMethodsSettingString(builder, ims);
+                }
+            }
+            if (isRemoved) {
+                // Update the setting with the new list of input methods.
+                putEnabledInputMethodsStr(builder.toString());
+            }
+            return isRemoved;
+        }
+
+        private List<InputMethodInfo> createEnabledInputMethodListLocked(
+                List<Pair<String, ArrayList<String>>> imsList) {
+            final ArrayList<InputMethodInfo> res = new ArrayList<InputMethodInfo>();
+            for (Pair<String, ArrayList<String>> ims: imsList) {
+                InputMethodInfo info = mMethodMap.get(ims.first);
+                if (info != null) {
+                    res.add(info);
+                }
+            }
+            return res;
+        }
+
+        private List<Pair<InputMethodInfo, ArrayList<String>>>
+                createEnabledInputMethodAndSubtypeListLocked(
+                        List<Pair<String, ArrayList<String>>> imsList) {
+            final ArrayList<Pair<InputMethodInfo, ArrayList<String>>> res
+                    = new ArrayList<Pair<InputMethodInfo, ArrayList<String>>>();
+            for (Pair<String, ArrayList<String>> ims : imsList) {
+                InputMethodInfo info = mMethodMap.get(ims.first);
+                if (info != null) {
+                    res.add(new Pair<InputMethodInfo, ArrayList<String>>(info, ims.second));
+                }
+            }
+            return res;
+        }
+
+        private void putEnabledInputMethodsStr(String str) {
+            Settings.Secure.putString(mResolver, Settings.Secure.ENABLED_INPUT_METHODS, str);
+            mEnabledInputMethodsStrCache = str;
+        }
+
+        private String getEnabledInputMethodsStr() {
+            mEnabledInputMethodsStrCache = Settings.Secure.getString(
+                    mResolver, Settings.Secure.ENABLED_INPUT_METHODS);
+            return mEnabledInputMethodsStrCache;
+        }
     }
 
     // ----------------------------------------------------------------------
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 10107c6..656ec4d 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -479,14 +479,17 @@
         mEnabledProviders.add(passiveProvider.getName());
 
         // initialize external network location and geocoder services
-        if (mNetworkLocationProviderPackageName != null) {
+        PackageManager pm = mContext.getPackageManager();
+        if (mNetworkLocationProviderPackageName != null &&
+                pm.resolveService(new Intent(mNetworkLocationProviderPackageName), 0) != null) {
             mNetworkLocationProvider =
                 new LocationProviderProxy(mContext, LocationManager.NETWORK_PROVIDER,
                         mNetworkLocationProviderPackageName, mLocationHandler);
             addProvider(mNetworkLocationProvider);
         }
 
-        if (mGeocodeProviderPackageName != null) {
+        if (mGeocodeProviderPackageName != null &&
+                pm.resolveService(new Intent(mGeocodeProviderPackageName), 0) != null) {
             mGeocodeProvider = new GeocoderProxy(mContext, mGeocodeProviderPackageName);
         }
 
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 85bb3aa..62dec7e 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -151,6 +151,8 @@
     private boolean                               mBooted = false;
     private boolean                               mReady = false;
     private boolean                               mSendUmsConnectedOnBoot = false;
+    // true if we should fake MEDIA_MOUNTED state for external storage
+    private boolean                               mEmulateExternalStorage = false;
 
     /**
      * Private hash of currently mounted secure containers.
@@ -445,7 +447,9 @@
                             String path = Environment.getExternalStorageDirectory().getPath();
                             String state = getVolumeState(path);
 
-                            if (state.equals(Environment.MEDIA_UNMOUNTED)) {
+                            if (mEmulateExternalStorage) {
+                                notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Mounted);
+                            } else if (state.equals(Environment.MEDIA_UNMOUNTED)) {
                                 int rc = doMountVolume(path);
                                 if (rc != StorageResultCode.OperationSucceeded) {
                                     Slog.e(TAG, String.format("Boot-time mount failed (%d)", rc));
@@ -516,21 +520,21 @@
             Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
             return;
         }
+        // Update state on PackageManager, but only of real events
+        if (!mEmulateExternalStorage) {
+            if (Environment.MEDIA_UNMOUNTED.equals(state)) {
+                mPms.updateExternalMediaStatus(false, false);
 
-        if (Environment.MEDIA_UNMOUNTED.equals(state)) {
-            // Tell the package manager the media is gone.
-            mPms.updateExternalMediaStatus(false, false);
-
-            /*
-             * Some OBBs might have been unmounted when this volume was
-             * unmounted, so send a message to the handler to let it know to
-             * remove those from the list of mounted OBBS.
-             */
-            mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
-                    path));
-        } else if (Environment.MEDIA_MOUNTED.equals(state)) {
-            // Tell the package manager the media is available for use.
-            mPms.updateExternalMediaStatus(true, false);
+                /*
+                 * Some OBBs might have been unmounted when this volume was
+                 * unmounted, so send a message to the handler to let it know to
+                 * remove those from the list of mounted OBBS.
+                 */
+                mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
+                        path));
+            } else if (Environment.MEDIA_MOUNTED.equals(state)) {
+                mPms.updateExternalMediaStatus(true, false);
+            }
         }
 
         String oldState = mLegacyState;
@@ -1040,6 +1044,13 @@
     public MountService(Context context) {
         mContext = context;
 
+        mEmulateExternalStorage = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_emulateExternalStorage);
+        if (mEmulateExternalStorage) {
+            Slog.d(TAG, "using emulated external storage");
+            mLegacyState = Environment.MEDIA_MOUNTED;
+        }
+
         // XXX: This will go away soon in favor of IMountServiceObserver
         mPms = (PackageManagerService) ServiceManager.getService("package");
 
@@ -1226,6 +1237,10 @@
         return mLegacyState;
     }
 
+    public boolean isExternalStorageEmulated() {
+        return mEmulateExternalStorage;
+    }
+
     public int mountVolume(String path) {
         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 7b68d68..cf87a9d 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -132,11 +132,12 @@
                                     Slog.e(TAG, String.format(
                                             "Error handling '%s'", event), ex);
                                 }
-                            }
-                            try {
-                                mResponseQueue.put(event);
-                            } catch (InterruptedException ex) {
-                                Slog.e(TAG, "Failed to put response onto queue", ex);
+                            } else {
+                                try {
+                                    mResponseQueue.put(event);
+                                } catch (InterruptedException ex) {
+                                    Slog.e(TAG, "Failed to put response onto queue", ex);
+                                }
                             }
                         } catch (NumberFormatException nfe) {
                             Slog.w(TAG, String.format("Bad msg (%s)", event));
@@ -219,6 +220,7 @@
      */
     public synchronized ArrayList<String> doCommand(String cmd)
             throws NativeDaemonConnectorException  {
+        mResponseQueue.clear();
         sendCommand(cmd);
 
         ArrayList<String> response = new ArrayList<String>();
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index f0acdc0..8dbd3e7 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -226,10 +226,10 @@
                 throw new UnknownHostException(addrString);
             }
 
-            int a = Integer.parseInt(parts[0])      ;
-            int b = Integer.parseInt(parts[1]) <<  8;
-            int c = Integer.parseInt(parts[2]) << 16;
-            int d = Integer.parseInt(parts[3]) << 24;
+            int a = Integer.parseInt(parts[0]) << 24;
+            int b = Integer.parseInt(parts[1]) << 16;
+            int c = Integer.parseInt(parts[2]) <<  8;
+            int d = Integer.parseInt(parts[3])      ;
 
             return a | b | c | d;
         } catch (NumberFormatException ex) {
diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java
new file mode 100644
index 0000000..52f84eb
--- /dev/null
+++ b/services/java/com/android/server/NetworkTimeUpdateService.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import com.android.internal.telephony.TelephonyIntents;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.SntpClient;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * Monitors the network time and updates the system time if it is out of sync
+ * and there hasn't been any NITZ update from the carrier recently.
+ * If looking up the network time fails for some reason, it tries a few times with a short
+ * interval and then resets to checking on longer intervals.
+ * <p>
+ * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
+ * available.
+ * </p>
+ */
+public class NetworkTimeUpdateService {
+
+    private static final String TAG = "NetworkTimeUpdateService";
+    private static final boolean DBG = false;
+
+    private static final int EVENT_AUTO_TIME_CHANGED = 1;
+    private static final int EVENT_POLL_NETWORK_TIME = 2;
+
+    /** Normal polling frequency */
+    private static final long POLLING_INTERVAL_MS = 24L * 60 * 60 * 1000; // 24 hrs
+    /** Try-again polling interval, in case the network request failed */
+    private static final long POLLING_INTERVAL_SHORTER_MS = 60 * 1000L; // 60 seconds
+    /** Number of times to try again */
+    private static final int TRY_AGAIN_TIMES_MAX = 3;
+    /** How long to wait for the NTP server to respond. */
+    private static final int MAX_NTP_FETCH_WAIT_MS = 20 * 1000;
+    /** If the time difference is greater than this threshold, then update the time. */
+    private static final int TIME_ERROR_THRESHOLD_MS = 5 * 1000;
+
+    private static final String ACTION_POLL =
+            "com.android.server.NetworkTimeUpdateService.action.POLL";
+    private static final String PROPERTIES_FILE = "/etc/gps.conf";
+    private static int POLL_REQUEST = 0;
+
+    private static final long NOT_SET = -1;
+    private long mNitzTimeSetTime = NOT_SET;
+    // TODO: Have a way to look up the timezone we are in
+    private long mNitzZoneSetTime = NOT_SET;
+
+    private Context mContext;
+    // NTP lookup is done on this thread and handler
+    private Handler mHandler;
+    private HandlerThread mThread;
+    private AlarmManager mAlarmManager;
+    private PendingIntent mPendingPollIntent;
+    private SettingsObserver mSettingsObserver;
+    // Address of the NTP server
+    private String mNtpServer;
+    // The last time that we successfully fetched the NTP time.
+    private long mLastNtpFetchTime = NOT_SET;
+    // Keeps track of how many quick attempts were made to fetch NTP time.
+    // During bootup, the network may not have been up yet, or it's taking time for the
+    // connection to happen.
+    private int mTryAgainCounter;
+
+    public NetworkTimeUpdateService(Context context) {
+        mContext = context;
+        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        Intent pollIntent = new Intent(ACTION_POLL, null);
+        mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
+    }
+
+    /** Initialize the receivers and initiate the first NTP request */
+    public void systemReady() {
+        mNtpServer = getNtpServerAddress();
+        if (mNtpServer == null) {
+            Slog.e(TAG, "NTP server address not found, not syncing to NTP time");
+            return;
+        }
+
+        registerForTelephonyIntents();
+        registerForAlarms();
+
+        mThread = new HandlerThread(TAG);
+        mThread.start();
+        mHandler = new MyHandler(mThread.getLooper());
+        // Check the network time on the new thread
+        mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+
+        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
+        mSettingsObserver.observe(mContext);
+    }
+
+    private String getNtpServerAddress() {
+        String serverAddress = null;
+        FileInputStream stream = null;
+        try {
+            Properties properties = new Properties();
+            File file = new File(PROPERTIES_FILE);
+            stream = new FileInputStream(file);
+            properties.load(stream);
+            serverAddress = properties.getProperty("NTP_SERVER", null);
+        } catch (IOException e) {
+            Slog.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (Exception e) {}
+            }
+        }
+        return serverAddress;
+    }
+
+    private void registerForTelephonyIntents() {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+        intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
+        mContext.registerReceiver(mNitzReceiver, intentFilter);
+    }
+
+    private void registerForAlarms() {
+        mContext.registerReceiver(
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+                }
+            }, new IntentFilter(ACTION_POLL));
+    }
+
+    private void onPollNetworkTime(int event) {
+        // If Automatic time is not set, don't bother.
+        if (!isAutomaticTimeRequested()) return;
+
+        final long refTime = SystemClock.elapsedRealtime();
+        // If NITZ time was received less than POLLING_INTERVAL_MS time ago,
+        // no need to sync to NTP.
+        if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < POLLING_INTERVAL_MS) {
+            resetAlarm(POLLING_INTERVAL_MS);
+            return;
+        }
+        final long currentTime = System.currentTimeMillis();
+        if (DBG) Log.d(TAG, "System time = " + currentTime);
+        // Get the NTP time
+        if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + POLLING_INTERVAL_MS
+                || event == EVENT_AUTO_TIME_CHANGED) {
+            if (DBG) Log.d(TAG, "Before Ntp fetch");
+            long ntp = getNtpTime();
+            if (DBG) Log.d(TAG, "Ntp = " + ntp);
+            if (ntp > 0) {
+                mTryAgainCounter = 0;
+                mLastNtpFetchTime = SystemClock.elapsedRealtime();
+                if (Math.abs(ntp - currentTime) > TIME_ERROR_THRESHOLD_MS) {
+                    // Set the system time
+                    if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
+                    // Make sure we don't overflow, since it's going to be converted to an int
+                    if (ntp / 1000 < Integer.MAX_VALUE) {
+                        SystemClock.setCurrentTimeMillis(ntp);
+                    }
+                } else {
+                    if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
+                }
+            } else {
+                // Try again shortly
+                mTryAgainCounter++;
+                if (mTryAgainCounter <= TRY_AGAIN_TIMES_MAX) {
+                    resetAlarm(POLLING_INTERVAL_SHORTER_MS);
+                } else {
+                    // Try much later
+                    mTryAgainCounter = 0;
+                    resetAlarm(POLLING_INTERVAL_MS);
+                }
+                return;
+            }
+        }
+        resetAlarm(POLLING_INTERVAL_MS);
+    }
+
+    /**
+     * Cancel old alarm and starts a new one for the specified interval.
+     *
+     * @param interval when to trigger the alarm, starting from now.
+     */
+    private void resetAlarm(long interval) {
+        mAlarmManager.cancel(mPendingPollIntent);
+        long now = SystemClock.elapsedRealtime();
+        long next = now + interval;
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
+    }
+
+    private long getNtpTime() {
+        SntpClient client = new SntpClient();
+        if (client.requestTime(mNtpServer, MAX_NTP_FETCH_WAIT_MS)) {
+            return client.getNtpTime();
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Checks if the user prefers to automatically set the time.
+     */
+    private boolean isAutomaticTimeRequested() {
+        return Settings.System.getInt(mContext.getContentResolver(), Settings.System.AUTO_TIME, 0)
+                != 0;
+    }
+
+    /** Receiver for Nitz time events */
+    private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {
+                mNitzTimeSetTime = SystemClock.elapsedRealtime();
+            } else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {
+                mNitzZoneSetTime = SystemClock.elapsedRealtime();
+            }
+        }
+    };
+
+    /** Handler to do the network accesses on */
+    private class MyHandler extends Handler {
+
+        public MyHandler(Looper l) {
+            super(l);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_AUTO_TIME_CHANGED:
+                case EVENT_POLL_NETWORK_TIME:
+                    onPollNetworkTime(msg.what);
+                    break;
+            }
+        }
+    }
+
+    /** Observer to watch for changes to the AUTO_TIME setting */
+    private static class SettingsObserver extends ContentObserver {
+
+        private int mMsg;
+        private Handler mHandler;
+
+        SettingsObserver(Handler handler, int msg) {
+            super(handler);
+            mHandler = handler;
+            mMsg = msg;
+        }
+
+        void observe(Context context) {
+            ContentResolver resolver = context.getContentResolver();
+            resolver.registerContentObserver(Settings.System.getUriFor(Settings.System.AUTO_TIME),
+                    false, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mHandler.obtainMessage(mMsg).sendToTarget();
+        }
+    }
+}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 4da5eb2..1081a20 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -61,6 +61,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.Toast;
+import android.widget.Toast;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -92,7 +93,6 @@
     private WorkerHandler mHandler;
     private StatusBarManagerService mStatusBar;
     private LightsService mLightsService;
-    private LightsService.Light mBatteryLight;
     private LightsService.Light mNotificationLight;
     private LightsService.Light mAttentionLight;
 
@@ -127,18 +127,8 @@
     private ArrayList<ToastRecord> mToastQueue;
 
     private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
-
-    private boolean mBatteryCharging;
-    private boolean mBatteryLow;
-    private boolean mBatteryFull;
     private NotificationRecord mLedNotification;
 
-    private static int mBatteryLowARGB;
-    private static int mBatteryMediumARGB;
-    private static int mBatteryFullARGB;
-    private static int mBatteryLedOn;
-    private static int mBatteryLedOff;
-
     private static String idDebugString(Context baseContext, String packageName, int id) {
         Context c = null;
 
@@ -285,6 +275,10 @@
                     Notification.FLAG_FOREGROUND_SERVICE);
         }
 
+        public void onNotificationClear(String pkg, String tag, int id) {
+            cancelNotification(pkg, tag, id, 0, 0); // maybe add some flags?
+        }
+
         public void onPanelRevealed() {
             synchronized (mNotificationList) {
                 // sound
@@ -337,22 +331,7 @@
 
             boolean queryRestart = false;
             
-            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
-                boolean batteryCharging = (intent.getIntExtra("plugged", 0) != 0);
-                int level = intent.getIntExtra("level", -1);
-                boolean batteryLow = (level >= 0 && level <= Power.LOW_BATTERY_THRESHOLD);
-                int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN);
-                boolean batteryFull = (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90);
-
-                if (batteryCharging != mBatteryCharging ||
-                        batteryLow != mBatteryLow ||
-                        batteryFull != mBatteryFull) {
-                    mBatteryCharging = batteryCharging;
-                    mBatteryLow = batteryLow;
-                    mBatteryFull = batteryFull;
-                    updateLights();
-                }
-            } else if (action.equals(Usb.ACTION_USB_STATE)) {
+            if (action.equals(Usb.ACTION_USB_STATE)) {
                 Bundle extras = intent.getExtras();
                 boolean usbConnected = extras.getBoolean(Usb.USB_CONNECTED);
                 boolean adbEnabled = (Usb.USB_FUNCTION_ENABLED.equals(
@@ -430,7 +409,6 @@
     {
         super();
         mContext = context;
-        mLightsService = lights;
         mAm = ActivityManagerNative.getDefault();
         mSound = new NotificationPlayer(TAG);
         mSound.setUsesWakeLock(context);
@@ -440,7 +418,6 @@
         mStatusBar = statusBar;
         statusBar.setNotificationCallbacks(mNotificationCallbacks);
 
-        mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY);
         mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS);
         mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION);
 
@@ -452,17 +429,6 @@
         mDefaultNotificationLedOff = resources.getInteger(
                 com.android.internal.R.integer.config_defaultNotificationLedOff);
 
-        mBatteryLowARGB = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_notificationsBatteryLowARGB);
-        mBatteryMediumARGB = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
-        mBatteryFullARGB = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_notificationsBatteryFullARGB);
-        mBatteryLedOn = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_notificationsBatteryLedOn);
-        mBatteryLedOff = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_notificationsBatteryLedOff);
-
         // Don't start allowing notifications until the setup wizard has run once.
         // After that, including subsequent boots, init with notifications turned on.
         // This works on the first boot because the setup wizard will toggle this
@@ -474,7 +440,6 @@
 
         // register for battery changed notifications
         IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
         filter.addAction(Usb.ACTION_USB_STATE);
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -733,10 +698,6 @@
                 throw new IllegalArgumentException("contentView required: pkg=" + pkg
                         + " id=" + id + " notification=" + notification);
             }
-            if (notification.contentIntent == null) {
-                throw new IllegalArgumentException("contentIntent required: pkg=" + pkg
-                        + " id=" + id + " notification=" + notification);
-            }
         }
 
         synchronized (mNotificationList) {
@@ -1075,25 +1036,6 @@
     // lock on mNotificationList
     private void updateLightsLocked()
     {
-        // Battery low always shows, other states only show if charging.
-        if (mBatteryLow) {
-            if (mBatteryCharging) {
-                mBatteryLight.setColor(mBatteryLowARGB);
-            } else {
-                // Flash when battery is low and not charging
-                mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED,
-                        mBatteryLedOn, mBatteryLedOff);
-            }
-        } else if (mBatteryCharging) {
-            if (mBatteryFull) {
-                mBatteryLight.setColor(mBatteryFullARGB);
-            } else {
-                mBatteryLight.setColor(mBatteryMediumARGB);
-            }
-        } else {
-            mBatteryLight.turnOff();
-        }
-
         // clear pending pulse notification if screen is on
         if (mScreenOn || mLedNotification == null) {
             mPendingPulseNotification = false;
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 4520f18..7ad95da 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -1225,6 +1225,7 @@
                 }
 
             }
+            permReader.close();
         } catch (XmlPullParserException e) {
             Slog.w(TAG, "Got execption parsing permissions.", e);
         } catch (IOException e) {
@@ -9672,7 +9673,8 @@
     * Update media status on PackageManager.
     */
    public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
-       if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+       int callingUid = Binder.getCallingUid();
+       if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
            throw new SecurityException("Media status can only be updated by the system");
        }
        synchronized (mPackages) {
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index a6daaef..41cbbf4 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -219,6 +219,8 @@
     private float mLightSensorValue = -1;
     private boolean mProxIgnoredBecauseScreenTurnedOff = false;
     private int mHighestLightSensorValue = -1;
+    private boolean mLightSensorPendingDecrease = false;
+    private boolean mLightSensorPendingIncrease = false;
     private float mLightSensorPendingValue = -1;
     private int mLightSensorScreenBrightness = -1;
     private int mLightSensorButtonBrightness = -1;
@@ -1141,6 +1143,8 @@
             pw.println("  mLightSensorEnabled=" + mLightSensorEnabled);
             pw.println("  mLightSensorValue=" + mLightSensorValue
                     + " mLightSensorPendingValue=" + mLightSensorPendingValue);
+            pw.println("  mLightSensorPendingDecrease=" + mLightSensorPendingDecrease
+                    + " mLightSensorPendingIncrease=" + mLightSensorPendingIncrease);
             pw.println("  mLightSensorScreenBrightness=" + mLightSensorScreenBrightness
                     + " mLightSensorButtonBrightness=" + mLightSensorButtonBrightness
                     + " mLightSensorKeyboardBrightness=" + mLightSensorKeyboardBrightness);
@@ -1609,7 +1613,7 @@
         if (err == 0) {
             mLastScreenOnTime = (on ? SystemClock.elapsedRealtime() : 0);
             if (mUseSoftwareAutoBrightness) {
-                enableLightSensor(on);
+                enableLightSensorLocked(on);
                 if (!on) {
                     // make sure button and key backlights are off too
                     mButtonLight.turnOff();
@@ -1742,6 +1746,8 @@
                 } else {
                     // cancel light sensor task
                     mHandler.removeCallbacks(mAutoBrightnessTask);
+                    mLightSensorPendingDecrease = false;
+                    mLightSensorPendingIncrease = false;
                     mScreenOffTime = SystemClock.elapsedRealtime();
                     long identity = Binder.clearCallingIdentity();
                     try {
@@ -2325,9 +2331,10 @@
     private Runnable mAutoBrightnessTask = new Runnable() {
         public void run() {
             synchronized (mLocks) {
-                int value = (int)mLightSensorPendingValue;
-                if (value >= 0) {
-                    mLightSensorPendingValue = -1;
+                if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
+                    int value = (int)mLightSensorPendingValue;
+                    mLightSensorPendingDecrease = false;
+                    mLightSensorPendingIncrease = false;
                     lightSensorChangedLocked(value);
                 }
             }
@@ -2569,16 +2576,12 @@
     }
 
     private void setScreenBrightnessMode(int mode) {
-        boolean enabled = (mode == SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
-        if (mUseSoftwareAutoBrightness && mAutoBrightessEnabled != enabled) {
-            mAutoBrightessEnabled = enabled;
-            if (isScreenOn()) {
-                // force recompute of backlight values
-                if (mLightSensorValue >= 0) {
-                    int value = (int)mLightSensorValue;
-                    mLightSensorValue = -1;
-                    lightSensorChangedLocked(value);
-                }
+        synchronized (mLocks) {
+            boolean enabled = (mode == SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+            if (mUseSoftwareAutoBrightness && mAutoBrightessEnabled != enabled) {
+                mAutoBrightessEnabled = enabled;
+                // This will get us a new value
+                enableLightSensorLocked(mAutoBrightessEnabled && isScreenOn());
             }
         }
     }
@@ -2604,7 +2607,8 @@
             }
             mKeylightDelay = LONG_KEYLIGHT_DELAY;
             if (totalDelay < 0) {
-                mScreenOffDelay = Integer.MAX_VALUE;
+                // negative number means stay on as long as possible.
+                mScreenOffDelay = mMaximumScreenOffTimeout;
             } else if (mKeylightDelay < totalDelay) {
                 // subtract the time that the keylight delay. This will give us the
                 // remainder of the time that we need to sleep to get the accurate
@@ -2729,7 +2733,6 @@
         // don't bother with the light sensor if auto brightness is handled in hardware
         if (mUseSoftwareAutoBrightness) {
             mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
-            enableLightSensor(true);
         }
 
         // wait until sensors are enabled before turning on screen.
@@ -2747,6 +2750,8 @@
             Slog.d(TAG, "system ready!");
             mDoneBooting = true;
 
+            enableLightSensorLocked(mUseSoftwareAutoBrightness && mAutoBrightessEnabled);
+
             long identity = Binder.clearCallingIdentity();
             try {
                 mBatteryStats.noteScreenBrightness(getPreferredBrightness());
@@ -2901,9 +2906,13 @@
         }
     }
 
-    private void enableLightSensor(boolean enable) {
+    private void enableLightSensorLocked(boolean enable) {
         if (mDebugLightSensor) {
-            Slog.d(TAG, "enableLightSensor " + enable);
+            Slog.d(TAG, "enableLightSensorLocked enable=" + enable
+                    + " mAutoBrightessEnabled=" + mAutoBrightessEnabled);
+        }
+        if (!mAutoBrightessEnabled) {
+            enable = false;
         }
         if (mSensorManager != null && mLightSensorEnabled != enable) {
             mLightSensorEnabled = enable;
@@ -2980,19 +2989,29 @@
                 if (mDebugLightSensor) {
                     Slog.d(TAG, "onSensorChanged: light value: " + value);
                 }
-                mHandler.removeCallbacks(mAutoBrightnessTask);
-                if (mLightSensorValue != value) {
-                    if (mLightSensorValue == -1 ||
-                            milliseconds < mLastScreenOnTime + mLightSensorWarmupTime) {
-                        // process the value immediately if screen has just turned on
-                        lightSensorChangedLocked(value);
-                    } else {
-                        // delay processing to debounce the sensor
-                        mLightSensorPendingValue = value;
-                        mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);
-                    }
+                if (mLightSensorValue == -1 ||
+                        milliseconds < mLastScreenOnTime + mLightSensorWarmupTime) {
+                    // process the value immediately if screen has just turned on
+                    mHandler.removeCallbacks(mAutoBrightnessTask);
+                    mLightSensorPendingDecrease = false;
+                    mLightSensorPendingIncrease = false;
+                    lightSensorChangedLocked(value);
                 } else {
-                    mLightSensorPendingValue = -1;
+                    if ((value > mLightSensorValue && mLightSensorPendingDecrease) ||
+                            (value < mLightSensorValue && mLightSensorPendingIncrease) ||
+                            (value == mLightSensorValue) ||
+                            (!mLightSensorPendingDecrease && !mLightSensorPendingIncrease)) {
+                        // delay processing to debounce the sensor
+                        mHandler.removeCallbacks(mAutoBrightnessTask);
+                        mLightSensorPendingDecrease = (value < mLightSensorValue);
+                        mLightSensorPendingIncrease = (value > mLightSensorValue);
+                        if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
+                            mLightSensorPendingValue = value;
+                            mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);
+                        }
+                    } else {
+                        mLightSensorPendingValue = value;
+                    }
                 }
             }
         }
diff --git a/services/java/com/android/server/SamplingProfilerService.java b/services/java/com/android/server/SamplingProfilerService.java
new file mode 100644
index 0000000..26af7f7
--- /dev/null
+++ b/services/java/com/android/server/SamplingProfilerService.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.ContentResolver;
+import android.os.DropBoxManager;
+import android.os.FileObserver;
+import android.os.Binder;
+
+import android.util.Slog;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import com.android.internal.os.SamplingProfilerIntegration;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class SamplingProfilerService extends Binder {
+
+    private static final String TAG = "SamplingProfilerService";
+    private static final boolean LOCAL_LOGV = false;
+    public static final String SNAPSHOT_DIR = SamplingProfilerIntegration.SNAPSHOT_DIR;
+
+    private FileObserver snapshotObserver;
+
+    public SamplingProfilerService(Context context) {
+        registerSettingObserver(context);
+        startWorking(context);
+    }
+
+    private void startWorking(Context context) {
+        if (LOCAL_LOGV) Slog.v(TAG, "starting SamplingProfilerService!");
+
+        final DropBoxManager dropbox =
+                (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE);
+
+        // before FileObserver is ready, there could have already been some snapshots
+        // in the directory, we don't want to miss them
+        File[] snapshotFiles = new File(SNAPSHOT_DIR).listFiles();
+        for (int i = 0; snapshotFiles != null && i < snapshotFiles.length; i++) {
+            handleSnapshotFile(snapshotFiles[i], dropbox);
+        }
+
+        // detect new snapshot and put it in dropbox
+        // delete it afterwards no matter what happened before
+        // Note: needs listening at event ATTRIB rather than CLOSE_WRITE, because we set the
+        // readability of snapshot files after writing them!
+        snapshotObserver = new FileObserver(SNAPSHOT_DIR, FileObserver.ATTRIB) {
+            @Override
+            public void onEvent(int event, String path) {
+                handleSnapshotFile(new File(SNAPSHOT_DIR, path), dropbox);
+            }
+        };
+        snapshotObserver.startWatching();
+
+        if (LOCAL_LOGV) Slog.v(TAG, "SamplingProfilerService activated");
+    }
+
+    private void handleSnapshotFile(File file, DropBoxManager dropbox) {
+        try {
+            dropbox.addFile(TAG, file, 0);
+            if (LOCAL_LOGV) Slog.v(TAG, file.getPath() + " added to dropbox");
+        } catch (IOException e) {
+            Slog.e(TAG, "Can't add " + file.getPath() + " to dropbox", e);
+        } finally {
+            file.delete();
+        }
+    }
+
+    private void registerSettingObserver(Context context) {
+        ContentResolver contentResolver = context.getContentResolver();
+        contentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.SAMPLING_PROFILER_HZ),
+                false, new SamplingProfilerSettingsObserver(contentResolver));
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("SamplingProfilerService:");
+        pw.println("Watching directory: " + SNAPSHOT_DIR);
+    }
+
+    private class SamplingProfilerSettingsObserver extends ContentObserver {
+        private ContentResolver mContentResolver;
+        public SamplingProfilerSettingsObserver(ContentResolver contentResolver) {
+            super(null);
+            mContentResolver = contentResolver;
+            onChange(false);
+        }
+        @Override
+        public void onChange(boolean selfChange) {
+            Integer samplingProfilerHz = Settings.Secure.getInt(
+                    mContentResolver, Settings.Secure.SAMPLING_PROFILER_HZ, 0);
+            // setting this secure property will start or stop sampling profiler,
+            // as well as adjust the frequency of taking snapshots.
+            SystemProperties.set("persist.sys.profiler_hz", samplingProfilerHz.toString());
+        }
+    }
+}
+
diff --git a/services/java/com/android/server/ShutdownActivity.java b/services/java/com/android/server/ShutdownActivity.java
index 64b9c5d..c9d4d01 100644
--- a/services/java/com/android/server/ShutdownActivity.java
+++ b/services/java/com/android/server/ShutdownActivity.java
@@ -27,19 +27,26 @@
 public class ShutdownActivity extends Activity {
 
     private static final String TAG = "ShutdownActivity";
+    private boolean mReboot;
     private boolean mConfirm;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
+        Intent intent = getIntent();
+        mReboot = Intent.ACTION_REBOOT.equals(intent.getAction());
+        mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
         Slog.i(TAG, "onCreate(): confirm=" + mConfirm);
 
         Handler h = new Handler();
         h.post(new Runnable() {
             public void run() {
-                ShutdownThread.shutdown(ShutdownActivity.this, mConfirm);
+                if (mReboot) {
+                    ShutdownThread.reboot(ShutdownActivity.this, null, mConfirm);
+                } else {
+                    ShutdownThread.shutdown(ShutdownActivity.this, mConfirm);
+                }
             }
         });
     }
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index e5dfb18..596db57 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -68,6 +68,12 @@
     ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
     int mDisabled = 0;
 
+    Object mLock = new Object();
+    // We usually call it lights out mode, but double negatives are annoying
+    boolean mLightsOn = true;
+    boolean mMenuVisible = false;
+    boolean mIMEButtonVisible = false;
+
     private class DisableRecord implements IBinder.DeathRecipient {
         String pkg;
         int what;
@@ -84,6 +90,7 @@
         void onSetDisabled(int status);
         void onClearAll();
         void onNotificationClick(String pkg, String tag, int id);
+        void onNotificationClear(String pkg, String tag, int id);
         void onPanelRevealed();
         void onNotificationError(String pkg, String tag, int id,
                 int uid, int initialPid, String message);
@@ -104,22 +111,6 @@
     }
 
     // ================================================================================
-    // Constructing the view
-    // ================================================================================
-
-    public void systemReady() {
-    }
-
-    public void systemReady2() {
-        ComponentName cn = ComponentName.unflattenFromString(
-                mContext.getString(com.android.internal.R.string.config_statusBarComponent));
-        Intent intent = new Intent();
-        intent.setComponent(cn);
-        Slog.i(TAG, "Starting service: " + cn);
-        mContext.startService(intent);
-    }
-
-    // ================================================================================
     // From IStatusBarService
     // ================================================================================
     public void expand() {
@@ -240,6 +231,105 @@
         }
     }
 
+    /** 
+     * Hide or show the on-screen Menu key. Only call this from the window manager, typically in
+     * response to a window with FLAG_NEEDS_MENU_KEY set.
+     */
+    public void setMenuKeyVisible(final boolean visible) {
+        enforceStatusBar();
+
+        if (SPEW) Slog.d(TAG, (visible?"showing":"hiding") + " MENU key");
+
+        synchronized(mLock) {
+            if (mMenuVisible != visible) {
+                mMenuVisible = visible;
+                mHandler.post(new Runnable() {
+                        public void run() {
+                            if (mBar != null) {
+                                try {
+                                    mBar.setMenuKeyVisible(visible);
+                                } catch (RemoteException ex) {
+                                }
+                            }
+                        }
+                    });
+            }
+        }
+    }
+
+    public void setIMEButtonVisible(final boolean visible) {
+        enforceStatusBar();
+
+        if (SPEW) Slog.d(TAG, (visible?"showing":"hiding") + " IME Button");
+
+        synchronized(mLock) {
+            // In case of IME change, we need to call up setIMEButtonVisible() regardless of
+            // mIMEButtonVisible because mIMEButtonVisible may not have been set to false when the
+            // previous IME was destroyed.
+            mIMEButtonVisible = visible;
+            mHandler.post(new Runnable() {
+                public void run() {
+                    if (mBar != null) {
+                        try {
+                            mBar.setIMEButtonVisible(visible);
+                        } catch (RemoteException ex) {
+                        }
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * This is used for the automatic version of lights-out mode.  Only call this from
+     * the window manager.
+     *
+     * @see setLightsOn(boolean)
+     */
+    public void setActiveWindowIsFullscreen(boolean fullscreen) {
+        // We could get away with a separate permission here, but STATUS_BAR is
+        // signatureOrSystem which is probably good enough.  There is no public API
+        // for this, so the question is a security issue, not an API compatibility issue.
+        enforceStatusBar();
+
+        synchronized (mLock) {
+            updateLightsOnLocked(!fullscreen);
+        }
+    }
+
+    /**
+     * This is used for the user-controlled version of lights-out mode.  Only call this from
+     * the status bar itself.
+     *
+     * We have two different functions here, because I think we're going to want to
+     * tweak the behavior when the user keeps turning lights-out mode off and the
+     * app keeps trying to turn it on.  For now they can just fight it out.  Having
+     * these two separte inputs will allow us to keep that change local to here.  --joeo
+     */
+    public void setLightsOn(boolean lightsOn) {
+        enforceStatusBarService();
+
+        synchronized (mLock) {
+            updateLightsOnLocked(lightsOn);
+        }
+    }
+
+    private void updateLightsOnLocked(final boolean lightsOn) {
+        if (mLightsOn != lightsOn) {
+            mLightsOn = lightsOn;
+            mHandler.post(new Runnable() {
+                    public void run() {
+                        if (mBar != null) {
+                            try {
+                                mBar.setLightsOn(lightsOn);
+                            } catch (RemoteException ex) {
+                            }
+                        }
+                    }
+                });
+        }
+    }
+
     private void enforceStatusBar() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
                 "StatusBarManagerService");
@@ -260,7 +350,8 @@
     // Callbacks from the status bar service.
     // ================================================================================
     public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
-            List<IBinder> notificationKeys, List<StatusBarNotification> notifications) {
+            List<IBinder> notificationKeys, List<StatusBarNotification> notifications,
+            int switches[]) {
         enforceStatusBarService();
 
         Slog.i(TAG, "registerStatusBar bar=" + bar);
@@ -274,6 +365,12 @@
                 notifications.add(e.getValue());
             }
         }
+        synchronized (mLock) {
+            switches[0] = gatherDisableActionsLocked();
+            switches[1] = mLightsOn ? 1 : 0;
+            switches[2] = mMenuVisible ? 1 : 0;
+            switches[3] = mIMEButtonVisible ? 1 : 0;
+        }
     }
 
     /**
@@ -301,6 +398,12 @@
         mNotificationCallbacks.onNotificationError(pkg, tag, id, uid, initialPid, message);
     }
 
+    public void onNotificationClear(String pkg, String tag, int id) {
+        enforceStatusBarService();
+
+        mNotificationCallbacks.onNotificationClear(pkg, tag, id);
+    }
+
     public void onClearAllNotifications() {
         enforceStatusBarService();
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index df69b76..54f7441 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -24,6 +24,7 @@
 import dalvik.system.VMRuntime;
 import dalvik.system.Zygote;
 
+import android.accounts.AccountManagerService;
 import android.app.ActivityManagerNative;
 import android.bluetooth.BluetoothAdapter;
 import android.content.ComponentName;
@@ -32,9 +33,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.IPackageManager;
+import android.content.res.Configuration;
 import android.database.ContentObserver;
-import android.database.Cursor;
 import android.media.AudioService;
+import android.os.Build;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -46,10 +48,12 @@
 import android.server.BluetoothA2dpService;
 import android.server.BluetoothService;
 import android.server.search.SearchManagerService;
+import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
-import android.accounts.AccountManagerService;
+import android.view.Display;
+import android.view.WindowManager;
 
 import java.io.File;
 import java.util.Timer;
@@ -57,11 +61,8 @@
 
 class ServerThread extends Thread {
     private static final String TAG = "SystemServer";
-    private final static boolean INCLUDE_DEMO = false;
 
-    private static final int LOG_BOOT_PROGRESS_SYSTEM_RUN = 3010;
-
-    private ContentResolver mContentResolver;
+    ContentResolver mContentResolver;
 
     private class AdbSettingsObserver extends ContentObserver {
         public AdbSettingsObserver() {
@@ -120,12 +121,13 @@
         WindowManagerService wm = null;
         BluetoothService bluetooth = null;
         BluetoothA2dpService bluetoothA2dp = null;
-        HeadsetObserver headset = null;
+        WiredAccessoryObserver wiredAccessory = null;
         DockObserver dock = null;
         UsbObserver usb = null;
         UiModeManagerService uiMode = null;
         RecognitionManagerService recognition = null;
         ThrottleService throttle = null;
+        NetworkTimeUpdateService networkTimeUpdater = null;
 
         // Critical services...
         try {
@@ -168,13 +170,13 @@
             Slog.i(TAG, "System Content Providers");
             ActivityManagerService.installSystemProviders();
 
-            Slog.i(TAG, "Battery Service");
-            battery = new BatteryService(context);
-            ServiceManager.addService("battery", battery);
-
             Slog.i(TAG, "Lights Service");
             lights = new LightsService(context);
 
+            Slog.i(TAG, "Battery Service");
+            battery = new BatteryService(context, lights);
+            ServiceManager.addService("battery", battery);
+
             Slog.i(TAG, "Vibrator Service");
             ServiceManager.addService("vibrator", new VibratorService(context));
 
@@ -215,6 +217,7 @@
                 bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);
                 ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE,
                                           bluetoothA2dp);
+                bluetooth.initAfterA2dpRegistration();
 
                 int bluetoothOn = Settings.Secure.getInt(mContentResolver,
                     Settings.Secure.BLUETOOTH_ON, 0);
@@ -234,6 +237,7 @@
         NotificationManagerService notification = null;
         WallpaperManagerService wallpaper = null;
         LocationManagerService location = null;
+        CountryDetectorService countryDetector = null;
 
         if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
             try {
@@ -345,6 +349,14 @@
             }
 
             try {
+                Slog.i(TAG, "Country Detector");
+                countryDetector = new CountryDetectorService(context);
+                ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
+            } catch (Throwable e) {
+                Slog.e(TAG, "Failure starting Country Detector", e);
+            }
+
+            try {
                 Slog.i(TAG, "Search Service");
                 ServiceManager.addService(Context.SEARCH_SERVICE,
                         new SearchManagerService(context));
@@ -352,11 +364,6 @@
                 Slog.e(TAG, "Failure starting Search Service", e);
             }
 
-            if (INCLUDE_DEMO) {
-                Slog.i(TAG, "Installing demo data...");
-                (new DemoThread(context)).start();
-            }
-
             try {
                 Slog.i(TAG, "DropBox Service");
                 ServiceManager.addService(Context.DROPBOX_SERVICE,
@@ -381,14 +388,6 @@
             }
 
             try {
-                Slog.i(TAG, "Headset Observer");
-                // Listen for wired headset changes
-                headset = new HeadsetObserver(context);
-            } catch (Throwable e) {
-                Slog.e(TAG, "Failure starting HeadsetObserver", e);
-            }
-
-            try {
                 Slog.i(TAG, "Dock Observer");
                 // Listen for dock station changes
                 dock = new DockObserver(context, power);
@@ -397,6 +396,14 @@
             }
 
             try {
+                Slog.i(TAG, "Wired Accessory Observer");
+                // Listen for wired headset changes
+                wiredAccessory = new WiredAccessoryObserver(context);
+            } catch (Throwable e) {
+                Slog.e(TAG, "Failure starting WiredAccessoryObserver", e);
+            }
+
+            try {
                 Slog.i(TAG, "USB Observer");
                 // Listen for USB changes
                 usb = new UsbObserver(context);
@@ -434,13 +441,32 @@
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting Recognition Service", e);
             }
-            
+
             try {
                 Slog.i(TAG, "DiskStats Service");
                 ServiceManager.addService("diskstats", new DiskStatsService(context));
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting DiskStats Service", e);
             }
+
+            try {
+                // need to add this service even if SamplingProfilerIntegration.isEnabled()
+                // is false, because it is this service that detects system property change and
+                // turns on SamplingProfilerIntegration. Plus, when sampling profiler doesn't work,
+                // there is little overhead for running this service.
+                Slog.i(TAG, "SamplingProfiler Service");
+                ServiceManager.addService("samplingprofiler",
+                            new SamplingProfilerService(context));
+            } catch (Throwable e) {
+                Slog.e(TAG, "Failure starting SamplingProfiler Service", e);
+            }
+
+            try {
+                Slog.i(TAG, "NetworkTimeUpdateService");
+                networkTimeUpdater = new NetworkTimeUpdateService(context);
+            } catch (Throwable e) {
+                Slog.e(TAG, "Failure starting NetworkTimeUpdate service");
+            }
         }
 
         // make sure the ADB_ENABLED setting value matches the secure property value
@@ -478,10 +504,17 @@
             notification.systemReady();
         }
 
-        if (statusBar != null) {
-            statusBar.systemReady();
-        }
         wm.systemReady();
+
+        // Update the configuration for this context by hand, because we're going
+        // to start using it before the config change done in wm.systemReady() will
+        // propagate to it.
+        Configuration config = wm.computeNewConfiguration();
+        DisplayMetrics metrics = new DisplayMetrics();
+        WindowManager w = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+        w.getDefaultDisplay().getMetrics(metrics);
+        context.getResources().updateConfiguration(config, metrics);
+
         power.systemReady();
         try {
             pm.systemReady();
@@ -489,7 +522,7 @@
         }
 
         // These are needed to propagate to the runnable below.
-        final StatusBarManagerService statusBarF = statusBar;
+        final Context contextF = context;
         final BatteryService batteryF = battery;
         final ConnectivityService connectivityF = connectivity;
         final DockObserver dockF = dock;
@@ -501,6 +534,8 @@
         final InputMethodManagerService immF = imm;
         final RecognitionManagerService recognitionF = recognition;
         final LocationManagerService locationF = location;
+        final CountryDetectorService countryDetectorF = countryDetector;
+        final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
 
         // We now tell the activity manager it is okay to run third party
         // code.  It will call back into us once it has gotten to the state
@@ -512,7 +547,7 @@
             public void run() {
                 Slog.i(TAG, "Making services ready");
 
-                if (statusBarF != null) statusBarF.systemReady2();
+                startSystemUi(contextF);
                 if (batteryF != null) batteryF.systemReady();
                 if (connectivityF != null) connectivityF.systemReady();
                 if (dockF != null) dockF.systemReady();
@@ -528,7 +563,9 @@
                 if (wallpaperF != null) wallpaperF.systemReady();
                 if (immF != null) immF.systemReady();
                 if (locationF != null) locationF.systemReady();
+                if (countryDetectorF != null) countryDetectorF.systemReady();
                 if (throttleF != null) throttleF.systemReady();
+                if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady();
             }
         });
 
@@ -540,39 +577,17 @@
         Looper.loop();
         Slog.d(TAG, "System ServerThread is exiting!");
     }
+
+    static final void startSystemUi(Context context) {
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName("com.android.systemui",
+                    "com.android.systemui.SystemUIService"));
+        Slog.d(TAG, "Starting service: " + intent);
+        context.startService(intent);
+    }
 }
 
-class DemoThread extends Thread
-{
-    DemoThread(Context context)
-    {
-        mContext = context;
-    }
-
-    @Override
-    public void run()
-    {
-        try {
-            Cursor c = mContext.getContentResolver().query(People.CONTENT_URI, null, null, null, null);
-            boolean hasData = c != null && c.moveToFirst();
-            if (c != null) {
-                c.deactivate();
-            }
-            if (!hasData) {
-                DemoDataSet dataset = new DemoDataSet();
-                dataset.add(mContext);
-            }
-        } catch (Throwable e) {
-            Slog.e("SystemServer", "Failure installing demo data", e);
-        }
-
-    }
-
-    Context mContext;
-}
-
-public class SystemServer
-{
+public class SystemServer {
     private static final String TAG = "SystemServer";
 
     public static final int FACTORY_TEST_OFF = 0;
@@ -610,7 +625,7 @@
             timer.schedule(new TimerTask() {
                 @Override
                 public void run() {
-                    SamplingProfilerIntegration.writeSnapshot("system_server");
+                    SamplingProfilerIntegration.writeSnapshot("system_server", null);
                 }
             }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);
         }
@@ -618,7 +633,7 @@
         // The system server has to run all of the time, so it needs to be
         // as efficient as possible with its memory usage.
         VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
-        
+
         System.loadLibrary("android_servers");
         init1(args);
     }
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 33702793..a7a0c68 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -19,7 +19,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.net.NetworkUtils;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -35,6 +36,7 @@
 import java.util.ArrayList;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.net.NetworkInterface;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.telephony.ITelephonyRegistry;
@@ -63,7 +65,9 @@
 
     private final Context mContext;
 
-    private final ArrayList<Record> mRecords = new ArrayList();
+    // access should be inside synchronized (mRecords) for these two fields
+    private final ArrayList<IBinder> mRemoveList = new ArrayList<IBinder>();
+    private final ArrayList<Record> mRecords = new ArrayList<Record>();
 
     private final IBatteryStats mBatteryStats;
 
@@ -89,14 +93,18 @@
 
     private String mDataConnectionApn = "";
 
-    private String[] mDataConnectionApnTypes = null;
+    private ArrayList<String> mConnectedApns;
 
-    private String mDataConnectionInterfaceName = "";
+    private LinkProperties mDataConnectionLinkProperties;
+
+    private LinkCapabilities mDataConnectionLinkCapabilities;
 
     private Bundle mCellLocation = new Bundle();
 
     private int mDataConnectionNetworkType;
 
+    private int mOtaspMode;
+
     static final int PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
                 PhoneStateListener.LISTEN_CALL_STATE |
@@ -121,6 +129,7 @@
         }
         mContext = context;
         mBatteryStats = BatteryStatsService.getService();
+        mConnectedApns = new ArrayList<String>();
     }
 
     public void listen(String pkgForDebug, IPhoneStateListener callback, int events,
@@ -153,7 +162,11 @@
                 r.events = events;
                 if (notifyNow) {
                     if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
-                        sendServiceState(r, mServiceState);
+                        try {
+                            r.callback.onServiceStateChanged(new ServiceState(mServiceState));
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
                     if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
                         try {
@@ -179,7 +192,11 @@
                         }
                     }
                     if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
-                        sendCellLocation(r, mCellLocation);
+                        try {
+                            r.callback.onCellLocationChanged(new Bundle(mCellLocation));
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
                     if ((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
                         try {
@@ -210,6 +227,13 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) {
+                        try {
+                            r.callback.onOtaspChanged(mOtaspMode);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                 }
             }
         } else {
@@ -236,16 +260,16 @@
         synchronized (mRecords) {
             mCallState = state;
             mCallIncomingNumber = incomingNumber;
-            for (int i = mRecords.size() - 1; i >= 0; i--) {
-                Record r = mRecords.get(i);
+            for (Record r : mRecords) {
                 if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
                     try {
                         r.callback.onCallStateChanged(state, incomingNumber);
                     } catch (RemoteException ex) {
-                        remove(r.binder);
+                        mRemoveList.add(r.binder);
                     }
                 }
             }
+            handleRemoveListLocked();
         }
         broadcastCallStateChanged(state, incomingNumber);
     }
@@ -257,12 +281,16 @@
         Slog.i(TAG, "notifyServiceState: " + state);
         synchronized (mRecords) {
             mServiceState = state;
-            for (int i = mRecords.size() - 1; i >= 0; i--) {
-                Record r = mRecords.get(i);
+            for (Record r : mRecords) {
                 if ((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
-                    sendServiceState(r, state);
+                    try {
+                        r.callback.onServiceStateChanged(new ServiceState(state));
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
                 }
             }
+            handleRemoveListLocked();
         }
         broadcastServiceStateChanged(state);
     }
@@ -273,10 +301,13 @@
         }
         synchronized (mRecords) {
             mSignalStrength = signalStrength;
-            for (int i = mRecords.size() - 1; i >= 0; i--) {
-                Record r = mRecords.get(i);
+            for (Record r : mRecords) {
                 if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
-                    sendSignalStrength(r, signalStrength);
+                    try {
+                        r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
                 }
                 if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
                     try {
@@ -284,10 +315,11 @@
                         r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
                                 : gsmSignalStrength));
                     } catch (RemoteException ex) {
-                        remove(r.binder);
+                        mRemoveList.add(r.binder);
                     }
                 }
             }
+            handleRemoveListLocked();
         }
         broadcastSignalStrengthChanged(signalStrength);
     }
@@ -296,19 +328,18 @@
         if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
             return;
         }
-        Slog.i(TAG, "notifyMessageWaitingChanged: " + mwi);
         synchronized (mRecords) {
             mMessageWaiting = mwi;
-            for (int i = mRecords.size() - 1; i >= 0; i--) {
-                Record r = mRecords.get(i);
+            for (Record r : mRecords) {
                 if ((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
                     try {
                         r.callback.onMessageWaitingIndicatorChanged(mwi);
                     } catch (RemoteException ex) {
-                        remove(r.binder);
+                        mRemoveList.add(r.binder);
                     }
                 }
             }
+            handleRemoveListLocked();
         }
     }
 
@@ -316,19 +347,18 @@
         if (!checkNotifyPermission("notifyCallForwardingChanged()")) {
             return;
         }
-        Slog.i(TAG, "notifyCallForwardingChanged: " + cfi);
         synchronized (mRecords) {
             mCallForwarding = cfi;
-            for (int i = mRecords.size() - 1; i >= 0; i--) {
-                Record r = mRecords.get(i);
+            for (Record r : mRecords) {
                 if ((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
                     try {
                         r.callback.onCallForwardingIndicatorChanged(cfi);
                     } catch (RemoteException ex) {
-                        remove(r.binder);
+                        mRemoveList.add(r.binder);
                     }
                 }
             }
+            handleRemoveListLocked();
         }
     }
 
@@ -338,58 +368,80 @@
         }
         synchronized (mRecords) {
             mDataActivity = state;
-            for (int i = mRecords.size() - 1; i >= 0; i--) {
-                Record r = mRecords.get(i);
+            for (Record r : mRecords) {
                 if ((r.events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
                     try {
                         r.callback.onDataActivity(state);
                     } catch (RemoteException ex) {
-                        remove(r.binder);
+                        mRemoveList.add(r.binder);
                     }
                 }
             }
+            handleRemoveListLocked();
         }
     }
 
     public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
-            String reason, String apn, String[] apnTypes, String interfaceName, int networkType,
-            String gateway) {
+            String reason, String apn, String apnType, LinkProperties linkProperties,
+            LinkCapabilities linkCapabilities, int networkType) {
         if (!checkNotifyPermission("notifyDataConnection()" )) {
             return;
         }
         Slog.i(TAG, "notifyDataConnection: state=" + state + " isDataConnectivityPossible="
                 + isDataConnectivityPossible + " reason=" + reason
-                + " interfaceName=" + interfaceName + " networkType=" + networkType);
+                + " apn=" + apn + " networkType=" + networkType);
         synchronized (mRecords) {
-            mDataConnectionState = state;
-            mDataConnectionPossible = isDataConnectivityPossible;
-            mDataConnectionReason = reason;
-            mDataConnectionApn = apn;
-            mDataConnectionApnTypes = apnTypes;
-            mDataConnectionInterfaceName = interfaceName;
-            mDataConnectionNetworkType = networkType;
-            for (int i = mRecords.size() - 1; i >= 0; i--) {
-                Record r = mRecords.get(i);
-                if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
-                    try {
-                        r.callback.onDataConnectionStateChanged(state, networkType);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
+            boolean modified = false;
+            if (state == TelephonyManager.DATA_CONNECTED) {
+                if (!mConnectedApns.contains(apnType)) {
+                    mConnectedApns.add(apnType);
+                    if (mDataConnectionState != state) {
+                        mDataConnectionState = state;
+                        modified = true;
                     }
                 }
+            } else {
+                mConnectedApns.remove(apnType);
+                if (mConnectedApns.isEmpty()) {
+                    mDataConnectionState = state;
+                    modified = true;
+                } else {
+                    // leave mDataConnectionState as is and
+                    // send out the new status for the APN in question.
+                }
+            }
+            mDataConnectionPossible = isDataConnectivityPossible;
+            mDataConnectionReason = reason;
+            mDataConnectionLinkProperties = linkProperties;
+            mDataConnectionLinkCapabilities = linkCapabilities;
+            if (mDataConnectionNetworkType != networkType) {
+                mDataConnectionNetworkType = networkType;
+                modified = true;
+            }
+            if (modified) {
+                for (Record r : mRecords) {
+                    if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
+                        try {
+                            r.callback.onDataConnectionStateChanged(state, networkType);
+                        } catch (RemoteException ex) {
+                            mRemoveList.add(r.binder);
+                        }
+                    }
+                }
+                handleRemoveListLocked();
             }
         }
         broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
-                apnTypes, interfaceName, gateway);
+                apnType, linkProperties, linkCapabilities);
     }
 
-    public void notifyDataConnectionFailed(String reason) {
+    public void notifyDataConnectionFailed(String reason, String apnType) {
         if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
             return;
         }
         /*
-         * This is commented out because there is on onDataConnectionFailed callback
-         * on PhoneStateListener. There should be
+         * This is commented out because there is no onDataConnectionFailed callback
+         * in PhoneStateListener. There should be.
         synchronized (mRecords) {
             mDataConnectionFailedReason = reason;
             final int N = mRecords.size();
@@ -401,7 +453,7 @@
             }
         }
         */
-        broadcastDataConnectionFailed(reason);
+        broadcastDataConnectionFailed(reason, apnType);
     }
 
     public void notifyCellLocation(Bundle cellLocation) {
@@ -410,39 +462,36 @@
         }
         synchronized (mRecords) {
             mCellLocation = cellLocation;
-            for (int i = mRecords.size() - 1; i >= 0; i--) {
-                Record r = mRecords.get(i);
+            for (Record r : mRecords) {
                 if ((r.events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
-                    sendCellLocation(r, cellLocation);
+                    try {
+                        r.callback.onCellLocationChanged(new Bundle(cellLocation));
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+
                 }
             }
+            handleRemoveListLocked();
         }
     }
 
-    /**
-     * Copy the service state object so they can't mess it up in the local calls
-     */
-    public void sendServiceState(Record r, ServiceState state) {
-        try {
-            r.callback.onServiceStateChanged(new ServiceState(state));
-        } catch (RemoteException ex) {
-            remove(r.binder);
+    public void notifyOtaspChanged(int otaspMode) {
+        if (!checkNotifyPermission("notifyOtaspChanged()" )) {
+            return;
         }
-    }
-
-    private void sendCellLocation(Record r, Bundle cellLocation) {
-        try {
-            r.callback.onCellLocationChanged(new Bundle(cellLocation));
-        } catch (RemoteException ex) {
-            remove(r.binder);
-        }
-    }
-
-    private void sendSignalStrength(Record r, SignalStrength signalStrength) {
-        try {
-            r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
-        } catch (RemoteException ex) {
-            remove(r.binder);
+        synchronized (mRecords) {
+            mOtaspMode = otaspMode;
+            for (Record r : mRecords) {
+                if ((r.events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) {
+                    try {
+                        r.callback.onOtaspChanged(otaspMode);
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            }
+            handleRemoveListLocked();
         }
     }
 
@@ -468,11 +517,11 @@
             pw.println("  mDataConnectionPossible=" + mDataConnectionPossible);
             pw.println("  mDataConnectionReason=" + mDataConnectionReason);
             pw.println("  mDataConnectionApn=" + mDataConnectionApn);
-            pw.println("  mDataConnectionInterfaceName=" + mDataConnectionInterfaceName);
+            pw.println("  mDataConnectionLinkProperties=" + mDataConnectionLinkProperties);
+            pw.println("  mDataConnectionLinkCapabilities=" + mDataConnectionLinkCapabilities);
             pw.println("  mCellLocation=" + mCellLocation);
             pw.println("registrations: count=" + recordCount);
-            for (int i = 0; i < recordCount; i++) {
-                Record r = mRecords.get(i);
+            for (Record r : mRecords) {
                 pw.println("  " + r.pkgForDebug + " 0x" + Integer.toHexString(r.events));
             }
         }
@@ -543,7 +592,8 @@
 
     private void broadcastDataConnectionStateChanged(int state,
             boolean isDataConnectivityPossible,
-            String reason, String apn, String[] apnTypes, String interfaceName, String gateway) {
+            String reason, String apn, String apnType, LinkProperties linkProperties,
+            LinkCapabilities linkCapabilities) {
         // Note: not reporting to the battery stats service here, because the
         // status bar takes care of that after taking into account all of the
         // required info.
@@ -556,29 +606,26 @@
         if (reason != null) {
             intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason);
         }
-        intent.putExtra(Phone.DATA_APN_KEY, apn);
-        String types = new String("");
-        if (apnTypes.length > 0) {
-            types = apnTypes[0];
-            for (int i = 1; i < apnTypes.length; i++) {
-                types = types+","+apnTypes[i];
+        if (linkProperties != null) {
+            intent.putExtra(Phone.DATA_LINK_PROPERTIES_KEY, linkProperties);
+            String iface = linkProperties.getInterfaceName();
+            if (iface != null) {
+                intent.putExtra(Phone.DATA_IFACE_NAME_KEY, iface);
             }
         }
-        intent.putExtra(Phone.DATA_APN_TYPES_KEY, types);
-        intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
-        int gatewayAddr = 0;
-        if (gateway != null) {
-            gatewayAddr = NetworkUtils.v4StringToInt(gateway);
+        if (linkCapabilities != null) {
+            intent.putExtra(Phone.DATA_LINK_CAPABILITIES_KEY, linkCapabilities);
         }
-        intent.putExtra(Phone.DATA_GATEWAY_KEY, gatewayAddr);
-
+        intent.putExtra(Phone.DATA_APN_KEY, apn);
+        intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType);
         mContext.sendStickyBroadcast(intent);
     }
 
-    private void broadcastDataConnectionFailed(String reason) {
+    private void broadcastDataConnectionFailed(String reason, String apnType) {
         Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
         intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         intent.putExtra(Phone.FAILURE_REASON_KEY, reason);
+        intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType);
         mContext.sendStickyBroadcast(intent);
     }
 
@@ -605,4 +652,13 @@
                     android.Manifest.permission.READ_PHONE_STATE, null);
         }
     }
+
+    private void handleRemoveListLocked() {
+        if (mRemoveList.size() > 0) {
+            for (IBinder b: mRemoveList) {
+                remove(b);
+            }
+            mRemoveList.clear();
+        }
+    }
 }
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index a93a6ee..d841cb3 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -60,6 +60,8 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Calendar;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.GregorianCalendar;
 import java.util.Properties;
 import java.util.Random;
@@ -83,8 +85,8 @@
     private static final long TESTING_THRESHOLD = 1 * 1024 * 1024;
 
     private int mPolicyPollPeriodSec;
-    private long mPolicyThreshold;
-    private int mPolicyThrottleValue;
+    private AtomicLong mPolicyThreshold;
+    private AtomicInteger mPolicyThrottleValue;
     private int mPolicyResetDay; // 1-28
     private int mPolicyNotificationsAllowedMask;
 
@@ -114,7 +116,7 @@
     private InterfaceObserver mInterfaceObserver;
     private SettingsObserver mSettingsObserver;
 
-    private int mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc
+    private AtomicInteger mThrottleIndex; // 0 for none, 1 for first throttle val, 2 for next, etc
     private static final int THROTTLE_INDEX_UNINITIALIZED = -1;
     private static final int THROTTLE_INDEX_UNTHROTTLED   =  0;
 
@@ -126,6 +128,10 @@
         if (VDBG) Slog.v(TAG, "Starting ThrottleService");
         mContext = context;
 
+        mPolicyThreshold = new AtomicLong();
+        mPolicyThrottleValue = new AtomicInteger();
+        mThrottleIndex = new AtomicInteger();
+
         mNtpActive = false;
 
         mIface = mContext.getResources().getString(R.string.config_datause_iface);
@@ -214,7 +220,7 @@
     }
 
     private long ntpToWallTime(long ntpTime) {
-        long bestNow = getBestTime();
+        long bestNow = getBestTime(true); // do it quickly
         long localNow = System.currentTimeMillis();
         return localNow + (ntpTime - bestNow);
     }
@@ -222,40 +228,42 @@
     // TODO - fetch for the iface
     // return time in the local, system wall time, correcting for the use of ntp
 
-    public synchronized long getResetTime(String iface) {
+    public long getResetTime(String iface) {
         enforceAccessPermission();
         long resetTime = 0;
         if (mRecorder != null) {
-            resetTime = ntpToWallTime(mRecorder.getPeriodEnd());
+            resetTime = mRecorder.getPeriodEnd();
         }
+        resetTime = ntpToWallTime(resetTime);
         return resetTime;
     }
 
     // TODO - fetch for the iface
     // return time in the local, system wall time, correcting for the use of ntp
-    public synchronized long getPeriodStartTime(String iface) {
-        enforceAccessPermission();
+    public long getPeriodStartTime(String iface) {
         long startTime = 0;
+        enforceAccessPermission();
         if (mRecorder != null) {
-            startTime = ntpToWallTime(mRecorder.getPeriodStart());
+            startTime = mRecorder.getPeriodStart();
         }
+        startTime = ntpToWallTime(startTime);
         return startTime;
     }
     //TODO - a better name?  getCliffByteCountThreshold?
     // TODO - fetch for the iface
-    public synchronized long getCliffThreshold(String iface, int cliff) {
+    public long getCliffThreshold(String iface, int cliff) {
         enforceAccessPermission();
         if (cliff == 1) {
-            return mPolicyThreshold;
+           return mPolicyThreshold.get();
         }
         return 0;
     }
     // TODO - a better name? getThrottleRate?
     // TODO - fetch for the iface
-    public synchronized int getCliffLevel(String iface, int cliff) {
+    public int getCliffLevel(String iface, int cliff) {
         enforceAccessPermission();
         if (cliff == 1) {
-            return mPolicyThrottleValue;
+            return mPolicyThrottleValue.get();
         }
         return 0;
     }
@@ -267,10 +275,9 @@
     }
 
     // TODO - fetch for the iface
-    public synchronized long getByteCount(String iface, int dir, int period, int ago) {
+    public long getByteCount(String iface, int dir, int period, int ago) {
         enforceAccessPermission();
-        if ((period == ThrottleManager.PERIOD_CYCLE) &&
-                (mRecorder != null)) {
+        if ((period == ThrottleManager.PERIOD_CYCLE) && (mRecorder != null)) {
             if (dir == ThrottleManager.DIRECTION_TX) return mRecorder.getPeriodTx(ago);
             if (dir == ThrottleManager.DIRECTION_RX) return mRecorder.getPeriodRx(ago);
         }
@@ -279,10 +286,10 @@
 
     // TODO - a better name - getCurrentThrottleRate?
     // TODO - fetch for the iface
-    public synchronized int getThrottle(String iface) {
+    public int getThrottle(String iface) {
         enforceAccessPermission();
-        if (mThrottleIndex == 1) {
-            return mPolicyThrottleValue;
+        if (mThrottleIndex.get() == 1) {
+            return mPolicyThrottleValue.get();
         }
         return 0;
     }
@@ -305,22 +312,6 @@
                 }
             }, new IntentFilter(ACTION_RESET));
 
-        // use a new thread as we don't want to stall the system for file writes
-        mThread = new HandlerThread(TAG);
-        mThread.start();
-        mHandler = new MyHandler(mThread.getLooper());
-        mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
-
-        mInterfaceObserver = new InterfaceObserver(mHandler, EVENT_IFACE_UP, mIface);
-        try {
-            mNMService.registerObserver(mInterfaceObserver);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Could not register InterfaceObserver " + e);
-        }
-
-        mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
-        mSettingsObserver.observe(mContext);
-
         FileInputStream stream = null;
         try {
             Properties properties = new Properties();
@@ -337,6 +328,22 @@
                 } catch (Exception e) {}
             }
         }
+
+        // use a new thread as we don't want to stall the system for file writes
+        mThread = new HandlerThread(TAG);
+        mThread.start();
+        mHandler = new MyHandler(mThread.getLooper());
+        mHandler.obtainMessage(EVENT_REBOOT_RECOVERY).sendToTarget();
+
+        mInterfaceObserver = new InterfaceObserver(mHandler, EVENT_IFACE_UP, mIface);
+        try {
+            mNMService.registerObserver(mInterfaceObserver);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Could not register InterfaceObserver " + e);
+        }
+
+        mSettingsObserver = new SettingsObserver(mHandler, EVENT_POLICY_CHANGED);
+        mSettingsObserver.observe(mContext);
     }
 
 
@@ -375,7 +382,7 @@
             // check for sim change TODO
             // reregister for notification of policy change
 
-            mThrottleIndex = THROTTLE_INDEX_UNINITIALIZED;
+            mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
 
             mRecorder = new DataRecorder(mContext, ThrottleService.this);
 
@@ -403,15 +410,16 @@
                     R.integer.config_datause_threshold_bytes);
             int defaultValue = mContext.getResources().getInteger(
                     R.integer.config_datause_throttle_kbitsps);
-            synchronized (ThrottleService.this) {
-                mPolicyThreshold = Settings.Secure.getLong(mContext.getContentResolver(),
-                        Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
-                mPolicyThrottleValue = Settings.Secure.getInt(mContext.getContentResolver(),
-                        Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
-                if (testing) {
-                    mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
-                    mPolicyThreshold = TESTING_THRESHOLD;
-                }
+            long threshold = Settings.Secure.getLong(mContext.getContentResolver(),
+                    Settings.Secure.THROTTLE_THRESHOLD_BYTES, defaultThreshold);
+            int value = Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.THROTTLE_VALUE_KBITSPS, defaultValue);
+
+            mPolicyThreshold.set(threshold);
+            mPolicyThrottleValue.set(value);
+            if (testing) {
+                mPolicyPollPeriodSec = TESTING_POLLING_PERIOD_SEC;
+                mPolicyThreshold.set(TESTING_THRESHOLD);
             }
 
             mPolicyResetDay = Settings.Secure.getInt(mContext.getContentResolver(),
@@ -423,10 +431,8 @@
                 Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.THROTTLE_RESET_DAY, mPolicyResetDay);
             }
-            synchronized (ThrottleService.this) {
-                if (mIface == null) {
-                    mPolicyThreshold = 0;
-                }
+            if (mIface == null) {
+                mPolicyThreshold.set(0);
             }
 
             int defaultNotificationType = mContext.getResources().getInteger(
@@ -437,15 +443,16 @@
             mMaxNtpCacheAgeSec = Settings.Secure.getInt(mContext.getContentResolver(),
                     Settings.Secure.THROTTLE_MAX_NTP_CACHE_AGE_SEC, MAX_NTP_CACHE_AGE_SEC);
 
-            if (VDBG || (mPolicyThreshold != 0)) {
+            if (VDBG || (mPolicyThreshold.get() != 0)) {
                 Slog.d(TAG, "onPolicyChanged testing=" + testing +", period=" +
-                        mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold + ", value=" +
-                        mPolicyThrottleValue + ", resetDay=" + mPolicyResetDay + ", noteType=" +
-                        mPolicyNotificationsAllowedMask + ", maxNtpCacheAge=" + mMaxNtpCacheAgeSec);
+                        mPolicyPollPeriodSec + ", threshold=" + mPolicyThreshold.get() +
+                        ", value=" + mPolicyThrottleValue.get() + ", resetDay=" + mPolicyResetDay +
+                        ", noteType=" + mPolicyNotificationsAllowedMask + ", maxNtpCacheAge=" +
+                        mMaxNtpCacheAgeSec);
             }
 
             // force updates
-            mThrottleIndex = THROTTLE_INDEX_UNINITIALIZED;
+            mThrottleIndex.set(THROTTLE_INDEX_UNINITIALIZED);
 
             onResetAlarm();
 
@@ -487,7 +494,7 @@
             long periodRx = mRecorder.getPeriodRx(0);
             long periodTx = mRecorder.getPeriodTx(0);
             long total = periodRx + periodTx;
-            if (VDBG || (mPolicyThreshold != 0)) {
+            if (VDBG || (mPolicyThreshold.get() != 0)) {
                 Slog.d(TAG, "onPollAlarm - roaming =" + roaming +
                         ", read =" + incRead + ", written =" + incWrite + ", new total =" + total);
             }
@@ -510,11 +517,11 @@
         private void onIfaceUp() {
             // if we were throttled before, be sure and set it again - the iface went down
             // (and may have disappeared all together) and these settings were lost
-            if (mThrottleIndex == 1) {
+            if (mThrottleIndex.get() == 1) {
                 try {
                     mNMService.setInterfaceThrottle(mIface, -1, -1);
                     mNMService.setInterfaceThrottle(mIface,
-                            mPolicyThrottleValue, mPolicyThrottleValue);
+                            mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
                 } catch (Exception e) {
                     Slog.e(TAG, "error setting Throttle: " + e);
                 }
@@ -523,7 +530,8 @@
 
         private void checkThrottleAndPostNotification(long currentTotal) {
             // is throttling enabled?
-            if (mPolicyThreshold == 0) {
+            long threshold = mPolicyThreshold.get();
+            if (threshold == 0) {
                 clearThrottleAndNotification();
                 return;
             }
@@ -535,15 +543,13 @@
             }
 
             // check if we need to throttle
-            if (currentTotal > mPolicyThreshold) {
-                if (mThrottleIndex != 1) {
-                    synchronized (ThrottleService.this) {
-                        mThrottleIndex = 1;
-                    }
-                    if (DBG) Slog.d(TAG, "Threshold " + mPolicyThreshold + " exceeded!");
+            if (currentTotal > threshold) {
+                if (mThrottleIndex.get() != 1) {
+                    mThrottleIndex.set(1);
+                    if (DBG) Slog.d(TAG, "Threshold " + threshold + " exceeded!");
                     try {
                         mNMService.setInterfaceThrottle(mIface,
-                                mPolicyThrottleValue, mPolicyThrottleValue);
+                                mPolicyThrottleValue.get(), mPolicyThrottleValue.get());
                     } catch (Exception e) {
                         Slog.e(TAG, "error setting Throttle: " + e);
                     }
@@ -556,7 +562,8 @@
                             Notification.FLAG_ONGOING_EVENT);
 
                     Intent broadcast = new Intent(ThrottleManager.THROTTLE_ACTION);
-                    broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL, mPolicyThrottleValue);
+                    broadcast.putExtra(ThrottleManager.EXTRA_THROTTLE_LEVEL,
+                            mPolicyThrottleValue.get());
                     mContext.sendStickyBroadcast(broadcast);
 
                 } // else already up!
@@ -579,8 +586,8 @@
                     long periodLength = end - start;
                     long now = System.currentTimeMillis();
                     long timeUsed = now - start;
-                    long warningThreshold = 2*mPolicyThreshold*timeUsed/(timeUsed+periodLength);
-                    if ((currentTotal > warningThreshold) && (currentTotal > mPolicyThreshold/4)) {
+                    long warningThreshold = 2*threshold*timeUsed/(timeUsed+periodLength);
+                    if ((currentTotal > warningThreshold) && (currentTotal > threshold/4)) {
                         if (mWarningNotificationSent == false) {
                             mWarningNotificationSent = true;
                             mNotificationManager.cancel(R.drawable.stat_sys_throttled);
@@ -625,11 +632,9 @@
         }
 
 
-        private synchronized void clearThrottleAndNotification() {
-            if (mThrottleIndex != THROTTLE_INDEX_UNTHROTTLED) {
-                synchronized (ThrottleService.this) {
-                    mThrottleIndex = THROTTLE_INDEX_UNTHROTTLED;
-                }
+        private void clearThrottleAndNotification() {
+            if (mThrottleIndex.get() != THROTTLE_INDEX_UNTHROTTLED) {
+                mThrottleIndex.set(THROTTLE_INDEX_UNTHROTTLED);
                 try {
                     mNMService.setInterfaceThrottle(mIface, -1, -1);
                 } catch (Exception e) {
@@ -687,12 +692,12 @@
         }
 
         private void onResetAlarm() {
-            if (VDBG || (mPolicyThreshold != 0)) {
+            if (VDBG || (mPolicyThreshold.get() != 0)) {
                 Slog.d(TAG, "onResetAlarm - last period had " + mRecorder.getPeriodRx(0) +
                         " bytes read and " + mRecorder.getPeriodTx(0) + " written");
             }
 
-            long now = getBestTime();
+            long now = getBestTime(false);
 
             if (mNtpActive || (mNtpServer == null)) {
                 Calendar end = calculatePeriodEnd(now);
@@ -719,20 +724,23 @@
 
         // will try to get the ntp time and switch to it if found.
         // will also cache the time so we don't fetch it repeatedly.
-        getBestTime();
+        getBestTime(false);
     }
 
     private static final int MAX_NTP_CACHE_AGE_SEC = 60 * 60 * 24; // 1 day
-    private static final int MAX_NTP_FETCH_WAIT = 10 * 1000;
+    private static final int MAX_NTP_FETCH_WAIT = 20 * 1000;
     private int mMaxNtpCacheAgeSec = MAX_NTP_CACHE_AGE_SEC;
     private long cachedNtp;
     private long cachedNtpTimestamp;
 
-    private long getBestTime() {
+    // if the request is tied to UI and ANR's are a danger, request a fast result
+    // the regular polling should have updated the cached time recently using the
+    // slower method (!fast)
+    private long getBestTime(boolean fast) {
         if (mNtpServer != null) {
             if (mNtpActive) {
                 long ntpAge = SystemClock.elapsedRealtime() - cachedNtpTimestamp;
-                if (ntpAge < mMaxNtpCacheAgeSec * 1000) {
+                if (ntpAge < mMaxNtpCacheAgeSec * 1000 || fast) {
                     if (VDBG) Slog.v(TAG, "using cached time");
                     return cachedNtp + ntpAge;
                 }
@@ -1025,39 +1033,57 @@
                 if (DBG) Slog.d(TAG, "data file empty");
                 return;
             }
-            synchronized (mParent) {
-                String[] parsed = data.split(":");
-                int parsedUsed = 0;
-                if (parsed.length < 6) {
-                    Slog.e(TAG, "reading data file with insufficient length - ignoring");
-                    return;
-                }
+            String[] parsed = data.split(":");
+            int parsedUsed = 0;
+            if (parsed.length < 6) {
+                Slog.e(TAG, "reading data file with insufficient length - ignoring");
+                return;
+            }
 
+            int periodCount;
+            long[] periodRxData;
+            long[] periodTxData;
+            int currentPeriod;
+            Calendar periodStart;
+            Calendar periodEnd;
+            try {
                 if (Integer.parseInt(parsed[parsedUsed++]) != DATA_FILE_VERSION) {
                     Slog.e(TAG, "reading data file with bad version - ignoring");
                     return;
                 }
 
-                mPeriodCount = Integer.parseInt(parsed[parsedUsed++]);
-                if (parsed.length != 5 + (2 * mPeriodCount)) {
+                periodCount = Integer.parseInt(parsed[parsedUsed++]);
+                if (parsed.length != 5 + (2 * periodCount)) {
                     Slog.e(TAG, "reading data file with bad length (" + parsed.length +
-                            " != " + (5+(2*mPeriodCount)) + ") - ignoring");
+                            " != " + (5 + (2 * periodCount)) + ") - ignoring");
                     return;
                 }
+                periodRxData = new long[periodCount];
+                for (int i = 0; i < periodCount; i++) {
+                    periodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
+                }
+                periodTxData = new long[periodCount];
+                for (int i = 0; i < periodCount; i++) {
+                    periodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
+                }
 
-                mPeriodRxData = new long[mPeriodCount];
-                for(int i = 0; i < mPeriodCount; i++) {
-                    mPeriodRxData[i] = Long.parseLong(parsed[parsedUsed++]);
-                }
-                mPeriodTxData = new long[mPeriodCount];
-                for(int i = 0; i < mPeriodCount; i++) {
-                    mPeriodTxData[i] = Long.parseLong(parsed[parsedUsed++]);
-                }
-                mCurrentPeriod = Integer.parseInt(parsed[parsedUsed++]);
-                mPeriodStart = new GregorianCalendar();
-                mPeriodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
-                mPeriodEnd = new GregorianCalendar();
-                mPeriodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
+                currentPeriod = Integer.parseInt(parsed[parsedUsed++]);
+
+                periodStart = new GregorianCalendar();
+                periodStart.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
+                periodEnd = new GregorianCalendar();
+                periodEnd.setTimeInMillis(Long.parseLong(parsed[parsedUsed++]));
+            } catch (Exception e) {
+                Slog.e(TAG, "Error parsing data file - ignoring");
+                return;
+            }
+            synchronized (mParent) {
+                mPeriodCount = periodCount;
+                mPeriodRxData = periodRxData;
+                mPeriodTxData = periodTxData;
+                mCurrentPeriod = currentPeriod;
+                mPeriodStart = periodStart;
+                mPeriodEnd = periodEnd;
             }
         }
 
@@ -1091,15 +1117,15 @@
         }
         pw.println();
 
-        pw.println("The threshold is " + mPolicyThreshold +
+        pw.println("The threshold is " + mPolicyThreshold.get() +
                 ", after which you experince throttling to " +
-                mPolicyThrottleValue + "kbps");
+                mPolicyThrottleValue.get() + "kbps");
         pw.println("Current period is " +
                 (mRecorder.getPeriodEnd() - mRecorder.getPeriodStart())/1000 + " seconds long " +
                 "and ends in " + (getResetTime(mIface) - System.currentTimeMillis()) / 1000 +
                 " seconds.");
         pw.println("Polling every " + mPolicyPollPeriodSec + " seconds");
-        pw.println("Current Throttle Index is " + mThrottleIndex);
+        pw.println("Current Throttle Index is " + mThrottleIndex.get());
         pw.println("Max NTP Cache Age is " + mMaxNtpCacheAgeSec);
 
         for (int i = 0; i < mRecorder.getPeriodCount(); i++) {
diff --git a/services/java/com/android/server/UsbObserver.java b/services/java/com/android/server/UsbObserver.java
index d08fe9b..cfa83be 100644
--- a/services/java/com/android/server/UsbObserver.java
+++ b/services/java/com/android/server/UsbObserver.java
@@ -24,6 +24,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.UEventObserver;
+import android.provider.Mtp;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.Slog;
@@ -118,6 +119,7 @@
         try {
             FileReader file = new FileReader(USB_CONFIGURATION_PATH);
             int len = file.read(buffer, 0, 1024);
+            file.close();
             mPreviousUsbConfig = mUsbConfig = Integer.valueOf((new String(buffer, 0, len)).trim());
 
         } catch (FileNotFoundException e) {
@@ -132,6 +134,7 @@
                 File file = new File(files[i], "enable");
                 FileReader reader = new FileReader(file);
                 int len = reader.read(buffer, 0, 1024);
+                reader.close();
                 int value = Integer.valueOf((new String(buffer, 0, len)).trim());
                 String functionName = files[i].getName();
                 if (value == 1) {
@@ -147,8 +150,43 @@
         }
     }
 
+    private native void monitorUsbHostBus();
+
+    // called from JNI in monitorUsbHostBus()
+    private void usbCameraAdded(int deviceID) {
+        Intent intent = new Intent(Usb.ACTION_USB_CAMERA_ATTACHED,
+                                Mtp.Device.getContentUri(deviceID));
+        Log.d(TAG, "usbCameraAdded, sending " + intent);
+        mContext.sendBroadcast(intent);
+    }
+
+    // called from JNI in monitorUsbHostBus()
+    private void usbCameraRemoved(int deviceID) {
+        Intent intent = new Intent(Usb.ACTION_USB_CAMERA_DETACHED,
+                                Mtp.Device.getContentUri(deviceID));
+        Log.d(TAG, "usbCameraRemoved, sending " + intent);
+        mContext.sendBroadcast(intent);
+    }
+
+    private void initHostSupport() {
+        // Create a thread to call into native code to wait for USB host events.
+        // This thread will call us back on usbCameraAdded and usbCameraRemoved.
+        Runnable runnable = new Runnable() {
+            public void run() {
+                monitorUsbHostBus();
+            }
+        };
+        new Thread(null, runnable, "UsbObserver host thread").start();
+    }
+
     void systemReady() {
         synchronized (this) {
+            if (mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_hasUsbHostSupport)) {
+                // start monitoring for connected USB devices
+                initHostSupport();
+            }
+
             update();
             mSystemReady = true;
         }
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index f0b5955..2fcdb5d 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -112,6 +112,10 @@
         context.registerReceiver(mIntentReceiver, filter);
     }
 
+    public boolean hasVibrator() {
+        return vibratorExists();
+    }
+    
     public void vibrate(long milliseconds, IBinder token) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -380,6 +384,7 @@
 
     volatile VibrateThread mThread;
 
+    native static boolean vibratorExists();
     native static void vibratorOn(long milliseconds);
     native static void vibratorOff();
 }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index c1e9965..853e46d 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,104 +16,87 @@
 
 package com.android.server;
 
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
-
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
-
 import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.net.wifi.WifiNative;
-import android.net.wifi.WifiStateTracker;
+import android.net.wifi.WifiStateMachine;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.SupplicantState;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
 import android.net.ConnectivityManager;
 import android.net.InterfaceConfiguration;
-import android.net.NetworkStateTracker;
 import android.net.DhcpInfo;
-import android.net.NetworkUtils;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
-import android.os.Looper;
 import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.WorkSource;
 import android.provider.Settings;
-import android.util.Slog;
 import android.text.TextUtils;
+import android.util.Slog;
 
 import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
-import java.util.regex.Pattern;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.net.UnknownHostException;
 
 import com.android.internal.app.IBatteryStats;
-import android.app.backup.IBackupManager;
+import com.android.internal.util.AsyncChannel;
 import com.android.server.am.BatteryStatsService;
 import com.android.internal.R;
 
 /**
  * WifiService handles remote WiFi operation requests by implementing
- * the IWifiManager interface. It also creates a WifiMonitor to listen
- * for Wifi-related events.
+ * the IWifiManager interface.
  *
  * @hide
  */
+//TODO: Clean up multiple locks and implement WifiService
+// as a SM to track soft AP/client/adhoc bring up based
+// on device idle state, airplane mode and boot.
+
 public class WifiService extends IWifiManager.Stub {
     private static final String TAG = "WifiService";
-    private static final boolean DBG = false;
-    private static final Pattern scanResultPattern = Pattern.compile("\t+");
-    private final WifiStateTracker mWifiStateTracker;
-    /* TODO: fetch a configurable interface */
-    private static final String SOFTAP_IFACE = "wl0.1";
+    private static final boolean DBG = true;
+
+    private final WifiStateMachine mWifiStateMachine;
 
     private Context mContext;
-    private int mWifiApState;
 
     private AlarmManager mAlarmManager;
     private PendingIntent mIdleIntent;
+    private BluetoothA2dp mBluetoothA2dp;
     private static final int IDLE_REQUEST = 0;
     private boolean mScreenOff;
     private boolean mDeviceIdle;
     private int mPluggedType;
 
-    private enum DriverAction {DRIVER_UNLOAD, NO_DRIVER_UNLOAD};
-
     // true if the user enabled Wifi while in airplane mode
-    private boolean mAirplaneModeOverwridden;
+    private AtomicBoolean mAirplaneModeOverwridden = new AtomicBoolean(false);
 
     private final LockList mLocks = new LockList();
     // some wifi lock statistics
@@ -131,9 +114,7 @@
 
     private final IBatteryStats mBatteryStats;
 
-    private INetworkManagementService nwService;
     ConnectivityManager mCm;
-    private WifiWatchdogService mWifiWatchdogService = null;
     private String[] mWifiRegexs;
 
     /**
@@ -143,52 +124,103 @@
      * being enabled but not active exceeds the battery drain caused by
      * re-establishing a connection to the mobile data network.
      */
-    private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */
+    private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */
 
-    private static final String WAKELOCK_TAG = "*wifi*";
+    private static final String ACTION_DEVICE_IDLE =
+            "com.android.server.WifiManager.action.DEVICE_IDLE";
+
+    private boolean mIsReceiverRegistered = false;
+
+
+    NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
+
+    // Variables relating to the 'available networks' notification
+    /**
+     * The icon to show in the 'available networks' notification. This will also
+     * be the ID of the Notification given to the NotificationManager.
+     */
+    private static final int ICON_NETWORKS_AVAILABLE =
+            com.android.internal.R.drawable.stat_notify_wifi_in_range;
+    /**
+     * When a notification is shown, we wait this amount before possibly showing it again.
+     */
+    private final long NOTIFICATION_REPEAT_DELAY_MS;
+    /**
+     * Whether the user has set the setting to show the 'available networks' notification.
+     */
+    private boolean mNotificationEnabled;
+    /**
+     * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
+     */
+    private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
+    /**
+     * The {@link System#currentTimeMillis()} must be at least this value for us
+     * to show the notification again.
+     */
+    private long mNotificationRepeatTime;
+    /**
+     * The Notification object given to the NotificationManager.
+     */
+    private Notification mNotification;
+    /**
+     * Whether the notification is being shown, as set by us. That is, if the
+     * user cancels the notification, we will not receive the callback so this
+     * will still be true. We only guarantee if this is false, then the
+     * notification is not showing.
+     */
+    private boolean mNotificationShown;
+    /**
+     * The number of continuous scans that must occur before consider the
+     * supplicant in a scanning state. This allows supplicant to associate with
+     * remembered networks that are in the scan results.
+     */
+    private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
+    /**
+     * The number of scans since the last network state change. When this
+     * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
+     * supplicant to actually be scanning. When the network state changes to
+     * something other than scanning, we reset this to 0.
+     */
+    private int mNumScansSinceNetworkStateChange;
 
     /**
-     * The maximum amount of time to hold the wake lock after a disconnect
-     * caused by stopping the driver. Establishing an EDGE connection has been
-     * observed to take about 5 seconds under normal circumstances. This
-     * provides a bit of extra margin.
-     * <p>
-     * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}.
-     * This is the default value if a Settings.Secure value is not present.
+     * Asynchronous channel to WifiStateMachine
      */
-    private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000;
+    private AsyncChannel mChannel;
 
-    // Wake lock used by driver-stop operation
-    private static PowerManager.WakeLock sDriverStopWakeLock;
-    // Wake lock used by other operations
-    private static PowerManager.WakeLock sWakeLock;
-
-    private static final int MESSAGE_ENABLE_WIFI        = 0;
-    private static final int MESSAGE_DISABLE_WIFI       = 1;
-    private static final int MESSAGE_STOP_WIFI          = 2;
-    private static final int MESSAGE_START_WIFI         = 3;
-    private static final int MESSAGE_RELEASE_WAKELOCK   = 4;
-    private static final int MESSAGE_UPDATE_STATE       = 5;
-    private static final int MESSAGE_START_ACCESS_POINT = 6;
-    private static final int MESSAGE_STOP_ACCESS_POINT  = 7;
-    private static final int MESSAGE_SET_CHANNELS       = 8;
-    private static final int MESSAGE_ENABLE_NETWORKS    = 9;
-    private static final int MESSAGE_START_SCAN         = 10;
-
-
-    private final  WifiHandler mWifiHandler;
-
-    /*
-     * Cache of scan results objects (size is somewhat arbitrary)
+    /**
+     * TODO: Possibly change WifiService into an AsyncService.
      */
-    private static final int SCAN_RESULT_CACHE_SIZE = 80;
-    private final LinkedHashMap<String, ScanResult> mScanResultCache;
+    private class WifiServiceHandler extends Handler {
+        private AsyncChannel mWshChannel;
 
-    /*
-     * Character buffer used to parse scan results (optimization)
-     */
-    private static final int SCAN_RESULT_BUFFER_SIZE = 512;
-    private boolean mNeedReconfig;
+        WifiServiceHandler(android.os.Looper looper, Context context) {
+            super(looper);
+            mWshChannel = new AsyncChannel();
+            mWshChannel.connect(context, this, mWifiStateMachine.getHandler(), 0);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
+                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+                        mChannel = mWshChannel;
+                    } else {
+                        Slog.d(TAG, "WifiServicehandler.handleMessage could not connect error=" +
+                                msg.arg1);
+                        mChannel = null;
+                    }
+                    break;
+                }
+                default: {
+                    Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg);
+                    break;
+                }
+            }
+        }
+    }
+    WifiServiceHandler mHandler;
 
     /**
      * Temporary for computing UIDS that are responsible for starting WIFI.
@@ -196,67 +228,26 @@
      */
     private final WorkSource mTmpWorkSource = new WorkSource();
 
-    /*
-     * Last UID that asked to enable WIFI.
-     */
-    private int mLastEnableUid = Process.myUid();
-
-    /*
-     * Last UID that asked to enable WIFI AP.
-     */
-    private int mLastApEnableUid = Process.myUid();
-
-
-    /**
-     * Number of allowed radio frequency channels in various regulatory domains.
-     * This list is sufficient for 802.11b/g networks (2.4GHz range).
-     */
-    private static int[] sValidRegulatoryChannelCounts = new int[] {11, 13, 14};
-
-    private static final String ACTION_DEVICE_IDLE =
-            "com.android.server.WifiManager.action.DEVICE_IDLE";
-
-    WifiService(Context context, WifiStateTracker tracker) {
+    WifiService(Context context) {
         mContext = context;
-        mWifiStateTracker = tracker;
-        mWifiStateTracker.enableRssiPolling(true);
+        mWifiStateMachine = new WifiStateMachine(mContext);
+        mWifiStateMachine.enableRssiPolling(true);
         mBatteryStats = BatteryStatsService.getService();
 
-        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-        nwService = INetworkManagementService.Stub.asInterface(b);
-
-        mScanResultCache = new LinkedHashMap<String, ScanResult>(
-            SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
-                /*
-                 * Limit the cache size by SCAN_RESULT_CACHE_SIZE
-                 * elements
-                 */
-                public boolean removeEldestEntry(Map.Entry eldest) {
-                    return SCAN_RESULT_CACHE_SIZE < this.size();
-                }
-            };
-
-        HandlerThread wifiThread = new HandlerThread("WifiService");
-        wifiThread.start();
-        mWifiHandler = new WifiHandler(wifiThread.getLooper());
-
-        mWifiStateTracker.setWifiState(WIFI_STATE_DISABLED);
-        mWifiApState = WIFI_AP_STATE_DISABLED;
-
         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
         Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
         mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
 
-        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
-        sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
-        sDriverStopWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
+        HandlerThread wifiThread = new HandlerThread("WifiService");
+        wifiThread.start();
+        mHandler = new WifiServiceHandler(wifiThread.getLooper(), context);
 
         mContext.registerReceiver(
                 new BroadcastReceiver() {
                     @Override
                     public void onReceive(Context context, Intent intent) {
                         // clear our flag indicating the user has overwridden airplane mode
-                        mAirplaneModeOverwridden = false;
+                        mAirplaneModeOverwridden.set(false);
                         // on airplane disable, restore Wifi if the saved state indicates so
                         if (!isAirplaneModeOn() && testAndClearWifiSavedState()) {
                             persistWifiEnabled(true);
@@ -271,14 +262,50 @@
                 @Override
                 public void onReceive(Context context, Intent intent) {
 
-                  ArrayList<String> available = intent.getStringArrayListExtra(
-                          ConnectivityManager.EXTRA_AVAILABLE_TETHER);
-                  ArrayList<String> active = intent.getStringArrayListExtra(
-                          ConnectivityManager.EXTRA_ACTIVE_TETHER);
-                  updateTetherState(available, active);
+                    ArrayList<String> available = intent.getStringArrayListExtra(
+                            ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+                    ArrayList<String> active = intent.getStringArrayListExtra(
+                            ConnectivityManager.EXTRA_ACTIVE_TETHER);
+                    updateTetherState(available, active);
 
                 }
             },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+                            // reset & clear notification on any wifi state change
+                            resetNotification();
+                        } else if (intent.getAction().equals(
+                                WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+                            mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+                                    WifiManager.EXTRA_NETWORK_INFO);
+                            // reset & clear notification on a network connect & disconnect
+                            switch(mNetworkInfo.getDetailedState()) {
+                                case CONNECTED:
+                                case DISCONNECTED:
+                                    resetNotification();
+                                    break;
+                            }
+                        } else if (intent.getAction().equals(
+                                WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+                            checkAndSetNotification();
+                        }
+                    }
+                }, filter);
+
+        // Setting is in seconds
+        NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
+        mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
+        mNotificationEnabledSettingObserver.register();
     }
 
     /**
@@ -287,7 +314,7 @@
      *
      * This function is used only at boot time
      */
-    public void startWifi() {
+    public void checkAndStartWifi() {
         /* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */
         boolean wifiEnabled = !isAirplaneModeOn()
                 && (getPersistedWifiEnabled() || testAndClearWifiSavedState());
@@ -304,7 +331,10 @@
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
 
-        mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (mCm == null) {
+            mCm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        }
+
         mWifiRegexs = mCm.getTetherableWifiRegexs();
 
         for (String intf : available) {
@@ -318,23 +348,20 @@
                             /* IP/netmask: 192.168.43.1/255.255.255.0 */
                             ifcg.ipAddr = (192 << 24) + (168 << 16) + (43 << 8) + 1;
                             ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
-                            ifcg.interfaceFlags = "up";
+                            ifcg.interfaceFlags = "[up]";
 
                             service.setInterfaceConfig(intf, ifcg);
                         }
                     } catch (Exception e) {
                         Slog.e(TAG, "Error configuring interface " + intf + ", :" + e);
-                        try {
-                            nwService.stopAccessPoint();
-                        } catch (Exception ee) {
-                            Slog.e(TAG, "Could not stop AP, :" + ee);
-                        }
-                        setWifiApEnabledState(WIFI_AP_STATE_FAILED, 0, DriverAction.DRIVER_UNLOAD);
+                        setWifiApEnabled(null, false);
                         return;
                     }
 
                     if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
-                        Slog.e(TAG, "Error tethering "+intf);
+                        Slog.e(TAG, "Error tethering on " + intf);
+                        setWifiApEnabled(null, false);
+                        return;
                     }
                     break;
                 }
@@ -370,18 +397,18 @@
         Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
     }
 
-    NetworkStateTracker getNetworkStateTracker() {
-        return mWifiStateTracker;
-    }
-
     /**
      * see {@link android.net.wifi.WifiManager#pingSupplicant()}
-     * @return {@code true} if the operation succeeds
+     * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public boolean pingSupplicant() {
-        enforceChangePermission();
-
-        return mWifiStateTracker.ping();
+        enforceAccessPermission();
+        if (mChannel != null) {
+            return mWifiStateMachine.syncPingSupplicant(mChannel);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
+            return false;
+        }
     }
 
     /**
@@ -389,160 +416,7 @@
      */
     public void startScan(boolean forceActive) {
         enforceChangePermission();
-        if (mWifiHandler == null) return;
-
-        Message.obtain(mWifiHandler, MESSAGE_START_SCAN, forceActive ? 1 : 0, 0).sendToTarget();
-    }
-
-    /**
-     * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
-     * @param enable {@code true} to enable, {@code false} to disable.
-     * @return {@code true} if the enable/disable operation was
-     *         started or is already in the queue.
-     */
-    public boolean setWifiEnabled(boolean enable) {
-        enforceChangePermission();
-        if (mWifiHandler == null) return false;
-
-        synchronized (mWifiHandler) {
-            // caller may not have WAKE_LOCK permission - it's not required here
-            long ident = Binder.clearCallingIdentity();
-            sWakeLock.acquire();
-            Binder.restoreCallingIdentity(ident);
-
-            mLastEnableUid = Binder.getCallingUid();
-            // set a flag if the user is enabling Wifi while in airplane mode
-            mAirplaneModeOverwridden = (enable && isAirplaneModeOn() && isAirplaneToggleable());
-            sendEnableMessage(enable, true, Binder.getCallingUid());
-        }
-
-        return true;
-    }
-
-    /**
-     * Enables/disables Wi-Fi synchronously.
-     * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
-     * @param persist {@code true} if the setting should be persisted.
-     * @param uid The UID of the process making the request.
-     * @return {@code true} if the operation succeeds (or if the existing state
-     *         is the same as the requested state)
-     */
-    private boolean setWifiEnabledBlocking(boolean enable, boolean persist, int uid) {
-        final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED;
-        final int wifiState = mWifiStateTracker.getWifiState();
-
-        if (wifiState == eventualWifiState) {
-            return true;
-        }
-        if (enable && isAirplaneModeOn() && !mAirplaneModeOverwridden) {
-            return false;
-        }
-
-        /**
-         * Multiple calls to unregisterReceiver() cause exception and a system crash.
-         * This can happen if a supplicant is lost (or firmware crash occurs) and user indicates
-         * disable wifi at the same time.
-         * Avoid doing a disable when the current Wifi state is UNKNOWN
-         * TODO: Handle driver load fail and supplicant lost as seperate states
-         */
-        if ((wifiState == WIFI_STATE_UNKNOWN) && !enable) {
-            return false;
-        }
-
-        /**
-         * Fail Wifi if AP is enabled
-         * TODO: Deprecate WIFI_STATE_UNKNOWN and rename it
-         * WIFI_STATE_FAILED
-         */
-        if ((mWifiApState == WIFI_AP_STATE_ENABLED) && enable) {
-            setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
-            return false;
-        }
-
-        setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
-
-        if (enable) {
-            if (!mWifiStateTracker.loadDriver()) {
-                Slog.e(TAG, "Failed to load Wi-Fi driver.");
-                setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
-                return false;
-            }
-            if (!mWifiStateTracker.startSupplicant()) {
-                mWifiStateTracker.unloadDriver();
-                Slog.e(TAG, "Failed to start supplicant daemon.");
-                setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
-                return false;
-            }
-
-            registerForBroadcasts();
-            mWifiStateTracker.startEventLoop();
-
-        } else {
-
-            mContext.unregisterReceiver(mReceiver);
-           // Remove notification (it will no-op if it isn't visible)
-            mWifiStateTracker.setNotificationVisible(false, 0, false, 0);
-
-            boolean failedToStopSupplicantOrUnloadDriver = false;
-
-            if (!mWifiStateTracker.stopSupplicant()) {
-                Slog.e(TAG, "Failed to stop supplicant daemon.");
-                setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
-                failedToStopSupplicantOrUnloadDriver = true;
-            }
-
-            /**
-             * Reset connections and disable interface
-             * before we unload the driver
-             */
-            mWifiStateTracker.resetConnections(true);
-
-            if (!mWifiStateTracker.unloadDriver()) {
-                Slog.e(TAG, "Failed to unload Wi-Fi driver.");
-                if (!failedToStopSupplicantOrUnloadDriver) {
-                    setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
-                    failedToStopSupplicantOrUnloadDriver = true;
-                }
-            }
-
-            if (failedToStopSupplicantOrUnloadDriver) {
-                return false;
-            }
-        }
-
-        // Success!
-
-        if (persist) {
-            persistWifiEnabled(enable);
-        }
-        setWifiEnabledState(eventualWifiState, uid);
-        return true;
-    }
-
-    private void setWifiEnabledState(int wifiState, int uid) {
-        final int previousWifiState = mWifiStateTracker.getWifiState();
-
-        long ident = Binder.clearCallingIdentity();
-        try {
-            if (wifiState == WIFI_STATE_ENABLED) {
-                mBatteryStats.noteWifiOn();
-            } else if (wifiState == WIFI_STATE_DISABLED) {
-                mBatteryStats.noteWifiOff();
-            }
-        } catch (RemoteException e) {
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-
-        // Update state
-        mWifiStateTracker.setWifiState(wifiState);
-
-        // Broadcast
-        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
-        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
-        mContext.sendStickyBroadcast(intent);
+        mWifiStateMachine.startScan(forceActive);
     }
 
     private void enforceAccessPermission() {
@@ -563,6 +437,50 @@
     }
 
     /**
+     * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
+     * @param enable {@code true} to enable, {@code false} to disable.
+     * @return {@code true} if the enable/disable operation was
+     *         started or is already in the queue.
+     */
+    public synchronized boolean setWifiEnabled(boolean enable) {
+        enforceChangePermission();
+
+        if (DBG) {
+            Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
+        }
+
+        // set a flag if the user is enabling Wifi while in airplane mode
+        if (enable && isAirplaneModeOn() && isAirplaneToggleable()) {
+            mAirplaneModeOverwridden.set(true);
+        }
+
+        if (enable) {
+            reportStartWorkSource();
+        }
+        mWifiStateMachine.setWifiEnabled(enable);
+
+        /*
+         * Caller might not have WRITE_SECURE_SETTINGS,
+         * only CHANGE_WIFI_STATE is enforced
+         */
+        long ident = Binder.clearCallingIdentity();
+        persistWifiEnabled(enable);
+        Binder.restoreCallingIdentity(ident);
+
+        if (enable) {
+            if (!mIsReceiverRegistered) {
+                registerForBroadcasts();
+                mIsReceiverRegistered = true;
+            }
+        } else if (mIsReceiverRegistered){
+            mContext.unregisterReceiver(mReceiver);
+            mIsReceiverRegistered = false;
+        }
+
+        return true;
+    }
+
+    /**
      * see {@link WifiManager#getWifiState()}
      * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
      *         {@link WifiManager#WIFI_STATE_DISABLING},
@@ -572,66 +490,59 @@
      */
     public int getWifiEnabledState() {
         enforceAccessPermission();
-        return mWifiStateTracker.getWifiState();
-    }
-
-    /**
-     * see {@link android.net.wifi.WifiManager#disconnect()}
-     * @return {@code true} if the operation succeeds
-     */
-    public boolean disconnect() {
-        enforceChangePermission();
-
-        return mWifiStateTracker.disconnect();
-    }
-
-    /**
-     * see {@link android.net.wifi.WifiManager#reconnect()}
-     * @return {@code true} if the operation succeeds
-     */
-    public boolean reconnect() {
-        enforceChangePermission();
-
-        return mWifiStateTracker.reconnectCommand();
-    }
-
-    /**
-     * see {@link android.net.wifi.WifiManager#reassociate()}
-     * @return {@code true} if the operation succeeds
-     */
-    public boolean reassociate() {
-        enforceChangePermission();
-
-        return mWifiStateTracker.reassociate();
+        return mWifiStateMachine.syncGetWifiState();
     }
 
     /**
      * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)}
      * @param wifiConfig SSID, security and channel details as
      *        part of WifiConfiguration
-     * @param enabled, true to enable and false to disable
+     * @param enabled true to enable and false to disable
      * @return {@code true} if the start operation was
      *         started or is already in the queue.
      */
-    public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
+    public synchronized boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
         enforceChangePermission();
-        if (mWifiHandler == null) return false;
 
-        synchronized (mWifiHandler) {
-
+        if (enabled) {
+            /* Use default config if there is no existing config */
+            if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) {
+                wifiConfig = new WifiConfiguration();
+                wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
+                wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
+            }
+            /*
+             * Caller might not have WRITE_SECURE_SETTINGS,
+             * only CHANGE_WIFI_STATE is enforced
+             */
             long ident = Binder.clearCallingIdentity();
-            sWakeLock.acquire();
+            setWifiApConfiguration(wifiConfig);
             Binder.restoreCallingIdentity(ident);
-
-            mLastApEnableUid = Binder.getCallingUid();
-            sendAccessPointMessage(enabled, wifiConfig, Binder.getCallingUid());
         }
 
+        mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
+
         return true;
     }
 
-    public WifiConfiguration getWifiApConfiguration() {
+    /**
+     * see {@link WifiManager#getWifiApState()}
+     * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
+     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
+     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
+     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
+     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
+     */
+    public int getWifiApEnabledState() {
         enforceAccessPermission();
+        return mWifiStateMachine.syncGetWifiApState();
+    }
+
+    /**
+     * see {@link WifiManager#getWifiApConfiguration()}
+     * @return soft access point configuration
+     */
+    public synchronized WifiConfiguration getWifiApConfiguration() {
         final ContentResolver cr = mContext.getContentResolver();
         WifiConfiguration wifiConfig = new WifiConfiguration();
         int authType;
@@ -649,7 +560,11 @@
         }
     }
 
-    public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
+    /**
+     * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
+     * @param wifiConfig WifiConfiguration details for soft access point
+     */
+    public synchronized void setWifiApConfiguration(WifiConfiguration wifiConfig) {
         enforceChangePermission();
         final ContentResolver cr = mContext.getContentResolver();
         boolean isWpa;
@@ -665,143 +580,27 @@
     }
 
     /**
-     * Enables/disables Wi-Fi AP synchronously. The driver is loaded
-     * and soft access point configured as a single operation.
-     * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off.
-     * @param uid The UID of the process making the request.
-     * @param wifiConfig The WifiConfiguration for AP
-     * @return {@code true} if the operation succeeds (or if the existing state
-     *         is the same as the requested state)
+     * see {@link android.net.wifi.WifiManager#disconnect()}
      */
-    private boolean setWifiApEnabledBlocking(boolean enable,
-                                int uid, WifiConfiguration wifiConfig) {
-        final int eventualWifiApState = enable ? WIFI_AP_STATE_ENABLED : WIFI_AP_STATE_DISABLED;
-
-        if (mWifiApState == eventualWifiApState) {
-            /* Configuration changed on a running access point */
-            if(enable && (wifiConfig != null)) {
-                try {
-                    nwService.setAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
-                                             SOFTAP_IFACE);
-                    setWifiApConfiguration(wifiConfig);
-                    return true;
-                } catch(Exception e) {
-                    Slog.e(TAG, "Exception in nwService during AP restart");
-                    try {
-                        nwService.stopAccessPoint();
-                    } catch (Exception ee) {
-                        Slog.e(TAG, "Could not stop AP, :" + ee);
-                    }
-                    setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
-                    return false;
-                }
-            } else {
-                return true;
-            }
-        }
-
-        /**
-         * Fail AP if Wifi is enabled
-         */
-        if ((mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLED) && enable) {
-            setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
-            return false;
-        }
-
-        setWifiApEnabledState(enable ? WIFI_AP_STATE_ENABLING :
-                                       WIFI_AP_STATE_DISABLING, uid, DriverAction.NO_DRIVER_UNLOAD);
-
-        if (enable) {
-
-            /* Use default config if there is no existing config */
-            if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) {
-                wifiConfig = new WifiConfiguration();
-                wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default);
-                wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE);
-            }
-
-            if (!mWifiStateTracker.loadDriver()) {
-                Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode");
-                setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
-                return false;
-            }
-
-            try {
-                nwService.startAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(),
-                                           SOFTAP_IFACE);
-            } catch(Exception e) {
-                Slog.e(TAG, "Exception in startAccessPoint()");
-                setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
-                return false;
-            }
-
-            setWifiApConfiguration(wifiConfig);
-
-        } else {
-
-            try {
-                nwService.stopAccessPoint();
-            } catch(Exception e) {
-                Slog.e(TAG, "Exception in stopAccessPoint()");
-                setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD);
-                return false;
-            }
-
-            if (!mWifiStateTracker.unloadDriver()) {
-                Slog.e(TAG, "Failed to unload Wi-Fi driver for AP mode");
-                setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD);
-                return false;
-            }
-        }
-
-        setWifiApEnabledState(eventualWifiApState, uid, DriverAction.NO_DRIVER_UNLOAD);
-        return true;
+    public void disconnect() {
+        enforceChangePermission();
+        mWifiStateMachine.disconnectCommand();
     }
 
     /**
-     * see {@link WifiManager#getWifiApState()}
-     * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
-     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
-     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
-     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
-     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
+     * see {@link android.net.wifi.WifiManager#reconnect()}
      */
-    public int getWifiApEnabledState() {
-        enforceAccessPermission();
-        return mWifiApState;
+    public void reconnect() {
+        enforceChangePermission();
+        mWifiStateMachine.reconnectCommand();
     }
 
-    private void setWifiApEnabledState(int wifiAPState, int uid, DriverAction flag) {
-        final int previousWifiApState = mWifiApState;
-
-        /**
-         * Unload the driver if going to a failed state
-         */
-        if ((mWifiApState == WIFI_AP_STATE_FAILED) && (flag == DriverAction.DRIVER_UNLOAD)) {
-            mWifiStateTracker.unloadDriver();
-        }
-
-        long ident = Binder.clearCallingIdentity();
-        try {
-            if (wifiAPState == WIFI_AP_STATE_ENABLED) {
-                mBatteryStats.noteWifiOn();
-            } else if (wifiAPState == WIFI_AP_STATE_DISABLED) {
-                mBatteryStats.noteWifiOff();
-            }
-        } catch (RemoteException e) {
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-
-        // Update state
-        mWifiApState = wifiAPState;
-
-        // Broadcast
-        final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiAPState);
-        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
-        mContext.sendStickyBroadcast(intent);
+    /**
+     * see {@link android.net.wifi.WifiManager#reassociate()}
+     */
+    public void reassociate() {
+        enforceChangePermission();
+        mWifiStateMachine.reassociateCommand();
     }
 
     /**
@@ -810,217 +609,7 @@
      */
     public List<WifiConfiguration> getConfiguredNetworks() {
         enforceAccessPermission();
-        String listStr;
-
-        /*
-         * We don't cache the list, because we want to allow
-         * for the possibility that the configuration file
-         * has been modified through some external means,
-         * such as the wpa_cli command line program.
-         */
-        listStr = mWifiStateTracker.listNetworks();
-
-        List<WifiConfiguration> networks =
-            new ArrayList<WifiConfiguration>();
-        if (listStr == null)
-            return networks;
-
-        String[] lines = listStr.split("\n");
-        // Skip the first line, which is a header
-       for (int i = 1; i < lines.length; i++) {
-           String[] result = lines[i].split("\t");
-           // network-id | ssid | bssid | flags
-           WifiConfiguration config = new WifiConfiguration();
-           try {
-               config.networkId = Integer.parseInt(result[0]);
-           } catch(NumberFormatException e) {
-               continue;
-           }
-           if (result.length > 3) {
-               if (result[3].indexOf("[CURRENT]") != -1)
-                   config.status = WifiConfiguration.Status.CURRENT;
-               else if (result[3].indexOf("[DISABLED]") != -1)
-                   config.status = WifiConfiguration.Status.DISABLED;
-               else
-                   config.status = WifiConfiguration.Status.ENABLED;
-           } else {
-               config.status = WifiConfiguration.Status.ENABLED;
-           }
-           readNetworkVariables(config);
-           networks.add(config);
-       }
-
-        return networks;
-    }
-
-    /**
-     * Read the variables from the supplicant daemon that are needed to
-     * fill in the WifiConfiguration object.
-     * <p/>
-     * The caller must hold the synchronization monitor.
-     * @param config the {@link WifiConfiguration} object to be filled in.
-     */
-    private void readNetworkVariables(WifiConfiguration config) {
-
-        int netId = config.networkId;
-        if (netId < 0)
-            return;
-
-        /*
-         * TODO: maybe should have a native method that takes an array of
-         * variable names and returns an array of values. But we'd still
-         * be doing a round trip to the supplicant daemon for each variable.
-         */
-        String value;
-
-        value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
-        if (!TextUtils.isEmpty(value)) {
-            config.SSID = value;
-        } else {
-            config.SSID = null;
-        }
-
-        value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
-        if (!TextUtils.isEmpty(value)) {
-            config.BSSID = value;
-        } else {
-            config.BSSID = null;
-        }
-
-        value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
-        config.priority = -1;
-        if (!TextUtils.isEmpty(value)) {
-            try {
-                config.priority = Integer.parseInt(value);
-            } catch (NumberFormatException ignore) {
-            }
-        }
-
-        value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
-        config.hiddenSSID = false;
-        if (!TextUtils.isEmpty(value)) {
-            try {
-                config.hiddenSSID = Integer.parseInt(value) != 0;
-            } catch (NumberFormatException ignore) {
-            }
-        }
-
-        value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
-        config.wepTxKeyIndex = -1;
-        if (!TextUtils.isEmpty(value)) {
-            try {
-                config.wepTxKeyIndex = Integer.parseInt(value);
-            } catch (NumberFormatException ignore) {
-            }
-        }
-
-        /*
-         * Get up to 4 WEP keys. Note that the actual keys are not passed back,
-         * just a "*" if the key is set, or the null string otherwise.
-         */
-        for (int i = 0; i < 4; i++) {
-            value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.wepKeyVarNames[i]);
-            if (!TextUtils.isEmpty(value)) {
-                config.wepKeys[i] = value;
-            } else {
-                config.wepKeys[i] = null;
-            }
-        }
-
-        /*
-         * Get the private shared key. Note that the actual keys are not passed back,
-         * just a "*" if the key is set, or the null string otherwise.
-         */
-        value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.pskVarName);
-        if (!TextUtils.isEmpty(value)) {
-            config.preSharedKey = value;
-        } else {
-            config.preSharedKey = null;
-        }
-
-        value = mWifiStateTracker.getNetworkVariable(config.networkId,
-                WifiConfiguration.Protocol.varName);
-        if (!TextUtils.isEmpty(value)) {
-            String vals[] = value.split(" ");
-            for (String val : vals) {
-                int index =
-                    lookupString(val, WifiConfiguration.Protocol.strings);
-                if (0 <= index) {
-                    config.allowedProtocols.set(index);
-                }
-            }
-        }
-
-        value = mWifiStateTracker.getNetworkVariable(config.networkId,
-                WifiConfiguration.KeyMgmt.varName);
-        if (!TextUtils.isEmpty(value)) {
-            String vals[] = value.split(" ");
-            for (String val : vals) {
-                int index =
-                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
-                if (0 <= index) {
-                    config.allowedKeyManagement.set(index);
-                }
-            }
-        }
-
-        value = mWifiStateTracker.getNetworkVariable(config.networkId,
-                WifiConfiguration.AuthAlgorithm.varName);
-        if (!TextUtils.isEmpty(value)) {
-            String vals[] = value.split(" ");
-            for (String val : vals) {
-                int index =
-                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
-                if (0 <= index) {
-                    config.allowedAuthAlgorithms.set(index);
-                }
-            }
-        }
-
-        value = mWifiStateTracker.getNetworkVariable(config.networkId,
-                WifiConfiguration.PairwiseCipher.varName);
-        if (!TextUtils.isEmpty(value)) {
-            String vals[] = value.split(" ");
-            for (String val : vals) {
-                int index =
-                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
-                if (0 <= index) {
-                    config.allowedPairwiseCiphers.set(index);
-                }
-            }
-        }
-
-        value = mWifiStateTracker.getNetworkVariable(config.networkId,
-                WifiConfiguration.GroupCipher.varName);
-        if (!TextUtils.isEmpty(value)) {
-            String vals[] = value.split(" ");
-            for (String val : vals) {
-                int index =
-                    lookupString(val, WifiConfiguration.GroupCipher.strings);
-                if (0 <= index) {
-                    config.allowedGroupCiphers.set(index);
-                }
-            }
-        }
-
-        for (WifiConfiguration.EnterpriseField field :
-                config.enterpriseFields) {
-            value = mWifiStateTracker.getNetworkVariable(netId,
-                    field.varName());
-            if (!TextUtils.isEmpty(value)) {
-                if (field != config.eap) value = removeDoubleQuotes(value);
-                field.setValue(value);
-            }
-        }
-    }
-
-    private static String removeDoubleQuotes(String string) {
-        if (string.length() <= 2) return "";
-        return string.substring(1, string.length() - 1);
-    }
-
-    private static String convertToQuotedString(String string) {
-        return "\"" + string + "\"";
+        return mWifiStateMachine.syncGetConfiguredNetworks();
     }
 
     /**
@@ -1030,280 +619,15 @@
      */
     public int addOrUpdateNetwork(WifiConfiguration config) {
         enforceChangePermission();
-
-        /*
-         * If the supplied networkId is -1, we create a new empty
-         * network configuration. Otherwise, the networkId should
-         * refer to an existing configuration.
-         */
-        int netId = config.networkId;
-        boolean newNetwork = netId == -1;
-        boolean doReconfig = false;
-        // networkId of -1 means we want to create a new network
-        synchronized (mWifiStateTracker) {
-            if (newNetwork) {
-                netId = mWifiStateTracker.addNetwork();
-                if (netId < 0) {
-                    if (DBG) {
-                        Slog.d(TAG, "Failed to add a network!");
-                    }
-                    return -1;
-                }
-                doReconfig = true;
-            }
-            mNeedReconfig = mNeedReconfig || doReconfig;
+        if (mChannel != null) {
+            return mWifiStateMachine.syncAddOrUpdateNetwork(mChannel, config);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
+            return -1;
         }
-
-        setVariables: {
-            /*
-             * Note that if a networkId for a non-existent network
-             * was supplied, then the first setNetworkVariable()
-             * will fail, so we don't bother to make a separate check
-             * for the validity of the ID up front.
-             */
-            if (config.SSID != null &&
-                    !mWifiStateTracker.setNetworkVariable(
-                        netId,
-                        WifiConfiguration.ssidVarName,
-                        config.SSID)) {
-                if (DBG) {
-                    Slog.d(TAG, "failed to set SSID: "+config.SSID);
-                }
-                break setVariables;
-            }
-
-            if (config.BSSID != null &&
-                    !mWifiStateTracker.setNetworkVariable(
-                        netId,
-                        WifiConfiguration.bssidVarName,
-                        config.BSSID)) {
-                if (DBG) {
-                    Slog.d(TAG, "failed to set BSSID: "+config.BSSID);
-                }
-                break setVariables;
-            }
-
-            String allowedKeyManagementString =
-                makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
-            if (config.allowedKeyManagement.cardinality() != 0 &&
-                    !mWifiStateTracker.setNetworkVariable(
-                        netId,
-                        WifiConfiguration.KeyMgmt.varName,
-                        allowedKeyManagementString)) {
-                if (DBG) {
-                    Slog.d(TAG, "failed to set key_mgmt: "+
-                            allowedKeyManagementString);
-                }
-                break setVariables;
-            }
-
-            String allowedProtocolsString =
-                makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
-            if (config.allowedProtocols.cardinality() != 0 &&
-                    !mWifiStateTracker.setNetworkVariable(
-                        netId,
-                        WifiConfiguration.Protocol.varName,
-                        allowedProtocolsString)) {
-                if (DBG) {
-                    Slog.d(TAG, "failed to set proto: "+
-                            allowedProtocolsString);
-                }
-                break setVariables;
-            }
-
-            String allowedAuthAlgorithmsString =
-                makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
-            if (config.allowedAuthAlgorithms.cardinality() != 0 &&
-                    !mWifiStateTracker.setNetworkVariable(
-                        netId,
-                        WifiConfiguration.AuthAlgorithm.varName,
-                        allowedAuthAlgorithmsString)) {
-                if (DBG) {
-                    Slog.d(TAG, "failed to set auth_alg: "+
-                            allowedAuthAlgorithmsString);
-                }
-                break setVariables;
-            }
-
-            String allowedPairwiseCiphersString =
-                makeString(config.allowedPairwiseCiphers, WifiConfiguration.PairwiseCipher.strings);
-            if (config.allowedPairwiseCiphers.cardinality() != 0 &&
-                    !mWifiStateTracker.setNetworkVariable(
-                        netId,
-                        WifiConfiguration.PairwiseCipher.varName,
-                        allowedPairwiseCiphersString)) {
-                if (DBG) {
-                    Slog.d(TAG, "failed to set pairwise: "+
-                            allowedPairwiseCiphersString);
-                }
-                break setVariables;
-            }
-
-            String allowedGroupCiphersString =
-                makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
-            if (config.allowedGroupCiphers.cardinality() != 0 &&
-                    !mWifiStateTracker.setNetworkVariable(
-                        netId,
-                        WifiConfiguration.GroupCipher.varName,
-                        allowedGroupCiphersString)) {
-                if (DBG) {
-                    Slog.d(TAG, "failed to set group: "+
-                            allowedGroupCiphersString);
-                }
-                break setVariables;
-            }
-
-            // Prevent client screw-up by passing in a WifiConfiguration we gave it
-            // by preventing "*" as a key.
-            if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
-                    !mWifiStateTracker.setNetworkVariable(
-                        netId,
-                        WifiConfiguration.pskVarName,
-                        config.preSharedKey)) {
-                if (DBG) {
-                    Slog.d(TAG, "failed to set psk: "+config.preSharedKey);
-                }
-                break setVariables;
-            }
-
-            boolean hasSetKey = false;
-            if (config.wepKeys != null) {
-                for (int i = 0; i < config.wepKeys.length; i++) {
-                    // Prevent client screw-up by passing in a WifiConfiguration we gave it
-                    // by preventing "*" as a key.
-                    if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
-                        if (!mWifiStateTracker.setNetworkVariable(
-                                    netId,
-                                    WifiConfiguration.wepKeyVarNames[i],
-                                    config.wepKeys[i])) {
-                            if (DBG) {
-                                Slog.d(TAG,
-                                        "failed to set wep_key"+i+": " +
-                                        config.wepKeys[i]);
-                            }
-                            break setVariables;
-                        }
-                        hasSetKey = true;
-                    }
-                }
-            }
-
-            if (hasSetKey) {
-                if (!mWifiStateTracker.setNetworkVariable(
-                            netId,
-                            WifiConfiguration.wepTxKeyIdxVarName,
-                            Integer.toString(config.wepTxKeyIndex))) {
-                    if (DBG) {
-                        Slog.d(TAG,
-                                "failed to set wep_tx_keyidx: "+
-                                config.wepTxKeyIndex);
-                    }
-                    break setVariables;
-                }
-            }
-
-            if (!mWifiStateTracker.setNetworkVariable(
-                        netId,
-                        WifiConfiguration.priorityVarName,
-                        Integer.toString(config.priority))) {
-                if (DBG) {
-                    Slog.d(TAG, config.SSID + ": failed to set priority: "
-                            +config.priority);
-                }
-                break setVariables;
-            }
-
-            if (config.hiddenSSID && !mWifiStateTracker.setNetworkVariable(
-                        netId,
-                        WifiConfiguration.hiddenSSIDVarName,
-                        Integer.toString(config.hiddenSSID ? 1 : 0))) {
-                if (DBG) {
-                    Slog.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
-                            config.hiddenSSID);
-                }
-                break setVariables;
-            }
-
-            for (WifiConfiguration.EnterpriseField field
-                    : config.enterpriseFields) {
-                String varName = field.varName();
-                String value = field.value();
-                if (value != null) {
-                    if (field != config.eap) {
-                        value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
-                    }
-                    if (!mWifiStateTracker.setNetworkVariable(
-                                netId,
-                                varName,
-                                value)) {
-                        if (DBG) {
-                            Slog.d(TAG, config.SSID + ": failed to set " + varName +
-                                    ": " + value);
-                        }
-                        break setVariables;
-                    }
-                }
-            }
-            return netId;
-        }
-
-        /*
-         * For an update, if one of the setNetworkVariable operations fails,
-         * we might want to roll back all the changes already made. But the
-         * chances are that if anything is going to go wrong, it'll happen
-         * the first time we try to set one of the variables.
-         */
-        if (newNetwork) {
-            removeNetwork(netId);
-            if (DBG) {
-                Slog.d(TAG,
-                        "Failed to set a network variable, removed network: "
-                        + netId);
-            }
-        }
-        return -1;
     }
 
-    private static String makeString(BitSet set, String[] strings) {
-        StringBuffer buf = new StringBuffer();
-        int nextSetBit = -1;
-
-        /* Make sure all set bits are in [0, strings.length) to avoid
-         * going out of bounds on strings.  (Shouldn't happen, but...) */
-        set = set.get(0, strings.length);
-
-        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
-            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
-        }
-
-        // remove trailing space
-        if (set.cardinality() > 0) {
-            buf.setLength(buf.length() - 1);
-        }
-
-        return buf.toString();
-    }
-
-    private static int lookupString(String string, String[] strings) {
-        int size = strings.length;
-
-        string = string.replace('-', '_');
-
-        for (int i = 0; i < size; i++)
-            if (string.equals(strings[i]))
-                return i;
-
-        if (DBG) {
-            // if we ever get here, we should probably add the
-            // value to WifiConfiguration to reflect that it's
-            // supported by the WPA supplicant
-            Slog.w(TAG, "Failed to look-up a string: " + string);
-        }
-
-        return -1;
-    }
-
-    /**
+     /**
      * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
      * @param netId the integer that identifies the network configuration
      * to the supplicant
@@ -1311,8 +635,12 @@
      */
     public boolean removeNetwork(int netId) {
         enforceChangePermission();
-
-        return mWifiStateTracker.removeNetwork(netId);
+        if (mChannel != null) {
+            return mWifiStateMachine.syncRemoveNetwork(mChannel, netId);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
+            return false;
+        }
     }
 
     /**
@@ -1324,14 +652,12 @@
      */
     public boolean enableNetwork(int netId, boolean disableOthers) {
         enforceChangePermission();
-
-        String ifname = mWifiStateTracker.getInterfaceName();
-        NetworkUtils.enableInterface(ifname);
-        boolean result = mWifiStateTracker.enableNetwork(netId, disableOthers);
-        if (!result) {
-            NetworkUtils.disableInterface(ifname);
+        if (mChannel != null) {
+            return mWifiStateMachine.syncEnableNetwork(mChannel, netId, disableOthers);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
+            return false;
         }
-        return result;
     }
 
     /**
@@ -1342,8 +668,12 @@
      */
     public boolean disableNetwork(int netId) {
         enforceChangePermission();
-
-        return mWifiStateTracker.disableNetwork(netId);
+        if (mChannel != null) {
+            return mWifiStateMachine.syncDisableNetwork(mChannel, netId);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
+            return false;
+        }
     }
 
     /**
@@ -1356,7 +686,7 @@
          * Make sure we have the latest information, by sending
          * a status request to the supplicant.
          */
-        return mWifiStateTracker.requestConnectionInfo();
+        return mWifiStateMachine.syncRequestConnectionInfo();
     }
 
     /**
@@ -1366,272 +696,40 @@
      */
     public List<ScanResult> getScanResults() {
         enforceAccessPermission();
-        String reply;
-
-        reply = mWifiStateTracker.scanResults();
-        if (reply == null) {
-            return null;
-        }
-
-        List<ScanResult> scanList = new ArrayList<ScanResult>();
-
-        int lineCount = 0;
-
-        int replyLen = reply.length();
-        // Parse the result string, keeping in mind that the last line does
-        // not end with a newline.
-        for (int lineBeg = 0, lineEnd = 0; lineEnd <= replyLen; ++lineEnd) {
-            if (lineEnd == replyLen || reply.charAt(lineEnd) == '\n') {
-                ++lineCount;
-                /*
-                 * Skip the first line, which is a header
-                 */
-                if (lineCount == 1) {
-                    lineBeg = lineEnd + 1;
-                    continue;
-                }
-                if (lineEnd > lineBeg) {
-                    String line = reply.substring(lineBeg, lineEnd);
-                    ScanResult scanResult = parseScanResult(line);
-                    if (scanResult != null) {
-                        scanList.add(scanResult);
-                    } else if (DBG) {
-                        Slog.w(TAG, "misformatted scan result for: " + line);
-                    }
-                }
-                lineBeg = lineEnd + 1;
-            }
-        }
-        mWifiStateTracker.setScanResultsList(scanList);
-        return scanList;
-    }
-
-    /**
-     * Parse the scan result line passed to us by wpa_supplicant (helper).
-     * @param line the line to parse
-     * @return the {@link ScanResult} object
-     */
-    private ScanResult parseScanResult(String line) {
-        ScanResult scanResult = null;
-        if (line != null) {
-            /*
-             * Cache implementation (LinkedHashMap) is not synchronized, thus,
-             * must synchronized here!
-             */
-            synchronized (mScanResultCache) {
-                String[] result = scanResultPattern.split(line);
-                if (3 <= result.length && result.length <= 5) {
-                    String bssid = result[0];
-                    // bssid | frequency | level | flags | ssid
-                    int frequency;
-                    int level;
-                    try {
-                        frequency = Integer.parseInt(result[1]);
-                        level = Integer.parseInt(result[2]);
-                        /* some implementations avoid negative values by adding 256
-                         * so we need to adjust for that here.
-                         */
-                        if (level > 0) level -= 256;
-                    } catch (NumberFormatException e) {
-                        frequency = 0;
-                        level = 0;
-                    }
-
-                    /*
-                     * The formatting of the results returned by
-                     * wpa_supplicant is intended to make the fields
-                     * line up nicely when printed,
-                     * not to make them easy to parse. So we have to
-                     * apply some heuristics to figure out which field
-                     * is the SSID and which field is the flags.
-                     */
-                    String ssid;
-                    String flags;
-                    if (result.length == 4) {
-                        if (result[3].charAt(0) == '[') {
-                            flags = result[3];
-                            ssid = "";
-                        } else {
-                            flags = "";
-                            ssid = result[3];
-                        }
-                    } else if (result.length == 5) {
-                        flags = result[3];
-                        ssid = result[4];
-                    } else {
-                        // Here, we must have 3 fields: no flags and ssid
-                        // set
-                        flags = "";
-                        ssid = "";
-                    }
-
-                    // bssid + ssid is the hash key
-                    String key = bssid + ssid;
-                    scanResult = mScanResultCache.get(key);
-                    if (scanResult != null) {
-                        scanResult.level = level;
-                        scanResult.SSID = ssid;
-                        scanResult.capabilities = flags;
-                        scanResult.frequency = frequency;
-                    } else {
-                        // Do not add scan results that have no SSID set
-                        if (0 < ssid.trim().length()) {
-                            scanResult =
-                                new ScanResult(
-                                    ssid, bssid, flags, level, frequency);
-                            mScanResultCache.put(key, scanResult);
-                        }
-                    }
-                } else {
-                    Slog.w(TAG, "Misformatted scan result text with " +
-                          result.length + " fields: " + line);
-                }
-            }
-        }
-
-        return scanResult;
-    }
-
-    /**
-     * Parse the "flags" field passed back in a scan result by wpa_supplicant,
-     * and construct a {@code WifiConfiguration} that describes the encryption,
-     * key management, and authenticaion capabilities of the access point.
-     * @param flags the string returned by wpa_supplicant
-     * @return the {@link WifiConfiguration} object, filled in
-     */
-    WifiConfiguration parseScanFlags(String flags) {
-        WifiConfiguration config = new WifiConfiguration();
-
-        if (flags.length() == 0) {
-            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
-        }
-        // ... to be implemented
-        return config;
+        return mWifiStateMachine.syncGetScanResultsList();
     }
 
     /**
      * Tell the supplicant to persist the current list of configured networks.
      * @return {@code true} if the operation succeeded
+     *
+     * TODO: deprecate this
      */
     public boolean saveConfiguration() {
-        boolean result;
+        boolean result = true;
         enforceChangePermission();
-
-        synchronized (mWifiStateTracker) {
-            result = mWifiStateTracker.saveConfig();
-            if (result && mNeedReconfig) {
-                mNeedReconfig = false;
-                result = mWifiStateTracker.reloadConfig();
-
-                if (result) {
-                    Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION);
-                    mContext.sendBroadcast(intent);
-                }
-            }
-        }
-        // Inform the backup manager about a data change
-        IBackupManager ibm = IBackupManager.Stub.asInterface(
-                ServiceManager.getService(Context.BACKUP_SERVICE));
-        if (ibm != null) {
-            try {
-                ibm.dataChanged("com.android.providers.settings");
-            } catch (Exception e) {
-                // Try again later
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Set the number of radio frequency channels that are allowed to be used
-     * in the current regulatory domain. This method should be used only
-     * if the correct number of channels cannot be determined automatically
-     * for some reason. If the operation is successful, the new value may be
-     * persisted as a Secure setting.
-     * @param numChannels the number of allowed channels. Must be greater than 0
-     * and less than or equal to 16.
-     * @param persist {@code true} if the setting should be remembered.
-     * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
-     * {@code numChannels} is outside the valid range.
-     */
-    public boolean setNumAllowedChannels(int numChannels, boolean persist) {
-        Slog.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+
-                " with persist set to "+persist);
-        enforceChangePermission();
-
-        /*
-         * Validate the argument. We'd like to let the Wi-Fi driver do this,
-         * but if Wi-Fi isn't currently enabled, that's not possible, and
-         * we want to persist the setting anyway,so that it will take
-         * effect when Wi-Fi does become enabled.
-         */
-        boolean found = false;
-        for (int validChan : sValidRegulatoryChannelCounts) {
-            if (validChan == numChannels) {
-                found = true;
-                break;
-            }
-        }
-        if (!found) {
+        if (mChannel != null) {
+            return mWifiStateMachine.syncSaveConfig(mChannel);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
             return false;
         }
-
-        if (mWifiHandler == null) return false;
-
-        Message.obtain(mWifiHandler,
-                MESSAGE_SET_CHANNELS, numChannels, (persist ? 1 : 0)).sendToTarget();
-
-        return true;
     }
 
     /**
-     * sets the number of allowed radio frequency channels synchronously
-     * @param numChannels the number of allowed channels. Must be greater than 0
-     * and less than or equal to 16.
+     * Set the country code
+     * @param countryCode ISO 3166 country code.
      * @param persist {@code true} if the setting should be remembered.
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
+     *
+     * The persist behavior exists so that wifi can fall back to the last
+     * persisted country code on a restart, when the locale information is
+     * not available from telephony.
      */
-    private boolean setNumAllowedChannelsBlocking(int numChannels, boolean persist) {
-        if (persist) {
-            Settings.Secure.putInt(mContext.getContentResolver(),
-                    Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
-                    numChannels);
-        }
-        return mWifiStateTracker.setNumAllowedChannels(numChannels);
-    }
-
-    /**
-     * Return the number of frequency channels that are allowed
-     * to be used in the current regulatory domain.
-     * @return the number of allowed channels, or {@code -1} if an error occurs
-     */
-    public int getNumAllowedChannels() {
-        int numChannels;
-
-        enforceAccessPermission();
-
-        /*
-         * If we can't get the value from the driver (e.g., because
-         * Wi-Fi is not currently enabled), get the value from
-         * Settings.
-         */
-        numChannels = mWifiStateTracker.getNumAllowedChannels();
-        if (numChannels < 0) {
-            numChannels = Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
-                    -1);
-        }
-        return numChannels;
-    }
-
-    /**
-     * Return the list of valid values for the number of allowed radio channels
-     * for various regulatory domains.
-     * @return the list of channel counts
-     */
-    public int[] getValidChannelCounts() {
-        enforceAccessPermission();
-        return sValidRegulatoryChannelCounts;
+    public void setCountryCode(String countryCode, boolean persist) {
+        Slog.i(TAG, "WifiService trying to set country code to " + countryCode +
+                " with persist set to " + persist);
+        enforceChangePermission();
+        mWifiStateMachine.setCountryCode(countryCode, persist);
     }
 
     /**
@@ -1641,7 +739,96 @@
      */
     public DhcpInfo getDhcpInfo() {
         enforceAccessPermission();
-        return mWifiStateTracker.getDhcpInfo();
+        return mWifiStateMachine.syncGetDhcpInfo();
+    }
+
+    /**
+     * see {@link android.net.wifi.WifiManager#startWifi}
+     *
+     */
+    public void startWifi() {
+        enforceChangePermission();
+        /* TODO: may be add permissions for access only to connectivity service
+         * TODO: if a start issued, keep wifi alive until a stop issued irrespective
+         * of WifiLock & device idle status unless wifi enabled status is toggled
+         */
+
+        mWifiStateMachine.setDriverStart(true);
+        mWifiStateMachine.reconnectCommand();
+    }
+
+    /**
+     * see {@link android.net.wifi.WifiManager#stopWifi}
+     *
+     */
+    public void stopWifi() {
+        enforceChangePermission();
+        /* TODO: may be add permissions for access only to connectivity service
+         * TODO: if a stop is issued, wifi is brought up only by startWifi
+         * unless wifi enabled status is toggled
+         */
+        mWifiStateMachine.setDriverStart(false);
+    }
+
+
+    /**
+     * see {@link android.net.wifi.WifiManager#addToBlacklist}
+     *
+     */
+    public void addToBlacklist(String bssid) {
+        enforceChangePermission();
+
+        mWifiStateMachine.addToBlacklist(bssid);
+    }
+
+    /**
+     * see {@link android.net.wifi.WifiManager#clearBlacklist}
+     *
+     */
+    public void clearBlacklist() {
+        enforceChangePermission();
+
+        mWifiStateMachine.clearBlacklist();
+    }
+
+    public void connectNetworkWithId(int networkId) {
+        enforceChangePermission();
+        mWifiStateMachine.connectNetwork(networkId);
+    }
+
+    public void connectNetworkWithConfig(WifiConfiguration config) {
+        enforceChangePermission();
+        mWifiStateMachine.connectNetwork(config);
+    }
+
+    public void saveNetwork(WifiConfiguration config) {
+        enforceChangePermission();
+        mWifiStateMachine.saveNetwork(config);
+    }
+
+    public void forgetNetwork(int netId) {
+        enforceChangePermission();
+        mWifiStateMachine.forgetNetwork(netId);
+    }
+
+    public void startWpsPbc(String bssid) {
+        enforceChangePermission();
+        mWifiStateMachine.startWpsPbc(bssid);
+    }
+
+    public void startWpsWithPinFromAccessPoint(String bssid, int apPin) {
+        enforceChangePermission();
+        mWifiStateMachine.startWpsWithPinFromAccessPoint(bssid, apPin);
+    }
+
+    public int startWpsWithPinFromDevice(String bssid) {
+        enforceChangePermission();
+        if (mChannel != null) {
+            return mWifiStateMachine.syncStartWpsWithPinFromDevice(mChannel, bssid);
+        } else {
+            Slog.e(TAG, "mChannel is not initialized");
+            return -1;
+        }
     }
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -1651,7 +838,7 @@
 
             long idleMillis =
                 Settings.Secure.getLong(mContext.getContentResolver(),
-                                        Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS);
+                                        Settings.Secure.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
             int stayAwakeConditions =
                 Settings.System.getInt(mContext.getContentResolver(),
                                        Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0);
@@ -1665,19 +852,14 @@
                 // Once the screen is on, we are not keeping WIFI running
                 // because of any locks so clear that tracking immediately.
                 reportStartWorkSource();
-                mWifiStateTracker.enableRssiPolling(true);
-                /* DHCP or other temporary failures in the past can prevent
-                 * a disabled network from being connected to, enable on screen on
-                 */
-                if (mWifiStateTracker.isAnyNetworkDisabled()) {
-                    sendEnableNetworksMessage();
-                }
+                mWifiStateMachine.enableRssiPolling(true);
+                updateWifiState();
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                 if (DBG) {
                     Slog.d(TAG, "ACTION_SCREEN_OFF");
                 }
                 mScreenOff = true;
-                mWifiStateTracker.enableRssiPolling(false);
+                mWifiStateMachine.enableRssiPolling(false);
                 /*
                  * Set a timer to put Wi-Fi to sleep, but only if the screen is off
                  * AND the "stay on while plugged in" setting doesn't match the
@@ -1685,11 +867,11 @@
                  * or plugged in to AC).
                  */
                 if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
-                    WifiInfo info = mWifiStateTracker.requestConnectionInfo();
+                    WifiInfo info = mWifiStateMachine.syncRequestConnectionInfo();
                     if (info.getSupplicantState() != SupplicantState.COMPLETED) {
                         // we used to go to sleep immediately, but this caused some race conditions
-                        // we don't have time to track down for this release.  Delay instead, but not
-                        // as long as we would if connected (below)
+                        // we don't have time to track down for this release.  Delay instead,
+                        // but not as long as we would if connected (below)
                         // TODO - fix the race conditions and switch back to the immediate turn-off
                         long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min
                         if (DBG) {
@@ -1708,14 +890,13 @@
                         mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
                     }
                 }
-                /* we can return now -- there's nothing to do until we get the idle intent back */
-                return;
             } else if (action.equals(ACTION_DEVICE_IDLE)) {
                 if (DBG) {
                     Slog.d(TAG, "got ACTION_DEVICE_IDLE");
                 }
                 mDeviceIdle = true;
                 reportStartWorkSource();
+                updateWifiState();
             } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
                 /*
                  * Set a timer to put Wi-Fi to sleep, but only if the screen is off
@@ -1735,26 +916,13 @@
                         Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for " + idleMillis + "ms");
                     }
                     mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
-                    mPluggedType = pluggedType;
-                    return;
                 }
                 mPluggedType = pluggedType;
-            } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
-                BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
-                Set<BluetoothDevice> sinks = a2dp.getConnectedSinks();
-                boolean isBluetoothPlaying = false;
-                for (BluetoothDevice sink : sinks) {
-                    if (a2dp.getSinkState(sink) == BluetoothA2dp.STATE_PLAYING) {
-                        isBluetoothPlaying = true;
-                    }
-                }
-                mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
-
-            } else {
-                return;
+            } else if (action.equals(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED)) {
+                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
+                                               BluetoothA2dp.STATE_NOT_PLAYING);
+                mWifiStateMachine.setBluetoothScanMode(state == BluetoothA2dp.STATE_PLAYING);
             }
-
-            updateWifiState();
         }
 
         /**
@@ -1787,7 +955,7 @@
          * of {@code 0} isn't really a plugged type, but rather an indication that the
          * device isn't plugged in at all, there is no bit value corresponding to a
          * {@code pluggedType} value of {@code 0}. That is why we shift by
-         * {@code pluggedType&nbsp;&#8212;&nbsp;1} instead of by {@code pluggedType}.
+         * {@code pluggedType - 1} instead of by {@code pluggedType}.
          * @param stayAwakeConditions a bit string specifying which "plugged types" should
          * keep the device (and hence Wi-Fi) awake.
          * @param pluggedType the type of plug (USB, AC, or none) for which the check is
@@ -1800,54 +968,20 @@
         }
     };
 
-    private void sendEnableMessage(boolean enable, boolean persist, int uid) {
-        Message msg = Message.obtain(mWifiHandler,
-                                     (enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI),
-                                     (persist ? 1 : 0), uid);
-        msg.sendToTarget();
-    }
-
-    private void sendStartMessage(int lockMode) {
-        Message.obtain(mWifiHandler, MESSAGE_START_WIFI, lockMode, 0).sendToTarget();
-    }
-
-    private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) {
-        Message.obtain(mWifiHandler,
-                (enable ? MESSAGE_START_ACCESS_POINT : MESSAGE_STOP_ACCESS_POINT),
-                uid, 0, wifiConfig).sendToTarget();
-    }
-
-    private void sendEnableNetworksMessage() {
-        Message.obtain(mWifiHandler, MESSAGE_ENABLE_NETWORKS).sendToTarget();
-    }
-
-    private void reportStartWorkSource() {
-        synchronized (mWifiStateTracker) {
-            mTmpWorkSource.clear();
-            if (mDeviceIdle) {
-                for (int i=0; i<mLocks.mList.size(); i++) {
-                    mTmpWorkSource.add(mLocks.mList.get(i).mWorkSource);
-                }
+    private synchronized void reportStartWorkSource() {
+        mTmpWorkSource.clear();
+        if (mDeviceIdle) {
+            for (int i=0; i<mLocks.mList.size(); i++) {
+                mTmpWorkSource.add(mLocks.mList.get(i).mWorkSource);
             }
-            mWifiStateTracker.updateBatteryWorkSourceLocked(mTmpWorkSource);
-            sWakeLock.setWorkSource(mTmpWorkSource);
         }
+        mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
     }
 
     private void updateWifiState() {
-        // send a message so it's all serialized
-        Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget();
-    }
-
-    private void doUpdateWifiState() {
         boolean wifiEnabled = getPersistedWifiEnabled();
-        boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
-
-        boolean lockHeld;
-        synchronized (mLocks) {
-            lockHeld = mLocks.hasLocks();
-        }
-
+        boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden.get();
+        boolean lockHeld = mLocks.hasLocks();
         int strongestLockMode = WifiManager.WIFI_MODE_FULL;
         boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
         boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
@@ -1860,43 +994,26 @@
             strongestLockMode = WifiManager.WIFI_MODE_FULL;
         }
 
-        synchronized (mWifiHandler) {
-            if ((mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLING) && !airplaneMode) {
-                return;
-            }
+        /* Disable tethering when airplane mode is enabled */
+        if (airplaneMode) {
+            mWifiStateMachine.setWifiApEnabled(null, false);
+        }
 
-            /* Disable tethering when airplane mode is enabled */
-            if (airplaneMode &&
-                (mWifiApState == WIFI_AP_STATE_ENABLING || mWifiApState == WIFI_AP_STATE_ENABLED)) {
-                sWakeLock.acquire();
-                sendAccessPointMessage(false, null, mLastApEnableUid);
-            }
-
-            if (wifiShouldBeEnabled) {
-                if (wifiShouldBeStarted) {
-                    sWakeLock.acquire();
-                    sendEnableMessage(true, false, mLastEnableUid);
-                    sWakeLock.acquire();
-                    sendStartMessage(strongestLockMode);
-                } else if (!mWifiStateTracker.isDriverStopped()) {
-                    int wakeLockTimeout =
-                            Settings.Secure.getInt(
-                                    mContext.getContentResolver(),
-                                    Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS,
-                                    DEFAULT_WAKELOCK_TIMEOUT);
-                    /*
-                     * We are assuming that ConnectivityService can make
-                     * a transition to cellular data within wakeLockTimeout time.
-                     * The wakelock is released by the delayed message.
-                     */
-                    sDriverStopWakeLock.acquire();
-                    mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI);
-                    mWifiHandler.sendEmptyMessageDelayed(MESSAGE_RELEASE_WAKELOCK, wakeLockTimeout);
-                }
+        if (wifiShouldBeEnabled) {
+            if (wifiShouldBeStarted) {
+                reportStartWorkSource();
+                mWifiStateMachine.setWifiEnabled(true);
+                mWifiStateMachine.setScanOnlyMode(
+                        strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
+                mWifiStateMachine.setDriverStart(true);
+                mWifiStateMachine.setHighPerfModeEnabled(strongestLockMode
+                        == WifiManager.WIFI_MODE_FULL_HIGH_PERF);
             } else {
-                sWakeLock.acquire();
-                sendEnableMessage(false, false, mLastEnableUid);
+                mWifiStateMachine.requestCmWakeLock();
+                mWifiStateMachine.setDriverStart(false);
             }
+        } else {
+            mWifiStateMachine.setWifiEnabled(false);
         }
     }
 
@@ -1906,7 +1023,7 @@
         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
         intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
         intentFilter.addAction(ACTION_DEVICE_IDLE);
-        intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
+        intentFilter.addAction(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
         mContext.registerReceiver(mReceiver, intentFilter);
     }
 
@@ -1934,97 +1051,6 @@
                 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
     }
 
-    /**
-     * Handler that allows posting to the WifiThread.
-     */
-    private class WifiHandler extends Handler {
-        public WifiHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-
-                case MESSAGE_ENABLE_WIFI:
-                    setWifiEnabledBlocking(true, msg.arg1 == 1, msg.arg2);
-                    if (mWifiWatchdogService == null) {
-                        mWifiWatchdogService = new WifiWatchdogService(mContext, mWifiStateTracker);
-                    }
-                    sWakeLock.release();
-                    break;
-
-                case MESSAGE_START_WIFI:
-                    reportStartWorkSource();
-                    mWifiStateTracker.setScanOnlyMode(msg.arg1 == WifiManager.WIFI_MODE_SCAN_ONLY);
-                    mWifiStateTracker.restart();
-                    mWifiStateTracker.setHighPerfMode(msg.arg1 ==
-                            WifiManager.WIFI_MODE_FULL_HIGH_PERF);
-                    sWakeLock.release();
-                    break;
-
-                case MESSAGE_UPDATE_STATE:
-                    doUpdateWifiState();
-                    break;
-
-                case MESSAGE_DISABLE_WIFI:
-                    // a non-zero msg.arg1 value means the "enabled" setting
-                    // should be persisted
-                    setWifiEnabledBlocking(false, msg.arg1 == 1, msg.arg2);
-                    mWifiWatchdogService = null;
-                    sWakeLock.release();
-                    break;
-
-                case MESSAGE_STOP_WIFI:
-                    mWifiStateTracker.disconnectAndStop();
-                    // don't release wakelock
-                    break;
-
-                case MESSAGE_RELEASE_WAKELOCK:
-                    sDriverStopWakeLock.release();
-                    break;
-
-                case MESSAGE_START_ACCESS_POINT:
-                    setWifiApEnabledBlocking(true,
-                                             msg.arg1,
-                                             (WifiConfiguration) msg.obj);
-                    sWakeLock.release();
-                    break;
-
-                case MESSAGE_STOP_ACCESS_POINT:
-                    setWifiApEnabledBlocking(false,
-                                             msg.arg1,
-                                             (WifiConfiguration) msg.obj);
-                    sWakeLock.release();
-                    break;
-
-                case MESSAGE_SET_CHANNELS:
-                    setNumAllowedChannelsBlocking(msg.arg1, msg.arg2 == 1);
-                    break;
-
-                case MESSAGE_ENABLE_NETWORKS:
-                    mWifiStateTracker.enableAllNetworks(getConfiguredNetworks());
-                    break;
-
-                case MESSAGE_START_SCAN:
-                    boolean forceActive = (msg.arg1 == 1);
-                    switch (mWifiStateTracker.getSupplicantState()) {
-                        case DISCONNECTED:
-                        case INACTIVE:
-                        case SCANNING:
-                        case DORMANT:
-                            break;
-                        default:
-                            mWifiStateTracker.setScanResultHandling(
-                                    WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY);
-                            break;
-                    }
-                    mWifiStateTracker.scan(forceActive);
-                    break;
-            }
-        }
-    }
-
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -2034,17 +1060,17 @@
                     + ", uid=" + Binder.getCallingUid());
             return;
         }
-        pw.println("Wi-Fi is " + stateName(mWifiStateTracker.getWifiState()));
+        pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
         pw.println("Stay-awake conditions: " +
                 Settings.System.getInt(mContext.getContentResolver(),
                                        Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
         pw.println();
 
         pw.println("Internal state:");
-        pw.println(mWifiStateTracker);
+        pw.println(mWifiStateMachine);
         pw.println();
         pw.println("Latest scan results:");
-        List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList();
+        List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
         if (scanResults != null && scanResults.size() != 0) {
             pw.println("  BSSID              Frequency   RSSI  Flags             SSID");
             for (ScanResult r : scanResults) {
@@ -2068,23 +1094,6 @@
         mLocks.dump(pw);
     }
 
-    private static String stateName(int wifiState) {
-        switch (wifiState) {
-            case WIFI_STATE_DISABLING:
-                return "disabling";
-            case WIFI_STATE_DISABLED:
-                return "disabled";
-            case WIFI_STATE_ENABLING:
-                return "enabling";
-            case WIFI_STATE_ENABLED:
-                return "enabled";
-            case WIFI_STATE_UNKNOWN:
-                return "unknown state";
-            default:
-                return "[invalid state]";
-        }
-    }
-
     private class WifiLock extends DeathRecipient {
         WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
             super(lockMode, tag, binder, ws);
@@ -2162,7 +1171,7 @@
     }
 
     void enforceWakeSourcePermission(int uid, int pid) {
-        if (uid == Process.myUid()) {
+        if (uid == android.os.Process.myUid()) {
             return;
         }
         mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
@@ -2196,10 +1205,7 @@
     private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException {
         switch(wifiLock.mMode) {
             case WifiManager.WIFI_MODE_FULL:
-                mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
-                break;
             case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
-                /* Treat high power as a full lock for battery stats */
                 mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
                 break;
             case WifiManager.WIFI_MODE_SCAN_ONLY:
@@ -2211,10 +1217,7 @@
     private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException {
         switch(wifiLock.mMode) {
             case WifiManager.WIFI_MODE_FULL:
-                mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
-                break;
             case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
-                /* Treat high power as a full lock for battery stats */
                 mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
                 break;
             case WifiManager.WIFI_MODE_SCAN_ONLY:
@@ -2238,6 +1241,7 @@
             case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
                 ++mFullHighPerfLocksAcquired;
                 break;
+
             case WifiManager.WIFI_MODE_SCAN_ONLY:
                 ++mScanLocksAcquired;
                 break;
@@ -2384,7 +1388,7 @@
             if (mMulticasters.size() != 0) {
                 return;
             } else {
-                mWifiStateTracker.startPacketFiltering();
+                mWifiStateMachine.startPacketFiltering();
             }
         }
     }
@@ -2399,7 +1403,7 @@
             // our new size == 1 (first call), but this function won't
             // be called often and by making the stopPacket call each
             // time we're less fragile and self-healing.
-            mWifiStateTracker.stopPacketFiltering();
+            mWifiStateMachine.stopPacketFiltering();
         }
 
         int uid = Binder.getCallingUid();
@@ -2436,7 +1440,7 @@
             removed.unlinkDeathRecipient();
         }
         if (mMulticasters.size() == 0) {
-            mWifiStateTracker.startPacketFiltering();
+            mWifiStateMachine.startPacketFiltering();
         }
 
         Long ident = Binder.clearCallingIdentity();
@@ -2455,4 +1459,144 @@
             return (mMulticasters.size() > 0);
         }
     }
+
+    private void checkAndSetNotification() {
+        // If we shouldn't place a notification on available networks, then
+        // don't bother doing any of the following
+        if (!mNotificationEnabled) return;
+
+        State state = mNetworkInfo.getState();
+        if ((state == NetworkInfo.State.DISCONNECTED)
+                || (state == NetworkInfo.State.UNKNOWN)) {
+            // Look for an open network
+            List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
+            if (scanResults != null) {
+                int numOpenNetworks = 0;
+                for (int i = scanResults.size() - 1; i >= 0; i--) {
+                    ScanResult scanResult = scanResults.get(i);
+
+                    if (TextUtils.isEmpty(scanResult.capabilities)) {
+                        numOpenNetworks++;
+                    }
+                }
+
+                if (numOpenNetworks > 0) {
+                    if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
+                        /*
+                         * We've scanned continuously at least
+                         * NUM_SCANS_BEFORE_NOTIFICATION times. The user
+                         * probably does not have a remembered network in range,
+                         * since otherwise supplicant would have tried to
+                         * associate and thus resetting this counter.
+                         */
+                        setNotificationVisible(true, numOpenNetworks, false, 0);
+                    }
+                    return;
+                }
+            }
+        }
+
+        // No open networks in range, remove the notification
+        setNotificationVisible(false, 0, false, 0);
+    }
+
+    /**
+     * Clears variables related to tracking whether a notification has been
+     * shown recently and clears the current notification.
+     */
+    private void resetNotification() {
+        mNotificationRepeatTime = 0;
+        mNumScansSinceNetworkStateChange = 0;
+        setNotificationVisible(false, 0, false, 0);
+    }
+
+    /**
+     * Display or don't display a notification that there are open Wi-Fi networks.
+     * @param visible {@code true} if notification should be visible, {@code false} otherwise
+     * @param numNetworks the number networks seen
+     * @param force {@code true} to force notification to be shown/not-shown,
+     * even if it is already shown/not-shown.
+     * @param delay time in milliseconds after which the notification should be made
+     * visible or invisible.
+     */
+    private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
+            int delay) {
+
+        // Since we use auto cancel on the notification, when the
+        // mNetworksAvailableNotificationShown is true, the notification may
+        // have actually been canceled.  However, when it is false we know
+        // for sure that it is not being shown (it will not be shown any other
+        // place than here)
+
+        // If it should be hidden and it is already hidden, then noop
+        if (!visible && !mNotificationShown && !force) {
+            return;
+        }
+
+        NotificationManager notificationManager = (NotificationManager) mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+
+        Message message;
+        if (visible) {
+
+            // Not enough time has passed to show the notification again
+            if (System.currentTimeMillis() < mNotificationRepeatTime) {
+                return;
+            }
+
+            if (mNotification == null) {
+                // Cache the Notification object.
+                mNotification = new Notification();
+                mNotification.when = 0;
+                mNotification.icon = ICON_NETWORKS_AVAILABLE;
+                mNotification.flags = Notification.FLAG_AUTO_CANCEL;
+                mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
+                        new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
+            }
+
+            CharSequence title = mContext.getResources().getQuantityText(
+                    com.android.internal.R.plurals.wifi_available, numNetworks);
+            CharSequence details = mContext.getResources().getQuantityText(
+                    com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
+            mNotification.tickerText = title;
+            mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
+
+            mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
+
+            notificationManager.notify(ICON_NETWORKS_AVAILABLE, mNotification);
+        } else {
+            notificationManager.cancel(ICON_NETWORKS_AVAILABLE);
+        }
+
+        mNotificationShown = visible;
+    }
+
+    private class NotificationEnabledSettingObserver extends ContentObserver {
+
+        public NotificationEnabledSettingObserver(Handler handler) {
+            super(handler);
+        }
+
+        public void register() {
+            ContentResolver cr = mContext.getContentResolver();
+            cr.registerContentObserver(Settings.Secure.getUriFor(
+                Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
+            mNotificationEnabled = getValue();
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            super.onChange(selfChange);
+
+            mNotificationEnabled = getValue();
+            resetNotification();
+        }
+
+        private boolean getValue() {
+            return Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
+        }
+    }
+
+
 }
diff --git a/services/java/com/android/server/WifiStateTracker.java b/services/java/com/android/server/WifiStateTracker.java
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/services/java/com/android/server/WifiStateTracker.java
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index 445dd03..46d6bef 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -27,7 +27,6 @@
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.net.wifi.WifiStateTracker;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -77,7 +76,6 @@
     
     private Context mContext;
     private ContentResolver mContentResolver;
-    private WifiStateTracker mWifiStateTracker;
     private WifiManager mWifiManager;
     
     /**
@@ -108,10 +106,9 @@
     /** Whether the current AP check should be canceled. */
     private boolean mShouldCancel;
     
-    WifiWatchdogService(Context context, WifiStateTracker wifiStateTracker) {
+    WifiWatchdogService(Context context) {
         mContext = context;
         mContentResolver = context.getContentResolver();
-        mWifiStateTracker = wifiStateTracker;
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
         
         createThread();
@@ -275,12 +272,13 @@
     /**
      * Unregister broadcasts and quit the watchdog thread
      */
-    private void quit() {
-        unregisterForWifiBroadcasts();
-        mContext.getContentResolver().unregisterContentObserver(mContentObserver);
-        mHandler.removeAllActions();
-        mHandler.getLooper().quit();
-    }
+    //TODO: Change back to running WWS when needed
+//    private void quit() {
+//        unregisterForWifiBroadcasts();
+//        mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+//        mHandler.removeAllActions();
+//        mHandler.getLooper().quit();
+//    }
 
     /**
      * Waits for the main watchdog thread to create the handler.
@@ -751,7 +749,7 @@
         // Black list this "bad" AP, this will cause an attempt to connect to another
         blacklistAp(ap.bssid);
         // Initiate an association to an alternate AP
-        mWifiStateTracker.reassociate();
+        mWifiManager.reassociate();
     }
 
     private void blacklistAp(String bssid) {
@@ -762,10 +760,7 @@
         // Before taking action, make sure we should not cancel our processing
         if (shouldCancel()) return;
         
-        if (!mWifiStateTracker.addToBlacklist(bssid)) {
-            // There's a known bug where this method returns failure on success
-            //Slog.e(TAG, "Blacklisting " + bssid + " failed");
-        }
+        mWifiManager.addToBlacklist(bssid);
 
         if (D) {
             myLogD("Blacklisting " + bssid);
@@ -860,10 +855,7 @@
              * (and blacklisted them). Clear the blacklist so the AP with best
              * signal is chosen.
              */
-            if (!mWifiStateTracker.clearBlacklist()) {
-                // There's a known bug where this method returns failure on success
-                //Slog.e(TAG, "Clearing blacklist failed");
-            }
+            mWifiManager.clearBlacklist();
             
             if (V) {
                 myLogV("handleSleep: Set state to SLEEP and cleared blacklist");
@@ -934,7 +926,7 @@
      * should revert anything done by the watchdog monitoring.
      */
     private void handleReset() {
-        mWifiStateTracker.clearBlacklist();
+        mWifiManager.clearBlacklist();
         setIdleState(true);
     }
     
@@ -1151,7 +1143,7 @@
 
         private void handleWifiStateChanged(int wifiState) {
             if (wifiState == WifiManager.WIFI_STATE_DISABLED) {
-                quit();
+                onDisconnected();
             } else if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
                 onEnabled();
             }
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 9ca53c6..90cdb4b 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -39,6 +40,7 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.policy.impl.PhoneWindowManager;
+import com.android.internal.view.BaseInputHandler;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
@@ -50,6 +52,8 @@
 import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
+import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -61,6 +65,7 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Typeface;
@@ -92,6 +97,7 @@
 import android.util.SparseIntArray;
 import android.util.TypedValue;
 import android.view.Display;
+import android.view.DragEvent;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.IApplicationToken;
@@ -103,6 +109,8 @@
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
+import android.view.InputHandler;
+import android.view.InputQueue;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -158,7 +166,7 @@
     static final boolean DEBUG_STARTING_WINDOW = false;
     static final boolean DEBUG_REORDER = false;
     static final boolean DEBUG_WALLPAPER = false;
-    static final boolean DEBUG_FREEZE = false;
+    static final boolean DEBUG_DRAG = true;
     static final boolean SHOW_TRANSACTIONS = false;
     static final boolean HIDE_STACK_CRAWLS = true;
 
@@ -486,6 +494,387 @@
     boolean mTurnOnScreen;
 
     /**
+     * Drag/drop state
+     */
+    class DragState {
+        IBinder mToken;
+        Surface mSurface;
+        boolean mLocalOnly;
+        IBinder mLocalWin;
+        ClipData mData;
+        ClipDescription mDataDescription;
+        boolean mDragResult;
+        float mCurrentX, mCurrentY;
+        float mThumbOffsetX, mThumbOffsetY;
+        InputChannel mServerChannel, mClientChannel;
+        WindowState mTargetWindow;
+        ArrayList<WindowState> mNotifiedWindows;
+        boolean mDragInProgress;
+
+        private final Rect tmpRect = new Rect();
+
+        DragState(IBinder token, Surface surface, boolean localOnly, IBinder localWin) {
+            mToken = token;
+            mSurface = surface;
+            mLocalOnly = localOnly;
+            mLocalWin = localWin;
+            mNotifiedWindows = new ArrayList<WindowState>();
+        }
+
+        void reset() {
+            if (mSurface != null) {
+                mSurface.destroy();
+            }
+            mSurface = null;
+            mLocalOnly = false;
+            mLocalWin = null;
+            mToken = null;
+            mData = null;
+            mThumbOffsetX = mThumbOffsetY = 0;
+            mNotifiedWindows = null;
+        }
+
+        void register() {
+            if (DEBUG_DRAG) Slog.d(TAG, "registering drag input channel");
+            if (mClientChannel != null) {
+                Slog.e(TAG, "Duplicate register of drag input channel");
+            } else {
+                InputChannel[] channels = InputChannel.openInputChannelPair("drag");
+                mServerChannel = channels[0];
+                mClientChannel = channels[1];
+                mInputManager.registerInputChannel(mServerChannel);
+                InputQueue.registerInputChannel(mClientChannel, mDragInputHandler,
+                        mH.getLooper().getQueue());
+            }
+        }
+
+        void unregister() {
+            if (DEBUG_DRAG) Slog.d(TAG, "unregistering drag input channel");
+            if (mClientChannel == null) {
+                Slog.e(TAG, "Unregister of nonexistent drag input channel");
+            } else {
+                mInputManager.unregisterInputChannel(mServerChannel);
+                InputQueue.unregisterInputChannel(mClientChannel);
+                mClientChannel.dispose();
+                mServerChannel.dispose();
+                mClientChannel = null;
+                mServerChannel = null;
+            }
+        }
+
+        int getDragLayerLw() {
+            return mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
+                    * TYPE_LAYER_MULTIPLIER
+                    + TYPE_LAYER_OFFSET;
+        }
+
+        /* call out to each visible window/session informing it about the drag
+         */
+        void broadcastDragStartedLw(final float touchX, final float touchY) {
+            // Cache a base-class instance of the clip metadata so that parceling
+            // works correctly in calling out to the apps.
+            mDataDescription = mData.getDescription();
+            mNotifiedWindows.clear();
+            mDragInProgress = true;
+
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
+            }
+
+            final int N = mWindows.size();
+            for (int i = 0; i < N; i++) {
+                sendDragStartedLw(mWindows.get(i), touchX, touchY, mDataDescription);
+            }
+        }
+
+        /* helper - send a caller-provided event, presumed to be DRAG_STARTED, if the
+         * designated window is potentially a drop recipient.  There are race situations
+         * around DRAG_ENDED broadcast, so we make sure that once we've declared that
+         * the drag has ended, we never send out another DRAG_STARTED for this drag action.
+         *
+         * This method clones the 'event' parameter if it's being delivered to the same
+         * process, so it's safe for the caller to call recycle() on the event afterwards.
+         */
+        private void sendDragStartedLw(WindowState newWin, float touchX, float touchY,
+                ClipDescription desc) {
+            // Don't actually send the event if the drag is supposed to be pinned
+            // to the originating window but 'newWin' is not that window.
+            if (mLocalOnly) {
+                final IBinder winBinder = newWin.mClient.asBinder();
+                if (winBinder != mLocalWin) {
+                    if (DEBUG_DRAG) {
+                        Slog.d(TAG, "Not dispatching local DRAG_STARTED to " + newWin);
+                    }
+                    return;
+                }
+            }
+
+            if (mDragInProgress && newWin.isPotentialDragTarget()) {
+                DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED,
+                        touchX - newWin.mFrame.left, touchY - newWin.mFrame.top,
+                        desc, null, false);
+                try {
+                    newWin.mClient.dispatchDragEvent(event);
+                    // track each window that we've notified that the drag is starting
+                    mNotifiedWindows.add(newWin);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Unable to drag-start window " + newWin);
+                } finally {
+                    // if the callee was local, the dispatch has already recycled the event
+                    if (Process.myPid() != newWin.mSession.mPid) {
+                        event.recycle();
+                    }
+                }
+            }
+        }
+
+        /* helper - construct and send a DRAG_STARTED event only if the window has not
+         * previously been notified, i.e. it became visible after the drag operation
+         * was begun.  This is a rare case.
+         */
+        private void sendDragStartedIfNeededLw(WindowState newWin) {
+            if (mDragInProgress) {
+                // If we have sent the drag-started, we needn't do so again
+                for (WindowState ws : mNotifiedWindows) {
+                    if (ws == newWin) {
+                        return;
+                    }
+                }
+                if (DEBUG_DRAG) {
+                    Slog.d(TAG, "need to send DRAG_STARTED to new window " + newWin);
+                }
+                sendDragStartedLw(newWin, mCurrentX, mCurrentY, mDataDescription);
+            }
+        }
+
+        void broadcastDragEndedLw() {
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "broadcasting DRAG_ENDED");
+            }
+            DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
+                    0, 0, null, null, mDragResult);
+            for (WindowState ws: mNotifiedWindows) {
+                try {
+                    ws.mClient.dispatchDragEvent(evt);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Unable to drag-end window " + ws);
+                }
+            }
+            mNotifiedWindows.clear();
+            mDragInProgress = false;
+            evt.recycle();
+        }
+
+        void endDragLw() {
+            mDragState.broadcastDragEndedLw();
+
+            // stop intercepting input
+            mDragState.unregister();
+            mInputMonitor.updateInputWindowsLw();
+
+            // free our resources and drop all the object references
+            mDragState.reset();
+            mDragState = null;
+        }
+
+        void notifyMoveLw(float x, float y) {
+            final int myPid = Process.myPid();
+
+            // Move the surface to the given touch
+            mSurface.openTransaction();
+            mSurface.setPosition((int)(x - mThumbOffsetX), (int)(y - mThumbOffsetY));
+            mSurface.closeTransaction();
+
+            // Tell the affected window
+            WindowState touchedWin = getTouchedWinAtPointLw(x, y);
+            if (mLocalOnly) {
+                final IBinder touchedBinder = touchedWin.mClient.asBinder();
+                if (touchedBinder != mLocalWin) {
+                    // This drag is pinned only to the originating window, but the drag
+                    // point is outside that window.  Pretend it's over empty space.
+                    touchedWin = null;
+                }
+            }
+            try {
+                // have we dragged over a new window?
+                if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
+                    if (DEBUG_DRAG) {
+                        Slog.d(TAG, "sending DRAG_EXITED to " + mTargetWindow);
+                    }
+                    // force DRAG_EXITED_EVENT if appropriate
+                    DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED,
+                            x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top,
+                            null, null, false);
+                    mTargetWindow.mClient.dispatchDragEvent(evt);
+                    if (myPid != mTargetWindow.mSession.mPid) {
+                        evt.recycle();
+                    }
+                }
+                if (touchedWin != null) {
+                    if (false && DEBUG_DRAG) {
+                        Slog.d(TAG, "sending DRAG_LOCATION to " + touchedWin);
+                    }
+                    DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION,
+                            x - touchedWin.mFrame.left, y - touchedWin.mFrame.top,
+                            null, null, false);
+                    touchedWin.mClient.dispatchDragEvent(evt);
+                    if (myPid != touchedWin.mSession.mPid) {
+                        evt.recycle();
+                    }
+                }
+            } catch (RemoteException e) {
+                Slog.w(TAG, "can't send drag notification to windows");
+            }
+            mTargetWindow = touchedWin;
+        }
+
+        // Tell the drop target about the data.  Returns 'true' if we can immediately
+        // dispatch the global drag-ended message, 'false' if we need to wait for a
+        // result from the recipient.
+        boolean notifyDropLw(float x, float y) {
+            WindowState touchedWin = getTouchedWinAtPointLw(x, y);
+            if (touchedWin == null) {
+                // "drop" outside a valid window -- no recipient to apply a
+                // timeout to, and we can send the drag-ended message immediately.
+                mDragResult = false;
+                return true;
+            }
+
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "sending DROP to " + touchedWin);
+            }
+            final int myPid = Process.myPid();
+            final IBinder token = touchedWin.mClient.asBinder();
+            DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP,
+                    x - touchedWin.mFrame.left, y - touchedWin.mFrame.top,
+                    null, mData, false);
+            try {
+                touchedWin.mClient.dispatchDragEvent(evt);
+
+                // 5 second timeout for this window to respond to the drop
+                mH.removeMessages(H.DRAG_END_TIMEOUT, token);
+                Message msg = mH.obtainMessage(H.DRAG_END_TIMEOUT, token);
+                mH.sendMessageDelayed(msg, 5000);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "can't send drop notification to win " + touchedWin);
+                return true;
+            } finally {
+                if (myPid != touchedWin.mSession.mPid) {
+                    evt.recycle();
+                }
+            }
+            mToken = token;
+            return false;
+        }
+
+        // Find the visible, touch-deliverable window under the given point
+        private WindowState getTouchedWinAtPointLw(float xf, float yf) {
+            WindowState touchedWin = null;
+            final int x = (int) xf;
+            final int y = (int) yf;
+            final ArrayList<WindowState> windows = mWindows;
+            final int N = windows.size();
+            for (int i = N - 1; i >= 0; i--) {
+                WindowState child = windows.get(i);
+                final int flags = child.mAttrs.flags;
+                if (!child.isVisibleLw()) {
+                    // not visible == don't tell about drags
+                    continue;
+                }
+                if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
+                    // not touchable == don't tell about drags
+                    continue;
+                }
+                // account for the window's decor etc
+                tmpRect.set(child.mFrame);
+                if (child.mTouchableInsets == ViewTreeObserver
+                            .InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
+                    // The point is inside of the window if it is
+                    // inside the frame, AND the content part of that
+                    // frame that was given by the application.
+                    tmpRect.left += child.mGivenContentInsets.left;
+                    tmpRect.top += child.mGivenContentInsets.top;
+                    tmpRect.right -= child.mGivenContentInsets.right;
+                    tmpRect.bottom -= child.mGivenContentInsets.bottom;
+                } else if (child.mTouchableInsets == ViewTreeObserver
+                            .InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
+                    // The point is inside of the window if it is
+                    // inside the frame, AND the visible part of that
+                    // frame that was given by the application.
+                    tmpRect.left += child.mGivenVisibleInsets.left;
+                    tmpRect.top += child.mGivenVisibleInsets.top;
+                    tmpRect.right -= child.mGivenVisibleInsets.right;
+                    tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
+                }
+                final int touchFlags = flags &
+                    (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+                if (tmpRect.contains(x, y) || touchFlags == 0) {
+                    // Found it
+                    touchedWin = child;
+                    break;
+                }
+            }
+
+            return touchedWin;
+        }
+    }
+
+    DragState mDragState = null;
+    private final InputHandler mDragInputHandler = new BaseInputHandler() {
+        @Override
+        public void handleMotion(MotionEvent event, Runnable finishedCallback) {
+            boolean endDrag = false;
+            final float newX = event.getRawX();
+            final float newY = event.getRawY();
+
+            try {
+                if (mDragState != null) {
+                    switch (event.getAction()) {
+                    case MotionEvent.ACTION_DOWN: {
+                        if (DEBUG_DRAG) {
+                            Slog.w(TAG, "Unexpected ACTION_DOWN in drag layer");
+                        }
+                    } break;
+
+                    case MotionEvent.ACTION_MOVE: {
+                        synchronized (mWindowMap) {
+                            // move the surface and tell the involved window(s) where we are
+                            mDragState.notifyMoveLw(newX, newY);
+                        }
+                    } break;
+
+                    case MotionEvent.ACTION_UP: {
+                        if (DEBUG_DRAG) Slog.d(TAG, "Got UP on move channel; dropping at "
+                                + newX + "," + newY);
+                        synchronized (mWindowMap) {
+                            endDrag = mDragState.notifyDropLw(newX, newY);
+                        }
+                    } break;
+
+                    case MotionEvent.ACTION_CANCEL: {
+                        if (DEBUG_DRAG) Slog.d(TAG, "Drag cancelled!");
+                        endDrag = true;
+                    } break;
+                    }
+
+                    if (endDrag) {
+                        if (DEBUG_DRAG) Slog.d(TAG, "Drag ended; tearing down state");
+                        // tell all the windows that the drag has ended
+                        synchronized (mWindowMap) {
+                            mDragState.endDragLw();
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                Slog.e(TAG, "Exception caught by drag handleMotion", e);
+            } finally {
+                finishedCallback.run();
+            }
+        }
+    };
+
+    /**
      * Whether the UI is currently running in touch mode (not showing
      * navigational focus because the user is directly pressing the screen).
      */
@@ -1789,18 +2178,11 @@
         boolean reportNewConfig = false;
         WindowState attachedWindow = null;
         WindowState win = null;
+        long origId;
 
         synchronized(mWindowMap) {
-            // Instantiating a Display requires talking with the simulator,
-            // so don't do it until we know the system is mostly up and
-            // running.
             if (mDisplay == null) {
-                WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
-                mDisplay = wm.getDefaultDisplay();
-                mInitialDisplayWidth = mDisplay.getWidth();
-                mInitialDisplayHeight = mDisplay.getHeight();
-                mInputManager.setDisplaySize(0, mInitialDisplayWidth, mInitialDisplayHeight);
-                reportNewConfig = true;
+                throw new IllegalStateException("Display has not been initialialized");
             }
 
             if (mWindowMap.containsKey(client.asBinder())) {
@@ -1906,7 +2288,7 @@
 
             res = WindowManagerImpl.ADD_OKAY;
 
-            final long origId = Binder.clearCallingIdentity();
+            origId = Binder.clearCallingIdentity();
 
             if (addToken) {
                 mTokenMap.put(attrs.token, token);
@@ -1983,14 +2365,10 @@
             }
         }
 
-        // sendNewConfiguration() checks caller permissions so we must call it with
-        // privilege.  updateOrientationFromAppTokens() clears and resets the caller
-        // identity anyway, so it's safe to just clear & restore around this whole
-        // block.
-        final long origId = Binder.clearCallingIdentity();
         if (reportNewConfig) {
             sendNewConfiguration();
         }
+
         Binder.restoreCallingIdentity(origId);
 
         return res;
@@ -4436,8 +4814,7 @@
             final int N = mWindows.size();
             for (int i=0; i<N; i++) {
                 WindowState w = mWindows.get(i);
-                if (w.isVisibleLw() && !w.mObscured
-                        && (w.mOrientationChanging || !w.isDrawnLw())) {
+                if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
                     return;
                 }
             }
@@ -4478,6 +4855,26 @@
         }
     }
 
+    public void freezeRotation() {
+        if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
+                "setRotation()")) {
+            throw new SecurityException("Requires SET_ORIENTATION permission");
+        }
+
+        mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED, mRotation);
+        setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false, 0);
+    }
+
+    public void thawRotation() {
+        if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
+                "setRotation()")) {
+            throw new SecurityException("Requires SET_ORIENTATION permission");
+        }
+
+        mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE, 0);
+        setRotationUnchecked(WindowManagerPolicy.USE_LAST_ROTATION, false, 0);
+    }
+
     public void setRotation(int rotation,
             boolean alwaysSendConfiguration, int animFlags) {
         if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
@@ -5055,7 +5452,61 @@
         mPolicy.adjustConfigurationLw(config);
         return true;
     }
-    
+
+    // -------------------------------------------------------------
+    // Drag and drop
+    // -------------------------------------------------------------
+
+    IBinder prepareDragSurface(IWindow window, SurfaceSession session,
+            boolean localOnly, int width, int height, Surface outSurface) {
+        if (DEBUG_DRAG) {
+            Slog.d(TAG, "prepare drag surface: w=" + width + " h=" + height
+                    + " local=" + localOnly + " win=" + window
+                    + " asbinder=" + window.asBinder());
+        }
+
+        final int callerPid = Binder.getCallingPid();
+        final long origId = Binder.clearCallingIdentity();
+        IBinder token = null;
+
+        try {
+            synchronized (mWindowMap) {
+                try {
+                    // !!! TODO: fail if the given window does not currently have touch focus?
+
+                    if (mDragState == null) {
+                        Surface surface = new Surface(session, callerPid, "drag surface", 0,
+                                width, height, PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+                        outSurface.copyFrom(surface);
+                        final IBinder winBinder = window.asBinder();
+                        token = new Binder();
+                        mDragState = new DragState(token, surface, localOnly, winBinder);
+                        mDragState.mSurface = surface;
+                        mDragState.mLocalOnly = localOnly;
+                        token = mDragState.mToken = new Binder();
+
+                        // 5 second timeout for this window to actually begin the drag
+                        mH.removeMessages(H.DRAG_START_TIMEOUT, winBinder);
+                        Message msg = mH.obtainMessage(H.DRAG_START_TIMEOUT, winBinder);
+                        mH.sendMessageDelayed(msg, 5000);
+                    } else {
+                        Slog.w(TAG, "Drag already in progress");
+                    }
+                } catch (Surface.OutOfResourcesException e) {
+                    Slog.e(TAG, "Can't allocate drag surface w=" + width + " h=" + height, e);
+                    if (mDragState != null) {
+                        mDragState.reset();
+                        mDragState = null;
+                    }
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+
+        return token;
+    }
+
     // -------------------------------------------------------------
     // Input Events and Focus Management
     // -------------------------------------------------------------
@@ -5154,7 +5605,40 @@
             
             return null;
         }
-        
+
+        private void addDragInputWindowLw(InputWindowList windowList) {
+            final InputWindow inputWindow = windowList.add();
+            inputWindow.inputChannel = mDragState.mServerChannel;
+            inputWindow.name = "drag";
+            inputWindow.layoutParamsFlags = 0;
+            inputWindow.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
+            inputWindow.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+            inputWindow.visible = true;
+            inputWindow.canReceiveKeys = false;
+            inputWindow.hasFocus = true;
+            inputWindow.hasWallpaper = false;
+            inputWindow.paused = false;
+            inputWindow.layer = mDragState.getDragLayerLw();
+            inputWindow.ownerPid = Process.myPid();
+            inputWindow.ownerUid = Process.myUid();
+
+            // The drag window covers the entire display
+            inputWindow.frameLeft = 0;
+            inputWindow.frameTop = 0;
+            inputWindow.frameRight = mDisplay.getWidth();
+            inputWindow.frameBottom = mDisplay.getHeight();
+
+            inputWindow.visibleFrameLeft = inputWindow.frameLeft;
+            inputWindow.visibleFrameTop = inputWindow.frameTop;
+            inputWindow.visibleFrameRight = inputWindow.frameRight;
+            inputWindow.visibleFrameBottom = inputWindow.frameBottom;
+
+            inputWindow.touchableAreaLeft = inputWindow.frameLeft;
+            inputWindow.touchableAreaTop = inputWindow.frameTop;
+            inputWindow.touchableAreaRight = inputWindow.frameRight;
+            inputWindow.touchableAreaBottom = inputWindow.frameBottom;
+        }
+
         /* Updates the cached window information provided to the input dispatcher. */
         public void updateInputWindowsLw() {
             // Populate the input window list with information about all of the windows that
@@ -5163,6 +5647,16 @@
             // out to be difficult because only the native code knows for sure which window
             // currently has touch focus.
             final ArrayList<WindowState> windows = mWindows;
+
+            // If there's a drag in flight, provide a pseudowindow to catch drag input
+            final boolean inDrag = (mDragState != null);
+            if (inDrag) {
+                if (DEBUG_DRAG) {
+                    Log.d(TAG, "Inserting drag window");
+                }
+                addDragInputWindowLw(mTempInputWindows);
+            }
+
             final int N = windows.size();
             for (int i = N - 1; i >= 0; i--) {
                 final WindowState child = windows.get(i);
@@ -5178,7 +5672,13 @@
                 final boolean isVisible = child.isVisibleLw();
                 final boolean hasWallpaper = (child == mWallpaperTarget)
                         && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
-                
+
+                // If there's a drag in progress and 'child' is a potential drop target,
+                // make sure it's been told about the drag
+                if (inDrag && isVisible) {
+                    mDragState.sendDragStartedIfNeededLw(child);
+                }
+
                 // Add a window to our list of input windows.
                 final InputWindow inputWindow = mTempInputWindows.add();
                 inputWindow.inputChannel = child.mInputChannel;
@@ -5251,20 +5751,20 @@
         
         /* Provides an opportunity for the window manager policy to intercept early key
          * processing as soon as the key has been read from the device. */
-        public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down,
-                int policyFlags, boolean isScreenOn) {
-            return mPolicy.interceptKeyBeforeQueueing(whenNanos,
-                    keyCode, down, policyFlags, isScreenOn);
+        public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,
+                int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {
+            return mPolicy.interceptKeyBeforeQueueing(whenNanos, action, flags,
+                    keyCode, scanCode, policyFlags, isScreenOn);
         }
         
         /* Provides an opportunity for the window manager policy to process a key before
          * ordinary dispatch. */
         public boolean interceptKeyBeforeDispatching(InputChannel focus,
-                int action, int flags, int keyCode, int metaState, int repeatCount,
+                int action, int flags, int keyCode, int scanCode, int metaState, int repeatCount,
                 int policyFlags) {
             WindowState windowState = getWindowStateForInputChannel(focus);
             return mPolicy.interceptKeyBeforeDispatching(windowState, action, flags,
-                    keyCode, metaState, repeatCount, policyFlags);
+                    keyCode, scanCode, metaState, repeatCount, policyFlags);
         }
         
         /* Called when the current input focus changes.
@@ -5562,6 +6062,22 @@
     }
 
     public void systemReady() {
+        synchronized(mWindowMap) {
+            if (mDisplay != null) {
+                throw new IllegalStateException("Display already initialized");
+            }
+            WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+            mDisplay = wm.getDefaultDisplay();
+            mInitialDisplayWidth = mDisplay.getWidth();
+            mInitialDisplayHeight = mDisplay.getHeight();
+            mInputManager.setDisplaySize(0, mInitialDisplayWidth, mInitialDisplayHeight);
+        }
+
+        try {
+            mActivityManager.updateConfiguration(null);
+        } catch (RemoteException e) {
+        }
+        
         mPolicy.systemReady();
     }
 
@@ -5727,6 +6243,126 @@
             }
         }
 
+        /* Drag/drop */
+        public IBinder prepareDrag(IWindow window, boolean localOnly,
+                int width, int height, Surface outSurface) {
+            return prepareDragSurface(window, mSurfaceSession, localOnly,
+                    width, height, outSurface);
+        }
+
+        public boolean performDrag(IWindow window, IBinder dragToken,
+                float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+                ClipData data) {
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "perform drag: win=" + window + " data=" + data);
+            }
+
+            synchronized (mWindowMap) {
+                if (mDragState == null) {
+                    Slog.w(TAG, "No drag prepared");
+                    throw new IllegalStateException("performDrag() without prepareDrag()");
+                }
+
+                if (dragToken != mDragState.mToken) {
+                    Slog.w(TAG, "Performing mismatched drag");
+                    throw new IllegalStateException("performDrag() does not match prepareDrag()");
+                }
+
+                WindowState callingWin = windowForClientLocked(null, window, false);
+                if (callingWin == null) {
+                    Slog.w(TAG, "Bad requesting window " + window);
+                    return false;  // !!! TODO: throw here?
+                }
+
+                // !!! TODO: if input is not still focused on the initiating window, fail
+                // the drag initiation (e.g. an alarm window popped up just as the application
+                // called performDrag()
+
+                mH.removeMessages(H.DRAG_START_TIMEOUT, window.asBinder());
+
+                // !!! TODO: extract the current touch (x, y) in screen coordinates.  That
+                // will let us eliminate the (touchX,touchY) parameters from the API.
+
+                // !!! FIXME: put all this heavy stuff onto the mH looper, as well as
+                // the actual drag event dispatch stuff in the dragstate
+
+                mDragState.register();
+                mInputMonitor.updateInputWindowsLw();
+                if (!mInputManager.transferTouchFocus(callingWin.mInputChannel,
+                        mDragState.mServerChannel)) {
+                    Slog.e(TAG, "Unable to transfer touch focus");
+                    mDragState.unregister();
+                    mDragState = null;
+                    mInputMonitor.updateInputWindowsLw();
+                    return false;
+                }
+
+                mDragState.mData = data;
+                mDragState.mCurrentX = touchX;
+                mDragState.mCurrentY = touchY;
+                mDragState.broadcastDragStartedLw(touchX, touchY);
+
+                // remember the thumb offsets for later
+                mDragState.mThumbOffsetX = thumbCenterX;
+                mDragState.mThumbOffsetY = thumbCenterY;
+
+                // Make the surface visible at the proper location
+                final Surface surface = mDragState.mSurface;
+                Surface.openTransaction();
+                try {
+                    surface.setPosition((int)(touchX - thumbCenterX),
+                            (int)(touchY - thumbCenterY));
+                    surface.setAlpha(.7071f);
+                    surface.setLayer(mDragState.getDragLayerLw());
+                    surface.show();
+                } finally {
+                    Surface.closeTransaction();
+                }
+            }
+
+            return true;    // success!
+        }
+
+        public void reportDropResult(IWindow window, boolean consumed) {
+            IBinder token = window.asBinder();
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "Drop result=" + consumed + " reported by " + token);
+            }
+
+            synchronized (mWindowMap) {
+                if (mDragState.mToken != token) {
+                    Slog.w(TAG, "Invalid drop-result claim by " + window);
+                    throw new IllegalStateException("reportDropResult() by non-recipient");
+                }
+
+                // The right window has responded, even if it's no longer around,
+                // so be sure to halt the timeout even if the later WindowState
+                // lookup fails.
+                mH.removeMessages(H.DRAG_END_TIMEOUT, window.asBinder());
+
+                WindowState callingWin = windowForClientLocked(null, window, false);
+                if (callingWin == null) {
+                    Slog.w(TAG, "Bad result-reporting window " + window);
+                    return;  // !!! TODO: throw here?
+                }
+
+                mDragState.mDragResult = consumed;
+                mDragState.endDragLw();
+            }
+        }
+
+        public void dragRecipientEntered(IWindow window) {
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "Drag into new candidate view @ " + window.asBinder());
+            }
+        }
+
+        public void dragRecipientExited(IWindow window) {
+            if (DEBUG_DRAG) {
+                Slog.d(TAG, "Drag from old candidate view @ " + window.asBinder());
+            }
+        }
+
         public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
             synchronized(mWindowMap) {
                 long ident = Binder.clearCallingIdentity();
@@ -6308,10 +6944,16 @@
                 mSurfaceW = w;
                 mSurfaceH = h;
                 try {
+                    final boolean isHwAccelerated = (mAttrs.flags &
+                            WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
+                    final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : mAttrs.format;
+                    if (isHwAccelerated && mAttrs.format == PixelFormat.OPAQUE) {
+                        flags |= Surface.OPAQUE;
+                    }
                     mSurface = new Surface(
                             mSession.mSurfaceSession, mSession.mPid,
                             mAttrs.getTitle().toString(),
-                            0, w, h, mAttrs.format, flags);
+                            0, w, h, format, flags);
                     if (SHOW_TRANSACTIONS) Slog.i(TAG, "  CREATE SURFACE "
                             + mSurface + " IN SESSION "
                             + mSession.mSurfaceSession
@@ -6842,7 +7484,7 @@
             final AppWindowToken atoken = mAppToken;
             return mSurface != null && !mAttachedHidden
                     && (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
-                    && (mOrientationChanging || (!mDrawPending && !mCommitDrawPending))
+                    && !mDrawPending && !mCommitDrawPending
                     && !mExiting && !mDestroying;
         }
 
@@ -6868,6 +7510,15 @@
         }
 
         /**
+         * Can this window possibly be a drag/drop target?  The test here is
+         * a combination of the above "visible now" with the check that the
+         * Input Manager uses when discarding windows from input consideration.
+         */
+        boolean isPotentialDragTarget() {
+            return isVisibleNow() && (mInputChannel != null) && !mRemoved;
+        }
+
+        /**
          * Same as isVisible(), but we also count it as visible between the
          * call to IWindowSession.add() and the first relayout().
          */
@@ -6946,14 +7597,12 @@
 
         /**
          * Returns true if the window has a surface that it has drawn a
-         * complete UI in to.  Note that this returns true if the orientation
-         * is changing even if the window hasn't redrawn because we don't want
-         * to stop things from executing during that time.
+         * complete UI in to.
          */
         public boolean isDrawnLw() {
             final AppWindowToken atoken = mAppToken;
             return mSurface != null && !mDestroying
-                && (mOrientationChanging || (!mDrawPending && !mCommitDrawPending));
+                && !mDrawPending && !mCommitDrawPending;
         }
 
         /**
@@ -7771,6 +8420,8 @@
         public static final int APP_FREEZE_TIMEOUT = 17;
         public static final int SEND_NEW_CONFIGURATION = 18;
         public static final int REPORT_WINDOWS_CHANGE = 19;
+        public static final int DRAG_START_TIMEOUT = 20;
+        public static final int DRAG_END_TIMEOUT = 21;
 
         private Session mLastReportedHold;
 
@@ -8113,6 +8764,35 @@
                     break;
                 }
 
+                case DRAG_START_TIMEOUT: {
+                    IBinder win = (IBinder)msg.obj;
+                    if (DEBUG_DRAG) {
+                        Slog.w(TAG, "Timeout starting drag by win " + win);
+                    }
+                    synchronized (mWindowMap) {
+                        // !!! TODO: ANR the app that has failed to start the drag in time
+                        if (mDragState != null) {
+                            mDragState.unregister();
+                            mInputMonitor.updateInputWindowsLw();
+                            mDragState.reset();
+                            mDragState = null;
+                        }
+                    }
+                    break;
+                }
+
+                case DRAG_END_TIMEOUT: {
+                    IBinder win = (IBinder)msg.obj;
+                    if (DEBUG_DRAG) {
+                        Slog.w(TAG, "Timeout ending drag to win " + win);
+                    }
+                    synchronized (mWindowMap) {
+                        // !!! TODO: ANR the drag-receiving app
+                        mDragState.mDragResult = false;
+                        mDragState.endDragLw();
+                    }
+                    break;
+                }
             }
         }
     }
@@ -8455,6 +9135,11 @@
 
     private final void performLayoutAndPlaceSurfacesLockedInner(
             boolean recoveringMemory) {
+        if (mDisplay == null) {
+            Slog.i(TAG, "skipping performLayoutAndPlaceSurfacesLockedInner with no mDisplay");
+            return;
+        }
+
         final long currentTime = SystemClock.uptimeMillis();
         final int dw = mDisplay.getWidth();
         final int dh = mDisplay.getHeight();
@@ -9243,12 +9928,6 @@
                     if (w.mAttachedHidden || !w.isReadyForDisplay()) {
                         if (!w.mLastHidden) {
                             //dump();
-                            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Window hiding: waitingToShow="
-                                    + w.mRootToken.waitingToShow + " polvis="
-                                    + w.mPolicyVisibility + " atthid="
-                                    + w.mAttachedHidden + " tokhid="
-                                    + w.mRootToken.hidden + " vis="
-                                    + w.mViewVisibility);
                             w.mLastHidden = true;
                             if (SHOW_TRANSACTIONS) logSurface(w,
                                     "HIDE (performLayout)", null);
@@ -9649,30 +10328,26 @@
         } else if (animating) {
             requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
         }
-        
+
         mInputMonitor.updateInputWindowsLw();
-        
-        if (DEBUG_FREEZE) Slog.v(TAG, "Layout: mDisplayFrozen=" + mDisplayFrozen
-                + " holdScreen=" + holdScreen);
-        if (!mDisplayFrozen) {
-            setHoldScreenLocked(holdScreen != null);
-            if (screenBrightness < 0 || screenBrightness > 1.0f) {
-                mPowerManager.setScreenBrightnessOverride(-1);
-            } else {
-                mPowerManager.setScreenBrightnessOverride((int)
-                        (screenBrightness * Power.BRIGHTNESS_ON));
-            }
-            if (buttonBrightness < 0 || buttonBrightness > 1.0f) {
-                mPowerManager.setButtonBrightnessOverride(-1);
-            } else {
-                mPowerManager.setButtonBrightnessOverride((int)
-                        (buttonBrightness * Power.BRIGHTNESS_ON));
-            }
-            if (holdScreen != mHoldingScreenOn) {
-                mHoldingScreenOn = holdScreen;
-                Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, holdScreen);
-                mH.sendMessage(m);
-            }
+
+        setHoldScreenLocked(holdScreen != null);
+        if (screenBrightness < 0 || screenBrightness > 1.0f) {
+            mPowerManager.setScreenBrightnessOverride(-1);
+        } else {
+            mPowerManager.setScreenBrightnessOverride((int)
+                    (screenBrightness * Power.BRIGHTNESS_ON));
+        }
+        if (buttonBrightness < 0 || buttonBrightness > 1.0f) {
+            mPowerManager.setButtonBrightnessOverride(-1);
+        } else {
+            mPowerManager.setButtonBrightnessOverride((int)
+                    (buttonBrightness * Power.BRIGHTNESS_ON));
+        }
+        if (holdScreen != mHoldingScreenOn) {
+            mHoldingScreenOn = holdScreen;
+            Message m = mH.obtainMessage(H.HOLD_SCREEN_CHANGED, holdScreen);
+            mH.sendMessage(m);
         }
 
         if (mTurnOnScreen) {
@@ -9961,8 +10636,6 @@
             mFreezeGcPending = now;
         }
 
-        if (DEBUG_FREEZE) Slog.v(TAG, "*** FREEZING DISPLAY", new RuntimeException());
-        
         mDisplayFrozen = true;
         
         mInputMonitor.freezeInputDispatchingLw();
@@ -9989,8 +10662,6 @@
             return;
         }
         
-        if (DEBUG_FREEZE) Slog.v(TAG, "*** UNFREEZING DISPLAY", new RuntimeException());
-        
         mDisplayFrozen = false;
         mH.removeMessages(H.APP_FREEZE_TIMEOUT);
         if (PROFILE_ORIENTATION) {
@@ -10039,7 +10710,7 @@
         return val;
     }
 
-    class Watermark {
+    static class Watermark {
         final String[] mTokens;
         final String mText;
         final Paint mTextPaint;
@@ -10055,9 +10726,9 @@
         int mLastDH;
         boolean mDrawNeeded;
 
-        Watermark(SurfaceSession session, String[] tokens) {
+        Watermark(Display display, SurfaceSession session, String[] tokens) {
             final DisplayMetrics dm = new DisplayMetrics();
-            mDisplay.getMetrics(dm);
+            display.getMetrics(dm);
 
             if (false) {
                 Log.i(TAG, "*********************** WATERMARK");
@@ -10151,6 +10822,8 @@
                 } catch (OutOfResourcesException e) {
                 }
                 if (c != null) {
+                    c.drawColor(0, PorterDuff.Mode.CLEAR);
+                    
                     int deltaX = mDeltaX;
                     int deltaY = mDeltaY;
 
@@ -10193,7 +10866,7 @@
             if (line != null) {
                 String[] toks = line.split("%");
                 if (toks != null && toks.length > 0) {
-                    mWatermark = new Watermark(mFxSession, toks);
+                    mWatermark = new Watermark(mDisplay, mFxSession, toks);
                 }
             }
         } catch (FileNotFoundException e) {
diff --git a/services/java/com/android/server/WiredAccessoryObserver.java b/services/java/com/android/server/WiredAccessoryObserver.java
new file mode 100644
index 0000000..4a85aff
--- /dev/null
+++ b/services/java/com/android/server/WiredAccessoryObserver.java
@@ -0,0 +1,277 @@
+/*
+ * 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.android.server;
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.UEventObserver;
+import android.util.Slog;
+import android.media.AudioManager;
+import android.util.Log;
+
+import java.io.FileReader;
+import java.io.FileNotFoundException;
+
+/**
+ * <p>WiredAccessoryObserver monitors for a wired headset on the main board or dock.
+ */
+class WiredAccessoryObserver extends UEventObserver {
+    private static final String TAG = WiredAccessoryObserver.class.getSimpleName();
+    private static final boolean LOG = true;
+    private static final int MAX_AUDIO_PORTS = 3; /* h2w, USB Audio & hdmi */
+    private static final String uEventInfo[][] = { {"DEVPATH=/devices/virtual/switch/h2w",
+                                                    "/sys/class/switch/h2w/state",
+                                                    "/sys/class/switch/h2w/name"},
+                                                   {"DEVPATH=/devices/virtual/switch/usb_audio",
+                                                    "/sys/class/switch/usb_audio/state",
+                                                    "/sys/class/switch/usb_audio/name"},
+                                                   {"DEVPATH=/devices/virtual/switch/hdmi",
+                                                    "/sys/class/switch/hdmi/state",
+                                                    "/sys/class/switch/hdmi/name"} };
+
+    private static final int BIT_HEADSET = (1 << 0);
+    private static final int BIT_HEADSET_NO_MIC = (1 << 1);
+    private static final int BIT_USB_HEADSET_ANLG = (1 << 2);
+    private static final int BIT_USB_HEADSET_DGTL = (1 << 3);
+    private static final int BIT_HDMI_AUDIO = (1 << 4);
+    private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+                                                   BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL|
+                                                   BIT_HDMI_AUDIO);
+    private static final int HEADSETS_WITH_MIC = BIT_HEADSET;
+
+    private int mHeadsetState;
+    private int mPrevHeadsetState;
+    private String mHeadsetName;
+    private int switchState;
+
+    private final Context mContext;
+    private final WakeLock mWakeLock;  // held while there is a pending route change
+
+    public WiredAccessoryObserver(Context context) {
+        mContext = context;
+        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WiredAccessoryObserver");
+        mWakeLock.setReferenceCounted(false);
+
+        // At any given time both headsets could be inserted
+        // one on the board and one on the dock
+        // observe two UEVENTs
+        for (int i = 0; i < MAX_AUDIO_PORTS; i++) {
+            startObserving(uEventInfo[i][0]);
+        }
+        init();  // set initial status
+    }
+
+    @Override
+    public void onUEvent(UEventObserver.UEvent event) {
+        if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
+
+        try {
+            if ((event.get("SWITCH_NAME")).equals("usb_audio")) {
+                if (Integer.parseInt(event.get("SWITCH_STATE")) == 1) {
+                    switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+                                                     BIT_USB_HEADSET_DGTL)) |
+                                   (Integer.parseInt(event.get("SWITCH_STATE")) << 2));
+                } else if (Integer.parseInt(event.get("SWITCH_STATE")) == 2) {
+                    switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+                                                     BIT_USB_HEADSET_ANLG)) |
+                                   (Integer.parseInt(event.get("SWITCH_STATE")) << 3));
+                }
+                else switchState = (mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC));
+            }
+            else if ((event.get("SWITCH_NAME")).equals("hdmi")) {
+                switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
+                                                 BIT_USB_HEADSET_DGTL|BIT_USB_HEADSET_ANLG)) |
+                               (Integer.parseInt(event.get("SWITCH_STATE")) << 4));
+            }
+            else {
+                switchState = ((mHeadsetState & (BIT_USB_HEADSET_ANLG|BIT_USB_HEADSET_DGTL)) |
+                              (Integer.parseInt(event.get("SWITCH_STATE"))));
+            }
+            update(event.get("SWITCH_NAME"), switchState);
+        } catch (NumberFormatException e) {
+            Slog.e(TAG, "Could not parse switch state from event " + event);
+        }
+    }
+
+    private synchronized final void init() {
+        char[] buffer = new char[1024];
+
+        String newName = mHeadsetName;
+        int newState = mHeadsetState;
+        mPrevHeadsetState = mHeadsetState;
+
+        for (int i = 0; i < MAX_AUDIO_PORTS; i++) {
+            try {
+                FileReader file = new FileReader(uEventInfo[i][1]);
+                int len = file.read(buffer, 0, 1024);
+                file.close();
+                newState = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+                file = new FileReader(uEventInfo[i][2]);
+                len = file.read(buffer, 0, 1024);
+                file.close();
+                newName = new String(buffer, 0, len).trim();
+
+            } catch (FileNotFoundException e) {
+                Slog.w(TAG, "This kernel does not have wired headset support");
+            } catch (Exception e) {
+                Slog.e(TAG, "" , e);
+            }
+
+            update(newName, newState);
+        }
+    }
+
+    private synchronized final void update(String newName, int newState) {
+        // Retain only relevant bits
+        int headsetState = newState & SUPPORTED_HEADSETS;
+        int newOrOld = headsetState | mHeadsetState;
+        int delay = 0;
+        int usb_headset_anlg = headsetState & BIT_USB_HEADSET_ANLG;
+        int usb_headset_dgtl = headsetState & BIT_USB_HEADSET_DGTL;
+        int h2w_headset = headsetState & (BIT_HEADSET | BIT_HEADSET_NO_MIC);
+        boolean h2wStateChange = true;
+        boolean usbStateChange = true;
+        // reject all suspect transitions: only accept state changes from:
+        // - a: 0 heaset to 1 headset
+        // - b: 1 headset to 0 headset
+        Log.v(TAG, "newState = "+newState+", headsetState = "+headsetState+", mHeadsetState = "+mHeadsetState);
+        if (mHeadsetState == headsetState || ((h2w_headset & (h2w_headset - 1)) != 0)) {
+            Log.e(TAG, "unsetting h2w flag");
+            h2wStateChange = false;
+        }
+        // - c: 0 usb headset to 1 usb headset
+        // - d: 1 usb headset to 0 usb headset
+        if ((usb_headset_anlg >> 2) == 1 && (usb_headset_dgtl >> 3) == 1) {
+            Log.e(TAG, "unsetting usb flag");
+            usbStateChange = false;
+        }
+        if (!h2wStateChange && !usbStateChange) {
+            Log.e(TAG, "invalid transition, returning ...");
+            return;
+        }
+
+        mHeadsetName = newName;
+        mPrevHeadsetState = mHeadsetState;
+        mHeadsetState = headsetState;
+
+        if (headsetState == 0) {
+            Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+            mContext.sendBroadcast(intent);
+            // It can take hundreds of ms flush the audio pipeline after
+            // apps pause audio playback, but audio route changes are
+            // immediate, so delay the route change by 1000ms.
+            // This could be improved once the audio sub-system provides an
+            // interface to clear the audio pipeline.
+            delay = 1000;
+        } else {
+            // Insert the same delay for headset connection so that the connection event is not
+            // broadcast before the disconnection event in case of fast removal/insertion
+            if (mHandler.hasMessages(0)) {
+                delay = 1000;
+            }
+        }
+        mWakeLock.acquire();
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(0,
+                                                           mHeadsetState,
+                                                           mPrevHeadsetState,
+                                                           mHeadsetName),
+                                    delay);
+    }
+
+    private synchronized final void sendIntents(int headsetState, int prevHeadsetState, String headsetName) {
+        int allHeadsets = SUPPORTED_HEADSETS;
+        for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
+            if ((curHeadset & allHeadsets) != 0) {
+                sendIntent(curHeadset, headsetState, prevHeadsetState, headsetName);
+                allHeadsets &= ~curHeadset;
+            }
+        }
+    }
+
+    private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
+        if ((headsetState & headset) != (prevHeadsetState & headset)) {
+
+            int state = 0;
+            if ((headsetState & headset) != 0) {
+                state = 1;
+            }
+            if((headset == BIT_USB_HEADSET_ANLG) || (headset == BIT_USB_HEADSET_DGTL) ||
+               (headset == BIT_HDMI_AUDIO)) {
+                Intent intent;
+
+                //  Pack up the values and broadcast them to everyone
+                if (headset == BIT_USB_HEADSET_ANLG) {
+                    intent = new Intent(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    intent.putExtra("state", state);
+                    intent.putExtra("name", headsetName);
+                    ActivityManagerNative.broadcastStickyIntent(intent, null);
+                } else if (headset == BIT_USB_HEADSET_DGTL) {
+                    intent = new Intent(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    intent.putExtra("state", state);
+                    intent.putExtra("name", headsetName);
+                    ActivityManagerNative.broadcastStickyIntent(intent, null);
+                } else if (headset == BIT_HDMI_AUDIO) {
+                    intent = new Intent(Intent.ACTION_HDMI_AUDIO_PLUG);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    intent.putExtra("state", state);
+                    intent.putExtra("name", headsetName);
+                    ActivityManagerNative.broadcastStickyIntent(intent, null);
+                }
+
+                if (LOG) Slog.v(TAG, "Intent.ACTION_USB_HEADSET_PLUG: state: "+state+" name: "+headsetName);
+                // TODO: Should we require a permission?
+            }
+            if((headset == BIT_HEADSET) || (headset == BIT_HEADSET_NO_MIC)) {
+
+                //  Pack up the values and broadcast them to everyone
+                Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                //int state = 0;
+                int microphone = 0;
+
+                if ((headset & HEADSETS_WITH_MIC) != 0) {
+                    microphone = 1;
+                }
+
+                intent.putExtra("state", state);
+                intent.putExtra("name", headsetName);
+                intent.putExtra("microphone", microphone);
+
+                if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone);
+                // TODO: Should we require a permission?
+                ActivityManagerNative.broadcastStickyIntent(intent, null);
+            }
+        }
+    }
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            sendIntents(msg.arg1, msg.arg2, (String)msg.obj);
+            mWakeLock.release();
+        }
+    };
+}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6a05d3c..7905dc6 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -959,6 +959,7 @@
     static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
     static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26;
     static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
+    static final int CLEAR_DNS_CACHE = 28;
 
     AlertDialog mUidAlert;
 
@@ -1110,6 +1111,20 @@
                     }
                 }
             } break;
+            case CLEAR_DNS_CACHE: {
+                synchronized (ActivityManagerService.this) {
+                    for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
+                        ProcessRecord r = mLruProcesses.get(i);
+                        if (r.thread != null) {
+                            try {
+                                r.thread.clearDnsCache();
+                            } catch (RemoteException ex) {
+                                Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
+                            }
+                        }
+                    }
+                }
+            } break;
             case SHOW_UID_ERROR_MSG: {
                 // XXX This is a temporary dialog, no need to localize.
                 AlertDialog d = new BaseErrorDialog(mContext);
@@ -2451,6 +2466,10 @@
             }
             
             if (proc.thread != null) {
+                if (proc.pid == Process.myPid()) {
+                    Log.w(TAG, "crashApplication: trying to crash self!");
+                    return;
+                }
                 long ident = Binder.clearCallingIdentity();
                 try {
                     proc.thread.scheduleCrash(message);
@@ -4268,8 +4287,10 @@
             return -1;
         }
 
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                "Checking grant " + targetPkg + " permission to " + uri);
+        if (targetPkg != null) {
+            if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+                    "Checking grant " + targetPkg + " permission to " + uri);
+        }
         
         final IPackageManager pm = AppGlobals.getPackageManager();
 
@@ -4298,23 +4319,45 @@
         }
 
         int targetUid;
-        try {
-            targetUid = pm.getPackageUid(targetPkg);
-            if (targetUid < 0) {
-                if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                        "Can't grant URI permission no uid for: " + targetPkg);
+        if (targetPkg != null) {
+            try {
+                targetUid = pm.getPackageUid(targetPkg);
+                if (targetUid < 0) {
+                    if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+                            "Can't grant URI permission no uid for: " + targetPkg);
+                    return -1;
+                }
+            } catch (RemoteException ex) {
                 return -1;
             }
-        } catch (RemoteException ex) {
-            return -1;
+        } else {
+            targetUid = -1;
         }
 
-        // First...  does the target actually need this permission?
-        if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
-            // No need to grant the target this permission.
-            if (DEBUG_URI_PERMISSION) Slog.v(TAG, 
-                    "Target " + targetPkg + " already has full permission to " + uri);
-            return -1;
+        if (targetUid >= 0) {
+            // First...  does the target actually need this permission?
+            if (checkHoldingPermissionsLocked(pm, pi, uri, targetUid, modeFlags)) {
+                // No need to grant the target this permission.
+                if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+                        "Target " + targetPkg + " already has full permission to " + uri);
+                return -1;
+            }
+        } else {
+            // First...  there is no target package, so can anyone access it?
+            boolean allowed = pi.exported;
+            if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+                if (pi.readPermission != null) {
+                    allowed = false;
+                }
+            }
+            if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+                if (pi.writePermission != null) {
+                    allowed = false;
+                }
+            }
+            if (allowed) {
+                return -1;
+            }
         }
 
         // Second...  is the provider allowing granting of URI permissions?
@@ -4344,16 +4387,25 @@
 
         // Third...  does the caller itself have permission to access
         // this uri?
-        if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
-            if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
-                throw new SecurityException("Uid " + callingUid
-                        + " does not have permission to uri " + uri);
+        if (callingUid != Process.myUid()) {
+            if (!checkHoldingPermissionsLocked(pm, pi, uri, callingUid, modeFlags)) {
+                if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
+                    throw new SecurityException("Uid " + callingUid
+                            + " does not have permission to uri " + uri);
+                }
             }
         }
 
         return targetUid;
     }
 
+    public int checkGrantUriPermission(int callingUid, String targetPkg,
+            Uri uri, int modeFlags) {
+        synchronized(this) {
+            return checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags);
+        }
+    }
+
     void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg,
             Uri uri, int modeFlags, UriPermissionOwner owner) {
         modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -4396,6 +4448,10 @@
 
     void grantUriPermissionLocked(int callingUid,
             String targetPkg, Uri uri, int modeFlags, UriPermissionOwner owner) {
+        if (targetPkg == null) {
+            throw new NullPointerException("targetPkg");
+        }
+
         int targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, uri, modeFlags);
         if (targetUid < 0) {
             return;
@@ -4414,6 +4470,10 @@
                 + " from " + intent + "; flags=0x"
                 + Integer.toHexString(intent != null ? intent.getFlags() : 0));
 
+        if (targetPkg == null) {
+            throw new NullPointerException("targetPkg");
+        }
+
         if (intent == null) {
             return -1;
         }
@@ -5878,6 +5938,35 @@
         }
     }
 
+    public void setImmersive(IBinder token, boolean immersive) {
+        synchronized(this) {
+            int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1;
+            if (index < 0) {
+                throw new IllegalArgumentException();
+            }
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
+            r.immersive = immersive;
+        }
+    }
+
+    public boolean isImmersive(IBinder token) {
+        synchronized (this) {
+            int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1;
+            if (index < 0) {
+                throw new IllegalArgumentException();
+            }
+            ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(index);
+            return r.immersive;
+        }
+    }
+
+    public boolean isTopActivityImmersive() {
+        synchronized (this) {
+            ActivityRecord r = mMainStack.topRunningActivityLocked(null);
+            return (r != null) ? r.immersive : false;
+        }
+    }
+
     public final void enterSafeMode() {
         synchronized(this) {
             // It only makes sense to do this before the system is ready
@@ -6602,6 +6691,12 @@
             if (info.violationNumThisLoop != 0) {
                 sb.append("Loop-Violation-Number: ").append(info.violationNumThisLoop).append("\n");
             }
+            if (info.numAnimationsRunning != 0) {
+                sb.append("Animations-Running: ").append(info.numAnimationsRunning).append("\n");
+            }
+            if (info.broadcastIntentAction != null) {
+                sb.append("Broadcast-Intent-Action: ").append(info.broadcastIntentAction).append("\n");
+            }
             if (info != null && info.durationMillis != -1) {
                 sb.append("Duration-Millis: ").append(info.durationMillis).append("\n");
             }
@@ -7210,13 +7305,19 @@
                 }
                 return;
             } else if ("service".equals(cmd)) {
-                dumpService(fd, pw, args, opti, dumpAll);
+                dumpService(fd, pw, args, opti);
                 return;
             } else if ("services".equals(cmd) || "s".equals(cmd)) {
                 synchronized (this) {
                     dumpServicesLocked(fd, pw, args, opti, true);
                 }
                 return;
+            } else {
+                // Dumping a single activity?
+                if (dumpActivity(fd, pw, cmd, args, opti, dumpAll)) {
+                    return;
+                }
+                pw.println("Bad activity command: " + cmd);
             }
         }
         
@@ -7563,8 +7664,7 @@
      *  - the first arg isn't the flattened component name of an existing service:
      *    dump all services whose component contains the first arg as a substring
      */
-    protected void dumpService(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpAll) {
+    protected void dumpService(FileDescriptor fd, PrintWriter pw, String[] args, int opti) {
         String[] newArgs;
         String componentNameString;
         ServiceRecord r;
@@ -7584,7 +7684,7 @@
         }
 
         if (r != null) {
-            dumpService(fd, pw, r, newArgs, dumpAll);
+            dumpService(fd, pw, r, newArgs);
         } else {
             ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
             synchronized (this) {
@@ -7596,7 +7696,7 @@
                 }
             }
             for (int i=0; i<services.size(); i++) {
-                dumpService(fd, pw, services.get(i), newArgs, dumpAll);
+                dumpService(fd, pw, services.get(i), newArgs);
             }
         }
     }
@@ -7605,9 +7705,79 @@
      * Invokes IApplicationThread.dumpService() on the thread of the specified service if
      * there is a thread associated with the service.
      */
-    private void dumpService(FileDescriptor fd, PrintWriter pw, ServiceRecord r, String[] args,
+    private void dumpService(FileDescriptor fd, PrintWriter pw, ServiceRecord r, String[] args) {
+        pw.println("------------------------------------------------------------"
+                + "-------------------");
+        pw.println("APP SERVICE: " + r.name.flattenToString());
+        if (r.app != null && r.app.thread != null) {
+            try {
+                // flush anything that is already in the PrintWriter since the thread is going
+                // to write to the file descriptor directly
+                pw.flush();
+                r.app.thread.dumpService(fd, r, args);
+                pw.print("\n");
+                pw.flush();
+            } catch (RemoteException e) {
+                pw.println("got a RemoteException while dumping the service");
+            }
+        }
+    }
+
+    /**
+     * There are three things that cmd can be:
+     *  - a flattened component name that matched an existing activity
+     *  - the cmd arg isn't the flattened component name of an existing activity:
+     *    dump all activity whose component contains the cmd as a substring
+     *  - A hex number of the ActivityRecord object instance.
+     */
+    protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args,
+            int opti, boolean dumpAll) {
+        String[] newArgs;
+        ComponentName componentName = ComponentName.unflattenFromString(name);
+        int objectId = 0;
+        try {
+            objectId = Integer.parseInt(name, 16);
+            name = null;
+            componentName = null;
+        } catch (RuntimeException e) {
+        }
+        newArgs = new String[args.length - opti];
+        if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
+
+        ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
+        synchronized (this) {
+            for (ActivityRecord r1 : (ArrayList<ActivityRecord>)mMainStack.mHistory) {
+                if (componentName != null) {
+                    if (r1.intent.getComponent().equals(componentName)) {
+                        activities.add(r1);
+                    }
+                } else if (name != null) {
+                    if (r1.intent.getComponent().flattenToString().contains(name)) {
+                        activities.add(r1);
+                    }
+                } else if (System.identityHashCode(this) == objectId) {
+                    activities.add(r1);
+                }
+            }
+        }
+
+        if (activities.size() <= 0) {
+            return false;
+        }
+
+        for (int i=0; i<activities.size(); i++) {
+            dumpActivity(fd, pw, activities.get(i), newArgs, dumpAll);
+        }
+        return true;
+    }
+
+    /**
+     * Invokes IApplicationThread.dumpActivity() on the thread of the specified activity if
+     * there is a thread associated with the activity.
+     */
+    private void dumpActivity(FileDescriptor fd, PrintWriter pw, ActivityRecord r, String[] args,
             boolean dumpAll) {
-        pw.println("  Service " + r.name.flattenToString());
+        pw.println("  Activity " + r.intent.getComponent().flattenToString());
         if (dumpAll) {
             synchronized (this) {
                 pw.print("  * "); pw.println(r);
@@ -7620,11 +7790,11 @@
                 // flush anything that is already in the PrintWriter since the thread is going
                 // to write to the file descriptor directly
                 pw.flush();
-                r.app.thread.dumpService(fd, r, args);
+                r.app.thread.dumpActivity(fd, r, args);
                 pw.print("\n");
                 pw.flush();
             } catch (RemoteException e) {
-                pw.println("got a RemoteException while dumping the service");
+                pw.println("got a RemoteException while dumping the activity");
             }
         }
     }
@@ -10218,6 +10388,10 @@
             mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
         }
 
+        if (intent.ACTION_CLEAR_DNS_CACHE.equals(intent.getAction())) {
+            mHandler.sendEmptyMessage(CLEAR_DNS_CACHE);
+        }
+
         /*
          * Prevent non-system code (defined here to be non-persistent
          * processes) from sending protected broadcasts.
@@ -12490,7 +12664,69 @@
             }
         }
     }
-    
+
+    public boolean dumpHeap(String process, boolean managed,
+            String path, ParcelFileDescriptor fd) throws RemoteException {
+
+        try {
+            synchronized (this) {
+                // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+                // its own permission (same as profileControl).
+                if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    throw new SecurityException("Requires permission "
+                            + android.Manifest.permission.SET_ACTIVITY_WATCHER);
+                }
+
+                if (fd == null) {
+                    throw new IllegalArgumentException("null fd");
+                }
+
+                ProcessRecord proc = null;
+                try {
+                    int pid = Integer.parseInt(process);
+                    synchronized (mPidsSelfLocked) {
+                        proc = mPidsSelfLocked.get(pid);
+                    }
+                } catch (NumberFormatException e) {
+                }
+
+                if (proc == null) {
+                    HashMap<String, SparseArray<ProcessRecord>> all
+                            = mProcessNames.getMap();
+                    SparseArray<ProcessRecord> procs = all.get(process);
+                    if (procs != null && procs.size() > 0) {
+                        proc = procs.valueAt(0);
+                    }
+                }
+
+                if (proc == null || proc.thread == null) {
+                    throw new IllegalArgumentException("Unknown process: " + process);
+                }
+
+                boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0"));
+                if (isSecure) {
+                    if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+                        throw new SecurityException("Process not debuggable: " + proc);
+                    }
+                }
+
+                proc.thread.dumpHeap(managed, path, fd);
+                fd = null;
+                return true;
+            }
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Process disappeared");
+        } finally {
+            if (fd != null) {
+                try {
+                    fd.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
     /** In this method we try to acquire our lock to make sure that we have not deadlocked */
     public void monitor() {
         synchronized (this) { }
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 4d773e4..47be6a2 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -104,6 +104,7 @@
     boolean idle;           // has the activity gone idle?
     boolean hasBeenLaunched;// has this activity ever been launched?
     boolean frozenBeforeDestroy;// has been frozen but not yet destroyed.
+    boolean immersive;      // immersive mode (don't interrupt if possible)
 
     String stringName;      // for caching of toString().
     
@@ -159,6 +160,7 @@
                 pw.print(" finishing="); pw.println(finishing);
         pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
                 pw.print(" inHistory="); pw.print(inHistory);
+                pw.print(" immersive="); pw.print(immersive);
                 pw.print(" launchMode="); pw.println(launchMode);
         pw.print(prefix); pw.print("fullscreen="); pw.print(fullscreen);
                 pw.print(" visible="); pw.print(visible);
@@ -289,6 +291,8 @@
             } else {
                 isHomeActivity = false;
             }
+
+            immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0;
         } else {
             realActivity = null;
             taskAffinity = null;
@@ -300,6 +304,7 @@
             packageName = null;
             fullscreen = true;
             isHomeActivity = false;
+            immersive = false;
         }
     }
 
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 73a5435..0a98ebd 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -16,7 +16,9 @@
 
 package com.android.server.am;
 
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.os.Binder;
 import android.os.IBinder;
@@ -43,6 +45,8 @@
     
     final BatteryStatsImpl mStats;
     Context mContext;
+    private boolean mBluetoothPendingStats;
+    private BluetoothHeadset mBluetoothHeadset;
 
     BatteryStatsService(String filename) {
         mStats = new BatteryStatsImpl(filename);
@@ -283,16 +287,43 @@
 
     public void noteBluetoothOn() {
         enforceCallingPermission();
-        BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+                                    BluetoothProfile.HEADSET);
+        }
         synchronized (mStats) {
-            mStats.noteBluetoothOnLocked();
-            mStats.setBtHeadset(headset);
+            if (mBluetoothHeadset != null) {
+                mStats.noteBluetoothOnLocked();
+                mStats.setBtHeadset(mBluetoothHeadset);
+            } else {
+                mBluetoothPendingStats = true;
+            }
         }
     }
-    
+
+    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            mBluetoothHeadset = (BluetoothHeadset) proxy;
+            synchronized (mStats) {
+                if (mBluetoothPendingStats) {
+                    mStats.noteBluetoothOnLocked();
+                    mStats.setBtHeadset(mBluetoothHeadset);
+                    mBluetoothPendingStats = false;
+                }
+            }
+        }
+
+        public void onServiceDisconnected(int profile) {
+            mBluetoothHeadset = null;
+        }
+    };
+
     public void noteBluetoothOff() {
         enforceCallingPermission();
         synchronized (mStats) {
+            mBluetoothPendingStats = false;
             mStats.noteBluetoothOffLocked();
         }
     }
diff --git a/services/java/com/android/server/am/UriPermissionOwner.java b/services/java/com/android/server/am/UriPermissionOwner.java
index 99c82e6..68a2e0f 100644
--- a/services/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/java/com/android/server/am/UriPermissionOwner.java
@@ -45,7 +45,7 @@
     }
 
     Binder getExternalTokenLocked() {
-        if (externalToken != null) {
+        if (externalToken == null) {
             externalToken = new ExternalToken();
         }
         return externalToken;
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index a73a4ce..d469d67 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -19,8 +19,8 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.bluetooth.BluetoothPan;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -32,7 +32,6 @@
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
 import android.net.NetworkInfo;
-import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
@@ -54,6 +53,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.Set;
 /**
  * @hide
@@ -66,7 +66,8 @@
 public class Tethering extends INetworkManagementEventObserver.Stub {
 
     private Context mContext;
-    private final String TAG = "Tethering";
+    private final static String TAG = "Tethering";
+    private final static boolean DEBUG = false;
 
     private boolean mBooted = false;
     //used to remember if we got connected before boot finished
@@ -75,6 +76,7 @@
     // TODO - remove both of these - should be part of interface inspection/selection stuff
     private String[] mTetherableUsbRegexs;
     private String[] mTetherableWifiRegexs;
+    private String[] mTetherableBluetoothRegexs;
     private String[] mUpstreamIfaceRegexs;
 
     private Looper mLooper;
@@ -87,17 +89,31 @@
     private static final String USB_NEAR_IFACE_ADDR      = "192.168.42.129";
     private static final String USB_NETMASK              = "255.255.255.0";
 
-    // FYI - the default wifi is 192.168.43.1 and 255.255.255.0
+    // USB is  192.168.42.1 and 255.255.255.0
+    // Wifi is 192.168.43.1 and 255.255.255.0
+    // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
+    // with 255.255.255.0
 
     private String[] mDhcpRange;
     private static final String DHCP_DEFAULT_RANGE1_START = "192.168.42.2";
     private static final String DHCP_DEFAULT_RANGE1_STOP  = "192.168.42.254";
     private static final String DHCP_DEFAULT_RANGE2_START = "192.168.43.2";
     private static final String DHCP_DEFAULT_RANGE2_STOP  = "192.168.43.254";
+    private static final String DHCP_DEFAULT_RANGE3_START = "192.168.44.2";
+    private static final String DHCP_DEFAULT_RANGE3_STOP  = "192.168.44.254";
+    private static final String DHCP_DEFAULT_RANGE4_START = "192.168.45.2";
+    private static final String DHCP_DEFAULT_RANGE4_STOP  = "192.168.45.254";
+    private static final String DHCP_DEFAULT_RANGE5_START = "192.168.46.2";
+    private static final String DHCP_DEFAULT_RANGE5_STOP  = "192.168.46.254";
+    private static final String DHCP_DEFAULT_RANGE6_START = "192.168.47.2";
+    private static final String DHCP_DEFAULT_RANGE6_STOP  = "192.168.47.254";
+    private static final String DHCP_DEFAULT_RANGE7_START = "192.168.48.2";
+    private static final String DHCP_DEFAULT_RANGE7_STOP  = "192.168.48.254";
+
 
     private String[] mDnsServers;
     private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
-    private static final String DNS_DEFAULT_SERVER2 = "4.2.2.2";
+    private static final String DNS_DEFAULT_SERVER2 = "8.8.4.4";
 
     // resampled each time we turn on tethering - used as cache for settings/config-val
     private boolean mDunRequired;  // configuration info - must use DUN apn on 3g
@@ -160,11 +176,21 @@
         mDhcpRange = context.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_dhcp_range);
         if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) {
-            mDhcpRange = new String[4];
+            mDhcpRange = new String[14];
             mDhcpRange[0] = DHCP_DEFAULT_RANGE1_START;
             mDhcpRange[1] = DHCP_DEFAULT_RANGE1_STOP;
             mDhcpRange[2] = DHCP_DEFAULT_RANGE2_START;
             mDhcpRange[3] = DHCP_DEFAULT_RANGE2_STOP;
+            mDhcpRange[4] = DHCP_DEFAULT_RANGE3_START;
+            mDhcpRange[5] = DHCP_DEFAULT_RANGE3_STOP;
+            mDhcpRange[6] = DHCP_DEFAULT_RANGE4_START;
+            mDhcpRange[7] = DHCP_DEFAULT_RANGE4_STOP;
+            mDhcpRange[8] = DHCP_DEFAULT_RANGE5_START;
+            mDhcpRange[9] = DHCP_DEFAULT_RANGE5_STOP;
+            mDhcpRange[10] = DHCP_DEFAULT_RANGE6_START;
+            mDhcpRange[11] = DHCP_DEFAULT_RANGE6_STOP;
+            mDhcpRange[12] = DHCP_DEFAULT_RANGE7_START;
+            mDhcpRange[13] = DHCP_DEFAULT_RANGE7_STOP;
         }
         mDunRequired = false; // resample when we turn on
 
@@ -172,6 +198,8 @@
                 com.android.internal.R.array.config_tether_usb_regexs);
         mTetherableWifiRegexs = context.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_wifi_regexs);
+        mTetherableBluetoothRegexs = context.getResources().getStringArray(
+                com.android.internal.R.array.config_tether_bluetooth_regexs);
         mUpstreamIfaceRegexs = context.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_upstream_regexs);
 
@@ -182,7 +210,7 @@
     }
 
     public void interfaceLinkStatusChanged(String iface, boolean link) {
-        Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link);
+        if (DEBUG) Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link);
         boolean found = false;
         boolean usb = false;
         if (isWifi(iface)) {
@@ -190,6 +218,8 @@
         } else if (isUsb(iface)) {
             found = true;
             usb = true;
+        } else if (isBluetooth(iface)) {
+            found = true;
         }
         if (found == false) return;
 
@@ -224,6 +254,12 @@
         return false;
     }
 
+    public boolean isBluetooth(String iface) {
+        for (String regex : mTetherableBluetoothRegexs) {
+            if (iface.matches(regex)) return true;
+        }
+        return false;
+    }
     public void interfaceAdded(String iface) {
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
@@ -236,29 +272,34 @@
             found = true;
             usb = true;
         }
+        if (isBluetooth(iface)) {
+            found = true;
+        }
         if (found == false) {
-            Log.d(TAG, iface + " is not a tetherable iface, ignoring");
+            if (DEBUG) Log.d(TAG, iface + " is not a tetherable iface, ignoring");
             return;
         }
 
         synchronized (mIfaces) {
             TetherInterfaceSM sm = mIfaces.get(iface);
             if (sm != null) {
-                Log.e(TAG, "active iface (" + iface + ") reported as added, ignoring");
+                if (DEBUG) Log.d(TAG, "active iface (" + iface + ") reported as added, ignoring");
                 return;
             }
             sm = new TetherInterfaceSM(iface, mLooper, usb);
             mIfaces.put(iface, sm);
             sm.start();
         }
-        Log.d(TAG, "interfaceAdded :" + iface);
+        if (DEBUG) Log.d(TAG, "interfaceAdded :" + iface);
     }
 
     public void interfaceRemoved(String iface) {
         synchronized (mIfaces) {
             TetherInterfaceSM sm = mIfaces.get(iface);
             if (sm == null) {
-                Log.e(TAG, "attempting to remove unknown iface (" + iface + "), ignoring");
+                if (DEBUG) {
+                    Log.e(TAG, "attempting to remove unknown iface (" + iface + "), ignoring");
+                }
                 return;
             }
             sm.sendMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN);
@@ -329,13 +370,14 @@
 
         boolean wifiTethered = false;
         boolean usbTethered = false;
+        boolean bluetoothTethered = false;
 
         synchronized (mIfaces) {
             Set ifaces = mIfaces.keySet();
             for (Object iface : ifaces) {
                 TetherInterfaceSM sm = mIfaces.get(iface);
                 if (sm != null) {
-                    if(sm.isErrored()) {
+                    if (sm.isErrored()) {
                         erroredList.add((String)iface);
                     } else if (sm.isAvailable()) {
                         availableList.add((String)iface);
@@ -344,6 +386,8 @@
                             usbTethered = true;
                         } else if (isWifi((String)iface)) {
                             wifiTethered = true;
+                      } else if (isBluetooth((String)iface)) {
+                            bluetoothTethered = true;
                         }
                         activeList.add((String)iface);
                     }
@@ -358,17 +402,25 @@
         broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
                 erroredList);
         mContext.sendStickyBroadcast(broadcast);
-        Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
-                activeList.size() + ", " + erroredList.size());
+        if (DEBUG) {
+            Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
+                    activeList.size() + ", " + erroredList.size());
+        }
 
         if (usbTethered) {
-            if (wifiTethered) {
+            if (wifiTethered || bluetoothTethered) {
                 showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_general);
             } else {
                 showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_usb);
             }
         } else if (wifiTethered) {
-            showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_wifi);
+            if (bluetoothTethered) {
+                showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_general);
+            } else {
+                showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_wifi);
+            }
+        } else if (bluetoothTethered) {
+            showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_bluetooth);
         } else {
             clearTetheredNotification();
         }
@@ -399,7 +451,7 @@
         CharSequence message = r.getText(com.android.internal.R.string.
                 tethered_notification_message);
 
-        if(mTetheredNotification == null) {
+        if (mTetheredNotification == null) {
             mTetheredNotification = new Notification();
             mTetheredNotification.when = 0;
         }
@@ -456,6 +508,7 @@
                 mUsbMassStorageOff = true;
                 updateUsbStatus();
             } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+                if (DEBUG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
                 mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
             } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
                 mBooted = true;
@@ -486,9 +539,9 @@
         }
     }
 
-    // toggled when we enter/leave the fully teathered state
+    // toggled when we enter/leave the fully tethered state
     private boolean enableUsbRndis(boolean enabled) {
-        Log.d(TAG, "enableUsbRndis(" + enabled + ")");
+        if (DEBUG) Log.d(TAG, "enableUsbRndis(" + enabled + ")");
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
                 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
 
@@ -513,7 +566,7 @@
 
     // configured when we start tethering and unconfig'd on error or conclusion
     private boolean configureUsbIface(boolean enabled) {
-        Log.d(TAG, "configureUsbIface(" + enabled + ")");
+        if (DEBUG) Log.d(TAG, "configureUsbIface(" + enabled + ")");
 
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
@@ -569,6 +622,10 @@
         return mTetherableWifiRegexs;
     }
 
+    public String[] getTetherableBluetoothRegexs() {
+        return mTetherableBluetoothRegexs;
+    }
+
     public String[] getUpstreamIfaceRegexs() {
         return mUpstreamIfaceRegexs;
     }
@@ -760,7 +817,7 @@
 
             @Override
             public boolean processMessage(Message message) {
-                Log.d(TAG, "InitialState.processMessage what=" + message.what);
+                if (DEBUG) Log.d(TAG, "InitialState.processMessage what=" + message.what);
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_REQUESTED:
@@ -801,7 +858,7 @@
             }
             @Override
             public boolean processMessage(Message message) {
-                Log.d(TAG, "StartingState.processMessage what=" + message.what);
+                if (DEBUG) Log.d(TAG, "StartingState.processMessage what=" + message.what);
                 boolean retValue = true;
                 switch (message.what) {
                     // maybe a parent class?
@@ -853,7 +910,7 @@
                     return;
                 }
                 if (mUsb) Tethering.this.enableUsbRndis(true);
-                Log.d(TAG, "Tethered " + mIfaceName);
+                if (DEBUG) Log.d(TAG, "Tethered " + mIfaceName);
                 setAvailable(false);
                 setTethered(true);
                 sendTetherStateChangedBroadcast();
@@ -864,7 +921,7 @@
             }
             @Override
             public boolean processMessage(Message message) {
-                Log.d(TAG, "TetheredState.processMessage what=" + message.what);
+                if (DEBUG) Log.d(TAG, "TetheredState.processMessage what=" + message.what);
                 boolean retValue = true;
                 boolean error = false;
                 switch (message.what) {
@@ -907,7 +964,7 @@
                         } else if (message.what == CMD_INTERFACE_DOWN) {
                             transitionTo(mUnavailableState);
                         }
-                        Log.d(TAG, "Untethered " + mIfaceName);
+                        if (DEBUG) Log.d(TAG, "Untethered " + mIfaceName);
                         break;
                     case CMD_TETHER_CONNECTION_CHANGED:
                         String newUpstreamIfaceName = (String)(message.obj);
@@ -980,7 +1037,7 @@
                                     ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
                             break;
                         }
-                        Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
+                        if (DEBUG) Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
                         sendTetherStateChangedBroadcast();
                         if (mUsb) {
                             if (!Tethering.this.configureUsbIface(false)) {
@@ -1218,8 +1275,10 @@
                 IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
                 IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
                 mConnectionRequested = false;
-                Log.d(TAG, "chooseUpstreamType(" + tryCell + "),  dunRequired ="
-                        + mDunRequired + ", iface=" + iface);
+                if (DEBUG) {
+                    Log.d(TAG, "chooseUpstreamType(" + tryCell + "),  dunRequired ="
+                            + mDunRequired + ", iface=" + iface);
+                }
                 if (iface != null) {
                     try {
                         if (mDunRequired) {
@@ -1227,7 +1286,7 @@
                             NetworkInfo info = cm.getNetworkInfo(
                                     ConnectivityManager.TYPE_MOBILE_DUN);
                             if (info.isConnected()) {
-                                Log.d(TAG, "setting dun ifacename =" + iface);
+                                if (DEBUG) Log.d(TAG, "setting dun ifacename =" + iface);
                                 // even if we're already connected - it may be somebody else's
                                 // refcount, so add our own
                                 turnOnMobileConnection();
@@ -1239,11 +1298,11 @@
                                 }
                             }
                         } else {
-                            Log.d(TAG, "checking if hipri brought us this connection");
+                            if (DEBUG) Log.d(TAG, "checking if hipri brought us this connection");
                             NetworkInfo info = cm.getNetworkInfo(
                                     ConnectivityManager.TYPE_MOBILE_HIPRI);
                             if (info.isConnected()) {
-                                Log.d(TAG, "yes - hipri in use");
+                                if (DEBUG) Log.d(TAG, "yes - hipri in use");
                                 // even if we're already connected - it may be sombody else's
                                 // refcount, so add our own
                                 turnOnMobileConnection();
@@ -1265,7 +1324,7 @@
                 notifyTetheredOfNewUpstreamIface(iface);
             }
             protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
-                Log.d(TAG, "notifying tethered with iface =" + ifaceName);
+                if (DEBUG) Log.d(TAG, "notifying tethered with iface =" + ifaceName);
                 mUpstreamIfaceName = ifaceName;
                 for (Object o : mNotifyList) {
                     TetherInterfaceSM sm = (TetherInterfaceSM)o;
@@ -1282,19 +1341,19 @@
             }
             @Override
             public boolean processMessage(Message message) {
-                Log.d(TAG, "MasterInitialState.processMessage what=" + message.what);
+                if (DEBUG) Log.d(TAG, "MasterInitialState.processMessage what=" + message.what);
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_MODE_REQUESTED:
                         mDunRequired = isDunRequired();
                         TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
-                        Log.d(TAG, "Tether Mode requested by " + who.toString());
+                        if (DEBUG) Log.d(TAG, "Tether Mode requested by " + who.toString());
                         mNotifyList.add(who);
                         transitionTo(mTetherModeAliveState);
                         break;
                     case CMD_TETHER_MODE_UNREQUESTED:
                         who = (TetherInterfaceSM)message.obj;
-                        Log.d(TAG, "Tether Mode unrequested by " + who.toString());
+                        if (DEBUG) Log.d(TAG, "Tether Mode unrequested by " + who.toString());
                         int index = mNotifyList.indexOf(who);
                         if (index != -1) {
                             mNotifyList.remove(who);
@@ -1324,7 +1383,7 @@
             }
             @Override
             public boolean processMessage(Message message) {
-                Log.d(TAG, "TetherModeAliveState.processMessage what=" + message.what);
+                if (DEBUG) Log.d(TAG, "TetherModeAliveState.processMessage what=" + message.what);
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_MODE_REQUESTED:
@@ -1352,8 +1411,10 @@
                         // make sure we're still using a requested connection - may have found
                         // wifi or something since then.
                         if (mConnectionRequested) {
-                            Log.d(TAG, "renewing mobile connection - requeuing for another " +
-                                    CELL_CONNECTION_RENEW_MS + "ms");
+                            if (DEBUG) {
+                                Log.d(TAG, "renewing mobile connection - requeuing for another " +
+                                        CELL_CONNECTION_RENEW_MS + "ms");
+                            }
                             turnOnMobileConnection();
                         }
                         break;
diff --git a/services/java/com/android/server/location/ComprehensiveCountryDetector.java b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
new file mode 100755
index 0000000..e9ce3ce
--- /dev/null
+++ b/services/java/com/android/server/location/ComprehensiveCountryDetector.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.location.Country;
+import android.location.CountryListener;
+import android.location.Geocoder;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import java.util.Locale;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * This class is used to detect the country where the user is. The sources of
+ * country are queried in order of reliability, like
+ * <ul>
+ * <li>Mobile network</li>
+ * <li>Location</li>
+ * <li>SIM's country</li>
+ * <li>Phone's locale</li>
+ * </ul>
+ * <p>
+ * Call the {@link #detectCountry()} to get the available country immediately.
+ * <p>
+ * To be notified of the future country change, using the
+ * {@link #setCountryListener(CountryListener)}
+ * <p>
+ * Using the {@link #stop()} to stop listening to the country change.
+ * <p>
+ * The country information will be refreshed every
+ * {@link #LOCATION_REFRESH_INTERVAL} once the location based country is used.
+ *
+ * @hide
+ */
+public class ComprehensiveCountryDetector extends CountryDetectorBase {
+
+    private final static String TAG = "ComprehensiveCountryDetector";
+
+    /**
+     * The refresh interval when the location based country was used
+     */
+    private final static long LOCATION_REFRESH_INTERVAL = 1000 * 60 * 60 * 24; // 1 day
+
+    protected CountryDetectorBase mLocationBasedCountryDetector;
+    protected Timer mLocationRefreshTimer;
+
+    private final int mPhoneType;
+    private Country mCountry;
+    private TelephonyManager mTelephonyManager;
+    private Country mCountryFromLocation;
+    private boolean mStopped = false;
+    private ServiceState mLastState;
+
+    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onServiceStateChanged(ServiceState serviceState) {
+            // TODO: Find out how often we will be notified, if this method is called too
+            // many times, let's consider querying the network.
+            Slog.d(TAG, "onServiceStateChanged");
+            // We only care the state change
+            if (mLastState == null || mLastState.getState() != serviceState.getState()) {
+                detectCountry(true, true);
+                mLastState = new ServiceState(serviceState);
+            }
+        }
+    };
+
+    /**
+     * The listener for receiving the notification from LocationBasedCountryDetector.
+     */
+    private CountryListener mLocationBasedCountryDetectionListener = new CountryListener() {
+        public void onCountryDetected(Country country) {
+            mCountryFromLocation = country;
+            // Don't start the LocationBasedCountryDetector.
+            detectCountry(true, false);
+            stopLocationBasedDetector();
+        }
+    };
+
+    public ComprehensiveCountryDetector(Context context) {
+        super(context);
+        mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        mPhoneType = mTelephonyManager.getPhoneType();
+    }
+
+    @Override
+    public Country detectCountry() {
+        // Don't start the LocationBasedCountryDetector if we have been stopped.
+        return detectCountry(false, !mStopped);
+    }
+
+    @Override
+    public void stop() {
+        Slog.i(TAG, "Stop the detector.");
+        cancelLocationRefresh();
+        removePhoneStateListener();
+        stopLocationBasedDetector();
+        mListener = null;
+        mStopped = true;
+    }
+
+    /**
+     * Get the country from different sources in order of the reliability.
+     */
+    private Country getCountry() {
+        Country result = null;
+        result = getNetworkBasedCountry();
+        if (result == null) {
+            result = getLastKnownLocationBasedCountry();
+        }
+        if (result == null) {
+            result = getSimBasedCountry();
+        }
+        if (result == null) {
+            result = getLocaleCountry();
+        }
+        return result;
+    }
+
+    /**
+     * @return the country from the mobile network.
+     */
+    protected Country getNetworkBasedCountry() {
+        String countryIso = null;
+        // TODO: The document says the result may be unreliable on CDMA networks. Shall we use
+        // it on CDMA phone? We may test the Android primarily used countries.
+        if (mPhoneType == TelephonyManager.PHONE_TYPE_GSM) {
+            countryIso = mTelephonyManager.getNetworkCountryIso();
+            if (!TextUtils.isEmpty(countryIso)) {
+                return new Country(countryIso, Country.COUNTRY_SOURCE_NETWORK);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return the cached location based country.
+     */
+    protected Country getLastKnownLocationBasedCountry() {
+        return mCountryFromLocation;
+    }
+
+    /**
+     * @return the country from SIM card
+     */
+    protected Country getSimBasedCountry() {
+        String countryIso = null;
+        countryIso = mTelephonyManager.getSimCountryIso();
+        if (!TextUtils.isEmpty(countryIso)) {
+            return new Country(countryIso, Country.COUNTRY_SOURCE_SIM);
+        }
+        return null;
+    }
+
+    /**
+     * @return the country from the system's locale.
+     */
+    protected Country getLocaleCountry() {
+        Locale defaultLocale = Locale.getDefault();
+        if (defaultLocale != null) {
+            return new Country(defaultLocale.getCountry(), Country.COUNTRY_SOURCE_LOCALE);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @param notifyChange indicates whether the listener should be notified the change of the
+     * country
+     * @param startLocationBasedDetection indicates whether the LocationBasedCountryDetector could
+     * be started if the current country source is less reliable than the location.
+     * @return the current available UserCountry
+     */
+    private Country detectCountry(boolean notifyChange, boolean startLocationBasedDetection) {
+        Country country = getCountry();
+        runAfterDetectionAsync(mCountry != null ? new Country(mCountry) : mCountry, country,
+                notifyChange, startLocationBasedDetection);
+        mCountry = country;
+        return mCountry;
+    }
+
+    /**
+     * Run the tasks in the service's thread.
+     */
+    protected void runAfterDetectionAsync(final Country country, final Country detectedCountry,
+            final boolean notifyChange, final boolean startLocationBasedDetection) {
+        mHandler.post(new Runnable() {
+            public void run() {
+                runAfterDetection(
+                        country, detectedCountry, notifyChange, startLocationBasedDetection);
+            }
+        });
+    }
+
+    @Override
+    public void setCountryListener(CountryListener listener) {
+        CountryListener prevListener = mListener;
+        mListener = listener;
+        if (mListener == null) {
+            // Stop listening all services
+            removePhoneStateListener();
+            stopLocationBasedDetector();
+            cancelLocationRefresh();
+        } else if (prevListener == null) {
+            addPhoneStateListener();
+            detectCountry(false, true);
+        }
+    }
+
+    void runAfterDetection(final Country country, final Country detectedCountry,
+            final boolean notifyChange, final boolean startLocationBasedDetection) {
+        if (notifyChange) {
+            notifyIfCountryChanged(country, detectedCountry);
+        }
+        if (startLocationBasedDetection && (detectedCountry == null
+                || detectedCountry.getSource() > Country.COUNTRY_SOURCE_LOCATION)
+                && isAirplaneModeOff() && mListener != null && isGeoCoderImplemented()) {
+            // Start finding location when the source is less reliable than the
+            // location and the airplane mode is off (as geocoder will not
+            // work).
+            // TODO : Shall we give up starting the detector within a
+            // period of time?
+            startLocationBasedDetector(mLocationBasedCountryDetectionListener);
+        }
+        if (detectedCountry == null
+                || detectedCountry.getSource() >= Country.COUNTRY_SOURCE_LOCATION) {
+            // Schedule the location refresh if the country source is
+            // not more reliable than the location or no country is
+            // found.
+            // TODO: Listen to the preference change of GPS, Wifi etc,
+            // and start detecting the country.
+            scheduleLocationRefresh();
+        } else {
+            // Cancel the location refresh once the current source is
+            // more reliable than the location.
+            cancelLocationRefresh();
+            stopLocationBasedDetector();
+        }
+    }
+
+    /**
+     * Find the country from LocationProvider.
+     */
+    private synchronized void startLocationBasedDetector(CountryListener listener) {
+        if (mLocationBasedCountryDetector != null) {
+            return;
+        }
+        mLocationBasedCountryDetector = createLocationBasedCountryDetector();
+        mLocationBasedCountryDetector.setCountryListener(listener);
+        mLocationBasedCountryDetector.detectCountry();
+    }
+
+    private synchronized void stopLocationBasedDetector() {
+        if (mLocationBasedCountryDetector != null) {
+            mLocationBasedCountryDetector.stop();
+            mLocationBasedCountryDetector = null;
+        }
+    }
+
+    protected CountryDetectorBase createLocationBasedCountryDetector() {
+        return new LocationBasedCountryDetector(mContext);
+    }
+
+    protected boolean isAirplaneModeOff() {
+        return Settings.System.getInt(
+                mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 0;
+    }
+
+    /**
+     * Notify the country change.
+     */
+    private void notifyIfCountryChanged(final Country country, final Country detectedCountry) {
+        if (detectedCountry != null && mListener != null
+                && (country == null || !country.equals(detectedCountry))) {
+            Slog.d(TAG,
+                    "The country was changed from " + country != null ? country.getCountryIso() :
+                        country + " to " + detectedCountry.getCountryIso());
+            notifyListener(detectedCountry);
+        }
+    }
+
+    /**
+     * Schedule the next location refresh. We will do nothing if the scheduled task exists.
+     */
+    private synchronized void scheduleLocationRefresh() {
+        if (mLocationRefreshTimer != null) return;
+        mLocationRefreshTimer = new Timer();
+        mLocationRefreshTimer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                mLocationRefreshTimer = null;
+                detectCountry(false, true);
+            }
+        }, LOCATION_REFRESH_INTERVAL);
+    }
+
+    /**
+     * Cancel the scheduled refresh task if it exists
+     */
+    private synchronized void cancelLocationRefresh() {
+        if (mLocationRefreshTimer != null) {
+            mLocationRefreshTimer.cancel();
+            mLocationRefreshTimer = null;
+        }
+    }
+
+    protected synchronized void addPhoneStateListener() {
+        if (mPhoneStateListener == null && mPhoneType == TelephonyManager.PHONE_TYPE_GSM) {
+            mLastState = null;
+            mPhoneStateListener = new PhoneStateListener() {
+                @Override
+                public void onServiceStateChanged(ServiceState serviceState) {
+                    // TODO: Find out how often we will be notified, if this
+                    // method is called too
+                    // many times, let's consider querying the network.
+                    Slog.d(TAG, "onServiceStateChanged");
+                    // We only care the state change
+                    if (mLastState == null || mLastState.getState() != serviceState.getState()) {
+                        detectCountry(true, true);
+                        mLastState = new ServiceState(serviceState);
+                    }
+                }
+            };
+            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+        }
+    }
+
+    protected synchronized void removePhoneStateListener() {
+        if (mPhoneStateListener != null) {
+            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+            mPhoneStateListener = null;
+        }
+    }
+
+    protected boolean isGeoCoderImplemented() {
+        return Geocoder.isPresent();
+    }
+}
diff --git a/services/java/com/android/server/location/CountryDetectorBase.java b/services/java/com/android/server/location/CountryDetectorBase.java
new file mode 100644
index 0000000..8326ef9
--- /dev/null
+++ b/services/java/com/android/server/location/CountryDetectorBase.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.location.Country;
+import android.location.CountryListener;
+import android.os.Handler;
+
+/**
+ * This class defines the methods need to be implemented by the country
+ * detector.
+ * <p>
+ * Calling {@link #detectCountry} to start detecting the country. The country
+ * could be returned immediately if it is available.
+ *
+ * @hide
+ */
+public abstract class CountryDetectorBase {
+    protected final Handler mHandler;
+    protected final Context mContext;
+    protected CountryListener mListener;
+    protected Country mDetectedCountry;
+
+    public CountryDetectorBase(Context ctx) {
+        mContext = ctx;
+        mHandler = new Handler();
+    }
+
+    /**
+     * Start detecting the country that the user is in.
+     *
+     * @return the country if it is available immediately, otherwise null should
+     *         be returned.
+     */
+    public abstract Country detectCountry();
+
+    /**
+     * Register a listener to receive the notification when the country is detected or changed.
+     * <p>
+     * The previous listener will be replaced if it exists.
+     */
+    public void setCountryListener(CountryListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Stop detecting the country. The detector should release all system services and be ready to
+     * be freed
+     */
+    public abstract void stop();
+
+    protected void notifyListener(Country country) {
+        if (mListener != null) {
+            mListener.onCountryDetected(country);
+        }
+    }
+}
diff --git a/services/java/com/android/server/location/LocationBasedCountryDetector.java b/services/java/com/android/server/location/LocationBasedCountryDetector.java
new file mode 100755
index 0000000..139f05d
--- /dev/null
+++ b/services/java/com/android/server/location/LocationBasedCountryDetector.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.location;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import android.content.Context;
+import android.location.Address;
+import android.location.Country;
+import android.location.Geocoder;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.util.Slog;
+
+/**
+ * This class detects which country the user currently is in through the enabled
+ * location providers and the GeoCoder
+ * <p>
+ * Use {@link #detectCountry} to start querying. If the location can not be
+ * resolved within the given time, the last known location will be used to get
+ * the user country through the GeoCoder. The IllegalStateException will be
+ * thrown if there is a ongoing query.
+ * <p>
+ * The current query can be stopped by {@link #stop()}
+ *
+ * @hide
+ */
+public class LocationBasedCountryDetector extends CountryDetectorBase {
+    private final static String TAG = "LocationBasedCountryDetector";
+    private final static long QUERY_LOCATION_TIMEOUT = 1000 * 60 * 5; // 5 mins
+
+    /**
+     * Used for canceling location query
+     */
+    protected Timer mTimer;
+
+    /**
+     * The thread to query the country from the GeoCoder.
+     */
+    protected Thread mQueryThread;
+    protected List<LocationListener> mLocationListeners;
+
+    private LocationManager mLocationManager;
+    private List<String> mEnabledProviders;
+
+    public LocationBasedCountryDetector(Context ctx) {
+        super(ctx);
+        mLocationManager = (LocationManager) ctx.getSystemService(Context.LOCATION_SERVICE);
+    }
+
+    /**
+     * @return the ISO 3166-1 two letters country code from the location
+     */
+    protected String getCountryFromLocation(Location location) {
+        String country = null;
+        Geocoder geoCoder = new Geocoder(mContext);
+        try {
+            List<Address> addresses = geoCoder.getFromLocation(
+                    location.getLatitude(), location.getLongitude(), 1);
+            if (addresses != null && addresses.size() > 0) {
+                country = addresses.get(0).getCountryCode();
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Exception occurs when getting country from location");
+        }
+        return country;
+    }
+
+    /**
+     * Register the listeners with the location providers
+     */
+    protected void registerEnabledProviders(List<LocationListener> listeners) {
+        int total = listeners.size();
+        for (int i = 0; i< total; i++) {
+            mLocationManager.requestLocationUpdates(
+                    mEnabledProviders.get(i), 0, 0, listeners.get(i));
+        }
+    }
+
+    /**
+     * Unregister the listeners with the location providers
+     */
+    protected void unregisterProviders(List<LocationListener> listeners) {
+        for (LocationListener listener : listeners) {
+            mLocationManager.removeUpdates(listener);
+        }
+    }
+
+    /**
+     * @return the last known location from all providers
+     */
+    protected Location getLastKnownLocation() {
+        List<String> providers = mLocationManager.getAllProviders();
+        Location bestLocation = null;
+        for (String provider : providers) {
+            Location lastKnownLocation = mLocationManager.getLastKnownLocation(provider);
+            if (lastKnownLocation != null) {
+                if (bestLocation == null || bestLocation.getTime() < lastKnownLocation.getTime()) {
+                    bestLocation = lastKnownLocation;
+                }
+            }
+        }
+        return bestLocation;
+    }
+
+    /**
+     * @return the timeout for querying the location.
+     */
+    protected long getQueryLocationTimeout() {
+        return QUERY_LOCATION_TIMEOUT;
+    }
+
+    /**
+     * @return the total number of enabled location providers
+     */
+    protected int getTotalEnabledProviders() {
+        if (mEnabledProviders == null) {
+            mEnabledProviders = mLocationManager.getProviders(true);
+        }
+        return mEnabledProviders.size();
+    }
+
+    /**
+     * Start detecting the country.
+     * <p>
+     * Queries the location from all location providers, then starts a thread to query the
+     * country from GeoCoder.
+     */
+    @Override
+    public synchronized Country detectCountry() {
+        if (mLocationListeners  != null) {
+            throw new IllegalStateException();
+        }
+        // Request the location from all enabled providers.
+        int totalProviders = getTotalEnabledProviders();
+        if (totalProviders > 0) {
+            mLocationListeners = new ArrayList<LocationListener>(totalProviders);
+            for (int i = 0; i < totalProviders; i++) {
+                LocationListener listener = new LocationListener () {
+                    public void onLocationChanged(Location location) {
+                        if (location != null) {
+                            LocationBasedCountryDetector.this.stop();
+                            queryCountryCode(location);
+                        }
+                    }
+                    public void onProviderDisabled(String provider) {
+                    }
+                    public void onProviderEnabled(String provider) {
+                    }
+                    public void onStatusChanged(String provider, int status, Bundle extras) {
+                    }
+                };
+                mLocationListeners.add(listener);
+            }
+            registerEnabledProviders(mLocationListeners);
+            mTimer = new Timer();
+            mTimer.schedule(new TimerTask() {
+                @Override
+                public void run() {
+                    mTimer = null;
+                    LocationBasedCountryDetector.this.stop();
+                    // Looks like no provider could provide the location, let's try the last
+                    // known location.
+                    queryCountryCode(getLastKnownLocation());
+                }
+            }, getQueryLocationTimeout());
+        } else {
+            // There is no provider enabled.
+            queryCountryCode(getLastKnownLocation());
+        }
+        return mDetectedCountry;
+    }
+
+    /**
+     * Stop the current query without notifying the listener.
+     */
+    @Override
+    public synchronized void stop() {
+        if (mLocationListeners != null) {
+            unregisterProviders(mLocationListeners);
+            mLocationListeners = null;
+        }
+        if (mTimer != null) {
+            mTimer.cancel();
+            mTimer = null;
+        }
+    }
+
+    /**
+     * Start a new thread to query the country from Geocoder.
+     */
+    private synchronized void queryCountryCode(final Location location) {
+        if (location == null) {
+            notifyListener(null);
+            return;
+        }
+        if (mQueryThread != null) return;
+        mQueryThread = new Thread(new Runnable() {
+            public void run() {
+                String countryIso = null;
+                if (location != null) {
+                    countryIso = getCountryFromLocation(location);
+                }
+                if (countryIso != null) {
+                    mDetectedCountry = new Country(countryIso, Country.COUNTRY_SOURCE_LOCATION);
+                } else {
+                    mDetectedCountry = null;
+                }
+                notifyListener(mDetectedCountry);
+                mQueryThread = null;
+            }
+        });
+        mQueryThread.start();
+    }
+}
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index c90879d..d10f54f 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -8,6 +8,7 @@
     com_android_server_LightsService.cpp \
     com_android_server_PowerManagerService.cpp \
     com_android_server_SystemServer.cpp \
+    com_android_server_UsbObserver.cpp \
     com_android_server_VibratorService.cpp \
 	com_android_server_location_GpsLocationProvider.cpp \
     onload.cpp
@@ -26,6 +27,8 @@
 	libui \
     libsurfaceflinger_client
 
+LOCAL_STATIC_LIBRARIES := libusbhost
+
 ifeq ($(TARGET_SIMULATOR),true)
 ifeq ($(TARGET_OS),linux)
 ifeq ($(TARGET_ARCH),x86)
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 1bd1874..d4c4ba4 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -857,7 +857,7 @@
         JNIEnv* env = jniEnv();
         jint wmActions = env->CallIntMethod(mCallbacksObj,
                 gCallbacksClassInfo.interceptKeyBeforeQueueing,
-                when, keyCode, action == AKEY_EVENT_ACTION_DOWN, policyFlags, isScreenOn);
+                when, action, flags, keyCode, scanCode, policyFlags, isScreenOn);
         if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
             wmActions = 0;
         }
@@ -926,7 +926,7 @@
         jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
                 gCallbacksClassInfo.interceptKeyBeforeDispatching,
                 inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(),
-                keyEvent->getKeyCode(), keyEvent->getMetaState(),
+                keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
                 keyEvent->getRepeatCount(), policyFlags);
         bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
 
@@ -1250,6 +1250,25 @@
     env->SetIntField(configObj, gConfigurationClassInfo.navigation, config.navigation);
 }
 
+static jboolean android_server_InputManager_nativeTransferTouchFocus(JNIEnv* env,
+        jclass clazz, jobject fromChannelObj, jobject toChannelObj) {
+    if (checkInputManagerUnitialized(env)) {
+        return false;
+    }
+
+    sp<InputChannel> fromChannel =
+            android_view_InputChannel_getInputChannel(env, fromChannelObj);
+    sp<InputChannel> toChannel =
+            android_view_InputChannel_getInputChannel(env, toChannelObj);
+
+    if (fromChannel == NULL || toChannel == NULL) {
+        return false;
+    }
+
+    return gNativeInputManager->getInputManager()->getDispatcher()->
+            transferTouchFocus(fromChannel, toChannel);
+}
+
 static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) {
     if (checkInputManagerUnitialized(env)) {
         return NULL;
@@ -1298,6 +1317,8 @@
             (void*) android_server_InputManager_nativeGetInputDeviceIds },
     { "nativeGetInputConfiguration", "(Landroid/content/res/Configuration;)V",
             (void*) android_server_InputManager_nativeGetInputConfiguration },
+    { "nativeTransferTouchFocus", "(Landroid/view/InputChannel;Landroid/view/InputChannel;)Z",
+            (void*) android_server_InputManager_nativeTransferTouchFocus },
     { "nativeDump", "()Ljava/lang/String;",
             (void*) android_server_InputManager_nativeDump },
 };
@@ -1337,10 +1358,10 @@
             "notifyANR", "(Ljava/lang/Object;Landroid/view/InputChannel;)J");
 
     GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz,
-            "interceptKeyBeforeQueueing", "(JIZIZ)I");
+            "interceptKeyBeforeQueueing", "(JIIIIIZ)I");
 
     GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz,
-            "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIII)Z");
+            "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIIII)Z");
 
     GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
             "checkInjectEventsPermission", "(II)Z");
diff --git a/services/jni/com_android_server_UsbObserver.cpp b/services/jni/com_android_server_UsbObserver.cpp
new file mode 100644
index 0000000..7c478d5
--- /dev/null
+++ b/services/jni/com_android_server_UsbObserver.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "UsbObserver"
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "utils/Vector.h"
+
+#include <usbhost/usbhost.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
+#include <linux/usb/ch9.h>
+#else
+#include <linux/usb_ch9.h>
+#endif
+
+#include <stdio.h>
+
+namespace android
+{
+
+static jmethodID method_usbCameraAdded;
+static jmethodID method_usbCameraRemoved;
+
+Vector<int> mDeviceList;
+
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+    if (env->ExceptionCheck()) {
+        LOGE("An exception was thrown by callback '%s'.", methodName);
+        LOGE_EX(env);
+        env->ExceptionClear();
+    }
+}
+
+static int usb_device_added(const char *devname, void* client_data) {
+    // check to see if it is a camera
+    struct usb_descriptor_header* desc;
+    struct usb_descriptor_iter iter;
+
+    struct usb_device *device = usb_device_open(devname);
+    if (!device) {
+        LOGE("usb_device_open failed\n");
+        return 0;
+    }
+
+    usb_descriptor_iter_init(device, &iter);
+
+    while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
+        if (desc->bDescriptorType == USB_DT_INTERFACE) {
+            struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
+
+            if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
+                interface->bInterfaceSubClass == 1 && // Still Image Capture
+                interface->bInterfaceProtocol == 1)     // Picture Transfer Protocol (PIMA 15470)
+            {
+                LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
+                        usb_device_get_product_name(device));
+
+                // interface should be followed by three endpoints
+                struct usb_endpoint_descriptor *ep;
+                struct usb_endpoint_descriptor *ep_in_desc = NULL;
+                struct usb_endpoint_descriptor *ep_out_desc = NULL;
+                struct usb_endpoint_descriptor *ep_intr_desc = NULL;
+                for (int i = 0; i < 3; i++) {
+                    ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
+                    if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
+                        LOGE("endpoints not found\n");
+                        goto done;
+                    }
+                    if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+                        if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+                            ep_in_desc = ep;
+                        else
+                            ep_out_desc = ep;
+                    } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
+                        ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+                        ep_intr_desc = ep;
+                    }
+                }
+                if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
+                    LOGE("endpoints not found\n");
+                    goto done;
+                }
+
+                // if we got here, we found a camera
+                JNIEnv* env = AndroidRuntime::getJNIEnv();
+                jobject thiz = (jobject)client_data;
+
+                int id = usb_device_get_unique_id_from_name(devname);
+                mDeviceList.add(id);
+
+                env->CallVoidMethod(thiz, method_usbCameraAdded, id);
+                checkAndClearExceptionFromCallback(env, __FUNCTION__);
+            }
+        }
+    }
+done:
+    usb_device_close(device);
+    return 0;
+}
+
+static int usb_device_removed(const char *devname, void* client_data) {
+    int id = usb_device_get_unique_id_from_name(devname);
+
+    // see if it is a device we know about
+    for (int i = 0; i < mDeviceList.size(); i++) {
+        if (id  == mDeviceList[i]) {
+            mDeviceList.removeAt(i);
+
+            JNIEnv* env = AndroidRuntime::getJNIEnv();
+            jobject thiz = (jobject)client_data;
+
+            env->CallVoidMethod(thiz, method_usbCameraRemoved, id);
+            checkAndClearExceptionFromCallback(env, __FUNCTION__);
+            break;
+        }
+    }
+    return 0;
+}
+
+static void android_server_UsbObserver_monitorUsbHostBus(JNIEnv *env, jobject thiz)
+{
+    struct usb_host_context* context = usb_host_init();
+    if (!context) {
+        LOGE("usb_host_init failed");
+        return;
+    }
+    // this will never return so it is safe to pass thiz directly
+    usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
+}
+
+static JNINativeMethod method_table[] = {
+    { "monitorUsbHostBus", "()V", (void*)android_server_UsbObserver_monitorUsbHostBus }
+};
+
+int register_android_server_UsbObserver(JNIEnv *env)
+{
+    jclass clazz = env->FindClass("com/android/server/UsbObserver");
+    if (clazz == NULL) {
+        LOGE("Can't find com/android/server/UsbObserver");
+        return -1;
+    }
+    method_usbCameraAdded = env->GetMethodID(clazz, "usbCameraAdded", "(I)V");
+    if (method_usbCameraAdded == NULL) {
+        LOGE("Can't find usbCameraAdded");
+        return -1;
+    }
+    method_usbCameraRemoved = env->GetMethodID(clazz, "usbCameraRemoved", "(I)V");
+    if (method_usbCameraRemoved == NULL) {
+        LOGE("Can't find usbCameraRemoved");
+        return -1;
+    }
+
+    return jniRegisterNativeMethods(env, "com/android/server/UsbObserver",
+            method_table, NELEM(method_table));
+}
+
+};
diff --git a/services/jni/com_android_server_VibratorService.cpp b/services/jni/com_android_server_VibratorService.cpp
index 6ec5c07..0912d43 100644
--- a/services/jni/com_android_server_VibratorService.cpp
+++ b/services/jni/com_android_server_VibratorService.cpp
@@ -29,6 +29,11 @@
 namespace android
 {
 
+static jboolean vibratorExists(JNIEnv *env, jobject clazz)
+{
+    return vibrator_exists() > 0 ? JNI_TRUE : JNI_FALSE;
+}
+
 static void vibratorOn(JNIEnv *env, jobject clazz, jlong timeout_ms)
 {
     // LOGI("vibratorOn\n");
@@ -42,6 +47,7 @@
 }
 
 static JNINativeMethod method_table[] = {
+    { "vibratorExists", "()Z", (void*)vibratorExists },
     { "vibratorOn", "(J)V", (void*)vibratorOn },
     { "vibratorOff", "()V", (void*)vibratorOff }
 };
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index cd4f0a4..3502aca 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -9,6 +9,7 @@
 int register_android_server_InputManager(JNIEnv* env);
 int register_android_server_LightsService(JNIEnv* env);
 int register_android_server_PowerManagerService(JNIEnv* env);
+int register_android_server_UsbObserver(JNIEnv* env);
 int register_android_server_VibratorService(JNIEnv* env);
 int register_android_server_SystemServer(JNIEnv* env);
 int register_android_server_location_GpsLocationProvider(JNIEnv* env);
@@ -32,6 +33,7 @@
     register_android_server_LightsService(env);
     register_android_server_AlarmManagerService(env);
     register_android_server_BatteryService(env);
+    register_android_server_UsbObserver(env);
     register_android_server_VibratorService(env);
     register_android_server_SystemServer(env);
     register_android_server_location_GpsLocationProvider(env);
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index e2f8a74..1c2a2c8 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -5,6 +5,7 @@
     clz.cpp.arm \
     DisplayHardware/DisplayHardware.cpp \
     DisplayHardware/DisplayHardwareBase.cpp \
+    DisplayHardware/HWComposer.cpp \
     BlurFilter.cpp.arm \
     GLExtensions.cpp \
     Layer.cpp \
@@ -21,7 +22,7 @@
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 
 ifeq ($(TARGET_BOARD_PLATFORM), omap3)
-	LOCAL_CFLAGS += -DNO_RGBX_8888
+	LOCAL_CFLAGS += -DNO_RGBX_8888 -DHAS_PUSH_BUFFERS
 endif
 ifeq ($(TARGET_BOARD_PLATFORM), s5pc110)
 	LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 28a512e..8926c03 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -36,11 +36,11 @@
 
 #include "DisplayHardware/DisplayHardware.h"
 
-#include <hardware/copybit.h>
 #include <hardware/overlay.h>
 #include <hardware/gralloc.h>
 
 #include "GLExtensions.h"
+#include "HWComposer.h"
 
 using namespace android;
 
@@ -76,7 +76,7 @@
         const sp<SurfaceFlinger>& flinger,
         uint32_t dpy)
     : DisplayHardwareBase(flinger, dpy),
-      mFlags(0)
+      mFlags(0), mHwc(0)
 {
     init(dpy);
 }
@@ -272,6 +272,17 @@
 
     // Unbind the context from this thread
     eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+
+    // initialize the H/W composer
+    mHwc = new HWComposer();
+    if (mHwc->initCheck() == NO_ERROR) {
+        mHwc->setFrameBuffer(mDisplay, mSurface);
+    }
+}
+
+HWComposer& DisplayHardware::getHwComposer() const {
+    return *mHwc;
 }
 
 /*
@@ -291,6 +302,9 @@
 void DisplayHardware::releaseScreen() const
 {
     DisplayHardwareBase::releaseScreen();
+    if (mHwc->initCheck() == NO_ERROR) {
+        mHwc->release();
+    }
 }
 
 void DisplayHardware::acquireScreen() const
@@ -331,7 +345,12 @@
     }
     
     mPageFlipCount++;
-    eglSwapBuffers(dpy, surface);
+
+    if (mHwc->initCheck() == NO_ERROR) {
+        mHwc->commit();
+    } else {
+        eglSwapBuffers(dpy, surface);
+    }
     checkEGLErrors("eglSwapBuffers");
 
     // for debugging
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index 2d7900c..75b55df 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -34,12 +34,11 @@
 #include "DisplayHardware/DisplayHardwareBase.h"
 
 struct overlay_control_device_t;
-struct framebuffer_device_t;
-struct copybit_image_t;
 
 namespace android {
 
 class FramebufferNativeWindow;
+class HWComposer;
 
 class DisplayHardware : public DisplayHardwareBase
 {
@@ -80,6 +79,9 @@
     uint32_t getPageFlipCount() const;
     EGLDisplay getEGLDisplay() const { return mDisplay; }
     overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; }
+
+    // Hardware Composer
+    HWComposer& getHwComposer() const;
     
     status_t compositionComplete() const;
     
@@ -110,6 +112,8 @@
     GLint           mMaxViewportDims;
     GLint           mMaxTextureSize;
     
+    HWComposer*     mHwc;
+
     sp<FramebufferNativeWindow> mNativeWindow;
     overlay_control_device_t* mOverlayEngine;
 };
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
new file mode 100644
index 0000000..4af274b
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+#include <hardware/hardware.h>
+
+#include <cutils/log.h>
+
+#include <EGL/egl.h>
+
+#include "HWComposer.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+HWComposer::HWComposer()
+    : mModule(0), mHwc(0), mList(0), mCapacity(0),
+      mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE)
+{
+    int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
+    LOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
+    if (err == 0) {
+        err = hwc_open(mModule, &mHwc);
+        LOGE_IF(err, "%s device failed to initialize (%s)",
+                HWC_HARDWARE_COMPOSER, strerror(-err));
+    }
+}
+
+HWComposer::~HWComposer() {
+    free(mList);
+    if (mHwc) {
+        hwc_close(mHwc);
+    }
+}
+
+status_t HWComposer::initCheck() const {
+    return mHwc ? NO_ERROR : NO_INIT;
+}
+
+void HWComposer::setFrameBuffer(EGLDisplay dpy, EGLSurface sur) {
+    mDpy = (hwc_display_t)dpy;
+    mSur = (hwc_surface_t)sur;
+}
+
+status_t HWComposer::createWorkList(size_t numLayers) {
+    if (mHwc) {
+        if (!mList || mCapacity < numLayers) {
+            free(mList);
+            size_t size = sizeof(hwc_layer_list) + numLayers*sizeof(hwc_layer_t);
+            mList = (hwc_layer_list_t*)malloc(size);
+            mCapacity = numLayers;
+        }
+        mList->flags = HWC_GEOMETRY_CHANGED;
+        mList->numHwLayers = numLayers;
+    }
+    return NO_ERROR;
+}
+
+status_t HWComposer::prepare() const {
+    int err = mHwc->prepare(mHwc, mList);
+    return (status_t)err;
+}
+
+status_t HWComposer::commit() const {
+    int err = mHwc->set(mHwc, mDpy, mSur, mList);
+    if (mList) {
+        mList->flags &= ~HWC_GEOMETRY_CHANGED;
+    }
+    return (status_t)err;
+}
+
+status_t HWComposer::release() const {
+    int err = mHwc->set(mHwc, NULL, NULL, NULL);
+    return (status_t)err;
+}
+
+size_t HWComposer::getNumLayers() const {
+    return mList ? mList->numHwLayers : 0;
+}
+
+hwc_layer_t* HWComposer::getLayers() const {
+    return mList ? mList->hwLayers : 0;
+}
+
+void HWComposer::dump(String8& result, char* buffer, size_t SIZE) const {
+    if (mHwc && mList) {
+        result.append("Hardware Composer state:\n");
+
+        snprintf(buffer, SIZE, "  numHwLayers=%u, flags=%08x\n",
+                mList->numHwLayers, mList->flags);
+        result.append(buffer);
+
+        for (size_t i=0 ; i<mList->numHwLayers ; i++) {
+            const hwc_layer_t& l(mList->hwLayers[i]);
+            snprintf(buffer, SIZE, "  %8s | %08x | %08x | %02x | %04x | [%5d,%5d,%5d,%5d] |  [%5d,%5d,%5d,%5d]\n",
+                    l.compositionType ? "OVERLAY" : "FB",
+                    l.hints, l.flags, l.transform, l.blending,
+                    l.sourceCrop.left, l.sourceCrop.top, l.sourceCrop.right, l.sourceCrop.bottom,
+                    l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom);
+            result.append(buffer);
+        }
+    }
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
new file mode 100644
index 0000000..5a9e9eb
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SF_HWCOMPOSER_H
+#define ANDROID_SF_HWCOMPOSER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+
+#include <hardware/hwcomposer.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class String8;
+
+class HWComposer
+{
+public:
+
+    HWComposer();
+    ~HWComposer();
+
+    status_t initCheck() const;
+
+    // tells the HAL what the framebuffer is
+    void setFrameBuffer(EGLDisplay dpy, EGLSurface sur);
+
+    // create a work list for numLayers layer
+    status_t createWorkList(size_t numLayers);
+
+    // Asks the HAL what it can do
+    status_t prepare() const;
+
+    // commits the list
+    status_t commit() const;
+
+    // release hardware resources
+    status_t release() const;
+
+    size_t getNumLayers() const;
+    hwc_layer_t* getLayers() const;
+
+    // for debugging
+    void dump(String8& out, char* scratch, size_t SIZE) const;
+
+private:
+    hw_module_t const*      mModule;
+    hwc_composer_device_t*  mHwc;
+    hwc_layer_list_t*       mList;
+    size_t                  mCapacity;
+    hwc_display_t           mDpy;
+    hwc_surface_t           mSur;
+};
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a060d31..1b06843 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -35,6 +35,7 @@
 #include "Layer.h"
 #include "SurfaceFlinger.h"
 #include "DisplayHardware/DisplayHardware.h"
+#include "DisplayHardware/HWComposer.h"
 
 
 #define DEBUG_RESIZE    0
@@ -170,7 +171,8 @@
     mReqHeight = h;
 
     mSecure = (flags & ISurfaceComposer::eSecure) ? true : false;
-    mNeedsBlending = (info.h_alpha - info.l_alpha) > 0;
+    mNeedsBlending = (info.h_alpha - info.l_alpha) > 0 &&
+            (flags & ISurfaceComposer::eOpaque) == 0;
 
     // we use the red index
     int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED);
@@ -181,6 +183,62 @@
     return NO_ERROR;
 }
 
+void Layer::setGeometry(hwc_layer_t* hwcl)
+{
+    hwcl->compositionType = HWC_FRAMEBUFFER;
+    hwcl->hints = 0;
+    hwcl->flags = 0;
+    hwcl->transform = 0;
+    hwcl->blending = HWC_BLENDING_NONE;
+
+    // we can't do alpha-fade with the hwc HAL
+    const State& s(drawingState());
+    if (s.alpha < 0xFF) {
+        hwcl->flags = HWC_SKIP_LAYER;
+        return;
+    }
+
+    // we can only handle simple transformation
+    if (mOrientation & Transform::ROT_INVALID) {
+        hwcl->flags = HWC_SKIP_LAYER;
+        return;
+    }
+
+    hwcl->transform = mOrientation;
+
+    if (needsBlending()) {
+        hwcl->blending = mPremultipliedAlpha ?
+                HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE;
+    }
+
+    hwcl->displayFrame.left   = mTransformedBounds.left;
+    hwcl->displayFrame.top    = mTransformedBounds.top;
+    hwcl->displayFrame.right  = mTransformedBounds.right;
+    hwcl->displayFrame.bottom = mTransformedBounds.bottom;
+
+    hwcl->visibleRegionScreen.rects =
+            reinterpret_cast<hwc_rect_t const *>(
+                    visibleRegionScreen.getArray(
+                            &hwcl->visibleRegionScreen.numRects));
+}
+
+void Layer::setPerFrameData(hwc_layer_t* hwcl) {
+    sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
+    if (buffer == NULL) {
+        // this situation can happen if we ran out of memory for instance.
+        // not much we can do. continue to use whatever texture was bound
+        // to this context.
+        hwcl->handle = NULL;
+        return;
+    }
+    hwcl->handle = const_cast<native_handle_t*>(buffer->handle);
+    // TODO: set the crop value properly
+    hwcl->sourceCrop.left   = 0;
+    hwcl->sourceCrop.top    = 0;
+    hwcl->sourceCrop.right  = buffer->width;
+    hwcl->sourceCrop.bottom = buffer->height;
+}
+
 void Layer::reloadTexture(const Region& dirty)
 {
     sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
@@ -321,6 +379,7 @@
         Mutex::Autolock _l(mLock);
 
         // zero means default
+        const bool fixedSize = reqWidth && reqHeight;
         if (!reqFormat) reqFormat = mFormat;
         if (!reqWidth)  reqWidth = mWidth;
         if (!reqHeight) reqHeight = mHeight;
@@ -334,6 +393,7 @@
             mReqWidth  = reqWidth;
             mReqHeight = reqHeight;
             mReqFormat = reqFormat;
+            mFixedSize = fixedSize;
 
             lcblk->reallocateAllExcept(index);
         }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 263c372..caa6d17 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -68,6 +68,8 @@
     bool isFixedSize() const;
 
     // LayerBase interface
+    virtual void setGeometry(hwc_layer_t* hwcl);
+    virtual void setPerFrameData(hwc_layer_t* hwcl);
     virtual void drawForSreenShot() const;
     virtual void onDraw(const Region& clip) const;
     virtual uint32_t doTransaction(uint32_t transactionFlags);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 64eed4b..df6aa51 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -309,6 +309,15 @@
     }
 }
 
+void LayerBase::setGeometry(hwc_layer_t* hwcl) {
+    hwcl->flags |= HWC_SKIP_LAYER;
+}
+
+void LayerBase::setPerFrameData(hwc_layer_t* hwcl) {
+    hwcl->compositionType = HWC_FRAMEBUFFER;
+    hwcl->handle = NULL;
+}
+
 void LayerBase::draw(const Region& clip) const
 {
     // reset GL state
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index d688f65..bdee05b 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -35,6 +35,8 @@
 
 #include <pixelflinger/pixelflinger.h>
 
+#include <hardware/hwcomposer.h>
+
 #include "Transform.h"
 
 namespace android {
@@ -108,6 +110,10 @@
 
     virtual const char* getTypeId() const { return "LayerBase"; }
 
+    virtual void setGeometry(hwc_layer_t* hwcl);
+
+    virtual void setPerFrameData(hwc_layer_t* hwcl);
+
     /**
      * draw - performs some global clipping optimizations
      * and calls onDraw().
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a9b3965..c59f2fd 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -53,6 +53,7 @@
 #include "SurfaceFlinger.h"
 
 #include "DisplayHardware/DisplayHardware.h"
+#include "DisplayHardware/HWComposer.h"
 
 /* ideally AID_GRAPHICS would be in a semi-public header
  * or there would be a way to map a user/group name to its id
@@ -78,6 +79,7 @@
         mReadFramebuffer("android.permission.READ_FRAME_BUFFER"),
         mDump("android.permission.DUMP"),
         mVisibleRegionsDirty(false),
+        mHwWorkListDirty(false),
         mDeferReleaseConsole(false),
         mFreezeDisplay(false),
         mElectronBeamAnimationMode(0),
@@ -85,6 +87,7 @@
         mFreezeDisplayTime(0),
         mDebugRegion(0),
         mDebugBackground(0),
+        mDebugDisableHWC(0),
         mDebugInSwapBuffers(0),
         mLastSwapBufferTime(0),
         mDebugInTransaction(0),
@@ -166,7 +169,7 @@
 {
     const nsecs_t now = systemTime();
     const nsecs_t duration = now - mBootTime;
-    LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );  
+    LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
     mBootFinished = true;
     property_set("ctl.stop", "bootanim");
 }
@@ -202,10 +205,10 @@
     mServerHeap = new MemoryHeapBase(4096,
             MemoryHeapBase::READ_ONLY, "SurfaceFlinger read-only heap");
     LOGE_IF(mServerHeap==0, "can't create shared memory dealer");
-    
+
     mServerCblk = static_cast<surface_flinger_cblk_t*>(mServerHeap->getBase());
     LOGE_IF(mServerCblk==0, "can't get to shared control block's address");
-    
+
     new(mServerCblk) surface_flinger_cblk_t;
 
     // initialize primary screen
@@ -234,7 +237,7 @@
 
     // Initialize OpenGL|ES
     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-    glPixelStorei(GL_PACK_ALIGNMENT, 4); 
+    glPixelStorei(GL_PACK_ALIGNMENT, 4);
     glEnableClientState(GL_VERTEX_ARRAY);
     glEnable(GL_SCISSOR_TEST);
     glShadeModel(GL_FLAT);
@@ -268,7 +271,7 @@
 
     // start boot animation
     property_set("ctl.start", "bootanim");
-    
+
     return NO_ERROR;
 }
 
@@ -371,6 +374,11 @@
     // post surfaces (if needed)
     handlePageFlip();
 
+    if (UNLIKELY(mHwWorkListDirty)) {
+        // build the h/w work list
+        handleWorkList();
+    }
+
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
     if (LIKELY(hw.canDraw() && !isFrozen())) {
         // repaint the framebuffer (if needed)
@@ -385,13 +393,12 @@
         logger.log(GraphicLog::SF_COMPOSITION_COMPLETE, index);
         hw.compositionComplete();
 
-        // release the clients before we flip ('cause flip might block)
-        logger.log(GraphicLog::SF_UNLOCK_CLIENTS, index);
-        unlockClients();
-
         logger.log(GraphicLog::SF_SWAP_BUFFERS, index);
         postFramebuffer();
 
+        logger.log(GraphicLog::SF_UNLOCK_CLIENTS, index);
+        unlockClients();
+
         logger.log(GraphicLog::SF_REPAINT_DONE, index);
     } else {
         // pretend we did the post
@@ -459,6 +466,7 @@
         handleTransactionLocked(transactionFlags, ditchedLayers);
         mLastTransactionTime = systemTime() - now;
         mDebugInTransaction = 0;
+        mHwWorkListDirty = true;
         // here the transaction has been committed
     }
 
@@ -466,6 +474,7 @@
      * Clean-up all layers that went away
      * (do this without the lock held)
      */
+
     const size_t count = ditchedLayers.size();
     for (size_t i=0 ; i<count ; i++) {
         if (ditchedLayers[i] != 0) {
@@ -669,7 +678,7 @@
 
         // Update aboveOpaqueLayers for next (lower) layer
         aboveOpaqueLayers.orSelf(opaqueRegion);
-        
+
         // Store the visible region is screen space
         layer->setVisibleRegion(visibleRegion);
         layer->setCoveredRegion(coveredRegion);
@@ -699,8 +708,8 @@
 void SurfaceFlinger::handlePageFlip()
 {
     bool visibleRegions = mVisibleRegionsDirty;
-    LayerVector& currentLayers = const_cast<LayerVector&>(
-            mDrawingState.layersSortedByZ);
+    LayerVector& currentLayers(
+            const_cast<LayerVector&>(mDrawingState.layersSortedByZ));
     visibleRegions |= lockPageFlip(currentLayers);
 
         const DisplayHardware& hw = graphicPlane(0).displayHardware();
@@ -723,6 +732,7 @@
 
             mWormholeRegion = screenRegion.subtract(opaqueRegion);
             mVisibleRegionsDirty = false;
+            mHwWorkListDirty = true;
         }
 
     unlockPageFlip(currentLayers);
@@ -753,6 +763,24 @@
     }
 }
 
+void SurfaceFlinger::handleWorkList()
+{
+    mHwWorkListDirty = false;
+    HWComposer& hwc(graphicPlane(0).displayHardware().getHwComposer());
+    if (hwc.initCheck() == NO_ERROR) {
+        const Vector< sp<LayerBase> >& currentLayers(mVisibleLayersSortedByZ);
+        const size_t count = currentLayers.size();
+        hwc.createWorkList(count);
+        hwc_layer_t* const cur(hwc.getLayers());
+        for (size_t i=0 ; cur && i<count ; i++) {
+            currentLayers[i]->setGeometry(&cur[i]);
+            if (mDebugDisableHWC) {
+                cur[i].compositionType = HWC_FRAMEBUFFER;
+                cur[i].flags |= HWC_SKIP_LAYER;
+            }
+        }
+    }
+}
 
 void SurfaceFlinger::handleRepaint()
 {
@@ -773,8 +801,8 @@
     glLoadIdentity();
 
     uint32_t flags = hw.getFlags();
-    if ((flags & DisplayHardware::SWAP_RECTANGLE) || 
-        (flags & DisplayHardware::BUFFER_PRESERVED)) 
+    if ((flags & DisplayHardware::SWAP_RECTANGLE) ||
+        (flags & DisplayHardware::BUFFER_PRESERVED))
     {
         // we can redraw only what's dirty, but since SWAP_RECTANGLE only
         // takes a rectangle, we must make sure to update that whole
@@ -817,9 +845,73 @@
         // draw something...
         drawWormhole();
     }
+
+    status_t err = NO_ERROR;
     const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
-    const size_t count = layers.size();
-    for (size_t i=0 ; i<count ; ++i) {
+    size_t count = layers.size();
+
+    const DisplayHardware& hw(graphicPlane(0).displayHardware());
+    HWComposer& hwc(hw.getHwComposer());
+    hwc_layer_t* const cur(hwc.getLayers());
+
+    LOGE_IF(cur && hwc.getNumLayers() != count,
+            "HAL number of layers (%d) doesn't match surfaceflinger (%d)",
+            hwc.getNumLayers(), count);
+
+    // just to be extra-safe, use the smallest count
+    if (hwc.initCheck() == NO_ERROR) {
+        count = count < hwc.getNumLayers() ? count : hwc.getNumLayers();
+    }
+
+    /*
+     *  update the per-frame h/w composer data for each layer
+     *  and build the transparent region of the FB
+     */
+    Region transparent;
+    if (cur) {
+        for (size_t i=0 ; i<count ; i++) {
+            const sp<LayerBase>& layer(layers[i]);
+            layer->setPerFrameData(&cur[i]);
+            if (cur[i].hints & HWC_HINT_CLEAR_FB) {
+                if (!(layer->needsBlending())) {
+                    transparent.orSelf(layer->visibleRegionScreen);
+                }
+            }
+        }
+        err = hwc.prepare();
+        LOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
+    }
+
+    /*
+     *  clear the area of the FB that need to be transparent
+     */
+    transparent.andSelf(dirty);
+    if (!transparent.isEmpty()) {
+        glClearColor(0,0,0,0);
+        Region::const_iterator it = transparent.begin();
+        Region::const_iterator const end = transparent.end();
+        const int32_t height = hw.getHeight();
+        while (it != end) {
+            const Rect& r(*it++);
+            const GLint sy = height - (r.top + r.height());
+            glScissor(r.left, sy, r.width(), r.height());
+            glClear(GL_COLOR_BUFFER_BIT);
+        }
+    }
+
+
+    /*
+     * and then, render the layers targeted at the framebuffer
+     */
+    for (size_t i=0 ; i<count ; i++) {
+        if (cur) {
+            if ((cur[i].compositionType != HWC_FRAMEBUFFER) &&
+                !(cur[i].flags & HWC_SKIP_LAYER)) {
+                // skip layers handled by the HAL
+                continue;
+            }
+        }
+
         const sp<LayerBase>& layer(layers[i]);
         const Region clip(dirty.intersect(layer->visibleRegionScreen));
         if (!clip.isEmpty()) {
@@ -1058,7 +1150,7 @@
     if (android_atomic_dec(&mTransactionCount) == 1) {
         signalEvent();
 
-        // if there is a transaction with a resize, wait for it to 
+        // if there is a transaction with a resize, wait for it to
         // take effect before returning.
         Mutex::Autolock _l(mStateLock);
         while (mResizeTransationPending) {
@@ -1102,7 +1194,7 @@
     return NO_ERROR;
 }
 
-int SurfaceFlinger::setOrientation(DisplayID dpy, 
+int SurfaceFlinger::setOrientation(DisplayID dpy,
         int orientation, uint32_t flags)
 {
     if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
@@ -1135,14 +1227,17 @@
                 int(w), int(h));
         return surfaceHandle;
     }
-    
+
     //LOGD("createSurface for pid %d (%d x %d)", pid, w, h);
     sp<Layer> normalLayer;
     switch (flags & eFXSurfaceMask) {
         case eFXSurfaceNormal:
+#if HAS_PUSH_BUFFERS
             if (UNLIKELY(flags & ePushBuffers)) {
                 layer = createPushBuffersSurface(client, d, w, h, flags);
-            } else {
+            } else
+#endif
+            {
                 normalLayer = createNormalSurface(client, d, w, h, flags, format);
                 layer = normalLayer;
             }
@@ -1161,7 +1256,7 @@
         ssize_t token = addClientLayer(client, layer);
 
         surfaceHandle = layer->getSurface();
-        if (surfaceHandle != 0) { 
+        if (surfaceHandle != 0) {
             params->token = token;
             params->identity = surfaceHandle->getIdentity();
             params->width = w;
@@ -1245,7 +1340,7 @@
     /*
      * called by the window manager, when a surface should be marked for
      * destruction.
-     * 
+     *
      * The surface is removed from the current and drawing lists, but placed
      * in the purgatory queue, so it's not destroyed right-away (we need
      * to wait for all client's references to go away first).
@@ -1266,7 +1361,7 @@
 status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer)
 {
     // called by ~ISurface() when all references are gone
-    
+
     class MessageDestroySurface : public MessageBase {
         SurfaceFlinger* flinger;
         sp<LayerBaseClient> layer;
@@ -1279,9 +1374,9 @@
             layer.clear(); // clear it outside of the lock;
             Mutex::Autolock _l(flinger->mStateLock);
             /*
-             * remove the layer from the current list -- chances are that it's 
-             * not in the list anyway, because it should have been removed 
-             * already upon request of the client (eg: window manager). 
+             * remove the layer from the current list -- chances are that it's
+             * not in the list anyway, because it should have been removed
+             * already upon request of the client (eg: window manager).
              * However, a buggy client could have not done that.
              * Since we know we don't have any more clients, we don't need
              * to use the purgatory.
@@ -1396,7 +1491,7 @@
         }
         const bool locked(retry >= 0);
         if (!locked) {
-            snprintf(buffer, SIZE, 
+            snprintf(buffer, SIZE,
                     "SurfaceFlinger appears to be unresponsive, "
                     "dumping anyways (no locks held)\n");
             result.append(buffer);
@@ -1438,6 +1533,13 @@
             result.append(buffer);
         }
 
+        HWComposer& hwc(hw.getHwComposer());
+        snprintf(buffer, SIZE, "  h/w composer %s and %s\n",
+                hwc.initCheck()==NO_ERROR ? "present" : "not present",
+                mDebugDisableHWC ? "disabled" : "enabled");
+        result.append(buffer);
+        hwc.dump(result, buffer, SIZE);
+
         const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
         alloc.dump(result);
 
@@ -1513,6 +1615,11 @@
                 n = data.readInt32();
                 mDebugBackground = n ? 1 : 0;
                 return NO_ERROR;
+            case 1008:  // toggle use of hw composer
+                n = data.readInt32();
+                mDebugDisableHWC = n ? 1 : 0;
+                mHwWorkListDirty = true;
+                // fall-through...
             case 1004:{ // repaint everything
                 Mutex::Autolock _l(mStateLock);
                 const DisplayHardware& hw(graphicPlane(0).displayHardware());
@@ -2308,12 +2415,15 @@
 {
     int32_t name = NAME_NOT_FOUND;
     sp<Layer> layer(mFlinger->getLayer(sur));
-    if (layer == 0) return name;
+    if (layer == 0) {
+        return name;
+    }
 
     // if this layer already has a token, just return it
     name = layer->getToken();
-    if ((name >= 0) && (layer->getClient() == this))
+    if ((name >= 0) && (layer->getClient() == this)) {
         return name;
+    }
 
     name = 0;
     do {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 4262175..dda25e8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -306,6 +306,7 @@
             void        handlePageFlip();
             bool        lockPageFlip(const LayerVector& currentLayers);
             void        unlockPageFlip(const LayerVector& currentLayers);
+            void        handleWorkList();
             void        handleRepaint();
             void        postFramebuffer();
             void        composeSurfaces(const Region& dirty);
@@ -393,6 +394,7 @@
                 Region                      mInvalidRegion;
                 Region                      mWormholeRegion;
                 bool                        mVisibleRegionsDirty;
+                bool                        mHwWorkListDirty;
                 bool                        mDeferReleaseConsole;
                 bool                        mFreezeDisplay;
                 int32_t                     mElectronBeamAnimationMode;
@@ -404,6 +406,7 @@
                 // don't use a lock for these, we don't care
                 int                         mDebugRegion;
                 int                         mDebugBackground;
+                int                         mDebugDisableHWC;
                 volatile nsecs_t            mDebugInSwapBuffers;
                 nsecs_t                     mLastSwapBufferTime;
                 volatile nsecs_t            mDebugInTransaction;
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index b07a10b..186b349 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -7,8 +7,10 @@
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_STATIC_JAVA_LIBRARIES := easymocklib
 
 LOCAL_JAVA_LIBRARIES := android.test.runner services
+
 LOCAL_PACKAGE_NAME := FrameworksServicesTests
 
 LOCAL_CERTIFICATE := platform
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 5ce109f..f115f42 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -23,6 +23,19 @@
     
     <application>
         <uses-library android:name="android.test.runner" />
+
+        <service android:name="com.android.server.AccessibilityManagerServiceTest$MyFirstMockAccessibilityService">
+          <intent-filter>
+            <action android:name="android.accessibilityservice.AccessibilityService"/>
+          </intent-filter>
+        </service>
+
+        <service android:name="com.android.server.AccessibilityManagerServiceTest$MySecondMockAccessibilityService">
+          <intent-filter>
+            <action android:name="android.accessibilityservice.AccessibilityService"/>
+          </intent-filter>
+        </service>
+
     </application>
 
     <instrumentation
diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
new file mode 100644
index 0000000..f6e3a82
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
@@ -0,0 +1,729 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.IAccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+
+/**
+ * This test exercises the
+ * {@link com.android.server.AccessibilityManagerService} by mocking the
+ * {@link android.view.accessibility.AccessibilityManager} which talks to to the
+ * service. The service itself is interacting with the platform. Note: Testing
+ * the service in full isolation would require significant amount of work for
+ * mocking all system interactions. It would also require a lot of mocking code.
+ */
+public class AccessibilityManagerServiceTest extends AndroidTestCase {
+
+    /**
+     * Timeout required for pending Binder calls or event processing to
+     * complete.
+     */
+    private static final long TIMEOUT_BINDER_CALL = 100;
+
+    /**
+     * Timeout in which we are waiting for the system to start the mock
+     * accessibility services.
+     */
+    private static final long TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES = 300;
+
+    /**
+     * Timeout used for testing that a service is notified only upon a
+     * notification timeout.
+     */
+    private static final long TIMEOUT_TEST_NOTIFICATION_TIMEOUT = 300;
+
+    /**
+     * The package name.
+     */
+    private static String sPackageName;
+
+    /**
+     * The interface used to talk to the tested service.
+     */
+    private IAccessibilityManager mManagerService;
+
+    @Override
+    public void setContext(Context context) {
+        super.setContext(context);
+        if (sPackageName == null) {
+            sPackageName = context.getPackageName();
+        }
+    }
+
+    /**
+     * Creates a new instance.
+     */
+    public AccessibilityManagerServiceTest() {
+        IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+        mManagerService = IAccessibilityManager.Stub.asInterface(iBinder);
+    }
+
+    @LargeTest
+    public void testAddClient_AccessibilityDisabledThenEnabled() throws Exception {
+        // make sure accessibility is disabled
+        ensureAccessibilityEnabled(mContext, false);
+
+        // create a client mock instance
+        MyMockAccessibilityManagerClient mockClient = new MyMockAccessibilityManagerClient();
+
+        // invoke the method under test
+        boolean enabledAccessibilityDisabled = mManagerService.addClient(mockClient);
+
+        // check expected result
+        assertFalse("The client must be disabled since accessibility is disabled.",
+                enabledAccessibilityDisabled);
+
+        // enable accessibility
+        ensureAccessibilityEnabled(mContext, true);
+
+        // invoke the method under test
+        boolean enabledAccessibilityEnabled = mManagerService.addClient(mockClient);
+
+        // check expected result
+        assertTrue("The client must be enabled since accessibility is enabled.",
+                enabledAccessibilityEnabled);
+    }
+
+    @LargeTest
+    public void testAddClient_AccessibilityEnabledThenDisabled() throws Exception {
+        // enable accessibility before registering the client
+        ensureAccessibilityEnabled(mContext, true);
+
+        // create a client mock instance
+        MyMockAccessibilityManagerClient mockClient = new MyMockAccessibilityManagerClient();
+
+        // invoke the method under test
+        boolean enabledAccessibilityEnabled = mManagerService.addClient(mockClient);
+
+        // check expected result
+        assertTrue("The client must be enabled since accessibility is enabled.",
+                enabledAccessibilityEnabled);
+
+        // disable accessibility
+        ensureAccessibilityEnabled(mContext, false);
+
+        // invoke the method under test
+        boolean enabledAccessibilityDisabled = mManagerService.addClient(mockClient);
+
+        // check expected result
+        assertFalse("The client must be disabled since accessibility is disabled.",
+                enabledAccessibilityDisabled);
+    }
+
+    @LargeTest
+    public void testGetAccessibilityServicesList() throws Exception {
+        boolean firstMockServiceInstalled = false;
+        boolean secondMockServiceInstalled = false;
+
+        String packageName = getContext().getPackageName();
+        String firstMockServiceClassName = MyFirstMockAccessibilityService.class.getName();
+        String secondMockServiceClassName = MySecondMockAccessibilityService.class.getName();
+
+        // look for the two mock services
+        for (ServiceInfo serviceInfo : mManagerService.getAccessibilityServiceList()) {
+            if (packageName.equals(serviceInfo.packageName)) {
+                if (firstMockServiceClassName.equals(serviceInfo.name)) {
+                    firstMockServiceInstalled = true;
+                } else if (secondMockServiceClassName.equals(serviceInfo.name)) {
+                    secondMockServiceInstalled = true;
+                }
+            }
+        }
+
+        // check expected result
+        assertTrue("First mock service must be installed", firstMockServiceInstalled);
+        assertTrue("Second mock service must be installed", secondMockServiceInstalled);
+    }
+
+    @LargeTest
+    public void testSendAccessibilityEvent_OneService_MatchingPackageAndEventType()
+            throws Exception {
+        // set the accessibility setting value
+        ensureAccessibilityEnabled(mContext, true);
+
+        // enable the mock accessibility service
+        ensureOnlyMockServicesEnabled(mContext, true, false);
+
+        // configure the mock service
+        MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
+        service.setServiceInfo(MockAccessibilityService.createDefaultInfo());
+
+        // wait for the binder call to #setService to complete
+        Thread.sleep(TIMEOUT_BINDER_CALL);
+
+        // create and populate an event to be sent
+        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
+        fullyPopulateDefaultAccessibilityEvent(sentEvent);
+
+        // set expectations
+        service.expectEvent(sentEvent);
+        service.replay();
+
+        // send the event
+        mManagerService.sendAccessibilityEvent(sentEvent);
+
+        // verify if all expected methods have been called
+        assertMockServiceVerifiedWithinTimeout(service);
+    }
+
+    @LargeTest
+    public void testSendAccessibilityEvent_OneService_NotMatchingPackage() throws Exception {
+        // set the accessibility setting value
+        ensureAccessibilityEnabled(mContext, true);
+
+        // enable the mock accessibility service
+        ensureOnlyMockServicesEnabled(mContext, true, false);
+
+        // configure the mock service
+        MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
+        service.setServiceInfo(MockAccessibilityService.createDefaultInfo());
+
+        // wait for the binder call to #setService to complete
+        Thread.sleep(TIMEOUT_BINDER_CALL);
+
+        // create and populate an event to be sent
+        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
+        fullyPopulateDefaultAccessibilityEvent(sentEvent);
+        sentEvent.setPackageName("no.service.registered.for.this.package");
+
+        // set expectations
+        service.replay();
+
+        // send the event
+        mManagerService.sendAccessibilityEvent(sentEvent);
+
+        // verify if all expected methods have been called
+        assertMockServiceVerifiedWithinTimeout(service);
+    }
+
+    @LargeTest
+    public void testSendAccessibilityEvent_OneService_NotMatchingEventType() throws Exception {
+        // set the accessibility setting value
+        ensureAccessibilityEnabled(mContext, true);
+
+        // enable the mock accessibility service
+        ensureOnlyMockServicesEnabled(mContext, true, false);
+
+        // configure the mock service
+        MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
+        service.setServiceInfo(MockAccessibilityService.createDefaultInfo());
+
+        // wait for the binder call to #setService to complete
+        Thread.sleep(TIMEOUT_BINDER_CALL);
+
+        // create and populate an event to be sent
+        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
+        fullyPopulateDefaultAccessibilityEvent(sentEvent);
+        sentEvent.setEventType(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
+
+        // set expectations
+        service.replay();
+
+        // send the event
+        mManagerService.sendAccessibilityEvent(sentEvent);
+
+        // verify if all expected methods have been called
+        assertMockServiceVerifiedWithinTimeout(service);
+    }
+
+    @LargeTest
+    public void testSendAccessibilityEvent_OneService_NotifivationAfterTimeout() throws Exception {
+        // set the accessibility setting value
+        ensureAccessibilityEnabled(mContext, true);
+
+        // enable the mock accessibility service
+        ensureOnlyMockServicesEnabled(mContext, true, false);
+
+        // configure the mock service
+        MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
+        AccessibilityServiceInfo info = MockAccessibilityService.createDefaultInfo();
+        info.notificationTimeout = TIMEOUT_TEST_NOTIFICATION_TIMEOUT;
+        service.setServiceInfo(info);
+
+        // wait for the binder call to #setService to complete
+        Thread.sleep(TIMEOUT_BINDER_CALL);
+
+        // create and populate the first event to be sent
+        AccessibilityEvent firstEvent = AccessibilityEvent.obtain();
+        fullyPopulateDefaultAccessibilityEvent(firstEvent);
+
+        // create and populate the second event to be sent
+        AccessibilityEvent secondEvent = AccessibilityEvent.obtain();
+        fullyPopulateDefaultAccessibilityEvent(secondEvent);
+
+        // set expectations
+        service.expectEvent(secondEvent);
+        service.replay();
+
+        // send the events
+        mManagerService.sendAccessibilityEvent(firstEvent);
+        mManagerService.sendAccessibilityEvent(secondEvent);
+
+        // wait for #sendAccessibilityEvent to reach the backing service
+        Thread.sleep(TIMEOUT_BINDER_CALL);
+
+        try {
+            service.verify();
+            fail("No events must be dispatched before the expiration of the notification timeout.");
+        } catch (IllegalStateException ise) {
+            /* expected */
+        }
+
+        // wait for the configured notification timeout to expire
+        Thread.sleep(TIMEOUT_TEST_NOTIFICATION_TIMEOUT);
+
+        // verify if all expected methods have been called
+        assertMockServiceVerifiedWithinTimeout(service);
+    }
+
+    @LargeTest
+    public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_DiffFeedback()
+            throws Exception {
+        // set the accessibility setting value
+        ensureAccessibilityEnabled(mContext, true);
+
+        // enable the mock accessibility services
+        ensureOnlyMockServicesEnabled(mContext, true, true);
+
+        // configure the first mock service
+        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
+        AccessibilityServiceInfo firstInfo = MockAccessibilityService.createDefaultInfo();
+        firstInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE;
+        firstService.setServiceInfo(firstInfo);
+
+        // configure the second mock service
+        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
+        AccessibilityServiceInfo secondInfo = MockAccessibilityService.createDefaultInfo();
+        secondInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_HAPTIC;
+        secondService.setServiceInfo(secondInfo);
+
+        // wait for the binder calls to #setService to complete
+        Thread.sleep(TIMEOUT_BINDER_CALL);
+
+        // create and populate an event to be sent
+        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
+        fullyPopulateDefaultAccessibilityEvent(sentEvent);
+
+        // set expectations for the first mock service
+        firstService.expectEvent(sentEvent);
+        firstService.replay();
+
+        // set expectations for the second mock service
+        secondService.expectEvent(sentEvent);
+        secondService.replay();
+
+        // send the event
+        mManagerService.sendAccessibilityEvent(sentEvent);
+
+        // verify if all expected methods have been called
+        assertMockServiceVerifiedWithinTimeout(firstService);
+        assertMockServiceVerifiedWithinTimeout(secondService);
+    }
+
+    @LargeTest
+    public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType()
+            throws Exception {
+        // set the accessibility setting value
+        ensureAccessibilityEnabled(mContext, true);
+
+        // enable the mock accessibility services
+        ensureOnlyMockServicesEnabled(mContext, true, true);
+
+        // configure the first mock service
+        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
+        firstService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
+
+        // configure the second mock service
+        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
+        secondService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
+
+        // wait for the binder calls to #setService to complete
+        Thread.sleep(TIMEOUT_BINDER_CALL);
+
+        // create and populate an event to be sent
+        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
+        fullyPopulateDefaultAccessibilityEvent(sentEvent);
+
+        // set expectations for the first mock service
+        firstService.expectEvent(sentEvent);
+        firstService.replay();
+
+        // set expectations for the second mock service
+        secondService.replay();
+
+        // send the event
+        mManagerService.sendAccessibilityEvent(sentEvent);
+
+        // verify if all expected methods have been called
+        assertMockServiceVerifiedWithinTimeout(firstService);
+        assertMockServiceVerifiedWithinTimeout(secondService);
+    }
+
+    @LargeTest
+    public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_OneDefault()
+            throws Exception {
+        // set the accessibility setting value
+        ensureAccessibilityEnabled(mContext, true);
+
+        // enable the mock accessibility services
+        ensureOnlyMockServicesEnabled(mContext, true, true);
+
+        // configure the first mock service
+        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
+        AccessibilityServiceInfo firstInfo = MyFirstMockAccessibilityService.createDefaultInfo();
+        firstInfo.flags = AccessibilityServiceInfo.DEFAULT;
+        firstService.setServiceInfo(firstInfo);
+
+        // configure the second mock service
+        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
+        secondService.setServiceInfo(MySecondMockAccessibilityService.createDefaultInfo());
+
+        // wait for the binder calls to #setService to complete
+        Thread.sleep(TIMEOUT_BINDER_CALL);
+
+        // create and populate an event to be sent
+        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
+        fullyPopulateDefaultAccessibilityEvent(sentEvent);
+
+        // set expectations for the first mock service
+        firstService.replay();
+
+        // set expectations for the second mock service
+        secondService.expectEvent(sentEvent);
+        secondService.replay();
+
+        // send the event
+        mManagerService.sendAccessibilityEvent(sentEvent);
+
+        // verify if all expected methods have been called
+        assertMockServiceVerifiedWithinTimeout(firstService);
+        assertMockServiceVerifiedWithinTimeout(secondService);
+    }
+
+    @LargeTest
+    public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_TwoDefault()
+            throws Exception {
+        // set the accessibility setting value
+        ensureAccessibilityEnabled(mContext, true);
+
+        // enable the mock accessibility services
+        ensureOnlyMockServicesEnabled(mContext, true, true);
+
+        // configure the first mock service
+        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
+        AccessibilityServiceInfo firstInfo = MyFirstMockAccessibilityService.createDefaultInfo();
+        firstInfo.flags = AccessibilityServiceInfo.DEFAULT;
+        firstService.setServiceInfo(firstInfo);
+
+        // configure the second mock service
+        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
+        AccessibilityServiceInfo secondInfo = MyFirstMockAccessibilityService.createDefaultInfo();
+        secondInfo.flags = AccessibilityServiceInfo.DEFAULT;
+        secondService.setServiceInfo(firstInfo);
+
+        // wait for the binder calls to #setService to complete
+        Thread.sleep(TIMEOUT_BINDER_CALL);
+
+        // create and populate an event to be sent
+        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
+        fullyPopulateDefaultAccessibilityEvent(sentEvent);
+
+        // set expectations for the first mock service
+        firstService.expectEvent(sentEvent);
+        firstService.replay();
+
+        // set expectations for the second mock service
+        secondService.replay();
+
+        // send the event
+        mManagerService.sendAccessibilityEvent(sentEvent);
+
+        // verify if all expected methods have been called
+        assertMockServiceVerifiedWithinTimeout(firstService);
+        assertMockServiceVerifiedWithinTimeout(secondService);
+    }
+
+    @LargeTest
+    public void testInterrupt() throws Exception {
+        // set the accessibility setting value
+        ensureAccessibilityEnabled(mContext, true);
+
+        // enable the mock accessibility services
+        ensureOnlyMockServicesEnabled(mContext, true, true);
+
+        // configure the first mock service
+        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
+        firstService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
+
+        // configure the second mock service
+        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
+        secondService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
+
+        // wait for the binder calls to #setService to complete
+        Thread.sleep(TIMEOUT_BINDER_CALL);
+
+        // set expectations for the first mock service
+        firstService.expectInterrupt();
+        firstService.replay();
+
+        // set expectations for the second mock service
+        secondService.expectInterrupt();
+        secondService.replay();
+
+        // call the method under test
+        mManagerService.interrupt();
+
+        // verify if all expected methods have been called
+        assertMockServiceVerifiedWithinTimeout(firstService);
+        assertMockServiceVerifiedWithinTimeout(secondService);
+    }
+
+    /**
+     * Fully populates the {@link AccessibilityEvent} to marshal.
+     *
+     * @param sentEvent The event to populate.
+     */
+    private void fullyPopulateDefaultAccessibilityEvent(AccessibilityEvent sentEvent) {
+        sentEvent.setAddedCount(1);
+        sentEvent.setBeforeText("BeforeText");
+        sentEvent.setChecked(true);
+        sentEvent.setClassName("foo.bar.baz.Class");
+        sentEvent.setContentDescription("ContentDescription");
+        sentEvent.setCurrentItemIndex(1);
+        sentEvent.setEnabled(true);
+        sentEvent.setEventType(AccessibilityEvent.TYPE_VIEW_CLICKED);
+        sentEvent.setEventTime(1000);
+        sentEvent.setFromIndex(1);
+        sentEvent.setFullScreen(true);
+        sentEvent.setItemCount(1);
+        sentEvent.setPackageName("foo.bar.baz");
+        sentEvent.setParcelableData(Message.obtain(null, 1, null));
+        sentEvent.setPassword(true);
+        sentEvent.setRemovedCount(1);
+    }
+
+    /**
+     * This class is a mock {@link IAccessibilityManagerClient}.
+     */
+    public class MyMockAccessibilityManagerClient extends IAccessibilityManagerClient.Stub {
+        boolean mIsEnabled;
+
+        public void setEnabled(boolean enabled) {
+            mIsEnabled = enabled;
+        }
+    }
+
+    /**
+     * Ensures accessibility is in a given state by writing the state to the
+     * settings and waiting until the accessibility manager service pick it up.
+     *
+     * @param context A context handle to access the settings.
+     * @param enabled The accessibility state to write to the settings.
+     * @throws Exception If any error occurs.
+     */
+    private void ensureAccessibilityEnabled(Context context, boolean enabled) throws Exception {
+        boolean isEnabled = (Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1 ? true : false);
+
+        if (isEnabled == enabled) {
+            return;
+        }
+
+        Settings.Secure.putInt(context.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED,
+                enabled ? 1 : 0);
+
+        // wait the accessibility manager service to pick the change up
+        Thread.sleep(TIMEOUT_BINDER_CALL);
+    }
+
+    /**
+     * Ensures the only {@link MockAccessibilityService}s with given component
+     * names are enabled by writing to the system settings and waiting until the
+     * accessibility manager service picks that up or the
+     * {@link #TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES} is exceeded.
+     *
+     * @param context A context handle to access the settings.
+     * @param firstMockServiceEnabled If the first mock accessibility service is enabled.
+     * @param secondMockServiceEnabled If the second mock accessibility service is enabled.
+     * @throws IllegalStateException If some of the requested for enabling mock services
+     *         is not properly started.
+     * @throws Exception Exception If any error occurs.
+     */
+    private void ensureOnlyMockServicesEnabled(Context context, boolean firstMockServiceEnabled,
+            boolean secondMockServiceEnabled) throws Exception {
+        String enabledServices = Settings.Secure.getString(context.getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+
+        StringBuilder servicesToEnable = new StringBuilder();
+        if (firstMockServiceEnabled) {
+            servicesToEnable.append(MyFirstMockAccessibilityService.sComponentName).append(":");
+        }
+        if (secondMockServiceEnabled) {
+            servicesToEnable.append(MySecondMockAccessibilityService.sComponentName).append(":");
+        }
+
+        if (servicesToEnable.equals(enabledServices)) {
+            return;
+        }
+
+        Settings.Secure.putString(context.getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, servicesToEnable.toString());
+
+        // we have enabled the services of interest and need to wait until they
+        // are instantiated and started (if needed) and the system binds to them
+        boolean firstMockServiceOK = false;
+        boolean secondMockServiceOK = false;
+        long start = SystemClock.uptimeMillis();
+        long pollingInterval = TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES / 6;
+
+        while (SystemClock.uptimeMillis() - start < TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES)  {
+            firstMockServiceOK = !firstMockServiceEnabled
+                    || (MyFirstMockAccessibilityService.sInstance != null
+                    && MyFirstMockAccessibilityService.sInstance.isSystemBoundAsClient());
+
+            secondMockServiceOK = !secondMockServiceEnabled
+                    || (MySecondMockAccessibilityService.sInstance != null
+                    && MySecondMockAccessibilityService.sInstance.isSystemBoundAsClient());
+
+            if (firstMockServiceOK && secondMockServiceOK) {
+                return;
+            }
+
+            Thread.sleep(pollingInterval);
+        }
+
+        StringBuilder message = new StringBuilder();
+        message.append("Mock accessibility services not started or system not bound as a client: ");
+        if (!firstMockServiceOK) {
+            message.append(MyFirstMockAccessibilityService.sComponentName);
+            message.append(" ");
+        }
+        if (!secondMockServiceOK) {
+            message.append(MySecondMockAccessibilityService.sComponentName);
+        }
+        throw new IllegalStateException(message.toString());
+    }
+
+    /**
+     * Asserts the the mock accessibility service has been successfully verified
+     * (which is it has received the expected method calls with expected
+     * arguments) within the {@link #TIMEOUT_BINDER_CALL}. The verified state is
+     * checked by polling upon small intervals.
+     *
+     * @param service The service to verify.
+     * @throws Exception If the verification has failed with exception after the
+     *             {@link #TIMEOUT_BINDER_CALL}.
+     */
+    private void assertMockServiceVerifiedWithinTimeout(MockAccessibilityService service)
+            throws Exception {
+        Exception lastVerifyException = null;
+        long beginTime = SystemClock.uptimeMillis();
+        long pollTmeout = TIMEOUT_BINDER_CALL / 5;
+
+        // poll until the timeout has elapsed
+        while (SystemClock.uptimeMillis() - beginTime < TIMEOUT_BINDER_CALL) {
+            // sleep first since immediate call will always fail
+            try {
+                Thread.sleep(pollTmeout);
+            } catch (InterruptedException ie) {
+                /* ignore */
+            }
+            // poll for verification and if this fails save the exception and
+            // keep polling
+            try {
+                service.verify();
+                // reset so it does not accept more events
+                service.reset();
+                return;
+            } catch (Exception e) {
+                lastVerifyException = e;
+            }
+        }
+
+        // reset, we have already failed
+        service.reset();
+
+        // always not null
+        throw lastVerifyException;
+    }
+
+    /**
+     * This class is the first mock {@link AccessibilityService}.
+     */
+    public static class MyFirstMockAccessibilityService extends MockAccessibilityService {
+
+        /**
+         * The service {@link ComponentName} flattened as a string.
+         */
+        static final String sComponentName = new ComponentName(
+                sPackageName,
+                MyFirstMockAccessibilityService.class.getName()
+                ).flattenToShortString();
+
+        /**
+         * Handle to the service instance.
+         */
+        static MyFirstMockAccessibilityService sInstance;
+
+        /**
+         * Creates a new instance.
+         */
+        public MyFirstMockAccessibilityService() {
+            sInstance = this;
+        }
+    }
+
+    /**
+     * This class is the first mock {@link AccessibilityService}.
+     */
+    public static class MySecondMockAccessibilityService extends MockAccessibilityService {
+
+        /**
+         * The service {@link ComponentName} flattened as a string.
+         */
+        static final String sComponentName = new ComponentName(
+                sPackageName,
+                MySecondMockAccessibilityService.class.getName()
+                ).flattenToShortString();
+
+        /**
+         * Handle to the service instance.
+         */
+        static MySecondMockAccessibilityService sInstance;
+
+        /**
+         * Creates a new instance.
+         */
+        public MySecondMockAccessibilityService() {
+            sInstance = this;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
new file mode 100644
index 0000000..38fed22
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static org.easymock.EasyMock.createStrictMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reportMatcher;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+
+import org.easymock.IArgumentMatcher;
+
+import android.content.pm.ServiceInfo;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for the AccessibilityManager which mocking the backing service.
+ */
+public class AccessibilityManagerTest extends AndroidTestCase {
+
+    /**
+     * Timeout required for pending Binder calls or event processing to
+     * complete.
+     */
+    public static final long TIMEOUT_BINDER_CALL = 50;
+
+    /**
+     * The reusable mock {@link IAccessibilityManager}.
+     */
+    private final IAccessibilityManager mMockServiceInterface =
+        createStrictMock(IAccessibilityManager.class);
+
+    @Override
+    public void setUp() throws Exception {
+        reset(mMockServiceInterface);
+    }
+
+    @MediumTest
+    public void testGetAccessibilityServiceList() throws Exception {
+        // create a list of installed accessibility services the mock service returns
+        List<ServiceInfo> expectedServices = new ArrayList<ServiceInfo>();
+        ServiceInfo serviceInfo = new ServiceInfo();
+        serviceInfo.name = "TestServiceInfoName";
+        expectedServices.add(serviceInfo);
+
+        // configure the mock service behavior
+        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
+        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(true);
+        expect(mockServiceInterface.getAccessibilityServiceList()).andReturn(expectedServices);
+        replay(mockServiceInterface);
+
+        // invoke the method under test
+        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
+        List<ServiceInfo> receivedServices = manager.getAccessibilityServiceList();
+
+        // check expected result (list equals() compares it contents as well)
+        assertEquals("All expected services must be returned", receivedServices, expectedServices);
+
+        // verify the mock service was properly called
+        verify(mockServiceInterface);
+    }
+
+    @MediumTest
+    public void testInterrupt() throws Exception {
+        // configure the mock service behavior
+        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
+        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(true);
+        mockServiceInterface.interrupt();
+        replay(mockServiceInterface);
+
+        // invoke the method under test
+        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
+        manager.interrupt();
+
+        // verify the mock service was properly called
+        verify(mockServiceInterface);
+    }
+
+    @LargeTest
+    public void testIsEnabled() throws Exception {
+        // configure the mock service behavior
+        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
+        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(true);
+        replay(mockServiceInterface);
+
+        // invoke the method under test
+        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
+        boolean isEnabledServiceEnabled = manager.isEnabled();
+
+        // check expected result
+        assertTrue("Must be enabled since the mock service is enabled", isEnabledServiceEnabled);
+
+        // disable accessibility
+        manager.getClient().setEnabled(false);
+
+        // wait for the asynchronous IBinder call to complete
+        Thread.sleep(TIMEOUT_BINDER_CALL);
+
+        // invoke the method under test
+        boolean isEnabledServcieDisabled = manager.isEnabled();
+
+        // check expected result
+        assertFalse("Must be disabled since the mock service is disabled",
+                isEnabledServcieDisabled);
+
+        // verify the mock service was properly called
+        verify(mockServiceInterface);
+    }
+
+    @MediumTest
+    public void testSendAccessibilityEvent_AccessibilityEnabled() throws Exception {
+        // create an event to be dispatched
+        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
+
+        // configure the mock service behavior
+        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
+        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(true);
+        expect(mockServiceInterface.sendAccessibilityEvent(eqAccessibilityEvent(sentEvent)))
+                .andReturn(true);
+        expect(mockServiceInterface.sendAccessibilityEvent(eqAccessibilityEvent(sentEvent)))
+                .andReturn(false);
+        replay(mockServiceInterface);
+
+        // invoke the method under test (manager and service in different processes)
+        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
+        manager.sendAccessibilityEvent(sentEvent);
+
+        // check expected result
+        AccessibilityEvent nextEventDifferentProcesses = AccessibilityEvent.obtain();
+        assertSame("The manager and the service are in different processes, so the event must be " +
+                "recycled", sentEvent, nextEventDifferentProcesses);
+
+        // invoke the method under test (manager and service in the same process)
+        manager.sendAccessibilityEvent(sentEvent);
+
+        // check expected result
+        AccessibilityEvent nextEventSameProcess = AccessibilityEvent.obtain();
+        assertNotSame("The manager and the service are in the same process, so the event must not" +
+                "be recycled", sentEvent, nextEventSameProcess);
+
+        // verify the mock service was properly called
+        verify(mockServiceInterface);
+    }
+
+    @MediumTest
+    public void testSendAccessibilityEvent_AccessibilityDisabled() throws Exception {
+        // create an event to be dispatched
+        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
+
+        // configure the mock service behavior
+        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
+        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(false);
+        replay(mockServiceInterface);
+
+        // invoke the method under test (accessibility disabled)
+        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
+        try {
+            manager.sendAccessibilityEvent(sentEvent);
+            fail("No accessibility events are sent if accessibility is disabled");
+        } catch (IllegalStateException ise) {
+            // check expected result
+            assertEquals("Accessibility off. Did you forget to check that?", ise.getMessage());
+        }
+
+        // verify the mock service was properly called
+        verify(mockServiceInterface);
+    }
+
+    /**
+     * Determines if an {@link AccessibilityEvent} passed as a method argument
+     * matches expectations.
+     *
+     * @param matched The event to check.
+     * @return True if expectations are matched.
+     */
+    private static AccessibilityEvent eqAccessibilityEvent(AccessibilityEvent matched) {
+        reportMatcher(new AccessibilityEventMather(matched));
+        return null;
+    }
+
+    /**
+     * Determines if an {@link IAccessibilityManagerClient} passed as a method argument
+     * matches expectations which in this case are that any instance is accepted.
+     *
+     * @return <code>null</code>.
+     */
+    private static IAccessibilityManagerClient anyIAccessibilityManagerClient() {
+        reportMatcher(new AnyIAccessibilityManagerClientMather());
+        return null;
+    }
+
+    /**
+     * Matcher for {@link AccessibilityEvent}s.
+     */
+    private static class AccessibilityEventMather implements IArgumentMatcher {
+        private AccessibilityEvent mExpectedEvent;
+
+        public AccessibilityEventMather(AccessibilityEvent expectedEvent) {
+            mExpectedEvent = expectedEvent;
+        }
+
+        public boolean matches(Object matched) {
+            if (!(matched instanceof AccessibilityEvent)) {
+                return false;
+            }
+            AccessibilityEvent receivedEvent = (AccessibilityEvent) matched;
+            return mExpectedEvent.getEventType() == receivedEvent.getEventType();
+        }
+
+        public void appendTo(StringBuffer buffer) {
+            buffer.append("sendAccessibilityEvent()");
+            buffer.append(" with event type \"");
+            buffer.append(mExpectedEvent.getEventType());
+            buffer.append("\"");
+        }
+    }
+
+    /**
+     * Matcher for {@link IAccessibilityManagerClient}s.
+     */
+    private static class AnyIAccessibilityManagerClientMather implements IArgumentMatcher {
+        public boolean matches(Object matched) {
+            if (!(matched instanceof IAccessibilityManagerClient)) {
+                return false;
+            }
+            return true;
+        }
+
+        public void appendTo(StringBuffer buffer) {
+            buffer.append("addClient() with any IAccessibilityManagerClient");
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
new file mode 100644
index 0000000..17a1585
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.location.Country;
+import android.location.CountryListener;
+import android.location.ICountryListener;
+import android.os.RemoteException;
+import android.test.AndroidTestCase;
+
+public class CountryDetectorServiceTest extends AndroidTestCase {
+    private class CountryListenerTester extends ICountryListener.Stub {
+        private Country mCountry;
+
+        @Override
+        public void onCountryDetected(Country country) throws RemoteException {
+            mCountry = country;
+        }
+
+        public Country getCountry() {
+            return mCountry;
+        }
+
+        public boolean isNotified() {
+            return mCountry != null;
+        }
+    }
+
+    private class CountryDetectorServiceTester extends CountryDetectorService {
+
+        private CountryListener mListener;
+
+        public CountryDetectorServiceTester(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void notifyReceivers(Country country) {
+            super.notifyReceivers(country);
+        }
+
+        @Override
+        protected void setCountryListener(final CountryListener listener) {
+            mListener = listener;
+        }
+
+        public boolean isListenerSet() {
+            return mListener != null;
+        }
+    }
+
+    public void testAddRemoveListener() throws RemoteException {
+        CountryDetectorServiceTester serviceTester = new CountryDetectorServiceTester(getContext());
+        serviceTester.systemReady();
+        waitForSystemReady(serviceTester);
+        CountryListenerTester listenerTester = new CountryListenerTester();
+        serviceTester.addCountryListener(listenerTester);
+        assertTrue(serviceTester.isListenerSet());
+        serviceTester.removeCountryListener(listenerTester);
+        assertFalse(serviceTester.isListenerSet());
+    }
+
+    public void testNotifyListeners() throws RemoteException {
+        CountryDetectorServiceTester serviceTester = new CountryDetectorServiceTester(getContext());
+        CountryListenerTester listenerTesterA = new CountryListenerTester();
+        CountryListenerTester listenerTesterB = new CountryListenerTester();
+        Country country = new Country("US", Country.COUNTRY_SOURCE_NETWORK);
+        serviceTester.systemReady();
+        waitForSystemReady(serviceTester);
+        serviceTester.addCountryListener(listenerTesterA);
+        serviceTester.addCountryListener(listenerTesterB);
+        serviceTester.notifyReceivers(country);
+        assertTrue(serviceTester.isListenerSet());
+        assertTrue(listenerTesterA.isNotified());
+        assertTrue(listenerTesterB.isNotified());
+        serviceTester.removeCountryListener(listenerTesterA);
+        serviceTester.removeCountryListener(listenerTesterB);
+        assertFalse(serviceTester.isListenerSet());
+    }
+
+    private void waitForSystemReady(CountryDetectorService service) {
+        int count = 5;
+        while (count-- > 0) {
+            try {
+                Thread.sleep(500);
+            } catch (Exception e) {
+            }
+            if (service.isSystemReady()) {
+                return;
+            }
+        }
+        throw new RuntimeException("Wait System Ready timeout");
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java b/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java
new file mode 100644
index 0000000..1bc9b86
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/MockAccessibilityService.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.Intent;
+import android.os.Message;
+import android.view.accessibility.AccessibilityEvent;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+import junit.framework.TestCase;
+
+/**
+ * This is the base class for mock {@link AccessibilityService}s.
+ */
+public abstract class MockAccessibilityService extends AccessibilityService {
+
+    /**
+     * The event this service expects to receive.
+     */
+    private final Queue<AccessibilityEvent> mExpectedEvents = new LinkedList<AccessibilityEvent>();
+
+    /**
+     * Interruption call this service expects to receive.
+     */
+    private boolean mExpectedInterrupt;
+
+    /**
+     * Flag if the mock is currently replaying.
+     */
+    private boolean mReplaying;
+
+    /**
+     * Flag if the system is bound as a client to this service.
+     */
+    private boolean mIsSystemBoundAsClient;
+
+    /**
+     * Creates an {@link AccessibilityServiceInfo} populated with default
+     * values.
+     *
+     * @return The default info.
+     */
+    public static AccessibilityServiceInfo createDefaultInfo() {
+        AccessibilityServiceInfo defaultInfo = new AccessibilityServiceInfo();
+        defaultInfo.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED;
+        defaultInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE;
+        defaultInfo.flags = 0;
+        defaultInfo.notificationTimeout = 0;
+        defaultInfo.packageNames = new String[] {
+            "foo.bar.baz"
+        };
+
+        return defaultInfo;
+    }
+
+    /**
+     * Starts replaying the mock.
+     */
+    public void replay() {
+        mReplaying = true;
+    }
+
+    /**
+     * Verifies if all expected service methods have been called.
+     */
+    public void verify() {
+        if (!mReplaying) {
+            throw new IllegalStateException("Did you forget to call replay()");
+        }
+
+        if (mExpectedInterrupt) {
+            throw new IllegalStateException("Expected call to #interrupt() not received");
+        }
+        if (!mExpectedEvents.isEmpty()) {
+            throw new IllegalStateException("Expected a call to onAccessibilityEvent() for "
+                    + "events \"" + mExpectedEvents + "\" not received");
+        }
+    }
+
+    /**
+     * Resets this instance so it can be reused.
+     */
+    public void reset() {
+        mExpectedEvents.clear();
+        mExpectedInterrupt = false;
+        mReplaying = false;
+    }
+
+    /**
+     * Sets an expected call to
+     * {@link #onAccessibilityEvent(AccessibilityEvent)} with given event as
+     * argument.
+     *
+     * @param expectedEvent The expected event argument.
+     */
+    public void expectEvent(AccessibilityEvent expectedEvent) {
+        mExpectedEvents.add(expectedEvent);
+    }
+
+    /**
+     * Sets an expected call of {@link #onInterrupt()}.
+     */
+    public void expectInterrupt() {
+        mExpectedInterrupt = true;
+    }
+
+    @Override
+    public void onAccessibilityEvent(AccessibilityEvent receivedEvent) {
+        if (!mReplaying) {
+            return;
+        }
+
+        if (mExpectedEvents.isEmpty()) {
+            throw new IllegalStateException("Unexpected event: " + receivedEvent);
+        }
+
+        AccessibilityEvent expectedEvent = mExpectedEvents.poll();
+        assertEqualsAccessiblityEvent(expectedEvent, receivedEvent);
+    }
+
+    @Override
+    public void onInterrupt() {
+        if (!mReplaying) {
+            return;
+        }
+
+        if (!mExpectedInterrupt) {
+            throw new IllegalStateException("Unexpected call to onInterrupt()");
+        }
+
+        mExpectedInterrupt = false;
+    }
+
+    @Override
+    protected void onServiceConnected() {
+        mIsSystemBoundAsClient = true;
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        mIsSystemBoundAsClient = false;
+        return false;
+    }
+
+    /**
+     * Returns if the system is bound as client to this service.
+     *
+     * @return True if the system is bound, false otherwise.
+     */
+    public boolean isSystemBoundAsClient() {
+        return mIsSystemBoundAsClient;
+    }
+
+    /**
+     * Compares all properties of the <code>expectedEvent</code> and the
+     * <code>receviedEvent</code> to verify that the received event is the one
+     * that is expected.
+     */
+    private void assertEqualsAccessiblityEvent(AccessibilityEvent expectedEvent,
+            AccessibilityEvent receivedEvent) {
+        TestCase.assertEquals("addedCount has incorrect value", expectedEvent.getAddedCount(),
+                receivedEvent.getAddedCount());
+        TestCase.assertEquals("beforeText has incorrect value", expectedEvent.getBeforeText(),
+                receivedEvent.getBeforeText());
+        TestCase.assertEquals("checked has incorrect value", expectedEvent.isChecked(),
+                receivedEvent.isChecked());
+        TestCase.assertEquals("className has incorrect value", expectedEvent.getClassName(),
+                receivedEvent.getClassName());
+        TestCase.assertEquals("contentDescription has incorrect value", expectedEvent
+                .getContentDescription(), receivedEvent.getContentDescription());
+        TestCase.assertEquals("currentItemIndex has incorrect value", expectedEvent
+                .getCurrentItemIndex(), receivedEvent.getCurrentItemIndex());
+        TestCase.assertEquals("enabled has incorrect value", expectedEvent.isEnabled(),
+                receivedEvent.isEnabled());
+        TestCase.assertEquals("eventType has incorrect value", expectedEvent.getEventType(),
+                receivedEvent.getEventType());
+        TestCase.assertEquals("fromIndex has incorrect value", expectedEvent.getFromIndex(),
+                receivedEvent.getFromIndex());
+        TestCase.assertEquals("fullScreen has incorrect value", expectedEvent.isFullScreen(),
+                receivedEvent.isFullScreen());
+        TestCase.assertEquals("itemCount has incorrect value", expectedEvent.getItemCount(),
+                receivedEvent.getItemCount());
+        assertEqualsNotificationAsParcelableData(expectedEvent, receivedEvent);
+        TestCase.assertEquals("password has incorrect value", expectedEvent.isPassword(),
+                receivedEvent.isPassword());
+        TestCase.assertEquals("removedCount has incorrect value", expectedEvent.getRemovedCount(),
+                receivedEvent.getRemovedCount());
+        assertEqualsText(expectedEvent, receivedEvent);
+    }
+
+    /**
+     * Compares the {@link android.os.Parcelable} data of the
+     * <code>expectedEvent</code> and <code>receivedEvent</code> to verify that
+     * the received event is the one that is expected.
+     */
+    private void assertEqualsNotificationAsParcelableData(AccessibilityEvent expectedEvent,
+            AccessibilityEvent receivedEvent) {
+        String message = "parcelableData has incorrect value";
+        Message expectedMessage = (Message) expectedEvent.getParcelableData();
+        Message receivedMessage = (Message) receivedEvent.getParcelableData();
+
+        if (expectedMessage == null) {
+            if (receivedMessage == null) {
+                return;
+            }
+        }
+
+        TestCase.assertNotNull(message, receivedMessage);
+
+        // we do a very simple sanity check since we do not test Message
+        TestCase.assertEquals(message, expectedMessage.what, receivedMessage.what);
+    }
+
+    /**
+     * Compares the text of the <code>expectedEvent</code> and
+     * <code>receivedEvent</code> by comparing the string representation of the
+     * corresponding {@link CharSequence}s.
+     */
+    private void assertEqualsText(AccessibilityEvent expectedEvent,
+            AccessibilityEvent receivedEvent) {
+        String message = "text has incorrect value";
+        List<CharSequence> expectedText = expectedEvent.getText();
+        List<CharSequence> receivedText = receivedEvent.getText();
+
+        TestCase.assertEquals(message, expectedText.size(), receivedText.size());
+
+        Iterator<CharSequence> expectedTextIterator = expectedText.iterator();
+        Iterator<CharSequence> receivedTextIterator = receivedText.iterator();
+
+        for (int i = 0; i < expectedText.size(); i++) {
+            // compare the string representation
+            TestCase.assertEquals(message, expectedTextIterator.next().toString(),
+                    receivedTextIterator.next().toString());
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/ComprehensiveCountryDetectorTest.java b/services/tests/servicestests/src/com/android/server/location/ComprehensiveCountryDetectorTest.java
new file mode 100644
index 0000000..98966c0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/ComprehensiveCountryDetectorTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.location;
+
+import android.location.Country;
+import android.location.CountryListener;
+import android.test.AndroidTestCase;
+
+public class ComprehensiveCountryDetectorTest extends AndroidTestCase {
+    private class TestCountryDetector extends ComprehensiveCountryDetector {
+        public final static String COUNTRY_ISO = "us";
+        private boolean mLocationBasedDetectorStarted;
+        private boolean mLocationBasedDetectorStopped;
+        protected boolean mNotified;
+        private boolean listenerAdded = false;
+
+        private Country mNotifiedCountry;
+        public TestCountryDetector() {
+            super(getContext());
+        }
+
+        public void notifyLocationBasedListener(Country country) {
+            mNotified = true;
+            mNotifiedCountry = country;
+            mLocationBasedCountryDetector.notifyListener(country);
+        }
+
+        public boolean locationBasedDetectorStarted() {
+            return mLocationBasedCountryDetector != null && mLocationBasedDetectorStarted;
+        }
+
+        public boolean locationBasedDetectorStopped() {
+            return mLocationBasedCountryDetector == null && mLocationBasedDetectorStopped;
+        }
+
+        public boolean locationRefreshStarted() {
+            return mLocationRefreshTimer != null;
+        }
+
+        public boolean locationRefreshCancelled() {
+            return mLocationRefreshTimer == null;
+        }
+
+        @Override
+        protected CountryDetectorBase createLocationBasedCountryDetector() {
+            return new CountryDetectorBase(mContext) {
+                @Override
+                public Country detectCountry() {
+                    mLocationBasedDetectorStarted = true;
+                    return null;
+                }
+
+                @Override
+                public void stop() {
+                    mLocationBasedDetectorStopped = true;
+                }
+            };
+        }
+
+        @Override
+        protected Country getNetworkBasedCountry() {
+            return null;
+        }
+
+        @Override
+        protected Country getLastKnownLocationBasedCountry() {
+            return mNotifiedCountry;
+        }
+
+        @Override
+        protected Country getSimBasedCountry() {
+            return null;
+        }
+
+        @Override
+        protected Country getLocaleCountry() {
+            return null;
+        }
+
+        @Override
+        protected void runAfterDetectionAsync(final Country country, final Country detectedCountry,
+                final boolean notifyChange, final boolean startLocationBasedDetection) {
+            runAfterDetection(country, detectedCountry, notifyChange, startLocationBasedDetection);
+        };
+
+        @Override
+        protected boolean isAirplaneModeOff() {
+            return true;
+        }
+
+        @Override
+        protected synchronized void addPhoneStateListener() {
+            listenerAdded = true;
+        }
+
+        @Override
+        protected synchronized void removePhoneStateListener() {
+            listenerAdded = false;
+        }
+
+        @Override
+        protected boolean isGeoCoderImplemented() {
+            return true;
+        }
+
+        public boolean isPhoneStateListenerAdded() {
+            return listenerAdded;
+        }
+    }
+
+    private class CountryListenerImpl implements CountryListener {
+        private boolean mNotified;
+        private Country mCountry;
+
+        public void onCountryDetected(Country country) {
+            mNotified = true;
+            mCountry = country;
+        }
+
+        public boolean notified() {
+            return mNotified;
+        }
+
+        public Country getCountry() {
+            return mCountry;
+        }
+    }
+
+    public void testDetectNetworkBasedCountry() {
+        final Country resultCountry = new Country(
+                TestCountryDetector.COUNTRY_ISO, Country.COUNTRY_SOURCE_NETWORK);
+        TestCountryDetector countryDetector = new TestCountryDetector() {
+            @Override
+            protected Country getNetworkBasedCountry() {
+                return resultCountry;
+            }
+        };
+        CountryListenerImpl listener = new CountryListenerImpl();
+        countryDetector.setCountryListener(listener);
+        Country country = countryDetector.detectCountry();
+        assertTrue(sameCountry(country, resultCountry));
+        assertFalse(listener.notified());
+        assertFalse(countryDetector.locationBasedDetectorStarted());
+        assertFalse(countryDetector.locationRefreshStarted());
+        countryDetector.stop();
+    }
+
+    public void testDetectLocationBasedCountry() {
+        final Country resultCountry = new Country(
+                TestCountryDetector.COUNTRY_ISO, Country.COUNTRY_SOURCE_SIM);
+        final Country locationBasedCountry = new Country(
+                TestCountryDetector.COUNTRY_ISO, Country.COUNTRY_SOURCE_LOCATION);
+        TestCountryDetector countryDetector = new TestCountryDetector() {
+            @Override
+            protected Country getSimBasedCountry() {
+                return resultCountry;
+            }
+        };
+        CountryListenerImpl listener = new CountryListenerImpl();
+        countryDetector.setCountryListener(listener);
+        Country country = countryDetector.detectCountry();
+        assertTrue(sameCountry(country, resultCountry));
+        assertTrue(countryDetector.locationBasedDetectorStarted());
+        countryDetector.notifyLocationBasedListener(locationBasedCountry);
+        assertTrue(listener.notified());
+        assertTrue(sameCountry(listener.getCountry(), locationBasedCountry));
+        assertTrue(countryDetector.locationBasedDetectorStopped());
+        assertTrue(countryDetector.locationRefreshStarted());
+        countryDetector.stop();
+        assertTrue(countryDetector.locationRefreshCancelled());
+    }
+
+    public void testLocaleBasedCountry() {
+        final Country resultCountry = new Country(
+                TestCountryDetector.COUNTRY_ISO, Country.COUNTRY_SOURCE_LOCALE);
+        TestCountryDetector countryDetector = new TestCountryDetector() {
+            @Override
+            protected Country getLocaleCountry() {
+                return resultCountry;
+            }
+        };
+        CountryListenerImpl listener = new CountryListenerImpl();
+        countryDetector.setCountryListener(listener);
+        Country country = countryDetector.detectCountry();
+        assertTrue(sameCountry(country, resultCountry));
+        assertFalse(listener.notified());
+        assertTrue(countryDetector.locationBasedDetectorStarted());
+        assertTrue(countryDetector.locationRefreshStarted());
+        countryDetector.stop();
+        assertTrue(countryDetector.locationRefreshCancelled());
+    }
+
+    public void testStoppingDetector() {
+        // Test stopping detector when LocationBasedCountryDetector was started
+        final Country resultCountry = new Country(
+                TestCountryDetector.COUNTRY_ISO, Country.COUNTRY_SOURCE_SIM);
+        TestCountryDetector countryDetector = new TestCountryDetector() {
+            @Override
+            protected Country getSimBasedCountry() {
+                return resultCountry;
+            }
+        };
+        CountryListenerImpl listener = new CountryListenerImpl();
+        countryDetector.setCountryListener(listener);
+        Country country = countryDetector.detectCountry();
+        assertTrue(sameCountry(country, resultCountry));
+        assertTrue(countryDetector.locationBasedDetectorStarted());
+        countryDetector.stop();
+        // The LocationBasedDetector should be stopped.
+        assertTrue(countryDetector.locationBasedDetectorStopped());
+        // The location refresh should not running.
+        assertTrue(countryDetector.locationRefreshCancelled());
+    }
+
+    public void testLocationBasedCountryNotFound() {
+        final Country resultCountry = new Country(
+                TestCountryDetector.COUNTRY_ISO, Country.COUNTRY_SOURCE_SIM);
+        TestCountryDetector countryDetector = new TestCountryDetector() {
+            @Override
+            protected Country getSimBasedCountry() {
+                return resultCountry;
+            }
+        };
+        CountryListenerImpl listener = new CountryListenerImpl();
+        countryDetector.setCountryListener(listener);
+        Country country = countryDetector.detectCountry();
+        assertTrue(sameCountry(country, resultCountry));
+        assertTrue(countryDetector.locationBasedDetectorStarted());
+        countryDetector.notifyLocationBasedListener(null);
+        assertFalse(listener.notified());
+        assertTrue(sameCountry(listener.getCountry(), null));
+        assertTrue(countryDetector.locationBasedDetectorStopped());
+        assertTrue(countryDetector.locationRefreshStarted());
+        countryDetector.stop();
+        assertTrue(countryDetector.locationRefreshCancelled());
+    }
+
+    public void testNoCountryFound() {
+        TestCountryDetector countryDetector = new TestCountryDetector();
+        CountryListenerImpl listener = new CountryListenerImpl();
+        countryDetector.setCountryListener(listener);
+        Country country = countryDetector.detectCountry();
+        assertTrue(sameCountry(country, null));
+        assertTrue(countryDetector.locationBasedDetectorStarted());
+        countryDetector.notifyLocationBasedListener(null);
+        assertFalse(listener.notified());
+        assertTrue(sameCountry(listener.getCountry(), null));
+        assertTrue(countryDetector.locationBasedDetectorStopped());
+        assertTrue(countryDetector.locationRefreshStarted());
+        countryDetector.stop();
+        assertTrue(countryDetector.locationRefreshCancelled());
+    }
+
+    public void testAddRemoveListener() {
+        TestCountryDetector countryDetector = new TestCountryDetector();
+        CountryListenerImpl listener = new CountryListenerImpl();
+        countryDetector.setCountryListener(listener);
+        assertTrue(countryDetector.isPhoneStateListenerAdded());
+        assertTrue(countryDetector.locationBasedDetectorStarted());
+        countryDetector.setCountryListener(null);
+        assertFalse(countryDetector.isPhoneStateListenerAdded());
+        assertTrue(countryDetector.locationBasedDetectorStopped());
+    }
+
+    public void testGeocoderNotImplemented() {
+        TestCountryDetector countryDetector = new TestCountryDetector() {
+            @Override
+            protected boolean isGeoCoderImplemented() {
+                return false;
+            }
+        };
+        CountryListenerImpl listener = new CountryListenerImpl();
+        countryDetector.setCountryListener(listener);
+        assertTrue(countryDetector.isPhoneStateListenerAdded());
+        assertFalse(countryDetector.locationBasedDetectorStarted());
+        countryDetector.setCountryListener(null);
+        assertFalse(countryDetector.isPhoneStateListenerAdded());
+    }
+
+    private boolean sameCountry(Country country1, Country country2) {
+        return country1 == null && country2 == null || country1 != null && country2 != null &&
+        country1.getCountryIso().equalsIgnoreCase(country2.getCountryIso()) &&
+        country1.getSource() == country2.getSource();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java b/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java
new file mode 100755
index 0000000..71e8e2a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.server.location;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+
+import android.location.Country;
+import android.location.CountryListener;
+import android.location.Location;
+import android.location.LocationListener;
+import android.test.AndroidTestCase;
+
+public class LocationBasedCountryDetectorTest extends AndroidTestCase {
+    private class TestCountryDetector extends LocationBasedCountryDetector {
+        public static final int TOTAL_PROVIDERS = 2;
+        protected Object countryFoundLocker = new Object();
+        protected boolean notifyCountry = false;
+        private final Location mLocation;
+        private final String mCountry;
+        private final long mQueryLocationTimeout;
+        private List<LocationListener> mListeners;
+
+        public TestCountryDetector(String country, String provider) {
+            this(country, provider, 1000 * 60 * 5);
+        }
+
+        public TestCountryDetector(String country, String provider, long queryLocationTimeout) {
+            super(getContext());
+            mCountry = country;
+            mLocation = new Location(provider);
+            mQueryLocationTimeout = queryLocationTimeout;
+            mListeners = new ArrayList<LocationListener>();
+        }
+
+        @Override
+        protected String getCountryFromLocation(Location location) {
+            synchronized (countryFoundLocker) {
+                if (!notifyCountry) {
+                    try {
+                        countryFoundLocker.wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+            }
+            if (mLocation.getProvider().endsWith(location.getProvider())) {
+                return mCountry;
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        protected Location getLastKnownLocation() {
+            return mLocation;
+        }
+
+        @Override
+        protected void registerEnabledProviders(List<LocationListener> listeners) {
+            mListeners.addAll(listeners);
+        }
+
+        @Override
+        protected void unregisterProviders(List<LocationListener> listeners) {
+            for (LocationListener listener : mLocationListeners) {
+                assertTrue(mListeners.remove(listener));
+            }
+        }
+
+        @Override
+        protected long getQueryLocationTimeout() {
+            return mQueryLocationTimeout;
+        }
+
+        @Override
+        protected int getTotalEnabledProviders() {
+            return TOTAL_PROVIDERS;
+        }
+
+        public void notifyLocationFound() {
+            // Listener could be removed in the notification.
+            LocationListener[] listeners = new LocationListener[mListeners.size()];
+            mLocationListeners.toArray(listeners);
+            for (LocationListener listener :listeners) {
+                listener.onLocationChanged(mLocation);
+            }
+        }
+
+        public int getListenersCount() {
+            return mListeners.size();
+        }
+
+        public void notifyCountryFound() {
+            synchronized (countryFoundLocker) {
+                notifyCountry = true;
+                countryFoundLocker.notify();
+            }
+        }
+
+        public Timer getTimer() {
+            return mTimer;
+        }
+
+        public Thread getQueryThread() {
+            return mQueryThread;
+        }
+    }
+
+    private class CountryListenerImpl implements CountryListener {
+        private boolean mNotified;
+        private String mCountryCode;
+        public void onCountryDetected(Country country) {
+            mNotified = true;
+            if (country != null) {
+                mCountryCode = country.getCountryIso();
+            }
+        }
+
+        public boolean notified() {
+            return mNotified;
+        }
+
+        public String getCountry() {
+            return mCountryCode;
+        }
+    }
+
+    public void testFindingCountry() {
+        final String country = "us";
+        final String provider = "Good";
+        CountryListenerImpl countryListener = new CountryListenerImpl();
+        TestCountryDetector detector = new TestCountryDetector(country, provider);
+        detector.setCountryListener(countryListener);
+        detector.detectCountry();
+        assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+        detector.notifyLocationFound();
+        // All listeners should be unregistered
+        assertEquals(detector.getListenersCount(), 0);
+        assertNull(detector.getTimer());
+        Thread queryThread = waitForQueryThreadLaunched(detector);
+        detector.notifyCountryFound();
+        // Wait for query thread ending
+        waitForThreadEnding(queryThread);
+        // QueryThread should be set to NULL
+        assertNull(detector.getQueryThread());
+        assertTrue(countryListener.notified());
+        assertEquals(countryListener.getCountry(), country);
+    }
+
+    public void testFindingCountryCancelled() {
+        final String country = "us";
+        final String provider = "Good";
+        CountryListenerImpl countryListener = new CountryListenerImpl();
+        TestCountryDetector detector = new TestCountryDetector(country, provider);
+        detector.setCountryListener(countryListener);
+        detector.detectCountry();
+        assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+        detector.notifyLocationFound();
+        // All listeners should be unregistered
+        assertEquals(detector.getListenersCount(), 0);
+        // The time should be stopped
+        assertNull(detector.getTimer());
+        Thread queryThread = waitForQueryThreadLaunched(detector);
+        detector.stop();
+        // There is no way to stop the thread, let's test it could be stopped, after get country
+        detector.notifyCountryFound();
+        // Wait for query thread ending
+        waitForThreadEnding(queryThread);
+        // QueryThread should be set to NULL
+        assertNull(detector.getQueryThread());
+        assertTrue(countryListener.notified());
+        assertEquals(countryListener.getCountry(), country);
+    }
+
+    public void testFindingLocationCancelled() {
+        final String country = "us";
+        final String provider = "Good";
+        CountryListenerImpl countryListener = new CountryListenerImpl();
+        TestCountryDetector detector = new TestCountryDetector(country, provider);
+        detector.setCountryListener(countryListener);
+        detector.detectCountry();
+        assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+        detector.stop();
+        // All listeners should be unregistered
+        assertEquals(detector.getListenersCount(), 0);
+        // The time should be stopped
+        assertNull(detector.getTimer());
+        // QueryThread should still be NULL
+        assertNull(detector.getQueryThread());
+        assertFalse(countryListener.notified());
+    }
+
+    public void testFindingLocationFailed() {
+        final String country = "us";
+        final String provider = "Good";
+        long timeout = 1000;
+        TestCountryDetector detector = new TestCountryDetector(country, provider, timeout) {
+            @Override
+            protected Location getLastKnownLocation() {
+                return null;
+            }
+        };
+        CountryListenerImpl countryListener = new CountryListenerImpl();
+        detector.setCountryListener(countryListener);
+        detector.detectCountry();
+        assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+        waitForTimerReset(detector);
+        // All listeners should be unregistered
+        assertEquals(detector.getListenersCount(), 0);
+        // QueryThread should still be NULL
+        assertNull(detector.getQueryThread());
+        assertTrue(countryListener.notified());
+        assertNull(countryListener.getCountry());
+    }
+
+    public void testFindingCountryFailed() {
+        final String country = "us";
+        final String provider = "Good";
+        TestCountryDetector detector = new TestCountryDetector(country, provider) {
+            @Override
+            protected String getCountryFromLocation(Location location) {
+                synchronized (countryFoundLocker) {
+                    if (! notifyCountry) {
+                        try {
+                            countryFoundLocker.wait();
+                        } catch (InterruptedException e) {
+                        }
+                    }
+                }
+                // We didn't find country.
+                return null;
+            }
+        };
+        CountryListenerImpl countryListener = new CountryListenerImpl();
+        detector.setCountryListener(countryListener);
+        detector.detectCountry();
+        assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+        detector.notifyLocationFound();
+        // All listeners should be unregistered
+        assertEquals(detector.getListenersCount(), 0);
+        assertNull(detector.getTimer());
+        Thread queryThread = waitForQueryThreadLaunched(detector);
+        detector.notifyCountryFound();
+        // Wait for query thread ending
+        waitForThreadEnding(queryThread);
+        // QueryThread should be set to NULL
+        assertNull(detector.getQueryThread());
+        // CountryListener should be notified
+        assertTrue(countryListener.notified());
+        assertNull(countryListener.getCountry());
+    }
+
+    public void testFindingCountryWithLastKnownLocation() {
+        final String country = "us";
+        final String provider = "Good";
+        long timeout = 1000;
+        TestCountryDetector detector = new TestCountryDetector(country, provider, timeout);
+        CountryListenerImpl countryListener = new CountryListenerImpl();
+        detector.setCountryListener(countryListener);
+        detector.detectCountry();
+        assertEquals(detector.getListenersCount(), TestCountryDetector.TOTAL_PROVIDERS);
+        waitForTimerReset(detector);
+        // All listeners should be unregistered
+        assertEquals(detector.getListenersCount(), 0);
+        Thread queryThread = waitForQueryThreadLaunched(detector);
+        detector.notifyCountryFound();
+        // Wait for query thread ending
+        waitForThreadEnding(queryThread);
+        // QueryThread should be set to NULL
+        assertNull(detector.getQueryThread());
+        // CountryListener should be notified
+        assertTrue(countryListener.notified());
+        assertEquals(countryListener.getCountry(), country);
+    }
+
+    private void waitForTimerReset(TestCountryDetector detector) {
+        int count = 5;
+        long interval = 1000;
+        try {
+            while (count-- > 0 && detector.getTimer() != null) {
+                Thread.sleep(interval);
+            }
+        } catch (InterruptedException e) {
+        }
+        Timer timer = detector.getTimer();
+        assertTrue(timer == null);
+    }
+
+    private void waitForThreadEnding(Thread thread) {
+        try {
+            thread.join(5000);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private Thread waitForQueryThreadLaunched(TestCountryDetector detector) {
+        int count = 5;
+        long interval = 1000;
+        try {
+            while (count-- > 0 && detector.getQueryThread() == null) {
+                Thread.sleep(interval);
+            }
+        } catch (InterruptedException e) {
+        }
+        Thread thread = detector.getQueryThread();
+        assertTrue(thread != null);
+        return thread;
+    }
+}
diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java
index 2f7666d..0f9a3b9 100644
--- a/telephony/java/android/telephony/NeighboringCellInfo.java
+++ b/telephony/java/android/telephony/NeighboringCellInfo.java
@@ -127,7 +127,7 @@
                 location = "0" + location;
             }
         }
-
+        // TODO - handle LTE and eHRPD (or find they can't be supported)
         try {// set LAC/CID or PSC based on radioType
             switch (radioType) {
             case NETWORK_TYPE_GPRS:
diff --git a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
index 8a47339..ffabb7b 100644
--- a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
+++ b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
@@ -16,83 +16,200 @@
 
 package android.telephony;
 
+import com.google.i18n.phonenumbers.AsYouTypeFormatter;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+
+import android.telephony.PhoneNumberUtils;
 import android.text.Editable;
 import android.text.Selection;
 import android.text.TextWatcher;
-import android.widget.TextView;
 
 import java.util.Locale;
 
 /**
- * Watches a {@link TextView} and if a phone number is entered will format it using
- * {@link PhoneNumberUtils#formatNumber(Editable, int)}. The formatting is based on
- * the current system locale when this object is created and future locale changes
- * may not take effect on this instance.
+ * Watches a {@link android.widget.TextView} and if a phone number is entered
+ * will format it.
+ * <p>
+ * Stop formatting when the user
+ * <ul>
+ * <li>Inputs non-dialable characters</li>
+ * <li>Removes the separator in the middle of string.</li>
+ * </ul>
+ * <p>
+ * The formatting will be restarted once the text is cleared.
  */
 public class PhoneNumberFormattingTextWatcher implements TextWatcher {
+    /**
+     * One or more characters were removed from the end.
+     */
+    private final static int STATE_REMOVE_LAST = 0;
 
-    static private int sFormatType;
-    static private Locale sCachedLocale;
-    private boolean mFormatting;
-    private boolean mDeletingHyphen;
-    private int mHyphenStart;
-    private boolean mDeletingBackward;
+    /**
+     * One or more characters were appended.
+     */
+    private final static int STATE_APPEND = 1;
 
+    /**
+     * One or more digits were changed in the beginning or the middle of text.
+     */
+    private final static int STATE_MODIFY_DIGITS = 2;
+
+    /**
+     * The changes other than the above.
+     */
+    private final static int STATE_OTHER = 3;
+
+    /**
+     * The state of this change could be one value of the above
+     */
+    private int mState;
+
+    /**
+     * Indicates the change was caused by ourselves.
+     */
+    private boolean mSelfChange = false;
+
+    /**
+     * Indicates the formatting has been stopped.
+     */
+    private boolean mStopFormatting;
+
+    private AsYouTypeFormatter mFormatter;
+
+    /**
+     * The formatting is based on the current system locale and future locale changes
+     * may not take effect on this instance.
+     */
     public PhoneNumberFormattingTextWatcher() {
-        if (sCachedLocale == null || sCachedLocale != Locale.getDefault()) {
-            sCachedLocale = Locale.getDefault();
-            sFormatType = PhoneNumberUtils.getFormatTypeForLocale(sCachedLocale);
-        }
+        this(Locale.getDefault().getCountry());
     }
 
-    public synchronized void afterTextChanged(Editable text) {
-        // Make sure to ignore calls to afterTextChanged caused by the work done below
-        if (!mFormatting) {
-            mFormatting = true;
-
-            // If deleting the hyphen, also delete the char before or after that
-            if (mDeletingHyphen && mHyphenStart > 0) {
-                if (mDeletingBackward) {
-                    if (mHyphenStart - 1 < text.length()) {
-                        text.delete(mHyphenStart - 1, mHyphenStart);
-                    }
-                } else if (mHyphenStart < text.length()) {
-                    text.delete(mHyphenStart, mHyphenStart + 1);
-                }
-            }
-
-            PhoneNumberUtils.formatNumber(text, sFormatType);
-
-            mFormatting = false;
-        }
+    /**
+     * The formatting is based on the given <code>countryCode</code>.
+     *
+     * @param countryCode the ISO 3166-1 two-letter country code that indicates the country/region
+     * where the phone number is being entered.
+     *
+     * @hide
+     */
+    public PhoneNumberFormattingTextWatcher(String countryCode) {
+        if (countryCode == null) throw new IllegalArgumentException();
+        mFormatter = PhoneNumberUtil.getInstance().getAsYouTypeFormatter(countryCode);
     }
 
-    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-        // Check if the user is deleting a hyphen
-        if (!mFormatting) {
-            // Make sure user is deleting one char, without a selection
-            final int selStart = Selection.getSelectionStart(s);
-            final int selEnd = Selection.getSelectionEnd(s);
-            if (s.length() > 1 // Can delete another character
-                    && count == 1 // Deleting only one character
-                    && after == 0 // Deleting
-                    && s.charAt(start) == '-' // a hyphen
-                    && selStart == selEnd) { // no selection
-                mDeletingHyphen = true;
-                mHyphenStart = start;
-                // Check if the user is deleting forward or backward
-                if (selStart == start + 1) {
-                    mDeletingBackward = true;
-                } else {
-                    mDeletingBackward = false;
-                }
-            } else {
-                mDeletingHyphen = false;
-            }
+    public void beforeTextChanged(CharSequence s, int start, int count,
+            int after) {
+        if (mSelfChange || mStopFormatting) {
+            return;
+        }
+        if (count == 0 && s.length() == start) {
+            // Append one or more new chars
+            mState = STATE_APPEND;
+        } else if (after == 0 && start + count == s.length() && count > 0) {
+            // Remove one or more chars from the end of string.
+            mState = STATE_REMOVE_LAST;
+        } else if (count > 0 && !hasSeparator(s, start, count)) {
+            // Remove the dialable chars in the begin or middle of text.
+            mState = STATE_MODIFY_DIGITS;
+        } else {
+            mState = STATE_OTHER;
         }
     }
 
     public void onTextChanged(CharSequence s, int start, int before, int count) {
-        // Does nothing
+        if (mSelfChange || mStopFormatting) {
+            return;
+        }
+        if (mState == STATE_OTHER) {
+            if (count > 0 && !hasSeparator(s, start, count)) {
+                // User inserted the dialable characters in the middle of text.
+                mState = STATE_MODIFY_DIGITS;
+            }
+        }
+        // Check whether we should stop formatting.
+        if (mState == STATE_APPEND && count > 0 && hasSeparator(s, start, count)) {
+            // User appended the non-dialable character, stop formatting.
+            stopFormatting();
+        } else if (mState == STATE_OTHER) {
+            // User must insert or remove the non-dialable characters in the begin or middle of
+            // number, stop formatting.
+            stopFormatting();
+        }
+    }
+
+    public synchronized void afterTextChanged(Editable s) {
+        if (mStopFormatting) {
+            // Restart the formatting when all texts were clear.
+            mStopFormatting = !(s.length() == 0);
+            return;
+        }
+        if (mSelfChange) {
+            // Ignore the change caused by s.replace().
+            return;
+        }
+        String formatted = reformat(s, Selection.getSelectionEnd(s));
+        if (formatted != null) {
+            int rememberedPos = mFormatter.getRememberedPosition();
+            mSelfChange = true;
+            s.replace(0, s.length(), formatted, 0, formatted.length());
+            // The text could be changed by other TextWatcher after we changed it. If we found the
+            // text is not the one we were expecting, just give up calling setSelection().
+            if (formatted.equals(s.toString())) {
+                Selection.setSelection(s, rememberedPos);
+            }
+            mSelfChange = false;
+        }
+    }
+
+    /**
+     * Generate the formatted number by ignoring all non-dialable chars and stick the cursor to the
+     * nearest dialable char to the left. For instance, if the number is  (650) 123-45678 and '4' is
+     * removed then the cursor should be behind '3' instead of '-'.
+     */
+    private String reformat(CharSequence s, int cursor) {
+        // The index of char to the leftward of the cursor.
+        int curIndex = cursor - 1;
+        String formatted = null;
+        mFormatter.clear();
+        char lastNonSeparator = 0;
+        boolean hasCursor = false;
+        int len = s.length();
+        for (int i = 0; i < len; i++) {
+            char c = s.charAt(i);
+            if (PhoneNumberUtils.isNonSeparator(c)) {
+                if (lastNonSeparator != 0) {
+                    formatted = getFormattedNumber(lastNonSeparator, hasCursor);
+                    hasCursor = false;
+                }
+                lastNonSeparator = c;
+            }
+            if (i == curIndex) {
+                hasCursor = true;
+            }
+        }
+        if (lastNonSeparator != 0) {
+            formatted = getFormattedNumber(lastNonSeparator, hasCursor);
+        }
+        return formatted;
+    }
+
+    private String getFormattedNumber(char lastNonSeparator, boolean hasCursor) {
+        return hasCursor ? mFormatter.inputDigitAndRememberPosition(lastNonSeparator)
+                : mFormatter.inputDigit(lastNonSeparator);
+    }
+
+    private void stopFormatting() {
+        mStopFormatting = true;
+        mFormatter.clear();
+    }
+
+    private boolean hasSeparator(final CharSequence s, final int start, final int count) {
+        for (int i = start; i < start + count; i++) {
+            char c = s.charAt(i);
+            if (!PhoneNumberUtils.isNonSeparator(c)) {
+                return true;
+            }
+        }
+        return false;
     }
 }
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 8e4f6fc..893ae88 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -16,6 +16,11 @@
 
 package android.telephony;
 
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
+
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
@@ -617,7 +622,7 @@
             }
         } else {
             // In the US, 1-650-555-1234 must be equal to 650-555-1234,
-            // while 090-1234-1234 must not be equalt to 90-1234-1234 in Japan.
+            // while 090-1234-1234 must not be equal to 90-1234-1234 in Japan.
             // This request exists just in US (with 1 trunk (NDD) prefix).
             // In addition, "011 11 7005554141" must not equal to "+17005554141",
             // while "011 1 7005554141" must equal to "+17005554141"
@@ -779,10 +784,10 @@
         if (prependPlus) {
             // This is an "international number" and should have
             // a plus prepended to the dialing number. But there
-            // can also be Gsm MMI codes as defined in TS 22.030 6.5.2
+            // can also be GSM MMI codes as defined in TS 22.030 6.5.2
             // so we need to handle those also.
             //
-            // http://web.telia.com/~u47904776/gsmkode.htm is a
+            // http://web.telia.com/~u47904776/gsmkode.htm
             // has a nice list of some of these GSM codes.
             //
             // Examples are:
@@ -870,10 +875,10 @@
 
             // FIXME(mkf) TS 23.040 9.1.2.3 says
             // "if a mobile receives 1111 in a position prior to
-            // the last semi-octet then processing shall commense with
+            // the last semi-octet then processing shall commence with
             // the next semi-octet and the intervening
             // semi-octet shall be ignored"
-            // How does this jive with 24,008 10.5.4.7
+            // How does this jive with 24.008 10.5.4.7
 
             b = (byte)((bytes[i] >> 4) & 0xf);
 
@@ -1004,7 +1009,7 @@
      * Convert a dialing number to BCD byte array
      *
      * @param number dialing number string
-     *        if the dialing number starts with '+', set to internationl TOA
+     *        if the dialing number starts with '+', set to international TOA
      * @return BCD byte array
      */
     public static byte[]
@@ -1108,10 +1113,10 @@
      *
      * @param source the phone number to format
      * @param defaultFormattingType The default formatting rules to apply if the number does
-     * not begin with +<country_code>
+     * not begin with +[country_code]
      * @return The phone number formatted with the given formatting type.
      *
-     * @hide TODO:Shuold be unhidden.
+     * @hide TODO: Should be unhidden.
      */
     public static String formatNumber(String source, int defaultFormattingType) {
         SpannableStringBuilder text = new SpannableStringBuilder(source);
@@ -1138,7 +1143,7 @@
      *
      * @param text The number to be formatted, will be modified with the formatting
      * @param defaultFormattingType The default formatting rules to apply if the number does
-     * not begin with +<country_code>
+     * not begin with +[country_code]
      */
     public static void formatNumber(Editable text, int defaultFormattingType) {
         int formatType = defaultFormattingType;
@@ -1319,6 +1324,132 @@
         }
     }
 
+    /**
+     * Format the given phoneNumber to the E.164 representation.
+     * <p>
+     * The given phone number must have an area code and could have a country
+     * code.
+     * <p>
+     * The defaultCountryIso is used to validate the given number and generate
+     * the E.164 phone number if the given number doesn't have a country code.
+     *
+     * @param phoneNumber
+     *            the phone number to format
+     * @param defaultCountryIso
+     *            the ISO 3166-1 two letters country code
+     * @return the E.164 representation, or null if the given phone number is
+     *         not valid.
+     *
+     * @hide
+     */
+    public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) {
+        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+        String result = null;
+        try {
+            PhoneNumber pn = util.parse(phoneNumber, defaultCountryIso);
+            if (util.isValidNumber(pn)) {
+                result = util.format(pn, PhoneNumberFormat.E164);
+            }
+        } catch (NumberParseException e) {
+        }
+        return result;
+    }
+
+    /**
+     * Format a phone number.
+     * <p>
+     * If the given number doesn't have the country code, the phone will be
+     * formatted to the default country's convention.
+     *
+     * @param phoneNumber
+     *            the number to be formatted.
+     * @param defaultCountryIso
+     *            the ISO 3166-1 two letters country code whose convention will
+     *            be used if the given number doesn't have the country code.
+     * @return the formatted number, or null if the given number is not valid.
+     *
+     * @hide
+     */
+    public static String formatNumber(String phoneNumber, String defaultCountryIso) {
+        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+        String result = null;
+        try {
+            PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
+            result = util.formatInOriginalFormat(pn, defaultCountryIso);
+        } catch (NumberParseException e) {
+        }
+        return result;
+    }
+
+    /**
+     * Format the phone number only if the given number hasn't been formatted.
+     * <p>
+     * The number which has only dailable character is treated as not being
+     * formatted.
+     *
+     * @param phoneNumber
+     *            the number to be formatted.
+     * @param phoneNumberE164
+     *            the E164 format number whose country code is used if the given
+     *            phoneNumber doesn't have the country code.
+     * @param defaultCountryIso
+     *            the ISO 3166-1 two letters country code whose convention will
+     *            be used if the phoneNumberE164 is null or invalid.
+     * @return the formatted number if the given number has been formatted,
+     *            otherwise, return the given number.
+     *
+     * @hide
+     */
+    public static String formatNumber(
+            String phoneNumber, String phoneNumberE164, String defaultCountryIso) {
+        int len = phoneNumber.length();
+        for (int i = 0; i < len; i++) {
+            if (!isDialable(phoneNumber.charAt(i))) {
+                return phoneNumber;
+            }
+        }
+        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+        // Get the country code from phoneNumberE164
+        if (phoneNumberE164 != null && phoneNumberE164.length() >= 2
+                && phoneNumberE164.charAt(0) == '+') {
+            try {
+                PhoneNumber pn = util.parse(phoneNumberE164, defaultCountryIso);
+                String regionCode = util.getRegionCodeForNumber(pn);
+                if (!TextUtils.isEmpty(regionCode)) {
+                    defaultCountryIso = regionCode;
+                }
+            } catch (NumberParseException e) {
+            }
+        }
+        String result = formatNumber(phoneNumber, defaultCountryIso);
+        return result != null ? result : phoneNumber;
+    }
+
+    /**
+     * Normalize a phone number by removing the characters other than digits. If
+     * the given number has keypad letters, the letters will be converted to
+     * digits first.
+     *
+     * @param phoneNumber
+     *            the number to be normalized.
+     * @return the normalized number.
+     *
+     * @hide
+     */
+    public static String normalizeNumber(String phoneNumber) {
+        StringBuilder sb = new StringBuilder();
+        int len = phoneNumber.length();
+        for (int i = 0; i < len; i++) {
+            char c = phoneNumber.charAt(i);
+            if ((i == 0 && c == '+') || PhoneNumberUtils.isISODigit(c)) {
+                sb.append(c);
+            } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+                return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber));
+            }
+        }
+        return sb.toString();
+    }
+
     // Three and four digit phone numbers for either special services,
     // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should
     // not match.
@@ -1546,7 +1677,7 @@
      * @hide
      */
     public static String
-    cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormt) {
+    cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormat) {
         String retStr = dialStr;
 
         // Checks if the plus sign character is in the passed-in dial string
@@ -1554,7 +1685,7 @@
             dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
             // Format the string based on the rules for the country the number is from,
             // and the current country the phone is camped on.
-            if ((currFormat == defaultFormt) && (currFormat == FORMAT_NANP)) {
+            if ((currFormat == defaultFormat) && (currFormat == FORMAT_NANP)) {
                 // Handle case where default and current telephone numbering plans are NANP.
                 String postDialStr = null;
                 String tempDialStr = dialStr;
@@ -1748,7 +1879,7 @@
         return -1;
     }
 
-    // This function appends the non-diablable P/W character to the original
+    // This function appends the non-dialable P/W character to the original
     // dial string based on the dialable index passed in
     private static String
     appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
@@ -1768,7 +1899,7 @@
         return retStr;
     }
 
-    //===== Begining of utility methods used in compareLoosely() =====
+    //===== Beginning of utility methods used in compareLoosely() =====
 
     /**
      * Phone numbers are stored in "lookup" form in the database
@@ -1890,12 +2021,12 @@
 
     //===== End of utility methods used only in compareLoosely() =====
 
-    //===== Beggining of utility methods used only in compareStrictly() ====
+    //===== Beginning of utility methods used only in compareStrictly() ====
 
     /*
      * If true, the number is country calling code.
      */
-    private static final boolean COUNTLY_CALLING_CALL[] = {
+    private static final boolean COUNTRY_CALLING_CALL[] = {
         true, true, false, false, false, false, false, true, false, false,
         false, false, false, false, false, false, false, false, false, false,
         true, false, false, false, false, false, false, true, true, false,
@@ -1907,18 +2038,18 @@
         false, true, true, true, true, false, true, false, false, true,
         true, true, true, true, true, true, false, false, true, false,
     };
-    private static final int CCC_LENGTH = COUNTLY_CALLING_CALL.length;
+    private static final int CCC_LENGTH = COUNTRY_CALLING_CALL.length;
 
     /**
      * @return true when input is valid Country Calling Code.
      */
     private static boolean isCountryCallingCode(int countryCallingCodeCandidate) {
         return countryCallingCodeCandidate > 0 && countryCallingCodeCandidate < CCC_LENGTH &&
-                COUNTLY_CALLING_CALL[countryCallingCodeCandidate];
+                COUNTRY_CALLING_CALL[countryCallingCodeCandidate];
     }
 
     /**
-     * Returns interger corresponding to the input if input "ch" is
+     * Returns integer corresponding to the input if input "ch" is
      * ISO-LATIN characters 0-9.
      * Returns -1 otherwise
      */
@@ -2053,7 +2184,7 @@
 
     /**
      * Return true if the prefix of "str" is "ignorable". Here, "ignorable" means
-     * that "str" has only one digit and separater characters. The one digit is
+     * that "str" has only one digit and separator characters. The one digit is
      * assumed to be trunk prefix.
      */
     private static boolean checkPrefixIsIgnorable(final String str,
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 830af47..eda9b71 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -25,6 +25,7 @@
 import android.util.Log;
 
 import com.android.internal.telephony.IPhoneStateListener;
+import com.android.internal.telephony.Phone;
 
 /**
  * A listener class for monitoring changes in specific telephony states
@@ -147,6 +148,14 @@
      */
     public static final int LISTEN_SIGNAL_STRENGTHS                         = 0x00000100;
 
+    /**
+     * Listen for changes to OTASP mode.
+     *
+     * @see #onOtaspChanged
+     * @hide
+     */
+    public static final int LISTEN_OTASP_CHANGED                            = 0x00000200;
+
     public PhoneStateListener() {
     }
 
@@ -251,6 +260,21 @@
         // default implementation empty
     }
 
+
+    /**
+     * The Over The Air Service Provisioning (OTASP) has changed. Requires
+     * the READ_PHONE_STATE permission.
+     * @param otaspMode is integer <code>OTASP_UNKNOWN=1<code>
+     *   means the value is currently unknown and the system should wait until
+     *   <code>OTASP_NEEDED=2<code> or <code>OTASP_NOT_NEEDED=3<code> is received before
+     *   making the decisision to perform OTASP or not.
+     *
+     * @hide
+     */
+    public void onOtaspChanged(int otaspMode) {
+        // default implementation empty
+    }
+
     /**
      * The callback methods need to be called on the handler thread where
      * this object was created.  If the binder did that for us it'd be nice.
@@ -284,16 +308,21 @@
         }
 
         public void onDataConnectionStateChanged(int state, int networkType) {
-            Message.obtain(mHandler, LISTEN_DATA_CONNECTION_STATE, state, networkType, null).
+            Message.obtain(mHandler, LISTEN_DATA_CONNECTION_STATE, state, networkType).
                     sendToTarget();
         }
 
         public void onDataActivity(int direction) {
             Message.obtain(mHandler, LISTEN_DATA_ACTIVITY, direction, 0, null).sendToTarget();
         }
+
         public void onSignalStrengthsChanged(SignalStrength signalStrength) {
             Message.obtain(mHandler, LISTEN_SIGNAL_STRENGTHS, 0, 0, signalStrength).sendToTarget();
         }
+
+        public void onOtaspChanged(int otaspMode) {
+            Message.obtain(mHandler, LISTEN_OTASP_CHANGED, otaspMode, 0).sendToTarget();
+        }
     };
 
     Handler mHandler = new Handler() {
@@ -328,6 +357,9 @@
                 case LISTEN_SIGNAL_STRENGTHS:
                     PhoneStateListener.this.onSignalStrengthsChanged((SignalStrength)msg.obj);
                     break;
+                case LISTEN_OTASP_CHANGED:
+                    PhoneStateListener.this.onOtaspChanged(msg.arg1);
+                    break;
             }
         }
     };
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index f5e9751..953696b 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -21,7 +21,6 @@
 import android.os.ServiceManager;
 import android.text.TextUtils;
 
-import com.android.internal.telephony.EncodeException;
 import com.android.internal.telephony.ISms;
 import com.android.internal.telephony.IccConstants;
 import com.android.internal.telephony.SmsRawData;
@@ -33,7 +32,7 @@
 /*
  * TODO(code review): Curious question... Why are a lot of these
  * methods not declared as static, since they do not seem to require
- * any local object state?  Assumedly this cannot be changed without
+ * any local object state?  Presumably this cannot be changed without
  * interfering with the API...
  */
 
@@ -42,7 +41,8 @@
  * Get this object by calling the static method SmsManager.getDefault().
  */
 public final class SmsManager {
-    private static SmsManager sInstance;
+    /** Singleton object constructed during class initialization. */
+    private static final SmsManager sInstance = new SmsManager();
 
     /**
      * Send a text based SMS.
@@ -52,8 +52,8 @@
      *  the current default SMSC
      * @param text the body of the message to send
      * @param sentIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is sucessfully sent, or failed.
-     *  The result code will be <code>Activity.RESULT_OK<code> for success,
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success,
      *  or one of these errors:<br>
      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
@@ -116,7 +116,7 @@
      * @param sentIntents if not null, an <code>ArrayList</code> of
      *   <code>PendingIntent</code>s (one for each message part) that is
      *   broadcast when the corresponding message part has been sent.
-     *   The result code will be <code>Activity.RESULT_OK<code> for success,
+     *   The result code will be <code>Activity.RESULT_OK</code> for success,
      *   or one of these errors:<br>
      *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
      *   <code>RESULT_ERROR_RADIO_OFF</code><br>
@@ -125,7 +125,7 @@
      *   the extra "errorCode" containing a radio technology specific value,
      *   generally only useful for troubleshooting.<br>
      *   The per-application based SMS control checks sentIntent. If sentIntent
-     *   is NULL the caller will be checked against all unknown applicaitons,
+     *   is NULL the caller will be checked against all unknown applications,
      *   which cause smaller number of SMS to be sent in checking period.
      * @param deliveryIntents if not null, an <code>ArrayList</code> of
      *   <code>PendingIntent</code>s (one for each message part) that is
@@ -178,8 +178,8 @@
      * @param destinationPort the port to deliver the message to
      * @param data the body of the message to send
      * @param sentIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is sucessfully sent, or failed.
-     *  The result code will be <code>Activity.RESULT_OK<code> for success,
+     *  broadcast when the message is successfully sent, or failed.
+     *  The result code will be <code>Activity.RESULT_OK</code> for success,
      *  or one of these errors:<br>
      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
@@ -188,7 +188,7 @@
      *  the extra "errorCode" containing a radio technology specific value,
      *  generally only useful for troubleshooting.<br>
      *  The per-application based SMS control checks sentIntent. If sentIntent
-     *  is NULL the caller will be checked against all unknown applicaitons,
+     *  is NULL the caller will be checked against all unknown applications,
      *  which cause smaller number of SMS to be sent in checking period.
      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
      *  broadcast when the message is delivered to the recipient.  The
@@ -224,9 +224,6 @@
      * @return the default instance of the SmsManager
      */
     public static SmsManager getDefault() {
-        if (sInstance == null) {
-            sInstance = new SmsManager();
-        }
         return sInstance;
     }
 
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index a284ea5..0746562 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -20,7 +20,6 @@
 import android.util.Log;
 
 import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.EncodeException;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
 import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
@@ -54,6 +53,10 @@
     public static final int ENCODING_7BIT = 1;
     public static final int ENCODING_8BIT = 2;
     public static final int ENCODING_16BIT = 3;
+    /**
+     * @hide This value is not defined in global standard. Only in Korea, this is used.
+     */
+    public static final int ENCODING_KSC5601 = 4;
 
     /** The maximum number of payload bytes per message */
     public static final int MAX_USER_DATA_BYTES = 140;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 27e08d4..1cd3679 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -272,13 +272,17 @@
     public static final int PHONE_TYPE_GSM = Phone.PHONE_TYPE_GSM;
     /** Phone radio is CDMA. */
     public static final int PHONE_TYPE_CDMA = Phone.PHONE_TYPE_CDMA;
+    /** Phone is via SIP. */
+    public static final int PHONE_TYPE_SIP = Phone.PHONE_TYPE_SIP;
 
     /**
-     * Returns a constant indicating the device phone type.
+     * Returns a constant indicating the device phone type.  This
+     * indicates the type of radio used to transmit voice calls.
      *
      * @see #PHONE_TYPE_NONE
      * @see #PHONE_TYPE_GSM
      * @see #PHONE_TYPE_CDMA
+     * @see #PHONE_TYPE_SIP
      */
     public int getPhoneType() {
         try{
@@ -393,11 +397,15 @@
     public static final int NETWORK_TYPE_IDEN = 11;
     /** Current network is EVDO revision B*/
     public static final int NETWORK_TYPE_EVDO_B = 12;
+    /** Current network is LTE */
+    public static final int NETWORK_TYPE_LTE = 13;
+    /** Current network is eHRPD */
+    public static final int NETWORK_TYPE_EHRPD = 14;
 
 
     /**
      * Returns a constant indicating the radio technology (network type)
-     * currently in use on the device.
+     * currently in use on the device for data transmission.
      * @return the network type
      *
      * @see #NETWORK_TYPE_UNKNOWN
@@ -412,6 +420,9 @@
      * @see #NETWORK_TYPE_EVDO_A
      * @see #NETWORK_TYPE_EVDO_B
      * @see #NETWORK_TYPE_1xRTT
+     * @see #NETWORK_TYPE_IDEN
+     * @see #NETWORK_TYPE_LTE
+     * @see #NETWORK_TYPE_EHRPD
      */
     public int getNetworkType() {
         try{
@@ -462,6 +473,10 @@
                 return "CDMA - EvDo rev. B";
             case NETWORK_TYPE_1xRTT:
                 return "CDMA - 1xRTT";
+            case NETWORK_TYPE_LTE:
+                return "LTE";
+            case NETWORK_TYPE_EHRPD:
+                return "CDMA - eHRPD";
             default:
                 return "UNKNOWN";
         }
@@ -912,4 +927,25 @@
             return null;
         }
     }
+
+    /**
+     * @return true if the current device is "voice capable".
+     * <p>
+     * "Voice capable" means that this device supports circuit-switched
+     * (i.e. voice) phone calls over the telephony network, and is allowed
+     * to display the in-call UI while a cellular voice call is active.
+     * This will be false on "data only" devices which can't make voice
+     * calls and don't support any in-call UI.
+     * <p>
+     * Note: the meaning of this flag is subtly different from the
+     * PackageManager.FEATURE_TELEPHONY system feature, which is available
+     * on any device with a telephony radio, even if the device is
+     * data-only.
+     *
+     * @hide pending API review
+     */
+    public boolean isVoiceCapable() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_voice_capable);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index f7506c6..82fcb6a 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -71,6 +71,7 @@
      */
     public String name;
     public String phoneNumber;
+    public String nomalizedNumber;
 
     public String cnapName;
     public int numberPresentation;
@@ -151,6 +152,12 @@
                     info.phoneNumber = cursor.getString(columnIndex);
                 }
 
+                // Look for the normalized number
+                columnIndex = cursor.getColumnIndex(PhoneLookup.NORMALIZED_NUMBER);
+                if (columnIndex != -1) {
+                    info.nomalizedNumber = cursor.getString(columnIndex);
+                }
+
                 // Look for the label/type combo
                 columnIndex = cursor.getColumnIndex(PhoneLookup.LABEL);
                 if (columnIndex != -1) {
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index 79bb832..c47e076 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.database.SQLException;
+import android.location.CountryDetector;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
@@ -240,7 +241,11 @@
 
                     // Use the number entered by the user for display.
                     if (!TextUtils.isEmpty(cw.number)) {
-                        mCallerInfo.phoneNumber = PhoneNumberUtils.formatNumber(cw.number);
+                        CountryDetector detector = (CountryDetector) mQueryContext.getSystemService(
+                                Context.COUNTRY_DETECTOR);
+                        mCallerInfo.phoneNumber = PhoneNumberUtils.formatNumber(cw.number,
+                                mCallerInfo.nomalizedNumber,
+                                detector.detectCountry().getCountryIso());
                     }
                 }
 
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 5de0426..27a4dca 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -27,8 +27,8 @@
  */
 public interface CommandsInterface {
     enum RadioState {
-        RADIO_OFF,         /* Radio explictly powered off (eg CFUN=0) */
-        RADIO_UNAVAILABLE, /* Radio unavailable (eg, resetting or not booted) */
+        RADIO_OFF,         /* Radio explicitly powered off (e.g. CFUN=0) */
+        RADIO_UNAVAILABLE, /* Radio unavailable (e.g. resetting or not booted) */
         SIM_NOT_READY,     /* Radio is on, but the SIM interface is not ready */
         SIM_LOCKED_OR_ABSENT,  /* SIM PIN locked, PUK required, network
                                personalization, or SIM absent */
@@ -121,7 +121,7 @@
     // See 27.007 +CCFC or +CLCK
     static final int SERVICE_CLASS_NONE     = 0; // no user input
     static final int SERVICE_CLASS_VOICE    = (1 << 0);
-    static final int SERVICE_CLASS_DATA     = (1 << 1); //synoym for 16+32+64+128
+    static final int SERVICE_CLASS_DATA     = (1 << 1); //synonym for 16+32+64+128
     static final int SERVICE_CLASS_FAX      = (1 << 2);
     static final int SERVICE_CLASS_SMS      = (1 << 3);
     static final int SERVICE_CLASS_DATA_SYNC = (1 << 4);
@@ -952,19 +952,19 @@
     void writeSmsToRuim(int status, String pdu, Message response);
 
     /**
-     * @deprecated
      * @param apn
      * @param user
      * @param password
      * @param response
      */
+    @Deprecated
     void setupDefaultPDP(String apn, String user, String password, Message response);
 
     /**
-     * @deprecated
      * @param cid
      * @param response
      */
+    @Deprecated
     void deactivateDefaultPDP(int cid, Message response);
 
     void setRadioPower(boolean on, Message response);
@@ -974,7 +974,7 @@
     void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message response);
 
     /**
-     * parameters equivilient to 27.007 AT+CRSM command
+     * parameters equivalent to 27.007 AT+CRSM command
      * response.obj will be an AsyncResult
      * response.obj.userObj will be a IccIoResult on success
      */
@@ -1079,7 +1079,7 @@
 
     /**
      * (AsyncResult)response.obj).result will be an Integer representing
-     * the sum of enabled serivice classes (sum of SERVICE_CLASS_*)
+     * the sum of enabled service classes (sum of SERVICE_CLASS_*)
      *
      * @param facility one of CB_FACILTY_*
      * @param password password or "" if not required
@@ -1152,7 +1152,7 @@
 
     /**
      * Request to enable/disable network state change notifications when
-     * location informateion (lac and/or cid) has changed.
+     * location information (lac and/or cid) has changed.
      *
      * @param enable true to enable, false to disable
      * @param response callback message
@@ -1183,7 +1183,7 @@
 
     /**
      * Indicates to the vendor ril that StkService is running
-     * rand is eady to receive RIL_UNSOL_STK_XXXX commands.
+     * and is ready to receive RIL_UNSOL_STK_XXXX commands.
      *
      * @param result callback message
      */
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 6634017..c3ad9e6 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -21,11 +21,20 @@
 import com.android.internal.util.HierarchicalState;
 import com.android.internal.util.HierarchicalStateMachine;
 
+import android.net.LinkAddress;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
 import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemProperties;
 import android.util.EventLog;
 
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
 /**
  * {@hide}
  *
@@ -60,7 +69,7 @@
  *        EVENT_GET_LAST_FAIL_DONE,
  *        EVENT_DEACTIVATE_DONE.
  *     }
- *   ++ # mInactiveState 
+ *   ++ # mInactiveState
  *        e(doNotifications)
  *        x(clearNotifications) {
  *            EVENT_RESET { notifiyDisconnectCompleted }.
@@ -252,13 +261,13 @@
     protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100;
 
     //***** Member Variables
+    protected int mId;
     protected int mTag;
     protected PhoneBase phone;
+    protected RetryManager mRetryMgr;
     protected int cid;
-    protected String interfaceName;
-    protected String ipAddress;
-    protected String gatewayAddress;
-    protected String[] dnsServers;
+    protected LinkProperties mLinkProperties = new LinkProperties();
+    protected LinkCapabilities mCapabilities = new LinkCapabilities();
     protected long createTime;
     protected long lastFailTime;
     protected FailCause lastFailCause;
@@ -278,13 +287,12 @@
 
 
    //***** Constructor
-    protected DataConnection(PhoneBase phone, String name) {
+    protected DataConnection(PhoneBase phone, String name, RetryManager rm) {
         super(name);
         if (DBG) log("DataConnection constructor E");
         this.phone = phone;
+        mRetryMgr = rm;
         this.cid = -1;
-        this.dnsServers = new String[2];
-
         clearSettings();
 
         setDbg(false);
@@ -353,8 +361,8 @@
 
         if (dp.onCompletedMsg != null) {
             Message msg = dp.onCompletedMsg;
-            log(String.format("msg.what=%d msg.obj=%s",
-                    msg.what, ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
+            log(String.format("msg=%s msg.obj=%s", msg.toString(),
+                    ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
             AsyncResult.forMessage(msg);
             msg.sendToTarget();
         }
@@ -367,6 +375,10 @@
         clearSettings();
     }
 
+    public RetryManager getRetryMgr() {
+        return mRetryMgr;
+    }
+
     /**
      * Clear all settings called when entering mInactiveState.
      */
@@ -377,11 +389,7 @@
         this.lastFailTime = -1;
         this.lastFailCause = FailCause.NONE;
 
-        interfaceName = null;
-        ipAddress = null;
-        gatewayAddress = null;
-        dnsServers[0] = null;
-        dnsServers[1] = null;
+        mLinkProperties = new LinkProperties();
     }
 
     /**
@@ -416,35 +424,67 @@
 //            for (int i = 0; i < response.length; i++) {
 //                log("  response[" + i + "]='" + response[i] + "'");
 //            }
+
+            // Start with clean network properties and if we have
+            // a failure we'll clear again at the bottom of this code.
+            LinkProperties linkProperties = new LinkProperties();
             if (response.length >= 2) {
                 cid = Integer.parseInt(response[0]);
-                interfaceName = response[1];
-                if (response.length > 2) {
-                    ipAddress = response[2];
+                String interfaceName = response[1];
+                result = SetupResult.SUCCESS;
+
+                try {
                     String prefix = "net." + interfaceName + ".";
-                    gatewayAddress = SystemProperties.get(prefix + "gw");
+
+                    NetworkInterface networkInterface = NetworkInterface.getByName(interfaceName);
+                    linkProperties.setInterfaceName(interfaceName);
+
+                    // TODO: Get gateway and dns via RIL interface not property?
+                    String gatewayAddress = SystemProperties.get(prefix + "gw");
+                    linkProperties.setGateway(InetAddress.getByName(gatewayAddress));
+
+                    for (InterfaceAddress addr : networkInterface.getInterfaceAddresses()) {
+                        linkProperties.addLinkAddress(new LinkAddress(addr));
+                    }
+                    // TODO: Get gateway and dns via RIL interface not property?
+                    String dnsServers[] = new String[2];
                     dnsServers[0] = SystemProperties.get(prefix + "dns1");
                     dnsServers[1] = SystemProperties.get(prefix + "dns2");
-                    if (DBG) {
-                        log("interface=" + interfaceName + " ipAddress=" + ipAddress
-                            + " gateway=" + gatewayAddress + " DNS1=" + dnsServers[0]
-                            + " DNS2=" + dnsServers[1]);
-                    }
-
                     if (isDnsOk(dnsServers)) {
-                        result = SetupResult.SUCCESS;
+                        linkProperties.addDns(InetAddress.getByName(dnsServers[0]));
+                        linkProperties.addDns(InetAddress.getByName(dnsServers[1]));
                     } else {
                         result = SetupResult.ERR_BadDns;
                     }
-                } else {
-                    result = SetupResult.SUCCESS;
+                } catch (UnknownHostException e1) {
+                    log("onSetupCompleted: UnknowHostException " + e1);
+                    e1.printStackTrace();
+                    result = SetupResult.ERR_Other;
+                } catch (SocketException e2) {
+                    log("onSetupCompleted: SocketException " + e2);
+                    e2.printStackTrace();
+                    result = SetupResult.ERR_Other;
                 }
             } else {
+                log("onSetupCompleted: error; expected number of responses >= 2 was " +
+                        response.length);
                 result = SetupResult.ERR_Other;
             }
+
+            // An error occurred so clear properties
+            if (result != SetupResult.SUCCESS) {
+                log("onSetupCompleted with an error clearing LinkProperties");
+                linkProperties.clear();
+            }
+            mLinkProperties = linkProperties;
         }
 
-        if (DBG) log("DataConnection setup result='" + result + "' on cid=" + cid);
+        if (DBG) {
+            log("DataConnection setup result='" + result + "' on cid=" + cid);
+            if (result == SetupResult.SUCCESS) {
+                log("LinkProperties: " + mLinkProperties.toString());
+            }
+        }
         return result;
     }
 
@@ -606,7 +646,16 @@
                             break;
                         case ERR_BadDns:
                             // Connection succeeded but DNS info is bad so disconnect
-                            EventLog.writeEvent(EventLogTags.PDP_BAD_DNS_ADDRESS, dnsServers[0]);
+                            StringBuilder dnsAddressesSb = new StringBuilder();
+                            for (InetAddress addr : mLinkProperties.getDnses()) {
+                                if (dnsAddressesSb.length() != 0) dnsAddressesSb.append(" ");
+                                dnsAddressesSb.append(addr.toString());
+                            }
+                            if (dnsAddressesSb.length() == 0) {
+                                dnsAddressesSb.append("no-dns-addresses");
+                            }
+                            EventLog.writeEvent(EventLogTags.PDP_BAD_DNS_ADDRESS,
+                                    dnsAddressesSb.toString());
                             tearDownData(cp);
                             transitionTo(mDisconnectingBadDnsState);
                             break;
@@ -815,13 +864,13 @@
 
     /**
      * Connect to the apn and return an AsyncResult in onCompletedMsg.
-     * Used for cellular networks that use Acess Point Names (APN) such
+     * Used for cellular networks that use Acesss Point Names (APN) such
      * as GSM networks.
      *
      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
      *        With AsyncResult.userObj set to the original msg.obj,
      *        AsyncResult.result = FailCause and AsyncResult.exception = Exception().
-     * @param apn is the Acces Point Name to connect to
+     * @param apn is the Access Point Name to connect to
      */
     public void connect(Message onCompletedMsg, ApnSetting apn) {
         sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg)));
@@ -873,31 +922,29 @@
     }
 
     /**
-     * @return the interface name as a string.
+     * Get the DataConnection ID
      */
-    public String getInterface() {
-        return interfaceName;
+    public int getDataConnectionId() {
+        return mId;
     }
 
     /**
-     * @return the ip address as a string.
+     * Return the LinkProperties for the connection.
+     *
+     * @return a copy of the LinkProperties, is never null.
      */
-    public String getIpAddress() {
-        return ipAddress;
+    public LinkProperties getLinkProperties() {
+        return new LinkProperties(mLinkProperties);
     }
 
     /**
-     * @return the gateway address as a string.
+     * A capability is an Integer/String pair, the capabilities
+     * are defined in the class LinkSocket#Key.
+     *
+     * @return a copy of this connections capabilities, may be empty but never null.
      */
-    public String getGatewayAddress() {
-        return gatewayAddress;
-    }
-
-    /**
-     * @return an array of associated DNS addresses.
-     */
-    public String[] getDnsServers() {
-        return dnsServers;
+    public LinkCapabilities getLinkCapabilities() {
+        return new LinkCapabilities(mCapabilities);
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 265bf7e..f8812ec 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -16,25 +16,38 @@
 
 package com.android.internal.telephony;
 
+import com.android.internal.telephony.cdma.CDMAPhone;
+
 import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.net.IConnectivityManager;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
-import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * {@hide}
- *
  */
 public abstract class DataConnectionTracker extends Handler {
-    protected static final boolean DBG = false;
-    protected final String LOG_TAG = "DataConnectionTracker";
+    protected static final boolean DBG = true;
 
     /**
      * IDLE: ready to start data connection setup, default state
@@ -119,9 +132,10 @@
     protected boolean mMasterDataEnabled = true;
 
     protected boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
+
     protected int enabledCount = 0;
 
-    /* Currently requested APN type */
+    /* Currently requested APN type (TODO: This should probably be a parameter not a member) */
     protected String mRequestedApnType = Phone.APN_TYPE_DEFAULT;
 
     /** Retry configuration: A doubling of retry times from 5secs to 30minutes */
@@ -163,20 +177,27 @@
     // represents an invalid IP address
     protected static final String NULL_IP = "0.0.0.0";
 
+    // TODO: See if we can remove INTENT_RECONNECT_ALARM
+    //       having to have different values for GSM and
+    //       CDMA. If so we can then remove the need for
+    //       getActionIntentReconnectAlarm.
+    protected static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason";
 
     // member variables
-    protected PhoneBase phone;
-    protected Activity activity = Activity.NONE;
-    protected State state = State.IDLE;
+    protected PhoneBase mPhone;
+    protected Activity mActivity = Activity.NONE;
+    protected State mState = State.IDLE;
     protected Handler mDataConnectionTracker = null;
 
 
-    protected long txPkts, rxPkts, sentSinceLastRecv;
-    protected int netStatPollPeriod;
+    protected long mTxPkts;
+    protected long mRxPkts;
+    protected long mSentSinceLastRecv;
+    protected int mNetStatPollPeriod;
     protected int mNoRecvPollCount = 0;
-    protected boolean netStatPollEnabled = false;
+    protected boolean mNetStatPollEnabled = false;
 
-    /** Manage the behavior of data retry after failure */
+    /** Manage the behavior of data retry after failure (TODO: One per connection in the future?) */
     protected RetryManager mRetryMgr = new RetryManager();
 
     // wifi connection status will be updated by sticky intent
@@ -186,28 +207,125 @@
     protected PendingIntent mReconnectIntent = null;
 
     /** CID of active data connection */
-    protected int cidActive;
+    protected int mCidActive;
 
-   /**
+    /** indication of our availability (preconditions to trysetupData are met) **/
+    protected boolean mAvailability = false;
+
+    // When false we will not auto attach and manully attaching is required.
+    protected boolean mAutoAttachOnCreation = false;
+
+    // State of screen
+    // (TODO: Reconsider tying directly to screen, maybe this is
+    //        really a lower power mode")
+    protected boolean mIsScreenOn = true;
+
+    /** The link properties (dns, gateway, ip, etc) */
+    protected LinkProperties mLinkProperties = new LinkProperties();
+
+    /** The link capabilities */
+    protected LinkCapabilities mLinkCapabilities = new LinkCapabilities();
+
+    /** Allows the generation of unique Id's for DataConnection objects */
+    protected AtomicInteger mUniqueIdGenerator = new AtomicInteger(0);
+
+    /** The data connections. */
+    protected HashMap<Integer, DataConnection> mDataConnections =
+        new HashMap<Integer, DataConnection>();
+
+    protected BroadcastReceiver mIntentReceiver = new BroadcastReceiver ()
+    {
+        @Override
+        public void onReceive(Context context, Intent intent)
+        {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_SCREEN_ON)) {
+                mIsScreenOn = true;
+                stopNetStatPoll();
+                startNetStatPoll();
+            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+                mIsScreenOn = false;
+                stopNetStatPoll();
+                startNetStatPoll();
+            } else if (action.equals(getActionIntentReconnectAlarm())) {
+                log("Reconnect alarm. Previous state was " + mState);
+
+                String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
+                if (mState == State.FAILED) {
+                    Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
+                    msg.arg1 = 0; // tearDown is false
+                    msg.obj = reason;
+                    sendMessage(msg);
+                }
+                sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
+            } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+                final android.net.NetworkInfo networkInfo = (NetworkInfo)
+                        intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+                mIsWifiConnected = (networkInfo != null && networkInfo.isConnected());
+            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+                final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                        WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
+
+                if (!enabled) {
+                    // when WiFi got disabled, the NETWORK_STATE_CHANGED_ACTION
+                    // quit and won't report disconnected until next enabling.
+                    mIsWifiConnected = false;
+                }
+            }
+        }
+    };
+
+    /**
      * Default constructor
      */
     protected DataConnectionTracker(PhoneBase phone) {
         super();
-        this.phone = phone;
+        mPhone = phone;
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(getActionIntentReconnectAlarm());
+        filter.addAction(Intent.ACTION_SCREEN_ON);
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+
+        // TODO: Why is this registering the phone as the receiver of the intent
+        //       and not its own handler?
+        mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
+
+        // This preference tells us 1) initial condition for "dataEnabled",
+        // and 2) whether the RIL will setup the baseband to auto-PS attach.
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
+        boolean dataEnabledSetting = true;
+        try {
+            dataEnabledSetting = IConnectivityManager.Stub.asInterface(ServiceManager.
+                    getService(Context.CONNECTIVITY_SERVICE)).getMobileDataEnabled();
+        } catch (Exception e) {
+            // nothing to do - use the old behavior and leave data on
+        }
+        dataEnabled[APN_DEFAULT_ID] =
+                !sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false) &&
+                dataEnabledSetting;
+        if (dataEnabled[APN_DEFAULT_ID]) {
+            enabledCount++;
+        }
+        mAutoAttachOnCreation = dataEnabled[APN_DEFAULT_ID];
     }
 
-    public abstract void dispose();
+    public void dispose() {
+        mPhone.getContext().unregisterReceiver(this.mIntentReceiver);
+    }
 
     public Activity getActivity() {
-        return activity;
+        return mActivity;
     }
 
     public State getState() {
-        return state;
+        return mState;
     }
 
     public String getStateInString() {
-        switch (state) {
+        switch (mState) {
             case IDLE:          return "IDLE";
             case INITING:       return "INIT";
             case CONNECTING:    return "CING";
@@ -220,6 +338,14 @@
     }
 
     /**
+     * @return the data connections
+     */
+    public ArrayList<DataConnection> getAllDataConnections() {
+        /** TODO: change return type to Collection? */
+        return new ArrayList<DataConnection>(mDataConnections.values());
+    }
+
+    /**
      * The data connection is expected to be setup while device
      *  1. has Icc card
      *  2. registered for data service
@@ -235,9 +361,9 @@
     //  the shared values.  If it is not, then update it.
     public void setDataOnRoamingEnabled(boolean enabled) {
         if (getDataOnRoamingEnabled() != enabled) {
-            Settings.Secure.putInt(phone.getContext().getContentResolver(),
+            Settings.Secure.putInt(mPhone.getContext().getContentResolver(),
                 Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
-            if (phone.getServiceState().getRoaming()) {
+            if (mPhone.getServiceState().getRoaming()) {
                 if (enabled) {
                     mRetryMgr.resetRetryCount();
                 }
@@ -246,16 +372,19 @@
         }
     }
 
-    //Retrieve the data roaming setting from the shared preferences.
+    // Retrieve the data roaming setting from the shared preferences.
     public boolean getDataOnRoamingEnabled() {
         try {
-            return Settings.Secure.getInt(phone.getContext().getContentResolver(),
-                Settings.Secure.DATA_ROAMING) > 0;
+            return Settings.Secure.getInt(
+                    mPhone.getContext().getContentResolver(), Settings.Secure.DATA_ROAMING) > 0;
         } catch (SettingNotFoundException snfe) {
             return false;
         }
     }
 
+
+    protected abstract String getActionIntentReconnectAlarm();
+
     // abstract handler methods
     protected abstract boolean onTrySetupData(String reason);
     protected abstract void onRoamingOff();
@@ -263,14 +392,14 @@
     protected abstract void onRadioAvailable();
     protected abstract void onRadioOffOrNotAvailable();
     protected abstract void onDataSetupComplete(AsyncResult ar);
-    protected abstract void onDisconnectDone(AsyncResult ar);
+    protected abstract void onDisconnectDone(int connId, AsyncResult ar);
     protected abstract void onResetDone(AsyncResult ar);
     protected abstract void onVoiceCallStarted();
     protected abstract void onVoiceCallEnded();
     protected abstract void onCleanUpConnection(boolean tearDown, String reason);
 
     @Override
-    public void handleMessage (Message msg) {
+    public void handleMessage(Message msg) {
         switch (msg.what) {
 
             case EVENT_ENABLE_NEW_APN:
@@ -280,7 +409,7 @@
             case EVENT_TRY_SETUP_DATA:
                 String reason = null;
                 if (msg.obj instanceof String) {
-                    reason = (String)msg.obj;
+                    reason = (String) msg.obj;
                 }
                 onTrySetupData(reason);
                 break;
@@ -305,12 +434,13 @@
                 break;
 
             case EVENT_DATA_SETUP_COMPLETE:
-                cidActive = msg.arg1;
+                mCidActive = msg.arg1;
                 onDataSetupComplete((AsyncResult) msg.obj);
                 break;
 
             case EVENT_DISCONNECT_DONE:
-                onDisconnectDone((AsyncResult) msg.obj);
+                log("DataConnectoinTracker.handleMessage: EVENT_DISCONNECT_DONE msg=" + msg);
+                onDisconnectDone(msg.arg1, (AsyncResult) msg.obj);
                 break;
 
             case EVENT_VOICE_CALL_STARTED:
@@ -323,7 +453,7 @@
 
             case EVENT_CLEAN_UP_CONNECTION:
                 boolean tearDown = (msg.arg1 == 0) ? false : true;
-                onCleanUpConnection(tearDown, (String)msg.obj);
+                onCleanUpConnection(tearDown, (String) msg.obj);
                 break;
 
             case EVENT_SET_MASTER_DATA_ENABLE:
@@ -343,8 +473,9 @@
 
     /**
      * Report the current state of data connectivity (enabled or disabled)
+     *
      * @return {@code false} if data connectivity has been explicitly disabled,
-     * {@code true} otherwise.
+     *         {@code true} otherwise.
      */
     public synchronized boolean getDataEnabled() {
         return dataEnabled[APN_DEFAULT_ID];
@@ -352,8 +483,9 @@
 
     /**
      * Report on whether data connectivity is enabled
+     *
      * @return {@code false} if data connectivity has been explicitly disabled,
-     * {@code true} otherwise.
+     *         {@code true} otherwise.
      */
     public boolean getAnyDataEnabled() {
         return (enabledCount != 0);
@@ -367,6 +499,8 @@
 
     protected abstract void log(String s);
 
+    protected abstract void loge(String s);
+
     protected int apnTypeToId(String type) {
         if (TextUtils.equals(type, Phone.APN_TYPE_DEFAULT)) {
             return APN_DEFAULT_ID;
@@ -396,7 +530,7 @@
         case APN_HIPRI_ID:
             return Phone.APN_TYPE_HIPRI;
         default:
-            Log.e(LOG_TAG, "Unknown id (" + id + ") in apnIdToType");
+            log("Unknown id (" + id + ") in apnIdToType");
             return Phone.APN_TYPE_DEFAULT;
         }
     }
@@ -409,19 +543,126 @@
 
     protected abstract String getActiveApnString();
 
-    public abstract ArrayList<DataConnection> getAllDataConnections();
-
-    protected abstract String getInterfaceName(String apnType);
-
-    protected abstract String getIpAddress(String apnType);
-
-    protected abstract String getGateway(String apnType);
-
-    protected abstract String[] getDnsServers(String apnType);
-
     protected abstract void setState(State s);
 
-    protected synchronized boolean isEnabled(int id) {
+    protected LinkProperties getLinkProperties(String apnType) {
+        int id = apnTypeToId(apnType);
+        if (isApnIdEnabled(id)) {
+            return new LinkProperties(mLinkProperties);
+        } else {
+            return new LinkProperties();
+        }
+    }
+
+    protected LinkCapabilities getLinkCapabilities(String apnType) {
+        int id = apnTypeToId(apnType);
+        if (isApnIdEnabled(id)) {
+            return new LinkCapabilities(mLinkCapabilities);
+        } else {
+            return new LinkCapabilities();
+        }
+    }
+
+    /**
+     * Return the LinkProperties for the connection.
+     *
+     * @param connection
+     * @return a copy of the LinkProperties, is never null.
+     */
+    protected LinkProperties getLinkProperties(DataConnection connection) {
+        return connection.getLinkProperties();
+    }
+
+    /**
+     * A capability is an Integer/String pair, the capabilities
+     * are defined in the class LinkSocket#Key.
+     *
+     * @param connection
+     * @return a copy of this connections capabilities, may be empty but never null.
+     */
+    protected LinkCapabilities getLinkCapabilities(DataConnection connection) {
+        return connection.getLinkCapabilities();
+    }
+
+    // tell all active apns of the current condition
+    protected void notifyDataConnection(String reason) {
+        for (int id = 0; id < APN_NUM_TYPES; id++) {
+            if (dataEnabled[id]) {
+                mPhone.notifyDataConnection(reason, apnIdToType(id));
+            }
+        }
+        notifyDataAvailability(reason);
+    }
+
+    // a new APN has gone active and needs to send events to catch up with the
+    // current condition
+    private void notifyApnIdUpToCurrent(String reason, int apnId) {
+        switch (mState) {
+            case IDLE:
+            case INITING:
+                break;
+            case CONNECTING:
+            case SCANNING:
+                mPhone.notifyDataConnection(reason, apnIdToType(apnId), Phone.DataState.CONNECTING);
+                break;
+            case CONNECTED:
+            case DISCONNECTING:
+                mPhone.notifyDataConnection(reason, apnIdToType(apnId), Phone.DataState.CONNECTING);
+                mPhone.notifyDataConnection(reason, apnIdToType(apnId), Phone.DataState.CONNECTED);
+                break;
+        }
+    }
+
+    // since we normally don't send info to a disconnected APN, we need to do this specially
+    private void notifyApnIdDisconnected(String reason, int apnId) {
+        mPhone.notifyDataConnection(reason, apnIdToType(apnId), Phone.DataState.DISCONNECTED);
+    }
+
+    // disabled apn's still need avail/unavail notificiations - send them out
+    protected void notifyOffApnsOfAvailability(String reason, boolean availability) {
+        if (mAvailability == availability) return;
+        mAvailability = availability;
+        for (int id = 0; id < APN_NUM_TYPES; id++) {
+            if (!isApnIdEnabled(id)) {
+                notifyApnIdDisconnected(reason, id);
+            }
+        }
+    }
+
+    // we had an availability change - tell the listeners
+    protected void notifyDataAvailability(String reason) {
+        // note that we either just turned all off because we lost availability
+        // or all were off and could now go on, so only have off apns to worry about
+        notifyOffApnsOfAvailability(reason, isDataPossible());
+    }
+
+    /**
+     * The only circumstances under which we report that data connectivity is not
+     * possible are
+     * <ul>
+     * <li>Data is disallowed (roaming, power state, voice call, etc).</li>
+     * <li>The current data state is {@code DISCONNECTED} for a reason other than
+     * having explicitly disabled connectivity. In other words, data is not available
+     * because the phone is out of coverage or some like reason.</li>
+     * </ul>
+     * @return {@code true} if data connectivity is possible, {@code false} otherwise.
+     */
+    protected boolean isDataPossible() {
+        boolean possible = (isDataAllowed()
+                && !(getDataEnabled() && (mState == State.FAILED || mState == State.IDLE)));
+        if (!possible && DBG && isDataAllowed()) {
+            log("Data not possible.  No coverage: dataState = " + mState);
+        }
+        return possible;
+    }
+
+    protected abstract boolean isDataAllowed();
+
+    public boolean isApnTypeEnabled(String apnType) {
+        return isApnIdEnabled(apnTypeToId(apnType));
+    }
+
+    protected synchronized boolean isApnIdEnabled(int id) {
         if (id != APN_INVALID_ID) {
             return dataEnabled[id];
         }
@@ -430,13 +671,13 @@
 
     /**
      * Ensure that we are connected to an APN of the specified type.
-     * @param type the APN type (currently the only valid values
-     * are {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL})
-     * @return the result of the operation. Success is indicated by
-     * a return value of either {@code Phone.APN_ALREADY_ACTIVE} or
-     * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a broadcast
-     * will be sent by the ConnectivityManager when a connection to
-     * the APN has been established.
+     *
+     * @param type the APN type (currently the only valid values are
+     *            {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL})
+     * @return Success is indicated by {@code Phone.APN_ALREADY_ACTIVE} or
+     *         {@code Phone.APN_REQUEST_STARTED}. In the latter case, a
+     *         broadcast will be sent by the ConnectivityManager when a
+     *         connection to the APN has been established.
      */
     public synchronized int enableApnType(String type) {
         int id = apnTypeToId(type);
@@ -444,41 +685,45 @@
             return Phone.APN_REQUEST_FAILED;
         }
 
-        if (DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = "
-                + isApnTypeActive(type) + " and state = " + state);
+        if (DBG) {
+            log("enableApnType(" + type + "), isApnTypeActive = " + isApnTypeActive(type)
+                    + ", isApnIdEnabled =" + isApnIdEnabled(id) + " and state = " + mState);
+        }
 
         if (!isApnTypeAvailable(type)) {
-            if (DBG) Log.d(LOG_TAG, "type not available");
+            if (DBG) log("type not available");
             return Phone.APN_TYPE_NOT_AVAILABLE;
         }
 
-        // just because it's active doesn't mean we had it explicitly requested before
-        // (a broad default may handle many types).  make sure we mark it enabled
-        // so if the default is disabled we keep the connection for others
-        setEnabled(id, true);
-
-        if (isApnTypeActive(type)) {
-            if (state == State.INITING) return Phone.APN_REQUEST_STARTED;
-            else if (state == State.CONNECTED) return Phone.APN_ALREADY_ACTIVE;
+        if (isApnIdEnabled(id)) {
+            return Phone.APN_ALREADY_ACTIVE;
+        } else {
+            setEnabled(id, true);
         }
         return Phone.APN_REQUEST_STARTED;
     }
 
     /**
-     * The APN of the specified type is no longer needed. Ensure that if
-     * use of the default APN has not been explicitly disabled, we are connected
-     * to the default APN.
+     * The APN of the specified type is no longer needed. Ensure that if use of
+     * the default APN has not been explicitly disabled, we are connected to the
+     * default APN.
+     *
      * @param type the APN type. The only valid values are currently
-     * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}.
-     * @return
+     *            {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}.
+     * @return Success is indicated by {@code Phone.APN_ALREADY_ACTIVE} or
+     *         {@code Phone.APN_REQUEST_STARTED}. In the latter case, a
+     *         broadcast will be sent by the ConnectivityManager when a
+     *         connection to the APN has been disconnected. A {@code
+     *         Phone.APN_REQUEST_FAILED} is returned if the type parameter is
+     *         invalid or if the apn wasn't enabled.
      */
     public synchronized int disableApnType(String type) {
-        if (DBG) Log.d(LOG_TAG, "disableApnType("+type+")");
+        if (DBG) log("disableApnType(" + type + ")");
         int id = apnTypeToId(type);
         if (id == APN_INVALID_ID) {
             return Phone.APN_REQUEST_FAILED;
         }
-        if (isEnabled(id)) {
+        if (isApnIdEnabled(id)) {
             setEnabled(id, false);
             if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
                 if (dataEnabled[APN_DEFAULT_ID]) {
@@ -495,9 +740,10 @@
     }
 
     private void setEnabled(int id, boolean enable) {
-        if (DBG) Log.d(LOG_TAG, "setEnabled(" + id + ", " + enable + ") with old state = " +
-                dataEnabled[id] + " and enabledCount = " + enabledCount);
-
+        if (DBG) {
+            log("setEnabled(" + id + ", " + enable + ") with old state = " + dataEnabled[id]
+                    + " and enabledCount = " + enabledCount);
+        }
         Message msg = obtainMessage(EVENT_ENABLE_NEW_APN);
         msg.arg1 = id;
         msg.arg2 = (enable ? ENABLED : DISABLED);
@@ -506,10 +752,10 @@
 
     protected synchronized void onEnableApn(int apnId, int enabled) {
         if (DBG) {
-            Log.d(LOG_TAG, "EVENT_APN_ENABLE_REQUEST " + apnId + ", " + enabled);
-            Log.d(LOG_TAG, " dataEnabled = " + dataEnabled[apnId] +
-                    ", enabledCount = " + enabledCount +
-                    ", isApnTypeActive = " + isApnTypeActive(apnIdToType(apnId)));
+            log("EVENT_APN_ENABLE_REQUEST apnId=" + apnId + ", apnType=" + apnIdToType(apnId) +
+                    ", enabled=" + enabled + ", dataEnabled = " + dataEnabled[apnId] +
+                    ", enabledCount = " + enabledCount + ", isApnTypeActive = " +
+                    isApnTypeActive(apnIdToType(apnId)));
         }
         if (enabled == ENABLED) {
             if (!dataEnabled[apnId]) {
@@ -520,6 +766,8 @@
             if (!isApnTypeActive(type)) {
                 mRequestedApnType = type;
                 onEnableNewApn();
+            } else {
+                notifyApnIdUpToCurrent(Phone.REASON_APN_SWITCHED, apnId);
             }
         } else {
             // disable
@@ -528,8 +776,17 @@
                 enabledCount--;
                 if (enabledCount == 0) {
                     onCleanUpConnection(true, Phone.REASON_DATA_DISABLED);
-                } else if (dataEnabled[APN_DEFAULT_ID] == true &&
-                        !isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
+                }
+
+                // send the disconnect msg manually, since the normal route wont send
+                // it (it's not enabled)
+                notifyApnIdDisconnected(Phone.REASON_DATA_DISABLED, apnId);
+                if (dataEnabled[APN_DEFAULT_ID] == true
+                        && !isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
+                    // TODO - this is an ugly way to restore the default conn - should be done
+                    // by a real contention manager and policy that disconnects the lower pri
+                    // stuff as enable requests come in and pops them back on as we disable back
+                    // down to the lower pri stuff
                     mRequestedApnType = Phone.APN_TYPE_DEFAULT;
                     onEnableNewApn();
                 }
@@ -547,18 +804,22 @@
     }
 
     /**
-     * Prevent mobile data connections from being established,
-     * or once again allow mobile data connections. If the state
-     * toggles, then either tear down or set up data, as
-     * appropriate to match the new state.
-     * <p>This operation only affects the default APN, and if the same APN is
-     * currently being used for MMS traffic, the teardown will not happen
-     * even when {@code enable} is {@code false}.</p>
-     * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data
+     * Prevent mobile data connections from being established, or once again
+     * allow mobile data connections. If the state toggles, then either tear
+     * down or set up data, as appropriate to match the new state.
+     * <p>
+     * This operation only affects the default APN, and if the same APN is
+     * currently being used for MMS traffic, the teardown will not happen even
+     * when {@code enable} is {@code false}.
+     * </p>
+     *
+     * @param enable indicates whether to enable ({@code true}) or disable (
+     *            {@code false}) data
      * @return {@code true} if the operation succeeded
      */
     public boolean setDataEnabled(boolean enable) {
-        if (DBG) Log.d(LOG_TAG, "setDataEnabled(" + enable + ")");
+        if (DBG)
+            log("setDataEnabled(" + enable + ")");
 
         Message msg = obtainMessage(EVENT_SET_MASTER_DATA_ENABLE);
         msg.arg1 = (enable ? ENABLED : DISABLED);
@@ -574,9 +835,7 @@
                 onTrySetupData(Phone.REASON_DATA_ENABLED);
             } else {
                 onCleanUpConnection(true, Phone.REASON_DATA_DISABLED);
-           }
+            }
         }
     }
-
-
 }
diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 057ba0a..14808b6 100644
--- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -92,26 +94,45 @@
         }
     }
 
-    public void notifyDataConnection(Phone sender, String reason) {
+    public void notifyDataConnection(Phone sender, String reason, String apnType) {
+        doNotifyDataConnection(sender, reason, apnType, sender.getDataConnectionState(apnType));
+    }
+
+    public void notifyDataConnection(Phone sender, String reason, String apnType,
+            Phone.DataState state) {
+        doNotifyDataConnection(sender, reason, apnType, state);
+    }
+
+    private void doNotifyDataConnection(Phone sender, String reason, String apnType,
+            Phone.DataState state) {
+        // TODO
+        // use apnType as the key to which connection we're talking about.
+        // pass apnType back up to fetch particular for this one.
         TelephonyManager telephony = TelephonyManager.getDefault();
+        LinkProperties linkProperties = null;
+        LinkCapabilities linkCapabilities = null;
+        if (state == Phone.DataState.CONNECTED) {
+            linkProperties = sender.getLinkProperties(apnType);
+            linkCapabilities = sender.getLinkCapabilities(apnType);
+        }
         try {
             mRegistry.notifyDataConnection(
-                    convertDataState(sender.getDataConnectionState()),
+                    convertDataState(state),
                     sender.isDataConnectivityPossible(), reason,
                     sender.getActiveApn(),
-                    sender.getActiveApnTypes(),
-                    sender.getInterfaceName(null),
+                    apnType,
+                    linkProperties,
+                    linkCapabilities,
                     ((telephony!=null) ? telephony.getNetworkType() :
-                    TelephonyManager.NETWORK_TYPE_UNKNOWN),
-                    sender.getGateway(null));
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN));
         } catch (RemoteException ex) {
             // system process is dead
         }
     }
 
-    public void notifyDataConnectionFailed(Phone sender, String reason) {
+    public void notifyDataConnectionFailed(Phone sender, String reason, String apnType) {
         try {
-            mRegistry.notifyDataConnectionFailed(reason);
+            mRegistry.notifyDataConnectionFailed(reason, apnType);
         } catch (RemoteException ex) {
             // system process is dead
         }
@@ -127,6 +148,14 @@
         }
     }
 
+    public void notifyOtaspChanged(Phone sender, int otaspMode) {
+        try {
+            mRegistry.notifyOtaspChanged(otaspMode);
+        } catch (RemoteException ex) {
+            // system process is dead
+        }
+    }
+
     private void log(String s) {
         Log.d(LOG_TAG, "[PhoneNotifier] " + s);
     }
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index 30ee77c..e42827f 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -16,11 +16,14 @@
 
 package com.android.internal.telephony;
 
-import android.telephony.SmsMessage;
+import android.text.TextUtils;
 import android.util.SparseIntArray;
 
 import android.util.Log;
 
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
 /**
  * This class implements the character set mapping between
  * the GSM SMS 7-bit alphabet specified in TS 23.038 6.2.1
@@ -320,7 +323,7 @@
 
                 gsmVal = (0x7f & (pdu[offset + byteOffset] >> shift));
 
-                // if it crosses a byte boundry
+                // if it crosses a byte boundary
                 if (shift > 1) {
                     // set msb bits to 0
                     gsmVal &= 0x7f >> (shift - 1);
@@ -355,6 +358,32 @@
      */
     public static String
     gsm8BitUnpackedToString(byte[] data, int offset, int length) {
+        return gsm8BitUnpackedToString(data, offset, length, "");
+    }
+
+    /**
+     * Convert a GSM alphabet string that's stored in 8-bit unpacked
+     * format (as it often appears in SIM records) into a String
+     *
+     * Field may be padded with trailing 0xff's. The decode stops
+     * at the first 0xff encountered.
+     *
+     * Additionally, in some country(ex. Korea), there are non-ASCII or MBCS characters.
+     * If a character set is given, characters in data are treat as MBCS.
+     */
+    public static String
+    gsm8BitUnpackedToString(byte[] data, int offset, int length, String characterset) {
+        boolean isMbcs = false;
+        Charset charset = null;
+        ByteBuffer mbcsBuffer = null;
+
+        if (!TextUtils.isEmpty(characterset)
+                && !characterset.equalsIgnoreCase("us-ascii")
+                && Charset.isSupported(characterset)) {
+            isMbcs = true;
+            charset = Charset.forName(characterset);
+            mbcsBuffer = ByteBuffer.allocate(2);
+        }
         boolean prevWasEscape;
         StringBuilder ret = new StringBuilder(length);
 
@@ -380,7 +409,15 @@
                 if (prevWasEscape) {
                     ret.append((char)gsmExtendedToChar.get(c, ' '));
                 } else {
-                    ret.append((char)gsmToChar.get(c, ' '));
+                    if (!isMbcs || c < 0x80 || i + 1 >= offset + length) {
+                        ret.append((char)gsmToChar.get(c, ' '));
+                    } else {
+                        // isMbcs must be true. So both mbcsBuffer and charset are initialized.
+                        mbcsBuffer.clear();
+                        mbcsBuffer.put(data, i++, 2);
+                        mbcsBuffer.flip();
+                        ret.append(charset.decode(mbcsBuffer).toString());
+                    }
                 }
                 prevWasEscape = false;
             }
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 856d663..082c097 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -32,5 +32,6 @@
     void onDataConnectionStateChanged(int state, int networkType);
     void onDataActivity(int direction);
     void onSignalStrengthsChanged(in SignalStrength signalStrength);
+    void onOtaspChanged(in int otaspMode);
 }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2328717..3c4bb12 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -66,7 +66,7 @@
     boolean showCallScreenWithDialpad(boolean showDialpad);
 
     /**
-     * End call or go to the Home screen
+     * End call if there is a call in progress, otherwise does nothing.
      *
      * @return whether it hung up
      */
@@ -239,9 +239,11 @@
     String getCdmaEriText();
 
     /**
-     * Returns true if CDMA provisioning needs to run.
+     * Returns true if OTA service provisioning needs to run.
+     * Only relevant on some technologies, others will always
+     * return false.
      */
-    boolean getCdmaNeedsProvisioning();
+    boolean needsOtaServiceProvisioning();
 
     /**
       * Returns the unread count of voicemails
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 87e4b52..3c83e50 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -17,6 +17,8 @@
 package com.android.internal.telephony;
 
 import android.content.Intent;
+import android.net.LinkProperties;
+import android.net.LinkCapabilities;
 import android.os.Bundle;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -32,8 +34,9 @@
     void notifyCallForwardingChanged(boolean cfi);
     void notifyDataActivity(int state);
     void notifyDataConnection(int state, boolean isDataConnectivityPossible,
-            String reason, String apn, in String[] apnTypes, String interfaceName, int networkType,
-            String gateway);
-    void notifyDataConnectionFailed(String reason);
+            String reason, String apn, String apnType, in LinkProperties linkProperties,
+            in LinkCapabilities linkCapabilities, int networkType);
+    void notifyDataConnectionFailed(String reason, String apnType);
     void notifyCellLocation(in Bundle cellLocation);
+    void notifyOtaspChanged(in int otaspMode);
 }
diff --git a/telephony/java/com/android/internal/telephony/IccUtils.java b/telephony/java/com/android/internal/telephony/IccUtils.java
index 957eddd..df579b0 100644
--- a/telephony/java/com/android/internal/telephony/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/IccUtils.java
@@ -16,13 +16,16 @@
 
 package com.android.internal.telephony;
 
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.util.Log;
 
 import com.android.internal.telephony.GsmAlphabet;
-
 import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
 
 /**
  * Various methods, useful for dealing with SIM data.
@@ -150,6 +153,9 @@
      */
     public static String
     adnStringFieldToString(byte[] data, int offset, int length) {
+        if (length == 0) {
+            return "";
+        }
         if (length >= 1) {
             if (data[offset] == (byte) 0x80) {
                 int ucslen = (length - 1) / 2;
@@ -225,7 +231,15 @@
             return ret.toString();
         }
 
-        return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length);
+        Resources resource = Resources.getSystem();
+        String defaultCharset = "";
+        try {
+            defaultCharset =
+                    resource.getString(com.android.internal.R.string.gsm_alphabet_default_charset);
+        } catch (NotFoundException e) {
+            // Ignore Exception and defaultCharset is set to a empty string.
+        }
+        return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim());
     }
 
     static int
@@ -267,9 +281,11 @@
 
 
     /**
-     * Converts a byte array into a String hexidecimal characters
+     * Converts a byte array into a String of hexadecimal characters.
      *
-     * null returns null
+     * @param bytes an array of bytes
+     *
+     * @return hex string representation of bytes array
      */
     public static String
     bytesToHexString(byte[] bytes) {
diff --git a/telephony/java/com/android/internal/telephony/MccTable.java b/telephony/java/com/android/internal/telephony/MccTable.java
index 5539057..c0bf7ec 100644
--- a/telephony/java/com/android/internal/telephony/MccTable.java
+++ b/telephony/java/com/android/internal/telephony/MccTable.java
@@ -23,346 +23,13 @@
 import android.net.wifi.WifiManager;
 import android.os.RemoteException;
 import android.os.SystemProperties;
-import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
-import java.util.Arrays;
-
-/**
- * The table below is built from two resources:
- *
- * 1) ITU "Mobile Network Code (MNC) for the international
- *   identification plan for mobile terminals and mobile users"
- *   which is available as an annex to the ITU operational bulletin
- *   available here: http://www.itu.int/itu-t/bulletin/annex.html
- *
- * 2) The ISO 3166 country codes list, available here:
- *    http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/index.html
- *
- * This table was verified (28 Aug 2009) against
- * http://en.wikipedia.org/wiki/List_of_mobile_country_codes with the
- * only unresolved discrepancy being that this list has an extra entry
- * (461) for China.
- *
- * TODO: Complete the mappings for timezones and language/locale codes.
- *
- * The actual table data used in the Java code is generated from the
- * below Python code for efficiency.  The information is expected to
- * be static, but if changes are required, the table in the python
- * code can be modified and the trailing code run to re-generate the
- * tables that are to be used by Java.
-
-mcc_table = [
-  (202, 'gr', 2, 'Greece'),
-  (204, 'nl', 2, 'Europe/Amsterdam', 'nl', 13, 'Netherlands (Kingdom of the)'),
-  (206, 'be', 2, 'Belgium'),
-  (208, 'fr', 2, 'Europe/Paris', 'fr', 'France'),
-  (212, 'mc', 2, 'Monaco (Principality of)'),
-  (213, 'ad', 2, 'Andorra (Principality of)'),
-  (214, 'es', 2, 'Europe/Madrid', 'es', 'Spain'),
-  (216, 'hu', 2, 'Hungary (Republic of)'),
-  (218, 'ba', 2, 'Bosnia and Herzegovina'),
-  (219, 'hr', 2, 'Croatia (Republic of)'),
-  (220, 'rs', 2, 'Serbia and Montenegro'),
-  (222, 'it', 2, 'Europe/Rome', 'it', 'Italy'),
-  (225, 'va', 2, 'Europe/Rome', 'it', 'Vatican City State'),
-  (226, 'ro', 2, 'Romania'),
-  (228, 'ch', 2, 'Europe/Zurich', 'de', 'Switzerland (Confederation of)'),
-  (230, 'cz', 2, 'Europe/Prague', 'cs', 13, 'Czech Republic'),
-  (231, 'sk', 2, 'Slovak Republic'),
-  (232, 'at', 2, 'Europe/Vienna', 'de', 13, 'Austria'),
-  (234, 'gb', 2, 'Europe/London', 'en', 13, 'United Kingdom of Great Britain and Northern Ireland'),
-  (235, 'gb', 2, 'Europe/London', 'en', 13, 'United Kingdom of Great Britain and Northern Ireland'),
-  (238, 'dk', 2, 'Denmark'),
-  (240, 'se', 2, 'Sweden'),
-  (242, 'no', 2, 'Norway'),
-  (244, 'fi', 2, 'Finland'),
-  (246, 'lt', 2, 'Lithuania (Republic of)'),
-  (247, 'lv', 2, 'Latvia (Republic of)'),
-  (248, 'ee', 2, 'Estonia (Republic of)'),
-  (250, 'ru', 2, 'Russian Federation'),
-  (255, 'ua', 2, 'Ukraine'),
-  (257, 'by', 2, 'Belarus (Republic of)'),
-  (259, 'md', 2, 'Moldova (Republic of)'),
-  (260, 'pl', 2, 'Europe/Warsaw', 'Poland (Republic of)'),
-  (262, 'de', 2, 'Europe/Berlin', 'de', 13, 'Germany (Federal Republic of)'),
-  (266, 'gi', 2, 'Gibraltar'),
-  (268, 'pt', 2, 'Portugal'),
-  (270, 'lu', 2, 'Luxembourg'),
-  (272, 'ie', 2, 'Europe/Dublin', 'en', 'Ireland'),
-  (274, 'is', 2, 'Iceland'),
-  (276, 'al', 2, 'Albania (Republic of)'),
-  (278, 'mt', 2, 'Malta'),
-  (280, 'cy', 2, 'Cyprus (Republic of)'),
-  (282, 'ge', 2, 'Georgia'),
-  (283, 'am', 2, 'Armenia (Republic of)'),
-  (284, 'bg', 2, 'Bulgaria (Republic of)'),
-  (286, 'tr', 2, 'Turkey'),
-  (288, 'fo', 2, 'Faroe Islands'),
-  (289, 'ge', 2, 'Abkhazia (Georgia)'),
-  (290, 'gl', 2, 'Greenland (Denmark)'),
-  (292, 'sm', 2, 'San Marino (Republic of)'),
-  (293, 'sl', 2, 'Slovenia (Republic of)'),
-  (294, 'mk', 2, 'The Former Yugoslav Republic of Macedonia'),
-  (295, 'li', 2, 'Liechtenstein (Principality of)'),
-  (297, 'me', 2, 'Montenegro (Republic of)'),
-  (302, 'ca', 3, '', '', 11, 'Canada'),
-  (308, 'pm', 2, 'Saint Pierre and Miquelon (Collectivit territoriale de la Rpublique franaise)'),
-  (310, 'us', 3, '', 'en', 11, 'United States of America'),
-  (311, 'us', 3, '', 'en', 11, 'United States of America'),
-  (312, 'us', 3, '', 'en', 11, 'United States of America'),
-  (313, 'us', 3, '', 'en', 11, 'United States of America'),
-  (314, 'us', 3, '', 'en', 11, 'United States of America'),
-  (315, 'us', 3, '', 'en', 11, 'United States of America'),
-  (316, 'us', 3, '', 'en', 11, 'United States of America'),
-  (330, 'pr', 2, 'Puerto Rico'),
-  (332, 'vi', 2, 'United States Virgin Islands'),
-  (334, 'mx', 3, 'Mexico'),
-  (338, 'jm', 3, 'Jamaica'),
-  (340, 'gp', 2, 'Guadeloupe (French Department of)'),
-  (342, 'bb', 3, 'Barbados'),
-  (344, 'ag', 3, 'Antigua and Barbuda'),
-  (346, 'ky', 3, 'Cayman Islands'),
-  (348, 'vg', 3, 'British Virgin Islands'),
-  (350, 'bm', 2, 'Bermuda'),
-  (352, 'gd', 2, 'Grenada'),
-  (354, 'ms', 2, 'Montserrat'),
-  (356, 'kn', 2, 'Saint Kitts and Nevis'),
-  (358, 'lc', 2, 'Saint Lucia'),
-  (360, 'vc', 2, 'Saint Vincent and the Grenadines'),
-  (362, 'nl', 2, 'Netherlands Antilles'),
-  (363, 'aw', 2, 'Aruba'),
-  (364, 'bs', 2, 'Bahamas (Commonwealth of the)'),
-  (365, 'ai', 3, 'Anguilla'),
-  (366, 'dm', 2, 'Dominica (Commonwealth of)'),
-  (368, 'cu', 2, 'Cuba'),
-  (370, 'do', 2, 'Dominican Republic'),
-  (372, 'ht', 2, 'Haiti (Republic of)'),
-  (374, 'tt', 2, 'Trinidad and Tobago'),
-  (376, 'tc', 2, 'Turks and Caicos Islands'),
-  (400, 'az', 2, 'Azerbaijani Republic'),
-  (401, 'kz', 2, 'Kazakhstan (Republic of)'),
-  (402, 'bt', 2, 'Bhutan (Kingdom of)'),
-  (404, 'in', 2, 'India (Republic of)'),
-  (405, 'in', 2, 'India (Republic of)'),
-  (410, 'pk', 2, 'Pakistan (Islamic Republic of)'),
-  (412, 'af', 2, 'Afghanistan'),
-  (413, 'lk', 2, 'Sri Lanka (Democratic Socialist Republic of)'),
-  (414, 'mm', 2, 'Myanmar (Union of)'),
-  (415, 'lb', 2, 'Lebanon'),
-  (416, 'jo', 2, 'Jordan (Hashemite Kingdom of)'),
-  (417, 'sy', 2, 'Syrian Arab Republic'),
-  (418, 'iq', 2, 'Iraq (Republic of)'),
-  (419, 'kw', 2, 'Kuwait (State of)'),
-  (420, 'sa', 2, 'Saudi Arabia (Kingdom of)'),
-  (421, 'ye', 2, 'Yemen (Republic of)'),
-  (422, 'om', 2, 'Oman (Sultanate of)'),
-  (423, 'ps', 2, 'Palestine'),
-  (424, 'ae', 2, 'United Arab Emirates'),
-  (425, 'il', 2, 'Israel (State of)'),
-  (426, 'bh', 2, 'Bahrain (Kingdom of)'),
-  (427, 'qa', 2, 'Qatar (State of)'),
-  (428, 'mn', 2, 'Mongolia'),
-  (429, 'np', 2, 'Nepal'),
-  (430, 'ae', 2, 'United Arab Emirates'),
-  (431, 'ae', 2, 'United Arab Emirates'),
-  (432, 'ir', 2, 'Iran (Islamic Republic of)'),
-  (434, 'uz', 2, 'Uzbekistan (Republic of)'),
-  (436, 'tj', 2, 'Tajikistan (Republic of)'),
-  (437, 'kg', 2, 'Kyrgyz Republic'),
-  (438, 'tm', 2, 'Turkmenistan'),
-  (440, 'jp', 2, 'Asia/Tokyo', 'ja', 14, 'Japan'),
-  (441, 'jp', 2, 'Asia/Tokyo', 'ja', 14, 'Japan'),
-  (450, 'kr', 2, 'Asia/Seoul', 'ko', 13, 'Korea (Republic of)'),
-  (452, 'vn', 2, 'Viet Nam (Socialist Republic of)'),
-  (454, 'hk', 2, '"Hong Kong, China"'),
-  (455, 'mo', 2, '"Macao, China"'),
-  (456, 'kh', 2, 'Cambodia (Kingdom of)'),
-  (457, 'la', 2, "Lao People's Democratic Republic"),
-  (460, 'cn', 2, "Asia/Beijing", 'zh', 13, "China (People's Republic of)"),
-  (461, 'cn', 2, "Asia/Beijing", 'zh', 13, "China (People's Republic of)"),
-  (466, 'tw', 2, "Taiwan (Republic of China)"),
-  (467, 'kp', 2, "Democratic People's Republic of Korea"),
-  (470, 'bd', 2, "Bangladesh (People's Republic of)"),
-  (472, 'mv', 2, 'Maldives (Republic of)'),
-  (502, 'my', 2, 'Malaysia'),
-  (505, 'au', 2, 'Australia/Sydney', 'en', 11, 'Australia'),
-  (510, 'id', 2, 'Indonesia (Republic of)'),
-  (514, 'tl', 2, 'Democratic Republic of Timor-Leste'),
-  (515, 'ph', 2, 'Philippines (Republic of the)'),
-  (520, 'th', 2, 'Thailand'),
-  (525, 'sg', 2, 'Asia/Singapore', 'en', 11, 'Singapore (Republic of)'),
-  (528, 'bn', 2, 'Brunei Darussalam'),
-  (530, 'nz', 2, 'Pacific/Auckland', 'en', 'New Zealand'),
-  (534, 'mp', 2, 'Northern Mariana Islands (Commonwealth of the)'),
-  (535, 'gu', 2, 'Guam'),
-  (536, 'nr', 2, 'Nauru (Republic of)'),
-  (537, 'pg', 2, 'Papua New Guinea'),
-  (539, 'to', 2, 'Tonga (Kingdom of)'),
-  (540, 'sb', 2, 'Solomon Islands'),
-  (541, 'vu', 2, 'Vanuatu (Republic of)'),
-  (542, 'fj', 2, 'Fiji (Republic of)'),
-  (543, 'wf', 2, "Wallis and Futuna (Territoire franais d'outre-mer)"),
-  (544, 'as', 2, 'American Samoa'),
-  (545, 'ki', 2, 'Kiribati (Republic of)'),
-  (546, 'nc', 2, "New Caledonia (Territoire franais d'outre-mer)"),
-  (547, 'pf', 2, "French Polynesia (Territoire franais d'outre-mer)"),
-  (548, 'ck', 2, 'Cook Islands'),
-  (549, 'ws', 2, 'Samoa (Independent State of)'),
-  (550, 'fm', 2, 'Micronesia (Federated States of)'),
-  (551, 'mh', 2, 'Marshall Islands (Republic of the)'),
-  (552, 'pw', 2, 'Palau (Republic of)'),
-  (602, 'eg', 2, 'Egypt (Arab Republic of)'),
-  (603, 'dz', 2, "Algeria (People's Democratic Republic of)"),
-  (604, 'ma', 2, 'Morocco (Kingdom of)'),
-  (605, 'tn', 2, 'Tunisia'),
-  (606, 'ly', 2, "Libya (Socialist People's Libyan Arab Jamahiriya)"),
-  (607, 'gm', 2, 'Gambia (Republic of the)'),
-  (608, 'sn', 2, 'Senegal (Republic of)'),
-  (609, 'mr', 2, 'Mauritania (Islamic Republic of)'),
-  (610, 'ml', 2, 'Mali (Republic of)'),
-  (611, 'gn', 2, 'Guinea (Republic of)'),
-  (612, 'ci', 2, "Cte d'Ivoire (Republic of)"),
-  (613, 'bf', 2, 'Burkina Faso'),
-  (614, 'ne', 2, 'Niger (Republic of the)'),
-  (615, 'tg', 2, 'Togolese Republic'),
-  (616, 'bj', 2, 'Benin (Republic of)'),
-  (617, 'mu', 2, 'Mauritius (Republic of)'),
-  (618, 'lr', 2, 'Liberia (Republic of)'),
-  (619, 'sl', 2, 'Sierra Leone'),
-  (620, 'gh', 2, 'Ghana'),
-  (621, 'ng', 2, 'Nigeria (Federal Republic of)'),
-  (622, 'td', 2, 'Chad (Republic of)'),
-  (623, 'cf', 2, 'Central African Republic'),
-  (624, 'cm', 2, 'Cameroon (Republic of)'),
-  (625, 'cv', 2, 'Cape Verde (Republic of)'),
-  (626, 'st', 2, 'Sao Tome and Principe (Democratic Republic of)'),
-  (627, 'gq', 2, 'Equatorial Guinea (Republic of)'),
-  (628, 'ga', 2, 'Gabonese Republic'),
-  (629, 'cg', 2, 'Congo (Republic of the)'),
-  (630, 'cg', 2, 'Democratic Republic of the Congo'),
-  (631, 'ao', 2, 'Angola (Republic of)'),
-  (632, 'gw', 2, 'Guinea-Bissau (Republic of)'),
-  (633, 'sc', 2, 'Seychelles (Republic of)'),
-  (634, 'sd', 2, 'Sudan (Republic of the)'),
-  (635, 'rw', 2, 'Rwanda (Republic of)'),
-  (636, 'et', 2, 'Ethiopia (Federal Democratic Republic of)'),
-  (637, 'so', 2, 'Somali Democratic Republic'),
-  (638, 'dj', 2, 'Djibouti (Republic of)'),
-  (639, 'ke', 2, 'Kenya (Republic of)'),
-  (640, 'tz', 2, 'Tanzania (United Republic of)'),
-  (641, 'ug', 2, 'Uganda (Republic of)'),
-  (642, 'bi', 2, 'Burundi (Republic of)'),
-  (643, 'mz', 2, 'Mozambique (Republic of)'),
-  (645, 'zm', 2, 'Zambia (Republic of)'),
-  (646, 'mg', 2, 'Madagascar (Republic of)'),
-  (647, 're', 2, 'Reunion (French Department of)'),
-  (648, 'zw', 2, 'Zimbabwe (Republic of)'),
-  (649, 'na', 2, 'Namibia (Republic of)'),
-  (650, 'mw', 2, 'Malawi'),
-  (651, 'ls', 2, 'Lesotho (Kingdom of)'),
-  (652, 'bw', 2, 'Botswana (Republic of)'),
-  (653, 'sz', 2, 'Swaziland (Kingdom of)'),
-  (654, 'km', 2, 'Comoros (Union of the)'),
-  (655, 'za', 2, 'Africa/Johannesburg', 'en', 'South Africa (Republic of)'),
-  (657, 'er', 2, 'Eritrea'),
-  (702, 'bz', 2, 'Belize'),
-  (704, 'gt', 2, 'Guatemala (Republic of)'),
-  (706, 'sv', 2, 'El Salvador (Republic of)'),
-  (708, 'hn', 3, 'Honduras (Republic of)'),
-  (710, 'ni', 2, 'Nicaragua'),
-  (712, 'cr', 2, 'Costa Rica'),
-  (714, 'pa', 2, 'Panama (Republic of)'),
-  (716, 'pe', 2, 'Peru'),
-  (722, 'ar', 3, 'Argentine Republic'),
-  (724, 'br', 2, 'Brazil (Federative Republic of)'),
-  (730, 'cl', 2, 'Chile'),
-  (732, 'co', 3, 'Colombia (Republic of)'),
-  (734, 've', 2, 'Venezuela (Bolivarian Republic of)'),
-  (736, 'bo', 2, 'Bolivia (Republic of)'),
-  (738, 'gy', 2, 'Guyana'),
-  (740, 'ec', 2, 'Ecuador'),
-  (742, 'gf', 2, 'French Guiana (French Department of)'),
-  (744, 'py', 2, 'Paraguay (Republic of)'),
-  (746, 'sr', 2, 'Suriname (Republic of)'),
-  (748, 'uy', 2, 'Uruguay (Eastern Republic of)'),
-  (750, 'fk', 2, 'Falkland Islands (Malvinas)')]
-
-get_mcc = lambda elt: elt[0]
-get_iso = lambda elt: elt[1]
-get_sd = lambda elt: elt[2]
-get_tz = lambda elt: len(elt) > 4 and elt[3] or ''
-get_lang = lambda elt: len(elt) > 5 and elt[4] or ''
-get_wifi = lambda elt: len(elt) > 6 and elt[5] or 0
-
-mcc_codes = ['0x%04x' % get_mcc(elt) for elt in mcc_table]
-tz_set = sorted(x for x in set(get_tz(elt) for elt in mcc_table))
-lang_set = sorted(x for x in set(get_lang(elt) for elt in mcc_table))
-
-def mk_ind_code(elt):
-  iso = get_iso(elt)
-  iso_code = ((ord(iso[0]) << 8) | ord(iso[1])) & 0xFFFF # 16 bits
-  wifi = get_wifi(elt) & 0x000F                          #  4 bits
-  sd = get_sd(elt) & 0x0003                              #  2 bits
-  tz_ind = tz_set.index(get_tz(elt)) & 0x001F            #  5 bits
-  lang_ind = lang_set.index(get_lang(elt)) & 0x000F      #  4 bits
-  return (iso_code << 16) | (wifi << 11) | (sd << 9) | (tz_ind << 4) | lang_ind
-
-ind_codes = ['0x%08x' % mk_ind_code(elt) for elt in mcc_table]
-
-def fmt_list(title, l, batch_sz):
-  sl = []
-  for i in range(len(l) / batch_sz + (len(l) % batch_sz and 1 or 0)):
-    j = i * batch_sz
-    sl.append((' ' * 8) + ', '.join(l[j:j + batch_sz]))
-  return '    private static final %s = {\n' % title + ',\n'.join(sl) + '\n    };\n'
-
-def do_autogen_comment(extra_desc=[]):
-  print '    /' + '**\n    * AUTO GENERATED (by the Python code above)'
-  for line in extra_desc:
-    print '    * %s' % line
-  print '    *' + '/'
-
-do_autogen_comment()
-print fmt_list('String[] TZ_STRINGS', ['"%s"' % x for x in tz_set], 1)
-do_autogen_comment()
-print fmt_list('String[] LANG_STRINGS', ['"%s"' % x for x in lang_set], 10)
-do_autogen_comment(['This table is a list of MCC codes.  The index in this table',
-                    'of a given MCC code is the index of extra information about',
-                    'that MCC in the IND_CODES table.'])
-print fmt_list('short[] MCC_CODES', mcc_codes, 10)
-do_autogen_comment(['The values in this table are broken down as follows (msb to lsb):',
-                    '    iso country code 16 bits',
-                    '    (unused)          1 bit',
-                    '    wifi channel      4 bits',
-                    '    smalled digit     2 bits',
-                    '    default timezone  5 bits',
-                    '    default language  4 bits'])
-print fmt_list('int[] IND_CODES', ind_codes, 6)
-
-def parse_ind_code(ind):
-  mcc = eval(mcc_codes[ind])
-  code = eval(ind_codes[ind])
-  iso_lsb = int((code >> 16) & 0x00FF)
-  iso_msb = int((code >> 24) & 0x00FF)
-  iso = '%s%s' % (chr(iso_msb), chr(iso_lsb))
-  wifi = int((code >> 11) & 0x000F)
-  sd = int((code >> 9) & 0x0003)
-  tz_ind = (code >> 4) & 0x001F
-  lang_ind = (code >> 0) & 0x000F
-  return (mcc, iso, sd, tz_set[tz_ind], lang_set[lang_ind], wifi)
-
-fmt_str = 'mcc = %s, iso = %s, sd = %s, tz = %s, lang = %s, wifi = %s'
-orig_table = [fmt_str % (get_mcc(elt), get_iso(elt), get_sd(elt),
-                         get_tz(elt), get_lang(elt), get_wifi(elt))
-              for elt in mcc_table]
-derived_table = [fmt_str % parse_ind_code(i) for i in range(len(ind_codes))]
-for i in range(len(orig_table)):
-  if orig_table[i] == derived_table[i]: continue
-  print 'MISMATCH ERROR : ', orig_table[i], " != ", derived_table[i]
-
-*/
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Locale;
+import libcore.icu.TimeZones;
 
 /**
  * Mobile Country Code
@@ -371,202 +38,130 @@
  */
 public final class MccTable
 {
-    /**
-     * AUTO GENERATED (by the Python code above)
-     */
-    private static final String[] TZ_STRINGS = {
-        "",
-        "Africa/Johannesburg",
-        "Asia/Beijing",
-        "Asia/Seoul",
-        "Asia/Singapore",
-        "Asia/Tokyo",
-        "Australia/Sydney",
-        "Europe/Amsterdam",
-        "Europe/Berlin",
-        "Europe/Dublin",
-        "Europe/London",
-        "Europe/Madrid",
-        "Europe/Paris",
-        "Europe/Prague",
-        "Europe/Rome",
-        "Europe/Vienna",
-        "Europe/Warsaw",
-        "Europe/Zurich",
-        "Pacific/Auckland"
-    };
-
-    /**
-     * AUTO GENERATED (by the Python code above)
-     */
-    private static final String[] LANG_STRINGS = {
-        "", "cs", "de", "en", "es", "fr", "it", "ja", "ko", "nl",
-        "zh"
-    };
-
-    /**
-     * AUTO GENERATED (by the Python code above)
-     * This table is a list of MCC codes.  The index in this table
-     * of a given MCC code is the index of extra information about
-     * that MCC in the IND_CODES table.
-     */
-    private static final short[] MCC_CODES = {
-        0x00ca, 0x00cc, 0x00ce, 0x00d0, 0x00d4, 0x00d5, 0x00d6, 0x00d8, 0x00da, 0x00db,
-        0x00dc, 0x00de, 0x00e1, 0x00e2, 0x00e4, 0x00e6, 0x00e7, 0x00e8, 0x00ea, 0x00eb,
-        0x00ee, 0x00f0, 0x00f2, 0x00f4, 0x00f6, 0x00f7, 0x00f8, 0x00fa, 0x00ff, 0x0101,
-        0x0103, 0x0104, 0x0106, 0x010a, 0x010c, 0x010e, 0x0110, 0x0112, 0x0114, 0x0116,
-        0x0118, 0x011a, 0x011b, 0x011c, 0x011e, 0x0120, 0x0121, 0x0122, 0x0124, 0x0125,
-        0x0126, 0x0127, 0x0129, 0x012e, 0x0134, 0x0136, 0x0137, 0x0138, 0x0139, 0x013a,
-        0x013b, 0x013c, 0x014a, 0x014c, 0x014e, 0x0152, 0x0154, 0x0156, 0x0158, 0x015a,
-        0x015c, 0x015e, 0x0160, 0x0162, 0x0164, 0x0166, 0x0168, 0x016a, 0x016b, 0x016c,
-        0x016d, 0x016e, 0x0170, 0x0172, 0x0174, 0x0176, 0x0178, 0x0190, 0x0191, 0x0192,
-        0x0194, 0x0195, 0x019a, 0x019c, 0x019d, 0x019e, 0x019f, 0x01a0, 0x01a1, 0x01a2,
-        0x01a3, 0x01a4, 0x01a5, 0x01a6, 0x01a7, 0x01a8, 0x01a9, 0x01aa, 0x01ab, 0x01ac,
-        0x01ad, 0x01ae, 0x01af, 0x01b0, 0x01b2, 0x01b4, 0x01b5, 0x01b6, 0x01b8, 0x01b9,
-        0x01c2, 0x01c4, 0x01c6, 0x01c7, 0x01c8, 0x01c9, 0x01cc, 0x01cd, 0x01d2, 0x01d3,
-        0x01d6, 0x01d8, 0x01f6, 0x01f9, 0x01fe, 0x0202, 0x0203, 0x0208, 0x020d, 0x0210,
-        0x0212, 0x0216, 0x0217, 0x0218, 0x0219, 0x021b, 0x021c, 0x021d, 0x021e, 0x021f,
-        0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0226, 0x0227, 0x0228, 0x025a,
-        0x025b, 0x025c, 0x025d, 0x025e, 0x025f, 0x0260, 0x0261, 0x0262, 0x0263, 0x0264,
-        0x0265, 0x0266, 0x0267, 0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e,
-        0x026f, 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, 0x0278,
-        0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f, 0x0280, 0x0281, 0x0282,
-        0x0283, 0x0285, 0x0286, 0x0287, 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d,
-        0x028e, 0x028f, 0x0291, 0x02be, 0x02c0, 0x02c2, 0x02c4, 0x02c6, 0x02c8, 0x02ca,
-        0x02cc, 0x02d2, 0x02d4, 0x02da, 0x02dc, 0x02de, 0x02e0, 0x02e2, 0x02e4, 0x02e6,
-        0x02e8, 0x02ea, 0x02ec, 0x02ee
-    };
-
-    /**
-     * AUTO GENERATED (by the Python code above)
-     * The values in this table are broken down as follows (msb to lsb):
-     *     iso country code 16 bits
-     *     (unused)          1 bit
-     *     wifi channel      4 bits
-     *     smalled digit     2 bits
-     *     default timezone  5 bits
-     *     default language  4 bits
-     */
-    private static final int[] IND_CODES = {
-        0x67720400, 0x6e6c6c79, 0x62650400, 0x667204c5, 0x6d630400, 0x61640400,
-        0x657304b4, 0x68750400, 0x62610400, 0x68720400, 0x72730400, 0x697404e6,
-        0x766104e6, 0x726f0400, 0x63680512, 0x637a6cd1, 0x736b0400, 0x61746cf2,
-        0x67626ca3, 0x67626ca3, 0x646b0400, 0x73650400, 0x6e6f0400, 0x66690400,
-        0x6c740400, 0x6c760400, 0x65650400, 0x72750400, 0x75610400, 0x62790400,
-        0x6d640400, 0x706c0500, 0x64656c82, 0x67690400, 0x70740400, 0x6c750400,
-        0x69650493, 0x69730400, 0x616c0400, 0x6d740400, 0x63790400, 0x67650400,
-        0x616d0400, 0x62670400, 0x74720400, 0x666f0400, 0x67650400, 0x676c0400,
-        0x736d0400, 0x736c0400, 0x6d6b0400, 0x6c690400, 0x6d650400, 0x63615e00,
-        0x706d0400, 0x75735e03, 0x75735e03, 0x75735e03, 0x75735e03, 0x75735e03,
-        0x75735e03, 0x75735e03, 0x70720400, 0x76690400, 0x6d780600, 0x6a6d0600,
-        0x67700400, 0x62620600, 0x61670600, 0x6b790600, 0x76670600, 0x626d0400,
-        0x67640400, 0x6d730400, 0x6b6e0400, 0x6c630400, 0x76630400, 0x6e6c0400,
-        0x61770400, 0x62730400, 0x61690600, 0x646d0400, 0x63750400, 0x646f0400,
-        0x68740400, 0x74740400, 0x74630400, 0x617a0400, 0x6b7a0400, 0x62740400,
-        0x696e0400, 0x696e0400, 0x706b0400, 0x61660400, 0x6c6b0400, 0x6d6d0400,
-        0x6c620400, 0x6a6f0400, 0x73790400, 0x69710400, 0x6b770400, 0x73610400,
-        0x79650400, 0x6f6d0400, 0x70730400, 0x61650400, 0x696c0400, 0x62680400,
-        0x71610400, 0x6d6e0400, 0x6e700400, 0x61650400, 0x61650400, 0x69720400,
-        0x757a0400, 0x746a0400, 0x6b670400, 0x746d0400, 0x6a707457, 0x6a707457,
-        0x6b726c38, 0x766e0400, 0x686b0400, 0x6d6f0400, 0x6b680400, 0x6c610400,
-        0x636e6c2a, 0x636e6c2a, 0x74770400, 0x6b700400, 0x62640400, 0x6d760400,
-        0x6d790400, 0x61755c63, 0x69640400, 0x746c0400, 0x70680400, 0x74680400,
-        0x73675c43, 0x626e0400, 0x6e7a0523, 0x6d700400, 0x67750400, 0x6e720400,
-        0x70670400, 0x746f0400, 0x73620400, 0x76750400, 0x666a0400, 0x77660400,
-        0x61730400, 0x6b690400, 0x6e630400, 0x70660400, 0x636b0400, 0x77730400,
-        0x666d0400, 0x6d680400, 0x70770400, 0x65670400, 0x647a0400, 0x6d610400,
-        0x746e0400, 0x6c790400, 0x676d0400, 0x736e0400, 0x6d720400, 0x6d6c0400,
-        0x676e0400, 0x63690400, 0x62660400, 0x6e650400, 0x74670400, 0x626a0400,
-        0x6d750400, 0x6c720400, 0x736c0400, 0x67680400, 0x6e670400, 0x74640400,
-        0x63660400, 0x636d0400, 0x63760400, 0x73740400, 0x67710400, 0x67610400,
-        0x63670400, 0x63670400, 0x616f0400, 0x67770400, 0x73630400, 0x73640400,
-        0x72770400, 0x65740400, 0x736f0400, 0x646a0400, 0x6b650400, 0x747a0400,
-        0x75670400, 0x62690400, 0x6d7a0400, 0x7a6d0400, 0x6d670400, 0x72650400,
-        0x7a770400, 0x6e610400, 0x6d770400, 0x6c730400, 0x62770400, 0x737a0400,
-        0x6b6d0400, 0x7a610413, 0x65720400, 0x627a0400, 0x67740400, 0x73760400,
-        0x686e0600, 0x6e690400, 0x63720400, 0x70610400, 0x70650400, 0x61720600,
-        0x62720400, 0x636c0400, 0x636f0600, 0x76650400, 0x626f0400, 0x67790400,
-        0x65630400, 0x67660400, 0x70790400, 0x73720400, 0x75790400, 0x666b0400
-    };
-
     static final String LOG_TAG = "MccTable";
 
+    static ArrayList<MccEntry> table;
+
+    static class MccEntry implements Comparable<MccEntry>
+    {
+        int mcc;
+        String iso;
+        int smallestDigitsMnc;
+        String language;
+
+        MccEntry(int mnc, String iso, int smallestDigitsMCC) {
+            this(mnc, iso, smallestDigitsMCC, null);
+        }
+
+        MccEntry(int mnc, String iso, int smallestDigitsMCC, String language) {
+            this.mcc = mnc;
+            this.iso = iso;
+            this.smallestDigitsMnc = smallestDigitsMCC;
+            this.language = language;
+        }
+
+
+        public int compareTo(MccEntry o)
+        {
+            return mcc - o.mcc;
+        }
+    }
+
+    private static MccEntry
+    entryForMcc(int mcc)
+    {
+        int index;
+
+        MccEntry m;
+
+        m = new MccEntry(mcc, null, 0);
+
+        index = Collections.binarySearch(table, m);
+
+        if (index < 0) {
+            return null;
+        } else {
+            return table.get(index);
+        }
+    }
+
     /**
-     * Given a Mobile Country Code, returns a default time zone ID
-     * if available.  Returns null if unavailable.
+     * Returns a default time zone ID for the given MCC.
+     * @param mcc Mobile Country Code
+     * @return default TimeZone ID, or null if not specified
      */
     public static String defaultTimeZoneForMcc(int mcc) {
-        int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
-        if (index < 0) {
+        MccEntry entry;
+
+        entry = entryForMcc(mcc);
+        if (entry == null || entry.iso == null) {
             return null;
+        } else {
+            Locale locale;
+            if (entry.language == null) {
+                locale = new Locale(entry.iso);
+            } else {
+                locale = new Locale(entry.language, entry.iso);
+            }
+            String[] tz = TimeZones.forLocale(locale);
+            if (tz.length == 0) return null;
+            return tz[0];
         }
-        int indCode = IND_CODES[index];
-        int tzInd = (indCode >>> 4) & 0x001F;
-        String tz = TZ_STRINGS[tzInd];
-        if (tz == "") {
-            return null;
-        }
-        return tz;
     }
 
     /**
-     * Given a Mobile Country Code, returns an ISO two-character
-     * country code if available.  Returns "" if unavailable.
+     * Given a GSM Mobile Country Code, returns
+     * an ISO two-character country code if available.
+     * Returns "" if unavailable.
      */
-    public static String countryCodeForMcc(int mcc) {
-        int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
-        if (index < 0) {
+    public static String
+    countryCodeForMcc(int mcc)
+    {
+        MccEntry entry;
+
+        entry = entryForMcc(mcc);
+
+        if (entry == null) {
             return "";
+        } else {
+            return entry.iso;
         }
-        int indCode = IND_CODES[index];
-        byte[] iso = {(byte)((indCode >>> 24) & 0x00FF), (byte)((indCode >>> 16) & 0x00FF)};
-        return new String(iso);
     }
 
     /**
-     * Given a GSM Mobile Country Code, returns an ISO 2-3 character
-     * language code if available.  Returns null if unavailable.
+     * Given a GSM Mobile Country Code, returns
+     * an ISO 2-3 character language code if available.
+     * Returns null if unavailable.
      */
     public static String defaultLanguageForMcc(int mcc) {
-        int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
-        if (index < 0) {
+        MccEntry entry;
+
+        entry = entryForMcc(mcc);
+
+        if (entry == null) {
             return null;
+        } else {
+            return entry.language;
         }
-        int indCode = IND_CODES[index];
-        int langInd = indCode & 0x000F;
-        String lang = LANG_STRINGS[langInd];
-        if (lang == "") {
-            return null;
-        }
-        return lang;
     }
 
     /**
-     * Given a GSM Mobile Country Code, returns the corresponding
-     * smallest number of digits field.  Returns 2 if unavailable.
+     * Given a GSM Mobile Country Code, returns
+     * the smallest number of digits that M if available.
+     * Returns 2 if unavailable.
      */
-    public static int smallestDigitsMccForMnc(int mcc) {
-        int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
-        if (index < 0) {
+    public static int
+    smallestDigitsMccForMnc(int mcc)
+    {
+        MccEntry entry;
+
+        entry = entryForMcc(mcc);
+
+        if (entry == null) {
             return 2;
+        } else {
+            return entry.smallestDigitsMnc;
         }
-        int indCode = IND_CODES[index];
-        int smDig = (indCode >>> 9) & 0x0003;
-        return smDig;
-    }
-
-    /**
-     * Given a GSM Mobile Country Code, returns the number of wifi
-     * channels allowed in that country.  Returns 0 if unavailable.
-     */
-    public static int wifiChannelsForMcc(int mcc) {
-        int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
-        if (index < 0) {
-            return 0;
-        }
-        int indCode = IND_CODES[index];
-        int wifi = (indCode >>> 11) & 0x000F;
-        return wifi;
     }
 
     /**
@@ -592,7 +187,7 @@
             if (mcc != 0) {
                 setTimezoneFromMccIfNeeded(phone, mcc);
                 setLocaleFromMccIfNeeded(phone, mcc);
-                setWifiChannelsFromMcc(phone, mcc);
+                setWifiCountryCodeFromMcc(phone, mcc);
             }
             try {
                 Configuration config = ActivityManagerNative.getDefault().getConfiguration();
@@ -648,14 +243,272 @@
      * @param phone PhoneBase to act on (get context from).
      * @param mcc Mobile Country Code of the SIM or SIM-like entity (build prop on CDMA)
      */
-    private static void setWifiChannelsFromMcc(PhoneBase phone, int mcc) {
-        int wifiChannels = MccTable.wifiChannelsForMcc(mcc);
-        if (wifiChannels != 0) {
+    private static void setWifiCountryCodeFromMcc(PhoneBase phone, int mcc) {
+        String country = MccTable.countryCodeForMcc(mcc);
+        if (!country.isEmpty()) {
             Context context = phone.getContext();
-            Log.d(LOG_TAG, "WIFI_NUM_ALLOWED_CHANNELS set to " + wifiChannels);
+            Log.d(LOG_TAG, "WIFI_COUNTRY_CODE set to " + country);
             WifiManager wM = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
             //persist
-            wM.setNumAllowedChannels(wifiChannels, true);
+            wM.setCountryCode(country, true);
         }
     }
+
+    static {
+        table = new ArrayList<MccEntry>(240);
+
+
+        /*
+         * The table below is built from two resources:
+         *
+         * 1) ITU "Mobile Network Code (MNC) for the international
+         *   identification plan for mobile terminals and mobile users"
+         *   which is available as an annex to the ITU operational bulletin
+         *   available here: http://www.itu.int/itu-t/bulletin/annex.html
+         *
+         * 2) The ISO 3166 country codes list, available here:
+         *    http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/index.html
+         *
+         * This table has not been verified.
+         *
+         */
+
+		table.add(new MccEntry(202,"gr",2));	//Greece
+		table.add(new MccEntry(204,"nl",2,"nl"));	//Netherlands (Kingdom of the)
+		table.add(new MccEntry(206,"be",2));	//Belgium
+		table.add(new MccEntry(208,"fr",2,"fr"));	//France
+		table.add(new MccEntry(212,"mc",2));	//Monaco (Principality of)
+		table.add(new MccEntry(213,"ad",2));	//Andorra (Principality of)
+		table.add(new MccEntry(214,"es",2,"es"));	//Spain
+		table.add(new MccEntry(216,"hu",2));	//Hungary (Republic of)
+		table.add(new MccEntry(218,"ba",2));	//Bosnia and Herzegovina
+		table.add(new MccEntry(219,"hr",2));	//Croatia (Republic of)
+		table.add(new MccEntry(220,"rs",2));	//Serbia and Montenegro
+		table.add(new MccEntry(222,"it",2,"it"));	//Italy
+		table.add(new MccEntry(225,"va",2,"it"));	//Vatican City State
+		table.add(new MccEntry(226,"ro",2));	//Romania
+		table.add(new MccEntry(228,"ch",2,"de"));	//Switzerland (Confederation of)
+		table.add(new MccEntry(230,"cz",2,"cs"));	//Czech Republic
+		table.add(new MccEntry(231,"sk",2));	//Slovak Republic
+		table.add(new MccEntry(232,"at",2,"de"));	//Austria
+		table.add(new MccEntry(234,"gb",2,"en"));	//United Kingdom of Great Britain and Northern Ireland
+		table.add(new MccEntry(235,"gb",2,"en"));	//United Kingdom of Great Britain and Northern Ireland
+		table.add(new MccEntry(238,"dk",2));	//Denmark
+		table.add(new MccEntry(240,"se",2));	//Sweden
+		table.add(new MccEntry(242,"no",2));	//Norway
+		table.add(new MccEntry(244,"fi",2));	//Finland
+		table.add(new MccEntry(246,"lt",2));	//Lithuania (Republic of)
+		table.add(new MccEntry(247,"lv",2));	//Latvia (Republic of)
+		table.add(new MccEntry(248,"ee",2));	//Estonia (Republic of)
+		table.add(new MccEntry(250,"ru",2));	//Russian Federation
+		table.add(new MccEntry(255,"ua",2));	//Ukraine
+		table.add(new MccEntry(257,"by",2));	//Belarus (Republic of)
+		table.add(new MccEntry(259,"md",2));	//Moldova (Republic of)
+		table.add(new MccEntry(260,"pl",2));	//Poland (Republic of)
+		table.add(new MccEntry(262,"de",2,"de"));	//Germany (Federal Republic of)
+		table.add(new MccEntry(266,"gi",2));	//Gibraltar
+		table.add(new MccEntry(268,"pt",2));	//Portugal
+		table.add(new MccEntry(270,"lu",2));	//Luxembourg
+		table.add(new MccEntry(272,"ie",2,"en"));	//Ireland
+		table.add(new MccEntry(274,"is",2));	//Iceland
+		table.add(new MccEntry(276,"al",2));	//Albania (Republic of)
+		table.add(new MccEntry(278,"mt",2));	//Malta
+		table.add(new MccEntry(280,"cy",2));	//Cyprus (Republic of)
+		table.add(new MccEntry(282,"ge",2));	//Georgia
+		table.add(new MccEntry(283,"am",2));	//Armenia (Republic of)
+		table.add(new MccEntry(284,"bg",2));	//Bulgaria (Republic of)
+		table.add(new MccEntry(286,"tr",2));	//Turkey
+		table.add(new MccEntry(288,"fo",2));	//Faroe Islands
+                table.add(new MccEntry(289,"ge",2));    //Abkhazia (Georgia)
+		table.add(new MccEntry(290,"gl",2));	//Greenland (Denmark)
+		table.add(new MccEntry(292,"sm",2));	//San Marino (Republic of)
+		table.add(new MccEntry(293,"sl",2));	//Slovenia (Republic of)
+                table.add(new MccEntry(294,"mk",2));   //The Former Yugoslav Republic of Macedonia
+		table.add(new MccEntry(295,"li",2));	//Liechtenstein (Principality of)
+                table.add(new MccEntry(297,"me",2));    //Montenegro (Republic of)
+		table.add(new MccEntry(302,"ca",3,""));	//Canada
+		table.add(new MccEntry(308,"pm",2));	//Saint Pierre and Miquelon (Collectivit territoriale de la Rpublique franaise)
+		table.add(new MccEntry(310,"us",3,"en"));	//United States of America
+		table.add(new MccEntry(311,"us",3,"en"));	//United States of America
+		table.add(new MccEntry(312,"us",3,"en"));	//United States of America
+		table.add(new MccEntry(313,"us",3,"en"));	//United States of America
+		table.add(new MccEntry(314,"us",3,"en"));	//United States of America
+		table.add(new MccEntry(315,"us",3,"en"));	//United States of America
+		table.add(new MccEntry(316,"us",3,"en"));	//United States of America
+		table.add(new MccEntry(330,"pr",2));	//Puerto Rico
+		table.add(new MccEntry(332,"vi",2));	//United States Virgin Islands
+		table.add(new MccEntry(334,"mx",3));	//Mexico
+		table.add(new MccEntry(338,"jm",3));	//Jamaica
+		table.add(new MccEntry(340,"gp",2));	//Guadeloupe (French Department of)
+		table.add(new MccEntry(342,"bb",3));	//Barbados
+		table.add(new MccEntry(344,"ag",3));	//Antigua and Barbuda
+		table.add(new MccEntry(346,"ky",3));	//Cayman Islands
+		table.add(new MccEntry(348,"vg",3));	//British Virgin Islands
+		table.add(new MccEntry(350,"bm",2));	//Bermuda
+		table.add(new MccEntry(352,"gd",2));	//Grenada
+		table.add(new MccEntry(354,"ms",2));	//Montserrat
+		table.add(new MccEntry(356,"kn",2));	//Saint Kitts and Nevis
+		table.add(new MccEntry(358,"lc",2));	//Saint Lucia
+		table.add(new MccEntry(360,"vc",2));	//Saint Vincent and the Grenadines
+		table.add(new MccEntry(362,"nl",2));	//Netherlands Antilles
+		table.add(new MccEntry(363,"aw",2));	//Aruba
+		table.add(new MccEntry(364,"bs",2));	//Bahamas (Commonwealth of the)
+		table.add(new MccEntry(365,"ai",3));	//Anguilla
+		table.add(new MccEntry(366,"dm",2));	//Dominica (Commonwealth of)
+		table.add(new MccEntry(368,"cu",2));	//Cuba
+		table.add(new MccEntry(370,"do",2));	//Dominican Republic
+		table.add(new MccEntry(372,"ht",2));	//Haiti (Republic of)
+		table.add(new MccEntry(374,"tt",2));	//Trinidad and Tobago
+		table.add(new MccEntry(376,"tc",2));	//Turks and Caicos Islands
+		table.add(new MccEntry(400,"az",2));	//Azerbaijani Republic
+		table.add(new MccEntry(401,"kz",2));	//Kazakhstan (Republic of)
+		table.add(new MccEntry(402,"bt",2));	//Bhutan (Kingdom of)
+		table.add(new MccEntry(404,"in",2));	//India (Republic of)
+		table.add(new MccEntry(405,"in",2));	//India (Republic of)
+		table.add(new MccEntry(410,"pk",2));	//Pakistan (Islamic Republic of)
+		table.add(new MccEntry(412,"af",2));	//Afghanistan
+		table.add(new MccEntry(413,"lk",2));	//Sri Lanka (Democratic Socialist Republic of)
+		table.add(new MccEntry(414,"mm",2));	//Myanmar (Union of)
+		table.add(new MccEntry(415,"lb",2));	//Lebanon
+		table.add(new MccEntry(416,"jo",2));	//Jordan (Hashemite Kingdom of)
+		table.add(new MccEntry(417,"sy",2));	//Syrian Arab Republic
+		table.add(new MccEntry(418,"iq",2));	//Iraq (Republic of)
+		table.add(new MccEntry(419,"kw",2));	//Kuwait (State of)
+		table.add(new MccEntry(420,"sa",2));	//Saudi Arabia (Kingdom of)
+		table.add(new MccEntry(421,"ye",2));	//Yemen (Republic of)
+		table.add(new MccEntry(422,"om",2));	//Oman (Sultanate of)
+                table.add(new MccEntry(423,"ps",2));    //Palestine
+		table.add(new MccEntry(424,"ae",2));	//United Arab Emirates
+		table.add(new MccEntry(425,"il",2));	//Israel (State of)
+		table.add(new MccEntry(426,"bh",2));	//Bahrain (Kingdom of)
+		table.add(new MccEntry(427,"qa",2));	//Qatar (State of)
+		table.add(new MccEntry(428,"mn",2));	//Mongolia
+		table.add(new MccEntry(429,"np",2));	//Nepal
+		table.add(new MccEntry(430,"ae",2));	//United Arab Emirates
+		table.add(new MccEntry(431,"ae",2));	//United Arab Emirates
+		table.add(new MccEntry(432,"ir",2));	//Iran (Islamic Republic of)
+		table.add(new MccEntry(434,"uz",2));	//Uzbekistan (Republic of)
+		table.add(new MccEntry(436,"tj",2));	//Tajikistan (Republic of)
+		table.add(new MccEntry(437,"kg",2));	//Kyrgyz Republic
+		table.add(new MccEntry(438,"tm",2));	//Turkmenistan
+		table.add(new MccEntry(440,"jp",2,"ja"));	//Japan
+		table.add(new MccEntry(441,"jp",2,"ja"));	//Japan
+		table.add(new MccEntry(450,"kr",2,"ko"));	//Korea (Republic of)
+		table.add(new MccEntry(452,"vn",2));	//Viet Nam (Socialist Republic of)
+		table.add(new MccEntry(454,"hk",2));	//"Hong Kong, China"
+		table.add(new MccEntry(455,"mo",2));	//"Macao, China"
+		table.add(new MccEntry(456,"kh",2));	//Cambodia (Kingdom of)
+		table.add(new MccEntry(457,"la",2));	//Lao People's Democratic Republic
+		table.add(new MccEntry(460,"cn",2,"zh"));	//China (People's Republic of)
+		table.add(new MccEntry(461,"cn",2,"zh"));	//China (People's Republic of)
+		table.add(new MccEntry(466,"tw",2));	//"Taiwan, China"
+		table.add(new MccEntry(467,"kp",2));	//Democratic People's Republic of Korea
+		table.add(new MccEntry(470,"bd",2));	//Bangladesh (People's Republic of)
+		table.add(new MccEntry(472,"mv",2));	//Maldives (Republic of)
+		table.add(new MccEntry(502,"my",2));	//Malaysia
+		table.add(new MccEntry(505,"au",2,"en"));	//Australia
+		table.add(new MccEntry(510,"id",2));	//Indonesia (Republic of)
+		table.add(new MccEntry(514,"tl",2));	//Democratic Republic of Timor-Leste
+		table.add(new MccEntry(515,"ph",2));	//Philippines (Republic of the)
+		table.add(new MccEntry(520,"th",2));	//Thailand
+		table.add(new MccEntry(525,"sg",2,"en"));	//Singapore (Republic of)
+		table.add(new MccEntry(528,"bn",2));	//Brunei Darussalam
+		table.add(new MccEntry(530,"nz",2, "en"));	//New Zealand
+		table.add(new MccEntry(534,"mp",2));	//Northern Mariana Islands (Commonwealth of the)
+		table.add(new MccEntry(535,"gu",2));	//Guam
+		table.add(new MccEntry(536,"nr",2));	//Nauru (Republic of)
+		table.add(new MccEntry(537,"pg",2));	//Papua New Guinea
+		table.add(new MccEntry(539,"to",2));	//Tonga (Kingdom of)
+		table.add(new MccEntry(540,"sb",2));	//Solomon Islands
+		table.add(new MccEntry(541,"vu",2));	//Vanuatu (Republic of)
+		table.add(new MccEntry(542,"fj",2));	//Fiji (Republic of)
+		table.add(new MccEntry(543,"wf",2));	//Wallis and Futuna (Territoire franais d'outre-mer)
+		table.add(new MccEntry(544,"as",2));	//American Samoa
+		table.add(new MccEntry(545,"ki",2));	//Kiribati (Republic of)
+		table.add(new MccEntry(546,"nc",2));	//New Caledonia (Territoire franais d'outre-mer)
+		table.add(new MccEntry(547,"pf",2));	//French Polynesia (Territoire franais d'outre-mer)
+		table.add(new MccEntry(548,"ck",2));	//Cook Islands
+		table.add(new MccEntry(549,"ws",2));	//Samoa (Independent State of)
+		table.add(new MccEntry(550,"fm",2));	//Micronesia (Federated States of)
+		table.add(new MccEntry(551,"mh",2));	//Marshall Islands (Republic of the)
+		table.add(new MccEntry(552,"pw",2));	//Palau (Republic of)
+		table.add(new MccEntry(602,"eg",2));	//Egypt (Arab Republic of)
+		table.add(new MccEntry(603,"dz",2));	//Algeria (People's Democratic Republic of)
+		table.add(new MccEntry(604,"ma",2));	//Morocco (Kingdom of)
+		table.add(new MccEntry(605,"tn",2));	//Tunisia
+		table.add(new MccEntry(606,"ly",2));	//Libya (Socialist People's Libyan Arab Jamahiriya)
+		table.add(new MccEntry(607,"gm",2));	//Gambia (Republic of the)
+		table.add(new MccEntry(608,"sn",2));	//Senegal (Republic of)
+		table.add(new MccEntry(609,"mr",2));	//Mauritania (Islamic Republic of)
+		table.add(new MccEntry(610,"ml",2));	//Mali (Republic of)
+		table.add(new MccEntry(611,"gn",2));	//Guinea (Republic of)
+		table.add(new MccEntry(612,"ci",2));	//Cte d'Ivoire (Republic of)
+		table.add(new MccEntry(613,"bf",2));	//Burkina Faso
+		table.add(new MccEntry(614,"ne",2));	//Niger (Republic of the)
+		table.add(new MccEntry(615,"tg",2));	//Togolese Republic
+		table.add(new MccEntry(616,"bj",2));	//Benin (Republic of)
+		table.add(new MccEntry(617,"mu",2));	//Mauritius (Republic of)
+		table.add(new MccEntry(618,"lr",2));	//Liberia (Republic of)
+		table.add(new MccEntry(619,"sl",2));	//Sierra Leone
+		table.add(new MccEntry(620,"gh",2));	//Ghana
+		table.add(new MccEntry(621,"ng",2));	//Nigeria (Federal Republic of)
+		table.add(new MccEntry(622,"td",2));	//Chad (Republic of)
+		table.add(new MccEntry(623,"cf",2));	//Central African Republic
+		table.add(new MccEntry(624,"cm",2));	//Cameroon (Republic of)
+		table.add(new MccEntry(625,"cv",2));	//Cape Verde (Republic of)
+		table.add(new MccEntry(626,"st",2));	//Sao Tome and Principe (Democratic Republic of)
+		table.add(new MccEntry(627,"gq",2));	//Equatorial Guinea (Republic of)
+		table.add(new MccEntry(628,"ga",2));	//Gabonese Republic
+		table.add(new MccEntry(629,"cg",2));	//Congo (Republic of the)
+		table.add(new MccEntry(630,"cg",2));	//Democratic Republic of the Congo
+		table.add(new MccEntry(631,"ao",2));	//Angola (Republic of)
+		table.add(new MccEntry(632,"gw",2));	//Guinea-Bissau (Republic of)
+		table.add(new MccEntry(633,"sc",2));	//Seychelles (Republic of)
+		table.add(new MccEntry(634,"sd",2));	//Sudan (Republic of the)
+		table.add(new MccEntry(635,"rw",2));	//Rwanda (Republic of)
+		table.add(new MccEntry(636,"et",2));	//Ethiopia (Federal Democratic Republic of)
+		table.add(new MccEntry(637,"so",2));	//Somali Democratic Republic
+		table.add(new MccEntry(638,"dj",2));	//Djibouti (Republic of)
+		table.add(new MccEntry(639,"ke",2));	//Kenya (Republic of)
+		table.add(new MccEntry(640,"tz",2));	//Tanzania (United Republic of)
+		table.add(new MccEntry(641,"ug",2));	//Uganda (Republic of)
+		table.add(new MccEntry(642,"bi",2));	//Burundi (Republic of)
+		table.add(new MccEntry(643,"mz",2));	//Mozambique (Republic of)
+		table.add(new MccEntry(645,"zm",2));	//Zambia (Republic of)
+		table.add(new MccEntry(646,"mg",2));	//Madagascar (Republic of)
+		table.add(new MccEntry(647,"re",2));	//Reunion (French Department of)
+		table.add(new MccEntry(648,"zw",2));	//Zimbabwe (Republic of)
+		table.add(new MccEntry(649,"na",2));	//Namibia (Republic of)
+		table.add(new MccEntry(650,"mw",2));	//Malawi
+		table.add(new MccEntry(651,"ls",2));	//Lesotho (Kingdom of)
+		table.add(new MccEntry(652,"bw",2));	//Botswana (Republic of)
+		table.add(new MccEntry(653,"sz",2));	//Swaziland (Kingdom of)
+		table.add(new MccEntry(654,"km",2));	//Comoros (Union of the)
+		table.add(new MccEntry(655,"za",2,"en"));	//South Africa (Republic of)
+		table.add(new MccEntry(657,"er",2));	//Eritrea
+		table.add(new MccEntry(702,"bz",2));	//Belize
+		table.add(new MccEntry(704,"gt",2));	//Guatemala (Republic of)
+		table.add(new MccEntry(706,"sv",2));	//El Salvador (Republic of)
+		table.add(new MccEntry(708,"hn",3));	//Honduras (Republic of)
+		table.add(new MccEntry(710,"ni",2));	//Nicaragua
+		table.add(new MccEntry(712,"cr",2));	//Costa Rica
+		table.add(new MccEntry(714,"pa",2));	//Panama (Republic of)
+		table.add(new MccEntry(716,"pe",2));	//Peru
+		table.add(new MccEntry(722,"ar",3));	//Argentine Republic
+		table.add(new MccEntry(724,"br",2));	//Brazil (Federative Republic of)
+		table.add(new MccEntry(730,"cl",2));	//Chile
+		table.add(new MccEntry(732,"co",3));	//Colombia (Republic of)
+		table.add(new MccEntry(734,"ve",2));	//Venezuela (Bolivarian Republic of)
+		table.add(new MccEntry(736,"bo",2));	//Bolivia (Republic of)
+		table.add(new MccEntry(738,"gy",2));	//Guyana
+		table.add(new MccEntry(740,"ec",2));	//Ecuador
+		table.add(new MccEntry(742,"gf",2));	//French Guiana (French Department of)
+		table.add(new MccEntry(744,"py",2));	//Paraguay (Republic of)
+		table.add(new MccEntry(746,"sr",2));	//Suriname (Republic of)
+		table.add(new MccEntry(748,"uy",2));	//Uruguay (Eastern Republic of)
+		table.add(new MccEntry(750,"fk",2));	//Falkland Islands (Malvinas)
+        //table.add(new MccEntry(901,"",2));	//"International Mobile, shared code"
+
+        Collections.sort(table);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index e426e94..2957c7e 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -17,10 +17,10 @@
 package com.android.internal.telephony;
 
 import android.content.Context;
-import android.content.SharedPreferences;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
 import android.os.Handler;
 import android.os.Message;
-import android.preference.PreferenceManager;
 import android.telephony.CellLocation;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
@@ -28,7 +28,6 @@
 
 import com.android.internal.telephony.DataConnection;
 import com.android.internal.telephony.gsm.NetworkInfo;
-import com.android.internal.telephony.gsm.GsmDataConnection;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 
 import java.util.List;
@@ -99,11 +98,12 @@
     static final String PHONE_NAME_KEY = "phoneName";
     static final String FAILURE_REASON_KEY = "reason";
     static final String STATE_CHANGE_REASON_KEY = "reason";
-    static final String DATA_APN_TYPES_KEY = "apnType";
+    static final String DATA_APN_TYPE_KEY = "apnType";
     static final String DATA_APN_KEY = "apn";
+    static final String DATA_LINK_PROPERTIES_KEY = "linkProperties";
+    static final String DATA_LINK_CAPABILITIES_KEY = "linkCapabilities";
 
     static final String DATA_IFACE_NAME_KEY = "iface";
-    static final String DATA_GATEWAY_KEY = "gateway";
     static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
     static final String PHONE_IN_ECM_STATE = "phoneinECMState";
 
@@ -243,13 +243,21 @@
     CellLocation getCellLocation();
 
     /**
-     * Get the current DataState. No change notification exists at this
-     * interface -- use
+     * Get the current for the default apn DataState. No change notification
+     * exists at this interface -- use
      * {@link android.telephony.PhoneStateListener} instead.
      */
     DataState getDataConnectionState();
 
     /**
+     * Get the current DataState. No change notification exists at this
+     * interface -- use
+     * {@link android.telephony.PhoneStateListener} instead.
+     * @param apnType specify for which apn to get connection state info.
+     */
+    DataState getDataConnectionState(String apnType);
+
+    /**
      * Get the current DataActivityState. No change notification exists at this
      * interface -- use
      * {@link android.telephony.TelephonyManager} instead.
@@ -313,6 +321,16 @@
     String getActiveApn();
 
     /**
+     * Return the LinkProperties for the named apn or null if not available
+     */
+    LinkProperties getLinkProperties(String apnType);
+
+    /**
+     * Return the LinkCapabilities
+     */
+    LinkCapabilities getLinkCapabilities(String apnType);
+
+    /**
      * Get current signal strength. No change notification available on this
      * interface. Use <code>PhoneStateNotifier</code> or an equivalent.
      * An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu).
@@ -1373,29 +1391,6 @@
     boolean isDataConnectivityPossible();
 
     /**
-     * Returns the name of the network interface used by the specified APN type.
-     */
-    String getInterfaceName(String apnType);
-
-    /**
-     * Returns the IP address of the network interface used by the specified
-     * APN type.
-     */
-    String getIpAddress(String apnType);
-
-    /**
-     * Returns the gateway for the network interface used by the specified APN
-     * type.
-     */
-    String getGateway(String apnType);
-
-    /**
-     * Returns the DNS servers for the network interface used by the specified
-     * APN type.
-     */
-    public String[] getDnsServers(String apnType);
-
-    /**
      * Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones.
      */
     String getDeviceId();
@@ -1540,6 +1535,11 @@
     boolean isOtaSpNumber(String dialStr);
 
     /**
+     * Returns true if OTA Service Provisioning needs to be performed.
+     */
+    boolean needsOtaServiceProvisioning();
+
+    /**
      * Register for notifications when CDMA call waiting comes
      *
      * @param h Handler that receives the notification message.
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 1674ad6..dddb493 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -21,6 +21,8 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.SharedPreferences;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
 import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
 import android.os.Handler;
@@ -35,10 +37,8 @@
 import android.util.Log;
 
 import com.android.internal.R;
-import com.android.internal.telephony.gsm.GsmDataConnection;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 
-import java.util.List;
 import java.util.Locale;
 
 
@@ -558,11 +558,6 @@
             String c = carrierLocales[i].toString();
             if (carrier.equals(c)) {
                 String l = carrierLocales[i+1].toString();
-                int wifiChannels = 0;
-                try {
-                    wifiChannels = Integer.parseInt(
-                            carrierLocales[i+2].toString());
-                } catch (NumberFormatException e) { }
 
                 String language = l.substring(0, 2);
                 String country = "";
@@ -571,15 +566,15 @@
                 }
                 setSystemLocale(language, country);
 
-                if (wifiChannels != 0) {
+                if (!country.isEmpty()) {
                     try {
                         Settings.Secure.getInt(mContext.getContentResolver(),
-                                Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS);
+                                Settings.Secure.WIFI_COUNTRY_CODE);
                     } catch (Settings.SettingNotFoundException e) {
                         // note this is not persisting
                         WifiManager wM = (WifiManager)
                                 mContext.getSystemService(Context.WIFI_SERVICE);
-                        wM.setNumAllowedChannels(wifiChannels, false);
+                        wM.setCountryCode(country, false);
                     }
                 }
                 return;
@@ -741,8 +736,17 @@
         mNotifier.notifyMessageWaitingChanged(this);
     }
 
-    public void notifyDataConnection(String reason) {
-        mNotifier.notifyDataConnection(this, reason);
+    public void notifyDataConnection(String reason, String apnType,
+            Phone.DataState state) {
+        mNotifier.notifyDataConnection(this, reason, apnType, state);
+    }
+
+    public void notifyDataConnection(String reason, String apnType) {
+        mNotifier.notifyDataConnection(this, reason, apnType);
+    }
+
+    public void notifyOtaspChanged(int otaspMode) {
+        mNotifier.notifyOtaspChanged(this, otaspMode);
     }
 
     public abstract String getPhoneName();
@@ -828,9 +832,19 @@
         logUnexpectedCdmaMethodCall("unregisterForSubscriptionInfoReady");
     }
 
+    /**
+     * Returns true if OTA Service Provisioning needs to be performed.
+     * If not overridden return false.
+     */
+    public boolean needsOtaServiceProvisioning() {
+        return false;
+    }
+
+    /**
+     * Return true if number is an OTASP number.
+     * If not overridden return false.
+     */
     public  boolean isOtaSpNumber(String dialStr) {
-        // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
-        logUnexpectedCdmaMethodCall("isOtaSpNumber");
         return false;
     }
 
@@ -920,30 +934,22 @@
          logUnexpectedCdmaMethodCall("unsetOnEcbModeExitResponse");
      }
 
-    public String getInterfaceName(String apnType) {
-        return mDataConnection.getInterfaceName(apnType);
-    }
-
-    public String getIpAddress(String apnType) {
-        return mDataConnection.getIpAddress(apnType);
-    }
-
     public boolean isDataConnectivityEnabled() {
         return mDataConnection.getDataEnabled();
     }
 
-    public String getGateway(String apnType) {
-        return mDataConnection.getGateway(apnType);
-    }
-
-    public String[] getDnsServers(String apnType) {
-        return mDataConnection.getDnsServers(apnType);
-    }
-
     public String[] getActiveApnTypes() {
         return mDataConnection.getActiveApnTypes();
     }
 
+    public LinkProperties getLinkProperties(String apnType) {
+        return mDataConnection.getLinkProperties(apnType);
+    }
+
+    public LinkCapabilities getLinkCapabilities(String apnType) {
+        return mDataConnection.getLinkCapabilities(apnType);
+    }
+
     public String getActiveApn() {
         return mDataConnection.getActiveApnString();
     }
@@ -956,6 +962,10 @@
         return mDataConnection.disableApnType(type);
     }
 
+    public boolean isDataConnectivityPossible() {
+        return ((mDataConnection != null) && (mDataConnection.isDataPossible()));
+    }
+
     /**
      * simulateDataConnection
      *
@@ -984,7 +994,7 @@
         }
 
         mDataConnection.setState(dcState);
-        notifyDataConnection(null);
+        notifyDataConnection(null, Phone.APN_TYPE_DEFAULT);
     }
 
     /**
@@ -1038,6 +1048,10 @@
                 "called, CDMAPhone inactive.");
     }
 
+    public DataState getDataConnectionState() {
+        return getDataConnectionState(APN_TYPE_DEFAULT);
+    }
+
     /**
      * Common error logger method for unexpected calls to GSM/WCDMA-only methods.
      */
diff --git a/telephony/java/com/android/internal/telephony/PhoneNotifier.java b/telephony/java/com/android/internal/telephony/PhoneNotifier.java
index e96eeae..b1cf953 100644
--- a/telephony/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/PhoneNotifier.java
@@ -33,10 +33,14 @@
 
     public void notifyCallForwardingChanged(Phone sender);
 
-    public void notifyDataConnection(Phone sender, String reason);
+    public void notifyDataConnection(Phone sender, String reason, String apnType);
 
-    public void notifyDataConnectionFailed(Phone sender, String reason);
+    public void notifyDataConnection(Phone sender, String reason, String apnType,
+            Phone.DataState state);
+
+    public void notifyDataConnectionFailed(Phone sender, String reason, String apnType);
 
     public void notifyDataActivity(Phone sender);
 
+    public void notifyOtaspChanged(Phone sender, int otaspMode);
 }
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index 77f1e6c..02fdf28 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -20,13 +20,12 @@
 import android.app.ActivityManagerNative;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemProperties;
-import android.preference.PreferenceManager;
 import android.telephony.CellLocation;
-import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.util.Log;
@@ -34,7 +33,6 @@
 import com.android.internal.telephony.cdma.CDMAPhone;
 import com.android.internal.telephony.gsm.GSMPhone;
 import com.android.internal.telephony.gsm.NetworkInfo;
-import com.android.internal.telephony.gsm.GsmDataConnection;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 
 import java.util.List;
@@ -172,7 +170,11 @@
     }
 
     public DataState getDataConnectionState() {
-        return mActivePhone.getDataConnectionState();
+        return mActivePhone.getDataConnectionState(Phone.APN_TYPE_DEFAULT);
+    }
+
+    public DataState getDataConnectionState(String apnType) {
+        return mActivePhone.getDataConnectionState(apnType);
     }
 
     public DataActivityState getDataActivityState() {
@@ -207,6 +209,14 @@
         return mActivePhone.getActiveApnTypes();
     }
 
+    public LinkProperties getLinkProperties(String apnType) {
+        return mActivePhone.getLinkProperties(apnType);
+    }
+
+    public LinkCapabilities getLinkCapabilities(String apnType) {
+        return mActivePhone.getLinkCapabilities(apnType);
+    }
+
     public String getActiveApn() {
         return mActivePhone.getActiveApn();
     }
@@ -664,22 +674,6 @@
         return mActivePhone.isDataConnectivityPossible();
     }
 
-    public String getInterfaceName(String apnType) {
-        return mActivePhone.getInterfaceName(apnType);
-    }
-
-    public String getIpAddress(String apnType) {
-        return mActivePhone.getIpAddress(apnType);
-    }
-
-    public String getGateway(String apnType) {
-        return mActivePhone.getGateway(apnType);
-    }
-
-    public String[] getDnsServers(String apnType) {
-        return mActivePhone.getDnsServers(apnType);
-    }
-
     public String getDeviceId() {
         return mActivePhone.getDeviceId();
     }
@@ -768,6 +762,10 @@
         mActivePhone.exitEmergencyCallbackMode();
     }
 
+    public boolean needsOtaServiceProvisioning(){
+        return mActivePhone.needsOtaServiceProvisioning();
+    }
+
     public boolean isOtaSpNumber(String dialStr){
         return mActivePhone.isOtaSpNumber(dialStr);
     }
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index a9a4be2..35d5564 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -285,9 +285,8 @@
         }
 
 
-        //***** Handler implemementation
-
-        public void
+        //***** Handler implementation
+        @Override public void
         handleMessage(Message msg) {
             RILRequest rr = (RILRequest)(msg.obj);
             RILRequest req = null;
@@ -780,7 +779,7 @@
         send(rr);
     }
 
-    public void
+    @Deprecated public void
     getPDPContextList(Message result) {
         getDataCallList(result);
     }
@@ -1299,10 +1298,18 @@
     public void
     setupDataCall(String radioTechnology, String profile, String apn,
             String user, String password, String authType, Message result) {
+        setupDataCallWithProtocol(radioTechnology, profile, apn, user, password,
+                              authType, "IP", result);
+    }
+
+    public void
+    setupDataCallWithProtocol(String radioTechnology, String profile,
+            String apn, String user, String password, String authType,
+            String protocolType, Message result) {
         RILRequest rr
                 = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
 
-        rr.mp.writeInt(6);
+        rr.mp.writeInt(7);
 
         rr.mp.writeString(radioTechnology);
         rr.mp.writeString(profile);
@@ -1310,11 +1317,12 @@
         rr.mp.writeString(user);
         rr.mp.writeString(password);
         rr.mp.writeString(authType);
+        rr.mp.writeString(protocolType);
 
         if (RILJ_LOGD) riljLog(rr.serialString() + "> "
                 + requestToString(rr.mRequest) + " " + radioTechnology + " "
                 + profile + " " + apn + " " + user + " "
-                + password + " " + authType);
+                + password + " " + authType + " " + protocolType);
 
         send(rr);
     }
@@ -2907,7 +2915,11 @@
             dataCall.active = p.readInt();
             dataCall.type = p.readString();
             dataCall.apn = p.readString();
-            dataCall.address = p.readString();
+            String address = p.readString();
+            if (address != null) {
+                address = address.split(" ")[0];
+            }
+            dataCall.address = address;
 
             response.add(dataCall);
         }
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 917e1d8..3a7ce47 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -255,6 +255,7 @@
         mCm.unregisterForOn(this);
     }
 
+    @Override
     protected void finalize() {
         Log.d(TAG, "SMSDispatcher finalized");
     }
@@ -927,14 +928,14 @@
      */
     static protected class SmsTracker {
         // fields need to be public for derived SmsDispatchers
-        public HashMap mData;
+        public HashMap<String, Object> mData;
         public int mRetryCount;
         public int mMessageRef;
 
         public PendingIntent mSentIntent;
         public PendingIntent mDeliveryIntent;
 
-        SmsTracker(HashMap data, PendingIntent sentIntent,
+        SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
                 PendingIntent deliveryIntent) {
             mData = data;
             mSentIntent = sentIntent;
@@ -943,7 +944,7 @@
         }
     }
 
-    protected SmsTracker SmsTrackerFactory(HashMap data, PendingIntent sentIntent,
+    protected SmsTracker SmsTrackerFactory(HashMap<String, Object> data, PendingIntent sentIntent,
             PendingIntent deliveryIntent) {
         return new SmsTracker(data, sentIntent, deliveryIntent);
     }
diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
index e8bbe5e..3f9ffc3 100644
--- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -53,6 +53,12 @@
 
     public SignalStrength mSignalStrength;
 
+    /* The otaspMode passed to PhoneStateListener#onOtaspChanged */
+    static public final int OTASP_UNINITIALIZED = 0;
+    static public final int OTASP_UNKNOWN = 1;
+    static public final int OTASP_NEEDED = 2;
+    static public final int OTASP_NOT_NEEDED = 3;
+
     /**
      * A unique identifier to track requests associated with a poll
      * and ignore stale responses.  The value is a count-down of
@@ -268,9 +274,11 @@
 
     public abstract void handleMessage(Message msg);
 
+    protected abstract Phone getPhone();
     protected abstract void handlePollStateResult(int what, AsyncResult ar);
     protected abstract void updateSpnDisplay();
     protected abstract void setPowerStateToDesired();
+    protected abstract void log(String s);
 
     /**
      * Clean up existing voice and data connection then turn off radio power.
diff --git a/telephony/java/com/android/internal/telephony/SmsHeader.java b/telephony/java/com/android/internal/telephony/SmsHeader.java
index 7872eec..7a65162 100644
--- a/telephony/java/com/android/internal/telephony/SmsHeader.java
+++ b/telephony/java/com/android/internal/telephony/SmsHeader.java
@@ -30,7 +30,7 @@
  */
 public class SmsHeader {
 
-    // TODO(cleanup): this datastructure is generally referred to as
+    // TODO(cleanup): this data structure is generally referred to as
     // the 'user data header' or UDH, and so the class name should
     // change to reflect this...
 
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index af6c5f8..cbd8606 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.telephony;
 
-import android.util.Log;
 import com.android.internal.telephony.SmsHeader;
 import java.util.Arrays;
 
@@ -366,13 +365,13 @@
     /**
      * Try to parse this message as an email gateway message
      * There are two ways specified in TS 23.040 Section 3.8 :
-     *  - SMS message "may have its TP-PID set for internet electronic mail - MT
+     *  - SMS message "may have its TP-PID set for Internet electronic mail - MT
      * SMS format: [<from-address><space>]<message> - "Depending on the
      * nature of the gateway, the destination/origination address is either
      * derived from the content of the SMS TP-OA or TP-DA field, or the
      * TP-OA/TP-DA field contains a generic gateway address and the to/from
      * address is added at the beginning as shown above." (which is supported here)
-     * - Multiple addreses separated by commas, no spaces, Subject field delimited
+     * - Multiple addresses separated by commas, no spaces, Subject field delimited
      * by '()' or '##' and '#' Section 9.2.3.24.11 (which are NOT supported here)
      */
     protected void extractEmailAddressFromMessageBody() {
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index a113787..136d5b1 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -45,7 +45,7 @@
      *  CDMA networks.
      */
     static final String PROPERTY_OPERATOR_ALPHA = "gsm.operator.alpha";
-    //TODO: most of these proprieties are generic, substitute gsm. with phone. bug 1856959
+    //TODO: most of these properties are generic, substitute gsm. with phone. bug 1856959
 
     /** Numeric name (MCC+MNC) of current registered operator.<p>
      *  Availability: when registered to a network. Result may be unreliable on
@@ -83,12 +83,12 @@
 
     /** The MCC+MNC (mobile country code+mobile network code) of the
      *  provider of the SIM. 5 or 6 decimal digits.
-     *  Availablity: SIM state must be "READY"
+     *  Availability: SIM state must be "READY"
      */
     static String PROPERTY_ICC_OPERATOR_NUMERIC = "gsm.sim.operator.numeric";
 
     /** PROPERTY_ICC_OPERATOR_ALPHA is also known as the SPN, or Service Provider Name.
-     *  Availablity: SIM state must be "READY"
+     *  Availability: SIM state must be "READY"
      */
     static String PROPERTY_ICC_OPERATOR_ALPHA = "gsm.sim.operator.alpha";
 
@@ -127,7 +127,7 @@
         "ro.telephony.call_ring.multiple";
 
     /**
-     * The number of milli-seconds between CALL_RING notifications.
+     * The number of milliseconds between CALL_RING notifications.
      */
     static final String PROPERTY_CALL_RING_DELAY = "ro.telephony.call_ring.delay";
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index f31bf24..b9d5673 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -61,6 +61,7 @@
 import com.android.internal.telephony.PhoneNotifier;
 import com.android.internal.telephony.PhoneProxy;
 import com.android.internal.telephony.PhoneSubInfo;
+import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.UUSInfo;
@@ -83,10 +84,6 @@
     static final String LOG_TAG = "CDMA";
     private static final boolean DBG = true;
 
-    // Min values used to by needsActivation
-    private static final String UNACTIVATED_MIN2_VALUE = "000000";
-    private static final String UNACTIVATED_MIN_VALUE = "1111110111";
-
     // Default Emergency Callback Mode exit timer
     private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000;
 
@@ -515,14 +512,6 @@
         return false;
     }
 
-    public boolean isDataConnectivityPossible() {
-        boolean noData = mDataConnection.getDataEnabled() &&
-                getDataConnectionState() == DataState.DISCONNECTED;
-        return !noData && getIccCard().getState() == IccCard.State.READY &&
-                getServiceState().getState() == ServiceState.STATE_IN_SERVICE &&
-                (mDataConnection.getDataOnRoamingEnabled() || !getServiceState().getRoaming());
-    }
-
     /**
      * Removes the given MMI from the pending list and notifies registrants that
      * it is complete.
@@ -613,7 +602,7 @@
         }
     }
 
-    public DataState getDataConnectionState() {
+    public DataState getDataConnectionState(String apnType) {
         DataState ret = DataState.DISCONNECTED;
 
         if (mSST == null) {
@@ -625,6 +614,8 @@
             // If we're out of service, open TCP sockets may still work
             // but no data will flow
             ret = DataState.DISCONNECTED;
+        } else if (mDataConnection.isApnTypeEnabled(apnType) == false) {
+            ret = DataState.DISCONNECTED;
         } else {
             switch (mDataConnection.getState()) {
                 case FAILED:
@@ -878,26 +869,6 @@
         mRuimRecords.setVoiceMessageWaiting(1, mwi);
     }
 
-    /**
-     * Returns true if CDMA OTA Service Provisioning needs to be performed.
-     */
-    /* package */ boolean
-    needsOtaServiceProvisioning() {
-        String cdmaMin = getCdmaMin();
-        boolean needsProvisioning;
-        if (cdmaMin == null || (cdmaMin.length() < 6)) {
-            if (DBG) Log.d(LOG_TAG, "needsOtaServiceProvisioning: illegal cdmaMin='"
-                                    + cdmaMin + "' assume provisioning needed.");
-            needsProvisioning = true;
-        } else {
-            needsProvisioning = (cdmaMin.equals(UNACTIVATED_MIN_VALUE)
-                    || cdmaMin.substring(0,6).equals(UNACTIVATED_MIN2_VALUE))
-                    || SystemProperties.getBoolean("test_cdma_setup", false);
-        }
-        if (DBG) Log.d(LOG_TAG, "needsOtaServiceProvisioning: ret=" + needsProvisioning);
-        return needsProvisioning;
-    }
-
     @Override
     public void exitEmergencyCallbackMode() {
         if (mWakeLock.isHeld()) {
@@ -1191,6 +1162,14 @@
         mSMS.setCellBroadcastConfig(configValuesArray, response);
     }
 
+    /**
+     * Returns true if OTA Service Provisioning needs to be performed.
+     */
+    @Override
+    public boolean needsOtaServiceProvisioning() {
+        return mSST.getOtasp() != ServiceStateTracker.OTASP_NOT_NEEDED;
+    }
+
     private static final String IS683A_FEATURE_CODE = "*228";
     private static final int IS683A_FEATURE_CODE_NUM_DIGITS = 4;
     private static final int IS683A_SYS_SEL_CODE_NUM_DIGITS = 2;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index 3669e60..325c2e1 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -354,6 +354,24 @@
                     || (foregroundCall.getState() == CdmaCall.State.ACTIVE)
                     || !backgroundCall.getState().isAlive());
 
+        if (!ret) {
+            log(String.format("canDial is false\n" +
+                              "((serviceState=%d) != ServiceState.STATE_POWER_OFF)::=%s\n" +
+                              "&& pendingMO == null::=%s\n" +
+                              "&& !ringingCall.isRinging()::=%s\n" +
+                              "&& !disableCall.equals(\"true\")::=%s\n" +
+                              "&& (!foregroundCall.getState().isAlive()::=%s\n" +
+                              "   || foregroundCall.getState() == CdmaCall.State.ACTIVE::=%s\n" +
+                              "   ||!backgroundCall.getState().isAlive())::=%s)",
+                    serviceState,
+                    serviceState != ServiceState.STATE_POWER_OFF,
+                    pendingMO == null,
+                    !ringingCall.isRinging(),
+                    !disableCall.equals("true"),
+                    !foregroundCall.getState().isAlive(),
+                    foregroundCall.getState() == CdmaCall.State.ACTIVE,
+                    !backgroundCall.getState().isAlive()));
+        }
         return ret;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
index 95cb1c6..8072c44 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
@@ -23,6 +23,7 @@
 import com.android.internal.telephony.gsm.ApnSetting;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.RetryManager;
 
 /**
  * {@hide}
@@ -39,23 +40,27 @@
 
 
     // ***** Constructor
-    private CdmaDataConnection(CDMAPhone phone, String name) {
-        super(phone, name);
+    private CdmaDataConnection(CDMAPhone phone, String name, RetryManager rm) {
+        super(phone, name, rm);
     }
 
     /**
      * Create the connection object
      *
-     * @param phone
+     * @param phone the Phone
+     * @param id the connection id
+     * @param rm the RetryManager
      * @return CdmaDataConnection that was created.
      */
-    static CdmaDataConnection makeDataConnection(CDMAPhone phone) {
+    static CdmaDataConnection makeDataConnection(CDMAPhone phone, int id, RetryManager rm) {
         synchronized (mCountLock) {
             mCount += 1;
         }
-        CdmaDataConnection cdmaDc = new CdmaDataConnection(phone, "CdmaDataConnection-" + mCount);
+        CdmaDataConnection cdmaDc = new CdmaDataConnection(phone,
+                "CdmaDataConnection-" + mCount, rm);
         cdmaDc.start();
         if (DBG) cdmaDc.log("Made " + cdmaDc.getName());
+        cdmaDc.mId = id;
         return cdmaDc;
     }
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 9f2a44b..7c652c5 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -18,23 +18,13 @@
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
-import android.net.NetworkInfo;
 import android.net.TrafficStats;
-import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
 import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.preference.PreferenceManager;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.telephony.cdma.CdmaCellLocation;
@@ -48,10 +38,9 @@
 import com.android.internal.telephony.DataConnection;
 import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.RetryManager;
 import com.android.internal.telephony.gsm.ApnSetting;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.ServiceStateTracker;
 
 import java.util.ArrayList;
 
@@ -63,20 +52,11 @@
 
     private CDMAPhone mCdmaPhone;
 
-    // Indicates baseband will not auto-attach
-    private boolean noAutoAttach = false;
-    private boolean mIsScreenOn = true;
-
     //useful for debugging
-    boolean failNextConnect = false;
+    boolean mFailNextConnect = false;
 
-    /**
-     * dataConnectionList holds all the Data connection
-     */
-    private ArrayList<DataConnection> dataConnectionList;
-
-    /** Currently active CdmaDataConnection */
-    private CdmaDataConnection mActiveDataConnection;
+    /** The DataConnection being setup */
+    private CdmaDataConnection mPendingDataConnection;
 
     private boolean mPendingRestartRadio = false;
     private static final int TIME_DELAYED_TO_RESTART_RADIO =
@@ -87,10 +67,8 @@
      */
     private static final int DATA_CONNECTION_POOL_SIZE = 1;
 
-    private static final int POLL_CONNECTION_MILLIS = 5 * 1000;
     private static final String INTENT_RECONNECT_ALARM =
-            "com.android.internal.telephony.cdma-reconnect";
-    private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason";
+        "com.android.internal.telephony.cdma-reconnect";
 
     /**
      * Constants for the data connection activity:
@@ -114,49 +92,6 @@
     // if we have no active Apn this is null
     protected ApnSetting mActiveApn;
 
-    // Possibly promote to base class, the only difference is
-    // the INTENT_RECONNECT_ALARM action is a different string.
-    // Do consider technology changes if it is promoted.
-    BroadcastReceiver mIntentReceiver = new BroadcastReceiver ()
-    {
-        @Override
-        public void onReceive(Context context, Intent intent)
-        {
-            String action = intent.getAction();
-            if (action.equals(Intent.ACTION_SCREEN_ON)) {
-                mIsScreenOn = true;
-                stopNetStatPoll();
-                startNetStatPoll();
-            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                mIsScreenOn = false;
-                stopNetStatPoll();
-                startNetStatPoll();
-            } else if (action.equals((INTENT_RECONNECT_ALARM))) {
-                Log.d(LOG_TAG, "Data reconnect alarm. Previous state was " + state);
-
-                String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
-                if (state == State.FAILED) {
-                    cleanUpConnection(false, reason);
-                }
-                trySetupData(reason);
-            } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-                final android.net.NetworkInfo networkInfo = (NetworkInfo)
-                        intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-                mIsWifiConnected = (networkInfo != null && networkInfo.isConnected());
-            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-                final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                        WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
-
-                if (!enabled) {
-                    // when wifi got disabeled, the NETWORK_STATE_CHANGED_ACTION
-                    // quit and wont report disconnected til next enalbing.
-                    mIsWifiConnected = false;
-                }
-            }
-        }
-    };
-
-
     /* Constructor */
 
     CdmaDataConnectionTracker(CDMAPhone p) {
@@ -176,79 +111,49 @@
         p.mSST.registerForRoamingOff(this, EVENT_ROAMING_OFF, null);
         p.mCM.registerForCdmaOtaProvision(this, EVENT_CDMA_OTA_PROVISION, null);
 
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(INTENT_RECONNECT_ALARM);
-        filter.addAction(Intent.ACTION_SCREEN_ON);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-
-        // TODO: Why is this registering the phone as the receiver of the intent
-        //       and not its own handler?
-        p.getContext().registerReceiver(mIntentReceiver, filter, null, p);
-
         mDataConnectionTracker = this;
 
         createAllDataConnectionList();
-
-        // This preference tells us 1) initial condition for "dataEnabled",
-        // and 2) whether the RIL will setup the baseband to auto-PS attach.
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext());
-
-        boolean dataEnabledSetting = true;
-        try {
-            dataEnabledSetting = IConnectivityManager.Stub.asInterface(ServiceManager.
-                    getService(Context.CONNECTIVITY_SERVICE)).getMobileDataEnabled();
-        } catch (Exception e) {
-            // nothing to do - use the old behavior and leave data on
-        }
-        dataEnabled[APN_DEFAULT_ID] =
-                !sp.getBoolean(CDMAPhone.DATA_DISABLED_ON_BOOT_KEY, false) &&
-                dataEnabledSetting;
-        if (dataEnabled[APN_DEFAULT_ID]) {
-            enabledCount++;
-        }
-        noAutoAttach = !dataEnabled[APN_DEFAULT_ID];
-
-        if (!mRetryMgr.configure(SystemProperties.get("ro.cdma.data_retry_config"))) {
-            if (!mRetryMgr.configure(DEFAULT_DATA_RETRY_CONFIG)) {
-                // Should never happen, log an error and default to a simple linear sequence.
-                Log.e(LOG_TAG, "Could not configure using DEFAULT_DATA_RETRY_CONFIG="
-                        + DEFAULT_DATA_RETRY_CONFIG);
-                mRetryMgr.configure(20, 2000, 1000);
-            }
-        }
     }
 
+    @Override
     public void dispose() {
+        super.dispose();
+
         // Unregister from all events
-        phone.mCM.unregisterForAvailable(this);
-        phone.mCM.unregisterForOffOrNotAvailable(this);
+        mPhone.mCM.unregisterForAvailable(this);
+        mPhone.mCM.unregisterForOffOrNotAvailable(this);
         mCdmaPhone.mRuimRecords.unregisterForRecordsLoaded(this);
-        phone.mCM.unregisterForNVReady(this);
-        phone.mCM.unregisterForDataStateChanged(this);
+        mPhone.mCM.unregisterForNVReady(this);
+        mPhone.mCM.unregisterForDataStateChanged(this);
         mCdmaPhone.mCT.unregisterForVoiceCallEnded(this);
         mCdmaPhone.mCT.unregisterForVoiceCallStarted(this);
         mCdmaPhone.mSST.unregisterForCdmaDataConnectionAttached(this);
         mCdmaPhone.mSST.unregisterForCdmaDataConnectionDetached(this);
         mCdmaPhone.mSST.unregisterForRoamingOn(this);
         mCdmaPhone.mSST.unregisterForRoamingOff(this);
-        phone.mCM.unregisterForCdmaOtaProvision(this);
+        mPhone.mCM.unregisterForCdmaOtaProvision(this);
 
-        phone.getContext().unregisterReceiver(this.mIntentReceiver);
         destroyAllDataConnectionList();
     }
 
+    @Override
     protected void finalize() {
-        if(DBG) Log.d(LOG_TAG, "CdmaDataConnectionTracker finalized");
+        if(DBG) log("CdmaDataConnectionTracker finalized");
     }
 
+    @Override
+    protected String getActionIntentReconnectAlarm() {
+        return INTENT_RECONNECT_ALARM;
+    }
+
+    @Override
     protected void setState(State s) {
         if (DBG) log ("setState: " + s);
-        if (state != s) {
+        if (mState != s) {
             EventLog.writeEvent(EventLogTags.CDMA_DATA_STATE_CHANGE,
-                    state.toString(), s.toString());
-            state = s;
+                    mState.toString(), s.toString());
+            mState = s;
         }
     }
 
@@ -267,6 +172,7 @@
         return false;
     }
 
+    @Override
     protected String[] getActiveApnTypes() {
         String[] result;
         if (mActiveApn != null) {
@@ -278,6 +184,7 @@
         return result;
     }
 
+    @Override
     protected String getActiveApnString() {
         return null;
     }
@@ -291,84 +198,98 @@
      *
      * @return false while no data connection if all above requirements are met.
      */
+    @Override
     public boolean isDataConnectionAsDesired() {
-        boolean roaming = phone.getServiceState().getRoaming();
+        boolean roaming = mPhone.getServiceState().getRoaming();
 
-        if (((phone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY) ||
+        if (((mPhone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY) ||
                  mCdmaPhone.mRuimRecords.getRecordsLoaded()) &&
                 (mCdmaPhone.mSST.getCurrentCdmaDataConnectionState() ==
                  ServiceState.STATE_IN_SERVICE) &&
                 (!roaming || getDataOnRoamingEnabled()) &&
                 !mIsWifiConnected ) {
-            return (state == State.CONNECTED);
+            return (mState == State.CONNECTED);
         }
         return true;
     }
 
-    private boolean isDataAllowed() {
-        boolean roaming = phone.getServiceState().getRoaming();
-        return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled()) && mMasterDataEnabled;
+    @Override
+    protected boolean isDataAllowed() {
+        int psState = mCdmaPhone.mSST.getCurrentCdmaDataConnectionState();
+        boolean roaming = (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled());
+        boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState();
+
+        boolean allowed =
+                    (psState == ServiceState.STATE_IN_SERVICE ||
+                            mAutoAttachOnCreation) &&
+                    (mPhone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY ||
+                            mCdmaPhone.mRuimRecords.getRecordsLoaded()) &&
+                    (mCdmaPhone.mSST.isConcurrentVoiceAndData() ||
+                            mPhone.getState() == Phone.State.IDLE) &&
+                    !roaming &&
+                    mMasterDataEnabled &&
+                    desiredPowerState &&
+                    !mPendingRestartRadio &&
+                    !mCdmaPhone.needsOtaServiceProvisioning();
+        if (!allowed && DBG) {
+            String reason = "";
+            if (!((psState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) {
+                reason += " - psState= " + psState;
+            }
+            if (!(mPhone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY ||
+                    mCdmaPhone.mRuimRecords.getRecordsLoaded())) {
+                reason += " - radioState= " + mPhone.mCM.getRadioState() + " - RUIM not loaded";
+            }
+            if (mPhone.getState() != Phone.State.IDLE &&
+                    mCdmaPhone.mSST.isConcurrentVoiceAndData()) {
+                reason += " - concurrentVoiceAndData not allowed and state= " + mPhone.getState();
+            }
+            if (roaming) reason += " - Roaming";
+            if (!mMasterDataEnabled) reason += " - mMasterDataEnabled= false";
+            if (!desiredPowerState) reason += " - desiredPowerState= false";
+            if (mPendingRestartRadio) reason += " - mPendingRestartRadio= true";
+            if (mCdmaPhone.needsOtaServiceProvisioning()) reason += " - needs Provisioning";
+            log("Data not allowed due to" + reason);
+        }
+        return allowed;
     }
 
     private boolean trySetupData(String reason) {
         if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason));
 
-        if (phone.getSimulatedRadioControl() != null) {
+        if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
             // FIXME  this can be improved
             setState(State.CONNECTED);
-            phone.notifyDataConnection(reason);
+            notifyDataConnection(reason);
+            notifyOffApnsOfAvailability(reason, true);
 
-            Log.i(LOG_TAG, "(fix?) We're on the simulator; assuming data is connected");
+            log("(fix?) We're on the simulator; assuming data is connected");
             return true;
         }
 
         int psState = mCdmaPhone.mSST.getCurrentCdmaDataConnectionState();
-        boolean roaming = phone.getServiceState().getRoaming();
+        boolean roaming = mPhone.getServiceState().getRoaming();
         boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState();
 
-        if ((state == State.IDLE || state == State.SCANNING)
-                && (psState == ServiceState.STATE_IN_SERVICE)
-                && ((phone.mCM.getRadioState() == CommandsInterface.RadioState.NV_READY) ||
-                        mCdmaPhone.mRuimRecords.getRecordsLoaded())
-                && (mCdmaPhone.mSST.isConcurrentVoiceAndData() ||
-                        phone.getState() == Phone.State.IDLE )
-                && isDataAllowed()
-                && desiredPowerState
-                && !mPendingRestartRadio
-                && !mCdmaPhone.needsOtaServiceProvisioning()) {
-
-            return setupData(reason);
-
+        if ((mState == State.IDLE || mState == State.SCANNING) &&
+                isDataAllowed() && getAnyDataEnabled()) {
+            boolean retValue = setupData(reason);
+            notifyOffApnsOfAvailability(reason, retValue);
+            return retValue;
         } else {
-            if (DBG) {
-                    log("trySetupData: Not ready for data: " +
-                    " dataState=" + state +
-                    " PS state=" + psState +
-                    " radio state=" + phone.mCM.getRadioState() +
-                    " ruim=" + mCdmaPhone.mRuimRecords.getRecordsLoaded() +
-                    " concurrentVoice&Data=" + mCdmaPhone.mSST.isConcurrentVoiceAndData() +
-                    " phoneState=" + phone.getState() +
-                    " dataEnabled=" + getAnyDataEnabled() +
-                    " roaming=" + roaming +
-                    " dataOnRoamingEnable=" + getDataOnRoamingEnabled() +
-                    " desiredPowerState=" + desiredPowerState +
-                    " PendingRestartRadio=" + mPendingRestartRadio +
-                    " MasterDataEnabled=" + mMasterDataEnabled +
-                    " needsOtaServiceProvisioning=" + mCdmaPhone.needsOtaServiceProvisioning());
-            }
+            notifyOffApnsOfAvailability(reason, false);
             return false;
         }
     }
 
     /**
-     * If tearDown is true, this only tears down a CONNECTED session. Presently,
-     * there is no mechanism for abandoning an INITING/CONNECTING session,
-     * but would likely involve cancelling pending async requests or
-     * setting a flag or new state to ignore them when they came in
-     * @param tearDown true if the underlying DataConnection should be
-     * disconnected.
-     * @param reason reason for the clean up.
+     * Cleanup all connections.
+     *
+     * TODO: Cleanup only a specified connection passed as a parameter.
+     *
+     * @param tearDown true if the underlying DataConnection should be disconnected.
+     * @param reason for the clean up.
      */
     private void cleanUpConnection(boolean tearDown, String reason) {
         if (DBG) log("cleanUpConnection: reason: " + reason);
@@ -376,19 +297,21 @@
         // Clear the reconnect alarm, if set.
         if (mReconnectIntent != null) {
             AlarmManager am =
-                (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE);
+                (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
             am.cancel(mReconnectIntent);
             mReconnectIntent = null;
         }
 
         setState(State.DISCONNECTING);
+        notifyDataAvailability(reason);
 
         boolean notificationDeferred = false;
-        for (DataConnection conn : dataConnectionList) {
+        for (DataConnection conn : mDataConnections.values()) {
             if(conn != null) {
                 if (tearDown) {
                     if (DBG) log("cleanUpConnection: teardown, call conn.disconnect");
-                    conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE, reason));
+                    conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE,
+                            conn.getDataConnectionId(), 0, reason));
                     notificationDeferred = true;
                 } else {
                     if (DBG) log("cleanUpConnection: !tearDown, call conn.resetSynchronously");
@@ -407,10 +330,9 @@
     }
 
     private CdmaDataConnection findFreeDataConnection() {
-        for (DataConnection connBase : dataConnectionList) {
-            CdmaDataConnection conn = (CdmaDataConnection) connBase;
-            if (conn.isInactive()) {
-                return conn;
+        for (DataConnection dc : mDataConnections.values()) {
+            if (dc.isInactive()) {
+                return (CdmaDataConnection) dc;
             }
         }
         return null;
@@ -424,7 +346,8 @@
             return false;
         }
 
-        mActiveDataConnection = conn;
+        /** TODO: We probably want the connection being setup to a parameter passed around */
+        mPendingDataConnection = conn;
         String[] types;
         if (mRequestedApnType.equals(Phone.APN_TYPE_DUN)) {
             types = new String[1];
@@ -440,40 +363,43 @@
         conn.connect(msg, mActiveApn);
 
         setState(State.INITING);
-        phone.notifyDataConnection(reason);
+        notifyDataConnection(reason);
         return true;
     }
 
     private void notifyDefaultData(String reason) {
         setState(State.CONNECTED);
-        phone.notifyDataConnection(reason);
+        notifyDataConnection(reason);
         startNetStatPoll();
         mRetryMgr.resetRetryCount();
     }
 
     private void resetPollStats() {
-        txPkts = -1;
-        rxPkts = -1;
-        sentSinceLastRecv = 0;
-        netStatPollPeriod = POLL_NETSTAT_MILLIS;
+        mTxPkts = -1;
+        mRxPkts = -1;
+        mSentSinceLastRecv = 0;
+        mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
         mNoRecvPollCount = 0;
     }
 
+    @Override
     protected void startNetStatPoll() {
-        if (state == State.CONNECTED && netStatPollEnabled == false) {
-            Log.d(LOG_TAG, "[DataConnection] Start poll NetStat");
+        if (mState == State.CONNECTED && mNetStatPollEnabled == false) {
+            log("[DataConnection] Start poll NetStat");
             resetPollStats();
-            netStatPollEnabled = true;
+            mNetStatPollEnabled = true;
             mPollNetStat.run();
         }
     }
 
+    @Override
     protected void stopNetStatPoll() {
-        netStatPollEnabled = false;
+        mNetStatPollEnabled = false;
         removeCallbacks(mPollNetStat);
-        Log.d(LOG_TAG, "[DataConnection] Stop poll NetStat");
+        log("[DataConnection] Stop poll NetStat");
     }
 
+    @Override
     protected void restartRadio() {
         if (DBG) log("Cleanup connection and wait " +
                 (TIME_DELAYED_TO_RESTART_RADIO / 1000) + "s to restart radio");
@@ -490,73 +416,73 @@
 
             Activity newActivity;
 
-            preTxPkts = txPkts;
-            preRxPkts = rxPkts;
+            preTxPkts = mTxPkts;
+            preRxPkts = mRxPkts;
 
-            txPkts = TrafficStats.getMobileTxPackets();
-            rxPkts = TrafficStats.getMobileRxPackets();
+            mTxPkts = TrafficStats.getMobileTxPackets();
+            mRxPkts = TrafficStats.getMobileRxPackets();
 
-            //Log.d(LOG_TAG, "rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts));
+            //log("rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts));
 
-            if (netStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
-                sent = txPkts - preTxPkts;
-                received = rxPkts - preRxPkts;
+            if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
+                sent = mTxPkts - preTxPkts;
+                received = mRxPkts - preRxPkts;
 
                 if ( sent > 0 && received > 0 ) {
-                    sentSinceLastRecv = 0;
+                    mSentSinceLastRecv = 0;
                     newActivity = Activity.DATAINANDOUT;
                 } else if (sent > 0 && received == 0) {
-                    if (phone.getState()  == Phone.State.IDLE) {
-                        sentSinceLastRecv += sent;
+                    if (mPhone.getState()  == Phone.State.IDLE) {
+                        mSentSinceLastRecv += sent;
                     } else {
-                        sentSinceLastRecv = 0;
+                        mSentSinceLastRecv = 0;
                     }
                     newActivity = Activity.DATAOUT;
                 } else if (sent == 0 && received > 0) {
-                    sentSinceLastRecv = 0;
+                    mSentSinceLastRecv = 0;
                     newActivity = Activity.DATAIN;
                 } else if (sent == 0 && received == 0) {
-                    newActivity = (activity == Activity.DORMANT) ? activity : Activity.NONE;
+                    newActivity = (mActivity == Activity.DORMANT) ? mActivity : Activity.NONE;
                 } else {
-                    sentSinceLastRecv = 0;
-                    newActivity = (activity == Activity.DORMANT) ? activity : Activity.NONE;
+                    mSentSinceLastRecv = 0;
+                    newActivity = (mActivity == Activity.DORMANT) ? mActivity : Activity.NONE;
                 }
 
-                if (activity != newActivity) {
-                    activity = newActivity;
-                    phone.notifyDataActivity();
+                if (mActivity != newActivity && mIsScreenOn) {
+                    mActivity = newActivity;
+                    mPhone.notifyDataActivity();
                 }
             }
 
-            if (sentSinceLastRecv >= NUMBER_SENT_PACKETS_OF_HANG) {
+            if (mSentSinceLastRecv >= NUMBER_SENT_PACKETS_OF_HANG) {
                 // Packets sent without ack exceeded threshold.
 
                 if (mNoRecvPollCount == 0) {
                     EventLog.writeEvent(
                             EventLogTags.PDP_RADIO_RESET_COUNTDOWN_TRIGGERED,
-                            sentSinceLastRecv);
+                            mSentSinceLastRecv);
                 }
 
                 if (mNoRecvPollCount < NO_RECV_POLL_LIMIT) {
                     mNoRecvPollCount++;
                     // Slow down the poll interval to let things happen
-                    netStatPollPeriod = POLL_NETSTAT_SLOW_MILLIS;
+                    mNetStatPollPeriod = POLL_NETSTAT_SLOW_MILLIS;
                 } else {
-                    if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) +
+                    if (DBG) log("Sent " + String.valueOf(mSentSinceLastRecv) +
                                         " pkts since last received");
                     // We've exceeded the threshold.  Restart the radio.
-                    netStatPollEnabled = false;
+                    mNetStatPollEnabled = false;
                     stopNetStatPoll();
                     restartRadio();
                     EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, NO_RECV_POLL_LIMIT);
                 }
             } else {
                 mNoRecvPollCount = 0;
-                netStatPollPeriod = POLL_NETSTAT_MILLIS;
+                mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
             }
 
-            if (netStatPollEnabled) {
-                mDataConnectionTracker.postDelayed(this, netStatPollPeriod);
+            if (mNetStatPollEnabled) {
+                mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod);
             }
         }
     };
@@ -588,7 +514,7 @@
     }
 
     private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) {
-        if (state == State.FAILED) {
+        if (mState == State.FAILED) {
             /**
              * For now With CDMA we never try to reconnect on
              * error and instead just continue to retry
@@ -596,15 +522,15 @@
              * TODO: Make this configurable?
              */
             int nextReconnectDelay = mRetryMgr.getRetryTimer();
-            Log.d(LOG_TAG, "Data Connection activate failed. Scheduling next attempt for "
+            log("Data Connection activate failed. Scheduling next attempt for "
                     + (nextReconnectDelay / 1000) + "s");
 
             AlarmManager am =
-                (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE);
+                (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
             Intent intent = new Intent(INTENT_RECONNECT_ALARM);
             intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason);
             mReconnectIntent = PendingIntent.getBroadcast(
-                    phone.getContext(), 0, intent, 0);
+                    mPhone.getContext(), 0, intent, 0);
             am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                     SystemClock.elapsedRealtime() + nextReconnectDelay,
                     mReconnectIntent);
@@ -612,7 +538,7 @@
             mRetryMgr.increaseRetryCount();
 
             if (!shouldPostNotification(lastFailCauseCode)) {
-                Log.d(LOG_TAG,"NOT Posting Data Connection Unavailable notification "
+                log("NOT Posting Data Connection Unavailable notification "
                                 + "-- likely transient error");
             } else {
                 notifyNoData(lastFailCauseCode);
@@ -622,24 +548,25 @@
 
     private void notifyNoData(FailCause lastFailCauseCode) {
         setState(State.FAILED);
+        notifyDataAvailability(null);
     }
 
     private void gotoIdleAndNotifyDataConnection(String reason) {
         if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
         setState(State.IDLE);
-        phone.notifyDataConnection(reason);
+        notifyDataConnection(reason);
         mActiveApn = null;
     }
 
     protected void onRecordsLoaded() {
-        if (state == State.FAILED) {
+        if (mState == State.FAILED) {
             cleanUpConnection(false, null);
         }
         sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED));
     }
 
     protected void onNVReady() {
-        if (state == State.FAILED) {
+        if (mState == State.FAILED) {
             cleanUpConnection(false, null);
         }
         sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
@@ -650,12 +577,13 @@
      */
     @Override
     protected void onEnableNewApn() {
-          cleanUpConnection(true, Phone.REASON_APN_SWITCHED);
+        cleanUpConnection(true, Phone.REASON_APN_SWITCHED);
     }
 
     /**
      * @override com.android.internal.telephony.DataConnectionTracker
      */
+    @Override
     protected boolean onTrySetupData(String reason) {
         return trySetupData(reason);
     }
@@ -663,6 +591,7 @@
     /**
      * @override com.android.internal.telephony.DataConnectionTracker
      */
+    @Override
     protected void onRoamingOff() {
         trySetupData(Phone.REASON_ROAMING_OFF);
     }
@@ -670,6 +599,7 @@
     /**
      * @override com.android.internal.telephony.DataConnectionTracker
      */
+    @Override
     protected void onRoamingOn() {
         if (getDataOnRoamingEnabled()) {
             trySetupData(Phone.REASON_ROAMING_ON);
@@ -682,17 +612,20 @@
     /**
      * @override com.android.internal.telephony.DataConnectionTracker
      */
+    @Override
     protected void onRadioAvailable() {
-        if (phone.getSimulatedRadioControl() != null) {
+        if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
             // FIXME  this can be improved
             setState(State.CONNECTED);
-            phone.notifyDataConnection(null);
+            notifyDataConnection(null);
 
-            Log.i(LOG_TAG, "We're on the simulator; assuming data is connected");
+            log("We're on the simulator; assuming data is connected");
         }
 
-        if (state != State.IDLE) {
+        notifyDataAvailability(null);
+
+        if (mState != State.IDLE) {
             cleanUpConnection(true, null);
         }
     }
@@ -700,13 +633,14 @@
     /**
      * @override com.android.internal.telephony.DataConnectionTracker
      */
+    @Override
     protected void onRadioOffOrNotAvailable() {
         mRetryMgr.resetRetryCount();
 
-        if (phone.getSimulatedRadioControl() != null) {
+        if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
             // FIXME  this can be improved
-            Log.i(LOG_TAG, "We're on the simulator; assuming radio off is meaningless");
+            log("We're on the simulator; assuming radio off is meaningless");
         } else {
             if (DBG) log("Radio is off and clean up all connection");
             cleanUpConnection(false, Phone.REASON_RADIO_TURNED_OFF);
@@ -716,6 +650,7 @@
     /**
      * @override com.android.internal.telephony.DataConnectionTracker
      */
+    @Override
     protected void onDataSetupComplete(AsyncResult ar) {
         String reason = null;
         if (ar.userObj instanceof String) {
@@ -723,6 +658,10 @@
         }
 
         if (ar.exception == null) {
+            // TODO: We should clear LinkProperties/Capabilities when torn down or disconnected
+            mLinkProperties = getLinkProperties(mPendingDataConnection);
+            mLinkCapabilities = getLinkCapabilities(mPendingDataConnection);
+
             // everything is setup
             notifyDefaultData(reason);
         } else {
@@ -741,8 +680,9 @@
     /**
      * Called when EVENT_DISCONNECT_DONE is received.
      */
-    protected void onDisconnectDone(AsyncResult ar) {
-        if(DBG) log("EVENT_DISCONNECT_DONE");
+    @Override
+    protected void onDisconnectDone(int connId, AsyncResult ar) {
+        if(DBG) log("EVENT_DISCONNECT_DONE connId=" + connId);
         String reason = null;
         if (ar.userObj instanceof String) {
             reason = (String) ar.userObj;
@@ -762,7 +702,7 @@
             onRestartRadio();
         }
 
-        phone.notifyDataConnection(reason);
+        notifyDataConnection(reason);
         mActiveApn = null;
         if (retryAfterDisconnected(reason)) {
           trySetupData(reason);
@@ -786,25 +726,29 @@
     /**
      * @override com.android.internal.telephony.DataConnectionTracker
      */
+    @Override
     protected void onVoiceCallStarted() {
-        if (state == State.CONNECTED && !mCdmaPhone.mSST.isConcurrentVoiceAndData()) {
+        if (mState == State.CONNECTED && !mCdmaPhone.mSST.isConcurrentVoiceAndData()) {
             stopNetStatPoll();
-            phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
+            notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
+            notifyDataAvailability(Phone.REASON_VOICE_CALL_STARTED);
         }
     }
 
     /**
      * @override com.android.internal.telephony.DataConnectionTracker
      */
+    @Override
     protected void onVoiceCallEnded() {
-        if (state == State.CONNECTED) {
+        if (mState == State.CONNECTED) {
             if (!mCdmaPhone.mSST.isConcurrentVoiceAndData()) {
                 startNetStatPoll();
-                phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
+                notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
             } else {
                 // clean slate after call end.
                 resetPollStats();
             }
+            notifyDataAvailability(Phone.REASON_VOICE_CALL_ENDED);
         } else {
             mRetryMgr.resetRetryCount();
             // in case data setup was attempted when we were on a voice call
@@ -812,39 +756,49 @@
         }
     }
 
-    /**
-     * @override com.android.internal.telephony.DataConnectionTracker
-     */
+    @Override
     protected void onCleanUpConnection(boolean tearDown, String reason) {
         cleanUpConnection(tearDown, reason);
     }
 
     private void createAllDataConnectionList() {
-       dataConnectionList = new ArrayList<DataConnection>();
         CdmaDataConnection dataConn;
 
-       for (int i = 0; i < DATA_CONNECTION_POOL_SIZE; i++) {
-            dataConn = CdmaDataConnection.makeDataConnection(mCdmaPhone);
-            dataConnectionList.add(dataConn);
-       }
+        /** TODO: Use one retry manager for all connections for now */
+        RetryManager rm = mRetryMgr;
+        if (!rm.configure(SystemProperties.get("ro.cdma.data_retry_config"))) {
+            if (!rm.configure(DEFAULT_DATA_RETRY_CONFIG)) {
+                // Should never happen, log an error and default to a simple linear sequence.
+                log("Could not configure using DEFAULT_DATA_RETRY_CONFIG="
+                        + DEFAULT_DATA_RETRY_CONFIG);
+                rm.configure(20, 2000, 1000);
+            }
+        }
+
+        for (int i = 0; i < DATA_CONNECTION_POOL_SIZE; i++) {
+            int id = mUniqueIdGenerator.getAndIncrement();
+
+            dataConn = CdmaDataConnection.makeDataConnection(mCdmaPhone, id, rm);
+            mDataConnections.put(id, dataConn);
+        }
     }
 
     private void destroyAllDataConnectionList() {
-        if(dataConnectionList != null) {
-            dataConnectionList.removeAll(dataConnectionList);
+        if(mDataConnections != null) {
+            mDataConnections.clear();
         }
     }
 
     private void onCdmaDataDetached() {
-        if (state == State.CONNECTED) {
+        if (mState == State.CONNECTED) {
             startNetStatPoll();
-            phone.notifyDataConnection(Phone.REASON_CDMA_DATA_DETACHED);
+            notifyDataConnection(Phone.REASON_CDMA_DATA_DETACHED);
         } else {
-            if (state == State.FAILED) {
+            if (mState == State.FAILED) {
                 cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED);
                 mRetryMgr.resetRetryCount();
 
-                CdmaCellLocation loc = (CdmaCellLocation)(phone.getCellLocation());
+                CdmaCellLocation loc = (CdmaCellLocation)(mPhone.getCellLocation());
                 EventLog.writeEvent(EventLogTags.CDMA_DATA_SETUP_FAILED,
                         loc != null ? loc.getBaseStationId() : -1,
                         TelephonyManager.getDefault().getNetworkType());
@@ -871,8 +825,8 @@
 
     private void onRestartRadio() {
         if (mPendingRestartRadio) {
-            Log.d(LOG_TAG, "************TURN OFF RADIO**************");
-            phone.mCM.setRadioPower(false, null);
+            log("************TURN OFF RADIO**************");
+            mPhone.mCM.setRadioPower(false, null);
             /* Note: no need to call setRadioPower(true).  Assuming the desired
              * radio power state is still ON (as tracked by ServiceStateTracker),
              * ServiceStateTracker will call setRadioPower when it receives the
@@ -885,7 +839,7 @@
     }
 
     private void writeEventLogCdmaDataDrop() {
-        CdmaCellLocation loc = (CdmaCellLocation)(phone.getCellLocation());
+        CdmaCellLocation loc = (CdmaCellLocation)(mPhone.getCellLocation());
         EventLog.writeEvent(EventLogTags.CDMA_DATA_DROP,
                 loc != null ? loc.getBaseStationId() : -1,
                 TelephonyManager.getDefault().getNetworkType());
@@ -901,7 +855,7 @@
             return;
         }
 
-        if (state == State.CONNECTED) {
+        if (mState == State.CONNECTED) {
             boolean isActiveOrDormantConnectionPresent = false;
             int connectionState = DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE;
 
@@ -917,7 +871,7 @@
 
             if (!isActiveOrDormantConnectionPresent) {
                 // No active or dormant connection
-                Log.i(LOG_TAG, "onDataStateChanged: No active connection"
+                log("onDataStateChanged: No active connection"
                         + "state is CONNECTED, disconnecting/cleanup");
                 writeEventLogCdmaDataDrop();
                 cleanUpConnection(true, null);
@@ -926,70 +880,39 @@
 
             switch (connectionState) {
                 case DATA_CONNECTION_ACTIVE_PH_LINK_UP:
-                    Log.v(LOG_TAG, "onDataStateChanged: active=LINK_ACTIVE && CONNECTED, ignore");
-                    activity = Activity.NONE;
-                    phone.notifyDataActivity();
+                    log("onDataStateChanged: active=LINK_ACTIVE && CONNECTED, ignore");
+                    mActivity = Activity.NONE;
+                    mPhone.notifyDataActivity();
                     startNetStatPoll();
                     break;
 
                 case DATA_CONNECTION_ACTIVE_PH_LINK_DOWN:
-                    Log.v(LOG_TAG, "onDataStateChanged active=LINK_DOWN && CONNECTED, dormant");
-                    activity = Activity.DORMANT;
-                    phone.notifyDataActivity();
+                    log("onDataStateChanged active=LINK_DOWN && CONNECTED, dormant");
+                    mActivity = Activity.DORMANT;
+                    mPhone.notifyDataActivity();
                     stopNetStatPoll();
                     break;
 
                 default:
-                    Log.v(LOG_TAG, "onDataStateChanged: IGNORE unexpected DataCallState.active="
+                    log("onDataStateChanged: IGNORE unexpected DataCallState.active="
                             + connectionState);
             }
         } else {
             // TODO: Do we need to do anything?
-            Log.i(LOG_TAG, "onDataStateChanged: not connected, state=" + state + " ignoring");
+            log("onDataStateChanged: not connected, state=" + mState + " ignoring");
         }
     }
 
-    protected String getInterfaceName(String apnType) {
-        if (mActiveDataConnection != null) {
-            return mActiveDataConnection.getInterface();
-        }
-        return null;
-    }
-
-    protected String getIpAddress(String apnType) {
-        if (mActiveDataConnection != null) {
-            return mActiveDataConnection.getIpAddress();
-        }
-        return null;
-    }
-
-    protected String getGateway(String apnType) {
-        if (mActiveDataConnection != null) {
-            return mActiveDataConnection.getGatewayAddress();
-        }
-        return null;
-    }
-
-    protected String[] getDnsServers(String apnType) {
-        if (mActiveDataConnection != null) {
-            return mActiveDataConnection.getDnsServers();
-        }
-        return null;
-    }
-
-    public ArrayList<DataConnection> getAllDataConnections() {
-        return dataConnectionList;
-    }
-
     private void startDelayedRetry(FailCause cause, String reason) {
         notifyNoData(cause);
         reconnectAfterFail(cause, reason);
     }
 
+    @Override
     public void handleMessage (Message msg) {
 
-        if (!phone.mIsTheCurrentActivePhone) {
-            Log.d(LOG_TAG, "Ignore CDMA msgs since CDMA phone is inactive");
+        if (!mPhone.mIsTheCurrentActivePhone) {
+            log("Ignore CDMA msgs since CDMA phone is inactive");
             return;
         }
 
@@ -1026,7 +949,13 @@
         }
     }
 
+    @Override
     protected void log(String s) {
         Log.d(LOG_TAG, "[CdmaDataConnectionTracker] " + s);
     }
+
+    @Override
+    protected void loge(String s) {
+        Log.e(LOG_TAG, "[CdmaDataConnectionTracker] " + s);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index dceff2a..37a33bc 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -70,6 +70,7 @@
      * @param ar AsyncResult passed into the message handler.  ar.result should
      *           be a String representing the status report PDU, as ASCII hex.
      */
+    @Override
     protected void handleStatusReport(AsyncResult ar) {
         Log.d(TAG, "handleStatusReport is a special GSM function, should never be called in CDMA!");
     }
@@ -92,6 +93,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected int dispatchMessage(SmsMessageBase smsb) {
 
         // If sms is null, means there was a parsing error.
@@ -335,6 +337,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendData(String destAddr, String scAddr, int destPort,
             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
@@ -343,6 +346,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendText(String destAddr, String scAddr, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
@@ -351,6 +355,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendMultipartText(String destAddr, String scAddr,
             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
             ArrayList<PendingIntent> deliveryIntents) {
@@ -359,7 +364,7 @@
          * TODO(cleanup): There is no real code difference between
          * this and the GSM version, and hence it should be moved to
          * the base class or consolidated somehow, provided calling
-         * the proper submitpdu stuff can be arranged.
+         * the proper submit pdu stuff can be arranged.
          */
 
         int refNumber = getNextConcatenatedRef() & 0x00FF;
@@ -432,8 +437,9 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendSms(SmsTracker tracker) {
-        HashMap map = tracker.mData;
+        HashMap<String, Object> map = tracker.mData;
 
         // byte smsc[] = (byte[]) map.get("smsc");  // unused for CDMA
         byte pdu[] = (byte[]) map.get("pdu");
@@ -444,11 +450,13 @@
     }
 
      /** {@inheritDoc} */
+    @Override
     protected void sendMultipartSms (SmsTracker tracker) {
         Log.d(TAG, "TODO: CdmaSMSDispatcher.sendMultipartSms not implemented");
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){
         // FIXME unit test leaves cm == null. this should change
 
@@ -469,16 +477,19 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void activateCellBroadcastSms(int activate, Message response) {
         mCm.setCdmaBroadcastActivation((activate == 0), response);
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void getCellBroadcastSmsConfig(Message response) {
         mCm.getCdmaBroadcastConfig(response);
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void setCellBroadcastConfig(int[] configValuesArray, Message response) {
         mCm.setCdmaBroadcastConfig(configValuesArray, response);
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index d2a4bd8..11e04d4 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -16,6 +16,17 @@
 
 package com.android.internal.telephony.cdma;
 
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.DataConnectionTracker;
+import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+
 import android.app.AlarmManager;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -38,19 +49,8 @@
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
-import android.util.Config;
 import android.util.TimeUtils;
 
-import com.android.internal.telephony.CommandException;
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.DataConnectionTracker;
-import com.android.internal.telephony.EventLogTags;
-import com.android.internal.telephony.IccCard;
-import com.android.internal.telephony.MccTable;
-import com.android.internal.telephony.ServiceStateTracker;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Date;
@@ -66,6 +66,13 @@
     CdmaCellLocation cellLoc;
     CdmaCellLocation newCellLoc;
 
+    // Min values used to by getOtasp()
+    private static final String UNACTIVATED_MIN2_VALUE = "000000";
+    private static final String UNACTIVATED_MIN_VALUE = "1111110111";
+
+    // Current Otasp value
+    int mCurrentOtaspMode = OTASP_UNINITIALIZED;
+
      /** if time between NITZ updates is less than mNitzUpdateSpacing the update may be ignored. */
     private static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10;
     private int mNitzUpdateSpacing = SystemProperties.getInt("ro.nitz_update_spacing",
@@ -447,6 +454,13 @@
                     if (!mIsMinInfoReady) {
                         mIsMinInfoReady = true;
                     }
+                    int otaspMode = getOtasp();
+                    if (mCurrentOtaspMode != otaspMode) {
+                        Log.d(LOG_TAG, "call phone.notifyOtaspChanged old otaspMode=" +
+                                mCurrentOtaspMode + " new otaspMode=" + otaspMode);
+                        mCurrentOtaspMode = otaspMode;
+                        phone.notifyOtaspChanged(mCurrentOtaspMode);
+                    }
                     phone.getIccCard().broadcastIccStateChangedIntent(IccCard.INTENT_VALUE_ICC_IMSI,
                             null);
                 } else {
@@ -643,6 +657,11 @@
         curPlmn = plmn;
     }
 
+    @Override
+    protected Phone getPhone() {
+        return phone;
+    }
+
     /**
      * Handle the result of one of the pollState()-related requests
      */
@@ -1008,7 +1027,7 @@
         mNeedFixZone = false;
 
         if (zone != null) {
-            if (getAutoTime()) {
+            if (getAutoTimeZone()) {
                 setAndBroadcastNetworkSetTimeZone(zone.getID());
             }
             saveNitzTimeZone(zone.getID());
@@ -1140,7 +1159,7 @@
         }
 
         if (hasCdmaDataConnectionChanged || hasNetworkTypeChanged) {
-            phone.notifyDataConnection(null);
+            phone.notifyDataConnection(null, null);
         }
 
         if (hasRoamingOn) {
@@ -1460,7 +1479,7 @@
             }
 
             if (zone != null) {
-                if (getAutoTime()) {
+                if (getAutoTimeZone()) {
                     setAndBroadcastNetworkSetTimeZone(zone.getID());
                 }
                 saveNitzTimeZone(zone.getID());
@@ -1550,6 +1569,14 @@
         }
     }
 
+    private boolean getAutoTimeZone() {
+        try {
+            return Settings.System.getInt(cr, Settings.System.AUTO_TIME_ZONE) > 0;
+        } catch (SettingNotFoundException snfe) {
+            return true;
+        }
+    }
+
     private void saveNitzTimeZone(String zoneId) {
         mSavedTimeZone = zoneId;
     }
@@ -1634,10 +1661,6 @@
         return false;
     }
 
-    protected void log(String s) {
-        Log.d(LOG_TAG, "[CdmaServiceStateTracker] " + s);
-    }
-
     public String getMdnNumber() {
         return mMdn;
     }
@@ -1693,6 +1716,32 @@
         }
     }
 
+    /**
+     * Returns OTASP_UNKNOWN, OTASP_NEEDED or OTASP_NOT_NEEDED
+     */
+    int getOtasp() {
+        int provisioningState;
+        if (mMin == null || (mMin.length() < 6)) {
+            if (DBG) Log.d(LOG_TAG, "getOtasp: bad mMin='" + mMin + "'");
+            provisioningState = OTASP_UNKNOWN;
+        } else {
+            if ((mMin.equals(UNACTIVATED_MIN_VALUE)
+                    || mMin.substring(0,6).equals(UNACTIVATED_MIN2_VALUE))
+                    || SystemProperties.getBoolean("test_cdma_setup", false)) {
+                provisioningState = OTASP_NEEDED;
+            } else {
+                provisioningState = OTASP_NOT_NEEDED;
+            }
+        }
+        if (DBG) Log.d(LOG_TAG, "getOtasp: state=" + provisioningState);
+        return provisioningState;
+    }
+
+    @Override
+    protected void log(String s) {
+        Log.d(LOG_TAG, "[CdmaServiceStateTracker] " + s);
+    }
+
     private void hangupAndPowerOff() {
         // hang up all active voice calls
         phone.mCT.ringingCall.hangupIfAlive();
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
index e97549d..d84b6ab 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
@@ -68,8 +68,7 @@
                     ar = (AsyncResult)msg.obj;
                     synchronized (mLock) {
                         if (ar.exception == null) {
-                            mSms  = (List<SmsRawData>)
-                                    buildValidRawData((ArrayList<byte[]>) ar.result);
+                            mSms = buildValidRawData((ArrayList<byte[]>) ar.result);
                         } else {
                             if(DBG) log("Cannot load Sms records");
                             if (mSms != null)
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index d563dc8..8accfd1 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -18,11 +18,8 @@
 
 import android.os.Parcel;
 import android.os.SystemProperties;
-import android.text.format.Time;
 import android.util.Config;
 import android.util.Log;
-import com.android.internal.telephony.EncodeException;
-import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.IccUtils;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
@@ -34,7 +31,6 @@
 import com.android.internal.telephony.cdma.sms.UserData;
 import com.android.internal.util.HexDump;
 
-import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index ab79fe9..cf06dab 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -21,7 +21,6 @@
 import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
 
 import android.util.Log;
-import android.util.SparseIntArray;
 
 import android.telephony.SmsMessage;
 
@@ -30,10 +29,8 @@
 import com.android.internal.telephony.IccUtils;
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.SmsHeader;
-import com.android.internal.telephony.cdma.sms.UserData;
 import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
 
-import com.android.internal.util.HexDump;
 import com.android.internal.util.BitwiseInputStream;
 import com.android.internal.util.BitwiseOutputStream;
 
@@ -45,13 +42,13 @@
     private final static String LOG_TAG = "SMS";
 
     /**
-     * Bearer Data Subparameter Indentifiers
+     * Bearer Data Subparameter Identifiers
      * (See 3GPP2 C.S0015-B, v2.0, table 4.5-1)
      * NOTE: Commented subparameter types are not implemented.
      */
     private final static byte SUBPARAM_MESSAGE_IDENTIFIER               = 0x00;
     private final static byte SUBPARAM_USER_DATA                        = 0x01;
-    private final static byte SUBPARAM_USER_REPONSE_CODE                = 0x02;
+    private final static byte SUBPARAM_USER_RESPONSE_CODE               = 0x02;
     private final static byte SUBPARAM_MESSAGE_CENTER_TIME_STAMP        = 0x03;
     private final static byte SUBPARAM_VALIDITY_PERIOD_ABSOLUTE         = 0x04;
     private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE         = 0x05;
@@ -697,7 +694,7 @@
     /*
      * TODO(cleanup): CdmaSmsAddress encoding should make use of
      * CdmaSmsAddress.parse provided that DTMF encoding is unified,
-     * and the difference in 4bit vs 8bit is resolved.
+     * and the difference in 4-bit vs. 8-bit is resolved.
      */
 
     private static void encodeCdmaSmsAddress(CdmaSmsAddress addr) throws CodingException {
@@ -805,6 +802,7 @@
      * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
      *
      * @param bData an instance of BearerData.
+     *
      * @return byte array of raw encoded SMS bearer data.
      */
     public static byte[] encode(BearerData bData) {
@@ -915,7 +913,7 @@
     private static String decodeUtf16(byte[] data, int offset, int numFields)
         throws CodingException
     {
-        // Start reading from the next 16-bit aligned boundry after offset.
+        // Start reading from the next 16-bit aligned boundary after offset.
         int padding = offset % 2;
         numFields -= (offset + padding) / 2;
         try {
@@ -961,7 +959,7 @@
     private static String decode7bitGsm(byte[] data, int offset, int numFields)
         throws CodingException
     {
-        // Start reading from the next 7-bit aligned boundry after offset.
+        // Start reading from the next 7-bit aligned boundary after offset.
         int offsetBits = offset * 8;
         int offsetSeptets = (offsetBits + 6) / 7;
         numFields -= offsetSeptets;
@@ -1554,7 +1552,7 @@
                 case SUBPARAM_USER_DATA:
                     decodeSuccess = decodeUserData(bData, inStream);
                     break;
-                case SUBPARAM_USER_REPONSE_CODE:
+                case SUBPARAM_USER_RESPONSE_CODE:
                     decodeSuccess = decodeUserResponseCode(bData, inStream);
                     break;
                 case SUBPARAM_REPLY_OPTION:
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
index 4f00163..4a60231 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
@@ -19,7 +19,7 @@
 
 import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress;
 
-public final class SmsEnvelope{
+public final class SmsEnvelope {
     /**
      * Message Types
      * (See 3GPP2 C.S0015-B 3.4.1)
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 3959c67..fc03d1a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -287,7 +287,7 @@
         return mPendingMMIs;
     }
 
-    public DataState getDataConnectionState() {
+    public DataState getDataConnectionState(String apnType) {
         DataState ret = DataState.DISCONNECTED;
 
         if (mSST == null) {
@@ -300,6 +300,8 @@
             // If we're out of service, open TCP sockets may still work
             // but no data will flow
             ret = DataState.DISCONNECTED;
+        } else if (mDataConnection.isApnTypeEnabled(apnType) == false) {
+            ret = DataState.DISCONNECTED;
         } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */
             switch (mDataConnection.getState()) {
                 case FAILED:
@@ -401,8 +403,8 @@
     }
 
     /*package*/ void
-    notifyDataConnectionFailed(String reason) {
-        mNotifier.notifyDataConnectionFailed(this, reason);
+    notifyDataConnectionFailed(String reason, String apnType) {
+        mNotifier.notifyDataConnectionFailed(this, reason, apnType);
     }
 
     /*package*/ void
@@ -1091,27 +1093,6 @@
     }
 
     /**
-     * The only circumstances under which we report that data connectivity is not
-     * possible are
-     * <ul>
-     * <li>Data roaming is disallowed and we are roaming.</li>
-     * <li>The current data state is {@code DISCONNECTED} for a reason other than
-     * having explicitly disabled connectivity. In other words, data is not available
-     * because the phone is out of coverage or some like reason.</li>
-     * </ul>
-     * @return {@code true} if data connectivity is possible, {@code false} otherwise.
-     */
-    public boolean isDataConnectivityPossible() {
-        // TODO: Currently checks if any GPRS connection is active. Should it only
-        // check for "default"?
-        boolean noData = mDataConnection.getDataEnabled() &&
-            getDataConnectionState() == DataState.DISCONNECTED;
-        return !noData && getIccCard().getState() == SimCard.State.READY &&
-                getServiceState().getState() == ServiceState.STATE_IN_SERVICE &&
-            (mDataConnection.getDataOnRoamingEnabled() || !getServiceState().getRoaming());
-    }
-
-    /**
      * Removes the given MMI from the pending list and notifies
      * registrants that it is complete.
      * @param mmi MMI that is done
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
index 09d46dd..d428099 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnection.java
@@ -23,6 +23,7 @@
 import com.android.internal.telephony.DataConnection;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.RetryManager;
 
 /**
  * {@hide}
@@ -53,23 +54,27 @@
     private ApnSetting apn;
 
     //***** Constructor
-    private GsmDataConnection(GSMPhone phone, String name) {
-        super(phone, name);
+    private GsmDataConnection(GSMPhone phone, String name, RetryManager rm) {
+        super(phone, name, rm);
     }
 
     /**
      * Create the connection object
      *
-     * @param phone
+     * @param phone the Phone
+     * @param id the connection id
+     * @param rm the RetryManager
      * @return GsmDataConnection that was created.
      */
-    static GsmDataConnection makeDataConnection(GSMPhone phone) {
+    static GsmDataConnection makeDataConnection(GSMPhone phone, int id, RetryManager rm) {
         synchronized (mCountLock) {
             mCount += 1;
         }
-        GsmDataConnection gsmDc = new GsmDataConnection(phone, "GsmDataConnection-" + mCount);
+        GsmDataConnection gsmDc = new GsmDataConnection(phone, "GsmDataConnection-" + mCount, rm);
         gsmDc.start();
         if (DBG) gsmDc.log("Made " + gsmDc.getName());
+        gsmDc.mId = id;
+        gsmDc.mRetryMgr = rm;
         return gsmDc;
     }
 
@@ -180,8 +185,8 @@
 
     @Override
     protected boolean isDnsOk(String[] domainNameServers) {
-        if (NULL_IP.equals(dnsServers[0]) && NULL_IP.equals(dnsServers[1])
-                    && !((GSMPhone) phone).isDnsCheckDisabled()) {
+        if (NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1])
+                && !((GSMPhone) phone).isDnsCheckDisabled()) {
             // Work around a race condition where QMI does not fill in DNS:
             // Deactivate PDP and let DataConnectionTracker retry.
             // Do not apply the race condition workaround for MMS APN
@@ -189,6 +194,9 @@
             // Otherwise, the default APN will not be restored anymore.
             if (!apn.types[0].equals(Phone.APN_TYPE_MMS)
                 || !isIpAddress(apn.mmsProxy)) {
+                log(String.format(
+                        "isDnsOk: return false apn.types[0]=%s APN_TYPE_MMS=%s isIpAddress(%s)=%s",
+                        apn.types[0], Phone.APN_TYPE_MMS, apn.mmsProxy, isIpAddress(apn.mmsProxy)));
                 return false;
             }
         }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index e7d57bc..2aca9ad 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -18,28 +18,19 @@
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
 import android.database.ContentObserver;
 import android.database.Cursor;
-import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
-import android.net.NetworkInfo;
+import android.net.ProxyProperties;
 import android.net.TrafficStats;
 import android.net.Uri;
-import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
 import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.provider.Telephony;
 import android.telephony.ServiceState;
@@ -58,7 +49,11 @@
 import com.android.internal.telephony.DataConnection.FailCause;
 
 import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.HashMap;
 
 /**
  * {@hide}
@@ -83,9 +78,6 @@
 
     //***** Instance Variables
 
-    // Indicates baseband will not auto-attach
-    private boolean noAutoAttach = false;
-
     private boolean mReregisterOnReconnectFailure = false;
     private ContentResolver mResolver;
 
@@ -93,13 +85,12 @@
     // Count of PDP reset attempts; reset when we see incoming,
     // call reRegisterNetwork, or pingTest succeeds.
     private int mPdpResetCount = 0;
-    private boolean mIsScreenOn = true;
 
     /** Delay between APN attempts */
     protected static final int APN_DELAY_MILLIS = 5000;
 
     //useful for debugging
-    boolean failNextConnect = false;
+    boolean mFailNextConnect = false;
 
     /**
      * allApns holds all apns for this sim spn, retrieved from
@@ -107,104 +98,49 @@
      *
      * Create once after simcard info is loaded
      */
-    private ArrayList<ApnSetting> allApns = null;
+    private ArrayList<ApnSetting> mAllApns = null;
 
     /**
      * waitingApns holds all apns that are waiting to be connected
      *
      * It is a subset of allApns and has the same format
      */
-    private ArrayList<ApnSetting> waitingApns = null;
+    private ArrayList<ApnSetting> mWaitingApns = null;
 
-    private ApnSetting preferredApn = null;
+    private ApnSetting mPreferredApn = null;
 
     /* Currently active APN */
     protected ApnSetting mActiveApn;
 
-    /**
-     * pdpList holds all the PDP connection, i.e. IP Link in GPRS
-     */
-    private ArrayList<DataConnection> pdpList;
+      /** The DataConnection being setup */
+    private GsmDataConnection mPendingDataConnection;
 
-    /** Currently active DataConnection */
-    private GsmDataConnection mActivePdp;
+    /** Convert an ApnType string to Id (TODO: Use "enumeration" instead of String for ApnType) */
+    private HashMap<String, Integer> mApnToDataConnectionId =
+                                    new HashMap<String, Integer>();
 
     /** Is packet service restricted by network */
     private boolean mIsPsRestricted = false;
 
     //***** Constants
 
-    // TODO: Increase this to match the max number of simultaneous
-    // PDP contexts we plan to support.
-    /**
-     * Pool size of DataConnection objects.
-     */
-    private static final int PDP_CONNECTION_POOL_SIZE = 1;
-
     private static final int POLL_PDP_MILLIS = 5 * 1000;
 
     private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.gprs-reconnect";
-    private static final String INTENT_RECONNECT_ALARM_EXTRA_REASON = "reason";
 
     static final Uri PREFERAPN_URI = Uri.parse("content://telephony/carriers/preferapn");
     static final String APN_ID = "apn_id";
     private boolean canSetPreferApn = false;
 
-    // for tracking retries on the default APN
-    private RetryManager mDefaultRetryManager;
-    // for tracking retries on a secondary APN
-    private RetryManager mSecondaryRetryManager;
-
-    BroadcastReceiver mIntentReceiver = new BroadcastReceiver ()
-    {
-        @Override
-        public void onReceive(Context context, Intent intent)
-        {
-            String action = intent.getAction();
-            if (action.equals(Intent.ACTION_SCREEN_ON)) {
-                mIsScreenOn = true;
-                stopNetStatPoll();
-                startNetStatPoll();
-            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                mIsScreenOn = false;
-                stopNetStatPoll();
-                startNetStatPoll();
-            } else if (action.equals((INTENT_RECONNECT_ALARM))) {
-                Log.d(LOG_TAG, "GPRS reconnect alarm. Previous state was " + state);
-
-                String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
-                if (state == State.FAILED) {
-                    Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
-                    msg.arg1 = 0; // tearDown is false
-                    msg.obj = (String) reason;
-                    sendMessage(msg);
-                }
-                sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
-            } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-                final android.net.NetworkInfo networkInfo = (NetworkInfo)
-                        intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-                mIsWifiConnected = (networkInfo != null && networkInfo.isConnected());
-            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-                final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                        WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
-
-                if (!enabled) {
-                    // when wifi got disabled, the NETWORK_STATE_CHANGED_ACTION
-                    // quit and won't report disconnected til next enabling.
-                    mIsWifiConnected = false;
-                }
-            }
-        }
-    };
-
     /** Watches for changes to the APN db. */
-    private ApnChangeObserver apnObserver;
+    private ApnChangeObserver mApnObserver;
 
     //***** Constructor
 
     GsmDataConnectionTracker(GSMPhone p) {
         super(p);
         mGsmPhone = p;
+
         p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
         p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
@@ -218,73 +154,26 @@
         p.mSST.registerForPsRestrictedEnabled(this, EVENT_PS_RESTRICT_ENABLED, null);
         p.mSST.registerForPsRestrictedDisabled(this, EVENT_PS_RESTRICT_DISABLED, null);
 
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(INTENT_RECONNECT_ALARM);
-        filter.addAction(Intent.ACTION_SCREEN_ON);
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-
-        // TODO: Why is this registering the phone as the receiver of the intent
-        //       and not its own handler?
-        p.getContext().registerReceiver(mIntentReceiver, filter, null, p);
-
-
         mDataConnectionTracker = this;
-        mResolver = phone.getContext().getContentResolver();
+        mResolver = mPhone.getContext().getContentResolver();
 
-        apnObserver = new ApnChangeObserver();
+        mApnObserver = new ApnChangeObserver();
         p.getContext().getContentResolver().registerContentObserver(
-                Telephony.Carriers.CONTENT_URI, true, apnObserver);
+                Telephony.Carriers.CONTENT_URI, true, mApnObserver);
 
-        createAllPdpList();
-
-        // This preference tells us 1) initial condition for "dataEnabled",
-        // and 2) whether the RIL will setup the baseband to auto-PS attach.
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext());
-        boolean dataEnabledSetting = true;
-        try {
-            dataEnabledSetting = IConnectivityManager.Stub.asInterface(ServiceManager.
-                getService(Context.CONNECTIVITY_SERVICE)).getMobileDataEnabled();
-        } catch (Exception e) {
-            // nothing to do - use the old behavior and leave data on
-        }
-        dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false) &&
-                dataEnabledSetting;
-        if (dataEnabled[APN_DEFAULT_ID]) {
-            enabledCount++;
-        }
-        noAutoAttach = !dataEnabled[APN_DEFAULT_ID];
-
-        if (!mRetryMgr.configure(SystemProperties.get("ro.gsm.data_retry_config"))) {
-            if (!mRetryMgr.configure(DEFAULT_DATA_RETRY_CONFIG)) {
-                // Should never happen, log an error and default to a simple linear sequence.
-                Log.e(LOG_TAG, "Could not configure using DEFAULT_DATA_RETRY_CONFIG="
-                        + DEFAULT_DATA_RETRY_CONFIG);
-                mRetryMgr.configure(20, 2000, 1000);
-            }
-        }
-
-        mDefaultRetryManager = mRetryMgr;
-        mSecondaryRetryManager = new RetryManager();
-
-        if (!mSecondaryRetryManager.configure(SystemProperties.get(
-                "ro.gsm.2nd_data_retry_config"))) {
-            if (!mSecondaryRetryManager.configure(SECONDARY_DATA_RETRY_CONFIG)) {
-                // Should never happen, log an error and default to a simple sequence.
-                Log.e(LOG_TAG, "Could note configure using SECONDARY_DATA_RETRY_CONFIG="
-                        + SECONDARY_DATA_RETRY_CONFIG);
-                mSecondaryRetryManager.configure("max_retries=3, 333, 333, 333");
-            }
-        }
+        /** Create the default connection */
+        createDataConnection(Phone.APN_TYPE_DEFAULT);
     }
 
+    @Override
     public void dispose() {
+        super.dispose();
+
         //Unregister for all events
-        phone.mCM.unregisterForAvailable(this);
-        phone.mCM.unregisterForOffOrNotAvailable(this);
+        mPhone.mCM.unregisterForAvailable(this);
+        mPhone.mCM.unregisterForOffOrNotAvailable(this);
         mGsmPhone.mSIMRecords.unregisterForRecordsLoaded(this);
-        phone.mCM.unregisterForDataStateChanged(this);
+        mPhone.mCM.unregisterForDataStateChanged(this);
         mGsmPhone.mCT.unregisterForVoiceCallEnded(this);
         mGsmPhone.mCT.unregisterForVoiceCallStarted(this);
         mGsmPhone.mSST.unregisterForGprsAttached(this);
@@ -294,29 +183,36 @@
         mGsmPhone.mSST.unregisterForPsRestrictedEnabled(this);
         mGsmPhone.mSST.unregisterForPsRestrictedDisabled(this);
 
-        phone.getContext().unregisterReceiver(this.mIntentReceiver);
-        phone.getContext().getContentResolver().unregisterContentObserver(this.apnObserver);
+        mPhone.getContext().getContentResolver().unregisterContentObserver(this.mApnObserver);
 
-        destroyAllPdpList();
+        destroyDataConnections();
     }
 
+    @Override
     protected void finalize() {
-        if(DBG) Log.d(LOG_TAG, "GsmDataConnectionTracker finalized");
+        if(DBG) log("finalize");
     }
 
+    @Override
+    protected String getActionIntentReconnectAlarm() {
+        return INTENT_RECONNECT_ALARM;
+    }
+
+    @Override
     protected void setState(State s) {
         if (DBG) log ("setState: " + s);
-        if (state != s) {
-            EventLog.writeEvent(EventLogTags.GSM_DATA_STATE_CHANGE, state.toString(), s.toString());
-            state = s;
+        if (mState != s) {
+            EventLog.writeEvent(EventLogTags.GSM_DATA_STATE_CHANGE, mState.toString(), s.toString());
+            mState = s;
         }
 
-        if (state == State.FAILED) {
-            if (waitingApns != null)
-                waitingApns.clear(); // when teardown the connection and set to IDLE
+        if (mState == State.FAILED) {
+            if (mWaitingApns != null)
+                mWaitingApns.clear(); // when tear down the connection and set to IDLE
         }
     }
 
+    @Override
     public String[] getActiveApnTypes() {
         String[] result;
         if (mActiveApn != null) {
@@ -328,6 +224,7 @@
         return result;
     }
 
+    @Override
     protected String getActiveApnString() {
         String result = null;
         if (mActiveApn != null) {
@@ -345,15 +242,16 @@
      *
      * @return false while no data connection if all above requirements are met.
      */
+    @Override
     public boolean isDataConnectionAsDesired() {
-        boolean roaming = phone.getServiceState().getRoaming();
+        boolean roaming = mPhone.getServiceState().getRoaming();
 
         if (mGsmPhone.mSIMRecords.getRecordsLoaded() &&
                 mGsmPhone.mSST.getCurrentGprsState() == ServiceState.STATE_IN_SERVICE &&
                 (!roaming || getDataOnRoamingEnabled()) &&
             !mIsWifiConnected &&
             !mIsPsRestricted ) {
-            return (state == State.CONNECTED);
+            return (mState == State.CONNECTED);
         }
         return true;
     }
@@ -370,8 +268,8 @@
             return (fetchDunApn() != null);
         }
 
-        if (allApns != null) {
-            for (ApnSetting apn : allApns) {
+        if (mAllApns != null) {
+            for (ApnSetting apn : mAllApns) {
                 if (apn.canHandleType(type)) {
                     return true;
                 }
@@ -380,20 +278,6 @@
         return false;
     }
 
-    /**
-     * Formerly this method was ArrayList<GsmDataConnection> getAllPdps()
-     */
-    public ArrayList<DataConnection> getAllDataConnections() {
-        ArrayList<DataConnection> pdps = (ArrayList<DataConnection>)pdpList.clone();
-        return pdps;
-    }
-
-    private boolean isDataAllowed() {
-        boolean roaming = phone.getServiceState().getRoaming();
-        return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled()) &&
-                mMasterDataEnabled;
-    }
-
     //****** Called from ServiceStateTracker
     /**
      * Invoked when ServiceStateTracker observes a transition from GPRS
@@ -405,15 +289,15 @@
          * when GPRS detaches, but we should stop the network polling.
          */
         stopNetStatPoll();
-        phone.notifyDataConnection(Phone.REASON_GPRS_DETACHED);
+        notifyDataConnection(Phone.REASON_GPRS_DETACHED);
     }
 
     private void onGprsAttached() {
-        if (state == State.CONNECTED) {
+        if (mState == State.CONNECTED) {
             startNetStatPoll();
-            phone.notifyDataConnection(Phone.REASON_GPRS_ATTACHED);
+            notifyDataConnection(Phone.REASON_GPRS_ATTACHED);
         } else {
-            if (state == State.FAILED) {
+            if (mState == State.FAILED) {
                 cleanUpConnection(false, Phone.REASON_GPRS_ATTACHED);
                 mRetryMgr.resetRetryCount();
             }
@@ -421,74 +305,91 @@
         }
     }
 
+    @Override
+    protected boolean isDataAllowed() {
+        int gprsState = mGsmPhone.mSST.getCurrentGprsState();
+        boolean desiredPowerState = mGsmPhone.mSST.getDesiredPowerState();
+
+        boolean allowed =
+                    (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
+                    mGsmPhone.mSIMRecords.getRecordsLoaded() &&
+                    mPhone.getState() == Phone.State.IDLE &&
+                    mMasterDataEnabled &&
+                    (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
+                    !mIsPsRestricted &&
+                    desiredPowerState;
+        if (!allowed && DBG) {
+            String reason = "";
+            if (!((gprsState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) {
+                reason += " - gprs= " + gprsState;
+            }
+            if (!mGsmPhone.mSIMRecords.getRecordsLoaded()) reason += " - SIM not loaded";
+            if (mPhone.getState() != Phone.State.IDLE) {
+                reason += " - PhoneState= " + mPhone.getState();
+            }
+            if (!mMasterDataEnabled) reason += " - mMasterDataEnabled= false";
+            if (mPhone.getServiceState().getRoaming() && getDataOnRoamingEnabled()) {
+                reason += " - Roaming";
+            }
+            if (mIsPsRestricted) reason += " - mIsPsRestricted= true";
+            if (!desiredPowerState) reason += " - desiredPowerState= false";
+            log("Data not allowed due to" + reason);
+        }
+        return allowed;
+    }
+
     private boolean trySetupData(String reason) {
         if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason));
 
-        Log.d(LOG_TAG, "[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted);
+        log("[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted);
 
-        if (phone.getSimulatedRadioControl() != null) {
+        if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
             // FIXME  this can be improved
             setState(State.CONNECTED);
-            phone.notifyDataConnection(reason);
+            notifyDataConnection(reason);
 
-            Log.i(LOG_TAG, "(fix?) We're on the simulator; assuming data is connected");
+            log("(fix?) We're on the simulator; assuming data is connected");
             return true;
         }
 
         int gprsState = mGsmPhone.mSST.getCurrentGprsState();
         boolean desiredPowerState = mGsmPhone.mSST.getDesiredPowerState();
 
-        if ((state == State.IDLE || state == State.SCANNING)
-                && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach)
-                && mGsmPhone.mSIMRecords.getRecordsLoaded()
-                && phone.getState() == Phone.State.IDLE
-                && isDataAllowed()
-                && !mIsPsRestricted
-                && desiredPowerState ) {
+        if (((mState == State.IDLE) || (mState == State.SCANNING)) &&
+                isDataAllowed() && getAnyDataEnabled()) {
 
-            if (state == State.IDLE) {
-                waitingApns = buildWaitingApns();
-                if (waitingApns.isEmpty()) {
+            if (mState == State.IDLE) {
+                mWaitingApns = buildWaitingApns(mRequestedApnType);
+                if (mWaitingApns.isEmpty()) {
                     if (DBG) log("No APN found");
                     notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN);
+                    notifyOffApnsOfAvailability(reason, false);
                     return false;
                 } else {
-                    log ("Create from allApns : " + apnListToString(allApns));
+                    log ("Create from allApns : " + apnListToString(mAllApns));
                 }
             }
 
             if (DBG) {
-                log ("Setup waitngApns : " + apnListToString(waitingApns));
+                log ("Setup waitngApns : " + apnListToString(mWaitingApns));
             }
-            return setupData(reason);
+            boolean retValue = setupData(reason);
+            notifyOffApnsOfAvailability(reason, retValue);
+            return retValue;
         } else {
-            if (DBG)
-                log("trySetupData: Not ready for data: " +
-                    " dataState=" + state +
-                    " gprsState=" + gprsState +
-                    " sim=" + mGsmPhone.mSIMRecords.getRecordsLoaded() +
-                    " UMTS=" + mGsmPhone.mSST.isConcurrentVoiceAndData() +
-                    " phoneState=" + phone.getState() +
-                    " isDataAllowed=" + isDataAllowed() +
-                    " dataEnabled=" + getAnyDataEnabled() +
-                    " roaming=" + phone.getServiceState().getRoaming() +
-                    " dataOnRoamingEnable=" + getDataOnRoamingEnabled() +
-                    " ps restricted=" + mIsPsRestricted +
-                    " desiredPowerState=" + desiredPowerState +
-                    " MasterDataEnabled=" + mMasterDataEnabled);
+            notifyOffApnsOfAvailability(reason, false);
             return false;
         }
     }
 
     /**
-     * If tearDown is true, this only tears down a CONNECTED session. Presently,
-     * there is no mechanism for abandoning an INITING/CONNECTING session,
-     * but would likely involve cancelling pending async requests or
-     * setting a flag or new state to ignore them when they came in
-     * @param tearDown true if the underlying GsmDataConnection should be
-     * disconnected.
-     * @param reason reason for the clean up.
+     * Cleanup all connections.
+     *
+     * TODO: Cleanup only a specified connection passed as a parameter.
+     *
+     * @param tearDown true if the underlying DataConnection should be disconnected.
+     * @param reason for the clean up.
      */
     private void cleanUpConnection(boolean tearDown, String reason) {
         if (DBG) log("Clean up connection due to " + reason);
@@ -496,7 +397,7 @@
         // Clear the reconnect alarm, if set.
         if (mReconnectIntent != null) {
             AlarmManager am =
-                (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE);
+                (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
             am.cancel(mReconnectIntent);
             mReconnectIntent = null;
         }
@@ -504,10 +405,11 @@
         setState(State.DISCONNECTING);
 
         boolean notificationDeferred = false;
-        for (DataConnection conn : pdpList) {
+        for (DataConnection conn : mDataConnections.values()) {
             if (tearDown) {
                 if (DBG) log("cleanUpConnection: teardown, call conn.disconnect");
-                conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE, reason));
+                conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE,
+                        conn.getDataConnectionId(), 0, reason));
                 notificationDeferred = true;
             } else {
                 if (DBG) log("cleanUpConnection: !tearDown, call conn.resetSynchronously");
@@ -565,87 +467,49 @@
         return result;
     }
 
-    private GsmDataConnection findFreePdp() {
-        for (DataConnection conn : pdpList) {
-            GsmDataConnection pdp = (GsmDataConnection) conn;
-            if (pdp.isInactive()) {
-                return pdp;
+    private GsmDataConnection findFreeDataConnection() {
+        for (DataConnection dc : mDataConnections.values()) {
+            if (dc.isInactive()) {
+                log("found free GsmDataConnection");
+                return (GsmDataConnection) dc;
             }
         }
+        log("NO free GsmDataConnection");
         return null;
     }
 
     private boolean setupData(String reason) {
         ApnSetting apn;
-        GsmDataConnection pdp;
+        GsmDataConnection gdc;
 
         apn = getNextApn();
         if (apn == null) return false;
-        pdp = findFreePdp();
-        if (pdp == null) {
+        gdc = findFreeDataConnection();
+        if (gdc == null) {
             if (DBG) log("setupData: No free GsmDataConnection found!");
             return false;
         }
         mActiveApn = apn;
-        mActivePdp = pdp;
+        mPendingDataConnection = gdc;
 
         Message msg = obtainMessage();
         msg.what = EVENT_DATA_SETUP_COMPLETE;
         msg.obj = reason;
-        pdp.connect(msg, apn);
+        gdc.connect(msg, apn);
 
         setState(State.INITING);
-        phone.notifyDataConnection(reason);
+        notifyDataConnection(reason);
         return true;
     }
 
-    protected String getInterfaceName(String apnType) {
-        if (mActivePdp != null &&
-                (apnType == null ||
-                (mActiveApn != null && mActiveApn.canHandleType(apnType)))) {
-            return mActivePdp.getInterface();
-        }
-        return null;
-    }
-
-    protected String getIpAddress(String apnType) {
-        if (mActivePdp != null &&
-                (apnType == null ||
-                (mActiveApn != null && mActiveApn.canHandleType(apnType)))) {
-            return mActivePdp.getIpAddress();
-        }
-        return null;
-    }
-
-    public String getGateway(String apnType) {
-        if (mActivePdp != null &&
-                (apnType == null ||
-                (mActiveApn != null && mActiveApn.canHandleType(apnType)))) {
-            return mActivePdp.getGatewayAddress();
-        }
-        return null;
-    }
-
-    protected String[] getDnsServers(String apnType) {
-        if (mActivePdp != null &&
-                (apnType == null ||
-                (mActiveApn != null && mActiveApn.canHandleType(apnType)))) {
-            return mActivePdp.getDnsServers();
-        }
-        return null;
-    }
-
-    private boolean
-    pdpStatesHasCID (ArrayList<DataCallState> states, int cid) {
+    private boolean dataCallStatesHasCID (ArrayList<DataCallState> states, int cid) {
         for (int i = 0, s = states.size() ; i < s ; i++) {
             if (states.get(i).cid == cid) return true;
         }
-
         return false;
     }
 
-    private boolean
-    pdpStatesHasActiveCID (ArrayList<DataCallState> states, int cid) {
+    private boolean dataCallStatesHasActiveCID (ArrayList<DataCallState> states, int cid) {
         for (int i = 0, s = states.size() ; i < s ; i++) {
             if ((states.get(i).cid == cid) && (states.get(i).active != 0)) {
                 return true;
@@ -661,7 +525,7 @@
     private void onApnChanged() {
         boolean isConnected;
 
-        isConnected = (state != State.IDLE && state != State.FAILED);
+        isConnected = (mState != State.IDLE && mState != State.FAILED);
 
         // The "current" may no longer be valid.  MMS depends on this to send properly.
         mGsmPhone.updateCurrentCarrierInProvider();
@@ -669,7 +533,7 @@
         // TODO: It'd be nice to only do this if the changed entrie(s)
         // match the current operator.
         createAllApnList();
-        if (state != State.DISCONNECTING) {
+        if (mState != State.DISCONNECTING) {
             cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED);
             if (!isConnected) {
                 // reset reconnect timer
@@ -686,10 +550,10 @@
      * via an unsolicited response (which could have happened at any
      * previous state
      */
-    protected void onPdpStateChanged (AsyncResult ar, boolean explicitPoll) {
-        ArrayList<DataCallState> pdpStates;
+    private void onDataStateChanged (AsyncResult ar, boolean explicitPoll) {
+        ArrayList<DataCallState> dataCallStates;
 
-        pdpStates = (ArrayList<DataCallState>)(ar.result);
+        dataCallStates = (ArrayList<DataCallState>)(ar.result);
 
         if (ar.exception != null) {
             // This is probably "radio not available" or something
@@ -698,42 +562,42 @@
             return;
         }
 
-        if (state == State.CONNECTED) {
+        if (mState == State.CONNECTED) {
             // The way things are supposed to work, the PDP list
             // should not contain the CID after it disconnects.
             // However, the way things really work, sometimes the PDP
             // context is still listed with active = false, which
             // makes it hard to distinguish an activating context from
             // an activated-and-then deactivated one.
-            if (!pdpStatesHasCID(pdpStates, cidActive)) {
+            if (!dataCallStatesHasCID(dataCallStates, mCidActive)) {
                 // It looks like the PDP context has deactivated.
                 // Tear everything down and try to reconnect.
 
-                Log.i(LOG_TAG, "PDP connection has dropped. Reconnecting");
+                log("PDP connection has dropped. Reconnecting");
 
                 // Add an event log when the network drops PDP
-                GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
+                GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
                 EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP,
                         loc != null ? loc.getCid() : -1,
                         TelephonyManager.getDefault().getNetworkType());
 
                 cleanUpConnection(true, null);
                 return;
-            } else if (!pdpStatesHasActiveCID(pdpStates, cidActive)) {
+            } else if (!dataCallStatesHasActiveCID(dataCallStates, mCidActive)) {
                 // Here, we only consider this authoritative if we asked for the
                 // PDP list. If it was an unsolicited response, we poll again
                 // to make sure everyone agrees on the initial state.
 
                 if (!explicitPoll) {
                     // We think it disconnected but aren't sure...poll from our side
-                    phone.mCM.getPDPContextList(
+                    mPhone.mCM.getPDPContextList(
                             this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
                 } else {
-                    Log.i(LOG_TAG, "PDP connection has dropped (active=false case). "
+                    log("PDP connection has dropped (active=false case). "
                                     + " Reconnecting");
 
                     // Log the network drop on the event log.
-                    GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
+                    GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
                     EventLog.writeEvent(EventLogTags.PDP_NETWORK_DROP,
                             loc != null ? loc.getCid() : -1,
                             TelephonyManager.getDefault().getNetworkType());
@@ -746,7 +610,7 @@
 
     private void notifyDefaultData(String reason) {
         setState(State.CONNECTED);
-        phone.notifyDataConnection(reason);
+        notifyDataConnection(reason);
         startNetStatPoll();
         // reset reconnect timer
         mRetryMgr.resetRetryCount();
@@ -756,42 +620,30 @@
     private void gotoIdleAndNotifyDataConnection(String reason) {
         if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
         setState(State.IDLE);
-        phone.notifyDataConnection(reason);
+        notifyDataConnection(reason);
         mActiveApn = null;
     }
 
-    /**
-     * This is a kludge to deal with the fact that
-     * the PDP state change notification doesn't always work
-     * with certain RIL impl's/basebands
-     *
-     */
-    private void startPeriodicPdpPoll() {
-        removeMessages(EVENT_POLL_PDP);
-
-        sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS);
-    }
-
     private void resetPollStats() {
-        txPkts = -1;
-        rxPkts = -1;
-        sentSinceLastRecv = 0;
-        netStatPollPeriod = POLL_NETSTAT_MILLIS;
+        mTxPkts = -1;
+        mRxPkts = -1;
+        mSentSinceLastRecv = 0;
+        mNetStatPollPeriod = POLL_NETSTAT_MILLIS;
         mNoRecvPollCount = 0;
     }
 
     private void doRecovery() {
-        if (state == State.CONNECTED) {
+        if (mState == State.CONNECTED) {
             int maxPdpReset = Settings.Secure.getInt(mResolver,
                     Settings.Secure.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT,
                     DEFAULT_MAX_PDP_RESET_FAIL);
             if (mPdpResetCount < maxPdpReset) {
                 mPdpResetCount++;
-                EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, sentSinceLastRecv);
+                EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET, mSentSinceLastRecv);
                 cleanUpConnection(true, Phone.REASON_PDP_RESET);
             } else {
                 mPdpResetCount = 0;
-                EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, sentSinceLastRecv);
+                EventLog.writeEvent(EventLogTags.PDP_REREGISTER_NETWORK, mSentSinceLastRecv);
                 mGsmPhone.mSST.reRegisterNetwork(null);
             }
             // TODO: Add increasingly drastic recovery steps, eg,
@@ -799,23 +651,26 @@
         }
     }
 
+    @Override
     protected void startNetStatPoll() {
-        if (state == State.CONNECTED && mPingTestActive == false && netStatPollEnabled == false) {
-            Log.d(LOG_TAG, "[DataConnection] Start poll NetStat");
+        if (mState == State.CONNECTED && mPingTestActive == false && mNetStatPollEnabled == false) {
+            log("[DataConnection] Start poll NetStat");
             resetPollStats();
-            netStatPollEnabled = true;
+            mNetStatPollEnabled = true;
             mPollNetStat.run();
         }
     }
 
+    @Override
     protected void stopNetStatPoll() {
-        netStatPollEnabled = false;
+        mNetStatPollEnabled = false;
         removeCallbacks(mPollNetStat);
-        Log.d(LOG_TAG, "[DataConnection] Stop poll NetStat");
+        log("[DataConnection] Stop poll NetStat");
     }
 
+    @Override
     protected void restartRadio() {
-        Log.d(LOG_TAG, "************TURN OFF RADIO**************");
+        log("************TURN OFF RADIO**************");
         cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF);
         mGsmPhone.mSST.powerOffRadioSafely();
         /* Note: no need to call setRadioPower(true).  Assuming the desired
@@ -839,43 +694,43 @@
 
             Activity newActivity;
 
-            preTxPkts = txPkts;
-            preRxPkts = rxPkts;
+            preTxPkts = mTxPkts;
+            preRxPkts = mRxPkts;
 
-            txPkts = TrafficStats.getMobileTxPackets();
-            rxPkts = TrafficStats.getMobileRxPackets();
+            mTxPkts = TrafficStats.getMobileTxPackets();
+            mRxPkts = TrafficStats.getMobileRxPackets();
 
-            //Log.d(LOG_TAG, "rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts));
+            //log("rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts));
 
-            if (netStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
-                sent = txPkts - preTxPkts;
-                received = rxPkts - preRxPkts;
+            if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
+                sent = mTxPkts - preTxPkts;
+                received = mRxPkts - preRxPkts;
 
                 if ( sent > 0 && received > 0 ) {
-                    sentSinceLastRecv = 0;
+                    mSentSinceLastRecv = 0;
                     newActivity = Activity.DATAINANDOUT;
                     mPdpResetCount = 0;
                 } else if (sent > 0 && received == 0) {
-                    if (phone.getState() == Phone.State.IDLE) {
-                        sentSinceLastRecv += sent;
+                    if (mPhone.getState() == Phone.State.IDLE) {
+                        mSentSinceLastRecv += sent;
                     } else {
-                        sentSinceLastRecv = 0;
+                        mSentSinceLastRecv = 0;
                     }
                     newActivity = Activity.DATAOUT;
                 } else if (sent == 0 && received > 0) {
-                    sentSinceLastRecv = 0;
+                    mSentSinceLastRecv = 0;
                     newActivity = Activity.DATAIN;
                     mPdpResetCount = 0;
                 } else if (sent == 0 && received == 0) {
                     newActivity = Activity.NONE;
                 } else {
-                    sentSinceLastRecv = 0;
+                    mSentSinceLastRecv = 0;
                     newActivity = Activity.NONE;
                 }
 
-                if (activity != newActivity && mIsScreenOn) {
-                    activity = newActivity;
-                    phone.notifyDataActivity();
+                if (mActivity != newActivity && mIsScreenOn) {
+                    mActivity = newActivity;
+                    mPhone.notifyDataActivity();
                 }
             }
 
@@ -883,11 +738,11 @@
                     Settings.Secure.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
                     NUMBER_SENT_PACKETS_OF_HANG);
 
-            if (sentSinceLastRecv >= watchdogTrigger) {
+            if (mSentSinceLastRecv >= watchdogTrigger) {
                 // we already have NUMBER_SENT_PACKETS sent without ack
                 if (mNoRecvPollCount == 0) {
                     EventLog.writeEvent(EventLogTags.PDP_RADIO_RESET_COUNTDOWN_TRIGGERED,
-                            sentSinceLastRecv);
+                            mSentSinceLastRecv);
                 }
 
                 int noRecvPollLimit = Settings.Secure.getInt(mResolver,
@@ -897,21 +752,22 @@
                     // It's possible the PDP context went down and we weren't notified.
                     // Start polling the context list in an attempt to recover.
                     if (DBG) log("no DATAIN in a while; polling PDP");
-                    phone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
+                    mPhone.mCM.getDataCallList(obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
 
                     mNoRecvPollCount++;
 
                     // Slow down the poll interval to let things happen
-                    netStatPollPeriod = Settings.Secure.getInt(mResolver,
+                    mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
                             Settings.Secure.PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS,
                             POLL_NETSTAT_SLOW_MILLIS);
                 } else {
-                    if (DBG) log("Sent " + String.valueOf(sentSinceLastRecv) +
+                    if (DBG) log("Sent " + String.valueOf(mSentSinceLastRecv) +
                                         " pkts since last received");
                     // We've exceeded the threshold.  Run ping test as a final check;
                     // it will proceed with recovery if ping fails.
                     stopNetStatPoll();
                     Thread pingTest = new Thread() {
+                        @Override
                         public void run() {
                             runPingTest();
                         }
@@ -922,17 +778,17 @@
             } else {
                 mNoRecvPollCount = 0;
                 if (mIsScreenOn) {
-                    netStatPollPeriod = Settings.Secure.getInt(mResolver,
+                    mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
                             Settings.Secure.PDP_WATCHDOG_POLL_INTERVAL_MS, POLL_NETSTAT_MILLIS);
                 } else {
-                    netStatPollPeriod = Settings.Secure.getInt(mResolver,
+                    mNetStatPollPeriod = Settings.Secure.getInt(mResolver,
                             Settings.Secure.PDP_WATCHDOG_LONG_POLL_INTERVAL_MS,
                             POLL_NETSTAT_SCREEN_OFF_MILLIS);
                 }
             }
 
-            if (netStatPollEnabled) {
-                mDataConnectionTracker.postDelayed(this, netStatPollPeriod);
+            if (mNetStatPollEnabled) {
+                mDataConnectionTracker.postDelayed(this, mNetStatPollPeriod);
             }
         }
     };
@@ -951,9 +807,9 @@
                 status = p.waitFor();
             }
         } catch (IOException e) {
-            Log.w(LOG_TAG, "ping failed: IOException");
+            loge("ping failed: IOException");
         } catch (Exception e) {
-            Log.w(LOG_TAG, "exception trying to ping");
+            loge("exception trying to ping");
         }
 
         if (status == 0) {
@@ -994,20 +850,21 @@
     }
 
     private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) {
-        if (state == State.FAILED) {
+        if (mState == State.FAILED) {
+            /** TODO: Retrieve retry manager from connection itself */
             if (!mRetryMgr.isRetryNeeded()) {
                 if (!mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
                     // if no more retries on a secondary APN attempt, tell the world and revert.
-                    phone.notifyDataConnection(Phone.REASON_APN_FAILED);
+                    notifyDataConnection(Phone.REASON_APN_FAILED);
                     onEnableApn(apnTypeToId(mRequestedApnType), DISABLED);
                     return;
                 }
                 if (mReregisterOnReconnectFailure) {
-                    // We've re-registerd once now just retry forever.
+                    // We've re-registered once now just retry forever.
                     mRetryMgr.retryForeverUsingLastTimeout();
                 } else {
                     // Try to re-register to the network.
-                    Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network");
+                    log("PDP activate failed, Reregistering to the network");
                     mReregisterOnReconnectFailure = true;
                     mGsmPhone.mSST.reRegisterNetwork(null);
                     mRetryMgr.resetRetryCount();
@@ -1016,15 +873,15 @@
             }
 
             int nextReconnectDelay = mRetryMgr.getRetryTimer();
-            Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for "
+            log("PDP activate failed. Scheduling next attempt for "
                     + (nextReconnectDelay / 1000) + "s");
 
             AlarmManager am =
-                (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE);
+                (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
             Intent intent = new Intent(INTENT_RECONNECT_ALARM);
             intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason);
             mReconnectIntent = PendingIntent.getBroadcast(
-                    phone.getContext(), 0, intent, 0);
+                    mPhone.getContext(), 0, intent, 0);
             am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                     SystemClock.elapsedRealtime() + nextReconnectDelay,
                     mReconnectIntent);
@@ -1032,7 +889,7 @@
             mRetryMgr.increaseRetryCount();
 
             if (!shouldPostNotification(lastFailCauseCode)) {
-                Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification "
+                log("NOT Posting GPRS Unavailable notification "
                                 + "-- likely transient error");
             } else {
                 notifyNoData(lastFailCauseCode);
@@ -1044,9 +901,9 @@
         setState(State.FAILED);
     }
 
-    protected void onRecordsLoaded() {
+    private void onRecordsLoaded() {
         createAllApnList();
-        if (state == State.FAILED) {
+        if (mState == State.FAILED) {
             cleanUpConnection(false, null);
         }
         sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED));
@@ -1054,19 +911,29 @@
 
     @Override
     protected void onEnableNewApn() {
+        log("onEnableNewApn E");
         // change our retry manager to use the appropriate numbers for the new APN
         if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
-            mRetryMgr = mDefaultRetryManager;
+            log("onEnableNewApn default type");
+            mRetryMgr = mPendingDataConnection.getRetryMgr();
+            mRetryMgr.resetRetryCount();
+        } else if (mApnToDataConnectionId.get(mRequestedApnType) == null) {
+            log("onEnableNewApn mRequestedApnType=" + mRequestedApnType +
+                    " missing, make a new connection");
+            int id = createDataConnection(mRequestedApnType);
+            mRetryMgr = mDataConnections.get(id).getRetryMgr();
+            mRetryMgr.resetRetryCount();
         } else {
-            mRetryMgr = mSecondaryRetryManager;
+            log("oneEnableNewApn connection already exists, nothing to setup");
         }
-        mRetryMgr.resetRetryCount();
 
         // TODO:  To support simultaneous PDP contexts, this should really only call
         // cleanUpConnection if it needs to free up a GsmDataConnection.
         cleanUpConnection(true, Phone.REASON_APN_SWITCHED);
+        log("onEnableNewApn X");
     }
 
+    @Override
     protected boolean onTrySetupData(String reason) {
         return trySetupData(reason);
     }
@@ -1086,31 +953,33 @@
         }
     }
 
+    @Override
     protected void onRadioAvailable() {
-        if (phone.getSimulatedRadioControl() != null) {
+        if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
             // FIXME  this can be improved
             setState(State.CONNECTED);
-            phone.notifyDataConnection(null);
+            notifyDataConnection(null);
 
-            Log.i(LOG_TAG, "We're on the simulator; assuming data is connected");
+            log("We're on the simulator; assuming data is connected");
         }
 
-        if (state != State.IDLE) {
+        if (mState != State.IDLE) {
             cleanUpConnection(true, null);
         }
     }
 
+    @Override
     protected void onRadioOffOrNotAvailable() {
         // Make sure our reconnect delay starts at the initial value
         // next time the radio comes on
         mRetryMgr.resetRetryCount();
         mReregisterOnReconnectFailure = false;
 
-        if (phone.getSimulatedRadioControl() != null) {
+        if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
             // FIXME  this can be improved
-            Log.i(LOG_TAG, "We're on the simulator; assuming radio off is meaningless");
+            log("We're on the simulator; assuming radio off is meaningless");
         } else {
             if (DBG) log("Radio is off and clean up all connection");
             // TODO: Should we reset mRequestedApnType to "default"?
@@ -1118,20 +987,43 @@
         }
     }
 
+    @Override
     protected void onDataSetupComplete(AsyncResult ar) {
+        /** TODO: Which connection is completing should be a parameter */
         String reason = null;
         if (ar.userObj instanceof String) {
             reason = (String) ar.userObj;
         }
 
         if (ar.exception == null) {
+            // TODO: We should clear LinkProperties/Capabilities when torn down or disconnected
+            mLinkProperties = getLinkProperties(mPendingDataConnection);
+            mLinkCapabilities = getLinkCapabilities(mPendingDataConnection);
+
+            ApnSetting apn = mPendingDataConnection.getApn();
+            if (apn.proxy != null && apn.proxy.length() != 0) {
+                try {
+                    ProxyProperties proxy = new ProxyProperties();
+                    proxy.setSocketAddress(new InetSocketAddress(InetAddress.getByName(apn.proxy),
+                            Integer.parseInt(apn.port)));
+                    mLinkProperties.setHttpProxy(proxy);
+                } catch (UnknownHostException e) {
+                    loge("UnknownHostException making ProxyProperties: " + e);
+                } catch (SecurityException e) {
+                    loge("SecurityException making ProxyProperties: " + e);
+                } catch (NumberFormatException e) {
+                    loge("NumberFormatException making ProxyProperties (" + apn.port +
+                            "): " + e);
+                }
+            }
+
             // everything is setup
             if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
                 SystemProperties.set("gsm.defaultpdpcontext.active", "true");
-                        if (canSetPreferApn && preferredApn == null) {
-                            Log.d(LOG_TAG, "PREFERRED APN is null");
-                            preferredApn = mActiveApn;
-                            setPreferredApn(preferredApn.id);
+                        if (canSetPreferApn && mPreferredApn == null) {
+                            log("PREFERRED APN is null");
+                            mPreferredApn = mActiveApn;
+                            setPreferredApn(mPreferredApn.id);
                         }
             } else {
                 SystemProperties.set("gsm.defaultpdpcontext.active", "false");
@@ -1148,22 +1040,23 @@
             if(DBG) log("PDP setup failed " + cause);
                     // Log this failure to the Event Logs.
             if (cause.isEventLoggable()) {
-                GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
+                GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
                 EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,
                         cause.ordinal(), loc != null ? loc.getCid() : -1,
                         TelephonyManager.getDefault().getNetworkType());
             }
 
-            // No try for permanent failure
+            // Do not retry on permanent failure
+            // TODO: We should not fail permanently if more Apns to try!
             if (cause.isPermanentFail()) {
                 notifyNoData(cause);
-                phone.notifyDataConnection(Phone.REASON_APN_FAILED);
+                notifyDataConnection(Phone.REASON_APN_FAILED);
                 onEnableApn(apnTypeToId(mRequestedApnType), DISABLED);
                 return;
             }
 
-            waitingApns.remove(0);
-            if (waitingApns.isEmpty()) {
+            mWaitingApns.remove(0);
+            if (mWaitingApns.isEmpty()) {
                 // No more to try, start delayed retry
                 startDelayedRetry(cause, reason);
             } else {
@@ -1179,14 +1072,15 @@
     /**
      * Called when EVENT_DISCONNECT_DONE is received.
      */
-    protected void onDisconnectDone(AsyncResult ar) {
+    @Override
+    protected void onDisconnectDone(int connId, AsyncResult ar) {
+        if(DBG) log("EVENT_DISCONNECT_DONE connId=" + connId);
         String reason = null;
-        if(DBG) log("EVENT_DISCONNECT_DONE");
         if (ar.userObj instanceof String) {
            reason = (String) ar.userObj;
         }
         setState(State.IDLE);
-        phone.notifyDataConnection(reason);
+        notifyDataConnection(reason);
         mActiveApn = null;
         if (retryAfterDisconnected(reason)) {
             trySetupData(reason);
@@ -1207,25 +1101,27 @@
     }
 
     protected void onPollPdp() {
-        if (state == State.CONNECTED) {
+        if (mState == State.CONNECTED) {
             // only poll when connected
-            phone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
+            mPhone.mCM.getPDPContextList(this.obtainMessage(EVENT_GET_PDP_LIST_COMPLETE));
             sendMessageDelayed(obtainMessage(EVENT_POLL_PDP), POLL_PDP_MILLIS);
         }
     }
 
+    @Override
     protected void onVoiceCallStarted() {
-        if (state == State.CONNECTED && ! mGsmPhone.mSST.isConcurrentVoiceAndData()) {
+        if (mState == State.CONNECTED && ! mGsmPhone.mSST.isConcurrentVoiceAndData()) {
             stopNetStatPoll();
-            phone.notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
+            notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
         }
     }
 
+    @Override
     protected void onVoiceCallEnded() {
-        if (state == State.CONNECTED) {
+        if (mState == State.CONNECTED) {
             if (!mGsmPhone.mSST.isConcurrentVoiceAndData()) {
                 startNetStatPoll();
-                phone.notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
+                notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
             } else {
                 // clean slate after call end.
                 resetPollStats();
@@ -1239,74 +1135,92 @@
         }
     }
 
+    @Override
     protected void onCleanUpConnection(boolean tearDown, String reason) {
         cleanUpConnection(tearDown, reason);
     }
 
     /**
-     * Based on the sim operator numeric, create a list for all possible pdps
-     * with all apns associated with that pdp
-     *
-     *
+     * Based on the sim operator numeric, create a list for all possible
+     * Data Connections and setup the preferredApn.
      */
     private void createAllApnList() {
-        allApns = new ArrayList<ApnSetting>();
+        mAllApns = new ArrayList<ApnSetting>();
         String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric();
 
         if (operator != null) {
             String selection = "numeric = '" + operator + "'";
 
-            Cursor cursor = phone.getContext().getContentResolver().query(
+            Cursor cursor = mPhone.getContext().getContentResolver().query(
                     Telephony.Carriers.CONTENT_URI, null, selection, null, null);
 
             if (cursor != null) {
                 if (cursor.getCount() > 0) {
-                    allApns = createApnList(cursor);
-                    // TODO: Figure out where this fits in.  This basically just
-                    // writes the pap-secrets file.  No longer tied to GsmDataConnection
-                    // object.  Not used on current platform (no ppp).
-                    //GsmDataConnection pdp = pdpList.get(pdp_name);
-                    //if (pdp != null && pdp.dataLink != null) {
-                    //    pdp.dataLink.setPasswordInfo(cursor);
-                    //}
+                    mAllApns = createApnList(cursor);
                 }
                 cursor.close();
             }
         }
 
-        if (allApns.isEmpty()) {
+        if (mAllApns.isEmpty()) {
             if (DBG) log("No APN found for carrier: " + operator);
-            preferredApn = null;
+            mPreferredApn = null;
             notifyNoData(GsmDataConnection.FailCause.MISSING_UNKNOWN_APN);
         } else {
-            preferredApn = getPreferredApn();
-            Log.d(LOG_TAG, "Get PreferredAPN");
-            if (preferredApn != null && !preferredApn.numeric.equals(operator)) {
-                preferredApn = null;
+            mPreferredApn = getPreferredApn();
+            log("Get PreferredAPN");
+            if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {
+                mPreferredApn = null;
                 setPreferredApn(-1);
             }
         }
     }
 
-    private void createAllPdpList() {
-        pdpList = new ArrayList<DataConnection>();
-        DataConnection pdp;
+    /** Return the id for a new data connection */
+    private int createDataConnection(String apnType) {
+        log("createDataConnection(" + apnType + ") E");
+        RetryManager rm = new RetryManager();
 
-        for (int i = 0; i < PDP_CONNECTION_POOL_SIZE; i++) {
-            pdp = GsmDataConnection.makeDataConnection(mGsmPhone);
-            pdpList.add(pdp);
-         }
+        if (apnType.equals(Phone.APN_TYPE_DEFAULT)) {
+            if (!rm.configure(SystemProperties.get("ro.gsm.data_retry_config"))) {
+                if (!rm.configure(DEFAULT_DATA_RETRY_CONFIG)) {
+                    // Should never happen, log an error and default to a simple linear sequence.
+                    log("Could not configure using DEFAULT_DATA_RETRY_CONFIG="
+                            + DEFAULT_DATA_RETRY_CONFIG);
+                    rm.configure(20, 2000, 1000);
+                }
+            }
+        } else {
+            if (!rm.configure(SystemProperties.get("ro.gsm.2nd_data_retry_config"))) {
+                if (!rm.configure(SECONDARY_DATA_RETRY_CONFIG)) {
+                    // Should never happen, log an error and default to a simple sequence.
+                    log("Could note configure using SECONDARY_DATA_RETRY_CONFIG="
+                            + SECONDARY_DATA_RETRY_CONFIG);
+                    rm.configure("max_retries=3, 333, 333, 333");
+                }
+            }
+        }
+
+        int id = mUniqueIdGenerator.getAndIncrement();
+        DataConnection conn = GsmDataConnection.makeDataConnection(mGsmPhone, id, rm);
+        mDataConnections.put(id, conn);
+        mApnToDataConnectionId.put(apnType, id);
+
+        log("createDataConnection(" + apnType + ") X id=" + id);
+        return id;
     }
 
-    private void destroyAllPdpList() {
-        if(pdpList != null) {
-            GsmDataConnection pdp;
-            pdpList.removeAll(pdpList);
+    private void destroyDataConnections() {
+        if(mDataConnections != null) {
+            log("destroyDataConnectionList clear mDataConnectionList");
+            mDataConnections.clear();
+        } else {
+            log("destroyDataConnectionList mDataConnecitonList is empty, ignore");
         }
     }
 
     private ApnSetting fetchDunApn() {
-        Context c = phone.getContext();
+        Context c = mPhone.getContext();
         String apnData = Settings.Secure.getString(c.getContentResolver(),
                                     Settings.Secure.TETHER_DUN_APN);
         ApnSetting dunSetting = ApnSetting.fromString(apnData);
@@ -1317,14 +1231,16 @@
     }
 
     /**
+     * Build a list of APNs to be used to create PDP's.
      *
+     * @param requestedApnType
      * @return waitingApns list to be used to create PDP
      *          error when waitingApns.isEmpty()
      */
-    private ArrayList<ApnSetting> buildWaitingApns() {
+    private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType) {
         ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
 
-        if (mRequestedApnType.equals(Phone.APN_TYPE_DUN)) {
+        if (requestedApnType.equals(Phone.APN_TYPE_DUN)) {
             ApnSetting dun = fetchDunApn();
             if (dun != null) apnList.add(dun);
             return apnList;
@@ -1332,24 +1248,24 @@
 
         String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric();
 
-        if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
-            if (canSetPreferApn && preferredApn != null) {
-                Log.i(LOG_TAG, "Preferred APN:" + operator + ":"
-                        + preferredApn.numeric + ":" + preferredApn);
-                if (preferredApn.numeric.equals(operator)) {
-                    Log.i(LOG_TAG, "Waiting APN set to preferred APN");
-                    apnList.add(preferredApn);
+        if (requestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {
+            if (canSetPreferApn && mPreferredApn != null) {
+                log("Preferred APN:" + operator + ":"
+                        + mPreferredApn.numeric + ":" + mPreferredApn);
+                if (mPreferredApn.numeric.equals(operator)) {
+                    log("Waiting APN set to preferred APN");
+                    apnList.add(mPreferredApn);
                     return apnList;
                 } else {
                     setPreferredApn(-1);
-                    preferredApn = null;
+                    mPreferredApn = null;
                 }
             }
         }
 
-        if (allApns != null) {
-            for (ApnSetting apn : allApns) {
-                if (apn.canHandleType(mRequestedApnType)) {
+        if (mAllApns != null) {
+            for (ApnSetting apn : mAllApns) {
+                if (apn.canHandleType(requestedApnType)) {
                     apnList.add(apn);
                 }
             }
@@ -1362,7 +1278,7 @@
      * @return the first apn found in waitingApns, null if none
      */
     private ApnSetting getNextApn() {
-        ArrayList<ApnSetting> list = waitingApns;
+        ArrayList<ApnSetting> list = mWaitingApns;
         ApnSetting apn = null;
 
         if (list != null) {
@@ -1393,7 +1309,7 @@
             return;
         }
 
-        ContentResolver resolver = phone.getContext().getContentResolver();
+        ContentResolver resolver = mPhone.getContext().getContentResolver();
         resolver.delete(PREFERAPN_URI, null, null);
 
         if (pos >= 0) {
@@ -1404,11 +1320,11 @@
     }
 
     private ApnSetting getPreferredApn() {
-        if (allApns.isEmpty()) {
+        if (mAllApns.isEmpty()) {
             return null;
         }
 
-        Cursor cursor = phone.getContext().getContentResolver().query(
+        Cursor cursor = mPhone.getContext().getContentResolver().query(
                 PREFERAPN_URI, new String[] { "_id", "name", "apn" },
                 null, null, Telephony.Carriers.DEFAULT_SORT_ORDER);
 
@@ -1422,7 +1338,7 @@
             int pos;
             cursor.moveToFirst();
             pos = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID));
-            for(ApnSetting p:allApns) {
+            for(ApnSetting p:mAllApns) {
                 if (p.id == pos && p.canHandleType(mRequestedApnType)) {
                     cursor.close();
                     return p;
@@ -1437,11 +1353,12 @@
         return null;
     }
 
+    @Override
     public void handleMessage (Message msg) {
-        if (DBG) Log.d(LOG_TAG,"GSMDataConnTrack handleMessage "+msg);
+        if (DBG) log("GSMDataConnTrack handleMessage "+msg);
 
         if (!mGsmPhone.mIsTheCurrentActivePhone) {
-            Log.d(LOG_TAG, "Ignore GSM msgs since GSM phone is inactive");
+            log("Ignore GSM msgs since GSM phone is inactive");
             return;
         }
 
@@ -1459,11 +1376,11 @@
                 break;
 
             case EVENT_DATA_STATE_CHANGED:
-                onPdpStateChanged((AsyncResult) msg.obj, false);
+                onDataStateChanged((AsyncResult) msg.obj, false);
                 break;
 
             case EVENT_GET_PDP_LIST_COMPLETE:
-                onPdpStateChanged((AsyncResult) msg.obj, true);
+                onDataStateChanged((AsyncResult) msg.obj, true);
                 break;
 
             case EVENT_POLL_PDP:
@@ -1491,7 +1408,7 @@
                  * PDP context and notify us with PDP_CONTEXT_CHANGED.
                  * But we should stop the network polling and prevent reset PDP.
                  */
-                Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
+                log("[DSAC DEB] " + "EVENT_PS_RESTRICT_ENABLED " + mIsPsRestricted);
                 stopNetStatPoll();
                 mIsPsRestricted = true;
                 break;
@@ -1501,12 +1418,12 @@
                  * When PS restrict is removed, we need setup PDP connection if
                  * PDP connection is down.
                  */
-                Log.d(LOG_TAG, "[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
+                log("[DSAC DEB] " + "EVENT_PS_RESTRICT_DISABLED " + mIsPsRestricted);
                 mIsPsRestricted  = false;
-                if (state == State.CONNECTED) {
+                if (mState == State.CONNECTED) {
                     startNetStatPoll();
                 } else {
-                    if (state == State.FAILED) {
+                    if (mState == State.FAILED) {
                         cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED);
                         mRetryMgr.resetRetryCount();
                         mReregisterOnReconnectFailure = false;
@@ -1522,7 +1439,13 @@
         }
     }
 
+    @Override
     protected void log(String s) {
         Log.d(LOG_TAG, "[GsmDataConnectionTracker] " + s);
     }
+
+    @Override
+    protected void loge(String s) {
+        Log.e(LOG_TAG, "[GsmDataConnectionTracker] " + s);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 3079a64..ed7066b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -56,6 +56,7 @@
      * @param ar AsyncResult passed into the message handler.  ar.result should
      *           be a String representing the status report PDU, as ASCII hex.
      */
+    @Override
     protected void handleStatusReport(AsyncResult ar) {
         String pduString = (String) ar.result;
         SmsMessage sms = SmsMessage.newFromCDS(pduString);
@@ -84,6 +85,7 @@
 
 
     /** {@inheritDoc} */
+    @Override
     protected int dispatchMessage(SmsMessageBase smsb) {
 
         // If sms is null, means there was a parsing error.
@@ -151,6 +153,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendData(String destAddr, String scAddr, int destPort,
             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
@@ -159,6 +162,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendText(String destAddr, String scAddr, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
@@ -167,6 +171,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendMultipartText(String destinationAddress, String scAddress,
             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
             ArrayList<PendingIntent> deliveryIntents) {
@@ -306,8 +311,9 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void sendSms(SmsTracker tracker) {
-        HashMap map = tracker.mData;
+        HashMap<String, Object> map = tracker.mData;
 
         byte smsc[] = (byte[]) map.get("smsc");
         byte pdu[] = (byte[]) map.get("pdu");
@@ -322,12 +328,13 @@
      *
      * @param tracker holds the multipart Sms tracker ready to be sent
      */
+    @Override
     protected void sendMultipartSms (SmsTracker tracker) {
         ArrayList<String> parts;
         ArrayList<PendingIntent> sentIntents;
         ArrayList<PendingIntent> deliveryIntents;
 
-        HashMap map = tracker.mData;
+        HashMap<String, Object> map = tracker.mData;
 
         String destinationAddress = (String) map.get("destination");
         String scAddress = (String) map.get("scaddress");
@@ -342,6 +349,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){
         // FIXME unit test leaves cm == null. this should change
         if (mCm != null) {
@@ -350,6 +358,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void activateCellBroadcastSms(int activate, Message response) {
         // Unless CBS is implemented for GSM, this point should be unreachable.
         Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
@@ -357,6 +366,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected void getCellBroadcastSmsConfig(Message response){
         // Unless CBS is implemented for GSM, this point should be unreachable.
         Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
@@ -364,6 +374,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     protected  void setCellBroadcastConfig(int[] configValuesArray, Message response) {
         // Unless CBS is implemented for GSM, this point should be unreachable.
         Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 6ddb312..bc41b01 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -16,6 +16,18 @@
 
 package com.android.internal.telephony.gsm;
 
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.DataConnectionTracker;
+import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+
 import android.app.AlarmManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -47,17 +59,6 @@
 import android.util.Log;
 import android.util.TimeUtils;
 
-import com.android.internal.telephony.CommandException;
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.DataConnectionTracker;
-import com.android.internal.telephony.EventLogTags;
-import com.android.internal.telephony.IccCard;
-import com.android.internal.telephony.MccTable;
-import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.ServiceStateTracker;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Date;
@@ -226,6 +227,9 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_LOCALE_CHANGED);
         phone.getContext().registerReceiver(mIntentReceiver, filter);
+
+        // Gsm doesn't support OTASP so its not needed
+        phone.notifyOtaspChanged(OTASP_NOT_NEEDED);
     }
 
     public void dispose() {
@@ -246,6 +250,11 @@
         if(DBG) Log.d(LOG_TAG, "GsmServiceStateTracker finalized");
     }
 
+    @Override
+    public Phone getPhone() {
+        return phone;
+    }
+
     /**
      * Registration point for transition into GPRS attached.
      * @param h handler to notify
@@ -1001,7 +1010,7 @@
                     mNeedFixZone = false;
 
                     if (zone != null) {
-                        if (getAutoTime()) {
+                        if (getAutoTimeZone()) {
                             setAndBroadcastNetworkSetTimeZone(zone.getID());
                         }
                         saveNitzTimeZone(zone.getID());
@@ -1024,7 +1033,8 @@
         }
 
         if (hasNetworkTypeChanged) {
-            phone.notifyDataConnection(null);
+            // TODO - do we really want this?
+            phone.notifyDataConnection(null, null);
         }
 
         if (hasRoamingOn) {
@@ -1481,7 +1491,7 @@
             }
 
             if (zone != null) {
-                if (getAutoTime()) {
+                if (getAutoTimeZone()) {
                     setAndBroadcastNetworkSetTimeZone(zone.getID());
                 }
                 saveNitzTimeZone(zone.getID());
@@ -1551,6 +1561,15 @@
         }
     }
 
+    private boolean getAutoTimeZone() {
+        try {
+            return Settings.System.getInt(phone.getContext().getContentResolver(),
+                    Settings.System.AUTO_TIME_ZONE) > 0;
+        } catch (SettingNotFoundException snfe) {
+            return true;
+        }
+    }
+
     private void saveNitzTimeZone(String zoneId) {
         mSavedTimeZone = zoneId;
     }
@@ -1666,7 +1685,8 @@
         }
     }
 
-    private void log(String s) {
+    @Override
+    protected void log(String s) {
         Log.d(LOG_TAG, "[GsmServiceStateTracker] " + s);
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
index 67ecc77..aab359f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
@@ -87,6 +87,7 @@
     public void dispose() {
     }
 
+    @Override
     protected void finalize() {
         try {
             super.finalize();
@@ -192,6 +193,7 @@
         return mSms;
     }
 
+    @Override
     protected void log(String msg) {
         Log.d(LOG_TAG, "[SimSmsInterfaceManager] " + msg);
     }
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 9a3c476..e24613f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -26,7 +26,6 @@
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
 
 import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
@@ -34,6 +33,7 @@
 import static android.telephony.SmsMessage.ENCODING_7BIT;
 import static android.telephony.SmsMessage.ENCODING_8BIT;
 import static android.telephony.SmsMessage.ENCODING_16BIT;
+import static android.telephony.SmsMessage.ENCODING_KSC5601;
 import static android.telephony.SmsMessage.ENCODING_UNKNOWN;
 import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
 import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
@@ -45,7 +45,7 @@
  * A Short Message Service message.
  *
  */
-public class SmsMessage extends SmsMessageBase{
+public class SmsMessage extends SmsMessageBase {
     static final String LOG_TAG = "GSM";
 
     private MessageClass messageClass;
@@ -311,7 +311,7 @@
             // the receiver's SIM card. You can then send messages to yourself
             // (on a phone with this change) and they'll end up on the SIM card.
             bo.write(0x00);
-        } else { //assume UCS-2
+        } else { // assume UCS-2
             if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
                 // Message too long
                 return null;
@@ -377,7 +377,7 @@
      * @param destinationAddress the address of the destination for the message
      * @param destinationPort the port to deliver the message to at the
      *        destination
-     * @param data the dat for the message
+     * @param data the data for the message
      * @return a <code>SubmitPdu</code> containing the encoded SC
      *         address, if applicable, and the encoded message.
      *         Returns null on encode error.
@@ -482,7 +482,7 @@
         return bo;
     }
 
-    static class PduParser {
+    private static class PduParser {
         byte pdu[];
         int cur;
         SmsHeader userDataHeader;
@@ -490,10 +490,6 @@
         int mUserDataSeptetPadding;
         int mUserDataSize;
 
-        PduParser(String s) {
-            this(IccUtils.hexStringToBytes(s));
-        }
-
         PduParser(byte[] pdu) {
             this.pdu = pdu;
             cur = 0;
@@ -545,7 +541,7 @@
             GsmSmsAddress ret;
 
             // "The Address-Length field is an integer representation of
-            // the number field, i.e. excludes any semi octet containing only
+            // the number field, i.e. excludes any semi-octet containing only
             // fill bits."
             // The TOA field is not included as part of this
             int addressLength = pdu[cur] & 0xff;
@@ -573,7 +569,7 @@
             int second = IccUtils.gsmBcdByteToInt(pdu[cur++]);
 
             // For the timezone, the most significant bit of the
-            // least signficant nibble is the sign byte
+            // least significant nibble is the sign byte
             // (meaning the max range of this field is 79 quarter-hours,
             // which is more than enough)
 
@@ -632,7 +628,7 @@
                 /*
                  * Here we just create the user data length to be the remainder of
                  * the pdu minus the user data header, since userDataLength means
-                 * the number of uncompressed sepets.
+                 * the number of uncompressed septets.
                  */
                 bufferLen = pdu.length - offset;
             } else {
@@ -671,10 +667,10 @@
         }
 
         /**
-         * Returns the number of padding bits at the begining of the user data
+         * Returns the number of padding bits at the beginning of the user data
          * array before the start of the septets.
          *
-         * @return the number of padding bits at the begining of the user data
+         * @return the number of padding bits at the beginning of the user data
          * array before the start of the septets
          */
         int getUserDataSeptetPadding() {
@@ -694,7 +690,7 @@
         XXX Not sure what this one is supposed to be doing, and no one is using
         it.
         String getUserDataGSM8bit() {
-            // System.out.println("remainder of pud:" +
+            // System.out.println("remainder of pdu:" +
             // HexDump.dumpHexString(pdu, cur, pdu.length - cur));
             int count = pdu[cur++] & 0xff;
             int size = pdu[cur++];
@@ -781,6 +777,27 @@
             return ret;
         }
 
+        /**
+         * Interprets the user data payload as KSC-5601 characters, and
+         * decodes them into a String.
+         *
+         * @param byteCount the number of bytes in the user data payload
+         * @return a String with the decoded characters
+         */
+        String getUserDataKSC5601(int byteCount) {
+            String ret;
+
+            try {
+                ret = new String(pdu, cur, byteCount, "KSC5601");
+            } catch (UnsupportedEncodingException ex) {
+                ret = "";
+                Log.e(LOG_TAG, "implausible UnsupportedEncodingException", ex);
+            }
+
+            cur += byteCount;
+            return ret;
+        }
+
         boolean moreDataPresent() {
             return (pdu.length > cur);
         }
@@ -827,11 +844,13 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public int getProtocolIdentifier() {
         return protocolIdentifier;
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isReplace() {
         return (protocolIdentifier & 0xc0) == 0x40
                 && (protocolIdentifier & 0x3f) > 0
@@ -839,12 +858,14 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isCphsMwiMessage() {
         return ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageClear()
                 || ((GsmSmsAddress) originatingAddress).isCphsVoiceMessageSet();
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isMWIClearMessage() {
         if (isMwi && (mwiSense == false)) {
             return true;
@@ -855,6 +876,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isMWISetMessage() {
         if (isMwi && (mwiSense == true)) {
             return true;
@@ -865,6 +887,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isMwiDontStore() {
         if (isMwi && mwiDontStore) {
             return true;
@@ -884,31 +907,34 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public int getStatus() {
         return status;
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isStatusReportMessage() {
         return isStatusReportMessage;
     }
 
     /** {@inheritDoc} */
+    @Override
     public boolean isReplyPathPresent() {
         return replyPathPresent;
     }
 
     /**
-     * TS 27.005 3.1, <pdu> definition "In the case of SMS: 3GPP TS 24.011 [6]
+     * TS 27.005 3.1, &lt;pdu&gt; definition "In the case of SMS: 3GPP TS 24.011 [6]
      * SC address followed by 3GPP TS 23.040 [3] TPDU in hexadecimal format:
      * ME/TA converts each octet of TP data unit into two IRA character long
-     * hexad number (e.g. octet with integer value 42 is presented to TE as two
+     * hex number (e.g. octet with integer value 42 is presented to TE as two
      * characters 2A (IRA 50 and 65))" ...in the case of cell broadcast,
      * something else...
      */
     private void parsePdu(byte[] pdu) {
         mPdu = pdu;
-        // Log.d(LOG_TAG, "raw sms mesage:");
+        // Log.d(LOG_TAG, "raw sms message:");
         // Log.d(LOG_TAG, s);
 
         PduParser p = new PduParser(pdu);
@@ -1107,6 +1133,16 @@
                 Log.w(LOG_TAG, "MWI for fax, email, or other "
                         + (dataCodingScheme & 0xff));
             }
+        } else if ((dataCodingScheme & 0xC0) == 0x80) {
+            // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
+            // 0x80..0xBF == Reserved coding groups
+            if (dataCodingScheme == 0x84) {
+                // This value used for KSC5601 by carriers in Korea.
+                encodingType = ENCODING_KSC5601;
+            } else {
+                Log.w(LOG_TAG, "5 - Unsupported SMS data coding scheme "
+                        + (dataCodingScheme & 0xff));
+            }
         } else {
             Log.w(LOG_TAG, "3 - Unsupported SMS data coding scheme "
                     + (dataCodingScheme & 0xff));
@@ -1131,6 +1167,10 @@
         case ENCODING_16BIT:
             messageBody = p.getUserDataUCS2(count);
             break;
+
+        case ENCODING_KSC5601:
+            messageBody = p.getUserDataKSC5601(count);
+            break;
         }
 
         if (Config.LOGV) Log.v(LOG_TAG, "SMS message body (raw): '" + messageBody + "'");
@@ -1162,6 +1202,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public MessageClass getMessageClass() {
         return messageClass;
     }
diff --git a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
index b642541..41f3b23 100755
--- a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
@@ -27,8 +27,6 @@
 import com.android.internal.telephony.IccUtils;
 import com.android.internal.telephony.PhoneBase;
 
-import org.apache.harmony.luni.lang.reflect.ListOfTypes;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
diff --git a/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
index 154a334..c83f696 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipConnectionBase.java
@@ -19,6 +19,7 @@
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.UUSInfo;
 
 import android.net.sip.SipAudioCall;
 import android.os.SystemClock;
@@ -171,4 +172,10 @@
         // TODO: add PRESENTATION_URL
         return Connection.PRESENTATION_ALLOWED;
     }
+
+    @Override
+    public UUSInfo getUUSInfo() {
+        // FIXME: what's this for SIP?
+        return null;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index b154c91..ef31ddd 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -36,7 +36,6 @@
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneNotifier;
-import com.android.internal.telephony.UUSInfo;
 
 import java.text.ParseException;
 import java.util.List;
@@ -70,6 +69,14 @@
         mSipManager = SipManager.newInstance(context);
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (!(o instanceof SipPhone)) return false;
+        SipPhone that = (SipPhone) o;
+        return mProfile.getUriString().equals(that.mProfile.getUriString());
+    }
+
     public String getPhoneName() {
         return "SIP:" + getUriString(mProfile);
     }
@@ -145,10 +152,6 @@
         }
     }
 
-    public Connection dial(String dialString, UUSInfo uusinfo) throws CallStateException {
-        return dial(dialString);
-    }
-
     public Connection dial(String dialString) throws CallStateException {
         synchronized (SipPhone.class) {
             return dialInternal(dialString);
@@ -832,10 +835,6 @@
             }
         }
 
-        @Override
-        public UUSInfo getUUSInfo() {
-            return null;
-        }
     }
 
     private static Call.State getCallStateFrom(SipAudioCall sipAudioCall) {
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
index 5499966..afd4d0c 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.sip;
 
 import android.content.Context;
+import android.net.LinkProperties;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
@@ -42,6 +43,7 @@
 import com.android.internal.telephony.PhoneNotifier;
 import com.android.internal.telephony.PhoneSubInfo;
 import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.telephony.UUSInfo;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -62,6 +64,12 @@
 
     public abstract Call getRingingCall();
 
+    public Connection dial(String dialString, UUSInfo uusInfo)
+            throws CallStateException {
+        // ignore UUSInfo
+        return dial(dialString);
+    }
+
     void migrateFrom(SipPhoneBase from) {
         migrate(mRingbackRegistrants, from.mRingbackRegistrants);
         migrate(mPreciseCallStateRegistrants, from.mPreciseCallStateRegistrants);
@@ -419,6 +427,18 @@
         Log.e(LOG_TAG, "Error! This functionality is not implemented for SIP.");
     }
 
+    //@Override
+    public boolean needsOtaServiceProvisioning() {
+        // FIXME: what's this for SIP?
+        return false;
+    }
+
+    //@Override
+    public LinkProperties getLinkProperties(String apnType) {
+        // FIXME: what's this for SIP?
+        return null;
+    }
+
     void updatePhoneState() {
         State oldState = state;
 
diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
index 9c72e5a..fdcf78d 100644
--- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.telephony.test;
 
-
 import android.os.AsyncResult;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -27,7 +26,6 @@
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.DataCallState;
-import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.gsm.CallFailCause;
@@ -335,7 +333,7 @@
 
     /**
      * (AsyncResult)response.obj).result will be an Integer representing
-     * the sum of enabled serivice classes (sum of SERVICE_CLASS_*)
+     * the sum of enabled service classes (sum of SERVICE_CLASS_*)
      *
      * @param facility one of CB_FACILTY_*
      * @param pin password or "" if not required
@@ -441,7 +439,7 @@
      *  returned message
      *  retMsg.obj = AsyncResult ar
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result contains a List of DriverCall
      *      The ar.result List is sorted by DriverCall.index
      */
@@ -468,7 +466,7 @@
      *  returned message
      *  retMsg.obj = AsyncResult ar
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result contains a List of DataCallState
      */
     public void getDataCallList(Message result) {
@@ -479,7 +477,7 @@
      *  returned message
      *  retMsg.obj = AsyncResult ar
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      *
      * CLIR_DEFAULT     == on "use subscription default value"
@@ -496,7 +494,7 @@
      *  returned message
      *  retMsg.obj = AsyncResult ar
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      *
      * CLIR_DEFAULT     == on "use subscription default value"
@@ -513,7 +511,7 @@
      *  returned message
      *  retMsg.obj = AsyncResult ar
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is String containing IMSI on success
      */
     public void getIMSI(Message result) {
@@ -524,7 +522,7 @@
      *  returned message
      *  retMsg.obj = AsyncResult ar
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is String containing IMEI on success
      */
     public void getIMEI(Message result) {
@@ -535,7 +533,7 @@
      *  returned message
      *  retMsg.obj = AsyncResult ar
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is String containing IMEISV on success
      */
     public void getIMEISV(Message result) {
@@ -547,7 +545,7 @@
      *  returned message
      *  retMsg.obj = AsyncResult ar
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      *
      *  3GPP 22.030 6.5.5
@@ -572,7 +570,7 @@
      *  "Releases all held calls or sets User Determined User Busy (UDUB)
      *   for a waiting call."
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      */
     public void hangupWaitingOrBackground (Message result) {
@@ -593,7 +591,7 @@
      *  the other (held or waiting) call."
      *
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      */
     public void hangupForegroundResumeBackground (Message result) {
@@ -614,7 +612,7 @@
      *  the other (held or waiting) call."
      *
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      */
     public void switchWaitingOrHoldingAndActive (Message result) {
@@ -634,7 +632,7 @@
      * "Adds a held call to the conversation"
      *
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      */
     public void conference (Message result) {
@@ -654,7 +652,7 @@
      * "Connects the two calls and disconnects the subscriber from both calls"
      *
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      */
     public void explicitCallTransfer (Message result) {
@@ -690,7 +688,7 @@
     /**
      *
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      */
     public void acceptCall (Message result) {
@@ -708,7 +706,7 @@
     /**
      *  also known as UDUB
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      */
     public void rejectCall (Message result) {
@@ -785,7 +783,7 @@
      *
      * @param result is callback message
      *        ((AsyncResult)response.obj).result  is an int[] with every
-     *        element representing one avialable BM_*_BAND
+     *        element representing one available BM_*_BAND
      */
     public void queryAvailableBandMode (Message result) {
         int ret[] = new int [4];
@@ -894,7 +892,7 @@
 
     /**
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      */
     public void sendDtmf(char c, Message result) {
@@ -903,7 +901,7 @@
 
     /**
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      */
     public void startDtmf(char c, Message result) {
@@ -912,7 +910,7 @@
 
     /**
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      */
     public void stopDtmf(Message result) {
@@ -921,7 +919,7 @@
 
     /**
      *  ar.exception carries exception on failure
-     *  ar.userObject contains the orignal value of result.obj
+     *  ar.userObject contains the original value of result.obj
      *  ar.result is null on success and failure
      */
     public void sendBurstDtmf(String dtmfString, int on, int off, Message result) {
@@ -956,6 +954,7 @@
         unimplemented(response);
     }
 
+    @Deprecated
     public void setupDefaultPDP(String apn, String user, String password, Message result) {
         unimplemented(result);
     }
@@ -967,9 +966,7 @@
 
     public void deactivateDataCall(int cid, Message result) {unimplemented(result);}
 
-    /**
-     * @deprecated
-     */
+    @Deprecated
     public void deactivateDefaultPDP(int cid, Message result) {unimplemented(result);}
 
     public void setPreferredNetworkType(int networkType , Message result) {
@@ -1046,7 +1043,7 @@
     }
 
     /**
-     * parameters equivilient to 27.007 AT+CRSM command
+     * parameters equivalent to 27.007 AT+CRSM command
      * response.obj will be an AsyncResult
      * response.obj.userObj will be a SimIoResult on success
      */
diff --git a/telephony/mockril/Android.mk b/telephony/mockril/Android.mk
index 7c39cb1..95ae84c 100644
--- a/telephony/mockril/Android.mk
+++ b/telephony/mockril/Android.mk
@@ -25,7 +25,6 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := librilproto-java
 
-LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE := mockrilcontroller
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java b/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java
index 99f0abe..0e75c72 100644
--- a/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java
+++ b/telephony/mockril/src/com/android/internal/telephony/mockril/MockRilController.java
@@ -18,7 +18,6 @@
 
 import android.os.Bundle;
 import android.util.Log;
-import android.telephony.PhoneNumberUtils;
 
 import com.android.internal.communication.MsgHeader;
 import com.android.internal.communication.Msg;
diff --git a/telephony/tests/telephonymockriltests/Android.mk b/telephony/tests/telephonymockriltests/Android.mk
new file mode 100644
index 0000000..9731d0d
--- /dev/null
+++ b/telephony/tests/telephonymockriltests/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_STATIC_JAVA_LIBRARIES := mockrilcontroller
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_PACKAGE_NAME := TelephonyMockRilTests
+
+include $(BUILD_PACKAGE)
diff --git a/telephony/tests/telephonymockriltests/AndroidManifest.xml b/telephony/tests/telephonymockriltests/AndroidManifest.xml
new file mode 100644
index 0000000..63f44a2
--- /dev/null
+++ b/telephony/tests/telephonymockriltests/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.telephonymockriltests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:label="TelephonyMockRilTest"
+                android:name="TelephonyMockRilTest">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name=".TelephonyMockTestRunner"
+        android:targetPackage="com.android.telephonymockriltests"
+        android:label="Test runner for Telephony Tests Using Mock RIL"
+    />
+
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+
+</manifest>
diff --git a/telephony/tests/telephonymockriltests/src/com/android/telephonymockriltests/TelephonyMockTestRunner.java b/telephony/tests/telephonymockriltests/src/com/android/telephonymockriltests/TelephonyMockTestRunner.java
new file mode 100644
index 0000000..78ee738
--- /dev/null
+++ b/telephony/tests/telephonymockriltests/src/com/android/telephonymockriltests/TelephonyMockTestRunner.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telephonymockriltests;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import com.android.internal.telephony.mockril.MockRilController;
+import android.util.Log;
+
+import com.android.telephonymockriltests.functional.SimpleTestUsingMockRil;
+
+import java.io.IOException;
+import junit.framework.TestSuite;
+import junit.framework.TestCase;
+
+/**
+ * Test runner for telephony tests that using Mock RIL
+ *
+ */
+public class TelephonyMockTestRunner extends InstrumentationTestRunner {
+    private static final String TAG="TelephonyMockTestRunner";
+    public MockRilController mController;
+
+    @Override
+    public TestSuite getAllTests() {
+        TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(SimpleTestUsingMockRil.class);
+        return suite;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        try {
+            mController = new MockRilController();
+        } catch (IOException e) {
+            e.printStackTrace();
+            TestCase.assertTrue("Create Mock RIl Controller failed", false);
+        }
+        TestCase.assertNotNull(mController);
+        super.onCreate(icicle);
+    }
+
+    @Override
+    public void finish(int resultCode, Bundle results) {
+        if (mController != null)
+            mController.closeChannel();
+        super.finish(resultCode, results);
+    }
+}
diff --git a/telephony/tests/telephonymockriltests/src/com/android/telephonymockriltests/functional/SimpleTestUsingMockRil.java b/telephony/tests/telephonymockriltests/src/com/android/telephonymockriltests/functional/SimpleTestUsingMockRil.java
new file mode 100644
index 0000000..3ea1cf2
--- /dev/null
+++ b/telephony/tests/telephonymockriltests/src/com/android/telephonymockriltests/functional/SimpleTestUsingMockRil.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telephonymockriltests.functional;
+
+import com.android.internal.telephony.mockril.MockRilController;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import com.android.telephonymockriltests.TelephonyMockTestRunner;
+
+/**
+ * A simple test that using Mock RIL Controller
+ */
+public class SimpleTestUsingMockRil extends InstrumentationTestCase {
+    private static final String TAG = "SimpleTestUsingMockRil";
+    private MockRilController mMockRilCtrl = null;
+    private TelephonyMockTestRunner mRunner;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mRunner = (TelephonyMockTestRunner)getInstrumentation();
+        mMockRilCtrl = mRunner.mController;
+        assertNotNull(mMockRilCtrl);
+    }
+
+    /**
+     * Get the current radio state of RIL
+     */
+    public void testGetRadioState() {
+        int state = mMockRilCtrl.getRadioState();
+        Log.v(TAG, "testGetRadioState: " + state);
+        assertTrue(state >= 0 && state <= 9);
+    }
+
+    /**
+     * Set the current radio state of RIL
+     * and verify the radio state is set correctly
+     */
+    public void testSetRadioState() {
+        for (int state = 0; state <= 9; state++) {
+            Log.v(TAG, "set radio state to be " + state);
+            assertTrue("set radio state: " + state + " failed.",
+                       mMockRilCtrl.setRadioState(state));
+        }
+        assertFalse("use an invalid radio state", mMockRilCtrl.setRadioState(-1));
+        assertFalse("the radio state doesn't exist", mMockRilCtrl.setRadioState(10));
+    }
+}
diff --git a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java
deleted file mode 100644
index 67df510..0000000
--- a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- * 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.telephony;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.text.SpannableStringBuilder;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
-import android.content.Context;
-
-import junit.framework.TestCase;
-
-public class PhoneNumberUtilsTest extends AndroidTestCase {
-
-    @SmallTest
-    public void testExtractNetworkPortion() throws Exception {
-        assertEquals(
-                "+17005554141",
-                PhoneNumberUtils.extractNetworkPortion("+17005554141")
-        );
-
-        assertEquals(
-                "+17005554141",
-                PhoneNumberUtils.extractNetworkPortion("+1 (700).555-4141")
-        );
-
-        assertEquals(
-                "17005554141",
-                PhoneNumberUtils.extractNetworkPortion("1 (700).555-4141")
-        );
-
-        // This may seem wrong, but it's probably ok
-        assertEquals(
-                "17005554141*#",
-                PhoneNumberUtils.extractNetworkPortion("1 (700).555-4141*#")
-        );
-
-        assertEquals(
-                "170055541NN",
-                PhoneNumberUtils.extractNetworkPortion("1 (700).555-41NN")
-        );
-
-        assertEquals(
-                "170055541NN",
-                PhoneNumberUtils.extractNetworkPortion("1 (700).555-41NN,1234")
-        );
-
-        assertEquals(
-                "170055541NN",
-                PhoneNumberUtils.extractNetworkPortion("1 (700).555-41NN;1234")
-        );
-
-        // An MMI string is unperterbed, even though it contains a
-        // (valid in this case) embedded +
-        assertEquals(
-                "**21**17005554141#",
-                PhoneNumberUtils.extractNetworkPortion("**21**+17005554141#")
-                //TODO this is the correct result, although the above
-                //result has been returned since change 31776
-                //"**21**+17005554141#"
-        );
-
-        assertEquals("", PhoneNumberUtils.extractNetworkPortion(""));
-
-        assertEquals("", PhoneNumberUtils.extractNetworkPortion(",1234"));
-
-        byte [] b = new byte[20];
-        b[0] = (byte) 0x81; b[1] = (byte) 0x71; b[2] = (byte) 0x00; b[3] = (byte) 0x55;
-        b[4] = (byte) 0x05; b[5] = (byte) 0x20; b[6] = (byte) 0xF0;
-        assertEquals("17005550020",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 7));
-
-        b[0] = (byte) 0x80; b[1] = (byte) 0x71; b[2] = (byte) 0x00; b[3] = (byte) 0x55;
-        b[4] = (byte) 0x05; b[5] = (byte) 0x20; b[6] = (byte) 0xF0;
-        assertEquals("17005550020",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 7));
-
-        b[0] = (byte) 0x90; b[1] = (byte) 0x71; b[2] = (byte) 0x00; b[3] = (byte) 0x55;
-        b[4] = (byte) 0x05; b[5] = (byte) 0x20; b[6] = (byte) 0xF0;
-        assertEquals("+17005550020",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 7));
-
-        b[0] = (byte) 0x91; b[1] = (byte) 0x71; b[2] = (byte) 0x00; b[3] = (byte) 0x55;
-        b[4] = (byte) 0x05; b[5] = (byte) 0x20; b[6] = (byte) 0xF0;
-        assertEquals("+17005550020",
-                PhoneNumberUtils.calledPartyBCDToString(b, 0, 7));
-
-        byte[] bRet = PhoneNumberUtils.networkPortionToCalledPartyBCD("+17005550020");
-        assertEquals(7, bRet.length);
-        for (int i = 0; i < 7; i++) {
-            assertEquals(b[i], bRet[i]);
-        }
-
-        bRet = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength("+17005550020");
-        assertEquals(8, bRet.length);
-        assertEquals(bRet[0], 7);
-        for (int i = 1; i < 8; i++) {
-            assertEquals(b[i - 1], bRet[i]);
-        }
-
-        bRet = PhoneNumberUtils.networkPortionToCalledPartyBCD("7005550020");
-        assertEquals("7005550020",
-            PhoneNumberUtils.calledPartyBCDToString(bRet, 0, bRet.length));
-
-        b[0] = (byte) 0x81; b[1] = (byte) 0x71; b[2] = (byte) 0x00; b[3] = (byte) 0x55;
-        b[4] = (byte) 0x05; b[5] = (byte) 0x20; b[6] = (byte) 0xB0;
-        assertEquals("17005550020#",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 7));
-
-        b[0] = (byte) 0x91; b[1] = (byte) 0x71; b[2] = (byte) 0x00; b[3] = (byte) 0x55;
-        b[4] = (byte) 0x05; b[5] = (byte) 0x20; b[6] = (byte) 0xB0;
-        assertEquals("+17005550020#",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 7));
-
-        b[0] = (byte) 0x81; b[1] = (byte) 0x2A; b[2] = (byte) 0xB1;
-        assertEquals("*21#",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 3));
-
-        b[0] = (byte) 0x81; b[1] = (byte) 0x2B; b[2] = (byte) 0xB1;
-        assertEquals("#21#",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 3));
-
-        b[0] = (byte) 0x91; b[1] = (byte) 0x2A; b[2] = (byte) 0xB1;
-        assertEquals("*21#+",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 3));
-
-        b[0] = (byte) 0x81; b[1] = (byte) 0xAA; b[2] = (byte) 0x12; b[3] = (byte) 0xFB;
-        assertEquals("**21#",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 4));
-
-        b[0] = (byte) 0x91; b[1] = (byte) 0xAA; b[2] = (byte) 0x12; b[3] = (byte) 0xFB;
-        assertEquals("**21#+",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 4));
-
-        b[0] = (byte) 0x81; b[1] = (byte) 0x9A; b[2] = (byte) 0xA9; b[3] = (byte) 0x71;
-        b[4] = (byte) 0x00; b[5] = (byte) 0x55; b[6] = (byte) 0x05; b[7] = (byte) 0x20;
-        b[8] = (byte) 0xB0;
-        assertEquals("*99*17005550020#",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 9));
-
-        b[0] = (byte) 0x91; b[1] = (byte) 0x9A; b[2] = (byte) 0xA9; b[3] = (byte) 0x71;
-        b[4] = (byte) 0x00; b[5] = (byte) 0x55; b[6] = (byte) 0x05; b[7] = (byte) 0x20;
-        b[8] = (byte) 0xB0;
-        assertEquals("*99*+17005550020#",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 9));
-
-        b[0] = (byte) 0x81; b[1] = (byte) 0xAA; b[2] = (byte) 0x12; b[3] = (byte) 0x1A;
-        b[4] = (byte) 0x07; b[5] = (byte) 0x50; b[6] = (byte) 0x55; b[7] = (byte) 0x00;
-        b[8] = (byte) 0x02; b[9] = (byte) 0xFB;
-        assertEquals("**21*17005550020#",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 10));
-
-        b[0] = (byte) 0x91; b[1] = (byte) 0xAA; b[2] = (byte) 0x12; b[3] = (byte) 0x1A;
-        b[4] = (byte) 0x07; b[5] = (byte) 0x50; b[6] = (byte) 0x55; b[7] = (byte) 0x00;
-        b[8] = (byte) 0x02; b[9] = (byte) 0xFB;
-        assertEquals("**21*+17005550020#",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 10));
-
-        b[0] = (byte) 0x81; b[1] = (byte) 0x2A; b[2] = (byte) 0xA1; b[3] = (byte) 0x71;
-        b[4] = (byte) 0x00; b[5] = (byte) 0x55; b[6] = (byte) 0x05; b[7] = (byte) 0x20;
-        b[8] = (byte) 0xF0;
-        assertEquals("*21*17005550020",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 9));
-
-        b[0] = (byte) 0x91; b[1] = (byte) 0x2A; b[2] = (byte) 0xB1; b[3] = (byte) 0x71;
-        b[4] = (byte) 0x00; b[5] = (byte) 0x55; b[6] = (byte) 0x05; b[7] = (byte) 0x20;
-        b[8] = (byte) 0xF0;
-        assertEquals("*21#+17005550020",
-            PhoneNumberUtils.calledPartyBCDToString(b, 0, 9));
-
-        assertNull(PhoneNumberUtils.extractNetworkPortion(null));
-        assertNull(PhoneNumberUtils.extractPostDialPortion(null));
-        assertTrue(PhoneNumberUtils.compare(null, null));
-        assertFalse(PhoneNumberUtils.compare(null, "123"));
-        assertFalse(PhoneNumberUtils.compare("123", null));
-        assertNull(PhoneNumberUtils.toCallerIDMinMatch(null));
-        assertNull(PhoneNumberUtils.getStrippedReversed(null));
-        assertNull(PhoneNumberUtils.stringFromStringAndTOA(null, 1));
-    }
-
-    @SmallTest
-    public void testExtractNetworkPortionAlt() throws Exception {
-        assertEquals(
-                "+17005554141",
-                PhoneNumberUtils.extractNetworkPortionAlt("+17005554141")
-        );
-
-        assertEquals(
-                "+17005554141",
-                PhoneNumberUtils.extractNetworkPortionAlt("+1 (700).555-4141")
-        );
-
-        assertEquals(
-                "17005554141",
-                PhoneNumberUtils.extractNetworkPortionAlt("1 (700).555-4141")
-        );
-
-        // This may seem wrong, but it's probably ok
-        assertEquals(
-                "17005554141*#",
-                PhoneNumberUtils.extractNetworkPortionAlt("1 (700).555-4141*#")
-        );
-
-        assertEquals(
-                "170055541NN",
-                PhoneNumberUtils.extractNetworkPortionAlt("1 (700).555-41NN")
-        );
-
-        assertEquals(
-                "170055541NN",
-                PhoneNumberUtils.extractNetworkPortionAlt("1 (700).555-41NN,1234")
-        );
-
-        assertEquals(
-                "170055541NN",
-                PhoneNumberUtils.extractNetworkPortionAlt("1 (700).555-41NN;1234")
-        );
-
-        // An MMI string is unperterbed, even though it contains a
-        // (valid in this case) embedded +
-        assertEquals(
-                "**21**+17005554141#",
-                PhoneNumberUtils.extractNetworkPortionAlt("**21**+17005554141#")
-        );
-
-        assertEquals(
-                "*31#+447966164208",
-                PhoneNumberUtils.extractNetworkPortionAlt("*31#+447966164208")
-        );
-
-        assertEquals(
-                "*31#+447966164208",
-                PhoneNumberUtils.extractNetworkPortionAlt("*31# (+44) 79 6616 4208")
-        );
-
-        assertEquals("", PhoneNumberUtils.extractNetworkPortionAlt(""));
-
-        assertEquals("", PhoneNumberUtils.extractNetworkPortionAlt(",1234"));
-
-        assertNull(PhoneNumberUtils.extractNetworkPortionAlt(null));
-    }
-
-    @SmallTest
-    public void testB() throws Exception {
-        assertEquals("", PhoneNumberUtils.extractPostDialPortion("+17005554141"));
-        assertEquals("", PhoneNumberUtils.extractPostDialPortion("+1 (700).555-4141"));
-        assertEquals("", PhoneNumberUtils.extractPostDialPortion("+1 (700).555-41NN"));
-        assertEquals(",1234", PhoneNumberUtils.extractPostDialPortion("+1 (700).555-41NN,1234"));
-        assertEquals(";1234", PhoneNumberUtils.extractPostDialPortion("+1 (700).555-41NN;1234"));
-        assertEquals(";1234,;N",
-                PhoneNumberUtils.extractPostDialPortion("+1 (700).555-41NN;1-2.34 ,;N"));
-    }
-
-    @SmallTest
-    public void testCompare() throws Exception {
-        // this is odd
-        assertFalse(PhoneNumberUtils.compare("", ""));
-
-        assertTrue(PhoneNumberUtils.compare("911", "911"));
-        assertFalse(PhoneNumberUtils.compare("911", "18005550911"));
-        assertTrue(PhoneNumberUtils.compare("5555", "5555"));
-        assertFalse(PhoneNumberUtils.compare("5555", "180055555555"));
-
-        assertTrue(PhoneNumberUtils.compare("+17005554141", "+17005554141"));
-        assertTrue(PhoneNumberUtils.compare("+17005554141", "+1 (700).555-4141"));
-        assertTrue(PhoneNumberUtils.compare("+17005554141", "+1 (700).555-4141,1234"));
-        assertTrue(PhoneNumberUtils.compare("+17005554141", "17005554141"));
-        assertTrue(PhoneNumberUtils.compare("+17005554141", "7005554141"));
-        assertTrue(PhoneNumberUtils.compare("+17005554141", "5554141"));
-        assertTrue(PhoneNumberUtils.compare("17005554141", "5554141"));
-        assertTrue(PhoneNumberUtils.compare("+17005554141", "01117005554141"));
-        assertTrue(PhoneNumberUtils.compare("+17005554141", "0017005554141"));
-        assertTrue(PhoneNumberUtils.compare("17005554141", "0017005554141"));
-
-
-        assertTrue(PhoneNumberUtils.compare("+17005554141", "**31#+17005554141"));
-
-        assertFalse(PhoneNumberUtils.compare("+1 999 7005554141", "+1 7005554141"));
-        assertTrue(PhoneNumberUtils.compare("011 1 7005554141", "7005554141"));
-
-        assertFalse(PhoneNumberUtils.compare("011 11 7005554141", "+17005554141"));
-
-        assertFalse(PhoneNumberUtils.compare("+17005554141", "7085882300"));
-
-        assertTrue(PhoneNumberUtils.compare("+44 207 792 3490", "0 207 792 3490"));
-
-        assertFalse(PhoneNumberUtils.compare("+44 207 792 3490", "00 207 792 3490"));
-        assertFalse(PhoneNumberUtils.compare("+44 207 792 3490", "011 207 792 3490"));
-
-        /***** FIXME's ******/
-        //
-        // MMI header should be ignored
-        assertFalse(PhoneNumberUtils.compare("+17005554141", "**31#17005554141"));
-
-        // It's too bad this is false
-        // +44 (0) 207 792 3490 is not a dialable number
-        // but it is commonly how European phone numbers are written
-        assertFalse(PhoneNumberUtils.compare("+44 207 792 3490", "+44 (0) 207 792 3490"));
-
-        // The japanese international prefix, for example, messes us up
-        // But who uses a GSM phone in Japan?
-        assertFalse(PhoneNumberUtils.compare("+44 207 792 3490", "010 44 207 792 3490"));
-
-        // The Australian one messes us up too
-        assertFalse(PhoneNumberUtils.compare("+44 207 792 3490", "0011 44 207 792 3490"));
-
-        // The Russian trunk prefix messes us up, as does current
-        // Russian area codes (which bein with 0)
-
-        assertFalse(PhoneNumberUtils.compare("+7(095)9100766", "8(095)9100766"));
-
-        // 444 is not a valid country code, but
-        // matchIntlPrefixAndCC doesnt know this
-        assertTrue(PhoneNumberUtils.compare("+444 207 792 3490", "0 207 792 3490"));
-
-        // compare SMS short code
-        assertTrue(PhoneNumberUtils.compare("404-04", "40404"));
-    }
-
-
-    @SmallTest
-    public void testToCallerIDIndexable() throws Exception {
-        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("17005554141"));
-        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141"));
-        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141,1234"));
-        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141;1234"));
-
-        //this seems wrong, or at least useless
-        assertEquals("NN14555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-41NN"));
-
-        //<shrug> -- these are all not useful, but not terribly wrong
-        assertEquals("", PhoneNumberUtils.toCallerIDMinMatch(""));
-        assertEquals("0032", PhoneNumberUtils.toCallerIDMinMatch("2300"));
-        assertEquals("0032+", PhoneNumberUtils.toCallerIDMinMatch("+2300"));
-        assertEquals("#130#*", PhoneNumberUtils.toCallerIDMinMatch("*#031#"));
-    }
-
-    @SmallTest
-    public void testGetIndexable() throws Exception {
-        assertEquals("14145550071", PhoneNumberUtils.getStrippedReversed("1-700-555-4141"));
-        assertEquals("14145550071", PhoneNumberUtils.getStrippedReversed("1-700-555-4141,1234"));
-        assertEquals("14145550071", PhoneNumberUtils.getStrippedReversed("1-700-555-4141;1234"));
-
-        //this seems wrong, or at least useless
-        assertEquals("NN145550071", PhoneNumberUtils.getStrippedReversed("1-700-555-41NN"));
-
-        //<shrug> -- these are all not useful, but not terribly wrong
-        assertEquals("", PhoneNumberUtils.getStrippedReversed(""));
-        assertEquals("0032", PhoneNumberUtils.getStrippedReversed("2300"));
-        assertEquals("0032+", PhoneNumberUtils.getStrippedReversed("+2300"));
-        assertEquals("#130#*", PhoneNumberUtils.getStrippedReversed("*#031#"));
-    }
-
-    @SmallTest
-    public void testNanpFormatting() {
-        SpannableStringBuilder number = new SpannableStringBuilder();
-        number.append("8005551212");
-        PhoneNumberUtils.formatNanpNumber(number);
-        assertEquals("800-555-1212", number.toString());
-
-        number.clear();
-        number.append("800555121");
-        PhoneNumberUtils.formatNanpNumber(number);
-        assertEquals("800-555-121", number.toString());
-
-        number.clear();
-        number.append("555-1212");
-        PhoneNumberUtils.formatNanpNumber(number);
-        assertEquals("555-1212", number.toString());
-
-        number.clear();
-        number.append("800-55512");
-        PhoneNumberUtils.formatNanpNumber(number);
-        assertEquals("800-555-12", number.toString());
-
-        number.clear();
-        number.append("46645");
-        PhoneNumberUtils.formatNanpNumber(number);
-        assertEquals("46645", number.toString());
-    }
-
-    @SmallTest
-    public void testConvertKeypadLettersToDigits() {
-        assertEquals("1-800-4664-411",
-                     PhoneNumberUtils.convertKeypadLettersToDigits("1-800-GOOG-411"));
-        assertEquals("18004664411",
-                     PhoneNumberUtils.convertKeypadLettersToDigits("1800GOOG411"));
-        assertEquals("1-800-466-4411",
-                     PhoneNumberUtils.convertKeypadLettersToDigits("1-800-466-4411"));
-        assertEquals("18004664411",
-                     PhoneNumberUtils.convertKeypadLettersToDigits("18004664411"));
-        assertEquals("222-333-444-555-666-7777-888-9999",
-                     PhoneNumberUtils.convertKeypadLettersToDigits(
-                             "ABC-DEF-GHI-JKL-MNO-PQRS-TUV-WXYZ"));
-        assertEquals("222-333-444-555-666-7777-888-9999",
-                     PhoneNumberUtils.convertKeypadLettersToDigits(
-                             "abc-def-ghi-jkl-mno-pqrs-tuv-wxyz"));
-        assertEquals("(800) 222-3334",
-                     PhoneNumberUtils.convertKeypadLettersToDigits("(800) ABC-DEFG"));
-    }
-
-    // To run this test, the device has to be registered with network
-    public void testCheckAndProcessPlusCode() {
-        assertEquals("0118475797000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+8475797000"));
-        assertEquals("18475797000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+18475797000"));
-        assertEquals("0111234567",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+1234567"));
-        assertEquals("01123456700000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+23456700000"));
-        assertEquals("01111875767800",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+11875767800"));
-        assertEquals("8475797000,18475231753",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,+18475231753"));
-        assertEquals("0118475797000,18475231753",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+8475797000,+18475231753"));
-        assertEquals("8475797000;0118469312345",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000;+8469312345"));
-        assertEquals("8475797000,0111234567",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,+1234567"));
-        assertEquals("847597000;01111875767000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("847597000;+11875767000"));
-        assertEquals("8475797000,,0118469312345",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,,+8469312345"));
-        assertEquals("8475797000;,0118469312345",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000;,+8469312345"));
-        assertEquals("8475797000,;18475231753",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,;+18475231753"));
-        assertEquals("8475797000;,01111875767000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000;,+11875767000"));
-        assertEquals("8475797000,;01111875767000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,;+11875767000"));
-        assertEquals("8475797000,,,01111875767000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,,,+11875767000"));
-        assertEquals("8475797000;,,01111875767000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000;,,+11875767000"));
-        assertEquals("+;,8475797000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+;,8475797000"));
-        assertEquals("8475797000,",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,"));
-        assertEquals("847+579-7000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("847+579-7000"));
-        assertEquals(",8475797000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode(",8475797000"));
-        assertEquals(";;8475797000,,",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode(";;8475797000,,"));
-        assertEquals("+this+is$weird;,+",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+this+is$weird;,+"));
-        assertEquals("",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCode(""));
-        assertNull(PhoneNumberUtils.cdmaCheckAndProcessPlusCode(null));
-    }
-
-    @SmallTest
-    public void testCheckAndProcessPlusCodeByNumberFormat() {
-        assertEquals("18475797000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
-                PhoneNumberUtils.FORMAT_NANP,PhoneNumberUtils.FORMAT_NANP));
-        assertEquals("+18475797000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
-                PhoneNumberUtils.FORMAT_NANP,PhoneNumberUtils.FORMAT_JAPAN));
-        assertEquals("+18475797000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
-                PhoneNumberUtils.FORMAT_NANP,PhoneNumberUtils.FORMAT_UNKNOWN));
-        assertEquals("+18475797000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
-                PhoneNumberUtils.FORMAT_JAPAN,PhoneNumberUtils.FORMAT_JAPAN));
-        assertEquals("+18475797000",
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
-                PhoneNumberUtils.FORMAT_UNKNOWN,PhoneNumberUtils.FORMAT_UNKNOWN));
-    }
-
-    /**
-     * Basic checks for the VoiceMail number.
-     */
-    @SmallTest
-    public void testWithNumberNotEqualToVoiceMail() throws Exception {
-        assertFalse(PhoneNumberUtils.isVoiceMailNumber("911"));
-        assertFalse(PhoneNumberUtils.isVoiceMailNumber("tel:911"));
-        assertFalse(PhoneNumberUtils.isVoiceMailNumber("+18001234567"));
-        assertFalse(PhoneNumberUtils.isVoiceMailNumber(""));
-        assertFalse(PhoneNumberUtils.isVoiceMailNumber(null));
-        // This test fails on a device without a sim card
-        /*TelephonyManager mTelephonyManager =
-            (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
-        String mVoiceMailNumber = mTelephonyManager.getDefault().getVoiceMailNumber();
-        assertTrue(PhoneNumberUtils.isVoiceMailNumber(mVoiceMailNumber));
-        */
-    }
-}
diff --git a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java
deleted file mode 100644
index 4c7ebca..0000000
--- a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony;
-
-import android.telephony.PhoneNumberFormattingTextWatcher;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.text.Selection;
-import android.text.SpannableStringBuilder;
-import android.text.TextWatcher;
-
-import junit.framework.TestCase;
-
-public class PhoneNumberWatcherTest extends TestCase {
-    @SmallTest
-    public void testHyphenation() throws Exception {
-        SpannableStringBuilder number = new SpannableStringBuilder();
-        TextWatcher tw = new PhoneNumberFormattingTextWatcher();
-        number.append("555-1212");
-        // Move the cursor to the left edge
-        Selection.setSelection(number, 0);
-        tw.beforeTextChanged(number, 0, 0, 1);
-        // Insert an 8 at the beginning
-        number.insert(0, "8");
-        tw.afterTextChanged(number);
-        assertEquals("855-512-12", number.toString());
-    }
-    
-    @SmallTest
-    public void testHyphenDeletion() throws Exception {
-        SpannableStringBuilder number = new SpannableStringBuilder();
-        TextWatcher tw = new PhoneNumberFormattingTextWatcher();
-        number.append("555-1212");
-        // Move the cursor to after the hyphen
-        Selection.setSelection(number, 4);
-        // Delete the hyphen
-        tw.beforeTextChanged(number, 3, 1, 0);
-        number.delete(3, 4);
-        tw.afterTextChanged(number);
-        // Make sure that it deleted the character before the hyphen 
-        assertEquals("551-212", number.toString());
-        
-        // Make sure it deals with left edge boundary case
-        number.insert(0, "-");
-        Selection.setSelection(number, 1);
-        tw.beforeTextChanged(number, 0, 1, 0);
-        number.delete(0, 1);
-        tw.afterTextChanged(number);
-        // Make sure that it deleted the character before the hyphen 
-        assertEquals("551-212", number.toString());
-    }
-}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmAlphabetTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmAlphabetTest.java
index 3a9c511..7011aeb 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmAlphabetTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmAlphabetTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import com.android.internal.telephony.GsmAlphabet;
+
 import junit.framework.TestCase;
 
 import android.test.suitebuilder.annotation.LargeTest;
@@ -51,7 +53,7 @@
         // '@' maps to char 0
         assertEquals(0, GsmAlphabet.charToGsm('@'));
 
-        // `a (a with grave accent) maps to last GSM charater
+        // `a (a with grave accent) maps to last GSM character
         assertEquals(0x7f, GsmAlphabet.charToGsm('\u00e0'));
 
         //
@@ -307,4 +309,26 @@
         assertEquals("a",
                 GsmAlphabet.gsm8BitUnpackedToString(unpacked, 1, unpacked.length - 1));
     }
+
+    @SmallTest
+    public void testGsm8BitUpackedWithEuckr() throws Exception {
+        // Some feature phones in Korea store contacts as euc-kr.
+        // Test this situations.
+        byte unpacked[];
+
+        // Test general alphabet strings.
+        unpacked = IccUtils.hexStringToBytes("61626320646566FF");
+        assertEquals("abc def",
+                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length, "euc-kr"));
+
+        // Test korean strings.
+        unpacked = IccUtils.hexStringToBytes("C5D7BDBAC6AEFF");
+        assertEquals("\uD14C\uC2A4\uD2B8",
+                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length, "euc-kr"));
+
+        // Test gsm Extented Characters.
+        unpacked = GsmAlphabet.stringToGsm8BitPacked(sGsmExtendedChars);
+        assertEquals(sGsmExtendedChars,
+                GsmAlphabet.gsm8BitUnpackedToString(unpacked, 0, unpacked.length, "euc-kr"));
+    }
 }
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
index 3103fc1..215c6ce 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
@@ -24,8 +24,6 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import android.util.Log;
-
 public class GsmSmsTest extends AndroidTestCase {
 
     @SmallTest
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
index 2d6977c..868c76d 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
@@ -28,7 +28,7 @@
 
     @SmallTest
     public void testTimeZone() throws Exception {
-        assertEquals(MccTable.defaultTimeZoneForMcc(208), "Europe/Paris");
+        assertEquals(MccTable.defaultTimeZoneForMcc(208), "ECT");
         assertEquals(MccTable.defaultTimeZoneForMcc(232), "Europe/Vienna");
         assertEquals(MccTable.defaultTimeZoneForMcc(655), "Africa/Johannesburg");
         assertEquals(MccTable.defaultTimeZoneForMcc(440), "Asia/Tokyo");
@@ -72,16 +72,4 @@
         assertEquals(MccTable.smallestDigitsMccForMnc(0), 2);    // mcc not defined, hence default
         assertEquals(MccTable.smallestDigitsMccForMnc(2000), 2); // mcc not defined, hence default
     }
-
-    @SmallTest
-    public void testWifi() throws Exception {
-        assertEquals(MccTable.wifiChannelsForMcc(262), 13);
-        assertEquals(MccTable.wifiChannelsForMcc(234), 13);
-        assertEquals(MccTable.wifiChannelsForMcc(505), 11);
-        assertEquals(MccTable.wifiChannelsForMcc(313), 11);
-        assertEquals(MccTable.wifiChannelsForMcc(330), 0);  // wifi not defined, hence default
-        assertEquals(MccTable.wifiChannelsForMcc(0), 0);    // mcc not defined, hence default
-        assertEquals(MccTable.wifiChannelsForMcc(2000), 0); // mcc not defined, hence default
-
-    }
 }
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
new file mode 100644
index 0000000..c4a6f53
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -0,0 +1,538 @@
+/*
+ * 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.telephony;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.SpannableStringBuilder;
+import android.telephony.PhoneNumberUtils;
+
+public class PhoneNumberUtilsTest extends AndroidTestCase {
+
+    @SmallTest
+    public void testExtractNetworkPortion() throws Exception {
+        assertEquals(
+                "+17005554141",
+                PhoneNumberUtils.extractNetworkPortion("+17005554141")
+        );
+
+        assertEquals(
+                "+17005554141",
+                PhoneNumberUtils.extractNetworkPortion("+1 (700).555-4141")
+        );
+
+        assertEquals(
+                "17005554141",
+                PhoneNumberUtils.extractNetworkPortion("1 (700).555-4141")
+        );
+
+        // This may seem wrong, but it's probably ok
+        assertEquals(
+                "17005554141*#",
+                PhoneNumberUtils.extractNetworkPortion("1 (700).555-4141*#")
+        );
+
+        assertEquals(
+                "170055541NN",
+                PhoneNumberUtils.extractNetworkPortion("1 (700).555-41NN")
+        );
+
+        assertEquals(
+                "170055541NN",
+                PhoneNumberUtils.extractNetworkPortion("1 (700).555-41NN,1234")
+        );
+
+        assertEquals(
+                "170055541NN",
+                PhoneNumberUtils.extractNetworkPortion("1 (700).555-41NN;1234")
+        );
+
+        // An MMI string is unperterbed, even though it contains a
+        // (valid in this case) embedded +
+        assertEquals(
+                "**21**17005554141#",
+                PhoneNumberUtils.extractNetworkPortion("**21**+17005554141#")
+                //TODO this is the correct result, although the above
+                //result has been returned since change 31776
+                //"**21**+17005554141#"
+        );
+
+        assertEquals("", PhoneNumberUtils.extractNetworkPortion(""));
+
+        assertEquals("", PhoneNumberUtils.extractNetworkPortion(",1234"));
+
+        byte [] b = new byte[20];
+        b[0] = (byte) 0x81; b[1] = (byte) 0x71; b[2] = (byte) 0x00; b[3] = (byte) 0x55;
+        b[4] = (byte) 0x05; b[5] = (byte) 0x20; b[6] = (byte) 0xF0;
+        assertEquals("17005550020",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 7));
+
+        b[0] = (byte) 0x80; b[1] = (byte) 0x71; b[2] = (byte) 0x00; b[3] = (byte) 0x55;
+        b[4] = (byte) 0x05; b[5] = (byte) 0x20; b[6] = (byte) 0xF0;
+        assertEquals("17005550020",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 7));
+
+        b[0] = (byte) 0x90; b[1] = (byte) 0x71; b[2] = (byte) 0x00; b[3] = (byte) 0x55;
+        b[4] = (byte) 0x05; b[5] = (byte) 0x20; b[6] = (byte) 0xF0;
+        assertEquals("+17005550020",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 7));
+
+        b[0] = (byte) 0x91; b[1] = (byte) 0x71; b[2] = (byte) 0x00; b[3] = (byte) 0x55;
+        b[4] = (byte) 0x05; b[5] = (byte) 0x20; b[6] = (byte) 0xF0;
+        assertEquals("+17005550020",
+                PhoneNumberUtils.calledPartyBCDToString(b, 0, 7));
+
+        byte[] bRet = PhoneNumberUtils.networkPortionToCalledPartyBCD("+17005550020");
+        assertEquals(7, bRet.length);
+        for (int i = 0; i < 7; i++) {
+            assertEquals(b[i], bRet[i]);
+        }
+
+        bRet = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength("+17005550020");
+        assertEquals(8, bRet.length);
+        assertEquals(bRet[0], 7);
+        for (int i = 1; i < 8; i++) {
+            assertEquals(b[i - 1], bRet[i]);
+        }
+
+        bRet = PhoneNumberUtils.networkPortionToCalledPartyBCD("7005550020");
+        assertEquals("7005550020",
+            PhoneNumberUtils.calledPartyBCDToString(bRet, 0, bRet.length));
+
+        b[0] = (byte) 0x81; b[1] = (byte) 0x71; b[2] = (byte) 0x00; b[3] = (byte) 0x55;
+        b[4] = (byte) 0x05; b[5] = (byte) 0x20; b[6] = (byte) 0xB0;
+        assertEquals("17005550020#",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 7));
+
+        b[0] = (byte) 0x91; b[1] = (byte) 0x71; b[2] = (byte) 0x00; b[3] = (byte) 0x55;
+        b[4] = (byte) 0x05; b[5] = (byte) 0x20; b[6] = (byte) 0xB0;
+        assertEquals("+17005550020#",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 7));
+
+        b[0] = (byte) 0x81; b[1] = (byte) 0x2A; b[2] = (byte) 0xB1;
+        assertEquals("*21#",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 3));
+
+        b[0] = (byte) 0x81; b[1] = (byte) 0x2B; b[2] = (byte) 0xB1;
+        assertEquals("#21#",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 3));
+
+        b[0] = (byte) 0x91; b[1] = (byte) 0x2A; b[2] = (byte) 0xB1;
+        assertEquals("*21#+",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 3));
+
+        b[0] = (byte) 0x81; b[1] = (byte) 0xAA; b[2] = (byte) 0x12; b[3] = (byte) 0xFB;
+        assertEquals("**21#",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 4));
+
+        b[0] = (byte) 0x91; b[1] = (byte) 0xAA; b[2] = (byte) 0x12; b[3] = (byte) 0xFB;
+        assertEquals("**21#+",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 4));
+
+        b[0] = (byte) 0x81; b[1] = (byte) 0x9A; b[2] = (byte) 0xA9; b[3] = (byte) 0x71;
+        b[4] = (byte) 0x00; b[5] = (byte) 0x55; b[6] = (byte) 0x05; b[7] = (byte) 0x20;
+        b[8] = (byte) 0xB0;
+        assertEquals("*99*17005550020#",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 9));
+
+        b[0] = (byte) 0x91; b[1] = (byte) 0x9A; b[2] = (byte) 0xA9; b[3] = (byte) 0x71;
+        b[4] = (byte) 0x00; b[5] = (byte) 0x55; b[6] = (byte) 0x05; b[7] = (byte) 0x20;
+        b[8] = (byte) 0xB0;
+        assertEquals("*99*+17005550020#",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 9));
+
+        b[0] = (byte) 0x81; b[1] = (byte) 0xAA; b[2] = (byte) 0x12; b[3] = (byte) 0x1A;
+        b[4] = (byte) 0x07; b[5] = (byte) 0x50; b[6] = (byte) 0x55; b[7] = (byte) 0x00;
+        b[8] = (byte) 0x02; b[9] = (byte) 0xFB;
+        assertEquals("**21*17005550020#",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 10));
+
+        b[0] = (byte) 0x91; b[1] = (byte) 0xAA; b[2] = (byte) 0x12; b[3] = (byte) 0x1A;
+        b[4] = (byte) 0x07; b[5] = (byte) 0x50; b[6] = (byte) 0x55; b[7] = (byte) 0x00;
+        b[8] = (byte) 0x02; b[9] = (byte) 0xFB;
+        assertEquals("**21*+17005550020#",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 10));
+
+        b[0] = (byte) 0x81; b[1] = (byte) 0x2A; b[2] = (byte) 0xA1; b[3] = (byte) 0x71;
+        b[4] = (byte) 0x00; b[5] = (byte) 0x55; b[6] = (byte) 0x05; b[7] = (byte) 0x20;
+        b[8] = (byte) 0xF0;
+        assertEquals("*21*17005550020",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 9));
+
+        b[0] = (byte) 0x91; b[1] = (byte) 0x2A; b[2] = (byte) 0xB1; b[3] = (byte) 0x71;
+        b[4] = (byte) 0x00; b[5] = (byte) 0x55; b[6] = (byte) 0x05; b[7] = (byte) 0x20;
+        b[8] = (byte) 0xF0;
+        assertEquals("*21#+17005550020",
+            PhoneNumberUtils.calledPartyBCDToString(b, 0, 9));
+
+        assertNull(PhoneNumberUtils.extractNetworkPortion(null));
+        assertNull(PhoneNumberUtils.extractPostDialPortion(null));
+        assertTrue(PhoneNumberUtils.compare(null, null));
+        assertFalse(PhoneNumberUtils.compare(null, "123"));
+        assertFalse(PhoneNumberUtils.compare("123", null));
+        assertNull(PhoneNumberUtils.toCallerIDMinMatch(null));
+        assertNull(PhoneNumberUtils.getStrippedReversed(null));
+        assertNull(PhoneNumberUtils.stringFromStringAndTOA(null, 1));
+    }
+
+    @SmallTest
+    public void testExtractNetworkPortionAlt() throws Exception {
+        assertEquals(
+                "+17005554141",
+                PhoneNumberUtils.extractNetworkPortionAlt("+17005554141")
+        );
+
+        assertEquals(
+                "+17005554141",
+                PhoneNumberUtils.extractNetworkPortionAlt("+1 (700).555-4141")
+        );
+
+        assertEquals(
+                "17005554141",
+                PhoneNumberUtils.extractNetworkPortionAlt("1 (700).555-4141")
+        );
+
+        // This may seem wrong, but it's probably ok
+        assertEquals(
+                "17005554141*#",
+                PhoneNumberUtils.extractNetworkPortionAlt("1 (700).555-4141*#")
+        );
+
+        assertEquals(
+                "170055541NN",
+                PhoneNumberUtils.extractNetworkPortionAlt("1 (700).555-41NN")
+        );
+
+        assertEquals(
+                "170055541NN",
+                PhoneNumberUtils.extractNetworkPortionAlt("1 (700).555-41NN,1234")
+        );
+
+        assertEquals(
+                "170055541NN",
+                PhoneNumberUtils.extractNetworkPortionAlt("1 (700).555-41NN;1234")
+        );
+
+        // An MMI string is unperterbed, even though it contains a
+        // (valid in this case) embedded +
+        assertEquals(
+                "**21**+17005554141#",
+                PhoneNumberUtils.extractNetworkPortionAlt("**21**+17005554141#")
+        );
+
+        assertEquals(
+                "*31#+447966164208",
+                PhoneNumberUtils.extractNetworkPortionAlt("*31#+447966164208")
+        );
+
+        assertEquals(
+                "*31#+447966164208",
+                PhoneNumberUtils.extractNetworkPortionAlt("*31# (+44) 79 6616 4208")
+        );
+
+        assertEquals("", PhoneNumberUtils.extractNetworkPortionAlt(""));
+
+        assertEquals("", PhoneNumberUtils.extractNetworkPortionAlt(",1234"));
+
+        assertNull(PhoneNumberUtils.extractNetworkPortionAlt(null));
+    }
+
+    @SmallTest
+    public void testB() throws Exception {
+        assertEquals("", PhoneNumberUtils.extractPostDialPortion("+17005554141"));
+        assertEquals("", PhoneNumberUtils.extractPostDialPortion("+1 (700).555-4141"));
+        assertEquals("", PhoneNumberUtils.extractPostDialPortion("+1 (700).555-41NN"));
+        assertEquals(",1234", PhoneNumberUtils.extractPostDialPortion("+1 (700).555-41NN,1234"));
+        assertEquals(";1234", PhoneNumberUtils.extractPostDialPortion("+1 (700).555-41NN;1234"));
+        assertEquals(";1234,;N",
+                PhoneNumberUtils.extractPostDialPortion("+1 (700).555-41NN;1-2.34 ,;N"));
+    }
+
+    @SmallTest
+    public void testCompare() throws Exception {
+        // this is odd
+        assertFalse(PhoneNumberUtils.compare("", ""));
+
+        assertTrue(PhoneNumberUtils.compare("911", "911"));
+        assertFalse(PhoneNumberUtils.compare("911", "18005550911"));
+        assertTrue(PhoneNumberUtils.compare("5555", "5555"));
+        assertFalse(PhoneNumberUtils.compare("5555", "180055555555"));
+
+        assertTrue(PhoneNumberUtils.compare("+17005554141", "+17005554141"));
+        assertTrue(PhoneNumberUtils.compare("+17005554141", "+1 (700).555-4141"));
+        assertTrue(PhoneNumberUtils.compare("+17005554141", "+1 (700).555-4141,1234"));
+        assertTrue(PhoneNumberUtils.compare("+17005554141", "17005554141"));
+        assertTrue(PhoneNumberUtils.compare("+17005554141", "7005554141"));
+        assertTrue(PhoneNumberUtils.compare("+17005554141", "5554141"));
+        assertTrue(PhoneNumberUtils.compare("17005554141", "5554141"));
+        assertTrue(PhoneNumberUtils.compare("+17005554141", "01117005554141"));
+        assertTrue(PhoneNumberUtils.compare("+17005554141", "0017005554141"));
+        assertTrue(PhoneNumberUtils.compare("17005554141", "0017005554141"));
+
+
+        assertTrue(PhoneNumberUtils.compare("+17005554141", "**31#+17005554141"));
+
+        assertFalse(PhoneNumberUtils.compare("+1 999 7005554141", "+1 7005554141"));
+        assertTrue(PhoneNumberUtils.compare("011 1 7005554141", "7005554141"));
+
+        assertFalse(PhoneNumberUtils.compare("011 11 7005554141", "+17005554141"));
+
+        assertFalse(PhoneNumberUtils.compare("+17005554141", "7085882300"));
+
+        assertTrue(PhoneNumberUtils.compare("+44 207 792 3490", "0 207 792 3490"));
+
+        assertFalse(PhoneNumberUtils.compare("+44 207 792 3490", "00 207 792 3490"));
+        assertFalse(PhoneNumberUtils.compare("+44 207 792 3490", "011 207 792 3490"));
+
+        /***** FIXME's ******/
+        //
+        // MMI header should be ignored
+        assertFalse(PhoneNumberUtils.compare("+17005554141", "**31#17005554141"));
+
+        // It's too bad this is false
+        // +44 (0) 207 792 3490 is not a dialable number
+        // but it is commonly how European phone numbers are written
+        assertFalse(PhoneNumberUtils.compare("+44 207 792 3490", "+44 (0) 207 792 3490"));
+
+        // The japanese international prefix, for example, messes us up
+        // But who uses a GSM phone in Japan?
+        assertFalse(PhoneNumberUtils.compare("+44 207 792 3490", "010 44 207 792 3490"));
+
+        // The Australian one messes us up too
+        assertFalse(PhoneNumberUtils.compare("+44 207 792 3490", "0011 44 207 792 3490"));
+
+        // The Russian trunk prefix messes us up, as does current
+        // Russian area codes (which bein with 0)
+
+        assertFalse(PhoneNumberUtils.compare("+7(095)9100766", "8(095)9100766"));
+
+        // 444 is not a valid country code, but
+        // matchIntlPrefixAndCC doesnt know this
+        assertTrue(PhoneNumberUtils.compare("+444 207 792 3490", "0 207 792 3490"));
+
+        // compare SMS short code
+        assertTrue(PhoneNumberUtils.compare("404-04", "40404"));
+    }
+
+
+    @SmallTest
+    public void testToCallerIDIndexable() throws Exception {
+        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("17005554141"));
+        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141"));
+        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141,1234"));
+        assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141;1234"));
+
+        //this seems wrong, or at least useless
+        assertEquals("NN14555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-41NN"));
+
+        //<shrug> -- these are all not useful, but not terribly wrong
+        assertEquals("", PhoneNumberUtils.toCallerIDMinMatch(""));
+        assertEquals("0032", PhoneNumberUtils.toCallerIDMinMatch("2300"));
+        assertEquals("0032+", PhoneNumberUtils.toCallerIDMinMatch("+2300"));
+        assertEquals("#130#*", PhoneNumberUtils.toCallerIDMinMatch("*#031#"));
+    }
+
+    @SmallTest
+    public void testGetIndexable() throws Exception {
+        assertEquals("14145550071", PhoneNumberUtils.getStrippedReversed("1-700-555-4141"));
+        assertEquals("14145550071", PhoneNumberUtils.getStrippedReversed("1-700-555-4141,1234"));
+        assertEquals("14145550071", PhoneNumberUtils.getStrippedReversed("1-700-555-4141;1234"));
+
+        //this seems wrong, or at least useless
+        assertEquals("NN145550071", PhoneNumberUtils.getStrippedReversed("1-700-555-41NN"));
+
+        //<shrug> -- these are all not useful, but not terribly wrong
+        assertEquals("", PhoneNumberUtils.getStrippedReversed(""));
+        assertEquals("0032", PhoneNumberUtils.getStrippedReversed("2300"));
+        assertEquals("0032+", PhoneNumberUtils.getStrippedReversed("+2300"));
+        assertEquals("#130#*", PhoneNumberUtils.getStrippedReversed("*#031#"));
+    }
+
+    @SmallTest
+    public void testNanpFormatting() {
+        SpannableStringBuilder number = new SpannableStringBuilder();
+        number.append("8005551212");
+        PhoneNumberUtils.formatNanpNumber(number);
+        assertEquals("800-555-1212", number.toString());
+
+        number.clear();
+        number.append("800555121");
+        PhoneNumberUtils.formatNanpNumber(number);
+        assertEquals("800-555-121", number.toString());
+
+        number.clear();
+        number.append("555-1212");
+        PhoneNumberUtils.formatNanpNumber(number);
+        assertEquals("555-1212", number.toString());
+
+        number.clear();
+        number.append("800-55512");
+        PhoneNumberUtils.formatNanpNumber(number);
+        assertEquals("800-555-12", number.toString());
+
+        number.clear();
+        number.append("46645");
+        PhoneNumberUtils.formatNanpNumber(number);
+        assertEquals("46645", number.toString());
+    }
+
+    @SmallTest
+    public void testConvertKeypadLettersToDigits() {
+        assertEquals("1-800-4664-411",
+                     PhoneNumberUtils.convertKeypadLettersToDigits("1-800-GOOG-411"));
+        assertEquals("18004664411",
+                     PhoneNumberUtils.convertKeypadLettersToDigits("1800GOOG411"));
+        assertEquals("1-800-466-4411",
+                     PhoneNumberUtils.convertKeypadLettersToDigits("1-800-466-4411"));
+        assertEquals("18004664411",
+                     PhoneNumberUtils.convertKeypadLettersToDigits("18004664411"));
+        assertEquals("222-333-444-555-666-7777-888-9999",
+                     PhoneNumberUtils.convertKeypadLettersToDigits(
+                             "ABC-DEF-GHI-JKL-MNO-PQRS-TUV-WXYZ"));
+        assertEquals("222-333-444-555-666-7777-888-9999",
+                     PhoneNumberUtils.convertKeypadLettersToDigits(
+                             "abc-def-ghi-jkl-mno-pqrs-tuv-wxyz"));
+        assertEquals("(800) 222-3334",
+                     PhoneNumberUtils.convertKeypadLettersToDigits("(800) ABC-DEFG"));
+    }
+
+    // To run this test, the device has to be registered with network
+    public void testCheckAndProcessPlusCode() {
+        assertEquals("0118475797000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+8475797000"));
+        assertEquals("18475797000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+18475797000"));
+        assertEquals("0111234567",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+1234567"));
+        assertEquals("01123456700000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+23456700000"));
+        assertEquals("01111875767800",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+11875767800"));
+        assertEquals("8475797000,18475231753",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,+18475231753"));
+        assertEquals("0118475797000,18475231753",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+8475797000,+18475231753"));
+        assertEquals("8475797000;0118469312345",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000;+8469312345"));
+        assertEquals("8475797000,0111234567",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,+1234567"));
+        assertEquals("847597000;01111875767000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("847597000;+11875767000"));
+        assertEquals("8475797000,,0118469312345",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,,+8469312345"));
+        assertEquals("8475797000;,0118469312345",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000;,+8469312345"));
+        assertEquals("8475797000,;18475231753",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,;+18475231753"));
+        assertEquals("8475797000;,01111875767000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000;,+11875767000"));
+        assertEquals("8475797000,;01111875767000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,;+11875767000"));
+        assertEquals("8475797000,,,01111875767000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,,,+11875767000"));
+        assertEquals("8475797000;,,01111875767000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000;,,+11875767000"));
+        assertEquals("+;,8475797000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+;,8475797000"));
+        assertEquals("8475797000,",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,"));
+        assertEquals("847+579-7000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("847+579-7000"));
+        assertEquals(",8475797000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode(",8475797000"));
+        assertEquals(";;8475797000,,",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode(";;8475797000,,"));
+        assertEquals("+this+is$weird;,+",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+this+is$weird;,+"));
+        assertEquals("",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCode(""));
+        assertNull(PhoneNumberUtils.cdmaCheckAndProcessPlusCode(null));
+    }
+
+    @SmallTest
+    public void testCheckAndProcessPlusCodeByNumberFormat() {
+        assertEquals("18475797000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
+                PhoneNumberUtils.FORMAT_NANP,PhoneNumberUtils.FORMAT_NANP));
+        assertEquals("+18475797000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
+                PhoneNumberUtils.FORMAT_NANP,PhoneNumberUtils.FORMAT_JAPAN));
+        assertEquals("+18475797000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
+                PhoneNumberUtils.FORMAT_NANP,PhoneNumberUtils.FORMAT_UNKNOWN));
+        assertEquals("+18475797000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
+                PhoneNumberUtils.FORMAT_JAPAN,PhoneNumberUtils.FORMAT_JAPAN));
+        assertEquals("+18475797000",
+                PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
+                PhoneNumberUtils.FORMAT_UNKNOWN,PhoneNumberUtils.FORMAT_UNKNOWN));
+    }
+
+    /**
+     * Basic checks for the VoiceMail number.
+     */
+    @SmallTest
+    public void testWithNumberNotEqualToVoiceMail() throws Exception {
+        assertFalse(PhoneNumberUtils.isVoiceMailNumber("911"));
+        assertFalse(PhoneNumberUtils.isVoiceMailNumber("tel:911"));
+        assertFalse(PhoneNumberUtils.isVoiceMailNumber("+18001234567"));
+        assertFalse(PhoneNumberUtils.isVoiceMailNumber(""));
+        assertFalse(PhoneNumberUtils.isVoiceMailNumber(null));
+        // This test fails on a device without a sim card
+        /*TelephonyManager mTelephonyManager =
+            (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
+        String mVoiceMailNumber = mTelephonyManager.getDefault().getVoiceMailNumber();
+        assertTrue(PhoneNumberUtils.isVoiceMailNumber(mVoiceMailNumber));
+        */
+    }
+
+    @SmallTest
+    public void testFormatNumberToE164() {
+        assertEquals("+16502910000", PhoneNumberUtils.formatNumberToE164("650 2910000", "us"));
+        assertNull(PhoneNumberUtils.formatNumberToE164("1234567", "us"));
+        assertEquals("+18004664114", PhoneNumberUtils.formatNumberToE164("800-GOOG-114", "us"));
+    }
+
+    @SmallTest
+    public void testFormatNumber() {
+        assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "us"));
+        assertEquals("123-4567", PhoneNumberUtils.formatNumber("1234567", "us"));
+        assertEquals("(800) 466-4114", PhoneNumberUtils.formatNumber("800-GOOG-114", "us"));
+
+    }
+
+    @SmallTest
+    public void testNormalizeNumber() {
+        assertEquals("6502910000", PhoneNumberUtils.normalizeNumber("650 2910000"));
+        assertEquals("1234567", PhoneNumberUtils.normalizeNumber("12,3#4*567"));
+        assertEquals("8004664114", PhoneNumberUtils.normalizeNumber("800-GOOG-114"));
+        assertEquals("+16502910000", PhoneNumberUtils.normalizeNumber("+1 650 2910000"));
+    }
+
+    @SmallTest
+    public void testFormatDailabeNumber() {
+        // Using the phoneNumberE164's country code
+        assertEquals("(650) 291-0000",
+                PhoneNumberUtils.formatNumber("6502910000", "+16502910000", "CN"));
+        // The phoneNumberE164 is null
+        assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("6502910000", null, "US"));
+        // The given number has a country code.
+        assertEquals("+1 650-291-0000", PhoneNumberUtils.formatNumber("+16502910000", null, "CN"));
+        // The given number was formatted.
+        assertEquals("650-291-0000", PhoneNumberUtils.formatNumber("650-291-0000", null, "US"));
+    }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberWatcherTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberWatcherTest.java
new file mode 100644
index 0000000..d2e573c
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberWatcherTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.android.internal.telephony;
+
+import android.telephony.PhoneNumberFormattingTextWatcher;
+import android.test.AndroidTestCase;
+import android.text.Editable;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.text.TextWatcher;
+
+public class PhoneNumberWatcherTest extends AndroidTestCase {
+    public void testAppendChars() {
+        final String multiChars = "65012345";
+        final String formatted1 = "(650) 123-45";
+        TextWatcher textWatcher = getTextWatcher();
+        SpannableStringBuilder number = new SpannableStringBuilder();
+        // Append more than one chars
+        textWatcher.beforeTextChanged(number, 0, 0, multiChars.length());
+        number.append(multiChars);
+        Selection.setSelection(number, number.length());
+        textWatcher.onTextChanged(number, 0, 0, number.length());
+        textWatcher.afterTextChanged(number);
+        assertEquals(formatted1, number.toString());
+        assertEquals(formatted1.length(), Selection.getSelectionEnd(number));
+        // Append one chars
+        final char appendChar = '6';
+        final String formatted2 = "(650) 123-456";
+        int len = number.length();
+        textWatcher.beforeTextChanged(number, number.length(), 0, 1);
+        number.append(appendChar);
+        Selection.setSelection(number, number.length());
+        textWatcher.onTextChanged(number, len, 0, 1);
+        textWatcher.afterTextChanged(number);
+        assertEquals(formatted2, number.toString());
+        assertEquals(formatted2.length(), Selection.getSelectionEnd(number));
+    }
+
+    public void testRemoveLastChars() {
+        final String init = "65012345678";
+        final String result1 = "(650) 123-4567";
+        TextWatcher textWatcher = getTextWatcher();
+        // Remove the last char.
+        SpannableStringBuilder number = new SpannableStringBuilder(init);
+        int len = number.length();
+        textWatcher.beforeTextChanged(number, len - 1, 1, 0);
+        number.delete(len - 1, len);
+        Selection.setSelection(number, number.length());
+        textWatcher.onTextChanged(number, number.length() - 1, 1, 0);
+        textWatcher.afterTextChanged(number);
+        assertEquals(result1, number.toString());
+        assertEquals(result1.length(), Selection.getSelectionEnd(number));
+        // Remove last 5 chars
+        final String result2 = "(650) 123";
+        textWatcher.beforeTextChanged(number, number.length() - 4, 4, 0);
+        number.delete(number.length() - 5, number.length());
+        Selection.setSelection(number, number.length());
+        textWatcher.onTextChanged(number, number.length(), 4, 0);
+        textWatcher.afterTextChanged(number);
+        assertEquals(result2, number.toString());
+        assertEquals(result2.length(), Selection.getSelectionEnd(number));
+    }
+
+    public void testInsertChars() {
+        final String init = "(650) 23";
+        final String expected1 = "(650) 123";
+        TextWatcher textWatcher = getTextWatcher();
+
+        // Insert one char
+        SpannableStringBuilder number = new SpannableStringBuilder(init);
+        textWatcher.beforeTextChanged(number, 4, 0, 1);
+        number.insert(4, "1"); // (6501) 23
+        Selection.setSelection(number, 5); // make the cursor at right of 1
+        textWatcher.onTextChanged(number, 4, 0, 1);
+        textWatcher.afterTextChanged(number);
+        assertEquals(expected1, number.toString());
+        // the cursor should still at the right of '1'
+        assertEquals(7, Selection.getSelectionEnd(number));
+
+        // Insert multiple chars
+        final String expected2 = "(650) 145-6723";
+        textWatcher.beforeTextChanged(number, 7, 0, 4);
+        number.insert(7, "4567"); // change to (650) 1456723
+        Selection.setSelection(number, 11); // the cursor is at the right of '7'.
+        textWatcher.onTextChanged(number, 7, 0, 4);
+        textWatcher.afterTextChanged(number);
+        assertEquals(expected2, number.toString());
+        // the cursor should be still at the right of '7'
+        assertEquals(12, Selection.getSelectionEnd(number));
+    }
+
+    public void testStopFormatting() {
+        final String init = "(650) 123";
+        final String expected1 = "(650) 123 4";
+        TextWatcher textWatcher = getTextWatcher();
+
+        // Append space
+        SpannableStringBuilder number = new SpannableStringBuilder(init);
+        textWatcher.beforeTextChanged(number, 9, 0, 2);
+        number.insert(9, " 4"); // (6501) 23 4
+        Selection.setSelection(number, number.length()); // make the cursor at right of 4
+        textWatcher.onTextChanged(number, 9, 0, 2);
+        textWatcher.afterTextChanged(number);
+        assertEquals(expected1, number.toString());
+        // the cursor should still at the right of '1'
+        assertEquals(expected1.length(), Selection.getSelectionEnd(number));
+
+        // Delete a ')'
+        final String expected2 ="(650 123";
+        textWatcher = getTextWatcher();
+        number = new SpannableStringBuilder(init);
+        textWatcher.beforeTextChanged(number, 4, 1, 0);
+        number.delete(4, 5); // (6501 23 4
+        Selection.setSelection(number, 5); // make the cursor at right of 1
+        textWatcher.onTextChanged(number, 4, 1, 0);
+        textWatcher.afterTextChanged(number);
+        assertEquals(expected2, number.toString());
+        // the cursor should still at the right of '1'
+        assertEquals(5, Selection.getSelectionEnd(number));
+
+        // Insert a hyphen
+        final String expected3 ="(650) 12-3";
+        textWatcher = getTextWatcher();
+        number = new SpannableStringBuilder(init);
+        textWatcher.beforeTextChanged(number, 8, 0, 1);
+        number.insert(8, "-"); // (650) 12-3
+        Selection.setSelection(number, 9); // make the cursor at right of -
+        textWatcher.onTextChanged(number, 8, 0, 1);
+        textWatcher.afterTextChanged(number);
+        assertEquals(expected3, number.toString());
+        // the cursor should still at the right of '-'
+        assertEquals(9, Selection.getSelectionEnd(number));
+    }
+
+    public void testRestartFormatting() {
+        final String init = "(650) 123";
+        final String expected1 = "(650) 123 4";
+        TextWatcher textWatcher = getTextWatcher();
+
+        // Append space
+        SpannableStringBuilder number = new SpannableStringBuilder(init);
+        textWatcher.beforeTextChanged(number, 9, 0, 2);
+        number.insert(9, " 4"); // (650) 123 4
+        Selection.setSelection(number, number.length()); // make the cursor at right of 4
+        textWatcher.onTextChanged(number, 9, 0, 2);
+        textWatcher.afterTextChanged(number);
+        assertEquals(expected1, number.toString());
+        // the cursor should still at the right of '4'
+        assertEquals(expected1.length(), Selection.getSelectionEnd(number));
+
+        // Clear the current string, and start formatting again.
+        int len = number.length();
+        textWatcher.beforeTextChanged(number, 0, len, 0);
+        number.delete(0, len);
+        textWatcher.onTextChanged(number, 0, len, 0);
+        textWatcher.afterTextChanged(number);
+
+        final String expected2 = "(650) 123-4";
+        number = new SpannableStringBuilder(init);
+        textWatcher.beforeTextChanged(number, 9, 0, 1);
+        number.insert(9, "4"); // (650) 1234
+        Selection.setSelection(number, number.length()); // make the cursor at right of 4
+        textWatcher.onTextChanged(number, 9, 0, 1);
+        textWatcher.afterTextChanged(number);
+        assertEquals(expected2, number.toString());
+        // the cursor should still at the right of '4'
+        assertEquals(expected2.length(), Selection.getSelectionEnd(number));
+    }
+
+    public void testTextChangedByOtherTextWatcher() {
+        final TextWatcher cleanupTextWatcher = new TextWatcher() {
+            public void afterTextChanged(Editable s) {
+                s.clear();
+            }
+
+            public void beforeTextChanged(CharSequence s, int start, int count,
+                    int after) {
+            }
+
+            public void onTextChanged(CharSequence s, int start, int before,
+                    int count) {
+            }
+        };
+        final String init = "(650) 123";
+        final String expected1 = "";
+        TextWatcher textWatcher = getTextWatcher();
+
+        SpannableStringBuilder number = new SpannableStringBuilder(init);
+        textWatcher.beforeTextChanged(number, 5, 0, 1);
+        number.insert(5, "4"); // (6504) 123
+        Selection.setSelection(number, 5); // make the cursor at right of 4
+        textWatcher.onTextChanged(number, 5, 0, 1);
+        number.setSpan(cleanupTextWatcher, 0, number.length(), 0);
+        textWatcher.afterTextChanged(number);
+        assertEquals(expected1, number.toString());
+    }
+
+    private TextWatcher getTextWatcher() {
+        return new PhoneNumberFormattingTextWatcher("US");
+    }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/SimUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/SimUtilsTest.java
index db38ede..ef62d85 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/SimUtilsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/SimUtilsTest.java
@@ -28,7 +28,7 @@
     public void testBasic() throws Exception {
         byte[] data, data2;
 
-        /* 
+        /*
          * bcdToString()
          */
 
@@ -40,9 +40,13 @@
         assertEquals("0126045001448486", IccUtils.bcdToString(data, 1, data.length - 2));
 
         // Stops on invalid BCD value
-        data = IccUtils.hexStringToBytes("98F062400510444868f2");
+        data = IccUtils.hexStringToBytes("98E062400510444868f2");
         assertEquals("890", IccUtils.bcdToString(data, 0, data.length));
 
+        // skip the high nibble 'F' since some PLMNs have it
+        data = IccUtils.hexStringToBytes("98F062400510444868f2");
+        assertEquals("890260450014484862", IccUtils.bcdToString(data, 0, data.length));
+
         /*
          * gsmBcdByteToInt()
          */
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java
index 427795b..7bbe696 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import com.android.internal.telephony.Phone;
+
 /**
  * Stub class used for unit tests
  */
@@ -32,7 +34,7 @@
 
     public void notifyCellLocation(Phone sender) {
     }
-    
+
     public void notifySignalStrength(Phone sender) {
     }
 
@@ -42,12 +44,19 @@
     public void notifyCallForwardingChanged(Phone sender) {
     }
 
-    public void notifyDataConnection(Phone sender, String reason) {
+    public void notifyDataConnection(Phone sender, String reason, String apnType) {
     }
 
-    public void notifyDataConnectionFailed(Phone sender, String reason) {
+    public void notifyDataConnection(Phone sender, String reason, String apnType,
+            Phone.DataState state) {
+    }
+
+    public void notifyDataConnectionFailed(Phone sender, String reason, String apnType) {
     }
 
     public void notifyDataActivity(Phone sender) {
     }
+
+    public void notifyOtaspChanged(Phone sender, int otaspMode) {
+    }
 }
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java
index b96743a..485542b 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java
@@ -18,27 +18,21 @@
 
 import android.os.AsyncResult;
 import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
 import android.os.Message;
-import android.os.Process;
 import android.telephony.ServiceState;
 import android.test.AndroidTestCase;
 import android.test.PerformanceTestCase;
-import android.util.Log;
 
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.MmiCode;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.TestPhoneNotifier;
 import com.android.internal.telephony.gsm.CallFailCause;
 import com.android.internal.telephony.gsm.GSMPhone;
 import com.android.internal.telephony.gsm.GSMTestHandler;
 import com.android.internal.telephony.gsm.GsmMmiCode;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
-import com.android.internal.telephony.test.SimulatedCommands;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 
 import java.util.List;
diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java
index 70d1643..3ac74f7 100644
--- a/test-runner/src/android/test/InstrumentationTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationTestRunner.java
@@ -240,6 +240,11 @@
     private static final String REPORT_KEY_RUN_TIME = "runtime";
     /**
      * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
+     * reports the number of total iterations of the current test.
+     */
+    private static final String REPORT_KEY_NUM_ITERATIONS = "numiterations";
+    /**
+     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
      * reports the guessed suite assignment for the current test.
      */
     private static final String REPORT_KEY_SUITE_ASSIGNMENT = "suiteassignment";
@@ -748,6 +753,20 @@
                 mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "");
             }
 
+            Method testMethod = null;
+            try {
+                testMethod = test.getClass().getMethod(testName);
+                // Report total number of iterations, if test is repetitive
+                if (testMethod.isAnnotationPresent(RepetitiveTest.class)) {
+                    int numIterations = testMethod.getAnnotation(
+                        RepetitiveTest.class).numIterations();
+                    mTestResult.putInt(REPORT_KEY_NUM_ITERATIONS, numIterations);
+                }
+            } catch (NoSuchMethodException e) {
+                // ignore- the test with given name does not exist. Will be handled during test
+                // execution
+            }
+
             // The delay_msec parameter is normally used to provide buffers of idle time
             // for power measurement purposes. To make sure there is a delay before and after
             // every test in a suite, we delay *after* every test (see endTest below) and also
@@ -766,9 +785,9 @@
             mIncludeDetailedStats = false;
             try {
                 // Look for TimedTest annotation on both test class and test method
-                if (test.getClass().getMethod(testName).isAnnotationPresent(TimedTest.class)) {
+                if (testMethod.isAnnotationPresent(TimedTest.class)) {
                     mIsTimedTest = true;
-                    mIncludeDetailedStats = test.getClass().getMethod(testName).getAnnotation(
+                    mIncludeDetailedStats = testMethod.getAnnotation(
                             TimedTest.class).includeDetailedStats();
                 } else if (test.getClass().isAnnotationPresent(TimedTest.class)) {
                     mIsTimedTest = true;
@@ -778,9 +797,6 @@
             } catch (SecurityException e) {
                 // ignore - the test with given name cannot be accessed. Will be handled during
                 // test execution
-            } catch (NoSuchMethodException e) {
-                // ignore- the test with given name does not exist. Will be handled during test
-                // execution
             }
 
             if (mIsTimedTest && mIncludeDetailedStats) {
diff --git a/test-runner/src/android/test/LoaderTestCase.java b/test-runner/src/android/test/LoaderTestCase.java
new file mode 100644
index 0000000..8be6590
--- /dev/null
+++ b/test-runner/src/android/test/LoaderTestCase.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.test;
+
+import android.content.Loader;
+import android.content.Loader.OnLoadCompleteListener;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import java.util.concurrent.ArrayBlockingQueue;
+
+/**
+ * A convenience class for testing {@link Loader}s. This test case
+ * provides a simple way to synchronously get the result from a Loader making
+ * it easy to assert that the Loader returns the expected result.
+ */
+public class LoaderTestCase extends AndroidTestCase {
+    static {
+        // Force class loading of AsyncTask on the main thread so that it's handlers are tied to
+        // the main thread and responses from the worker thread get delivered on the main thread.
+        // The tests are run on another thread, allowing them to block waiting on a response from
+        // the code running on the main thread. The main thread can't block since the AysncTask
+        // results come in via the event loop.
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... args) {return null;}
+            @Override
+            protected void onPostExecute(Void result) {}
+        };
+    }
+
+    /**
+     * Runs a Loader synchronously and returns the result of the load. The loader will
+     * be started, stopped, and destroyed by this method so it cannot be reused.
+     *
+     * @param loader The loader to run synchronously
+     * @return The result from the loader
+     */
+    public <T> T getLoaderResultSynchronously(final Loader<T> loader) {
+        // The test thread blocks on this queue until the loader puts it's result in
+        final ArrayBlockingQueue<T> queue = new ArrayBlockingQueue<T>(1);
+
+        // This callback runs on the "main" thread and unblocks the test thread
+        // when it puts the result into the blocking queue
+        final OnLoadCompleteListener<T> listener = new OnLoadCompleteListener<T>() {
+            @Override
+            public void onLoadComplete(Loader<T> completedLoader, T data) {
+                // Shut the loader down
+                completedLoader.unregisterListener(this);
+                completedLoader.stopLoading();
+                completedLoader.destroy();
+
+                // Store the result, unblocking the test thread
+                queue.add(data);
+            }
+        };
+
+        // This handler runs on the "main" thread of the process since AsyncTask
+        // is documented as needing to run on the main thread and many Loaders use
+        // AsyncTask
+        final Handler mainThreadHandler = new Handler(Looper.getMainLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                loader.registerListener(0, listener);
+                loader.startLoading();
+            }
+        };
+
+        // Ask the main thread to start the loading process
+        mainThreadHandler.sendEmptyMessage(0);
+
+        // Block on the queue waiting for the result of the load to be inserted
+        T result;
+        while (true) {
+            try {
+                result = queue.take();
+                break;
+            } catch (InterruptedException e) {
+                throw new RuntimeException("waiting thread interrupted", e);
+            }
+        }
+
+        return result;
+    }
+}
diff --git a/test-runner/src/android/test/ProviderTestCase.java b/test-runner/src/android/test/ProviderTestCase.java
index e1172cf..74cebee 100644
--- a/test-runner/src/android/test/ProviderTestCase.java
+++ b/test-runner/src/android/test/ProviderTestCase.java
@@ -73,6 +73,18 @@
         mResolver.addProvider(mProviderAuthority, getProvider());
     }
 
+    /**
+     * Tears down the environment for the test fixture.
+     * <p>
+     * Calls {@link android.content.ContentProvider#shutdown()} on the
+     * {@link android.content.ContentProvider} represented by mProvider.
+     */
+    @Override
+    protected void tearDown() throws Exception {
+        mProvider.shutdown();
+        super.tearDown();
+    }
+
     public MockContentResolver getMockContentResolver() {
         return mResolver;
     }
diff --git a/test-runner/src/android/test/ProviderTestCase2.java b/test-runner/src/android/test/ProviderTestCase2.java
index 1fb5538..2811b0d 100644
--- a/test-runner/src/android/test/ProviderTestCase2.java
+++ b/test-runner/src/android/test/ProviderTestCase2.java
@@ -74,7 +74,7 @@
     private IsolatedContext mProviderContext;
     private MockContentResolver mResolver;
 
-       private class MockContext2 extends MockContext {
+    private class MockContext2 extends MockContext {
 
         @Override
         public Resources getResources() {
@@ -87,6 +87,11 @@
             // one created through the regular Context
             return getContext().getDir("mockcontext2_" + name, mode);
         }
+
+        @Override
+        public Context getApplicationContext() {
+            return this;
+        }
     }
     /**
      * Constructor.
@@ -141,6 +146,18 @@
     }
 
     /**
+     * Tears down the environment for the test fixture.
+     * <p>
+     * Calls {@link android.content.ContentProvider#shutdown()} on the
+     * {@link android.content.ContentProvider} represented by mProvider.
+     */
+    @Override
+    protected void tearDown() throws Exception {
+        mProvider.shutdown();
+        super.tearDown();
+    }
+
+    /**
      * Gets the {@link MockContentResolver} created by this class during initialization. You
      * must use the methods of this resolver to access the provider under test.
      *
diff --git a/test-runner/src/android/test/RenamingDelegatingContext.java b/test-runner/src/android/test/RenamingDelegatingContext.java
index 973b9f2..eee3ad7 100644
--- a/test-runner/src/android/test/RenamingDelegatingContext.java
+++ b/test-runner/src/android/test/RenamingDelegatingContext.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.ContentProvider;
+import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
 import android.os.FileUtils;
 import android.util.Log;
@@ -148,6 +149,17 @@
     }
 
     @Override
+    public SQLiteDatabase openOrCreateDatabase(String name,
+            int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
+        final String internalName = renamedFileName(name);
+        if (!mDatabaseNames.contains(name)) {
+            mDatabaseNames.add(name);
+            mFileContext.deleteDatabase(internalName);
+        }
+        return mFileContext.openOrCreateDatabase(internalName, mode, factory, errorHandler);
+    }
+
+    @Override
     public boolean deleteDatabase(String name) {
         if (mDatabaseNames.contains(name)) {
             mDatabaseNames.remove(name);
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 3fd71c8..b63ff3d 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -127,6 +127,16 @@
             throw new UnsupportedOperationException();
         }
 
+        @SuppressWarnings("unused")
+        public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
+            return MockContentProvider.this.getStreamTypes(url, mimeTypeFilter);
+        }
+
+        @SuppressWarnings("unused")
+        public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+                throws RemoteException, FileNotFoundException {
+            return MockContentProvider.this.openTypedAssetFile(url, mimeType, opts);
+        }
     }
     private final InversionIContentProvider mIContentProvider = new InversionIContentProvider();
 
@@ -222,6 +232,14 @@
         throw new UnsupportedOperationException("unimplemented mock method call");
     }
 
+    public String[] getStreamTypes(Uri url, String mimeTypeFilter) {
+        throw new UnsupportedOperationException("unimplemented mock method call");
+    }
+
+    public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts) {
+        throw new UnsupportedOperationException("unimplemented mock method call");
+    }
+
     /**
      * Returns IContentProvider which calls back same methods in this class.
      * By overriding this class, we avoid the mechanism hidden behind ContentProvider
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index ffd757c..c31c9cc 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -29,6 +29,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
+import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
@@ -184,6 +185,12 @@
     }
 
     @Override
+    public SQLiteDatabase openOrCreateDatabase(String file, int mode,
+            SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public File getDatabasePath(String name) {
         throw new UnsupportedOperationException();
     }
diff --git a/test-runner/src/android/test/mock/MockCursor.java b/test-runner/src/android/test/mock/MockCursor.java
index 9b1c0ef..baa150a 100644
--- a/test-runner/src/android/test/mock/MockCursor.java
+++ b/test-runner/src/android/test/mock/MockCursor.java
@@ -178,36 +178,11 @@
     }
 
     @SuppressWarnings("deprecation")
-    public boolean commitUpdates() {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
-    public boolean commitUpdates(Map<? extends Long, ? extends Map<String, Object>> values) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
-    public boolean hasUpdates() {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
     public void setNotificationUri(ContentResolver cr, Uri uri) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
     @SuppressWarnings("deprecation")
-    public boolean supportsUpdates() {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
-    public boolean deleteRow() {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
     public void unregisterContentObserver(ContentObserver observer) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
@@ -217,48 +192,7 @@
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
-    @SuppressWarnings("deprecation")
-    public boolean updateBlob(int columnIndex, byte[] value) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
-    public boolean updateDouble(int columnIndex, double value) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
-    public boolean updateFloat(int columnIndex, float value) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
-    public boolean updateInt(int columnIndex, int value) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
-    public boolean updateLong(int columnIndex, long value) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
-    public boolean updateShort(int columnIndex, short value) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
-    public boolean updateString(int columnIndex, String value) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
-    public boolean updateToNull(int columnIndex) {
-        throw new UnsupportedOperationException("unimplemented mock method");
-    }
-
-    @SuppressWarnings("deprecation")
-    public void abortUpdates() {
+    public int getType(int columnIndex) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 }
\ No newline at end of file
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index 0be5bea..183be41 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -32,6 +32,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 
+import java.io.FileNotFoundException;
 import java.util.ArrayList;
 
 /**
@@ -102,4 +103,13 @@
     public IBinder asBinder() {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
+
+    public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+            throws RemoteException, FileNotFoundException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
 }
diff --git a/test-runner/src/android/test/suitebuilder/TestGrouping.java b/test-runner/src/android/test/suitebuilder/TestGrouping.java
index df6da70..a2b94ff 100644
--- a/test-runner/src/android/test/suitebuilder/TestGrouping.java
+++ b/test-runner/src/android/test/suitebuilder/TestGrouping.java
@@ -46,6 +46,8 @@
  */
 public class TestGrouping {
 
+    private static final String LOG_TAG = "TestGrouping";
+
     SortedSet<Class<? extends TestCase>> testCaseClasses;
 
     public static final Comparator<Class<? extends TestCase>> SORT_BY_SIMPLE_NAME
@@ -114,7 +116,7 @@
         for (String packageName : packageNames) {
             List<Class<? extends TestCase>> addedClasses = testCaseClassesInPackage(packageName);
             if (addedClasses.isEmpty()) {
-                Log.w("TestGrouping", "Invalid Package: '" + packageName
+                Log.w(LOG_TAG, "Invalid Package: '" + packageName
                         + "' could not be found or has no tests");
             }
             testCaseClasses.addAll(addedClasses);
@@ -234,6 +236,10 @@
                     }
                 }
             }
+            Log.i(LOG_TAG, String.format(
+                    "TestCase class %s is missing a public constructor with no parameters " +
+                    "or a single String parameter - skipping",
+                    aClass.getName()));
             return false;
         }
     }
diff --git a/test-runner/tests/src/android/test/suitebuilder/TestGroupingTest.java b/test-runner/tests/src/android/test/suitebuilder/TestGroupingTest.java
new file mode 100644
index 0000000..f4477d1
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/TestGroupingTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.suitebuilder;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link TestGrouping}
+ */
+public class TestGroupingTest extends TestCase {
+
+    private TestGrouping mGrouping;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mGrouping = new TestGrouping(TestGrouping.SORT_BY_SIMPLE_NAME);
+    }
+
+    /**
+     * Verifies that TestCases with no public constructor are not loaded.
+     * Relies on fixture classes in android.test.suitebuilder.examples.constructor
+     */
+    public void testGetTests_noPublicConstructor() {
+        mGrouping.addPackagesRecursive("android.test.suitebuilder.examples.constructor");
+        List<TestMethod> tests = mGrouping.getTests();
+        // only the PublicConstructorTest's test method should be present
+        assertEquals(1, tests.size());
+        assertEquals("testPublicConstructor", tests.get(0).getName());
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/constructor/NoPublicConstructorTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/constructor/NoPublicConstructorTest.java
new file mode 100644
index 0000000..d7909a1
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/constructor/NoPublicConstructorTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.suitebuilder.examples.constructor;
+
+import junit.framework.TestCase;
+
+/**
+ * A {@link TestCase} which should not be loaded since it has non-public constructors with no args.
+ */
+public class NoPublicConstructorTest extends TestCase {
+
+    NoPublicConstructorTest() {
+    }
+
+    public NoPublicConstructorTest(String foo, String foo2) {
+    }
+
+    public void testNotRun() {
+        fail("method in NoPublicConstructorTest run unexpectedly");
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/constructor/ProtectedConstructorTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/constructor/ProtectedConstructorTest.java
new file mode 100644
index 0000000..d2862fd
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/constructor/ProtectedConstructorTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.suitebuilder.examples.constructor;
+
+import junit.framework.TestCase;
+
+/**
+ * A protected constructor test case that should not be loaded.
+ */
+public class ProtectedConstructorTest extends TestCase {
+
+    protected ProtectedConstructorTest() {
+    }
+
+    public void testNotRun() {
+        fail("method in ProtectedConstructorTest run unexpectedly");
+    }
+
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/constructor/PublicConstructorTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/constructor/PublicConstructorTest.java
new file mode 100644
index 0000000..a11e25d
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/constructor/PublicConstructorTest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.suitebuilder.examples.constructor;
+
+import junit.framework.TestCase;
+
+/**
+ * A public constructor test case that should be loaded.
+ */
+public class PublicConstructorTest extends TestCase {
+
+    public void testPublicConstructor() {
+    }
+}
diff --git a/tests/BrowserTestPlugin/jni/PluginObject.h b/tests/BrowserTestPlugin/jni/PluginObject.h
index a058d4a..037367e 100644
--- a/tests/BrowserTestPlugin/jni/PluginObject.h
+++ b/tests/BrowserTestPlugin/jni/PluginObject.h
@@ -65,7 +65,7 @@
 public:
     SubPlugin(NPP inst) : m_inst(inst) {}
     virtual ~SubPlugin() {}
-    virtual int16 handleEvent(const ANPEvent* evt) = 0;
+    virtual int16_t handleEvent(const ANPEvent* evt) = 0;
 
     NPP inst() const { return m_inst; }
 
diff --git a/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp b/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
index 2eff394..91f1b3d 100644
--- a/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
+++ b/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
@@ -138,7 +138,7 @@
     browser->memfree(beginMem);
 }
 
-int16 EventPlugin::handleEvent(const ANPEvent* evt) {
+int16_t EventPlugin::handleEvent(const ANPEvent* evt) {
     switch (evt->eventType) {
 
         case kDraw_ANPEventType: {
diff --git a/tests/BrowserTestPlugin/jni/event/EventPlugin.h b/tests/BrowserTestPlugin/jni/event/EventPlugin.h
index 88b7c9d..043be85 100644
--- a/tests/BrowserTestPlugin/jni/event/EventPlugin.h
+++ b/tests/BrowserTestPlugin/jni/event/EventPlugin.h
@@ -32,7 +32,7 @@
 public:
     EventPlugin(NPP inst);
     virtual ~EventPlugin();
-    virtual int16 handleEvent(const ANPEvent* evt);
+    virtual int16_t handleEvent(const ANPEvent* evt);
 
 private:
     void drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip);
diff --git a/tests/BrowserTestPlugin/jni/main.cpp b/tests/BrowserTestPlugin/jni/main.cpp
index 402a7e2..511180c 100644
--- a/tests/BrowserTestPlugin/jni/main.cpp
+++ b/tests/BrowserTestPlugin/jni/main.cpp
@@ -34,19 +34,19 @@
 NPNetscapeFuncs* browser;
 #define EXPORT __attribute__((visibility("default")))
 
-NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc,
         char* argn[], char* argv[], NPSavedData* saved);
 NPError NPP_Destroy(NPP instance, NPSavedData** save);
 NPError NPP_SetWindow(NPP instance, NPWindow* window);
 NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
-        NPBool seekable, uint16* stype);
+        NPBool seekable, uint16_t* stype);
 NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason);
-int32   NPP_WriteReady(NPP instance, NPStream* stream);
-int32   NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len,
+int32_t   NPP_WriteReady(NPP instance, NPStream* stream);
+int32_t   NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len,
         void* buffer);
 void    NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
 void    NPP_Print(NPP instance, NPPrint* platformPrint);
-int16   NPP_HandleEvent(NPP instance, void* event);
+int16_t   NPP_HandleEvent(NPP instance, void* event);
 void    NPP_URLNotify(NPP instance, const char* URL, NPReason reason,
         void* notifyData);
 NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value);
@@ -129,7 +129,7 @@
     return "application/x-browsertestplugin:btp:Android Browser Test Plugin";
 }
 
-NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc,
                 char* argn[], char* argv[], NPSavedData* saved)
 {
 
@@ -188,7 +188,7 @@
     return NPERR_NO_ERROR;
 }
 
-NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype)
 {
     *stype = NP_ASFILEONLY;
     return NPERR_NO_ERROR;
@@ -199,12 +199,12 @@
     return NPERR_NO_ERROR;
 }
 
-int32 NPP_WriteReady(NPP instance, NPStream* stream)
+int32_t NPP_WriteReady(NPP instance, NPStream* stream)
 {
     return 0;
 }
 
-int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer)
+int32_t NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer)
 {
     return 0;
 }
@@ -217,7 +217,7 @@
 {
 }
 
-int16 NPP_HandleEvent(NPP instance, void* event)
+int16_t NPP_HandleEvent(NPP instance, void* event)
 {
     PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
     const ANPEvent* evt = reinterpret_cast<const ANPEvent*>(event);
diff --git a/tests/CoreTests/android/core/NIOTest.java b/tests/CoreTests/android/core/NIOTest.java
index fd279ca..9476d07 100644
--- a/tests/CoreTests/android/core/NIOTest.java
+++ b/tests/CoreTests/android/core/NIOTest.java
@@ -40,27 +40,31 @@
     }
 
     @SmallTest
-    public void testNIO() throws Exception {
-        ByteBuffer b;
-
+    public void testNIO_byte_array() throws Exception {
         // Test byte array-based buffer
-        b = ByteBuffer.allocate(12);
-        byteBufferTest(b);
+        byteBufferTest(ByteBuffer.allocate(12));
+    }
 
+    public void testNIO_direct() throws Exception {
         // Test native heap-allocated buffer
-        b = ByteBuffer.allocateDirect(12);
-        byteBufferTest(b);
+        byteBufferTest(ByteBuffer.allocateDirect(12));
+    }
 
+    public void testNIO_short_array() throws Exception {
         // Test short array-based buffer
         short[] shortArray = new short[8];
         ShortBuffer sb = ShortBuffer.wrap(shortArray);
         shortBufferTest(sb);
+    }
 
+    public void testNIO_int_array() throws Exception {
         // Test int array-based buffer
         int[] intArray = new int[8];
         IntBuffer ib = IntBuffer.wrap(intArray);
         intBufferTest(ib);
+    }
 
+    public void testNIO_float_array() throws Exception {
         // Test float array-based buffer
         float[] floatArray = new float[8];
         FloatBuffer fb = FloatBuffer.wrap(floatArray);
@@ -70,6 +74,12 @@
     private void byteBufferTest(ByteBuffer b) {
         checkBuffer(b);
 
+        // Duplicate buffers revert to big-endian.
+        b.order(ByteOrder.LITTLE_ENDIAN);
+        ByteBuffer dupe = b.duplicate();
+        assertEquals(ByteOrder.BIG_ENDIAN, dupe.order());
+        b.order(ByteOrder.BIG_ENDIAN);
+
         // Bounds checks
         try {
             b.put(-1, (byte) 0);
@@ -272,9 +282,9 @@
         // Check 'getFloat'
         b.order(ByteOrder.LITTLE_ENDIAN);
         b.position(0);
-        assertEquals(0xA3A2A1A0, Float.floatToIntBits(b.getFloat()));
-        assertEquals(0xA7A6A5A4, Float.floatToIntBits(b.getFloat()));
-        assertEquals(0xABAAA9A8, Float.floatToIntBits(b.getFloat()));
+        assertEquals(0xA3A2A1A0, Float.floatToRawIntBits(b.getFloat()));
+        assertEquals(0xA7A6A5A4, Float.floatToRawIntBits(b.getFloat()));
+        assertEquals(0xABAAA9A8, Float.floatToRawIntBits(b.getFloat()));
         try {
             b.getFloat();
             fail("expected exception not thrown");
@@ -284,9 +294,9 @@
 
         b.order(ByteOrder.BIG_ENDIAN);
         b.position(0);
-        assertEquals(0xA0A1A2A3, Float.floatToIntBits(b.getFloat()));
-        assertEquals(0xA4A5A6A7, Float.floatToIntBits(b.getFloat()));
-        assertEquals(0xA8A9AAAB, Float.floatToIntBits(b.getFloat()));
+        assertEquals(0xA0A1A2A3, Float.floatToRawIntBits(b.getFloat()));
+        assertEquals(0xA4A5A6A7, Float.floatToRawIntBits(b.getFloat()));
+        assertEquals(0xA8A9AAAB, Float.floatToRawIntBits(b.getFloat()));
         try {
             b.getFloat();
             fail("expected exception not thrown");
@@ -296,8 +306,8 @@
 
         // Check 'getDouble(int position)'
         b.order(ByteOrder.LITTLE_ENDIAN);
-        assertEquals(0xA7A6A5A4A3A2A1A0L, Double.doubleToLongBits(b.getDouble(0)));
-        assertEquals(0xA8A7A6A5A4A3A2A1L, Double.doubleToLongBits(b.getDouble(1)));
+        assertEquals(0xA7A6A5A4A3A2A1A0L, Double.doubleToRawLongBits(b.getDouble(0)));
+        assertEquals(0xA8A7A6A5A4A3A2A1L, Double.doubleToRawLongBits(b.getDouble(1)));
         try {
             b.getDouble(-1);
             fail("expected exception not thrown");
@@ -312,8 +322,8 @@
         }
 
         b.order(ByteOrder.BIG_ENDIAN);
-        assertEquals(0xA0A1A2A3A4A5A6A7L, Double.doubleToLongBits(b.getDouble(0)));
-        assertEquals(0xA1A2A3A4A5A6A7A8L, Double.doubleToLongBits(b.getDouble(1)));
+        assertEquals(0xA0A1A2A3A4A5A6A7L, Double.doubleToRawLongBits(b.getDouble(0)));
+        assertEquals(0xA1A2A3A4A5A6A7A8L, Double.doubleToRawLongBits(b.getDouble(1)));
         try {
             b.getDouble(-1);
             fail("expected exception not thrown");
@@ -333,6 +343,9 @@
         b.order(ByteOrder.LITTLE_ENDIAN);
         bb = b.slice();
         assertEquals(4, bb.capacity());
+        assertEquals(ByteOrder.BIG_ENDIAN, bb.order());
+        assertEquals(0xA1A2A3A4, bb.getInt(0));
+        bb.order(ByteOrder.LITTLE_ENDIAN);
         assertEquals(0xA4A3A2A1, bb.getInt(0));
 
         bb.order(ByteOrder.LITTLE_ENDIAN);
@@ -370,14 +383,14 @@
 
         checkBuffer(fb);
         assertEquals(1, fb.capacity());
-        assertEquals(0xA4A3A2A1, Float.floatToIntBits(fb.get()));
+        assertEquals(0xA4A3A2A1, Float.floatToRawIntBits(fb.get()));
 
         bb.order(ByteOrder.BIG_ENDIAN);
         fb = bb.asFloatBuffer();
 
         checkBuffer(fb);
         assertEquals(1, fb.capacity());
-        assertEquals(0xA1A2A3A4, Float.floatToIntBits(fb.get()));
+        assertEquals(0xA1A2A3A4, Float.floatToRawIntBits(fb.get()));
     }
 
     private void shortBufferTest(ShortBuffer sb) {
diff --git a/tests/DpiTest/Android.mk b/tests/DpiTest/Android.mk
index 5463a17..f55ce6e 100644
--- a/tests/DpiTest/Android.mk
+++ b/tests/DpiTest/Android.mk
@@ -7,6 +7,6 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_AAPT_FLAGS = -c 120dpi -c 240dpi -c 160dpi
+LOCAL_AAPT_FLAGS = -c 120dpi,240dpi,160dpi,nodpi
 
 include $(BUILD_PACKAGE)
diff --git a/tests/DumpRenderTree/AndroidManifest.xml b/tests/DumpRenderTree/AndroidManifest.xml
index 03b7e26..c151251 100644
--- a/tests/DumpRenderTree/AndroidManifest.xml
+++ b/tests/DumpRenderTree/AndroidManifest.xml
@@ -18,15 +18,18 @@
     <application android:name="HTMLHostApp">
         <uses-library android:name="android.test.runner" />
         <activity android:name="Menu" android:label="Dump Render Tree"
-          android:screenOrientation="portrait">
+          android:screenOrientation="portrait"
+          android:theme="@android:style/Theme.Light">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
             </intent-filter>
         </activity>
         <activity android:name="TestShellActivity" android:launchMode="singleTop"
-          android:screenOrientation="portrait"/>
-        <activity android:name="ReliabilityTestActivity" android:screenOrientation="portrait"/>
+          android:screenOrientation="portrait"
+          android:theme="@android:style/Theme.Light"/>
+        <activity android:name="ReliabilityTestActivity" android:screenOrientation="portrait"
+          android:theme="@android:style/Theme.Light"/>
     </application>
 
     <instrumentation android:name=".LayoutTestsAutoRunner"
diff --git a/tests/DumpRenderTree/assets/run_layout_tests.py b/tests/DumpRenderTree/assets/run_layout_tests.py
index b6e7bf3..ceac5d2 100755
--- a/tests/DumpRenderTree/assets/run_layout_tests.py
+++ b/tests/DumpRenderTree/assets/run_layout_tests.py
@@ -176,7 +176,7 @@
   # Count crashed tests.
   crashed_tests = []
 
-  timeout_ms = '30000'
+  timeout_ms = '15000'
   if options.time_out_ms:
     timeout_ms = options.time_out_ms
 
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
index 5780c43..740f544 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
@@ -74,6 +74,8 @@
     private static final int LAYOUT_SET_CAN_OPEN_WINDOWS = 42;
     private static final int SET_GEOLOCATION_PERMISSION = 43;
     private static final int OVERRIDE_PREFERENCE = 44;
+    private static final int LAYOUT_DUMP_CHILD_FRAMES_TEXT = 45;
+    private static final int SET_XSS_AUDITOR_ENABLED = 46;
     
     CallbackProxy(EventSender eventSender, 
             LayoutTestController layoutTestController) {
@@ -175,7 +177,11 @@
             break;
 
         case LAYOUT_DUMP_TEXT:
-            mLayoutTestController.dumpAsText();
+            mLayoutTestController.dumpAsText(msg.arg1 == 1);
+            break;
+
+        case LAYOUT_DUMP_CHILD_FRAMES_TEXT:
+            mLayoutTestController.dumpChildFramesAsText();
             break;
 
         case LAYOUT_DUMP_HISTORY:
@@ -273,6 +279,10 @@
             boolean value = msg.getData().getBoolean("value");
             mLayoutTestController.overridePreference(key, value);
             break;
+
+        case SET_XSS_AUDITOR_ENABLED:
+            mLayoutTestController.setXSSAuditorEnabled(msg.arg1 == 1);
+            break;
         }
     }
 
@@ -377,7 +387,15 @@
     }
 
     public void dumpAsText() {
-        obtainMessage(LAYOUT_DUMP_TEXT).sendToTarget();
+        obtainMessage(LAYOUT_DUMP_TEXT, 0).sendToTarget();
+    }
+
+    public void dumpAsText(boolean enablePixelTests) {
+        obtainMessage(LAYOUT_DUMP_TEXT, enablePixelTests ? 1 : 0).sendToTarget();
+    }
+
+    public void dumpChildFramesAsText() {
+        obtainMessage(LAYOUT_DUMP_CHILD_FRAMES_TEXT).sendToTarget();
     }
 
     public void dumpBackForwardList() {
@@ -492,10 +510,22 @@
         obtainMessage(SET_GEOLOCATION_PERMISSION, allow ? 1 : 0, 0).sendToTarget();
     }
 
+    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+        // Configuration is in WebKit, so stay on WebCore thread, but go via the TestShellActivity
+        // as we need access to the Webview.
+        mLayoutTestController.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+                canProvideGamma, gamma);
+    }
+
     public void overridePreference(String key, boolean value) {
         Message message = obtainMessage(OVERRIDE_PREFERENCE);
         message.getData().putString("key", key);
         message.getData().putBoolean("value", value);
         message.sendToTarget();
     }
+
+    public void setXSSAuditorEnabled(boolean flag) {
+        obtainMessage(SET_XSS_AUDITOR_ENABLED, flag ? 1 : 0, 0).sendToTarget();
+    }
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 77fd3ed..50111f6 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -72,44 +72,83 @@
     };
 
     static final String[] ignoreTestList = {
+        "canvas/philip/tests/2d.drawImage.broken.html", // blocks test, http://b/2982500
         "editing/selection/move-left-right.html", // Causes DumpRenderTree to hang
+        "fast/js/excessive-comma-usage.html", // Tests huge initializer list, causes OOM.
         "fast/js/regexp-charclass-crash.html", // RegExp is too large, causing OOM
+        "fast/js/regexp-overflow.html", // Result is too large, causing OOM when reading by DRT, http://b/2697589
         "fast/regex/test1.html", // Causes DumpRenderTree to hang with V8
-        "fast/regex/slow.html" // Causes DumpRenderTree to hang with V8
+        "fast/regex/slow.html", // Causes DumpRenderTree to hang with V8
+        "http/tests/xmlhttprequest/simple-cross-origin-progress-events.html", // runs webcore into bad state, http://b/2929261
+        "ietestcenter/Javascript/15.4.4.15-3-14.html", // hangs the layout tests, http://b/2889595
+        "ietestcenter/Javascript/15.4.4.15-3-29.html", // hangs the layout tests, http://b/2889596
+        "ietestcenter/Javascript/15.4.4.15-3-8.html", // hangs the layout tests, http://b/2889598
     };
 
     static void fillIgnoreResultList() {
-        // This first block of tests are for HTML5 features, for which Android
+        // This first block of tests are for features for which Android
         // should pass all tests. They are skipped only temporarily.
         // TODO: Fix these failing tests and remove them from this list.
+        ignoreResultList.add("fast/encoding/char-encoding.html"); // fails in Java HTTP stack, see http://b/issue?id=3047156
+        ignoreResultList.add("fast/encoding/char-decoding.html"); // fails in Java HTTP stack, see http://b/issue?id=3047156
+        ignoreResultList.add("fast/encoding/hanarei-blog32-fc2-com.html"); // fails in Java HTTP stack, see http://b/issue?id=3046986
+        ignoreResultList.add("fast/encoding/mailto-always-utf-8.html"); // Requires waitForPolicyDelegate(), see http://b/issue?id=3043468
+        ignoreResultList.add("fast/encoding/percent-escaping.html"); // fails in Java HTTP stack, see http://b/issue?id=3046984
         ignoreResultList.add("http/tests/appcache/empty-manifest.html"); // flaky
+        ignoreResultList.add("http/tests/appcache/fallback.html"); // http://b/issue?id=2713004
         ignoreResultList.add("http/tests/appcache/foreign-iframe-main.html"); // flaky - skips states
         ignoreResultList.add("http/tests/appcache/manifest-with-empty-file.html"); // flaky
+        ignoreResultList.add("http/tests/appcache/origin-quota.html"); // needs clearAllApplicationCaches(), see http://b/issue?id=2944196
         ignoreResultList.add("storage/database-lock-after-reload.html"); // Succeeds but DumpRenderTree does not read result correctly
         ignoreResultList.add("storage/hash-change-with-xhr.html"); // Succeeds but DumpRenderTree does not read result correctly
+        ignoreResultList.add("storage/open-database-creation-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
+        ignoreResultList.add("storage/statement-error-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
+        ignoreResultList.add("storage/statement-success-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
+        ignoreResultList.add("storage/transaction-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
+        ignoreResultList.add("storage/transaction-error-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
+        ignoreResultList.add("storage/transaction-success-callback-isolated-world.html"); // Requires layoutTestController.evaluateScriptInIsolatedWorld()
 
-        // Will always fail
-        ignoreResultList.add("dom/svg/level3/xpath"); // XPath not supported
+        // Expected failures due to unsupported features or tests unsuitable for Android.
+        ignoreResultList.add("fast/encoding/char-decoding-mac.html"); // Mac-specific encodings (also marked Won't Fix in Chromium, bug 7388)
+        ignoreResultList.add("fast/encoding/char-encoding-mac.html"); // Mac-specific encodings (also marked Won't Fix in Chromium, bug 7388)
+        ignoreResultList.add("fast/encoding/idn-security.html"); // Mac-specific IDN checks (also marked Won't Fix in Chromium, bug 21814)
+        ignoreResultList.add("fast/events/touch/basic-multi-touch-events.html"); // Requires multi-touch gestures not supported by Android system
+        ignoreResultList.add("fast/events/touch/touch-coords-in-zoom-and-scroll.html"); // Requires eventSender.zoomPageIn(),zoomPageOut()
+        ignoreResultList.add("fast/events/touch/touch-target.html"); // Requires multi-touch gestures not supported by Android system
         ignoreResultList.add("fast/workers"); // workers not supported
-        ignoreResultList.add("fast/xpath"); // XPath not supported
         ignoreResultList.add("http/tests/eventsource/workers"); // workers not supported
         ignoreResultList.add("http/tests/workers"); // workers not supported
         ignoreResultList.add("http/tests/xmlhttprequest/workers"); // workers not supported
         ignoreResultList.add("storage/domstorage/localstorage/private-browsing-affects-storage.html"); // private browsing not supported
         ignoreResultList.add("storage/domstorage/sessionstorage/private-browsing-affects-storage.html"); // private browsing not supported
+        ignoreResultList.add("storage/indexeddb"); // indexeddb not supported
         ignoreResultList.add("storage/private-browsing-readonly.html"); // private browsing not supported
         ignoreResultList.add("websocket/tests/workers"); // workers not supported
 
+        // Expected failures due to missing expected results
+        ignoreResultList.add("dom/xhtml/level3/core/canonicalform08.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/canonicalform09.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/documentgetinputencoding03.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/entitygetinputencoding02.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/entitygetxmlversion02.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri05.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri07.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri09.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri10.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri11.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri15.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri17.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/nodegetbaseuri18.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/nodelookupnamespaceuri01.xhtml");
+        ignoreResultList.add("dom/xhtml/level3/core/nodelookupprefix19.xhtml");
+
         // TODO: These need to be triaged
         ignoreResultList.add("fast/css/case-transform.html"); // will not fix #619707
         ignoreResultList.add("fast/dom/Element/offsetLeft-offsetTop-body-quirk.html"); // different screen size result in extra spaces in Apple compared to us
         ignoreResultList.add("fast/dom/Window/Plug-ins.html"); // need test plugin
-        ignoreResultList.add("fast/dom/Window/window-properties.html"); // xslt and xpath elements missing from property list
         ignoreResultList.add("fast/dom/Window/window-screen-properties.html"); // pixel depth
         ignoreResultList.add("fast/dom/Window/window-xy-properties.html"); // requires eventSender.mouseDown(),mouseUp()
         ignoreResultList.add("fast/dom/attribute-namespaces-get-set.html"); // http://b/733229
-        ignoreResultList.add("fast/dom/gc-9.html"); // requires xpath support
-        ignoreResultList.add("fast/dom/global-constructors.html"); // requires xslt and xpath support
         ignoreResultList.add("fast/dom/object-embed-plugin-scripting.html"); // dynamic plugins not supported
         ignoreResultList.add("fast/dom/tabindex-clamp.html"); // there is extra spacing in the file due to multiple input boxes fitting on one line on Apple, ours are wrapped. Space at line ends are stripped.
         ignoreResultList.add("fast/events/anchor-image-scrolled-x-y.html"); // requires eventSender.mouseDown(),mouseUp()
@@ -172,8 +211,6 @@
         ignoreResultList.add("fast/replaced/image-map.html"); // requires eventSender.mouseDown(),mouseUp()
         ignoreResultList.add("fast/text/plain-text-line-breaks.html"); // extra spacing because iFrames rendered next to each other on Apple
         ignoreResultList.add("profiler"); // profiler is not supported
-        ignoreResultList.add("svg"); // svg is not supported
-
     }
 
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
index 6cfce41..5d34e25 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java
@@ -49,8 +49,13 @@
         //no creation of instances
     }
 
-    public static void findLayoutTestsRecursively(BufferedOutputStream bos,
+    /**
+     * @return the number of tests in the list.
+     */
+    public static int writeLayoutTestListRecursively(BufferedOutputStream bos,
             String dir, boolean ignoreResultsInDir) throws IOException {
+
+        int testCount = 0;
         Log.v(LOGTAG, "Searching tests under " + dir);
 
         File d = new File(dir);
@@ -68,7 +73,7 @@
                 // If this is not a test directory, we don't recurse into it.
                 if (!FileFilter.isNonTestDir(s)) {
                     Log.v(LOGTAG, "Recursing on " + s);
-                    findLayoutTestsRecursively(bos, s, ignoreResultsInDir);
+                    testCount += writeLayoutTestListRecursively(bos, s, ignoreResultsInDir);
                 }
                 continue;
             }
@@ -79,7 +84,9 @@
                 continue;
             }
 
-            if ((s.toLowerCase().endsWith(".html") || s.toLowerCase().endsWith(".xml"))
+            if ((s.toLowerCase().endsWith(".html")
+                    || s.toLowerCase().endsWith(".xml")
+                    || s.toLowerCase().endsWith(".xhtml"))
                     && !s.endsWith("TEMPLATE.html")) {
                 Log.v(LOGTAG, "Recording " + s);
                 bos.write(s.getBytes());
@@ -88,8 +95,10 @@
                     bos.write((" IGNORE_RESULT").getBytes());
                 }
                 bos.write('\n');
+                testCount++;
             }
         }
+        return testCount;
     }
 
     public static void updateTestStatus(String statusFile, String s) {
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
index 9236345..9be2f1c 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
@@ -18,29 +18,30 @@
 
 public interface LayoutTestController {
 
-	public void dumpAsText();
-	public void waitUntilDone();
-	public void notifyDone();
-	
-	// Force a redraw of the page
-	public void display();
-	// Used with pixel dumps of content
-	public void testRepaint();
-	
-	// If the page title changes, add the information to the output.
-	public void dumpTitleChanges();
-	public void dumpBackForwardList();
-	public void dumpChildFrameScrollPositions();
-	public void dumpEditingCallbacks();
-	
-	// Show/Hide window for window.onBlur() testing
-	public void setWindowIsKey(boolean b);
-	// Mac function, used to disable events going to the window
-	public void setMainFrameIsFirstResponder(boolean b);
-	
-	public void dumpSelectionRect();
-	
-	// invalidate and draw one line at a time of the web view.
+    public void dumpAsText(boolean enablePixelTests);
+    public void dumpChildFramesAsText();
+    public void waitUntilDone();
+    public void notifyDone();
+
+    // Force a redraw of the page
+    public void display();
+    // Used with pixel dumps of content
+    public void testRepaint();
+
+    // If the page title changes, add the information to the output.
+    public void dumpTitleChanges();
+    public void dumpBackForwardList();
+    public void dumpChildFrameScrollPositions();
+    public void dumpEditingCallbacks();
+
+    // Show/Hide window for window.onBlur() testing
+    public void setWindowIsKey(boolean b);
+    // Mac function, used to disable events going to the window
+    public void setMainFrameIsFirstResponder(boolean b);
+
+    public void dumpSelectionRect();
+
+    // invalidate and draw one line at a time of the web view.
     public void repaintSweepHorizontally();
     
     // History testing functions
@@ -67,4 +68,11 @@
     public void setGeolocationPermission(boolean allow);
 
     public void overridePreference(String key, boolean value);
+
+    // For XSSAuditor tests
+    public void setXSSAuditorEnabled(boolean flag);
+
+    // For DeviceOrientation tests
+    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma);
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index 9ccf549..132f17a 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -20,9 +20,7 @@
 import com.android.dumprendertree.forwarder.AdbUtils;
 import com.android.dumprendertree.forwarder.ForwardService;
 
-import android.app.Instrumentation;
 import android.content.Intent;
-import android.os.Bundle;
 import android.os.Environment;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
@@ -158,18 +156,11 @@
     private String mJsEngine;
     private String mTestPathPrefix;
     private boolean mFinished;
+    private int mTestCount;
+    private int mResumeIndex;
 
     public LayoutTestsAutoTest() {
-      super("com.android.dumprendertree", TestShellActivity.class);
-    }
-
-    // This function writes the result of the layout test to
-    // Am status so that it can be picked up from a script.
-    private void passOrFailCallback(String file, boolean result) {
-      Instrumentation inst = getInstrumentation();
-      Bundle bundle = new Bundle();
-      bundle.putBoolean(file, result);
-      inst.sendStatus(0, bundle);
+      super(TestShellActivity.class);
     }
 
     private void getTestList() {
@@ -190,6 +181,7 @@
         } catch (Exception e) {
             Log.e(LOGTAG, "Error while reading test list : " + e.getMessage());
         }
+        mTestCount = mTestList.size();
     }
 
     private void resumeTestList() {
@@ -200,6 +192,7 @@
                 if (mTestList.elementAt(i).equals(line)) {
                     mTestList = new Vector<String>(mTestList.subList(i+1, mTestList.size()));
                     mTestListIgnoreResult = new Vector<Boolean>(mTestListIgnoreResult.subList(i+1, mTestListIgnoreResult.size()));
+                    mResumeIndex = i + 1;
                     break;
                 }
             }
@@ -232,14 +225,22 @@
         // The generic result is at <path>/<name>-expected.txt
         // First try the Android-specific result at
         // platform/android-<js-engine>/<path>/<name>-expected.txt
+        // then
+        // platform/android/<path>/<name>-expected.txt
         int pos = test.lastIndexOf('.');
         if (pos == -1)
             return null;
         String genericExpectedResult = test.substring(0, pos) + "-expected.txt";
         String androidExpectedResultsDir = "platform/android-" + mJsEngine + "/";
-        String androidExpectedResult =
-            genericExpectedResult.replaceFirst(LAYOUT_TESTS_ROOT, LAYOUT_TESTS_ROOT + androidExpectedResultsDir);
+        String androidExpectedResult = genericExpectedResult.replaceFirst(LAYOUT_TESTS_ROOT,
+                LAYOUT_TESTS_ROOT + androidExpectedResultsDir);
         File f = new File(androidExpectedResult);
+        if (f.exists())
+            return androidExpectedResult;
+        androidExpectedResultsDir = "platform/android/";
+        androidExpectedResult = genericExpectedResult.replaceFirst(LAYOUT_TESTS_ROOT,
+                LAYOUT_TESTS_ROOT + androidExpectedResultsDir);
+        f = new File(androidExpectedResult);
         return f.exists() ? androidExpectedResult : genericExpectedResult;
     }
 
@@ -300,7 +301,7 @@
         }
     }
 
-    private void runTestAndWaitUntilDone(TestShellActivity activity, String test, int timeout, boolean ignoreResult) {
+    private void runTestAndWaitUntilDone(TestShellActivity activity, String test, int timeout, boolean ignoreResult, int testNumber) {
         activity.setCallback(new TestShellCallback() {
             public void finished() {
                 synchronized (LayoutTestsAutoTest.this) {
@@ -336,6 +337,9 @@
         intent.putExtra(TestShellActivity.TEST_URL, FsUtils.getTestUrl(test));
         intent.putExtra(TestShellActivity.RESULT_FILE, resultFile);
         intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout);
+        intent.putExtra(TestShellActivity.TOTAL_TEST_COUNT, mTestCount);
+        intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, testNumber);
+        intent.putExtra(TestShellActivity.STOP_ON_REF_ERROR, true);
         activity.startActivity(intent);
 
         // Wait until done.
@@ -375,8 +379,8 @@
         // Read settings
         mTestPathPrefix = (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getAbsolutePath();
         mRebaselineResults = runner.mRebaseline;
-        // JSC is the default JavaScript engine.
-        mJsEngine = runner.mJsEngine == null ? "jsc" : runner.mJsEngine;
+        // V8 is the default JavaScript engine.
+        mJsEngine = runner.mJsEngine == null ? "v8" : runner.mJsEngine;
 
         int timeout = runner.mTimeoutInMillis;
         if (timeout <= 0) {
@@ -393,7 +397,7 @@
             resumeTestList();
 
         TestShellActivity activity = getActivity();
-        activity.setDefaultDumpDataType(DumpDataType.DUMP_AS_TEXT);
+        activity.setDefaultDumpDataType(DumpDataType.EXT_REPR);
 
         // Run tests.
         int addr = -1;
@@ -410,7 +414,9 @@
             boolean ignoreResult = mTestListIgnoreResult.elementAt(i);
             FsUtils.updateTestStatus(TEST_STATUS_FILE, s);
             // Run tests
-            runTestAndWaitUntilDone(activity, s, runner.mTimeoutInMillis, ignoreResult);
+            // i is 0 based, but test count is 1 based so add 1 to i here.
+            runTestAndWaitUntilDone(activity, s, runner.mTimeoutInMillis, ignoreResult,
+                    i + 1 + mResumeIndex);
         }
 
         FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE");
@@ -435,7 +441,7 @@
         try {
             File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false));
-            FsUtils.findLayoutTestsRecursively(bos, getTestPath(), false); // Don't ignore results
+            FsUtils.writeLayoutTestListRecursively(bos, getTestPath(), false); // Don't ignore results
             bos.flush();
             bos.close();
        } catch (Exception e) {
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
index 9c4b57256..0b00d65 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/Menu.java
@@ -53,29 +53,38 @@
         intent.setClass(this, TestShellActivity.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         intent.putExtra(TestShellActivity.TEST_URL, "file://" + filename);
+        intent.putExtra(TestShellActivity.TOTAL_TEST_COUNT, 1);
+        intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, 1);
         startActivity(intent);
     }
 
     @Override
     void processDirectory(String path, boolean selection) {
-        generateTestList(path);
+        int testCount = generateTestList(path);
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setClass(this, TestShellActivity.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         intent.putExtra(TestShellActivity.UI_AUTO_TEST, LAYOUT_TESTS_LIST_FILE);
+        intent.putExtra(TestShellActivity.TOTAL_TEST_COUNT, testCount);
+        // TestShellActivity will process this intent once and increment the test index
+        // before running the first test, so pass 0 here to allow for that.
+        intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, 0);
         startActivity(intent);
     }
 
-    private void generateTestList(String path) {
+    private int generateTestList(String path) {
+        int testCount = 0;
         try {
             File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false));
-            FsUtils.findLayoutTestsRecursively(bos, path, false); // Don't ignore results
+            testCount = FsUtils.writeLayoutTestListRecursively(
+                    bos, path, false); // Don't ignore results
             bos.flush();
             bos.close();
        } catch (Exception e) {
            Log.e(LOGTAG, "Error when creating test list: " + e.getMessage());
        }
+       return testCount;
     }
 
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 7475719..643dbf5 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -35,6 +35,8 @@
 import android.os.Message;
 import android.util.Log;
 import android.view.ViewGroup;
+import android.view.Window;
+import android.webkit.ConsoleMessage;
 import android.webkit.GeolocationPermissions;
 import android.webkit.HttpAuthHandler;
 import android.webkit.JsPromptResult;
@@ -55,6 +57,7 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Vector;
 
@@ -63,7 +66,9 @@
     static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP}
 
     // String constants for use with layoutTestController.overridePreferences
-    private final String WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED = "WebKitOfflineWebApplicationCacheEnabled";
+    private final String WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED =
+            "WebKitOfflineWebApplicationCacheEnabled";
+    private final String WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY = "WebKitUsesPageCachePreferenceKey";
 
     public class AsyncHandler extends Handler {
         @Override
@@ -99,6 +104,8 @@
         Log.v(LOGTAG, "message sent to WebView to dump text.");
         switch (mDumpDataType) {
             case DUMP_AS_TEXT:
+                callback.arg1 = mDumpTopFrameAsText ? 1 : 0;
+                callback.arg2 = mDumpChildFramesAsText ? 1 : 0;
                 mWebView.documentAsText(callback);
                 break;
             case EXT_REPR:
@@ -117,6 +124,7 @@
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
+        requestWindowFeature(Window.FEATURE_PROGRESS);
 
         LinearLayout contentView = new LinearLayout(this);
         contentView.setOrientation(LinearLayout.VERTICAL);
@@ -139,12 +147,19 @@
         // WebView::setJsFlags is noop in JSC build.
         mWebView.setJsFlags("--expose_gc");
 
+        // Always send multitouch events to Webkit since the layout test
+        // is only for the Webkit not the browser's UI.
+        mWebView.setDeferMultiTouch(true);
+
         mHandler = new AsyncHandler();
 
         Intent intent = getIntent();
         if (intent != null) {
             executeIntent(intent);
         }
+
+        // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
+        mWebView.useMockDeviceOrientation();
     }
 
     @Override
@@ -159,6 +174,9 @@
             return;
         }
 
+        mTotalTestCount = intent.getIntExtra(TOTAL_TEST_COUNT, mTotalTestCount);
+        mCurrentTestNumber = intent.getIntExtra(CURRENT_TEST_NUMBER, mCurrentTestNumber);
+
         mTestUrl = intent.getStringExtra(TEST_URL);
         if (mTestUrl == null) {
             mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST);
@@ -172,6 +190,11 @@
         mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0);
         mGetDrawtime = intent.getBooleanExtra(GET_DRAW_TIME, false);
         mSaveImagePath = intent.getStringExtra(SAVE_IMAGE);
+        mStopOnRefError = intent.getBooleanExtra(STOP_ON_REF_ERROR, false);
+        setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount);
+        float ratio = (float)mCurrentTestNumber / mTotalTestCount;
+        int progress = (int)(ratio * Window.PROGRESS_END);
+        getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress);
 
         Log.v(LOGTAG, "  Loading " + mTestUrl);
         mWebView.loadUrl(mTestUrl);
@@ -237,6 +260,7 @@
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         intent.putExtra(TestShellActivity.TEST_URL, FsUtils.getTestUrl(url));
+        intent.putExtra(TestShellActivity.CURRENT_TEST_NUMBER, ++mCurrentTestNumber);
         intent.putExtra(TIMEOUT_IN_MILLIS, 10000);
         executeIntent(intent);
     }
@@ -329,14 +353,29 @@
 
     // .......................................
     // LayoutTestController Functions
-    public void dumpAsText() {
+    public void dumpAsText(boolean enablePixelTests) {
+        // Added after webkit update to r63859. See trac.webkit.org/changeset/63730.
+        if (enablePixelTests) {
+            Log.v(LOGTAG, "dumpAsText(enablePixelTests == true) not implemented on Android!");
+        }
+
         mDumpDataType = DumpDataType.DUMP_AS_TEXT;
+        mDumpTopFrameAsText = true;
         if (mWebView != null) {
             String url = mWebView.getUrl();
             Log.v(LOGTAG, "dumpAsText called: "+url);
         }
     }
 
+    public void dumpChildFramesAsText() {
+        mDumpDataType = DumpDataType.DUMP_AS_TEXT;
+        mDumpChildFramesAsText = true;
+        if (mWebView != null) {
+            String url = mWebView.getUrl();
+            Log.v(LOGTAG, "dumpChildFramesAsText called: "+url);
+        }
+    }
+
     public void waitUntilDone() {
         mWaitUntilDone = true;
         String url = mWebView.getUrl();
@@ -348,7 +387,9 @@
         Log.v(LOGTAG, "notifyDone called: " + url);
         if (mWaitUntilDone) {
             mWaitUntilDone = false;
-            mChromeClient.onProgressChanged(mWebView, 101);
+            if (!mRequestedWebKitData && !mTimedOut && !finished()) {
+                requestWebKitData();
+            }
         }
     }
 
@@ -459,8 +500,25 @@
      * Sets the Geolocation permission state to be used for all future requests.
      */
     public void setGeolocationPermission(boolean allow) {
-        mGeolocationPermissionSet = true;
+        mIsGeolocationPermissionSet = true;
         mGeolocationPermission = allow;
+
+        if (mPendingGeolocationPermissionCallbacks != null) {
+            Iterator iter = mPendingGeolocationPermissionCallbacks.keySet().iterator();
+            while (iter.hasNext()) {
+                GeolocationPermissions.Callback callback =
+                        (GeolocationPermissions.Callback) iter.next();
+                String origin = (String) mPendingGeolocationPermissionCallbacks.get(callback);
+                callback.invoke(origin, mGeolocationPermission, false);
+            }
+            mPendingGeolocationPermissionCallbacks = null;
+        }
+    }
+
+    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+        mWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+                canProvideGamma, gamma);
     }
 
     public void overridePreference(String key, boolean value) {
@@ -468,11 +526,21 @@
         // called the layoutTestController method. Currently, we just use the
         // WebView for the main frame. EventSender suffers from the same
         // problem.
-        if (key.equals(WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED)) {
+        if (WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED.equals(key)) {
             mWebView.getSettings().setAppCacheEnabled(value);
+        } else if (WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY.equals(key)) {
+            // Cache the maximum possible number of pages.
+            mWebView.getSettings().setPageCacheCapacity(Integer.MAX_VALUE);
+        } else {
+            Log.w(LOGTAG, "LayoutTestController.overridePreference(): " +
+                  "Unsupported preference '" + key + "'");
         }
     }
 
+    public void setXSSAuditorEnabled (boolean flag) {
+        mWebView.getSettings().setXSSAuditorEnabled(flag);
+    }
+
     private final WebViewClient mViewClient = new WebViewClient(){
         @Override
         public void onPageFinished(WebView view, String url) {
@@ -490,11 +558,30 @@
                     drawPageToFile(mSaveImagePath + "/" + name + ".png", mWebView);
                 }
             }
+
             // Calling finished() will check if we've met all the conditions for completing
-            // this test and move to the next one if we are ready.
+            // this test and move to the next one if we are ready. Otherwise we ask WebCore to
+            // dump the page.
             if (finished()) {
                 return;
             }
+
+            if (!mWaitUntilDone && !mRequestedWebKitData && !mTimedOut) {
+                requestWebKitData();
+            } else {
+                if (mWaitUntilDone) {
+                    Log.v(LOGTAG, "page finished loading but waiting for notifyDone to be called: " + url);
+                }
+
+                if (mRequestedWebKitData) {
+                    Log.v(LOGTAG, "page finished loading but webkit data has already been requested: " + url);
+                }
+
+                if (mTimedOut) {
+                    Log.v(LOGTAG, "page finished loading but already timed out: " + url);
+                }
+            }
+
             super.onPageFinished(view, url);
         }
 
@@ -536,44 +623,8 @@
 
     private final WebChromeClient mChromeClient = new WebChromeClient() {
         @Override
-        public void onProgressChanged(WebView view, int newProgress) {
-
-            // notifyDone calls this with 101%. We only want to update this flag if this
-            // is the real call from WebCore.
-            if (newProgress == 100) {
-                mOneHundredPercentComplete = true;
-            }
-
-            // With the flag updated, we can now proceed as normal whether the progress update came from
-            // WebCore or notifyDone.
-            if (newProgress >= 100) {
-                // finished() will check if we are ready to move to the next test and do so if we are.
-                if (finished()) {
-                    return;
-                }
-
-                if (!mTimedOut && !mWaitUntilDone && !mRequestedWebKitData) {
-                    String url = mWebView.getUrl();
-                    Log.v(LOGTAG, "Finished: "+ url);
-                    requestWebKitData();
-                } else {
-                    String url = mWebView.getUrl();
-                    if (mTimedOut) {
-                        Log.v(LOGTAG, "Timed out before finishing: " + url);
-                    } else if (mWaitUntilDone) {
-                        Log.v(LOGTAG, "Waiting for notifyDone: " + url);
-                    } else if (mRequestedWebKitData) {
-                        Log.v(LOGTAG, "Requested webkit data ready: " + url);
-                    }
-                }
-            }
-        }
-
-        @Override
         public void onReceivedTitle(WebView view, String title) {
-            if (title.length() > 30)
-                title = "..."+title.substring(title.length()-30);
-            setTitle(title);
+            setTitle("Test " + mCurrentTestNumber + " of " + mTotalTestCount + ": "+ title);
             if (mDumpTitleChanges) {
                 mTitleChanges.append("TITLE CHANGED: ");
                 mTitleChanges.append(title);
@@ -670,21 +721,40 @@
         @Override
         public void onGeolocationPermissionsShowPrompt(String origin,
                 GeolocationPermissions.Callback callback) {
-            if (mGeolocationPermissionSet) {
+            if (mIsGeolocationPermissionSet) {
                 callback.invoke(origin, mGeolocationPermission, false);
+                return;
             }
+            if (mPendingGeolocationPermissionCallbacks == null) {
+                mPendingGeolocationPermissionCallbacks =
+                        new HashMap<GeolocationPermissions.Callback, String>();
+            }
+            mPendingGeolocationPermissionCallbacks.put(callback, origin);
         }
 
         @Override
-        public void onConsoleMessage(String message, int lineNumber,
-                String sourceID) {
+        public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+            String msg = "CONSOLE MESSAGE: line " + consoleMessage.lineNumber() + ": "
+                    + consoleMessage.message() + "\n";
             if (mConsoleMessages == null) {
                 mConsoleMessages = new StringBuffer();
             }
-            String consoleMessage = "CONSOLE MESSAGE: line "
-                    + lineNumber +": "+ message +"\n";
-            mConsoleMessages.append(consoleMessage);
-            Log.v(LOGTAG, "LOG: "+consoleMessage);
+            mConsoleMessages.append(msg);
+            Log.v(LOGTAG, "LOG: " + msg);
+            // the rationale here is that if there's an error of either type, and the test was
+            // waiting for "notifyDone" signal to finish, then there's no point in waiting
+            // anymore because the JS execution is already terminated at this point and a
+            // "notifyDone" will never come out so it's just wasting time till timeout kicks in
+            if ((msg.contains("Uncaught ReferenceError:") || msg.contains("Uncaught TypeError:"))
+                    && mWaitUntilDone && mStopOnRefError) {
+                Log.w(LOGTAG, "Terminating test case on uncaught ReferenceError or TypeError.");
+                mHandler.postDelayed(new Runnable() {
+                    public void run() {
+                        notifyDone();
+                    }
+                }, 500);
+            }
+            return true;
         }
 
         @Override
@@ -723,13 +793,15 @@
 
     private static class NewWindowWebView extends WebView {
         public NewWindowWebView(Context context, Map<String, Object> jsIfaces) {
-            super(context, null, 0, jsIfaces);
+            super(context, null, 0, jsIfaces, false);
         }
     }
 
     private void resetTestStatus() {
         mWaitUntilDone = false;
         mDumpDataType = mDefaultDumpDataType;
+        mDumpTopFrameAsText = false;
+        mDumpChildFramesAsText = false;
         mTimedOut = false;
         mDumpTitleChanges = false;
         mRequestedWebKitData = false;
@@ -739,10 +811,12 @@
         mEventSender.clearTouchPoints();
         mEventSender.clearTouchMetaState();
         mPageFinished = false;
-        mOneHundredPercentComplete = false;
         mDumpWebKitData = false;
         mGetDrawtime = false;
         mSaveImagePath = null;
+        setDefaultWebSettings(mWebView);
+        mIsGeolocationPermissionSet = false;
+        mPendingGeolocationPermissionCallbacks = null;
     }
 
     private long[] getDrawWebViewTime(WebView view, int count) {
@@ -779,7 +853,7 @@
     }
 
     private boolean canMoveToNextTest() {
-        return (mDumpWebKitData && mOneHundredPercentComplete && mPageFinished && !mWaitUntilDone) || mTimedOut;
+        return (mDumpWebKitData && mPageFinished && !mWaitUntilDone) || mTimedOut;
     }
 
     private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) {
@@ -787,6 +861,19 @@
             return;
         }
 
+        setDefaultWebSettings(webview);
+
+        webview.setWebChromeClient(mChromeClient);
+        webview.setWebViewClient(mViewClient);
+        // Setting a touch interval of -1 effectively disables the optimisation in WebView
+        // that stops repeated touch events flooding WebCore. The Event Sender only sends a
+        // single event rather than a stream of events (like what would generally happen in
+        // a real use of touch events in a WebView)  and so if the WebView drops the event,
+        // the test will fail as the test expects one callback for every touch it synthesizes.
+        webview.setTouchInterval(-1);
+    }
+
+    public void setDefaultWebSettings(WebView webview) {
         WebSettings settings = webview.getSettings();
         settings.setAppCacheEnabled(true);
         settings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
@@ -799,15 +886,8 @@
         settings.setDatabasePath(getDir("databases",0).getAbsolutePath());
         settings.setDomStorageEnabled(true);
         settings.setWorkersEnabled(false);
-
-        webview.setWebChromeClient(mChromeClient);
-        webview.setWebViewClient(mViewClient);
-        // Setting a touch interval of -1 effectively disables the optimisation in WebView
-        // that stops repeated touch events flooding WebCore. The Event Sender only sends a
-        // single event rather than a stream of events (like what would generally happen in
-        // a real use of touch events in a WebView)  and so if the WebView drops the event,
-        // the test will fail as the test expects one callback for every touch it synthesizes.
-        webview.setTouchInterval(-1);
+        settings.setXSSAuditorEnabled(false);
+        settings.setPageCacheCapacity(0);
     }
 
     private WebView mWebView;
@@ -824,6 +904,9 @@
     private String mSaveImagePath;
     private BufferedReader mTestListReader;
     private boolean mGetDrawtime;
+    private int mTotalTestCount;
+    private int mCurrentTestNumber;
+    private boolean mStopOnRefError;
 
     // States
     private boolean mTimedOut;
@@ -833,6 +916,8 @@
     // Layout test controller variables.
     private DumpDataType mDumpDataType;
     private DumpDataType mDefaultDumpDataType = DumpDataType.EXT_REPR;
+    private boolean mDumpTopFrameAsText;
+    private boolean mDumpChildFramesAsText;
     private boolean mWaitUntilDone;
     private boolean mDumpTitleChanges;
     private StringBuffer mTitleChanges;
@@ -846,7 +931,6 @@
 
     private boolean mPageFinished = false;
     private boolean mDumpWebKitData = false;
-    private boolean mOneHundredPercentComplete = false;
 
     static final String TIMEOUT_STR = "**Test timeout";
 
@@ -861,11 +945,15 @@
     static final String UI_AUTO_TEST = "UiAutoTest";
     static final String GET_DRAW_TIME = "GetDrawTime";
     static final String SAVE_IMAGE = "SaveImage";
+    static final String TOTAL_TEST_COUNT = "TestCount";
+    static final String CURRENT_TEST_NUMBER = "TestNumber";
+    static final String STOP_ON_REF_ERROR = "StopOnReferenceError";
 
     static final int DRAW_RUNS = 5;
     static final String DRAW_TIME_LOG = Environment.getExternalStorageDirectory() +
         "/android/page_draw_time.txt";
 
-    private boolean mGeolocationPermissionSet;
+    private boolean mIsGeolocationPermissionSet;
     private boolean mGeolocationPermission;
+    private Map mPendingGeolocationPermissionCallbacks;
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
index 716086b..383d782 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java
@@ -31,6 +31,7 @@
 	
     WebViewEventSender(WebView webView) {
         mWebView = webView;
+        mWebView.getSettings().setBuiltInZoomControls(true);
         mTouchPoints = new Vector<TouchPoint>();
     }
 	
@@ -170,70 +171,128 @@
 	}
 
     public void touchStart() {
-        // We only support single touch so examine the first touch point only.
-        // If multi touch is enabled in the future, we need to re-examine this to send
-        // all the touch points with the event.
-        TouchPoint tp = mTouchPoints.get(0);
-
-        if (tp == null) {
+        final int numPoints = mTouchPoints.size();
+        if (numPoints == 0) {
             return;
         }
 
-        tp.setDownTime(SystemClock.uptimeMillis());
-        MotionEvent event = MotionEvent.obtain(tp.downTime(), tp.downTime(),
-                MotionEvent.ACTION_DOWN, tp.getX(), tp.getY(), mTouchMetaState);
+        int[] pointerIds = new int[numPoints];
+        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints];
+        long downTime = SystemClock.uptimeMillis();
+
+        for (int i = 0; i < numPoints; ++i) {
+            pointerIds[i] = mTouchPoints.get(i).getId();
+            pointerCoords[i] = new MotionEvent.PointerCoords();
+            pointerCoords[i].x = mTouchPoints.get(i).getX();
+            pointerCoords[i].y = mTouchPoints.get(i).getY();
+            mTouchPoints.get(i).setDownTime(downTime);
+        }
+
+        MotionEvent event = MotionEvent.obtain(downTime, downTime,
+            MotionEvent.ACTION_DOWN, numPoints, pointerIds, pointerCoords,
+            mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0);
+
         mWebView.onTouchEvent(event);
     }
 
     public void touchMove() {
-        TouchPoint tp = mTouchPoints.get(0);
-
-        if (tp == null) {
+        final int numPoints = mTouchPoints.size();
+        if (numPoints == 0) {
             return;
         }
 
-        if (!tp.hasMoved()) {
+        int[] pointerIds = new int[numPoints];
+        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints];
+        int numMovedPoints = 0;
+        for (int i = 0; i < numPoints; ++i) {
+            TouchPoint tp = mTouchPoints.get(i);
+            if (tp.hasMoved()) {
+                pointerIds[numMovedPoints] = mTouchPoints.get(i).getId();
+                pointerCoords[i] = new MotionEvent.PointerCoords();
+                pointerCoords[numMovedPoints].x = mTouchPoints.get(i).getX();
+                pointerCoords[numMovedPoints].y = mTouchPoints.get(i).getY();
+                ++numMovedPoints;
+                tp.setMoved(false);
+            }
+        }
+
+        if (numMovedPoints == 0) {
             return;
         }
 
-        MotionEvent event = MotionEvent.obtain(tp.downTime(), SystemClock.uptimeMillis(),
-                MotionEvent.ACTION_MOVE, tp.getX(), tp.getY(), mTouchMetaState);
+        MotionEvent event = MotionEvent.obtain(mTouchPoints.get(0).downTime(),
+                SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE,
+                numMovedPoints, pointerIds, pointerCoords,
+                mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0);
         mWebView.onTouchEvent(event);
-
-        tp.setMoved(false);
     }
 
     public void touchEnd() {
-        TouchPoint tp = mTouchPoints.get(0);
-
-        if (tp == null) {
+        final int numPoints = mTouchPoints.size();
+        if (numPoints == 0) {
             return;
         }
 
-        MotionEvent event = MotionEvent.obtain(tp.downTime(), SystemClock.uptimeMillis(),
-                MotionEvent.ACTION_UP, tp.getX(), tp.getY(), mTouchMetaState);
+        int[] pointerIds = new int[numPoints];
+        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints];
+
+        for (int i = 0; i < numPoints; ++i) {
+            pointerIds[i] = mTouchPoints.get(i).getId();
+            pointerCoords[i] = new MotionEvent.PointerCoords();
+            pointerCoords[i].x = mTouchPoints.get(i).getX();
+            pointerCoords[i].y = mTouchPoints.get(i).getY();
+        }
+
+        MotionEvent event = MotionEvent.obtain(mTouchPoints.get(0).downTime(),
+                SystemClock.uptimeMillis(), MotionEvent.ACTION_UP,
+                numPoints, pointerIds, pointerCoords,
+                mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0);
         mWebView.onTouchEvent(event);
 
-        if (tp.isReleased()) {
-            mTouchPoints.remove(0);
+        for (int i = numPoints - 1; i >= 0; --i) {  // remove released points.
+            TouchPoint tp = mTouchPoints.get(i);
+            if (tp.isReleased()) {
+              mTouchPoints.remove(i);
+            }
         }
     }
 
     public void touchCancel() {
-        TouchPoint tp = mTouchPoints.get(0);
-        if (tp == null) {
+        final int numPoints = mTouchPoints.size();
+        if (numPoints == 0) {
             return;
         }
 
-        if (tp.cancelled()) {
-            MotionEvent event = MotionEvent.obtain(tp.downTime(), SystemClock.uptimeMillis(),
-                    MotionEvent.ACTION_CANCEL, tp.getX(), tp.getY(), mTouchMetaState);
-            mWebView.onTouchEvent(event);
+        int[] pointerIds = new int[numPoints];
+        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints];
+        long cancelTime = SystemClock.uptimeMillis();
+        int numCanceledPoints = 0;
+
+        for (int i = 0; i < numPoints; ++i) {
+            TouchPoint tp = mTouchPoints.get(i);
+            if (tp.cancelled()) {
+                pointerIds[numCanceledPoints] = mTouchPoints.get(i).getId();
+                pointerCoords[numCanceledPoints] = new MotionEvent.PointerCoords();
+                pointerCoords[numCanceledPoints].x = mTouchPoints.get(i).getX();
+                pointerCoords[numCanceledPoints].y = mTouchPoints.get(i).getY();
+                ++numCanceledPoints;
+            }
         }
+
+        if (numCanceledPoints == 0) {
+            return;
+        }
+
+        MotionEvent event = MotionEvent.obtain(mTouchPoints.get(0).downTime(),
+            SystemClock.uptimeMillis(), MotionEvent.ACTION_CANCEL,
+            numCanceledPoints, pointerIds, pointerCoords,
+            mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0);
+
+        mWebView.onTouchEvent(event);
     }
 
     public void cancelTouchPoint(int id) {
-        TouchPoint tp = mTouchPoints.get(0);
+        TouchPoint tp = mTouchPoints.get(id);
         if (tp == null) {
             return;
         }
@@ -242,14 +301,19 @@
     }
 
     public void addTouchPoint(int x, int y) {
-        mTouchPoints.add(new TouchPoint(contentsToWindowX(x), contentsToWindowY(y)));
-        if (mTouchPoints.size() > 1) {
-            Log.w(LOGTAG, "Adding more than one touch point, but multi touch is not supported!");
+        final int numPoints = mTouchPoints.size();
+        int id;
+        if (numPoints == 0) {
+          id = 0;
+        } else {
+          id = mTouchPoints.get(numPoints - 1).getId() + 1;
         }
+
+        mTouchPoints.add(new TouchPoint(id, contentsToWindowX(x), contentsToWindowY(y)));
     }
 
-    public void updateTouchPoint(int id, int x, int y) {
-        TouchPoint tp = mTouchPoints.get(0);
+    public void updateTouchPoint(int i, int x, int y) {
+        TouchPoint tp = mTouchPoints.get(i);
         if (tp == null) {
             return;
         }
@@ -276,7 +340,7 @@
     }
 
     public void releaseTouchPoint(int id) {
-        TouchPoint tp = mTouchPoints.get(0);
+        TouchPoint tp = mTouchPoints.get(id);
         if (tp == null) {
             return;
         }
@@ -305,6 +369,7 @@
     private int mouseY;
 
     private class TouchPoint {
+        private int mId;
         private int mX;
         private int mY;
         private long mDownTime;
@@ -312,7 +377,8 @@
         private boolean mMoved;
         private boolean mCancelled;
 
-        public TouchPoint(int x, int y) {
+        public TouchPoint(int id, int x, int y) {
+            mId = id;
             mX = x;
             mY = y;
             mReleased = false;
@@ -332,6 +398,7 @@
         public void setMoved(boolean moved) { mMoved = moved; }
         public boolean hasMoved() { return mMoved; }
 
+        public int getId() { return mId; }
         public int getX() { return mX; }
         public int getY() { return mY; }
 
diff --git a/tests/DumpRenderTree2/Android.mk b/tests/DumpRenderTree2/Android.mk
new file mode 100644
index 0000000..81fc633
--- /dev/null
+++ b/tests/DumpRenderTree2/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := diff_match_patch
+
+LOCAL_PACKAGE_NAME := DumpRenderTree2
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/AndroidManifest.xml b/tests/DumpRenderTree2/AndroidManifest.xml
new file mode 100644
index 0000000..ea6571e
--- /dev/null
+++ b/tests/DumpRenderTree2/AndroidManifest.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2010 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.dumprendertree2">
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name=".ui.DirListActivity"
+                  android:label="Dump Render Tree 2"
+                  android:configChanges="orientation">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.TEST" />
+            </intent-filter>
+        </activity>
+
+        <!-- android:launchMode="singleTask" is there so we only have a one instance
+             of this activity. However, it doesn't seem to work exactly like described in the
+             documentation, because the behaviour of the application suggest
+             there is only a single task for all 3 activities. We don't understand
+             how exactly it all works, but at the moment it works just fine.
+             It can lead to some weird behaviour in the future. -->
+        <activity android:name=".TestsListActivity"
+                  android:label="Tests' list activity"
+                  android:launchMode="singleTask"
+                  android:configChanges="orientation">
+        </activity>
+
+        <activity android:name=".LayoutTestsExecutor"
+                  android:theme="@style/WhiteBackground"
+                  android:label="Layout tests' executor"
+                  android:process=":executor">
+        </activity>
+
+        <service android:name="ManagerService">
+        </service>
+    </application>
+
+    <instrumentation android:name="com.android.dumprendertree2.scriptsupport.ScriptTestRunner"
+                     android:targetPackage="com.android.dumprendertree2"
+                     android:label="Layout tests script runner" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WRITE_SDCARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+</manifest>
diff --git a/tests/DumpRenderTree2/assets/run_apache2.py b/tests/DumpRenderTree2/assets/run_apache2.py
new file mode 100755
index 0000000..b4a8685
--- /dev/null
+++ b/tests/DumpRenderTree2/assets/run_apache2.py
@@ -0,0 +1,163 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Start, stop, or restart apache2 server.
+
+  Apache2 must be installed with mod_php!
+
+  Usage:
+    run_apache2.py start|stop|restart
+"""
+
+import sys
+import os
+import subprocess
+import logging
+import optparse
+import time
+
+def main(run_cmd, options):
+  # Setup logging class
+  logging.basicConfig(level=logging.INFO, format='%(message)s')
+
+  if not run_cmd in ("start", "stop", "restart"):
+    logging.info("illegal argument: " + run_cmd)
+    logging.info("Usage: python run_apache2.py start|stop|restart")
+    return False
+
+  # Create /tmp/WebKit if it doesn't exist. This is needed for various files used by apache2
+  tmp_WebKit = os.path.join("/tmp", "WebKit")
+  if not os.path.exists(tmp_WebKit):
+    os.mkdir(tmp_WebKit)
+
+  # Get the path to android tree root based on the script location.
+  # Basically we go 5 levels up
+  parent = os.pardir
+  script_location = os.path.abspath(os.path.dirname(sys.argv[0]))
+  android_tree_root = os.path.join(script_location, parent, parent, parent, parent, parent)
+  android_tree_root = os.path.normpath(android_tree_root)
+
+  # If any of these is relative, then it's relative to ServerRoot (in our case android_tree_root)
+  webkit_path = os.path.join("external", "webkit")
+  if (options.tests_root_directory != None):
+    # if options.tests_root_directory is absolute, os.getcwd() is discarded!
+    layout_tests_path = os.path.normpath(os.path.join(os.getcwd(), options.tests_root_directory))
+  else:
+    layout_tests_path = os.path.join(webkit_path, "LayoutTests")
+  http_conf_path = os.path.join(layout_tests_path, "http", "conf")
+
+  # Prepare the command to set ${APACHE_RUN_USER} and ${APACHE_RUN_GROUP}
+  envvars_path = os.path.join("/etc", "apache2", "envvars")
+  export_envvars_cmd = "source " + envvars_path
+
+  error_log_path = os.path.join(tmp_WebKit, "apache2-error.log")
+  custom_log_path = os.path.join(tmp_WebKit, "apache2-access.log")
+
+  # Prepare the command to (re)start/stop the server with specified settings
+  apache2_restart_template = "apache2 -k %s"
+  directives  = " -c \"ServerRoot " + android_tree_root + "\""
+
+  # The default config in apache2-debian-httpd.conf listens on ports 8080 and
+  # 8443. We also need to listen on port 8000 for HTTP tests.
+  directives += " -c \"Listen 8000\""
+
+  # We use http/tests as the document root as the HTTP tests use hardcoded
+  # resources at the server root. We then use aliases to make available the
+  # complete set of tests and the required scripts.
+  directives += " -c \"DocumentRoot " + os.path.join(layout_tests_path, "http", "tests/") + "\""
+  directives += " -c \"Alias /LayoutTests " + layout_tests_path + "\""
+  directives += " -c \"Alias /WebKitTools/DumpRenderTree/android " + \
+    os.path.join(webkit_path, "WebKitTools", "DumpRenderTree", "android") + "\""
+  directives += " -c \"Alias /ThirdPartyProject.prop " + \
+    os.path.join(webkit_path, "ThirdPartyProject.prop") + "\""
+
+  # This directive is commented out in apache2-debian-httpd.conf for some reason
+  # However, it is useful to browse through tests in the browser, so it's added here.
+  # One thing to note is that because of problems with mod_dir and port numbers, mod_dir
+  # is turned off. That means that there _must_ be a trailing slash at the end of URL
+  # for auto indexes to work correctly.
+  directives += " -c \"LoadModule autoindex_module /usr/lib/apache2/modules/mod_autoindex.so\""
+
+  directives += " -c \"ErrorLog " + error_log_path +"\""
+  directives += " -c \"CustomLog " + custom_log_path + " combined\""
+  directives += " -c \"SSLCertificateFile " + os.path.join(http_conf_path, "webkit-httpd.pem") + \
+    "\""
+  directives += " -c \"User ${APACHE_RUN_USER}\""
+  directives += " -c \"Group ${APACHE_RUN_GROUP}\""
+  directives += " -C \"TypesConfig " + \
+    os.path.join(android_tree_root, http_conf_path, "mime.types") + "\""
+  conf_file_cmd = " -f " + \
+    os.path.join(android_tree_root, http_conf_path, "apache2-debian-httpd.conf")
+
+  # Try to execute the commands
+  logging.info("Will " + run_cmd + " apache2 server.")
+
+  # It is worth noting here that if the configuration file with which we restart the server points
+  # to a different PidFile it will not work and will result in a second apache2 instance.
+  if (run_cmd == 'restart'):
+    logging.info("First will stop...")
+    if execute_cmd(envvars_path, error_log_path,
+                   export_envvars_cmd + " && " + (apache2_restart_template % ('stop')) + directives + conf_file_cmd) == False:
+      logging.info("Failed to stop Apache2")
+      return False
+    logging.info("Stopped. Will start now...")
+    # We need to sleep breifly to avoid errors with apache being stopped and started too quickly
+    time.sleep(0.5)
+
+  if execute_cmd(envvars_path, error_log_path,
+                 export_envvars_cmd + " && " +
+                 (apache2_restart_template % (run_cmd)) + directives +
+                 conf_file_cmd) == False:
+    logging.info("Failed to start Apache2")
+    return False
+
+  logging.info("Successfully started")
+  return True
+
+def execute_cmd(envvars_path, error_log_path, cmd):
+  p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+  (out, err) = p.communicate()
+
+  # Output the stdout from the command to console
+  logging.info(out)
+
+  # Report any errors
+  if p.returncode != 0:
+    logging.info("!! ERRORS:")
+
+    if err.find(envvars_path) != -1:
+      logging.info(err)
+    elif err.find('command not found') != -1:
+      logging.info("apache2 is probably not installed")
+    else:
+      logging.info(err)
+      logging.info("Try looking in " + error_log_path + " for details")
+    return False
+
+  return True
+
+if __name__ == "__main__":
+  option_parser = optparse.OptionParser(usage="Usage: %prog [options] start|stop|restart")
+  option_parser.add_option("", "--tests-root-directory",
+                           help="The directory from which to take the tests, default is external/webkit/LayoutTests in this checkout of the Android tree")
+  options, args = option_parser.parse_args();
+
+  if len(args) < 1:
+    run_cmd = ""
+  else:
+    run_cmd = args[0]
+
+  main(run_cmd, options)
diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py
new file mode 100755
index 0000000..0dfd229
--- /dev/null
+++ b/tests/DumpRenderTree2/assets/run_layout_tests.py
@@ -0,0 +1,92 @@
+#!/usr/bin/python
+
+"""Run layout tests on the device.
+
+  It runs the specified tests on the device, downloads the summaries to the temporary directory
+  and optionally shows the detailed results the host's default browser.
+
+  Usage:
+    run_layout_tests.py --show-results-in-browser test-relative-path
+"""
+
+import logging
+import optparse
+import os
+import re
+import sys
+import subprocess
+import tempfile
+import webbrowser
+
+import run_apache2
+
+#TODO: These should not be hardcoded
+RESULTS_ABSOLUTE_PATH = "/sdcard/layout-test-results/"
+DETAILS_HTML = "details.html"
+SUMMARY_TXT = "summary.txt"
+
+def main(path, options):
+  tmpdir = tempfile.gettempdir()
+
+  # Restart the server
+  if run_apache2.main("restart", options) == False:
+    return
+
+  # Run the tests in path
+  adb_cmd = "adb"
+  if options.serial:
+    adb_cmd += " -s " + options.serial
+  cmd = adb_cmd + " shell am instrument "
+  cmd += "-e class com.android.dumprendertree2.scriptsupport.Starter#startLayoutTests "
+  cmd += "-e path \"" + path + "\" "
+  cmd += "-w com.android.dumprendertree2/com.android.dumprendertree2.scriptsupport.ScriptTestRunner"
+
+  logging.info("Running the tests...")
+  (stdoutdata, stderrdata) = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+  if re.search("^INSTRUMENTATION_STATUS_CODE: -1", stdoutdata, re.MULTILINE) != None:
+    logging.info("Failed to run the tests. Is DumpRenderTree2 installed on the device?")
+    return
+
+  logging.info("Downloading the summaries...")
+
+  # Download the txt summary to tmp folder
+  summary_txt_tmp_path = os.path.join(tmpdir, SUMMARY_TXT)
+  cmd = "rm -f " + summary_txt_tmp_path + ";"
+  cmd += adb_cmd + " pull " + RESULTS_ABSOLUTE_PATH + SUMMARY_TXT + " " + summary_txt_tmp_path
+  subprocess.Popen(cmd, shell=True).wait()
+
+  # Download the html summary to tmp folder
+  details_html_tmp_path = os.path.join(tmpdir, DETAILS_HTML)
+  cmd = "rm -f " + details_html_tmp_path + ";"
+  cmd += adb_cmd + " pull " + RESULTS_ABSOLUTE_PATH + DETAILS_HTML + " " + details_html_tmp_path
+  subprocess.Popen(cmd, shell=True).wait()
+
+  # Print summary to console
+  logging.info("All done.\n")
+  cmd = "cat " + summary_txt_tmp_path
+  os.system(cmd)
+  logging.info("")
+
+  # Open the browser with summary
+  if options.show_results_in_browser != "false":
+    webbrowser.open(details_html_tmp_path)
+
+if __name__ == "__main__":
+  option_parser = optparse.OptionParser(usage="Usage: %prog [options] test-relative-path")
+  option_parser.add_option("", "--show-results-in-browser", default="true",
+                           help="Show the results the host's default web browser, default=true")
+  option_parser.add_option("", "--tests-root-directory",
+                           help="The directory from which to take the tests, default is external/webkit/LayoutTests in this checkout of the Android tree")
+  option_parser.add_option("-s", "--serial", default=None, help="Specify the serial number of device to run test on")
+  options, args = option_parser.parse_args();
+
+  logging.basicConfig(level=logging.INFO, format='%(message)s')
+
+  if len(args) > 1:
+    logging.fatal("Usage: run_layout_tests.py [options] test-relative-path")
+  else:
+    if len(args) < 1:
+      path = "";
+    else:
+      path = args[0]
+    main(path, options);
diff --git a/tests/DumpRenderTree2/res/drawable/folder.png b/tests/DumpRenderTree2/res/drawable/folder.png
new file mode 100644
index 0000000..5b3fcec
--- /dev/null
+++ b/tests/DumpRenderTree2/res/drawable/folder.png
Binary files differ
diff --git a/tests/DumpRenderTree2/res/drawable/runtest.png b/tests/DumpRenderTree2/res/drawable/runtest.png
new file mode 100644
index 0000000..910c654
--- /dev/null
+++ b/tests/DumpRenderTree2/res/drawable/runtest.png
Binary files differ
diff --git a/tests/DumpRenderTree2/res/layout/dirlist_row.xml b/tests/DumpRenderTree2/res/layout/dirlist_row.xml
new file mode 100644
index 0000000..e5578a6
--- /dev/null
+++ b/tests/DumpRenderTree2/res/layout/dirlist_row.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2010 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:gravity="center_vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="80px"
+        android:adjustViewBounds="true"
+        android:paddingLeft="15px"
+        android:paddingRight="15px"
+        android:paddingTop="15px"
+        android:paddingBottom="15px"
+        android:layout_height="wrap_content"
+    />
+
+    <TextView
+        android:id="@+id/label"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="60px"
+        android:gravity="center_vertical"
+        android:textSize="14sp"
+    />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/res/menu/gui_menu.xml b/tests/DumpRenderTree2/res/menu/gui_menu.xml
new file mode 100644
index 0000000..a5b2b65
--- /dev/null
+++ b/tests/DumpRenderTree2/res/menu/gui_menu.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2010 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/run_all"
+          android:title="@string/run_all_tests" />
+</menu>
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/res/values/strings.xml b/tests/DumpRenderTree2/res/values/strings.xml
new file mode 100644
index 0000000..0496404
--- /dev/null
+++ b/tests/DumpRenderTree2/res/values/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2010 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources>
+    <string name="dialog_run_abort_dir_title_prefix">Directory:</string>
+    <string name="dialog_run_abort_dir_msg">This will run all the tests in this directory and all
+            the subdirectories. It may take a few hours!</string>
+    <string name="dialog_run_abort_dir_ok_button">Run tests!</string>
+    <string name="dialog_run_abort_dir_abort_button">Abort</string>
+
+    <string name="dialog_progress_title">Loading items.</string>
+    <string name="dialog_progress_msg">Please wait...</string>
+
+    <string name="runner_preloading_title">Preloading tests...</string>
+
+    <string name="run_all_tests">Run all tests in the current directory</string>
+</resources>
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/res/values/style.xml b/tests/DumpRenderTree2/res/values/style.xml
new file mode 100644
index 0000000..35f3419
--- /dev/null
+++ b/tests/DumpRenderTree2/res/values/style.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2010 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<resources>
+    <style name="WhiteBackground">
+        <item name="android:background">@android:color/white</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java
new file mode 100644
index 0000000..614b03c
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AbstractResult.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.os.Bundle;
+import android.os.Message;
+import android.util.Log;
+import android.webkit.WebView;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+/**
+ * A class that represent a result of the test. It is responsible for returning the result's
+ * raw data and generating its own diff in HTML format.
+ */
+public abstract class AbstractResult implements Comparable<AbstractResult>, Serializable {
+
+    private static final String LOG_TAG = "AbstractResult";
+
+    public enum TestType {
+        TEXT {
+            @Override
+            public AbstractResult createResult(Bundle bundle) {
+                return new TextResult(bundle);
+            }
+        },
+        RENDER_TREE {
+            @Override
+            public AbstractResult createResult(Bundle bundle) {
+                /** TODO: RenderTree tests are not yet supported */
+                return null;
+            }
+        };
+
+        public abstract AbstractResult createResult(Bundle bundle);
+    }
+
+    /**
+     * A code representing the result of comparing actual and expected results.
+     */
+    public enum ResultCode implements Serializable {
+        RESULTS_MATCH("Results match"),
+        RESULTS_DIFFER("Results differ"),
+        NO_EXPECTED_RESULT("No expected result"),
+        NO_ACTUAL_RESULT("No actual result");
+
+        private String mTitle;
+
+        private ResultCode(String title) {
+            mTitle = title;
+        }
+
+        @Override
+        public String toString() {
+            return mTitle;
+        }
+    }
+
+    String mAdditionalTextOutputString;
+
+    public int compareTo(AbstractResult another) {
+        return getRelativePath().compareTo(another.getRelativePath());
+    }
+
+    public void setAdditionalTextOutputString(String additionalTextOutputString) {
+        mAdditionalTextOutputString = additionalTextOutputString;
+    }
+
+    public String getAdditionalTextOutputString() {
+        return mAdditionalTextOutputString;
+    }
+
+    public byte[] getBytes() {
+        ByteArrayOutputStream baos = null;
+        ObjectOutputStream oos = null;
+        try {
+            try {
+                baos = new ByteArrayOutputStream();
+                oos = new ObjectOutputStream(baos);
+                oos.writeObject(this);
+            } finally {
+                if (baos != null) {
+                    baos.close();
+                }
+                if (oos != null) {
+                    oos.close();
+                }
+            }
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Unable to serialize result: " + getRelativePath(), e);
+        }
+
+        return baos == null ? null : baos.toByteArray();
+    }
+
+    public static AbstractResult create(byte[] bytes) {
+        ByteArrayInputStream bais = null;
+        ObjectInputStream ois = null;
+        AbstractResult result = null;
+        try {
+            try {
+                bais = new ByteArrayInputStream(bytes);
+                ois = new ObjectInputStream(bais);
+                result = (AbstractResult)ois.readObject();
+            } finally {
+                if (bais != null) {
+                    bais.close();
+                }
+                if (ois != null) {
+                    ois.close();
+                }
+            }
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Unable to deserialize result!", e);
+        } catch (ClassNotFoundException e) {
+            Log.e(LOG_TAG, "Unable to deserialize result!", e);
+        }
+        return result;
+    }
+
+    public void clearResults() {
+        mAdditionalTextOutputString = null;
+    }
+
+    /**
+     * Makes the result object obtain the results of the test from the webview
+     * and store them in the format that suits itself bests. This method is asynchronous.
+     * The message passed as a parameter is a message that should be sent to its target
+     * when the result finishes obtaining the result.
+     *
+     * @param webview
+     * @param resultObtainedMsg
+     */
+    public abstract void obtainActualResults(WebView webview, Message resultObtainedMsg);
+
+    public abstract void setExpectedImageResult(byte[] expectedResult);
+
+    public abstract void setExpectedImageResultPath(String relativePath);
+
+    public abstract String getExpectedImageResultPath();
+
+    public abstract void setExpectedTextResult(String expectedResult);
+
+    public abstract void setExpectedTextResultPath(String relativePath);
+
+    public abstract String getExpectedTextResultPath();
+
+    /**
+     * Returns result's image data that can be written to the disk. It can be null
+     * if there is an error of some sort or for example the test times out.
+     *
+     * <p> Some tests will not provide data (like text tests)
+     *
+     * @return
+     *      results image data
+     */
+    public abstract byte[] getActualImageResult();
+
+    /**
+     * Returns result's text data. It can be null
+     * if there is an error of some sort or for example the test times out.
+     *
+     * @return
+     *      results text data
+     */
+    public abstract String getActualTextResult();
+
+    /**
+     * Returns the status code representing the result of comparing actual and expected results.
+     *
+     * @return
+     *      the status code from comparing actual and expected results
+     */
+    public abstract ResultCode getResultCode();
+
+    /**
+     * Returns whether this test crashed.
+     *
+     * @return
+     *      whether this test crashed
+     */
+    public abstract boolean didCrash();
+
+    /**
+     * Returns whether this test timed out.
+     *
+     * @return
+     *      whether this test timed out
+     */
+    public abstract boolean didTimeOut();
+
+    /**
+     * Sets that this test timed out.
+     */
+    public abstract void setDidTimeOut();
+
+    /**
+     * Returns whether the test passed.
+     *
+     * @return
+     *      whether the test passed
+     */
+    public boolean didPass() {
+        // Tests that crash can't have timed out or have an actual result.
+        assert !(didCrash() && didTimeOut());
+        assert !(didCrash() && getResultCode() != ResultCode.NO_ACTUAL_RESULT);
+        return !didCrash() && !didTimeOut() && getResultCode() == ResultCode.RESULTS_MATCH;
+    }
+
+    /**
+     * Return the type of the result data.
+     *
+     * @return
+     *      the type of the result data.
+     */
+    public abstract TestType getType();
+
+    public abstract String getRelativePath();
+
+    /**
+     * Returns a piece of HTML code that presents a visual diff between a result and
+     * the expected result.
+     *
+     * @return
+     *      a piece of HTML code with a visual diff between the result and the expected result
+     */
+    public abstract String getDiffAsHtml();
+
+    public abstract Bundle getBundle();
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java
new file mode 100644
index 0000000..bb9a916
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/AdditionalTextOutput.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.util.Log;
+import android.webkit.ConsoleMessage;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * A class that stores consoles messages, database callbacks, alert messages, etc.
+ */
+public class AdditionalTextOutput {
+    private static final String LOG_TAG = "AdditionalTextOutput";
+
+    /**
+     * Ordering of enums is important as it determines ordering of the toString method!
+     * StringBuilders will be printed in the order the corresponding types appear here.
+     */
+    private enum OutputType {
+        JS_DIALOG,
+        EXCEEDED_DB_QUOTA_MESSAGE,
+        CONSOLE_MESSAGE;
+    }
+
+    StringBuilder[] mOutputs = new StringBuilder[OutputType.values().length];
+
+    private StringBuilder getStringBuilderForType(OutputType outputType) {
+        int index = outputType.ordinal();
+        if (mOutputs[index] == null) {
+            mOutputs[index] = new StringBuilder();
+        }
+        return mOutputs[index];
+    }
+
+    public void appendExceededDbQuotaMessage(String urlString, String databaseIdentifier) {
+        StringBuilder output = getStringBuilderForType(OutputType.EXCEEDED_DB_QUOTA_MESSAGE);
+
+        String protocol = "";
+        String host = "";
+        int port = 0;
+
+        try {
+            URL url = new URL(urlString);
+            protocol = url.getProtocol();
+            host = url.getHost();
+            if (url.getPort() > -1) {
+                port = url.getPort();
+            }
+        } catch (MalformedURLException e) {
+            Log.e(LOG_TAG, "urlString=" + urlString + " databaseIdentifier=" + databaseIdentifier,
+                    e);
+        }
+
+        output.append("UI DELEGATE DATABASE CALLBACK: ");
+        output.append("exceededDatabaseQuotaForSecurityOrigin:{");
+        output.append(protocol + ", " + host + ", " + port + "} ");
+        output.append("database:" + databaseIdentifier + "\n");
+    }
+
+    public void appendConsoleMessage(ConsoleMessage consoleMessage) {
+        StringBuilder output = getStringBuilderForType(OutputType.CONSOLE_MESSAGE);
+
+        output.append("CONSOLE MESSAGE: line " + consoleMessage.lineNumber());
+        output.append(": " + consoleMessage.message() + "\n");
+    }
+
+    public void appendJsAlert(String message) {
+        StringBuilder output = getStringBuilderForType(OutputType.JS_DIALOG);
+
+        output.append("ALERT: ");
+        output.append(message);
+        output.append('\n');
+    }
+
+    public void appendJsConfirm(String message) {
+        StringBuilder output = getStringBuilderForType(OutputType.JS_DIALOG);
+
+        output.append("CONFIRM: ");
+        output.append(message);
+        output.append('\n');
+    }
+
+    public void appendJsPrompt(String message, String defaultValue) {
+        StringBuilder output = getStringBuilderForType(OutputType.JS_DIALOG);
+
+        output.append("PROMPT: ");
+        output.append(message);
+        output.append(", default text: ");
+        output.append(defaultValue);
+        output.append('\n');
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder result = new StringBuilder();
+        for (int i = 0; i < mOutputs.length; i++) {
+            if (mOutputs[i] != null) {
+                result.append(mOutputs[i].toString());
+            }
+        }
+        return result.toString();
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java
new file mode 100644
index 0000000..4831168
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.os.Bundle;
+import android.os.Message;
+import android.webkit.WebView;
+
+/**
+ * A dummy class representing test that crashed.
+ *
+ * TODO: All the methods regarding expected results need implementing.
+ */
+public class CrashedDummyResult extends AbstractResult {
+    String mRelativePath;
+
+    public CrashedDummyResult(String relativePath) {
+        mRelativePath = relativePath;
+    }
+
+    @Override
+    public byte[] getActualImageResult() {
+        return null;
+    }
+
+    @Override
+    public String getActualTextResult() {
+        return null;
+    }
+
+    @Override
+    public Bundle getBundle() {
+        /** TODO:  */
+        return null;
+    }
+
+    @Override
+    public String getDiffAsHtml() {
+        /** TODO: Probably show at least expected results */
+        return "Ooops, I crashed...";
+    }
+
+    @Override
+    public String getRelativePath() {
+        return mRelativePath;
+    }
+
+    @Override
+    public ResultCode getResultCode() {
+        return ResultCode.NO_ACTUAL_RESULT;
+    }
+
+    @Override
+    public boolean didCrash() {
+        return true;
+    }
+
+    @Override
+    public boolean didTimeOut() {
+        return false;
+    }
+
+    @Override
+    public void setDidTimeOut() {
+        /** This method is not applicable for this type of result */
+        assert false;
+    }
+
+    @Override
+    public TestType getType() {
+        return null;
+    }
+
+    @Override
+    public void obtainActualResults(WebView webview, Message resultObtainedMsg) {
+        /** This method is not applicable for this type of result */
+        assert false;
+    }
+
+    @Override
+    public void setExpectedImageResult(byte[] expectedResult) {
+        /** TODO */
+    }
+
+    @Override
+    public void setExpectedTextResult(String expectedResult) {
+        /** TODO */
+    }
+
+    @Override
+    public String getExpectedImageResultPath() {
+        /** TODO */
+        return null;
+    }
+
+    @Override
+    public String getExpectedTextResultPath() {
+        /** TODO */
+        return null;
+    }
+
+    @Override
+    public void setExpectedImageResultPath(String relativePath) {
+        /** TODO */
+    }
+
+    @Override
+    public void setExpectedTextResultPath(String relativePath) {
+        /** TODO */
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java
new file mode 100644
index 0000000..5b7cbc4
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.webkit.WebView;
+
+/**
+ * A class that acts as a JS interface for webview to mock various touch events,
+ * mouse actions and key presses.
+ *
+ * The methods here just call corresponding methods on EventSenderImpl
+ * that contains the logic of how to execute the methods.
+ */
+public class EventSender {
+    EventSenderImpl mEventSenderImpl = new EventSenderImpl();
+
+    public void reset(WebView webView) {
+        mEventSenderImpl.reset(webView);
+    }
+
+    public void enableDOMUIEventLogging(int domNode) {
+        mEventSenderImpl.enableDOMUIEventLogging(domNode);
+    }
+
+    public void fireKeyboardEventsToElement(int domNode) {
+        mEventSenderImpl.fireKeyboardEventsToElement(domNode);
+    }
+
+    public void keyDown(String character, String[] withModifiers) {
+        mEventSenderImpl.keyDown(character, withModifiers);
+    }
+
+    public void keyDown(String character) {
+        keyDown(character, null);
+    }
+
+    public void leapForward(int milliseconds) {
+        mEventSenderImpl.leapForward(milliseconds);
+    }
+
+    public void mouseClick() {
+        mEventSenderImpl.mouseClick();
+    }
+
+    public void mouseDown() {
+        mEventSenderImpl.mouseDown();
+    }
+
+    public void mouseMoveTo(int x, int y) {
+        mEventSenderImpl.mouseMoveTo(x, y);
+    }
+
+    public void mouseUp() {
+        mEventSenderImpl.mouseUp();
+    }
+
+    public void touchStart() {
+        mEventSenderImpl.touchStart();
+    }
+
+    public void addTouchPoint(int x, int y) {
+        mEventSenderImpl.addTouchPoint(x, y);
+    }
+
+    public void updateTouchPoint(int id, int x, int y) {
+        mEventSenderImpl.updateTouchPoint(id, x, y);
+    }
+
+    public void setTouchModifier(String modifier, boolean enabled) {
+        mEventSenderImpl.setTouchModifier(modifier, enabled);
+    }
+
+    public void touchMove() {
+        mEventSenderImpl.touchMove();
+    }
+
+    public void releaseTouchPoint(int id) {
+        mEventSenderImpl.releaseTouchPoint(id);
+    }
+
+    public void touchEnd() {
+        mEventSenderImpl.touchEnd();
+    }
+
+    public void touchCancel() {
+        mEventSenderImpl.touchCancel();
+    }
+
+    public void clearTouchPoints() {
+        mEventSenderImpl.clearTouchPoints();
+    }
+
+    public void cancelTouchPoint(int id) {
+        mEventSenderImpl.cancelTouchPoint(id);
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
new file mode 100644
index 0000000..68bcf11
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.webkit.WebView;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * An implementation of EventSender
+ */
+public class EventSenderImpl {
+    private static final String LOG_TAG = "EventSenderImpl";
+
+    private static final int MSG_ENABLE_DOM_UI_EVENT_LOGGING = 0;
+    private static final int MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT = 1;
+    private static final int MSG_LEAP_FORWARD = 2;
+
+    private static final int MSG_KEY_DOWN = 3;
+
+    private static final int MSG_MOUSE_DOWN = 4;
+    private static final int MSG_MOUSE_UP = 5;
+    private static final int MSG_MOUSE_CLICK = 6;
+    private static final int MSG_MOUSE_MOVE_TO = 7;
+
+    private static final int MSG_ADD_TOUCH_POINT = 8;
+    private static final int MSG_TOUCH_START = 9;
+    private static final int MSG_UPDATE_TOUCH_POINT = 10;
+    private static final int MSG_TOUCH_MOVE = 11;
+    private static final int MSG_CLEAR_TOUCH_POINTS = 12;
+    private static final int MSG_TOUCH_CANCEL = 13;
+    private static final int MSG_RELEASE_TOUCH_POINT = 14;
+    private static final int MSG_TOUCH_END = 15;
+    private static final int MSG_SET_TOUCH_MODIFIER = 16;
+    private static final int MSG_CANCEL_TOUCH_POINT = 17;
+
+    public static class TouchPoint {
+        WebView mWebView;
+        private int mId;
+        private int mX;
+        private int mY;
+        private long mDownTime;
+        private boolean mReleased = false;
+        private boolean mMoved = false;
+        private boolean mCancelled = false;
+
+        public TouchPoint(WebView webView, int id, int x, int y) {
+            mWebView = webView;
+            mId = id;
+            mX = scaleX(x);
+            mY = scaleY(y);
+        }
+
+        public int getId() {
+          return mId;
+        }
+
+        public int getX() {
+            return mX;
+        }
+
+        public int getY() {
+            return mY;
+        }
+
+        public boolean hasMoved() {
+            return mMoved;
+        }
+
+        public void move(int newX, int newY) {
+            mX = scaleX(newX);
+            mY = scaleY(newY);
+            mMoved = true;
+        }
+
+        public void resetHasMoved() {
+            mMoved = false;
+        }
+
+        public long getDownTime() {
+            return mDownTime;
+        }
+
+        public void setDownTime(long downTime) {
+            mDownTime = downTime;
+        }
+
+        public boolean isReleased() {
+            return mReleased;
+        }
+
+        public void release() {
+            mReleased = true;
+        }
+
+        public boolean isCancelled() {
+            return mCancelled;
+        }
+
+        public void cancel() {
+            mCancelled = true;
+        }
+
+        private int scaleX(int x) {
+            return (int)(x * mWebView.getScale()) - mWebView.getScrollX();
+        }
+
+        private int scaleY(int y) {
+            return (int)(y * mWebView.getScale()) - mWebView.getScrollY();
+        }
+    }
+
+    private List<TouchPoint> mTouchPoints;
+    private int mTouchMetaState;
+    private int mMouseX;
+    private int mMouseY;
+
+    private WebView mWebView;
+
+    private Handler mEventSenderHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            Bundle bundle;
+            MotionEvent event;
+            long ts;
+
+            switch (msg.what) {
+                case MSG_ENABLE_DOM_UI_EVENT_LOGGING:
+                    /** TODO: implement */
+                    break;
+
+                case MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT:
+                    /** TODO: implement */
+                    break;
+
+                case MSG_LEAP_FORWARD:
+                    /** TODO: implement */
+                    break;
+
+                case MSG_KEY_DOWN:
+                    bundle = (Bundle)msg.obj;
+                    String character = bundle.getString("character");
+                    String[] withModifiers = bundle.getStringArray("withModifiers");
+
+                    if (withModifiers != null && withModifiers.length > 0) {
+                        for (int i = 0; i < withModifiers.length; i++) {
+                            executeKeyEvent(KeyEvent.ACTION_DOWN,
+                                    modifierToKeyCode(withModifiers[i]));
+                        }
+                    }
+                    executeKeyEvent(KeyEvent.ACTION_DOWN,
+                            charToKeyCode(character.toLowerCase().toCharArray()[0]));
+                    break;
+
+                /** MOUSE */
+
+                case MSG_MOUSE_DOWN:
+                    ts = SystemClock.uptimeMillis();
+                    event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_DOWN, mMouseX, mMouseY, 0);
+                    mWebView.onTouchEvent(event);
+                    break;
+
+                case MSG_MOUSE_UP:
+                    ts = SystemClock.uptimeMillis();
+                    event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_UP, mMouseX, mMouseY, 0);
+                    mWebView.onTouchEvent(event);
+                    break;
+
+                case MSG_MOUSE_CLICK:
+                    mouseDown();
+                    mouseUp();
+                    break;
+
+                case MSG_MOUSE_MOVE_TO:
+                    mMouseX = msg.arg1;
+                    mMouseY = msg.arg2;
+                    break;
+
+                /** TOUCH */
+
+                case MSG_ADD_TOUCH_POINT:
+                    int numPoints = getTouchPoints().size();
+                    int id;
+                    if (numPoints == 0) {
+                        id = 0;
+                    } else {
+                        id = getTouchPoints().get(numPoints - 1).getId() + 1;
+                    }
+                    getTouchPoints().add(new TouchPoint(mWebView, id,
+                            msg.arg1, msg.arg2));
+                    break;
+
+                case MSG_TOUCH_START:
+                    if (getTouchPoints().isEmpty()) {
+                        return;
+                    }
+                    for (int i = 0; i < getTouchPoints().size(); ++i) {
+                        getTouchPoints().get(i).setDownTime(SystemClock.uptimeMillis());
+                    }
+                    executeTouchEvent(MotionEvent.ACTION_DOWN);
+                    break;
+
+                case MSG_UPDATE_TOUCH_POINT:
+                    bundle = (Bundle)msg.obj;
+
+                    int index = bundle.getInt("id");
+                    if (index >= getTouchPoints().size()) {
+                        Log.w(LOG_TAG + "::MSG_UPDATE_TOUCH_POINT", "TouchPoint out of bounds: "
+                                + index);
+                        break;
+                    }
+
+                    getTouchPoints().get(index).move(bundle.getInt("x"), bundle.getInt("y"));
+                    break;
+
+                case MSG_TOUCH_MOVE:
+                    /**
+                     * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+                     * the first touch point. In future this method will need rewriting.
+                     */
+                    if (getTouchPoints().isEmpty()) {
+                        return;
+                    }
+                    executeTouchEvent(MotionEvent.ACTION_MOVE);
+                    for (int i = 0; i < getTouchPoints().size(); ++i) {
+                        getTouchPoints().get(i).resetHasMoved();
+                    }
+                    break;
+
+                case MSG_CANCEL_TOUCH_POINT:
+                    if (msg.arg1 >= getTouchPoints().size()) {
+                        Log.w(LOG_TAG + "::MSG_RELEASE_TOUCH_POINT", "TouchPoint out of bounds: "
+                                + msg.arg1);
+                        break;
+                    }
+
+                    getTouchPoints().get(msg.arg1).cancel();
+                    break;
+
+                case MSG_TOUCH_CANCEL:
+                    /**
+                     * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+                     * the first touch point. In future this method will need rewriting.
+                     */
+                    if (getTouchPoints().isEmpty()) {
+                        return;
+                    }
+                    executeTouchEvent(MotionEvent.ACTION_CANCEL);
+                    break;
+
+                case MSG_RELEASE_TOUCH_POINT:
+                    if (msg.arg1 >= getTouchPoints().size()) {
+                        Log.w(LOG_TAG + "::MSG_RELEASE_TOUCH_POINT", "TouchPoint out of bounds: "
+                                + msg.arg1);
+                        break;
+                    }
+
+                    getTouchPoints().get(msg.arg1).release();
+                    break;
+
+                case MSG_TOUCH_END:
+                    /**
+                     * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+                     * the first touch point. In future this method will need rewriting.
+                     */
+                    if (getTouchPoints().isEmpty()) {
+                        return;
+                    }
+                    executeTouchEvent(MotionEvent.ACTION_UP);
+                    // remove released points.
+                    for (int i = getTouchPoints().size() - 1; i >= 0; --i) {
+                        if (getTouchPoints().get(i).isReleased()) {
+                            getTouchPoints().remove(i);
+                        }
+                    }
+                    break;
+
+                case MSG_SET_TOUCH_MODIFIER:
+                    bundle = (Bundle)msg.obj;
+                    String modifier = bundle.getString("modifier");
+                    boolean enabled = bundle.getBoolean("enabled");
+
+                    int mask = 0;
+                    if ("alt".equals(modifier.toLowerCase())) {
+                        mask = KeyEvent.META_ALT_ON;
+                    } else if ("shift".equals(modifier.toLowerCase())) {
+                        mask = KeyEvent.META_SHIFT_ON;
+                    } else if ("ctrl".equals(modifier.toLowerCase())) {
+                        mask = KeyEvent.META_SYM_ON;
+                    }
+
+                    if (enabled) {
+                        mTouchMetaState |= mask;
+                    } else {
+                        mTouchMetaState &= ~mask;
+                    }
+
+                    break;
+
+                case MSG_CLEAR_TOUCH_POINTS:
+                    getTouchPoints().clear();
+                    break;
+
+                default:
+                    break;
+            }
+        }
+    };
+
+    public void reset(WebView webView) {
+        mWebView = webView;
+        mTouchPoints = null;
+        mTouchMetaState = 0;
+        mMouseX = 0;
+        mMouseY = 0;
+    }
+
+    public void enableDOMUIEventLogging(int domNode) {
+        Message msg = mEventSenderHandler.obtainMessage(MSG_ENABLE_DOM_UI_EVENT_LOGGING);
+        msg.arg1 = domNode;
+        msg.sendToTarget();
+    }
+
+    public void fireKeyboardEventsToElement(int domNode) {
+        Message msg = mEventSenderHandler.obtainMessage(MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT);
+        msg.arg1 = domNode;
+        msg.sendToTarget();
+    }
+
+    public void leapForward(int milliseconds) {
+        Message msg = mEventSenderHandler.obtainMessage(MSG_LEAP_FORWARD);
+        msg.arg1 = milliseconds;
+        msg.sendToTarget();
+    }
+
+    public void keyDown(String character, String[] withModifiers) {
+        Bundle bundle = new Bundle();
+        bundle.putString("character", character);
+        bundle.putStringArray("withModifiers", withModifiers);
+        mEventSenderHandler.obtainMessage(MSG_KEY_DOWN, bundle).sendToTarget();
+    }
+
+    /** MOUSE */
+
+    public void mouseDown() {
+        mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_DOWN);
+    }
+
+    public void mouseUp() {
+        mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_UP);
+    }
+
+    public void mouseClick() {
+        mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_CLICK);
+    }
+
+    public void mouseMoveTo(int x, int y) {
+        mEventSenderHandler.obtainMessage(MSG_MOUSE_MOVE_TO, x, y).sendToTarget();
+    }
+
+    /** TOUCH */
+
+    public void addTouchPoint(int x, int y) {
+        mEventSenderHandler.obtainMessage(MSG_ADD_TOUCH_POINT, x, y).sendToTarget();
+    }
+
+    public void touchStart() {
+        mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_START);
+    }
+
+    public void updateTouchPoint(int id, int x, int y) {
+        Bundle bundle = new Bundle();
+        bundle.putInt("id", id);
+        bundle.putInt("x", x);
+        bundle.putInt("y", y);
+        mEventSenderHandler.obtainMessage(MSG_UPDATE_TOUCH_POINT, bundle).sendToTarget();
+    }
+
+    public void touchMove() {
+        mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_MOVE);
+    }
+
+    public void cancelTouchPoint(int id) {
+        Message msg = mEventSenderHandler.obtainMessage(MSG_CANCEL_TOUCH_POINT);
+        msg.arg1 = id;
+        msg.sendToTarget();
+    }
+
+    public void touchCancel() {
+        mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_CANCEL);
+    }
+
+    public void releaseTouchPoint(int id) {
+        Message msg = mEventSenderHandler.obtainMessage(MSG_RELEASE_TOUCH_POINT);
+        msg.arg1 = id;
+        msg.sendToTarget();
+    }
+
+    public void touchEnd() {
+        mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_END);
+    }
+
+    public void setTouchModifier(String modifier, boolean enabled) {
+        Bundle bundle = new Bundle();
+        bundle.putString("modifier", modifier);
+        bundle.putBoolean("enabled", enabled);
+        mEventSenderHandler.obtainMessage(MSG_SET_TOUCH_MODIFIER, bundle).sendToTarget();
+    }
+
+    public void clearTouchPoints() {
+        mEventSenderHandler.sendEmptyMessage(MSG_CLEAR_TOUCH_POINTS);
+    }
+
+    private List<TouchPoint> getTouchPoints() {
+        if (mTouchPoints == null) {
+            mTouchPoints = new LinkedList<TouchPoint>();
+        }
+
+        return mTouchPoints;
+    }
+
+    private void executeTouchEvent(int action) {
+        int numPoints = getTouchPoints().size();
+        int[] pointerIds = new int[numPoints];
+        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[numPoints];
+
+        for (int i = 0; i < numPoints; ++i) {
+            boolean isNeeded = false;
+            switch(action) {
+            case MotionEvent.ACTION_DOWN:
+            case MotionEvent.ACTION_UP:
+                isNeeded = true;
+                break;
+            case MotionEvent.ACTION_MOVE:
+                isNeeded = getTouchPoints().get(i).hasMoved();
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                isNeeded = getTouchPoints().get(i).isCancelled();
+                break;
+            default:
+                Log.w(LOG_TAG + "::executeTouchEvent(),", "action not supported:" + action);
+                break;
+            }
+
+            numPoints = 0;
+            if (isNeeded) {
+                pointerIds[numPoints] = getTouchPoints().get(i).getId();
+                pointerCoords[numPoints] = new MotionEvent.PointerCoords();
+                pointerCoords[numPoints].x = getTouchPoints().get(i).getX();
+                pointerCoords[numPoints].y = getTouchPoints().get(i).getY();
+                ++numPoints;
+            }
+        }
+
+        if (numPoints == 0) {
+            return;
+        }
+
+        MotionEvent event = MotionEvent.obtain(mTouchPoints.get(0).getDownTime(),
+                SystemClock.uptimeMillis(), action,
+                numPoints, pointerIds, pointerCoords,
+                mTouchMetaState, 1.0f, 1.0f, 0, 0, 0, 0);
+
+        mWebView.onTouchEvent(event);
+    }
+
+    private void executeKeyEvent(int action, int keyCode) {
+        KeyEvent event = new KeyEvent(action, keyCode);
+        mWebView.onKeyDown(event.getKeyCode(), event);
+    }
+
+    /**
+     * Assumes lowercase chars, case needs to be handled by calling function.
+     */
+    private static int charToKeyCode(char c) {
+        // handle numbers
+        if (c >= '0' && c <= '9') {
+            int offset = c - '0';
+            return KeyEvent.KEYCODE_0 + offset;
+        }
+
+        // handle characters
+        if (c >= 'a' && c <= 'z') {
+            int offset = c - 'a';
+            return KeyEvent.KEYCODE_A + offset;
+        }
+
+        // handle all others
+        switch (c) {
+            case '*':
+                return KeyEvent.KEYCODE_STAR;
+
+            case '#':
+                return KeyEvent.KEYCODE_POUND;
+
+            case ',':
+                return KeyEvent.KEYCODE_COMMA;
+
+            case '.':
+                return KeyEvent.KEYCODE_PERIOD;
+
+            case '\t':
+                return KeyEvent.KEYCODE_TAB;
+
+            case ' ':
+                return KeyEvent.KEYCODE_SPACE;
+
+            case '\n':
+                return KeyEvent.KEYCODE_ENTER;
+
+            case '\b':
+            case 0x7F:
+                return KeyEvent.KEYCODE_DEL;
+
+            case '~':
+                return KeyEvent.KEYCODE_GRAVE;
+
+            case '-':
+                return KeyEvent.KEYCODE_MINUS;
+
+            case '=':
+                return KeyEvent.KEYCODE_EQUALS;
+
+            case '(':
+                return KeyEvent.KEYCODE_LEFT_BRACKET;
+
+            case ')':
+                return KeyEvent.KEYCODE_RIGHT_BRACKET;
+
+            case '\\':
+                return KeyEvent.KEYCODE_BACKSLASH;
+
+            case ';':
+                return KeyEvent.KEYCODE_SEMICOLON;
+
+            case '\'':
+                return KeyEvent.KEYCODE_APOSTROPHE;
+
+            case '/':
+                return KeyEvent.KEYCODE_SLASH;
+
+            default:
+                return c;
+        }
+    }
+
+    private static int modifierToKeyCode(String modifier) {
+        if (modifier.equals("ctrlKey")) {
+            return KeyEvent.KEYCODE_ALT_LEFT;
+        } else if (modifier.equals("shiftKey")) {
+            return KeyEvent.KEYCODE_SHIFT_LEFT;
+        } else if (modifier.equals("altKey")) {
+            return KeyEvent.KEYCODE_SYM;
+        }
+
+        return KeyEvent.KEYCODE_UNKNOWN;
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
new file mode 100644
index 0000000..9bbf64a
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FileFilter.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.util.Log;
+
+import com.android.dumprendertree2.forwarder.ForwarderManager;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A utility to filter out some files/directories from the views and tests that run.
+ */
+public class FileFilter {
+    private static final String LOG_TAG = "FileFilter";
+
+    private static final String TEST_EXPECTATIONS_TXT_PATH =
+            "platform/android/test_expectations.txt";
+
+    private static final String HTTP_TESTS_PATH = "http/tests/";
+    private static final String SSL_PATH = "ssl/";
+
+    private static final String TOKEN_CRASH = "CRASH";
+    private static final String TOKEN_FAIL = "FAIL";
+    private static final String TOKEN_SLOW = "SLOW";
+
+    private final Set<String> mCrashList = new HashSet<String>();
+    private final Set<String> mFailList = new HashSet<String>();
+    private final Set<String> mSlowList = new HashSet<String>();
+
+    public FileFilter() {
+        loadTestExpectations();
+    }
+
+    private static final String trimTrailingSlashIfPresent(String path) {
+        File file = new File(path);
+        return file.getPath();
+    }
+
+    public void loadTestExpectations() {
+        URL url = null;
+        try {
+            url = new URL(ForwarderManager.getHostSchemePort(false) + "LayoutTests/" +
+                    TEST_EXPECTATIONS_TXT_PATH);
+        } catch (MalformedURLException e) {
+            assert false;
+        }
+
+        try {
+            InputStream inputStream = null;
+            BufferedReader bufferedReader = null;
+            try {
+                bufferedReader = new BufferedReader(new StringReader(new String(
+                        FsUtils.readDataFromUrl(url))));
+
+                String line;
+                String entry;
+                String[] parts;
+                String path;
+                Set<String> tokens;
+                while (true) {
+                    line = bufferedReader.readLine();
+                    if (line == null) {
+                        break;
+                    }
+
+                    /** Remove the comment and trim */
+                    entry = line.split("//", 2)[0].trim();
+
+                    /** Omit empty lines, advance to next line */
+                    if (entry.isEmpty()) {
+                        continue;
+                    }
+
+                    /** Split on whitespace into path part and the rest */
+                    parts = entry.split("\\s", 2);
+
+                    /** At this point parts.length >= 1 */
+                    if (parts.length == 1) {
+                        Log.w(LOG_TAG + "::reloadConfiguration",
+                                "There are no options specified for the test!");
+                        continue;
+                    }
+
+                    path = trimTrailingSlashIfPresent(parts[0]);
+
+                    /** Split on whitespace */
+                    tokens = new HashSet<String>(Arrays.asList(parts[1].split("\\s", 0)));
+
+                    /** Chose the right collections to add to */
+                    if (tokens.contains(TOKEN_CRASH)) {
+                        mCrashList.add(path);
+
+                        /** If test is on skip list we ignore any further options */
+                        continue;
+                    }
+
+                    if (tokens.contains(TOKEN_FAIL)) {
+                        mFailList.add(path);
+                    }
+                    if (tokens.contains(TOKEN_SLOW)) {
+                        mSlowList.add(path);
+                    }
+                }
+            } finally {
+                if (inputStream != null) {
+                    inputStream.close();
+                }
+                if (bufferedReader != null) {
+                    bufferedReader.close();
+                }
+            }
+        } catch (FileNotFoundException e) {
+            Log.w(LOG_TAG, "reloadConfiguration(): File not found: " + e.getMessage());
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "url=" + url, e);
+        }
+    }
+
+    /**
+     * Checks if test is expected to crash.
+     *
+     * <p>
+     * Path given should relative within LayoutTests folder, e.g. fast/dom/foo.html
+     *
+     * @param testPath
+     *            - a relative path within LayoutTests folder
+     * @return if the test is supposed to be skipped
+     */
+    public boolean isCrash(String testPath) {
+        for (String prefix : getPrefixes(testPath)) {
+            if (mCrashList.contains(prefix)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks if test result is supposed to be "failed".
+     *
+     * <p>
+     * Path given should relative within LayoutTests folder, e.g. fast/dom/foo.html
+     *
+     * @param testPath
+     *            - a relative path within LayoutTests folder
+     * @return if the test result is supposed to be "failed"
+     */
+    public boolean isFail(String testPath) {
+        for (String prefix : getPrefixes(testPath)) {
+            if (mFailList.contains(prefix)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Checks if test is slow and should have timeout increased.
+     *
+     * <p>
+     * Path given should relative within LayoutTests folder, e.g. fast/dom/foo.html
+     *
+     * @param testPath
+     *            - a relative path within LayoutTests folder
+     * @return if the test is slow and should have timeout increased.
+     */
+    public boolean isSlow(String testPath) {
+        for (String prefix : getPrefixes(testPath)) {
+            if (mSlowList.contains(prefix)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the list of all path prefixes of the given path.
+     *
+     * <p>
+     * e.g. this/is/a/path returns the list: this this/is this/is/a this/is/a/path
+     *
+     * @param path
+     * @return the list of all path prefixes of the given path.
+     */
+    private static List<String> getPrefixes(String path) {
+        File file = new File(path);
+        List<String> prefixes = new ArrayList<String>(8);
+
+        do {
+            prefixes.add(file.getPath());
+            file = file.getParentFile();
+        } while (file != null);
+
+        return prefixes;
+    }
+
+    /**
+     * Checks if the directory may contain tests or contains just helper files.
+     *
+     * @param dirName
+     * @return
+     *      if the directory may contain tests
+     */
+    public static boolean isTestDir(String dirName) {
+        return (!dirName.equals("script-tests")
+                && !dirName.equals("resources") && !dirName.startsWith("."));
+    }
+
+    /**
+     * Checks if the file is a test.
+     * Currently we run .html and .xhtml tests.
+     *
+     * @param testName
+     * @return
+     *      if the file is a test
+     */
+    public static boolean isTestFile(String testName) {
+        return testName.endsWith(".html") || testName.endsWith(".xhtml");
+    }
+
+    /**
+     * Return a URL of the test on the server.
+     *
+     * @param relativePath
+     * @return a URL of the test on the server
+     */
+    public static URL getUrl(String relativePath) {
+        String urlBase = ForwarderManager.getHostSchemePort(false);
+
+        /**
+         * URL is formed differently for HTTP vs non-HTTP tests, because HTTP tests
+         * expect different document root. See run_apache2.py and .conf file for details
+         */
+        if (relativePath.startsWith(HTTP_TESTS_PATH)) {
+            relativePath = relativePath.substring(HTTP_TESTS_PATH.length());
+            if (relativePath.startsWith(SSL_PATH)) {
+                urlBase = ForwarderManager.getHostSchemePort(true);
+            }
+        } else {
+            relativePath = "LayoutTests/" + relativePath;
+        }
+
+        try {
+            return new URL(urlBase + relativePath);
+        } catch (MalformedURLException e) {
+            Log.e(LOG_TAG, "Malformed URL!", e);
+        }
+
+        return null;
+    }
+
+    /**
+     * If the path contains extension (e.g .foo at the end of the file) then it changes
+     * this (.foo) into newEnding (so it has to contain the dot if we want to preserve it).
+     *
+     * <p>If the path doesn't contain an extension, it adds the ending to the path.
+     *
+     * @param relativePath
+     * @param newEnding
+     * @return
+     *      a new path, containing the newExtension
+     */
+    public static String setPathEnding(String relativePath, String newEnding) {
+        int dotPos = relativePath.lastIndexOf('.');
+        if (dotPos == -1) {
+            return relativePath + newEnding;
+        }
+
+        return relativePath.substring(0, dotPos) + newEnding;
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
new file mode 100644
index 0000000..4f9a737
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.util.Log;
+
+import com.android.dumprendertree2.forwarder.ForwarderManager;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.EntityUtils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class FsUtils {
+    public static final String LOG_TAG = "FsUtils";
+
+    private static final String SCRIPT_URL = ForwarderManager.getHostSchemePort(false) +
+            "WebKitTools/DumpRenderTree/android/get_layout_tests_dir_contents.php";
+
+    private static final int HTTP_TIMEOUT_MS = 5000;
+
+    private static HttpClient sHttpClient;
+
+    private static HttpClient getHttpClient() {
+        if (sHttpClient == null) {
+            HttpParams params = new BasicHttpParams();
+
+            SchemeRegistry schemeRegistry = new SchemeRegistry();
+            schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(),
+                    ForwarderManager.HTTP_PORT));
+            schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(),
+                    ForwarderManager.HTTPS_PORT));
+
+            ClientConnectionManager connectionManager = new ThreadSafeClientConnManager(params,
+                    schemeRegistry);
+            sHttpClient = new DefaultHttpClient(connectionManager, params);
+            HttpConnectionParams.setSoTimeout(sHttpClient.getParams(), HTTP_TIMEOUT_MS);
+            HttpConnectionParams.setConnectionTimeout(sHttpClient.getParams(), HTTP_TIMEOUT_MS);
+        }
+        return sHttpClient;
+    }
+
+    public static void writeDataToStorage(File file, byte[] bytes, boolean append) {
+        Log.d(LOG_TAG, "writeDataToStorage(): " + file.getAbsolutePath());
+        try {
+            OutputStream outputStream = null;
+            try {
+                file.getParentFile().mkdirs();
+                file.createNewFile();
+                Log.d(LOG_TAG, "writeDataToStorage(): File created: " + file.getAbsolutePath());
+                outputStream = new FileOutputStream(file, append);
+                outputStream.write(bytes);
+            } finally {
+                if (outputStream != null) {
+                    outputStream.close();
+                }
+            }
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "file.getAbsolutePath=" + file.getAbsolutePath() + " append=" + append,
+                    e);
+        }
+    }
+
+    public static byte[] readDataFromStorage(File file) {
+        if (!file.exists()) {
+            Log.d(LOG_TAG, "readDataFromStorage(): File does not exist: "
+                    + file.getAbsolutePath());
+            return null;
+        }
+
+        byte[] bytes = null;
+        try {
+            FileInputStream fis = null;
+            try {
+                fis = new FileInputStream(file);
+                bytes = new byte[(int)file.length()];
+                fis.read(bytes);
+            } finally {
+                if (fis != null) {
+                    fis.close();
+                }
+            }
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "file.getAbsolutePath=" + file.getAbsolutePath(), e);
+        }
+
+        return bytes;
+    }
+
+    public static byte[] readDataFromUrl(URL url) {
+        if (url == null) {
+            Log.w(LOG_TAG, "readDataFromUrl(): url is null!");
+            return null;
+        }
+
+        HttpGet httpRequest = new HttpGet(url.toString());
+        ResponseHandler<byte[]> handler = new ResponseHandler<byte[]>() {
+            @Override
+            public byte[] handleResponse(HttpResponse response) throws IOException {
+                if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+                    return null;
+                }
+                HttpEntity entity = response.getEntity();
+                return (entity == null ? null : EntityUtils.toByteArray(entity));
+            }
+        };
+
+        byte[] bytes = null;
+        try {
+            /**
+             * TODO: Not exactly sure why some requests hang indefinitely, but adding this
+             * timeout (in static getter for http client) in loop helps.
+             */
+            boolean timedOut;
+            do {
+                timedOut = false;
+                try {
+                    bytes = getHttpClient().execute(httpRequest, handler);
+                } catch (SocketTimeoutException e) {
+                    timedOut = true;
+                    Log.w(LOG_TAG, "Expected SocketTimeoutException: " + url, e);
+                }
+            } while (timedOut);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "url=" + url, e);
+        }
+
+        return bytes;
+    }
+
+    public static List<String> getLayoutTestsDirContents(String dirRelativePath, boolean recurse,
+            boolean mode) {
+        String modeString = (mode ? "folders" : "files");
+
+        URL url = null;
+        try {
+            url = new URL(SCRIPT_URL +
+                    "?path=" + dirRelativePath +
+                    "&recurse=" + recurse +
+                    "&mode=" + modeString);
+        } catch (MalformedURLException e) {
+            Log.e(LOG_TAG, "path=" + dirRelativePath + " recurse=" + recurse + " mode=" +
+                    modeString, e);
+            return new LinkedList<String>();
+        }
+
+        HttpGet httpRequest = new HttpGet(url.toString());
+        ResponseHandler<LinkedList<String>> handler = new ResponseHandler<LinkedList<String>>() {
+            @Override
+            public LinkedList<String> handleResponse(HttpResponse response)
+                    throws IOException {
+                LinkedList<String> lines = new LinkedList<String>();
+
+                if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+                    return lines;
+                }
+                HttpEntity entity = response.getEntity();
+                if (entity == null) {
+                    return lines;
+                }
+
+                BufferedReader reader =
+                        new BufferedReader(new InputStreamReader(entity.getContent()));
+                String line;
+                try {
+                    while ((line = reader.readLine()) != null) {
+                        lines.add(line);
+                    }
+                } finally {
+                    if (reader != null) {
+                        reader.close();
+                    }
+                }
+
+                return lines;
+            }
+        };
+
+        try {
+            return getHttpClient().execute(httpRequest, handler);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "getLayoutTestsDirContents(): HTTP GET failed for URL " + url);
+            return null;
+        }
+    }
+
+    public static void closeInputStream(InputStream inputStream) {
+        try {
+            if (inputStream != null) {
+                inputStream.close();
+            }
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Couldn't close stream!", e);
+        }
+    }
+
+    public static void closeOutputStream(OutputStream outputStream) {
+        try {
+            if (outputStream != null) {
+                outputStream.close();
+            }
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Couldn't close stream!", e);
+        }
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
new file mode 100644
index 0000000..e608e2d
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.net.Uri;
+import android.util.Log;
+import android.webkit.MockGeolocation;
+import android.webkit.WebStorage;
+
+import java.io.File;
+
+/**
+ * A class that is registered as JS interface for webview in LayoutTestExecutor
+ */
+public class LayoutTestController {
+    private static final String LOG_TAG = "LayoutTestController";
+
+    LayoutTestsExecutor mLayoutTestsExecutor;
+
+    public LayoutTestController(LayoutTestsExecutor layoutTestsExecutor) {
+        mLayoutTestsExecutor = layoutTestsExecutor;
+    }
+
+    public void clearAllDatabases() {
+        Log.i(LOG_TAG, "clearAllDatabases() called");
+        WebStorage.getInstance().deleteAllData();
+    }
+
+    public void dumpAsText() {
+        dumpAsText(false);
+    }
+
+    public void dumpAsText(boolean enablePixelTest) {
+        mLayoutTestsExecutor.dumpAsText(enablePixelTest);
+    }
+
+    public void dumpChildFramesAsText() {
+        mLayoutTestsExecutor.dumpChildFramesAsText();
+    }
+
+    public void dumpDatabaseCallbacks() {
+        mLayoutTestsExecutor.dumpDatabaseCallbacks();
+    }
+
+    public void notifyDone() {
+        mLayoutTestsExecutor.notifyDone();
+    }
+
+    public void overridePreference(String key, boolean value) {
+        mLayoutTestsExecutor.overridePreference(key, value);
+    }
+
+    public void setAppCacheMaximumSize(long size) {
+        Log.i(LOG_TAG, "setAppCacheMaximumSize() called with: " + size);
+        WebStorage.getInstance().setAppCacheMaximumSize(size);
+    }
+
+    public void setCanOpenWindows() {
+        mLayoutTestsExecutor.setCanOpenWindows();
+    }
+
+    public void setDatabaseQuota(long quota) {
+        /** TODO: Reset this before every test! */
+        Log.i(LOG_TAG, "setDatabaseQuota() called with: " + quota);
+        WebStorage.getInstance().setQuotaForOrigin(Uri.fromFile(new File("")).toString(),
+                quota);
+    }
+
+    public void setGeolocationPermission(boolean allow) {
+        mLayoutTestsExecutor.setGeolocationPermission(allow);
+    }
+
+    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+        // Configuration is in WebKit, so stay on WebCore thread, but go via LayoutTestsExecutor
+        // as we need access to the Webview.
+        Log.i(LOG_TAG, "setMockDeviceOrientation(" + canProvideAlpha +
+                ", " + alpha + ", " + canProvideBeta + ", " + beta + ", " + canProvideGamma +
+                ", " + gamma + ")");
+        mLayoutTestsExecutor.setMockDeviceOrientation(
+                canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
+    }
+
+    public void setMockGeolocationError(int code, String message) {
+        Log.i(LOG_TAG, "setMockGeolocationError(): " + "code=" + code + " message=" + message);
+        MockGeolocation.getInstance().setError(code, message);
+    }
+
+    public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
+        Log.i(LOG_TAG, "setMockGeolocationPosition(): " + "latitude=" + latitude +
+                " longitude=" + longitude + " accuracy=" + accuracy);
+        MockGeolocation.getInstance().setPosition(latitude, longitude, accuracy);
+    }
+
+    public void setXSSAuditorEnabled(boolean flag) {
+        mLayoutTestsExecutor.setXSSAuditorEnabled(flag);
+    }
+
+    public void waitUntilDone() {
+        mLayoutTestsExecutor.waitUntilDone();
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
new file mode 100644
index 0000000..58f7183
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -0,0 +1,738 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.net.http.SslError;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.PowerManager.WakeLock;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Window;
+import android.webkit.ConsoleMessage;
+import android.webkit.GeolocationPermissions;
+import android.webkit.HttpAuthHandler;
+import android.webkit.JsPromptResult;
+import android.webkit.JsResult;
+import android.webkit.SslErrorHandler;
+import android.webkit.WebChromeClient;
+import android.webkit.WebSettings;
+import android.webkit.WebStorage;
+import android.webkit.WebStorage.QuotaUpdater;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import java.io.File;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This activity executes the test. It contains WebView and logic of LayoutTestController
+ * functions. It runs in a separate process and sends the results of running the test
+ * to ManagerService. The reason why is to handle crashing (test that crashes brings down
+ * whole process with it).
+ */
+public class LayoutTestsExecutor extends Activity {
+
+    private enum CurrentState {
+        IDLE,
+        RENDERING_PAGE,
+        WAITING_FOR_ASYNCHRONOUS_TEST,
+        OBTAINING_RESULT;
+
+        public boolean isRunningState() {
+            return this == CurrentState.RENDERING_PAGE ||
+                    this == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
+        }
+    }
+
+    private static final String LOG_TAG = "LayoutTestsExecutor";
+
+    public static final String EXTRA_TESTS_LIST = "TestsList";
+    public static final String EXTRA_TEST_INDEX = "TestIndex";
+
+    private static final int MSG_ACTUAL_RESULT_OBTAINED = 0;
+    private static final int MSG_TEST_TIMED_OUT = 1;
+
+    private static final int DEFAULT_TIME_OUT_MS = 15 * 1000;
+
+    /** A list of tests that remain to run since last crash */
+    private List<String> mTestsList;
+
+    /**
+     * This is a number of currently running test. It is 0-based and doesn't reset after
+     * the crash. Initial index is passed to LayoutTestsExecuter in the intent that starts
+     * it.
+     */
+    private int mCurrentTestIndex;
+
+    /** The total number of tests to run, doesn't reset after crash */
+    private int mTotalTestCount;
+
+    private WebView mCurrentWebView;
+    private String mCurrentTestRelativePath;
+    private String mCurrentTestUri;
+    private CurrentState mCurrentState = CurrentState.IDLE;
+
+    private boolean mCurrentTestTimedOut;
+    private AbstractResult mCurrentResult;
+    private AdditionalTextOutput mCurrentAdditionalTextOutput;
+
+    private LayoutTestController mLayoutTestController = new LayoutTestController(this);
+    private boolean mCanOpenWindows;
+    private boolean mDumpDatabaseCallbacks;
+    private boolean mIsGeolocationPermissionSet;
+    private boolean mGeolocationPermission;
+    private Map<GeolocationPermissions.Callback, String> mPendingGeolocationPermissionCallbacks;
+
+    private EventSender mEventSender = new EventSender();
+
+    private WakeLock mScreenDimLock;
+
+    /** COMMUNICATION WITH ManagerService */
+
+    private Messenger mManagerServiceMessenger;
+
+    private ServiceConnection mServiceConnection = new ServiceConnection() {
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mManagerServiceMessenger = new Messenger(service);
+            startTests();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            /** TODO */
+        }
+    };
+
+    private final Handler mResultHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_ACTUAL_RESULT_OBTAINED:
+                    onActualResultsObtained();
+                    break;
+
+                case MSG_TEST_TIMED_OUT:
+                    onTestTimedOut();
+                    break;
+
+                default:
+                    break;
+            }
+        }
+    };
+
+    /** WEBVIEW CONFIGURATION */
+
+    private WebViewClient mWebViewClient = new WebViewClient() {
+        @Override
+        public void onPageFinished(WebView view, String url) {
+            /** Some tests fire up many page loads, we don't want to detect them */
+            if (!url.equals(mCurrentTestUri)) {
+                return;
+            }
+
+            if (mCurrentState == CurrentState.RENDERING_PAGE) {
+                onTestFinished();
+            }
+        }
+
+         @Override
+         public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
+                 String host, String realm) {
+             if (handler.useHttpAuthUsernamePassword() && view != null) {
+                 String[] credentials = view.getHttpAuthUsernamePassword(host, realm);
+                 if (credentials != null && credentials.length == 2) {
+                     handler.proceed(credentials[0], credentials[1]);
+                     return;
+                 }
+             }
+             handler.cancel();
+         }
+
+         @Override
+         public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+             // We ignore SSL errors. In particular, the certificate used by the LayoutTests server
+             // produces an error as it lacks a CN field.
+             handler.proceed();
+         }
+    };
+
+    private WebChromeClient mWebChromeClient = new WebChromeClient() {
+        @Override
+        public void onExceededDatabaseQuota(String url, String databaseIdentifier,
+                long currentQuota, long estimatedSize, long totalUsedQuota,
+                QuotaUpdater quotaUpdater) {
+            /** TODO: This should be recorded as part of the text result */
+            /** TODO: The quota should also probably be reset somehow for every test? */
+            if (mDumpDatabaseCallbacks) {
+                getCurrentAdditionalTextOutput().appendExceededDbQuotaMessage(url,
+                        databaseIdentifier);
+            }
+            quotaUpdater.updateQuota(currentQuota + 5 * 1024 * 1024);
+        }
+
+        @Override
+        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
+            getCurrentAdditionalTextOutput().appendJsAlert(message);
+            result.confirm();
+            return true;
+        }
+
+        @Override
+        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
+            getCurrentAdditionalTextOutput().appendJsConfirm(message);
+            result.confirm();
+            return true;
+        }
+
+        @Override
+        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
+                JsPromptResult result) {
+            getCurrentAdditionalTextOutput().appendJsPrompt(message, defaultValue);
+            result.confirm();
+            return true;
+        }
+
+        @Override
+        public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+            getCurrentAdditionalTextOutput().appendConsoleMessage(consoleMessage);
+            return true;
+        }
+
+        @Override
+        public boolean onCreateWindow(WebView view, boolean dialog, boolean userGesture,
+                Message resultMsg) {
+            WebView.WebViewTransport transport = (WebView.WebViewTransport)resultMsg.obj;
+            /** By default windows cannot be opened, so just send null back. */
+            WebView newWindowWebView = null;
+
+            if (mCanOpenWindows) {
+                /**
+                 * We never display the new window, just create the view and allow it's content to
+                 * execute and be recorded by the executor.
+                 */
+                newWindowWebView = createWebViewWithJavascriptInterfaces();
+                setupWebView(newWindowWebView);
+            }
+
+            transport.setWebView(newWindowWebView);
+            resultMsg.sendToTarget();
+            return true;
+        }
+
+        @Override
+        public void onGeolocationPermissionsShowPrompt(String origin,
+                GeolocationPermissions.Callback callback) {
+            if (mIsGeolocationPermissionSet) {
+                callback.invoke(origin, mGeolocationPermission, false);
+                return;
+            }
+            if (mPendingGeolocationPermissionCallbacks == null) {
+                mPendingGeolocationPermissionCallbacks =
+                        new HashMap<GeolocationPermissions.Callback, String>();
+            }
+            mPendingGeolocationPermissionCallbacks.put(callback, origin);
+        }
+    };
+
+    /** IMPLEMENTATION */
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        /**
+         * It detects the crash by catching all the uncaught exceptions. However, we
+         * still have to kill the process, because after catching the exception the
+         * activity remains in a strange state, where intents don't revive it.
+         * However, we send the message to the service to speed up the rebooting
+         * (we don't have to wait for time-out to kick in).
+         */
+        Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+            @Override
+            public void uncaughtException(Thread thread, Throwable e) {
+                Log.w(LOG_TAG,
+                        "onTestCrashed(): " + mCurrentTestRelativePath + " thread=" + thread, e);
+
+                try {
+                    Message serviceMsg =
+                            Message.obtain(null, ManagerService.MSG_CURRENT_TEST_CRASHED);
+
+                    mManagerServiceMessenger.send(serviceMsg);
+                } catch (RemoteException e2) {
+                    Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e2);
+                }
+
+                Process.killProcess(Process.myPid());
+            }
+        });
+
+        requestWindowFeature(Window.FEATURE_PROGRESS);
+
+        Intent intent = getIntent();
+        mTestsList = intent.getStringArrayListExtra(EXTRA_TESTS_LIST);
+        mCurrentTestIndex = intent.getIntExtra(EXTRA_TEST_INDEX, -1);
+        mTotalTestCount = mCurrentTestIndex + mTestsList.size();
+
+        PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
+        mScreenDimLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK
+                | PowerManager.ON_AFTER_RELEASE, "WakeLock in LayoutTester");
+        mScreenDimLock.acquire();
+
+        bindService(new Intent(this, ManagerService.class), mServiceConnection,
+                Context.BIND_AUTO_CREATE);
+    }
+
+    private void reset() {
+        WebView previousWebView = mCurrentWebView;
+
+        resetLayoutTestController();
+
+        mCurrentTestTimedOut = false;
+        mCurrentResult = null;
+        mCurrentAdditionalTextOutput = null;
+
+        mCurrentWebView = createWebViewWithJavascriptInterfaces();
+        // When we create the first WebView, we need to pause to wait for the WebView thread to spin
+        // and up and for it to register its message handlers.
+        if (previousWebView == null) {
+            try {
+                Thread.currentThread().sleep(1000);
+            } catch (Exception e) {}
+        }
+        setupWebView(mCurrentWebView);
+
+        mEventSender.reset(mCurrentWebView);
+
+        setContentView(mCurrentWebView);
+        if (previousWebView != null) {
+            Log.d(LOG_TAG + "::reset", "previousWebView != null");
+            previousWebView.destroy();
+        }
+    }
+
+    private static class WebViewWithJavascriptInterfaces extends WebView {
+        public WebViewWithJavascriptInterfaces(
+                Context context, Map<String, Object> javascriptInterfaces) {
+            super(context,
+                  null, // attribute set
+                  0, // default style resource ID
+                  javascriptInterfaces,
+                  false); // is private browsing
+        }
+    }
+    private WebView createWebViewWithJavascriptInterfaces() {
+        Map<String, Object> javascriptInterfaces = new HashMap<String, Object>();
+        javascriptInterfaces.put("layoutTestController", mLayoutTestController);
+        javascriptInterfaces.put("eventSender", mEventSender);
+        return new WebViewWithJavascriptInterfaces(this, javascriptInterfaces);
+    }
+
+    private void setupWebView(WebView webView) {
+        webView.setWebViewClient(mWebViewClient);
+        webView.setWebChromeClient(mWebChromeClient);
+
+        /**
+         * Setting a touch interval of -1 effectively disables the optimisation in WebView
+         * that stops repeated touch events flooding WebCore. The Event Sender only sends a
+         * single event rather than a stream of events (like what would generally happen in
+         * a real use of touch events in a WebView)  and so if the WebView drops the event,
+         * the test will fail as the test expects one callback for every touch it synthesizes.
+         */
+        webView.setTouchInterval(-1);
+
+        webView.clearCache(true);
+        webView.setDeferMultiTouch(true);
+
+        WebSettings webViewSettings = webView.getSettings();
+        webViewSettings.setAppCacheEnabled(true);
+        webViewSettings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
+        // Use of larger values causes unexplained AppCache database corruption.
+        // TODO: Investigate what's really going on here.
+        webViewSettings.setAppCacheMaxSize(100 * 1024 * 1024);
+        webViewSettings.setJavaScriptEnabled(true);
+        webViewSettings.setJavaScriptCanOpenWindowsAutomatically(true);
+        webViewSettings.setSupportMultipleWindows(true);
+        webViewSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
+        webViewSettings.setDatabaseEnabled(true);
+        webViewSettings.setDatabasePath(getDir("databases", 0).getAbsolutePath());
+        webViewSettings.setDomStorageEnabled(true);
+        webViewSettings.setWorkersEnabled(false);
+        webViewSettings.setXSSAuditorEnabled(false);
+        webViewSettings.setPageCacheCapacity(0);
+
+        // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
+        mCurrentWebView.useMockDeviceOrientation();
+
+        // Must do this after setting the AppCache path.
+        WebStorage.getInstance().deleteAllData();
+    }
+
+    private void startTests() {
+        if (mCurrentTestIndex == 0) {
+            sendFirstTestMessage();
+        }
+
+        runNextTest();
+    }
+
+    private void sendFirstTestMessage() {
+        try {
+            Message serviceMsg = Message.obtain(null, ManagerService.MSG_FIRST_TEST);
+
+            Bundle bundle = new Bundle();
+            bundle.putString("firstTest", mTestsList.get(0));
+            bundle.putInt("index", mCurrentTestIndex);
+
+            serviceMsg.setData(bundle);
+            mManagerServiceMessenger.send(serviceMsg);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Error sending message to manager service:", e);
+        }
+    }
+
+    private void runNextTest() {
+        assert mCurrentState == CurrentState.IDLE : "mCurrentState = " + mCurrentState.name();
+
+        if (mTestsList.isEmpty()) {
+            onAllTestsFinished();
+            return;
+        }
+
+        mCurrentTestRelativePath = mTestsList.remove(0);
+
+        Log.i(LOG_TAG, "runNextTest(): Start: " + mCurrentTestRelativePath +
+                " (" + mCurrentTestIndex + ")");
+
+        mCurrentTestUri = FileFilter.getUrl(mCurrentTestRelativePath).toString();
+
+        reset();
+
+        /** Start time-out countdown and the test */
+        mCurrentState = CurrentState.RENDERING_PAGE;
+        mResultHandler.sendEmptyMessageDelayed(MSG_TEST_TIMED_OUT, DEFAULT_TIME_OUT_MS);
+        mCurrentWebView.loadUrl(mCurrentTestUri);
+    }
+
+    private void onTestTimedOut() {
+        assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
+
+        Log.w(LOG_TAG, "onTestTimedOut(): " + mCurrentTestRelativePath);
+        mCurrentTestTimedOut = true;
+
+        /**
+         * While it is theoretically possible that the test times out because
+         * of webview becoming unresponsive, it is very unlikely. Therefore it's
+         * assumed that obtaining results (that calls various webview methods)
+         * will not itself hang.
+         */
+        obtainActualResultsFromWebView();
+    }
+
+    private void onTestFinished() {
+        assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
+
+        Log.i(LOG_TAG, "onTestFinished(): " + mCurrentTestRelativePath);
+        mResultHandler.removeMessages(MSG_TEST_TIMED_OUT);
+        obtainActualResultsFromWebView();
+    }
+
+    private void obtainActualResultsFromWebView() {
+        /**
+         * If the result has not been set by the time the test finishes we create
+         * a default type of result.
+         */
+        if (mCurrentResult == null) {
+            /** TODO: Default type should be RenderTreeResult. We don't support it now. */
+            mCurrentResult = new TextResult(mCurrentTestRelativePath);
+        }
+
+        mCurrentState = CurrentState.OBTAINING_RESULT;
+
+        if (mCurrentTestTimedOut) {
+            mCurrentResult.setDidTimeOut();
+        }
+        mCurrentResult.obtainActualResults(mCurrentWebView,
+                mResultHandler.obtainMessage(MSG_ACTUAL_RESULT_OBTAINED));
+    }
+
+    private void onActualResultsObtained() {
+        assert mCurrentState == CurrentState.OBTAINING_RESULT
+                : "mCurrentState = " + mCurrentState.name();
+
+        Log.i(LOG_TAG, "onActualResultsObtained(): " + mCurrentTestRelativePath);
+        mCurrentState = CurrentState.IDLE;
+
+        reportResultToService();
+        mCurrentTestIndex++;
+        updateProgressBar();
+        runNextTest();
+    }
+
+    private void reportResultToService() {
+        if (mCurrentAdditionalTextOutput != null) {
+            mCurrentResult.setAdditionalTextOutputString(mCurrentAdditionalTextOutput.toString());
+        }
+
+        try {
+            Message serviceMsg =
+                    Message.obtain(null, ManagerService.MSG_PROCESS_ACTUAL_RESULTS);
+
+            Bundle bundle = mCurrentResult.getBundle();
+            bundle.putInt("testIndex", mCurrentTestIndex);
+            if (!mTestsList.isEmpty()) {
+                bundle.putString("nextTest", mTestsList.get(0));
+            }
+
+            serviceMsg.setData(bundle);
+            mManagerServiceMessenger.send(serviceMsg);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
+        }
+    }
+
+    private void updateProgressBar() {
+        getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
+                mCurrentTestIndex * Window.PROGRESS_END / mTotalTestCount);
+        setTitle(mCurrentTestIndex * 100 / mTotalTestCount + "% " +
+                "(" + mCurrentTestIndex + "/" + mTotalTestCount + ")");
+    }
+
+    private void onAllTestsFinished() {
+        mScreenDimLock.release();
+
+        try {
+            Message serviceMsg =
+                    Message.obtain(null, ManagerService.MSG_ALL_TESTS_FINISHED);
+            mManagerServiceMessenger.send(serviceMsg);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "mCurrentTestRelativePath=" + mCurrentTestRelativePath, e);
+        }
+
+        unbindService(mServiceConnection);
+    }
+
+    private AdditionalTextOutput getCurrentAdditionalTextOutput() {
+        if (mCurrentAdditionalTextOutput == null) {
+            mCurrentAdditionalTextOutput = new AdditionalTextOutput();
+        }
+        return mCurrentAdditionalTextOutput;
+    }
+
+    /** LAYOUT TEST CONTROLLER */
+
+    private static final int MSG_WAIT_UNTIL_DONE = 0;
+    private static final int MSG_NOTIFY_DONE = 1;
+    private static final int MSG_DUMP_AS_TEXT = 2;
+    private static final int MSG_DUMP_CHILD_FRAMES_AS_TEXT = 3;
+    private static final int MSG_SET_CAN_OPEN_WINDOWS = 4;
+    private static final int MSG_DUMP_DATABASE_CALLBACKS = 5;
+    private static final int MSG_SET_GEOLOCATION_PERMISSION = 6;
+    private static final int MSG_OVERRIDE_PREFERENCE = 7;
+    private static final int MSG_SET_XSS_AUDITOR_ENABLED = 8;
+
+    /** String constants for use with layoutTestController.overridePreference() */
+    private final String WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED =
+            "WebKitOfflineWebApplicationCacheEnabled";
+    private final String WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY = "WebKitUsesPageCachePreferenceKey";
+
+    Handler mLayoutTestControllerHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
+
+            switch (msg.what) {
+                case MSG_DUMP_AS_TEXT:
+                    if (mCurrentResult == null) {
+                        mCurrentResult = new TextResult(mCurrentTestRelativePath);
+                    }
+                    assert mCurrentResult instanceof TextResult
+                            : "mCurrentResult instanceof" + mCurrentResult.getClass().getName();
+                    break;
+
+                case MSG_DUMP_CHILD_FRAMES_AS_TEXT:
+                    /** If dumpAsText was not called we assume that the result should be text */
+                    if (mCurrentResult == null) {
+                        mCurrentResult = new TextResult(mCurrentTestRelativePath);
+                    }
+
+                    assert mCurrentResult instanceof TextResult
+                            : "mCurrentResult instanceof" + mCurrentResult.getClass().getName();
+
+                    ((TextResult)mCurrentResult).setDumpChildFramesAsText(true);
+                    break;
+
+                case MSG_DUMP_DATABASE_CALLBACKS:
+                    mDumpDatabaseCallbacks = true;
+                    break;
+
+                case MSG_NOTIFY_DONE:
+                    if (mCurrentState == CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST) {
+                        onTestFinished();
+                    }
+                    break;
+
+                case MSG_OVERRIDE_PREFERENCE:
+                    /**
+                     * TODO: We should look up the correct WebView for the frame which
+                     * called the layoutTestController method. Currently, we just use the
+                     * WebView for the main frame. EventSender suffers from the same
+                     * problem.
+                     */
+                    String key = msg.getData().getString("key");
+                    boolean value = msg.getData().getBoolean("value");
+                    if (WEBKIT_OFFLINE_WEB_APPLICATION_CACHE_ENABLED.equals(key)) {
+                        mCurrentWebView.getSettings().setAppCacheEnabled(value);
+                    } else if (WEBKIT_USES_PAGE_CACHE_PREFERENCE_KEY.equals(key)) {
+                        // Cache the maximum possible number of pages.
+                        mCurrentWebView.getSettings().setPageCacheCapacity(Integer.MAX_VALUE);
+                    } else {
+                        Log.w(LOG_TAG, "LayoutTestController.overridePreference(): " +
+                              "Unsupported preference '" + key + "'");
+                    }
+                    break;
+
+                case MSG_SET_CAN_OPEN_WINDOWS:
+                    mCanOpenWindows = true;
+                    break;
+
+                case MSG_SET_GEOLOCATION_PERMISSION:
+                    mIsGeolocationPermissionSet = true;
+                    mGeolocationPermission = msg.arg1 == 1;
+
+                    if (mPendingGeolocationPermissionCallbacks != null) {
+                        Iterator<GeolocationPermissions.Callback> iter =
+                                mPendingGeolocationPermissionCallbacks.keySet().iterator();
+                        while (iter.hasNext()) {
+                            GeolocationPermissions.Callback callback = iter.next();
+                            String origin = mPendingGeolocationPermissionCallbacks.get(callback);
+                            callback.invoke(origin, mGeolocationPermission, false);
+                        }
+                        mPendingGeolocationPermissionCallbacks = null;
+                    }
+                    break;
+
+                case MSG_SET_XSS_AUDITOR_ENABLED:
+                    mCurrentWebView.getSettings().setXSSAuditorEnabled(msg.arg1 == 1);
+                    break;
+
+                case MSG_WAIT_UNTIL_DONE:
+                    mCurrentState = CurrentState.WAITING_FOR_ASYNCHRONOUS_TEST;
+                    break;
+
+                default:
+                    assert false : "msg.what=" + msg.what;
+                    break;
+            }
+        }
+    };
+
+    private void resetLayoutTestController() {
+        mCanOpenWindows = false;
+        mDumpDatabaseCallbacks = false;
+        mIsGeolocationPermissionSet = false;
+        mPendingGeolocationPermissionCallbacks = null;
+    }
+
+    public void dumpAsText(boolean enablePixelTest) {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpAsText(" + enablePixelTest + ") called");
+        /** TODO: Implement */
+        if (enablePixelTest) {
+            Log.w(LOG_TAG, "enablePixelTest not implemented, switching to false");
+        }
+        mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_AS_TEXT);
+    }
+
+    public void dumpChildFramesAsText() {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpChildFramesAsText() called");
+        mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_CHILD_FRAMES_AS_TEXT);
+    }
+
+    public void dumpDatabaseCallbacks() {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": dumpDatabaseCallbacks() called");
+        mLayoutTestControllerHandler.sendEmptyMessage(MSG_DUMP_DATABASE_CALLBACKS);
+    }
+
+    public void notifyDone() {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": notifyDone() called");
+        mLayoutTestControllerHandler.sendEmptyMessage(MSG_NOTIFY_DONE);
+    }
+
+    public void overridePreference(String key, boolean value) {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": overridePreference(" + key + ", " + value +
+        ") called");
+        Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_OVERRIDE_PREFERENCE);
+        msg.getData().putString("key", key);
+        msg.getData().putBoolean("value", value);
+        msg.sendToTarget();
+    }
+
+    public void setCanOpenWindows() {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setCanOpenWindows() called");
+        mLayoutTestControllerHandler.sendEmptyMessage(MSG_SET_CAN_OPEN_WINDOWS);
+    }
+
+    public void setGeolocationPermission(boolean allow) {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setGeolocationPermission(" + allow +
+                ") called");
+        Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_GEOLOCATION_PERMISSION);
+        msg.arg1 = allow ? 1 : 0;
+        msg.sendToTarget();
+    }
+
+    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setMockDeviceOrientation(" + canProvideAlpha +
+                ", " + alpha + ", " + canProvideBeta + ", " + beta + ", " + canProvideGamma +
+                ", " + gamma + ")");
+        mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+                canProvideGamma, gamma);
+    }
+
+    public void setXSSAuditorEnabled(boolean flag) {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": setXSSAuditorEnabled(" + flag + ") called");
+        Message msg = mLayoutTestControllerHandler.obtainMessage(MSG_SET_XSS_AUDITOR_ENABLED);
+        msg.arg1 = flag ? 1 : 0;
+        msg.sendToTarget();
+    }
+
+    public void waitUntilDone() {
+        Log.i(LOG_TAG, mCurrentTestRelativePath + ": waitUntilDone() called");
+        mLayoutTestControllerHandler.sendEmptyMessage(MSG_WAIT_UNTIL_DONE);
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
new file mode 100644
index 0000000..f42dc86
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.util.Log;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A service that handles managing the results of tests, informing of crashes, generating
+ * summaries, etc.
+ */
+public class ManagerService extends Service {
+
+    private static final String LOG_TAG = "ManagerService";
+
+    private static final int MSG_CRASH_TIMEOUT_EXPIRED = 0;
+    private static final int MSG_SUMMARIZER_DONE = 1;
+
+    private static final int CRASH_TIMEOUT_MS = 20 * 1000;
+
+    /** TODO: make it a setting */
+    static final String RESULTS_ROOT_DIR_PATH =
+            Environment.getExternalStorageDirectory() + File.separator + "layout-test-results";
+
+    /** TODO: Make it a setting */
+    private static final List<String> EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES =
+            new ArrayList<String>(3);
+    {
+        EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.add("platform" + File.separator +
+                "android-v8" + File.separator);
+        EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.add("platform" + File.separator +
+                "android" + File.separator);
+        EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.add("");
+    }
+
+    /** TODO: Make these settings */
+    private static final String TEXT_RESULT_EXTENSION = "txt";
+    private static final String IMAGE_RESULT_EXTENSION = "png";
+
+    static final int MSG_PROCESS_ACTUAL_RESULTS = 0;
+    static final int MSG_ALL_TESTS_FINISHED = 1;
+    static final int MSG_FIRST_TEST = 2;
+    static final int MSG_CURRENT_TEST_CRASHED = 3;
+
+    /**
+     * This handler is purely for IPC. It is used to create mMessenger
+     * that generates a binder returned in onBind method.
+     */
+    private Handler mIncomingHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_FIRST_TEST:
+                    mSummarizer.reset();
+                    Bundle bundle = msg.getData();
+                    ensureNextTestSetup(bundle.getString("firstTest"), bundle.getInt("index"));
+                    break;
+
+                case MSG_PROCESS_ACTUAL_RESULTS:
+                    Log.d(LOG_TAG,"mIncomingHandler: " + msg.getData().getString("relativePath"));
+                    onActualResultsObtained(msg.getData());
+                    break;
+
+                case MSG_CURRENT_TEST_CRASHED:
+                    mInternalMessagesHandler.removeMessages(MSG_CRASH_TIMEOUT_EXPIRED);
+                    onTestCrashed();
+                    break;
+
+                case MSG_ALL_TESTS_FINISHED:
+                    /** We run it in a separate thread to avoid ANR */
+                    new Thread() {
+                        @Override
+                        public void run() {
+                            mSummarizer.setTestsRelativePath(mAllTestsRelativePath);
+                            Message msg = Message.obtain(mInternalMessagesHandler,
+                                    MSG_SUMMARIZER_DONE);
+                            mSummarizer.summarize(msg);
+                        }
+                    }.start();
+            }
+        }
+    };
+
+    private Messenger mMessenger = new Messenger(mIncomingHandler);
+
+    private Handler mInternalMessagesHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_CRASH_TIMEOUT_EXPIRED:
+                    onTestCrashed();
+                    break;
+
+                case MSG_SUMMARIZER_DONE:
+                    Intent intent = new Intent(ManagerService.this, TestsListActivity.class);
+                    intent.setAction(Intent.ACTION_SHUTDOWN);
+                    /** This flag is needed because we send the intent from the service */
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    startActivity(intent);
+                    break;
+            }
+        }
+    };
+
+    private FileFilter mFileFilter;
+    private Summarizer mSummarizer;
+
+    private String mCurrentlyRunningTest;
+    private int mCurrentlyRunningTestIndex;
+
+    /**
+     * These are implementation details of getExpectedResultPath() used to reduce the number
+     * of requests required to the host server.
+     */
+    private String mLastExpectedResultPathRequested;
+    private String mLastExpectedResultPathFetched;
+
+    private String mAllTestsRelativePath;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        mFileFilter = new FileFilter();
+        mSummarizer = new Summarizer(mFileFilter, RESULTS_ROOT_DIR_PATH, getApplicationContext());
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        mAllTestsRelativePath = intent.getStringExtra("path");
+        assert mAllTestsRelativePath != null;
+        return START_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+
+    private void onActualResultsObtained(Bundle bundle) {
+        mInternalMessagesHandler.removeMessages(MSG_CRASH_TIMEOUT_EXPIRED);
+        ensureNextTestSetup(bundle.getString("nextTest"), bundle.getInt("testIndex") + 1);
+
+        AbstractResult results =
+                AbstractResult.TestType.valueOf(bundle.getString("type")).createResult(bundle);
+
+        Log.i(LOG_TAG, "onActualResultObtained: " + results.getRelativePath());
+        handleResults(results);
+    }
+
+    private void ensureNextTestSetup(String nextTest, int index) {
+        if (nextTest == null) {
+            Log.w(LOG_TAG, "ensureNextTestSetup(): nextTest=null");
+            return;
+        }
+
+        mCurrentlyRunningTest = nextTest;
+        mCurrentlyRunningTestIndex = index;
+        mInternalMessagesHandler.sendEmptyMessageDelayed(MSG_CRASH_TIMEOUT_EXPIRED, CRASH_TIMEOUT_MS);
+    }
+
+    /**
+     * This sends an intent to TestsListActivity to restart LayoutTestsExecutor.
+     * The more detailed description of the flow is in the comment of onNewIntent
+     * method in TestsListActivity.
+     */
+    private void onTestCrashed() {
+        handleResults(new CrashedDummyResult(mCurrentlyRunningTest));
+
+        Log.w(LOG_TAG, "onTestCrashed(): " + mCurrentlyRunningTest +
+                " (" + mCurrentlyRunningTestIndex + ")");
+
+        Intent intent = new Intent(this, TestsListActivity.class);
+        intent.setAction(Intent.ACTION_REBOOT);
+        /** This flag is needed because we send the intent from the service */
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra("crashedTestIndex", mCurrentlyRunningTestIndex);
+        startActivity(intent);
+    }
+
+    private void handleResults(AbstractResult results) {
+        String relativePath = results.getRelativePath();
+        results.setExpectedTextResult(getExpectedTextResult(relativePath));
+        results.setExpectedTextResultPath(getExpectedTextResultPath(relativePath));
+        results.setExpectedImageResult(getExpectedImageResult(relativePath));
+        results.setExpectedImageResultPath(getExpectedImageResultPath(relativePath));
+
+        dumpActualTextResult(results);
+        dumpActualImageResult(results);
+
+        mSummarizer.appendTest(results);
+    }
+
+    private void dumpActualTextResult(AbstractResult result) {
+        String testPath = result.getRelativePath();
+        String actualTextResult = result.getActualTextResult();
+        if (actualTextResult == null) {
+            return;
+        }
+
+        String resultPath = FileFilter.setPathEnding(testPath, "-actual." + TEXT_RESULT_EXTENSION);
+        FsUtils.writeDataToStorage(new File(RESULTS_ROOT_DIR_PATH, resultPath),
+                actualTextResult.getBytes(), false);
+    }
+
+    private void dumpActualImageResult(AbstractResult result) {
+        String testPath = result.getRelativePath();
+        byte[] actualImageResult = result.getActualImageResult();
+        if (actualImageResult == null) {
+            return;
+        }
+
+        String resultPath = FileFilter.setPathEnding(testPath,
+                "-actual." + IMAGE_RESULT_EXTENSION);
+        FsUtils.writeDataToStorage(new File(RESULTS_ROOT_DIR_PATH, resultPath),
+                actualImageResult, false);
+    }
+
+    public String getExpectedTextResult(String relativePath) {
+        byte[] result = getExpectedResult(relativePath, TEXT_RESULT_EXTENSION);
+        if (result != null) {
+            return new String(result);
+        }
+        return null;
+    }
+
+    public byte[] getExpectedImageResult(String relativePath) {
+        return getExpectedResult(relativePath, IMAGE_RESULT_EXTENSION);
+    }
+
+    private byte[] getExpectedResult(String relativePath, String extension) {
+        String originalRelativePath =
+                FileFilter.setPathEnding(relativePath, "-expected." + extension);
+        mLastExpectedResultPathRequested = originalRelativePath;
+
+        byte[] bytes = null;
+        List<String> locations = EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES;
+
+        int size = EXPECTED_RESULT_LOCATION_RELATIVE_DIR_PREFIXES.size();
+        for (int i = 0; bytes == null && i < size; i++) {
+            relativePath = locations.get(i) + originalRelativePath;
+            bytes = FsUtils.readDataFromUrl(FileFilter.getUrl(relativePath));
+        }
+
+        mLastExpectedResultPathFetched = bytes == null ? null : relativePath;
+        return bytes;
+    }
+
+    private String getExpectedTextResultPath(String relativePath) {
+        return getExpectedResultPath(relativePath, TEXT_RESULT_EXTENSION);
+    }
+
+    private String getExpectedImageResultPath(String relativePath) {
+        return getExpectedResultPath(relativePath, IMAGE_RESULT_EXTENSION);
+    }
+
+    private String getExpectedResultPath(String relativePath, String extension) {
+        String originalRelativePath =
+            FileFilter.setPathEnding(relativePath, "-expected." + extension);
+        if (!originalRelativePath.equals(mLastExpectedResultPathRequested)) {
+            getExpectedResult(relativePath, extension);
+        }
+
+        return mLastExpectedResultPathFetched;
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
new file mode 100644
index 0000000..bd8c4ad
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.os.Build;
+import android.os.Message;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+import com.android.dumprendertree2.forwarder.ForwarderManager;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A class that collects information about tests that ran and can create HTML
+ * files with summaries and easy navigation.
+ */
+public class Summarizer {
+
+    private static final String LOG_TAG = "Summarizer";
+
+    private static final String CSS =
+            "<style type=\"text/css\">" +
+            "* {" +
+            "       font-family: Verdana;" +
+            "       border: 0;" +
+            "       margin: 0;" +
+            "       padding: 0;}" +
+            "body {" +
+            "       margin: 10px;}" +
+            "h1 {" +
+            "       font-size: 24px;" +
+            "       margin: 4px 0 4px 0;}" +
+            "h2 {" +
+            "       font-size:18px;" +
+            "       text-transform: uppercase;" +
+            "       margin: 20px 0 3px 0;}" +
+            "h3, h3 a {" +
+            "       font-size: 14px;" +
+            "       color: black;" +
+            "       text-decoration: none;" +
+            "       margin-top: 4px;" +
+            "       margin-bottom: 2px;}" +
+            "h3 a span.path {" +
+            "       text-decoration: underline;}" +
+            "h3 span.tri {" +
+            "       text-decoration: none;" +
+            "       float: left;" +
+            "       width: 20px;}" +
+            "h3 span.sqr {" +
+            "       text-decoration: none;" +
+            "       float: left;" +
+            "       width: 20px;}" +
+            "h3 span.sqr_pass {" +
+            "       color: #8ee100;}" +
+            "h3 span.sqr_fail {" +
+            "       color: #c30000;}" +
+            "span.source {" +
+            "       display: block;" +
+            "       font-size: 10px;" +
+            "       color: #888;" +
+            "       margin-left: 20px;" +
+            "       margin-bottom: 1px;}" +
+            "span.source a {" +
+            "       font-size: 10px;" +
+            "       color: #888;}" +
+            "h3 img {" +
+            "       width: 8px;" +
+            "       margin-right: 4px;}" +
+            "div.diff {" +
+            "       margin-bottom: 25px;}" +
+            "div.diff a {" +
+            "       font-size: 12px;" +
+            "       color: #888;}" +
+            "table.visual_diff {" +
+            "       border-bottom: 0px solid;" +
+            "       border-collapse: collapse;" +
+            "       width: 100%;" +
+            "       margin-bottom: 2px;}" +
+            "table.visual_diff tr.headers td {" +
+            "       border-bottom: 1px solid;" +
+            "       border-top: 0;" +
+            "       padding-bottom: 3px;}" +
+            "table.visual_diff tr.results td {" +
+            "       border-top: 1px dashed;" +
+            "       border-right: 1px solid;" +
+            "       font-size: 15px;" +
+            "       vertical-align: top;}" +
+            "table.visual_diff tr.results td.line_count {" +
+            "       background-color:#aaa;" +
+            "       min-width:20px;" +
+            "       text-align: right;" +
+            "       border-right: 1px solid;" +
+            "       border-left: 1px solid;" +
+            "       padding: 2px 1px 2px 0px;}" +
+            "table.visual_diff tr.results td.line {" +
+            "       padding: 2px 0px 2px 4px;" +
+            "       border-right: 1px solid;" +
+            "       width: 49.8%;}" +
+            "table.visual_diff tr.footers td {" +
+            "       border-top: 1px solid;" +
+            "       border-bottom: 0;}" +
+            "table.visual_diff tr td.space {" +
+            "       border: 0;" +
+            "       width: 0.4%}" +
+            "div.space {" +
+            "       margin-top:4px;}" +
+            "span.eql {" +
+            "       background-color: #f3f3f3;}" +
+            "span.del {" +
+            "       background-color: #ff8888; }" +
+            "span.ins {" +
+            "       background-color: #88ff88; }" +
+            "table.summary {" +
+            "       border: 1px solid black;" +
+            "       margin-top: 20px;}" +
+            "table.summary td {" +
+            "       padding: 3px;}" +
+            "span.listItem {" +
+            "       font-size: 11px;" +
+            "       font-weight: normal;" +
+            "       text-transform: uppercase;" +
+            "       padding: 3px;" +
+            "       -webkit-border-radius: 4px;}" +
+            "span." + AbstractResult.ResultCode.RESULTS_DIFFER.name() + "{" +
+            "       background-color: #ccc;" +
+            "       color: black;}" +
+            "span." + AbstractResult.ResultCode.NO_EXPECTED_RESULT.name() + "{" +
+            "       background-color: #a700e4;" +
+            "       color: #fff;}" +
+            "span.timed_out {" +
+            "       background-color: #f3cb00;" +
+            "       color: black;}" +
+            "span.crashed {" +
+            "       background-color: #c30000;" +
+            "       color: #fff;}" +
+            "span.noLtc {" +
+            "       background-color: #944000;" +
+            "       color: #fff;}" +
+            "span.noEventSender {" +
+            "       background-color: #815600;" +
+            "       color: #fff;}" +
+            "</style>";
+
+    private static final String SCRIPT =
+            "<script type=\"text/javascript\">" +
+            "    function toggleDisplay(id) {" +
+            "        element = document.getElementById(id);" +
+            "        triangle = document.getElementById('tri.' + id);" +
+            "        if (element.style.display == 'none') {" +
+            "            element.style.display = 'inline';" +
+            "            triangle.innerHTML = '&#x25bc; ';" +
+            "        } else {" +
+            "            element.style.display = 'none';" +
+            "            triangle.innerHTML = '&#x25b6; ';" +
+            "        }" +
+            "    }" +
+            "</script>";
+
+    /** TODO: Make it a setting */
+    private static final String HTML_DETAILS_RELATIVE_PATH = "details.html";
+    private static final String TXT_SUMMARY_RELATIVE_PATH = "summary.txt";
+
+    private static final int RESULTS_PER_DUMP = 500;
+    private static final int RESULTS_PER_DB_ACCESS = 50;
+
+    private int mCrashedTestsCount = 0;
+    private List<AbstractResult> mUnexpectedFailures = new ArrayList<AbstractResult>();
+    private List<AbstractResult> mExpectedFailures = new ArrayList<AbstractResult>();
+    private List<AbstractResult> mExpectedPasses = new ArrayList<AbstractResult>();
+    private List<AbstractResult> mUnexpectedPasses = new ArrayList<AbstractResult>();
+
+    private Cursor mUnexpectedFailuresCursor;
+    private Cursor mExpectedFailuresCursor;
+    private Cursor mUnexpectedPassesCursor;
+    private Cursor mExpectedPassesCursor;
+
+    private FileFilter mFileFilter;
+    private String mResultsRootDirPath;
+    private String mTestsRelativePath;
+    private Date mDate;
+
+    private int mResultsSinceLastHtmlDump = 0;
+    private int mResultsSinceLastDbAccess = 0;
+
+    private SummarizerDBHelper mDbHelper;
+
+    public Summarizer(FileFilter fileFilter, String resultsRootDirPath, Context context) {
+        mFileFilter = fileFilter;
+        mResultsRootDirPath = resultsRootDirPath;
+
+        /**
+         * We don't run the database I/O in a separate thread to avoid consumer/producer problem
+         * and to simplify code.
+         */
+        mDbHelper = new SummarizerDBHelper(context);
+        mDbHelper.open();
+    }
+
+    public static URI getDetailsUri() {
+        return new File(ManagerService.RESULTS_ROOT_DIR_PATH + File.separator +
+                HTML_DETAILS_RELATIVE_PATH).toURI();
+    }
+
+    public void appendTest(AbstractResult result) {
+        String relativePath = result.getRelativePath();
+
+        if (result.didCrash()) {
+            mCrashedTestsCount++;
+        }
+
+        if (result.didPass()) {
+            result.clearResults();
+            if (mFileFilter.isFail(relativePath)) {
+                mUnexpectedPasses.add(result);
+            } else {
+                mExpectedPasses.add(result);
+            }
+        } else {
+            if (mFileFilter.isFail(relativePath)) {
+                mExpectedFailures.add(result);
+            } else {
+                mUnexpectedFailures.add(result);
+            }
+        }
+
+        if (++mResultsSinceLastDbAccess == RESULTS_PER_DB_ACCESS) {
+            persistLists();
+            clearLists();
+        }
+    }
+
+    private void clearLists() {
+        mUnexpectedFailures.clear();
+        mExpectedFailures.clear();
+        mUnexpectedPasses.clear();
+        mExpectedPasses.clear();
+    }
+
+    private void persistLists() {
+        persistListToTable(mUnexpectedFailures, SummarizerDBHelper.UNEXPECTED_FAILURES_TABLE);
+        persistListToTable(mExpectedFailures, SummarizerDBHelper.EXPECTED_FAILURES_TABLE);
+        persistListToTable(mUnexpectedPasses, SummarizerDBHelper.UNEXPECTED_PASSES_TABLE);
+        persistListToTable(mExpectedPasses, SummarizerDBHelper.EXPECTED_PASSES_TABLE);
+        mResultsSinceLastDbAccess = 0;
+    }
+
+    private void persistListToTable(List<AbstractResult> results, String table) {
+        for (AbstractResult abstractResult : results) {
+            mDbHelper.insertAbstractResult(abstractResult, table);
+        }
+    }
+
+    public void setTestsRelativePath(String testsRelativePath) {
+        mTestsRelativePath = testsRelativePath;
+    }
+
+    public void summarize(Message onFinishMessage) {
+        persistLists();
+        clearLists();
+
+        mUnexpectedFailuresCursor =
+            mDbHelper.getAbstractResults(SummarizerDBHelper.UNEXPECTED_FAILURES_TABLE);
+        mUnexpectedPassesCursor =
+            mDbHelper.getAbstractResults(SummarizerDBHelper.UNEXPECTED_PASSES_TABLE);
+        mExpectedFailuresCursor =
+            mDbHelper.getAbstractResults(SummarizerDBHelper.EXPECTED_FAILURES_TABLE);
+        mExpectedPassesCursor =
+            mDbHelper.getAbstractResults(SummarizerDBHelper.EXPECTED_PASSES_TABLE);
+
+        String webKitRevision = getWebKitRevision();
+        createHtmlDetails(webKitRevision);
+        createTxtSummary(webKitRevision);
+
+        clearLists();
+        mUnexpectedFailuresCursor.close();
+        mUnexpectedPassesCursor.close();
+        mExpectedFailuresCursor.close();
+        mExpectedPassesCursor.close();
+
+        onFinishMessage.sendToTarget();
+    }
+
+    public void reset() {
+        mCrashedTestsCount = 0;
+        clearLists();
+        mDbHelper.reset();
+        mDate = new Date();
+    }
+
+    private void dumpHtmlToFile(StringBuilder html, boolean append) {
+        FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_DETAILS_RELATIVE_PATH),
+                html.toString().getBytes(), append);
+        html.setLength(0);
+        mResultsSinceLastHtmlDump = 0;
+    }
+
+    private void createTxtSummary(String webKitRevision) {
+        StringBuilder txt = new StringBuilder();
+
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+        txt.append("Path: " + mTestsRelativePath + "\n");
+        txt.append("Date: " + dateFormat.format(mDate) + "\n");
+        txt.append("Build fingerprint: " + Build.FINGERPRINT + "\n");
+        txt.append("WebKit version: " + getWebKitVersionFromUserAgentString() + "\n");
+        txt.append("WebKit revision: " + webKitRevision + "\n");
+
+        txt.append("TOTAL:                     " + getTotalTestCount() + "\n");
+        txt.append("CRASHED (among all tests): " + mCrashedTestsCount + "\n");
+        txt.append("UNEXPECTED FAILURES:       " + mUnexpectedFailuresCursor.getCount() + "\n");
+        txt.append("UNEXPECTED PASSES:         " + mUnexpectedPassesCursor.getCount() + "\n");
+        txt.append("EXPECTED FAILURES:         " + mExpectedFailuresCursor.getCount() + "\n");
+        txt.append("EXPECTED PASSES:           " + mExpectedPassesCursor.getCount() + "\n");
+
+        FsUtils.writeDataToStorage(new File(mResultsRootDirPath, TXT_SUMMARY_RELATIVE_PATH),
+                txt.toString().getBytes(), false);
+    }
+
+    private void createHtmlDetails(String webKitRevision) {
+        StringBuilder html = new StringBuilder();
+
+        html.append("<html><head>");
+        html.append(CSS);
+        html.append(SCRIPT);
+        html.append("</head><body>");
+
+        createTopSummaryTable(webKitRevision, html);
+        dumpHtmlToFile(html, false);
+
+        createResultsList(html, "Unexpected failures", mUnexpectedFailuresCursor);
+        createResultsList(html, "Unexpected passes", mUnexpectedPassesCursor);
+        createResultsList(html, "Expected failures", mExpectedFailuresCursor);
+        createResultsList(html, "Expected passes", mExpectedPassesCursor);
+
+        html.append("</body></html>");
+        dumpHtmlToFile(html, true);
+    }
+
+    private int getTotalTestCount() {
+        return mUnexpectedFailuresCursor.getCount() +
+                mUnexpectedPassesCursor.getCount() +
+                mExpectedPassesCursor.getCount() +
+                mExpectedFailuresCursor.getCount();
+    }
+
+    private String getWebKitVersionFromUserAgentString() {
+        Resources resources = new Resources(new AssetManager(), new DisplayMetrics(),
+                new Configuration());
+        String userAgent =
+                resources.getString(com.android.internal.R.string.web_user_agent);
+
+        Matcher matcher = Pattern.compile("AppleWebKit/([0-9]+?\\.[0-9])").matcher(userAgent);
+        if (matcher.find()) {
+            return matcher.group(1);
+        }
+        return "unknown";
+    }
+
+    private String getWebKitRevision() {
+        URL url = null;
+        try {
+            url = new URL(ForwarderManager.getHostSchemePort(false) + "ThirdPartyProject.prop");
+        } catch (MalformedURLException e) {
+            assert false;
+        }
+
+        String thirdPartyProjectContents = new String(FsUtils.readDataFromUrl(url));
+        Matcher matcher = Pattern.compile("^version=([0-9]+)", Pattern.MULTILINE).matcher(
+                thirdPartyProjectContents);
+        if (matcher.find()) {
+            return matcher.group(1);
+        }
+        return "unknown";
+    }
+
+    private void createTopSummaryTable(String webKitRevision, StringBuilder html) {
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+        html.append("<h1>" + "Layout tests' results for: " +
+                (mTestsRelativePath.equals("") ? "all tests" : mTestsRelativePath) + "</h1>");
+        html.append("<h3>" + "Date: " + dateFormat.format(new Date()) + "</h3>");
+        html.append("<h3>" + "Build fingerprint: " + Build.FINGERPRINT + "</h3>");
+        html.append("<h3>" + "WebKit version: " + getWebKitVersionFromUserAgentString() + "</h3>");
+
+        html.append("<h3>" + "WebKit revision: ");
+        html.append("<a href=\"http://trac.webkit.org/browser/trunk?rev=" + webKitRevision +
+                "\" target=\"_blank\"><span class=\"path\">" + webKitRevision + "</span></a>");
+        html.append("</h3>");
+
+        html.append("<table class=\"summary\">");
+        createSummaryTableRow(html, "TOTAL", getTotalTestCount());
+        createSummaryTableRow(html, "CRASHED (among all tests)", mCrashedTestsCount);
+        createSummaryTableRow(html, "UNEXPECTED FAILURES", mUnexpectedFailuresCursor.getCount());
+        createSummaryTableRow(html, "UNEXPECTED PASSES", mUnexpectedPassesCursor.getCount());
+        createSummaryTableRow(html, "EXPECTED FAILURES", mExpectedFailuresCursor.getCount());
+        createSummaryTableRow(html, "EXPECTED PASSES", mExpectedPassesCursor.getCount());
+        html.append("</table>");
+    }
+
+    private void createSummaryTableRow(StringBuilder html, String caption, int size) {
+        html.append("<tr>");
+        html.append("    <td>" + caption + "</td>");
+        html.append("    <td>" + size + "</td>");
+        html.append("</tr>");
+    }
+
+    private void createResultsList(
+            StringBuilder html, String title, Cursor cursor) {
+        String relativePath;
+        String id = "";
+        AbstractResult.ResultCode resultCode;
+
+        html.append("<h2>" + title + " [" + cursor.getCount() + "]</h2>");
+
+        if (!cursor.moveToFirst()) {
+            return;
+        }
+
+        AbstractResult result;
+        do {
+            result = SummarizerDBHelper.getAbstractResult(cursor);
+
+            relativePath = result.getRelativePath();
+            resultCode = result.getResultCode();
+
+            html.append("<h3>");
+
+            /**
+             * Technically, two different paths could end up being the same, because
+             * ':' is a valid  character in a path. However, it is probably not going
+             * to cause any problems in this case
+             */
+            id = relativePath.replace(File.separator, ":");
+
+            /** Write the test name */
+            if (resultCode == AbstractResult.ResultCode.RESULTS_DIFFER) {
+                html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');");
+                html.append("return false;\">");
+                html.append("<span class=\"tri\" id=\"tri." + id + "\">&#x25b6; </span>");
+                html.append("<span class=\"path\">" + relativePath + "</span>");
+                html.append("</a>");
+            } else {
+                html.append("<a href=\"" + getViewSourceUrl(result.getRelativePath()).toString() + "\"");
+                html.append(" target=\"_blank\">");
+                html.append("<span class=\"sqr sqr_" + (result.didPass() ? "pass" : "fail"));
+                html.append("\">&#x25a0; </span>");
+                html.append("<span class=\"path\">" + result.getRelativePath() + "</span>");
+                html.append("</a>");
+            }
+
+            if (!result.didPass()) {
+                appendTags(html, result);
+            }
+
+            html.append("</h3>");
+            appendExpectedResultsSources(result, html);
+
+            if (resultCode == AbstractResult.ResultCode.RESULTS_DIFFER) {
+                html.append("<div class=\"diff\" style=\"display: none;\" id=\"" + id + "\">");
+                html.append(result.getDiffAsHtml());
+                html.append("<a href=\"#\" onClick=\"toggleDisplay('" + id + "');");
+                html.append("return false;\">Hide</a>");
+                html.append(" | ");
+                html.append("<a href=\"" + getViewSourceUrl(relativePath).toString() + "\"");
+                html.append(" target=\"_blank\">Show source</a>");
+                html.append("</div>");
+            }
+
+            html.append("<div class=\"space\"></div>");
+
+            if (++mResultsSinceLastHtmlDump == RESULTS_PER_DUMP) {
+                dumpHtmlToFile(html, true);
+            }
+
+            cursor.moveToNext();
+        } while (!cursor.isAfterLast());
+    }
+
+    private void appendTags(StringBuilder html, AbstractResult result) {
+        /** Tag tests which crash, time out or where results don't match */
+        if (result.didCrash()) {
+            html.append(" <span class=\"listItem crashed\">Crashed</span>");
+        } else {
+            if (result.didTimeOut()) {
+                html.append(" <span class=\"listItem timed_out\">Timed out</span>");
+            }
+            AbstractResult.ResultCode resultCode = result.getResultCode();
+            if (resultCode != AbstractResult.ResultCode.RESULTS_MATCH) {
+                html.append(" <span class=\"listItem " + resultCode.name() + "\">");
+                html.append(resultCode.toString());
+                html.append("</span>");
+            }
+        }
+
+        /** Detect missing LTC function */
+        String additionalTextOutputString = result.getAdditionalTextOutputString();
+        if (additionalTextOutputString != null &&
+                additionalTextOutputString.contains("com.android.dumprendertree") &&
+                additionalTextOutputString.contains("has no method")) {
+            if (additionalTextOutputString.contains("LayoutTestController")) {
+                html.append(" <span class=\"listItem noLtc\">LTC function missing</span>");
+            }
+            if (additionalTextOutputString.contains("EventSender")) {
+                html.append(" <span class=\"listItem noEventSender\">");
+                html.append("ES function missing</span>");
+            }
+        }
+    }
+
+    private static final void appendExpectedResultsSources(AbstractResult result,
+            StringBuilder html) {
+        String textSource = result.getExpectedTextResultPath();
+        String imageSource = result.getExpectedImageResultPath();
+
+        if (result.didCrash()) {
+            html.append("<span class=\"source\">Did not look for expected results</span>");
+            return;
+        }
+
+        if (textSource == null) {
+            // Show if a text result is missing. We may want to revisit this decision when we add
+            // support for image results.
+            html.append("<span class=\"source\">Expected textual result missing</span>");
+        } else {
+            html.append("<span class=\"source\">Expected textual result from: ");
+            html.append("<a href=\"" + ForwarderManager.getHostSchemePort(false) + "LayoutTests/" +
+                    textSource + "\"");
+            html.append(" target=\"_blank\">");
+            html.append(textSource + "</a></span>");
+        }
+        if (imageSource != null) {
+            html.append("<span class=\"source\">Expected image result from: ");
+            html.append("<a href=\"" + ForwarderManager.getHostSchemePort(false) + "LayoutTests/" +
+                    imageSource + "\"");
+            html.append(" target=\"_blank\">");
+            html.append(imageSource + "</a></span>");
+        }
+    }
+
+    private static final URL getViewSourceUrl(String relativePath) {
+        URL url = null;
+        try {
+            url = new URL("http", "localhost", ForwarderManager.HTTP_PORT,
+                    "/WebKitTools/DumpRenderTree/android/view_source.php?src=" +
+                    relativePath);
+        } catch (MalformedURLException e) {
+            assert false : "relativePath=" + relativePath;
+        }
+        return url;
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/SummarizerDBHelper.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/SummarizerDBHelper.java
new file mode 100644
index 0000000..23e13ec
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/SummarizerDBHelper.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A basic class that wraps database accesses inside itself and provides functionality to
+ * store and retrieve AbstractResults.
+ */
+public class SummarizerDBHelper {
+    private static final String KEY_ID = "id";
+    private static final String KEY_PATH = "path";
+    private static final String KEY_BYTES = "bytes";
+
+    private static final String DATABASE_NAME = "SummarizerDB";
+    private static final int DATABASE_VERSION = 1;
+
+    static final String EXPECTED_FAILURES_TABLE = "expectedFailures";
+    static final String UNEXPECTED_FAILURES_TABLE = "unexpectedFailures";
+    static final String EXPECTED_PASSES_TABLE = "expextedPasses";
+    static final String UNEXPECTED_PASSES_TABLE = "unexpextedPasses";
+    private static final Set<String> TABLES_NAMES = new HashSet<String>();
+    {
+        TABLES_NAMES.add(EXPECTED_FAILURES_TABLE);
+        TABLES_NAMES.add(EXPECTED_PASSES_TABLE);
+        TABLES_NAMES.add(UNEXPECTED_FAILURES_TABLE);
+        TABLES_NAMES.add(UNEXPECTED_PASSES_TABLE);
+    }
+
+    private static final void createTables(SQLiteDatabase db) {
+        String cmd;
+        for (String tableName : TABLES_NAMES) {
+            cmd = "create table " + tableName + " ("
+                    + KEY_ID + " integer primary key autoincrement, "
+                    + KEY_PATH + " text not null, "
+                    + KEY_BYTES + " blob not null);";
+            db.execSQL(cmd);
+        }
+    }
+
+    private static final void dropTables(SQLiteDatabase db) {
+        for (String tableName : TABLES_NAMES) {
+            db.execSQL("DROP TABLE IF EXISTS " + tableName);
+        }
+    }
+
+    private static class DatabaseHelper extends SQLiteOpenHelper {
+        DatabaseHelper(Context context) {
+            super(context, DATABASE_NAME, null, DATABASE_VERSION);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            dropTables(db);
+            createTables(db);
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            /** NOOP for now, because we will never upgrade the db */
+        }
+
+        public void reset(SQLiteDatabase db) {
+            dropTables(db);
+            createTables(db);
+        }
+    }
+
+    private DatabaseHelper mDbHelper;
+    private SQLiteDatabase mDb;
+
+    private final Context mContext;
+
+    public SummarizerDBHelper(Context ctx) {
+        mContext = ctx;
+        mDbHelper = new DatabaseHelper(mContext);
+    }
+
+    public void reset() {
+        mDbHelper.reset(this.mDb);
+    }
+
+    public void open() throws SQLException {
+        mDb = mDbHelper.getWritableDatabase();
+    }
+
+    public void close() {
+        mDbHelper.close();
+    }
+
+    public void insertAbstractResult(AbstractResult result, String table) {
+        ContentValues cv = new ContentValues();
+        cv.put(KEY_PATH, result.getRelativePath());
+        cv.put(KEY_BYTES, result.getBytes());
+        mDb.insert(table, null, cv);
+    }
+
+    public Cursor getAbstractResults(String table) throws SQLException {
+        return mDb.query(false, table, new String[] {KEY_BYTES}, null, null, null, null,
+                KEY_PATH + " ASC", null);
+    }
+
+    public static AbstractResult getAbstractResult(Cursor cursor) {
+        return AbstractResult.create(cursor.getBlob(cursor.getColumnIndex(KEY_BYTES)));
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
new file mode 100644
index 0000000..9db4d2b
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.Gravity;
+import android.view.Window;
+import android.webkit.WebView;
+import android.widget.Toast;
+
+import com.android.dumprendertree2.scriptsupport.OnEverythingFinishedCallback;
+
+import java.util.ArrayList;
+
+/**
+ * An Activity that generates a list of tests and sends the intent to
+ * LayoutTestsExecuter to run them. It also restarts the LayoutTestsExecuter
+ * after it crashes.
+ */
+public class TestsListActivity extends Activity {
+
+    private static final int MSG_TEST_LIST_PRELOADER_DONE = 0;
+
+    /** Constants for adding extras to an intent */
+    public static final String EXTRA_TEST_PATH = "TestPath";
+
+    private static ProgressDialog sProgressDialog;
+
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_TEST_LIST_PRELOADER_DONE:
+                    sProgressDialog.dismiss();
+                    mTestsList = (ArrayList<String>)msg.obj;
+                    mTotalTestCount = mTestsList.size();
+                    restartExecutor(0);
+                    break;
+            }
+        }
+    };
+
+    private ArrayList<String> mTestsList;
+    private int mTotalTestCount;
+
+    private OnEverythingFinishedCallback mOnEverythingFinishedCallback;
+    private boolean mEverythingFinished;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        /** Prepare the progress dialog */
+        sProgressDialog = new ProgressDialog(TestsListActivity.this);
+        sProgressDialog.setCancelable(false);
+        sProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+        sProgressDialog.setTitle(R.string.dialog_progress_title);
+        sProgressDialog.setMessage(getText(R.string.dialog_progress_msg));
+
+        requestWindowFeature(Window.FEATURE_PROGRESS);
+
+        Intent intent = getIntent();
+        if (!intent.getAction().equals(Intent.ACTION_RUN)) {
+            return;
+        }
+        String path = intent.getStringExtra(EXTRA_TEST_PATH);
+
+        sProgressDialog.show();
+        Message doneMsg = Message.obtain(mHandler, MSG_TEST_LIST_PRELOADER_DONE);
+
+        Intent serviceIntent = new Intent(this, ManagerService.class);
+        serviceIntent.putExtra("path", path);
+        startService(serviceIntent);
+
+        new TestsListPreloaderThread(path, doneMsg).start();
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        if (intent.getAction().equals(Intent.ACTION_REBOOT)) {
+            onCrashIntent(intent);
+        } else if (intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
+            onEverythingFinishedIntent(intent);
+        }
+    }
+
+    /**
+     * This method handles an intent that comes from ManageService when crash is detected.
+     * The intent contains an index in mTestsList of the test that crashed. TestsListActivity
+     * restarts the LayoutTestsExecutor from the following test in mTestsList, by sending
+     * an intent to it. This new intent contains a list of remaining tests to run,
+     * total count of all tests, and the index of the first test to run after restarting.
+     * LayoutTestExecutor runs then as usual, sending reports to ManagerService. If it
+     * detects the crash it sends a new intent and the flow repeats.
+     */
+    private void onCrashIntent(Intent intent) {
+        int nextTestToRun = intent.getIntExtra("crashedTestIndex", -1) + 1;
+        if (nextTestToRun > 0 && nextTestToRun <= mTotalTestCount) {
+            restartExecutor(nextTestToRun);
+        }
+    }
+
+    public void registerOnEverythingFinishedCallback(OnEverythingFinishedCallback callback) {
+        mOnEverythingFinishedCallback = callback;
+        if (mEverythingFinished) {
+            mOnEverythingFinishedCallback.onFinished();
+        }
+    }
+
+    private void onEverythingFinishedIntent(Intent intent) {
+        Toast toast = Toast.makeText(this,
+                "All tests finished.\nPress back key to return to the tests' list.",
+                Toast.LENGTH_LONG);
+        toast.setGravity(Gravity.CENTER, -40, 0);
+        toast.show();
+
+        /** Show the details to the user */
+        WebView webView = new WebView(this);
+        webView.getSettings().setJavaScriptEnabled(true);
+        webView.getSettings().setBuiltInZoomControls(true);
+        webView.getSettings().setEnableSmoothTransition(true);
+        /** This enables double-tap to zoom */
+        webView.getSettings().setUseWideViewPort(true);
+
+        setContentView(webView);
+        webView.loadUrl(Summarizer.getDetailsUri().toString());
+
+        mEverythingFinished = true;
+        if (mOnEverythingFinishedCallback != null) {
+            mOnEverythingFinishedCallback.onFinished();
+        }
+    }
+
+    /**
+     * This, together with android:configChanges="orientation" in manifest file, prevents
+     * the activity from restarting on orientation change.
+     */
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        outState.putStringArrayList("testsList", mTestsList);
+        outState.putInt("totalCount", mTotalTestCount);
+
+        super.onSaveInstanceState(outState);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+
+        mTestsList = savedInstanceState.getStringArrayList("testsList");
+        mTotalTestCount = savedInstanceState.getInt("totalCount");
+    }
+
+    /**
+     * (Re)starts the executer activity from the given test number (inclusive, 0-based).
+     * This number is an index in mTestsList, not the sublist passed in the intent.
+     *
+     * @param startFrom
+     *      test index in mTestsList to start the tests from (inclusive, 0-based)
+     */
+    private void restartExecutor(int startFrom) {
+        Intent intent = new Intent();
+        intent.setClass(this, LayoutTestsExecutor.class);
+        intent.setAction(Intent.ACTION_RUN);
+
+        if (startFrom < mTotalTestCount) {
+            intent.putStringArrayListExtra(LayoutTestsExecutor.EXTRA_TESTS_LIST,
+                    new ArrayList<String>(mTestsList.subList(startFrom, mTotalTestCount)));
+            intent.putExtra(LayoutTestsExecutor.EXTRA_TEST_INDEX, startFrom);
+        } else {
+            intent.putStringArrayListExtra(LayoutTestsExecutor.EXTRA_TESTS_LIST,
+                    new ArrayList<String>());
+        }
+
+        startActivity(intent);
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
new file mode 100644
index 0000000..0e7d47a
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.os.Environment;
+import android.os.Message;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A Thread that is responsible for generating a lists of tests to run.
+ */
+public class TestsListPreloaderThread extends Thread {
+
+    private static final String LOG_TAG = "TestsListPreloaderThread";
+
+    /** A list containing relative paths of tests to run */
+    private ArrayList<String> mTestsList = new ArrayList<String>();
+
+    private FileFilter mFileFilter;
+
+    /**
+     * A relative path to the directory with the tests we want to run or particular test.
+     * Used up to and including preloadTests().
+     */
+    private String mRelativePath;
+
+    private Message mDoneMsg;
+
+    /**
+     * The given path must be relative to the root dir.
+     *
+     * @param path
+     * @param doneMsg
+     */
+    public TestsListPreloaderThread(String path, Message doneMsg) {
+        mFileFilter = new FileFilter();
+        mRelativePath = path;
+        mDoneMsg = doneMsg;
+    }
+
+    @Override
+    public void run() {
+        if (FileFilter.isTestFile(mRelativePath)) {
+            mTestsList.add(mRelativePath);
+        } else {
+            loadTestsFromUrl(mRelativePath);
+        }
+
+        mDoneMsg.obj = mTestsList;
+        mDoneMsg.sendToTarget();
+    }
+
+    /**
+     * Loads all the tests from the given directories and all the subdirectories
+     * into mTestsList.
+     *
+     * @param dirRelativePath
+     */
+    private void loadTestsFromUrl(String rootRelativePath) {
+        LinkedList<String> directoriesList = new LinkedList<String>();
+        directoriesList.add(rootRelativePath);
+
+        String relativePath;
+        String itemName;
+        while (!directoriesList.isEmpty()) {
+            relativePath = directoriesList.removeFirst();
+
+            List<String> dirRelativePaths = FsUtils.getLayoutTestsDirContents(relativePath, false, true);
+            if (dirRelativePaths != null) {
+                for (String dirRelativePath : dirRelativePaths) {
+                    itemName = new File(dirRelativePath).getName();
+                    if (FileFilter.isTestDir(itemName)) {
+                        directoriesList.add(dirRelativePath);
+                    }
+                }
+            }
+
+            List<String> testRelativePaths = FsUtils.getLayoutTestsDirContents(relativePath, false, false);
+            if (testRelativePaths != null) {
+                for (String testRelativePath : testRelativePaths) {
+                    itemName = new File(testRelativePath).getName();
+                    if (FileFilter.isTestFile(itemName)) {
+                        /** We choose to skip all the tests that are expected to crash. */
+                        if (!mFileFilter.isCrash(testRelativePath)) {
+                            mTestsList.add(testRelativePath);
+                        } else {
+                            /**
+                             * TODO: Summarizer is now in service - figure out how to send the info.
+                             * Previously: mSummarizer.addSkippedTest(relativePath);
+                             */
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
new file mode 100644
index 0000000..3d2b98b
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TextResult.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.webkit.WebView;
+
+import name.fraser.neil.plaintext.diff_match_patch;
+
+import java.util.LinkedList;
+
+/**
+ * A result object for which the expected output is text. It does not have an image
+ * expected result.
+ *
+ * <p>Created if layoutTestController.dumpAsText() was called.
+ */
+public class TextResult extends AbstractResult {
+
+    private static final int MSG_DOCUMENT_AS_TEXT = 0;
+
+    private String mExpectedResult;
+    private String mExpectedResultPath;
+    private String mActualResult;
+    private String mRelativePath;
+    private boolean mDidTimeOut;
+    private ResultCode mResultCode;
+    transient private Message mResultObtainedMsg;
+
+    private boolean mDumpChildFramesAsText;
+
+    transient private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_DOCUMENT_AS_TEXT) {
+                mActualResult = (String)msg.obj;
+                mResultObtainedMsg.sendToTarget();
+            }
+        }
+    };
+
+    public TextResult(String relativePath) {
+        mRelativePath = relativePath;
+    }
+
+    public void setDumpChildFramesAsText(boolean dumpChildFramesAsText) {
+        mDumpChildFramesAsText = dumpChildFramesAsText;
+    }
+
+    /**
+     * Used to recreate the Result when received by the service.
+     *
+     * @param bundle
+     *      bundle with data used to recreate the result
+     */
+    public TextResult(Bundle bundle) {
+        mExpectedResult = bundle.getString("expectedTextualResult");
+        mExpectedResultPath = bundle.getString("expectedTextualResultPath");
+        mActualResult = bundle.getString("actualTextualResult");
+        setAdditionalTextOutputString(bundle.getString("additionalTextOutputString"));
+        mRelativePath = bundle.getString("relativePath");
+        mDidTimeOut = bundle.getBoolean("didTimeOut");
+    }
+
+    @Override
+    public void clearResults() {
+        super.clearResults();
+        mExpectedResult = null;
+        mActualResult = null;
+    }
+
+    @Override
+    public ResultCode getResultCode() {
+        if (mResultCode == null) {
+            mResultCode = resultsMatch() ? AbstractResult.ResultCode.RESULTS_MATCH
+                    : AbstractResult.ResultCode.RESULTS_DIFFER;
+        }
+        return mResultCode;
+    }
+
+    private boolean resultsMatch() {
+        assert mExpectedResult != null;
+        assert mActualResult != null;
+        // Trim leading and trailing empty lines, as other WebKit platforms do.
+        String leadingEmptyLines = "^\\n+";
+        String trailingEmptyLines = "\\n+$";
+        String trimmedExpectedResult = mExpectedResult.replaceFirst(leadingEmptyLines, "")
+                .replaceFirst(trailingEmptyLines, "");
+        String trimmedActualResult = mActualResult.replaceFirst(leadingEmptyLines, "")
+                .replaceFirst(trailingEmptyLines, "");
+        return trimmedExpectedResult.equals(trimmedActualResult);
+    }
+
+    @Override
+    public boolean didCrash() {
+        return false;
+    }
+
+    @Override
+    public boolean didTimeOut() {
+        return mDidTimeOut;
+    }
+
+    @Override
+    public void setDidTimeOut() {
+        mDidTimeOut = true;
+    }
+
+    @Override
+    public byte[] getActualImageResult() {
+        return null;
+    }
+
+    @Override
+    public String getActualTextResult() {
+        String additionalTextResultString = getAdditionalTextOutputString();
+        if (additionalTextResultString != null) {
+            return additionalTextResultString + mActualResult;
+        }
+
+        return mActualResult;
+    }
+
+    @Override
+    public void setExpectedImageResult(byte[] expectedResult) {
+        /** This method is not applicable to this type of result */
+    }
+
+    @Override
+    public void setExpectedImageResultPath(String relativePath) {
+        /** This method is not applicable to this type of result */
+    }
+
+    @Override
+    public String getExpectedImageResultPath() {
+        /** This method is not applicable to this type of result */
+        return null;
+    }
+
+    @Override
+    public void setExpectedTextResultPath(String relativePath) {
+        mExpectedResultPath = relativePath;
+    }
+
+    @Override
+    public String getExpectedTextResultPath() {
+        return mExpectedResultPath;
+    }
+
+    @Override
+    public void setExpectedTextResult(String expectedResult) {
+        // For text results, we use an empty string for the expected result when none is
+        // present, as other WebKit platforms do.
+        mExpectedResult = expectedResult == null ? "" : expectedResult;
+    }
+
+    @Override
+    public String getDiffAsHtml() {
+        assert mExpectedResult != null;
+        assert mActualResult != null;
+
+        StringBuilder html = new StringBuilder();
+        html.append("<table class=\"visual_diff\">");
+        html.append("    <tr class=\"headers\">");
+        html.append("        <td colspan=\"2\">Expected result:</td>");
+        html.append("        <td class=\"space\"></td>");
+        html.append("        <td colspan=\"2\">Actual result:</td>");
+        html.append("    </tr>");
+
+        appendDiffHtml(html);
+
+        html.append("    <tr class=\"footers\">");
+        html.append("        <td colspan=\"2\"></td>");
+        html.append("        <td class=\"space\"></td>");
+        html.append("        <td colspan=\"2\"></td>");
+        html.append("    </tr>");
+        html.append("</table>");
+
+        return html.toString();
+    }
+
+    private void appendDiffHtml(StringBuilder html) {
+        LinkedList<diff_match_patch.Diff> diffs =
+                new diff_match_patch().diff_main(mExpectedResult, mActualResult);
+
+        diffs = VisualDiffUtils.splitDiffsOnNewline(diffs);
+
+        LinkedList<String> expectedLines = new LinkedList<String>();
+        LinkedList<Integer> expectedLineNums = new LinkedList<Integer>();
+        LinkedList<String> actualLines = new LinkedList<String>();
+        LinkedList<Integer> actualLineNums = new LinkedList<Integer>();
+
+        VisualDiffUtils.generateExpectedResultLines(diffs, expectedLineNums, expectedLines);
+        VisualDiffUtils.generateActualResultLines(diffs, actualLineNums, actualLines);
+        // TODO: We should use a map for each line number and lines pair.
+        assert expectedLines.size() == expectedLineNums.size();
+        assert actualLines.size() == actualLineNums.size();
+        assert expectedLines.size() == actualLines.size();
+
+        html.append(VisualDiffUtils.getHtml(expectedLineNums, expectedLines,
+                actualLineNums, actualLines));
+    }
+
+    @Override
+    public TestType getType() {
+        return TestType.TEXT;
+    }
+
+    @Override
+    public void obtainActualResults(WebView webview, Message resultObtainedMsg) {
+        mResultObtainedMsg = resultObtainedMsg;
+        Message msg = mHandler.obtainMessage(MSG_DOCUMENT_AS_TEXT);
+
+        /**
+         * arg1 - should dump top frame as text
+         * arg2 - should dump child frames as text
+         */
+        msg.arg1 = 1;
+        msg.arg2 = mDumpChildFramesAsText ? 1 : 0;
+        webview.documentAsText(msg);
+    }
+
+    @Override
+    public Bundle getBundle() {
+        Bundle bundle = new Bundle();
+        bundle.putString("expectedTextualResult", mExpectedResult);
+        bundle.putString("expectedTextualResultPath", mExpectedResultPath);
+        bundle.putString("actualTextualResult", getActualTextResult());
+        bundle.putString("additionalTextOutputString", getAdditionalTextOutputString());
+        bundle.putString("relativePath", mRelativePath);
+        bundle.putBoolean("didTimeOut", mDidTimeOut);
+        bundle.putString("type", getType().name());
+        return bundle;
+    }
+
+    @Override
+    public String getRelativePath() {
+        return mRelativePath;
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
new file mode 100644
index 0000000..d7f7313
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/VisualDiffUtils.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import name.fraser.neil.plaintext.diff_match_patch;
+
+import java.util.LinkedList;
+
+/**
+ * Helper methods fo TextResult.getDiffAsHtml()
+ */
+public class VisualDiffUtils {
+
+    private static final int DONT_PRINT_LINE_NUMBER = -1;
+
+    /**
+     * Preprocesses the list of diffs so that new line characters appear only at the end of
+     * diff.text
+     *
+     * @param diffs
+     * @return
+     *      LinkedList of diffs where new line character appears only on the end of
+     *      diff.text
+     */
+    public static LinkedList<diff_match_patch.Diff> splitDiffsOnNewline(
+            LinkedList<diff_match_patch.Diff> diffs) {
+        LinkedList<diff_match_patch.Diff> newDiffs = new LinkedList<diff_match_patch.Diff>();
+
+        String[] parts;
+        int lengthMinusOne;
+        for (diff_match_patch.Diff diff : diffs) {
+            parts = diff.text.split("\n", -1);
+            if (parts.length == 1) {
+                newDiffs.add(diff);
+                continue;
+            }
+
+            lengthMinusOne = parts.length - 1;
+            for (int i = 0; i < lengthMinusOne; i++) {
+                newDiffs.add(new diff_match_patch.Diff(diff.operation, parts[i] + "\n"));
+            }
+            if (!parts[lengthMinusOne].isEmpty()) {
+                newDiffs.add(new diff_match_patch.Diff(diff.operation, parts[lengthMinusOne]));
+            }
+        }
+
+        return newDiffs;
+    }
+
+    public static void generateExpectedResultLines(LinkedList<diff_match_patch.Diff> diffs,
+            LinkedList<Integer> lineNums, LinkedList<String> lines) {
+        String delSpan = "<span class=\"del\">";
+        String eqlSpan = "<span class=\"eql\">";
+
+        String line = "";
+        int i = 1;
+        diff_match_patch.Diff diff;
+        int size = diffs.size();
+        boolean isLastDiff;
+        for (int j = 0; j < size; j++) {
+            diff = diffs.get(j);
+            isLastDiff = j == size - 1;
+            switch (diff.operation) {
+                case DELETE:
+                    line = processDiff(diff, lineNums, lines, line, i, delSpan, isLastDiff);
+                    if (line.equals("")) {
+                        i++;
+                    }
+                    break;
+
+                case INSERT:
+                    // If the line is currently empty and this insertion is the entire line, the
+                    // expected line is absent, so it has no line number.
+                    if (diff.text.endsWith("\n") || isLastDiff) {
+                        lineNums.add(line.equals("") ? DONT_PRINT_LINE_NUMBER : i++);
+                        lines.add(line);
+                        line = "";
+                    }
+                    break;
+
+                case EQUAL:
+                    line = processDiff(diff, lineNums, lines, line, i, eqlSpan, isLastDiff);
+                    if (line.equals("")) {
+                        i++;
+                    }
+                    break;
+            }
+        }
+    }
+
+    public static void generateActualResultLines(LinkedList<diff_match_patch.Diff> diffs,
+            LinkedList<Integer> lineNums, LinkedList<String> lines) {
+        String insSpan = "<span class=\"ins\">";
+        String eqlSpan = "<span class=\"eql\">";
+
+        String line = "";
+        int i = 1;
+        diff_match_patch.Diff diff;
+        int size = diffs.size();
+        boolean isLastDiff;
+        for (int j = 0; j < size; j++) {
+            diff = diffs.get(j);
+            isLastDiff = j == size - 1;
+            switch (diff.operation) {
+                case INSERT:
+                    line = processDiff(diff, lineNums, lines, line, i, insSpan, isLastDiff);
+                    if (line.equals("")) {
+                        i++;
+                    }
+                    break;
+
+                case DELETE:
+                    // If the line is currently empty and deletion is the entire line, the
+                    // actual line is absent, so it has no line number.
+                    if (diff.text.endsWith("\n") || isLastDiff) {
+                        lineNums.add(line.equals("") ? DONT_PRINT_LINE_NUMBER : i++);
+                        lines.add(line);
+                        line = "";
+                    }
+                    break;
+
+                case EQUAL:
+                    line = processDiff(diff, lineNums, lines, line, i, eqlSpan, isLastDiff);
+                    if (line.equals("")) {
+                        i++;
+                    }
+                    break;
+            }
+        }
+    }
+
+    /**
+     * Generate or append a line for a given diff and add it to given collections if necessary.
+     * It puts diffs in HTML spans.
+     *
+     * @param diff
+     * @param lineNums
+     * @param lines
+     * @param line
+     * @param i
+     * @param begSpan
+     * @param forceOutputLine Force the current line to be output
+     * @return
+     */
+    public static String processDiff(diff_match_patch.Diff diff, LinkedList<Integer> lineNums,
+            LinkedList<String> lines, String line, int i, String begSpan, boolean forceOutputLine) {
+        String endSpan = "</span>";
+        String br = "&nbsp;";
+
+        if (diff.text.endsWith("\n") || forceOutputLine) {
+            lineNums.add(i);
+            /** TODO: Think of better way to replace stuff */
+            line += begSpan + diff.text.replace("  ", "&nbsp;&nbsp;")
+                    + endSpan + br;
+            lines.add(line);
+            line = "";
+        } else {
+            line += begSpan + diff.text.replace("  ", "&nbsp;&nbsp;") + endSpan;
+        }
+
+        return line;
+    }
+
+    public static String getHtml(LinkedList<Integer> lineNums1, LinkedList<String> lines1,
+            LinkedList<Integer> lineNums2, LinkedList<String> lines2) {
+        StringBuilder html = new StringBuilder();
+        int lineNum;
+        int size = lines1.size();
+        for (int i = 0; i < size; i++) {
+            html.append("<tr class=\"results\">");
+
+            html.append("    <td class=\"line_count\">");
+            lineNum = lineNums1.removeFirst();
+            if (lineNum > 0) {
+                html.append(lineNum);
+            }
+            html.append("    </td>");
+
+            html.append("    <td class=\"line\">");
+            html.append(lines1.removeFirst());
+            html.append("    </td>");
+
+            html.append("    <td class=\"space\"></td>");
+
+            html.append("    <td class=\"line_count\">");
+            lineNum = lineNums2.removeFirst();
+            if (lineNum > 0) {
+                html.append(lineNum);
+            }
+            html.append("    </td>");
+
+            html.append("    <td class=\"line\">");
+            html.append(lines2.removeFirst());
+            html.append("    </td>");
+
+            html.append("</tr>");
+        }
+        return html.toString();
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java
new file mode 100644
index 0000000..224509d
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/AdbUtils.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * The utility class that can setup a socket allowing the device to communicate with remote
+ * machines through the machine that the device is connected to via adb.
+ */
+public class AdbUtils {
+    private static final String LOG_TAG = "AdbUtils";
+
+    private static final String ADB_OK = "OKAY";
+    private static final int ADB_PORT = 5037;
+    private static final String ADB_HOST = "127.0.0.1";
+    private static final int ADB_RESPONSE_SIZE = 4;
+
+    /**
+     * Creates a new socket that can be configured to serve as a transparent proxy to a
+     * remote machine. This can be achieved by calling configureSocket()
+     *
+     * @return a socket that can be configured to link to remote machine
+     * @throws IOException
+     */
+    public static Socket createSocket() throws IOException{
+        return new Socket(ADB_HOST, ADB_PORT);
+    }
+
+    /**
+     * Configures the connection to serve as a transparent proxy to a remote machine.
+     * The given streams must belong to a socket created by createSocket().
+     *
+     * @param inputStream inputStream of the socket we want to configure
+     * @param outputStream outputStream of the socket we want to configure
+     * @param remoteAddress address of the remote machine (as you would type in a browser
+     *      in a machine that the device is connected to via adb)
+     * @param remotePort port on which to connect
+     * @return if the configuration suceeded
+     * @throws IOException
+     */
+    public static boolean configureConnection(InputStream inputStream, OutputStream outputStream,
+            String remoteAddress, int remotePort) throws IOException {
+        String cmd = "tcp:" + remotePort + ":" + remoteAddress;
+        cmd = String.format("%04X", cmd.length()) + cmd;
+
+        byte[] buf = new byte[ADB_RESPONSE_SIZE];
+        outputStream.write(cmd.getBytes());
+        int read = inputStream.read(buf);
+        if (read != ADB_RESPONSE_SIZE || !ADB_OK.equals(new String(buf))) {
+            Log.w(LOG_TAG, "adb cmd failed.");
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java
new file mode 100644
index 0000000..f19cd41
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ConnectionHandler.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.forwarder;
+
+import android.util.Log;
+
+import com.android.dumprendertree2.FsUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * Worker class for {@link Forwarder}. A ConnectionHandler will be created once the Forwarder
+ * accepts an incoming connection, and it will then forward the incoming/outgoing streams to a
+ * connection already proxied by adb networking (see also {@link AdbUtils}).
+ */
+public class ConnectionHandler {
+
+    private static final String LOG_TAG = "ConnectionHandler";
+
+    public static interface OnFinishedCallback {
+        public void onFinished();
+    }
+
+    private class SocketPipeThread extends Thread {
+
+        private InputStream mInputStream;
+        private OutputStream mOutputStream;
+
+        public SocketPipeThread(InputStream inputStream, OutputStream outputStream) {
+            mInputStream = inputStream;
+            mOutputStream = outputStream;
+            setName("SocketPipeThread: " + getName());
+        }
+
+        @Override
+        public void run() {
+            byte[] buffer = new byte[4096];
+            int length;
+            while (true) {
+                try {
+                    if ((length = mInputStream.read(buffer)) < 0) {
+                        break;
+                    }
+                    mOutputStream.write(buffer, 0, length);
+                } catch (IOException e) {
+                    /** This exception means one of the streams is closed */
+                    Log.v(LOG_TAG, this.toString(), e);
+                    break;
+                }
+            }
+
+            synchronized (mThreadsRunning) {
+                mThreadsRunning--;
+                if (mThreadsRunning == 0) {
+                    ConnectionHandler.this.stop();
+                    mOnFinishedCallback.onFinished();
+                }
+            }
+        }
+
+        @Override
+        public String toString() {
+            return getName();
+        }
+    }
+
+    private Integer mThreadsRunning;
+
+    private Socket mFromSocket, mToSocket;
+    private SocketPipeThread mFromToPipe, mToFromPipe;
+    private InputStream mFromSocketInputStream, mToSocketInputStream;
+    private OutputStream mFromSocketOutputStream, mToSocketOutputStream;
+
+    private int mPort;
+    private String mRemoteMachineIpAddress;
+
+    private OnFinishedCallback mOnFinishedCallback;
+
+    public ConnectionHandler(String remoteMachineIp, int port, Socket fromSocket, Socket toSocket)
+            throws IOException {
+        mRemoteMachineIpAddress = remoteMachineIp;
+        mPort = port;
+
+        mFromSocket = fromSocket;
+        mToSocket = toSocket;
+
+        try {
+            mFromSocketInputStream = mFromSocket.getInputStream();
+            mToSocketInputStream = mToSocket.getInputStream();
+            mFromSocketOutputStream = mFromSocket.getOutputStream();
+            mToSocketOutputStream = mToSocket.getOutputStream();
+            AdbUtils.configureConnection(mToSocketInputStream, mToSocketOutputStream,
+                    mRemoteMachineIpAddress, mPort);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Unable to start ConnectionHandler", e);
+            closeStreams();
+            throw e;
+        }
+
+        mFromToPipe = new SocketPipeThread(mFromSocketInputStream, mToSocketOutputStream);
+        mToFromPipe = new SocketPipeThread(mToSocketInputStream, mFromSocketOutputStream);
+    }
+
+    public void registerOnConnectionHandlerFinishedCallback(OnFinishedCallback callback) {
+        mOnFinishedCallback = callback;
+    }
+
+    private void closeStreams() {
+        FsUtils.closeInputStream(mFromSocketInputStream);
+        FsUtils.closeInputStream(mToSocketInputStream);
+        FsUtils.closeOutputStream(mFromSocketOutputStream);
+        FsUtils.closeOutputStream(mToSocketOutputStream);
+    }
+
+    public void start() {
+        /** We have 2 threads running, one for each pipe, that we start here. */
+        mThreadsRunning = 2;
+        mFromToPipe.start();
+        mToFromPipe.start();
+    }
+
+    public void stop() {
+        shutdown(mFromSocket);
+        shutdown(mToSocket);
+    }
+
+    private void shutdown(Socket socket) {
+        synchronized (mFromToPipe) {
+            synchronized (mToFromPipe) {
+                /** This will stop the while loop in the run method */
+                try {
+                    if (!socket.isInputShutdown()) {
+                        socket.shutdownInput();
+                    }
+                } catch (IOException e) {
+                    Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
+                }
+                try {
+                    if (!socket.isOutputShutdown()) {
+                        socket.shutdownOutput();
+                    }
+                } catch (IOException e) {
+                    Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
+                }
+                try {
+                    if (!socket.isClosed()) {
+                        socket.close();
+                    }
+                } catch (IOException e) {
+                    Log.e(LOG_TAG, "mFromToPipe=" + mFromToPipe + " mToFromPipe=" + mToFromPipe, e);
+                }
+            }
+        }
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java
new file mode 100644
index 0000000..ce22fa0
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/Forwarder.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A port forwarding server. Listens on localhost on specified port and forwards the tcp
+ * communications to external socket via adb networking proxy.
+ */
+public class Forwarder extends Thread {
+    private static final String LOG_TAG = "Forwarder";
+
+    private int mPort;
+    private String mRemoteMachineIpAddress;
+
+    private ServerSocket mServerSocket;
+
+    private Set<ConnectionHandler> mConnectionHandlers = new HashSet<ConnectionHandler>();
+
+    public Forwarder(int port, String remoteMachineIpAddress) {
+        mPort = port;
+        mRemoteMachineIpAddress = remoteMachineIpAddress;
+    }
+
+    @Override
+    public void start() {
+        Log.i(LOG_TAG, "start(): Starting fowarder on port: " + mPort);
+
+        try {
+            mServerSocket = new ServerSocket(mPort);
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "mPort=" + mPort, e);
+            return;
+        }
+
+        super.start();
+    }
+
+    @Override
+    public void run() {
+        while (true) {
+            Socket localSocket;
+            try {
+                localSocket = mServerSocket.accept();
+            } catch (IOException e) {
+                /** This most likely means that mServerSocket is already closed */
+                Log.w(LOG_TAG, "mPort=" + mPort, e);
+                break;
+            }
+
+            Socket remoteSocket = null;
+            final ConnectionHandler connectionHandler;
+            try {
+                remoteSocket = AdbUtils.createSocket();
+                connectionHandler = new ConnectionHandler(
+                        mRemoteMachineIpAddress, mPort, localSocket, remoteSocket);
+            } catch (IOException exception) {
+                try {
+                    localSocket.close();
+                } catch (IOException e) {
+                    Log.e(LOG_TAG, "mPort=" + mPort, e);
+                }
+                if (remoteSocket != null) {
+                    try {
+                        remoteSocket.close();
+                    } catch (IOException e) {
+                        Log.e(LOG_TAG, "mPort=" + mPort, e);
+                    }
+                }
+                continue;
+            }
+
+            /**
+             * We have to close the sockets after the ConnectionHandler finishes, so we
+             * don't get "Too may open files" exception. We also remove the ConnectionHandler
+             * from the collection to avoid memory issues.
+             * */
+            ConnectionHandler.OnFinishedCallback callback =
+                    new ConnectionHandler.OnFinishedCallback() {
+                @Override
+                public void onFinished() {
+                    synchronized (this) {
+                        if (!mConnectionHandlers.remove(connectionHandler)) {
+                            assert false : "removeConnectionHandler(): not in the collection";
+                        }
+                    }
+                }
+            };
+            connectionHandler.registerOnConnectionHandlerFinishedCallback(callback);
+
+            synchronized (this) {
+                mConnectionHandlers.add(connectionHandler);
+            }
+            connectionHandler.start();
+        }
+
+        synchronized (this) {
+            for (ConnectionHandler connectionHandler : mConnectionHandlers) {
+                connectionHandler.stop();
+            }
+        }
+    }
+
+    public void finish() {
+        try {
+            mServerSocket.close();
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "mPort=" + mPort, e);
+        }
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java
new file mode 100644
index 0000000..cff436ff
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/forwarder/ForwarderManager.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.forwarder;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import android.util.Log;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A simple class to start and stop Forwarders running on some ports.
+ *
+ * It uses a singleton pattern and is thread safe.
+ */
+public class ForwarderManager {
+    private static final String LOG_TAG = "ForwarderManager";
+
+    /**
+     * The IP address of the server serving the tests.
+     */
+    private static final String HOST_IP = "127.0.0.1";
+
+    /**
+     * We use these ports because other webkit platforms do. They are set up in
+     * external/webkit/LayoutTests/http/conf/apache2-debian-httpd.conf
+     */
+    public static final int HTTP_PORT = 8000;
+    public static final int HTTPS_PORT = 8443;
+
+    private static ForwarderManager forwarderManager;
+
+    private Set<Forwarder> mForwarders;
+    private boolean mIsStarted;
+
+    private ForwarderManager() {
+        mForwarders = new HashSet<Forwarder>(2);
+        mForwarders.add(new Forwarder(HTTP_PORT, HOST_IP));
+        mForwarders.add(new Forwarder(HTTPS_PORT, HOST_IP));
+    }
+
+    /**
+     * Returns the main part of the URL with the trailing slash
+     *
+     * @param isHttps
+     * @return
+     */
+    public static final String getHostSchemePort(boolean isHttps) {
+        int port;
+        String protocol;
+        if (isHttps) {
+            protocol = "https";
+            port = HTTPS_PORT;
+        } else {
+            protocol = "http";
+            port = HTTP_PORT;
+        }
+
+        URL url = null;
+        try {
+            url = new URL(protocol, HOST_IP, port, "/");
+        } catch (MalformedURLException e) {
+            assert false : "isHttps=" + isHttps;
+        }
+
+        return url.toString();
+    }
+
+    public static synchronized ForwarderManager getForwarderManager() {
+        if (forwarderManager == null) {
+            forwarderManager = new ForwarderManager();
+        }
+        return forwarderManager;
+    }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+    public synchronized void start() {
+        if (mIsStarted) {
+            Log.w(LOG_TAG, "start(): ForwarderManager already running! NOOP.");
+            return;
+        }
+
+        for (Forwarder forwarder : mForwarders) {
+            forwarder.start();
+        }
+
+        mIsStarted = true;
+        Log.i(LOG_TAG, "ForwarderManager started.");
+    }
+
+    public synchronized void stop() {
+        if (!mIsStarted) {
+            Log.w(LOG_TAG, "stop(): ForwarderManager already stopped! NOOP.");
+            return;
+        }
+
+        for (Forwarder forwarder : mForwarders) {
+            forwarder.finish();
+        }
+
+        mIsStarted = false;
+        Log.i(LOG_TAG, "ForwarderManager stopped.");
+    }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java
new file mode 100644
index 0000000..e1d4364
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.scriptsupport;
+
+/**
+ * Callback used to inform scriptsupport.Starter that everything is finished and
+ * we can exit
+ */
+public interface OnEverythingFinishedCallback {
+    public void onFinished();
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java
new file mode 100644
index 0000000..78f58d5
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.scriptsupport;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+
+/**
+ * Extends InstrumentationTestRunner to allow the script to pass arguments to the application
+ */
+public class ScriptTestRunner extends InstrumentationTestRunner {
+    String mTestsRelativePath;
+
+    @Override
+    public void onCreate(Bundle arguments) {
+        mTestsRelativePath = arguments.getString("path");
+        super.onCreate(arguments);
+    }
+
+    public String getTestsRelativePath() {
+        return mTestsRelativePath;
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
new file mode 100644
index 0000000..6f41a0f
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.scriptsupport;
+
+import android.content.Intent;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import com.android.dumprendertree2.TestsListActivity;
+import com.android.dumprendertree2.forwarder.ForwarderManager;
+
+/**
+ * A class which provides methods that can be invoked by a script running on the host machine to
+ * run the tests.
+ *
+ * It starts a TestsListActivity and does not return until all the tests finish executing.
+ */
+public class Starter extends ActivityInstrumentationTestCase2<TestsListActivity> {
+    private static final String LOG_TAG = "Starter";
+    private boolean mEverythingFinished;
+
+    public Starter() {
+        super(TestsListActivity.class);
+    }
+
+    /**
+     * This method is called from adb to start executing the tests. It doesn't return
+     * until everything is finished so that the script can wait for the end if it needs
+     * to.
+     */
+    public void startLayoutTests() {
+        ScriptTestRunner runner = (ScriptTestRunner)getInstrumentation();
+        String relativePath = runner.getTestsRelativePath();
+
+        ForwarderManager.getForwarderManager().start();
+
+        Intent intent = new Intent();
+        intent.setClassName("com.android.dumprendertree2", "TestsListActivity");
+        intent.setAction(Intent.ACTION_RUN);
+        intent.putExtra(TestsListActivity.EXTRA_TEST_PATH, relativePath);
+        setActivityIntent(intent);
+        getActivity().registerOnEverythingFinishedCallback(new OnEverythingFinishedCallback() {
+            /** This method is safe to call on any thread */
+            @Override
+            public void onFinished() {
+                synchronized (Starter.this) {
+                    mEverythingFinished = true;
+                    Starter.this.notifyAll();
+                }
+            }
+        });
+
+        synchronized (this) {
+            while (!mEverythingFinished) {
+                try {
+                    this.wait();
+                } catch (InterruptedException e) {
+                    Log.e(LOG_TAG, "startLayoutTests()", e);
+                }
+            }
+        }
+
+        ForwarderManager.getForwarderManager().stop();
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
new file mode 100644
index 0000000..5de69a7
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.ui;
+
+import com.android.dumprendertree2.FileFilter;
+import com.android.dumprendertree2.FsUtils;
+import com.android.dumprendertree2.TestsListActivity;
+import com.android.dumprendertree2.R;
+import com.android.dumprendertree2.forwarder.ForwarderManager;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An Activity that allows navigating through tests folders and choosing folders or tests to run.
+ */
+public class DirListActivity extends ListActivity {
+
+    private static final String LOG_TAG = "DirListActivity";
+
+    /** TODO: This is just a guess - think of a better way to achieve it */
+    private static final int MEAN_TITLE_CHAR_SIZE = 13;
+
+    private static final int PROGRESS_DIALOG_DELAY_MS = 200;
+
+    /** Code for the dialog, used in showDialog and onCreateDialog */
+    private static final int DIALOG_RUN_ABORT_DIR = 0;
+
+    /** Messages codes */
+    private static final int MSG_LOADED_ITEMS = 0;
+    private static final int MSG_SHOW_PROGRESS_DIALOG = 1;
+
+    private static final CharSequence NO_RESPONSE_MESSAGE =
+            "No response from host when getting directory contents. Is the host server running?";
+
+    /** Initialized lazily before first sProgressDialog.show() */
+    private static ProgressDialog sProgressDialog;
+
+    private ListView mListView;
+
+    /** This is a relative path! */
+    private String mCurrentDirPath;
+
+    /**
+     * A thread responsible for loading the contents of the directory from sd card
+     * and sending them via Message to main thread that then loads them into
+     * ListView
+     */
+    private class LoadListItemsThread extends Thread {
+        private Handler mHandler;
+        private String mRelativePath;
+
+        public LoadListItemsThread(String relativePath, Handler handler) {
+            mRelativePath = relativePath;
+            mHandler = handler;
+        }
+
+        @Override
+        public void run() {
+            Message msg = mHandler.obtainMessage(MSG_LOADED_ITEMS);
+            msg.obj = getDirList(mRelativePath);
+            mHandler.sendMessage(msg);
+        }
+    }
+
+    /**
+     * Very simple object to use inside ListView as an item.
+     */
+    private static class ListItem implements Comparable<ListItem> {
+        private String mRelativePath;
+        private String mName;
+        private boolean mIsDirectory;
+
+        public ListItem(String relativePath, boolean isDirectory) {
+            mRelativePath = relativePath;
+            mName = new File(relativePath).getName();
+            mIsDirectory = isDirectory;
+        }
+
+        public boolean isDirectory() {
+            return mIsDirectory;
+        }
+
+        public String getRelativePath() {
+            return mRelativePath;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        @Override
+        public int compareTo(ListItem another) {
+            return mRelativePath.compareTo(another.getRelativePath());
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof ListItem)) {
+                return false;
+            }
+
+            return mRelativePath.equals(((ListItem)o).getRelativePath());
+        }
+
+        @Override
+        public int hashCode() {
+            return mRelativePath.hashCode();
+        }
+
+    }
+
+    /**
+     * A custom adapter that sets the proper icon and label in the list view.
+     */
+    private static class DirListAdapter extends ArrayAdapter<ListItem> {
+        private Activity mContext;
+        private ListItem[] mItems;
+
+        public DirListAdapter(Activity context, ListItem[] items) {
+            super(context, R.layout.dirlist_row, items);
+
+            mContext = context;
+            mItems = items;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            LayoutInflater inflater = mContext.getLayoutInflater();
+            View row = inflater.inflate(R.layout.dirlist_row, null);
+
+            TextView label = (TextView)row.findViewById(R.id.label);
+            label.setText(mItems[position].getName());
+
+            ImageView icon = (ImageView)row.findViewById(R.id.icon);
+            if (mItems[position].isDirectory()) {
+                icon.setImageResource(R.drawable.folder);
+            } else {
+                icon.setImageResource(R.drawable.runtest);
+            }
+
+            return row;
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        ForwarderManager.getForwarderManager().start();
+
+        mListView = getListView();
+
+        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                ListItem item = (ListItem)parent.getItemAtPosition(position);
+
+                if (item.isDirectory()) {
+                    showDir(item.getRelativePath());
+                } else {
+                    /** Run the test */
+                    runAllTestsUnder(item.getRelativePath());
+                }
+            }
+        });
+
+        mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+            @Override
+            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+                ListItem item = (ListItem)parent.getItemAtPosition(position);
+
+                if (item.isDirectory()) {
+                    Bundle arguments = new Bundle(1);
+                    arguments.putString("name", item.getName());
+                    arguments.putString("relativePath", item.getRelativePath());
+                    showDialog(DIALOG_RUN_ABORT_DIR, arguments);
+                } else {
+                    /** TODO: Maybe show some info about a test? */
+                }
+
+                return true;
+            }
+        });
+
+        /** All the paths are relative to test root dir where possible */
+        showDir("");
+    }
+
+    private void runAllTestsUnder(String relativePath) {
+        Intent intent = new Intent();
+        intent.setClass(DirListActivity.this, TestsListActivity.class);
+        intent.setAction(Intent.ACTION_RUN);
+        intent.putExtra(TestsListActivity.EXTRA_TEST_PATH, relativePath);
+        startActivity(intent);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.gui_menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.run_all:
+                runAllTestsUnder(mCurrentDirPath);
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
+    @Override
+    /**
+     * Moves to the parent directory if one exists. Does not allow to move above
+     * the test 'root' directory.
+     */
+    public void onBackPressed() {
+        File currentDirParent = new File(mCurrentDirPath).getParentFile();
+        if (currentDirParent != null) {
+            showDir(currentDirParent.getPath());
+        } else {
+            showDir("");
+        }
+    }
+
+    /**
+     * Prevents the activity from recreating on change of orientation. The title needs to
+     * be recalculated.
+     */
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        setTitle(shortenTitle(mCurrentDirPath));
+    }
+
+    @Override
+    protected Dialog onCreateDialog(int id, final Bundle args) {
+        Dialog dialog = null;
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+        switch (id) {
+            case DIALOG_RUN_ABORT_DIR:
+                builder.setTitle(getText(R.string.dialog_run_abort_dir_title_prefix) + " " +
+                        args.getString("name"));
+                builder.setMessage(R.string.dialog_run_abort_dir_msg);
+                builder.setCancelable(true);
+
+                builder.setPositiveButton(R.string.dialog_run_abort_dir_ok_button,
+                        new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        removeDialog(DIALOG_RUN_ABORT_DIR);
+                        runAllTestsUnder(args.getString("relativePath"));
+                    }
+                });
+
+                builder.setNegativeButton(R.string.dialog_run_abort_dir_abort_button,
+                        new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        removeDialog(DIALOG_RUN_ABORT_DIR);
+                    }
+                });
+
+                dialog = builder.create();
+                dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+                    @Override
+                    public void onCancel(DialogInterface dialog) {
+                        removeDialog(DIALOG_RUN_ABORT_DIR);
+                    }
+                });
+                break;
+        }
+
+        return dialog;
+    }
+
+    /**
+     * Loads the contents of dir into the list view.
+     *
+     * @param dirPath
+     *      directory to load into list view
+     */
+    private void showDir(String dirPath) {
+        mCurrentDirPath = dirPath;
+
+        /** Show progress dialog with a delay */
+        final Handler delayedDialogHandler = new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                if (msg.what == MSG_SHOW_PROGRESS_DIALOG) {
+                    if (sProgressDialog == null) {
+                        sProgressDialog = new ProgressDialog(DirListActivity.this);
+                        sProgressDialog.setCancelable(false);
+                        sProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+                        sProgressDialog.setTitle(R.string.dialog_progress_title);
+                        sProgressDialog.setMessage(getText(R.string.dialog_progress_msg));
+                    }
+                    sProgressDialog.show();
+                }
+            }
+        };
+        Message msgShowDialog = delayedDialogHandler.obtainMessage(MSG_SHOW_PROGRESS_DIALOG);
+        delayedDialogHandler.sendMessageDelayed(msgShowDialog, PROGRESS_DIALOG_DELAY_MS);
+
+        /** Delegate loading contents from SD card to a new thread */
+        new LoadListItemsThread(mCurrentDirPath, new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                if (msg.what == MSG_LOADED_ITEMS) {
+                    setTitle(shortenTitle(mCurrentDirPath));
+                    delayedDialogHandler.removeMessages(MSG_SHOW_PROGRESS_DIALOG);
+                    if (sProgressDialog != null) {
+                        sProgressDialog.dismiss();
+                    }
+                    if (msg.obj == null) {
+                        Toast.makeText(DirListActivity.this, NO_RESPONSE_MESSAGE,
+                                Toast.LENGTH_LONG).show();
+                    } else {
+                        setListAdapter(new DirListAdapter(DirListActivity.this,
+                                (ListItem[])msg.obj));
+                    }
+                }
+            }
+        }).start();
+    }
+
+    /**
+     * TODO: find a neat way to determine number of characters that fit in the title
+     * bar.
+     * */
+    private String shortenTitle(String title) {
+        if (title.equals("")) {
+            return "Tests' root dir:";
+        }
+        int charCount = mListView.getWidth() / MEAN_TITLE_CHAR_SIZE;
+
+        if (title.length() > charCount) {
+            return "..." + title.substring(title.length() - charCount);
+        } else {
+            return title;
+        }
+    }
+
+    /**
+     * Return the array with contents of the given directory.
+     * First it contains the subfolders, then the files. Both sorted
+     * alphabetically.
+     *
+     * The dirPath is relative.
+     */
+    private ListItem[] getDirList(String dirPath) {
+        List<ListItem> subDirs = new ArrayList<ListItem>();
+        List<ListItem> subFiles = new ArrayList<ListItem>();
+
+        List<String> dirRelativePaths = FsUtils.getLayoutTestsDirContents(dirPath, false, true);
+        if (dirRelativePaths == null) {
+            return null;
+        }
+        for (String dirRelativePath : dirRelativePaths) {
+            if (FileFilter.isTestDir(new File(dirRelativePath).getName())) {
+                subDirs.add(new ListItem(dirRelativePath, true));
+            }
+        }
+
+        List<String> testRelativePaths = FsUtils.getLayoutTestsDirContents(dirPath, false, false);
+        if (testRelativePaths == null) {
+            return null;
+        }
+        for (String testRelativePath : testRelativePaths) {
+            if (FileFilter.isTestFile(new File(testRelativePath).getName())) {
+                subFiles.add(new ListItem(testRelativePath, false));
+            }
+        }
+
+        /** Concatenate the two lists */
+        subDirs.addAll(subFiles);
+
+        return subDirs.toArray(new ListItem[subDirs.size()]);
+    }
+}
diff --git a/tests/HwAccelerationTest/Android.mk b/tests/HwAccelerationTest/Android.mk
new file mode 100644
index 0000000..d4743f9
--- /dev/null
+++ b/tests/HwAccelerationTest/Android.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := HwAccelerationTest
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
new file mode 100644
index 0000000..7f3a968
--- /dev/null
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -0,0 +1,328 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.test.hwui">
+
+    <uses-permission android:name="android.permission.INTERNET" />    
+    
+    <application
+        android:label="HwUi"
+        android:hardwareAccelerated="true">
+
+        <activity
+                android:name="AlphaLayersActivity"
+                android:label="_αLayers">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="AdvancedGradientsActivity"
+                android:label="_Advanced Gradients">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="Bitmaps3dActivity"
+                android:label="_Bitmaps3d">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="LabelsActivity"
+                android:label="_Labels">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+            <activity
+                android:name="ViewFlipperActivity"
+                android:label="_ViewFlipper"
+                android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="ResizeActivity"
+                android:label="_Resize"
+                android:windowSoftInputMode="adjustResize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="TextGammaActivity"
+                android:label="_Gamma"
+                android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+            <activity
+                android:name="TextGammaActivity$SubGammaActivity"
+                android:label="_Sub Gamma"
+                android:theme="@android:style/Theme.Translucent.NoTitleBar"
+                android:hardwareAccelerated="false">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="LayersActivity"
+                android:label="_Layers"
+                android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="NewLayersActivity"
+                android:label="_NewLayers">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="XfermodeActivity"
+                android:label="_Xfermodes"
+                android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="BitmapsActivity"
+                android:label="_Bitmaps"
+                android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="BitmapsAlphaActivity"
+                android:label="_BitmapsAlpha"
+                android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="BitmapsRectActivity"
+                android:label="_BitmapsRect"
+                android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="ThinPatchesActivity"
+                android:label="_9patchThin"
+                android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="NinePatchesActivity"
+                android:label="_9patch">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="QuickRejectActivity"
+                android:label="_QuickReject">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="RotationActivity"
+                android:label="_Rotation">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="GradientsActivity"
+                android:label="_Gradients">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="ShadersActivity"
+                android:label="_Shaders">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="TextActivity"
+                android:label="_Text"
+                android:theme="@android:style/Theme.NoTitleBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="ListActivity"
+                android:label="_List">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="TransparentListActivity"
+                android:label="_TransparentList">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
+                android:name="MoreShadersActivity"
+                android:label="_Shaders2">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="ColorFiltersActivity"
+                android:label="_ColorFilters">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="LinesActivity"
+                android:label="_Lines"
+                android:hardwareAccelerated="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="PathsActivity"
+                android:label="_Paths">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="Transform3dActivity"
+                android:label="_3d">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="SimplePathsActivity"
+                android:label="_SimplePaths">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="AdvancedBlendActivity"
+                android:label="_Advanced Blend">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+                android:name="FramebufferBlendActivity"
+                android:label="_FramebufferBlend">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+
+        <activity
+                android:name="StackActivity"
+                android:label="_Stacks">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+</manifest>
diff --git a/tests/HwAccelerationTest/res/anim/fade_in.xml b/tests/HwAccelerationTest/res/anim/fade_in.xml
new file mode 100644
index 0000000..34310f5
--- /dev/null
+++ b/tests/HwAccelerationTest/res/anim/fade_in.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+        android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="600" />
diff --git a/tests/HwAccelerationTest/res/anim/fade_out.xml b/tests/HwAccelerationTest/res/anim/fade_out.xml
new file mode 100644
index 0000000..9832c32
--- /dev/null
+++ b/tests/HwAccelerationTest/res/anim/fade_out.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fromAlpha="1.0" android:toAlpha="0.0"
+    android:duration="600" />
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_background.xml b/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_background.xml
new file mode 100644
index 0000000..abbb9e6
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:drawable="@drawable/appwidget_bg" />
+    <item android:state_pressed="true" android:drawable="@drawable/appwidget_bg_press" />
+    <item android:state_focused="true" android:drawable="@drawable/appwidget_bg_focus" />
+    <item android:drawable="@drawable/appwidget_bg" />
+</selector>
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_bg.9.png b/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_bg.9.png
new file mode 100644
index 0000000..8049191
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_bg.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_bg_focus.9.png b/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_bg_focus.9.png
new file mode 100644
index 0000000..c81f675
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_bg_focus.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_bg_press.9.png b/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_bg_press.9.png
new file mode 100644
index 0000000..d060b77
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_bg_press.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/green_gradient.9.png b/tests/HwAccelerationTest/res/drawable-hdpi/green_gradient.9.png
new file mode 100644
index 0000000..a535678
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/green_gradient.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/icon.png b/tests/HwAccelerationTest/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..60fbdf5
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg b/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg
new file mode 100644
index 0000000..92851f3
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png b/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png
new file mode 100644
index 0000000..3258ee7
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png b/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png
new file mode 100644
index 0000000..6508b27
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/widget_header.png b/tests/HwAccelerationTest/res/drawable-hdpi/widget_header.png
new file mode 100644
index 0000000..cd9ec44
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/widget_header.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/widget_title_bg.9.png b/tests/HwAccelerationTest/res/drawable-hdpi/widget_title_bg.9.png
new file mode 100644
index 0000000..79615c2
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/widget_title_bg.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/appwidget_background.xml b/tests/HwAccelerationTest/res/drawable/appwidget_background.xml
new file mode 100644
index 0000000..abbb9e6
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/appwidget_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_window_focused="false" android:drawable="@drawable/appwidget_bg" />
+    <item android:state_pressed="true" android:drawable="@drawable/appwidget_bg_press" />
+    <item android:state_focused="true" android:drawable="@drawable/appwidget_bg_focus" />
+    <item android:drawable="@drawable/appwidget_bg" />
+</selector>
diff --git a/tests/HwAccelerationTest/res/drawable/appwidget_bg.9.png b/tests/HwAccelerationTest/res/drawable/appwidget_bg.9.png
new file mode 100644
index 0000000..8049191
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/appwidget_bg.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/appwidget_bg_focus.9.png b/tests/HwAccelerationTest/res/drawable/appwidget_bg_focus.9.png
new file mode 100644
index 0000000..c81f675
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/appwidget_bg_focus.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/appwidget_bg_press.9.png b/tests/HwAccelerationTest/res/drawable/appwidget_bg_press.9.png
new file mode 100644
index 0000000..d060b77
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/appwidget_bg_press.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png b/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png
new file mode 100644
index 0000000..26ee1c2
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png b/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png
new file mode 100644
index 0000000..53e95af
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/default_wallpaper.jpg b/tests/HwAccelerationTest/res/drawable/default_wallpaper.jpg
new file mode 100644
index 0000000..5acad94
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/default_wallpaper.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/green_gradient.9.png b/tests/HwAccelerationTest/res/drawable/green_gradient.9.png
new file mode 100644
index 0000000..a535678
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/green_gradient.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/icon.png b/tests/HwAccelerationTest/res/drawable/icon.png
new file mode 100644
index 0000000..cb40a19
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/icon.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/patch.9.png b/tests/HwAccelerationTest/res/drawable/patch.9.png
new file mode 100644
index 0000000..e3b3639
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/patch.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/sunset1.jpg b/tests/HwAccelerationTest/res/drawable/sunset1.jpg
new file mode 100644
index 0000000..92851f3
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/sunset1.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/sunset2.png b/tests/HwAccelerationTest/res/drawable/sunset2.png
new file mode 100644
index 0000000..3258ee7
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/sunset2.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/sunset3.png b/tests/HwAccelerationTest/res/drawable/sunset3.png
new file mode 100644
index 0000000..6508b27
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/sunset3.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/widget_header.png b/tests/HwAccelerationTest/res/drawable/widget_header.png
new file mode 100644
index 0000000..0297dd1
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/widget_header.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/widget_title_bg.9.png b/tests/HwAccelerationTest/res/drawable/widget_title_bg.9.png
new file mode 100644
index 0000000..79615c2
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/widget_title_bg.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/layout/flipper_item.xml b/tests/HwAccelerationTest/res/layout/flipper_item.xml
new file mode 100644
index 0000000..43a7bbf
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/flipper_item.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/widget_asset"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:focusable="true">
+
+    <ImageView android:id="@+id/widget_image"
+        android:layout_width="91dip"
+        android:layout_height="61dip"
+        android:layout_margin="8dip"
+        android:background="@android:color/white" />
+
+    <TextView android:id="@+id/widget_text"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:layout_toRightOf="@id/widget_image"
+        android:padding="6dip"
+        android:textSize="12sp"
+        android:textColor="@android:color/black"
+        android:fadingEdge="vertical"
+        android:fadingEdgeLength="30dip" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/res/layout/form.xml b/tests/HwAccelerationTest/res/layout/form.xml
new file mode 100644
index 0000000..0b17db1
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/form.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="First name" />
+        
+        <EditText
+            android:layout_width="0dip"
+            android:layout_weight="1.0"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="3dip"
+            android:hint="Enter your first name" />
+        
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Last name" />
+        
+        <EditText
+            android:layout_width="0dip"
+            android:layout_weight="1.0"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="3dip"
+            android:hint="Enter your last name" />
+        
+    </LinearLayout>
+    
+    <EditText
+        android:layout_width="match_parent"
+        android:layout_weight="1.0"
+        android:layout_height="0dip"
+        android:background="#ff7f0000" />
+    
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/res/layout/labels.xml b/tests/HwAccelerationTest/res/layout/labels.xml
new file mode 100644
index 0000000..695a2cc
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/labels.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView
+        android:layout_width="200dip"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:text="This is a long text view for ellipsizing" />
+
+    <TextView
+        android:layout_width="200dip"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:gravity="center_horizontal"
+        android:text="This is a very long text view for ellipsizing" />
+
+    <TextView
+        android:layout_width="200dip"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:gravity="right"
+        android:text="This is a very long text view for ellipsizing" />
+   
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/res/layout/list_activity.xml b/tests/HwAccelerationTest/res/layout/list_activity.xml
new file mode 100644
index 0000000..6bba370
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/list_activity.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        
+        <Button
+            android:layout_width="0dip"
+            android:layout_weight="1.0"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="10dip"
+            android:layout_marginRight="3dip"
+
+            android:text="Add" />
+        
+        <Button
+            android:layout_width="0dip"
+            android:layout_weight="1.0"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="3dip"
+            android:layout_marginRight="10dip"
+
+            android:text="Remove" />
+        
+    </LinearLayout>
+    
+    <ListView
+        android:id="@+id/list"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1.0" />
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        
+        <Button
+            android:layout_width="0dip"
+            android:layout_weight="1.0"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="10dip"
+            android:layout_marginRight="3dip"
+
+            android:text="Add" />
+        
+        <Button
+            android:layout_width="0dip"
+            android:layout_weight="1.0"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="3dip"
+            android:layout_marginRight="10dip"
+
+            android:text="Remove" />
+        
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/res/layout/stack.xml b/tests/HwAccelerationTest/res/layout/stack.xml
new file mode 100644
index 0000000..b4d2d73a
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/stack.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:paddingTop="0dp"
+    android:paddingBottom="0dp"
+    android:paddingLeft="12dp"
+    android:paddingRight="12dp"
+    android:focusable="true">
+    <StackView
+        android:id="@+id/stack_view" 
+        android:layout_width="348px" 
+        android:layout_height="374px"
+        android:layout_gravity="center"
+        android:background="#00000000"
+        android:cacheColorHint="#00000000"
+        android:autoStart="true" />
+</FrameLayout>
diff --git a/tests/HwAccelerationTest/res/layout/stack_item.xml b/tests/HwAccelerationTest/res/layout/stack_item.xml
new file mode 100644
index 0000000..3504018
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/stack_item.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/stack_item"  
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"> 
+    <FrameLayout  
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"> 
+        <ImageView android:id="@+id/textview_icon"
+            android:layout_height="250dip"
+            android:layout_width="250dip"
+            android:layout_gravity="center" />
+        <TextView android:id="@+id/mini_text"
+            android:layout_width="wrap_content" 
+            android:layout_height="wrap_content" 
+            android:layout_gravity="center" />
+    </FrameLayout>
+    <TextView 
+        android:layout_width="wrap_content" 
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:layout_gravity="center" />
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/res/layout/text_large.xml b/tests/HwAccelerationTest/res/layout/text_large.xml
new file mode 100644
index 0000000..85b374c
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/text_large.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="6dip"
+    android:layout_marginLeft="6dip"
+    android:textAppearance="?android:attr/textAppearanceLargeInverse"
+    android:text="Widget "/> 
diff --git a/tests/HwAccelerationTest/res/layout/text_medium.xml b/tests/HwAccelerationTest/res/layout/text_medium.xml
new file mode 100644
index 0000000..8e195e6
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/text_medium.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="6dip"
+    android:layout_marginLeft="6dip"
+    android:textAppearance="?android:attr/textAppearanceMediumInverse"
+    android:text="Widget "/> 
diff --git a/tests/HwAccelerationTest/res/layout/text_small.xml b/tests/HwAccelerationTest/res/layout/text_small.xml
new file mode 100644
index 0000000..45eee60
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/text_small.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="6dip"
+    android:layout_marginLeft="6dip"
+    android:textAppearance="?android:attr/textAppearanceSmallInverse"
+    android:text="Widget "/> 
diff --git a/tests/HwAccelerationTest/res/layout/widget.xml b/tests/HwAccelerationTest/res/layout/widget.xml
new file mode 100644
index 0000000..503face
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/widget.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/widget_root"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:focusable="true"
+    android:background="@drawable/appwidget_background">
+
+    <ImageView
+        android:id="@+id/widget_top"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/widget_title_bg"
+        android:padding="6dip"
+        android:src="@drawable/widget_header"
+        android:scaleType="center" />
+
+    <ViewFlipper
+        android:id="@+id/flipper"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:addStatesFromChildren="true"
+        android:flipInterval="2000"
+        android:autoStart="true"
+        android:inAnimation="@anim/fade_in"
+        android:outAnimation="@anim/fade_out"
+        android:background="@drawable/green_gradient"
+        android:measureAllChildren="false" />
+
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedBlendActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedBlendActivity.java
new file mode 100644
index 0000000..5baa20c
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedBlendActivity.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ComposeShader;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class AdvancedBlendActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(new ShadersView(this));
+    }
+
+    static class ShadersView extends View {
+        private BitmapShader mScaledShader;
+        private int mTexWidth;
+        private int mTexHeight;
+        private Paint mPaint;
+        private float mDrawWidth;
+        private float mDrawHeight;
+        private LinearGradient mHorGradient;
+        private ComposeShader mComposeShader;
+        private ComposeShader mCompose2Shader;
+        private ComposeShader mCompose3Shader;
+        private ComposeShader mCompose4Shader;
+        private ComposeShader mCompose5Shader;
+        private ComposeShader mCompose6Shader;
+        private BitmapShader mScaled2Shader;
+
+        ShadersView(Context c) {
+            super(c);
+
+            Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            mTexWidth = texture.getWidth();
+            mTexHeight = texture.getHeight();
+            mDrawWidth = mTexWidth * 2.2f;
+            mDrawHeight = mTexHeight * 1.2f;
+
+            mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+                    Shader.TileMode.MIRROR);
+            Matrix m2 = new Matrix();
+            m2.setScale(0.5f, 0.5f);
+            mScaledShader.setLocalMatrix(m2);
+            
+            mScaled2Shader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+                    Shader.TileMode.MIRROR);
+            Matrix m3 = new Matrix();
+            m3.setScale(0.1f, 0.1f);
+            mScaled2Shader.setLocalMatrix(m3);
+
+            mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+                    Color.BLACK, Color.WHITE, Shader.TileMode.CLAMP);
+            
+            mComposeShader = new ComposeShader(mScaledShader, mHorGradient,
+                    PorterDuff.Mode.DARKEN);
+            mCompose2Shader = new ComposeShader(mScaledShader, mHorGradient,
+                    PorterDuff.Mode.LIGHTEN);
+            mCompose3Shader = new ComposeShader(mScaledShader, mHorGradient,
+                    PorterDuff.Mode.MULTIPLY);
+            mCompose4Shader = new ComposeShader(mScaledShader, mHorGradient,
+                    PorterDuff.Mode.SCREEN);
+            mCompose5Shader = new ComposeShader(mScaledShader, mHorGradient,
+                    PorterDuff.Mode.ADD);
+            mCompose6Shader = new ComposeShader(mHorGradient, mScaledShader,
+                    PorterDuff.Mode.OVERLAY);
+
+            mPaint = new Paint();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRGB(255, 255, 255);
+
+            canvas.save();
+            canvas.translate(40.0f, 40.0f);
+
+            mPaint.setShader(mComposeShader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+            
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mCompose2Shader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mCompose3Shader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.restore();
+            
+            canvas.save();
+            canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f);
+            
+            mPaint.setShader(mCompose4Shader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mCompose5Shader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mCompose6Shader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+            
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedGradientsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedGradientsActivity.java
new file mode 100644
index 0000000..27974e7
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedGradientsActivity.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RadialGradient;
+import android.graphics.Shader;
+import android.graphics.SweepGradient;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class AdvancedGradientsActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(new GradientsView(this));
+    }
+
+    static class GradientsView extends View {
+        private final Paint mPaint;
+        private final SweepGradient mSweepGradient;
+        private final RadialGradient mRadialGradient;
+        private final Matrix mMatrix;
+        private final Matrix mMatrix2;
+        private final Matrix mMatrix3;
+
+        GradientsView(Context c) {
+            super(c);
+
+            mSweepGradient = new SweepGradient(0.0f, 0.0f, 0xff000000, 0xffffffff);
+            mRadialGradient = new RadialGradient(0.0f, 0.0f, 100.0f, 0xff000000, 0xffffffff,
+                    Shader.TileMode.MIRROR);
+            
+            mMatrix = new Matrix();
+            mMatrix.setRotate(-45, 0.0f, 0.0f);
+            mMatrix.postTranslate(100.0f, 100.0f);
+
+            mMatrix2 = new Matrix();
+            mMatrix2.setScale(1.0f, 2.0f);
+            mMatrix2.postRotate(-45, 0.0f, 0.0f);
+
+            mMatrix3 = new Matrix();
+            mMatrix3.setTranslate(100.0f, 100.0f);            
+            
+            mPaint = new Paint();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRGB(255, 255, 255);
+
+            canvas.save();
+            canvas.translate(130.0f, 100.0f);
+
+            mSweepGradient.setLocalMatrix(mMatrix3);
+            mPaint.setShader(mSweepGradient);
+            canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);
+
+            canvas.translate(400.0f, 000.0f);
+            
+            mSweepGradient.setLocalMatrix(mMatrix);
+            mPaint.setShader(mSweepGradient);
+            canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);            
+
+            canvas.translate(400.0f, 000.0f);
+            
+            mSweepGradient.setLocalMatrix(mMatrix2);
+            mPaint.setShader(mSweepGradient);
+            canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);
+
+            canvas.translate(-800.0f, 300.0f);
+
+            mRadialGradient.setLocalMatrix(null);
+            mPaint.setShader(mRadialGradient);
+            canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);
+            
+            canvas.translate(400.0f, 000.0f);
+            
+            mRadialGradient.setLocalMatrix(mMatrix);
+            mPaint.setShader(mRadialGradient);
+            canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);
+
+            canvas.translate(400.0f, 000.0f);
+            
+            mRadialGradient.setLocalMatrix(mMatrix2);
+            mPaint.setShader(mRadialGradient);
+            canvas.drawRect(0.0f, 0.0f, 200.0f, 200.0f, mPaint);
+            
+            
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/AlphaLayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/AlphaLayersActivity.java
new file mode 100644
index 0000000..1a68a93
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/AlphaLayersActivity.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class AlphaLayersActivity extends Activity {
+    private static final String LOG_TAG = "HwUi";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        DirtyBitmapView container = new DirtyBitmapView(this);
+
+        ColorView color = new ColorView(this);
+        container.addView(color, new DirtyBitmapView.LayoutParams(
+                dipToPx(this, 100), dipToPx(this, 100), Gravity.CENTER));
+
+        AlphaAnimation a = new AlphaAnimation(1.0f, 0.0f);
+        a.setDuration(2000);
+        a.setRepeatCount(Animation.INFINITE);
+        a.setRepeatMode(Animation.REVERSE);
+        color.startAnimation(a);
+
+        setContentView(container);
+    }
+    
+    @SuppressWarnings({"UnusedDeclaration"})
+    static int dipToPx(Context c, int dip) {
+        return (int) (c.getResources().getDisplayMetrics().density * dip + 0.5f);
+    }
+
+    static class ColorView extends View {
+        ColorView(Context c) {
+            super(c);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRGB(0, 255, 0);
+        }
+    }
+
+    static class DirtyBitmapView extends FrameLayout {
+        private final Paint mPaint;
+
+        DirtyBitmapView(Context c) {
+            super(c);
+            mPaint = new Paint();
+        }
+
+        @Override
+        public void dispatchDraw(Canvas canvas) {
+            canvas.drawRGB(255, 255, 255);
+
+            mPaint.setColor(0xffff0000);
+            canvas.drawRect(200.0f, 0.0f, 220.0f, 20.0f, mPaint);
+
+            canvas.save();
+            canvas.clipRect(20.0f, 0.0f, 40.0f, 20.0f);
+            Log.d(LOG_TAG, "clipRect = " + canvas.getClipBounds());
+            Log.d(LOG_TAG, "rejected = " + canvas.quickReject(100.0f, 100.0f, 110.0f, 110.0f,
+                    Canvas.EdgeType.BW));
+            Log.d(LOG_TAG, "rejected = " + canvas.quickReject(25.0f, 5.0f, 30.0f, 10.0f,
+                    Canvas.EdgeType.BW));
+            canvas.restore();
+            
+            canvas.save();
+            canvas.scale(2.0f, 2.0f);
+            canvas.clipRect(20.0f, 0.0f, 40.0f, 20.0f);
+            Log.d(LOG_TAG, "clipRect = " + canvas.getClipBounds());
+            Log.d(LOG_TAG, "rejected = " + canvas.quickReject(50.0f, 50.0f, 60.0f, 60.0f,
+                    Canvas.EdgeType.BW));
+            Log.d(LOG_TAG, "rejected = " + canvas.quickReject(25.0f, 5.0f, 30.0f, 10.0f,
+                    Canvas.EdgeType.BW));
+            canvas.restore();
+
+            canvas.save();
+            canvas.translate(20.0f, 20.0f);
+            canvas.clipRect(20.0f, 0.0f, 40.0f, 20.0f);
+            Log.d(LOG_TAG, "clipRect = " + canvas.getClipBounds());
+            Log.d(LOG_TAG, "rejected = " + canvas.quickReject(80.0f, 80.0f, 90.0f, 90.0f,
+                    Canvas.EdgeType.BW));
+            Log.d(LOG_TAG, "rejected = " + canvas.quickReject(25.0f, 5.0f, 30.0f, 10.0f,
+                    Canvas.EdgeType.BW));
+            canvas.restore();
+
+            canvas.save();
+            canvas.scale(2.0f, 2.0f);            
+            canvas.clipRect(20.0f, 0.0f, 40.0f, 20.0f);
+
+            mPaint.setColor(0xff00ff00);
+            canvas.drawRect(0.0f, 0.0f, 20.0f, 20.0f, mPaint);
+            
+            mPaint.setColor(0xff0000ff);
+            canvas.drawRect(20.0f, 0.0f, 40.0f, 20.0f, mPaint);
+            
+            canvas.restore();
+
+            final int restoreTo = canvas.save();
+            canvas.saveLayerAlpha(0.0f, 100.0f, getWidth(), 150.0f, 127,
+                    Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+            mPaint.setColor(0xff0000ff);
+            canvas.drawRect(0.0f, 100.0f, 40.0f, 150.0f, mPaint);
+            mPaint.setColor(0xff00ffff);
+            canvas.drawRect(40.0f, 100.0f, 140.0f, 150.0f, mPaint);
+            mPaint.setColor(0xffff00ff);
+            canvas.drawRect(140.0f, 100.0f, 240.0f, 150.0f, mPaint);
+            canvas.restoreToCount(restoreTo);
+
+            super.dispatchDraw(canvas);
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Bitmaps3dActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/Bitmaps3dActivity.java
new file mode 100644
index 0000000..baa1cb9
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/Bitmaps3dActivity.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class Bitmaps3dActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final BitmapsView view = new BitmapsView(this);
+        final FrameLayout layout = new FrameLayout(this);
+        layout.addView(view, new FrameLayout.LayoutParams(800, 400, Gravity.CENTER));
+        view.setRotationX(-35.0f);
+        setContentView(layout);
+    }
+
+    static class BitmapsView extends View {
+        private final Paint mBitmapPaint;
+        private final Bitmap mBitmap1;
+        private Matrix mMatrix;
+
+        BitmapsView(Context c) {
+            super(c);
+
+            mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            mBitmapPaint = new Paint();
+
+            mMatrix = new Matrix();
+            mMatrix.setScale(2.0f, 2.0f);
+            mMatrix.preTranslate(0.0f, -10.0f);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            
+            canvas.drawColor(0xffffffff);
+
+            canvas.save();
+            canvas.translate(120.0f, 50.0f);
+
+            canvas.concat(mMatrix);
+            canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+            
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsActivity.java
new file mode 100644
index 0000000..40543530
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsActivity.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.ScaleAnimation;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BitmapsActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final BitmapsView view = new BitmapsView(this);
+        final FrameLayout layout = new FrameLayout(this);
+        layout.addView(view, new FrameLayout.LayoutParams(480, 800, Gravity.CENTER));
+        setContentView(layout);
+        
+        ScaleAnimation a = new ScaleAnimation(1.0f, 2.0f, 1.0f, 2.0f,
+                ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
+                ScaleAnimation.RELATIVE_TO_SELF,0.5f);
+        a.setDuration(2000);
+        a.setRepeatCount(Animation.INFINITE);
+        a.setRepeatMode(Animation.REVERSE);
+        view.startAnimation(a);
+    }
+
+    static class BitmapsView extends View {
+        private Paint mBitmapPaint;
+        private final Bitmap mBitmap1;
+        private final Bitmap mBitmap2;
+        private final PorterDuffXfermode mDstIn;
+
+        BitmapsView(Context c) {
+            super(c);
+
+            mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
+
+            mBitmapPaint = new Paint();
+            mDstIn = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.translate(120.0f, 50.0f);
+            canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+
+            canvas.translate(0.0f, mBitmap1.getHeight());
+            canvas.translate(0.0f, 25.0f);
+            canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, null);
+            
+            mBitmapPaint.setAlpha(127);
+            canvas.translate(0.0f, mBitmap2.getHeight());
+            canvas.translate(0.0f, 25.0f);
+            canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+            
+            mBitmapPaint.setAlpha(255);
+            canvas.translate(0.0f, mBitmap1.getHeight());
+            canvas.translate(0.0f, 25.0f);
+            mBitmapPaint.setColor(0xffff0000);
+            canvas.drawRect(0.0f, 0.0f, mBitmap2.getWidth(), mBitmap2.getHeight(), mBitmapPaint);
+            mBitmapPaint.setXfermode(mDstIn);
+            canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mBitmapPaint);
+
+            mBitmapPaint.reset();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsAlphaActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsAlphaActivity.java
new file mode 100644
index 0000000..ef49c7f
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsAlphaActivity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BitmapsAlphaActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final BitmapsView view = new BitmapsView(this);
+        final FrameLayout layout = new FrameLayout(this);
+        layout.addView(view, new FrameLayout.LayoutParams(480, 800, Gravity.CENTER));
+        setContentView(layout);
+    }
+
+    static class BitmapsView extends View {
+        private Paint mBitmapPaint;
+        private final Bitmap mBitmap1;
+        private final Bitmap mBitmap2;
+        private Bitmap mBitmap3;
+
+        BitmapsView(Context c) {
+            super(c);
+
+            Log.d("OpenGLRenderer", "Loading sunset1, default options");
+            mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            Log.d("OpenGLRenderer", "Loading sunset2, default options");
+            mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
+            Log.d("OpenGLRenderer", "Loading sunset3, forcing ARGB-8888");
+            BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
+            mBitmap3 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset3, opts);
+            Log.d("OpenGLRenderer", "    has bitmap alpha? " + mBitmap3.hasAlpha());
+
+            mBitmapPaint = new Paint();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            
+            Log.d("OpenGLRenderer", "================= Draw");
+
+            canvas.translate(120.0f, 50.0f);
+            canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+
+            canvas.translate(0.0f, mBitmap1.getHeight());
+            canvas.translate(0.0f, 25.0f);
+            canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, null);
+            
+            canvas.translate(0.0f, mBitmap2.getHeight());
+            canvas.translate(0.0f, 25.0f);
+            canvas.drawBitmap(mBitmap3, 0.0f, 0.0f, null);
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsRectActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsRectActivity.java
new file mode 100644
index 0000000..b192209
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsRectActivity.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BitmapsRectActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final BitmapsView view = new BitmapsView(this);
+        setContentView(view);
+    }
+
+    static class BitmapsView extends View {
+        private Paint mBitmapPaint;
+        private final Bitmap mBitmap1;
+        private final Bitmap mBitmap2;
+        private final Rect mSrcRect;
+        private final RectF mDstRect;
+        private final RectF mDstRect2;
+
+        BitmapsView(Context c) {
+            super(c);
+
+            mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
+
+            mBitmapPaint = new Paint();
+            mBitmapPaint.setFilterBitmap(true);
+
+            final float fourth = mBitmap1.getWidth() / 4.0f;
+            final float half = mBitmap1.getHeight() / 2.0f;
+            mSrcRect = new Rect((int) fourth, (int) (half - half / 2.0f),
+                    (int) (fourth + fourth), (int) (half + half / 2.0f));
+            mDstRect = new RectF(fourth, half - half / 2.0f, fourth + fourth, half + half / 2.0f);
+            mDstRect2 = new RectF(fourth, half - half / 2.0f,
+                    (fourth + fourth) * 3.0f, (half + half / 2.0f) * 3.0f);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.translate(120.0f, 50.0f);
+            canvas.drawBitmap(mBitmap1, mSrcRect, mDstRect, mBitmapPaint);
+
+            canvas.translate(0.0f, mBitmap1.getHeight());
+            canvas.translate(-100.0f, 25.0f);
+            canvas.drawBitmap(mBitmap1, mSrcRect, mDstRect2, mBitmapPaint);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersActivity.java
new file mode 100644
index 0000000..09d63d6
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersActivity.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.LightingColorFilter;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ColorFiltersActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final BitmapsView view = new BitmapsView(this);
+        setContentView(view);
+    }
+
+    static class BitmapsView extends View {
+        private final Bitmap mBitmap1;
+        private final Bitmap mBitmap2;
+        private final Paint mColorMatrixPaint;
+        private final Paint mLightingPaint;
+        private final Paint mBlendPaint;
+
+        BitmapsView(Context c) {
+            super(c);
+
+            mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
+
+            mColorMatrixPaint = new Paint();
+            final ColorMatrix colorMatrix = new ColorMatrix();
+            colorMatrix.setSaturation(0);
+            mColorMatrixPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
+
+            mLightingPaint = new Paint();
+            mLightingPaint.setColorFilter(new LightingColorFilter(0x0060ffff, 0x00101030));
+
+            mBlendPaint = new Paint();
+            mBlendPaint.setColorFilter(new PorterDuffColorFilter(0x7f990040,
+                    PorterDuff.Mode.SRC_OVER));
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.drawARGB(255, 255, 255, 255);
+            
+            canvas.save();
+            canvas.translate(120.0f, 50.0f);
+            canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mColorMatrixPaint);
+
+            canvas.translate(0.0f, 50.0f + mBitmap1.getHeight());
+            canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mLightingPaint);
+            
+            canvas.translate(0.0f, 50.0f + mBitmap1.getHeight());
+            canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBlendPaint);
+            canvas.restore();
+
+            canvas.save();
+            canvas.translate(120.0f + mBitmap1.getWidth() + 120.0f, 50.0f);
+            canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mColorMatrixPaint);
+
+            canvas.translate(0.0f, 50.0f + mBitmap2.getHeight());
+            canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mLightingPaint);
+            
+            canvas.translate(0.0f, 50.0f + mBitmap2.getHeight());
+            canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mBlendPaint);
+            canvas.restore();            
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/FramebufferBlendActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/FramebufferBlendActivity.java
new file mode 100644
index 0000000..1556bae
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/FramebufferBlendActivity.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class FramebufferBlendActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(new BlendView(this));
+    }
+
+    static class BlendView extends View {
+        private int mTexWidth;
+        private int mTexHeight;
+        private Paint mPaint;
+        private LinearGradient mHorGradient;
+        private Bitmap mTexture;
+
+        BlendView(Context c) {
+            super(c);
+
+            mTexture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            mTexWidth = mTexture.getWidth();
+            mTexHeight = mTexture.getHeight();
+
+            mHorGradient = new LinearGradient(0.0f, 0.0f, mTexWidth, 0.0f,
+                    Color.BLACK, Color.WHITE, Shader.TileMode.CLAMP);
+
+            mPaint = new Paint();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRGB(255, 255, 255);
+
+            canvas.save();
+            canvas.translate(40.0f, 40.0f);
+
+            drawBlendedBitmap(canvas, PorterDuff.Mode.DARKEN);
+            drawBlendedBitmap(canvas, PorterDuff.Mode.LIGHTEN);
+            drawBlendedBitmap(canvas, PorterDuff.Mode.MULTIPLY);
+
+            canvas.restore();
+
+            canvas.save();
+            canvas.translate(40.0f + mTexWidth + 40.0f, 40.0f);
+
+            drawBlendedBitmap(canvas, PorterDuff.Mode.SCREEN);
+            drawBlendedBitmap(canvas, PorterDuff.Mode.ADD);
+            drawBlendedBitmapInverse(canvas, PorterDuff.Mode.OVERLAY);
+
+            canvas.restore();
+        }
+
+        private void drawBlendedBitmap(Canvas canvas, PorterDuff.Mode mode) {
+            mPaint.setShader(null);
+            mPaint.setXfermode(null);
+            canvas.drawBitmap(mTexture, 0.0f, 0.0f, mPaint);
+
+            mPaint.setShader(mHorGradient);
+            mPaint.setXfermode(new PorterDuffXfermode(mode));
+            canvas.drawRect(0.0f, 0.0f, mTexWidth, mTexHeight, mPaint);
+
+            canvas.translate(0.0f, 40.0f + mTexHeight);
+        }
+
+        private void drawBlendedBitmapInverse(Canvas canvas, PorterDuff.Mode mode) {
+            mPaint.setXfermode(null);
+            mPaint.setShader(mHorGradient);
+            canvas.drawRect(0.0f, 0.0f, mTexWidth, mTexHeight, mPaint);
+
+            mPaint.setXfermode(new PorterDuffXfermode(mode));
+            mPaint.setShader(null);
+            canvas.drawBitmap(mTexture, 0.0f, 0.0f, mPaint);
+
+            canvas.translate(0.0f, 40.0f + mTexHeight);
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java
new file mode 100644
index 0000000..8fa626b
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.LinearGradient;
+import android.graphics.RadialGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.graphics.SweepGradient;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.SeekBar;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class GradientsActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final FrameLayout layout = new FrameLayout(this);
+
+        final ShadersView shadersView = new ShadersView(this);
+        final GradientView gradientView = new GradientView(this);
+        final RadialGradientView radialGradientView = new RadialGradientView(this);
+        final SweepGradientView sweepGradientView = new SweepGradientView(this);
+        final BitmapView bitmapView = new BitmapView(this);
+
+        final SeekBar rotateView = new SeekBar(this);
+        rotateView.setMax(360);
+        rotateView.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+            }
+
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                gradientView.setRotationY((float) progress);
+                radialGradientView.setRotationX((float) progress);
+                sweepGradientView.setRotationY((float) progress);
+                bitmapView.setRotationX((float) progress);
+            }
+        });
+        
+        layout.addView(shadersView);
+        layout.addView(gradientView, new FrameLayout.LayoutParams(
+                200, 200, Gravity.CENTER));
+
+        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(200, 200, Gravity.CENTER);
+        lp.setMargins(220, 0, 0, 0);
+        layout.addView(radialGradientView, lp);
+
+        lp = new FrameLayout.LayoutParams(200, 200, Gravity.CENTER);
+        lp.setMargins(440, 0, 0, 0);
+        layout.addView(sweepGradientView, lp);
+
+        lp = new FrameLayout.LayoutParams(200, 200, Gravity.CENTER);
+        lp.setMargins(220, -220, 0, 0);
+        layout.addView(bitmapView, lp);
+
+        layout.addView(rotateView, new FrameLayout.LayoutParams(
+                300, FrameLayout.LayoutParams.WRAP_CONTENT,
+                Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM));
+
+        setContentView(layout);
+    }
+    
+    static class BitmapView extends View {
+        private final Paint mPaint;
+
+        BitmapView(Context c) {
+            super(c);
+
+            Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            BitmapShader shader = new BitmapShader(texture, Shader.TileMode.REPEAT,
+                    Shader.TileMode.REPEAT);
+            mPaint = new Paint();
+            mPaint.setShader(shader);
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            setMeasuredDimension(200, 200);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mPaint);
+        }
+    }
+    
+    static class GradientView extends View {
+        private final Paint mPaint;
+
+        GradientView(Context c) {
+            super(c);
+
+            LinearGradient gradient = new LinearGradient(0, 0, 200, 0, 0xFF000000, 0,
+                    Shader.TileMode.CLAMP);
+            mPaint = new Paint();
+            mPaint.setShader(gradient);
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            setMeasuredDimension(200, 200);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mPaint);
+        }
+    }
+
+    static class RadialGradientView extends View {
+        private final Paint mPaint;
+
+        RadialGradientView(Context c) {
+            super(c);
+
+            RadialGradient gradient = new RadialGradient(0.0f, 0.0f, 100.0f, 0xff000000, 0xffffffff,
+                    Shader.TileMode.MIRROR);
+            mPaint = new Paint();
+            mPaint.setShader(gradient);
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            setMeasuredDimension(200, 200);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mPaint);
+        }
+    }
+    
+    static class SweepGradientView extends View {
+        private final Paint mPaint;
+
+        SweepGradientView(Context c) {
+            super(c);
+
+            SweepGradient gradient = new SweepGradient(100.0f, 100.0f, 0xff000000, 0xffffffff);                
+            mPaint = new Paint();
+            mPaint.setShader(gradient);
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            setMeasuredDimension(200, 200);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mPaint);
+        }
+    }
+        
+    static class ShadersView extends View {
+        private final Paint mPaint;
+        private final float mDrawWidth;
+        private final float mDrawHeight;
+        private final LinearGradient mGradient;
+        private final Matrix mMatrix;
+
+        ShadersView(Context c) {
+            super(c);
+
+            mDrawWidth = 200;
+            mDrawHeight = 200;
+
+            mGradient = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, Shader.TileMode.CLAMP);
+            mMatrix = new Matrix();
+
+            mPaint = new Paint();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRGB(255, 255, 255);
+
+            // Gradients
+            canvas.save();
+            float top = 40.0f;
+            float right = 40.0f + mDrawWidth;
+            float left = 40.0f;
+            float bottom = 40.0f + mDrawHeight;
+
+            mPaint.setShader(mGradient);
+
+            mMatrix.setScale(1, mDrawWidth);
+            mMatrix.postRotate(90);
+            mMatrix.postTranslate(right, top);
+            mGradient.setLocalMatrix(mMatrix);
+            canvas.drawRect(right - mDrawWidth, top, right, top + mDrawHeight, mPaint);
+
+            top += 40.0f + mDrawHeight;
+            bottom += 40.0f + mDrawHeight;
+            
+            mMatrix.setScale(1, mDrawHeight);
+            mMatrix.postTranslate(left, top);
+            mGradient.setLocalMatrix(mMatrix);
+            canvas.drawRect(left, top, right, top + mDrawHeight, mPaint);
+            
+            left += 40.0f + mDrawWidth;
+            right += 40.0f + mDrawWidth;
+            top -= 40.0f + mDrawHeight;
+            bottom -= 40.0f + mDrawHeight;
+
+            mMatrix.setScale(1, mDrawHeight);
+            mMatrix.postRotate(180);
+            mMatrix.postTranslate(left, bottom);
+            mGradient.setLocalMatrix(mMatrix);
+            canvas.drawRect(left, bottom - mDrawHeight, right, bottom, mPaint);
+
+            top += 40.0f + mDrawHeight;
+            bottom += 40.0f + mDrawHeight;
+            
+            mMatrix.setScale(1, mDrawWidth);
+            mMatrix.postRotate(-90);
+            mMatrix.postTranslate(left, top);
+            mGradient.setLocalMatrix(mMatrix);
+            canvas.drawRect(left, top, left + mDrawWidth, bottom, mPaint);
+           
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LabelsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LabelsActivity.java
new file mode 100644
index 0000000..bae0500
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LabelsActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class LabelsActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.labels);
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java
new file mode 100644
index 0000000..b705117
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class LayersActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(new LayersView(this));
+    }
+
+    static class LayersView extends View {
+        private Paint mLayerPaint;
+        private final Paint mRectPaint;
+
+        LayersView(Context c) {
+            super(c);
+
+            mLayerPaint = new Paint();
+            mRectPaint = new Paint();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            
+            canvas.translate(140.0f, 100.0f);
+
+            //canvas.drawRGB(255, 255, 255);
+            
+            int count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint,
+                    Canvas.ALL_SAVE_FLAG);
+
+            mRectPaint.setColor(0xffff0000);
+            canvas.drawRect(0.0f, 0.0f, 200.0f, 100.0f, mRectPaint);
+
+            canvas.restoreToCount(count);
+            
+            canvas.translate(0.0f, 125.0f);
+            
+            count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint,
+                    Canvas.ALL_SAVE_FLAG);
+
+            mRectPaint.setColor(0xff00ff00);
+            mRectPaint.setAlpha(50);
+            canvas.drawRect(0.0f, 0.0f, 200.0f, 100.0f, mRectPaint);
+
+            canvas.restoreToCount(count);
+
+            canvas.translate(25.0f, 125.0f);
+
+            mRectPaint.setColor(0xff0000ff);
+            mRectPaint.setAlpha(255);
+            canvas.drawRect(0.0f, 0.0f, 100.0f, 50.0f, mRectPaint);            
+            
+            mLayerPaint.setAlpha(127);
+            mLayerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
+            count = canvas.saveLayer(50.0f, 25.0f, 150.0f, 75.0f, mLayerPaint,
+                    Canvas.ALL_SAVE_FLAG);
+
+            mRectPaint.setColor(0xffff0000);
+            canvas.drawRect(50.0f, 25.0f, 150.0f, 75.0f, mRectPaint);
+
+            canvas.restoreToCount(count);
+            
+            canvas.translate(0.0f, 125.0f);
+
+            mRectPaint.setColor(0xff0000ff);
+            mRectPaint.setAlpha(255);
+            canvas.drawRect(0.0f, 0.0f, 100.0f, 50.0f, mRectPaint);            
+            
+            mLayerPaint.setColor(0xffff0000);
+            mLayerPaint.setAlpha(127);
+            mLayerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
+            count = canvas.saveLayer(50.0f, 25.0f, 150.0f, 75.0f, mLayerPaint,
+                    Canvas.ALL_SAVE_FLAG);
+
+            mRectPaint.setColor(0xffff0000);
+            canvas.drawRect(50.0f, 25.0f, 150.0f, 75.0f, mRectPaint);
+
+            canvas.restoreToCount(count);
+            
+            mLayerPaint = new Paint();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
new file mode 100644
index 0000000..0dc836d
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class LinesActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final LinesView view = new LinesView(this);
+        setContentView(view);
+    }
+
+    static class LinesView extends View {
+        private final Bitmap mBitmap1;
+        private final Paint mSmallPaint;
+        private final Paint mMediumPaint;
+        private final Paint mLargePaint;
+        private final BitmapShader mShader;
+        private final float[] mPoints;
+        private final Paint mAlphaPaint;
+
+        LinesView(Context c) {
+            super(c);
+
+            mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+
+            mSmallPaint = new Paint();
+            mSmallPaint.setAntiAlias(true);
+            mSmallPaint.setColor(0xffff0000);
+            mSmallPaint.setStrokeWidth(1.0f);
+
+            mMediumPaint = new Paint();
+            mMediumPaint.setAntiAlias(true);
+            mMediumPaint.setColor(0xff0000ff);
+            mMediumPaint.setStrokeWidth(4.0f);
+
+            mLargePaint = new Paint();
+            mLargePaint.setAntiAlias(true);
+            mLargePaint.setColor(0xff00ff00);
+            mLargePaint.setStrokeWidth(15.0f);
+
+            mAlphaPaint = new Paint();
+            mAlphaPaint.setAntiAlias(true);
+            mAlphaPaint.setColor(0x7fff0050);
+            mAlphaPaint.setStrokeWidth(10.0f);
+            
+            mShader = new BitmapShader(mBitmap1, BitmapShader.TileMode.MIRROR,
+                    BitmapShader.TileMode.MIRROR);
+
+            mPoints = new float[] {
+                    62.0f, 0.0f, 302.0f, 400.0f,
+                    302.0f, 400.0f, 352.0f, 400.0f,
+                    352.0f, 400.0f, 352.0f, 500.0f
+            };
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.drawARGB(255, 255, 255, 255);
+
+            canvas.save();
+            canvas.translate(100.0f, 20.0f);
+
+            canvas.drawLine(0.0f, 0.0f, 40.0f, 400.0f, mSmallPaint);
+            canvas.drawLine(5.0f, 0.0f, 95.0f, 400.0f, mMediumPaint);
+            canvas.drawLine(22.0f, 0.0f, 162.0f, 400.0f, mLargePaint);
+            
+            mLargePaint.setShader(mShader);
+            canvas.drawLine(42.0f, 0.0f, 222.0f, 400.0f, mLargePaint);
+            mLargePaint.setShader(null);
+            
+            canvas.drawLines(mPoints, mAlphaPaint);
+
+            mSmallPaint.setAntiAlias(false);
+            canvas.drawLine(0.0f, 0.0f, 400.0f, 0.0f, mSmallPaint);
+            mSmallPaint.setAntiAlias(true);
+            canvas.drawLine(0.0f, 0.0f, 0.0f, 400.0f, mSmallPaint);
+            canvas.drawLine(0.0f, 400.0f, 400.0f, 400.0f, mSmallPaint);
+            
+            canvas.translate(120.0f, 0.0f);
+            mAlphaPaint.setShader(mShader);            
+            canvas.drawLines(mPoints, mAlphaPaint);
+            mAlphaPaint.setShader(null);
+
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
new file mode 100644
index 0000000..8fd4f6b
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.ContextMenu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ListActivity extends Activity {
+    private static final String[] DATA_LIST = {
+            "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
+            "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
+            "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
+            "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
+            "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
+            "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil",
+            "British Indian Ocean Territory", "British Virgin Islands", "Brunei", "Bulgaria",
+            "Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
+            "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
+            "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
+            "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+            "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
+            "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
+            "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
+            "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
+            "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
+            "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
+            "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
+            "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
+            "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
+            "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
+            "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
+            "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
+            "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
+            "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
+            "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
+            "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
+            "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
+            "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
+            "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
+            "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
+            "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
+            "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
+            "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
+            "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
+            "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
+            "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
+            "Ukraine", "United Arab Emirates", "United Kingdom",
+            "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
+            "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
+            "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.list_activity);
+
+        ListAdapter adapter = new SimpleListAdapter(this);
+
+        ListView list = (ListView) findViewById(R.id.list);
+        list.setAdapter(adapter);
+        
+        registerForContextMenu(list);
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+        super.onCreateContextMenu(menu, v, menuInfo);
+        menu.setHeaderTitle("Context menu");
+        menu.add("List item 1");
+        menu.add("List item 2");
+        menu.add("List item 3");
+    }
+
+    private static class SimpleListAdapter extends ArrayAdapter<String> {
+        public SimpleListAdapter(Context context) {
+            super(context, android.R.layout.simple_list_item_1, DATA_LIST);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TextView v = (TextView) super.getView(position, convertView, parent);
+            final Resources r = getContext().getResources();
+            final DisplayMetrics metrics = r.getDisplayMetrics();
+            v.setCompoundDrawablePadding((int) (6 * metrics.density + 0.5f));
+            v.setCompoundDrawablesWithIntrinsicBounds(r.getDrawable(R.drawable.icon),
+                    null, null, null);
+            return v;
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java
new file mode 100644
index 0000000..182a474
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ComposeShader;
+import android.graphics.LightingColorFilter;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class MoreShadersActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(new ShadersView(this));
+    }
+
+    static class ShadersView extends View {
+        private BitmapShader mScaledShader;
+        private int mTexWidth;
+        private int mTexHeight;
+        private Paint mPaint;
+        private float mDrawWidth;
+        private float mDrawHeight;
+        private LinearGradient mHorGradient;
+        private LinearGradient mVertGradient;
+        private ComposeShader mComposeShader;
+        private ComposeShader mCompose2Shader;
+        private Paint mLargePaint;
+        private BitmapShader mScaled2Shader;
+        private ColorFilter mColorFilter;
+
+        ShadersView(Context c) {
+            super(c);
+
+            Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            mTexWidth = texture.getWidth();
+            mTexHeight = texture.getHeight();
+            mDrawWidth = mTexWidth * 2.2f;
+            mDrawHeight = mTexHeight * 1.2f;
+
+            mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+                    Shader.TileMode.MIRROR);
+            Matrix m2 = new Matrix();
+            m2.setScale(0.5f, 0.5f);
+            mScaledShader.setLocalMatrix(m2);
+            
+            mScaled2Shader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+                    Shader.TileMode.MIRROR);
+            Matrix m3 = new Matrix();
+            m3.setScale(0.1f, 0.1f);
+            mScaled2Shader.setLocalMatrix(m3);
+
+            mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+                    Color.RED, 0x7f00ff00, Shader.TileMode.CLAMP);
+            
+            mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
+                    Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
+
+            mComposeShader = new ComposeShader(mScaledShader, mHorGradient,
+                    PorterDuff.Mode.SRC_OVER);
+            mCompose2Shader = new ComposeShader(mHorGradient, mScaledShader,
+                    PorterDuff.Mode.SRC_OUT);
+
+            mColorFilter = new LightingColorFilter(0x0060ffff, 0x00101030);
+ 
+            mLargePaint = new Paint();
+            mLargePaint.setAntiAlias(true);
+            mLargePaint.setTextSize(36.0f);
+            mLargePaint.setColor(0xff000000);
+            
+            mPaint = new Paint();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRGB(255, 255, 255);
+
+            canvas.save();
+            canvas.translate(40.0f, 40.0f);
+
+            mPaint.setShader(mComposeShader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+            
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mCompose2Shader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.restore();
+
+            canvas.save();
+            canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f);
+
+            mLargePaint.setShader(mHorGradient);
+            canvas.drawText("OpenGL rendering", 0.0f, 20.0f, mLargePaint);
+            
+            mLargePaint.setShader(mScaled2Shader);
+            canvas.drawText("OpenGL rendering", 0.0f, 60.0f, mLargePaint);
+            
+            mLargePaint.setShader(mCompose2Shader);
+            mLargePaint.setColorFilter(mColorFilter);
+            canvas.drawText("OpenGL rendering", 0.0f, 100.0f, mLargePaint);
+            mLargePaint.setColorFilter(null);
+            
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mLargePaint.setShader(mVertGradient);
+            canvas.drawText("OpenGL rendering", 0.0f, 20.0f, mLargePaint);
+            
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
new file mode 100644
index 0000000..d9a2893
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class NewLayersActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(new LayersView(this));
+    }
+
+    static class LayersView extends View {
+        private Paint mLayerPaint;
+        private final Paint mRectPaint;
+
+        LayersView(Context c) {
+            super(c);
+
+            mLayerPaint = new Paint();
+            mLayerPaint.setAlpha(127);
+            mRectPaint = new Paint();
+            mRectPaint.setAntiAlias(true);
+            mRectPaint.setTextSize(24.0f);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRGB(128, 255, 128);
+
+            canvas.save();
+
+            canvas.translate(140.0f, 100.0f);
+            drawStuff(canvas, Canvas.ALL_SAVE_FLAG);
+
+            canvas.translate(0.0f, 200.0f);
+            drawStuff(canvas, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
+            
+            canvas.restore();
+        }
+
+        private void drawStuff(Canvas canvas, int saveFlags) {
+            int count = canvas.saveLayer(0.0f, 0.0f, 200.0f, 100.0f, mLayerPaint, saveFlags);
+
+            mRectPaint.setColor(0x7fff0000);
+            canvas.drawRect(-20.0f, -20.0f, 220.0f, 120.0f, mRectPaint);
+
+            mRectPaint.setColor(0xff000000);
+            canvas.drawText("This is a very long string to overlap between layers and framebuffer",
+                    -100.0f, 50.0f, mRectPaint);
+
+            canvas.restoreToCount(count);
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/NinePatchesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/NinePatchesActivity.java
new file mode 100644
index 0000000..7410f79
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/NinePatchesActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class NinePatchesActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        FrameLayout layout = new FrameLayout(this);
+        Button b = new Button(this);
+        b.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
+                FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
+        b.setText("9 patches");
+        layout.addView(b);
+        layout.setBackgroundColor(0xffffffff);
+        
+        setContentView(layout);
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PathsActivity.java
new file mode 100644
index 0000000..8d9bf37
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PathsActivity.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class PathsActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final PathsView view = new PathsView(this);
+        setContentView(view);
+    }
+
+    static class PathsView extends View {
+        private final Bitmap mBitmap1;
+        private final Paint mSmallPaint;
+        private final Paint mMediumPaint;
+        private final Paint mLargePaint;
+        private final BitmapShader mShader;
+        private final Path mPath;
+        private final RectF mPathBounds;
+        private final Paint mBoundsPaint;
+        private final Bitmap mBitmap;
+        private final float mOffset;
+        private final Paint mLinePaint;
+
+        PathsView(Context c) {
+            super(c);
+
+            mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+
+            mSmallPaint = new Paint();
+            mSmallPaint.setAntiAlias(true);
+            mSmallPaint.setColor(0xffff0000);
+            mSmallPaint.setStrokeWidth(1.0f);
+            mSmallPaint.setStyle(Paint.Style.STROKE);
+            
+            mLinePaint = new Paint();
+            mLinePaint.setAntiAlias(true);
+            mLinePaint.setColor(0xffff00ff);
+            mLinePaint.setStrokeWidth(1.0f);
+            mLinePaint.setStyle(Paint.Style.STROKE);
+
+            mMediumPaint = new Paint();
+            mMediumPaint.setAntiAlias(true);
+            mMediumPaint.setColor(0xff0000ff);
+            mMediumPaint.setStrokeWidth(10.0f);
+            mMediumPaint.setStyle(Paint.Style.STROKE);
+
+            mLargePaint = new Paint();
+            mLargePaint.setAntiAlias(true);
+            mLargePaint.setColor(0xff00ff00);
+            mLargePaint.setStrokeWidth(15.0f);
+            mLargePaint.setStyle(Paint.Style.FILL);
+
+            mShader = new BitmapShader(mBitmap1, BitmapShader.TileMode.MIRROR,
+                    BitmapShader.TileMode.MIRROR);
+
+            mPath = new Path();
+            mPath.moveTo(0.0f, 0.0f);
+            mPath.cubicTo(0.0f, 0.0f, 100.0f, 150.0f, 100.0f, 200.0f);
+            mPath.cubicTo(100.0f, 200.0f, 50.0f, 300.0f, -80.0f, 200.0f);
+            mPath.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f);
+
+            mPathBounds = new RectF();
+            mPath.computeBounds(mPathBounds, true);
+
+            mBoundsPaint = new Paint();
+            mBoundsPaint.setColor(0x4000ff00);
+
+            mOffset = mMediumPaint.getStrokeWidth();
+            final int width = (int) (mPathBounds.width() + mOffset * 3.0f + 0.5f);
+            final int height = (int) (mPathBounds.height() + mOffset * 3.0f + 0.5f);
+            mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
+            Canvas canvas = new Canvas(mBitmap);
+            canvas.translate(-mPathBounds.left + mOffset * 1.5f, -mPathBounds.top + mOffset * 1.5f);
+            canvas.drawPath(mPath, mMediumPaint);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.drawARGB(255, 255, 255, 255);
+
+            canvas.save();
+            canvas.translate(200.0f, 60.0f);
+            canvas.drawPath(mPath, mSmallPaint);
+            
+            canvas.translate(350.0f, 0.0f);
+            canvas.drawPath(mPath, mMediumPaint);
+
+            mLargePaint.setShader(mShader);
+            canvas.translate(350.0f, 0.0f);
+            canvas.drawPath(mPath, mLargePaint);
+            mLargePaint.setShader(null);
+            canvas.restore();
+            
+            canvas.save();
+            canvas.translate(200.0f, 360.0f);
+            canvas.drawPath(mPath, mSmallPaint);
+            canvas.drawRect(mPathBounds, mBoundsPaint);
+            
+            canvas.translate(350.0f, 0.0f);
+            canvas.drawBitmap(mBitmap, mPathBounds.left - mOffset * 1.5f,
+                    mPathBounds.top - mOffset * 1.5f, null);
+            canvas.drawRect(mPathBounds, mBoundsPaint);
+            canvas.drawLine(0.0f, -360.0f, 0.0f, 500.0f, mLinePaint);
+
+            mLargePaint.setShader(mShader);
+            canvas.translate(350.0f, 0.0f);
+            canvas.drawPath(mPath, mLargePaint);
+            canvas.drawRect(mPathBounds, mBoundsPaint);
+            mLargePaint.setShader(null);
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/QuickRejectActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/QuickRejectActivity.java
new file mode 100644
index 0000000..5192bfe
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/QuickRejectActivity.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class QuickRejectActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final QuickRejectView view = new QuickRejectView(this);
+        setContentView(view);
+    }
+
+    static class QuickRejectView extends View {
+        private Paint mBitmapPaint;
+        private final Bitmap mBitmap1;
+
+        QuickRejectView(Context c) {
+            super(c);
+
+            mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+
+            mBitmapPaint = new Paint();
+            mBitmapPaint.setFilterBitmap(true);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            int count = canvas.getSaveCount();
+            Log.d("OpenGLRenderer", "count=" + count);
+            count = canvas.save();
+            Log.d("OpenGLRenderer", "count after save=" + count);
+            count = canvas.getSaveCount();
+            Log.d("OpenGLRenderer", "getSaveCount after save=" + count);
+            canvas.restore();
+            count = canvas.getSaveCount();
+            Log.d("OpenGLRenderer", "count after restore=" + count);
+            canvas.save();
+            Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+            canvas.save();
+            Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+            canvas.save();
+            Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+            canvas.restoreToCount(count);
+            count = canvas.getSaveCount();
+            Log.d("OpenGLRenderer", "count after restoreToCount=" + count);
+            count = canvas.saveLayer(0, 0, 10, 10, mBitmapPaint, Canvas.ALL_SAVE_FLAG);
+            Log.d("OpenGLRenderer", "count after saveLayer=" + count);
+            count = canvas.getSaveCount();
+            Log.d("OpenGLRenderer", "getSaveCount after saveLayer=" + count);
+            canvas.restore();
+            count = canvas.getSaveCount();
+            Log.d("OpenGLRenderer", "count after restore=" + count);
+
+            canvas.save();
+            canvas.clipRect(0.0f, 0.0f, 40.0f, 40.0f);
+            canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+            canvas.drawBitmap(mBitmap1, -mBitmap1.getWidth(), 0.0f, mBitmapPaint);
+            canvas.drawBitmap(mBitmap1, 50.0f, 0.0f, mBitmapPaint);
+            canvas.restore();
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ResizeActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ResizeActivity.java
new file mode 100644
index 0000000..04f9de1
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ResizeActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ResizeActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        setContentView(R.layout.form);
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RotationActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RotationActivity.java
new file mode 100644
index 0000000..5c309b4
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RotationActivity.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class RotationActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        DrawingView container = new DrawingView(this);
+
+        setContentView(container);
+    }
+    
+    @SuppressWarnings({"UnusedDeclaration"})
+    static int dipToPx(Context c, int dip) {
+        return (int) (c.getResources().getDisplayMetrics().density * dip + 0.5f);
+    }
+
+    static class DrawingView extends View {
+        private final Paint mPaint;
+
+        DrawingView(Context c) {
+            super(c);
+            mPaint = new Paint();
+            mPaint.setAntiAlias(true);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            canvas.save();
+            canvas.translate(dipToPx(getContext(), 400), dipToPx(getContext(), 200));
+            canvas.rotate(45.0f);
+            canvas.drawRGB(255, 255, 255);
+            mPaint.setColor(0xffff0000);
+            canvas.drawRect(-80.0f, -80.0f, 80.0f, 80.0f, mPaint);
+            canvas.drawRect(0.0f, 0.0f, 220.0f, 220.0f, mPaint);            
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java
new file mode 100644
index 0000000..02eaa7c
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ShadersActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(new ShadersView(this));
+    }
+
+    static class ShadersView extends View {
+        private BitmapShader mRepeatShader;
+        private BitmapShader mTranslatedShader;
+        private BitmapShader mScaledShader;
+        private int mTexWidth;
+        private int mTexHeight;
+        private Paint mPaint;
+        private float mDrawWidth;
+        private float mDrawHeight;
+        private LinearGradient mHorGradient;
+        private LinearGradient mDiagGradient;
+        private LinearGradient mVertGradient;
+        private Bitmap mTexture;
+
+        ShadersView(Context c) {
+            super(c);
+
+            mTexture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            mTexWidth = mTexture.getWidth();
+            mTexHeight = mTexture.getHeight();
+            mDrawWidth = mTexWidth * 2.2f;
+            mDrawHeight = mTexHeight * 1.2f;
+
+            mRepeatShader = new BitmapShader(mTexture, Shader.TileMode.REPEAT,
+                    Shader.TileMode.REPEAT);
+
+            mTranslatedShader = new BitmapShader(mTexture, Shader.TileMode.REPEAT,
+                    Shader.TileMode.REPEAT);
+            Matrix m1 = new Matrix();
+            m1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f);
+            m1.postRotate(45, 0, 0);
+            mTranslatedShader.setLocalMatrix(m1);
+            
+            mScaledShader = new BitmapShader(mTexture, Shader.TileMode.MIRROR,
+                    Shader.TileMode.MIRROR);
+            Matrix m2 = new Matrix();
+            m2.setScale(0.5f, 0.5f);
+            mScaledShader.setLocalMatrix(m2);
+
+            mHorGradient = new LinearGradient(0.0f, 0.0f, 1.0f, 0.0f,
+                    Color.RED, Color.GREEN, Shader.TileMode.CLAMP);
+            Matrix m3 = new Matrix();
+            m3.setScale(mDrawHeight, 1.0f);
+            m3.postRotate(-90.0f);
+            m3.postTranslate(0.0f, mDrawHeight);
+            mHorGradient.setLocalMatrix(m3);
+            
+            mDiagGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth / 1.5f, mDrawHeight,
+                    Color.BLUE, Color.MAGENTA, Shader.TileMode.CLAMP);
+            
+            mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
+                    Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
+
+            mPaint = new Paint();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            //canvas.drawRGB(255, 255, 255);
+            canvas.drawBitmap(mTexture, 0.0f, 0.0f, null);
+
+            // Bitmap shaders
+            canvas.save();
+            canvas.translate(40.0f, 40.0f);
+
+            mPaint.setShader(mRepeatShader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+            
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mTranslatedShader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mScaledShader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.restore();
+
+            // Gradients
+            canvas.save();
+            canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f);
+
+            mPaint.setShader(mHorGradient);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+            
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mDiagGradient);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mVertGradient);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+            
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/SimplePathsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/SimplePathsActivity.java
new file mode 100644
index 0000000..c3e18a3
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/SimplePathsActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class SimplePathsActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        FrameLayout layout = new FrameLayout(this);
+        EditText text = new EditText(this);
+        layout.addView(text, new FrameLayout.LayoutParams(600, 350, Gravity.CENTER));
+        text.setText("This is an example of an EditText widget \n" +
+                "using simple paths to create the selection.");
+        //text.setSelection(0, text.getText().length());
+
+        setContentView(layout);
+    }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StackActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StackActivity.java
new file mode 100644
index 0000000..5655adf
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StackActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.StackView;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class StackActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.stack);
+
+        StackView stack = (StackView) findViewById(R.id.stack_view);
+        stack.setAdapter(new ArrayAdapter<Drawable>(this, android.R.layout.simple_list_item_1,
+                android.R.id.text1, new Drawable[] {
+            getResources().getDrawable(R.drawable.sunset1),
+            getResources().getDrawable(R.drawable.sunset2),
+        }) {
+            @Override
+            public View getView(int position, View convertView, ViewGroup parent) {
+                View item = convertView;
+                if (item == null) {
+                    item = LayoutInflater.from(getContext()).inflate(
+                            R.layout.stack_item, null, false);                    
+                }
+                ((ImageView) item.findViewById(R.id.textview_icon)).setImageDrawable(
+                        getItem(position % getCount()));
+                ((TextView) item.findViewById(R.id.mini_text)).setText("" + position);
+                return item;
+            }
+        });
+        stack.setDisplayedChild(0);
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
new file mode 100644
index 0000000..eb0df51
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class TextActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(new CustomTextView(this));
+    }
+
+    static class CustomTextView extends View {
+        private final Paint mMediumPaint;
+        private final Paint mLargePaint;
+        private final Paint mStrikePaint;
+        private final Paint mScaledPaint;
+
+        CustomTextView(Context c) {
+            super(c);
+
+            mMediumPaint = new Paint();
+            mMediumPaint.setAntiAlias(true);
+            mMediumPaint.setColor(0xffff0000);
+
+            mLargePaint = new Paint();
+            mLargePaint.setAntiAlias(true);
+            mLargePaint.setTextSize(36.0f);
+
+            mStrikePaint = new Paint();
+            mStrikePaint.setAntiAlias(true);
+            mStrikePaint.setTextSize(16.0f);
+            mStrikePaint.setUnderlineText(true);
+
+            mScaledPaint = new Paint();
+            mScaledPaint.setAntiAlias(true);
+            mScaledPaint.setTextSize(16.0f);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRGB(255, 255, 255);
+
+            canvas.drawText("Hello OpenGL renderer!", 100, 20, mMediumPaint);
+            mMediumPaint.setTextAlign(Paint.Align.CENTER);
+            canvas.drawText("Hello OpenGL renderer!", 100, 40, mMediumPaint);
+            mMediumPaint.setTextAlign(Paint.Align.RIGHT);
+            canvas.drawText("Hello OpenGL renderer!", 100, 60, mMediumPaint);
+            mMediumPaint.setTextAlign(Paint.Align.LEFT);
+            canvas.drawText("Hello OpenGL renderer!", 100, 100, mMediumPaint);
+            mMediumPaint.setShadowLayer(2.5f, 0.0f, 0.0f, 0xff000000);
+            canvas.drawText("Hello OpenGL renderer!", 100, 150, mMediumPaint);
+            mMediumPaint.clearShadowLayer();
+            canvas.drawText("Hello OpenGL renderer!", 100, 200, mLargePaint);
+
+            mLargePaint.setShadowLayer(2.5f, 3.0f, 3.0f, 0xff000000);
+            canvas.drawText("Hello OpenGL renderer!", 100, 400, mLargePaint);
+            mLargePaint.setShadowLayer(3.0f, 3.0f, 3.0f, 0xff00ff00);
+            mLargePaint.setAlpha(100);
+            canvas.drawText("Hello OpenGL renderer!", 100, 500, mLargePaint);
+            mLargePaint.setAlpha(255);
+            mLargePaint.clearShadowLayer();
+            
+            canvas.drawText("Hello OpenGL renderer!", 500, 40, mStrikePaint);
+            mStrikePaint.setStrikeThruText(true);
+            canvas.drawText("Hello OpenGL renderer!", 500, 70, mStrikePaint);
+            mStrikePaint.setUnderlineText(false);
+            canvas.drawText("Hello OpenGL renderer!", 500, 100, mStrikePaint);
+            mStrikePaint.setStrikeThruText(false);
+            mStrikePaint.setUnderlineText(true);
+            
+            mScaledPaint.setTextScaleX(0.5f);
+            canvas.drawText("Hello OpenGL renderer!", 500, 200, mScaledPaint);
+            mScaledPaint.setTextScaleX(2.0f);
+            canvas.drawText("Hello OpenGL renderer!", 500, 230, mScaledPaint);
+            mScaledPaint.setTextScaleX(1.0f);
+            canvas.drawText("Hello OpenGL renderer!", 500, 260, mScaledPaint);
+            
+            canvas.save();
+            canvas.clipRect(150.0f, 220.0f, 450.0f, 320.0f);
+            canvas.drawText("Hello OpenGL renderer!", 100, 300, mLargePaint);
+            canvas.restore();
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextGammaActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextGammaActivity.java
new file mode 100644
index 0000000..773d390
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextGammaActivity.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class TextGammaActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final LinearLayout layout = new LinearLayout(this);
+        layout.setOrientation(LinearLayout.VERTICAL);
+
+        final GammaTextView gamma = new GammaTextView(this);
+        layout.addView(gamma, new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT
+        ));
+
+        setContentView(layout);
+        
+        layout.post(new Runnable() {
+            @Override
+            public void run() {
+                Bitmap b = Bitmap.createBitmap(gamma.getWidth(), gamma.getHeight(),
+                        Bitmap.Config.ARGB_8888);
+                Canvas c = new Canvas(b);
+                c.drawColor(0, PorterDuff.Mode.CLEAR);
+                gamma.draw(c);
+
+                ImageView image = new ImageView(TextGammaActivity.this);
+                image.setImageBitmap(b);
+
+                layout.addView(image, new LinearLayout.LayoutParams(
+                        LinearLayout.LayoutParams.WRAP_CONTENT,
+                        LinearLayout.LayoutParams.WRAP_CONTENT
+                ));
+
+                startActivity(new Intent(TextGammaActivity.this, SubGammaActivity.class));
+            }
+        });
+
+        getWindow().setBackgroundDrawable(new ColorDrawable(0xffffffff));
+    }
+
+    static class GammaTextView extends LinearLayout {
+        GammaTextView(Context c) {
+            super(c);
+
+            setBackgroundColor(0xffffffff);
+
+            final LayoutInflater inflater = LayoutInflater.from(c);
+            inflater.inflate(R.layout.text_large, this, true);
+            inflater.inflate(R.layout.text_medium, this, true);
+            inflater.inflate(R.layout.text_small, this, true);
+        }
+    }
+
+    public static class SubGammaActivity extends Activity {
+        @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            final LinearLayout layout = new LinearLayout(this);
+            layout.setOrientation(LinearLayout.VERTICAL);
+        
+            final GammaTextView gamma = new GammaTextView(this);
+            final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+                    LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT
+            );
+            lp.setMargins(0, 74, 0, 0);
+            layout.addView(gamma, lp);
+
+            setContentView(layout);
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
new file mode 100644
index 0000000..4f605fa
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ThinPatchesActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        FrameLayout layout = new FrameLayout(this);
+        PatchView b = new PatchView(this);
+        b.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
+                FrameLayout.LayoutParams.MATCH_PARENT));
+        layout.addView(b);
+        layout.setBackgroundColor(0xffffffff);
+        
+        setContentView(layout);
+    }
+
+    private class PatchView extends View {
+        private Drawable mPatch1, mPatch2;
+
+        public PatchView(Activity activity) {
+            super(activity);
+
+            final Resources resources = activity.getResources();
+            mPatch1 = resources.getDrawable(R.drawable.patch);
+            mPatch2 = resources.getDrawable(R.drawable.btn_toggle_on);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            final int width = 100;
+            final int height = 60;
+
+            final int left = (getWidth() - width) / 2;
+            final int top  = (getHeight() - height) / 2;
+
+            mPatch1.setBounds(left, top, left + width, top + height);
+            mPatch1.draw(canvas);
+
+            canvas.translate(0.0f, height + 20.0f);
+            
+            mPatch2.setBounds(left, top, left + width, top + height);
+            mPatch2.draw(canvas);
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Transform3dActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/Transform3dActivity.java
new file mode 100644
index 0000000..6df66e6
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/Transform3dActivity.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Camera;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class Transform3dActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final Transform3dView view = new Transform3dView(this);
+        setContentView(view);
+    }
+
+    static class Transform3dView extends View {
+        private final Bitmap mBitmap1;
+        private Camera mCamera;
+        private Matrix mMatrix;
+
+        Transform3dView(Context c) {
+            super(c);
+
+            mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            mCamera = new Camera();
+            mMatrix = new Matrix();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.drawARGB(255, 255, 255, 255);
+
+            final float centerX = getWidth() / 2.0f - mBitmap1.getWidth() / 2.0f;
+            final float centerY = getHeight() / 2.0f - mBitmap1.getHeight() / 2.0f;
+            final Camera camera = mCamera;
+            
+            final Matrix matrix = mMatrix;
+
+            rotate(centerX, centerY, camera, matrix, 32.0f);
+            drawBitmap(canvas, centerX, centerY, 0.0f, matrix);
+
+            rotate(centerX, centerY, camera, matrix, 12.0f);
+            drawBitmap(canvas, centerX, centerY, -mBitmap1.getWidth(), matrix);
+            
+            rotate(centerX, centerY, camera, matrix, 52.0f);
+            drawBitmap(canvas, centerX, centerY, mBitmap1.getWidth(), matrix);
+            
+            rotate(centerX, centerY, camera, matrix, 122.0f);
+            drawBitmap(canvas, centerX, centerY, mBitmap1.getWidth() * 2.0f, matrix);
+            
+        }
+
+        private void drawBitmap(Canvas canvas, float centerX, float centerY, float offset,
+                Matrix matrix) {
+            canvas.save();
+            canvas.translate(offset, 0.0f);
+            canvas.concat(matrix);
+            canvas.drawBitmap(mBitmap1, centerX, centerY, null);
+            canvas.restore();
+        }
+
+        private void rotate(float centerX, float centerY, Camera camera,
+                Matrix matrix, float angle) {
+            camera.save();
+            camera.rotateY(angle);
+            camera.getMatrix(matrix);
+            camera.restore();
+
+            matrix.preTranslate(-centerX, -centerY);
+            matrix.postTranslate(centerX, centerY);
+        }
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
new file mode 100644
index 0000000..763169a
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.ContextMenu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class TransparentListActivity extends Activity {
+    private static final String[] DATA_LIST = {
+            "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
+            "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
+            "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
+            "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
+            "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
+            "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil",
+            "British Indian Ocean Territory", "British Virgin Islands", "Brunei", "Bulgaria",
+            "Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
+            "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
+            "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
+            "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+            "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
+            "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
+            "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
+            "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
+            "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
+            "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
+            "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
+            "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
+            "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
+            "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
+            "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
+            "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
+            "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
+            "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
+            "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
+            "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
+            "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
+            "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
+            "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
+            "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
+            "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
+            "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
+            "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
+            "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
+            "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
+            "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
+            "Ukraine", "United Arab Emirates", "United Kingdom",
+            "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
+            "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
+            "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        getWindow().setBackgroundDrawable(getResources().getDrawable(R.drawable.default_wallpaper));
+        setContentView(R.layout.list_activity);
+
+        ListAdapter adapter = new SimpleListAdapter(this);
+
+        ListView list = (ListView) findViewById(R.id.list);
+        list.setAdapter(adapter);
+        list.setCacheColorHint(0);
+        list.setVerticalFadingEdgeEnabled(true);
+
+        registerForContextMenu(list);
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+        super.onCreateContextMenu(menu, v, menuInfo);
+        menu.setHeaderTitle("Context menu");
+        menu.add("List item 1");
+        menu.add("List item 2");
+        menu.add("List item 3");
+    }
+
+    private static class SimpleListAdapter extends ArrayAdapter<String> {
+        public SimpleListAdapter(Context context) {
+            super(context, android.R.layout.simple_list_item_1, DATA_LIST);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TextView v = (TextView) super.getView(position, convertView, parent);
+            final Resources r = getContext().getResources();
+            final DisplayMetrics metrics = r.getDisplayMetrics();
+            v.setCompoundDrawablePadding((int) (6 * metrics.density + 0.5f));
+            v.setCompoundDrawablesWithIntrinsicBounds(r.getDrawable(R.drawable.icon),
+                    null, null, null);
+            return v;
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewFlipperActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewFlipperActivity.java
new file mode 100644
index 0000000..0e244fc
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewFlipperActivity.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.ViewFlipper;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ViewFlipperActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final LayoutInflater inflater = getLayoutInflater();
+        final View widget = inflater.inflate(R.layout.widget, null);
+        widget.setLayoutParams(new FrameLayout.LayoutParams(180, 180, Gravity.CENTER));
+        
+        ViewFlipper flipper = (ViewFlipper) widget.findViewById(R.id.flipper);
+        
+        View view = inflater.inflate(R.layout.flipper_item, flipper, false);
+        flipper.addView(view);
+        ((ImageView) view.findViewById(R.id.widget_image)).setImageResource(R.drawable.sunset1);
+        ((TextView) view.findViewById(R.id.widget_text)).setText("This is a long line of text, " 
+                + "enjoy the wrapping and drawing");
+
+        view = inflater.inflate(R.layout.flipper_item, flipper, false);
+        flipper.addView(view);
+        ((ImageView) view.findViewById(R.id.widget_image)).setImageResource(R.drawable.sunset3);
+        ((TextView) view.findViewById(R.id.widget_text)).setText("Another very long line of text, " 
+                + "enjoy the wrapping and drawing");
+
+        FrameLayout layout = new FrameLayout(this);
+        layout.addView(widget);
+        
+        setContentView(layout);
+    }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/XfermodeActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/XfermodeActivity.java
new file mode 100644
index 0000000..411077f
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/XfermodeActivity.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.os.Bundle;
+import android.view.View;
+
+import static android.graphics.PorterDuff.Mode;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class XfermodeActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        setContentView(new XfermodesView(this));
+    }
+
+    static class XfermodesView extends View {
+        private final Paint mBluePaint;
+        private final Paint mRedPaint;
+
+        XfermodesView(Context c) {
+            super(c);
+
+            mBluePaint = new Paint();
+            mBluePaint.setColor(0xff0000ff);
+            
+            mRedPaint = new Paint();
+            mRedPaint.setColor(0x7fff0000);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            //canvas.drawRGB(255, 255, 255);
+
+            canvas.translate(100.0f, 100.0f);
+            
+            // SRC modes
+            canvas.save();
+
+            drawRects(canvas, Mode.SRC_OVER);
+            canvas.translate(0.0f, 100.0f);
+
+            drawRects(canvas, Mode.SRC_IN);
+            canvas.translate(0.0f, 100.0f);            
+
+            drawRects(canvas, Mode.SRC_OUT);
+            canvas.translate(0.0f, 100.0f);
+
+            drawRects(canvas, Mode.SRC_ATOP);
+            canvas.translate(0.0f, 100.0f);
+            
+            drawRects(canvas, Mode.SRC);
+
+            canvas.restore();
+            
+            canvas.translate(100.0f, 0.0f);
+            
+            // DST modes
+            canvas.save();
+
+            drawRects(canvas, Mode.DST_OVER);
+            canvas.translate(0.0f, 100.0f);
+
+            drawRects(canvas, Mode.DST_IN);
+            canvas.translate(0.0f, 100.0f);            
+
+            drawRects(canvas, Mode.DST_OUT);
+            canvas.translate(0.0f, 100.0f);
+
+            drawRects(canvas, Mode.DST_ATOP);
+            canvas.translate(0.0f, 100.0f);
+            
+            drawRects(canvas, Mode.DST);
+
+            canvas.restore();
+            
+            canvas.translate(100.0f, 0.0f);
+            
+            // Other modes
+            canvas.save();
+
+            drawRects(canvas, Mode.CLEAR);
+            canvas.translate(0.0f, 100.0f);
+
+            drawRects(canvas, Mode.XOR);
+            
+            canvas.translate(0.0f, 100.0f);
+            
+            mBluePaint.setAlpha(127);
+            canvas.drawRect(0.0f, 0.0f, 50.0f, 50.0f, mBluePaint);
+            
+            canvas.translate(0.0f, 100.0f);
+            
+            mBluePaint.setAlpha(10);
+            mBluePaint.setColor(0x7f0000ff);
+            canvas.drawRect(0.0f, 0.0f, 50.0f, 50.0f, mBluePaint);
+            
+            mBluePaint.setColor(0xff0000ff);
+            mBluePaint.setAlpha(255);
+
+            canvas.restore();
+        }
+
+        private void drawRects(Canvas canvas, PorterDuff.Mode mode) {
+            canvas.drawRect(0.0f, 0.0f, 50.0f, 50.0f, mBluePaint);
+
+            canvas.save();
+            canvas.translate(25.0f, 25.0f);
+            mRedPaint.setXfermode(new PorterDuffXfermode(mode));
+            canvas.drawRect(0.0f, 0.0f, 50.0f, 50.0f, mRedPaint);
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/StatusBar/res/drawable-hdpi/app_gmail.png b/tests/StatusBar/res/drawable-hdpi/app_gmail.png
new file mode 100644
index 0000000..23d33e5
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/app_gmail.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/app_phone.png b/tests/StatusBar/res/drawable-hdpi/app_phone.png
new file mode 100644
index 0000000..772e7d3
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/app_phone.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/ic_statusbar_chat.png b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_chat.png
new file mode 100644
index 0000000..e3b4e2b
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_chat.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/ic_statusbar_email.png b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_email.png
new file mode 100644
index 0000000..8cefd36
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_email.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/ic_statusbar_missedcall.png b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_missedcall.png
new file mode 100644
index 0000000..4feec28
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/ic_statusbar_missedcall.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/icon1.png b/tests/StatusBar/res/drawable-hdpi/icon1.png
new file mode 100644
index 0000000..0485257
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/icon1.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/icon2.png b/tests/StatusBar/res/drawable-hdpi/icon2.png
new file mode 100644
index 0000000..a630986
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/icon2.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/icon3.png b/tests/StatusBar/res/drawable-hdpi/icon3.png
new file mode 100644
index 0000000..9d72b66
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/icon3.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-hdpi/icon4.png b/tests/StatusBar/res/drawable-hdpi/icon4.png
new file mode 100644
index 0000000..a4a40f2
--- /dev/null
+++ b/tests/StatusBar/res/drawable-hdpi/icon4.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/app_gmail.png b/tests/StatusBar/res/drawable-mdpi/app_gmail.png
similarity index 100%
rename from tests/StatusBar/res/drawable/app_gmail.png
rename to tests/StatusBar/res/drawable-mdpi/app_gmail.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/app_phone.png b/tests/StatusBar/res/drawable-mdpi/app_phone.png
similarity index 100%
rename from tests/StatusBar/res/drawable/app_phone.png
rename to tests/StatusBar/res/drawable-mdpi/app_phone.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/ic_statusbar_chat.png b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_chat.png
similarity index 100%
rename from tests/StatusBar/res/drawable/ic_statusbar_chat.png
rename to tests/StatusBar/res/drawable-mdpi/ic_statusbar_chat.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/ic_statusbar_email.png b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_email.png
similarity index 100%
rename from tests/StatusBar/res/drawable/ic_statusbar_email.png
rename to tests/StatusBar/res/drawable-mdpi/ic_statusbar_email.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/ic_statusbar_missedcall.png b/tests/StatusBar/res/drawable-mdpi/ic_statusbar_missedcall.png
similarity index 100%
rename from tests/StatusBar/res/drawable/ic_statusbar_missedcall.png
rename to tests/StatusBar/res/drawable-mdpi/ic_statusbar_missedcall.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/icon1.png b/tests/StatusBar/res/drawable-mdpi/icon1.png
new file mode 100644
index 0000000..a999834
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/icon1.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/icon2.png b/tests/StatusBar/res/drawable-mdpi/icon2.png
new file mode 100644
index 0000000..b7763cb
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/icon2.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/icon3.png b/tests/StatusBar/res/drawable-mdpi/icon3.png
similarity index 100%
rename from tests/StatusBar/res/drawable/icon3.png
rename to tests/StatusBar/res/drawable-mdpi/icon3.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/icon4.png b/tests/StatusBar/res/drawable-mdpi/icon4.png
similarity index 100%
rename from tests/StatusBar/res/drawable/icon4.png
rename to tests/StatusBar/res/drawable-mdpi/icon4.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification0.png b/tests/StatusBar/res/drawable-mdpi/notification0.png
new file mode 100644
index 0000000..39794f3
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification0.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification1.png b/tests/StatusBar/res/drawable-mdpi/notification1.png
new file mode 100644
index 0000000..b7bbe2b
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification1.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification2.png b/tests/StatusBar/res/drawable-mdpi/notification2.png
new file mode 100644
index 0000000..b014280
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification2.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification3.png b/tests/StatusBar/res/drawable-mdpi/notification3.png
new file mode 100644
index 0000000..2c618211
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification3.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification4.png b/tests/StatusBar/res/drawable-mdpi/notification4.png
new file mode 100644
index 0000000..1e04793
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification4.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification5.png b/tests/StatusBar/res/drawable-mdpi/notification5.png
new file mode 100644
index 0000000..f05dfe5
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification5.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification6.png b/tests/StatusBar/res/drawable-mdpi/notification6.png
new file mode 100644
index 0000000..b807181
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification6.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification7.png b/tests/StatusBar/res/drawable-mdpi/notification7.png
new file mode 100644
index 0000000..63b2544
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification7.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification8.png b/tests/StatusBar/res/drawable-mdpi/notification8.png
new file mode 100644
index 0000000..13c1499
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification8.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notification9.png b/tests/StatusBar/res/drawable-mdpi/notification9.png
new file mode 100644
index 0000000..587d04c
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notification9.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable-mdpi/notificationx.png b/tests/StatusBar/res/drawable-mdpi/notificationx.png
new file mode 100644
index 0000000..4df9804
--- /dev/null
+++ b/tests/StatusBar/res/drawable-mdpi/notificationx.png
Binary files differ
diff --git a/tests/StatusBar/res/drawable/icon1.png b/tests/StatusBar/res/drawable/icon1.png
deleted file mode 100644
index abfb6fa..0000000
--- a/tests/StatusBar/res/drawable/icon1.png
+++ /dev/null
Binary files differ
diff --git a/tests/StatusBar/res/drawable/icon2.png b/tests/StatusBar/res/drawable/icon2.png
deleted file mode 100644
index 564b38b..0000000
--- a/tests/StatusBar/res/drawable/icon2.png
+++ /dev/null
Binary files differ
diff --git a/tests/StatusBar/res/layout/button_notification.xml b/tests/StatusBar/res/layout/button_notification.xml
new file mode 100644
index 0000000..822e8cd
--- /dev/null
+++ b/tests/StatusBar/res/layout/button_notification.xml
@@ -0,0 +1,21 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="80sp"
+    android:orientation="horizontal"
+    >
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:src="@drawable/icon1"
+        />
+
+    <Button android:id="@+id/button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="button"
+        />
+
+</LinearLayout>
+
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index b793d62..2df97dc 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -16,12 +16,14 @@
 
 package com.android.statusbartest;
 
+import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.ContentResolver;
 import android.content.Intent;
-import android.app.Notification;
-import android.app.NotificationManager;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import android.os.Environment;
 import android.os.Vibrator;
 import android.os.Handler;
@@ -42,6 +44,20 @@
     long mActivityCreateTime = System.currentTimeMillis();
     long mChronometerBase = 0;
 
+    final int[] kNumberedIconResIDs = {
+        R.drawable.notification0,
+        R.drawable.notification1,
+        R.drawable.notification2,
+        R.drawable.notification3,
+        R.drawable.notification4,
+        R.drawable.notification5,
+        R.drawable.notification6,
+        R.drawable.notification7,
+        R.drawable.notification8,
+        R.drawable.notification9
+    };
+    final int kUnnumberedIconResID = R.drawable.notificationx;
+
     @Override
     protected String tag() {
         return TAG;
@@ -77,6 +93,137 @@
             }
         },
 
+        new Test("Cancel #1") {
+            public void run()
+            {
+                mNM.cancel(1);
+            }
+        },
+
+        new Test("Button") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, null,
+                        mActivityCreateTime);
+                n.contentView = new RemoteViews(getPackageName(), R.layout.button_notification);
+                n.flags |= Notification.FLAG_ONGOING_EVENT;
+                n.contentIntent = makeIntent();
+                n.contentView.setOnClickPendingIntent(R.id.button, makeIntent2());
+
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("custom intent on text view") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, null,
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", null);
+                n.contentView.setOnClickPendingIntent(com.android.internal.R.id.text,
+                        makeIntent2());
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker 1 line") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, "tick tick tick",
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker 1 line & icon") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, "tick tick tick",
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                n.tickerIcons = new Bitmap[1];
+                n.tickerIcons[0] = loadBitmap(R.drawable.icon3);
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker 2 lines") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, "tick tick tick\ntock tock",
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker title") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, null,
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                n.tickerTitle = "This is a title";
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker subtitle") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, null,
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                n.tickerSubtitle = "and a subtitle";
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker title & subtitle") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, null,
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                n.tickerTitle = "This is a title it is really really longggggg long long long long";
+                n.tickerSubtitle = "and a subtitle it is really really longggggg long long long long long long long long long long long long long long long long";
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker text, title & subtitle") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, "not visible",
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                n.tickerTitle = "This is a title";
+                n.tickerSubtitle = "and a subtitle";
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker title, subtitle & 2 icons") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, null,
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                n.tickerTitle = "This is a title";
+                n.tickerSubtitle = "and a subtitle";
+
+                n.tickerIcons = new Bitmap[2];
+                n.tickerIcons[0] = loadBitmap(R.drawable.icon3);
+                n.tickerIcons[1] = loadBitmap(R.drawable.app_gmail);
+
+                mNM.notify(1, n);
+                /*
+                n.tickerIcons[0].recycle();
+                n.tickerIcons[1].recycle();
+                */
+            }
+        },
+
         new Test("No view") {
             public void run() {
                 Notification n = new Notification(R.drawable.icon1, "No view",
@@ -98,12 +245,16 @@
         new Test("Layout") {
             public void run()
             {
+                Notification n;
 
-                mNM.notify(1, new Notification(NotificationTestList.this,
+                n = new Notification(NotificationTestList.this,
                             R.drawable.ic_statusbar_missedcall,
                             null, System.currentTimeMillis()-(1000*60*60*24),
                             "(453) 123-2328",
-                            "", null));
+                            "", null);
+                n.flags |= Notification.FLAG_ONGOING_EVENT;
+
+                mNM.notify(1, n);
 
                 mNM.notify(2, new Notification(NotificationTestList.this,
                             R.drawable.ic_statusbar_email,
@@ -479,7 +630,7 @@
 
         new Test("Persistent #3") {
             public void run() {
-                Notification n = new Notification(R.drawable.icon2, "tock tock tock",
+                Notification n = new Notification(R.drawable.icon2, "tock tock tock\nmooooo",
                         System.currentTimeMillis());
                 n.setLatestEventInfo(NotificationTestList.this, "Persistent #3",
                             "Notify me!!!", makeIntent());
@@ -584,7 +735,8 @@
         new Test("Ten Notifications") {
             public void run() {
                 for (int i = 0; i < 2; i++) {
-                    Notification n = new Notification(NotificationTestList.this, R.drawable.icon2,
+                    Notification n = new Notification(NotificationTestList.this,
+                            kNumberedIconResIDs[i],
                             null, System.currentTimeMillis(), "Persistent #" + i,
                             "Notify me!!!" + i, null);
                     n.flags |= Notification.FLAG_ONGOING_EVENT;
@@ -592,7 +744,8 @@
                     mNM.notify((i+1)*10, n);
                 }
                 for (int i = 2; i < 10; i++) {
-                    Notification n = new Notification(NotificationTestList.this, R.drawable.icon2,
+                    Notification n = new Notification(NotificationTestList.this,
+                            kNumberedIconResIDs[i],
                             null, System.currentTimeMillis(), "Persistent #" + i,
                             "Notify me!!!" + i, null);
                     n.number = i;
@@ -609,6 +762,13 @@
             }
         },
         
+        new Test("Cancel the other two notifications") {
+            public void run() {
+                mNM.cancel(10);
+                mNM.cancel(100);
+            }
+        },
+        
         new Test("Persistent with numbers 1") {
             public void run() {
                 mNM.notify(1, notificationWithNumbers(1));
@@ -633,20 +793,6 @@
             }
         },
 
-        new Test("Ticker") {
-            public void run() {
-                Notification not = new Notification(
-                    R.drawable.app_gmail, 
-                    "New mail from joeo@example.com, on the topic of very long ticker texts",
-                    System.currentTimeMillis());
-                not.setLatestEventInfo(NotificationTestList.this,
-                    "A new message awaits",
-                    "The contents are very interesting and important",
-                    makeIntent());
-                mNM.notify(1, not);
-            }
-        },
-
         new Test("Crash") {
             public void run()
             {
@@ -666,8 +812,14 @@
     };
 
     private Notification notificationWithNumbers(int num) {
-        Notification n = new Notification(this, R.drawable.icon2, null, System.currentTimeMillis(),
-                "Persistent #2", "Notify me!!!", null);
+        Notification n = new Notification(this,
+                (num >= 0 && num < kNumberedIconResIDs.length)
+                    ? kNumberedIconResIDs[num]
+                    : kUnnumberedIconResID,
+                null,
+                System.currentTimeMillis(),
+                "Notification", "Number=" + num,
+                null);
         n.number = num;
         return n;
     }
@@ -678,6 +830,12 @@
         return PendingIntent.getActivity(this, 0, intent, 0);
     }
 
+    private PendingIntent makeIntent2() {
+        Intent intent = new Intent(this, StatusBarTest.class);
+        return PendingIntent.getActivity(this, 0, intent, 0);
+    }
+
+
     class StateStress extends Test {
         StateStress(String name, int pause, int iterations, Runnable[] tasks) {
             super(name);
@@ -715,5 +873,10 @@
                     time, label, "" + new java.util.Date(time), null));
 
     }
+
+    Bitmap loadBitmap(int resId) {
+        BitmapDrawable bd = (BitmapDrawable)getResources().getDrawable(resId);
+        return Bitmap.createBitmap(bd.getBitmap());
+    }
 }
 
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 438a060..7e69ee4 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -81,23 +81,32 @@
                 mStatusBarManager.setIcon("speakerphone", R.drawable.stat_sys_phone, 0);
             }
         },
-        new Test("Hide") {
+        new Test("Hide (FLAG_FULLSCREEN)") {
             public void run() {
                 Window win = getWindow();
-                WindowManager.LayoutParams winParams = win.getAttributes();
-                winParams.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
-                win.setAttributes(winParams);
+                win.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+                        WindowManager.LayoutParams.FLAG_FULLSCREEN);
+                Log.d(TAG, "flags=" + Integer.toHexString(win.getAttributes().flags));
             }
         },
-        new Test("Show") {
+        new Test("Show (~FLAG_FULLSCREEN)") {
             public void run() {
                 Window win = getWindow();
-                WindowManager.LayoutParams winParams = win.getAttributes();
-                winParams.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
-                win.setAttributes(winParams);
+                win.setFlags(0, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+                Log.d(TAG, "flags=" + Integer.toHexString(win.getAttributes().flags));
             }
         },
-        new Test("fullScreenIntent") {
+        new Test("Immersive: Enter") {
+            public void run() {
+                setImmersive(true);
+            }
+        },
+        new Test("Immersive: Exit") {
+            public void run() {
+                setImmersive(false);
+            }
+        },
+        new Test("Priority notification") {
             public void run() {
                 Notification not = new Notification(StatusBarTest.this,
                                 R.drawable.stat_sys_phone,
@@ -107,8 +116,9 @@
                                 "(888) 555-5038",
                                 null
                                 );
+                not.flags |= Notification.FLAG_HIGH_PRIORITY;
                 Intent fullScreenIntent = new Intent(StatusBarTest.this, TestAlertActivity.class);
-                int id = (int)System.currentTimeMillis();
+                int id = (int)System.currentTimeMillis(); // XXX HAX
                 fullScreenIntent.putExtra("id", id);
                 not.fullScreenIntent = PendingIntent.getActivity(
                     StatusBarTest.this,
@@ -158,6 +168,24 @@
                     }, 3000);
             }
         },
+        new Test("Disable Navigation") {
+            public void run() {
+                mStatusBarManager.disable(StatusBarManager.DISABLE_NAVIGATION);
+            }
+        },
+        new Test("Disable everything in 3 sec") {
+            public void run() {
+                mHandler.postDelayed(new Runnable() {
+                        public void run() {
+                            mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND
+                                    | StatusBarManager.DISABLE_NOTIFICATION_ICONS
+                                    | StatusBarManager.DISABLE_NOTIFICATION_ALERTS
+                                    | StatusBarManager.DISABLE_SYSTEM_INFO
+                                    | StatusBarManager.DISABLE_NAVIGATION);
+                        }
+                    }, 3000);
+            }
+        },
         new Test("Enable everything") {
             public void run() {
                 mStatusBarManager.disable(0);
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 661ecb1..16fa95a 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -198,8 +198,10 @@
         if (&res == NULL) {
             printf("\nNo resource table found.\n");
         } else {
+#ifndef HAVE_ANDROID_OS
             printf("\nResource table:\n");
             res.print(false);
+#endif
         }
 
         Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
@@ -428,8 +430,9 @@
     }
 
     if (strcmp("resources", option) == 0) {
+#ifndef HAVE_ANDROID_OS
         res.print(bundle->getValues());
-
+#endif
     } else if (strcmp("xmltree", option) == 0) {
         if (bundle->getFileSpecCount() < 3) {
             fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 999a5cf..3cb614f 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -441,7 +441,7 @@
 
 ssize_t processJarFiles(Bundle* bundle, ZipFile* zip)
 {
-    ssize_t err;
+    status_t err;
     ssize_t count = 0;
     const android::Vector<const char*>& jars = bundle->getJarFiles();
 
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index c8ba904..822262e 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -542,11 +542,11 @@
                         DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
                                 baseGroup->getFiles();
                         for (size_t i=0; i < baseFiles.size(); i++) {
-                            printf("baseFile %ld has flavor %s\n", i,
+                            printf("baseFile %d has flavor %s\n", i,
                                     baseFiles.keyAt(i).toString().string());
                         }
                         for (size_t i=0; i < overlayFiles.size(); i++) {
-                            printf("overlayFile %ld has flavor %s\n", i,
+                            printf("overlayFile %d has flavor %s\n", i,
                                     overlayFiles.keyAt(i).toString().string());
                         }
                     }
@@ -560,7 +560,7 @@
                                 keyAt(overlayGroupIndex));
                         if(baseFileIndex < UNKNOWN_ERROR) {
                             if (bundle->getVerbose()) {
-                                printf("found a match (%ld) for overlay file %s, for flavor %s\n",
+                                printf("found a match (%d) for overlay file %s, for flavor %s\n",
                                         baseFileIndex,
                                         overlayGroup->getLeaf().string(),
                                         overlayFiles.keyAt(overlayGroupIndex).toString().string());
@@ -2083,12 +2083,13 @@
     // tag:attribute pairs that should be checked in layout files.
     KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs;
     addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
+    addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class");
     addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
 
     // tag:attribute pairs that should be checked in xml files.
     KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs;
     addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
-    addTagAttrPair(&kXmlTagAttrPairs, "Header", RESOURCES_ANDROID_NAMESPACE, "fragment");
+    addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
 
     const Vector<sp<AaptDir> >& dirs = assets->resDirs();
     const size_t K = dirs.size();
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index fc576a6..a77042a 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2428,7 +2428,7 @@
         if (configSet.count(defaultLocale) == 0) {
             fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
                     String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
-            for (set<String8>::iterator locales = configSet.begin();
+            for (set<String8>::const_iterator locales = configSet.begin();
                  locales != configSet.end();
                  locales++) {
                 fprintf(stdout, " %s", (*locales).string());
diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp
index a09cec0..e28bdff 100644
--- a/tools/aapt/StringPool.cpp
+++ b/tools/aapt/StringPool.cpp
@@ -30,7 +30,7 @@
             str = String8(pool->stringAt(s, &len)).string();
         }
 
-        printf("String #%ld: %s\n", s, str);
+        printf("String #%d: %s\n", s, str);
     }
 }
 
diff --git a/tools/aapt/ZipFile.h b/tools/aapt/ZipFile.h
index dbbd072..7877550 100644
--- a/tools/aapt/ZipFile.h
+++ b/tools/aapt/ZipFile.h
@@ -57,7 +57,7 @@
     /*
      * Open a new or existing archive.
      */
-    typedef enum {
+    enum {
         kOpenReadOnly   = 0x01,
         kOpenReadWrite  = 0x02,
         kOpenCreate     = 0x04,     // create if it doesn't exist
diff --git a/tools/layoutlib/README b/tools/layoutlib/README
new file mode 100644
index 0000000..0fea9bd
--- /dev/null
+++ b/tools/layoutlib/README
@@ -0,0 +1,4 @@
+Layoutlib is a custom version of the android View framework designed to run inside Eclipse.
+The goal of the library is to provide layout rendering in Eclipse that are very very close to their rendering on devices.
+
+None of the com.android.* or android.* classes in layoutlib run on devices.
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath
index 70140d8..aeeffa6 100644
--- a/tools/layoutlib/bridge/.classpath
+++ b/tools/layoutlib/bridge/.classpath
@@ -6,7 +6,7 @@
 	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
 	<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/layoutlib_api/layoutlib_api-prebuilt.jar"/>
 	<classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/dalvik/libcore/xml/src/main/java"/>
-	<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/layoutlib.jar" sourcepath="/ANDROID_SRC/frameworks/base/core/java"/>
 	<classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/ninepatch.jar" sourcepath="/ANDROID_SRC/development/tools/ninepatch/src"/>
+	<classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
deleted file mode 100644
index 35f022e..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.imageio.ImageIO;
-
-public final class Bitmap extends _Original_Bitmap {
-
-    private BufferedImage mImage;
-
-    public Bitmap(File input) throws IOException {
-        super(1, true, null, -1);
-
-        mImage = ImageIO.read(input);
-    }
-
-    public Bitmap(InputStream is) throws IOException {
-        super(1, true, null, -1);
-
-        mImage = ImageIO.read(is);
-    }
-
-    Bitmap(BufferedImage image) {
-        super(1, true, null, -1);
-        mImage = image;
-    }
-
-    public BufferedImage getImage() {
-        return mImage;
-    }
-
-    // ----- overriden methods
-
-    public enum Config {
-        // these native values must match up with the enum in SkBitmap.h
-        ALPHA_8     (2),
-        RGB_565     (4),
-        ARGB_4444   (5),
-        ARGB_8888   (6);
-
-        Config(int ni) {
-            this.nativeInt = ni;
-        }
-        final int nativeInt;
-
-        /* package */ static Config nativeToConfig(int ni) {
-            return sConfigs[ni];
-        }
-
-        private static Config sConfigs[] = {
-            null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888
-        };
-    }
-
-    @Override
-    public int getWidth() {
-        return mImage.getWidth();
-    }
-
-    @Override
-    public int getHeight() {
-        return mImage.getHeight();
-    }
-
-    /**
-     * Returns an immutable bitmap from the source bitmap. The new bitmap may
-     * be the same object as source, or a copy may have been made.
-     */
-    public static Bitmap createBitmap(Bitmap src) {
-        return createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), null, false);
-    }
-
-    /**
-     * Returns an immutable bitmap from the specified subset of the source
-     * bitmap. The new bitmap may be the same object as source, or a copy may
-     * have been made.
-     *
-     * @param source   The bitmap we are subsetting
-     * @param x        The x coordinate of the first pixel in source
-     * @param y        The y coordinate of the first pixel in source
-     * @param width    The number of pixels in each row
-     * @param height   The number of rows
-     */
-    public static Bitmap createBitmap(Bitmap source, int x, int y,
-                                      int width, int height) {
-        return new Bitmap(source.mImage.getSubimage(x, y, width, height));
-    }
-
-    /**
-     * Returns an immutable bitmap from subset of the source bitmap,
-     * transformed by the optional matrix.
-     *
-     * @param source   The bitmap we are subsetting
-     * @param x        The x coordinate of the first pixel in source
-     * @param y        The y coordinate of the first pixel in source
-     * @param width    The number of pixels in each row
-     * @param height   The number of rows
-     * @param m        Option matrix to be applied to the pixels
-     * @param filter   true if the source should be filtered.
-     *                   Only applies if the matrix contains more than just
-     *                   translation.
-     * @return A bitmap that represents the specified subset of source
-     * @throws IllegalArgumentException if the x, y, width, height values are
-     *         outside of the dimensions of the source bitmap.
-     */
-    public static Bitmap createBitmap(Bitmap source, int x, int y, int width,
-                                      int height, Matrix m, boolean filter) {
-        checkXYSign(x, y);
-        checkWidthHeight(width, height);
-        if (x + width > source.getWidth()) {
-            throw new IllegalArgumentException(
-                    "x + width must be <= bitmap.width()");
-        }
-        if (y + height > source.getHeight()) {
-            throw new IllegalArgumentException(
-                    "y + height must be <= bitmap.height()");
-        }
-
-        // check if we can just return our argument unchanged
-        if (!source.isMutable() && x == 0 && y == 0
-                && width == source.getWidth() && height == source.getHeight()
-                && (m == null || m.isIdentity())) {
-            return source;
-        }
-
-        if (m == null || m.isIdentity()) {
-            return new Bitmap(source.mImage.getSubimage(x, y, width, height));
-        }
-
-        int neww = width;
-        int newh = height;
-        Paint paint;
-
-        Rect srcR = new Rect(x, y, x + width, y + height);
-        RectF dstR = new RectF(0, 0, width, height);
-
-        /*  the dst should have alpha if the src does, or if our matrix
-            doesn't preserve rectness
-        */
-        boolean hasAlpha = source.hasAlpha() || !m.rectStaysRect();
-        RectF deviceR = new RectF();
-        m.mapRect(deviceR, dstR);
-        neww = Math.round(deviceR.width());
-        newh = Math.round(deviceR.height());
-
-        Canvas canvas = new Canvas(neww, newh);
-
-        canvas.translate(-deviceR.left, -deviceR.top);
-        canvas.concat(m);
-        paint = new Paint();
-        paint.setFilterBitmap(filter);
-        if (!m.rectStaysRect()) {
-            paint.setAntiAlias(true);
-        }
-
-        canvas.drawBitmap(source, srcR, dstR, paint);
-
-        return new Bitmap(canvas.getImage());
-    }
-
-    /**
-     * Returns a mutable bitmap with the specified width and height.
-     *
-     * @param width    The width of the bitmap
-     * @param height   The height of the bitmap
-     * @param config   The bitmap config to create.
-     * @throws IllegalArgumentException if the width or height are <= 0
-     */
-    public static Bitmap createBitmap(int width, int height, Config config) {
-        return new Bitmap(new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB));
-    }
-
-    /**
-     * Returns a immutable bitmap with the specified width and height, with each
-     * pixel value set to the corresponding value in the colors array.
-     *
-     * @param colors   Array of {@link Color} used to initialize the pixels.
-     * @param offset   Number of values to skip before the first color in the
-     *                 array of colors.
-     * @param stride   Number of colors in the array between rows (must be >=
-     *                 width or <= -width).
-     * @param width    The width of the bitmap
-     * @param height   The height of the bitmap
-     * @param config   The bitmap config to create. If the config does not
-     *                 support per-pixel alpha (e.g. RGB_565), then the alpha
-     *                 bytes in the colors[] will be ignored (assumed to be FF)
-     * @throws IllegalArgumentException if the width or height are <= 0, or if
-     *         the color array's length is less than the number of pixels.
-     */
-    public static Bitmap createBitmap(int colors[], int offset, int stride,
-                                      int width, int height, Config config) {
-        checkWidthHeight(width, height);
-        if (Math.abs(stride) < width) {
-            throw new IllegalArgumentException("abs(stride) must be >= width");
-        }
-        int lastScanline = offset + (height - 1) * stride;
-        int length = colors.length;
-        if (offset < 0 || (offset + width > length)
-            || lastScanline < 0
-            || (lastScanline + width > length)) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-
-        // TODO: create an immutable bitmap...
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Returns a immutable bitmap with the specified width and height, with each
-     * pixel value set to the corresponding value in the colors array.
-     *
-     * @param colors   Array of {@link Color} used to initialize the pixels.
-     *                 This array must be at least as large as width * height.
-     * @param width    The width of the bitmap
-     * @param height   The height of the bitmap
-     * @param config   The bitmap config to create. If the config does not
-     *                 support per-pixel alpha (e.g. RGB_565), then the alpha
-     *                 bytes in the colors[] will be ignored (assumed to be FF)
-     * @throws IllegalArgumentException if the width or height are <= 0, or if
-     *         the color array's length is less than the number of pixels.
-     */
-    public static Bitmap createBitmap(int colors[], int width, int height,
-                                      Config config) {
-        return createBitmap(colors, 0, width, width, height, config);
-    }
-
-    public static Bitmap createScaledBitmap(Bitmap src, int dstWidth,
-            int dstHeight, boolean filter) {
-        Matrix m;
-        synchronized (Bitmap.class) {
-            // small pool of just 1 matrix
-            m = sScaleMatrix;
-            sScaleMatrix = null;
-        }
-
-        if (m == null) {
-            m = new Matrix();
-        }
-
-        final int width = src.getWidth();
-        final int height = src.getHeight();
-        final float sx = dstWidth  / (float)width;
-        final float sy = dstHeight / (float)height;
-        m.setScale(sx, sy);
-        Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter);
-
-        synchronized (Bitmap.class) {
-            // do we need to check for null? why not just assign everytime?
-            if (sScaleMatrix == null) {
-                sScaleMatrix = m;
-            }
-        }
-
-        return b;
-    }
-
-
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
index e978fe8..6fd59c4 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
+
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.util.DisplayMetrics;
@@ -454,7 +456,7 @@
             // into is.read(...) This number is not related to the value passed
             // to mark(...) above.
             try {
-                bm = new Bitmap(is);
+                bm = Bitmap_Delegate.createBitmap(is, Density.MEDIUM);
             } catch (IOException e) {
                 return null;
             }
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
deleted file mode 100644
index ad3974c..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import java.awt.Paint;
-
-public class BitmapShader extends Shader {
-
-    // we hold on just for the GC, since our native counterpart is using it
-    private final Bitmap mBitmap;
-
-    /**
-     * Call this to create a new shader that will draw with a bitmap.
-     *
-     * @param bitmap            The bitmap to use inside the shader
-     * @param tileX             The tiling mode for x to draw the bitmap in.
-     * @param tileY             The tiling mode for y to draw the bitmap in.
-     */
-    public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY) {
-        mBitmap = bitmap;
-    }
-
-    //---------- Custom methods
-
-    public Bitmap getBitmap() {
-        return mBitmap;
-    }
-
-    @Override
-    Paint getJavaPaint() {
-        return null;
-    }
-}
-
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
new file mode 100644
index 0000000..e97b1e6
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
+import com.android.layoutlib.bridge.DelegateManager;
+
+import android.graphics.Bitmap.Config;
+import android.os.Parcel;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.Buffer;
+
+import javax.imageio.ImageIO;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Bitmap
+ *
+ * Through the layoutlib_create tool, the original native methods of Bitmap have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Bitmap class.
+ *
+ * @see DelegateManager
+ *
+ */
+public class Bitmap_Delegate {
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Bitmap_Delegate> sManager =
+            new DelegateManager<Bitmap_Delegate>();
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+    private BufferedImage mImage;
+    private boolean mHasAlpha = true;
+
+    // ---- Public Helper methods ----
+
+    /**
+     * Returns the native delegate associated to a given {@link Bitmap_Delegate} object.
+     */
+    public static Bitmap_Delegate getDelegate(Bitmap bitmap) {
+        return sManager.getDelegate(bitmap.mNativeBitmap);
+    }
+
+    /**
+     * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object.
+     */
+    public static Bitmap_Delegate getDelegate(int native_bitmap) {
+        return sManager.getDelegate(native_bitmap);
+    }
+
+    /**
+     * Creates and returns a {@link Bitmap} initialized with the given file content.
+     */
+    public static Bitmap createBitmap(File input, Density density) throws IOException {
+        // create a delegate with the content of the file.
+        Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input));
+
+        return createBitmap(delegate, density.getValue());
+    }
+
+    /**
+     * Creates and returns a {@link Bitmap} initialized with the given stream content.
+     */
+    public static Bitmap createBitmap(InputStream input, Density density) throws IOException {
+        // create a delegate with the content of the stream.
+        Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input));
+
+        return createBitmap(delegate, density.getValue());
+    }
+
+    /**
+     * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
+     */
+    public static Bitmap createBitmap(BufferedImage image, Density density) throws IOException {
+        // create a delegate with the given image.
+        Bitmap_Delegate delegate = new Bitmap_Delegate(image);
+
+        return createBitmap(delegate, density.getValue());
+    }
+
+    /**
+     * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
+     */
+    public static BufferedImage getImage(Bitmap bitmap) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(bitmap.mNativeBitmap);
+        if (delegate == null) {
+            assert false;
+            return null;
+        }
+
+        return delegate.mImage;
+    }
+
+    public static int getBufferedImageType(int nativeBitmapConfig) {
+        switch (Config.sConfigs[nativeBitmapConfig]) {
+            case ALPHA_8:
+                return BufferedImage.TYPE_INT_ARGB;
+            case RGB_565:
+                return BufferedImage.TYPE_INT_ARGB;
+            case ARGB_4444:
+                return BufferedImage.TYPE_INT_ARGB;
+            case ARGB_8888:
+                return BufferedImage.TYPE_INT_ARGB;
+        }
+
+        return BufferedImage.TYPE_INT_ARGB;
+    }
+
+    /**
+     * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
+     */
+    public BufferedImage getImage() {
+        return mImage;
+    }
+
+    // ---- native methods ----
+
+    /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
+            int height, int nativeConfig, boolean mutable) {
+        int imageType = getBufferedImageType(nativeConfig);
+
+        // create the image
+        BufferedImage image = new BufferedImage(width, height, imageType);
+
+        // FIXME fill the bitmap!
+
+        // create a delegate with the content of the stream.
+        Bitmap_Delegate delegate = new Bitmap_Delegate(image);
+
+        return createBitmap(delegate, Bitmap.getDefaultDensity());
+    }
+
+    /*package*/ static Bitmap nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap");
+    }
+
+    /*package*/ static void nativeDestructor(int nativeBitmap) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap");
+    }
+
+    /*package*/ static void nativeRecycle(int nativeBitmap) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap");
+    }
+
+    /*package*/ static boolean nativeCompress(int nativeBitmap, int format, int quality,
+            OutputStream stream, byte[] tempStorage) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap");
+    }
+
+    /*package*/ static void nativeErase(int nativeBitmap, int color) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        BufferedImage image = delegate.mImage;
+
+        Graphics2D g = image.createGraphics();
+        try {
+            if (delegate.mHasAlpha == false) {
+                color |= color & 0xFF000000;
+            }
+            g.setColor(new java.awt.Color(color));
+
+            g.fillRect(0, 0, image.getWidth(), image.getHeight());
+        } finally {
+            g.dispose();
+        }
+    }
+
+    /*package*/ static int nativeWidth(int nativeBitmap) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mImage.getWidth();
+    }
+
+    /*package*/ static int nativeHeight(int nativeBitmap) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mImage.getHeight();
+    }
+
+    /*package*/ static int nativeRowBytes(int nativeBitmap) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mImage.getWidth();
+    }
+
+    /*package*/ static int nativeConfig(int nativeBitmap) {
+        return Config.ARGB_8888.nativeInt;
+    }
+
+    /*package*/ static boolean nativeHasAlpha(int nativeBitmap) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            assert false;
+            return true;
+        }
+
+        return delegate.mHasAlpha;
+    }
+
+    /*package*/ static int nativeGetPixel(int nativeBitmap, int x, int y) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mImage.getRGB(x, y);
+    }
+
+    /*package*/ static void nativeGetPixels(int nativeBitmap, int[] pixels, int offset,
+            int stride, int x, int y, int width, int height) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeGetPixels");
+    }
+
+
+    /*package*/ static void nativeSetPixel(int nativeBitmap, int x, int y, int color) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeSetPixel");
+    }
+
+    /*package*/ static void nativeSetPixels(int nativeBitmap, int[] colors, int offset,
+            int stride, int x, int y, int width, int height) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeSetPixels");
+    }
+
+    /*package*/ static void nativeCopyPixelsToBuffer(int nativeBitmap, Buffer dst) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeCopyPixelsToBuffer");
+    }
+
+    /*package*/ static void nativeCopyPixelsFromBuffer(int nb, Buffer src) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeCopyPixelsFromBuffer");
+    }
+
+    /*package*/ static int nativeGenerationId(int nativeBitmap) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeGenerationId");
+    }
+
+    /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeCreateFromParcel");
+    }
+
+    /*package*/ static boolean nativeWriteToParcel(int nativeBitmap, boolean isMutable,
+            int density, Parcel p) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeWriteToParcel");
+    }
+
+    /*package*/ static Bitmap nativeExtractAlpha(int nativeBitmap, int nativePaint,
+            int[] offsetXY) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeExtractAlpha");
+    }
+
+
+    /*package*/ static void nativePrepareToDraw(int nativeBitmap) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativePrepareToDraw");
+    }
+
+    /*package*/ static void nativeSetHasAlpha(int nativeBitmap, boolean hasAlpha) {
+        // get the delegate from the native int.
+        Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mHasAlpha = hasAlpha;
+    }
+
+    /*package*/ static boolean nativeSameAs(int nb0, int nb1) {
+        // FIXME implement native delegate
+        throw new UnsupportedOperationException("Native delegate needed for Bitmap.nativeSameAs");
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private Bitmap_Delegate(BufferedImage image) {
+        mImage = image;
+    }
+
+    private static Bitmap createBitmap(Bitmap_Delegate delegate, int density) {
+        // get its native_int
+        int nativeInt = sManager.addDelegate(delegate);
+
+        // and create/return a new Bitmap with it
+        return new Bitmap(nativeInt, true /*isMutable*/, null /*ninePatchChunk*/, density);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
deleted file mode 100644
index d5d315e..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ /dev/null
@@ -1,1279 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.api.ILayoutLog;
-
-import android.graphics.DrawFilter;
-import android.graphics.Picture;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.graphics.Xfermode;
-import android.graphics.Paint.Align;
-import android.graphics.Paint.FontInfo;
-import android.graphics.Paint.Style;
-import android.graphics.Region.Op;
-
-import java.awt.AlphaComposite;
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Composite;
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.geom.AffineTransform;
-import java.awt.image.BufferedImage;
-import java.util.List;
-import java.util.Stack;
-
-import javax.microedition.khronos.opengles.GL;
-
-/**
- * Re-implementation of the Canvas, 100% in java on top of a BufferedImage.
- */
-public class Canvas extends _Original_Canvas {
-
-    private BufferedImage mBufferedImage;
-    private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>();
-    private final ILayoutLog mLogger;
-
-    public Canvas() {
-        mLogger = null;
-        // the mBufferedImage will be taken from a bitmap in #setBitmap()
-    }
-
-    public Canvas(Bitmap bitmap) {
-        mLogger = null;
-        mBufferedImage = bitmap.getImage();
-        mGraphicsStack.push(mBufferedImage.createGraphics());
-    }
-
-    public Canvas(int nativeCanvas) {
-        mLogger = null;
-        throw new UnsupportedOperationException("Can't create Canvas(int)");
-    }
-
-    public Canvas(javax.microedition.khronos.opengles.GL gl) {
-        mLogger = null;
-        throw new UnsupportedOperationException("Can't create Canvas(javax.microedition.khronos.opengles.GL)");
-    }
-
-    // custom constructors for our use.
-    public Canvas(int width, int height, ILayoutLog logger) {
-        mLogger = logger;
-        mBufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-        mGraphicsStack.push(mBufferedImage.createGraphics());
-    }
-
-    public Canvas(int width, int height) {
-        this(width, height, null /* logger*/);
-    }
-
-    // custom mehtods
-    public BufferedImage getImage() {
-        return mBufferedImage;
-    }
-
-    public Graphics2D getGraphics2d() {
-        return mGraphicsStack.peek();
-    }
-
-    public void dispose() {
-        while (mGraphicsStack.size() > 0) {
-            mGraphicsStack.pop().dispose();
-        }
-    }
-
-    /**
-     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
-     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
-     */
-    private Graphics2D getCustomGraphics(Paint paint) {
-        // make new one
-        Graphics2D g = getGraphics2d();
-        g = (Graphics2D)g.create();
-
-        // configure it
-        g.setColor(new Color(paint.getColor()));
-        int alpha = paint.getAlpha();
-        float falpha = alpha / 255.f;
-
-        Style style = paint.getStyle();
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            PathEffect e = paint.getPathEffect();
-            if (e instanceof DashPathEffect) {
-                DashPathEffect dpe = (DashPathEffect)e;
-                g.setStroke(new BasicStroke(
-                        paint.getStrokeWidth(),
-                        paint.getStrokeCap().getJavaCap(),
-                        paint.getStrokeJoin().getJavaJoin(),
-                        paint.getStrokeMiter(),
-                        dpe.getIntervals(),
-                        dpe.getPhase()));
-            } else {
-                g.setStroke(new BasicStroke(
-                        paint.getStrokeWidth(),
-                        paint.getStrokeCap().getJavaCap(),
-                        paint.getStrokeJoin().getJavaJoin(),
-                        paint.getStrokeMiter()));
-            }
-        }
-
-        Xfermode xfermode = paint.getXfermode();
-        if (xfermode instanceof PorterDuffXfermode) {
-            PorterDuff.Mode mode = ((PorterDuffXfermode)xfermode).getMode();
-
-            setModeInGraphics(mode, g, falpha);
-        } else {
-            if (mLogger != null && xfermode != null) {
-                mLogger.warning(String.format(
-                        "Xfermode '%1$s' is not supported in the Layout Editor.",
-                        xfermode.getClass().getCanonicalName()));
-            }
-            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
-        }
-
-        Shader shader = paint.getShader();
-        if (shader != null) {
-            java.awt.Paint shaderPaint = shader.getJavaPaint();
-            if (shaderPaint != null) {
-                g.setPaint(shaderPaint);
-            } else {
-                if (mLogger != null) {
-                    mLogger.warning(String.format(
-                            "Shader '%1$s' is not supported in the Layout Editor.",
-                            shader.getClass().getCanonicalName()));
-                }
-            }
-        }
-
-        return g;
-    }
-
-    private void setModeInGraphics(PorterDuff.Mode mode, Graphics2D g, float falpha) {
-        switch (mode) {
-            case CLEAR:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha));
-                break;
-            case DARKEN:
-                break;
-            case DST:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST, falpha));
-                break;
-            case DST_ATOP:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha));
-                break;
-            case DST_IN:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha));
-                break;
-            case DST_OUT:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha));
-                break;
-            case DST_OVER:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha));
-                break;
-            case LIGHTEN:
-                break;
-            case MULTIPLY:
-                break;
-            case SCREEN:
-                break;
-            case SRC:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, falpha));
-                break;
-            case SRC_ATOP:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha));
-                break;
-            case SRC_IN:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha));
-                break;
-            case SRC_OUT:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha));
-                break;
-            case SRC_OVER:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
-                break;
-            case XOR:
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, falpha));
-                break;
-        }
-    }
-
-
-    // --------------------
-    // OVERRIDEN ENUMS
-    // This is needed since we rename Canvas into _Original_Canvas
-    // --------------------
-
-    public enum EdgeType {
-        BW(0),  //!< treat edges by just rounding to nearest pixel boundary
-        AA(1);  //!< treat edges by rounding-out, since they may be antialiased
-
-        EdgeType(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-
-    // --------------------
-    // OVERRIDEN METHODS
-    // --------------------
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#setBitmap(android.graphics.Bitmap)
-     */
-    @Override
-    public void setBitmap(Bitmap bitmap) {
-        mBufferedImage = bitmap.getImage();
-        mGraphicsStack.push(mBufferedImage.createGraphics());
-    }
-
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#translate(float, float)
-     */
-    @Override
-    public void translate(float dx, float dy) {
-        getGraphics2d().translate(dx, dy);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#save()
-     */
-    @Override
-    public int save() {
-        // get the current save count
-        int count = mGraphicsStack.size();
-
-        // create a new graphics and add it to the stack
-        Graphics2D g = (Graphics2D)getGraphics2d().create();
-        mGraphicsStack.push(g);
-
-        // return the old save count
-        return count;
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#save(int)
-     */
-    @Override
-    public int save(int saveFlags) {
-        // For now we ignore saveFlags
-        return save();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#restore()
-     */
-    @Override
-    public void restore() {
-        mGraphicsStack.pop();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#restoreToCount(int)
-     */
-    @Override
-    public void restoreToCount(int saveCount) {
-        while (mGraphicsStack.size() > saveCount) {
-            mGraphicsStack.pop();
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getSaveCount()
-     */
-    @Override
-    public int getSaveCount() {
-        return mGraphicsStack.size();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(float, float, float, float, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipRect(float left, float top, float right, float bottom, Op op) {
-        return clipRect(left, top, right, bottom);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(float, float, float, float)
-     */
-    @Override
-    public boolean clipRect(float left, float top, float right, float bottom) {
-        getGraphics2d().clipRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
-        return true;
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(int, int, int, int)
-     */
-    @Override
-    public boolean clipRect(int left, int top, int right, int bottom) {
-        getGraphics2d().clipRect(left, top, right-left, bottom-top);
-        return true;
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(android.graphics.Rect, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipRect(Rect rect, Op op) {
-        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(android.graphics.Rect)
-     */
-    @Override
-    public boolean clipRect(Rect rect) {
-        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(android.graphics.RectF, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipRect(RectF rect, Op op) {
-        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRect(android.graphics.RectF)
-     */
-    @Override
-    public boolean clipRect(RectF rect) {
-        return clipRect(rect.left, rect.top, rect.right, rect.bottom);
-    }
-
-    public boolean quickReject(RectF rect, EdgeType type) {
-        return false;
-    }
-
-    @Override
-    public boolean quickReject(RectF rect, _Original_Canvas.EdgeType type) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public boolean quickReject(Path path, EdgeType type) {
-        return false;
-    }
-
-    @Override
-    public boolean quickReject(Path path, _Original_Canvas.EdgeType type) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public boolean quickReject(float left, float top, float right, float bottom,
-                               EdgeType type) {
-        return false;
-    }
-
-    @Override
-    public boolean quickReject(float left, float top, float right, float bottom,
-                               _Original_Canvas.EdgeType type) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-     * Retrieve the clip bounds, returning true if they are non-empty.
-     *
-     * @param bounds Return the clip bounds here. If it is null, ignore it but
-     *               still return true if the current clip is non-empty.
-     * @return true if the current clip is non-empty.
-     */
-    @Override
-    public boolean getClipBounds(Rect bounds) {
-        Rectangle rect = getGraphics2d().getClipBounds();
-        if (rect != null) {
-            bounds.left = rect.x;
-            bounds.top = rect.y;
-            bounds.right = rect.x + rect.width;
-            bounds.bottom = rect.y + rect.height;
-            return true;
-        }
-        return false;
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawColor(int, android.graphics.PorterDuff.Mode)
-     */
-    @Override
-    public void drawColor(int color, PorterDuff.Mode mode) {
-        Graphics2D g = getGraphics2d();
-
-        // save old color
-        Color c = g.getColor();
-
-        Composite composite = g.getComposite();
-
-        // get the alpha from the color
-        int alpha = color >>> 24;
-        float falpha = alpha / 255.f;
-
-        setModeInGraphics(mode, g, falpha);
-
-        g.setColor(new Color(color));
-
-        g.fillRect(0, 0, getWidth(), getHeight());
-
-        g.setComposite(composite);
-
-        // restore color
-        g.setColor(c);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawColor(int)
-     */
-    @Override
-    public void drawColor(int color) {
-        drawColor(color, PorterDuff.Mode.SRC_OVER);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawARGB(int, int, int, int)
-     */
-    @Override
-    public void drawARGB(int a, int r, int g, int b) {
-        drawColor(a << 24 | r << 16 | g << 8 | b, PorterDuff.Mode.SRC_OVER);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRGB(int, int, int)
-     */
-    @Override
-    public void drawRGB(int r, int g, int b) {
-        drawColor(0xFF << 24 | r << 16 | g << 8 | b, PorterDuff.Mode.SRC_OVER);
-    }
-
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getWidth()
-     */
-    @Override
-    public int getWidth() {
-        return mBufferedImage.getWidth();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getHeight()
-     */
-    @Override
-    public int getHeight() {
-        return mBufferedImage.getHeight();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPaint(android.graphics.Paint)
-     */
-    @Override
-    public void drawPaint(Paint paint) {
-        drawColor(paint.getColor());
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
-        drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
-                (int)left, (int)top,
-                (int)left+bitmap.getWidth(), (int)top+bitmap.getHeight(), paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Matrix, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
-        boolean needsRestore = false;
-        if (matrix.isIdentity() == false) {
-            // create a new graphics and apply the matrix to it
-            save(); // this creates a new Graphics2D, and stores it for children call to use
-            needsRestore = true;
-            Graphics2D g = getGraphics2d(); // get the newly create Graphics2D
-
-            // get the Graphics2D current matrix
-            AffineTransform currentTx = g.getTransform();
-            // get the AffineTransform from the matrix
-            AffineTransform matrixTx = matrix.getTransform();
-
-            // combine them so that the matrix is applied after.
-            currentTx.preConcatenate(matrixTx);
-
-            // give it to the graphics as a new matrix replacing all previous transform
-            g.setTransform(currentTx);
-        }
-
-        // draw the bitmap
-        drawBitmap(bitmap, 0, 0, paint);
-
-        if (needsRestore) {
-            // remove the new graphics
-            restore();
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Rect, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
-        if (src == null) {
-            drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
-                    dst.left, dst.top, dst.right, dst.bottom, paint);
-        } else {
-            drawBitmap(bitmap, src.left, src.top, src.width(), src.height(),
-                    dst.left, dst.top, dst.right, dst.bottom, paint);
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, android.graphics.Rect, android.graphics.RectF, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
-        if (src == null) {
-            drawBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
-                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom, paint);
-        } else {
-            drawBitmap(bitmap, src.left, src.top, src.width(), src.height(),
-                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom, paint);
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmap(int[], int, int, int, int, int, int, boolean, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmap(int[] colors, int offset, int stride, int x, int y, int width,
-            int height, boolean hasAlpha, Paint paint) {
-        throw new UnsupportedOperationException();
-    }
-
-    private void drawBitmap(Bitmap bitmap, int sleft, int stop, int sright, int sbottom, int dleft,
-            int dtop, int dright, int dbottom, Paint paint) {
-        BufferedImage image = bitmap.getImage();
-
-        Graphics2D g = getGraphics2d();
-
-        Composite c = null;
-
-        if (paint != null) {
-            if (paint.isFilterBitmap()) {
-                g = (Graphics2D)g.create();
-                g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
-                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-            }
-
-            if (paint.getAlpha() != 0xFF) {
-                c = g.getComposite();
-                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
-                        paint.getAlpha()/255.f));
-            }
-        }
-
-        g.drawImage(image, dleft, dtop, dright, dbottom,
-                sleft, stop, sright, sbottom, null);
-
-        if (paint != null) {
-            if (paint.isFilterBitmap()) {
-                g.dispose();
-            }
-            if (c != null) {
-                g.setComposite(c);
-            }
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#rotate(float, float, float)
-     */
-    @Override
-    public void rotate(float degrees, float px, float py) {
-        if (degrees != 0) {
-            Graphics2D g = getGraphics2d();
-            g.translate(px, py);
-            g.rotate(Math.toRadians(degrees));
-            g.translate(-px, -py);
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#rotate(float)
-     */
-    @Override
-    public void rotate(float degrees) {
-        getGraphics2d().rotate(Math.toRadians(degrees));
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#scale(float, float, float, float)
-     */
-    @Override
-    public void scale(float sx, float sy, float px, float py) {
-        Graphics2D g = getGraphics2d();
-        g.translate(px, py);
-        g.scale(sx, sy);
-        g.translate(-px, -py);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#scale(float, float)
-     */
-    @Override
-    public void scale(float sx, float sy) {
-        getGraphics2d().scale(sx, sy);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawText(char[], int, int, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
-        // WARNING: the logic in this method is similar to Paint.measureText.
-        // Any change to this method should be reflected in Paint.measureText
-        Graphics2D g = getGraphics2d();
-
-        g = (Graphics2D)g.create();
-        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-
-        // set the color. because this only handles RGB, the alpha channel is handled
-        // as a composite.
-        g.setColor(new Color(paint.getColor()));
-        int alpha = paint.getAlpha();
-        float falpha = alpha / 255.f;
-        g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
-
-
-        // Paint.TextAlign indicates how the text is positioned relative to X.
-        // LEFT is the default and there's nothing to do.
-        if (paint.getTextAlign() != Align.LEFT) {
-            float m = paint.measureText(text, index, count);
-            if (paint.getTextAlign() == Align.CENTER) {
-                x -= m / 2;
-            } else if (paint.getTextAlign() == Align.RIGHT) {
-                x -= m;
-            }
-        }
-
-        List<FontInfo> fonts = paint.getFonts();
-        try {
-            if (fonts.size() > 0) {
-                FontInfo mainFont = fonts.get(0);
-                int i = index;
-                int lastIndex = index + count;
-                while (i < lastIndex) {
-                    // always start with the main font.
-                    int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
-                    if (upTo == -1) {
-                        // draw all the rest and exit.
-                        g.setFont(mainFont.mFont);
-                        g.drawChars(text, i, lastIndex - i, (int)x, (int)y);
-                        return;
-                    } else if (upTo > 0) {
-                        // draw what's possible
-                        g.setFont(mainFont.mFont);
-                        g.drawChars(text, i, upTo - i, (int)x, (int)y);
-
-                        // compute the width that was drawn to increase x
-                        x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
-
-                        // move index to the first non displayed char.
-                        i = upTo;
-
-                        // don't call continue at this point. Since it is certain the main font
-                        // cannot display the font a index upTo (now ==i), we move on to the
-                        // fallback fonts directly.
-                    }
-
-                    // no char supported, attempt to read the next char(s) with the
-                    // fallback font. In this case we only test the first character
-                    // and then go back to test with the main font.
-                    // Special test for 2-char characters.
-                    boolean foundFont = false;
-                    for (int f = 1 ; f < fonts.size() ; f++) {
-                        FontInfo fontInfo = fonts.get(f);
-
-                        // need to check that the font can display the character. We test
-                        // differently if the char is a high surrogate.
-                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                        upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
-                        if (upTo == -1) {
-                            // draw that char
-                            g.setFont(fontInfo.mFont);
-                            g.drawChars(text, i, charCount, (int)x, (int)y);
-
-                            // update x
-                            x += fontInfo.mMetrics.charsWidth(text, i, charCount);
-
-                            // update the index in the text, and move on
-                            i += charCount;
-                            foundFont = true;
-                            break;
-
-                        }
-                    }
-
-                    // in case no font can display the char, display it with the main font.
-                    // (it'll put a square probably)
-                    if (foundFont == false) {
-                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-
-                        g.setFont(mainFont.mFont);
-                        g.drawChars(text, i, charCount, (int)x, (int)y);
-
-                        // measure it to advance x
-                        x += mainFont.mMetrics.charsWidth(text, i, charCount);
-
-                        // and move to the next chars.
-                        i += charCount;
-                    }
-                }
-            }
-        } finally {
-            g.dispose();
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
-        drawText(text.toString().toCharArray(), start, end - start, x, y, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawText(java.lang.String, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawText(String text, float x, float y, Paint paint) {
-        drawText(text.toCharArray(), 0, text.length(), x, y, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawText(java.lang.String, int, int, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawText(String text, int start, int end, float x, float y, Paint paint) {
-        drawText(text.toCharArray(), start, end - start, x, y, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRect(android.graphics.RectF, android.graphics.Paint)
-     */
-    @Override
-    public void drawRect(RectF rect, Paint paint) {
-        doDrawRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRect(float, float, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawRect(float left, float top, float right, float bottom, Paint paint) {
-        doDrawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top), paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRect(android.graphics.Rect, android.graphics.Paint)
-     */
-    @Override
-    public void drawRect(Rect r, Paint paint) {
-        doDrawRect(r.left, r.top, r.width(), r.height(), paint);
-    }
-
-    private final void doDrawRect(int left, int top, int width, int height, Paint paint) {
-        if (width > 0 && height > 0) {
-            // get a Graphics2D object configured with the drawing parameters.
-            Graphics2D g = getCustomGraphics(paint);
-
-            Style style = paint.getStyle();
-
-            // draw
-            if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-                g.fillRect(left, top, width, height);
-            }
-
-            if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-                g.drawRect(left, top, width, height);
-            }
-
-            // dispose Graphics2D object
-            g.dispose();
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
-        if (rect.width() > 0 && rect.height() > 0) {
-            // get a Graphics2D object configured with the drawing parameters.
-            Graphics2D g = getCustomGraphics(paint);
-
-            Style style = paint.getStyle();
-
-            // draw
-
-            int arcWidth = (int)(rx * 2);
-            int arcHeight = (int)(ry * 2);
-
-            if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-                g.fillRoundRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
-                        arcWidth, arcHeight);
-            }
-
-            if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-                g.drawRoundRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
-                        arcWidth, arcHeight);
-            }
-
-            // dispose Graphics2D object
-            g.dispose();
-        }
-    }
-
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawLine(float, float, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawLines(float[], int, int, android.graphics.Paint)
-     */
-    @Override
-    public void drawLines(float[] pts, int offset, int count, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        for (int i = 0 ; i < count ; i += 4) {
-            g.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
-                    (int)pts[i + offset + 2], (int)pts[i + offset + 3]);
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawLines(float[], android.graphics.Paint)
-     */
-    @Override
-    public void drawLines(float[] pts, Paint paint) {
-        drawLines(pts, 0, pts.length, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawCircle(float, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawCircle(float cx, float cy, float radius, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        Style style = paint.getStyle();
-
-        int size = (int)(radius * 2);
-
-        // draw
-        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-            g.fillOval((int)(cx - radius), (int)(cy - radius), size, size);
-        }
-
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            g.drawOval((int)(cx - radius), (int)(cy - radius), size, size);
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawOval(android.graphics.RectF, android.graphics.Paint)
-     */
-    @Override
-    public void drawOval(RectF oval, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        Style style = paint.getStyle();
-
-        // draw
-        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-            g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
-        }
-
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            g.drawOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPath(android.graphics.Path, android.graphics.Paint)
-     */
-    @Override
-    public void drawPath(Path path, Paint paint) {
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D g = getCustomGraphics(paint);
-
-        Style style = paint.getStyle();
-
-        // draw
-        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-            g.fill(path.getAwtShape());
-        }
-
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            g.draw(path.getAwtShape());
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#setMatrix(android.graphics.Matrix)
-     */
-    @Override
-    public void setMatrix(Matrix matrix) {
-        // get the new current graphics
-        Graphics2D g = getGraphics2d();
-
-        // and apply the matrix
-        g.setTransform(matrix.getTransform());
-
-        if (mLogger != null && matrix.hasPerspective()) {
-            mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor.");
-        }
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#concat(android.graphics.Matrix)
-     */
-    @Override
-    public void concat(Matrix matrix) {
-        // get the current top graphics2D object.
-        Graphics2D g = getGraphics2d();
-
-        // get its current matrix
-        AffineTransform currentTx = g.getTransform();
-        // get the AffineTransform of the given matrix
-        AffineTransform matrixTx = matrix.getTransform();
-
-        // combine them so that the given matrix is applied after.
-        currentTx.preConcatenate(matrixTx);
-
-        // give it to the graphics2D as a new matrix replacing all previous transform
-        g.setTransform(currentTx);
-    }
-
-
-    // --------------------
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipPath(android.graphics.Path, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipPath(Path path, Op op) {
-        // TODO Auto-generated method stub
-        return super.clipPath(path, op);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipPath(android.graphics.Path)
-     */
-    @Override
-    public boolean clipPath(Path path) {
-        // TODO Auto-generated method stub
-        return super.clipPath(path);
-    }
-
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRegion(android.graphics.Region, android.graphics.Region.Op)
-     */
-    @Override
-    public boolean clipRegion(Region region, Op op) {
-        // TODO Auto-generated method stub
-        return super.clipRegion(region, op);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#clipRegion(android.graphics.Region)
-     */
-    @Override
-    public boolean clipRegion(Region region) {
-        // TODO Auto-generated method stub
-        return super.clipRegion(region);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint)
-     */
-    @Override
-    public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
-            Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawArc(oval, startAngle, sweepAngle, useCenter, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawBitmapMesh(android.graphics.Bitmap, int, int, float[], int, int[], int, android.graphics.Paint)
-     */
-    @Override
-    public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
-            int vertOffset, int[] colors, int colorOffset, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPicture(android.graphics.Picture, android.graphics.Rect)
-     */
-    @Override
-    public void drawPicture(Picture picture, Rect dst) {
-        // TODO Auto-generated method stub
-        super.drawPicture(picture, dst);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPicture(android.graphics.Picture, android.graphics.RectF)
-     */
-    @Override
-    public void drawPicture(Picture picture, RectF dst) {
-        // TODO Auto-generated method stub
-        super.drawPicture(picture, dst);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPicture(android.graphics.Picture)
-     */
-    @Override
-    public void drawPicture(Picture picture) {
-        // TODO Auto-generated method stub
-        super.drawPicture(picture);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPoint(float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawPoint(float x, float y, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPoint(x, y, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPoints(float[], int, int, android.graphics.Paint)
-     */
-    @Override
-    public void drawPoints(float[] pts, int offset, int count, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPoints(pts, offset, count, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPoints(float[], android.graphics.Paint)
-     */
-    @Override
-    public void drawPoints(float[] pts, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPoints(pts, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPosText(char[], int, int, float[], android.graphics.Paint)
-     */
-    @Override
-    public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPosText(text, index, count, pos, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawPosText(java.lang.String, float[], android.graphics.Paint)
-     */
-    @Override
-    public void drawPosText(String text, float[] pos, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawPosText(text, pos, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawTextOnPath(char[], int, int, android.graphics.Path, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawTextOnPath(char[] text, int index, int count, Path path, float offset,
-            float offset2, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawTextOnPath(text, index, count, path, offset, offset2, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawTextOnPath(java.lang.String, android.graphics.Path, float, float, android.graphics.Paint)
-     */
-    @Override
-    public void drawTextOnPath(String text, Path path, float offset, float offset2, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawTextOnPath(text, path, offset, offset2, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#drawVertices(android.graphics.Canvas.VertexMode, int, float[], int, float[], int, int[], int, short[], int, int, android.graphics.Paint)
-     */
-    @Override
-    public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
-            float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
-            int indexOffset, int indexCount, Paint paint) {
-        // TODO Auto-generated method stub
-        super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset, colors, colorOffset,
-                indices, indexOffset, indexCount, paint);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getDrawFilter()
-     */
-    @Override
-    public DrawFilter getDrawFilter() {
-        // TODO Auto-generated method stub
-        return super.getDrawFilter();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getGL()
-     */
-    @Override
-    public GL getGL() {
-        // TODO Auto-generated method stub
-        return super.getGL();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getMatrix()
-     */
-    @Override
-    public Matrix getMatrix() {
-        // TODO Auto-generated method stub
-        return super.getMatrix();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#getMatrix(android.graphics.Matrix)
-     */
-    @Override
-    public void getMatrix(Matrix ctm) {
-        // TODO Auto-generated method stub
-        super.getMatrix(ctm);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#isOpaque()
-     */
-    @Override
-    public boolean isOpaque() {
-        // TODO Auto-generated method stub
-        return super.isOpaque();
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#saveLayer(float, float, float, float, android.graphics.Paint, int)
-     */
-    @Override
-    public int saveLayer(float left, float top, float right, float bottom, Paint paint,
-            int saveFlags) {
-        // TODO Auto-generated method stub
-        return super.saveLayer(left, top, right, bottom, paint, saveFlags);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#saveLayer(android.graphics.RectF, android.graphics.Paint, int)
-     */
-    @Override
-    public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
-        // TODO Auto-generated method stub
-        return super.saveLayer(bounds, paint, saveFlags);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#saveLayerAlpha(float, float, float, float, int, int)
-     */
-    @Override
-    public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
-            int saveFlags) {
-        // TODO Auto-generated method stub
-        return super.saveLayerAlpha(left, top, right, bottom, alpha, saveFlags);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#saveLayerAlpha(android.graphics.RectF, int, int)
-     */
-    @Override
-    public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) {
-        // TODO Auto-generated method stub
-        return super.saveLayerAlpha(bounds, alpha, saveFlags);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#setDrawFilter(android.graphics.DrawFilter)
-     */
-    @Override
-    public void setDrawFilter(DrawFilter filter) {
-        // TODO Auto-generated method stub
-        super.setDrawFilter(filter);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#setViewport(int, int)
-     */
-    @Override
-    public void setViewport(int width, int height) {
-        // TODO Auto-generated method stub
-        super.setViewport(width, height);
-    }
-
-    /* (non-Javadoc)
-     * @see android.graphics.Canvas#skew(float, float)
-     */
-    @Override
-    public void skew(float sx, float sy) {
-        // TODO Auto-generated method stub
-        super.skew(sx, sy);
-    }
-
-
-
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
new file mode 100644
index 0000000..80db8c3
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -0,0 +1,1182 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.api.ILayoutLog;
+import com.android.layoutlib.bridge.DelegateManager;
+
+import android.graphics.Paint_Delegate.FontInfo;
+import android.text.TextUtils;
+
+import java.awt.AlphaComposite;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.util.List;
+import java.util.Stack;
+
+
+/**
+ * Delegate implementing the native methods of android.graphics.Canvas
+ *
+ * Through the layoutlib_create tool, the original native methods of Canvas have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Canvas class.
+ *
+ * @see DelegateManager
+ *
+ */
+public class Canvas_Delegate {
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Canvas_Delegate> sManager =
+            new DelegateManager<Canvas_Delegate>();
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+    private BufferedImage mBufferedImage;
+    private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>();
+    private ILayoutLog mLogger;
+
+    // ---- Public Helper methods ----
+
+    /**
+     * Returns the native delegate associated to a given {@link Canvas} object.
+     */
+    public static Canvas_Delegate getDelegate(Canvas canvas) {
+        return sManager.getDelegate(canvas.mNativeCanvas);
+    }
+
+    /**
+     * Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
+     */
+    public static Canvas_Delegate getDelegate(int native_canvas) {
+        return sManager.getDelegate(native_canvas);
+    }
+
+    /**
+     * Sets the layoutlib logger into the canvas.
+     * @param logger
+     */
+    public void setLogger(ILayoutLog logger) {
+        mLogger = logger;
+    }
+
+    /**
+     * Returns the current {@link Graphics2D} used to draw.
+     */
+    public Graphics2D getGraphics2d() {
+        return mGraphicsStack.peek();
+    }
+
+    /**
+     * Disposes of the {@link Graphics2D} stack.
+     */
+    public void dispose() {
+
+    }
+
+    // ---- native methods ----
+
+    /*package*/ static boolean isOpaque(Canvas thisCanvas) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int getWidth(Canvas thisCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return canvasDelegate.mBufferedImage.getWidth();
+    }
+
+    /*package*/ static int getHeight(Canvas thisCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return canvasDelegate.mBufferedImage.getHeight();
+    }
+
+    /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        canvasDelegate.getGraphics2d().translate(dx, dy);
+    }
+
+    /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        canvasDelegate.getGraphics2d().rotate(Math.toRadians(degrees));
+    }
+
+    /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        canvasDelegate.getGraphics2d().scale(sx, sy);
+    }
+
+    /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the current top graphics2D object.
+        Graphics2D g = canvasDelegate.getGraphics2d();
+
+        // get its current matrix
+        AffineTransform currentTx = g.getTransform();
+        // get the AffineTransform for the given skew.
+        float[] mtx = Matrix_Delegate.getSkew(kx, ky);
+        AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
+
+        // combine them so that the given matrix is applied after.
+        currentTx.preConcatenate(matrixTx);
+
+        // give it to the graphics2D as a new matrix replacing all previous transform
+        g.setTransform(currentTx);
+    }
+
+    /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) {
+        return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
+    }
+
+    /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) {
+        return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
+    }
+
+    /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
+            float bottom) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return false;
+        }
+
+        canvasDelegate.getGraphics2d().clipRect((int)left, (int)top, (int)(right-left),
+                (int)(bottom-top));
+        return true;
+    }
+
+    /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
+            int bottom) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return false;
+        }
+
+        canvasDelegate.getGraphics2d().clipRect(left, top, right - left, bottom - top);
+        return true;
+    }
+
+    /*package*/ static int save(Canvas thisCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return 0;
+        }
+
+        // get the current save count
+        int count = canvasDelegate.mGraphicsStack.size();
+
+        // create a new graphics and add it to the stack
+        Graphics2D g = (Graphics2D)canvasDelegate.getGraphics2d().create();
+        canvasDelegate.mGraphicsStack.push(g);
+
+        // return the old save count
+        return count;
+
+    }
+
+    /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
+        // FIXME implement save(flags)
+        return save(thisCanvas);
+    }
+
+    /*package*/ static void restore(Canvas thisCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        canvasDelegate.mGraphicsStack.pop();
+    }
+
+    /*package*/ static int getSaveCount(Canvas thisCanvas) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return canvasDelegate.mGraphicsStack.size();
+    }
+
+    /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        while (canvasDelegate.mGraphicsStack.size() > saveCount) {
+            canvasDelegate.mGraphicsStack.pop();
+        }
+    }
+
+    /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count,
+            Paint paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void drawLines(Canvas thisCanvas, float[] pts, int offset, int count,
+            Paint paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void freeCaches() {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int initRaster(int nativeBitmapOrZero) {
+        if (nativeBitmapOrZero > 0) {
+            // get the Bitmap from the int
+            Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero);
+
+            // create a new Canvas_Delegate with the given bitmap and return its new native int.
+            Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate.getImage());
+
+            return sManager.addDelegate(newDelegate);
+        } else {
+            // create a new Canvas_Delegate and return its new native int.
+            Canvas_Delegate newDelegate = new Canvas_Delegate();
+
+            return sManager.addDelegate(newDelegate);
+        }
+    }
+
+    /*package*/ static void native_setBitmap(int nativeCanvas, int bitmap) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            assert false;
+            return;
+        }
+
+        canvasDelegate.setBitmap(bitmapDelegate.getImage());
+    }
+
+    /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds,
+                                               int paint, int layerFlags) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_saveLayer(int nativeCanvas, float l,
+                                               float t, float r, float b,
+                                               int paint, int layerFlags) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_saveLayerAlpha(int nativeCanvas,
+                                                    RectF bounds, int alpha,
+                                                    int layerFlags) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l,
+                                                    float t, float r, float b,
+                                                    int alpha, int layerFlags) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+
+    /*package*/ static void native_concat(int nCanvas, int nMatrix) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean native_clipRect(int nCanvas,
+                                                  float left, float top,
+                                                  float right, float bottom,
+                                                  int regionOp) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean native_clipPath(int nativeCanvas,
+                                                  int nativePath,
+                                                  int regionOp) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean native_clipRegion(int nativeCanvas,
+                                                    int nativeRegion,
+                                                    int regionOp) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void nativeSetDrawFilter(int nativeCanvas,
+                                                   int nativeFilter) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean native_getClipBounds(int nativeCanvas,
+                                                       Rect bounds) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return false;
+        }
+
+        Rectangle rect = canvasDelegate.getGraphics2d().getClipBounds();
+        if (rect != null) {
+            bounds.left = rect.x;
+            bounds.top = rect.y;
+            bounds.right = rect.x + rect.width;
+            bounds.bottom = rect.y + rect.height;
+            return true;
+        }
+        return false;
+    }
+
+    /*package*/ static void native_getCTM(int canvas, int matrix) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static boolean native_quickReject(int nativeCanvas,
+                                                     RectF rect,
+                                                     int native_edgeType) {
+        // FIXME properly implement quickReject
+        return false;
+    }
+
+    /*package*/ static boolean native_quickReject(int nativeCanvas,
+                                                     int path,
+                                                     int native_edgeType) {
+        // FIXME properly implement quickReject
+        return false;
+    }
+
+    /*package*/ static boolean native_quickReject(int nativeCanvas,
+                                                     float left, float top,
+                                                     float right, float bottom,
+                                                     int native_edgeType) {
+        // FIXME properly implement quickReject
+        return false;
+    }
+
+    /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g, int b) {
+        native_drawColor(nativeCanvas, 0xFF000000 | r << 16 | (g&0xFF) << 8 | (b&0xFF),
+                PorterDuff.Mode.SRC_OVER.nativeInt);
+
+    }
+
+    /*package*/ static void native_drawARGB(int nativeCanvas, int a, int r, int g, int b) {
+        native_drawColor(nativeCanvas, a << 24 | (r&0xFF) << 16 | (g&0xFF) << 8 | (b&0xFF),
+                PorterDuff.Mode.SRC_OVER.nativeInt);
+    }
+
+    /*package*/ static void native_drawColor(int nativeCanvas, int color) {
+        native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt);
+    }
+
+    /*package*/ static void native_drawColor(int nativeCanvas, int color, int mode) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get a new graphics context.
+        Graphics2D graphics = (Graphics2D)canvasDelegate.getGraphics2d().create();
+        try {
+            // reset its transform just in case
+            graphics.setTransform(new AffineTransform());
+
+            // set the color
+            graphics.setColor(new Color(color));
+
+            // set the mode and alpha.
+            int alpha = color >>> 24;
+            float falpha = alpha / 255.f;
+
+            setModeInGraphics(graphics, mode, falpha);
+
+            graphics.fillRect(0, 0, canvasDelegate.mBufferedImage.getWidth(),
+                    canvasDelegate.mBufferedImage.getHeight());
+        } finally {
+            // dispose Graphics2D object
+            graphics.dispose();
+        }
+    }
+
+    /*package*/ static void native_drawPaint(int nativeCanvas, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawLine(int nativeCanvas, float startX,
+                                               float startY, float stopX,
+                                               float stopY, int paint) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
+        if (paintDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get a Graphics2D object configured with the drawing parameters.
+        Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
+
+        g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
+
+        // dispose Graphics2D object
+        g.dispose();
+    }
+
+    /*package*/ static void native_drawRect(int nativeCanvas, RectF rect,
+                                               int paint) {
+        native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
+    }
+
+    /*package*/ static void native_drawRect(int nativeCanvas, float left,
+                                               float top, float right,
+                                               float bottom, int paint) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
+        if (paintDelegate == null) {
+            assert false;
+            return;
+        }
+
+        if (right > left && bottom > top) {
+            // get a Graphics2D object configured with the drawing parameters.
+            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
+
+            int style = paintDelegate.getStyle();
+
+            // draw
+            if (style == Paint.Style.FILL.nativeInt ||
+                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
+            }
+
+            if (style == Paint.Style.STROKE.nativeInt ||
+                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top));
+            }
+
+            // dispose Graphics2D object
+            g.dispose();
+        }
+    }
+
+    /*package*/ static void native_drawOval(int nativeCanvas, RectF oval,
+                                               int paint) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
+        if (paintDelegate == null) {
+            assert false;
+            return;
+        }
+
+        if (oval.right > oval.left && oval.bottom > oval.top) {
+            // get a Graphics2D object configured with the drawing parameters.
+            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
+
+            int style = paintDelegate.getStyle();
+
+            // draw
+            if (style == Paint.Style.FILL.nativeInt ||
+                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
+            }
+
+            if (style == Paint.Style.STROKE.nativeInt ||
+                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                g.drawOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height());
+            }
+
+            // dispose Graphics2D object
+            g.dispose();
+        }
+    }
+
+    /*package*/ static void native_drawCircle(int nativeCanvas, float cx,
+                                                 float cy, float radius,
+                                                 int paint) {
+        native_drawOval(nativeCanvas,
+                new RectF(cx - radius, cy - radius, radius*2, radius*2),
+                paint);
+    }
+
+    /*package*/ static void native_drawArc(int nativeCanvas, RectF oval,
+                                              float startAngle, float sweep,
+                                              boolean useCenter, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawRoundRect(int nativeCanvas,
+                                                    RectF rect, float rx,
+                                                    float ry, int paint) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
+        if (paintDelegate == null) {
+            assert false;
+            return;
+        }
+
+        if (rect.right > rect.left && rect.bottom > rect.top) {
+            // get a Graphics2D object configured with the drawing parameters.
+            Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate);
+
+            int style = paintDelegate.getStyle();
+
+            // draw
+            if (style == Paint.Style.FILL.nativeInt ||
+                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                g.fillRoundRect(
+                        (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
+                        (int)rx, (int)ry);
+            }
+
+            if (style == Paint.Style.STROKE.nativeInt ||
+                    style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+                g.drawRoundRect(
+                        (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
+                        (int)rx, (int)ry);
+            }
+
+            // dispose Graphics2D object
+            g.dispose();
+        }
+    }
+
+    /*package*/ static void native_drawPath(int nativeCanvas, int path,
+                                               int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
+                                                 float left, float top,
+                                                 int nativePaintOrZero,
+                                                 int canvasDensity,
+                                                 int screenDensity,
+                                                 int bitmapDensity) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
+                                                 Rect src, RectF dst,
+                                                 int nativePaintOrZero,
+                                                 int screenDensity,
+                                                 int bitmapDensity) {
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            assert false;
+            return;
+        }
+
+        BufferedImage image = bitmapDelegate.getImage();
+
+        if (src == null) {
+            drawBitmap(nativeCanvas, image, nativePaintOrZero,
+                    0, 0, image.getWidth(), image.getHeight(),
+                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
+        } else {
+            drawBitmap(nativeCanvas, image, nativePaintOrZero,
+                    src.left, src.top, src.width(), src.height(),
+                    (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
+        }
+    }
+
+    /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap,
+                                                 Rect src, Rect dst,
+                                                 int nativePaintOrZero,
+                                                 int screenDensity,
+                                                 int bitmapDensity) {
+        // get the delegate from the native int.
+        Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+        if (bitmapDelegate == null) {
+            assert false;
+            return;
+        }
+
+        BufferedImage image = bitmapDelegate.getImage();
+
+        if (src == null) {
+            drawBitmap(nativeCanvas, image, nativePaintOrZero,
+                    0, 0, image.getWidth(), image.getHeight(),
+                    dst.left, dst.top, dst.right, dst.bottom);
+        } else {
+            drawBitmap(nativeCanvas, image, nativePaintOrZero,
+                    src.left, src.top, src.width(), src.height(),
+                    dst.left, dst.top, dst.right, dst.bottom);
+        }
+    }
+
+    /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors,
+                                                int offset, int stride, float x,
+                                                 float y, int width, int height,
+                                                 boolean hasAlpha,
+                                                 int nativePaintOrZero) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap,
+                                                      int nMatrix, int nPaint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap,
+                                                    int meshWidth, int meshHeight,
+                                                    float[] verts, int vertOffset,
+                                                    int[] colors, int colorOffset, int nPaint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n,
+                   float[] verts, int vertOffset, float[] texs, int texOffset,
+                   int[] colors, int colorOffset, short[] indices,
+                   int indexOffset, int indexCount, int nPaint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawText(int nativeCanvas, char[] text,
+                                               int index, int count, float x,
+                                               float y, int flags, int paint) {
+        // WARNING: the logic in this method is similar to Paint.measureText.
+        // Any change to this method should be reflected in Paint.measureText
+
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
+        if (paintDelegate == null) {
+            assert false;
+            return;
+        }
+
+        Graphics2D g = (Graphics2D) canvasDelegate.getCustomGraphics(paintDelegate);
+        try {
+            // Paint.TextAlign indicates how the text is positioned relative to X.
+            // LEFT is the default and there's nothing to do.
+            if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
+                float m = paintDelegate.measureText(text, index, count);
+                if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
+                    x -= m / 2;
+                } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
+                    x -= m;
+                }
+            }
+
+            List<FontInfo> fonts = paintDelegate.getFonts();
+
+            if (fonts.size() > 0) {
+                FontInfo mainFont = fonts.get(0);
+                int i = index;
+                int lastIndex = index + count;
+                while (i < lastIndex) {
+                    // always start with the main font.
+                    int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
+                    if (upTo == -1) {
+                        // draw all the rest and exit.
+                        g.setFont(mainFont.mFont);
+                        g.drawChars(text, i, lastIndex - i, (int)x, (int)y);
+                        return;
+                    } else if (upTo > 0) {
+                        // draw what's possible
+                        g.setFont(mainFont.mFont);
+                        g.drawChars(text, i, upTo - i, (int)x, (int)y);
+
+                        // compute the width that was drawn to increase x
+                        x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
+
+                        // move index to the first non displayed char.
+                        i = upTo;
+
+                        // don't call continue at this point. Since it is certain the main font
+                        // cannot display the font a index upTo (now ==i), we move on to the
+                        // fallback fonts directly.
+                    }
+
+                    // no char supported, attempt to read the next char(s) with the
+                    // fallback font. In this case we only test the first character
+                    // and then go back to test with the main font.
+                    // Special test for 2-char characters.
+                    boolean foundFont = false;
+                    for (int f = 1 ; f < fonts.size() ; f++) {
+                        FontInfo fontInfo = fonts.get(f);
+
+                        // need to check that the font can display the character. We test
+                        // differently if the char is a high surrogate.
+                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
+                        upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
+                        if (upTo == -1) {
+                            // draw that char
+                            g.setFont(fontInfo.mFont);
+                            g.drawChars(text, i, charCount, (int)x, (int)y);
+
+                            // update x
+                            x += fontInfo.mMetrics.charsWidth(text, i, charCount);
+
+                            // update the index in the text, and move on
+                            i += charCount;
+                            foundFont = true;
+                            break;
+
+                        }
+                    }
+
+                    // in case no font can display the char, display it with the main font.
+                    // (it'll put a square probably)
+                    if (foundFont == false) {
+                        int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
+
+                        g.setFont(mainFont.mFont);
+                        g.drawChars(text, i, charCount, (int)x, (int)y);
+
+                        // measure it to advance x
+                        x += mainFont.mMetrics.charsWidth(text, i, charCount);
+
+                        // and move to the next chars.
+                        i += charCount;
+                    }
+                }
+            }
+        } finally {
+            g.dispose();
+        }
+    }
+
+    /*package*/ static void native_drawText(int nativeCanvas, String text,
+                                               int start, int end, float x,
+                                               float y, int flags, int paint) {
+        int count = end - start;
+        char[] buffer = TemporaryBuffer.obtain(count);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
+    }
+
+
+    /*package*/ static void native_drawTextRun(int nativeCanvas, String text,
+            int start, int end, int contextStart, int contextEnd,
+            float x, float y, int flags, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+
+    /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text,
+            int start, int count, int contextStart, int contextCount,
+            float x, float y, int flags, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+
+    /*package*/ static void native_drawPosText(int nativeCanvas,
+                                                  char[] text, int index,
+                                                  int count, float[] pos,
+                                                  int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawPosText(int nativeCanvas,
+                                                  String text, float[] pos,
+                                                  int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
+                                                     char[] text, int index,
+                                                     int count, int path,
+                                                     float hOffset,
+                                                     float vOffset, int bidiFlags,
+                                                     int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawTextOnPath(int nativeCanvas,
+                                                     String text, int path,
+                                                     float hOffset,
+                                                     float vOffset,
+                                                     int flags, int paint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_drawPicture(int nativeCanvas,
+                                                  int nativePicture) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void finalizer(int nativeCanvas) {
+        sManager.removeDelegate(nativeCanvas);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private Canvas_Delegate(BufferedImage image) {
+        setBitmap(image);
+    }
+
+    private Canvas_Delegate() {
+    }
+
+    private void setBitmap(BufferedImage image) {
+        mBufferedImage = image;
+        mGraphicsStack.push(mBufferedImage.createGraphics());
+    }
+
+    /**
+     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
+     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
+     */
+    private Graphics2D getCustomGraphics(Paint_Delegate paint) {
+        // make new one
+        Graphics2D g = getGraphics2d();
+        g = (Graphics2D)g.create();
+
+        // configure it
+
+        if (paint.isAntiAliased()) {
+            g.setRenderingHint(
+                    RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+            g.setRenderingHint(
+                    RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+        }
+
+        boolean useColorPaint = true;
+
+        // get the shader first, as it'll replace the color if it can be used it.
+        Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(paint.getShader());
+        if (shaderDelegate != null) {
+            java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
+            if (shaderPaint != null) {
+                g.setPaint(shaderPaint);
+                useColorPaint = false;
+            } else {
+                if (mLogger != null) {
+                    mLogger.warning(String.format(
+                            "Shader '%1$s' is not supported in the Layout Editor.",
+                            shaderDelegate.getClass().getCanonicalName()));
+                }
+            }
+        }
+
+        // need to get the alpha to set it in the composite.
+        float falpha = 1.f;
+
+        if (useColorPaint) {
+            g.setColor(new Color(paint.getColor()));
+
+            // the alpha is taken from the alpha channel of the color
+            int alpha = paint.getAlpha();
+            falpha = alpha / 255.f;
+        }
+
+        int style = paint.getStyle();
+        if (style == Paint.Style.STROKE.nativeInt ||
+                style == Paint.Style.FILL_AND_STROKE.nativeInt) {
+
+            PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(
+                    paint.getPathEffect());
+
+            if (effectDelegate instanceof DashPathEffect_Delegate) {
+                DashPathEffect_Delegate dpe = (DashPathEffect_Delegate)effectDelegate;
+                g.setStroke(new BasicStroke(
+                        paint.getStrokeWidth(),
+                        paint.getJavaCap(),
+                        paint.getJavaJoin(),
+                        paint.getStrokeMiter(),
+                        dpe.getIntervals(),
+                        dpe.getPhase()));
+            } else {
+                g.setStroke(new BasicStroke(
+                        paint.getStrokeWidth(),
+                        paint.getJavaCap(),
+                        paint.getJavaJoin(),
+                        paint.getStrokeMiter()));
+            }
+        }
+
+        Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(paint.getXfermode());
+        if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
+            int mode = ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
+
+            setModeInGraphics(g, mode, falpha);
+        } else {
+            // default mode is src_over
+            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
+
+            // if xfermode wasn't null, then it's something we don't support. log it.
+            if (mLogger != null && xfermodeDelegate != null) {
+                mLogger.warning(String.format(
+                        "Xfermode '%1$s' is not supported in the Layout Editor.",
+                        xfermodeDelegate.getClass().getCanonicalName()));
+            }
+        }
+
+        return g;
+    }
+
+    private static void setModeInGraphics(Graphics2D g, int mode, float falpha) {
+        for (PorterDuff.Mode m : PorterDuff.Mode.values()) {
+            if (m.nativeInt == mode) {
+                setModeInGraphics(g, m, falpha);
+                return;
+            }
+        }
+    }
+
+    private static void setModeInGraphics(Graphics2D g, PorterDuff.Mode mode, float falpha) {
+        switch (mode) {
+            case CLEAR:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha));
+                break;
+            case DARKEN:
+                break;
+            case DST:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST, falpha));
+                break;
+            case DST_ATOP:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha));
+                break;
+            case DST_IN:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha));
+                break;
+            case DST_OUT:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha));
+                break;
+            case DST_OVER:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha));
+                break;
+            case LIGHTEN:
+                break;
+            case MULTIPLY:
+                break;
+            case SCREEN:
+                break;
+            case SRC:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, falpha));
+                break;
+            case SRC_ATOP:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha));
+                break;
+            case SRC_IN:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha));
+                break;
+            case SRC_OUT:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha));
+                break;
+            case SRC_OVER:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
+                break;
+            case XOR:
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, falpha));
+                break;
+        }
+    }
+
+
+    private static void drawBitmap(
+            int nativeCanvas,
+            BufferedImage image,
+            int nativePaintOrZero,
+            int sleft, int stop, int sright, int sbottom,
+            int dleft, int dtop, int dright, int dbottom) {
+        // get the delegate from the native int.
+        Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+        if (canvasDelegate == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Paint_Delegate paintDelegate = null;
+        if (nativePaintOrZero > 0) {
+            paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
+            if (paintDelegate == null) {
+                assert false;
+                return;
+            }
+        }
+
+        drawBitmap(canvasDelegate, image, paintDelegate,
+                sleft, stop, sright, sbottom,
+                dleft, dtop, dright, dbottom);
+    }
+
+    private static void drawBitmap(
+            Canvas_Delegate canvasDelegate,
+            BufferedImage image,
+            Paint_Delegate paintDelegate,
+            int sleft, int stop, int sright, int sbottom,
+            int dleft, int dtop, int dright, int dbottom) {
+
+        Graphics2D g = canvasDelegate.getGraphics2d();
+
+        Composite c = null;
+
+        if (paintDelegate != null) {
+            if (paintDelegate.isFilterBitmap()) {
+                g = (Graphics2D)g.create();
+                g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+                        RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+            }
+
+            if (paintDelegate.getAlpha() != 0xFF) {
+                c = g.getComposite();
+                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
+                        paintDelegate.getAlpha()/255.f));
+            }
+        }
+
+        g.drawImage(image, dleft, dtop, dright, dbottom,
+                sleft, stop, sright, sbottom, null);
+
+        if (paintDelegate != null) {
+            if (paintDelegate.isFilterBitmap()) {
+                g.dispose();
+            }
+            if (c != null) {
+                g.setComposite(c);
+            }
+        }
+    }
+
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
deleted file mode 100644
index 863d64a..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import java.awt.Paint;
-
-/** A subclass of shader that returns the composition of two other shaders, combined by
-    an {@link android.graphics.Xfermode} subclass.
-*/
-public class ComposeShader extends Shader {
-    /** Create a new compose shader, given shaders A, B, and a combining mode.
-        When the mode is applied, it will be given the result from shader A as its
-        "dst", and the result of from shader B as its "src".
-        @param shaderA  The colors from this shader are seen as the "dst" by the mode
-        @param shaderB  The colors from this shader are seen as the "src" by the mode
-        @param mode     The mode that combines the colors from the two shaders. If mode
-                        is null, then SRC_OVER is assumed.
-    */
-    public ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode) {
-        // FIXME Implement shader
-    }
-
-    /** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
-        When the mode is applied, it will be given the result from shader A as its
-        "dst", and the result of from shader B as its "src".
-        @param shaderA  The colors from this shader are seen as the "dst" by the mode
-        @param shaderB  The colors from this shader are seen as the "src" by the mode
-        @param mode     The PorterDuff mode that combines the colors from the two shaders.
-    */
-    public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) {
-        // FIXME Implement shader
-    }
-
-    @Override
-    Paint getJavaPaint() {
-        return null;
-    }
-}
-
diff --git a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect.java b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect.java
deleted file mode 100644
index 46d4c70..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-public class DashPathEffect extends PathEffect {
-
-    private final float[] mIntervals;
-    private final float mPhase;
-
-    /**
-     * The intervals array must contain an even number of entries (>=2), with
-     * the even indices specifying the "on" intervals, and the odd indices
-     * specifying the "off" intervals. phase is an offset into the intervals
-     * array (mod the sum of all of the intervals). The intervals array
-     * controls the length of the dashes. The paint's strokeWidth controls the
-     * thickness of the dashes.
-     * Note: this patheffect only affects drawing with the paint's style is set
-     * to STROKE or STROKE_AND_FILL. It is ignored if the drawing is done with
-     * style == FILL.
-     * @param intervals array of ON and OFF distances
-     * @param phase offset into the intervals array
-     */
-    public DashPathEffect(float intervals[], float phase) {
-        if (intervals.length < 2) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-
-        mIntervals = intervals;
-        mPhase = phase;
-    }
-
-    public float[] getIntervals() {
-        return mIntervals;
-    }
-
-    public float getPhase() {
-        return mPhase;
-    }
-}
-
diff --git a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java
new file mode 100644
index 0000000..59b6a91
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+/**
+ * Delegate implementing the native methods of android.graphics.DashPathEffect
+ *
+ * Through the layoutlib_create tool, the original native methods of DashPathEffect have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original DashPathEffect class.
+ *
+ * Because this extends {@link PathEffect_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
+ * {@link PathEffect_Delegate}.
+ *
+ */
+public class DashPathEffect_Delegate extends PathEffect_Delegate {
+
+    // ---- delegate data ----
+
+    private final float[] mIntervals;
+    private final float mPhase;
+
+    // ---- Public Helper methods ----
+
+    public float[] getIntervals() {
+        return mIntervals;
+    }
+
+    public float getPhase() {
+        return mPhase;
+    }
+
+    // ---- native methods ----
+
+    /*package*/ static int nativeCreate(float intervals[], float phase) {
+        DashPathEffect_Delegate newDelegate = new DashPathEffect_Delegate(intervals, phase);
+        return sManager.addDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private DashPathEffect_Delegate(float intervals[], float phase) {
+        mIntervals = new float[intervals.length];
+        System.arraycopy(intervals, 0, mIntervals, 0, intervals.length);
+        mPhase = phase;
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/GradientShader.java b/tools/layoutlib/bridge/src/android/graphics/GradientShader.java
deleted file mode 100644
index 8c5a2a4..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/GradientShader.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-
-/**
- * Base class for Gradient shader. This is not a standard android class and is just used
- * as a base class for the re-implemented gradient classes.
- *
- * It also provides a base class to handle common code between the different shaders'
- * implementations of {@link java.awt.Paint}.
- *
- * @see LinearGradient
- * @see RadialGradient
- * @see SweepGradient
- */
-public abstract class GradientShader extends Shader {
-
-    protected final int[] mColors;
-    protected final float[] mPositions;
-
-    /**
-     * Creates the base shader and do some basic test on the parameters.
-     *
-     * @param colors The colors to be distributed along the gradient line
-     * @param positions May be null. The relative positions [0..1] of each
-     *            corresponding color in the colors array. If this is null, the
-     *            the colors are distributed evenly along the gradient line.
-     */
-    protected GradientShader(int colors[], float positions[]) {
-        if (colors.length < 2) {
-            throw new IllegalArgumentException("needs >= 2 number of colors");
-        }
-        if (positions != null && colors.length != positions.length) {
-            throw new IllegalArgumentException("color and position arrays must be of equal length");
-        }
-
-        if (positions == null) {
-            float spacing = 1.f / (colors.length - 1);
-            positions = new float[colors.length];
-            positions[0] = 0.f;
-            positions[colors.length-1] = 1.f;
-            for (int i = 1; i < colors.length - 1 ; i++) {
-                positions[i] = spacing * i;
-            }
-        }
-
-        mColors = colors;
-        mPositions = positions;
-    }
-
-    /**
-     * Base class for (Java) Gradient Paints. This handles computing the gradient colors based
-     * on the color and position lists, as well as the {@link TileMode}
-     *
-     */
-    protected abstract static class GradientPaint implements java.awt.Paint {
-        private final static int GRADIENT_SIZE = 100;
-
-        private final int[] mColors;
-        private final float[] mPositions;
-        private final TileMode mTileMode;
-        private int[] mGradient;
-
-        protected GradientPaint(int[] colors, float[] positions, TileMode tileMode) {
-            mColors = colors;
-            mPositions = positions;
-            mTileMode = tileMode;
-        }
-
-        public int getTransparency() {
-            return java.awt.Paint.TRANSLUCENT;
-        }
-
-        /**
-         * Pre-computes the colors for the gradient. This must be called once before any call
-         * to {@link #getGradientColor(float)}
-         */
-        protected synchronized void precomputeGradientColors() {
-            if (mGradient == null) {
-                // actually create an array with an extra size, so that we can really go
-                // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0
-                mGradient = new int[GRADIENT_SIZE+1];
-
-                int prevPos = 0;
-                int nextPos = 1;
-                for (int i  = 0 ; i <= GRADIENT_SIZE ; i++) {
-                    // compute current position
-                    float currentPos = (float)i/GRADIENT_SIZE;
-                    while (currentPos > mPositions[nextPos]) {
-                        prevPos = nextPos++;
-                    }
-
-                    float percent = (currentPos - mPositions[prevPos]) /
-                            (mPositions[nextPos] - mPositions[prevPos]);
-
-                    mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent);
-                }
-            }
-        }
-
-        /**
-         * Returns the color based on the position in the gradient.
-         * <var>pos</var> can be anything, even &lt; 0 or &gt; > 1, as the gradient
-         * will use {@link TileMode} value to convert it into a [0,1] value.
-         */
-        protected int getGradientColor(float pos) {
-            if (pos < 0.f) {
-                if (mTileMode != null) {
-                    switch (mTileMode) {
-                        case CLAMP:
-                            pos = 0.f;
-                            break;
-                        case REPEAT:
-                            // remove the integer part to stay in the [0,1] range
-                            // careful: this is a negative value, so use ceil instead of floor
-                            pos = pos - (float)Math.ceil(pos);
-                            break;
-                        case MIRROR:
-                            // get the integer and the decimal part
-                            // careful: this is a negative value, so use ceil instead of floor
-                            int intPart = (int)Math.ceil(pos);
-                            pos = pos - intPart;
-                            // 0  -> -1 : mirrored order
-                            // -1 -> -2: normal order
-                            // etc..
-                            // this means if the intpart is even we invert
-                            if ((intPart % 2) == 0) {
-                                pos = 1.f - pos;
-                            }
-                            break;
-                    }
-                } else {
-                    pos = 0.0f;
-                }
-            } else if (pos > 1f) {
-                if (mTileMode != null) {
-                    switch (mTileMode) {
-                        case CLAMP:
-                            pos = 1.f;
-                            break;
-                        case REPEAT:
-                            // remove the integer part to stay in the [0,1] range
-                            pos = pos - (float)Math.floor(pos);
-                            break;
-                        case MIRROR:
-                            // get the integer and the decimal part
-                            int intPart = (int)Math.floor(pos);
-                            pos = pos - intPart;
-                            // 0 -> 1 : normal order
-                            // 1 -> 2: mirrored
-                            // etc..
-                            // this means if the intpart is odd we invert
-                            if ((intPart % 2) == 1) {
-                                pos = 1.f - pos;
-                            }
-                            break;
-                    }
-                } else {
-                    pos = 1.0f;
-                }
-            }
-
-            int index = (int)((pos * GRADIENT_SIZE) + .5);
-
-            return mGradient[index];
-        }
-
-        /**
-         * Returns the color between c1, and c2, based on the percent of the distance
-         * between c1 and c2.
-         */
-        private int computeColor(int c1, int c2, float percent) {
-            int a = computeChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent);
-            int r = computeChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent);
-            int g = computeChannel((c1 >>  8) & 0xFF, (c2 >>  8) & 0xFF, percent);
-            int b = computeChannel((c1      ) & 0xFF, (c2      ) & 0xFF, percent);
-            return a << 24 | r << 16 | g << 8 | b;
-        }
-
-        /**
-         * Returns the channel value between 2 values based on the percent of the distance between
-         * the 2 values..
-         */
-        private int computeChannel(int c1, int c2, float percent) {
-            return c1 + (int)((percent * (c2-c1)) + .5);
-        }
-
-
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
new file mode 100644
index 0000000..042d557
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Gradient_Delegate.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.graphics.Shader.TileMode;
+
+/**
+ * Base class for true Gradient shader delegate.
+ */
+public abstract class Gradient_Delegate extends Shader_Delegate {
+
+    protected final int[] mColors;
+    protected final float[] mPositions;
+
+    /**
+     * Creates the base shader and do some basic test on the parameters.
+     *
+     * @param colors The colors to be distributed along the gradient line
+     * @param positions May be null. The relative positions [0..1] of each
+     *            corresponding color in the colors array. If this is null, the
+     *            the colors are distributed evenly along the gradient line.
+     */
+    protected Gradient_Delegate(int colors[], float positions[]) {
+        if (colors.length < 2) {
+            throw new IllegalArgumentException("needs >= 2 number of colors");
+        }
+        if (positions != null && colors.length != positions.length) {
+            throw new IllegalArgumentException("color and position arrays must be of equal length");
+        }
+
+        if (positions == null) {
+            float spacing = 1.f / (colors.length - 1);
+            positions = new float[colors.length];
+            positions[0] = 0.f;
+            positions[colors.length-1] = 1.f;
+            for (int i = 1; i < colors.length - 1 ; i++) {
+                positions[i] = spacing * i;
+            }
+        }
+
+        mColors = colors;
+        mPositions = positions;
+    }
+
+    /**
+     * Base class for (Java) Gradient Paints. This handles computing the gradient colors based
+     * on the color and position lists, as well as the {@link TileMode}
+     *
+     */
+    protected abstract static class GradientPaint implements java.awt.Paint {
+        private final static int GRADIENT_SIZE = 100;
+
+        private final int[] mColors;
+        private final float[] mPositions;
+        private final TileMode mTileMode;
+        private int[] mGradient;
+
+        protected GradientPaint(int[] colors, float[] positions, TileMode tileMode) {
+            mColors = colors;
+            mPositions = positions;
+            mTileMode = tileMode;
+        }
+
+        public int getTransparency() {
+            return java.awt.Paint.TRANSLUCENT;
+        }
+
+        /**
+         * Pre-computes the colors for the gradient. This must be called once before any call
+         * to {@link #getGradientColor(float)}
+         */
+        protected synchronized void precomputeGradientColors() {
+            if (mGradient == null) {
+                // actually create an array with an extra size, so that we can really go
+                // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0
+                mGradient = new int[GRADIENT_SIZE+1];
+
+                int prevPos = 0;
+                int nextPos = 1;
+                for (int i  = 0 ; i <= GRADIENT_SIZE ; i++) {
+                    // compute current position
+                    float currentPos = (float)i/GRADIENT_SIZE;
+                    while (currentPos > mPositions[nextPos]) {
+                        prevPos = nextPos++;
+                    }
+
+                    float percent = (currentPos - mPositions[prevPos]) /
+                            (mPositions[nextPos] - mPositions[prevPos]);
+
+                    mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent);
+                }
+            }
+        }
+
+        /**
+         * Returns the color based on the position in the gradient.
+         * <var>pos</var> can be anything, even &lt; 0 or &gt; > 1, as the gradient
+         * will use {@link TileMode} value to convert it into a [0,1] value.
+         */
+        protected int getGradientColor(float pos) {
+            if (pos < 0.f) {
+                if (mTileMode != null) {
+                    switch (mTileMode) {
+                        case CLAMP:
+                            pos = 0.f;
+                            break;
+                        case REPEAT:
+                            // remove the integer part to stay in the [0,1] range
+                            // careful: this is a negative value, so use ceil instead of floor
+                            pos = pos - (float)Math.ceil(pos);
+                            break;
+                        case MIRROR:
+                            // get the integer and the decimal part
+                            // careful: this is a negative value, so use ceil instead of floor
+                            int intPart = (int)Math.ceil(pos);
+                            pos = pos - intPart;
+                            // 0  -> -1 : mirrored order
+                            // -1 -> -2: normal order
+                            // etc..
+                            // this means if the intpart is even we invert
+                            if ((intPart % 2) == 0) {
+                                pos = 1.f - pos;
+                            }
+                            break;
+                    }
+                } else {
+                    pos = 0.0f;
+                }
+            } else if (pos > 1f) {
+                if (mTileMode != null) {
+                    switch (mTileMode) {
+                        case CLAMP:
+                            pos = 1.f;
+                            break;
+                        case REPEAT:
+                            // remove the integer part to stay in the [0,1] range
+                            pos = pos - (float)Math.floor(pos);
+                            break;
+                        case MIRROR:
+                            // get the integer and the decimal part
+                            int intPart = (int)Math.floor(pos);
+                            pos = pos - intPart;
+                            // 0 -> 1 : normal order
+                            // 1 -> 2: mirrored
+                            // etc..
+                            // this means if the intpart is odd we invert
+                            if ((intPart % 2) == 1) {
+                                pos = 1.f - pos;
+                            }
+                            break;
+                    }
+                } else {
+                    pos = 1.0f;
+                }
+            }
+
+            int index = (int)((pos * GRADIENT_SIZE) + .5);
+
+            return mGradient[index];
+        }
+
+        /**
+         * Returns the color between c1, and c2, based on the percent of the distance
+         * between c1 and c2.
+         */
+        private int computeColor(int c1, int c2, float percent) {
+            int a = computeChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent);
+            int r = computeChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent);
+            int g = computeChannel((c1 >>  8) & 0xFF, (c2 >>  8) & 0xFF, percent);
+            int b = computeChannel((c1      ) & 0xFF, (c2      ) & 0xFF, percent);
+            return a << 24 | r << 16 | g << 8 | b;
+        }
+
+        /**
+         * Returns the channel value between 2 values based on the percent of the distance between
+         * the 2 values..
+         */
+        private int computeChannel(int c1, int c2, float percent) {
+            return c1 + (int)((percent * (c2-c1)) + .5);
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
deleted file mode 100644
index 10c4a5e..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-public class LinearGradient extends GradientShader {
-
-    private java.awt.Paint mJavaPaint;
-
-    /**
-     * Create a shader that draws a linear gradient along a line.
-     *
-     * @param x0 The x-coordinate for the start of the gradient line
-     * @param y0 The y-coordinate for the start of the gradient line
-     * @param x1 The x-coordinate for the end of the gradient line
-     * @param y1 The y-coordinate for the end of the gradient line
-     * @param colors The colors to be distributed along the gradient line
-     * @param positions May be null. The relative positions [0..1] of each
-     *            corresponding color in the colors array. If this is null, the
-     *            the colors are distributed evenly along the gradient line.
-     * @param tile The Shader tiling mode
-     */
-    public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
-            TileMode tile) {
-        super(colors, positions);
-        mJavaPaint = new LinearGradientPaint(x0, y0, x1, y1, mColors, mPositions, tile);
-    }
-
-    /**
-     * Create a shader that draws a linear gradient along a line.
-     *
-     * @param x0 The x-coordinate for the start of the gradient line
-     * @param y0 The y-coordinate for the start of the gradient line
-     * @param x1 The x-coordinate for the end of the gradient line
-     * @param y1 The y-coordinate for the end of the gradient line
-     * @param color0 The color at the start of the gradient line.
-     * @param color1 The color at the end of the gradient line.
-     * @param tile The Shader tiling mode
-     */
-    public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,
-            TileMode tile) {
-        this(x0, y0, x1, y1, new int[] { color0, color1}, null /*positions*/, tile);
-    }
-
-    // ---------- Custom Methods
-
-    @Override
-    java.awt.Paint getJavaPaint() {
-        return mJavaPaint;
-    }
-
-    /**
-     * Linear Gradient (Java) Paint able to handle more than 2 points, as
-     * {@link java.awt.GradientPaint} only supports 2 points and does not support Android's tile
-     * modes.
-     */
-    private static class LinearGradientPaint extends GradientPaint {
-
-        private final float mX0;
-        private final float mY0;
-        private final float mDx;
-        private final float mDy;
-        private final float mDSize2;
-
-        public LinearGradientPaint(float x0, float y0, float x1, float y1, int colors[],
-                float positions[], TileMode tile) {
-            super(colors, positions, tile);
-                mX0 = x0;
-                mY0 = y0;
-                mDx = x1 - x0;
-                mDy = y1 - y0;
-                mDSize2 = mDx * mDx + mDy * mDy;
-        }
-
-        public java.awt.PaintContext createContext(
-                java.awt.image.ColorModel      colorModel,
-                java.awt.Rectangle             deviceBounds,
-                java.awt.geom.Rectangle2D      userBounds,
-                java.awt.geom.AffineTransform  xform,
-                java.awt.RenderingHints        hints) {
-            precomputeGradientColors();
-            return new LinearGradientPaintContext(colorModel);
-        }
-
-        private class LinearGradientPaintContext implements java.awt.PaintContext {
-
-            private final java.awt.image.ColorModel mColorModel;
-
-            public LinearGradientPaintContext(java.awt.image.ColorModel colorModel) {
-                mColorModel = colorModel;
-                // FIXME: so far all this is always the same rect gotten in getRaster with an indentity matrix?
-            }
-
-            public void dispose() {
-            }
-
-            public java.awt.image.ColorModel getColorModel() {
-                return mColorModel;
-            }
-
-            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
-                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
-
-                int[] data = new int[w*h];
-
-                if (mDx == 0) { // vertical gradient
-                    // compute first column and copy to all other columns
-                    int index = 0;
-                    for (int iy = 0 ; iy < h ; iy++) {
-                        int color = getColor(iy + y, mY0, mDy);
-                        for (int ix = 0 ; ix < w ; ix++) {
-                            data[index++] = color;
-                        }
-                    }
-                } else if (mDy == 0) { // horizontal
-                    // compute first line in a tmp array and copy to all lines
-                    int[] line = new int[w];
-                    for (int ix = 0 ; ix < w ; ix++) {
-                        line[ix] = getColor(ix + x, mX0, mDx);
-                    }
-
-                    for (int iy = 0 ; iy < h ; iy++) {
-                        System.arraycopy(line, 0, data, iy*w, line.length);
-                    }
-                } else {
-                    int index = 0;
-                    for (int iy = 0 ; iy < h ; iy++) {
-                        for (int ix = 0 ; ix < w ; ix++) {
-                            data[index++] = getColor(ix + x, iy + y);
-                        }
-                    }
-                }
-
-                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
-
-                return image.getRaster();
-            }
-        }
-
-        /** Returns a color for the easy vertical/horizontal mode */
-        private int getColor(float absPos, float refPos, float refSize) {
-            float pos = (absPos - refPos) / refSize;
-
-            return getGradientColor(pos);
-        }
-
-        /**
-         * Returns a color for an arbitrary point.
-         */
-        private int getColor(float x, float y) {
-            // find the x position on the gradient vector.
-            float _x = (mDx*mDy*(y-mY0) + mDy*mDy*mX0 + mDx*mDx*x) / mDSize2;
-            // from it get the position relative to the vector
-            float pos = (float) ((_x - mX0) / mDx);
-
-            return getGradientColor(pos);
-        }
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
new file mode 100644
index 0000000..405e537
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+import android.graphics.Shader.TileMode;
+
+import java.awt.Paint;
+
+/**
+ * Delegate implementing the native methods of android.graphics.LinearGradient
+ *
+ * Through the layoutlib_create tool, the original native methods of LinearGradient have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original LinearGradient class.
+ *
+ * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
+ *
+ * @see Shader_Delegate
+ *
+ */
+public class LinearGradient_Delegate extends Gradient_Delegate {
+
+    // ---- delegate data ----
+    private java.awt.Paint mJavaPaint;
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public Paint getJavaPaint() {
+        return mJavaPaint;
+    }
+
+    // ---- native methods ----
+
+    /*package*/ static int nativeCreate1(LinearGradient thisGradient,
+            float x0, float y0, float x1, float y1,
+            int colors[], float positions[], int tileMode) {
+        // figure out the tile
+        TileMode tile = null;
+        for (TileMode tm : TileMode.values()) {
+            if (tm.nativeInt == tileMode) {
+                tile = tm;
+                break;
+            }
+        }
+
+        LinearGradient_Delegate newDelegate = new LinearGradient_Delegate(x0, y0, x1, y1,
+                colors, positions, tile);
+        return sManager.addDelegate(newDelegate);
+    }
+    /*package*/ static int nativeCreate2(LinearGradient thisGradient,
+            float x0, float y0, float x1, float y1,
+            int color0, int color1, int tileMode) {
+        return nativeCreate1(thisGradient,
+                x0, y0, x1, y1, new int[] { color0, color1}, null /*positions*/,
+                tileMode);
+    }
+    /*package*/ static int nativePostCreate1(LinearGradient thisGradient,
+            int native_shader, float x0, float y0, float x1, float y1,
+            int colors[], float positions[], int tileMode) {
+        // nothing to be done here.
+        return 0;
+    }
+    /*package*/ static int nativePostCreate2(LinearGradient thisGradient,
+            int native_shader, float x0, float y0, float x1, float y1,
+            int color0, int color1, int tileMode) {
+        // nothing to be done here.
+        return 0;
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    /**
+     * Create a shader that draws a linear gradient along a line.
+     *
+     * @param x0 The x-coordinate for the start of the gradient line
+     * @param y0 The y-coordinate for the start of the gradient line
+     * @param x1 The x-coordinate for the end of the gradient line
+     * @param y1 The y-coordinate for the end of the gradient line
+     * @param colors The colors to be distributed along the gradient line
+     * @param positions May be null. The relative positions [0..1] of each
+     *            corresponding color in the colors array. If this is null, the
+     *            the colors are distributed evenly along the gradient line.
+     * @param tile The Shader tiling mode
+     */
+    private LinearGradient_Delegate(float x0, float y0, float x1, float y1,
+            int colors[], float positions[], TileMode tile) {
+        super(colors, positions);
+        mJavaPaint = new LinearGradientPaint(x0, y0, x1, y1, mColors, mPositions, tile);
+    }
+
+    // ---- Custom Java Paint ----
+    /**
+     * Linear Gradient (Java) Paint able to handle more than 2 points, as
+     * {@link java.awt.GradientPaint} only supports 2 points and does not support Android's tile
+     * modes.
+     */
+    private static class LinearGradientPaint extends GradientPaint {
+
+        private final float mX0;
+        private final float mY0;
+        private final float mDx;
+        private final float mDy;
+        private final float mDSize2;
+
+        public LinearGradientPaint(float x0, float y0, float x1, float y1, int colors[],
+                float positions[], TileMode tile) {
+            super(colors, positions, tile);
+                mX0 = x0;
+                mY0 = y0;
+                mDx = x1 - x0;
+                mDy = y1 - y0;
+                mDSize2 = mDx * mDx + mDy * mDy;
+        }
+
+        public java.awt.PaintContext createContext(
+                java.awt.image.ColorModel      colorModel,
+                java.awt.Rectangle             deviceBounds,
+                java.awt.geom.Rectangle2D      userBounds,
+                java.awt.geom.AffineTransform  xform,
+                java.awt.RenderingHints        hints) {
+            precomputeGradientColors();
+            return new LinearGradientPaintContext(colorModel);
+        }
+
+        private class LinearGradientPaintContext implements java.awt.PaintContext {
+
+            private final java.awt.image.ColorModel mColorModel;
+
+            public LinearGradientPaintContext(java.awt.image.ColorModel colorModel) {
+                mColorModel = colorModel;
+                // FIXME: so far all this is always the same rect gotten in getRaster with an identity matrix?
+            }
+
+            public void dispose() {
+            }
+
+            public java.awt.image.ColorModel getColorModel() {
+                return mColorModel;
+            }
+
+            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
+
+                int[] data = new int[w*h];
+
+                if (mDx == 0) { // vertical gradient
+                    // compute first column and copy to all other columns
+                    int index = 0;
+                    for (int iy = 0 ; iy < h ; iy++) {
+                        int color = getColor(iy + y, mY0, mDy);
+                        for (int ix = 0 ; ix < w ; ix++) {
+                            data[index++] = color;
+                        }
+                    }
+                } else if (mDy == 0) { // horizontal
+                    // compute first line in a tmp array and copy to all lines
+                    int[] line = new int[w];
+                    for (int ix = 0 ; ix < w ; ix++) {
+                        line[ix] = getColor(ix + x, mX0, mDx);
+                    }
+
+                    for (int iy = 0 ; iy < h ; iy++) {
+                        System.arraycopy(line, 0, data, iy*w, line.length);
+                    }
+                } else {
+                    int index = 0;
+                    for (int iy = 0 ; iy < h ; iy++) {
+                        for (int ix = 0 ; ix < w ; ix++) {
+                            data[index++] = getColor(ix + x, iy + y);
+                        }
+                    }
+                }
+
+                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
+
+                return image.getRaster();
+            }
+        }
+
+        /** Returns a color for the easy vertical/horizontal mode */
+        private int getColor(float absPos, float refPos, float refSize) {
+            float pos = (absPos - refPos) / refSize;
+
+            return getGradientColor(pos);
+        }
+
+        /**
+         * Returns a color for an arbitrary point.
+         */
+        private int getColor(float x, float y) {
+            // find the x position on the gradient vector.
+            float _x = (mDx*mDy*(y-mY0) + mDy*mDy*mX0 + mDx*mDx*x) / mDSize2;
+            // from it get the position relative to the vector
+            float pos = (float) ((_x - mX0) / mDx);
+
+            return getGradientColor(pos);
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
deleted file mode 100644
index 9e30671..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix.java
+++ /dev/null
@@ -1,1032 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import java.awt.geom.AffineTransform;
-import java.awt.geom.NoninvertibleTransformException;
-
-
-/**
- * A matrix implementation overridden by the LayoutLib bridge.
- */
-public class Matrix extends _Original_Matrix {
-
-    float mValues[] = new float[9];
-
-    /**
-     * Create an identity matrix
-     */
-    public Matrix() {
-        reset();
-    }
-
-    /**
-     * Create a matrix that is a (deep) copy of src
-     * @param src The matrix to copy into this matrix
-     */
-    public Matrix(Matrix src) {
-        set(src);
-    }
-
-    /**
-     * Creates a Matrix object from the float array. The array becomes the internal storage
-     * of the object.
-     * @param data
-     */
-    private Matrix(float[] data) {
-        assert data.length != 9;
-        mValues = data;
-    }
-
-    //---------- Custom Methods
-
-    /**
-     * Adds the given transformation to the current Matrix
-     * <p/>This in effect does this = this*matrix
-     * @param matrix
-     */
-    private void addTransform(float[] matrix) {
-        float[] tmp = new float[9];
-
-        // first row
-        tmp[0] = matrix[0] * mValues[0] + matrix[1] * mValues[3] + matrix[2] * mValues[6];
-        tmp[1] = matrix[0] * mValues[1] + matrix[1] * mValues[4] + matrix[2] * mValues[7];
-        tmp[2] = matrix[0] * mValues[2] + matrix[1] * mValues[5] + matrix[2] * mValues[8];
-
-        // 2nd row
-        tmp[3] = matrix[3] * mValues[0] + matrix[4] * mValues[3] + matrix[5] * mValues[6];
-        tmp[4] = matrix[3] * mValues[1] + matrix[4] * mValues[4] + matrix[5] * mValues[7];
-        tmp[5] = matrix[3] * mValues[2] + matrix[4] * mValues[5] + matrix[5] * mValues[8];
-
-        // 3rd row
-        tmp[6] = matrix[6] * mValues[0] + matrix[7] * mValues[3] + matrix[8] * mValues[6];
-        tmp[7] = matrix[6] * mValues[1] + matrix[7] * mValues[4] + matrix[8] * mValues[7];
-        tmp[8] = matrix[6] * mValues[2] + matrix[7] * mValues[5] + matrix[8] * mValues[8];
-
-        // copy the result over to mValues
-        mValues = tmp;
-    }
-
-    public AffineTransform getTransform() {
-        // the AffineTransform constructor takes the value in a different order
-        // for a matrix [ 0 1 2 ]
-        //              [ 3 4 5 ]
-        // the order is 0, 3, 1, 4, 2, 5...
-        return new AffineTransform(mValues[0], mValues[3], mValues[1],
-                mValues[4], mValues[2], mValues[5]);
-    }
-
-    public boolean hasPerspective() {
-        return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1);
-    }
-
-    //----------
-
-    /**
-     * Returns true if the matrix is identity.
-     * This maybe faster than testing if (getType() == 0)
-     */
-    @Override
-    public boolean isIdentity() {
-        for (int i = 0, k = 0; i < 3; i++) {
-            for (int j = 0; j < 3; j++, k++) {
-                if (mValues[k] != ((i==j) ? 1 : 0)) {
-                    return false;
-                }
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Returns true if will map a rectangle to another rectangle. This can be
-     * true if the matrix is identity, scale-only, or rotates a multiple of 90
-     * degrees.
-     */
-    @Override
-    public boolean rectStaysRect() {
-        return (computeTypeMask() & kRectStaysRect_Mask) != 0;
-    }
-
-    /**
-     * (deep) copy the src matrix into this matrix. If src is null, reset this
-     * matrix to the identity matrix.
-     */
-    public void set(Matrix src) {
-        if (src == null) {
-            reset();
-        } else {
-            System.arraycopy(src.mValues, 0, mValues, 0, mValues.length);
-        }
-    }
-
-    @Override
-    public void set(_Original_Matrix src) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /** Returns true if obj is a Matrix and its values equal our values.
-    */
-    @Override
-    public boolean equals(Object obj) {
-        if (obj != null && obj instanceof Matrix) {
-            Matrix matrix = (Matrix)obj;
-            for (int i = 0 ; i < 9 ; i++) {
-                if (mValues[i] != matrix.mValues[i]) {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    /** Set the matrix to identity */
-    @Override
-    public void reset() {
-        for (int i = 0, k = 0; i < 3; i++) {
-            for (int j = 0; j < 3; j++, k++) {
-                mValues[k] = ((i==j) ? 1 : 0);
-            }
-        }
-    }
-
-    /** Set the matrix to translate by (dx, dy). */
-    @Override
-    public void setTranslate(float dx, float dy) {
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = dx;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = dy;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to scale by sx and sy, with a pivot point at (px, py).
-     * The pivot point is the coordinate that should remain unchanged by the
-     * specified transformation.
-     */
-    @Override
-    public void setScale(float sx, float sy, float px, float py) {
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = -px;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = -py;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-
-        // scale
-        addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-    }
-
-    /** Set the matrix to scale by sx and sy. */
-    @Override
-    public void setScale(float sx, float sy) {
-        mValues[0] = sx;
-        mValues[1] = 0;
-        mValues[2] = 0;
-        mValues[3] = 0;
-        mValues[4] = sy;
-        mValues[5] = 0;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to rotate by the specified number of degrees, with a pivot
-     * point at (px, py). The pivot point is the coordinate that should remain
-     * unchanged by the specified transformation.
-     */
-    @Override
-    public void setRotate(float degrees, float px, float py) {
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = -px;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = -py;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-
-        // scale
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-        addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-    }
-
-    /**
-     * Set the matrix to rotate about (0,0) by the specified number of degrees.
-     */
-    @Override
-    public void setRotate(float degrees) {
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-
-        mValues[0] = cos;
-        mValues[1] = -sin;
-        mValues[2] = 0;
-        mValues[3] = sin;
-        mValues[4] = cos;
-        mValues[5] = 0;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to rotate by the specified sine and cosine values, with a
-     * pivot point at (px, py). The pivot point is the coordinate that should
-     * remain unchanged by the specified transformation.
-     */
-    @Override
-    public void setSinCos(float sinValue, float cosValue, float px, float py) {
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = -px;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = -py;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-
-        // scale
-        addTransform(new float[] { cosValue, -sinValue, 0, sinValue, cosValue, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-    }
-
-    /** Set the matrix to rotate by the specified sine and cosine values. */
-    @Override
-    public void setSinCos(float sinValue, float cosValue) {
-        mValues[0] = cosValue;
-        mValues[1] = -sinValue;
-        mValues[2] = 0;
-        mValues[3] = sinValue;
-        mValues[4] = cosValue;
-        mValues[5] = 0;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to skew by sx and sy, with a pivot point at (px, py).
-     * The pivot point is the coordinate that should remain unchanged by the
-     * specified transformation.
-     */
-    @Override
-    public void setSkew(float kx, float ky, float px, float py) {
-        // TODO: do it in one pass
-
-        // translate so that the pivot is in 0,0
-        mValues[0] = 1;
-        mValues[1] = 0;
-        mValues[2] = -px;
-        mValues[3] = 0;
-        mValues[4] = 1;
-        mValues[5] = -py;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-
-        // scale
-        addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-    }
-
-    /** Set the matrix to skew by sx and sy. */
-    @Override
-    public void setSkew(float kx, float ky) {
-        mValues[0] = 1;
-        mValues[1] = kx;
-        mValues[2] = -0;
-        mValues[3] = ky;
-        mValues[4] = 1;
-        mValues[5] = 0;
-        mValues[6] = 0;
-        mValues[7] = 0;
-        mValues[8] = 1;
-    }
-
-    /**
-     * Set the matrix to the concatenation of the two specified matrices,
-     * returning true if the the result can be represented. Either of the two
-     * matrices may also be the target matrix. this = a * b
-     */
-    public boolean setConcat(Matrix a, Matrix b) {
-        if (a == this) {
-            preConcat(b);
-        } else if (b == this) {
-            postConcat(b);
-        } else {
-            Matrix tmp = new Matrix(b);
-            tmp.addTransform(a.mValues);
-            set(tmp);
-        }
-
-        return true;
-    }
-
-    @Override
-    public boolean setConcat(_Original_Matrix a, _Original_Matrix b) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-     * Preconcats the matrix with the specified translation.
-     * M' = M * T(dx, dy)
-     */
-    @Override
-    public boolean preTranslate(float dx, float dy) {
-        // create a matrix that will be multiply by this
-        Matrix m = new Matrix(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 });
-        m.addTransform(this.mValues);
-
-        System.arraycopy(m.mValues, 0, mValues, 0, 9);
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified scale.
-     * M' = M * S(sx, sy, px, py)
-     */
-    @Override
-    public boolean preScale(float sx, float sy, float px, float py) {
-        Matrix m = new Matrix();
-        m.setScale(sx, sy, px, py);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified scale.
-     * M' = M * S(sx, sy)
-     */
-    @Override
-    public boolean preScale(float sx, float sy) {
-        Matrix m = new Matrix();
-        m.setScale(sx, sy);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified rotation.
-     * M' = M * R(degrees, px, py)
-     */
-    @Override
-    public boolean preRotate(float degrees, float px, float py) {
-        Matrix m = new Matrix();
-        m.setRotate(degrees, px, py);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified rotation.
-     * M' = M * R(degrees)
-     */
-    @Override
-    public boolean preRotate(float degrees) {
-        Matrix m = new Matrix();
-        m.setRotate(degrees);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified skew.
-     * M' = M * K(kx, ky, px, py)
-     */
-    @Override
-    public boolean preSkew(float kx, float ky, float px, float py) {
-        Matrix m = new Matrix();
-        m.setSkew(kx, ky, px, py);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified skew.
-     * M' = M * K(kx, ky)
-     */
-    @Override
-    public boolean preSkew(float kx, float ky) {
-        Matrix m = new Matrix();
-        m.setSkew(kx, ky);
-        m.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    /**
-     * Preconcats the matrix with the specified matrix.
-     * M' = M * other
-     */
-    public boolean preConcat(Matrix other) {
-        Matrix m = new Matrix(other);
-        other.addTransform(mValues);
-        set(m);
-
-        return true;
-    }
-
-    @Override
-    public boolean preConcat(_Original_Matrix other) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-     * Postconcats the matrix with the specified translation.
-     * M' = T(dx, dy) * M
-     */
-    @Override
-    public boolean postTranslate(float dx, float dy) {
-        addTransform(new float[] { 1, 0, dx, 0, 1, dy, 0, 0, 1 });
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified scale.
-     * M' = S(sx, sy, px, py) * M
-     */
-    @Override
-    public boolean postScale(float sx, float sy, float px, float py) {
-        // TODO: do it in one pass
-        // translate so that the pivot is in 0,0
-        addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
-        // scale
-        addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified scale.
-     * M' = S(sx, sy) * M
-     */
-    @Override
-    public boolean postScale(float sx, float sy) {
-        addTransform(new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 });
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified rotation.
-     * M' = R(degrees, px, py) * M
-     */
-    @Override
-    public boolean postRotate(float degrees, float px, float py) {
-        // TODO: do it in one pass
-        // translate so that the pivot is in 0,0
-        addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
-        // scale
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-        addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified rotation.
-     * M' = R(degrees) * M
-     */
-    @Override
-    public boolean postRotate(float degrees) {
-        double rad = Math.toRadians(degrees);
-        float cos = (float)Math.cos(rad);
-        float sin = (float)Math.sin(rad);
-        addTransform(new float[] { cos, -sin, 0, sin, cos, 0, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified skew.
-     * M' = K(kx, ky, px, py) * M
-     */
-    @Override
-    public boolean postSkew(float kx, float ky, float px, float py) {
-        // TODO: do it in one pass
-        // translate so that the pivot is in 0,0
-        addTransform(new float[] { 1, 0, -px, 0, 1, py, 0, 0, 1 });
-        // scale
-        addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
-        // translate back the pivot
-        addTransform(new float[] { 1, 0, px, 0, 1, py, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified skew.
-     * M' = K(kx, ky) * M
-     */
-    @Override
-    public boolean postSkew(float kx, float ky) {
-        addTransform(new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
-
-        return true;
-    }
-
-    /**
-     * Postconcats the matrix with the specified matrix.
-     * M' = other * M
-     */
-    public boolean postConcat(Matrix other) {
-        addTransform(other.mValues);
-
-        return true;
-    }
-
-    @Override
-    public boolean postConcat(_Original_Matrix other) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /** Controlls how the src rect should align into the dst rect for
-        setRectToRect().
-    */
-    public enum ScaleToFit {
-        /**
-         * Scale in X and Y independently, so that src matches dst exactly.
-         * This may change the aspect ratio of the src.
-         */
-        FILL    (0),
-        /**
-         * Compute a scale that will maintain the original src aspect ratio,
-         * but will also ensure that src fits entirely inside dst. At least one
-         * axis (X or Y) will fit exactly. START aligns the result to the
-         * left and top edges of dst.
-         */
-        START   (1),
-        /**
-         * Compute a scale that will maintain the original src aspect ratio,
-         * but will also ensure that src fits entirely inside dst. At least one
-         * axis (X or Y) will fit exactly. The result is centered inside dst.
-         */
-        CENTER  (2),
-        /**
-         * Compute a scale that will maintain the original src aspect ratio,
-         * but will also ensure that src fits entirely inside dst. At least one
-         * axis (X or Y) will fit exactly. END aligns the result to the
-         * right and bottom edges of dst.
-         */
-        END     (3);
-
-        // the native values must match those in SkMatrix.h
-        ScaleToFit(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-    /**
-     * Set the matrix to the scale and translate values that map the source
-     * rectangle to the destination rectangle, returning true if the result
-     * can be represented.
-     *
-     * @param src the source rectangle to map from.
-     * @param dst the destination rectangle to map to.
-     * @param stf the ScaleToFit option
-     * @return true if the matrix can be represented by the rectangle mapping.
-     */
-    public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf) {
-        if (dst == null || src == null) {
-            throw new NullPointerException();
-        }
-
-        if (src.isEmpty()) {
-            reset();
-            return false;
-        }
-
-        if (dst.isEmpty()) {
-            mValues[0] = mValues[1] = mValues[2] = mValues[3] = mValues[4] = mValues[5]
-               = mValues[6] = mValues[7] = 0;
-            mValues[8] = 1;
-        } else {
-            float    tx, sx = dst.width() / src.width();
-            float    ty, sy = dst.height() / src.height();
-            boolean  xLarger = false;
-
-            if (stf != ScaleToFit.FILL) {
-                if (sx > sy) {
-                    xLarger = true;
-                    sx = sy;
-                } else {
-                    sy = sx;
-                }
-            }
-
-            tx = dst.left - src.left * sx;
-            ty = dst.top - src.top * sy;
-            if (stf == ScaleToFit.CENTER || stf == ScaleToFit.END) {
-                float diff;
-
-                if (xLarger) {
-                    diff = dst.width() - src.width() * sy;
-                } else {
-                    diff = dst.height() - src.height() * sy;
-                }
-
-                if (stf == ScaleToFit.CENTER) {
-                    diff = diff / 2;
-                }
-
-                if (xLarger) {
-                    tx += diff;
-                } else {
-                    ty += diff;
-                }
-            }
-
-            mValues[0] = sx;
-            mValues[4] = sy;
-            mValues[2] = tx;
-            mValues[5] = ty;
-            mValues[1]  = mValues[3] = mValues[6] = mValues[7] = 0;
-
-        }
-        // shared cleanup
-        mValues[8] = 1;
-        return true;
-    }
-
-    @Override
-    public boolean setRectToRect(RectF src, RectF dst, _Original_Matrix.ScaleToFit stf) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-     * Set the matrix such that the specified src points would map to the
-     * specified dst points. The "points" are represented as an array of floats,
-     * order [x0, y0, x1, y1, ...], where each "point" is 2 float values.
-     *
-     * @param src   The array of src [x,y] pairs (points)
-     * @param srcIndex Index of the first pair of src values
-     * @param dst   The array of dst [x,y] pairs (points)
-     * @param dstIndex Index of the first pair of dst values
-     * @param pointCount The number of pairs/points to be used. Must be [0..4]
-     * @return true if the matrix was set to the specified transformation
-     */
-    @Override
-    public boolean setPolyToPoly(float[] src, int srcIndex,
-                                 float[] dst, int dstIndex,
-                                 int pointCount) {
-        if (pointCount > 4) {
-            throw new IllegalArgumentException();
-        }
-        checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
-        throw new UnsupportedOperationException("STUB NEEDED");
-    }
-
-    /**
-     * If this matrix can be inverted, return true and if inverse is not null,
-     * set inverse to be the inverse of this matrix. If this matrix cannot be
-     * inverted, ignore inverse and return false.
-     */
-    public boolean invert(Matrix inverse) {
-        if (inverse == null) {
-            return false;
-        }
-
-        try {
-            AffineTransform affineTransform = getTransform();
-            AffineTransform inverseTransform = affineTransform.createInverse();
-            inverse.mValues[0] = (float)inverseTransform.getScaleX();
-            inverse.mValues[1] = (float)inverseTransform.getShearX();
-            inverse.mValues[2] = (float)inverseTransform.getTranslateX();
-            inverse.mValues[3] = (float)inverseTransform.getScaleX();
-            inverse.mValues[4] = (float)inverseTransform.getShearY();
-            inverse.mValues[5] = (float)inverseTransform.getTranslateY();
-
-            return true;
-        } catch (NoninvertibleTransformException e) {
-            return false;
-        }
-    }
-
-    @Override
-    public boolean invert(_Original_Matrix inverse) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    /**
-    * Apply this matrix to the array of 2D points specified by src, and write
-     * the transformed points into the array of points specified by dst. The
-     * two arrays represent their "points" as pairs of floats [x, y].
-     *
-     * @param dst   The array of dst points (x,y pairs)
-     * @param dstIndex The index of the first [x,y] pair of dst floats
-     * @param src   The array of src points (x,y pairs)
-     * @param srcIndex The index of the first [x,y] pair of src floats
-     * @param pointCount The number of points (x,y pairs) to transform
-     */
-    @Override
-    public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
-                          int pointCount) {
-        checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
-
-        for (int i = 0 ; i < pointCount ; i++) {
-            // just in case we are doing in place, we better put this in temp vars
-            float x = mValues[0] * src[i + srcIndex] +
-                      mValues[1] * src[i + srcIndex + 1] +
-                      mValues[2];
-            float y = mValues[3] * src[i + srcIndex] +
-                      mValues[4] * src[i + srcIndex + 1] +
-                      mValues[5];
-
-            dst[i + dstIndex]     = x;
-            dst[i + dstIndex + 1] = y;
-        }
-    }
-
-    /**
-    * Apply this matrix to the array of 2D vectors specified by src, and write
-     * the transformed vectors into the array of vectors specified by dst. The
-     * two arrays represent their "vectors" as pairs of floats [x, y].
-     *
-     * @param dst   The array of dst vectors (x,y pairs)
-     * @param dstIndex The index of the first [x,y] pair of dst floats
-     * @param src   The array of src vectors (x,y pairs)
-     * @param srcIndex The index of the first [x,y] pair of src floats
-     * @param vectorCount The number of vectors (x,y pairs) to transform
-     */
-    @Override
-    public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,
-                          int vectorCount) {
-        checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount);
-        throw new UnsupportedOperationException("STUB NEEDED");
-    }
-
-    /**
-     * Apply this matrix to the array of 2D points specified by src, and write
-     * the transformed points into the array of points specified by dst. The
-     * two arrays represent their "points" as pairs of floats [x, y].
-     *
-     * @param dst   The array of dst points (x,y pairs)
-     * @param src   The array of src points (x,y pairs)
-     */
-    @Override
-    public void mapPoints(float[] dst, float[] src) {
-        if (dst.length != src.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        mapPoints(dst, 0, src, 0, dst.length >> 1);
-    }
-
-    /**
-     * Apply this matrix to the array of 2D vectors specified by src, and write
-     * the transformed vectors into the array of vectors specified by dst. The
-     * two arrays represent their "vectors" as pairs of floats [x, y].
-     *
-     * @param dst   The array of dst vectors (x,y pairs)
-     * @param src   The array of src vectors (x,y pairs)
-     */
-    @Override
-    public void mapVectors(float[] dst, float[] src) {
-        if (dst.length != src.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        mapVectors(dst, 0, src, 0, dst.length >> 1);
-    }
-
-    /**
-     * Apply this matrix to the array of 2D points, and write the transformed
-     * points back into the array
-     *
-     * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
-     */
-    @Override
-    public void mapPoints(float[] pts) {
-        mapPoints(pts, 0, pts, 0, pts.length >> 1);
-    }
-
-    /**
-     * Apply this matrix to the array of 2D vectors, and write the transformed
-     * vectors back into the array.
-     * @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform.
-     */
-    @Override
-    public void mapVectors(float[] vecs) {
-        mapVectors(vecs, 0, vecs, 0, vecs.length >> 1);
-    }
-
-    /**
-     * Apply this matrix to the src rectangle, and write the transformed
-     * rectangle into dst. This is accomplished by transforming the 4 corners of
-     * src, and then setting dst to the bounds of those points.
-     *
-     * @param dst Where the transformed rectangle is written.
-     * @param src The original rectangle to be transformed.
-     * @return the result of calling rectStaysRect()
-     */
-    @Override
-    public boolean mapRect(RectF dst, RectF src) {
-        if (dst == null || src == null) {
-            throw new NullPointerException();
-        }
-
-        // array with 4 corners
-        float[] corners = new float[] {
-                src.left, src.top,
-                src.right, src.top,
-                src.right, src.bottom,
-                src.left, src.bottom,
-        };
-
-        // apply the transform to them.
-        mapPoints(corners);
-
-        // now put the result in the rect. We take the min/max of Xs and min/max of Ys
-        dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
-        dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
-
-        dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
-        dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
-
-        return rectStaysRect();
-    }
-
-    /**
-     * Apply this matrix to the rectangle, and write the transformed rectangle
-     * back into it. This is accomplished by transforming the 4 corners of rect,
-     * and then setting it to the bounds of those points
-     *
-     * @param rect The rectangle to transform.
-     * @return the result of calling rectStaysRect()
-     */
-    @Override
-    public boolean mapRect(RectF rect) {
-        return mapRect(rect, rect);
-    }
-
-    /**
-     * Return the mean radius of a circle after it has been mapped by
-     * this matrix. NOTE: in perspective this value assumes the circle
-     * has its center at the origin.
-     */
-    @Override
-    public float mapRadius(float radius) {
-        throw new UnsupportedOperationException("STUB NEEDED");
-    }
-
-    /** Copy 9 values from the matrix into the array.
-    */
-    @Override
-    public void getValues(float[] values) {
-        if (values.length < 9) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        System.arraycopy(mValues, 0, values, 0, mValues.length);
-    }
-
-    /** Copy 9 values from the array into the matrix.
-        Depending on the implementation of Matrix, these may be
-        transformed into 16.16 integers in the Matrix, such that
-        a subsequent call to getValues() will not yield exactly
-        the same values.
-    */
-    @Override
-    public void setValues(float[] values) {
-        if (values.length < 9) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        System.arraycopy(values, 0, mValues, 0, mValues.length);
-    }
-
-    @SuppressWarnings("unused")
-    private final static int kIdentity_Mask      = 0;
-    private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
-    private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
-    private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
-    private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
-    private final static int kRectStaysRect_Mask = 0x10;
-    @SuppressWarnings("unused")
-    private final static int kUnknown_Mask       = 0x80;
-
-    @SuppressWarnings("unused")
-    private final static int kAllMasks           = kTranslate_Mask |
-                                                     kScale_Mask |
-                                                     kAffine_Mask |
-                                                     kPerspective_Mask |
-                                                     kRectStaysRect_Mask;
-
-    // these guys align with the masks, so we can compute a mask from a variable 0/1
-    @SuppressWarnings("unused")
-    private final static int kTranslate_Shift = 0;
-    @SuppressWarnings("unused")
-    private final static int kScale_Shift = 1;
-    @SuppressWarnings("unused")
-    private final static int kAffine_Shift = 2;
-    @SuppressWarnings("unused")
-    private final static int kPerspective_Shift = 3;
-    private final static int kRectStaysRect_Shift = 4;
-
-    private int computeTypeMask() {
-        int mask = 0;
-
-        if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
-            mask |= kPerspective_Mask;
-        }
-
-        if (mValues[2] != 0. || mValues[5] != 0.) {
-            mask |= kTranslate_Mask;
-        }
-
-        float m00 = mValues[0];
-        float m01 = mValues[1];
-        float m10 = mValues[3];
-        float m11 = mValues[4];
-
-        if (m01 != 0. || m10 != 0.) {
-            mask |= kAffine_Mask;
-        }
-
-        if (m00 != 1. || m11 != 1.) {
-            mask |= kScale_Mask;
-        }
-
-        if ((mask & kPerspective_Mask) == 0) {
-            // map non-zero to 1
-            int im00 = m00 != 0 ? 1 : 0;
-            int im01 = m01 != 0 ? 1 : 0;
-            int im10 = m10 != 0 ? 1 : 0;
-            int im11 = m11 != 0 ? 1 : 0;
-
-            // record if the (p)rimary and (s)econdary diagonals are all 0 or
-            // all non-zero (answer is 0 or 1)
-            int dp0 = (im00 | im11) ^ 1;  // true if both are 0
-            int dp1 = im00 & im11;        // true if both are 1
-            int ds0 = (im01 | im10) ^ 1;  // true if both are 0
-            int ds1 = im01 & im10;        // true if both are 1
-
-            // return 1 if primary is 1 and secondary is 0 or
-            // primary is 0 and secondary is 1
-            mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
-        }
-
-        return mask;
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
new file mode 100644
index 0000000..0966f39
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -0,0 +1,1040 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+import android.graphics.Matrix.ScaleToFit;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Matrix
+ *
+ * Through the layoutlib_create tool, the original native methods of Matrix have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Matrix class.
+ *
+ * @see DelegateManager
+ *
+ */
+public final class Matrix_Delegate {
+
+    private final static int MATRIX_SIZE = 9;
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Matrix_Delegate> sManager =
+            new DelegateManager<Matrix_Delegate>();
+
+    // ---- delegate data ----
+    private float mValues[] = new float[MATRIX_SIZE];
+
+    // ---- Public Helper methods ----
+
+    public static Matrix_Delegate getDelegate(int native_instance) {
+        return sManager.getDelegate(native_instance);
+    }
+
+    /**
+     * Returns an {@link AffineTransform} matching the given Matrix.
+     */
+    public static AffineTransform getAffineTransform(Matrix m) {
+        Matrix_Delegate delegate = sManager.getDelegate(m.native_instance);
+        if (delegate == null) {
+            assert false;
+            return null;
+        }
+
+        return getAffineTransform(delegate);
+    }
+
+    public static boolean hasPerspective(Matrix m) {
+        Matrix_Delegate delegate = sManager.getDelegate(m.native_instance);
+        if (delegate == null) {
+            assert false;
+            return false;
+        }
+
+        return (delegate.mValues[6] != 0 || delegate.mValues[7] != 0 || delegate.mValues[8] != 1);
+    }
+
+    /**
+     * Sets the content of the matrix with the content of another matrix.
+     */
+    public void set(Matrix_Delegate matrix) {
+        System.arraycopy(matrix.mValues, 0, mValues, 0, MATRIX_SIZE);
+    }
+
+    /**
+     * Resets the matrix to be the identity matrix.
+     */
+    public void reset() {
+        reset(mValues);
+    }
+
+    /**
+     * Returns whether or not the matrix is identity.
+     */
+    public boolean isIdentity() {
+        for (int i = 0, k = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++, k++) {
+                if (mValues[k] != ((i==j) ? 1 : 0)) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+    // ---- native methods ----
+
+    /*package*/ static int native_create(int native_src_or_zero) {
+        // create the delegate
+        Matrix_Delegate newDelegate = new Matrix_Delegate();
+
+        // copy from values if needed.
+        if (native_src_or_zero > 0) {
+            Matrix_Delegate oldDelegate = sManager.getDelegate(native_src_or_zero);
+            if (oldDelegate != null) {
+                System.arraycopy(
+                        oldDelegate.mValues, 0,
+                        newDelegate.mValues, 0,
+                        MATRIX_SIZE);
+            }
+        }
+
+        return sManager.addDelegate(newDelegate);
+    }
+
+    /*package*/ static boolean native_isIdentity(int native_object) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        return d.isIdentity();
+    }
+
+    /*package*/ static boolean native_rectStaysRect(int native_object) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return true;
+        }
+
+        return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
+    }
+
+    /*package*/ static void native_reset(int native_object) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        reset(d.mValues);
+    }
+
+    /*package*/ static void native_set(int native_object, int other) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        Matrix_Delegate src = sManager.getDelegate(other);
+        if (src == null) {
+            assert false;
+            return;
+        }
+
+        System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE);
+    }
+
+    /*package*/ static void native_setTranslate(int native_object, float dx, float dy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        setTranslate(d.mValues, dx, dy);
+    }
+
+    /*package*/ static void native_setScale(int native_object, float sx, float sy, float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        d.mValues = getScale(sx, sy, px, py);
+    }
+
+    /*package*/ static void native_setScale(int native_object, float sx, float sy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        d.mValues[0] = sx;
+        d.mValues[1] = 0;
+        d.mValues[2] = 0;
+        d.mValues[3] = 0;
+        d.mValues[4] = sy;
+        d.mValues[5] = 0;
+        d.mValues[6] = 0;
+        d.mValues[7] = 0;
+        d.mValues[8] = 1;
+    }
+
+    /*package*/ static void native_setRotate(int native_object, float degrees, float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        d.mValues = getRotate(degrees, px, py);
+    }
+
+    /*package*/ static void native_setRotate(int native_object, float degrees) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        setRotate(d.mValues, degrees);
+    }
+
+    /*package*/ static void native_setSinCos(int native_object, float sinValue, float cosValue,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        setTranslate(d.mValues, -px, -py);
+
+        // scale
+        d.postTransform(getRotate(sinValue, cosValue));
+        // translate back the pivot
+        d.postTransform(getTranslate(px, py));
+    }
+
+    /*package*/ static void native_setSinCos(int native_object, float sinValue, float cosValue) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        setRotate(d.mValues, sinValue, cosValue);
+    }
+
+    /*package*/ static void native_setSkew(int native_object, float kx, float ky, float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        d.mValues = getSkew(kx, ky, px, py);
+    }
+
+    /*package*/ static void native_setSkew(int native_object, float kx, float ky) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        d.mValues[0] = 1;
+        d.mValues[1] = kx;
+        d.mValues[2] = -0;
+        d.mValues[3] = ky;
+        d.mValues[4] = 1;
+        d.mValues[5] = 0;
+        d.mValues[6] = 0;
+        d.mValues[7] = 0;
+        d.mValues[8] = 1;
+    }
+
+    /*package*/ static boolean native_setConcat(int native_object, int a, int b) {
+        if (a == native_object) {
+            return native_preConcat(native_object, b);
+        } else if (b == native_object) {
+            return native_postConcat(native_object, a);
+        }
+
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate a_mtx = sManager.getDelegate(a);
+        if (a_mtx == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate b_mtx = sManager.getDelegate(b);
+        if (b_mtx == null) {
+            assert false;
+            return false;
+        }
+
+        multiply(d.mValues, a_mtx.mValues, b_mtx.mValues);
+
+        return true;
+    }
+
+    /*package*/ static boolean native_preTranslate(int native_object, float dx, float dy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getTranslate(dx, dy));
+        return true;
+    }
+
+    /*package*/ static boolean native_preScale(int native_object, float sx, float sy,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getScale(sx, sy, px, py));
+        return true;
+    }
+
+    /*package*/ static boolean native_preScale(int native_object, float sx, float sy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getScale(sx, sy));
+        return true;
+    }
+
+    /*package*/ static boolean native_preRotate(int native_object, float degrees, float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getRotate(degrees, px, py));
+        return true;
+    }
+
+    /*package*/ static boolean native_preRotate(int native_object, float degrees) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        double rad = Math.toRadians(degrees);
+        float sin = (float)Math.sin(rad);
+        float cos = (float)Math.cos(rad);
+
+        d.preTransform(getRotate(sin, cos));
+        return true;
+    }
+
+    /*package*/ static boolean native_preSkew(int native_object, float kx, float ky,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getSkew(kx, ky, px, py));
+        return true;
+    }
+
+    /*package*/ static boolean native_preSkew(int native_object, float kx, float ky) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getSkew(kx, ky));
+        return true;
+    }
+
+    /*package*/ static boolean native_preConcat(int native_object, int other_matrix) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate other = sManager.getDelegate(other_matrix);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(other.mValues);
+        return true;
+    }
+
+    /*package*/ static boolean native_postTranslate(int native_object, float dx, float dy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(getTranslate(dx, dy));
+        return true;
+    }
+
+    /*package*/ static boolean native_postScale(int native_object, float sx, float sy,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(getScale(sx, sy, px, py));
+        return true;
+    }
+
+    /*package*/ static boolean native_postScale(int native_object, float sx, float sy) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(getScale(sx, sy));
+        return true;
+    }
+
+    /*package*/ static boolean native_postRotate(int native_object, float degrees, float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.preTransform(getRotate(degrees, px, py));
+        return true;
+    }
+
+    /*package*/ static boolean native_postRotate(int native_object, float degrees) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(getRotate(degrees));
+        return true;
+    }
+
+    /*package*/ static boolean native_postSkew(int native_object, float kx, float ky,
+            float px, float py) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(getSkew(kx, ky, px, py));
+        return true;
+    }
+
+    /*package*/ static boolean native_postSkew(int native_object, float kx, float ky) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(getSkew(kx, ky));
+        return true;
+    }
+
+    /*package*/ static boolean native_postConcat(int native_object, int other_matrix) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate other = sManager.getDelegate(other_matrix);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        d.postTransform(other.mValues);
+        return true;
+    }
+
+    /*package*/ static boolean native_setRectToRect(int native_object, RectF src, RectF dst, int stf) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        if (src.isEmpty()) {
+            reset(d.mValues);
+            return false;
+        }
+
+        if (dst.isEmpty()) {
+            d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5]
+               = d.mValues[6] = d.mValues[7] = 0;
+            d.mValues[8] = 1;
+        } else {
+            float    tx, sx = dst.width() / src.width();
+            float    ty, sy = dst.height() / src.height();
+            boolean  xLarger = false;
+
+            if (stf != ScaleToFit.FILL.nativeInt) {
+                if (sx > sy) {
+                    xLarger = true;
+                    sx = sy;
+                } else {
+                    sy = sx;
+                }
+            }
+
+            tx = dst.left - src.left * sx;
+            ty = dst.top - src.top * sy;
+            if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) {
+                float diff;
+
+                if (xLarger) {
+                    diff = dst.width() - src.width() * sy;
+                } else {
+                    diff = dst.height() - src.height() * sy;
+                }
+
+                if (stf == ScaleToFit.CENTER.nativeInt) {
+                    diff = diff / 2;
+                }
+
+                if (xLarger) {
+                    tx += diff;
+                } else {
+                    ty += diff;
+                }
+            }
+
+            d.mValues[0] = sx;
+            d.mValues[4] = sy;
+            d.mValues[2] = tx;
+            d.mValues[5] = ty;
+            d.mValues[1]  = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0;
+
+        }
+        // shared cleanup
+        d.mValues[8] = 1;
+        return true;
+    }
+
+    /*package*/ static boolean native_setPolyToPoly(int native_object, float[] src, int srcIndex,
+            float[] dst, int dstIndex, int pointCount) {
+        // FIXME
+        throw new UnsupportedOperationException("Native delegate needed: Matrix_Delegate.native_setPolyToPoly");
+    }
+
+    /*package*/ static boolean native_invert(int native_object, int inverse) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate inv_mtx = sManager.getDelegate(inverse);
+        if (inv_mtx == null) {
+            assert false;
+            return false;
+        }
+
+
+        try {
+            AffineTransform affineTransform = getAffineTransform(d);
+            AffineTransform inverseTransform = affineTransform.createInverse();
+            inv_mtx.mValues[0] = (float)inverseTransform.getScaleX();
+            inv_mtx.mValues[1] = (float)inverseTransform.getShearX();
+            inv_mtx.mValues[2] = (float)inverseTransform.getTranslateX();
+            inv_mtx.mValues[3] = (float)inverseTransform.getScaleX();
+            inv_mtx.mValues[4] = (float)inverseTransform.getShearY();
+            inv_mtx.mValues[5] = (float)inverseTransform.getTranslateY();
+
+            return true;
+        } catch (NoninvertibleTransformException e) {
+            return false;
+        }
+    }
+
+    /*package*/ static void native_mapPoints(int native_object, float[] dst, int dstIndex,
+            float[] src, int srcIndex, int ptCount, boolean isPts) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        if (isPts) {
+            d.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
+        } else {
+            // src is vectors
+            // FIXME
+            throw new UnsupportedOperationException("Native delegate needed: Matrix_Delegate.native_mapPoints");
+        }
+    }
+
+    /*package*/ static boolean native_mapRect(int native_object, RectF dst, RectF src) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return false;
+        }
+
+        // array with 4 corners
+        float[] corners = new float[] {
+                src.left, src.top,
+                src.right, src.top,
+                src.right, src.bottom,
+                src.left, src.bottom,
+        };
+
+        // apply the transform to them.
+        d.mapPoints(corners);
+
+        // now put the result in the rect. We take the min/max of Xs and min/max of Ys
+        dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
+        dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
+
+        dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
+        dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
+
+
+        return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
+    }
+
+    /*package*/ static float native_mapRadius(int native_object, float radius) {
+        // FIXME
+        throw new UnsupportedOperationException("Native delegate needed: Matrix_Delegate.native_mapRadius");
+    }
+
+    /*package*/ static void native_getValues(int native_object, float[] values) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE);
+    }
+
+    /*package*/ static void native_setValues(int native_object, float[] values) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            assert false;
+            return;
+        }
+
+        System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE);
+    }
+
+    /*package*/ static boolean native_equals(int native_a, int native_b) {
+        Matrix_Delegate a = sManager.getDelegate(native_a);
+        if (a == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate b = sManager.getDelegate(native_b);
+        if (b == null) {
+            assert false;
+            return false;
+        }
+
+        for (int i = 0 ; i < MATRIX_SIZE ; i++) {
+            if (a.mValues[i] != b.mValues[i]) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /*package*/ static void finalizer(int native_instance) {
+        sManager.removeDelegate(native_instance);
+    }
+
+    // ---- Private helper methods ----
+
+    private static AffineTransform getAffineTransform(Matrix_Delegate d) {
+        return getAffineTransform(d.mValues);
+    }
+
+    /*package*/ static AffineTransform getAffineTransform(float[] matrix) {
+        // the AffineTransform constructor takes the value in a different order
+        // for a matrix [ 0 1 2 ]
+        //              [ 3 4 5 ]
+        // the order is 0, 3, 1, 4, 2, 5...
+        return new AffineTransform(
+                matrix[0], matrix[3], matrix[1],
+                matrix[4], matrix[2], matrix[5]);
+    }
+
+
+    /**
+     * Reset a matrix to the identity
+     */
+    private static void reset(float[] mtx) {
+        for (int i = 0, k = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++, k++) {
+                mtx[k] = ((i==j) ? 1 : 0);
+            }
+        }
+    }
+
+    @SuppressWarnings("unused")
+    private final static int kIdentity_Mask      = 0;
+    private final static int kTranslate_Mask     = 0x01;  //!< set if the matrix has translation
+    private final static int kScale_Mask         = 0x02;  //!< set if the matrix has X or Y scale
+    private final static int kAffine_Mask        = 0x04;  //!< set if the matrix skews or rotates
+    private final static int kPerspective_Mask   = 0x08;  //!< set if the matrix is in perspective
+    private final static int kRectStaysRect_Mask = 0x10;
+    @SuppressWarnings("unused")
+    private final static int kUnknown_Mask       = 0x80;
+
+    @SuppressWarnings("unused")
+    private final static int kAllMasks           = kTranslate_Mask |
+                                                   kScale_Mask |
+                                                   kAffine_Mask |
+                                                   kPerspective_Mask |
+                                                   kRectStaysRect_Mask;
+
+    // these guys align with the masks, so we can compute a mask from a variable 0/1
+    @SuppressWarnings("unused")
+    private final static int kTranslate_Shift = 0;
+    @SuppressWarnings("unused")
+    private final static int kScale_Shift = 1;
+    @SuppressWarnings("unused")
+    private final static int kAffine_Shift = 2;
+    @SuppressWarnings("unused")
+    private final static int kPerspective_Shift = 3;
+    private final static int kRectStaysRect_Shift = 4;
+
+    private int computeTypeMask() {
+        int mask = 0;
+
+        if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
+            mask |= kPerspective_Mask;
+        }
+
+        if (mValues[2] != 0. || mValues[5] != 0.) {
+            mask |= kTranslate_Mask;
+        }
+
+        float m00 = mValues[0];
+        float m01 = mValues[1];
+        float m10 = mValues[3];
+        float m11 = mValues[4];
+
+        if (m01 != 0. || m10 != 0.) {
+            mask |= kAffine_Mask;
+        }
+
+        if (m00 != 1. || m11 != 1.) {
+            mask |= kScale_Mask;
+        }
+
+        if ((mask & kPerspective_Mask) == 0) {
+            // map non-zero to 1
+            int im00 = m00 != 0 ? 1 : 0;
+            int im01 = m01 != 0 ? 1 : 0;
+            int im10 = m10 != 0 ? 1 : 0;
+            int im11 = m11 != 0 ? 1 : 0;
+
+            // record if the (p)rimary and (s)econdary diagonals are all 0 or
+            // all non-zero (answer is 0 or 1)
+            int dp0 = (im00 | im11) ^ 1;  // true if both are 0
+            int dp1 = im00 & im11;        // true if both are 1
+            int ds0 = (im01 | im10) ^ 1;  // true if both are 0
+            int ds1 = im01 & im10;        // true if both are 1
+
+            // return 1 if primary is 1 and secondary is 0 or
+            // primary is 0 and secondary is 1
+            mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
+        }
+
+        return mask;
+    }
+
+    /**
+     * Adds the given transformation to the current Matrix
+     * <p/>This in effect does this = this*matrix
+     * @param matrix
+     */
+    private void postTransform(float[] matrix) {
+        float[] tmp = new float[9];
+        multiply(tmp, mValues, matrix);
+        mValues = tmp;
+    }
+
+    /**
+     * Adds the given transformation to the current Matrix
+     * <p/>This in effect does this = matrix*this
+     * @param matrix
+     */
+    private void preTransform(float[] matrix) {
+        float[] tmp = new float[9];
+        multiply(tmp, matrix, mValues);
+        mValues = tmp;
+    }
+
+    /**
+     * Apply this matrix to the array of 2D points specified by src, and write
+      * the transformed points into the array of points specified by dst. The
+      * two arrays represent their "points" as pairs of floats [x, y].
+      *
+      * @param dst   The array of dst points (x,y pairs)
+      * @param dstIndex The index of the first [x,y] pair of dst floats
+      * @param src   The array of src points (x,y pairs)
+      * @param srcIndex The index of the first [x,y] pair of src floats
+      * @param pointCount The number of points (x,y pairs) to transform
+      */
+
+     private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
+                           int pointCount) {
+         //checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
+
+         float[] tmpDest = dst;
+         boolean inPlace = dst == src;
+         if (inPlace) {
+             tmpDest = new float[dstIndex + pointCount * 2];
+         }
+
+         for (int i = 0 ; i < pointCount ; i++) {
+             // just in case we are doing in place, we better put this in temp vars
+             float x = mValues[0] * src[i + srcIndex] +
+                       mValues[1] * src[i + srcIndex + 1] +
+                       mValues[2];
+             float y = mValues[3] * src[i + srcIndex] +
+                       mValues[4] * src[i + srcIndex + 1] +
+                       mValues[5];
+
+             tmpDest[i + dstIndex]     = x;
+             tmpDest[i + dstIndex + 1] = y;
+         }
+
+         if (inPlace) {
+             System.arraycopy(tmpDest, dstIndex, dst, dstIndex, pointCount * 2);
+         }
+     }
+
+     /**
+      * Apply this matrix to the array of 2D points, and write the transformed
+      * points back into the array
+      *
+      * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
+      */
+
+     private void mapPoints(float[] pts) {
+         mapPoints(pts, 0, pts, 0, pts.length >> 1);
+     }
+
+    /**
+     * multiply two matrices and store them in a 3rd.
+     * <p/>This in effect does dest = a*b
+     * dest cannot be the same as a or b.
+     */
+     /*package*/ static void multiply(float dest[], float[] a, float[] b) {
+        // first row
+        dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
+        dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
+        dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8];
+
+        // 2nd row
+        dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6];
+        dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7];
+        dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8];
+
+        // 3rd row
+        dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6];
+        dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7];
+        dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8];
+    }
+
+    /**
+     * Returns a matrix that represents a given translate
+     * @param dx
+     * @param dy
+     * @return
+     */
+    /*package*/ static float[] getTranslate(float dx, float dy) {
+        return setTranslate(new float[9], dx, dy);
+    }
+
+    /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) {
+        dest[0] = 1;
+        dest[1] = 0;
+        dest[2] = dx;
+        dest[3] = 0;
+        dest[4] = 1;
+        dest[5] = dy;
+        dest[6] = 0;
+        dest[7] = 0;
+        dest[8] = 1;
+        return dest;
+    }
+
+    /*package*/ static float[] getScale(float sx, float sy) {
+        return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
+    }
+
+    /**
+     * Returns a matrix that represents the given scale info.
+     * @param sx
+     * @param sy
+     * @param px
+     * @param py
+     */
+    /*package*/ static float[] getScale(float sx, float sy, float px, float py) {
+        float[] tmp = new float[9];
+        float[] tmp2 = new float[9];
+
+        // TODO: do it in one pass
+
+        // translate tmp so that the pivot is in 0,0
+        setTranslate(tmp, -px, -py);
+
+        // scale into tmp2
+        multiply(tmp2, tmp, getScale(sx, sy));
+
+        // translate back the pivot back into tmp
+        multiply(tmp, tmp2, getTranslate(px, py));
+
+        return tmp;
+    }
+
+
+    /*package*/ static float[] getRotate(float degrees) {
+        double rad = Math.toRadians(degrees);
+        float sin = (float)Math.sin(rad);
+        float cos = (float)Math.cos(rad);
+
+        return getRotate(sin, cos);
+    }
+
+    /*package*/ static float[] getRotate(float sin, float cos) {
+        return setRotate(new float[9], sin, cos);
+    }
+
+    /*package*/ static float[] setRotate(float[] dest, float degrees) {
+        double rad = Math.toRadians(degrees);
+        float sin = (float)Math.sin(rad);
+        float cos = (float)Math.cos(rad);
+
+        return setRotate(dest, sin, cos);
+    }
+
+    /*package*/ static float[] setRotate(float[] dest, float sin, float cos) {
+        dest[0] = cos;
+        dest[1] = -sin;
+        dest[2] = 0;
+        dest[3] = sin;
+        dest[4] = cos;
+        dest[5] = 0;
+        dest[6] = 0;
+        dest[7] = 0;
+        dest[8] = 1;
+        return dest;
+    }
+
+    /*package*/ static float[] getRotate(float degrees, float px, float py) {
+        float[] tmp = new float[9];
+        float[] tmp2 = new float[9];
+
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        setTranslate(tmp, -px, -py);
+
+        // rotate into tmp2
+        double rad = Math.toRadians(degrees);
+        float cos = (float)Math.cos(rad);
+        float sin = (float)Math.sin(rad);
+        multiply(tmp2, tmp, getRotate(sin, cos));
+
+        // translate back the pivot back into tmp
+        multiply(tmp, tmp2, getTranslate(px, py));
+
+        return tmp;
+    }
+
+    /*package*/ static float[] getSkew(float kx, float ky) {
+        return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
+    }
+
+    /*package*/ static float[] getSkew(float kx, float ky, float px, float py) {
+        float[] tmp = new float[9];
+        float[] tmp2 = new float[9];
+
+        // TODO: do it in one pass
+
+        // translate so that the pivot is in 0,0
+        setTranslate(tmp, -px, -py);
+
+        // skew into tmp2
+        multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
+        // translate back the pivot back into tmp
+        multiply(tmp, tmp2, getTranslate(px, py));
+
+        return tmp;
+    }
+
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java
deleted file mode 100644
index d13b5fe..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Paint.java
+++ /dev/null
@@ -1,1211 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import android.text.SpannableString;
-import android.text.SpannableStringBuilder;
-import android.text.SpannedString;
-import android.text.TextUtils;
-
-import java.awt.BasicStroke;
-import java.awt.Font;
-import java.awt.Toolkit;
-import java.awt.font.FontRenderContext;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A paint implementation overridden by the LayoutLib bridge.
- */
-public class Paint extends _Original_Paint {
-
-    private int mColor = 0xFFFFFFFF;
-    private float mStrokeWidth = 1.f;
-    private float mTextSize = 20;
-    private float mScaleX = 1;
-    private float mSkewX = 0;
-    private Align mAlign = Align.LEFT;
-    private Style mStyle = Style.FILL;
-    private float mStrokeMiter = 4.0f;
-    private Cap mCap = Cap.BUTT;
-    private Join mJoin = Join.MITER;
-    private int mFlags = 0;
-
-    /**
-     * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
-     */
-    public static final class FontInfo {
-        Font mFont;
-        java.awt.FontMetrics mMetrics;
-    }
-
-    private List<FontInfo> mFonts;
-    private final FontRenderContext mFontContext = new FontRenderContext(
-            new AffineTransform(), true, true);
-
-    public static final int ANTI_ALIAS_FLAG       = _Original_Paint.ANTI_ALIAS_FLAG;
-    public static final int FILTER_BITMAP_FLAG    = _Original_Paint.FILTER_BITMAP_FLAG;
-    public static final int DITHER_FLAG           = _Original_Paint.DITHER_FLAG;
-    public static final int UNDERLINE_TEXT_FLAG   = _Original_Paint.UNDERLINE_TEXT_FLAG;
-    public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG;
-    public static final int FAKE_BOLD_TEXT_FLAG   = _Original_Paint.FAKE_BOLD_TEXT_FLAG;
-    public static final int LINEAR_TEXT_FLAG      = _Original_Paint.LINEAR_TEXT_FLAG;
-    public static final int SUBPIXEL_TEXT_FLAG    = _Original_Paint.SUBPIXEL_TEXT_FLAG;
-    public static final int DEV_KERN_TEXT_FLAG    = _Original_Paint.DEV_KERN_TEXT_FLAG;
-
-    public static class FontMetrics extends _Original_Paint.FontMetrics {
-    }
-
-    public static class FontMetricsInt extends _Original_Paint.FontMetricsInt {
-    }
-
-    /**
-     * The Style specifies if the primitive being drawn is filled,
-     * stroked, or both (in the same color). The default is FILL.
-     */
-    public enum Style {
-        /**
-         * Geometry and text drawn with this style will be filled, ignoring all
-         * stroke-related settings in the paint.
-         */
-        FILL            (0),
-        /**
-         * Geometry and text drawn with this style will be stroked, respecting
-         * the stroke-related fields on the paint.
-         */
-        STROKE          (1),
-        /**
-         * Geometry and text drawn with this style will be both filled and
-         * stroked at the same time, respecting the stroke-related fields on
-         * the paint.
-         */
-        FILL_AND_STROKE (2);
-
-        Style(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-    /**
-     * The Cap specifies the treatment for the beginning and ending of
-     * stroked lines and paths. The default is BUTT.
-     */
-    public enum Cap {
-        /**
-         * The stroke ends with the path, and does not project beyond it.
-         */
-        BUTT    (0),
-        /**
-         * The stroke projects out as a square, with the center at the end
-         * of the path.
-         */
-        ROUND   (1),
-        /**
-         * The stroke projects out as a semicircle, with the center at the
-         * end of the path.
-         */
-        SQUARE  (2);
-
-        private Cap(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-
-        /** custom for layoutlib */
-        public int getJavaCap() {
-            switch (this) {
-                case BUTT:
-                    return BasicStroke.CAP_BUTT;
-                case ROUND:
-                    return BasicStroke.CAP_ROUND;
-                default:
-                case SQUARE:
-                    return BasicStroke.CAP_SQUARE;
-            }
-        }
-    }
-
-    /**
-     * The Join specifies the treatment where lines and curve segments
-     * join on a stroked path. The default is MITER.
-     */
-    public enum Join {
-        /**
-         * The outer edges of a join meet at a sharp angle
-         */
-        MITER   (0),
-        /**
-         * The outer edges of a join meet in a circular arc.
-         */
-        ROUND   (1),
-        /**
-         * The outer edges of a join meet with a straight line
-         */
-        BEVEL   (2);
-
-        private Join(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-
-        /** custom for layoutlib */
-        public int getJavaJoin() {
-            switch (this) {
-                default:
-                case MITER:
-                    return BasicStroke.JOIN_MITER;
-                case ROUND:
-                    return BasicStroke.JOIN_ROUND;
-                case BEVEL:
-                    return BasicStroke.JOIN_BEVEL;
-            }
-        }
-    }
-
-    /**
-     * Align specifies how drawText aligns its text relative to the
-     * [x,y] coordinates. The default is LEFT.
-     */
-    public enum Align {
-        /**
-         * The text is drawn to the right of the x,y origin
-         */
-        LEFT    (0),
-        /**
-         * The text is drawn centered horizontally on the x,y origin
-         */
-        CENTER  (1),
-        /**
-         * The text is drawn to the left of the x,y origin
-         */
-        RIGHT   (2);
-
-        private Align(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-    public Paint() {
-        this(0);
-    }
-
-    /*
-     * Do not remove or com.android.layoutlib.bridge.TestClassReplacement fails.
-     */
-    @Override
-    public void finalize() { }
-
-    public Paint(int flags) {
-        setFlags(flags | DEFAULT_PAINT_FLAGS);
-        initFont();
-    }
-
-    public Paint(Paint paint) {
-        set(paint);
-        initFont();
-    }
-
-    @Override
-    public void reset() {
-        super.reset();
-    }
-
-    /**
-     * Returns the list of {@link Font} objects. The first item is the main font, the rest
-     * are fall backs for characters not present in the main font.
-     */
-    public List<FontInfo> getFonts() {
-        return mFonts;
-    }
-
-    private void initFont() {
-        mTypeface = Typeface.DEFAULT;
-        updateFontObject();
-    }
-
-    /**
-     * Update the {@link Font} object from the typeface, text size and scaling
-     */
-    @SuppressWarnings("deprecation")
-    private void updateFontObject() {
-        if (mTypeface != null) {
-            // Get the fonts from the TypeFace object.
-            List<Font> fonts = mTypeface.getFonts();
-
-            // create new font objects as well as FontMetrics, based on the current text size
-            // and skew info.
-            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
-            for (Font font : fonts) {
-                FontInfo info = new FontInfo();
-                info.mFont = font.deriveFont(mTextSize);
-                if (mScaleX != 1.0 || mSkewX != 0) {
-                    // TODO: support skew
-                    info.mFont = info.mFont.deriveFont(new AffineTransform(
-                            mScaleX, mSkewX, 0, 0, 1, 0));
-                }
-                info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
-
-                infoList.add(info);
-            }
-
-            mFonts = Collections.unmodifiableList(infoList);
-        }
-    }
-
-    //----------------------------------------
-
-    public void set(Paint src) {
-        if (this != src) {
-            mColor = src.mColor;
-            mTextSize = src.mTextSize;
-            mScaleX = src.mScaleX;
-            mSkewX = src.mSkewX;
-            mAlign = src.mAlign;
-            mStyle = src.mStyle;
-            mFlags = src.mFlags;
-
-            updateFontObject();
-
-            super.set(src);
-        }
-    }
-
-    @Override
-    public void setCompatibilityScaling(float factor) {
-        super.setCompatibilityScaling(factor);
-    }
-
-    @Override
-    public int getFlags() {
-        return mFlags;
-    }
-
-    @Override
-    public void setFlags(int flags) {
-        mFlags = flags;
-    }
-
-    @Override
-    public boolean isAntiAlias() {
-        return super.isAntiAlias();
-    }
-
-    @Override
-    public boolean isDither() {
-        return super.isDither();
-    }
-
-    @Override
-    public boolean isLinearText() {
-        return super.isLinearText();
-    }
-
-    @Override
-    public boolean isStrikeThruText() {
-        return super.isStrikeThruText();
-    }
-
-    @Override
-    public boolean isUnderlineText() {
-        return super.isUnderlineText();
-    }
-
-    @Override
-    public boolean isFakeBoldText() {
-        return super.isFakeBoldText();
-    }
-
-    @Override
-    public boolean isSubpixelText() {
-        return super.isSubpixelText();
-    }
-
-    @Override
-    public boolean isFilterBitmap() {
-        return super.isFilterBitmap();
-    }
-
-    /**
-     * Return the font's recommended interline spacing, given the Paint's
-     * settings for typeface, textSize, etc. If metrics is not null, return the
-     * fontmetric values in it.
-     *
-     * @param metrics If this object is not null, its fields are filled with
-     *                the appropriate values given the paint's text attributes.
-     * @return the font's recommended interline spacing.
-     */
-    public float getFontMetrics(FontMetrics metrics) {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
-            if (metrics != null) {
-                // Android expects negative ascent so we invert the value from Java.
-                metrics.top = - javaMetrics.getMaxAscent();
-                metrics.ascent = - javaMetrics.getAscent();
-                metrics.descent = javaMetrics.getDescent();
-                metrics.bottom = javaMetrics.getMaxDescent();
-                metrics.leading = javaMetrics.getLeading();
-            }
-
-            return javaMetrics.getHeight();
-        }
-
-        return 0;
-    }
-
-    public int getFontMetricsInt(FontMetricsInt metrics) {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
-            if (metrics != null) {
-                // Android expects negative ascent so we invert the value from Java.
-                metrics.top = - javaMetrics.getMaxAscent();
-                metrics.ascent = - javaMetrics.getAscent();
-                metrics.descent = javaMetrics.getDescent();
-                metrics.bottom = javaMetrics.getMaxDescent();
-                metrics.leading = javaMetrics.getLeading();
-            }
-
-            return javaMetrics.getHeight();
-        }
-
-        return 0;
-    }
-
-    /**
-     * Reimplemented to return Paint.FontMetrics instead of _Original_Paint.FontMetrics
-     */
-    public FontMetrics getFontMetrics() {
-        FontMetrics fm = new FontMetrics();
-        getFontMetrics(fm);
-        return fm;
-    }
-
-    /**
-     * Reimplemented to return Paint.FontMetricsInt instead of _Original_Paint.FontMetricsInt
-     */
-    public FontMetricsInt getFontMetricsInt() {
-        FontMetricsInt fm = new FontMetricsInt();
-        getFontMetricsInt(fm);
-        return fm;
-    }
-
-
-
-    @Override
-    public float getFontMetrics(_Original_Paint.FontMetrics metrics) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    @Override
-    public int getFontMetricsInt(_Original_Paint.FontMetricsInt metrics) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    @Override
-    public Typeface setTypeface(Typeface typeface) {
-        if (typeface != null) {
-            mTypeface = typeface;
-        } else {
-            mTypeface = Typeface.DEFAULT;
-        }
-
-        updateFontObject();
-
-        return typeface;
-    }
-
-    @Override
-    public Typeface getTypeface() {
-        return super.getTypeface();
-    }
-
-    @Override
-    public int getColor() {
-        return mColor;
-    }
-
-    @Override
-    public void setColor(int color) {
-        mColor = color;
-    }
-
-    @Override
-    public void setARGB(int a, int r, int g, int b) {
-        super.setARGB(a, r, g, b);
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
-    }
-
-    @Override
-    public int getAlpha() {
-        return mColor >>> 24;
-    }
-
-    /**
-     * Set or clear the shader object.
-     * <p />
-     * Pass null to clear any previous shader.
-     * As a convenience, the parameter passed is also returned.
-     *
-     * @param shader May be null. the new shader to be installed in the paint
-     * @return       shader
-     */
-    @Override
-    public Shader setShader(Shader shader) {
-        return mShader = shader;
-    }
-
-    @Override
-    public Shader getShader() {
-        return super.getShader();
-    }
-
-    /**
-     * Set or clear the paint's colorfilter, returning the parameter.
-     *
-     * @param filter May be null. The new filter to be installed in the paint
-     * @return       filter
-     */
-    @Override
-    public ColorFilter setColorFilter(ColorFilter filter) {
-        mColorFilter = filter;
-        return filter;
-    }
-
-    @Override
-    public ColorFilter getColorFilter() {
-        return super.getColorFilter();
-    }
-
-    /**
-     * Set or clear the xfermode object.
-     * <p />
-     * Pass null to clear any previous xfermode.
-     * As a convenience, the parameter passed is also returned.
-     *
-     * @param xfermode May be null. The xfermode to be installed in the paint
-     * @return         xfermode
-     */
-    @Override
-    public Xfermode setXfermode(Xfermode xfermode) {
-        return mXfermode = xfermode;
-    }
-
-    @Override
-    public Xfermode getXfermode() {
-        return super.getXfermode();
-    }
-
-    @Override
-    public Rasterizer setRasterizer(Rasterizer rasterizer) {
-        mRasterizer = rasterizer;
-        return rasterizer;
-    }
-
-    @Override
-    public Rasterizer getRasterizer() {
-        return super.getRasterizer();
-    }
-
-    @Override
-    public void setShadowLayer(float radius, float dx, float dy, int color) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void clearShadowLayer() {
-        super.clearShadowLayer();
-    }
-
-    public void setTextAlign(Align align) {
-        mAlign = align;
-    }
-
-    @Override
-    public void setTextAlign(android.graphics._Original_Paint.Align align) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public Align getTextAlign() {
-        return mAlign;
-    }
-
-    public void setStyle(Style style) {
-        mStyle = style;
-    }
-
-    @Override
-    public void setStyle(android.graphics._Original_Paint.Style style) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public Style getStyle() {
-        return mStyle;
-    }
-
-    @Override
-    public void setDither(boolean dither) {
-        mFlags |= dither ? DITHER_FLAG : ~DITHER_FLAG;
-    }
-
-    @Override
-    public void setAntiAlias(boolean aa) {
-        mFlags |= aa ? ANTI_ALIAS_FLAG : ~ANTI_ALIAS_FLAG;
-    }
-
-    @Override
-    public void setFakeBoldText(boolean flag) {
-        mFlags |= flag ? FAKE_BOLD_TEXT_FLAG : ~FAKE_BOLD_TEXT_FLAG;
-    }
-
-    @Override
-    public void setLinearText(boolean flag) {
-        mFlags |= flag ? LINEAR_TEXT_FLAG : ~LINEAR_TEXT_FLAG;
-    }
-
-    @Override
-    public void setSubpixelText(boolean flag) {
-        mFlags |= flag ? SUBPIXEL_TEXT_FLAG : ~SUBPIXEL_TEXT_FLAG;
-    }
-
-    @Override
-    public void setUnderlineText(boolean flag) {
-        mFlags |= flag ? UNDERLINE_TEXT_FLAG : ~UNDERLINE_TEXT_FLAG;
-    }
-
-    @Override
-    public void setStrikeThruText(boolean flag) {
-        mFlags |= flag ? STRIKE_THRU_TEXT_FLAG : ~STRIKE_THRU_TEXT_FLAG;
-    }
-
-    @Override
-    public void setFilterBitmap(boolean flag) {
-        mFlags |= flag ? FILTER_BITMAP_FLAG : ~FILTER_BITMAP_FLAG;
-    }
-
-    @Override
-    public float getStrokeWidth() {
-        return mStrokeWidth;
-    }
-
-    @Override
-    public void setStrokeWidth(float width) {
-        mStrokeWidth = width;
-    }
-
-    @Override
-    public float getStrokeMiter() {
-        return mStrokeMiter;
-    }
-
-    @Override
-    public void setStrokeMiter(float miter) {
-        mStrokeMiter = miter;
-    }
-
-    @Override
-    public void setStrokeCap(android.graphics._Original_Paint.Cap cap) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public void setStrokeCap(Cap cap) {
-        mCap = cap;
-    }
-
-    public Cap getStrokeCap() {
-        return mCap;
-    }
-
-    @Override
-    public void setStrokeJoin(android.graphics._Original_Paint.Join join) {
-        throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN");
-    }
-
-    public void setStrokeJoin(Join join) {
-        mJoin = join;
-    }
-
-    public Join getStrokeJoin() {
-        return mJoin;
-    }
-
-    @Override
-    public boolean getFillPath(Path src, Path dst) {
-        return super.getFillPath(src, dst);
-    }
-
-    @Override
-    public PathEffect setPathEffect(PathEffect effect) {
-        mPathEffect = effect;
-        return effect;
-    }
-
-    @Override
-    public PathEffect getPathEffect() {
-        return super.getPathEffect();
-    }
-
-    @Override
-    public MaskFilter setMaskFilter(MaskFilter maskfilter) {
-        mMaskFilter = maskfilter;
-        return maskfilter;
-    }
-
-    @Override
-    public MaskFilter getMaskFilter() {
-        return super.getMaskFilter();
-    }
-
-    /**
-     * Return the paint's text size.
-     *
-     * @return the paint's text size.
-     */
-    @Override
-    public float getTextSize() {
-        return mTextSize;
-    }
-
-    /**
-     * Set the paint's text size. This value must be > 0
-     *
-     * @param textSize set the paint's text size.
-     */
-    @Override
-    public void setTextSize(float textSize) {
-        mTextSize = textSize;
-
-        updateFontObject();
-    }
-
-    /**
-     * Return the paint's horizontal scale factor for text. The default value
-     * is 1.0.
-     *
-     * @return the paint's scale factor in X for drawing/measuring text
-     */
-    @Override
-    public float getTextScaleX() {
-        return mScaleX;
-    }
-
-    /**
-     * Set the paint's horizontal scale factor for text. The default value
-     * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
-     * stretch the text narrower.
-     *
-     * @param scaleX set the paint's scale in X for drawing/measuring text.
-     */
-    @Override
-    public void setTextScaleX(float scaleX) {
-        mScaleX = scaleX;
-
-        updateFontObject();
-    }
-
-    /**
-     * Return the paint's horizontal skew factor for text. The default value
-     * is 0.
-     *
-     * @return         the paint's skew factor in X for drawing text.
-     */
-    @Override
-    public float getTextSkewX() {
-        return mSkewX;
-    }
-
-    /**
-     * Set the paint's horizontal skew factor for text. The default value
-     * is 0. For approximating oblique text, use values around -0.25.
-     *
-     * @param skewX set the paint's skew factor in X for drawing text.
-     */
-    @Override
-    public void setTextSkewX(float skewX) {
-        mSkewX = skewX;
-
-        updateFontObject();
-    }
-
-    @Override
-    public float getFontSpacing() {
-        return super.getFontSpacing();
-    }
-
-    /**
-     * Return the distance above (negative) the baseline (ascent) based on the
-     * current typeface and text size.
-     *
-     * @return the distance above (negative) the baseline (ascent) based on the
-     *         current typeface and text size.
-     */
-    @Override
-    public float ascent() {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
-            // Android expects negative ascent so we invert the value from Java.
-            return - javaMetrics.getAscent();
-        }
-
-        return 0;
-    }
-
-    /**
-     * Return the distance below (positive) the baseline (descent) based on the
-     * current typeface and text size.
-     *
-     * @return the distance below (positive) the baseline (descent) based on
-     *         the current typeface and text size.
-     */
-    @Override
-    public float descent() {
-        if (mFonts.size() > 0) {
-            java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
-            return javaMetrics.getDescent();
-        }
-
-        return 0;
-    }
-
-    /**
-     * Return the width of the text.
-     *
-     * @param text  The text to measure
-     * @param index The index of the first character to start measuring
-     * @param count THe number of characters to measure, beginning with start
-     * @return      The width of the text
-     */
-    @Override
-    public float measureText(char[] text, int index, int count) {
-        // WARNING: the logic in this method is similar to Canvas.drawText.
-        // Any change to this method should be reflected in Canvas.drawText
-        if (mFonts.size() > 0) {
-            FontInfo mainFont = mFonts.get(0);
-            int i = index;
-            int lastIndex = index + count;
-            float total = 0f;
-            while (i < lastIndex) {
-                // always start with the main font.
-                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
-                if (upTo == -1) {
-                    // shortcut to exit
-                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
-                } else if (upTo > 0) {
-                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
-                    i = upTo;
-                    // don't call continue at this point. Since it is certain the main font
-                    // cannot display the font a index upTo (now ==i), we move on to the
-                    // fallback fonts directly.
-                }
-
-                // no char supported, attempt to read the next char(s) with the
-                // fallback font. In this case we only test the first character
-                // and then go back to test with the main font.
-                // Special test for 2-char characters.
-                boolean foundFont = false;
-                for (int f = 1 ; f < mFonts.size() ; f++) {
-                    FontInfo fontInfo = mFonts.get(f);
-
-                    // need to check that the font can display the character. We test
-                    // differently if the char is a high surrogate.
-                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
-                    if (upTo == -1) {
-                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
-                        i += charCount;
-                        foundFont = true;
-                        break;
-
-                    }
-                }
-
-                // in case no font can display the char, measure it with the main font.
-                if (foundFont == false) {
-                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                    total += mainFont.mMetrics.charsWidth(text, i, size);
-                    i += size;
-                }
-            }
-        }
-
-        return 0;
-    }
-
-    /**
-     * Return the width of the text.
-     *
-     * @param text  The text to measure
-     * @param start The index of the first character to start measuring
-     * @param end   1 beyond the index of the last character to measure
-     * @return      The width of the text
-     */
-    @Override
-    public float measureText(String text, int start, int end) {
-        return measureText(text.toCharArray(), start, end - start);
-    }
-
-    /**
-     * Return the width of the text.
-     *
-     * @param text  The text to measure
-     * @return      The width of the text
-     */
-    @Override
-    public float measureText(String text) {
-        return measureText(text.toCharArray(), 0, text.length());
-    }
-
-    /*
-     * re-implement to call SpannableStringBuilder.measureText with a Paint object
-     * instead of an _Original_Paint
-     */
-    @Override
-    public float measureText(CharSequence text, int start, int end) {
-        if (text instanceof String) {
-            return measureText((String)text, start, end);
-        }
-        if (text instanceof SpannedString ||
-            text instanceof SpannableString) {
-            return measureText(text.toString(), start, end);
-        }
-        if (text instanceof SpannableStringBuilder) {
-            return ((SpannableStringBuilder)text).measureText(start, end, this);
-        }
-
-        char[] buf = TemporaryBuffer.obtain(end - start);
-        TextUtils.getChars(text, start, end, buf, 0);
-        float result = measureText(buf, 0, end - start);
-        TemporaryBuffer.recycle(buf);
-        return result;
-    }
-
-    /**
-     * Measure the text, stopping early if the measured width exceeds maxWidth.
-     * Return the number of chars that were measured, and if measuredWidth is
-     * not null, return in it the actual width measured.
-     *
-     * @param text  The text to measure
-     * @param index The offset into text to begin measuring at
-     * @param count The number of maximum number of entries to measure. If count
-     *              is negative, then the characters before index are measured
-     *              in reverse order. This allows for measuring the end of
-     *              string.
-     * @param maxWidth The maximum width to accumulate.
-     * @param measuredWidth Optional. If not null, returns the actual width
-     *                     measured.
-     * @return The number of chars that were measured. Will always be <=
-     *         abs(count).
-     */
-    @Override
-    public int breakText(char[] text, int index, int count,
-                                float maxWidth, float[] measuredWidth) {
-        int inc = count > 0 ? 1 : -1;
-
-        int measureIndex = 0;
-        float measureAcc = 0;
-        for (int i = index ; i != index + count ; i += inc, measureIndex++) {
-            int start, end;
-            if (i < index) {
-                start = i;
-                end = index;
-            } else {
-                start = index;
-                end = i;
-            }
-
-            // measure from start to end
-            float res = measureText(text, start, end - start + 1);
-
-            if (measuredWidth != null) {
-                measuredWidth[measureIndex] = res;
-            }
-
-            measureAcc += res;
-            if (res > maxWidth) {
-                // we should not return this char index, but since it's 0-based and we need
-                // to return a count, we simply return measureIndex;
-                return measureIndex;
-            }
-
-        }
-
-        return measureIndex;
-    }
-
-    /**
-     * Measure the text, stopping early if the measured width exceeds maxWidth.
-     * Return the number of chars that were measured, and if measuredWidth is
-     * not null, return in it the actual width measured.
-     *
-     * @param text  The text to measure
-     * @param measureForwards If true, measure forwards, starting at index.
-     *                        Otherwise, measure backwards, starting with the
-     *                        last character in the string.
-     * @param maxWidth The maximum width to accumulate.
-     * @param measuredWidth Optional. If not null, returns the actual width
-     *                     measured.
-     * @return The number of chars that were measured. Will always be <=
-     *         abs(count).
-     */
-    @Override
-    public int breakText(String text, boolean measureForwards,
-                                float maxWidth, float[] measuredWidth) {
-        return breakText(text,
-                0 /* start */, text.length() /* end */,
-                measureForwards, maxWidth, measuredWidth);
-    }
-
-    /**
-     * Measure the text, stopping early if the measured width exceeds maxWidth.
-     * Return the number of chars that were measured, and if measuredWidth is
-     * not null, return in it the actual width measured.
-     *
-     * @param text  The text to measure
-     * @param start The offset into text to begin measuring at
-     * @param end   The end of the text slice to measure.
-     * @param measureForwards If true, measure forwards, starting at start.
-     *                        Otherwise, measure backwards, starting with end.
-     * @param maxWidth The maximum width to accumulate.
-     * @param measuredWidth Optional. If not null, returns the actual width
-     *                     measured.
-     * @return The number of chars that were measured. Will always be <=
-     *         abs(end - start).
-     */
-    @Override
-    public int breakText(CharSequence text, int start, int end, boolean measureForwards,
-            float maxWidth, float[] measuredWidth) {
-        char[] buf = new char[end - start];
-        int result;
-
-        TextUtils.getChars(text, start, end, buf, 0);
-
-        if (measureForwards) {
-            result = breakText(buf, 0, end - start, maxWidth, measuredWidth);
-        } else {
-            result = breakText(buf, 0, -(end - start), maxWidth, measuredWidth);
-        }
-
-        return result;
-    }
-
-    /**
-     * Return the advance widths for the characters in the string.
-     *
-     * @param text     The text to measure
-     * @param index    The index of the first char to to measure
-     * @param count    The number of chars starting with index to measure
-     * @param widths   array to receive the advance widths of the characters.
-     *                 Must be at least a large as count.
-     * @return         the actual number of widths returned.
-     */
-    @Override
-    public int getTextWidths(char[] text, int index, int count,
-                             float[] widths) {
-        if (mFonts.size() > 0) {
-            if ((index | count) < 0 || index + count > text.length
-                    || count > widths.length) {
-                throw new ArrayIndexOutOfBoundsException();
-            }
-
-            // FIXME: handle multi-char characters.
-            // Need to figure out if the lengths of the width array takes into account
-            // multi-char characters.
-            for (int i = 0; i < count; i++) {
-                char c = text[i + index];
-                boolean found = false;
-                for (FontInfo info : mFonts) {
-                    if (info.mFont.canDisplay(c)) {
-                        widths[i] = info.mMetrics.charWidth(c);
-                        found = true;
-                        break;
-                    }
-                }
-
-                if (found == false) {
-                    // we stop there.
-                    return i;
-                }
-            }
-
-            return count;
-        }
-
-        return 0;
-    }
-
-    /**
-     * Return the advance widths for the characters in the string.
-     *
-     * @param text   The text to measure
-     * @param start  The index of the first char to to measure
-     * @param end    The end of the text slice to measure
-     * @param widths array to receive the advance widths of the characters.
-     *               Must be at least a large as the text.
-     * @return       the number of unichars in the specified text.
-     */
-    @Override
-    public int getTextWidths(String text, int start, int end, float[] widths) {
-        if ((start | end | (end - start) | (text.length() - end)) < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-        if (end - start > widths.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-
-        return getTextWidths(text.toCharArray(), start, end - start, widths);
-    }
-
-    /*
-     * re-implement to call SpannableStringBuilder.getTextWidths with a Paint object
-     * instead of an _Original_Paint
-     */
-    @Override
-    public int getTextWidths(CharSequence text, int start, int end, float[] widths) {
-        if (text instanceof String) {
-            return getTextWidths((String)text, start, end, widths);
-        }
-        if (text instanceof SpannedString || text instanceof SpannableString) {
-            return getTextWidths(text.toString(), start, end, widths);
-        }
-        if (text instanceof SpannableStringBuilder) {
-            return ((SpannableStringBuilder)text).getTextWidths(start, end, widths, this);
-        }
-
-        char[] buf = TemporaryBuffer.obtain(end - start);
-        TextUtils.getChars(text, start, end, buf, 0);
-        int result = getTextWidths(buf, 0, end - start, widths);
-        TemporaryBuffer.recycle(buf);
-        return result;
-    }
-
-    @Override
-    public int getTextWidths(String text, float[] widths) {
-        return super.getTextWidths(text, widths);
-    }
-
-    /**
-     * Return the path (outline) for the specified text.
-     * Note: just like Canvas.drawText, this will respect the Align setting in
-     * the paint.
-     *
-     * @param text     The text to retrieve the path from
-     * @param index    The index of the first character in text
-     * @param count    The number of characterss starting with index
-     * @param x        The x coordinate of the text's origin
-     * @param y        The y coordinate of the text's origin
-     * @param path     The path to receive the data describing the text. Must
-     *                 be allocated by the caller.
-     */
-    @Override
-    public void getTextPath(char[] text, int index, int count,
-                            float x, float y, Path path) {
-
-        // TODO this is the ORIGINAL implementation. REPLACE AS NEEDED OR REMOVE
-
-        if ((index | count) < 0 || index + count > text.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-
-        // TODO native_getTextPath(mNativePaint, text, index, count, x, y, path.ni());
-
-        throw new UnsupportedOperationException("IMPLEMENT AS NEEDED");
-    }
-
-    /**
-     * Return the path (outline) for the specified text.
-     * Note: just like Canvas.drawText, this will respect the Align setting
-     * in the paint.
-     *
-     * @param text  The text to retrieve the path from
-     * @param start The first character in the text
-     * @param end   1 past the last charcter in the text
-     * @param x     The x coordinate of the text's origin
-     * @param y     The y coordinate of the text's origin
-     * @param path  The path to receive the data describing the text. Must
-     *              be allocated by the caller.
-     */
-    @Override
-    public void getTextPath(String text, int start, int end,
-                            float x, float y, Path path) {
-        if ((start | end | (end - start) | (text.length() - end)) < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-
-        getTextPath(text.toCharArray(), start, end - start, x, y, path);
-    }
-
-    /**
-     * Return in bounds (allocated by the caller) the smallest rectangle that
-     * encloses all of the characters, with an implied origin at (0,0).
-     *
-     * @param text  String to measure and return its bounds
-     * @param start Index of the first char in the string to measure
-     * @param end   1 past the last char in the string measure
-     * @param bounds Returns the unioned bounds of all the text. Must be
-     *               allocated by the caller.
-     */
-    @Override
-    public void getTextBounds(String text, int start, int end, Rect bounds) {
-        if ((start | end | (end - start) | (text.length() - end)) < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-        if (bounds == null) {
-            throw new NullPointerException("need bounds Rect");
-        }
-
-        getTextBounds(text.toCharArray(), start, end - start, bounds);
-    }
-
-    /**
-     * Return in bounds (allocated by the caller) the smallest rectangle that
-     * encloses all of the characters, with an implied origin at (0,0).
-     *
-     * @param text  Array of chars to measure and return their unioned bounds
-     * @param index Index of the first char in the array to measure
-     * @param count The number of chars, beginning at index, to measure
-     * @param bounds Returns the unioned bounds of all the text. Must be
-     *               allocated by the caller.
-     */
-    @Override
-    public void getTextBounds(char[] text, int index, int count, Rect bounds) {
-        // FIXME
-        if (mFonts.size() > 0) {
-            if ((index | count) < 0 || index + count > text.length) {
-                throw new ArrayIndexOutOfBoundsException();
-            }
-            if (bounds == null) {
-                throw new NullPointerException("need bounds Rect");
-            }
-
-            FontInfo mainInfo = mFonts.get(0);
-
-            Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, mFontContext);
-            bounds.set(0, 0, (int)rect.getWidth(), (int)rect.getHeight());
-        }
-    }
-
-    public static void finalizer(int foo) {
-        // pass
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
new file mode 100644
index 0000000..6e90bdd
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -0,0 +1,963 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+import android.graphics.Paint.FontMetrics;
+import android.graphics.Paint.FontMetricsInt;
+import android.text.TextUtils;
+
+import java.awt.BasicStroke;
+import java.awt.Font;
+import java.awt.Toolkit;
+import java.awt.font.FontRenderContext;
+import java.awt.geom.AffineTransform;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Paint
+ *
+ * Through the layoutlib_create tool, the original native methods of Paint have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Paint class.
+ *
+ * @see DelegateManager
+ *
+ */
+public class Paint_Delegate {
+
+    /**
+     * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
+     */
+    /*package*/ static final class FontInfo {
+        Font mFont;
+        java.awt.FontMetrics mMetrics;
+    }
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Paint_Delegate> sManager =
+            new DelegateManager<Paint_Delegate>();
+
+    // ---- delegate helper data ----
+    private List<FontInfo> mFonts;
+    private final FontRenderContext mFontContext = new FontRenderContext(
+            new AffineTransform(), true, true);
+
+    // ---- delegate data ----
+    private int mFlags;
+    private int mColor;
+    private int mStyle;
+    private int mCap;
+    private int mJoin;
+    private int mTextAlign;
+    private int mTypeface;
+    private float mStrokeWidth;
+    private float mStrokeMiter;
+    private float mTextSize;
+    private float mTextScaleX;
+    private float mTextSkewX;
+
+    private int mXfermode;
+    private int mColorFilter;
+    private int mShader;
+    private int mPathEffect;
+    private int mMaskFilter;
+
+
+    // ---- Public Helper methods ----
+
+    public static Paint_Delegate getDelegate(int native_paint) {
+        return sManager.getDelegate(native_paint);
+    }
+
+    /**
+     * Returns the list of {@link Font} objects. The first item is the main font, the rest
+     * are fall backs for characters not present in the main font.
+     */
+    public List<FontInfo> getFonts() {
+        return mFonts;
+    }
+
+    public boolean isAntiAliased() {
+        return (mFlags & Paint.ANTI_ALIAS_FLAG) != 0;
+    }
+
+    public boolean isFilterBitmap() {
+        return (mFlags & Paint.FILTER_BITMAP_FLAG) != 0;
+    }
+
+    public int getStyle() {
+        return mStyle;
+    }
+
+    public int getColor() {
+        return mColor;
+    }
+
+    public int getAlpha() {
+        return mColor >>> 24;
+    }
+
+    public int getTextAlign() {
+        return mTextAlign;
+    }
+
+    public float getStrokeWidth() {
+        return mStrokeWidth;
+    }
+
+    public float getStrokeMiter() {
+        return mStrokeMiter;
+    }
+
+    public int getJavaCap() {
+        switch (Paint.sCapArray[mCap]) {
+            case BUTT:
+                return BasicStroke.CAP_BUTT;
+            case ROUND:
+                return BasicStroke.CAP_ROUND;
+            default:
+            case SQUARE:
+                return BasicStroke.CAP_SQUARE;
+        }
+    }
+
+    public int getJavaJoin() {
+        switch (Paint.sJoinArray[mJoin]) {
+            default:
+            case MITER:
+                return BasicStroke.JOIN_MITER;
+            case ROUND:
+                return BasicStroke.JOIN_ROUND;
+            case BEVEL:
+                return BasicStroke.JOIN_BEVEL;
+        }
+    }
+
+    public int getXfermode() {
+        return mXfermode;
+    }
+
+    public int getColorFilter() {
+        return mColorFilter;
+    }
+
+    public int getShader() {
+        return mShader;
+    }
+
+    public int getPathEffect() {
+        return mPathEffect;
+    }
+
+    public int getMaskFilter() {
+        return mMaskFilter;
+    }
+
+    // ---- native methods ----
+
+    /*package*/ static int getFlags(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mFlags;
+    }
+
+    /*package*/ static void setFlags(Paint thisPaint, int flags) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mFlags = flags;
+    }
+
+    /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) {
+        setFlag(thisPaint, Paint.FILTER_BITMAP_FLAG, filter);
+    }
+
+    /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) {
+        setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa);
+    }
+
+    /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) {
+        setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
+    }
+
+    /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) {
+        setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
+    }
+
+    /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) {
+        setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
+    }
+
+    /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) {
+        setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
+    }
+
+    /*package*/ static void setDither(Paint thisPaint, boolean dither) {
+        setFlag(thisPaint, Paint.DITHER_FLAG, dither);
+    }
+
+    /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) {
+        setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText);
+    }
+
+    /*package*/ static int getColor(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mColor;
+    }
+
+    /*package*/ static void setColor(Paint thisPaint, int color) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mColor = color;
+    }
+
+    /*package*/ static int getAlpha(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.getAlpha();
+    }
+
+    /*package*/ static void setAlpha(Paint thisPaint, int a) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mColor = (a << 24) | (delegate.mColor & 0x00FFFFFF);
+    }
+
+    /*package*/ static float getStrokeWidth(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 1.f;
+        }
+
+        return delegate.mStrokeWidth;
+    }
+
+    /*package*/ static void setStrokeWidth(Paint thisPaint, float width) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mStrokeWidth = width;
+    }
+
+    /*package*/ static float getStrokeMiter(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 1.f;
+        }
+
+        return delegate.mStrokeMiter;
+    }
+
+    /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mStrokeMiter = miter;
+    }
+
+    /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy,
+            int color) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static float getTextSize(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 1.f;
+        }
+
+        return delegate.mTextSize;
+    }
+
+    /*package*/ static void setTextSize(Paint thisPaint, float textSize) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mTextSize = textSize;
+    }
+
+    /*package*/ static float getTextScaleX(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 1.f;
+        }
+
+        return delegate.mTextScaleX;
+    }
+
+    /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mTextScaleX = scaleX;
+    }
+
+    /*package*/ static float getTextSkewX(Paint thisPaint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 1.f;
+        }
+
+        return delegate.mTextSkewX;
+    }
+
+    /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mTextSkewX = skewX;
+    }
+
+    /*package*/ static float ascent(Paint thisPaint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static float descent(Paint thisPaint) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
+            if (metrics != null) {
+                // Android expects negative ascent so we invert the value from Java.
+                metrics.top = - javaMetrics.getMaxAscent();
+                metrics.ascent = - javaMetrics.getAscent();
+                metrics.descent = javaMetrics.getDescent();
+                metrics.bottom = javaMetrics.getMaxDescent();
+                metrics.leading = javaMetrics.getLeading();
+            }
+
+            return javaMetrics.getHeight();
+        }
+
+        return 0;
+    }
+
+    /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics;
+            if (fmi != null) {
+                // Android expects negative ascent so we invert the value from Java.
+                fmi.top = - javaMetrics.getMaxAscent();
+                fmi.ascent = - javaMetrics.getAscent();
+                fmi.descent = javaMetrics.getDescent();
+                fmi.bottom = javaMetrics.getMaxDescent();
+                fmi.leading = javaMetrics.getLeading();
+            }
+
+            return javaMetrics.getHeight();
+        }
+
+        return 0;
+    }
+
+    /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
+            int count) {
+        // WARNING: the logic in this method is similar to Canvas.drawText.
+        // Any change to this method should be reflected in Canvas.drawText
+
+        // get the delegate
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.measureText(text, index, count);
+    }
+
+    /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) {
+        return native_measureText(thisPaint, text.toCharArray(), start, end - start);
+    }
+
+    /*package*/ static float native_measureText(Paint thisPaint, String text) {
+        return native_measureText(thisPaint, text.toCharArray(), 0, text.length());
+    }
+
+    /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
+            float maxWidth, float[] measuredWidth) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
+            float maxWidth, float[] measuredWidth) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+
+    /*package*/ static int native_init() {
+        Paint_Delegate newDelegate = new Paint_Delegate();
+        return sManager.addDelegate(newDelegate);
+    }
+
+    /*package*/ static int native_initWithPaint(int paint) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(paint);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        Paint_Delegate newDelegate = new Paint_Delegate(delegate);
+        return sManager.addDelegate(newDelegate);
+    }
+
+    /*package*/ static void native_reset(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.reset();
+    }
+
+    /*package*/ static void native_set(int native_dst, int native_src) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
+        if (delegate_dst == null) {
+            assert false;
+            return;
+        }
+
+        // get the delegate from the native int.
+        Paint_Delegate delegate_src = sManager.getDelegate(native_src);
+        if (delegate_src == null) {
+            assert false;
+            return;
+        }
+
+        delegate_dst.set(delegate_src);
+    }
+
+    /*package*/ static int native_getStyle(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mStyle;
+    }
+
+    /*package*/ static void native_setStyle(int native_object, int style) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mStyle = style;
+    }
+
+    /*package*/ static int native_getStrokeCap(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mCap;
+    }
+
+    /*package*/ static void native_setStrokeCap(int native_object, int cap) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mCap = cap;
+    }
+
+    /*package*/ static int native_getStrokeJoin(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mJoin;
+    }
+
+    /*package*/ static void native_setStrokeJoin(int native_object, int join) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mJoin = join;
+    }
+
+    /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_setShader(int native_object, int shader) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return shader;
+        }
+
+        return delegate.mShader = shader;
+    }
+
+    /*package*/ static int native_setColorFilter(int native_object, int filter) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return filter;
+        }
+
+        return delegate.mColorFilter = filter;
+    }
+
+    /*package*/ static int native_setXfermode(int native_object, int xfermode) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return xfermode;
+        }
+
+        return delegate.mXfermode = xfermode;
+    }
+
+    /*package*/ static int native_setPathEffect(int native_object, int effect) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return effect;
+        }
+
+        return delegate.mPathEffect = effect;
+    }
+
+    /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return maskfilter;
+        }
+
+        return delegate.mMaskFilter = maskfilter;
+    }
+
+    /*package*/ static int native_setTypeface(int native_object, int typeface) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mTypeface = typeface;
+    }
+
+    /*package*/ static int native_setRasterizer(int native_object, int rasterizer) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+
+    /*package*/ static int native_getTextAlign(int native_object) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mTextAlign;
+    }
+
+    /*package*/ static void native_setTextAlign(int native_object, int align) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        delegate.mTextAlign = align;
+    }
+
+    /*package*/ static float native_getFontMetrics(int native_paint, FontMetrics metrics) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_getTextWidths(int native_object, char[] text, int index,
+            int count, float[] widths) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_getTextWidths(int native_object, String text, int start,
+            int end, float[] widths) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static float native_getTextRunAdvances(int native_object,
+            char[] text, int index, int count, int contextIndex, int contextCount,
+            int flags, float[] advances, int advancesIndex) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(native_object);
+        if (delegate == null) {
+            assert false;
+            return 0.f;
+        }
+
+        if (delegate.mFonts.size() > 0) {
+            // FIXME: handle multi-char characters (see measureText)
+            float totalAdvance = 0;
+            for (int i = 0; i < count; i++) {
+                char c = text[i + index];
+                boolean found = false;
+                for (FontInfo info : delegate.mFonts) {
+                    if (info.mFont.canDisplay(c)) {
+                        float adv = info.mMetrics.charWidth(c);
+                        totalAdvance += adv;
+                        if (advances != null) {
+                            advances[i] = adv;
+                        }
+
+                        found = true;
+                        break;
+                    }
+                }
+
+                if (found == false) {
+                    // no advance for this char.
+                    if (advances != null) {
+                        advances[i] = 0.f;
+                    }
+                }
+            }
+
+            return totalAdvance;
+        }
+
+        return 0;
+
+    }
+
+    /*package*/ static float native_getTextRunAdvances(int native_object,
+            String text, int start, int end, int contextStart, int contextEnd,
+            int flags, float[] advances, int advancesIndex) {
+        // FIXME: support contextStart, contextEnd and direction flag
+        int count = end - start;
+        char[] buffer = TemporaryBuffer.obtain(count);
+        TextUtils.getChars(text, start, end, buffer, 0);
+
+        return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
+                contextEnd - contextStart, flags, advances, advancesIndex);
+    }
+
+    /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text,
+            int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text,
+            int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
+                char[] text, int index, int count, float x, float y, int path) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
+            String text, int start, int end, float x, float y, int path) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start,
+            int end, Rect bounds) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index,
+            int count, Rect bounds) {
+        // FIXME
+        throw new UnsupportedOperationException();
+    }
+
+    /*package*/ static void finalizer(int nativePaint) {
+        sManager.removeDelegate(nativePaint);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private Paint_Delegate() {
+        reset();
+
+        mTypeface = Typeface.sDefaults[0].native_instance;
+        updateFontObject();
+    }
+
+    private Paint_Delegate(Paint_Delegate paint) {
+        set(paint);
+        updateFontObject();
+    }
+
+    private void set(Paint_Delegate paint) {
+        mFlags = paint.mFlags;
+        mColor = paint.mColor;
+        mStyle = paint.mStyle;
+        mCap = paint.mCap;
+        mJoin = paint.mJoin;
+        mTextAlign = paint.mTextAlign;
+        mTypeface = paint.mTypeface;
+        mStrokeWidth = paint.mStrokeWidth;
+        mStrokeMiter = paint.mStrokeMiter;
+        mTextSize = paint.mTextSize;
+        mTextScaleX = paint.mTextScaleX;
+        mTextSkewX = paint.mTextSkewX;
+        mXfermode = paint.mXfermode;
+        mColorFilter = paint.mColorFilter;
+        mShader = paint.mShader;
+        mPathEffect = paint.mPathEffect;
+        mMaskFilter = paint.mMaskFilter;
+    }
+
+    private void reset() {
+        mFlags = Paint.DEFAULT_PAINT_FLAGS;
+        mColor = 0;
+        mStyle = 0;
+        mCap = 0;
+        mJoin = 0;
+        mTextAlign = 0;
+        mTypeface = 0;
+        mStrokeWidth = 1.f;
+        mStrokeMiter = 2.f;
+        mTextSize = 20.f;
+        mTextScaleX = 1.f;
+        mTextSkewX = 0.f;
+        mXfermode = 0;
+        mColorFilter = 0;
+        mShader = 0;
+        mPathEffect = 0;
+        mMaskFilter = 0;
+    }
+
+    /**
+     * Update the {@link Font} object from the typeface, text size and scaling
+     */
+    private void updateFontObject() {
+        if (mTypeface != 0) {
+            // Get the fonts from the TypeFace object.
+            List<Font> fonts = Typeface_Delegate.getFonts(mTypeface);
+
+            // create new font objects as well as FontMetrics, based on the current text size
+            // and skew info.
+            ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
+            for (Font font : fonts) {
+                FontInfo info = new FontInfo();
+                info.mFont = font.deriveFont(mTextSize);
+                if (mTextScaleX != 1.0 || mTextSkewX != 0) {
+                    // TODO: support skew
+                    info.mFont = info.mFont.deriveFont(new AffineTransform(
+                            mTextScaleX, mTextSkewX, 0, 0, 1, 0));
+                }
+                info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
+
+                infoList.add(info);
+            }
+
+            mFonts = Collections.unmodifiableList(infoList);
+        }
+    }
+
+    /*package*/ float measureText(char[] text, int index, int count) {
+        if (mFonts.size() > 0) {
+            FontInfo mainFont = mFonts.get(0);
+            int i = index;
+            int lastIndex = index + count;
+            float total = 0f;
+            while (i < lastIndex) {
+                // always start with the main font.
+                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
+                if (upTo == -1) {
+                    // shortcut to exit
+                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
+                } else if (upTo > 0) {
+                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
+                    i = upTo;
+                    // don't call continue at this point. Since it is certain the main font
+                    // cannot display the font a index upTo (now ==i), we move on to the
+                    // fallback fonts directly.
+                }
+
+                // no char supported, attempt to read the next char(s) with the
+                // fallback font. In this case we only test the first character
+                // and then go back to test with the main font.
+                // Special test for 2-char characters.
+                boolean foundFont = false;
+                for (int f = 1 ; f < mFonts.size() ; f++) {
+                    FontInfo fontInfo = mFonts.get(f);
+
+                    // need to check that the font can display the character. We test
+                    // differently if the char is a high surrogate.
+                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
+                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
+                    if (upTo == -1) {
+                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
+                        i += charCount;
+                        foundFont = true;
+                        break;
+
+                    }
+                }
+
+                // in case no font can display the char, measure it with the main font.
+                if (foundFont == false) {
+                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
+                    total += mainFont.mMetrics.charsWidth(text, i, size);
+                    i += size;
+                }
+            }
+        }
+
+        return 0;
+
+    }
+
+    private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            assert false;
+            return;
+        }
+
+        if (flagValue) {
+            delegate.mFlags |= flagMask;
+        } else {
+            delegate.mFlags &= ~flagMask;
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path.java b/tools/layoutlib/bridge/src/android/graphics/Path.java
index 12d2cde..c0bc005 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path.java
@@ -31,13 +31,13 @@
  * text on a path.
  */
 public class Path {
-    
+
     private FillType mFillType = FillType.WINDING;
     private GeneralPath mPath = new GeneralPath();
-    
+
     private float mLastX = 0;
     private float mLastY = 0;
-    
+
     //---------- Custom methods ----------
 
     public Shape getAwtShape() {
@@ -60,7 +60,7 @@
     public Path(Path src) {
         mPath.append(src.mPath, false /* connect */);
     }
-    
+
     /**
      * Clear any lines and curves from the path, making it empty.
      * This does NOT change the fill-type setting.
@@ -92,7 +92,7 @@
         EVEN_ODD        (GeneralPath.WIND_EVEN_ODD, false),
         INVERSE_WINDING (GeneralPath.WIND_NON_ZERO, true),
         INVERSE_EVEN_ODD(GeneralPath.WIND_EVEN_ODD, true);
-        
+
         FillType(int rule, boolean inverse) {
             this.rule = rule;
             this.inverse = inverse;
@@ -101,7 +101,7 @@
         final int rule;
         final boolean inverse;
     }
-    
+
     /**
      * Return the path's fill type. This defines how "inside" is
      * computed. The default value is WINDING.
@@ -121,7 +121,7 @@
         mFillType = ft;
         mPath.setWindingRule(ft.rule);
     }
-    
+
     /**
      * Returns true if the filltype is one of the INVERSE variants
      *
@@ -130,7 +130,7 @@
     public boolean isInverseFillType() {
         return mFillType.inverse;
     }
-    
+
     /**
      * Toggles the INVERSE state of the filltype
      */
@@ -150,7 +150,7 @@
                 break;
         }
     }
-    
+
     /**
      * Returns true if the path is empty (contains no lines or curves)
      *
@@ -350,7 +350,7 @@
                       boolean forceMoveTo) {
         throw new UnsupportedOperationException();
     }
-    
+
     /**
      * Append the specified arc to the path as a new contour. If the start of
      * the path is different from the path's current last point, then an
@@ -365,7 +365,7 @@
     public void arcTo(RectF oval, float startAngle, float sweepAngle) {
         throw new UnsupportedOperationException();
     }
-    
+
     /**
      * Close the current contour. If the current point is not equal to the
      * first point of the contour, a line segment is automatically added.
@@ -383,13 +383,13 @@
         CW  (0),    // must match enum in SkPath.h
         /** counter-clockwise */
         CCW (1);    // must match enum in SkPath.h
-        
+
         Direction(int ni) {
             nativeInt = ni;
         }
         final int nativeInt;
     }
-    
+
     /**
      * Add a closed rectangle contour to the path
      *
@@ -400,7 +400,7 @@
         if (rect == null) {
             throw new NullPointerException("need rect parameter");
         }
-        
+
         addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
     }
 
@@ -446,7 +446,7 @@
 
         // FIXME Need to support direction
         Ellipse2D ovalShape = new Ellipse2D.Float(oval.left, oval.top, oval.width(), oval.height());
-        
+
         mPath.append(ovalShape, false /* connect */);
     }
 
@@ -493,7 +493,7 @@
         // FIXME
         throw new UnsupportedOperationException();
     }
-    
+
     /**
      * Add a closed round-rectangle contour to the path. Each corner receives
      * two radius values [X, Y]. The corners are ordered top-left, top-right,
@@ -513,7 +513,7 @@
         // FIXME
         throw new UnsupportedOperationException();
     }
-    
+
     /**
      * Add a copy of src to the path, offset by (dx,dy)
      *
@@ -554,11 +554,11 @@
      */
     public void offset(float dx, float dy, Path dst) {
         GeneralPath newPath = new GeneralPath();
-        
+
         PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
-        
+
         newPath.append(iterator, false /* connect */);
-        
+
         if (dst != null) {
             dst.mPath = newPath;
         } else {
diff --git a/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java
new file mode 100644
index 0000000..6827ae7
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+/**
+ * Delegate implementing the native methods of android.graphics.PathEffect
+ *
+ * Through the layoutlib_create tool, the original native methods of PathEffect have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original PathEffect class.
+ *
+ * This also serve as a base class for all PathEffect delegate classes.
+ *
+ * @see DelegateManager
+ *
+ */
+public class PathEffect_Delegate {
+
+    // ---- delegate manager ----
+    protected static final DelegateManager<PathEffect_Delegate> sManager =
+            new DelegateManager<PathEffect_Delegate>();
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    public static PathEffect_Delegate getDelegate(int nativeShader) {
+        return sManager.getDelegate(nativeShader);
+    }
+
+    // ---- native methods ----
+
+    /*package*/ static void nativeDestructor(int native_patheffect) {
+        sManager.removeDelegate(native_patheffect);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode.java
deleted file mode 100644
index 974ae49..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import android.graphics.PorterDuff.Mode;
-
-public class PorterDuffXfermode extends Xfermode {
-    private final Mode mMode;
-
-    /**
-     * Create an xfermode that uses the specified porter-duff mode.
-     *
-     * @param mode           The porter-duff mode that is applied
-     */
-    public PorterDuffXfermode(PorterDuff.Mode mode) {
-        mMode = mode;
-    }
-    
-    //---------- Custom Methods
-    
-    public PorterDuff.Mode getMode() {
-        return mMode;
-    }
-    
-    //----------
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
new file mode 100644
index 0000000..c242e80
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+/**
+ * Delegate implementing the native methods of android.graphics.PorterDuffXfermode
+ *
+ * Through the layoutlib_create tool, the original native methods of PorterDuffXfermode have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original PorterDuffXfermode class.
+ *
+ * Because this extends {@link Xfermode_Delegate}, there's no need to use a
+ * {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
+ * {@link Xfermode_Delegate}.
+ *
+ */
+public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
+
+    // ---- delegate data ----
+
+    private final int mMode;
+
+    // ---- Public Helper methods ----
+
+    public int getMode() {
+        return mMode;
+    }
+
+    // ---- native methods ----
+
+    /*package*/ static int nativeCreateXfermode(int mode) {
+        PorterDuffXfermode_Delegate newDelegate = new PorterDuffXfermode_Delegate(mode);
+        return sManager.addDelegate(newDelegate);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private PorterDuffXfermode_Delegate(int mode) {
+        mMode = mode;
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
deleted file mode 100644
index 4409a80..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-public class RadialGradient extends GradientShader {
-
-    private RadialGradientPaint mPaint;
-
-    /**
-     * Create a shader that draws a radial gradient given the center and radius.
-     *
-     * @param x The x-coordinate of the center of the radius
-     * @param y The y-coordinate of the center of the radius
-     * @param radius Must be positive. The radius of the circle for this
-     *            gradient
-     * @param colors The colors to be distributed between the center and edge of
-     *            the circle
-     * @param positions May be NULL. The relative position of each corresponding
-     *            color in the colors array. If this is NULL, the the colors are
-     *            distributed evenly between the center and edge of the circle.
-     * @param tile The Shader tiling mode
-     */
-    public RadialGradient(float x, float y, float radius, int colors[], float positions[],
-            TileMode tile) {
-        super(colors, positions);
-        if (radius <= 0) {
-            throw new IllegalArgumentException("radius must be > 0");
-        }
-
-        mPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile);
-    }
-
-    /**
-     * Create a shader that draws a radial gradient given the center and radius.
-     *
-     * @param x The x-coordinate of the center of the radius
-     * @param y The y-coordinate of the center of the radius
-     * @param radius Must be positive. The radius of the circle for this
-     *            gradient
-     * @param color0 The color at the center of the circle.
-     * @param color1 The color at the edge of the circle.
-     * @param tile The Shader tiling mode
-     */
-    public RadialGradient(float x, float y, float radius, int color0, int color1, TileMode tile) {
-        this(x, y, radius, new int[] { color0, color1 }, null /* positions */, tile);
-    }
-
-    @Override
-    java.awt.Paint getJavaPaint() {
-        return mPaint;
-    }
-
-    private static class RadialGradientPaint extends GradientPaint {
-
-        private final float mX;
-        private final float mY;
-        private final float mRadius;
-
-        public RadialGradientPaint(float x, float y, float radius, int[] colors, float[] positions, TileMode mode) {
-            super(colors, positions, mode);
-            mX = x;
-            mY = y;
-            mRadius = radius;
-        }
-
-        public java.awt.PaintContext createContext(
-                java.awt.image.ColorModel     colorModel,
-                java.awt.Rectangle            deviceBounds,
-                java.awt.geom.Rectangle2D     userBounds,
-                java.awt.geom.AffineTransform xform,
-                java.awt.RenderingHints       hints) {
-            precomputeGradientColors();
-            return new RadialGradientPaintContext(colorModel);
-        }
-
-        private class RadialGradientPaintContext implements java.awt.PaintContext {
-
-            private final java.awt.image.ColorModel mColorModel;
-
-            public RadialGradientPaintContext(java.awt.image.ColorModel colorModel) {
-                mColorModel = colorModel;
-            }
-
-            public void dispose() {
-            }
-
-            public java.awt.image.ColorModel getColorModel() {
-                return mColorModel;
-            }
-
-            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
-                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
-
-                int[] data = new int[w*h];
-
-                // compute distance from each point to the center, and figure out the distance from
-                // it.
-                int index = 0;
-                for (int iy = 0 ; iy < h ; iy++) {
-                    for (int ix = 0 ; ix < w ; ix++) {
-                        float _x = x + ix - mX;
-                        float _y = y + iy - mY;
-                        float distance = (float) Math.sqrt(_x * _x + _y * _y);
-
-                        data[index++] = getGradientColor(distance / mRadius);
-                    }
-                }
-
-                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
-
-                return image.getRaster();
-            }
-
-        }
-    }
-
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
new file mode 100644
index 0000000..c4e764c
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+import android.graphics.Shader.TileMode;
+
+import java.awt.Paint;
+
+/**
+ * Delegate implementing the native methods of android.graphics.RadialGradient
+ *
+ * Through the layoutlib_create tool, the original native methods of RadialGradient have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original RadialGradient class.
+ *
+ * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
+ *
+ * @see Shader_Delegate
+ *
+ */
+public class RadialGradient_Delegate extends Gradient_Delegate {
+
+    // ---- delegate data ----
+    private java.awt.Paint mJavaPaint;
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public Paint getJavaPaint() {
+        return mJavaPaint;
+    }
+
+    // ---- native methods ----
+
+    /*package*/ static int nativeCreate1(float x, float y, float radius,
+            int colors[], float positions[], int tileMode) {
+        // figure out the tile
+        TileMode tile = null;
+        for (TileMode tm : TileMode.values()) {
+            if (tm.nativeInt == tileMode) {
+                tile = tm;
+                break;
+            }
+        }
+
+        RadialGradient_Delegate newDelegate = new RadialGradient_Delegate(x, y, radius,
+                colors, positions, tile);
+        return sManager.addDelegate(newDelegate);
+    }
+
+    /*package*/ static int nativeCreate2(float x, float y, float radius,
+            int color0, int color1, int tileMode) {
+        return nativeCreate1(x, y, radius, new int[] { color0, color1 }, null /*positions*/,
+                tileMode);
+    }
+
+    /*package*/ static int nativePostCreate1(int native_shader, float x, float y, float radius,
+            int colors[], float positions[], int tileMode) {
+        // nothing to be done here.
+        return 0;
+    }
+
+    /*package*/ static int nativePostCreate2(int native_shader, float x, float y, float radius,
+            int color0, int color1, int tileMode) {
+        // nothing to be done here.
+        return 0;
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    /**
+     * Create a shader that draws a radial gradient given the center and radius.
+     *
+     * @param x The x-coordinate of the center of the radius
+     * @param y The y-coordinate of the center of the radius
+     * @param radius Must be positive. The radius of the circle for this
+     *            gradient
+     * @param colors The colors to be distributed between the center and edge of
+     *            the circle
+     * @param positions May be NULL. The relative position of each corresponding
+     *            color in the colors array. If this is NULL, the the colors are
+     *            distributed evenly between the center and edge of the circle.
+     * @param tile The Shader tiling mode
+     */
+    private RadialGradient_Delegate(float x, float y, float radius, int colors[], float positions[],
+            TileMode tile) {
+        super(colors, positions);
+
+        mJavaPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile);
+    }
+
+    private static class RadialGradientPaint extends GradientPaint {
+
+        private final float mX;
+        private final float mY;
+        private final float mRadius;
+
+        public RadialGradientPaint(float x, float y, float radius, int[] colors, float[] positions,
+                TileMode mode) {
+            super(colors, positions, mode);
+            mX = x;
+            mY = y;
+            mRadius = radius;
+        }
+
+        public java.awt.PaintContext createContext(
+                java.awt.image.ColorModel     colorModel,
+                java.awt.Rectangle            deviceBounds,
+                java.awt.geom.Rectangle2D     userBounds,
+                java.awt.geom.AffineTransform xform,
+                java.awt.RenderingHints       hints) {
+            precomputeGradientColors();
+            return new RadialGradientPaintContext(colorModel);
+        }
+
+        private class RadialGradientPaintContext implements java.awt.PaintContext {
+
+            private final java.awt.image.ColorModel mColorModel;
+
+            public RadialGradientPaintContext(java.awt.image.ColorModel colorModel) {
+                mColorModel = colorModel;
+            }
+
+            public void dispose() {
+            }
+
+            public java.awt.image.ColorModel getColorModel() {
+                return mColorModel;
+            }
+
+            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
+
+                int[] data = new int[w*h];
+
+                // compute distance from each point to the center, and figure out the distance from
+                // it.
+                int index = 0;
+                for (int iy = 0 ; iy < h ; iy++) {
+                    for (int ix = 0 ; ix < w ; ix++) {
+                        float _x = x + ix - mX;
+                        float _y = y + iy - mY;
+                        float distance = (float) Math.sqrt(_x * _x + _y * _y);
+
+                        data[index++] = getGradientColor(distance / mRadius);
+                    }
+                }
+
+                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
+
+                return image.getRaster();
+            }
+
+        }
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader.java b/tools/layoutlib/bridge/src/android/graphics/Shader.java
deleted file mode 100644
index 0cc5940..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Shader.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-
-
-/**
- * Shader is the based class for objects that return horizontal spans of colors
- * during drawing. A subclass of Shader is installed in a Paint calling
- * paint.setShader(shader). After that any object (other than a bitmap) that is
- * drawn with that paint will get its color(s) from the shader.
- */
-public abstract class Shader {
-
-    private final Matrix mMatrix = new Matrix();
-
-    public enum TileMode {
-        /**
-         * replicate the edge color if the shader draws outside of its
-         * original bounds
-         */
-        CLAMP   (0),
-        /**
-         * repeat the shader's image horizontally and vertically
-         */
-        REPEAT  (1),
-        /**
-         * repeat the shader's image horizontally and vertically, alternating
-         * mirror images so that adjacent images always seam
-         */
-        MIRROR  (2);
-
-        TileMode(int nativeInt) {
-            this.nativeInt = nativeInt;
-        }
-        final int nativeInt;
-    }
-
-    /**
-     * Return true if the shader has a non-identity local matrix.
-     * @param localM If not null, it is set to the shader's local matrix.
-     * @return true if the shader has a non-identity local matrix
-     */
-    public boolean getLocalMatrix(Matrix localM) {
-        if (localM != null) {
-            localM.set(mMatrix);
-        }
-
-        return !mMatrix.isIdentity();
-    }
-
-    /**
-     * Set the shader's local matrix. Passing null will reset the shader's
-     * matrix to identity
-     * @param localM The shader's new local matrix, or null to specify identity
-     */
-    public void setLocalMatrix(Matrix localM) {
-        if (localM != null) {
-            mMatrix.set(localM);
-        } else {
-            mMatrix.reset();
-        }
-    }
-
-    /**
-     * Returns a java.awt.Paint object matching this shader.
-     */
-    abstract java.awt.Paint getJavaPaint();
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
new file mode 100644
index 0000000..4dcf144
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Shader
+ *
+ * Through the layoutlib_create tool, the original native methods of Shader have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Shader class.
+ *
+ * This also serve as a base class for all Shader delegate classes.
+ *
+ * @see DelegateManager
+ *
+ */
+public abstract class Shader_Delegate {
+
+    // ---- delegate manager ----
+    protected static final DelegateManager<Shader_Delegate> sManager =
+            new DelegateManager<Shader_Delegate>();
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+    private int mLocalMatrix = 0;
+
+    // ---- Public Helper methods ----
+
+    public static Shader_Delegate getDelegate(int nativeShader) {
+        return sManager.getDelegate(nativeShader);
+    }
+
+    public abstract java.awt.Paint getJavaPaint();
+
+    // ---- native methods ----
+
+    /*package*/ static void nativeDestructor(int native_shader, int native_skiaShader) {
+        sManager.removeDelegate(native_shader);
+    }
+
+    /*package*/ static boolean nativeGetLocalMatrix(int native_shader, int matrix_instance) {
+        // get the delegate from the native int.
+        Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader);
+        if (shaderDelegate == null) {
+            assert false;
+            return false;
+        }
+
+        Matrix_Delegate localMatrixDelegate = null;
+        if (shaderDelegate.mLocalMatrix > 0) {
+            localMatrixDelegate = Matrix_Delegate.getDelegate(shaderDelegate.mLocalMatrix);
+            if (localMatrixDelegate == null) {
+                assert false;
+                return false;
+            }
+        }
+
+        Matrix_Delegate destMatrixDelegate = null;
+        if (matrix_instance > 0) {
+            destMatrixDelegate = Matrix_Delegate.getDelegate(shaderDelegate.mLocalMatrix);
+            if (destMatrixDelegate == null) {
+                assert false;
+                return false;
+            }
+
+            if (localMatrixDelegate != null) {
+                destMatrixDelegate.set(localMatrixDelegate);
+            } else {
+                // since there's no local matrix, it's considered to be the identity, reset
+                // the destination matrix
+                destMatrixDelegate.reset();
+            }
+        }
+
+        return localMatrixDelegate == null || localMatrixDelegate.isIdentity();
+    }
+
+    /*package*/ static void nativeSetLocalMatrix(int native_shader, int native_skiaShader,
+            int matrix_instance) {
+        // get the delegate from the native int.
+        Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader);
+        if (shaderDelegate == null) {
+            assert false;
+            return;
+        }
+
+        shaderDelegate.mLocalMatrix = matrix_instance;
+    }
+
+    // ---- Private delegate/helper methods ----
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
deleted file mode 100644
index 87036ed..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-public class SweepGradient extends GradientShader {
-
-    private SweepGradientPaint mPaint;
-
-    /**
-     * A subclass of Shader that draws a sweep gradient around a center point.
-     *
-     * @param cx       The x-coordinate of the center
-     * @param cy       The y-coordinate of the center
-     * @param colors   The colors to be distributed between around the center.
-     *                 There must be at least 2 colors in the array.
-     * @param positions May be NULL. The relative position of
-     *                 each corresponding color in the colors array, beginning
-     *                 with 0 and ending with 1.0. If the values are not
-     *                 monotonic, the drawing may produce unexpected results.
-     *                 If positions is NULL, then the colors are automatically
-     *                 spaced evenly.
-     */
-    public SweepGradient(float cx, float cy,
-                         int colors[], float positions[]) {
-        super(colors, positions);
-
-        mPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
-    }
-
-    /**
-     * A subclass of Shader that draws a sweep gradient around a center point.
-     *
-     * @param cx       The x-coordinate of the center
-     * @param cy       The y-coordinate of the center
-     * @param color0   The color to use at the start of the sweep
-     * @param color1   The color to use at the end of the sweep
-     */
-    public SweepGradient(float cx, float cy, int color0, int color1) {
-        this(cx, cy, new int[] { color0, color1}, null /*positions*/);
-    }
-
-    @Override
-    java.awt.Paint getJavaPaint() {
-        return mPaint;
-    }
-
-    private static class SweepGradientPaint extends GradientPaint {
-
-        private final float mCx;
-        private final float mCy;
-
-        public SweepGradientPaint(float cx, float cy, int[] colors, float[] positions) {
-            super(colors, positions, null /*tileMode*/);
-            mCx = cx;
-            mCy = cy;
-        }
-
-        public java.awt.PaintContext createContext(
-                java.awt.image.ColorModel     colorModel,
-                java.awt.Rectangle            deviceBounds,
-                java.awt.geom.Rectangle2D     userBounds,
-                java.awt.geom.AffineTransform xform,
-                java.awt.RenderingHints       hints) {
-            precomputeGradientColors();
-            return new SweepGradientPaintContext(colorModel);
-        }
-
-        private class SweepGradientPaintContext implements java.awt.PaintContext {
-
-            private final java.awt.image.ColorModel mColorModel;
-
-            public SweepGradientPaintContext(java.awt.image.ColorModel colorModel) {
-                mColorModel = colorModel;
-            }
-
-            public void dispose() {
-            }
-
-            public java.awt.image.ColorModel getColorModel() {
-                return mColorModel;
-            }
-
-            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
-                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
-
-                int[] data = new int[w*h];
-
-                // compute angle from each point to the center, and figure out the distance from
-                // it.
-                int index = 0;
-                for (int iy = 0 ; iy < h ; iy++) {
-                    for (int ix = 0 ; ix < w ; ix++) {
-                        float dx = x + ix - mCx;
-                        float dy = y + iy - mCy;
-                        float angle;
-                        if (dx == 0) {
-                            angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2);
-                        } else if (dy == 0) {
-                            angle = (float) (dx < 0 ? Math.PI : 0);
-                        } else {
-                            angle = (float) Math.atan(dy / dx);
-                            if (dx > 0) {
-                                if (dy < 0) {
-                                    angle += Math.PI * 2;
-                                }
-                            } else {
-                                angle += Math.PI;
-                            }
-                        }
-
-                        // convert to 0-1. value and get color
-                        data[index++] = getGradientColor((float) (angle / (2 * Math.PI)));
-                    }
-                }
-
-                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
-
-                return image.getRaster();
-            }
-
-        }
-    }
-
-}
-
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
new file mode 100644
index 0000000..0492e4f
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+import java.awt.Paint;
+
+/**
+ * Delegate implementing the native methods of android.graphics.SweepGradient
+ *
+ * Through the layoutlib_create tool, the original native methods of SweepGradient have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original SweepGradient class.
+ *
+ * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
+ * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
+ *
+ * @see Shader_Delegate
+ *
+ */
+public class SweepGradient_Delegate extends Gradient_Delegate {
+
+    // ---- delegate data ----
+    private java.awt.Paint mJavaPaint;
+
+    // ---- Public Helper methods ----
+
+    @Override
+    public Paint getJavaPaint() {
+        return mJavaPaint;
+    }
+
+    // ---- native methods ----
+
+    /*package*/ static int nativeCreate1(float x, float y, int colors[], float positions[]) {
+        SweepGradient_Delegate newDelegate = new SweepGradient_Delegate(x, y, colors, positions);
+        return sManager.addDelegate(newDelegate);
+    }
+
+    /*package*/ static int nativeCreate2(float x, float y, int color0, int color1) {
+        return nativeCreate1(x, y, new int[] { color0, color1 }, null /*positions*/);
+    }
+
+    /*package*/ static int nativePostCreate1(int native_shader, float cx, float cy,
+            int[] colors, float[] positions) {
+        // nothing to be done here.
+        return 0;
+    }
+
+    /*package*/ static int nativePostCreate2(int native_shader, float cx, float cy,
+            int color0, int color1) {
+        // nothing to be done here.
+        return 0;
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    /**
+     * A subclass of Shader that draws a sweep gradient around a center point.
+     *
+     * @param cx       The x-coordinate of the center
+     * @param cy       The y-coordinate of the center
+     * @param colors   The colors to be distributed between around the center.
+     *                 There must be at least 2 colors in the array.
+     * @param positions May be NULL. The relative position of
+     *                 each corresponding color in the colors array, beginning
+     *                 with 0 and ending with 1.0. If the values are not
+     *                 monotonic, the drawing may produce unexpected results.
+     *                 If positions is NULL, then the colors are automatically
+     *                 spaced evenly.
+     */
+    private SweepGradient_Delegate(float cx, float cy,
+                         int colors[], float positions[]) {
+        super(colors, positions);
+
+        mJavaPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
+    }
+
+    private static class SweepGradientPaint extends GradientPaint {
+
+        private final float mCx;
+        private final float mCy;
+
+        public SweepGradientPaint(float cx, float cy, int[] colors, float[] positions) {
+            super(colors, positions, null /*tileMode*/);
+            mCx = cx;
+            mCy = cy;
+        }
+
+        public java.awt.PaintContext createContext(
+                java.awt.image.ColorModel     colorModel,
+                java.awt.Rectangle            deviceBounds,
+                java.awt.geom.Rectangle2D     userBounds,
+                java.awt.geom.AffineTransform xform,
+                java.awt.RenderingHints       hints) {
+            precomputeGradientColors();
+            return new SweepGradientPaintContext(colorModel);
+        }
+
+        private class SweepGradientPaintContext implements java.awt.PaintContext {
+
+            private final java.awt.image.ColorModel mColorModel;
+
+            public SweepGradientPaintContext(java.awt.image.ColorModel colorModel) {
+                mColorModel = colorModel;
+            }
+
+            public void dispose() {
+            }
+
+            public java.awt.image.ColorModel getColorModel() {
+                return mColorModel;
+            }
+
+            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
+
+                int[] data = new int[w*h];
+
+                // compute angle from each point to the center, and figure out the distance from
+                // it.
+                int index = 0;
+                for (int iy = 0 ; iy < h ; iy++) {
+                    for (int ix = 0 ; ix < w ; ix++) {
+                        float dx = x + ix - mCx;
+                        float dy = y + iy - mCy;
+                        float angle;
+                        if (dx == 0) {
+                            angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2);
+                        } else if (dy == 0) {
+                            angle = (float) (dx < 0 ? Math.PI : 0);
+                        } else {
+                            angle = (float) Math.atan(dy / dx);
+                            if (dx > 0) {
+                                if (dy < 0) {
+                                    angle += Math.PI * 2;
+                                }
+                            } else {
+                                angle += Math.PI;
+                            }
+                        }
+
+                        // convert to 0-1. value and get color
+                        data[index++] = getGradientColor((float) (angle / (2 * Math.PI)));
+                    }
+                }
+
+                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
+
+                return image.getRaster();
+            }
+
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface.java b/tools/layoutlib/bridge/src/android/graphics/Typeface.java
deleted file mode 100644
index af3adb5..0000000
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.FontLoader;
-
-import android.content.res.AssetManager;
-
-import java.awt.Font;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Re-implementation of Typeface over java.awt
- */
-public class Typeface {
-    private static final String DEFAULT_FAMILY = "sans-serif";
-    private static final int[] styleBuffer = new int[1];
-
-    /** The default NORMAL typeface object */
-    public static Typeface DEFAULT;
-    /**
-     * The default BOLD typeface object. Note: this may be not actually be
-     * bold, depending on what fonts are installed. Call getStyle() to know
-     * for sure.
-     */
-    public static Typeface DEFAULT_BOLD;
-    /** The NORMAL style of the default sans serif typeface. */
-    public static Typeface SANS_SERIF;
-    /** The NORMAL style of the default serif typeface. */
-    public static Typeface SERIF;
-    /** The NORMAL style of the default monospace typeface. */
-    public static Typeface MONOSPACE;
-
-    private static Typeface[] sDefaults;
-    private static FontLoader mFontLoader;
-
-    private final int mStyle;
-    private final List<Font> mFonts;
-    private final String mFamily;
-
-    // Style
-    public static final int NORMAL = _Original_Typeface.NORMAL;
-    public static final int BOLD = _Original_Typeface.BOLD;
-    public static final int ITALIC = _Original_Typeface.ITALIC;
-    public static final int BOLD_ITALIC = _Original_Typeface.BOLD_ITALIC;
-
-    /**
-     * Returns the underlying {@link Font} objects. The first item in the list is the real
-     * font. Any other items are fallback fonts for characters not found in the first one.
-     */
-    public List<Font> getFonts() {
-        return mFonts;
-    }
-
-    /** Returns the typeface's intrinsic style attributes */
-    public int getStyle() {
-        return mStyle;
-    }
-
-    /** Returns true if getStyle() has the BOLD bit set. */
-    public final boolean isBold() {
-        return (getStyle() & BOLD) != 0;
-    }
-
-    /** Returns true if getStyle() has the ITALIC bit set. */
-    public final boolean isItalic() {
-        return (getStyle() & ITALIC) != 0;
-    }
-
-    /**
-     * Create a typeface object given a family name, and option style information.
-     * If null is passed for the name, then the "default" font will be chosen.
-     * The resulting typeface object can be queried (getStyle()) to discover what
-     * its "real" style characteristics are.
-     *
-     * @param familyName May be null. The name of the font family.
-     * @param style  The style (normal, bold, italic) of the typeface.
-     *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
-     * @return The best matching typeface.
-     */
-    public static Typeface create(String familyName, int style) {
-        styleBuffer[0] = style;
-        Font font = mFontLoader.getFont(familyName, styleBuffer);
-        if (font != null) {
-            ArrayList<Font> list = new ArrayList<Font>();
-            list.add(font);
-            list.addAll(mFontLoader.getFallBackFonts());
-            return new Typeface(familyName, styleBuffer[0], list);
-        }
-
-        return null;
-    }
-
-    /**
-     * Create a typeface object that best matches the specified existing
-     * typeface and the specified Style. Use this call if you want to pick a new
-     * style from the same family of an existing typeface object. If family is
-     * null, this selects from the default font's family.
-     *
-     * @param family May be null. The name of the existing type face.
-     * @param style  The style (normal, bold, italic) of the typeface.
-     *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
-     * @return The best matching typeface.
-     */
-    public static Typeface create(Typeface family, int style) {
-        styleBuffer[0] = style;
-        Font font = mFontLoader.getFont(family.mFamily, styleBuffer);
-        if (font != null) {
-            ArrayList<Font> list = new ArrayList<Font>();
-            list.add(font);
-            list.addAll(mFontLoader.getFallBackFonts());
-            return new Typeface(family.mFamily, styleBuffer[0], list);
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns one of the default typeface objects, based on the specified style
-     *
-     * @return the default typeface that corresponds to the style
-     */
-    public static Typeface defaultFromStyle(int style) {
-        return sDefaults[style];
-    }
-
-    /**
-     * Create a new typeface from the specified font data.
-     * @param mgr The application's asset manager
-     * @param path  The file name of the font data in the assets directory
-     * @return The new typeface.
-     */
-    public static Typeface createFromAsset(AssetManager mgr, String path) {
-        return null;
-        //return new Typeface(nativeCreateFromAsset(mgr, path));
-    }
-
-    // don't allow clients to call this directly
-    private Typeface(String family, int style, List<Font> fonts) {
-        mFamily = family;
-        mFonts = Collections.unmodifiableList(fonts);
-        mStyle = style;
-    }
-
-    public static void init(FontLoader fontLoader) {
-        mFontLoader = fontLoader;
-
-        DEFAULT = create(DEFAULT_FAMILY, NORMAL);
-        DEFAULT_BOLD = create(DEFAULT_FAMILY, BOLD);
-        SANS_SERIF = create("sans-serif", NORMAL);
-        SERIF = create("serif", NORMAL);
-        MONOSPACE = create("monospace", NORMAL);
-        sDefaults = new Typeface[] {
-                DEFAULT,
-                DEFAULT_BOLD,
-                create(DEFAULT_FAMILY, ITALIC),
-                create(DEFAULT_FAMILY, BOLD_ITALIC),
-        };
-
-        /*
-        DEFAULT         = create((String)null, 0);
-        DEFAULT_BOLD    = create((String)null, Typeface.BOLD);
-        SANS_SERIF      = create("sans-serif", 0);
-        SERIF           = create("serif", 0);
-        MONOSPACE       = create("monospace", 0);
-
-        sDefaults = new Typeface[] {
-            DEFAULT,
-            DEFAULT_BOLD,
-            create((String)null, Typeface.ITALIC),
-            create((String)null, Typeface.BOLD_ITALIC),
-        };*/
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
new file mode 100644
index 0000000..7e90e7d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.DelegateManager;
+import com.android.layoutlib.bridge.FontLoader;
+
+import android.content.res.AssetManager;
+
+import java.awt.Font;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Typeface
+ *
+ * Through the layoutlib_create tool, the original native methods of Typeface have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Typeface class.
+ *
+ * @see DelegateManager
+ *
+ */
+public final class Typeface_Delegate {
+
+    // ---- delegate manager ----
+    private static final DelegateManager<Typeface_Delegate> sManager =
+            new DelegateManager<Typeface_Delegate>();
+
+    // ---- delegate helper data ----
+    private static final String DEFAULT_FAMILY = "sans-serif";
+    private static final int[] STYLE_BUFFER = new int[1];
+
+    private static FontLoader sFontLoader;
+    private static final List<Typeface_Delegate> sPostInitDelegate =
+            new ArrayList<Typeface_Delegate>();
+
+    // ---- delegate data ----
+
+    private final String mFamily;
+    private int mStyle;
+    private List<Font> mFonts;
+
+
+    // ---- Public Helper methods ----
+
+    public static synchronized void init(FontLoader fontLoader) {
+        sFontLoader = fontLoader;
+
+        for (Typeface_Delegate delegate : sPostInitDelegate) {
+            delegate.init();
+        }
+        sPostInitDelegate.clear();
+    }
+
+    public static List<Font> getFonts(Typeface typeface) {
+        return getFonts(typeface.native_instance);
+    }
+
+    public static List<Font> getFonts(int native_int) {
+        Typeface_Delegate delegate = sManager.getDelegate(native_int);
+        if (delegate == null) {
+            assert false;
+            return null;
+        }
+
+        return delegate.mFonts;
+    }
+
+
+    // ---- native methods ----
+
+    /*package*/ static synchronized int nativeCreate(String familyName, int style) {
+        if (familyName == null) {
+            familyName = DEFAULT_FAMILY;
+        }
+
+        Typeface_Delegate newDelegate = new Typeface_Delegate(familyName, style);
+        if (sFontLoader != null) {
+            newDelegate.init();
+        } else {
+            // font loader has not been initialized yet, add the delegate to a list of delegates
+            // to init when the font loader is initialized.
+            // There won't be any rendering before this happens anyway.
+            sPostInitDelegate.add(newDelegate);
+        }
+
+        return sManager.addDelegate(newDelegate);
+    }
+
+    /*package*/ static synchronized int nativeCreateFromTypeface(int native_instance, int style) {
+        Typeface_Delegate delegate = sManager.getDelegate(native_instance);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        Typeface_Delegate newDelegate = new Typeface_Delegate(delegate.mFamily, style);
+        if (sFontLoader != null) {
+            newDelegate.init();
+        } else {
+            // font loader has not been initialized yet, add the delegate to a list of delegates
+            // to init when the font loader is initialized.
+            // There won't be any rendering before this happens anyway.
+            sPostInitDelegate.add(newDelegate);
+        }
+
+        return sManager.addDelegate(newDelegate);
+    }
+
+    /*package*/ static synchronized int nativeCreateFromAsset(AssetManager mgr, String path) {
+        // FIXME
+        throw new UnsupportedOperationException("Native delegate needed: Typeface_Delegate.nativeCreateFromAsset");
+    }
+
+    /*package*/ static synchronized int nativeCreateFromFile(String path) {
+        // FIXME
+        throw new UnsupportedOperationException("Native delegate needed: Typeface_Delegate.nativeCreateFromFile");
+    }
+
+    /*package*/ static void nativeUnref(int native_instance) {
+        sManager.removeDelegate(native_instance);
+    }
+
+    /*package*/ static int nativeGetStyle(int native_instance) {
+        Typeface_Delegate delegate = sManager.getDelegate(native_instance);
+        if (delegate == null) {
+            assert false;
+            return 0;
+        }
+
+        return delegate.mStyle;
+    }
+
+    /*package*/ static void setGammaForText(float blackGamma, float whiteGamma) {
+        // This is for device testing only: pass
+    }
+
+    // ---- Private delegate/helper methods ----
+
+    private Typeface_Delegate(String family, int style) {
+        mFamily = family;
+        mStyle = style;
+    }
+
+    private void init() {
+        STYLE_BUFFER[0] = mStyle;
+        Font font = sFontLoader.getFont(mFamily, STYLE_BUFFER);
+        if (font != null) {
+            List<Font> list = new ArrayList<Font>();
+            list.add(font);
+            list.addAll(sFontLoader.getFallBackFonts());
+            mFonts = Collections.unmodifiableList(list);
+            mStyle = STYLE_BUFFER[0];
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java
new file mode 100644
index 0000000..d4408cf
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+/**
+ * Delegate implementing the native methods of android.graphics.Xfermode
+ *
+ * Through the layoutlib_create tool, the original native methods of Xfermode have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original Xfermode class.
+ *
+ * This also serve as a base class for all Xfermode delegate classes.
+ *
+ * @see DelegateManager
+ *
+ */
+public class Xfermode_Delegate {
+
+    // ---- delegate manager ----
+    protected static final DelegateManager<Xfermode_Delegate> sManager =
+            new DelegateManager<Xfermode_Delegate>();
+
+    // ---- delegate helper data ----
+
+    // ---- delegate data ----
+
+    // ---- Public Helper methods ----
+
+    public static Xfermode_Delegate getDelegate(int native_instance) {
+        return sManager.getDelegate(native_instance);
+    }
+
+    // ---- native methods ----
+
+    /*package*/ static void finalizer(int native_instance) {
+        sManager.removeDelegate(native_instance);
+    }
+
+    // ---- Private delegate/helper methods ----
+
+}
diff --git a/tools/layoutlib/bridge/src/android/util/FloatMath.java b/tools/layoutlib/bridge/src/android/util/FloatMath.java
deleted file mode 100644
index aae44f2..0000000
--- a/tools/layoutlib/bridge/src/android/util/FloatMath.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.util;
-
-/**
- * Reimplements _Original_FloatMath with the standard libraries.
- * 
- * Math routines similar to those found in {@link java.lang.Math}. Performs
- * computations on {@code float} values directly without incurring the overhead
- * of conversions to and from {@code double}.
- *
- * <p>On one platform, {@code FloatMath.sqrt(100)} executes in one third of the
- * time required by {@code java.lang.Math.sqrt(100)}.</p>
- */
-public class FloatMath {
-
-    /** Prevents instantiation. */
-    private FloatMath() {}
-
-    /**
-     * Returns the float conversion of the most positive (i.e. closest to
-     * positive infinity) integer value which is less than the argument.
-     *
-     * @param value to be converted
-     * @return the floor of value
-     */
-    public static float floor(float value) {
-        return (float)Math.floor(value);
-    }
-
-    /**
-     * Returns the float conversion of the most negative (i.e. closest to
-     * negative infinity) integer value which is greater than the argument.
-     *
-     * @param value to be converted
-     * @return the ceiling of value
-     */
-    public static float ceil(float value) {
-        return (float)Math.ceil(value);
-    }
-
-    /**
-     * Returns the closest float approximation of the sine of the argument.
-     *
-     * @param angle to compute the cosine of, in radians
-     * @return the sine of angle
-     */
-    public static  float sin(float angle) {
-        return (float)Math.sin(angle);
-    }
-
-    /**
-     * Returns the closest float approximation of the cosine of the argument.
-     *
-     * @param angle to compute the cosine of, in radians
-     * @return the cosine of angle
-     */
-    public static float cos(float angle) {
-        return (float)Math.cos(angle);
-    }
-
-    /**
-     * Returns the closest float approximation of the square root of the
-     * argument.
-     *
-     * @param value to compute sqrt of
-     * @return the square root of value
-     */
-    public static float sqrt(float value) {
-        return (float)Math.sqrt(value);
-    }
-}
diff --git a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
new file mode 100644
index 0000000..ed24e16
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
@@ -0,0 +1,88 @@
+/*
+ * 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.util;
+
+import com.android.layoutlib.bridge.DelegateManager;
+
+/**
+ * Delegate implementing the native methods of android.util.FloatMath
+ *
+ * Through the layoutlib_create tool, the original native methods of FloatMath have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ *
+ */
+/*package*/ final class FloatMath_Delegate {
+
+    /** Prevents instantiation. */
+    private FloatMath_Delegate() {}
+
+    /**
+     * Returns the float conversion of the most positive (i.e. closest to
+     * positive infinity) integer value which is less than the argument.
+     *
+     * @param value to be converted
+     * @return the floor of value
+     */
+    /*package*/ static float floor(float value) {
+        return (float)Math.floor(value);
+    }
+
+    /**
+     * Returns the float conversion of the most negative (i.e. closest to
+     * negative infinity) integer value which is greater than the argument.
+     *
+     * @param value to be converted
+     * @return the ceiling of value
+     */
+    /*package*/ static float ceil(float value) {
+        return (float)Math.ceil(value);
+    }
+
+    /**
+     * Returns the closest float approximation of the sine of the argument.
+     *
+     * @param angle to compute the cosine of, in radians
+     * @return the sine of angle
+     */
+    /*package*/ static  float sin(float angle) {
+        return (float)Math.sin(angle);
+    }
+
+    /**
+     * Returns the closest float approximation of the cosine of the argument.
+     *
+     * @param angle to compute the cosine of, in radians
+     * @return the cosine of angle
+     */
+    /*package*/ static float cos(float angle) {
+        return (float)Math.cos(angle);
+    }
+
+    /**
+     * Returns the closest float approximation of the square root of the
+     * argument.
+     *
+     * @param value to compute sqrt of
+     * @return the square root of value
+     */
+    /*package*/ static float sqrt(float value) {
+        return (float)Math.sqrt(value);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 0910d79..4bc8855 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -116,10 +116,10 @@
     }
     
     @Override
-    public View createViewFromTag(String name, AttributeSet attrs) {
+    public View createViewFromTag(View parent, String name, AttributeSet attrs) {
         View view = null;
         try {
-            view = super.createViewFromTag(name, attrs);
+            view = super.createViewFromTag(parent, name, attrs);
         } catch (InflateException e) {
             // try to load the class from using the custom view loader
             try {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index f91f601..cdb4148 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -24,18 +24,22 @@
 import com.android.layoutlib.api.IResourceValue;
 import com.android.layoutlib.api.IStyleResourceValue;
 import com.android.layoutlib.api.IXmlPullParser;
+import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
 import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
 import com.android.layoutlib.bridge.LayoutResult.LayoutViewInfo;
 import com.android.ninepatch.NinePatch;
 import com.android.tools.layoutlib.create.MethodAdapter;
 import com.android.tools.layoutlib.create.OverrideMethod;
 
+import android.content.ClipData;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.Bitmap_Delegate;
 import android.graphics.Canvas;
+import android.graphics.Canvas_Delegate;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.graphics.Typeface;
+import android.graphics.Typeface_Delegate;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
@@ -46,9 +50,10 @@
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.BridgeInflater;
-import android.view.InputChannel;
+import android.view.DragEvent;
 import android.view.IWindow;
 import android.view.IWindowSession;
+import android.view.InputChannel;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -62,6 +67,7 @@
 import android.widget.TabHost;
 import android.widget.TabWidget;
 
+import java.awt.image.BufferedImage;
 import java.lang.ref.SoftReference;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -217,7 +223,7 @@
         // load the fonts.
         FontLoader fontLoader = FontLoader.create(fontOsLocation);
         if (fontLoader != null) {
-            Typeface.init(fontLoader);
+            Typeface_Delegate.init(fontLoader);
         } else {
             return false;
         }
@@ -448,13 +454,28 @@
             view.layout(0, screenOffset, screenWidth, screenHeight);
 
             // draw the views
-            Canvas canvas = new Canvas(screenWidth, screenHeight - screenOffset, logger);
+            // create the BufferedImage into which the layout will be rendered.
+            BufferedImage image = new BufferedImage(screenWidth, screenHeight - screenOffset,
+                    BufferedImage.TYPE_INT_ARGB);
+
+            // create an Android bitmap around the BufferedImage
+            Bitmap bitmap = Bitmap_Delegate.createBitmap(image, Density.getEnum(density));
+
+            // create a Canvas around the Android bitmap
+            Canvas canvas = new Canvas(bitmap);
+
+            // to set the logger, get the native delegate
+            Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
+            canvasDelegate.setLogger(logger);
+
 
             root.draw(canvas);
-            canvas.dispose();
+            canvasDelegate.dispose();
 
-            return new LayoutResult(visit(((ViewGroup)view).getChildAt(0), context),
-                    canvas.getImage());
+            return new LayoutResult(
+                    visit(((ViewGroup)view).getChildAt(0), context),
+                    image);
+
         } catch (PostInflateException e) {
             return new LayoutResult(ILayoutResult.ERROR, "Error during post inflation process:\n"
                     + e.getMessage());
@@ -1004,7 +1025,7 @@
             // pass for now.
             return 0;
         }
-        
+
         @SuppressWarnings("unused")
         public void finishDrawing(IWindow arg0) throws RemoteException {
             // pass for now.
@@ -1073,6 +1094,38 @@
         }
 
         @SuppressWarnings("unused")
+        public IBinder prepareDrag(IWindow window, boolean localOnly,
+                int thumbnailWidth, int thumbnailHeight, Surface outSurface)
+                throws RemoteException {
+            // pass for now
+            return null;
+        }
+
+        @SuppressWarnings("unused")
+        public boolean performDrag(IWindow window, IBinder dragToken,
+                float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+                ClipData data)
+                throws RemoteException {
+            // pass for now
+            return false;
+        }
+
+        @SuppressWarnings("unused")
+        public void reportDropResult(IWindow window, boolean consumed) throws RemoteException {
+            // pass for now
+        }
+
+        @SuppressWarnings("unused")
+        public void dragRecipientEntered(IWindow window) throws RemoteException {
+            // pass for now
+        }
+
+        @SuppressWarnings("unused")
+        public void dragRecipientExited(IWindow window) throws RemoteException {
+            // pass for now
+        }
+
+        @SuppressWarnings("unused")
         public void setWallpaperPosition(IBinder window, float x, float y,
             float xStep, float yStep) {
             // pass for now.
@@ -1170,6 +1223,11 @@
             // pass for now.
         }
 
+        @SuppressWarnings("unused")
+        public void dispatchDragEvent(DragEvent event) {
+            // pass for now.
+        }
+
         public IBinder asBinder() {
             // pass for now.
             return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentProvider.java
new file mode 100644
index 0000000..9d6dd27
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentProvider.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentValues;
+import android.content.IContentProvider;
+import android.content.OperationApplicationException;
+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.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+/**
+ * Mock implementation of {@link IContentProvider}.
+ *
+ * TODO: never return null when the method is not supposed to. Return fake data instead.
+ */
+public final class BridgeContentProvider implements IContentProvider {
+
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> arg0)
+            throws RemoteException, OperationApplicationException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public int bulkInsert(Uri arg0, ContentValues[] arg1) throws RemoteException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public IBulkCursor bulkQuery(Uri arg0, String[] arg1, String arg2, String[] arg3,
+            String arg4, IContentObserver arg5, CursorWindow arg6) throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public Bundle call(String arg0, String arg1, Bundle arg2) throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public int delete(Uri arg0, String arg1, String[] arg2) throws RemoteException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public String getType(Uri arg0) throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public Uri insert(Uri arg0, ContentValues arg1) throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public AssetFileDescriptor openAssetFile(Uri arg0, String arg1) throws RemoteException,
+            FileNotFoundException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public ParcelFileDescriptor openFile(Uri arg0, String arg1) throws RemoteException,
+            FileNotFoundException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4)
+            throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3)
+            throws RemoteException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public IBinder asBinder() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public String[] getStreamTypes(Uri arg0, String arg1) throws RemoteException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public AssetFileDescriptor openTypedAssetFile(Uri arg0, String arg1, Bundle arg2)
+            throws RemoteException, FileNotFoundException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
index 20ccc0b..e15cb69 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java
@@ -16,27 +16,12 @@
 
 package com.android.layoutlib.bridge;
 
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
 import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.IContentProvider;
-import android.content.OperationApplicationException;
-import android.content.res.AssetFileDescriptor;
 import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.CursorWindow;
-import android.database.IBulkCursor;
-import android.database.IContentObserver;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
 
 /**
  * A mock content resolver for the LayoutLib Bridge.
@@ -49,76 +34,6 @@
 
     private BridgeContentProvider mProvider = null;
 
-    public static final class BridgeContentProvider implements IContentProvider {
-
-        public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> arg0)
-                throws RemoteException, OperationApplicationException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public int bulkInsert(Uri arg0, ContentValues[] arg1) throws RemoteException {
-            // TODO Auto-generated method stub
-            return 0;
-        }
-
-        public IBulkCursor bulkQuery(Uri arg0, String[] arg1, String arg2, String[] arg3,
-                String arg4, IContentObserver arg5, CursorWindow arg6) throws RemoteException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public Bundle call(String arg0, String arg1, Bundle arg2) throws RemoteException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public int delete(Uri arg0, String arg1, String[] arg2) throws RemoteException {
-            // TODO Auto-generated method stub
-            return 0;
-        }
-
-        public String getType(Uri arg0) throws RemoteException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public Uri insert(Uri arg0, ContentValues arg1) throws RemoteException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public AssetFileDescriptor openAssetFile(Uri arg0, String arg1) throws RemoteException,
-                FileNotFoundException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public ParcelFileDescriptor openFile(Uri arg0, String arg1) throws RemoteException,
-                FileNotFoundException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4)
-                throws RemoteException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3)
-                throws RemoteException {
-            // TODO Auto-generated method stub
-            return 0;
-        }
-
-        public IBinder asBinder() {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-    }
-
     public BridgeContentResolver(Context context) {
         super(context);
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index ecd22e2..8592731 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -37,6 +37,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.Resources.Theme;
+import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
@@ -1067,6 +1068,13 @@
     }
 
     @Override
+    public SQLiteDatabase openOrCreateDatabase(String arg0, int arg1,
+            CursorFactory arg2, DatabaseErrorHandler arg3) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
     public Drawable peekWallpaper() {
         // TODO Auto-generated method stub
         return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/DelegateManager.java
new file mode 100644
index 0000000..3d9f960
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/DelegateManager.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import android.util.SparseArray;
+
+/**
+ * Manages native delegates.
+ *
+ * This is used in conjunction with layoublib_create: certain Android java classes are mere
+ * wrappers around a heavily native based implementation, and we need a way to run these classes
+ * in our Eclipse rendering framework without bringing all the native code from the Android
+ * platform.
+ *
+ * Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their
+ * native methods by "delegate calls".
+ *
+ * For example, a native method android.graphics.Matrix.init(...) will actually become
+ * a call to android.graphics.Matrix_Delegate.init(...).
+ *
+ * The Android java classes that use native code uses an int (Java side) to reference native
+ * objects. This int is generally directly the pointer to the C structure counterpart.
+ * Typically a creation method will return such an int, and then this int will be passed later
+ * to a Java method to identify the C object to manipulate.
+ *
+ * Since we cannot use the Java object reference as the int directly, DelegateManager manages the
+ * int -> Delegate class link.
+ *
+ * Native methods usually always have the int as parameters. The first thing the delegate method
+ * will do is call {@link #getDelegate(int)} to get the Java object matching the int.
+ *
+ * Typical native init methods are returning a new int back to the Java class, so
+ * {@link #addDelegate(Object)} does the same.
+ *
+ * @param <T> the delegate class to manage
+ */
+public final class DelegateManager<T> {
+
+    private final SparseArray<T> mDelegates = new SparseArray<T>();
+    private int mDelegateCounter = 0;
+
+    /**
+     * Returns the delegate from the given native int.
+     * @param native_object the native int.
+     * @return the delegate or null if not found.
+     */
+    public T getDelegate(int native_object) {
+        synchronized (mDelegates) {
+            return mDelegates.get(native_object);
+        }
+    }
+
+    /**
+     * Adds a delegate to the manager and returns the native int used to identify it.
+     * @param newDelegate the delegate to add
+     * @return a unique native int to identify the delegate
+     */
+    public int addDelegate(T newDelegate) {
+        synchronized (mDelegates) {
+            int native_object = ++mDelegateCounter;
+            mDelegates.put(native_object, newDelegate);
+            return native_object;
+        }
+    }
+
+    /**
+     * Removes the delegate matching the given native int.
+     * @param native_object the native int.
+     */
+    public void removeDelegate(int native_object) {
+        synchronized (mDelegates) {
+            mDelegates.remove(native_object);
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java
index 801503b..de89a81 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java
@@ -134,6 +134,15 @@
         return mFallBackFonts;
     }
 
+    /**
+     * Returns a {@link Font} object given a family name and a style value (constant in
+     * {@link Typeface}).
+     * @param family the family name
+     * @param style a 1-item array containing the requested style. Based on the font being read
+     *              the actual style may be different. The array contains the actual style after
+     *              the method returns.
+     * @return the font object or null if no match could be found.
+     */
     public synchronized Font getFont(String family, int[] style) {
         if (family == null) {
             return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java
index abbf2f0..2c92567 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java
@@ -19,6 +19,7 @@
 import com.android.ninepatch.NinePatch;
 
 import android.graphics.Canvas;
+import android.graphics.Canvas_Delegate;
 import android.graphics.ColorFilter;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -80,7 +81,8 @@
     @Override
     public void draw(Canvas canvas) {
         Rect r = getBounds();
-        m9Patch.draw(canvas.getGraphics2d(), r.left, r.top, r.width(), r.height());
+        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
+        m9Patch.draw(canvasDelegate.getGraphics2d(), r.left, r.top, r.width(), r.height());
 
         return;
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
index f624753..f13ecdc 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java
@@ -17,8 +17,8 @@
 package com.android.layoutlib.bridge;
 
 import com.android.layoutlib.api.IDensityBasedResourceValue;
-import com.android.layoutlib.api.IResourceValue;
 import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
+import com.android.layoutlib.api.IResourceValue;
 import com.android.ninepatch.NinePatch;
 
 import org.kxml2.io.KXmlParser;
@@ -26,6 +26,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.graphics.Bitmap;
+import android.graphics.Bitmap_Delegate;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -105,7 +106,8 @@
      * @param isFramework indicates whether the resource is a framework resources.
      * Framework resources are cached, and loaded only once.
      */
-    public static Drawable getDrawable(IResourceValue value, BridgeContext context, boolean isFramework) {
+    public static Drawable getDrawable(IResourceValue value, BridgeContext context,
+            boolean isFramework) {
         Drawable d = null;
 
         String stringValue = value.getValue();
@@ -168,14 +170,8 @@
                             isFramework ? null : context.getProjectKey());
 
                     if (bitmap == null) {
-                        bitmap = new Bitmap(bmpFile);
-                        try {
-                            bitmap.setDensity(Density.MEDIUM.getValue());
-                        } catch (NoClassDefFoundError error) {
-                            // look like we're running in an older version of ADT that doesn't
-                            // include the new layoutlib_api. Let's just ignore this, the drawing
-                            // will just be wrong.
-                        }
+                        // always create the cache copy in the original density.
+                        bitmap = Bitmap_Delegate.createBitmap(bmpFile, Density.MEDIUM);
                         Bridge.setCachedBitmap(stringValue, bitmap,
                                 isFramework ? null : context.getProjectKey());
                     }
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
index 6e14e82..ba3c51a 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/AndroidGraphicsTests.java
@@ -17,8 +17,6 @@
 package com.android.layoutlib.bridge;
 
 import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics._Original_Paint;
 import android.text.TextPaint;
 
 import junit.framework.TestCase;
@@ -58,14 +56,6 @@
         }
     }
 
-    public void testPaint() {
-        _Original_Paint o = new _Original_Paint();
-        assertNotNull(o);
-
-        Paint p = new Paint();
-        assertNotNull(p);
-    }
-
     public void textTextPaint() {
         TextPaint p = new TextPaint();
         assertNotNull(p);
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
index e0dc55f..adb693d 100644
--- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestClassReplacement.java
@@ -17,6 +17,8 @@
 package com.android.layoutlib.bridge;
 
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
 
 import junit.framework.TestCase;
 
@@ -26,7 +28,8 @@
         // TODO: we want to test all the classes. For now only Paint passes the tests.
 //        final String[] classes = CreateInfo.RENAMED_CLASSES;
         final String[] classes = new String[] {
-                "android.graphics.Paint",               "android.graphics._Original_Paint"
+                "android.graphics.Paint",               "android.graphics._Original_Paint",
+                "android.graphics.Canvas",               "android.graphics._Original_Canvas",
         };
         final int count = classes.length;
         for (int i = 0 ; i < count ; i += 2) {
@@ -52,12 +55,21 @@
         Method[] oldClassMethods = oldClass.getDeclaredMethods();
 
         for (Method oldMethod : oldClassMethods) {
-            // we ignore anything that starts with native
+            // we ignore anything that starts with native. This is because the class we are looking
+            // at has already been modified to remove the native modifiers.
             if (oldMethod.getName().startsWith("native")) {
                 continue;
             }
+
+            // or static and private
+            int privateStatic = Modifier.STATIC | Modifier.PRIVATE;
+            if ((oldMethod.getModifiers() & privateStatic) == privateStatic) {
+                continue;
+            }
+
             boolean found = false;
             for (Method newMethod : newClassMethods) {
+
                 if (compareMethods(newClass, newMethod, oldClass, oldMethod)) {
                     found = true;
                     break;
@@ -65,7 +77,31 @@
             }
 
             if (found == false) {
-                fail(String.format("Unable to find %1$s", oldMethod.toGenericString()));
+                // compute a full class name that's long but not too long.
+                StringBuilder sb = new StringBuilder(oldMethod.getName() + "(");
+                Type[] params = oldMethod.getGenericParameterTypes();
+                for (int j = 0; j < params.length; j++) {
+                    if (params[j] instanceof Class) {
+                        Class theClass = (Class)params[j];
+                        sb.append(theClass.getName());
+                        int dimensions = 0;
+                        while (theClass.isArray()) {
+                            dimensions++;
+                            theClass = theClass.getComponentType();
+                        }
+                        for (int i = 0; i < dimensions; i++) {
+                            sb.append("[]");
+                        }
+
+                    } else {
+                        sb.append(params[j].toString());
+                    }
+                if (j < (params.length - 1))
+                    sb.append(",");
+                }
+                sb.append(")");
+
+                fail(String.format("Missing %1$s.%2$s", newClass.getName(), sb.toString()));
             }
         }
 
diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java
new file mode 100644
index 0000000..a86b5c9
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/TestNativeDelegate.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.create.CreateInfo;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests that native delegate classes implement all the required methods.
+ *
+ * This looks at {@link CreateInfo#DELEGATE_CLASS_NATIVES} to get the list of classes that
+ * have their native methods reimplemented through a delegate.
+ *
+ * Since the reimplemented methods are not native anymore, we look for the annotation
+ * {@link LayoutlibDelegate}, and look for a matching method in the delegate (named the same
+ * as the modified class with _Delegate added as a suffix).
+ * If the original native method is not static, then we make sure the delegate method also
+ * include the original class as first parameter (to access "this").
+ *
+ */
+public class TestNativeDelegate extends TestCase {
+
+    public void  testNativeDelegates() {
+
+        final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
+        final int count = classes.length;
+        for (int i = 0 ; i < count ; i++) {
+            loadAndCompareClasses(classes[i], classes[i] + "_Delegate");
+        }
+    }
+
+    private void loadAndCompareClasses(String originalClassName, String delegateClassName) {
+        // load the classes
+        try {
+            ClassLoader classLoader = TestNativeDelegate.class.getClassLoader();
+            Class<?> originalClass = classLoader.loadClass(originalClassName);
+            Class<?> delegateClass = classLoader.loadClass(delegateClassName);
+
+            compare(originalClass, delegateClass);
+        } catch (ClassNotFoundException e) {
+           fail("Failed to load class: " + e.getMessage());
+        } catch (SecurityException e) {
+            fail("Failed to load class: " + e.getMessage());
+        }
+    }
+
+    private void compare(Class<?> originalClass, Class<?> delegateClass) throws SecurityException {
+        Method[] originalMethods = originalClass.getDeclaredMethods();
+
+        for (Method originalMethod : originalMethods) {
+            // look for methods that were native: they have the LayoutlibDelegate annotation
+            if (originalMethod.getAnnotation(LayoutlibDelegate.class) == null) {
+                continue;
+            }
+
+            // get the signature.
+            Class<?>[] parameters = originalMethod.getParameterTypes();
+
+            // if the method is not static, then the class is added as the first parameter
+            // (for "this")
+            if ((originalMethod.getModifiers() & Modifier.STATIC) == 0) {
+
+                Class<?>[] newParameters = new Class<?>[parameters.length + 1];
+                newParameters[0] = originalClass;
+                System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
+                parameters = newParameters;
+            }
+
+            try {
+                // try to load the method with the given parameter types.
+                Method delegateMethod = delegateClass.getDeclaredMethod(originalMethod.getName(),
+                        parameters);
+
+                // check that the method is static
+                assertTrue((delegateMethod.getModifiers() & Modifier.STATIC) == Modifier.STATIC);
+            } catch (NoSuchMethodException e) {
+                // compute a full class name that's long but not too long.
+                StringBuilder sb = new StringBuilder(originalMethod.getName() + "(");
+                for (int j = 0; j < parameters.length; j++) {
+                    Class<?> theClass = parameters[j];
+                    sb.append(theClass.getName());
+                    int dimensions = 0;
+                    while (theClass.isArray()) {
+                        dimensions++;
+                        theClass = theClass.getComponentType();
+                    }
+                    for (int i = 0; i < dimensions; i++) {
+                        sb.append("[]");
+                    }
+                    if (j < (parameters.length - 1)) {
+                        sb.append(",");
+                    }
+                }
+                sb.append(")");
+
+                fail(String.format("Missing %1$s.%2$s", delegateClass.getName(), sb.toString()));
+            }
+        }
+    }
+}
diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt
index c59e20d..65a64cd 100644
--- a/tools/layoutlib/create/README.txt
+++ b/tools/layoutlib/create/README.txt
@@ -195,5 +195,22 @@
 bridge will provide its own implementation.
 
 
+- References -
+--------------
+
+
+The JVM Specification 2nd edition:
+  http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html
+
+Understanding bytecode:
+  http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/
+
+Bytecode opcode list:
+  http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
+
+ASM user guide:
+  http://download.forge.objectweb.org/asm/asm-guide.pdf
+
+
 --
 end
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/LayoutlibDelegate.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/LayoutlibDelegate.java
new file mode 100644
index 0000000..9a48ea6
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/LayoutlibDelegate.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes a method that has been converted to a delegate by layoutlib_create.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface LayoutlibDelegate {
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index 7b55ed3e..a9ede26 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -28,9 +28,9 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.Map.Entry;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
 
@@ -60,38 +60,56 @@
      *  old-FQCN to rename and they get erased as they get renamed. At the end, classes still
      *  left here are not in the code base anymore and thus were not renamed. */
     private HashSet<String> mClassesNotRenamed;
-    /** A map { FQCN => map { list of return types to delete from the FQCN } }. */
+    /** A map { FQCN => set { list of return types to delete from the FQCN } }. */
     private HashMap<String, Set<String>> mDeleteReturns;
+    /** A map { FQCN => set { method names } } of methods to rewrite as delegates.
+     *  The special name {@link DelegateClassAdapter#ALL_NATIVES} can be used as in internal set. */
+    private final HashMap<String, Set<String>> mDelegateMethods;
 
     /**
      * Creates a new generator that can generate the output JAR with the stubbed classes.
-     * 
+     *
      * @param log Output logger.
      * @param osDestJar The path of the destination JAR to create.
-     * @param injectClasses The list of class from layoutlib_create to inject in layoutlib.
-     * @param stubMethods The list of methods to stub out. Each entry must be in the form
-     *          "package.package.OuterClass$InnerClass#MethodName".
-     * @param renameClasses The list of classes to rename, must be an even list: the binary FQCN
-     *          of class to replace followed by the new FQCN.
-     * @param deleteReturns List of classes for which the methods returning them should be deleted.
-     * The array contains a list of null terminated section starting with the name of the class
-     * to rename in which the methods are deleted, followed by a list of return types identifying
-     * the methods to delete.
+     * @param createInfo Creation parameters. Must not be null.
      */
-    public AsmGenerator(Log log, String osDestJar,
-            Class<?>[] injectClasses,
-            String[] stubMethods,
-            String[] renameClasses, String[] deleteReturns) {
+    public AsmGenerator(Log log, String osDestJar, ICreateInfo createInfo) {
         mLog = log;
         mOsDestJar = osDestJar;
-        mInjectClasses = injectClasses != null ? injectClasses : new Class<?>[0];
-        mStubMethods = stubMethods != null ? new HashSet<String>(Arrays.asList(stubMethods)) :
-                                             new HashSet<String>();
+        mInjectClasses = createInfo.getInjectedClasses();
+        mStubMethods = new HashSet<String>(Arrays.asList(createInfo.getOverriddenMethods()));
+
+        // Create the map/set of methods to change to delegates
+        mDelegateMethods = new HashMap<String, Set<String>>();
+        for (String signature : createInfo.getDelegateMethods()) {
+            int pos = signature.indexOf('#');
+            if (pos <= 0 || pos >= signature.length() - 1) {
+                continue;
+            }
+            String className = binaryToInternalClassName(signature.substring(0, pos));
+            String methodName = signature.substring(pos + 1);
+            Set<String> methods = mDelegateMethods.get(className);
+            if (methods == null) {
+                methods = new HashSet<String>();
+                mDelegateMethods.put(className, methods);
+            }
+            methods.add(methodName);
+        }
+        for (String className : createInfo.getDelegateClassNatives()) {
+            className = binaryToInternalClassName(className);
+            Set<String> methods = mDelegateMethods.get(className);
+            if (methods == null) {
+                methods = new HashSet<String>();
+                mDelegateMethods.put(className, methods);
+            }
+            methods.add(DelegateClassAdapter.ALL_NATIVES);
+        }
 
         // Create the map of classes to rename.
         mRenameClasses = new HashMap<String, String>();
         mClassesNotRenamed = new HashSet<String>();
-        int n = renameClasses == null ? 0 : renameClasses.length;
+        String[] renameClasses = createInfo.getRenamedClasses();
+        int n = renameClasses.length;
         for (int i = 0; i < n; i += 2) {
             assert i + 1 < n;
             // The ASM class names uses "/" separators, whereas regular FQCN use "."
@@ -100,38 +118,37 @@
             mRenameClasses.put(oldFqcn, newFqcn);
             mClassesNotRenamed.add(oldFqcn);
         }
-        
+
         // create the map of renamed class -> return type of method to delete.
         mDeleteReturns = new HashMap<String, Set<String>>();
-        if (deleteReturns != null) {
-            Set<String> returnTypes = null;
-            String renamedClass = null;
-            for (String className : deleteReturns) {
-                // if we reach the end of a section, add it to the main map
-                if (className == null) {
-                    if (returnTypes != null) {
-                        mDeleteReturns.put(renamedClass, returnTypes);
-                    }
-                    
-                    renamedClass = null;
-                    continue;
+        String[] deleteReturns = createInfo.getDeleteReturns();
+        Set<String> returnTypes = null;
+        String renamedClass = null;
+        for (String className : deleteReturns) {
+            // if we reach the end of a section, add it to the main map
+            if (className == null) {
+                if (returnTypes != null) {
+                    mDeleteReturns.put(renamedClass, returnTypes);
                 }
-    
-                // if the renamed class is null, this is the beginning of a section
-                if (renamedClass == null) {
-                    renamedClass = binaryToInternalClassName(className);
-                    continue;
-                }
-    
-                // just a standard return type, we add it to the list.
-                if (returnTypes == null) {
-                    returnTypes = new HashSet<String>();
-                }
-                returnTypes.add(binaryToInternalClassName(className));
+
+                renamedClass = null;
+                continue;
             }
+
+            // if the renamed class is null, this is the beginning of a section
+            if (renamedClass == null) {
+                renamedClass = binaryToInternalClassName(className);
+                continue;
+            }
+
+            // just a standard return type, we add it to the list.
+            if (returnTypes == null) {
+                returnTypes = new HashSet<String>();
+            }
+            returnTypes.add(binaryToInternalClassName(className));
         }
     }
-    
+
     /**
      * Returns the list of classes that have not been renamed yet.
      * <p/>
@@ -163,12 +180,12 @@
     public void setDeps(Map<String, ClassReader> deps) {
         mDeps = deps;
     }
-    
+
     /** Gets the map of classes to output as-is, except if they have native methods */
     public Map<String, ClassReader> getKeep() {
         return mKeep;
     }
-    
+
     /** Gets the map of dependencies that must be completely stubbed */
     public Map<String, ClassReader> getDeps() {
         return mDeps;
@@ -177,7 +194,7 @@
     /** Generates the final JAR */
     public void generate() throws FileNotFoundException, IOException {
         TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
-        
+
         for (Class<?> clazz : mInjectClasses) {
             String name = classToEntryPath(clazz);
             InputStream is = ClassLoader.getSystemResourceAsStream(name);
@@ -186,7 +203,7 @@
             name = classNameToEntryPath(transformName(cr.getClassName()));
             all.put(name, b);
         }
-        
+
         for (Entry<String, ClassReader> entry : mDeps.entrySet()) {
             ClassReader cr = entry.getValue();
             byte[] b = transform(cr, true /* stubNativesOnly */);
@@ -211,8 +228,8 @@
 
     /**
      * Writes the JAR file.
-     * 
-     * @param outStream The file output stream were to write the JAR. 
+     *
+     * @param outStream The file output stream were to write the JAR.
      * @param all The map of all classes to output.
      * @throws IOException if an I/O error has occurred
      */
@@ -236,7 +253,7 @@
     String classNameToEntryPath(String className) {
         return className.replaceAll("\\.", "/").concat(".class");
     }
-    
+
     /**
      * Utility method to get the JAR entry path from a Class name.
      * e.g. it returns someting like "com/foo/OuterClass$InnerClass1$InnerClass2.class"
@@ -248,30 +265,32 @@
             name = "$" + clazz.getSimpleName() + name;
             clazz = parent;
         }
-        return classNameToEntryPath(clazz.getCanonicalName() + name);        
+        return classNameToEntryPath(clazz.getCanonicalName() + name);
     }
 
     /**
      * Transforms a class.
      * <p/>
      * There are 3 kind of transformations:
-     * 
+     *
      * 1- For "mock" dependencies classes, we want to remove all code from methods and replace
      * by a stub. Native methods must be implemented with this stub too. Abstract methods are
      * left intact. Modified classes must be overridable (non-private, non-final).
      * Native methods must be made non-final, non-private.
-     * 
+     *
      * 2- For "keep" classes, we want to rewrite all native methods as indicated above.
      * If a class has native methods, it must also be made non-private, non-final.
-     * 
+     *
      * Note that unfortunately static methods cannot be changed to non-static (since static and
      * non-static are invoked differently.)
      */
     byte[] transform(ClassReader cr, boolean stubNativesOnly) {
 
         boolean hasNativeMethods = hasNativeMethods(cr);
+
+        // Get the class name, as an internal name (e.g. com/android/SomeClass$InnerClass)
         String className = cr.getClassName();
-        
+
         String newName = transformName(className);
         // transformName returns its input argument if there's no need to rename the class
         if (newName != className) {
@@ -288,16 +307,28 @@
         // Rewrite the new class from scratch, without reusing the constant pool from the
         // original class reader.
         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
-        
+
         ClassVisitor rv = cw;
         if (newName != className) {
             rv = new RenameClassAdapter(cw, className, newName);
         }
-        
-        TransformClassAdapter cv = new TransformClassAdapter(mLog, mStubMethods, 
+
+        ClassVisitor cv = new TransformClassAdapter(mLog, mStubMethods,
                 mDeleteReturns.get(className),
                 newName, rv,
                 stubNativesOnly, stubNativesOnly || hasNativeMethods);
+
+        Set<String> delegateMethods = mDelegateMethods.get(className);
+        if (delegateMethods != null && !delegateMethods.isEmpty()) {
+            // If delegateMethods only contains one entry ALL_NATIVES and the class is
+            // known to have no native methods, just skip this step.
+            if (hasNativeMethods ||
+                    !(delegateMethods.size() == 1 &&
+                            delegateMethods.contains(DelegateClassAdapter.ALL_NATIVES))) {
+                cv = new DelegateClassAdapter(mLog, cv, className, delegateMethods);
+            }
+        }
+
         cr.accept(cv, 0 /* flags */);
         return cw.toByteArray();
     }
@@ -323,7 +354,7 @@
                 return newName + className.substring(pos);
             }
         }
-        
+
         return className;
     }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 2ed8641..8e05c31 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -16,49 +16,128 @@
 
 package com.android.tools.layoutlib.create;
 
-public class CreateInfo {
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Describes the work to be done by {@link AsmGenerator}.
+ */
+public final class CreateInfo implements ICreateInfo {
+
+    /**
+     * Returns the list of class from layoutlib_create to inject in layoutlib.
+     * The list can be empty but must not be null.
+     */
+    public Class<?>[] getInjectedClasses() {
+        return INJECTED_CLASSES;
+    }
+
+    /**
+     * Returns the list of methods to rewrite as delegates.
+     * The list can be empty but must not be null.
+     */
+    public String[] getDelegateMethods() {
+        return DELEGATE_METHODS;
+    }
+
+    /**
+     * Returns the list of classes on which to delegate all native methods.
+     * The list can be empty but must not be null.
+     */
+    public String[] getDelegateClassNatives() {
+        return DELEGATE_CLASS_NATIVES;
+    }
+
+    /**
+     * Returns The list of methods to stub out. Each entry must be in the form
+     * "package.package.OuterClass$InnerClass#MethodName".
+     * The list can be empty but must not be null.
+     */
+    public String[] getOverriddenMethods() {
+        return OVERRIDDEN_METHODS;
+    }
+
+    /**
+     * Returns the list of classes to rename, must be an even list: the binary FQCN
+     * of class to replace followed by the new FQCN.
+     * The list can be empty but must not be null.
+     */
+    public String[] getRenamedClasses() {
+        return RENAMED_CLASSES;
+    }
+
+    /**
+     * Returns the list of classes for which the methods returning them should be deleted.
+     * The array contains a list of null terminated section starting with the name of the class
+     * to rename in which the methods are deleted, followed by a list of return types identifying
+     * the methods to delete.
+     * The list can be empty but must not be null.
+     */
+    public String[] getDeleteReturns() {
+        return DELETE_RETURNS;
+    }
+
+    //-----
+
     /**
      * The list of class from layoutlib_create to inject in layoutlib.
      */
-    public final static Class<?>[] INJECTED_CLASSES = new Class<?>[] {
+    private final static Class<?>[] INJECTED_CLASSES = new Class<?>[] {
             OverrideMethod.class,
             MethodListener.class,
             MethodAdapter.class,
-            CreateInfo.class
+            ICreateInfo.class,
+            CreateInfo.class,
+            LayoutlibDelegate.class
         };
 
     /**
+     * The list of methods to rewrite as delegates.
+     */
+    private final static String[] DELEGATE_METHODS = new String[] {
+        // TODO: comment out once DelegateClass is working
+        // "android.view.View#isInEditMode",
+        // "android.content.res.Resources$Theme#obtainStyledAttributes",
+    };
+
+    /**
+     * The list of classes on which to delegate all native methods.
+     */
+    private final static String[] DELEGATE_CLASS_NATIVES = new String[] {
+        "android.graphics.Bitmap",
+        "android.graphics.Canvas",
+        "android.graphics.DashPathEffect",
+        "android.graphics.LinearGradient",
+        "android.graphics.Matrix",
+        "android.graphics.Paint",
+        "android.graphics.PathEffect",
+        "android.graphics.PorterDuffXfermode",
+        "android.graphics.RadialGradient",
+        "android.graphics.Shader",
+        "android.graphics.SweepGradient",
+        "android.graphics.Typeface",
+        "android.graphics.Xfermode",
+        "android.util.FloatMath",
+    };
+
+    /**
      * The list of methods to stub out. Each entry must be in the form
      *  "package.package.OuterClass$InnerClass#MethodName".
      */
-    public final static String[] OVERRIDDEN_METHODS = new String[] {
-            "android.view.View#isInEditMode",
-            "android.content.res.Resources$Theme#obtainStyledAttributes",
-        };
+    private final static String[] OVERRIDDEN_METHODS = new String[] {
+        // TODO: remove once DelegateClass is working
+        "android.view.View#isInEditMode",
+        "android.content.res.Resources$Theme#obtainStyledAttributes",
+    };
 
     /**
      *  The list of classes to rename, must be an even list: the binary FQCN
      *  of class to replace followed by the new FQCN.
      */
-    public final static String[] RENAMED_CLASSES =
+    private final static String[] RENAMED_CLASSES =
         new String[] {
-            "android.graphics.Bitmap",              "android.graphics._Original_Bitmap",
             "android.graphics.BitmapFactory",       "android.graphics._Original_BitmapFactory",
-            "android.graphics.BitmapShader",        "android.graphics._Original_BitmapShader",
-            "android.graphics.Canvas",              "android.graphics._Original_Canvas",
-            "android.graphics.ComposeShader",       "android.graphics._Original_ComposeShader",
-            "android.graphics.DashPathEffect",       "android.graphics._Original_DashPathEffect",
-            "android.graphics.LinearGradient",      "android.graphics._Original_LinearGradient",
-            "android.graphics.Matrix",              "android.graphics._Original_Matrix",
-            "android.graphics.Paint",               "android.graphics._Original_Paint",
             "android.graphics.Path",                "android.graphics._Original_Path",
-            "android.graphics.PorterDuffXfermode",  "android.graphics._Original_PorterDuffXfermode",
-            "android.graphics.RadialGradient",      "android.graphics._Original_RadialGradient",
-            "android.graphics.Shader",              "android.graphics._Original_Shader",
-            "android.graphics.SweepGradient",       "android.graphics._Original_SweepGradient",
-            "android.graphics.Typeface",            "android.graphics._Original_Typeface",
             "android.os.ServiceManager",            "android.os._Original_ServiceManager",
-            "android.util.FloatMath",               "android.util._Original_FloatMath",
             "android.view.SurfaceView",             "android.view._Original_SurfaceView",
             "android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager",
         };
@@ -69,15 +148,8 @@
      * to rename in which the methods are deleted, followed by a list of return types identifying
      * the methods to delete.
      */
-    public final static String[] REMOVED_METHODS =
+    private final static String[] DELETE_RETURNS =
         new String[] {
-            "android.graphics.Paint",       // class to delete methods from
-            "android.graphics.Paint$Align", // list of type identifying methods to delete
-            "android.graphics.Paint$Style",
-            "android.graphics.Paint$Join",
-            "android.graphics.Paint$Cap",
-            "android.graphics.Paint$FontMetrics",
-            "android.graphics.Paint$FontMetricsInt",
             null };                         // separator, for next class/methods list.
 }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
new file mode 100644
index 0000000..9cba8a0
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Set;
+
+/**
+ * A {@link DelegateClassAdapter} can transform some methods from a class into
+ * delegates that defer the call to an associated delegate class.
+ * <p/>
+ * This is used to override specific methods and or all native methods in classes.
+ */
+public class DelegateClassAdapter extends ClassAdapter {
+
+    public final static String ALL_NATIVES = "<<all_natives>>";
+
+    private final String mClassName;
+    private final Set<String> mDelegateMethods;
+    private final Log mLog;
+
+    /**
+     * Creates a new {@link DelegateClassAdapter} that can transform some methods
+     * from a class into delegates that defer the call to an associated delegate class.
+     * <p/>
+     * This is used to override specific methods and or all native methods in classes.
+     *
+     * @param log The logger object. Must not be null.
+     * @param cv the class visitor to which this adapter must delegate calls.
+     * @param className The internal class name of the class to visit,
+     *          e.g. <code>com/android/SomeClass$InnerClass</code>.
+     * @param delegateMethods The set of method names to modify and/or the
+     *          special constant {@link #ALL_NATIVES} to convert all native methods.
+     */
+    public DelegateClassAdapter(Log log,
+            ClassVisitor cv,
+            String className,
+            Set<String> delegateMethods) {
+        super(cv);
+        mLog = log;
+        mClassName = className;
+        mDelegateMethods = delegateMethods;
+    }
+
+    //----------------------------------
+    // Methods from the ClassAdapter
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+
+        boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
+        boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
+
+        boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) ||
+                              mDelegateMethods.contains(name);
+
+        if (useDelegate) {
+            // remove native
+            access = access & ~Opcodes.ACC_NATIVE;
+        }
+
+        MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
+        if (useDelegate) {
+            DelegateMethodAdapter a = new DelegateMethodAdapter(mLog, mw, mClassName,
+                                                                name, desc, isStatic);
+            if (isNative) {
+                // A native has no code to visit, so we need to generate it directly.
+                a.generateCode();
+            } else {
+                return a;
+            }
+        }
+        return mw;
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
new file mode 100644
index 0000000..c7968a4
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
@@ -0,0 +1,319 @@
+/*
+ * 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.android.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * This method adapter rewrites a method by discarding the original code and generating
+ * a call to a delegate. Original annotations are passed along unchanged.
+ * <p/>
+ * Calls are delegated to a class named <code>&lt;className&gt;_Delegate</code> with
+ * static methods matching the methods to be overridden here. The methods have the
+ * same return type. The argument type list is the same except the "this" reference is
+ * passed first for non-static methods.
+ * <p/>
+ * A new annotation is added.
+ * <p/>
+ * Note that native methods have, by definition, no code so there's nothing a visitor
+ * can visit. That means the caller must call {@link #generateCode()} directly for
+ * a native and use the visitor pattern for non-natives.
+ * <p/>
+ * Instances of this class are not re-usable. You need a new instance for each method.
+ */
+class DelegateMethodAdapter implements MethodVisitor {
+
+    /**
+     * Suffix added to delegate classes.
+     */
+    public static final String DELEGATE_SUFFIX = "_Delegate";
+
+    private static String CONSTRUCTOR = "<init>";
+    private static String CLASS_INIT = "<clinit>";
+
+    /** The parent method writer */
+    private MethodVisitor mParentVisitor;
+    /** Flag to output the first line number. */
+    private boolean mOutputFirstLineNumber = true;
+    /** The original method descriptor (return type + argument types.) */
+    private String mDesc;
+    /** True if the original method is static. */
+    private final boolean mIsStatic;
+    /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
+    private final String mClassName;
+    /** The method name. */
+    private final String mMethodName;
+    /** Logger object. */
+    private final Log mLog;
+    /** True if {@link #visitCode()} has been invoked. */
+    private boolean mVisitCodeCalled;
+
+    /**
+     * Creates a new {@link DelegateMethodAdapter} that will transform this method
+     * into a delegate call.
+     * <p/>
+     * See {@link DelegateMethodAdapter} for more details.
+     *
+     * @param log The logger object. Must not be null.
+     * @param mv the method visitor to which this adapter must delegate calls.
+     * @param className The internal class name of the class to visit,
+     *          e.g. <code>com/android/SomeClass$InnerClass</code>.
+     * @param methodName The simple name of the method.
+     * @param desc A method descriptor (c.f. {@link Type#getReturnType(String)} +
+     *          {@link Type#getArgumentTypes(String)})
+     * @param isStatic True if the method is declared static.
+     */
+    public DelegateMethodAdapter(Log log,
+            MethodVisitor mv,
+            String className,
+            String methodName,
+            String desc,
+            boolean isStatic) {
+        mLog = log;
+        mParentVisitor = mv;
+        mClassName = className;
+        mMethodName = methodName;
+        mDesc = desc;
+        mIsStatic = isStatic;
+
+        if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) {
+            // We're going to simplify by not supporting constructors.
+            // The only trick with a constructor is to find the proper super constructor
+            // and call it (and deciding if we should mirror the original method call to
+            // a custom constructor or call a default one.)
+            throw new UnsupportedOperationException(
+                    String.format("Delegate doesn't support overriding constructor %1$s:%2$s(%3$s)",
+                            className, methodName, desc));
+        }
+    }
+
+    /**
+     * Generates the new code for the method.
+     * <p/>
+     * For native methods, this must be invoked directly by {@link DelegateClassAdapter}
+     * (since they have no code to visit).
+     * <p/>
+     * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to
+     * return this instance of {@link DelegateMethodAdapter} and let the normal visitor pattern
+     * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then
+     * this method will be invoked from {@link MethodVisitor#visitEnd()}.
+     */
+    public void generateCode() {
+        /*
+         * The goal is to generate a call to a static delegate method.
+         * If this method is not-static, the first parameter will be this.
+         * All the parameters must be passed and then the eventual return type returned.
+         *
+         * Example, let's say we have a method such as
+         *   public void method_1(int a, Object b, ArrayList<String> c) { ... }
+         *
+         * We'll want to create a body that calls a delegate method like this:
+         *   TheClass_Delegate.method_1(this, a, b, c);
+         *
+         * The generated class name is the current class name with "_Delegate" appended to it.
+         * One thing to realize is that we don't care about generics -- since generic types
+         * are erased at runtime, they have no influence on the method being called.
+         */
+
+        // Add our annotation
+        AnnotationVisitor aw = mParentVisitor.visitAnnotation(
+                Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(),
+                true); // visible at runtime
+        aw.visitEnd();
+
+        if (!mVisitCodeCalled) {
+            // If this is a direct call to generateCode() as done by DelegateClassAdapter
+            // for natives, visitCode() hasn't been called yet.
+            mParentVisitor.visitCode();
+            mVisitCodeCalled = true;
+        }
+
+        int numVars = 0;
+
+        // Push "this" for an instance method, which is always ALOAD 0
+        if (!mIsStatic) {
+            mParentVisitor.visitVarInsn(Opcodes.ALOAD, numVars++);
+        }
+
+        // Push all other arguments
+        Type[] argTypes = Type.getArgumentTypes(mDesc);
+        for (Type t : argTypes) {
+            int size = t.getSize();
+            mParentVisitor.visitVarInsn(t.getOpcode(Opcodes.ILOAD), numVars);
+            numVars += size;
+        }
+
+        // Construct the descriptor of the delegate. For a static method, it's the same
+        // however for an instance method we need to pass the 'this' reference first
+        String desc = mDesc;
+        if (!mIsStatic) {
+            Type[] argTypes2 = new Type[argTypes.length + 1];
+
+            argTypes2[0] = Type.getObjectType(mClassName);
+            System.arraycopy(argTypes, 0, argTypes2, 1, argTypes.length);
+
+            desc = Type.getMethodDescriptor(Type.getReturnType(mDesc), argTypes2);
+        }
+
+        String delegateClassName = mClassName + DELEGATE_SUFFIX;
+
+        // Invoke the static delegate
+        mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC,
+                delegateClassName,
+                mMethodName,
+                desc);
+
+        Type returnType = Type.getReturnType(mDesc);
+        mParentVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
+
+        mParentVisitor.visitMaxs(numVars, numVars);
+        mParentVisitor.visitEnd();
+
+        // For debugging now. Maybe we should collect these and store them in
+        // a text file for helping create the delegates. We could also compare
+        // the text file to a golden and break the build on unsupported changes
+        // or regressions. Even better we could fancy-print something that looks
+        // like the expected Java method declaration.
+        mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc);
+    }
+
+    /* Pass down to visitor writer. In this implementation, either do nothing. */
+    public void visitCode() {
+        mVisitCodeCalled = true;
+        mParentVisitor.visitCode();
+    }
+
+    /*
+     * visitMaxs is called just before visitEnd if there was any code to rewrite.
+     * Skip the original.
+     */
+    public void visitMaxs(int maxStack, int maxLocals) {
+    }
+
+    /**
+     * End of visiting. Generate the messaging code.
+     */
+    public void visitEnd() {
+        generateCode();
+    }
+
+    /* Writes all annotation from the original method. */
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        return mParentVisitor.visitAnnotation(desc, visible);
+    }
+
+    /* Writes all annotation default values from the original method. */
+    public AnnotationVisitor visitAnnotationDefault() {
+        return mParentVisitor.visitAnnotationDefault();
+    }
+
+    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+            boolean visible) {
+        return mParentVisitor.visitParameterAnnotation(parameter, desc, visible);
+    }
+
+    /* Writes all attributes from the original method. */
+    public void visitAttribute(Attribute attr) {
+        mParentVisitor.visitAttribute(attr);
+    }
+
+    /*
+     * Only writes the first line number present in the original code so that source
+     * viewers can direct to the correct method, even if the content doesn't match.
+     */
+    public void visitLineNumber(int line, Label start) {
+        if (mOutputFirstLineNumber) {
+            mParentVisitor.visitLineNumber(line, start);
+            mOutputFirstLineNumber = false;
+        }
+    }
+
+    public void visitInsn(int opcode) {
+        // Skip original code.
+    }
+
+    public void visitLabel(Label label) {
+        // Skip original code.
+    }
+
+    public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+        // Skip original code.
+    }
+
+    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+        // Skip original code.
+    }
+
+    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+        // Skip original code.
+    }
+
+    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+        // Skip original code.
+    }
+
+    public void visitIincInsn(int var, int increment) {
+        // Skip original code.
+    }
+
+    public void visitIntInsn(int opcode, int operand) {
+        // Skip original code.
+    }
+
+    public void visitJumpInsn(int opcode, Label label) {
+        // Skip original code.
+    }
+
+    public void visitLdcInsn(Object cst) {
+        // Skip original code.
+    }
+
+    public void visitLocalVariable(String name, String desc, String signature,
+            Label start, Label end, int index) {
+        // Skip original code.
+    }
+
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+        // Skip original code.
+    }
+
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+        // Skip original code.
+    }
+
+    public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+        // Skip original code.
+    }
+
+    public void visitTypeInsn(int opcode, String type) {
+        // Skip original code.
+    }
+
+    public void visitVarInsn(int opcode, int var) {
+        // Skip original code.
+    }
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
new file mode 100644
index 0000000..40c1706
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+/**
+ * Interface describing the work to be done by {@link AsmGenerator}.
+ */
+public interface ICreateInfo {
+
+    /**
+     * Returns the list of class from layoutlib_create to inject in layoutlib.
+     * The list can be empty but must not be null.
+     */
+    public abstract Class<?>[] getInjectedClasses();
+
+    /**
+     * Returns the list of methods to rewrite as delegates.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getDelegateMethods();
+
+    /**
+     * Returns the list of classes on which to delegate all native methods.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getDelegateClassNatives();
+
+    /**
+     * Returns The list of methods to stub out. Each entry must be in the form
+     * "package.package.OuterClass$InnerClass#MethodName".
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getOverriddenMethods();
+
+    /**
+     * Returns the list of classes to rename, must be an even list: the binary FQCN
+     * of class to replace followed by the new FQCN.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getRenamedClasses();
+
+    /**
+     * Returns the list of classes for which the methods returning them should be deleted.
+     * The array contains a list of null terminated section starting with the name of the class
+     * to rename in which the methods are deleted, followed by a list of return types identifying
+     * the methods to delete.
+     * The list can be empty but must not be null.
+     */
+    public abstract String[] getDeleteReturns();
+
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
index b30e9e5..43f2971 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -21,7 +21,28 @@
 import java.util.Set;
 
 
-
+/**
+ * Entry point for the layoutlib_create tool.
+ * <p/>
+ * The tool does not currently rely on any external configuration file.
+ * Instead the configuration is mostly done via the {@link CreateInfo} class.
+ * <p/>
+ * For a complete description of the tool and its implementation, please refer to
+ * the "README.txt" file at the root of this project.
+ * <p/>
+ * For a quick test, invoke this as follows:
+ * <pre>
+ * $ make layoutlib
+ * </pre>
+ * which does:
+ * <pre>
+ * $ make layoutlib_create &lt;bunch of framework jars&gt;
+ * $ out/host/linux-x86/framework/bin/layoutlib_create \
+ *        out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar \
+ *        out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar \
+ *        out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
+ * </pre>
+ */
 public class Main {
 
     public static void main(String[] args) {
@@ -42,12 +63,7 @@
         }
 
         try {
-            AsmGenerator agen = new AsmGenerator(log, osDestJar[0],
-                    CreateInfo.INJECTED_CLASSES,
-                    CreateInfo.OVERRIDDEN_METHODS,
-                    CreateInfo.RENAMED_CLASSES,
-                    CreateInfo.REMOVED_METHODS
-            );
+            AsmGenerator agen = new AsmGenerator(log, osDestJar[0], new CreateInfo());
 
             AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen,
                     new String[] { "android.view.View" },   // derived from
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
index e294d56..f2d9755 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
@@ -26,7 +26,7 @@
 import java.util.Set;
 
 /**
- * Class adapter that can stub some or all of the methods of the class. 
+ * Class adapter that can stub some or all of the methods of the class.
  */
 class TransformClassAdapter extends ClassAdapter {
 
@@ -41,12 +41,12 @@
 
     /**
      * Creates a new class adapter that will stub some or all methods.
-     * @param logger 
-     * @param stubMethods 
+     * @param logger
+     * @param stubMethods  list of method signatures to always stub out
      * @param deleteReturns list of types that trigger the deletion of methods returning them.
      * @param className The name of the class being modified
      * @param cv The parent class writer visitor
-     * @param stubNativesOnly True if only native methods should be stubbed. False if all 
+     * @param stubNativesOnly True if only native methods should be stubbed. False if all
      *                        methods should be stubbed.
      * @param hasNative True if the method has natives, in which case its access should be
      *                  changed.
@@ -67,10 +67,10 @@
     @Override
     public void visit(int version, int access, String name,
             String signature, String superName, String[] interfaces) {
-        
+
         // This class might be being renamed.
         name = mClassName;
-        
+
         // remove protected or private and set as public
         access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED);
         access |= Opcodes.ACC_PUBLIC;
@@ -82,7 +82,7 @@
         mIsInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
         super.visit(version, access, name, signature, superName, interfaces);
     }
-    
+
     /* Visits the header of an inner class. */
     @Override
     public void visitInnerClass(String name, String outerName, String innerName, int access) {
@@ -101,7 +101,7 @@
     @Override
     public MethodVisitor visitMethod(int access, String name, String desc,
             String signature, String[] exceptions) {
-        
+
         if (mDeleteReturns != null) {
             Type t = Type.getReturnType(desc);
             if (t.getSort() == Type.OBJECT) {
@@ -130,16 +130,16 @@
             (mStubAll ||
              (access & Opcodes.ACC_NATIVE) != 0) ||
              mStubMethods.contains(methodSignature)) {
-            
+
             boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
             boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
 
             // remove abstract, final and native
             access = access & ~(Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL | Opcodes.ACC_NATIVE);
-            
+
             String invokeSignature = methodSignature + desc;
             mLog.debug("  Stub: %s (%s)", invokeSignature, isNative ? "native" : "");
-            
+
             MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
             return new StubMethodAdapter(mw, name, returnType(desc), invokeSignature,
                     isStatic, isNative);
@@ -149,7 +149,7 @@
             return super.visitMethod(access, name, desc, signature, exceptions);
         }
     }
-    
+
     /* Visits a field. Makes it public. */
     @Override
     public FieldVisitor visitField(int access, String name, String desc, String signature,
@@ -157,7 +157,7 @@
         // change access to public
         access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
         access |= Opcodes.ACC_PUBLIC;
-        
+
         return super.visitField(access, name, desc, signature, value);
     }
 
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
index 603284e..d6dba6a 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertNotNull;
 
 import com.android.tools.layoutlib.create.AsmAnalyzer.DependencyVisitor;
-import com.android.tools.layoutlib.create.LogTest.MockLog;
 
 import org.junit.After;
 import org.junit.Before;
@@ -46,9 +45,9 @@
 
     @Before
     public void setUp() throws Exception {
-        mLog = new LogTest.MockLog();
+        mLog = new MockLog();
         URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
-        
+
         mOsJarPath = new ArrayList<String>();
         mOsJarPath.add(url.getFile());
 
@@ -69,9 +68,9 @@
                 "mock_android.dummy.InnerTest$DerivingClass",
                 "mock_android.dummy.InnerTest$MyGenerics1",
                 "mock_android.dummy.InnerTest$MyIntEnum",
-                "mock_android.dummy.InnerTest$MyStaticInnerClass",   
-                "mock_android.dummy.InnerTest$NotStaticInner1", 
-                "mock_android.dummy.InnerTest$NotStaticInner2",  
+                "mock_android.dummy.InnerTest$MyStaticInnerClass",
+                "mock_android.dummy.InnerTest$NotStaticInner1",
+                "mock_android.dummy.InnerTest$NotStaticInner2",
                 "mock_android.view.View",
                 "mock_android.view.ViewGroup",
                 "mock_android.view.ViewGroup$LayoutParams",
@@ -83,7 +82,7 @@
             },
             map.keySet().toArray());
     }
-    
+
     @Test
     public void testFindClass() throws IOException, LogAbortException {
         Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
@@ -91,7 +90,7 @@
 
         ClassReader cr = mAa.findClass("mock_android.view.ViewGroup$LayoutParams",
                 zipClasses, found);
-        
+
         assertNotNull(cr);
         assertEquals("mock_android/view/ViewGroup$LayoutParams", cr.getClassName());
         assertArrayEquals(new String[] { "mock_android.view.ViewGroup$LayoutParams" },
@@ -172,14 +171,14 @@
                 "mock_android.widget.TableLayout",
             },
             found.keySet().toArray());
-        
+
         for (String key : found.keySet()) {
             ClassReader value = found.get(key);
             assertNotNull("No value for " + key, value);
             assertEquals(key, AsmAnalyzer.classReaderToClassName(value));
         }
     }
-    
+
     @Test
     public void testDependencyVisitor() throws IOException, LogAbortException {
         Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
@@ -190,7 +189,7 @@
 
         ClassReader cr = mAa.findClass("mock_android.widget.TableLayout", zipClasses, keep);
         DependencyVisitor visitor = mAa.getVisitor(zipClasses, keep, new_keep, in_deps, out_deps);
-        
+
         // get first level dependencies
         cr.accept(visitor, 0 /* flags */);
 
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index 7cdf79a..f4ff389 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -20,8 +20,6 @@
 
 import static org.junit.Assert.assertArrayEquals;
 
-import com.android.tools.layoutlib.create.LogTest.MockLog;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -44,9 +42,9 @@
 
     @Before
     public void setUp() throws Exception {
-        mLog = new LogTest.MockLog();
+        mLog = new MockLog();
         URL url = this.getClass().getClassLoader().getResource("data/mock_android.jar");
-        
+
         mOsJarPath = new ArrayList<String>();
         mOsJarPath.add(url.getFile());
 
@@ -65,16 +63,41 @@
 
     @Test
     public void testClassRenaming() throws IOException, LogAbortException {
-        
-        AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar,
-            null, // classes to inject in the final JAR
-            null,  // methods to force override
-            new String[] {  // classes to rename (so that we can replace them)
-                "mock_android.view.View", "mock_android.view._Original_View",
-                "not.an.actual.ClassName", "anoter.fake.NewClassName",
-            },
-            null // methods deleted from their return type.
-            );
+
+        ICreateInfo ci = new ICreateInfo() {
+            public Class<?>[] getInjectedClasses() {
+                // classes to inject in the final JAR
+                return new Class<?>[0];
+            }
+
+            public String[] getDelegateMethods() {
+                return new String[0];
+            }
+
+            public String[] getDelegateClassNatives() {
+                return new String[0];
+            }
+
+            public String[] getOverriddenMethods() {
+                // methods to force override
+                return new String[0];
+            }
+
+            public String[] getRenamedClasses() {
+                // classes to rename (so that we can replace them)
+                return new String[] {
+                        "mock_android.view.View", "mock_android.view._Original_View",
+                        "not.an.actual.ClassName", "anoter.fake.NewClassName",
+                };
+            }
+
+            public String[] getDeleteReturns() {
+                 // methods deleted from their return type.
+                return new String[0];
+            }
+        };
+
+        AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
 
         AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
                 null,                 // derived from
@@ -83,7 +106,7 @@
                 });
         aa.analyze();
         agen.generate();
-        
+
         Set<String> notRenamed = agen.getClassesNotRenamed();
         assertArrayEquals(new String[] { "not/an/actual/ClassName" }, notRenamed.toArray());
     }
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
index d6916ae..0135c40 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java
@@ -33,8 +33,9 @@
     @Test
     public void testHasNative() throws IOException {
         MockClassHasNativeVisitor cv = new MockClassHasNativeVisitor();
-        ClassReader cr = new ClassReader(
-                "com.android.tools.layoutlib.create.ClassHasNativeVisitorTest$ClassWithNative");
+        String className =
+                this.getClass().getCanonicalName() + "$" + ClassWithNative.class.getSimpleName();
+        ClassReader cr = new ClassReader(className);
 
         cr.accept(cv, 0 /* flags */);
         assertArrayEquals(new String[] { "native_method" }, cv.getMethodsFound());
@@ -44,14 +45,17 @@
     @Test
     public void testHasNoNative() throws IOException {
         MockClassHasNativeVisitor cv = new MockClassHasNativeVisitor();
-        ClassReader cr = new ClassReader(
-                "com.android.tools.layoutlib.create.ClassHasNativeVisitorTest$ClassWithoutNative");
+        String className =
+            this.getClass().getCanonicalName() + "$" + ClassWithoutNative.class.getSimpleName();
+        ClassReader cr = new ClassReader(className);
 
         cr.accept(cv, 0 /* flags */);
         assertArrayEquals(new String[0], cv.getMethodsFound());
         assertFalse(cv.hasNativeMethods());
     }
 
+    //-------
+
     /**
      * Overrides {@link ClassHasNativeVisitor} to collec the name of the native methods found.
      */
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
new file mode 100644
index 0000000..7d80796
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashSet;
+
+public class DelegateClassAdapterTest {
+
+    private MockLog mLog;
+
+    private static final String CLASS_NAME =
+        DelegateClassAdapterTest.class.getCanonicalName() + "$" +
+        ClassWithNative.class.getSimpleName();
+
+    @Before
+    public void setUp() throws Exception {
+        mLog = new MockLog();
+        mLog.setVerbose(true); // capture debug error too
+    }
+
+    /**
+     * Tests that a class not being modified still works.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testNoOp() throws Exception {
+        // create an instance of the class that will be modified
+        // (load the class in a distinct class loader so that we can trash its definition later)
+        ClassLoader cl1 = new ClassLoader(this.getClass().getClassLoader()) { };
+        Class<ClassWithNative> clazz1 = (Class<ClassWithNative>) cl1.loadClass(CLASS_NAME);
+        ClassWithNative instance1 = clazz1.newInstance();
+        assertEquals(42, instance1.add(20, 22));
+        try {
+            instance1.callNativeInstance(10, 3.1415, new Object[0] );
+            fail("Test should have failed to invoke callTheNativeMethod [1]");
+        } catch (UnsatisfiedLinkError e) {
+            // This is expected to fail since the native method is not implemented.
+        }
+
+        // Now process it but tell the delegate to not modify any method
+        ClassWriter cw = new ClassWriter(0 /*flags*/);
+
+        HashSet<String> delegateMethods = new HashSet<String>();
+        String internalClassName = CLASS_NAME.replace('.', '/');
+        DelegateClassAdapter cv = new DelegateClassAdapter(
+                mLog, cw, internalClassName, delegateMethods);
+
+        ClassReader cr = new ClassReader(CLASS_NAME);
+        cr.accept(cv, 0 /* flags */);
+
+        // Load the generated class in a different class loader and try it again
+        final byte[] bytes = cw.toByteArray();
+
+        ClassLoader2 cl2 = new ClassLoader2(bytes) {
+            @Override
+            public void testModifiedInstance() throws Exception {
+                Class<?> clazz2 = loadClass(CLASS_NAME);
+                Object i2 = clazz2.newInstance();
+                assertNotNull(i2);
+                assertEquals(42, callAdd(i2, 20, 22));
+
+                try {
+                    callCallNativeInstance(i2, 10, 3.1415, new Object[0]);
+                    fail("Test should have failed to invoke callTheNativeMethod [2]");
+                } catch (InvocationTargetException e) {
+                    // This is expected to fail since the native method has NOT been
+                    // overridden here.
+                    assertEquals(UnsatisfiedLinkError.class, e.getCause().getClass());
+                }
+
+                // Check that the native method does NOT have the new annotation
+                Method[] m = clazz2.getDeclaredMethods();
+                assertEquals("native_instance", m[2].getName());
+                assertTrue(Modifier.isNative(m[2].getModifiers()));
+                Annotation[] a = m[2].getAnnotations();
+                assertEquals(0, a.length);
+            }
+        };
+        cl2.testModifiedInstance();
+    }
+
+    /**
+     * {@link DelegateMethodAdapter} does not support overriding constructors yet,
+     * so this should fail with an {@link UnsupportedOperationException}.
+     *
+     * Although not tested here, the message of the exception should contain the
+     * constructor signature.
+     */
+    @Test(expected=UnsupportedOperationException.class)
+    public void testConstructorsNotSupported() throws IOException {
+        ClassWriter cw = new ClassWriter(0 /*flags*/);
+
+        String internalClassName = CLASS_NAME.replace('.', '/');
+
+        HashSet<String> delegateMethods = new HashSet<String>();
+        delegateMethods.add("<init>");
+        DelegateClassAdapter cv = new DelegateClassAdapter(
+                mLog, cw, internalClassName, delegateMethods);
+
+        ClassReader cr = new ClassReader(CLASS_NAME);
+        cr.accept(cv, 0 /* flags */);
+    }
+
+    @Test
+    public void testDelegateNative() throws Exception {
+        ClassWriter cw = new ClassWriter(0 /*flags*/);
+        String internalClassName = CLASS_NAME.replace('.', '/');
+
+        HashSet<String> delegateMethods = new HashSet<String>();
+        delegateMethods.add(DelegateClassAdapter.ALL_NATIVES);
+        DelegateClassAdapter cv = new DelegateClassAdapter(
+                mLog, cw, internalClassName, delegateMethods);
+
+        ClassReader cr = new ClassReader(CLASS_NAME);
+        cr.accept(cv, 0 /* flags */);
+
+        // Load the generated class in a different class loader and try it
+        final byte[] bytes = cw.toByteArray();
+
+        try {
+            ClassLoader2 cl2 = new ClassLoader2(bytes) {
+                @Override
+                public void testModifiedInstance() throws Exception {
+                    Class<?> clazz2 = loadClass(CLASS_NAME);
+                    Object i2 = clazz2.newInstance();
+                    assertNotNull(i2);
+
+                    // Use reflection to access inner methods
+                    assertEquals(42, callAdd(i2, 20, 22));
+
+                     Object[] objResult = new Object[] { null };
+                     int result = callCallNativeInstance(i2, 10, 3.1415, objResult);
+                     assertEquals((int)(10 + 3.1415), result);
+                     assertSame(i2, objResult[0]);
+
+                     // Check that the native method now has the new annotation and is not native
+                     Method[] m = clazz2.getDeclaredMethods();
+                     assertEquals("native_instance", m[2].getName());
+                     assertFalse(Modifier.isNative(m[2].getModifiers()));
+                     Annotation[] a = m[2].getAnnotations();
+                     assertEquals("LayoutlibDelegate", a[0].annotationType().getSimpleName());
+                }
+            };
+            cl2.testModifiedInstance();
+
+        // This code block is useful for debugging. However to make it work you need to
+        // pull in the org.objectweb.asm.util.TraceClassVisitor class and associated
+        // utilities which are found in the ASM source jar.
+        //
+        // } catch (Throwable t) {
+        //     For debugging, dump the bytecode of the class in case of unexpected error.
+        //     StringWriter sw = new StringWriter();
+        //     PrintWriter pw = new PrintWriter(sw);
+        //     TraceClassVisitor tcv = new TraceClassVisitor(pw);
+        //     ClassReader cr2 = new ClassReader(bytes);
+        //     cr2.accept(tcv, 0 /* flags */);
+        //     String msg = "\n" + t.getClass().getCanonicalName();
+        //     if (t.getMessage() != null) {
+        //       msg += ": " + t.getMessage();
+        //     }
+        //     msg = msg + "\nBytecode dump:\n" + sw.toString();
+        //  // Re-throw exception with new message
+        //     RuntimeException ex = new RuntimeException(msg, t);
+        //     throw ex;
+        } finally {
+        }
+    }
+
+    //-------
+
+    /**
+     * A class loader than can define and instantiate our dummy {@link ClassWithNative}.
+     * <p/>
+     * The trick here is that this class loader will test our modified version of ClassWithNative.
+     * Trying to do so in the original class loader generates all sort of link issues because
+     * there are 2 different definitions of the same class name. This class loader will
+     * define and load the class when requested by name and provide helpers to access the
+     * instance methods via reflection.
+     */
+    private abstract class ClassLoader2 extends ClassLoader {
+        private final byte[] mClassWithNative;
+
+        public ClassLoader2(byte[] classWithNative) {
+            super(null);
+            mClassWithNative = classWithNative;
+        }
+
+        @SuppressWarnings("unused")
+        @Override
+        protected Class<?> findClass(String name) throws ClassNotFoundException {
+            try {
+                return super.findClass(name);
+            } catch (ClassNotFoundException e) {
+
+                if (CLASS_NAME.equals(name)) {
+                    // Load the modified ClassWithNative from its bytes representation.
+                    return defineClass(CLASS_NAME, mClassWithNative, 0, mClassWithNative.length);
+                }
+
+                try {
+                    // Load everything else from the original definition into the new class loader.
+                    ClassReader cr = new ClassReader(name);
+                    ClassWriter cw = new ClassWriter(0);
+                    cr.accept(cw, 0);
+                    byte[] bytes = cw.toByteArray();
+                    return defineClass(name, bytes, 0, bytes.length);
+
+                } catch (IOException ioe) {
+                    throw new RuntimeException(ioe);
+                }
+            }
+        }
+
+        /**
+         * Accesses {@link ClassWithNative#add(int, int)} via reflection.
+         */
+        public int callAdd(Object instance, int a, int b) throws Exception {
+            Method m = instance.getClass().getMethod("add",
+                    new Class<?>[] { int.class, int.class });
+
+            Object result = m.invoke(instance, new Object[] { a, b });
+            return ((Integer) result).intValue();
+        }
+
+        /**
+         * Accesses {@link ClassWithNative#callNativeInstance(int, double, Object[])}
+         * via reflection.
+         */
+        public int callCallNativeInstance(Object instance, int a, double d, Object[] o)
+                throws Exception {
+            Method m = instance.getClass().getMethod("callNativeInstance",
+                    new Class<?>[] { int.class, double.class, Object[].class });
+
+            Object result = m.invoke(instance, new Object[] { a, d, o });
+            return ((Integer) result).intValue();
+        }
+
+        public abstract void testModifiedInstance() throws Exception;
+    }
+
+    /**
+     * Dummy test class with a native method.
+     * The native method is not defined and any attempt to invoke it will
+     * throw an {@link UnsatisfiedLinkError}.
+     */
+    public static class ClassWithNative {
+        public ClassWithNative() {
+        }
+
+        public int add(int a, int b) {
+            return a + b;
+        }
+
+        public int callNativeInstance(int a, double d, Object[] o) {
+            return native_instance(a, d, o);
+        }
+
+        private native int native_instance(int a, double d, Object[] o);
+    }
+
+    /**
+     * The delegate that receives the call to {@link ClassWithNative}'s overridden methods.
+     */
+    public static class ClassWithNative_Delegate {
+        public static int native_instance(ClassWithNative instance, int a, double d, Object[] o) {
+            if (o != null && o.length > 0) {
+                o[0] = instance;
+            }
+            return (int)(a + d);
+        }
+    }
+}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java
index 3f13158..1a5f653 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/LogTest.java
@@ -24,33 +24,8 @@
 
 public class LogTest {
 
-    public static class MockLog extends Log {
-        StringBuilder mOut = new StringBuilder();
-        StringBuilder mErr = new StringBuilder();
-        
-        public String getOut() {
-            return mOut.toString();
-        }
-        
-        public String getErr() {
-            return mErr.toString();
-        }
-        
-        @Override
-        protected void outPrintln(String msg) {
-            mOut.append(msg);
-            mOut.append('\n');
-        }
-        
-        @Override
-        protected void errPrintln(String msg) {
-            mErr.append(msg);
-            mErr.append('\n');
-        }
-    }
-
     private MockLog mLog;
-    
+
     @Before
     public void setUp() throws Exception {
         mLog = new MockLog();
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/MockLog.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/MockLog.java
new file mode 100644
index 0000000..de750a3
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/MockLog.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+
+public class MockLog extends Log {
+    StringBuilder mOut = new StringBuilder();
+    StringBuilder mErr = new StringBuilder();
+
+    public String getOut() {
+        return mOut.toString();
+    }
+
+    public String getErr() {
+        return mErr.toString();
+    }
+
+    @Override
+    protected void outPrintln(String msg) {
+        mOut.append(msg);
+        mOut.append('\n');
+    }
+
+    @Override
+    protected void errPrintln(String msg) {
+        mErr.append(msg);
+        mErr.append('\n');
+    }
+}
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index b847ff6..f275e39 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -741,6 +741,7 @@
     }
 
     private void grabWifiHighPerfLock() {
+        /* not available in master yet
         if (mWifiHighPerfLock == null) {
             Log.v(TAG, "acquire wifi high perf lock");
             mWifiHighPerfLock = ((WifiManager)
@@ -748,6 +749,7 @@
                     .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG);
             mWifiHighPerfLock.acquire();
         }
+        */
     }
 
     private void releaseWifiHighPerfLock() {
diff --git a/voip/jni/rtp/EchoSuppressor.cpp b/voip/jni/rtp/EchoSuppressor.cpp
index d5cff6e8..4cff588 100644
--- a/voip/jni/rtp/EchoSuppressor.cpp
+++ b/voip/jni/rtp/EchoSuppressor.cpp
@@ -17,6 +17,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdint.h>
+#include <string.h>
 #include <math.h>
 
 #define LOG_TAG "Echo"
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 198b1e6..4bd5286 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -46,11 +46,11 @@
 
     List<ScanResult> getScanResults();
 
-    boolean disconnect();
+    void disconnect();
 
-    boolean reconnect();
+    void reconnect();
 
-    boolean reassociate();
+    void reassociate();
 
     WifiInfo getConnectionInfo();
 
@@ -58,11 +58,7 @@
 
     int getWifiEnabledState();
 
-    int getNumAllowedChannels();
-
-    boolean setNumAllowedChannels(int numChannels, boolean persist);
-
-    int[] getValidChannelCounts();
+    void setCountryCode(String country, boolean persist);
 
     boolean saveConfiguration();
 
@@ -89,5 +85,27 @@
     WifiConfiguration getWifiApConfiguration();
 
     void setWifiApConfiguration(in WifiConfiguration wifiConfig);
+
+    void startWifi();
+
+    void stopWifi();
+
+    void addToBlacklist(String bssid);
+
+    void clearBlacklist();
+
+    void connectNetworkWithConfig(in WifiConfiguration wifiConfig);
+
+    void connectNetworkWithId(int networkId);
+
+    void saveNetwork(in WifiConfiguration wifiConfig);
+
+    void forgetNetwork(int networkId);
+
+    void startWpsPbc(String bssid);
+
+    void startWpsWithPinFromAccessPoint(String bssid, int apPin);
+
+    int startWpsWithPinFromDevice(String bssid);
 }
 
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
new file mode 100644
index 0000000..04b3891
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -0,0 +1,1332 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.Intent;
+import android.net.DhcpInfo;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
+import android.net.ProxyProperties;
+import android.net.wifi.WifiConfiguration.IpAssignment;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiConfiguration.ProxySettings;
+import android.net.wifi.WifiConfiguration.Status;
+import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
+import android.os.Environment;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class provides the API to manage configured
+ * wifi networks. The API is not thread safe is being
+ * used only from WifiStateMachine.
+ *
+ * It deals with the following
+ * - Add/update/remove a WifiConfiguration
+ *   The configuration contains two types of information.
+ *     = IP and proxy configuration that is handled by WifiConfigStore and
+ *       is saved to disk on any change.
+ *
+ *       The format of configuration file is as follows:
+ *       <version>
+ *       <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
+ *       <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
+ *       ..
+ *
+ *       (key, value) pairs for a given network are grouped together and can
+ *       be in any order. A EOS at the end of a set of (key, value) pairs
+ *       indicates that the next set of (key, value) pairs are for a new
+ *       network. A network is identified by a unique ID_KEY. If there is no
+ *       ID_KEY in the (key, value) pairs, the data is discarded.
+ *
+ *       An invalid version on read would result in discarding the contents of
+ *       the file. On the next write, the latest version is written to file.
+ *
+ *       Any failures during read or write to the configuration file are ignored
+ *       without reporting to the user since the likelihood of these errors are
+ *       low and the impact on connectivity is low.
+ *
+ *     = SSID & security details that is pushed to the supplicant.
+ *       supplicant saves these details to the disk on calling
+ *       saveConfigCommand().
+ *
+ *       We have two kinds of APIs exposed:
+ *        > public API calls that provide fine grained control
+ *          - enableNetwork, disableNetwork, addOrUpdateNetwork(),
+ *          removeNetwork(). For these calls, the config is not persisted
+ *          to the disk. (TODO: deprecate these calls in WifiManager)
+ *        > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
+ *          These calls persist the supplicant config to disk.
+ *
+ * - Maintain a list of configured networks for quick access
+ *
+ */
+class WifiConfigStore {
+
+    private static Context sContext;
+    private static final String TAG = "WifiConfigStore";
+
+    /* configured networks with network id as the key */
+    private static HashMap<Integer, WifiConfiguration> sConfiguredNetworks =
+            new HashMap<Integer, WifiConfiguration>();
+
+    /* A network id is a unique identifier for a network configured in the
+     * supplicant. Network ids are generated when the supplicant reads
+     * the configuration file at start and can thus change for networks.
+     * We store the IP configuration for networks along with a unique id
+     * that is generated from SSID and security type of the network. A mapping
+     * from the generated unique id to network id of the network is needed to
+     * map supplicant config to IP configuration. */
+    private static HashMap<Integer, Integer> sNetworkIds =
+            new HashMap<Integer, Integer>();
+
+    /* Tracks the highest priority of configured networks */
+    private static int sLastPriority = -1;
+
+    private static final String ipConfigFile = Environment.getDataDirectory() +
+            "/misc/wifi/ipconfig.txt";
+
+    private static final int IPCONFIG_FILE_VERSION = 1;
+
+    /* IP and proxy configuration keys */
+    private static final String ID_KEY = "id";
+    private static final String IP_ASSIGNMENT_KEY = "ipAssignment";
+    private static final String LINK_ADDRESS_KEY = "linkAddress";
+    private static final String GATEWAY_KEY = "gateway";
+    private static final String DNS_KEY = "dns";
+    private static final String PROXY_SETTINGS_KEY = "proxySettings";
+    private static final String PROXY_HOST_KEY = "proxyHost";
+    private static final String PROXY_PORT_KEY = "proxyPort";
+    private static final String EXCLUSION_LIST_KEY = "exclusionList";
+    private static final String EOS = "eos";
+
+    /**
+     * Initialize context, fetch the list of configured networks
+     * and enable all stored networks in supplicant.
+     */
+    static void initialize(Context context) {
+        Log.d(TAG, "Loading config and enabling all networks");
+        sContext = context;
+        loadConfiguredNetworks();
+        enableAllNetworks();
+    }
+
+    /**
+     * Fetch the list of currently configured networks
+     * @return List of networks
+     */
+    static List<WifiConfiguration> getConfiguredNetworks() {
+        List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
+        synchronized (sConfiguredNetworks) {
+            for(WifiConfiguration config : sConfiguredNetworks.values()) {
+                networks.add(new WifiConfiguration(config));
+            }
+        }
+        return networks;
+    }
+
+    /**
+     * enable all networks and save config. This will be a no-op if the list
+     * of configured networks indicates all networks as being enabled
+     */
+    static void enableAllNetworks() {
+        synchronized (sConfiguredNetworks) {
+            for(WifiConfiguration config : sConfiguredNetworks.values()) {
+                if(config != null && config.status == Status.DISABLED) {
+                    if(WifiNative.enableNetworkCommand(config.networkId, false)) {
+                        config.status = Status.ENABLED;
+                    } else {
+                        Log.e(TAG, "Enable network failed on " + config.networkId);
+                    }
+                }
+            }
+        }
+
+        WifiNative.saveConfigCommand();
+        sendConfigChangeBroadcast();
+    }
+
+    /**
+     * Selects the specified network config for connection. This involves
+     * addition/update of the specified config, updating the priority of
+     * all the networks and enabling the given network while disabling others.
+     *
+     * Selecting a network will leave the other networks disabled and
+     * a call to enableAllNetworks() needs to be issued upon a connection
+     * or a failure event from supplicant
+     *
+     * @param config The configuration details in WifiConfiguration
+     */
+    static void selectNetwork(WifiConfiguration config) {
+        if (config != null) {
+            int netId = addOrUpdateNetworkNative(config);
+            if (netId != INVALID_NETWORK_ID) {
+                selectNetwork(netId);
+            } else {
+                Log.e(TAG, "Failed to update network " + config);
+            }
+        }
+    }
+
+    /**
+     * Selects the specified network for connection. This involves
+     * updating the priority of all the networks and enabling the given
+     * network while disabling others.
+     *
+     * Selecting a network will leave the other networks disabled and
+     * a call to enableAllNetworks() needs to be issued upon a connection
+     * or a failure event from supplicant
+     *
+     * @param netId network to select for connection
+     */
+    static void selectNetwork(int netId) {
+        // Reset the priority of each network at start or if it goes too high.
+        if (sLastPriority == -1 || sLastPriority > 1000000) {
+            synchronized (sConfiguredNetworks) {
+                for(WifiConfiguration config : sConfiguredNetworks.values()) {
+                    if (config.networkId != INVALID_NETWORK_ID) {
+                        config.priority = 0;
+                        addOrUpdateNetworkNative(config);
+                    }
+                }
+            }
+            sLastPriority = 0;
+        }
+
+        // Set to the highest priority and save the configuration.
+        WifiConfiguration config = new WifiConfiguration();
+        config.networkId = netId;
+        config.priority = ++sLastPriority;
+
+        addOrUpdateNetworkNative(config);
+        WifiNative.saveConfigCommand();
+
+        /* Enable the given network while disabling all other networks */
+        enableNetworkWithoutBroadcast(netId, true);
+
+       /* Avoid saving the config & sending a broadcast to prevent settings
+        * from displaying a disabled list of networks */
+    }
+
+    /**
+     * Add/update the specified configuration and save config
+     *
+     * @param config WifiConfiguration to be saved
+     */
+    static void saveNetwork(WifiConfiguration config) {
+        boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
+        int netId = addOrUpdateNetworkNative(config);
+        /* enable a new network */
+        if (newNetwork && netId != INVALID_NETWORK_ID) {
+            WifiNative.enableNetworkCommand(netId, false);
+            synchronized (sConfiguredNetworks) {
+                sConfiguredNetworks.get(netId).status = Status.ENABLED;
+            }
+        }
+        WifiNative.saveConfigCommand();
+        sendConfigChangeBroadcast();
+    }
+
+    /**
+     * Forget the specified network and save config
+     *
+     * @param netId network to forget
+     */
+    static void forgetNetwork(int netId) {
+        if (WifiNative.removeNetworkCommand(netId)) {
+            WifiNative.saveConfigCommand();
+            synchronized (sConfiguredNetworks) {
+                sConfiguredNetworks.remove(netId);
+            }
+            writeIpAndProxyConfigurations();
+            sendConfigChangeBroadcast();
+        } else {
+            Log.e(TAG, "Failed to remove network " + netId);
+        }
+    }
+
+    /**
+     * Add/update a network. Note that there is no saveConfig operation.
+     * This function is retained for compatibility with the public
+     * API. The more powerful saveNetwork() is used by the
+     * state machine
+     *
+     * @param config wifi configuration to add/update
+     */
+    static int addOrUpdateNetwork(WifiConfiguration config) {
+        int ret = addOrUpdateNetworkNative(config);
+        sendConfigChangeBroadcast();
+        return ret;
+    }
+
+    /**
+     * Remove a network. Note that there is no saveConfig operation.
+     * This function is retained for compatibility with the public
+     * API. The more powerful forgetNetwork() is used by the
+     * state machine for network removal
+     *
+     * @param netId network to be removed
+     */
+    static boolean removeNetwork(int netId) {
+        boolean ret = WifiNative.removeNetworkCommand(netId);
+        synchronized (sConfiguredNetworks) {
+            if (ret) sConfiguredNetworks.remove(netId);
+        }
+        sendConfigChangeBroadcast();
+        return ret;
+    }
+
+    /**
+     * Enable a network. Note that there is no saveConfig operation.
+     * This function is retained for compatibility with the public
+     * API. The more powerful selectNetwork()/saveNetwork() is used by the
+     * state machine for connecting to a network
+     *
+     * @param netId network to be removed
+     */
+    static boolean enableNetwork(int netId, boolean disableOthers) {
+        boolean ret = enableNetworkWithoutBroadcast(netId, disableOthers);
+        sendConfigChangeBroadcast();
+        return ret;
+    }
+
+    static boolean enableNetworkWithoutBroadcast(int netId, boolean disableOthers) {
+        boolean ret = WifiNative.enableNetworkCommand(netId, disableOthers);
+
+        synchronized (sConfiguredNetworks) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null) config.status = Status.ENABLED;
+        }
+
+        if (disableOthers) {
+            markAllNetworksDisabledExcept(netId);
+        }
+        return ret;
+    }
+
+    /**
+     * Disable a network. Note that there is no saveConfig operation.
+     * @param netId network to be disabled
+     */
+    static boolean disableNetwork(int netId) {
+        boolean ret = WifiNative.disableNetworkCommand(netId);
+        synchronized (sConfiguredNetworks) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null) config.status = Status.DISABLED;
+        }
+        sendConfigChangeBroadcast();
+        return ret;
+    }
+
+    /**
+     * Save the configured networks in supplicant to disk
+     */
+    static boolean saveConfig() {
+        return WifiNative.saveConfigCommand();
+    }
+
+    /**
+     * Start WPS pin method configuration with pin obtained
+     * from the access point
+     */
+    static boolean startWpsWithPinFromAccessPoint(String bssid, int apPin) {
+        if (WifiNative.startWpsWithPinFromAccessPointCommand(bssid, apPin)) {
+            /* WPS leaves all networks disabled */
+            markAllNetworksDisabled();
+            return true;
+        }
+        Log.e(TAG, "Failed to start WPS pin method configuration");
+        return false;
+    }
+
+    /**
+     * Start WPS pin method configuration with pin obtained
+     * from the device
+     */
+    static int startWpsWithPinFromDevice(String bssid) {
+        int pin = WifiNative.startWpsWithPinFromDeviceCommand(bssid);
+        /* WPS leaves all networks disabled */
+        if (pin != -1) {
+            markAllNetworksDisabled();
+        } else {
+            Log.e(TAG, "Failed to start WPS pin method configuration");
+        }
+        return pin;
+    }
+
+    /**
+     * Start WPS push button configuration
+     */
+    static boolean startWpsPbc(String bssid) {
+        if (WifiNative.startWpsPbcCommand(bssid)) {
+            /* WPS leaves all networks disabled */
+            markAllNetworksDisabled();
+            return true;
+        }
+        Log.e(TAG, "Failed to start WPS push button configuration");
+        return false;
+    }
+
+    /**
+     * Fetch the link properties for a given network id
+     */
+    static LinkProperties getLinkProperties(int netId) {
+        synchronized (sConfiguredNetworks) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null) return new LinkProperties(config.linkProperties);
+        }
+        return null;
+    }
+
+    /**
+     * get IP configuration for a given network id
+     * TODO: We cannot handle IPv6 addresses for configuration
+     *       right now until NetworkUtils is fixed. When we do
+     *       that, we should remove handling DhcpInfo and move
+     *       to using LinkProperties
+     */
+    static DhcpInfo getIpConfiguration(int netId) {
+        DhcpInfo dhcpInfo = new DhcpInfo();
+        LinkProperties linkProperties = getLinkProperties(netId);
+
+        if (linkProperties != null) {
+            Iterator<LinkAddress> iter = linkProperties.getLinkAddresses().iterator();
+            if (iter.hasNext()) {
+                try {
+                    LinkAddress linkAddress = iter.next();
+                    dhcpInfo.ipAddress = NetworkUtils.inetAddressToInt(
+                            linkAddress.getAddress());
+                    dhcpInfo.gateway = NetworkUtils.inetAddressToInt(
+                            linkProperties.getGateway());
+                    dhcpInfo.netmask = NetworkUtils.prefixLengthToNetmaskInt(
+                            linkAddress.getNetworkPrefixLength());
+                    Iterator<InetAddress> dnsIterator = linkProperties.getDnses().iterator();
+                    dhcpInfo.dns1 = NetworkUtils.inetAddressToInt(dnsIterator.next());
+                    if (dnsIterator.hasNext()) {
+                        dhcpInfo.dns2 = NetworkUtils.inetAddressToInt(dnsIterator.next());
+                    }
+                } catch (IllegalArgumentException e1) {
+                    Log.e(TAG, "IPv6 address cannot be handled " + e1);
+                } catch (NullPointerException e2) {
+                    /* Should not happen since a stored static config should be valid */
+                    Log.e(TAG, "Invalid partial IP configuration " + e2);
+                }
+            }
+        }
+        return dhcpInfo;
+    }
+
+    /**
+     * Fetch the proxy properties for a given network id
+     */
+    static ProxyProperties getProxyProperties(int netId) {
+        LinkProperties linkProperties = getLinkProperties(netId);
+        if (linkProperties != null) {
+            return new ProxyProperties(linkProperties.getHttpProxy());
+        }
+        return null;
+    }
+
+    /**
+     * Return if the specified network is using static IP
+     */
+    static boolean isUsingStaticIp(int netId) {
+        synchronized (sConfiguredNetworks) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null && config.ipAssignment == IpAssignment.STATIC) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static void sendConfigChangeBroadcast() {
+        if (!ActivityManagerNative.isSystemReady()) return;
+        Intent intent = new Intent(WifiManager.SUPPLICANT_CONFIG_CHANGED_ACTION);
+        sContext.sendBroadcast(intent);
+    }
+
+    static void loadConfiguredNetworks() {
+        String listStr = WifiNative.listNetworksCommand();
+        sLastPriority = 0;
+
+        synchronized (sConfiguredNetworks) {
+            sConfiguredNetworks.clear();
+            sNetworkIds.clear();
+
+            if (listStr == null)
+                return;
+
+            String[] lines = listStr.split("\n");
+            // Skip the first line, which is a header
+            for (int i = 1; i < lines.length; i++) {
+                String[] result = lines[i].split("\t");
+                // network-id | ssid | bssid | flags
+                WifiConfiguration config = new WifiConfiguration();
+                try {
+                    config.networkId = Integer.parseInt(result[0]);
+                } catch(NumberFormatException e) {
+                    continue;
+                }
+                if (result.length > 3) {
+                    if (result[3].indexOf("[CURRENT]") != -1)
+                        config.status = WifiConfiguration.Status.CURRENT;
+                    else if (result[3].indexOf("[DISABLED]") != -1)
+                        config.status = WifiConfiguration.Status.DISABLED;
+                    else
+                        config.status = WifiConfiguration.Status.ENABLED;
+                } else {
+                    config.status = WifiConfiguration.Status.ENABLED;
+                }
+                readNetworkVariables(config);
+                if (config.priority > sLastPriority) {
+                    sLastPriority = config.priority;
+                }
+                sConfiguredNetworks.put(config.networkId, config);
+                sNetworkIds.put(configKey(config), config.networkId);
+            }
+        }
+        readIpAndProxyConfigurations();
+        sendConfigChangeBroadcast();
+    }
+
+    /* Mark all networks except specified netId as disabled */
+    private static void markAllNetworksDisabledExcept(int netId) {
+        synchronized (sConfiguredNetworks) {
+            for(WifiConfiguration config : sConfiguredNetworks.values()) {
+                if(config != null && config.networkId != netId) {
+                    config.status = Status.DISABLED;
+                }
+            }
+        }
+    }
+
+    private static void markAllNetworksDisabled() {
+        markAllNetworksDisabledExcept(INVALID_NETWORK_ID);
+    }
+
+    private static void writeIpAndProxyConfigurations() {
+
+        DataOutputStream out = null;
+        try {
+            out = new DataOutputStream(new BufferedOutputStream(
+                    new FileOutputStream(ipConfigFile)));
+
+            out.writeInt(IPCONFIG_FILE_VERSION);
+
+            synchronized (sConfiguredNetworks) {
+                for(WifiConfiguration config : sConfiguredNetworks.values()) {
+                    boolean writeToFile = false;
+
+                    try {
+                        LinkProperties linkProperties = config.linkProperties;
+                        switch (config.ipAssignment) {
+                            case STATIC:
+                                out.writeUTF(IP_ASSIGNMENT_KEY);
+                                out.writeUTF(config.ipAssignment.toString());
+                                for (LinkAddress linkAddr : linkProperties.getLinkAddresses()) {
+                                    out.writeUTF(LINK_ADDRESS_KEY);
+                                    out.writeUTF(linkAddr.getAddress().getHostAddress());
+                                    out.writeInt(linkAddr.getNetworkPrefixLength());
+                                }
+                                InetAddress gateway = linkProperties.getGateway();
+                                if (gateway != null) {
+                                    out.writeUTF(GATEWAY_KEY);
+                                    out.writeUTF(gateway.getHostAddress());
+                                }
+                                for (InetAddress inetAddr : linkProperties.getDnses()) {
+                                    out.writeUTF(DNS_KEY);
+                                    out.writeUTF(inetAddr.getHostAddress());
+                                }
+                                writeToFile = true;
+                                break;
+                            case DHCP:
+                                out.writeUTF(IP_ASSIGNMENT_KEY);
+                                out.writeUTF(config.ipAssignment.toString());
+                                writeToFile = true;
+                                break;
+                            case UNASSIGNED:
+                                /* Ignore */
+                                break;
+                            default:
+                                Log.e(TAG, "Ignore invalid ip assignment while writing");
+                                break;
+                        }
+
+                        switch (config.proxySettings) {
+                            case STATIC:
+                                ProxyProperties proxyProperties = linkProperties.getHttpProxy();
+                                String exclusionList = proxyProperties.getExclusionList();
+                                out.writeUTF(PROXY_SETTINGS_KEY);
+                                out.writeUTF(config.proxySettings.toString());
+                                out.writeUTF(PROXY_HOST_KEY);
+                                out.writeUTF(proxyProperties.getSocketAddress().getHostName());
+                                out.writeUTF(PROXY_PORT_KEY);
+                                out.writeInt(proxyProperties.getSocketAddress().getPort());
+                                out.writeUTF(EXCLUSION_LIST_KEY);
+                                out.writeUTF(exclusionList);
+                                writeToFile = true;
+                                break;
+                            case NONE:
+                                out.writeUTF(PROXY_SETTINGS_KEY);
+                                out.writeUTF(config.proxySettings.toString());
+                                writeToFile = true;
+                                break;
+                            case UNASSIGNED:
+                                /* Ignore */
+                                break;
+                            default:
+                                Log.e(TAG, "Ignore invalid proxy settings while writing");
+                                break;
+                        }
+                        if (writeToFile) {
+                            out.writeUTF(ID_KEY);
+                            out.writeInt(configKey(config));
+                        }
+                    } catch (NullPointerException e) {
+                        Log.e(TAG, "Failure in writing " + config.linkProperties + e);
+                    }
+                    out.writeUTF(EOS);
+                }
+            }
+
+        } catch (IOException e) {
+            Log.e(TAG, "Error writing data file");
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (Exception e) {}
+            }
+        }
+    }
+
+    private static void readIpAndProxyConfigurations() {
+
+        DataInputStream in = null;
+        try {
+            in = new DataInputStream(new BufferedInputStream(new FileInputStream(
+                    ipConfigFile)));
+
+            if (in.readInt() != IPCONFIG_FILE_VERSION) {
+                Log.e(TAG, "Bad version on IP configuration file, ignore read");
+                return;
+            }
+
+            while (true) {
+                int id = -1;
+                IpAssignment ipAssignment = IpAssignment.UNASSIGNED;
+                ProxySettings proxySettings = ProxySettings.UNASSIGNED;
+                LinkProperties linkProperties = new LinkProperties();
+                String proxyHost = null;
+                int proxyPort = -1;
+                String exclusionList = null;
+                String key;
+
+                do {
+                    key = in.readUTF();
+                    try {
+                        if (key.equals(ID_KEY)) {
+                            id = in.readInt();
+                        } else if (key.equals(IP_ASSIGNMENT_KEY)) {
+                            ipAssignment = IpAssignment.valueOf(in.readUTF());
+                        } else if (key.equals(LINK_ADDRESS_KEY)) {
+                            LinkAddress linkAddr = new LinkAddress(InetAddress.getByName(
+                                    in.readUTF()), in.readInt());
+                            linkProperties.addLinkAddress(linkAddr);
+                        } else if (key.equals(GATEWAY_KEY)) {
+                            linkProperties.setGateway(InetAddress.getByName(in.readUTF()));
+                        } else if (key.equals(DNS_KEY)) {
+                            linkProperties.addDns(InetAddress.getByName(in.readUTF()));
+                        } else if (key.equals(PROXY_SETTINGS_KEY)) {
+                            proxySettings = ProxySettings.valueOf(in.readUTF());
+                        } else if (key.equals(PROXY_HOST_KEY)) {
+                            proxyHost = in.readUTF();
+                        } else if (key.equals(PROXY_PORT_KEY)) {
+                            proxyPort = in.readInt();
+                        } else if (key.equals(EXCLUSION_LIST_KEY)) {
+                            exclusionList = in.readUTF();
+                        } else if (key.equals(EOS)) {
+                            break;
+                        } else {
+                            Log.e(TAG, "Ignore unknown key " + key + "while reading");
+                        }
+                    } catch (UnknownHostException e) {
+                        Log.e(TAG, "Ignore invalid address while reading" + e);
+                    }
+                } while (true);
+
+                if (id != -1) {
+                    synchronized (sConfiguredNetworks) {
+                        WifiConfiguration config = sConfiguredNetworks.get(
+                                sNetworkIds.get(id));
+
+                        if (config == null) {
+                            Log.e(TAG, "configuration found for missing network, ignored");
+                        } else {
+                            config.linkProperties = linkProperties;
+                            switch (ipAssignment) {
+                                case STATIC:
+                                case DHCP:
+                                    config.ipAssignment = ipAssignment;
+                                    break;
+                                case UNASSIGNED:
+                                    //Ignore
+                                    break;
+                                default:
+                                    Log.e(TAG, "Ignore invalid ip assignment while reading");
+                                    break;
+                            }
+
+                            switch (proxySettings) {
+                                case STATIC:
+                                    config.proxySettings = proxySettings;
+                                    ProxyProperties proxyProperties = new ProxyProperties();
+                                    proxyProperties.setSocketAddress(
+                                            new InetSocketAddress(proxyHost, proxyPort));
+                                    proxyProperties.setExclusionList(exclusionList);
+                                    linkProperties.setHttpProxy(proxyProperties);
+                                    break;
+                                case NONE:
+                                    config.proxySettings = proxySettings;
+                                    break;
+                                case UNASSIGNED:
+                                    //Ignore
+                                    break;
+                                default:
+                                    Log.e(TAG, "Ignore invalid proxy settings while reading");
+                                    break;
+                            }
+                        }
+                    }
+                } else {
+                    Log.e(TAG, "Missing id while parsing configuration");
+                }
+            }
+        } catch (EOFException ignore) {
+        } catch (IOException e) {
+            Log.e(TAG, "Error parsing configuration" + e);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (Exception e) {}
+            }
+        }
+    }
+
+    private static int addOrUpdateNetworkNative(WifiConfiguration config) {
+        /*
+         * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
+         * network configuration. Otherwise, the networkId should
+         * refer to an existing configuration.
+         */
+        int netId = config.networkId;
+        boolean updateFailed = true;
+        boolean newNetwork = (netId == INVALID_NETWORK_ID);
+        // networkId of INVALID_NETWORK_ID means we want to create a new network
+
+        if (newNetwork) {
+            netId = WifiNative.addNetworkCommand();
+            if (netId < 0) {
+                Log.e(TAG, "Failed to add a network!");
+                return INVALID_NETWORK_ID;
+          }
+        }
+
+        setVariables: {
+
+            if (config.SSID != null &&
+                    !WifiNative.setNetworkVariableCommand(
+                        netId,
+                        WifiConfiguration.ssidVarName,
+                        config.SSID)) {
+                Log.d(TAG, "failed to set SSID: "+config.SSID);
+                break setVariables;
+            }
+
+            if (config.BSSID != null &&
+                    !WifiNative.setNetworkVariableCommand(
+                        netId,
+                        WifiConfiguration.bssidVarName,
+                        config.BSSID)) {
+                Log.d(TAG, "failed to set BSSID: "+config.BSSID);
+                break setVariables;
+            }
+
+            String allowedKeyManagementString =
+                makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
+            if (config.allowedKeyManagement.cardinality() != 0 &&
+                    !WifiNative.setNetworkVariableCommand(
+                        netId,
+                        WifiConfiguration.KeyMgmt.varName,
+                        allowedKeyManagementString)) {
+                Log.d(TAG, "failed to set key_mgmt: "+
+                        allowedKeyManagementString);
+                break setVariables;
+            }
+
+            String allowedProtocolsString =
+                makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
+            if (config.allowedProtocols.cardinality() != 0 &&
+                    !WifiNative.setNetworkVariableCommand(
+                        netId,
+                        WifiConfiguration.Protocol.varName,
+                        allowedProtocolsString)) {
+                Log.d(TAG, "failed to set proto: "+
+                        allowedProtocolsString);
+                break setVariables;
+            }
+
+            String allowedAuthAlgorithmsString =
+                makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
+            if (config.allowedAuthAlgorithms.cardinality() != 0 &&
+                    !WifiNative.setNetworkVariableCommand(
+                        netId,
+                        WifiConfiguration.AuthAlgorithm.varName,
+                        allowedAuthAlgorithmsString)) {
+                Log.d(TAG, "failed to set auth_alg: "+
+                        allowedAuthAlgorithmsString);
+                break setVariables;
+            }
+
+            String allowedPairwiseCiphersString =
+                    makeString(config.allowedPairwiseCiphers,
+                    WifiConfiguration.PairwiseCipher.strings);
+            if (config.allowedPairwiseCiphers.cardinality() != 0 &&
+                    !WifiNative.setNetworkVariableCommand(
+                        netId,
+                        WifiConfiguration.PairwiseCipher.varName,
+                        allowedPairwiseCiphersString)) {
+                Log.d(TAG, "failed to set pairwise: "+
+                        allowedPairwiseCiphersString);
+                break setVariables;
+            }
+
+            String allowedGroupCiphersString =
+                makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
+            if (config.allowedGroupCiphers.cardinality() != 0 &&
+                    !WifiNative.setNetworkVariableCommand(
+                        netId,
+                        WifiConfiguration.GroupCipher.varName,
+                        allowedGroupCiphersString)) {
+                Log.d(TAG, "failed to set group: "+
+                        allowedGroupCiphersString);
+                break setVariables;
+            }
+
+            // Prevent client screw-up by passing in a WifiConfiguration we gave it
+            // by preventing "*" as a key.
+            if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
+                    !WifiNative.setNetworkVariableCommand(
+                        netId,
+                        WifiConfiguration.pskVarName,
+                        config.preSharedKey)) {
+                Log.d(TAG, "failed to set psk: "+config.preSharedKey);
+                break setVariables;
+            }
+
+            boolean hasSetKey = false;
+            if (config.wepKeys != null) {
+                for (int i = 0; i < config.wepKeys.length; i++) {
+                    // Prevent client screw-up by passing in a WifiConfiguration we gave it
+                    // by preventing "*" as a key.
+                    if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
+                        if (!WifiNative.setNetworkVariableCommand(
+                                    netId,
+                                    WifiConfiguration.wepKeyVarNames[i],
+                                    config.wepKeys[i])) {
+                            Log.d(TAG,
+                                    "failed to set wep_key"+i+": " +
+                                    config.wepKeys[i]);
+                            break setVariables;
+                        }
+                        hasSetKey = true;
+                    }
+                }
+            }
+
+            if (hasSetKey) {
+                if (!WifiNative.setNetworkVariableCommand(
+                            netId,
+                            WifiConfiguration.wepTxKeyIdxVarName,
+                            Integer.toString(config.wepTxKeyIndex))) {
+                    Log.d(TAG,
+                            "failed to set wep_tx_keyidx: "+
+                            config.wepTxKeyIndex);
+                    break setVariables;
+                }
+            }
+
+            if (!WifiNative.setNetworkVariableCommand(
+                        netId,
+                        WifiConfiguration.priorityVarName,
+                        Integer.toString(config.priority))) {
+                Log.d(TAG, config.SSID + ": failed to set priority: "
+                        +config.priority);
+                break setVariables;
+            }
+
+            if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
+                        netId,
+                        WifiConfiguration.hiddenSSIDVarName,
+                        Integer.toString(config.hiddenSSID ? 1 : 0))) {
+                Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
+                        config.hiddenSSID);
+                break setVariables;
+            }
+
+            for (WifiConfiguration.EnterpriseField field
+                    : config.enterpriseFields) {
+                String varName = field.varName();
+                String value = field.value();
+                if (value != null) {
+                    if (field != config.eap) {
+                        value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
+                    }
+                    if (!WifiNative.setNetworkVariableCommand(
+                                netId,
+                                varName,
+                                value)) {
+                        Log.d(TAG, config.SSID + ": failed to set " + varName +
+                                ": " + value);
+                        break setVariables;
+                    }
+                }
+            }
+            updateFailed = false;
+        }
+
+        if (updateFailed) {
+            if (newNetwork) {
+                WifiNative.removeNetworkCommand(netId);
+                Log.d(TAG,
+                        "Failed to set a network variable, removed network: "
+                        + netId);
+            }
+            return INVALID_NETWORK_ID;
+        }
+
+        /* An update of the network variables requires reading them
+         * back from the supplicant to update sConfiguredNetworks.
+         * This is because some of the variables (SSID, wep keys &
+         * passphrases) reflect different values when read back than
+         * when written. For example, wep key is stored as * irrespective
+         * of the value sent to the supplicant
+         */
+        WifiConfiguration sConfig;
+        synchronized (sConfiguredNetworks) {
+            sConfig = sConfiguredNetworks.get(netId);
+        }
+        if (sConfig == null) {
+            sConfig = new WifiConfiguration();
+            sConfig.networkId = netId;
+            synchronized (sConfiguredNetworks) {
+                sConfiguredNetworks.put(netId, sConfig);
+            }
+        }
+        readNetworkVariables(sConfig);
+        writeIpAndProxyConfigurationsOnChange(sConfig, config);
+        return netId;
+    }
+
+    /* Compare current and new configuration and write to file on change */
+    private static void writeIpAndProxyConfigurationsOnChange(WifiConfiguration currentConfig,
+            WifiConfiguration newConfig) {
+        boolean ipChanged = false;
+        boolean proxyChanged = false;
+        LinkProperties linkProperties = new LinkProperties();
+
+        switch (newConfig.ipAssignment) {
+            case STATIC:
+                Collection<LinkAddress> currentLinkAddresses = currentConfig.linkProperties
+                        .getLinkAddresses();
+                Collection<LinkAddress> newLinkAddresses = newConfig.linkProperties
+                        .getLinkAddresses();
+                Collection<InetAddress> currentDnses = currentConfig.linkProperties.getDnses();
+                Collection<InetAddress> newDnses = newConfig.linkProperties.getDnses();
+                InetAddress currentGateway = currentConfig.linkProperties.getGateway();
+                InetAddress newGateway = newConfig.linkProperties.getGateway();
+
+                boolean linkAddressesDiffer = !currentLinkAddresses.containsAll(newLinkAddresses) ||
+                        (currentLinkAddresses.size() != newLinkAddresses.size());
+                boolean dnsesDiffer = !currentDnses.containsAll(newDnses) ||
+                        (currentDnses.size() != newDnses.size());
+                boolean gatewaysDiffer = (currentGateway == null) ||
+                        !currentGateway.equals(newGateway);
+
+                if ((currentConfig.ipAssignment != newConfig.ipAssignment) ||
+                        linkAddressesDiffer ||
+                        dnsesDiffer ||
+                        gatewaysDiffer) {
+                    ipChanged = true;
+                }
+                break;
+            case DHCP:
+                if (currentConfig.ipAssignment != newConfig.ipAssignment) {
+                    ipChanged = true;
+                }
+                break;
+            case UNASSIGNED:
+                /* Ignore */
+                break;
+            default:
+                Log.e(TAG, "Ignore invalid ip assignment during write");
+                break;
+        }
+
+        switch (newConfig.proxySettings) {
+            case STATIC:
+                InetSocketAddress newSockAddr = null;
+                String newExclusionList = null;
+                InetSocketAddress currentSockAddr = null;
+                String currentExclusionList = null;
+
+                ProxyProperties newHttpProxy = newConfig.linkProperties.getHttpProxy();
+                if (newHttpProxy != null) {
+                    newSockAddr = newHttpProxy.getSocketAddress();
+                    newExclusionList = newHttpProxy.getExclusionList();
+                }
+
+                ProxyProperties currentHttpProxy = currentConfig.linkProperties.getHttpProxy();
+                if (currentHttpProxy != null) {
+                    currentSockAddr =  currentHttpProxy.getSocketAddress();
+                    currentExclusionList = currentHttpProxy.getExclusionList();
+                }
+
+                boolean socketAddressDiffers = false;
+                boolean exclusionListDiffers = false;
+
+                if (newSockAddr != null && currentSockAddr != null ) {
+                    socketAddressDiffers = !currentSockAddr.equals(newSockAddr);
+                } else if (newSockAddr != null || currentSockAddr != null) {
+                    socketAddressDiffers = true;
+                }
+
+                if (newExclusionList != null && currentExclusionList != null) {
+                    exclusionListDiffers = !currentExclusionList.equals(newExclusionList);
+                } else if (newExclusionList != null || currentExclusionList != null) {
+                    exclusionListDiffers = true;
+                }
+
+                if ((currentConfig.proxySettings != newConfig.proxySettings) ||
+                        socketAddressDiffers ||
+                        exclusionListDiffers) {
+                    proxyChanged = true;
+                }
+                break;
+            case NONE:
+                if (currentConfig.proxySettings != newConfig.proxySettings) {
+                    proxyChanged = true;
+                }
+                break;
+            case UNASSIGNED:
+                /* Ignore */
+                break;
+            default:
+                Log.e(TAG, "Ignore invalid proxy configuration during write");
+                break;
+        }
+
+        if (!ipChanged) {
+            addIpSettingsFromConfig(linkProperties, currentConfig);
+        } else {
+            currentConfig.ipAssignment = newConfig.ipAssignment;
+            addIpSettingsFromConfig(linkProperties, newConfig);
+            Log.d(TAG, "IP config changed SSID = " + currentConfig.SSID + " linkProperties: " +
+                    linkProperties.toString());
+        }
+
+
+        if (!proxyChanged) {
+            linkProperties.setHttpProxy(currentConfig.linkProperties.getHttpProxy());
+        } else {
+            currentConfig.proxySettings = newConfig.proxySettings;
+            linkProperties.setHttpProxy(newConfig.linkProperties.getHttpProxy());
+            Log.d(TAG, "proxy changed SSID = " + currentConfig.SSID);
+            if (linkProperties.getHttpProxy() != null) {
+                Log.d(TAG, " proxyProperties: " + linkProperties.getHttpProxy().toString());
+            }
+        }
+
+        if (ipChanged || proxyChanged) {
+            currentConfig.linkProperties = linkProperties;
+            writeIpAndProxyConfigurations();
+            sendConfigChangeBroadcast();
+        }
+    }
+
+    private static void addIpSettingsFromConfig(LinkProperties linkProperties,
+            WifiConfiguration config) {
+        for (LinkAddress linkAddr : config.linkProperties.getLinkAddresses()) {
+            linkProperties.addLinkAddress(linkAddr);
+        }
+        linkProperties.setGateway(config.linkProperties.getGateway());
+        for (InetAddress dns : config.linkProperties.getDnses()) {
+            linkProperties.addDns(dns);
+        }
+    }
+
+    /**
+     * Read the variables from the supplicant daemon that are needed to
+     * fill in the WifiConfiguration object.
+     *
+     * @param config the {@link WifiConfiguration} object to be filled in.
+     */
+    private static void readNetworkVariables(WifiConfiguration config) {
+
+        int netId = config.networkId;
+        if (netId < 0)
+            return;
+
+        /*
+         * TODO: maybe should have a native method that takes an array of
+         * variable names and returns an array of values. But we'd still
+         * be doing a round trip to the supplicant daemon for each variable.
+         */
+        String value;
+
+        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
+        if (!TextUtils.isEmpty(value)) {
+            config.SSID = removeDoubleQuotes(value);
+        } else {
+            config.SSID = null;
+        }
+
+        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
+        if (!TextUtils.isEmpty(value)) {
+            config.BSSID = value;
+        } else {
+            config.BSSID = null;
+        }
+
+        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
+        config.priority = -1;
+        if (!TextUtils.isEmpty(value)) {
+            try {
+                config.priority = Integer.parseInt(value);
+            } catch (NumberFormatException ignore) {
+            }
+        }
+
+        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
+        config.hiddenSSID = false;
+        if (!TextUtils.isEmpty(value)) {
+            try {
+                config.hiddenSSID = Integer.parseInt(value) != 0;
+            } catch (NumberFormatException ignore) {
+            }
+        }
+
+        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
+        config.wepTxKeyIndex = -1;
+        if (!TextUtils.isEmpty(value)) {
+            try {
+                config.wepTxKeyIndex = Integer.parseInt(value);
+            } catch (NumberFormatException ignore) {
+            }
+        }
+
+        for (int i = 0; i < 4; i++) {
+            value = WifiNative.getNetworkVariableCommand(netId,
+                    WifiConfiguration.wepKeyVarNames[i]);
+            if (!TextUtils.isEmpty(value)) {
+                config.wepKeys[i] = value;
+            } else {
+                config.wepKeys[i] = null;
+            }
+        }
+
+        value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
+        if (!TextUtils.isEmpty(value)) {
+            config.preSharedKey = value;
+        } else {
+            config.preSharedKey = null;
+        }
+
+        value = WifiNative.getNetworkVariableCommand(config.networkId,
+                WifiConfiguration.Protocol.varName);
+        if (!TextUtils.isEmpty(value)) {
+            String vals[] = value.split(" ");
+            for (String val : vals) {
+                int index =
+                    lookupString(val, WifiConfiguration.Protocol.strings);
+                if (0 <= index) {
+                    config.allowedProtocols.set(index);
+                }
+            }
+        }
+
+        value = WifiNative.getNetworkVariableCommand(config.networkId,
+                WifiConfiguration.KeyMgmt.varName);
+        if (!TextUtils.isEmpty(value)) {
+            String vals[] = value.split(" ");
+            for (String val : vals) {
+                int index =
+                    lookupString(val, WifiConfiguration.KeyMgmt.strings);
+                if (0 <= index) {
+                    config.allowedKeyManagement.set(index);
+                }
+            }
+        }
+
+        value = WifiNative.getNetworkVariableCommand(config.networkId,
+                WifiConfiguration.AuthAlgorithm.varName);
+        if (!TextUtils.isEmpty(value)) {
+            String vals[] = value.split(" ");
+            for (String val : vals) {
+                int index =
+                    lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
+                if (0 <= index) {
+                    config.allowedAuthAlgorithms.set(index);
+                }
+            }
+        }
+
+        value = WifiNative.getNetworkVariableCommand(config.networkId,
+                WifiConfiguration.PairwiseCipher.varName);
+        if (!TextUtils.isEmpty(value)) {
+            String vals[] = value.split(" ");
+            for (String val : vals) {
+                int index =
+                    lookupString(val, WifiConfiguration.PairwiseCipher.strings);
+                if (0 <= index) {
+                    config.allowedPairwiseCiphers.set(index);
+                }
+            }
+        }
+
+        value = WifiNative.getNetworkVariableCommand(config.networkId,
+                WifiConfiguration.GroupCipher.varName);
+        if (!TextUtils.isEmpty(value)) {
+            String vals[] = value.split(" ");
+            for (String val : vals) {
+                int index =
+                    lookupString(val, WifiConfiguration.GroupCipher.strings);
+                if (0 <= index) {
+                    config.allowedGroupCiphers.set(index);
+                }
+            }
+        }
+
+        for (WifiConfiguration.EnterpriseField field :
+                config.enterpriseFields) {
+            value = WifiNative.getNetworkVariableCommand(netId,
+                    field.varName());
+            if (!TextUtils.isEmpty(value)) {
+                if (field != config.eap) value = removeDoubleQuotes(value);
+                field.setValue(value);
+            }
+        }
+    }
+
+    private static String removeDoubleQuotes(String string) {
+        if (string.length() <= 2) return "";
+        return string.substring(1, string.length() - 1);
+    }
+
+    private static String convertToQuotedString(String string) {
+        return "\"" + string + "\"";
+    }
+
+    private static String makeString(BitSet set, String[] strings) {
+        StringBuffer buf = new StringBuffer();
+        int nextSetBit = -1;
+
+        /* Make sure all set bits are in [0, strings.length) to avoid
+         * going out of bounds on strings.  (Shouldn't happen, but...) */
+        set = set.get(0, strings.length);
+
+        while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
+            buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
+        }
+
+        // remove trailing space
+        if (set.cardinality() > 0) {
+            buf.setLength(buf.length() - 1);
+        }
+
+        return buf.toString();
+    }
+
+    private static int lookupString(String string, String[] strings) {
+        int size = strings.length;
+
+        string = string.replace('-', '_');
+
+        for (int i = 0; i < size; i++)
+            if (string.equals(strings[i]))
+                return i;
+
+        // if we ever get here, we should probably add the
+        // value to WifiConfiguration to reflect that it's
+        // supported by the WPA supplicant
+        Log.w(TAG, "Failed to look-up a string: " + string);
+
+        return -1;
+    }
+
+    /* Returns a unique for a given configuration */
+    private static int configKey(WifiConfiguration config) {
+        String key;
+
+        if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+            key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
+        } else if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
+                config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
+            key = config.SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
+        } else if (config.wepKeys[0] != null) {
+            key = config.SSID + "WEP";
+        } else {
+            key = config.SSID + KeyMgmt.strings[KeyMgmt.NONE];
+        }
+
+        return key.hashCode();
+    }
+
+    static String dump() {
+        StringBuffer sb = new StringBuffer();
+        String LS = System.getProperty("line.separator");
+        sb.append("sLastPriority ").append(sLastPriority).append(LS);
+        sb.append("Configured networks ").append(LS);
+        for (WifiConfiguration conf : getConfiguredNetworks()) {
+            sb.append(conf).append(LS);
+        }
+        return sb.toString();
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 01bc919..d5fb63e 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi;
 
+import android.net.LinkProperties;
 import android.os.Parcelable;
 import android.os.Parcel;
 
@@ -42,6 +43,8 @@
     public static final String priorityVarName = "priority";
     /** {@hide} */
     public static final String hiddenSSIDVarName = "scan_ssid";
+    /** {@hide} */
+    public static final int INVALID_NETWORK_ID = -1;
 
     /** {@hide} */
     public class EnterpriseField {
@@ -216,7 +219,7 @@
     /**
      * The network's SSID. Can either be an ASCII string,
      * which must be enclosed in double quotation marks
-     * (e.g., {@code &quot;MyNetwork&quot;}, or a string of
+     * (e.g., {@code "MyNetwork"}, or a string of
      * hex digits,which are not enclosed in quotes
      * (e.g., {@code 01a243f405}).
      */
@@ -239,7 +242,7 @@
     public String preSharedKey;
     /**
      * Up to four WEP keys. Either an ASCII string enclosed in double
-     * quotation marks (e.g., {@code &quot;abcdef&quot;} or a string
+     * quotation marks (e.g., {@code "abcdef"} or a string
      * of hex digits (e.g., {@code 0102030405}).
      * <p/>
      * When the value of one of these keys is read, the actual key is
@@ -294,9 +297,49 @@
      */
     public BitSet allowedGroupCiphers;
 
+    /**
+     * @hide
+     */
+    public enum IpAssignment {
+        /* Use statically configured IP settings. Configuration can be accessed
+         * with linkProperties */
+        STATIC,
+        /* Use dynamically configured IP settigns */
+        DHCP,
+        /* no IP details are assigned, this is used to indicate
+         * that any existing IP settings should be retained */
+        UNASSIGNED
+    }
+    /**
+     * @hide
+     */
+    public IpAssignment ipAssignment;
+
+    /**
+     * @hide
+     */
+    public enum ProxySettings {
+        /* No proxy is to be used. Any existing proxy settings
+         * should be cleared. */
+        NONE,
+        /* Use statically configured proxy. Configuration can be accessed
+         * with linkProperties */
+        STATIC,
+        /* no proxy details are assigned, this is used to indicate
+         * that any existing proxy settings should be retained */
+        UNASSIGNED
+    }
+    /**
+     * @hide
+     */
+    public ProxySettings proxySettings;
+    /**
+     * @hide
+     */
+    public LinkProperties linkProperties;
 
     public WifiConfiguration() {
-        networkId = -1;
+        networkId = INVALID_NETWORK_ID;
         SSID = null;
         BSSID = null;
         priority = 0;
@@ -312,6 +355,9 @@
         for (EnterpriseField field : enterpriseFields) {
             field.setValue(null);
         }
+        ipAssignment = IpAssignment.UNASSIGNED;
+        proxySettings = ProxySettings.UNASSIGNED;
+        linkProperties = new LinkProperties();
     }
 
     public String toString() {
@@ -393,6 +439,13 @@
             if (value != null) sbuf.append(value);
         }
         sbuf.append('\n');
+        sbuf.append("IP assignment: " + ipAssignment.toString());
+        sbuf.append("\n");
+        sbuf.append("Proxy settings: " + proxySettings.toString());
+        sbuf.append("\n");
+        sbuf.append(linkProperties.toString());
+        sbuf.append("\n");
+
         return sbuf.toString();
     }
 
@@ -432,6 +485,37 @@
         return 0;
     }
 
+    /** copy constructor {@hide} */
+    public WifiConfiguration(WifiConfiguration source) {
+        if (source != null) {
+            networkId = source.networkId;
+            status = source.status;
+            SSID = source.SSID;
+            BSSID = source.BSSID;
+            preSharedKey = source.preSharedKey;
+
+            wepKeys = new String[4];
+            for (int i = 0; i < wepKeys.length; i++)
+                wepKeys[i] = source.wepKeys[i];
+
+            wepTxKeyIndex = source.wepTxKeyIndex;
+            priority = source.priority;
+            hiddenSSID = source.hiddenSSID;
+            allowedKeyManagement   = (BitSet) source.allowedKeyManagement.clone();
+            allowedProtocols       = (BitSet) source.allowedProtocols.clone();
+            allowedAuthAlgorithms  = (BitSet) source.allowedAuthAlgorithms.clone();
+            allowedPairwiseCiphers = (BitSet) source.allowedPairwiseCiphers.clone();
+            allowedGroupCiphers    = (BitSet) source.allowedGroupCiphers.clone();
+
+            for (int i = 0; i < source.enterpriseFields.length; i++) {
+                enterpriseFields[i].setValue(source.enterpriseFields[i].value());
+            }
+            ipAssignment = source.ipAssignment;
+            proxySettings = source.proxySettings;
+            linkProperties = new LinkProperties(source.linkProperties);
+        }
+    }
+
     /** Implement the Parcelable interface {@hide} */
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(networkId);
@@ -454,6 +538,9 @@
         for (EnterpriseField field : enterpriseFields) {
             dest.writeString(field.value());
         }
+        dest.writeString(ipAssignment.name());
+        dest.writeString(proxySettings.name());
+        dest.writeParcelable(linkProperties, flags);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -480,6 +567,10 @@
                 for (EnterpriseField field : config.enterpriseFields) {
                     field.setValue(in.readString());
                 }
+
+                config.ipAssignment = IpAssignment.valueOf(in.readString());
+                config.proxySettings = ProxySettings.valueOf(in.readString());
+                config.linkProperties = in.readParcelable(null);
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index f883588..c85a988 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -166,7 +166,7 @@
      *
      * @hide
      */
-    public static final int WIFI_AP_STATE_DISABLING = 0;
+    public static final int WIFI_AP_STATE_DISABLING = 10;
     /**
      * Wi-Fi AP is disabled.
      *
@@ -175,7 +175,7 @@
      *
      * @hide
      */
-    public static final int WIFI_AP_STATE_DISABLED = 1;
+    public static final int WIFI_AP_STATE_DISABLED = 11;
     /**
      * Wi-Fi AP is currently being enabled. The state will change to
      * {@link #WIFI_AP_STATE_ENABLED} if it finishes successfully.
@@ -185,7 +185,7 @@
      *
      * @hide
      */
-    public static final int WIFI_AP_STATE_ENABLING = 2;
+    public static final int WIFI_AP_STATE_ENABLING = 12;
     /**
      * Wi-Fi AP is enabled.
      *
@@ -194,7 +194,7 @@
      *
      * @hide
      */
-    public static final int WIFI_AP_STATE_ENABLED = 3;
+    public static final int WIFI_AP_STATE_ENABLED = 13;
     /**
      * Wi-Fi AP is in a failed state. This state will occur when an error occurs during
      * enabling or disabling
@@ -204,7 +204,7 @@
      *
      * @hide
      */
-    public static final int WIFI_AP_STATE_FAILED = 4;
+    public static final int WIFI_AP_STATE_FAILED = 14;
 
     /**
      * Broadcast intent action indicating that a connection to the supplicant has
@@ -275,7 +275,14 @@
      * @see #ERROR_AUTHENTICATING
      */
     public static final String EXTRA_SUPPLICANT_ERROR = "supplicantError";
-
+    /**
+     * Broadcast intent action indicating that the supplicant configuration changed.
+     * This can be as a result of adding/updating/deleting a network
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String SUPPLICANT_CONFIG_CHANGED_ACTION =
+        "android.net.wifi.supplicant.CONFIG_CHANGE";
     /**
      * An access point scan has completed, and results are available from the supplicant.
      * Call {@link #getScanResults()} to obtain the results.
@@ -294,11 +301,35 @@
     public static final String EXTRA_NEW_RSSI = "newRssi";
 
     /**
+     * Broadcast intent action indicating that the IP configuration
+     * changed on wifi.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String CONFIG_CHANGED_ACTION = "android.net.wifi.CONFIG_CHANGED";
+
+    /**
+     * The lookup key for a {@link android.net.LinkProperties} object associated with the
+     * Wi-Fi network. Retrieve with
+     * {@link android.content.Intent#getParcelableExtra(String)}.
+     * @hide
+     */
+    public static final String EXTRA_LINK_PROPERTIES = "linkProperties";
+
+    /**
+     * The lookup key for a {@link android.net.LinkCapabilities} object associated with the
+     * Wi-Fi network. Retrieve with
+     * {@link android.content.Intent#getParcelableExtra(String)}.
+     * @hide
+     */
+    public static final String EXTRA_LINK_CAPABILITIES = "linkCapabilities";
+
+    /**
      * The network IDs of the configured networks could have changed.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
-    
+
     /**
      * Activity Action: Pick a Wi-Fi network to connect to.
      * <p>Input: Nothing.
@@ -513,7 +544,8 @@
      */
     public boolean disconnect() {
         try {
-            return mService.disconnect();
+            mService.disconnect();
+            return true;
         } catch (RemoteException e) {
             return false;
         }
@@ -527,7 +559,8 @@
      */
     public boolean reconnect() {
         try {
-            return mService.reconnect();
+            mService.reconnect();
+            return true;
         } catch (RemoteException e) {
             return false;
         }
@@ -541,7 +574,8 @@
      */
     public boolean reassociate() {
         try {
-            return mService.reassociate();
+            mService.reassociate();
+            return true;
         } catch (RemoteException e) {
             return false;
         }
@@ -638,57 +672,19 @@
     }
 
     /**
-     * Return the number of frequency channels that are allowed
-     * to be used in the current regulatory domain.
-     * @return the number of allowed channels, or {@code -1} if an error occurs
+     * Set the country code.
+     * @param countryCode country code in ISO 3166 format.
+     * @param persist {@code true} if this needs to be remembered
      *
-     * @hide pending API council
+     * @hide
      */
-    public int getNumAllowedChannels() {
+    public void setCountryCode(String country, boolean persist) {
         try {
-            return mService.getNumAllowedChannels();
-        } catch (RemoteException e) {
-            return -1;
-        }
+            mService.setCountryCode(country, persist);
+        } catch (RemoteException e) { }
     }
 
     /**
-     * Set the number of frequency channels that are allowed to be used
-     * in the current regulatory domain. This method should be used only
-     * if the correct number of channels cannot be determined automatically
-     * for some reason.
-     * @param numChannels the number of allowed channels. Must be greater than 0
-     * and less than or equal to 16.
-     * @param persist {@code true} if you want this remembered
-     * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
-     * {@code numChannels} is out of range.
-     *
-     * @hide pending API council
-     */
-    public boolean setNumAllowedChannels(int numChannels, boolean persist) {
-        try {
-            return mService.setNumAllowedChannels(numChannels, persist);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Return the list of valid values for the number of allowed radio channels
-     * for various regulatory domains.
-     * @return the list of channel counts, or {@code null} if the operation fails
-     *
-     * @hide pending API council review
-     */
-    public int[] getValidChannelCounts() {
-        try {
-            return mService.getValidChannelCounts();
-        } catch (RemoteException e) {
-            return null;
-        }
-   }
-
-    /**
      * Return the DHCP-assigned addresses from the last successful DHCP request,
      * if any.
      * @return the DHCP information
@@ -756,8 +752,9 @@
         } else if (rssi >= MAX_RSSI) {
             return numLevels - 1;
         } else {
-            int partitionSize = (MAX_RSSI - MIN_RSSI) / (numLevels - 1);
-            return (rssi - MIN_RSSI) / partitionSize;
+            float inputRange = (MAX_RSSI - MIN_RSSI);
+            float outputRange = (numLevels - 1);
+            return (int)((float)(rssi - MIN_RSSI) * outputRange / inputRange);
         }
     }
     
@@ -851,6 +848,209 @@
         }
     }
 
+   /**
+     * Start the driver and connect to network.
+     *
+     * This function will over-ride WifiLock and device idle status. For example,
+     * even if the device is idle or there is only a scan-only lock held,
+     * a start wifi would mean that wifi connection is kept active until
+     * a stopWifi() is sent.
+     *
+     * This API is used by WifiStateTracker
+     *
+     * @return {@code true} if the operation succeeds else {@code false}
+     * @hide
+     */
+    public boolean startWifi() {
+        try {
+            mService.startWifi();
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Disconnect from a network (if any) and stop the driver.
+     *
+     * This function will over-ride WifiLock and device idle status. Wi-Fi
+     * stays inactive until a startWifi() is issued.
+     *
+     * This API is used by WifiStateTracker
+     *
+     * @return {@code true} if the operation succeeds else {@code false}
+     * @hide
+     */
+    public boolean stopWifi() {
+        try {
+            mService.stopWifi();
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Add a bssid to the supplicant blacklist
+     *
+     * This API is used by WifiWatchdogService
+     *
+     * @return {@code true} if the operation succeeds else {@code false}
+     * @hide
+     */
+    public boolean addToBlacklist(String bssid) {
+        try {
+            mService.addToBlacklist(bssid);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Clear the supplicant blacklist
+     *
+     * This API is used by WifiWatchdogService
+     *
+     * @return {@code true} if the operation succeeds else {@code false}
+     * @hide
+     */
+    public boolean clearBlacklist() {
+        try {
+            mService.clearBlacklist();
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /* TODO: deprecate synchronous API and open up the following API */
+    /**
+     * Connect to a network with the given configuration. The network also
+     * gets added to the supplicant configuration.
+     *
+     * For a new network, this function is used instead of a
+     * sequence of addNetwork(), enableNetwork(), saveConfiguration() and
+     * reconnect()
+     *
+     * @param config the set of variables that describe the configuration,
+     *            contained in a {@link WifiConfiguration} object.
+     * @hide
+     */
+    public void connectNetwork(WifiConfiguration config) {
+        if (config == null) {
+            return;
+        }
+        try {
+            mService.connectNetworkWithConfig(config);
+        } catch (RemoteException e) { }
+    }
+
+    /**
+     * Connect to a network with the given networkId.
+     *
+     * This function is used instead of a enableNetwork(), saveConfiguration() and
+     * reconnect()
+     *
+     * @param networkId the network id identifiying the network in the
+     *                supplicant configuration list
+     * @hide
+     */
+    public void connectNetwork(int networkId) {
+        if (networkId < 0) {
+            return;
+        }
+        try {
+            mService.connectNetworkWithId(networkId);
+        } catch (RemoteException e) { }
+    }
+
+    /**
+     * Save the given network in the supplicant config. If the network already
+     * exists, the configuration is updated. A new network is enabled
+     * by default.
+     *
+     * For a new network, this function is used instead of a
+     * sequence of addNetwork(), enableNetwork() and saveConfiguration().
+     *
+     * For an existing network, it accomplishes the task of updateNetwork()
+     * and saveConfiguration()
+     *
+     * @param config the set of variables that describe the configuration,
+     *            contained in a {@link WifiConfiguration} object.
+     * @hide
+     */
+    public void saveNetwork(WifiConfiguration config) {
+        if (config == null) {
+            return;
+        }
+        try {
+            mService.saveNetwork(config);
+        } catch (RemoteException e) { }
+    }
+
+    /**
+     * Delete the network in the supplicant config.
+     *
+     * This function is used instead of a sequence of removeNetwork()
+     * and saveConfiguration().
+     *
+     * @param config the set of variables that describe the configuration,
+     *            contained in a {@link WifiConfiguration} object.
+     * @hide
+     */
+    public void forgetNetwork(int netId) {
+        if (netId < 0) {
+            return;
+        }
+        try {
+            mService.forgetNetwork(netId);
+        } catch (RemoteException e) { }
+    }
+
+    /**
+     * Start Wi-fi protected setup push button Configuration
+     *
+     * @param bssid BSSID of the access point
+     * @hide
+     */
+    public void startWpsPbc(String bssid) {
+        try {
+            mService.startWpsPbc(bssid);
+        } catch (RemoteException e) { }
+    }
+
+    /**
+     * Start Wi-fi Protected Setup pin method configuration
+     * with pin obtained from the access point
+     *
+     * @param bssid BSSID of the access point
+     * @param apPin PIN issued by the access point
+     *
+     * @hide
+     */
+    public void startWpsWithPinFromAccessPoint(String bssid, int apPin) {
+        try {
+            mService.startWpsWithPinFromAccessPoint(bssid, apPin);
+        } catch (RemoteException e) { }
+    }
+
+    /**
+     * Start Wi-fi Protected Setup pin method configuration
+     * with pin obtained from the device
+     *
+     * @param bssid BSSID of the access point
+     * @return pin generated by device
+     * @hide
+     */
+    public int startWpsWithPinFromDevice(String bssid) {
+        try {
+            return mService.startWpsWithPinFromDevice(bssid);
+        } catch (RemoteException e) {
+            return -1;
+        }
+    }
+
     /**
      * Allows an application to keep the Wi-Fi radio awake.
      * Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
@@ -1035,7 +1235,6 @@
      *
      * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL},
      * and {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
-     *
      * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is 
      *            never shown to the user under normal conditions, but should be descriptive 
      *            enough to identify your application and the specific WifiLock within it, if it
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index 266d801..934e509 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -19,14 +19,13 @@
 import android.util.Log;
 import android.util.Config;
 import android.net.NetworkInfo;
-import android.net.NetworkStateTracker;
 
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
 
 /**
  * Listens for events from the wpa_supplicant server, and passes them on
- * to the {@link WifiStateTracker} for handling. Runs in its own thread.
+ * to the {@link WifiStateMachine} for handling. Runs in its own thread.
  *
  * @hide
  */
@@ -117,7 +116,7 @@
     private static Pattern mConnectedEventPattern =
         Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) ");
 
-    private final WifiStateTracker mWifiStateTracker;
+    private final WifiStateMachine mWifiStateMachine;
 
     /**
      * This indicates the supplicant connection for the monitor is closed
@@ -139,31 +138,27 @@
      */
     private static final int MAX_RECV_ERRORS    = 10;
 
-    public WifiMonitor(WifiStateTracker tracker) {
-        mWifiStateTracker = tracker;
+    public WifiMonitor(WifiStateMachine wifiStateMachine) {
+        mWifiStateMachine = wifiStateMachine;
     }
 
     public void startMonitoring() {
         new MonitorThread().start();
     }
 
-    public NetworkStateTracker getNetworkStateTracker() {
-        return mWifiStateTracker;
-    }
-
     class MonitorThread extends Thread {
         public MonitorThread() {
             super("WifiMonitor");
         }
-        
+
         public void run() {
 
             if (connectToSupplicant()) {
                 // Send a message indicating that it is now possible to send commands
                 // to the supplicant
-                mWifiStateTracker.notifySupplicantConnection();
+                mWifiStateMachine.notifySupplicantConnection();
             } else {
-                mWifiStateTracker.notifySupplicantLost();
+                mWifiStateMachine.notifySupplicantLost();
                 return;
             }
 
@@ -259,7 +254,7 @@
                     }
 
                     // notify and exit
-                    mWifiStateTracker.notifySupplicantLost();
+                    mWifiStateMachine.notifySupplicantLost();
                     break;
                 } else {
                     handleEvent(event, eventData);
@@ -272,11 +267,11 @@
             int connectTries = 0;
 
             while (true) {
-                if (mWifiStateTracker.connectToSupplicant()) {
+                if (WifiNative.connectToSupplicant()) {
                     return true;
                 }
-                if (connectTries++ < 3) {
-                    nap(5);
+                if (connectTries++ < 5) {
+                    nap(1);
                 } else {
                     break;
                 }
@@ -285,7 +280,7 @@
         }
 
         private void handlePasswordKeyMayBeIncorrect() {
-            mWifiStateTracker.notifyPasswordKeyMayBeIncorrect();
+            mWifiStateMachine.notifyPasswordKeyMayBeIncorrect();
         }
 
         private void handleDriverEvent(String state) {
@@ -293,11 +288,11 @@
                 return;
             }
             if (state.equals("STOPPED")) {
-                mWifiStateTracker.notifyDriverStopped();
+                mWifiStateMachine.notifyDriverStopped();
             } else if (state.equals("STARTED")) {
-                mWifiStateTracker.notifyDriverStarted();
+                mWifiStateMachine.notifyDriverStarted();
             } else if (state.equals("HANGED")) {
-                mWifiStateTracker.notifyDriverHung();
+                mWifiStateMachine.notifyDriverHung();
             }
         }
 
@@ -318,7 +313,7 @@
                     break;
 
                 case SCAN_RESULTS:
-                    mWifiStateTracker.notifyScanResultsAvailable();
+                    mWifiStateMachine.notifyScanResultsAvailable();
                     break;
 
                 case UNKNOWN:
@@ -375,7 +370,7 @@
             if (newSupplicantState == SupplicantState.INVALID) {
                 Log.w(TAG, "Invalid supplicant state: " + newState);
             }
-            mWifiStateTracker.notifyStateChange(networkId, BSSID, newSupplicantState);
+            mWifiStateMachine.notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
         }
     }
 
@@ -395,7 +390,7 @@
                 }
             }
         }
-        mWifiStateTracker.notifyStateChange(newState, BSSID, networkId);
+        mWifiStateMachine.notifyNetworkStateChange(newState, BSSID, networkId);
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 25f05c0..06f945b 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -41,6 +41,8 @@
     public native static String getErrorString(int errorCode);
 
     public native static boolean loadDriver();
+
+    public native static boolean isDriverLoaded();
     
     public native static boolean unloadDriver();
 
@@ -109,12 +111,12 @@
 
     public native static boolean setPowerModeCommand(int mode);
 
+    public native static int getBandCommand();
+
+    public native static boolean setBandCommand(int band);
+
     public native static int getPowerModeCommand();
 
-    public native static boolean setNumAllowedChannelsCommand(int numChannels);
-
-    public native static int getNumAllowedChannelsCommand();
-
     /**
      * Sets the bluetooth coexistence mode.
      * 
@@ -145,12 +147,20 @@
 
     public native static boolean clearBlacklistCommand();
 
+    public native static boolean startWpsPbcCommand(String bssid);
+
+    public native static boolean startWpsWithPinFromAccessPointCommand(String bssid, int apPin);
+
+    public native static int startWpsWithPinFromDeviceCommand(String bssid);
+
     public native static boolean doDhcpRequest(DhcpInfo results);
 
     public native static String getDhcpError();
 
     public native static boolean setSuspendOptimizationsCommand(boolean enabled);
 
+    public native static boolean setCountryCodeCommand(String countryCode);
+
     /**
      * Wait for the supplicant to send an event, returning the event string.
      * @return the event string sent by the supplicant.
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
new file mode 100644
index 0000000..93c38cd
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -0,0 +1,3125 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
+
+/**
+ * TODO: Add soft AP states as part of WIFI_STATE_XXX
+ * Retain WIFI_STATE_ENABLING that indicates driver is loading
+ * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started
+ * and WIFI_STATE_FAILED for failure
+ * Deprecate WIFI_STATE_UNKNOWN
+ *
+ * Doing this will simplify the logic for sending broadcasts
+ */
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
+
+import android.app.ActivityManagerNative;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.net.LinkAddress;
+import android.net.NetworkInfo;
+import android.net.DhcpInfo;
+import android.net.NetworkUtils;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo.DetailedState;
+import android.net.LinkProperties;
+import android.os.Binder;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.PowerManager;
+import android.os.SystemProperties;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.Process;
+import android.os.WorkSource;
+import android.provider.Settings;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.app.backup.IBackupManager;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.Context;
+import android.content.IntentFilter;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
+
+/**
+ * Track the state of Wifi connectivity. All event handling is done here,
+ * and all changes in connectivity state are initiated here.
+ *
+ * @hide
+ */
+//TODO: we still need frequent scanning for the case when
+// we issue disconnect but need scan results for open network notification
+public class WifiStateMachine extends HierarchicalStateMachine {
+
+    private static final String TAG = "WifiStateMachine";
+    private static final String NETWORKTYPE = "WIFI";
+    private static final boolean DBG = false;
+
+    /* TODO: fetch a configurable interface */
+    private static final String SOFTAP_IFACE = "wl0.1";
+
+    private WifiMonitor mWifiMonitor;
+    private INetworkManagementService nwService;
+    private ConnectivityManager mCm;
+
+    /* Scan results handling */
+    private List<ScanResult> mScanResults;
+    private static final Pattern scanResultPattern = Pattern.compile("\t+");
+    private static final int SCAN_RESULT_CACHE_SIZE = 80;
+    private final LinkedHashMap<String, ScanResult> mScanResultCache;
+
+    private String mInterfaceName;
+
+    private int mLastSignalLevel = -1;
+    private String mLastBssid;
+    private int mLastNetworkId;
+    private boolean mEnableRssiPolling = false;
+    private boolean mPasswordKeyMayBeIncorrect = false;
+    private int mReconnectCount = 0;
+    private boolean mIsScanMode = false;
+
+    /**
+     * Instance of the bluetooth headset helper. This needs to be created
+     * early because there is a delay before it actually 'connects', as
+     * noted by its javadoc. If we check before it is connected, it will be
+     * in an error state and we will not disable coexistence.
+     */
+    private BluetoothHeadset mBluetoothHeadset;
+
+    private BluetoothA2dp mBluetoothA2dp;
+
+    private LinkProperties mLinkProperties;
+
+    // Wakelock held during wifi start/stop and driver load/unload
+    private PowerManager.WakeLock mWakeLock;
+
+    private Context mContext;
+
+    private DhcpInfo mDhcpInfo;
+    private WifiInfo mWifiInfo;
+    private NetworkInfo mNetworkInfo;
+    private SupplicantStateTracker mSupplicantStateTracker;
+    /* Connection to a specific network involves disabling all networks,
+     * this flag tracks if networks need to be re-enabled */
+    private boolean mEnableAllNetworks = false;
+    /* Track if WPS was started since we need to re-enable networks
+     * and load configuration afterwards */
+    private boolean mWpsStarted = false;
+
+    private AlarmManager mAlarmManager;
+    private PendingIntent mScanIntent;
+
+    // Channel for sending replies.
+    private AsyncChannel mReplyChannel = new AsyncChannel();
+
+    // Event log tags (must be in sync with event-log-tags)
+    private static final int EVENTLOG_WIFI_STATE_CHANGED        = 50021;
+    private static final int EVENTLOG_WIFI_EVENT_HANDLED        = 50022;
+    private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED  = 50023;
+
+    /* Load the driver */
+    private static final int CMD_LOAD_DRIVER                      = 1;
+    /* Unload the driver */
+    private static final int CMD_UNLOAD_DRIVER                    = 2;
+    /* Indicates driver load succeeded */
+    private static final int CMD_LOAD_DRIVER_SUCCESS              = 3;
+    /* Indicates driver load failed */
+    private static final int CMD_LOAD_DRIVER_FAILURE              = 4;
+    /* Indicates driver unload succeeded */
+    private static final int CMD_UNLOAD_DRIVER_SUCCESS            = 5;
+    /* Indicates driver unload failed */
+    private static final int CMD_UNLOAD_DRIVER_FAILURE            = 6;
+    /* Set bluetooth headset proxy */
+    private static final int CMD_SET_BLUETOOTH_HEADSET_PROXY      = 7;
+    /* Set bluetooth A2dp proxy */
+    private static final int CMD_SET_BLUETOOTH_A2DP_PROXY         = 8;
+
+    /* Start the supplicant */
+    private static final int CMD_START_SUPPLICANT                 = 11;
+    /* Stop the supplicant */
+    private static final int CMD_STOP_SUPPLICANT                  = 12;
+    /* Start the driver */
+    private static final int CMD_START_DRIVER                     = 13;
+    /* Start the driver */
+    private static final int CMD_STOP_DRIVER                      = 14;
+    /* Indicates DHCP succeded */
+    private static final int CMD_IP_CONFIG_SUCCESS                = 15;
+    /* Indicates DHCP failed */
+    private static final int CMD_IP_CONFIG_FAILURE                = 16;
+    /* Re-configure interface */
+    private static final int CMD_RECONFIGURE_IP                   = 17;
+
+
+    /* Start the soft access point */
+    private static final int CMD_START_AP                         = 21;
+    /* Stop the soft access point */
+    private static final int CMD_STOP_AP                          = 22;
+
+
+    /* Supplicant events */
+    /* Connection to supplicant established */
+    private static final int SUP_CONNECTION_EVENT                 = 31;
+    /* Connection to supplicant lost */
+    private static final int SUP_DISCONNECTION_EVENT              = 32;
+    /* Driver start completed */
+    private static final int DRIVER_START_EVENT                   = 33;
+    /* Driver stop completed */
+    private static final int DRIVER_STOP_EVENT                    = 34;
+    /* Network connection completed */
+    private static final int NETWORK_CONNECTION_EVENT             = 36;
+    /* Network disconnection completed */
+    private static final int NETWORK_DISCONNECTION_EVENT          = 37;
+    /* Scan results are available */
+    private static final int SCAN_RESULTS_EVENT                   = 38;
+    /* Supplicate state changed */
+    private static final int SUPPLICANT_STATE_CHANGE_EVENT        = 39;
+    /* Password may be incorrect */
+    private static final int PASSWORD_MAY_BE_INCORRECT_EVENT      = 40;
+
+    /* Supplicant commands */
+    /* Is supplicant alive ? */
+    private static final int CMD_PING_SUPPLICANT                  = 51;
+    /* Add/update a network configuration */
+    private static final int CMD_ADD_OR_UPDATE_NETWORK            = 52;
+    /* Delete a network */
+    private static final int CMD_REMOVE_NETWORK                   = 53;
+    /* Enable a network. The device will attempt a connection to the given network. */
+    private static final int CMD_ENABLE_NETWORK                   = 54;
+    /* Disable a network. The device does not attempt a connection to the given network. */
+    private static final int CMD_DISABLE_NETWORK                  = 55;
+    /* Blacklist network. De-prioritizes the given BSSID for connection. */
+    private static final int CMD_BLACKLIST_NETWORK                = 56;
+    /* Clear the blacklist network list */
+    private static final int CMD_CLEAR_BLACKLIST                  = 57;
+    /* Save configuration */
+    private static final int CMD_SAVE_CONFIG                      = 58;
+
+    /* Supplicant commands after driver start*/
+    /* Initiate a scan */
+    private static final int CMD_START_SCAN                       = 71;
+    /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
+    private static final int CMD_SET_SCAN_MODE                    = 72;
+    /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
+    private static final int CMD_SET_SCAN_TYPE                    = 73;
+    /* Disconnect from a network */
+    private static final int CMD_DISCONNECT                       = 74;
+    /* Reconnect to a network */
+    private static final int CMD_RECONNECT                        = 75;
+    /* Reassociate to a network */
+    private static final int CMD_REASSOCIATE                      = 76;
+    /* Controls power mode and suspend mode optimizations
+     *
+     * When high perf mode is enabled, power mode is set to
+     * POWER_MODE_ACTIVE and suspend mode optimizations are disabled
+     *
+     * When high perf mode is disabled, power mode is set to
+     * POWER_MODE_AUTO and suspend mode optimizations are enabled
+     *
+     * Suspend mode optimizations include:
+     * - packet filtering
+     * - turn off roaming
+     * - DTIM wake up settings
+     */
+    private static final int CMD_SET_HIGH_PERF_MODE               = 77;
+    /* Set bluetooth co-existence
+     * BLUETOOTH_COEXISTENCE_MODE_ENABLED
+     * BLUETOOTH_COEXISTENCE_MODE_DISABLED
+     * BLUETOOTH_COEXISTENCE_MODE_SENSE
+     */
+    private static final int CMD_SET_BLUETOOTH_COEXISTENCE        = 78;
+    /* Enable/disable bluetooth scan mode
+     * true(1)
+     * false(0)
+     */
+    private static final int CMD_SET_BLUETOOTH_SCAN_MODE          = 79;
+    /* Set the country code */
+    private static final int CMD_SET_COUNTRY_CODE                 = 80;
+    /* Request connectivity manager wake lock before driver stop */
+    private static final int CMD_REQUEST_CM_WAKELOCK              = 81;
+    /* Enables RSSI poll */
+    private static final int CMD_ENABLE_RSSI_POLL                 = 82;
+    /* RSSI poll */
+    private static final int CMD_RSSI_POLL                        = 83;
+    /* Set up packet filtering */
+    private static final int CMD_START_PACKET_FILTERING           = 84;
+    /* Clear packet filter */
+    private static final int CMD_STOP_PACKET_FILTERING            = 85;
+    /* Connect to a specified network (network id
+     * or WifiConfiguration) This involves increasing
+     * the priority of the network, enabling the network
+     * (while disabling others) and issuing a reconnect.
+     * Note that CMD_RECONNECT just does a reconnect to
+     * an existing network. All the networks get enabled
+     * upon a successful connection or a failure.
+     */
+    private static final int CMD_CONNECT_NETWORK                  = 86;
+    /* Save the specified network. This involves adding
+     * an enabled network (if new) and updating the
+     * config and issuing a save on supplicant config.
+     */
+    private static final int CMD_SAVE_NETWORK                     = 87;
+    /* Delete the specified network. This involves
+     * removing the network and issuing a save on
+     * supplicant config.
+     */
+    private static final int CMD_FORGET_NETWORK                   = 88;
+    /* Start Wi-Fi protected setup push button configuration */
+    private static final int CMD_START_WPS_PBC                    = 89;
+    /* Start Wi-Fi protected setup pin method configuration with pin obtained from AP */
+    private static final int CMD_START_WPS_PIN_FROM_AP            = 90;
+    /* Start Wi-Fi protected setup pin method configuration with pin obtained from device */
+    private static final int CMD_START_WPS_PIN_FROM_DEVICE        = 91;
+
+    /**
+     * Interval in milliseconds between polling for connection
+     * status items that are not sent via asynchronous events.
+     * An example is RSSI (signal strength).
+     */
+    private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
+
+    private static final int CONNECT_MODE   = 1;
+    private static final int SCAN_ONLY_MODE = 2;
+
+    private static final int SCAN_ACTIVE = 1;
+    private static final int SCAN_PASSIVE = 2;
+
+    /* Auto allows 802.11A/B/G operation */
+    private static final int BAND_AUTO = 0;
+    /* 5GHz allows 802.11A operation */
+    private static final int BAND_5G = 1;
+    /* 2.4GHz allows 802.11B/G operation */
+    private static final int BAND_2G = 2;
+
+    private static final int SUCCESS = 1;
+    private static final int FAILURE = -1;
+
+    /**
+     * The maximum number of times we will retry a connection to an access point
+     * for which we have failed in acquiring an IP address from DHCP. A value of
+     * N means that we will make N+1 connection attempts in all.
+     * <p>
+     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
+     * value if a Settings value is not present.
+     */
+    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
+
+    private static final int POWER_MODE_ACTIVE = 1;
+    private static final int POWER_MODE_AUTO = 0;
+
+    /**
+     * See {@link Settings.Secure#WIFI_SCAN_INTERVAL_MS}. This is the default value if a
+     * Settings.Secure value is not present.
+     */
+    private static final long DEFAULT_SCAN_INTERVAL_MS = 60 * 1000; /* 1 minute */
+
+    /* Default parent state */
+    private HierarchicalState mDefaultState = new DefaultState();
+    /* Temporary initial state */
+    private HierarchicalState mInitialState = new InitialState();
+    /* Unloading the driver */
+    private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
+    /* Loading the driver */
+    private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
+    /* Driver load/unload failed */
+    private HierarchicalState mDriverFailedState = new DriverFailedState();
+    /* Driver loading */
+    private HierarchicalState mDriverLoadingState = new DriverLoadingState();
+    /* Driver loaded */
+    private HierarchicalState mDriverLoadedState = new DriverLoadedState();
+    /* Driver loaded, waiting for supplicant to start */
+    private HierarchicalState mWaitForSupState = new WaitForSupState();
+
+    /* Driver loaded and supplicant ready */
+    private HierarchicalState mDriverSupReadyState = new DriverSupReadyState();
+    /* Driver start issued, waiting for completed event */
+    private HierarchicalState mDriverStartingState = new DriverStartingState();
+    /* Driver started */
+    private HierarchicalState mDriverStartedState = new DriverStartedState();
+    /* Driver stopping */
+    private HierarchicalState mDriverStoppingState = new DriverStoppingState();
+    /* Driver stopped */
+    private HierarchicalState mDriverStoppedState = new DriverStoppedState();
+    /* Scan for networks, no connection will be established */
+    private HierarchicalState mScanModeState = new ScanModeState();
+    /* Connecting to an access point */
+    private HierarchicalState mConnectModeState = new ConnectModeState();
+    /* Fetching IP after network connection (assoc+auth complete) */
+    private HierarchicalState mConnectingState = new ConnectingState();
+    /* Connected with IP addr */
+    private HierarchicalState mConnectedState = new ConnectedState();
+    /* disconnect issued, waiting for network disconnect confirmation */
+    private HierarchicalState mDisconnectingState = new DisconnectingState();
+    /* Network is not connected, supplicant assoc+auth is not complete */
+    private HierarchicalState mDisconnectedState = new DisconnectedState();
+
+    /* Soft Ap is running */
+    private HierarchicalState mSoftApStartedState = new SoftApStartedState();
+
+
+    /**
+     * One of  {@link WifiManager#WIFI_STATE_DISABLED},
+     *         {@link WifiManager#WIFI_STATE_DISABLING},
+     *         {@link WifiManager#WIFI_STATE_ENABLED},
+     *         {@link WifiManager#WIFI_STATE_ENABLING},
+     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
+     *
+     */
+    private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
+
+    /**
+     * One of  {@link WifiManager#WIFI_AP_STATE_DISABLED},
+     *         {@link WifiManager#WIFI_AP_STATE_DISABLING},
+     *         {@link WifiManager#WIFI_AP_STATE_ENABLED},
+     *         {@link WifiManager#WIFI_AP_STATE_ENABLING},
+     *         {@link WifiManager#WIFI_AP_STATE_FAILED}
+     *
+     */
+    private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
+
+    private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
+    private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
+
+    private static final int SCAN_REQUEST = 0;
+    private static final String ACTION_START_SCAN =
+        "com.android.server.WifiManager.action.START_SCAN";
+
+    /**
+     * Keep track of whether WIFI is running.
+     */
+    private boolean mIsRunning = false;
+
+    /**
+     * Keep track of whether we last told the battery stats we had started.
+     */
+    private boolean mReportedRunning = false;
+
+    /**
+     * Most recently set source of starting WIFI.
+     */
+    private final WorkSource mRunningWifiUids = new WorkSource();
+
+    /**
+     * The last reported UIDs that were responsible for starting WIFI.
+     */
+    private final WorkSource mLastRunningWifiUids = new WorkSource();
+
+    private final IBatteryStats mBatteryStats;
+
+    public WifiStateMachine(Context context) {
+        super(TAG);
+
+        mContext = context;
+
+        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
+        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        nwService = INetworkManagementService.Stub.asInterface(b);
+
+        mWifiMonitor = new WifiMonitor(this);
+        mDhcpInfo = new DhcpInfo();
+        mWifiInfo = new WifiInfo();
+        mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
+        mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler());
+
+        mLinkProperties = new LinkProperties();
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+                                    BluetoothProfile.A2DP);
+            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+                                    BluetoothProfile.HEADSET);
+        }
+
+        mNetworkInfo.setIsAvailable(false);
+        mLinkProperties.clear();
+        mLastBssid = null;
+        mLastNetworkId = -1;
+        mLastSignalLevel = -1;
+
+        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+        Intent scanIntent = new Intent(ACTION_START_SCAN, null);
+        mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
+
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        startScan(false);
+                    }
+                },
+                new IntentFilter(ACTION_START_SCAN));
+
+        mScanResultCache = new LinkedHashMap<String, ScanResult>(
+            SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
+                /*
+                 * Limit the cache size by SCAN_RESULT_CACHE_SIZE
+                 * elements
+                 */
+                @Override
+                public boolean removeEldestEntry(Map.Entry eldest) {
+                    return SCAN_RESULT_CACHE_SIZE < this.size();
+                }
+        };
+
+        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+        addState(mDefaultState);
+            addState(mInitialState, mDefaultState);
+            addState(mDriverUnloadingState, mDefaultState);
+            addState(mDriverUnloadedState, mDefaultState);
+                addState(mDriverFailedState, mDriverUnloadedState);
+            addState(mDriverLoadingState, mDefaultState);
+            addState(mDriverLoadedState, mDefaultState);
+                addState(mWaitForSupState, mDriverLoadedState);
+            addState(mDriverSupReadyState, mDefaultState);
+                addState(mDriverStartingState, mDriverSupReadyState);
+                addState(mDriverStartedState, mDriverSupReadyState);
+                    addState(mScanModeState, mDriverStartedState);
+                    addState(mConnectModeState, mDriverStartedState);
+                        addState(mConnectingState, mConnectModeState);
+                        addState(mConnectedState, mConnectModeState);
+                        addState(mDisconnectingState, mConnectModeState);
+                        addState(mDisconnectedState, mConnectModeState);
+                addState(mDriverStoppingState, mDriverSupReadyState);
+                addState(mDriverStoppedState, mDriverSupReadyState);
+            addState(mSoftApStartedState, mDefaultState);
+
+        setInitialState(mInitialState);
+
+        if (DBG) setDbg(true);
+
+        //start the state machine
+        start();
+    }
+
+    /*********************************************************
+     * Methods exposed for public use
+     ********************************************************/
+
+    /**
+     * TODO: doc
+     */
+    public boolean syncPingSupplicant(AsyncChannel channel) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
+        boolean result = (resultMsg.arg1 != FAILURE);
+        resultMsg.recycle();
+        return result;
+    }
+
+    /**
+     * TODO: doc
+     */
+    public void startScan(boolean forceActive) {
+        sendMessage(obtainMessage(CMD_START_SCAN, forceActive ?
+                SCAN_ACTIVE : SCAN_PASSIVE, 0));
+    }
+
+    /**
+     * TODO: doc
+     */
+    public void setWifiEnabled(boolean enable) {
+        mLastEnableUid.set(Binder.getCallingUid());
+        if (enable) {
+            /* Argument is the state that is entered prior to load */
+            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
+            sendMessage(CMD_START_SUPPLICANT);
+        } else {
+            sendMessage(CMD_STOP_SUPPLICANT);
+            /* Argument is the state that is entered upon success */
+            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
+        }
+    }
+
+    /**
+     * TODO: doc
+     */
+    public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
+        mLastApEnableUid.set(Binder.getCallingUid());
+        if (enable) {
+            /* Argument is the state that is entered prior to load */
+            sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
+            sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
+        } else {
+            sendMessage(CMD_STOP_AP);
+            /* Argument is the state that is entered upon success */
+            sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
+        }
+    }
+
+    /**
+     * TODO: doc
+     */
+    public int syncGetWifiState() {
+        return mWifiState.get();
+    }
+
+    /**
+     * TODO: doc
+     */
+    public String syncGetWifiStateByName() {
+        switch (mWifiState.get()) {
+            case WIFI_STATE_DISABLING:
+                return "disabling";
+            case WIFI_STATE_DISABLED:
+                return "disabled";
+            case WIFI_STATE_ENABLING:
+                return "enabling";
+            case WIFI_STATE_ENABLED:
+                return "enabled";
+            case WIFI_STATE_UNKNOWN:
+                return "unknown state";
+            default:
+                return "[invalid state]";
+        }
+    }
+
+    /**
+     * TODO: doc
+     */
+    public int syncGetWifiApState() {
+        return mWifiApState.get();
+    }
+
+    /**
+     * TODO: doc
+     */
+    public String syncGetWifiApStateByName() {
+        switch (mWifiApState.get()) {
+            case WIFI_AP_STATE_DISABLING:
+                return "disabling";
+            case WIFI_AP_STATE_DISABLED:
+                return "disabled";
+            case WIFI_AP_STATE_ENABLING:
+                return "enabling";
+            case WIFI_AP_STATE_ENABLED:
+                return "enabled";
+            case WIFI_AP_STATE_FAILED:
+                return "failed";
+            default:
+                return "[invalid state]";
+        }
+    }
+
+    /**
+     * Get status information for the current connection, if any.
+     * @return a {@link WifiInfo} object containing information about the current connection
+     *
+     */
+    public WifiInfo syncRequestConnectionInfo() {
+        return mWifiInfo;
+    }
+
+    public DhcpInfo syncGetDhcpInfo() {
+        synchronized (mDhcpInfo) {
+            return new DhcpInfo(mDhcpInfo);
+        }
+    }
+
+    /**
+     * TODO: doc
+     */
+    public void setDriverStart(boolean enable) {
+        if (enable) {
+            sendMessage(CMD_START_DRIVER);
+        } else {
+            sendMessage(CMD_STOP_DRIVER);
+        }
+    }
+
+    /**
+     * TODO: doc
+     */
+    public void setScanOnlyMode(boolean enable) {
+      if (enable) {
+          sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
+      } else {
+          sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
+      }
+    }
+
+    /**
+     * TODO: doc
+     */
+    public void setScanType(boolean active) {
+      if (active) {
+          sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0));
+      } else {
+          sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0));
+      }
+    }
+
+    /**
+     * TODO: doc
+     */
+    public List<ScanResult> syncGetScanResultsList() {
+        return mScanResults;
+    }
+
+    /**
+     * Disconnect from Access Point
+     */
+    public void disconnectCommand() {
+        sendMessage(CMD_DISCONNECT);
+    }
+
+    /**
+     * Initiate a reconnection to AP
+     */
+    public void reconnectCommand() {
+        sendMessage(CMD_RECONNECT);
+    }
+
+    /**
+     * Initiate a re-association to AP
+     */
+    public void reassociateCommand() {
+        sendMessage(CMD_REASSOCIATE);
+    }
+
+    /**
+     * Add a network synchronously
+     *
+     * @return network id of the new network
+     */
+    public int syncAddOrUpdateNetwork(AsyncChannel channel, WifiConfiguration config) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_NETWORK, config);
+        int result = resultMsg.arg1;
+        resultMsg.recycle();
+        return result;
+    }
+
+    public List<WifiConfiguration> syncGetConfiguredNetworks() {
+        return WifiConfigStore.getConfiguredNetworks();
+    }
+
+    /**
+     * Delete a network
+     *
+     * @param networkId id of the network to be removed
+     */
+    public boolean syncRemoveNetwork(AsyncChannel channel, int networkId) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_NETWORK, networkId);
+        boolean result = (resultMsg.arg1 != FAILURE);
+        resultMsg.recycle();
+        return result;
+    }
+
+    /**
+     * Enable a network
+     *
+     * @param netId network id of the network
+     * @param disableOthers true, if all other networks have to be disabled
+     * @return {@code true} if the operation succeeds, {@code false} otherwise
+     */
+    public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId,
+                disableOthers ? 1 : 0);
+        boolean result = (resultMsg.arg1 != FAILURE);
+        resultMsg.recycle();
+        return result;
+    }
+
+    /**
+     * Disable a network
+     *
+     * @param netId network id of the network
+     * @return {@code true} if the operation succeeds, {@code false} otherwise
+     */
+    public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_DISABLE_NETWORK, netId);
+        boolean result = (resultMsg.arg1 != FAILURE);
+        resultMsg.recycle();
+        return result;
+    }
+
+    /**
+     * Blacklist a BSSID. This will avoid the AP if there are
+     * alternate APs to connect
+     *
+     * @param bssid BSSID of the network
+     */
+    public void addToBlacklist(String bssid) {
+        sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
+    }
+
+    /**
+     * Clear the blacklist list
+     *
+     */
+    public void clearBlacklist() {
+        sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
+    }
+
+    public void connectNetwork(int netId) {
+        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, netId, 0));
+    }
+
+    public void connectNetwork(WifiConfiguration wifiConfig) {
+        /* arg1 is used to indicate netId, force a netId value of -1 when
+         * we are passing a configuration since the default value of
+         * 0 is a valid netId
+         */
+        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, -1, 0, wifiConfig));
+    }
+
+    public void saveNetwork(WifiConfiguration wifiConfig) {
+        sendMessage(obtainMessage(CMD_SAVE_NETWORK, wifiConfig));
+    }
+
+    public void forgetNetwork(int netId) {
+        sendMessage(obtainMessage(CMD_FORGET_NETWORK, netId, 0));
+    }
+
+    public void startWpsPbc(String bssid) {
+        sendMessage(obtainMessage(CMD_START_WPS_PBC, bssid));
+    }
+
+    public void startWpsWithPinFromAccessPoint(String bssid, int apPin) {
+        sendMessage(obtainMessage(CMD_START_WPS_PIN_FROM_AP, apPin, 0, bssid));
+    }
+
+    public int syncStartWpsWithPinFromDevice(AsyncChannel channel, String bssid) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_START_WPS_PIN_FROM_DEVICE, bssid);
+        int result = resultMsg.arg1;
+        resultMsg.recycle();
+        return result;
+    }
+
+    public void enableRssiPolling(boolean enabled) {
+       sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
+    }
+
+    /**
+     * Start packet filtering
+     */
+    public void startPacketFiltering() {
+        sendMessage(CMD_START_PACKET_FILTERING);
+    }
+
+    /**
+     * Stop packet filtering
+     */
+    public void stopPacketFiltering() {
+        sendMessage(CMD_STOP_PACKET_FILTERING);
+    }
+
+    /**
+     * Set high performance mode of operation.
+     * Enabling would set active power mode and disable suspend optimizations;
+     * disabling would set auto power mode and enable suspend optimizations
+     * @param enable true if enable, false otherwise
+     */
+    public void setHighPerfModeEnabled(boolean enable) {
+        sendMessage(obtainMessage(CMD_SET_HIGH_PERF_MODE, enable ? 1 : 0, 0));
+    }
+
+    /**
+     * Set the country code
+     * @param countryCode following ISO 3166 format
+     * @param persist {@code true} if the setting should be remembered.
+     */
+    public void setCountryCode(String countryCode, boolean persist) {
+        if (persist) {
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.WIFI_COUNTRY_CODE,
+                    countryCode);
+        }
+        sendMessage(obtainMessage(CMD_SET_COUNTRY_CODE, countryCode));
+    }
+
+    /**
+     * Set bluetooth coex mode:
+     *
+     * @param mode
+     *  BLUETOOTH_COEXISTENCE_MODE_ENABLED
+     *  BLUETOOTH_COEXISTENCE_MODE_DISABLED
+     *  BLUETOOTH_COEXISTENCE_MODE_SENSE
+     */
+    public void setBluetoothCoexistenceMode(int mode) {
+        sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0));
+    }
+
+    /**
+     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
+     * some of the low-level scan parameters used by the driver are changed to
+     * reduce interference with A2DP streaming.
+     *
+     * @param isBluetoothPlaying whether to enable or disable this mode
+     */
+    public void setBluetoothScanMode(boolean isBluetoothPlaying) {
+        sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0));
+    }
+
+    /**
+     * Save configuration on supplicant
+     *
+     * @return {@code true} if the operation succeeds, {@code false} otherwise
+     *
+     * TODO: deprecate this
+     */
+    public boolean syncSaveConfig(AsyncChannel channel) {
+        Message resultMsg = channel.sendMessageSynchronously(CMD_SAVE_CONFIG);
+        boolean result = (resultMsg.arg1 != FAILURE);
+        resultMsg.recycle();
+        return result;
+    }
+
+    /**
+     * TODO: doc
+     */
+    public void requestCmWakeLock() {
+        sendMessage(CMD_REQUEST_CM_WAKELOCK);
+    }
+
+    public void updateBatteryWorkSource(WorkSource newSource) {
+        synchronized (mRunningWifiUids) {
+            try {
+                if (newSource != null) {
+                    mRunningWifiUids.set(newSource);
+                }
+                if (mIsRunning) {
+                    if (mReportedRunning) {
+                        // If the work source has changed since last time, need
+                        // to remove old work from battery stats.
+                        if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
+                            mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
+                                    mRunningWifiUids);
+                            mLastRunningWifiUids.set(mRunningWifiUids);
+                        }
+                    } else {
+                        // Now being started, report it.
+                        mBatteryStats.noteWifiRunning(mRunningWifiUids);
+                        mLastRunningWifiUids.set(mRunningWifiUids);
+                        mReportedRunning = true;
+                    }
+                } else {
+                    if (mReportedRunning) {
+                        // Last reported we were running, time to stop.
+                        mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
+                        mLastRunningWifiUids.clear();
+                        mReportedRunning = false;
+                    }
+                }
+                mWakeLock.setWorkSource(newSource);
+            } catch (RemoteException ignore) {
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        String LS = System.getProperty("line.separator");
+        sb.append("current HSM state: ").append(getCurrentState().getName()).append(LS);
+        sb.append("mLinkProperties ").append(mLinkProperties).append(LS);
+        sb.append("mWifiInfo ").append(mWifiInfo).append(LS);
+        sb.append("mDhcpInfo ").append(mDhcpInfo).append(LS);
+        sb.append("mNetworkInfo ").append(mNetworkInfo).append(LS);
+        sb.append("mLastSignalLevel ").append(mLastSignalLevel).append(LS);
+        sb.append("mLastBssid ").append(mLastBssid).append(LS);
+        sb.append("mLastNetworkId ").append(mLastNetworkId).append(LS);
+        sb.append("mEnableAllNetworks ").append(mEnableAllNetworks).append(LS);
+        sb.append("mEnableRssiPolling ").append(mEnableRssiPolling).append(LS);
+        sb.append("mPasswordKeyMayBeIncorrect ").append(mPasswordKeyMayBeIncorrect).append(LS);
+        sb.append("mReconnectCount ").append(mReconnectCount).append(LS);
+        sb.append("mIsScanMode ").append(mIsScanMode).append(LS);
+        sb.append("Supplicant status").append(LS)
+                .append(WifiNative.statusCommand()).append(LS).append(LS);
+
+        sb.append(WifiConfigStore.dump());
+        return sb.toString();
+    }
+
+    /*********************************************************
+     * Internal private functions
+     ********************************************************/
+
+    /**
+     * Set the country code from the system setting value, if any.
+     */
+    private void setCountryCode() {
+        String countryCode = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.WIFI_COUNTRY_CODE);
+        if (countryCode != null && !countryCode.isEmpty()) {
+            setCountryCode(countryCode, false);
+        } else {
+            //use driver default
+        }
+    }
+
+    private void setWifiState(int wifiState) {
+        final int previousWifiState = mWifiState.get();
+
+        try {
+            if (wifiState == WIFI_STATE_ENABLED) {
+                mBatteryStats.noteWifiOn();
+            } else if (wifiState == WIFI_STATE_DISABLED) {
+                mBatteryStats.noteWifiOff();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to note battery stats in wifi");
+        }
+
+        mWifiState.set(wifiState);
+
+        if (DBG) Log.d(TAG, "setWifiState: " + syncGetWifiStateByName());
+
+        final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
+        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
+        mContext.sendStickyBroadcast(intent);
+    }
+
+    private void setWifiApState(int wifiApState) {
+        final int previousWifiApState = mWifiApState.get();
+
+        try {
+            if (wifiApState == WIFI_AP_STATE_ENABLED) {
+                mBatteryStats.noteWifiOn();
+            } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
+                mBatteryStats.noteWifiOff();
+            }
+        } catch (RemoteException e) {
+            Log.d(TAG, "Failed to note battery stats in wifi");
+        }
+
+        // Update state
+        mWifiApState.set(wifiApState);
+
+        if (DBG) Log.d(TAG, "setWifiApState: " + syncGetWifiApStateByName());
+
+        final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
+        intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
+        mContext.sendStickyBroadcast(intent);
+    }
+
+    /**
+     * Parse the scan result line passed to us by wpa_supplicant (helper).
+     * @param line the line to parse
+     * @return the {@link ScanResult} object
+     */
+    private ScanResult parseScanResult(String line) {
+        ScanResult scanResult = null;
+        if (line != null) {
+            /*
+             * Cache implementation (LinkedHashMap) is not synchronized, thus,
+             * must synchronized here!
+             */
+            synchronized (mScanResultCache) {
+                String[] result = scanResultPattern.split(line);
+                if (3 <= result.length && result.length <= 5) {
+                    String bssid = result[0];
+                    // bssid | frequency | level | flags | ssid
+                    int frequency;
+                    int level;
+                    try {
+                        frequency = Integer.parseInt(result[1]);
+                        level = Integer.parseInt(result[2]);
+                        /* some implementations avoid negative values by adding 256
+                         * so we need to adjust for that here.
+                         */
+                        if (level > 0) level -= 256;
+                    } catch (NumberFormatException e) {
+                        frequency = 0;
+                        level = 0;
+                    }
+
+                    /*
+                     * The formatting of the results returned by
+                     * wpa_supplicant is intended to make the fields
+                     * line up nicely when printed,
+                     * not to make them easy to parse. So we have to
+                     * apply some heuristics to figure out which field
+                     * is the SSID and which field is the flags.
+                     */
+                    String ssid;
+                    String flags;
+                    if (result.length == 4) {
+                        if (result[3].charAt(0) == '[') {
+                            flags = result[3];
+                            ssid = "";
+                        } else {
+                            flags = "";
+                            ssid = result[3];
+                        }
+                    } else if (result.length == 5) {
+                        flags = result[3];
+                        ssid = result[4];
+                    } else {
+                        // Here, we must have 3 fields: no flags and ssid
+                        // set
+                        flags = "";
+                        ssid = "";
+                    }
+
+                    // bssid + ssid is the hash key
+                    String key = bssid + ssid;
+                    scanResult = mScanResultCache.get(key);
+                    if (scanResult != null) {
+                        scanResult.level = level;
+                        scanResult.SSID = ssid;
+                        scanResult.capabilities = flags;
+                        scanResult.frequency = frequency;
+                    } else {
+                        // Do not add scan results that have no SSID set
+                        if (0 < ssid.trim().length()) {
+                            scanResult =
+                                new ScanResult(
+                                    ssid, bssid, flags, level, frequency);
+                            mScanResultCache.put(key, scanResult);
+                        }
+                    }
+                } else {
+                    Log.w(TAG, "Misformatted scan result text with " +
+                          result.length + " fields: " + line);
+                }
+            }
+        }
+
+        return scanResult;
+    }
+
+    /**
+     * scanResults input format
+     * 00:bb:cc:dd:cc:ee       2427    166     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net1
+     * 00:bb:cc:dd:cc:ff       2412    165     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net2
+     */
+    private void setScanResults(String scanResults) {
+        if (scanResults == null) {
+            return;
+        }
+
+        List<ScanResult> scanList = new ArrayList<ScanResult>();
+
+        int lineCount = 0;
+
+        int scanResultsLen = scanResults.length();
+        // Parse the result string, keeping in mind that the last line does
+        // not end with a newline.
+        for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
+            if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
+                ++lineCount;
+
+                if (lineCount == 1) {
+                    lineBeg = lineEnd + 1;
+                    continue;
+                }
+                if (lineEnd > lineBeg) {
+                    String line = scanResults.substring(lineBeg, lineEnd);
+                    ScanResult scanResult = parseScanResult(line);
+                    if (scanResult != null) {
+                        scanList.add(scanResult);
+                    } else {
+                        //TODO: hidden network handling
+                    }
+                }
+                lineBeg = lineEnd + 1;
+            }
+        }
+
+        mScanResults = scanList;
+    }
+
+    private String fetchSSID() {
+        String status = WifiNative.statusCommand();
+        if (status == null) {
+            return null;
+        }
+        // extract ssid from a series of "name=value"
+        String[] lines = status.split("\n");
+        for (String line : lines) {
+            String[] prop = line.split(" *= *");
+            if (prop.length < 2) continue;
+            String name = prop[0];
+            String value = prop[1];
+            if (name.equalsIgnoreCase("ssid")) return value;
+        }
+        return null;
+    }
+
+    private void setHighPerfModeEnabledNative(boolean enable) {
+        if(!WifiNative.setSuspendOptimizationsCommand(!enable)) {
+            Log.e(TAG, "set suspend optimizations failed!");
+        }
+        if (enable) {
+            if (!WifiNative.setPowerModeCommand(POWER_MODE_ACTIVE)) {
+                Log.e(TAG, "set power mode active failed!");
+            }
+        } else {
+            if (!WifiNative.setPowerModeCommand(POWER_MODE_AUTO)) {
+                Log.e(TAG, "set power mode auto failed!");
+            }
+        }
+    }
+
+    private void configureLinkProperties() {
+        if (WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
+            mLinkProperties = WifiConfigStore.getLinkProperties(mLastNetworkId);
+        } else {
+            // TODO - fix this for v6
+            synchronized (mDhcpInfo) {
+                mLinkProperties.addLinkAddress(new LinkAddress(
+                        NetworkUtils.intToInetAddress(mDhcpInfo.ipAddress),
+                        NetworkUtils.intToInetAddress(mDhcpInfo.netmask)));
+                mLinkProperties.setGateway(NetworkUtils.intToInetAddress(mDhcpInfo.gateway));
+                mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns1));
+                mLinkProperties.addDns(NetworkUtils.intToInetAddress(mDhcpInfo.dns2));
+            }
+            mLinkProperties.setHttpProxy(WifiConfigStore.getProxyProperties(mLastNetworkId));
+        }
+        mLinkProperties.setInterfaceName(mInterfaceName);
+        Log.d(TAG, "netId=" + mLastNetworkId  + " Link configured: " + mLinkProperties.toString());
+    }
+
+    private int getMaxDhcpRetries() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                                      Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
+                                      DEFAULT_MAX_DHCP_RETRIES);
+    }
+
+    /**
+     * Whether to disable coexistence mode while obtaining IP address. We
+     * disable coexistence if the headset indicates that there are no
+     * connected devices. If we have not got an indication of the service
+     * connection yet, we go ahead with disabling coexistence mode.
+     *
+     * @return Whether to disable coexistence mode.
+     */
+    private boolean shouldDisableCoexistenceMode() {
+        if (mBluetoothHeadset == null) return true;
+        List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
+        return (devices.size() != 0 ? false : true);
+    }
+
+    private void checkIsBluetoothPlaying() {
+        boolean isBluetoothPlaying = false;
+        if (mBluetoothA2dp != null) {
+            List<BluetoothDevice> connected = mBluetoothA2dp.getConnectedDevices();
+
+            for (BluetoothDevice device : connected) {
+                if (mBluetoothA2dp.isA2dpPlaying(device)) {
+                    isBluetoothPlaying = true;
+                    break;
+                }
+            }
+        }
+        setBluetoothScanMode(isBluetoothPlaying);
+    }
+
+    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
+        new BluetoothProfile.ServiceListener() {
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+                if (profile == BluetoothProfile.HEADSET) {
+                    sendMessage(CMD_SET_BLUETOOTH_HEADSET_PROXY, proxy);
+                } else if (profile == BluetoothProfile.A2DP) {
+                    sendMessage(CMD_SET_BLUETOOTH_A2DP_PROXY, proxy);
+                }
+        }
+
+        public void onServiceDisconnected(int profile) {
+                if (profile == BluetoothProfile.HEADSET) {
+                    sendMessage(CMD_SET_BLUETOOTH_HEADSET_PROXY, null);
+                } else if (profile == BluetoothProfile.A2DP) {
+                    sendMessage(CMD_SET_BLUETOOTH_A2DP_PROXY, null);
+                }
+        }
+    };
+
+    private void sendScanResultsAvailableBroadcast() {
+        if (!ActivityManagerNative.isSystemReady()) return;
+
+        mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+    }
+
+    private void sendRssiChangeBroadcast(final int newRssi) {
+        if (!ActivityManagerNative.isSystemReady()) return;
+
+        Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
+        intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
+        mContext.sendBroadcast(intent);
+    }
+
+    private void sendNetworkStateChangeBroadcast(String bssid) {
+        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
+        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, mLinkProperties);
+        if (bssid != null)
+            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
+        mContext.sendStickyBroadcast(intent);
+    }
+
+    /* TODO: Unused for now, will be used when ip change on connected network is handled */
+    private void sendConfigChangeBroadcast() {
+        if (!ActivityManagerNative.isSystemReady()) return;
+        Intent intent = new Intent(WifiManager.CONFIG_CHANGED_ACTION);
+        intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, mLinkProperties);
+        mContext.sendBroadcast(intent);
+    }
+
+    private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
+        Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
+        if (failedAuth) {
+            intent.putExtra(
+                WifiManager.EXTRA_SUPPLICANT_ERROR,
+                WifiManager.ERROR_AUTHENTICATING);
+        }
+        mContext.sendStickyBroadcast(intent);
+    }
+
+    private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
+        if (!ActivityManagerNative.isSystemReady()) return;
+
+        Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+        intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
+        mContext.sendBroadcast(intent);
+    }
+
+    /**
+     * Record the detailed state of a network.
+     * @param state the new @{code DetailedState}
+     */
+    private void setDetailedState(NetworkInfo.DetailedState state) {
+        Log.d(TAG, "setDetailed state, old ="
+                + mNetworkInfo.getDetailedState() + " and new state=" + state);
+        if (state != mNetworkInfo.getDetailedState()) {
+            mNetworkInfo.setDetailedState(state, null, null);
+        }
+    }
+
+    /**
+     * Poll for info not reported via events
+     * RSSI & Linkspeed
+     */
+    private void requestPolledInfo() {
+        int newRssi = WifiNative.getRssiCommand();
+        if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
+            /* some implementations avoid negative values by adding 256
+             * so we need to adjust for that here.
+             */
+            if (newRssi > 0) newRssi -= 256;
+            mWifiInfo.setRssi(newRssi);
+            /*
+             * Rather then sending the raw RSSI out every time it
+             * changes, we precalculate the signal level that would
+             * be displayed in the status bar, and only send the
+             * broadcast if that much more coarse-grained number
+             * changes. This cuts down greatly on the number of
+             * broadcasts, at the cost of not mWifiInforming others
+             * interested in RSSI of all the changes in signal
+             * level.
+             */
+            // TODO: The second arg to the call below needs to be a symbol somewhere, but
+            // it's actually the size of an array of icons that's private
+            // to StatusBar Policy.
+            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
+            if (newSignalLevel != mLastSignalLevel) {
+                sendRssiChangeBroadcast(newRssi);
+            }
+            mLastSignalLevel = newSignalLevel;
+        } else {
+            mWifiInfo.setRssi(-200);
+        }
+        int newLinkSpeed = WifiNative.getLinkSpeedCommand();
+        if (newLinkSpeed != -1) {
+            mWifiInfo.setLinkSpeed(newLinkSpeed);
+        }
+    }
+
+    /**
+     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
+     * using the interface, stopping DHCP & disabling interface
+     */
+    private void handleNetworkDisconnect() {
+        Log.d(TAG, "Reset connections and stopping DHCP");
+
+        /*
+         * Reset connections & stop DHCP
+         */
+        NetworkUtils.resetConnections(mInterfaceName);
+
+        if (!NetworkUtils.stopDhcp(mInterfaceName)) {
+            Log.e(TAG, "Could not stop DHCP");
+        }
+
+        /* Disable interface */
+        NetworkUtils.disableInterface(mInterfaceName);
+
+        /* send event to CM & network change broadcast */
+        setDetailedState(DetailedState.DISCONNECTED);
+        sendNetworkStateChangeBroadcast(mLastBssid);
+
+        /* Reset data structures */
+        mWifiInfo.setIpAddress(0);
+        mWifiInfo.setBSSID(null);
+        mWifiInfo.setSSID(null);
+        mWifiInfo.setNetworkId(-1);
+
+        /* Clear network properties */
+        mLinkProperties.clear();
+
+        mLastBssid= null;
+        mLastNetworkId = -1;
+
+    }
+
+
+    /*********************************************************
+     * Notifications from WifiMonitor
+     ********************************************************/
+
+    /**
+     * A structure for supplying information about a supplicant state
+     * change in the STATE_CHANGE event message that comes from the
+     * WifiMonitor
+     * thread.
+     */
+    private static class StateChangeResult {
+        StateChangeResult(int networkId, String BSSID, Object state) {
+            this.state = state;
+            this.BSSID = BSSID;
+            this.networkId = networkId;
+        }
+        int networkId;
+        String BSSID;
+        Object state;
+    }
+
+    /**
+     * Send the tracker a notification that a user-entered password key
+     * may be incorrect (i.e., caused authentication to fail).
+     */
+    void notifyPasswordKeyMayBeIncorrect() {
+        sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
+    }
+
+    /**
+     * Send the tracker a notification that a connection to the supplicant
+     * daemon has been established.
+     */
+    void notifySupplicantConnection() {
+        sendMessage(SUP_CONNECTION_EVENT);
+    }
+
+    /**
+     * Send the tracker a notification that connection to the supplicant
+     * daemon is lost
+     */
+    void notifySupplicantLost() {
+        sendMessage(SUP_DISCONNECTION_EVENT);
+    }
+
+    /**
+     * Send the tracker a notification that the state of Wifi connectivity
+     * has changed.
+     * @param networkId the configured network on which the state change occurred
+     * @param newState the new network state
+     * @param BSSID when the new state is {@link DetailedState#CONNECTED
+     * NetworkInfo.DetailedState.CONNECTED},
+     * this is the MAC address of the access point. Otherwise, it
+     * is {@code null}.
+     */
+    void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
+        if (newState == NetworkInfo.DetailedState.CONNECTED) {
+            sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT,
+                    new StateChangeResult(networkId, BSSID, newState)));
+        } else {
+            sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT,
+                    new StateChangeResult(networkId, BSSID, newState)));
+        }
+    }
+
+    /**
+     * Send the tracker a notification that the state of the supplicant
+     * has changed.
+     * @param networkId the configured network on which the state change occurred
+     * @param newState the new {@code SupplicantState}
+     */
+    void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
+        sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
+                new StateChangeResult(networkId, BSSID, newState)));
+    }
+
+    /**
+     * Send the tracker a notification that a scan has completed, and results
+     * are available.
+     */
+    void notifyScanResultsAvailable() {
+        /**
+         * Switch scan mode over to passive.
+         * Turning off scan-only mode happens only in "Connect" mode
+         */
+        setScanType(false);
+        sendMessage(SCAN_RESULTS_EVENT);
+    }
+
+    void notifyDriverStarted() {
+        sendMessage(DRIVER_START_EVENT);
+    }
+
+    void notifyDriverStopped() {
+        sendMessage(DRIVER_STOP_EVENT);
+    }
+
+    void notifyDriverHung() {
+        setWifiEnabled(false);
+        setWifiEnabled(true);
+    }
+
+
+    /********************************************************
+     * HSM states
+     *******************************************************/
+
+    class DefaultState extends HierarchicalState {
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_SET_BLUETOOTH_HEADSET_PROXY:
+                    mBluetoothHeadset = (BluetoothHeadset) message.obj;
+                    break;
+                case CMD_SET_BLUETOOTH_A2DP_PROXY:
+                    mBluetoothA2dp = (BluetoothA2dp) message.obj;
+                    break;
+                    /* Synchronous call returns */
+                case CMD_PING_SUPPLICANT:
+                case CMD_ENABLE_NETWORK:
+                case CMD_DISABLE_NETWORK:
+                case CMD_ADD_OR_UPDATE_NETWORK:
+                case CMD_REMOVE_NETWORK:
+                case CMD_SAVE_CONFIG:
+                case CMD_START_WPS_PIN_FROM_DEVICE:
+                    mReplyChannel.replyToMessage(message, message.what, FAILURE);
+                    break;
+                case CMD_ENABLE_RSSI_POLL:
+                    mEnableRssiPolling = (message.arg1 == 1);
+                    mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
+                    break;
+                    /* Discard */
+                case CMD_LOAD_DRIVER:
+                case CMD_UNLOAD_DRIVER:
+                case CMD_START_SUPPLICANT:
+                case CMD_STOP_SUPPLICANT:
+                case CMD_START_DRIVER:
+                case CMD_STOP_DRIVER:
+                case CMD_START_AP:
+                case CMD_STOP_AP:
+                case CMD_START_SCAN:
+                case CMD_DISCONNECT:
+                case CMD_RECONNECT:
+                case CMD_REASSOCIATE:
+                case CMD_RECONFIGURE_IP:
+                case SUP_CONNECTION_EVENT:
+                case SUP_DISCONNECTION_EVENT:
+                case DRIVER_START_EVENT:
+                case DRIVER_STOP_EVENT:
+                case NETWORK_CONNECTION_EVENT:
+                case NETWORK_DISCONNECTION_EVENT:
+                case SCAN_RESULTS_EVENT:
+                case SUPPLICANT_STATE_CHANGE_EVENT:
+                case PASSWORD_MAY_BE_INCORRECT_EVENT:
+                case CMD_BLACKLIST_NETWORK:
+                case CMD_CLEAR_BLACKLIST:
+                case CMD_SET_SCAN_MODE:
+                case CMD_SET_SCAN_TYPE:
+                case CMD_SET_HIGH_PERF_MODE:
+                case CMD_SET_BLUETOOTH_COEXISTENCE:
+                case CMD_SET_BLUETOOTH_SCAN_MODE:
+                case CMD_SET_COUNTRY_CODE:
+                case CMD_REQUEST_CM_WAKELOCK:
+                case CMD_CONNECT_NETWORK:
+                case CMD_SAVE_NETWORK:
+                case CMD_FORGET_NETWORK:
+                case CMD_START_WPS_PBC:
+                case CMD_START_WPS_PIN_FROM_AP:
+                    break;
+                default:
+                    Log.e(TAG, "Error! unhandled message" + message);
+                    break;
+            }
+            return HANDLED;
+        }
+    }
+
+    class InitialState extends HierarchicalState {
+        @Override
+        //TODO: could move logging into a common class
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            // [31-8] Reserved for future use
+            // [7 - 0] HSM state change
+            // 50021 wifi_state_changed (custom|1|5)
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+            if (WifiNative.isDriverLoaded()) {
+                transitionTo(mDriverLoadedState);
+            }
+            else {
+                transitionTo(mDriverUnloadedState);
+            }
+        }
+    }
+
+    class DriverLoadingState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+            final Message message = new Message();
+            message.copyFrom(getCurrentMessage());
+            /* TODO: add a timeout to fail when driver load is hung.
+             * Similarly for driver unload.
+             */
+            new Thread(new Runnable() {
+                public void run() {
+                    mWakeLock.acquire();
+                    //enabling state
+                    switch(message.arg1) {
+                        case WIFI_STATE_ENABLING:
+                            setWifiState(WIFI_STATE_ENABLING);
+                            break;
+                        case WIFI_AP_STATE_ENABLING:
+                            setWifiApState(WIFI_AP_STATE_ENABLING);
+                            break;
+                    }
+
+                    if(WifiNative.loadDriver()) {
+                        Log.d(TAG, "Driver load successful");
+                        sendMessage(CMD_LOAD_DRIVER_SUCCESS);
+                    } else {
+                        Log.e(TAG, "Failed to load driver!");
+                        switch(message.arg1) {
+                            case WIFI_STATE_ENABLING:
+                                setWifiState(WIFI_STATE_UNKNOWN);
+                                break;
+                            case WIFI_AP_STATE_ENABLING:
+                                setWifiApState(WIFI_AP_STATE_FAILED);
+                                break;
+                        }
+                        sendMessage(CMD_LOAD_DRIVER_FAILURE);
+                    }
+                    mWakeLock.release();
+                }
+            }).start();
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_LOAD_DRIVER_SUCCESS:
+                    transitionTo(mDriverLoadedState);
+                    break;
+                case CMD_LOAD_DRIVER_FAILURE:
+                    transitionTo(mDriverFailedState);
+                    break;
+                case CMD_LOAD_DRIVER:
+                case CMD_UNLOAD_DRIVER:
+                case CMD_START_SUPPLICANT:
+                case CMD_STOP_SUPPLICANT:
+                case CMD_START_AP:
+                case CMD_STOP_AP:
+                case CMD_START_DRIVER:
+                case CMD_STOP_DRIVER:
+                case CMD_SET_SCAN_MODE:
+                case CMD_SET_SCAN_TYPE:
+                case CMD_SET_HIGH_PERF_MODE:
+                case CMD_SET_BLUETOOTH_COEXISTENCE:
+                case CMD_SET_BLUETOOTH_SCAN_MODE:
+                case CMD_SET_COUNTRY_CODE:
+                case CMD_START_PACKET_FILTERING:
+                case CMD_STOP_PACKET_FILTERING:
+                    deferMessage(message);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+    class DriverLoadedState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch(message.what) {
+                case CMD_UNLOAD_DRIVER:
+                    transitionTo(mDriverUnloadingState);
+                    break;
+                case CMD_START_SUPPLICANT:
+                    if(WifiNative.startSupplicant()) {
+                        Log.d(TAG, "Supplicant start successful");
+                        mWifiMonitor.startMonitoring();
+                        setWifiState(WIFI_STATE_ENABLED);
+                        transitionTo(mWaitForSupState);
+                    } else {
+                        Log.e(TAG, "Failed to start supplicant!");
+                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
+                    }
+                    break;
+                case CMD_START_AP:
+                    try {
+                        nwService.startAccessPoint((WifiConfiguration) message.obj,
+                                    mInterfaceName,
+                                    SOFTAP_IFACE);
+                    } catch(Exception e) {
+                        Log.e(TAG, "Exception in startAccessPoint()");
+                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
+                        break;
+                    }
+                    Log.d(TAG, "Soft AP start successful");
+                    setWifiApState(WIFI_AP_STATE_ENABLED);
+                    transitionTo(mSoftApStartedState);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+    class DriverUnloadingState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+            final Message message = new Message();
+            message.copyFrom(getCurrentMessage());
+            new Thread(new Runnable() {
+                public void run() {
+                    if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+                    mWakeLock.acquire();
+                    if(WifiNative.unloadDriver()) {
+                        Log.d(TAG, "Driver unload successful");
+                        sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
+
+                        switch(message.arg1) {
+                            case WIFI_STATE_DISABLED:
+                            case WIFI_STATE_UNKNOWN:
+                                setWifiState(message.arg1);
+                                break;
+                            case WIFI_AP_STATE_DISABLED:
+                            case WIFI_AP_STATE_FAILED:
+                                setWifiApState(message.arg1);
+                                break;
+                        }
+                    } else {
+                        Log.e(TAG, "Failed to unload driver!");
+                        sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
+
+                        switch(message.arg1) {
+                            case WIFI_STATE_DISABLED:
+                            case WIFI_STATE_UNKNOWN:
+                                setWifiState(WIFI_STATE_UNKNOWN);
+                                break;
+                            case WIFI_AP_STATE_DISABLED:
+                            case WIFI_AP_STATE_FAILED:
+                                setWifiApState(WIFI_AP_STATE_FAILED);
+                                break;
+                        }
+                    }
+                    mWakeLock.release();
+                }
+            }).start();
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_UNLOAD_DRIVER_SUCCESS:
+                    transitionTo(mDriverUnloadedState);
+                    break;
+                case CMD_UNLOAD_DRIVER_FAILURE:
+                    transitionTo(mDriverFailedState);
+                    break;
+                case CMD_LOAD_DRIVER:
+                case CMD_UNLOAD_DRIVER:
+                case CMD_START_SUPPLICANT:
+                case CMD_STOP_SUPPLICANT:
+                case CMD_START_AP:
+                case CMD_STOP_AP:
+                case CMD_START_DRIVER:
+                case CMD_STOP_DRIVER:
+                case CMD_SET_SCAN_MODE:
+                case CMD_SET_SCAN_TYPE:
+                case CMD_SET_HIGH_PERF_MODE:
+                case CMD_SET_BLUETOOTH_COEXISTENCE:
+                case CMD_SET_BLUETOOTH_SCAN_MODE:
+                case CMD_SET_COUNTRY_CODE:
+                case CMD_START_PACKET_FILTERING:
+                case CMD_STOP_PACKET_FILTERING:
+                    deferMessage(message);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+    class DriverUnloadedState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_LOAD_DRIVER:
+                    transitionTo(mDriverLoadingState);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+    class DriverFailedState extends HierarchicalState {
+        @Override
+        public void enter() {
+            Log.e(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            return NOT_HANDLED;
+        }
+    }
+
+
+    class WaitForSupState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch(message.what) {
+                case SUP_CONNECTION_EVENT:
+                    Log.d(TAG, "Supplicant connection established");
+                    mSupplicantStateTracker.resetSupplicantState();
+                    /* Initialize data structures */
+                    mLastBssid = null;
+                    mLastNetworkId = -1;
+                    mLastSignalLevel = -1;
+
+                    mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
+
+                    WifiConfigStore.initialize(mContext);
+
+                    //TODO: initialize and fix multicast filtering
+                    //mWM.initializeMulticastFiltering();
+
+                    checkIsBluetoothPlaying();
+
+                    sendSupplicantConnectionChangedBroadcast(true);
+                    transitionTo(mDriverStartedState);
+                    break;
+                case SUP_DISCONNECTION_EVENT:
+                    Log.e(TAG, "Failed to setup control channel, restart supplicant");
+                    WifiNative.stopSupplicant();
+                    transitionTo(mDriverLoadedState);
+                    sendMessageAtFrontOfQueue(CMD_START_SUPPLICANT);
+                    break;
+                case CMD_LOAD_DRIVER:
+                case CMD_UNLOAD_DRIVER:
+                case CMD_START_SUPPLICANT:
+                case CMD_STOP_SUPPLICANT:
+                case CMD_START_AP:
+                case CMD_STOP_AP:
+                case CMD_START_DRIVER:
+                case CMD_STOP_DRIVER:
+                case CMD_SET_SCAN_MODE:
+                case CMD_SET_SCAN_TYPE:
+                case CMD_SET_HIGH_PERF_MODE:
+                case CMD_SET_BLUETOOTH_COEXISTENCE:
+                case CMD_SET_BLUETOOTH_SCAN_MODE:
+                case CMD_SET_COUNTRY_CODE:
+                case CMD_START_PACKET_FILTERING:
+                case CMD_STOP_PACKET_FILTERING:
+                    deferMessage(message);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+    class DriverSupReadyState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+            /* Initialize for connect mode operation at start */
+            mIsScanMode = false;
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            WifiConfiguration config;
+            switch(message.what) {
+                case CMD_STOP_SUPPLICANT:   /* Supplicant stopped by user */
+                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+                    Log.d(TAG, "Stop supplicant received");
+                    WifiNative.closeSupplicantConnection();
+                    WifiNative.stopSupplicant();
+                    handleNetworkDisconnect();
+                    sendSupplicantConnectionChangedBroadcast(false);
+                    mSupplicantStateTracker.resetSupplicantState();
+                    transitionTo(mDriverLoadedState);
+                    break;
+                case SUP_DISCONNECTION_EVENT:  /* Supplicant died */
+                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+                    Log.e(TAG, "Supplicant died, restarting");
+                    WifiNative.closeSupplicantConnection();
+                    handleNetworkDisconnect();
+                    sendSupplicantConnectionChangedBroadcast(false);
+                    mSupplicantStateTracker.resetSupplicantState();
+                    transitionTo(mDriverLoadedState);
+                    sendMessageAtFrontOfQueue(CMD_START_SUPPLICANT); /* restart */
+                    break;
+                case SCAN_RESULTS_EVENT:
+                    setScanResults(WifiNative.scanResultsCommand());
+                    sendScanResultsAvailableBroadcast();
+                    break;
+                case CMD_PING_SUPPLICANT:
+                    boolean ok = WifiNative.pingCommand();
+                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+                    break;
+                case CMD_ADD_OR_UPDATE_NETWORK:
+                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+                    config = (WifiConfiguration) message.obj;
+                    mReplyChannel.replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK,
+                            WifiConfigStore.addOrUpdateNetwork(config));
+                    break;
+                case CMD_REMOVE_NETWORK:
+                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+                    ok = WifiConfigStore.removeNetwork(message.arg1);
+                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+                    break;
+                case CMD_ENABLE_NETWORK:
+                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+                    ok = WifiConfigStore.enableNetwork(message.arg1, message.arg2 == 1);
+                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+                    break;
+                case CMD_DISABLE_NETWORK:
+                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+                    ok = WifiConfigStore.disableNetwork(message.arg1);
+                    mReplyChannel.replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+                    break;
+                case CMD_BLACKLIST_NETWORK:
+                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+                    WifiNative.addToBlacklistCommand((String)message.obj);
+                    break;
+                case CMD_CLEAR_BLACKLIST:
+                    WifiNative.clearBlacklistCommand();
+                    break;
+                case CMD_SAVE_CONFIG:
+                    ok = WifiConfigStore.saveConfig();
+                    mReplyChannel.replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
+
+                    // Inform the backup manager about a data change
+                    IBackupManager ibm = IBackupManager.Stub.asInterface(
+                            ServiceManager.getService(Context.BACKUP_SERVICE));
+                    if (ibm != null) {
+                        try {
+                            ibm.dataChanged("com.android.providers.settings");
+                        } catch (Exception e) {
+                            // Try again later
+                        }
+                    }
+                    break;
+                    /* Cannot start soft AP while in client mode */
+                case CMD_START_AP:
+                    Log.d(TAG, "Failed to start soft AP with a running supplicant");
+                    EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+                    setWifiApState(WIFI_AP_STATE_FAILED);
+                    break;
+                case CMD_SET_SCAN_MODE:
+                    mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
+                    break;
+                case CMD_SAVE_NETWORK:
+                    config = (WifiConfiguration) message.obj;
+                    WifiConfigStore.saveNetwork(config);
+                    break;
+                case CMD_FORGET_NETWORK:
+                    WifiConfigStore.forgetNetwork(message.arg1);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+    class DriverStartingState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch(message.what) {
+                case DRIVER_START_EVENT:
+                    transitionTo(mDriverStartedState);
+                    break;
+                    /* Queue driver commands & connection events */
+                case CMD_START_DRIVER:
+                case CMD_STOP_DRIVER:
+                case SUPPLICANT_STATE_CHANGE_EVENT:
+                case NETWORK_CONNECTION_EVENT:
+                case NETWORK_DISCONNECTION_EVENT:
+                case PASSWORD_MAY_BE_INCORRECT_EVENT:
+                case CMD_SET_SCAN_TYPE:
+                case CMD_SET_HIGH_PERF_MODE:
+                case CMD_SET_BLUETOOTH_COEXISTENCE:
+                case CMD_SET_BLUETOOTH_SCAN_MODE:
+                case CMD_SET_COUNTRY_CODE:
+                case CMD_START_PACKET_FILTERING:
+                case CMD_STOP_PACKET_FILTERING:
+                case CMD_START_SCAN:
+                case CMD_DISCONNECT:
+                case CMD_REASSOCIATE:
+                case CMD_RECONNECT:
+                    deferMessage(message);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+    class DriverStartedState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+            mIsRunning = true;
+            updateBatteryWorkSource(null);
+
+            /* set country code */
+            setCountryCode();
+
+            if (mIsScanMode) {
+                WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+                WifiNative.disconnectCommand();
+                transitionTo(mScanModeState);
+            } else {
+                WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+                WifiNative.reconnectCommand();
+                transitionTo(mDisconnectedState);
+            }
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch(message.what) {
+                case CMD_SET_SCAN_TYPE:
+                    if (message.arg1 == SCAN_ACTIVE) {
+                        WifiNative.setScanModeCommand(true);
+                    } else {
+                        WifiNative.setScanModeCommand(false);
+                    }
+                    break;
+                case CMD_SET_HIGH_PERF_MODE:
+                    setHighPerfModeEnabledNative(message.arg1 == 1);
+                    break;
+                case CMD_SET_BLUETOOTH_COEXISTENCE:
+                    WifiNative.setBluetoothCoexistenceModeCommand(message.arg1);
+                    break;
+                case CMD_SET_BLUETOOTH_SCAN_MODE:
+                    WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1);
+                    break;
+                case CMD_SET_COUNTRY_CODE:
+                    String country = (String) message.obj;
+                    Log.d(TAG, "set country code " + country);
+                    if (!WifiNative.setCountryCodeCommand(country.toUpperCase())) {
+                        Log.e(TAG, "Failed to set country code " + country);
+                    }
+                    break;
+                case CMD_STOP_DRIVER:
+                    mWakeLock.acquire();
+                    WifiNative.stopDriverCommand();
+                    transitionTo(mDriverStoppingState);
+                    mWakeLock.release();
+                    break;
+                case CMD_REQUEST_CM_WAKELOCK:
+                    if (mCm == null) {
+                        mCm = (ConnectivityManager)mContext.getSystemService(
+                                Context.CONNECTIVITY_SERVICE);
+                    }
+                    mCm.requestNetworkTransitionWakelock(TAG);
+                    break;
+                case CMD_START_PACKET_FILTERING:
+                    WifiNative.startPacketFiltering();
+                    break;
+                case CMD_STOP_PACKET_FILTERING:
+                    WifiNative.stopPacketFiltering();
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+        @Override
+        public void exit() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            mIsRunning = false;
+            updateBatteryWorkSource(null);
+            mScanResults = null;
+        }
+    }
+
+    class DriverStoppingState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch(message.what) {
+                case DRIVER_STOP_EVENT:
+                    transitionTo(mDriverStoppedState);
+                    break;
+                    /* Queue driver commands */
+                case CMD_START_DRIVER:
+                case CMD_STOP_DRIVER:
+                case CMD_SET_SCAN_TYPE:
+                case CMD_SET_HIGH_PERF_MODE:
+                case CMD_SET_BLUETOOTH_COEXISTENCE:
+                case CMD_SET_BLUETOOTH_SCAN_MODE:
+                case CMD_SET_COUNTRY_CODE:
+                case CMD_START_PACKET_FILTERING:
+                case CMD_STOP_PACKET_FILTERING:
+                case CMD_START_SCAN:
+                case CMD_DISCONNECT:
+                case CMD_REASSOCIATE:
+                case CMD_RECONNECT:
+                    deferMessage(message);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+    class DriverStoppedState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_START_DRIVER:
+                    mWakeLock.acquire();
+                    WifiNative.startDriverCommand();
+                    transitionTo(mDriverStartingState);
+                    mWakeLock.release();
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+    class ScanModeState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch(message.what) {
+                case CMD_SET_SCAN_MODE:
+                    if (message.arg1 == SCAN_ONLY_MODE) {
+                        /* Ignore */
+                        return HANDLED;
+                    } else {
+                        WifiNative.setScanResultHandlingCommand(message.arg1);
+                        WifiNative.reconnectCommand();
+                        mIsScanMode = false;
+                        transitionTo(mDisconnectedState);
+                    }
+                    break;
+                case CMD_START_SCAN:
+                    WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+                    break;
+                    /* Ignore */
+                case CMD_DISCONNECT:
+                case CMD_RECONNECT:
+                case CMD_REASSOCIATE:
+                case SUPPLICANT_STATE_CHANGE_EVENT:
+                case NETWORK_CONNECTION_EVENT:
+                case NETWORK_DISCONNECTION_EVENT:
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+    class ConnectModeState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            StateChangeResult stateChangeResult;
+            switch(message.what) {
+                case PASSWORD_MAY_BE_INCORRECT_EVENT:
+                    mPasswordKeyMayBeIncorrect = true;
+                    break;
+                case SUPPLICANT_STATE_CHANGE_EVENT:
+                    stateChangeResult = (StateChangeResult) message.obj;
+                    mSupplicantStateTracker.handleEvent(stateChangeResult);
+                    break;
+                case CMD_START_SCAN:
+                    /* We need to set scan type in completed state */
+                    Message newMsg = obtainMessage();
+                    newMsg.copyFrom(message);
+                    mSupplicantStateTracker.sendMessage(newMsg);
+                    break;
+                    /* Do a redundant disconnect without transition */
+                case CMD_DISCONNECT:
+                    WifiNative.disconnectCommand();
+                    break;
+                case CMD_RECONNECT:
+                    WifiNative.reconnectCommand();
+                    break;
+                case CMD_REASSOCIATE:
+                    WifiNative.reassociateCommand();
+                    break;
+                case CMD_CONNECT_NETWORK:
+                    int netId = message.arg1;
+                    WifiConfiguration config = (WifiConfiguration) message.obj;
+
+                    /* We connect to a specific network by issuing a select
+                     * to the WifiConfigStore. This enables the network,
+                     * while disabling all other networks in the supplicant.
+                     * Disabling a connected network will cause a disconnection
+                     * from the network. A reconnectCommand() will then initiate
+                     * a connection to the enabled network.
+                     */
+                    if (config != null) {
+                        WifiConfigStore.selectNetwork(config);
+                    } else {
+                        WifiConfigStore.selectNetwork(netId);
+                    }
+
+                    /* Save a flag to indicate that we need to enable all
+                     * networks after supplicant indicates a network
+                     * state change event
+                     */
+                    mEnableAllNetworks = true;
+
+                    WifiNative.reconnectCommand();
+
+                    /* Expect a disconnection from the old connection */
+                    transitionTo(mDisconnectingState);
+                    break;
+                case CMD_START_WPS_PBC:
+                    String bssid = (String) message.obj;
+                    /* WPS push button configuration */
+                    boolean success = WifiConfigStore.startWpsPbc(bssid);
+
+                    /* During WPS setup, all other networks are disabled. After
+                     * a successful connect a new config is created in the supplicant.
+                     *
+                     * We need to enable all networks after a successful connection
+                     * or when supplicant goes inactive due to failure. Enabling all
+                     * networks after a disconnect is observed as done with connectNetwork
+                     * does not lead to a successful WPS setup.
+                     *
+                     * Upon success, the configuration list needs to be reloaded
+                     */
+                    if (success) {
+                        mWpsStarted = true;
+                        /* Expect a disconnection from the old connection */
+                        transitionTo(mDisconnectingState);
+                    }
+                    break;
+                case CMD_START_WPS_PIN_FROM_AP:
+                    bssid = (String) message.obj;
+                    int apPin = message.arg1;
+
+                    /* WPS pin from access point */
+                    success = WifiConfigStore.startWpsWithPinFromAccessPoint(bssid, apPin);
+
+                    if (success) {
+                        mWpsStarted = true;
+                        /* Expect a disconnection from the old connection */
+                        transitionTo(mDisconnectingState);
+                    }
+                    break;
+                case CMD_START_WPS_PIN_FROM_DEVICE:
+                    bssid = (String) message.obj;
+                    int pin = WifiConfigStore.startWpsWithPinFromDevice(bssid);
+                    success = (pin != FAILURE);
+                    mReplyChannel.replyToMessage(message, CMD_START_WPS_PIN_FROM_DEVICE, pin);
+
+                    if (success) {
+                        mWpsStarted = true;
+                        /* Expect a disconnection from the old connection */
+                        transitionTo(mDisconnectingState);
+                    }
+                    break;
+                case SCAN_RESULTS_EVENT:
+                    /* Set the scan setting back to "connect" mode */
+                    WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+                    /* Handle scan results */
+                    return NOT_HANDLED;
+                case NETWORK_CONNECTION_EVENT:
+                    Log.d(TAG,"Network connection established");
+                    stateChangeResult = (StateChangeResult) message.obj;
+
+                    //TODO: make supplicant modification to push this in events
+                    mWifiInfo.setSSID(fetchSSID());
+                    mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
+                    mWifiInfo.setNetworkId(stateChangeResult.networkId);
+                    mLastNetworkId = stateChangeResult.networkId;
+                    /* send event to CM & network change broadcast */
+                    setDetailedState(DetailedState.OBTAINING_IPADDR);
+                    sendNetworkStateChangeBroadcast(mLastBssid);
+                    transitionTo(mConnectingState);
+                    break;
+                case NETWORK_DISCONNECTION_EVENT:
+                    Log.d(TAG,"Network connection lost");
+                    handleNetworkDisconnect();
+                    transitionTo(mDisconnectedState);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+    class ConnectingState extends HierarchicalState {
+        boolean mModifiedBluetoothCoexistenceMode;
+        int mPowerMode;
+        boolean mUseStaticIp;
+        Thread mDhcpThread;
+
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+            mUseStaticIp = WifiConfigStore.isUsingStaticIp(mLastNetworkId);
+            if (!mUseStaticIp) {
+                mDhcpThread = null;
+                mModifiedBluetoothCoexistenceMode = false;
+                mPowerMode = POWER_MODE_AUTO;
+
+                if (shouldDisableCoexistenceMode()) {
+                    /*
+                     * There are problems setting the Wi-Fi driver's power
+                     * mode to active when bluetooth coexistence mode is
+                     * enabled or sense.
+                     * <p>
+                     * We set Wi-Fi to active mode when
+                     * obtaining an IP address because we've found
+                     * compatibility issues with some routers with low power
+                     * mode.
+                     * <p>
+                     * In order for this active power mode to properly be set,
+                     * we disable coexistence mode until we're done with
+                     * obtaining an IP address.  One exception is if we
+                     * are currently connected to a headset, since disabling
+                     * coexistence would interrupt that connection.
+                     */
+                    mModifiedBluetoothCoexistenceMode = true;
+
+                    // Disable the coexistence mode
+                    WifiNative.setBluetoothCoexistenceModeCommand(
+                            WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
+                }
+
+                mPowerMode =  WifiNative.getPowerModeCommand();
+                if (mPowerMode < 0) {
+                  // Handle the case where supplicant driver does not support
+                  // getPowerModeCommand.
+                    mPowerMode = POWER_MODE_AUTO;
+                }
+                if (mPowerMode != POWER_MODE_ACTIVE) {
+                    WifiNative.setPowerModeCommand(POWER_MODE_ACTIVE);
+                }
+
+                Log.d(TAG, "DHCP request started");
+                mDhcpThread = new Thread(new Runnable() {
+                    public void run() {
+                        DhcpInfo dhcpInfo = new DhcpInfo();
+                        if (NetworkUtils.runDhcp(mInterfaceName, dhcpInfo)) {
+                            Log.d(TAG, "DHCP request succeeded");
+                            synchronized (mDhcpInfo) {
+                                mDhcpInfo = dhcpInfo;
+                            }
+                            sendMessage(CMD_IP_CONFIG_SUCCESS);
+                        } else {
+                            Log.d(TAG, "DHCP request failed: " +
+                                    NetworkUtils.getDhcpError());
+                            sendMessage(CMD_IP_CONFIG_FAILURE);
+                        }
+                    }
+                });
+                mDhcpThread.start();
+            } else {
+                DhcpInfo dhcpInfo = WifiConfigStore.getIpConfiguration(mLastNetworkId);
+                if (NetworkUtils.configureInterface(mInterfaceName, dhcpInfo)) {
+                    Log.v(TAG, "Static IP configuration succeeded");
+                    synchronized (mDhcpInfo) {
+                        mDhcpInfo = dhcpInfo;
+                    }
+                    sendMessage(CMD_IP_CONFIG_SUCCESS);
+                } else {
+                    Log.v(TAG, "Static IP configuration failed");
+                    sendMessage(CMD_IP_CONFIG_FAILURE);
+                }
+            }
+         }
+      @Override
+      public boolean processMessage(Message message) {
+          if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+
+          switch(message.what) {
+              case CMD_IP_CONFIG_SUCCESS:
+                  mLastSignalLevel = -1; // force update of signal strength
+                  synchronized (mDhcpInfo) {
+                      mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
+                  }
+                  configureLinkProperties();
+                  setDetailedState(DetailedState.CONNECTED);
+                  sendNetworkStateChangeBroadcast(mLastBssid);
+                  //TODO: The framework is not detecting a DHCP renewal and a possible
+                  //IP change. we should detect this and send out a config change broadcast
+                  transitionTo(mConnectedState);
+                  break;
+              case CMD_IP_CONFIG_FAILURE:
+                  mWifiInfo.setIpAddress(0);
+
+                  Log.e(TAG, "IP configuration failed");
+                  /**
+                   * If we've exceeded the maximum number of retries for DHCP
+                   * to a given network, disable the network
+                   */
+                  if (++mReconnectCount > getMaxDhcpRetries()) {
+                      Log.e(TAG, "Failed " +
+                              mReconnectCount + " times, Disabling " + mLastNetworkId);
+                      WifiConfigStore.disableNetwork(mLastNetworkId);
+                      mReconnectCount = 0;
+                  }
+
+                  /* DHCP times out after about 30 seconds, we do a
+                   * disconnect and an immediate reconnect to try again
+                   */
+                  WifiNative.disconnectCommand();
+                  WifiNative.reconnectCommand();
+                  transitionTo(mDisconnectingState);
+                  break;
+              case CMD_DISCONNECT:
+                  WifiNative.disconnectCommand();
+                  transitionTo(mDisconnectingState);
+                  break;
+                  /* Ignore connection to same network */
+              case CMD_CONNECT_NETWORK:
+                  int netId = message.arg1;
+                  if (mWifiInfo.getNetworkId() == netId) {
+                      break;
+                  }
+                  return NOT_HANDLED;
+                  /* Ignore */
+              case NETWORK_CONNECTION_EVENT:
+                  break;
+              case CMD_STOP_DRIVER:
+                  sendMessage(CMD_DISCONNECT);
+                  deferMessage(message);
+                  break;
+              case CMD_SET_SCAN_MODE:
+                  if (message.arg1 == SCAN_ONLY_MODE) {
+                      sendMessage(CMD_DISCONNECT);
+                      deferMessage(message);
+                  }
+                  break;
+              case CMD_RECONFIGURE_IP:
+                  deferMessage(message);
+                  break;
+                  /* Defer any power mode changes since we must keep active power mode at DHCP */
+              case CMD_SET_HIGH_PERF_MODE:
+                  deferMessage(message);
+                  break;
+              default:
+                return NOT_HANDLED;
+          }
+          EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+          return HANDLED;
+      }
+
+      @Override
+      public void exit() {
+          /* reset power state & bluetooth coexistence if on DHCP */
+          if (!mUseStaticIp) {
+              if (mPowerMode != POWER_MODE_ACTIVE) {
+                  WifiNative.setPowerModeCommand(mPowerMode);
+              }
+
+              if (mModifiedBluetoothCoexistenceMode) {
+                  // Set the coexistence mode back to its default value
+                  WifiNative.setBluetoothCoexistenceModeCommand(
+                          WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
+              }
+          }
+
+      }
+    }
+
+    class ConnectedState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            /* A successful WPS connection */
+            if (mWpsStarted) {
+                WifiConfigStore.enableAllNetworks();
+                WifiConfigStore.loadConfiguredNetworks();
+                mWpsStarted = false;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_DISCONNECT:
+                    WifiNative.disconnectCommand();
+                    transitionTo(mDisconnectingState);
+                    break;
+                case CMD_RECONFIGURE_IP:
+                    Log.d(TAG,"Reconfiguring IP on connection");
+                    NetworkUtils.resetConnections(mInterfaceName);
+                    transitionTo(mConnectingState);
+                    break;
+                case CMD_STOP_DRIVER:
+                    sendMessage(CMD_DISCONNECT);
+                    deferMessage(message);
+                    break;
+                case CMD_SET_SCAN_MODE:
+                    if (message.arg1 == SCAN_ONLY_MODE) {
+                        sendMessage(CMD_DISCONNECT);
+                        deferMessage(message);
+                    }
+                    break;
+                    /* Ignore connection to same network */
+                case CMD_CONNECT_NETWORK:
+                    int netId = message.arg1;
+                    if (mWifiInfo.getNetworkId() == netId) {
+                        break;
+                    }
+                    return NOT_HANDLED;
+                    /* Ignore */
+                case NETWORK_CONNECTION_EVENT:
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+    class DisconnectingState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
+                    deferMessage(message);
+                    break;
+                case CMD_SET_SCAN_MODE:
+                    if (message.arg1 == SCAN_ONLY_MODE) {
+                        deferMessage(message);
+                    }
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+        @Override
+        public void exit() {
+            if (mEnableAllNetworks) {
+                mEnableAllNetworks = false;
+                WifiConfigStore.enableAllNetworks();
+            }
+        }
+    }
+
+    class DisconnectedState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+            /**
+             * In a disconnected state, an infrequent scan that wakes
+             * up the device is needed to ensure a user connects to
+             * an access point on the move
+             */
+            long scanMs = Settings.Secure.getLong(mContext.getContentResolver(),
+                    Settings.Secure.WIFI_SCAN_INTERVAL_MS, DEFAULT_SCAN_INTERVAL_MS);
+
+            mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
+                    System.currentTimeMillis() + scanMs, scanMs, mScanIntent);
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_SET_SCAN_MODE:
+                    if (message.arg1 == SCAN_ONLY_MODE) {
+                        WifiNative.setScanResultHandlingCommand(message.arg1);
+                        //Supplicant disconnect to prevent further connects
+                        WifiNative.disconnectCommand();
+                        mIsScanMode = true;
+                        transitionTo(mScanModeState);
+                    }
+                    break;
+                    /* Ignore network disconnect */
+                case NETWORK_DISCONNECTION_EVENT:
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+
+        @Override
+        public void exit() {
+            mAlarmManager.cancel(mScanIntent);
+        }
+    }
+
+    class SoftApStartedState extends HierarchicalState {
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, getName() + "\n");
+            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+        }
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch(message.what) {
+                case CMD_STOP_AP:
+                    Log.d(TAG,"Stopping Soft AP");
+                    setWifiApState(WIFI_AP_STATE_DISABLING);
+                    try {
+                        nwService.stopAccessPoint();
+                    } catch(Exception e) {
+                        Log.e(TAG, "Exception in stopAccessPoint()");
+                    }
+                    transitionTo(mDriverLoadedState);
+                    break;
+                case CMD_START_AP:
+                    Log.d(TAG,"SoftAP set on a running access point");
+                    try {
+                        nwService.setAccessPoint((WifiConfiguration) message.obj,
+                                    mInterfaceName,
+                                    SOFTAP_IFACE);
+                    } catch(Exception e) {
+                        Log.e(TAG, "Exception in nwService during soft AP set");
+                        try {
+                            nwService.stopAccessPoint();
+                        } catch (Exception ee) {
+                            Slog.e(TAG, "Could not stop AP, :" + ee);
+                        }
+                        sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
+                    }
+                    break;
+                /* Fail client mode operation when soft AP is enabled */
+                case CMD_START_SUPPLICANT:
+                    Log.e(TAG,"Cannot start supplicant with a running soft AP");
+                    setWifiState(WIFI_STATE_UNKNOWN);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+            return HANDLED;
+        }
+    }
+
+
+    class SupplicantStateTracker extends HierarchicalStateMachine {
+
+        private int mRssiPollToken = 0;
+
+        /**
+         * The max number of the WPA supplicant loop iterations before we
+         * decide that the loop should be terminated:
+         */
+        private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
+        private int mLoopDetectIndex = 0;
+        private int mLoopDetectCount = 0;
+
+        /**
+         *  Supplicant state change commands follow
+         *  the ordinal values defined in SupplicantState.java
+         */
+        private static final int DISCONNECTED           = 0;
+        private static final int INACTIVE               = 1;
+        private static final int SCANNING               = 2;
+        private static final int ASSOCIATING            = 3;
+        private static final int ASSOCIATED             = 4;
+        private static final int FOUR_WAY_HANDSHAKE     = 5;
+        private static final int GROUP_HANDSHAKE        = 6;
+        private static final int COMPLETED              = 7;
+        private static final int DORMANT                = 8;
+        private static final int UNINITIALIZED          = 9;
+        private static final int INVALID                = 10;
+
+        private HierarchicalState mUninitializedState = new UninitializedState();
+        private HierarchicalState mInitializedState = new InitializedState();;
+        private HierarchicalState mInactiveState = new InactiveState();
+        private HierarchicalState mDisconnectState = new DisconnectedState();
+        private HierarchicalState mScanState = new ScanState();
+        private HierarchicalState mConnectState = new ConnectState();
+        private HierarchicalState mHandshakeState = new HandshakeState();
+        private HierarchicalState mCompletedState = new CompletedState();
+        private HierarchicalState mDormantState = new DormantState();
+
+        public SupplicantStateTracker(Context context, Handler target) {
+            super(TAG, target.getLooper());
+
+            addState(mUninitializedState);
+            addState(mInitializedState);
+                addState(mInactiveState, mInitializedState);
+                addState(mDisconnectState, mInitializedState);
+                addState(mScanState, mInitializedState);
+                addState(mConnectState, mInitializedState);
+                    addState(mHandshakeState, mConnectState);
+                    addState(mCompletedState, mConnectState);
+                addState(mDormantState, mInitializedState);
+
+            setInitialState(mUninitializedState);
+
+            //start the state machine
+            start();
+        }
+
+        public void handleEvent(StateChangeResult stateChangeResult) {
+            SupplicantState newState = (SupplicantState) stateChangeResult.state;
+
+            // Supplicant state change
+            // [31-13] Reserved for future use
+            // [8 - 0] Supplicant state (as defined in SupplicantState.java)
+            // 50023 supplicant_state_changed (custom|1|5)
+            EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal());
+
+            sendMessage(obtainMessage(newState.ordinal(), stateChangeResult));
+        }
+
+        public void resetSupplicantState() {
+            transitionTo(mUninitializedState);
+        }
+
+        private void resetLoopDetection() {
+            mLoopDetectCount = 0;
+            mLoopDetectIndex = 0;
+        }
+
+        private boolean handleTransition(Message msg) {
+            if (DBG) Log.d(TAG, getName() + msg.toString() + "\n");
+            switch (msg.what) {
+                case DISCONNECTED:
+                    transitionTo(mDisconnectState);
+                    break;
+                case SCANNING:
+                    transitionTo(mScanState);
+                    break;
+                case ASSOCIATING:
+                    StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
+                    /* BSSID is valid only in ASSOCIATING state */
+                    mWifiInfo.setBSSID(stateChangeResult.BSSID);
+                    //$FALL-THROUGH$
+                case ASSOCIATED:
+                case FOUR_WAY_HANDSHAKE:
+                case GROUP_HANDSHAKE:
+                    transitionTo(mHandshakeState);
+                    break;
+                case COMPLETED:
+                    transitionTo(mCompletedState);
+                    break;
+                case DORMANT:
+                    transitionTo(mDormantState);
+                    break;
+                case INACTIVE:
+                    transitionTo(mInactiveState);
+                    break;
+                case UNINITIALIZED:
+                case INVALID:
+                    transitionTo(mUninitializedState);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
+            SupplicantState supState = (SupplicantState) stateChangeResult.state;
+            setDetailedState(WifiInfo.getDetailedStateOf(supState));
+            mWifiInfo.setSupplicantState(supState);
+            mWifiInfo.setNetworkId(stateChangeResult.networkId);
+            return HANDLED;
+        }
+
+        /********************************************************
+         * HSM states
+         *******************************************************/
+
+        class InitializedState extends HierarchicalState {
+            @Override
+             public void enter() {
+                 if (DBG) Log.d(TAG, getName() + "\n");
+             }
+            @Override
+            public boolean processMessage(Message message) {
+                if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+                switch (message.what) {
+                    case CMD_START_SCAN:
+                        WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+                        break;
+                    default:
+                        if (DBG) Log.w(TAG, "Ignoring " + message);
+                        break;
+                }
+                return HANDLED;
+            }
+        }
+
+        class UninitializedState extends HierarchicalState {
+            @Override
+             public void enter() {
+                 if (DBG) Log.d(TAG, getName() + "\n");
+                 mNetworkInfo.setIsAvailable(false);
+                 resetLoopDetection();
+                 mPasswordKeyMayBeIncorrect = false;
+             }
+            @Override
+            public boolean processMessage(Message message) {
+                switch(message.what) {
+                    default:
+                        if (!handleTransition(message)) {
+                            if (DBG) Log.w(TAG, "Ignoring " + message);
+                        }
+                        break;
+                }
+                return HANDLED;
+            }
+            @Override
+            public void exit() {
+                mNetworkInfo.setIsAvailable(true);
+            }
+        }
+
+        class InactiveState extends HierarchicalState {
+            @Override
+             public void enter() {
+                 if (DBG) Log.d(TAG, getName() + "\n");
+
+                 /* A failed WPS connection */
+                 if (mWpsStarted) {
+                     Log.e(TAG, "WPS set up failed, enabling other networks");
+                     WifiConfigStore.enableAllNetworks();
+                     mWpsStarted = false;
+                 }
+
+                 Message message = getCurrentMessage();
+                 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+                 mNetworkInfo.setIsAvailable(false);
+                 resetLoopDetection();
+                 mPasswordKeyMayBeIncorrect = false;
+
+                 sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+             }
+            @Override
+            public boolean processMessage(Message message) {
+                return handleTransition(message);
+            }
+            @Override
+            public void exit() {
+                mNetworkInfo.setIsAvailable(true);
+            }
+        }
+
+
+        class DisconnectedState extends HierarchicalState {
+            @Override
+             public void enter() {
+                 if (DBG) Log.d(TAG, getName() + "\n");
+                 Message message = getCurrentMessage();
+                 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+                 resetLoopDetection();
+
+                 /* If a disconnect event happens after a password key failure
+                  * event, disable the network
+                  */
+                 if (mPasswordKeyMayBeIncorrect) {
+                     Log.d(TAG, "Failed to authenticate, disabling network " +
+                             mWifiInfo.getNetworkId());
+                     WifiConfigStore.disableNetwork(mWifiInfo.getNetworkId());
+                     mPasswordKeyMayBeIncorrect = false;
+                     sendSupplicantStateChangedBroadcast(stateChangeResult, true);
+                 }
+                 else {
+                     sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+                 }
+             }
+            @Override
+            public boolean processMessage(Message message) {
+                return handleTransition(message);
+            }
+        }
+
+        class ScanState extends HierarchicalState {
+            @Override
+             public void enter() {
+                 if (DBG) Log.d(TAG, getName() + "\n");
+                 Message message = getCurrentMessage();
+                 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+                 mPasswordKeyMayBeIncorrect = false;
+                 resetLoopDetection();
+                 sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+             }
+            @Override
+            public boolean processMessage(Message message) {
+                return handleTransition(message);
+            }
+        }
+
+        class ConnectState extends HierarchicalState {
+            @Override
+             public void enter() {
+                 if (DBG) Log.d(TAG, getName() + "\n");
+             }
+            @Override
+            public boolean processMessage(Message message) {
+                switch (message.what) {
+                    case CMD_START_SCAN:
+                        WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+                        WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+                        break;
+                    default:
+                        return NOT_HANDLED;
+                }
+                return HANDLED;
+            }
+        }
+
+        class HandshakeState extends HierarchicalState {
+            @Override
+             public void enter() {
+                 if (DBG) Log.d(TAG, getName() + "\n");
+                 final Message message = getCurrentMessage();
+                 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+                 if (mLoopDetectIndex > message.what) {
+                     mLoopDetectCount++;
+                 }
+                 if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
+                     WifiConfigStore.disableNetwork(stateChangeResult.networkId);
+                     mLoopDetectCount = 0;
+                 }
+
+                 mLoopDetectIndex = message.what;
+
+                 mPasswordKeyMayBeIncorrect = false;
+                 sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+             }
+            @Override
+            public boolean processMessage(Message message) {
+                return handleTransition(message);
+            }
+        }
+
+        class CompletedState extends HierarchicalState {
+            @Override
+             public void enter() {
+                 if (DBG) Log.d(TAG, getName() + "\n");
+                 Message message = getCurrentMessage();
+                 StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+                 mRssiPollToken++;
+                 if (mEnableRssiPolling) {
+                     sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+                             POLL_RSSI_INTERVAL_MSECS);
+                 }
+
+                 resetLoopDetection();
+
+                 mPasswordKeyMayBeIncorrect = false;
+                 sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+             }
+            @Override
+            public boolean processMessage(Message message) {
+                switch(message.what) {
+                    case ASSOCIATING:
+                    case ASSOCIATED:
+                    case FOUR_WAY_HANDSHAKE:
+                    case GROUP_HANDSHAKE:
+                    case COMPLETED:
+                        break;
+                    case CMD_RSSI_POLL:
+                        if (message.arg1 == mRssiPollToken) {
+                            // Get Info and continue polling
+                            requestPolledInfo();
+                            sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+                                    POLL_RSSI_INTERVAL_MSECS);
+                        } else {
+                            // Polling has completed
+                        }
+                        break;
+                    case CMD_ENABLE_RSSI_POLL:
+                        mRssiPollToken++;
+                        if (mEnableRssiPolling) {
+                            // first poll
+                            requestPolledInfo();
+                            sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+                                    POLL_RSSI_INTERVAL_MSECS);
+                        }
+                        break;
+                    default:
+                        return handleTransition(message);
+                }
+                return HANDLED;
+            }
+        }
+
+        class DormantState extends HierarchicalState {
+            @Override
+            public void enter() {
+                if (DBG) Log.d(TAG, getName() + "\n");
+                Message message = getCurrentMessage();
+                StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+                resetLoopDetection();
+                mPasswordKeyMayBeIncorrect = false;
+
+                sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+
+                /* TODO: reconnect is now being handled at DHCP failure handling
+                 * If we run into issues with staying in Dormant state, might
+                 * need a reconnect here
+                 */
+            }
+            @Override
+            public boolean processMessage(Message message) {
+                return handleTransition(message);
+            }
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index bcbd07c..f8c9bd6 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,466 +16,110 @@
 
 package android.net.wifi;
 
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
 
-import android.app.ActivityManagerNative;
-import android.net.NetworkInfo;
-import android.net.NetworkStateTracker;
-import android.net.DhcpInfo;
-import android.net.NetworkUtils;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo.State;
-import android.os.Message;
-import android.os.Parcelable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.SystemProperties;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.WorkSource;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Config;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothA2dp;
-import android.content.ContentResolver;
-import android.content.Intent;
+
+import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.database.ContentObserver;
-import com.android.internal.app.IBatteryStats;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.LinkCapabilities;
+import android.net.NetworkInfo;
+import android.net.LinkProperties;
+import android.net.NetworkStateTracker;
+import android.os.Handler;
+import android.os.Message;
 
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * Track the state of Wifi connectivity. All event handling is done here,
- * and all changes in connectivity state are initiated here.
+ * Track the state of wifi for connectivity service.
  *
  * @hide
  */
-public class WifiStateTracker extends NetworkStateTracker {
+public class WifiStateTracker implements NetworkStateTracker {
 
-    private static final boolean LOCAL_LOGD = Config.LOGD || false;
-    
+    private static final String NETWORKTYPE = "WIFI";
     private static final String TAG = "WifiStateTracker";
 
-    // Event log tags (must be in sync with event-log-tags)
-    private static final int EVENTLOG_NETWORK_STATE_CHANGED = 50021;
-    private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50022;
-    private static final int EVENTLOG_DRIVER_STATE_CHANGED = 50023;
-    private static final int EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED = 50024;
-    private static final int EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED = 50025;
+    private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
+    private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
+    private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
+    private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
 
-    // Event codes
-    private static final int EVENT_SUPPLICANT_CONNECTION             = 1;
-    private static final int EVENT_SUPPLICANT_DISCONNECT             = 2;
-    private static final int EVENT_SUPPLICANT_STATE_CHANGED          = 3;
-    private static final int EVENT_NETWORK_STATE_CHANGED             = 4;
-    private static final int EVENT_SCAN_RESULTS_AVAILABLE            = 5;
-    private static final int EVENT_INTERFACE_CONFIGURATION_SUCCEEDED = 6;
-    private static final int EVENT_INTERFACE_CONFIGURATION_FAILED    = 7;
-    private static final int EVENT_POLL_INTERVAL                     = 8;
-    private static final int EVENT_DHCP_START                        = 9;
-    private static final int EVENT_DEFERRED_DISCONNECT               = 10;
-    private static final int EVENT_DEFERRED_RECONNECT                = 11;
-    /**
-     * The driver is started or stopped. The object will be the state: true for
-     * started, false for stopped.
-     */
-    private static final int EVENT_DRIVER_STATE_CHANGED              = 12;
-    private static final int EVENT_PASSWORD_KEY_MAY_BE_INCORRECT     = 13;
-    private static final int EVENT_MAYBE_START_SCAN_POST_DISCONNECT  = 14;
+    private LinkProperties mLinkProperties;
+    private LinkCapabilities mLinkCapabilities;
+    private NetworkInfo mNetworkInfo;
 
-    /**
-     * The driver state indication.
-     */
-    private static final int DRIVER_STARTED                          = 0;
-    private static final int DRIVER_STOPPED                          = 1;
-    private static final int DRIVER_HUNG                             = 2;
+    /* For sending events to connectivity service handler */
+    private Handler mCsHandler;
+    private Context mContext;
+    private BroadcastReceiver mWifiStateReceiver;
+    private WifiManager mWifiManager;
 
-    /**
-     * Interval in milliseconds between polling for connection
-     * status items that are not sent via asynchronous events.
-     * An example is RSSI (signal strength).
-     */
-    private static final int POLL_STATUS_INTERVAL_MSECS = 3000;
+    public WifiStateTracker() {
+        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
+        mLinkProperties = new LinkProperties();
+        mLinkCapabilities = new LinkCapabilities();
 
-    /**
-     * The max number of the WPA supplicant loop iterations before we
-     * decide that the loop should be terminated:
-     */
-    private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
+        mNetworkInfo.setIsAvailable(false);
+        setTeardownRequested(false);
+    }
 
-    /**
-     * When a DISCONNECT event is received, we defer handling it to
-     * allow for the possibility that the DISCONNECT is about to
-     * be followed shortly by a CONNECT to the same network we were
-     * just connected to. In such a case, we don't want to report
-     * the network as down, nor do we want to reconfigure the network
-     * interface, etc. If we get a CONNECT event for another network
-     * within the delay window, we immediately handle the pending
-     * disconnect before processing the CONNECT.<p/>
-     * The five second delay is chosen somewhat arbitrarily, but is
-     * meant to cover most of the cases where a DISCONNECT/CONNECT
-     * happens to a network.
-     */
-    private static final int DISCONNECT_DELAY_MSECS = 5000;
-    /**
-     * When the supplicant goes idle after we do an explicit disconnect
-     * following a DHCP failure, we need to kick the supplicant into
-     * trying to associate with access points.
-     */
-    private static final int RECONNECT_DELAY_MSECS = 2000;
 
-    /**
-     * When the supplicant disconnects from an AP it sometimes forgets
-     * to restart scanning.  Wait this delay before asking it to start
-     * scanning (in case it forgot).  15 sec is the standard delay between
-     * scans.
-     */
-    private static final int KICKSTART_SCANNING_DELAY_MSECS = 15000;
+    public void setTeardownRequested(boolean isRequested) {
+        mTeardownRequested.set(isRequested);
+    }
 
-    /**
-     * The maximum number of times we will retry a connection to an access point
-     * for which we have failed in acquiring an IP address from DHCP. A value of
-     * N means that we will make N+1 connection attempts in all.
-     * <p>
-     * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
-     * value if a Settings value is not present.
-     */
-    private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
-
-    private static final int DRIVER_POWER_MODE_AUTO = 0;
-    private static final int DRIVER_POWER_MODE_ACTIVE = 1;
-
-    /**
-     * The current WPA supplicant loop state (used to detect looping behavior):
-     */
-    private SupplicantState mSupplicantLoopState = SupplicantState.DISCONNECTED;
-
-    /**
-     * The current number of WPA supplicant loop iterations:
-     */
-    private int mNumSupplicantLoopIterations = 0;
-
-    /**
-     * The current number of supplicant state changes.  This is used to determine
-     * if we've received any new info since we found out it was DISCONNECTED or
-     * INACTIVE.  If we haven't for X ms, we then request a scan - it should have
-     * done that automatically, but sometimes some firmware does not.
-     */
-    private int mNumSupplicantStateChanges = 0;
-
-    /**
-     * True if we received an event that that a password-key may be incorrect.
-     * If the next incoming supplicant state change event is DISCONNECT,
-     * broadcast a message that we have a possible password error and disable
-     * the network.
-     */
-    private boolean mPasswordKeyMayBeIncorrect = false;
-
-    public static final int SUPPL_SCAN_HANDLING_NORMAL = 1;
-    public static final int SUPPL_SCAN_HANDLING_LIST_ONLY = 2;
-
-    private WifiMonitor mWifiMonitor;
-    private WifiInfo mWifiInfo;
-    private List<ScanResult> mScanResults;
-    private WifiManager mWM;
-    private boolean mHaveIpAddress;
-    private boolean mObtainingIpAddress;
-    private boolean mTornDownByConnMgr;
-    /**
-     * A DISCONNECT event has been received, but processing it
-     * is being deferred.
-     */
-    private boolean mDisconnectPending;
-    /**
-     * An operation has been performed as a result of which we expect the next event
-     * will be a DISCONNECT.
-     */
-    private boolean mDisconnectExpected;
-    private DhcpHandler mDhcpTarget;
-    private DhcpInfo mDhcpInfo;
-    private int mLastSignalLevel = -1;
-    private String mLastBssid;
-    private String mLastSsid;
-    private int mLastNetworkId = -1;
-    private boolean mUseStaticIp = false;
-    private int mReconnectCount;
-
-    /* Tracks if any network in the configuration is disabled */
-    private AtomicBoolean mIsAnyNetworkDisabled = new AtomicBoolean(false);
-
-    // used to store the (non-persisted) num determined during device boot 
-    // (from mcc or other phone info) before the driver is started.
-    private int mNumAllowedChannels = 0;
-
-    // Variables relating to the 'available networks' notification
-    
-    /**
-     * The icon to show in the 'available networks' notification. This will also
-     * be the ID of the Notification given to the NotificationManager.
-     */
-    private static final int ICON_NETWORKS_AVAILABLE =
-            com.android.internal.R.drawable.stat_notify_wifi_in_range;
-    /**
-     * When a notification is shown, we wait this amount before possibly showing it again.
-     */
-    private final long NOTIFICATION_REPEAT_DELAY_MS;
-    /**
-     * Whether the user has set the setting to show the 'available networks' notification.
-     */
-    private boolean mNotificationEnabled;
-    /**
-     * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
-     */
-    private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
-    /**
-     * The {@link System#currentTimeMillis()} must be at least this value for us
-     * to show the notification again.
-     */
-    private long mNotificationRepeatTime;
-    /**
-     * The Notification object given to the NotificationManager.
-     */
-    private Notification mNotification;
-    /**
-     * Whether the notification is being shown, as set by us. That is, if the
-     * user cancels the notification, we will not receive the callback so this
-     * will still be true. We only guarantee if this is false, then the
-     * notification is not showing.
-     */
-    private boolean mNotificationShown;
-    /**
-     * The number of continuous scans that must occur before consider the
-     * supplicant in a scanning state. This allows supplicant to associate with
-     * remembered networks that are in the scan results.
-     */
-    private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
-    /**
-     * The number of scans since the last network state change. When this
-     * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
-     * supplicant to actually be scanning. When the network state changes to
-     * something other than scanning, we reset this to 0.
-     */
-    private int mNumScansSinceNetworkStateChange;
-    /**
-     * Observes the static IP address settings.
-     */
-    private SettingsObserver mSettingsObserver;
-    
-    private boolean mIsScanModeActive;
-    private boolean mEnableRssiPolling;
-    private boolean mIsHighPerfEnabled;
-    private int mPowerModeRefCount = 0;
-    private int mOptimizationsDisabledRefCount = 0;
-
-    /**
-     * One of  {@link WifiManager#WIFI_STATE_DISABLED},
-     *         {@link WifiManager#WIFI_STATE_DISABLING},
-     *         {@link WifiManager#WIFI_STATE_ENABLED},
-     *         {@link WifiManager#WIFI_STATE_ENABLING},
-     *         {@link WifiManager#WIFI_STATE_UNKNOWN}
-     *
-     * getWifiState() is not synchronized to make sure it's always fast,
-     * even when the instance lock is held on other slow operations.
-     * Use a atomic variable for state.
-     */
-    private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_UNKNOWN);
-
-    // Wi-Fi run states:
-    private static final int RUN_STATE_STARTING = 1;
-    private static final int RUN_STATE_RUNNING  = 2;
-    private static final int RUN_STATE_STOPPING = 3;
-    private static final int RUN_STATE_STOPPED  = 4;
-
-    private static final String mRunStateNames[] = {
-            "Starting",
-            "Running",
-            "Stopping",
-            "Stopped"
-    };
-    private int mRunState;
-
-    private final IBatteryStats mBatteryStats;
-
-    private boolean mIsScanOnly;
-
-    private BluetoothA2dp mBluetoothA2dp;
-
-    private String mInterfaceName;
-    private static String LS = System.getProperty("line.separator");
-
-    private static String[] sDnsPropNames;
-    private Runnable mReleaseWakeLockCallback;
-
-    /**
-     * Keep track of whether we last told the battery stats we had started.
-     */
-    private boolean mReportedRunning = false;
-
-    /**
-     * Most recently set source of starting WIFI.
-     */
-    private final WorkSource mRunningWifiUids = new WorkSource();
-
-    /**
-     * The last reported UIDs that were responsible for starting WIFI.
-     */
-    private final WorkSource mLastRunningWifiUids = new WorkSource();
-
-    /**
-     * A structure for supplying information about a supplicant state
-     * change in the STATE_CHANGE event message that comes from the
-     * WifiMonitor
-     * thread.
-     */
-    private static class SupplicantStateChangeResult {
-        SupplicantStateChangeResult(int networkId, String BSSID, SupplicantState state) {
-            this.state = state;
-            this.BSSID = BSSID;
-            this.networkId = networkId;
-        }
-        int networkId;
-        String BSSID;
-        SupplicantState state;
+    public boolean isTeardownRequested() {
+        return mTeardownRequested.get();
     }
 
     /**
-     * A structure for supplying information about a connection in
-     * the CONNECTED event message that comes from the WifiMonitor
-     * thread.
+     * Begin monitoring wifi connectivity
      */
-    private static class NetworkStateChangeResult {
-        NetworkStateChangeResult(DetailedState state, String BSSID, int networkId) {
-            this.state = state;
-            this.BSSID = BSSID;
-            this.networkId = networkId;
-        }
-        DetailedState state;
-        String BSSID;
-        int networkId;
-    }
+    public void startMonitoring(Context context, Handler target) {
+        mCsHandler = target;
+        mContext = context;
 
-    public WifiStateTracker(Context context, Handler target) {
-        super(context, target, ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
-        
-        mWifiInfo = new WifiInfo();
-        mWifiMonitor = new WifiMonitor(this);
-        mHaveIpAddress = false;
-        mObtainingIpAddress = false;
-        setTornDownByConnMgr(false);
-        mDisconnectPending = false;
-        mScanResults = new ArrayList<ScanResult>();
-        // Allocate DHCP info object once, and fill it in on each request
-        mDhcpInfo = new DhcpInfo();
-        mRunState = RUN_STATE_STARTING;
+        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.CONFIG_CHANGED_ACTION);
 
-        // Setting is in seconds
-        NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(), 
-                Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
-        mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
-        mNotificationEnabledSettingObserver.register();
-
-        mSettingsObserver = new SettingsObserver(new Handler());
-
-        mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
-        sDnsPropNames = new String[] {
-            "dhcp." + mInterfaceName + ".dns1",
-            "dhcp." + mInterfaceName + ".dns2"
-        };
-        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
-
+        mWifiStateReceiver = new WifiStateReceiver();
+        mContext.registerReceiver(mWifiStateReceiver, filter);
     }
 
     /**
-     * Helper method: sets the supplicant state and keeps the network
-     * info updated.
-     * @param state the new state
+     * Disable connectivity to a network
+     * TODO: do away with return value after making MobileDataStateTracker async
      */
-    private void setSupplicantState(SupplicantState state) {
-        mWifiInfo.setSupplicantState(state);
-        updateNetworkInfo();
-        checkPollTimer();
-    }
-
-    public SupplicantState getSupplicantState() {
-        return mWifiInfo.getSupplicantState();
+    public boolean teardown() {
+        mTeardownRequested.set(true);
+        mWifiManager.stopWifi();
+        return true;
     }
 
     /**
-     * Helper method: sets the supplicant state and keeps the network
-     * info updated (string version).
-     * @param stateName the string name of the new state
+     * Re-enable connectivity to a network after a {@link #teardown()}.
+     * TODO: do away with return value after making MobileDataStateTracker async
      */
-    private void setSupplicantState(String stateName) {
-        mWifiInfo.setSupplicantState(stateName);
-        updateNetworkInfo();
-        checkPollTimer();
+    public boolean reconnect() {
+        mTeardownRequested.set(false);
+        mWifiManager.startWifi();
+        return true;
     }
 
     /**
-     * Helper method: sets the boolean indicating that the connection
-     * manager asked the network to be torn down (and so only the connection
-     * manager can set it up again).
-     * network info updated.
-     * @param flag {@code true} if explicitly disabled.
+     * Turn the wireless radio off for a network.
+     * @param turnOn {@code true} to turn the radio on, {@code false}
+     * TODO: do away with return value after making MobileDataStateTracker async
      */
-    private void setTornDownByConnMgr(boolean flag) {
-        mTornDownByConnMgr = flag;
-        updateNetworkInfo();
-    }
-
-    /**
-     * Return the IP addresses of the DNS servers available for the WLAN
-     * network interface.
-     * @return a list of DNS addresses, with no holes.
-     */
-    public String[] getNameServers() {
-        return getNameServerList(sDnsPropNames);
-    }
-
-    /**
-     * Return the name of our WLAN network interface.
-     * @return the name of our interface.
-     */
-    public String getInterfaceName() {
-        return mInterfaceName;
-    }
-
-    /**
-     * Return the system properties name associated with the tcp buffer sizes
-     * for this network.
-     */
-    public String getTcpBufferSizesPropName() {
-        return "net.tcp.buffersize.wifi";
-    }
-
-    public void startMonitoring() {
-        /*
-         * Get a handle on the WifiManager. This cannot be done in our
-         * constructor, because the Wifi service is not yet registered.
-         */
-        mWM = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
-    }
-
-    public void startEventLoop() {
-        mWifiMonitor.startMonitoring();
+    public boolean setRadio(boolean turnOn) {
+        mWifiManager.setWifiEnabled(turnOn);
+        return true;
     }
 
     /**
@@ -487,2187 +131,136 @@
      * unavailable.
      * @return {@code true} if Wi-Fi connections are possible
      */
-    public synchronized boolean isAvailable() {
-        /*
-         * TODO: Need to also look at scan results to see whether we're
-         * in range of any access points. If we have scan results that
-         * are no more than N seconds old, use those, otherwise, initiate
-         * a scan and wait for the results. This only matters if we
-         * allow mobile to be the preferred network.
-         */
-        SupplicantState suppState = mWifiInfo.getSupplicantState();
-        return suppState != SupplicantState.UNINITIALIZED &&
-                suppState != SupplicantState.INACTIVE &&
-                (mTornDownByConnMgr || !isDriverStopped());
+    public boolean isAvailable() {
+        return mNetworkInfo.isAvailable();
     }
 
     /**
-     * {@inheritDoc}
-     * There are currently no defined Wi-Fi subtypes.
-     */
-    public int getNetworkSubtype() {
-        return 0;
-    }
-
-    /**
-     * Helper method: updates the network info object to keep it in sync with
-     * the Wi-Fi state tracker.
-     */
-    private void updateNetworkInfo() {
-        mNetworkInfo.setIsAvailable(isAvailable());
-    }
-
-    /**
-     * Report whether the Wi-Fi connection is fully configured for data.
-     * @return {@code true} if the {@link SupplicantState} is
-     * {@link android.net.wifi.SupplicantState#COMPLETED COMPLETED}.
-     */
-    public boolean isConnectionCompleted() {
-        return mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED;
-    }
-
-    /**
-     * Report whether the Wi-Fi connection has successfully acquired an IP address.
-     * @return {@code true} if the Wi-Fi connection has been assigned an IP address.
-     */
-    public boolean hasIpAddress() {
-        return mHaveIpAddress;
-    }
-
-    /**
-     * Send the tracker a notification that a user-entered password key
-     * may be incorrect (i.e., caused authentication to fail).
-     */
-    void notifyPasswordKeyMayBeIncorrect() {
-        sendEmptyMessage(EVENT_PASSWORD_KEY_MAY_BE_INCORRECT);
-    }
-
-    /**
-     * Send the tracker a notification that a connection to the supplicant
-     * daemon has been established.
-     */
-    void notifySupplicantConnection() {
-        sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION);
-    }
-
-    /**
-     * Send the tracker a notification that the state of the supplicant
-     * has changed.
-     * @param networkId the configured network on which the state change occurred
-     * @param newState the new {@code SupplicantState}
-     */
-    void notifyStateChange(int networkId, String BSSID, SupplicantState newState) {
-        Message msg = Message.obtain(
-            this, EVENT_SUPPLICANT_STATE_CHANGED,
-            new SupplicantStateChangeResult(networkId, BSSID, newState));
-        msg.sendToTarget();
-    }
-
-    /**
-     * Send the tracker a notification that the state of Wifi connectivity
-     * has changed.
-     * @param networkId the configured network on which the state change occurred
-     * @param newState the new network state
-     * @param BSSID when the new state is {@link DetailedState#CONNECTED
-     * NetworkInfo.DetailedState.CONNECTED},
-     * this is the MAC address of the access point. Otherwise, it
-     * is {@code null}.
-     */
-    void notifyStateChange(DetailedState newState, String BSSID, int networkId) {
-        Message msg = Message.obtain(
-            this, EVENT_NETWORK_STATE_CHANGED,
-            new NetworkStateChangeResult(newState, BSSID, networkId));
-        msg.sendToTarget();
-    }
-
-    /**
-     * Send the tracker a notification that a scan has completed, and results
-     * are available.
-     */
-    void notifyScanResultsAvailable() {
-        // reset the supplicant's handling of scan results to "normal" mode
-        setScanResultHandling(SUPPL_SCAN_HANDLING_NORMAL);
-        sendEmptyMessage(EVENT_SCAN_RESULTS_AVAILABLE);
-    }
-
-    /**
-     * Send the tracker a notification that we can no longer communicate with
-     * the supplicant daemon.
-     */
-    void notifySupplicantLost() {
-        sendEmptyMessage(EVENT_SUPPLICANT_DISCONNECT);
-    }
-
-    /**
-     * Send the tracker a notification that the Wi-Fi driver has been stopped.
-     */
-    void notifyDriverStopped() {
-        mRunState = RUN_STATE_STOPPED;
-
-        // Send a driver stopped message to our handler
-        Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STOPPED, 0).sendToTarget();
-    }
-
-    /**
-     * Send the tracker a notification that the Wi-Fi driver has been restarted after
-     * having been stopped.
-     */
-    void notifyDriverStarted() {
-        // Send a driver started message to our handler
-        Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STARTED, 0).sendToTarget();
-    }
-
-    /**
-     * Send the tracker a notification that the Wi-Fi driver has hung and needs restarting.
-     */
-    void notifyDriverHung() {
-        // Send a driver hanged message to our handler
-        Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_HUNG, 0).sendToTarget();
-    }
-
-    /**
-     * Set the interval timer for polling connection information
-     * that is not delivered asynchronously.
-     */
-    private synchronized void checkPollTimer() {
-        if (mEnableRssiPolling &&
-                mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED &&
-                !hasMessages(EVENT_POLL_INTERVAL)) {
-            sendEmptyMessageDelayed(EVENT_POLL_INTERVAL, POLL_STATUS_INTERVAL_MSECS);
-        }
-    }
-
-    /**
-     * TODO: mRunState is not synchronized in some places
-     * address this as part of re-architect.
-     *
-     * TODO: We are exposing an additional public synchronized call
-     * for a wakelock optimization in WifiService. Remove it
-     * when we handle the wakelock in ConnectivityService.
-     */
-    public synchronized boolean isDriverStopped() {
-        return mRunState == RUN_STATE_STOPPED || mRunState == RUN_STATE_STOPPING;
-    }
-
-    public void updateBatteryWorkSourceLocked(WorkSource newSource) {
-        try {
-            if (newSource != null) {
-                mRunningWifiUids.set(newSource);
-            }
-            if (mRunState == RUN_STATE_RUNNING) {
-                if (mReportedRunning) {
-                    // If the work source has changed since last time, need
-                    // to remove old work from battery stats.
-                    if (mLastRunningWifiUids.diff(mRunningWifiUids)) {
-                        mBatteryStats.noteWifiRunningChanged(mLastRunningWifiUids,
-                                mRunningWifiUids);
-                        mLastRunningWifiUids.set(mRunningWifiUids);
-                    }
-                } else {
-                    // Now being started, report it.
-                    mBatteryStats.noteWifiRunning(mRunningWifiUids);
-                    mLastRunningWifiUids.set(mRunningWifiUids);
-                    mReportedRunning = true;
-                }
-            } else if (mRunState == RUN_STATE_STOPPED) {
-                if (mReportedRunning) {
-                    // Last reported we were running, time to stop.
-                    mBatteryStats.noteWifiStopped(mLastRunningWifiUids);
-                    mLastRunningWifiUids.clear();
-                    mReportedRunning = false;
-                }
-            } else {
-                // State in transition -- nothing to update yet.
-            }
-        } catch (RemoteException ignore) {
-        }
-    }
-
-    /**
-     * Set the run state to either "normal" or "scan-only".
-     * @param scanOnlyMode true if the new mode should be scan-only.
-     */
-    public synchronized void setScanOnlyMode(boolean scanOnlyMode) {
-        // do nothing unless scan-only mode is changing
-        if (mIsScanOnly != scanOnlyMode) {
-            int scanType = (scanOnlyMode ?
-                    SUPPL_SCAN_HANDLING_LIST_ONLY : SUPPL_SCAN_HANDLING_NORMAL);
-            if (LOCAL_LOGD) Log.v(TAG, "Scan-only mode changing to " + scanOnlyMode + " scanType=" + scanType);
-            if (setScanResultHandling(scanType)) {
-                mIsScanOnly = scanOnlyMode;
-                if (!isDriverStopped()) {
-                    if (scanOnlyMode) {
-                        disconnect();
-                    } else {
-                        reconnectCommand();
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Set suspend mode optimizations. These include:
-     * - packet filtering
-     * - turn off roaming
-     * - DTIM settings
-     *
-     * Uses reference counting to keep the suspend optimizations disabled
-     * as long as one entity wants optimizations disabled.
-     *
-     * For example, WifiLock can keep suspend optimizations disabled
-     * or the user setting (wifi never sleeps) can keep suspend optimizations
-     * disabled. As long as one entity wants it disabled, it should stay
-     * that way
-     *
-     * @param enabled true if optimizations need enabled, false otherwise
-     */
-    public synchronized void setSuspendModeOptimizations(boolean enabled) {
-
-        /* It is good to plumb suspend optimization enable
-         * or disable even if ref count indicates already done
-         * since we could have a case of previous failure.
-         */
-        if (!enabled) {
-            mOptimizationsDisabledRefCount++;
-        } else {
-            mOptimizationsDisabledRefCount--;
-            if (mOptimizationsDisabledRefCount > 0) {
-                return;
-            } else {
-                /* Keep refcount from becoming negative */
-                mOptimizationsDisabledRefCount = 0;
-            }
-        }
-
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return;
-        }
-
-        WifiNative.setSuspendOptimizationsCommand(enabled);
-    }
-
-
-    /**
-     * Set high performance mode of operation. This would mean
-     * use active power mode and disable suspend optimizations
-     * @param enabled true if enabled, false otherwise
-     */
-    public synchronized void setHighPerfMode(boolean enabled) {
-        if (mIsHighPerfEnabled != enabled) {
-            if (enabled) {
-                setPowerMode(DRIVER_POWER_MODE_ACTIVE);
-                setSuspendModeOptimizations(false);
-            } else {
-                setPowerMode(DRIVER_POWER_MODE_AUTO);
-                setSuspendModeOptimizations(true);
-            }
-            mIsHighPerfEnabled = enabled;
-            Log.d(TAG,"high performance mode: " + enabled);
-        }
-    }
-
-
-    private void checkIsBluetoothPlaying() {
-        boolean isBluetoothPlaying = false;
-        Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
-
-        for (BluetoothDevice device : connected) {
-            if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
-                isBluetoothPlaying = true;
-                break;
-            }
-        }
-        setBluetoothScanMode(isBluetoothPlaying);
-    }
-
-    public void enableRssiPolling(boolean enable) {
-        if (mEnableRssiPolling != enable) {
-            mEnableRssiPolling = enable;
-            checkPollTimer();
-        }
-    }
-
-    /**
-     * We release the wakelock in WifiService
-     * using a timer.
-     *
-     * TODO:
-     * Releasing wakelock using both timer and
-     * a call from ConnectivityService requires
-     * a rethink. We had problems where WifiService
-     * could keep a wakelock forever if we delete
-     * messages in the asynchronous call
-     * from ConnectivityService
-     */
-    @Override
-    public void releaseWakeLock() {
-    }
-
-    /**
-     * Tracks the WPA supplicant states to detect "loop" situations.
-     * @param newSupplicantState The new WPA supplicant state.
-     * @return {@code true} if the supplicant loop should be stopped
-     * and {@code false} if it should continue.
-     */
-    private boolean isSupplicantLooping(SupplicantState newSupplicantState) {
-        if (SupplicantState.ASSOCIATING.ordinal() <= newSupplicantState.ordinal()
-            && newSupplicantState.ordinal() < SupplicantState.COMPLETED.ordinal()) {
-            if (mSupplicantLoopState != newSupplicantState) {
-                if (newSupplicantState.ordinal() < mSupplicantLoopState.ordinal()) {
-                    ++mNumSupplicantLoopIterations;
-                }
-
-                mSupplicantLoopState = newSupplicantState;
-            }
-        } else if (newSupplicantState == SupplicantState.COMPLETED) {
-            resetSupplicantLoopState();
-        }
-
-        return mNumSupplicantLoopIterations >= MAX_SUPPLICANT_LOOP_ITERATIONS;
-    }
-
-    /**
-     * Resets the WPA supplicant loop state.
-     */
-    private void resetSupplicantLoopState() {
-        mNumSupplicantLoopIterations = 0;
-    }
-
-    @Override
-    public void handleMessage(Message msg) {
-        Intent intent;
-
-        switch (msg.what) {
-            case EVENT_SUPPLICANT_CONNECTION:
-                mRunState = RUN_STATE_RUNNING;
-                String macaddr;
-                synchronized (this) {
-                    updateBatteryWorkSourceLocked(null);
-                    macaddr = WifiNative.getMacAddressCommand();
-                }
-                if (macaddr != null) {
-                    mWifiInfo.setMacAddress(macaddr);
-                }
-
-                checkUseStaticIp();
-                /* Reset notification state on new connection */
-                resetNotificationTimer();
-                /*
-                 * DHCP requests are blocking, so run them in a separate thread.
-                 */
-                HandlerThread dhcpThread = new HandlerThread("DHCP Handler Thread");
-                dhcpThread.start();
-                mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this);
-                mIsScanModeActive = true;
-                mIsHighPerfEnabled = false;
-                mOptimizationsDisabledRefCount = 0;
-                mPowerModeRefCount = 0;
-                mTornDownByConnMgr = false;
-                mLastBssid = null;
-                mLastSsid = null;
-                mIsAnyNetworkDisabled.set(false);
-                requestConnectionInfo();
-                SupplicantState supplState = mWifiInfo.getSupplicantState();
-
-                if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant established, state=" +
-                    supplState);
-                // Wi-Fi supplicant connection state changed:
-                // [31- 2] Reserved for future use
-                // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) ,
-                //         or supplicant died (2)
-                EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, 1);
-                /*
-                 * The COMPLETED state change from the supplicant may have occurred
-                 * in between polling for supplicant availability, in which case
-                 * we didn't perform a DHCP request to get an IP address.
-                 */
-                if (supplState == SupplicantState.COMPLETED) {
-                    mLastBssid = mWifiInfo.getBSSID();
-                    mLastSsid = mWifiInfo.getSSID();
-                    configureInterface();
-                }
-                if (ActivityManagerNative.isSystemReady()) {
-                    intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
-                    intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, true);
-                    mContext.sendBroadcast(intent);
-                }
-                if (supplState == SupplicantState.COMPLETED && mHaveIpAddress) {
-                    setDetailedState(DetailedState.CONNECTED);
-                } else {
-                    setDetailedState(WifiInfo.getDetailedStateOf(supplState));
-                }
-                /*
-                 * Filter out multicast packets. This saves battery power, since
-                 * the CPU doesn't have to spend time processing packets that
-                 * are going to end up being thrown away.
-                 */
-                mWM.initializeMulticastFiltering();
-
-                if (mBluetoothA2dp == null) {
-                    mBluetoothA2dp = new BluetoothA2dp(mContext);
-                }
-                checkIsBluetoothPlaying();
-
-                // initialize this after the supplicant is alive
-                setNumAllowedChannels();
-                break;
-
-            case EVENT_SUPPLICANT_DISCONNECT:
-                mRunState = RUN_STATE_STOPPED;
-                synchronized (this) {
-                    updateBatteryWorkSourceLocked(null);
-                }
-                boolean died = mWifiState.get() != WIFI_STATE_DISABLED &&
-                               mWifiState.get() != WIFI_STATE_DISABLING;
-                if (died) {
-                    if (LOCAL_LOGD) Log.v(TAG, "Supplicant died unexpectedly");
-                } else {
-                    if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant lost");
-                }
-                // Wi-Fi supplicant connection state changed:
-                // [31- 2] Reserved for future use
-                // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) ,
-                //         or supplicant died (2)
-                EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, died ? 2 : 0);
-                closeSupplicantConnection();
-
-                if (died) {
-                    resetConnections(true);
-                }
-                // When supplicant dies, kill the DHCP thread
-                if (mDhcpTarget != null) {
-                    mDhcpTarget.getLooper().quit();
-                    mDhcpTarget = null;
-                }
-                mContext.removeStickyBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
-                if (ActivityManagerNative.isSystemReady()) {
-                    intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
-                    intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false);
-                    mContext.sendBroadcast(intent);
-                }
-                setDetailedState(DetailedState.DISCONNECTED);
-                setSupplicantState(SupplicantState.UNINITIALIZED);
-                mHaveIpAddress = false;
-                mObtainingIpAddress = false;
-                if (died) {
-                    mWM.setWifiEnabled(false);
-                }
-                break;
-
-            case EVENT_MAYBE_START_SCAN_POST_DISCONNECT:
-                // Only do this if we haven't gotten a new supplicant status since the timer
-                // started
-                if (mNumSupplicantStateChanges == msg.arg1) {
-                    scan(false); // do a passive scan
-                }
-                break;
-
-            case EVENT_SUPPLICANT_STATE_CHANGED:
-                mNumSupplicantStateChanges++;
-                SupplicantStateChangeResult supplicantStateResult =
-                    (SupplicantStateChangeResult) msg.obj;
-                SupplicantState newState = supplicantStateResult.state;
-                SupplicantState currentState = mWifiInfo.getSupplicantState();
-
-                // Wi-Fi supplicant state changed:
-                // [31- 6] Reserved for future use
-                // [ 5- 0] Supplicant state ordinal (as defined by SupplicantState)
-                int eventLogParam = (newState.ordinal() & 0x3f);
-                EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, eventLogParam);
-
-                if (LOCAL_LOGD) Log.v(TAG, "Changing supplicant state: "
-                                      + currentState +
-                                      " ==> " + newState);
-
-                int networkId = supplicantStateResult.networkId;
-
-                /**
-                 * The SupplicantState BSSID value is valid in ASSOCIATING state only.
-                 * The NetworkState BSSID value comes upon a successful connection.
-                 */
-                if (supplicantStateResult.state == SupplicantState.ASSOCIATING) {
-                    mLastBssid = supplicantStateResult.BSSID;
-                }
-                /*
-                 * If we get disconnect or inactive we need to start our
-                 * watchdog timer to start a scan
-                 */
-                if (newState == SupplicantState.DISCONNECTED ||
-                        newState == SupplicantState.INACTIVE) {
-                    sendMessageDelayed(obtainMessage(EVENT_MAYBE_START_SCAN_POST_DISCONNECT,
-                            mNumSupplicantStateChanges, 0), KICKSTART_SCANNING_DELAY_MSECS);
-                }
-
-
-                /*
-                 * Did we get to DISCONNECTED state due to an
-                 * authentication (password) failure?
-                 */
-                boolean failedToAuthenticate = false;
-                if (newState == SupplicantState.DISCONNECTED) {
-                    failedToAuthenticate = mPasswordKeyMayBeIncorrect;
-                }
-                mPasswordKeyMayBeIncorrect = false;
-
-                /*
-                 * Keep track of the supplicant state and check if we should
-                 * disable the network
-                 */
-                boolean disabledNetwork = false;
-                if (isSupplicantLooping(newState)) {
-                    if (LOCAL_LOGD) {
-                        Log.v(TAG,
-                              "Stop WPA supplicant loop and disable network");
-                    }
-                    disabledNetwork = wifiManagerDisableNetwork(networkId);
-                }
-
-                if (disabledNetwork) {
-                    /*
-                     * Reset the loop state if we disabled the network
-                     */
-                    resetSupplicantLoopState();
-                } else if (newState != currentState ||
-                        (newState == SupplicantState.DISCONNECTED && isDriverStopped())) {
-                    setSupplicantState(newState);
-                    if (newState == SupplicantState.DORMANT) {
-                        DetailedState newDetailedState;
-                        Message reconnectMsg = obtainMessage(EVENT_DEFERRED_RECONNECT, mLastBssid);
-                        if (mIsScanOnly || mRunState == RUN_STATE_STOPPING) {
-                            newDetailedState = DetailedState.IDLE;
-                        } else {
-                            newDetailedState = DetailedState.FAILED;
-                        }
-                        handleDisconnectedState(newDetailedState, true);
-                        /**
-                         * If we were associated with a network (networkId != -1),
-                         * assume we reached this state because of a failed attempt
-                         * to acquire an IP address, and attempt another connection
-                         * and IP address acquisition in RECONNECT_DELAY_MSECS
-                         * milliseconds.
-                         */
-                        if (mRunState == RUN_STATE_RUNNING && !mIsScanOnly && networkId != -1) {
-                            sendMessageDelayed(reconnectMsg, RECONNECT_DELAY_MSECS);
-                        } else if (mRunState == RUN_STATE_STOPPING) {
-                            stopDriver();
-                        } else if (mRunState == RUN_STATE_STARTING && !mIsScanOnly) {
-                            reconnectCommand();
-                        }
-                    } else if (newState == SupplicantState.DISCONNECTED) {
-                        mHaveIpAddress = false;
-                        if (isDriverStopped() || mDisconnectExpected) {
-                            handleDisconnectedState(DetailedState.DISCONNECTED, true);
-                        } else {
-                            scheduleDisconnect();
-                        }
-                    } else if (newState != SupplicantState.COMPLETED && !mDisconnectPending) {
-                        /**
-                         * Ignore events that don't change the connectivity state,
-                         * such as WPA rekeying operations.
-                         */
-                        if (!(currentState == SupplicantState.COMPLETED &&
-                               (newState == SupplicantState.ASSOCIATING ||
-                                newState == SupplicantState.ASSOCIATED ||
-                                newState == SupplicantState.FOUR_WAY_HANDSHAKE ||
-                                newState == SupplicantState.GROUP_HANDSHAKE))) {
-                            setDetailedState(WifiInfo.getDetailedStateOf(newState));
-                        }
-                    }
-
-                    mDisconnectExpected = false;
-                    intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
-                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
-                            | Intent.FLAG_RECEIVER_REPLACE_PENDING);
-                    intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)newState);
-                    if (failedToAuthenticate) {
-                        if (LOCAL_LOGD) Log.d(TAG, "Failed to authenticate, disabling network " + networkId);
-                        wifiManagerDisableNetwork(networkId);
-                        intent.putExtra(
-                            WifiManager.EXTRA_SUPPLICANT_ERROR,
-                            WifiManager.ERROR_AUTHENTICATING);
-                    }
-                    mContext.sendStickyBroadcast(intent);
-                }
-                break;
-
-            case EVENT_NETWORK_STATE_CHANGED:
-                /*
-                 * Each CONNECT or DISCONNECT generates a pair of events.
-                 * One is a supplicant state change event, and the other
-                 * is a network state change event. For connects, the
-                 * supplicant event always arrives first, followed by
-                 * the network state change event. Only the latter event
-                 * has the BSSID, which we are interested in capturing.
-                 * For disconnects, the order is the opposite -- the
-                 * network state change event comes first, followed by
-                 * the supplicant state change event.
-                 */
-                NetworkStateChangeResult result =
-                    (NetworkStateChangeResult) msg.obj;
-
-                // Wi-Fi network state changed:
-                // [31- 6] Reserved for future use
-                // [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState)   
-                eventLogParam = (result.state.ordinal() & 0x3f);
-                EventLog.writeEvent(EVENTLOG_NETWORK_STATE_CHANGED, eventLogParam);
-                
-                if (LOCAL_LOGD) Log.v(TAG, "New network state is " + result.state);
-                /*
-                 * If we're in scan-only mode, don't advance the state machine, and
-                 * don't report the state change to clients.
-                 */
-                if (mIsScanOnly) {
-                    if (LOCAL_LOGD) Log.v(TAG, "Dropping event in scan-only mode");
-                    break;
-                }
-                if (result.state != DetailedState.SCANNING) {
-                    /*
-                     * Reset the scan count since there was a network state
-                     * change. This could be from supplicant trying to associate
-                     * with a network.
-                     */
-                    mNumScansSinceNetworkStateChange = 0;
-                }
-                /*
-                 * If the supplicant sent us a CONNECTED event, we don't
-                 * want to send out an indication of overall network
-                 * connectivity until we have our IP address. If the
-                 * supplicant sent us a DISCONNECTED event, we delay
-                 * sending a notification in case a reconnection to
-                 * the same access point occurs within a short time.
-                 */
-                if (result.state == DetailedState.DISCONNECTED) {
-                    if (mWifiInfo.getSupplicantState() != SupplicantState.DORMANT) {
-                        scheduleDisconnect();
-                    }
-                    break;
-                }
-                requestConnectionStatus(mWifiInfo);
-                if (!(result.state == DetailedState.CONNECTED &&
-                        (!mHaveIpAddress || mDisconnectPending))) {
-                    setDetailedState(result.state);
-                }
-
-                if (result.state == DetailedState.CONNECTED) {
-                    /*
-                     * Remove the 'available networks' notification when we
-                     * successfully connect to a network.
-                     */
-                    setNotificationVisible(false, 0, false, 0);
-                    boolean wasDisconnectPending = mDisconnectPending;
-                    cancelDisconnect();
-                    /*
-                     * The connection is fully configured as far as link-level
-                     * connectivity is concerned, but we may still need to obtain
-                     * an IP address.
-                     */
-                    if (wasDisconnectPending) {
-                        DetailedState saveState = getNetworkInfo().getDetailedState();
-                        handleDisconnectedState(DetailedState.DISCONNECTED, false);
-                        setDetailedStateInternal(saveState);
-                    }
-
-                    configureInterface();
-                    mLastBssid = result.BSSID;
-                    mLastSsid = mWifiInfo.getSSID();
-                    mLastNetworkId = result.networkId;
-                    if (mHaveIpAddress) {
-                        setDetailedState(DetailedState.CONNECTED);
-                    } else {
-                        setDetailedState(DetailedState.OBTAINING_IPADDR);
-                    }
-                }
-                sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID());
-                break;
-
-            case EVENT_SCAN_RESULTS_AVAILABLE:
-                if (ActivityManagerNative.isSystemReady()) {
-                    mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
-                }
-                sendScanResultsAvailable();
-                /**
-                 * On receiving the first scan results after connecting to
-                 * the supplicant, switch scan mode over to passive.
-                 */
-                setScanMode(false);
-                break;
-
-            case EVENT_POLL_INTERVAL:
-                if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
-                    requestPolledInfo(mWifiInfo, true);
-                    checkPollTimer();
-                }
-                break;
-            
-            case EVENT_DEFERRED_DISCONNECT:
-                if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
-                    handleDisconnectedState(DetailedState.DISCONNECTED, true);
-                }
-                break;
-
-            case EVENT_DEFERRED_RECONNECT:
-                /**
-                 * mLastBssid can be null when there is a reconnect
-                 * request on the first BSSID we connect to
-                 */
-                String BSSID = (msg.obj != null) ? msg.obj.toString() : null;
-                /**
-                 * If we've exceeded the maximum number of retries for reconnecting
-                 * to a given network, disable the network
-                 */
-                if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
-                    if (++mReconnectCount > getMaxDhcpRetries()) {
-                        if (LOCAL_LOGD) {
-                            Log.d(TAG, "Failed reconnect count: " +
-                                    mReconnectCount + " Disabling " + BSSID);
-                        }
-                        mWM.disableNetwork(mLastNetworkId);
-                    }
-                    reconnectCommand();
-                }
-                break;
-
-            case EVENT_INTERFACE_CONFIGURATION_SUCCEEDED:
-                /**
-                 * Since this event is sent from another thread, it might have been
-                 * sent after we closed our connection to the supplicant in the course
-                 * of disabling Wi-Fi. In that case, we should just ignore the event.
-                 */
-                if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
-                    break;
-                }
-                mReconnectCount = 0;
-                mHaveIpAddress = true;
-                mObtainingIpAddress = false;
-                mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
-                mLastSignalLevel = -1; // force update of signal strength
-                if (mNetworkInfo.getDetailedState() != DetailedState.CONNECTED) {
-                    setDetailedState(DetailedState.CONNECTED);
-                    sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID());
-                } else {
-                    msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
-                    msg.sendToTarget();
-                }
-                if (LOCAL_LOGD) Log.v(TAG, "IP configuration: " + mDhcpInfo);
-                // Wi-Fi interface configuration state changed:
-                // [31- 1] Reserved for future use
-                // [ 0- 0] Interface configuration succeeded (1) or failed (0)   
-                EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 1);
-
-                // We've connected successfully, so allow the notification again in the future
-                resetNotificationTimer();
-                break;
-
-            case EVENT_INTERFACE_CONFIGURATION_FAILED:
-                if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
-                    // Wi-Fi interface configuration state changed:
-                    // [31- 1] Reserved for future use
-                    // [ 0- 0] Interface configuration succeeded (1) or failed (0)
-                    EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 0);
-                    mHaveIpAddress = false;
-                    mWifiInfo.setIpAddress(0);
-                    mObtainingIpAddress = false;
-                    disconnect();
-                }
-                break;
-
-            case EVENT_DRIVER_STATE_CHANGED:
-                // Wi-Fi driver state changed:
-                // 0 STARTED
-                // 1 STOPPED
-                // 2 HUNG
-                EventLog.writeEvent(EVENTLOG_DRIVER_STATE_CHANGED, msg.arg1);
-
-                switch (msg.arg1) {
-                case DRIVER_STARTED:
-                    /**
-                     * Set the number of allowed radio channels according
-                     * to the system setting, since it gets reset by the
-                     * driver upon changing to the STARTED state.
-                     */
-                    setNumAllowedChannels();
-                    synchronized (this) {
-                        macaddr = WifiNative.getMacAddressCommand();
-                        if (macaddr != null) {
-                            mWifiInfo.setMacAddress(macaddr);
-                        }
-                        mRunState = RUN_STATE_RUNNING;
-                        if (!mIsScanOnly) {
-                            reconnectCommand();
-                        } else {
-                            // In some situations, supplicant needs to be kickstarted to
-                            // start the background scanning
-                            scan(true);
-                        }
-                    }
-                    break;
-                case DRIVER_HUNG:
-                    Log.e(TAG, "Wifi Driver reports HUNG - reloading.");
-                    /**
-                     * restart the driver - toggle off and on
-                     */
-                    mWM.setWifiEnabled(false);
-                    mWM.setWifiEnabled(true);
-                    break;
-                }
-                synchronized (this) {
-                    updateBatteryWorkSourceLocked(null);
-                }
-                break;
-
-            case EVENT_PASSWORD_KEY_MAY_BE_INCORRECT:
-                mPasswordKeyMayBeIncorrect = true;
-                break;
-        }
-    }
-
-    private boolean wifiManagerDisableNetwork(int networkId) {
-        boolean disabledNetwork = false;
-        if (0 <= networkId) {
-            disabledNetwork = mWM.disableNetwork(networkId);
-            if (LOCAL_LOGD) {
-                if (disabledNetwork) {
-                    Log.v(TAG, "Disabled network: " + networkId);
-                }
-            }
-        }
-        if (LOCAL_LOGD) {
-            if (!disabledNetwork) {
-                Log.e(TAG, "Failed to disable network:" +
-                      " invalid network id: " + networkId);
-            }
-        }
-        return disabledNetwork;
-    }
-
-    private void configureInterface() {
-        checkPollTimer();
-        mLastSignalLevel = -1;
-        if (!mUseStaticIp) {
-            if (!mHaveIpAddress && !mObtainingIpAddress) {
-                mObtainingIpAddress = true;
-                mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);
-            }
-        } else {
-            int event;
-            if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
-                mHaveIpAddress = true;
-                event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
-                if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration succeeded");
-            } else {
-                mHaveIpAddress = false;
-                event = EVENT_INTERFACE_CONFIGURATION_FAILED;
-                if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed");
-            }
-            sendEmptyMessage(event);
-        }
-    }
-
-    /**
-     * Reset our IP state and send out broadcasts following a disconnect.
-     * @param newState the {@code DetailedState} to set. Should be either
-     * {@code DISCONNECTED} or {@code FAILED}.
-     * @param disableInterface indicates whether the interface should
-     * be disabled
-     */
-    private void handleDisconnectedState(DetailedState newState, boolean disableInterface) {
-        if (mDisconnectPending) {
-            cancelDisconnect();
-        }
-        mDisconnectExpected = false;
-        resetConnections(disableInterface);
-        setDetailedState(newState);
-        sendNetworkStateChangeBroadcast(mLastBssid);
-        mWifiInfo.setBSSID(null);
-        mLastBssid = null;
-        mLastSsid = null;
-        mDisconnectPending = false;
-    }
-
-    /**
-     * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
-     * using the interface, stopping DHCP, and disabling the interface.
-     */
-    public void resetConnections(boolean disableInterface) {
-        if (LOCAL_LOGD) Log.d(TAG, "Reset connections and stopping DHCP");
-        mHaveIpAddress = false;
-        mObtainingIpAddress = false;
-        mWifiInfo.setIpAddress(0);
-
-        /*
-         * Reset connection depends on both the interface and the IP assigned,
-         * so it should be done before any chance of the IP being lost.
-         */
-        NetworkUtils.resetConnections(mInterfaceName);
-
-        // Stop DHCP
-        if (mDhcpTarget != null) {
-            mDhcpTarget.setCancelCallback(true);
-            mDhcpTarget.removeMessages(EVENT_DHCP_START);
-        }
-        if (!NetworkUtils.stopDhcp(mInterfaceName)) {
-            Log.e(TAG, "Could not stop DHCP");
-        }
-
-        /**
-         * Interface is re-enabled in the supplicant
-         * when moving out of ASSOCIATING state
-         */
-        if(disableInterface) {
-            if (LOCAL_LOGD) Log.d(TAG, "Disabling interface");
-            NetworkUtils.disableInterface(mInterfaceName);
-        }
-    }
-
-    /**
-     * The supplicant is reporting that we are disconnected from the current
-     * access point. Often, however, a disconnect will be followed very shortly
-     * by a reconnect to the same access point. Therefore, we delay resetting
-     * the connection's IP state for a bit.
-     */
-    private void scheduleDisconnect() {
-        mDisconnectPending = true;
-        if (!hasMessages(EVENT_DEFERRED_DISCONNECT)) {
-            sendEmptyMessageDelayed(EVENT_DEFERRED_DISCONNECT, DISCONNECT_DELAY_MSECS);
-        }
-    }
-
-    private void cancelDisconnect() {
-        mDisconnectPending = false;
-        removeMessages(EVENT_DEFERRED_DISCONNECT);
-    }
-
-    public DhcpInfo getDhcpInfo() {
-        return mDhcpInfo;
-    }
-
-    public synchronized List<ScanResult> getScanResultsList() {
-        return mScanResults;
-    }
-
-    public synchronized void setScanResultsList(List<ScanResult> scanList) {
-        mScanResults = scanList;
-    }
-
-    /**
-     * Get status information for the current connection, if any.
-     * @return a {@link WifiInfo} object containing information about the current connection
-     */
-    public WifiInfo requestConnectionInfo() {
-        requestConnectionStatus(mWifiInfo);
-        requestPolledInfo(mWifiInfo, false);
-        return mWifiInfo;
-    }
-
-    private void requestConnectionStatus(WifiInfo info) {
-        String reply = status();
-        if (reply == null) {
-            return;
-        }
-        /*
-         * Parse the reply from the supplicant to the status command, and update
-         * local state accordingly. The reply is a series of lines of the form
-         * "name=value".
-         */
-        String SSID = null;
-        String BSSID = null;
-        String suppState = null;
-        int netId = -1;
-        String[] lines = reply.split("\n");
-        for (String line : lines) {
-            String[] prop = line.split(" *= *", 2);
-            if (prop.length < 2)
-                continue;
-            String name = prop[0];
-            String value = prop[1];
-            if (name.equalsIgnoreCase("id"))
-                netId = Integer.parseInt(value);
-            else if (name.equalsIgnoreCase("ssid"))
-                SSID = value;
-            else if (name.equalsIgnoreCase("bssid"))
-                BSSID = value;
-            else if (name.equalsIgnoreCase("wpa_state"))
-                suppState = value;
-        }
-        info.setNetworkId(netId);
-        info.setSSID(SSID);
-        info.setBSSID(BSSID);
-        /*
-         * We only set the supplicant state if the previous state was
-         * UNINITIALIZED. This should only happen when we first connect to
-         * the supplicant. Once we're connected, we should always receive
-         * an event upon any state change, but in this case, we want to
-         * make sure any listeners are made aware of the state change.
-         */
-        if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED && suppState != null)
-            setSupplicantState(suppState);
-    }
-
-    /**
-     * Get the dynamic information that is not reported via events.
-     * @param info the object into which the information should be captured.
-     */
-    private synchronized void requestPolledInfo(WifiInfo info, boolean polling)
-    {
-        int newRssi = (polling ? getRssiApprox() : getRssi());
-        if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
-            /* some implementations avoid negative values by adding 256
-             * so we need to adjust for that here.
-             */
-            if (newRssi > 0) newRssi -= 256;
-            info.setRssi(newRssi);
-            /*
-             * Rather then sending the raw RSSI out every time it
-             * changes, we precalculate the signal level that would
-             * be displayed in the status bar, and only send the
-             * broadcast if that much more coarse-grained number
-             * changes. This cuts down greatly on the number of
-             * broadcasts, at the cost of not informing others
-             * interested in RSSI of all the changes in signal
-             * level.
-             */
-            // TODO: The second arg to the call below needs to be a symbol somewhere, but
-            // it's actually the size of an array of icons that's private
-            // to StatusBar Policy.
-            int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
-            if (newSignalLevel != mLastSignalLevel) {
-                sendRssiChangeBroadcast(newRssi);
-            }
-            mLastSignalLevel = newSignalLevel;
-        } else {
-            info.setRssi(-200);
-        }
-        int newLinkSpeed = getLinkSpeed();
-        if (newLinkSpeed != -1) {
-            info.setLinkSpeed(newLinkSpeed);
-        }
-    }
-
-    private void sendRssiChangeBroadcast(final int newRssi) {
-        if (ActivityManagerNative.isSystemReady()) {
-            Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
-            intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
-            mContext.sendBroadcast(intent);
-        }
-    }
-
-    private void sendNetworkStateChangeBroadcast(String bssid) {
-        Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
-                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
-        if (bssid != null)
-            intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
-        mContext.sendStickyBroadcast(intent);
-    }
-
-    /**
-     * Disable Wi-Fi connectivity by stopping the driver.
-     */
-    public boolean teardown() {
-        if (!mTornDownByConnMgr) {
-            if (disconnectAndStop()) {
-                setTornDownByConnMgr(true);
-                return true;
-            } else {
-                return false;
-            }
-        } else {
-            return true;
-        }
-    }
-
-    /**
-     * Reenable Wi-Fi connectivity by restarting the driver.
-     */
-    public boolean reconnect() {
-        if (mTornDownByConnMgr) {
-            if (restart()) {
-                setTornDownByConnMgr(false);
-                return true;
-            } else {
-                return false;
-            }
-        } else {
-            return true;
-        }
-    }
-
-    /**
-     * We want to stop the driver, but if we're connected to a network,
-     * we first want to disconnect, so that the supplicant is always in
-     * a known state (DISCONNECTED) when the driver is stopped.
-     * @return {@code true} if the operation succeeds, which means that the
-     * disconnect or stop command was initiated.
-     */
-    public synchronized boolean disconnectAndStop() {
-        boolean ret = true;;
-        if (mRunState != RUN_STATE_STOPPING && mRunState != RUN_STATE_STOPPED) {
-            // Take down any open network notifications
-            setNotificationVisible(false, 0, false, 0);
-
-            if (mWifiInfo.getSupplicantState() == SupplicantState.DORMANT) {
-                ret = stopDriver();
-            } else {
-                ret = disconnect();
-            }
-            mRunState = RUN_STATE_STOPPING;
-        }
-        return ret;
-    }
-
-    public synchronized boolean restart() {
-        if (isDriverStopped()) {
-            mRunState = RUN_STATE_STARTING;
-            resetConnections(true);
-            return startDriver();
-        }
-        return true;
-    }
-
-    public int getWifiState() {
-        return mWifiState.get();
-    }
-
-    public void setWifiState(int wifiState) {
-        mWifiState.set(wifiState);
-    }
-
-    public boolean isAnyNetworkDisabled() {
-        return mIsAnyNetworkDisabled.get();
-    }
-
-   /**
-     * The WifiNative interface functions are listed below.
-     * The only native call that is not synchronized on
-     * WifiStateTracker is waitForEvent() which waits on a
-     * seperate monitor channel.
-     *
-     * All supplicant commands need the wifi to be in an
-     * enabled state. This can be done by checking the
-     * mWifiState to be WIFI_STATE_ENABLED.
-     *
-     * All commands that can cause commands to driver
-     * initiated need the driver state to be started.
-     * This is done by checking isDriverStopped() to
-     * be false.
-     */
-
-    /**
-     * Load the driver and firmware
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean loadDriver() {
-        return WifiNative.loadDriver();
-    }
-
-    /**
-     * Unload the driver and firmware
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean unloadDriver() {
-        return WifiNative.unloadDriver();
-    }
-
-    /**
-     * Check the supplicant config and
-     * start the supplicant daemon
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean startSupplicant() {
-        return WifiNative.startSupplicant();
-    }
-
-    /**
-     * Stop the supplicant daemon
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean stopSupplicant() {
-        return WifiNative.stopSupplicant();
-    }
-
-    /**
-     * Establishes two channels - control channel for commands
-     * and monitor channel for notifying WifiMonitor
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean connectToSupplicant() {
-        return WifiNative.connectToSupplicant();
-    }
-
-    /**
-     * Close the control/monitor channels to supplicant
-     */
-    public synchronized void closeSupplicantConnection() {
-        WifiNative.closeSupplicantConnection();
-    }
-
-    /**
-     * Check if the supplicant is alive
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean ping() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return false;
-        }
-        return WifiNative.pingCommand();
-    }
-
-    /**
-     * initiate an active or passive scan
-     *
-     * @param forceActive true if it is a active scan
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean scan(boolean forceActive) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return false;
-        }
-        return WifiNative.scanCommand(forceActive);
-    }
-
-    /**
-     * Specifies whether the supplicant or driver
-     * take care of initiating scan and doing AP selection
-     *
-     * @param mode
-     *    SUPPL_SCAN_HANDLING_NORMAL
-     *    SUPPL_SCAN_HANDLING_LIST_ONLY
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean setScanResultHandling(int mode) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return false;
-        }
-        return WifiNative.setScanResultHandlingCommand(mode);
-    }
-
-    /**
-     * Fetch the scan results from the supplicant
-     *
-     * @return example result string
-     * 00:bb:cc:dd:cc:ee       2427    166     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net1
-     * 00:bb:cc:dd:cc:ff       2412    165     [WPA-EAP-TKIP][WPA2-EAP-CCMP]   Net2
-     */
-    public synchronized String scanResults() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return null;
-        }
-        return WifiNative.scanResultsCommand();
-    }
-
-    /**
-     * Set the scan mode - active or passive
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean setScanMode(boolean isScanModeActive) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return false;
-        }
-        if (mIsScanModeActive != isScanModeActive) {
-            return WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive);
-        }
-        return true;
-    }
-
-    /**
-     * Disconnect from Access Point
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean disconnect() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return false;
-        }
-        return WifiNative.disconnectCommand();
-    }
-
-    /**
-     * Initiate a reconnection to AP
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean reconnectCommand() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return false;
-        }
-        return WifiNative.reconnectCommand();
-    }
-
-    /**
-     * Add a network
-     *
-     * @return network id of the new network
-     */
-    public synchronized int addNetwork() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return -1;
-        }
-        return WifiNative.addNetworkCommand();
-    }
-
-    /**
-     * Delete a network
-     *
-     * @param networkId id of the network to be removed
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean removeNetwork(int networkId) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return false;
-        }
-        return mDisconnectExpected = WifiNative.removeNetworkCommand(networkId);
-    }
-
-    /**
-     * Enable a network
-     *
-     * @param netId network id of the network
-     * @param disableOthers true, if all other networks have to be disabled
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean enableNetwork(int netId, boolean disableOthers) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return false;
-        }
-        if (disableOthers) mIsAnyNetworkDisabled.set(true);
-        return WifiNative.enableNetworkCommand(netId, disableOthers);
-    }
-
-    /**
-     * Enable all networks
-     *
-     * @param networks list of configured networks
-     */
-    public synchronized void enableAllNetworks(List<WifiConfiguration> networks) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return;
-        }
-        mIsAnyNetworkDisabled.set(false);
-        for (WifiConfiguration config : networks) {
-            if (config.status == WifiConfiguration.Status.DISABLED) {
-                WifiNative.enableNetworkCommand(config.networkId, false);
-            }
-        }
-    }
-
-    /**
-     * Disable a network
-     *
-     * @param netId network id of the network
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean disableNetwork(int netId) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return false;
-        }
-        mIsAnyNetworkDisabled.set(true);
-        return WifiNative.disableNetworkCommand(netId);
-    }
-
-    /**
-     * Initiate a re-association in supplicant
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean reassociate() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return false;
-        }
-        return WifiNative.reassociateCommand();
-    }
-
-    /**
-     * Blacklist a BSSID. This will avoid the AP if there are
-     * alternate APs to connect
-     *
-     * @param bssid BSSID of the network
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean addToBlacklist(String bssid) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return false;
-        }
-        return WifiNative.addToBlacklistCommand(bssid);
-    }
-
-    /**
-     * Clear the blacklist list
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean clearBlacklist() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return false;
-        }
-        return WifiNative.clearBlacklistCommand();
-    }
-
-    /**
-     * List all configured networks
-     *
-     * @return list of networks or null on failure
-     */
-    public synchronized String listNetworks() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return null;
-        }
-        return WifiNative.listNetworksCommand();
-    }
-
-    /**
-     * Get network setting by name
-     *
-     * @param netId network id of the network
-     * @param name network variable key
-     * @return value corresponding to key
-     */
-    public synchronized String getNetworkVariable(int netId, String name) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return null;
-        }
-        return WifiNative.getNetworkVariableCommand(netId, name);
-    }
-
-    /**
-     * Set network setting by name
-     *
-     * @param netId network id of the network
-     * @param name network variable key
-     * @param value network variable value
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean setNetworkVariable(int netId, String name, String value) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return false;
-        }
-        return WifiNative.setNetworkVariableCommand(netId, name, value);
-    }
-
-    /**
-     * Get detailed status of the connection
-     *
-     * @return Example status result
-     *  bssid=aa:bb:cc:dd:ee:ff
-     *  ssid=TestNet
-     *  id=3
-     *  pairwise_cipher=NONE
-     *  group_cipher=NONE
-     *  key_mgmt=NONE
-     *  wpa_state=COMPLETED
-     *  ip_address=X.X.X.X
-     */
-    public synchronized String status() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return null;
-        }
-        return WifiNative.statusCommand();
-    }
-
-    /**
-     * Get RSSI to currently connected network
-     *
-     * @return RSSI value, -1 on failure
-     */
-    public synchronized int getRssi() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return -1;
-        }
-        return WifiNative.getRssiApproxCommand();
-    }
-
-    /**
-     * Get approx RSSI to currently connected network
-     *
-     * @return RSSI value, -1 on failure
-     */
-    public synchronized int getRssiApprox() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return -1;
-        }
-        return WifiNative.getRssiApproxCommand();
-    }
-
-    /**
-     * Get link speed to currently connected network
-     *
-     * @return link speed, -1 on failure
-     */
-    public synchronized int getLinkSpeed() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return -1;
-        }
-        return WifiNative.getLinkSpeedCommand();
-    }
-
-    /**
-     * Start driver
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean startDriver() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return false;
-        }
-        return WifiNative.startDriverCommand();
-    }
-
-    /**
-     * Stop driver
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean stopDriver() {
-        /* Driver stop should not happen only when supplicant event
-         * DRIVER_STOPPED has already been handled */
-        if (mWifiState.get() != WIFI_STATE_ENABLED || mRunState == RUN_STATE_STOPPED) {
-            return false;
-        }
-        return WifiNative.stopDriverCommand();
-    }
-
-    /**
-     * Start packet filtering
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean startPacketFiltering() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return false;
-        }
-        return WifiNative.startPacketFiltering();
-    }
-
-    /**
-     * Stop packet filtering
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean stopPacketFiltering() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return false;
-        }
-        return WifiNative.stopPacketFiltering();
-    }
-
-    /**
-     * Get power mode
-     * @return power mode
-     */
-    public synchronized int getPowerMode() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) {
-            return -1;
-        }
-        return WifiNative.getPowerModeCommand();
-    }
-
-    /**
-     * Set power mode
-     * @param mode
-     *     DRIVER_POWER_MODE_AUTO
-     *     DRIVER_POWER_MODE_ACTIVE
-     *
-     * Uses reference counting to keep power mode active
-     * as long as one entity wants power mode to be active.
-     *
-     * For example, WifiLock high perf mode can keep power mode active
-     * or a DHCP session can keep it active. As long as one entity wants
-     * it enabled, it should stay that way
-     *
-     */
-    private synchronized void setPowerMode(int mode) {
-
-        /* It is good to plumb power mode change
-         * even if ref count indicates already done
-         * since we could have a case of previous failure.
-         */
-        switch(mode) {
-            case DRIVER_POWER_MODE_ACTIVE:
-                mPowerModeRefCount++;
-                break;
-            case DRIVER_POWER_MODE_AUTO:
-                mPowerModeRefCount--;
-                if (mPowerModeRefCount > 0) {
-                    return;
-                } else {
-                    /* Keep refcount from becoming negative */
-                    mPowerModeRefCount = 0;
-                }
-                break;
-        }
-
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return;
-        }
-
-        WifiNative.setPowerModeCommand(mode);
-    }
-
-    /**
-     * Set the number of allowed radio frequency channels from the system
-     * setting value, if any.
-     * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
-     * the number of channels is invalid.
-     */
-    public synchronized boolean setNumAllowedChannels() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return false;
-        }
-        try {
-            return setNumAllowedChannels(
-                    Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
-        } catch (Settings.SettingNotFoundException e) {
-            if (mNumAllowedChannels != 0) {
-                WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels);
-            }
-            // otherwise, use the driver default
-        }
-        return true;
-    }
-
-    /**
-     * Set the number of radio frequency channels that are allowed to be used
-     * in the current regulatory domain.
-     * @param numChannels the number of allowed channels. Must be greater than 0
-     * and less than or equal to 16.
-     * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
-     * {@code numChannels} is outside the valid range.
-     */
-    public synchronized boolean setNumAllowedChannels(int numChannels) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return false;
-        }
-        mNumAllowedChannels = numChannels;
-        return WifiNative.setNumAllowedChannelsCommand(numChannels);
-    }
-
-    /**
-     * Get number of allowed channels
-     *
-     * @return channel count, -1 on failure
-     */
-    public synchronized int getNumAllowedChannels() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return -1;
-        }
-        return WifiNative.getNumAllowedChannelsCommand();
-    }
-
-    /**
-     * Set bluetooth coex mode:
-     *
-     * @param mode
-     *  BLUETOOTH_COEXISTENCE_MODE_ENABLED
-     *  BLUETOOTH_COEXISTENCE_MODE_DISABLED
-     *  BLUETOOTH_COEXISTENCE_MODE_SENSE
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean setBluetoothCoexistenceMode(int mode) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return false;
-        }
-        return WifiNative.setBluetoothCoexistenceModeCommand(mode);
-    }
-
-    /**
-     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
-     * some of the low-level scan parameters used by the driver are changed to
-     * reduce interference with A2DP streaming.
-     *
-     * @param isBluetoothPlaying whether to enable or disable this mode
-     */
-    public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return;
-        }
-        WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying);
-    }
-
-    /**
-     * Save configuration on supplicant
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean saveConfig() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return false;
-        }
-        return WifiNative.saveConfigCommand();
-    }
-
-    /**
-     * Reload the configuration from file
-     *
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
-     */
-    public synchronized boolean reloadConfig() {
-        if (mWifiState.get() != WIFI_STATE_ENABLED) {
-            return false;
-        }
-        return WifiNative.reloadConfigCommand();
-    }
-
-    public boolean setRadio(boolean turnOn) {
-        return mWM.setWifiEnabled(turnOn);
-    }
-
-    /**
-     * {@inheritDoc}
-     * There are currently no Wi-Fi-specific features supported.
-     * @param feature the name of the feature
-     * @return {@code -1} indicating failure, always
+     * Tells the underlying networking system that the caller wants to
+     * begin using the named feature. The interpretation of {@code feature}
+     * is completely up to each networking implementation.
+     * @param feature the name of the feature to be used
+     * @param callingPid the process ID of the process that is issuing this request
+     * @param callingUid the user ID of the process that is issuing this request
+     * @return an integer value representing the outcome of the request.
+     * The interpretation of this value is specific to each networking
+     * implementation+feature combination, except that the value {@code -1}
+     * always indicates failure.
+     * TODO: needs to go away
      */
     public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
         return -1;
     }
 
     /**
-     * {@inheritDoc}
-     * There are currently no Wi-Fi-specific features supported.
-     * @param feature the name of the feature
-     * @return {@code -1} indicating failure, always
+     * Tells the underlying networking system that the caller is finished
+     * using the named feature. The interpretation of {@code feature}
+     * is completely up to each networking implementation.
+     * @param feature the name of the feature that is no longer needed.
+     * @param callingPid the process ID of the process that is issuing this request
+     * @param callingUid the user ID of the process that is issuing this request
+     * @return an integer value representing the outcome of the request.
+     * The interpretation of this value is specific to each networking
+     * implementation+feature combination, except that the value {@code -1}
+     * always indicates failure.
+     * TODO: needs to go away
      */
     public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
         return -1;
     }
 
-    @Override
-    public void interpretScanResultsAvailable() {
-
-        // If we shouldn't place a notification on available networks, then
-        // don't bother doing any of the following
-        if (!mNotificationEnabled) return;
-
-        NetworkInfo networkInfo = getNetworkInfo();
-
-        State state = networkInfo.getState();
-        if ((state == NetworkInfo.State.DISCONNECTED)
-                || (state == NetworkInfo.State.UNKNOWN)) {
-
-            // Look for an open network
-            List<ScanResult> scanResults = getScanResultsList();
-            if (scanResults != null) {
-                int numOpenNetworks = 0;
-                for (int i = scanResults.size() - 1; i >= 0; i--) {
-                    ScanResult scanResult = scanResults.get(i);
-
-                    if (TextUtils.isEmpty(scanResult.capabilities)) {
-                        numOpenNetworks++;
-                    }
-                }
-            
-                if (numOpenNetworks > 0) {
-                    if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
-                        /*
-                         * We've scanned continuously at least
-                         * NUM_SCANS_BEFORE_NOTIFICATION times. The user
-                         * probably does not have a remembered network in range,
-                         * since otherwise supplicant would have tried to
-                         * associate and thus resetting this counter.
-                         */
-                        setNotificationVisible(true, numOpenNetworks, false, 0);
-                    }
-                    return;
-                }
-            }
-        }
-        
-        // No open networks in range, remove the notification
-        setNotificationVisible(false, 0, false, 0);
+    /**
+     * Check if private DNS route is set for the network
+     */
+    public boolean isPrivateDnsRouteSet() {
+        return mPrivateDnsRouteSet.get();
     }
 
     /**
-     * Display or don't display a notification that there are open Wi-Fi networks.
-     * @param visible {@code true} if notification should be visible, {@code false} otherwise
-     * @param numNetworks the number networks seen
-     * @param force {@code true} to force notification to be shown/not-shown,
-     * even if it is already shown/not-shown.
-     * @param delay time in milliseconds after which the notification should be made
-     * visible or invisible.
+     * Set a flag indicating private DNS route is set
      */
-    public void setNotificationVisible(boolean visible, int numNetworks, boolean force, int delay) {
-        
-        // Since we use auto cancel on the notification, when the
-        // mNetworksAvailableNotificationShown is true, the notification may
-        // have actually been canceled.  However, when it is false we know
-        // for sure that it is not being shown (it will not be shown any other
-        // place than here)
-        
-        // If it should be hidden and it is already hidden, then noop
-        if (!visible && !mNotificationShown && !force) {
-            return;
-        }
-
-        Message message;
-        if (visible) {
-            
-            // Not enough time has passed to show the notification again
-            if (System.currentTimeMillis() < mNotificationRepeatTime) {
-                return;
-            }
-            
-            if (mNotification == null) {
-                // Cache the Notification mainly so we can remove the
-                // EVENT_NOTIFICATION_CHANGED message with this Notification from
-                // the queue later
-                mNotification = new Notification();
-                mNotification.when = 0;
-                mNotification.icon = ICON_NETWORKS_AVAILABLE;
-                mNotification.flags = Notification.FLAG_AUTO_CANCEL;
-                mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
-                        new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
-            }
-
-            CharSequence title = mContext.getResources().getQuantityText(
-                    com.android.internal.R.plurals.wifi_available, numNetworks);
-            CharSequence details = mContext.getResources().getQuantityText(
-                    com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
-            mNotification.tickerText = title;
-            mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
-            
-            mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
-
-            message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
-                    ICON_NETWORKS_AVAILABLE, mNotification);
-            
-        } else {
-
-            // Remove any pending messages to show the notification
-            mTarget.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
-            
-            message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, ICON_NETWORKS_AVAILABLE);
-        }
-
-        mTarget.sendMessageDelayed(message, delay);
-        
-        mNotificationShown = visible;
+    public void privateDnsRouteSet(boolean enabled) {
+        mPrivateDnsRouteSet.set(enabled);
     }
 
     /**
-     * Clears variables related to tracking whether a notification has been
-     * shown recently.
-     * <p>
-     * After calling this method, the timer that prevents notifications from
-     * being shown too often will be cleared.
+     * Fetch NetworkInfo for the network
      */
-    private void resetNotificationTimer() {
-        mNotificationRepeatTime = 0;
-        mNumScansSinceNetworkStateChange = 0;
-    }
-    
-    @Override
-    public String toString() {
-        StringBuffer sb = new StringBuffer();
-        sb.append("interface ").append(mInterfaceName);
-        sb.append(" runState=");
-        if (mRunState >= 1 && mRunState <= mRunStateNames.length) {
-            sb.append(mRunStateNames[mRunState-1]);
-        } else {
-            sb.append(mRunState);
-        }
-        sb.append(LS).append(mWifiInfo).append(LS);
-        sb.append(mDhcpInfo).append(LS);
-        sb.append("haveIpAddress=").append(mHaveIpAddress).
-                append(", obtainingIpAddress=").append(mObtainingIpAddress).
-                append(", scanModeActive=").append(mIsScanModeActive).append(LS).
-                append("lastSignalLevel=").append(mLastSignalLevel).
-                append(", explicitlyDisabled=").append(mTornDownByConnMgr);
-        return sb.toString();
+    public NetworkInfo getNetworkInfo() {
+        return mNetworkInfo;
     }
 
-    private class DhcpHandler extends Handler {
-
-        private Handler mTarget;
-        
-        /**
-         * Whether to skip the DHCP result callback to the target. For example,
-         * this could be set if the network we were requesting an IP for has
-         * since been disconnected.
-         * <p>
-         * Note: There is still a chance where the client's intended DHCP
-         * request not being canceled. For example, we are request for IP on
-         * A, and he queues request for IP on B, and then cancels the request on
-         * B while we're still requesting from A.
-         */
-        private boolean mCancelCallback;
-
-        /**
-         * Instance of the bluetooth headset helper. This needs to be created
-         * early because there is a delay before it actually 'connects', as
-         * noted by its javadoc. If we check before it is connected, it will be
-         * in an error state and we will not disable coexistence.
-         */
-        private BluetoothHeadset mBluetoothHeadset;
-        
-        public DhcpHandler(Looper looper, Handler target) {
-            super(looper);
-            mTarget = target;
-            
-            mBluetoothHeadset = new BluetoothHeadset(mContext, null);
-        }
-
-        public void handleMessage(Message msg) {
-            int event;
-
-            switch (msg.what) {
-                case EVENT_DHCP_START:
-                    
-                    boolean modifiedBluetoothCoexistenceMode = false;
-                    int powerMode = DRIVER_POWER_MODE_AUTO;
-
-                    if (shouldDisableCoexistenceMode()) {
-                        /*
-                         * There are problems setting the Wi-Fi driver's power
-                         * mode to active when bluetooth coexistence mode is
-                         * enabled or sense.
-                         * <p>
-                         * We set Wi-Fi to active mode when
-                         * obtaining an IP address because we've found
-                         * compatibility issues with some routers with low power
-                         * mode.
-                         * <p>
-                         * In order for this active power mode to properly be set,
-                         * we disable coexistence mode until we're done with
-                         * obtaining an IP address.  One exception is if we
-                         * are currently connected to a headset, since disabling
-                         * coexistence would interrupt that connection.
-                         */
-                        modifiedBluetoothCoexistenceMode = true;
-
-                        // Disable the coexistence mode
-                        setBluetoothCoexistenceMode(
-                                WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
-                    }
-                    
-                    powerMode = getPowerMode();
-                    if (powerMode < 0) {
-                      // Handle the case where supplicant driver does not support
-                      // getPowerModeCommand.
-                        powerMode = DRIVER_POWER_MODE_AUTO;
-                    }
-                    if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
-                        setPowerMode(DRIVER_POWER_MODE_ACTIVE);
-                    }
-
-                    synchronized (this) {
-                        // A new request is being made, so assume we will callback
-                        mCancelCallback = false;
-                    }
-                    Log.d(TAG, "DhcpHandler: DHCP request started");
-                    if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
-                        event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
-                        if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded");
-                    } else {
-                        event = EVENT_INTERFACE_CONFIGURATION_FAILED;
-                        Log.i(TAG, "DhcpHandler: DHCP request failed: " +
-                            NetworkUtils.getDhcpError());
-                    }
-
-                    if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
-                        setPowerMode(powerMode);
-                    }
-
-                    if (modifiedBluetoothCoexistenceMode) {
-                        // Set the coexistence mode back to its default value
-                        setBluetoothCoexistenceMode(
-                                WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
-                    }
-
-                    synchronized (this) {
-                        if (!mCancelCallback) {
-                            mTarget.sendEmptyMessage(event);
-                        }
-                    }
-                    break;
-            }
-        }
-
-        public synchronized void setCancelCallback(boolean cancelCallback) {
-            mCancelCallback = cancelCallback;
-        }
-
-        /**
-         * Whether to disable coexistence mode while obtaining IP address. This
-         * logic will return true only if the current bluetooth
-         * headset/handsfree state is disconnected. This means if it is in an
-         * error state, we will NOT disable coexistence mode to err on the side
-         * of safety.
-         * 
-         * @return Whether to disable coexistence mode.
-         */
-        private boolean shouldDisableCoexistenceMode() {
-            int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
-            return state == BluetoothHeadset.STATE_DISCONNECTED;
-        }
-    }
-    
-    private void checkUseStaticIp() {
-        mUseStaticIp = false;
-        final ContentResolver cr = mContext.getContentResolver();
-        try {
-            if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
-                return;
-            }
-        } catch (Settings.SettingNotFoundException e) {
-            return;
-        }
-
-        try {
-            String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
-            if (addr != null) {
-                mDhcpInfo.ipAddress = stringToIpAddr(addr);
-            } else {
-                return;
-            }
-            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
-            if (addr != null) {
-                mDhcpInfo.gateway = stringToIpAddr(addr);
-            } else {
-                return;
-            }
-            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
-            if (addr != null) {
-                mDhcpInfo.netmask = stringToIpAddr(addr);
-            } else {
-                return;
-            }
-            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
-            if (addr != null) {
-                mDhcpInfo.dns1 = stringToIpAddr(addr);
-            } else {
-                return;
-            }
-            addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
-            if (addr != null) {
-                mDhcpInfo.dns2 = stringToIpAddr(addr);
-            } else {
-                mDhcpInfo.dns2 = 0;
-            }
-        } catch (UnknownHostException e) {
-            return;
-        }
-        mUseStaticIp = true;
+    /**
+     * Fetch LinkProperties for the network
+     */
+    public LinkProperties getLinkProperties() {
+        return new LinkProperties(mLinkProperties);
     }
 
-    private static int stringToIpAddr(String addrString) throws UnknownHostException {
-        try {
-            String[] parts = addrString.split("\\.");
-            if (parts.length != 4) {
-                throw new UnknownHostException(addrString);
-            }
-
-            int a = Integer.parseInt(parts[0])      ;
-            int b = Integer.parseInt(parts[1]) <<  8;
-            int c = Integer.parseInt(parts[2]) << 16;
-            int d = Integer.parseInt(parts[3]) << 24;
-
-            return a | b | c | d;
-        } catch (NumberFormatException ex) {
-            throw new UnknownHostException(addrString);
-        }
+    /**
+     * A capability is an Integer/String pair, the capabilities
+     * are defined in the class LinkSocket#Key.
+     *
+     * @return a copy of this connections capabilities, may be empty but never null.
+     */
+    public LinkCapabilities getLinkCapabilities() {
+        return new LinkCapabilities(mLinkCapabilities);
     }
 
-    private int getMaxDhcpRetries() {
-        return Settings.Secure.getInt(mContext.getContentResolver(),
-                                      Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
-                                      DEFAULT_MAX_DHCP_RETRIES);
+    /**
+     * Fetch default gateway address for the network
+     */
+    public int getDefaultGatewayAddr() {
+        return mDefaultGatewayAddr.get();
     }
 
-    private class SettingsObserver extends ContentObserver {
-        public SettingsObserver(Handler handler) {
-            super(handler);
-            ContentResolver cr = mContext.getContentResolver();
-            cr.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.WIFI_USE_STATIC_IP), false, this);
-            cr.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.WIFI_STATIC_IP), false, this);
-            cr.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.WIFI_STATIC_GATEWAY), false, this);
-            cr.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.WIFI_STATIC_NETMASK), false, this);
-            cr.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.WIFI_STATIC_DNS1), false, this);
-            cr.registerContentObserver(Settings.System.getUriFor(
-                Settings.System.WIFI_STATIC_DNS2), false, this);
-        }
-
-        public void onChange(boolean selfChange) {
-            super.onChange(selfChange);
-
-            boolean wasStaticIp = mUseStaticIp;
-            int oIp, oGw, oMsk, oDns1, oDns2;
-            oIp = oGw = oMsk = oDns1 = oDns2 = 0;
-            if (wasStaticIp) {
-                oIp = mDhcpInfo.ipAddress;
-                oGw = mDhcpInfo.gateway;
-                oMsk = mDhcpInfo.netmask;
-                oDns1 = mDhcpInfo.dns1;
-                oDns2 = mDhcpInfo.dns2;
-            }
-            checkUseStaticIp();
-
-            if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
-                return;
-            }
-
-            boolean changed =
-                (wasStaticIp != mUseStaticIp) ||
-                    (wasStaticIp && (
-                        oIp   != mDhcpInfo.ipAddress ||
-                        oGw   != mDhcpInfo.gateway ||
-                        oMsk  != mDhcpInfo.netmask ||
-                        oDns1 != mDhcpInfo.dns1 ||
-                        oDns2 != mDhcpInfo.dns2));
-
-            if (changed) {
-                resetConnections(true);
-                configureInterface();
-                if (mUseStaticIp) {
-                    Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
-                    msg.sendToTarget();
-                }
-            }
-        }
+    /**
+     * Check if default route is set
+     */
+    public boolean isDefaultRouteSet() {
+        return mDefaultRouteSet.get();
     }
 
-    private class NotificationEnabledSettingObserver extends ContentObserver {
+    /**
+     * Set a flag indicating default route is set for the network
+     */
+    public void defaultRouteSet(boolean enabled) {
+        mDefaultRouteSet.set(enabled);
+    }
 
-        public NotificationEnabledSettingObserver(Handler handler) {
-            super(handler);
-        }
+    /**
+     * Return the system properties name associated with the tcp buffer sizes
+     * for this network.
+     */
+    public String getTcpBufferSizesPropName() {
+        return "net.tcp.buffersize.wifi";
+    }
 
-        public void register() {
-            ContentResolver cr = mContext.getContentResolver();
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
-            mNotificationEnabled = getValue();
-        }
-
+    private class WifiStateReceiver extends BroadcastReceiver {
         @Override
-        public void onChange(boolean selfChange) {
-            super.onChange(selfChange);
-
-            mNotificationEnabled = getValue();
-            if (!mNotificationEnabled) {
-                // Remove any notification that may be showing
-                setNotificationVisible(false, 0, true, 0);
+        public void onReceive(Context context, Intent intent) {
+           if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+                mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+                        WifiManager.EXTRA_NETWORK_INFO);
+                mLinkProperties = intent.getParcelableExtra(
+                        WifiManager.EXTRA_LINK_PROPERTIES);
+                if (mLinkProperties == null) {
+                    mLinkProperties = new LinkProperties();
+                }
+                mLinkCapabilities = intent.getParcelableExtra(
+                        WifiManager.EXTRA_LINK_CAPABILITIES);
+                if (mLinkCapabilities == null) {
+                    mLinkCapabilities = new LinkCapabilities();
+                }
+                Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
+                msg.sendToTarget();
+            } else if (intent.getAction().equals(WifiManager.CONFIG_CHANGED_ACTION)) {
+                mLinkProperties = (LinkProperties) intent.getParcelableExtra(
+                        WifiManager.EXTRA_LINK_PROPERTIES);
+                Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
+                msg.sendToTarget();
             }
-
-            resetNotificationTimer();
-        }
-
-        private boolean getValue() {
-            return Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
         }
     }
+
 }